plugin-agent-orchestrator 1.0.17 → 1.0.19
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 +152 -54
- package/dist/server/skill-hub/tasks/SkillExecutionTask.d.ts +16 -0
- package/dist/server/skill-hub/tasks/SkillExecutionTask.js +15 -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 +897 -771
- package/src/server/skill-hub/tasks/SkillExecutionTask.ts +15 -0
- 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
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
function toPlain(record: any) {
|
|
2
|
+
return record?.toJSON?.() || record;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
function trimText(value: any, max = 50000) {
|
|
6
|
+
let text = '';
|
|
7
|
+
if (typeof value === 'string') {
|
|
8
|
+
text = value;
|
|
9
|
+
} else if (value != null) {
|
|
10
|
+
try {
|
|
11
|
+
text = JSON.stringify(value);
|
|
12
|
+
} catch {
|
|
13
|
+
text = String(value);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
return text.length > max ? `${text.slice(0, max)}\n...[truncated]` : text;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export class AgentLoopRepository {
|
|
20
|
+
constructor(private readonly plugin: any) {}
|
|
21
|
+
|
|
22
|
+
get db() {
|
|
23
|
+
return this.plugin.db;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
async getRun(runId: string | number) {
|
|
27
|
+
const run = await this.db.getRepository('agentLoopRuns').findOne({
|
|
28
|
+
filter: { id: runId },
|
|
29
|
+
});
|
|
30
|
+
return run ? toPlain(run) : null;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async requireRun(runId: string | number) {
|
|
34
|
+
const run = await this.getRun(runId);
|
|
35
|
+
if (!run) {
|
|
36
|
+
throw new Error(`Agent loop run "${runId}" was not found.`);
|
|
37
|
+
}
|
|
38
|
+
return run;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async createRun(values: any) {
|
|
42
|
+
const run = await this.db.getRepository('agentLoopRuns').create({
|
|
43
|
+
values,
|
|
44
|
+
});
|
|
45
|
+
return toPlain(run);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async updateRun(runId: string | number, values: any) {
|
|
49
|
+
await this.db.getRepository('agentLoopRuns').update({
|
|
50
|
+
filterByTk: runId,
|
|
51
|
+
values,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async getStep(stepId: string | number) {
|
|
56
|
+
const step = await this.db.getRepository('agentLoopSteps').findOne({
|
|
57
|
+
filter: { id: stepId },
|
|
58
|
+
});
|
|
59
|
+
return step ? toPlain(step) : null;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async requireStep(stepId: string | number) {
|
|
63
|
+
const step = await this.getStep(stepId);
|
|
64
|
+
if (!step) {
|
|
65
|
+
throw new Error(`Agent loop step "${stepId}" was not found.`);
|
|
66
|
+
}
|
|
67
|
+
return step;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
async createStep(values: any) {
|
|
71
|
+
const step = await this.db.getRepository('agentLoopSteps').create({
|
|
72
|
+
values,
|
|
73
|
+
});
|
|
74
|
+
return toPlain(step);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async updateStep(stepId: string | number, values: any) {
|
|
78
|
+
await this.db.getRepository('agentLoopSteps').update({
|
|
79
|
+
filterByTk: stepId,
|
|
80
|
+
values,
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async getSteps(runId: string | number) {
|
|
85
|
+
const steps = await this.db.getRepository('agentLoopSteps').find({
|
|
86
|
+
filter: { runId },
|
|
87
|
+
sort: ['index', 'createdAt'],
|
|
88
|
+
pageSize: 1000,
|
|
89
|
+
});
|
|
90
|
+
return steps.map(toPlain);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
async createEvent(values: any) {
|
|
94
|
+
const record = await this.db.getRepository('agentLoopEvents').create({
|
|
95
|
+
values: {
|
|
96
|
+
...values,
|
|
97
|
+
content: trimText(values.content || '', 10000),
|
|
98
|
+
payload: values.payload || {},
|
|
99
|
+
createdAt: new Date(),
|
|
100
|
+
},
|
|
101
|
+
});
|
|
102
|
+
return toPlain(record);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
async getEvents(runId: string | number) {
|
|
106
|
+
const events = await this.db.getRepository('agentLoopEvents').find({
|
|
107
|
+
filter: { runId },
|
|
108
|
+
sort: ['createdAt'],
|
|
109
|
+
pageSize: 500,
|
|
110
|
+
});
|
|
111
|
+
return events.map(toPlain);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
async getLinkedSpans(runId: string | number, rootRunId?: string) {
|
|
115
|
+
const repo = this.db.getRepository('agentExecutionSpans');
|
|
116
|
+
if (!repo) return [];
|
|
117
|
+
const filters = [];
|
|
118
|
+
if (rootRunId) filters.push({ rootRunId });
|
|
119
|
+
filters.push({ 'metadata.agentLoopRunId': String(runId) });
|
|
120
|
+
try {
|
|
121
|
+
const rows = await repo.find({
|
|
122
|
+
filter: { $or: filters },
|
|
123
|
+
sort: ['createdAt'],
|
|
124
|
+
pageSize: 1000,
|
|
125
|
+
});
|
|
126
|
+
return rows.map(toPlain);
|
|
127
|
+
} catch {
|
|
128
|
+
if (!rootRunId) return [];
|
|
129
|
+
const rows = await repo.find({
|
|
130
|
+
filter: { rootRunId },
|
|
131
|
+
sort: ['createdAt'],
|
|
132
|
+
pageSize: 1000,
|
|
133
|
+
});
|
|
134
|
+
return rows.map(toPlain);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
async getLinkedSkillExecutions(runId: string | number, steps: any[]) {
|
|
139
|
+
const ids = Array.from(
|
|
140
|
+
new Set(
|
|
141
|
+
steps
|
|
142
|
+
.map((step) => step.skillExecutionId)
|
|
143
|
+
.filter(Boolean)
|
|
144
|
+
.map(String),
|
|
145
|
+
),
|
|
146
|
+
);
|
|
147
|
+
const repo = this.db.getRepository('skillExecutions');
|
|
148
|
+
if (!repo) return [];
|
|
149
|
+
const filters: any[] = [{ agentLoopRunId: String(runId) }];
|
|
150
|
+
if (ids.length) {
|
|
151
|
+
filters.push({ id: { $in: ids } });
|
|
152
|
+
}
|
|
153
|
+
const rows = await repo.find({
|
|
154
|
+
filter: { $or: filters },
|
|
155
|
+
sort: ['createdAt'],
|
|
156
|
+
pageSize: 1000,
|
|
157
|
+
});
|
|
158
|
+
return rows.map(toPlain);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
async lockRun(runId: string | number, lockName: string, durationMs: number): Promise<boolean> {
|
|
162
|
+
const repo = this.db.getRepository('agentLoopRuns');
|
|
163
|
+
if (!repo) return false;
|
|
164
|
+
const now = new Date();
|
|
165
|
+
const Op = repo.model.sequelize.Sequelize.Op;
|
|
166
|
+
|
|
167
|
+
const result = await repo.model.update(
|
|
168
|
+
{
|
|
169
|
+
lockedBy: lockName,
|
|
170
|
+
lockedUntil: new Date(now.getTime() + durationMs),
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
where: {
|
|
174
|
+
id: runId,
|
|
175
|
+
[Op.or]: [
|
|
176
|
+
{ lockedBy: null },
|
|
177
|
+
{ lockedUntil: { [Op.lt]: now.toISOString() } },
|
|
178
|
+
{ lockedBy: lockName }
|
|
179
|
+
]
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
);
|
|
183
|
+
|
|
184
|
+
const affectedCount = Array.isArray(result) ? result[0] : Number(result || 0);
|
|
185
|
+
return affectedCount > 0;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
async unlockRun(runId: string | number) {
|
|
189
|
+
await this.updateRun(runId, {
|
|
190
|
+
lockedBy: null,
|
|
191
|
+
lockedUntil: null,
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
import { AgentRegistryService } from './AgentRegistryService';
|
|
2
|
+
import { AgentPlannerService } from './AgentPlannerService';
|
|
3
|
+
import { AgentPlanValidator } from './AgentPlanValidator';
|
|
4
|
+
import { AgentLoopRepository } from './AgentLoopRepository';
|
|
5
|
+
import { AgentHarness } from './AgentHarness';
|
|
6
|
+
import { AgentLoopController } from './AgentLoopController';
|
|
7
|
+
|
|
8
|
+
export type AgentLoopRunStatus =
|
|
9
|
+
| 'planning'
|
|
10
|
+
| 'waiting_plan_approval'
|
|
11
|
+
| 'approved'
|
|
12
|
+
| 'running'
|
|
13
|
+
| 'waiting_user'
|
|
14
|
+
| 'needs_replan'
|
|
15
|
+
| 'succeeded'
|
|
16
|
+
| 'failed'
|
|
17
|
+
| 'rejected'
|
|
18
|
+
| 'canceled';
|
|
19
|
+
export type AgentLoopStepStatus = 'pending' | 'running' | 'waiting_user' | 'succeeded' | 'failed' | 'skipped';
|
|
20
|
+
export type AgentLoopStepType = 'reasoning' | 'skill' | 'tool' | 'sub_agent' | 'verification';
|
|
21
|
+
export type AgentLoopStepDependencyPolicy = 'require_success' | 'allow_skipped';
|
|
22
|
+
|
|
23
|
+
export type AgentLoopPolicy = {
|
|
24
|
+
maxIterations: number;
|
|
25
|
+
maxStepAttempts: number;
|
|
26
|
+
allowReplan: boolean;
|
|
27
|
+
requireVerification: boolean;
|
|
28
|
+
stopOnApprovalRequired: boolean;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
export type AgentLoopPlanStepInput = {
|
|
32
|
+
id?: string;
|
|
33
|
+
key?: string;
|
|
34
|
+
planKey?: string;
|
|
35
|
+
parentStepId?: string | number;
|
|
36
|
+
title?: string;
|
|
37
|
+
description?: string;
|
|
38
|
+
type?: AgentLoopStepType;
|
|
39
|
+
target?: string;
|
|
40
|
+
input?: any;
|
|
41
|
+
dependsOn?: string[];
|
|
42
|
+
dependencyPolicy?: AgentLoopStepDependencyPolicy;
|
|
43
|
+
maxAttempts?: number;
|
|
44
|
+
metadata?: any;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export class AgentLoopService {
|
|
48
|
+
public readonly registryService: AgentRegistryService;
|
|
49
|
+
public readonly plannerService: AgentPlannerService;
|
|
50
|
+
public readonly validator: AgentPlanValidator;
|
|
51
|
+
public readonly repository: AgentLoopRepository;
|
|
52
|
+
public readonly harness: AgentHarness;
|
|
53
|
+
public readonly controller: AgentLoopController;
|
|
54
|
+
|
|
55
|
+
constructor(private readonly plugin: any) {
|
|
56
|
+
this.registryService = new AgentRegistryService(plugin);
|
|
57
|
+
this.plannerService = new AgentPlannerService();
|
|
58
|
+
this.validator = new AgentPlanValidator();
|
|
59
|
+
this.repository = new AgentLoopRepository(plugin);
|
|
60
|
+
this.harness = new AgentHarness(plugin, this.registryService);
|
|
61
|
+
this.controller = new AgentLoopController(
|
|
62
|
+
this.registryService,
|
|
63
|
+
this.plannerService,
|
|
64
|
+
this.validator,
|
|
65
|
+
this.repository,
|
|
66
|
+
this.harness
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
get db() {
|
|
71
|
+
return this.plugin.db;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
get app() {
|
|
75
|
+
return this.plugin.app;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
async createRun(options: any) {
|
|
79
|
+
return this.controller.createRun(options);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
async planGoal(options: any) {
|
|
83
|
+
return this.controller.planGoal(options);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
async revisePlanGoal(runId: any, plan: any, options: any = {}) {
|
|
87
|
+
return this.controller.revisePlanGoal(runId, plan, options);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
async approvePlanAndExecute(runId: any, options: any = {}) {
|
|
91
|
+
return this.controller.approvePlanAndExecute(runId, options);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
async rejectPlan(runId: any, options: any = {}) {
|
|
95
|
+
return this.controller.rejectPlan(runId, options);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
async requestPlanChanges(runId: any, options: any = {}) {
|
|
99
|
+
return this.controller.requestPlanChanges(runId, options);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
async replacePlan(runId: any, plan: any, options: any = {}) {
|
|
103
|
+
return this.controller.replacePlan(runId, plan, options);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
async replan(runId: any, plan: any, options: any = {}) {
|
|
107
|
+
return this.controller.replan(runId, plan, options);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
async startStep(stepId: any, options: any = {}) {
|
|
111
|
+
return this.controller.startStep(stepId, options);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
async completeStep(stepId: any, output: any, options: any = {}) {
|
|
115
|
+
return this.controller.completeStep(stepId, output, options);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
async failStep(stepId: any, error: any, options: any = {}) {
|
|
119
|
+
return this.controller.failStep(stepId, error, options);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
async skipStep(stepId: any, reason: any = 'Skipped', options: any = {}) {
|
|
123
|
+
return this.controller.skipStep(stepId, reason, options);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
async requestApproval(stepId: any, approval: any, options: any = {}) {
|
|
127
|
+
return this.controller.requestApproval(stepId, approval, options);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
async resumeRun(runId: any, options: any) {
|
|
131
|
+
return this.controller.resumeRun(runId, options);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
async retryStep(stepId: any, options: any = {}) {
|
|
135
|
+
return this.controller.retryStep(stepId, options);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
async finishRun(runId: any, finalAnswer: any, options: any = {}) {
|
|
139
|
+
return this.controller.finishRun(runId, finalAnswer, options);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
async cancelRun(runId: any, options: any = {}) {
|
|
143
|
+
return this.controller.cancelRun(runId, options);
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
async executeApprovedPlan(runId: any, options: any = {}) {
|
|
147
|
+
return this.controller.executeApprovedPlan(runId, options);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
async getRunSnapshot(runId: any) {
|
|
151
|
+
return this.controller.getRunSnapshot(runId);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
async getRunDetail(runId: any) {
|
|
155
|
+
return this.controller.getRunDetail(runId);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
async createEvent(values: any) {
|
|
159
|
+
return this.repository.createEvent(values);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { AgentLoopPlanStepInput } from './AgentLoopService';
|
|
2
|
+
|
|
3
|
+
function normalizeStepType(value: any) {
|
|
4
|
+
return ['reasoning', 'skill', 'tool', 'sub_agent', 'verification'].includes(value) ? value : 'tool';
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
function normalizePlanKey(step: any, index: number) {
|
|
8
|
+
return String(step.planKey || step.key || step.id || `step_${index + 1}`);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function asArray(value: any): any[] {
|
|
12
|
+
return Array.isArray(value) ? value : [];
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const ORCHESTRATOR_CONTROLLER_MAX_STEPS = 100;
|
|
16
|
+
|
|
17
|
+
export class AgentPlanValidator {
|
|
18
|
+
validate(plan: AgentLoopPlanStepInput[]) {
|
|
19
|
+
if (!Array.isArray(plan) || plan.length === 0) {
|
|
20
|
+
throw new Error('Plan must include at least one step.');
|
|
21
|
+
}
|
|
22
|
+
if (plan.length > ORCHESTRATOR_CONTROLLER_MAX_STEPS) {
|
|
23
|
+
throw new Error(`Plan cannot exceed ${ORCHESTRATOR_CONTROLLER_MAX_STEPS} steps.`);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const keys = new Set<string>();
|
|
27
|
+
const graph = new Map<string, string[]>();
|
|
28
|
+
for (let i = 0; i < plan.length; i++) {
|
|
29
|
+
const key = normalizePlanKey(plan[i], i).trim();
|
|
30
|
+
if (!key) {
|
|
31
|
+
throw new Error(`Plan step ${i + 1} has an empty planKey.`);
|
|
32
|
+
}
|
|
33
|
+
if (keys.has(key)) {
|
|
34
|
+
throw new Error(`Duplicate planKey "${key}" in plan.`);
|
|
35
|
+
}
|
|
36
|
+
const type = normalizeStepType(plan[i].type);
|
|
37
|
+
if (['tool', 'skill', 'sub_agent'].includes(type) && !plan[i].target) {
|
|
38
|
+
throw new Error(`Step "${key}" of type "${type}" must include a target.`);
|
|
39
|
+
}
|
|
40
|
+
keys.add(key);
|
|
41
|
+
graph.set(key, asArray(plan[i].dependsOn).map(String));
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
for (const [key, dependencies] of graph.entries()) {
|
|
45
|
+
for (const dependency of dependencies) {
|
|
46
|
+
if (!keys.has(dependency)) {
|
|
47
|
+
throw new Error(`Step "${key}" depends on unknown step "${dependency}".`);
|
|
48
|
+
}
|
|
49
|
+
if (dependency === key) {
|
|
50
|
+
throw new Error(`Step "${key}" cannot depend on itself.`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const visiting = new Set<string>();
|
|
56
|
+
const visited = new Set<string>();
|
|
57
|
+
const visit = (key: string) => {
|
|
58
|
+
if (visited.has(key)) return;
|
|
59
|
+
if (visiting.has(key)) {
|
|
60
|
+
throw new Error(`Plan has a dependency cycle at "${key}".`);
|
|
61
|
+
}
|
|
62
|
+
visiting.add(key);
|
|
63
|
+
for (const dependency of graph.get(key) || []) {
|
|
64
|
+
visit(dependency);
|
|
65
|
+
}
|
|
66
|
+
visiting.delete(key);
|
|
67
|
+
visited.add(key);
|
|
68
|
+
};
|
|
69
|
+
for (const key of graph.keys()) {
|
|
70
|
+
visit(key);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { AgentLoopPlanStepInput } from './AgentLoopService';
|
|
2
|
+
|
|
3
|
+
function normalizeStepType(value: any) {
|
|
4
|
+
return ['reasoning', 'skill', 'tool', 'sub_agent', 'verification'].includes(value) ? value : 'tool';
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
function normalizePlanKey(step: any, index: number) {
|
|
8
|
+
return String(step.planKey || step.key || step.id || `step_${index + 1}`);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function asArray(value: any): any[] {
|
|
12
|
+
return Array.isArray(value) ? value : [];
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function asObject(value: any) {
|
|
16
|
+
if (value && typeof value === 'object' && !Array.isArray(value)) return value;
|
|
17
|
+
if (typeof value === 'string' && value.trim()) {
|
|
18
|
+
try {
|
|
19
|
+
const parsed = JSON.parse(value);
|
|
20
|
+
return parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? parsed : {};
|
|
21
|
+
} catch {
|
|
22
|
+
return {};
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return {};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export class AgentPlannerService {
|
|
29
|
+
buildPlan(
|
|
30
|
+
goal: string,
|
|
31
|
+
plan: AgentLoopPlanStepInput[] | undefined,
|
|
32
|
+
options: { targetAgent?: string; harnessTag?: string; metadata?: any }
|
|
33
|
+
): AgentLoopPlanStepInput[] {
|
|
34
|
+
if (Array.isArray(plan) && plan.length > 0) {
|
|
35
|
+
return plan.map((step, index) => ({
|
|
36
|
+
...step,
|
|
37
|
+
planKey: normalizePlanKey(step, index),
|
|
38
|
+
type: normalizeStepType(step.type),
|
|
39
|
+
dependsOn: asArray(step.dependsOn).map(String),
|
|
40
|
+
metadata: {
|
|
41
|
+
...asObject(step.metadata),
|
|
42
|
+
harnessTag: options.harnessTag || options.metadata?.harnessTag || 'default',
|
|
43
|
+
},
|
|
44
|
+
}));
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const harnessTag = options.harnessTag || options.metadata?.harnessTag || 'default';
|
|
48
|
+
const steps: AgentLoopPlanStepInput[] = [
|
|
49
|
+
{
|
|
50
|
+
planKey: 'prepare_context',
|
|
51
|
+
title: 'Prepare execution context',
|
|
52
|
+
description: 'Review the user goal, available context, and constraints before execution.',
|
|
53
|
+
type: 'reasoning',
|
|
54
|
+
input: { goal },
|
|
55
|
+
metadata: { harnessTag },
|
|
56
|
+
},
|
|
57
|
+
];
|
|
58
|
+
|
|
59
|
+
if (options.targetAgent) {
|
|
60
|
+
steps.push({
|
|
61
|
+
planKey: 'delegate_execution',
|
|
62
|
+
title: `Delegate execution to ${options.targetAgent}`,
|
|
63
|
+
description: goal,
|
|
64
|
+
type: 'sub_agent',
|
|
65
|
+
target: options.targetAgent,
|
|
66
|
+
input: { goal },
|
|
67
|
+
dependsOn: ['prepare_context'],
|
|
68
|
+
metadata: { harnessTag },
|
|
69
|
+
});
|
|
70
|
+
} else {
|
|
71
|
+
steps.push({
|
|
72
|
+
planKey: 'execute_goal',
|
|
73
|
+
title: 'Execute approved goal',
|
|
74
|
+
description: 'Execute the user goal in the leader harness using the available approved tools.',
|
|
75
|
+
type: 'reasoning',
|
|
76
|
+
input: { goal },
|
|
77
|
+
dependsOn: ['prepare_context'],
|
|
78
|
+
metadata: { harnessTag, controllerOnly: true },
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
steps.push({
|
|
83
|
+
planKey: 'verify_result',
|
|
84
|
+
title: 'Verify result',
|
|
85
|
+
description: 'Check that the completed work matches the approved goal and report evidence.',
|
|
86
|
+
type: 'verification',
|
|
87
|
+
input: { goal },
|
|
88
|
+
dependsOn: [steps[steps.length - 1].planKey || 'execute_goal'],
|
|
89
|
+
metadata: { harnessTag },
|
|
90
|
+
});
|
|
91
|
+
return steps;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
function toPlain(record: any) {
|
|
2
|
+
return record?.toJSON?.() || record;
|
|
3
|
+
}
|
|
4
|
+
|
|
5
|
+
function asObject(value: any) {
|
|
6
|
+
if (value && typeof value === 'object' && !Array.isArray(value)) return value;
|
|
7
|
+
if (typeof value === 'string' && value.trim()) {
|
|
8
|
+
try {
|
|
9
|
+
const parsed = JSON.parse(value);
|
|
10
|
+
return parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? parsed : {};
|
|
11
|
+
} catch {
|
|
12
|
+
return {};
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
return {};
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
function normalizeEmployeeUsername(raw: any) {
|
|
19
|
+
if (!raw) return null;
|
|
20
|
+
if (typeof raw === 'string') return raw;
|
|
21
|
+
return raw.username || raw.aiEmployeeUsername || raw.name || null;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export class AgentRegistryService {
|
|
25
|
+
constructor(private readonly plugin: any) {}
|
|
26
|
+
|
|
27
|
+
get db() {
|
|
28
|
+
return this.plugin.db;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async getHarnessProfile(tag: string) {
|
|
32
|
+
try {
|
|
33
|
+
const repo = this.db.getRepository('agentHarnessProfiles');
|
|
34
|
+
if (!repo) return null;
|
|
35
|
+
const profile = await repo.findOne({
|
|
36
|
+
filter: {
|
|
37
|
+
tag: tag || 'default',
|
|
38
|
+
enabled: true,
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
return profile ? toPlain(profile) : null;
|
|
42
|
+
} catch {
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async getOrchestratorConfig(leaderUsername: string, subAgentUsername: string) {
|
|
48
|
+
try {
|
|
49
|
+
const repo = this.db.getRepository('orchestratorConfig');
|
|
50
|
+
if (!repo) return null;
|
|
51
|
+
const config = await repo.findOne({
|
|
52
|
+
filter: {
|
|
53
|
+
leaderUsername,
|
|
54
|
+
subAgentUsername,
|
|
55
|
+
enabled: true,
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
return config ? toPlain(config) : null;
|
|
59
|
+
} catch {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async getAIEmployee(username: string) {
|
|
65
|
+
try {
|
|
66
|
+
const repo = this.db.getRepository('aiEmployees');
|
|
67
|
+
if (!repo) return null;
|
|
68
|
+
const employee = await repo.findOne({
|
|
69
|
+
filter: { username },
|
|
70
|
+
});
|
|
71
|
+
return employee ? toPlain(employee) : null;
|
|
72
|
+
} catch {
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async resolveModelSettings(
|
|
78
|
+
subAgentUsername: string,
|
|
79
|
+
leaderUsername?: string,
|
|
80
|
+
dynamicValues?: any
|
|
81
|
+
) {
|
|
82
|
+
const subAgent = await this.getAIEmployee(subAgentUsername);
|
|
83
|
+
if (!subAgent) {
|
|
84
|
+
throw new Error(`Sub-agent "${subAgentUsername}" was not found.`);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
const hasModelSettings = (val: any): val is { llmService: string; model: string } => {
|
|
88
|
+
return Boolean(val?.llmService && val?.model);
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
let modelSettings = hasModelSettings(dynamicValues)
|
|
92
|
+
? dynamicValues
|
|
93
|
+
: undefined;
|
|
94
|
+
|
|
95
|
+
if (!modelSettings) {
|
|
96
|
+
if (hasModelSettings(subAgent.modelSettings)) {
|
|
97
|
+
modelSettings = subAgent.modelSettings;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (!modelSettings && leaderUsername) {
|
|
102
|
+
const leader = await this.getAIEmployee(leaderUsername);
|
|
103
|
+
if (leader && hasModelSettings(leader.modelSettings)) {
|
|
104
|
+
modelSettings = leader.modelSettings;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return modelSettings;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
async isRegisteredDelegationTool(toolName: string): Promise<boolean> {
|
|
112
|
+
if (!toolName || (typeof toolName !== 'string')) return false;
|
|
113
|
+
if (!toolName.startsWith('delegate_') && !toolName.startsWith('dispatch_subagents_')) {
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
try {
|
|
118
|
+
const configRepo = this.db.getRepository('orchestratorConfig');
|
|
119
|
+
if (!configRepo) return false;
|
|
120
|
+
|
|
121
|
+
// 1. Check if it matches dispatch_subagents_${leader}
|
|
122
|
+
if (toolName.startsWith('dispatch_subagents_')) {
|
|
123
|
+
const leader = toolName.substring('dispatch_subagents_'.length);
|
|
124
|
+
const count = await configRepo.count({
|
|
125
|
+
filter: {
|
|
126
|
+
leaderUsername: leader,
|
|
127
|
+
enabled: true,
|
|
128
|
+
},
|
|
129
|
+
});
|
|
130
|
+
return count > 0;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// 2. Check if it matches delegate_${leader}_to_${subAgent}
|
|
134
|
+
if (toolName.startsWith('delegate_') && toolName.includes('_to_')) {
|
|
135
|
+
const parts = toolName.substring('delegate_'.length).split('_to_');
|
|
136
|
+
if (parts.length === 2) {
|
|
137
|
+
const [leader, subAgent] = parts;
|
|
138
|
+
const count = await configRepo.count({
|
|
139
|
+
filter: {
|
|
140
|
+
leaderUsername: leader,
|
|
141
|
+
subAgentUsername: subAgent,
|
|
142
|
+
enabled: true,
|
|
143
|
+
},
|
|
144
|
+
});
|
|
145
|
+
if (count > 0) return true;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// 3. Check legacy alias: delegate_to_${subAgent}
|
|
150
|
+
if (toolName.startsWith('delegate_to_')) {
|
|
151
|
+
const subAgent = toolName.substring('delegate_to_'.length);
|
|
152
|
+
const configs = await configRepo.find({
|
|
153
|
+
filter: {
|
|
154
|
+
subAgentUsername: subAgent,
|
|
155
|
+
enabled: true,
|
|
156
|
+
},
|
|
157
|
+
});
|
|
158
|
+
// Legacy alias is only registered if there is exactly one leader for this subAgent
|
|
159
|
+
if (configs?.length === 1) {
|
|
160
|
+
return true;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
return false;
|
|
165
|
+
} catch {
|
|
166
|
+
return false;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|