Skip to content

feat(coding-agent): direct session transport between TUI and worker (ENG-5817) - #1926

Merged
xeophon merged 21 commits into
mainfrom
feat/direct-session-transport
Sep 1, 2026
Merged

xeophon merged 21 commits into
mainfrom
feat/direct-session-transport

Conversation

@snimu

@snimu snimu commented Aug 31, 2026

Copy link
Copy Markdown
Contributor

Routes session-plane traffic (prompt/steer/attach/streaming deltas) directly between the TUI and the worker process over a ticketed unix-socket transport, so streaming volume no longer transits the supervisor event loop. The supervisor stays the authority: it issues single-use grants and keeps the whole control plane.

Linear: ENG-5817
Prior art: tkellogg's fork branch feat/direct-worker-transport (discussion #1805) — the grant/ticket security design is ported largely as-is; the routing and integration layers are rebuilt for the agent-roster stack.

Stacked on #1909 (→ #1900#1897#1895). Will be rebased onto main and retargeted once the stack merges; review the top 7 commits.

Design

  • Ticketed admission: TUI asks the supervisor (get_direct_worker_transport); supervisor issues a single-use grant (32-byte token, TTL 10s, bound to a per-incarnation workerInstanceId, checked with sha256 + timingSafeEqual, burned before compare) and pre-registers it in the worker; the TUI connects to the worker's own socket and authenticates via peer_auth. Session-scoped: a direct peer can only issue commands for its granted session; pre-auth sockets accept nothing but auth and are ended on any violation.
  • Compile-time-total routing: DAEMON_COMMAND_PLANE (as const satisfies Record<DaemonCommand["type"], "session" | "control">) colocated with the command union — adding a command without classifying it fails tsc. 33 control-plane commands stay on the supervisor; unknown types never route direct. One predicate shared by client routing and worker enforcement.
  • Fallback, not failure: any direct-path problem (ticket refusal, dev/ino mismatch, malformed frames, socket loss) silently falls back to supervisor routing. In-flight direct requests hard-reject and are never replayed through the supervisor (no double execution). A healthy direct link survives supervisor-socket loss; authoritative closes (shutdown/update) stay terminal.
  • Version skew: schema 24→25; supervisor-only capability direct_peer_transport; workers advertise peer_transport in worker_auth; the routed client re-checks every command against the worker's own daemon_hello. Downgraded (schema-24) supervisors still adopt live workers (instance match enforced only when presented). Both directions pinned.
  • Roster integration, one truth: direct attachments flow as directAttachedClients through the one summary builder into roster pushes, public list, and eviction snapshots — idle eviction stays honest with zero new bookkeeping.

Invariant worth knowing (for future edits)

The archive-shutdown path closes sessions before fencing peer transports, so direct peers receive session_closed "killed" through normal vocabulary rather than a reasonless FIN. The correctness of close-reason delivery on the archive leg lives in that ordering (pinned), not in types — an archive path that skips closeSession would regress it.

Review status

Implemented and reviewed by two independent agent passes (design-note veto round, fresh-eyes security review with a fix round, verification round → APPROVE): grant races, scope enforcement, double-execution, eviction honesty, and all 106 plane classifications audited. Benchmark of the supervisor choke point (motivation): at 20 concurrent streamers the supervisor burns ~2.5 cores forwarding deltas on all current builds.

Testing

  • test/daemon-peer-transport.test.ts (12): grant lifecycle matrix, pre-auth lockdown, scope enforcement, fencing, dev/ino ticket, eviction counting.
  • Routed-client + connection pins: malformed-frame fallback, mid-attach fallback, supervisor-loss survival, clean-stop terminality (with recoverDaemon set), update restoration, reattach semantics.
  • Both-direction protocol compat pins; full package suite green in sandbox (failing set byte-identical to the environmental baseline).

Note

Medium Risk
Large changes to daemon wire protocol, authentication, and reconnection semantics on the critical TUI↔session path; mitigated by capability gating, compile-time command plane classification, and broad tests, but regressions could affect session stability or routing during supervisor/worker failures.

Overview
Adds direct peer transport so interactive clients can run session-plane daemon traffic (prompt, attach, streaming) over a dedicated worker socket instead of relaying through the supervisor. The supervisor still owns control-plane work and mints single-use, short-lived tickets (get_direct_worker_transport → worker grant registration → peer_auth on the worker socket), bound to worker instance id and socket dev/ino identity.

Introduces DaemonRoutedClient and DaemonTransportClient: session commands route to the worker when a direct link exists; failures degrade to supervisor routing without double-executing in-flight direct requests. DaemonAgentConnection attach/reconnect behavior changes so a lost supervisor socket can recover in the background while a healthy direct link keeps the session live; shutdown/update closes stay authoritative. Workers gain session_client auth role, grant fencing on update/shutdown, and DAEMON_COMMAND_PLANE (schema 25, direct_peer_transport) so every command is explicitly control vs session plane.

Roster/list summaries expose directAttachedClients so attachment counts and idle eviction stay accurate when viewers attach directly.

Reviewed by Cursor Bugbot for commit 60efcec. Bugbot is set up for automated code reviews on this repo. Configure here.

Note

Add direct session transport between TUI and worker in coding-agent daemon

  • Supervisor mints single-use, 10-second peer-transport tickets scoped to a worker incarnation and active session; workers register grants via worker_register_peer_transport and clients authenticate with peer_auth
  • New DaemonRoutedClient multiplexes a supervisor control-plane socket with an optional direct worker socket, routing session-plane commands directly to the worker and falling back to the supervisor on direct-link loss
  • DaemonAgentConnection reconnect logic distinguishes control-plane vs session-plane recovery: a surviving direct link avoids session_resynced, and direct-link loss triggers a bounded reattach instead of a terminal close
  • Each worker incarnation gets a unique workerInstanceId (randomUUID) passed via DAEMON_WORKER_INSTANCE_ID_ENV; grants and auth bind to this id plus process and socket identity
  • Schema revision bumped to 25; new direct_peer_transport capability and get_direct_worker_transport command gated by schema 25 and protocol 7
  • Behavioral Change: passivatedWorkerRosterEntry now omits directAttachedClients from passivated summaries; direct session peers receive plain JSONL payloads instead of compact assistant deltas; watchSession watchers stay on the shared control-plane socket and never use direct transport; worker_archive_and_shutdown fences peers and emits session_closed before socket end. Risk: fencing revokes outstanding grants and terminates live direct peers — any out-of-tree consumer expecting compact deltas on a direct peer socket or relying on session_resynced during supervisor-only reconnect will see changed behavior

Macroscope summarized 60efcec.

Benchmark (same harness/workload as the roster-stack bench; 24 live sessions, 60s windows; sandbox 8c/16GB)

burst 10 streamers stack (supervisor-routed) this PR (direct)
delta e2e p50/p90/max 4 / 12 / 125 ms 1 / 6 / 23 ms
list RTT p50/p90/max 15 / 103 / 231 ms 5 / 14 / 21 ms
supervisor CPU (mean) 258% 17%
supervisor RSS (max) 1054 MiB 610 MiB

At 20–24 streamers: delta p50 13→1 ms, supervisor RSS 1694→745 MiB (residual supervisor CPU is roster/state upkeep, not delta forwarding). Direct engagement verified per-connection (hasDirectTransport 30/30 across windows, zero silent fallbacks). Fallback probes: supervisor SIGKILL mid-stream → the stream never paused (~210 deltas/s through the outage), control plane recovered on its own; worker SIGKILL mid-stream → terminal close in 29 ms, no reconnect spin. Idle latency and hung-worker isolation identical to the stack baseline.

Comment thread packages/coding-agent/src/modes/agent-connection/daemon-agent-connection.ts Outdated
Comment thread packages/coding-agent/src/modes/agent-connection/daemon-agent-connection.ts Outdated
Comment thread packages/coding-agent/src/modes/daemon/daemon-mode.ts
Comment thread packages/coding-agent/src/modes/agent-connection/daemon-agent-connection.ts Outdated
@xeophon
xeophon force-pushed the fix/daemon-client-reconnect-recoverable branch from 15c5075 to aa2a4bf Compare September 1, 2026 07:18
@snimu
snimu force-pushed the fix/daemon-client-reconnect-recoverable branch from aa2a4bf to 7c1c6d8 Compare September 1, 2026 08:34
@snimu
snimu force-pushed the feat/direct-session-transport branch from a586234 to 6cea3e9 Compare September 1, 2026 08:34
@snimu
snimu force-pushed the fix/daemon-client-reconnect-recoverable branch from 7c1c6d8 to ae22984 Compare September 1, 2026 08:48
@snimu
snimu force-pushed the feat/direct-session-transport branch from 83358a5 to 959331e Compare September 1, 2026 08:53
Comment thread packages/coding-agent/src/modes/daemon/daemon-routed-client.ts Outdated
Comment thread packages/coding-agent/src/modes/agent-connection/daemon-agent-connection.ts Outdated
Comment thread packages/coding-agent/src/modes/daemon/daemon-mode.ts
Comment thread packages/coding-agent/src/modes/daemon/daemon-mode.ts
Comment thread packages/coding-agent/src/modes/daemon/daemon-routed-client.ts Outdated
Base automatically changed from fix/daemon-client-reconnect-recoverable to main September 1, 2026 09:59
snimu added 13 commits September 1, 2026 12:00
Schema revision 25 adds the capability-gated get_direct_worker_transport
command, the single-use DaemonPeerTransportTicket shape, and the peer grant,
peer_auth, and worker_register_peer_transport wire types. DAEMON_COMMAND_PLANE
classifies every daemon command as session- or control-plane at compile time
so unclassified commands can never route to a direct transport, and the
per-command hello compatibility predicate moves to daemon-protocol.ts so the
routed client and DaemonClient share one check. Worker descriptors gain the
per-incarnation workerInstanceId that peer grants bind to.
Workers accept supervisor-registered single-use peer grants
(worker_register_peer_transport; bounded, TTL-capped, burned before their
token is compared with sha256 + timingSafeEqual) and authenticate direct
session clients via peer_auth on their own socket. A pre-auth socket may only
authenticate; a failed peer_auth or any other command ends it. Authenticated
peers are pinned to the session plane of their granted session, so worker
control commands, other sessions, and unknown command types are rejected.
Grants and live peers are fenced during update preparation, archive shutdown,
and worker shutdown; compact assistant deltas stay a supervisor-only encoding.
Session summaries report directAttachedClients so the supervisor roster,
attachment counts, and idle eviction can stay honest.
get_direct_worker_transport asks the supervisor for a single-use ticket to a
resident worker's own socket. The supervisor pre-registers a 10s grant in the
worker over its authenticated channel, binds it to the fresh per-incarnation
workerInstanceId (launched via env, echoed in worker_auth, persisted on the
descriptor), verifies the worker process identity is current, and stamps the
socket's dev/ino into the ticket so clients can detect a swapped socket.
Workers advertise peer-transport support in their worker_auth capabilities;
older workers never get grants and client-owned workers are refused at issue
time. Public summaries and idle-eviction snapshots add worker-reported direct
attachments to the supervisor-side count so eviction and the roster stay
honest.
…port

DaemonAgentConnection.attach upgrades its supervisor connection with a direct
link to the session's worker when the supervisor advertises
direct_peer_transport: it redeems a single-use ticket, verifies the socket's
dev/ino, and wraps both sockets in a DaemonRoutedClient. Session-plane
commands the worker's own hello serves go direct; control-plane commands, an
old worker, or any direct-path failure fall back silently to supervisor
routing, and a malformed direct frame closes the link instead of throwing
into the client. An established direct link outlives supervisor-socket loss:
the session keeps streaming while the control plane reconnects in the
background. Watchers and owned sessions stay on the shared supervisor socket,
and a successful reattach drops the now-stale direct link.
… not the live claim

The fence check revokes a stale supervisor claim before ending its socket, so
the disconnect cleanup can no longer consult supervisorClaims to decide
whether to probe supervisor availability; the authentication role survives
revocation and excludes direct session peers. Also drop the redundant
shuttingDown clause from peer_auth (shutdown already fences admissions and
clears grants) and teach the affected daemon fakes the fields the transport
now touches.
…ransport

Review fixes for the direct session transport:
- Peer fencing now writes daemon_closing with the real reason before ending
  direct sockets, and the archive shutdown closes sessions first so direct
  peers read session_closed "killed" instead of a fake daemon shutdown; a
  clean daemon stop therefore stays terminal and never respawns the daemon.
- The connection checks the authoritative shutdown/update close reason before
  the surviving-direct-link reroute, so reasoned closes stay terminal (stop)
  or take the update restoration path (restart) even while the direct link is
  healthy.
- worker_auth enforces the instance binding only when the authenticating
  supervisor presents one, so a downgraded supervisor still adopts live
  workers; the peer-transport fencing between current builds is unchanged.
- One helper owns the direct-plus-supervisor attachment sum; peer_auth drops
  the same-process re-check of the worker's own instance id (registration is
  the single enforcement point; the client-presented field stays checked) and
  the peer command gate drops the updateRestart clause that fencing makes
  unreachable.
Drop comments that narrate the adjacent code and squeeze multi-line docs to
one line, keeping the load-bearing notes: grant burn-before-compare, the
archive close-before-fence ordering, the reason-before-FIN contract, the
fail-fast rule on dropped request options, the shutdown-window fence
reachability, and the command-plane classification rule.
…n state

Control-plane recovery with a healthy direct link completed a reconnect cycle
per supervisor-socket flap (attach rides the direct link, so every cycle
succeeded instantly) and each cycle emitted session_resynced, re-rendering an
unchanged transcript up to 15 times during one supervisor replacement. The
reconnect loop now emits session_resynced only when the session transport
itself was re-established through a supervisor attach; a held direct link
streamed state throughout, so recovery emits just the connected status.
…back

A direct-transport loss is never itself a session loss: the connection now
reconnects through a supervisor re-attach even without a recoverDaemon hook
(the supervisor socket is still healthy; a worker that actually died keeps
signalling through the authoritative session_closed path). The transport
close handler is extracted into one method and the control-plane close saved
during an initial direct attach replays through it, so a daemon shutdown
during initial attach stays terminal instead of respawning the daemon.
A direct peer joining or leaving an idle session changes
directAttachedClients without any session event to carry it, so the
supervisor's roster (and roster-based idle eviction) held a stale count until
unrelated activity. The attach handler and the shared detach path now
schedule a roster flush for session_client sockets; supervisor-relayed
viewers keep their existing event-driven cadence.
…irect link holds

The resident-worker restart test predates the direct transport: its client
now rides the worker's own socket through the supervisor swap, so the
recovery emits connected without a session_resynced. The end-to-end pin now
asserts the new semantics (no resync, no close, state queryable), matching
the unit pins from the resync-emission fix.
…tial direct attach

Replaying a saved supervisor-shutdown close through the close handler inside
the static attach emitted the terminal event before any listener could
subscribe, and terminalCloseEmitted then swallowed every later signal: a
daemon stop during first attach produced a zombie connected TUI. No listener
can exist during construction, so a terminal saved close now rejects the
attach with the socket-close error — the pre-transport semantics every call
site already handles; non-terminal saved closes keep replaying through the
single close handler.
…ck; re-acquire direct after reattach

A direct-socket drop while a session input pause was held hit the fail-closed
pause branch before the recoverable-direct-loss ordering, terminalizing a
session the supervisor could still serve and skipping update restoration. The
fence stays fail-closed only for control-plane losses; a lost direct link
clears it (holders still learn through the invalidation generation) and falls
through to the normal close ordering.

A successful in-window reattach dropped the stale direct link but never
acquired one for the target, leaving the session supervisor-routed for good.
The routed client can now upgrade itself with the same silent ticket dance as
the initial attach (one shared acquisition helper), and reattachSession
invokes it on success only — a rejected reattach still keeps the old link.
…the ticket request

The post-reattach upgradeDirectTransport was half a mechanism: its own pin
showed no attach ever crosses the new link, so events stayed on the
supervisor relay while requests silently switched sockets, and supervisor
attachment bookkeeping leaked. Post-reattach supervisor routing is the honest
state until a designed-as-one-piece re-upgrade exists. With its only other
caller gone, the ticket acquisition folds back into
createDaemonSessionTransport, and the ticket request now opts out of
reconnect parking (recoverable:false) so a supervisor drop mid-acquire fails
into supervisor routing instead of pending the attach forever.
…ounded

While the direct link is held, the reconnect loop's success is connect plus
hello: no re-attach and no snapshot cross a socket that never stopped serving
the session, which deletes the snapshot-restream-over-live-socket failure
class and makes the resync skip structural. The bounded reconnect deadline
arms only once the direct link is gone (re-armed at the transition), so a
supervisor staying down never terminally closes a healthy direct stream; a
mid-recovery direct death reruns the loop as a normal bounded session-plane
reconnect.
…its type

The session pin is scoped to the active-session slot and guards against
routing accidents, not privilege escalation: ticket holders already hold
supervisor access.
…recovery

Control-plane-only recovery skips attach(), which was the roster store's only
rebind seam, so a supervisor blip under a healthy direct link left the agents
view subscribed to the dead hello. The subscription is a control-plane
accessory: held-direct recovery now re-attaches the store non-fatally after
connect + hello, and the session-plane path keeps its single rebind inside
attach().
…sert

Drop docs that restate their declarations (transport interface, role union),
the close-handler narration the predicate name now carries, and squeeze three
multi-line whys to one line each. The half-open handshake test loses its
zero-resync assert: the deadline-policy pin and the supervisor-swap E2E both
pin held-mode zero-resync across the whole recovery.
@macroscopeapp

macroscopeapp Bot commented Sep 1, 2026

Copy link
Copy Markdown
Contributor

Macroscope has since reviewed this pull request. An earlier review was skipped by a cost limit; a review has now completed, so that notice no longer applies.

@snimu
snimu force-pushed the feat/direct-session-transport branch from 2360981 to 11c5f74 Compare September 1, 2026 10:01
…er the held-recovery awaits

The supervisor-side retry of a failed initial direct attach owns its bounded
outcome, so it now opts out of reconnect parking and, on failure, rejects with
the control-plane close saved during the window — a daemon stop racing the
retry reaches the caller as the authoritative reason instead of a pend or a
generic socket error. Held-direct recovery revalidates once after its last
await (the roster re-attach): a terminal close inside the window stops the
loop without emitting connected, and a direct loss inside it falls through to
the bounded session-plane rerun instead of joining the finished recovery.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit bb47b4a. Configure here.

…restoration

The post-await validation only re-derived two of the close handler's three
outcomes, so an update close landing inside the held roster re-attach fell
into the bounded session-plane rerun, whose deadline could close the client
under the running restoration. The check now consults the handler's own
dispatch outputs — terminalCloseEmitted, updateRestartPending, or the
recoverable close that joined this loop — so a window close lands in exactly
the branch the handler chose.
@snimu
snimu requested a review from xeophon September 1, 2026 10:29
@xeophon
xeophon merged commit 173d845 into main Sep 1, 2026
23 checks passed
@xeophon
xeophon deleted the feat/direct-session-transport branch September 1, 2026 10:41
ketema added a commit to ketema/prime-agent that referenced this pull request Sep 1, 2026
- Direct session transport between TUI and worker (ENG-5817, PrimeIntellect-ai#1926)
- Event-driven supervisor agent roster with push subscriptions (PrimeIntellect-ai#1897, PrimeIntellect-ai#1900, PrimeIntellect-ai#1895)
- Hardened daemon startup, recovery ownership, and worker launch diagnostics (PrimeIntellect-ai#1929, PrimeIntellect-ai#1918)
- Python REPL runtime single-dump snapshots and bash preview tool (PrimeIntellect-ai#1945, PrimeIntellect-ai#1911)
- Non-blocking RLM subagent deletion and snapshot update suppression (PrimeIntellect-ai#1954, PrimeIntellect-ai#1944)
- Saved catalog loading on Agents View open (PrimeIntellect-ai#1960)
- Advanced Anthropic prompt caching marker across tool results (PrimeIntellect-ai#1927)
- TUI process replacement on update and empty draft eviction (PrimeIntellect-ai#1631, PrimeIntellect-ai#1946, PrimeIntellect-ai#1920)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants