@band-ai/sdk 0.2.0 → 0.3.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/dist/{ClaudeSDKAdapter-Cna5P1dR.d.ts → ClaudeSDKAdapter-CXud2DBE.d.ts} +2 -1
- package/dist/{ClaudeSDKAdapter-BY4zEbCl.d.cts → ClaudeSDKAdapter-Cx6zhSaG.d.cts} +2 -1
- package/dist/adapters.cjs +347 -172
- package/dist/adapters.d.cts +22 -5
- package/dist/adapters.d.ts +22 -5
- package/dist/adapters.js +204 -60
- package/dist/chunk-F63TPOI3.js +29 -0
- package/dist/{chunk-3BKAC4OZ.js → chunk-JADCAMZY.js} +8 -0
- package/dist/{chunk-N2QTUMGO.js → chunk-JJF44XWA.js} +365 -249
- package/dist/{chunk-TLBCKDQO.js → chunk-K5KAEYPF.js} +2 -2
- package/dist/{chunk-ZZJRRBPQ.js → chunk-KXJ74J73.js} +1 -1
- package/dist/{chunk-NMZLX7TP.js → chunk-MQBBWYRP.js} +52 -45
- package/dist/{chunk-RV76SKFT.js → chunk-MQY74R7D.js} +2 -20
- package/dist/{chunk-LBWKISBS.js → chunk-OH6VVVKT.js} +21 -0
- package/dist/{chunk-EAI2M3WM.js → chunk-W5IAI6Z4.js} +1 -1
- package/dist/{chunk-4IAXZBXX.js → chunk-WA7CHXUS.js} +2 -2
- package/dist/{chunk-NT7TBFX2.js → chunk-XWHWJP4I.js} +2 -2
- package/dist/{chunk-WLCM2OXG.js → chunk-YJ42AZO4.js} +1 -1
- package/dist/converters.js +6 -5
- package/dist/index.cjs +405 -277
- package/dist/index.d.cts +5 -5
- package/dist/index.d.ts +5 -5
- package/dist/index.js +12 -11
- package/dist/mcp-claude.js +4 -3
- package/dist/mcp.js +5 -4
- package/dist/rest.js +3 -3
- package/dist/runtime.cjs +387 -362
- package/dist/runtime.d.cts +10 -39
- package/dist/runtime.d.ts +10 -39
- package/dist/runtime.js +20 -142
- package/dist/{schemas-BotL3ZLH.d.cts → schemas-BgJhlqqO.d.cts} +57 -13
- package/dist/{schemas-DBsC4J1g.d.ts → schemas-C4mK1WSs.d.ts} +57 -13
- package/dist/testing.cjs +11 -0
- package/dist/testing.js +8 -1
- package/dist/{types-D958TC71.d.cts → types-BNNtOXu3.d.cts} +1 -1
- package/dist/{types-BTN_ZETM.d.ts → types-DFUtpxah.d.ts} +1 -1
- package/package.json +2 -2
|
@@ -6,9 +6,8 @@ import {
|
|
|
6
6
|
buildParticipantsMessage,
|
|
7
7
|
toParticipantRecord,
|
|
8
8
|
toParticipantRecordFromRest
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-OH6VVVKT.js";
|
|
10
10
|
import {
|
|
11
|
-
CHAT_EVENT_TYPES,
|
|
12
11
|
CHAT_TOOL_NAMES,
|
|
13
12
|
MEMORY_LIST_SCOPES,
|
|
14
13
|
MEMORY_SEGMENTS,
|
|
@@ -18,7 +17,6 @@ import {
|
|
|
18
17
|
MEMORY_TOOL_NAMES,
|
|
19
18
|
MEMORY_TYPES,
|
|
20
19
|
TOOL_MODELS,
|
|
21
|
-
assertChatEventType,
|
|
22
20
|
expectedList,
|
|
23
21
|
expectedMemoryTypesForSystem,
|
|
24
22
|
getToolDescription,
|
|
@@ -30,7 +28,11 @@ import {
|
|
|
30
28
|
isMemoryType,
|
|
31
29
|
isMemoryTypeForSystem,
|
|
32
30
|
memoryTypeForSystemError
|
|
33
|
-
} from "./chunk-
|
|
31
|
+
} from "./chunk-MQY74R7D.js";
|
|
32
|
+
import {
|
|
33
|
+
CHAT_EVENT_TYPES,
|
|
34
|
+
assertChatEventType
|
|
35
|
+
} from "./chunk-F63TPOI3.js";
|
|
34
36
|
import {
|
|
35
37
|
WebSocketDisconnectError,
|
|
36
38
|
genericCloseReason,
|
|
@@ -41,12 +43,13 @@ import {
|
|
|
41
43
|
DEFAULT_REQUEST_OPTIONS,
|
|
42
44
|
FernRestAdapter,
|
|
43
45
|
fetchPaginated
|
|
44
|
-
} from "./chunk-
|
|
46
|
+
} from "./chunk-W5IAI6Z4.js";
|
|
45
47
|
import {
|
|
46
48
|
DEFAULT_AGENT_TOOLS_CAPABILITIES,
|
|
47
49
|
createToolExecutorError,
|
|
50
|
+
isStructuredToolFailure,
|
|
48
51
|
isToolExecutorError
|
|
49
|
-
} from "./chunk-
|
|
52
|
+
} from "./chunk-JADCAMZY.js";
|
|
50
53
|
import {
|
|
51
54
|
NoopLogger
|
|
52
55
|
} from "./chunk-OD2G5LFQ.js";
|
|
@@ -1279,10 +1282,12 @@ var AgentTools = class {
|
|
|
1279
1282
|
roster;
|
|
1280
1283
|
adapterTools;
|
|
1281
1284
|
toolHandlers;
|
|
1285
|
+
logger;
|
|
1282
1286
|
constructor(options) {
|
|
1283
1287
|
this.roomId = options.roomId;
|
|
1284
1288
|
this.rest = options.rest;
|
|
1285
1289
|
this.roster = options.roster ?? new ParticipantRoster();
|
|
1290
|
+
this.logger = options.logger ?? new NoopLogger();
|
|
1286
1291
|
this.capabilities = {
|
|
1287
1292
|
...DEFAULT_AGENT_TOOLS_CAPABILITIES,
|
|
1288
1293
|
...options.capabilities
|
|
@@ -1312,14 +1317,23 @@ var AgentTools = class {
|
|
|
1312
1317
|
}
|
|
1313
1318
|
async sendEvent(content, messageType, metadata) {
|
|
1314
1319
|
assertChatEventType(messageType);
|
|
1315
|
-
|
|
1316
|
-
this.
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1320
|
+
try {
|
|
1321
|
+
return await this.rest.createChatEvent(
|
|
1322
|
+
this.roomId,
|
|
1323
|
+
{
|
|
1324
|
+
content,
|
|
1325
|
+
messageType,
|
|
1326
|
+
metadata
|
|
1327
|
+
}
|
|
1328
|
+
);
|
|
1329
|
+
} catch (error) {
|
|
1330
|
+
try {
|
|
1331
|
+
this.logger.warn("chat event send failed", { roomId: this.roomId, messageType, error });
|
|
1332
|
+
} catch {
|
|
1321
1333
|
}
|
|
1322
|
-
|
|
1334
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
1335
|
+
return { ok: false, status: "failed", message };
|
|
1336
|
+
}
|
|
1323
1337
|
}
|
|
1324
1338
|
async createChatroom(taskId) {
|
|
1325
1339
|
const room = await this.rest.createChat(taskId, DEFAULT_REQUEST_OPTIONS);
|
|
@@ -1422,7 +1436,16 @@ var AgentTools = class {
|
|
|
1422
1436
|
});
|
|
1423
1437
|
}
|
|
1424
1438
|
try {
|
|
1425
|
-
|
|
1439
|
+
const result = await handler(arguments_);
|
|
1440
|
+
if (toolName === "band_send_event" && isStructuredToolFailure(result) && !isToolExecutorError(result)) {
|
|
1441
|
+
return createToolExecutorError({
|
|
1442
|
+
errorType: "ToolExecutionError",
|
|
1443
|
+
toolName,
|
|
1444
|
+
message: result.message,
|
|
1445
|
+
legacyMessage: `Error executing ${toolName}: ${result.message}`
|
|
1446
|
+
});
|
|
1447
|
+
}
|
|
1448
|
+
return result;
|
|
1426
1449
|
} catch (error) {
|
|
1427
1450
|
if (isToolExecutorError(error)) {
|
|
1428
1451
|
return error;
|
|
@@ -2123,7 +2146,8 @@ var ExecutionContext = class {
|
|
|
2123
2146
|
roomId: this.roomId,
|
|
2124
2147
|
rest: this.link.rest,
|
|
2125
2148
|
roster: this.roster,
|
|
2126
|
-
capabilities: this.link.capabilities
|
|
2149
|
+
capabilities: this.link.capabilities,
|
|
2150
|
+
logger: options.logger ?? new NoopLogger()
|
|
2127
2151
|
});
|
|
2128
2152
|
this.adapterTools = this.tools.getAdapterTools();
|
|
2129
2153
|
}
|
|
@@ -2371,70 +2395,278 @@ var ExecutionContext = class {
|
|
|
2371
2395
|
}
|
|
2372
2396
|
};
|
|
2373
2397
|
|
|
2398
|
+
// src/runtime/rooms/RoomPresence.ts
|
|
2399
|
+
import { RoomRoster } from "@band-ai/band-sdk-core";
|
|
2400
|
+
|
|
2374
2401
|
// src/runtime/rooms/subscriptions.ts
|
|
2375
|
-
function
|
|
2376
|
-
|
|
2402
|
+
async function hydrateExistingRooms(options) {
|
|
2403
|
+
try {
|
|
2404
|
+
const rooms = await options.link.listAllChats(void 0, options.requestOptions);
|
|
2405
|
+
for (const room of rooms) {
|
|
2406
|
+
const roomId = typeof room.id === "string" ? room.id : null;
|
|
2407
|
+
if (!roomId) {
|
|
2408
|
+
continue;
|
|
2409
|
+
}
|
|
2410
|
+
if (options.roomFilter && !options.roomFilter(room)) {
|
|
2411
|
+
continue;
|
|
2412
|
+
}
|
|
2413
|
+
await options.onRoom(roomId, room);
|
|
2414
|
+
}
|
|
2415
|
+
} catch (error) {
|
|
2416
|
+
if (error instanceof UnsupportedFeatureError) {
|
|
2417
|
+
return;
|
|
2418
|
+
}
|
|
2419
|
+
if (options.onError) {
|
|
2420
|
+
await options.onError(error);
|
|
2421
|
+
return;
|
|
2422
|
+
}
|
|
2423
|
+
throw error;
|
|
2424
|
+
}
|
|
2377
2425
|
}
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2426
|
+
|
|
2427
|
+
// src/runtime/rooms/RoomPresence.ts
|
|
2428
|
+
var RoomPresence = class {
|
|
2429
|
+
roster = new RoomRoster();
|
|
2430
|
+
onRoomJoined = null;
|
|
2431
|
+
onRoomLeft = null;
|
|
2432
|
+
onRoomEvent = null;
|
|
2433
|
+
onContactEvent = null;
|
|
2434
|
+
link;
|
|
2435
|
+
roomFilter;
|
|
2436
|
+
autoSubscribeExistingRooms;
|
|
2437
|
+
logger;
|
|
2438
|
+
eventController = null;
|
|
2439
|
+
eventTask = null;
|
|
2440
|
+
contactsSubscribed = false;
|
|
2441
|
+
lifecycle = Promise.resolve();
|
|
2442
|
+
admissionInFlight = /* @__PURE__ */ new Map();
|
|
2443
|
+
// Read once by `admitRoomOrThrow` right after a failed admission; a
|
|
2444
|
+
// subsequent successful subscribe clears it so a caller never attributes
|
|
2445
|
+
// today's failure to a stale reason from an earlier attempt.
|
|
2446
|
+
lastSubscribeError = /* @__PURE__ */ new Map();
|
|
2447
|
+
constructor(options) {
|
|
2448
|
+
this.link = options.link;
|
|
2449
|
+
this.roomFilter = options.roomFilter;
|
|
2450
|
+
this.autoSubscribeExistingRooms = options.autoSubscribeExistingRooms ?? true;
|
|
2451
|
+
this.logger = options.logger ?? new NoopLogger();
|
|
2381
2452
|
}
|
|
2382
|
-
|
|
2383
|
-
return
|
|
2453
|
+
async start() {
|
|
2454
|
+
return this.serialize(() => this.startBody());
|
|
2384
2455
|
}
|
|
2385
|
-
|
|
2386
|
-
return
|
|
2456
|
+
async stop() {
|
|
2457
|
+
return this.serialize(() => this.stopBody());
|
|
2387
2458
|
}
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
if (options.onJoined) {
|
|
2391
|
-
await options.onJoined(options.roomId, options.payload);
|
|
2459
|
+
async [Symbol.asyncDispose]() {
|
|
2460
|
+
await this.stop();
|
|
2392
2461
|
}
|
|
2393
|
-
|
|
2394
|
-
|
|
2395
|
-
async function trackRoomLeave(options) {
|
|
2396
|
-
if (!hasRoomId(options.roomId) || !options.trackedRooms.has(options.roomId)) {
|
|
2397
|
-
return false;
|
|
2462
|
+
abortEventLoop() {
|
|
2463
|
+
this.eventController?.abort();
|
|
2398
2464
|
}
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
if (options.onLeft) {
|
|
2402
|
-
await options.onLeft(options.roomId);
|
|
2465
|
+
async waitUntilStopped() {
|
|
2466
|
+
await this.eventTask;
|
|
2403
2467
|
}
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2408
|
-
|
|
2409
|
-
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2468
|
+
async admitRoom(roomId, payload, notify = true) {
|
|
2469
|
+
const ticket = this.roster.beginRoomAdmission(roomId, true);
|
|
2470
|
+
const admitted = ticket === void 0 ? await this.awaitInFlightAdmission(roomId) : await this.performAdmission(roomId, ticket);
|
|
2471
|
+
if (admitted && notify) {
|
|
2472
|
+
await this.onRoomJoined?.(roomId, payload);
|
|
2473
|
+
}
|
|
2474
|
+
return admitted;
|
|
2475
|
+
}
|
|
2476
|
+
/** Admit a room for a caller that needs to know *why* a failed subscribe happened, not just that it did. */
|
|
2477
|
+
async admitRoomOrThrow(roomId) {
|
|
2478
|
+
if (!await this.admitRoom(roomId, {}, false)) {
|
|
2479
|
+
throw new TransportError(`Failed to subscribe to room ${roomId}`, this.lastSubscribeError.get(roomId));
|
|
2480
|
+
}
|
|
2481
|
+
}
|
|
2482
|
+
async awaitInFlightAdmission(roomId) {
|
|
2483
|
+
const inFlight = this.admissionInFlight.get(roomId);
|
|
2484
|
+
return inFlight ? await inFlight : this.roster.roomMembership(roomId) === "admitted";
|
|
2485
|
+
}
|
|
2486
|
+
async performAdmission(roomId, ticket) {
|
|
2487
|
+
const admission = this.completeAdmission(roomId, ticket);
|
|
2488
|
+
this.admissionInFlight.set(roomId, admission);
|
|
2489
|
+
try {
|
|
2490
|
+
return await admission;
|
|
2491
|
+
} finally {
|
|
2492
|
+
if (this.admissionInFlight.get(roomId) === admission) {
|
|
2493
|
+
this.admissionInFlight.delete(roomId);
|
|
2494
|
+
}
|
|
2495
|
+
}
|
|
2496
|
+
}
|
|
2497
|
+
async completeAdmission(roomId, ticket) {
|
|
2498
|
+
let succeeded = false;
|
|
2499
|
+
let admitted = false;
|
|
2500
|
+
let subscribeError;
|
|
2501
|
+
try {
|
|
2502
|
+
await this.link.subscribeRoom(roomId);
|
|
2503
|
+
succeeded = true;
|
|
2504
|
+
} catch (error) {
|
|
2505
|
+
this.logger.warn("RoomPresence failed to subscribe room", { roomId, error });
|
|
2506
|
+
subscribeError = error;
|
|
2507
|
+
} finally {
|
|
2508
|
+
admitted = this.roster.recordRoomAdmission(roomId, ticket, succeeded);
|
|
2509
|
+
}
|
|
2510
|
+
const roomIsAdmitted = admitted || this.roster.roomMembership(roomId) === "admitted";
|
|
2511
|
+
if (succeeded && !admitted && !roomIsAdmitted) {
|
|
2512
|
+
this.logger.debug("RoomPresence admission ticket went stale", { roomId });
|
|
2513
|
+
await this.unsubscribeRoom(roomId);
|
|
2514
|
+
}
|
|
2515
|
+
if (roomIsAdmitted) {
|
|
2516
|
+
this.lastSubscribeError.delete(roomId);
|
|
2517
|
+
} else if (subscribeError !== void 0) {
|
|
2518
|
+
this.lastSubscribeError.set(roomId, subscribeError);
|
|
2519
|
+
}
|
|
2520
|
+
return roomIsAdmitted;
|
|
2521
|
+
}
|
|
2522
|
+
async serialize(body) {
|
|
2523
|
+
const run = this.lifecycle.then(body, body);
|
|
2524
|
+
this.lifecycle = run.then(
|
|
2525
|
+
() => void 0,
|
|
2526
|
+
() => void 0
|
|
2413
2527
|
);
|
|
2414
|
-
|
|
2415
|
-
|
|
2416
|
-
|
|
2417
|
-
|
|
2418
|
-
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2528
|
+
return run;
|
|
2529
|
+
}
|
|
2530
|
+
async startBody() {
|
|
2531
|
+
if (this.eventTask) {
|
|
2532
|
+
throw new RuntimeStateError("RoomPresence is already started");
|
|
2533
|
+
}
|
|
2534
|
+
if (!this.link.isConnected()) {
|
|
2535
|
+
await this.link.connect();
|
|
2536
|
+
}
|
|
2537
|
+
const contactsReady = this.subscribeContacts();
|
|
2538
|
+
try {
|
|
2539
|
+
await this.link.subscribeAgentRooms();
|
|
2540
|
+
} catch (error) {
|
|
2541
|
+
this.logger.warn("RoomPresence failed to subscribe agent_rooms channel, continuing without it", {
|
|
2542
|
+
error
|
|
2422
2543
|
});
|
|
2423
2544
|
}
|
|
2424
|
-
|
|
2425
|
-
|
|
2545
|
+
if (this.autoSubscribeExistingRooms) {
|
|
2546
|
+
await this.subscribeExistingRooms();
|
|
2547
|
+
}
|
|
2548
|
+
await contactsReady;
|
|
2549
|
+
this.eventController = new AbortController();
|
|
2550
|
+
this.eventTask = this.consumeEvents(this.eventController.signal);
|
|
2551
|
+
}
|
|
2552
|
+
async subscribeContacts() {
|
|
2553
|
+
if (!this.link.capabilities.contacts) {
|
|
2426
2554
|
return;
|
|
2427
2555
|
}
|
|
2428
|
-
|
|
2429
|
-
await
|
|
2556
|
+
try {
|
|
2557
|
+
await this.link.subscribeAgentContacts();
|
|
2558
|
+
this.contactsSubscribed = true;
|
|
2559
|
+
} catch (error) {
|
|
2560
|
+
this.logger.warn("RoomPresence failed to subscribe agent_contacts channel, continuing without it", {
|
|
2561
|
+
error
|
|
2562
|
+
});
|
|
2563
|
+
}
|
|
2564
|
+
}
|
|
2565
|
+
async stopBody() {
|
|
2566
|
+
this.abortEventLoop();
|
|
2567
|
+
await this.eventTask?.catch(() => void 0);
|
|
2568
|
+
this.eventTask = null;
|
|
2569
|
+
this.eventController = null;
|
|
2570
|
+
if (this.contactsSubscribed) {
|
|
2571
|
+
try {
|
|
2572
|
+
await this.link.unsubscribeAgentContacts();
|
|
2573
|
+
} catch (error) {
|
|
2574
|
+
this.logger.warn("RoomPresence failed to unsubscribe agent_contacts channel", { error });
|
|
2575
|
+
} finally {
|
|
2576
|
+
this.contactsSubscribed = false;
|
|
2577
|
+
}
|
|
2578
|
+
}
|
|
2579
|
+
const roomIds = this.roster.trackedRoomIds();
|
|
2580
|
+
this.roster.clear();
|
|
2581
|
+
this.lastSubscribeError.clear();
|
|
2582
|
+
await Promise.all(
|
|
2583
|
+
roomIds.map(async (roomId) => {
|
|
2584
|
+
await this.unsubscribeRoom(roomId);
|
|
2585
|
+
await this.onRoomLeft?.(roomId);
|
|
2586
|
+
})
|
|
2587
|
+
);
|
|
2588
|
+
}
|
|
2589
|
+
async consumeEvents(signal) {
|
|
2590
|
+
while (!signal.aborted) {
|
|
2591
|
+
const event = await this.link.nextEvent(signal);
|
|
2592
|
+
if (!event) {
|
|
2593
|
+
return;
|
|
2594
|
+
}
|
|
2595
|
+
switch (event.type) {
|
|
2596
|
+
case "room_added":
|
|
2597
|
+
await this.handleRoomAdded(event.roomId, event.payload);
|
|
2598
|
+
break;
|
|
2599
|
+
case "room_removed":
|
|
2600
|
+
case "room_deleted":
|
|
2601
|
+
await this.handleRoomRemoved(event.roomId);
|
|
2602
|
+
break;
|
|
2603
|
+
case "contact_request_received":
|
|
2604
|
+
case "contact_request_updated":
|
|
2605
|
+
case "contact_added":
|
|
2606
|
+
case "contact_removed":
|
|
2607
|
+
await this.onContactEvent?.(event);
|
|
2608
|
+
break;
|
|
2609
|
+
case "message_created":
|
|
2610
|
+
case "participant_added":
|
|
2611
|
+
case "participant_removed":
|
|
2612
|
+
if (event.roomId && this.roster.roomMembership(event.roomId) === "admitted") {
|
|
2613
|
+
await this.onRoomEvent?.(event.roomId, event);
|
|
2614
|
+
}
|
|
2615
|
+
break;
|
|
2616
|
+
default:
|
|
2617
|
+
assertNever(event);
|
|
2618
|
+
}
|
|
2619
|
+
}
|
|
2620
|
+
}
|
|
2621
|
+
async handleRoomAdded(roomId, payload) {
|
|
2622
|
+
if (!roomId) {
|
|
2430
2623
|
return;
|
|
2431
2624
|
}
|
|
2432
|
-
|
|
2625
|
+
if (this.roomFilter && !this.roomFilter(payload)) {
|
|
2626
|
+
return;
|
|
2627
|
+
}
|
|
2628
|
+
await this.admitRoom(roomId, payload);
|
|
2629
|
+
}
|
|
2630
|
+
async handleRoomRemoved(roomId) {
|
|
2631
|
+
if (!roomId) {
|
|
2632
|
+
return;
|
|
2633
|
+
}
|
|
2634
|
+
await this.unsubscribeRoom(roomId);
|
|
2635
|
+
this.lastSubscribeError.delete(roomId);
|
|
2636
|
+
if (!this.roster.recordRoomRemoved(roomId)) {
|
|
2637
|
+
this.logger.debug("RoomPresence ignoring removal for untracked room", { roomId });
|
|
2638
|
+
return;
|
|
2639
|
+
}
|
|
2640
|
+
await this.onRoomLeft?.(roomId);
|
|
2641
|
+
}
|
|
2642
|
+
async unsubscribeRoom(roomId) {
|
|
2643
|
+
try {
|
|
2644
|
+
await this.link.unsubscribeRoom(roomId);
|
|
2645
|
+
} catch (error) {
|
|
2646
|
+
this.logger.warn("RoomPresence failed to unsubscribe room", { roomId, error });
|
|
2647
|
+
}
|
|
2648
|
+
}
|
|
2649
|
+
async subscribeExistingRooms() {
|
|
2650
|
+
await hydrateExistingRooms({
|
|
2651
|
+
link: this.link,
|
|
2652
|
+
roomFilter: this.roomFilter,
|
|
2653
|
+
requestOptions: DEFAULT_REQUEST_OPTIONS,
|
|
2654
|
+
onRoom: async (roomId, payload) => {
|
|
2655
|
+
await this.admitRoom(roomId, payload);
|
|
2656
|
+
},
|
|
2657
|
+
onError: (error) => {
|
|
2658
|
+
this.logger.warn("RoomPresence failed to subscribe existing rooms", { error });
|
|
2659
|
+
}
|
|
2660
|
+
});
|
|
2433
2661
|
}
|
|
2662
|
+
};
|
|
2663
|
+
function assertNever(value) {
|
|
2664
|
+
throw new Error(`Unhandled platform event: ${JSON.stringify(value)}`);
|
|
2434
2665
|
}
|
|
2435
2666
|
|
|
2436
2667
|
// src/runtime/rooms/AgentRuntime.ts
|
|
2437
2668
|
var AgentRuntime = class {
|
|
2669
|
+
presence;
|
|
2438
2670
|
link;
|
|
2439
2671
|
agentId;
|
|
2440
2672
|
onExecute;
|
|
@@ -2445,19 +2677,14 @@ var AgentRuntime = class {
|
|
|
2445
2677
|
onParticipantAdded;
|
|
2446
2678
|
onParticipantRemoved;
|
|
2447
2679
|
onError;
|
|
2448
|
-
roomFilter;
|
|
2449
2680
|
contextFactory;
|
|
2450
2681
|
sessionConfig;
|
|
2451
|
-
autoSubscribeExistingRooms;
|
|
2452
|
-
subscribedRooms = /* @__PURE__ */ new Set();
|
|
2453
2682
|
contexts = /* @__PURE__ */ new Map();
|
|
2454
2683
|
executions = /* @__PURE__ */ new Map();
|
|
2455
2684
|
executionWatchers = /* @__PURE__ */ new Map();
|
|
2456
2685
|
logger;
|
|
2457
2686
|
running = false;
|
|
2458
2687
|
stopping = false;
|
|
2459
|
-
stopController = new AbortController();
|
|
2460
|
-
consumeTask = null;
|
|
2461
2688
|
fatalError = null;
|
|
2462
2689
|
constructor(options) {
|
|
2463
2690
|
this.link = options.link;
|
|
@@ -2471,7 +2698,6 @@ var AgentRuntime = class {
|
|
|
2471
2698
|
this.onContactEvent = options.onContactEvent;
|
|
2472
2699
|
this.onParticipantAdded = options.onParticipantAdded;
|
|
2473
2700
|
this.onParticipantRemoved = options.onParticipantRemoved;
|
|
2474
|
-
this.roomFilter = options.roomFilter;
|
|
2475
2701
|
this.contextFactory = options.contextFactory;
|
|
2476
2702
|
this.sessionConfig = {
|
|
2477
2703
|
enableContextCache: options.sessionConfig?.enableContextCache ?? true,
|
|
@@ -2480,7 +2706,48 @@ var AgentRuntime = class {
|
|
|
2480
2706
|
maxMessageRetries: options.sessionConfig?.maxMessageRetries ?? 1,
|
|
2481
2707
|
enableContextHydration: options.sessionConfig?.enableContextHydration ?? true
|
|
2482
2708
|
};
|
|
2483
|
-
this.
|
|
2709
|
+
this.presence = new RoomPresence({
|
|
2710
|
+
link: this.link,
|
|
2711
|
+
roomFilter: options.roomFilter,
|
|
2712
|
+
autoSubscribeExistingRooms: options.agentConfig?.autoSubscribeExistingRooms ?? false,
|
|
2713
|
+
logger: this.logger
|
|
2714
|
+
});
|
|
2715
|
+
this.presence.onRoomJoined = async (roomId, payload) => {
|
|
2716
|
+
this.getOrCreateExecution(roomId);
|
|
2717
|
+
await this.onRoomJoined?.(roomId, payload);
|
|
2718
|
+
};
|
|
2719
|
+
this.presence.onRoomLeft = async (roomId) => {
|
|
2720
|
+
await this.teardownExecution(roomId);
|
|
2721
|
+
await this.onRoomLeft?.(roomId);
|
|
2722
|
+
};
|
|
2723
|
+
this.presence.onRoomEvent = async (roomId, event) => {
|
|
2724
|
+
switch (event.type) {
|
|
2725
|
+
case "participant_added": {
|
|
2726
|
+
const context = this.getOrCreateContext(roomId);
|
|
2727
|
+
const participant = {
|
|
2728
|
+
id: event.payload.id,
|
|
2729
|
+
name: event.payload.name,
|
|
2730
|
+
type: event.payload.type,
|
|
2731
|
+
handle: event.payload.handle
|
|
2732
|
+
};
|
|
2733
|
+
context.addParticipant(participant);
|
|
2734
|
+
await this.onParticipantAdded?.(roomId, participant);
|
|
2735
|
+
return;
|
|
2736
|
+
}
|
|
2737
|
+
case "participant_removed": {
|
|
2738
|
+
const context = this.getOrCreateContext(roomId);
|
|
2739
|
+
context.removeParticipant(event.payload.id);
|
|
2740
|
+
await this.onParticipantRemoved?.(roomId, event.payload.id);
|
|
2741
|
+
return;
|
|
2742
|
+
}
|
|
2743
|
+
case "message_created":
|
|
2744
|
+
await this.getOrCreateExecution(roomId).enqueue(event);
|
|
2745
|
+
return;
|
|
2746
|
+
default:
|
|
2747
|
+
assertNever2(event);
|
|
2748
|
+
}
|
|
2749
|
+
};
|
|
2750
|
+
this.presence.onContactEvent = this.onContactEvent ?? null;
|
|
2484
2751
|
}
|
|
2485
2752
|
async start() {
|
|
2486
2753
|
if (this.running) {
|
|
@@ -2489,45 +2756,17 @@ var AgentRuntime = class {
|
|
|
2489
2756
|
this.running = true;
|
|
2490
2757
|
this.stopping = false;
|
|
2491
2758
|
this.fatalError = null;
|
|
2492
|
-
if (!this.stopController.signal.aborted) {
|
|
2493
|
-
this.stopController.abort();
|
|
2494
|
-
}
|
|
2495
|
-
this.stopController = new AbortController();
|
|
2496
2759
|
try {
|
|
2497
|
-
await this.
|
|
2760
|
+
await this.presence.start();
|
|
2498
2761
|
} catch (error) {
|
|
2499
2762
|
await this.handleStartFailure();
|
|
2500
2763
|
throw error;
|
|
2501
2764
|
}
|
|
2502
|
-
|
|
2503
|
-
await this.link.subscribeAgentRooms();
|
|
2504
|
-
} catch {
|
|
2505
|
-
this.logger.warn("AgentRuntime failed to subscribe agent_rooms channel, continuing without it");
|
|
2506
|
-
}
|
|
2507
|
-
try {
|
|
2508
|
-
await this.subscribeExistingRooms();
|
|
2509
|
-
} catch (error) {
|
|
2510
|
-
await this.handleStartFailure();
|
|
2511
|
-
throw error;
|
|
2512
|
-
}
|
|
2513
|
-
this.consumeTask = this.consumeLoop(this.stopController.signal);
|
|
2514
|
-
if (!this.link.capabilities.contacts) {
|
|
2515
|
-
return;
|
|
2516
|
-
}
|
|
2517
|
-
try {
|
|
2518
|
-
await this.link.subscribeAgentContacts();
|
|
2519
|
-
} catch {
|
|
2520
|
-
this.logger.warn("AgentRuntime failed to subscribe agent_contacts channel, continuing without it");
|
|
2521
|
-
}
|
|
2765
|
+
void this.presence.waitUntilStopped().catch((error) => this.failRuntime(error, syntheticRuntimeFailureEvent(this.agentId)));
|
|
2522
2766
|
}
|
|
2523
2767
|
async handleStartFailure() {
|
|
2524
2768
|
this.running = false;
|
|
2525
2769
|
this.stopping = false;
|
|
2526
|
-
this.stopController.abort();
|
|
2527
|
-
if (this.consumeTask) {
|
|
2528
|
-
await this.consumeTask;
|
|
2529
|
-
this.consumeTask = null;
|
|
2530
|
-
}
|
|
2531
2770
|
await this.link.disconnect();
|
|
2532
2771
|
}
|
|
2533
2772
|
async stop(timeoutMs) {
|
|
@@ -2536,34 +2775,25 @@ var AgentRuntime = class {
|
|
|
2536
2775
|
}
|
|
2537
2776
|
this.stopping = true;
|
|
2538
2777
|
this.running = false;
|
|
2539
|
-
this.
|
|
2540
|
-
|
|
2541
|
-
await this.consumeTask;
|
|
2542
|
-
this.consumeTask = null;
|
|
2543
|
-
}
|
|
2544
|
-
const deadline = timeoutMs === void 0 ? void 0 : Date.now() + timeoutMs;
|
|
2778
|
+
this.presence.abortEventLoop();
|
|
2779
|
+
await this.presence.waitUntilStopped().catch(() => void 0);
|
|
2545
2780
|
let graceful = true;
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
}
|
|
2781
|
+
await Promise.all(
|
|
2782
|
+
[...this.executions].map(async ([roomId, execution]) => {
|
|
2783
|
+
const stopped = await execution.stop(timeoutMs);
|
|
2784
|
+
if (!stopped) {
|
|
2785
|
+
graceful = false;
|
|
2786
|
+
}
|
|
2787
|
+
this.executions.delete(roomId);
|
|
2788
|
+
})
|
|
2789
|
+
);
|
|
2790
|
+
await this.presence.stop();
|
|
2557
2791
|
for (const roomId of [...this.contexts.keys()]) {
|
|
2558
2792
|
await this.onSessionCleanup(roomId);
|
|
2559
2793
|
}
|
|
2560
|
-
this.subscribedRooms.clear();
|
|
2561
2794
|
this.contexts.clear();
|
|
2562
2795
|
this.executions.clear();
|
|
2563
2796
|
this.executionWatchers.clear();
|
|
2564
|
-
if (this.link.capabilities.contacts) {
|
|
2565
|
-
await this.link.unsubscribeAgentContacts();
|
|
2566
|
-
}
|
|
2567
2797
|
await this.link.disconnect();
|
|
2568
2798
|
if (this.fatalError) {
|
|
2569
2799
|
throw this.fatalError instanceof Error ? this.fatalError : new Error(String(this.fatalError));
|
|
@@ -2574,11 +2804,7 @@ var AgentRuntime = class {
|
|
|
2574
2804
|
return this.contexts.get(roomId);
|
|
2575
2805
|
}
|
|
2576
2806
|
async waitUntilStopped() {
|
|
2577
|
-
|
|
2578
|
-
await this.consumeTask;
|
|
2579
|
-
} else {
|
|
2580
|
-
await this.link.runForever(this.stopController.signal);
|
|
2581
|
-
}
|
|
2807
|
+
await this.presence.waitUntilStopped().catch(() => void 0);
|
|
2582
2808
|
if (this.fatalError) {
|
|
2583
2809
|
throw this.fatalError instanceof Error ? this.fatalError : new Error(String(this.fatalError));
|
|
2584
2810
|
}
|
|
@@ -2586,97 +2812,19 @@ var AgentRuntime = class {
|
|
|
2586
2812
|
getContexts() {
|
|
2587
2813
|
return [...this.contexts.values()];
|
|
2588
2814
|
}
|
|
2589
|
-
async consumeLoop(signal) {
|
|
2590
|
-
while (!signal.aborted) {
|
|
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
|
-
}
|
|
2598
|
-
if (!event) {
|
|
2599
|
-
return;
|
|
2600
|
-
}
|
|
2601
|
-
try {
|
|
2602
|
-
await this.handleEvent(event);
|
|
2603
|
-
} catch (error) {
|
|
2604
|
-
await this.failRuntime(error, event);
|
|
2605
|
-
return;
|
|
2606
|
-
}
|
|
2607
|
-
}
|
|
2608
|
-
}
|
|
2609
|
-
async handleEvent(event) {
|
|
2610
|
-
switch (event.type) {
|
|
2611
|
-
case "room_added":
|
|
2612
|
-
await trackRoomJoin({
|
|
2613
|
-
link: this.link,
|
|
2614
|
-
roomId: event.roomId,
|
|
2615
|
-
payload: event.payload,
|
|
2616
|
-
trackedRooms: this.subscribedRooms,
|
|
2617
|
-
roomFilter: this.roomFilter,
|
|
2618
|
-
onJoined: async (roomId) => {
|
|
2619
|
-
this.getOrCreateExecution(roomId);
|
|
2620
|
-
await this.onRoomJoined?.(roomId, event.payload);
|
|
2621
|
-
}
|
|
2622
|
-
});
|
|
2623
|
-
return;
|
|
2624
|
-
case "room_removed":
|
|
2625
|
-
case "room_deleted":
|
|
2626
|
-
if (event.roomId) {
|
|
2627
|
-
await this.onRoomLeft?.(event.roomId);
|
|
2628
|
-
await this.leaveTrackedRoom(event.roomId);
|
|
2629
|
-
}
|
|
2630
|
-
return;
|
|
2631
|
-
case "participant_added":
|
|
2632
|
-
if (event.roomId) {
|
|
2633
|
-
const context = this.getOrCreateContext(event.roomId);
|
|
2634
|
-
const participant = {
|
|
2635
|
-
id: event.payload.id,
|
|
2636
|
-
name: event.payload.name,
|
|
2637
|
-
type: event.payload.type,
|
|
2638
|
-
handle: event.payload.handle
|
|
2639
|
-
};
|
|
2640
|
-
context.addParticipant(participant);
|
|
2641
|
-
await this.onParticipantAdded?.(event.roomId, participant);
|
|
2642
|
-
}
|
|
2643
|
-
return;
|
|
2644
|
-
case "participant_removed":
|
|
2645
|
-
if (event.roomId) {
|
|
2646
|
-
const context = this.getOrCreateContext(event.roomId);
|
|
2647
|
-
context.removeParticipant(event.payload.id);
|
|
2648
|
-
await this.onParticipantRemoved?.(event.roomId, event.payload.id);
|
|
2649
|
-
}
|
|
2650
|
-
return;
|
|
2651
|
-
case "contact_request_received":
|
|
2652
|
-
case "contact_request_updated":
|
|
2653
|
-
case "contact_added":
|
|
2654
|
-
case "contact_removed":
|
|
2655
|
-
await this.onContactEvent?.(event);
|
|
2656
|
-
return;
|
|
2657
|
-
case "message_created":
|
|
2658
|
-
if (!event.roomId) {
|
|
2659
|
-
return;
|
|
2660
|
-
}
|
|
2661
|
-
await this.getOrCreateExecution(event.roomId).enqueue(event);
|
|
2662
|
-
return;
|
|
2663
|
-
}
|
|
2664
|
-
return assertNever(event);
|
|
2665
|
-
}
|
|
2666
2815
|
async enqueueEvent(roomId, event) {
|
|
2667
2816
|
await this.getOrCreateExecution(roomId).enqueue(event);
|
|
2668
2817
|
}
|
|
2669
2818
|
async bootstrapRoomMessage(roomId, message) {
|
|
2670
|
-
await this.
|
|
2671
|
-
this.subscribedRooms.add(roomId);
|
|
2819
|
+
await this.presence.admitRoomOrThrow(roomId);
|
|
2672
2820
|
await this.getOrCreateExecution(roomId).bootstrapMessage(message);
|
|
2673
2821
|
}
|
|
2674
2822
|
async resetRoomSession(roomId, timeoutMs) {
|
|
2823
|
+
return this.teardownExecution(roomId, timeoutMs);
|
|
2824
|
+
}
|
|
2825
|
+
async teardownExecution(roomId, timeoutMs) {
|
|
2675
2826
|
const execution = this.executions.get(roomId);
|
|
2676
|
-
|
|
2677
|
-
if (execution) {
|
|
2678
|
-
graceful = await execution.stop(timeoutMs);
|
|
2679
|
-
}
|
|
2827
|
+
const graceful = execution ? await execution.stop(timeoutMs) : true;
|
|
2680
2828
|
this.executions.delete(roomId);
|
|
2681
2829
|
this.contexts.delete(roomId);
|
|
2682
2830
|
await this.onSessionCleanup(roomId);
|
|
@@ -2732,44 +2880,13 @@ var AgentRuntime = class {
|
|
|
2732
2880
|
maxMessageRetries: this.sessionConfig.maxMessageRetries,
|
|
2733
2881
|
enableContextCache: this.sessionConfig.enableContextCache,
|
|
2734
2882
|
contextCacheTtlSeconds: this.sessionConfig.contextCacheTtlSeconds,
|
|
2735
|
-
enableContextHydration: this.sessionConfig.enableContextHydration
|
|
2883
|
+
enableContextHydration: this.sessionConfig.enableContextHydration,
|
|
2884
|
+
logger: this.logger
|
|
2736
2885
|
};
|
|
2737
2886
|
const context = this.contextFactory ? this.contextFactory(roomId, defaults) : new ExecutionContext(defaults);
|
|
2738
2887
|
this.contexts.set(roomId, context);
|
|
2739
2888
|
return context;
|
|
2740
2889
|
}
|
|
2741
|
-
async subscribeExistingRooms() {
|
|
2742
|
-
if (!this.autoSubscribeExistingRooms) {
|
|
2743
|
-
return;
|
|
2744
|
-
}
|
|
2745
|
-
await hydrateTrackedRooms({
|
|
2746
|
-
link: this.link,
|
|
2747
|
-
trackedRooms: this.subscribedRooms,
|
|
2748
|
-
roomFilter: this.roomFilter,
|
|
2749
|
-
onJoined: async (roomId, payload) => {
|
|
2750
|
-
this.getOrCreateExecution(roomId);
|
|
2751
|
-
await this.onRoomJoined?.(roomId, payload);
|
|
2752
|
-
},
|
|
2753
|
-
onError: async (error) => {
|
|
2754
|
-
this.logger.warn("AgentRuntime failed to subscribe existing rooms", {
|
|
2755
|
-
error
|
|
2756
|
-
});
|
|
2757
|
-
}
|
|
2758
|
-
});
|
|
2759
|
-
}
|
|
2760
|
-
async leaveTrackedRoom(roomId, timeoutMs) {
|
|
2761
|
-
await trackRoomLeave({
|
|
2762
|
-
link: this.link,
|
|
2763
|
-
roomId,
|
|
2764
|
-
trackedRooms: this.subscribedRooms,
|
|
2765
|
-
onLeft: async (leftRoomId) => {
|
|
2766
|
-
await this.executions.get(leftRoomId)?.stop(timeoutMs);
|
|
2767
|
-
this.contexts.delete(leftRoomId);
|
|
2768
|
-
this.executions.delete(leftRoomId);
|
|
2769
|
-
await this.onSessionCleanup(leftRoomId);
|
|
2770
|
-
}
|
|
2771
|
-
});
|
|
2772
|
-
}
|
|
2773
2890
|
async failRuntime(error, event) {
|
|
2774
2891
|
if (!this.fatalError) {
|
|
2775
2892
|
this.fatalError = error;
|
|
@@ -2781,9 +2898,7 @@ var AgentRuntime = class {
|
|
|
2781
2898
|
});
|
|
2782
2899
|
this.notifyOnError(error, event);
|
|
2783
2900
|
}
|
|
2784
|
-
|
|
2785
|
-
this.stopController.abort();
|
|
2786
|
-
}
|
|
2901
|
+
this.presence.abortEventLoop();
|
|
2787
2902
|
}
|
|
2788
2903
|
notifyOnError(error, event) {
|
|
2789
2904
|
if (!this.onError) {
|
|
@@ -2800,6 +2915,9 @@ var AgentRuntime = class {
|
|
|
2800
2915
|
}
|
|
2801
2916
|
}
|
|
2802
2917
|
};
|
|
2918
|
+
function assertNever2(value) {
|
|
2919
|
+
throw new Error(`Unhandled room event: ${JSON.stringify(value)}`);
|
|
2920
|
+
}
|
|
2803
2921
|
function syntheticRuntimeFailureEvent(agentId) {
|
|
2804
2922
|
return {
|
|
2805
2923
|
type: "message_created",
|
|
@@ -2817,9 +2935,6 @@ function syntheticRuntimeFailureEvent(agentId) {
|
|
|
2817
2935
|
}
|
|
2818
2936
|
};
|
|
2819
2937
|
}
|
|
2820
|
-
function assertNever(value) {
|
|
2821
|
-
throw new Error(`Unhandled platform event: ${JSON.stringify(value)}`);
|
|
2822
|
-
}
|
|
2823
2938
|
|
|
2824
2939
|
// src/runtime/preprocessing/DefaultPreprocessor.ts
|
|
2825
2940
|
function toPlatformMessage2(roomId, payload) {
|
|
@@ -3417,7 +3532,7 @@ Type: ${event.payload.type}, ID: ${event.payload.id}`;
|
|
|
3417
3532
|
case "contact_removed":
|
|
3418
3533
|
return `[Contact Removed] Contact ${event.payload.id} was removed.`;
|
|
3419
3534
|
}
|
|
3420
|
-
return
|
|
3535
|
+
return assertNever3(event);
|
|
3421
3536
|
}
|
|
3422
3537
|
formatBroadcast(event) {
|
|
3423
3538
|
switch (event.type) {
|
|
@@ -3547,7 +3662,7 @@ Type: ${event.payload.type}, ID: ${event.payload.id}`;
|
|
|
3547
3662
|
return { message: String(error) };
|
|
3548
3663
|
}
|
|
3549
3664
|
};
|
|
3550
|
-
function
|
|
3665
|
+
function assertNever3(value) {
|
|
3551
3666
|
throw new Error(`Unhandled contact event: ${JSON.stringify(value)}`);
|
|
3552
3667
|
}
|
|
3553
3668
|
function normalizeHandle(handle) {
|
|
@@ -3764,6 +3879,9 @@ var PlatformRuntime = class {
|
|
|
3764
3879
|
this.stopping = false;
|
|
3765
3880
|
return graceful;
|
|
3766
3881
|
}
|
|
3882
|
+
async [Symbol.asyncDispose]() {
|
|
3883
|
+
await this.stop();
|
|
3884
|
+
}
|
|
3767
3885
|
async runForever() {
|
|
3768
3886
|
if (!this.runtime) {
|
|
3769
3887
|
throw new RuntimeStateError("Runtime not started");
|
|
@@ -3880,9 +3998,7 @@ export {
|
|
|
3880
3998
|
Execution,
|
|
3881
3999
|
AgentTools,
|
|
3882
4000
|
ExecutionContext,
|
|
3883
|
-
|
|
3884
|
-
trackRoomLeave,
|
|
3885
|
-
hydrateTrackedRooms,
|
|
4001
|
+
RoomPresence,
|
|
3886
4002
|
AgentRuntime,
|
|
3887
4003
|
DefaultPreprocessor,
|
|
3888
4004
|
ContactToolsImpl,
|