Repository navigation
AA03: Assistant stream transport (WS /assistant/stream) - #854
Merged
Merged
Conversation
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>
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.
What changed
Wire (
apps/agent-forge/shared/authorWire.ts)AuthorClientMessage(chat.send/chat.stop/chat.resume) andAUTHOR_STREAM_PATH = '/assistant/stream'.Server (
server/author/authorStream.ts, new)createAuthorUpgradeHandler({ runner, sessions })— registered on the single D15UpgradeRouterunderAUTHOR_STREAM_PATHinserver/index.ts, so the shared Host/Origin/token gate runs before it (no newupgradelistener,wsServer.tsdispatch untouched).WebSocketServer({ noServer: true, maxPayload: 64KB }); per-sessionMap<sessionId, Set<WebSocket>>subscribers;runner.on('event')fans out to that session's sockets only.chat.send→runner.start(a refused start is aturn.errorseq 0 on the sending socket only);chat.stop→runner.stop(no reply);chat.resumeand any unparseable/unknown frame →turn.errorBAD_MESSAGE(echoing the requestId when the frame carried one). Binary frames ignored; nothing is logged.?session=is validated after the upgrade: an id outsideAUTHOR_SESSION_ID_REor missing from the store closes with code4404, reasonChat not found. Sockets subscribe before the async check resolves so achat.sendsent on open can't missturn.started.Wiring (
server/author/wiring.ts,server/app.ts,server/index.ts)AuthorTurnRunnerconstruction (system prompt + AA06 tools + per-turn workspace resolution) moved out ofcreateAppintocreateAuthorRunner, used by bothcreateApp(itsauthorRunnerdefault) andstartStudioServer.startStudioServernow owns the session store + runner and passes both intocreateApp, soWS /assistant/stream, the session routes and the DELETE 409 share one set of turn slots. NewauthorRunneroption onstartStudioServeris the test seam (providerFor→mockModel).Client (
src/runtime/runtimeClient.ts,src/author/authorStream.ts, new)createAuthorSession/listAuthorSessions/getAuthorSession/deleteAuthorSession/getAuthorSettingsREST methods andopenAuthorSocket(sessionId)(?session=<id>&token=<token>).connectAuthorStream(sessionId, handlers, { open?, retryMs? }): reconnects everyretryMs(default 2000) untilclose(); a 4404 close goes straight toclosed;send()returnsfalse(sends nothing) when not open;onOpenfires after every (re)connect for AA04'schat.resume.newRequestId()stampsr_<32 hex>ids.How it was tested
server/__tests__/authorStream.test.ts(new, 6 tests): realstartStudioServer+wsclient — 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/ malformedchat.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,sendwhile down →false.npm run typecheck✓,npm run typecheck:server✓,npm test -- --run✓ (956),npm run test:server✓ (975),npm run lint✓ (0 errors).npx tsc --noEmit✓,npm run lint✓,npm run test:coverage✓ (7163 passed; requiredpackages/create-loushobuild + 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✓.Acceptance criteria
turn.started … turn.finishedfor achat.send; a second socket on the same session receives the same events.WS /agents/:id/streamstill works (existing tests pass unchanged).reconnectingthenopen;close()stops retries.Generated with Devin