@tea-agent/loop-agent 0.39.0-beta.1 → 0.39.0-beta.11
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 +4 -1
- package/CHANGELOG.md +349 -97
- package/README.md +9 -5
- package/bin/loop-agent.js +7 -3
- package/dist/application/task-lifecycle/advance.js +12 -0
- package/dist/application/task-lifecycle/recommendations.js +13 -2
- package/dist/build-stamp.json +3 -3
- package/dist/cli/command-definitions.js +23 -5
- package/dist/cli/program.js +21 -2
- package/dist/cli/update/notifier.js +2 -2
- package/dist/cli/update/runtime-activity.js +1 -29
- package/dist/cli.js +2 -1
- package/dist/commands/client-recovery.js +3 -0
- package/dist/commands/dag-follow-up.js +138 -0
- package/dist/commands/dag-rerun.js +55 -1
- package/dist/commands/init-model-catalog.js +464 -0
- package/dist/commands/init-upgrade.js +265 -97
- package/dist/commands/init.js +455 -28
- package/dist/commands/inspect-next.js +8 -0
- package/dist/commands/task-advance.js +19 -0
- package/dist/executors/dag-pi-executor.js +2370 -64
- package/dist/executors/pi-executor.js +11 -2
- package/dist/executors/pi-extension-resolver.js +233 -0
- package/dist/executors/pi-playwright-cli-tool.js +74 -28
- package/dist/executors/pi-read-budget-policy.js +239 -0
- package/dist/executors/pi-sdk-executor.js +225 -33
- package/dist/executors/pi-writer-tool-policy.js +57 -1
- package/dist/executors/shell-executor.js +1258 -224
- package/dist/executors/shell-write-guard.js +14 -0
- package/dist/executors/workspace-write-snapshot.js +68 -0
- package/dist/infrastructure/harness/atomic-write.js +23 -0
- package/dist/shared/dag-failure-category.js +150 -0
- package/dist/shared/dag-prompt-override.js +27 -0
- package/dist/shared/openspec-spec.js +70 -4
- package/dist/shared/operator/capabilities.js +39 -0
- package/dist/shared/playwright-cli-command-policy.js +15 -0
- package/dist/shared/update/console-notifier.js +100 -0
- package/dist/{cli → shared}/update/npm-client.js +70 -15
- package/dist/{cli → shared}/update/state.js +41 -13
- package/dist/task/config-types.js +50 -5
- package/dist/task/contract/apply.js +11 -1
- package/dist/task/contract/constants.js +2 -0
- package/dist/task/contract/hash.js +3 -0
- package/dist/task/contract/observe.js +25 -4
- package/dist/task/contract/paths.js +2 -1
- package/dist/task/contract/project.js +18 -1
- package/dist/task/contract/schema.js +4 -1
- package/dist/task/contract/transaction.js +12 -0
- package/dist/task/frontend-project-capability.js +199 -18
- package/dist/task/source-prepare/completeness.js +188 -0
- package/dist/task/source-prepare/fragment-inventory.js +477 -0
- package/dist/task/source-prepare/index.js +3 -0
- package/dist/task/source-prepare/ledger-reconciliation.js +127 -0
- package/dist/task/source-prepare/ledger-review.js +214 -0
- package/dist/task/source-prepare/ledger.js +545 -0
- package/dist/task/source-prepare/parse-intent.js +24 -4
- package/dist/task/source-prepare/prepare.js +298 -1
- package/dist/task/source-prepare/semantic-intake.js +25 -5
- package/dist/task/source-prepare/source-fidelity-pi.js +384 -0
- package/dist/task/source-prepare/types.js +22 -0
- package/dist/task/source-references.js +22 -1
- package/dist/worker/cli.js +18 -2
- package/dist/worker/console/chat/chat-event-store.js +75 -5
- package/dist/worker/console/chat/context-panel.js +7 -0
- package/dist/worker/console/chat/deferred-turn.js +12 -0
- package/dist/worker/console/chat/operation-card.js +8 -1
- package/dist/worker/console/chat/pi-console-config.js +81 -10
- package/dist/worker/console/chat/pi-runtime.js +331 -18
- package/dist/worker/console/chat/provider-error.js +98 -0
- package/dist/worker/console/chat/routes.js +395 -59
- package/dist/worker/console/chat/session-stats.js +179 -0
- package/dist/worker/console/chat/session-store.js +93 -63
- package/dist/worker/console/chat/todos.js +115 -0
- package/dist/worker/console/chat/tool-preview.js +42 -0
- package/dist/worker/console/chat/turn-process.js +20 -55
- package/dist/worker/console/chat/user-questions.js +266 -0
- package/dist/worker/console/chat/workspace-landing.js +79 -0
- package/dist/worker/console/console-handoff.js +82 -0
- package/dist/worker/console/console-update-and-init.js +115 -0
- package/dist/worker/console/console-update-runtime.js +84 -0
- package/dist/worker/console/dag-execution-receipt.js +33 -0
- package/dist/worker/console/draft-store.js +49 -0
- package/dist/worker/console/frontend-human-decision-adapter.js +19 -0
- package/dist/worker/console/frontend-split-operation-adapter.js +20 -0
- package/dist/worker/console/index.js +3 -0
- package/dist/worker/console/inspect-split.js +18 -0
- package/dist/worker/console/native-directory-picker.js +13 -0
- package/dist/worker/console/operation-run-facts.js +19 -3
- package/dist/worker/console/operation-store.js +227 -7
- package/dist/worker/console/operator-actions.js +388 -0
- package/dist/worker/console/operator-surface-health.js +1 -0
- package/dist/worker/console/operator-user-error.js +2 -2
- package/dist/worker/console/prd-intake-bridge.js +13 -0
- package/dist/worker/console/routes.js +355 -3
- package/dist/worker/console/security.js +67 -13
- package/dist/worker/console/server.js +609 -164
- package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-C4kVYweA.js → abnfDiagram-N423BO3Z-CXj_GnSb.js} +1 -1
- package/dist/worker/console/static/assets/{arc-Bj2M8iO5.js → arc-BZp6JAp7.js} +1 -1
- package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-CLlXe4-6.js → architectureDiagram-T3A2C74G-DfpcEuYU.js} +1 -1
- package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-psEp5xH0.js → blockDiagram-VBNYF7ZC-_Gf0xadb.js} +1 -1
- package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-DeKcOCGJ.js → c4Diagram-5PPSVZJV-Ct2QPCmv.js} +1 -1
- package/dist/worker/console/static/assets/channel-3TxJgYaH.js +1 -0
- package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-K0C744p1.js → chunk-2GRJ4B5K-BfklkzKl.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-xbNw4J9-.js → chunk-2Q5K7J3B-Dy25vZJV.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5RXB4S5H-DjLaSDUO.js → chunk-5RXB4S5H-BFlCRZep.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5VM5RSS4-CtiPlKel.js → chunk-5VM5RSS4-op3oVxIE.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-DZtolir7.js → chunk-6Q2QTUOP-C0R7rzF2.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-GF5L2VYU-CPcdNeew.js → chunk-GF5L2VYU-Bt44TCGy.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-JWPE2WC7-NgHc6V37.js → chunk-JWPE2WC7-_uEE_XFx.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-KBJHAD2P-DQ_T-hg8.js → chunk-KBJHAD2P-C3TOYGZ9.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-RYQCIY6F-DgLsxcVP.js → chunk-RYQCIY6F-Cz60oBPV.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-XXDRQBXY-UrVoM9z1.js → chunk-XXDRQBXY-8Bik0qis.js} +1 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-BHIkXpp3.js +1 -0
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-BHIkXpp3.js +1 -0
- package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-CLkYvTb3.js → cose-bilkent-JH36ORCC-C2QIOA4a.js} +1 -1
- package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-BwT_xKrE.js → cynefin-VYW2F7L2-CSH_yUUd.js} +1 -1
- package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-Bi9tuzif.js → cynefinDiagram-MW4NZA55-BDLw2XFM.js} +1 -1
- package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-BCIqkWBV.js → dagre-VZM6K2ZE-CcD9ZtF4.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-7IWD3JNH-Dip6l9_Z.js → diagram-7IWD3JNH-DqwtkBsI.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-B-xkh_wK.js → diagram-B4RE2ZJO-BWVEcqsC.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-LBJQPF4R-DZO0kTt_.js → diagram-LBJQPF4R-M-WAbtQi.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-Q27KOJAE-D6ZplNbZ.js → diagram-Q27KOJAE-DKW7SixP.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-UB23O5K3-BPekbhoS.js → diagram-UB23O5K3-PHHPPtrj.js} +1 -1
- package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-BtmaJpK5.js → ebnfDiagram-BXEA7PRR-BQD-B4RX.js} +1 -1
- package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-6DS0Xc44.js → erDiagram-JOGREHBK-BFvULb51.js} +1 -1
- package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-CihnROSm.js → flowDiagram-UKHOOZJN-Bw-aXTCb.js} +1 -1
- package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-CZC4zOE2.js → ganttDiagram-PKOTCBZU-BGKw04Qx.js} +1 -1
- package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-Dq1fhzGN.js → gitGraphDiagram-DS77QQ5N-RqKaPR-I.js} +1 -1
- package/dist/worker/console/static/assets/index-B_D8rbWc.js +407 -0
- package/dist/worker/console/static/assets/index-BdNx6fj0.css +1 -0
- package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-DlSl4BGx.js → infoDiagram-6WML65LV-YO5dnzrJ.js} +1 -1
- package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-snnzFl_U.js → ishikawaDiagram-WSZJBQD7-Bi1VZHMq.js} +1 -1
- package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-BDE7qpMx.js → journeyDiagram-NVQOT4AX-Bszls1DD.js} +1 -1
- package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-CD2Ci04G.js → kanban-definition-27J2QSJJ-PhzeaZ09.js} +1 -1
- package/dist/worker/console/static/assets/{linear-DdnapKIH.js → linear-BsjbDoXi.js} +1 -1
- package/dist/worker/console/static/assets/{mermaid.core-BVjAT9b8.js → mermaid.core-0B7NnWKk.js} +5 -5
- package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-D0yJk-zA.js → mindmap-definition-FAOFIHXS-BJr4Fj-q.js} +1 -1
- package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-BVo9tFP-.js → pegDiagram-VL7TDLO6-moC4fpGB.js} +1 -1
- package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-DnOV-mZ_.js → pieDiagram-7S7Q4E2Y-BEw37-2c.js} +1 -1
- package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-CzUJo58i.js → quadrantDiagram-CIZ2JOQS-Cq6LyasU.js} +1 -1
- package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-Bvikrolh.js → railroadDiagram-AXF67PYL-DUCMcK0D.js} +1 -1
- package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-DtaSVCap.js → requirementDiagram-LRYGKXZP-C3upTZm7.js} +1 -1
- package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-C2c9A0wW.js → sankeyDiagram-W5VNT64P-BI_gMsCW.js} +1 -1
- package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-DmXJcU7r.js → sequenceDiagram-SI44F4Z6-YFOIRzfN.js} +1 -1
- package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-B4GUFW92.js → sizeCapture-X5ZJPWSS-dOnB7UDD.js} +1 -1
- package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-Dsad2MXf.js → stateDiagram-OKZ733FA-BXUniaIh.js} +1 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-Cdi6UhLa.js +1 -0
- package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-DGq48Fbi.js → swimlanes-SLNWSIFB-2tA4wTNu.js} +2 -2
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-D-RJBbb0.js +8 -0
- package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-CSSi6OFf.js → timeline-definition-Z64GVDOM-DO0HkJXC.js} +1 -1
- package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-BqyzPrWv.js → vennDiagram-T6HMQDX7-DvIOzixv.js} +1 -1
- package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-DlznVb6S.js → wardleyDiagram-T6FBY63Y-DXDZ0cTj.js} +1 -1
- package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-CfBcgJ_K.js → xychartDiagram-ELKLHX3M-B32Ark0D.js} +1 -1
- package/dist/worker/console/static/index.html +2 -2
- package/dist/worker/console/static-src/active-run-badge.js +17 -0
- package/dist/worker/console/static-src/app/console-types.js +68 -4
- package/dist/worker/console/static-src/app/useConsoleShell.js +30 -12
- package/dist/worker/console/static-src/app/useOperatorActions.js +41 -6
- package/dist/worker/console/static-src/app/useRecoveryConsole.js +45 -10
- package/dist/worker/console/static-src/app/useRunProgress.js +47 -0
- package/dist/worker/console/static-src/app/useTaskWizard.js +74 -2
- package/dist/worker/console/static-src/night/useNightBoard.js +8 -5
- package/dist/worker/console/static-src/operator-chat/cards/failure-category-advice.js +25 -0
- package/dist/worker/console/static-src/operator-chat/chat-sse-events.js +141 -8
- package/dist/worker/console/static-src/operator-chat/compaction-message.js +79 -0
- package/dist/worker/console/static-src/operator-chat/details-dag-actions.js +122 -0
- package/dist/worker/console/static-src/operator-chat/input-history.js +76 -0
- package/dist/worker/console/static-src/operator-chat/mutation-gate.js +1 -1
- package/dist/worker/console/static-src/operator-chat/refs.js +3 -0
- package/dist/worker/console/static-src/operator-chat/runtime-selection-labels.js +27 -0
- package/dist/worker/console/static-src/operator-chat/spatial-overlay.js +1 -2
- package/dist/worker/console/static-src/operator-chat/turn-stream-controller.js +25 -0
- package/dist/worker/console/static-src/operator-chat/turn-submission.js +16 -3
- package/dist/worker/console/static-src/operator-chat/useChatSessions.js +181 -87
- package/dist/worker/console/static-src/operator-chat/useChatStream.js +183 -48
- package/dist/worker/console/static-src/operator-chat/useChatThread.js +177 -8
- package/dist/worker/console/static-src/operator-chat/useComposer.js +32 -0
- package/dist/worker/console/static-src/operator-chat/useRepoBrowser.js +63 -9
- package/dist/worker/console/static-src/operator-chat/useWorkspaceBrowserGate.js +4 -0
- package/dist/worker/console/static-src/operator-chat/workspace-layout-mode.js +3 -3
- package/dist/worker/console/static-src/pages/tasks/run-id-resolution.js +79 -0
- package/dist/worker/console/static-src/pages/tasks/run-ownership-verify.js +23 -0
- package/dist/worker/console/static-src/pages/tasks/run-panel-progress.js +110 -0
- package/dist/worker/console/static-src/shell/useWorkspaces.js +299 -0
- package/dist/worker/console/static-src/shell/workspace-route.js +339 -0
- package/dist/worker/console/workspace-context.js +344 -0
- package/dist/worker/console/workspace-registry.js +214 -0
- package/dist/worker/loop-agent/loop-agent-client.js +7 -3
- package/dist/worker/materialize/frontend-split-task-materializer.js +72 -0
- package/dist/worker/materialize/harness-task-lineage.js +5 -2
- package/dist/worker/observability/init-runtime-activity.js +26 -0
- package/dist/worker/observability/read-model.js +22 -0
- package/dist/worker/observe/node-input.js +160 -3
- package/dist/worker/observe/node-process.js +377 -0
- package/dist/worker/observe/routes.js +224 -3
- package/dist/worker/observe/server.js +4 -0
- package/dist/worker/observe/shell-handler-keys.js +34 -0
- package/dist/worker/observe/static/api.js +90 -4
- package/dist/worker/observe/static/app.js +18 -70
- package/dist/worker/observe/static/constants.js +8 -1
- package/dist/worker/observe/static/dag-history-labels.js +4 -0
- package/dist/worker/observe/static/dag-node-purpose.d.ts +6 -0
- package/dist/worker/observe/static/dag-node-purpose.js +208 -0
- package/dist/worker/observe/static/format.js +60 -0
- package/dist/worker/observe/static/index.html +6 -1
- package/dist/worker/observe/static/inspect-workspace.js +307 -0
- package/dist/worker/observe/static/markdown-render.js +20 -1
- package/dist/worker/observe/static/operator-chrome.css +145 -20
- package/dist/worker/observe/static/operator-chrome.d.ts +20 -3
- package/dist/worker/observe/static/operator-chrome.js +330 -96
- package/dist/worker/observe/static/prompt-restart-candidates.d.ts +18 -0
- package/dist/worker/observe/static/prompt-restart-candidates.js +36 -0
- package/dist/worker/observe/static/router.d.ts +46 -0
- package/dist/worker/observe/static/router.js +25 -1
- package/dist/worker/observe/static/state.d.ts +50 -0
- package/dist/worker/observe/static/state.js +184 -1
- package/dist/worker/observe/static/styles.css +395 -0
- package/dist/worker/observe/static/views/dag-graph.js +4 -2
- package/dist/worker/observe/static/views/dag-inspector.js +1067 -9
- package/dist/worker/observe/static/views/dag.d.ts +13 -0
- package/dist/worker/observe/static/views/dag.js +14 -5
- package/dist/worker/observe/static/views/dashboard.js +6 -4
- package/dist/worker/observe/static/views/failures.js +5 -2
- package/dist/worker/observe/static/views/night.js +2 -2
- package/dist/worker/observe/static/views/pool.js +1 -0
- package/dist/worker/observe/static/views/run.js +3 -1
- package/dist/worker/observe/static/views/session-timeline.js +78 -9
- package/dist/worker/observe/static/views/task.js +56 -1
- package/dist/workflows/dag/backend-test-case-coverage-analysis.js +49 -3
- package/dist/workflows/dag/backend-test-markdown-workflow.js +176 -1
- package/dist/workflows/dag/backend-test-pytest-collection.js +98 -14
- package/dist/workflows/dag/backend-test-writer-completeness.js +17 -17
- package/dist/workflows/dag/contract-output-registry.js +15 -0
- package/dist/workflows/dag/contract-validator-registrations.js +1 -2
- package/dist/workflows/dag/dynamic-runtime/loop-until.js +1 -1
- package/dist/workflows/dag/dynamic-runtime/map.js +2 -1
- package/dist/workflows/dag/failure-category.js +7 -116
- package/dist/workflows/dag/frontend-closeout.js +221 -0
- package/dist/workflows/dag/frontend-design-policy.js +400 -0
- package/dist/workflows/dag/frontend-human-decision.js +182 -0
- package/dist/workflows/dag/frontend-implementation-contract.js +538 -166
- package/dist/workflows/dag/frontend-plan-render.js +24 -0
- package/dist/workflows/dag/frontend-prewrite-gate.js +388 -392
- package/dist/workflows/dag/frontend-provider-capability-matrix.js +159 -0
- package/dist/workflows/dag/frontend-recovery-capsule.js +455 -0
- package/dist/workflows/dag/frontend-recovery-controller.js +202 -0
- package/dist/workflows/dag/frontend-recovery-lineage.js +178 -0
- package/dist/workflows/dag/frontend-recovery-plan.js +17 -10
- package/dist/workflows/dag/frontend-recovery-run.js +33 -20
- package/dist/workflows/dag/frontend-repair.js +7 -424
- package/dist/workflows/dag/frontend-review-context.js +288 -14
- package/dist/workflows/dag/frontend-review-findings.js +270 -0
- package/dist/workflows/dag/frontend-risk.js +15 -2
- package/dist/workflows/dag/frontend-shadow-dual-write.js +814 -0
- package/dist/workflows/dag/frontend-shape-capsule-store.js +191 -0
- package/dist/workflows/dag/frontend-shape-facts.js +409 -0
- package/dist/workflows/dag/frontend-shape.js +427 -0
- package/dist/workflows/dag/frontend-source-fidelity-ledger.js +108 -0
- package/dist/workflows/dag/frontend-split-application-service.js +203 -0
- package/dist/workflows/dag/frontend-split-orchestrator.js +899 -0
- package/dist/workflows/dag/frontend-test-case-checklist.js +30 -4
- package/dist/workflows/dag/frontend-test-case-manifest.js +11 -4
- package/dist/workflows/dag/frontend-test-case-quality.js +11 -16
- package/dist/workflows/dag/frontend-test-environment-probe.js +230 -0
- package/dist/workflows/dag/frontend-test-html-report.js +3 -1
- package/dist/workflows/dag/frontend-test-l5-report.js +3 -1
- package/dist/workflows/dag/frontend-test-layout.js +159 -0
- package/dist/workflows/dag/frontend-test-markdown.js +61 -0
- package/dist/workflows/dag/frontend-test-result-contract.js +188 -42
- package/dist/workflows/dag/frontend-test-standard-scenarios.js +70 -0
- package/dist/workflows/dag/frontend-typed-event-store.js +452 -0
- package/dist/workflows/dag/frontend-typed-event-transaction.js +180 -0
- package/dist/workflows/dag/frontend-verification-trace.js +52 -17
- package/dist/workflows/dag/frontend-worktree-diff.js +250 -17
- package/dist/workflows/dag/frontend-writer-admission.js +272 -0
- package/dist/workflows/dag/frontend-writer-status.js +244 -0
- package/dist/workflows/dag/init-hybrid.js +1072 -660
- package/dist/workflows/dag/interrupt-request.js +10 -3
- package/dist/workflows/dag/lifecycle.js +3 -3
- package/dist/workflows/dag/node-execution.js +552 -58
- package/dist/workflows/dag/prompt.js +44 -19
- package/dist/workflows/dag/recovery-lease.js +80 -0
- package/dist/workflows/dag/report.js +37 -1
- package/dist/workflows/dag/rerun-feedback.js +124 -1
- package/dist/workflows/dag/rerun-plan.js +144 -5
- package/dist/workflows/dag/rerun-run.js +81 -6
- package/dist/workflows/dag/rerun-task.js +29 -0
- package/dist/workflows/dag/retry-policy.js +318 -8
- package/dist/workflows/dag/runner.js +429 -122
- package/dist/workflows/dag/scheduler.js +114 -16
- package/dist/workflows/dag/structured-output-repair.js +712 -0
- package/dist/workflows/dag/types.js +338 -31
- package/dist/workflows/dag/upstream-artifacts.js +0 -4
- package/dist/workflows/dag/validate.js +92 -32
- package/docs/README.md +2 -0
- package/docs/architecture/evolution.md +45 -0
- package/docs/init-surface.manifest.json +9 -0
- package/docs/operations/README.md +1 -1
- package/docs/operations/local-development-environment.md +21 -0
- package/docs/templates/README.md +2 -1
- package/docs/templates/agent-dag-report.schema.json +8 -2
- package/docs/templates/agent-dag.schema.json +63 -1
- package/docs/templates/backend-test-dag.json +265 -237
- package/docs/templates/frontend-implementation-contract.schema.json +684 -24
- package/docs/templates/frontend-test-case-checklist.md +2 -2
- package/docs/templates/frontend-test-dag.json +8 -10
- package/docs/templates/frontend-test-dag.retrieve-context.prompt.md +1 -1
- package/docs/templates/init-managed-agents.md +13 -3
- package/docs/templates/spec-registry.schema.json +45 -0
- package/harness.json +1 -1
- package/package.json +7 -1
- package/scripts/next-info.mjs +356 -0
- package/scripts/next-publish-gate.mjs +238 -0
- package/scripts/release-source-binding.mjs +251 -0
- package/skills/codebase-scout/SKILL.md +1 -1
- package/skills/fe-test-ui-scout/SKILL.md +65 -0
- package/skills/fe-test-ui-scout/references/ledger-schema.md +62 -0
- package/skills/fe-test-ui-scout/references/recon-protocol.md +54 -0
- package/skills/frontend-bounded-implement/SKILL.md +8 -14
- package/skills/frontend-design-review/SKILL.md +20 -41
- package/skills/frontend-implementation/SKILL.md +3 -4
- package/skills/frontend-implementation/references/design-spec.md +15 -10
- package/skills/frontend-implementation/references/node-contracts.md +23 -13
- package/skills/frontend-review/SKILL.md +22 -14
- package/skills/frontend-review/references/review-findings.md +6 -7
- package/skills/loop-agent/references/command-reference.md +12 -4
- package/skills/loop-agent/references/hybrid-dag.md +8 -1
- package/skills/playwright-cli/SKILL.md +23 -1
- package/skills/playwright-cli-case-generator/SKILL.md +29 -8
- package/dist/worker/console/static/assets/channel-CPF4N7pf.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-C2DwA_Y1.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-C2DwA_Y1.js +0 -1
- package/dist/worker/console/static/assets/index-DTZOKgAn.js +0 -404
- package/dist/worker/console/static/assets/index-Ya5FE7cD.css +0 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-B8FB_cNk.js +0 -1
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-CrZYaWfx.js +0 -8
- package/dist/worker/console/static-src/operator-chat/activity-rail-presentation.js +0 -73
- package/dist/worker/console/static-src/operator-chat/useActivityRailTransition.js +0 -59
|
@@ -45,9 +45,42 @@ function formatResolvedSkillInstructions(instructions) {
|
|
|
45
45
|
if (!instructions || instructions.length === 0)
|
|
46
46
|
return "(none)";
|
|
47
47
|
return instructions
|
|
48
|
-
.map((instruction) => instruction
|
|
48
|
+
.map((instruction) => formatSkillChecklist(instruction))
|
|
49
49
|
.join("\n\n---\n\n");
|
|
50
50
|
}
|
|
51
|
+
/** Keep skill provenance, but turn bounded inline manuals into a small action list. */
|
|
52
|
+
function formatSkillChecklist(instruction) {
|
|
53
|
+
const metadata = [
|
|
54
|
+
`Skill: ${instruction.name}`,
|
|
55
|
+
`Resolution: ${instruction.resolution}`,
|
|
56
|
+
...(instruction.path ? [`Path: ${instruction.path}`] : []),
|
|
57
|
+
...(instruction.sha256 ? [`Sha256: ${instruction.sha256}`] : []),
|
|
58
|
+
"Injection mode: checklist",
|
|
59
|
+
];
|
|
60
|
+
if (instruction.resolution !== "resolved")
|
|
61
|
+
return [...metadata, instruction.promptText].join("\n");
|
|
62
|
+
const source = instruction.promptText.split("Instructions:\n")[1] ?? "";
|
|
63
|
+
const candidates = source
|
|
64
|
+
.split(/\r?\n/)
|
|
65
|
+
.map((line) => line.trim())
|
|
66
|
+
.filter((line) => /^(?:#{1,3}\s+|[-*]\s+|\d+[.)]\s+)/.test(line) || line.length > 0)
|
|
67
|
+
.map((line) => line.replace(/^#{1,3}\s+/, "").replace(/^[-*]\s+/, "").replace(/^\d+[.)]\s+/, ""))
|
|
68
|
+
.filter((line) => line.length > 0);
|
|
69
|
+
const checklist = [];
|
|
70
|
+
let chars = 0;
|
|
71
|
+
for (const item of candidates) {
|
|
72
|
+
const bounded = item.slice(0, 240);
|
|
73
|
+
if (checklist.length >= 10 || chars + bounded.length > 1_600)
|
|
74
|
+
break;
|
|
75
|
+
checklist.push(`- ${bounded}`);
|
|
76
|
+
chars += bounded.length;
|
|
77
|
+
}
|
|
78
|
+
return [
|
|
79
|
+
...metadata,
|
|
80
|
+
"Checklist (apply these actions; consult the immutable resolved file only when a listed action requires detail):",
|
|
81
|
+
...(checklist.length > 0 ? checklist : ["- Follow the resolved skill's declared workflow."]),
|
|
82
|
+
].join("\n");
|
|
83
|
+
}
|
|
51
84
|
export function formatOutputLanguageBlock(language = DEFAULT_DAG_OUTPUT_LANGUAGE) {
|
|
52
85
|
if (language === "en") {
|
|
53
86
|
return [
|
|
@@ -67,7 +100,6 @@ export function buildUpstreamContext(task, upstream, maxChars = MAX_UPSTREAM_CHA
|
|
|
67
100
|
const toleratedFailures = new Set(task.failureAwareDependsOn ?? []);
|
|
68
101
|
for (const depId of task.depends_on) {
|
|
69
102
|
const record = upstream[depId];
|
|
70
|
-
const artifactFirst = task.artifactFirstUpstreamNodeIds?.includes(depId);
|
|
71
103
|
const stdout = record?.stdout?.trim() ? record.stdout : "";
|
|
72
104
|
const assistantText = record?.assistantText?.trim()
|
|
73
105
|
? record.assistantText
|
|
@@ -119,23 +151,16 @@ export function buildUpstreamContext(task, upstream, maxChars = MAX_UPSTREAM_CHA
|
|
|
119
151
|
}
|
|
120
152
|
if (!record || record.status !== "FINISHED")
|
|
121
153
|
continue;
|
|
122
|
-
if (
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
record.
|
|
129
|
-
record.structuredArtifactSchemaId
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
lines.push(`- payload kind: ${record.frontendPlanOutput.payloadKind}`, `- citations: ${record.frontendPlanOutput.citationsArtifactPath}`, `- citations sha256: ${record.frontendPlanOutput.citationsArtifactSha256}`);
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
else {
|
|
136
|
-
lines.push("- artifact: unavailable (do not reconstruct the contract from node prose; treat this as an upstream artifact integrity failure)");
|
|
137
|
-
}
|
|
138
|
-
sections.push(lines.join("\n"));
|
|
154
|
+
if (record.structuredArtifactPath &&
|
|
155
|
+
record.structuredArtifactSchemaId ===
|
|
156
|
+
"frontend-implementation-contract-v1") {
|
|
157
|
+
sections.push([
|
|
158
|
+
`## Upstream output: ${depId}`,
|
|
159
|
+
"Validated structured artifact (read-only runner evidence — use read tool to fetch; do not edit). Do not infer the contract from stdout.",
|
|
160
|
+
`- path: ${record.structuredArtifactPath}`,
|
|
161
|
+
`- schema: ${record.structuredArtifactSchemaId}`,
|
|
162
|
+
`- sha256: ${record.structuredArtifactSha256 ?? "unknown"}`,
|
|
163
|
+
].join("\n"));
|
|
139
164
|
continue;
|
|
140
165
|
}
|
|
141
166
|
if (!upstreamText)
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { createHash, randomBytes } from "node:crypto";
|
|
2
|
+
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { writeJsonAtomic } from "../../infrastructure/harness/atomic-write.js";
|
|
5
|
+
import { DAG_RUNS_DIR } from "./lifecycle.js";
|
|
6
|
+
function recoveryLeasePath(cwd, parentRunId) {
|
|
7
|
+
// Never derive a filesystem name directly from an externally supplied legacy
|
|
8
|
+
// run id. The content remains auditable while the filename is traversal-safe.
|
|
9
|
+
const key = createHash("sha256").update(parentRunId).digest("hex");
|
|
10
|
+
return path.join(cwd, DAG_RUNS_DIR, ".recovery-leases", `${key}.json`);
|
|
11
|
+
}
|
|
12
|
+
function parseLease(raw, parentRunId) {
|
|
13
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw)) {
|
|
14
|
+
throw new Error("invalid dag recovery lease: expected object");
|
|
15
|
+
}
|
|
16
|
+
const record = raw;
|
|
17
|
+
if (record.schemaVersion !== 1 ||
|
|
18
|
+
record.parentRunId !== parentRunId ||
|
|
19
|
+
typeof record.requestId !== "string" ||
|
|
20
|
+
record.requestId.length === 0 ||
|
|
21
|
+
(record.kind !== "auto-frontend-recovery" &&
|
|
22
|
+
record.kind !== "rerun-from-node" &&
|
|
23
|
+
record.kind !== "standalone-task-rerun") ||
|
|
24
|
+
typeof record.leaseToken !== "string" ||
|
|
25
|
+
record.leaseToken.length === 0 ||
|
|
26
|
+
typeof record.acquiredAt !== "string" ||
|
|
27
|
+
typeof record.executionRef !== "undefined" &&
|
|
28
|
+
typeof record.executionRef !== "string") {
|
|
29
|
+
throw new Error("invalid dag recovery lease: schema mismatch");
|
|
30
|
+
}
|
|
31
|
+
return record;
|
|
32
|
+
}
|
|
33
|
+
export async function acquireDagRecoveryLease(input) {
|
|
34
|
+
const leasePath = recoveryLeasePath(input.cwd, input.parentRunId);
|
|
35
|
+
const lease = {
|
|
36
|
+
schemaVersion: 1,
|
|
37
|
+
parentRunId: input.parentRunId,
|
|
38
|
+
requestId: input.requestId,
|
|
39
|
+
kind: input.kind,
|
|
40
|
+
leaseToken: randomBytes(16).toString("hex"),
|
|
41
|
+
acquiredAt: (input.now ?? (() => new Date()))().toISOString(),
|
|
42
|
+
};
|
|
43
|
+
await mkdir(path.dirname(leasePath), { recursive: true });
|
|
44
|
+
try {
|
|
45
|
+
// `wx` is the cross-process compare-and-create primitive. The lease is
|
|
46
|
+
// deliberately immutable for a parent run: a later recovery must follow
|
|
47
|
+
// the committed child lineage rather than fork another DAG.
|
|
48
|
+
await writeFile(leasePath, `${JSON.stringify(lease, null, 2)}\n`, {
|
|
49
|
+
encoding: "utf8",
|
|
50
|
+
flag: "wx",
|
|
51
|
+
});
|
|
52
|
+
return { kind: "acquired", leasePath, lease };
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
if (!(error && typeof error === "object" && "code" in error && error.code === "EEXIST")) {
|
|
56
|
+
throw error;
|
|
57
|
+
}
|
|
58
|
+
const existing = parseLease(JSON.parse(await readFile(leasePath, "utf8")), input.parentRunId);
|
|
59
|
+
return { kind: "held", leasePath, lease: existing };
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/** Persist the operation/run identity before the caller is allowed to execute it. */
|
|
63
|
+
export async function bindDagRecoveryLeaseExecution(input) {
|
|
64
|
+
if (!input.executionRef.trim()) {
|
|
65
|
+
throw new Error("dag recovery lease executionRef is required");
|
|
66
|
+
}
|
|
67
|
+
const existing = parseLease(JSON.parse(await readFile(input.leasePath, "utf8")), input.lease.parentRunId);
|
|
68
|
+
if (existing.leaseToken !== input.lease.leaseToken) {
|
|
69
|
+
throw new Error("dag recovery lease ownership drift");
|
|
70
|
+
}
|
|
71
|
+
if (existing.executionRef && existing.executionRef !== input.executionRef) {
|
|
72
|
+
throw new Error("dag recovery lease execution identity drift");
|
|
73
|
+
}
|
|
74
|
+
const bound = {
|
|
75
|
+
...existing,
|
|
76
|
+
executionRef: input.executionRef,
|
|
77
|
+
};
|
|
78
|
+
await writeJsonAtomic(input.leasePath, bound);
|
|
79
|
+
return bound;
|
|
80
|
+
}
|
|
@@ -125,6 +125,8 @@ const dagNodeArtifactsReportSchema = z
|
|
|
125
125
|
humanEscalationMd: dagArtifactRefSchema,
|
|
126
126
|
humanApproval: dagArtifactRefSchema,
|
|
127
127
|
humanRejection: dagArtifactRefSchema,
|
|
128
|
+
changeManifest: dagArtifactRefSchema,
|
|
129
|
+
changeManifestChangedFiles: z.array(z.string()).optional(),
|
|
128
130
|
})
|
|
129
131
|
.strict();
|
|
130
132
|
const dagNodeReportRowSchema = z
|
|
@@ -259,6 +261,20 @@ export async function probeArtifact(filePath) {
|
|
|
259
261
|
export async function buildNodeArtifactsReport(runDir, nodeId) {
|
|
260
262
|
const nodeDir = path.join(runDir, nodeId);
|
|
261
263
|
const artifact = (name) => probeArtifact(path.join(nodeDir, name));
|
|
264
|
+
const changeManifestRef = await artifact("change-manifest.json");
|
|
265
|
+
let changeManifestChangedFiles;
|
|
266
|
+
if (changeManifestRef.exists) {
|
|
267
|
+
try {
|
|
268
|
+
const manifest = JSON.parse(await readFile(path.join(nodeDir, "change-manifest.json"), "utf-8"));
|
|
269
|
+
changeManifestChangedFiles = Array.isArray(manifest.changedFiles)
|
|
270
|
+
? manifest.changedFiles
|
|
271
|
+
.filter((item) => typeof item === "string")
|
|
272
|
+
: undefined;
|
|
273
|
+
}
|
|
274
|
+
catch {
|
|
275
|
+
// unreadable manifest → leave undefined
|
|
276
|
+
}
|
|
277
|
+
}
|
|
262
278
|
return {
|
|
263
279
|
resultSummary: await artifact("result.summary.md"),
|
|
264
280
|
promptRedacted: await artifact("prompt.redacted.md"),
|
|
@@ -270,6 +286,8 @@ export async function buildNodeArtifactsReport(runDir, nodeId) {
|
|
|
270
286
|
humanEscalationMd: await artifact("human-escalation.md"),
|
|
271
287
|
humanApproval: await artifact("human-approval.json"),
|
|
272
288
|
humanRejection: await artifact("human-rejection.json"),
|
|
289
|
+
changeManifest: changeManifestRef,
|
|
290
|
+
changeManifestChangedFiles,
|
|
273
291
|
};
|
|
274
292
|
}
|
|
275
293
|
function taskById(spec) {
|
|
@@ -726,6 +744,12 @@ function formatArtifactFlags(artifacts) {
|
|
|
726
744
|
flags.push("approval");
|
|
727
745
|
if (artifacts.humanRejection.exists)
|
|
728
746
|
flags.push("rejection");
|
|
747
|
+
if (artifacts.changeManifest.exists) {
|
|
748
|
+
const count = artifacts.changeManifestChangedFiles?.length;
|
|
749
|
+
flags.push(count !== undefined
|
|
750
|
+
? `changed:${count}`
|
|
751
|
+
: "change-manifest");
|
|
752
|
+
}
|
|
729
753
|
return flags.length > 0 ? flags.join(",") : "-";
|
|
730
754
|
}
|
|
731
755
|
function formatTimestamp(value) {
|
|
@@ -749,7 +773,19 @@ function summarizeNodeStatuses(nodes) {
|
|
|
749
773
|
return `${total} total / ${finished} finished / ${failed} failed / ${skipped} skipped / ${pending} pending`;
|
|
750
774
|
}
|
|
751
775
|
function hasArtifact(artifacts) {
|
|
752
|
-
return
|
|
776
|
+
return [
|
|
777
|
+
artifacts.resultSummary,
|
|
778
|
+
artifacts.promptRedacted,
|
|
779
|
+
artifacts.skills,
|
|
780
|
+
artifacts.nodeRecord,
|
|
781
|
+
artifacts.decisionEnvelope,
|
|
782
|
+
artifacts.decisionEnvelopeValidation,
|
|
783
|
+
artifacts.humanEscalationJson,
|
|
784
|
+
artifacts.humanEscalationMd,
|
|
785
|
+
artifacts.humanApproval,
|
|
786
|
+
artifacts.humanRejection,
|
|
787
|
+
artifacts.changeManifest,
|
|
788
|
+
].some((artifact) => artifact.exists);
|
|
753
789
|
}
|
|
754
790
|
function failureRows(run) {
|
|
755
791
|
return run.nodes.filter((node) => node.status === "ERROR" ||
|
|
@@ -2,6 +2,7 @@ import { createHash } from "node:crypto";
|
|
|
2
2
|
import { readFile } from "node:fs/promises";
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { parseJsonReviewVerdict } from "./output-protocol.js";
|
|
5
|
+
import { FRONTEND_DESIGN_REVIEW_NODE_ID, FRONTEND_REVIEW_NODE_ID, readCommittedDesignRequestFact, readCommittedReviewRequestFact, } from "./frontend-review-findings.js";
|
|
5
6
|
export const MAX_RERUN_FEEDBACK_CHARS = 12_000;
|
|
6
7
|
const RERUN_FEEDBACK_TRUNCATION_MARKER = "\n\n...[上一轮反馈已按长度上限截断]...\n\n";
|
|
7
8
|
const MAX_EVIDENCE_REFS = 8;
|
|
@@ -35,6 +36,44 @@ function requestRevision(text) {
|
|
|
35
36
|
return (emphasized?.[1] ?? trimmed).trim() === "VERDICT: request-revision";
|
|
36
37
|
});
|
|
37
38
|
}
|
|
39
|
+
/**
|
|
40
|
+
* Deterministically render a typed `request_review_changes` fact into feedback
|
|
41
|
+
* text. Unlike the legacy text channel (VERDICT: request-revision), the
|
|
42
|
+
* frontend review terminal is a committed typed fact; the findings are the
|
|
43
|
+
* actionable body the next round must consume.
|
|
44
|
+
*/
|
|
45
|
+
export function formatTypedReviewRequestFeedback(fact) {
|
|
46
|
+
const lines = [];
|
|
47
|
+
lines.push(`frontend review committed request_review_changes (issueCategory=${typeof fact.issueCategory === "string" ? fact.issueCategory : "unknown"})`);
|
|
48
|
+
if (Array.isArray(fact.findings) && fact.findings.length > 0) {
|
|
49
|
+
lines.push("review findings:");
|
|
50
|
+
for (const finding of fact.findings) {
|
|
51
|
+
if (!finding || typeof finding !== "object")
|
|
52
|
+
continue;
|
|
53
|
+
const record = finding;
|
|
54
|
+
const severity = typeof record.severity === "string" ? record.severity : "";
|
|
55
|
+
const issue = typeof record.issue === "string" ? record.issue : "";
|
|
56
|
+
const location = [
|
|
57
|
+
typeof record.file === "string" ? record.file : "",
|
|
58
|
+
typeof record.line === "number" ? String(record.line) : "",
|
|
59
|
+
]
|
|
60
|
+
.filter(Boolean)
|
|
61
|
+
.join(":");
|
|
62
|
+
const prefix = location ? `[${location}] ` : "";
|
|
63
|
+
const requiredChange = typeof record.requiredChange === "string" &&
|
|
64
|
+
record.requiredChange.length > 0
|
|
65
|
+
? ` (requiredChange: ${record.requiredChange})`
|
|
66
|
+
: "";
|
|
67
|
+
lines.push(`- ${prefix}${severity}: ${issue}${requiredChange}`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
if (Array.isArray(fact.evidenceRefs) && fact.evidenceRefs.length > 0) {
|
|
71
|
+
lines.push(`evidenceRefs: ${fact.evidenceRefs
|
|
72
|
+
.filter((ref) => typeof ref === "string")
|
|
73
|
+
.join(", ")}`);
|
|
74
|
+
}
|
|
75
|
+
return lines.join("\n");
|
|
76
|
+
}
|
|
38
77
|
function pathInside(root, candidate) {
|
|
39
78
|
const relative = path.relative(root, candidate);
|
|
40
79
|
return (relative === "" ||
|
|
@@ -127,6 +166,41 @@ function evidenceRefs(repoRoot, runDir, pass, sourceNodeId) {
|
|
|
127
166
|
}
|
|
128
167
|
return result;
|
|
129
168
|
}
|
|
169
|
+
/**
|
|
170
|
+
* Read the committed frontend review request fact (typed terminal channel).
|
|
171
|
+
* Returns { nodeId, fact } when the review node committed `request_review_changes`.
|
|
172
|
+
*/
|
|
173
|
+
async function readTypedReviewRequestFact(runDir) {
|
|
174
|
+
const fact = await readCommittedReviewRequestFact({ runDir });
|
|
175
|
+
if (!fact)
|
|
176
|
+
return undefined;
|
|
177
|
+
return {
|
|
178
|
+
nodeId: FRONTEND_REVIEW_NODE_ID,
|
|
179
|
+
fact: {
|
|
180
|
+
issueCategory: fact.issueCategory,
|
|
181
|
+
evidenceRefs: fact.evidenceRefs,
|
|
182
|
+
findings: fact.findings,
|
|
183
|
+
},
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
/**
|
|
187
|
+
* Read the committed frontend design request fact (typed terminal channel).
|
|
188
|
+
* Returns { nodeId, fact } when the design review node committed
|
|
189
|
+
* `request_design_changes` — the design analog of request_review_changes.
|
|
190
|
+
*/
|
|
191
|
+
async function readTypedDesignRequestFact(runDir) {
|
|
192
|
+
const fact = await readCommittedDesignRequestFact({ runDir });
|
|
193
|
+
if (!fact)
|
|
194
|
+
return undefined;
|
|
195
|
+
return {
|
|
196
|
+
nodeId: FRONTEND_DESIGN_REVIEW_NODE_ID,
|
|
197
|
+
fact: {
|
|
198
|
+
issueCategory: fact.issueCategory,
|
|
199
|
+
evidenceRefs: fact.evidenceRefs,
|
|
200
|
+
findings: fact.findings,
|
|
201
|
+
},
|
|
202
|
+
};
|
|
203
|
+
}
|
|
130
204
|
/**
|
|
131
205
|
* Derive only the latest unresolved verify/review feedback from canonical
|
|
132
206
|
* parent-run facts. Provider-only failures intentionally return undefined.
|
|
@@ -144,8 +218,57 @@ export async function deriveDagRerunFeedback(input) {
|
|
|
144
218
|
break;
|
|
145
219
|
}
|
|
146
220
|
}
|
|
147
|
-
if (!sourceNodeId || !sourceNode)
|
|
221
|
+
if (!sourceNodeId || !sourceNode) {
|
|
222
|
+
// Typed terminal channel: the frontend review/design review commit their
|
|
223
|
+
// verdicts as typed facts (request_review_changes / request_design_changes)
|
|
224
|
+
// instead of VERDICT text, and the frontend DAG has no convergence
|
|
225
|
+
// passHistory. Fall back to reading the typed event stores so findings
|
|
226
|
+
// still flow into the next round as unresolved feedback. Prefer review;
|
|
227
|
+
// if absent, fall through to the design review (a design rejection also
|
|
228
|
+
// carries actionable findings the next plan round must consume).
|
|
229
|
+
const typedFact = (await readTypedReviewRequestFact(input.runDir)) ??
|
|
230
|
+
(await readTypedDesignRequestFact(input.runDir));
|
|
231
|
+
if (typedFact) {
|
|
232
|
+
const parentSourceBindingHash = sourceBindingHash(input.spec);
|
|
233
|
+
return withDigest({
|
|
234
|
+
schemaVersion: 1,
|
|
235
|
+
kind: "unresolved-terminal-feedback",
|
|
236
|
+
parentRunId: input.parentRunId,
|
|
237
|
+
taskId: input.taskId,
|
|
238
|
+
sourceNodeId: typedFact.nodeId,
|
|
239
|
+
verdict: "request-revision",
|
|
240
|
+
failureCategory: typedFact.nodeId === FRONTEND_REVIEW_NODE_ID
|
|
241
|
+
? "review-request-changes"
|
|
242
|
+
: "design-request-changes",
|
|
243
|
+
feedbackText: scrubAndBoundFeedbackText(formatTypedReviewRequestFeedback(typedFact.fact), input.runDir, input.state.cwd),
|
|
244
|
+
evidenceRefs: [
|
|
245
|
+
{
|
|
246
|
+
nodeId: typedFact.nodeId,
|
|
247
|
+
preservedNodeRecordPath: `${typedFact.nodeId}.json`,
|
|
248
|
+
},
|
|
249
|
+
],
|
|
250
|
+
attemptSummary: {
|
|
251
|
+
completedPasses: 1,
|
|
252
|
+
maxPasses: 1,
|
|
253
|
+
...(input.state.terminalReason
|
|
254
|
+
? { terminalReason: input.state.terminalReason }
|
|
255
|
+
: {}),
|
|
256
|
+
},
|
|
257
|
+
binding: {
|
|
258
|
+
...(parentSourceBindingHash
|
|
259
|
+
? { sourceCanonicalHash: parentSourceBindingHash }
|
|
260
|
+
: {}),
|
|
261
|
+
...(input.spec.taskContractBinding?.canonicalHash
|
|
262
|
+
? {
|
|
263
|
+
taskContractCanonicalHash: input.spec.taskContractBinding.canonicalHash,
|
|
264
|
+
}
|
|
265
|
+
: {}),
|
|
266
|
+
status: "unknown",
|
|
267
|
+
},
|
|
268
|
+
});
|
|
269
|
+
}
|
|
148
270
|
return undefined;
|
|
271
|
+
}
|
|
149
272
|
const completedPasses = pass?.pass ?? Math.max(1, input.state.convergence?.currentPass ?? 1);
|
|
150
273
|
const maxPasses = Math.max(completedPasses, input.state.convergence?.maxPasses ?? 1);
|
|
151
274
|
const parentSourceBindingHash = sourceBindingHash(input.spec);
|
|
@@ -3,6 +3,7 @@ import { readFile } from "node:fs/promises";
|
|
|
3
3
|
import { assertTaskContractBindingFresh } from "./task-contract-binding.js";
|
|
4
4
|
import { isSafeReadOnlyPiRetryCandidate, isTransportCrashedExclusiveWriter } from "./retry-policy.js";
|
|
5
5
|
import { resolveDagTaskSourcePath } from "../../task/dag-source-paths.js";
|
|
6
|
+
import { composeDagPromptOverridePrompt, MAX_DAG_RERUN_PROMPT_OVERRIDE_CHARS, } from "../../shared/dag-prompt-override.js";
|
|
6
7
|
const REVIEW_VERDICT_RECOVERY_NODE_ID = "review-verdict-recovery-pi";
|
|
7
8
|
const SAFE_SHELL_GATE_KEYS = [
|
|
8
9
|
"verdictGate",
|
|
@@ -159,12 +160,16 @@ function isCanonicalGeneratedVerifyShell(task) {
|
|
|
159
160
|
return false;
|
|
160
161
|
if (shell.projectGovernanceGate)
|
|
161
162
|
return false;
|
|
162
|
-
if (shell.
|
|
163
|
+
if (shell.frontendDesignPolicy)
|
|
164
|
+
return false;
|
|
165
|
+
if (shell.frontendWriterAdmission)
|
|
163
166
|
return false;
|
|
164
167
|
if (shell.frontendVerificationBundle)
|
|
165
168
|
return false;
|
|
166
169
|
if (shell.frontendReviewContext)
|
|
167
170
|
return false;
|
|
171
|
+
if (shell.frontendCloseout)
|
|
172
|
+
return false;
|
|
168
173
|
if (shell.backendTestPipeline)
|
|
169
174
|
return false;
|
|
170
175
|
if (shell.nonZeroExitPolicy === "record")
|
|
@@ -190,12 +195,16 @@ function isSafeGateOnlyShell(task) {
|
|
|
190
195
|
return false;
|
|
191
196
|
if (shell.preset)
|
|
192
197
|
return false;
|
|
193
|
-
if (shell.
|
|
198
|
+
if (shell.frontendDesignPolicy)
|
|
199
|
+
return false;
|
|
200
|
+
if (shell.frontendWriterAdmission)
|
|
194
201
|
return false;
|
|
195
202
|
if (shell.frontendVerificationBundle)
|
|
196
203
|
return false;
|
|
197
204
|
if (shell.frontendReviewContext)
|
|
198
205
|
return false;
|
|
206
|
+
if (shell.frontendCloseout)
|
|
207
|
+
return false;
|
|
199
208
|
if (shell.backendTestPipeline)
|
|
200
209
|
return false;
|
|
201
210
|
if (shell.verifyEvidence)
|
|
@@ -306,6 +315,9 @@ export function computeResetClosure(spec, effectiveFromNodeId) {
|
|
|
306
315
|
...collectReachableDescendants(spec, effectiveFromNodeId),
|
|
307
316
|
].sort();
|
|
308
317
|
}
|
|
318
|
+
function isNeverStartedNodeStatus(status) {
|
|
319
|
+
return status === undefined || status === "SKIPPED" || status === "PENDING";
|
|
320
|
+
}
|
|
309
321
|
export function assessResetClosureSafety(spec, resetNodeIds, options = {}) {
|
|
310
322
|
const tasks = taskById(spec);
|
|
311
323
|
const blockingNodes = [];
|
|
@@ -314,9 +326,17 @@ export function assessResetClosureSafety(spec, resetNodeIds, options = {}) {
|
|
|
314
326
|
const task = tasks.get(nodeId);
|
|
315
327
|
if (!task)
|
|
316
328
|
continue;
|
|
329
|
+
if (options.parentNodes !== undefined &&
|
|
330
|
+
isNeverStartedNodeStatus(options.parentNodes[nodeId]?.status)) {
|
|
331
|
+
continue;
|
|
332
|
+
}
|
|
317
333
|
if (isWriterTask(task, spec)) {
|
|
318
334
|
if (nodeId === options.allowedTransportWriterNodeId)
|
|
319
335
|
continue;
|
|
336
|
+
if (nodeId === options.allowedExclusiveShellFromNodeId &&
|
|
337
|
+
task.executor === "shell") {
|
|
338
|
+
continue;
|
|
339
|
+
}
|
|
320
340
|
blockingNodes.push({
|
|
321
341
|
nodeId,
|
|
322
342
|
reasonCode: "restart-subgraph-contains-writer",
|
|
@@ -370,6 +390,100 @@ export function assessResetClosureSafety(spec, resetNodeIds, options = {}) {
|
|
|
370
390
|
reasonCodes: [...new Set(reasonCodes)],
|
|
371
391
|
};
|
|
372
392
|
}
|
|
393
|
+
/**
|
|
394
|
+
* Experimental prompt-override admission (fail-closed):
|
|
395
|
+
* - requires the internal switch (see shared/dag-prompt-override.ts);
|
|
396
|
+
* - the target must be a failed (ERROR) node inside the reset closure, so an
|
|
397
|
+
* override can only adjust how an already-failed node retries — never a
|
|
398
|
+
* finished, skipped, or not-yet-started node;
|
|
399
|
+
* - only `subtask_prompt` semantics change: "append" keeps the frozen base
|
|
400
|
+
* and adds a marked addendum block, "replace" swaps the whole prompt;
|
|
401
|
+
* base/final sha256 are recorded for provenance and bound into planHash.
|
|
402
|
+
*/
|
|
403
|
+
function assessPromptOverrides(input) {
|
|
404
|
+
const blockingNodes = [];
|
|
405
|
+
const reasonCodes = [];
|
|
406
|
+
const tasks = taskById(input.spec);
|
|
407
|
+
const resetSet = new Set(input.resetNodeIds);
|
|
408
|
+
const seen = new Set();
|
|
409
|
+
const entries = [];
|
|
410
|
+
if (!input.enabled) {
|
|
411
|
+
reasonCodes.push("prompt-override-disabled");
|
|
412
|
+
}
|
|
413
|
+
for (const override of input.overrides) {
|
|
414
|
+
const nodeId = override.nodeId?.trim() ?? "";
|
|
415
|
+
if (seen.has(nodeId)) {
|
|
416
|
+
blockingNodes.push({
|
|
417
|
+
nodeId,
|
|
418
|
+
reasonCode: "prompt-override-duplicate-node",
|
|
419
|
+
});
|
|
420
|
+
reasonCodes.push("prompt-override-duplicate-node");
|
|
421
|
+
continue;
|
|
422
|
+
}
|
|
423
|
+
seen.add(nodeId);
|
|
424
|
+
const task = tasks.get(nodeId);
|
|
425
|
+
if (!task) {
|
|
426
|
+
blockingNodes.push({
|
|
427
|
+
nodeId,
|
|
428
|
+
reasonCode: "prompt-override-node-missing",
|
|
429
|
+
});
|
|
430
|
+
reasonCodes.push("prompt-override-node-missing");
|
|
431
|
+
continue;
|
|
432
|
+
}
|
|
433
|
+
if (!resetSet.has(nodeId)) {
|
|
434
|
+
blockingNodes.push({
|
|
435
|
+
nodeId,
|
|
436
|
+
reasonCode: "prompt-override-outside-reset-closure",
|
|
437
|
+
});
|
|
438
|
+
reasonCodes.push("prompt-override-outside-reset-closure");
|
|
439
|
+
continue;
|
|
440
|
+
}
|
|
441
|
+
if (input.state.nodes[nodeId]?.status !== "ERROR") {
|
|
442
|
+
blockingNodes.push({
|
|
443
|
+
nodeId,
|
|
444
|
+
reasonCode: "prompt-override-node-not-failed",
|
|
445
|
+
});
|
|
446
|
+
reasonCodes.push("prompt-override-node-not-failed");
|
|
447
|
+
continue;
|
|
448
|
+
}
|
|
449
|
+
const mode = override.mode === "append" ? "append" : "replace";
|
|
450
|
+
const prompt = override.prompt ?? "";
|
|
451
|
+
if (!prompt.trim() ||
|
|
452
|
+
prompt.length > MAX_DAG_RERUN_PROMPT_OVERRIDE_CHARS) {
|
|
453
|
+
blockingNodes.push({
|
|
454
|
+
nodeId,
|
|
455
|
+
reasonCode: "prompt-override-invalid-prompt",
|
|
456
|
+
});
|
|
457
|
+
reasonCodes.push("prompt-override-invalid-prompt");
|
|
458
|
+
continue;
|
|
459
|
+
}
|
|
460
|
+
const basePromptSha256 = createHash("sha256")
|
|
461
|
+
.update(task.subtask_prompt, "utf8")
|
|
462
|
+
.digest("hex");
|
|
463
|
+
const finalPrompt = composeDagPromptOverridePrompt({
|
|
464
|
+
basePrompt: task.subtask_prompt,
|
|
465
|
+
text: prompt,
|
|
466
|
+
mode,
|
|
467
|
+
});
|
|
468
|
+
const promptSha256 = createHash("sha256")
|
|
469
|
+
.update(finalPrompt, "utf8")
|
|
470
|
+
.digest("hex");
|
|
471
|
+
if (promptSha256 === basePromptSha256) {
|
|
472
|
+
blockingNodes.push({
|
|
473
|
+
nodeId,
|
|
474
|
+
reasonCode: "prompt-override-unchanged",
|
|
475
|
+
});
|
|
476
|
+
reasonCodes.push("prompt-override-unchanged");
|
|
477
|
+
continue;
|
|
478
|
+
}
|
|
479
|
+
entries.push({ nodeId, mode, basePromptSha256, promptSha256 });
|
|
480
|
+
}
|
|
481
|
+
return {
|
|
482
|
+
entries: entries.length > 0 ? entries : undefined,
|
|
483
|
+
blockingNodes,
|
|
484
|
+
reasonCodes: [...new Set(reasonCodes)],
|
|
485
|
+
};
|
|
486
|
+
}
|
|
373
487
|
export function computePlanHash(planWithoutHash) {
|
|
374
488
|
const canonical = JSON.stringify(sortKeysDeep(planWithoutHash));
|
|
375
489
|
return createHash("sha256").update(canonical, "utf8").digest("hex");
|
|
@@ -594,9 +708,17 @@ export async function evaluateDagRerunPlan(input) {
|
|
|
594
708
|
blockedReasons.push("ambiguous-skipped-rewrite");
|
|
595
709
|
}
|
|
596
710
|
}
|
|
597
|
-
const closureSafety = assessResetClosureSafety(input.parentSpec, resetNodeIds,
|
|
598
|
-
|
|
599
|
-
|
|
711
|
+
const closureSafety = assessResetClosureSafety(input.parentSpec, resetNodeIds, {
|
|
712
|
+
...(transportWriterRestart && effectiveFromNodeId
|
|
713
|
+
? { allowedTransportWriterNodeId: effectiveFromNodeId }
|
|
714
|
+
: {}),
|
|
715
|
+
parentNodes: input.parentState.nodes,
|
|
716
|
+
...(effectiveFromNodeId &&
|
|
717
|
+
effectiveTask?.executor === "shell" &&
|
|
718
|
+
effectiveRecord?.status === "ERROR"
|
|
719
|
+
? { allowedExclusiveShellFromNodeId: effectiveFromNodeId }
|
|
720
|
+
: {}),
|
|
721
|
+
});
|
|
600
722
|
blockingNodes.push(...closureSafety.blockingNodes);
|
|
601
723
|
for (const code of closureSafety.reasonCodes) {
|
|
602
724
|
reasonCodes.push(code);
|
|
@@ -629,6 +751,22 @@ export async function evaluateDagRerunPlan(input) {
|
|
|
629
751
|
blockingNodes.push({ nodeId, reasonCode: "parent-facts-invalid" });
|
|
630
752
|
}
|
|
631
753
|
}
|
|
754
|
+
let promptOverrideEntries;
|
|
755
|
+
if (input.promptOverrides !== undefined && input.promptOverrides.length > 0) {
|
|
756
|
+
const overrideAssessment = assessPromptOverrides({
|
|
757
|
+
spec: input.parentSpec,
|
|
758
|
+
state: input.parentState,
|
|
759
|
+
resetNodeIds,
|
|
760
|
+
overrides: input.promptOverrides,
|
|
761
|
+
enabled: input.promptOverrideEnabled === true,
|
|
762
|
+
});
|
|
763
|
+
blockingNodes.push(...overrideAssessment.blockingNodes);
|
|
764
|
+
for (const code of overrideAssessment.reasonCodes) {
|
|
765
|
+
reasonCodes.push(code);
|
|
766
|
+
blockedReasons.push(code);
|
|
767
|
+
}
|
|
768
|
+
promptOverrideEntries = overrideAssessment.entries;
|
|
769
|
+
}
|
|
632
770
|
const uniqueReasonCodes = [...new Set(reasonCodes)];
|
|
633
771
|
const uniqueBlockedReasons = [...new Set(blockedReasons)];
|
|
634
772
|
const eligible = uniqueBlockedReasons.length === 0 &&
|
|
@@ -672,6 +810,7 @@ export async function evaluateDagRerunPlan(input) {
|
|
|
672
810
|
parentLifecycle: input.parentLifecycle,
|
|
673
811
|
workerManaged: input.workerManaged,
|
|
674
812
|
}),
|
|
813
|
+
...(promptOverrideEntries ? { promptOverrides: promptOverrideEntries } : {}),
|
|
675
814
|
};
|
|
676
815
|
return {
|
|
677
816
|
...planWithoutHash,
|