@tea-agent/loop-agent 0.10.0 → 0.11.0
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/AGENTS.md +10 -2
- package/CHANGELOG.md +67 -25
- package/README.md +82 -11
- package/dist/application/dag/args.js +1 -12
- package/dist/application/dag/generate-task-dag.js +23 -2
- package/dist/application/dag/run-dag.js +1 -27
- package/dist/application/dag/validate-dag.js +2 -2
- package/dist/application/loop/run-action.js +0 -4
- package/dist/cli/command-definitions.js +44 -16
- package/dist/cli/program.js +40 -23
- package/dist/cli/update/notifier.js +117 -0
- package/dist/cli/update/npm-client.js +151 -0
- package/dist/cli/update/policy.js +58 -0
- package/dist/cli/update/state.js +68 -0
- package/dist/cli.js +33 -0
- package/dist/commands/cursor-prompt.js +42 -82
- package/dist/commands/dag-approve.js +36 -0
- package/dist/commands/delegate.js +75 -77
- package/dist/commands/doctor.js +0 -18
- package/dist/commands/init.js +476 -91
- package/dist/commands/instructions.js +7 -10
- package/dist/commands/loop.js +4 -20
- package/dist/commands/plan.js +50 -0
- package/dist/executors/config-core.js +0 -51
- package/dist/executors/dag-pi-executor.js +1 -1
- package/dist/executors/dag.js +0 -1
- package/dist/executors/index.js +0 -2
- package/dist/executors/model-routing.js +9 -9
- package/dist/executors/shell-executor.js +1 -1
- package/dist/governance/checks.js +6 -3
- package/dist/governance/exec-plans.js +545 -0
- package/dist/governance/manifest-types.js +24 -2
- package/dist/infrastructure/harness/loop-action-store.js +0 -3
- package/dist/records/harvest.js +2 -23
- package/dist/records/one-shot-runs.js +1 -1
- package/dist/shared/artifacts-core.js +24 -5
- package/dist/shared/output-truncation.js +37 -0
- package/dist/shared/package-metadata.js +353 -0
- package/dist/{executors/cursor-executor.js → sidecars/cursor-prompt/executor.js} +2 -42
- package/dist/sidecars/cursor-prompt/index.js +3 -0
- package/dist/sidecars/cursor-prompt/stream.js +121 -0
- package/dist/task/config-types.js +28 -12
- package/dist/task/delegate.js +9 -21
- package/dist/task/runtime.js +1 -2
- package/dist/worker/cli.js +29 -2
- package/dist/worker/delivery/final-verification.js +47 -11
- package/dist/worker/delivery/package.js +63 -10
- package/dist/worker/feature/run.js +60 -8
- package/dist/worker/loop-agent/loop-agent-client.js +329 -126
- package/dist/worker/observability/read-model.js +27 -1
- package/dist/worker/observe/static/app.js +326 -45
- package/dist/worker/observe/static/index.html +1 -1
- package/dist/worker/observe/static/styles.css +5 -4
- package/dist/worker/preflight.js +49 -1
- package/dist/worker/run-task/run-task.js +22 -12
- package/dist/worker/runner/run-ready.js +76 -12
- package/dist/worker/task-spec/schema.js +0 -1
- package/dist/workflows/dag/convergence/controller.js +1 -1
- package/dist/workflows/dag/executor-registry.js +0 -2
- package/dist/workflows/dag/init-hybrid.js +402 -25
- package/dist/workflows/dag/node-execution.js +61 -7
- package/dist/workflows/dag/runner.js +45 -17
- package/dist/workflows/dag/scheduler.js +7 -2
- package/dist/workflows/dag/sdd-embedded.js +128 -0
- package/dist/workflows/dag/skill-instructions.js +5 -4
- package/dist/workflows/dag/skill-snapshot.js +527 -0
- package/dist/workflows/dag/types.js +42 -9
- package/dist/workflows/dag/validate.js +5 -8
- package/dist/workflows/loop/actions/dag-action.js +0 -2
- package/dist/workflows/loop/actions/shared.js +1 -1
- package/dist/workflows/loop/actions.js +14 -31
- package/dist/workflows/loop/benchmark.js +1 -1
- package/dist/workflows/loop/index.js +1 -1
- package/dist/workflows/loop/policy/auto-policy.js +22 -14
- package/dist/workflows/loop/policy/path-patterns.js +13 -0
- package/docs/README.md +36 -33
- package/docs/agent-dag-recovery-playbook.md +1 -1
- package/docs/agent-dag-runner.md +2 -2
- package/docs/architecture/README.md +26 -0
- package/docs/architecture/dag-execution.md +134 -0
- package/docs/architecture/evolution.md +52 -0
- package/docs/architecture/facts-and-state.md +58 -0
- package/docs/architecture/runtime-boundaries.md +45 -17
- package/docs/architecture/system-overview.md +93 -0
- package/docs/architecture/worker-and-feature.md +81 -0
- package/docs/cursor-prompt-sidecar.md +36 -0
- package/docs/decisions/README.md +13 -1
- package/docs/design/README.md +42 -21
- package/docs/development-principles.md +2 -2
- package/docs/exec-plans/active/README.md +2 -2
- package/docs/exec-plans/completed/README.md +12 -0
- package/docs/feature-workflow.md +50 -4
- package/docs/harness-methodology-debugging.md +1 -1
- package/docs/harness-methodology-tdd.md +3 -3
- package/docs/init-surface.manifest.json +60 -25
- package/docs/loop-agent-harness.md +28 -4
- package/docs/progress/README.md +32 -1
- package/docs/reports/README.md +84 -18
- package/docs/skills/README.md +2 -1
- package/docs/skills/vetted-skill-registry.md +2 -1
- package/docs/templates/agent-dag-report.schema.json +6 -6
- package/docs/templates/agent-dag.base.json +0 -5
- package/docs/templates/agent-dag.final-verification.json +0 -5
- package/docs/templates/agent-dag.schema.json +1 -2
- package/docs/templates/agent-dag.supervised-implementation.json +1 -6
- package/docs/templates/frontend-design-contract.md +33 -0
- package/docs/templates/frontend-task-constraints.md +25 -0
- package/docs/templates/frontend-task-requirement.md +61 -0
- package/docs/templates/harness.schema.json +10 -12
- package/docs/templates/hybrid-dag.json +1 -6
- package/docs/templates/interactive-ui-round2-experiment.md +1 -1
- package/docs/templates/product-line/task.yaml +0 -1
- package/docs/templates/project-start-checklist.md +2 -2
- package/docs/templates/worker-dogfood-evidence.md +28 -0
- package/docs/templates/worker-dogfood-setup.md +20 -0
- package/docs/verification-matrix.md +10 -0
- package/examples/decision-gate-agent-dag.json +87 -33
- package/examples/example-dag.json +0 -5
- package/examples/hybrid-loop-agent-dag.json +0 -5
- package/harness.json +7 -15
- package/package.json +22 -46
- package/scripts/check-product-line-docs.sh +10 -7
- package/skills/agent-worker/SKILL.md +37 -0
- package/skills/agent-worker/references/agent-worker-operator.md +43 -0
- package/skills/frontend-design-review/SKILL.md +59 -0
- package/skills/frontend-design-review/references/review-checklist.md +37 -0
- package/skills/frontend-implementation/SKILL.md +51 -0
- package/skills/frontend-implementation/references/code-standards.md +34 -0
- package/skills/frontend-implementation/references/design-spec.md +46 -0
- package/skills/frontend-implementation/references/node-contracts.md +32 -0
- package/skills/frontend-review/SKILL.md +53 -0
- package/skills/frontend-review/references/review-findings.md +42 -0
- package/skills/frontend-verification/SKILL.md +40 -0
- package/skills/frontend-verification/references/verification-checklist.md +56 -0
- package/skills/grill-me/SKILL.md +10 -0
- package/skills/grill-with-docs/SKILL.md +88 -0
- package/skills/grill-with-docs/adr-format.md +47 -0
- package/skills/grill-with-docs/context-format.md +60 -0
- package/skills/loop-agent/SKILL.md +11 -9
- package/skills/loop-agent/references/command-reference.md +13 -15
- package/skills/loop-agent/references/docs-converge.md +126 -0
- package/skills/loop-agent/references/harness-policy.md +7 -7
- package/skills/loop-agent/references/hybrid-dag.md +15 -18
- package/skills/loop-agent/references/long-running-loop.md +4 -6
- package/skills/loop-agent/references/multi-worktree.md +6 -6
- package/skills/loop-agent/references/orchestrator-and-interventions.md +3 -3
- package/skills/loop-agent/references/pi-subagent-assisted-mode.md +14 -11
- package/skills/loop-agent/references/task-workflow.md +1 -1
- package/skills/using-git-worktrees/SKILL.md +215 -0
- package/dist/commands/cursor-worker.js +0 -43
- package/dist/cursor-worker-entry.js +0 -8
- package/dist/executors/cursor-artifacts.js +0 -33
- package/dist/executors/cursor-execution-log.js +0 -81
- package/dist/executors/cursor-executor-artifacts.js +0 -134
- package/dist/executors/cursor-run.js +0 -115
- package/dist/executors/cursor-tool.js +0 -94
- package/dist/executors/cursor-worker-client.js +0 -223
- package/dist/executors/cursor-worker-protocol.js +0 -18
- package/dist/executors/cursor-worker-server.js +0 -54
- package/dist/executors/cursor-worker.js +0 -3
- package/dist/executors/cursor.js +0 -6
- package/dist/executors/dag-cursor-executor.js +0 -87
- package/dist/workflows/loop/actions/cursor-fix.js +0 -191
- package/dist/workflows/loop/policy/cursor-fix-policy.js +0 -31
- package/docs/cursor-executor-usage.md +0 -25
- package/docs/dynamic-workflow-dag-engine-roadmap.md +0 -1749
|
@@ -8,6 +8,7 @@ import { BASELINE_FORBIDDEN_PATHS } from "./governance-constants.js";
|
|
|
8
8
|
import { resolveAdapter } from "../../adapters/index.js";
|
|
9
9
|
import { loadHarnessManifest } from "../../governance/harness.js";
|
|
10
10
|
import { buildAuthoritySurfaceAuditNode, buildAuthoritySurfaceGateNode, resolveAuthoritySurfaceAudit, } from "./authority-surface.js";
|
|
11
|
+
import { applySddEmbeddedEnhancements, probeRepoLocalSddSkills, } from "./sdd-embedded.js";
|
|
11
12
|
import { getTaskPaths, loadTaskConfig } from "../../task/runtime.js";
|
|
12
13
|
import { materializeTaskReferenceDocs } from "../../task/source-references.js";
|
|
13
14
|
import { resolveVerifyPreset } from "../../executors/shell-verification.js";
|
|
@@ -58,27 +59,38 @@ const HYBRID_SKILLS_BY_ROLE = {
|
|
|
58
59
|
verifier: ["verification-before-completion", "systematic-debugging"],
|
|
59
60
|
closeout: ["loop-agent", "verification-before-completion"],
|
|
60
61
|
};
|
|
62
|
+
const FRONTEND_DEFAULTS = {
|
|
63
|
+
...HYBRID_DEFAULTS,
|
|
64
|
+
skills: [],
|
|
65
|
+
};
|
|
66
|
+
const FRONTEND_SKILLS_BY_ROLE = {
|
|
67
|
+
planner: [],
|
|
68
|
+
scout: [],
|
|
69
|
+
implementer: [],
|
|
70
|
+
reviewer: [],
|
|
71
|
+
supervisor: [],
|
|
72
|
+
verifier: [],
|
|
73
|
+
closeout: [],
|
|
74
|
+
};
|
|
75
|
+
const FRONTEND_IMPLEMENTATION_SKILLS = ["frontend-implementation"];
|
|
76
|
+
const FRONTEND_DESIGN_REVIEW_SKILLS = ["frontend-design-review"];
|
|
77
|
+
const FRONTEND_REVIEW_SKILLS = ["frontend-review"];
|
|
78
|
+
const FRONTEND_VERIFICATION_SKILLS = ["frontend-verification"];
|
|
61
79
|
const IMPLEMENT_WRITESET_PLACEHOLDER = "REPLACE/WITH/NARROW/IMPLEMENT/PATHS/**";
|
|
62
80
|
function resolveEnabledExecutors(manifestExecutors) {
|
|
63
81
|
return Object.entries(manifestExecutors)
|
|
64
82
|
.filter(([, config]) => config.enabled !== false)
|
|
65
|
-
.map(([name]) => name)
|
|
83
|
+
.map(([name]) => name)
|
|
84
|
+
.filter((name) => name === "pi");
|
|
66
85
|
}
|
|
67
|
-
function
|
|
68
|
-
|
|
69
|
-
? new Set(sources.enabledExecutors)
|
|
70
|
-
: undefined;
|
|
71
|
-
if (!enabled || enabled.has("pi"))
|
|
72
|
-
return "pi";
|
|
73
|
-
if (enabled.has("cursor"))
|
|
74
|
-
return "cursor";
|
|
75
|
-
return "pi";
|
|
86
|
+
function implementationNodeId() {
|
|
87
|
+
return "implement-pi";
|
|
76
88
|
}
|
|
77
|
-
function
|
|
78
|
-
return
|
|
89
|
+
function repairNodeId() {
|
|
90
|
+
return "repair-pi";
|
|
79
91
|
}
|
|
80
|
-
function
|
|
81
|
-
return
|
|
92
|
+
function frontendImplementationNodeId() {
|
|
93
|
+
return "frontend-implement-pi";
|
|
82
94
|
}
|
|
83
95
|
function probePathsForWriteSetEntry(entry) {
|
|
84
96
|
const normalized = entry.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
@@ -275,6 +287,65 @@ function buildVerifyShellCommands(input) {
|
|
|
275
287
|
const repoRoot = input.repoRoot;
|
|
276
288
|
return input.commands.map((command) => verifyCommandToShell(repoRoot, command));
|
|
277
289
|
}
|
|
290
|
+
function markdownVerifyCommand(repoRoot, command) {
|
|
291
|
+
const args = command.split(/\s+/).filter(Boolean);
|
|
292
|
+
if (args.length === 0 ||
|
|
293
|
+
args.some((arg) => !/^[a-z0-9@._/:=,+-]+$/i.test(arg))) {
|
|
294
|
+
return null;
|
|
295
|
+
}
|
|
296
|
+
return {
|
|
297
|
+
args,
|
|
298
|
+
cwd: repoRoot,
|
|
299
|
+
label: command,
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
function extractFrontendVerifyCommandsFromMarkdown(input) {
|
|
303
|
+
if (!input.repoRoot)
|
|
304
|
+
return { staticCommands: [], behaviorCommands: [] };
|
|
305
|
+
const markdown = [
|
|
306
|
+
input.requirementMarkdown,
|
|
307
|
+
input.constraintMarkdown ?? "",
|
|
308
|
+
].join("\n");
|
|
309
|
+
const commands = new Set();
|
|
310
|
+
for (const line of markdown.split(/\r?\n/)) {
|
|
311
|
+
const bulletless = line
|
|
312
|
+
.trim()
|
|
313
|
+
.replace(/^[-*]\s*(?:\[[ xX]\]\s*)?/, "")
|
|
314
|
+
.trim();
|
|
315
|
+
const codeSpanCommands = Array.from(bulletless.matchAll(/`([^`]+)`/g), (match) => match[1].trim());
|
|
316
|
+
const candidates = codeSpanCommands.length > 0 ? codeSpanCommands : [bulletless];
|
|
317
|
+
for (const candidate of candidates) {
|
|
318
|
+
if (/^(npm|pnpm|yarn|bun)\s+(run\s+)?[a-z0-9:_-]+(?:\s.*)?$/i.test(candidate) ||
|
|
319
|
+
/^(npx|pnpm\s+exec|yarn\s+exec|bunx)\s+(vitest|jest|playwright|cypress|tsc|eslint)(?:\s.*)?$/i.test(candidate)) {
|
|
320
|
+
commands.add(candidate);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
const staticCommands = [];
|
|
325
|
+
const behaviorCommands = [];
|
|
326
|
+
for (const command of commands) {
|
|
327
|
+
const verifyCommand = markdownVerifyCommand(input.repoRoot, command);
|
|
328
|
+
if (!verifyCommand)
|
|
329
|
+
continue;
|
|
330
|
+
if (/\b(typecheck|lint|eslint|tsc|build)\b/i.test(command)) {
|
|
331
|
+
staticCommands.push(verifyCommand);
|
|
332
|
+
continue;
|
|
333
|
+
}
|
|
334
|
+
if (/\b(test|vitest|jest|playwright|cypress|e2e)\b/i.test(command)) {
|
|
335
|
+
behaviorCommands.push(verifyCommand);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
return { staticCommands, behaviorCommands };
|
|
339
|
+
}
|
|
340
|
+
function chooseFrontendVerifyCommands(input) {
|
|
341
|
+
if (input.parsedCommands.length > 0) {
|
|
342
|
+
return { commands: input.parsedCommands, commandSource: "inline" };
|
|
343
|
+
}
|
|
344
|
+
if (input.adapterCommands && input.adapterCommands.length > 0) {
|
|
345
|
+
return { commands: input.adapterCommands, commandSource: "adapter" };
|
|
346
|
+
}
|
|
347
|
+
return { commandSource: "inline" };
|
|
348
|
+
}
|
|
278
349
|
function resolveDagVerifyStrategy(taskConfig) {
|
|
279
350
|
return {
|
|
280
351
|
intermediateQuota: taskConfig.dagVerifyStrategy?.intermediateQuota ?? taskConfig.verifyQuota,
|
|
@@ -311,6 +382,17 @@ function deriveParallelScoutPaths(taskConfig) {
|
|
|
311
382
|
: allowed,
|
|
312
383
|
};
|
|
313
384
|
}
|
|
385
|
+
function deriveFrontendBehaviorPaths(taskConfig) {
|
|
386
|
+
if (taskConfig.allowedPaths.length === 0)
|
|
387
|
+
return ["**"];
|
|
388
|
+
const explicitTestPaths = taskConfig.allowedPaths.filter((entry) => entry.includes("/test/") ||
|
|
389
|
+
entry.includes("/tests/") ||
|
|
390
|
+
entry.includes("/e2e/") ||
|
|
391
|
+
entry.includes("/spec/"));
|
|
392
|
+
if (explicitTestPaths.length > 0)
|
|
393
|
+
return explicitTestPaths;
|
|
394
|
+
return taskConfig.allowedPaths;
|
|
395
|
+
}
|
|
314
396
|
function toTaskRelativeSourcePath(sources, absolutePath) {
|
|
315
397
|
return path
|
|
316
398
|
.relative(sources.taskDir, absolutePath)
|
|
@@ -454,6 +536,7 @@ export async function loadTaskHybridSources(repoRoot, taskId) {
|
|
|
454
536
|
enabledExecutors: resolveEnabledExecutors(manifest.executors),
|
|
455
537
|
executorModelMatrix: resolveExecutorModelMatrices(manifest),
|
|
456
538
|
verifyCommands,
|
|
539
|
+
sddEmbeddedSkills: await probeRepoLocalSddSkills(repoRoot),
|
|
457
540
|
};
|
|
458
541
|
}
|
|
459
542
|
function mergeFinalVerifyCommands(repoRoot, taskConfig, adapterCommands) {
|
|
@@ -479,8 +562,7 @@ export function buildStandardHybridDagFromTask(sources) {
|
|
|
479
562
|
const scoutPaths = deriveParallelScoutPaths(taskConfig);
|
|
480
563
|
const scoutComplexity = mapTaskComplexity(taskConfig.complexity);
|
|
481
564
|
const implementComplexity = resolveWriterComplexity(taskConfig);
|
|
482
|
-
const
|
|
483
|
-
const implementId = implementationNodeId(writerExecutor);
|
|
565
|
+
const implementId = implementationNodeId();
|
|
484
566
|
const finalVerifyCommands = sources.verifyCommands?.final ?? [];
|
|
485
567
|
const verifyShellCommands = buildVerifyShellCommands({
|
|
486
568
|
repoRoot: sources.repoRoot,
|
|
@@ -604,7 +686,7 @@ export function buildStandardHybridDagFromTask(sources) {
|
|
|
604
686
|
id: implementId,
|
|
605
687
|
depends_on: ["plan-pi"],
|
|
606
688
|
role: "implementer",
|
|
607
|
-
executor:
|
|
689
|
+
executor: "pi",
|
|
608
690
|
toolProfile: "write",
|
|
609
691
|
complexity: implementComplexity,
|
|
610
692
|
writePolicy: "exclusive",
|
|
@@ -656,11 +738,305 @@ export function buildStandardHybridDagFromTask(sources) {
|
|
|
656
738
|
},
|
|
657
739
|
],
|
|
658
740
|
};
|
|
741
|
+
applySddEmbeddedEnhancements(spec, sources.sddEmbeddedSkills ?? new Set());
|
|
742
|
+
parseDagSpec(spec);
|
|
743
|
+
assertValidDagSpec(spec);
|
|
744
|
+
return spec;
|
|
745
|
+
}
|
|
746
|
+
function buildFrontendHybridDagFromTask(sources) {
|
|
747
|
+
const { taskConfig } = sources;
|
|
748
|
+
const forbiddenPaths = mergeForbiddenPaths(taskConfig);
|
|
749
|
+
const implementPaths = resolveImplementPaths(taskConfig);
|
|
750
|
+
const implementId = frontendImplementationNodeId();
|
|
751
|
+
const sourceContext = buildSourceContextBlock(sources);
|
|
752
|
+
const strategy = resolveDagVerifyStrategy(taskConfig);
|
|
753
|
+
const readOnlyPaths = taskConfig.allowedPaths.length > 0 ? taskConfig.allowedPaths : ["**"];
|
|
754
|
+
const behaviorPaths = deriveFrontendBehaviorPaths(taskConfig);
|
|
755
|
+
const globalConstraints = [
|
|
756
|
+
...taskConfig.hardConstraints,
|
|
757
|
+
...(sources.constraintMarkdown
|
|
758
|
+
? [`See 执行约束.md in task source (${sources.taskId})`]
|
|
759
|
+
: []),
|
|
760
|
+
...STANDARD_GLOBAL_CONSTRAINTS,
|
|
761
|
+
"frontend-implementation DAGs must pass the design verdict gate before any write node executes.",
|
|
762
|
+
"frontend-implementation DAGs must complete deterministic static verification and behavior verification before final review.",
|
|
763
|
+
"frontend review must block closeout unless review verdict is exactly VERDICT: pass.",
|
|
764
|
+
];
|
|
765
|
+
const staticFallbackCommands = ["npm run typecheck", "npm run build"];
|
|
766
|
+
const behaviorFallbackCommands = ["npm test"];
|
|
767
|
+
const parsedFrontendVerifyCommands = extractFrontendVerifyCommandsFromMarkdown({
|
|
768
|
+
repoRoot: sources.repoRoot,
|
|
769
|
+
requirementMarkdown: sources.requirementMarkdown,
|
|
770
|
+
constraintMarkdown: sources.constraintMarkdown,
|
|
771
|
+
});
|
|
772
|
+
const staticVerifyCommands = chooseFrontendVerifyCommands({
|
|
773
|
+
parsedCommands: parsedFrontendVerifyCommands.staticCommands,
|
|
774
|
+
adapterCommands: sources.verifyCommands?.intermediate,
|
|
775
|
+
});
|
|
776
|
+
const behaviorVerifyCommands = chooseFrontendVerifyCommands({
|
|
777
|
+
parsedCommands: parsedFrontendVerifyCommands.behaviorCommands,
|
|
778
|
+
adapterCommands: sources.verifyCommands?.final,
|
|
779
|
+
});
|
|
780
|
+
const spec = {
|
|
781
|
+
version: 2,
|
|
782
|
+
title: `Frontend implementation DAG: ${taskConfig.title}`,
|
|
783
|
+
objective: extractObjective(sources.requirementMarkdown, taskConfig.title),
|
|
784
|
+
successCriteria: extractSuccessCriteria(sources.requirementMarkdown, sources.taskId),
|
|
785
|
+
globalConstraints,
|
|
786
|
+
defaults: {
|
|
787
|
+
...FRONTEND_DEFAULTS,
|
|
788
|
+
contextProfile: taskConfig.contextProfile,
|
|
789
|
+
},
|
|
790
|
+
skillsByRole: FRONTEND_SKILLS_BY_ROLE,
|
|
791
|
+
executorModels: sources.executorModelMatrix ?? DEFAULT_DAG_EXECUTOR_MODELS,
|
|
792
|
+
verifyStrategy: resolveDagVerifyStrategy(taskConfig),
|
|
793
|
+
tasks: [
|
|
794
|
+
{
|
|
795
|
+
id: "frontend-contract-pi",
|
|
796
|
+
depends_on: [],
|
|
797
|
+
role: "planner",
|
|
798
|
+
executor: "pi",
|
|
799
|
+
complexity: "MED",
|
|
800
|
+
writePolicy: "read-only",
|
|
801
|
+
allowedPaths: readOnlyPaths,
|
|
802
|
+
forbiddenPaths,
|
|
803
|
+
skills: FRONTEND_IMPLEMENTATION_SKILLS,
|
|
804
|
+
outputContract: "Markdown contract with Scope, Non-goals, Acceptance Criteria, UI States, Target Runtime Environment, Risks, and Verification Expectations. No file writes.",
|
|
805
|
+
subtask_prompt: [
|
|
806
|
+
"Read task source and produce a concise frontend implementation contract.",
|
|
807
|
+
"Cover scope, non-goals, acceptance criteria, UI states, target runtime environment, risks, and verification expectations.",
|
|
808
|
+
"Read-only: do not modify code, docs, artifacts, or repository files.",
|
|
809
|
+
sourceContext,
|
|
810
|
+
].join("\n\n"),
|
|
811
|
+
},
|
|
812
|
+
{
|
|
813
|
+
id: "frontend-scout-pi",
|
|
814
|
+
depends_on: ["frontend-contract-pi"],
|
|
815
|
+
role: "scout",
|
|
816
|
+
executor: "pi",
|
|
817
|
+
complexity: mapTaskComplexity(taskConfig.complexity),
|
|
818
|
+
writePolicy: "read-only",
|
|
819
|
+
allowedPaths: readOnlyPaths,
|
|
820
|
+
forbiddenPaths,
|
|
821
|
+
skills: FRONTEND_IMPLEMENTATION_SKILLS,
|
|
822
|
+
outputContract: "Markdown scout report covering frontend stack, routes, components, styling system, existing design conventions, state/data flow, test entry points, reuse opportunities, and risks. No file writes.",
|
|
823
|
+
subtask_prompt: [
|
|
824
|
+
"Inspect frontend code, routing, components, styles, package scripts, and tests.",
|
|
825
|
+
"Return code and design observations, existing reuse opportunities, and verification entry points.",
|
|
826
|
+
"Read-only: do not modify repository files.",
|
|
827
|
+
sourceContext,
|
|
828
|
+
].join("\n\n"),
|
|
829
|
+
},
|
|
830
|
+
{
|
|
831
|
+
id: "frontend-plan-pi",
|
|
832
|
+
depends_on: ["frontend-scout-pi"],
|
|
833
|
+
role: "planner",
|
|
834
|
+
executor: "pi",
|
|
835
|
+
complexity: "MED",
|
|
836
|
+
writePolicy: "read-only",
|
|
837
|
+
allowedPaths: readOnlyPaths,
|
|
838
|
+
forbiddenPaths,
|
|
839
|
+
skills: FRONTEND_IMPLEMENTATION_SKILLS,
|
|
840
|
+
outputContract: "Markdown implementation plan with Implementation Steps, Target Files, UI State Handling, Styling / Component Strategy, Interaction Notes, Dependency Policy, Verification Plan, and Residual Risks. No file writes.",
|
|
841
|
+
subtask_prompt: [
|
|
842
|
+
"Based on frontend-contract-pi and frontend-scout-pi, return a minimal frontend implementation plan.",
|
|
843
|
+
"Include ordered steps, target files, UI state handling, styling/component strategy, interaction notes, dependency policy, and deterministic verification commands.",
|
|
844
|
+
"Read-only: do not modify code, docs, artifacts, or repository files.",
|
|
845
|
+
sourceContext,
|
|
846
|
+
].join("\n\n"),
|
|
847
|
+
},
|
|
848
|
+
{
|
|
849
|
+
id: "frontend-design-gate-pi",
|
|
850
|
+
depends_on: ["frontend-plan-pi"],
|
|
851
|
+
role: "reviewer",
|
|
852
|
+
executor: "pi",
|
|
853
|
+
complexity: "MED",
|
|
854
|
+
writePolicy: "read-only",
|
|
855
|
+
allowedPaths: readOnlyPaths,
|
|
856
|
+
forbiddenPaths,
|
|
857
|
+
skills: FRONTEND_DESIGN_REVIEW_SKILLS,
|
|
858
|
+
outputContract: "Plain Markdown whose first non-empty line is VERDICT: pass or VERDICT: request-revision, followed by Findings, Required Plan Corrections, and Checked Items. No file writes.",
|
|
859
|
+
subtask_prompt: [
|
|
860
|
+
"Audit the frontend plan before implementation.",
|
|
861
|
+
"First non-empty line must be exactly VERDICT: pass or VERDICT: request-revision.",
|
|
862
|
+
"Request revision for missing applicable UI states, unsupported dependency additions, design-system drift without reason, weak interaction coverage, broad scope, or missing deterministic verification commands.",
|
|
863
|
+
"Read-only: do not modify repository files.",
|
|
864
|
+
sourceContext,
|
|
865
|
+
].join("\n\n"),
|
|
866
|
+
},
|
|
867
|
+
{
|
|
868
|
+
id: "frontend-design-gate-shell",
|
|
869
|
+
depends_on: ["frontend-design-gate-pi"],
|
|
870
|
+
role: "verifier",
|
|
871
|
+
executor: "shell",
|
|
872
|
+
complexity: "LOW",
|
|
873
|
+
writePolicy: "read-only",
|
|
874
|
+
allowedPaths: readOnlyPaths,
|
|
875
|
+
forbiddenPaths,
|
|
876
|
+
outputContract: "Deterministic frontend design verdict gate: exit 0 only when frontend-design-gate-pi emits VERDICT: pass.",
|
|
877
|
+
subtask_prompt: "Deterministic gate: block frontend implementation unless frontend-design-gate-pi emitted VERDICT: pass.",
|
|
878
|
+
shell: {
|
|
879
|
+
commands: [],
|
|
880
|
+
verdictGate: {
|
|
881
|
+
fromNodeId: "frontend-design-gate-pi",
|
|
882
|
+
accept: ["VERDICT: pass"],
|
|
883
|
+
label: "frontend design gate",
|
|
884
|
+
lineMode: "first-verdict-line",
|
|
885
|
+
},
|
|
886
|
+
cwd: ".",
|
|
887
|
+
timeoutMs: 60000,
|
|
888
|
+
},
|
|
889
|
+
},
|
|
890
|
+
{
|
|
891
|
+
id: implementId,
|
|
892
|
+
depends_on: ["frontend-design-gate-shell"],
|
|
893
|
+
role: "implementer",
|
|
894
|
+
executor: "pi",
|
|
895
|
+
toolProfile: "write",
|
|
896
|
+
complexity: taskConfig.complexity === "large"
|
|
897
|
+
? "HIGH"
|
|
898
|
+
: "MED",
|
|
899
|
+
writePolicy: "exclusive",
|
|
900
|
+
writeSet: implementPaths.writeSet,
|
|
901
|
+
allowedPaths: implementPaths.allowedPaths,
|
|
902
|
+
forbiddenPaths,
|
|
903
|
+
skills: FRONTEND_IMPLEMENTATION_SKILLS,
|
|
904
|
+
outputContract: "Markdown summary with Changed Files, Implemented Behavior, UI States Covered, Styling / Component Notes, Verification Attempted, and Residual Risks.",
|
|
905
|
+
subtask_prompt: [
|
|
906
|
+
"Implement the approved frontend plan with minimal focused changes.",
|
|
907
|
+
"Stay within writeSet and preserve unrelated files. Do not write root artifacts/** unless explicitly included in writeSet.",
|
|
908
|
+
sourceContext,
|
|
909
|
+
].join("\n\n"),
|
|
910
|
+
},
|
|
911
|
+
{
|
|
912
|
+
id: "frontend-static-verify-shell",
|
|
913
|
+
depends_on: [implementId],
|
|
914
|
+
role: "verifier",
|
|
915
|
+
executor: "shell",
|
|
916
|
+
complexity: "LOW",
|
|
917
|
+
writePolicy: "read-only",
|
|
918
|
+
allowedPaths: readOnlyPaths,
|
|
919
|
+
forbiddenPaths,
|
|
920
|
+
outputContract: "Archived shell stdout/stderr with exit codes for deterministic static verification; no worktree writes.",
|
|
921
|
+
subtask_prompt: "Run deterministic static verification for the frontend implementation.",
|
|
922
|
+
shell: {
|
|
923
|
+
commands: buildVerifyShellCommands({
|
|
924
|
+
repoRoot: sources.repoRoot,
|
|
925
|
+
commands: staticVerifyCommands.commands,
|
|
926
|
+
fallbackCommands: staticFallbackCommands,
|
|
927
|
+
}),
|
|
928
|
+
verifyEvidence: buildVerifyEvidence({
|
|
929
|
+
phase: "intermediate",
|
|
930
|
+
quota: strategy.intermediateQuota ?? "full",
|
|
931
|
+
commandSource: staticVerifyCommands.commandSource,
|
|
932
|
+
commands: staticVerifyCommands.commands,
|
|
933
|
+
fallbackCommands: staticFallbackCommands,
|
|
934
|
+
}),
|
|
935
|
+
cwd: ".",
|
|
936
|
+
timeoutMs: 300000,
|
|
937
|
+
},
|
|
938
|
+
},
|
|
939
|
+
{
|
|
940
|
+
id: "frontend-behavior-verify-shell",
|
|
941
|
+
depends_on: ["frontend-static-verify-shell"],
|
|
942
|
+
role: "verifier",
|
|
943
|
+
executor: "shell",
|
|
944
|
+
complexity: "LOW",
|
|
945
|
+
writePolicy: "read-only",
|
|
946
|
+
allowedPaths: behaviorPaths,
|
|
947
|
+
forbiddenPaths,
|
|
948
|
+
outputContract: "Archived shell stdout/stderr with exit codes for deterministic behavior verification; no worktree writes.",
|
|
949
|
+
subtask_prompt: "Run deterministic behavior verification for frontend flows, states, and integration points.",
|
|
950
|
+
shell: {
|
|
951
|
+
commands: buildVerifyShellCommands({
|
|
952
|
+
repoRoot: sources.repoRoot,
|
|
953
|
+
commands: behaviorVerifyCommands.commands,
|
|
954
|
+
fallbackCommands: behaviorFallbackCommands,
|
|
955
|
+
}),
|
|
956
|
+
verifyEvidence: buildVerifyEvidence({
|
|
957
|
+
phase: "final",
|
|
958
|
+
quota: "full",
|
|
959
|
+
commandSource: behaviorVerifyCommands.commandSource,
|
|
960
|
+
commands: behaviorVerifyCommands.commands,
|
|
961
|
+
fallbackCommands: behaviorFallbackCommands,
|
|
962
|
+
finalFullRequired: true,
|
|
963
|
+
}),
|
|
964
|
+
cwd: ".",
|
|
965
|
+
timeoutMs: 300000,
|
|
966
|
+
},
|
|
967
|
+
},
|
|
968
|
+
{
|
|
969
|
+
id: "frontend-review-pi",
|
|
970
|
+
depends_on: ["frontend-behavior-verify-shell"],
|
|
971
|
+
role: "reviewer",
|
|
972
|
+
executor: "pi",
|
|
973
|
+
complexity: "HIGH",
|
|
974
|
+
writePolicy: "read-only",
|
|
975
|
+
allowedPaths: readOnlyPaths,
|
|
976
|
+
forbiddenPaths,
|
|
977
|
+
skills: FRONTEND_REVIEW_SKILLS,
|
|
978
|
+
outputContract: "Plain Markdown whose first non-empty line is VERDICT: pass or VERDICT: request-revision, followed by Findings, Verification Assessment, UX Assessment, and Residual Risks. No file writes.",
|
|
979
|
+
subtask_prompt: [
|
|
980
|
+
"Review the frontend implementation and verification evidence.",
|
|
981
|
+
"First non-empty line must be exactly VERDICT: pass or VERDICT: request-revision.",
|
|
982
|
+
"Any Critical or Important finding must force VERDICT: request-revision.",
|
|
983
|
+
"Review implementation quality, behavior/state coverage, verification evidence, and maintainability. Read-only: do not modify files.",
|
|
984
|
+
sourceContext,
|
|
985
|
+
].join("\n\n"),
|
|
986
|
+
},
|
|
987
|
+
{
|
|
988
|
+
id: "frontend-review-gate-shell",
|
|
989
|
+
depends_on: ["frontend-review-pi"],
|
|
990
|
+
role: "verifier",
|
|
991
|
+
executor: "shell",
|
|
992
|
+
complexity: "LOW",
|
|
993
|
+
writePolicy: "read-only",
|
|
994
|
+
allowedPaths: readOnlyPaths,
|
|
995
|
+
forbiddenPaths,
|
|
996
|
+
outputContract: "Deterministic frontend review verdict gate: exit 0 only when frontend-review-pi emits VERDICT: pass.",
|
|
997
|
+
subtask_prompt: "Deterministic gate: block downstream closeout unless frontend-review-pi emitted VERDICT: pass.",
|
|
998
|
+
shell: {
|
|
999
|
+
commands: [],
|
|
1000
|
+
verdictGate: {
|
|
1001
|
+
fromNodeId: "frontend-review-pi",
|
|
1002
|
+
accept: ["VERDICT: pass"],
|
|
1003
|
+
label: "frontend review",
|
|
1004
|
+
lineMode: "first-verdict-line",
|
|
1005
|
+
},
|
|
1006
|
+
cwd: ".",
|
|
1007
|
+
timeoutMs: 60000,
|
|
1008
|
+
},
|
|
1009
|
+
},
|
|
1010
|
+
{
|
|
1011
|
+
id: "frontend-closeout-pi",
|
|
1012
|
+
depends_on: ["frontend-review-gate-shell"],
|
|
1013
|
+
role: "closeout",
|
|
1014
|
+
executor: "pi",
|
|
1015
|
+
complexity: "MED",
|
|
1016
|
+
writePolicy: "read-only",
|
|
1017
|
+
allowedPaths: taskConfig.allowedPaths.length > 0
|
|
1018
|
+
? [...taskConfig.allowedPaths, "docs/**"]
|
|
1019
|
+
: ["**", "docs/**"],
|
|
1020
|
+
forbiddenPaths,
|
|
1021
|
+
skills: FRONTEND_VERIFICATION_SKILLS,
|
|
1022
|
+
outputContract: "Markdown closeout summary with Changes, Verification Evidence, Review Result, Known Risks, and Follow-up. No file writes.",
|
|
1023
|
+
subtask_prompt: [
|
|
1024
|
+
"Return a frontend closeout summary covering changes, verification evidence, review result, known risks, and follow-up.",
|
|
1025
|
+
"Read-only: do not modify code, docs, artifacts, or .harness/dag-runs/.",
|
|
1026
|
+
sourceContext,
|
|
1027
|
+
].join("\n\n"),
|
|
1028
|
+
},
|
|
1029
|
+
],
|
|
1030
|
+
};
|
|
659
1031
|
parseDagSpec(spec);
|
|
660
1032
|
assertValidDagSpec(spec);
|
|
661
1033
|
return spec;
|
|
662
1034
|
}
|
|
663
1035
|
export function buildHybridDagFromTask(sources, options = {}) {
|
|
1036
|
+
if (sources.taskConfig.taskKind === "frontend-implementation" ||
|
|
1037
|
+
options.template === "frontend-implementation") {
|
|
1038
|
+
return buildFrontendHybridDagFromTask(sources);
|
|
1039
|
+
}
|
|
664
1040
|
const standard = buildStandardHybridDagFromTask(sources);
|
|
665
1041
|
const template = options.template ?? "standard-dag";
|
|
666
1042
|
if (template === "standard-dag")
|
|
@@ -758,6 +1134,7 @@ function buildReviewGatedHybridDag(standard, sources) {
|
|
|
758
1134
|
const closeout = getTaskOrThrow(spec, "closeout-pi");
|
|
759
1135
|
replaceTask(spec, cloneTask(closeout, { depends_on: ["review-gate-shell"] }));
|
|
760
1136
|
spec.tasks.splice(spec.tasks.length - 1, 0, buildReviewNode(sources), buildReviewGateNode(sources));
|
|
1137
|
+
applySddEmbeddedEnhancements(spec, sources.sddEmbeddedSkills ?? new Set());
|
|
761
1138
|
parseDagSpec(spec);
|
|
762
1139
|
assertValidDagSpec(spec);
|
|
763
1140
|
return spec;
|
|
@@ -807,7 +1184,7 @@ function buildWriteSetGateNode(sources) {
|
|
|
807
1184
|
};
|
|
808
1185
|
}
|
|
809
1186
|
function buildSoftVerifyNode(sources) {
|
|
810
|
-
const implementId = implementationNodeId(
|
|
1187
|
+
const implementId = implementationNodeId();
|
|
811
1188
|
const strategy = resolveDagVerifyStrategy(sources.taskConfig);
|
|
812
1189
|
const fallbackCommands = ["npm run typecheck"];
|
|
813
1190
|
const commands = buildVerifyShellCommands({
|
|
@@ -841,8 +1218,8 @@ function buildSoftVerifyNode(sources) {
|
|
|
841
1218
|
};
|
|
842
1219
|
}
|
|
843
1220
|
function buildProcessSupervisorNode(sources) {
|
|
844
|
-
const implementId = implementationNodeId(
|
|
845
|
-
const repairId = repairNodeId(
|
|
1221
|
+
const implementId = implementationNodeId();
|
|
1222
|
+
const repairId = repairNodeId();
|
|
846
1223
|
return {
|
|
847
1224
|
id: "process-supervisor-pi",
|
|
848
1225
|
depends_on: ["soft-verify-shell", implementId],
|
|
@@ -893,13 +1270,12 @@ function buildProcessGateNode(sources) {
|
|
|
893
1270
|
};
|
|
894
1271
|
}
|
|
895
1272
|
function buildRepairNode(sources) {
|
|
896
|
-
const writerExecutor = resolveImplementationExecutor(sources);
|
|
897
1273
|
const implement = resolveImplementPaths(sources.taskConfig);
|
|
898
1274
|
return {
|
|
899
|
-
id: repairNodeId(
|
|
1275
|
+
id: repairNodeId(),
|
|
900
1276
|
depends_on: ["process-gate-shell", "process-supervisor-pi"],
|
|
901
1277
|
role: "implementer",
|
|
902
|
-
executor:
|
|
1278
|
+
executor: "pi",
|
|
903
1279
|
toolProfile: "write",
|
|
904
1280
|
complexity: isInteractiveUiTask(sources.taskConfig)
|
|
905
1281
|
? "HIGH"
|
|
@@ -921,7 +1297,7 @@ function buildRepairNode(sources) {
|
|
|
921
1297
|
};
|
|
922
1298
|
}
|
|
923
1299
|
function buildHardVerifyNode(sources) {
|
|
924
|
-
const repairId = repairNodeId(
|
|
1300
|
+
const repairId = repairNodeId();
|
|
925
1301
|
const fallbackCommands = [
|
|
926
1302
|
"HARNESS_ALLOW_ACTIVE_DAG_RUNS=1 bash scripts/check-repo.sh",
|
|
927
1303
|
];
|
|
@@ -980,7 +1356,7 @@ function buildSupervisedHybridDag(standard, sources) {
|
|
|
980
1356
|
const scoutSrc = getTaskOrThrow(standard, "scout-src");
|
|
981
1357
|
const scoutTests = getTaskOrThrow(standard, "scout-tests");
|
|
982
1358
|
const plan = getTaskOrThrow(standard, "plan-pi");
|
|
983
|
-
const implement = getTaskOrThrow(standard, implementationNodeId(
|
|
1359
|
+
const implement = getTaskOrThrow(standard, implementationNodeId());
|
|
984
1360
|
const closeout = getTaskOrThrow(standard, "closeout-pi");
|
|
985
1361
|
const authorityDecision = resolveAuthoritySurfaceAudit(sources);
|
|
986
1362
|
const reviewDependsOn = authorityDecision.enabled
|
|
@@ -1028,6 +1404,7 @@ function buildSupervisedHybridDag(standard, sources) {
|
|
|
1028
1404
|
cloneTask(closeout, { depends_on: ["decision-pi"] }),
|
|
1029
1405
|
],
|
|
1030
1406
|
};
|
|
1407
|
+
applySddEmbeddedEnhancements(spec, sources.sddEmbeddedSkills ?? new Set());
|
|
1031
1408
|
parseDagSpec(spec);
|
|
1032
1409
|
assertValidDagSpec(spec);
|
|
1033
1410
|
return spec;
|
|
@@ -3,6 +3,7 @@ import { recordDecisionEnvelopeForNode, shouldPauseOnHumanEscalation, writeHuman
|
|
|
3
3
|
import { writeNodeRecord, writeNodeSkillArtifacts } from "./run-store.js";
|
|
4
4
|
import { buildDagNodePromptEnvelope } from "./prompt.js";
|
|
5
5
|
import { persistLongNodeOutputArtifacts } from "./upstream-artifacts.js";
|
|
6
|
+
import { assertSkillSnapshotCoversSpec, buildNodePromptFromSnapshot, isDagSkillSnapshotIntegrityError, readSkillSnapshot, } from "./skill-snapshot.js";
|
|
6
7
|
import { resolveDagSkillInstructions, skillInstructionMetadata, } from "./skill-instructions.js";
|
|
7
8
|
import { resolveDagNodeSkills } from "./skills.js";
|
|
8
9
|
import { parseRepairArtifactFromText, validateRepairArtifactScope, } from "./repair-artifact.js";
|
|
@@ -17,7 +18,7 @@ export function buildNodePrompt(spec, task, upstream) {
|
|
|
17
18
|
}
|
|
18
19
|
export async function buildNodePromptWithResolvedSkillInstructions(spec, task, upstream, cwd) {
|
|
19
20
|
const skillNames = resolveDagNodeSkills(spec, task);
|
|
20
|
-
const resolvedSkillInstructions = task.executor === "
|
|
21
|
+
const resolvedSkillInstructions = task.executor === "pi"
|
|
21
22
|
? await resolveDagSkillInstructions(skillNames, {
|
|
22
23
|
cwd,
|
|
23
24
|
includeLearnedPatterns: task.role === "implementer",
|
|
@@ -55,7 +56,7 @@ function assertRepairArtifactVerdictMatchesSupervisor(input) {
|
|
|
55
56
|
}
|
|
56
57
|
function findRepairTaskForGate(input) {
|
|
57
58
|
return Array.from(input.tasksById.values()).find((candidate) => candidate.depends_on.includes(input.gateTask.id) &&
|
|
58
|
-
candidate.id === "repair-
|
|
59
|
+
candidate.id === "repair-pi");
|
|
59
60
|
}
|
|
60
61
|
function parseSupervisorRepairArtifact(node) {
|
|
61
62
|
const text = `${node?.assistantText ?? ""}\n${node?.stdout ?? ""}`;
|
|
@@ -111,6 +112,45 @@ export async function executeDagNode(input) {
|
|
|
111
112
|
const { nodeId, tasksById, state, spec, cwd, runDir, executeNode } = input;
|
|
112
113
|
const task = tasksById.get(nodeId);
|
|
113
114
|
const node = state.nodes[nodeId];
|
|
115
|
+
const failSkillSnapshot = async (error) => {
|
|
116
|
+
const failedAt = new Date().toISOString();
|
|
117
|
+
node.startedAt ??= failedAt;
|
|
118
|
+
node.status = "ERROR";
|
|
119
|
+
node.stderr = error instanceof Error ? error.message : String(error);
|
|
120
|
+
node.failureCategory = "skill-snapshot-integrity";
|
|
121
|
+
node.finishedAt = failedAt;
|
|
122
|
+
node.lastActivityAt = node.finishedAt;
|
|
123
|
+
node.durationMs = Math.max(0, Date.now() - new Date(node.startedAt).getTime());
|
|
124
|
+
state.nodes[nodeId].nodeRecordPath = path.join(runDir, `${nodeId}.json`);
|
|
125
|
+
await writeNodeRecord(runDir, nodeId, state.nodes[nodeId]);
|
|
126
|
+
await input.persistState();
|
|
127
|
+
await notifyNodeObserver(input.observer, "onNodeFinish", nodeId, state);
|
|
128
|
+
};
|
|
129
|
+
const isDynamicTask = Boolean(task.dynamicExpansion
|
|
130
|
+
|| task.dynamicReduction
|
|
131
|
+
|| task.dynamicCondition
|
|
132
|
+
|| task.dynamicLoopUntil);
|
|
133
|
+
let snapshotPrompt;
|
|
134
|
+
if (state.skillSnapshotRef) {
|
|
135
|
+
try {
|
|
136
|
+
const skillSnapshot = await readSkillSnapshot(runDir, state.skillSnapshotRef, {
|
|
137
|
+
expectedRunId: state.runId,
|
|
138
|
+
});
|
|
139
|
+
assertSkillSnapshotCoversSpec(skillSnapshot, spec, state);
|
|
140
|
+
if (!isDynamicTask) {
|
|
141
|
+
snapshotPrompt = buildNodePromptFromSnapshot({
|
|
142
|
+
spec,
|
|
143
|
+
task,
|
|
144
|
+
upstream: state.nodes,
|
|
145
|
+
snapshot: skillSnapshot,
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
catch (error) {
|
|
150
|
+
await failSkillSnapshot(error);
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
114
154
|
node.status = "RUNNING";
|
|
115
155
|
node.startedAt = new Date().toISOString();
|
|
116
156
|
node.lastActivityAt = node.startedAt;
|
|
@@ -119,10 +159,7 @@ export async function executeDagNode(input) {
|
|
|
119
159
|
}
|
|
120
160
|
await input.persistState();
|
|
121
161
|
await notifyNodeObserver(input.observer, "onNodeStart", nodeId, state);
|
|
122
|
-
if (
|
|
123
|
-
task.dynamicReduction ||
|
|
124
|
-
task.dynamicCondition ||
|
|
125
|
-
task.dynamicLoopUntil) {
|
|
162
|
+
if (isDynamicTask) {
|
|
126
163
|
const started = Date.now();
|
|
127
164
|
try {
|
|
128
165
|
const result = await input.executeDynamicNode({
|
|
@@ -165,7 +202,24 @@ export async function executeDagNode(input) {
|
|
|
165
202
|
await notifyNodeObserver(input.observer, "onNodeFinish", nodeId, state);
|
|
166
203
|
return;
|
|
167
204
|
}
|
|
168
|
-
|
|
205
|
+
let prompt;
|
|
206
|
+
let resolvedSkills;
|
|
207
|
+
try {
|
|
208
|
+
if (snapshotPrompt) {
|
|
209
|
+
prompt = snapshotPrompt.prompt;
|
|
210
|
+
resolvedSkills = snapshotPrompt.resolvedSkills;
|
|
211
|
+
}
|
|
212
|
+
else {
|
|
213
|
+
({ prompt, resolvedSkills } =
|
|
214
|
+
await buildNodePromptWithResolvedSkillInstructions(spec, task, state.nodes, cwd));
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
catch (error) {
|
|
218
|
+
if (!isDagSkillSnapshotIntegrityError(error))
|
|
219
|
+
throw error;
|
|
220
|
+
await failSkillSnapshot(error);
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
169
223
|
node.resolvedSkills = resolvedSkills;
|
|
170
224
|
await writeNodeSkillArtifacts(runDir, nodeId, resolvedSkills);
|
|
171
225
|
const model = resolveModelForTask(task, spec.executorModels);
|