feat(photon): markdown rendering, emoji reactions, sidecar lifecycle fixes, telemetry toggle (salvage #44486)#44713
Merged
Merged
Conversation
…eactions Pin spectrum-ts to exactly 3.0.0 (was ^1.18.0 plus an `npm install spectrum-ts@latest` on every setup) so breaking SDK majors can't take down fresh installs silently; `hermes photon setup` now runs `npm ci`. Upgrade procedure documented in the README. Migrate resolveSpace to the v3 namespace API: `im.space.create(phone)` for DMs and `im.space.get(id)` for everything else — group spaces are now rehydratable from their persisted id after a sidecar restart, which v1 could not do. Markdown: replies go out via the v3 `markdown()` builder (iMessage renders natively; other Spectrum platforms degrade to plain text). `PHOTON_MARKDOWN=false` reverts to the stripped plain-text path. Reactions, behind PHOTON_REACTIONS (default off): lifecycle tapbacks (👀 while processing, 👍/👎 on completion) via new sidecar /react and /unreact endpoints with per-target reaction-handle tracking, and user tapbacks on bot-sent messages routed to the agent as synthetic `reaction:added:<emoji>` events. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…port A hard gateway exit (crash, SIGKILL, supervisor restart) left the detached Node sidecar running with a token the next gateway run doesn't know, so it could never be told to /shutdown. Every replacement spawn then died on EADDRINUSE, failing each 30→300s reconnect attempt while the orphan kept consuming the inbound gRPC stream. Two layers: - Lifetime binding: the adapter now holds the sidecar's stdin as a pipe, and the sidecar (PHOTON_SIDECAR_WATCH_STDIN=1) shuts down on stdin EOF — fired by the OS on any parent death, including SIGKILL. - Startup reaping: before spawning, the adapter probes the port and terminates a stale listener, but only after verifying its command line is a Photon sidecar; a foreign listener raises a clear error instead of being signalled. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Prevents "Future attached to a different loop" errors when _sidecar_call is invoked from a worker thread via _run_async in send_message_tool. The persistent _http_client remains in use for the inbound streaming loop, which always runs on the gateway's loop.
Add `action='react'` to `send_message` tool and expose `add_reaction`/ `remove_reaction` on the Photon adapter. - Track latest inbound message id per chat (`_last_inbound_by_chat`, bounded to 200 entries) so the agent can react without threading message ids through tool calls - New `add_reaction`/`remove_reaction` public methods on PhotonAdapter; unlike the lifecycle tapbacks, these are not gated by PHOTON_REACTIONS - `send_message` gains `action='react'` with `emoji` and optional `message_id` params; resolves target via existing channel-directory and home-channel logic; requires a live gateway adapter
Inbound events key the tracker by the DM chat GUID (any;-;+1555...), but home-channel react calls address the same space by bare E.164 — normalize both to the phone so add_reaction's last-inbound default resolves regardless of which form the caller uses (mirrors the sidecar's phoneTargetFromSpaceId). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…atch tests Follow-up for salvaged PR #44486: the adapter shipped remove_reaction but the tool only exposed 'react'. Generalize _handle_react(remove=) and add tool-level dispatch tests for react/unreact (missing from the original PR).
Contributor
🔎 Lint report:
|
| Rule | Count |
|---|---|
invalid-assignment |
3 |
unresolved-import |
3 |
First entries
tests/plugins/platforms/photon/test_markdown.py:34: [invalid-assignment] invalid-assignment: Object of type `def _fake_call(path: str, body: dict[str, Any]) -> CoroutineType[Any, Any, dict[str, Any]]` is not assignable to attribute `_sidecar_call` of type `def _sidecar_call(self, path: str, body: dict[str, Any]) -> CoroutineType[Any, Any, dict[str, Any]]`
tests/plugins/platforms/photon/test_sidecar_lifecycle.py:14: [unresolved-import] unresolved-import: Cannot resolve imported module `pytest`
tests/plugins/platforms/photon/test_reactions.py:38: [invalid-assignment] invalid-assignment: Object of type `def _fake_call(path: str, body: dict[str, Any]) -> CoroutineType[Any, Any, dict[str, Any]]` is not assignable to attribute `_sidecar_call` of type `def _sidecar_call(self, path: str, body: dict[str, Any]) -> CoroutineType[Any, Any, dict[str, Any]]`
tests/plugins/platforms/photon/test_reactions.py:13: [unresolved-import] unresolved-import: Cannot resolve imported module `pytest`
tests/plugins/platforms/photon/test_reactions.py:133: [invalid-assignment] invalid-assignment: Object of type `def _boom(path: str, body: dict[str, Any]) -> CoroutineType[Any, Any, dict[str, Any]]` is not assignable to attribute `_sidecar_call` of type `def _sidecar_call(self, path: str, body: dict[str, Any]) -> CoroutineType[Any, Any, dict[str, Any]]`
tests/plugins/platforms/photon/test_markdown.py:11: [unresolved-import] unresolved-import: Cannot resolve imported module `pytest`
✅ Fixed issues (1):
| Rule | Count |
|---|---|
invalid-assignment |
1 |
First entries
plugins/platforms/photon/adapter.py:148: [invalid-assignment] invalid-assignment: Invalid subscript assignment with key of type `Literal["home_channel"]` and value of type `dict[str, str]` on object of type `dict[str, str]`
Unchanged: 5686 pre-existing issues carried over.
Diagnostics are surfaced as warnings — this check never fails the build.
Closed
14 tasks
1 task
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Salvage of #44486 by @underthestars-zhy onto current main: Photon (iMessage via Spectrum) markdown rendering, emoji tapback reactions, sidecar orphan reaping + stdin-EOF lifecycle, spectrum-ts 3.1.0 pin, and a telemetry opt-in toggle — plus a follow-up commit adding the
unreactaction the adapter shipped but the tool didn't expose.Changes
action=react/unreactwithemoji/message_idparams (extends the existing gated tool, no new tool)Validation
Contributor commits cherry-picked with authorship preserved — rebase-merge this PR. Closes #44486.
Infographic