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