@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.d.ts +8 -13
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1974 -1882
- package/dist/index.js.map +14 -14
- package/dist/src/actions/messaging.d.ts.map +1 -1
- package/dist/src/actions/subagent-management.d.ts.map +1 -1
- package/dist/src/actions/task-management.d.ts.map +1 -1
- package/dist/src/providers/orchestrator-config.d.ts +1 -1
- package/dist/src/providers/orchestrator-config.d.ts.map +1 -1
- package/dist/src/services/messaging-service.d.ts +1 -1
- package/dist/src/services/messaging-service.d.ts.map +1 -1
- package/dist/src/services/sandbox-service.d.ts +2 -2
- package/dist/src/services/sandbox-service.d.ts.map +1 -1
- package/dist/src/services/subagent-service.d.ts +2 -2
- package/dist/src/services/subagent-service.d.ts.map +1 -1
- package/dist/src/types/index.d.ts +4 -4
- package/dist/src/types/index.d.ts.map +1 -1
- package/dist/src/types/messaging.d.ts.map +1 -1
- package/dist/src/types/subagent.d.ts +1 -1
- package/dist/src/types/subagent.d.ts.map +1 -1
- package/dist/src/utils/index.d.ts +1 -2
- package/dist/src/utils/index.d.ts.map +1 -1
- package/dist/src/utils/session.d.ts +2 -2
- package/dist/src/utils/session.d.ts.map +1 -1
- package/package.json +14 -2
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/
|
|
5
|
-
function
|
|
6
|
-
const
|
|
7
|
-
if (!
|
|
8
|
-
|
|
4
|
+
// src/actions/messaging.ts
|
|
5
|
+
function extractMessagingParams(message, _state) {
|
|
6
|
+
const params = message.content?.params;
|
|
7
|
+
if (!params) {
|
|
8
|
+
return {};
|
|
9
9
|
}
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
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
|
|
16
|
-
name: "
|
|
17
|
-
similes: [
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
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,
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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
|
-
|
|
47
|
-
${stepLines.map((s, i) => `${i + 1}. ${s}`).join(`
|
|
48
|
-
`)}`);
|
|
66
|
+
return { success: false, error: "Messaging service not available" };
|
|
49
67
|
}
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
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
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
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
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
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
|
|
94
|
-
name: "
|
|
95
|
-
similes: ["
|
|
96
|
-
description: "
|
|
97
|
-
validate: async (runtime, message) => {
|
|
98
|
-
const
|
|
99
|
-
if (!
|
|
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
|
|
103
|
-
|
|
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
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
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
|
|
114
|
-
const
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
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
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
}
|
|
126
|
-
|
|
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
|
|
154
|
-
|
|
155
|
-
|
|
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
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
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
|
|
164
|
-
name: "
|
|
165
|
-
similes: ["
|
|
166
|
-
description: "
|
|
167
|
-
validate: async (runtime, message) => {
|
|
168
|
-
const
|
|
169
|
-
if (!
|
|
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
|
|
173
|
-
|
|
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
|
|
200
|
-
return
|
|
291
|
+
const hasText = params.text || message.content?.text;
|
|
292
|
+
return !!hasText;
|
|
201
293
|
},
|
|
202
294
|
handler: async (runtime, message, _state, _options, callback) => {
|
|
203
|
-
const
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
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
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
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
|
|
222
|
-
name: "
|
|
223
|
-
similes: ["
|
|
224
|
-
description: "
|
|
225
|
-
validate: async (runtime, message) => {
|
|
226
|
-
const
|
|
227
|
-
if (!
|
|
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
|
|
231
|
-
|
|
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
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
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
|
-
|
|
243
|
-
const
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
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
|
-
|
|
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
|
-
|
|
283
|
-
|
|
284
|
-
createSendPolicyProvider,
|
|
285
|
-
getSessionProviders,
|
|
286
|
-
extractSessionContext,
|
|
545
|
+
resolveThreadParentSessionKey,
|
|
546
|
+
resolveThreadSessionKeys,
|
|
287
547
|
SessionStateManager,
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
763
|
+
function extractSessionContext2(_runtime, message) {
|
|
489
764
|
const metadata = message.content?.metadata;
|
|
490
765
|
const sessionKey = typeof metadata?.sessionKey === "string" ? metadata.sessionKey : undefined;
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
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
|
|
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
|
-
|
|
536
|
-
|
|
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
|
|
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
|
-
|
|
593
|
-
|
|
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,
|
|
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,
|
|
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/
|
|
743
|
-
function
|
|
744
|
-
const
|
|
745
|
-
if (!
|
|
746
|
-
|
|
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
|
-
|
|
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
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
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
|
|
775
|
-
|
|
776
|
-
|
|
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,
|
|
783
|
-
const
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
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
|
-
|
|
1058
|
+
await svc.appendOutput(task.id ?? "", `Plan:
|
|
1059
|
+
${stepLines.map((s, i) => `${i + 1}. ${s}`).join(`
|
|
1060
|
+
`)}`);
|
|
791
1061
|
}
|
|
792
|
-
const
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
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
|
-
|
|
947
|
-
|
|
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
|
|
990
|
-
name: "
|
|
991
|
-
similes: [
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
"
|
|
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
|
|
1007
|
-
return
|
|
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,
|
|
1010
|
-
const
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
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
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
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
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
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
|
|
1093
|
-
|
|
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
|
|
1097
|
-
return
|
|
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,
|
|
1100
|
-
const
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
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
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
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
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
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
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
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
|
-
|
|
1142
|
-
|
|
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
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
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
|
|
1170
|
-
name: "
|
|
1171
|
-
similes: [
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
"
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
const
|
|
1179
|
-
return
|
|
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,
|
|
1182
|
-
const
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
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
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
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
|
-
|
|
1206
|
-
|
|
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
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
}
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
1443
|
+
// node_modules/uuid/dist-node/native.js
|
|
1408
1444
|
import { randomUUID } from "node:crypto";
|
|
1409
1445
|
var native_default = { randomUUID };
|
|
1410
1446
|
|
|
1411
|
-
//
|
|
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/
|
|
1891
|
-
import
|
|
1892
|
-
import
|
|
1893
|
-
import {
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
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
|
|
1912
|
-
|
|
1913
|
-
|
|
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.
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
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
|
|
1968
|
-
const
|
|
1969
|
-
if (!
|
|
2156
|
+
async sendToRoom(roomId, content, options) {
|
|
2157
|
+
const room = await this.runtime.getRoom(roomId);
|
|
2158
|
+
if (!room) {
|
|
1970
2159
|
return {
|
|
1971
|
-
|
|
1972
|
-
|
|
2160
|
+
success: false,
|
|
2161
|
+
channel: "unknown",
|
|
2162
|
+
targetId: roomId,
|
|
2163
|
+
error: `Room not found: ${roomId}`
|
|
1973
2164
|
};
|
|
1974
2165
|
}
|
|
1975
|
-
|
|
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
|
-
|
|
1978
|
-
|
|
2171
|
+
success: false,
|
|
2172
|
+
channel,
|
|
2173
|
+
targetId: roomId,
|
|
2174
|
+
error: "Room has no messaging target configured"
|
|
1979
2175
|
};
|
|
1980
2176
|
}
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
|
|
1986
|
-
|
|
1987
|
-
|
|
1988
|
-
|
|
1989
|
-
|
|
1990
|
-
|
|
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
|
|
2082
|
-
const
|
|
2083
|
-
const
|
|
2084
|
-
|
|
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
|
|
2105
|
-
const
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2122
|
-
const
|
|
2123
|
-
if (!
|
|
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
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
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
|
|
2162
|
-
const
|
|
2163
|
-
if (!
|
|
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
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
2170
|
-
|
|
2171
|
-
|
|
2172
|
-
|
|
2173
|
-
|
|
2174
|
-
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
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
|
|
2186
|
-
const
|
|
2187
|
-
if (!
|
|
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
|
-
|
|
2191
|
-
|
|
2192
|
-
|
|
2193
|
-
|
|
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
|
|
2198
|
-
const
|
|
2199
|
-
if (!
|
|
2200
|
-
return
|
|
2201
|
-
|
|
2202
|
-
|
|
2203
|
-
|
|
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
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
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
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
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
|
|
2366
|
+
roomId,
|
|
2240
2367
|
content: {
|
|
2241
|
-
text:
|
|
2368
|
+
text: params.content.text,
|
|
2242
2369
|
type: "text",
|
|
2370
|
+
source: "internal",
|
|
2243
2371
|
metadata: {
|
|
2244
|
-
|
|
2245
|
-
|
|
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:
|
|
2253
|
-
source: "
|
|
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
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
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
|
-
|
|
2424
|
-
|
|
2425
|
-
|
|
2426
|
-
error:
|
|
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
|
-
|
|
2454
|
-
|
|
2455
|
-
|
|
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
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
2433
|
+
emitMessagingEvent(type, payload) {
|
|
2508
2434
|
this.emitter.emit(type, payload);
|
|
2509
|
-
this.emitter.emit("
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
|
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(
|
|
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/
|
|
3107
|
-
import { EventEmitter as EventEmitter4 } from "node:events";
|
|
3011
|
+
// src/services/subagent-service.ts
|
|
3108
3012
|
import crypto3 from "node:crypto";
|
|
3109
|
-
import {
|
|
3110
|
-
|
|
3111
|
-
|
|
3112
|
-
|
|
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
|
-
|
|
3115
|
-
|
|
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
|
|
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.
|
|
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.
|
|
3171
|
-
|
|
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
|
-
|
|
3177
|
-
this.
|
|
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
|
-
|
|
3180
|
-
|
|
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
|
-
|
|
3183
|
-
const
|
|
3184
|
-
|
|
3185
|
-
|
|
3186
|
-
|
|
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.
|
|
3210
|
-
|
|
3211
|
-
|
|
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
|
-
|
|
3217
|
-
|
|
3218
|
-
|
|
3219
|
-
|
|
3220
|
-
|
|
3221
|
-
|
|
3222
|
-
|
|
3223
|
-
|
|
3224
|
-
|
|
3225
|
-
|
|
3226
|
-
|
|
3227
|
-
|
|
3228
|
-
|
|
3229
|
-
|
|
3230
|
-
|
|
3231
|
-
|
|
3232
|
-
|
|
3233
|
-
|
|
3234
|
-
|
|
3235
|
-
|
|
3236
|
-
|
|
3237
|
-
|
|
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
|
-
|
|
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
|
-
|
|
3256
|
-
|
|
3257
|
-
|
|
3258
|
-
|
|
3259
|
-
|
|
3260
|
-
|
|
3261
|
-
if (
|
|
3262
|
-
|
|
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
|
-
|
|
3280
|
-
|
|
3281
|
-
|
|
3282
|
-
|
|
3283
|
-
|
|
3284
|
-
|
|
3285
|
-
|
|
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
|
-
|
|
3301
|
-
const
|
|
3302
|
-
|
|
3303
|
-
|
|
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
|
-
|
|
3312
|
-
|
|
3313
|
-
|
|
3314
|
-
|
|
3315
|
-
|
|
3316
|
-
|
|
3317
|
-
|
|
3318
|
-
|
|
3319
|
-
|
|
3320
|
-
|
|
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
|
-
|
|
3324
|
-
const
|
|
3325
|
-
if (!
|
|
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
|
-
|
|
3345
|
-
|
|
3346
|
-
|
|
3347
|
-
|
|
3348
|
-
|
|
3349
|
-
|
|
3350
|
-
|
|
3351
|
-
|
|
3352
|
-
|
|
3353
|
-
|
|
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
|
|
3357
|
-
const
|
|
3358
|
-
|
|
3359
|
-
|
|
3360
|
-
|
|
3361
|
-
|
|
3362
|
-
|
|
3363
|
-
|
|
3364
|
-
|
|
3365
|
-
|
|
3366
|
-
|
|
3367
|
-
|
|
3368
|
-
|
|
3369
|
-
|
|
3370
|
-
|
|
3371
|
-
|
|
3372
|
-
|
|
3373
|
-
|
|
3374
|
-
|
|
3375
|
-
|
|
3376
|
-
|
|
3377
|
-
|
|
3378
|
-
|
|
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
|
|
3402
|
-
const
|
|
3403
|
-
if (!
|
|
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
|
-
|
|
3412
|
-
|
|
3413
|
-
|
|
3414
|
-
|
|
3415
|
-
|
|
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
|
|
3435
|
-
const
|
|
3436
|
-
if (!
|
|
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
|
-
|
|
3445
|
-
|
|
3446
|
-
|
|
3447
|
-
|
|
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
|
-
|
|
3450
|
-
|
|
3451
|
-
|
|
3452
|
-
|
|
3453
|
-
|
|
3454
|
-
|
|
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
|
-
|
|
3457
|
-
|
|
3458
|
-
|
|
3459
|
-
|
|
3460
|
-
|
|
3461
|
-
|
|
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
|
-
|
|
3466
|
-
const
|
|
3467
|
-
|
|
3468
|
-
|
|
3469
|
-
|
|
3470
|
-
|
|
3471
|
-
|
|
3472
|
-
|
|
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
|
-
|
|
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
|
-
|
|
3488
|
-
|
|
3489
|
-
|
|
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
|
-
|
|
3495
|
-
|
|
3496
|
-
|
|
3497
|
-
|
|
3498
|
-
|
|
3499
|
-
|
|
3500
|
-
|
|
3501
|
-
|
|
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
|
-
|
|
3505
|
-
|
|
3506
|
-
|
|
3507
|
-
|
|
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
|
-
|
|
3510
|
-
|
|
3511
|
-
|
|
3512
|
-
|
|
3513
|
-
targetId: params.target.to,
|
|
3514
|
-
sentAt: Date.now()
|
|
3537
|
+
const asyncPayload = {
|
|
3538
|
+
runId,
|
|
3539
|
+
childSessionKey: targetSessionKey,
|
|
3540
|
+
task: params.message
|
|
3515
3541
|
};
|
|
3516
|
-
|
|
3542
|
+
if (requesterContext.sessionKey)
|
|
3543
|
+
asyncPayload.requesterSessionKey = requesterContext.sessionKey;
|
|
3544
|
+
this.emitSubagentEvent("A2A_MESSAGE_SENT", asyncPayload);
|
|
3517
3545
|
return {
|
|
3518
|
-
|
|
3519
|
-
|
|
3520
|
-
|
|
3521
|
-
|
|
3546
|
+
status: "accepted",
|
|
3547
|
+
runId,
|
|
3548
|
+
sessionKey: targetSessionKey,
|
|
3549
|
+
delivery: { status: "pending", mode: "async" }
|
|
3522
3550
|
};
|
|
3523
3551
|
}
|
|
3524
|
-
|
|
3525
|
-
|
|
3526
|
-
|
|
3527
|
-
|
|
3528
|
-
|
|
3529
|
-
|
|
3530
|
-
|
|
3531
|
-
|
|
3532
|
-
|
|
3533
|
-
|
|
3534
|
-
|
|
3535
|
-
|
|
3536
|
-
|
|
3537
|
-
|
|
3538
|
-
|
|
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
|
-
|
|
3552
|
-
|
|
3553
|
-
|
|
3554
|
-
|
|
3555
|
-
|
|
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
|
-
|
|
3567
|
-
|
|
3568
|
-
|
|
3569
|
-
|
|
3570
|
-
|
|
3571
|
-
|
|
3572
|
-
|
|
3573
|
-
|
|
3574
|
-
|
|
3575
|
-
|
|
3576
|
-
|
|
3577
|
-
|
|
3578
|
-
|
|
3579
|
-
|
|
3580
|
-
|
|
3581
|
-
|
|
3582
|
-
|
|
3583
|
-
|
|
3584
|
-
|
|
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
|
-
|
|
3587
|
-
|
|
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
|
-
|
|
3590
|
-
|
|
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
|
|
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
|
-
|
|
3668
|
+
emitSubagentEvent(type, payload) {
|
|
3601
3669
|
this.emitter.emit(type, payload);
|
|
3602
|
-
this.emitter.emit("
|
|
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.
|
|
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/
|
|
3610
|
-
var
|
|
3611
|
-
|
|
3612
|
-
|
|
3613
|
-
|
|
3614
|
-
|
|
3615
|
-
|
|
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/
|
|
3631
|
-
var
|
|
3632
|
-
|
|
3633
|
-
|
|
3634
|
-
|
|
3635
|
-
|
|
3636
|
-
|
|
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=
|
|
3843
|
+
//# debugId=D58B592C4A9AA05064756E2164756E21
|