Skip to content

FE-873: Web-as-driver streaming chat transport (topology A)#225

Merged
lunelson merged 4 commits into
nextfrom
ln/fe-873-web-as-driver
Jun 17, 2026
Merged

FE-873: Web-as-driver streaming chat transport (topology A)#225
lunelson merged 4 commits into
nextfrom
ln/fe-873-web-as-driver

Conversation

@lunelson

@lunelson lunelson commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Add SessionEventRelay so the TUI-started sidecar can stream the live in-process AgentSession event feed over the existing /rpc WebSocket as brunch.sessionEvent, multiplexed with brunch.updated and without a second canonical store.
  • Add the narrow web re-entry seams: session.driveTurn for plain prompts and session.answerExchange via LiveExchangeBroker for live request_answer exchanges.
  • Land the topology-A streaming battery and reconcile SPEC/PLAN/RPC/session docs around observer relay, reconnect, fan-out, command intake, and live answer convergence.

Verification

  • npm run check

lunelson commented Jun 16, 2026

Copy link
Copy Markdown
Contributor Author

@lunelson lunelson changed the base branch from ln/fe-870-renderer-golden-context-tools to graphite-base/225 June 16, 2026 16:48
@lunelson lunelson marked this pull request as ready for review June 17, 2026 08:09
Copilot AI review requested due to automatic review settings June 17, 2026 08:09
@cursor

cursor Bot commented Jun 17, 2026

Copy link
Copy Markdown

PR Summary

Medium Risk
Introduces live session driving and in-turn exchange answering over WebSocket sidecar RPC; mitigated by handle-gated discovery, named JSON-RPC errors, transcript-backed truth (D19-L), and a broad tier-2 oracle battery on real runBrunchTui wiring.

Overview
Topology A observer + command path for the TUI-started web sidecar: live AgentSession events forward through SessionEventRelay as brunch.sessionEvent on the existing /rpc WebSocket, multiplexed with brunch.updated, with no replay to late subscribers (reconnect is projection refetch + new live frames).

Sidecar RPC is split via createWebSidecarRpcHandlers: read methods stay read-only; session.driveTurn and session.answerExchange appear only when process-local handles are attached. request_answer can await a LiveExchangeBroker when there is no interactive UI, resolved by session.answerExchange.

runBrunchTui owns the relay, live session ref, turn driver, and broker and passes them into startWebHost. Tier-2 web-driver-streaming.* tests cover relay/differential/multiplex, reconnect, fan-out, command intake, and live request_answer convergence; memory/PLAN.md and memory/SPEC.md record D84-L / A28-L / A29-L status.

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

Copilot AI 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.

Pull request overview

Implements the Topology A observer relay seam to forward in-process Pi AgentSession events over the existing TUI sidecar /rpc WebSocket as brunch.sessionEvent JSON-RPC notifications, multiplexed alongside existing brunch.updated domain invalidations.

Changes:

  • Add SessionEventRelay (brunch.sessionEvent frames with {seq, event}) plus unit tests.
  • Wire the relay through the TUI boot path into the web sidecar WebSocket transport, and add an end-to-end dev test that proves relay↔transcript reduction and multiplexing.
  • Update RPC docs + SPEC/PLAN to record the validated/built slice and decision D84-L.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
src/rpc/websocket.ts Subscribes to session-event relay and forwards frames over the /rpc WebSocket transport.
src/rpc/web-host.ts Threads optional SessionEventRelay into the web host transport wiring.
src/rpc/session-event-relay.ts Introduces the process-local relay that envelopes Pi events as brunch.sessionEvent notifications with monotonic seq.
src/rpc/session-event-relay.test.ts Unit coverage for envelope shape, no replay, and explicit unsubscription.
src/rpc/README.md Documents brunch.sessionEvent as an observer-only, non-canonical notification.
src/dev/tests/web-driver-streaming.relay.test.ts End-to-end proof: real runBrunchTui host relays live events over ws, multiplexed with brunch.updated, and streamed text appears in JSONL.
src/app/brunch-tui.ts Creates the relay, threads it into the sidecar, and attaches the live AgentSession after creation.
memory/SPEC.md Records A28-L as validated and adds D84-L decision for the relay seam.
memory/PLAN.md Updates FE-873 status/certainty and topology materialization notes to reflect the built observer relay slice.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/rpc/websocket.ts
Comment on lines +39 to +41
const unsubscribeSessionEvents = options.sessionEvents?.subscribe((frame) => {
publishNotification(JSON.stringify(frame));
});
lunelson added 4 commits June 17, 2026 18:24
…uash merge

   The FE-870 squash merge (#217, e4a63fd) reintroduced the exhausted
   renderer-golden-coverage--graph-render-migration.md scope card in its
stale
   pre-build form (Cards 0-3 shown blocked/not-done), though Cards 0-3
shipped
   and PLAN records the scope card as retired. Re-delete it so HEAD
matches
   bkp/ln/fe-870-renderer-golden-context-tools and PLAN's "scope card
retired"
   state. No other canonical-doc drift from the restack.
…pology A)

   SessionEventRelay forwards the in-process AgentSession event stream over the
   existing TUI sidecar /rpc WebSocket as brunch.sessionEvent frames, multiplexed
   with brunch.updated. Ephemeral/process-local; no second canonical store (D19-L,
   D84-L). Battery claims 1-4 production-wired; claims 5-7 are follow-on slices.

Signed-off-by: Lu Nelson <ln@hash.ai>
@lunelson lunelson force-pushed the graphite-base/225 branch from ec02a3d to e50a407 Compare June 17, 2026 17:44
@lunelson lunelson force-pushed the ln/fe-873-web-as-driver branch from 291c5d9 to 1c702f1 Compare June 17, 2026 17:44
@lunelson lunelson changed the base branch from graphite-base/225 to ln/fe-870-renderer-ii June 17, 2026 17:44

@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 2 potential issues.

Fix All in Cursor

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

Want reviews to match your repository better? Bugbot Learning can learn team-specific rules from PR activity. A team admin can enable Learning in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 1c702f1. Configure here.

if (ctx.hasUI && typeof ctx.ui.editor === 'function') {
answer = await ctx.ui.editor(params.prompt);
} else if (answerBroker) {
answer = await answerBroker.awaitAnswer({ exchangeId: params.exchangeId });

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Broker bypassed when TUI UI

High Severity

In the default runBrunchTui host, request_answer checks ctx.hasUI before the live-exchange broker, so mid-turn exchanges block on the terminal editor even though LiveExchangeBroker and session.answerExchange are wired. Web-side session.answerExchange then resolves an unused broker while the tool never awaits it, so live web answering fails in topology A.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 1c702f1. Configure here.

return createJsonRpcFailure(requestId, -32010, NO_LIVE_AGENT_SESSION_DRIVER_MESSAGE);
}
return createJsonRpcSuccess(requestId, { status: 'completed' });
},

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Sidecar allows observer driveTurn

Medium Severity

Every WebSocket client on the TUI sidecar shares the same handler registry, so session.driveTurn is callable from any connection. The fan-out acceptance model treats extra clients as read-only observers, but this method is not restricted to a single driver role and there is no rejection path for observer sockets.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 1c702f1. Configure here.

@lunelson lunelson changed the title FE-873: observer relay seam for live AgentSession event streaming (topology A) FE-873: Web-as-driver streaming chat transport (topology A) Jun 17, 2026
Base automatically changed from ln/fe-870-renderer-ii to next June 17, 2026 20:20
@lunelson lunelson merged commit 8c3748f into next Jun 17, 2026
18 checks passed
@lunelson lunelson deleted the ln/fe-873-web-as-driver branch June 17, 2026 20:20
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