refactor: replace welcome message with server_info status payload

Unify the initial connection handshake and capability update paths.
The server now sends a server_info status message on connect and
whenever capabilities change, enabling the onboarding flow where
voice becomes available after models are configured.
This commit is contained in:
Mohamed Boudra
2026-03-12 20:38:03 +07:00
parent 554017b0b8
commit b09a731d85
16 changed files with 177 additions and 162 deletions
@@ -29,7 +29,7 @@ function createMockTransport() {
let onOpen: () => void = () => {}
let onClose: (_event?: unknown) => void = () => {}
let onError: (_event?: unknown) => void = () => {}
let welcomeOrdinal = 1
let serverInfoOrdinal = 1
const transport: DaemonTransport = {
send: (data) => sent.push(data),
@@ -61,11 +61,16 @@ function createMockTransport() {
sent.length = 0
onMessage(
JSON.stringify({
type: 'welcome',
serverId: `srv_test_${welcomeOrdinal++}`,
hostname: null,
version: null,
resumed: false,
type: 'session',
message: {
type: 'status',
payload: {
status: 'server_info',
serverId: `srv_test_${serverInfoOrdinal++}`,
hostname: null,
version: null,
},
},
})
)
},
+24 -19
View File
@@ -4,10 +4,11 @@ import {
AgentCreatedStatusPayloadSchema,
AgentRefreshedStatusPayloadSchema,
AgentResumedStatusPayloadSchema,
parseServerInfoStatusPayload,
RestartRequestedStatusPayloadSchema,
ShutdownRequestedStatusPayloadSchema,
SessionInboundMessageSchema,
type WSWelcomeMessage,
type ServerInfoStatusPayload,
WSOutboundMessageSchema,
} from '../shared/messages.js'
import type {
@@ -450,7 +451,7 @@ export class DaemonClient {
private readonly logServerId: string | null
private readonly logClientIdHash: string
private readonly logGeneration: number | null
private lastWelcomeMessage: WSWelcomeMessage | null = null
private lastServerInfoMessage: ServerInfoStatusPayload | null = null
constructor(private config: DaemonClientConfig) {
this.logger = config.logger ?? consoleLogger
@@ -565,7 +566,7 @@ export class DaemonClient {
const transportUrl = this.resolveTransportUrlForAttempt()
const transport = transportFactory({ url: transportUrl, headers })
this.transport = transport
this.lastWelcomeMessage = null
this.lastServerInfoMessage = null
this.updateConnectionState({
status: 'connecting',
@@ -700,7 +701,7 @@ export class DaemonClient {
this.clearWaiters(new Error('Daemon client closed'))
this.rejectPendingSendQueue(new Error('Daemon client closed'))
this.terminalStreams.clearAll()
this.lastWelcomeMessage = null
this.lastServerInfoMessage = null
this.updateConnectionState(
{ status: 'disposed' },
{ event: 'DISPOSE', reason: 'Client closed', reasonCode: 'disposed' }
@@ -2796,8 +2797,8 @@ export class DaemonClient {
return requestId ?? crypto.randomUUID()
}
getLastWelcomeMessage(): WSWelcomeMessage | null {
return this.lastWelcomeMessage
getLastServerInfoMessage(): ServerInfoStatusPayload | null {
return this.lastServerInfoMessage
}
private resolveTransportUrlForAttempt(): string {
@@ -2962,18 +2963,6 @@ export class DaemonClient {
return
}
if (parsed.data.type === 'welcome') {
this.lastWelcomeMessage = parsed.data
this.resetConnectTimeout()
this.reconnectAttempt = 0
this.updateConnectionState({ status: 'connected' }, { event: 'HELLO_WELCOME' })
this.resubscribeCheckoutDiffSubscriptions()
this.resubscribeTerminalDirectorySubscriptions()
this.flushPendingSendQueue()
this.resolveConnect()
return
}
this.handleSessionMessage(parsed.data.message)
}
@@ -3050,7 +3039,7 @@ export class DaemonClient {
this.clearWaiters(new Error(reason ?? 'Connection lost'))
this.rejectPendingSendQueue(new Error(reason ?? 'Connection lost'))
this.terminalStreams.clearAll()
this.lastWelcomeMessage = null
this.lastServerInfoMessage = null
if (wasDisposed) {
this.rejectConnect(new Error(reason ?? 'Daemon client is disposed'))
@@ -3084,6 +3073,22 @@ export class DaemonClient {
}
private handleSessionMessage(msg: SessionOutboundMessage): void {
if (msg.type === 'status') {
const serverInfo = parseServerInfoStatusPayload(msg.payload)
if (serverInfo) {
this.lastServerInfoMessage = serverInfo
if (this.connectionState.status === 'connecting') {
this.resetConnectTimeout()
this.reconnectAttempt = 0
this.updateConnectionState({ status: 'connected' }, { event: 'HELLO_SERVER_INFO' })
this.resubscribeCheckoutDiffSubscriptions()
this.resubscribeTerminalDirectorySubscriptions()
this.flushPendingSendQueue()
this.resolveConnect()
}
}
}
if (msg.type === 'terminal_stream_exit') {
this.terminalStreams.clearStream({ streamId: msg.payload.streamId })
}