@elizaos/plugin-agent-orchestrator 2.0.0-alpha.2 → 2.0.0-alpha.4

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/index.js CHANGED
@@ -1,3 +1,1032 @@
1
+ // index.ts
2
+ import { getSessionProviders as getSessionProviders2 } from "@elizaos/core";
3
+
4
+ // src/actions/messaging.ts
5
+ function extractMessagingParams(message, _state) {
6
+ const params = message.content?.params;
7
+ if (!params) {
8
+ return {};
9
+ }
10
+ let target;
11
+ if (params.target) {
12
+ target = params.target;
13
+ } else {
14
+ target = {};
15
+ if (params.channel)
16
+ target.channel = params.channel;
17
+ if (params.to)
18
+ target.to = params.to;
19
+ if (params.threadId !== undefined)
20
+ target.threadId = params.threadId;
21
+ if (params.replyTo)
22
+ target.replyToMessageId = params.replyTo;
23
+ }
24
+ let content;
25
+ if (params.content) {
26
+ content = params.content;
27
+ } else {
28
+ content = {};
29
+ const text = params.text ?? message.content?.text;
30
+ if (text)
31
+ content.text = text;
32
+ }
33
+ return { target, content };
34
+ }
35
+ var sendCrossPlatformMessageAction = {
36
+ name: "SEND_CROSS_PLATFORM_MESSAGE",
37
+ similes: [
38
+ "CROSS_PLATFORM_MESSAGE",
39
+ "UNIFIED_SEND",
40
+ "SEND_TO_CHANNEL",
41
+ "RELAY_MESSAGE",
42
+ "BROADCAST_MESSAGE"
43
+ ],
44
+ description: "Send a message to any supported platform (Discord, Telegram, Slack, WhatsApp, Twitch). " + "Requires specifying the target channel/platform and recipient.",
45
+ validate: async (runtime, message, _state) => {
46
+ const messagingService = runtime.getService("MESSAGING");
47
+ if (!messagingService) {
48
+ return false;
49
+ }
50
+ const params = message.content?.params;
51
+ if (!params) {
52
+ return true;
53
+ }
54
+ const hasTarget = params.target || params.channel && params.to;
55
+ const hasContent = params.content || params.text || message.content?.text;
56
+ return !!(hasTarget && hasContent);
57
+ },
58
+ handler: async (runtime, message, state, _options, callback) => {
59
+ const messagingService = runtime.getService("MESSAGING");
60
+ if (!messagingService) {
61
+ if (callback) {
62
+ await callback({
63
+ text: "Messaging service is not available. Please ensure the orchestrator plugin is properly configured."
64
+ });
65
+ }
66
+ return { success: false, error: "Messaging service not available" };
67
+ }
68
+ const { target, content } = extractMessagingParams(message, state);
69
+ if (!target?.channel) {
70
+ if (callback) {
71
+ await callback({
72
+ text: "Please specify the target channel (discord, telegram, slack, whatsapp, twitch)."
73
+ });
74
+ }
75
+ return { success: false, error: "Missing target channel" };
76
+ }
77
+ if (!target?.to) {
78
+ if (callback) {
79
+ await callback({
80
+ text: "Please specify the recipient (channel ID, chat ID, or room ID)."
81
+ });
82
+ }
83
+ return { success: false, error: "Missing recipient" };
84
+ }
85
+ if (!content?.text) {
86
+ if (callback) {
87
+ await callback({
88
+ text: "Please provide the message text to send."
89
+ });
90
+ }
91
+ return { success: false, error: "Missing message text" };
92
+ }
93
+ const sendTarget = {
94
+ channel: target.channel,
95
+ to: target.to
96
+ };
97
+ if (target.threadId !== undefined)
98
+ sendTarget.threadId = target.threadId;
99
+ if (target.replyToMessageId)
100
+ sendTarget.replyToMessageId = target.replyToMessageId;
101
+ const sendContent = {
102
+ text: content.text
103
+ };
104
+ if (content.attachments)
105
+ sendContent.attachments = content.attachments;
106
+ if (content.embed)
107
+ sendContent.embed = content.embed;
108
+ if (content.buttons)
109
+ sendContent.buttons = content.buttons;
110
+ if (content.disableLinkPreview !== undefined)
111
+ sendContent.disableLinkPreview = content.disableLinkPreview;
112
+ if (content.silent !== undefined)
113
+ sendContent.silent = content.silent;
114
+ const result = await messagingService.send({
115
+ target: sendTarget,
116
+ content: sendContent
117
+ });
118
+ if (callback) {
119
+ if (result.success) {
120
+ const callbackData = {};
121
+ if (result.messageId)
122
+ callbackData.messageId = result.messageId;
123
+ if (result.sentAt)
124
+ callbackData.sentAt = result.sentAt;
125
+ await callback({
126
+ text: `Message sent successfully to ${target.channel}.`,
127
+ data: callbackData
128
+ });
129
+ } else {
130
+ await callback({
131
+ text: `Failed to send message: ${result.error}`
132
+ });
133
+ }
134
+ }
135
+ return {
136
+ success: result.success,
137
+ data: result,
138
+ ...result.error !== undefined ? { error: result.error } : {}
139
+ };
140
+ },
141
+ examples: [
142
+ [
143
+ {
144
+ name: "{{name1}}",
145
+ content: {
146
+ text: "Send 'Hello from the agent!' to Discord channel 123456789",
147
+ params: {
148
+ channel: "discord",
149
+ to: "123456789",
150
+ text: "Hello from the agent!"
151
+ }
152
+ }
153
+ },
154
+ {
155
+ name: "{{agentName}}",
156
+ content: {
157
+ text: "Message sent successfully to Discord.",
158
+ actions: ["SEND_CROSS_PLATFORM_MESSAGE"]
159
+ }
160
+ }
161
+ ],
162
+ [
163
+ {
164
+ name: "{{name1}}",
165
+ content: {
166
+ text: "Send a Telegram message",
167
+ params: {
168
+ channel: "telegram",
169
+ to: "-1001234567890",
170
+ text: "Automated notification from your AI assistant."
171
+ }
172
+ }
173
+ },
174
+ {
175
+ name: "{{agentName}}",
176
+ content: {
177
+ text: "Message sent successfully to Telegram.",
178
+ actions: ["SEND_CROSS_PLATFORM_MESSAGE"]
179
+ }
180
+ }
181
+ ]
182
+ ]
183
+ };
184
+ var sendToDeliveryContextAction = {
185
+ name: "SEND_TO_DELIVERY_CONTEXT",
186
+ similes: ["DELIVER_MESSAGE", "SEND_TO_CONTEXT", "ROUTE_MESSAGE"],
187
+ description: "Send a message using a delivery context that specifies the target channel and recipient. " + "Useful for routing messages back to the original requester or to a specific context.",
188
+ validate: async (runtime, message, _state) => {
189
+ const messagingService = runtime.getService("MESSAGING");
190
+ if (!messagingService) {
191
+ return false;
192
+ }
193
+ const params = message.content?.params;
194
+ if (!params?.deliveryContext) {
195
+ return false;
196
+ }
197
+ const hasText = params.text || message.content?.text;
198
+ return !!hasText;
199
+ },
200
+ handler: async (runtime, message, _state, _options, callback) => {
201
+ const messagingService = runtime.getService("MESSAGING");
202
+ if (!messagingService) {
203
+ if (callback) {
204
+ await callback({
205
+ text: "Messaging service is not available."
206
+ });
207
+ }
208
+ return { success: false, error: "Messaging service not available" };
209
+ }
210
+ const params = message.content?.params;
211
+ const deliveryContext = params?.deliveryContext;
212
+ const text = params?.text ?? message.content?.text;
213
+ if (!deliveryContext) {
214
+ if (callback) {
215
+ await callback({
216
+ text: "Please provide a delivery context with channel and recipient information."
217
+ });
218
+ }
219
+ return { success: false, error: "Missing delivery context" };
220
+ }
221
+ if (!text) {
222
+ if (callback) {
223
+ await callback({
224
+ text: "Please provide the message text to send."
225
+ });
226
+ }
227
+ return { success: false, error: "Missing message text" };
228
+ }
229
+ const result = await messagingService.sendToDeliveryContext(deliveryContext, {
230
+ text
231
+ });
232
+ if (callback) {
233
+ if (result.success) {
234
+ const callbackData = {};
235
+ if (result.messageId)
236
+ callbackData.messageId = result.messageId;
237
+ await callback({
238
+ text: `Message delivered successfully via ${result.channel}.`,
239
+ data: callbackData
240
+ });
241
+ } else {
242
+ await callback({
243
+ text: `Failed to deliver message: ${result.error}`
244
+ });
245
+ }
246
+ }
247
+ return {
248
+ success: result.success,
249
+ data: result,
250
+ ...result.error !== undefined ? { error: result.error } : {}
251
+ };
252
+ },
253
+ examples: [
254
+ [
255
+ {
256
+ name: "{{name1}}",
257
+ content: {
258
+ text: "Send result to delivery context",
259
+ params: {
260
+ deliveryContext: {
261
+ channel: "discord",
262
+ to: "123456789"
263
+ },
264
+ text: "Task completed successfully!"
265
+ }
266
+ }
267
+ },
268
+ {
269
+ name: "{{agentName}}",
270
+ content: {
271
+ text: "Message delivered successfully via discord.",
272
+ actions: ["SEND_TO_DELIVERY_CONTEXT"]
273
+ }
274
+ }
275
+ ]
276
+ ]
277
+ };
278
+ var sendToRoomAction = {
279
+ name: "SEND_TO_ROOM",
280
+ similes: ["MESSAGE_ROOM", "ROOM_MESSAGE", "NOTIFY_ROOM"],
281
+ description: "Send a message to an Eliza room. The room's metadata determines which platform and recipient to use.",
282
+ validate: async (runtime, message, _state) => {
283
+ const messagingService = runtime.getService("MESSAGING");
284
+ if (!messagingService) {
285
+ return false;
286
+ }
287
+ const params = message.content?.params;
288
+ if (!params?.roomId) {
289
+ return false;
290
+ }
291
+ const hasText = params.text || message.content?.text;
292
+ return !!hasText;
293
+ },
294
+ handler: async (runtime, message, _state, _options, callback) => {
295
+ const messagingService = runtime.getService("MESSAGING");
296
+ if (!messagingService) {
297
+ if (callback) {
298
+ await callback({
299
+ text: "Messaging service is not available."
300
+ });
301
+ }
302
+ return { success: false, error: "Messaging service not available" };
303
+ }
304
+ const params = message.content?.params;
305
+ const roomId = params?.roomId;
306
+ const text = params?.text ?? message.content?.text;
307
+ if (!roomId) {
308
+ if (callback) {
309
+ await callback({
310
+ text: "Please specify the room ID to send the message to."
311
+ });
312
+ }
313
+ return { success: false, error: "Missing room ID" };
314
+ }
315
+ if (!text) {
316
+ if (callback) {
317
+ await callback({
318
+ text: "Please provide the message text to send."
319
+ });
320
+ }
321
+ return { success: false, error: "Missing message text" };
322
+ }
323
+ const result = await messagingService.sendToRoom(roomId, { text });
324
+ if (callback) {
325
+ if (result.success) {
326
+ const callbackData = {};
327
+ if (result.messageId)
328
+ callbackData.messageId = result.messageId;
329
+ await callback({
330
+ text: `Message sent to room via ${result.channel}.`,
331
+ data: callbackData
332
+ });
333
+ } else {
334
+ await callback({
335
+ text: `Failed to send to room: ${result.error}`
336
+ });
337
+ }
338
+ }
339
+ return {
340
+ success: result.success,
341
+ data: result,
342
+ ...result.error !== undefined ? { error: result.error } : {}
343
+ };
344
+ },
345
+ examples: [
346
+ [
347
+ {
348
+ name: "{{name1}}",
349
+ content: {
350
+ text: "Send to room",
351
+ params: {
352
+ roomId: "550e8400-e29b-41d4-a716-446655440000",
353
+ text: "Hello, room!"
354
+ }
355
+ }
356
+ },
357
+ {
358
+ name: "{{agentName}}",
359
+ content: {
360
+ text: "Message sent to room via discord.",
361
+ actions: ["SEND_TO_ROOM"]
362
+ }
363
+ }
364
+ ]
365
+ ]
366
+ };
367
+ var sendToSessionMessageAction = {
368
+ name: "SEND_TO_SESSION_MESSAGE",
369
+ similes: ["SESSION_MESSAGE", "MESSAGE_SESSION", "NOTIFY_SESSION"],
370
+ description: "Send a message to a session by its session key. The session key is mapped to an Eliza room.",
371
+ validate: async (runtime, message, _state) => {
372
+ const messagingService = runtime.getService("MESSAGING");
373
+ if (!messagingService) {
374
+ return false;
375
+ }
376
+ const params = message.content?.params;
377
+ if (!params?.sessionKey) {
378
+ return false;
379
+ }
380
+ const hasText = params.text || message.content?.text;
381
+ return !!hasText;
382
+ },
383
+ handler: async (runtime, message, _state, _options, callback) => {
384
+ const messagingService = runtime.getService("MESSAGING");
385
+ if (!messagingService) {
386
+ if (callback) {
387
+ await callback({
388
+ text: "Messaging service is not available."
389
+ });
390
+ }
391
+ return { success: false, error: "Messaging service not available" };
392
+ }
393
+ const params = message.content?.params;
394
+ const sessionKey = params?.sessionKey;
395
+ const text = params?.text ?? message.content?.text;
396
+ if (!sessionKey) {
397
+ if (callback) {
398
+ await callback({
399
+ text: "Please specify the session key to send the message to."
400
+ });
401
+ }
402
+ return { success: false, error: "Missing session key" };
403
+ }
404
+ if (!text) {
405
+ if (callback) {
406
+ await callback({
407
+ text: "Please provide the message text to send."
408
+ });
409
+ }
410
+ return { success: false, error: "Missing message text" };
411
+ }
412
+ const result = await messagingService.sendToSession(sessionKey, { text });
413
+ if (callback) {
414
+ if (result.success) {
415
+ const callbackData = {};
416
+ if (result.messageId)
417
+ callbackData.messageId = result.messageId;
418
+ await callback({
419
+ text: `Message sent to session via ${result.channel}.`,
420
+ data: callbackData
421
+ });
422
+ } else {
423
+ await callback({
424
+ text: `Failed to send to session: ${result.error}`
425
+ });
426
+ }
427
+ }
428
+ return {
429
+ success: result.success,
430
+ data: result,
431
+ ...result.error !== undefined ? { error: result.error } : {}
432
+ };
433
+ },
434
+ examples: [
435
+ [
436
+ {
437
+ name: "{{name1}}",
438
+ content: {
439
+ text: "Send to session",
440
+ params: {
441
+ sessionKey: "agent:main:dm:user123",
442
+ text: "Update from your subagent!"
443
+ }
444
+ }
445
+ },
446
+ {
447
+ name: "{{agentName}}",
448
+ content: {
449
+ text: "Message sent to session via discord.",
450
+ actions: ["SEND_TO_SESSION_MESSAGE"]
451
+ }
452
+ }
453
+ ]
454
+ ]
455
+ };
456
+ var listMessagingChannelsAction = {
457
+ name: "LIST_MESSAGING_CHANNELS",
458
+ similes: ["AVAILABLE_CHANNELS", "GET_CHANNELS", "MESSAGING_PLATFORMS"],
459
+ description: "List all available messaging channels/platforms that the agent can send messages to.",
460
+ validate: async (runtime, _message, _state) => {
461
+ const messagingService = runtime.getService("MESSAGING");
462
+ return !!messagingService;
463
+ },
464
+ handler: async (runtime, _message, _state, _options, callback) => {
465
+ const messagingService = runtime.getService("MESSAGING");
466
+ if (!messagingService) {
467
+ if (callback) {
468
+ await callback({
469
+ text: "Messaging service is not available."
470
+ });
471
+ }
472
+ return { success: false, error: "Messaging service not available" };
473
+ }
474
+ const channels = messagingService.getAvailableChannels();
475
+ if (callback) {
476
+ if (channels.length > 0) {
477
+ await callback({
478
+ text: `Available messaging channels: ${channels.join(", ")}`,
479
+ data: { channels }
480
+ });
481
+ } else {
482
+ await callback({
483
+ text: "No messaging channels are currently available.",
484
+ data: { channels: [] }
485
+ });
486
+ }
487
+ }
488
+ return {
489
+ success: true,
490
+ data: { channels }
491
+ };
492
+ },
493
+ examples: [
494
+ [
495
+ {
496
+ name: "{{name1}}",
497
+ content: {
498
+ text: "What messaging platforms can you use?"
499
+ }
500
+ },
501
+ {
502
+ name: "{{agentName}}",
503
+ content: {
504
+ text: "Available messaging channels: discord, telegram, internal",
505
+ actions: ["LIST_MESSAGING_CHANNELS"]
506
+ }
507
+ }
508
+ ]
509
+ ]
510
+ };
511
+
512
+ // src/utils/session.ts
513
+ import crypto from "node:crypto";
514
+ import {
515
+ buildAcpSessionKey,
516
+ buildAgentMainSessionKey,
517
+ buildAgentPeerSessionKey,
518
+ buildAgentSessionKey,
519
+ buildGroupHistoryKey,
520
+ buildSubagentSessionKey,
521
+ createSendPolicyProvider,
522
+ createSessionEntry,
523
+ createSessionProvider,
524
+ createSessionSkillsProvider,
525
+ deleteSessionEntry,
526
+ extractSessionContext,
527
+ getSessionEntry,
528
+ getSessionProviders,
529
+ isAcpSessionKey,
530
+ isSubagentSessionKey,
531
+ isValidSessionEntry,
532
+ listSessionKeys,
533
+ loadSessionStore,
534
+ mergeSessionEntry,
535
+ normalizeAccountId,
536
+ normalizeAgentId,
537
+ normalizeMainKey,
538
+ parseAgentSessionKey,
539
+ resolveAgentIdFromSessionKey,
540
+ resolveAgentSessionsDir,
541
+ resolveDefaultSessionStorePath,
542
+ resolveSessionTranscriptPath,
543
+ resolveStateDir,
544
+ resolveStorePath,
545
+ resolveThreadParentSessionKey,
546
+ resolveThreadSessionKeys,
547
+ SessionStateManager,
548
+ saveSessionStore,
549
+ toAgentRequestSessionKey,
550
+ toAgentStoreSessionKey,
551
+ updateSessionStore,
552
+ updateSessionStoreEntry,
553
+ upsertSessionEntry
554
+ } from "@elizaos/core";
555
+ function hashToUUID(input) {
556
+ const hash = crypto.createHash("sha256").update(input).digest("hex");
557
+ const uuid = [
558
+ hash.slice(0, 8),
559
+ hash.slice(8, 12),
560
+ `5${hash.slice(13, 16)}`,
561
+ `${(parseInt(hash.slice(16, 17), 16) & 3 | 8).toString(16)}${hash.slice(17, 20)}`,
562
+ hash.slice(20, 32)
563
+ ].join("-");
564
+ return uuid;
565
+ }
566
+ function sessionKeyToRoomId(sessionKey, agentId) {
567
+ const normalized = normalizeSessionKey(sessionKey);
568
+ const input = agentId ? `${agentId}:${normalized}` : normalized;
569
+ return hashToUUID(input);
570
+ }
571
+ function normalizeSessionKey(sessionKey) {
572
+ const trimmed = sessionKey.trim();
573
+ if (!trimmed) {
574
+ return "";
575
+ }
576
+ if (trimmed.startsWith("agent:")) {
577
+ return trimmed;
578
+ }
579
+ return trimmed;
580
+ }
581
+ function parseSessionKey(sessionKey) {
582
+ const trimmed = sessionKey.trim();
583
+ if (!trimmed) {
584
+ return {
585
+ agentId: "unknown",
586
+ keyType: "unknown",
587
+ identifier: ""
588
+ };
589
+ }
590
+ if (trimmed.startsWith("agent:")) {
591
+ const parts = trimmed.split(":");
592
+ if (parts.length >= 4) {
593
+ const agentId = parts[1];
594
+ const keyTypeRaw = parts[2];
595
+ const identifier = parts.slice(3).join(":");
596
+ const keyType = normalizeKeyType(keyTypeRaw);
597
+ return {
598
+ agentId,
599
+ keyType,
600
+ identifier
601
+ };
602
+ }
603
+ if (parts.length >= 2) {
604
+ return {
605
+ agentId: parts[1],
606
+ keyType: "unknown",
607
+ identifier: parts.slice(2).join(":")
608
+ };
609
+ }
610
+ }
611
+ const colonIndex = trimmed.indexOf(":");
612
+ if (colonIndex > 0) {
613
+ const keyTypeRaw = trimmed.slice(0, colonIndex);
614
+ const identifier = trimmed.slice(colonIndex + 1);
615
+ const keyType = normalizeKeyType(keyTypeRaw);
616
+ return {
617
+ agentId: "unknown",
618
+ keyType,
619
+ identifier
620
+ };
621
+ }
622
+ return {
623
+ agentId: "unknown",
624
+ keyType: "unknown",
625
+ identifier: trimmed
626
+ };
627
+ }
628
+ function normalizeKeyType(raw) {
629
+ const lower = raw.toLowerCase();
630
+ switch (lower) {
631
+ case "dm":
632
+ case "direct":
633
+ return "dm";
634
+ case "subagent":
635
+ case "sub":
636
+ return "subagent";
637
+ case "group":
638
+ case "server":
639
+ return "group";
640
+ case "channel":
641
+ return "channel";
642
+ default:
643
+ return "unknown";
644
+ }
645
+ }
646
+ function buildSessionKey(agentId, keyType, identifier) {
647
+ const normalizedAgentId = normalizeAgentId2(agentId);
648
+ return `agent:${normalizedAgentId}:${keyType}:${identifier}`;
649
+ }
650
+ function normalizeAgentId2(agentId) {
651
+ return agentId.trim().toLowerCase();
652
+ }
653
+ function isSubagentSessionKey2(sessionKey) {
654
+ const parsed = parseSessionKey(sessionKey);
655
+ return parsed.keyType === "subagent";
656
+ }
657
+ function extractAgentIdFromSessionKey(sessionKey) {
658
+ const parsed = parseSessionKey(sessionKey);
659
+ return parsed.agentId;
660
+ }
661
+ function createSubagentSessionKey(agentId) {
662
+ const uuid = crypto.randomUUID();
663
+ return buildSessionKey(agentId, "subagent", uuid);
664
+ }
665
+ function normalizeDeliveryContext(context) {
666
+ if (!context) {
667
+ return;
668
+ }
669
+ const result = {};
670
+ if (context.channel && typeof context.channel === "string") {
671
+ const trimmed = context.channel.trim();
672
+ if (trimmed)
673
+ result.channel = trimmed;
674
+ }
675
+ if (context.accountId && typeof context.accountId === "string") {
676
+ const trimmed = context.accountId.trim();
677
+ if (trimmed)
678
+ result.accountId = trimmed;
679
+ }
680
+ if (context.to && typeof context.to === "string") {
681
+ const trimmed = context.to.trim();
682
+ if (trimmed)
683
+ result.to = trimmed;
684
+ }
685
+ if (context.threadId !== undefined && context.threadId !== null) {
686
+ result.threadId = context.threadId;
687
+ }
688
+ if (context.groupId && typeof context.groupId === "string") {
689
+ const trimmed = context.groupId.trim();
690
+ if (trimmed)
691
+ result.groupId = trimmed;
692
+ }
693
+ if (context.groupChannel && typeof context.groupChannel === "string") {
694
+ const trimmed = context.groupChannel.trim();
695
+ if (trimmed)
696
+ result.groupChannel = trimmed;
697
+ }
698
+ if (context.groupSpace && typeof context.groupSpace === "string") {
699
+ const trimmed = context.groupSpace.trim();
700
+ if (trimmed)
701
+ result.groupSpace = trimmed;
702
+ }
703
+ const hasValues = Object.values(result).some((v) => v !== undefined && v !== null);
704
+ return hasValues ? result : undefined;
705
+ }
706
+ function mergeDeliveryContext(primary, secondary) {
707
+ if (!primary && !secondary) {
708
+ return;
709
+ }
710
+ if (!primary) {
711
+ return normalizeDeliveryContext(secondary);
712
+ }
713
+ if (!secondary) {
714
+ return normalizeDeliveryContext(primary);
715
+ }
716
+ const merged = {};
717
+ const ch = primary.channel || secondary.channel;
718
+ if (ch)
719
+ merged.channel = ch;
720
+ const acct = primary.accountId || secondary.accountId;
721
+ if (acct)
722
+ merged.accountId = acct;
723
+ const to = primary.to || secondary.to;
724
+ if (to)
725
+ merged.to = to;
726
+ const tid = primary.threadId ?? secondary.threadId;
727
+ if (tid !== undefined)
728
+ merged.threadId = tid;
729
+ const gid = primary.groupId || secondary.groupId;
730
+ if (gid)
731
+ merged.groupId = gid;
732
+ const gch = primary.groupChannel || secondary.groupChannel;
733
+ if (gch)
734
+ merged.groupChannel = gch;
735
+ const gsp = primary.groupSpace || secondary.groupSpace;
736
+ if (gsp)
737
+ merged.groupSpace = gsp;
738
+ return normalizeDeliveryContext(merged);
739
+ }
740
+ function formatDurationShort(ms) {
741
+ if (!ms || !Number.isFinite(ms) || ms <= 0) {
742
+ return;
743
+ }
744
+ const totalSeconds = Math.round(ms / 1000);
745
+ const hours = Math.floor(totalSeconds / 3600);
746
+ const minutes = Math.floor(totalSeconds % 3600 / 60);
747
+ const seconds = totalSeconds % 60;
748
+ if (hours > 0) {
749
+ return `${hours}h${minutes}m`;
750
+ }
751
+ if (minutes > 0) {
752
+ return `${minutes}m${seconds}s`;
753
+ }
754
+ return `${seconds}s`;
755
+ }
756
+ function formatTokenCount(count) {
757
+ if (!count || !Number.isFinite(count)) {
758
+ return "0";
759
+ }
760
+ if (count >= 1e6) {
761
+ return `${(count / 1e6).toFixed(1)}m`;
762
+ }
763
+ if (count >= 1000) {
764
+ return `${(count / 1000).toFixed(1)}k`;
765
+ }
766
+ return String(Math.round(count));
767
+ }
768
+
769
+ // src/actions/subagent-management.ts
770
+ function getSubagentService(runtime) {
771
+ const svc = runtime.getService("SUBAGENT");
772
+ if (!svc) {
773
+ throw new Error("SubagentService not available (SUBAGENT)");
774
+ }
775
+ return svc;
776
+ }
777
+ function extractSessionContext2(_runtime, message) {
778
+ const metadata = message.content?.metadata;
779
+ const sessionKey = typeof metadata?.sessionKey === "string" ? metadata.sessionKey : undefined;
780
+ const result = {};
781
+ if (sessionKey)
782
+ result.sessionKey = sessionKey;
783
+ if (message.roomId)
784
+ result.roomId = message.roomId;
785
+ return result;
786
+ }
787
+ var spawnSubagentAction = {
788
+ name: "SPAWN_SUBAGENT",
789
+ similes: ["SPAWN_TASK", "BACKGROUND_TASK", "START_SUBAGENT", "SESSIONS_SPAWN", "CREATE_SUBAGENT"],
790
+ description: "Spawn a background sub-agent run to execute a task asynchronously. The subagent will complete the task and announce results back.",
791
+ validate: async (runtime, message) => {
792
+ const svc = runtime.getService("SUBAGENT");
793
+ if (!svc) {
794
+ return false;
795
+ }
796
+ const text = message.content.text?.toLowerCase() ?? "";
797
+ const hasSpawnIntent = text.includes("spawn") || text.includes("background") || text.includes("subagent") || text.includes("async task");
798
+ const hasTaskWords = text.includes("task") || text.includes("research") || text.includes("investigate") || text.includes("analyze") || text.includes("look into");
799
+ return hasSpawnIntent || hasTaskWords && text.includes("in background");
800
+ },
801
+ handler: async (runtime, message, _state, options, callback) => {
802
+ const svc = getSubagentService(runtime);
803
+ const context = extractSessionContext2(runtime, message);
804
+ const opts = options;
805
+ const task = opts?.task ?? message.content.text ?? "";
806
+ if (!task.trim()) {
807
+ const msg2 = "Please specify a task for the subagent to execute.";
808
+ await callback?.({ content: { text: msg2 } });
809
+ return { success: false, text: msg2 };
810
+ }
811
+ const spawnParams = {
812
+ task,
813
+ runTimeoutSeconds: opts?.timeoutSeconds ?? 300,
814
+ cleanup: opts?.cleanup ?? "keep"
815
+ };
816
+ if (opts?.label)
817
+ spawnParams.label = opts.label;
818
+ if (opts?.agentId)
819
+ spawnParams.agentId = opts.agentId;
820
+ if (opts?.model)
821
+ spawnParams.model = opts.model;
822
+ if (opts?.thinking)
823
+ spawnParams.thinking = opts.thinking;
824
+ const result = await svc.spawnSubagent(spawnParams, context);
825
+ if (result.status !== "accepted") {
826
+ const msg2 = `Failed to spawn subagent: ${result.error ?? "unknown error"}`;
827
+ await callback?.({ content: { text: msg2 } });
828
+ return { success: false, text: msg2 };
829
+ }
830
+ const labelText = opts?.label ? ` "${opts.label}"` : "";
831
+ const msg = `Spawned background task${labelText}. Run ID: ${result.runId?.slice(0, 8)}...
832
+ I'll announce results when complete.`;
833
+ await callback?.({ content: { text: msg } });
834
+ return {
835
+ success: true,
836
+ text: msg,
837
+ data: {
838
+ runId: result.runId,
839
+ childSessionKey: result.childSessionKey,
840
+ childRoomId: result.childRoomId
841
+ }
842
+ };
843
+ }
844
+ };
845
+ var sendToSessionAction = {
846
+ name: "SEND_TO_SESSION",
847
+ similes: ["SESSIONS_SEND", "SEND_MESSAGE", "MESSAGE_AGENT", "A2A_SEND"],
848
+ description: "Send a message to another agent session. Use sessionKey or label to identify the target.",
849
+ validate: async (runtime, message) => {
850
+ const svc = runtime.getService("SUBAGENT");
851
+ if (!svc) {
852
+ return false;
853
+ }
854
+ const text = message.content.text?.toLowerCase() ?? "";
855
+ return text.includes("send to session") || text.includes("message session") || text.includes("send") && text.includes("agent") || text.includes("sessions_send");
856
+ },
857
+ handler: async (runtime, message, _state, options, callback) => {
858
+ const svc = getSubagentService(runtime);
859
+ const context = extractSessionContext2(runtime, message);
860
+ const opts = options;
861
+ const targetMessage = opts?.message ?? message.content.text ?? "";
862
+ if (!targetMessage.trim()) {
863
+ const msg2 = "Please specify a message to send.";
864
+ await callback?.({ content: { text: msg2 } });
865
+ return { success: false, text: msg2 };
866
+ }
867
+ if (!opts?.sessionKey && !opts?.label) {
868
+ const msg2 = "Please specify either a sessionKey or label to identify the target session.";
869
+ await callback?.({ content: { text: msg2 } });
870
+ return { success: false, text: msg2 };
871
+ }
872
+ const sendParams = {
873
+ message: targetMessage,
874
+ timeoutSeconds: opts?.timeoutSeconds ?? 30
875
+ };
876
+ if (opts?.sessionKey)
877
+ sendParams.sessionKey = opts.sessionKey;
878
+ if (opts?.label)
879
+ sendParams.label = opts.label;
880
+ if (opts?.agentId)
881
+ sendParams.agentId = opts.agentId;
882
+ const result = await svc.sendToAgent(sendParams, context);
883
+ if (result.status === "forbidden") {
884
+ const msg2 = `Access denied: ${result.error}`;
885
+ await callback?.({ content: { text: msg2 } });
886
+ return { success: false, text: msg2 };
887
+ }
888
+ if (result.status === "error") {
889
+ const msg2 = `Failed to send message: ${result.error ?? "unknown error"}`;
890
+ await callback?.({ content: { text: msg2 } });
891
+ return { success: false, text: msg2 };
892
+ }
893
+ if (result.status === "timeout") {
894
+ const msg2 = `Request timed out waiting for response.`;
895
+ await callback?.({ content: { text: msg2 } });
896
+ return { success: false, text: msg2 };
897
+ }
898
+ const replyText = result.reply ? `
899
+
900
+ Reply: ${result.reply}` : "";
901
+ const msg = `Message sent to ${result.sessionKey}.${replyText}`;
902
+ await callback?.({ content: { text: msg } });
903
+ return {
904
+ success: true,
905
+ text: msg,
906
+ data: {
907
+ runId: result.runId,
908
+ sessionKey: result.sessionKey,
909
+ reply: result.reply
910
+ }
911
+ };
912
+ }
913
+ };
914
+ var listSubagentsAction = {
915
+ name: "LIST_SUBAGENTS",
916
+ similes: ["SHOW_SUBAGENTS", "SUBAGENT_STATUS", "RUNNING_TASKS"],
917
+ description: "List active and recent subagent runs.",
918
+ validate: async (runtime, message) => {
919
+ const svc = runtime.getService("SUBAGENT");
920
+ if (!svc) {
921
+ return false;
922
+ }
923
+ const text = message.content.text?.toLowerCase() ?? "";
924
+ return text.includes("list subagent") || text.includes("show subagent") || text.includes("subagent status") || text.includes("running task") || text.includes("background task");
925
+ },
926
+ handler: async (runtime, message, _state, _options, callback) => {
927
+ const svc = getSubagentService(runtime);
928
+ const context = extractSessionContext2(runtime, message);
929
+ const runs = svc.listSubagentRuns(context.sessionKey);
930
+ if (runs.length === 0) {
931
+ const msg2 = "No subagent runs found.";
932
+ await callback?.({ content: { text: msg2 } });
933
+ return { success: true, text: msg2 };
934
+ }
935
+ const lines = ["Subagent Runs:"];
936
+ for (const run of runs.slice(0, 20)) {
937
+ const status = run.outcome?.status ?? (run.endedAt ? "done" : "running");
938
+ const duration = run.endedAt ? formatDurationShort(run.endedAt - (run.startedAt ?? run.createdAt)) : "...";
939
+ const label = run.label || run.task.slice(0, 40);
940
+ lines.push(`- [${status}] ${label} (${duration})`);
941
+ }
942
+ const msg = lines.join(`
943
+ `);
944
+ await callback?.({ content: { text: msg } });
945
+ return {
946
+ success: true,
947
+ text: msg,
948
+ data: { count: runs.length }
949
+ };
950
+ }
951
+ };
952
+ var cancelSubagentAction = {
953
+ name: "CANCEL_SUBAGENT",
954
+ similes: ["STOP_SUBAGENT", "ABORT_TASK", "KILL_SUBAGENT"],
955
+ description: "Cancel a running subagent by its run ID.",
956
+ validate: async (runtime, message) => {
957
+ const svc = runtime.getService("SUBAGENT");
958
+ if (!svc) {
959
+ return false;
960
+ }
961
+ const text = message.content.text?.toLowerCase() ?? "";
962
+ return (text.includes("cancel") || text.includes("stop") || text.includes("abort")) && (text.includes("subagent") || text.includes("background"));
963
+ },
964
+ handler: async (runtime, _message, _state, options, callback) => {
965
+ const svc = getSubagentService(runtime);
966
+ const opts = options;
967
+ const runId = opts?.runId;
968
+ if (!runId) {
969
+ const msg2 = "Please specify the run ID to cancel. Use LIST_SUBAGENTS to see active runs.";
970
+ await callback?.({ content: { text: msg2 } });
971
+ return { success: false, text: msg2 };
972
+ }
973
+ const cancelled = svc.cancelSubagentRun(runId);
974
+ if (!cancelled) {
975
+ const msg2 = `No active run found with ID: ${runId.slice(0, 8)}...`;
976
+ await callback?.({ content: { text: msg2 } });
977
+ return { success: false, text: msg2 };
978
+ }
979
+ const msg = `Cancelled subagent run: ${runId.slice(0, 8)}...`;
980
+ await callback?.({ content: { text: msg } });
981
+ return { success: true, text: msg, data: { runId } };
982
+ }
983
+ };
984
+ var getSubagentStatusAction = {
985
+ name: "GET_SUBAGENT_STATUS",
986
+ similes: ["SUBAGENT_INFO", "TASK_STATUS", "CHECK_SUBAGENT"],
987
+ description: "Get detailed status of a specific subagent run.",
988
+ validate: async (runtime, message) => {
989
+ const svc = runtime.getService("SUBAGENT");
990
+ if (!svc) {
991
+ return false;
992
+ }
993
+ const text = message.content.text?.toLowerCase() ?? "";
994
+ return text.includes("subagent status") || text.includes("task status") || text.includes("check subagent") || text.includes("subagent info");
995
+ },
996
+ handler: async (runtime, _message, _state, options, callback) => {
997
+ const svc = getSubagentService(runtime);
998
+ const opts = options;
999
+ const runId = opts?.runId;
1000
+ if (!runId) {
1001
+ const msg2 = "Please specify the run ID to check. Use LIST_SUBAGENTS to see available runs.";
1002
+ await callback?.({ content: { text: msg2 } });
1003
+ return { success: false, text: msg2 };
1004
+ }
1005
+ const run = svc.getSubagentRun(runId);
1006
+ if (!run) {
1007
+ const msg2 = `No run found with ID: ${runId.slice(0, 8)}...`;
1008
+ await callback?.({ content: { text: msg2 } });
1009
+ return { success: false, text: msg2 };
1010
+ }
1011
+ const status = run.outcome?.status ?? (run.endedAt ? "done" : "running");
1012
+ const duration = run.endedAt ? formatDurationShort(run.endedAt - (run.startedAt ?? run.createdAt)) : "in progress";
1013
+ const lines = [
1014
+ `## Subagent Run: ${runId.slice(0, 8)}...`,
1015
+ "",
1016
+ `**Status:** ${status}`,
1017
+ `**Task:** ${run.task}`,
1018
+ run.label ? `**Label:** ${run.label}` : undefined,
1019
+ `**Duration:** ${duration}`,
1020
+ `**Session:** ${run.childSessionKey}`,
1021
+ run.outcome?.error ? `**Error:** ${run.outcome.error}` : undefined
1022
+ ].filter((l) => l !== undefined);
1023
+ const msg = lines.join(`
1024
+ `);
1025
+ await callback?.({ content: { text: msg } });
1026
+ return { success: true, text: msg, data: { run } };
1027
+ }
1028
+ };
1029
+
1
1030
  // src/actions/task-management.ts
2
1031
  function getService(runtime) {
3
1032
  const svc = runtime.getService("CODE_TASK");
@@ -13,7 +1042,11 @@ var createTaskAction = {
13
1042
  name: "CREATE_TASK",
14
1043
  similes: ["START_TASK", "SPAWN_TASK", "NEW_TASK", "BEGIN_TASK"],
15
1044
  description: "Create an orchestrated background task to be executed by a selected agent provider.",
16
- validate: async (_runtime, message) => {
1045
+ validate: async (runtime, message) => {
1046
+ const svc = runtime.getService("CODE_TASK");
1047
+ if (!svc) {
1048
+ return false;
1049
+ }
17
1050
  const text = message.content.text?.toLowerCase() ?? "";
18
1051
  const hasExplicit = text.includes("create task") || text.includes("new task") || text.includes("start a task");
19
1052
  const hasIntent = text.includes("implement") || text.includes("build") || text.includes("create") || text.includes("develop") || text.includes("refactor") || text.includes("fix") || text.includes("add");
@@ -44,7 +1077,10 @@ ${stepLines.map((s, i) => `${i + 1}. ${s}`).join(`
44
1077
  Provider: ${task.metadata.providerLabel ?? task.metadata.providerId}
45
1078
  Starting execution…`;
46
1079
  await callback?.({ content: { text: msg } });
47
- svc.startTaskExecution(task.id ?? "").catch(() => {});
1080
+ svc.startTaskExecution(task.id ?? "").catch((err) => {
1081
+ const errorMsg = err instanceof Error ? err.message : String(err);
1082
+ svc.appendOutput(task.id ?? "", `Execution error: ${errorMsg}`).catch(() => {});
1083
+ });
48
1084
  return { success: true, text: msg, data: { taskId: task.id ?? "" } };
49
1085
  }
50
1086
  };
@@ -52,8 +1088,12 @@ var listTasksAction = {
52
1088
  name: "LIST_TASKS",
53
1089
  similes: ["SHOW_TASKS", "GET_TASKS", "TASKS", "VIEW_TASKS"],
54
1090
  description: "List tasks managed by the orchestrator.",
55
- validate: async (_runtime, message) => {
56
- const t = message.content.text?.toLowerCase() ?? "";
1091
+ validate: async (runtime, message) => {
1092
+ const svc = runtime.getService("CODE_TASK");
1093
+ if (!svc) {
1094
+ return false;
1095
+ }
1096
+ const t = message.content.text?.toLowerCase() ?? "";
57
1097
  return t.includes("list task") || t.includes("show task") || t === "tasks" || t.includes("my task");
58
1098
  },
59
1099
  handler: async (runtime, _message, _state, _options, callback) => {
@@ -80,7 +1120,11 @@ var switchTaskAction = {
80
1120
  name: "SWITCH_TASK",
81
1121
  similes: ["SELECT_TASK", "SET_TASK", "CHANGE_TASK", "GO_TO_TASK"],
82
1122
  description: "Switch the current task context to a different task.",
83
- validate: async (_runtime, message) => {
1123
+ validate: async (runtime, message) => {
1124
+ const svc = runtime.getService("CODE_TASK");
1125
+ if (!svc) {
1126
+ return false;
1127
+ }
84
1128
  const t = message.content.text?.toLowerCase() ?? "";
85
1129
  return t.includes("switch to task") || t.includes("select task") || t.includes("task") && t.includes("switch");
86
1130
  },
@@ -109,7 +1153,11 @@ var searchTasksAction = {
109
1153
  name: "SEARCH_TASKS",
110
1154
  similes: ["FIND_TASK", "LOOKUP_TASK"],
111
1155
  description: "Search tasks by query.",
112
- validate: async (_runtime, message) => {
1156
+ validate: async (runtime, message) => {
1157
+ const svc = runtime.getService("CODE_TASK");
1158
+ if (!svc) {
1159
+ return false;
1160
+ }
113
1161
  const t = message.content.text?.toLowerCase() ?? "";
114
1162
  return t.includes("search task") || t.includes("find task") || t.includes("look for task");
115
1163
  },
@@ -142,7 +1190,11 @@ var pauseTaskAction = {
142
1190
  name: "PAUSE_TASK",
143
1191
  similes: ["STOP_TASK", "HALT_TASK"],
144
1192
  description: "Pause a running task.",
145
- validate: async (_runtime, message) => {
1193
+ validate: async (runtime, message) => {
1194
+ const svc = runtime.getService("CODE_TASK");
1195
+ if (!svc) {
1196
+ return false;
1197
+ }
146
1198
  const t = message.content.text?.toLowerCase() ?? "";
147
1199
  return (t.includes("pause") || t.includes("stop") || t.includes("halt")) && t.includes("task");
148
1200
  },
@@ -165,7 +1217,11 @@ var resumeTaskAction = {
165
1217
  name: "RESUME_TASK",
166
1218
  similes: ["CONTINUE_TASK", "RESTART_TASK", "RUN_TASK"],
167
1219
  description: "Resume a paused task.",
168
- validate: async (_runtime, message) => {
1220
+ validate: async (runtime, message) => {
1221
+ const svc = runtime.getService("CODE_TASK");
1222
+ if (!svc) {
1223
+ return false;
1224
+ }
169
1225
  const t = message.content.text?.toLowerCase() ?? "";
170
1226
  return t.includes("task") && (t.includes("resume") || t.includes("restart") || t.includes("continue"));
171
1227
  },
@@ -179,7 +1235,11 @@ var resumeTaskAction = {
179
1235
  return { success: false, text: msg2 };
180
1236
  }
181
1237
  await svc.resumeTask(task.id);
182
- svc.startTaskExecution(task.id).catch(() => {});
1238
+ const taskId = task.id;
1239
+ svc.startTaskExecution(taskId).catch((err) => {
1240
+ const errorMsg = err instanceof Error ? err.message : String(err);
1241
+ svc.appendOutput(taskId, `Execution error: ${errorMsg}`).catch(() => {});
1242
+ });
183
1243
  const msg = `Resumed task: ${task.name}`;
184
1244
  await callback?.({ content: { text: msg } });
185
1245
  return { success: true, text: msg };
@@ -189,7 +1249,11 @@ var cancelTaskAction = {
189
1249
  name: "CANCEL_TASK",
190
1250
  similes: ["DELETE_TASK", "REMOVE_TASK", "ABORT_TASK"],
191
1251
  description: "Cancel a task.",
192
- validate: async (_runtime, message) => {
1252
+ validate: async (runtime, message) => {
1253
+ const svc = runtime.getService("CODE_TASK");
1254
+ if (!svc) {
1255
+ return false;
1256
+ }
193
1257
  const t = message.content.text?.toLowerCase() ?? "";
194
1258
  return (t.includes("cancel") || t.includes("delete") || t.includes("remove")) && t.includes("task");
195
1259
  },
@@ -218,6 +1282,96 @@ function getConfiguredAgentOrchestratorOptions() {
218
1282
  return globalOptions;
219
1283
  }
220
1284
 
1285
+ // src/providers/orchestrator-config.ts
1286
+ var DEFAULT_CONFIG = {
1287
+ subagents: {
1288
+ enabled: true,
1289
+ timeoutSeconds: 300,
1290
+ allowAgents: [],
1291
+ archiveAfterMinutes: 60
1292
+ },
1293
+ agentToAgent: {
1294
+ enabled: false,
1295
+ allow: []
1296
+ },
1297
+ sandbox: {
1298
+ mode: "off",
1299
+ scope: "session",
1300
+ workspaceAccess: "rw"
1301
+ }
1302
+ };
1303
+ function getOrchestratorConfig(runtime) {
1304
+ const settings = runtime.character?.settings;
1305
+ if (!settings) {
1306
+ return DEFAULT_CONFIG;
1307
+ }
1308
+ const subagentsRaw = settings.subagents ?? {};
1309
+ const a2aRaw = settings.agentToAgent ?? {};
1310
+ const sandboxRaw = settings.sandbox ?? {};
1311
+ const subagents = {
1312
+ enabled: subagentsRaw.enabled ?? true,
1313
+ timeoutSeconds: subagentsRaw.timeoutSeconds ?? 300,
1314
+ allowAgents: subagentsRaw.allowAgents ?? [],
1315
+ archiveAfterMinutes: subagentsRaw.archiveAfterMinutes ?? 60
1316
+ };
1317
+ if (subagentsRaw.model)
1318
+ subagents.model = subagentsRaw.model;
1319
+ if (subagentsRaw.thinking)
1320
+ subagents.thinking = subagentsRaw.thinking;
1321
+ return {
1322
+ subagents,
1323
+ agentToAgent: {
1324
+ enabled: a2aRaw.enabled ?? DEFAULT_CONFIG.agentToAgent.enabled,
1325
+ allow: a2aRaw.allow ?? DEFAULT_CONFIG.agentToAgent.allow
1326
+ },
1327
+ sandbox: {
1328
+ ...DEFAULT_CONFIG.sandbox,
1329
+ ...sandboxRaw
1330
+ }
1331
+ };
1332
+ }
1333
+ function formatConfigContext(config) {
1334
+ const lines = ["## Orchestrator Configuration", ""];
1335
+ lines.push("### Subagents");
1336
+ lines.push(`- Enabled: ${config.subagents.enabled}`);
1337
+ if (config.subagents.model) {
1338
+ lines.push(`- Default Model: ${config.subagents.model}`);
1339
+ }
1340
+ if (config.subagents.thinking) {
1341
+ lines.push(`- Thinking Level: ${config.subagents.thinking}`);
1342
+ }
1343
+ lines.push(`- Timeout: ${config.subagents.timeoutSeconds}s`);
1344
+ const allowAgents = config.subagents.allowAgents ?? [];
1345
+ if (allowAgents.length > 0) {
1346
+ lines.push(`- Allowed Agents: ${allowAgents.join(", ")}`);
1347
+ }
1348
+ lines.push("");
1349
+ lines.push("### Agent-to-Agent Communication");
1350
+ lines.push(`- Enabled: ${config.agentToAgent.enabled}`);
1351
+ if (config.agentToAgent.allow.length > 0) {
1352
+ lines.push("- Allow Rules:");
1353
+ for (const rule of config.agentToAgent.allow) {
1354
+ lines.push(` - ${rule.source} → ${rule.target}`);
1355
+ }
1356
+ }
1357
+ lines.push("");
1358
+ lines.push("### Sandbox");
1359
+ lines.push(`- Mode: ${config.sandbox.mode ?? "off"}`);
1360
+ lines.push(`- Scope: ${config.sandbox.scope ?? "session"}`);
1361
+ lines.push(`- Workspace Access: ${config.sandbox.workspaceAccess ?? "rw"}`);
1362
+ lines.push("");
1363
+ return lines.join(`
1364
+ `);
1365
+ }
1366
+ var orchestratorConfigProvider = {
1367
+ name: "orchestrator_config",
1368
+ description: "Provides orchestrator configuration including subagents, A2A, and sandbox settings",
1369
+ get: async (runtime, _message, _state) => {
1370
+ const config = getOrchestratorConfig(runtime);
1371
+ return { text: formatConfigContext(config) };
1372
+ }
1373
+ };
1374
+
221
1375
  // src/providers/task-context.ts
222
1376
  function getService2(runtime) {
223
1377
  return runtime.getService("CODE_TASK");
@@ -279,7 +1433,7 @@ import {
279
1433
  Service
280
1434
  } from "@elizaos/core";
281
1435
 
282
- // ../../../node_modules/uuid/dist-node/stringify.js
1436
+ // ../../../node_modules/.bun/uuid@13.0.0/node_modules/uuid/dist-node/stringify.js
283
1437
  var byteToHex = [];
284
1438
  for (let i = 0;i < 256; ++i) {
285
1439
  byteToHex.push((i + 256).toString(16).slice(1));
@@ -288,7 +1442,7 @@ function unsafeStringify(arr, offset = 0) {
288
1442
  return (byteToHex[arr[offset + 0]] + byteToHex[arr[offset + 1]] + byteToHex[arr[offset + 2]] + byteToHex[arr[offset + 3]] + "-" + byteToHex[arr[offset + 4]] + byteToHex[arr[offset + 5]] + "-" + byteToHex[arr[offset + 6]] + byteToHex[arr[offset + 7]] + "-" + byteToHex[arr[offset + 8]] + byteToHex[arr[offset + 9]] + "-" + byteToHex[arr[offset + 10]] + byteToHex[arr[offset + 11]] + byteToHex[arr[offset + 12]] + byteToHex[arr[offset + 13]] + byteToHex[arr[offset + 14]] + byteToHex[arr[offset + 15]]).toLowerCase();
289
1443
  }
290
1444
 
291
- // ../../../node_modules/uuid/dist-node/rng.js
1445
+ // ../../../node_modules/.bun/uuid@13.0.0/node_modules/uuid/dist-node/rng.js
292
1446
  import { randomFillSync } from "node:crypto";
293
1447
  var rnds8Pool = new Uint8Array(256);
294
1448
  var poolPtr = rnds8Pool.length;
@@ -300,11 +1454,11 @@ function rng() {
300
1454
  return rnds8Pool.slice(poolPtr, poolPtr += 16);
301
1455
  }
302
1456
 
303
- // ../../../node_modules/uuid/dist-node/native.js
1457
+ // ../../../node_modules/.bun/uuid@13.0.0/node_modules/uuid/dist-node/native.js
304
1458
  import { randomUUID } from "node:crypto";
305
1459
  var native_default = { randomUUID };
306
1460
 
307
- // ../../../node_modules/uuid/dist-node/v4.js
1461
+ // ../../../node_modules/.bun/uuid@13.0.0/node_modules/uuid/dist-node/v4.js
308
1462
  function _v4(options, buf, offset) {
309
1463
  options = options || {};
310
1464
  const rnds = options.random ?? options.rng?.() ?? rng();
@@ -782,12 +1936,1824 @@ Provider: ${provider.label} (${provider.id})`);
782
1936
  `).trim();
783
1937
  }
784
1938
  }
1939
+
1940
+ // src/services/messaging-service.ts
1941
+ import crypto2 from "node:crypto";
1942
+ import { EventEmitter as EventEmitter2 } from "node:events";
1943
+ import { EventType, Service as Service2 } from "@elizaos/core";
1944
+ class MessagingService extends Service2 {
1945
+ static serviceType = "MESSAGING";
1946
+ capabilityDescription = "Unified cross-platform messaging for sending messages to any supported channel";
1947
+ emitter = new EventEmitter2;
1948
+ adapters = new Map;
1949
+ pendingDeliveries = new Map;
1950
+ initialized = false;
1951
+ static async start(runtime) {
1952
+ const service = new MessagingService(runtime);
1953
+ await service.initialize();
1954
+ return service;
1955
+ }
1956
+ async initialize() {
1957
+ if (this.initialized) {
1958
+ return;
1959
+ }
1960
+ this.initialized = true;
1961
+ this.registerBuiltInAdapters();
1962
+ }
1963
+ registerBuiltInAdapters() {
1964
+ this.registerAdapter({
1965
+ channel: "discord",
1966
+ isAvailable: () => {
1967
+ const service = this.runtime.getService("DISCORD");
1968
+ return !!service;
1969
+ },
1970
+ send: async (params) => this.sendViaDiscord(params)
1971
+ });
1972
+ this.registerAdapter({
1973
+ channel: "telegram",
1974
+ isAvailable: () => {
1975
+ const service = this.runtime.getService("TELEGRAM");
1976
+ return !!service;
1977
+ },
1978
+ send: async (params) => this.sendViaTelegram(params)
1979
+ });
1980
+ this.registerAdapter({
1981
+ channel: "slack",
1982
+ isAvailable: () => {
1983
+ const service = this.runtime.getService("slack");
1984
+ return !!service;
1985
+ },
1986
+ send: async (params) => this.sendViaSlack(params)
1987
+ });
1988
+ this.registerAdapter({
1989
+ channel: "whatsapp",
1990
+ isAvailable: () => {
1991
+ const service = this.runtime.getService("whatsapp");
1992
+ return !!service;
1993
+ },
1994
+ send: async (params) => this.sendViaWhatsApp(params)
1995
+ });
1996
+ this.registerAdapter({
1997
+ channel: "twitch",
1998
+ isAvailable: () => {
1999
+ const service = this.runtime.getService("twitch");
2000
+ return !!service;
2001
+ },
2002
+ send: async (params) => this.sendViaTwitch(params)
2003
+ });
2004
+ this.registerAdapter({
2005
+ channel: "internal",
2006
+ isAvailable: () => true,
2007
+ send: async (params) => this.sendViaInternal(params)
2008
+ });
2009
+ }
2010
+ registerAdapter(adapter) {
2011
+ this.adapters.set(adapter.channel, adapter);
2012
+ }
2013
+ getAdapter(channel) {
2014
+ return this.adapters.get(channel);
2015
+ }
2016
+ getAvailableChannels() {
2017
+ const channels = [];
2018
+ for (const [channel, adapter] of this.adapters) {
2019
+ if (adapter.isAvailable()) {
2020
+ channels.push(channel);
2021
+ }
2022
+ }
2023
+ return channels;
2024
+ }
2025
+ async send(params) {
2026
+ const idempotencyKey = params.idempotencyKey ?? crypto2.randomUUID();
2027
+ const channel = params.target.channel;
2028
+ const existing = this.pendingDeliveries.get(idempotencyKey);
2029
+ if (existing) {
2030
+ const result = {
2031
+ success: existing.status.status === "sent" || existing.status.status === "delivered",
2032
+ channel,
2033
+ targetId: params.target.to
2034
+ };
2035
+ if (existing.status.messageId)
2036
+ result.messageId = existing.status.messageId;
2037
+ if (existing.status.error)
2038
+ result.error = existing.status.error;
2039
+ if (existing.status.status === "sent")
2040
+ result.sentAt = existing.status.updatedAt;
2041
+ return result;
2042
+ }
2043
+ const status = {
2044
+ status: "pending",
2045
+ updatedAt: Date.now()
2046
+ };
2047
+ this.pendingDeliveries.set(idempotencyKey, { params, status });
2048
+ this.emitMessagingEvent("MESSAGING_SEND_REQUESTED", {
2049
+ idempotencyKey,
2050
+ channel,
2051
+ targetId: params.target.to,
2052
+ status: "pending"
2053
+ });
2054
+ const adapter = this.adapters.get(channel);
2055
+ if (!adapter) {
2056
+ const errorMsg = `No adapter registered for channel: ${channel}`;
2057
+ const result = {
2058
+ success: false,
2059
+ channel,
2060
+ targetId: params.target.to,
2061
+ error: errorMsg
2062
+ };
2063
+ status.status = "failed";
2064
+ status.error = errorMsg;
2065
+ status.updatedAt = Date.now();
2066
+ this.emitMessagingEvent("MESSAGING_SEND_FAILED", {
2067
+ idempotencyKey,
2068
+ channel,
2069
+ targetId: params.target.to,
2070
+ status: "failed",
2071
+ error: errorMsg
2072
+ });
2073
+ return result;
2074
+ }
2075
+ if (!adapter.isAvailable()) {
2076
+ const errorMsg = `${channel} service is not available`;
2077
+ const result = {
2078
+ success: false,
2079
+ channel,
2080
+ targetId: params.target.to,
2081
+ error: errorMsg
2082
+ };
2083
+ status.status = "failed";
2084
+ status.error = errorMsg;
2085
+ status.updatedAt = Date.now();
2086
+ this.emitMessagingEvent("MESSAGING_SEND_FAILED", {
2087
+ idempotencyKey,
2088
+ channel,
2089
+ targetId: params.target.to,
2090
+ status: "failed",
2091
+ error: errorMsg
2092
+ });
2093
+ return result;
2094
+ }
2095
+ try {
2096
+ const result = await adapter.send({ ...params, idempotencyKey });
2097
+ status.status = result.success ? "sent" : "failed";
2098
+ if (result.messageId)
2099
+ status.messageId = result.messageId;
2100
+ if (result.error)
2101
+ status.error = result.error;
2102
+ status.updatedAt = Date.now();
2103
+ if (result.success) {
2104
+ const sentPayload = {
2105
+ idempotencyKey,
2106
+ channel,
2107
+ targetId: params.target.to,
2108
+ status: "sent"
2109
+ };
2110
+ if (result.messageId)
2111
+ sentPayload.messageId = result.messageId;
2112
+ if (result.sentAt)
2113
+ sentPayload.sentAt = result.sentAt;
2114
+ this.emitMessagingEvent("MESSAGING_SENT", sentPayload);
2115
+ } else {
2116
+ const failedPayload = {
2117
+ idempotencyKey,
2118
+ channel,
2119
+ targetId: params.target.to,
2120
+ status: "failed"
2121
+ };
2122
+ if (result.error)
2123
+ failedPayload.error = result.error;
2124
+ this.emitMessagingEvent("MESSAGING_SEND_FAILED", failedPayload);
2125
+ }
2126
+ return result;
2127
+ } catch (error) {
2128
+ const errorMessage = error instanceof Error ? error.message : String(error);
2129
+ status.status = "failed";
2130
+ status.error = errorMessage;
2131
+ status.updatedAt = Date.now();
2132
+ this.emitMessagingEvent("MESSAGING_SEND_FAILED", {
2133
+ idempotencyKey,
2134
+ channel,
2135
+ targetId: params.target.to,
2136
+ status: "failed",
2137
+ error: errorMessage
2138
+ });
2139
+ return {
2140
+ success: false,
2141
+ channel,
2142
+ targetId: params.target.to,
2143
+ error: errorMessage
2144
+ };
2145
+ }
2146
+ }
2147
+ async sendToDeliveryContext(deliveryContext, content, options) {
2148
+ const channel = this.normalizeChannel(deliveryContext.channel);
2149
+ const to = deliveryContext.to ?? deliveryContext.accountId ?? "";
2150
+ if (!to) {
2151
+ return {
2152
+ success: false,
2153
+ channel,
2154
+ targetId: "",
2155
+ error: "No recipient specified in delivery context"
2156
+ };
2157
+ }
2158
+ return this.send({
2159
+ target: {
2160
+ channel,
2161
+ to,
2162
+ ...deliveryContext.accountId ? { accountId: deliveryContext.accountId } : {},
2163
+ ...deliveryContext.threadId !== undefined ? { threadId: deliveryContext.threadId } : {}
2164
+ },
2165
+ content,
2166
+ ...options?.idempotencyKey ? { idempotencyKey: options.idempotencyKey } : {},
2167
+ ...options?.timeoutMs !== undefined ? { timeoutMs: options.timeoutMs } : {}
2168
+ });
2169
+ }
2170
+ async sendToRoom(roomId, content, options) {
2171
+ const room = await this.runtime.getRoom(roomId);
2172
+ if (!room) {
2173
+ return {
2174
+ success: false,
2175
+ channel: "unknown",
2176
+ targetId: roomId,
2177
+ error: `Room not found: ${roomId}`
2178
+ };
2179
+ }
2180
+ const metadata = room.metadata;
2181
+ const channel = this.normalizeChannel(metadata?.messagingChannel);
2182
+ const to = metadata?.messagingTo ?? room.channelId ?? "";
2183
+ if (!to) {
2184
+ return {
2185
+ success: false,
2186
+ channel,
2187
+ targetId: roomId,
2188
+ error: "Room has no messaging target configured"
2189
+ };
2190
+ }
2191
+ return this.send({
2192
+ target: {
2193
+ channel,
2194
+ to,
2195
+ ...metadata?.messagingAccountId ? { accountId: metadata.messagingAccountId } : {},
2196
+ ...metadata?.messagingThreadId !== undefined ? { threadId: metadata.messagingThreadId } : {}
2197
+ },
2198
+ content,
2199
+ ...options?.idempotencyKey ? { idempotencyKey: options.idempotencyKey } : {},
2200
+ ...options?.timeoutMs !== undefined ? { timeoutMs: options.timeoutMs } : {}
2201
+ });
2202
+ }
2203
+ async sendToSession(sessionKey, content, options) {
2204
+ const agentId = extractAgentIdFromSessionKey(sessionKey);
2205
+ const roomId = sessionKeyToRoomId(sessionKey, agentId);
2206
+ return this.sendToRoom(roomId, content, options);
2207
+ }
2208
+ async sendViaDiscord(params) {
2209
+ const discordService = this.runtime.getService("DISCORD");
2210
+ if (!discordService?.client) {
2211
+ return {
2212
+ success: false,
2213
+ channel: "discord",
2214
+ targetId: params.target.to,
2215
+ error: "Discord service not available"
2216
+ };
2217
+ }
2218
+ try {
2219
+ const channel = await discordService.client.channels.fetch(params.target.to);
2220
+ if (!channel?.send) {
2221
+ return {
2222
+ success: false,
2223
+ channel: "discord",
2224
+ targetId: params.target.to,
2225
+ error: "Channel not found or not a text channel"
2226
+ };
2227
+ }
2228
+ const message = await channel.send({
2229
+ content: params.content.text,
2230
+ ...params.target.replyToMessageId ? { reply: { messageReference: params.target.replyToMessageId } } : {}
2231
+ });
2232
+ return {
2233
+ success: true,
2234
+ messageId: message.id,
2235
+ channel: "discord",
2236
+ targetId: params.target.to,
2237
+ sentAt: Date.now()
2238
+ };
2239
+ } catch (error) {
2240
+ return {
2241
+ success: false,
2242
+ channel: "discord",
2243
+ targetId: params.target.to,
2244
+ error: error instanceof Error ? error.message : String(error)
2245
+ };
2246
+ }
2247
+ }
2248
+ async sendViaTelegram(params) {
2249
+ const telegramService = this.runtime.getService("TELEGRAM");
2250
+ if (!telegramService?.bot?.telegram) {
2251
+ return {
2252
+ success: false,
2253
+ channel: "telegram",
2254
+ targetId: params.target.to,
2255
+ error: "Telegram service not available"
2256
+ };
2257
+ }
2258
+ try {
2259
+ const chatId = Number.isNaN(Number(params.target.to)) ? params.target.to : Number(params.target.to);
2260
+ const result = await telegramService.bot.telegram.sendMessage(chatId, params.content.text, {
2261
+ reply_to_message_id: params.target.replyToMessageId ? Number(params.target.replyToMessageId) : undefined,
2262
+ disable_web_page_preview: params.content.disableLinkPreview,
2263
+ disable_notification: params.content.silent
2264
+ });
2265
+ return {
2266
+ success: true,
2267
+ messageId: String(result.message_id),
2268
+ channel: "telegram",
2269
+ targetId: params.target.to,
2270
+ sentAt: Date.now()
2271
+ };
2272
+ } catch (error) {
2273
+ return {
2274
+ success: false,
2275
+ channel: "telegram",
2276
+ targetId: params.target.to,
2277
+ error: error instanceof Error ? error.message : String(error)
2278
+ };
2279
+ }
2280
+ }
2281
+ async sendViaSlack(params) {
2282
+ const slackService = this.runtime.getService("slack");
2283
+ if (!slackService?.sendMessage) {
2284
+ return {
2285
+ success: false,
2286
+ channel: "slack",
2287
+ targetId: params.target.to,
2288
+ error: "Slack service not available or sendMessage method not found"
2289
+ };
2290
+ }
2291
+ try {
2292
+ const result = await slackService.sendMessage(params.target.to, params.content.text, {
2293
+ ...params.target.threadId ? { threadTs: String(params.target.threadId) } : {},
2294
+ replyBroadcast: false
2295
+ });
2296
+ return {
2297
+ success: true,
2298
+ messageId: result.ts,
2299
+ channel: "slack",
2300
+ targetId: params.target.to,
2301
+ sentAt: Date.now()
2302
+ };
2303
+ } catch (error) {
2304
+ return {
2305
+ success: false,
2306
+ channel: "slack",
2307
+ targetId: params.target.to,
2308
+ error: error instanceof Error ? error.message : String(error)
2309
+ };
2310
+ }
2311
+ }
2312
+ async sendViaWhatsApp(params) {
2313
+ const whatsappService = this.runtime.getService("whatsapp");
2314
+ if (!whatsappService?.sendText) {
2315
+ return {
2316
+ success: false,
2317
+ channel: "whatsapp",
2318
+ targetId: params.target.to,
2319
+ error: "WhatsApp service not available or sendText method not found"
2320
+ };
2321
+ }
2322
+ try {
2323
+ const result = await whatsappService.sendText(params.target.to, params.content.text);
2324
+ const messageId = result.messages?.[0]?.id;
2325
+ return {
2326
+ success: true,
2327
+ ...messageId ? { messageId } : {},
2328
+ channel: "whatsapp",
2329
+ targetId: params.target.to,
2330
+ sentAt: Date.now()
2331
+ };
2332
+ } catch (error) {
2333
+ return {
2334
+ success: false,
2335
+ channel: "whatsapp",
2336
+ targetId: params.target.to,
2337
+ error: error instanceof Error ? error.message : String(error)
2338
+ };
2339
+ }
2340
+ }
2341
+ async sendViaTwitch(params) {
2342
+ const twitchService = this.runtime.getService("twitch");
2343
+ if (!twitchService?.sendMessage) {
2344
+ return {
2345
+ success: false,
2346
+ channel: "twitch",
2347
+ targetId: params.target.to,
2348
+ error: "Twitch service not available or sendMessage method not found"
2349
+ };
2350
+ }
2351
+ try {
2352
+ const result = await twitchService.sendMessage(params.content.text, {
2353
+ channel: params.target.to,
2354
+ ...params.target.replyToMessageId ? { replyTo: params.target.replyToMessageId } : {}
2355
+ });
2356
+ return {
2357
+ success: result.success,
2358
+ ...result.messageId !== undefined ? { messageId: result.messageId } : {},
2359
+ channel: "twitch",
2360
+ targetId: params.target.to,
2361
+ sentAt: Date.now()
2362
+ };
2363
+ } catch (error) {
2364
+ return {
2365
+ success: false,
2366
+ channel: "twitch",
2367
+ targetId: params.target.to,
2368
+ error: error instanceof Error ? error.message : String(error)
2369
+ };
2370
+ }
2371
+ }
2372
+ async sendViaInternal(params) {
2373
+ const roomId = params.target.to;
2374
+ const messageId = crypto2.randomUUID();
2375
+ try {
2376
+ const memory = {
2377
+ id: messageId,
2378
+ entityId: this.runtime.agentId,
2379
+ agentId: this.runtime.agentId,
2380
+ roomId,
2381
+ content: {
2382
+ text: params.content.text,
2383
+ type: "text",
2384
+ source: "internal",
2385
+ metadata: {
2386
+ isInternalMessage: true,
2387
+ idempotencyKey: params.idempotencyKey
2388
+ }
2389
+ },
2390
+ createdAt: Date.now()
2391
+ };
2392
+ await this.runtime.emitEvent(EventType.MESSAGE_RECEIVED, {
2393
+ runtime: this.runtime,
2394
+ message: memory,
2395
+ source: "internal_messaging"
2396
+ });
2397
+ return {
2398
+ success: true,
2399
+ messageId,
2400
+ channel: "internal",
2401
+ targetId: params.target.to,
2402
+ sentAt: Date.now()
2403
+ };
2404
+ } catch (error) {
2405
+ return {
2406
+ success: false,
2407
+ channel: "internal",
2408
+ targetId: params.target.to,
2409
+ error: error instanceof Error ? error.message : String(error)
2410
+ };
2411
+ }
2412
+ }
2413
+ normalizeChannel(channel) {
2414
+ if (!channel) {
2415
+ return "unknown";
2416
+ }
2417
+ const lower = channel.toLowerCase();
2418
+ if (lower === "discord" || lower.includes("discord")) {
2419
+ return "discord";
2420
+ }
2421
+ if (lower === "telegram" || lower.includes("telegram")) {
2422
+ return "telegram";
2423
+ }
2424
+ if (lower === "slack" || lower.includes("slack")) {
2425
+ return "slack";
2426
+ }
2427
+ if (lower === "whatsapp" || lower.includes("whatsapp")) {
2428
+ return "whatsapp";
2429
+ }
2430
+ if (lower === "twitch" || lower.includes("twitch")) {
2431
+ return "twitch";
2432
+ }
2433
+ if (lower === "google_chat" || lower.includes("google") || lower.includes("gchat")) {
2434
+ return "google_chat";
2435
+ }
2436
+ if (lower === "internal" || lower === "a2a") {
2437
+ return "internal";
2438
+ }
2439
+ return "unknown";
2440
+ }
2441
+ on(event, handler) {
2442
+ this.emitter.on(event, handler);
2443
+ }
2444
+ off(event, handler) {
2445
+ this.emitter.off(event, handler);
2446
+ }
2447
+ emitMessagingEvent(type, payload) {
2448
+ this.emitter.emit(type, payload);
2449
+ this.emitter.emit("messaging", { type, ...payload });
2450
+ }
2451
+ async stop() {
2452
+ this.pendingDeliveries.clear();
2453
+ this.emitter.removeAllListeners();
2454
+ }
2455
+ }
2456
+
2457
+ // src/services/sandbox-service.ts
2458
+ import { spawn } from "node:child_process";
2459
+ import { EventEmitter as EventEmitter3 } from "node:events";
2460
+ import fs from "node:fs/promises";
2461
+ import os from "node:os";
2462
+ import path from "node:path";
2463
+ import { Service as Service3 } from "@elizaos/core";
2464
+ var DEFAULT_DOCKER_CONFIG = {
2465
+ image: "ubuntu:22.04",
2466
+ containerPrefix: "eliza-sandbox",
2467
+ workdir: "/workspace",
2468
+ autoRemove: true,
2469
+ memoryLimit: "2g",
2470
+ cpuLimit: "2",
2471
+ network: "none",
2472
+ env: {},
2473
+ mounts: [],
2474
+ extraArgs: []
2475
+ };
2476
+ var DEFAULT_BROWSER_CONFIG = {
2477
+ enabled: false,
2478
+ image: "browserless/chrome:latest",
2479
+ containerPrefix: "eliza-browser",
2480
+ cdpPort: 9222,
2481
+ vncPort: 5900,
2482
+ noVncPort: 6080,
2483
+ headless: true,
2484
+ enableNoVnc: false,
2485
+ allowHostControl: false,
2486
+ autoStart: false,
2487
+ autoStartTimeoutMs: 30000
2488
+ };
2489
+ var DEFAULT_PRUNE_CONFIG = {
2490
+ idleHours: 1,
2491
+ maxAgeDays: 7
2492
+ };
2493
+
2494
+ class SandboxService extends Service3 {
2495
+ static serviceType = "SANDBOX";
2496
+ capabilityDescription = "Manages sandboxed execution environments for secure tool execution";
2497
+ emitter = new EventEmitter3;
2498
+ contexts = new Map;
2499
+ activeContainers = new Set;
2500
+ containerLocks = new Map;
2501
+ sweeper = null;
2502
+ initialized = false;
2503
+ static async start(runtime) {
2504
+ const service = new SandboxService(runtime);
2505
+ await service.initialize();
2506
+ return service;
2507
+ }
2508
+ async initialize() {
2509
+ if (this.initialized) {
2510
+ return;
2511
+ }
2512
+ this.initialized = true;
2513
+ this.startSweeper();
2514
+ }
2515
+ getConfig() {
2516
+ const settings = this.runtime.character?.settings;
2517
+ const sandbox = settings?.sandbox ?? {};
2518
+ return {
2519
+ mode: sandbox.mode ?? "off",
2520
+ scope: sandbox.scope ?? "session",
2521
+ workspaceAccess: sandbox.workspaceAccess ?? "rw",
2522
+ workspaceRoot: sandbox.workspaceRoot ?? path.join(os.homedir(), ".eliza", "sandboxes"),
2523
+ docker: { ...DEFAULT_DOCKER_CONFIG, ...sandbox.docker },
2524
+ browser: { ...DEFAULT_BROWSER_CONFIG, ...sandbox.browser },
2525
+ tools: sandbox.tools ?? { allow: [], deny: [] },
2526
+ prune: { ...DEFAULT_PRUNE_CONFIG, ...sandbox.prune }
2527
+ };
2528
+ }
2529
+ shouldSandbox(sessionKey) {
2530
+ const config = this.getConfig();
2531
+ if (config.mode === "off") {
2532
+ return false;
2533
+ }
2534
+ if (config.mode === "all") {
2535
+ return true;
2536
+ }
2537
+ const parsed = parseSessionKey(sessionKey);
2538
+ if (parsed.keyType === "subagent") {
2539
+ return true;
2540
+ }
2541
+ const identifier = parsed.identifier.toLowerCase();
2542
+ if (identifier === "main" || identifier === "global") {
2543
+ return false;
2544
+ }
2545
+ return true;
2546
+ }
2547
+ isToolAllowed(toolName, policy) {
2548
+ const config = this.getConfig();
2549
+ const p = policy ?? config.tools;
2550
+ if (p.allow && p.allow.length > 0) {
2551
+ const allowed = p.allow.some((pattern) => pattern === "*" || pattern.toLowerCase() === toolName.toLowerCase() || pattern.endsWith("*") && toolName.toLowerCase().startsWith(pattern.slice(0, -1).toLowerCase()));
2552
+ if (allowed) {
2553
+ return true;
2554
+ }
2555
+ }
2556
+ if (p.deny && p.deny.length > 0) {
2557
+ const denied = p.deny.some((pattern) => pattern === "*" || pattern.toLowerCase() === toolName.toLowerCase() || pattern.endsWith("*") && toolName.toLowerCase().startsWith(pattern.slice(0, -1).toLowerCase()));
2558
+ if (denied) {
2559
+ return false;
2560
+ }
2561
+ }
2562
+ return !p.allow || p.allow.length === 0;
2563
+ }
2564
+ async getSandboxContext(sessionKey, options) {
2565
+ const trimmedKey = sessionKey.trim();
2566
+ if (!trimmedKey) {
2567
+ return null;
2568
+ }
2569
+ if (!this.shouldSandbox(trimmedKey)) {
2570
+ return null;
2571
+ }
2572
+ const cached = this.contexts.get(trimmedKey);
2573
+ if (cached) {
2574
+ cached.lastAccessedAt = Date.now();
2575
+ return cached;
2576
+ }
2577
+ const config = this.getConfig();
2578
+ const agentId = extractAgentIdFromSessionKey(trimmedKey);
2579
+ const agentWorkspaceDir = options?.workspaceDir ?? path.join(os.homedir(), ".eliza", "workspace");
2580
+ const scopeKey = this.resolveScopeKey(config.scope, trimmedKey);
2581
+ const sandboxWorkspaceDir = config.scope === "shared" ? config.workspaceRoot : path.join(config.workspaceRoot, scopeKey);
2582
+ const workspaceDir = config.workspaceAccess === "rw" ? agentWorkspaceDir : sandboxWorkspaceDir;
2583
+ await fs.mkdir(workspaceDir, { recursive: true });
2584
+ const containerName = `${config.docker.containerPrefix}-${scopeKey.slice(0, 12)}`;
2585
+ const context = {
2586
+ enabled: true,
2587
+ sessionKey: trimmedKey,
2588
+ roomId: options?.roomId ?? sessionKeyToRoomId(trimmedKey, agentId),
2589
+ workspaceDir,
2590
+ agentWorkspaceDir,
2591
+ workspaceAccess: config.workspaceAccess,
2592
+ containerName,
2593
+ containerWorkdir: config.docker.workdir,
2594
+ docker: config.docker,
2595
+ tools: config.tools,
2596
+ browserAllowHostControl: config.browser.allowHostControl,
2597
+ createdAt: Date.now(),
2598
+ lastAccessedAt: Date.now()
2599
+ };
2600
+ this.contexts.set(trimmedKey, context);
2601
+ this.emitSandboxEvent("SANDBOX_CREATED", {
2602
+ sessionKey: trimmedKey,
2603
+ ...context.roomId !== undefined ? { roomId: context.roomId } : {},
2604
+ containerName
2605
+ });
2606
+ return context;
2607
+ }
2608
+ resolveScopeKey(scope, sessionKey) {
2609
+ const parsed = parseSessionKey(sessionKey);
2610
+ switch (scope) {
2611
+ case "session":
2612
+ return hashToUUID(sessionKey).slice(0, 16);
2613
+ case "agent":
2614
+ return hashToUUID(parsed.agentId).slice(0, 16);
2615
+ case "shared":
2616
+ return "shared";
2617
+ default:
2618
+ return hashToUUID(sessionKey).slice(0, 16);
2619
+ }
2620
+ }
2621
+ async destroySandbox(sessionKey) {
2622
+ const context = this.contexts.get(sessionKey);
2623
+ if (!context) {
2624
+ return;
2625
+ }
2626
+ if (this.activeContainers.has(context.containerName)) {
2627
+ await this.stopContainer(context.containerName);
2628
+ }
2629
+ if (context.browser) {
2630
+ await this.stopContainer(context.browser.containerName);
2631
+ }
2632
+ this.contexts.delete(sessionKey);
2633
+ this.emitSandboxEvent("SANDBOX_DESTROYED", {
2634
+ sessionKey,
2635
+ ...context.roomId !== undefined ? { roomId: context.roomId } : {},
2636
+ containerName: context.containerName
2637
+ });
2638
+ }
2639
+ async execute(sessionKey, params) {
2640
+ const context = await this.getSandboxContext(sessionKey);
2641
+ if (!context) {
2642
+ return this.executeLocal(params);
2643
+ }
2644
+ const startTime = Date.now();
2645
+ this.emitSandboxEvent("SANDBOX_COMMAND_STARTED", {
2646
+ sessionKey,
2647
+ ...context.roomId !== undefined ? { roomId: context.roomId } : {},
2648
+ containerName: context.containerName,
2649
+ command: params.command
2650
+ });
2651
+ try {
2652
+ const result = await this.executeInContainer(context, params);
2653
+ this.emitSandboxEvent("SANDBOX_COMMAND_COMPLETED", {
2654
+ sessionKey,
2655
+ ...context.roomId !== undefined ? { roomId: context.roomId } : {},
2656
+ containerName: context.containerName,
2657
+ command: params.command,
2658
+ result
2659
+ });
2660
+ return result;
2661
+ } catch (error) {
2662
+ const errorMessage = error instanceof Error ? error.message : String(error);
2663
+ const result = {
2664
+ success: false,
2665
+ exitCode: 1,
2666
+ stdout: "",
2667
+ stderr: errorMessage,
2668
+ durationMs: Date.now() - startTime,
2669
+ timedOut: false,
2670
+ error: errorMessage
2671
+ };
2672
+ this.emitSandboxEvent("SANDBOX_COMMAND_FAILED", {
2673
+ sessionKey,
2674
+ ...context.roomId !== undefined ? { roomId: context.roomId } : {},
2675
+ containerName: context.containerName,
2676
+ command: params.command,
2677
+ result,
2678
+ error: errorMessage
2679
+ });
2680
+ return result;
2681
+ }
2682
+ }
2683
+ executeLocal(params) {
2684
+ return new Promise((resolve) => {
2685
+ const startTime = Date.now();
2686
+ const timeout = params.timeoutMs ?? 60000;
2687
+ const child = spawn(params.command, params.args ?? [], {
2688
+ cwd: params.cwd,
2689
+ env: { ...process.env, ...params.env },
2690
+ shell: true
2691
+ });
2692
+ let stdout = "";
2693
+ let stderr = "";
2694
+ let timedOut = false;
2695
+ const timeoutId = setTimeout(() => {
2696
+ timedOut = true;
2697
+ child.kill("SIGKILL");
2698
+ }, timeout);
2699
+ child.stdout?.on("data", (data) => {
2700
+ stdout += data.toString();
2701
+ });
2702
+ child.stderr?.on("data", (data) => {
2703
+ stderr += data.toString();
2704
+ });
2705
+ if (params.stdin) {
2706
+ child.stdin?.write(params.stdin);
2707
+ child.stdin?.end();
2708
+ }
2709
+ child.on("close", (code) => {
2710
+ clearTimeout(timeoutId);
2711
+ const result = {
2712
+ success: code === 0 && !timedOut,
2713
+ exitCode: code ?? 1,
2714
+ stdout: stdout.slice(0, 1e5),
2715
+ stderr: stderr.slice(0, 1e5),
2716
+ durationMs: Date.now() - startTime,
2717
+ timedOut
2718
+ };
2719
+ if (timedOut)
2720
+ result.error = "Command timed out";
2721
+ resolve(result);
2722
+ });
2723
+ child.on("error", (error) => {
2724
+ clearTimeout(timeoutId);
2725
+ resolve({
2726
+ success: false,
2727
+ exitCode: 1,
2728
+ stdout,
2729
+ stderr: error.message,
2730
+ durationMs: Date.now() - startTime,
2731
+ timedOut: false,
2732
+ error: error.message
2733
+ });
2734
+ });
2735
+ });
2736
+ }
2737
+ executeDockerCommand(args, options) {
2738
+ return new Promise((resolve) => {
2739
+ const startTime = Date.now();
2740
+ const timeout = options?.timeoutMs ?? 60000;
2741
+ const child = spawn("docker", args, {
2742
+ env: process.env
2743
+ });
2744
+ let stdout = "";
2745
+ let stderr = "";
2746
+ let timedOut = false;
2747
+ const timeoutId = setTimeout(() => {
2748
+ timedOut = true;
2749
+ child.kill("SIGKILL");
2750
+ }, timeout);
2751
+ child.stdout?.on("data", (data) => {
2752
+ stdout += data.toString();
2753
+ });
2754
+ child.stderr?.on("data", (data) => {
2755
+ stderr += data.toString();
2756
+ });
2757
+ child.on("close", (code) => {
2758
+ clearTimeout(timeoutId);
2759
+ const result = {
2760
+ success: code === 0 && !timedOut,
2761
+ exitCode: code ?? 1,
2762
+ stdout: stdout.slice(0, 1e5),
2763
+ stderr: stderr.slice(0, 1e5),
2764
+ durationMs: Date.now() - startTime,
2765
+ timedOut
2766
+ };
2767
+ if (timedOut)
2768
+ result.error = "Command timed out";
2769
+ resolve(result);
2770
+ });
2771
+ child.on("error", (error) => {
2772
+ clearTimeout(timeoutId);
2773
+ resolve({
2774
+ success: false,
2775
+ exitCode: 1,
2776
+ stdout,
2777
+ stderr: error.message,
2778
+ durationMs: Date.now() - startTime,
2779
+ timedOut: false,
2780
+ error: error.message
2781
+ });
2782
+ });
2783
+ });
2784
+ }
2785
+ async executeInContainer(context, params) {
2786
+ const startTime = Date.now();
2787
+ const timeout = params.timeoutMs ?? 60000;
2788
+ await this.ensureContainer(context);
2789
+ const dockerArgs = ["exec"];
2790
+ const workdir = params.cwd ? path.join(context.containerWorkdir, params.cwd) : context.containerWorkdir;
2791
+ dockerArgs.push("-w", workdir);
2792
+ for (const [key, value] of Object.entries(params.env ?? {})) {
2793
+ dockerArgs.push("-e", `${key}=${value}`);
2794
+ }
2795
+ dockerArgs.push(context.containerName);
2796
+ dockerArgs.push("sh", "-c", params.command);
2797
+ return new Promise((resolve) => {
2798
+ const child = spawn("docker", dockerArgs, {
2799
+ env: process.env
2800
+ });
2801
+ let stdout = "";
2802
+ let stderr = "";
2803
+ let timedOut = false;
2804
+ const timeoutId = setTimeout(() => {
2805
+ timedOut = true;
2806
+ child.kill("SIGKILL");
2807
+ }, timeout);
2808
+ child.stdout?.on("data", (data) => {
2809
+ stdout += data.toString();
2810
+ });
2811
+ child.stderr?.on("data", (data) => {
2812
+ stderr += data.toString();
2813
+ });
2814
+ if (params.stdin) {
2815
+ child.stdin?.write(params.stdin);
2816
+ child.stdin?.end();
2817
+ }
2818
+ child.on("close", (code) => {
2819
+ clearTimeout(timeoutId);
2820
+ const result = {
2821
+ success: code === 0 && !timedOut,
2822
+ exitCode: code ?? 1,
2823
+ stdout: stdout.slice(0, 1e5),
2824
+ stderr: stderr.slice(0, 1e5),
2825
+ durationMs: Date.now() - startTime,
2826
+ timedOut
2827
+ };
2828
+ if (timedOut)
2829
+ result.error = "Command timed out";
2830
+ resolve(result);
2831
+ });
2832
+ child.on("error", (error) => {
2833
+ clearTimeout(timeoutId);
2834
+ resolve({
2835
+ success: false,
2836
+ exitCode: 1,
2837
+ stdout,
2838
+ stderr: error.message,
2839
+ durationMs: Date.now() - startTime,
2840
+ timedOut: false,
2841
+ error: error.message
2842
+ });
2843
+ });
2844
+ });
2845
+ }
2846
+ async ensureContainer(context) {
2847
+ if (this.activeContainers.has(context.containerName)) {
2848
+ return;
2849
+ }
2850
+ const existingLock = this.containerLocks.get(context.containerName);
2851
+ if (existingLock) {
2852
+ await existingLock;
2853
+ return;
2854
+ }
2855
+ const lockPromise = this.ensureContainerInternal(context);
2856
+ this.containerLocks.set(context.containerName, lockPromise);
2857
+ try {
2858
+ await lockPromise;
2859
+ } finally {
2860
+ this.containerLocks.delete(context.containerName);
2861
+ }
2862
+ }
2863
+ async ensureContainerInternal(context) {
2864
+ if (this.activeContainers.has(context.containerName)) {
2865
+ return;
2866
+ }
2867
+ const checkResult = await this.executeDockerCommand([
2868
+ "ps",
2869
+ "-a",
2870
+ "-q",
2871
+ "-f",
2872
+ `name=^${context.containerName}$`
2873
+ ]);
2874
+ if (checkResult.stdout.trim()) {
2875
+ const startResult = await this.executeDockerCommand(["start", context.containerName]);
2876
+ if (startResult.success) {
2877
+ this.activeContainers.add(context.containerName);
2878
+ return;
2879
+ }
2880
+ }
2881
+ const dockerArgs = ["run", "-d", "--name", context.containerName];
2882
+ if (context.docker.memoryLimit) {
2883
+ dockerArgs.push("-m", context.docker.memoryLimit);
2884
+ }
2885
+ if (context.docker.cpuLimit) {
2886
+ dockerArgs.push("--cpus", context.docker.cpuLimit);
2887
+ }
2888
+ if (context.docker.network) {
2889
+ dockerArgs.push("--network", context.docker.network);
2890
+ }
2891
+ const mountMode = context.workspaceAccess === "ro" ? "ro" : "rw";
2892
+ dockerArgs.push("-v", `${context.workspaceDir}:${context.containerWorkdir}:${mountMode}`);
2893
+ for (const mount of context.docker.mounts ?? []) {
2894
+ dockerArgs.push("-v", `${mount.host}:${mount.container}:${mount.mode}`);
2895
+ }
2896
+ for (const [key, value] of Object.entries(context.docker.env ?? {})) {
2897
+ dockerArgs.push("-e", `${key}=${value}`);
2898
+ }
2899
+ if (context.docker.extraArgs) {
2900
+ dockerArgs.push(...context.docker.extraArgs);
2901
+ }
2902
+ dockerArgs.push(context.docker.image, "tail", "-f", "/dev/null");
2903
+ const result = await this.executeDockerCommand(dockerArgs);
2904
+ if (result.success) {
2905
+ this.activeContainers.add(context.containerName);
2906
+ } else {
2907
+ throw new Error(`Failed to create container: ${result.stderr}`);
2908
+ }
2909
+ }
2910
+ async stopContainer(containerName) {
2911
+ await this.executeDockerCommand(["stop", containerName], { timeoutMs: 1e4 });
2912
+ await this.executeDockerCommand(["rm", "-f", containerName], { timeoutMs: 1e4 });
2913
+ this.activeContainers.delete(containerName);
2914
+ }
2915
+ async startBrowser(sessionKey) {
2916
+ const context = await this.getSandboxContext(sessionKey);
2917
+ if (!context) {
2918
+ return null;
2919
+ }
2920
+ const config = this.getConfig();
2921
+ if (!config.browser.enabled) {
2922
+ return null;
2923
+ }
2924
+ if (context.browser) {
2925
+ return context.browser;
2926
+ }
2927
+ const browserContainerName = `${config.browser.containerPrefix}-${context.containerName.slice(-12)}`;
2928
+ const browserArgs = [
2929
+ "run",
2930
+ "-d",
2931
+ "--name",
2932
+ browserContainerName,
2933
+ "-p",
2934
+ `${config.browser.cdpPort}:9222`
2935
+ ];
2936
+ if (config.browser.enableNoVnc) {
2937
+ browserArgs.push("-p", `${config.browser.noVncPort}:6080`);
2938
+ }
2939
+ browserArgs.push(config.browser.image);
2940
+ const result = await this.executeDockerCommand(browserArgs);
2941
+ if (!result.success) {
2942
+ this.runtime.logger.error({
2943
+ sessionKey,
2944
+ error: result.stderr
2945
+ }, "Failed to start browser container");
2946
+ return null;
2947
+ }
2948
+ const browserContext = {
2949
+ bridgeUrl: `http://localhost:${config.browser.cdpPort}`,
2950
+ ...config.browser.enableNoVnc ? { noVncUrl: `http://localhost:${config.browser.noVncPort}` } : {},
2951
+ containerName: browserContainerName
2952
+ };
2953
+ context.browser = browserContext;
2954
+ this.emitSandboxEvent("SANDBOX_BROWSER_STARTED", {
2955
+ sessionKey,
2956
+ ...context.roomId !== undefined ? { roomId: context.roomId } : {},
2957
+ containerName: browserContainerName
2958
+ });
2959
+ return browserContext;
2960
+ }
2961
+ async stopBrowser(sessionKey) {
2962
+ const context = this.contexts.get(sessionKey);
2963
+ if (!context?.browser) {
2964
+ return;
2965
+ }
2966
+ await this.stopContainer(context.browser.containerName);
2967
+ this.emitSandboxEvent("SANDBOX_BROWSER_STOPPED", {
2968
+ sessionKey,
2969
+ ...context.roomId !== undefined ? { roomId: context.roomId } : {},
2970
+ containerName: context.browser.containerName
2971
+ });
2972
+ delete context.browser;
2973
+ }
2974
+ startSweeper() {
2975
+ if (this.sweeper) {
2976
+ return;
2977
+ }
2978
+ const config = this.getConfig();
2979
+ const intervalMs = Math.max(config.prune.idleHours * 60 * 60 * 1000 / 4, 60000);
2980
+ this.sweeper = setInterval(() => {
2981
+ this.sweepIdleSandboxes();
2982
+ }, intervalMs);
2983
+ this.sweeper.unref?.();
2984
+ }
2985
+ sweepIdleSandboxes() {
2986
+ const config = this.getConfig();
2987
+ const idleMs = config.prune.idleHours * 60 * 60 * 1000;
2988
+ const maxAgeMs = config.prune.maxAgeDays * 24 * 60 * 60 * 1000;
2989
+ const now2 = Date.now();
2990
+ for (const [sessionKey, context] of this.contexts.entries()) {
2991
+ const idle = now2 - context.lastAccessedAt > idleMs;
2992
+ const tooOld = now2 - context.createdAt > maxAgeMs;
2993
+ if (idle || tooOld) {
2994
+ this.destroySandbox(sessionKey).catch((err) => {
2995
+ this.runtime.logger.error({
2996
+ sessionKey,
2997
+ error: err instanceof Error ? err.message : String(err)
2998
+ }, "Failed to destroy idle sandbox");
2999
+ });
3000
+ }
3001
+ }
3002
+ }
3003
+ on(event, handler) {
3004
+ this.emitter.on(event, handler);
3005
+ }
3006
+ off(event, handler) {
3007
+ this.emitter.off(event, handler);
3008
+ }
3009
+ emitSandboxEvent(type, payload) {
3010
+ this.emitter.emit(type, payload);
3011
+ this.emitter.emit("sandbox", { type, ...payload });
3012
+ }
3013
+ async stop() {
3014
+ if (this.sweeper) {
3015
+ clearInterval(this.sweeper);
3016
+ this.sweeper = null;
3017
+ }
3018
+ for (const sessionKey of this.contexts.keys()) {
3019
+ await this.destroySandbox(sessionKey);
3020
+ }
3021
+ this.emitter.removeAllListeners();
3022
+ }
3023
+ }
3024
+
3025
+ // src/services/subagent-service.ts
3026
+ import crypto3 from "node:crypto";
3027
+ import { EventEmitter as EventEmitter4 } from "node:events";
3028
+ import {
3029
+ ChannelType,
3030
+ EventType as EventType2,
3031
+ Service as Service4
3032
+ } from "@elizaos/core";
3033
+ class SubagentService extends Service4 {
3034
+ static serviceType = "SUBAGENT";
3035
+ capabilityDescription = "Manages subagent spawning, lifecycle, and communication";
3036
+ emitter = new EventEmitter4;
3037
+ subagentRuns = new Map;
3038
+ activeRuns = new Map;
3039
+ sweeper = null;
3040
+ initialized = false;
3041
+ static async start(runtime) {
3042
+ const service = new SubagentService(runtime);
3043
+ await service.initialize();
3044
+ return service;
3045
+ }
3046
+ async initialize() {
3047
+ if (this.initialized) {
3048
+ return;
3049
+ }
3050
+ this.initialized = true;
3051
+ this.runtime.registerEvent(EventType2.RUN_ENDED, async (payload) => {
3052
+ await this.handleRunEnded(payload);
3053
+ });
3054
+ this.runtime.registerEvent(EventType2.RUN_TIMEOUT, async (payload) => {
3055
+ await this.handleRunTimeout(payload);
3056
+ });
3057
+ this.startSweeper();
3058
+ }
3059
+ getConfig() {
3060
+ const settings = this.runtime.character?.settings;
3061
+ const subagents = settings?.subagents ?? {};
3062
+ return {
3063
+ enabled: subagents.enabled !== false,
3064
+ ...subagents.model !== undefined ? { model: subagents.model } : {},
3065
+ ...subagents.thinking !== undefined ? { thinking: subagents.thinking } : {},
3066
+ timeoutSeconds: subagents.timeoutSeconds ?? 300,
3067
+ allowAgents: subagents.allowAgents ?? [],
3068
+ archiveAfterMinutes: subagents.archiveAfterMinutes ?? 60
3069
+ };
3070
+ }
3071
+ getAgentToAgentPolicy() {
3072
+ const settings = this.runtime.character?.settings;
3073
+ const a2aConfig = settings?.agentToAgent ?? {};
3074
+ const enabled = a2aConfig.enabled === true;
3075
+ const allowRules = (a2aConfig.allow ?? []).map((rule) => ({
3076
+ source: rule.source ?? "*",
3077
+ target: rule.target ?? "*"
3078
+ }));
3079
+ return {
3080
+ enabled,
3081
+ allowRules,
3082
+ isAllowed: (sourceAgentId, targetAgentId) => {
3083
+ if (!enabled) {
3084
+ return false;
3085
+ }
3086
+ if (sourceAgentId === targetAgentId) {
3087
+ return true;
3088
+ }
3089
+ const sourceNorm = normalizeAgentId2(sourceAgentId);
3090
+ const targetNorm = normalizeAgentId2(targetAgentId);
3091
+ for (const rule of allowRules) {
3092
+ const sourceMatch = rule.source === "*" || normalizeAgentId2(rule.source) === sourceNorm;
3093
+ const targetMatch = rule.target === "*" || normalizeAgentId2(rule.target) === targetNorm;
3094
+ if (sourceMatch && targetMatch) {
3095
+ return true;
3096
+ }
3097
+ }
3098
+ return false;
3099
+ }
3100
+ };
3101
+ }
3102
+ async spawnSubagent(params, requesterContext) {
3103
+ const config = this.getConfig();
3104
+ if (!config.enabled) {
3105
+ return {
3106
+ status: "forbidden",
3107
+ error: "Subagent spawning is disabled"
3108
+ };
3109
+ }
3110
+ if (requesterContext.sessionKey && isSubagentSessionKey2(requesterContext.sessionKey)) {
3111
+ return {
3112
+ status: "forbidden",
3113
+ error: "sessions_spawn is not allowed from sub-agent sessions"
3114
+ };
3115
+ }
3116
+ const requesterAgentId = requesterContext.sessionKey ? extractAgentIdFromSessionKey(requesterContext.sessionKey) : this.runtime.character?.name ?? "unknown";
3117
+ const targetAgentId = params.agentId ? normalizeAgentId2(params.agentId) : requesterAgentId;
3118
+ if (targetAgentId !== requesterAgentId) {
3119
+ const allowAgents = config.allowAgents ?? [];
3120
+ const allowAny = allowAgents.some((v) => v.trim() === "*");
3121
+ const allowSet = new Set(allowAgents.filter((v) => v.trim() && v.trim() !== "*").map((v) => normalizeAgentId2(v)));
3122
+ if (!allowAny && !allowSet.has(targetAgentId)) {
3123
+ return {
3124
+ status: "forbidden",
3125
+ error: `agentId "${targetAgentId}" is not allowed for subagent spawning`
3126
+ };
3127
+ }
3128
+ }
3129
+ const childSessionKey = createSubagentSessionKey(targetAgentId);
3130
+ const runId = crypto3.randomUUID();
3131
+ const childRoomId = sessionKeyToRoomId(childSessionKey, targetAgentId);
3132
+ const roomMetadata = {
3133
+ isSubagent: true,
3134
+ sessionKey: childSessionKey,
3135
+ task: params.task,
3136
+ spawnedAt: Date.now(),
3137
+ cleanup: params.cleanup ?? "keep"
3138
+ };
3139
+ if (requesterContext.roomId)
3140
+ roomMetadata.parentRoomId = requesterContext.roomId;
3141
+ if (requesterContext.sessionKey)
3142
+ roomMetadata.parentSessionKey = requesterContext.sessionKey;
3143
+ if (params.label)
3144
+ roomMetadata.label = params.label;
3145
+ const childRoom = {
3146
+ id: childRoomId,
3147
+ name: params.label || `Subagent: ${params.task.slice(0, 50)}`,
3148
+ source: "subagent",
3149
+ type: ChannelType.SELF,
3150
+ channelId: childSessionKey,
3151
+ agentId: this.runtime.agentId,
3152
+ worldId: this.runtime.agentId,
3153
+ metadata: roomMetadata
3154
+ };
3155
+ await this.runtime.ensureRoomExists(childRoom);
3156
+ const now2 = Date.now();
3157
+ const archiveAfterMs = config.archiveAfterMinutes ? config.archiveAfterMinutes * 60000 : undefined;
3158
+ const record = {
3159
+ runId,
3160
+ childSessionKey,
3161
+ requesterSessionKey: requesterContext.sessionKey ?? "unknown",
3162
+ requesterDisplayKey: requesterContext.sessionKey ?? "main",
3163
+ task: params.task,
3164
+ cleanup: params.cleanup ?? "keep",
3165
+ createdAt: now2,
3166
+ startedAt: now2,
3167
+ cleanupHandled: false,
3168
+ roomId: childRoomId,
3169
+ worldId: this.runtime.agentId
3170
+ };
3171
+ const normalizedOrigin = normalizeDeliveryContext(requesterContext.origin);
3172
+ if (normalizedOrigin)
3173
+ record.requesterOrigin = normalizedOrigin;
3174
+ if (params.label)
3175
+ record.label = params.label;
3176
+ if (archiveAfterMs)
3177
+ record.archiveAtMs = now2 + archiveAfterMs;
3178
+ this.subagentRuns.set(runId, record);
3179
+ const spawnPayload = {
3180
+ runId,
3181
+ childSessionKey,
3182
+ childRoomId,
3183
+ task: params.task
3184
+ };
3185
+ if (requesterContext.sessionKey)
3186
+ spawnPayload.requesterSessionKey = requesterContext.sessionKey;
3187
+ if (requesterContext.roomId)
3188
+ spawnPayload.requesterRoomId = requesterContext.roomId;
3189
+ if (params.label)
3190
+ spawnPayload.label = params.label;
3191
+ this.emitSubagentEvent("SUBAGENT_SPAWN_REQUESTED", spawnPayload);
3192
+ const systemPromptParams = {
3193
+ childSessionKey,
3194
+ task: params.task
3195
+ };
3196
+ if (requesterContext.sessionKey)
3197
+ systemPromptParams.requesterSessionKey = requesterContext.sessionKey;
3198
+ if (requesterContext.origin)
3199
+ systemPromptParams.requesterOrigin = requesterContext.origin;
3200
+ if (params.label)
3201
+ systemPromptParams.label = params.label;
3202
+ const systemPrompt = this.buildSubagentSystemPrompt(systemPromptParams);
3203
+ const taskMetadata = {
3204
+ isSubagentTask: true,
3205
+ runId,
3206
+ systemPromptOverride: systemPrompt
3207
+ };
3208
+ const modelOverride = params.model || config.model;
3209
+ const thinkingOverride = params.thinking || config.thinking;
3210
+ if (modelOverride)
3211
+ taskMetadata.modelOverride = modelOverride;
3212
+ if (thinkingOverride)
3213
+ taskMetadata.thinkingOverride = thinkingOverride;
3214
+ const initialMessage = {
3215
+ id: hashToUUID(`${runId}-initial`),
3216
+ entityId: this.runtime.agentId,
3217
+ agentId: this.runtime.agentId,
3218
+ roomId: childRoomId,
3219
+ content: {
3220
+ text: params.task,
3221
+ type: "text",
3222
+ metadata: taskMetadata
3223
+ }
3224
+ };
3225
+ this.executeSubagentRun(runId, initialMessage, params.runTimeoutSeconds).catch((error) => {
3226
+ this.runtime.logger.error(`Subagent execution error [runId=${runId}]: ${error}`);
3227
+ this.handleSubagentError(runId, error);
3228
+ });
3229
+ return {
3230
+ status: "accepted",
3231
+ childSessionKey,
3232
+ childRoomId,
3233
+ runId,
3234
+ modelApplied: !!(params.model || config.model)
3235
+ };
3236
+ }
3237
+ async executeSubagentRun(runId, initialMessage, timeoutSeconds) {
3238
+ const config = this.getConfig();
3239
+ const timeout = (timeoutSeconds ?? config.timeoutSeconds ?? 300) * 1000;
3240
+ const controller = new AbortController;
3241
+ this.activeRuns.set(runId, controller);
3242
+ const timeoutId = timeout > 0 ? setTimeout(() => {
3243
+ controller.abort();
3244
+ this.handleSubagentTimeout(runId);
3245
+ }, timeout) : null;
3246
+ try {
3247
+ await this.runtime.emitEvent(EventType2.MESSAGE_RECEIVED, {
3248
+ runtime: this.runtime,
3249
+ message: initialMessage,
3250
+ source: "subagent"
3251
+ });
3252
+ await this.waitForCompletion(runId, timeout, controller.signal);
3253
+ } finally {
3254
+ if (timeoutId) {
3255
+ clearTimeout(timeoutId);
3256
+ }
3257
+ this.activeRuns.delete(runId);
3258
+ }
3259
+ }
3260
+ async waitForCompletion(runId, timeoutMs, signal) {
3261
+ const startTime = Date.now();
3262
+ const pollInterval = 500;
3263
+ while (!signal.aborted) {
3264
+ const record = this.subagentRuns.get(runId);
3265
+ if (!record) {
3266
+ return;
3267
+ }
3268
+ if (record.endedAt) {
3269
+ return;
3270
+ }
3271
+ if (Date.now() - startTime > timeoutMs + 5000) {
3272
+ return;
3273
+ }
3274
+ await new Promise((resolve) => setTimeout(resolve, pollInterval));
3275
+ }
3276
+ }
3277
+ handleSubagentTimeout(runId) {
3278
+ const record = this.subagentRuns.get(runId);
3279
+ if (!record || record.endedAt) {
3280
+ return;
3281
+ }
3282
+ record.endedAt = Date.now();
3283
+ record.outcome = { status: "timeout" };
3284
+ const timeoutPayload = {
3285
+ runId,
3286
+ childSessionKey: record.childSessionKey,
3287
+ task: record.task,
3288
+ status: "timeout"
3289
+ };
3290
+ if (record.roomId)
3291
+ timeoutPayload.childRoomId = record.roomId;
3292
+ this.emitSubagentEvent("SUBAGENT_RUN_TIMEOUT", timeoutPayload);
3293
+ this.announceSubagentResult(runId).catch((err) => {
3294
+ this.runtime.logger.error(`Failed to announce timeout [runId=${runId}]: ${err}`);
3295
+ });
3296
+ }
3297
+ handleSubagentError(runId, error) {
3298
+ const record = this.subagentRuns.get(runId);
3299
+ if (!record) {
3300
+ return;
3301
+ }
3302
+ record.endedAt = Date.now();
3303
+ record.outcome = {
3304
+ status: "error",
3305
+ error: error instanceof Error ? error.message : String(error)
3306
+ };
3307
+ const errorPayload = {
3308
+ runId,
3309
+ childSessionKey: record.childSessionKey,
3310
+ task: record.task,
3311
+ status: "error"
3312
+ };
3313
+ if (record.outcome.error)
3314
+ errorPayload.error = record.outcome.error;
3315
+ if (record.roomId)
3316
+ errorPayload.childRoomId = record.roomId;
3317
+ this.emitSubagentEvent("SUBAGENT_RUN_FAILED", errorPayload);
3318
+ this.announceSubagentResult(runId).catch((err) => {
3319
+ this.runtime.logger.error(`Failed to announce error [runId=${runId}]: ${err}`);
3320
+ });
3321
+ }
3322
+ async handleRunEnded(payload) {
3323
+ const p = payload;
3324
+ if (!p.roomId) {
3325
+ return;
3326
+ }
3327
+ for (const [runId, record] of this.subagentRuns.entries()) {
3328
+ if (record.roomId === p.roomId && !record.endedAt) {
3329
+ record.endedAt = Date.now();
3330
+ record.outcome = { status: "ok" };
3331
+ const completedPayload = {
3332
+ runId,
3333
+ childSessionKey: record.childSessionKey,
3334
+ childRoomId: record.roomId,
3335
+ task: record.task,
3336
+ status: "completed",
3337
+ endedAt: record.endedAt,
3338
+ durationMs: record.endedAt - (record.startedAt ?? record.createdAt)
3339
+ };
3340
+ if (record.startedAt)
3341
+ completedPayload.startedAt = record.startedAt;
3342
+ this.emitSubagentEvent("SUBAGENT_RUN_COMPLETED", completedPayload);
3343
+ await this.announceSubagentResult(runId);
3344
+ break;
3345
+ }
3346
+ }
3347
+ }
3348
+ async handleRunTimeout(payload) {
3349
+ const p = payload;
3350
+ if (!p.roomId) {
3351
+ return;
3352
+ }
3353
+ for (const [runId, record] of this.subagentRuns.entries()) {
3354
+ if (record.roomId === p.roomId && !record.endedAt) {
3355
+ this.handleSubagentTimeout(runId);
3356
+ break;
3357
+ }
3358
+ }
3359
+ }
3360
+ async announceSubagentResult(runId) {
3361
+ const record = this.subagentRuns.get(runId);
3362
+ if (!record) {
3363
+ return false;
3364
+ }
3365
+ if (record.cleanupCompletedAt || record.cleanupHandled) {
3366
+ return false;
3367
+ }
3368
+ record.cleanupHandled = true;
3369
+ let reply;
3370
+ if (record.roomId) {
3371
+ const memories = await this.runtime.getMemories({
3372
+ tableName: "messages",
3373
+ roomId: record.roomId,
3374
+ count: 10
3375
+ });
3376
+ const lastAssistant = memories.filter((m) => m.entityId === this.runtime.agentId).sort((a, b) => (b.createdAt ?? 0) - (a.createdAt ?? 0))[0];
3377
+ reply = lastAssistant?.content?.text;
3378
+ }
3379
+ const durationMs = record.endedAt ? record.endedAt - (record.startedAt ?? record.createdAt) : undefined;
3380
+ const statsLine = `Runtime: ${formatDurationShort(durationMs) ?? "n/a"} • Session: ${record.childSessionKey}`;
3381
+ const outcome = record.outcome ?? { status: "unknown" };
3382
+ const statusLabel = outcome.status === "ok" ? "completed successfully" : outcome.status === "timeout" ? "timed out" : outcome.status === "error" ? `failed: ${outcome.error || "unknown error"}` : "finished with unknown status";
3383
+ const taskLabel = record.label || record.task || "background task";
3384
+ const triggerMessage = [
3385
+ `A background task "${taskLabel}" just ${statusLabel}.`,
3386
+ "",
3387
+ "Findings:",
3388
+ reply || "(no output)",
3389
+ "",
3390
+ statsLine,
3391
+ "",
3392
+ "Summarize this naturally for the user. Keep it brief (1-2 sentences).",
3393
+ "Do not mention technical details like tokens, stats, or that this was a background task.",
3394
+ "You can respond with NO_REPLY if no announcement is needed."
3395
+ ].join(`
3396
+ `);
3397
+ if (record.requesterSessionKey && record.requesterSessionKey !== "unknown") {
3398
+ const requesterRoomId = sessionKeyToRoomId(record.requesterSessionKey, extractAgentIdFromSessionKey(record.requesterSessionKey));
3399
+ const metadata = {
3400
+ isSubagentAnnouncement: true,
3401
+ subagentRunId: runId
3402
+ };
3403
+ if (record.requesterOrigin) {
3404
+ metadata.deliveryContext = record.requesterOrigin;
3405
+ }
3406
+ const announceMessage = {
3407
+ id: hashToUUID(`${runId}-announce`),
3408
+ entityId: this.runtime.agentId,
3409
+ agentId: this.runtime.agentId,
3410
+ roomId: requesterRoomId,
3411
+ content: {
3412
+ text: triggerMessage,
3413
+ type: "text",
3414
+ metadata
3415
+ }
3416
+ };
3417
+ await this.runtime.emitEvent(EventType2.MESSAGE_RECEIVED, {
3418
+ runtime: this.runtime,
3419
+ message: announceMessage,
3420
+ source: "subagent_announce"
3421
+ });
3422
+ }
3423
+ const announcePayload = {
3424
+ runId,
3425
+ childSessionKey: record.childSessionKey,
3426
+ task: record.task,
3427
+ outcome
3428
+ };
3429
+ if (record.requesterSessionKey)
3430
+ announcePayload.requesterSessionKey = record.requesterSessionKey;
3431
+ this.emitSubagentEvent("SUBAGENT_ANNOUNCE_SENT", announcePayload);
3432
+ record.cleanupCompletedAt = Date.now();
3433
+ if (record.cleanup === "delete") {
3434
+ this.subagentRuns.delete(runId);
3435
+ }
3436
+ return true;
3437
+ }
3438
+ buildSubagentSystemPrompt(params) {
3439
+ const taskText = typeof params.task === "string" && params.task.trim() ? params.task.replace(/\s+/g, " ").trim() : "{{TASK_DESCRIPTION}}";
3440
+ const lines = [
3441
+ "# Subagent Context",
3442
+ "",
3443
+ "You are a **subagent** spawned by the main agent for a specific task.",
3444
+ "",
3445
+ "## Your Role",
3446
+ `- You were created to handle: ${taskText}`,
3447
+ "- Complete this task. That's your entire purpose.",
3448
+ "- You are NOT the main agent. Don't try to be.",
3449
+ "",
3450
+ "## Rules",
3451
+ "1. **Stay focused** - Do your assigned task, nothing else",
3452
+ "2. **Complete the task** - Your final message will be automatically reported to the main agent",
3453
+ "3. **Don't initiate** - No heartbeats, no proactive actions, no side quests",
3454
+ "4. **Be ephemeral** - You may be terminated after task completion. That's fine.",
3455
+ "",
3456
+ "## Output Format",
3457
+ "When complete, your final response should include:",
3458
+ "- What you accomplished or found",
3459
+ "- Any relevant details the main agent should know",
3460
+ "- Keep it concise but informative",
3461
+ "",
3462
+ "## What You DON'T Do",
3463
+ "- NO user conversations (that's main agent's job)",
3464
+ "- NO external messages unless explicitly tasked with a specific recipient",
3465
+ "- NO cron jobs or persistent state",
3466
+ "- NO pretending to be the main agent",
3467
+ "",
3468
+ "## Session Context",
3469
+ params.label ? `- Label: ${params.label}` : undefined,
3470
+ params.requesterSessionKey ? `- Requester session: ${params.requesterSessionKey}` : undefined,
3471
+ params.requesterOrigin?.channel ? `- Requester channel: ${params.requesterOrigin.channel}` : undefined,
3472
+ `- Your session: ${params.childSessionKey}`,
3473
+ ""
3474
+ ].filter((line) => line !== undefined);
3475
+ return lines.join(`
3476
+ `);
3477
+ }
3478
+ async sendToAgent(params, requesterContext) {
3479
+ const runId = crypto3.randomUUID();
3480
+ const policy = this.getAgentToAgentPolicy();
3481
+ let targetSessionKey = params.sessionKey;
3482
+ if (!targetSessionKey && params.label) {
3483
+ const matchingRun = this.findSubagentRunByLabel(params.label, params.agentId);
3484
+ if (matchingRun) {
3485
+ targetSessionKey = matchingRun.childSessionKey;
3486
+ } else {
3487
+ return {
3488
+ status: "error",
3489
+ runId,
3490
+ error: `No subagent found with label "${params.label}"`
3491
+ };
3492
+ }
3493
+ }
3494
+ if (!targetSessionKey) {
3495
+ return {
3496
+ status: "error",
3497
+ runId,
3498
+ error: "Either sessionKey or label is required"
3499
+ };
3500
+ }
3501
+ const requesterAgentId = requesterContext.sessionKey ? extractAgentIdFromSessionKey(requesterContext.sessionKey) : this.runtime.character?.name ?? "unknown";
3502
+ const targetAgentId = extractAgentIdFromSessionKey(targetSessionKey);
3503
+ if (requesterAgentId !== targetAgentId) {
3504
+ if (!policy.enabled) {
3505
+ return {
3506
+ status: "forbidden",
3507
+ runId,
3508
+ error: "Agent-to-agent messaging is disabled. Set settings.agentToAgent.enabled=true to allow cross-agent sends."
3509
+ };
3510
+ }
3511
+ if (!policy.isAllowed(requesterAgentId, targetAgentId)) {
3512
+ return {
3513
+ status: "forbidden",
3514
+ runId,
3515
+ error: "Agent-to-agent messaging denied by policy."
3516
+ };
3517
+ }
3518
+ }
3519
+ const targetRoomId = sessionKeyToRoomId(targetSessionKey, targetAgentId);
3520
+ const a2aContextParams = {
3521
+ targetSessionKey
3522
+ };
3523
+ if (requesterContext.sessionKey)
3524
+ a2aContextParams.requesterSessionKey = requesterContext.sessionKey;
3525
+ const contextMessage = this.buildAgentToAgentContext(a2aContextParams);
3526
+ const a2aMetadata = {
3527
+ isAgentToAgent: true,
3528
+ runId,
3529
+ systemPromptOverride: contextMessage
3530
+ };
3531
+ if (requesterContext.sessionKey)
3532
+ a2aMetadata.senderSessionKey = requesterContext.sessionKey;
3533
+ if (requesterContext.roomId)
3534
+ a2aMetadata.senderRoomId = requesterContext.roomId;
3535
+ const message = {
3536
+ id: hashToUUID(`${runId}-a2a`),
3537
+ entityId: this.runtime.agentId,
3538
+ agentId: this.runtime.agentId,
3539
+ roomId: targetRoomId,
3540
+ content: {
3541
+ text: params.message,
3542
+ type: "text",
3543
+ metadata: a2aMetadata
3544
+ }
3545
+ };
3546
+ const timeoutMs = (params.timeoutSeconds ?? 30) * 1000;
3547
+ if (params.timeoutSeconds === 0) {
3548
+ this.runtime.emitEvent(EventType2.MESSAGE_RECEIVED, {
3549
+ runtime: this.runtime,
3550
+ message,
3551
+ source: "a2a"
3552
+ }).catch((err) => {
3553
+ this.runtime.logger.error(`A2A send error [runId=${runId}]: ${err}`);
3554
+ });
3555
+ const asyncPayload = {
3556
+ runId,
3557
+ childSessionKey: targetSessionKey,
3558
+ task: params.message
3559
+ };
3560
+ if (requesterContext.sessionKey)
3561
+ asyncPayload.requesterSessionKey = requesterContext.sessionKey;
3562
+ this.emitSubagentEvent("A2A_MESSAGE_SENT", asyncPayload);
3563
+ return {
3564
+ status: "accepted",
3565
+ runId,
3566
+ sessionKey: targetSessionKey,
3567
+ delivery: { status: "pending", mode: "async" }
3568
+ };
3569
+ }
3570
+ const sentAt = Date.now();
3571
+ await this.runtime.emitEvent(EventType2.MESSAGE_RECEIVED, {
3572
+ runtime: this.runtime,
3573
+ message,
3574
+ source: "a2a"
3575
+ });
3576
+ const pollIntervalMs = 500;
3577
+ const maxPolls = Math.ceil(timeoutMs / pollIntervalMs);
3578
+ let lastReply;
3579
+ for (let poll = 0;poll < maxPolls; poll++) {
3580
+ await new Promise((r) => setTimeout(r, pollIntervalMs));
3581
+ const memories = await this.runtime.getMemories({
3582
+ tableName: "messages",
3583
+ roomId: targetRoomId,
3584
+ count: 10
3585
+ });
3586
+ const newReplies = memories.filter((m) => m.entityId === this.runtime.agentId && m.id !== message.id && m.createdAt && m.createdAt > sentAt);
3587
+ if (newReplies.length > 0) {
3588
+ lastReply = newReplies.sort((a, b) => (b.createdAt ?? 0) - (a.createdAt ?? 0))[0];
3589
+ break;
3590
+ }
3591
+ }
3592
+ const syncPayload = {
3593
+ runId,
3594
+ childSessionKey: targetSessionKey,
3595
+ task: params.message
3596
+ };
3597
+ if (requesterContext.sessionKey)
3598
+ syncPayload.requesterSessionKey = requesterContext.sessionKey;
3599
+ this.emitSubagentEvent("A2A_MESSAGE_SENT", syncPayload);
3600
+ if (!lastReply) {
3601
+ return {
3602
+ status: "timeout",
3603
+ runId,
3604
+ sessionKey: targetSessionKey,
3605
+ error: `No response received within ${params.timeoutSeconds ?? 30} seconds`,
3606
+ delivery: { status: "timeout", mode: "sync" }
3607
+ };
3608
+ }
3609
+ const result = {
3610
+ status: "ok",
3611
+ runId,
3612
+ sessionKey: targetSessionKey,
3613
+ delivery: { status: "delivered", mode: "sync" }
3614
+ };
3615
+ if (lastReply.content?.text)
3616
+ result.reply = lastReply.content.text;
3617
+ return result;
3618
+ }
3619
+ buildAgentToAgentContext(params) {
3620
+ return [
3621
+ "# Agent-to-Agent Message Context",
3622
+ "",
3623
+ "This message was sent by another agent session.",
3624
+ params.requesterSessionKey ? `- Sender: ${params.requesterSessionKey}` : undefined,
3625
+ `- Target: ${params.targetSessionKey}`,
3626
+ "",
3627
+ "Process this message and respond appropriately."
3628
+ ].filter((l) => l !== undefined).join(`
3629
+ `);
3630
+ }
3631
+ getSubagentRun(runId) {
3632
+ return this.subagentRuns.get(runId);
3633
+ }
3634
+ findSubagentRunByLabel(label, agentId) {
3635
+ const normalizedLabel = label.toLowerCase().trim();
3636
+ const normalizedAgentId = agentId ? normalizeAgentId2(agentId) : undefined;
3637
+ for (const run of this.subagentRuns.values()) {
3638
+ const runLabel = run.label?.toLowerCase().trim();
3639
+ if (runLabel !== normalizedLabel) {
3640
+ continue;
3641
+ }
3642
+ if (normalizedAgentId) {
3643
+ const runAgentId = extractAgentIdFromSessionKey(run.childSessionKey);
3644
+ if (normalizeAgentId2(runAgentId) !== normalizedAgentId) {
3645
+ continue;
3646
+ }
3647
+ }
3648
+ if (!run.endedAt) {
3649
+ return run;
3650
+ }
3651
+ }
3652
+ const completedRuns = [...this.subagentRuns.values()].filter((run) => {
3653
+ const runLabel = run.label?.toLowerCase().trim();
3654
+ if (runLabel !== normalizedLabel) {
3655
+ return false;
3656
+ }
3657
+ if (normalizedAgentId) {
3658
+ const runAgentId = extractAgentIdFromSessionKey(run.childSessionKey);
3659
+ if (normalizeAgentId2(runAgentId) !== normalizedAgentId) {
3660
+ return false;
3661
+ }
3662
+ }
3663
+ return true;
3664
+ }).sort((a, b) => (b.createdAt ?? 0) - (a.createdAt ?? 0));
3665
+ return completedRuns[0];
3666
+ }
3667
+ listSubagentRuns(requesterSessionKey) {
3668
+ const runs = [...this.subagentRuns.values()];
3669
+ if (!requesterSessionKey) {
3670
+ return runs;
3671
+ }
3672
+ return runs.filter((r) => r.requesterSessionKey === requesterSessionKey);
3673
+ }
3674
+ cancelSubagentRun(runId) {
3675
+ const controller = this.activeRuns.get(runId);
3676
+ if (controller) {
3677
+ controller.abort();
3678
+ return true;
3679
+ }
3680
+ return false;
3681
+ }
3682
+ on(event, handler) {
3683
+ this.emitter.on(event, handler);
3684
+ }
3685
+ off(event, handler) {
3686
+ this.emitter.off(event, handler);
3687
+ }
3688
+ emitSubagentEvent(type, payload) {
3689
+ this.emitter.emit(type, payload);
3690
+ this.emitter.emit("task", { type, ...payload });
3691
+ }
3692
+ startSweeper() {
3693
+ if (this.sweeper) {
3694
+ return;
3695
+ }
3696
+ this.sweeper = setInterval(() => {
3697
+ this.sweepOldRuns();
3698
+ }, 60000);
3699
+ this.sweeper.unref?.();
3700
+ }
3701
+ sweepOldRuns() {
3702
+ const now2 = Date.now();
3703
+ for (const [runId, record] of this.subagentRuns.entries()) {
3704
+ if (record.archiveAtMs && record.archiveAtMs <= now2) {
3705
+ this.subagentRuns.delete(runId);
3706
+ }
3707
+ }
3708
+ }
3709
+ async stop() {
3710
+ if (this.sweeper) {
3711
+ clearInterval(this.sweeper);
3712
+ this.sweeper = null;
3713
+ }
3714
+ for (const controller of this.activeRuns.values()) {
3715
+ controller.abort();
3716
+ }
3717
+ this.activeRuns.clear();
3718
+ this.emitter.removeAllListeners();
3719
+ }
3720
+ }
3721
+ // src/types/messaging.ts
3722
+ var MessagingEventType = {
3723
+ SEND_REQUESTED: "MESSAGING_SEND_REQUESTED",
3724
+ SENT: "MESSAGING_SENT",
3725
+ SEND_FAILED: "MESSAGING_SEND_FAILED",
3726
+ DELIVERED: "MESSAGING_DELIVERED",
3727
+ READ: "MESSAGING_READ"
3728
+ };
3729
+ // src/types/sandbox.ts
3730
+ var SandboxEventType = {
3731
+ CREATED: "SANDBOX_CREATED",
3732
+ DESTROYED: "SANDBOX_DESTROYED",
3733
+ COMMAND_STARTED: "SANDBOX_COMMAND_STARTED",
3734
+ COMMAND_COMPLETED: "SANDBOX_COMMAND_COMPLETED",
3735
+ COMMAND_FAILED: "SANDBOX_COMMAND_FAILED",
3736
+ BROWSER_STARTED: "SANDBOX_BROWSER_STARTED",
3737
+ BROWSER_STOPPED: "SANDBOX_BROWSER_STOPPED"
3738
+ };
3739
+ // src/types/subagent.ts
3740
+ var SubagentEventType = {
3741
+ SPAWN_REQUESTED: "SUBAGENT_SPAWN_REQUESTED",
3742
+ RUN_STARTED: "SUBAGENT_RUN_STARTED",
3743
+ RUN_COMPLETED: "SUBAGENT_RUN_COMPLETED",
3744
+ RUN_FAILED: "SUBAGENT_RUN_FAILED",
3745
+ RUN_TIMEOUT: "SUBAGENT_RUN_TIMEOUT",
3746
+ ANNOUNCE_SENT: "SUBAGENT_ANNOUNCE_SENT",
3747
+ A2A_MESSAGE_SENT: "A2A_MESSAGE_SENT",
3748
+ A2A_MESSAGE_RECEIVED: "A2A_MESSAGE_RECEIVED"
3749
+ };
785
3750
  // index.ts
3751
+ var sessionProviders = getSessionProviders2();
786
3752
  var agentOrchestratorPlugin = {
787
3753
  name: "agent-orchestrator",
788
- description: "Orchestrate tasks across one or more agent providers (no filesystem tools)",
789
- services: [AgentOrchestratorService],
790
- providers: [taskContextProvider],
3754
+ description: "Orchestrate tasks across agent providers with subagent spawning, agent-to-agent communication, sandboxed execution, and cross-platform messaging",
3755
+ services: [AgentOrchestratorService, SubagentService, SandboxService, MessagingService],
3756
+ providers: [taskContextProvider, orchestratorConfigProvider, ...sessionProviders],
791
3757
  actions: [
792
3758
  createTaskAction,
793
3759
  listTasksAction,
@@ -795,23 +3761,103 @@ var agentOrchestratorPlugin = {
795
3761
  searchTasksAction,
796
3762
  pauseTaskAction,
797
3763
  resumeTaskAction,
798
- cancelTaskAction
3764
+ cancelTaskAction,
3765
+ spawnSubagentAction,
3766
+ sendToSessionAction,
3767
+ listSubagentsAction,
3768
+ cancelSubagentAction,
3769
+ getSubagentStatusAction,
3770
+ sendCrossPlatformMessageAction,
3771
+ sendToDeliveryContextAction,
3772
+ sendToRoomAction,
3773
+ sendToSessionMessageAction,
3774
+ listMessagingChannelsAction
799
3775
  ]
800
3776
  };
801
3777
  var typescript_default = agentOrchestratorPlugin;
802
3778
  export {
3779
+ upsertSessionEntry,
3780
+ updateSessionStoreEntry,
3781
+ updateSessionStore,
3782
+ toAgentStoreSessionKey,
3783
+ toAgentRequestSessionKey,
803
3784
  switchTaskAction,
3785
+ spawnSubagentAction,
3786
+ sessionKeyToRoomId,
3787
+ sendToSessionMessageAction,
3788
+ sendToSessionAction,
3789
+ sendToRoomAction,
3790
+ sendToDeliveryContextAction,
3791
+ sendCrossPlatformMessageAction,
804
3792
  searchTasksAction,
3793
+ saveSessionStore,
805
3794
  resumeTaskAction,
3795
+ resolveThreadSessionKeys,
3796
+ resolveThreadParentSessionKey,
3797
+ resolveStorePath,
3798
+ resolveStateDir,
3799
+ resolveSessionTranscriptPath,
3800
+ resolveDefaultSessionStorePath,
3801
+ resolveAgentSessionsDir,
3802
+ resolveAgentIdFromSessionKey,
806
3803
  pauseTaskAction,
3804
+ parseSessionKey,
3805
+ parseAgentSessionKey,
3806
+ orchestratorConfigProvider,
3807
+ normalizeSessionKey,
3808
+ normalizeMainKey,
3809
+ normalizeDeliveryContext,
3810
+ normalizeAgentId as normalizeCoreAgentId,
3811
+ normalizeAgentId2 as normalizeAgentId,
3812
+ normalizeAccountId,
3813
+ mergeSessionEntry,
3814
+ mergeDeliveryContext,
3815
+ loadSessionStore,
807
3816
  listTasksAction,
3817
+ listSubagentsAction,
3818
+ listSessionKeys,
3819
+ listMessagingChannelsAction,
3820
+ isValidSessionEntry,
3821
+ isSubagentSessionKey2 as isSubagentSessionKey,
3822
+ isSubagentSessionKey as isCoreSubagentSessionKey,
3823
+ isAcpSessionKey,
3824
+ hashToUUID,
3825
+ getSubagentStatusAction,
3826
+ getSessionProviders,
3827
+ getSessionEntry,
3828
+ getOrchestratorConfig,
808
3829
  getConfiguredAgentOrchestratorOptions,
3830
+ formatTokenCount,
3831
+ formatDurationShort,
3832
+ extractSessionContext,
3833
+ extractAgentIdFromSessionKey,
3834
+ deleteSessionEntry,
809
3835
  typescript_default as default,
810
3836
  createTaskAction,
3837
+ createSubagentSessionKey,
3838
+ createSessionSkillsProvider,
3839
+ createSessionProvider,
3840
+ createSessionEntry,
3841
+ createSendPolicyProvider,
811
3842
  configureAgentOrchestratorPlugin,
812
3843
  cancelTaskAction,
3844
+ cancelSubagentAction,
3845
+ buildSubagentSessionKey,
3846
+ buildSessionKey,
3847
+ buildGroupHistoryKey,
3848
+ buildAgentSessionKey,
3849
+ buildAgentPeerSessionKey,
3850
+ buildAgentMainSessionKey,
3851
+ buildAcpSessionKey,
813
3852
  agentOrchestratorPlugin,
3853
+ SubagentService,
3854
+ SubagentEventType,
3855
+ SessionStateManager,
3856
+ SandboxService,
3857
+ SandboxEventType,
3858
+ MessagingService,
3859
+ MessagingEventType,
814
3860
  AgentOrchestratorService
815
3861
  };
816
3862
 
817
- //# debugId=CC8D88226518D89764756E2164756E21
3863
+ //# debugId=4AD40853FB59A54864756E2164756E21