@band-ai/sdk 0.1.4 → 0.1.10

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 (73) hide show
  1. package/README.md +372 -0
  2. package/dist/{ClaudeSDKAdapter-CRq4R-WB.d.cts → ClaudeSDKAdapter-DfjaI5ke.d.cts} +9 -7
  3. package/dist/{ClaudeSDKAdapter-BHOkjN3X.d.ts → ClaudeSDKAdapter-DonPGJ43.d.ts} +9 -7
  4. package/dist/{acp-server-BdRz9_UN.d.cts → acp-server-CUBXIiG6.d.cts} +2 -2
  5. package/dist/{acp-server-B1Da7L6u.d.ts → acp-server-NZAeg-WO.d.ts} +2 -2
  6. package/dist/adapters.cjs +494 -106
  7. package/dist/adapters.d.cts +12 -12
  8. package/dist/adapters.d.ts +12 -12
  9. package/dist/adapters.js +7 -7
  10. package/dist/{backends-SVMT-7l4.d.cts → backends-DYtEhCZG.d.cts} +7 -2
  11. package/dist/{backends-CzDbiQar.d.ts → backends-oYAb7EU-.d.ts} +7 -2
  12. package/dist/{chunk-MSL6CWHY.js → chunk-2BHHR6YW.js} +17 -16
  13. package/dist/{chunk-EDPKUUJL.js → chunk-6RXWL6TC.js} +9 -1
  14. package/dist/{chunk-SFVKOEQH.js → chunk-DNPVCR3S.js} +92 -14
  15. package/dist/{chunk-I5EZG52P.js → chunk-FP237EAG.js} +424 -136
  16. package/dist/{chunk-RHVH2GZR.js → chunk-J3M3PFFY.js} +81 -31
  17. package/dist/{chunk-GVEQ3RAH.js → chunk-MLROFDKR.js} +132 -23
  18. package/dist/{chunk-INA6YZTZ.js → chunk-RDM4TE4B.js} +32 -30
  19. package/dist/chunk-US226AS4.js +130 -0
  20. package/dist/{chunk-W5NDZSYE.js → chunk-XA4N2JPH.js} +1 -1
  21. package/dist/converters.cjs +469 -13
  22. package/dist/converters.d.cts +5 -5
  23. package/dist/converters.d.ts +5 -5
  24. package/dist/converters.js +3 -2
  25. package/dist/core.cjs +45 -2
  26. package/dist/core.d.cts +4 -3
  27. package/dist/core.d.ts +4 -3
  28. package/dist/core.js +5 -1
  29. package/dist/disconnectReason-IuADl6TH.d.cts +34 -0
  30. package/dist/disconnectReason-IuADl6TH.d.ts +34 -0
  31. package/dist/{dtos-DUpbIu8k.d.cts → dtos-Dd_fTfLn.d.cts} +26 -6
  32. package/dist/{dtos-DUpbIu8k.d.ts → dtos-Dd_fTfLn.d.ts} +26 -6
  33. package/dist/index.cjs +1089 -269
  34. package/dist/index.d.cts +16 -15
  35. package/dist/index.d.ts +16 -15
  36. package/dist/index.js +14 -8
  37. package/dist/linear.cjs +985 -53
  38. package/dist/linear.d.cts +137 -7
  39. package/dist/linear.d.ts +137 -7
  40. package/dist/linear.js +974 -53
  41. package/dist/mcp-claude.cjs +303 -32
  42. package/dist/mcp-claude.d.cts +3 -3
  43. package/dist/mcp-claude.d.ts +3 -3
  44. package/dist/mcp-claude.js +210 -12
  45. package/dist/mcp.cjs +313 -45
  46. package/dist/mcp.d.cts +5 -5
  47. package/dist/mcp.d.ts +5 -5
  48. package/dist/mcp.js +3 -3
  49. package/dist/{opencode-BBcDchcN.d.cts → opencode--iA5-KYk.d.cts} +2 -2
  50. package/dist/{opencode-DthQQUsZ.d.ts → opencode-YNacv-eJ.d.ts} +2 -2
  51. package/dist/{pagination-CcDkVFxK.d.ts → pagination-BBLsYLhT.d.ts} +2 -2
  52. package/dist/{pagination-BPiys10t.d.cts → pagination-CqidD-L_.d.cts} +2 -2
  53. package/dist/{protocols-DrhzOH6K.d.ts → protocols-BF6jamIl.d.ts} +1 -1
  54. package/dist/{protocols-CInhtFpA.d.cts → protocols-D2bTrQ_t.d.cts} +1 -1
  55. package/dist/rest.d.cts +5 -5
  56. package/dist/rest.d.ts +5 -5
  57. package/dist/runtime.cjs +721 -172
  58. package/dist/runtime.d.cts +15 -10
  59. package/dist/runtime.d.ts +15 -10
  60. package/dist/runtime.js +11 -8
  61. package/dist/{schemas-vos1AaBc.d.ts → schemas-B1Fd3oUM.d.ts} +17 -8
  62. package/dist/{schemas-BpEBkq5V.d.cts → schemas-CI0qgcp_.d.cts} +17 -8
  63. package/dist/{sdk-CcnQ5OUC.d.ts → sdk-CyqN1W5g.d.ts} +32 -2
  64. package/dist/{sdk-CwTAxhwI.d.cts → sdk-D2rTDU_o.d.cts} +32 -2
  65. package/dist/{simpleAdapter-DqhvjsGr.d.cts → simpleAdapter-C_f-01Cu.d.cts} +1 -1
  66. package/dist/{simpleAdapter-9OE_p6QW.d.ts → simpleAdapter-qQt-tSz4.d.ts} +1 -1
  67. package/dist/testing.d.cts +3 -3
  68. package/dist/testing.d.ts +3 -3
  69. package/dist/{types-CovcHpYf.d.cts → types-CKqrhGB1.d.cts} +2 -2
  70. package/dist/{types-CxII3XkA.d.ts → types-D3AsUnd0.d.ts} +1 -1
  71. package/dist/{types-7jVzOeOP.d.ts → types-DNySTI_8.d.ts} +2 -2
  72. package/dist/{types-CSHFipIc.d.cts → types-DtMms5Fi.d.cts} +1 -1
  73. package/package.json +22 -21
@@ -4,15 +4,37 @@ import {
4
4
  SYNTHETIC_CONTACT_EVENTS_SENDER_NAME,
5
5
  SYNTHETIC_SENDER_TYPE,
6
6
  buildParticipantsMessage
7
- } from "./chunk-INA6YZTZ.js";
7
+ } from "./chunk-RDM4TE4B.js";
8
8
  import {
9
9
  CHAT_EVENT_TYPES,
10
10
  CHAT_TOOL_NAMES,
11
+ MEMORY_LIST_SCOPES,
12
+ MEMORY_SEGMENTS,
13
+ MEMORY_STATUSES,
14
+ MEMORY_STORE_SCOPES,
15
+ MEMORY_SYSTEMS,
11
16
  MEMORY_TOOL_NAMES,
17
+ MEMORY_TYPES,
12
18
  TOOL_MODELS,
13
19
  assertChatEventType,
14
- getToolDescription
15
- } from "./chunk-GVEQ3RAH.js";
20
+ expectedList,
21
+ expectedMemoryTypesForSystem,
22
+ getToolDescription,
23
+ isMemoryListScope,
24
+ isMemorySegment,
25
+ isMemoryStatus,
26
+ isMemoryStoreScope,
27
+ isMemorySystem,
28
+ isMemoryType,
29
+ isMemoryTypeForSystem,
30
+ memoryTypeForSystemError
31
+ } from "./chunk-MLROFDKR.js";
32
+ import {
33
+ WebSocketDisconnectError,
34
+ genericCloseReason,
35
+ parseSupersedeDisconnectReason,
36
+ parseUpgradeDisconnectReason
37
+ } from "./chunk-US226AS4.js";
16
38
  import {
17
39
  DEFAULT_REQUEST_OPTIONS,
18
40
  FernRestAdapter,
@@ -143,55 +165,117 @@ var payloadSchemas = {
143
165
 
144
166
  // src/platform/streaming/PhoenixChannelsTransport.ts
145
167
  import { Socket } from "phoenix";
168
+
169
+ // src/platform/streaming/nodeWebSocketFactory.ts
146
170
  import { WebSocket as NodeWebSocket } from "ws";
171
+ function createNodeWebSocketFactory(headers) {
172
+ const Constructor = NodeWebSocket;
173
+ class ThenvoiNodeWebSocket {
174
+ constructor(address, protocols) {
175
+ const socket = new Constructor(address, protocols, headers ? { headers } : void 0);
176
+ socket.once("unexpected-response", (request, response) => {
177
+ void emitUpgradeError(socket, request, response);
178
+ });
179
+ return socket;
180
+ }
181
+ }
182
+ return ThenvoiNodeWebSocket;
183
+ }
184
+ async function emitUpgradeError(websocket, request, response) {
185
+ const body = await readResponseBody(response);
186
+ const error = new Error(
187
+ `Unexpected server response: ${response.statusCode ?? "unknown"}`
188
+ );
189
+ error.status = response.statusCode;
190
+ error.body = body;
191
+ error.headers = response.headers;
192
+ request.destroy();
193
+ websocket.emit("error", error);
194
+ websocket.emit("close", 1006, Buffer.alloc(0));
195
+ }
196
+ async function readResponseBody(response) {
197
+ const chunks = [];
198
+ for await (const chunk of response) {
199
+ chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
200
+ }
201
+ return Buffer.concat(chunks).toString("utf8");
202
+ }
203
+
204
+ // src/platform/streaming/PhoenixChannelsTransport.ts
147
205
  var PhoenixChannelsTransport = class {
148
206
  socket;
207
+ agentId;
149
208
  channels = /* @__PURE__ */ new Map();
150
209
  channelRefs = /* @__PURE__ */ new Map();
151
210
  pendingJoins = /* @__PURE__ */ new Map();
152
211
  logger;
212
+ onTerminalDisconnect;
153
213
  onHandlerError;
154
214
  connected = false;
155
215
  connectPromise = null;
156
216
  connectResolve = null;
217
+ connectReject = null;
218
+ lastDisconnectReason = null;
219
+ terminalDisconnectError = null;
220
+ runForeverWaiters = /* @__PURE__ */ new Set();
221
+ stoppingReconnect = false;
222
+ suppressNextCloseReason = false;
157
223
  constructor(options) {
158
224
  this.logger = options.logger ?? new NoopLogger();
225
+ this.agentId = options.agentId;
226
+ this.onTerminalDisconnect = options.onTerminalDisconnect;
159
227
  let wsUrl = options.wsUrl;
160
228
  if (wsUrl.endsWith("/websocket")) {
161
229
  wsUrl = wsUrl.slice(0, -"/websocket".length);
162
230
  }
231
+ const reconnectAfterMs = options.reconnectAfterMs ?? ((tries) => [1e3, 2e3, 5e3, 1e4, 3e4][tries - 1] ?? 3e4);
163
232
  this.socket = new Socket(wsUrl, {
164
233
  params: {
165
- api_key: options.apiKey,
166
- agent_id: options.agentId
234
+ agent_id: options.agentId,
235
+ ...options.conflictPolicy ? { on_conflict: options.conflictPolicy } : {}
167
236
  },
168
237
  heartbeatIntervalMs: options.heartbeatIntervalMs,
169
- reconnectAfterMs: options.reconnectAfterMs ?? ((tries) => [1e3, 2e3, 5e3, 1e4, 3e4][tries - 1] ?? 3e4),
170
- transport: options.websocketFactory ?? resolveWebSocketFactory()
238
+ reconnectAfterMs: (tries) => {
239
+ if (this.terminalDisconnectError) {
240
+ return Number.POSITIVE_INFINITY;
241
+ }
242
+ return reconnectAfterMs(tries);
243
+ },
244
+ transport: options.websocketFactory ?? resolveWebSocketFactory(options.apiKey)
171
245
  });
172
246
  this.socket.onOpen(() => {
173
- this.connected = true;
174
- this.connectResolve?.();
175
- this.connectResolve = null;
176
- this.logger.info("Phoenix socket opened", {
177
- channels: getSocketChannelCount(this.socket)
178
- });
247
+ void this.handleOpen();
179
248
  });
180
249
  this.socket.onClose((event) => {
181
- this.connected = false;
182
- if (getSocketChannelCount(this.socket) === 0) {
183
- this.socket.disconnect();
184
- }
185
- this.logger.info("Phoenix socket closed", {
186
- code: event?.code ?? null,
187
- reason: event?.reason ?? null
188
- });
250
+ this.recordSocketClose(event);
189
251
  });
190
252
  this.socket.onError((event) => {
253
+ const errorEvent = unwrapErrorEvent(event);
254
+ const upgradeReason = parseUpgradeDisconnectReason(errorEvent);
255
+ if (upgradeReason) {
256
+ if (upgradeReason.retryable) {
257
+ this.lastDisconnectReason = upgradeReason;
258
+ this.connectReject?.(new WebSocketDisconnectError(upgradeReason));
259
+ this.stopReconnectIfNoChannels({ suppressCloseReason: true });
260
+ } else {
261
+ this.recordTerminalDisconnect(upgradeReason);
262
+ }
263
+ this.logger.warn("Phoenix socket upgrade failed", {
264
+ reason: upgradeReason
265
+ });
266
+ return;
267
+ }
268
+ this.connectReject?.(
269
+ new TransportError("Phoenix socket connection failed", errorEvent)
270
+ );
271
+ this.stopReconnectIfNoChannels({ suppressCloseReason: true });
191
272
  this.logger.warn("Phoenix socket error", { event });
192
273
  });
193
274
  }
194
275
  async connect() {
276
+ if (this.terminalDisconnectError) {
277
+ throw this.terminalDisconnectError;
278
+ }
195
279
  if (this.connected) {
196
280
  return;
197
281
  }
@@ -215,15 +299,30 @@ var PhoenixChannelsTransport = class {
215
299
  await this.connectPromise;
216
300
  }
217
301
  async disconnect() {
218
- for (const topic of this.channels.keys()) {
219
- await this.leave(topic);
220
- }
302
+ const results = await Promise.allSettled(
303
+ [...this.channels.keys()].map((topic) => this.leave(topic))
304
+ );
221
305
  this.socket.disconnect();
222
306
  this.connected = false;
307
+ const failures = [];
308
+ for (const result of results) {
309
+ if (result.status === "rejected") {
310
+ failures.push(result.reason);
311
+ }
312
+ }
313
+ if (failures.length > 0) {
314
+ throw new AggregateError(
315
+ failures,
316
+ "Failed to leave one or more Phoenix topics during disconnect"
317
+ );
318
+ }
223
319
  }
224
320
  isConnected() {
225
321
  return this.connected;
226
322
  }
323
+ getDisconnectReason() {
324
+ return this.lastDisconnectReason;
325
+ }
227
326
  async join(topic, handlers) {
228
327
  if (this.channels.has(topic)) {
229
328
  return;
@@ -259,7 +358,10 @@ var PhoenixChannelsTransport = class {
259
358
  channel.join().receive("ok", () => resolve()).receive(
260
359
  "error",
261
360
  (error) => reject(new TransportError(`Failed to join topic ${topic}`, error))
262
- ).receive("timeout", () => reject(new TransportError(`Timeout joining topic ${topic}`)));
361
+ ).receive(
362
+ "timeout",
363
+ () => reject(new TransportError(`Timeout joining topic ${topic}`))
364
+ );
263
365
  });
264
366
  } catch (error) {
265
367
  for (const [event, ref] of refs) {
@@ -287,19 +389,127 @@ var PhoenixChannelsTransport = class {
287
389
  channel.leave().receive("ok", () => resolve()).receive(
288
390
  "error",
289
391
  (error) => reject(new TransportError(`Failed to leave topic ${topic}`, error))
290
- ).receive("timeout", () => reject(new TransportError(`Timeout leaving topic ${topic}`)));
392
+ ).receive(
393
+ "timeout",
394
+ () => reject(new TransportError(`Timeout leaving topic ${topic}`))
395
+ );
291
396
  });
292
397
  this.channels.delete(topic);
293
398
  this.logger.debug("Left topic", { topic });
294
399
  }
295
400
  async runForever(signal) {
401
+ if (this.terminalDisconnectError) {
402
+ throw this.terminalDisconnectError;
403
+ }
296
404
  if (signal.aborted) {
297
405
  return;
298
406
  }
299
- await new Promise((resolve) => {
300
- signal.addEventListener("abort", () => resolve(), { once: true });
407
+ await new Promise((resolve, reject) => {
408
+ let settled = false;
409
+ const abortController = new AbortController();
410
+ const waiter = {
411
+ reject: (error) => {
412
+ if (settled) {
413
+ return;
414
+ }
415
+ settled = true;
416
+ this.runForeverWaiters.delete(waiter);
417
+ abortController.abort();
418
+ reject(error);
419
+ }
420
+ };
421
+ this.runForeverWaiters.add(waiter);
422
+ signal.addEventListener(
423
+ "abort",
424
+ () => {
425
+ if (settled) {
426
+ return;
427
+ }
428
+ settled = true;
429
+ this.runForeverWaiters.delete(waiter);
430
+ resolve();
431
+ },
432
+ { once: true, signal: abortController.signal }
433
+ );
434
+ });
435
+ }
436
+ async handleOpen() {
437
+ try {
438
+ await this.subscribeAgentControl();
439
+ } catch (error) {
440
+ this.connected = false;
441
+ this.socket.disconnect();
442
+ this.connectReject?.(
443
+ error instanceof Error ? error : new TransportError(String(error))
444
+ );
445
+ this.logger.warn("Failed to join mandatory agent_control channel", {
446
+ error
447
+ });
448
+ return;
449
+ }
450
+ this.connected = true;
451
+ this.connectResolve?.();
452
+ this.connectResolve = null;
453
+ this.connectReject = null;
454
+ this.logger.info("Phoenix socket opened", {
455
+ channels: getSocketChannelCount(this.socket)
301
456
  });
302
457
  }
458
+ stopReconnectIfNoChannels(options = {}) {
459
+ if (this.stoppingReconnect || getSocketChannelCount(this.socket) !== 0) {
460
+ return;
461
+ }
462
+ this.suppressNextCloseReason = options.suppressCloseReason ?? false;
463
+ this.stoppingReconnect = true;
464
+ this.socket.disconnect();
465
+ this.stoppingReconnect = false;
466
+ }
467
+ async subscribeAgentControl() {
468
+ if (!this.agentId) {
469
+ return;
470
+ }
471
+ await this.join(`agent_control:${this.agentId}`, {
472
+ supersede: (payload) => {
473
+ const reason = parseSupersedeDisconnectReason(payload);
474
+ if (!reason) {
475
+ this.logger.warn("Invalid agent_control supersede payload", {
476
+ payload
477
+ });
478
+ return;
479
+ }
480
+ this.recordTerminalDisconnect(reason);
481
+ }
482
+ });
483
+ }
484
+ recordSocketClose(event) {
485
+ this.connected = false;
486
+ const suppressCloseReason = this.suppressNextCloseReason;
487
+ this.suppressNextCloseReason = false;
488
+ this.stopReconnectIfNoChannels();
489
+ if (!suppressCloseReason && !this.terminalDisconnectError && !this.lastDisconnectReason) {
490
+ this.lastDisconnectReason = genericCloseReason(event);
491
+ }
492
+ this.logger.info("Phoenix socket closed", {
493
+ code: event?.code ?? null,
494
+ reason: event?.reason ?? null,
495
+ platformReason: this.lastDisconnectReason
496
+ });
497
+ }
498
+ recordTerminalDisconnect(reason) {
499
+ if (this.terminalDisconnectError) {
500
+ return;
501
+ }
502
+ const error = new WebSocketDisconnectError(reason);
503
+ this.lastDisconnectReason = reason;
504
+ this.terminalDisconnectError = error;
505
+ this.onTerminalDisconnect?.(reason);
506
+ this.connectReject?.(error);
507
+ for (const waiter of this.runForeverWaiters) {
508
+ waiter.reject(error);
509
+ }
510
+ this.runForeverWaiters.clear();
511
+ this.socket.disconnect();
512
+ }
303
513
  async waitForConnection(timeoutMs = 1e4) {
304
514
  if (this.connected) {
305
515
  return;
@@ -307,20 +517,43 @@ var PhoenixChannelsTransport = class {
307
517
  await new Promise((resolve, reject) => {
308
518
  const timeout = setTimeout(() => {
309
519
  this.connectResolve = null;
310
- reject(new TransportError("Timed out waiting for Phoenix socket connection"));
520
+ this.connectReject = null;
521
+ reject(
522
+ new TransportError("Timed out waiting for Phoenix socket connection")
523
+ );
311
524
  }, timeoutMs);
312
525
  this.connectResolve = () => {
313
526
  clearTimeout(timeout);
527
+ this.connectReject = null;
314
528
  resolve();
315
529
  };
530
+ this.connectReject = (error) => {
531
+ clearTimeout(timeout);
532
+ this.connectResolve = null;
533
+ this.connectReject = null;
534
+ reject(
535
+ error instanceof Error ? error : new TransportError(String(error))
536
+ );
537
+ };
316
538
  });
317
539
  }
318
540
  };
319
- function resolveWebSocketFactory() {
541
+ function resolveWebSocketFactory(apiKey) {
320
542
  if (typeof process !== "undefined" && process.versions?.node) {
321
- return NodeWebSocket;
543
+ return createNodeWebSocketFactory({ "x-api-key": apiKey });
544
+ }
545
+ throw new TransportError(
546
+ "Phoenix WebSocket API-key auth requires a WebSocket transport that can set handshake headers."
547
+ );
548
+ }
549
+ function unwrapErrorEvent(event) {
550
+ if (!isErrorEvent(event)) {
551
+ return event;
322
552
  }
323
- return WebSocket;
553
+ return event.error;
554
+ }
555
+ function isErrorEvent(event) {
556
+ return typeof event === "object" && event !== null && "error" in event;
324
557
  }
325
558
  function removeSocketChannel(socket, channel) {
326
559
  const candidate = socket;
@@ -342,6 +575,12 @@ function deriveDefaultRestUrl(wsUrl) {
342
575
  const protocol = parsed.protocol === "ws:" ? "http:" : "https:";
343
576
  return `${protocol}//${parsed.host}`;
344
577
  }
578
+ function roomTopics(roomId) {
579
+ return {
580
+ chat: `chat_room:${roomId}`,
581
+ participants: `room_participants:${roomId}`
582
+ };
583
+ }
345
584
  function toPlatformMessage(roomId, message) {
346
585
  return {
347
586
  id: message.id,
@@ -368,6 +607,8 @@ var ThenvoiLink = class {
368
607
  eventQueue = [];
369
608
  waiters = [];
370
609
  connected = false;
610
+ lastDisconnectReason = null;
611
+ terminalDisconnectError = null;
371
612
  constructor(options) {
372
613
  this.agentId = options.agentId;
373
614
  this.apiKey = options.apiKey;
@@ -389,17 +630,35 @@ var ThenvoiLink = class {
389
630
  wsUrl: this.wsUrl,
390
631
  apiKey: this.apiKey,
391
632
  agentId: this.agentId,
392
- logger: this.logger
633
+ logger: this.logger,
634
+ conflictPolicy: options.conflictPolicy,
635
+ onTerminalDisconnect: (reason) => {
636
+ this.recordDisconnectReason(reason);
637
+ }
393
638
  });
394
639
  }
395
640
  isConnected() {
396
641
  return this.connected;
397
642
  }
643
+ getDisconnectReason() {
644
+ return this.lastDisconnectReason;
645
+ }
398
646
  async connect() {
399
647
  if (this.connected) {
400
648
  return;
401
649
  }
402
- await this.transport.connect();
650
+ try {
651
+ await this.transport.connect();
652
+ } catch (error) {
653
+ if (error instanceof WebSocketDisconnectError) {
654
+ if (error.reason.retryable) {
655
+ this.lastDisconnectReason = error.reason;
656
+ } else {
657
+ this.recordDisconnectError(error);
658
+ }
659
+ }
660
+ throw error;
661
+ }
403
662
  this.connected = true;
404
663
  }
405
664
  async disconnect() {
@@ -413,8 +672,23 @@ var ThenvoiLink = class {
413
672
  this.connected = false;
414
673
  }
415
674
  async runForever(signal) {
675
+ if (this.terminalDisconnectError) {
676
+ throw this.terminalDisconnectError;
677
+ }
416
678
  await this.transport.runForever(signal);
417
679
  }
680
+ recordDisconnectReason(reason) {
681
+ this.recordDisconnectError(new WebSocketDisconnectError(reason));
682
+ }
683
+ recordDisconnectError(error) {
684
+ this.connected = false;
685
+ this.lastDisconnectReason = error.reason;
686
+ this.terminalDisconnectError = error;
687
+ while (this.waiters.length > 0) {
688
+ const waiter = this.waiters.shift();
689
+ waiter?.reject(error);
690
+ }
691
+ }
418
692
  queueEvent(event) {
419
693
  const waiter = this.waiters.shift();
420
694
  if (waiter) {
@@ -439,32 +713,42 @@ var ThenvoiLink = class {
439
713
  if (this.subscribedRooms.has(roomId)) {
440
714
  return;
441
715
  }
442
- await this.transport.join(`chat_room:${roomId}`, {
443
- message_created: (payload) => {
444
- this.emit("message_created", payload, roomId);
445
- }
446
- });
447
- await this.transport.join(`room_participants:${roomId}`, {
448
- participant_added: (payload) => {
449
- this.emit("participant_added", payload, roomId);
450
- },
451
- participant_removed: (payload) => {
452
- this.emit("participant_removed", payload, roomId);
453
- },
454
- room_deleted: (payload) => {
455
- this.emit("room_deleted", payload, roomId);
456
- }
457
- });
716
+ await this.joinRoomTopics(roomId);
458
717
  this.subscribedRooms.add(roomId);
459
718
  }
460
719
  async unsubscribeRoom(roomId) {
461
720
  if (!this.subscribedRooms.has(roomId)) {
462
721
  return;
463
722
  }
464
- await this.transport.leave(`chat_room:${roomId}`);
465
- await this.transport.leave(`room_participants:${roomId}`);
723
+ const topics = roomTopics(roomId);
724
+ await this.transport.leave(topics.chat);
725
+ await this.transport.leave(topics.participants);
466
726
  this.subscribedRooms.delete(roomId);
467
727
  }
728
+ async joinRoomTopics(roomId) {
729
+ const topics = roomTopics(roomId);
730
+ await this.transport.join(topics.chat, {
731
+ message_created: (payload) => {
732
+ this.emit("message_created", payload, roomId);
733
+ }
734
+ });
735
+ try {
736
+ await this.transport.join(topics.participants, {
737
+ participant_added: (payload) => {
738
+ this.emit("participant_added", payload, roomId);
739
+ },
740
+ participant_removed: (payload) => {
741
+ this.emit("participant_removed", payload, roomId);
742
+ },
743
+ room_deleted: (payload) => {
744
+ this.emit("room_deleted", payload, roomId);
745
+ }
746
+ });
747
+ } catch (error) {
748
+ await this.transport.leave(topics.chat);
749
+ throw error;
750
+ }
751
+ }
468
752
  async subscribeAgentContacts() {
469
753
  assertCapability(this.capabilities, "contacts", "Contacts streaming");
470
754
  await this.transport.join(`agent_contacts:${this.agentId}`, {
@@ -486,6 +770,9 @@ var ThenvoiLink = class {
486
770
  await this.transport.leave(`agent_contacts:${this.agentId}`);
487
771
  }
488
772
  async nextEvent(signal) {
773
+ if (this.terminalDisconnectError) {
774
+ throw this.terminalDisconnectError;
775
+ }
489
776
  if (signal?.aborted) {
490
777
  return null;
491
778
  }
@@ -493,19 +780,19 @@ var ThenvoiLink = class {
493
780
  if (queued) {
494
781
  return queued;
495
782
  }
496
- return new Promise((resolve) => {
783
+ return new Promise((resolve, reject) => {
497
784
  let settled = false;
498
785
  const onAbort = () => {
499
786
  const index = this.waiters.indexOf(waiter);
500
787
  if (index >= 0) {
501
788
  this.waiters.splice(index, 1);
502
789
  }
503
- finalize(null);
790
+ finalizeResolve(null);
504
791
  };
505
792
  const cleanup = () => {
506
793
  signal?.removeEventListener("abort", onAbort);
507
794
  };
508
- const finalize = (event) => {
795
+ const finalizeResolve = (event) => {
509
796
  if (settled) {
510
797
  return;
511
798
  }
@@ -513,8 +800,17 @@ var ThenvoiLink = class {
513
800
  cleanup();
514
801
  resolve(event);
515
802
  };
803
+ const finalizeReject = (error) => {
804
+ if (settled) {
805
+ return;
806
+ }
807
+ settled = true;
808
+ cleanup();
809
+ reject(error);
810
+ };
516
811
  const waiter = {
517
- resolve: finalize,
812
+ resolve: finalizeResolve,
813
+ reject: finalizeReject,
518
814
  cleanup
519
815
  };
520
816
  this.waiters.push(waiter);
@@ -526,7 +822,10 @@ var ThenvoiLink = class {
526
822
  next: async () => {
527
823
  const value = await this.nextEvent();
528
824
  if (value === null) {
529
- return { done: true, value: void 0 };
825
+ return {
826
+ done: true,
827
+ value: void 0
828
+ };
530
829
  }
531
830
  return { done: false, value };
532
831
  }
@@ -608,7 +907,9 @@ var ThenvoiLink = class {
608
907
  }
609
908
  async listChats(request, requestOptions) {
610
909
  if (!this.rest.listChats) {
611
- throw new UnsupportedFeatureError("Chat listing is not available in current REST adapter");
910
+ throw new UnsupportedFeatureError(
911
+ "Chat listing is not available in current REST adapter"
912
+ );
612
913
  }
613
914
  return this.rest.listChats(request, requestOptions);
614
915
  }
@@ -968,40 +1269,6 @@ var CONTACT_REQUEST_ACTIONS = /* @__PURE__ */ new Set([
968
1269
  "reject",
969
1270
  "cancel"
970
1271
  ]);
971
- var MEMORY_SYSTEMS = /* @__PURE__ */ new Set([
972
- "sensory",
973
- "working",
974
- "long_term"
975
- ]);
976
- var MEMORY_TYPES = /* @__PURE__ */ new Set([
977
- "iconic",
978
- "echoic",
979
- "haptic",
980
- "episodic",
981
- "semantic",
982
- "procedural"
983
- ]);
984
- var MEMORY_SEGMENTS = /* @__PURE__ */ new Set([
985
- "user",
986
- "agent",
987
- "tool",
988
- "guideline"
989
- ]);
990
- var LIST_MEMORY_SCOPES = /* @__PURE__ */ new Set([
991
- "subject",
992
- "organization",
993
- "all"
994
- ]);
995
- var LIST_MEMORY_STATUSES = /* @__PURE__ */ new Set([
996
- "active",
997
- "superseded",
998
- "archived",
999
- "all"
1000
- ]);
1001
- var MEMORY_SCOPES = /* @__PURE__ */ new Set([
1002
- "subject",
1003
- "organization"
1004
- ]);
1005
1272
  var AgentTools = class {
1006
1273
  roomId;
1007
1274
  capabilities;
@@ -1579,6 +1846,9 @@ var AgentTools = class {
1579
1846
  const status = this.normalizeOptionalMemoryStatus(arguments_.status);
1580
1847
  const subjectId = this.normalizeOptionalString(arguments_.subject_id);
1581
1848
  const contentQuery = this.normalizeOptionalString(arguments_.content_query);
1849
+ if (system && type && !isMemoryTypeForSystem(system, type)) {
1850
+ throw new ValidationError(memoryTypeForSystemError(system));
1851
+ }
1582
1852
  return {
1583
1853
  ...subjectId ? { subject_id: subjectId } : {},
1584
1854
  ...scope ? { scope } : {},
@@ -1600,13 +1870,21 @@ var AgentTools = class {
1600
1870
  const subjectId = this.normalizeOptionalString(arguments_.subject_id);
1601
1871
  const metadata = this.normalizeOptionalMetadata(arguments_.metadata);
1602
1872
  if (!system) {
1603
- throw new ValidationError("system must be one of: sensory, working, long_term");
1873
+ throw new ValidationError(`system must be one of: ${expectedList(MEMORY_SYSTEMS)}`);
1604
1874
  }
1605
1875
  if (!type) {
1606
- throw new ValidationError("type must be one of: iconic, echoic, haptic, episodic, semantic, procedural");
1876
+ throw new ValidationError(`type must be one of: ${expectedList(MEMORY_TYPES)}`);
1877
+ }
1878
+ if (!isMemoryTypeForSystem(system, type)) {
1879
+ throw new ValidationError(memoryTypeForSystemError(system));
1607
1880
  }
1608
1881
  if (!segment) {
1609
- throw new ValidationError("segment must be one of: user, agent, tool, guideline");
1882
+ throw new ValidationError(`segment must be one of: ${expectedList(MEMORY_SEGMENTS)}`);
1883
+ }
1884
+ if (scope === "subject" && !subjectId) {
1885
+ throw new ValidationError(
1886
+ 'scope="subject" requires a subject_id (the UUID of the person or agent the memory is about). If you do not have a concrete subject UUID, retry with scope="organization" and omit subject_id. Do not invent a UUID.'
1887
+ );
1610
1888
  }
1611
1889
  return {
1612
1890
  content,
@@ -1686,8 +1964,8 @@ var AgentTools = class {
1686
1964
  if (!normalized) {
1687
1965
  return void 0;
1688
1966
  }
1689
- if (!isListMemoryScope(normalized)) {
1690
- throw new ValidationError("scope must be one of: subject, organization, all");
1967
+ if (!isMemoryListScope(normalized)) {
1968
+ throw new ValidationError(`scope must be one of: ${expectedList(MEMORY_LIST_SCOPES)}`);
1691
1969
  }
1692
1970
  return normalized;
1693
1971
  }
@@ -1697,7 +1975,7 @@ var AgentTools = class {
1697
1975
  return void 0;
1698
1976
  }
1699
1977
  if (!isMemorySystem(normalized)) {
1700
- throw new ValidationError("system must be one of: sensory, working, long_term");
1978
+ throw new ValidationError(`system must be one of: ${expectedList(MEMORY_SYSTEMS)}`);
1701
1979
  }
1702
1980
  return normalized;
1703
1981
  }
@@ -1707,7 +1985,7 @@ var AgentTools = class {
1707
1985
  return void 0;
1708
1986
  }
1709
1987
  if (!isMemoryType(normalized)) {
1710
- throw new ValidationError("type must be one of: iconic, echoic, haptic, episodic, semantic, procedural");
1988
+ throw new ValidationError(`type must be one of: ${expectedList(MEMORY_TYPES)}`);
1711
1989
  }
1712
1990
  return normalized;
1713
1991
  }
@@ -1717,7 +1995,7 @@ var AgentTools = class {
1717
1995
  return void 0;
1718
1996
  }
1719
1997
  if (!isMemorySegment(normalized)) {
1720
- throw new ValidationError("segment must be one of: user, agent, tool, guideline");
1998
+ throw new ValidationError(`segment must be one of: ${expectedList(MEMORY_SEGMENTS)}`);
1721
1999
  }
1722
2000
  return normalized;
1723
2001
  }
@@ -1727,7 +2005,7 @@ var AgentTools = class {
1727
2005
  return void 0;
1728
2006
  }
1729
2007
  if (!isMemoryStatus(normalized)) {
1730
- throw new ValidationError("status must be one of: active, superseded, archived, all");
2008
+ throw new ValidationError(`status must be one of: ${expectedList(MEMORY_STATUSES)}`);
1731
2009
  }
1732
2010
  return normalized;
1733
2011
  }
@@ -1736,8 +2014,8 @@ var AgentTools = class {
1736
2014
  if (!normalized) {
1737
2015
  return void 0;
1738
2016
  }
1739
- if (!isStoreMemoryScope(normalized)) {
1740
- throw new ValidationError("scope must be one of: subject, organization");
2017
+ if (!isMemoryStoreScope(normalized)) {
2018
+ throw new ValidationError(`scope must be one of: ${expectedList(MEMORY_STORE_SCOPES)}`);
1741
2019
  }
1742
2020
  return normalized;
1743
2021
  }
@@ -1745,24 +2023,6 @@ var AgentTools = class {
1745
2023
  function isContactRequestAction(value) {
1746
2024
  return CONTACT_REQUEST_ACTIONS.has(value);
1747
2025
  }
1748
- function isMemorySystem(value) {
1749
- return MEMORY_SYSTEMS.has(value);
1750
- }
1751
- function isMemoryType(value) {
1752
- return MEMORY_TYPES.has(value);
1753
- }
1754
- function isMemorySegment(value) {
1755
- return MEMORY_SEGMENTS.has(value);
1756
- }
1757
- function isStoreMemoryScope(value) {
1758
- return MEMORY_SCOPES.has(value);
1759
- }
1760
- function isListMemoryScope(value) {
1761
- return LIST_MEMORY_SCOPES.has(value);
1762
- }
1763
- function isMemoryStatus(value) {
1764
- return LIST_MEMORY_STATUSES.has(value);
1765
- }
1766
2026
  function coercePositiveInt(value, fallback) {
1767
2027
  const num = Number(value ?? fallback);
1768
2028
  return Number.isFinite(num) && num >= 1 ? Math.floor(num) : fallback;
@@ -1802,17 +2062,19 @@ function validateToolArgs(toolName, args) {
1802
2062
  }
1803
2063
  }
1804
2064
  if (toolName === "thenvoi_store_memory") {
1805
- const validSystems = ["sensory", "working", "long_term"];
1806
- const validTypes = ["iconic", "echoic", "haptic", "episodic", "semantic", "procedural"];
1807
- const validSegments = ["user", "agent", "tool", "guideline"];
1808
- if (typeof args.system === "string" && !validSystems.includes(args.system)) {
1809
- errors.push(`system: Invalid value '${args.system}'. Expected one of: ${validSystems.join(", ")}`);
2065
+ if (typeof args.system === "string" && !isMemorySystem(args.system)) {
2066
+ errors.push(`system: Invalid value '${args.system}'. Expected one of: ${expectedList(MEMORY_SYSTEMS)}`);
1810
2067
  }
1811
- if (typeof args.type === "string" && !validTypes.includes(args.type)) {
1812
- errors.push(`type: Invalid value '${args.type}'. Expected one of: ${validTypes.join(", ")}`);
2068
+ if (typeof args.type === "string" && !isMemoryType(args.type)) {
2069
+ errors.push(`type: Invalid value '${args.type}'. Expected one of: ${expectedList(MEMORY_TYPES)}`);
1813
2070
  }
1814
- if (typeof args.segment === "string" && !validSegments.includes(args.segment)) {
1815
- errors.push(`segment: Invalid value '${args.segment}'. Expected one of: ${validSegments.join(", ")}`);
2071
+ if (typeof args.system === "string" && isMemorySystem(args.system) && typeof args.type === "string" && isMemoryType(args.type) && !isMemoryTypeForSystem(args.system, args.type)) {
2072
+ errors.push(
2073
+ `type: Invalid value '${args.type}' for system '${args.system}'. Expected one of: ${expectedMemoryTypesForSystem(args.system)}`
2074
+ );
2075
+ }
2076
+ if (typeof args.segment === "string" && !isMemorySegment(args.segment)) {
2077
+ errors.push(`segment: Invalid value '${args.segment}'. Expected one of: ${expectedList(MEMORY_SEGMENTS)}`);
1816
2078
  }
1817
2079
  }
1818
2080
  if (errors.length > 0) {
@@ -2349,7 +2611,7 @@ var AgentRuntime = class {
2349
2611
  await this.link.disconnect();
2350
2612
  }
2351
2613
  async stop(timeoutMs) {
2352
- if (!this.running || this.stopping) {
2614
+ if (this.stopping || !this.running && !this.fatalError) {
2353
2615
  return true;
2354
2616
  }
2355
2617
  this.stopping = true;
@@ -2369,7 +2631,8 @@ var AgentRuntime = class {
2369
2631
  }
2370
2632
  }
2371
2633
  for (const roomId of [...this.subscribedRooms]) {
2372
- await this.leaveTrackedRoom(roomId);
2634
+ const remaining = deadline === void 0 ? void 0 : Math.max(0, deadline - Date.now());
2635
+ await this.leaveTrackedRoom(roomId, remaining);
2373
2636
  }
2374
2637
  for (const roomId of [...this.contexts.keys()]) {
2375
2638
  await this.onSessionCleanup(roomId);
@@ -2405,7 +2668,13 @@ var AgentRuntime = class {
2405
2668
  }
2406
2669
  async consumeLoop(signal) {
2407
2670
  while (!signal.aborted) {
2408
- const event = await this.link.nextEvent(signal);
2671
+ let event;
2672
+ try {
2673
+ event = await this.link.nextEvent(signal);
2674
+ } catch (error) {
2675
+ await this.failRuntime(error, syntheticRuntimeFailureEvent(this.agentId));
2676
+ return;
2677
+ }
2409
2678
  if (!event) {
2410
2679
  return;
2411
2680
  }
@@ -2568,13 +2837,13 @@ var AgentRuntime = class {
2568
2837
  }
2569
2838
  });
2570
2839
  }
2571
- async leaveTrackedRoom(roomId) {
2840
+ async leaveTrackedRoom(roomId, timeoutMs) {
2572
2841
  await trackRoomLeave({
2573
2842
  link: this.link,
2574
2843
  roomId,
2575
2844
  trackedRooms: this.subscribedRooms,
2576
2845
  onLeft: async (leftRoomId) => {
2577
- await this.executions.get(leftRoomId)?.stop();
2846
+ await this.executions.get(leftRoomId)?.stop(timeoutMs);
2578
2847
  this.contexts.delete(leftRoomId);
2579
2848
  this.executions.delete(leftRoomId);
2580
2849
  await this.onSessionCleanup(leftRoomId);
@@ -2611,6 +2880,23 @@ var AgentRuntime = class {
2611
2880
  }
2612
2881
  }
2613
2882
  };
2883
+ function syntheticRuntimeFailureEvent(agentId) {
2884
+ return {
2885
+ type: "message_created",
2886
+ roomId: null,
2887
+ payload: {
2888
+ id: "runtime-failed",
2889
+ content: "",
2890
+ sender_id: agentId,
2891
+ sender_type: "Agent",
2892
+ sender_name: null,
2893
+ message_type: "text",
2894
+ metadata: {},
2895
+ inserted_at: (/* @__PURE__ */ new Date(0)).toISOString(),
2896
+ updated_at: (/* @__PURE__ */ new Date(0)).toISOString()
2897
+ }
2898
+ };
2899
+ }
2614
2900
  function assertNever(value) {
2615
2901
  throw new Error(`Unhandled platform event: ${JSON.stringify(value)}`);
2616
2902
  }
@@ -3453,7 +3739,8 @@ var PlatformRuntime = class {
3453
3739
  agentId: this._agentId,
3454
3740
  apiKey: this._apiKey,
3455
3741
  wsUrl: this._wsUrl,
3456
- restUrl: this._restUrl
3742
+ restUrl: this._restUrl,
3743
+ logger: this.logger
3457
3744
  });
3458
3745
  }
3459
3746
  if (this.configuredIdentity) {
@@ -3670,6 +3957,7 @@ async function runWithGracefulShutdown(agent, options) {
3670
3957
  }
3671
3958
 
3672
3959
  export {
3960
+ deriveDefaultRestUrl,
3673
3961
  ThenvoiLink,
3674
3962
  Execution,
3675
3963
  AgentTools,