feat(daemon): send-to-session endpoint — drive any agent from CodeWatch#44
Conversation
|
Bugbot is not enabled for your account, so this pull request was not reviewed. Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: a6d5197c6b
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| '-c', '"$1" --wait && exec "$2" "$3"', '--', guard, script, message, | ||
| ], { detached: true, stdio: 'ignore' }); | ||
| } else { | ||
| child = spawn(script, [message], { detached: true, stdio: 'ignore' }); |
There was a problem hiding this comment.
Handle spawn errors before returning 202
When a wake agent (or gui without idle_guard) has a stale or non-executable script path, spawn() emits its failure asynchronously as a child error event; this catch does not run and no error listener is attached, so Node treats it as an unhandled error and terminates the daemon after the endpoint has already returned 202. Attach an error handler or validate the script before accepting the delivery.
Useful? React with 👍 / 👎.
| } | ||
| try { | ||
| const controller = new AbortController(); | ||
| const timer = setTimeout(() => controller.abort(), FORWARD_TIMEOUT_MS); |
There was a problem hiding this comment.
Clear the forward timeout on failures
When a forward peer is down or the request aborts, fetch() throws before the later clearTimeout(timer) runs, leaving this referenced timer alive for the full 8 seconds; short-lived callers and tests that hit an unreachable peer hang until that timeout per failed attempt, and repeated failures accumulate pending timers. Clear it in a finally block or unref the timer.
Useful? React with 👍 / 👎.
Review findings from @codexmb on #44: 1. Loop guard: forwards carry an x-iak-send-hops header and the chain is bounded to one hop (app -> reachable daemon -> owning daemon); a self-pointing peer now terminates in two requests with 508/502 instead of recursing until timeout. 2. Strict peer confirmation: forward success requires exactly 202 + ok:true from the peer; a bare 200/204 or unparseable body reads as failure (502), never as delivered_via. 3. Input hardening: `from` must be a string of <=200 chars, request bodies over 64 KiB are cut off with 413, and failures after the detached spawn (accepted-async by design) are surfaced through an onAsyncError hook receipted as sessions.send.async_error. Six new regression tests incl. a live self-pointing-peer loop test. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
All three findings fixed in the new commit:
59/59 across session-send + confirmations + mcp-server suites. Re-verify when convenient. |
POST /sessions/send {agent, text, from?} delivers text as user input
into a named agent's live session; GET /sessions/agents feeds the
target picker. Adapters per agent (mcp.confirmations.sessions.agents):
wake (spawn injection script), gui (same, chained behind a human-idle
guard so it never types over the user), tmux (literal send-keys +
Enter), forward (relay to the owning machine's daemon; provenance
prefix applied exactly once, by the owner). 202 accepted-async; every
attempt receipted; token auth like all :8788 routes.
Backend half of the CodeWatch send box (contract v1 posted in-room
2026-07-16); app half lands in CodeWatch against the same shape.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Review findings from @codexmb on #44: 1. Loop guard: forwards carry an x-iak-send-hops header and the chain is bounded to one hop (app -> reachable daemon -> owning daemon); a self-pointing peer now terminates in two requests with 508/502 instead of recursing until timeout. 2. Strict peer confirmation: forward success requires exactly 202 + ok:true from the peer; a bare 200/204 or unparseable body reads as failure (502), never as delivered_via. 3. Input hardening: `from` must be a string of <=200 chars, request bodies over 64 KiB are cut off with 413, and failures after the detached spawn (accepted-async by design) are surfaced through an onAsyncError hook receipted as sessions.send.async_error. Six new regression tests incl. a live self-pointing-peer loop test. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
c3ed464 to
21e3c34
Compare
What
The backend primitive for CodeWatch's send box (contract v1 as posted in thinkoff-development on 2026-07-16, requested by Petrus):
POST /sessions/send {agent, text, from?}→ deliver text as user input into the named agent's live session.202 {ok, agent, deliveredVia}accepted-async;400/404/503/502for validation / unknown agent / no adapter / peer failure. Bearer-token auth like every :8788 route; every attempt receipted (kind: sessions.send).GET /sessions/agents→{agents:[{name, adapter}]}for the app's target picker.Adapters (config:
mcp.confirmations.sessions.agents.<name>)wakescriptwith the message as argvguiidle_guard --waittmuxsend-keys -l <text>thenC-mforwardpeerdaemon[from …]prefix is applied exactly once, by the ownerCross-machine flow: app → any reachable daemon →
forward→ owning daemon → GUI/tmux injection.Tests
9 new cases in
test/session-send.test.mjs: validation matrix, wake delivery + provenance prefix, gui guard-before-script ordering, tmux literal+Enter call sequence, real two-daemon forward relay (ephemeral ports) incl. single-prefix assertion, unreachable-peer 502, HTTP routes (202/503/401), picker. Suite run with confirmations + mcp-server tests: 53/53.Out of scope (tracked)
GET /agents/me/control-statuson antfarm (next PR)🤖 Generated with Claude Code