plugin-agent-orchestrator 1.0.19 → 1.0.21
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/hooks/useRunEventStream.d.ts +22 -0
- package/dist/client/index.d.ts +1 -0
- package/dist/client/index.js +1 -1
- package/dist/externalVersion.js +6 -6
- package/dist/server/collections/agent-execution-spans.js +24 -0
- package/dist/server/collections/agent-loop-runs.js +36 -0
- package/dist/server/collections/orchestrator-config.js +14 -0
- package/dist/server/migrations/20260601000000-add-token-fields.d.ts +7 -0
- package/dist/server/migrations/20260601000000-add-token-fields.js +101 -0
- package/dist/server/plugin.js +47 -0
- package/dist/server/resources/agent-loop.js +33 -25
- package/dist/server/resources/tracing.js +5 -8
- package/dist/server/services/AgentHarness.d.ts +2 -0
- package/dist/server/services/AgentHarness.js +56 -90
- package/dist/server/services/AgentLoopController.d.ts +33 -20
- package/dist/server/services/AgentLoopController.js +164 -125
- package/dist/server/services/AgentLoopRepository.js +16 -34
- package/dist/server/services/AgentLoopService.d.ts +28 -18
- package/dist/server/services/AgentLoopService.js +7 -1
- package/dist/server/services/AgentPlannerService.js +5 -25
- package/dist/server/services/AgentRegistryService.d.ts +8 -0
- package/dist/server/services/AgentRegistryService.js +34 -24
- package/dist/server/services/CircuitBreaker.d.ts +40 -0
- package/dist/server/services/CircuitBreaker.js +120 -0
- package/dist/server/services/ContextAggregator.d.ts +45 -0
- package/dist/server/services/ContextAggregator.js +201 -0
- package/dist/server/services/ExecutionSpanService.js +2 -5
- package/dist/server/services/RunEventBus.d.ts +9 -0
- package/dist/server/services/RunEventBus.js +73 -0
- package/dist/server/services/TokenTracker.d.ts +62 -0
- package/dist/server/services/TokenTracker.js +173 -0
- package/dist/server/skill-hub/plugin.js +6 -6
- package/dist/server/skill-hub/tasks/SkillExecutionTask.js +6 -6
- package/dist/server/tools/agent-loop.d.ts +8 -8
- package/dist/server/tools/agent-loop.js +30 -63
- package/dist/server/tools/delegate-task.js +14 -72
- package/dist/server/tools/orchestrator-plan.d.ts +6 -6
- package/dist/server/tools/orchestrator-plan.js +10 -47
- package/dist/server/types.d.ts +47 -0
- package/dist/server/types.js +24 -0
- package/dist/server/utils/ctx-utils.d.ts +30 -0
- package/dist/server/utils/ctx-utils.js +152 -0
- package/dist/server/utils/logging.d.ts +6 -0
- package/dist/server/utils/logging.js +86 -0
- package/package.json +44 -44
- package/src/client/AgentRunsTab.tsx +764 -764
- package/src/client/HarnessProfilesTab.tsx +247 -247
- package/src/client/OrchestratorSettings.tsx +106 -106
- package/src/client/RulesTab.tsx +716 -716
- package/src/client/hooks/useRunEventStream.ts +76 -0
- package/src/client/index.tsx +2 -1
- package/src/client/plugin.tsx +27 -27
- package/src/client/skill-hub/components/LoopSettings.tsx +331 -331
- package/src/client/skill-hub/index.tsx +51 -51
- package/src/client/skill-hub/tools/InteractionSchemasProvider.tsx +99 -99
- package/src/client/skill-hub/tools/SkillHubCard.tsx +109 -109
- package/src/client/skill-hub/tools/loopTemplates.ts +52 -52
- package/src/client/skill-hub/tools/registerSkillLoopCards.ts +58 -58
- package/src/client/tools/PlanApprovalCard.tsx +175 -175
- package/src/client/tools/registerOrchestratorCards.ts +7 -7
- package/src/server/__tests__/agent-loop-controller.test.ts +375 -0
- package/src/server/__tests__/circuit-breaker.test.ts +169 -0
- package/src/server/__tests__/context-aggregator.test.ts +222 -0
- package/src/server/__tests__/parallel-execution.test.ts +318 -0
- package/src/server/__tests__/smoke.test.ts +120 -0
- package/src/server/collections/agent-execution-spans.ts +24 -0
- package/src/server/collections/agent-harness-profiles.ts +59 -59
- package/src/server/collections/agent-loop-events.ts +71 -71
- package/src/server/collections/agent-loop-runs.ts +38 -1
- package/src/server/collections/agent-loop-steps.ts +144 -144
- package/src/server/collections/orchestrator-config.ts +14 -0
- package/src/server/collections/skill-executions.ts +106 -106
- package/src/server/collections/skill-loop-configs.ts +65 -65
- package/src/server/migrations/20260524000000-add-agent-loop-fields-to-skill-executions.ts +30 -30
- package/src/server/migrations/20260524001000-add-plan-approval-and-harness-profiles.ts +142 -142
- package/src/server/migrations/20260601000000-add-token-fields.ts +89 -0
- package/src/server/plugin.ts +53 -0
- package/src/server/resources/agent-loop.ts +21 -12
- package/src/server/resources/tracing.ts +3 -7
- package/src/server/services/AgentHarness.ts +78 -116
- package/src/server/services/AgentLoopController.ts +197 -122
- package/src/server/services/AgentLoopRepository.ts +9 -25
- package/src/server/services/AgentLoopService.ts +13 -1
- package/src/server/services/AgentPlanValidator.ts +73 -73
- package/src/server/services/AgentPlannerService.ts +2 -25
- package/src/server/services/AgentRegistryService.ts +40 -31
- package/src/server/services/CircuitBreaker.ts +116 -0
- package/src/server/services/ContextAggregator.ts +239 -0
- package/src/server/services/ExecutionSpanService.ts +2 -4
- package/src/server/services/RunEventBus.ts +45 -0
- package/src/server/services/TokenTracker.ts +209 -0
- package/src/server/skill-hub/plugin.ts +898 -897
- package/src/server/skill-hub/tasks/SkillExecutionTask.ts +460 -458
- package/src/server/tools/agent-loop.ts +18 -57
- package/src/server/tools/delegate-task.ts +11 -93
- package/src/server/tools/orchestrator-plan.ts +26 -50
- package/src/server/tools/skill-execute.ts +160 -160
- package/src/server/types.ts +55 -0
- package/src/server/utils/ctx-utils.ts +118 -0
- package/src/server/utils/logging.ts +63 -0
|
@@ -29,13 +29,20 @@ __export(AgentLoopController_exports, {
|
|
|
29
29
|
AgentLoopController: () => AgentLoopController
|
|
30
30
|
});
|
|
31
31
|
module.exports = __toCommonJS(AgentLoopController_exports);
|
|
32
|
+
var import_CircuitBreaker = require("./CircuitBreaker");
|
|
32
33
|
var import_crypto = require("crypto");
|
|
34
|
+
var import_ctx_utils = require("../utils/ctx-utils");
|
|
33
35
|
const DEFAULT_POLICY = {
|
|
34
36
|
maxIterations: 20,
|
|
35
37
|
maxStepAttempts: 2,
|
|
36
38
|
allowReplan: true,
|
|
37
39
|
requireVerification: true,
|
|
38
|
-
stopOnApprovalRequired: true
|
|
40
|
+
stopOnApprovalRequired: true,
|
|
41
|
+
maxContextTokens: 4e3,
|
|
42
|
+
contextSummaryStrategy: "last_n",
|
|
43
|
+
includeToolResults: false,
|
|
44
|
+
includeStepOutputs: true,
|
|
45
|
+
maxConcurrency: 5
|
|
39
46
|
};
|
|
40
47
|
const TERMINAL_RUN_STATUSES = /* @__PURE__ */ new Set(["succeeded", "failed", "rejected", "canceled"]);
|
|
41
48
|
const TERMINAL_STEP_STATUSES = /* @__PURE__ */ new Set(["succeeded", "skipped"]);
|
|
@@ -54,50 +61,21 @@ function normalizePolicy(policy) {
|
|
|
54
61
|
next.allowReplan = next.allowReplan !== false;
|
|
55
62
|
next.requireVerification = next.requireVerification !== false;
|
|
56
63
|
next.stopOnApprovalRequired = next.stopOnApprovalRequired !== false;
|
|
64
|
+
next.maxContextTokens = next.maxContextTokens || DEFAULT_POLICY.maxContextTokens;
|
|
65
|
+
next.contextSummaryStrategy = next.contextSummaryStrategy || DEFAULT_POLICY.contextSummaryStrategy;
|
|
66
|
+
next.includeToolResults = next.includeToolResults !== false;
|
|
67
|
+
next.includeStepOutputs = next.includeStepOutputs !== false;
|
|
68
|
+
next.maxConcurrency = Math.max(1, Number(next.maxConcurrency || DEFAULT_POLICY.maxConcurrency));
|
|
57
69
|
return next;
|
|
58
70
|
}
|
|
59
|
-
function asArray(value) {
|
|
60
|
-
return Array.isArray(value) ? value : [];
|
|
61
|
-
}
|
|
62
|
-
function asObject(value) {
|
|
63
|
-
if (value && typeof value === "object" && !Array.isArray(value)) return value;
|
|
64
|
-
if (typeof value === "string" && value.trim()) {
|
|
65
|
-
try {
|
|
66
|
-
const parsed = JSON.parse(value);
|
|
67
|
-
return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
|
|
68
|
-
} catch {
|
|
69
|
-
return {};
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
return {};
|
|
73
|
-
}
|
|
74
|
-
function trimText(value, max = 5e4) {
|
|
75
|
-
let text = "";
|
|
76
|
-
if (typeof value === "string") {
|
|
77
|
-
text = value;
|
|
78
|
-
} else if (value != null) {
|
|
79
|
-
try {
|
|
80
|
-
text = JSON.stringify(value);
|
|
81
|
-
} catch {
|
|
82
|
-
text = String(value);
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
return text.length > max ? `${text.slice(0, max)}
|
|
86
|
-
...[truncated]` : text;
|
|
87
|
-
}
|
|
88
|
-
function normalizeStepType(value) {
|
|
89
|
-
return ["reasoning", "skill", "tool", "sub_agent", "verification"].includes(value) ? value : "tool";
|
|
90
|
-
}
|
|
91
|
-
function normalizePlanKey(step, index) {
|
|
92
|
-
return String(step.planKey || step.key || step.id || `step_${index + 1}`);
|
|
93
|
-
}
|
|
94
71
|
class AgentLoopController {
|
|
95
|
-
constructor(registryService, plannerService, validator, repository, harness) {
|
|
72
|
+
constructor(registryService, plannerService, validator, repository, harness, tokenTracker = null) {
|
|
96
73
|
this.registryService = registryService;
|
|
97
74
|
this.plannerService = plannerService;
|
|
98
75
|
this.validator = validator;
|
|
99
76
|
this.repository = repository;
|
|
100
77
|
this.harness = harness;
|
|
78
|
+
this.tokenTracker = tokenTracker;
|
|
101
79
|
}
|
|
102
80
|
async createRun(options) {
|
|
103
81
|
const goal = String(options.goal || "").trim();
|
|
@@ -144,7 +122,7 @@ class AgentLoopController {
|
|
|
144
122
|
this.validator.validate(plan);
|
|
145
123
|
const harnessTag = String(options.harnessTag || ((_a = options.metadata) == null ? void 0 : _a.harnessTag) || "default").trim() || "default";
|
|
146
124
|
const harnessProfile = await this.registryService.getHarnessProfile(harnessTag);
|
|
147
|
-
const harnessSettings = asObject(harnessProfile == null ? void 0 : harnessProfile.settings);
|
|
125
|
+
const harnessSettings = (0, import_ctx_utils.asObject)(harnessProfile == null ? void 0 : harnessProfile.settings);
|
|
148
126
|
if (options.runId) {
|
|
149
127
|
return this.revisePlanGoal(options.runId, plan, {
|
|
150
128
|
goal: options.goal,
|
|
@@ -165,7 +143,7 @@ class AgentLoopController {
|
|
|
165
143
|
userId: options.userId,
|
|
166
144
|
policy: options.policy,
|
|
167
145
|
metadata: {
|
|
168
|
-
...asObject(options.metadata),
|
|
146
|
+
...(0, import_ctx_utils.asObject)(options.metadata),
|
|
169
147
|
harnessTag,
|
|
170
148
|
harnessProfileId: harnessProfile == null ? void 0 : harnessProfile.id,
|
|
171
149
|
harnessSettings,
|
|
@@ -198,11 +176,11 @@ class AgentLoopController {
|
|
|
198
176
|
planVersion: 1,
|
|
199
177
|
harnessTag,
|
|
200
178
|
steps: plan.map((step, index) => ({
|
|
201
|
-
planKey: normalizePlanKey(step, index),
|
|
179
|
+
planKey: (0, import_ctx_utils.normalizePlanKey)(step, index),
|
|
202
180
|
title: step.title || `Step ${index + 1}`,
|
|
203
|
-
type: normalizeStepType(step.type),
|
|
181
|
+
type: (0, import_ctx_utils.normalizeStepType)(step.type),
|
|
204
182
|
target: step.target || "",
|
|
205
|
-
dependsOn: asArray(step.dependsOn).map(String)
|
|
183
|
+
dependsOn: (0, import_ctx_utils.asArray)(step.dependsOn).map(String)
|
|
206
184
|
}))
|
|
207
185
|
}
|
|
208
186
|
});
|
|
@@ -234,11 +212,11 @@ class AgentLoopController {
|
|
|
234
212
|
rejectionReason: "",
|
|
235
213
|
changeRequest: "",
|
|
236
214
|
metadata: {
|
|
237
|
-
...asObject(run.metadata),
|
|
238
|
-
...asObject(options.metadata),
|
|
239
|
-
harnessTag: options.harnessTag || asObject(run.metadata).harnessTag || "default",
|
|
240
|
-
harnessProfileId: options.harnessProfileId || asObject(run.metadata).harnessProfileId,
|
|
241
|
-
harnessSettings: asObject(options.harnessSettings || asObject(run.metadata).harnessSettings),
|
|
215
|
+
...(0, import_ctx_utils.asObject)(run.metadata),
|
|
216
|
+
...(0, import_ctx_utils.asObject)(options.metadata),
|
|
217
|
+
harnessTag: options.harnessTag || (0, import_ctx_utils.asObject)(run.metadata).harnessTag || "default",
|
|
218
|
+
harnessProfileId: options.harnessProfileId || (0, import_ctx_utils.asObject)(run.metadata).harnessProfileId,
|
|
219
|
+
harnessSettings: (0, import_ctx_utils.asObject)(options.harnessSettings || (0, import_ctx_utils.asObject)(run.metadata).harnessSettings),
|
|
242
220
|
approvalMode: "plan_first"
|
|
243
221
|
},
|
|
244
222
|
updatedAt: now()
|
|
@@ -252,7 +230,7 @@ class AgentLoopController {
|
|
|
252
230
|
userId: options.userId,
|
|
253
231
|
payload: {
|
|
254
232
|
planVersion: nextPlanVersion,
|
|
255
|
-
steps: plan.map((step, index) => normalizePlanKey(step, index))
|
|
233
|
+
steps: plan.map((step, index) => (0, import_ctx_utils.normalizePlanKey)(step, index))
|
|
256
234
|
}
|
|
257
235
|
});
|
|
258
236
|
return this.getRunDetail(run.id);
|
|
@@ -377,20 +355,20 @@ class AgentLoopController {
|
|
|
377
355
|
const created = await this.repository.createStep({
|
|
378
356
|
runId: run.id,
|
|
379
357
|
parentStepId: step.parentStepId,
|
|
380
|
-
planKey: normalizePlanKey(step, i),
|
|
358
|
+
planKey: (0, import_ctx_utils.normalizePlanKey)(step, i),
|
|
381
359
|
index: indexStart + i,
|
|
382
360
|
title: step.title || `Step ${indexStart + i + 1}`,
|
|
383
361
|
description: step.description || "",
|
|
384
|
-
type: normalizeStepType(step.type),
|
|
362
|
+
type: (0, import_ctx_utils.normalizeStepType)(step.type),
|
|
385
363
|
target: step.target || "",
|
|
386
364
|
input: step.input || {},
|
|
387
365
|
output: {},
|
|
388
366
|
status: "pending",
|
|
389
367
|
attempt: 0,
|
|
390
368
|
maxAttempts: step.maxAttempts || policy.maxStepAttempts,
|
|
391
|
-
dependsOn: asArray(step.dependsOn).map(String),
|
|
369
|
+
dependsOn: (0, import_ctx_utils.asArray)(step.dependsOn).map(String),
|
|
392
370
|
dependencyPolicy: step.dependencyPolicy || ((_a = step.metadata) == null ? void 0 : _a.dependencyPolicy) || "require_success",
|
|
393
|
-
metadata: asObject(step.metadata)
|
|
371
|
+
metadata: (0, import_ctx_utils.asObject)(step.metadata)
|
|
394
372
|
});
|
|
395
373
|
createdSteps.push(created);
|
|
396
374
|
}
|
|
@@ -460,7 +438,6 @@ class AgentLoopController {
|
|
|
460
438
|
});
|
|
461
439
|
await this.repository.updateRun(run.id, {
|
|
462
440
|
status: "running",
|
|
463
|
-
currentStepId: step.id,
|
|
464
441
|
updatedAt: now()
|
|
465
442
|
});
|
|
466
443
|
await this.repository.createEvent({
|
|
@@ -498,22 +475,18 @@ class AgentLoopController {
|
|
|
498
475
|
if (step.status !== "running") {
|
|
499
476
|
throw new Error(`Step ${step.id} cannot complete from status "${step.status}".`);
|
|
500
477
|
}
|
|
501
|
-
if (!run.currentStepId || String(run.currentStepId) !== String(step.id)) {
|
|
502
|
-
throw new Error(`Step ${step.id} is not the current running step for run ${run.id}.`);
|
|
503
|
-
}
|
|
504
478
|
await this.repository.updateStep(step.id, {
|
|
505
479
|
status: "succeeded",
|
|
506
480
|
output: output === void 0 ? {} : output,
|
|
507
481
|
error: "",
|
|
508
482
|
skillExecutionId: options.skillExecutionId || step.skillExecutionId,
|
|
509
483
|
agentExecutionSpanId: options.agentExecutionSpanId || step.agentExecutionSpanId,
|
|
510
|
-
metadata: { ...asObject(step.metadata), ...asObject(options.metadata) },
|
|
484
|
+
metadata: { ...(0, import_ctx_utils.asObject)(step.metadata), ...(0, import_ctx_utils.asObject)(options.metadata) },
|
|
511
485
|
endedAt: now(),
|
|
512
486
|
updatedAt: now()
|
|
513
487
|
});
|
|
514
488
|
await this.repository.updateRun(run.id, {
|
|
515
489
|
status: "running",
|
|
516
|
-
currentStepId: null,
|
|
517
490
|
updatedAt: now()
|
|
518
491
|
});
|
|
519
492
|
await this.repository.createEvent({
|
|
@@ -521,7 +494,7 @@ class AgentLoopController {
|
|
|
521
494
|
stepId: step.id,
|
|
522
495
|
type: "step_succeeded",
|
|
523
496
|
title: `Completed: ${step.title || step.planKey}`,
|
|
524
|
-
content: trimText(output, 2e3),
|
|
497
|
+
content: (0, import_ctx_utils.trimText)(output, 2e3),
|
|
525
498
|
status: "succeeded",
|
|
526
499
|
userId: options.userId
|
|
527
500
|
});
|
|
@@ -536,20 +509,16 @@ class AgentLoopController {
|
|
|
536
509
|
if (step.status !== "running") {
|
|
537
510
|
throw new Error(`Step ${step.id} cannot fail from status "${step.status}".`);
|
|
538
511
|
}
|
|
539
|
-
if (!run.currentStepId || String(run.currentStepId) !== String(step.id)) {
|
|
540
|
-
throw new Error(`Step ${step.id} is not the current running step for run ${run.id}.`);
|
|
541
|
-
}
|
|
542
512
|
const policy = normalizePolicy(run.policy);
|
|
543
513
|
await this.repository.updateStep(step.id, {
|
|
544
514
|
status: "failed",
|
|
545
|
-
error: trimText(error, 1e4),
|
|
546
|
-
metadata: { ...asObject(step.metadata), ...asObject(options.metadata) },
|
|
515
|
+
error: (0, import_ctx_utils.trimText)(error, 1e4),
|
|
516
|
+
metadata: { ...(0, import_ctx_utils.asObject)(step.metadata), ...(0, import_ctx_utils.asObject)(options.metadata) },
|
|
547
517
|
endedAt: now(),
|
|
548
518
|
updatedAt: now()
|
|
549
519
|
});
|
|
550
520
|
await this.repository.updateRun(run.id, {
|
|
551
521
|
status: "running",
|
|
552
|
-
currentStepId: null,
|
|
553
522
|
updatedAt: now()
|
|
554
523
|
});
|
|
555
524
|
await this.repository.createEvent({
|
|
@@ -575,11 +544,6 @@ class AgentLoopController {
|
|
|
575
544
|
if (!["pending", "running", "failed"].includes(step.status)) {
|
|
576
545
|
throw new Error(`Step ${step.id} cannot skip from status "${step.status}".`);
|
|
577
546
|
}
|
|
578
|
-
if (step.status === "running") {
|
|
579
|
-
if (!run.currentStepId || String(run.currentStepId) !== String(step.id)) {
|
|
580
|
-
throw new Error(`Step ${step.id} is not the current running step for run ${run.id}.`);
|
|
581
|
-
}
|
|
582
|
-
}
|
|
583
547
|
await this.repository.updateStep(step.id, {
|
|
584
548
|
status: "skipped",
|
|
585
549
|
error: reason,
|
|
@@ -606,11 +570,6 @@ class AgentLoopController {
|
|
|
606
570
|
if (!["pending", "running"].includes(step.status)) {
|
|
607
571
|
throw new Error(`Step ${step.id} cannot request approval for status "${step.status}".`);
|
|
608
572
|
}
|
|
609
|
-
if (step.status === "running") {
|
|
610
|
-
if (!run.currentStepId || String(run.currentStepId) !== String(step.id)) {
|
|
611
|
-
throw new Error(`Step ${step.id} is not the current running step for run ${run.id}.`);
|
|
612
|
-
}
|
|
613
|
-
}
|
|
614
573
|
await this.repository.updateStep(step.id, {
|
|
615
574
|
status: "waiting_user",
|
|
616
575
|
approval: approval || {},
|
|
@@ -646,9 +605,6 @@ class AgentLoopController {
|
|
|
646
605
|
if (run.status !== "waiting_user" || step.status !== "waiting_user") {
|
|
647
606
|
throw new Error("Run is not waiting for user approval.");
|
|
648
607
|
}
|
|
649
|
-
if (run.currentStepId && String(run.currentStepId) !== String(step.id)) {
|
|
650
|
-
throw new Error(`Step ${step.id} is not the current waiting step for run ${run.id}.`);
|
|
651
|
-
}
|
|
652
608
|
if (options.approved) {
|
|
653
609
|
const nextValues = {
|
|
654
610
|
status: "pending",
|
|
@@ -708,12 +664,35 @@ class AgentLoopController {
|
|
|
708
664
|
if (Number(step.attempt || 0) >= Number(step.maxAttempts || policy.maxStepAttempts)) {
|
|
709
665
|
throw new Error(`Step ${step.id} reached maxAttempts=${step.maxAttempts}.`);
|
|
710
666
|
}
|
|
711
|
-
|
|
667
|
+
let routedTarget;
|
|
668
|
+
let routeReason;
|
|
669
|
+
if (step.type === "sub_agent" && run.leaderUsername) {
|
|
670
|
+
const target = step.target || "";
|
|
671
|
+
if (target) {
|
|
672
|
+
const circuitBreaker = (0, import_CircuitBreaker.getCircuitBreaker)();
|
|
673
|
+
const state = circuitBreaker.getState(target);
|
|
674
|
+
const attempt = Number(step.attempt || 0);
|
|
675
|
+
const shouldRoute = (state == null ? void 0 : state.state) === "open" || state && state.failures >= 2 && attempt >= 1;
|
|
676
|
+
if (shouldRoute) {
|
|
677
|
+
const alternatives = await this.registryService.findAlternativeSubAgents(run.leaderUsername, target);
|
|
678
|
+
if (alternatives.length > 0) {
|
|
679
|
+
const chosen = alternatives[0];
|
|
680
|
+
routedTarget = chosen.username;
|
|
681
|
+
routeReason = `Sub-agent "${target}" has ${(state == null ? void 0 : state.failures) || 0} failure(s) (circuit: ${(state == null ? void 0 : state.state) || "closed"}). Routing retry to alternative "${routedTarget}".`;
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
const updateValues = {
|
|
712
687
|
status: "pending",
|
|
713
688
|
error: "",
|
|
714
689
|
endedAt: null,
|
|
715
690
|
updatedAt: now()
|
|
716
|
-
}
|
|
691
|
+
};
|
|
692
|
+
if (routedTarget) {
|
|
693
|
+
updateValues.target = routedTarget;
|
|
694
|
+
}
|
|
695
|
+
await this.repository.updateStep(step.id, updateValues);
|
|
717
696
|
await this.repository.updateRun(run.id, {
|
|
718
697
|
status: "running",
|
|
719
698
|
updatedAt: now()
|
|
@@ -722,9 +701,11 @@ class AgentLoopController {
|
|
|
722
701
|
runId: run.id,
|
|
723
702
|
stepId: step.id,
|
|
724
703
|
type: "step_retry",
|
|
725
|
-
title: `Retry queued: ${step.title || step.planKey}`,
|
|
704
|
+
title: routedTarget ? `Retry routed: ${step.title || step.planKey} \u2192 ${routedTarget}` : `Retry queued: ${step.title || step.planKey}`,
|
|
705
|
+
content: routeReason || "",
|
|
726
706
|
status: "pending",
|
|
727
|
-
userId: options.userId
|
|
707
|
+
userId: options.userId,
|
|
708
|
+
payload: routedTarget ? { originalTarget: step.target, routedTarget, reason: routeReason } : void 0
|
|
728
709
|
});
|
|
729
710
|
return this.getRunSnapshot(run.id);
|
|
730
711
|
}
|
|
@@ -751,7 +732,7 @@ class AgentLoopController {
|
|
|
751
732
|
finalAnswer: finalAnswer || "",
|
|
752
733
|
summary: options.summary || run.summary,
|
|
753
734
|
currentStepId: null,
|
|
754
|
-
metadata: { ...asObject(run.metadata), evidence: options.evidence },
|
|
735
|
+
metadata: { ...(0, import_ctx_utils.asObject)(run.metadata), evidence: options.evidence },
|
|
755
736
|
endedAt: now(),
|
|
756
737
|
updatedAt: now()
|
|
757
738
|
});
|
|
@@ -766,6 +747,32 @@ class AgentLoopController {
|
|
|
766
747
|
});
|
|
767
748
|
return this.getRunSnapshot(run.id);
|
|
768
749
|
}
|
|
750
|
+
async stepFeedback(stepId, feedback, options = {}) {
|
|
751
|
+
const step = await this.repository.requireStep(stepId);
|
|
752
|
+
const run = await this.repository.requireRun(step.runId);
|
|
753
|
+
if (step.status !== "succeeded" && step.status !== "failed") {
|
|
754
|
+
throw new Error(`Cannot provide feedback for step ${step.id} in status "${step.status}".`);
|
|
755
|
+
}
|
|
756
|
+
await this.repository.createEvent({
|
|
757
|
+
runId: run.id,
|
|
758
|
+
stepId: step.id,
|
|
759
|
+
type: "step_feedback",
|
|
760
|
+
title: `Feedback: ${feedback.rating === "positive" ? "\u{1F44D}" : "\u{1F44E}"} ${step.title || step.planKey}`,
|
|
761
|
+
content: feedback.comment || "",
|
|
762
|
+
status: step.status,
|
|
763
|
+
userId: options.userId,
|
|
764
|
+
payload: {
|
|
765
|
+
rating: feedback.rating,
|
|
766
|
+
comment: feedback.comment || "",
|
|
767
|
+
category: feedback.category || "",
|
|
768
|
+
stepType: step.type,
|
|
769
|
+
stepPlanKey: step.planKey,
|
|
770
|
+
stepTarget: step.target || "",
|
|
771
|
+
attempt: step.attempt || 0
|
|
772
|
+
}
|
|
773
|
+
});
|
|
774
|
+
return this.getRunSnapshot(run.id);
|
|
775
|
+
}
|
|
769
776
|
async cancelRun(runId, options = {}) {
|
|
770
777
|
const run = await this.repository.requireRun(runId);
|
|
771
778
|
if (TERMINAL_RUN_STATUSES.has(run.status)) {
|
|
@@ -808,7 +815,7 @@ class AgentLoopController {
|
|
|
808
815
|
try {
|
|
809
816
|
let iterations = 0;
|
|
810
817
|
let snapshot = await this.getRunSnapshot(runId);
|
|
811
|
-
const harnessSettings = asObject((_b = (_a = snapshot.run) == null ? void 0 : _a.metadata) == null ? void 0 : _b.harnessSettings);
|
|
818
|
+
const harnessSettings = (0, import_ctx_utils.asObject)((_b = (_a = snapshot.run) == null ? void 0 : _a.metadata) == null ? void 0 : _b.harnessSettings);
|
|
812
819
|
const maxControllerSteps = Math.max(
|
|
813
820
|
1,
|
|
814
821
|
Math.min(
|
|
@@ -817,39 +824,67 @@ class AgentLoopController {
|
|
|
817
824
|
)
|
|
818
825
|
);
|
|
819
826
|
const policy = normalizePolicy(snapshot.run.policy);
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
const
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
} catch (error) {
|
|
832
|
-
if ((error == null ? void 0 : error.message) === "requires_approval") {
|
|
833
|
-
snapshot = await this.requestApproval(runningStep.id, {
|
|
834
|
-
prompt: `Execution of step "${runningStep.title}" requires permission.`
|
|
835
|
-
}, {
|
|
836
|
-
userId: options.userId,
|
|
837
|
-
reason: "Dynamic tool approval required by policy."
|
|
827
|
+
const maxConcurrency = policy.maxConcurrency;
|
|
828
|
+
while (snapshot.nextSteps && snapshot.nextSteps.length > 0 && iterations < maxControllerSteps) {
|
|
829
|
+
const batch = snapshot.nextSteps.slice(0, maxConcurrency);
|
|
830
|
+
iterations += batch.length;
|
|
831
|
+
if (this.tokenTracker) {
|
|
832
|
+
const budgetCheck = await this.tokenTracker.checkBudget(runId);
|
|
833
|
+
if (!budgetCheck.allowed) {
|
|
834
|
+
return this.finishRun(runId, budgetCheck.reason, {
|
|
835
|
+
status: "failed",
|
|
836
|
+
summary: budgetCheck.reason,
|
|
837
|
+
userId: options.userId
|
|
838
838
|
});
|
|
839
|
-
break;
|
|
840
839
|
}
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
840
|
+
}
|
|
841
|
+
await Promise.all(batch.map((step) => this.startStep(step.id, { userId: options.userId })));
|
|
842
|
+
snapshot = await this.getRunSnapshot(runId);
|
|
843
|
+
const runningSteps = batch.map(
|
|
844
|
+
(step) => snapshot.steps.find((s) => String(s.id) === String(step.id)) || step
|
|
845
|
+
);
|
|
846
|
+
const results = await Promise.allSettled(
|
|
847
|
+
runningSteps.map((step) => this.harness.executeStep(snapshot.run, step, options))
|
|
848
|
+
);
|
|
849
|
+
for (let i = 0; i < results.length; i++) {
|
|
850
|
+
const runningStep = runningSteps[i];
|
|
851
|
+
const result = results[i];
|
|
852
|
+
if (result.status === "fulfilled") {
|
|
853
|
+
snapshot = await this.completeStep(runningStep.id, result.value, {
|
|
854
|
+
userId: options.userId,
|
|
855
|
+
metadata: { controller: "agent-loop-service" }
|
|
856
|
+
});
|
|
857
|
+
} else {
|
|
858
|
+
const error = result.reason;
|
|
859
|
+
if ((error == null ? void 0 : error.message) === "requires_approval") {
|
|
860
|
+
snapshot = await this.requestApproval(
|
|
861
|
+
runningStep.id,
|
|
862
|
+
{ prompt: `Execution of step "${runningStep.title}" requires permission.` },
|
|
863
|
+
{ userId: options.userId, reason: "Dynamic tool approval required by policy." }
|
|
864
|
+
);
|
|
865
|
+
break;
|
|
866
|
+
}
|
|
867
|
+
snapshot = await this.failStep(runningStep.id, (error == null ? void 0 : error.message) || String(error), {
|
|
868
|
+
userId: options.userId,
|
|
869
|
+
metadata: { controller: "agent-loop-service" }
|
|
870
|
+
});
|
|
871
|
+
const failedStep = snapshot.steps.find((s) => String(s.id) === String(runningStep.id));
|
|
872
|
+
if (failedStep && Number(failedStep.attempt || 0) < Number(failedStep.maxAttempts || policy.maxStepAttempts)) {
|
|
873
|
+
const attempt = Number(failedStep.attempt || 1);
|
|
874
|
+
const baseDelay = 1e3;
|
|
875
|
+
const maxDelay = 6e4;
|
|
876
|
+
const delay = Math.min(baseDelay * Math.pow(2, attempt - 1), maxDelay);
|
|
877
|
+
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
878
|
+
}
|
|
848
879
|
}
|
|
849
880
|
}
|
|
881
|
+
snapshot = await this.getRunSnapshot(runId);
|
|
882
|
+
if (snapshot.run.status === "waiting_user") {
|
|
883
|
+
break;
|
|
884
|
+
}
|
|
850
885
|
}
|
|
851
886
|
snapshot = await this.getRunSnapshot(runId);
|
|
852
|
-
if (iterations >= maxControllerSteps && snapshot.
|
|
887
|
+
if (iterations >= maxControllerSteps && snapshot.nextSteps && snapshot.nextSteps.length > 0) {
|
|
853
888
|
return this.finishRun(runId, `Agent loop stopped after ${maxControllerSteps} controller steps.`, {
|
|
854
889
|
status: "failed",
|
|
855
890
|
summary: "Controller iteration limit reached.",
|
|
@@ -861,7 +896,9 @@ class AgentLoopController {
|
|
|
861
896
|
}
|
|
862
897
|
const steps = snapshot.steps || [];
|
|
863
898
|
const failed = steps.filter((step) => step.status === "failed");
|
|
864
|
-
const unfinished = steps.filter(
|
|
899
|
+
const unfinished = steps.filter(
|
|
900
|
+
(step) => !TERMINAL_STEP_STATUSES.has(step.status) && step.status !== "failed"
|
|
901
|
+
);
|
|
865
902
|
if (failed.length || unfinished.length) {
|
|
866
903
|
return this.finishRun(
|
|
867
904
|
runId,
|
|
@@ -869,7 +906,10 @@ class AgentLoopController {
|
|
|
869
906
|
{
|
|
870
907
|
status: "failed",
|
|
871
908
|
summary: ((_c = failed[0]) == null ? void 0 : _c.error) || "No executable step is available.",
|
|
872
|
-
evidence: {
|
|
909
|
+
evidence: {
|
|
910
|
+
failedStepIds: failed.map((step) => step.id),
|
|
911
|
+
unfinishedStepIds: unfinished.map((step) => step.id)
|
|
912
|
+
},
|
|
873
913
|
userId: options.userId
|
|
874
914
|
}
|
|
875
915
|
);
|
|
@@ -891,11 +931,12 @@ class AgentLoopController {
|
|
|
891
931
|
async getRunSnapshot(runId) {
|
|
892
932
|
const run = await this.repository.requireRun(runId);
|
|
893
933
|
const steps = await this.repository.getSteps(run.id);
|
|
894
|
-
const
|
|
934
|
+
const nextSteps = this.pickNextSteps(steps, run.policy);
|
|
935
|
+
const runningStepIds = steps.filter((s) => s.status === "running").map((s) => s.id);
|
|
895
936
|
return {
|
|
896
|
-
run,
|
|
937
|
+
run: { ...run, runningStepIds },
|
|
897
938
|
steps,
|
|
898
|
-
|
|
939
|
+
nextSteps
|
|
899
940
|
};
|
|
900
941
|
}
|
|
901
942
|
async getRunDetail(runId) {
|
|
@@ -910,28 +951,26 @@ class AgentLoopController {
|
|
|
910
951
|
skillExecutions
|
|
911
952
|
};
|
|
912
953
|
}
|
|
913
|
-
|
|
954
|
+
pickNextSteps(steps, runPolicy) {
|
|
914
955
|
var _a;
|
|
915
956
|
const byPlanKey = new Map(steps.map((step) => [String(step.planKey), step]));
|
|
916
957
|
const policy = normalizePolicy(runPolicy);
|
|
917
958
|
const candidates = steps.filter(
|
|
918
959
|
(step) => step.status === "pending" || step.status === "failed" && Number(step.attempt || 0) < Number(step.maxAttempts || policy.maxStepAttempts)
|
|
919
960
|
).sort((a, b) => Number(a.index || 0) - Number(b.index || 0));
|
|
961
|
+
const ready = [];
|
|
920
962
|
for (const step of candidates) {
|
|
921
|
-
const dependencies = asArray(step.dependsOn).map(String);
|
|
963
|
+
const dependencies = (0, import_ctx_utils.asArray)(step.dependsOn).map(String);
|
|
922
964
|
const allowSkipped = step.dependencyPolicy === "allow_skipped" || ((_a = step.metadata) == null ? void 0 : _a.dependencyPolicy) === "allow_skipped";
|
|
923
|
-
const
|
|
965
|
+
const allDepsReady = dependencies.every((key) => {
|
|
924
966
|
const dependency = byPlanKey.get(key);
|
|
925
967
|
return (dependency == null ? void 0 : dependency.status) === "succeeded" || allowSkipped && (dependency == null ? void 0 : dependency.status) === "skipped";
|
|
926
968
|
});
|
|
927
|
-
if (
|
|
928
|
-
|
|
929
|
-
...step,
|
|
930
|
-
retryable: step.status === "failed"
|
|
931
|
-
};
|
|
969
|
+
if (allDepsReady) {
|
|
970
|
+
ready.push(step);
|
|
932
971
|
}
|
|
933
972
|
}
|
|
934
|
-
return
|
|
973
|
+
return ready;
|
|
935
974
|
}
|
|
936
975
|
}
|
|
937
976
|
// Annotate the CommonJS export names for ESM import in node:
|
|
@@ -29,24 +29,8 @@ __export(AgentLoopRepository_exports, {
|
|
|
29
29
|
AgentLoopRepository: () => AgentLoopRepository
|
|
30
30
|
});
|
|
31
31
|
module.exports = __toCommonJS(AgentLoopRepository_exports);
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
return ((_a = record == null ? void 0 : record.toJSON) == null ? void 0 : _a.call(record)) || record;
|
|
35
|
-
}
|
|
36
|
-
function trimText(value, max = 5e4) {
|
|
37
|
-
let text = "";
|
|
38
|
-
if (typeof value === "string") {
|
|
39
|
-
text = value;
|
|
40
|
-
} else if (value != null) {
|
|
41
|
-
try {
|
|
42
|
-
text = JSON.stringify(value);
|
|
43
|
-
} catch {
|
|
44
|
-
text = String(value);
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
return text.length > max ? `${text.slice(0, max)}
|
|
48
|
-
...[truncated]` : text;
|
|
49
|
-
}
|
|
32
|
+
var import_ctx_utils = require("../utils/ctx-utils");
|
|
33
|
+
var import_RunEventBus = require("./RunEventBus");
|
|
50
34
|
class AgentLoopRepository {
|
|
51
35
|
constructor(plugin) {
|
|
52
36
|
this.plugin = plugin;
|
|
@@ -58,7 +42,7 @@ class AgentLoopRepository {
|
|
|
58
42
|
const run = await this.db.getRepository("agentLoopRuns").findOne({
|
|
59
43
|
filter: { id: runId }
|
|
60
44
|
});
|
|
61
|
-
return run ? toPlain(run) : null;
|
|
45
|
+
return run ? (0, import_ctx_utils.toPlain)(run) : null;
|
|
62
46
|
}
|
|
63
47
|
async requireRun(runId) {
|
|
64
48
|
const run = await this.getRun(runId);
|
|
@@ -71,7 +55,7 @@ class AgentLoopRepository {
|
|
|
71
55
|
const run = await this.db.getRepository("agentLoopRuns").create({
|
|
72
56
|
values
|
|
73
57
|
});
|
|
74
|
-
return toPlain(run);
|
|
58
|
+
return (0, import_ctx_utils.toPlain)(run);
|
|
75
59
|
}
|
|
76
60
|
async updateRun(runId, values) {
|
|
77
61
|
await this.db.getRepository("agentLoopRuns").update({
|
|
@@ -83,7 +67,7 @@ class AgentLoopRepository {
|
|
|
83
67
|
const step = await this.db.getRepository("agentLoopSteps").findOne({
|
|
84
68
|
filter: { id: stepId }
|
|
85
69
|
});
|
|
86
|
-
return step ? toPlain(step) : null;
|
|
70
|
+
return step ? (0, import_ctx_utils.toPlain)(step) : null;
|
|
87
71
|
}
|
|
88
72
|
async requireStep(stepId) {
|
|
89
73
|
const step = await this.getStep(stepId);
|
|
@@ -96,7 +80,7 @@ class AgentLoopRepository {
|
|
|
96
80
|
const step = await this.db.getRepository("agentLoopSteps").create({
|
|
97
81
|
values
|
|
98
82
|
});
|
|
99
|
-
return toPlain(step);
|
|
83
|
+
return (0, import_ctx_utils.toPlain)(step);
|
|
100
84
|
}
|
|
101
85
|
async updateStep(stepId, values) {
|
|
102
86
|
await this.db.getRepository("agentLoopSteps").update({
|
|
@@ -110,18 +94,20 @@ class AgentLoopRepository {
|
|
|
110
94
|
sort: ["index", "createdAt"],
|
|
111
95
|
pageSize: 1e3
|
|
112
96
|
});
|
|
113
|
-
return steps.map(toPlain);
|
|
97
|
+
return steps.map(import_ctx_utils.toPlain);
|
|
114
98
|
}
|
|
115
99
|
async createEvent(values) {
|
|
116
100
|
const record = await this.db.getRepository("agentLoopEvents").create({
|
|
117
101
|
values: {
|
|
118
102
|
...values,
|
|
119
|
-
content: trimText(values.content || "", 1e4),
|
|
103
|
+
content: (0, import_ctx_utils.trimText)(values.content || "", 1e4),
|
|
120
104
|
payload: values.payload || {},
|
|
121
105
|
createdAt: /* @__PURE__ */ new Date()
|
|
122
106
|
}
|
|
123
107
|
});
|
|
124
|
-
|
|
108
|
+
const event = (0, import_ctx_utils.toPlain)(record);
|
|
109
|
+
(0, import_RunEventBus.getRunEventBus)().emit(values.runId, event);
|
|
110
|
+
return event;
|
|
125
111
|
}
|
|
126
112
|
async getEvents(runId) {
|
|
127
113
|
const events = await this.db.getRepository("agentLoopEvents").find({
|
|
@@ -129,7 +115,7 @@ class AgentLoopRepository {
|
|
|
129
115
|
sort: ["createdAt"],
|
|
130
116
|
pageSize: 500
|
|
131
117
|
});
|
|
132
|
-
return events.map(toPlain);
|
|
118
|
+
return events.map(import_ctx_utils.toPlain);
|
|
133
119
|
}
|
|
134
120
|
async getLinkedSpans(runId, rootRunId) {
|
|
135
121
|
const repo = this.db.getRepository("agentExecutionSpans");
|
|
@@ -143,7 +129,7 @@ class AgentLoopRepository {
|
|
|
143
129
|
sort: ["createdAt"],
|
|
144
130
|
pageSize: 1e3
|
|
145
131
|
});
|
|
146
|
-
return rows.map(toPlain);
|
|
132
|
+
return rows.map(import_ctx_utils.toPlain);
|
|
147
133
|
} catch {
|
|
148
134
|
if (!rootRunId) return [];
|
|
149
135
|
const rows = await repo.find({
|
|
@@ -151,7 +137,7 @@ class AgentLoopRepository {
|
|
|
151
137
|
sort: ["createdAt"],
|
|
152
138
|
pageSize: 1e3
|
|
153
139
|
});
|
|
154
|
-
return rows.map(toPlain);
|
|
140
|
+
return rows.map(import_ctx_utils.toPlain);
|
|
155
141
|
}
|
|
156
142
|
}
|
|
157
143
|
async getLinkedSkillExecutions(runId, steps) {
|
|
@@ -171,7 +157,7 @@ class AgentLoopRepository {
|
|
|
171
157
|
sort: ["createdAt"],
|
|
172
158
|
pageSize: 1e3
|
|
173
159
|
});
|
|
174
|
-
return rows.map(toPlain);
|
|
160
|
+
return rows.map(import_ctx_utils.toPlain);
|
|
175
161
|
}
|
|
176
162
|
async lockRun(runId, lockName, durationMs) {
|
|
177
163
|
const repo = this.db.getRepository("agentLoopRuns");
|
|
@@ -186,11 +172,7 @@ class AgentLoopRepository {
|
|
|
186
172
|
{
|
|
187
173
|
where: {
|
|
188
174
|
id: runId,
|
|
189
|
-
[Op.or]: [
|
|
190
|
-
{ lockedBy: null },
|
|
191
|
-
{ lockedUntil: { [Op.lt]: now.toISOString() } },
|
|
192
|
-
{ lockedBy: lockName }
|
|
193
|
-
]
|
|
175
|
+
[Op.or]: [{ lockedBy: null }, { lockedUntil: { [Op.lt]: now.toISOString() } }, { lockedBy: lockName }]
|
|
194
176
|
}
|
|
195
177
|
}
|
|
196
178
|
);
|