@getpaseo/server 0.1.15 → 0.1.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/dist/server/client/daemon-client.d.ts +41 -4
  2. package/dist/server/client/daemon-client.d.ts.map +1 -1
  3. package/dist/server/client/daemon-client.js +355 -84
  4. package/dist/server/client/daemon-client.js.map +1 -1
  5. package/dist/server/server/agent/agent-manager.d.ts +10 -0
  6. package/dist/server/server/agent/agent-manager.d.ts.map +1 -1
  7. package/dist/server/server/agent/agent-manager.js +261 -18
  8. package/dist/server/server/agent/agent-manager.js.map +1 -1
  9. package/dist/server/server/agent/agent-projections.d.ts +5 -0
  10. package/dist/server/server/agent/agent-projections.d.ts.map +1 -1
  11. package/dist/server/server/agent/agent-projections.js +24 -0
  12. package/dist/server/server/agent/agent-projections.js.map +1 -1
  13. package/dist/server/server/agent/agent-sdk-types.d.ts +11 -0
  14. package/dist/server/server/agent/agent-sdk-types.d.ts.map +1 -1
  15. package/dist/server/server/agent/agent-storage.d.ts +15 -5
  16. package/dist/server/server/agent/agent-storage.d.ts.map +1 -1
  17. package/dist/server/server/agent/agent-storage.js +2 -0
  18. package/dist/server/server/agent/agent-storage.js.map +1 -1
  19. package/dist/server/server/agent/providers/claude/tool-call-detail-parser.d.ts.map +1 -1
  20. package/dist/server/server/agent/providers/claude/tool-call-detail-parser.js +2 -0
  21. package/dist/server/server/agent/providers/claude/tool-call-detail-parser.js.map +1 -1
  22. package/dist/server/server/agent/providers/claude/tool-call-mapper.d.ts.map +1 -1
  23. package/dist/server/server/agent/providers/claude/tool-call-mapper.js +2 -0
  24. package/dist/server/server/agent/providers/claude/tool-call-mapper.js.map +1 -1
  25. package/dist/server/server/agent/providers/claude-agent.d.ts +7 -1
  26. package/dist/server/server/agent/providers/claude-agent.d.ts.map +1 -1
  27. package/dist/server/server/agent/providers/claude-agent.js +1470 -232
  28. package/dist/server/server/agent/providers/claude-agent.js.map +1 -1
  29. package/dist/server/server/agent/providers/codex-app-server-agent.d.ts.map +1 -1
  30. package/dist/server/server/agent/providers/codex-app-server-agent.js +19 -4
  31. package/dist/server/server/agent/providers/codex-app-server-agent.js.map +1 -1
  32. package/dist/server/server/agent/providers/tool-call-detail-primitives.d.ts +40 -0
  33. package/dist/server/server/agent/providers/tool-call-detail-primitives.d.ts.map +1 -1
  34. package/dist/server/server/agent/providers/tool-call-detail-primitives.js +1 -0
  35. package/dist/server/server/agent/providers/tool-call-detail-primitives.js.map +1 -1
  36. package/dist/server/server/client-message-id.d.ts +3 -0
  37. package/dist/server/server/client-message-id.d.ts.map +1 -0
  38. package/dist/server/server/client-message-id.js +12 -0
  39. package/dist/server/server/client-message-id.js.map +1 -0
  40. package/dist/server/server/persisted-config.d.ts +8 -8
  41. package/dist/server/server/persistence-hooks.js +1 -1
  42. package/dist/server/server/persistence-hooks.js.map +1 -1
  43. package/dist/server/server/relay-transport.d.ts.map +1 -1
  44. package/dist/server/server/relay-transport.js +27 -28
  45. package/dist/server/server/relay-transport.js.map +1 -1
  46. package/dist/server/server/session.d.ts +4 -2
  47. package/dist/server/server/session.d.ts.map +1 -1
  48. package/dist/server/server/session.js +122 -31
  49. package/dist/server/server/session.js.map +1 -1
  50. package/dist/server/server/websocket-server.d.ts +8 -4
  51. package/dist/server/server/websocket-server.d.ts.map +1 -1
  52. package/dist/server/server/websocket-server.js +272 -75
  53. package/dist/server/server/websocket-server.js.map +1 -1
  54. package/dist/server/shared/daemon-endpoints.d.ts +9 -1
  55. package/dist/server/shared/daemon-endpoints.d.ts.map +1 -1
  56. package/dist/server/shared/daemon-endpoints.js +18 -3
  57. package/dist/server/shared/daemon-endpoints.js.map +1 -1
  58. package/dist/server/shared/messages.d.ts +2065 -313
  59. package/dist/server/shared/messages.d.ts.map +1 -1
  60. package/dist/server/shared/messages.js +40 -1
  61. package/dist/server/shared/messages.js.map +1 -1
  62. package/dist/server/shared/tool-call-display.d.ts.map +1 -1
  63. package/dist/server/shared/tool-call-display.js +4 -0
  64. package/dist/server/shared/tool-call-display.js.map +1 -1
  65. package/package.json +3 -3
@@ -16,7 +16,6 @@ import type { VoiceCallerContext, VoiceMcpStdioConfig, VoiceSpeakHandler } from
16
16
  export type AgentMcpTransportFactory = () => Promise<Transport>;
17
17
  export type ExternalSocketMetadata = {
18
18
  transport: "relay";
19
- externalSessionKey: string;
20
19
  };
21
20
  type WebSocketServerConfig = {
22
21
  allowedOrigins: Set<string>;
@@ -38,9 +37,9 @@ export declare class MissingDaemonVersionError extends Error {
38
37
  export declare class VoiceAssistantWebSocketServer {
39
38
  private readonly logger;
40
39
  private readonly wss;
40
+ private readonly pendingConnections;
41
41
  private readonly sessions;
42
42
  private readonly externalSessionsByKey;
43
- private clientIdCounter;
44
43
  private readonly serverId;
45
44
  private readonly daemonVersion;
46
45
  private readonly agentManager;
@@ -82,10 +81,15 @@ export declare class VoiceAssistantWebSocketServer {
82
81
  close(): Promise<void>;
83
82
  private sendToClient;
84
83
  private sendBinaryToClient;
84
+ private sendToConnection;
85
+ private sendBinaryToConnection;
85
86
  private attachSocket;
87
+ private createSessionConnection;
88
+ private clearPendingConnection;
89
+ private buildWelcomeMessage;
90
+ private handleHello;
86
91
  private buildServerInfoStatusPayload;
87
- private broadcastServerInfo;
88
- private sendServerInfo;
92
+ private broadcastCapabilitiesUpdate;
89
93
  private bindSocketHandlers;
90
94
  resolveVoiceSpeakHandler(callerAgentId: string): VoiceSpeakHandler | null;
91
95
  resolveVoiceCallerContext(callerAgentId: string): VoiceCallerContext | null;
@@ -1 +1 @@
1
- {"version":3,"file":"websocket-server.d.ts","sourceRoot":"","sources":["../../../src/server/websocket-server.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"websocket-server.d.ts","sourceRoot":"","sources":["../../../src/server/websocket-server.ts"],"names":[],"mappings":"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"}
@@ -68,6 +68,11 @@ function bufferFromWsData(data) {
68
68
  return Buffer.from(data);
69
69
  }
70
70
  const EXTERNAL_SESSION_DISCONNECT_GRACE_MS = 90000;
71
+ const HELLO_TIMEOUT_MS = 15000;
72
+ const WS_CLOSE_HELLO_TIMEOUT = 4001;
73
+ const WS_CLOSE_INVALID_HELLO = 4002;
74
+ const WS_CLOSE_INCOMPATIBLE_PROTOCOL = 4003;
75
+ const WS_PROTOCOL_VERSION = 1;
71
76
  export class MissingDaemonVersionError extends Error {
72
77
  constructor() {
73
78
  super("VoiceAssistantWebSocketServer requires a non-empty daemonVersion.");
@@ -79,9 +84,9 @@ export class MissingDaemonVersionError extends Error {
79
84
  */
80
85
  export class VoiceAssistantWebSocketServer {
81
86
  constructor(server, logger, serverId, agentManager, agentStorage, downloadTokenStore, paseoHome, createAgentMcpTransport, wsConfig, speech, terminalManager, voice, dictation, agentProviderRuntimeSettings, daemonVersion) {
87
+ this.pendingConnections = new Map();
82
88
  this.sessions = new Map();
83
89
  this.externalSessionsByKey = new Map();
84
- this.clientIdCounter = 0;
85
90
  this.voiceSpeakHandlers = new Map();
86
91
  this.voiceCallerContexts = new Map();
87
92
  this.ACTIVITY_THRESHOLD_MS = 120000;
@@ -159,7 +164,7 @@ export class VoiceAssistantWebSocketServer {
159
164
  return;
160
165
  }
161
166
  this.serverCapabilities = next;
162
- this.broadcastServerInfo();
167
+ this.broadcastCapabilitiesUpdate();
163
168
  }
164
169
  async attachExternalSocket(ws, metadata) {
165
170
  await this.attachSocket(ws, undefined, metadata);
@@ -169,16 +174,34 @@ export class VoiceAssistantWebSocketServer {
169
174
  ...this.sessions.values(),
170
175
  ...this.externalSessionsByKey.values(),
171
176
  ]);
177
+ const pendingSockets = new Set(this.pendingConnections.keys());
178
+ for (const pending of this.pendingConnections.values()) {
179
+ if (pending.helloTimeout) {
180
+ clearTimeout(pending.helloTimeout);
181
+ pending.helloTimeout = null;
182
+ }
183
+ }
172
184
  const cleanupPromises = [];
173
185
  for (const connection of uniqueConnections) {
174
186
  if (connection.externalDisconnectCleanupTimeout) {
175
187
  clearTimeout(connection.externalDisconnectCleanupTimeout);
176
188
  connection.externalDisconnectCleanupTimeout = null;
177
189
  }
178
- const ws = connection.socketRef.current;
179
190
  cleanupPromises.push(connection.session.cleanup());
191
+ for (const ws of connection.sockets) {
192
+ cleanupPromises.push(new Promise((resolve) => {
193
+ // WebSocket.CLOSED = 3
194
+ if (ws.readyState === 3) {
195
+ resolve();
196
+ return;
197
+ }
198
+ ws.once("close", () => resolve());
199
+ ws.close();
200
+ }));
201
+ }
202
+ }
203
+ for (const ws of pendingSockets) {
180
204
  cleanupPromises.push(new Promise((resolve) => {
181
- // WebSocket.CLOSED = 3
182
205
  if (ws.readyState === 3) {
183
206
  resolve();
184
207
  return;
@@ -188,6 +211,7 @@ export class VoiceAssistantWebSocketServer {
188
211
  }));
189
212
  }
190
213
  await Promise.all(cleanupPromises);
214
+ this.pendingConnections.clear();
191
215
  this.sessions.clear();
192
216
  this.externalSessionsByKey.clear();
193
217
  this.wss.close();
@@ -204,38 +228,20 @@ export class VoiceAssistantWebSocketServer {
204
228
  }
205
229
  ws.send(encodeBinaryMuxFrame(frame));
206
230
  }
207
- async attachSocket(ws, request, metadata) {
208
- const externalSessionKey = metadata?.transport === "relay" && metadata.externalSessionKey.trim().length > 0
209
- ? metadata.externalSessionKey
210
- : null;
211
- if (externalSessionKey) {
212
- const existing = this.externalSessionsByKey.get(externalSessionKey);
213
- if (existing) {
214
- if (existing.externalDisconnectCleanupTimeout) {
215
- clearTimeout(existing.externalDisconnectCleanupTimeout);
216
- existing.externalDisconnectCleanupTimeout = null;
217
- }
218
- const previousSocket = existing.socketRef.current;
219
- if (previousSocket !== ws) {
220
- this.sessions.delete(previousSocket);
221
- existing.socketRef.current = ws;
222
- }
223
- this.sessions.set(ws, existing);
224
- this.sendServerInfo(ws);
225
- existing.connectionLogger.trace({
226
- clientId: existing.clientId,
227
- externalSessionKey,
228
- totalSessions: this.sessions.size,
229
- }, "Client reconnected");
230
- this.bindSocketHandlers(ws, existing);
231
- return;
232
- }
231
+ sendToConnection(connection, message) {
232
+ for (const ws of connection.sockets) {
233
+ this.sendToClient(ws, message);
233
234
  }
234
- const clientId = `client-${++this.clientIdCounter}`;
235
+ }
236
+ sendBinaryToConnection(connection, frame) {
237
+ for (const ws of connection.sockets) {
238
+ this.sendBinaryToClient(ws, frame);
239
+ }
240
+ }
241
+ async attachSocket(ws, request, metadata) {
235
242
  const requestMetadata = extractSocketRequestMetadata(request);
236
243
  const connectionLoggerFields = {
237
- clientId,
238
- transport: externalSessionKey ? "relay" : "direct",
244
+ transport: metadata?.transport === "relay" ? "relay" : "direct",
239
245
  };
240
246
  if (requestMetadata.host) {
241
247
  connectionLoggerFields.host = requestMetadata.host;
@@ -250,14 +256,48 @@ export class VoiceAssistantWebSocketServer {
250
256
  connectionLoggerFields.remoteAddress = requestMetadata.remoteAddress;
251
257
  }
252
258
  const connectionLogger = this.logger.child(connectionLoggerFields);
253
- const socketRef = { current: ws };
259
+ const pending = {
260
+ connectionLogger,
261
+ helloTimeout: null,
262
+ };
263
+ const timeout = setTimeout(() => {
264
+ if (this.pendingConnections.get(ws) !== pending) {
265
+ return;
266
+ }
267
+ pending.helloTimeout = null;
268
+ this.pendingConnections.delete(ws);
269
+ pending.connectionLogger.warn({ timeoutMs: HELLO_TIMEOUT_MS }, "Closing connection due to missing hello");
270
+ try {
271
+ ws.close(WS_CLOSE_HELLO_TIMEOUT, "Hello timeout");
272
+ }
273
+ catch {
274
+ // ignore close errors
275
+ }
276
+ }, HELLO_TIMEOUT_MS);
277
+ pending.helloTimeout = timeout;
278
+ timeout.unref?.();
279
+ this.pendingConnections.set(ws, pending);
280
+ this.bindSocketHandlers(ws);
281
+ pending.connectionLogger.trace({
282
+ totalPendingConnections: this.pendingConnections.size,
283
+ }, "Client connected; awaiting hello");
284
+ }
285
+ createSessionConnection(params) {
286
+ const { ws, clientId, connectionLogger } = params;
287
+ let connection = null;
254
288
  const session = new Session({
255
289
  clientId,
256
290
  onMessage: (msg) => {
257
- this.sendToClient(socketRef.current, wrapSessionMessage(msg));
291
+ if (!connection) {
292
+ return;
293
+ }
294
+ this.sendToConnection(connection, wrapSessionMessage(msg));
258
295
  },
259
296
  onBinaryMessage: (frame) => {
260
- this.sendBinaryToClient(socketRef.current, frame);
297
+ if (!connection) {
298
+ return;
299
+ }
300
+ this.sendBinaryToConnection(connection, frame);
261
301
  },
262
302
  logger: connectionLogger.child({ module: "session" }),
263
303
  downloadTokenStore: this.downloadTokenStore,
@@ -289,21 +329,96 @@ export class VoiceAssistantWebSocketServer {
289
329
  dictation: this.dictation ?? undefined,
290
330
  agentProviderRuntimeSettings: this.agentProviderRuntimeSettings,
291
331
  });
292
- const connection = {
332
+ connection = {
293
333
  session,
294
334
  clientId,
295
335
  connectionLogger,
296
- socketRef,
297
- externalSessionKey,
336
+ sockets: new Set([ws]),
298
337
  externalDisconnectCleanupTimeout: null,
299
338
  };
300
- this.sessions.set(ws, connection);
301
- if (externalSessionKey) {
302
- this.externalSessionsByKey.set(externalSessionKey, connection);
339
+ return connection;
340
+ }
341
+ clearPendingConnection(ws) {
342
+ const pending = this.pendingConnections.get(ws);
343
+ if (!pending) {
344
+ return null;
345
+ }
346
+ if (pending.helloTimeout) {
347
+ clearTimeout(pending.helloTimeout);
348
+ pending.helloTimeout = null;
349
+ }
350
+ this.pendingConnections.delete(ws);
351
+ return pending;
352
+ }
353
+ buildWelcomeMessage(params) {
354
+ return {
355
+ type: "welcome",
356
+ serverId: this.serverId,
357
+ hostname: getHostname(),
358
+ version: this.daemonVersion,
359
+ resumed: params.resumed,
360
+ ...(this.serverCapabilities ? { capabilities: this.serverCapabilities } : {}),
361
+ };
362
+ }
363
+ handleHello(params) {
364
+ const { ws, message, pending } = params;
365
+ if (message.protocolVersion !== WS_PROTOCOL_VERSION) {
366
+ this.clearPendingConnection(ws);
367
+ pending.connectionLogger.warn({
368
+ receivedProtocolVersion: message.protocolVersion,
369
+ expectedProtocolVersion: WS_PROTOCOL_VERSION,
370
+ }, "Rejected hello due to protocol version mismatch");
371
+ try {
372
+ ws.close(WS_CLOSE_INCOMPATIBLE_PROTOCOL, "Incompatible protocol version");
373
+ }
374
+ catch {
375
+ // ignore close errors
376
+ }
377
+ return;
378
+ }
379
+ const clientId = message.clientId.trim();
380
+ if (clientId.length === 0) {
381
+ this.clearPendingConnection(ws);
382
+ pending.connectionLogger.warn("Rejected hello with empty clientId");
383
+ try {
384
+ ws.close(WS_CLOSE_INVALID_HELLO, "Invalid hello");
385
+ }
386
+ catch {
387
+ // ignore close errors
388
+ }
389
+ return;
390
+ }
391
+ this.clearPendingConnection(ws);
392
+ const existing = this.externalSessionsByKey.get(clientId);
393
+ if (existing) {
394
+ if (existing.externalDisconnectCleanupTimeout) {
395
+ clearTimeout(existing.externalDisconnectCleanupTimeout);
396
+ existing.externalDisconnectCleanupTimeout = null;
397
+ }
398
+ existing.sockets.add(ws);
399
+ this.sessions.set(ws, existing);
400
+ this.sendToClient(ws, this.buildWelcomeMessage({ resumed: true }));
401
+ existing.connectionLogger.trace({
402
+ clientId,
403
+ resumed: true,
404
+ totalSessions: this.sessions.size,
405
+ }, "Client connected via hello");
406
+ return;
303
407
  }
304
- this.sendServerInfo(ws);
305
- connectionLogger.trace({ clientId, externalSessionKey, totalSessions: this.sessions.size }, "Client connected");
306
- this.bindSocketHandlers(ws, connection);
408
+ const connectionLogger = pending.connectionLogger.child({ clientId });
409
+ const connection = this.createSessionConnection({
410
+ ws,
411
+ clientId,
412
+ connectionLogger,
413
+ });
414
+ this.sessions.set(ws, connection);
415
+ this.externalSessionsByKey.set(clientId, connection);
416
+ this.sendToClient(ws, this.buildWelcomeMessage({ resumed: false }));
417
+ connection.connectionLogger.trace({
418
+ clientId,
419
+ resumed: false,
420
+ totalSessions: this.sessions.size,
421
+ }, "Client connected via hello");
307
422
  }
308
423
  buildServerInfoStatusPayload() {
309
424
  return {
@@ -314,33 +429,29 @@ export class VoiceAssistantWebSocketServer {
314
429
  ...(this.serverCapabilities ? { capabilities: this.serverCapabilities } : {}),
315
430
  };
316
431
  }
317
- broadcastServerInfo() {
432
+ broadcastCapabilitiesUpdate() {
318
433
  this.broadcast(wrapSessionMessage({
319
434
  type: "status",
320
435
  payload: this.buildServerInfoStatusPayload(),
321
436
  }));
322
437
  }
323
- sendServerInfo(ws) {
324
- // Advertise stable server identity immediately on connect (used for URL/shareable IDs).
325
- this.sendToClient(ws, wrapSessionMessage({
326
- type: "status",
327
- payload: this.buildServerInfoStatusPayload(),
328
- }));
329
- }
330
- bindSocketHandlers(ws, connection) {
438
+ bindSocketHandlers(ws) {
331
439
  ws.on("message", (data) => {
332
440
  void this.handleRawMessage(ws, data);
333
441
  });
334
442
  ws.on("close", async (code, reason) => {
335
- await this.detachSocket(ws, connection, {
443
+ await this.detachSocket(ws, {
336
444
  code: typeof code === "number" ? code : undefined,
337
445
  reason,
338
446
  });
339
447
  });
340
448
  ws.on("error", async (error) => {
341
449
  const err = error instanceof Error ? error : new Error(String(error));
342
- connection.connectionLogger.error({ err }, "Client error");
343
- await this.detachSocket(ws, connection, { error: err });
450
+ const active = this.sessions.get(ws);
451
+ const pending = this.pendingConnections.get(ws);
452
+ const log = active?.connectionLogger ?? pending?.connectionLogger ?? this.logger;
453
+ log.error({ err }, "Client error");
454
+ await this.detachSocket(ws, { error: err });
344
455
  });
345
456
  }
346
457
  resolveVoiceSpeakHandler(callerAgentId) {
@@ -349,13 +460,22 @@ export class VoiceAssistantWebSocketServer {
349
460
  resolveVoiceCallerContext(callerAgentId) {
350
461
  return this.voiceCallerContexts.get(callerAgentId) ?? null;
351
462
  }
352
- async detachSocket(ws, connection, details) {
353
- const activeConnection = this.sessions.get(ws);
354
- if (activeConnection !== connection)
463
+ async detachSocket(ws, details) {
464
+ const pending = this.clearPendingConnection(ws);
465
+ if (pending) {
466
+ pending.connectionLogger.trace({
467
+ code: details.code,
468
+ reason: stringifyCloseReason(details.reason),
469
+ }, "Pending client disconnected");
355
470
  return;
471
+ }
472
+ const connection = this.sessions.get(ws);
473
+ if (!connection) {
474
+ return;
475
+ }
356
476
  this.sessions.delete(ws);
357
- if (connection.externalSessionKey &&
358
- connection.socketRef.current === ws) {
477
+ connection.sockets.delete(ws);
478
+ if (connection.sockets.size === 0) {
359
479
  if (connection.externalDisconnectCleanupTimeout) {
360
480
  clearTimeout(connection.externalDisconnectCleanupTimeout);
361
481
  }
@@ -369,13 +489,21 @@ export class VoiceAssistantWebSocketServer {
369
489
  connection.externalDisconnectCleanupTimeout = timeout;
370
490
  connection.connectionLogger.trace({
371
491
  clientId: connection.clientId,
372
- externalSessionKey: connection.externalSessionKey,
373
492
  code: details.code,
374
493
  reason: stringifyCloseReason(details.reason),
375
494
  reconnectGraceMs: EXTERNAL_SESSION_DISCONNECT_GRACE_MS,
376
495
  }, "Client disconnected; waiting for reconnect");
377
496
  return;
378
497
  }
498
+ if (connection.sockets.size > 0) {
499
+ connection.connectionLogger.trace({
500
+ clientId: connection.clientId,
501
+ remainingSockets: connection.sockets.size,
502
+ code: details.code,
503
+ reason: stringifyCloseReason(details.reason),
504
+ }, "Client socket disconnected; session remains attached");
505
+ return;
506
+ }
379
507
  await this.cleanupConnection(connection, "Client disconnected");
380
508
  }
381
509
  async cleanupConnection(connection, logMessage) {
@@ -383,27 +511,36 @@ export class VoiceAssistantWebSocketServer {
383
511
  clearTimeout(connection.externalDisconnectCleanupTimeout);
384
512
  connection.externalDisconnectCleanupTimeout = null;
385
513
  }
386
- const currentSocket = connection.socketRef.current;
387
- this.sessions.delete(currentSocket);
388
- if (connection.externalSessionKey) {
389
- const existing = this.externalSessionsByKey.get(connection.externalSessionKey);
390
- if (existing === connection) {
391
- this.externalSessionsByKey.delete(connection.externalSessionKey);
392
- }
514
+ for (const socket of connection.sockets) {
515
+ this.sessions.delete(socket);
516
+ }
517
+ connection.sockets.clear();
518
+ const existing = this.externalSessionsByKey.get(connection.clientId);
519
+ if (existing === connection) {
520
+ this.externalSessionsByKey.delete(connection.clientId);
393
521
  }
394
522
  connection.connectionLogger.trace({ clientId: connection.clientId, totalSessions: this.sessions.size }, logMessage);
395
523
  await connection.session.cleanup();
396
524
  }
397
525
  async handleRawMessage(ws, data) {
526
+ const activeConnection = this.sessions.get(ws);
527
+ const pendingConnection = this.pendingConnections.get(ws);
528
+ const log = activeConnection?.connectionLogger ?? pendingConnection?.connectionLogger ?? this.logger;
398
529
  try {
399
- const activeConnection = this.sessions.get(ws);
400
530
  const buffer = bufferFromWsData(data);
401
531
  const asBytes = asUint8Array(buffer);
402
532
  if (asBytes) {
403
533
  const frame = decodeBinaryMuxFrame(asBytes);
404
534
  if (frame) {
405
535
  if (!activeConnection) {
406
- this.logger.error("No session found for client");
536
+ log.warn("Rejected binary frame before hello");
537
+ this.clearPendingConnection(ws);
538
+ try {
539
+ ws.close(WS_CLOSE_INVALID_HELLO, "Session message before hello");
540
+ }
541
+ catch {
542
+ // ignore close errors
543
+ }
407
544
  return;
408
545
  }
409
546
  activeConnection.session.handleBinaryFrame(frame);
@@ -413,13 +550,25 @@ export class VoiceAssistantWebSocketServer {
413
550
  const parsed = JSON.parse(buffer.toString());
414
551
  const parsedMessage = WSInboundMessageSchema.safeParse(parsed);
415
552
  if (!parsedMessage.success) {
553
+ if (pendingConnection) {
554
+ pendingConnection.connectionLogger.warn({
555
+ error: parsedMessage.error.message,
556
+ }, "Rejected pending message before hello");
557
+ this.clearPendingConnection(ws);
558
+ try {
559
+ ws.close(WS_CLOSE_INVALID_HELLO, "Invalid hello");
560
+ }
561
+ catch {
562
+ // ignore close errors
563
+ }
564
+ return;
565
+ }
416
566
  const requestInfo = extractRequestInfoFromUnknownWsInbound(parsed);
417
567
  const isUnknownSchema = requestInfo?.requestId != null &&
418
568
  typeof parsed === "object" &&
419
569
  parsed != null &&
420
570
  "type" in parsed &&
421
571
  parsed.type === "session";
422
- const log = activeConnection?.connectionLogger ?? this.logger;
423
572
  log.warn({
424
573
  clientId: activeConnection?.clientId,
425
574
  requestId: requestInfo?.requestId,
@@ -456,8 +605,39 @@ export class VoiceAssistantWebSocketServer {
456
605
  if (message.type === "recording_state") {
457
606
  return;
458
607
  }
608
+ if (pendingConnection) {
609
+ if (message.type === "hello") {
610
+ this.handleHello({
611
+ ws,
612
+ message,
613
+ pending: pendingConnection,
614
+ });
615
+ return;
616
+ }
617
+ pendingConnection.connectionLogger.warn({
618
+ messageType: message.type,
619
+ }, "Rejected pending message before hello");
620
+ this.clearPendingConnection(ws);
621
+ try {
622
+ ws.close(WS_CLOSE_INVALID_HELLO, "Session message before hello");
623
+ }
624
+ catch {
625
+ // ignore close errors
626
+ }
627
+ return;
628
+ }
459
629
  if (!activeConnection) {
460
- this.logger.error("No session found for client");
630
+ this.logger.error("No connection found for websocket");
631
+ return;
632
+ }
633
+ if (message.type === "hello") {
634
+ activeConnection.connectionLogger.warn("Received hello on active connection");
635
+ try {
636
+ ws.close(WS_CLOSE_INVALID_HELLO, "Unexpected hello");
637
+ }
638
+ catch {
639
+ // ignore close errors
640
+ }
461
641
  return;
462
642
  }
463
643
  if (message.type === "session") {
@@ -482,11 +662,21 @@ export class VoiceAssistantWebSocketServer {
482
662
  const trimmedRawPayload = typeof rawPayload === "string" && rawPayload.length > 2000
483
663
  ? `${rawPayload.slice(0, 2000)}... (truncated)`
484
664
  : rawPayload;
485
- this.logger.error({
665
+ log.error({
486
666
  err,
487
667
  rawPayload: trimmedRawPayload,
488
668
  parsedPayload,
489
669
  }, "Failed to parse/handle message");
670
+ if (this.pendingConnections.has(ws)) {
671
+ this.clearPendingConnection(ws);
672
+ try {
673
+ ws.close(WS_CLOSE_INVALID_HELLO, "Invalid hello");
674
+ }
675
+ catch {
676
+ // ignore close errors
677
+ }
678
+ return;
679
+ }
490
680
  const requestInfo = extractRequestInfoFromUnknownWsInbound(parsedPayload);
491
681
  if (requestInfo) {
492
682
  this.sendToClient(ws, wrapSessionMessage({
@@ -534,6 +724,13 @@ export class VoiceAssistantWebSocketServer {
534
724
  }
535
725
  const allStates = clientEntries.map((e) => e.state);
536
726
  const agent = this.agentManager.getAgent(params.agentId);
727
+ if (agent?.labels?.ui !== "true") {
728
+ this.logger.debug({
729
+ agentId: params.agentId,
730
+ labels: agent?.labels ?? null,
731
+ }, "Skipping attention notification for non-UI agent");
732
+ return;
733
+ }
537
734
  const notification = buildAgentAttentionNotificationPayload({
538
735
  reason: params.reason,
539
736
  serverId: this.serverId,