feat(host-service): emit chat:session-changed on the events bus
What
Adds a chat:session-changed message to the host-service /events WebSocket bus, so a client that watches many chat-v3 sessions across hosts (Roster) can react to a push instead of polling chat.listSessions on each host.
Event shape
interface ChatSessionChangedMessage {
type: "chat:session-changed";
sessionId: string;
workspaceId: string; // the session's scopeId
status: SessionStatus; // from @superset/chat/protocol, the value listSessions stores
live: boolean; // false once the session is closed, same meaning as getSession's `live`
occurredAt: number;
}
live is one field more than the original sketch. Closing a session does not change its stored status: listSessions keeps returning the last status (for example idle, or running if a turn was cut off). Without live, the close event would look the same as the event before it.
When it fires
LiveSessionRegistry gets an optional onSessionChanged hook, which createChatRuntime passes through. Every session event a live session writes to its journal goes through the registry's publish wrapper, so the hook sees each status change:
- Created: the first
sessionevent (starting) fromLiveSession.start. - Status changed: any
sessionevent whose status differs from the last one reported for that session. Session events that keep the same status (title, mode, queue-pause updates) do not fire. Item and delta events never fire. - Closed:
LiveSessionRegistry.dispose(called bycloseSession) fires the last status withlive: false, even if adapter teardown throws. A session event that arrives after close is ignored.
disposeAll (host shutdown) does not fire. The host wires the hook to EventBus.broadcastChatSessionChanged in app.ts. The chat-v3 mount is now created after the EventBus.
Compatibility with released clients
The only /events consumer is packages/workspace-client/src/lib/eventBus.ts (desktop renderer, mobile). Its handleMessage parses the JSON and calls only listeners where entry.type === message.type. A message type that has no registered listener goes through the loop and nothing happens. The only special case is type === "error". This dispatch has been in place since #3224 (April 2026), so released clients ignore chat:session-changed. Nothing on the client side subscribes to it in this PR.
Not included
setModel:setModelInputSchemaexists in the protocol, but the runtime has nosetModelcommand andHarnessAdapterhas no method for it. Wiring it means changing every adapter, so it is left out.
Testing
packages/chat-runtime:bun test --isolate: 179 pass, 0 fail. New tests inregistry.test.ts, using the fake harness:- one turn reports
starting -> running -> idlewithlive: true, thenidlewithlive: falseon close - a second
idlesession event (title change) is not reported again
- one turn reports
packages/host-service:bun test --isolate src/events/event-bus.test.ts src/chat-v3: 26 pass, 0 fail. The new test bridges a test runtime toEventBusand checks the exact message a socket receives when a session is created.bunx biome checkon the 10 changed files: clean.bun run typecheckinhost-serviceandworkspace-clientboth fail with 22 errors, all in../authand../trpc(better-auth plugin types). Plainorigin/mainfails with the same 22 errors in this checkout, and no errors are in the changed files.chat-runtimetypecheck passes.
Summary by cubic
Emits chat:session-changed messages on the host /events WebSocket bus so clients watching chat-v3 sessions across hosts can react to pushes instead of polling chat.listSessions.
Details
- Fires on session creation, each status change, and close; skips session events that keep the same status, and item/delta events never fire.
liveisfalseon close, since closing a session does not change the statuslistSessionsstores.- Host shutdown does not fire the event.
- Released clients ignore the new message type, since
/eventsdispatch only reaches registered listeners and none subscribe to it. setModelwiring is out of scope: the runtime has nosetModelcommand and no adapter method for it.
Summary by CodeRabbit
- New Features
- Chat session changes are now broadcast in real time, including the session’s workspace, status, and whether it is still active.
- Notifications cover session creation, status changes, and closure. Repeated updates that leave the status unchanged are not sent.