plugin-agent-orchestrator 1.0.6 → 1.0.13
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/client/index.js +1 -1
- package/dist/externalVersion.js +6 -6
- package/dist/server/collections/orchestrator-config.js +10 -0
- package/dist/server/collections/orchestrator-logs.js +19 -2
- package/dist/server/migrations/20260427000000-add-tracing-detail-fields.d.ts +7 -0
- package/dist/server/migrations/20260427000000-add-tracing-detail-fields.js +62 -0
- package/dist/server/migrations/20260429000000-add-llm-fields.d.ts +7 -0
- package/dist/server/migrations/20260429000000-add-llm-fields.js +60 -0
- package/dist/server/resources/tracing.js +8 -3
- package/dist/server/tools/delegate-task.js +306 -95
- package/package.json +1 -1
- package/src/client/RulesTab.tsx +134 -8
- package/src/client/TracingTab.tsx +171 -21
- package/src/server/collections/orchestrator-config.ts +10 -0
- package/src/server/collections/orchestrator-logs.ts +19 -2
- package/src/server/migrations/20260427000000-add-tracing-detail-fields.ts +41 -0
- package/src/server/migrations/20260429000000-add-llm-fields.ts +37 -0
- package/src/server/resources/tracing.ts +6 -2
- package/src/server/tools/delegate-task.ts +363 -101
|
@@ -34,6 +34,90 @@ var import_prebuilt = require("@langchain/langgraph/prebuilt");
|
|
|
34
34
|
var import_tools = require("@langchain/core/tools");
|
|
35
35
|
var import_messages = require("@langchain/core/messages");
|
|
36
36
|
const ORCHESTRATOR_DEPTH_KEY = "__orchestratorDepth";
|
|
37
|
+
function sanitizeToolPart(value) {
|
|
38
|
+
return (value || "").replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
39
|
+
}
|
|
40
|
+
function buildDelegateToolName(leaderUsername, subAgentUsername) {
|
|
41
|
+
return `delegate_${sanitizeToolPart(leaderUsername)}_to_${sanitizeToolPart(subAgentUsername)}`;
|
|
42
|
+
}
|
|
43
|
+
function isDelegateToolName(toolName) {
|
|
44
|
+
return toolName.startsWith("delegate_to_") || toolName.startsWith("delegate_") && toolName.includes("_to_");
|
|
45
|
+
}
|
|
46
|
+
function createDelegateToolOptions(plugin, options) {
|
|
47
|
+
const { leaderUsername, subAgentUsername, subAgentEmployee, maxDepth, timeout, toolName, legacyAlias, llmService, model } = options;
|
|
48
|
+
const toolDescription = [
|
|
49
|
+
`Delegate a task from "${leaderUsername}" to the AI Employee "${subAgentEmployee.nickname || subAgentUsername}".`,
|
|
50
|
+
legacyAlias ? "This is a backward-compatible alias for existing skill assignments." : "",
|
|
51
|
+
subAgentEmployee.about ? `Specialist profile: ${subAgentEmployee.about.substring(0, 200)}` : "",
|
|
52
|
+
"The sub-agent will execute the task independently and return its final answer."
|
|
53
|
+
].filter(Boolean).join(" ");
|
|
54
|
+
return {
|
|
55
|
+
scope: "CUSTOM",
|
|
56
|
+
execution: "backend",
|
|
57
|
+
defaultPermission: "ALLOW",
|
|
58
|
+
silence: false,
|
|
59
|
+
introduction: {
|
|
60
|
+
title: `[${leaderUsername}] ${subAgentEmployee.nickname || subAgentUsername}${legacyAlias ? " (legacy)" : ""}`,
|
|
61
|
+
about: toolDescription
|
|
62
|
+
},
|
|
63
|
+
definition: {
|
|
64
|
+
name: toolName,
|
|
65
|
+
description: toolDescription,
|
|
66
|
+
schema: import_zod.z.object({
|
|
67
|
+
task: import_zod.z.string().describe("The detailed task description for the sub-agent to execute."),
|
|
68
|
+
context: import_zod.z.string().optional().describe("Optional additional context to help the sub-agent understand the task better.")
|
|
69
|
+
})
|
|
70
|
+
},
|
|
71
|
+
invoke: async (ctx, args, id) => {
|
|
72
|
+
const callingEmployee = resolveCallingEmployee(ctx);
|
|
73
|
+
if (callingEmployee && callingEmployee !== leaderUsername) {
|
|
74
|
+
await logDelegation(ctx, plugin, {
|
|
75
|
+
leaderUsername,
|
|
76
|
+
subAgentUsername,
|
|
77
|
+
toolName,
|
|
78
|
+
task: args.task,
|
|
79
|
+
context: args.context,
|
|
80
|
+
result: "",
|
|
81
|
+
status: "error",
|
|
82
|
+
depth: ctx[ORCHESTRATOR_DEPTH_KEY] ?? 0,
|
|
83
|
+
durationMs: 0,
|
|
84
|
+
error: `Employee "${callingEmployee}" is not authorized to use this delegation rule.`
|
|
85
|
+
});
|
|
86
|
+
return {
|
|
87
|
+
status: "error",
|
|
88
|
+
content: `Employee "${callingEmployee}" is not authorized to delegate to "${subAgentUsername}". Configure an orchestration rule first.`
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
return invokeDelegateTask(ctx, plugin, {
|
|
92
|
+
leaderUsername,
|
|
93
|
+
subAgentUsername,
|
|
94
|
+
subAgentEmployee,
|
|
95
|
+
task: args.task,
|
|
96
|
+
context: args.context,
|
|
97
|
+
maxDepth: maxDepth ?? 1,
|
|
98
|
+
timeout: timeout ?? 12e4,
|
|
99
|
+
toolCallId: id,
|
|
100
|
+
toolName,
|
|
101
|
+
llmService,
|
|
102
|
+
model
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
function resolveCallingEmployee(ctx) {
|
|
108
|
+
var _a, _b, _c;
|
|
109
|
+
const raw = ctx._currentAIEmployee || ((_a = ctx.state) == null ? void 0 : _a.currentAIEmployee) || ((_c = (_b = ctx.runtime) == null ? void 0 : _b.context) == null ? void 0 : _c.currentAIEmployee);
|
|
110
|
+
if (!raw) return null;
|
|
111
|
+
return typeof raw === "string" ? raw : raw.username;
|
|
112
|
+
}
|
|
113
|
+
function truncateText(value, maxLen) {
|
|
114
|
+
const text = typeof value === "string" ? value : value == null ? "" : JSON.stringify(value);
|
|
115
|
+
return text.length > maxLen ? `${text.slice(0, maxLen)}
|
|
116
|
+
...[truncated]` : text;
|
|
117
|
+
}
|
|
118
|
+
function nowIso() {
|
|
119
|
+
return (/* @__PURE__ */ new Date()).toISOString();
|
|
120
|
+
}
|
|
37
121
|
function createDelegateToolsProvider(plugin) {
|
|
38
122
|
return async (register) => {
|
|
39
123
|
try {
|
|
@@ -43,68 +127,59 @@ function createDelegateToolsProvider(plugin) {
|
|
|
43
127
|
filter: { enabled: true }
|
|
44
128
|
});
|
|
45
129
|
if (!(configs == null ? void 0 : configs.length)) return;
|
|
46
|
-
const
|
|
130
|
+
const employeeCache = /* @__PURE__ */ new Map();
|
|
131
|
+
const tools = [];
|
|
132
|
+
const configsBySubAgent = /* @__PURE__ */ new Map();
|
|
47
133
|
for (const config of configs) {
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
}
|
|
52
|
-
leadersByTool.get(toolName).add(config.leaderUsername);
|
|
134
|
+
const items = configsBySubAgent.get(config.subAgentUsername) || [];
|
|
135
|
+
items.push(config);
|
|
136
|
+
configsBySubAgent.set(config.subAgentUsername, items);
|
|
53
137
|
}
|
|
54
|
-
const seenSubAgents = /* @__PURE__ */ new Set();
|
|
55
138
|
for (const config of configs) {
|
|
56
139
|
const { leaderUsername, subAgentUsername, maxDepth, timeout } = config;
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
const toolDescription = [
|
|
65
|
-
`Delegate a task to the AI Employee "${subAgentEmployee.nickname || subAgentUsername}".`,
|
|
66
|
-
subAgentEmployee.about ? `Specialist profile: ${subAgentEmployee.about.substring(0, 200)}` : "",
|
|
67
|
-
"The sub-agent will execute the task independently and return its final answer."
|
|
68
|
-
].filter(Boolean).join(" ");
|
|
69
|
-
const allowedLeaders = leadersByTool.get(toolName);
|
|
70
|
-
register.registerTools({
|
|
71
|
-
scope: "CUSTOM",
|
|
72
|
-
execution: "backend",
|
|
73
|
-
defaultPermission: "ALLOW",
|
|
74
|
-
silence: false,
|
|
75
|
-
introduction: {
|
|
76
|
-
title: `[Sub-Agent] ${subAgentEmployee.nickname || subAgentUsername}`,
|
|
77
|
-
about: toolDescription
|
|
78
|
-
},
|
|
79
|
-
definition: {
|
|
80
|
-
name: toolName,
|
|
81
|
-
description: toolDescription,
|
|
82
|
-
schema: import_zod.z.object({
|
|
83
|
-
task: import_zod.z.string().describe("The detailed task description for the sub-agent to execute."),
|
|
84
|
-
context: import_zod.z.string().optional().describe("Optional additional context to help the sub-agent understand the task better.")
|
|
85
|
-
})
|
|
86
|
-
},
|
|
87
|
-
invoke: async (ctx, args, id) => {
|
|
88
|
-
var _a, _b;
|
|
89
|
-
const callingEmployee = ((_a = ctx._currentAIEmployee) == null ? void 0 : _a.username) || ((_b = ctx.state) == null ? void 0 : _b.currentAIEmployee);
|
|
90
|
-
if (callingEmployee && !allowedLeaders.has(callingEmployee)) {
|
|
91
|
-
return {
|
|
92
|
-
status: "error",
|
|
93
|
-
content: `Employee "${callingEmployee}" is not authorized to delegate to "${subAgentUsername}". Configure an orchestration rule first.`
|
|
94
|
-
};
|
|
95
|
-
}
|
|
96
|
-
return invokeDelegateTask(ctx, plugin, {
|
|
97
|
-
leaderUsername: callingEmployee || Array.from(allowedLeaders)[0] || "",
|
|
98
|
-
subAgentUsername,
|
|
99
|
-
subAgentEmployee,
|
|
100
|
-
task: args.task,
|
|
101
|
-
context: args.context,
|
|
102
|
-
maxDepth: maxDepth ?? 1,
|
|
103
|
-
timeout: timeout ?? 12e4,
|
|
104
|
-
toolCallId: id
|
|
105
|
-
});
|
|
140
|
+
let subAgentEmployee = employeeCache.get(subAgentUsername);
|
|
141
|
+
if (!subAgentEmployee) {
|
|
142
|
+
subAgentEmployee = await plugin.db.getRepository("aiEmployees").findOne({
|
|
143
|
+
filter: { username: subAgentUsername }
|
|
144
|
+
});
|
|
145
|
+
if (subAgentEmployee) {
|
|
146
|
+
employeeCache.set(subAgentUsername, subAgentEmployee);
|
|
106
147
|
}
|
|
107
|
-
}
|
|
148
|
+
}
|
|
149
|
+
if (!subAgentEmployee) continue;
|
|
150
|
+
const toolName = buildDelegateToolName(leaderUsername, subAgentUsername);
|
|
151
|
+
tools.push(createDelegateToolOptions(plugin, {
|
|
152
|
+
leaderUsername,
|
|
153
|
+
subAgentUsername,
|
|
154
|
+
subAgentEmployee,
|
|
155
|
+
maxDepth,
|
|
156
|
+
timeout,
|
|
157
|
+
toolName,
|
|
158
|
+
llmService: config.llmService,
|
|
159
|
+
model: config.model
|
|
160
|
+
}));
|
|
161
|
+
}
|
|
162
|
+
for (const [subAgentUsername, items] of configsBySubAgent.entries()) {
|
|
163
|
+
if (items.length !== 1) continue;
|
|
164
|
+
const config = items[0];
|
|
165
|
+
const subAgentEmployee = employeeCache.get(subAgentUsername);
|
|
166
|
+
if (!subAgentEmployee) continue;
|
|
167
|
+
const legacyToolName = `delegate_to_${sanitizeToolPart(subAgentUsername)}`;
|
|
168
|
+
if (tools.some((tool) => tool.definition.name === legacyToolName)) continue;
|
|
169
|
+
tools.push(createDelegateToolOptions(plugin, {
|
|
170
|
+
leaderUsername: config.leaderUsername,
|
|
171
|
+
subAgentUsername,
|
|
172
|
+
subAgentEmployee,
|
|
173
|
+
maxDepth: config.maxDepth,
|
|
174
|
+
timeout: config.timeout,
|
|
175
|
+
toolName: legacyToolName,
|
|
176
|
+
legacyAlias: true,
|
|
177
|
+
llmService: config.llmService,
|
|
178
|
+
model: config.model
|
|
179
|
+
}));
|
|
180
|
+
}
|
|
181
|
+
if (tools.length) {
|
|
182
|
+
register.registerTools(tools);
|
|
108
183
|
}
|
|
109
184
|
} catch (e) {
|
|
110
185
|
plugin.app.log.error("[AgentOrchestrator] Failed to register delegate tools", e);
|
|
@@ -112,24 +187,66 @@ function createDelegateToolsProvider(plugin) {
|
|
|
112
187
|
};
|
|
113
188
|
}
|
|
114
189
|
async function invokeDelegateTask(ctx, plugin, options) {
|
|
115
|
-
var _a, _b;
|
|
116
|
-
const { leaderUsername, subAgentUsername, subAgentEmployee, task, context, maxDepth, timeout, toolCallId } = options;
|
|
190
|
+
var _a, _b, _c, _d, _e;
|
|
191
|
+
const { leaderUsername, subAgentUsername, subAgentEmployee, task, context, maxDepth, timeout, toolCallId, toolName, llmService, model } = options;
|
|
117
192
|
const currentDepth = ctx[ORCHESTRATOR_DEPTH_KEY] ?? 0;
|
|
118
193
|
if (currentDepth >= maxDepth) {
|
|
194
|
+
await logDelegation(ctx, plugin, {
|
|
195
|
+
leaderUsername,
|
|
196
|
+
subAgentUsername,
|
|
197
|
+
toolName,
|
|
198
|
+
task,
|
|
199
|
+
context,
|
|
200
|
+
result: "",
|
|
201
|
+
status: "error",
|
|
202
|
+
depth: currentDepth,
|
|
203
|
+
durationMs: 0,
|
|
204
|
+
error: `Delegation depth limit reached (${currentDepth}/${maxDepth}).`
|
|
205
|
+
});
|
|
119
206
|
return {
|
|
120
207
|
status: "error",
|
|
121
208
|
content: `Delegation depth limit reached (${currentDepth}/${maxDepth}). Sub-agent "${subAgentUsername}" cannot delegate further.`
|
|
122
209
|
};
|
|
123
210
|
}
|
|
124
211
|
const startTime = Date.now();
|
|
212
|
+
const trace = [
|
|
213
|
+
{
|
|
214
|
+
type: "start",
|
|
215
|
+
at: nowIso(),
|
|
216
|
+
title: `Delegation started: ${leaderUsername} -> ${subAgentUsername}`,
|
|
217
|
+
content: task
|
|
218
|
+
}
|
|
219
|
+
];
|
|
220
|
+
const logRecord = await logDelegation(ctx, plugin, {
|
|
221
|
+
leaderUsername,
|
|
222
|
+
subAgentUsername,
|
|
223
|
+
toolName,
|
|
224
|
+
task,
|
|
225
|
+
context,
|
|
226
|
+
result: "",
|
|
227
|
+
status: "running",
|
|
228
|
+
depth: currentDepth,
|
|
229
|
+
durationMs: 0,
|
|
230
|
+
trace
|
|
231
|
+
});
|
|
125
232
|
try {
|
|
126
233
|
const aiPlugin = ctx.app.pm.get("ai");
|
|
127
234
|
if (!aiPlugin) {
|
|
128
235
|
throw new Error("Plugin AI is not installed or enabled");
|
|
129
236
|
}
|
|
130
|
-
|
|
237
|
+
let modelSettings = subAgentEmployee.modelSettings;
|
|
238
|
+
if (llmService && model) {
|
|
239
|
+
modelSettings = { llmService, model };
|
|
240
|
+
}
|
|
131
241
|
if (!(modelSettings == null ? void 0 : modelSettings.llmService) || !(modelSettings == null ? void 0 : modelSettings.model)) {
|
|
132
|
-
|
|
242
|
+
const leaderEmployee = await plugin.db.getRepository("aiEmployees").findOne({
|
|
243
|
+
filter: { username: leaderUsername }
|
|
244
|
+
});
|
|
245
|
+
const dynamicModel = (_c = (_b = (_a = ctx.action) == null ? void 0 : _a.params) == null ? void 0 : _b.values) == null ? void 0 : _c.model;
|
|
246
|
+
modelSettings = (leaderEmployee == null ? void 0 : leaderEmployee.modelSettings) || dynamicModel;
|
|
247
|
+
if (!(modelSettings == null ? void 0 : modelSettings.llmService) || !(modelSettings == null ? void 0 : modelSettings.model)) {
|
|
248
|
+
throw new Error(`Sub-agent "${subAgentUsername}" has no LLM model configured (and leader fallback failed). Please configure a model in the Orchestrator Config or AI Employee settings.`);
|
|
249
|
+
}
|
|
133
250
|
}
|
|
134
251
|
const { provider } = await aiPlugin.aiManager.getLLMService({
|
|
135
252
|
llmService: modelSettings.llmService,
|
|
@@ -138,27 +255,54 @@ async function invokeDelegateTask(ctx, plugin, options) {
|
|
|
138
255
|
const chatModel = provider.createModel();
|
|
139
256
|
const coreToolsManager = ctx.app.aiManager.toolsManager;
|
|
140
257
|
const allTools = await coreToolsManager.listTools();
|
|
141
|
-
const employeeSkills = (((
|
|
258
|
+
const employeeSkills = (((_d = subAgentEmployee.skillSettings) == null ? void 0 : _d.skills) ?? []).map((s) => typeof s === "string" ? s : s.name).filter(Boolean);
|
|
142
259
|
const langchainTools = [];
|
|
143
260
|
for (const toolEntry of allTools) {
|
|
144
|
-
const
|
|
145
|
-
if (!
|
|
146
|
-
if (!employeeSkills.includes(
|
|
261
|
+
const toolName2 = toolEntry.definition.name;
|
|
262
|
+
if (!toolName2) continue;
|
|
263
|
+
if (!employeeSkills.includes(toolName2) || isDelegateToolName(toolName2)) {
|
|
147
264
|
continue;
|
|
148
265
|
}
|
|
149
266
|
langchainTools.push(
|
|
150
267
|
new import_tools.DynamicStructuredTool({
|
|
151
|
-
name:
|
|
152
|
-
description: toolEntry.definition.description ||
|
|
268
|
+
name: toolName2.replace(/[^a-zA-Z0-9_-]/g, "_"),
|
|
269
|
+
description: toolEntry.definition.description || toolName2,
|
|
153
270
|
schema: toolEntry.definition.schema || import_zod.z.object({}),
|
|
154
271
|
func: async (toolArgs) => {
|
|
155
272
|
const invokeCtx = Object.create(ctx);
|
|
156
273
|
invokeCtx[ORCHESTRATOR_DEPTH_KEY] = currentDepth + 1;
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
274
|
+
trace.push({
|
|
275
|
+
type: "tool_call",
|
|
276
|
+
at: nowIso(),
|
|
277
|
+
title: `Calling tool: ${toolEntry.definition.name}`,
|
|
278
|
+
toolName: toolEntry.definition.name,
|
|
279
|
+
args: toolArgs
|
|
280
|
+
});
|
|
281
|
+
try {
|
|
282
|
+
const res = await toolEntry.invoke(invokeCtx, toolArgs, `orch-${toolCallId}`);
|
|
283
|
+
trace.push({
|
|
284
|
+
type: "tool_result",
|
|
285
|
+
at: nowIso(),
|
|
286
|
+
title: `Tool finished: ${toolEntry.definition.name}`,
|
|
287
|
+
toolName: toolEntry.definition.name,
|
|
288
|
+
status: (res == null ? void 0 : res.status) || "success",
|
|
289
|
+
content: truncateText((res == null ? void 0 : res.content) ?? res, 2e3)
|
|
290
|
+
});
|
|
291
|
+
if ((res == null ? void 0 : res.status) === "error") {
|
|
292
|
+
throw new Error(`Tool <${toolEntry.definition.name}> failed: ${res.content}`);
|
|
293
|
+
}
|
|
294
|
+
return typeof (res == null ? void 0 : res.content) === "string" ? res.content : JSON.stringify(res);
|
|
295
|
+
} catch (e) {
|
|
296
|
+
trace.push({
|
|
297
|
+
type: "tool_error",
|
|
298
|
+
at: nowIso(),
|
|
299
|
+
title: `Tool failed: ${toolEntry.definition.name}`,
|
|
300
|
+
toolName: toolEntry.definition.name,
|
|
301
|
+
status: "error",
|
|
302
|
+
content: e.message
|
|
303
|
+
});
|
|
304
|
+
throw e;
|
|
160
305
|
}
|
|
161
|
-
return typeof (res == null ? void 0 : res.content) === "string" ? res.content : JSON.stringify(res);
|
|
162
306
|
}
|
|
163
307
|
})
|
|
164
308
|
);
|
|
@@ -168,7 +312,7 @@ async function invokeDelegateTask(ctx, plugin, options) {
|
|
|
168
312
|
llm: chatModel,
|
|
169
313
|
tools: langchainTools
|
|
170
314
|
});
|
|
171
|
-
const systemPrompt = ((
|
|
315
|
+
const systemPrompt = ((_e = subAgentEmployee.chatSettings) == null ? void 0 : _e.systemPrompt) || subAgentEmployee.bio || `You are an AI assistant named "${subAgentEmployee.nickname || subAgentUsername}". ${subAgentEmployee.about || ""}`;
|
|
172
316
|
const combinedTask = context ? `Task: ${task}
|
|
173
317
|
|
|
174
318
|
Context Provided:
|
|
@@ -178,15 +322,27 @@ ${context}` : `Task: ${task}`;
|
|
|
178
322
|
invokePromise,
|
|
179
323
|
createTimeout(timeout, subAgentUsername, abortController)
|
|
180
324
|
]);
|
|
181
|
-
const content = result || "Sub-agent completed the task but produced no output.";
|
|
325
|
+
const content = result.content || "Sub-agent completed the task but produced no output.";
|
|
326
|
+
trace.push({
|
|
327
|
+
type: "finish",
|
|
328
|
+
at: nowIso(),
|
|
329
|
+
title: `Delegation finished: ${subAgentUsername}`,
|
|
330
|
+
status: "success",
|
|
331
|
+
content: truncateText(content, 2e3)
|
|
332
|
+
});
|
|
182
333
|
await logDelegation(ctx, plugin, {
|
|
334
|
+
id: logRecord == null ? void 0 : logRecord.id,
|
|
183
335
|
leaderUsername,
|
|
184
336
|
subAgentUsername,
|
|
337
|
+
toolName,
|
|
185
338
|
task,
|
|
339
|
+
context,
|
|
186
340
|
result: content,
|
|
187
341
|
status: "success",
|
|
188
342
|
depth: currentDepth,
|
|
189
|
-
durationMs: Date.now() - startTime
|
|
343
|
+
durationMs: Date.now() - startTime,
|
|
344
|
+
trace,
|
|
345
|
+
messages: result.messages
|
|
190
346
|
});
|
|
191
347
|
return {
|
|
192
348
|
status: "success",
|
|
@@ -195,15 +351,29 @@ ${context}` : `Task: ${task}`;
|
|
|
195
351
|
} catch (e) {
|
|
196
352
|
plugin.app.log.error(`[AgentOrchestrator] Sub-agent ${subAgentUsername} failed`, e);
|
|
197
353
|
await logDelegation(ctx, plugin, {
|
|
354
|
+
id: logRecord == null ? void 0 : logRecord.id,
|
|
198
355
|
leaderUsername,
|
|
199
356
|
subAgentUsername,
|
|
357
|
+
toolName,
|
|
200
358
|
task,
|
|
359
|
+
context,
|
|
201
360
|
result: "",
|
|
202
361
|
status: "error",
|
|
203
362
|
depth: currentDepth,
|
|
204
363
|
durationMs: Date.now() - startTime,
|
|
205
|
-
error: e.message
|
|
206
|
-
|
|
364
|
+
error: e.message,
|
|
365
|
+
trace: [
|
|
366
|
+
...trace,
|
|
367
|
+
{
|
|
368
|
+
type: "error",
|
|
369
|
+
at: nowIso(),
|
|
370
|
+
title: `Delegation failed: ${subAgentUsername}`,
|
|
371
|
+
status: "error",
|
|
372
|
+
content: e.message
|
|
373
|
+
}
|
|
374
|
+
]
|
|
375
|
+
}).catch((logErr) => {
|
|
376
|
+
plugin.app.log.warn("[AgentOrchestrator] Failed to save error log for delegation", logErr);
|
|
207
377
|
});
|
|
208
378
|
return {
|
|
209
379
|
status: "error",
|
|
@@ -212,24 +382,48 @@ ${context}` : `Task: ${task}`;
|
|
|
212
382
|
}
|
|
213
383
|
}
|
|
214
384
|
async function logDelegation(ctx, plugin, data) {
|
|
215
|
-
var _a, _b, _c, _d;
|
|
385
|
+
var _a, _b, _c, _d, _e;
|
|
216
386
|
try {
|
|
217
387
|
const logsRepo = plugin.db.getRepository("orchestratorLogs");
|
|
218
|
-
if (!logsRepo)
|
|
219
|
-
|
|
388
|
+
if (!logsRepo) {
|
|
389
|
+
plugin.app.log.warn("[AgentOrchestrator] orchestratorLogs repository not found \u2014 skipping log");
|
|
390
|
+
return;
|
|
391
|
+
}
|
|
392
|
+
let userId;
|
|
393
|
+
try {
|
|
394
|
+
userId = ((_b = (_a = ctx.auth) == null ? void 0 : _a.user) == null ? void 0 : _b.id) || ((_d = (_c = ctx.state) == null ? void 0 : _c.currentUser) == null ? void 0 : _d.id);
|
|
395
|
+
} catch {
|
|
396
|
+
}
|
|
397
|
+
const values = {
|
|
398
|
+
leaderUsername: data.leaderUsername,
|
|
399
|
+
subAgentUsername: data.subAgentUsername,
|
|
400
|
+
toolName: data.toolName,
|
|
401
|
+
task: truncateText(data.task, 1e4),
|
|
402
|
+
context: truncateText(data.context || "", 1e4),
|
|
403
|
+
result: truncateText(data.result || "", 5e4),
|
|
404
|
+
status: data.status,
|
|
405
|
+
depth: data.depth,
|
|
406
|
+
durationMs: data.durationMs,
|
|
407
|
+
error: truncateText(data.error || "", 1e4),
|
|
408
|
+
trace: data.trace || [],
|
|
409
|
+
messages: data.messages || [],
|
|
410
|
+
userId,
|
|
411
|
+
updatedAt: /* @__PURE__ */ new Date()
|
|
412
|
+
};
|
|
413
|
+
if (data.id) {
|
|
414
|
+
await logsRepo.update({
|
|
415
|
+
filterByTk: data.id,
|
|
416
|
+
values
|
|
417
|
+
});
|
|
418
|
+
return { id: data.id };
|
|
419
|
+
}
|
|
420
|
+
const record = await logsRepo.create({
|
|
220
421
|
values: {
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
toolName: `delegate_to_${data.subAgentUsername}`,
|
|
224
|
-
task: (data.task || "").substring(0, 2e3),
|
|
225
|
-
result: (data.result || "").substring(0, 5e3),
|
|
226
|
-
status: data.status,
|
|
227
|
-
depth: data.depth,
|
|
228
|
-
durationMs: data.durationMs,
|
|
229
|
-
error: (data.error || "").substring(0, 2e3),
|
|
230
|
-
userId: ((_b = (_a = ctx.auth) == null ? void 0 : _a.user) == null ? void 0 : _b.id) || ((_d = (_c = ctx.state) == null ? void 0 : _c.currentUser) == null ? void 0 : _d.id)
|
|
422
|
+
...values,
|
|
423
|
+
createdAt: /* @__PURE__ */ new Date()
|
|
231
424
|
}
|
|
232
425
|
});
|
|
426
|
+
return ((_e = record == null ? void 0 : record.toJSON) == null ? void 0 : _e.call(record)) || record;
|
|
233
427
|
} catch (e) {
|
|
234
428
|
plugin.app.log.warn("[AgentOrchestrator] Failed to log delegation event", e);
|
|
235
429
|
}
|
|
@@ -248,15 +442,32 @@ async function executeAgent(executor, systemPrompt, task, signal) {
|
|
|
248
442
|
const messages = (finalState == null ? void 0 : finalState.messages) || [];
|
|
249
443
|
const lastAIMessage = [...messages].reverse().find((m) => m.getType() === "ai");
|
|
250
444
|
if (!lastAIMessage || !lastAIMessage.content) {
|
|
251
|
-
return "";
|
|
445
|
+
return { content: "", messages: serializeMessages(messages) };
|
|
252
446
|
}
|
|
447
|
+
let content = "";
|
|
253
448
|
if (typeof lastAIMessage.content === "string") {
|
|
254
|
-
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
|
|
449
|
+
content = lastAIMessage.content;
|
|
450
|
+
} else if (Array.isArray(lastAIMessage.content)) {
|
|
451
|
+
content = lastAIMessage.content.map((c) => c.text || JSON.stringify(c)).join("\n");
|
|
452
|
+
} else {
|
|
453
|
+
content = String(lastAIMessage.content);
|
|
258
454
|
}
|
|
259
|
-
return
|
|
455
|
+
return { content, messages: serializeMessages(messages) };
|
|
456
|
+
}
|
|
457
|
+
function serializeMessages(messages) {
|
|
458
|
+
return (messages || []).map((message, index) => {
|
|
459
|
+
const type = typeof message.getType === "function" ? message.getType() : message.type;
|
|
460
|
+
return {
|
|
461
|
+
index,
|
|
462
|
+
type,
|
|
463
|
+
name: message.name,
|
|
464
|
+
content: truncateText(message.content, 1e4),
|
|
465
|
+
toolCalls: message.tool_calls || message.toolCalls || [],
|
|
466
|
+
toolCallId: message.tool_call_id,
|
|
467
|
+
additionalKwargs: message.additional_kwargs,
|
|
468
|
+
responseMetadata: message.response_metadata
|
|
469
|
+
};
|
|
470
|
+
});
|
|
260
471
|
}
|
|
261
472
|
function createTimeout(ms, agentName, abortController) {
|
|
262
473
|
return new Promise(
|
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"displayName.zh-CN": "代理协调器",
|
|
5
5
|
"displayName.vi-VN": "Điều phối Agent",
|
|
6
6
|
"description": "Hierarchical Multi-Agent orchestration for NocoBase AI Employees. Enables Leader agents to delegate tasks to Sub-Agent employees without modifying core plugin-ai.",
|
|
7
|
-
"version": "1.0.
|
|
7
|
+
"version": "1.0.13",
|
|
8
8
|
"license": "Apache-2.0",
|
|
9
9
|
"main": "dist/server/index.js",
|
|
10
10
|
"keywords": [
|