@meshagent/meshagent 0.37.1 → 0.38.0

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 (77) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dist/browser/agent.js +74 -10
  3. package/dist/browser/developer-client.js +3 -0
  4. package/dist/browser/helpers.d.ts +2 -2
  5. package/dist/browser/helpers.js +1 -1
  6. package/dist/browser/meshagent-client.d.ts +25 -0
  7. package/dist/browser/meshagent-client.js +65 -0
  8. package/dist/browser/messaging-client.d.ts +29 -16
  9. package/dist/browser/messaging-client.js +256 -154
  10. package/dist/browser/participant.d.ts +7 -2
  11. package/dist/browser/participant.js +9 -9
  12. package/dist/browser/protocol.d.ts +85 -28
  13. package/dist/browser/protocol.js +356 -119
  14. package/dist/browser/room-client.d.ts +165 -29
  15. package/dist/browser/room-client.js +1114 -74
  16. package/dist/browser/room-event.d.ts +11 -0
  17. package/dist/browser/room-event.js +21 -1
  18. package/dist/browser/room-server-client.d.ts +2 -0
  19. package/dist/browser/room-server-client.js +6 -0
  20. package/dist/browser/runtime.d.ts +1 -1
  21. package/dist/browser/runtime.js +3 -1
  22. package/dist/browser/secrets-client.js +6 -2
  23. package/dist/browser/storage-client.d.ts +1 -0
  24. package/dist/browser/storage-client.js +9 -0
  25. package/dist/browser/sync-client.d.ts +16 -14
  26. package/dist/browser/sync-client.js +195 -116
  27. package/dist/esm/agent.js +74 -10
  28. package/dist/esm/developer-client.js +3 -0
  29. package/dist/esm/helpers.d.ts +2 -2
  30. package/dist/esm/helpers.js +1 -1
  31. package/dist/esm/meshagent-client.d.ts +25 -0
  32. package/dist/esm/meshagent-client.js +65 -0
  33. package/dist/esm/messaging-client.d.ts +29 -16
  34. package/dist/esm/messaging-client.js +256 -154
  35. package/dist/esm/participant.d.ts +7 -2
  36. package/dist/esm/participant.js +9 -9
  37. package/dist/esm/protocol.d.ts +85 -28
  38. package/dist/esm/protocol.js +352 -118
  39. package/dist/esm/room-client.d.ts +165 -29
  40. package/dist/esm/room-client.js +1112 -73
  41. package/dist/esm/room-event.d.ts +11 -0
  42. package/dist/esm/room-event.js +19 -0
  43. package/dist/esm/room-server-client.d.ts +2 -0
  44. package/dist/esm/room-server-client.js +7 -1
  45. package/dist/esm/runtime.d.ts +1 -1
  46. package/dist/esm/runtime.js +1 -1
  47. package/dist/esm/secrets-client.js +6 -2
  48. package/dist/esm/storage-client.d.ts +1 -0
  49. package/dist/esm/storage-client.js +9 -0
  50. package/dist/esm/sync-client.d.ts +16 -14
  51. package/dist/esm/sync-client.js +196 -117
  52. package/dist/node/agent.js +74 -10
  53. package/dist/node/developer-client.js +3 -0
  54. package/dist/node/helpers.d.ts +2 -2
  55. package/dist/node/helpers.js +1 -1
  56. package/dist/node/meshagent-client.d.ts +25 -0
  57. package/dist/node/meshagent-client.js +65 -0
  58. package/dist/node/messaging-client.d.ts +29 -16
  59. package/dist/node/messaging-client.js +256 -154
  60. package/dist/node/participant.d.ts +7 -2
  61. package/dist/node/participant.js +9 -9
  62. package/dist/node/protocol.d.ts +85 -28
  63. package/dist/node/protocol.js +356 -119
  64. package/dist/node/room-client.d.ts +165 -29
  65. package/dist/node/room-client.js +1114 -74
  66. package/dist/node/room-event.d.ts +11 -0
  67. package/dist/node/room-event.js +21 -1
  68. package/dist/node/room-server-client.d.ts +2 -0
  69. package/dist/node/room-server-client.js +6 -0
  70. package/dist/node/runtime.d.ts +1 -1
  71. package/dist/node/runtime.js +3 -1
  72. package/dist/node/secrets-client.js +6 -2
  73. package/dist/node/storage-client.d.ts +1 -0
  74. package/dist/node/storage-client.js +9 -0
  75. package/dist/node/sync-client.d.ts +16 -14
  76. package/dist/node/sync-client.js +195 -116
  77. package/package.json +6 -3
@@ -1,35 +1,248 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.RoomClient = void 0;
3
+ exports.RoomClient = exports.RoomProtocolProxy = void 0;
4
4
  const completer_1 = require("./completer");
5
- const utils_1 = require("./utils");
6
- const participant_1 = require("./participant");
7
- const stream_controller_1 = require("./stream-controller");
8
- const sync_client_1 = require("./sync-client");
5
+ const database_client_1 = require("./database-client");
9
6
  const developer_client_1 = require("./developer-client");
10
- const storage_client_1 = require("./storage-client");
7
+ const event_emitter_1 = require("./event-emitter");
11
8
  const messaging_client_1 = require("./messaging-client");
9
+ const memory_client_1 = require("./memory-client");
10
+ const participant_1 = require("./participant");
11
+ const protocol_1 = require("./protocol");
12
12
  const queues_client_1 = require("./queues-client");
13
- const database_client_1 = require("./database-client");
14
- const agent_client_1 = require("./agent-client");
13
+ const response_1 = require("./response");
14
+ const room_event_1 = require("./room-event");
15
+ const room_server_client_1 = require("./room-server-client");
15
16
  const secrets_client_1 = require("./secrets-client");
16
- const containers_client_1 = require("./containers-client");
17
- const memory_client_1 = require("./memory-client");
18
17
  const services_client_1 = require("./services-client");
19
- const room_server_client_1 = require("./room-server-client");
20
- const response_1 = require("./response");
18
+ const storage_client_1 = require("./storage-client");
19
+ const stream_controller_1 = require("./stream-controller");
20
+ const sync_client_1 = require("./sync-client");
21
+ const utils_1 = require("./utils");
22
+ const agent_client_1 = require("./agent-client");
23
+ const containers_client_1 = require("./containers-client");
24
+ class ProtocolStartupFailure extends Error {
25
+ constructor({ kind, reason }) {
26
+ super(reason ?? kind);
27
+ this.name = "ProtocolStartupFailure";
28
+ this.kind = kind;
29
+ this.reason = reason;
30
+ }
31
+ }
32
+ class RoomClientTerminalState {
33
+ constructor({ requestMessage, toolCallMessage, messageSendMessage, }) {
34
+ this.requestMessage = requestMessage;
35
+ this.toolCallMessage = toolCallMessage;
36
+ this.messageSendMessage = messageSendMessage;
37
+ }
38
+ requestError() {
39
+ return new room_server_client_1.RoomServerException(this.requestMessage);
40
+ }
41
+ toolCallError() {
42
+ return new room_server_client_1.RoomServerException(this.toolCallMessage);
43
+ }
44
+ messageSendError() {
45
+ return new room_server_client_1.RoomServerException(this.messageSendMessage);
46
+ }
47
+ }
48
+ class RoomConnectionStatusException extends room_server_client_1.RoomServerException {
49
+ constructor({ statusCode, statusText, }) {
50
+ const normalizedStatusText = statusText?.trim();
51
+ super(normalizedStatusText == null || normalizedStatusText.length === 0
52
+ ? `websocket connect failed with status ${statusCode}`
53
+ : `websocket connect failed with status ${statusCode}: ${normalizedStatusText}`);
54
+ this.name = "RoomConnectionStatusException";
55
+ this.statusCode = statusCode;
56
+ }
57
+ }
58
+ function normalizeCloseReason(reason) {
59
+ if (reason == null) {
60
+ return null;
61
+ }
62
+ const normalized = reason.trim();
63
+ return normalized.length === 0 ? null : normalized;
64
+ }
65
+ function wrapRoomConnectionError(error) {
66
+ if (error instanceof room_server_client_1.RoomServerException) {
67
+ return error;
68
+ }
69
+ if (error instanceof protocol_1.ProtocolHandshakeException) {
70
+ return new RoomConnectionStatusException({
71
+ statusCode: error.statusCode,
72
+ statusText: error.statusText,
73
+ });
74
+ }
75
+ if (error instanceof protocol_1.ProtocolCloseException) {
76
+ return new room_server_client_1.RoomServerException(normalizeCloseReason(error.reason) ?? `room connection closed with status ${error.closeCode}`);
77
+ }
78
+ return new room_server_client_1.RoomServerException(`room connection error: ${String(error)}`);
79
+ }
80
+ function nonRetryableConnectFailureReason(error) {
81
+ if (error instanceof RoomConnectionStatusException
82
+ && (error.statusCode === 403 || error.statusCode === 404)) {
83
+ return error.message;
84
+ }
85
+ return null;
86
+ }
87
+ function roomClosedBeforeReadyError(protocol) {
88
+ return new room_server_client_1.RoomServerException(normalizeCloseReason(protocol.closeReason) ?? "room connection closed before request completed");
89
+ }
90
+ function getEnvironmentValue(name) {
91
+ if (typeof process === "undefined") {
92
+ return undefined;
93
+ }
94
+ return process.env?.[name];
95
+ }
96
+ function websocketRoomUrlFromEnvironment(roomName) {
97
+ const configuredBaseUrl = getEnvironmentValue("MESHAGENT_ROOM_URL")
98
+ ?? getEnvironmentValue("MESHAGENT_API_URL")
99
+ ?? "wss://api.meshagent.com";
100
+ let baseUrl = configuredBaseUrl;
101
+ if (baseUrl.startsWith("https:")) {
102
+ baseUrl = `wss:${baseUrl.slice("https:".length)}`;
103
+ }
104
+ else if (baseUrl.startsWith("http:")) {
105
+ baseUrl = `ws:${baseUrl.slice("http:".length)}`;
106
+ }
107
+ return `${baseUrl}/rooms/${roomName}`;
108
+ }
109
+ function createProtocolFactoryFromEnvironment() {
110
+ const roomName = getEnvironmentValue("MESHAGENT_ROOM");
111
+ const token = getEnvironmentValue("MESHAGENT_TOKEN");
112
+ if (roomName == null || roomName.trim().length === 0 || token == null || token.trim().length === 0) {
113
+ throw new Error("protocolFactory must be configured or MESHAGENT_ROOM and MESHAGENT_TOKEN must be set in the environment");
114
+ }
115
+ return protocol_1.WebSocketClientProtocol.createFactory({
116
+ url: websocketRoomUrlFromEnvironment(roomName),
117
+ token,
118
+ });
119
+ }
120
+ class RoomProtocolProxy {
121
+ constructor({ room }) {
122
+ this._handlers = new Map();
123
+ this._room = room;
124
+ }
125
+ _bind(protocol) {
126
+ for (const [type, handler] of this._handlers.entries()) {
127
+ if (protocol.getHandler(type) === handler) {
128
+ continue;
129
+ }
130
+ protocol.addHandler(type, handler);
131
+ }
132
+ }
133
+ _unbind(protocol) {
134
+ for (const [type, handler] of this._handlers.entries()) {
135
+ const current = protocol.getHandler(type);
136
+ if (current === handler) {
137
+ protocol.removeHandler(type, handler);
138
+ }
139
+ }
140
+ }
141
+ addHandler(type, handler) {
142
+ if (this._handlers.has(type)) {
143
+ throw new Error(`already registered handler for ${type}`);
144
+ }
145
+ this._handlers.set(type, handler);
146
+ this._bind(this._room._protocolInstance);
147
+ }
148
+ removeHandler(type, handler) {
149
+ const registered = this._handlers.get(type);
150
+ if (registered !== handler) {
151
+ throw new Error(`handler mismatch for ${type}`);
152
+ }
153
+ this._handlers.delete(type);
154
+ if (this._room._protocolInstance.getHandler(type) === handler) {
155
+ this._room._protocolInstance.removeHandler(type, handler);
156
+ }
157
+ }
158
+ getHandler(type) {
159
+ return this._handlers.get(type);
160
+ }
161
+ async send(type, data, { id } = {}) {
162
+ if (this._room._entered && !this._room.isConnected && !this._room._allowDisconnectedRequests) {
163
+ throw this._room._disconnectedError({ baseMessage: "room connection is disconnected" });
164
+ }
165
+ await this._room._protocolInstance.send(type, data, id);
166
+ }
167
+ sendNowait(type, data, { id } = {}) {
168
+ if (this._room._entered && !this._room.isConnected && !this._room._allowDisconnectedRequests) {
169
+ throw this._room._disconnectedError({ baseMessage: "room connection is disconnected" });
170
+ }
171
+ return this._room._protocolInstance.sendNowait(type, data, { id });
172
+ }
173
+ getNextMessageId() {
174
+ if (this._room._entered && !this._room.isConnected && !this._room._allowDisconnectedRequests) {
175
+ throw this._room._disconnectedError({ baseMessage: "room connection is disconnected" });
176
+ }
177
+ return this._room._protocolInstance.getNextMessageId();
178
+ }
179
+ get done() {
180
+ return this._room.waitForClose();
181
+ }
182
+ async waitForClose() {
183
+ await this._room.waitForClose();
184
+ }
185
+ get closeKind() {
186
+ return this._room.closeKind;
187
+ }
188
+ get closeReason() {
189
+ return this._room.closeReason;
190
+ }
191
+ get isOpen() {
192
+ return this._room._protocolInstance.isOpen;
193
+ }
194
+ get isClosed() {
195
+ return this._room.isClosed;
196
+ }
197
+ get token() {
198
+ return this._room._protocolInstance.token;
199
+ }
200
+ get url() {
201
+ return this._room._protocolInstance.url;
202
+ }
203
+ }
204
+ exports.RoomProtocolProxy = RoomProtocolProxy;
21
205
  class RoomClient {
22
- constructor({ protocol }) {
206
+ constructor({ protocolFactory = null, reconnectTimeout = null, } = {}) {
207
+ this._entered = false;
208
+ this._allowDisconnectedRequests = false;
209
+ this._eventsController = new stream_controller_1.StreamController();
210
+ this._eventEmitter = new event_emitter_1.EventEmitter();
23
211
  this._pendingRequests = new Map();
212
+ this._toolCallStreams = new Map();
213
+ this._ignoredResponseLabels = new Map();
24
214
  this._ready = new completer_1.Completer();
215
+ this._roomClosed = new completer_1.Completer();
216
+ this._connectionReady = new completer_1.Completer();
217
+ this._localParticipantReady = new completer_1.Completer();
218
+ this._connected = false;
219
+ this._closing = false;
25
220
  this._localParticipant = null;
26
- this._eventsController = new stream_controller_1.StreamController();
27
- this._toolCallStreams = new Map();
28
- this.protocol = protocol;
29
- protocol.addHandler("room_ready", this._handleRoomReady.bind(this));
30
- protocol.addHandler("connected", this._handleParticipant.bind(this));
31
- protocol.addHandler("__response__", this._handleResponse.bind(this));
32
- protocol.addHandler("room.tool_call_response_chunk", this._handleToolCallResponseChunk.bind(this));
221
+ this._lifecycleTask = null;
222
+ this._terminalState = null;
223
+ this._closeKind = null;
224
+ this._closeReason = null;
225
+ this._terminalCallbacksInvoked = false;
226
+ this._roomName = null;
227
+ this._roomUrl = null;
228
+ this._sessionId = null;
229
+ this._handleRoomReadyBound = this._handleRoomReady.bind(this);
230
+ this._handleRoomStatusBound = this._handleRoomStatus.bind(this);
231
+ this._handleParticipantBound = this._handleParticipant.bind(this);
232
+ this._handleResponseBound = this._handleResponse.bind(this);
233
+ this._handleToolCallResponseChunkBound = this._handleToolCallResponseChunk.bind(this);
234
+ if (reconnectTimeout != null && reconnectTimeout < 0) {
235
+ throw new Error("reconnectTimeout must be null or non-negative");
236
+ }
237
+ this._protocolFactory = protocolFactory ?? createProtocolFactoryFromEnvironment();
238
+ this._reconnectTimeout = reconnectTimeout;
239
+ this._protocolInstance = this._protocolFactory();
240
+ this.protocol = new RoomProtocolProxy({ room: this });
241
+ this.protocol.addHandler("room_ready", this._handleRoomReadyBound);
242
+ this.protocol.addHandler("room.status", this._handleRoomStatusBound);
243
+ this.protocol.addHandler("connected", this._handleParticipantBound);
244
+ this.protocol.addHandler("__response__", this._handleResponseBound);
245
+ this.protocol.addHandler("room.tool_call_response_chunk", this._handleToolCallResponseChunkBound);
33
246
  this.sync = new sync_client_1.SyncClient({ room: this });
34
247
  this.storage = new storage_client_1.StorageClient({ room: this });
35
248
  this.developer = new developer_client_1.DeveloperClient({ room: this });
@@ -48,33 +261,820 @@ class RoomClient {
48
261
  get ready() {
49
262
  return this._ready.fut;
50
263
  }
51
- async start({ onDone, onError } = {}) {
52
- await this.messaging.start();
53
- this.sync.start({ onDone, onError });
54
- await this.ready;
264
+ get isConnected() {
265
+ return this._connected;
55
266
  }
56
- dispose() {
57
- for (const prKey of this._pendingRequests.keys()) {
58
- const pr = this._pendingRequests.get(prKey);
59
- pr?.reject(new Error("Disposed"));
60
- this._pendingRequests.delete(prKey);
267
+ get isClosed() {
268
+ return this._closing || this._terminalState != null || this._roomClosed.completed;
269
+ }
270
+ get isClosing() {
271
+ return this._closing;
272
+ }
273
+ get closeKind() {
274
+ return this._closeKind ?? this._protocolInstance.closeKind;
275
+ }
276
+ get closeReason() {
277
+ return this._closeReason ?? normalizeCloseReason(this._protocolInstance.closeReason);
278
+ }
279
+ get roomName() {
280
+ return this._roomName;
281
+ }
282
+ get roomUrl() {
283
+ return this._roomUrl;
284
+ }
285
+ get sessionId() {
286
+ return this._sessionId;
287
+ }
288
+ isActiveProtocol(protocol) {
289
+ return protocol === this._protocolInstance;
290
+ }
291
+ on(eventName, callback) {
292
+ this._eventEmitter.on(eventName, callback);
293
+ }
294
+ off(eventName, callback) {
295
+ this._eventEmitter.off(eventName, callback);
296
+ }
297
+ emit(event) {
298
+ this._eventsController.add(event);
299
+ this._eventEmitter.emit(event.name, event);
300
+ }
301
+ listen() {
302
+ return this._eventsController.stream;
303
+ }
304
+ async waitForClose() {
305
+ await this._roomClosed.fut;
306
+ }
307
+ async waitUntilConnected() {
308
+ while (!this._connected) {
309
+ this._raiseIfTerminal();
310
+ if (this._roomClosed.completed) {
311
+ this._raiseIfTerminal();
312
+ throw this._disconnectedError({
313
+ baseMessage: "room connection closed before reconnect completed",
314
+ });
315
+ }
316
+ await new Promise((resolve) => setTimeout(resolve, 50));
61
317
  }
62
- this.messaging.dispose();
63
- this.sync.dispose();
64
- for (const stream of this._toolCallStreams.values()) {
65
- stream.close();
318
+ }
319
+ async _waitUntilConnectedForMessages() {
320
+ while (!this._connected) {
321
+ this._raiseIfTerminalForMessages();
322
+ if (this._roomClosed.completed) {
323
+ this._raiseIfTerminalForMessages();
324
+ throw this._messageDisconnectedError({
325
+ baseMessage: "room connection closed before message send completed",
326
+ });
327
+ }
328
+ await new Promise((resolve) => setTimeout(resolve, 50));
329
+ }
330
+ }
331
+ _markConnected() {
332
+ this._connected = true;
333
+ this._closeKind = null;
334
+ this._closeReason = null;
335
+ }
336
+ _markDisconnected({ reason, kind, }) {
337
+ this._connected = false;
338
+ this._closeKind = kind;
339
+ this._closeReason = normalizeCloseReason(reason);
340
+ this._ignoredResponseLabels.clear();
341
+ }
342
+ _completeRoomClosed() {
343
+ if (!this._roomClosed.completed) {
344
+ this._roomClosed.complete();
345
+ }
346
+ }
347
+ _invokeTerminalCallbacks({ useErrorCallback, error, }) {
348
+ if (this._terminalCallbacksInvoked) {
349
+ return;
350
+ }
351
+ this._terminalCallbacksInvoked = true;
352
+ if (useErrorCallback) {
353
+ this._errorHandler?.(error);
354
+ return;
355
+ }
356
+ this._doneHandler?.();
357
+ }
358
+ _formatClosedMessage({ baseMessage, protocol, closeReason, }) {
359
+ const normalized = normalizeCloseReason(closeReason) ??
360
+ normalizeCloseReason((protocol ?? this._protocolInstance).closeReason);
361
+ if (normalized == null) {
362
+ return baseMessage;
363
+ }
364
+ return `${baseMessage}: ${normalized}`;
365
+ }
366
+ _connectionFailureReason(error) {
367
+ if (error instanceof room_server_client_1.RoomServerException) {
368
+ return normalizeCloseReason(error.message);
369
+ }
370
+ return normalizeCloseReason(String(error));
371
+ }
372
+ _protocolTerminalState({ protocol } = {}) {
373
+ return new RoomClientTerminalState({
374
+ requestMessage: this._formatClosedMessage({
375
+ baseMessage: "room connection closed before request completed",
376
+ protocol,
377
+ }),
378
+ toolCallMessage: this._formatClosedMessage({
379
+ baseMessage: "room connection closed before tool call completed",
380
+ protocol,
381
+ }),
382
+ messageSendMessage: this._formatClosedMessage({
383
+ baseMessage: "room connection closed before message send completed",
384
+ protocol,
385
+ }),
386
+ });
387
+ }
388
+ _clientClosedTerminalState() {
389
+ return new RoomClientTerminalState({
390
+ requestMessage: "room client was closed before request completed",
391
+ toolCallMessage: "room client was closed before tool call completed",
392
+ messageSendMessage: "room client was closed before message send completed",
393
+ });
394
+ }
395
+ _unexpectedCloseTerminalState({ closeReason, }) {
396
+ return new RoomClientTerminalState({
397
+ requestMessage: this._formatClosedMessage({
398
+ baseMessage: "room connection unexpectedly closed before request completed",
399
+ closeReason,
400
+ }),
401
+ toolCallMessage: this._formatClosedMessage({
402
+ baseMessage: "room connection unexpectedly closed before tool call completed",
403
+ closeReason,
404
+ }),
405
+ messageSendMessage: this._formatClosedMessage({
406
+ baseMessage: "room connection unexpectedly closed before message send completed",
407
+ closeReason,
408
+ }),
409
+ });
410
+ }
411
+ _setStartupTerminalState({ closeKind, closeReason, protocol, }) {
412
+ const normalizedCloseReason = normalizeCloseReason(closeReason);
413
+ this._closeKind = closeKind;
414
+ this._closeReason = normalizedCloseReason;
415
+ if (closeKind === protocol_1.ProtocolCloseKind.ERROR) {
416
+ this._setTerminalState({
417
+ state: this._unexpectedCloseTerminalState({ closeReason: normalizedCloseReason }),
418
+ });
419
+ }
420
+ else if (closeKind === protocol_1.ProtocolCloseKind.CLIENT) {
421
+ this._setTerminalState({ state: this._clientClosedTerminalState() });
422
+ }
423
+ else {
424
+ this._setTerminalState({ state: this._protocolTerminalState({ protocol }) });
425
+ }
426
+ if (!this._ready.completed) {
427
+ this._ready.completeError(this._startupException({
428
+ closeKind,
429
+ closeReason: normalizedCloseReason,
430
+ protocol,
431
+ }));
432
+ }
433
+ this._completeRoomClosed();
434
+ }
435
+ _setTerminalState({ state }) {
436
+ if (this._terminalState == null) {
437
+ this._terminalState = state;
438
+ }
439
+ return this._terminalState;
440
+ }
441
+ _raiseIfTerminal() {
442
+ if (this._terminalState != null) {
443
+ throw this._terminalState.requestError();
444
+ }
445
+ }
446
+ _raiseIfTerminalForMessages() {
447
+ if (this._terminalState != null) {
448
+ throw this._terminalState.messageSendError();
449
+ }
450
+ }
451
+ _disconnectedError({ baseMessage }) {
452
+ return new room_server_client_1.RoomServerException(this._formatClosedMessage({
453
+ baseMessage,
454
+ }));
455
+ }
456
+ _messageDisconnectedError({ baseMessage, }) {
457
+ return new room_server_client_1.RoomServerException(this._formatClosedMessage({
458
+ baseMessage,
459
+ }));
460
+ }
461
+ _startupException({ closeKind, closeReason, protocol, }) {
462
+ const baseMessage = closeKind === protocol_1.ProtocolCloseKind.ERROR
463
+ ? "room connection unexpectedly closed before the room became ready"
464
+ : closeKind === protocol_1.ProtocolCloseKind.CLIENT
465
+ ? "room client was closed before the room became ready"
466
+ : "room connection closed before the room became ready";
467
+ return new room_server_client_1.RoomServerException(this._formatClosedMessage({
468
+ baseMessage,
469
+ protocol,
470
+ closeReason,
471
+ }));
472
+ }
473
+ _finalizeInitialStartupRetryFailure({ retryResult, }) {
474
+ const closeKind = retryResult.closeKind ?? null;
475
+ if (closeKind == null) {
476
+ throw new Error("initial startup retry failure requires a close kind");
477
+ }
478
+ this._setStartupTerminalState({
479
+ closeKind,
480
+ closeReason: retryResult.closeReason ?? null,
481
+ protocol: this._protocolInstance,
482
+ });
483
+ throw this._startupException({
484
+ closeKind,
485
+ closeReason: retryResult.closeReason ?? null,
486
+ protocol: this._protocolInstance,
487
+ });
488
+ }
489
+ _coerceMessageSendError(error) {
490
+ if (this._terminalState == null) {
491
+ return error;
492
+ }
493
+ if (error.message === this._terminalState.requestMessage ||
494
+ error.message === this._terminalState.toolCallMessage) {
495
+ return this._terminalState.messageSendError();
496
+ }
497
+ return error;
498
+ }
499
+ _messageStopError() {
500
+ if (this._closing && this._terminalState != null) {
501
+ return this._terminalState.messageSendError();
502
+ }
503
+ return new room_server_client_1.RoomServerException("Cannot send messages because messaging has been stopped");
504
+ }
505
+ _failPendingRequests(error) {
506
+ if (this._pendingRequests.size === 0) {
507
+ return;
66
508
  }
509
+ const pending = [...this._pendingRequests.values()];
510
+ this._pendingRequests.clear();
511
+ for (const request of pending) {
512
+ if (!request.completed) {
513
+ request.completeError(error);
514
+ }
515
+ }
516
+ }
517
+ async _failToolCallStreams({ error, }) {
518
+ if (this._toolCallStreams.size === 0) {
519
+ return;
520
+ }
521
+ const streams = [...this._toolCallStreams.values()];
67
522
  this._toolCallStreams.clear();
68
- this.protocol.dispose();
523
+ for (const stream of streams) {
524
+ stream.add(new response_1.ErrorContent({ text: error.message }));
525
+ stream.close();
526
+ }
527
+ }
528
+ async _failPendingWork({ state, }) {
529
+ this._failPendingRequests(state.requestError());
530
+ await this._failToolCallStreams({ error: state.toolCallError() });
531
+ }
532
+ async _openProtocol({ initial }) {
533
+ const protocol = this._protocolInstance;
534
+ this._connectionReady = new completer_1.Completer();
535
+ this._localParticipantReady = new completer_1.Completer();
536
+ protocol.start({
537
+ onDone: () => {
538
+ const error = roomClosedBeforeReadyError(protocol);
539
+ if (!this._connectionReady.completed) {
540
+ this._connectionReady.completeError(error);
541
+ }
542
+ if (!this._localParticipantReady.completed) {
543
+ this._localParticipantReady.completeError(error);
544
+ }
545
+ if (!initial && !this._ready.completed) {
546
+ this._ready.completeError(error);
547
+ }
548
+ },
549
+ onError: (error) => {
550
+ const wrapped = wrapRoomConnectionError(error);
551
+ if (!this._connectionReady.completed) {
552
+ this._connectionReady.completeError(wrapped);
553
+ }
554
+ if (!this._localParticipantReady.completed) {
555
+ this._localParticipantReady.completeError(wrapped);
556
+ }
557
+ if (!initial && !this._ready.completed) {
558
+ this._ready.completeError(wrapped);
559
+ }
560
+ },
561
+ });
562
+ try {
563
+ await Promise.all([this._connectionReady.fut, this._localParticipantReady.fut]);
564
+ }
565
+ catch (error) {
566
+ const kind = protocol.closeKind ?? protocol_1.ProtocolCloseKind.ERROR;
567
+ if (!initial && kind !== protocol_1.ProtocolCloseKind.ERROR) {
568
+ throw new ProtocolStartupFailure({
569
+ kind,
570
+ reason: normalizeCloseReason(protocol.closeReason),
571
+ });
572
+ }
573
+ throw error;
574
+ }
575
+ }
576
+ async start({ onDone, onError, } = {}) {
577
+ if (this._entered) {
578
+ throw new room_server_client_1.RoomServerException("room client already started");
579
+ }
580
+ this._doneHandler = onDone;
581
+ this._errorHandler = onError;
582
+ try {
583
+ try {
584
+ await this._openProtocol({ initial: true });
585
+ }
586
+ catch (error) {
587
+ if (error instanceof ProtocolStartupFailure) {
588
+ if (error.kind !== protocol_1.ProtocolCloseKind.ERROR || this._reconnectTimeout === 0) {
589
+ this._setStartupTerminalState({
590
+ closeKind: error.kind,
591
+ closeReason: error.reason,
592
+ protocol: this._protocolInstance,
593
+ });
594
+ throw this._startupException({
595
+ closeKind: error.kind,
596
+ closeReason: error.reason,
597
+ protocol: this._protocolInstance,
598
+ });
599
+ }
600
+ await this._closeProtocol(this._protocolInstance);
601
+ const retryResult = await this._retryProtocolConnection({
602
+ disconnectReason: error.reason,
603
+ protocolFactoryFailureLogMessage: "unable to create replacement room protocol during initial startup",
604
+ attemptFailureLogMessage: "room startup attempt failed",
605
+ attempt: this._attemptInitialProtocolStartup.bind(this),
606
+ });
607
+ if (!retryResult.connected) {
608
+ this._finalizeInitialStartupRetryFailure({ retryResult });
609
+ }
610
+ }
611
+ else {
612
+ const nonRetryableCloseReason = nonRetryableConnectFailureReason(error);
613
+ if (nonRetryableCloseReason != null) {
614
+ this._finalizeInitialStartupRetryFailure({
615
+ retryResult: {
616
+ connected: false,
617
+ closeKind: protocol_1.ProtocolCloseKind.ERROR,
618
+ closeReason: nonRetryableCloseReason,
619
+ },
620
+ });
621
+ }
622
+ const closeKind = this._protocolInstance.closeKind;
623
+ const protocolCloseReason = normalizeCloseReason(this._protocolInstance.closeReason);
624
+ if (closeKind != null && closeKind !== protocol_1.ProtocolCloseKind.ERROR) {
625
+ this._setStartupTerminalState({
626
+ closeKind,
627
+ closeReason: protocolCloseReason,
628
+ protocol: this._protocolInstance,
629
+ });
630
+ throw this._startupException({
631
+ closeKind,
632
+ closeReason: protocolCloseReason,
633
+ protocol: this._protocolInstance,
634
+ });
635
+ }
636
+ const closeReason = this._connectionFailureReason(error);
637
+ if (this._reconnectTimeout === 0) {
638
+ this._setStartupTerminalState({
639
+ closeKind: protocol_1.ProtocolCloseKind.ERROR,
640
+ closeReason,
641
+ protocol: this._protocolInstance,
642
+ });
643
+ throw this._startupException({
644
+ closeKind: protocol_1.ProtocolCloseKind.ERROR,
645
+ closeReason,
646
+ protocol: this._protocolInstance,
647
+ });
648
+ }
649
+ console.debug("room startup attempt failed", error);
650
+ await this._closeProtocol(this._protocolInstance);
651
+ const retryResult = await this._retryProtocolConnection({
652
+ disconnectReason: closeReason,
653
+ protocolFactoryFailureLogMessage: "unable to create replacement room protocol during initial startup",
654
+ attemptFailureLogMessage: "room startup attempt failed",
655
+ attempt: this._attemptInitialProtocolStartup.bind(this),
656
+ });
657
+ if (!retryResult.connected) {
658
+ this._finalizeInitialStartupRetryFailure({ retryResult });
659
+ }
660
+ }
661
+ }
662
+ this.sync.start();
663
+ this.messaging.start();
664
+ this._entered = true;
665
+ this._markConnected();
666
+ this.messaging._onRoomReconnect();
667
+ this._lifecycleTask = this._connectionLifecycle();
668
+ }
669
+ catch (error) {
670
+ this.sync.dispose();
671
+ void this.messaging.stop();
672
+ this._protocolInstance.dispose();
673
+ throw error;
674
+ }
675
+ await this.ready;
676
+ }
677
+ async _completeReconnect() {
678
+ await this._openProtocol({ initial: false });
679
+ this._allowDisconnectedRequests = true;
680
+ try {
681
+ this._resendLocalAttributesNowait();
682
+ await this.sync._onRoomReconnect();
683
+ this.messaging._onRoomReconnect();
684
+ this._markConnected();
685
+ }
686
+ finally {
687
+ this._allowDisconnectedRequests = false;
688
+ }
689
+ }
690
+ _replaceProtocol(nextProtocol) {
691
+ const currentProtocol = this._protocolInstance;
692
+ this.protocol._unbind(currentProtocol);
693
+ this._protocolInstance = nextProtocol;
694
+ this.protocol._bind(nextProtocol);
695
+ }
696
+ _remainingReconnectTimeout(deadline) {
697
+ if (deadline == null) {
698
+ return null;
699
+ }
700
+ const remaining = deadline - Date.now();
701
+ return remaining <= 0 ? 0 : remaining;
702
+ }
703
+ async _attemptInitialProtocolStartup({ protocol, remaining, }) {
704
+ void protocol;
705
+ if (remaining == null) {
706
+ await this._openProtocol({ initial: false });
707
+ return;
708
+ }
709
+ await Promise.race([
710
+ this._openProtocol({ initial: false }),
711
+ new Promise((_resolve, reject) => {
712
+ setTimeout(() => reject(new Error("timeout")), remaining);
713
+ }),
714
+ ]);
715
+ }
716
+ async _attemptReconnect({ protocol, remaining, }) {
717
+ try {
718
+ if (remaining == null) {
719
+ await this._completeReconnect();
720
+ }
721
+ else {
722
+ await Promise.race([
723
+ this._completeReconnect(),
724
+ new Promise((_resolve, reject) => {
725
+ setTimeout(() => reject(new Error("timeout")), remaining);
726
+ }),
727
+ ]);
728
+ }
729
+ }
730
+ catch (error) {
731
+ if (error instanceof ProtocolStartupFailure) {
732
+ throw error;
733
+ }
734
+ if (error instanceof Error && error.message === "timeout") {
735
+ this._allowDisconnectedRequests = false;
736
+ await this.sync._onRoomDisconnect();
737
+ this.messaging._onRoomDisconnect({ reason: normalizeCloseReason(protocol.closeReason) });
738
+ throw error;
739
+ }
740
+ this._allowDisconnectedRequests = false;
741
+ await this.sync._onRoomDisconnect();
742
+ this.messaging._onRoomDisconnect({ reason: normalizeCloseReason(protocol.closeReason) });
743
+ throw error;
744
+ }
745
+ }
746
+ _formatDuration(milliseconds) {
747
+ if (milliseconds % 1000 === 0) {
748
+ return `${milliseconds / 1000}s`;
749
+ }
750
+ return `${milliseconds / 1000}s`;
751
+ }
752
+ _reconnectTimeoutReason({ disconnectReason, }) {
753
+ if (this._reconnectTimeout == null) {
754
+ throw new Error("reconnect timeout reason requires a configured timeout");
755
+ }
756
+ const timeoutDisplay = this._formatDuration(this._reconnectTimeout);
757
+ if (disconnectReason == null) {
758
+ return `room reconnect timed out after ${timeoutDisplay}`;
759
+ }
760
+ return `room reconnect timed out after ${timeoutDisplay} (${disconnectReason})`;
761
+ }
762
+ _timedOutRetryResult({ disconnectReason, }) {
763
+ if (this._reconnectTimeout == null) {
764
+ throw new Error("timed out retry result requires a configured timeout");
765
+ }
766
+ return {
767
+ connected: false,
768
+ closeKind: protocol_1.ProtocolCloseKind.ERROR,
769
+ closeReason: this._reconnectTimeoutReason({ disconnectReason }),
770
+ };
771
+ }
772
+ async _closeAfterUnexpectedDisconnect({ closeReason, }) {
773
+ const normalized = normalizeCloseReason(closeReason);
774
+ const state = this._unexpectedCloseTerminalState({ closeReason: normalized });
775
+ this._closeKind = protocol_1.ProtocolCloseKind.ERROR;
776
+ this._closeReason = normalized;
777
+ this._setTerminalState({ state });
778
+ this._completeRoomClosed();
779
+ this._invokeTerminalCallbacks({
780
+ useErrorCallback: true,
781
+ error: state.requestError(),
782
+ });
783
+ }
784
+ async _closeProtocol(protocol) {
785
+ protocol.dispose();
786
+ await protocol.waitForClose();
787
+ }
788
+ async _retryProtocolConnection({ disconnectReason, protocolFactoryFailureLogMessage, attemptFailureLogMessage, attempt, }) {
789
+ let failureReason = normalizeCloseReason(disconnectReason);
790
+ const recordFailureReason = (reason) => {
791
+ const normalizedReason = normalizeCloseReason(reason);
792
+ if (failureReason == null && normalizedReason != null) {
793
+ failureReason = normalizedReason;
794
+ }
795
+ };
796
+ const deadline = this._reconnectTimeout == null ? null : Date.now() + this._reconnectTimeout;
797
+ let firstAttempt = true;
798
+ while (!this._closing) {
799
+ if (firstAttempt) {
800
+ firstAttempt = false;
801
+ if (this._reconnectTimeout == null) {
802
+ await new Promise((resolve) => setTimeout(resolve, RoomClient.RECONNECT_RETRY_INTERVAL_MS));
803
+ }
804
+ }
805
+ else {
806
+ const remaining = this._remainingReconnectTimeout(deadline);
807
+ if (remaining != null && remaining === 0) {
808
+ return this._timedOutRetryResult({ disconnectReason: failureReason });
809
+ }
810
+ const delay = remaining == null
811
+ ? RoomClient.RECONNECT_RETRY_INTERVAL_MS
812
+ : Math.min(remaining, RoomClient.RECONNECT_RETRY_INTERVAL_MS);
813
+ if (delay > 0) {
814
+ await new Promise((resolve) => setTimeout(resolve, delay));
815
+ }
816
+ }
817
+ const remaining = this._remainingReconnectTimeout(deadline);
818
+ if (remaining != null && remaining === 0) {
819
+ return this._timedOutRetryResult({ disconnectReason: failureReason });
820
+ }
821
+ let nextProtocol;
822
+ try {
823
+ nextProtocol = this._protocolFactory();
824
+ }
825
+ catch (error) {
826
+ if (error instanceof protocol_1.ProtocolReconnectUnsupportedException) {
827
+ return {
828
+ connected: false,
829
+ closeKind: protocol_1.ProtocolCloseKind.ERROR,
830
+ closeReason: failureReason,
831
+ };
832
+ }
833
+ recordFailureReason(String(error));
834
+ console.debug(protocolFactoryFailureLogMessage, error);
835
+ continue;
836
+ }
837
+ this._replaceProtocol(nextProtocol);
838
+ try {
839
+ await attempt({ protocol: nextProtocol, remaining });
840
+ }
841
+ catch (error) {
842
+ if (error instanceof Error && error.message === "timeout") {
843
+ recordFailureReason(normalizeCloseReason(nextProtocol.closeReason));
844
+ await this._closeProtocol(nextProtocol);
845
+ return this._timedOutRetryResult({ disconnectReason: failureReason });
846
+ }
847
+ if (error instanceof ProtocolStartupFailure) {
848
+ recordFailureReason(error.reason);
849
+ await this._closeProtocol(nextProtocol);
850
+ if (error.kind !== protocol_1.ProtocolCloseKind.ERROR) {
851
+ return {
852
+ connected: false,
853
+ closeKind: error.kind,
854
+ closeReason: error.reason,
855
+ };
856
+ }
857
+ continue;
858
+ }
859
+ const nonRetryableCloseReason = nonRetryableConnectFailureReason(error);
860
+ if (nonRetryableCloseReason != null) {
861
+ await this._closeProtocol(nextProtocol);
862
+ return {
863
+ connected: false,
864
+ closeKind: protocol_1.ProtocolCloseKind.ERROR,
865
+ closeReason: nonRetryableCloseReason,
866
+ };
867
+ }
868
+ recordFailureReason(this._connectionFailureReason(error));
869
+ console.debug(attemptFailureLogMessage, error);
870
+ await this._closeProtocol(nextProtocol);
871
+ continue;
872
+ }
873
+ return { connected: true };
874
+ }
875
+ return {
876
+ connected: false,
877
+ closeKind: protocol_1.ProtocolCloseKind.CLIENT,
878
+ closeReason: this.closeReason,
879
+ };
880
+ }
881
+ async _reconnect({ disconnectReason, }) {
882
+ const retryResult = await this._retryProtocolConnection({
883
+ disconnectReason,
884
+ protocolFactoryFailureLogMessage: "unable to create replacement room protocol",
885
+ attemptFailureLogMessage: "room reconnect attempt failed",
886
+ attempt: this._attemptReconnect.bind(this),
887
+ });
888
+ if (retryResult.connected) {
889
+ this._emitStatus({
890
+ status: "reconnected",
891
+ message: "room connection restored",
892
+ });
893
+ return true;
894
+ }
895
+ const closeKind = retryResult.closeKind ?? null;
896
+ if (closeKind === protocol_1.ProtocolCloseKind.ERROR) {
897
+ const closeReason = retryResult.closeReason ?? null;
898
+ if (closeReason != null && closeReason.startsWith("room reconnect timed out after")) {
899
+ console.warn(`${closeReason}; closing room client`);
900
+ }
901
+ await this._closeAfterUnexpectedDisconnect({ closeReason });
902
+ return false;
903
+ }
904
+ if (closeKind == null) {
905
+ throw new Error("reconnect failure requires a close kind");
906
+ }
907
+ const state = this._protocolTerminalState({ protocol: this._protocolInstance });
908
+ this._setTerminalState({ state });
909
+ this._closeKind = closeKind;
910
+ this._closeReason = normalizeCloseReason(retryResult.closeReason ?? null);
911
+ this._completeRoomClosed();
912
+ this._invokeTerminalCallbacks({ useErrorCallback: false });
913
+ return false;
914
+ }
915
+ async _connectionLifecycle() {
916
+ while (true) {
917
+ const protocol = this._protocolInstance;
918
+ await protocol.done;
919
+ const closeKind = protocol.closeKind ?? protocol_1.ProtocolCloseKind.ERROR;
920
+ const closeReason = normalizeCloseReason(protocol.closeReason);
921
+ const state = this._protocolTerminalState({ protocol });
922
+ if (this._closing) {
923
+ this._completeRoomClosed();
924
+ return;
925
+ }
926
+ if (closeKind !== protocol_1.ProtocolCloseKind.ERROR) {
927
+ this._setTerminalState({ state });
928
+ }
929
+ this._markDisconnected({ reason: closeReason, kind: closeKind });
930
+ this._emitStatus({
931
+ status: "disconnected",
932
+ message: closeReason ?? "room connection lost",
933
+ });
934
+ await this.sync._onRoomDisconnect();
935
+ this.messaging._onRoomDisconnect({ reason: closeReason });
936
+ await this._failPendingWork({ state });
937
+ await this._closeProtocol(protocol);
938
+ if (closeKind === protocol_1.ProtocolCloseKind.ERROR) {
939
+ if (this._reconnectTimeout === 0) {
940
+ if (closeReason == null) {
941
+ console.warn("room connection lost; automatic reconnect disabled");
942
+ }
943
+ else {
944
+ console.warn(`room connection lost (${closeReason}); automatic reconnect disabled`);
945
+ }
946
+ await this._closeAfterUnexpectedDisconnect({ closeReason });
947
+ return;
948
+ }
949
+ if (closeReason == null) {
950
+ console.warn("room connection lost; automatically attempting to reconnect");
951
+ }
952
+ else {
953
+ console.warn(`room connection lost (${closeReason}); automatically attempting to reconnect`);
954
+ }
955
+ if (await this._reconnect({ disconnectReason: closeReason })) {
956
+ continue;
957
+ }
958
+ return;
959
+ }
960
+ this._closeKind = closeKind;
961
+ this._closeReason = closeReason;
962
+ this._completeRoomClosed();
963
+ this._invokeTerminalCallbacks({ useErrorCallback: false });
964
+ return;
965
+ }
966
+ }
967
+ dispose() {
968
+ this._closing = true;
969
+ this._markDisconnected({
970
+ reason: this.closeReason,
971
+ kind: this.closeKind ?? protocol_1.ProtocolCloseKind.CLIENT,
972
+ });
973
+ const closingState = this._clientClosedTerminalState();
974
+ this._setTerminalState({ state: closingState });
975
+ this._failPendingRequests(closingState.requestError());
976
+ void this._failToolCallStreams({ error: closingState.toolCallError() });
977
+ this.sync.dispose();
978
+ void this.messaging.stop();
979
+ this._protocolInstance.dispose();
980
+ this._entered = false;
981
+ this._closeKind = protocol_1.ProtocolCloseKind.CLIENT;
982
+ this._completeRoomClosed();
983
+ this._invokeTerminalCallbacks({ useErrorCallback: false });
69
984
  this._localParticipant = null;
70
985
  }
986
+ _sendProtocolNowait({ type, data, label, messageId, expectResponse = false, }) {
987
+ try {
988
+ this._raiseIfTerminal();
989
+ }
990
+ catch (error) {
991
+ console.debug(`skipping ${label} because the room is closed`, error);
992
+ return null;
993
+ }
994
+ if (this._entered && !this._connected && !this._allowDisconnectedRequests) {
995
+ console.debug(`skipping ${label} while room is disconnected`);
996
+ return null;
997
+ }
998
+ const protocol = this._protocolInstance;
999
+ const resolvedMessageId = messageId ?? protocol.getNextMessageId();
1000
+ if (expectResponse) {
1001
+ this._ignoredResponseLabels.set(resolvedMessageId, label);
1002
+ }
1003
+ try {
1004
+ protocol.sendNowait(type, data, { id: resolvedMessageId });
1005
+ }
1006
+ catch (error) {
1007
+ this._ignoredResponseLabels.delete(resolvedMessageId);
1008
+ if (this.isClosed) {
1009
+ console.debug(`skipping ${label} because the room is closed`, error);
1010
+ }
1011
+ else {
1012
+ console.warn(`unable to queue ${label}`, error);
1013
+ }
1014
+ return null;
1015
+ }
1016
+ return resolvedMessageId;
1017
+ }
1018
+ _sendRoomRequestNowait(type, request, { data, label, expectResponse = false, }) {
1019
+ return this._sendProtocolNowait({
1020
+ type,
1021
+ data: (0, utils_1.packMessage)(request, data),
1022
+ label,
1023
+ expectResponse,
1024
+ });
1025
+ }
1026
+ invokeNowait({ toolkit, tool, input, participantId, onBehalfOfId, callerContext, }) {
1027
+ const resolvedInput = input ?? new response_1.EmptyContent();
1028
+ const packedInput = (0, utils_1.unpackMessage)(resolvedInput.pack());
1029
+ const request = {
1030
+ toolkit,
1031
+ tool,
1032
+ participant_id: participantId,
1033
+ on_behalf_of_id: onBehalfOfId,
1034
+ caller_context: callerContext,
1035
+ tool_call_id: `${Date.now()}-${Math.random().toString(16).slice(2)}`,
1036
+ arguments: packedInput[0],
1037
+ };
1038
+ this._sendRoomRequestNowait("room.invoke_tool", request, {
1039
+ data: packedInput[1].length > 0 ? packedInput[1] : undefined,
1040
+ label: `${toolkit}.${tool}`,
1041
+ expectResponse: true,
1042
+ });
1043
+ }
1044
+ _sendLocalAttributesNowait(attributes) {
1045
+ this._sendProtocolNowait({
1046
+ type: "set_attributes",
1047
+ data: (0, utils_1.packMessage)(attributes),
1048
+ label: "local participant attribute update",
1049
+ });
1050
+ }
1051
+ _resendLocalAttributesNowait() {
1052
+ const localParticipant = this._localParticipant;
1053
+ if (localParticipant == null) {
1054
+ return;
1055
+ }
1056
+ const attributes = localParticipant._attributesSnapshot();
1057
+ if (Object.keys(attributes).length === 0) {
1058
+ return;
1059
+ }
1060
+ this._sendLocalAttributesNowait(attributes);
1061
+ }
71
1062
  async sendRequest(type, request, data) {
72
- const requestId = this.protocol.getNextMessageId();
73
- const pr = new completer_1.Completer();
74
- this._pendingRequests.set(requestId, pr);
75
- const message = (0, utils_1.packMessage)(request, data);
76
- await this.protocol.send(type, message, requestId);
77
- return await pr.fut;
1063
+ this._raiseIfTerminal();
1064
+ if (this._entered && !this._connected && !this._allowDisconnectedRequests) {
1065
+ throw this._disconnectedError({ baseMessage: "room connection is disconnected" });
1066
+ }
1067
+ const requestId = this._protocolInstance.getNextMessageId();
1068
+ const completer = new completer_1.Completer();
1069
+ this._pendingRequests.set(requestId, completer);
1070
+ try {
1071
+ await this._protocolInstance.send(type, (0, utils_1.packMessage)(request, data), requestId);
1072
+ return await completer.fut;
1073
+ }
1074
+ catch (error) {
1075
+ this._pendingRequests.delete(requestId);
1076
+ throw error;
1077
+ }
78
1078
  }
79
1079
  async call(params) {
80
1080
  await this.sendRequest("room.call", params);
@@ -149,7 +1149,7 @@ class RoomClient {
149
1149
  return await this.sendRequest("room.invoke_tool", request, requestData);
150
1150
  }
151
1151
  async invokeWithStreamInput(params) {
152
- const toolCallId = `${Date.now()}-${this.protocol.getNextMessageId()}-${Math.random().toString(16).slice(2)}`;
1152
+ const toolCallId = `${Date.now()}-${Math.random().toString(16).slice(2)}`;
153
1153
  const request = {
154
1154
  toolkit: params.toolkit,
155
1155
  tool: params.tool,
@@ -180,7 +1180,7 @@ class RoomClient {
180
1180
  }
181
1181
  }
182
1182
  async invokeStream(params) {
183
- const toolCallId = `${Date.now()}-${this.protocol.getNextMessageId()}-${Math.random().toString(16).slice(2)}`;
1183
+ const toolCallId = `${Date.now()}-${Math.random().toString(16).slice(2)}`;
184
1184
  const controller = new stream_controller_1.StreamController();
185
1185
  const responseIterator = controller.stream[Symbol.asyncIterator]();
186
1186
  this._toolCallStreams.set(toolCallId, controller);
@@ -202,7 +1202,7 @@ class RoomClient {
202
1202
  const requestTask = this._streamInvokeToolRequestChunks(toolCallId, params.input);
203
1203
  void requestTask.catch((error) => {
204
1204
  const stream = this._toolCallStreams.get(toolCallId);
205
- if (!stream) {
1205
+ if (stream == null) {
206
1206
  return;
207
1207
  }
208
1208
  stream.add(new response_1.ErrorContent({ text: `request stream failed: ${String(error)}` }));
@@ -252,8 +1252,8 @@ class RoomClient {
252
1252
  }
253
1253
  return new response_1.JsonContent({ json: { chunk } });
254
1254
  }
255
- async _handleToolCallResponseChunk(protocol, messageId, type, data) {
256
- if (!data) {
1255
+ async _handleToolCallResponseChunk(protocol, _messageId, _type, data) {
1256
+ if (!this.isActiveProtocol(protocol)) {
257
1257
  return;
258
1258
  }
259
1259
  const [header, payload] = (0, utils_1.unpackMessage)(data);
@@ -262,7 +1262,7 @@ class RoomClient {
262
1262
  return;
263
1263
  }
264
1264
  const stream = this._toolCallStreams.get(toolCallId);
265
- if (!stream) {
1265
+ if (stream == null) {
266
1266
  return;
267
1267
  }
268
1268
  const content = this._decodeToolCallContent({ header, payload });
@@ -272,52 +1272,92 @@ class RoomClient {
272
1272
  this._toolCallStreams.delete(toolCallId);
273
1273
  }
274
1274
  }
275
- async _handleResponse(protocol, messageId, type, data) {
276
- if (!data) {
277
- console.error("No data in response");
1275
+ async _handleResponse(protocol, messageId, _type, data) {
1276
+ if (!this.isActiveProtocol(protocol)) {
278
1277
  return;
279
1278
  }
280
1279
  const response = (0, response_1.unpackContent)(data);
281
- console.log("GOT RESPONSE", response);
282
- if (!response) {
283
- console.error("No response");
284
- return;
285
- }
286
- if (this._pendingRequests.has(messageId)) {
287
- const pr = this._pendingRequests.get(messageId);
1280
+ const pending = this._pendingRequests.get(messageId);
1281
+ if (pending != null) {
288
1282
  this._pendingRequests.delete(messageId);
289
1283
  if (response instanceof response_1.ErrorContent) {
290
- pr.reject(new room_server_client_1.RoomServerException(response.text, response.code));
1284
+ pending.completeError(new room_server_client_1.RoomServerException(response.text, response.code));
291
1285
  }
292
1286
  else {
293
- pr.resolve(response);
1287
+ pending.complete(response);
294
1288
  }
1289
+ return;
295
1290
  }
296
- else {
297
- console.warn(`Received a response for a request that is not pending ${messageId}`);
1291
+ const ignoredLabel = this._ignoredResponseLabels.get(messageId);
1292
+ if (ignoredLabel != null) {
1293
+ this._ignoredResponseLabels.delete(messageId);
1294
+ if (response instanceof response_1.ErrorContent) {
1295
+ console.warn(`one-way room request failed for ${ignoredLabel}: ${response.text}`);
1296
+ }
298
1297
  }
299
1298
  }
300
- async _handleRoomReady(protocol, messageId, type, data) {
301
- const [message, _] = (0, utils_1.unpackMessage)(data);
302
- this._ready.complete(message["room_name"]);
1299
+ async _handleRoomStatus(protocol, _messageId, _type, data) {
1300
+ if (!this.isActiveProtocol(protocol)) {
1301
+ return;
1302
+ }
1303
+ const [payload] = (0, utils_1.unpackMessage)(data);
1304
+ this.emit(room_event_1.RoomStatusEvent.fromJson(payload));
1305
+ }
1306
+ async _handleRoomReady(protocol, _messageId, _type, data) {
1307
+ if (!this.isActiveProtocol(protocol)) {
1308
+ return;
1309
+ }
1310
+ const [message] = (0, utils_1.unpackMessage)(data);
1311
+ this._roomName = typeof message["room_name"] === "string" ? message["room_name"] : null;
1312
+ this._roomUrl = typeof message["room_url"] === "string" ? message["room_url"] : null;
1313
+ this._sessionId = typeof message["session_id"] === "string" ? message["session_id"] : null;
1314
+ if (!this._ready.completed) {
1315
+ this._ready.complete();
1316
+ }
1317
+ if (!this._connectionReady.completed) {
1318
+ this._connectionReady.complete();
1319
+ }
303
1320
  }
304
1321
  _onParticipantInit(participantId, attributes) {
305
- this._localParticipant = new participant_1.LocalParticipant(this, participantId);
306
- for (const k in attributes) {
307
- this._localParticipant.setAttribute(k, attributes[k]);
1322
+ if (this._localParticipant == null) {
1323
+ this._localParticipant = new participant_1.LocalParticipant(this, participantId);
1324
+ this._localParticipant._setAttributes(attributes);
1325
+ }
1326
+ else {
1327
+ const merged = { ...attributes, ...this._localParticipant._attributesSnapshot() };
1328
+ this._localParticipant._replaceIdentity({
1329
+ participantId,
1330
+ attributes: merged,
1331
+ });
1332
+ }
1333
+ if (!this._localParticipantReady.completed) {
1334
+ this._localParticipantReady.complete();
308
1335
  }
309
1336
  }
310
- async _handleParticipant(protocol, messageId, type, data) {
311
- const [message, _] = (0, utils_1.unpackMessage)(data);
1337
+ async _handleParticipant(protocol, _messageId, _type, data) {
1338
+ if (!this.isActiveProtocol(protocol)) {
1339
+ return;
1340
+ }
1341
+ const [message] = (0, utils_1.unpackMessage)(data);
312
1342
  switch (message["type"]) {
313
- case "init": this._onParticipantInit(message["participantId"], message["attributes"]);
1343
+ case "init": {
1344
+ const participantId = message["participantId"];
1345
+ const attributes = message["attributes"];
1346
+ if (typeof participantId === "string" &&
1347
+ typeof attributes === "object" &&
1348
+ attributes !== null &&
1349
+ !Array.isArray(attributes)) {
1350
+ this._onParticipantInit(participantId, attributes);
1351
+ }
1352
+ break;
1353
+ }
1354
+ default:
1355
+ break;
314
1356
  }
315
1357
  }
316
- emit(event) {
317
- this._eventsController.add(event);
318
- }
319
- listen() {
320
- return this._eventsController.stream;
1358
+ _emitStatus({ status, message, }) {
1359
+ this.emit(new room_event_1.RoomStatusEvent({ status, message }));
321
1360
  }
322
1361
  }
323
1362
  exports.RoomClient = RoomClient;
1363
+ RoomClient.RECONNECT_RETRY_INTERVAL_MS = 1000;