plugin-agent-orchestrator 1.0.17 → 1.0.18
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/AIEmployeeSelect.d.ts +11 -0
- package/dist/client/AIEmployeesContext.d.ts +30 -0
- package/dist/client/AgentRunsTab.d.ts +2 -0
- package/dist/client/HarnessProfilesTab.d.ts +2 -0
- package/dist/client/OrchestratorSettings.d.ts +3 -0
- package/dist/client/RulesTab.d.ts +2 -0
- package/dist/client/TracingTab.d.ts +2 -0
- package/dist/client/index.d.ts +1 -0
- package/dist/client/index.js +1 -1
- package/dist/client/plugin.d.ts +6 -0
- package/dist/client/skill-hub/components/ExecutionHistory.d.ts +2 -0
- package/dist/client/skill-hub/components/ExecutionProgress.d.ts +20 -0
- package/dist/client/skill-hub/components/GitSkillImport.d.ts +7 -0
- package/dist/client/skill-hub/components/LoopSettings.d.ts +2 -0
- package/dist/client/skill-hub/components/SkillEditor.d.ts +7 -0
- package/dist/client/skill-hub/components/SkillManager.d.ts +2 -0
- package/dist/client/skill-hub/components/SkillMetrics.d.ts +2 -0
- package/dist/client/skill-hub/components/SkillTestPanel.d.ts +7 -0
- package/dist/client/skill-hub/index.d.ts +11 -0
- package/dist/client/skill-hub/locale.d.ts +3 -0
- package/dist/client/skill-hub/tools/InteractionSchemasProvider.d.ts +6 -0
- package/dist/client/skill-hub/tools/SkillHubCard.d.ts +3 -0
- package/dist/client/skill-hub/tools/loopTemplates.d.ts +22 -0
- package/dist/client/skill-hub/tools/registerSkillLoopCards.d.ts +1 -0
- package/dist/client/skill-hub/utils/jsonFields.d.ts +3 -0
- package/dist/client/tools/PlanApprovalCard.d.ts +3 -0
- package/dist/client/tools/registerOrchestratorCards.d.ts +1 -0
- package/dist/externalVersion.js +6 -6
- package/dist/index.d.ts +2 -0
- package/dist/server/collections/agent-execution-spans.d.ts +9 -0
- package/dist/server/collections/agent-harness-profiles.d.ts +2 -0
- package/dist/server/collections/agent-harness-profiles.js +89 -0
- package/dist/server/collections/agent-loop-events.d.ts +2 -0
- package/dist/server/collections/agent-loop-events.js +101 -0
- package/dist/server/collections/agent-loop-runs.d.ts +2 -0
- package/dist/server/collections/agent-loop-runs.js +188 -0
- package/dist/server/collections/agent-loop-steps.d.ts +2 -0
- package/dist/server/collections/agent-loop-steps.js +174 -0
- package/dist/server/collections/orchestrator-config.d.ts +2 -0
- package/dist/server/collections/orchestrator-config.js +7 -0
- package/dist/server/collections/orchestrator-logs.d.ts +8 -0
- package/dist/server/collections/skill-definitions.d.ts +3 -0
- package/dist/server/collections/skill-executions.d.ts +3 -0
- package/dist/server/collections/skill-executions.js +12 -0
- package/dist/server/collections/skill-loop-configs.d.ts +3 -0
- package/dist/server/collections/skill-loop-configs.js +94 -0
- package/dist/server/collections/skill-worker-configs.d.ts +3 -0
- package/dist/server/index.d.ts +1 -0
- package/dist/server/migrations/20260423000000-add-progress-fields.d.ts +4 -0
- package/dist/server/migrations/20260425000000-add-interaction-schema.d.ts +4 -0
- package/dist/server/migrations/20260427000000-add-tracing-detail-fields.d.ts +7 -0
- package/dist/server/migrations/20260427000000-change-packages-to-text.d.ts +4 -0
- package/dist/server/migrations/20260427000001-change-other-json-to-text.d.ts +4 -0
- package/dist/server/migrations/20260429000000-add-llm-fields.d.ts +7 -0
- package/dist/server/migrations/20260429000000-fix-inputargs-json-to-text.d.ts +16 -0
- package/dist/server/migrations/20260503000000-add-orchestrator-trace-fields.d.ts +7 -0
- package/dist/server/migrations/20260524000000-add-agent-loop-fields-to-skill-executions.d.ts +7 -0
- package/dist/server/migrations/20260524000000-add-agent-loop-fields-to-skill-executions.js +55 -0
- package/dist/server/migrations/20260524001000-add-plan-approval-and-harness-profiles.d.ts +12 -0
- package/dist/server/migrations/20260524001000-add-plan-approval-and-harness-profiles.js +162 -0
- package/dist/server/plugin.d.ts +16 -0
- package/dist/server/plugin.js +13 -0
- package/dist/server/resources/agent-loop.d.ts +3 -0
- package/dist/server/resources/agent-loop.js +205 -0
- package/dist/server/resources/tracing.d.ts +7 -0
- package/dist/server/services/AgentHarness.d.ts +42 -0
- package/dist/server/services/AgentHarness.js +565 -0
- package/dist/server/services/AgentLoopController.d.ts +205 -0
- package/dist/server/services/AgentLoopController.js +940 -0
- package/dist/server/services/AgentLoopRepository.d.ts +20 -0
- package/dist/server/services/AgentLoopRepository.js +210 -0
- package/dist/server/services/AgentLoopService.d.ts +149 -0
- package/dist/server/services/AgentLoopService.js +133 -0
- package/dist/server/services/AgentPlanValidator.d.ts +4 -0
- package/dist/server/services/AgentPlanValidator.js +99 -0
- package/dist/server/services/AgentPlannerService.d.ts +8 -0
- package/dist/server/services/AgentPlannerService.js +119 -0
- package/dist/server/services/AgentRegistryService.d.ts +13 -0
- package/dist/server/services/AgentRegistryService.js +178 -0
- package/dist/server/services/CodeValidator.d.ts +32 -0
- package/dist/server/services/ExecutionSpanService.d.ts +46 -0
- package/dist/server/services/FileManager.d.ts +28 -0
- package/dist/server/services/SandboxRunner.d.ts +41 -0
- package/dist/server/services/SkillManager.d.ts +6 -0
- package/dist/server/services/SkillRepositoryService.d.ts +22 -0
- package/dist/server/services/WorkerEnvManager.d.ts +26 -0
- package/dist/server/skill-hub/actions/git-import.d.ts +21 -0
- package/dist/server/skill-hub/mcp/McpController.d.ts +15 -0
- package/dist/server/skill-hub/plugin.d.ts +61 -0
- package/dist/server/skill-hub/plugin.js +137 -54
- package/dist/server/skill-hub/tasks/SkillExecutionTask.d.ts +16 -0
- package/dist/server/skill-hub/utils/json-fields.d.ts +7 -0
- package/dist/server/tools/agent-loop.d.ts +235 -0
- package/dist/server/tools/agent-loop.js +406 -0
- package/dist/server/tools/delegate-task.d.ts +19 -0
- package/dist/server/tools/delegate-task.js +19 -368
- package/dist/server/tools/external-rag-search.d.ts +42 -0
- package/dist/server/tools/orchestrator-plan.d.ts +205 -0
- package/dist/server/tools/orchestrator-plan.js +291 -0
- package/dist/server/tools/skill-execute.d.ts +36 -0
- package/dist/server/tools/skill-execute.js +2 -0
- package/package.json +1 -1
- package/src/client/AgentRunsTab.tsx +764 -0
- package/src/client/HarnessProfilesTab.tsx +247 -0
- package/src/client/OrchestratorSettings.tsx +40 -2
- package/src/client/RulesTab.tsx +103 -6
- package/src/client/plugin.tsx +27 -54
- package/src/client/skill-hub/components/LoopSettings.tsx +331 -0
- package/src/client/skill-hub/index.tsx +51 -75
- package/src/client/skill-hub/tools/InteractionSchemasProvider.tsx +56 -16
- package/src/client/skill-hub/tools/SkillHubCard.tsx +35 -4
- package/src/client/skill-hub/tools/loopTemplates.ts +52 -0
- package/src/client/skill-hub/tools/registerSkillLoopCards.ts +58 -0
- package/src/client/tools/PlanApprovalCard.tsx +175 -0
- package/src/client/tools/registerOrchestratorCards.ts +7 -0
- package/src/server/collections/agent-harness-profiles.ts +59 -0
- package/src/server/collections/agent-loop-events.ts +71 -0
- package/src/server/collections/agent-loop-runs.ts +158 -0
- package/src/server/collections/agent-loop-steps.ts +144 -0
- package/src/server/collections/orchestrator-config.ts +7 -0
- package/src/server/collections/skill-executions.ts +63 -51
- package/src/server/collections/skill-loop-configs.ts +65 -0
- package/src/server/migrations/20260524000000-add-agent-loop-fields-to-skill-executions.ts +30 -0
- package/src/server/migrations/20260524001000-add-plan-approval-and-harness-profiles.ts +142 -0
- package/src/server/plugin.ts +15 -0
- package/src/server/resources/agent-loop.ts +183 -0
- package/src/server/services/AgentHarness.ts +663 -0
- package/src/server/services/AgentLoopController.ts +1128 -0
- package/src/server/services/AgentLoopRepository.ts +194 -0
- package/src/server/services/AgentLoopService.ts +161 -0
- package/src/server/services/AgentPlanValidator.ts +73 -0
- package/src/server/services/AgentPlannerService.ts +93 -0
- package/src/server/services/AgentRegistryService.ts +169 -0
- package/src/server/services/ExecutionSpanService.ts +2 -0
- package/src/server/skill-hub/plugin.ts +881 -771
- package/src/server/tools/agent-loop.ts +399 -0
- package/src/server/tools/delegate-task.ts +23 -485
- package/src/server/tools/orchestrator-plan.ts +279 -0
- package/src/server/tools/skill-execute.ts +68 -64
|
@@ -24,7 +24,6 @@ const ORCHESTRATOR_DEPTH_KEY = '__orchestratorDepth';
|
|
|
24
24
|
*/
|
|
25
25
|
const ORCHESTRATOR_PATH_KEY = '__orchestratorPath';
|
|
26
26
|
|
|
27
|
-
|
|
28
27
|
/** Max sub-agents that the dispatch tool runs concurrently in one call. */
|
|
29
28
|
const MAX_DISPATCH_CONCURRENCY = 5;
|
|
30
29
|
/** Hard cap on tasks per dispatch call to keep output bounded. */
|
|
@@ -65,10 +64,7 @@ function buildDispatchToolName(leaderUsername: string) {
|
|
|
65
64
|
}
|
|
66
65
|
|
|
67
66
|
function createRootRunId(seed = '') {
|
|
68
|
-
const hash = createHash('sha1')
|
|
69
|
-
.update(`${Date.now()}::${Math.random()}::${seed}`)
|
|
70
|
-
.digest('hex')
|
|
71
|
-
.slice(0, 10);
|
|
67
|
+
const hash = createHash('sha1').update(`${Date.now()}::${Math.random()}::${seed}`).digest('hex').slice(0, 10);
|
|
72
68
|
return `run_${Date.now()}_${hash}`;
|
|
73
69
|
}
|
|
74
70
|
|
|
@@ -148,7 +144,7 @@ function createDelegateToolOptions(
|
|
|
148
144
|
return {
|
|
149
145
|
scope: 'CUSTOM',
|
|
150
146
|
execution: 'backend',
|
|
151
|
-
defaultPermission: '
|
|
147
|
+
defaultPermission: 'ASK',
|
|
152
148
|
silence: false,
|
|
153
149
|
introduction: {
|
|
154
150
|
title: `[${leaderUsername}] ${subAgentEmployee.nickname || subAgentUsername}${legacyAlias ? ' (legacy)' : ''}`,
|
|
@@ -295,7 +291,7 @@ function createDispatchToolOptions(
|
|
|
295
291
|
return {
|
|
296
292
|
scope: 'CUSTOM',
|
|
297
293
|
execution: 'backend',
|
|
298
|
-
defaultPermission: '
|
|
294
|
+
defaultPermission: 'ASK',
|
|
299
295
|
silence: false,
|
|
300
296
|
introduction: {
|
|
301
297
|
title: `[${leaderUsername}] Dispatch sub-agents`,
|
|
@@ -794,13 +790,9 @@ async function invokeDelegateTask(
|
|
|
794
790
|
} = options;
|
|
795
791
|
|
|
796
792
|
// --- Snapshot ctx fields up-front ---
|
|
797
|
-
// Long-running agent execution (up to `timeout` ms) outlives the parent HTTP
|
|
798
|
-
// request, so middleware may have cleared `ctx.auth`, `ctx.state`, or even
|
|
799
|
-
// disposed the underlying socket by the time we finalize the log row.
|
|
800
|
-
// Capturing the values once here keeps log/audit fields stable.
|
|
801
793
|
const ctxSnapshot = captureCtxSnapshot(ctx);
|
|
802
794
|
|
|
803
|
-
// ---
|
|
795
|
+
// --- Depth enforcement & Circular Delegation Detection ---
|
|
804
796
|
const currentDepth: number = (ctx as any)[ORCHESTRATOR_DEPTH_KEY] ?? 0;
|
|
805
797
|
const currentPath: string[] = (ctx as any)[ORCHESTRATOR_PATH_KEY] ?? [leaderUsername];
|
|
806
798
|
|
|
@@ -845,396 +837,33 @@ async function invokeDelegateTask(
|
|
|
845
837
|
};
|
|
846
838
|
}
|
|
847
839
|
|
|
848
|
-
const spanService = new ExecutionSpanService(plugin);
|
|
849
840
|
const upstreamTraceContext = getOrchestratorTraceContext(ctx);
|
|
850
841
|
const rootRunId =
|
|
851
842
|
providedRootRunId || upstreamTraceContext?.rootRunId || createRootRunId(`${leaderUsername}:${subAgentUsername}`);
|
|
852
843
|
const parentSpanId = providedParentSpanId || upstreamTraceContext?.spanId || upstreamTraceContext?.parentSpanId;
|
|
853
|
-
const
|
|
854
|
-
const
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
at: nowIso(),
|
|
858
|
-
title: `Delegation started: ${leaderUsername} -> ${subAgentUsername}`,
|
|
859
|
-
content: task,
|
|
860
|
-
},
|
|
861
|
-
];
|
|
862
|
-
const executionSpan = await spanService.create({
|
|
863
|
-
rootRunId,
|
|
864
|
-
parentSpanId,
|
|
865
|
-
type: 'sub_agent',
|
|
866
|
-
status: 'running',
|
|
867
|
-
leaderUsername,
|
|
868
|
-
employeeUsername: subAgentUsername,
|
|
869
|
-
title: `Delegation: ${leaderUsername} -> ${subAgentUsername}`,
|
|
870
|
-
input: { task, context },
|
|
871
|
-
metadata: {
|
|
872
|
-
depth: currentDepth,
|
|
873
|
-
maxDepth,
|
|
874
|
-
toolName,
|
|
875
|
-
recursionLimit,
|
|
876
|
-
llmOverride: llmService && model ? { llmService, model } : undefined,
|
|
877
|
-
},
|
|
878
|
-
userId: ctxSnapshot.userId,
|
|
879
|
-
});
|
|
880
|
-
const executionSpanId = executionSpan?.id ? String(executionSpan.id) : undefined;
|
|
881
|
-
const logRecord = await logDelegation(ctx, plugin, {
|
|
844
|
+
const agentLoopRunId = upstreamTraceContext?.agentLoopRunId;
|
|
845
|
+
const agentLoopStepId = upstreamTraceContext?.agentLoopStepId;
|
|
846
|
+
|
|
847
|
+
return plugin.agentLoopService.harness.runSubAgent(ctx, {
|
|
882
848
|
leaderUsername,
|
|
883
849
|
subAgentUsername,
|
|
884
|
-
|
|
850
|
+
subAgentEmployee,
|
|
885
851
|
task,
|
|
886
852
|
context,
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
853
|
+
currentDepth,
|
|
854
|
+
currentPath,
|
|
855
|
+
maxDepth,
|
|
856
|
+
timeout,
|
|
857
|
+
toolCallId,
|
|
858
|
+
toolName,
|
|
859
|
+
llmService,
|
|
860
|
+
model,
|
|
861
|
+
recursionLimit,
|
|
862
|
+
rootRunId,
|
|
863
|
+
parentSpanId,
|
|
864
|
+
agentLoopRunId,
|
|
865
|
+
agentLoopStepId,
|
|
893
866
|
});
|
|
894
|
-
if (executionSpanId && logRecord?.id) {
|
|
895
|
-
await spanService.update(executionSpanId, { orchestratorLogId: logRecord.id });
|
|
896
|
-
}
|
|
897
|
-
|
|
898
|
-
try {
|
|
899
|
-
const aiPlugin = ctx.app.pm.get('ai') as PluginAIServer;
|
|
900
|
-
if (!aiPlugin) {
|
|
901
|
-
throw new Error('Plugin AI is not installed or enabled');
|
|
902
|
-
}
|
|
903
|
-
|
|
904
|
-
// --- Step 1: Resolve LLM model from sub-agent's employee config ---
|
|
905
|
-
let modelSettings = hasModelSettings(subAgentEmployee.modelSettings) ? subAgentEmployee.modelSettings : undefined;
|
|
906
|
-
|
|
907
|
-
// Override with orchestrator config if provided
|
|
908
|
-
if (llmService && model) {
|
|
909
|
-
modelSettings = { llmService, model };
|
|
910
|
-
}
|
|
911
|
-
|
|
912
|
-
if (!hasModelSettings(modelSettings)) {
|
|
913
|
-
// Fallback to leader's LLM model if sub-agent doesn't have one
|
|
914
|
-
const leaderEmployee = await plugin.db.getRepository('aiEmployees').findOne({
|
|
915
|
-
filter: { username: leaderUsername },
|
|
916
|
-
});
|
|
917
|
-
|
|
918
|
-
// The leader's model might be empty in the DB if it relies on the dynamic system default.
|
|
919
|
-
// In that case, we extract the dynamic `model` passed from the frontend request.
|
|
920
|
-
const dynamicModel = ctx.action?.params?.values?.model;
|
|
921
|
-
modelSettings = hasModelSettings(leaderEmployee?.modelSettings)
|
|
922
|
-
? leaderEmployee.modelSettings
|
|
923
|
-
: hasModelSettings(dynamicModel)
|
|
924
|
-
? dynamicModel
|
|
925
|
-
: undefined;
|
|
926
|
-
|
|
927
|
-
if (!hasModelSettings(modelSettings)) {
|
|
928
|
-
throw new Error(
|
|
929
|
-
`Sub-agent "${subAgentUsername}" has no LLM model configured (and leader fallback failed). Please configure a model in the Orchestrator Config or AI Employee settings.`,
|
|
930
|
-
);
|
|
931
|
-
}
|
|
932
|
-
}
|
|
933
|
-
|
|
934
|
-
const { provider } = await aiPlugin.aiManager.getLLMService({
|
|
935
|
-
llmService: modelSettings.llmService,
|
|
936
|
-
model: modelSettings.model,
|
|
937
|
-
});
|
|
938
|
-
const chatModel = provider.createModel();
|
|
939
|
-
|
|
940
|
-
// --- Step 2: Resolve tools via CORE toolsManager ---
|
|
941
|
-
// Uses app.aiManager.toolsManager (same as AIEmployee.getToolsMap at ai-employee.ts:1286)
|
|
942
|
-
// NOT plugin-ai's local toolManager (which has a different grouped format).
|
|
943
|
-
const coreToolsManager = ctx.app.aiManager.toolsManager;
|
|
944
|
-
const allTools: ToolsEntry[] = await coreToolsManager.listTools();
|
|
945
|
-
|
|
946
|
-
// skillSettings.skills is { name: string, autoCall: boolean }[]
|
|
947
|
-
// (verified at ai-employee.ts:1028-1029)
|
|
948
|
-
const employeeSkills: EmployeeSkillConfig[] = (subAgentEmployee.skillSettings?.skills ?? [])
|
|
949
|
-
.map((s: any) =>
|
|
950
|
-
typeof s === 'string'
|
|
951
|
-
? { name: s, autoCall: false }
|
|
952
|
-
: { name: s?.name, autoCall: s?.autoCall === true },
|
|
953
|
-
)
|
|
954
|
-
.filter((s: EmployeeSkillConfig) => Boolean(s.name));
|
|
955
|
-
const employeeSkillMap = new Map(employeeSkills.map((skill) => [skill.name, skill]));
|
|
956
|
-
|
|
957
|
-
const langchainTools: DynamicStructuredTool[] = [];
|
|
958
|
-
|
|
959
|
-
for (const toolEntry of allTools) {
|
|
960
|
-
const entryName = toolEntry.definition.name;
|
|
961
|
-
if (!entryName) continue;
|
|
962
|
-
const employeeSkill = employeeSkillMap.get(entryName);
|
|
963
|
-
|
|
964
|
-
// Only include tools that the sub-agent employee is configured to use.
|
|
965
|
-
// Also skip our own orchestration tools to prevent circular delegation
|
|
966
|
-
// (belt-and-suspenders with the depth check above).
|
|
967
|
-
//
|
|
968
|
-
// Headless sub-agent execution has no human confirmation surface, so we
|
|
969
|
-
// require both the employee assignment and the tool definition to be
|
|
970
|
-
// explicitly auto-callable. This prevents ASK/interactionSchema Skill Hub
|
|
971
|
-
// tools from being executed silently by a delegated sub-agent.
|
|
972
|
-
if (
|
|
973
|
-
!employeeSkill ||
|
|
974
|
-
isDelegateToolName(plugin, entryName) ||
|
|
975
|
-
employeeSkill.autoCall !== true
|
|
976
|
-
) {
|
|
977
|
-
continue;
|
|
978
|
-
}
|
|
979
|
-
|
|
980
|
-
langchainTools.push(
|
|
981
|
-
new DynamicStructuredTool({
|
|
982
|
-
name: entryName.replace(/[^a-zA-Z0-9_-]/g, '_'),
|
|
983
|
-
description: toolEntry.definition.description || entryName,
|
|
984
|
-
schema: (toolEntry.definition.schema || z.object({})) as any,
|
|
985
|
-
func: async (toolArgs) => {
|
|
986
|
-
// Forward the invoke with depth tracking, circular path tracking and identity overrides
|
|
987
|
-
const invokeCtx = Object.create(ctx);
|
|
988
|
-
(invokeCtx as any)[ORCHESTRATOR_DEPTH_KEY] = currentDepth + 1;
|
|
989
|
-
(invokeCtx as any)[ORCHESTRATOR_PATH_KEY] = [...currentPath, subAgentUsername];
|
|
990
|
-
(invokeCtx as any)._currentAIEmployee = subAgentUsername;
|
|
991
|
-
if (ctx.state) {
|
|
992
|
-
invokeCtx.state = Object.create(ctx.state);
|
|
993
|
-
invokeCtx.state.currentAIEmployee = subAgentUsername;
|
|
994
|
-
}
|
|
995
|
-
const toolStartedAt = Date.now();
|
|
996
|
-
const isSkillHubTool = entryName === 'skill_hub_execute' || entryName.startsWith('skill_hub_');
|
|
997
|
-
const toolSpan = await spanService.create({
|
|
998
|
-
rootRunId,
|
|
999
|
-
parentSpanId: executionSpanId,
|
|
1000
|
-
type: isSkillHubTool ? 'skill' : 'tool',
|
|
1001
|
-
status: 'running',
|
|
1002
|
-
leaderUsername,
|
|
1003
|
-
employeeUsername: subAgentUsername,
|
|
1004
|
-
toolName: toolEntry.definition.name,
|
|
1005
|
-
title: isSkillHubTool ? `Skill: ${toolEntry.definition.name}` : `Tool: ${toolEntry.definition.name}`,
|
|
1006
|
-
input: toolArgs,
|
|
1007
|
-
metadata: {
|
|
1008
|
-
depth: currentDepth + 1,
|
|
1009
|
-
toolCallId: `orch-${toolCallId}`,
|
|
1010
|
-
defaultPermission: toolEntry.defaultPermission,
|
|
1011
|
-
},
|
|
1012
|
-
userId: ctxSnapshot.userId,
|
|
1013
|
-
});
|
|
1014
|
-
const toolSpanId = toolSpan?.id ? String(toolSpan.id) : undefined;
|
|
1015
|
-
setOrchestratorTraceContext(invokeCtx, {
|
|
1016
|
-
rootRunId,
|
|
1017
|
-
spanId: toolSpanId,
|
|
1018
|
-
parentSpanId: executionSpanId,
|
|
1019
|
-
toolCallId: `orch-${toolCallId}`,
|
|
1020
|
-
leaderUsername,
|
|
1021
|
-
employeeUsername: subAgentUsername,
|
|
1022
|
-
toolName: toolEntry.definition.name,
|
|
1023
|
-
});
|
|
1024
|
-
|
|
1025
|
-
trace.push({
|
|
1026
|
-
type: 'tool_call',
|
|
1027
|
-
at: nowIso(),
|
|
1028
|
-
title: `Calling tool: ${toolEntry.definition.name}`,
|
|
1029
|
-
toolName: toolEntry.definition.name,
|
|
1030
|
-
args: toolArgs,
|
|
1031
|
-
});
|
|
1032
|
-
|
|
1033
|
-
try {
|
|
1034
|
-
const res = await toolEntry.invoke(invokeCtx, toolArgs, `orch-${toolCallId}`);
|
|
1035
|
-
const output = truncateText(res?.content ?? res?.result ?? res, 50000);
|
|
1036
|
-
trace.push({
|
|
1037
|
-
type: 'tool_result',
|
|
1038
|
-
at: nowIso(),
|
|
1039
|
-
title: `Tool finished: ${toolEntry.definition.name}`,
|
|
1040
|
-
toolName: toolEntry.definition.name,
|
|
1041
|
-
status: res?.status || 'success',
|
|
1042
|
-
content: truncateText(output, 2000),
|
|
1043
|
-
});
|
|
1044
|
-
if (res?.status === 'error') {
|
|
1045
|
-
await spanService.finish(toolSpanId, 'error', toolStartedAt, {
|
|
1046
|
-
output,
|
|
1047
|
-
error: truncateText(res.content || output, 10000),
|
|
1048
|
-
});
|
|
1049
|
-
throw new Error(`Tool <${toolEntry.definition.name}> failed: ${res.content}`);
|
|
1050
|
-
}
|
|
1051
|
-
await spanService.finish(toolSpanId, 'success', toolStartedAt, {
|
|
1052
|
-
output,
|
|
1053
|
-
skillExecutionId: res?.result?.execId || res?.execId,
|
|
1054
|
-
});
|
|
1055
|
-
return typeof res?.content === 'string' ? res.content : JSON.stringify(res);
|
|
1056
|
-
} catch (e: any) {
|
|
1057
|
-
trace.push({
|
|
1058
|
-
type: 'tool_error',
|
|
1059
|
-
at: nowIso(),
|
|
1060
|
-
title: `Tool failed: ${toolEntry.definition.name}`,
|
|
1061
|
-
toolName: toolEntry.definition.name,
|
|
1062
|
-
status: 'error',
|
|
1063
|
-
content: e.message,
|
|
1064
|
-
});
|
|
1065
|
-
await spanService.finish(toolSpanId, 'error', toolStartedAt, {
|
|
1066
|
-
error: truncateText(e.message, 10000),
|
|
1067
|
-
});
|
|
1068
|
-
throw e;
|
|
1069
|
-
}
|
|
1070
|
-
},
|
|
1071
|
-
}),
|
|
1072
|
-
);
|
|
1073
|
-
}
|
|
1074
|
-
|
|
1075
|
-
// --- Step 3: Build the agent ---
|
|
1076
|
-
const abortController = new AbortController();
|
|
1077
|
-
const executor = createReactAgent({
|
|
1078
|
-
llm: chatModel,
|
|
1079
|
-
tools: langchainTools,
|
|
1080
|
-
});
|
|
1081
|
-
|
|
1082
|
-
// --- Step 4: Construct messages ---
|
|
1083
|
-
let systemPrompt =
|
|
1084
|
-
subAgentEmployee.chatSettings?.systemPrompt ||
|
|
1085
|
-
subAgentEmployee.bio ||
|
|
1086
|
-
`You are an AI assistant named "${subAgentEmployee.nickname || subAgentUsername}". ${
|
|
1087
|
-
subAgentEmployee.about || ''
|
|
1088
|
-
}`;
|
|
1089
|
-
|
|
1090
|
-
// --- Step 4b: Inject shared context from Knowledge Base (soft dependency) ---
|
|
1091
|
-
// If plugin-knowledge-base is installed, inject the session context summary
|
|
1092
|
-
// so the sub-agent is aware of findings from previous agents in this run.
|
|
1093
|
-
try {
|
|
1094
|
-
const kbPlugin = ctx.app.pm.get('plugin-knowledge-base') as any;
|
|
1095
|
-
if (kbPlugin?.sessionContext) {
|
|
1096
|
-
const sessionId =
|
|
1097
|
-
ctx.action?.params?.values?.sessionId ||
|
|
1098
|
-
ctx.action?.params?.sessionId ||
|
|
1099
|
-
ctx.state?.sessionId;
|
|
1100
|
-
|
|
1101
|
-
const contextSummary = await kbPlugin.sessionContext.buildSummary(
|
|
1102
|
-
{ rootRunId, ...(sessionId ? { sessionId } : {}) },
|
|
1103
|
-
6000,
|
|
1104
|
-
);
|
|
1105
|
-
if (contextSummary) {
|
|
1106
|
-
systemPrompt += `\n\n<shared_context>\nThe following context was shared by other agents in this workflow. Use it to avoid redundant work:\n${contextSummary}\n</shared_context>`;
|
|
1107
|
-
}
|
|
1108
|
-
}
|
|
1109
|
-
} catch (e: any) {
|
|
1110
|
-
// Graceful fallback — never block delegation due to context injection failure.
|
|
1111
|
-
ctx.app.log?.debug?.(`[AgentOrchestrator] Shared context injection skipped: ${e.message}`);
|
|
1112
|
-
}
|
|
1113
|
-
|
|
1114
|
-
const combinedTask = context ? `Task: ${task}\n\nContext Provided:\n${context}` : `Task: ${task}`;
|
|
1115
|
-
|
|
1116
|
-
// --- Step 5: Execute with timeout + abort ---
|
|
1117
|
-
// P3 FIX: AbortController signal cancels the in-flight stream on timeout,
|
|
1118
|
-
// preventing continued token consumption after the timeout fires.
|
|
1119
|
-
const effectiveRecursionLimit =
|
|
1120
|
-
Number.isFinite(recursionLimit) && (recursionLimit as number) > 0 ? (recursionLimit as number) : 50;
|
|
1121
|
-
const invokePromise = executeAgent(
|
|
1122
|
-
executor,
|
|
1123
|
-
systemPrompt,
|
|
1124
|
-
combinedTask,
|
|
1125
|
-
abortController.signal,
|
|
1126
|
-
effectiveRecursionLimit,
|
|
1127
|
-
);
|
|
1128
|
-
|
|
1129
|
-
const timeoutHandle = createTimeout(timeout, subAgentUsername, abortController);
|
|
1130
|
-
let result: AgentExecutionResult;
|
|
1131
|
-
try {
|
|
1132
|
-
result = (await Promise.race([invokePromise, timeoutHandle.promise])) as AgentExecutionResult;
|
|
1133
|
-
} finally {
|
|
1134
|
-
// Always release the timer so it doesn't keep the event loop alive.
|
|
1135
|
-
timeoutHandle.cancel();
|
|
1136
|
-
}
|
|
1137
|
-
|
|
1138
|
-
const content = result.content || 'Sub-agent completed the task but produced no output.';
|
|
1139
|
-
trace.push({
|
|
1140
|
-
type: 'finish',
|
|
1141
|
-
at: nowIso(),
|
|
1142
|
-
title: `Delegation finished: ${subAgentUsername}`,
|
|
1143
|
-
status: 'success',
|
|
1144
|
-
content: truncateText(content, 2000),
|
|
1145
|
-
});
|
|
1146
|
-
|
|
1147
|
-
// Log successful execution for tracing
|
|
1148
|
-
await logDelegation(ctx, plugin, {
|
|
1149
|
-
id: logRecord?.id,
|
|
1150
|
-
leaderUsername,
|
|
1151
|
-
subAgentUsername,
|
|
1152
|
-
toolName,
|
|
1153
|
-
task,
|
|
1154
|
-
context,
|
|
1155
|
-
result: content,
|
|
1156
|
-
status: 'success',
|
|
1157
|
-
depth: currentDepth,
|
|
1158
|
-
durationMs: Date.now() - startTime,
|
|
1159
|
-
trace,
|
|
1160
|
-
messages: result.messages,
|
|
1161
|
-
snapshot: ctxSnapshot,
|
|
1162
|
-
});
|
|
1163
|
-
await spanService.finish(executionSpanId, 'success', startTime, {
|
|
1164
|
-
output: content,
|
|
1165
|
-
metadata: {
|
|
1166
|
-
depth: currentDepth,
|
|
1167
|
-
maxDepth,
|
|
1168
|
-
toolName,
|
|
1169
|
-
recursionLimit,
|
|
1170
|
-
messages: result.messages,
|
|
1171
|
-
traceCount: trace.length,
|
|
1172
|
-
},
|
|
1173
|
-
});
|
|
1174
|
-
|
|
1175
|
-
return {
|
|
1176
|
-
status: 'success' as const,
|
|
1177
|
-
content,
|
|
1178
|
-
};
|
|
1179
|
-
} catch (e) {
|
|
1180
|
-
plugin.app.log.error(`[AgentOrchestrator] Sub-agent ${subAgentUsername} failed`, e);
|
|
1181
|
-
|
|
1182
|
-
// Log failed execution for tracing
|
|
1183
|
-
await logDelegation(ctx, plugin, {
|
|
1184
|
-
id: logRecord?.id,
|
|
1185
|
-
leaderUsername,
|
|
1186
|
-
subAgentUsername,
|
|
1187
|
-
toolName,
|
|
1188
|
-
task,
|
|
1189
|
-
context,
|
|
1190
|
-
result: '',
|
|
1191
|
-
status: 'error',
|
|
1192
|
-
depth: currentDepth,
|
|
1193
|
-
durationMs: Date.now() - startTime,
|
|
1194
|
-
error: e.message,
|
|
1195
|
-
trace: [
|
|
1196
|
-
...trace,
|
|
1197
|
-
{
|
|
1198
|
-
type: 'error',
|
|
1199
|
-
at: nowIso(),
|
|
1200
|
-
title: `Delegation failed: ${subAgentUsername}`,
|
|
1201
|
-
status: 'error',
|
|
1202
|
-
content: e.message,
|
|
1203
|
-
},
|
|
1204
|
-
],
|
|
1205
|
-
snapshot: ctxSnapshot,
|
|
1206
|
-
}).catch((logErr) => {
|
|
1207
|
-
plugin.app.log.warn('[AgentOrchestrator] Failed to save error log for delegation', logErr);
|
|
1208
|
-
});
|
|
1209
|
-
await spanService.finish(executionSpanId, 'error', startTime, {
|
|
1210
|
-
error: truncateText(e.message, 10000),
|
|
1211
|
-
metadata: {
|
|
1212
|
-
depth: currentDepth,
|
|
1213
|
-
maxDepth,
|
|
1214
|
-
toolName,
|
|
1215
|
-
recursionLimit,
|
|
1216
|
-
traceCount: trace.length + 1,
|
|
1217
|
-
},
|
|
1218
|
-
});
|
|
1219
|
-
|
|
1220
|
-
const diagnosticTrace = trace
|
|
1221
|
-
.filter((t) => t.type === 'tool_error' || t.type === 'error')
|
|
1222
|
-
.map((t) => `[${t.toolName ? `Tool: ${t.toolName}` : 'Sub-agent'}] Error: ${t.content || t.title}`)
|
|
1223
|
-
.join('\n');
|
|
1224
|
-
|
|
1225
|
-
const formattedError = [
|
|
1226
|
-
`Sub-agent "${subAgentUsername}" failed execution: ${e.message}`,
|
|
1227
|
-
diagnosticTrace ? `\nDiagnostic Details of internal failures:\n${diagnosticTrace}` : '',
|
|
1228
|
-
`Suggestion: Review the tool parameters above or try dividing the task into simpler independent tasks.`,
|
|
1229
|
-
]
|
|
1230
|
-
.filter(Boolean)
|
|
1231
|
-
.join('\n');
|
|
1232
|
-
|
|
1233
|
-
return {
|
|
1234
|
-
status: 'error' as const,
|
|
1235
|
-
content: formattedError,
|
|
1236
|
-
};
|
|
1237
|
-
}
|
|
1238
867
|
}
|
|
1239
868
|
|
|
1240
869
|
/**
|
|
@@ -1267,15 +896,12 @@ async function logDelegation(
|
|
|
1267
896
|
return;
|
|
1268
897
|
}
|
|
1269
898
|
|
|
1270
|
-
// Prefer the early snapshot captured in invokeDelegateTask — by the time
|
|
1271
|
-
// the agent finishes, ctx may already be disposed. Fall back to ctx for
|
|
1272
|
-
// call sites that don't pass a snapshot (e.g. authz-failure short-circuit).
|
|
1273
899
|
let userId: number | undefined = data.snapshot?.userId;
|
|
1274
900
|
if (userId == null) {
|
|
1275
901
|
try {
|
|
1276
902
|
userId = ctx.auth?.user?.id || ctx.state?.currentUser?.id;
|
|
1277
903
|
} catch {
|
|
1278
|
-
// ctx lifecycle ended
|
|
904
|
+
// ctx lifecycle ended
|
|
1279
905
|
}
|
|
1280
906
|
}
|
|
1281
907
|
|
|
@@ -1315,91 +941,3 @@ async function logDelegation(
|
|
|
1315
941
|
plugin.app.log.warn('[AgentOrchestrator] Failed to log delegation event', e);
|
|
1316
942
|
}
|
|
1317
943
|
}
|
|
1318
|
-
|
|
1319
|
-
/**
|
|
1320
|
-
* Execute the agent and extract the final AI message content.
|
|
1321
|
-
* Uses executor.invoke to get the final state cleanly, avoiding chunk parsing issues.
|
|
1322
|
-
* Accepts an AbortSignal so the execution can be cancelled on timeout.
|
|
1323
|
-
*/
|
|
1324
|
-
async function executeAgent(
|
|
1325
|
-
executor: any,
|
|
1326
|
-
systemPrompt: string,
|
|
1327
|
-
task: string,
|
|
1328
|
-
signal?: AbortSignal,
|
|
1329
|
-
recursionLimit = 50,
|
|
1330
|
-
): Promise<AgentExecutionResult> {
|
|
1331
|
-
const config: any = { recursionLimit };
|
|
1332
|
-
if (signal) {
|
|
1333
|
-
config.signal = signal;
|
|
1334
|
-
}
|
|
1335
|
-
|
|
1336
|
-
const finalState = await executor.invoke(
|
|
1337
|
-
{
|
|
1338
|
-
messages: [new SystemMessage(systemPrompt), new HumanMessage(task)],
|
|
1339
|
-
},
|
|
1340
|
-
config,
|
|
1341
|
-
);
|
|
1342
|
-
|
|
1343
|
-
// finalState.messages contains the entire conversation history of this delegation
|
|
1344
|
-
const messages = finalState?.messages || [];
|
|
1345
|
-
|
|
1346
|
-
// Find the last AI message in the chain
|
|
1347
|
-
const lastAIMessage = [...messages].reverse().find((m) => m.getType() === 'ai');
|
|
1348
|
-
|
|
1349
|
-
if (!lastAIMessage || !lastAIMessage.content) {
|
|
1350
|
-
return { content: '', messages: serializeMessages(messages) };
|
|
1351
|
-
}
|
|
1352
|
-
|
|
1353
|
-
let content = '';
|
|
1354
|
-
if (typeof lastAIMessage.content === 'string') {
|
|
1355
|
-
content = lastAIMessage.content;
|
|
1356
|
-
} else if (Array.isArray(lastAIMessage.content)) {
|
|
1357
|
-
content = lastAIMessage.content.map((c: any) => c.text || JSON.stringify(c)).join('\n');
|
|
1358
|
-
} else {
|
|
1359
|
-
content = String(lastAIMessage.content);
|
|
1360
|
-
}
|
|
1361
|
-
|
|
1362
|
-
return { content, messages: serializeMessages(messages) };
|
|
1363
|
-
}
|
|
1364
|
-
|
|
1365
|
-
function serializeMessages(messages: any[]) {
|
|
1366
|
-
return (messages || []).map((message, index) => {
|
|
1367
|
-
const type = typeof message.getType === 'function' ? message.getType() : message.type;
|
|
1368
|
-
return {
|
|
1369
|
-
index,
|
|
1370
|
-
type,
|
|
1371
|
-
name: message.name,
|
|
1372
|
-
content: truncateText(message.content, 10000),
|
|
1373
|
-
toolCalls: message.tool_calls || message.toolCalls || [],
|
|
1374
|
-
toolCallId: message.tool_call_id,
|
|
1375
|
-
additionalKwargs: message.additional_kwargs,
|
|
1376
|
-
responseMetadata: message.response_metadata,
|
|
1377
|
-
};
|
|
1378
|
-
});
|
|
1379
|
-
}
|
|
1380
|
-
|
|
1381
|
-
/**
|
|
1382
|
-
* Schedule a rejection-on-timeout that also aborts the in-flight stream.
|
|
1383
|
-
* Returns the promise plus a `cancel()` so callers can release the timer
|
|
1384
|
-
* when the race resolves successfully (otherwise the handle keeps the event
|
|
1385
|
-
* loop alive until `ms` elapses).
|
|
1386
|
-
*/
|
|
1387
|
-
function createTimeout(
|
|
1388
|
-
ms: number,
|
|
1389
|
-
agentName: string,
|
|
1390
|
-
abortController?: AbortController,
|
|
1391
|
-
): { promise: Promise<never>; cancel: () => void } {
|
|
1392
|
-
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
1393
|
-
const promise = new Promise<never>((_resolve, reject) => {
|
|
1394
|
-
timer = setTimeout(() => {
|
|
1395
|
-
abortController?.abort();
|
|
1396
|
-
reject(new Error(`Sub-agent "${agentName}" timed out after ${ms / 1000}s`));
|
|
1397
|
-
}, ms);
|
|
1398
|
-
});
|
|
1399
|
-
return {
|
|
1400
|
-
promise,
|
|
1401
|
-
cancel: () => {
|
|
1402
|
-
if (timer) clearTimeout(timer);
|
|
1403
|
-
},
|
|
1404
|
-
};
|
|
1405
|
-
}
|