@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
|
@@ -3,21 +3,45 @@ import {
|
|
|
3
3
|
SYNTHETIC_CONTACT_EVENTS_SENDER_ID,
|
|
4
4
|
SYNTHETIC_CONTACT_EVENTS_SENDER_NAME,
|
|
5
5
|
SYNTHETIC_SENDER_TYPE,
|
|
6
|
-
buildParticipantsMessage
|
|
7
|
-
|
|
6
|
+
buildParticipantsMessage,
|
|
7
|
+
toParticipantRecord,
|
|
8
|
+
toParticipantRecordFromRest
|
|
9
|
+
} from "./chunk-LBWKISBS.js";
|
|
8
10
|
import {
|
|
9
11
|
CHAT_EVENT_TYPES,
|
|
10
12
|
CHAT_TOOL_NAMES,
|
|
13
|
+
MEMORY_LIST_SCOPES,
|
|
14
|
+
MEMORY_SEGMENTS,
|
|
15
|
+
MEMORY_STATUSES,
|
|
16
|
+
MEMORY_STORE_SCOPES,
|
|
17
|
+
MEMORY_SYSTEMS,
|
|
11
18
|
MEMORY_TOOL_NAMES,
|
|
19
|
+
MEMORY_TYPES,
|
|
12
20
|
TOOL_MODELS,
|
|
13
21
|
assertChatEventType,
|
|
14
|
-
|
|
15
|
-
|
|
22
|
+
expectedList,
|
|
23
|
+
expectedMemoryTypesForSystem,
|
|
24
|
+
getToolDescription,
|
|
25
|
+
isMemoryListScope,
|
|
26
|
+
isMemorySegment,
|
|
27
|
+
isMemoryStatus,
|
|
28
|
+
isMemoryStoreScope,
|
|
29
|
+
isMemorySystem,
|
|
30
|
+
isMemoryType,
|
|
31
|
+
isMemoryTypeForSystem,
|
|
32
|
+
memoryTypeForSystemError
|
|
33
|
+
} from "./chunk-RV76SKFT.js";
|
|
34
|
+
import {
|
|
35
|
+
WebSocketDisconnectError,
|
|
36
|
+
genericCloseReason,
|
|
37
|
+
parseSupersedeDisconnectReason,
|
|
38
|
+
parseUpgradeDisconnectReason
|
|
39
|
+
} from "./chunk-US226AS4.js";
|
|
16
40
|
import {
|
|
17
41
|
DEFAULT_REQUEST_OPTIONS,
|
|
18
42
|
FernRestAdapter,
|
|
19
43
|
fetchPaginated
|
|
20
|
-
} from "./chunk-
|
|
44
|
+
} from "./chunk-EAI2M3WM.js";
|
|
21
45
|
import {
|
|
22
46
|
DEFAULT_AGENT_TOOLS_CAPABILITIES,
|
|
23
47
|
createToolExecutorError,
|
|
@@ -31,7 +55,7 @@ import {
|
|
|
31
55
|
TransportError,
|
|
32
56
|
UnsupportedFeatureError,
|
|
33
57
|
ValidationError
|
|
34
|
-
} from "./chunk-
|
|
58
|
+
} from "./chunk-JY75VSQK.js";
|
|
35
59
|
|
|
36
60
|
// src/contracts/capabilities.ts
|
|
37
61
|
var CAPABILITY_LABELS = {
|
|
@@ -143,55 +167,117 @@ var payloadSchemas = {
|
|
|
143
167
|
|
|
144
168
|
// src/platform/streaming/PhoenixChannelsTransport.ts
|
|
145
169
|
import { Socket } from "phoenix";
|
|
170
|
+
|
|
171
|
+
// src/platform/streaming/nodeWebSocketFactory.ts
|
|
146
172
|
import { WebSocket as NodeWebSocket } from "ws";
|
|
173
|
+
function createNodeWebSocketFactory(headers) {
|
|
174
|
+
const Constructor = NodeWebSocket;
|
|
175
|
+
class BandNodeWebSocket {
|
|
176
|
+
constructor(address, protocols) {
|
|
177
|
+
const socket = new Constructor(address, protocols, headers ? { headers } : void 0);
|
|
178
|
+
socket.once("unexpected-response", (request, response) => {
|
|
179
|
+
void emitUpgradeError(socket, request, response);
|
|
180
|
+
});
|
|
181
|
+
return socket;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
return BandNodeWebSocket;
|
|
185
|
+
}
|
|
186
|
+
async function emitUpgradeError(websocket, request, response) {
|
|
187
|
+
const body = await readResponseBody(response);
|
|
188
|
+
const error = new Error(
|
|
189
|
+
`Unexpected server response: ${response.statusCode ?? "unknown"}`
|
|
190
|
+
);
|
|
191
|
+
error.status = response.statusCode;
|
|
192
|
+
error.body = body;
|
|
193
|
+
error.headers = response.headers;
|
|
194
|
+
request.destroy();
|
|
195
|
+
websocket.emit("error", error);
|
|
196
|
+
websocket.emit("close", 1006, Buffer.alloc(0));
|
|
197
|
+
}
|
|
198
|
+
async function readResponseBody(response) {
|
|
199
|
+
const chunks = [];
|
|
200
|
+
for await (const chunk of response) {
|
|
201
|
+
chunks.push(typeof chunk === "string" ? Buffer.from(chunk) : chunk);
|
|
202
|
+
}
|
|
203
|
+
return Buffer.concat(chunks).toString("utf8");
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// src/platform/streaming/PhoenixChannelsTransport.ts
|
|
147
207
|
var PhoenixChannelsTransport = class {
|
|
148
208
|
socket;
|
|
209
|
+
agentId;
|
|
149
210
|
channels = /* @__PURE__ */ new Map();
|
|
150
211
|
channelRefs = /* @__PURE__ */ new Map();
|
|
151
212
|
pendingJoins = /* @__PURE__ */ new Map();
|
|
152
213
|
logger;
|
|
214
|
+
onTerminalDisconnect;
|
|
153
215
|
onHandlerError;
|
|
154
216
|
connected = false;
|
|
155
217
|
connectPromise = null;
|
|
156
218
|
connectResolve = null;
|
|
219
|
+
connectReject = null;
|
|
220
|
+
lastDisconnectReason = null;
|
|
221
|
+
terminalDisconnectError = null;
|
|
222
|
+
runForeverWaiters = /* @__PURE__ */ new Set();
|
|
223
|
+
stoppingReconnect = false;
|
|
224
|
+
suppressNextCloseReason = false;
|
|
157
225
|
constructor(options) {
|
|
158
226
|
this.logger = options.logger ?? new NoopLogger();
|
|
227
|
+
this.agentId = options.agentId;
|
|
228
|
+
this.onTerminalDisconnect = options.onTerminalDisconnect;
|
|
159
229
|
let wsUrl = options.wsUrl;
|
|
160
230
|
if (wsUrl.endsWith("/websocket")) {
|
|
161
231
|
wsUrl = wsUrl.slice(0, -"/websocket".length);
|
|
162
232
|
}
|
|
233
|
+
const reconnectAfterMs = options.reconnectAfterMs ?? ((tries) => [1e3, 2e3, 5e3, 1e4, 3e4][tries - 1] ?? 3e4);
|
|
163
234
|
this.socket = new Socket(wsUrl, {
|
|
164
235
|
params: {
|
|
165
|
-
|
|
166
|
-
|
|
236
|
+
agent_id: options.agentId,
|
|
237
|
+
...options.conflictPolicy ? { on_conflict: options.conflictPolicy } : {}
|
|
167
238
|
},
|
|
168
239
|
heartbeatIntervalMs: options.heartbeatIntervalMs,
|
|
169
|
-
reconnectAfterMs:
|
|
170
|
-
|
|
240
|
+
reconnectAfterMs: (tries) => {
|
|
241
|
+
if (this.terminalDisconnectError) {
|
|
242
|
+
return Number.POSITIVE_INFINITY;
|
|
243
|
+
}
|
|
244
|
+
return reconnectAfterMs(tries);
|
|
245
|
+
},
|
|
246
|
+
transport: options.websocketFactory ?? resolveWebSocketFactory(options.apiKey)
|
|
171
247
|
});
|
|
172
248
|
this.socket.onOpen(() => {
|
|
173
|
-
this.
|
|
174
|
-
this.connectResolve?.();
|
|
175
|
-
this.connectResolve = null;
|
|
176
|
-
this.logger.info("Phoenix socket opened", {
|
|
177
|
-
channels: getSocketChannelCount(this.socket)
|
|
178
|
-
});
|
|
249
|
+
void this.handleOpen();
|
|
179
250
|
});
|
|
180
251
|
this.socket.onClose((event) => {
|
|
181
|
-
this.
|
|
182
|
-
if (getSocketChannelCount(this.socket) === 0) {
|
|
183
|
-
this.socket.disconnect();
|
|
184
|
-
}
|
|
185
|
-
this.logger.info("Phoenix socket closed", {
|
|
186
|
-
code: event?.code ?? null,
|
|
187
|
-
reason: event?.reason ?? null
|
|
188
|
-
});
|
|
252
|
+
this.recordSocketClose(event);
|
|
189
253
|
});
|
|
190
254
|
this.socket.onError((event) => {
|
|
255
|
+
const errorEvent = unwrapErrorEvent(event);
|
|
256
|
+
const upgradeReason = parseUpgradeDisconnectReason(errorEvent);
|
|
257
|
+
if (upgradeReason) {
|
|
258
|
+
if (upgradeReason.retryable) {
|
|
259
|
+
this.lastDisconnectReason = upgradeReason;
|
|
260
|
+
this.connectReject?.(new WebSocketDisconnectError(upgradeReason));
|
|
261
|
+
this.stopReconnectIfNoChannels({ suppressCloseReason: true });
|
|
262
|
+
} else {
|
|
263
|
+
this.recordTerminalDisconnect(upgradeReason);
|
|
264
|
+
}
|
|
265
|
+
this.logger.warn("Phoenix socket upgrade failed", {
|
|
266
|
+
reason: upgradeReason
|
|
267
|
+
});
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
this.connectReject?.(
|
|
271
|
+
new TransportError("Phoenix socket connection failed", errorEvent)
|
|
272
|
+
);
|
|
273
|
+
this.stopReconnectIfNoChannels({ suppressCloseReason: true });
|
|
191
274
|
this.logger.warn("Phoenix socket error", { event });
|
|
192
275
|
});
|
|
193
276
|
}
|
|
194
277
|
async connect() {
|
|
278
|
+
if (this.terminalDisconnectError) {
|
|
279
|
+
throw this.terminalDisconnectError;
|
|
280
|
+
}
|
|
195
281
|
if (this.connected) {
|
|
196
282
|
return;
|
|
197
283
|
}
|
|
@@ -215,15 +301,30 @@ var PhoenixChannelsTransport = class {
|
|
|
215
301
|
await this.connectPromise;
|
|
216
302
|
}
|
|
217
303
|
async disconnect() {
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
304
|
+
const results = await Promise.allSettled(
|
|
305
|
+
[...this.channels.keys()].map((topic) => this.leave(topic))
|
|
306
|
+
);
|
|
221
307
|
this.socket.disconnect();
|
|
222
308
|
this.connected = false;
|
|
309
|
+
const failures = [];
|
|
310
|
+
for (const result of results) {
|
|
311
|
+
if (result.status === "rejected") {
|
|
312
|
+
failures.push(result.reason);
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
if (failures.length > 0) {
|
|
316
|
+
throw new AggregateError(
|
|
317
|
+
failures,
|
|
318
|
+
"Failed to leave one or more Phoenix topics during disconnect"
|
|
319
|
+
);
|
|
320
|
+
}
|
|
223
321
|
}
|
|
224
322
|
isConnected() {
|
|
225
323
|
return this.connected;
|
|
226
324
|
}
|
|
325
|
+
getDisconnectReason() {
|
|
326
|
+
return this.lastDisconnectReason;
|
|
327
|
+
}
|
|
227
328
|
async join(topic, handlers) {
|
|
228
329
|
if (this.channels.has(topic)) {
|
|
229
330
|
return;
|
|
@@ -259,7 +360,10 @@ var PhoenixChannelsTransport = class {
|
|
|
259
360
|
channel.join().receive("ok", () => resolve()).receive(
|
|
260
361
|
"error",
|
|
261
362
|
(error) => reject(new TransportError(`Failed to join topic ${topic}`, error))
|
|
262
|
-
).receive(
|
|
363
|
+
).receive(
|
|
364
|
+
"timeout",
|
|
365
|
+
() => reject(new TransportError(`Timeout joining topic ${topic}`))
|
|
366
|
+
);
|
|
263
367
|
});
|
|
264
368
|
} catch (error) {
|
|
265
369
|
for (const [event, ref] of refs) {
|
|
@@ -287,19 +391,127 @@ var PhoenixChannelsTransport = class {
|
|
|
287
391
|
channel.leave().receive("ok", () => resolve()).receive(
|
|
288
392
|
"error",
|
|
289
393
|
(error) => reject(new TransportError(`Failed to leave topic ${topic}`, error))
|
|
290
|
-
).receive(
|
|
394
|
+
).receive(
|
|
395
|
+
"timeout",
|
|
396
|
+
() => reject(new TransportError(`Timeout leaving topic ${topic}`))
|
|
397
|
+
);
|
|
291
398
|
});
|
|
292
399
|
this.channels.delete(topic);
|
|
293
400
|
this.logger.debug("Left topic", { topic });
|
|
294
401
|
}
|
|
295
402
|
async runForever(signal) {
|
|
403
|
+
if (this.terminalDisconnectError) {
|
|
404
|
+
throw this.terminalDisconnectError;
|
|
405
|
+
}
|
|
296
406
|
if (signal.aborted) {
|
|
297
407
|
return;
|
|
298
408
|
}
|
|
299
|
-
await new Promise((resolve) => {
|
|
300
|
-
|
|
409
|
+
await new Promise((resolve, reject) => {
|
|
410
|
+
let settled = false;
|
|
411
|
+
const abortController = new AbortController();
|
|
412
|
+
const waiter = {
|
|
413
|
+
reject: (error) => {
|
|
414
|
+
if (settled) {
|
|
415
|
+
return;
|
|
416
|
+
}
|
|
417
|
+
settled = true;
|
|
418
|
+
this.runForeverWaiters.delete(waiter);
|
|
419
|
+
abortController.abort();
|
|
420
|
+
reject(error);
|
|
421
|
+
}
|
|
422
|
+
};
|
|
423
|
+
this.runForeverWaiters.add(waiter);
|
|
424
|
+
signal.addEventListener(
|
|
425
|
+
"abort",
|
|
426
|
+
() => {
|
|
427
|
+
if (settled) {
|
|
428
|
+
return;
|
|
429
|
+
}
|
|
430
|
+
settled = true;
|
|
431
|
+
this.runForeverWaiters.delete(waiter);
|
|
432
|
+
resolve();
|
|
433
|
+
},
|
|
434
|
+
{ once: true, signal: abortController.signal }
|
|
435
|
+
);
|
|
436
|
+
});
|
|
437
|
+
}
|
|
438
|
+
async handleOpen() {
|
|
439
|
+
try {
|
|
440
|
+
await this.subscribeAgentControl();
|
|
441
|
+
} catch (error) {
|
|
442
|
+
this.connected = false;
|
|
443
|
+
this.socket.disconnect();
|
|
444
|
+
this.connectReject?.(
|
|
445
|
+
error instanceof Error ? error : new TransportError(String(error))
|
|
446
|
+
);
|
|
447
|
+
this.logger.warn("Failed to join mandatory agent_control channel", {
|
|
448
|
+
error
|
|
449
|
+
});
|
|
450
|
+
return;
|
|
451
|
+
}
|
|
452
|
+
this.connected = true;
|
|
453
|
+
this.connectResolve?.();
|
|
454
|
+
this.connectResolve = null;
|
|
455
|
+
this.connectReject = null;
|
|
456
|
+
this.logger.info("Phoenix socket opened", {
|
|
457
|
+
channels: getSocketChannelCount(this.socket)
|
|
458
|
+
});
|
|
459
|
+
}
|
|
460
|
+
stopReconnectIfNoChannels(options = {}) {
|
|
461
|
+
if (this.stoppingReconnect || getSocketChannelCount(this.socket) !== 0) {
|
|
462
|
+
return;
|
|
463
|
+
}
|
|
464
|
+
this.suppressNextCloseReason = options.suppressCloseReason ?? false;
|
|
465
|
+
this.stoppingReconnect = true;
|
|
466
|
+
this.socket.disconnect();
|
|
467
|
+
this.stoppingReconnect = false;
|
|
468
|
+
}
|
|
469
|
+
async subscribeAgentControl() {
|
|
470
|
+
if (!this.agentId) {
|
|
471
|
+
return;
|
|
472
|
+
}
|
|
473
|
+
await this.join(`agent_control:${this.agentId}`, {
|
|
474
|
+
supersede: (payload) => {
|
|
475
|
+
const reason = parseSupersedeDisconnectReason(payload);
|
|
476
|
+
if (!reason) {
|
|
477
|
+
this.logger.warn("Invalid agent_control supersede payload", {
|
|
478
|
+
payload
|
|
479
|
+
});
|
|
480
|
+
return;
|
|
481
|
+
}
|
|
482
|
+
this.recordTerminalDisconnect(reason);
|
|
483
|
+
}
|
|
301
484
|
});
|
|
302
485
|
}
|
|
486
|
+
recordSocketClose(event) {
|
|
487
|
+
this.connected = false;
|
|
488
|
+
const suppressCloseReason = this.suppressNextCloseReason;
|
|
489
|
+
this.suppressNextCloseReason = false;
|
|
490
|
+
this.stopReconnectIfNoChannels();
|
|
491
|
+
if (!suppressCloseReason && !this.terminalDisconnectError && !this.lastDisconnectReason) {
|
|
492
|
+
this.lastDisconnectReason = genericCloseReason(event);
|
|
493
|
+
}
|
|
494
|
+
this.logger.info("Phoenix socket closed", {
|
|
495
|
+
code: event?.code ?? null,
|
|
496
|
+
reason: event?.reason ?? null,
|
|
497
|
+
platformReason: this.lastDisconnectReason
|
|
498
|
+
});
|
|
499
|
+
}
|
|
500
|
+
recordTerminalDisconnect(reason) {
|
|
501
|
+
if (this.terminalDisconnectError) {
|
|
502
|
+
return;
|
|
503
|
+
}
|
|
504
|
+
const error = new WebSocketDisconnectError(reason);
|
|
505
|
+
this.lastDisconnectReason = reason;
|
|
506
|
+
this.terminalDisconnectError = error;
|
|
507
|
+
this.onTerminalDisconnect?.(reason);
|
|
508
|
+
this.connectReject?.(error);
|
|
509
|
+
for (const waiter of this.runForeverWaiters) {
|
|
510
|
+
waiter.reject(error);
|
|
511
|
+
}
|
|
512
|
+
this.runForeverWaiters.clear();
|
|
513
|
+
this.socket.disconnect();
|
|
514
|
+
}
|
|
303
515
|
async waitForConnection(timeoutMs = 1e4) {
|
|
304
516
|
if (this.connected) {
|
|
305
517
|
return;
|
|
@@ -307,20 +519,43 @@ var PhoenixChannelsTransport = class {
|
|
|
307
519
|
await new Promise((resolve, reject) => {
|
|
308
520
|
const timeout = setTimeout(() => {
|
|
309
521
|
this.connectResolve = null;
|
|
310
|
-
|
|
522
|
+
this.connectReject = null;
|
|
523
|
+
reject(
|
|
524
|
+
new TransportError("Timed out waiting for Phoenix socket connection")
|
|
525
|
+
);
|
|
311
526
|
}, timeoutMs);
|
|
312
527
|
this.connectResolve = () => {
|
|
313
528
|
clearTimeout(timeout);
|
|
529
|
+
this.connectReject = null;
|
|
314
530
|
resolve();
|
|
315
531
|
};
|
|
532
|
+
this.connectReject = (error) => {
|
|
533
|
+
clearTimeout(timeout);
|
|
534
|
+
this.connectResolve = null;
|
|
535
|
+
this.connectReject = null;
|
|
536
|
+
reject(
|
|
537
|
+
error instanceof Error ? error : new TransportError(String(error))
|
|
538
|
+
);
|
|
539
|
+
};
|
|
316
540
|
});
|
|
317
541
|
}
|
|
318
542
|
};
|
|
319
|
-
function resolveWebSocketFactory() {
|
|
543
|
+
function resolveWebSocketFactory(apiKey) {
|
|
320
544
|
if (typeof process !== "undefined" && process.versions?.node) {
|
|
321
|
-
return
|
|
545
|
+
return createNodeWebSocketFactory({ "x-api-key": apiKey });
|
|
322
546
|
}
|
|
323
|
-
|
|
547
|
+
throw new TransportError(
|
|
548
|
+
"Phoenix WebSocket API-key auth requires a WebSocket transport that can set handshake headers."
|
|
549
|
+
);
|
|
550
|
+
}
|
|
551
|
+
function unwrapErrorEvent(event) {
|
|
552
|
+
if (!isErrorEvent(event)) {
|
|
553
|
+
return event;
|
|
554
|
+
}
|
|
555
|
+
return event.error;
|
|
556
|
+
}
|
|
557
|
+
function isErrorEvent(event) {
|
|
558
|
+
return typeof event === "object" && event !== null && "error" in event;
|
|
324
559
|
}
|
|
325
560
|
function removeSocketChannel(socket, channel) {
|
|
326
561
|
const candidate = socket;
|
|
@@ -334,14 +569,20 @@ function getSocketChannelCount(socket) {
|
|
|
334
569
|
return candidate.channels.length;
|
|
335
570
|
}
|
|
336
571
|
|
|
337
|
-
// src/platform/
|
|
338
|
-
import {
|
|
339
|
-
var DEFAULT_WS_URL = "wss://app.
|
|
572
|
+
// src/platform/BandLink.ts
|
|
573
|
+
import { BandClient } from "@band-ai/rest-client";
|
|
574
|
+
var DEFAULT_WS_URL = "wss://app.band.ai/api/v1/socket";
|
|
340
575
|
function deriveDefaultRestUrl(wsUrl) {
|
|
341
576
|
const parsed = new URL(wsUrl);
|
|
342
577
|
const protocol = parsed.protocol === "ws:" ? "http:" : "https:";
|
|
343
578
|
return `${protocol}//${parsed.host}`;
|
|
344
579
|
}
|
|
580
|
+
function roomTopics(roomId) {
|
|
581
|
+
return {
|
|
582
|
+
chat: `chat_room:${roomId}`,
|
|
583
|
+
participants: `room_participants:${roomId}`
|
|
584
|
+
};
|
|
585
|
+
}
|
|
345
586
|
function toPlatformMessage(roomId, message) {
|
|
346
587
|
return {
|
|
347
588
|
id: message.id,
|
|
@@ -355,7 +596,7 @@ function toPlatformMessage(roomId, message) {
|
|
|
355
596
|
createdAt: new Date(message.inserted_at)
|
|
356
597
|
};
|
|
357
598
|
}
|
|
358
|
-
var
|
|
599
|
+
var BandLink = class {
|
|
359
600
|
agentId;
|
|
360
601
|
apiKey;
|
|
361
602
|
wsUrl;
|
|
@@ -368,6 +609,8 @@ var ThenvoiLink = class {
|
|
|
368
609
|
eventQueue = [];
|
|
369
610
|
waiters = [];
|
|
370
611
|
connected = false;
|
|
612
|
+
lastDisconnectReason = null;
|
|
613
|
+
terminalDisconnectError = null;
|
|
371
614
|
constructor(options) {
|
|
372
615
|
this.agentId = options.agentId;
|
|
373
616
|
this.apiKey = options.apiKey;
|
|
@@ -379,7 +622,7 @@ var ThenvoiLink = class {
|
|
|
379
622
|
...options.capabilities
|
|
380
623
|
};
|
|
381
624
|
const restApi = options.restApi ?? new FernRestAdapter(
|
|
382
|
-
new
|
|
625
|
+
new BandClient({
|
|
383
626
|
apiKey: this.apiKey,
|
|
384
627
|
baseUrl: this.restUrl
|
|
385
628
|
})
|
|
@@ -389,17 +632,35 @@ var ThenvoiLink = class {
|
|
|
389
632
|
wsUrl: this.wsUrl,
|
|
390
633
|
apiKey: this.apiKey,
|
|
391
634
|
agentId: this.agentId,
|
|
392
|
-
logger: this.logger
|
|
635
|
+
logger: this.logger,
|
|
636
|
+
conflictPolicy: options.conflictPolicy,
|
|
637
|
+
onTerminalDisconnect: (reason) => {
|
|
638
|
+
this.recordDisconnectReason(reason);
|
|
639
|
+
}
|
|
393
640
|
});
|
|
394
641
|
}
|
|
395
642
|
isConnected() {
|
|
396
643
|
return this.connected;
|
|
397
644
|
}
|
|
645
|
+
getDisconnectReason() {
|
|
646
|
+
return this.lastDisconnectReason;
|
|
647
|
+
}
|
|
398
648
|
async connect() {
|
|
399
649
|
if (this.connected) {
|
|
400
650
|
return;
|
|
401
651
|
}
|
|
402
|
-
|
|
652
|
+
try {
|
|
653
|
+
await this.transport.connect();
|
|
654
|
+
} catch (error) {
|
|
655
|
+
if (error instanceof WebSocketDisconnectError) {
|
|
656
|
+
if (error.reason.retryable) {
|
|
657
|
+
this.lastDisconnectReason = error.reason;
|
|
658
|
+
} else {
|
|
659
|
+
this.recordDisconnectError(error);
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
throw error;
|
|
663
|
+
}
|
|
403
664
|
this.connected = true;
|
|
404
665
|
}
|
|
405
666
|
async disconnect() {
|
|
@@ -413,8 +674,23 @@ var ThenvoiLink = class {
|
|
|
413
674
|
this.connected = false;
|
|
414
675
|
}
|
|
415
676
|
async runForever(signal) {
|
|
677
|
+
if (this.terminalDisconnectError) {
|
|
678
|
+
throw this.terminalDisconnectError;
|
|
679
|
+
}
|
|
416
680
|
await this.transport.runForever(signal);
|
|
417
681
|
}
|
|
682
|
+
recordDisconnectReason(reason) {
|
|
683
|
+
this.recordDisconnectError(new WebSocketDisconnectError(reason));
|
|
684
|
+
}
|
|
685
|
+
recordDisconnectError(error) {
|
|
686
|
+
this.connected = false;
|
|
687
|
+
this.lastDisconnectReason = error.reason;
|
|
688
|
+
this.terminalDisconnectError = error;
|
|
689
|
+
while (this.waiters.length > 0) {
|
|
690
|
+
const waiter = this.waiters.shift();
|
|
691
|
+
waiter?.reject(error);
|
|
692
|
+
}
|
|
693
|
+
}
|
|
418
694
|
queueEvent(event) {
|
|
419
695
|
const waiter = this.waiters.shift();
|
|
420
696
|
if (waiter) {
|
|
@@ -439,32 +715,42 @@ var ThenvoiLink = class {
|
|
|
439
715
|
if (this.subscribedRooms.has(roomId)) {
|
|
440
716
|
return;
|
|
441
717
|
}
|
|
442
|
-
await this.
|
|
443
|
-
message_created: (payload) => {
|
|
444
|
-
this.emit("message_created", payload, roomId);
|
|
445
|
-
}
|
|
446
|
-
});
|
|
447
|
-
await this.transport.join(`room_participants:${roomId}`, {
|
|
448
|
-
participant_added: (payload) => {
|
|
449
|
-
this.emit("participant_added", payload, roomId);
|
|
450
|
-
},
|
|
451
|
-
participant_removed: (payload) => {
|
|
452
|
-
this.emit("participant_removed", payload, roomId);
|
|
453
|
-
},
|
|
454
|
-
room_deleted: (payload) => {
|
|
455
|
-
this.emit("room_deleted", payload, roomId);
|
|
456
|
-
}
|
|
457
|
-
});
|
|
718
|
+
await this.joinRoomTopics(roomId);
|
|
458
719
|
this.subscribedRooms.add(roomId);
|
|
459
720
|
}
|
|
460
721
|
async unsubscribeRoom(roomId) {
|
|
461
722
|
if (!this.subscribedRooms.has(roomId)) {
|
|
462
723
|
return;
|
|
463
724
|
}
|
|
464
|
-
|
|
465
|
-
await this.transport.leave(
|
|
725
|
+
const topics = roomTopics(roomId);
|
|
726
|
+
await this.transport.leave(topics.chat);
|
|
727
|
+
await this.transport.leave(topics.participants);
|
|
466
728
|
this.subscribedRooms.delete(roomId);
|
|
467
729
|
}
|
|
730
|
+
async joinRoomTopics(roomId) {
|
|
731
|
+
const topics = roomTopics(roomId);
|
|
732
|
+
await this.transport.join(topics.chat, {
|
|
733
|
+
message_created: (payload) => {
|
|
734
|
+
this.emit("message_created", payload, roomId);
|
|
735
|
+
}
|
|
736
|
+
});
|
|
737
|
+
try {
|
|
738
|
+
await this.transport.join(topics.participants, {
|
|
739
|
+
participant_added: (payload) => {
|
|
740
|
+
this.emit("participant_added", payload, roomId);
|
|
741
|
+
},
|
|
742
|
+
participant_removed: (payload) => {
|
|
743
|
+
this.emit("participant_removed", payload, roomId);
|
|
744
|
+
},
|
|
745
|
+
room_deleted: (payload) => {
|
|
746
|
+
this.emit("room_deleted", payload, roomId);
|
|
747
|
+
}
|
|
748
|
+
});
|
|
749
|
+
} catch (error) {
|
|
750
|
+
await this.transport.leave(topics.chat);
|
|
751
|
+
throw error;
|
|
752
|
+
}
|
|
753
|
+
}
|
|
468
754
|
async subscribeAgentContacts() {
|
|
469
755
|
assertCapability(this.capabilities, "contacts", "Contacts streaming");
|
|
470
756
|
await this.transport.join(`agent_contacts:${this.agentId}`, {
|
|
@@ -486,6 +772,9 @@ var ThenvoiLink = class {
|
|
|
486
772
|
await this.transport.leave(`agent_contacts:${this.agentId}`);
|
|
487
773
|
}
|
|
488
774
|
async nextEvent(signal) {
|
|
775
|
+
if (this.terminalDisconnectError) {
|
|
776
|
+
throw this.terminalDisconnectError;
|
|
777
|
+
}
|
|
489
778
|
if (signal?.aborted) {
|
|
490
779
|
return null;
|
|
491
780
|
}
|
|
@@ -493,19 +782,19 @@ var ThenvoiLink = class {
|
|
|
493
782
|
if (queued) {
|
|
494
783
|
return queued;
|
|
495
784
|
}
|
|
496
|
-
return new Promise((resolve) => {
|
|
785
|
+
return new Promise((resolve, reject) => {
|
|
497
786
|
let settled = false;
|
|
498
787
|
const onAbort = () => {
|
|
499
788
|
const index = this.waiters.indexOf(waiter);
|
|
500
789
|
if (index >= 0) {
|
|
501
790
|
this.waiters.splice(index, 1);
|
|
502
791
|
}
|
|
503
|
-
|
|
792
|
+
finalizeResolve(null);
|
|
504
793
|
};
|
|
505
794
|
const cleanup = () => {
|
|
506
795
|
signal?.removeEventListener("abort", onAbort);
|
|
507
796
|
};
|
|
508
|
-
const
|
|
797
|
+
const finalizeResolve = (event) => {
|
|
509
798
|
if (settled) {
|
|
510
799
|
return;
|
|
511
800
|
}
|
|
@@ -513,8 +802,17 @@ var ThenvoiLink = class {
|
|
|
513
802
|
cleanup();
|
|
514
803
|
resolve(event);
|
|
515
804
|
};
|
|
805
|
+
const finalizeReject = (error) => {
|
|
806
|
+
if (settled) {
|
|
807
|
+
return;
|
|
808
|
+
}
|
|
809
|
+
settled = true;
|
|
810
|
+
cleanup();
|
|
811
|
+
reject(error);
|
|
812
|
+
};
|
|
516
813
|
const waiter = {
|
|
517
|
-
resolve:
|
|
814
|
+
resolve: finalizeResolve,
|
|
815
|
+
reject: finalizeReject,
|
|
518
816
|
cleanup
|
|
519
817
|
};
|
|
520
818
|
this.waiters.push(waiter);
|
|
@@ -526,7 +824,10 @@ var ThenvoiLink = class {
|
|
|
526
824
|
next: async () => {
|
|
527
825
|
const value = await this.nextEvent();
|
|
528
826
|
if (value === null) {
|
|
529
|
-
return {
|
|
827
|
+
return {
|
|
828
|
+
done: true,
|
|
829
|
+
value: void 0
|
|
830
|
+
};
|
|
530
831
|
}
|
|
531
832
|
return { done: false, value };
|
|
532
833
|
}
|
|
@@ -608,7 +909,9 @@ var ThenvoiLink = class {
|
|
|
608
909
|
}
|
|
609
910
|
async listChats(request, requestOptions) {
|
|
610
911
|
if (!this.rest.listChats) {
|
|
611
|
-
throw new UnsupportedFeatureError(
|
|
912
|
+
throw new UnsupportedFeatureError(
|
|
913
|
+
"Chat listing is not available in current REST adapter"
|
|
914
|
+
);
|
|
612
915
|
}
|
|
613
916
|
return this.rest.listChats(request, requestOptions);
|
|
614
917
|
}
|
|
@@ -934,6 +1237,7 @@ var Execution = class {
|
|
|
934
1237
|
};
|
|
935
1238
|
|
|
936
1239
|
// src/runtime/tools/AgentTools.ts
|
|
1240
|
+
import { ParticipantRoster } from "@band-ai/band-sdk-core";
|
|
937
1241
|
var REQUIRED_ADAPTER_TOOL_METHODS = [
|
|
938
1242
|
"sendMessage",
|
|
939
1243
|
"sendEvent",
|
|
@@ -968,51 +1272,17 @@ var CONTACT_REQUEST_ACTIONS = /* @__PURE__ */ new Set([
|
|
|
968
1272
|
"reject",
|
|
969
1273
|
"cancel"
|
|
970
1274
|
]);
|
|
971
|
-
var MEMORY_SYSTEMS = /* @__PURE__ */ new Set([
|
|
972
|
-
"sensory",
|
|
973
|
-
"working",
|
|
974
|
-
"long_term"
|
|
975
|
-
]);
|
|
976
|
-
var MEMORY_TYPES = /* @__PURE__ */ new Set([
|
|
977
|
-
"iconic",
|
|
978
|
-
"echoic",
|
|
979
|
-
"haptic",
|
|
980
|
-
"episodic",
|
|
981
|
-
"semantic",
|
|
982
|
-
"procedural"
|
|
983
|
-
]);
|
|
984
|
-
var MEMORY_SEGMENTS = /* @__PURE__ */ new Set([
|
|
985
|
-
"user",
|
|
986
|
-
"agent",
|
|
987
|
-
"tool",
|
|
988
|
-
"guideline"
|
|
989
|
-
]);
|
|
990
|
-
var LIST_MEMORY_SCOPES = /* @__PURE__ */ new Set([
|
|
991
|
-
"subject",
|
|
992
|
-
"organization",
|
|
993
|
-
"all"
|
|
994
|
-
]);
|
|
995
|
-
var LIST_MEMORY_STATUSES = /* @__PURE__ */ new Set([
|
|
996
|
-
"active",
|
|
997
|
-
"superseded",
|
|
998
|
-
"archived",
|
|
999
|
-
"all"
|
|
1000
|
-
]);
|
|
1001
|
-
var MEMORY_SCOPES = /* @__PURE__ */ new Set([
|
|
1002
|
-
"subject",
|
|
1003
|
-
"organization"
|
|
1004
|
-
]);
|
|
1005
1275
|
var AgentTools = class {
|
|
1006
1276
|
roomId;
|
|
1007
1277
|
capabilities;
|
|
1008
1278
|
rest;
|
|
1009
|
-
|
|
1279
|
+
roster;
|
|
1010
1280
|
adapterTools;
|
|
1011
1281
|
toolHandlers;
|
|
1012
1282
|
constructor(options) {
|
|
1013
1283
|
this.roomId = options.roomId;
|
|
1014
1284
|
this.rest = options.rest;
|
|
1015
|
-
this.
|
|
1285
|
+
this.roster = options.roster ?? new ParticipantRoster();
|
|
1016
1286
|
this.capabilities = {
|
|
1017
1287
|
...DEFAULT_AGENT_TOOLS_CAPABILITIES,
|
|
1018
1288
|
...options.capabilities
|
|
@@ -1024,17 +1294,20 @@ var AgentTools = class {
|
|
|
1024
1294
|
return this.adapterTools;
|
|
1025
1295
|
}
|
|
1026
1296
|
async sendMessage(content, mentions = []) {
|
|
1027
|
-
|
|
1028
|
-
|
|
1297
|
+
let participants;
|
|
1298
|
+
if (mentions.length > 0 && typeof mentions[0] === "string") {
|
|
1299
|
+
participants = this.roster.list();
|
|
1300
|
+
if (participants.length === 0) {
|
|
1301
|
+
participants = await this.syncParticipants();
|
|
1302
|
+
}
|
|
1029
1303
|
}
|
|
1030
|
-
const resolvedMentions = this.resolveMentions(mentions);
|
|
1304
|
+
const resolvedMentions = this.resolveMentions(mentions, participants);
|
|
1031
1305
|
return this.rest.createChatMessage(
|
|
1032
1306
|
this.roomId,
|
|
1033
1307
|
{
|
|
1034
1308
|
content,
|
|
1035
1309
|
mentions: resolvedMentions
|
|
1036
|
-
}
|
|
1037
|
-
DEFAULT_REQUEST_OPTIONS
|
|
1310
|
+
}
|
|
1038
1311
|
);
|
|
1039
1312
|
}
|
|
1040
1313
|
async sendEvent(content, messageType, metadata) {
|
|
@@ -1045,8 +1318,7 @@ var AgentTools = class {
|
|
|
1045
1318
|
content,
|
|
1046
1319
|
messageType,
|
|
1047
1320
|
metadata
|
|
1048
|
-
}
|
|
1049
|
-
DEFAULT_REQUEST_OPTIONS
|
|
1321
|
+
}
|
|
1050
1322
|
);
|
|
1051
1323
|
}
|
|
1052
1324
|
async createChatroom(taskId) {
|
|
@@ -1085,7 +1357,7 @@ var AgentTools = class {
|
|
|
1085
1357
|
type: String(peer.type ?? "Agent"),
|
|
1086
1358
|
handle: typeof peer.handle === "string" ? peer.handle : null
|
|
1087
1359
|
};
|
|
1088
|
-
this.
|
|
1360
|
+
this.roster.add(participantRecord);
|
|
1089
1361
|
return {
|
|
1090
1362
|
...participantRecord,
|
|
1091
1363
|
status: "added"
|
|
@@ -1100,7 +1372,7 @@ var AgentTools = class {
|
|
|
1100
1372
|
throw new ValidationError(`Participant '${name}' not found in room`);
|
|
1101
1373
|
}
|
|
1102
1374
|
await this.rest.removeChatParticipant(this.roomId, String(participant.id), DEFAULT_REQUEST_OPTIONS);
|
|
1103
|
-
this.
|
|
1375
|
+
this.roster.remove(participant.id);
|
|
1104
1376
|
return {
|
|
1105
1377
|
id: participant.id,
|
|
1106
1378
|
name,
|
|
@@ -1128,17 +1400,12 @@ var AgentTools = class {
|
|
|
1128
1400
|
}
|
|
1129
1401
|
async fetchParticipants() {
|
|
1130
1402
|
const participants = await this.rest.listChatParticipants(this.roomId, DEFAULT_REQUEST_OPTIONS);
|
|
1131
|
-
return participants.map(
|
|
1132
|
-
id: participant.id,
|
|
1133
|
-
name: participant.name,
|
|
1134
|
-
type: participant.type,
|
|
1135
|
-
handle: participant.handle ?? null
|
|
1136
|
-
}));
|
|
1403
|
+
return participants.map(toParticipantRecordFromRest);
|
|
1137
1404
|
}
|
|
1138
1405
|
async syncParticipants() {
|
|
1139
1406
|
const participants = await this.fetchParticipants();
|
|
1140
|
-
this.
|
|
1141
|
-
return
|
|
1407
|
+
this.roster.setAll(participants);
|
|
1408
|
+
return this.roster.list().map(toParticipantRecord);
|
|
1142
1409
|
}
|
|
1143
1410
|
async executeToolCall(toolName, arguments_) {
|
|
1144
1411
|
const validationError = validateToolArgs(toolName, arguments_);
|
|
@@ -1186,7 +1453,7 @@ var AgentTools = class {
|
|
|
1186
1453
|
if (!CHAT_TOOL_NAMES.has(name) && !this.capabilities.contacts) {
|
|
1187
1454
|
return false;
|
|
1188
1455
|
}
|
|
1189
|
-
if (name === "
|
|
1456
|
+
if (name === "band_lookup_peers" && !this.capabilities.peers) {
|
|
1190
1457
|
return false;
|
|
1191
1458
|
}
|
|
1192
1459
|
return true;
|
|
@@ -1381,7 +1648,7 @@ var AgentTools = class {
|
|
|
1381
1648
|
}
|
|
1382
1649
|
return this.rest.archiveMemory(normalizedMemoryId, DEFAULT_REQUEST_OPTIONS);
|
|
1383
1650
|
}
|
|
1384
|
-
resolveMentions(mentions) {
|
|
1651
|
+
resolveMentions(mentions, participants) {
|
|
1385
1652
|
if (mentions.length === 0) {
|
|
1386
1653
|
return [];
|
|
1387
1654
|
}
|
|
@@ -1394,7 +1661,7 @@ var AgentTools = class {
|
|
|
1394
1661
|
const participantsByHandle = /* @__PURE__ */ new Map();
|
|
1395
1662
|
const participantsById = /* @__PURE__ */ new Map();
|
|
1396
1663
|
const participantsByName = /* @__PURE__ */ new Map();
|
|
1397
|
-
for (const participant of
|
|
1664
|
+
for (const participant of participants ?? []) {
|
|
1398
1665
|
const ref = {
|
|
1399
1666
|
id: String(participant.id),
|
|
1400
1667
|
handle: typeof participant.handle === "string" ? participant.handle : void 0
|
|
@@ -1450,9 +1717,6 @@ var AgentTools = class {
|
|
|
1450
1717
|
normalizeMentionHandle(handle) {
|
|
1451
1718
|
return handle.trim().replace(/^@+/, "").toLowerCase();
|
|
1452
1719
|
}
|
|
1453
|
-
replaceParticipants(participants) {
|
|
1454
|
-
this.participants.splice(0, this.participants.length, ...participants);
|
|
1455
|
-
}
|
|
1456
1720
|
buildAdapterTools() {
|
|
1457
1721
|
const tools = {
|
|
1458
1722
|
capabilities: this.capabilities
|
|
@@ -1483,51 +1747,51 @@ var AgentTools = class {
|
|
|
1483
1747
|
}
|
|
1484
1748
|
buildMessagingToolHandlers() {
|
|
1485
1749
|
return {
|
|
1486
|
-
|
|
1750
|
+
band_send_message: async (arguments_) => this.sendMessage(
|
|
1487
1751
|
String(arguments_.content ?? ""),
|
|
1488
1752
|
this.normalizeMentionInput(arguments_.mentions)
|
|
1489
1753
|
),
|
|
1490
|
-
|
|
1754
|
+
band_send_event: async (arguments_) => this.sendEvent(
|
|
1491
1755
|
String(arguments_.content ?? ""),
|
|
1492
1756
|
String(arguments_.message_type ?? "task"),
|
|
1493
1757
|
this.normalizeOptionalMetadata(arguments_.metadata)
|
|
1494
1758
|
),
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1759
|
+
band_add_participant: async (arguments_) => this.addParticipant(String(arguments_.name ?? ""), String(arguments_.role ?? "member")),
|
|
1760
|
+
band_remove_participant: async (arguments_) => this.removeParticipant(String(arguments_.name ?? "")),
|
|
1761
|
+
band_lookup_peers: async (arguments_) => this.lookupPeers(
|
|
1498
1762
|
coercePositiveInt(arguments_.page, 1),
|
|
1499
1763
|
coercePositiveInt(arguments_.page_size, 50)
|
|
1500
1764
|
),
|
|
1501
|
-
|
|
1502
|
-
|
|
1765
|
+
band_get_participants: async () => this.getParticipants(),
|
|
1766
|
+
band_create_chatroom: async (arguments_) => this.createChatroom(this.normalizeOptionalString(arguments_.task_id))
|
|
1503
1767
|
};
|
|
1504
1768
|
}
|
|
1505
1769
|
buildContactToolHandlers() {
|
|
1506
1770
|
return {
|
|
1507
|
-
|
|
1771
|
+
band_list_contacts: async (arguments_) => this.listContacts({
|
|
1508
1772
|
page: coercePositiveInt(arguments_.page, 1),
|
|
1509
1773
|
pageSize: coercePositiveInt(arguments_.page_size, 50)
|
|
1510
1774
|
}),
|
|
1511
|
-
|
|
1775
|
+
band_add_contact: async (arguments_) => this.addContact({
|
|
1512
1776
|
handle: String(arguments_.handle ?? ""),
|
|
1513
1777
|
...typeof arguments_.message === "string" ? { message: arguments_.message } : {}
|
|
1514
1778
|
}),
|
|
1515
|
-
|
|
1516
|
-
|
|
1779
|
+
band_remove_contact: async (arguments_) => this.removeContact(this.toRemoveContactArgs(arguments_)),
|
|
1780
|
+
band_list_contact_requests: async (arguments_) => this.listContactRequests({
|
|
1517
1781
|
page: coercePositiveInt(arguments_.page, 1),
|
|
1518
1782
|
pageSize: coercePositiveInt(arguments_.page_size, 50),
|
|
1519
1783
|
sentStatus: String(arguments_.sent_status ?? "pending")
|
|
1520
1784
|
}),
|
|
1521
|
-
|
|
1785
|
+
band_respond_contact_request: async (arguments_) => this.respondContactRequest(this.toRespondContactRequestArgs(arguments_))
|
|
1522
1786
|
};
|
|
1523
1787
|
}
|
|
1524
1788
|
buildMemoryToolHandlers() {
|
|
1525
1789
|
return {
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1790
|
+
band_list_memories: async (arguments_) => this.listMemories(this.toListMemoriesArgs(arguments_)),
|
|
1791
|
+
band_store_memory: async (arguments_) => this.storeMemory(this.toStoreMemoryArgs(arguments_)),
|
|
1792
|
+
band_get_memory: async (arguments_) => this.getMemory(String(arguments_.memory_id ?? "")),
|
|
1793
|
+
band_supersede_memory: async (arguments_) => this.supersedeMemory(String(arguments_.memory_id ?? "")),
|
|
1794
|
+
band_archive_memory: async (arguments_) => this.archiveMemory(String(arguments_.memory_id ?? ""))
|
|
1531
1795
|
};
|
|
1532
1796
|
}
|
|
1533
1797
|
toRemoveContactArgs(arguments_) {
|
|
@@ -1579,6 +1843,9 @@ var AgentTools = class {
|
|
|
1579
1843
|
const status = this.normalizeOptionalMemoryStatus(arguments_.status);
|
|
1580
1844
|
const subjectId = this.normalizeOptionalString(arguments_.subject_id);
|
|
1581
1845
|
const contentQuery = this.normalizeOptionalString(arguments_.content_query);
|
|
1846
|
+
if (system && type && !isMemoryTypeForSystem(system, type)) {
|
|
1847
|
+
throw new ValidationError(memoryTypeForSystemError(system));
|
|
1848
|
+
}
|
|
1582
1849
|
return {
|
|
1583
1850
|
...subjectId ? { subject_id: subjectId } : {},
|
|
1584
1851
|
...scope ? { scope } : {},
|
|
@@ -1600,13 +1867,21 @@ var AgentTools = class {
|
|
|
1600
1867
|
const subjectId = this.normalizeOptionalString(arguments_.subject_id);
|
|
1601
1868
|
const metadata = this.normalizeOptionalMetadata(arguments_.metadata);
|
|
1602
1869
|
if (!system) {
|
|
1603
|
-
throw new ValidationError(
|
|
1870
|
+
throw new ValidationError(`system must be one of: ${expectedList(MEMORY_SYSTEMS)}`);
|
|
1604
1871
|
}
|
|
1605
1872
|
if (!type) {
|
|
1606
|
-
throw new ValidationError(
|
|
1873
|
+
throw new ValidationError(`type must be one of: ${expectedList(MEMORY_TYPES)}`);
|
|
1874
|
+
}
|
|
1875
|
+
if (!isMemoryTypeForSystem(system, type)) {
|
|
1876
|
+
throw new ValidationError(memoryTypeForSystemError(system));
|
|
1607
1877
|
}
|
|
1608
1878
|
if (!segment) {
|
|
1609
|
-
throw new ValidationError(
|
|
1879
|
+
throw new ValidationError(`segment must be one of: ${expectedList(MEMORY_SEGMENTS)}`);
|
|
1880
|
+
}
|
|
1881
|
+
if (scope === "subject" && !subjectId) {
|
|
1882
|
+
throw new ValidationError(
|
|
1883
|
+
'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.'
|
|
1884
|
+
);
|
|
1610
1885
|
}
|
|
1611
1886
|
return {
|
|
1612
1887
|
content,
|
|
@@ -1686,8 +1961,8 @@ var AgentTools = class {
|
|
|
1686
1961
|
if (!normalized) {
|
|
1687
1962
|
return void 0;
|
|
1688
1963
|
}
|
|
1689
|
-
if (!
|
|
1690
|
-
throw new ValidationError(
|
|
1964
|
+
if (!isMemoryListScope(normalized)) {
|
|
1965
|
+
throw new ValidationError(`scope must be one of: ${expectedList(MEMORY_LIST_SCOPES)}`);
|
|
1691
1966
|
}
|
|
1692
1967
|
return normalized;
|
|
1693
1968
|
}
|
|
@@ -1697,7 +1972,7 @@ var AgentTools = class {
|
|
|
1697
1972
|
return void 0;
|
|
1698
1973
|
}
|
|
1699
1974
|
if (!isMemorySystem(normalized)) {
|
|
1700
|
-
throw new ValidationError(
|
|
1975
|
+
throw new ValidationError(`system must be one of: ${expectedList(MEMORY_SYSTEMS)}`);
|
|
1701
1976
|
}
|
|
1702
1977
|
return normalized;
|
|
1703
1978
|
}
|
|
@@ -1707,7 +1982,7 @@ var AgentTools = class {
|
|
|
1707
1982
|
return void 0;
|
|
1708
1983
|
}
|
|
1709
1984
|
if (!isMemoryType(normalized)) {
|
|
1710
|
-
throw new ValidationError(
|
|
1985
|
+
throw new ValidationError(`type must be one of: ${expectedList(MEMORY_TYPES)}`);
|
|
1711
1986
|
}
|
|
1712
1987
|
return normalized;
|
|
1713
1988
|
}
|
|
@@ -1717,7 +1992,7 @@ var AgentTools = class {
|
|
|
1717
1992
|
return void 0;
|
|
1718
1993
|
}
|
|
1719
1994
|
if (!isMemorySegment(normalized)) {
|
|
1720
|
-
throw new ValidationError(
|
|
1995
|
+
throw new ValidationError(`segment must be one of: ${expectedList(MEMORY_SEGMENTS)}`);
|
|
1721
1996
|
}
|
|
1722
1997
|
return normalized;
|
|
1723
1998
|
}
|
|
@@ -1727,7 +2002,7 @@ var AgentTools = class {
|
|
|
1727
2002
|
return void 0;
|
|
1728
2003
|
}
|
|
1729
2004
|
if (!isMemoryStatus(normalized)) {
|
|
1730
|
-
throw new ValidationError(
|
|
2005
|
+
throw new ValidationError(`status must be one of: ${expectedList(MEMORY_STATUSES)}`);
|
|
1731
2006
|
}
|
|
1732
2007
|
return normalized;
|
|
1733
2008
|
}
|
|
@@ -1736,8 +2011,8 @@ var AgentTools = class {
|
|
|
1736
2011
|
if (!normalized) {
|
|
1737
2012
|
return void 0;
|
|
1738
2013
|
}
|
|
1739
|
-
if (!
|
|
1740
|
-
throw new ValidationError(
|
|
2014
|
+
if (!isMemoryStoreScope(normalized)) {
|
|
2015
|
+
throw new ValidationError(`scope must be one of: ${expectedList(MEMORY_STORE_SCOPES)}`);
|
|
1741
2016
|
}
|
|
1742
2017
|
return normalized;
|
|
1743
2018
|
}
|
|
@@ -1745,24 +2020,6 @@ var AgentTools = class {
|
|
|
1745
2020
|
function isContactRequestAction(value) {
|
|
1746
2021
|
return CONTACT_REQUEST_ACTIONS.has(value);
|
|
1747
2022
|
}
|
|
1748
|
-
function isMemorySystem(value) {
|
|
1749
|
-
return MEMORY_SYSTEMS.has(value);
|
|
1750
|
-
}
|
|
1751
|
-
function isMemoryType(value) {
|
|
1752
|
-
return MEMORY_TYPES.has(value);
|
|
1753
|
-
}
|
|
1754
|
-
function isMemorySegment(value) {
|
|
1755
|
-
return MEMORY_SEGMENTS.has(value);
|
|
1756
|
-
}
|
|
1757
|
-
function isStoreMemoryScope(value) {
|
|
1758
|
-
return MEMORY_SCOPES.has(value);
|
|
1759
|
-
}
|
|
1760
|
-
function isListMemoryScope(value) {
|
|
1761
|
-
return LIST_MEMORY_SCOPES.has(value);
|
|
1762
|
-
}
|
|
1763
|
-
function isMemoryStatus(value) {
|
|
1764
|
-
return LIST_MEMORY_STATUSES.has(value);
|
|
1765
|
-
}
|
|
1766
2023
|
function coercePositiveInt(value, fallback) {
|
|
1767
2024
|
const num = Number(value ?? fallback);
|
|
1768
2025
|
return Number.isFinite(num) && num >= 1 ? Math.floor(num) : fallback;
|
|
@@ -1778,13 +2035,13 @@ function validateToolArgs(toolName, args) {
|
|
|
1778
2035
|
errors.push(`${field}: Field required`);
|
|
1779
2036
|
}
|
|
1780
2037
|
}
|
|
1781
|
-
if (toolName === "
|
|
2038
|
+
if (toolName === "band_send_message") {
|
|
1782
2039
|
const mentions = args.mentions;
|
|
1783
2040
|
if (Array.isArray(mentions) && mentions.length === 0) {
|
|
1784
2041
|
errors.push("mentions: At least one mention is required");
|
|
1785
2042
|
}
|
|
1786
2043
|
}
|
|
1787
|
-
if (toolName === "
|
|
2044
|
+
if (toolName === "band_send_event") {
|
|
1788
2045
|
const messageType = args.message_type;
|
|
1789
2046
|
if (typeof messageType === "string" && !CHAT_EVENT_TYPES.includes(messageType)) {
|
|
1790
2047
|
errors.push(
|
|
@@ -1792,7 +2049,7 @@ function validateToolArgs(toolName, args) {
|
|
|
1792
2049
|
);
|
|
1793
2050
|
}
|
|
1794
2051
|
}
|
|
1795
|
-
if (toolName === "
|
|
2052
|
+
if (toolName === "band_respond_contact_request") {
|
|
1796
2053
|
const action = args.action;
|
|
1797
2054
|
const validActions = ["approve", "reject", "cancel"];
|
|
1798
2055
|
if (typeof action === "string" && !validActions.includes(action)) {
|
|
@@ -1801,18 +2058,20 @@ function validateToolArgs(toolName, args) {
|
|
|
1801
2058
|
);
|
|
1802
2059
|
}
|
|
1803
2060
|
}
|
|
1804
|
-
if (toolName === "
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
const validSegments = ["user", "agent", "tool", "guideline"];
|
|
1808
|
-
if (typeof args.system === "string" && !validSystems.includes(args.system)) {
|
|
1809
|
-
errors.push(`system: Invalid value '${args.system}'. Expected one of: ${validSystems.join(", ")}`);
|
|
2061
|
+
if (toolName === "band_store_memory") {
|
|
2062
|
+
if (typeof args.system === "string" && !isMemorySystem(args.system)) {
|
|
2063
|
+
errors.push(`system: Invalid value '${args.system}'. Expected one of: ${expectedList(MEMORY_SYSTEMS)}`);
|
|
1810
2064
|
}
|
|
1811
|
-
if (typeof args.type === "string" && !
|
|
1812
|
-
errors.push(`type: Invalid value '${args.type}'. Expected one of: ${
|
|
2065
|
+
if (typeof args.type === "string" && !isMemoryType(args.type)) {
|
|
2066
|
+
errors.push(`type: Invalid value '${args.type}'. Expected one of: ${expectedList(MEMORY_TYPES)}`);
|
|
2067
|
+
}
|
|
2068
|
+
if (typeof args.system === "string" && isMemorySystem(args.system) && typeof args.type === "string" && isMemoryType(args.type) && !isMemoryTypeForSystem(args.system, args.type)) {
|
|
2069
|
+
errors.push(
|
|
2070
|
+
`type: Invalid value '${args.type}' for system '${args.system}'. Expected one of: ${expectedMemoryTypesForSystem(args.system)}`
|
|
2071
|
+
);
|
|
1813
2072
|
}
|
|
1814
|
-
if (typeof args.segment === "string" && !
|
|
1815
|
-
errors.push(`segment: Invalid value '${args.segment}'. Expected one of: ${
|
|
2073
|
+
if (typeof args.segment === "string" && !isMemorySegment(args.segment)) {
|
|
2074
|
+
errors.push(`segment: Invalid value '${args.segment}'. Expected one of: ${expectedList(MEMORY_SEGMENTS)}`);
|
|
1816
2075
|
}
|
|
1817
2076
|
}
|
|
1818
2077
|
if (errors.length > 0) {
|
|
@@ -1828,58 +2087,8 @@ function validateToolArgs(toolName, args) {
|
|
|
1828
2087
|
return null;
|
|
1829
2088
|
}
|
|
1830
2089
|
|
|
1831
|
-
// src/runtime/retryTracker.ts
|
|
1832
|
-
var DEFAULT_MAX_TRACKED = 1e4;
|
|
1833
|
-
var MessageRetryTracker = class {
|
|
1834
|
-
maxRetriesValue;
|
|
1835
|
-
maxTracked;
|
|
1836
|
-
attempts = /* @__PURE__ */ new Map();
|
|
1837
|
-
failed = /* @__PURE__ */ new Set();
|
|
1838
|
-
constructor(maxRetries = 1, maxTracked = DEFAULT_MAX_TRACKED) {
|
|
1839
|
-
this.maxRetriesValue = maxRetries;
|
|
1840
|
-
this.maxTracked = maxTracked;
|
|
1841
|
-
}
|
|
1842
|
-
get maxRetries() {
|
|
1843
|
-
return this.maxRetriesValue;
|
|
1844
|
-
}
|
|
1845
|
-
isPermanentlyFailed(messageId) {
|
|
1846
|
-
return this.failed.has(messageId);
|
|
1847
|
-
}
|
|
1848
|
-
recordAttempt(messageId) {
|
|
1849
|
-
const attempts = (this.attempts.get(messageId) ?? 0) + 1;
|
|
1850
|
-
this.attempts.set(messageId, attempts);
|
|
1851
|
-
const exceeded = attempts > this.maxRetriesValue;
|
|
1852
|
-
if (exceeded) {
|
|
1853
|
-
this.evictSetIfNeeded(this.failed);
|
|
1854
|
-
this.failed.add(messageId);
|
|
1855
|
-
}
|
|
1856
|
-
this.evictMapIfNeeded(this.attempts);
|
|
1857
|
-
return [attempts, exceeded];
|
|
1858
|
-
}
|
|
1859
|
-
markSuccess(messageId) {
|
|
1860
|
-
this.attempts.delete(messageId);
|
|
1861
|
-
}
|
|
1862
|
-
markPermanentlyFailed(messageId) {
|
|
1863
|
-
this.evictSetIfNeeded(this.failed);
|
|
1864
|
-
this.failed.add(messageId);
|
|
1865
|
-
}
|
|
1866
|
-
evictMapIfNeeded(map) {
|
|
1867
|
-
while (map.size >= this.maxTracked) {
|
|
1868
|
-
const first = map.keys().next();
|
|
1869
|
-
if (first.done) break;
|
|
1870
|
-
map.delete(first.value);
|
|
1871
|
-
}
|
|
1872
|
-
}
|
|
1873
|
-
evictSetIfNeeded(set) {
|
|
1874
|
-
while (set.size >= this.maxTracked) {
|
|
1875
|
-
const first = set.values().next();
|
|
1876
|
-
if (first.done) break;
|
|
1877
|
-
set.delete(first.value);
|
|
1878
|
-
}
|
|
1879
|
-
}
|
|
1880
|
-
};
|
|
1881
|
-
|
|
1882
2090
|
// src/runtime/ExecutionContext.ts
|
|
2091
|
+
import { ParticipantRoster as ParticipantRoster2, RetryTracker } from "@band-ai/band-sdk-core";
|
|
1883
2092
|
var DEDUP_CACHE_MAX = 500;
|
|
1884
2093
|
var ExecutionContext = class {
|
|
1885
2094
|
roomId;
|
|
@@ -1891,11 +2100,10 @@ var ExecutionContext = class {
|
|
|
1891
2100
|
history = [];
|
|
1892
2101
|
messageIds = /* @__PURE__ */ new Set();
|
|
1893
2102
|
dedupCache = /* @__PURE__ */ new Map();
|
|
1894
|
-
|
|
2103
|
+
roster = new ParticipantRoster2();
|
|
1895
2104
|
tools;
|
|
1896
2105
|
adapterTools;
|
|
1897
2106
|
participantsMessage = null;
|
|
1898
|
-
lastSentParticipantIds = null;
|
|
1899
2107
|
contactsMessage = null;
|
|
1900
2108
|
contextCache = null;
|
|
1901
2109
|
contextCacheExpiresAt = 0;
|
|
@@ -1910,11 +2118,11 @@ var ExecutionContext = class {
|
|
|
1910
2118
|
this.enableContextCache = options.enableContextCache ?? true;
|
|
1911
2119
|
this.contextCacheTtlMs = Math.max(0, (options.contextCacheTtlSeconds ?? 300) * 1e3);
|
|
1912
2120
|
this.enableContextHydration = options.enableContextHydration ?? true;
|
|
1913
|
-
this.retryTrackerInstance = new
|
|
2121
|
+
this.retryTrackerInstance = new RetryTracker(options.maxMessageRetries ?? 1);
|
|
1914
2122
|
this.tools = new AgentTools({
|
|
1915
2123
|
roomId: this.roomId,
|
|
1916
2124
|
rest: this.link.rest,
|
|
1917
|
-
|
|
2125
|
+
roster: this.roster,
|
|
1918
2126
|
capabilities: this.link.capabilities
|
|
1919
2127
|
});
|
|
1920
2128
|
this.adapterTools = this.tools.getAdapterTools();
|
|
@@ -1982,23 +2190,20 @@ var ExecutionContext = class {
|
|
|
1982
2190
|
}));
|
|
1983
2191
|
}
|
|
1984
2192
|
setParticipants(participants) {
|
|
1985
|
-
this.
|
|
2193
|
+
this.roster.setAll(participants);
|
|
1986
2194
|
this.updateCachedParticipants();
|
|
1987
2195
|
}
|
|
1988
2196
|
addParticipant(participant) {
|
|
1989
|
-
const
|
|
1990
|
-
if (
|
|
1991
|
-
|
|
2197
|
+
const isNew = this.roster.add(participant);
|
|
2198
|
+
if (isNew) {
|
|
2199
|
+
const name = String(participant.name ?? "unknown");
|
|
2200
|
+
this.participantsMessage = `${name} joined the room.`;
|
|
1992
2201
|
}
|
|
1993
|
-
this.participants.push(participant);
|
|
1994
|
-
const name = String(participant.name ?? "unknown");
|
|
1995
|
-
this.participantsMessage = `${name} joined the room.`;
|
|
1996
2202
|
this.updateCachedParticipants();
|
|
1997
2203
|
}
|
|
1998
2204
|
removeParticipant(participantId) {
|
|
1999
|
-
const removed = this.
|
|
2000
|
-
|
|
2001
|
-
this.replaceParticipants(next);
|
|
2205
|
+
const removed = this.roster.list().find((entry) => entry.id === participantId);
|
|
2206
|
+
this.roster.remove(participantId);
|
|
2002
2207
|
if (removed) {
|
|
2003
2208
|
this.participantsMessage = `${String(removed.name ?? participantId)} left the room.`;
|
|
2004
2209
|
}
|
|
@@ -2008,16 +2213,7 @@ var ExecutionContext = class {
|
|
|
2008
2213
|
this.contactsMessage = message;
|
|
2009
2214
|
}
|
|
2010
2215
|
consumeParticipantsMessage() {
|
|
2011
|
-
const
|
|
2012
|
-
let changed = !this.lastSentParticipantIds || currentIds.size !== this.lastSentParticipantIds.size;
|
|
2013
|
-
if (!changed) {
|
|
2014
|
-
for (const id of currentIds) {
|
|
2015
|
-
if (!this.lastSentParticipantIds.has(id)) {
|
|
2016
|
-
changed = true;
|
|
2017
|
-
break;
|
|
2018
|
-
}
|
|
2019
|
-
}
|
|
2020
|
-
}
|
|
2216
|
+
const changed = this.roster.changed();
|
|
2021
2217
|
if (!changed && !this.participantsMessage) {
|
|
2022
2218
|
return null;
|
|
2023
2219
|
}
|
|
@@ -2027,15 +2223,9 @@ var ExecutionContext = class {
|
|
|
2027
2223
|
this.participantsMessage = null;
|
|
2028
2224
|
}
|
|
2029
2225
|
if (changed) {
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
type: p.type,
|
|
2034
|
-
handle: p.handle ?? null
|
|
2035
|
-
}));
|
|
2036
|
-
parts.push(buildParticipantsMessage(asRecords));
|
|
2037
|
-
}
|
|
2038
|
-
this.lastSentParticipantIds = currentIds;
|
|
2226
|
+
parts.push(buildParticipantsMessage(this.roster.list().map((p) => ({ ...toParticipantRecord(p) }))));
|
|
2227
|
+
}
|
|
2228
|
+
this.roster.markSent();
|
|
2039
2229
|
return parts.length > 0 ? parts.join("\n") : null;
|
|
2040
2230
|
}
|
|
2041
2231
|
consumeContactsMessage() {
|
|
@@ -2086,27 +2276,19 @@ var ExecutionContext = class {
|
|
|
2086
2276
|
return fallback;
|
|
2087
2277
|
}
|
|
2088
2278
|
}
|
|
2089
|
-
replaceParticipants(participants) {
|
|
2090
|
-
this.participants.splice(0, this.participants.length, ...participants);
|
|
2091
|
-
}
|
|
2092
2279
|
buildLocalContext() {
|
|
2093
2280
|
return {
|
|
2094
2281
|
roomId: this.roomId,
|
|
2095
2282
|
messages: this.getRawHistory(),
|
|
2096
|
-
participants:
|
|
2283
|
+
participants: this.roster.list().map(toParticipantRecord),
|
|
2097
2284
|
hydratedAt: /* @__PURE__ */ new Date()
|
|
2098
2285
|
};
|
|
2099
2286
|
}
|
|
2100
2287
|
async loadParticipants() {
|
|
2101
2288
|
const participants = await this.link.rest.listChatParticipants(this.roomId, DEFAULT_REQUEST_OPTIONS);
|
|
2102
|
-
const normalized = participants.map(
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
type: participant.type,
|
|
2106
|
-
handle: participant.handle ?? null
|
|
2107
|
-
}));
|
|
2108
|
-
this.replaceParticipants(normalized);
|
|
2109
|
-
return [...this.participants];
|
|
2289
|
+
const normalized = participants.map(toParticipantRecordFromRest);
|
|
2290
|
+
this.roster.setAll(normalized);
|
|
2291
|
+
return this.roster.list().map(toParticipantRecord);
|
|
2110
2292
|
}
|
|
2111
2293
|
async loadHydratedMessages() {
|
|
2112
2294
|
const messages = [];
|
|
@@ -2148,7 +2330,7 @@ var ExecutionContext = class {
|
|
|
2148
2330
|
if (!this.contextCache) {
|
|
2149
2331
|
return;
|
|
2150
2332
|
}
|
|
2151
|
-
this.contextCache.participants =
|
|
2333
|
+
this.contextCache.participants = this.roster.list().map(toParticipantRecord);
|
|
2152
2334
|
this.contextCache.hydratedAt = /* @__PURE__ */ new Date();
|
|
2153
2335
|
this.contextCacheExpiresAt = this.nextCacheExpiry();
|
|
2154
2336
|
}
|
|
@@ -2349,7 +2531,7 @@ var AgentRuntime = class {
|
|
|
2349
2531
|
await this.link.disconnect();
|
|
2350
2532
|
}
|
|
2351
2533
|
async stop(timeoutMs) {
|
|
2352
|
-
if (!this.running
|
|
2534
|
+
if (this.stopping || !this.running && !this.fatalError) {
|
|
2353
2535
|
return true;
|
|
2354
2536
|
}
|
|
2355
2537
|
this.stopping = true;
|
|
@@ -2369,7 +2551,8 @@ var AgentRuntime = class {
|
|
|
2369
2551
|
}
|
|
2370
2552
|
}
|
|
2371
2553
|
for (const roomId of [...this.subscribedRooms]) {
|
|
2372
|
-
|
|
2554
|
+
const remaining = deadline === void 0 ? void 0 : Math.max(0, deadline - Date.now());
|
|
2555
|
+
await this.leaveTrackedRoom(roomId, remaining);
|
|
2373
2556
|
}
|
|
2374
2557
|
for (const roomId of [...this.contexts.keys()]) {
|
|
2375
2558
|
await this.onSessionCleanup(roomId);
|
|
@@ -2405,7 +2588,13 @@ var AgentRuntime = class {
|
|
|
2405
2588
|
}
|
|
2406
2589
|
async consumeLoop(signal) {
|
|
2407
2590
|
while (!signal.aborted) {
|
|
2408
|
-
|
|
2591
|
+
let event;
|
|
2592
|
+
try {
|
|
2593
|
+
event = await this.link.nextEvent(signal);
|
|
2594
|
+
} catch (error) {
|
|
2595
|
+
await this.failRuntime(error, syntheticRuntimeFailureEvent(this.agentId));
|
|
2596
|
+
return;
|
|
2597
|
+
}
|
|
2409
2598
|
if (!event) {
|
|
2410
2599
|
return;
|
|
2411
2600
|
}
|
|
@@ -2568,13 +2757,13 @@ var AgentRuntime = class {
|
|
|
2568
2757
|
}
|
|
2569
2758
|
});
|
|
2570
2759
|
}
|
|
2571
|
-
async leaveTrackedRoom(roomId) {
|
|
2760
|
+
async leaveTrackedRoom(roomId, timeoutMs) {
|
|
2572
2761
|
await trackRoomLeave({
|
|
2573
2762
|
link: this.link,
|
|
2574
2763
|
roomId,
|
|
2575
2764
|
trackedRooms: this.subscribedRooms,
|
|
2576
2765
|
onLeft: async (leftRoomId) => {
|
|
2577
|
-
await this.executions.get(leftRoomId)?.stop();
|
|
2766
|
+
await this.executions.get(leftRoomId)?.stop(timeoutMs);
|
|
2578
2767
|
this.contexts.delete(leftRoomId);
|
|
2579
2768
|
this.executions.delete(leftRoomId);
|
|
2580
2769
|
await this.onSessionCleanup(leftRoomId);
|
|
@@ -2611,6 +2800,23 @@ var AgentRuntime = class {
|
|
|
2611
2800
|
}
|
|
2612
2801
|
}
|
|
2613
2802
|
};
|
|
2803
|
+
function syntheticRuntimeFailureEvent(agentId) {
|
|
2804
|
+
return {
|
|
2805
|
+
type: "message_created",
|
|
2806
|
+
roomId: null,
|
|
2807
|
+
payload: {
|
|
2808
|
+
id: "runtime-failed",
|
|
2809
|
+
content: "",
|
|
2810
|
+
sender_id: agentId,
|
|
2811
|
+
sender_type: "Agent",
|
|
2812
|
+
sender_name: null,
|
|
2813
|
+
message_type: "text",
|
|
2814
|
+
metadata: {},
|
|
2815
|
+
inserted_at: (/* @__PURE__ */ new Date(0)).toISOString(),
|
|
2816
|
+
updated_at: (/* @__PURE__ */ new Date(0)).toISOString()
|
|
2817
|
+
}
|
|
2818
|
+
};
|
|
2819
|
+
}
|
|
2614
2820
|
function assertNever(value) {
|
|
2615
2821
|
throw new Error(`Unhandled platform event: ${JSON.stringify(value)}`);
|
|
2616
2822
|
}
|
|
@@ -2762,14 +2968,6 @@ var ContactToolsImpl = class {
|
|
|
2762
2968
|
};
|
|
2763
2969
|
|
|
2764
2970
|
// src/runtime/tools/ContactCallbackTools.ts
|
|
2765
|
-
function toParticipantRecord(participant) {
|
|
2766
|
-
return {
|
|
2767
|
-
id: participant.id,
|
|
2768
|
-
name: participant.name,
|
|
2769
|
-
type: participant.type,
|
|
2770
|
-
handle: participant.handle ?? null
|
|
2771
|
-
};
|
|
2772
|
-
}
|
|
2773
2971
|
function normalizePage(value, fallback) {
|
|
2774
2972
|
return Number.isInteger(value) && value && value > 0 ? value : fallback;
|
|
2775
2973
|
}
|
|
@@ -2813,8 +3011,7 @@ var ContactCallbackTools = class {
|
|
|
2813
3011
|
{
|
|
2814
3012
|
content,
|
|
2815
3013
|
...normalizedMentions ? { mentions: normalizedMentions } : {}
|
|
2816
|
-
}
|
|
2817
|
-
DEFAULT_REQUEST_OPTIONS
|
|
3014
|
+
}
|
|
2818
3015
|
);
|
|
2819
3016
|
}
|
|
2820
3017
|
async sendEvent(content, messageType, metadata) {
|
|
@@ -2828,8 +3025,7 @@ var ContactCallbackTools = class {
|
|
|
2828
3025
|
content,
|
|
2829
3026
|
messageType,
|
|
2830
3027
|
...metadata ? { metadata } : {}
|
|
2831
|
-
}
|
|
2832
|
-
DEFAULT_REQUEST_OPTIONS
|
|
3028
|
+
}
|
|
2833
3029
|
);
|
|
2834
3030
|
}
|
|
2835
3031
|
async addParticipant() {
|
|
@@ -2843,7 +3039,7 @@ var ContactCallbackTools = class {
|
|
|
2843
3039
|
if (!this.rest.listChatParticipants) {
|
|
2844
3040
|
throw new UnsupportedFeatureError("Participant listing is not available in current REST adapter");
|
|
2845
3041
|
}
|
|
2846
|
-
return (await this.rest.listChatParticipants(roomId, DEFAULT_REQUEST_OPTIONS)).map(
|
|
3042
|
+
return (await this.rest.listChatParticipants(roomId, DEFAULT_REQUEST_OPTIONS)).map(toParticipantRecordFromRest);
|
|
2847
3043
|
}
|
|
2848
3044
|
async createChatroom(taskId) {
|
|
2849
3045
|
const chat = await this.rest.createChat(taskId, DEFAULT_REQUEST_OPTIONS);
|
|
@@ -2933,48 +3129,48 @@ var ContactCallbackTools = class {
|
|
|
2933
3129
|
}
|
|
2934
3130
|
async executeToolCall(toolName, toolArgs) {
|
|
2935
3131
|
switch (toolName) {
|
|
2936
|
-
case "
|
|
3132
|
+
case "band_send_message":
|
|
2937
3133
|
return this.sendMessage(
|
|
2938
3134
|
String(toolArgs.content ?? ""),
|
|
2939
3135
|
toolArgs.mentions
|
|
2940
3136
|
);
|
|
2941
|
-
case "
|
|
3137
|
+
case "band_send_event":
|
|
2942
3138
|
return this.sendEvent(
|
|
2943
3139
|
String(toolArgs.content ?? ""),
|
|
2944
3140
|
String(toolArgs.message_type ?? "task"),
|
|
2945
3141
|
toolArgs.metadata
|
|
2946
3142
|
);
|
|
2947
|
-
case "
|
|
3143
|
+
case "band_get_participants":
|
|
2948
3144
|
return this.getParticipants();
|
|
2949
|
-
case "
|
|
3145
|
+
case "band_create_chatroom":
|
|
2950
3146
|
return this.createChatroom(typeof toolArgs.task_id === "string" ? toolArgs.task_id : void 0);
|
|
2951
|
-
case "
|
|
3147
|
+
case "band_lookup_peers":
|
|
2952
3148
|
return this.lookupPeers(
|
|
2953
3149
|
typeof toolArgs.page === "number" ? toolArgs.page : void 0,
|
|
2954
3150
|
typeof toolArgs.page_size === "number" ? toolArgs.page_size : void 0
|
|
2955
3151
|
);
|
|
2956
|
-
case "
|
|
3152
|
+
case "band_list_contacts":
|
|
2957
3153
|
return this.listContacts({
|
|
2958
3154
|
page: typeof toolArgs.page === "number" ? toolArgs.page : void 0,
|
|
2959
3155
|
pageSize: typeof toolArgs.page_size === "number" ? toolArgs.page_size : void 0
|
|
2960
3156
|
});
|
|
2961
|
-
case "
|
|
3157
|
+
case "band_add_contact":
|
|
2962
3158
|
return this.addContact({
|
|
2963
3159
|
handle: String(toolArgs.handle ?? ""),
|
|
2964
3160
|
...typeof toolArgs.message === "string" ? { message: toolArgs.message } : {}
|
|
2965
3161
|
});
|
|
2966
|
-
case "
|
|
3162
|
+
case "band_remove_contact":
|
|
2967
3163
|
if (typeof toolArgs.contact_id === "string") {
|
|
2968
3164
|
return this.removeContact({ target: "contactId", contactId: toolArgs.contact_id });
|
|
2969
3165
|
}
|
|
2970
3166
|
return this.removeContact({ target: "handle", handle: String(toolArgs.handle ?? "") });
|
|
2971
|
-
case "
|
|
3167
|
+
case "band_list_contact_requests":
|
|
2972
3168
|
return this.listContactRequests({
|
|
2973
3169
|
page: typeof toolArgs.page === "number" ? toolArgs.page : void 0,
|
|
2974
3170
|
pageSize: typeof toolArgs.page_size === "number" ? toolArgs.page_size : void 0,
|
|
2975
3171
|
sentStatus: typeof toolArgs.sent_status === "string" ? toolArgs.sent_status : void 0
|
|
2976
3172
|
});
|
|
2977
|
-
case "
|
|
3173
|
+
case "band_respond_contact_request":
|
|
2978
3174
|
if (typeof toolArgs.request_id === "string") {
|
|
2979
3175
|
return this.respondContactRequest({
|
|
2980
3176
|
action: String(toolArgs.action ?? "approve"),
|
|
@@ -2987,15 +3183,15 @@ var ContactCallbackTools = class {
|
|
|
2987
3183
|
target: "handle",
|
|
2988
3184
|
handle: String(toolArgs.handle ?? "")
|
|
2989
3185
|
});
|
|
2990
|
-
case "
|
|
3186
|
+
case "band_list_memories":
|
|
2991
3187
|
return this.listMemories(toolArgs);
|
|
2992
|
-
case "
|
|
3188
|
+
case "band_store_memory":
|
|
2993
3189
|
return this.storeMemory(toolArgs);
|
|
2994
|
-
case "
|
|
3190
|
+
case "band_get_memory":
|
|
2995
3191
|
return this.getMemory(String(toolArgs.memory_id ?? ""));
|
|
2996
|
-
case "
|
|
3192
|
+
case "band_supersede_memory":
|
|
2997
3193
|
return this.supersedeMemory(String(toolArgs.memory_id ?? ""));
|
|
2998
|
-
case "
|
|
3194
|
+
case "band_archive_memory":
|
|
2999
3195
|
return this.archiveMemory(String(toolArgs.memory_id ?? ""));
|
|
3000
3196
|
default:
|
|
3001
3197
|
throw new UnsupportedFeatureError(`Unsupported tool call for contact callback: ${toolName}`);
|
|
@@ -3009,14 +3205,14 @@ var HUB_ROOM_SYSTEM_PROMPT = `## OVERRIDE: Contact Management Mode
|
|
|
3009
3205
|
|
|
3010
3206
|
This is your CONTACTS HUB - a dedicated room for managing contact requests.
|
|
3011
3207
|
|
|
3012
|
-
**IMPORTANT: Do NOT delegate or add participants here.** You handle contact events DIRECTLY using the contact tools below. Do NOT call
|
|
3208
|
+
**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.
|
|
3013
3209
|
|
|
3014
3210
|
## Your Role
|
|
3015
3211
|
|
|
3016
3212
|
1. **Review incoming contact requests** - When you see a [Contact Request] message, evaluate it
|
|
3017
3213
|
2. **Take action** - Use the contact tools to respond:
|
|
3018
|
-
- \`
|
|
3019
|
-
- \`
|
|
3214
|
+
- \`band_respond_contact_request(action="approve", request_id="...")\` to accept
|
|
3215
|
+
- \`band_respond_contact_request(action="reject", request_id="...")\` to decline
|
|
3020
3216
|
3. **Report your decision** - Send a thought event explaining what you did
|
|
3021
3217
|
|
|
3022
3218
|
## Example
|
|
@@ -3025,14 +3221,14 @@ This is your CONTACTS HUB - a dedicated room for managing contact requests.
|
|
|
3025
3221
|
Request ID: abc-123
|
|
3026
3222
|
|
|
3027
3223
|
Your response:
|
|
3028
|
-
1.
|
|
3029
|
-
2.
|
|
3030
|
-
3.
|
|
3224
|
+
1. band_send_event("Received contact request from Alice. Approving.", message_type="thought")
|
|
3225
|
+
2. band_respond_contact_request(action="approve", request_id="abc-123")
|
|
3226
|
+
3. band_send_event("Approved contact request from Alice (@alice)", message_type="thought")
|
|
3031
3227
|
|
|
3032
3228
|
## Contact Tools (use these, NOT participant tools)
|
|
3033
|
-
- \`
|
|
3034
|
-
- \`
|
|
3035
|
-
- \`
|
|
3229
|
+
- \`band_respond_contact_request(action, request_id)\` - Approve/reject requests
|
|
3230
|
+
- \`band_list_contact_requests()\` - List pending requests
|
|
3231
|
+
- \`band_list_contacts()\` - List current contacts`;
|
|
3036
3232
|
var ContactEventHandlerError = class extends Error {
|
|
3037
3233
|
retryable;
|
|
3038
3234
|
stage;
|
|
@@ -3360,6 +3556,8 @@ function normalizeHandle(handle) {
|
|
|
3360
3556
|
}
|
|
3361
3557
|
|
|
3362
3558
|
// src/runtime/PlatformRuntime.ts
|
|
3559
|
+
var MAX_MESSAGE_RETRIES = 4294967295;
|
|
3560
|
+
var isValidRetryCount = (value) => Number.isInteger(value) && value >= 0 && value <= MAX_MESSAGE_RETRIES;
|
|
3363
3561
|
var PlatformRuntime = class {
|
|
3364
3562
|
_agentId;
|
|
3365
3563
|
_apiKey;
|
|
@@ -3396,6 +3594,12 @@ var PlatformRuntime = class {
|
|
|
3396
3594
|
"apiKey is required and must be a non-empty string. Use loadAgentConfig() to load credentials from agent_config.yaml."
|
|
3397
3595
|
);
|
|
3398
3596
|
}
|
|
3597
|
+
const maxMessageRetries = options.sessionConfig?.maxMessageRetries;
|
|
3598
|
+
if (maxMessageRetries !== void 0 && !isValidRetryCount(maxMessageRetries)) {
|
|
3599
|
+
throw new ValidationError(
|
|
3600
|
+
`sessionConfig.maxMessageRetries must be an integer between 0 and ${MAX_MESSAGE_RETRIES}, got ${maxMessageRetries}.`
|
|
3601
|
+
);
|
|
3602
|
+
}
|
|
3399
3603
|
this._agentId = options.agentId;
|
|
3400
3604
|
this._apiKey = options.apiKey;
|
|
3401
3605
|
this._wsUrl = options.wsUrl;
|
|
@@ -3448,12 +3652,13 @@ var PlatformRuntime = class {
|
|
|
3448
3652
|
}
|
|
3449
3653
|
async doInitialize() {
|
|
3450
3654
|
if (!this.linkInstance) {
|
|
3451
|
-
this.linkInstance = new
|
|
3655
|
+
this.linkInstance = new BandLink({
|
|
3452
3656
|
...this.linkOptions,
|
|
3453
3657
|
agentId: this._agentId,
|
|
3454
3658
|
apiKey: this._apiKey,
|
|
3455
3659
|
wsUrl: this._wsUrl,
|
|
3456
|
-
restUrl: this._restUrl
|
|
3660
|
+
restUrl: this._restUrl,
|
|
3661
|
+
logger: this.logger
|
|
3457
3662
|
});
|
|
3458
3663
|
}
|
|
3459
3664
|
if (this.configuredIdentity) {
|
|
@@ -3670,10 +3875,10 @@ async function runWithGracefulShutdown(agent, options) {
|
|
|
3670
3875
|
}
|
|
3671
3876
|
|
|
3672
3877
|
export {
|
|
3673
|
-
|
|
3878
|
+
deriveDefaultRestUrl,
|
|
3879
|
+
BandLink,
|
|
3674
3880
|
Execution,
|
|
3675
3881
|
AgentTools,
|
|
3676
|
-
MessageRetryTracker,
|
|
3677
3882
|
ExecutionContext,
|
|
3678
3883
|
trackRoomJoin,
|
|
3679
3884
|
trackRoomLeave,
|