Skip to content

AA03: Assistant stream transport (WS /assistant/stream) - #854

Merged
LinuxDevil merged 1 commit into
mainfrom
fb-aa03-stream
Nov 3, 2026
Merged

LinuxDevil merged 1 commit into
mainfrom
fb-aa03-stream

Conversation

@LinuxDevil

Copy link
Copy Markdown
Owner

What changed

Wire (apps/agent-forge/shared/authorWire.ts)

  • AuthorClientMessage (chat.send / chat.stop / chat.resume) and AUTHOR_STREAM_PATH = '/assistant/stream'.

Server (server/author/authorStream.ts, new)

  • createAuthorUpgradeHandler({ runner, sessions }) — registered on the single D15 UpgradeRouter under AUTHOR_STREAM_PATH in server/index.ts, so the shared Host/Origin/token gate runs before it (no new upgrade listener, wsServer.ts dispatch untouched).
  • WebSocketServer({ noServer: true, maxPayload: 64KB }); per-session Map<sessionId, Set<WebSocket>> subscribers; runner.on('event') fans out to that session's sockets only.
  • Frames: chat.send → runner.start (a refused start is a turn.error seq 0 on the sending socket only); chat.stop → runner.stop (no reply); chat.resume and any unparseable/unknown frame → turn.error BAD_MESSAGE (echoing the requestId when the frame carried one). Binary frames ignored; nothing is logged.
  • ?session= is validated after the upgrade: an id outside AUTHOR_SESSION_ID_RE or missing from the store closes with code 4404, reason Chat not found. Sockets subscribe before the async check resolves so a chat.send sent on open can't miss turn.started.

Wiring (server/author/wiring.ts, server/app.ts, server/index.ts)

  • The AuthorTurnRunner construction (system prompt + AA06 tools + per-turn workspace resolution) moved out of createApp into createAuthorRunner, used by both createApp (its authorRunner default) and startStudioServer. startStudioServer now owns the session store + runner and passes both into createApp, so WS /assistant/stream, the session routes and the DELETE 409 share one set of turn slots. New authorRunner option on startStudioServer is the test seam (providerFor → mockModel).

Client (src/runtime/runtimeClient.ts, src/author/authorStream.ts, new)

  • createAuthorSession / listAuthorSessions / getAuthorSession / deleteAuthorSession / getAuthorSettings REST methods and openAuthorSocket(sessionId) (?session=<id>&token=<token>).
  • connectAuthorStream(sessionId, handlers, { open?, retryMs? }): reconnects every retryMs (default 2000) until close(); a 4404 close goes straight to closed; send() returns false (sends nothing) when not open; onOpen fires after every (re)connect for AA04's chat.resume. newRequestId() stamps r_<32 hex> ids.

How it was tested

  • server/__tests__/authorStream.test.ts (new, 6 tests): real startStudioServer + ws client — broadcast to two sockets on one session, isolation from another session, 401 without token, 4404 for unknown/malformed session, BUSY only to the sender, chat.stop → stopped, bad JSON / chat.resume / malformed chat.send → BAD_MESSAGE.
  • src/author/__tests__/authorStream.test.ts (new, 11 tests incl. newRequestId): fake WebSocket + vi.useFakeTimers() — connect/open/send, reconnect after drop (reconnecting → open), retry-until-close, 4404 → closed, send while down → false.
  • Studio: npm run typecheck ✓, npm run typecheck:server ✓, npm test -- --run ✓ (956), npm run test:server ✓ (975), npm run lint ✓ (0 errors).
  • Root: npx tsc --noEmit ✓, npm run lint ✓, npm run test:coverage ✓ (7163 passed; required packages/create-lousho build + LF template READMEs locally — environmental, unrelated changes not committed), npm run fallow ✓ (0 above threshold), npm run docs:ratchet ✓, npm run api:check ✓, npm run docs:llms:check ✓, npm run size-check ✓.
  • Note: the previously flaky runRegistry "stop() then run() resumes from the last checkpoint" test passed in this run.

Acceptance criteria

  • A socket for a valid session receives turn.started … turn.finished for a chat.send; a second socket on the same session receives the same events.
  • A socket for another session receives nothing.
  • Missing token → upgrade refused with 401; unknown session → close code 4404.
  • WS /agents/:id/stream still works (existing tests pass unchanged).
  • Client wrapper reconnects after a drop and reports reconnecting then open; close() stops retries.

Generated with Devin

WS /assistant/stream?session=<id> on the D15 upgrade router: per-session
subscriber map over AuthorTurnRunner 'event' broadcasts, chat.send /
chat.stop / BAD_MESSAGE handling, 4404 close for unknown sessions. Client
side: RuntimeClient author methods + reconnecting connectAuthorStream
wrapper. startStudioServer gains the authorRunner test seam; the runner
wiring moves to server/author/wiring.ts so index.ts and createApp share
the one instance.

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
@LinuxDevil
LinuxDevil merged commit b5e9260 into main Nov 3, 2026
4 checks passed
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.

1 participant