@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/index.cjs CHANGED
@@ -31,35 +31,35 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
31
31
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
32
32
 
33
33
  // src/core/errors.ts
34
- var ThenvoiSdkError, UnsupportedFeatureError, ValidationError, TransportError, RuntimeStateError;
34
+ var BandSdkError, UnsupportedFeatureError, ValidationError, TransportError, RuntimeStateError;
35
35
  var init_errors = __esm({
36
36
  "src/core/errors.ts"() {
37
37
  "use strict";
38
- ThenvoiSdkError = class extends Error {
38
+ BandSdkError = class extends Error {
39
39
  constructor(message, cause) {
40
40
  super(message, cause !== void 0 ? { cause } : void 0);
41
- this.name = "ThenvoiSdkError";
41
+ this.name = "BandSdkError";
42
42
  }
43
43
  };
44
- UnsupportedFeatureError = class extends ThenvoiSdkError {
44
+ UnsupportedFeatureError = class extends BandSdkError {
45
45
  constructor(message) {
46
46
  super(message);
47
47
  this.name = "UnsupportedFeatureError";
48
48
  }
49
49
  };
50
- ValidationError = class extends ThenvoiSdkError {
50
+ ValidationError = class extends BandSdkError {
51
51
  constructor(message, cause) {
52
52
  super(message, cause);
53
53
  this.name = "ValidationError";
54
54
  }
55
55
  };
56
- TransportError = class extends ThenvoiSdkError {
56
+ TransportError = class extends BandSdkError {
57
57
  constructor(message, cause) {
58
58
  super(message, cause);
59
59
  this.name = "TransportError";
60
60
  }
61
61
  };
62
- RuntimeStateError = class extends ThenvoiSdkError {
62
+ RuntimeStateError = class extends BandSdkError {
63
63
  constructor(message) {
64
64
  super(message);
65
65
  this.name = "RuntimeStateError";
@@ -133,6 +133,114 @@ var init_chatEvents = __esm({
133
133
  }
134
134
  });
135
135
 
136
+ // src/contracts/memory.ts
137
+ function namedValues(values) {
138
+ return Object.fromEntries(values.map((value) => [value, value]));
139
+ }
140
+ function isMemorySystem(value) {
141
+ return MEMORY_SYSTEMS.includes(value);
142
+ }
143
+ function isMemoryType(value) {
144
+ return MEMORY_TYPES.includes(value);
145
+ }
146
+ function isMemoryTypeForSystem(system, type) {
147
+ return MEMORY_SYSTEM_TYPES[system].includes(type);
148
+ }
149
+ function isMemorySegment(value) {
150
+ return MEMORY_SEGMENTS.includes(value);
151
+ }
152
+ function isMemoryStoreScope(value) {
153
+ return MEMORY_STORE_SCOPES.includes(value);
154
+ }
155
+ function isMemoryListScope(value) {
156
+ return MEMORY_LIST_SCOPES.includes(value);
157
+ }
158
+ function isMemoryStatus(value) {
159
+ return MEMORY_STATUSES.includes(value);
160
+ }
161
+ function expectedList(values) {
162
+ return values.join(", ");
163
+ }
164
+ function expectedMemoryTypesForSystem(system) {
165
+ return expectedList(MEMORY_SYSTEM_TYPES[system]);
166
+ }
167
+ function memoryTypeForSystemError(system) {
168
+ return `type must be one of: ${expectedMemoryTypesForSystem(system)} for system "${system}"`;
169
+ }
170
+ var SENSORY_MEMORY_TYPES, COGNITIVE_MEMORY_TYPES, MEMORY_SYSTEM_TYPES, MEMORY_SYSTEMS, MEMORY_TYPES, MEMORY_SEGMENTS, MEMORY_STORE_SCOPES, MEMORY_LIST_SCOPES, MEMORY_STATUSES, MEMORY_SYSTEM, MEMORY_TYPE, MEMORY_SEGMENT, MEMORY_STORE_SCOPE;
171
+ var init_memory = __esm({
172
+ "src/contracts/memory.ts"() {
173
+ "use strict";
174
+ SENSORY_MEMORY_TYPES = [
175
+ "iconic",
176
+ // Visual input
177
+ "echoic",
178
+ // Auditory input
179
+ "haptic"
180
+ // Tactile input
181
+ ];
182
+ COGNITIVE_MEMORY_TYPES = [
183
+ "episodic",
184
+ // Events that occurred
185
+ "semantic",
186
+ // Facts, preferences, learned knowledge
187
+ "procedural"
188
+ // How to perform tasks
189
+ ];
190
+ MEMORY_SYSTEM_TYPES = {
191
+ sensory: SENSORY_MEMORY_TYPES,
192
+ // Brief sensory inputs
193
+ working: COGNITIVE_MEMORY_TYPES,
194
+ // Short-term session context
195
+ long_term: COGNITIVE_MEMORY_TYPES
196
+ // Persistent cross-conversation memory
197
+ };
198
+ MEMORY_SYSTEMS = Object.keys(MEMORY_SYSTEM_TYPES);
199
+ MEMORY_TYPES = [
200
+ ...SENSORY_MEMORY_TYPES,
201
+ ...COGNITIVE_MEMORY_TYPES
202
+ ];
203
+ MEMORY_SEGMENTS = [
204
+ "user",
205
+ // User preferences or profile info
206
+ "agent",
207
+ // Facts or events about agents/entities
208
+ "tool",
209
+ // Tool usage or task procedures
210
+ "guideline"
211
+ // Behavioral rules or policies
212
+ ];
213
+ MEMORY_STORE_SCOPES = [
214
+ "subject",
215
+ // About one person/agent; requires subject_id
216
+ "organization"
217
+ // Shared org-wide
218
+ ];
219
+ MEMORY_LIST_SCOPES = [
220
+ "subject",
221
+ // Subject-scoped memories only
222
+ "organization",
223
+ // Organization-scoped memories only
224
+ "all"
225
+ // Both scopes (no scope filter)
226
+ ];
227
+ MEMORY_STATUSES = [
228
+ "active",
229
+ // Normal, visible memories
230
+ "superseded",
231
+ // Outdated; soft-deleted via band_supersede_memory
232
+ "archived",
233
+ // Hidden but preserved via band_archive_memory
234
+ "all"
235
+ // Any status (no filter)
236
+ ];
237
+ MEMORY_SYSTEM = namedValues(MEMORY_SYSTEMS);
238
+ MEMORY_TYPE = namedValues(MEMORY_TYPES);
239
+ MEMORY_SEGMENT = namedValues(MEMORY_SEGMENTS);
240
+ MEMORY_STORE_SCOPE = namedValues(MEMORY_STORE_SCOPES);
241
+ }
242
+ });
243
+
136
244
  // src/runtime/tools/schemas.ts
137
245
  function mcpToolNames(names) {
138
246
  return [...names].sort((a, b) => a.localeCompare(b)).map((name) => `${MCP_TOOL_PREFIX}${name}`);
@@ -141,13 +249,14 @@ function getToolDescription(name) {
141
249
  const model = TOOL_MODELS[name];
142
250
  return model?.description ?? `Execute ${name}`;
143
251
  }
144
- var TOOL_MODELS, ALL_TOOL_NAMES, MEMORY_TOOL_NAMES, CONTACT_TOOL_NAMES, BASE_TOOL_NAMES, CHAT_TOOL_NAMES, MCP_TOOL_PREFIX;
252
+ var TOOL_MODELS, ALL_TOOL_NAMES, MEMORY_TOOL_NAMES, CONTACT_TOOL_NAMES, BASE_TOOL_NAMES, CHAT_TOOL_NAMES, MCP_TOOL_PREFIX, MCP_SERVER_NAME, SEND_MESSAGE_TOOL_NAME, SEND_EVENT_TOOL_NAME;
145
253
  var init_schemas = __esm({
146
254
  "src/runtime/tools/schemas.ts"() {
147
255
  "use strict";
148
256
  init_chatEvents();
257
+ init_memory();
149
258
  TOOL_MODELS = {
150
- thenvoi_send_message: {
259
+ band_send_message: {
151
260
  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.",
152
261
  properties: {
153
262
  content: {
@@ -163,7 +272,7 @@ var init_schemas = __esm({
163
272
  },
164
273
  required: ["content", "mentions"]
165
274
  },
166
- thenvoi_send_event: {
275
+ band_send_event: {
167
276
  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.",
168
277
  properties: {
169
278
  content: {
@@ -182,12 +291,12 @@ var init_schemas = __esm({
182
291
  },
183
292
  required: ["content", "message_type"]
184
293
  },
185
- thenvoi_add_participant: {
186
- 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'.",
294
+ band_add_participant: {
295
+ 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'.",
187
296
  properties: {
188
297
  name: {
189
298
  type: "string",
190
- description: "Name of participant to add (must match a name from thenvoi_lookup_peers)."
299
+ description: "Name of participant to add (must match a name from band_lookup_peers)."
191
300
  },
192
301
  role: {
193
302
  type: "string",
@@ -197,7 +306,7 @@ var init_schemas = __esm({
197
306
  },
198
307
  required: ["name"]
199
308
  },
200
- thenvoi_remove_participant: {
309
+ band_remove_participant: {
201
310
  description: "Remove a participant from the chat room by name.",
202
311
  properties: {
203
312
  name: {
@@ -207,12 +316,12 @@ var init_schemas = __esm({
207
316
  },
208
317
  required: ["name"]
209
318
  },
210
- thenvoi_get_participants: {
319
+ band_get_participants: {
211
320
  description: "Get a list of all participants in the current chat room.",
212
321
  properties: {},
213
322
  required: []
214
323
  },
215
- thenvoi_lookup_peers: {
324
+ band_lookup_peers: {
216
325
  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.",
217
326
  properties: {
218
327
  page: {
@@ -227,7 +336,7 @@ var init_schemas = __esm({
227
336
  },
228
337
  required: []
229
338
  },
230
- thenvoi_create_chatroom: {
339
+ band_create_chatroom: {
231
340
  description: "Create a new chat room for a specific task or conversation.",
232
341
  properties: {
233
342
  task_id: {
@@ -237,7 +346,7 @@ var init_schemas = __esm({
237
346
  },
238
347
  required: []
239
348
  },
240
- thenvoi_list_contacts: {
349
+ band_list_contacts: {
241
350
  description: "List agent's contacts with pagination.",
242
351
  properties: {
243
352
  page: {
@@ -254,7 +363,7 @@ var init_schemas = __esm({
254
363
  },
255
364
  required: []
256
365
  },
257
- thenvoi_add_contact: {
366
+ band_add_contact: {
258
367
  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.",
259
368
  properties: {
260
369
  handle: {
@@ -268,7 +377,7 @@ var init_schemas = __esm({
268
377
  },
269
378
  required: ["handle"]
270
379
  },
271
- thenvoi_remove_contact: {
380
+ band_remove_contact: {
272
381
  description: "Remove an existing contact by handle or ID.",
273
382
  properties: {
274
383
  handle: {
@@ -282,7 +391,7 @@ var init_schemas = __esm({
282
391
  },
283
392
  required: []
284
393
  },
285
- thenvoi_list_contact_requests: {
394
+ band_list_contact_requests: {
286
395
  description: "List both received and sent contact requests. Received requests are always filtered to pending status. Sent requests can be filtered by status.",
287
396
  properties: {
288
397
  page: {
@@ -304,7 +413,7 @@ var init_schemas = __esm({
304
413
  },
305
414
  required: []
306
415
  },
307
- thenvoi_respond_contact_request: {
416
+ band_respond_contact_request: {
308
417
  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).",
309
418
  properties: {
310
419
  action: {
@@ -323,7 +432,7 @@ var init_schemas = __esm({
323
432
  },
324
433
  required: ["action"]
325
434
  },
326
- thenvoi_list_memories: {
435
+ band_list_memories: {
327
436
  description: "List memories accessible to the agent. Returns memories about the specified subject (cross-agent sharing) and organization-wide shared memories.",
328
437
  properties: {
329
438
  subject_id: {
@@ -332,29 +441,22 @@ var init_schemas = __esm({
332
441
  },
333
442
  scope: {
334
443
  type: "string",
335
- enum: ["subject", "organization", "all"],
444
+ enum: [...MEMORY_LIST_SCOPES],
336
445
  description: "Filter by scope."
337
446
  },
338
447
  system: {
339
448
  type: "string",
340
- enum: ["sensory", "working", "long_term"],
449
+ enum: [...MEMORY_SYSTEMS],
341
450
  description: "Filter by memory system."
342
451
  },
343
452
  type: {
344
453
  type: "string",
345
- enum: [
346
- "iconic",
347
- "echoic",
348
- "haptic",
349
- "episodic",
350
- "semantic",
351
- "procedural"
352
- ],
454
+ enum: [...MEMORY_TYPES],
353
455
  description: "Filter by memory type."
354
456
  },
355
457
  segment: {
356
458
  type: "string",
357
- enum: ["user", "agent", "tool", "guideline"],
459
+ enum: [...MEMORY_SEGMENTS],
358
460
  description: "Filter by segment."
359
461
  },
360
462
  content_query: {
@@ -369,13 +471,13 @@ var init_schemas = __esm({
369
471
  },
370
472
  status: {
371
473
  type: "string",
372
- enum: ["active", "superseded", "archived", "all"],
474
+ enum: [...MEMORY_STATUSES],
373
475
  description: "Filter by status."
374
476
  }
375
477
  },
376
478
  required: []
377
479
  },
378
- thenvoi_store_memory: {
480
+ band_store_memory: {
379
481
  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.",
380
482
  properties: {
381
483
  content: {
@@ -384,24 +486,17 @@ var init_schemas = __esm({
384
486
  },
385
487
  system: {
386
488
  type: "string",
387
- enum: ["sensory", "working", "long_term"],
489
+ enum: [...MEMORY_SYSTEMS],
388
490
  description: "Memory system tier."
389
491
  },
390
492
  type: {
391
493
  type: "string",
392
- enum: [
393
- "iconic",
394
- "echoic",
395
- "haptic",
396
- "episodic",
397
- "semantic",
398
- "procedural"
399
- ],
494
+ enum: [...MEMORY_TYPES],
400
495
  description: "Memory type (must be valid for selected system)."
401
496
  },
402
497
  segment: {
403
498
  type: "string",
404
- enum: ["user", "agent", "tool", "guideline"],
499
+ enum: [...MEMORY_SEGMENTS],
405
500
  description: "Logical segment."
406
501
  },
407
502
  thought: {
@@ -410,7 +505,7 @@ var init_schemas = __esm({
410
505
  },
411
506
  scope: {
412
507
  type: "string",
413
- enum: ["subject", "organization"],
508
+ enum: [...MEMORY_STORE_SCOPES],
414
509
  description: "Visibility scope."
415
510
  },
416
511
  subject_id: {
@@ -424,7 +519,7 @@ var init_schemas = __esm({
424
519
  },
425
520
  required: ["content", "system", "type", "segment", "thought"]
426
521
  },
427
- thenvoi_get_memory: {
522
+ band_get_memory: {
428
523
  description: "Retrieve a specific memory by ID.",
429
524
  properties: {
430
525
  memory_id: {
@@ -434,7 +529,7 @@ var init_schemas = __esm({
434
529
  },
435
530
  required: ["memory_id"]
436
531
  },
437
- thenvoi_supersede_memory: {
532
+ band_supersede_memory: {
438
533
  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.",
439
534
  properties: {
440
535
  memory_id: {
@@ -444,7 +539,7 @@ var init_schemas = __esm({
444
539
  },
445
540
  required: ["memory_id"]
446
541
  },
447
- thenvoi_archive_memory: {
542
+ band_archive_memory: {
448
543
  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.",
449
544
  properties: {
450
545
  memory_id: {
@@ -457,18 +552,18 @@ var init_schemas = __esm({
457
552
  };
458
553
  ALL_TOOL_NAMES = new Set(Object.keys(TOOL_MODELS));
459
554
  MEMORY_TOOL_NAMES = /* @__PURE__ */ new Set([
460
- "thenvoi_list_memories",
461
- "thenvoi_store_memory",
462
- "thenvoi_get_memory",
463
- "thenvoi_supersede_memory",
464
- "thenvoi_archive_memory"
555
+ "band_list_memories",
556
+ "band_store_memory",
557
+ "band_get_memory",
558
+ "band_supersede_memory",
559
+ "band_archive_memory"
465
560
  ]);
466
561
  CONTACT_TOOL_NAMES = /* @__PURE__ */ new Set([
467
- "thenvoi_list_contacts",
468
- "thenvoi_add_contact",
469
- "thenvoi_remove_contact",
470
- "thenvoi_list_contact_requests",
471
- "thenvoi_respond_contact_request"
562
+ "band_list_contacts",
563
+ "band_add_contact",
564
+ "band_remove_contact",
565
+ "band_list_contact_requests",
566
+ "band_respond_contact_request"
472
567
  ]);
473
568
  BASE_TOOL_NAMES = new Set(
474
569
  [...ALL_TOOL_NAMES].filter((name) => !MEMORY_TOOL_NAMES.has(name))
@@ -476,7 +571,10 @@ var init_schemas = __esm({
476
571
  CHAT_TOOL_NAMES = new Set(
477
572
  [...BASE_TOOL_NAMES].filter((name) => !CONTACT_TOOL_NAMES.has(name))
478
573
  );
479
- MCP_TOOL_PREFIX = "mcp__thenvoi__";
574
+ MCP_TOOL_PREFIX = "mcp__band__";
575
+ MCP_SERVER_NAME = "band";
576
+ SEND_MESSAGE_TOOL_NAME = "band_send_message";
577
+ SEND_EVENT_TOOL_NAME = "band_send_event";
480
578
  }
481
579
  });
482
580
 
@@ -598,6 +696,13 @@ function serializeValue(value) {
598
696
  return String(value);
599
697
  }
600
698
  }
699
+ function resolveSingleRoomTools(getToolsForRoom) {
700
+ const tools = getToolsForRoom("");
701
+ if (!tools) {
702
+ throw new Error('Single-room mode requires getToolsForRoom("") to return a tools instance');
703
+ }
704
+ return tools;
705
+ }
601
706
  var init_registrations = __esm({
602
707
  "src/mcp/registrations.ts"() {
603
708
  "use strict";
@@ -607,42 +712,42 @@ var init_registrations = __esm({
607
712
  });
608
713
 
609
714
  // src/mcp/zod.ts
610
- function buildZodShape(z5, properties, required) {
715
+ function buildZodShape(z6, properties, required) {
611
716
  const shape = {};
612
717
  for (const [name, schema] of Object.entries(properties)) {
613
- const validator = jsonSchemaToZod(z5, schema);
718
+ const validator = jsonSchemaToZod(z6, schema);
614
719
  shape[name] = required.has(name) ? validator : validator.optional();
615
720
  }
616
721
  return shape;
617
722
  }
618
- function jsonSchemaToZod(z5, schema) {
723
+ function jsonSchemaToZod(z6, schema) {
619
724
  const type = schema.type;
620
725
  if (type === "string") {
621
726
  if (Array.isArray(schema.enum) && schema.enum.every((v) => typeof v === "string")) {
622
727
  const values = schema.enum;
623
728
  if (values.length > 0) {
624
- return z5.enum(values);
729
+ return z6.enum(values);
625
730
  }
626
731
  }
627
- return z5.string();
732
+ return z6.string();
628
733
  }
629
734
  if (type === "integer" || type === "number") {
630
- return z5.number();
735
+ return z6.number();
631
736
  }
632
737
  if (type === "boolean") {
633
- return z5.boolean();
738
+ return z6.boolean();
634
739
  }
635
740
  if (type === "array") {
636
741
  const itemSchema = schema.items;
637
742
  if (itemSchema && typeof itemSchema === "object") {
638
- return z5.array(jsonSchemaToZod(z5, itemSchema));
743
+ return z6.array(jsonSchemaToZod(z6, itemSchema));
639
744
  }
640
- return z5.array(z5.unknown());
745
+ return z6.array(z6.unknown());
641
746
  }
642
747
  if (type === "object") {
643
- return z5.record(z5.string(), z5.unknown());
748
+ return z6.record(z6.string(), z6.unknown());
644
749
  }
645
- return z5.unknown();
750
+ return z6.unknown();
646
751
  }
647
752
  var init_zod = __esm({
648
753
  "src/mcp/zod.ts"() {
@@ -653,32 +758,228 @@ var init_zod = __esm({
653
758
  // src/mcp/sdk.ts
654
759
  var sdk_exports = {};
655
760
  __export(sdk_exports, {
656
- createThenvoiSdkMcpServer: () => createThenvoiSdkMcpServer
761
+ createBandSdkMcpServer: () => createBandSdkMcpServer
657
762
  });
658
- function createThenvoiSdkMcpServer(options) {
659
- const registrations = buildRoomScopedRegistrations(
660
- options.getToolsForRoom,
661
- {
662
- enableMemoryTools: options.enableMemoryTools,
663
- enableContactTools: true,
664
- additionalTools: options.additionalTools
665
- }
666
- );
763
+ function createBandSdkMcpServer(options) {
764
+ const registrationOptions = {
765
+ enableMemoryTools: options.enableMemoryTools,
766
+ enableContactTools: true,
767
+ additionalTools: options.additionalTools
768
+ };
769
+ const registrations = options.multiRoom === false ? buildSingleContextRegistrations(resolveSingleRoomTools(options.getToolsForRoom), registrationOptions) : buildRoomScopedRegistrations(options.getToolsForRoom, registrationOptions);
667
770
  const toolDefinitions = registrations.map(toSdkToolDefinition);
668
771
  const toolNames = new Set(registrations.map((r) => r.name));
772
+ const contextCache = /* @__PURE__ */ new Map();
773
+ const MAX_CONTEXT_CACHE_ENTRIES = 100;
669
774
  const serverConfig = (0, import_claude_agent_sdk.createSdkMcpServer)({
670
- name: "thenvoi",
775
+ name: MCP_SERVER_NAME,
671
776
  tools: toolDefinitions
672
777
  });
673
778
  return {
674
779
  serverConfig,
675
780
  allowedTools: mcpToolNames(toolNames),
676
- toolDefinitions
781
+ toolDefinitions,
782
+ getSystemPromptContext: async (roomId, contextOptions) => {
783
+ const context = await getOrBuildSystemPromptContext(
784
+ roomId,
785
+ contextOptions,
786
+ options.getToolsForRoom,
787
+ contextCache,
788
+ MAX_CONTEXT_CACHE_ENTRIES
789
+ );
790
+ return context.markdown;
791
+ },
792
+ getSystemPromptContextData: (roomId, contextOptions) => {
793
+ return getOrBuildSystemPromptContext(
794
+ roomId,
795
+ contextOptions,
796
+ options.getToolsForRoom,
797
+ contextCache,
798
+ MAX_CONTEXT_CACHE_ENTRIES
799
+ );
800
+ }
801
+ };
802
+ }
803
+ async function getOrBuildSystemPromptContext(roomId, contextOptions, getToolsForRoom, contextCache, maxEntries) {
804
+ const ttlMs = contextOptions?.ttlMs ?? 3e4;
805
+ const now = Date.now();
806
+ const cached = contextCache.get(roomId);
807
+ if (cached && cached.expiresAt > now) {
808
+ cached.lastAccessedAt = now;
809
+ return cached.value;
810
+ }
811
+ if (cached) {
812
+ contextCache.delete(roomId);
813
+ }
814
+ const tools = getToolsForRoom(roomId);
815
+ if (!tools) {
816
+ return buildUnavailableSystemPromptContext(roomId);
817
+ }
818
+ const context = await buildSystemPromptContext(roomId, tools);
819
+ contextCache.set(roomId, {
820
+ value: context,
821
+ expiresAt: now + ttlMs,
822
+ lastAccessedAt: now
823
+ });
824
+ evictLeastRecentlyUsedContext(contextCache, maxEntries);
825
+ return context;
826
+ }
827
+ async function buildSystemPromptContext(roomId, tools) {
828
+ const participants = await tools.getParticipants();
829
+ const warnings = [];
830
+ const agentResolution = await resolveAgentIdentity(tools);
831
+ if (agentResolution.warning) {
832
+ warnings.push(agentResolution.warning);
833
+ }
834
+ const roomResolution = await resolveRoomTitle(tools, roomId);
835
+ if (roomResolution.warning) {
836
+ warnings.push(roomResolution.warning);
837
+ }
838
+ const agentIdentity = agentResolution.value;
839
+ const roomTitle = roomResolution.value;
840
+ const normalizedParticipants = participants.map((participant) => ({
841
+ id: String(participant.id),
842
+ name: String(participant.name ?? "Unknown"),
843
+ type: String(participant.type ?? "Unknown"),
844
+ handle: normalizeHandle2(participant.handle),
845
+ isSelf: participant.id === agentIdentity?.id
846
+ }));
847
+ const selfHandle = normalizeHandle2(agentIdentity?.handle);
848
+ const selfName = agentIdentity?.name ?? "Agent";
849
+ const mentionHandles = normalizedParticipants.filter((participant) => !participant.isSelf).map((participant) => participant.handle).filter((handle) => Boolean(handle));
850
+ const mentionFormat = mentionHandles.length > 0 ? mentionHandles.join(", ") : "No participant handles available";
851
+ const roomLabel = roomTitle ? `"${roomTitle}"` : "this room";
852
+ const agentLabel = selfHandle ? `${selfName} (${selfHandle})` : selfName;
853
+ const participantLines = normalizedParticipants.length > 0 ? normalizedParticipants.map((participant) => {
854
+ const suffix = participant.isSelf ? " (you)" : "";
855
+ const handle = participant.handle ? ` (${participant.handle})` : "";
856
+ return `- **${participant.name}**${handle} -- ${participant.type}${suffix}`;
857
+ }).join("\n") : "- No participants found";
858
+ const markdown = [
859
+ "## Room Context",
860
+ "",
861
+ `You are **${agentLabel}** in room ${roomLabel} (id: ${roomId}).`,
862
+ "",
863
+ "### Participants",
864
+ participantLines,
865
+ "",
866
+ "### Mention Format",
867
+ `To address someone, use their exact handle: ${mentionFormat}`,
868
+ ...warnings.length > 0 ? ["", "### Warnings", ...warnings.map((warning) => `- ${warning}`)] : []
869
+ ].join("\n");
870
+ return {
871
+ roomId,
872
+ roomTitle,
873
+ agent: {
874
+ id: agentIdentity?.id ?? "unknown-agent",
875
+ name: selfName,
876
+ handle: selfHandle,
877
+ description: agentIdentity?.description ?? null
878
+ },
879
+ participants: normalizedParticipants,
880
+ mentionFormat,
881
+ warnings,
882
+ markdown
677
883
  };
678
884
  }
885
+ async function resolveAgentIdentity(tools) {
886
+ const maybeTools = tools;
887
+ try {
888
+ if (maybeTools.getAgentIdentity) {
889
+ return { value: await maybeTools.getAgentIdentity(), warning: null };
890
+ }
891
+ if (maybeTools.rest?.getAgentMe) {
892
+ return { value: await maybeTools.rest.getAgentMe(), warning: null };
893
+ }
894
+ } catch (error) {
895
+ return {
896
+ value: null,
897
+ warning: `Unable to resolve agent identity: ${error instanceof Error ? error.message : String(error)}`
898
+ };
899
+ }
900
+ return { value: null, warning: null };
901
+ }
902
+ async function resolveRoomTitle(tools, roomId) {
903
+ const maybeTools = tools;
904
+ const rest = maybeTools.rest;
905
+ if (!rest?.listChats) {
906
+ return { value: null, warning: null };
907
+ }
908
+ try {
909
+ let page = 1;
910
+ const pageSize = 100;
911
+ while (true) {
912
+ const response = await rest.listChats({ page, pageSize });
913
+ const room = response?.data?.find((entry) => entry.id === roomId);
914
+ if (typeof room?.title === "string" && room.title.length > 0) {
915
+ return { value: room.title, warning: null };
916
+ }
917
+ const totalPages = response?.metadata?.totalPages ?? page;
918
+ if (page >= totalPages) {
919
+ return { value: null, warning: null };
920
+ }
921
+ page += 1;
922
+ }
923
+ } catch (error) {
924
+ return {
925
+ value: null,
926
+ warning: `Unable to resolve room title: ${error instanceof Error ? error.message : String(error)}`
927
+ };
928
+ }
929
+ }
930
+ function buildUnavailableSystemPromptContext(roomId) {
931
+ const warnings = [`No tool context found for room_id ${roomId}`];
932
+ const markdown = [
933
+ "## Room Context",
934
+ "",
935
+ `You are **Agent** in this room (id: ${roomId}).`,
936
+ "",
937
+ "### Participants",
938
+ "- No participants found",
939
+ "",
940
+ "### Mention Format",
941
+ "No participant handles available",
942
+ "",
943
+ "### Warnings",
944
+ ...warnings.map((warning) => `- ${warning}`)
945
+ ].join("\n");
946
+ return {
947
+ roomId,
948
+ roomTitle: null,
949
+ agent: {
950
+ id: "unknown-agent",
951
+ name: "Agent",
952
+ handle: null,
953
+ description: null
954
+ },
955
+ participants: [],
956
+ mentionFormat: "No participant handles available",
957
+ warnings,
958
+ markdown
959
+ };
960
+ }
961
+ function evictLeastRecentlyUsedContext(contextCache, maxEntries) {
962
+ if (contextCache.size <= maxEntries) {
963
+ return;
964
+ }
965
+ let oldestKey = null;
966
+ let oldestAccessedAt = Number.POSITIVE_INFINITY;
967
+ for (const [key, entry] of contextCache.entries()) {
968
+ if (entry.lastAccessedAt < oldestAccessedAt) {
969
+ oldestAccessedAt = entry.lastAccessedAt;
970
+ oldestKey = key;
971
+ }
972
+ }
973
+ if (oldestKey) {
974
+ contextCache.delete(oldestKey);
975
+ }
976
+ }
977
+ function normalizeHandle2(handle) {
978
+ return typeof handle === "string" && handle.trim().length > 0 ? handle.trim() : null;
979
+ }
679
980
  function toSdkToolDefinition(registration) {
680
981
  const shape = buildZodShape(
681
- import_zod6.z,
982
+ import_zod7.z,
682
983
  registration.inputSchema.properties,
683
984
  new Set(registration.inputSchema.required)
684
985
  );
@@ -689,12 +990,12 @@ function toSdkToolDefinition(registration) {
689
990
  async (args) => registration.execute(args)
690
991
  );
691
992
  }
692
- var import_claude_agent_sdk, import_zod6;
993
+ var import_claude_agent_sdk, import_zod7;
693
994
  var init_sdk = __esm({
694
995
  "src/mcp/sdk.ts"() {
695
996
  "use strict";
696
997
  import_claude_agent_sdk = require("@anthropic-ai/claude-agent-sdk");
697
- import_zod6 = require("zod");
998
+ import_zod7 = require("zod");
698
999
  init_schemas();
699
1000
  init_registrations();
700
1001
  init_zod();
@@ -709,6 +1010,7 @@ __export(src_exports, {
709
1010
  Agent: () => Agent,
710
1011
  AgentRuntime: () => AgentRuntime,
711
1012
  AnthropicAdapter: () => AnthropicAdapter,
1013
+ BandLink: () => BandLink,
712
1014
  CODEX_REASONING_EFFORTS: () => CODEX_REASONING_EFFORTS,
713
1015
  CODEX_REASONING_SUMMARIES: () => CODEX_REASONING_SUMMARIES,
714
1016
  CODEX_WEB_SEARCH_MODES: () => CODEX_WEB_SEARCH_MODES,
@@ -720,6 +1022,7 @@ __export(src_exports, {
720
1022
  GoogleADKAdapter: () => GoogleADKAdapter,
721
1023
  LangGraphAdapter: () => LangGraphAdapter,
722
1024
  LettaAdapter: () => LettaAdapter,
1025
+ MCP_SERVER_NAME: () => MCP_SERVER_NAME,
723
1026
  MCP_TOOL_PREFIX: () => MCP_TOOL_PREFIX,
724
1027
  OpenAIAdapter: () => OpenAIAdapter,
725
1028
  OpencodeAdapter: () => OpencodeAdapter,
@@ -727,8 +1030,9 @@ __export(src_exports, {
727
1030
  PlatformRuntime: () => PlatformRuntime,
728
1031
  SimpleAdapter: () => SimpleAdapter,
729
1032
  TOOL_MODELS: () => TOOL_MODELS,
730
- ThenvoiLink: () => ThenvoiLink,
731
1033
  VercelAISDKAdapter: () => VercelAISDKAdapter,
1034
+ WebSocketDisconnectError: () => WebSocketDisconnectError,
1035
+ deriveDefaultRestUrl: () => deriveDefaultRestUrl,
732
1036
  isDirectExecution: () => isDirectExecution,
733
1037
  loadAgentConfig: () => loadAgentConfig,
734
1038
  loadAgentConfigFromEnv: () => loadAgentConfigFromEnv
@@ -1024,56 +1328,19 @@ init_errors();
1024
1328
  var DEFAULT_REQUEST_OPTIONS = {
1025
1329
  maxRetries: 3
1026
1330
  };
1331
+ var MESSAGE_SEND_MAX_RETRIES = 2;
1027
1332
 
1028
1333
  // src/client/rest/FernRestAdapter.ts
1029
- function mergeOptions(options) {
1334
+ function mergeOptions(options, maxRetries) {
1030
1335
  return {
1031
1336
  ...DEFAULT_REQUEST_OPTIONS,
1337
+ ...maxRetries !== void 0 ? { maxRetries } : {},
1032
1338
  ...options
1033
1339
  };
1034
1340
  }
1035
- var AGENT_ME_RETRY_LIMIT = 4;
1036
- var AGENT_ME_RETRY_BASE_DELAY_MS = 2e3;
1037
- var MESSAGE_SEND_RETRY_LIMIT = 3;
1038
- var MESSAGE_SEND_RETRY_BASE_DELAY_MS = 500;
1039
1341
  function asMetadataMap2(value) {
1040
1342
  return asOptionalRecord(value);
1041
1343
  }
1042
- function extractHttpStatus(error) {
1043
- const record = asOptionalRecord(error);
1044
- if (!record) {
1045
- return void 0;
1046
- }
1047
- const response = asOptionalRecord(record.response);
1048
- const status = record.statusCode ?? record.status ?? response?.statusCode ?? response?.status;
1049
- return typeof status === "number" ? status : void 0;
1050
- }
1051
- function isFernRateLimitError(error) {
1052
- return extractHttpStatus(error) === 429;
1053
- }
1054
- async function sleep(ms) {
1055
- await new Promise((resolveDelay) => setTimeout(resolveDelay, ms));
1056
- }
1057
- function computeRetryDelayMs(baseDelayMs, attempt) {
1058
- const backoff = baseDelayMs * 2 ** (attempt - 1);
1059
- const jitter = Math.floor(Math.random() * Math.max(1, Math.floor(backoff / 4)));
1060
- return backoff + jitter;
1061
- }
1062
- async function withRateLimitRetry(operation, options) {
1063
- let lastError;
1064
- for (let attempt = 1; attempt <= options.retryLimit; attempt += 1) {
1065
- try {
1066
- return await operation();
1067
- } catch (error) {
1068
- lastError = error;
1069
- if (!isFernRateLimitError(error) || attempt === options.retryLimit) {
1070
- throw error;
1071
- }
1072
- await sleep(computeRetryDelayMs(options.baseDelayMs, attempt));
1073
- }
1074
- }
1075
- throw lastError instanceof Error ? lastError : new Error("Rate-limit retry exhausted without a terminal error.");
1076
- }
1077
1344
  function requireNonEmptyStringField(value, field, source) {
1078
1345
  if (typeof value !== "string" || value.trim().length === 0) {
1079
1346
  throw new Error(`Invalid ${source} response: expected non-empty string AgentIdentity.${field}`);
@@ -1281,28 +1548,21 @@ var FernRestAdapter = class {
1281
1548
  this.client = client;
1282
1549
  }
1283
1550
  async getAgentMe(options) {
1284
- return withRateLimitRetry(
1285
- async () => {
1286
- if (this.client.agentApiIdentity?.getAgentMe) {
1287
- const response = await this.client.agentApiIdentity.getAgentMe(mergeOptions(options));
1288
- return normalizeAgentIdentityEnvelope(response, "agentApiIdentity.getAgentMe");
1289
- }
1290
- const profileClient = this.client.myProfile ?? this.client.humanApiProfile;
1291
- if (!profileClient?.getMyProfile) {
1292
- throw new UnsupportedFeatureError(
1293
- "Fern client missing agentApiIdentity.getAgentMe or humanApiProfile.getMyProfile"
1294
- );
1295
- }
1296
- const profile = await profileClient.getMyProfile(mergeOptions(options));
1297
- return normalizeLegacyProfileIdentity(
1298
- normalizeLegacyProfileEnvelope(profile),
1299
- "profile.getMyProfile"
1300
- );
1301
- },
1302
- {
1303
- retryLimit: AGENT_ME_RETRY_LIMIT,
1304
- baseDelayMs: AGENT_ME_RETRY_BASE_DELAY_MS
1305
- }
1551
+ const requestOptions = mergeOptions(options);
1552
+ if (this.client.agentApiIdentity?.getAgentMe) {
1553
+ const response = await this.client.agentApiIdentity.getAgentMe(requestOptions);
1554
+ return normalizeAgentIdentityEnvelope(response, "agentApiIdentity.getAgentMe");
1555
+ }
1556
+ const profileClient = this.client.myProfile ?? this.client.humanApiProfile;
1557
+ if (!profileClient?.getMyProfile) {
1558
+ throw new UnsupportedFeatureError(
1559
+ "Fern client missing agentApiIdentity.getAgentMe or humanApiProfile.getMyProfile"
1560
+ );
1561
+ }
1562
+ const profile = await profileClient.getMyProfile(requestOptions);
1563
+ return normalizeLegacyProfileIdentity(
1564
+ normalizeLegacyProfileEnvelope(profile),
1565
+ "profile.getMyProfile"
1306
1566
  );
1307
1567
  }
1308
1568
  async createChatMessage(chatId, message, options) {
@@ -1310,45 +1570,33 @@ var FernRestAdapter = class {
1310
1570
  if (!api) {
1311
1571
  throw new UnsupportedFeatureError("Fern client missing chat message creation endpoint");
1312
1572
  }
1313
- const response = await withRateLimitRetry(
1314
- async () => await api(
1315
- chatId,
1316
- {
1317
- message: {
1318
- content: message.content,
1319
- message_type: message.messageType,
1320
- metadata: message.metadata,
1321
- mentions: message.mentions
1322
- }
1323
- },
1324
- mergeOptions(options)
1325
- ),
1573
+ const response = await api(
1574
+ chatId,
1326
1575
  {
1327
- retryLimit: MESSAGE_SEND_RETRY_LIMIT,
1328
- baseDelayMs: MESSAGE_SEND_RETRY_BASE_DELAY_MS
1329
- }
1576
+ message: {
1577
+ content: message.content,
1578
+ message_type: message.messageType,
1579
+ metadata: message.metadata,
1580
+ mentions: message.mentions
1581
+ }
1582
+ },
1583
+ mergeOptions(options, MESSAGE_SEND_MAX_RETRIES)
1330
1584
  );
1331
1585
  return normalizeToolOperationResult(response);
1332
1586
  }
1333
1587
  async createChatEvent(chatId, event, options) {
1334
1588
  const createAgentChatEvent = this.client.agentApiEvents?.createAgentChatEvent?.bind(this.client.agentApiEvents);
1335
1589
  if (createAgentChatEvent) {
1336
- const response = await withRateLimitRetry(
1337
- async () => await createAgentChatEvent(
1338
- chatId,
1339
- {
1340
- event: {
1341
- content: event.content,
1342
- message_type: event.messageType,
1343
- metadata: event.metadata
1344
- }
1345
- },
1346
- mergeOptions(options)
1347
- ),
1590
+ const response = await createAgentChatEvent(
1591
+ chatId,
1348
1592
  {
1349
- retryLimit: MESSAGE_SEND_RETRY_LIMIT,
1350
- baseDelayMs: MESSAGE_SEND_RETRY_BASE_DELAY_MS
1351
- }
1593
+ event: {
1594
+ content: event.content,
1595
+ message_type: event.messageType,
1596
+ metadata: event.metadata
1597
+ }
1598
+ },
1599
+ mergeOptions(options, MESSAGE_SEND_MAX_RETRIES)
1352
1600
  );
1353
1601
  return normalizeToolOperationResult(response);
1354
1602
  }
@@ -1644,7 +1892,7 @@ var FernRestAdapter = class {
1644
1892
  }
1645
1893
  };
1646
1894
 
1647
- // src/platform/ThenvoiLink.ts
1895
+ // src/platform/BandLink.ts
1648
1896
  init_errors();
1649
1897
 
1650
1898
  // src/contracts/capabilities.ts
@@ -1758,56 +2006,242 @@ var payloadSchemas = {
1758
2006
 
1759
2007
  // src/platform/streaming/PhoenixChannelsTransport.ts
1760
2008
  var import_phoenix = require("phoenix");
1761
- var import_ws = require("ws");
1762
2009
  init_errors();
2010
+
2011
+ // src/platform/streaming/disconnectReason.ts
2012
+ var import_zod2 = require("zod");
2013
+ var UPGRADE_CODES = [
2014
+ "invalid_on_conflict",
2015
+ "connection_conflict",
2016
+ "too_many_requests",
2017
+ "tracking_failed"
2018
+ ];
2019
+ var UPGRADE_REASONS = {
2020
+ invalid_on_conflict: {
2021
+ status: 400,
2022
+ message: "Invalid websocket conflict policy.",
2023
+ retryable: false
2024
+ },
2025
+ connection_conflict: {
2026
+ status: 409,
2027
+ message: "Websocket connection conflict.",
2028
+ retryable: false
2029
+ },
2030
+ too_many_requests: {
2031
+ status: 429,
2032
+ message: "Too many websocket connection attempts.",
2033
+ retryable: true
2034
+ },
2035
+ tracking_failed: {
2036
+ status: 503,
2037
+ message: "Websocket connection tracking failed.",
2038
+ retryable: true
2039
+ }
2040
+ };
2041
+ var upgradeErrorSchema = import_zod2.z.object({
2042
+ status: import_zod2.z.number(),
2043
+ body: import_zod2.z.preprocess(
2044
+ (body) => {
2045
+ if (typeof body !== "string") {
2046
+ return body;
2047
+ }
2048
+ try {
2049
+ return JSON.parse(body);
2050
+ } catch {
2051
+ return null;
2052
+ }
2053
+ },
2054
+ import_zod2.z.object({
2055
+ error: import_zod2.z.object({
2056
+ code: import_zod2.z.enum(UPGRADE_CODES),
2057
+ message: import_zod2.z.string().optional(),
2058
+ retry_after: import_zod2.z.number().nullable().optional(),
2059
+ request_id: import_zod2.z.string().nullable().optional()
2060
+ })
2061
+ })
2062
+ ),
2063
+ headers: import_zod2.z.record(import_zod2.z.unknown()).optional()
2064
+ });
2065
+ var WebSocketDisconnectError = class extends Error {
2066
+ reason;
2067
+ constructor(reason) {
2068
+ super(reason.message);
2069
+ this.name = "WebSocketDisconnectError";
2070
+ this.reason = reason;
2071
+ }
2072
+ };
2073
+ function parseSupersedeDisconnectReason(payload) {
2074
+ if (typeof payload.reason !== "string" || typeof payload.message !== "string") {
2075
+ return null;
2076
+ }
2077
+ return {
2078
+ source: "agent_control",
2079
+ code: payload.reason,
2080
+ message: payload.message,
2081
+ retryable: false,
2082
+ retryAfter: typeof payload.retry_after === "number" ? payload.retry_after : null,
2083
+ targetSocketId: typeof payload.target_socket_id === "string" ? payload.target_socket_id : null,
2084
+ correlationId: typeof payload.correlation_id === "string" ? payload.correlation_id : null
2085
+ };
2086
+ }
2087
+ function parseUpgradeDisconnectReason(event) {
2088
+ const parsed = upgradeErrorSchema.safeParse(event);
2089
+ if (!parsed.success) {
2090
+ return null;
2091
+ }
2092
+ const { status, body, headers } = parsed.data;
2093
+ const {
2094
+ code,
2095
+ message,
2096
+ retry_after: retryAfter,
2097
+ request_id: requestId
2098
+ } = body.error;
2099
+ const reason = UPGRADE_REASONS[code];
2100
+ if (status !== reason.status) {
2101
+ return null;
2102
+ }
2103
+ return {
2104
+ source: "upgrade",
2105
+ status: reason.status,
2106
+ code,
2107
+ message: message ?? reason.message,
2108
+ retryable: reason.retryable,
2109
+ retryAfter: retryAfter ?? retryAfterFromHeaders(headers),
2110
+ requestId: requestId ?? null
2111
+ };
2112
+ }
2113
+ function genericCloseReason(event) {
2114
+ return {
2115
+ source: "websocket_close",
2116
+ code: "websocket.closed",
2117
+ message: "Phoenix socket closed without a platform disconnect reason.",
2118
+ retryable: true,
2119
+ closeCode: typeof event?.code === "number" ? event.code : null,
2120
+ closeReason: typeof event?.reason === "string" && event.reason.length > 0 ? event.reason : null
2121
+ };
2122
+ }
2123
+ function retryAfterFromHeaders(headers) {
2124
+ const value = headers?.["retry-after"] ?? headers?.["Retry-After"];
2125
+ if (typeof value === "number") {
2126
+ return value;
2127
+ }
2128
+ if (typeof value === "string" && value.trim() !== "") {
2129
+ const seconds = Number(value);
2130
+ return Number.isFinite(seconds) ? seconds : null;
2131
+ }
2132
+ return null;
2133
+ }
2134
+
2135
+ // src/platform/streaming/nodeWebSocketFactory.ts
2136
+ var import_ws = require("ws");
2137
+ function createNodeWebSocketFactory(headers) {
2138
+ const Constructor = import_ws.WebSocket;
2139
+ class BandNodeWebSocket {
2140
+ constructor(address, protocols) {
2141
+ const socket = new Constructor(address, protocols, headers ? { headers } : void 0);
2142
+ socket.once("unexpected-response", (request, response) => {
2143
+ void emitUpgradeError(socket, request, response);
2144
+ });
2145
+ return socket;
2146
+ }
2147
+ }
2148
+ return BandNodeWebSocket;
2149
+ }
2150
+ async function emitUpgradeError(websocket, request, response) {
2151
+ const body = await readResponseBody(response);
2152
+ const error = new Error(
2153
+ `Unexpected server response: ${response.statusCode ?? "unknown"}`
2154
+ );
2155
+ error.status = response.statusCode;
2156
+ error.body = body;
2157
+ error.headers = response.headers;
2158
+ request.destroy();
2159
+ websocket.emit("error", error);
2160
+ websocket.emit("close", 1006, Buffer.alloc(0));
2161
+ }
2162
+ async function readResponseBody(response) {
2163
+ const chunks = [];
2164
+ for await (const chunk of response) {
2165
+ chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
2166
+ }
2167
+ return Buffer.concat(chunks).toString("utf8");
2168
+ }
2169
+
2170
+ // src/platform/streaming/PhoenixChannelsTransport.ts
1763
2171
  var PhoenixChannelsTransport = class {
1764
2172
  socket;
2173
+ agentId;
1765
2174
  channels = /* @__PURE__ */ new Map();
1766
2175
  channelRefs = /* @__PURE__ */ new Map();
1767
2176
  pendingJoins = /* @__PURE__ */ new Map();
1768
2177
  logger;
2178
+ onTerminalDisconnect;
1769
2179
  onHandlerError;
1770
2180
  connected = false;
1771
2181
  connectPromise = null;
1772
2182
  connectResolve = null;
2183
+ connectReject = null;
2184
+ lastDisconnectReason = null;
2185
+ terminalDisconnectError = null;
2186
+ runForeverWaiters = /* @__PURE__ */ new Set();
2187
+ stoppingReconnect = false;
2188
+ suppressNextCloseReason = false;
1773
2189
  constructor(options) {
1774
2190
  this.logger = options.logger ?? new NoopLogger();
2191
+ this.agentId = options.agentId;
2192
+ this.onTerminalDisconnect = options.onTerminalDisconnect;
1775
2193
  let wsUrl = options.wsUrl;
1776
2194
  if (wsUrl.endsWith("/websocket")) {
1777
2195
  wsUrl = wsUrl.slice(0, -"/websocket".length);
1778
2196
  }
2197
+ const reconnectAfterMs = options.reconnectAfterMs ?? ((tries) => [1e3, 2e3, 5e3, 1e4, 3e4][tries - 1] ?? 3e4);
1779
2198
  this.socket = new import_phoenix.Socket(wsUrl, {
1780
2199
  params: {
1781
- api_key: options.apiKey,
1782
- agent_id: options.agentId
2200
+ agent_id: options.agentId,
2201
+ ...options.conflictPolicy ? { on_conflict: options.conflictPolicy } : {}
1783
2202
  },
1784
2203
  heartbeatIntervalMs: options.heartbeatIntervalMs,
1785
- reconnectAfterMs: options.reconnectAfterMs ?? ((tries) => [1e3, 2e3, 5e3, 1e4, 3e4][tries - 1] ?? 3e4),
1786
- transport: options.websocketFactory ?? resolveWebSocketFactory()
2204
+ reconnectAfterMs: (tries) => {
2205
+ if (this.terminalDisconnectError) {
2206
+ return Number.POSITIVE_INFINITY;
2207
+ }
2208
+ return reconnectAfterMs(tries);
2209
+ },
2210
+ transport: options.websocketFactory ?? resolveWebSocketFactory(options.apiKey)
1787
2211
  });
1788
2212
  this.socket.onOpen(() => {
1789
- this.connected = true;
1790
- this.connectResolve?.();
1791
- this.connectResolve = null;
1792
- this.logger.info("Phoenix socket opened", {
1793
- channels: getSocketChannelCount(this.socket)
1794
- });
2213
+ void this.handleOpen();
1795
2214
  });
1796
2215
  this.socket.onClose((event) => {
1797
- this.connected = false;
1798
- if (getSocketChannelCount(this.socket) === 0) {
1799
- this.socket.disconnect();
1800
- }
1801
- this.logger.info("Phoenix socket closed", {
1802
- code: event?.code ?? null,
1803
- reason: event?.reason ?? null
1804
- });
2216
+ this.recordSocketClose(event);
1805
2217
  });
1806
2218
  this.socket.onError((event) => {
2219
+ const errorEvent = unwrapErrorEvent(event);
2220
+ const upgradeReason = parseUpgradeDisconnectReason(errorEvent);
2221
+ if (upgradeReason) {
2222
+ if (upgradeReason.retryable) {
2223
+ this.lastDisconnectReason = upgradeReason;
2224
+ this.connectReject?.(new WebSocketDisconnectError(upgradeReason));
2225
+ this.stopReconnectIfNoChannels({ suppressCloseReason: true });
2226
+ } else {
2227
+ this.recordTerminalDisconnect(upgradeReason);
2228
+ }
2229
+ this.logger.warn("Phoenix socket upgrade failed", {
2230
+ reason: upgradeReason
2231
+ });
2232
+ return;
2233
+ }
2234
+ this.connectReject?.(
2235
+ new TransportError("Phoenix socket connection failed", errorEvent)
2236
+ );
2237
+ this.stopReconnectIfNoChannels({ suppressCloseReason: true });
1807
2238
  this.logger.warn("Phoenix socket error", { event });
1808
2239
  });
1809
2240
  }
1810
2241
  async connect() {
2242
+ if (this.terminalDisconnectError) {
2243
+ throw this.terminalDisconnectError;
2244
+ }
1811
2245
  if (this.connected) {
1812
2246
  return;
1813
2247
  }
@@ -1831,15 +2265,30 @@ var PhoenixChannelsTransport = class {
1831
2265
  await this.connectPromise;
1832
2266
  }
1833
2267
  async disconnect() {
1834
- for (const topic of this.channels.keys()) {
1835
- await this.leave(topic);
1836
- }
2268
+ const results = await Promise.allSettled(
2269
+ [...this.channels.keys()].map((topic) => this.leave(topic))
2270
+ );
1837
2271
  this.socket.disconnect();
1838
2272
  this.connected = false;
2273
+ const failures = [];
2274
+ for (const result of results) {
2275
+ if (result.status === "rejected") {
2276
+ failures.push(result.reason);
2277
+ }
2278
+ }
2279
+ if (failures.length > 0) {
2280
+ throw new AggregateError(
2281
+ failures,
2282
+ "Failed to leave one or more Phoenix topics during disconnect"
2283
+ );
2284
+ }
1839
2285
  }
1840
2286
  isConnected() {
1841
2287
  return this.connected;
1842
2288
  }
2289
+ getDisconnectReason() {
2290
+ return this.lastDisconnectReason;
2291
+ }
1843
2292
  async join(topic, handlers) {
1844
2293
  if (this.channels.has(topic)) {
1845
2294
  return;
@@ -1875,7 +2324,10 @@ var PhoenixChannelsTransport = class {
1875
2324
  channel.join().receive("ok", () => resolve()).receive(
1876
2325
  "error",
1877
2326
  (error) => reject(new TransportError(`Failed to join topic ${topic}`, error))
1878
- ).receive("timeout", () => reject(new TransportError(`Timeout joining topic ${topic}`)));
2327
+ ).receive(
2328
+ "timeout",
2329
+ () => reject(new TransportError(`Timeout joining topic ${topic}`))
2330
+ );
1879
2331
  });
1880
2332
  } catch (error) {
1881
2333
  for (const [event, ref] of refs) {
@@ -1903,19 +2355,127 @@ var PhoenixChannelsTransport = class {
1903
2355
  channel.leave().receive("ok", () => resolve()).receive(
1904
2356
  "error",
1905
2357
  (error) => reject(new TransportError(`Failed to leave topic ${topic}`, error))
1906
- ).receive("timeout", () => reject(new TransportError(`Timeout leaving topic ${topic}`)));
2358
+ ).receive(
2359
+ "timeout",
2360
+ () => reject(new TransportError(`Timeout leaving topic ${topic}`))
2361
+ );
1907
2362
  });
1908
2363
  this.channels.delete(topic);
1909
2364
  this.logger.debug("Left topic", { topic });
1910
2365
  }
1911
2366
  async runForever(signal) {
2367
+ if (this.terminalDisconnectError) {
2368
+ throw this.terminalDisconnectError;
2369
+ }
1912
2370
  if (signal.aborted) {
1913
2371
  return;
1914
2372
  }
1915
- await new Promise((resolve) => {
1916
- signal.addEventListener("abort", () => resolve(), { once: true });
2373
+ await new Promise((resolve, reject) => {
2374
+ let settled = false;
2375
+ const abortController = new AbortController();
2376
+ const waiter = {
2377
+ reject: (error) => {
2378
+ if (settled) {
2379
+ return;
2380
+ }
2381
+ settled = true;
2382
+ this.runForeverWaiters.delete(waiter);
2383
+ abortController.abort();
2384
+ reject(error);
2385
+ }
2386
+ };
2387
+ this.runForeverWaiters.add(waiter);
2388
+ signal.addEventListener(
2389
+ "abort",
2390
+ () => {
2391
+ if (settled) {
2392
+ return;
2393
+ }
2394
+ settled = true;
2395
+ this.runForeverWaiters.delete(waiter);
2396
+ resolve();
2397
+ },
2398
+ { once: true, signal: abortController.signal }
2399
+ );
1917
2400
  });
1918
2401
  }
2402
+ async handleOpen() {
2403
+ try {
2404
+ await this.subscribeAgentControl();
2405
+ } catch (error) {
2406
+ this.connected = false;
2407
+ this.socket.disconnect();
2408
+ this.connectReject?.(
2409
+ error instanceof Error ? error : new TransportError(String(error))
2410
+ );
2411
+ this.logger.warn("Failed to join mandatory agent_control channel", {
2412
+ error
2413
+ });
2414
+ return;
2415
+ }
2416
+ this.connected = true;
2417
+ this.connectResolve?.();
2418
+ this.connectResolve = null;
2419
+ this.connectReject = null;
2420
+ this.logger.info("Phoenix socket opened", {
2421
+ channels: getSocketChannelCount(this.socket)
2422
+ });
2423
+ }
2424
+ stopReconnectIfNoChannels(options = {}) {
2425
+ if (this.stoppingReconnect || getSocketChannelCount(this.socket) !== 0) {
2426
+ return;
2427
+ }
2428
+ this.suppressNextCloseReason = options.suppressCloseReason ?? false;
2429
+ this.stoppingReconnect = true;
2430
+ this.socket.disconnect();
2431
+ this.stoppingReconnect = false;
2432
+ }
2433
+ async subscribeAgentControl() {
2434
+ if (!this.agentId) {
2435
+ return;
2436
+ }
2437
+ await this.join(`agent_control:${this.agentId}`, {
2438
+ supersede: (payload) => {
2439
+ const reason = parseSupersedeDisconnectReason(payload);
2440
+ if (!reason) {
2441
+ this.logger.warn("Invalid agent_control supersede payload", {
2442
+ payload
2443
+ });
2444
+ return;
2445
+ }
2446
+ this.recordTerminalDisconnect(reason);
2447
+ }
2448
+ });
2449
+ }
2450
+ recordSocketClose(event) {
2451
+ this.connected = false;
2452
+ const suppressCloseReason = this.suppressNextCloseReason;
2453
+ this.suppressNextCloseReason = false;
2454
+ this.stopReconnectIfNoChannels();
2455
+ if (!suppressCloseReason && !this.terminalDisconnectError && !this.lastDisconnectReason) {
2456
+ this.lastDisconnectReason = genericCloseReason(event);
2457
+ }
2458
+ this.logger.info("Phoenix socket closed", {
2459
+ code: event?.code ?? null,
2460
+ reason: event?.reason ?? null,
2461
+ platformReason: this.lastDisconnectReason
2462
+ });
2463
+ }
2464
+ recordTerminalDisconnect(reason) {
2465
+ if (this.terminalDisconnectError) {
2466
+ return;
2467
+ }
2468
+ const error = new WebSocketDisconnectError(reason);
2469
+ this.lastDisconnectReason = reason;
2470
+ this.terminalDisconnectError = error;
2471
+ this.onTerminalDisconnect?.(reason);
2472
+ this.connectReject?.(error);
2473
+ for (const waiter of this.runForeverWaiters) {
2474
+ waiter.reject(error);
2475
+ }
2476
+ this.runForeverWaiters.clear();
2477
+ this.socket.disconnect();
2478
+ }
1919
2479
  async waitForConnection(timeoutMs = 1e4) {
1920
2480
  if (this.connected) {
1921
2481
  return;
@@ -1923,20 +2483,43 @@ var PhoenixChannelsTransport = class {
1923
2483
  await new Promise((resolve, reject) => {
1924
2484
  const timeout = setTimeout(() => {
1925
2485
  this.connectResolve = null;
1926
- reject(new TransportError("Timed out waiting for Phoenix socket connection"));
2486
+ this.connectReject = null;
2487
+ reject(
2488
+ new TransportError("Timed out waiting for Phoenix socket connection")
2489
+ );
1927
2490
  }, timeoutMs);
1928
2491
  this.connectResolve = () => {
1929
2492
  clearTimeout(timeout);
2493
+ this.connectReject = null;
1930
2494
  resolve();
1931
2495
  };
2496
+ this.connectReject = (error) => {
2497
+ clearTimeout(timeout);
2498
+ this.connectResolve = null;
2499
+ this.connectReject = null;
2500
+ reject(
2501
+ error instanceof Error ? error : new TransportError(String(error))
2502
+ );
2503
+ };
1932
2504
  });
1933
2505
  }
1934
2506
  };
1935
- function resolveWebSocketFactory() {
2507
+ function resolveWebSocketFactory(apiKey) {
1936
2508
  if (typeof process !== "undefined" && process.versions?.node) {
1937
- return import_ws.WebSocket;
2509
+ return createNodeWebSocketFactory({ "x-api-key": apiKey });
1938
2510
  }
1939
- return WebSocket;
2511
+ throw new TransportError(
2512
+ "Phoenix WebSocket API-key auth requires a WebSocket transport that can set handshake headers."
2513
+ );
2514
+ }
2515
+ function unwrapErrorEvent(event) {
2516
+ if (!isErrorEvent(event)) {
2517
+ return event;
2518
+ }
2519
+ return event.error;
2520
+ }
2521
+ function isErrorEvent(event) {
2522
+ return typeof event === "object" && event !== null && "error" in event;
1940
2523
  }
1941
2524
  function removeSocketChannel(socket, channel) {
1942
2525
  const candidate = socket;
@@ -1950,15 +2533,21 @@ function getSocketChannelCount(socket) {
1950
2533
  return candidate.channels.length;
1951
2534
  }
1952
2535
 
1953
- // src/platform/ThenvoiLink.ts
2536
+ // src/platform/BandLink.ts
1954
2537
  init_protocols();
1955
- var import_rest_client = require("@thenvoi/rest-client");
1956
- var DEFAULT_WS_URL = "wss://app.thenvoi.com/api/v1/socket";
2538
+ var import_rest_client = require("@band-ai/rest-client");
2539
+ var DEFAULT_WS_URL = "wss://app.band.ai/api/v1/socket";
1957
2540
  function deriveDefaultRestUrl(wsUrl) {
1958
2541
  const parsed = new URL(wsUrl);
1959
2542
  const protocol = parsed.protocol === "ws:" ? "http:" : "https:";
1960
2543
  return `${protocol}//${parsed.host}`;
1961
2544
  }
2545
+ function roomTopics(roomId) {
2546
+ return {
2547
+ chat: `chat_room:${roomId}`,
2548
+ participants: `room_participants:${roomId}`
2549
+ };
2550
+ }
1962
2551
  function toPlatformMessage(roomId, message) {
1963
2552
  return {
1964
2553
  id: message.id,
@@ -1972,7 +2561,7 @@ function toPlatformMessage(roomId, message) {
1972
2561
  createdAt: new Date(message.inserted_at)
1973
2562
  };
1974
2563
  }
1975
- var ThenvoiLink = class {
2564
+ var BandLink = class {
1976
2565
  agentId;
1977
2566
  apiKey;
1978
2567
  wsUrl;
@@ -1985,6 +2574,8 @@ var ThenvoiLink = class {
1985
2574
  eventQueue = [];
1986
2575
  waiters = [];
1987
2576
  connected = false;
2577
+ lastDisconnectReason = null;
2578
+ terminalDisconnectError = null;
1988
2579
  constructor(options) {
1989
2580
  this.agentId = options.agentId;
1990
2581
  this.apiKey = options.apiKey;
@@ -1996,7 +2587,7 @@ var ThenvoiLink = class {
1996
2587
  ...options.capabilities
1997
2588
  };
1998
2589
  const restApi = options.restApi ?? new FernRestAdapter(
1999
- new import_rest_client.ThenvoiClient({
2590
+ new import_rest_client.BandClient({
2000
2591
  apiKey: this.apiKey,
2001
2592
  baseUrl: this.restUrl
2002
2593
  })
@@ -2006,17 +2597,35 @@ var ThenvoiLink = class {
2006
2597
  wsUrl: this.wsUrl,
2007
2598
  apiKey: this.apiKey,
2008
2599
  agentId: this.agentId,
2009
- logger: this.logger
2600
+ logger: this.logger,
2601
+ conflictPolicy: options.conflictPolicy,
2602
+ onTerminalDisconnect: (reason) => {
2603
+ this.recordDisconnectReason(reason);
2604
+ }
2010
2605
  });
2011
2606
  }
2012
2607
  isConnected() {
2013
2608
  return this.connected;
2014
2609
  }
2610
+ getDisconnectReason() {
2611
+ return this.lastDisconnectReason;
2612
+ }
2015
2613
  async connect() {
2016
2614
  if (this.connected) {
2017
2615
  return;
2018
2616
  }
2019
- await this.transport.connect();
2617
+ try {
2618
+ await this.transport.connect();
2619
+ } catch (error) {
2620
+ if (error instanceof WebSocketDisconnectError) {
2621
+ if (error.reason.retryable) {
2622
+ this.lastDisconnectReason = error.reason;
2623
+ } else {
2624
+ this.recordDisconnectError(error);
2625
+ }
2626
+ }
2627
+ throw error;
2628
+ }
2020
2629
  this.connected = true;
2021
2630
  }
2022
2631
  async disconnect() {
@@ -2030,8 +2639,23 @@ var ThenvoiLink = class {
2030
2639
  this.connected = false;
2031
2640
  }
2032
2641
  async runForever(signal) {
2642
+ if (this.terminalDisconnectError) {
2643
+ throw this.terminalDisconnectError;
2644
+ }
2033
2645
  await this.transport.runForever(signal);
2034
2646
  }
2647
+ recordDisconnectReason(reason) {
2648
+ this.recordDisconnectError(new WebSocketDisconnectError(reason));
2649
+ }
2650
+ recordDisconnectError(error) {
2651
+ this.connected = false;
2652
+ this.lastDisconnectReason = error.reason;
2653
+ this.terminalDisconnectError = error;
2654
+ while (this.waiters.length > 0) {
2655
+ const waiter = this.waiters.shift();
2656
+ waiter?.reject(error);
2657
+ }
2658
+ }
2035
2659
  queueEvent(event) {
2036
2660
  const waiter = this.waiters.shift();
2037
2661
  if (waiter) {
@@ -2056,32 +2680,42 @@ var ThenvoiLink = class {
2056
2680
  if (this.subscribedRooms.has(roomId)) {
2057
2681
  return;
2058
2682
  }
2059
- await this.transport.join(`chat_room:${roomId}`, {
2060
- message_created: (payload) => {
2061
- this.emit("message_created", payload, roomId);
2062
- }
2063
- });
2064
- await this.transport.join(`room_participants:${roomId}`, {
2065
- participant_added: (payload) => {
2066
- this.emit("participant_added", payload, roomId);
2067
- },
2068
- participant_removed: (payload) => {
2069
- this.emit("participant_removed", payload, roomId);
2070
- },
2071
- room_deleted: (payload) => {
2072
- this.emit("room_deleted", payload, roomId);
2073
- }
2074
- });
2683
+ await this.joinRoomTopics(roomId);
2075
2684
  this.subscribedRooms.add(roomId);
2076
2685
  }
2077
2686
  async unsubscribeRoom(roomId) {
2078
2687
  if (!this.subscribedRooms.has(roomId)) {
2079
2688
  return;
2080
2689
  }
2081
- await this.transport.leave(`chat_room:${roomId}`);
2082
- await this.transport.leave(`room_participants:${roomId}`);
2690
+ const topics = roomTopics(roomId);
2691
+ await this.transport.leave(topics.chat);
2692
+ await this.transport.leave(topics.participants);
2083
2693
  this.subscribedRooms.delete(roomId);
2084
2694
  }
2695
+ async joinRoomTopics(roomId) {
2696
+ const topics = roomTopics(roomId);
2697
+ await this.transport.join(topics.chat, {
2698
+ message_created: (payload) => {
2699
+ this.emit("message_created", payload, roomId);
2700
+ }
2701
+ });
2702
+ try {
2703
+ await this.transport.join(topics.participants, {
2704
+ participant_added: (payload) => {
2705
+ this.emit("participant_added", payload, roomId);
2706
+ },
2707
+ participant_removed: (payload) => {
2708
+ this.emit("participant_removed", payload, roomId);
2709
+ },
2710
+ room_deleted: (payload) => {
2711
+ this.emit("room_deleted", payload, roomId);
2712
+ }
2713
+ });
2714
+ } catch (error) {
2715
+ await this.transport.leave(topics.chat);
2716
+ throw error;
2717
+ }
2718
+ }
2085
2719
  async subscribeAgentContacts() {
2086
2720
  assertCapability(this.capabilities, "contacts", "Contacts streaming");
2087
2721
  await this.transport.join(`agent_contacts:${this.agentId}`, {
@@ -2103,6 +2737,9 @@ var ThenvoiLink = class {
2103
2737
  await this.transport.leave(`agent_contacts:${this.agentId}`);
2104
2738
  }
2105
2739
  async nextEvent(signal) {
2740
+ if (this.terminalDisconnectError) {
2741
+ throw this.terminalDisconnectError;
2742
+ }
2106
2743
  if (signal?.aborted) {
2107
2744
  return null;
2108
2745
  }
@@ -2110,19 +2747,19 @@ var ThenvoiLink = class {
2110
2747
  if (queued) {
2111
2748
  return queued;
2112
2749
  }
2113
- return new Promise((resolve) => {
2750
+ return new Promise((resolve, reject) => {
2114
2751
  let settled = false;
2115
2752
  const onAbort = () => {
2116
2753
  const index = this.waiters.indexOf(waiter);
2117
2754
  if (index >= 0) {
2118
2755
  this.waiters.splice(index, 1);
2119
2756
  }
2120
- finalize(null);
2757
+ finalizeResolve(null);
2121
2758
  };
2122
2759
  const cleanup = () => {
2123
2760
  signal?.removeEventListener("abort", onAbort);
2124
2761
  };
2125
- const finalize = (event) => {
2762
+ const finalizeResolve = (event) => {
2126
2763
  if (settled) {
2127
2764
  return;
2128
2765
  }
@@ -2130,8 +2767,17 @@ var ThenvoiLink = class {
2130
2767
  cleanup();
2131
2768
  resolve(event);
2132
2769
  };
2770
+ const finalizeReject = (error) => {
2771
+ if (settled) {
2772
+ return;
2773
+ }
2774
+ settled = true;
2775
+ cleanup();
2776
+ reject(error);
2777
+ };
2133
2778
  const waiter = {
2134
- resolve: finalize,
2779
+ resolve: finalizeResolve,
2780
+ reject: finalizeReject,
2135
2781
  cleanup
2136
2782
  };
2137
2783
  this.waiters.push(waiter);
@@ -2143,7 +2789,10 @@ var ThenvoiLink = class {
2143
2789
  next: async () => {
2144
2790
  const value = await this.nextEvent();
2145
2791
  if (value === null) {
2146
- return { done: true, value: void 0 };
2792
+ return {
2793
+ done: true,
2794
+ value: void 0
2795
+ };
2147
2796
  }
2148
2797
  return { done: false, value };
2149
2798
  }
@@ -2225,7 +2874,9 @@ var ThenvoiLink = class {
2225
2874
  }
2226
2875
  async listChats(request, requestOptions) {
2227
2876
  if (!this.rest.listChats) {
2228
- throw new UnsupportedFeatureError("Chat listing is not available in current REST adapter");
2877
+ throw new UnsupportedFeatureError(
2878
+ "Chat listing is not available in current REST adapter"
2879
+ );
2229
2880
  }
2230
2881
  return this.rest.listChats(request, requestOptions);
2231
2882
  }
@@ -2554,10 +3205,109 @@ var Execution = class {
2554
3205
  init_errors();
2555
3206
 
2556
3207
  // src/runtime/tools/AgentTools.ts
2557
- init_errors();
3208
+ init_errors();
3209
+ var import_band_sdk_core = require("@band-ai/band-sdk-core");
3210
+
3211
+ // src/runtime/types.ts
3212
+ var HistoryProvider = class {
3213
+ raw;
3214
+ constructor(raw) {
3215
+ this.raw = raw;
3216
+ }
3217
+ convert(converter) {
3218
+ return converter.convert(this.raw);
3219
+ }
3220
+ get length() {
3221
+ return this.raw.length;
3222
+ }
3223
+ };
3224
+ var SYNTHETIC_SENDER_TYPE = "System";
3225
+ var SYNTHETIC_CONTACT_EVENTS_SENDER_ID = "contact-events";
3226
+ var SYNTHETIC_CONTACT_EVENTS_SENDER_NAME = "Contact Events";
3227
+ function ensureHandlePrefix(handle) {
3228
+ if (!handle) {
3229
+ return null;
3230
+ }
3231
+ return handle.startsWith("@") ? handle : `@${handle}`;
3232
+ }
3233
+
3234
+ // src/runtime/formatters.ts
3235
+ function isMetadataMap(value) {
3236
+ return value !== null && typeof value === "object" && !Array.isArray(value);
3237
+ }
3238
+ function replaceUuidMentions(content, participants) {
3239
+ if (!content || participants.length === 0) {
3240
+ return content;
3241
+ }
3242
+ let next = content;
3243
+ for (const participant of participants) {
3244
+ const participantId = participant.id;
3245
+ const handle = participant.handle;
3246
+ if (typeof participantId === "string" && typeof handle === "string") {
3247
+ next = next.replaceAll(`@[[${participantId}]]`, ensureHandlePrefix(handle) ?? "");
3248
+ }
3249
+ }
3250
+ return next;
3251
+ }
3252
+ function formatMessageForLlm(message, participants) {
3253
+ const senderType = String(message.sender_type ?? "");
3254
+ const senderName = String(message.sender_name ?? message.name ?? senderType);
3255
+ const content = participants ? replaceUuidMentions(String(message.content ?? ""), participants) : String(message.content ?? "");
3256
+ return {
3257
+ role: senderType === "Agent" ? "assistant" : "user",
3258
+ content,
3259
+ sender_name: senderName,
3260
+ sender_type: senderType,
3261
+ message_type: String(message.message_type ?? "text"),
3262
+ metadata: isMetadataMap(message.metadata) ? message.metadata : {}
3263
+ };
3264
+ }
3265
+ function formatHistoryForLlm(messages, options) {
3266
+ const excludeId = options?.excludeId;
3267
+ const participants = options?.participants;
3268
+ return messages.filter((message) => String(message.id ?? "") !== excludeId).map((message) => formatMessageForLlm(message, participants));
3269
+ }
3270
+ function toParticipantRecord(fields) {
3271
+ return {
3272
+ id: fields.id,
3273
+ name: fields.name ?? "unknown",
3274
+ type: fields.type ?? "unknown",
3275
+ handle: fields.handle ?? null
3276
+ };
3277
+ }
3278
+ function toParticipantRecordFromRest(participant) {
3279
+ return {
3280
+ id: participant.id,
3281
+ name: participant.name,
3282
+ type: participant.type,
3283
+ handle: participant.handle ?? null
3284
+ };
3285
+ }
3286
+ function buildParticipantsMessage(participants) {
3287
+ if (participants.length === 0) {
3288
+ return "## Current Participants\nNo other participants in this room.";
3289
+ }
3290
+ const lines = ["## Current Participants"];
3291
+ for (const participant of participants) {
3292
+ const participantType = String(participant.type ?? "Unknown");
3293
+ const participantName = String(participant.name ?? "Unknown");
3294
+ const participantHandle = String(participant.handle ?? "Unknown");
3295
+ lines.push(
3296
+ `- ${ensureHandlePrefix(participantHandle) ?? ""} \u2014 ${participantName} (${participantType})`
3297
+ );
3298
+ }
3299
+ lines.push("");
3300
+ lines.push(
3301
+ "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."
3302
+ );
3303
+ return lines.join("\n");
3304
+ }
3305
+
3306
+ // src/runtime/tools/AgentTools.ts
2558
3307
  init_chatEvents();
2559
3308
  init_protocols();
2560
3309
  init_schemas();
3310
+ init_memory();
2561
3311
  var REQUIRED_ADAPTER_TOOL_METHODS = [
2562
3312
  "sendMessage",
2563
3313
  "sendEvent",
@@ -2592,51 +3342,17 @@ var CONTACT_REQUEST_ACTIONS = /* @__PURE__ */ new Set([
2592
3342
  "reject",
2593
3343
  "cancel"
2594
3344
  ]);
2595
- var MEMORY_SYSTEMS = /* @__PURE__ */ new Set([
2596
- "sensory",
2597
- "working",
2598
- "long_term"
2599
- ]);
2600
- var MEMORY_TYPES = /* @__PURE__ */ new Set([
2601
- "iconic",
2602
- "echoic",
2603
- "haptic",
2604
- "episodic",
2605
- "semantic",
2606
- "procedural"
2607
- ]);
2608
- var MEMORY_SEGMENTS = /* @__PURE__ */ new Set([
2609
- "user",
2610
- "agent",
2611
- "tool",
2612
- "guideline"
2613
- ]);
2614
- var LIST_MEMORY_SCOPES = /* @__PURE__ */ new Set([
2615
- "subject",
2616
- "organization",
2617
- "all"
2618
- ]);
2619
- var LIST_MEMORY_STATUSES = /* @__PURE__ */ new Set([
2620
- "active",
2621
- "superseded",
2622
- "archived",
2623
- "all"
2624
- ]);
2625
- var MEMORY_SCOPES = /* @__PURE__ */ new Set([
2626
- "subject",
2627
- "organization"
2628
- ]);
2629
3345
  var AgentTools = class {
2630
3346
  roomId;
2631
3347
  capabilities;
2632
3348
  rest;
2633
- participants;
3349
+ roster;
2634
3350
  adapterTools;
2635
3351
  toolHandlers;
2636
3352
  constructor(options) {
2637
3353
  this.roomId = options.roomId;
2638
3354
  this.rest = options.rest;
2639
- this.participants = options.participants ?? [];
3355
+ this.roster = options.roster ?? new import_band_sdk_core.ParticipantRoster();
2640
3356
  this.capabilities = {
2641
3357
  ...DEFAULT_AGENT_TOOLS_CAPABILITIES,
2642
3358
  ...options.capabilities
@@ -2648,17 +3364,20 @@ var AgentTools = class {
2648
3364
  return this.adapterTools;
2649
3365
  }
2650
3366
  async sendMessage(content, mentions = []) {
2651
- if (mentions.length > 0 && typeof mentions[0] === "string" && this.participants.length === 0) {
2652
- await this.syncParticipants();
3367
+ let participants;
3368
+ if (mentions.length > 0 && typeof mentions[0] === "string") {
3369
+ participants = this.roster.list();
3370
+ if (participants.length === 0) {
3371
+ participants = await this.syncParticipants();
3372
+ }
2653
3373
  }
2654
- const resolvedMentions = this.resolveMentions(mentions);
3374
+ const resolvedMentions = this.resolveMentions(mentions, participants);
2655
3375
  return this.rest.createChatMessage(
2656
3376
  this.roomId,
2657
3377
  {
2658
3378
  content,
2659
3379
  mentions: resolvedMentions
2660
- },
2661
- DEFAULT_REQUEST_OPTIONS
3380
+ }
2662
3381
  );
2663
3382
  }
2664
3383
  async sendEvent(content, messageType, metadata) {
@@ -2669,8 +3388,7 @@ var AgentTools = class {
2669
3388
  content,
2670
3389
  messageType,
2671
3390
  metadata
2672
- },
2673
- DEFAULT_REQUEST_OPTIONS
3391
+ }
2674
3392
  );
2675
3393
  }
2676
3394
  async createChatroom(taskId) {
@@ -2709,7 +3427,7 @@ var AgentTools = class {
2709
3427
  type: String(peer.type ?? "Agent"),
2710
3428
  handle: typeof peer.handle === "string" ? peer.handle : null
2711
3429
  };
2712
- this.participants.push(participantRecord);
3430
+ this.roster.add(participantRecord);
2713
3431
  return {
2714
3432
  ...participantRecord,
2715
3433
  status: "added"
@@ -2724,7 +3442,7 @@ var AgentTools = class {
2724
3442
  throw new ValidationError(`Participant '${name}' not found in room`);
2725
3443
  }
2726
3444
  await this.rest.removeChatParticipant(this.roomId, String(participant.id), DEFAULT_REQUEST_OPTIONS);
2727
- this.replaceParticipants(this.participants.filter((entry) => entry.id !== participant.id));
3445
+ this.roster.remove(participant.id);
2728
3446
  return {
2729
3447
  id: participant.id,
2730
3448
  name,
@@ -2752,17 +3470,12 @@ var AgentTools = class {
2752
3470
  }
2753
3471
  async fetchParticipants() {
2754
3472
  const participants = await this.rest.listChatParticipants(this.roomId, DEFAULT_REQUEST_OPTIONS);
2755
- return participants.map((participant) => ({
2756
- id: participant.id,
2757
- name: participant.name,
2758
- type: participant.type,
2759
- handle: participant.handle ?? null
2760
- }));
3473
+ return participants.map(toParticipantRecordFromRest);
2761
3474
  }
2762
3475
  async syncParticipants() {
2763
3476
  const participants = await this.fetchParticipants();
2764
- this.replaceParticipants(participants);
2765
- return [...this.participants];
3477
+ this.roster.setAll(participants);
3478
+ return this.roster.list().map(toParticipantRecord);
2766
3479
  }
2767
3480
  async executeToolCall(toolName, arguments_) {
2768
3481
  const validationError = validateToolArgs(toolName, arguments_);
@@ -2810,7 +3523,7 @@ var AgentTools = class {
2810
3523
  if (!CHAT_TOOL_NAMES.has(name) && !this.capabilities.contacts) {
2811
3524
  return false;
2812
3525
  }
2813
- if (name === "thenvoi_lookup_peers" && !this.capabilities.peers) {
3526
+ if (name === "band_lookup_peers" && !this.capabilities.peers) {
2814
3527
  return false;
2815
3528
  }
2816
3529
  return true;
@@ -3005,7 +3718,7 @@ var AgentTools = class {
3005
3718
  }
3006
3719
  return this.rest.archiveMemory(normalizedMemoryId, DEFAULT_REQUEST_OPTIONS);
3007
3720
  }
3008
- resolveMentions(mentions) {
3721
+ resolveMentions(mentions, participants) {
3009
3722
  if (mentions.length === 0) {
3010
3723
  return [];
3011
3724
  }
@@ -3018,7 +3731,7 @@ var AgentTools = class {
3018
3731
  const participantsByHandle = /* @__PURE__ */ new Map();
3019
3732
  const participantsById = /* @__PURE__ */ new Map();
3020
3733
  const participantsByName = /* @__PURE__ */ new Map();
3021
- for (const participant of this.participants) {
3734
+ for (const participant of participants ?? []) {
3022
3735
  const ref = {
3023
3736
  id: String(participant.id),
3024
3737
  handle: typeof participant.handle === "string" ? participant.handle : void 0
@@ -3074,9 +3787,6 @@ var AgentTools = class {
3074
3787
  normalizeMentionHandle(handle) {
3075
3788
  return handle.trim().replace(/^@+/, "").toLowerCase();
3076
3789
  }
3077
- replaceParticipants(participants) {
3078
- this.participants.splice(0, this.participants.length, ...participants);
3079
- }
3080
3790
  buildAdapterTools() {
3081
3791
  const tools = {
3082
3792
  capabilities: this.capabilities
@@ -3107,51 +3817,51 @@ var AgentTools = class {
3107
3817
  }
3108
3818
  buildMessagingToolHandlers() {
3109
3819
  return {
3110
- thenvoi_send_message: async (arguments_) => this.sendMessage(
3820
+ band_send_message: async (arguments_) => this.sendMessage(
3111
3821
  String(arguments_.content ?? ""),
3112
3822
  this.normalizeMentionInput(arguments_.mentions)
3113
3823
  ),
3114
- thenvoi_send_event: async (arguments_) => this.sendEvent(
3824
+ band_send_event: async (arguments_) => this.sendEvent(
3115
3825
  String(arguments_.content ?? ""),
3116
3826
  String(arguments_.message_type ?? "task"),
3117
3827
  this.normalizeOptionalMetadata(arguments_.metadata)
3118
3828
  ),
3119
- thenvoi_add_participant: async (arguments_) => this.addParticipant(String(arguments_.name ?? ""), String(arguments_.role ?? "member")),
3120
- thenvoi_remove_participant: async (arguments_) => this.removeParticipant(String(arguments_.name ?? "")),
3121
- thenvoi_lookup_peers: async (arguments_) => this.lookupPeers(
3829
+ band_add_participant: async (arguments_) => this.addParticipant(String(arguments_.name ?? ""), String(arguments_.role ?? "member")),
3830
+ band_remove_participant: async (arguments_) => this.removeParticipant(String(arguments_.name ?? "")),
3831
+ band_lookup_peers: async (arguments_) => this.lookupPeers(
3122
3832
  coercePositiveInt(arguments_.page, 1),
3123
3833
  coercePositiveInt(arguments_.page_size, 50)
3124
3834
  ),
3125
- thenvoi_get_participants: async () => this.getParticipants(),
3126
- thenvoi_create_chatroom: async (arguments_) => this.createChatroom(this.normalizeOptionalString(arguments_.task_id))
3835
+ band_get_participants: async () => this.getParticipants(),
3836
+ band_create_chatroom: async (arguments_) => this.createChatroom(this.normalizeOptionalString(arguments_.task_id))
3127
3837
  };
3128
3838
  }
3129
3839
  buildContactToolHandlers() {
3130
3840
  return {
3131
- thenvoi_list_contacts: async (arguments_) => this.listContacts({
3841
+ band_list_contacts: async (arguments_) => this.listContacts({
3132
3842
  page: coercePositiveInt(arguments_.page, 1),
3133
3843
  pageSize: coercePositiveInt(arguments_.page_size, 50)
3134
3844
  }),
3135
- thenvoi_add_contact: async (arguments_) => this.addContact({
3845
+ band_add_contact: async (arguments_) => this.addContact({
3136
3846
  handle: String(arguments_.handle ?? ""),
3137
3847
  ...typeof arguments_.message === "string" ? { message: arguments_.message } : {}
3138
3848
  }),
3139
- thenvoi_remove_contact: async (arguments_) => this.removeContact(this.toRemoveContactArgs(arguments_)),
3140
- thenvoi_list_contact_requests: async (arguments_) => this.listContactRequests({
3849
+ band_remove_contact: async (arguments_) => this.removeContact(this.toRemoveContactArgs(arguments_)),
3850
+ band_list_contact_requests: async (arguments_) => this.listContactRequests({
3141
3851
  page: coercePositiveInt(arguments_.page, 1),
3142
3852
  pageSize: coercePositiveInt(arguments_.page_size, 50),
3143
3853
  sentStatus: String(arguments_.sent_status ?? "pending")
3144
3854
  }),
3145
- thenvoi_respond_contact_request: async (arguments_) => this.respondContactRequest(this.toRespondContactRequestArgs(arguments_))
3855
+ band_respond_contact_request: async (arguments_) => this.respondContactRequest(this.toRespondContactRequestArgs(arguments_))
3146
3856
  };
3147
3857
  }
3148
3858
  buildMemoryToolHandlers() {
3149
3859
  return {
3150
- thenvoi_list_memories: async (arguments_) => this.listMemories(this.toListMemoriesArgs(arguments_)),
3151
- thenvoi_store_memory: async (arguments_) => this.storeMemory(this.toStoreMemoryArgs(arguments_)),
3152
- thenvoi_get_memory: async (arguments_) => this.getMemory(String(arguments_.memory_id ?? "")),
3153
- thenvoi_supersede_memory: async (arguments_) => this.supersedeMemory(String(arguments_.memory_id ?? "")),
3154
- thenvoi_archive_memory: async (arguments_) => this.archiveMemory(String(arguments_.memory_id ?? ""))
3860
+ band_list_memories: async (arguments_) => this.listMemories(this.toListMemoriesArgs(arguments_)),
3861
+ band_store_memory: async (arguments_) => this.storeMemory(this.toStoreMemoryArgs(arguments_)),
3862
+ band_get_memory: async (arguments_) => this.getMemory(String(arguments_.memory_id ?? "")),
3863
+ band_supersede_memory: async (arguments_) => this.supersedeMemory(String(arguments_.memory_id ?? "")),
3864
+ band_archive_memory: async (arguments_) => this.archiveMemory(String(arguments_.memory_id ?? ""))
3155
3865
  };
3156
3866
  }
3157
3867
  toRemoveContactArgs(arguments_) {
@@ -3203,6 +3913,9 @@ var AgentTools = class {
3203
3913
  const status = this.normalizeOptionalMemoryStatus(arguments_.status);
3204
3914
  const subjectId = this.normalizeOptionalString(arguments_.subject_id);
3205
3915
  const contentQuery = this.normalizeOptionalString(arguments_.content_query);
3916
+ if (system && type && !isMemoryTypeForSystem(system, type)) {
3917
+ throw new ValidationError(memoryTypeForSystemError(system));
3918
+ }
3206
3919
  return {
3207
3920
  ...subjectId ? { subject_id: subjectId } : {},
3208
3921
  ...scope ? { scope } : {},
@@ -3224,13 +3937,21 @@ var AgentTools = class {
3224
3937
  const subjectId = this.normalizeOptionalString(arguments_.subject_id);
3225
3938
  const metadata = this.normalizeOptionalMetadata(arguments_.metadata);
3226
3939
  if (!system) {
3227
- throw new ValidationError("system must be one of: sensory, working, long_term");
3940
+ throw new ValidationError(`system must be one of: ${expectedList(MEMORY_SYSTEMS)}`);
3228
3941
  }
3229
3942
  if (!type) {
3230
- throw new ValidationError("type must be one of: iconic, echoic, haptic, episodic, semantic, procedural");
3943
+ throw new ValidationError(`type must be one of: ${expectedList(MEMORY_TYPES)}`);
3944
+ }
3945
+ if (!isMemoryTypeForSystem(system, type)) {
3946
+ throw new ValidationError(memoryTypeForSystemError(system));
3231
3947
  }
3232
3948
  if (!segment) {
3233
- throw new ValidationError("segment must be one of: user, agent, tool, guideline");
3949
+ throw new ValidationError(`segment must be one of: ${expectedList(MEMORY_SEGMENTS)}`);
3950
+ }
3951
+ if (scope === "subject" && !subjectId) {
3952
+ throw new ValidationError(
3953
+ '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.'
3954
+ );
3234
3955
  }
3235
3956
  return {
3236
3957
  content,
@@ -3310,8 +4031,8 @@ var AgentTools = class {
3310
4031
  if (!normalized) {
3311
4032
  return void 0;
3312
4033
  }
3313
- if (!isListMemoryScope(normalized)) {
3314
- throw new ValidationError("scope must be one of: subject, organization, all");
4034
+ if (!isMemoryListScope(normalized)) {
4035
+ throw new ValidationError(`scope must be one of: ${expectedList(MEMORY_LIST_SCOPES)}`);
3315
4036
  }
3316
4037
  return normalized;
3317
4038
  }
@@ -3321,7 +4042,7 @@ var AgentTools = class {
3321
4042
  return void 0;
3322
4043
  }
3323
4044
  if (!isMemorySystem(normalized)) {
3324
- throw new ValidationError("system must be one of: sensory, working, long_term");
4045
+ throw new ValidationError(`system must be one of: ${expectedList(MEMORY_SYSTEMS)}`);
3325
4046
  }
3326
4047
  return normalized;
3327
4048
  }
@@ -3331,7 +4052,7 @@ var AgentTools = class {
3331
4052
  return void 0;
3332
4053
  }
3333
4054
  if (!isMemoryType(normalized)) {
3334
- throw new ValidationError("type must be one of: iconic, echoic, haptic, episodic, semantic, procedural");
4055
+ throw new ValidationError(`type must be one of: ${expectedList(MEMORY_TYPES)}`);
3335
4056
  }
3336
4057
  return normalized;
3337
4058
  }
@@ -3341,7 +4062,7 @@ var AgentTools = class {
3341
4062
  return void 0;
3342
4063
  }
3343
4064
  if (!isMemorySegment(normalized)) {
3344
- throw new ValidationError("segment must be one of: user, agent, tool, guideline");
4065
+ throw new ValidationError(`segment must be one of: ${expectedList(MEMORY_SEGMENTS)}`);
3345
4066
  }
3346
4067
  return normalized;
3347
4068
  }
@@ -3351,7 +4072,7 @@ var AgentTools = class {
3351
4072
  return void 0;
3352
4073
  }
3353
4074
  if (!isMemoryStatus(normalized)) {
3354
- throw new ValidationError("status must be one of: active, superseded, archived, all");
4075
+ throw new ValidationError(`status must be one of: ${expectedList(MEMORY_STATUSES)}`);
3355
4076
  }
3356
4077
  return normalized;
3357
4078
  }
@@ -3360,8 +4081,8 @@ var AgentTools = class {
3360
4081
  if (!normalized) {
3361
4082
  return void 0;
3362
4083
  }
3363
- if (!isStoreMemoryScope(normalized)) {
3364
- throw new ValidationError("scope must be one of: subject, organization");
4084
+ if (!isMemoryStoreScope(normalized)) {
4085
+ throw new ValidationError(`scope must be one of: ${expectedList(MEMORY_STORE_SCOPES)}`);
3365
4086
  }
3366
4087
  return normalized;
3367
4088
  }
@@ -3369,24 +4090,6 @@ var AgentTools = class {
3369
4090
  function isContactRequestAction(value) {
3370
4091
  return CONTACT_REQUEST_ACTIONS.has(value);
3371
4092
  }
3372
- function isMemorySystem(value) {
3373
- return MEMORY_SYSTEMS.has(value);
3374
- }
3375
- function isMemoryType(value) {
3376
- return MEMORY_TYPES.has(value);
3377
- }
3378
- function isMemorySegment(value) {
3379
- return MEMORY_SEGMENTS.has(value);
3380
- }
3381
- function isStoreMemoryScope(value) {
3382
- return MEMORY_SCOPES.has(value);
3383
- }
3384
- function isListMemoryScope(value) {
3385
- return LIST_MEMORY_SCOPES.has(value);
3386
- }
3387
- function isMemoryStatus(value) {
3388
- return LIST_MEMORY_STATUSES.has(value);
3389
- }
3390
4093
  function coercePositiveInt(value, fallback) {
3391
4094
  const num = Number(value ?? fallback);
3392
4095
  return Number.isFinite(num) && num >= 1 ? Math.floor(num) : fallback;
@@ -3402,13 +4105,13 @@ function validateToolArgs(toolName, args) {
3402
4105
  errors.push(`${field}: Field required`);
3403
4106
  }
3404
4107
  }
3405
- if (toolName === "thenvoi_send_message") {
4108
+ if (toolName === "band_send_message") {
3406
4109
  const mentions = args.mentions;
3407
4110
  if (Array.isArray(mentions) && mentions.length === 0) {
3408
4111
  errors.push("mentions: At least one mention is required");
3409
4112
  }
3410
4113
  }
3411
- if (toolName === "thenvoi_send_event") {
4114
+ if (toolName === "band_send_event") {
3412
4115
  const messageType = args.message_type;
3413
4116
  if (typeof messageType === "string" && !CHAT_EVENT_TYPES.includes(messageType)) {
3414
4117
  errors.push(
@@ -3416,7 +4119,7 @@ function validateToolArgs(toolName, args) {
3416
4119
  );
3417
4120
  }
3418
4121
  }
3419
- if (toolName === "thenvoi_respond_contact_request") {
4122
+ if (toolName === "band_respond_contact_request") {
3420
4123
  const action = args.action;
3421
4124
  const validActions = ["approve", "reject", "cancel"];
3422
4125
  if (typeof action === "string" && !validActions.includes(action)) {
@@ -3425,18 +4128,20 @@ function validateToolArgs(toolName, args) {
3425
4128
  );
3426
4129
  }
3427
4130
  }
3428
- if (toolName === "thenvoi_store_memory") {
3429
- const validSystems = ["sensory", "working", "long_term"];
3430
- const validTypes = ["iconic", "echoic", "haptic", "episodic", "semantic", "procedural"];
3431
- const validSegments = ["user", "agent", "tool", "guideline"];
3432
- if (typeof args.system === "string" && !validSystems.includes(args.system)) {
3433
- errors.push(`system: Invalid value '${args.system}'. Expected one of: ${validSystems.join(", ")}`);
4131
+ if (toolName === "band_store_memory") {
4132
+ if (typeof args.system === "string" && !isMemorySystem(args.system)) {
4133
+ errors.push(`system: Invalid value '${args.system}'. Expected one of: ${expectedList(MEMORY_SYSTEMS)}`);
3434
4134
  }
3435
- if (typeof args.type === "string" && !validTypes.includes(args.type)) {
3436
- errors.push(`type: Invalid value '${args.type}'. Expected one of: ${validTypes.join(", ")}`);
4135
+ if (typeof args.type === "string" && !isMemoryType(args.type)) {
4136
+ errors.push(`type: Invalid value '${args.type}'. Expected one of: ${expectedList(MEMORY_TYPES)}`);
4137
+ }
4138
+ if (typeof args.system === "string" && isMemorySystem(args.system) && typeof args.type === "string" && isMemoryType(args.type) && !isMemoryTypeForSystem(args.system, args.type)) {
4139
+ errors.push(
4140
+ `type: Invalid value '${args.type}' for system '${args.system}'. Expected one of: ${expectedMemoryTypesForSystem(args.system)}`
4141
+ );
3437
4142
  }
3438
- if (typeof args.segment === "string" && !validSegments.includes(args.segment)) {
3439
- errors.push(`segment: Invalid value '${args.segment}'. Expected one of: ${validSegments.join(", ")}`);
4143
+ if (typeof args.segment === "string" && !isMemorySegment(args.segment)) {
4144
+ errors.push(`segment: Invalid value '${args.segment}'. Expected one of: ${expectedList(MEMORY_SEGMENTS)}`);
3440
4145
  }
3441
4146
  }
3442
4147
  if (errors.length > 0) {
@@ -3452,112 +4157,8 @@ function validateToolArgs(toolName, args) {
3452
4157
  return null;
3453
4158
  }
3454
4159
 
3455
- // src/runtime/retryTracker.ts
3456
- var DEFAULT_MAX_TRACKED = 1e4;
3457
- var MessageRetryTracker = class {
3458
- maxRetriesValue;
3459
- maxTracked;
3460
- attempts = /* @__PURE__ */ new Map();
3461
- failed = /* @__PURE__ */ new Set();
3462
- constructor(maxRetries = 1, maxTracked = DEFAULT_MAX_TRACKED) {
3463
- this.maxRetriesValue = maxRetries;
3464
- this.maxTracked = maxTracked;
3465
- }
3466
- get maxRetries() {
3467
- return this.maxRetriesValue;
3468
- }
3469
- isPermanentlyFailed(messageId) {
3470
- return this.failed.has(messageId);
3471
- }
3472
- recordAttempt(messageId) {
3473
- const attempts = (this.attempts.get(messageId) ?? 0) + 1;
3474
- this.attempts.set(messageId, attempts);
3475
- const exceeded = attempts > this.maxRetriesValue;
3476
- if (exceeded) {
3477
- this.evictSetIfNeeded(this.failed);
3478
- this.failed.add(messageId);
3479
- }
3480
- this.evictMapIfNeeded(this.attempts);
3481
- return [attempts, exceeded];
3482
- }
3483
- markSuccess(messageId) {
3484
- this.attempts.delete(messageId);
3485
- }
3486
- markPermanentlyFailed(messageId) {
3487
- this.evictSetIfNeeded(this.failed);
3488
- this.failed.add(messageId);
3489
- }
3490
- evictMapIfNeeded(map) {
3491
- while (map.size >= this.maxTracked) {
3492
- const first = map.keys().next();
3493
- if (first.done) break;
3494
- map.delete(first.value);
3495
- }
3496
- }
3497
- evictSetIfNeeded(set) {
3498
- while (set.size >= this.maxTracked) {
3499
- const first = set.values().next();
3500
- if (first.done) break;
3501
- set.delete(first.value);
3502
- }
3503
- }
3504
- };
3505
-
3506
- // src/runtime/formatters.ts
3507
- function isMetadataMap(value) {
3508
- return value !== null && typeof value === "object" && !Array.isArray(value);
3509
- }
3510
- function replaceUuidMentions(content, participants) {
3511
- if (!content || participants.length === 0) {
3512
- return content;
3513
- }
3514
- let next = content;
3515
- for (const participant of participants) {
3516
- const participantId = participant.id;
3517
- const handle = participant.handle;
3518
- if (typeof participantId === "string" && typeof handle === "string") {
3519
- next = next.replaceAll(`@[[${participantId}]]`, `@${handle}`);
3520
- }
3521
- }
3522
- return next;
3523
- }
3524
- function formatMessageForLlm(message, participants) {
3525
- const senderType = String(message.sender_type ?? "");
3526
- const senderName = String(message.sender_name ?? message.name ?? senderType);
3527
- const content = participants ? replaceUuidMentions(String(message.content ?? ""), participants) : String(message.content ?? "");
3528
- return {
3529
- role: senderType === "Agent" ? "assistant" : "user",
3530
- content,
3531
- sender_name: senderName,
3532
- sender_type: senderType,
3533
- message_type: String(message.message_type ?? "text"),
3534
- metadata: isMetadataMap(message.metadata) ? message.metadata : {}
3535
- };
3536
- }
3537
- function formatHistoryForLlm(messages, options) {
3538
- const excludeId = options?.excludeId;
3539
- const participants = options?.participants;
3540
- return messages.filter((message) => String(message.id ?? "") !== excludeId).map((message) => formatMessageForLlm(message, participants));
3541
- }
3542
- function buildParticipantsMessage(participants) {
3543
- if (participants.length === 0) {
3544
- return "## Current Participants\nNo other participants in this room.";
3545
- }
3546
- const lines = ["## Current Participants"];
3547
- for (const participant of participants) {
3548
- const participantType = String(participant.type ?? "Unknown");
3549
- const participantName = String(participant.name ?? "Unknown");
3550
- const participantHandle = String(participant.handle ?? "Unknown");
3551
- lines.push(`- @${participantHandle} \u2014 ${participantName} (${participantType})`);
3552
- }
3553
- lines.push("");
3554
- lines.push(
3555
- "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."
3556
- );
3557
- return lines.join("\n");
3558
- }
3559
-
3560
4160
  // src/runtime/ExecutionContext.ts
4161
+ var import_band_sdk_core2 = require("@band-ai/band-sdk-core");
3561
4162
  var DEDUP_CACHE_MAX = 500;
3562
4163
  var ExecutionContext = class {
3563
4164
  roomId;
@@ -3569,11 +4170,10 @@ var ExecutionContext = class {
3569
4170
  history = [];
3570
4171
  messageIds = /* @__PURE__ */ new Set();
3571
4172
  dedupCache = /* @__PURE__ */ new Map();
3572
- participants = [];
4173
+ roster = new import_band_sdk_core2.ParticipantRoster();
3573
4174
  tools;
3574
4175
  adapterTools;
3575
4176
  participantsMessage = null;
3576
- lastSentParticipantIds = null;
3577
4177
  contactsMessage = null;
3578
4178
  contextCache = null;
3579
4179
  contextCacheExpiresAt = 0;
@@ -3588,11 +4188,11 @@ var ExecutionContext = class {
3588
4188
  this.enableContextCache = options.enableContextCache ?? true;
3589
4189
  this.contextCacheTtlMs = Math.max(0, (options.contextCacheTtlSeconds ?? 300) * 1e3);
3590
4190
  this.enableContextHydration = options.enableContextHydration ?? true;
3591
- this.retryTrackerInstance = new MessageRetryTracker(options.maxMessageRetries ?? 1);
4191
+ this.retryTrackerInstance = new import_band_sdk_core2.RetryTracker(options.maxMessageRetries ?? 1);
3592
4192
  this.tools = new AgentTools({
3593
4193
  roomId: this.roomId,
3594
4194
  rest: this.link.rest,
3595
- participants: this.participants,
4195
+ roster: this.roster,
3596
4196
  capabilities: this.link.capabilities
3597
4197
  });
3598
4198
  this.adapterTools = this.tools.getAdapterTools();
@@ -3660,23 +4260,20 @@ var ExecutionContext = class {
3660
4260
  }));
3661
4261
  }
3662
4262
  setParticipants(participants) {
3663
- this.replaceParticipants(participants);
4263
+ this.roster.setAll(participants);
3664
4264
  this.updateCachedParticipants();
3665
4265
  }
3666
4266
  addParticipant(participant) {
3667
- const existingIndex = this.participants.findIndex((entry) => entry.id === participant.id);
3668
- if (existingIndex >= 0) {
3669
- this.participants.splice(existingIndex, 1);
4267
+ const isNew = this.roster.add(participant);
4268
+ if (isNew) {
4269
+ const name = String(participant.name ?? "unknown");
4270
+ this.participantsMessage = `${name} joined the room.`;
3670
4271
  }
3671
- this.participants.push(participant);
3672
- const name = String(participant.name ?? "unknown");
3673
- this.participantsMessage = `${name} joined the room.`;
3674
4272
  this.updateCachedParticipants();
3675
4273
  }
3676
4274
  removeParticipant(participantId) {
3677
- const removed = this.participants.find((entry) => String(entry.id) === participantId);
3678
- const next = this.participants.filter((entry) => String(entry.id) !== participantId);
3679
- this.replaceParticipants(next);
4275
+ const removed = this.roster.list().find((entry) => entry.id === participantId);
4276
+ this.roster.remove(participantId);
3680
4277
  if (removed) {
3681
4278
  this.participantsMessage = `${String(removed.name ?? participantId)} left the room.`;
3682
4279
  }
@@ -3686,16 +4283,7 @@ var ExecutionContext = class {
3686
4283
  this.contactsMessage = message;
3687
4284
  }
3688
4285
  consumeParticipantsMessage() {
3689
- const currentIds = new Set(this.participants.map((p) => String(p.id)));
3690
- let changed = !this.lastSentParticipantIds || currentIds.size !== this.lastSentParticipantIds.size;
3691
- if (!changed) {
3692
- for (const id of currentIds) {
3693
- if (!this.lastSentParticipantIds.has(id)) {
3694
- changed = true;
3695
- break;
3696
- }
3697
- }
3698
- }
4286
+ const changed = this.roster.changed();
3699
4287
  if (!changed && !this.participantsMessage) {
3700
4288
  return null;
3701
4289
  }
@@ -3705,15 +4293,9 @@ var ExecutionContext = class {
3705
4293
  this.participantsMessage = null;
3706
4294
  }
3707
4295
  if (changed) {
3708
- const asRecords = this.participants.map((p) => ({
3709
- id: p.id,
3710
- name: p.name,
3711
- type: p.type,
3712
- handle: p.handle ?? null
3713
- }));
3714
- parts.push(buildParticipantsMessage(asRecords));
4296
+ parts.push(buildParticipantsMessage(this.roster.list().map((p) => ({ ...toParticipantRecord(p) }))));
3715
4297
  }
3716
- this.lastSentParticipantIds = currentIds;
4298
+ this.roster.markSent();
3717
4299
  return parts.length > 0 ? parts.join("\n") : null;
3718
4300
  }
3719
4301
  consumeContactsMessage() {
@@ -3764,27 +4346,19 @@ var ExecutionContext = class {
3764
4346
  return fallback;
3765
4347
  }
3766
4348
  }
3767
- replaceParticipants(participants) {
3768
- this.participants.splice(0, this.participants.length, ...participants);
3769
- }
3770
4349
  buildLocalContext() {
3771
4350
  return {
3772
4351
  roomId: this.roomId,
3773
4352
  messages: this.getRawHistory(),
3774
- participants: [...this.participants],
4353
+ participants: this.roster.list().map(toParticipantRecord),
3775
4354
  hydratedAt: /* @__PURE__ */ new Date()
3776
4355
  };
3777
4356
  }
3778
4357
  async loadParticipants() {
3779
4358
  const participants = await this.link.rest.listChatParticipants(this.roomId, DEFAULT_REQUEST_OPTIONS);
3780
- const normalized = participants.map((participant) => ({
3781
- id: participant.id,
3782
- name: participant.name,
3783
- type: participant.type,
3784
- handle: participant.handle ?? null
3785
- }));
3786
- this.replaceParticipants(normalized);
3787
- return [...this.participants];
4359
+ const normalized = participants.map(toParticipantRecordFromRest);
4360
+ this.roster.setAll(normalized);
4361
+ return this.roster.list().map(toParticipantRecord);
3788
4362
  }
3789
4363
  async loadHydratedMessages() {
3790
4364
  const messages = [];
@@ -3826,7 +4400,7 @@ var ExecutionContext = class {
3826
4400
  if (!this.contextCache) {
3827
4401
  return;
3828
4402
  }
3829
- this.contextCache.participants = [...this.participants];
4403
+ this.contextCache.participants = this.roster.list().map(toParticipantRecord);
3830
4404
  this.contextCache.hydratedAt = /* @__PURE__ */ new Date();
3831
4405
  this.contextCacheExpiresAt = this.nextCacheExpiry();
3832
4406
  }
@@ -4028,7 +4602,7 @@ var AgentRuntime = class {
4028
4602
  await this.link.disconnect();
4029
4603
  }
4030
4604
  async stop(timeoutMs) {
4031
- if (!this.running || this.stopping) {
4605
+ if (this.stopping || !this.running && !this.fatalError) {
4032
4606
  return true;
4033
4607
  }
4034
4608
  this.stopping = true;
@@ -4048,7 +4622,8 @@ var AgentRuntime = class {
4048
4622
  }
4049
4623
  }
4050
4624
  for (const roomId of [...this.subscribedRooms]) {
4051
- await this.leaveTrackedRoom(roomId);
4625
+ const remaining = deadline === void 0 ? void 0 : Math.max(0, deadline - Date.now());
4626
+ await this.leaveTrackedRoom(roomId, remaining);
4052
4627
  }
4053
4628
  for (const roomId of [...this.contexts.keys()]) {
4054
4629
  await this.onSessionCleanup(roomId);
@@ -4084,7 +4659,13 @@ var AgentRuntime = class {
4084
4659
  }
4085
4660
  async consumeLoop(signal) {
4086
4661
  while (!signal.aborted) {
4087
- const event = await this.link.nextEvent(signal);
4662
+ let event;
4663
+ try {
4664
+ event = await this.link.nextEvent(signal);
4665
+ } catch (error) {
4666
+ await this.failRuntime(error, syntheticRuntimeFailureEvent(this.agentId));
4667
+ return;
4668
+ }
4088
4669
  if (!event) {
4089
4670
  return;
4090
4671
  }
@@ -4247,13 +4828,13 @@ var AgentRuntime = class {
4247
4828
  }
4248
4829
  });
4249
4830
  }
4250
- async leaveTrackedRoom(roomId) {
4831
+ async leaveTrackedRoom(roomId, timeoutMs) {
4251
4832
  await trackRoomLeave({
4252
4833
  link: this.link,
4253
4834
  roomId,
4254
4835
  trackedRooms: this.subscribedRooms,
4255
4836
  onLeft: async (leftRoomId) => {
4256
- await this.executions.get(leftRoomId)?.stop();
4837
+ await this.executions.get(leftRoomId)?.stop(timeoutMs);
4257
4838
  this.contexts.delete(leftRoomId);
4258
4839
  this.executions.delete(leftRoomId);
4259
4840
  await this.onSessionCleanup(leftRoomId);
@@ -4290,27 +4871,27 @@ var AgentRuntime = class {
4290
4871
  }
4291
4872
  }
4292
4873
  };
4874
+ function syntheticRuntimeFailureEvent(agentId) {
4875
+ return {
4876
+ type: "message_created",
4877
+ roomId: null,
4878
+ payload: {
4879
+ id: "runtime-failed",
4880
+ content: "",
4881
+ sender_id: agentId,
4882
+ sender_type: "Agent",
4883
+ sender_name: null,
4884
+ message_type: "text",
4885
+ metadata: {},
4886
+ inserted_at: (/* @__PURE__ */ new Date(0)).toISOString(),
4887
+ updated_at: (/* @__PURE__ */ new Date(0)).toISOString()
4888
+ }
4889
+ };
4890
+ }
4293
4891
  function assertNever(value) {
4294
4892
  throw new Error(`Unhandled platform event: ${JSON.stringify(value)}`);
4295
4893
  }
4296
4894
 
4297
- // src/runtime/types.ts
4298
- var HistoryProvider = class {
4299
- raw;
4300
- constructor(raw) {
4301
- this.raw = raw;
4302
- }
4303
- convert(converter) {
4304
- return converter.convert(this.raw);
4305
- }
4306
- get length() {
4307
- return this.raw.length;
4308
- }
4309
- };
4310
- var SYNTHETIC_SENDER_TYPE = "System";
4311
- var SYNTHETIC_CONTACT_EVENTS_SENDER_ID = "contact-events";
4312
- var SYNTHETIC_CONTACT_EVENTS_SENDER_NAME = "Contact Events";
4313
-
4314
4895
  // src/runtime/PlatformRuntime.ts
4315
4896
  init_errors();
4316
4897
 
@@ -4465,14 +5046,6 @@ var ContactToolsImpl = class {
4465
5046
  };
4466
5047
 
4467
5048
  // src/runtime/tools/ContactCallbackTools.ts
4468
- function toParticipantRecord(participant) {
4469
- return {
4470
- id: participant.id,
4471
- name: participant.name,
4472
- type: participant.type,
4473
- handle: participant.handle ?? null
4474
- };
4475
- }
4476
5049
  function normalizePage(value, fallback) {
4477
5050
  return Number.isInteger(value) && value && value > 0 ? value : fallback;
4478
5051
  }
@@ -4516,8 +5089,7 @@ var ContactCallbackTools = class {
4516
5089
  {
4517
5090
  content,
4518
5091
  ...normalizedMentions ? { mentions: normalizedMentions } : {}
4519
- },
4520
- DEFAULT_REQUEST_OPTIONS
5092
+ }
4521
5093
  );
4522
5094
  }
4523
5095
  async sendEvent(content, messageType, metadata) {
@@ -4531,8 +5103,7 @@ var ContactCallbackTools = class {
4531
5103
  content,
4532
5104
  messageType,
4533
5105
  ...metadata ? { metadata } : {}
4534
- },
4535
- DEFAULT_REQUEST_OPTIONS
5106
+ }
4536
5107
  );
4537
5108
  }
4538
5109
  async addParticipant() {
@@ -4546,7 +5117,7 @@ var ContactCallbackTools = class {
4546
5117
  if (!this.rest.listChatParticipants) {
4547
5118
  throw new UnsupportedFeatureError("Participant listing is not available in current REST adapter");
4548
5119
  }
4549
- return (await this.rest.listChatParticipants(roomId, DEFAULT_REQUEST_OPTIONS)).map(toParticipantRecord);
5120
+ return (await this.rest.listChatParticipants(roomId, DEFAULT_REQUEST_OPTIONS)).map(toParticipantRecordFromRest);
4550
5121
  }
4551
5122
  async createChatroom(taskId) {
4552
5123
  const chat = await this.rest.createChat(taskId, DEFAULT_REQUEST_OPTIONS);
@@ -4636,48 +5207,48 @@ var ContactCallbackTools = class {
4636
5207
  }
4637
5208
  async executeToolCall(toolName, toolArgs) {
4638
5209
  switch (toolName) {
4639
- case "thenvoi_send_message":
5210
+ case "band_send_message":
4640
5211
  return this.sendMessage(
4641
5212
  String(toolArgs.content ?? ""),
4642
5213
  toolArgs.mentions
4643
5214
  );
4644
- case "thenvoi_send_event":
5215
+ case "band_send_event":
4645
5216
  return this.sendEvent(
4646
5217
  String(toolArgs.content ?? ""),
4647
5218
  String(toolArgs.message_type ?? "task"),
4648
5219
  toolArgs.metadata
4649
5220
  );
4650
- case "thenvoi_get_participants":
5221
+ case "band_get_participants":
4651
5222
  return this.getParticipants();
4652
- case "thenvoi_create_chatroom":
5223
+ case "band_create_chatroom":
4653
5224
  return this.createChatroom(typeof toolArgs.task_id === "string" ? toolArgs.task_id : void 0);
4654
- case "thenvoi_lookup_peers":
5225
+ case "band_lookup_peers":
4655
5226
  return this.lookupPeers(
4656
5227
  typeof toolArgs.page === "number" ? toolArgs.page : void 0,
4657
5228
  typeof toolArgs.page_size === "number" ? toolArgs.page_size : void 0
4658
5229
  );
4659
- case "thenvoi_list_contacts":
5230
+ case "band_list_contacts":
4660
5231
  return this.listContacts({
4661
5232
  page: typeof toolArgs.page === "number" ? toolArgs.page : void 0,
4662
5233
  pageSize: typeof toolArgs.page_size === "number" ? toolArgs.page_size : void 0
4663
5234
  });
4664
- case "thenvoi_add_contact":
5235
+ case "band_add_contact":
4665
5236
  return this.addContact({
4666
5237
  handle: String(toolArgs.handle ?? ""),
4667
5238
  ...typeof toolArgs.message === "string" ? { message: toolArgs.message } : {}
4668
5239
  });
4669
- case "thenvoi_remove_contact":
5240
+ case "band_remove_contact":
4670
5241
  if (typeof toolArgs.contact_id === "string") {
4671
5242
  return this.removeContact({ target: "contactId", contactId: toolArgs.contact_id });
4672
5243
  }
4673
5244
  return this.removeContact({ target: "handle", handle: String(toolArgs.handle ?? "") });
4674
- case "thenvoi_list_contact_requests":
5245
+ case "band_list_contact_requests":
4675
5246
  return this.listContactRequests({
4676
5247
  page: typeof toolArgs.page === "number" ? toolArgs.page : void 0,
4677
5248
  pageSize: typeof toolArgs.page_size === "number" ? toolArgs.page_size : void 0,
4678
5249
  sentStatus: typeof toolArgs.sent_status === "string" ? toolArgs.sent_status : void 0
4679
5250
  });
4680
- case "thenvoi_respond_contact_request":
5251
+ case "band_respond_contact_request":
4681
5252
  if (typeof toolArgs.request_id === "string") {
4682
5253
  return this.respondContactRequest({
4683
5254
  action: String(toolArgs.action ?? "approve"),
@@ -4690,15 +5261,15 @@ var ContactCallbackTools = class {
4690
5261
  target: "handle",
4691
5262
  handle: String(toolArgs.handle ?? "")
4692
5263
  });
4693
- case "thenvoi_list_memories":
5264
+ case "band_list_memories":
4694
5265
  return this.listMemories(toolArgs);
4695
- case "thenvoi_store_memory":
5266
+ case "band_store_memory":
4696
5267
  return this.storeMemory(toolArgs);
4697
- case "thenvoi_get_memory":
5268
+ case "band_get_memory":
4698
5269
  return this.getMemory(String(toolArgs.memory_id ?? ""));
4699
- case "thenvoi_supersede_memory":
5270
+ case "band_supersede_memory":
4700
5271
  return this.supersedeMemory(String(toolArgs.memory_id ?? ""));
4701
- case "thenvoi_archive_memory":
5272
+ case "band_archive_memory":
4702
5273
  return this.archiveMemory(String(toolArgs.memory_id ?? ""));
4703
5274
  default:
4704
5275
  throw new UnsupportedFeatureError(`Unsupported tool call for contact callback: ${toolName}`);
@@ -4712,14 +5283,14 @@ var HUB_ROOM_SYSTEM_PROMPT = `## OVERRIDE: Contact Management Mode
4712
5283
 
4713
5284
  This is your CONTACTS HUB - a dedicated room for managing contact requests.
4714
5285
 
4715
- **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.
5286
+ **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.
4716
5287
 
4717
5288
  ## Your Role
4718
5289
 
4719
5290
  1. **Review incoming contact requests** - When you see a [Contact Request] message, evaluate it
4720
5291
  2. **Take action** - Use the contact tools to respond:
4721
- - \`thenvoi_respond_contact_request(action="approve", request_id="...")\` to accept
4722
- - \`thenvoi_respond_contact_request(action="reject", request_id="...")\` to decline
5292
+ - \`band_respond_contact_request(action="approve", request_id="...")\` to accept
5293
+ - \`band_respond_contact_request(action="reject", request_id="...")\` to decline
4723
5294
  3. **Report your decision** - Send a thought event explaining what you did
4724
5295
 
4725
5296
  ## Example
@@ -4728,14 +5299,14 @@ This is your CONTACTS HUB - a dedicated room for managing contact requests.
4728
5299
  Request ID: abc-123
4729
5300
 
4730
5301
  Your response:
4731
- 1. thenvoi_send_event("Received contact request from Alice. Approving.", message_type="thought")
4732
- 2. thenvoi_respond_contact_request(action="approve", request_id="abc-123")
4733
- 3. thenvoi_send_event("Approved contact request from Alice (@alice)", message_type="thought")
5302
+ 1. band_send_event("Received contact request from Alice. Approving.", message_type="thought")
5303
+ 2. band_respond_contact_request(action="approve", request_id="abc-123")
5304
+ 3. band_send_event("Approved contact request from Alice (@alice)", message_type="thought")
4734
5305
 
4735
5306
  ## Contact Tools (use these, NOT participant tools)
4736
- - \`thenvoi_respond_contact_request(action, request_id)\` - Approve/reject requests
4737
- - \`thenvoi_list_contact_requests()\` - List pending requests
4738
- - \`thenvoi_list_contacts()\` - List current contacts`;
5307
+ - \`band_respond_contact_request(action, request_id)\` - Approve/reject requests
5308
+ - \`band_list_contact_requests()\` - List pending requests
5309
+ - \`band_list_contacts()\` - List current contacts`;
4739
5310
  var ContactEventHandlerError = class extends Error {
4740
5311
  retryable;
4741
5312
  stage;
@@ -5063,6 +5634,8 @@ function normalizeHandle(handle) {
5063
5634
  }
5064
5635
 
5065
5636
  // src/runtime/PlatformRuntime.ts
5637
+ var MAX_MESSAGE_RETRIES = 4294967295;
5638
+ var isValidRetryCount = (value) => Number.isInteger(value) && value >= 0 && value <= MAX_MESSAGE_RETRIES;
5066
5639
  var PlatformRuntime = class {
5067
5640
  _agentId;
5068
5641
  _apiKey;
@@ -5099,6 +5672,12 @@ var PlatformRuntime = class {
5099
5672
  "apiKey is required and must be a non-empty string. Use loadAgentConfig() to load credentials from agent_config.yaml."
5100
5673
  );
5101
5674
  }
5675
+ const maxMessageRetries = options.sessionConfig?.maxMessageRetries;
5676
+ if (maxMessageRetries !== void 0 && !isValidRetryCount(maxMessageRetries)) {
5677
+ throw new ValidationError(
5678
+ `sessionConfig.maxMessageRetries must be an integer between 0 and ${MAX_MESSAGE_RETRIES}, got ${maxMessageRetries}.`
5679
+ );
5680
+ }
5102
5681
  this._agentId = options.agentId;
5103
5682
  this._apiKey = options.apiKey;
5104
5683
  this._wsUrl = options.wsUrl;
@@ -5151,12 +5730,13 @@ var PlatformRuntime = class {
5151
5730
  }
5152
5731
  async doInitialize() {
5153
5732
  if (!this.linkInstance) {
5154
- this.linkInstance = new ThenvoiLink({
5733
+ this.linkInstance = new BandLink({
5155
5734
  ...this.linkOptions,
5156
5735
  agentId: this._agentId,
5157
5736
  apiKey: this._apiKey,
5158
5737
  wsUrl: this._wsUrl,
5159
- restUrl: this._restUrl
5738
+ restUrl: this._restUrl,
5739
+ logger: this.logger
5160
5740
  });
5161
5741
  }
5162
5742
  if (this.configuredIdentity) {
@@ -5485,7 +6065,21 @@ var import_node_fs = require("fs");
5485
6065
  var import_js_yaml = __toESM(require("js-yaml"), 1);
5486
6066
  init_errors();
5487
6067
  var DEFAULT_CONFIG_PATH = "./agent_config.yaml";
5488
- var DEFAULT_ENV_PREFIX = "THENVOI_";
6068
+ var DEFAULT_ENV_PREFIX = "BAND_";
6069
+ var LEGACY_ENV_PREFIX = "THENVOI_";
6070
+ var _warnedLegacyEnvVars = /* @__PURE__ */ new Set();
6071
+ function readBandFirstEnv(env, field) {
6072
+ const bandVar = `${DEFAULT_ENV_PREFIX}${field}`;
6073
+ const bandValue = env[bandVar];
6074
+ if (bandValue !== void 0) return bandValue;
6075
+ const legacyVar = `${LEGACY_ENV_PREFIX}${field}`;
6076
+ const legacyValue = env[legacyVar];
6077
+ if (legacyValue !== void 0 && !_warnedLegacyEnvVars.has(legacyVar)) {
6078
+ _warnedLegacyEnvVars.add(legacyVar);
6079
+ console.warn(`[band] ${legacyVar} is deprecated; use ${bandVar} instead`);
6080
+ }
6081
+ return legacyValue;
6082
+ }
5489
6083
  var REQUIRED_FIELDS = ["agent_id", "api_key"];
5490
6084
  var UNSAFE_KEYS = /* @__PURE__ */ new Set(["__proto__", "constructor", "prototype", "toString", "valueOf"]);
5491
6085
  function toSnakeCase(key) {
@@ -5569,19 +6163,32 @@ function loadAgentConfig(agentKey, configPath) {
5569
6163
  }
5570
6164
  function loadAgentConfigFromEnv(options) {
5571
6165
  const env = options?.env ?? process.env;
5572
- const prefix = options?.prefix === void 0 ? DEFAULT_ENV_PREFIX : options.prefix === "" || options.prefix.endsWith("_") ? options.prefix : `${options.prefix}_`;
5573
- const section = normalizeKeys({
5574
- agent_id: env[`${prefix}AGENT_ID`],
5575
- api_key: env[`${prefix}API_KEY`],
5576
- ws_url: env[`${prefix}WS_URL`],
5577
- rest_url: env[`${prefix}REST_URL`]
5578
- });
6166
+ let section;
6167
+ let requiredHint;
6168
+ if (options?.prefix === void 0) {
6169
+ section = normalizeKeys({
6170
+ agent_id: readBandFirstEnv(env, "AGENT_ID"),
6171
+ api_key: readBandFirstEnv(env, "API_KEY"),
6172
+ ws_url: readBandFirstEnv(env, "WS_URL"),
6173
+ rest_url: readBandFirstEnv(env, "REST_URL")
6174
+ });
6175
+ requiredHint = `${DEFAULT_ENV_PREFIX}AGENT_ID and ${DEFAULT_ENV_PREFIX}API_KEY (legacy ${LEGACY_ENV_PREFIX}* still accepted)`;
6176
+ } else {
6177
+ const prefix = options.prefix === "" || options.prefix.endsWith("_") ? options.prefix : `${options.prefix}_`;
6178
+ section = normalizeKeys({
6179
+ agent_id: env[`${prefix}AGENT_ID`],
6180
+ api_key: env[`${prefix}API_KEY`],
6181
+ ws_url: env[`${prefix}WS_URL`],
6182
+ rest_url: env[`${prefix}REST_URL`]
6183
+ });
6184
+ requiredHint = `${prefix}AGENT_ID and ${prefix}API_KEY`;
6185
+ }
5579
6186
  try {
5580
- return toAgentConfigResult(section, `environment variables (${prefix}AGENT_ID, ${prefix}API_KEY)`);
6187
+ return toAgentConfigResult(section, `environment variables (${requiredHint})`);
5581
6188
  } catch (error) {
5582
6189
  if (error instanceof ValidationError) {
5583
6190
  throw new ValidationError(
5584
- `${error.message}. Set ${prefix}AGENT_ID and ${prefix}API_KEY, or use loadAgentConfig() for agent_config.yaml.`
6191
+ `${error.message}. Set ${requiredHint}, or use loadAgentConfig() for agent_config.yaml.`
5585
6192
  );
5586
6193
  }
5587
6194
  throw error;
@@ -5659,26 +6266,25 @@ var GenericAdapter = class extends SimpleAdapter {
5659
6266
  // src/adapters/codex/CodexAdapter.ts
5660
6267
  init_protocols();
5661
6268
 
5662
- // src/runtime/prompts.ts
5663
- var BASE_INSTRUCTIONS = `
6269
+ // src/runtime/prompts/base.ts
6270
+ var ENVIRONMENT_SECTION = `
5664
6271
  ## Environment
5665
6272
 
5666
6273
  Multi-participant chat. Messages show sender: [Name]: content.
5667
6274
  Messages prefixed with [System]: are platform updates (participant changes, contact updates, etc.) \u2014 not messages from users.
5668
- Use \`thenvoi_send_message(content, mentions)\` to respond. Plain text output is not delivered.
5669
- Mentions use handles: @<username> for users, @<username>/<agent-name> for agents.
5670
-
5671
- ## CRITICAL: Delegate When You Cannot Help Directly
6275
+ Use \`band_send_message(content, mentions)\` to respond. Plain text output is not delivered.
6276
+ Mentions use handles: @<username> for users, @<username>/<agent-name> for agents.`;
6277
+ var MULTI_AGENT_RULES_SECTION = `## CRITICAL: Delegate When You Cannot Help Directly
5672
6278
 
5673
6279
  You have NO internet access and NO real-time data. When asked about weather, news, stock prices,
5674
6280
  or any current information you cannot answer directly:
5675
6281
 
5676
- 1. Call \`thenvoi_lookup_peers()\` to find available specialized agents
5677
- 2. If a relevant agent exists, call \`thenvoi_add_participant(name)\` to add them
5678
- 3. Ask that agent using \`thenvoi_send_message(question, mentions=[agent_handle])\`
6282
+ 1. Call \`band_lookup_peers()\` to find available specialized agents
6283
+ 2. If a relevant agent exists, call \`band_add_participant(name)\` to add them
6284
+ 3. Ask that agent using \`band_send_message(question, mentions=[agent_handle])\`
5679
6285
  4. Wait for their response and relay it back to the user
5680
6286
 
5681
- NEVER say "I can't do that" without first checking if another agent can help via \`thenvoi_lookup_peers()\`.
6287
+ NEVER say "I can't do that" without first checking if another agent can help via \`band_lookup_peers()\`.
5682
6288
 
5683
6289
  ## CRITICAL: Do NOT Remove Agents Automatically
5684
6290
 
@@ -5694,48 +6300,113 @@ Only remove agents if the user explicitly requests it.
5694
6300
  When someone asks you to get information from another agent:
5695
6301
  1. Ask the other agent for the information
5696
6302
  2. When you receive the response, IMMEDIATELY relay it back to the ORIGINAL REQUESTER
5697
- 3. Do NOT just thank the helper agent - the requester is waiting for their answer!
5698
-
5699
- ## IMPORTANT: Always Share Your Thinking
6303
+ 3. Do NOT just thank the helper agent - the requester is waiting for their answer!`;
6304
+ var THINKING_SECTION = `## IMPORTANT: Always Share Your Thinking
5700
6305
 
5701
- You MUST call \`thenvoi_send_event(content, message_type="thought")\` BEFORE every action.
5702
- This is required so users can see your reasoning process.
5703
-
5704
- ## Examples
6306
+ You MUST call \`band_send_event(content, message_type="thought")\` BEFORE every action.
6307
+ This is required so users can see your reasoning process.`;
6308
+ var EXAMPLES_SECTION = `## Examples
5705
6309
 
5706
6310
  ### Simple question - answer directly
5707
6311
  [John Doe]: What's 2+2?
5708
- -> thenvoi_send_event("Simple arithmetic, answering directly.", message_type="thought")
5709
- -> thenvoi_send_message("4", mentions=["@john"])
6312
+ -> band_send_event("Simple arithmetic, answering directly.", message_type="thought")
6313
+ -> band_send_message("4", mentions=["@john"])
5710
6314
 
5711
6315
  ### User asks about weather (you cannot answer directly)
5712
6316
  [John Doe]: What's the weather in Tokyo?
5713
- -> thenvoi_send_event("I can't check weather directly. Looking for a Weather Agent.", message_type="thought")
5714
- -> thenvoi_lookup_peers()
5715
- -> thenvoi_send_event("Found Weather Agent. Adding to room.", message_type="thought")
5716
- -> thenvoi_add_participant("Weather Agent")
5717
- -> thenvoi_send_message("What's the weather in Tokyo?", mentions=["@john/weather-agent"])
6317
+ -> band_send_event("I can't check weather directly. Looking for a Weather Agent.", message_type="thought")
6318
+ -> band_lookup_peers()
6319
+ -> band_send_event("Found Weather Agent. Adding to room.", message_type="thought")
6320
+ -> band_add_participant("Weather Agent")
6321
+ -> band_send_message("What's the weather in Tokyo?", mentions=["@john/weather-agent"])
5718
6322
 
5719
6323
  [Weather Agent]: Tokyo is 15\xB0C and cloudy.
5720
- -> thenvoi_send_event("Got weather response. Relaying back to John Doe.", message_type="thought")
5721
- -> thenvoi_send_message("The weather in Tokyo is 15\xB0C and cloudy.", mentions=["@john"])
6324
+ -> band_send_event("Got weather response. Relaying back to John Doe.", message_type="thought")
6325
+ -> band_send_message("The weather in Tokyo is 15\xB0C and cloudy.", mentions=["@john"])
5722
6326
 
5723
6327
  ### No suitable agent available
5724
6328
  [John Doe]: What's the stock price of AAPL?
5725
- -> thenvoi_send_event("I can't check stock prices. Looking for a Stock Agent.", message_type="thought")
5726
- -> thenvoi_lookup_peers()
5727
- -> thenvoi_send_event("No stock agent available. Must inform user.", message_type="thought")
5728
- -> thenvoi_send_message("I don't have access to stock prices, and there's no specialized agent available to help with that.", mentions=["@john"])
6329
+ -> band_send_event("I can't check stock prices. Looking for a Stock Agent.", message_type="thought")
6330
+ -> band_lookup_peers()
6331
+ -> band_send_event("No stock agent available. Must inform user.", message_type="thought")
6332
+ -> band_send_message("I don't have access to stock prices, and there's no specialized agent available to help with that.", mentions=["@john"])
5729
6333
 
5730
6334
  ### Follow-up question in same conversation
5731
6335
  [John Doe]: What about London?
5732
- -> thenvoi_send_event("Follow-up weather question. Asking Weather Agent.", message_type="thought")
5733
- -> thenvoi_send_message("What's the weather in London?", mentions=["@john/weather-agent"])
6336
+ -> band_send_event("Follow-up weather question. Asking Weather Agent.", message_type="thought")
6337
+ -> band_send_message("What's the weather in London?", mentions=["@john/weather-agent"])
5734
6338
 
5735
6339
  [Weather Agent]: London is 8\xB0C and rainy.
5736
- -> thenvoi_send_event("Got London weather. Relaying to John Doe.", message_type="thought")
5737
- -> thenvoi_send_message("London is 8\xB0C and rainy.", mentions=["@john"])
5738
- `;
6340
+ -> band_send_event("Got London weather. Relaying to John Doe.", message_type="thought")
6341
+ -> band_send_message("London is 8\xB0C and rainy.", mentions=["@john"])`;
6342
+ var BASE_INSTRUCTIONS = [
6343
+ ENVIRONMENT_SECTION,
6344
+ MULTI_AGENT_RULES_SECTION,
6345
+ THINKING_SECTION,
6346
+ EXAMPLES_SECTION
6347
+ ].join("\n\n") + "\n";
6348
+
6349
+ // src/runtime/prompts/memory.ts
6350
+ init_memory();
6351
+ init_schemas();
6352
+ var MEMORY_INTRO = `## Memory Tools
6353
+
6354
+ You have access to memory tools for storing and retrieving information
6355
+ across conversations. Use \`band_store_memory\` to persist important
6356
+ information and \`band_list_memories\` / \`band_get_memory\` to recall it.
6357
+ Use \`band_supersede_memory\` to mark outdated memories and
6358
+ \`band_archive_memory\` to hide memories that should be preserved.`;
6359
+ function longTermPattern(label, type, segment, scope) {
6360
+ return {
6361
+ label,
6362
+ system: MEMORY_SYSTEM.long_term,
6363
+ type,
6364
+ segment,
6365
+ scope
6366
+ };
6367
+ }
6368
+ var COMMON_MEMORY_PATTERNS = [
6369
+ longTermPattern("Facts learned about a specific agent/entity", MEMORY_TYPE.semantic, MEMORY_SEGMENT.agent, MEMORY_STORE_SCOPE.subject),
6370
+ longTermPattern("Events involving a specific person/agent", MEMORY_TYPE.episodic, MEMORY_SEGMENT.agent, MEMORY_STORE_SCOPE.subject),
6371
+ longTermPattern("A user's preferences or profile info", MEMORY_TYPE.semantic, MEMORY_SEGMENT.user, MEMORY_STORE_SCOPE.subject),
6372
+ longTermPattern("Org-wide knowledge or how to perform a task", MEMORY_TYPE.procedural, MEMORY_SEGMENT.tool, MEMORY_STORE_SCOPE.organization)
6373
+ ];
6374
+ function renderMemoryPattern(pattern) {
6375
+ return `- ${pattern.label}: \`system="${pattern.system}"\`, \`type="${pattern.type}"\`, \`segment="${pattern.segment}"\`, \`scope="${pattern.scope}"\``;
6376
+ }
6377
+ var MEMORY_COMMON_PATTERNS = [
6378
+ "Common patterns:",
6379
+ ...COMMON_MEMORY_PATTERNS.map(renderMemoryPattern)
6380
+ ].join("\n");
6381
+ var MEMORY_SCOPE_GUIDANCE = `Prefer \`scope="${MEMORY_STORE_SCOPE.subject}"\` whenever the memory is about a specific person or agent, so it
6382
+ stays attached to that subject rather than leaking org-wide. Storing with \`scope="${MEMORY_STORE_SCOPE.subject}"\` requires a
6383
+ real \`subject_id\` UUID, so resolve it first via \`band_lookup_peers\` or the participant list.
6384
+ Reserve \`scope="${MEMORY_STORE_SCOPE.organization}"\` for knowledge that is genuinely shared across the whole organization and
6385
+ is not about any one subject.`;
6386
+ function quoteChoices(values) {
6387
+ return values.map((value) => `\`"${value}"\``).join(" | ");
6388
+ }
6389
+ function memoryTypeLines() {
6390
+ return Object.entries(MEMORY_SYSTEM_TYPES).map(([system, types]) => ` - ${system}: ${quoteChoices(types)}`).join("\n");
6391
+ }
6392
+ function buildMemorySection() {
6393
+ const storeMemoryProps = TOOL_MODELS.band_store_memory.properties;
6394
+ const systems = storeMemoryProps.system.enum;
6395
+ const segments = storeMemoryProps.segment.enum;
6396
+ const scopes = storeMemoryProps.scope.enum;
6397
+ const fieldRules = `When calling \`band_store_memory\`, the \`system\`, \`type\`, \`segment\`, and \`scope\` fields
6398
+ must use these exact values (case-sensitive):
6399
+
6400
+ - **system**: ${quoteChoices(systems)}
6401
+ - **type** (must match the chosen system):
6402
+ ${memoryTypeLines()}
6403
+ - **segment**: ${quoteChoices(segments)}
6404
+ - **scope**: ${quoteChoices(scopes)}`;
6405
+ return [MEMORY_INTRO, fieldRules, MEMORY_COMMON_PATTERNS, MEMORY_SCOPE_GUIDANCE].join("\n\n");
6406
+ }
6407
+ var MEMORY_SECTION = buildMemorySection();
6408
+
6409
+ // src/runtime/prompts/templates.ts
5739
6410
  var TEMPLATES = {
5740
6411
  default: `You are {agent_name}, {agent_description}.
5741
6412
 
@@ -5754,11 +6425,20 @@ ${customSection}`.trim();
5754
6425
  }
5755
6426
  const template = options?.template ?? "default";
5756
6427
  const templateString = TEMPLATES[template] ?? TEMPLATES.default;
5757
- return templateString.replaceAll("{agent_name}", agentName).replaceAll("{agent_description}", agentDescription).replaceAll("{custom_section}", customSection);
6428
+ const parts = [
6429
+ templateString.replaceAll("{agent_name}", agentName).replaceAll("{agent_description}", agentDescription).replaceAll("{custom_section}", customSection)
6430
+ ];
6431
+ if (options?.capabilities?.memory) {
6432
+ parts.push(MEMORY_SECTION);
6433
+ }
6434
+ return parts.join("\n\n");
5758
6435
  }
5759
6436
 
6437
+ // src/adapters/codex/CodexAdapter.ts
6438
+ init_schemas();
6439
+
5760
6440
  // src/runtime/tools/customTools.ts
5761
- var import_zod2 = require("zod");
6441
+ var import_zod3 = require("zod");
5762
6442
  var import_zod_to_json_schema = require("zod-to-json-schema");
5763
6443
  var CustomToolDefinitionError = class extends Error {
5764
6444
  constructor(message) {
@@ -6180,8 +6860,8 @@ var CODEX_WEB_SEARCH_MODES = ["disabled", "cached", "live"];
6180
6860
  var CODEX_REASONING_SUMMARIES = ["auto", "concise", "detailed", "none"];
6181
6861
  var CODEX_REASONING_EFFORTS = ["low", "medium", "high", "xhigh"];
6182
6862
  var SILENT_REPORTING_TOOLS = /* @__PURE__ */ new Set([
6183
- "thenvoi_send_message",
6184
- "thenvoi_send_event"
6863
+ SEND_MESSAGE_TOOL_NAME,
6864
+ SEND_EVENT_TOOL_NAME
6185
6865
  ]);
6186
6866
  var CodexAdapter = class extends SimpleAdapter {
6187
6867
  baseConfig;
@@ -6211,8 +6891,8 @@ var CodexAdapter = class extends SimpleAdapter {
6211
6891
  includeBaseInstructions: true,
6212
6892
  experimentalApi: true,
6213
6893
  fallbackSendAgentText: true,
6214
- clientName: "thenvoi_codex_adapter",
6215
- clientTitle: "Thenvoi Codex Adapter",
6894
+ clientName: "band_codex_adapter",
6895
+ clientTitle: "Band Codex Adapter",
6216
6896
  clientVersion: "0.1.0",
6217
6897
  turnTimeoutMs: 18e4,
6218
6898
  ...options?.config
@@ -6453,7 +7133,8 @@ var CodexAdapter = class extends SimpleAdapter {
6453
7133
  agentName: this.agentName,
6454
7134
  agentDescription: this.agentDescription,
6455
7135
  customSection,
6456
- includeBaseInstructions: this.baseConfig.includeBaseInstructions ?? true
7136
+ includeBaseInstructions: this.baseConfig.includeBaseInstructions ?? true,
7137
+ capabilities: { memory: this.includeMemoryTools }
6457
7138
  }).trim();
6458
7139
  this.systemPrompt = prompt.length > 0 ? prompt : null;
6459
7140
  }
@@ -6477,8 +7158,8 @@ var CodexAdapter = class extends SimpleAdapter {
6477
7158
  await client.connect();
6478
7159
  await client.initialize({
6479
7160
  clientInfo: {
6480
- name: this.baseConfig.clientName ?? "thenvoi_codex_adapter",
6481
- title: this.baseConfig.clientTitle ?? "Thenvoi Codex Adapter",
7161
+ name: this.baseConfig.clientName ?? "band_codex_adapter",
7162
+ title: this.baseConfig.clientTitle ?? "Band Codex Adapter",
6482
7163
  version: this.baseConfig.clientVersion ?? "0.1.0"
6483
7164
  },
6484
7165
  capabilities: {
@@ -6751,7 +7432,7 @@ var CodexAdapter = class extends SimpleAdapter {
6751
7432
  tool_call_id: callId
6752
7433
  }), "tool_result");
6753
7434
  }
6754
- return !isError && toolName === "thenvoi_send_message";
7435
+ return !isError && toolName === SEND_MESSAGE_TOOL_NAME;
6755
7436
  } catch (error) {
6756
7437
  const output = {
6757
7438
  ok: false,
@@ -8476,7 +9157,7 @@ var GoogleADKHistoryConverter = class {
8476
9157
  };
8477
9158
 
8478
9159
  // src/adapters/google-adk/GoogleADKAdapter.ts
8479
- var APP_NAME = "thenvoi";
9160
+ var APP_NAME = "band";
8480
9161
  var DEFAULT_MAX_HISTORY_MESSAGES = 50;
8481
9162
  var DEFAULT_MAX_TRANSCRIPT_CHARS = 1e5;
8482
9163
  var MAX_TOOL_OUTPUT_PREVIEW = 200;
@@ -8576,7 +9257,8 @@ var GoogleADKAdapter = class extends SimpleAdapter {
8576
9257
  this.systemPrompt = this.systemPromptOverride ?? renderSystemPrompt({
8577
9258
  agentName,
8578
9259
  agentDescription,
8579
- customSection: this.customSection
9260
+ customSection: this.customSection,
9261
+ capabilities: { memory: this.enableMemoryTools }
8580
9262
  });
8581
9263
  }
8582
9264
  async onMessage(message, tools, history, participantsMessage, contactsMessage, context) {
@@ -8649,7 +9331,7 @@ var GoogleADKAdapter = class extends SimpleAdapter {
8649
9331
  }
8650
9332
  buildAgent(sdk, tools) {
8651
9333
  return sdk.createAgent({
8652
- name: this.agentName || "thenvoi_agent",
9334
+ name: this.agentName || "band_agent",
8653
9335
  model: this.model,
8654
9336
  instruction: this.systemPrompt,
8655
9337
  tools: this.buildTools(sdk, tools)
@@ -8816,7 +9498,8 @@ var LangGraphAdapter = class extends SimpleAdapter {
8816
9498
  this.renderedSystemPrompt = this.systemPromptOverride ?? renderSystemPrompt({
8817
9499
  agentName,
8818
9500
  agentDescription,
8819
- customSection: this.customSection
9501
+ customSection: this.customSection,
9502
+ capabilities: { memory: this.includeMemoryTools }
8820
9503
  });
8821
9504
  }
8822
9505
  async onMessage(message, tools, history, participantsMessage, contactsMessage, context) {
@@ -8835,14 +9518,17 @@ var LangGraphAdapter = class extends SimpleAdapter {
8835
9518
  ];
8836
9519
  }
8837
9520
  const graph = await this.resolveGraph(sdk, langGraphTools);
8838
- const messages = this.buildMessages(
8839
- history,
8840
- message,
8841
- participantsMessage,
8842
- contactsMessage,
8843
- context.isSessionBootstrap,
8844
- context.roomId
8845
- );
9521
+ const usesCheckpointer = Boolean(this.checkpointer);
9522
+ const isFirstBootstrap = context.isSessionBootstrap && !this.bootstrappedRooms.has(context.roomId);
9523
+ const replayHistory = !usesCheckpointer || isFirstBootstrap;
9524
+ const includeSystemPrompt = this.usesCustomGraph && (!usesCheckpointer || isFirstBootstrap);
9525
+ if (context.isSessionBootstrap) {
9526
+ this.bootstrappedRooms.add(context.roomId);
9527
+ }
9528
+ const messages = this.buildMessages(history, message, participantsMessage, contactsMessage, {
9529
+ replayHistory,
9530
+ includeSystemPrompt
9531
+ });
8846
9532
  const input = { messages };
8847
9533
  const graphConfig = {
8848
9534
  configurable: {
@@ -8869,6 +9555,10 @@ var LangGraphAdapter = class extends SimpleAdapter {
8869
9555
  async onCleanup(roomId) {
8870
9556
  this.bootstrappedRooms.delete(roomId);
8871
9557
  }
9558
+ /** A caller-supplied graph rather than the built-in createReactAgent. */
9559
+ get usesCustomGraph() {
9560
+ return Boolean(this.graph || this.graphFactory);
9561
+ }
8872
9562
  resolveGraph(sdk, langGraphTools) {
8873
9563
  if (this.graphFactory) {
8874
9564
  return Promise.resolve(this.graphFactory(langGraphTools));
@@ -8889,14 +9579,13 @@ var LangGraphAdapter = class extends SimpleAdapter {
8889
9579
  prompt: this.renderedSystemPrompt
8890
9580
  }));
8891
9581
  }
8892
- buildMessages(history, message, participantsMessage, contactsMessage, isSessionBootstrap, roomId) {
9582
+ buildMessages(history, message, participantsMessage, contactsMessage, options) {
8893
9583
  const messages = [];
8894
- if (isSessionBootstrap && !this.bootstrappedRooms.has(roomId)) {
9584
+ if (options.includeSystemPrompt) {
8895
9585
  messages.push(["system", this.renderedSystemPrompt]);
8896
- this.bootstrappedRooms.add(roomId);
8897
9586
  }
8898
- if (isSessionBootstrap && history.length > 0) {
8899
- const historical = history.raw.slice(-this.maxHistoryMessages);
9587
+ if (options.replayHistory && history.length > 0) {
9588
+ const historical = history.raw.filter((item) => item.id !== message.id).slice(-this.maxHistoryMessages);
8900
9589
  for (const item of historical) {
8901
9590
  const role = String(item.sender_type ?? "") === "Agent" ? "assistant" : "user";
8902
9591
  const content = String(item.content ?? "");
@@ -8915,7 +9604,7 @@ var LangGraphAdapter = class extends SimpleAdapter {
8915
9604
  return messages;
8916
9605
  }
8917
9606
  async forwardStreamEvents(graph, input, config, tools) {
8918
- const stream = graph.streamEvents?.(input, config, { version: "v2" });
9607
+ const stream = graph.streamEvents?.(input, { ...config, version: "v2" });
8919
9608
  if (!stream) {
8920
9609
  return null;
8921
9610
  }
@@ -8942,6 +9631,10 @@ var LangGraphAdapter = class extends SimpleAdapter {
8942
9631
  );
8943
9632
  }
8944
9633
  if (eventType === "on_chain_end") {
9634
+ const chainName = String(data.name ?? "");
9635
+ if (!this.usesCustomGraph && chainName !== "LangGraph" && chainName !== "agent") {
9636
+ continue;
9637
+ }
8945
9638
  const output = asOptionalRecord(data.data) ?? {};
8946
9639
  const text = extractAssistantText(output.output);
8947
9640
  if (text) {
@@ -9003,18 +9696,25 @@ function stringifyToolResult2(value, logger, toolName) {
9003
9696
  });
9004
9697
  }
9005
9698
  function extractAssistantText(result) {
9006
- if (typeof result === "string" && result.trim().length > 0) {
9007
- return result.trim();
9699
+ if (typeof result === "string") {
9700
+ const trimmed = result.trim();
9701
+ return trimmed.length > 0 && !isInternalLangGraphMarker(trimmed) ? trimmed : null;
9008
9702
  }
9009
9703
  const record = asOptionalRecord(result) ?? {};
9704
+ if (isLangChainAssistantMessage(record)) {
9705
+ const text = asLangChainMessageContent(record);
9706
+ if (text && !isInternalLangGraphMarker(text)) {
9707
+ return text;
9708
+ }
9709
+ }
9010
9710
  const directContent = asMessageContent(record.content);
9011
- if (directContent) {
9711
+ if (directContent && !isInternalLangGraphMarker(directContent)) {
9012
9712
  return directContent;
9013
9713
  }
9014
9714
  const messages = Array.isArray(record.messages) ? record.messages : [];
9015
9715
  for (let index = messages.length - 1; index >= 0; index -= 1) {
9016
9716
  const text = asMessageText(messages[index]);
9017
- if (text) {
9717
+ if (text && !isInternalLangGraphMarker(text)) {
9018
9718
  return text;
9019
9719
  }
9020
9720
  }
@@ -9031,12 +9731,40 @@ function asMessageText(value) {
9031
9731
  if (!record) {
9032
9732
  return null;
9033
9733
  }
9734
+ if (isLangChainAssistantMessage(record)) {
9735
+ return asLangChainMessageContent(record);
9736
+ }
9034
9737
  const role = String(record.role ?? "");
9035
9738
  if (role !== "assistant") {
9036
9739
  return null;
9037
9740
  }
9038
9741
  return asMessageContent(record.content);
9039
9742
  }
9743
+ function isLangChainAssistantMessage(record) {
9744
+ const id = record.id;
9745
+ if (Array.isArray(id)) {
9746
+ const typeName = String(id[id.length - 1] ?? "");
9747
+ if (typeName === "AIMessage" || typeName === "AIMessageChunk") {
9748
+ return true;
9749
+ }
9750
+ }
9751
+ const getType = record.getType;
9752
+ if (typeof getType === "function") {
9753
+ try {
9754
+ return getType.call(record) === "ai";
9755
+ } catch {
9756
+ return false;
9757
+ }
9758
+ }
9759
+ return false;
9760
+ }
9761
+ function asLangChainMessageContent(record) {
9762
+ const kwargs = asOptionalRecord(record.kwargs);
9763
+ return asMessageContent(kwargs?.content ?? record.content);
9764
+ }
9765
+ function isInternalLangGraphMarker(text) {
9766
+ return text === "__end__" || text === "__start__";
9767
+ }
9040
9768
  function asMessageContent(value) {
9041
9769
  if (typeof value === "string" && value.trim().length > 0) {
9042
9770
  return value.trim();
@@ -9966,7 +10694,7 @@ function buildAgentCard(peer, gatewayUrl) {
9966
10694
  id: "default",
9967
10695
  name: peer.name,
9968
10696
  description: peer.description,
9969
- tags: ["thenvoi", "gateway"]
10697
+ tags: ["band", "gateway"]
9970
10698
  }
9971
10699
  ],
9972
10700
  defaultInputModes: ["text/plain"],
@@ -10148,7 +10876,7 @@ var DEFAULT_RESPONSE_TIMEOUT_MS = 12e4;
10148
10876
  var DEFAULT_PEER_PAGE_SIZE = 100;
10149
10877
  var DEFAULT_MAX_PEER_PAGES = 25;
10150
10878
  var A2AGatewayAdapter = class extends SimpleAdapter {
10151
- thenvoiRest;
10879
+ bandRest;
10152
10880
  gatewayUrl;
10153
10881
  host;
10154
10882
  port;
@@ -10169,7 +10897,7 @@ var A2AGatewayAdapter = class extends SimpleAdapter {
10169
10897
  super({
10170
10898
  historyConverter: new GatewayHistoryConverter()
10171
10899
  });
10172
- this.thenvoiRest = options.thenvoiRest;
10900
+ this.bandRest = options.bandRest;
10173
10901
  this.gatewayUrl = options.gatewayUrl ?? DEFAULT_GATEWAY_URL;
10174
10902
  this.host = options.host ?? DEFAULT_HOST;
10175
10903
  this.port = options.port ?? DEFAULT_PORT;
@@ -10249,13 +10977,13 @@ var A2AGatewayAdapter = class extends SimpleAdapter {
10249
10977
  async refreshPeers() {
10250
10978
  this.peersBySlug.clear();
10251
10979
  this.peersById.clear();
10252
- if (!this.thenvoiRest.listPeers) {
10980
+ if (!this.bandRest.listPeers) {
10253
10981
  throw new UnsupportedFeatureError(
10254
10982
  "Peer listing is not available in current REST adapter"
10255
10983
  );
10256
10984
  }
10257
10985
  for (let page = 1; page <= this.maxPeerPages; page += 1) {
10258
- const response = await this.thenvoiRest.listPeers({
10986
+ const response = await this.bandRest.listPeers({
10259
10987
  page,
10260
10988
  pageSize: this.peerPageSize,
10261
10989
  notInChat: ""
@@ -10325,7 +11053,7 @@ var A2AGatewayAdapter = class extends SimpleAdapter {
10325
11053
  try {
10326
11054
  await this.emitContextEvent(roomId, contextId);
10327
11055
  const content = extractMessageText2(request.message) ?? "";
10328
- await this.thenvoiRest.createChatMessage(roomId, {
11056
+ await this.bandRest.createChatMessage(roomId, {
10329
11057
  content: buildMentionedContent(peer.name, content),
10330
11058
  mentions: buildMentions(peer),
10331
11059
  metadata: {
@@ -10356,7 +11084,7 @@ var A2AGatewayAdapter = class extends SimpleAdapter {
10356
11084
  contextId: pending.contextId,
10357
11085
  state: "failed",
10358
11086
  final: true,
10359
- text: "Timed out waiting for a Thenvoi peer response."
11087
+ text: "Timed out waiting for a Band peer response."
10360
11088
  });
10361
11089
  return;
10362
11090
  }
@@ -10443,7 +11171,7 @@ var A2AGatewayAdapter = class extends SimpleAdapter {
10443
11171
  if (existingRoom) {
10444
11172
  const participants = this.roomParticipants.get(existingRoom) ?? /* @__PURE__ */ new Set();
10445
11173
  if (!participants.has(targetPeerId)) {
10446
- await this.thenvoiRest.addChatParticipant(existingRoom, {
11174
+ await this.bandRest.addChatParticipant(existingRoom, {
10447
11175
  participantId: targetPeerId,
10448
11176
  role: "member"
10449
11177
  });
@@ -10453,11 +11181,11 @@ var A2AGatewayAdapter = class extends SimpleAdapter {
10453
11181
  return [existingRoom, contextId];
10454
11182
  }
10455
11183
  const normalizedContextId = contextId && contextId.length > 0 ? contextId : (0, import_node_crypto5.randomUUID)();
10456
- const created = await this.thenvoiRest.createChat(
11184
+ const created = await this.bandRest.createChat(
10457
11185
  `a2a:gateway:${normalizedContextId}`
10458
11186
  );
10459
11187
  const roomId = created.id;
10460
- await this.thenvoiRest.addChatParticipant(roomId, {
11188
+ await this.bandRest.addChatParticipant(roomId, {
10461
11189
  participantId: targetPeerId,
10462
11190
  role: "member"
10463
11191
  });
@@ -10466,7 +11194,7 @@ var A2AGatewayAdapter = class extends SimpleAdapter {
10466
11194
  return [roomId, normalizedContextId];
10467
11195
  }
10468
11196
  async emitContextEvent(roomId, contextId) {
10469
- await this.thenvoiRest.createChatEvent(roomId, {
11197
+ await this.bandRest.createChatEvent(roomId, {
10470
11198
  content: "A2A gateway context",
10471
11199
  messageType: "task",
10472
11200
  metadata: {
@@ -10644,10 +11372,10 @@ function toStatusUpdateEvent(message, taskId, contextId) {
10644
11372
  final,
10645
11373
  text: message.content,
10646
11374
  metadata: {
10647
- thenvoi_message_id: message.id,
10648
- thenvoi_message_type: message.messageType,
10649
- thenvoi_sender_id: message.senderId,
10650
- thenvoi_room_id: message.roomId
11375
+ band_message_id: message.id,
11376
+ band_message_type: message.messageType,
11377
+ band_sender_id: message.senderId,
11378
+ band_room_id: message.roomId
10651
11379
  }
10652
11380
  });
10653
11381
  }
@@ -10817,8 +11545,8 @@ var ParlantAdapter = class extends SimpleAdapter {
10817
11545
  message: userMessage,
10818
11546
  moderation: "none",
10819
11547
  metadata: {
10820
- thenvoi_source: "thenvoi-sdk-typescript",
10821
- thenvoi_room_id: context.roomId
11548
+ band_source: "band-sdk-typescript",
11549
+ band_room_id: context.roomId
10822
11550
  }
10823
11551
  },
10824
11552
  this.requestOptions()
@@ -10893,9 +11621,9 @@ var ParlantAdapter = class extends SimpleAdapter {
10893
11621
  {
10894
11622
  agentId: this.agentId,
10895
11623
  customerId,
10896
- title: `Thenvoi Room ${roomId.slice(0, 8)}`,
11624
+ title: `Band Room ${roomId.slice(0, 8)}`,
10897
11625
  metadata: {
10898
- thenvoi_room_id: roomId
11626
+ band_room_id: roomId
10899
11627
  }
10900
11628
  },
10901
11629
  this.requestOptions()
@@ -10908,7 +11636,7 @@ var ParlantAdapter = class extends SimpleAdapter {
10908
11636
  source: "system",
10909
11637
  message: this.systemPrompt,
10910
11638
  metadata: {
10911
- thenvoi_system_prompt: true
11639
+ band_system_prompt: true
10912
11640
  }
10913
11641
  },
10914
11642
  this.requestOptions()
@@ -10962,7 +11690,7 @@ var ParlantAdapter = class extends SimpleAdapter {
10962
11690
  {
10963
11691
  name: customerName,
10964
11692
  metadata: {
10965
- thenvoi_room_id: roomId
11693
+ band_room_id: roomId
10966
11694
  }
10967
11695
  },
10968
11696
  this.requestOptions()
@@ -11512,7 +12240,7 @@ var LettaAdapter = class extends SimpleAdapter {
11512
12240
  const agent = await this.raceTimeout(
11513
12241
  (s) => client.agents.create(
11514
12242
  {
11515
- name: `thenvoi-room-${roomId.slice(0, 16)}`,
12243
+ name: `band-room-${roomId.slice(0, 16)}`,
11516
12244
  model: this.model,
11517
12245
  ...this.embedding ? { embedding: this.embedding } : {},
11518
12246
  system: this.systemPrompt || void 0,
@@ -11926,10 +12654,31 @@ async function loadLettaClientFactory(config) {
11926
12654
  init_schemas();
11927
12655
  init_registrations();
11928
12656
 
12657
+ // src/mcp/auth.ts
12658
+ var import_node_crypto6 = require("crypto");
12659
+ function generateAuthToken() {
12660
+ return (0, import_node_crypto6.randomBytes)(32).toString("hex");
12661
+ }
12662
+ function isAuthorizedRequest(authorizationHeader, authToken) {
12663
+ if (!authToken) {
12664
+ return true;
12665
+ }
12666
+ if (!authorizationHeader) {
12667
+ return false;
12668
+ }
12669
+ const expected = Buffer.from(`Bearer ${authToken}`);
12670
+ const actual = Buffer.from(authorizationHeader);
12671
+ if (expected.length !== actual.length) {
12672
+ return false;
12673
+ }
12674
+ return (0, import_node_crypto6.timingSafeEqual)(expected, actual);
12675
+ }
12676
+
11929
12677
  // src/mcp/stdio.ts
11930
12678
  init_registrations();
11931
12679
  init_zod();
11932
- var ThenvoiMcpStdioServer = class {
12680
+ init_schemas();
12681
+ var BandMcpStdioServer = class {
11933
12682
  options;
11934
12683
  registrations;
11935
12684
  mcpServer = null;
@@ -11956,12 +12705,12 @@ var ThenvoiMcpStdioServer = class {
11956
12705
  }
11957
12706
  const { McpServer } = await import("@modelcontextprotocol/sdk/server/mcp.js");
11958
12707
  const { StdioServerTransport } = await import("@modelcontextprotocol/sdk/server/stdio.js");
11959
- const { z: z5 } = await import("zod");
12708
+ const { z: z6 } = await import("zod");
11960
12709
  const mcpServer = new McpServer({
11961
- name: this.options.name ?? "thenvoi",
12710
+ name: this.options.name ?? MCP_SERVER_NAME,
11962
12711
  version: "1.0.0"
11963
12712
  });
11964
- registerTools(mcpServer, z5, this.registrations);
12713
+ registerTools(mcpServer, z6, this.registrations);
11965
12714
  const transport = new StdioServerTransport(this.options.stdin, this.options.stdout);
11966
12715
  await mcpServer.connect(transport);
11967
12716
  this.mcpServer = mcpServer;
@@ -11973,14 +12722,14 @@ var ThenvoiMcpStdioServer = class {
11973
12722
  this.mcpServer = null;
11974
12723
  }
11975
12724
  };
11976
- function registerTools(mcpServer, z5, registrations) {
12725
+ function registerTools(mcpServer, z6, registrations) {
11977
12726
  for (const reg of registrations) {
11978
- const zodShape = buildZodShape(z5, reg.inputSchema.properties, new Set(reg.inputSchema.required));
12727
+ const zodShape = buildZodShape(z6, reg.inputSchema.properties, new Set(reg.inputSchema.required));
11979
12728
  mcpServer.registerTool(
11980
12729
  reg.name,
11981
12730
  {
11982
12731
  description: reg.description,
11983
- inputSchema: z5.object(zodShape)
12732
+ inputSchema: z6.object(zodShape)
11984
12733
  },
11985
12734
  // eslint-disable-next-line @typescript-eslint/no-explicit-any -- MCP SDK handler signature is complex; our McpToolResult is compatible
11986
12735
  async (args) => reg.execute(args)
@@ -11991,12 +12740,13 @@ function registerTools(mcpServer, z5, registrations) {
11991
12740
  // src/mcp/server.ts
11992
12741
  init_registrations();
11993
12742
  init_zod();
12743
+ init_schemas();
11994
12744
  var PORT_RANGE_START = 5e4;
11995
12745
  var PORT_RANGE_END = 6e4;
11996
12746
  var MCP_SERVER_VERSION = "1.0.0";
11997
12747
  var SESSION_IDLE_TTL_MS = 15 * 60 * 1e3;
11998
12748
  var SESSION_SWEEP_INTERVAL_MS = 60 * 1e3;
11999
- var ThenvoiMcpServer = class {
12749
+ var BandMcpServer = class {
12000
12750
  options;
12001
12751
  registrations;
12002
12752
  httpServer = null;
@@ -12039,8 +12789,8 @@ var ThenvoiMcpServer = class {
12039
12789
  const express = expressModule.default ?? expressModule;
12040
12790
  const http = await import("http");
12041
12791
  const { randomUUID: randomUUID6 } = await import("crypto");
12042
- const { z: z5 } = await import("zod");
12043
- const serverName = this.options.name ?? "thenvoi";
12792
+ const { z: z6 } = await import("zod");
12793
+ const serverName = this.options.name ?? MCP_SERVER_NAME;
12044
12794
  const app = express();
12045
12795
  app.use(express.json());
12046
12796
  app.get("/healthz", (_req, res) => {
@@ -12048,6 +12798,10 @@ var ThenvoiMcpServer = class {
12048
12798
  });
12049
12799
  const handleMcpRequest = async (req, res) => {
12050
12800
  try {
12801
+ if (!isAuthorizedRequest(req.headers.authorization, this.options.authToken)) {
12802
+ sendMcpError(res, 401, "Unauthorized");
12803
+ return;
12804
+ }
12051
12805
  const sessionId = getSessionIdHeader(req.headers["mcp-session-id"]);
12052
12806
  let session = sessionId ? this.sessions.get(sessionId) : void 0;
12053
12807
  if (!session) {
@@ -12060,7 +12814,7 @@ var ThenvoiMcpServer = class {
12060
12814
  StreamableHTTPServerTransport,
12061
12815
  serverName,
12062
12816
  sessionIdGenerator: randomUUID6,
12063
- z: z5
12817
+ z: z6
12064
12818
  });
12065
12819
  }
12066
12820
  session.lastSeenAt = Date.now();
@@ -12186,14 +12940,14 @@ function sendMcpError(res, status, message) {
12186
12940
  id: null
12187
12941
  });
12188
12942
  }
12189
- function registerTools2(mcpServer, z5, registrations) {
12943
+ function registerTools2(mcpServer, z6, registrations) {
12190
12944
  for (const reg of registrations) {
12191
- const zodShape = buildZodShape(z5, reg.inputSchema.properties, new Set(reg.inputSchema.required));
12945
+ const zodShape = buildZodShape(z6, reg.inputSchema.properties, new Set(reg.inputSchema.required));
12192
12946
  mcpServer.registerTool(
12193
12947
  reg.name,
12194
12948
  {
12195
12949
  description: reg.description,
12196
- inputSchema: z5.object(zodShape)
12950
+ inputSchema: z6.object(zodShape)
12197
12951
  },
12198
12952
  // eslint-disable-next-line @typescript-eslint/no-explicit-any -- MCP SDK handler signature is complex; our McpToolResult is compatible
12199
12953
  async (args) => reg.execute(args)
@@ -12223,11 +12977,12 @@ function checkPort(http, port) {
12223
12977
  // src/mcp/sse.ts
12224
12978
  init_registrations();
12225
12979
  init_zod();
12980
+ init_schemas();
12226
12981
  var PORT_RANGE_START2 = 5e4;
12227
12982
  var PORT_RANGE_END2 = 6e4;
12228
12983
  var SESSION_IDLE_TTL_MS2 = 15 * 60 * 1e3;
12229
12984
  var SESSION_SWEEP_INTERVAL_MS2 = 60 * 1e3;
12230
- var ThenvoiMcpSseServer = class {
12985
+ var BandMcpSseServer = class {
12231
12986
  options;
12232
12987
  registrations;
12233
12988
  httpServer = null;
@@ -12268,19 +13023,23 @@ var ThenvoiMcpSseServer = class {
12268
13023
  const expressModule = await import("express");
12269
13024
  const express = expressModule.default ?? expressModule;
12270
13025
  const http = await import("http");
12271
- const { z: z5 } = await import("zod");
13026
+ const { z: z6 } = await import("zod");
12272
13027
  const app = express();
12273
13028
  app.use(express.json());
12274
13029
  app.get("/healthz", (_req, res) => {
12275
13030
  res.json({ status: "ok" });
12276
13031
  });
12277
- app.get("/sse", async (_req, res) => {
13032
+ app.get("/sse", async (req, res) => {
13033
+ if (!isAuthorizedRequest(req.headers.authorization, this.options.authToken)) {
13034
+ res.status(401).send("Unauthorized");
13035
+ return;
13036
+ }
12278
13037
  const transport = new SSEServerTransport("/messages", res);
12279
13038
  const mcpServer = new McpServer({
12280
- name: this.options.name ?? "thenvoi",
13039
+ name: this.options.name ?? MCP_SERVER_NAME,
12281
13040
  version: "1.0.0"
12282
13041
  });
12283
- registerTools3(mcpServer, z5, this.registrations);
13042
+ registerTools3(mcpServer, z6, this.registrations);
12284
13043
  await mcpServer.connect(transport);
12285
13044
  const sessionId = transport.sessionId;
12286
13045
  this.sessions.set(sessionId, {
@@ -12299,6 +13058,10 @@ var ThenvoiMcpSseServer = class {
12299
13058
  });
12300
13059
  });
12301
13060
  app.post("/messages", async (req, res) => {
13061
+ if (!isAuthorizedRequest(req.headers.authorization, this.options.authToken)) {
13062
+ res.status(401).send("Unauthorized");
13063
+ return;
13064
+ }
12302
13065
  const sessionId = typeof req.query.sessionId === "string" ? req.query.sessionId : null;
12303
13066
  if (!sessionId) {
12304
13067
  res.status(400).send("Missing sessionId");
@@ -12372,10 +13135,10 @@ var ThenvoiMcpSseServer = class {
12372
13135
  }
12373
13136
  }
12374
13137
  };
12375
- function registerTools3(mcpServer, z5, registrations) {
13138
+ function registerTools3(mcpServer, z6, registrations) {
12376
13139
  for (const registration of registrations) {
12377
13140
  const zodShape = buildZodShape(
12378
- z5,
13141
+ z6,
12379
13142
  registration.inputSchema.properties,
12380
13143
  new Set(registration.inputSchema.required)
12381
13144
  );
@@ -12383,7 +13146,7 @@ function registerTools3(mcpServer, z5, registrations) {
12383
13146
  registration.name,
12384
13147
  {
12385
13148
  description: registration.description,
12386
- inputSchema: z5.object(zodShape)
13149
+ inputSchema: z6.object(zodShape)
12387
13150
  },
12388
13151
  // eslint-disable-next-line @typescript-eslint/no-explicit-any -- MCP SDK handler signature is complex; our McpToolResult is compatible
12389
13152
  async (args) => registration.execute(args)
@@ -12412,21 +13175,18 @@ function checkPort2(http, port) {
12412
13175
  }
12413
13176
 
12414
13177
  // src/mcp/backends.ts
12415
- async function createThenvoiMcpBackend(options) {
12416
- const registrations = buildRoomScopedRegistrations(
12417
- options.getToolsForRoom,
12418
- {
12419
- enableMemoryTools: options.enableMemoryTools,
12420
- enableContactTools: true,
12421
- additionalTools: options.additionalTools
12422
- }
12423
- );
12424
- const allowedTools = mcpToolNames(new Set(registrations.map((registration) => registration.name)));
13178
+ async function createBandMcpBackend(options) {
13179
+ const registrationOptions = {
13180
+ enableMemoryTools: options.enableMemoryTools,
13181
+ enableContactTools: true,
13182
+ additionalTools: options.additionalTools
13183
+ };
12425
13184
  if (options.kind === "sdk") {
12426
- const { createThenvoiSdkMcpServer: createThenvoiSdkMcpServer2 } = await Promise.resolve().then(() => (init_sdk(), sdk_exports));
12427
- const server2 = createThenvoiSdkMcpServer2({
12428
- enableMemoryTools: options.enableMemoryTools,
13185
+ const { createBandSdkMcpServer: createBandSdkMcpServer2 } = await Promise.resolve().then(() => (init_sdk(), sdk_exports));
13186
+ const server2 = createBandSdkMcpServer2({
12429
13187
  getToolsForRoom: options.getToolsForRoom,
13188
+ multiRoom: options.multiRoom,
13189
+ enableMemoryTools: options.enableMemoryTools,
12430
13190
  additionalTools: options.additionalTools
12431
13191
  });
12432
13192
  return {
@@ -12436,9 +13196,12 @@ async function createThenvoiMcpBackend(options) {
12436
13196
  stop: async () => void 0
12437
13197
  };
12438
13198
  }
13199
+ const resolvedTools = options.multiRoom === false ? resolveSingleRoomTools(options.getToolsForRoom) : options.getToolsForRoom;
13200
+ const registrations = options.multiRoom === false ? buildSingleContextRegistrations(resolvedTools, registrationOptions) : buildRoomScopedRegistrations(resolvedTools, registrationOptions);
13201
+ const allowedTools = mcpToolNames(new Set(registrations.map((registration) => registration.name)));
12439
13202
  if (options.kind === "stdio") {
12440
- const server2 = new ThenvoiMcpStdioServer({
12441
- tools: options.getToolsForRoom,
13203
+ const server2 = new BandMcpStdioServer({
13204
+ tools: resolvedTools,
12442
13205
  enableMemoryTools: options.enableMemoryTools,
12443
13206
  enableContactTools: true,
12444
13207
  additionalTools: options.additionalTools
@@ -12453,34 +13216,39 @@ async function createThenvoiMcpBackend(options) {
12453
13216
  }
12454
13217
  };
12455
13218
  }
13219
+ const authToken = generateAuthToken();
12456
13220
  if (options.kind === "sse") {
12457
- const server2 = new ThenvoiMcpSseServer({
12458
- tools: options.getToolsForRoom,
13221
+ const server2 = new BandMcpSseServer({
13222
+ tools: resolvedTools,
12459
13223
  enableMemoryTools: options.enableMemoryTools,
12460
13224
  enableContactTools: true,
12461
- additionalTools: options.additionalTools
13225
+ additionalTools: options.additionalTools,
13226
+ authToken
12462
13227
  });
12463
13228
  await server2.start();
12464
13229
  return {
12465
13230
  kind: "sse",
12466
13231
  server: server2,
12467
13232
  allowedTools,
13233
+ authToken,
12468
13234
  stop: async () => {
12469
13235
  await server2.stop();
12470
13236
  }
12471
13237
  };
12472
13238
  }
12473
- const server = new ThenvoiMcpServer({
12474
- tools: options.getToolsForRoom,
13239
+ const server = new BandMcpServer({
13240
+ tools: resolvedTools,
12475
13241
  enableMemoryTools: options.enableMemoryTools,
12476
13242
  enableContactTools: true,
12477
- additionalTools: options.additionalTools
13243
+ additionalTools: options.additionalTools,
13244
+ authToken
12478
13245
  });
12479
13246
  await server.start();
12480
13247
  return {
12481
13248
  kind: "http",
12482
13249
  server,
12483
13250
  allowedTools,
13251
+ authToken,
12484
13252
  stop: async () => {
12485
13253
  await server.stop();
12486
13254
  }
@@ -12489,6 +13257,7 @@ async function createThenvoiMcpBackend(options) {
12489
13257
 
12490
13258
  // src/adapters/opencode/OpencodeAdapter.ts
12491
13259
  init_registrations();
13260
+ init_schemas();
12492
13261
 
12493
13262
  // src/converters/opencode.ts
12494
13263
  var OpencodeHistoryConverter = class {
@@ -12622,7 +13391,7 @@ var SdkOpencodeClientBase = class {
12622
13391
  const result = await runtime.client.mcp.add({
12623
13392
  ...this.scope(),
12624
13393
  name: input.name,
12625
- config: { type: "remote", url: input.url }
13394
+ config: { type: "remote", url: input.url, headers: input.headers }
12626
13395
  });
12627
13396
  return expectRecord(result);
12628
13397
  }
@@ -12710,7 +13479,7 @@ var SdkOpencodeClientBase = class {
12710
13479
  }
12711
13480
  const response = await fetch(url, { method: "DELETE" });
12712
13481
  if (!response.ok && response.status !== 404) {
12713
- throw new HttpStatusError(response.status, await readResponseBody(response));
13482
+ throw new HttpStatusError(response.status, await readResponseBody2(response));
12714
13483
  }
12715
13484
  }
12716
13485
  };
@@ -12799,7 +13568,7 @@ async function findAvailablePort3() {
12799
13568
  });
12800
13569
  });
12801
13570
  }
12802
- async function readResponseBody(response) {
13571
+ async function readResponseBody2(response) {
12803
13572
  const contentType = response.headers.get("content-type") ?? "";
12804
13573
  if (contentType.includes("application/json")) {
12805
13574
  try {
@@ -12813,8 +13582,8 @@ async function readResponseBody(response) {
12813
13582
 
12814
13583
  // src/adapters/opencode/OpencodeAdapter.ts
12815
13584
  var OPENCODE_SYSTEM_NOTE = [
12816
- "Responses are relayed back into the Thenvoi room by the adapter.",
12817
- "Use the thenvoi_ prefixed tools (for example thenvoi_send_message) for Thenvoi platform actions when available.",
13585
+ "Responses are relayed back into the Band room by the adapter.",
13586
+ "Use the band_ prefixed tools (for example band_send_message) for Band platform actions when available.",
12818
13587
  "When you need approval or clarification, ask clearly and wait for the user's next room message."
12819
13588
  ].join("\n");
12820
13589
  function createDeferred() {
@@ -12845,8 +13614,8 @@ function withDefaults(config) {
12845
13614
  approvalTimeoutReply: "reject",
12846
13615
  questionMode: "manual",
12847
13616
  questionWaitTimeoutMs: 3e5,
12848
- sessionTitlePrefix: "Thenvoi",
12849
- mcpServerName: "thenvoi",
13617
+ sessionTitlePrefix: "Band",
13618
+ mcpServerName: MCP_SERVER_NAME,
12850
13619
  ...config
12851
13620
  };
12852
13621
  }
@@ -12902,7 +13671,7 @@ var OpencodeAdapter = class extends SimpleAdapter {
12902
13671
  directory: config.directory || void 0,
12903
13672
  workspace: config.workspace || void 0
12904
13673
  }));
12905
- this.mcpBackendFactory = options.mcpBackendFactory ?? createThenvoiMcpBackend;
13674
+ this.mcpBackendFactory = options.mcpBackendFactory ?? createBandMcpBackend;
12906
13675
  this.logger = options.logger ?? new NoopLogger();
12907
13676
  }
12908
13677
  async onStarted(agentName, agentDescription) {
@@ -12911,7 +13680,8 @@ var OpencodeAdapter = class extends SimpleAdapter {
12911
13680
  agentName,
12912
13681
  agentDescription,
12913
13682
  customSection: this.config.customSection,
12914
- includeBaseInstructions: this.config.includeBaseInstructions
13683
+ includeBaseInstructions: this.config.includeBaseInstructions,
13684
+ capabilities: { memory: this.config.enableMemoryTools }
12915
13685
  }).trim();
12916
13686
  this.systemPrompt = `${systemPrompt}
12917
13687
 
@@ -13055,7 +13825,8 @@ ${OPENCODE_SYSTEM_NOTE}`.trim();
13055
13825
  }
13056
13826
  await client.registerMcpServer({
13057
13827
  name: this.config.mcpServerName,
13058
- url: server.url
13828
+ url: server.url,
13829
+ headers: backend.authToken ? { Authorization: `Bearer ${backend.authToken}` } : void 0
13059
13830
  });
13060
13831
  } catch (error) {
13061
13832
  this.logger.warn("Failed to register OpenCode MCP backend", { error });
@@ -13691,9 +14462,9 @@ function extractClaudeSessionId(raw) {
13691
14462
  // src/adapters/claude-sdk/ClaudeSDKAdapter.ts
13692
14463
  init_registrations();
13693
14464
  init_zod();
13694
- var import_zod9 = require("zod");
14465
+ var import_zod10 = require("zod");
13695
14466
  var DEFAULT_MODEL2 = "claude-sonnet-4-6";
13696
- var thenvoiMcpBridgeFactory = new LazyAsyncValue({
14467
+ var bandMcpBridgeFactory = new LazyAsyncValue({
13697
14468
  load: async () => {
13698
14469
  const module2 = await import("@anthropic-ai/claude-agent-sdk").catch((error) => {
13699
14470
  throw new UnsupportedFeatureError(
@@ -13718,7 +14489,7 @@ var thenvoiMcpBridgeFactory = new LazyAsyncValue({
13718
14489
  );
13719
14490
  const toolDefinitions = registrations.map((registration) => {
13720
14491
  const shape = buildZodShape(
13721
- import_zod9.z,
14492
+ import_zod10.z,
13722
14493
  registration.inputSchema.properties,
13723
14494
  new Set(registration.inputSchema.required)
13724
14495
  );
@@ -13731,7 +14502,7 @@ var thenvoiMcpBridgeFactory = new LazyAsyncValue({
13731
14502
  });
13732
14503
  return {
13733
14504
  serverConfig: createSdkMcpServer2({
13734
- name: "thenvoi",
14505
+ name: MCP_SERVER_NAME,
13735
14506
  tools: toolDefinitions
13736
14507
  }),
13737
14508
  allowedTools: mcpToolNames(new Set(registrations.map((registration) => registration.name)))
@@ -13778,11 +14549,12 @@ var ClaudeSDKAdapter = class extends SimpleAdapter {
13778
14549
  agentName,
13779
14550
  agentDescription,
13780
14551
  customSection: this.customSection,
13781
- includeBaseInstructions: this.includeBaseInstructions
14552
+ includeBaseInstructions: this.includeBaseInstructions,
14553
+ capabilities: { memory: this.enableMemoryTools }
13782
14554
  });
13783
14555
  if (this.enableMcpTools) {
13784
- const createThenvoiMcpBridge = await thenvoiMcpBridgeFactory.get();
13785
- this.mcpBridge = createThenvoiMcpBridge({
14556
+ const createBandMcpBridge = await bandMcpBridgeFactory.get();
14557
+ this.mcpBridge = createBandMcpBridge({
13786
14558
  enableMemoryTools: this.enableMemoryTools,
13787
14559
  getToolsForRoom: (roomId) => this.roomTools.get(roomId),
13788
14560
  additionalTools: this.additionalMcpTools.length > 0 ? this.additionalMcpTools : void 0
@@ -13830,14 +14602,14 @@ var ClaudeSDKAdapter = class extends SimpleAdapter {
13830
14602
  }
13831
14603
  if (this.mcpBridge && this.enableMcpTools) {
13832
14604
  options.mcpServers = {
13833
- thenvoi: this.mcpBridge.serverConfig
14605
+ [MCP_SERVER_NAME]: this.mcpBridge.serverConfig
13834
14606
  };
13835
14607
  options.allowedTools = this.mcpBridge.allowedTools;
13836
14608
  this.roomTools.set(context.roomId, tools);
13837
14609
  }
13838
14610
  const roomToolHint = this.enableMcpTools ? `
13839
14611
 
13840
- [Tooling note]: For any mcp__thenvoi__* tool call, pass room_id="${context.roomId}".` : "";
14612
+ [Tooling note]: For any mcp__band__* tool call, pass room_id="${context.roomId}".` : "";
13841
14613
  const query = queryFn({
13842
14614
  prompt: buildConversationPrompt({
13843
14615
  history,
@@ -13933,6 +14705,7 @@ init_schemas();
13933
14705
  Agent,
13934
14706
  AgentRuntime,
13935
14707
  AnthropicAdapter,
14708
+ BandLink,
13936
14709
  CODEX_REASONING_EFFORTS,
13937
14710
  CODEX_REASONING_SUMMARIES,
13938
14711
  CODEX_WEB_SEARCH_MODES,
@@ -13944,6 +14717,7 @@ init_schemas();
13944
14717
  GoogleADKAdapter,
13945
14718
  LangGraphAdapter,
13946
14719
  LettaAdapter,
14720
+ MCP_SERVER_NAME,
13947
14721
  MCP_TOOL_PREFIX,
13948
14722
  OpenAIAdapter,
13949
14723
  OpencodeAdapter,
@@ -13951,8 +14725,9 @@ init_schemas();
13951
14725
  PlatformRuntime,
13952
14726
  SimpleAdapter,
13953
14727
  TOOL_MODELS,
13954
- ThenvoiLink,
13955
14728
  VercelAISDKAdapter,
14729
+ WebSocketDisconnectError,
14730
+ deriveDefaultRestUrl,
13956
14731
  isDirectExecution,
13957
14732
  loadAgentConfig,
13958
14733
  loadAgentConfigFromEnv