@elizaos/plugin-agent-orchestrator 2.0.0-alpha.3 → 2.0.0-alpha.5

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,293 +1,556 @@
1
1
  // index.ts
2
2
  import { getSessionProviders as getSessionProviders2 } from "@elizaos/core";
3
3
 
4
- // src/actions/task-management.ts
5
- function getService(runtime) {
6
- const svc = runtime.getService("CODE_TASK");
7
- if (!svc) {
8
- throw new Error("AgentOrchestratorService not available (CODE_TASK)");
4
+ // src/actions/messaging.ts
5
+ function extractMessagingParams(message, _state) {
6
+ const params = message.content?.params;
7
+ if (!params) {
8
+ return {};
9
9
  }
10
- return svc;
11
- }
12
- function extractQuery(text) {
13
- return text.toLowerCase().replace(/\b(switch|select|go|change|search|find|pause|stop|halt|resume|restart|continue|start|run|begin|cancel|delete|remove|list|show|view)\b/g, "").replace(/\b(about|for|named|called|with|to|my|your|our|this|current)\b/g, "").replace(/\b(task|tasks|the|a|an)\b/g, "").replace(/\s+/g, " ").trim();
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 };
14
34
  }
15
- var createTaskAction = {
16
- name: "CREATE_TASK",
17
- similes: ["START_TASK", "SPAWN_TASK", "NEW_TASK", "BEGIN_TASK"],
18
- description: "Create an orchestrated background task to be executed by a selected agent provider.",
19
- validate: async (runtime, message) => {
20
- const svc = runtime.getService("CODE_TASK");
21
- if (!svc) {
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) {
22
48
  return false;
23
49
  }
24
- const text = message.content.text?.toLowerCase() ?? "";
25
- const hasExplicit = text.includes("create task") || text.includes("new task") || text.includes("start a task");
26
- const hasIntent = text.includes("implement") || text.includes("build") || text.includes("create") || text.includes("develop") || text.includes("refactor") || text.includes("fix") || text.includes("add");
27
- return hasExplicit || hasIntent;
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);
28
57
  },
29
- handler: async (runtime, message, _state, options, callback) => {
30
- const svc = getService(runtime);
31
- const raw = message.content.text ?? "";
32
- const opts = options;
33
- const name = (opts?.title ?? raw.split(`
34
- `)[0] ?? "New Task").trim().slice(0, 100) || "New Task";
35
- const description = (opts?.description ?? raw).trim().slice(0, 4000) || name;
36
- const roomId = message.roomId;
37
- const task = await svc.createTask(name, description, roomId);
38
- const stepLines = Array.isArray(opts?.steps) ? opts?.steps : undefined;
39
- if (stepLines && stepLines.length > 0) {
40
- for (const s of stepLines) {
41
- const step = String(s).trim();
42
- if (!step)
43
- continue;
44
- await svc.addStep(task.id ?? "", step);
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
+ });
45
65
  }
46
- await svc.appendOutput(task.id ?? "", `Plan:
47
- ${stepLines.map((s, i) => `${i + 1}. ${s}`).join(`
48
- `)}`);
66
+ return { success: false, error: "Messaging service not available" };
49
67
  }
50
- const msg = `Created task: ${task.name}
51
- Provider: ${task.metadata.providerLabel ?? task.metadata.providerId}
52
- Starting execution…`;
53
- await callback?.({ content: { text: msg } });
54
- svc.startTaskExecution(task.id ?? "").catch((err) => {
55
- const errorMsg = err instanceof Error ? err.message : String(err);
56
- svc.appendOutput(task.id ?? "", `Execution error: ${errorMsg}`).catch(() => {});
57
- });
58
- return { success: true, text: msg, data: { taskId: task.id ?? "" } };
59
- }
60
- };
61
- var listTasksAction = {
62
- name: "LIST_TASKS",
63
- similes: ["SHOW_TASKS", "GET_TASKS", "TASKS", "VIEW_TASKS"],
64
- description: "List tasks managed by the orchestrator.",
65
- validate: async (runtime, message) => {
66
- const svc = runtime.getService("CODE_TASK");
67
- if (!svc) {
68
- return false;
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" };
69
76
  }
70
- const t = message.content.text?.toLowerCase() ?? "";
71
- return t.includes("list task") || t.includes("show task") || t === "tasks" || t.includes("my task");
72
- },
73
- handler: async (runtime, _message, _state, _options, callback) => {
74
- const svc = getService(runtime);
75
- const tasks = await svc.getRecentTasks(20);
76
- if (tasks.length === 0) {
77
- const msg2 = "No tasks.";
78
- await callback?.({ content: { text: msg2 } });
79
- return { success: true, text: msg2 };
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" };
80
84
  }
81
- const lines = ["Tasks:"];
82
- const current = svc.getCurrentTaskId();
83
- for (const t of tasks) {
84
- const marker = t.id === current ? " (current)" : "";
85
- lines.push(`- ${t.name} — ${t.metadata.status} ${t.metadata.progress}%${marker}`);
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" };
86
92
  }
87
- const msg = lines.join(`
88
- `);
89
- await callback?.({ content: { text: msg } });
90
- return { success: true, text: msg };
91
- }
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
+ ]
92
183
  };
93
- var switchTaskAction = {
94
- name: "SWITCH_TASK",
95
- similes: ["SELECT_TASK", "SET_TASK", "CHANGE_TASK", "GO_TO_TASK"],
96
- description: "Switch the current task context to a different task.",
97
- validate: async (runtime, message) => {
98
- const svc = runtime.getService("CODE_TASK");
99
- if (!svc) {
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) {
100
191
  return false;
101
192
  }
102
- const t = message.content.text?.toLowerCase() ?? "";
103
- return t.includes("switch to task") || t.includes("select task") || t.includes("task") && t.includes("switch");
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;
104
199
  },
105
200
  handler: async (runtime, message, _state, _options, callback) => {
106
- const svc = getService(runtime);
107
- const query = extractQuery(message.content.text ?? "");
108
- if (!query) {
109
- const msg2 = "Please specify which task to switch to (by name or id).";
110
- await callback?.({ content: { text: msg2 } });
111
- return { success: false, text: msg2 };
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" };
112
209
  }
113
- const matches = await svc.searchTasks(query);
114
- const chosen = matches[0];
115
- if (!chosen?.id) {
116
- const msg2 = `No task found matching: "${query}"`;
117
- await callback?.({ content: { text: msg2 } });
118
- return { success: false, text: msg2 };
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" };
119
220
  }
120
- svc.setCurrentTask(chosen.id);
121
- const msg = `Switched to task: ${chosen.name}`;
122
- await callback?.({ content: { text: msg } });
123
- return { success: true, text: msg, data: { taskId: chosen.id } };
124
- }
125
- };
126
- var searchTasksAction = {
127
- name: "SEARCH_TASKS",
128
- similes: ["FIND_TASK", "LOOKUP_TASK"],
129
- description: "Search tasks by query.",
130
- validate: async (runtime, message) => {
131
- const svc = runtime.getService("CODE_TASK");
132
- if (!svc) {
133
- return false;
134
- }
135
- const t = message.content.text?.toLowerCase() ?? "";
136
- return t.includes("search task") || t.includes("find task") || t.includes("look for task");
137
- },
138
- handler: async (runtime, message, _state, options, callback) => {
139
- const svc = getService(runtime);
140
- const opt = options;
141
- const query = (opt?.query ?? extractQuery(message.content.text ?? "")).trim();
142
- if (!query) {
143
- const msg2 = "What would you like to search for?";
144
- await callback?.({ content: { text: msg2 } });
145
- return { success: false, text: msg2 };
146
- }
147
- const matches = await svc.searchTasks(query);
148
- if (matches.length === 0) {
149
- const msg2 = `No tasks found matching: "${query}"`;
150
- await callback?.({ content: { text: msg2 } });
151
- return { success: true, text: msg2 };
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" };
152
228
  }
153
- const lines = [`Found ${matches.length} task(s) matching "${query}":`];
154
- for (const t of matches.slice(0, 10)) {
155
- lines.push(`- ${t.name} — ${t.metadata.status} ${t.metadata.progress}%`);
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
+ }
156
246
  }
157
- const msg = lines.join(`
158
- `);
159
- await callback?.({ content: { text: msg } });
160
- return { success: true, text: msg };
161
- }
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
+ ]
162
277
  };
163
- var pauseTaskAction = {
164
- name: "PAUSE_TASK",
165
- similes: ["STOP_TASK", "HALT_TASK"],
166
- description: "Pause a running task.",
167
- validate: async (runtime, message) => {
168
- const svc = runtime.getService("CODE_TASK");
169
- if (!svc) {
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) {
170
285
  return false;
171
286
  }
172
- const t = message.content.text?.toLowerCase() ?? "";
173
- return (t.includes("pause") || t.includes("stop") || t.includes("halt")) && t.includes("task");
174
- },
175
- handler: async (runtime, message, _state, _options, callback) => {
176
- const svc = getService(runtime);
177
- const query = extractQuery(message.content.text ?? "");
178
- const task = query ? (await svc.searchTasks(query))[0] : await svc.getCurrentTask();
179
- if (!task?.id) {
180
- const msg2 = "No task to pause.";
181
- await callback?.({ content: { text: msg2 } });
182
- return { success: false, text: msg2 };
183
- }
184
- await svc.pauseTask(task.id);
185
- const msg = `Paused task: ${task.name}`;
186
- await callback?.({ content: { text: msg } });
187
- return { success: true, text: msg };
188
- }
189
- };
190
- var resumeTaskAction = {
191
- name: "RESUME_TASK",
192
- similes: ["CONTINUE_TASK", "RESTART_TASK", "RUN_TASK"],
193
- description: "Resume a paused task.",
194
- validate: async (runtime, message) => {
195
- const svc = runtime.getService("CODE_TASK");
196
- if (!svc) {
287
+ const params = message.content?.params;
288
+ if (!params?.roomId) {
197
289
  return false;
198
290
  }
199
- const t = message.content.text?.toLowerCase() ?? "";
200
- return t.includes("task") && (t.includes("resume") || t.includes("restart") || t.includes("continue"));
291
+ const hasText = params.text || message.content?.text;
292
+ return !!hasText;
201
293
  },
202
294
  handler: async (runtime, message, _state, _options, callback) => {
203
- const svc = getService(runtime);
204
- const query = extractQuery(message.content.text ?? "");
205
- const task = query ? (await svc.searchTasks(query))[0] : await svc.getCurrentTask();
206
- if (!task?.id) {
207
- const msg2 = "No task to resume.";
208
- await callback?.({ content: { text: msg2 } });
209
- return { success: false, text: msg2 };
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" };
210
303
  }
211
- await svc.resumeTask(task.id);
212
- svc.startTaskExecution(task.id).catch((err) => {
213
- const errorMsg = err instanceof Error ? err.message : String(err);
214
- svc.appendOutput(task.id, `Execution error: ${errorMsg}`).catch(() => {});
215
- });
216
- const msg = `Resumed task: ${task.name}`;
217
- await callback?.({ content: { text: msg } });
218
- return { success: true, text: msg };
219
- }
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
+ ]
220
366
  };
221
- var cancelTaskAction = {
222
- name: "CANCEL_TASK",
223
- similes: ["DELETE_TASK", "REMOVE_TASK", "ABORT_TASK"],
224
- description: "Cancel a task.",
225
- validate: async (runtime, message) => {
226
- const svc = runtime.getService("CODE_TASK");
227
- if (!svc) {
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) {
228
374
  return false;
229
375
  }
230
- const t = message.content.text?.toLowerCase() ?? "";
231
- return (t.includes("cancel") || t.includes("delete") || t.includes("remove")) && t.includes("task");
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;
232
382
  },
233
383
  handler: async (runtime, message, _state, _options, callback) => {
234
- const svc = getService(runtime);
235
- const query = extractQuery(message.content.text ?? "");
236
- const task = query ? (await svc.searchTasks(query))[0] : await svc.getCurrentTask();
237
- if (!task?.id) {
238
- const msg2 = "No task to cancel.";
239
- await callback?.({ content: { text: msg2 } });
240
- return { success: false, text: msg2 };
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" };
241
392
  }
242
- await svc.cancelTask(task.id);
243
- const msg = `Cancelled task: ${task.name}`;
244
- await callback?.({ content: { text: msg } });
245
- return { success: true, text: msg };
246
- }
247
- };
248
-
249
- // src/utils/session.ts
250
- import crypto from "node:crypto";
251
- import {
252
- buildAgentMainSessionKey,
253
- buildAgentSessionKey,
254
- buildAgentPeerSessionKey,
255
- buildAcpSessionKey,
256
- buildSubagentSessionKey,
257
- parseAgentSessionKey,
258
- isAcpSessionKey,
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,
259
530
  isSubagentSessionKey,
531
+ isValidSessionEntry,
532
+ listSessionKeys,
533
+ loadSessionStore,
534
+ mergeSessionEntry,
535
+ normalizeAccountId,
260
536
  normalizeAgentId,
261
537
  normalizeMainKey,
262
- normalizeAccountId,
263
- toAgentRequestSessionKey,
264
- toAgentStoreSessionKey,
538
+ parseAgentSessionKey,
265
539
  resolveAgentIdFromSessionKey,
266
- resolveThreadParentSessionKey,
267
- resolveThreadSessionKeys,
268
- buildGroupHistoryKey,
269
- loadSessionStore,
270
- saveSessionStore,
271
- updateSessionStore,
272
- updateSessionStoreEntry,
273
- getSessionEntry,
274
- upsertSessionEntry,
275
- deleteSessionEntry,
276
- listSessionKeys,
277
- resolveStateDir,
278
540
  resolveAgentSessionsDir,
279
541
  resolveDefaultSessionStorePath,
280
542
  resolveSessionTranscriptPath,
543
+ resolveStateDir,
281
544
  resolveStorePath,
282
- createSessionProvider,
283
- createSessionSkillsProvider,
284
- createSendPolicyProvider,
285
- getSessionProviders,
286
- extractSessionContext,
545
+ resolveThreadParentSessionKey,
546
+ resolveThreadSessionKeys,
287
547
  SessionStateManager,
288
- createSessionEntry,
289
- mergeSessionEntry,
290
- isValidSessionEntry
548
+ saveSessionStore,
549
+ toAgentRequestSessionKey,
550
+ toAgentStoreSessionKey,
551
+ updateSessionStore,
552
+ updateSessionStoreEntry,
553
+ upsertSessionEntry
291
554
  } from "@elizaos/core";
292
555
  function hashToUUID(input) {
293
556
  const hash = crypto.createHash("sha256").update(input).digest("hex");
@@ -405,25 +668,37 @@ function normalizeDeliveryContext(context) {
405
668
  }
406
669
  const result = {};
407
670
  if (context.channel && typeof context.channel === "string") {
408
- result.channel = context.channel.trim() || undefined;
671
+ const trimmed = context.channel.trim();
672
+ if (trimmed)
673
+ result.channel = trimmed;
409
674
  }
410
675
  if (context.accountId && typeof context.accountId === "string") {
411
- result.accountId = context.accountId.trim() || undefined;
676
+ const trimmed = context.accountId.trim();
677
+ if (trimmed)
678
+ result.accountId = trimmed;
412
679
  }
413
680
  if (context.to && typeof context.to === "string") {
414
- result.to = context.to.trim() || undefined;
681
+ const trimmed = context.to.trim();
682
+ if (trimmed)
683
+ result.to = trimmed;
415
684
  }
416
685
  if (context.threadId !== undefined && context.threadId !== null) {
417
686
  result.threadId = context.threadId;
418
687
  }
419
688
  if (context.groupId && typeof context.groupId === "string") {
420
- result.groupId = context.groupId.trim() || undefined;
689
+ const trimmed = context.groupId.trim();
690
+ if (trimmed)
691
+ result.groupId = trimmed;
421
692
  }
422
693
  if (context.groupChannel && typeof context.groupChannel === "string") {
423
- result.groupChannel = context.groupChannel.trim() || undefined;
694
+ const trimmed = context.groupChannel.trim();
695
+ if (trimmed)
696
+ result.groupChannel = trimmed;
424
697
  }
425
698
  if (context.groupSpace && typeof context.groupSpace === "string") {
426
- result.groupSpace = context.groupSpace.trim() || undefined;
699
+ const trimmed = context.groupSpace.trim();
700
+ if (trimmed)
701
+ result.groupSpace = trimmed;
427
702
  }
428
703
  const hasValues = Object.values(result).some((v) => v !== undefined && v !== null);
429
704
  return hasValues ? result : undefined;
@@ -485,23 +760,19 @@ function getSubagentService(runtime) {
485
760
  }
486
761
  return svc;
487
762
  }
488
- function extractSessionContext2(runtime, message) {
763
+ function extractSessionContext2(_runtime, message) {
489
764
  const metadata = message.content?.metadata;
490
765
  const sessionKey = typeof metadata?.sessionKey === "string" ? metadata.sessionKey : undefined;
491
- return {
492
- sessionKey,
493
- roomId: message.roomId
494
- };
766
+ const result = {};
767
+ if (sessionKey)
768
+ result.sessionKey = sessionKey;
769
+ if (message.roomId)
770
+ result.roomId = message.roomId;
771
+ return result;
495
772
  }
496
773
  var spawnSubagentAction = {
497
774
  name: "SPAWN_SUBAGENT",
498
- similes: [
499
- "SPAWN_TASK",
500
- "BACKGROUND_TASK",
501
- "START_SUBAGENT",
502
- "SESSIONS_SPAWN",
503
- "CREATE_SUBAGENT"
504
- ],
775
+ similes: ["SPAWN_TASK", "BACKGROUND_TASK", "START_SUBAGENT", "SESSIONS_SPAWN", "CREATE_SUBAGENT"],
505
776
  description: "Spawn a background sub-agent run to execute a task asynchronously. The subagent will complete the task and announce results back.",
506
777
  validate: async (runtime, message) => {
507
778
  const svc = runtime.getService("SUBAGENT");
@@ -523,18 +794,20 @@ var spawnSubagentAction = {
523
794
  await callback?.({ content: { text: msg2 } });
524
795
  return { success: false, text: msg2 };
525
796
  }
526
- const result = await svc.spawnSubagent({
797
+ const spawnParams = {
527
798
  task,
528
- label: opts?.label,
529
- agentId: opts?.agentId,
530
- model: opts?.model,
531
- thinking: opts?.thinking,
532
799
  runTimeoutSeconds: opts?.timeoutSeconds ?? 300,
533
800
  cleanup: opts?.cleanup ?? "keep"
534
- }, {
535
- sessionKey: context.sessionKey,
536
- roomId: context.roomId
537
- });
801
+ };
802
+ if (opts?.label)
803
+ spawnParams.label = opts.label;
804
+ if (opts?.agentId)
805
+ spawnParams.agentId = opts.agentId;
806
+ if (opts?.model)
807
+ spawnParams.model = opts.model;
808
+ if (opts?.thinking)
809
+ spawnParams.thinking = opts.thinking;
810
+ const result = await svc.spawnSubagent(spawnParams, context);
538
811
  if (result.status !== "accepted") {
539
812
  const msg2 = `Failed to spawn subagent: ${result.error ?? "unknown error"}`;
540
813
  await callback?.({ content: { text: msg2 } });
@@ -582,16 +855,17 @@ var sendToSessionAction = {
582
855
  await callback?.({ content: { text: msg2 } });
583
856
  return { success: false, text: msg2 };
584
857
  }
585
- const result = await svc.sendToAgent({
586
- sessionKey: opts?.sessionKey,
587
- label: opts?.label,
588
- agentId: opts?.agentId,
858
+ const sendParams = {
589
859
  message: targetMessage,
590
860
  timeoutSeconds: opts?.timeoutSeconds ?? 30
591
- }, {
592
- sessionKey: context.sessionKey,
593
- roomId: context.roomId
594
- });
861
+ };
862
+ if (opts?.sessionKey)
863
+ sendParams.sessionKey = opts.sessionKey;
864
+ if (opts?.label)
865
+ sendParams.label = opts.label;
866
+ if (opts?.agentId)
867
+ sendParams.agentId = opts.agentId;
868
+ const result = await svc.sendToAgent(sendParams, context);
595
869
  if (result.status === "forbidden") {
596
870
  const msg2 = `Access denied: ${result.error}`;
597
871
  await callback?.({ content: { text: msg2 } });
@@ -673,7 +947,7 @@ var cancelSubagentAction = {
673
947
  const text = message.content.text?.toLowerCase() ?? "";
674
948
  return (text.includes("cancel") || text.includes("stop") || text.includes("abort")) && (text.includes("subagent") || text.includes("background"));
675
949
  },
676
- handler: async (runtime, message, _state, options, callback) => {
950
+ handler: async (runtime, _message, _state, options, callback) => {
677
951
  const svc = getSubagentService(runtime);
678
952
  const opts = options;
679
953
  const runId = opts?.runId;
@@ -705,7 +979,7 @@ var getSubagentStatusAction = {
705
979
  const text = message.content.text?.toLowerCase() ?? "";
706
980
  return text.includes("subagent status") || text.includes("task status") || text.includes("check subagent") || text.includes("subagent info");
707
981
  },
708
- handler: async (runtime, message, _state, options, callback) => {
982
+ handler: async (runtime, _message, _state, options, callback) => {
709
983
  const svc = getSubagentService(runtime);
710
984
  const opts = options;
711
985
  const runId = opts?.runId;
@@ -739,491 +1013,250 @@ var getSubagentStatusAction = {
739
1013
  }
740
1014
  };
741
1015
 
742
- // src/actions/messaging.ts
743
- function extractMessagingParams(message, state) {
744
- const params = message.content?.params;
745
- if (!params) {
746
- return {};
1016
+ // src/actions/task-management.ts
1017
+ function getService(runtime) {
1018
+ const svc = runtime.getService("CODE_TASK");
1019
+ if (!svc) {
1020
+ throw new Error("AgentOrchestratorService not available (CODE_TASK)");
747
1021
  }
748
- const target = params.target ?? {
749
- channel: params.channel,
750
- to: params.to,
751
- threadId: params.threadId,
752
- replyToMessageId: params.replyTo
753
- };
754
- const content = params.content ?? {
755
- text: params.text ?? message.content?.text
756
- };
757
- return { target, content };
1022
+ return svc;
758
1023
  }
759
- var sendCrossPlatformMessageAction = {
760
- name: "SEND_CROSS_PLATFORM_MESSAGE",
761
- similes: [
762
- "CROSS_PLATFORM_MESSAGE",
763
- "UNIFIED_SEND",
764
- "SEND_TO_CHANNEL",
765
- "RELAY_MESSAGE",
766
- "BROADCAST_MESSAGE"
767
- ],
768
- description: "Send a message to any supported platform (Discord, Telegram, Slack, WhatsApp, Twitch). " + "Requires specifying the target channel/platform and recipient.",
769
- validate: async (runtime, message, _state) => {
770
- const messagingService = runtime.getService("MESSAGING");
771
- if (!messagingService) {
1024
+ function extractQuery(text) {
1025
+ return text.toLowerCase().replace(/\b(switch|select|go|change|search|find|pause|stop|halt|resume|restart|continue|start|run|begin|cancel|delete|remove|list|show|view)\b/g, "").replace(/\b(about|for|named|called|with|to|my|your|our|this|current)\b/g, "").replace(/\b(task|tasks|the|a|an)\b/g, "").replace(/\s+/g, " ").trim();
1026
+ }
1027
+ var createTaskAction = {
1028
+ name: "CREATE_TASK",
1029
+ similes: ["START_TASK", "SPAWN_TASK", "NEW_TASK", "BEGIN_TASK"],
1030
+ description: "Create an orchestrated background task to be executed by a selected agent provider.",
1031
+ validate: async (runtime, message) => {
1032
+ const svc = runtime.getService("CODE_TASK");
1033
+ if (!svc) {
772
1034
  return false;
773
1035
  }
774
- const params = message.content?.params;
775
- if (!params) {
776
- return true;
777
- }
778
- const hasTarget = params.target || params.channel && params.to;
779
- const hasContent = params.content || params.text || message.content?.text;
780
- return !!(hasTarget && hasContent);
1036
+ const text = message.content.text?.toLowerCase() ?? "";
1037
+ const hasExplicit = text.includes("create task") || text.includes("new task") || text.includes("start a task");
1038
+ const hasIntent = text.includes("implement") || text.includes("build") || text.includes("create") || text.includes("develop") || text.includes("refactor") || text.includes("fix") || text.includes("add");
1039
+ return hasExplicit || hasIntent;
781
1040
  },
782
- handler: async (runtime, message, state, _options, callback) => {
783
- const messagingService = runtime.getService("MESSAGING");
784
- if (!messagingService) {
785
- if (callback) {
786
- await callback({
787
- text: "Messaging service is not available. Please ensure the orchestrator plugin is properly configured."
788
- });
1041
+ handler: async (runtime, message, _state, options, callback) => {
1042
+ const svc = getService(runtime);
1043
+ const raw = message.content.text ?? "";
1044
+ const opts = options;
1045
+ const name = (opts?.title ?? raw.split(`
1046
+ `)[0] ?? "New Task").trim().slice(0, 100) || "New Task";
1047
+ const description = (opts?.description ?? raw).trim().slice(0, 4000) || name;
1048
+ const roomId = message.roomId;
1049
+ const task = await svc.createTask(name, description, roomId);
1050
+ const stepLines = Array.isArray(opts?.steps) ? opts?.steps : undefined;
1051
+ if (stepLines && stepLines.length > 0) {
1052
+ for (const s of stepLines) {
1053
+ const step = String(s).trim();
1054
+ if (!step)
1055
+ continue;
1056
+ await svc.addStep(task.id ?? "", step);
789
1057
  }
790
- return { success: false, error: "Messaging service not available" };
1058
+ await svc.appendOutput(task.id ?? "", `Plan:
1059
+ ${stepLines.map((s, i) => `${i + 1}. ${s}`).join(`
1060
+ `)}`);
791
1061
  }
792
- const { target, content } = extractMessagingParams(message, state);
793
- if (!target?.channel) {
794
- if (callback) {
795
- await callback({
796
- text: "Please specify the target channel (discord, telegram, slack, whatsapp, twitch)."
797
- });
798
- }
799
- return { success: false, error: "Missing target channel" };
800
- }
801
- if (!target?.to) {
802
- if (callback) {
803
- await callback({
804
- text: "Please specify the recipient (channel ID, chat ID, or room ID)."
805
- });
806
- }
807
- return { success: false, error: "Missing recipient" };
808
- }
809
- if (!content?.text) {
810
- if (callback) {
811
- await callback({
812
- text: "Please provide the message text to send."
813
- });
814
- }
815
- return { success: false, error: "Missing message text" };
816
- }
817
- const result = await messagingService.send({
818
- target: {
819
- channel: target.channel,
820
- to: target.to,
821
- threadId: target.threadId,
822
- replyToMessageId: target.replyToMessageId
823
- },
824
- content: {
825
- text: content.text,
826
- attachments: content.attachments,
827
- embed: content.embed,
828
- buttons: content.buttons,
829
- disableLinkPreview: content.disableLinkPreview,
830
- silent: content.silent
831
- }
832
- });
833
- if (callback) {
834
- if (result.success) {
835
- await callback({
836
- text: `Message sent successfully to ${target.channel}.`,
837
- data: { messageId: result.messageId, sentAt: result.sentAt }
838
- });
839
- } else {
840
- await callback({
841
- text: `Failed to send message: ${result.error}`
842
- });
843
- }
844
- }
845
- return {
846
- success: result.success,
847
- data: result,
848
- error: result.error
849
- };
850
- },
851
- examples: [
852
- [
853
- {
854
- name: "{{name1}}",
855
- content: {
856
- text: "Send 'Hello from the agent!' to Discord channel 123456789",
857
- params: {
858
- channel: "discord",
859
- to: "123456789",
860
- text: "Hello from the agent!"
861
- }
862
- }
863
- },
864
- {
865
- name: "{{agentName}}",
866
- content: {
867
- text: "Message sent successfully to Discord.",
868
- actions: ["SEND_CROSS_PLATFORM_MESSAGE"]
869
- }
870
- }
871
- ],
872
- [
873
- {
874
- name: "{{name1}}",
875
- content: {
876
- text: "Send a Telegram message",
877
- params: {
878
- channel: "telegram",
879
- to: "-1001234567890",
880
- text: "Automated notification from your AI assistant."
881
- }
882
- }
883
- },
884
- {
885
- name: "{{agentName}}",
886
- content: {
887
- text: "Message sent successfully to Telegram.",
888
- actions: ["SEND_CROSS_PLATFORM_MESSAGE"]
889
- }
890
- }
891
- ]
892
- ]
893
- };
894
- var sendToDeliveryContextAction = {
895
- name: "SEND_TO_DELIVERY_CONTEXT",
896
- similes: [
897
- "DELIVER_MESSAGE",
898
- "SEND_TO_CONTEXT",
899
- "ROUTE_MESSAGE"
900
- ],
901
- 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.",
902
- validate: async (runtime, message, _state) => {
903
- const messagingService = runtime.getService("MESSAGING");
904
- if (!messagingService) {
905
- return false;
906
- }
907
- const params = message.content?.params;
908
- if (!params?.deliveryContext) {
909
- return false;
910
- }
911
- const hasText = params.text || message.content?.text;
912
- return !!hasText;
913
- },
914
- handler: async (runtime, message, state, _options, callback) => {
915
- const messagingService = runtime.getService("MESSAGING");
916
- if (!messagingService) {
917
- if (callback) {
918
- await callback({
919
- text: "Messaging service is not available."
920
- });
921
- }
922
- return { success: false, error: "Messaging service not available" };
923
- }
924
- const params = message.content?.params;
925
- const deliveryContext = params?.deliveryContext;
926
- const text = params?.text ?? message.content?.text;
927
- if (!deliveryContext) {
928
- if (callback) {
929
- await callback({
930
- text: "Please provide a delivery context with channel and recipient information."
931
- });
932
- }
933
- return { success: false, error: "Missing delivery context" };
934
- }
935
- if (!text) {
936
- if (callback) {
937
- await callback({
938
- text: "Please provide the message text to send."
939
- });
940
- }
941
- return { success: false, error: "Missing message text" };
942
- }
943
- const result = await messagingService.sendToDeliveryContext(deliveryContext, {
944
- text
1062
+ const msg = `Created task: ${task.name}
1063
+ Provider: ${task.metadata.providerLabel ?? task.metadata.providerId}
1064
+ Starting execution…`;
1065
+ await callback?.({ content: { text: msg } });
1066
+ svc.startTaskExecution(task.id ?? "").catch((err) => {
1067
+ const errorMsg = err instanceof Error ? err.message : String(err);
1068
+ svc.appendOutput(task.id ?? "", `Execution error: ${errorMsg}`).catch(() => {});
945
1069
  });
946
- if (callback) {
947
- if (result.success) {
948
- await callback({
949
- text: `Message delivered successfully via ${result.channel}.`,
950
- data: { messageId: result.messageId }
951
- });
952
- } else {
953
- await callback({
954
- text: `Failed to deliver message: ${result.error}`
955
- });
956
- }
957
- }
958
- return {
959
- success: result.success,
960
- data: result,
961
- error: result.error
962
- };
963
- },
964
- examples: [
965
- [
966
- {
967
- name: "{{name1}}",
968
- content: {
969
- text: "Send result to delivery context",
970
- params: {
971
- deliveryContext: {
972
- channel: "discord",
973
- to: "123456789"
974
- },
975
- text: "Task completed successfully!"
976
- }
977
- }
978
- },
979
- {
980
- name: "{{agentName}}",
981
- content: {
982
- text: "Message delivered successfully via discord.",
983
- actions: ["SEND_TO_DELIVERY_CONTEXT"]
984
- }
985
- }
986
- ]
987
- ]
1070
+ return { success: true, text: msg, data: { taskId: task.id ?? "" } };
1071
+ }
988
1072
  };
989
- var sendToRoomAction = {
990
- name: "SEND_TO_ROOM",
991
- similes: [
992
- "MESSAGE_ROOM",
993
- "ROOM_MESSAGE",
994
- "NOTIFY_ROOM"
995
- ],
996
- description: "Send a message to an Eliza room. The room's metadata determines which platform and recipient to use.",
997
- validate: async (runtime, message, _state) => {
998
- const messagingService = runtime.getService("MESSAGING");
999
- if (!messagingService) {
1000
- return false;
1001
- }
1002
- const params = message.content?.params;
1003
- if (!params?.roomId) {
1073
+ var listTasksAction = {
1074
+ name: "LIST_TASKS",
1075
+ similes: ["SHOW_TASKS", "GET_TASKS", "TASKS", "VIEW_TASKS"],
1076
+ description: "List tasks managed by the orchestrator.",
1077
+ validate: async (runtime, message) => {
1078
+ const svc = runtime.getService("CODE_TASK");
1079
+ if (!svc) {
1004
1080
  return false;
1005
1081
  }
1006
- const hasText = params.text || message.content?.text;
1007
- return !!hasText;
1082
+ const t = message.content.text?.toLowerCase() ?? "";
1083
+ return t.includes("list task") || t.includes("show task") || t === "tasks" || t.includes("my task");
1008
1084
  },
1009
- handler: async (runtime, message, state, _options, callback) => {
1010
- const messagingService = runtime.getService("MESSAGING");
1011
- if (!messagingService) {
1012
- if (callback) {
1013
- await callback({
1014
- text: "Messaging service is not available."
1015
- });
1016
- }
1017
- return { success: false, error: "Messaging service not available" };
1018
- }
1019
- const params = message.content?.params;
1020
- const roomId = params?.roomId;
1021
- const text = params?.text ?? message.content?.text;
1022
- if (!roomId) {
1023
- if (callback) {
1024
- await callback({
1025
- text: "Please specify the room ID to send the message to."
1026
- });
1027
- }
1028
- return { success: false, error: "Missing room ID" };
1029
- }
1030
- if (!text) {
1031
- if (callback) {
1032
- await callback({
1033
- text: "Please provide the message text to send."
1034
- });
1035
- }
1036
- return { success: false, error: "Missing message text" };
1085
+ handler: async (runtime, _message, _state, _options, callback) => {
1086
+ const svc = getService(runtime);
1087
+ const tasks = await svc.getRecentTasks(20);
1088
+ if (tasks.length === 0) {
1089
+ const msg2 = "No tasks.";
1090
+ await callback?.({ content: { text: msg2 } });
1091
+ return { success: true, text: msg2 };
1037
1092
  }
1038
- const result = await messagingService.sendToRoom(roomId, { text });
1039
- if (callback) {
1040
- if (result.success) {
1041
- await callback({
1042
- text: `Message sent to room via ${result.channel}.`,
1043
- data: { messageId: result.messageId }
1044
- });
1045
- } else {
1046
- await callback({
1047
- text: `Failed to send to room: ${result.error}`
1048
- });
1049
- }
1093
+ const lines = ["Tasks:"];
1094
+ const current = svc.getCurrentTaskId();
1095
+ for (const t of tasks) {
1096
+ const marker = t.id === current ? " (current)" : "";
1097
+ lines.push(`- ${t.name} ${t.metadata.status} ${t.metadata.progress}%${marker}`);
1050
1098
  }
1051
- return {
1052
- success: result.success,
1053
- data: result,
1054
- error: result.error
1055
- };
1056
- },
1057
- examples: [
1058
- [
1059
- {
1060
- name: "{{name1}}",
1061
- content: {
1062
- text: "Send to room",
1063
- params: {
1064
- roomId: "550e8400-e29b-41d4-a716-446655440000",
1065
- text: "Hello, room!"
1066
- }
1067
- }
1068
- },
1069
- {
1070
- name: "{{agentName}}",
1071
- content: {
1072
- text: "Message sent to room via discord.",
1073
- actions: ["SEND_TO_ROOM"]
1074
- }
1075
- }
1076
- ]
1077
- ]
1078
- };
1079
- var sendToSessionMessageAction = {
1080
- name: "SEND_TO_SESSION_MESSAGE",
1081
- similes: [
1082
- "SESSION_MESSAGE",
1083
- "MESSAGE_SESSION",
1084
- "NOTIFY_SESSION"
1085
- ],
1086
- description: "Send a message to a session by its session key. The session key is mapped to an Eliza room.",
1087
- validate: async (runtime, message, _state) => {
1088
- const messagingService = runtime.getService("MESSAGING");
1089
- if (!messagingService) {
1099
+ const msg = lines.join(`
1100
+ `);
1101
+ await callback?.({ content: { text: msg } });
1102
+ return { success: true, text: msg };
1103
+ }
1104
+ };
1105
+ var switchTaskAction = {
1106
+ name: "SWITCH_TASK",
1107
+ similes: ["SELECT_TASK", "SET_TASK", "CHANGE_TASK", "GO_TO_TASK"],
1108
+ description: "Switch the current task context to a different task.",
1109
+ validate: async (runtime, message) => {
1110
+ const svc = runtime.getService("CODE_TASK");
1111
+ if (!svc) {
1090
1112
  return false;
1091
1113
  }
1092
- const params = message.content?.params;
1093
- if (!params?.sessionKey) {
1114
+ const t = message.content.text?.toLowerCase() ?? "";
1115
+ return t.includes("switch to task") || t.includes("select task") || t.includes("task") && t.includes("switch");
1116
+ },
1117
+ handler: async (runtime, message, _state, _options, callback) => {
1118
+ const svc = getService(runtime);
1119
+ const query = extractQuery(message.content.text ?? "");
1120
+ if (!query) {
1121
+ const msg2 = "Please specify which task to switch to (by name or id).";
1122
+ await callback?.({ content: { text: msg2 } });
1123
+ return { success: false, text: msg2 };
1124
+ }
1125
+ const matches = await svc.searchTasks(query);
1126
+ const chosen = matches[0];
1127
+ if (!chosen?.id) {
1128
+ const msg2 = `No task found matching: "${query}"`;
1129
+ await callback?.({ content: { text: msg2 } });
1130
+ return { success: false, text: msg2 };
1131
+ }
1132
+ svc.setCurrentTask(chosen.id);
1133
+ const msg = `Switched to task: ${chosen.name}`;
1134
+ await callback?.({ content: { text: msg } });
1135
+ return { success: true, text: msg, data: { taskId: chosen.id } };
1136
+ }
1137
+ };
1138
+ var searchTasksAction = {
1139
+ name: "SEARCH_TASKS",
1140
+ similes: ["FIND_TASK", "LOOKUP_TASK"],
1141
+ description: "Search tasks by query.",
1142
+ validate: async (runtime, message) => {
1143
+ const svc = runtime.getService("CODE_TASK");
1144
+ if (!svc) {
1094
1145
  return false;
1095
1146
  }
1096
- const hasText = params.text || message.content?.text;
1097
- return !!hasText;
1147
+ const t = message.content.text?.toLowerCase() ?? "";
1148
+ return t.includes("search task") || t.includes("find task") || t.includes("look for task");
1098
1149
  },
1099
- handler: async (runtime, message, state, _options, callback) => {
1100
- const messagingService = runtime.getService("MESSAGING");
1101
- if (!messagingService) {
1102
- if (callback) {
1103
- await callback({
1104
- text: "Messaging service is not available."
1105
- });
1106
- }
1107
- return { success: false, error: "Messaging service not available" };
1150
+ handler: async (runtime, message, _state, options, callback) => {
1151
+ const svc = getService(runtime);
1152
+ const opt = options;
1153
+ const query = (opt?.query ?? extractQuery(message.content.text ?? "")).trim();
1154
+ if (!query) {
1155
+ const msg2 = "What would you like to search for?";
1156
+ await callback?.({ content: { text: msg2 } });
1157
+ return { success: false, text: msg2 };
1108
1158
  }
1109
- const params = message.content?.params;
1110
- const sessionKey = params?.sessionKey;
1111
- const text = params?.text ?? message.content?.text;
1112
- if (!sessionKey) {
1113
- if (callback) {
1114
- await callback({
1115
- text: "Please specify the session key to send the message to."
1116
- });
1117
- }
1118
- return { success: false, error: "Missing session key" };
1159
+ const matches = await svc.searchTasks(query);
1160
+ if (matches.length === 0) {
1161
+ const msg2 = `No tasks found matching: "${query}"`;
1162
+ await callback?.({ content: { text: msg2 } });
1163
+ return { success: true, text: msg2 };
1119
1164
  }
1120
- if (!text) {
1121
- if (callback) {
1122
- await callback({
1123
- text: "Please provide the message text to send."
1124
- });
1125
- }
1126
- return { success: false, error: "Missing message text" };
1165
+ const lines = [`Found ${matches.length} task(s) matching "${query}":`];
1166
+ for (const t of matches.slice(0, 10)) {
1167
+ lines.push(`- ${t.name} — ${t.metadata.status} ${t.metadata.progress}%`);
1127
1168
  }
1128
- const result = await messagingService.sendToSession(sessionKey, { text });
1129
- if (callback) {
1130
- if (result.success) {
1131
- await callback({
1132
- text: `Message sent to session via ${result.channel}.`,
1133
- data: { messageId: result.messageId }
1134
- });
1135
- } else {
1136
- await callback({
1137
- text: `Failed to send to session: ${result.error}`
1138
- });
1139
- }
1169
+ const msg = lines.join(`
1170
+ `);
1171
+ await callback?.({ content: { text: msg } });
1172
+ return { success: true, text: msg };
1173
+ }
1174
+ };
1175
+ var pauseTaskAction = {
1176
+ name: "PAUSE_TASK",
1177
+ similes: ["STOP_TASK", "HALT_TASK"],
1178
+ description: "Pause a running task.",
1179
+ validate: async (runtime, message) => {
1180
+ const svc = runtime.getService("CODE_TASK");
1181
+ if (!svc) {
1182
+ return false;
1140
1183
  }
1141
- return {
1142
- success: result.success,
1143
- data: result,
1144
- error: result.error
1145
- };
1184
+ const t = message.content.text?.toLowerCase() ?? "";
1185
+ return (t.includes("pause") || t.includes("stop") || t.includes("halt")) && t.includes("task");
1146
1186
  },
1147
- examples: [
1148
- [
1149
- {
1150
- name: "{{name1}}",
1151
- content: {
1152
- text: "Send to session",
1153
- params: {
1154
- sessionKey: "agent:main:dm:user123",
1155
- text: "Update from your subagent!"
1156
- }
1157
- }
1158
- },
1159
- {
1160
- name: "{{agentName}}",
1161
- content: {
1162
- text: "Message sent to session via discord.",
1163
- actions: ["SEND_TO_SESSION_MESSAGE"]
1164
- }
1165
- }
1166
- ]
1167
- ]
1187
+ handler: async (runtime, message, _state, _options, callback) => {
1188
+ const svc = getService(runtime);
1189
+ const query = extractQuery(message.content.text ?? "");
1190
+ const task = query ? (await svc.searchTasks(query))[0] : await svc.getCurrentTask();
1191
+ if (!task?.id) {
1192
+ const msg2 = "No task to pause.";
1193
+ await callback?.({ content: { text: msg2 } });
1194
+ return { success: false, text: msg2 };
1195
+ }
1196
+ await svc.pauseTask(task.id);
1197
+ const msg = `Paused task: ${task.name}`;
1198
+ await callback?.({ content: { text: msg } });
1199
+ return { success: true, text: msg };
1200
+ }
1168
1201
  };
1169
- var listMessagingChannelsAction = {
1170
- name: "LIST_MESSAGING_CHANNELS",
1171
- similes: [
1172
- "AVAILABLE_CHANNELS",
1173
- "GET_CHANNELS",
1174
- "MESSAGING_PLATFORMS"
1175
- ],
1176
- description: "List all available messaging channels/platforms that the agent can send messages to.",
1177
- validate: async (runtime, _message, _state) => {
1178
- const messagingService = runtime.getService("MESSAGING");
1179
- return !!messagingService;
1202
+ var resumeTaskAction = {
1203
+ name: "RESUME_TASK",
1204
+ similes: ["CONTINUE_TASK", "RESTART_TASK", "RUN_TASK"],
1205
+ description: "Resume a paused task.",
1206
+ validate: async (runtime, message) => {
1207
+ const svc = runtime.getService("CODE_TASK");
1208
+ if (!svc) {
1209
+ return false;
1210
+ }
1211
+ const t = message.content.text?.toLowerCase() ?? "";
1212
+ return t.includes("task") && (t.includes("resume") || t.includes("restart") || t.includes("continue"));
1180
1213
  },
1181
- handler: async (runtime, _message, _state, _options, callback) => {
1182
- const messagingService = runtime.getService("MESSAGING");
1183
- if (!messagingService) {
1184
- if (callback) {
1185
- await callback({
1186
- text: "Messaging service is not available."
1187
- });
1188
- }
1189
- return { success: false, error: "Messaging service not available" };
1214
+ handler: async (runtime, message, _state, _options, callback) => {
1215
+ const svc = getService(runtime);
1216
+ const query = extractQuery(message.content.text ?? "");
1217
+ const task = query ? (await svc.searchTasks(query))[0] : await svc.getCurrentTask();
1218
+ if (!task?.id) {
1219
+ const msg2 = "No task to resume.";
1220
+ await callback?.({ content: { text: msg2 } });
1221
+ return { success: false, text: msg2 };
1190
1222
  }
1191
- const channels = messagingService.getAvailableChannels();
1192
- if (callback) {
1193
- if (channels.length > 0) {
1194
- await callback({
1195
- text: `Available messaging channels: ${channels.join(", ")}`,
1196
- data: { channels }
1197
- });
1198
- } else {
1199
- await callback({
1200
- text: "No messaging channels are currently available.",
1201
- data: { channels: [] }
1202
- });
1203
- }
1223
+ await svc.resumeTask(task.id);
1224
+ const taskId = task.id;
1225
+ svc.startTaskExecution(taskId).catch((err) => {
1226
+ const errorMsg = err instanceof Error ? err.message : String(err);
1227
+ svc.appendOutput(taskId, `Execution error: ${errorMsg}`).catch(() => {});
1228
+ });
1229
+ const msg = `Resumed task: ${task.name}`;
1230
+ await callback?.({ content: { text: msg } });
1231
+ return { success: true, text: msg };
1232
+ }
1233
+ };
1234
+ var cancelTaskAction = {
1235
+ name: "CANCEL_TASK",
1236
+ similes: ["DELETE_TASK", "REMOVE_TASK", "ABORT_TASK"],
1237
+ description: "Cancel a task.",
1238
+ validate: async (runtime, message) => {
1239
+ const svc = runtime.getService("CODE_TASK");
1240
+ if (!svc) {
1241
+ return false;
1204
1242
  }
1205
- return {
1206
- success: true,
1207
- data: { channels }
1208
- };
1243
+ const t = message.content.text?.toLowerCase() ?? "";
1244
+ return (t.includes("cancel") || t.includes("delete") || t.includes("remove")) && t.includes("task");
1209
1245
  },
1210
- examples: [
1211
- [
1212
- {
1213
- name: "{{name1}}",
1214
- content: {
1215
- text: "What messaging platforms can you use?"
1216
- }
1217
- },
1218
- {
1219
- name: "{{agentName}}",
1220
- content: {
1221
- text: "Available messaging channels: discord, telegram, internal",
1222
- actions: ["LIST_MESSAGING_CHANNELS"]
1223
- }
1224
- }
1225
- ]
1226
- ]
1246
+ handler: async (runtime, message, _state, _options, callback) => {
1247
+ const svc = getService(runtime);
1248
+ const query = extractQuery(message.content.text ?? "");
1249
+ const task = query ? (await svc.searchTasks(query))[0] : await svc.getCurrentTask();
1250
+ if (!task?.id) {
1251
+ const msg2 = "No task to cancel.";
1252
+ await callback?.({ content: { text: msg2 } });
1253
+ return { success: false, text: msg2 };
1254
+ }
1255
+ await svc.cancelTask(task.id);
1256
+ const msg = `Cancelled task: ${task.name}`;
1257
+ await callback?.({ content: { text: msg } });
1258
+ return { success: true, text: msg };
1259
+ }
1227
1260
  };
1228
1261
 
1229
1262
  // src/config.ts
@@ -1235,61 +1268,6 @@ function getConfiguredAgentOrchestratorOptions() {
1235
1268
  return globalOptions;
1236
1269
  }
1237
1270
 
1238
- // src/providers/task-context.ts
1239
- function getService2(runtime) {
1240
- return runtime.getService("CODE_TASK");
1241
- }
1242
- function formatStatus(status) {
1243
- switch (status) {
1244
- case "pending":
1245
- return "⏳ pending";
1246
- case "running":
1247
- return "\uD83D\uDD04 running";
1248
- case "paused":
1249
- return "⏸️ paused";
1250
- case "completed":
1251
- return "✅ completed";
1252
- case "failed":
1253
- return "❌ failed";
1254
- case "cancelled":
1255
- return "\uD83D\uDED1 cancelled";
1256
- default:
1257
- return status;
1258
- }
1259
- }
1260
- var taskContextProvider = {
1261
- name: "TASK_CONTEXT",
1262
- description: "Summary of orchestrated tasks and current selection",
1263
- dynamic: true,
1264
- get: async (runtime, _message, _state) => {
1265
- const svc = getService2(runtime);
1266
- if (!svc) {
1267
- return { text: "Task service not available." };
1268
- }
1269
- const current = await svc.getCurrentTask();
1270
- const tasks = await svc.getRecentTasks(10);
1271
- if (tasks.length === 0) {
1272
- return { text: "No tasks have been created yet." };
1273
- }
1274
- const lines = [];
1275
- if (current) {
1276
- lines.push(`## Current Task`);
1277
- lines.push(`- Name: ${current.name}`);
1278
- lines.push(`- Status: ${formatStatus(current.metadata.status)}`);
1279
- lines.push(`- Progress: ${current.metadata.progress}%`);
1280
- lines.push(`- Provider: ${current.metadata.providerLabel ?? current.metadata.providerId}`);
1281
- lines.push("");
1282
- }
1283
- lines.push("## Recent Tasks");
1284
- for (const t of tasks) {
1285
- const marker = current?.id === t.id ? " (current)" : "";
1286
- lines.push(`- ${t.name} — ${formatStatus(t.metadata.status)} ${t.metadata.progress}%${marker}`);
1287
- }
1288
- return { text: lines.join(`
1289
- `).trim() };
1290
- }
1291
- };
1292
-
1293
1271
  // src/providers/orchestrator-config.ts
1294
1272
  var DEFAULT_CONFIG = {
1295
1273
  subagents: {
@@ -1316,15 +1294,18 @@ function getOrchestratorConfig(runtime) {
1316
1294
  const subagentsRaw = settings.subagents ?? {};
1317
1295
  const a2aRaw = settings.agentToAgent ?? {};
1318
1296
  const sandboxRaw = settings.sandbox ?? {};
1297
+ const subagents = {
1298
+ enabled: subagentsRaw.enabled ?? true,
1299
+ timeoutSeconds: subagentsRaw.timeoutSeconds ?? 300,
1300
+ allowAgents: subagentsRaw.allowAgents ?? [],
1301
+ archiveAfterMinutes: subagentsRaw.archiveAfterMinutes ?? 60
1302
+ };
1303
+ if (subagentsRaw.model)
1304
+ subagents.model = subagentsRaw.model;
1305
+ if (subagentsRaw.thinking)
1306
+ subagents.thinking = subagentsRaw.thinking;
1319
1307
  return {
1320
- subagents: {
1321
- enabled: subagentsRaw.enabled ?? DEFAULT_CONFIG.subagents.enabled,
1322
- model: subagentsRaw.model,
1323
- thinking: subagentsRaw.thinking,
1324
- timeoutSeconds: subagentsRaw.timeoutSeconds ?? DEFAULT_CONFIG.subagents.timeoutSeconds,
1325
- allowAgents: subagentsRaw.allowAgents ?? DEFAULT_CONFIG.subagents.allowAgents,
1326
- archiveAfterMinutes: subagentsRaw.archiveAfterMinutes ?? DEFAULT_CONFIG.subagents.archiveAfterMinutes
1327
- },
1308
+ subagents,
1328
1309
  agentToAgent: {
1329
1310
  enabled: a2aRaw.enabled ?? DEFAULT_CONFIG.agentToAgent.enabled,
1330
1311
  allow: a2aRaw.allow ?? DEFAULT_CONFIG.agentToAgent.allow
@@ -1373,7 +1354,62 @@ var orchestratorConfigProvider = {
1373
1354
  description: "Provides orchestrator configuration including subagents, A2A, and sandbox settings",
1374
1355
  get: async (runtime, _message, _state) => {
1375
1356
  const config = getOrchestratorConfig(runtime);
1376
- return formatConfigContext(config);
1357
+ return { text: formatConfigContext(config) };
1358
+ }
1359
+ };
1360
+
1361
+ // src/providers/task-context.ts
1362
+ function getService2(runtime) {
1363
+ return runtime.getService("CODE_TASK");
1364
+ }
1365
+ function formatStatus(status) {
1366
+ switch (status) {
1367
+ case "pending":
1368
+ return "⏳ pending";
1369
+ case "running":
1370
+ return "\uD83D\uDD04 running";
1371
+ case "paused":
1372
+ return "⏸️ paused";
1373
+ case "completed":
1374
+ return "✅ completed";
1375
+ case "failed":
1376
+ return "❌ failed";
1377
+ case "cancelled":
1378
+ return "\uD83D\uDED1 cancelled";
1379
+ default:
1380
+ return status;
1381
+ }
1382
+ }
1383
+ var taskContextProvider = {
1384
+ name: "TASK_CONTEXT",
1385
+ description: "Summary of orchestrated tasks and current selection",
1386
+ dynamic: true,
1387
+ get: async (runtime, _message, _state) => {
1388
+ const svc = getService2(runtime);
1389
+ if (!svc) {
1390
+ return { text: "Task service not available." };
1391
+ }
1392
+ const current = await svc.getCurrentTask();
1393
+ const tasks = await svc.getRecentTasks(10);
1394
+ if (tasks.length === 0) {
1395
+ return { text: "No tasks have been created yet." };
1396
+ }
1397
+ const lines = [];
1398
+ if (current) {
1399
+ lines.push(`## Current Task`);
1400
+ lines.push(`- Name: ${current.name}`);
1401
+ lines.push(`- Status: ${formatStatus(current.metadata.status)}`);
1402
+ lines.push(`- Progress: ${current.metadata.progress}%`);
1403
+ lines.push(`- Provider: ${current.metadata.providerLabel ?? current.metadata.providerId}`);
1404
+ lines.push("");
1405
+ }
1406
+ lines.push("## Recent Tasks");
1407
+ for (const t of tasks) {
1408
+ const marker = current?.id === t.id ? " (current)" : "";
1409
+ lines.push(`- ${t.name} — ${formatStatus(t.metadata.status)} ${t.metadata.progress}%${marker}`);
1410
+ }
1411
+ return { text: lines.join(`
1412
+ `).trim() };
1377
1413
  }
1378
1414
  };
1379
1415
 
@@ -1383,7 +1419,7 @@ import {
1383
1419
  Service
1384
1420
  } from "@elizaos/core";
1385
1421
 
1386
- // ../../../node_modules/.bun/uuid@13.0.0/node_modules/uuid/dist-node/stringify.js
1422
+ // node_modules/uuid/dist-node/stringify.js
1387
1423
  var byteToHex = [];
1388
1424
  for (let i = 0;i < 256; ++i) {
1389
1425
  byteToHex.push((i + 256).toString(16).slice(1));
@@ -1392,7 +1428,7 @@ function unsafeStringify(arr, offset = 0) {
1392
1428
  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();
1393
1429
  }
1394
1430
 
1395
- // ../../../node_modules/.bun/uuid@13.0.0/node_modules/uuid/dist-node/rng.js
1431
+ // node_modules/uuid/dist-node/rng.js
1396
1432
  import { randomFillSync } from "node:crypto";
1397
1433
  var rnds8Pool = new Uint8Array(256);
1398
1434
  var poolPtr = rnds8Pool.length;
@@ -1404,11 +1440,11 @@ function rng() {
1404
1440
  return rnds8Pool.slice(poolPtr, poolPtr += 16);
1405
1441
  }
1406
1442
 
1407
- // ../../../node_modules/.bun/uuid@13.0.0/node_modules/uuid/dist-node/native.js
1443
+ // node_modules/uuid/dist-node/native.js
1408
1444
  import { randomUUID } from "node:crypto";
1409
1445
  var native_default = { randomUUID };
1410
1446
 
1411
- // ../../../node_modules/.bun/uuid@13.0.0/node_modules/uuid/dist-node/v4.js
1447
+ // node_modules/uuid/dist-node/v4.js
1412
1448
  function _v4(options, buf, offset) {
1413
1449
  options = options || {};
1414
1450
  const rnds = options.random ?? options.rng?.() ?? rng();
@@ -1882,621 +1918,511 @@ Provider: ${provider.label} (${provider.id})`);
1882
1918
  lines.push("");
1883
1919
  }
1884
1920
  }
1885
- return lines.join(`
1886
- `).trim();
1887
- }
1888
- }
1889
-
1890
- // src/services/subagent-service.ts
1891
- import { EventEmitter as EventEmitter2 } from "node:events";
1892
- import crypto2 from "node:crypto";
1893
- import {
1894
- EventType,
1895
- ChannelType,
1896
- Service as Service2
1897
- } from "@elizaos/core";
1898
- class SubagentService extends Service2 {
1899
- static serviceType = "SUBAGENT";
1900
- capabilityDescription = "Manages subagent spawning, lifecycle, and communication";
1901
- emitter = new EventEmitter2;
1902
- subagentRuns = new Map;
1903
- activeRuns = new Map;
1904
- sweeper = null;
1905
- initialized = false;
1906
- static async start(runtime) {
1907
- const service = new SubagentService(runtime);
1908
- await service.initialize();
1909
- return service;
1921
+ return lines.join(`
1922
+ `).trim();
1923
+ }
1924
+ }
1925
+
1926
+ // src/services/messaging-service.ts
1927
+ import crypto2 from "node:crypto";
1928
+ import { EventEmitter as EventEmitter2 } from "node:events";
1929
+ import { EventType, Service as Service2 } from "@elizaos/core";
1930
+ class MessagingService extends Service2 {
1931
+ static serviceType = "MESSAGING";
1932
+ capabilityDescription = "Unified cross-platform messaging for sending messages to any supported channel";
1933
+ emitter = new EventEmitter2;
1934
+ adapters = new Map;
1935
+ pendingDeliveries = new Map;
1936
+ initialized = false;
1937
+ static async start(runtime) {
1938
+ const service = new MessagingService(runtime);
1939
+ await service.initialize();
1940
+ return service;
1941
+ }
1942
+ async initialize() {
1943
+ if (this.initialized) {
1944
+ return;
1945
+ }
1946
+ this.initialized = true;
1947
+ this.registerBuiltInAdapters();
1948
+ }
1949
+ registerBuiltInAdapters() {
1950
+ this.registerAdapter({
1951
+ channel: "discord",
1952
+ isAvailable: () => {
1953
+ const service = this.runtime.getService("DISCORD");
1954
+ return !!service;
1955
+ },
1956
+ send: async (params) => this.sendViaDiscord(params)
1957
+ });
1958
+ this.registerAdapter({
1959
+ channel: "telegram",
1960
+ isAvailable: () => {
1961
+ const service = this.runtime.getService("TELEGRAM");
1962
+ return !!service;
1963
+ },
1964
+ send: async (params) => this.sendViaTelegram(params)
1965
+ });
1966
+ this.registerAdapter({
1967
+ channel: "slack",
1968
+ isAvailable: () => {
1969
+ const service = this.runtime.getService("slack");
1970
+ return !!service;
1971
+ },
1972
+ send: async (params) => this.sendViaSlack(params)
1973
+ });
1974
+ this.registerAdapter({
1975
+ channel: "whatsapp",
1976
+ isAvailable: () => {
1977
+ const service = this.runtime.getService("whatsapp");
1978
+ return !!service;
1979
+ },
1980
+ send: async (params) => this.sendViaWhatsApp(params)
1981
+ });
1982
+ this.registerAdapter({
1983
+ channel: "twitch",
1984
+ isAvailable: () => {
1985
+ const service = this.runtime.getService("twitch");
1986
+ return !!service;
1987
+ },
1988
+ send: async (params) => this.sendViaTwitch(params)
1989
+ });
1990
+ this.registerAdapter({
1991
+ channel: "internal",
1992
+ isAvailable: () => true,
1993
+ send: async (params) => this.sendViaInternal(params)
1994
+ });
1995
+ }
1996
+ registerAdapter(adapter) {
1997
+ this.adapters.set(adapter.channel, adapter);
1998
+ }
1999
+ getAdapter(channel) {
2000
+ return this.adapters.get(channel);
2001
+ }
2002
+ getAvailableChannels() {
2003
+ const channels = [];
2004
+ for (const [channel, adapter] of this.adapters) {
2005
+ if (adapter.isAvailable()) {
2006
+ channels.push(channel);
2007
+ }
2008
+ }
2009
+ return channels;
2010
+ }
2011
+ async send(params) {
2012
+ const idempotencyKey = params.idempotencyKey ?? crypto2.randomUUID();
2013
+ const channel = params.target.channel;
2014
+ const existing = this.pendingDeliveries.get(idempotencyKey);
2015
+ if (existing) {
2016
+ const result = {
2017
+ success: existing.status.status === "sent" || existing.status.status === "delivered",
2018
+ channel,
2019
+ targetId: params.target.to
2020
+ };
2021
+ if (existing.status.messageId)
2022
+ result.messageId = existing.status.messageId;
2023
+ if (existing.status.error)
2024
+ result.error = existing.status.error;
2025
+ if (existing.status.status === "sent")
2026
+ result.sentAt = existing.status.updatedAt;
2027
+ return result;
2028
+ }
2029
+ const status = {
2030
+ status: "pending",
2031
+ updatedAt: Date.now()
2032
+ };
2033
+ this.pendingDeliveries.set(idempotencyKey, { params, status });
2034
+ this.emitMessagingEvent("MESSAGING_SEND_REQUESTED", {
2035
+ idempotencyKey,
2036
+ channel,
2037
+ targetId: params.target.to,
2038
+ status: "pending"
2039
+ });
2040
+ const adapter = this.adapters.get(channel);
2041
+ if (!adapter) {
2042
+ const errorMsg = `No adapter registered for channel: ${channel}`;
2043
+ const result = {
2044
+ success: false,
2045
+ channel,
2046
+ targetId: params.target.to,
2047
+ error: errorMsg
2048
+ };
2049
+ status.status = "failed";
2050
+ status.error = errorMsg;
2051
+ status.updatedAt = Date.now();
2052
+ this.emitMessagingEvent("MESSAGING_SEND_FAILED", {
2053
+ idempotencyKey,
2054
+ channel,
2055
+ targetId: params.target.to,
2056
+ status: "failed",
2057
+ error: errorMsg
2058
+ });
2059
+ return result;
2060
+ }
2061
+ if (!adapter.isAvailable()) {
2062
+ const errorMsg = `${channel} service is not available`;
2063
+ const result = {
2064
+ success: false,
2065
+ channel,
2066
+ targetId: params.target.to,
2067
+ error: errorMsg
2068
+ };
2069
+ status.status = "failed";
2070
+ status.error = errorMsg;
2071
+ status.updatedAt = Date.now();
2072
+ this.emitMessagingEvent("MESSAGING_SEND_FAILED", {
2073
+ idempotencyKey,
2074
+ channel,
2075
+ targetId: params.target.to,
2076
+ status: "failed",
2077
+ error: errorMsg
2078
+ });
2079
+ return result;
2080
+ }
2081
+ try {
2082
+ const result = await adapter.send({ ...params, idempotencyKey });
2083
+ status.status = result.success ? "sent" : "failed";
2084
+ if (result.messageId)
2085
+ status.messageId = result.messageId;
2086
+ if (result.error)
2087
+ status.error = result.error;
2088
+ status.updatedAt = Date.now();
2089
+ if (result.success) {
2090
+ const sentPayload = {
2091
+ idempotencyKey,
2092
+ channel,
2093
+ targetId: params.target.to,
2094
+ status: "sent"
2095
+ };
2096
+ if (result.messageId)
2097
+ sentPayload.messageId = result.messageId;
2098
+ if (result.sentAt)
2099
+ sentPayload.sentAt = result.sentAt;
2100
+ this.emitMessagingEvent("MESSAGING_SENT", sentPayload);
2101
+ } else {
2102
+ const failedPayload = {
2103
+ idempotencyKey,
2104
+ channel,
2105
+ targetId: params.target.to,
2106
+ status: "failed"
2107
+ };
2108
+ if (result.error)
2109
+ failedPayload.error = result.error;
2110
+ this.emitMessagingEvent("MESSAGING_SEND_FAILED", failedPayload);
2111
+ }
2112
+ return result;
2113
+ } catch (error) {
2114
+ const errorMessage = error instanceof Error ? error.message : String(error);
2115
+ status.status = "failed";
2116
+ status.error = errorMessage;
2117
+ status.updatedAt = Date.now();
2118
+ this.emitMessagingEvent("MESSAGING_SEND_FAILED", {
2119
+ idempotencyKey,
2120
+ channel,
2121
+ targetId: params.target.to,
2122
+ status: "failed",
2123
+ error: errorMessage
2124
+ });
2125
+ return {
2126
+ success: false,
2127
+ channel,
2128
+ targetId: params.target.to,
2129
+ error: errorMessage
2130
+ };
2131
+ }
1910
2132
  }
1911
- async initialize() {
1912
- if (this.initialized) {
1913
- return;
2133
+ async sendToDeliveryContext(deliveryContext, content, options) {
2134
+ const channel = this.normalizeChannel(deliveryContext.channel);
2135
+ const to = deliveryContext.to ?? deliveryContext.accountId ?? "";
2136
+ if (!to) {
2137
+ return {
2138
+ success: false,
2139
+ channel,
2140
+ targetId: "",
2141
+ error: "No recipient specified in delivery context"
2142
+ };
1914
2143
  }
1915
- this.initialized = true;
1916
- this.runtime.registerEvent(EventType.RUN_ENDED, async (payload) => {
1917
- await this.handleRunEnded(payload);
1918
- });
1919
- this.runtime.registerEvent(EventType.RUN_TIMEOUT, async (payload) => {
1920
- await this.handleRunTimeout(payload);
2144
+ return this.send({
2145
+ target: {
2146
+ channel,
2147
+ to,
2148
+ ...deliveryContext.accountId ? { accountId: deliveryContext.accountId } : {},
2149
+ ...deliveryContext.threadId !== undefined ? { threadId: deliveryContext.threadId } : {}
2150
+ },
2151
+ content,
2152
+ ...options?.idempotencyKey ? { idempotencyKey: options.idempotencyKey } : {},
2153
+ ...options?.timeoutMs !== undefined ? { timeoutMs: options.timeoutMs } : {}
1921
2154
  });
1922
- this.startSweeper();
1923
- }
1924
- getConfig() {
1925
- const settings = this.runtime.character?.settings;
1926
- const subagents = settings?.subagents ?? {};
1927
- return {
1928
- enabled: subagents.enabled !== false,
1929
- model: subagents.model,
1930
- thinking: subagents.thinking,
1931
- timeoutSeconds: subagents.timeoutSeconds ?? 300,
1932
- allowAgents: subagents.allowAgents ?? [],
1933
- archiveAfterMinutes: subagents.archiveAfterMinutes ?? 60
1934
- };
1935
- }
1936
- getAgentToAgentPolicy() {
1937
- const settings = this.runtime.character?.settings;
1938
- const a2aConfig = settings?.agentToAgent ?? {};
1939
- const enabled = a2aConfig.enabled === true;
1940
- const allowRules = (a2aConfig.allow ?? []).map((rule) => ({
1941
- source: rule.source ?? "*",
1942
- target: rule.target ?? "*"
1943
- }));
1944
- return {
1945
- enabled,
1946
- allowRules,
1947
- isAllowed: (sourceAgentId, targetAgentId) => {
1948
- if (!enabled) {
1949
- return false;
1950
- }
1951
- if (sourceAgentId === targetAgentId) {
1952
- return true;
1953
- }
1954
- const sourceNorm = normalizeAgentId2(sourceAgentId);
1955
- const targetNorm = normalizeAgentId2(targetAgentId);
1956
- for (const rule of allowRules) {
1957
- const sourceMatch = rule.source === "*" || normalizeAgentId2(rule.source) === sourceNorm;
1958
- const targetMatch = rule.target === "*" || normalizeAgentId2(rule.target) === targetNorm;
1959
- if (sourceMatch && targetMatch) {
1960
- return true;
1961
- }
1962
- }
1963
- return false;
1964
- }
1965
- };
1966
2155
  }
1967
- async spawnSubagent(params, requesterContext) {
1968
- const config = this.getConfig();
1969
- if (!config.enabled) {
2156
+ async sendToRoom(roomId, content, options) {
2157
+ const room = await this.runtime.getRoom(roomId);
2158
+ if (!room) {
1970
2159
  return {
1971
- status: "forbidden",
1972
- error: "Subagent spawning is disabled"
2160
+ success: false,
2161
+ channel: "unknown",
2162
+ targetId: roomId,
2163
+ error: `Room not found: ${roomId}`
1973
2164
  };
1974
2165
  }
1975
- if (requesterContext.sessionKey && isSubagentSessionKey2(requesterContext.sessionKey)) {
2166
+ const metadata = room.metadata;
2167
+ const channel = this.normalizeChannel(metadata?.messagingChannel);
2168
+ const to = metadata?.messagingTo ?? room.channelId ?? "";
2169
+ if (!to) {
1976
2170
  return {
1977
- status: "forbidden",
1978
- error: "sessions_spawn is not allowed from sub-agent sessions"
2171
+ success: false,
2172
+ channel,
2173
+ targetId: roomId,
2174
+ error: "Room has no messaging target configured"
1979
2175
  };
1980
2176
  }
1981
- const requesterAgentId = requesterContext.sessionKey ? extractAgentIdFromSessionKey(requesterContext.sessionKey) : this.runtime.character?.name ?? "unknown";
1982
- const targetAgentId = params.agentId ? normalizeAgentId2(params.agentId) : requesterAgentId;
1983
- if (targetAgentId !== requesterAgentId) {
1984
- const allowAgents = config.allowAgents ?? [];
1985
- const allowAny = allowAgents.some((v) => v.trim() === "*");
1986
- const allowSet = new Set(allowAgents.filter((v) => v.trim() && v.trim() !== "*").map((v) => normalizeAgentId2(v)));
1987
- if (!allowAny && !allowSet.has(targetAgentId)) {
1988
- return {
1989
- status: "forbidden",
1990
- error: `agentId "${targetAgentId}" is not allowed for subagent spawning`
1991
- };
1992
- }
1993
- }
1994
- const childSessionKey = createSubagentSessionKey(targetAgentId);
1995
- const runId = crypto2.randomUUID();
1996
- const childRoomId = sessionKeyToRoomId(childSessionKey, targetAgentId);
1997
- const roomMetadata = {
1998
- isSubagent: true,
1999
- sessionKey: childSessionKey,
2000
- parentRoomId: requesterContext.roomId,
2001
- parentSessionKey: requesterContext.sessionKey,
2002
- task: params.task,
2003
- label: params.label,
2004
- spawnedAt: Date.now(),
2005
- cleanup: params.cleanup ?? "keep"
2006
- };
2007
- const childRoom = {
2008
- id: childRoomId,
2009
- name: params.label || `Subagent: ${params.task.slice(0, 50)}`,
2010
- type: ChannelType.SELF,
2011
- channelId: childSessionKey,
2012
- agentId: this.runtime.agentId,
2013
- worldId: this.runtime.agentId,
2014
- metadata: roomMetadata
2015
- };
2016
- await this.runtime.ensureRoomExists(childRoom);
2017
- const now2 = Date.now();
2018
- const archiveAfterMs = config.archiveAfterMinutes ? config.archiveAfterMinutes * 60000 : undefined;
2019
- const record = {
2020
- runId,
2021
- childSessionKey,
2022
- requesterSessionKey: requesterContext.sessionKey ?? "unknown",
2023
- requesterOrigin: normalizeDeliveryContext(requesterContext.origin),
2024
- requesterDisplayKey: requesterContext.sessionKey ?? "main",
2025
- task: params.task,
2026
- cleanup: params.cleanup ?? "keep",
2027
- label: params.label,
2028
- createdAt: now2,
2029
- startedAt: now2,
2030
- archiveAtMs: archiveAfterMs ? now2 + archiveAfterMs : undefined,
2031
- cleanupHandled: false,
2032
- roomId: childRoomId,
2033
- worldId: this.runtime.agentId
2034
- };
2035
- this.subagentRuns.set(runId, record);
2036
- this.emitSubagentEvent("SUBAGENT_SPAWN_REQUESTED", {
2037
- runId,
2038
- childSessionKey,
2039
- childRoomId,
2040
- requesterSessionKey: requesterContext.sessionKey,
2041
- requesterRoomId: requesterContext.roomId,
2042
- task: params.task,
2043
- label: params.label
2044
- });
2045
- const systemPrompt = this.buildSubagentSystemPrompt({
2046
- requesterSessionKey: requesterContext.sessionKey,
2047
- requesterOrigin: requesterContext.origin,
2048
- childSessionKey,
2049
- label: params.label,
2050
- task: params.task
2051
- });
2052
- const initialMessage = {
2053
- id: hashToUUID(`${runId}-initial`),
2054
- entityId: this.runtime.agentId,
2055
- agentId: this.runtime.agentId,
2056
- roomId: childRoomId,
2057
- content: {
2058
- text: params.task,
2059
- type: "text",
2060
- metadata: {
2061
- isSubagentTask: true,
2062
- runId,
2063
- systemPromptOverride: systemPrompt,
2064
- modelOverride: params.model || config.model,
2065
- thinkingOverride: params.thinking || config.thinking
2066
- }
2067
- }
2068
- };
2069
- this.executeSubagentRun(runId, initialMessage, params.runTimeoutSeconds).catch((error) => {
2070
- this.runtime.logger.error("Subagent execution error", { runId, error });
2071
- this.handleSubagentError(runId, error);
2177
+ return this.send({
2178
+ target: {
2179
+ channel,
2180
+ to,
2181
+ ...metadata?.messagingAccountId ? { accountId: metadata.messagingAccountId } : {},
2182
+ ...metadata?.messagingThreadId !== undefined ? { threadId: metadata.messagingThreadId } : {}
2183
+ },
2184
+ content,
2185
+ ...options?.idempotencyKey ? { idempotencyKey: options.idempotencyKey } : {},
2186
+ ...options?.timeoutMs !== undefined ? { timeoutMs: options.timeoutMs } : {}
2072
2187
  });
2073
- return {
2074
- status: "accepted",
2075
- childSessionKey,
2076
- childRoomId,
2077
- runId,
2078
- modelApplied: !!(params.model || config.model)
2079
- };
2080
2188
  }
2081
- async executeSubagentRun(runId, initialMessage, timeoutSeconds) {
2082
- const config = this.getConfig();
2083
- const timeout = (timeoutSeconds ?? config.timeoutSeconds ?? 300) * 1000;
2084
- const controller = new AbortController;
2085
- this.activeRuns.set(runId, controller);
2086
- const timeoutId = timeout > 0 ? setTimeout(() => {
2087
- controller.abort();
2088
- this.handleSubagentTimeout(runId);
2089
- }, timeout) : null;
2090
- try {
2091
- await this.runtime.emitEvent(EventType.MESSAGE_RECEIVED, {
2092
- runtime: this.runtime,
2093
- message: initialMessage,
2094
- source: "subagent"
2095
- });
2096
- await this.waitForCompletion(runId, timeout, controller.signal);
2097
- } finally {
2098
- if (timeoutId) {
2099
- clearTimeout(timeoutId);
2100
- }
2101
- this.activeRuns.delete(runId);
2102
- }
2189
+ async sendToSession(sessionKey, content, options) {
2190
+ const agentId = extractAgentIdFromSessionKey(sessionKey);
2191
+ const roomId = sessionKeyToRoomId(sessionKey, agentId);
2192
+ return this.sendToRoom(roomId, content, options);
2103
2193
  }
2104
- async waitForCompletion(runId, timeoutMs, signal) {
2105
- const startTime = Date.now();
2106
- const pollInterval = 500;
2107
- while (!signal.aborted) {
2108
- const record = this.subagentRuns.get(runId);
2109
- if (!record) {
2110
- return;
2111
- }
2112
- if (record.endedAt) {
2113
- return;
2114
- }
2115
- if (Date.now() - startTime > timeoutMs + 5000) {
2116
- return;
2194
+ async sendViaDiscord(params) {
2195
+ const discordService = this.runtime.getService("DISCORD");
2196
+ if (!discordService?.client) {
2197
+ return {
2198
+ success: false,
2199
+ channel: "discord",
2200
+ targetId: params.target.to,
2201
+ error: "Discord service not available"
2202
+ };
2203
+ }
2204
+ try {
2205
+ const channel = await discordService.client.channels.fetch(params.target.to);
2206
+ if (!channel?.send) {
2207
+ return {
2208
+ success: false,
2209
+ channel: "discord",
2210
+ targetId: params.target.to,
2211
+ error: "Channel not found or not a text channel"
2212
+ };
2117
2213
  }
2118
- await new Promise((resolve) => setTimeout(resolve, pollInterval));
2214
+ const message = await channel.send({
2215
+ content: params.content.text,
2216
+ ...params.target.replyToMessageId ? { reply: { messageReference: params.target.replyToMessageId } } : {}
2217
+ });
2218
+ return {
2219
+ success: true,
2220
+ messageId: message.id,
2221
+ channel: "discord",
2222
+ targetId: params.target.to,
2223
+ sentAt: Date.now()
2224
+ };
2225
+ } catch (error) {
2226
+ return {
2227
+ success: false,
2228
+ channel: "discord",
2229
+ targetId: params.target.to,
2230
+ error: error instanceof Error ? error.message : String(error)
2231
+ };
2119
2232
  }
2120
2233
  }
2121
- handleSubagentTimeout(runId) {
2122
- const record = this.subagentRuns.get(runId);
2123
- if (!record || record.endedAt) {
2124
- return;
2234
+ async sendViaTelegram(params) {
2235
+ const telegramService = this.runtime.getService("TELEGRAM");
2236
+ if (!telegramService?.bot?.telegram) {
2237
+ return {
2238
+ success: false,
2239
+ channel: "telegram",
2240
+ targetId: params.target.to,
2241
+ error: "Telegram service not available"
2242
+ };
2125
2243
  }
2126
- record.endedAt = Date.now();
2127
- record.outcome = { status: "timeout" };
2128
- this.emitSubagentEvent("SUBAGENT_RUN_TIMEOUT", {
2129
- runId,
2130
- childSessionKey: record.childSessionKey,
2131
- childRoomId: record.roomId,
2132
- task: record.task,
2133
- status: "timeout"
2134
- });
2135
- this.announceSubagentResult(runId).catch((err) => {
2136
- this.runtime.logger.error("Failed to announce timeout", { runId, error: err });
2137
- });
2138
- }
2139
- handleSubagentError(runId, error) {
2140
- const record = this.subagentRuns.get(runId);
2141
- if (!record) {
2142
- return;
2244
+ try {
2245
+ const chatId = Number.isNaN(Number(params.target.to)) ? params.target.to : Number(params.target.to);
2246
+ const result = await telegramService.bot.telegram.sendMessage(chatId, params.content.text, {
2247
+ reply_to_message_id: params.target.replyToMessageId ? Number(params.target.replyToMessageId) : undefined,
2248
+ disable_web_page_preview: params.content.disableLinkPreview,
2249
+ disable_notification: params.content.silent
2250
+ });
2251
+ return {
2252
+ success: true,
2253
+ messageId: String(result.message_id),
2254
+ channel: "telegram",
2255
+ targetId: params.target.to,
2256
+ sentAt: Date.now()
2257
+ };
2258
+ } catch (error) {
2259
+ return {
2260
+ success: false,
2261
+ channel: "telegram",
2262
+ targetId: params.target.to,
2263
+ error: error instanceof Error ? error.message : String(error)
2264
+ };
2143
2265
  }
2144
- record.endedAt = Date.now();
2145
- record.outcome = {
2146
- status: "error",
2147
- error: error instanceof Error ? error.message : String(error)
2148
- };
2149
- this.emitSubagentEvent("SUBAGENT_RUN_FAILED", {
2150
- runId,
2151
- childSessionKey: record.childSessionKey,
2152
- childRoomId: record.roomId,
2153
- task: record.task,
2154
- status: "error",
2155
- error: record.outcome.error
2156
- });
2157
- this.announceSubagentResult(runId).catch((err) => {
2158
- this.runtime.logger.error("Failed to announce error", { runId, error: err });
2159
- });
2160
2266
  }
2161
- async handleRunEnded(payload) {
2162
- const p = payload;
2163
- if (!p.roomId) {
2164
- return;
2267
+ async sendViaSlack(params) {
2268
+ const slackService = this.runtime.getService("slack");
2269
+ if (!slackService?.sendMessage) {
2270
+ return {
2271
+ success: false,
2272
+ channel: "slack",
2273
+ targetId: params.target.to,
2274
+ error: "Slack service not available or sendMessage method not found"
2275
+ };
2165
2276
  }
2166
- for (const [runId, record] of this.subagentRuns.entries()) {
2167
- if (record.roomId === p.roomId && !record.endedAt) {
2168
- record.endedAt = Date.now();
2169
- record.outcome = { status: "ok" };
2170
- this.emitSubagentEvent("SUBAGENT_RUN_COMPLETED", {
2171
- runId,
2172
- childSessionKey: record.childSessionKey,
2173
- childRoomId: record.roomId,
2174
- task: record.task,
2175
- status: "completed",
2176
- startedAt: record.startedAt,
2177
- endedAt: record.endedAt,
2178
- durationMs: record.endedAt - (record.startedAt ?? record.createdAt)
2179
- });
2180
- await this.announceSubagentResult(runId);
2181
- break;
2182
- }
2277
+ try {
2278
+ const result = await slackService.sendMessage(params.target.to, params.content.text, {
2279
+ ...params.target.threadId ? { threadTs: String(params.target.threadId) } : {},
2280
+ replyBroadcast: false
2281
+ });
2282
+ return {
2283
+ success: true,
2284
+ messageId: result.ts,
2285
+ channel: "slack",
2286
+ targetId: params.target.to,
2287
+ sentAt: Date.now()
2288
+ };
2289
+ } catch (error) {
2290
+ return {
2291
+ success: false,
2292
+ channel: "slack",
2293
+ targetId: params.target.to,
2294
+ error: error instanceof Error ? error.message : String(error)
2295
+ };
2183
2296
  }
2184
2297
  }
2185
- async handleRunTimeout(payload) {
2186
- const p = payload;
2187
- if (!p.roomId) {
2188
- return;
2298
+ async sendViaWhatsApp(params) {
2299
+ const whatsappService = this.runtime.getService("whatsapp");
2300
+ if (!whatsappService?.sendText) {
2301
+ return {
2302
+ success: false,
2303
+ channel: "whatsapp",
2304
+ targetId: params.target.to,
2305
+ error: "WhatsApp service not available or sendText method not found"
2306
+ };
2189
2307
  }
2190
- for (const [runId, record] of this.subagentRuns.entries()) {
2191
- if (record.roomId === p.roomId && !record.endedAt) {
2192
- this.handleSubagentTimeout(runId);
2193
- break;
2194
- }
2308
+ try {
2309
+ const result = await whatsappService.sendText(params.target.to, params.content.text);
2310
+ const messageId = result.messages?.[0]?.id;
2311
+ return {
2312
+ success: true,
2313
+ ...messageId ? { messageId } : {},
2314
+ channel: "whatsapp",
2315
+ targetId: params.target.to,
2316
+ sentAt: Date.now()
2317
+ };
2318
+ } catch (error) {
2319
+ return {
2320
+ success: false,
2321
+ channel: "whatsapp",
2322
+ targetId: params.target.to,
2323
+ error: error instanceof Error ? error.message : String(error)
2324
+ };
2195
2325
  }
2196
2326
  }
2197
- async announceSubagentResult(runId) {
2198
- const record = this.subagentRuns.get(runId);
2199
- if (!record) {
2200
- return false;
2201
- }
2202
- if (record.cleanupCompletedAt || record.cleanupHandled) {
2203
- return false;
2327
+ async sendViaTwitch(params) {
2328
+ const twitchService = this.runtime.getService("twitch");
2329
+ if (!twitchService?.sendMessage) {
2330
+ return {
2331
+ success: false,
2332
+ channel: "twitch",
2333
+ targetId: params.target.to,
2334
+ error: "Twitch service not available or sendMessage method not found"
2335
+ };
2204
2336
  }
2205
- record.cleanupHandled = true;
2206
- let reply;
2207
- if (record.roomId) {
2208
- const memories = await this.runtime.getMemories({
2209
- roomId: record.roomId,
2210
- count: 10
2337
+ try {
2338
+ const result = await twitchService.sendMessage(params.content.text, {
2339
+ channel: params.target.to,
2340
+ ...params.target.replyToMessageId ? { replyTo: params.target.replyToMessageId } : {}
2211
2341
  });
2212
- const lastAssistant = memories.filter((m) => m.entityId === this.runtime.agentId).sort((a, b) => (b.createdAt ?? 0) - (a.createdAt ?? 0))[0];
2213
- reply = lastAssistant?.content?.text;
2214
- }
2215
- const durationMs = record.endedAt ? record.endedAt - (record.startedAt ?? record.createdAt) : undefined;
2216
- const statsLine = `Runtime: ${formatDurationShort(durationMs) ?? "n/a"} • Session: ${record.childSessionKey}`;
2217
- const outcome = record.outcome ?? { status: "unknown" };
2218
- const statusLabel = outcome.status === "ok" ? "completed successfully" : outcome.status === "timeout" ? "timed out" : outcome.status === "error" ? `failed: ${outcome.error || "unknown error"}` : "finished with unknown status";
2219
- const taskLabel = record.label || record.task || "background task";
2220
- const triggerMessage = [
2221
- `A background task "${taskLabel}" just ${statusLabel}.`,
2222
- "",
2223
- "Findings:",
2224
- reply || "(no output)",
2225
- "",
2226
- statsLine,
2227
- "",
2228
- "Summarize this naturally for the user. Keep it brief (1-2 sentences).",
2229
- "Do not mention technical details like tokens, stats, or that this was a background task.",
2230
- "You can respond with NO_REPLY if no announcement is needed."
2231
- ].join(`
2232
- `);
2233
- if (record.requesterSessionKey && record.requesterSessionKey !== "unknown") {
2234
- const requesterRoomId = sessionKeyToRoomId(record.requesterSessionKey, extractAgentIdFromSessionKey(record.requesterSessionKey));
2235
- const announceMessage = {
2236
- id: hashToUUID(`${runId}-announce`),
2342
+ return {
2343
+ success: result.success,
2344
+ ...result.messageId !== undefined ? { messageId: result.messageId } : {},
2345
+ channel: "twitch",
2346
+ targetId: params.target.to,
2347
+ sentAt: Date.now()
2348
+ };
2349
+ } catch (error) {
2350
+ return {
2351
+ success: false,
2352
+ channel: "twitch",
2353
+ targetId: params.target.to,
2354
+ error: error instanceof Error ? error.message : String(error)
2355
+ };
2356
+ }
2357
+ }
2358
+ async sendViaInternal(params) {
2359
+ const roomId = params.target.to;
2360
+ const messageId = crypto2.randomUUID();
2361
+ try {
2362
+ const memory = {
2363
+ id: messageId,
2237
2364
  entityId: this.runtime.agentId,
2238
2365
  agentId: this.runtime.agentId,
2239
- roomId: requesterRoomId,
2366
+ roomId,
2240
2367
  content: {
2241
- text: triggerMessage,
2368
+ text: params.content.text,
2242
2369
  type: "text",
2370
+ source: "internal",
2243
2371
  metadata: {
2244
- isSubagentAnnouncement: true,
2245
- subagentRunId: runId,
2246
- deliveryContext: record.requesterOrigin
2372
+ isInternalMessage: true,
2373
+ idempotencyKey: params.idempotencyKey
2247
2374
  }
2248
- }
2375
+ },
2376
+ createdAt: Date.now()
2249
2377
  };
2250
2378
  await this.runtime.emitEvent(EventType.MESSAGE_RECEIVED, {
2251
2379
  runtime: this.runtime,
2252
- message: announceMessage,
2253
- source: "subagent_announce"
2254
- });
2255
- }
2256
- this.emitSubagentEvent("SUBAGENT_ANNOUNCE_SENT", {
2257
- runId,
2258
- childSessionKey: record.childSessionKey,
2259
- requesterSessionKey: record.requesterSessionKey,
2260
- task: record.task,
2261
- outcome
2262
- });
2263
- record.cleanupCompletedAt = Date.now();
2264
- if (record.cleanup === "delete") {
2265
- this.subagentRuns.delete(runId);
2266
- }
2267
- return true;
2268
- }
2269
- buildSubagentSystemPrompt(params) {
2270
- const taskText = typeof params.task === "string" && params.task.trim() ? params.task.replace(/\s+/g, " ").trim() : "{{TASK_DESCRIPTION}}";
2271
- const lines = [
2272
- "# Subagent Context",
2273
- "",
2274
- "You are a **subagent** spawned by the main agent for a specific task.",
2275
- "",
2276
- "## Your Role",
2277
- `- You were created to handle: ${taskText}`,
2278
- "- Complete this task. That's your entire purpose.",
2279
- "- You are NOT the main agent. Don't try to be.",
2280
- "",
2281
- "## Rules",
2282
- "1. **Stay focused** - Do your assigned task, nothing else",
2283
- "2. **Complete the task** - Your final message will be automatically reported to the main agent",
2284
- "3. **Don't initiate** - No heartbeats, no proactive actions, no side quests",
2285
- "4. **Be ephemeral** - You may be terminated after task completion. That's fine.",
2286
- "",
2287
- "## Output Format",
2288
- "When complete, your final response should include:",
2289
- "- What you accomplished or found",
2290
- "- Any relevant details the main agent should know",
2291
- "- Keep it concise but informative",
2292
- "",
2293
- "## What You DON'T Do",
2294
- "- NO user conversations (that's main agent's job)",
2295
- "- NO external messages unless explicitly tasked with a specific recipient",
2296
- "- NO cron jobs or persistent state",
2297
- "- NO pretending to be the main agent",
2298
- "",
2299
- "## Session Context",
2300
- params.label ? `- Label: ${params.label}` : undefined,
2301
- params.requesterSessionKey ? `- Requester session: ${params.requesterSessionKey}` : undefined,
2302
- params.requesterOrigin?.channel ? `- Requester channel: ${params.requesterOrigin.channel}` : undefined,
2303
- `- Your session: ${params.childSessionKey}`,
2304
- ""
2305
- ].filter((line) => line !== undefined);
2306
- return lines.join(`
2307
- `);
2308
- }
2309
- async sendToAgent(params, requesterContext) {
2310
- const runId = crypto2.randomUUID();
2311
- const policy = this.getAgentToAgentPolicy();
2312
- let targetSessionKey = params.sessionKey;
2313
- if (!targetSessionKey && params.label) {
2314
- const matchingRun = this.findSubagentRunByLabel(params.label, params.agentId);
2315
- if (matchingRun) {
2316
- targetSessionKey = matchingRun.childSessionKey;
2317
- } else {
2318
- return {
2319
- status: "error",
2320
- runId,
2321
- error: `No subagent found with label "${params.label}"`
2322
- };
2323
- }
2324
- }
2325
- if (!targetSessionKey) {
2326
- return {
2327
- status: "error",
2328
- runId,
2329
- error: "Either sessionKey or label is required"
2330
- };
2331
- }
2332
- const requesterAgentId = requesterContext.sessionKey ? extractAgentIdFromSessionKey(requesterContext.sessionKey) : this.runtime.character?.name ?? "unknown";
2333
- const targetAgentId = extractAgentIdFromSessionKey(targetSessionKey);
2334
- if (requesterAgentId !== targetAgentId) {
2335
- if (!policy.enabled) {
2336
- return {
2337
- status: "forbidden",
2338
- runId,
2339
- error: "Agent-to-agent messaging is disabled. Set settings.agentToAgent.enabled=true to allow cross-agent sends."
2340
- };
2341
- }
2342
- if (!policy.isAllowed(requesterAgentId, targetAgentId)) {
2343
- return {
2344
- status: "forbidden",
2345
- runId,
2346
- error: "Agent-to-agent messaging denied by policy."
2347
- };
2348
- }
2349
- }
2350
- const targetRoomId = sessionKeyToRoomId(targetSessionKey, targetAgentId);
2351
- const contextMessage = this.buildAgentToAgentContext({
2352
- requesterSessionKey: requesterContext.sessionKey,
2353
- targetSessionKey
2354
- });
2355
- const message = {
2356
- id: hashToUUID(`${runId}-a2a`),
2357
- entityId: this.runtime.agentId,
2358
- agentId: this.runtime.agentId,
2359
- roomId: targetRoomId,
2360
- content: {
2361
- text: params.message,
2362
- type: "text",
2363
- metadata: {
2364
- isAgentToAgent: true,
2365
- runId,
2366
- senderSessionKey: requesterContext.sessionKey,
2367
- senderRoomId: requesterContext.roomId,
2368
- systemPromptOverride: contextMessage
2369
- }
2370
- }
2371
- };
2372
- const timeoutMs = (params.timeoutSeconds ?? 30) * 1000;
2373
- if (params.timeoutSeconds === 0) {
2374
- this.runtime.emitEvent(EventType.MESSAGE_RECEIVED, {
2375
- runtime: this.runtime,
2376
- message,
2377
- source: "a2a"
2378
- }).catch((err) => {
2379
- this.runtime.logger.error("A2A send error", { runId, error: err });
2380
- });
2381
- this.emitSubagentEvent("A2A_MESSAGE_SENT", {
2382
- runId,
2383
- childSessionKey: targetSessionKey,
2384
- requesterSessionKey: requesterContext.sessionKey,
2385
- task: params.message
2380
+ message: memory,
2381
+ source: "internal_messaging"
2386
2382
  });
2387
2383
  return {
2388
- status: "accepted",
2389
- runId,
2390
- sessionKey: targetSessionKey,
2391
- delivery: { status: "pending", mode: "async" }
2384
+ success: true,
2385
+ messageId,
2386
+ channel: "internal",
2387
+ targetId: params.target.to,
2388
+ sentAt: Date.now()
2392
2389
  };
2393
- }
2394
- const sentAt = Date.now();
2395
- await this.runtime.emitEvent(EventType.MESSAGE_RECEIVED, {
2396
- runtime: this.runtime,
2397
- message,
2398
- source: "a2a"
2399
- });
2400
- const pollIntervalMs = 500;
2401
- const maxPolls = Math.ceil(timeoutMs / pollIntervalMs);
2402
- let lastReply;
2403
- for (let poll = 0;poll < maxPolls; poll++) {
2404
- await new Promise((r) => setTimeout(r, pollIntervalMs));
2405
- const memories = await this.runtime.getMemories({
2406
- roomId: targetRoomId,
2407
- count: 10
2408
- });
2409
- const newReplies = memories.filter((m) => m.entityId === this.runtime.agentId && m.id !== message.id && m.createdAt && m.createdAt > sentAt);
2410
- if (newReplies.length > 0) {
2411
- lastReply = newReplies.sort((a, b) => (b.createdAt ?? 0) - (a.createdAt ?? 0))[0];
2412
- break;
2413
- }
2414
- }
2415
- this.emitSubagentEvent("A2A_MESSAGE_SENT", {
2416
- runId,
2417
- childSessionKey: targetSessionKey,
2418
- requesterSessionKey: requesterContext.sessionKey,
2419
- task: params.message
2420
- });
2421
- if (!lastReply) {
2390
+ } catch (error) {
2422
2391
  return {
2423
- status: "timeout",
2424
- runId,
2425
- sessionKey: targetSessionKey,
2426
- error: `No response received within ${params.timeoutSeconds ?? 30} seconds`,
2427
- delivery: { status: "timeout", mode: "sync" }
2392
+ success: false,
2393
+ channel: "internal",
2394
+ targetId: params.target.to,
2395
+ error: error instanceof Error ? error.message : String(error)
2428
2396
  };
2429
2397
  }
2430
- return {
2431
- status: "ok",
2432
- runId,
2433
- sessionKey: targetSessionKey,
2434
- reply: lastReply.content?.text,
2435
- delivery: { status: "delivered", mode: "sync" }
2436
- };
2437
- }
2438
- buildAgentToAgentContext(params) {
2439
- return [
2440
- "# Agent-to-Agent Message Context",
2441
- "",
2442
- "This message was sent by another agent session.",
2443
- params.requesterSessionKey ? `- Sender: ${params.requesterSessionKey}` : undefined,
2444
- `- Target: ${params.targetSessionKey}`,
2445
- "",
2446
- "Process this message and respond appropriately."
2447
- ].filter((l) => l !== undefined).join(`
2448
- `);
2449
- }
2450
- getSubagentRun(runId) {
2451
- return this.subagentRuns.get(runId);
2452
2398
  }
2453
- findSubagentRunByLabel(label, agentId) {
2454
- const normalizedLabel = label.toLowerCase().trim();
2455
- const normalizedAgentId = agentId ? normalizeAgentId2(agentId) : undefined;
2456
- for (const run of this.subagentRuns.values()) {
2457
- const runLabel = run.label?.toLowerCase().trim();
2458
- if (runLabel !== normalizedLabel) {
2459
- continue;
2460
- }
2461
- if (normalizedAgentId) {
2462
- const runAgentId = extractAgentIdFromSessionKey(run.childSessionKey);
2463
- if (normalizeAgentId2(runAgentId) !== normalizedAgentId) {
2464
- continue;
2465
- }
2466
- }
2467
- if (!run.endedAt) {
2468
- return run;
2469
- }
2399
+ normalizeChannel(channel) {
2400
+ if (!channel) {
2401
+ return "unknown";
2470
2402
  }
2471
- const completedRuns = [...this.subagentRuns.values()].filter((run) => {
2472
- const runLabel = run.label?.toLowerCase().trim();
2473
- if (runLabel !== normalizedLabel) {
2474
- return false;
2475
- }
2476
- if (normalizedAgentId) {
2477
- const runAgentId = extractAgentIdFromSessionKey(run.childSessionKey);
2478
- if (normalizeAgentId2(runAgentId) !== normalizedAgentId) {
2479
- return false;
2480
- }
2481
- }
2482
- return true;
2483
- }).sort((a, b) => (b.createdAt ?? 0) - (a.createdAt ?? 0));
2484
- return completedRuns[0];
2485
- }
2486
- listSubagentRuns(requesterSessionKey) {
2487
- const runs = [...this.subagentRuns.values()];
2488
- if (!requesterSessionKey) {
2489
- return runs;
2403
+ const lower = channel.toLowerCase();
2404
+ if (lower === "discord" || lower.includes("discord")) {
2405
+ return "discord";
2406
+ }
2407
+ if (lower === "telegram" || lower.includes("telegram")) {
2408
+ return "telegram";
2490
2409
  }
2491
- return runs.filter((r) => r.requesterSessionKey === requesterSessionKey);
2492
- }
2493
- cancelSubagentRun(runId) {
2494
- const controller = this.activeRuns.get(runId);
2495
- if (controller) {
2496
- controller.abort();
2497
- return true;
2410
+ if (lower === "slack" || lower.includes("slack")) {
2411
+ return "slack";
2498
2412
  }
2499
- return false;
2413
+ if (lower === "whatsapp" || lower.includes("whatsapp")) {
2414
+ return "whatsapp";
2415
+ }
2416
+ if (lower === "twitch" || lower.includes("twitch")) {
2417
+ return "twitch";
2418
+ }
2419
+ if (lower === "google_chat" || lower.includes("google") || lower.includes("gchat")) {
2420
+ return "google_chat";
2421
+ }
2422
+ if (lower === "internal" || lower === "a2a") {
2423
+ return "internal";
2424
+ }
2425
+ return "unknown";
2500
2426
  }
2501
2427
  on(event, handler) {
2502
2428
  this.emitter.on(event, handler);
@@ -2504,46 +2430,22 @@ class SubagentService extends Service2 {
2504
2430
  off(event, handler) {
2505
2431
  this.emitter.off(event, handler);
2506
2432
  }
2507
- emitSubagentEvent(type, payload) {
2433
+ emitMessagingEvent(type, payload) {
2508
2434
  this.emitter.emit(type, payload);
2509
- this.emitter.emit("task", { type, ...payload });
2510
- }
2511
- startSweeper() {
2512
- if (this.sweeper) {
2513
- return;
2514
- }
2515
- this.sweeper = setInterval(() => {
2516
- this.sweepOldRuns();
2517
- }, 60000);
2518
- this.sweeper.unref?.();
2519
- }
2520
- sweepOldRuns() {
2521
- const now2 = Date.now();
2522
- for (const [runId, record] of this.subagentRuns.entries()) {
2523
- if (record.archiveAtMs && record.archiveAtMs <= now2) {
2524
- this.subagentRuns.delete(runId);
2525
- }
2526
- }
2435
+ this.emitter.emit("messaging", { type, ...payload });
2527
2436
  }
2528
2437
  async stop() {
2529
- if (this.sweeper) {
2530
- clearInterval(this.sweeper);
2531
- this.sweeper = null;
2532
- }
2533
- for (const controller of this.activeRuns.values()) {
2534
- controller.abort();
2535
- }
2536
- this.activeRuns.clear();
2438
+ this.pendingDeliveries.clear();
2537
2439
  this.emitter.removeAllListeners();
2538
2440
  }
2539
2441
  }
2540
2442
 
2541
2443
  // src/services/sandbox-service.ts
2542
- import { EventEmitter as EventEmitter3 } from "node:events";
2543
2444
  import { spawn } from "node:child_process";
2445
+ import { EventEmitter as EventEmitter3 } from "node:events";
2544
2446
  import fs from "node:fs/promises";
2545
- import path from "node:path";
2546
2447
  import os from "node:os";
2448
+ import path from "node:path";
2547
2449
  import { Service as Service3 } from "@elizaos/core";
2548
2450
  var DEFAULT_DOCKER_CONFIG = {
2549
2451
  image: "ubuntu:22.04",
@@ -2684,7 +2586,7 @@ class SandboxService extends Service3 {
2684
2586
  this.contexts.set(trimmedKey, context);
2685
2587
  this.emitSandboxEvent("SANDBOX_CREATED", {
2686
2588
  sessionKey: trimmedKey,
2687
- roomId: context.roomId,
2589
+ ...context.roomId !== undefined ? { roomId: context.roomId } : {},
2688
2590
  containerName
2689
2591
  });
2690
2592
  return context;
@@ -2716,7 +2618,7 @@ class SandboxService extends Service3 {
2716
2618
  this.contexts.delete(sessionKey);
2717
2619
  this.emitSandboxEvent("SANDBOX_DESTROYED", {
2718
2620
  sessionKey,
2719
- roomId: context.roomId,
2621
+ ...context.roomId !== undefined ? { roomId: context.roomId } : {},
2720
2622
  containerName: context.containerName
2721
2623
  });
2722
2624
  }
@@ -2728,7 +2630,7 @@ class SandboxService extends Service3 {
2728
2630
  const startTime = Date.now();
2729
2631
  this.emitSandboxEvent("SANDBOX_COMMAND_STARTED", {
2730
2632
  sessionKey,
2731
- roomId: context.roomId,
2633
+ ...context.roomId !== undefined ? { roomId: context.roomId } : {},
2732
2634
  containerName: context.containerName,
2733
2635
  command: params.command
2734
2636
  });
@@ -2736,7 +2638,7 @@ class SandboxService extends Service3 {
2736
2638
  const result = await this.executeInContainer(context, params);
2737
2639
  this.emitSandboxEvent("SANDBOX_COMMAND_COMPLETED", {
2738
2640
  sessionKey,
2739
- roomId: context.roomId,
2641
+ ...context.roomId !== undefined ? { roomId: context.roomId } : {},
2740
2642
  containerName: context.containerName,
2741
2643
  command: params.command,
2742
2644
  result
@@ -2755,7 +2657,7 @@ class SandboxService extends Service3 {
2755
2657
  };
2756
2658
  this.emitSandboxEvent("SANDBOX_COMMAND_FAILED", {
2757
2659
  sessionKey,
2758
- roomId: context.roomId,
2660
+ ...context.roomId !== undefined ? { roomId: context.roomId } : {},
2759
2661
  containerName: context.containerName,
2760
2662
  command: params.command,
2761
2663
  result,
@@ -2792,15 +2694,17 @@ class SandboxService extends Service3 {
2792
2694
  }
2793
2695
  child.on("close", (code) => {
2794
2696
  clearTimeout(timeoutId);
2795
- resolve({
2697
+ const result = {
2796
2698
  success: code === 0 && !timedOut,
2797
2699
  exitCode: code ?? 1,
2798
2700
  stdout: stdout.slice(0, 1e5),
2799
2701
  stderr: stderr.slice(0, 1e5),
2800
2702
  durationMs: Date.now() - startTime,
2801
- timedOut,
2802
- error: timedOut ? "Command timed out" : undefined
2803
- });
2703
+ timedOut
2704
+ };
2705
+ if (timedOut)
2706
+ result.error = "Command timed out";
2707
+ resolve(result);
2804
2708
  });
2805
2709
  child.on("error", (error) => {
2806
2710
  clearTimeout(timeoutId);
@@ -2838,15 +2742,17 @@ class SandboxService extends Service3 {
2838
2742
  });
2839
2743
  child.on("close", (code) => {
2840
2744
  clearTimeout(timeoutId);
2841
- resolve({
2745
+ const result = {
2842
2746
  success: code === 0 && !timedOut,
2843
2747
  exitCode: code ?? 1,
2844
2748
  stdout: stdout.slice(0, 1e5),
2845
2749
  stderr: stderr.slice(0, 1e5),
2846
2750
  durationMs: Date.now() - startTime,
2847
- timedOut,
2848
- error: timedOut ? "Command timed out" : undefined
2849
- });
2751
+ timedOut
2752
+ };
2753
+ if (timedOut)
2754
+ result.error = "Command timed out";
2755
+ resolve(result);
2850
2756
  });
2851
2757
  child.on("error", (error) => {
2852
2758
  clearTimeout(timeoutId);
@@ -2897,15 +2803,17 @@ class SandboxService extends Service3 {
2897
2803
  }
2898
2804
  child.on("close", (code) => {
2899
2805
  clearTimeout(timeoutId);
2900
- resolve({
2806
+ const result = {
2901
2807
  success: code === 0 && !timedOut,
2902
2808
  exitCode: code ?? 1,
2903
2809
  stdout: stdout.slice(0, 1e5),
2904
2810
  stderr: stderr.slice(0, 1e5),
2905
2811
  durationMs: Date.now() - startTime,
2906
- timedOut,
2907
- error: timedOut ? "Command timed out" : undefined
2908
- });
2812
+ timedOut
2813
+ };
2814
+ if (timedOut)
2815
+ result.error = "Command timed out";
2816
+ resolve(result);
2909
2817
  });
2910
2818
  child.on("error", (error) => {
2911
2819
  clearTimeout(timeoutId);
@@ -2950,10 +2858,7 @@ class SandboxService extends Service3 {
2950
2858
  `name=^${context.containerName}$`
2951
2859
  ]);
2952
2860
  if (checkResult.stdout.trim()) {
2953
- const startResult = await this.executeDockerCommand([
2954
- "start",
2955
- context.containerName
2956
- ]);
2861
+ const startResult = await this.executeDockerCommand(["start", context.containerName]);
2957
2862
  if (startResult.success) {
2958
2863
  this.activeContainers.add(context.containerName);
2959
2864
  return;
@@ -3020,21 +2925,21 @@ class SandboxService extends Service3 {
3020
2925
  browserArgs.push(config.browser.image);
3021
2926
  const result = await this.executeDockerCommand(browserArgs);
3022
2927
  if (!result.success) {
3023
- this.runtime.logger.error("Failed to start browser container", {
2928
+ this.runtime.logger.error({
3024
2929
  sessionKey,
3025
2930
  error: result.stderr
3026
- });
2931
+ }, "Failed to start browser container");
3027
2932
  return null;
3028
2933
  }
3029
2934
  const browserContext = {
3030
2935
  bridgeUrl: `http://localhost:${config.browser.cdpPort}`,
3031
- noVncUrl: config.browser.enableNoVnc ? `http://localhost:${config.browser.noVncPort}` : undefined,
2936
+ ...config.browser.enableNoVnc ? { noVncUrl: `http://localhost:${config.browser.noVncPort}` } : {},
3032
2937
  containerName: browserContainerName
3033
2938
  };
3034
2939
  context.browser = browserContext;
3035
2940
  this.emitSandboxEvent("SANDBOX_BROWSER_STARTED", {
3036
2941
  sessionKey,
3037
- roomId: context.roomId,
2942
+ ...context.roomId !== undefined ? { roomId: context.roomId } : {},
3038
2943
  containerName: browserContainerName
3039
2944
  });
3040
2945
  return browserContext;
@@ -3047,10 +2952,10 @@ class SandboxService extends Service3 {
3047
2952
  await this.stopContainer(context.browser.containerName);
3048
2953
  this.emitSandboxEvent("SANDBOX_BROWSER_STOPPED", {
3049
2954
  sessionKey,
3050
- roomId: context.roomId,
2955
+ ...context.roomId !== undefined ? { roomId: context.roomId } : {},
3051
2956
  containerName: context.browser.containerName
3052
2957
  });
3053
- context.browser = undefined;
2958
+ delete context.browser;
3054
2959
  }
3055
2960
  startSweeper() {
3056
2961
  if (this.sweeper) {
@@ -3073,10 +2978,10 @@ class SandboxService extends Service3 {
3073
2978
  const tooOld = now2 - context.createdAt > maxAgeMs;
3074
2979
  if (idle || tooOld) {
3075
2980
  this.destroySandbox(sessionKey).catch((err) => {
3076
- this.runtime.logger.error("Failed to destroy idle sandbox", {
2981
+ this.runtime.logger.error({
3077
2982
  sessionKey,
3078
- error: err
3079
- });
2983
+ error: err instanceof Error ? err.message : String(err)
2984
+ }, "Failed to destroy idle sandbox");
3080
2985
  });
3081
2986
  }
3082
2987
  }
@@ -3103,19 +3008,24 @@ class SandboxService extends Service3 {
3103
3008
  }
3104
3009
  }
3105
3010
 
3106
- // src/services/messaging-service.ts
3107
- import { EventEmitter as EventEmitter4 } from "node:events";
3011
+ // src/services/subagent-service.ts
3108
3012
  import crypto3 from "node:crypto";
3109
- import { Service as Service4, EventType as EventType2 } from "@elizaos/core";
3110
- class MessagingService extends Service4 {
3111
- static serviceType = "MESSAGING";
3112
- capabilityDescription = "Unified cross-platform messaging for sending messages to any supported channel";
3013
+ import { EventEmitter as EventEmitter4 } from "node:events";
3014
+ import {
3015
+ ChannelType,
3016
+ EventType as EventType2,
3017
+ Service as Service4
3018
+ } from "@elizaos/core";
3019
+ class SubagentService extends Service4 {
3020
+ static serviceType = "SUBAGENT";
3021
+ capabilityDescription = "Manages subagent spawning, lifecycle, and communication";
3113
3022
  emitter = new EventEmitter4;
3114
- adapters = new Map;
3115
- pendingDeliveries = new Map;
3023
+ subagentRuns = new Map;
3024
+ activeRuns = new Map;
3025
+ sweeper = null;
3116
3026
  initialized = false;
3117
3027
  static async start(runtime) {
3118
- const service = new MessagingService(runtime);
3028
+ const service = new SubagentService(runtime);
3119
3029
  await service.initialize();
3120
3030
  return service;
3121
3031
  }
@@ -3124,472 +3034,630 @@ class MessagingService extends Service4 {
3124
3034
  return;
3125
3035
  }
3126
3036
  this.initialized = true;
3127
- this.registerBuiltInAdapters();
3128
- }
3129
- registerBuiltInAdapters() {
3130
- this.registerAdapter({
3131
- channel: "discord",
3132
- isAvailable: () => {
3133
- const service = this.runtime.getService("DISCORD");
3134
- return !!service;
3135
- },
3136
- send: async (params) => this.sendViaDiscord(params)
3137
- });
3138
- this.registerAdapter({
3139
- channel: "telegram",
3140
- isAvailable: () => {
3141
- const service = this.runtime.getService("TELEGRAM");
3142
- return !!service;
3143
- },
3144
- send: async (params) => this.sendViaTelegram(params)
3145
- });
3146
- this.registerAdapter({
3147
- channel: "slack",
3148
- isAvailable: () => {
3149
- const service = this.runtime.getService("slack");
3150
- return !!service;
3151
- },
3152
- send: async (params) => this.sendViaSlack(params)
3153
- });
3154
- this.registerAdapter({
3155
- channel: "whatsapp",
3156
- isAvailable: () => {
3157
- const service = this.runtime.getService("whatsapp");
3158
- return !!service;
3159
- },
3160
- send: async (params) => this.sendViaWhatsApp(params)
3161
- });
3162
- this.registerAdapter({
3163
- channel: "twitch",
3164
- isAvailable: () => {
3165
- const service = this.runtime.getService("twitch");
3166
- return !!service;
3167
- },
3168
- send: async (params) => this.sendViaTwitch(params)
3037
+ this.runtime.registerEvent(EventType2.RUN_ENDED, async (payload) => {
3038
+ await this.handleRunEnded(payload);
3169
3039
  });
3170
- this.registerAdapter({
3171
- channel: "internal",
3172
- isAvailable: () => true,
3173
- send: async (params) => this.sendViaInternal(params)
3040
+ this.runtime.registerEvent(EventType2.RUN_TIMEOUT, async (payload) => {
3041
+ await this.handleRunTimeout(payload);
3174
3042
  });
3043
+ this.startSweeper();
3175
3044
  }
3176
- registerAdapter(adapter) {
3177
- this.adapters.set(adapter.channel, adapter);
3045
+ getConfig() {
3046
+ const settings = this.runtime.character?.settings;
3047
+ const subagents = settings?.subagents ?? {};
3048
+ return {
3049
+ enabled: subagents.enabled !== false,
3050
+ ...subagents.model !== undefined ? { model: subagents.model } : {},
3051
+ ...subagents.thinking !== undefined ? { thinking: subagents.thinking } : {},
3052
+ timeoutSeconds: subagents.timeoutSeconds ?? 300,
3053
+ allowAgents: subagents.allowAgents ?? [],
3054
+ archiveAfterMinutes: subagents.archiveAfterMinutes ?? 60
3055
+ };
3178
3056
  }
3179
- getAdapter(channel) {
3180
- return this.adapters.get(channel);
3057
+ getAgentToAgentPolicy() {
3058
+ const settings = this.runtime.character?.settings;
3059
+ const a2aConfig = settings?.agentToAgent ?? {};
3060
+ const enabled = a2aConfig.enabled === true;
3061
+ const allowRules = (a2aConfig.allow ?? []).map((rule) => ({
3062
+ source: rule.source ?? "*",
3063
+ target: rule.target ?? "*"
3064
+ }));
3065
+ return {
3066
+ enabled,
3067
+ allowRules,
3068
+ isAllowed: (sourceAgentId, targetAgentId) => {
3069
+ if (!enabled) {
3070
+ return false;
3071
+ }
3072
+ if (sourceAgentId === targetAgentId) {
3073
+ return true;
3074
+ }
3075
+ const sourceNorm = normalizeAgentId2(sourceAgentId);
3076
+ const targetNorm = normalizeAgentId2(targetAgentId);
3077
+ for (const rule of allowRules) {
3078
+ const sourceMatch = rule.source === "*" || normalizeAgentId2(rule.source) === sourceNorm;
3079
+ const targetMatch = rule.target === "*" || normalizeAgentId2(rule.target) === targetNorm;
3080
+ if (sourceMatch && targetMatch) {
3081
+ return true;
3082
+ }
3083
+ }
3084
+ return false;
3085
+ }
3086
+ };
3181
3087
  }
3182
- getAvailableChannels() {
3183
- const channels = [];
3184
- for (const [channel, adapter] of this.adapters) {
3185
- if (adapter.isAvailable()) {
3186
- channels.push(channel);
3088
+ async spawnSubagent(params, requesterContext) {
3089
+ const config = this.getConfig();
3090
+ if (!config.enabled) {
3091
+ return {
3092
+ status: "forbidden",
3093
+ error: "Subagent spawning is disabled"
3094
+ };
3095
+ }
3096
+ if (requesterContext.sessionKey && isSubagentSessionKey2(requesterContext.sessionKey)) {
3097
+ return {
3098
+ status: "forbidden",
3099
+ error: "sessions_spawn is not allowed from sub-agent sessions"
3100
+ };
3101
+ }
3102
+ const requesterAgentId = requesterContext.sessionKey ? extractAgentIdFromSessionKey(requesterContext.sessionKey) : this.runtime.character?.name ?? "unknown";
3103
+ const targetAgentId = params.agentId ? normalizeAgentId2(params.agentId) : requesterAgentId;
3104
+ if (targetAgentId !== requesterAgentId) {
3105
+ const allowAgents = config.allowAgents ?? [];
3106
+ const allowAny = allowAgents.some((v) => v.trim() === "*");
3107
+ const allowSet = new Set(allowAgents.filter((v) => v.trim() && v.trim() !== "*").map((v) => normalizeAgentId2(v)));
3108
+ if (!allowAny && !allowSet.has(targetAgentId)) {
3109
+ return {
3110
+ status: "forbidden",
3111
+ error: `agentId "${targetAgentId}" is not allowed for subagent spawning`
3112
+ };
3113
+ }
3114
+ }
3115
+ const childSessionKey = createSubagentSessionKey(targetAgentId);
3116
+ const runId = crypto3.randomUUID();
3117
+ const childRoomId = sessionKeyToRoomId(childSessionKey, targetAgentId);
3118
+ const roomMetadata = {
3119
+ isSubagent: true,
3120
+ sessionKey: childSessionKey,
3121
+ task: params.task,
3122
+ spawnedAt: Date.now(),
3123
+ cleanup: params.cleanup ?? "keep"
3124
+ };
3125
+ if (requesterContext.roomId)
3126
+ roomMetadata.parentRoomId = requesterContext.roomId;
3127
+ if (requesterContext.sessionKey)
3128
+ roomMetadata.parentSessionKey = requesterContext.sessionKey;
3129
+ if (params.label)
3130
+ roomMetadata.label = params.label;
3131
+ const childRoom = {
3132
+ id: childRoomId,
3133
+ name: params.label || `Subagent: ${params.task.slice(0, 50)}`,
3134
+ type: ChannelType.SELF,
3135
+ channelId: childSessionKey,
3136
+ agentId: this.runtime.agentId,
3137
+ worldId: this.runtime.agentId,
3138
+ metadata: roomMetadata
3139
+ };
3140
+ await this.runtime.ensureRoomExists(childRoom);
3141
+ const now2 = Date.now();
3142
+ const archiveAfterMs = config.archiveAfterMinutes ? config.archiveAfterMinutes * 60000 : undefined;
3143
+ const record = {
3144
+ runId,
3145
+ childSessionKey,
3146
+ requesterSessionKey: requesterContext.sessionKey ?? "unknown",
3147
+ requesterDisplayKey: requesterContext.sessionKey ?? "main",
3148
+ task: params.task,
3149
+ cleanup: params.cleanup ?? "keep",
3150
+ createdAt: now2,
3151
+ startedAt: now2,
3152
+ cleanupHandled: false,
3153
+ roomId: childRoomId,
3154
+ worldId: this.runtime.agentId
3155
+ };
3156
+ const normalizedOrigin = normalizeDeliveryContext(requesterContext.origin);
3157
+ if (normalizedOrigin)
3158
+ record.requesterOrigin = normalizedOrigin;
3159
+ if (params.label)
3160
+ record.label = params.label;
3161
+ if (archiveAfterMs)
3162
+ record.archiveAtMs = now2 + archiveAfterMs;
3163
+ this.subagentRuns.set(runId, record);
3164
+ const spawnPayload = {
3165
+ runId,
3166
+ childSessionKey,
3167
+ childRoomId,
3168
+ task: params.task
3169
+ };
3170
+ if (requesterContext.sessionKey)
3171
+ spawnPayload.requesterSessionKey = requesterContext.sessionKey;
3172
+ if (requesterContext.roomId)
3173
+ spawnPayload.requesterRoomId = requesterContext.roomId;
3174
+ if (params.label)
3175
+ spawnPayload.label = params.label;
3176
+ this.emitSubagentEvent("SUBAGENT_SPAWN_REQUESTED", spawnPayload);
3177
+ const systemPromptParams = {
3178
+ childSessionKey,
3179
+ task: params.task
3180
+ };
3181
+ if (requesterContext.sessionKey)
3182
+ systemPromptParams.requesterSessionKey = requesterContext.sessionKey;
3183
+ if (requesterContext.origin)
3184
+ systemPromptParams.requesterOrigin = requesterContext.origin;
3185
+ if (params.label)
3186
+ systemPromptParams.label = params.label;
3187
+ const systemPrompt = this.buildSubagentSystemPrompt(systemPromptParams);
3188
+ const taskMetadata = {
3189
+ isSubagentTask: true,
3190
+ runId,
3191
+ systemPromptOverride: systemPrompt
3192
+ };
3193
+ const modelOverride = params.model || config.model;
3194
+ const thinkingOverride = params.thinking || config.thinking;
3195
+ if (modelOverride)
3196
+ taskMetadata.modelOverride = modelOverride;
3197
+ if (thinkingOverride)
3198
+ taskMetadata.thinkingOverride = thinkingOverride;
3199
+ const initialMessage = {
3200
+ id: hashToUUID(`${runId}-initial`),
3201
+ entityId: this.runtime.agentId,
3202
+ agentId: this.runtime.agentId,
3203
+ roomId: childRoomId,
3204
+ content: {
3205
+ text: params.task,
3206
+ type: "text",
3207
+ metadata: taskMetadata
3187
3208
  }
3188
- }
3189
- return channels;
3190
- }
3191
- async send(params) {
3192
- const idempotencyKey = params.idempotencyKey ?? crypto3.randomUUID();
3193
- const channel = params.target.channel;
3194
- const existing = this.pendingDeliveries.get(idempotencyKey);
3195
- if (existing) {
3196
- return {
3197
- success: existing.status.status === "sent" || existing.status.status === "delivered",
3198
- messageId: existing.status.messageId,
3199
- channel,
3200
- targetId: params.target.to,
3201
- error: existing.status.error,
3202
- sentAt: existing.status.status === "sent" ? existing.status.updatedAt : undefined
3203
- };
3204
- }
3205
- const status = {
3206
- status: "pending",
3207
- updatedAt: Date.now()
3208
3209
  };
3209
- this.pendingDeliveries.set(idempotencyKey, { params, status });
3210
- this.emitMessagingEvent("MESSAGING_SEND_REQUESTED", {
3211
- idempotencyKey,
3212
- channel,
3213
- targetId: params.target.to,
3214
- status: "pending"
3210
+ this.executeSubagentRun(runId, initialMessage, params.runTimeoutSeconds).catch((error) => {
3211
+ this.runtime.logger.error(`Subagent execution error [runId=${runId}]: ${error}`);
3212
+ this.handleSubagentError(runId, error);
3215
3213
  });
3216
- const adapter = this.adapters.get(channel);
3217
- if (!adapter) {
3218
- const result = {
3219
- success: false,
3220
- channel,
3221
- targetId: params.target.to,
3222
- error: `No adapter registered for channel: ${channel}`
3223
- };
3224
- status.status = "failed";
3225
- status.error = result.error;
3226
- status.updatedAt = Date.now();
3227
- this.emitMessagingEvent("MESSAGING_SEND_FAILED", {
3228
- idempotencyKey,
3229
- channel,
3230
- targetId: params.target.to,
3231
- status: "failed",
3232
- error: result.error
3233
- });
3234
- return result;
3235
- }
3236
- if (!adapter.isAvailable()) {
3237
- const result = {
3238
- success: false,
3239
- channel,
3240
- targetId: params.target.to,
3241
- error: `${channel} service is not available`
3242
- };
3243
- status.status = "failed";
3244
- status.error = result.error;
3245
- status.updatedAt = Date.now();
3246
- this.emitMessagingEvent("MESSAGING_SEND_FAILED", {
3247
- idempotencyKey,
3248
- channel,
3249
- targetId: params.target.to,
3250
- status: "failed",
3251
- error: result.error
3214
+ return {
3215
+ status: "accepted",
3216
+ childSessionKey,
3217
+ childRoomId,
3218
+ runId,
3219
+ modelApplied: !!(params.model || config.model)
3220
+ };
3221
+ }
3222
+ async executeSubagentRun(runId, initialMessage, timeoutSeconds) {
3223
+ const config = this.getConfig();
3224
+ const timeout = (timeoutSeconds ?? config.timeoutSeconds ?? 300) * 1000;
3225
+ const controller = new AbortController;
3226
+ this.activeRuns.set(runId, controller);
3227
+ const timeoutId = timeout > 0 ? setTimeout(() => {
3228
+ controller.abort();
3229
+ this.handleSubagentTimeout(runId);
3230
+ }, timeout) : null;
3231
+ try {
3232
+ await this.runtime.emitEvent(EventType2.MESSAGE_RECEIVED, {
3233
+ runtime: this.runtime,
3234
+ message: initialMessage,
3235
+ source: "subagent"
3252
3236
  });
3253
- return result;
3237
+ await this.waitForCompletion(runId, timeout, controller.signal);
3238
+ } finally {
3239
+ if (timeoutId) {
3240
+ clearTimeout(timeoutId);
3241
+ }
3242
+ this.activeRuns.delete(runId);
3254
3243
  }
3255
- try {
3256
- const result = await adapter.send({ ...params, idempotencyKey });
3257
- status.status = result.success ? "sent" : "failed";
3258
- status.messageId = result.messageId;
3259
- status.error = result.error;
3260
- status.updatedAt = Date.now();
3261
- if (result.success) {
3262
- this.emitMessagingEvent("MESSAGING_SENT", {
3263
- idempotencyKey,
3264
- messageId: result.messageId,
3265
- channel,
3266
- targetId: params.target.to,
3267
- status: "sent",
3268
- sentAt: result.sentAt
3269
- });
3270
- } else {
3271
- this.emitMessagingEvent("MESSAGING_SEND_FAILED", {
3272
- idempotencyKey,
3273
- channel,
3274
- targetId: params.target.to,
3275
- status: "failed",
3276
- error: result.error
3277
- });
3244
+ }
3245
+ async waitForCompletion(runId, timeoutMs, signal) {
3246
+ const startTime = Date.now();
3247
+ const pollInterval = 500;
3248
+ while (!signal.aborted) {
3249
+ const record = this.subagentRuns.get(runId);
3250
+ if (!record) {
3251
+ return;
3278
3252
  }
3279
- return result;
3280
- } catch (error) {
3281
- const errorMessage = error instanceof Error ? error.message : String(error);
3282
- status.status = "failed";
3283
- status.error = errorMessage;
3284
- status.updatedAt = Date.now();
3285
- this.emitMessagingEvent("MESSAGING_SEND_FAILED", {
3286
- idempotencyKey,
3287
- channel,
3288
- targetId: params.target.to,
3289
- status: "failed",
3290
- error: errorMessage
3291
- });
3292
- return {
3293
- success: false,
3294
- channel,
3295
- targetId: params.target.to,
3296
- error: errorMessage
3297
- };
3253
+ if (record.endedAt) {
3254
+ return;
3255
+ }
3256
+ if (Date.now() - startTime > timeoutMs + 5000) {
3257
+ return;
3258
+ }
3259
+ await new Promise((resolve) => setTimeout(resolve, pollInterval));
3298
3260
  }
3299
3261
  }
3300
- async sendToDeliveryContext(deliveryContext, content, options) {
3301
- const channel = this.normalizeChannel(deliveryContext.channel);
3302
- const to = deliveryContext.to ?? deliveryContext.accountId ?? "";
3303
- if (!to) {
3304
- return {
3305
- success: false,
3306
- channel,
3307
- targetId: "",
3308
- error: "No recipient specified in delivery context"
3309
- };
3262
+ handleSubagentTimeout(runId) {
3263
+ const record = this.subagentRuns.get(runId);
3264
+ if (!record || record.endedAt) {
3265
+ return;
3310
3266
  }
3311
- return this.send({
3312
- target: {
3313
- channel,
3314
- to,
3315
- accountId: deliveryContext.accountId,
3316
- threadId: deliveryContext.threadId
3317
- },
3318
- content,
3319
- idempotencyKey: options?.idempotencyKey,
3320
- timeoutMs: options?.timeoutMs
3267
+ record.endedAt = Date.now();
3268
+ record.outcome = { status: "timeout" };
3269
+ const timeoutPayload = {
3270
+ runId,
3271
+ childSessionKey: record.childSessionKey,
3272
+ task: record.task,
3273
+ status: "timeout"
3274
+ };
3275
+ if (record.roomId)
3276
+ timeoutPayload.childRoomId = record.roomId;
3277
+ this.emitSubagentEvent("SUBAGENT_RUN_TIMEOUT", timeoutPayload);
3278
+ this.announceSubagentResult(runId).catch((err) => {
3279
+ this.runtime.logger.error(`Failed to announce timeout [runId=${runId}]: ${err}`);
3321
3280
  });
3322
3281
  }
3323
- async sendToRoom(roomId, content, options) {
3324
- const room = await this.runtime.getRoom(roomId);
3325
- if (!room) {
3326
- return {
3327
- success: false,
3328
- channel: "unknown",
3329
- targetId: roomId,
3330
- error: `Room not found: ${roomId}`
3331
- };
3332
- }
3333
- const metadata = room.metadata;
3334
- const channel = this.normalizeChannel(metadata?.messagingChannel);
3335
- const to = metadata?.messagingTo ?? room.channelId ?? "";
3336
- if (!to) {
3337
- return {
3338
- success: false,
3339
- channel,
3340
- targetId: roomId,
3341
- error: "Room has no messaging target configured"
3342
- };
3282
+ handleSubagentError(runId, error) {
3283
+ const record = this.subagentRuns.get(runId);
3284
+ if (!record) {
3285
+ return;
3343
3286
  }
3344
- return this.send({
3345
- target: {
3346
- channel,
3347
- to,
3348
- accountId: metadata?.messagingAccountId,
3349
- threadId: metadata?.messagingThreadId
3350
- },
3351
- content,
3352
- idempotencyKey: options?.idempotencyKey,
3353
- timeoutMs: options?.timeoutMs
3287
+ record.endedAt = Date.now();
3288
+ record.outcome = {
3289
+ status: "error",
3290
+ error: error instanceof Error ? error.message : String(error)
3291
+ };
3292
+ const errorPayload = {
3293
+ runId,
3294
+ childSessionKey: record.childSessionKey,
3295
+ task: record.task,
3296
+ status: "error",
3297
+ error: record.outcome.error
3298
+ };
3299
+ if (record.roomId)
3300
+ errorPayload.childRoomId = record.roomId;
3301
+ this.emitSubagentEvent("SUBAGENT_RUN_FAILED", errorPayload);
3302
+ this.announceSubagentResult(runId).catch((err) => {
3303
+ this.runtime.logger.error("Failed to announce error", { runId, error: err });
3354
3304
  });
3355
3305
  }
3356
- async sendToSession(sessionKey, content, options) {
3357
- const agentId = extractAgentIdFromSessionKey(sessionKey);
3358
- const roomId = sessionKeyToRoomId(sessionKey, agentId);
3359
- return this.sendToRoom(roomId, content, options);
3360
- }
3361
- async sendViaDiscord(params) {
3362
- const discordService = this.runtime.getService("DISCORD");
3363
- if (!discordService?.client) {
3364
- return {
3365
- success: false,
3366
- channel: "discord",
3367
- targetId: params.target.to,
3368
- error: "Discord service not available"
3369
- };
3370
- }
3371
- try {
3372
- const channel = await discordService.client.channels.fetch(params.target.to);
3373
- if (!channel?.send) {
3374
- return {
3375
- success: false,
3376
- channel: "discord",
3377
- targetId: params.target.to,
3378
- error: "Channel not found or not a text channel"
3379
- };
3380
- }
3381
- const message = await channel.send({
3382
- content: params.content.text,
3383
- ...params.target.replyToMessageId ? { reply: { messageReference: params.target.replyToMessageId } } : {}
3384
- });
3385
- return {
3386
- success: true,
3387
- messageId: message.id,
3388
- channel: "discord",
3389
- targetId: params.target.to,
3390
- sentAt: Date.now()
3391
- };
3392
- } catch (error) {
3393
- return {
3394
- success: false,
3395
- channel: "discord",
3396
- targetId: params.target.to,
3397
- error: error instanceof Error ? error.message : String(error)
3398
- };
3306
+ async handleRunEnded(payload) {
3307
+ const p = payload;
3308
+ if (!p.roomId) {
3309
+ return;
3310
+ }
3311
+ for (const [runId, record] of this.subagentRuns.entries()) {
3312
+ if (record.roomId === p.roomId && !record.endedAt) {
3313
+ record.endedAt = Date.now();
3314
+ record.outcome = { status: "ok" };
3315
+ const completedPayload = {
3316
+ runId,
3317
+ childSessionKey: record.childSessionKey,
3318
+ childRoomId: record.roomId,
3319
+ task: record.task,
3320
+ status: "completed",
3321
+ endedAt: record.endedAt,
3322
+ durationMs: record.endedAt - (record.startedAt ?? record.createdAt)
3323
+ };
3324
+ if (record.startedAt)
3325
+ completedPayload.startedAt = record.startedAt;
3326
+ this.emitSubagentEvent("SUBAGENT_RUN_COMPLETED", completedPayload);
3327
+ await this.announceSubagentResult(runId);
3328
+ break;
3329
+ }
3399
3330
  }
3400
3331
  }
3401
- async sendViaTelegram(params) {
3402
- const telegramService = this.runtime.getService("TELEGRAM");
3403
- if (!telegramService?.bot?.telegram) {
3404
- return {
3405
- success: false,
3406
- channel: "telegram",
3407
- targetId: params.target.to,
3408
- error: "Telegram service not available"
3409
- };
3332
+ async handleRunTimeout(payload) {
3333
+ const p = payload;
3334
+ if (!p.roomId) {
3335
+ return;
3410
3336
  }
3411
- try {
3412
- const chatId = Number.isNaN(Number(params.target.to)) ? params.target.to : Number(params.target.to);
3413
- const result = await telegramService.bot.telegram.sendMessage(chatId, params.content.text, {
3414
- reply_to_message_id: params.target.replyToMessageId ? Number(params.target.replyToMessageId) : undefined,
3415
- disable_web_page_preview: params.content.disableLinkPreview,
3416
- disable_notification: params.content.silent
3417
- });
3418
- return {
3419
- success: true,
3420
- messageId: String(result.message_id),
3421
- channel: "telegram",
3422
- targetId: params.target.to,
3423
- sentAt: Date.now()
3424
- };
3425
- } catch (error) {
3426
- return {
3427
- success: false,
3428
- channel: "telegram",
3429
- targetId: params.target.to,
3430
- error: error instanceof Error ? error.message : String(error)
3431
- };
3337
+ for (const [runId, record] of this.subagentRuns.entries()) {
3338
+ if (record.roomId === p.roomId && !record.endedAt) {
3339
+ this.handleSubagentTimeout(runId);
3340
+ break;
3341
+ }
3432
3342
  }
3433
3343
  }
3434
- async sendViaSlack(params) {
3435
- const slackService = this.runtime.getService("slack");
3436
- if (!slackService?.sendMessage) {
3437
- return {
3438
- success: false,
3439
- channel: "slack",
3440
- targetId: params.target.to,
3441
- error: "Slack service not available or sendMessage method not found"
3442
- };
3344
+ async announceSubagentResult(runId) {
3345
+ const record = this.subagentRuns.get(runId);
3346
+ if (!record) {
3347
+ return false;
3443
3348
  }
3444
- try {
3445
- const result = await slackService.sendMessage(params.target.to, params.content.text, {
3446
- threadTs: params.target.threadId ? String(params.target.threadId) : undefined,
3447
- replyBroadcast: false
3349
+ if (record.cleanupCompletedAt || record.cleanupHandled) {
3350
+ return false;
3351
+ }
3352
+ record.cleanupHandled = true;
3353
+ let reply;
3354
+ if (record.roomId) {
3355
+ const memories = await this.runtime.getMemories({
3356
+ tableName: "messages",
3357
+ roomId: record.roomId,
3358
+ count: 10
3448
3359
  });
3449
- return {
3450
- success: true,
3451
- messageId: result.ts,
3452
- channel: "slack",
3453
- targetId: params.target.to,
3454
- sentAt: Date.now()
3360
+ const lastAssistant = memories.filter((m) => m.entityId === this.runtime.agentId).sort((a, b) => (b.createdAt ?? 0) - (a.createdAt ?? 0))[0];
3361
+ reply = lastAssistant?.content?.text;
3362
+ }
3363
+ const durationMs = record.endedAt ? record.endedAt - (record.startedAt ?? record.createdAt) : undefined;
3364
+ const statsLine = `Runtime: ${formatDurationShort(durationMs) ?? "n/a"} • Session: ${record.childSessionKey}`;
3365
+ const outcome = record.outcome ?? { status: "unknown" };
3366
+ const statusLabel = outcome.status === "ok" ? "completed successfully" : outcome.status === "timeout" ? "timed out" : outcome.status === "error" ? `failed: ${outcome.error || "unknown error"}` : "finished with unknown status";
3367
+ const taskLabel = record.label || record.task || "background task";
3368
+ const triggerMessage = [
3369
+ `A background task "${taskLabel}" just ${statusLabel}.`,
3370
+ "",
3371
+ "Findings:",
3372
+ reply || "(no output)",
3373
+ "",
3374
+ statsLine,
3375
+ "",
3376
+ "Summarize this naturally for the user. Keep it brief (1-2 sentences).",
3377
+ "Do not mention technical details like tokens, stats, or that this was a background task.",
3378
+ "You can respond with NO_REPLY if no announcement is needed."
3379
+ ].join(`
3380
+ `);
3381
+ if (record.requesterSessionKey && record.requesterSessionKey !== "unknown") {
3382
+ const requesterRoomId = sessionKeyToRoomId(record.requesterSessionKey, extractAgentIdFromSessionKey(record.requesterSessionKey));
3383
+ const metadata = {
3384
+ isSubagentAnnouncement: true,
3385
+ subagentRunId: runId
3455
3386
  };
3456
- } catch (error) {
3457
- return {
3458
- success: false,
3459
- channel: "slack",
3460
- targetId: params.target.to,
3461
- error: error instanceof Error ? error.message : String(error)
3387
+ if (record.requesterOrigin) {
3388
+ metadata.deliveryContext = record.requesterOrigin;
3389
+ }
3390
+ const announceMessage = {
3391
+ id: hashToUUID(`${runId}-announce`),
3392
+ entityId: this.runtime.agentId,
3393
+ agentId: this.runtime.agentId,
3394
+ roomId: requesterRoomId,
3395
+ content: {
3396
+ text: triggerMessage,
3397
+ type: "text",
3398
+ metadata
3399
+ }
3462
3400
  };
3401
+ await this.runtime.emitEvent(EventType2.MESSAGE_RECEIVED, {
3402
+ runtime: this.runtime,
3403
+ message: announceMessage,
3404
+ source: "subagent_announce"
3405
+ });
3406
+ }
3407
+ const announcePayload = {
3408
+ runId,
3409
+ childSessionKey: record.childSessionKey,
3410
+ task: record.task,
3411
+ outcome
3412
+ };
3413
+ if (record.requesterSessionKey)
3414
+ announcePayload.requesterSessionKey = record.requesterSessionKey;
3415
+ this.emitSubagentEvent("SUBAGENT_ANNOUNCE_SENT", announcePayload);
3416
+ record.cleanupCompletedAt = Date.now();
3417
+ if (record.cleanup === "delete") {
3418
+ this.subagentRuns.delete(runId);
3463
3419
  }
3420
+ return true;
3464
3421
  }
3465
- async sendViaWhatsApp(params) {
3466
- const whatsappService = this.runtime.getService("whatsapp");
3467
- if (!whatsappService?.sendText) {
3468
- return {
3469
- success: false,
3470
- channel: "whatsapp",
3471
- targetId: params.target.to,
3472
- error: "WhatsApp service not available or sendText method not found"
3473
- };
3422
+ buildSubagentSystemPrompt(params) {
3423
+ const taskText = typeof params.task === "string" && params.task.trim() ? params.task.replace(/\s+/g, " ").trim() : "{{TASK_DESCRIPTION}}";
3424
+ const lines = [
3425
+ "# Subagent Context",
3426
+ "",
3427
+ "You are a **subagent** spawned by the main agent for a specific task.",
3428
+ "",
3429
+ "## Your Role",
3430
+ `- You were created to handle: ${taskText}`,
3431
+ "- Complete this task. That's your entire purpose.",
3432
+ "- You are NOT the main agent. Don't try to be.",
3433
+ "",
3434
+ "## Rules",
3435
+ "1. **Stay focused** - Do your assigned task, nothing else",
3436
+ "2. **Complete the task** - Your final message will be automatically reported to the main agent",
3437
+ "3. **Don't initiate** - No heartbeats, no proactive actions, no side quests",
3438
+ "4. **Be ephemeral** - You may be terminated after task completion. That's fine.",
3439
+ "",
3440
+ "## Output Format",
3441
+ "When complete, your final response should include:",
3442
+ "- What you accomplished or found",
3443
+ "- Any relevant details the main agent should know",
3444
+ "- Keep it concise but informative",
3445
+ "",
3446
+ "## What You DON'T Do",
3447
+ "- NO user conversations (that's main agent's job)",
3448
+ "- NO external messages unless explicitly tasked with a specific recipient",
3449
+ "- NO cron jobs or persistent state",
3450
+ "- NO pretending to be the main agent",
3451
+ "",
3452
+ "## Session Context",
3453
+ params.label ? `- Label: ${params.label}` : undefined,
3454
+ params.requesterSessionKey ? `- Requester session: ${params.requesterSessionKey}` : undefined,
3455
+ params.requesterOrigin?.channel ? `- Requester channel: ${params.requesterOrigin.channel}` : undefined,
3456
+ `- Your session: ${params.childSessionKey}`,
3457
+ ""
3458
+ ].filter((line) => line !== undefined);
3459
+ return lines.join(`
3460
+ `);
3461
+ }
3462
+ async sendToAgent(params, requesterContext) {
3463
+ const runId = crypto3.randomUUID();
3464
+ const policy = this.getAgentToAgentPolicy();
3465
+ let targetSessionKey = params.sessionKey;
3466
+ if (!targetSessionKey && params.label) {
3467
+ const matchingRun = this.findSubagentRunByLabel(params.label, params.agentId);
3468
+ if (matchingRun) {
3469
+ targetSessionKey = matchingRun.childSessionKey;
3470
+ } else {
3471
+ return {
3472
+ status: "error",
3473
+ runId,
3474
+ error: `No subagent found with label "${params.label}"`
3475
+ };
3476
+ }
3474
3477
  }
3475
- try {
3476
- const result = await whatsappService.sendText(params.target.to, params.content.text);
3477
- const messageId = result.messages?.[0]?.id;
3478
- return {
3479
- success: true,
3480
- messageId,
3481
- channel: "whatsapp",
3482
- targetId: params.target.to,
3483
- sentAt: Date.now()
3484
- };
3485
- } catch (error) {
3478
+ if (!targetSessionKey) {
3486
3479
  return {
3487
- success: false,
3488
- channel: "whatsapp",
3489
- targetId: params.target.to,
3490
- error: error instanceof Error ? error.message : String(error)
3480
+ status: "error",
3481
+ runId,
3482
+ error: "Either sessionKey or label is required"
3491
3483
  };
3492
3484
  }
3493
- }
3494
- async sendViaTwitch(params) {
3495
- const twitchService = this.runtime.getService("twitch");
3496
- if (!twitchService?.sendMessage) {
3497
- return {
3498
- success: false,
3499
- channel: "twitch",
3500
- targetId: params.target.to,
3501
- error: "Twitch service not available or sendMessage method not found"
3502
- };
3485
+ const requesterAgentId = requesterContext.sessionKey ? extractAgentIdFromSessionKey(requesterContext.sessionKey) : this.runtime.character?.name ?? "unknown";
3486
+ const targetAgentId = extractAgentIdFromSessionKey(targetSessionKey);
3487
+ if (requesterAgentId !== targetAgentId) {
3488
+ if (!policy.enabled) {
3489
+ return {
3490
+ status: "forbidden",
3491
+ runId,
3492
+ error: "Agent-to-agent messaging is disabled. Set settings.agentToAgent.enabled=true to allow cross-agent sends."
3493
+ };
3494
+ }
3495
+ if (!policy.isAllowed(requesterAgentId, targetAgentId)) {
3496
+ return {
3497
+ status: "forbidden",
3498
+ runId,
3499
+ error: "Agent-to-agent messaging denied by policy."
3500
+ };
3501
+ }
3503
3502
  }
3504
- try {
3505
- const result = await twitchService.sendMessage(params.content.text, {
3506
- channel: params.target.to,
3507
- replyTo: params.target.replyToMessageId
3503
+ const targetRoomId = sessionKeyToRoomId(targetSessionKey, targetAgentId);
3504
+ const contextMessage = this.buildAgentToAgentContext({
3505
+ requesterSessionKey: requesterContext.sessionKey,
3506
+ targetSessionKey
3507
+ });
3508
+ const a2aMetadata = {
3509
+ isAgentToAgent: true,
3510
+ runId,
3511
+ systemPromptOverride: contextMessage
3512
+ };
3513
+ if (requesterContext.sessionKey)
3514
+ a2aMetadata.senderSessionKey = requesterContext.sessionKey;
3515
+ if (requesterContext.roomId)
3516
+ a2aMetadata.senderRoomId = requesterContext.roomId;
3517
+ const message = {
3518
+ id: hashToUUID(`${runId}-a2a`),
3519
+ entityId: this.runtime.agentId,
3520
+ agentId: this.runtime.agentId,
3521
+ roomId: targetRoomId,
3522
+ content: {
3523
+ text: params.message,
3524
+ type: "text",
3525
+ metadata: a2aMetadata
3526
+ }
3527
+ };
3528
+ const timeoutMs = (params.timeoutSeconds ?? 30) * 1000;
3529
+ if (params.timeoutSeconds === 0) {
3530
+ this.runtime.emitEvent(EventType2.MESSAGE_RECEIVED, {
3531
+ runtime: this.runtime,
3532
+ message,
3533
+ source: "a2a"
3534
+ }).catch((err) => {
3535
+ this.runtime.logger.error("A2A send error", { runId, error: err });
3508
3536
  });
3509
- return {
3510
- success: result.success,
3511
- messageId: result.messageId,
3512
- channel: "twitch",
3513
- targetId: params.target.to,
3514
- sentAt: Date.now()
3537
+ const asyncPayload = {
3538
+ runId,
3539
+ childSessionKey: targetSessionKey,
3540
+ task: params.message
3515
3541
  };
3516
- } catch (error) {
3542
+ if (requesterContext.sessionKey)
3543
+ asyncPayload.requesterSessionKey = requesterContext.sessionKey;
3544
+ this.emitSubagentEvent("A2A_MESSAGE_SENT", asyncPayload);
3517
3545
  return {
3518
- success: false,
3519
- channel: "twitch",
3520
- targetId: params.target.to,
3521
- error: error instanceof Error ? error.message : String(error)
3546
+ status: "accepted",
3547
+ runId,
3548
+ sessionKey: targetSessionKey,
3549
+ delivery: { status: "pending", mode: "async" }
3522
3550
  };
3523
3551
  }
3524
- }
3525
- async sendViaInternal(params) {
3526
- const roomId = params.target.to;
3527
- const messageId = crypto3.randomUUID();
3528
- try {
3529
- const memory = {
3530
- id: messageId,
3531
- entityId: this.runtime.agentId,
3532
- agentId: this.runtime.agentId,
3533
- roomId,
3534
- content: {
3535
- text: params.content.text,
3536
- type: "text",
3537
- source: "internal",
3538
- metadata: {
3539
- isInternalMessage: true,
3540
- idempotencyKey: params.idempotencyKey
3541
- }
3542
- },
3543
- createdAt: Date.now()
3544
- };
3545
- await this.runtime.emitEvent(EventType2.MESSAGE_RECEIVED, {
3546
- runtime: this.runtime,
3547
- message: memory,
3548
- source: "internal_messaging"
3552
+ const sentAt = Date.now();
3553
+ await this.runtime.emitEvent(EventType2.MESSAGE_RECEIVED, {
3554
+ runtime: this.runtime,
3555
+ message,
3556
+ source: "a2a"
3557
+ });
3558
+ const pollIntervalMs = 500;
3559
+ const maxPolls = Math.ceil(timeoutMs / pollIntervalMs);
3560
+ let lastReply;
3561
+ for (let poll = 0;poll < maxPolls; poll++) {
3562
+ await new Promise((r) => setTimeout(r, pollIntervalMs));
3563
+ const memories = await this.runtime.getMemories({
3564
+ tableName: "messages",
3565
+ roomId: targetRoomId,
3566
+ count: 10
3549
3567
  });
3568
+ const newReplies = memories.filter((m) => m.entityId === this.runtime.agentId && m.id !== message.id && m.createdAt && m.createdAt > sentAt);
3569
+ if (newReplies.length > 0) {
3570
+ lastReply = newReplies.sort((a, b) => (b.createdAt ?? 0) - (a.createdAt ?? 0))[0];
3571
+ break;
3572
+ }
3573
+ }
3574
+ const syncPayload = {
3575
+ runId,
3576
+ childSessionKey: targetSessionKey,
3577
+ task: params.message
3578
+ };
3579
+ if (requesterContext.sessionKey)
3580
+ syncPayload.requesterSessionKey = requesterContext.sessionKey;
3581
+ this.emitSubagentEvent("A2A_MESSAGE_SENT", syncPayload);
3582
+ if (!lastReply) {
3550
3583
  return {
3551
- success: true,
3552
- messageId,
3553
- channel: "internal",
3554
- targetId: params.target.to,
3555
- sentAt: Date.now()
3556
- };
3557
- } catch (error) {
3558
- return {
3559
- success: false,
3560
- channel: "internal",
3561
- targetId: params.target.to,
3562
- error: error instanceof Error ? error.message : String(error)
3584
+ status: "timeout",
3585
+ runId,
3586
+ sessionKey: targetSessionKey,
3587
+ error: `No response received within ${params.timeoutSeconds ?? 30} seconds`,
3588
+ delivery: { status: "timeout", mode: "sync" }
3563
3589
  };
3564
3590
  }
3591
+ return {
3592
+ status: "ok",
3593
+ runId,
3594
+ sessionKey: targetSessionKey,
3595
+ reply: lastReply.content?.text,
3596
+ delivery: { status: "delivered", mode: "sync" }
3597
+ };
3565
3598
  }
3566
- normalizeChannel(channel) {
3567
- if (!channel) {
3568
- return "unknown";
3569
- }
3570
- const lower = channel.toLowerCase();
3571
- if (lower === "discord" || lower.includes("discord")) {
3572
- return "discord";
3573
- }
3574
- if (lower === "telegram" || lower.includes("telegram")) {
3575
- return "telegram";
3576
- }
3577
- if (lower === "slack" || lower.includes("slack")) {
3578
- return "slack";
3579
- }
3580
- if (lower === "whatsapp" || lower.includes("whatsapp")) {
3581
- return "whatsapp";
3582
- }
3583
- if (lower === "twitch" || lower.includes("twitch")) {
3584
- return "twitch";
3599
+ buildAgentToAgentContext(params) {
3600
+ return [
3601
+ "# Agent-to-Agent Message Context",
3602
+ "",
3603
+ "This message was sent by another agent session.",
3604
+ params.requesterSessionKey ? `- Sender: ${params.requesterSessionKey}` : undefined,
3605
+ `- Target: ${params.targetSessionKey}`,
3606
+ "",
3607
+ "Process this message and respond appropriately."
3608
+ ].filter((l) => l !== undefined).join(`
3609
+ `);
3610
+ }
3611
+ getSubagentRun(runId) {
3612
+ return this.subagentRuns.get(runId);
3613
+ }
3614
+ findSubagentRunByLabel(label, agentId) {
3615
+ const normalizedLabel = label.toLowerCase().trim();
3616
+ const normalizedAgentId = agentId ? normalizeAgentId2(agentId) : undefined;
3617
+ for (const run of this.subagentRuns.values()) {
3618
+ const runLabel = run.label?.toLowerCase().trim();
3619
+ if (runLabel !== normalizedLabel) {
3620
+ continue;
3621
+ }
3622
+ if (normalizedAgentId) {
3623
+ const runAgentId = extractAgentIdFromSessionKey(run.childSessionKey);
3624
+ if (normalizeAgentId2(runAgentId) !== normalizedAgentId) {
3625
+ continue;
3626
+ }
3627
+ }
3628
+ if (!run.endedAt) {
3629
+ return run;
3630
+ }
3585
3631
  }
3586
- if (lower === "google_chat" || lower.includes("google") || lower.includes("gchat")) {
3587
- return "google_chat";
3632
+ const completedRuns = [...this.subagentRuns.values()].filter((run) => {
3633
+ const runLabel = run.label?.toLowerCase().trim();
3634
+ if (runLabel !== normalizedLabel) {
3635
+ return false;
3636
+ }
3637
+ if (normalizedAgentId) {
3638
+ const runAgentId = extractAgentIdFromSessionKey(run.childSessionKey);
3639
+ if (normalizeAgentId2(runAgentId) !== normalizedAgentId) {
3640
+ return false;
3641
+ }
3642
+ }
3643
+ return true;
3644
+ }).sort((a, b) => (b.createdAt ?? 0) - (a.createdAt ?? 0));
3645
+ return completedRuns[0];
3646
+ }
3647
+ listSubagentRuns(requesterSessionKey) {
3648
+ const runs = [...this.subagentRuns.values()];
3649
+ if (!requesterSessionKey) {
3650
+ return runs;
3588
3651
  }
3589
- if (lower === "internal" || lower === "a2a") {
3590
- return "internal";
3652
+ return runs.filter((r) => r.requesterSessionKey === requesterSessionKey);
3653
+ }
3654
+ cancelSubagentRun(runId) {
3655
+ const controller = this.activeRuns.get(runId);
3656
+ if (controller) {
3657
+ controller.abort();
3658
+ return true;
3591
3659
  }
3592
- return "unknown";
3660
+ return false;
3593
3661
  }
3594
3662
  on(event, handler) {
3595
3663
  this.emitter.on(event, handler);
@@ -3597,25 +3665,46 @@ class MessagingService extends Service4 {
3597
3665
  off(event, handler) {
3598
3666
  this.emitter.off(event, handler);
3599
3667
  }
3600
- emitMessagingEvent(type, payload) {
3668
+ emitSubagentEvent(type, payload) {
3601
3669
  this.emitter.emit(type, payload);
3602
- this.emitter.emit("messaging", { type, ...payload });
3670
+ this.emitter.emit("task", { type, ...payload });
3671
+ }
3672
+ startSweeper() {
3673
+ if (this.sweeper) {
3674
+ return;
3675
+ }
3676
+ this.sweeper = setInterval(() => {
3677
+ this.sweepOldRuns();
3678
+ }, 60000);
3679
+ this.sweeper.unref?.();
3680
+ }
3681
+ sweepOldRuns() {
3682
+ const now2 = Date.now();
3683
+ for (const [runId, record] of this.subagentRuns.entries()) {
3684
+ if (record.archiveAtMs && record.archiveAtMs <= now2) {
3685
+ this.subagentRuns.delete(runId);
3686
+ }
3687
+ }
3603
3688
  }
3604
3689
  async stop() {
3605
- this.pendingDeliveries.clear();
3690
+ if (this.sweeper) {
3691
+ clearInterval(this.sweeper);
3692
+ this.sweeper = null;
3693
+ }
3694
+ for (const controller of this.activeRuns.values()) {
3695
+ controller.abort();
3696
+ }
3697
+ this.activeRuns.clear();
3606
3698
  this.emitter.removeAllListeners();
3607
3699
  }
3608
3700
  }
3609
- // src/types/subagent.ts
3610
- var SubagentEventType = {
3611
- SPAWN_REQUESTED: "SUBAGENT_SPAWN_REQUESTED",
3612
- RUN_STARTED: "SUBAGENT_RUN_STARTED",
3613
- RUN_COMPLETED: "SUBAGENT_RUN_COMPLETED",
3614
- RUN_FAILED: "SUBAGENT_RUN_FAILED",
3615
- RUN_TIMEOUT: "SUBAGENT_RUN_TIMEOUT",
3616
- ANNOUNCE_SENT: "SUBAGENT_ANNOUNCE_SENT",
3617
- A2A_MESSAGE_SENT: "A2A_MESSAGE_SENT",
3618
- A2A_MESSAGE_RECEIVED: "A2A_MESSAGE_RECEIVED"
3701
+ // src/types/messaging.ts
3702
+ var MessagingEventType = {
3703
+ SEND_REQUESTED: "MESSAGING_SEND_REQUESTED",
3704
+ SENT: "MESSAGING_SENT",
3705
+ SEND_FAILED: "MESSAGING_SEND_FAILED",
3706
+ DELIVERED: "MESSAGING_DELIVERED",
3707
+ READ: "MESSAGING_READ"
3619
3708
  };
3620
3709
  // src/types/sandbox.ts
3621
3710
  var SandboxEventType = {
@@ -3627,13 +3716,16 @@ var SandboxEventType = {
3627
3716
  BROWSER_STARTED: "SANDBOX_BROWSER_STARTED",
3628
3717
  BROWSER_STOPPED: "SANDBOX_BROWSER_STOPPED"
3629
3718
  };
3630
- // src/types/messaging.ts
3631
- var MessagingEventType = {
3632
- SEND_REQUESTED: "MESSAGING_SEND_REQUESTED",
3633
- SENT: "MESSAGING_SENT",
3634
- SEND_FAILED: "MESSAGING_SEND_FAILED",
3635
- DELIVERED: "MESSAGING_DELIVERED",
3636
- READ: "MESSAGING_READ"
3719
+ // src/types/subagent.ts
3720
+ var SubagentEventType = {
3721
+ SPAWN_REQUESTED: "SUBAGENT_SPAWN_REQUESTED",
3722
+ RUN_STARTED: "SUBAGENT_RUN_STARTED",
3723
+ RUN_COMPLETED: "SUBAGENT_RUN_COMPLETED",
3724
+ RUN_FAILED: "SUBAGENT_RUN_FAILED",
3725
+ RUN_TIMEOUT: "SUBAGENT_RUN_TIMEOUT",
3726
+ ANNOUNCE_SENT: "SUBAGENT_ANNOUNCE_SENT",
3727
+ A2A_MESSAGE_SENT: "A2A_MESSAGE_SENT",
3728
+ A2A_MESSAGE_RECEIVED: "A2A_MESSAGE_RECEIVED"
3637
3729
  };
3638
3730
  // index.ts
3639
3731
  var sessionProviders = getSessionProviders2();
@@ -3748,4 +3840,4 @@ export {
3748
3840
  AgentOrchestratorService
3749
3841
  };
3750
3842
 
3751
- //# debugId=9695C525D670EDC964756E2164756E21
3843
+ //# debugId=D58B592C4A9AA05064756E2164756E21