@band-ai/sdk 0.1.6 → 0.2.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 (85) hide show
  1. package/README.md +372 -0
  2. package/dist/{ClaudeSDKAdapter-CRq4R-WB.d.cts → ClaudeSDKAdapter-BY4zEbCl.d.cts} +14 -10
  3. package/dist/{ClaudeSDKAdapter-BHOkjN3X.d.ts → ClaudeSDKAdapter-Cna5P1dR.d.ts} +14 -10
  4. package/dist/{acp-server-B1Da7L6u.d.ts → acp-server-CiUqN3G3.d.ts} +2 -2
  5. package/dist/{acp-server-BdRz9_UN.d.cts → acp-server-Dlj7D563.d.cts} +2 -2
  6. package/dist/adapters.cjs +734 -279
  7. package/dist/adapters.d.cts +21 -19
  8. package/dist/adapters.d.ts +21 -19
  9. package/dist/adapters.js +65 -44
  10. package/dist/backends-D-dBdDm-.d.ts +27 -0
  11. package/dist/backends-ZLxmQrTZ.d.cts +27 -0
  12. package/dist/{chunk-MSL6CWHY.js → chunk-4IAXZBXX.js} +77 -37
  13. package/dist/{chunk-SG4WLD2H.js → chunk-EAI2M3WM.js} +38 -94
  14. package/dist/{chunk-YR3BOQNW.js → chunk-JY75VSQK.js} +7 -7
  15. package/dist/{chunk-INA6YZTZ.js → chunk-LBWKISBS.js} +51 -31
  16. package/dist/{chunk-I5EZG52P.js → chunk-N2QTUMGO.js} +532 -327
  17. package/dist/{chunk-RHVH2GZR.js → chunk-NMZLX7TP.js} +110 -56
  18. package/dist/{chunk-EDPKUUJL.js → chunk-NT7TBFX2.js} +9 -1
  19. package/dist/{chunk-GVEQ3RAH.js → chunk-RV76SKFT.js} +169 -54
  20. package/dist/{chunk-W5NDZSYE.js → chunk-TLBCKDQO.js} +23 -23
  21. package/dist/chunk-US226AS4.js +130 -0
  22. package/dist/{chunk-IZPV6QS6.js → chunk-WL2HNJZ6.js} +38 -11
  23. package/dist/chunk-WLCM2OXG.js +181 -0
  24. package/dist/config.cjs +40 -13
  25. package/dist/config.d.cts +7 -1
  26. package/dist/config.d.ts +7 -1
  27. package/dist/config.js +2 -2
  28. package/dist/converters.cjs +491 -35
  29. package/dist/converters.d.cts +5 -5
  30. package/dist/converters.d.ts +5 -5
  31. package/dist/converters.js +4 -3
  32. package/dist/core.cjs +53 -10
  33. package/dist/core.d.cts +10 -9
  34. package/dist/core.d.ts +10 -9
  35. package/dist/core.js +8 -4
  36. package/dist/disconnectReason-IuADl6TH.d.cts +34 -0
  37. package/dist/disconnectReason-IuADl6TH.d.ts +34 -0
  38. package/dist/{dtos-DUpbIu8k.d.ts → dtos-B8KU_q4d.d.cts} +26 -6
  39. package/dist/{dtos-DUpbIu8k.d.cts → dtos-B8KU_q4d.d.ts} +26 -6
  40. package/dist/index.cjs +1510 -735
  41. package/dist/index.d.cts +18 -16
  42. package/dist/index.d.ts +18 -16
  43. package/dist/index.js +21 -13
  44. package/dist/linear.cjs +1044 -112
  45. package/dist/linear.d.cts +146 -16
  46. package/dist/linear.d.ts +146 -16
  47. package/dist/linear.js +1031 -110
  48. package/dist/mcp-claude.cjs +338 -66
  49. package/dist/mcp-claude.d.cts +3 -3
  50. package/dist/mcp-claude.d.ts +3 -3
  51. package/dist/mcp-claude.js +215 -16
  52. package/dist/mcp.cjs +414 -105
  53. package/dist/mcp.d.cts +29 -16
  54. package/dist/mcp.d.ts +29 -16
  55. package/dist/mcp.js +14 -14
  56. package/dist/{opencode-BBcDchcN.d.cts → opencode-2YdscIc1.d.cts} +5 -5
  57. package/dist/{opencode-DthQQUsZ.d.ts → opencode-Bld62x5t.d.ts} +5 -5
  58. package/dist/{pagination-CcDkVFxK.d.ts → pagination-BgEx-HeL.d.ts} +2 -2
  59. package/dist/{pagination-BPiys10t.d.cts → pagination-DqdkQArT.d.cts} +2 -2
  60. package/dist/{protocols-DrhzOH6K.d.ts → protocols-Bwwcql0h.d.ts} +1 -1
  61. package/dist/{protocols-CInhtFpA.d.cts → protocols-Bz1rxcUg.d.cts} +1 -1
  62. package/dist/rest.cjs +41 -97
  63. package/dist/rest.d.cts +7 -7
  64. package/dist/rest.d.ts +7 -7
  65. package/dist/rest.js +2 -2
  66. package/dist/runtime.cjs +998 -620
  67. package/dist/runtime.d.cts +22 -29
  68. package/dist/runtime.d.ts +22 -29
  69. package/dist/runtime.js +19 -67
  70. package/dist/{schemas-BpEBkq5V.d.cts → schemas-BotL3ZLH.d.cts} +51 -56
  71. package/dist/{schemas-vos1AaBc.d.ts → schemas-DBsC4J1g.d.ts} +51 -56
  72. package/dist/{sdk-CwTAxhwI.d.cts → sdk-BTciSLRQ.d.cts} +35 -5
  73. package/dist/{sdk-CcnQ5OUC.d.ts → sdk-qKJtMma_.d.ts} +35 -5
  74. package/dist/{simpleAdapter-9OE_p6QW.d.ts → simpleAdapter--wznuoOw.d.ts} +1 -1
  75. package/dist/{simpleAdapter-DqhvjsGr.d.cts → simpleAdapter-BpT4XbZC.d.cts} +1 -1
  76. package/dist/testing.d.cts +3 -3
  77. package/dist/testing.d.ts +3 -3
  78. package/dist/{types-7jVzOeOP.d.ts → types-BTN_ZETM.d.ts} +2 -2
  79. package/dist/{types-CxII3XkA.d.ts → types-BgYFd_Yw.d.cts} +5 -5
  80. package/dist/{types-CSHFipIc.d.cts → types-BivGO7I9.d.ts} +5 -5
  81. package/dist/{types-CovcHpYf.d.cts → types-D958TC71.d.cts} +2 -2
  82. package/package.json +25 -23
  83. package/dist/backends-CzDbiQar.d.ts +0 -20
  84. package/dist/backends-SVMT-7l4.d.cts +0 -20
  85. package/dist/chunk-SFVKOEQH.js +0 -103
package/dist/runtime.cjs CHANGED
@@ -37,11 +37,13 @@ __export(runtime_exports, {
37
37
  GracefulShutdown: () => GracefulShutdown,
38
38
  HUB_ROOM_SYSTEM_PROMPT: () => HUB_ROOM_SYSTEM_PROMPT,
39
39
  HistoryProvider: () => HistoryProvider,
40
+ MCP_SERVER_NAME: () => MCP_SERVER_NAME,
40
41
  MCP_TOOL_PREFIX: () => MCP_TOOL_PREFIX,
42
+ MEMORY_SECTION: () => MEMORY_SECTION,
41
43
  MEMORY_TOOL_NAMES: () => MEMORY_TOOL_NAMES,
42
- MessageRetryTracker: () => MessageRetryTracker,
43
- ParticipantTracker: () => ParticipantTracker,
44
+ ParticipantRoster: () => import_band_sdk_core3.ParticipantRoster,
44
45
  PlatformRuntime: () => PlatformRuntime,
46
+ RetryTracker: () => import_band_sdk_core3.RetryTracker,
45
47
  RoomPresence: () => RoomPresence,
46
48
  SYNTHETIC_CONTACT_EVENTS_SENDER_ID: () => SYNTHETIC_CONTACT_EVENTS_SENDER_ID,
47
49
  SYNTHETIC_CONTACT_EVENTS_SENDER_NAME: () => SYNTHETIC_CONTACT_EVENTS_SENDER_NAME,
@@ -98,39 +100,112 @@ var NoopLogger = class {
98
100
  var DEFAULT_REQUEST_OPTIONS = {
99
101
  maxRetries: 3
100
102
  };
103
+ var MESSAGE_SEND_MAX_RETRIES = 2;
101
104
 
102
105
  // src/core/errors.ts
103
- var ThenvoiSdkError = class extends Error {
106
+ var BandSdkError = class extends Error {
104
107
  constructor(message, cause) {
105
108
  super(message, cause !== void 0 ? { cause } : void 0);
106
- this.name = "ThenvoiSdkError";
109
+ this.name = "BandSdkError";
107
110
  }
108
111
  };
109
- var UnsupportedFeatureError = class extends ThenvoiSdkError {
112
+ var UnsupportedFeatureError = class extends BandSdkError {
110
113
  constructor(message) {
111
114
  super(message);
112
115
  this.name = "UnsupportedFeatureError";
113
116
  }
114
117
  };
115
- var ValidationError = class extends ThenvoiSdkError {
118
+ var ValidationError = class extends BandSdkError {
116
119
  constructor(message, cause) {
117
120
  super(message, cause);
118
121
  this.name = "ValidationError";
119
122
  }
120
123
  };
121
- var TransportError = class extends ThenvoiSdkError {
124
+ var TransportError = class extends BandSdkError {
122
125
  constructor(message, cause) {
123
126
  super(message, cause);
124
127
  this.name = "TransportError";
125
128
  }
126
129
  };
127
- var RuntimeStateError = class extends ThenvoiSdkError {
130
+ var RuntimeStateError = class extends BandSdkError {
128
131
  constructor(message) {
129
132
  super(message);
130
133
  this.name = "RuntimeStateError";
131
134
  }
132
135
  };
133
136
 
137
+ // src/runtime/formatters.ts
138
+ function isMetadataMap(value) {
139
+ return value !== null && typeof value === "object" && !Array.isArray(value);
140
+ }
141
+ function replaceUuidMentions(content, participants) {
142
+ if (!content || participants.length === 0) {
143
+ return content;
144
+ }
145
+ let next = content;
146
+ for (const participant of participants) {
147
+ const participantId = participant.id;
148
+ const handle = participant.handle;
149
+ if (typeof participantId === "string" && typeof handle === "string") {
150
+ next = next.replaceAll(`@[[${participantId}]]`, ensureHandlePrefix(handle) ?? "");
151
+ }
152
+ }
153
+ return next;
154
+ }
155
+ function formatMessageForLlm(message, participants) {
156
+ const senderType = String(message.sender_type ?? "");
157
+ const senderName = String(message.sender_name ?? message.name ?? senderType);
158
+ const content = participants ? replaceUuidMentions(String(message.content ?? ""), participants) : String(message.content ?? "");
159
+ return {
160
+ role: senderType === "Agent" ? "assistant" : "user",
161
+ content,
162
+ sender_name: senderName,
163
+ sender_type: senderType,
164
+ message_type: String(message.message_type ?? "text"),
165
+ metadata: isMetadataMap(message.metadata) ? message.metadata : {}
166
+ };
167
+ }
168
+ function formatHistoryForLlm(messages, options) {
169
+ const excludeId = options?.excludeId;
170
+ const participants = options?.participants;
171
+ return messages.filter((message) => String(message.id ?? "") !== excludeId).map((message) => formatMessageForLlm(message, participants));
172
+ }
173
+ function toParticipantRecord(fields) {
174
+ return {
175
+ id: fields.id,
176
+ name: fields.name ?? "unknown",
177
+ type: fields.type ?? "unknown",
178
+ handle: fields.handle ?? null
179
+ };
180
+ }
181
+ function toParticipantRecordFromRest(participant) {
182
+ return {
183
+ id: participant.id,
184
+ name: participant.name,
185
+ type: participant.type,
186
+ handle: participant.handle ?? null
187
+ };
188
+ }
189
+ function buildParticipantsMessage(participants) {
190
+ if (participants.length === 0) {
191
+ return "## Current Participants\nNo other participants in this room.";
192
+ }
193
+ const lines = ["## Current Participants"];
194
+ for (const participant of participants) {
195
+ const participantType = String(participant.type ?? "Unknown");
196
+ const participantName = String(participant.name ?? "Unknown");
197
+ const participantHandle = String(participant.handle ?? "Unknown");
198
+ lines.push(
199
+ `- ${ensureHandlePrefix(participantHandle) ?? ""} \u2014 ${participantName} (${participantType})`
200
+ );
201
+ }
202
+ lines.push("");
203
+ lines.push(
204
+ "IMPORTANT: In band_send_message mentions, always use the exact handle shown above (e.g. '@john' for users, '@john/weather-agent' for agents), NOT the display name. Handles are lowercase with no spaces."
205
+ );
206
+ return lines.join("\n");
207
+ }
208
+
134
209
  // src/runtime/tools/ContactToolsImpl.ts
135
210
  var ContactToolsImpl = class {
136
211
  rest;
@@ -227,14 +302,6 @@ var ContactToolsImpl = class {
227
302
  };
228
303
 
229
304
  // src/runtime/tools/ContactCallbackTools.ts
230
- function toParticipantRecord(participant) {
231
- return {
232
- id: participant.id,
233
- name: participant.name,
234
- type: participant.type,
235
- handle: participant.handle ?? null
236
- };
237
- }
238
305
  function normalizePage(value, fallback) {
239
306
  return Number.isInteger(value) && value && value > 0 ? value : fallback;
240
307
  }
@@ -278,8 +345,7 @@ var ContactCallbackTools = class {
278
345
  {
279
346
  content,
280
347
  ...normalizedMentions ? { mentions: normalizedMentions } : {}
281
- },
282
- DEFAULT_REQUEST_OPTIONS
348
+ }
283
349
  );
284
350
  }
285
351
  async sendEvent(content, messageType, metadata) {
@@ -293,8 +359,7 @@ var ContactCallbackTools = class {
293
359
  content,
294
360
  messageType,
295
361
  ...metadata ? { metadata } : {}
296
- },
297
- DEFAULT_REQUEST_OPTIONS
362
+ }
298
363
  );
299
364
  }
300
365
  async addParticipant() {
@@ -308,7 +373,7 @@ var ContactCallbackTools = class {
308
373
  if (!this.rest.listChatParticipants) {
309
374
  throw new UnsupportedFeatureError("Participant listing is not available in current REST adapter");
310
375
  }
311
- return (await this.rest.listChatParticipants(roomId, DEFAULT_REQUEST_OPTIONS)).map(toParticipantRecord);
376
+ return (await this.rest.listChatParticipants(roomId, DEFAULT_REQUEST_OPTIONS)).map(toParticipantRecordFromRest);
312
377
  }
313
378
  async createChatroom(taskId) {
314
379
  const chat = await this.rest.createChat(taskId, DEFAULT_REQUEST_OPTIONS);
@@ -398,48 +463,48 @@ var ContactCallbackTools = class {
398
463
  }
399
464
  async executeToolCall(toolName, toolArgs) {
400
465
  switch (toolName) {
401
- case "thenvoi_send_message":
466
+ case "band_send_message":
402
467
  return this.sendMessage(
403
468
  String(toolArgs.content ?? ""),
404
469
  toolArgs.mentions
405
470
  );
406
- case "thenvoi_send_event":
471
+ case "band_send_event":
407
472
  return this.sendEvent(
408
473
  String(toolArgs.content ?? ""),
409
474
  String(toolArgs.message_type ?? "task"),
410
475
  toolArgs.metadata
411
476
  );
412
- case "thenvoi_get_participants":
477
+ case "band_get_participants":
413
478
  return this.getParticipants();
414
- case "thenvoi_create_chatroom":
479
+ case "band_create_chatroom":
415
480
  return this.createChatroom(typeof toolArgs.task_id === "string" ? toolArgs.task_id : void 0);
416
- case "thenvoi_lookup_peers":
481
+ case "band_lookup_peers":
417
482
  return this.lookupPeers(
418
483
  typeof toolArgs.page === "number" ? toolArgs.page : void 0,
419
484
  typeof toolArgs.page_size === "number" ? toolArgs.page_size : void 0
420
485
  );
421
- case "thenvoi_list_contacts":
486
+ case "band_list_contacts":
422
487
  return this.listContacts({
423
488
  page: typeof toolArgs.page === "number" ? toolArgs.page : void 0,
424
489
  pageSize: typeof toolArgs.page_size === "number" ? toolArgs.page_size : void 0
425
490
  });
426
- case "thenvoi_add_contact":
491
+ case "band_add_contact":
427
492
  return this.addContact({
428
493
  handle: String(toolArgs.handle ?? ""),
429
494
  ...typeof toolArgs.message === "string" ? { message: toolArgs.message } : {}
430
495
  });
431
- case "thenvoi_remove_contact":
496
+ case "band_remove_contact":
432
497
  if (typeof toolArgs.contact_id === "string") {
433
498
  return this.removeContact({ target: "contactId", contactId: toolArgs.contact_id });
434
499
  }
435
500
  return this.removeContact({ target: "handle", handle: String(toolArgs.handle ?? "") });
436
- case "thenvoi_list_contact_requests":
501
+ case "band_list_contact_requests":
437
502
  return this.listContactRequests({
438
503
  page: typeof toolArgs.page === "number" ? toolArgs.page : void 0,
439
504
  pageSize: typeof toolArgs.page_size === "number" ? toolArgs.page_size : void 0,
440
505
  sentStatus: typeof toolArgs.sent_status === "string" ? toolArgs.sent_status : void 0
441
506
  });
442
- case "thenvoi_respond_contact_request":
507
+ case "band_respond_contact_request":
443
508
  if (typeof toolArgs.request_id === "string") {
444
509
  return this.respondContactRequest({
445
510
  action: String(toolArgs.action ?? "approve"),
@@ -452,15 +517,15 @@ var ContactCallbackTools = class {
452
517
  target: "handle",
453
518
  handle: String(toolArgs.handle ?? "")
454
519
  });
455
- case "thenvoi_list_memories":
520
+ case "band_list_memories":
456
521
  return this.listMemories(toolArgs);
457
- case "thenvoi_store_memory":
522
+ case "band_store_memory":
458
523
  return this.storeMemory(toolArgs);
459
- case "thenvoi_get_memory":
524
+ case "band_get_memory":
460
525
  return this.getMemory(String(toolArgs.memory_id ?? ""));
461
- case "thenvoi_supersede_memory":
526
+ case "band_supersede_memory":
462
527
  return this.supersedeMemory(String(toolArgs.memory_id ?? ""));
463
- case "thenvoi_archive_memory":
528
+ case "band_archive_memory":
464
529
  return this.archiveMemory(String(toolArgs.memory_id ?? ""));
465
530
  default:
466
531
  throw new UnsupportedFeatureError(`Unsupported tool call for contact callback: ${toolName}`);
@@ -474,14 +539,14 @@ var HUB_ROOM_SYSTEM_PROMPT = `## OVERRIDE: Contact Management Mode
474
539
 
475
540
  This is your CONTACTS HUB - a dedicated room for managing contact requests.
476
541
 
477
- **IMPORTANT: Do NOT delegate or add participants here.** You handle contact events DIRECTLY using the contact tools below. Do NOT call thenvoi_lookup_peers() or thenvoi_add_participant() in this room.
542
+ **IMPORTANT: Do NOT delegate or add participants here.** You handle contact events DIRECTLY using the contact tools below. Do NOT call band_lookup_peers() or band_add_participant() in this room.
478
543
 
479
544
  ## Your Role
480
545
 
481
546
  1. **Review incoming contact requests** - When you see a [Contact Request] message, evaluate it
482
547
  2. **Take action** - Use the contact tools to respond:
483
- - \`thenvoi_respond_contact_request(action="approve", request_id="...")\` to accept
484
- - \`thenvoi_respond_contact_request(action="reject", request_id="...")\` to decline
548
+ - \`band_respond_contact_request(action="approve", request_id="...")\` to accept
549
+ - \`band_respond_contact_request(action="reject", request_id="...")\` to decline
485
550
  3. **Report your decision** - Send a thought event explaining what you did
486
551
 
487
552
  ## Example
@@ -490,14 +555,14 @@ This is your CONTACTS HUB - a dedicated room for managing contact requests.
490
555
  Request ID: abc-123
491
556
 
492
557
  Your response:
493
- 1. thenvoi_send_event("Received contact request from Alice. Approving.", message_type="thought")
494
- 2. thenvoi_respond_contact_request(action="approve", request_id="abc-123")
495
- 3. thenvoi_send_event("Approved contact request from Alice (@alice)", message_type="thought")
558
+ 1. band_send_event("Received contact request from Alice. Approving.", message_type="thought")
559
+ 2. band_respond_contact_request(action="approve", request_id="abc-123")
560
+ 3. band_send_event("Approved contact request from Alice (@alice)", message_type="thought")
496
561
 
497
562
  ## Contact Tools (use these, NOT participant tools)
498
- - \`thenvoi_respond_contact_request(action, request_id)\` - Approve/reject requests
499
- - \`thenvoi_list_contact_requests()\` - List pending requests
500
- - \`thenvoi_list_contacts()\` - List current contacts`;
563
+ - \`band_respond_contact_request(action, request_id)\` - Approve/reject requests
564
+ - \`band_list_contact_requests()\` - List pending requests
565
+ - \`band_list_contacts()\` - List current contacts`;
501
566
  var ContactEventHandlerError = class extends Error {
502
567
  retryable;
503
568
  stage;
@@ -1117,6 +1182,9 @@ var Execution = class {
1117
1182
  }
1118
1183
  };
1119
1184
 
1185
+ // src/runtime/tools/AgentTools.ts
1186
+ var import_band_sdk_core = require("@band-ai/band-sdk-core");
1187
+
1120
1188
  // src/contracts/capabilities.ts
1121
1189
  var CAPABILITY_LABELS = {
1122
1190
  peers: "Peer lookup",
@@ -1172,9 +1240,111 @@ var DEFAULT_AGENT_TOOLS_CAPABILITIES = {
1172
1240
  memory: true
1173
1241
  };
1174
1242
 
1243
+ // src/contracts/memory.ts
1244
+ var SENSORY_MEMORY_TYPES = [
1245
+ "iconic",
1246
+ // Visual input
1247
+ "echoic",
1248
+ // Auditory input
1249
+ "haptic"
1250
+ // Tactile input
1251
+ ];
1252
+ var COGNITIVE_MEMORY_TYPES = [
1253
+ "episodic",
1254
+ // Events that occurred
1255
+ "semantic",
1256
+ // Facts, preferences, learned knowledge
1257
+ "procedural"
1258
+ // How to perform tasks
1259
+ ];
1260
+ function namedValues(values) {
1261
+ return Object.fromEntries(values.map((value) => [value, value]));
1262
+ }
1263
+ var MEMORY_SYSTEM_TYPES = {
1264
+ sensory: SENSORY_MEMORY_TYPES,
1265
+ // Brief sensory inputs
1266
+ working: COGNITIVE_MEMORY_TYPES,
1267
+ // Short-term session context
1268
+ long_term: COGNITIVE_MEMORY_TYPES
1269
+ // Persistent cross-conversation memory
1270
+ };
1271
+ var MEMORY_SYSTEMS = Object.keys(MEMORY_SYSTEM_TYPES);
1272
+ var MEMORY_TYPES = [
1273
+ ...SENSORY_MEMORY_TYPES,
1274
+ ...COGNITIVE_MEMORY_TYPES
1275
+ ];
1276
+ var MEMORY_SEGMENTS = [
1277
+ "user",
1278
+ // User preferences or profile info
1279
+ "agent",
1280
+ // Facts or events about agents/entities
1281
+ "tool",
1282
+ // Tool usage or task procedures
1283
+ "guideline"
1284
+ // Behavioral rules or policies
1285
+ ];
1286
+ var MEMORY_STORE_SCOPES = [
1287
+ "subject",
1288
+ // About one person/agent; requires subject_id
1289
+ "organization"
1290
+ // Shared org-wide
1291
+ ];
1292
+ var MEMORY_LIST_SCOPES = [
1293
+ "subject",
1294
+ // Subject-scoped memories only
1295
+ "organization",
1296
+ // Organization-scoped memories only
1297
+ "all"
1298
+ // Both scopes (no scope filter)
1299
+ ];
1300
+ var MEMORY_STATUSES = [
1301
+ "active",
1302
+ // Normal, visible memories
1303
+ "superseded",
1304
+ // Outdated; soft-deleted via band_supersede_memory
1305
+ "archived",
1306
+ // Hidden but preserved via band_archive_memory
1307
+ "all"
1308
+ // Any status (no filter)
1309
+ ];
1310
+ var MEMORY_SYSTEM = namedValues(MEMORY_SYSTEMS);
1311
+ var MEMORY_TYPE = namedValues(MEMORY_TYPES);
1312
+ var MEMORY_SEGMENT = namedValues(MEMORY_SEGMENTS);
1313
+ var MEMORY_STORE_SCOPE = namedValues(MEMORY_STORE_SCOPES);
1314
+ function isMemorySystem(value) {
1315
+ return MEMORY_SYSTEMS.includes(value);
1316
+ }
1317
+ function isMemoryType(value) {
1318
+ return MEMORY_TYPES.includes(value);
1319
+ }
1320
+ function isMemoryTypeForSystem(system, type) {
1321
+ return MEMORY_SYSTEM_TYPES[system].includes(type);
1322
+ }
1323
+ function isMemorySegment(value) {
1324
+ return MEMORY_SEGMENTS.includes(value);
1325
+ }
1326
+ function isMemoryStoreScope(value) {
1327
+ return MEMORY_STORE_SCOPES.includes(value);
1328
+ }
1329
+ function isMemoryListScope(value) {
1330
+ return MEMORY_LIST_SCOPES.includes(value);
1331
+ }
1332
+ function isMemoryStatus(value) {
1333
+ return MEMORY_STATUSES.includes(value);
1334
+ }
1335
+ function expectedList(values) {
1336
+ return values.join(", ");
1337
+ }
1338
+ function expectedMemoryTypesForSystem(system) {
1339
+ return expectedList(MEMORY_SYSTEM_TYPES[system]);
1340
+ }
1341
+ function memoryTypeForSystemError(system) {
1342
+ return `type must be one of: ${expectedMemoryTypesForSystem(system)} for system "${system}"`;
1343
+ }
1344
+
1175
1345
  // src/runtime/tools/schemas.ts
1176
1346
  var TOOL_MODELS = {
1177
- thenvoi_send_message: {
1347
+ band_send_message: {
1178
1348
  description: "Send a message to the chat room. Use this to respond to users or other agents. Messages require at least one @mention in the mentions array. You MUST use this tool to communicate \u2014 plain text responses won't reach users. When delegating, send the full task context in this message instead of assuming hidden state.",
1179
1349
  properties: {
1180
1350
  content: {
@@ -1190,7 +1360,7 @@ var TOOL_MODELS = {
1190
1360
  },
1191
1361
  required: ["content", "mentions"]
1192
1362
  },
1193
- thenvoi_send_event: {
1363
+ band_send_event: {
1194
1364
  description: "Send an event to the chat room. No mentions required. 'thought': Share your reasoning or plan BEFORE taking actions. Explain what you're about to do and why. 'error': Report an error or problem that occurred. 'task': Report task progress or completion status. Always send a thought before complex actions to keep users informed.",
1195
1365
  properties: {
1196
1366
  content: {
@@ -1209,12 +1379,12 @@ var TOOL_MODELS = {
1209
1379
  },
1210
1380
  required: ["content", "message_type"]
1211
1381
  },
1212
- thenvoi_add_participant: {
1213
- description: "Add a participant (agent or user) to the chat room by name. IMPORTANT: Use thenvoi_lookup_peers() first to find available agents. Pass the exact peer name from thenvoi_lookup_peers, not the handle. For normal delegation, omit role or use 'member'.",
1382
+ band_add_participant: {
1383
+ description: "Add a participant (agent or user) to the chat room by name. IMPORTANT: Use band_lookup_peers() first to find available agents. Pass the exact peer name from band_lookup_peers, not the handle. For normal delegation, omit role or use 'member'.",
1214
1384
  properties: {
1215
1385
  name: {
1216
1386
  type: "string",
1217
- description: "Name of participant to add (must match a name from thenvoi_lookup_peers)."
1387
+ description: "Name of participant to add (must match a name from band_lookup_peers)."
1218
1388
  },
1219
1389
  role: {
1220
1390
  type: "string",
@@ -1224,7 +1394,7 @@ var TOOL_MODELS = {
1224
1394
  },
1225
1395
  required: ["name"]
1226
1396
  },
1227
- thenvoi_remove_participant: {
1397
+ band_remove_participant: {
1228
1398
  description: "Remove a participant from the chat room by name.",
1229
1399
  properties: {
1230
1400
  name: {
@@ -1234,12 +1404,12 @@ var TOOL_MODELS = {
1234
1404
  },
1235
1405
  required: ["name"]
1236
1406
  },
1237
- thenvoi_get_participants: {
1407
+ band_get_participants: {
1238
1408
  description: "Get a list of all participants in the current chat room.",
1239
1409
  properties: {},
1240
1410
  required: []
1241
1411
  },
1242
- thenvoi_lookup_peers: {
1412
+ band_lookup_peers: {
1243
1413
  description: "List available peers (agents and users) that can be added to this room. Automatically excludes peers already in the room. Returns dict with 'peers' list and 'metadata' (page, page_size, total_count, total_pages). Use this to find specialized agents (e.g., Weather Agent) when you cannot answer a question directly.",
1244
1414
  properties: {
1245
1415
  page: {
@@ -1254,7 +1424,7 @@ var TOOL_MODELS = {
1254
1424
  },
1255
1425
  required: []
1256
1426
  },
1257
- thenvoi_create_chatroom: {
1427
+ band_create_chatroom: {
1258
1428
  description: "Create a new chat room for a specific task or conversation.",
1259
1429
  properties: {
1260
1430
  task_id: {
@@ -1264,7 +1434,7 @@ var TOOL_MODELS = {
1264
1434
  },
1265
1435
  required: []
1266
1436
  },
1267
- thenvoi_list_contacts: {
1437
+ band_list_contacts: {
1268
1438
  description: "List agent's contacts with pagination.",
1269
1439
  properties: {
1270
1440
  page: {
@@ -1281,7 +1451,7 @@ var TOOL_MODELS = {
1281
1451
  },
1282
1452
  required: []
1283
1453
  },
1284
- thenvoi_add_contact: {
1454
+ band_add_contact: {
1285
1455
  description: "Send a contact request to add someone as a contact. Returns 'pending' when request is created. Returns 'approved' when inverse request existed and was auto-accepted.",
1286
1456
  properties: {
1287
1457
  handle: {
@@ -1295,7 +1465,7 @@ var TOOL_MODELS = {
1295
1465
  },
1296
1466
  required: ["handle"]
1297
1467
  },
1298
- thenvoi_remove_contact: {
1468
+ band_remove_contact: {
1299
1469
  description: "Remove an existing contact by handle or ID.",
1300
1470
  properties: {
1301
1471
  handle: {
@@ -1309,7 +1479,7 @@ var TOOL_MODELS = {
1309
1479
  },
1310
1480
  required: []
1311
1481
  },
1312
- thenvoi_list_contact_requests: {
1482
+ band_list_contact_requests: {
1313
1483
  description: "List both received and sent contact requests. Received requests are always filtered to pending status. Sent requests can be filtered by status.",
1314
1484
  properties: {
1315
1485
  page: {
@@ -1331,7 +1501,7 @@ var TOOL_MODELS = {
1331
1501
  },
1332
1502
  required: []
1333
1503
  },
1334
- thenvoi_respond_contact_request: {
1504
+ band_respond_contact_request: {
1335
1505
  description: "Respond to a contact request. 'approve'/'reject': For requests you RECEIVED (handle = requester's handle). 'cancel': For requests you SENT (handle = recipient's handle).",
1336
1506
  properties: {
1337
1507
  action: {
@@ -1350,7 +1520,7 @@ var TOOL_MODELS = {
1350
1520
  },
1351
1521
  required: ["action"]
1352
1522
  },
1353
- thenvoi_list_memories: {
1523
+ band_list_memories: {
1354
1524
  description: "List memories accessible to the agent. Returns memories about the specified subject (cross-agent sharing) and organization-wide shared memories.",
1355
1525
  properties: {
1356
1526
  subject_id: {
@@ -1359,29 +1529,22 @@ var TOOL_MODELS = {
1359
1529
  },
1360
1530
  scope: {
1361
1531
  type: "string",
1362
- enum: ["subject", "organization", "all"],
1532
+ enum: [...MEMORY_LIST_SCOPES],
1363
1533
  description: "Filter by scope."
1364
1534
  },
1365
1535
  system: {
1366
1536
  type: "string",
1367
- enum: ["sensory", "working", "long_term"],
1537
+ enum: [...MEMORY_SYSTEMS],
1368
1538
  description: "Filter by memory system."
1369
1539
  },
1370
1540
  type: {
1371
1541
  type: "string",
1372
- enum: [
1373
- "iconic",
1374
- "echoic",
1375
- "haptic",
1376
- "episodic",
1377
- "semantic",
1378
- "procedural"
1379
- ],
1542
+ enum: [...MEMORY_TYPES],
1380
1543
  description: "Filter by memory type."
1381
1544
  },
1382
1545
  segment: {
1383
1546
  type: "string",
1384
- enum: ["user", "agent", "tool", "guideline"],
1547
+ enum: [...MEMORY_SEGMENTS],
1385
1548
  description: "Filter by segment."
1386
1549
  },
1387
1550
  content_query: {
@@ -1396,13 +1559,13 @@ var TOOL_MODELS = {
1396
1559
  },
1397
1560
  status: {
1398
1561
  type: "string",
1399
- enum: ["active", "superseded", "archived", "all"],
1562
+ enum: [...MEMORY_STATUSES],
1400
1563
  description: "Filter by status."
1401
1564
  }
1402
1565
  },
1403
1566
  required: []
1404
1567
  },
1405
- thenvoi_store_memory: {
1568
+ band_store_memory: {
1406
1569
  description: "Store a new memory entry. The memory will be associated with the authenticated agent as the source. For subject-scoped memories, provide a subject_id. For organization-scoped memories, omit subject_id.",
1407
1570
  properties: {
1408
1571
  content: {
@@ -1411,24 +1574,17 @@ var TOOL_MODELS = {
1411
1574
  },
1412
1575
  system: {
1413
1576
  type: "string",
1414
- enum: ["sensory", "working", "long_term"],
1577
+ enum: [...MEMORY_SYSTEMS],
1415
1578
  description: "Memory system tier."
1416
1579
  },
1417
1580
  type: {
1418
1581
  type: "string",
1419
- enum: [
1420
- "iconic",
1421
- "echoic",
1422
- "haptic",
1423
- "episodic",
1424
- "semantic",
1425
- "procedural"
1426
- ],
1582
+ enum: [...MEMORY_TYPES],
1427
1583
  description: "Memory type (must be valid for selected system)."
1428
1584
  },
1429
1585
  segment: {
1430
1586
  type: "string",
1431
- enum: ["user", "agent", "tool", "guideline"],
1587
+ enum: [...MEMORY_SEGMENTS],
1432
1588
  description: "Logical segment."
1433
1589
  },
1434
1590
  thought: {
@@ -1437,7 +1593,7 @@ var TOOL_MODELS = {
1437
1593
  },
1438
1594
  scope: {
1439
1595
  type: "string",
1440
- enum: ["subject", "organization"],
1596
+ enum: [...MEMORY_STORE_SCOPES],
1441
1597
  description: "Visibility scope."
1442
1598
  },
1443
1599
  subject_id: {
@@ -1451,7 +1607,7 @@ var TOOL_MODELS = {
1451
1607
  },
1452
1608
  required: ["content", "system", "type", "segment", "thought"]
1453
1609
  },
1454
- thenvoi_get_memory: {
1610
+ band_get_memory: {
1455
1611
  description: "Retrieve a specific memory by ID.",
1456
1612
  properties: {
1457
1613
  memory_id: {
@@ -1461,7 +1617,7 @@ var TOOL_MODELS = {
1461
1617
  },
1462
1618
  required: ["memory_id"]
1463
1619
  },
1464
- thenvoi_supersede_memory: {
1620
+ band_supersede_memory: {
1465
1621
  description: "Mark a memory as superseded (soft delete). Use when information is outdated or incorrect. The memory remains for audit trail but won't appear in normal queries. Only the source agent can supersede.",
1466
1622
  properties: {
1467
1623
  memory_id: {
@@ -1471,7 +1627,7 @@ var TOOL_MODELS = {
1471
1627
  },
1472
1628
  required: ["memory_id"]
1473
1629
  },
1474
- thenvoi_archive_memory: {
1630
+ band_archive_memory: {
1475
1631
  description: "Archive a memory (hide but preserve). Use when memory is valid but not currently needed. Archived memories can be restored later by humans. Only the source agent can archive.",
1476
1632
  properties: {
1477
1633
  memory_id: {
@@ -1484,18 +1640,18 @@ var TOOL_MODELS = {
1484
1640
  };
1485
1641
  var ALL_TOOL_NAMES = new Set(Object.keys(TOOL_MODELS));
1486
1642
  var MEMORY_TOOL_NAMES = /* @__PURE__ */ new Set([
1487
- "thenvoi_list_memories",
1488
- "thenvoi_store_memory",
1489
- "thenvoi_get_memory",
1490
- "thenvoi_supersede_memory",
1491
- "thenvoi_archive_memory"
1643
+ "band_list_memories",
1644
+ "band_store_memory",
1645
+ "band_get_memory",
1646
+ "band_supersede_memory",
1647
+ "band_archive_memory"
1492
1648
  ]);
1493
1649
  var CONTACT_TOOL_NAMES = /* @__PURE__ */ new Set([
1494
- "thenvoi_list_contacts",
1495
- "thenvoi_add_contact",
1496
- "thenvoi_remove_contact",
1497
- "thenvoi_list_contact_requests",
1498
- "thenvoi_respond_contact_request"
1650
+ "band_list_contacts",
1651
+ "band_add_contact",
1652
+ "band_remove_contact",
1653
+ "band_list_contact_requests",
1654
+ "band_respond_contact_request"
1499
1655
  ]);
1500
1656
  var BASE_TOOL_NAMES = new Set(
1501
1657
  [...ALL_TOOL_NAMES].filter((name) => !MEMORY_TOOL_NAMES.has(name))
@@ -1503,7 +1659,8 @@ var BASE_TOOL_NAMES = new Set(
1503
1659
  var CHAT_TOOL_NAMES = new Set(
1504
1660
  [...BASE_TOOL_NAMES].filter((name) => !CONTACT_TOOL_NAMES.has(name))
1505
1661
  );
1506
- var MCP_TOOL_PREFIX = "mcp__thenvoi__";
1662
+ var MCP_TOOL_PREFIX = "mcp__band__";
1663
+ var MCP_SERVER_NAME = "band";
1507
1664
  function mcpToolNames(names) {
1508
1665
  return [...names].sort((a, b) => a.localeCompare(b)).map((name) => `${MCP_TOOL_PREFIX}${name}`);
1509
1666
  }
@@ -1547,51 +1704,17 @@ var CONTACT_REQUEST_ACTIONS = /* @__PURE__ */ new Set([
1547
1704
  "reject",
1548
1705
  "cancel"
1549
1706
  ]);
1550
- var MEMORY_SYSTEMS = /* @__PURE__ */ new Set([
1551
- "sensory",
1552
- "working",
1553
- "long_term"
1554
- ]);
1555
- var MEMORY_TYPES = /* @__PURE__ */ new Set([
1556
- "iconic",
1557
- "echoic",
1558
- "haptic",
1559
- "episodic",
1560
- "semantic",
1561
- "procedural"
1562
- ]);
1563
- var MEMORY_SEGMENTS = /* @__PURE__ */ new Set([
1564
- "user",
1565
- "agent",
1566
- "tool",
1567
- "guideline"
1568
- ]);
1569
- var LIST_MEMORY_SCOPES = /* @__PURE__ */ new Set([
1570
- "subject",
1571
- "organization",
1572
- "all"
1573
- ]);
1574
- var LIST_MEMORY_STATUSES = /* @__PURE__ */ new Set([
1575
- "active",
1576
- "superseded",
1577
- "archived",
1578
- "all"
1579
- ]);
1580
- var MEMORY_SCOPES = /* @__PURE__ */ new Set([
1581
- "subject",
1582
- "organization"
1583
- ]);
1584
1707
  var AgentTools = class {
1585
1708
  roomId;
1586
1709
  capabilities;
1587
1710
  rest;
1588
- participants;
1711
+ roster;
1589
1712
  adapterTools;
1590
1713
  toolHandlers;
1591
1714
  constructor(options) {
1592
1715
  this.roomId = options.roomId;
1593
1716
  this.rest = options.rest;
1594
- this.participants = options.participants ?? [];
1717
+ this.roster = options.roster ?? new import_band_sdk_core.ParticipantRoster();
1595
1718
  this.capabilities = {
1596
1719
  ...DEFAULT_AGENT_TOOLS_CAPABILITIES,
1597
1720
  ...options.capabilities
@@ -1603,17 +1726,20 @@ var AgentTools = class {
1603
1726
  return this.adapterTools;
1604
1727
  }
1605
1728
  async sendMessage(content, mentions = []) {
1606
- if (mentions.length > 0 && typeof mentions[0] === "string" && this.participants.length === 0) {
1607
- await this.syncParticipants();
1729
+ let participants;
1730
+ if (mentions.length > 0 && typeof mentions[0] === "string") {
1731
+ participants = this.roster.list();
1732
+ if (participants.length === 0) {
1733
+ participants = await this.syncParticipants();
1734
+ }
1608
1735
  }
1609
- const resolvedMentions = this.resolveMentions(mentions);
1736
+ const resolvedMentions = this.resolveMentions(mentions, participants);
1610
1737
  return this.rest.createChatMessage(
1611
1738
  this.roomId,
1612
1739
  {
1613
1740
  content,
1614
1741
  mentions: resolvedMentions
1615
- },
1616
- DEFAULT_REQUEST_OPTIONS
1742
+ }
1617
1743
  );
1618
1744
  }
1619
1745
  async sendEvent(content, messageType, metadata) {
@@ -1624,8 +1750,7 @@ var AgentTools = class {
1624
1750
  content,
1625
1751
  messageType,
1626
1752
  metadata
1627
- },
1628
- DEFAULT_REQUEST_OPTIONS
1753
+ }
1629
1754
  );
1630
1755
  }
1631
1756
  async createChatroom(taskId) {
@@ -1664,7 +1789,7 @@ var AgentTools = class {
1664
1789
  type: String(peer.type ?? "Agent"),
1665
1790
  handle: typeof peer.handle === "string" ? peer.handle : null
1666
1791
  };
1667
- this.participants.push(participantRecord);
1792
+ this.roster.add(participantRecord);
1668
1793
  return {
1669
1794
  ...participantRecord,
1670
1795
  status: "added"
@@ -1679,7 +1804,7 @@ var AgentTools = class {
1679
1804
  throw new ValidationError(`Participant '${name}' not found in room`);
1680
1805
  }
1681
1806
  await this.rest.removeChatParticipant(this.roomId, String(participant.id), DEFAULT_REQUEST_OPTIONS);
1682
- this.replaceParticipants(this.participants.filter((entry) => entry.id !== participant.id));
1807
+ this.roster.remove(participant.id);
1683
1808
  return {
1684
1809
  id: participant.id,
1685
1810
  name,
@@ -1707,17 +1832,12 @@ var AgentTools = class {
1707
1832
  }
1708
1833
  async fetchParticipants() {
1709
1834
  const participants = await this.rest.listChatParticipants(this.roomId, DEFAULT_REQUEST_OPTIONS);
1710
- return participants.map((participant) => ({
1711
- id: participant.id,
1712
- name: participant.name,
1713
- type: participant.type,
1714
- handle: participant.handle ?? null
1715
- }));
1835
+ return participants.map(toParticipantRecordFromRest);
1716
1836
  }
1717
1837
  async syncParticipants() {
1718
1838
  const participants = await this.fetchParticipants();
1719
- this.replaceParticipants(participants);
1720
- return [...this.participants];
1839
+ this.roster.setAll(participants);
1840
+ return this.roster.list().map(toParticipantRecord);
1721
1841
  }
1722
1842
  async executeToolCall(toolName, arguments_) {
1723
1843
  const validationError = validateToolArgs(toolName, arguments_);
@@ -1765,7 +1885,7 @@ var AgentTools = class {
1765
1885
  if (!CHAT_TOOL_NAMES.has(name) && !this.capabilities.contacts) {
1766
1886
  return false;
1767
1887
  }
1768
- if (name === "thenvoi_lookup_peers" && !this.capabilities.peers) {
1888
+ if (name === "band_lookup_peers" && !this.capabilities.peers) {
1769
1889
  return false;
1770
1890
  }
1771
1891
  return true;
@@ -1960,7 +2080,7 @@ var AgentTools = class {
1960
2080
  }
1961
2081
  return this.rest.archiveMemory(normalizedMemoryId, DEFAULT_REQUEST_OPTIONS);
1962
2082
  }
1963
- resolveMentions(mentions) {
2083
+ resolveMentions(mentions, participants) {
1964
2084
  if (mentions.length === 0) {
1965
2085
  return [];
1966
2086
  }
@@ -1973,7 +2093,7 @@ var AgentTools = class {
1973
2093
  const participantsByHandle = /* @__PURE__ */ new Map();
1974
2094
  const participantsById = /* @__PURE__ */ new Map();
1975
2095
  const participantsByName = /* @__PURE__ */ new Map();
1976
- for (const participant of this.participants) {
2096
+ for (const participant of participants ?? []) {
1977
2097
  const ref = {
1978
2098
  id: String(participant.id),
1979
2099
  handle: typeof participant.handle === "string" ? participant.handle : void 0
@@ -2029,9 +2149,6 @@ var AgentTools = class {
2029
2149
  normalizeMentionHandle(handle) {
2030
2150
  return handle.trim().replace(/^@+/, "").toLowerCase();
2031
2151
  }
2032
- replaceParticipants(participants) {
2033
- this.participants.splice(0, this.participants.length, ...participants);
2034
- }
2035
2152
  buildAdapterTools() {
2036
2153
  const tools = {
2037
2154
  capabilities: this.capabilities
@@ -2062,51 +2179,51 @@ var AgentTools = class {
2062
2179
  }
2063
2180
  buildMessagingToolHandlers() {
2064
2181
  return {
2065
- thenvoi_send_message: async (arguments_) => this.sendMessage(
2182
+ band_send_message: async (arguments_) => this.sendMessage(
2066
2183
  String(arguments_.content ?? ""),
2067
2184
  this.normalizeMentionInput(arguments_.mentions)
2068
2185
  ),
2069
- thenvoi_send_event: async (arguments_) => this.sendEvent(
2186
+ band_send_event: async (arguments_) => this.sendEvent(
2070
2187
  String(arguments_.content ?? ""),
2071
2188
  String(arguments_.message_type ?? "task"),
2072
2189
  this.normalizeOptionalMetadata(arguments_.metadata)
2073
2190
  ),
2074
- thenvoi_add_participant: async (arguments_) => this.addParticipant(String(arguments_.name ?? ""), String(arguments_.role ?? "member")),
2075
- thenvoi_remove_participant: async (arguments_) => this.removeParticipant(String(arguments_.name ?? "")),
2076
- thenvoi_lookup_peers: async (arguments_) => this.lookupPeers(
2191
+ band_add_participant: async (arguments_) => this.addParticipant(String(arguments_.name ?? ""), String(arguments_.role ?? "member")),
2192
+ band_remove_participant: async (arguments_) => this.removeParticipant(String(arguments_.name ?? "")),
2193
+ band_lookup_peers: async (arguments_) => this.lookupPeers(
2077
2194
  coercePositiveInt(arguments_.page, 1),
2078
2195
  coercePositiveInt(arguments_.page_size, 50)
2079
2196
  ),
2080
- thenvoi_get_participants: async () => this.getParticipants(),
2081
- thenvoi_create_chatroom: async (arguments_) => this.createChatroom(this.normalizeOptionalString(arguments_.task_id))
2197
+ band_get_participants: async () => this.getParticipants(),
2198
+ band_create_chatroom: async (arguments_) => this.createChatroom(this.normalizeOptionalString(arguments_.task_id))
2082
2199
  };
2083
2200
  }
2084
2201
  buildContactToolHandlers() {
2085
2202
  return {
2086
- thenvoi_list_contacts: async (arguments_) => this.listContacts({
2203
+ band_list_contacts: async (arguments_) => this.listContacts({
2087
2204
  page: coercePositiveInt(arguments_.page, 1),
2088
2205
  pageSize: coercePositiveInt(arguments_.page_size, 50)
2089
2206
  }),
2090
- thenvoi_add_contact: async (arguments_) => this.addContact({
2207
+ band_add_contact: async (arguments_) => this.addContact({
2091
2208
  handle: String(arguments_.handle ?? ""),
2092
2209
  ...typeof arguments_.message === "string" ? { message: arguments_.message } : {}
2093
2210
  }),
2094
- thenvoi_remove_contact: async (arguments_) => this.removeContact(this.toRemoveContactArgs(arguments_)),
2095
- thenvoi_list_contact_requests: async (arguments_) => this.listContactRequests({
2211
+ band_remove_contact: async (arguments_) => this.removeContact(this.toRemoveContactArgs(arguments_)),
2212
+ band_list_contact_requests: async (arguments_) => this.listContactRequests({
2096
2213
  page: coercePositiveInt(arguments_.page, 1),
2097
2214
  pageSize: coercePositiveInt(arguments_.page_size, 50),
2098
2215
  sentStatus: String(arguments_.sent_status ?? "pending")
2099
2216
  }),
2100
- thenvoi_respond_contact_request: async (arguments_) => this.respondContactRequest(this.toRespondContactRequestArgs(arguments_))
2217
+ band_respond_contact_request: async (arguments_) => this.respondContactRequest(this.toRespondContactRequestArgs(arguments_))
2101
2218
  };
2102
2219
  }
2103
2220
  buildMemoryToolHandlers() {
2104
2221
  return {
2105
- thenvoi_list_memories: async (arguments_) => this.listMemories(this.toListMemoriesArgs(arguments_)),
2106
- thenvoi_store_memory: async (arguments_) => this.storeMemory(this.toStoreMemoryArgs(arguments_)),
2107
- thenvoi_get_memory: async (arguments_) => this.getMemory(String(arguments_.memory_id ?? "")),
2108
- thenvoi_supersede_memory: async (arguments_) => this.supersedeMemory(String(arguments_.memory_id ?? "")),
2109
- thenvoi_archive_memory: async (arguments_) => this.archiveMemory(String(arguments_.memory_id ?? ""))
2222
+ band_list_memories: async (arguments_) => this.listMemories(this.toListMemoriesArgs(arguments_)),
2223
+ band_store_memory: async (arguments_) => this.storeMemory(this.toStoreMemoryArgs(arguments_)),
2224
+ band_get_memory: async (arguments_) => this.getMemory(String(arguments_.memory_id ?? "")),
2225
+ band_supersede_memory: async (arguments_) => this.supersedeMemory(String(arguments_.memory_id ?? "")),
2226
+ band_archive_memory: async (arguments_) => this.archiveMemory(String(arguments_.memory_id ?? ""))
2110
2227
  };
2111
2228
  }
2112
2229
  toRemoveContactArgs(arguments_) {
@@ -2158,6 +2275,9 @@ var AgentTools = class {
2158
2275
  const status = this.normalizeOptionalMemoryStatus(arguments_.status);
2159
2276
  const subjectId = this.normalizeOptionalString(arguments_.subject_id);
2160
2277
  const contentQuery = this.normalizeOptionalString(arguments_.content_query);
2278
+ if (system && type && !isMemoryTypeForSystem(system, type)) {
2279
+ throw new ValidationError(memoryTypeForSystemError(system));
2280
+ }
2161
2281
  return {
2162
2282
  ...subjectId ? { subject_id: subjectId } : {},
2163
2283
  ...scope ? { scope } : {},
@@ -2179,13 +2299,21 @@ var AgentTools = class {
2179
2299
  const subjectId = this.normalizeOptionalString(arguments_.subject_id);
2180
2300
  const metadata = this.normalizeOptionalMetadata(arguments_.metadata);
2181
2301
  if (!system) {
2182
- throw new ValidationError("system must be one of: sensory, working, long_term");
2302
+ throw new ValidationError(`system must be one of: ${expectedList(MEMORY_SYSTEMS)}`);
2183
2303
  }
2184
2304
  if (!type) {
2185
- throw new ValidationError("type must be one of: iconic, echoic, haptic, episodic, semantic, procedural");
2305
+ throw new ValidationError(`type must be one of: ${expectedList(MEMORY_TYPES)}`);
2306
+ }
2307
+ if (!isMemoryTypeForSystem(system, type)) {
2308
+ throw new ValidationError(memoryTypeForSystemError(system));
2186
2309
  }
2187
2310
  if (!segment) {
2188
- throw new ValidationError("segment must be one of: user, agent, tool, guideline");
2311
+ throw new ValidationError(`segment must be one of: ${expectedList(MEMORY_SEGMENTS)}`);
2312
+ }
2313
+ if (scope === "subject" && !subjectId) {
2314
+ throw new ValidationError(
2315
+ '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.'
2316
+ );
2189
2317
  }
2190
2318
  return {
2191
2319
  content,
@@ -2265,8 +2393,8 @@ var AgentTools = class {
2265
2393
  if (!normalized) {
2266
2394
  return void 0;
2267
2395
  }
2268
- if (!isListMemoryScope(normalized)) {
2269
- throw new ValidationError("scope must be one of: subject, organization, all");
2396
+ if (!isMemoryListScope(normalized)) {
2397
+ throw new ValidationError(`scope must be one of: ${expectedList(MEMORY_LIST_SCOPES)}`);
2270
2398
  }
2271
2399
  return normalized;
2272
2400
  }
@@ -2276,7 +2404,7 @@ var AgentTools = class {
2276
2404
  return void 0;
2277
2405
  }
2278
2406
  if (!isMemorySystem(normalized)) {
2279
- throw new ValidationError("system must be one of: sensory, working, long_term");
2407
+ throw new ValidationError(`system must be one of: ${expectedList(MEMORY_SYSTEMS)}`);
2280
2408
  }
2281
2409
  return normalized;
2282
2410
  }
@@ -2286,7 +2414,7 @@ var AgentTools = class {
2286
2414
  return void 0;
2287
2415
  }
2288
2416
  if (!isMemoryType(normalized)) {
2289
- throw new ValidationError("type must be one of: iconic, echoic, haptic, episodic, semantic, procedural");
2417
+ throw new ValidationError(`type must be one of: ${expectedList(MEMORY_TYPES)}`);
2290
2418
  }
2291
2419
  return normalized;
2292
2420
  }
@@ -2296,7 +2424,7 @@ var AgentTools = class {
2296
2424
  return void 0;
2297
2425
  }
2298
2426
  if (!isMemorySegment(normalized)) {
2299
- throw new ValidationError("segment must be one of: user, agent, tool, guideline");
2427
+ throw new ValidationError(`segment must be one of: ${expectedList(MEMORY_SEGMENTS)}`);
2300
2428
  }
2301
2429
  return normalized;
2302
2430
  }
@@ -2306,7 +2434,7 @@ var AgentTools = class {
2306
2434
  return void 0;
2307
2435
  }
2308
2436
  if (!isMemoryStatus(normalized)) {
2309
- throw new ValidationError("status must be one of: active, superseded, archived, all");
2437
+ throw new ValidationError(`status must be one of: ${expectedList(MEMORY_STATUSES)}`);
2310
2438
  }
2311
2439
  return normalized;
2312
2440
  }
@@ -2315,8 +2443,8 @@ var AgentTools = class {
2315
2443
  if (!normalized) {
2316
2444
  return void 0;
2317
2445
  }
2318
- if (!isStoreMemoryScope(normalized)) {
2319
- throw new ValidationError("scope must be one of: subject, organization");
2446
+ if (!isMemoryStoreScope(normalized)) {
2447
+ throw new ValidationError(`scope must be one of: ${expectedList(MEMORY_STORE_SCOPES)}`);
2320
2448
  }
2321
2449
  return normalized;
2322
2450
  }
@@ -2324,24 +2452,6 @@ var AgentTools = class {
2324
2452
  function isContactRequestAction(value) {
2325
2453
  return CONTACT_REQUEST_ACTIONS.has(value);
2326
2454
  }
2327
- function isMemorySystem(value) {
2328
- return MEMORY_SYSTEMS.has(value);
2329
- }
2330
- function isMemoryType(value) {
2331
- return MEMORY_TYPES.has(value);
2332
- }
2333
- function isMemorySegment(value) {
2334
- return MEMORY_SEGMENTS.has(value);
2335
- }
2336
- function isStoreMemoryScope(value) {
2337
- return MEMORY_SCOPES.has(value);
2338
- }
2339
- function isListMemoryScope(value) {
2340
- return LIST_MEMORY_SCOPES.has(value);
2341
- }
2342
- function isMemoryStatus(value) {
2343
- return LIST_MEMORY_STATUSES.has(value);
2344
- }
2345
2455
  function coercePositiveInt(value, fallback) {
2346
2456
  const num = Number(value ?? fallback);
2347
2457
  return Number.isFinite(num) && num >= 1 ? Math.floor(num) : fallback;
@@ -2357,13 +2467,13 @@ function validateToolArgs(toolName, args) {
2357
2467
  errors.push(`${field}: Field required`);
2358
2468
  }
2359
2469
  }
2360
- if (toolName === "thenvoi_send_message") {
2470
+ if (toolName === "band_send_message") {
2361
2471
  const mentions = args.mentions;
2362
2472
  if (Array.isArray(mentions) && mentions.length === 0) {
2363
2473
  errors.push("mentions: At least one mention is required");
2364
2474
  }
2365
2475
  }
2366
- if (toolName === "thenvoi_send_event") {
2476
+ if (toolName === "band_send_event") {
2367
2477
  const messageType = args.message_type;
2368
2478
  if (typeof messageType === "string" && !CHAT_EVENT_TYPES.includes(messageType)) {
2369
2479
  errors.push(
@@ -2371,7 +2481,7 @@ function validateToolArgs(toolName, args) {
2371
2481
  );
2372
2482
  }
2373
2483
  }
2374
- if (toolName === "thenvoi_respond_contact_request") {
2484
+ if (toolName === "band_respond_contact_request") {
2375
2485
  const action = args.action;
2376
2486
  const validActions = ["approve", "reject", "cancel"];
2377
2487
  if (typeof action === "string" && !validActions.includes(action)) {
@@ -2380,18 +2490,20 @@ function validateToolArgs(toolName, args) {
2380
2490
  );
2381
2491
  }
2382
2492
  }
2383
- if (toolName === "thenvoi_store_memory") {
2384
- const validSystems = ["sensory", "working", "long_term"];
2385
- const validTypes = ["iconic", "echoic", "haptic", "episodic", "semantic", "procedural"];
2386
- const validSegments = ["user", "agent", "tool", "guideline"];
2387
- if (typeof args.system === "string" && !validSystems.includes(args.system)) {
2388
- errors.push(`system: Invalid value '${args.system}'. Expected one of: ${validSystems.join(", ")}`);
2493
+ if (toolName === "band_store_memory") {
2494
+ if (typeof args.system === "string" && !isMemorySystem(args.system)) {
2495
+ errors.push(`system: Invalid value '${args.system}'. Expected one of: ${expectedList(MEMORY_SYSTEMS)}`);
2389
2496
  }
2390
- if (typeof args.type === "string" && !validTypes.includes(args.type)) {
2391
- errors.push(`type: Invalid value '${args.type}'. Expected one of: ${validTypes.join(", ")}`);
2497
+ if (typeof args.type === "string" && !isMemoryType(args.type)) {
2498
+ errors.push(`type: Invalid value '${args.type}'. Expected one of: ${expectedList(MEMORY_TYPES)}`);
2499
+ }
2500
+ if (typeof args.system === "string" && isMemorySystem(args.system) && typeof args.type === "string" && isMemoryType(args.type) && !isMemoryTypeForSystem(args.system, args.type)) {
2501
+ errors.push(
2502
+ `type: Invalid value '${args.type}' for system '${args.system}'. Expected one of: ${expectedMemoryTypesForSystem(args.system)}`
2503
+ );
2392
2504
  }
2393
- if (typeof args.segment === "string" && !validSegments.includes(args.segment)) {
2394
- errors.push(`segment: Invalid value '${args.segment}'. Expected one of: ${validSegments.join(", ")}`);
2505
+ if (typeof args.segment === "string" && !isMemorySegment(args.segment)) {
2506
+ errors.push(`segment: Invalid value '${args.segment}'. Expected one of: ${expectedList(MEMORY_SEGMENTS)}`);
2395
2507
  }
2396
2508
  }
2397
2509
  if (errors.length > 0) {
@@ -2407,112 +2519,8 @@ function validateToolArgs(toolName, args) {
2407
2519
  return null;
2408
2520
  }
2409
2521
 
2410
- // src/runtime/retryTracker.ts
2411
- var DEFAULT_MAX_TRACKED = 1e4;
2412
- var MessageRetryTracker = class {
2413
- maxRetriesValue;
2414
- maxTracked;
2415
- attempts = /* @__PURE__ */ new Map();
2416
- failed = /* @__PURE__ */ new Set();
2417
- constructor(maxRetries = 1, maxTracked = DEFAULT_MAX_TRACKED) {
2418
- this.maxRetriesValue = maxRetries;
2419
- this.maxTracked = maxTracked;
2420
- }
2421
- get maxRetries() {
2422
- return this.maxRetriesValue;
2423
- }
2424
- isPermanentlyFailed(messageId) {
2425
- return this.failed.has(messageId);
2426
- }
2427
- recordAttempt(messageId) {
2428
- const attempts = (this.attempts.get(messageId) ?? 0) + 1;
2429
- this.attempts.set(messageId, attempts);
2430
- const exceeded = attempts > this.maxRetriesValue;
2431
- if (exceeded) {
2432
- this.evictSetIfNeeded(this.failed);
2433
- this.failed.add(messageId);
2434
- }
2435
- this.evictMapIfNeeded(this.attempts);
2436
- return [attempts, exceeded];
2437
- }
2438
- markSuccess(messageId) {
2439
- this.attempts.delete(messageId);
2440
- }
2441
- markPermanentlyFailed(messageId) {
2442
- this.evictSetIfNeeded(this.failed);
2443
- this.failed.add(messageId);
2444
- }
2445
- evictMapIfNeeded(map) {
2446
- while (map.size >= this.maxTracked) {
2447
- const first = map.keys().next();
2448
- if (first.done) break;
2449
- map.delete(first.value);
2450
- }
2451
- }
2452
- evictSetIfNeeded(set) {
2453
- while (set.size >= this.maxTracked) {
2454
- const first = set.values().next();
2455
- if (first.done) break;
2456
- set.delete(first.value);
2457
- }
2458
- }
2459
- };
2460
-
2461
- // src/runtime/formatters.ts
2462
- function isMetadataMap(value) {
2463
- return value !== null && typeof value === "object" && !Array.isArray(value);
2464
- }
2465
- function replaceUuidMentions(content, participants) {
2466
- if (!content || participants.length === 0) {
2467
- return content;
2468
- }
2469
- let next = content;
2470
- for (const participant of participants) {
2471
- const participantId = participant.id;
2472
- const handle = participant.handle;
2473
- if (typeof participantId === "string" && typeof handle === "string") {
2474
- next = next.replaceAll(`@[[${participantId}]]`, `@${handle}`);
2475
- }
2476
- }
2477
- return next;
2478
- }
2479
- function formatMessageForLlm(message, participants) {
2480
- const senderType = String(message.sender_type ?? "");
2481
- const senderName = String(message.sender_name ?? message.name ?? senderType);
2482
- const content = participants ? replaceUuidMentions(String(message.content ?? ""), participants) : String(message.content ?? "");
2483
- return {
2484
- role: senderType === "Agent" ? "assistant" : "user",
2485
- content,
2486
- sender_name: senderName,
2487
- sender_type: senderType,
2488
- message_type: String(message.message_type ?? "text"),
2489
- metadata: isMetadataMap(message.metadata) ? message.metadata : {}
2490
- };
2491
- }
2492
- function formatHistoryForLlm(messages, options) {
2493
- const excludeId = options?.excludeId;
2494
- const participants = options?.participants;
2495
- return messages.filter((message) => String(message.id ?? "") !== excludeId).map((message) => formatMessageForLlm(message, participants));
2496
- }
2497
- function buildParticipantsMessage(participants) {
2498
- if (participants.length === 0) {
2499
- return "## Current Participants\nNo other participants in this room.";
2500
- }
2501
- const lines = ["## Current Participants"];
2502
- for (const participant of participants) {
2503
- const participantType = String(participant.type ?? "Unknown");
2504
- const participantName = String(participant.name ?? "Unknown");
2505
- const participantHandle = String(participant.handle ?? "Unknown");
2506
- lines.push(`- @${participantHandle} \u2014 ${participantName} (${participantType})`);
2507
- }
2508
- lines.push("");
2509
- lines.push(
2510
- "IMPORTANT: In thenvoi_send_message mentions, always use the exact handle shown above (e.g. '@john' for users, '@john/weather-agent' for agents), NOT the display name. Handles are lowercase with no spaces."
2511
- );
2512
- return lines.join("\n");
2513
- }
2514
-
2515
2522
  // src/runtime/ExecutionContext.ts
2523
+ var import_band_sdk_core2 = require("@band-ai/band-sdk-core");
2516
2524
  var DEDUP_CACHE_MAX = 500;
2517
2525
  var ExecutionContext = class {
2518
2526
  roomId;
@@ -2524,11 +2532,10 @@ var ExecutionContext = class {
2524
2532
  history = [];
2525
2533
  messageIds = /* @__PURE__ */ new Set();
2526
2534
  dedupCache = /* @__PURE__ */ new Map();
2527
- participants = [];
2535
+ roster = new import_band_sdk_core2.ParticipantRoster();
2528
2536
  tools;
2529
2537
  adapterTools;
2530
2538
  participantsMessage = null;
2531
- lastSentParticipantIds = null;
2532
2539
  contactsMessage = null;
2533
2540
  contextCache = null;
2534
2541
  contextCacheExpiresAt = 0;
@@ -2543,11 +2550,11 @@ var ExecutionContext = class {
2543
2550
  this.enableContextCache = options.enableContextCache ?? true;
2544
2551
  this.contextCacheTtlMs = Math.max(0, (options.contextCacheTtlSeconds ?? 300) * 1e3);
2545
2552
  this.enableContextHydration = options.enableContextHydration ?? true;
2546
- this.retryTrackerInstance = new MessageRetryTracker(options.maxMessageRetries ?? 1);
2553
+ this.retryTrackerInstance = new import_band_sdk_core2.RetryTracker(options.maxMessageRetries ?? 1);
2547
2554
  this.tools = new AgentTools({
2548
2555
  roomId: this.roomId,
2549
2556
  rest: this.link.rest,
2550
- participants: this.participants,
2557
+ roster: this.roster,
2551
2558
  capabilities: this.link.capabilities
2552
2559
  });
2553
2560
  this.adapterTools = this.tools.getAdapterTools();
@@ -2615,23 +2622,20 @@ var ExecutionContext = class {
2615
2622
  }));
2616
2623
  }
2617
2624
  setParticipants(participants) {
2618
- this.replaceParticipants(participants);
2625
+ this.roster.setAll(participants);
2619
2626
  this.updateCachedParticipants();
2620
2627
  }
2621
2628
  addParticipant(participant) {
2622
- const existingIndex = this.participants.findIndex((entry) => entry.id === participant.id);
2623
- if (existingIndex >= 0) {
2624
- this.participants.splice(existingIndex, 1);
2629
+ const isNew = this.roster.add(participant);
2630
+ if (isNew) {
2631
+ const name = String(participant.name ?? "unknown");
2632
+ this.participantsMessage = `${name} joined the room.`;
2625
2633
  }
2626
- this.participants.push(participant);
2627
- const name = String(participant.name ?? "unknown");
2628
- this.participantsMessage = `${name} joined the room.`;
2629
2634
  this.updateCachedParticipants();
2630
2635
  }
2631
2636
  removeParticipant(participantId) {
2632
- const removed = this.participants.find((entry) => String(entry.id) === participantId);
2633
- const next = this.participants.filter((entry) => String(entry.id) !== participantId);
2634
- this.replaceParticipants(next);
2637
+ const removed = this.roster.list().find((entry) => entry.id === participantId);
2638
+ this.roster.remove(participantId);
2635
2639
  if (removed) {
2636
2640
  this.participantsMessage = `${String(removed.name ?? participantId)} left the room.`;
2637
2641
  }
@@ -2641,16 +2645,7 @@ var ExecutionContext = class {
2641
2645
  this.contactsMessage = message;
2642
2646
  }
2643
2647
  consumeParticipantsMessage() {
2644
- const currentIds = new Set(this.participants.map((p) => String(p.id)));
2645
- let changed = !this.lastSentParticipantIds || currentIds.size !== this.lastSentParticipantIds.size;
2646
- if (!changed) {
2647
- for (const id of currentIds) {
2648
- if (!this.lastSentParticipantIds.has(id)) {
2649
- changed = true;
2650
- break;
2651
- }
2652
- }
2653
- }
2648
+ const changed = this.roster.changed();
2654
2649
  if (!changed && !this.participantsMessage) {
2655
2650
  return null;
2656
2651
  }
@@ -2660,15 +2655,9 @@ var ExecutionContext = class {
2660
2655
  this.participantsMessage = null;
2661
2656
  }
2662
2657
  if (changed) {
2663
- const asRecords = this.participants.map((p) => ({
2664
- id: p.id,
2665
- name: p.name,
2666
- type: p.type,
2667
- handle: p.handle ?? null
2668
- }));
2669
- parts.push(buildParticipantsMessage(asRecords));
2670
- }
2671
- this.lastSentParticipantIds = currentIds;
2658
+ parts.push(buildParticipantsMessage(this.roster.list().map((p) => ({ ...toParticipantRecord(p) }))));
2659
+ }
2660
+ this.roster.markSent();
2672
2661
  return parts.length > 0 ? parts.join("\n") : null;
2673
2662
  }
2674
2663
  consumeContactsMessage() {
@@ -2719,27 +2708,19 @@ var ExecutionContext = class {
2719
2708
  return fallback;
2720
2709
  }
2721
2710
  }
2722
- replaceParticipants(participants) {
2723
- this.participants.splice(0, this.participants.length, ...participants);
2724
- }
2725
2711
  buildLocalContext() {
2726
2712
  return {
2727
2713
  roomId: this.roomId,
2728
2714
  messages: this.getRawHistory(),
2729
- participants: [...this.participants],
2715
+ participants: this.roster.list().map(toParticipantRecord),
2730
2716
  hydratedAt: /* @__PURE__ */ new Date()
2731
2717
  };
2732
2718
  }
2733
2719
  async loadParticipants() {
2734
2720
  const participants = await this.link.rest.listChatParticipants(this.roomId, DEFAULT_REQUEST_OPTIONS);
2735
- const normalized = participants.map((participant) => ({
2736
- id: participant.id,
2737
- name: participant.name,
2738
- type: participant.type,
2739
- handle: participant.handle ?? null
2740
- }));
2741
- this.replaceParticipants(normalized);
2742
- return [...this.participants];
2721
+ const normalized = participants.map(toParticipantRecordFromRest);
2722
+ this.roster.setAll(normalized);
2723
+ return this.roster.list().map(toParticipantRecord);
2743
2724
  }
2744
2725
  async loadHydratedMessages() {
2745
2726
  const messages = [];
@@ -2781,7 +2762,7 @@ var ExecutionContext = class {
2781
2762
  if (!this.contextCache) {
2782
2763
  return;
2783
2764
  }
2784
- this.contextCache.participants = [...this.participants];
2765
+ this.contextCache.participants = this.roster.list().map(toParticipantRecord);
2785
2766
  this.contextCache.hydratedAt = /* @__PURE__ */ new Date();
2786
2767
  this.contextCacheExpiresAt = this.nextCacheExpiry();
2787
2768
  }
@@ -2982,7 +2963,7 @@ var AgentRuntime = class {
2982
2963
  await this.link.disconnect();
2983
2964
  }
2984
2965
  async stop(timeoutMs) {
2985
- if (!this.running || this.stopping) {
2966
+ if (this.stopping || !this.running && !this.fatalError) {
2986
2967
  return true;
2987
2968
  }
2988
2969
  this.stopping = true;
@@ -3002,7 +2983,8 @@ var AgentRuntime = class {
3002
2983
  }
3003
2984
  }
3004
2985
  for (const roomId of [...this.subscribedRooms]) {
3005
- await this.leaveTrackedRoom(roomId);
2986
+ const remaining = deadline === void 0 ? void 0 : Math.max(0, deadline - Date.now());
2987
+ await this.leaveTrackedRoom(roomId, remaining);
3006
2988
  }
3007
2989
  for (const roomId of [...this.contexts.keys()]) {
3008
2990
  await this.onSessionCleanup(roomId);
@@ -3038,7 +3020,13 @@ var AgentRuntime = class {
3038
3020
  }
3039
3021
  async consumeLoop(signal) {
3040
3022
  while (!signal.aborted) {
3041
- const event = await this.link.nextEvent(signal);
3023
+ let event;
3024
+ try {
3025
+ event = await this.link.nextEvent(signal);
3026
+ } catch (error) {
3027
+ await this.failRuntime(error, syntheticRuntimeFailureEvent(this.agentId));
3028
+ return;
3029
+ }
3042
3030
  if (!event) {
3043
3031
  return;
3044
3032
  }
@@ -3201,13 +3189,13 @@ var AgentRuntime = class {
3201
3189
  }
3202
3190
  });
3203
3191
  }
3204
- async leaveTrackedRoom(roomId) {
3192
+ async leaveTrackedRoom(roomId, timeoutMs) {
3205
3193
  await trackRoomLeave({
3206
3194
  link: this.link,
3207
3195
  roomId,
3208
3196
  trackedRooms: this.subscribedRooms,
3209
3197
  onLeft: async (leftRoomId) => {
3210
- await this.executions.get(leftRoomId)?.stop();
3198
+ await this.executions.get(leftRoomId)?.stop(timeoutMs);
3211
3199
  this.contexts.delete(leftRoomId);
3212
3200
  this.executions.delete(leftRoomId);
3213
3201
  await this.onSessionCleanup(leftRoomId);
@@ -3244,6 +3232,23 @@ var AgentRuntime = class {
3244
3232
  }
3245
3233
  }
3246
3234
  };
3235
+ function syntheticRuntimeFailureEvent(agentId) {
3236
+ return {
3237
+ type: "message_created",
3238
+ roomId: null,
3239
+ payload: {
3240
+ id: "runtime-failed",
3241
+ content: "",
3242
+ sender_id: agentId,
3243
+ sender_type: "Agent",
3244
+ sender_name: null,
3245
+ message_type: "text",
3246
+ metadata: {},
3247
+ inserted_at: (/* @__PURE__ */ new Date(0)).toISOString(),
3248
+ updated_at: (/* @__PURE__ */ new Date(0)).toISOString()
3249
+ }
3250
+ };
3251
+ }
3247
3252
  function assertNever2(value) {
3248
3253
  throw new Error(`Unhandled platform event: ${JSON.stringify(value)}`);
3249
3254
  }
@@ -3469,54 +3474,16 @@ function normalizeContactRequestsResult(result) {
3469
3474
  }
3470
3475
 
3471
3476
  // src/client/rest/FernRestAdapter.ts
3472
- function mergeOptions(options) {
3477
+ function mergeOptions(options, maxRetries) {
3473
3478
  return {
3474
3479
  ...DEFAULT_REQUEST_OPTIONS,
3480
+ ...maxRetries !== void 0 ? { maxRetries } : {},
3475
3481
  ...options
3476
3482
  };
3477
3483
  }
3478
- var AGENT_ME_RETRY_LIMIT = 4;
3479
- var AGENT_ME_RETRY_BASE_DELAY_MS = 2e3;
3480
- var MESSAGE_SEND_RETRY_LIMIT = 3;
3481
- var MESSAGE_SEND_RETRY_BASE_DELAY_MS = 500;
3482
3484
  function asMetadataMap2(value) {
3483
3485
  return asOptionalRecord(value);
3484
3486
  }
3485
- function extractHttpStatus(error) {
3486
- const record = asOptionalRecord(error);
3487
- if (!record) {
3488
- return void 0;
3489
- }
3490
- const response = asOptionalRecord(record.response);
3491
- const status = record.statusCode ?? record.status ?? response?.statusCode ?? response?.status;
3492
- return typeof status === "number" ? status : void 0;
3493
- }
3494
- function isFernRateLimitError(error) {
3495
- return extractHttpStatus(error) === 429;
3496
- }
3497
- async function sleep(ms) {
3498
- await new Promise((resolveDelay) => setTimeout(resolveDelay, ms));
3499
- }
3500
- function computeRetryDelayMs(baseDelayMs, attempt) {
3501
- const backoff = baseDelayMs * 2 ** (attempt - 1);
3502
- const jitter = Math.floor(Math.random() * Math.max(1, Math.floor(backoff / 4)));
3503
- return backoff + jitter;
3504
- }
3505
- async function withRateLimitRetry(operation, options) {
3506
- let lastError;
3507
- for (let attempt = 1; attempt <= options.retryLimit; attempt += 1) {
3508
- try {
3509
- return await operation();
3510
- } catch (error) {
3511
- lastError = error;
3512
- if (!isFernRateLimitError(error) || attempt === options.retryLimit) {
3513
- throw error;
3514
- }
3515
- await sleep(computeRetryDelayMs(options.baseDelayMs, attempt));
3516
- }
3517
- }
3518
- throw lastError instanceof Error ? lastError : new Error("Rate-limit retry exhausted without a terminal error.");
3519
- }
3520
3487
  function requireNonEmptyStringField(value, field, source) {
3521
3488
  if (typeof value !== "string" || value.trim().length === 0) {
3522
3489
  throw new Error(`Invalid ${source} response: expected non-empty string AgentIdentity.${field}`);
@@ -3724,28 +3691,21 @@ var FernRestAdapter = class {
3724
3691
  this.client = client;
3725
3692
  }
3726
3693
  async getAgentMe(options) {
3727
- return withRateLimitRetry(
3728
- async () => {
3729
- if (this.client.agentApiIdentity?.getAgentMe) {
3730
- const response = await this.client.agentApiIdentity.getAgentMe(mergeOptions(options));
3731
- return normalizeAgentIdentityEnvelope(response, "agentApiIdentity.getAgentMe");
3732
- }
3733
- const profileClient = this.client.myProfile ?? this.client.humanApiProfile;
3734
- if (!profileClient?.getMyProfile) {
3735
- throw new UnsupportedFeatureError(
3736
- "Fern client missing agentApiIdentity.getAgentMe or humanApiProfile.getMyProfile"
3737
- );
3738
- }
3739
- const profile = await profileClient.getMyProfile(mergeOptions(options));
3740
- return normalizeLegacyProfileIdentity(
3741
- normalizeLegacyProfileEnvelope(profile),
3742
- "profile.getMyProfile"
3743
- );
3744
- },
3745
- {
3746
- retryLimit: AGENT_ME_RETRY_LIMIT,
3747
- baseDelayMs: AGENT_ME_RETRY_BASE_DELAY_MS
3748
- }
3694
+ const requestOptions = mergeOptions(options);
3695
+ if (this.client.agentApiIdentity?.getAgentMe) {
3696
+ const response = await this.client.agentApiIdentity.getAgentMe(requestOptions);
3697
+ return normalizeAgentIdentityEnvelope(response, "agentApiIdentity.getAgentMe");
3698
+ }
3699
+ const profileClient = this.client.myProfile ?? this.client.humanApiProfile;
3700
+ if (!profileClient?.getMyProfile) {
3701
+ throw new UnsupportedFeatureError(
3702
+ "Fern client missing agentApiIdentity.getAgentMe or humanApiProfile.getMyProfile"
3703
+ );
3704
+ }
3705
+ const profile = await profileClient.getMyProfile(requestOptions);
3706
+ return normalizeLegacyProfileIdentity(
3707
+ normalizeLegacyProfileEnvelope(profile),
3708
+ "profile.getMyProfile"
3749
3709
  );
3750
3710
  }
3751
3711
  async createChatMessage(chatId, message, options) {
@@ -3753,45 +3713,33 @@ var FernRestAdapter = class {
3753
3713
  if (!api) {
3754
3714
  throw new UnsupportedFeatureError("Fern client missing chat message creation endpoint");
3755
3715
  }
3756
- const response = await withRateLimitRetry(
3757
- async () => await api(
3758
- chatId,
3759
- {
3760
- message: {
3761
- content: message.content,
3762
- message_type: message.messageType,
3763
- metadata: message.metadata,
3764
- mentions: message.mentions
3765
- }
3766
- },
3767
- mergeOptions(options)
3768
- ),
3716
+ const response = await api(
3717
+ chatId,
3769
3718
  {
3770
- retryLimit: MESSAGE_SEND_RETRY_LIMIT,
3771
- baseDelayMs: MESSAGE_SEND_RETRY_BASE_DELAY_MS
3772
- }
3719
+ message: {
3720
+ content: message.content,
3721
+ message_type: message.messageType,
3722
+ metadata: message.metadata,
3723
+ mentions: message.mentions
3724
+ }
3725
+ },
3726
+ mergeOptions(options, MESSAGE_SEND_MAX_RETRIES)
3773
3727
  );
3774
3728
  return normalizeToolOperationResult(response);
3775
3729
  }
3776
3730
  async createChatEvent(chatId, event, options) {
3777
3731
  const createAgentChatEvent = this.client.agentApiEvents?.createAgentChatEvent?.bind(this.client.agentApiEvents);
3778
3732
  if (createAgentChatEvent) {
3779
- const response = await withRateLimitRetry(
3780
- async () => await createAgentChatEvent(
3781
- chatId,
3782
- {
3783
- event: {
3784
- content: event.content,
3785
- message_type: event.messageType,
3786
- metadata: event.metadata
3787
- }
3788
- },
3789
- mergeOptions(options)
3790
- ),
3733
+ const response = await createAgentChatEvent(
3734
+ chatId,
3791
3735
  {
3792
- retryLimit: MESSAGE_SEND_RETRY_LIMIT,
3793
- baseDelayMs: MESSAGE_SEND_RETRY_BASE_DELAY_MS
3794
- }
3736
+ event: {
3737
+ content: event.content,
3738
+ message_type: event.messageType,
3739
+ metadata: event.metadata
3740
+ }
3741
+ },
3742
+ mergeOptions(options, MESSAGE_SEND_MAX_RETRIES)
3795
3743
  );
3796
3744
  return normalizeToolOperationResult(response);
3797
3745
  }
@@ -4185,55 +4133,241 @@ var payloadSchemas = {
4185
4133
 
4186
4134
  // src/platform/streaming/PhoenixChannelsTransport.ts
4187
4135
  var import_phoenix = require("phoenix");
4136
+
4137
+ // src/platform/streaming/disconnectReason.ts
4138
+ var import_zod2 = require("zod");
4139
+ var UPGRADE_CODES = [
4140
+ "invalid_on_conflict",
4141
+ "connection_conflict",
4142
+ "too_many_requests",
4143
+ "tracking_failed"
4144
+ ];
4145
+ var UPGRADE_REASONS = {
4146
+ invalid_on_conflict: {
4147
+ status: 400,
4148
+ message: "Invalid websocket conflict policy.",
4149
+ retryable: false
4150
+ },
4151
+ connection_conflict: {
4152
+ status: 409,
4153
+ message: "Websocket connection conflict.",
4154
+ retryable: false
4155
+ },
4156
+ too_many_requests: {
4157
+ status: 429,
4158
+ message: "Too many websocket connection attempts.",
4159
+ retryable: true
4160
+ },
4161
+ tracking_failed: {
4162
+ status: 503,
4163
+ message: "Websocket connection tracking failed.",
4164
+ retryable: true
4165
+ }
4166
+ };
4167
+ var upgradeErrorSchema = import_zod2.z.object({
4168
+ status: import_zod2.z.number(),
4169
+ body: import_zod2.z.preprocess(
4170
+ (body) => {
4171
+ if (typeof body !== "string") {
4172
+ return body;
4173
+ }
4174
+ try {
4175
+ return JSON.parse(body);
4176
+ } catch {
4177
+ return null;
4178
+ }
4179
+ },
4180
+ import_zod2.z.object({
4181
+ error: import_zod2.z.object({
4182
+ code: import_zod2.z.enum(UPGRADE_CODES),
4183
+ message: import_zod2.z.string().optional(),
4184
+ retry_after: import_zod2.z.number().nullable().optional(),
4185
+ request_id: import_zod2.z.string().nullable().optional()
4186
+ })
4187
+ })
4188
+ ),
4189
+ headers: import_zod2.z.record(import_zod2.z.unknown()).optional()
4190
+ });
4191
+ var WebSocketDisconnectError = class extends Error {
4192
+ reason;
4193
+ constructor(reason) {
4194
+ super(reason.message);
4195
+ this.name = "WebSocketDisconnectError";
4196
+ this.reason = reason;
4197
+ }
4198
+ };
4199
+ function parseSupersedeDisconnectReason(payload) {
4200
+ if (typeof payload.reason !== "string" || typeof payload.message !== "string") {
4201
+ return null;
4202
+ }
4203
+ return {
4204
+ source: "agent_control",
4205
+ code: payload.reason,
4206
+ message: payload.message,
4207
+ retryable: false,
4208
+ retryAfter: typeof payload.retry_after === "number" ? payload.retry_after : null,
4209
+ targetSocketId: typeof payload.target_socket_id === "string" ? payload.target_socket_id : null,
4210
+ correlationId: typeof payload.correlation_id === "string" ? payload.correlation_id : null
4211
+ };
4212
+ }
4213
+ function parseUpgradeDisconnectReason(event) {
4214
+ const parsed = upgradeErrorSchema.safeParse(event);
4215
+ if (!parsed.success) {
4216
+ return null;
4217
+ }
4218
+ const { status, body, headers } = parsed.data;
4219
+ const {
4220
+ code,
4221
+ message,
4222
+ retry_after: retryAfter,
4223
+ request_id: requestId
4224
+ } = body.error;
4225
+ const reason = UPGRADE_REASONS[code];
4226
+ if (status !== reason.status) {
4227
+ return null;
4228
+ }
4229
+ return {
4230
+ source: "upgrade",
4231
+ status: reason.status,
4232
+ code,
4233
+ message: message ?? reason.message,
4234
+ retryable: reason.retryable,
4235
+ retryAfter: retryAfter ?? retryAfterFromHeaders(headers),
4236
+ requestId: requestId ?? null
4237
+ };
4238
+ }
4239
+ function genericCloseReason(event) {
4240
+ return {
4241
+ source: "websocket_close",
4242
+ code: "websocket.closed",
4243
+ message: "Phoenix socket closed without a platform disconnect reason.",
4244
+ retryable: true,
4245
+ closeCode: typeof event?.code === "number" ? event.code : null,
4246
+ closeReason: typeof event?.reason === "string" && event.reason.length > 0 ? event.reason : null
4247
+ };
4248
+ }
4249
+ function retryAfterFromHeaders(headers) {
4250
+ const value = headers?.["retry-after"] ?? headers?.["Retry-After"];
4251
+ if (typeof value === "number") {
4252
+ return value;
4253
+ }
4254
+ if (typeof value === "string" && value.trim() !== "") {
4255
+ const seconds = Number(value);
4256
+ return Number.isFinite(seconds) ? seconds : null;
4257
+ }
4258
+ return null;
4259
+ }
4260
+
4261
+ // src/platform/streaming/nodeWebSocketFactory.ts
4188
4262
  var import_ws = require("ws");
4263
+ function createNodeWebSocketFactory(headers) {
4264
+ const Constructor = import_ws.WebSocket;
4265
+ class BandNodeWebSocket {
4266
+ constructor(address, protocols) {
4267
+ const socket = new Constructor(address, protocols, headers ? { headers } : void 0);
4268
+ socket.once("unexpected-response", (request, response) => {
4269
+ void emitUpgradeError(socket, request, response);
4270
+ });
4271
+ return socket;
4272
+ }
4273
+ }
4274
+ return BandNodeWebSocket;
4275
+ }
4276
+ async function emitUpgradeError(websocket, request, response) {
4277
+ const body = await readResponseBody(response);
4278
+ const error = new Error(
4279
+ `Unexpected server response: ${response.statusCode ?? "unknown"}`
4280
+ );
4281
+ error.status = response.statusCode;
4282
+ error.body = body;
4283
+ error.headers = response.headers;
4284
+ request.destroy();
4285
+ websocket.emit("error", error);
4286
+ websocket.emit("close", 1006, Buffer.alloc(0));
4287
+ }
4288
+ async function readResponseBody(response) {
4289
+ const chunks = [];
4290
+ for await (const chunk of response) {
4291
+ chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
4292
+ }
4293
+ return Buffer.concat(chunks).toString("utf8");
4294
+ }
4295
+
4296
+ // src/platform/streaming/PhoenixChannelsTransport.ts
4189
4297
  var PhoenixChannelsTransport = class {
4190
4298
  socket;
4299
+ agentId;
4191
4300
  channels = /* @__PURE__ */ new Map();
4192
4301
  channelRefs = /* @__PURE__ */ new Map();
4193
4302
  pendingJoins = /* @__PURE__ */ new Map();
4194
4303
  logger;
4304
+ onTerminalDisconnect;
4195
4305
  onHandlerError;
4196
4306
  connected = false;
4197
4307
  connectPromise = null;
4198
4308
  connectResolve = null;
4309
+ connectReject = null;
4310
+ lastDisconnectReason = null;
4311
+ terminalDisconnectError = null;
4312
+ runForeverWaiters = /* @__PURE__ */ new Set();
4313
+ stoppingReconnect = false;
4314
+ suppressNextCloseReason = false;
4199
4315
  constructor(options) {
4200
4316
  this.logger = options.logger ?? new NoopLogger();
4317
+ this.agentId = options.agentId;
4318
+ this.onTerminalDisconnect = options.onTerminalDisconnect;
4201
4319
  let wsUrl = options.wsUrl;
4202
4320
  if (wsUrl.endsWith("/websocket")) {
4203
4321
  wsUrl = wsUrl.slice(0, -"/websocket".length);
4204
4322
  }
4323
+ const reconnectAfterMs = options.reconnectAfterMs ?? ((tries) => [1e3, 2e3, 5e3, 1e4, 3e4][tries - 1] ?? 3e4);
4205
4324
  this.socket = new import_phoenix.Socket(wsUrl, {
4206
4325
  params: {
4207
- api_key: options.apiKey,
4208
- agent_id: options.agentId
4326
+ agent_id: options.agentId,
4327
+ ...options.conflictPolicy ? { on_conflict: options.conflictPolicy } : {}
4209
4328
  },
4210
4329
  heartbeatIntervalMs: options.heartbeatIntervalMs,
4211
- reconnectAfterMs: options.reconnectAfterMs ?? ((tries) => [1e3, 2e3, 5e3, 1e4, 3e4][tries - 1] ?? 3e4),
4212
- transport: options.websocketFactory ?? resolveWebSocketFactory()
4330
+ reconnectAfterMs: (tries) => {
4331
+ if (this.terminalDisconnectError) {
4332
+ return Number.POSITIVE_INFINITY;
4333
+ }
4334
+ return reconnectAfterMs(tries);
4335
+ },
4336
+ transport: options.websocketFactory ?? resolveWebSocketFactory(options.apiKey)
4213
4337
  });
4214
4338
  this.socket.onOpen(() => {
4215
- this.connected = true;
4216
- this.connectResolve?.();
4217
- this.connectResolve = null;
4218
- this.logger.info("Phoenix socket opened", {
4219
- channels: getSocketChannelCount(this.socket)
4220
- });
4339
+ void this.handleOpen();
4221
4340
  });
4222
4341
  this.socket.onClose((event) => {
4223
- this.connected = false;
4224
- if (getSocketChannelCount(this.socket) === 0) {
4225
- this.socket.disconnect();
4226
- }
4227
- this.logger.info("Phoenix socket closed", {
4228
- code: event?.code ?? null,
4229
- reason: event?.reason ?? null
4230
- });
4342
+ this.recordSocketClose(event);
4231
4343
  });
4232
4344
  this.socket.onError((event) => {
4345
+ const errorEvent = unwrapErrorEvent(event);
4346
+ const upgradeReason = parseUpgradeDisconnectReason(errorEvent);
4347
+ if (upgradeReason) {
4348
+ if (upgradeReason.retryable) {
4349
+ this.lastDisconnectReason = upgradeReason;
4350
+ this.connectReject?.(new WebSocketDisconnectError(upgradeReason));
4351
+ this.stopReconnectIfNoChannels({ suppressCloseReason: true });
4352
+ } else {
4353
+ this.recordTerminalDisconnect(upgradeReason);
4354
+ }
4355
+ this.logger.warn("Phoenix socket upgrade failed", {
4356
+ reason: upgradeReason
4357
+ });
4358
+ return;
4359
+ }
4360
+ this.connectReject?.(
4361
+ new TransportError("Phoenix socket connection failed", errorEvent)
4362
+ );
4363
+ this.stopReconnectIfNoChannels({ suppressCloseReason: true });
4233
4364
  this.logger.warn("Phoenix socket error", { event });
4234
4365
  });
4235
4366
  }
4236
4367
  async connect() {
4368
+ if (this.terminalDisconnectError) {
4369
+ throw this.terminalDisconnectError;
4370
+ }
4237
4371
  if (this.connected) {
4238
4372
  return;
4239
4373
  }
@@ -4257,15 +4391,30 @@ var PhoenixChannelsTransport = class {
4257
4391
  await this.connectPromise;
4258
4392
  }
4259
4393
  async disconnect() {
4260
- for (const topic of this.channels.keys()) {
4261
- await this.leave(topic);
4262
- }
4394
+ const results = await Promise.allSettled(
4395
+ [...this.channels.keys()].map((topic) => this.leave(topic))
4396
+ );
4263
4397
  this.socket.disconnect();
4264
4398
  this.connected = false;
4399
+ const failures = [];
4400
+ for (const result of results) {
4401
+ if (result.status === "rejected") {
4402
+ failures.push(result.reason);
4403
+ }
4404
+ }
4405
+ if (failures.length > 0) {
4406
+ throw new AggregateError(
4407
+ failures,
4408
+ "Failed to leave one or more Phoenix topics during disconnect"
4409
+ );
4410
+ }
4265
4411
  }
4266
4412
  isConnected() {
4267
4413
  return this.connected;
4268
4414
  }
4415
+ getDisconnectReason() {
4416
+ return this.lastDisconnectReason;
4417
+ }
4269
4418
  async join(topic, handlers) {
4270
4419
  if (this.channels.has(topic)) {
4271
4420
  return;
@@ -4301,7 +4450,10 @@ var PhoenixChannelsTransport = class {
4301
4450
  channel.join().receive("ok", () => resolve()).receive(
4302
4451
  "error",
4303
4452
  (error) => reject(new TransportError(`Failed to join topic ${topic}`, error))
4304
- ).receive("timeout", () => reject(new TransportError(`Timeout joining topic ${topic}`)));
4453
+ ).receive(
4454
+ "timeout",
4455
+ () => reject(new TransportError(`Timeout joining topic ${topic}`))
4456
+ );
4305
4457
  });
4306
4458
  } catch (error) {
4307
4459
  for (const [event, ref] of refs) {
@@ -4329,19 +4481,127 @@ var PhoenixChannelsTransport = class {
4329
4481
  channel.leave().receive("ok", () => resolve()).receive(
4330
4482
  "error",
4331
4483
  (error) => reject(new TransportError(`Failed to leave topic ${topic}`, error))
4332
- ).receive("timeout", () => reject(new TransportError(`Timeout leaving topic ${topic}`)));
4484
+ ).receive(
4485
+ "timeout",
4486
+ () => reject(new TransportError(`Timeout leaving topic ${topic}`))
4487
+ );
4333
4488
  });
4334
4489
  this.channels.delete(topic);
4335
4490
  this.logger.debug("Left topic", { topic });
4336
4491
  }
4337
4492
  async runForever(signal) {
4493
+ if (this.terminalDisconnectError) {
4494
+ throw this.terminalDisconnectError;
4495
+ }
4338
4496
  if (signal.aborted) {
4339
4497
  return;
4340
4498
  }
4341
- await new Promise((resolve) => {
4342
- signal.addEventListener("abort", () => resolve(), { once: true });
4499
+ await new Promise((resolve, reject) => {
4500
+ let settled = false;
4501
+ const abortController = new AbortController();
4502
+ const waiter = {
4503
+ reject: (error) => {
4504
+ if (settled) {
4505
+ return;
4506
+ }
4507
+ settled = true;
4508
+ this.runForeverWaiters.delete(waiter);
4509
+ abortController.abort();
4510
+ reject(error);
4511
+ }
4512
+ };
4513
+ this.runForeverWaiters.add(waiter);
4514
+ signal.addEventListener(
4515
+ "abort",
4516
+ () => {
4517
+ if (settled) {
4518
+ return;
4519
+ }
4520
+ settled = true;
4521
+ this.runForeverWaiters.delete(waiter);
4522
+ resolve();
4523
+ },
4524
+ { once: true, signal: abortController.signal }
4525
+ );
4343
4526
  });
4344
4527
  }
4528
+ async handleOpen() {
4529
+ try {
4530
+ await this.subscribeAgentControl();
4531
+ } catch (error) {
4532
+ this.connected = false;
4533
+ this.socket.disconnect();
4534
+ this.connectReject?.(
4535
+ error instanceof Error ? error : new TransportError(String(error))
4536
+ );
4537
+ this.logger.warn("Failed to join mandatory agent_control channel", {
4538
+ error
4539
+ });
4540
+ return;
4541
+ }
4542
+ this.connected = true;
4543
+ this.connectResolve?.();
4544
+ this.connectResolve = null;
4545
+ this.connectReject = null;
4546
+ this.logger.info("Phoenix socket opened", {
4547
+ channels: getSocketChannelCount(this.socket)
4548
+ });
4549
+ }
4550
+ stopReconnectIfNoChannels(options = {}) {
4551
+ if (this.stoppingReconnect || getSocketChannelCount(this.socket) !== 0) {
4552
+ return;
4553
+ }
4554
+ this.suppressNextCloseReason = options.suppressCloseReason ?? false;
4555
+ this.stoppingReconnect = true;
4556
+ this.socket.disconnect();
4557
+ this.stoppingReconnect = false;
4558
+ }
4559
+ async subscribeAgentControl() {
4560
+ if (!this.agentId) {
4561
+ return;
4562
+ }
4563
+ await this.join(`agent_control:${this.agentId}`, {
4564
+ supersede: (payload) => {
4565
+ const reason = parseSupersedeDisconnectReason(payload);
4566
+ if (!reason) {
4567
+ this.logger.warn("Invalid agent_control supersede payload", {
4568
+ payload
4569
+ });
4570
+ return;
4571
+ }
4572
+ this.recordTerminalDisconnect(reason);
4573
+ }
4574
+ });
4575
+ }
4576
+ recordSocketClose(event) {
4577
+ this.connected = false;
4578
+ const suppressCloseReason = this.suppressNextCloseReason;
4579
+ this.suppressNextCloseReason = false;
4580
+ this.stopReconnectIfNoChannels();
4581
+ if (!suppressCloseReason && !this.terminalDisconnectError && !this.lastDisconnectReason) {
4582
+ this.lastDisconnectReason = genericCloseReason(event);
4583
+ }
4584
+ this.logger.info("Phoenix socket closed", {
4585
+ code: event?.code ?? null,
4586
+ reason: event?.reason ?? null,
4587
+ platformReason: this.lastDisconnectReason
4588
+ });
4589
+ }
4590
+ recordTerminalDisconnect(reason) {
4591
+ if (this.terminalDisconnectError) {
4592
+ return;
4593
+ }
4594
+ const error = new WebSocketDisconnectError(reason);
4595
+ this.lastDisconnectReason = reason;
4596
+ this.terminalDisconnectError = error;
4597
+ this.onTerminalDisconnect?.(reason);
4598
+ this.connectReject?.(error);
4599
+ for (const waiter of this.runForeverWaiters) {
4600
+ waiter.reject(error);
4601
+ }
4602
+ this.runForeverWaiters.clear();
4603
+ this.socket.disconnect();
4604
+ }
4345
4605
  async waitForConnection(timeoutMs = 1e4) {
4346
4606
  if (this.connected) {
4347
4607
  return;
@@ -4349,20 +4609,43 @@ var PhoenixChannelsTransport = class {
4349
4609
  await new Promise((resolve, reject) => {
4350
4610
  const timeout = setTimeout(() => {
4351
4611
  this.connectResolve = null;
4352
- reject(new TransportError("Timed out waiting for Phoenix socket connection"));
4612
+ this.connectReject = null;
4613
+ reject(
4614
+ new TransportError("Timed out waiting for Phoenix socket connection")
4615
+ );
4353
4616
  }, timeoutMs);
4354
4617
  this.connectResolve = () => {
4355
4618
  clearTimeout(timeout);
4619
+ this.connectReject = null;
4356
4620
  resolve();
4357
4621
  };
4622
+ this.connectReject = (error) => {
4623
+ clearTimeout(timeout);
4624
+ this.connectResolve = null;
4625
+ this.connectReject = null;
4626
+ reject(
4627
+ error instanceof Error ? error : new TransportError(String(error))
4628
+ );
4629
+ };
4358
4630
  });
4359
4631
  }
4360
4632
  };
4361
- function resolveWebSocketFactory() {
4633
+ function resolveWebSocketFactory(apiKey) {
4362
4634
  if (typeof process !== "undefined" && process.versions?.node) {
4363
- return import_ws.WebSocket;
4635
+ return createNodeWebSocketFactory({ "x-api-key": apiKey });
4636
+ }
4637
+ throw new TransportError(
4638
+ "Phoenix WebSocket API-key auth requires a WebSocket transport that can set handshake headers."
4639
+ );
4640
+ }
4641
+ function unwrapErrorEvent(event) {
4642
+ if (!isErrorEvent(event)) {
4643
+ return event;
4364
4644
  }
4365
- return WebSocket;
4645
+ return event.error;
4646
+ }
4647
+ function isErrorEvent(event) {
4648
+ return typeof event === "object" && event !== null && "error" in event;
4366
4649
  }
4367
4650
  function removeSocketChannel(socket, channel) {
4368
4651
  const candidate = socket;
@@ -4376,14 +4659,20 @@ function getSocketChannelCount(socket) {
4376
4659
  return candidate.channels.length;
4377
4660
  }
4378
4661
 
4379
- // src/platform/ThenvoiLink.ts
4380
- var import_rest_client = require("@thenvoi/rest-client");
4381
- var DEFAULT_WS_URL = "wss://app.thenvoi.com/api/v1/socket";
4662
+ // src/platform/BandLink.ts
4663
+ var import_rest_client = require("@band-ai/rest-client");
4664
+ var DEFAULT_WS_URL = "wss://app.band.ai/api/v1/socket";
4382
4665
  function deriveDefaultRestUrl(wsUrl) {
4383
4666
  const parsed = new URL(wsUrl);
4384
4667
  const protocol = parsed.protocol === "ws:" ? "http:" : "https:";
4385
4668
  return `${protocol}//${parsed.host}`;
4386
4669
  }
4670
+ function roomTopics(roomId) {
4671
+ return {
4672
+ chat: `chat_room:${roomId}`,
4673
+ participants: `room_participants:${roomId}`
4674
+ };
4675
+ }
4387
4676
  function toPlatformMessage(roomId, message) {
4388
4677
  return {
4389
4678
  id: message.id,
@@ -4397,7 +4686,7 @@ function toPlatformMessage(roomId, message) {
4397
4686
  createdAt: new Date(message.inserted_at)
4398
4687
  };
4399
4688
  }
4400
- var ThenvoiLink = class {
4689
+ var BandLink = class {
4401
4690
  agentId;
4402
4691
  apiKey;
4403
4692
  wsUrl;
@@ -4410,6 +4699,8 @@ var ThenvoiLink = class {
4410
4699
  eventQueue = [];
4411
4700
  waiters = [];
4412
4701
  connected = false;
4702
+ lastDisconnectReason = null;
4703
+ terminalDisconnectError = null;
4413
4704
  constructor(options) {
4414
4705
  this.agentId = options.agentId;
4415
4706
  this.apiKey = options.apiKey;
@@ -4421,7 +4712,7 @@ var ThenvoiLink = class {
4421
4712
  ...options.capabilities
4422
4713
  };
4423
4714
  const restApi = options.restApi ?? new FernRestAdapter(
4424
- new import_rest_client.ThenvoiClient({
4715
+ new import_rest_client.BandClient({
4425
4716
  apiKey: this.apiKey,
4426
4717
  baseUrl: this.restUrl
4427
4718
  })
@@ -4431,17 +4722,35 @@ var ThenvoiLink = class {
4431
4722
  wsUrl: this.wsUrl,
4432
4723
  apiKey: this.apiKey,
4433
4724
  agentId: this.agentId,
4434
- logger: this.logger
4725
+ logger: this.logger,
4726
+ conflictPolicy: options.conflictPolicy,
4727
+ onTerminalDisconnect: (reason) => {
4728
+ this.recordDisconnectReason(reason);
4729
+ }
4435
4730
  });
4436
4731
  }
4437
4732
  isConnected() {
4438
4733
  return this.connected;
4439
4734
  }
4735
+ getDisconnectReason() {
4736
+ return this.lastDisconnectReason;
4737
+ }
4440
4738
  async connect() {
4441
4739
  if (this.connected) {
4442
4740
  return;
4443
4741
  }
4444
- await this.transport.connect();
4742
+ try {
4743
+ await this.transport.connect();
4744
+ } catch (error) {
4745
+ if (error instanceof WebSocketDisconnectError) {
4746
+ if (error.reason.retryable) {
4747
+ this.lastDisconnectReason = error.reason;
4748
+ } else {
4749
+ this.recordDisconnectError(error);
4750
+ }
4751
+ }
4752
+ throw error;
4753
+ }
4445
4754
  this.connected = true;
4446
4755
  }
4447
4756
  async disconnect() {
@@ -4455,8 +4764,23 @@ var ThenvoiLink = class {
4455
4764
  this.connected = false;
4456
4765
  }
4457
4766
  async runForever(signal) {
4767
+ if (this.terminalDisconnectError) {
4768
+ throw this.terminalDisconnectError;
4769
+ }
4458
4770
  await this.transport.runForever(signal);
4459
4771
  }
4772
+ recordDisconnectReason(reason) {
4773
+ this.recordDisconnectError(new WebSocketDisconnectError(reason));
4774
+ }
4775
+ recordDisconnectError(error) {
4776
+ this.connected = false;
4777
+ this.lastDisconnectReason = error.reason;
4778
+ this.terminalDisconnectError = error;
4779
+ while (this.waiters.length > 0) {
4780
+ const waiter = this.waiters.shift();
4781
+ waiter?.reject(error);
4782
+ }
4783
+ }
4460
4784
  queueEvent(event) {
4461
4785
  const waiter = this.waiters.shift();
4462
4786
  if (waiter) {
@@ -4481,32 +4805,42 @@ var ThenvoiLink = class {
4481
4805
  if (this.subscribedRooms.has(roomId)) {
4482
4806
  return;
4483
4807
  }
4484
- await this.transport.join(`chat_room:${roomId}`, {
4485
- message_created: (payload) => {
4486
- this.emit("message_created", payload, roomId);
4487
- }
4488
- });
4489
- await this.transport.join(`room_participants:${roomId}`, {
4490
- participant_added: (payload) => {
4491
- this.emit("participant_added", payload, roomId);
4492
- },
4493
- participant_removed: (payload) => {
4494
- this.emit("participant_removed", payload, roomId);
4495
- },
4496
- room_deleted: (payload) => {
4497
- this.emit("room_deleted", payload, roomId);
4498
- }
4499
- });
4808
+ await this.joinRoomTopics(roomId);
4500
4809
  this.subscribedRooms.add(roomId);
4501
4810
  }
4502
4811
  async unsubscribeRoom(roomId) {
4503
4812
  if (!this.subscribedRooms.has(roomId)) {
4504
4813
  return;
4505
4814
  }
4506
- await this.transport.leave(`chat_room:${roomId}`);
4507
- await this.transport.leave(`room_participants:${roomId}`);
4815
+ const topics = roomTopics(roomId);
4816
+ await this.transport.leave(topics.chat);
4817
+ await this.transport.leave(topics.participants);
4508
4818
  this.subscribedRooms.delete(roomId);
4509
4819
  }
4820
+ async joinRoomTopics(roomId) {
4821
+ const topics = roomTopics(roomId);
4822
+ await this.transport.join(topics.chat, {
4823
+ message_created: (payload) => {
4824
+ this.emit("message_created", payload, roomId);
4825
+ }
4826
+ });
4827
+ try {
4828
+ await this.transport.join(topics.participants, {
4829
+ participant_added: (payload) => {
4830
+ this.emit("participant_added", payload, roomId);
4831
+ },
4832
+ participant_removed: (payload) => {
4833
+ this.emit("participant_removed", payload, roomId);
4834
+ },
4835
+ room_deleted: (payload) => {
4836
+ this.emit("room_deleted", payload, roomId);
4837
+ }
4838
+ });
4839
+ } catch (error) {
4840
+ await this.transport.leave(topics.chat);
4841
+ throw error;
4842
+ }
4843
+ }
4510
4844
  async subscribeAgentContacts() {
4511
4845
  assertCapability(this.capabilities, "contacts", "Contacts streaming");
4512
4846
  await this.transport.join(`agent_contacts:${this.agentId}`, {
@@ -4528,6 +4862,9 @@ var ThenvoiLink = class {
4528
4862
  await this.transport.leave(`agent_contacts:${this.agentId}`);
4529
4863
  }
4530
4864
  async nextEvent(signal) {
4865
+ if (this.terminalDisconnectError) {
4866
+ throw this.terminalDisconnectError;
4867
+ }
4531
4868
  if (signal?.aborted) {
4532
4869
  return null;
4533
4870
  }
@@ -4535,19 +4872,19 @@ var ThenvoiLink = class {
4535
4872
  if (queued) {
4536
4873
  return queued;
4537
4874
  }
4538
- return new Promise((resolve) => {
4875
+ return new Promise((resolve, reject) => {
4539
4876
  let settled = false;
4540
4877
  const onAbort = () => {
4541
4878
  const index = this.waiters.indexOf(waiter);
4542
4879
  if (index >= 0) {
4543
4880
  this.waiters.splice(index, 1);
4544
4881
  }
4545
- finalize(null);
4882
+ finalizeResolve(null);
4546
4883
  };
4547
4884
  const cleanup = () => {
4548
4885
  signal?.removeEventListener("abort", onAbort);
4549
4886
  };
4550
- const finalize = (event) => {
4887
+ const finalizeResolve = (event) => {
4551
4888
  if (settled) {
4552
4889
  return;
4553
4890
  }
@@ -4555,8 +4892,17 @@ var ThenvoiLink = class {
4555
4892
  cleanup();
4556
4893
  resolve(event);
4557
4894
  };
4895
+ const finalizeReject = (error) => {
4896
+ if (settled) {
4897
+ return;
4898
+ }
4899
+ settled = true;
4900
+ cleanup();
4901
+ reject(error);
4902
+ };
4558
4903
  const waiter = {
4559
- resolve: finalize,
4904
+ resolve: finalizeResolve,
4905
+ reject: finalizeReject,
4560
4906
  cleanup
4561
4907
  };
4562
4908
  this.waiters.push(waiter);
@@ -4568,7 +4914,10 @@ var ThenvoiLink = class {
4568
4914
  next: async () => {
4569
4915
  const value = await this.nextEvent();
4570
4916
  if (value === null) {
4571
- return { done: true, value: void 0 };
4917
+ return {
4918
+ done: true,
4919
+ value: void 0
4920
+ };
4572
4921
  }
4573
4922
  return { done: false, value };
4574
4923
  }
@@ -4650,7 +4999,9 @@ var ThenvoiLink = class {
4650
4999
  }
4651
5000
  async listChats(request, requestOptions) {
4652
5001
  if (!this.rest.listChats) {
4653
- throw new UnsupportedFeatureError("Chat listing is not available in current REST adapter");
5002
+ throw new UnsupportedFeatureError(
5003
+ "Chat listing is not available in current REST adapter"
5004
+ );
4654
5005
  }
4655
5006
  return this.rest.listChats(request, requestOptions);
4656
5007
  }
@@ -4734,6 +5085,8 @@ var DefaultPreprocessor = class {
4734
5085
  };
4735
5086
 
4736
5087
  // src/runtime/PlatformRuntime.ts
5088
+ var MAX_MESSAGE_RETRIES = 4294967295;
5089
+ var isValidRetryCount = (value) => Number.isInteger(value) && value >= 0 && value <= MAX_MESSAGE_RETRIES;
4737
5090
  var PlatformRuntime = class {
4738
5091
  _agentId;
4739
5092
  _apiKey;
@@ -4770,6 +5123,12 @@ var PlatformRuntime = class {
4770
5123
  "apiKey is required and must be a non-empty string. Use loadAgentConfig() to load credentials from agent_config.yaml."
4771
5124
  );
4772
5125
  }
5126
+ const maxMessageRetries = options.sessionConfig?.maxMessageRetries;
5127
+ if (maxMessageRetries !== void 0 && !isValidRetryCount(maxMessageRetries)) {
5128
+ throw new ValidationError(
5129
+ `sessionConfig.maxMessageRetries must be an integer between 0 and ${MAX_MESSAGE_RETRIES}, got ${maxMessageRetries}.`
5130
+ );
5131
+ }
4773
5132
  this._agentId = options.agentId;
4774
5133
  this._apiKey = options.apiKey;
4775
5134
  this._wsUrl = options.wsUrl;
@@ -4822,12 +5181,13 @@ var PlatformRuntime = class {
4822
5181
  }
4823
5182
  async doInitialize() {
4824
5183
  if (!this.linkInstance) {
4825
- this.linkInstance = new ThenvoiLink({
5184
+ this.linkInstance = new BandLink({
4826
5185
  ...this.linkOptions,
4827
5186
  agentId: this._agentId,
4828
5187
  apiKey: this._apiKey,
4829
5188
  wsUrl: this._wsUrl,
4830
- restUrl: this._restUrl
5189
+ restUrl: this._restUrl,
5190
+ logger: this.logger
4831
5191
  });
4832
5192
  }
4833
5193
  if (this.configuredIdentity) {
@@ -5104,26 +5464,28 @@ var RoomPresence = class {
5104
5464
  }
5105
5465
  };
5106
5466
 
5107
- // src/runtime/prompts.ts
5108
- var BASE_INSTRUCTIONS = `
5467
+ // src/runtime/index.ts
5468
+ var import_band_sdk_core3 = require("@band-ai/band-sdk-core");
5469
+
5470
+ // src/runtime/prompts/base.ts
5471
+ var ENVIRONMENT_SECTION = `
5109
5472
  ## Environment
5110
5473
 
5111
5474
  Multi-participant chat. Messages show sender: [Name]: content.
5112
5475
  Messages prefixed with [System]: are platform updates (participant changes, contact updates, etc.) \u2014 not messages from users.
5113
- Use \`thenvoi_send_message(content, mentions)\` to respond. Plain text output is not delivered.
5114
- Mentions use handles: @<username> for users, @<username>/<agent-name> for agents.
5115
-
5116
- ## CRITICAL: Delegate When You Cannot Help Directly
5476
+ Use \`band_send_message(content, mentions)\` to respond. Plain text output is not delivered.
5477
+ Mentions use handles: @<username> for users, @<username>/<agent-name> for agents.`;
5478
+ var MULTI_AGENT_RULES_SECTION = `## CRITICAL: Delegate When You Cannot Help Directly
5117
5479
 
5118
5480
  You have NO internet access and NO real-time data. When asked about weather, news, stock prices,
5119
5481
  or any current information you cannot answer directly:
5120
5482
 
5121
- 1. Call \`thenvoi_lookup_peers()\` to find available specialized agents
5122
- 2. If a relevant agent exists, call \`thenvoi_add_participant(name)\` to add them
5123
- 3. Ask that agent using \`thenvoi_send_message(question, mentions=[agent_handle])\`
5483
+ 1. Call \`band_lookup_peers()\` to find available specialized agents
5484
+ 2. If a relevant agent exists, call \`band_add_participant(name)\` to add them
5485
+ 3. Ask that agent using \`band_send_message(question, mentions=[agent_handle])\`
5124
5486
  4. Wait for their response and relay it back to the user
5125
5487
 
5126
- NEVER say "I can't do that" without first checking if another agent can help via \`thenvoi_lookup_peers()\`.
5488
+ NEVER say "I can't do that" without first checking if another agent can help via \`band_lookup_peers()\`.
5127
5489
 
5128
5490
  ## CRITICAL: Do NOT Remove Agents Automatically
5129
5491
 
@@ -5139,48 +5501,111 @@ Only remove agents if the user explicitly requests it.
5139
5501
  When someone asks you to get information from another agent:
5140
5502
  1. Ask the other agent for the information
5141
5503
  2. When you receive the response, IMMEDIATELY relay it back to the ORIGINAL REQUESTER
5142
- 3. Do NOT just thank the helper agent - the requester is waiting for their answer!
5504
+ 3. Do NOT just thank the helper agent - the requester is waiting for their answer!`;
5505
+ var THINKING_SECTION = `## IMPORTANT: Always Share Your Thinking
5143
5506
 
5144
- ## IMPORTANT: Always Share Your Thinking
5145
-
5146
- You MUST call \`thenvoi_send_event(content, message_type="thought")\` BEFORE every action.
5147
- This is required so users can see your reasoning process.
5148
-
5149
- ## Examples
5507
+ You MUST call \`band_send_event(content, message_type="thought")\` BEFORE every action.
5508
+ This is required so users can see your reasoning process.`;
5509
+ var EXAMPLES_SECTION = `## Examples
5150
5510
 
5151
5511
  ### Simple question - answer directly
5152
5512
  [John Doe]: What's 2+2?
5153
- -> thenvoi_send_event("Simple arithmetic, answering directly.", message_type="thought")
5154
- -> thenvoi_send_message("4", mentions=["@john"])
5513
+ -> band_send_event("Simple arithmetic, answering directly.", message_type="thought")
5514
+ -> band_send_message("4", mentions=["@john"])
5155
5515
 
5156
5516
  ### User asks about weather (you cannot answer directly)
5157
5517
  [John Doe]: What's the weather in Tokyo?
5158
- -> thenvoi_send_event("I can't check weather directly. Looking for a Weather Agent.", message_type="thought")
5159
- -> thenvoi_lookup_peers()
5160
- -> thenvoi_send_event("Found Weather Agent. Adding to room.", message_type="thought")
5161
- -> thenvoi_add_participant("Weather Agent")
5162
- -> thenvoi_send_message("What's the weather in Tokyo?", mentions=["@john/weather-agent"])
5518
+ -> band_send_event("I can't check weather directly. Looking for a Weather Agent.", message_type="thought")
5519
+ -> band_lookup_peers()
5520
+ -> band_send_event("Found Weather Agent. Adding to room.", message_type="thought")
5521
+ -> band_add_participant("Weather Agent")
5522
+ -> band_send_message("What's the weather in Tokyo?", mentions=["@john/weather-agent"])
5163
5523
 
5164
5524
  [Weather Agent]: Tokyo is 15\xB0C and cloudy.
5165
- -> thenvoi_send_event("Got weather response. Relaying back to John Doe.", message_type="thought")
5166
- -> thenvoi_send_message("The weather in Tokyo is 15\xB0C and cloudy.", mentions=["@john"])
5525
+ -> band_send_event("Got weather response. Relaying back to John Doe.", message_type="thought")
5526
+ -> band_send_message("The weather in Tokyo is 15\xB0C and cloudy.", mentions=["@john"])
5167
5527
 
5168
5528
  ### No suitable agent available
5169
5529
  [John Doe]: What's the stock price of AAPL?
5170
- -> thenvoi_send_event("I can't check stock prices. Looking for a Stock Agent.", message_type="thought")
5171
- -> thenvoi_lookup_peers()
5172
- -> thenvoi_send_event("No stock agent available. Must inform user.", message_type="thought")
5173
- -> thenvoi_send_message("I don't have access to stock prices, and there's no specialized agent available to help with that.", mentions=["@john"])
5530
+ -> band_send_event("I can't check stock prices. Looking for a Stock Agent.", message_type="thought")
5531
+ -> band_lookup_peers()
5532
+ -> band_send_event("No stock agent available. Must inform user.", message_type="thought")
5533
+ -> band_send_message("I don't have access to stock prices, and there's no specialized agent available to help with that.", mentions=["@john"])
5174
5534
 
5175
5535
  ### Follow-up question in same conversation
5176
5536
  [John Doe]: What about London?
5177
- -> thenvoi_send_event("Follow-up weather question. Asking Weather Agent.", message_type="thought")
5178
- -> thenvoi_send_message("What's the weather in London?", mentions=["@john/weather-agent"])
5537
+ -> band_send_event("Follow-up weather question. Asking Weather Agent.", message_type="thought")
5538
+ -> band_send_message("What's the weather in London?", mentions=["@john/weather-agent"])
5179
5539
 
5180
5540
  [Weather Agent]: London is 8\xB0C and rainy.
5181
- -> thenvoi_send_event("Got London weather. Relaying to John Doe.", message_type="thought")
5182
- -> thenvoi_send_message("London is 8\xB0C and rainy.", mentions=["@john"])
5183
- `;
5541
+ -> band_send_event("Got London weather. Relaying to John Doe.", message_type="thought")
5542
+ -> band_send_message("London is 8\xB0C and rainy.", mentions=["@john"])`;
5543
+ var BASE_INSTRUCTIONS = [
5544
+ ENVIRONMENT_SECTION,
5545
+ MULTI_AGENT_RULES_SECTION,
5546
+ THINKING_SECTION,
5547
+ EXAMPLES_SECTION
5548
+ ].join("\n\n") + "\n";
5549
+
5550
+ // src/runtime/prompts/memory.ts
5551
+ var MEMORY_INTRO = `## Memory Tools
5552
+
5553
+ You have access to memory tools for storing and retrieving information
5554
+ across conversations. Use \`band_store_memory\` to persist important
5555
+ information and \`band_list_memories\` / \`band_get_memory\` to recall it.
5556
+ Use \`band_supersede_memory\` to mark outdated memories and
5557
+ \`band_archive_memory\` to hide memories that should be preserved.`;
5558
+ function longTermPattern(label, type, segment, scope) {
5559
+ return {
5560
+ label,
5561
+ system: MEMORY_SYSTEM.long_term,
5562
+ type,
5563
+ segment,
5564
+ scope
5565
+ };
5566
+ }
5567
+ var COMMON_MEMORY_PATTERNS = [
5568
+ longTermPattern("Facts learned about a specific agent/entity", MEMORY_TYPE.semantic, MEMORY_SEGMENT.agent, MEMORY_STORE_SCOPE.subject),
5569
+ longTermPattern("Events involving a specific person/agent", MEMORY_TYPE.episodic, MEMORY_SEGMENT.agent, MEMORY_STORE_SCOPE.subject),
5570
+ longTermPattern("A user's preferences or profile info", MEMORY_TYPE.semantic, MEMORY_SEGMENT.user, MEMORY_STORE_SCOPE.subject),
5571
+ longTermPattern("Org-wide knowledge or how to perform a task", MEMORY_TYPE.procedural, MEMORY_SEGMENT.tool, MEMORY_STORE_SCOPE.organization)
5572
+ ];
5573
+ function renderMemoryPattern(pattern) {
5574
+ return `- ${pattern.label}: \`system="${pattern.system}"\`, \`type="${pattern.type}"\`, \`segment="${pattern.segment}"\`, \`scope="${pattern.scope}"\``;
5575
+ }
5576
+ var MEMORY_COMMON_PATTERNS = [
5577
+ "Common patterns:",
5578
+ ...COMMON_MEMORY_PATTERNS.map(renderMemoryPattern)
5579
+ ].join("\n");
5580
+ var MEMORY_SCOPE_GUIDANCE = `Prefer \`scope="${MEMORY_STORE_SCOPE.subject}"\` whenever the memory is about a specific person or agent, so it
5581
+ stays attached to that subject rather than leaking org-wide. Storing with \`scope="${MEMORY_STORE_SCOPE.subject}"\` requires a
5582
+ real \`subject_id\` UUID, so resolve it first via \`band_lookup_peers\` or the participant list.
5583
+ Reserve \`scope="${MEMORY_STORE_SCOPE.organization}"\` for knowledge that is genuinely shared across the whole organization and
5584
+ is not about any one subject.`;
5585
+ function quoteChoices(values) {
5586
+ return values.map((value) => `\`"${value}"\``).join(" | ");
5587
+ }
5588
+ function memoryTypeLines() {
5589
+ return Object.entries(MEMORY_SYSTEM_TYPES).map(([system, types]) => ` - ${system}: ${quoteChoices(types)}`).join("\n");
5590
+ }
5591
+ function buildMemorySection() {
5592
+ const storeMemoryProps = TOOL_MODELS.band_store_memory.properties;
5593
+ const systems = storeMemoryProps.system.enum;
5594
+ const segments = storeMemoryProps.segment.enum;
5595
+ const scopes = storeMemoryProps.scope.enum;
5596
+ const fieldRules = `When calling \`band_store_memory\`, the \`system\`, \`type\`, \`segment\`, and \`scope\` fields
5597
+ must use these exact values (case-sensitive):
5598
+
5599
+ - **system**: ${quoteChoices(systems)}
5600
+ - **type** (must match the chosen system):
5601
+ ${memoryTypeLines()}
5602
+ - **segment**: ${quoteChoices(segments)}
5603
+ - **scope**: ${quoteChoices(scopes)}`;
5604
+ return [MEMORY_INTRO, fieldRules, MEMORY_COMMON_PATTERNS, MEMORY_SCOPE_GUIDANCE].join("\n\n");
5605
+ }
5606
+ var MEMORY_SECTION = buildMemorySection();
5607
+
5608
+ // src/runtime/prompts/templates.ts
5184
5609
  var TEMPLATES = {
5185
5610
  default: `You are {agent_name}, {agent_description}.
5186
5611
 
@@ -5199,63 +5624,14 @@ ${customSection}`.trim();
5199
5624
  }
5200
5625
  const template = options?.template ?? "default";
5201
5626
  const templateString = TEMPLATES[template] ?? TEMPLATES.default;
5202
- return templateString.replaceAll("{agent_name}", agentName).replaceAll("{agent_description}", agentDescription).replaceAll("{custom_section}", customSection);
5203
- }
5204
-
5205
- // src/runtime/participantTracker.ts
5206
- var ParticipantTracker = class {
5207
- participantsList = [];
5208
- lastSent = null;
5209
- loaded = false;
5210
- get participants() {
5211
- return [...this.participantsList];
5212
- }
5213
- get isLoaded() {
5214
- return this.loaded;
5215
- }
5216
- setLoaded(participants) {
5217
- this.participantsList = participants.map((participant) => ({ ...participant }));
5218
- this.loaded = true;
5219
- }
5220
- add(participant) {
5221
- if (this.participantsList.some((entry) => entry.id === participant.id)) {
5222
- return false;
5223
- }
5224
- this.participantsList.push({
5225
- id: participant.id,
5226
- name: participant.name,
5227
- type: participant.type,
5228
- handle: participant.handle
5229
- });
5230
- return true;
5231
- }
5232
- remove(participantId) {
5233
- const before = this.participantsList.length;
5234
- this.participantsList = this.participantsList.filter(
5235
- (participant) => participant.id !== participantId
5236
- );
5237
- return this.participantsList.length < before;
5627
+ const parts = [
5628
+ templateString.replaceAll("{agent_name}", agentName).replaceAll("{agent_description}", agentDescription).replaceAll("{custom_section}", customSection)
5629
+ ];
5630
+ if (options?.capabilities?.memory) {
5631
+ parts.push(MEMORY_SECTION);
5238
5632
  }
5239
- changed() {
5240
- if (!this.lastSent) {
5241
- return true;
5242
- }
5243
- const oldIds = new Set(this.lastSent.map((participant) => participant.id));
5244
- const newIds = new Set(this.participantsList.map((participant) => participant.id));
5245
- if (oldIds.size !== newIds.size) {
5246
- return true;
5247
- }
5248
- for (const id of oldIds) {
5249
- if (!newIds.has(id)) {
5250
- return true;
5251
- }
5252
- }
5253
- return false;
5254
- }
5255
- markSent() {
5256
- this.lastSent = this.participantsList.map((participant) => ({ ...participant }));
5257
- }
5258
- };
5633
+ return parts.join("\n\n");
5634
+ }
5259
5635
 
5260
5636
  // src/runtime/shutdown.ts
5261
5637
  var DEFAULT_SHUTDOWN_TIMEOUT_MS = 3e4;
@@ -5335,11 +5711,13 @@ async function runWithGracefulShutdown(agent, options) {
5335
5711
  GracefulShutdown,
5336
5712
  HUB_ROOM_SYSTEM_PROMPT,
5337
5713
  HistoryProvider,
5714
+ MCP_SERVER_NAME,
5338
5715
  MCP_TOOL_PREFIX,
5716
+ MEMORY_SECTION,
5339
5717
  MEMORY_TOOL_NAMES,
5340
- MessageRetryTracker,
5341
- ParticipantTracker,
5718
+ ParticipantRoster,
5342
5719
  PlatformRuntime,
5720
+ RetryTracker,
5343
5721
  RoomPresence,
5344
5722
  SYNTHETIC_CONTACT_EVENTS_SENDER_ID,
5345
5723
  SYNTHETIC_CONTACT_EVENTS_SENDER_NAME,