@tea-agent/loop-agent 0.41.0 → 0.42.0-next.1
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/CHANGELOG.md +102 -14
- package/dist/adapters/context-transfer/optional-pi-handoff.js +31 -0
- package/dist/adapters/context-transfer/pi-session.js +61 -0
- package/dist/build-stamp.json +3 -3
- package/dist/cli/command-definitions.js +12 -0
- package/dist/cli/program.js +23 -1
- package/dist/commands/dag-artifact.js +284 -0
- package/dist/commands/dag-context.js +184 -0
- package/dist/commands/dag-rerun.js +206 -1
- package/dist/executors/dag-pi-executor.js +39 -12
- package/dist/executors/pi-sdk-executor.js +3 -0
- package/dist/{worker → infrastructure}/console/app-data.js +4 -0
- package/dist/infrastructure/console/artifact-revision-store.js +430 -0
- package/dist/infrastructure/console/context-export-store.js +160 -0
- package/dist/infrastructure/console/dir-lock.js +132 -0
- package/dist/{worker → infrastructure}/console/operation-store.js +28 -0
- package/dist/infrastructure/harness/artifact-store.js +10 -1
- package/dist/infrastructure/harness/atomic-write.js +12 -2
- package/dist/shared/context-transfer/artifact-revision.js +172 -0
- package/dist/shared/context-transfer.js +418 -0
- package/dist/shared/operator/capabilities.js +14 -0
- package/dist/shared/operator/safe-run-summary.js +1 -0
- package/dist/shared/path-safety.js +93 -0
- package/dist/shared/preview.js +28 -4
- package/dist/task/config-types.js +2 -1
- package/dist/task/task-demand-routing.js +3 -0
- package/dist/worker/console/chat/chat-event-store.js +2 -2
- package/dist/worker/console/chat/resource-preferences-store.js +1 -1
- package/dist/worker/console/chat/routes.js +1 -1
- package/dist/worker/console/chat/session-store.js +1 -1
- package/dist/worker/console/chat/user-questions.js +1 -1
- package/dist/worker/console/console-update-runtime.js +1 -1
- package/dist/worker/console/context-transfer-diagnostics.js +198 -0
- package/dist/worker/console/dag-confirmation.js +1 -1
- package/dist/worker/console/dag-execution-receipt.js +1 -1
- package/dist/worker/console/doctor.js +1 -1
- package/dist/worker/console/draft-store.js +1 -1
- package/dist/worker/console/human-gate-token.js +1 -1
- package/dist/worker/console/index.js +3 -3
- package/dist/worker/console/interview/assessment.js +1 -1
- package/dist/worker/console/interview/session.js +1 -1
- package/dist/worker/console/operation-runner.js +45 -4
- package/dist/worker/console/operation-sse.js +1 -1
- package/dist/worker/console/operation-wait.js +1 -1
- package/dist/worker/console/operator-actions.js +104 -3
- package/dist/worker/console/operator-surface-health.js +1 -1
- package/dist/worker/console/operator-user-error.js +169 -0
- package/dist/worker/console/pi-plugins.js +57 -0
- package/dist/worker/console/routes.js +637 -8
- package/dist/worker/console/security.js +35 -0
- package/dist/worker/console/server.js +6 -3
- package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-DwVJxUxz.js → abnfDiagram-N423BO3Z-DZZ8m3PO.js} +1 -1
- package/dist/worker/console/static/assets/{arc-DqfCcXdu.js → arc-D6PvaVd-.js} +1 -1
- package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-qMVtggRv.js → architectureDiagram-T3A2C74G-B_OTOiI8.js} +1 -1
- package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-ECrV38dh.js → blockDiagram-VBNYF7ZC-Bv6rqHBg.js} +1 -1
- package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-DBaaLqQv.js → c4Diagram-5PPSVZJV-B8eHr0oz.js} +1 -1
- package/dist/worker/console/static/assets/channel-BU5gOilw.js +1 -0
- package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-BvfNHkAz.js → chunk-2GRJ4B5K-DYwR0im2.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-C5StiIbc.js → chunk-2Q5K7J3B-D2WPGqXt.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5RXB4S5H-HabGiJkS.js → chunk-5RXB4S5H-CQISJ_I7.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5VM5RSS4-BiTg127i.js → chunk-5VM5RSS4-C0o2Du1e.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-CZsXBPuR.js → chunk-6Q2QTUOP-4f8kr-U7.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-GF5L2VYU-D-r1MxLK.js → chunk-GF5L2VYU-D_OWvXzX.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-JWPE2WC7-DQxb9plr.js → chunk-JWPE2WC7-CasdPz5X.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-KBJHAD2P-DMndGvCT.js → chunk-KBJHAD2P-Cj8lRrla.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-RYQCIY6F-ByL4BgOJ.js → chunk-RYQCIY6F-CUvD1FSd.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-XXDRQBXY-BVe1b1gL.js → chunk-XXDRQBXY-CqLw_eqB.js} +1 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-DIzKJGHr.js +1 -0
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-DIzKJGHr.js +1 -0
- package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-BAyGzN8j.js → cose-bilkent-JH36ORCC-ku-WqJPx.js} +1 -1
- package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-C5yVYU7E.js → cynefin-VYW2F7L2-Bq_PYDhl.js} +1 -1
- package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-x-MwsEm9.js → cynefinDiagram-MW4NZA55-BiGZV641.js} +1 -1
- package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-CT5Tle5F.js → dagre-VZM6K2ZE-C4JRlglF.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-7IWD3JNH-BARERhlS.js → diagram-7IWD3JNH-B0dNeWYG.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-BwfUz0VM.js → diagram-B4RE2ZJO-Cj35YvYM.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-LBJQPF4R-Mk4r2WvJ.js → diagram-LBJQPF4R-uzQoJ2-8.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-Q27KOJAE-KUrxuBs0.js → diagram-Q27KOJAE-D1a-Buoz.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-UB23O5K3-CjZOceZn.js → diagram-UB23O5K3-XjRrLRSs.js} +1 -1
- package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-rnwUCiEI.js → ebnfDiagram-BXEA7PRR-Da_O24cW.js} +1 -1
- package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-DM4j23FM.js → erDiagram-JOGREHBK-BLJ8jrYU.js} +1 -1
- package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-DD5HdDEs.js → flowDiagram-UKHOOZJN-B9GrLjM0.js} +1 -1
- package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-CIAGSF47.js → ganttDiagram-PKOTCBZU-CeJ0TqiK.js} +1 -1
- package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-DbwFbVEL.js → gitGraphDiagram-DS77QQ5N-Bm2eNNFX.js} +1 -1
- package/dist/worker/console/static/assets/index-H9rFJiGL.css +1 -0
- package/dist/worker/console/static/assets/{index-Bih9w9h9.js → index-xwu9GxEc.js} +80 -80
- package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-B-Pb0T66.js → infoDiagram-6WML65LV-DB26i3d8.js} +1 -1
- package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-B9Unk-YX.js → ishikawaDiagram-WSZJBQD7-BOGRyeZT.js} +1 -1
- package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-C2F3JkPt.js → journeyDiagram-NVQOT4AX-CZRoBO_6.js} +1 -1
- package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-Bl8-Xcoz.js → kanban-definition-27J2QSJJ-qlWhJyoB.js} +1 -1
- package/dist/worker/console/static/assets/{linear-yK4uZmC5.js → linear-CfUiDDB3.js} +1 -1
- package/dist/worker/console/static/assets/{mermaid.core-Bpa6PmuE.js → mermaid.core-BQe6fpqj.js} +5 -5
- package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-BfGRoCSe.js → mindmap-definition-FAOFIHXS-zFzWHw64.js} +1 -1
- package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-Bz6_HtST.js → pegDiagram-VL7TDLO6-DFGUqjZO.js} +1 -1
- package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-i0Umbsby.js → pieDiagram-7S7Q4E2Y-BB5i0l8Z.js} +1 -1
- package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-DaVQBobe.js → quadrantDiagram-CIZ2JOQS-obbVZ_X5.js} +1 -1
- package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-DMTGnSq7.js → railroadDiagram-AXF67PYL-BwdaNzc1.js} +1 -1
- package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-2ao_DaSq.js → requirementDiagram-LRYGKXZP-CkTJaugh.js} +1 -1
- package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-2SG1lTB9.js → sankeyDiagram-W5VNT64P-BwJ-hIgq.js} +1 -1
- package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-BQYOLybR.js → sequenceDiagram-SI44F4Z6-BzSgmm7w.js} +1 -1
- package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-C8g3yjuI.js → sizeCapture-X5ZJPWSS-Bn3oj5Q1.js} +1 -1
- package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-q_F8nAQG.js → stateDiagram-OKZ733FA-dxhGtL8K.js} +1 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-Cld2qK9v.js +1 -0
- package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-8LrWpH2s.js → swimlanes-SLNWSIFB-BLpEInya.js} +2 -2
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-TIHpiT7w.js +8 -0
- package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-BQoY3qzl.js → timeline-definition-Z64GVDOM-ChJGYcXy.js} +1 -1
- package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-BDu1UjSp.js → vennDiagram-T6HMQDX7-DK-qjRer.js} +1 -1
- package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-C48M7Y0a.js → wardleyDiagram-T6FBY63Y-3qeaWAg-.js} +1 -1
- package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-YU6FTl_F.js → xychartDiagram-ELKLHX3M-CootlyP9.js} +1 -1
- package/dist/worker/console/static/index.html +7 -2
- package/dist/worker/console/static-src/app/useOperatorActions.js +3 -2
- package/dist/worker/console/static-src/shell/console-update-reload.js +25 -0
- package/dist/worker/console/workspace-context.js +28 -2
- package/dist/worker/console/workspace-registry.js +2 -2
- package/dist/worker/continuation/worker-continuation.js +278 -0
- package/dist/worker/observe/health.js +1 -1
- package/dist/worker/observe/node-transparency.js +572 -0
- package/dist/worker/observe/routes.js +51 -1
- package/dist/worker/observe/static/api.js +69 -5
- package/dist/worker/observe/static/dag-context-reason-labels.d.ts +9 -0
- package/dist/worker/observe/static/dag-context-reason-labels.js +120 -0
- package/dist/worker/observe/static/state.js +3 -1
- package/dist/worker/observe/static/styles.css +1037 -126
- package/dist/worker/observe/static/views/dag-inspector.js +1215 -58
- package/dist/worker/observe/static/views/session-timeline.js +75 -10
- package/dist/worker/outcomes/adapters.js +19 -0
- package/dist/worker/outcomes/types.js +1 -0
- package/dist/worker/pool/attempt-lease.js +97 -66
- package/dist/worker/pool/begin-attempt-with-lease.js +1 -0
- package/dist/worker/pool/reconcile.js +46 -1
- package/dist/worker/pool/run-store.js +2 -0
- package/dist/worker/pool/state-projection.js +2 -0
- package/dist/worker/task-spec/workflow-routing.js +8 -3
- package/dist/workflows/dag/artifact-bindings.js +149 -0
- package/dist/workflows/dag/artifact-revision-schema-registry.js +31 -0
- package/dist/workflows/dag/budget-enforcement.js +10 -0
- package/dist/workflows/dag/context-receipt.js +305 -0
- package/dist/workflows/dag/context-transfer/context-bundle.js +423 -0
- package/dist/workflows/dag/context-transfer/operator-actions.js +61 -0
- package/dist/workflows/dag/context-transfer/renderers.js +93 -0
- package/dist/workflows/dag/frontend-writer-rollback.js +32 -0
- package/dist/workflows/dag/init-hybrid.js +146 -0
- package/dist/workflows/dag/node-execution.js +112 -27
- package/dist/workflows/dag/path-safety.js +1 -0
- package/dist/workflows/dag/prompt.js +29 -4
- package/dist/workflows/dag/rerun-plan.js +555 -1
- package/dist/workflows/dag/rerun-run.js +601 -15
- package/dist/workflows/dag/runner.js +20 -1
- package/dist/workflows/dag/skill-snapshot.js +17 -0
- package/dist/workflows/dag/types.js +183 -7
- package/docs/architecture/runtime-boundaries.md +6 -6
- package/docs/architecture/worker-and-feature.md +1 -1
- package/docs/templates/agent-dag.schema.json +32 -0
- package/docs/templates/product-line/task.yaml +1 -1
- package/package.json +3 -1
- package/skills/loop-agent/references/command-reference.md +6 -0
- package/dist/worker/console/static/assets/channel-CL1NRAmS.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-PBGzzjZA.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-PBGzzjZA.js +0 -1
- package/dist/worker/console/static/assets/index-BfleX-qj.css +0 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-DrxGBDJu.js +0 -1
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-BhpDxzAf.js +0 -8
- /package/dist/{worker → infrastructure}/console/repo-fingerprint.js +0 -0
|
@@ -45,6 +45,60 @@ const CONSTRAINT_FILE = "执行约束.md";
|
|
|
45
45
|
const REFERENCE_DIRECTORY = "references";
|
|
46
46
|
const MAX_SOURCE_EXCERPT_CHARS = 2000;
|
|
47
47
|
const MAX_INLINE_SOURCE_REFERENCE_DOCUMENTS = 8;
|
|
48
|
+
/**
|
|
49
|
+
* Generation-time compiler for machine-declared structured artifacts. The
|
|
50
|
+
* frozen spec remains the sole runtime authority: this compiler never reads
|
|
51
|
+
* prompt/outputContract prose or workspace filenames, and historical specs
|
|
52
|
+
* loaded outside init-hybrid are left untouched.
|
|
53
|
+
*/
|
|
54
|
+
function stampGeneratedArtifactBindings(spec) {
|
|
55
|
+
const producedByNode = new Map();
|
|
56
|
+
for (const task of spec.tasks) {
|
|
57
|
+
if ((task.producesArtifacts?.length ?? 0) > 0) {
|
|
58
|
+
for (const artifact of task.producesArtifacts ?? []) {
|
|
59
|
+
producedByNode.set(task.id, artifact.artifactId);
|
|
60
|
+
}
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
const gate = task.shell?.jsonArtifactGate;
|
|
64
|
+
const structured = task.structuredContractOutput;
|
|
65
|
+
if (!gate && !structured)
|
|
66
|
+
continue;
|
|
67
|
+
const artifactId = `${task.id}.structured-output`;
|
|
68
|
+
const path = gate
|
|
69
|
+
? `${gate.outputDir}/${gate.artifactName}`
|
|
70
|
+
: `contracts/candidates/${task.id}/canonical-contract.json`;
|
|
71
|
+
task.producesArtifacts = [
|
|
72
|
+
{
|
|
73
|
+
artifactId,
|
|
74
|
+
kind: "structured",
|
|
75
|
+
path,
|
|
76
|
+
mediaType: "application/json",
|
|
77
|
+
schemaId: gate?.schemaId ?? FRONTEND_IMPLEMENTATION_CONTRACT_SCHEMA_ID,
|
|
78
|
+
revisionPolicy: "replace-file",
|
|
79
|
+
},
|
|
80
|
+
];
|
|
81
|
+
producedByNode.set(task.id, artifactId);
|
|
82
|
+
}
|
|
83
|
+
for (const task of spec.tasks) {
|
|
84
|
+
const existing = new Set((task.consumesArtifacts ?? []).map((binding) => binding.artifactId));
|
|
85
|
+
const generated = task.depends_on.flatMap((parentNodeId) => {
|
|
86
|
+
const artifactId = producedByNode.get(parentNodeId);
|
|
87
|
+
if (!artifactId || existing.has(artifactId))
|
|
88
|
+
return [];
|
|
89
|
+
existing.add(artifactId);
|
|
90
|
+
return [
|
|
91
|
+
{
|
|
92
|
+
artifactId,
|
|
93
|
+
required: task.dependsPolicy !== "all-or-condition-skip",
|
|
94
|
+
},
|
|
95
|
+
];
|
|
96
|
+
});
|
|
97
|
+
if (generated.length > 0) {
|
|
98
|
+
task.consumesArtifacts = [...(task.consumesArtifacts ?? []), ...generated];
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
48
102
|
const INTERACTIVE_UI_DELIVERY_CONTRACT = [
|
|
49
103
|
"[INTERACTIVE_UI_DELIVERY_CONTRACT]",
|
|
50
104
|
"This is an interactive UI delivery task.",
|
|
@@ -2074,6 +2128,7 @@ export function buildStandardHybridDagFromTask(sources) {
|
|
|
2074
2128
|
applySddEmbeddedEnhancements(spec, sources.sddEmbeddedSkills ?? new Set());
|
|
2075
2129
|
stampTargetTemplateTransientRetryProfile(spec);
|
|
2076
2130
|
applyDefaultReadOnlyRetryPolicy(spec);
|
|
2131
|
+
stampGeneratedArtifactBindings(spec);
|
|
2077
2132
|
parseDagSpec(spec);
|
|
2078
2133
|
assertValidDagSpec(spec);
|
|
2079
2134
|
return spec;
|
|
@@ -2279,6 +2334,7 @@ function buildBlockedFrontendMockDag(sources, readOnlyPaths, forbiddenPaths, glo
|
|
|
2279
2334
|
],
|
|
2280
2335
|
};
|
|
2281
2336
|
applyDefaultReadOnlyRetryPolicy(spec);
|
|
2337
|
+
stampGeneratedArtifactBindings(spec);
|
|
2282
2338
|
parseDagSpec(spec);
|
|
2283
2339
|
assertValidDagSpec(spec);
|
|
2284
2340
|
return spec;
|
|
@@ -3353,6 +3409,7 @@ async function buildFrontendHybridDagFromTask(sources) {
|
|
|
3353
3409
|
};
|
|
3354
3410
|
spec.tasks = pruneFrontendTasksForRisk(spec.tasks, frontendRisk);
|
|
3355
3411
|
applyDefaultReadOnlyRetryPolicy(spec);
|
|
3412
|
+
stampGeneratedArtifactBindings(spec);
|
|
3356
3413
|
parseDagSpec(spec);
|
|
3357
3414
|
assertValidDagSpec(spec);
|
|
3358
3415
|
return spec;
|
|
@@ -4020,6 +4077,84 @@ const BACKEND_TEST_SKILLS_BY_ROLE = {
|
|
|
4020
4077
|
verifier: ["verification-before-completion", "systematic-debugging"],
|
|
4021
4078
|
closeout: ["loop-agent", "verification-before-completion"],
|
|
4022
4079
|
};
|
|
4080
|
+
/**
|
|
4081
|
+
* Read-only Worker workflow for producing a revision-eligible Backend Test
|
|
4082
|
+
* Analysis v2 contract without crossing into test generation or execution.
|
|
4083
|
+
* The explicit consumer keeps artifact lineage meaningful for safe
|
|
4084
|
+
* continuation: replacing the contract reruns review + deterministic closeout
|
|
4085
|
+
* only.
|
|
4086
|
+
*/
|
|
4087
|
+
function buildBackendTestAnalysisHybridDag(sources) {
|
|
4088
|
+
const analysis = buildAnalyzeInputsNode(sources);
|
|
4089
|
+
const contract = buildBackendTestAnalysisContractGateNode(sources);
|
|
4090
|
+
const review = {
|
|
4091
|
+
id: "review-backend-test-analysis-pi",
|
|
4092
|
+
depends_on: [contract.id],
|
|
4093
|
+
role: "reviewer",
|
|
4094
|
+
executor: "pi",
|
|
4095
|
+
complexity: "LOW",
|
|
4096
|
+
writePolicy: "read-only",
|
|
4097
|
+
allowedPaths: commonReadOnlyPaths(sources),
|
|
4098
|
+
forbiddenPaths: commonForbiddenPaths(sources),
|
|
4099
|
+
outputContract: "Concise Markdown review of the validated Backend Test Analysis v2 contract, covering source binding, explicit requirement coverage, evidence gaps, risks, and downstream readiness. No file writes.",
|
|
4100
|
+
subtask_prompt: [
|
|
4101
|
+
"Review the validated run-owned `contracts/backend-test-analysis.json` artifact.",
|
|
4102
|
+
"Treat that structured artifact as the authoritative analysis contract; do not substitute the producer's assistant prose.",
|
|
4103
|
+
"Check that every explicit AC/REQ/BR is represented by acceptanceCriteria or an evidenceGap, source references are precise, and unknown behavior remains an evidence gap rather than an invented requirement.",
|
|
4104
|
+
"Summarize source-binding integrity, coverage, risks, evidence gaps, and whether the contract is ready for a later backend-test generation workflow.",
|
|
4105
|
+
"Read-only: do not modify code, docs, artifacts, or repository files.",
|
|
4106
|
+
].join("\n\n"),
|
|
4107
|
+
};
|
|
4108
|
+
const closeout = {
|
|
4109
|
+
id: "backend-test-analysis-closeout-static",
|
|
4110
|
+
depends_on: [review.id],
|
|
4111
|
+
role: "closeout",
|
|
4112
|
+
executor: "static",
|
|
4113
|
+
complexity: "LOW",
|
|
4114
|
+
writePolicy: "none",
|
|
4115
|
+
allowedPaths: [],
|
|
4116
|
+
forbiddenPaths: commonForbiddenPaths(sources),
|
|
4117
|
+
outputContract: "Deterministic handoff pointing to the validated analysis contract and its independent read-only review.",
|
|
4118
|
+
subtask_prompt: "Close the read-only analysis workflow after the structured contract and review both succeed.",
|
|
4119
|
+
static: {
|
|
4120
|
+
resultMarkdown: [
|
|
4121
|
+
"# Backend-test analysis closeout",
|
|
4122
|
+
"",
|
|
4123
|
+
"Validated contract: `contracts/backend-test-analysis.json`.",
|
|
4124
|
+
"Independent review: `review-backend-test-analysis-pi`.",
|
|
4125
|
+
].join("\n"),
|
|
4126
|
+
},
|
|
4127
|
+
};
|
|
4128
|
+
const spec = {
|
|
4129
|
+
version: 3,
|
|
4130
|
+
title: `Backend test analysis DAG: ${sources.taskConfig.title}`,
|
|
4131
|
+
runtimeContract: GENERATED_DAG_RUNTIME_CONTRACT,
|
|
4132
|
+
outputLanguage: sources.outputLanguage ?? DEFAULT_DAG_OUTPUT_LANGUAGE,
|
|
4133
|
+
objective: extractObjective(sources.requirementMarkdown, sources.taskConfig.title),
|
|
4134
|
+
successCriteria: [
|
|
4135
|
+
"A strict Backend Test Analysis v2 contract is materialized with immutable source binding",
|
|
4136
|
+
"Every explicit requirement is covered or preserved as an evidence gap",
|
|
4137
|
+
"An independent read-only review consumes the validated structured artifact",
|
|
4138
|
+
],
|
|
4139
|
+
globalConstraints: [
|
|
4140
|
+
...sources.taskConfig.hardConstraints,
|
|
4141
|
+
"This workflow is analysis-only: no repository writer, dynamic expansion, test generation, or test execution is allowed.",
|
|
4142
|
+
],
|
|
4143
|
+
defaults: {
|
|
4144
|
+
...BACKEND_TEST_DEFAULTS,
|
|
4145
|
+
contextProfile: sources.taskConfig.contextProfile,
|
|
4146
|
+
},
|
|
4147
|
+
skillsByRole: BACKEND_TEST_SKILLS_BY_ROLE,
|
|
4148
|
+
executorModels: sources.executorModelMatrix ?? DEFAULT_DAG_EXECUTOR_MODELS,
|
|
4149
|
+
verifyStrategy: resolveDagVerifyStrategy(sources.taskConfig),
|
|
4150
|
+
tasks: [analysis, contract, review, closeout],
|
|
4151
|
+
};
|
|
4152
|
+
applyDefaultReadOnlyRetryPolicy(spec);
|
|
4153
|
+
stampGeneratedArtifactBindings(spec);
|
|
4154
|
+
parseDagSpec(spec);
|
|
4155
|
+
assertValidDagSpec(spec);
|
|
4156
|
+
return spec;
|
|
4157
|
+
}
|
|
4023
4158
|
/**
|
|
4024
4159
|
* Plan D: gap-fill incremental DAG (mode=gap-fill).
|
|
4025
4160
|
*
|
|
@@ -4421,6 +4556,7 @@ async function buildBackendTestGapFillDag(sources) {
|
|
|
4421
4556
|
spec.backendTestSharedSetup = { ...sharedSetup };
|
|
4422
4557
|
}
|
|
4423
4558
|
applyDefaultReadOnlyRetryPolicy(spec);
|
|
4559
|
+
stampGeneratedArtifactBindings(spec);
|
|
4424
4560
|
parseDagSpec(spec);
|
|
4425
4561
|
assertValidDagSpec(spec);
|
|
4426
4562
|
return spec;
|
|
@@ -4923,6 +5059,7 @@ async function buildBackendTestHybridDag(sources) {
|
|
|
4923
5059
|
spec.backendTestSharedSetup = { ...intake.sharedSetup };
|
|
4924
5060
|
}
|
|
4925
5061
|
applyDefaultReadOnlyRetryPolicy(spec);
|
|
5062
|
+
stampGeneratedArtifactBindings(spec);
|
|
4926
5063
|
parseDagSpec(spec);
|
|
4927
5064
|
assertValidDagSpec(spec);
|
|
4928
5065
|
return spec;
|
|
@@ -5677,6 +5814,7 @@ function buildFrontendTestHybridDag(sources) {
|
|
|
5677
5814
|
};
|
|
5678
5815
|
applyFrontendTestLayoutToDagSpec(spec, layout);
|
|
5679
5816
|
applyDefaultReadOnlyRetryPolicy(spec);
|
|
5817
|
+
stampGeneratedArtifactBindings(spec);
|
|
5680
5818
|
parseDagSpec(spec);
|
|
5681
5819
|
assertValidDagSpec(spec);
|
|
5682
5820
|
return spec;
|
|
@@ -6179,6 +6317,7 @@ function buildKnowledgeSyncHybridDag(sources) {
|
|
|
6179
6317
|
buildKnowledgeSyncPointerNode(sources, featureId),
|
|
6180
6318
|
],
|
|
6181
6319
|
};
|
|
6320
|
+
stampGeneratedArtifactBindings(spec);
|
|
6182
6321
|
parseDagSpec(spec);
|
|
6183
6322
|
assertValidDagSpec(spec);
|
|
6184
6323
|
return spec;
|
|
@@ -6625,6 +6764,7 @@ function buildKnowledgeGraphBootstrapHybridDag(sources) {
|
|
|
6625
6764
|
buildKgBootstrapMaterializeNode(sources),
|
|
6626
6765
|
],
|
|
6627
6766
|
};
|
|
6767
|
+
stampGeneratedArtifactBindings(spec);
|
|
6628
6768
|
parseDagSpec(spec);
|
|
6629
6769
|
assertValidDagSpec(spec);
|
|
6630
6770
|
return spec;
|
|
@@ -6722,6 +6862,8 @@ async function buildHybridDagForTemplate(sources, template, options = {}) {
|
|
|
6722
6862
|
}
|
|
6723
6863
|
else if (template === "frontend-test-dag")
|
|
6724
6864
|
spec = buildFrontendTestHybridDag(sources);
|
|
6865
|
+
else if (template === "backend-test-analysis-dag")
|
|
6866
|
+
spec = buildBackendTestAnalysisHybridDag(sources);
|
|
6725
6867
|
else if (template === "backend-test-dag")
|
|
6726
6868
|
spec = await buildBackendTestHybridDag(sources);
|
|
6727
6869
|
else if (template === "knowledge-sync-dag")
|
|
@@ -6758,6 +6900,7 @@ async function buildHybridDagForTemplate(sources, template, options = {}) {
|
|
|
6758
6900
|
template === "supervised-implementation") {
|
|
6759
6901
|
stampTargetTemplateTransientRetryProfile(spec);
|
|
6760
6902
|
}
|
|
6903
|
+
stampGeneratedArtifactBindings(spec);
|
|
6761
6904
|
spec = parseDagSpec(spec);
|
|
6762
6905
|
assertValidDagSpec(spec);
|
|
6763
6906
|
return spec;
|
|
@@ -6765,6 +6908,7 @@ async function buildHybridDagForTemplate(sources, template, options = {}) {
|
|
|
6765
6908
|
const GOVERNANCE_DISALLOWED_TEMPLATES = new Set([
|
|
6766
6909
|
"frontend-implementation",
|
|
6767
6910
|
"frontend-test-dag",
|
|
6911
|
+
"backend-test-analysis-dag",
|
|
6768
6912
|
"backend-test-dag",
|
|
6769
6913
|
"knowledge-sync-dag",
|
|
6770
6914
|
"knowledge-graph-bootstrap-dag",
|
|
@@ -7125,6 +7269,7 @@ function buildReviewGatedHybridDag(standard, sources) {
|
|
|
7125
7269
|
applySddEmbeddedEnhancements(spec, sources.sddEmbeddedSkills ?? new Set());
|
|
7126
7270
|
stampTargetTemplateTransientRetryProfile(spec);
|
|
7127
7271
|
applyDefaultReadOnlyRetryPolicy(spec);
|
|
7272
|
+
stampGeneratedArtifactBindings(spec);
|
|
7128
7273
|
parseDagSpec(spec);
|
|
7129
7274
|
assertValidDagSpec(spec);
|
|
7130
7275
|
return spec;
|
|
@@ -7630,6 +7775,7 @@ async function buildSupervisedHybridDag(standard, sources) {
|
|
|
7630
7775
|
applySddEmbeddedEnhancements(spec, sources.sddEmbeddedSkills ?? new Set());
|
|
7631
7776
|
stampTargetTemplateTransientRetryProfile(spec);
|
|
7632
7777
|
applyDefaultReadOnlyRetryPolicy(spec);
|
|
7778
|
+
stampGeneratedArtifactBindings(spec);
|
|
7633
7779
|
parseDagSpec(spec);
|
|
7634
7780
|
assertValidDagSpec(spec);
|
|
7635
7781
|
return spec;
|
|
@@ -10,6 +10,7 @@ import { writeNodeRecord, writeNodeSkillArtifacts } from "./run-store.js";
|
|
|
10
10
|
import { resolveContextPolicy } from "./context-policy.js";
|
|
11
11
|
import { buildDagNodePromptEnvelope, formatConvergenceFeedbackBlock, } from "./prompt.js";
|
|
12
12
|
import { persistLongNodeOutputArtifacts } from "./upstream-artifacts.js";
|
|
13
|
+
import { materializeDeclaredArtifactFacts } from "./artifact-bindings.js";
|
|
13
14
|
import { buildOutputLimitRecoverySection, loadBackendTestWriterProgressForRetry, } from "./backend-test-writer-completeness.js";
|
|
14
15
|
import { computeBackoffDelayMs, isCanonicalFinalVerifyShellRetryCandidate, isRetryablePiFailureCategory, isSafeReadOnlyPiRetryCandidate, isTargetTemplateTransientRetryNode, isWriterEmptyDiffRetryCandidate, isWriterTransportRetryCandidate, VERIFY_SHELL_RETRY_POLICY, } from "./retry-policy.js";
|
|
15
16
|
import { applyNodeActivity, evaluateNodeLiveness, resolveLivenessPolicy, } from "./liveness-policy.js";
|
|
@@ -42,7 +43,11 @@ function canonicalApprovalJson(value) {
|
|
|
42
43
|
export function parseAndValidateFinalWriteSetApproval(input) {
|
|
43
44
|
const binding = input.task.finalWriteSetApproval;
|
|
44
45
|
if (!binding) {
|
|
45
|
-
return {
|
|
46
|
+
return {
|
|
47
|
+
ok: false,
|
|
48
|
+
reason: "missing final write-set approval binding",
|
|
49
|
+
approvalSourceNodeId: "(missing)",
|
|
50
|
+
};
|
|
46
51
|
}
|
|
47
52
|
const source = input.state.nodes[binding.approvalSourceNodeId];
|
|
48
53
|
if (source?.status !== "FINISHED" || parseProcessVerdict(source) !== "pass") {
|
|
@@ -53,20 +58,44 @@ export function parseAndValidateFinalWriteSetApproval(input) {
|
|
|
53
58
|
};
|
|
54
59
|
}
|
|
55
60
|
const raw = canonicalNodeOutput(source);
|
|
56
|
-
const blocks = [
|
|
61
|
+
const blocks = [
|
|
62
|
+
...raw.matchAll(/```FINAL_WRITE_SET_APPROVAL_JSON\s*\r?\n([\s\S]*?)\r?\n```/g),
|
|
63
|
+
];
|
|
57
64
|
if (blocks.length !== 1) {
|
|
58
|
-
return {
|
|
65
|
+
return {
|
|
66
|
+
ok: false,
|
|
67
|
+
reason: `expected exactly one FINAL_WRITE_SET_APPROVAL_JSON block, found ${blocks.length}`,
|
|
68
|
+
approvalSourceNodeId: binding.approvalSourceNodeId,
|
|
69
|
+
};
|
|
59
70
|
}
|
|
60
71
|
let approval;
|
|
61
72
|
try {
|
|
62
73
|
approval = JSON.parse(blocks[0][1]);
|
|
63
74
|
}
|
|
64
75
|
catch {
|
|
65
|
-
return {
|
|
76
|
+
return {
|
|
77
|
+
ok: false,
|
|
78
|
+
reason: "final write-set approval is not valid JSON",
|
|
79
|
+
approvalSourceNodeId: binding.approvalSourceNodeId,
|
|
80
|
+
};
|
|
66
81
|
}
|
|
67
|
-
const required = [
|
|
68
|
-
|
|
69
|
-
|
|
82
|
+
const required = [
|
|
83
|
+
"schemaVersion",
|
|
84
|
+
"writerNodeId",
|
|
85
|
+
"approvalSourceNodeId",
|
|
86
|
+
"auditedPlanNodeId",
|
|
87
|
+
"approvedWriteSet",
|
|
88
|
+
"taskContractSha256",
|
|
89
|
+
"auditedPlanSha256",
|
|
90
|
+
"approvalDigest",
|
|
91
|
+
];
|
|
92
|
+
if (Object.keys(approval).length !== required.length ||
|
|
93
|
+
required.some((key) => !(key in approval))) {
|
|
94
|
+
return {
|
|
95
|
+
ok: false,
|
|
96
|
+
reason: "final write-set approval has an invalid schema",
|
|
97
|
+
approvalSourceNodeId: binding.approvalSourceNodeId,
|
|
98
|
+
};
|
|
70
99
|
}
|
|
71
100
|
const approved = approval.approvedWriteSet;
|
|
72
101
|
if (approval.schemaVersion !== 1 ||
|
|
@@ -79,12 +108,20 @@ export function parseAndValidateFinalWriteSetApproval(input) {
|
|
|
79
108
|
typeof approval.taskContractSha256 !== "string" ||
|
|
80
109
|
typeof approval.auditedPlanSha256 !== "string" ||
|
|
81
110
|
typeof approval.approvalDigest !== "string") {
|
|
82
|
-
return {
|
|
111
|
+
return {
|
|
112
|
+
ok: false,
|
|
113
|
+
reason: "final write-set approval binding or field types are invalid",
|
|
114
|
+
approvalSourceNodeId: binding.approvalSourceNodeId,
|
|
115
|
+
};
|
|
83
116
|
}
|
|
84
117
|
if (!/^[a-f0-9]{64}$/.test(approval.taskContractSha256) ||
|
|
85
118
|
!/^[a-f0-9]{64}$/.test(approval.auditedPlanSha256) ||
|
|
86
119
|
!/^[a-f0-9]{64}$/.test(approval.approvalDigest)) {
|
|
87
|
-
return {
|
|
120
|
+
return {
|
|
121
|
+
ok: false,
|
|
122
|
+
reason: "final write-set approval digest fields are invalid",
|
|
123
|
+
approvalSourceNodeId: binding.approvalSourceNodeId,
|
|
124
|
+
};
|
|
88
125
|
}
|
|
89
126
|
const canonicalPayload = { ...approval };
|
|
90
127
|
delete canonicalPayload.approvalDigest;
|
|
@@ -92,33 +129,70 @@ export function parseAndValidateFinalWriteSetApproval(input) {
|
|
|
92
129
|
.update(canonicalApprovalJson(canonicalPayload))
|
|
93
130
|
.digest("hex");
|
|
94
131
|
if (approval.approvalDigest !== expectedDigest) {
|
|
95
|
-
return {
|
|
132
|
+
return {
|
|
133
|
+
ok: false,
|
|
134
|
+
reason: "final write-set approval digest mismatch",
|
|
135
|
+
approvalSourceNodeId: binding.approvalSourceNodeId,
|
|
136
|
+
};
|
|
96
137
|
}
|
|
97
138
|
const expectedTaskDigest = input.spec.taskContractBinding?.canonicalHash;
|
|
98
139
|
const plan = input.state.nodes[binding.auditedPlanNodeId];
|
|
99
140
|
const expectedPlanDigest = createHash("sha256")
|
|
100
141
|
.update(canonicalNodeOutput(plan))
|
|
101
142
|
.digest("hex");
|
|
102
|
-
if (!expectedTaskDigest ||
|
|
103
|
-
|
|
143
|
+
if (!expectedTaskDigest ||
|
|
144
|
+
approval.taskContractSha256 !== expectedTaskDigest ||
|
|
145
|
+
approval.auditedPlanSha256 !== expectedPlanDigest) {
|
|
146
|
+
return {
|
|
147
|
+
ok: false,
|
|
148
|
+
reason: "final write-set approval is stale for the task contract or audited plan",
|
|
149
|
+
approvalSourceNodeId: binding.approvalSourceNodeId,
|
|
150
|
+
};
|
|
104
151
|
}
|
|
105
152
|
const effectiveWriteSet = approved;
|
|
106
|
-
if (effectiveWriteSet.length === 0 ||
|
|
107
|
-
|
|
153
|
+
if (effectiveWriteSet.length === 0 ||
|
|
154
|
+
new Set(effectiveWriteSet).size !== effectiveWriteSet.length) {
|
|
155
|
+
return {
|
|
156
|
+
ok: false,
|
|
157
|
+
reason: "final write-set approval must contain a non-empty ordered unique path set",
|
|
158
|
+
approvalSourceNodeId: binding.approvalSourceNodeId,
|
|
159
|
+
};
|
|
108
160
|
}
|
|
109
161
|
for (const entry of effectiveWriteSet) {
|
|
110
162
|
const normalized = entry.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
111
|
-
if (!normalized ||
|
|
112
|
-
|
|
163
|
+
if (!normalized ||
|
|
164
|
+
normalized === "." ||
|
|
165
|
+
normalized === ".." ||
|
|
166
|
+
normalized.includes("*") ||
|
|
167
|
+
normalized.includes("?") ||
|
|
168
|
+
normalized.includes("REPLACE/") ||
|
|
169
|
+
normalized.includes("PLACEHOLDER")) {
|
|
170
|
+
return {
|
|
171
|
+
ok: false,
|
|
172
|
+
reason: `final write-set approval contains a broad or placeholder path: ${entry}`,
|
|
173
|
+
approvalSourceNodeId: binding.approvalSourceNodeId,
|
|
174
|
+
};
|
|
113
175
|
}
|
|
114
176
|
if (!input.task.allowedPaths.some((allowed) => pathMatchesPattern(normalized, allowed))) {
|
|
115
|
-
return {
|
|
177
|
+
return {
|
|
178
|
+
ok: false,
|
|
179
|
+
reason: `final write-set approval exceeds allowedPaths: ${entry}`,
|
|
180
|
+
approvalSourceNodeId: binding.approvalSourceNodeId,
|
|
181
|
+
};
|
|
116
182
|
}
|
|
117
183
|
if (input.task.forbiddenPaths.some((forbidden) => pathMatchesPattern(normalized, forbidden))) {
|
|
118
|
-
return {
|
|
184
|
+
return {
|
|
185
|
+
ok: false,
|
|
186
|
+
reason: `final write-set approval overlaps forbiddenPaths: ${entry}`,
|
|
187
|
+
approvalSourceNodeId: binding.approvalSourceNodeId,
|
|
188
|
+
};
|
|
119
189
|
}
|
|
120
190
|
}
|
|
121
|
-
return {
|
|
191
|
+
return {
|
|
192
|
+
ok: true,
|
|
193
|
+
effectiveWriteSet,
|
|
194
|
+
approvalDigest: approval.approvalDigest,
|
|
195
|
+
};
|
|
122
196
|
}
|
|
123
197
|
export function buildNodePrompt(spec, task, upstream, options) {
|
|
124
198
|
const policy = resolveContextPolicy(spec);
|
|
@@ -126,6 +200,7 @@ export function buildNodePrompt(spec, task, upstream, options) {
|
|
|
126
200
|
spec,
|
|
127
201
|
task,
|
|
128
202
|
upstream,
|
|
203
|
+
runDir: options?.runDir,
|
|
129
204
|
resolvedSkills: policy.resolveSkills(spec, task),
|
|
130
205
|
maxUpstreamChars: policy.resolveMaxUpstreamChars(task),
|
|
131
206
|
projectGovernanceContext: options?.projectGovernanceContext,
|
|
@@ -314,10 +389,7 @@ function promptRestartCandidateForAttempt(input) {
|
|
|
314
389
|
}
|
|
315
390
|
const appendedPrefix = `${input.baseRuntimePrompt}\n\n`;
|
|
316
391
|
if (input.attemptPrompt.startsWith(appendedPrefix)) {
|
|
317
|
-
return [
|
|
318
|
-
input.taskPrompt,
|
|
319
|
-
input.attemptPrompt.slice(appendedPrefix.length),
|
|
320
|
-
]
|
|
392
|
+
return [input.taskPrompt, input.attemptPrompt.slice(appendedPrefix.length)]
|
|
321
393
|
.filter(Boolean)
|
|
322
394
|
.join("\n\n");
|
|
323
395
|
}
|
|
@@ -353,6 +425,7 @@ export async function buildNodePromptWithResolvedSkillInstructions(spec, task, u
|
|
|
353
425
|
spec,
|
|
354
426
|
task,
|
|
355
427
|
upstream,
|
|
428
|
+
runDir: options?.runDir,
|
|
356
429
|
resolvedSkills: skillNames,
|
|
357
430
|
resolvedSkillInstructions,
|
|
358
431
|
maxUpstreamChars: policy.resolveMaxUpstreamChars(task),
|
|
@@ -533,7 +606,11 @@ export async function executeDagNode(input) {
|
|
|
533
606
|
await notifyNodeObserver(input.observer, "onNodeFinish", nodeId, state);
|
|
534
607
|
};
|
|
535
608
|
if (task.finalWriteSetApproval) {
|
|
536
|
-
const authorization = parseAndValidateFinalWriteSetApproval({
|
|
609
|
+
const authorization = parseAndValidateFinalWriteSetApproval({
|
|
610
|
+
task,
|
|
611
|
+
spec,
|
|
612
|
+
state,
|
|
613
|
+
});
|
|
537
614
|
if (!authorization.ok) {
|
|
538
615
|
node.runtimeWriteAuthorization = {
|
|
539
616
|
schemaVersion: 1,
|
|
@@ -622,6 +699,7 @@ export async function executeDagNode(input) {
|
|
|
622
699
|
spec,
|
|
623
700
|
task,
|
|
624
701
|
upstream: state.nodes,
|
|
702
|
+
runDir,
|
|
625
703
|
snapshot: skillSnapshot,
|
|
626
704
|
projectGovernanceContext,
|
|
627
705
|
});
|
|
@@ -712,6 +790,7 @@ export async function executeDagNode(input) {
|
|
|
712
790
|
else {
|
|
713
791
|
({ prompt, resolvedSkills } =
|
|
714
792
|
await buildNodePromptWithResolvedSkillInstructions(spec, task, state.nodes, cwd, {
|
|
793
|
+
runDir,
|
|
715
794
|
projectGovernanceContext,
|
|
716
795
|
convergenceFeedback: deriveConvergenceFeedback(state),
|
|
717
796
|
}));
|
|
@@ -875,6 +954,7 @@ export async function executeDagNode(input) {
|
|
|
875
954
|
model,
|
|
876
955
|
...(thinking ? { thinking } : {}),
|
|
877
956
|
prompt: attemptPrompt,
|
|
957
|
+
resolvedSkills: resolvedSkills.map((skill) => skill.name),
|
|
878
958
|
attempt: attemptNumber,
|
|
879
959
|
reportActivity,
|
|
880
960
|
timeoutMs: livenessPolicy.absoluteMaxWallClockMs,
|
|
@@ -912,9 +992,7 @@ export async function executeDagNode(input) {
|
|
|
912
992
|
...result,
|
|
913
993
|
ok: false,
|
|
914
994
|
failureCategory: GOVERNANCE_BLOCKED_CATEGORY,
|
|
915
|
-
stderr: [result.stderr, audit.reason]
|
|
916
|
-
.filter(Boolean)
|
|
917
|
-
.join("\n"),
|
|
995
|
+
stderr: [result.stderr, audit.reason].filter(Boolean).join("\n"),
|
|
918
996
|
};
|
|
919
997
|
}
|
|
920
998
|
}
|
|
@@ -1237,6 +1315,13 @@ export async function executeDagNode(input) {
|
|
|
1237
1315
|
node.structuredArtifactSha256 = pendingStructuredArtifact.sha256;
|
|
1238
1316
|
node.structuredArtifactSchemaId = pendingStructuredArtifact.schemaId;
|
|
1239
1317
|
}
|
|
1318
|
+
if ((task.producesArtifacts?.length ?? 0) > 0) {
|
|
1319
|
+
node.declaredArtifacts = await materializeDeclaredArtifactFacts({
|
|
1320
|
+
runDir,
|
|
1321
|
+
task,
|
|
1322
|
+
node,
|
|
1323
|
+
});
|
|
1324
|
+
}
|
|
1240
1325
|
}
|
|
1241
1326
|
const finishedAt = new Date().toISOString();
|
|
1242
1327
|
node.finishedAt = finishedAt;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { assertReadableRegularFile, assertRealPathInsideBoundary, readFileBoundarySafe, } from "../../shared/path-safety.js";
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import path from "node:path";
|
|
1
2
|
import { DEFAULT_DAG_OUTPUT_LANGUAGE, } from "./types.js";
|
|
2
3
|
import { formatStdoutPreview, formatUpstreamArtifactPointerMap, } from "./upstream-artifacts.js";
|
|
3
4
|
export const MAX_UPSTREAM_CHARS = 2_000;
|
|
@@ -62,9 +63,10 @@ export function formatOutputLanguageBlock(language = DEFAULT_DAG_OUTPUT_LANGUAGE
|
|
|
62
63
|
"如果当前节点任务明确要求其他语言,以节点的明确要求为准。",
|
|
63
64
|
].join("\n");
|
|
64
65
|
}
|
|
65
|
-
export function buildUpstreamContext(task, upstream, maxChars = MAX_UPSTREAM_CHARS) {
|
|
66
|
+
export function buildUpstreamContext(task, upstream, maxChars = MAX_UPSTREAM_CHARS, runDir) {
|
|
66
67
|
const sections = [];
|
|
67
68
|
const toleratedFailures = new Set(task.failureAwareDependsOn ?? []);
|
|
69
|
+
const consumedArtifactIds = new Set((task.consumesArtifacts ?? []).map((binding) => binding.artifactId));
|
|
68
70
|
for (const depId of task.depends_on) {
|
|
69
71
|
const record = upstream[depId];
|
|
70
72
|
const stdout = record?.stdout?.trim() ? record.stdout : "";
|
|
@@ -118,6 +120,22 @@ export function buildUpstreamContext(task, upstream, maxChars = MAX_UPSTREAM_CHA
|
|
|
118
120
|
}
|
|
119
121
|
if (!record || record.status !== "FINISHED")
|
|
120
122
|
continue;
|
|
123
|
+
const consumedArtifacts = (record.declaredArtifacts ?? []).filter((artifact) => consumedArtifactIds.has(artifact.artifactId));
|
|
124
|
+
const consumedArtifactSection = runDir && consumedArtifacts.length > 0
|
|
125
|
+
? [
|
|
126
|
+
"Declared artifact bindings (read-only run evidence — use the exact paths below; do not edit):",
|
|
127
|
+
...consumedArtifacts.flatMap((artifact) => {
|
|
128
|
+
const absolutePath = path.resolve(runDir, ...artifact.path.split("/"));
|
|
129
|
+
return [
|
|
130
|
+
`- artifactId: ${artifact.artifactId}`,
|
|
131
|
+
` path: ${absolutePath}`,
|
|
132
|
+
` status: ${artifact.status}`,
|
|
133
|
+
` sha256: ${artifact.sha256 ?? "unknown"}`,
|
|
134
|
+
` mediaType: ${artifact.mediaType}`,
|
|
135
|
+
];
|
|
136
|
+
}),
|
|
137
|
+
].join("\n")
|
|
138
|
+
: "";
|
|
121
139
|
if (record.structuredArtifactPath &&
|
|
122
140
|
record.structuredArtifactSchemaId ===
|
|
123
141
|
"frontend-implementation-contract-v1") {
|
|
@@ -130,8 +148,12 @@ export function buildUpstreamContext(task, upstream, maxChars = MAX_UPSTREAM_CHA
|
|
|
130
148
|
].join("\n"));
|
|
131
149
|
continue;
|
|
132
150
|
}
|
|
133
|
-
if (!upstreamText)
|
|
151
|
+
if (!upstreamText) {
|
|
152
|
+
if (consumedArtifactSection) {
|
|
153
|
+
sections.push(`## Upstream output: ${depId}\n${consumedArtifactSection}`);
|
|
154
|
+
}
|
|
134
155
|
continue;
|
|
156
|
+
}
|
|
135
157
|
const artifactKind = stdout ? "stdout" : "assistant";
|
|
136
158
|
const artifactPath = stdout
|
|
137
159
|
? record.stdoutArtifactPath
|
|
@@ -147,6 +169,9 @@ export function buildUpstreamContext(task, upstream, maxChars = MAX_UPSTREAM_CHA
|
|
|
147
169
|
`- sha256: ${record.structuredArtifactSha256 ?? "unknown"}`,
|
|
148
170
|
].join("\n");
|
|
149
171
|
}
|
|
172
|
+
if (consumedArtifactSection) {
|
|
173
|
+
section += `\n\n${consumedArtifactSection}`;
|
|
174
|
+
}
|
|
150
175
|
if (truncated && artifactPath) {
|
|
151
176
|
section += formatUpstreamArtifactPointerMap(record, artifactKind);
|
|
152
177
|
}
|
|
@@ -329,7 +354,7 @@ function formatProjectGovernanceContext(ctx) {
|
|
|
329
354
|
return lines.join("\n");
|
|
330
355
|
}
|
|
331
356
|
export function buildDagNodePromptEnvelope(input) {
|
|
332
|
-
const { spec, task, upstream, resolvedSkills = [], resolvedSkillInstructions = [], maxUpstreamChars = MAX_UPSTREAM_CHARS, projectGovernanceContext, convergenceFeedback, } = input;
|
|
357
|
+
const { spec, task, upstream, runDir, resolvedSkills = [], resolvedSkillInstructions = [], maxUpstreamChars = MAX_UPSTREAM_CHARS, projectGovernanceContext, convergenceFeedback, } = input;
|
|
333
358
|
const objective = spec.objective ?? spec.title;
|
|
334
359
|
const successCriteria = formatBulletList(spec.successCriteria, "(none specified)");
|
|
335
360
|
const globalConstraints = formatBulletList(spec.globalConstraints, "(none specified)");
|
|
@@ -339,7 +364,7 @@ export function buildDagNodePromptEnvelope(input) {
|
|
|
339
364
|
? resolvedSkills.map((skill) => `- ${skill}`).join("\n")
|
|
340
365
|
: "(none)";
|
|
341
366
|
const skillInstructionsSection = formatResolvedSkillInstructions(resolvedSkillInstructions);
|
|
342
|
-
const upstreamContext = buildUpstreamContext(task, upstream, maxUpstreamChars);
|
|
367
|
+
const upstreamContext = buildUpstreamContext(task, upstream, maxUpstreamChars, runDir);
|
|
343
368
|
const readOnlyBoundary = buildReadOnlyBoundary(writePolicy);
|
|
344
369
|
const sections = [
|
|
345
370
|
`<dag_objective>\n${objective}\n</dag_objective>`,
|