@tea-agent/loop-agent 0.39.0-beta.0 → 0.39.0-beta.10
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 +248 -95
- 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 +2306 -61
- package/dist/executors/pi-executor.js +7 -0
- 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 +180 -33
- package/dist/executors/shell-executor.js +1249 -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 +62 -4
- 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 +264 -16
- package/dist/worker/console/chat/provider-error.js +98 -0
- package/dist/worker/console/chat/routes.js +298 -32
- 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 +14 -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-BAsjg7ew.js} +1 -1
- package/dist/worker/console/static/assets/{arc-Bj2M8iO5.js → arc-CyIlrwZw.js} +1 -1
- package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-CLlXe4-6.js → architectureDiagram-T3A2C74G-Bo4tobhH.js} +1 -1
- package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-psEp5xH0.js → blockDiagram-VBNYF7ZC-Dfpow9gu.js} +1 -1
- package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-DeKcOCGJ.js → c4Diagram-5PPSVZJV-DPmzG_B4.js} +1 -1
- package/dist/worker/console/static/assets/channel-DSrJAn0-.js +1 -0
- package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-K0C744p1.js → chunk-2GRJ4B5K-pKQpB1UW.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-xbNw4J9-.js → chunk-2Q5K7J3B-5n9YYquZ.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5RXB4S5H-DjLaSDUO.js → chunk-5RXB4S5H-DP3CtA2a.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5VM5RSS4-CtiPlKel.js → chunk-5VM5RSS4-DYxMJPUy.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-DZtolir7.js → chunk-6Q2QTUOP-CS3D50Io.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-GF5L2VYU-CPcdNeew.js → chunk-GF5L2VYU-gSdDfo5_.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-JWPE2WC7-NgHc6V37.js → chunk-JWPE2WC7-A48q0ZNt.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-KBJHAD2P-DQ_T-hg8.js → chunk-KBJHAD2P-ow8Mm3DY.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-RYQCIY6F-DgLsxcVP.js → chunk-RYQCIY6F-CMKfLWYE.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-XXDRQBXY-UrVoM9z1.js → chunk-XXDRQBXY-BiTBdQPX.js} +1 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-DeMmPl0_.js +1 -0
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-DeMmPl0_.js +1 -0
- package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-CLkYvTb3.js → cose-bilkent-JH36ORCC-Cm7exRz_.js} +1 -1
- package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-BwT_xKrE.js → cynefin-VYW2F7L2-D9O5qGOu.js} +1 -1
- package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-Bi9tuzif.js → cynefinDiagram-MW4NZA55-CkvQnako.js} +1 -1
- package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-BCIqkWBV.js → dagre-VZM6K2ZE-B1rzp7CB.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-7IWD3JNH-Dip6l9_Z.js → diagram-7IWD3JNH-BP07Ipcc.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-B-xkh_wK.js → diagram-B4RE2ZJO-BlPzweQb.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-LBJQPF4R-DZO0kTt_.js → diagram-LBJQPF4R-CxPa7n0S.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-Q27KOJAE-D6ZplNbZ.js → diagram-Q27KOJAE-CMTZs53A.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-UB23O5K3-BPekbhoS.js → diagram-UB23O5K3-CxG_X52r.js} +1 -1
- package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-BtmaJpK5.js → ebnfDiagram-BXEA7PRR-D8axtJM0.js} +1 -1
- package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-6DS0Xc44.js → erDiagram-JOGREHBK-DIsGzn7b.js} +1 -1
- package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-CihnROSm.js → flowDiagram-UKHOOZJN-CU9zkDG0.js} +1 -1
- package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-CZC4zOE2.js → ganttDiagram-PKOTCBZU-DCjTmz5P.js} +1 -1
- package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-Dq1fhzGN.js → gitGraphDiagram-DS77QQ5N-BksdmFzg.js} +1 -1
- package/dist/worker/console/static/assets/index-B-O5ChYq.css +1 -0
- package/dist/worker/console/static/assets/index-CfnY8AQx.js +407 -0
- package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-DlSl4BGx.js → infoDiagram-6WML65LV-CGzzzJtL.js} +1 -1
- package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-snnzFl_U.js → ishikawaDiagram-WSZJBQD7-BdseB80N.js} +1 -1
- package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-BDE7qpMx.js → journeyDiagram-NVQOT4AX-DM9gsgha.js} +1 -1
- package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-CD2Ci04G.js → kanban-definition-27J2QSJJ-3q-_9R2b.js} +1 -1
- package/dist/worker/console/static/assets/{linear-DdnapKIH.js → linear-B8N-Mxsd.js} +1 -1
- package/dist/worker/console/static/assets/{mermaid.core-BVjAT9b8.js → mermaid.core-EPIHUUsu.js} +5 -5
- package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-D0yJk-zA.js → mindmap-definition-FAOFIHXS-CxwO12aa.js} +1 -1
- package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-BVo9tFP-.js → pegDiagram-VL7TDLO6-bPqbVX6Y.js} +1 -1
- package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-DnOV-mZ_.js → pieDiagram-7S7Q4E2Y-KzqIvzeX.js} +1 -1
- package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-CzUJo58i.js → quadrantDiagram-CIZ2JOQS-CyofpWiV.js} +1 -1
- package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-Bvikrolh.js → railroadDiagram-AXF67PYL-cTWJvLTP.js} +1 -1
- package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-DtaSVCap.js → requirementDiagram-LRYGKXZP-BK--q27B.js} +1 -1
- package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-C2c9A0wW.js → sankeyDiagram-W5VNT64P-C0SDOHXK.js} +1 -1
- package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-DmXJcU7r.js → sequenceDiagram-SI44F4Z6-Cong6YIo.js} +1 -1
- package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-B4GUFW92.js → sizeCapture-X5ZJPWSS-DUVkgZx8.js} +1 -1
- package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-Dsad2MXf.js → stateDiagram-OKZ733FA-C7XgxuZg.js} +1 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-Dzbnd5oX.js +1 -0
- package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-DGq48Fbi.js → swimlanes-SLNWSIFB-D9Ayvwcy.js} +2 -2
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-BY0hHk6G.js +8 -0
- package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-CSSi6OFf.js → timeline-definition-Z64GVDOM-Byb000cH.js} +1 -1
- package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-BqyzPrWv.js → vennDiagram-T6HMQDX7-DMLegPt4.js} +1 -1
- package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-DlznVb6S.js → wardleyDiagram-T6FBY63Y-ChMMeOsP.js} +1 -1
- package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-CfBcgJ_K.js → xychartDiagram-ELKLHX3M-bEMEPisW.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 +140 -8
- 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/turn-submission.js +16 -3
- package/dist/worker/console/static-src/operator-chat/useChatSessions.js +168 -72
- package/dist/worker/console/static-src/operator-chat/useChatStream.js +102 -21
- package/dist/worker/console/static-src/operator-chat/useChatThread.js +184 -4
- 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 +337 -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 +10 -5
- 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 +1 -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 +534 -178
- package/dist/workflows/dag/frontend-plan-render.js +24 -0
- package/dist/workflows/dag/frontend-prewrite-gate.js +371 -393
- 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 +984 -618
- package/dist/workflows/dag/interrupt-request.js +10 -3
- package/dist/workflows/dag/lifecycle.js +3 -3
- package/dist/workflows/dag/node-execution.js +468 -34
- 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 +321 -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 +45 -1
- 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 +3 -3
- 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
|
@@ -0,0 +1,384 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 生产 Source Fidelity Pi 适配器(AC-PROD-001..006 / AC-001..006)
|
|
3
|
+
*
|
|
4
|
+
* 为真实 `task advance` 的 prepare-contract 路径提供两个独立的 Pi 调用面:
|
|
5
|
+
* - reconcile:仅叙述性/歧义 ledger fragment 的顺序 reconciliation executor
|
|
6
|
+
* (结构化片段由 ledger-reconciliation.ts 的 isStructuredMappableFragment
|
|
7
|
+
* 走代码映射,零 Pi 调用);
|
|
8
|
+
* - review:context-only / duplicate / conflict 的独立 reviewer(与 reconciler
|
|
9
|
+
* 是不同调用阶段,最多一次有界修订,LEDGER_REVIEW_MAX_REVISIONS=1)。
|
|
10
|
+
*
|
|
11
|
+
* 模型队列镜像 semantic-intake.ts(MED→HIGH→LOW harness 档位 + Pi SDK 目录),
|
|
12
|
+
* 单次尝试沿用有界超时(SOURCE_FIDELITY_ATTEMPT_TIMEOUT_MS),工具面仅只读
|
|
13
|
+
* read/grep/find/ls(无写工具)。所有模型输出经 validateOutput + zod 严格校验:
|
|
14
|
+
* 无效 JSON/schema、越界 canonical ID、排除类 disposition 缺理由一律抛
|
|
15
|
+
* LedgerReconciliationError / LedgerReviewError,由 prepare.ts 既有 catch 转为
|
|
16
|
+
* 稳定 SOURCE_FIDELITY_* 阻断码,绝不回退确定性 heuristics。队列空 / 执行失败 /
|
|
17
|
+
* 超时同样 fail closed。
|
|
18
|
+
*/
|
|
19
|
+
import { z } from "zod";
|
|
20
|
+
import { executePiStep } from "../../executors/pi-executor.js";
|
|
21
|
+
import { resolveDagPiModelConfig } from "../../executors/dag-pi-executor.js";
|
|
22
|
+
import { resolveExecutorModelMatrices } from "../../executors/model-routing.js";
|
|
23
|
+
import { listPiSdkAvailableModels } from "../../executors/pi-sdk-executor.js";
|
|
24
|
+
import { loadHarnessManifest } from "../../governance/harness.js";
|
|
25
|
+
import { loadTaskConfig } from "../runtime.js";
|
|
26
|
+
import { buildSemanticIntakeModelQueue, extractJsonObject, } from "./semantic-intake.js";
|
|
27
|
+
import { LEDGER_RECONCILIATION_DEFAULT_BUDGET_CHARS, LedgerReconciliationError, } from "./ledger-reconciliation.js";
|
|
28
|
+
import { LedgerReviewError, } from "./ledger-review.js";
|
|
29
|
+
/** 只读工具面(AC-003):source fidelity Pi 调用无任何写工具。 */
|
|
30
|
+
export const SOURCE_FIDELITY_PI_TOOL_NAMES = [
|
|
31
|
+
"read",
|
|
32
|
+
"grep",
|
|
33
|
+
"find",
|
|
34
|
+
"ls",
|
|
35
|
+
];
|
|
36
|
+
/** 单次 reconciliation/review Pi 尝试的墙钟上限(镜像 SEMANTIC_INTAKE_ATTEMPT_TIMEOUT_MS)。 */
|
|
37
|
+
export const SOURCE_FIDELITY_ATTEMPT_TIMEOUT_MS = 300_000;
|
|
38
|
+
/** 接线侧门 taskKind(AC-PROD-005)。 */
|
|
39
|
+
export const SOURCE_FIDELITY_TASK_KIND = "frontend-implementation";
|
|
40
|
+
const FRAGMENT_DISPOSITIONS = [
|
|
41
|
+
"requirement",
|
|
42
|
+
"acceptance",
|
|
43
|
+
"constraint",
|
|
44
|
+
"non-goal",
|
|
45
|
+
"duplicate",
|
|
46
|
+
"context-only",
|
|
47
|
+
"unresolved",
|
|
48
|
+
];
|
|
49
|
+
const EXCLUSION_DISPOSITIONS = new Set([
|
|
50
|
+
"context-only",
|
|
51
|
+
"duplicate",
|
|
52
|
+
"unresolved",
|
|
53
|
+
]);
|
|
54
|
+
const reconciliationResultSchema = z.object({
|
|
55
|
+
fragmentId: z.string().min(1),
|
|
56
|
+
mappedIds: z.array(z.string().min(1)).default([]),
|
|
57
|
+
disposition: z.enum(FRAGMENT_DISPOSITIONS),
|
|
58
|
+
reason: z.string().min(1).optional(),
|
|
59
|
+
});
|
|
60
|
+
const reviewResponseSchema = z.object({
|
|
61
|
+
fragmentId: z.string().min(1),
|
|
62
|
+
approved: z.boolean(),
|
|
63
|
+
reason: z.string().min(1).optional(),
|
|
64
|
+
revision: z
|
|
65
|
+
.object({
|
|
66
|
+
disposition: z.enum(FRAGMENT_DISPOSITIONS).optional(),
|
|
67
|
+
reason: z.string().min(1).optional(),
|
|
68
|
+
})
|
|
69
|
+
.optional(),
|
|
70
|
+
});
|
|
71
|
+
/**
|
|
72
|
+
* 模型队列构建镜像 semantic-intake.ts:MED→HIGH→LOW(harness executors.pi)+
|
|
73
|
+
* Pi SDK 目录(已认证优先、非 chat 模型过滤、额外模型上限沿用
|
|
74
|
+
* SEMANTIC_INTAKE_MAX_EXTRA_MODELS)。队列空即 fail closed。
|
|
75
|
+
*/
|
|
76
|
+
export function buildSourceFidelityModelQueue(input) {
|
|
77
|
+
return buildSemanticIntakeModelQueue(input);
|
|
78
|
+
}
|
|
79
|
+
async function buildProductionModelQueue(repoRoot, listAvailableModels) {
|
|
80
|
+
const manifest = await loadHarnessManifest(repoRoot);
|
|
81
|
+
const models = resolveExecutorModelMatrices(manifest);
|
|
82
|
+
let available = [];
|
|
83
|
+
try {
|
|
84
|
+
available = listAvailableModels
|
|
85
|
+
? await listAvailableModels()
|
|
86
|
+
: await listPiSdkAvailableModels();
|
|
87
|
+
}
|
|
88
|
+
catch {
|
|
89
|
+
available = [];
|
|
90
|
+
}
|
|
91
|
+
return buildSourceFidelityModelQueue({ harness: models.pi, available });
|
|
92
|
+
}
|
|
93
|
+
function buildReconciliationPrompt(request) {
|
|
94
|
+
return [
|
|
95
|
+
"You are a read-only source-fidelity-reconcile agent for loop-agent task intake.",
|
|
96
|
+
"Map the source fragment to EXISTING canonical requirement ids only.",
|
|
97
|
+
"Return exactly one JSON object (optional json fence); no prose outside it.",
|
|
98
|
+
"Rules:",
|
|
99
|
+
"- mappedIds: canonical requirement ids from the provided list that this fragment directly supports; empty array when none.",
|
|
100
|
+
"- fragmentId: MUST equal the requested fragment id exactly (echo the `fragment id:` line verbatim); identity mismatch is rejected.",
|
|
101
|
+
"- disposition: exactly one of requirement|acceptance|constraint|non-goal|duplicate|context-only|unresolved.",
|
|
102
|
+
"- reason: REQUIRED (non-empty) when disposition is context-only, duplicate or unresolved; optional otherwise.",
|
|
103
|
+
"- Never invent canonical ids; only use ids present in the provided canonical requirements.",
|
|
104
|
+
"- Never invent requirements or human resolutions.",
|
|
105
|
+
JSON.stringify({
|
|
106
|
+
fragmentId: "REQ-SRC-AB12CD34EF56",
|
|
107
|
+
mappedIds: ["AC-001"],
|
|
108
|
+
disposition: "requirement",
|
|
109
|
+
reason: "optional; required for exclusion dispositions",
|
|
110
|
+
}, null, 2),
|
|
111
|
+
`fragment id: ${request.fragment.id}`,
|
|
112
|
+
`fragment path: ${request.fragment.path}`,
|
|
113
|
+
`fragment headingPath: ${request.fragment.headingPath}`,
|
|
114
|
+
`fragment lineRange: ${JSON.stringify(request.fragment.lineRange)}`,
|
|
115
|
+
`budgetChars: ${request.budgetChars}`,
|
|
116
|
+
"fragment text:",
|
|
117
|
+
"```markdown",
|
|
118
|
+
request.fragment.text,
|
|
119
|
+
"```",
|
|
120
|
+
"canonical requirements:",
|
|
121
|
+
JSON.stringify(request.canonicalRequirements, null, 2),
|
|
122
|
+
].join("\n\n");
|
|
123
|
+
}
|
|
124
|
+
function buildReviewPrompt(request) {
|
|
125
|
+
return [
|
|
126
|
+
"You are an independent read-only source-fidelity-review agent for loop-agent task intake.",
|
|
127
|
+
"Review whether the given fragment's context-only / duplicate / conflict disposition is justified.",
|
|
128
|
+
"You are a separate stage from reconciliation: do not re-map fragments; only approve, reject, or propose at most one bounded revision.",
|
|
129
|
+
"Return exactly one JSON object (optional json fence); no prose outside it.",
|
|
130
|
+
"Rules:",
|
|
131
|
+
"- approved: true only when the exclusion/conflict handling is justified.",
|
|
132
|
+
"- fragmentId: MUST equal the requested fragment id exactly (echo the `fragment id:` line verbatim); identity mismatch is rejected.",
|
|
133
|
+
"- reason: recommended; explain the verdict.",
|
|
134
|
+
"- revision: optional single bounded revision {disposition, reason}; never invent requirements or human resolutions.",
|
|
135
|
+
JSON.stringify({
|
|
136
|
+
fragmentId: "REQ-SRC-AB12CD34EF56",
|
|
137
|
+
approved: true,
|
|
138
|
+
reason: "verdict rationale",
|
|
139
|
+
revision: {
|
|
140
|
+
disposition: "duplicate",
|
|
141
|
+
reason: "optional bounded revision",
|
|
142
|
+
},
|
|
143
|
+
}, null, 2),
|
|
144
|
+
`review kind: ${request.kind}`,
|
|
145
|
+
`fragment id: ${request.fragment.id}`,
|
|
146
|
+
`fragment disposition: ${request.fragment.disposition}`,
|
|
147
|
+
...(request.fragment.reason
|
|
148
|
+
? [`fragment reason: ${request.fragment.reason}`]
|
|
149
|
+
: []),
|
|
150
|
+
`fragment risk: ${request.fragment.risk}`,
|
|
151
|
+
`fragment canonicalRequirementIds: ${JSON.stringify(request.fragment.canonicalRequirementIds)}`,
|
|
152
|
+
...(request.fragment.text.trim()
|
|
153
|
+
? [
|
|
154
|
+
"fragment text:",
|
|
155
|
+
"```markdown",
|
|
156
|
+
request.fragment.text,
|
|
157
|
+
"```",
|
|
158
|
+
]
|
|
159
|
+
: [
|
|
160
|
+
"fragment text: (bounded original text unavailable for this record)",
|
|
161
|
+
`fragment id: ${request.fragment.id}`,
|
|
162
|
+
]),
|
|
163
|
+
"canonical requirements:",
|
|
164
|
+
JSON.stringify(request.canonicalRequirements, null, 2),
|
|
165
|
+
...(request.resolutionAvailable
|
|
166
|
+
? ["resolutionAvailable: true (conflict has a valid human resolution)"]
|
|
167
|
+
: []),
|
|
168
|
+
].join("\n\n");
|
|
169
|
+
}
|
|
170
|
+
function validateReconciliationOutput(text, request) {
|
|
171
|
+
let raw;
|
|
172
|
+
try {
|
|
173
|
+
raw = extractJsonObject(text);
|
|
174
|
+
}
|
|
175
|
+
catch (error) {
|
|
176
|
+
throw new LedgerReconciliationError(`reconciliation output is not a JSON object: ${error instanceof Error ? error.message : String(error)}`);
|
|
177
|
+
}
|
|
178
|
+
let parsed;
|
|
179
|
+
try {
|
|
180
|
+
parsed = reconciliationResultSchema.parse(raw);
|
|
181
|
+
}
|
|
182
|
+
catch (error) {
|
|
183
|
+
throw new LedgerReconciliationError(`reconciliation output failed schema validation: ${error instanceof Error ? error.message : String(error)}`);
|
|
184
|
+
}
|
|
185
|
+
// 生产 fragmentId 绑定(AC-005 / AC-HARD-005):缺失/错配即 fail closed,
|
|
186
|
+
// 保证 reviewer/reconciler 决策绑定到实际请求的 source fragment。
|
|
187
|
+
if (parsed.fragmentId !== request.fragmentId) {
|
|
188
|
+
throw new LedgerReconciliationError(`reconciliation output fragmentId ${parsed.fragmentId} does not match requested fragment ${request.fragmentId}`);
|
|
189
|
+
}
|
|
190
|
+
const outOfBounds = parsed.mappedIds.filter((id) => !request.canonicalIds.has(id));
|
|
191
|
+
if (outOfBounds.length > 0) {
|
|
192
|
+
throw new LedgerReconciliationError(`reconciliation output contains out-of-bounds canonical ids: ${outOfBounds.join(", ")}`);
|
|
193
|
+
}
|
|
194
|
+
if (EXCLUSION_DISPOSITIONS.has(parsed.disposition) && !parsed.reason?.trim()) {
|
|
195
|
+
throw new LedgerReconciliationError(`reconciliation output omitted the required exclusion reason for disposition ${parsed.disposition}`);
|
|
196
|
+
}
|
|
197
|
+
return {
|
|
198
|
+
mappedIds: parsed.mappedIds,
|
|
199
|
+
disposition: parsed.disposition,
|
|
200
|
+
...(parsed.reason ? { reason: parsed.reason } : {}),
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
function validateReviewOutput(text, request) {
|
|
204
|
+
let raw;
|
|
205
|
+
try {
|
|
206
|
+
raw = extractJsonObject(text);
|
|
207
|
+
}
|
|
208
|
+
catch (error) {
|
|
209
|
+
throw new LedgerReviewError(`review output is not a JSON object: ${error instanceof Error ? error.message : String(error)}`);
|
|
210
|
+
}
|
|
211
|
+
let parsed;
|
|
212
|
+
try {
|
|
213
|
+
parsed = reviewResponseSchema.parse(raw);
|
|
214
|
+
}
|
|
215
|
+
catch (error) {
|
|
216
|
+
throw new LedgerReviewError(`review output failed schema validation: ${error instanceof Error ? error.message : String(error)}`);
|
|
217
|
+
}
|
|
218
|
+
// 生产 fragmentId 绑定(AC-005 / AC-HARD-005):缺失/错配即 fail closed。
|
|
219
|
+
if (parsed.fragmentId !== request.fragmentId) {
|
|
220
|
+
throw new LedgerReviewError(`review output fragmentId ${parsed.fragmentId} does not match requested fragment ${request.fragmentId}`);
|
|
221
|
+
}
|
|
222
|
+
return {
|
|
223
|
+
approved: parsed.approved,
|
|
224
|
+
...(parsed.reason ? { reason: parsed.reason } : {}),
|
|
225
|
+
...(parsed.revision ? { revision: parsed.revision } : {}),
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* 执行一次生产 reconciliation:模型队列串行尝试(默认真实 executePiStep),
|
|
230
|
+
* 每个成功输出都经 validateOutput + zod 严格校验,全部失败抛
|
|
231
|
+
* LedgerReconciliationError(稳定 SOURCE_FIDELITY_RECONCILIATION_FAILED)。
|
|
232
|
+
*/
|
|
233
|
+
export async function runProductionLedgerReconciliation(request, options) {
|
|
234
|
+
const canonicalIds = new Set(request.canonicalRequirements.map((requirement) => requirement.id));
|
|
235
|
+
const parseAndValidate = (text) => validateReconciliationOutput(text, {
|
|
236
|
+
fragmentId: request.fragment.id,
|
|
237
|
+
canonicalIds,
|
|
238
|
+
});
|
|
239
|
+
const queue = options.modelQueue ??
|
|
240
|
+
(await buildProductionModelQueue(options.repoRoot, options.listAvailableModels));
|
|
241
|
+
if (queue.length === 0) {
|
|
242
|
+
throw new LedgerReconciliationError("source fidelity reconciliation has no MED/HIGH/LOW or Pi-supported models to try (fail closed)");
|
|
243
|
+
}
|
|
244
|
+
const prompt = buildReconciliationPrompt(request);
|
|
245
|
+
const execute = options.executePi ?? executePiStep;
|
|
246
|
+
const timeoutMs = options.timeoutMs ?? SOURCE_FIDELITY_ATTEMPT_TIMEOUT_MS;
|
|
247
|
+
let lastFailure = "";
|
|
248
|
+
for (const modelRef of queue) {
|
|
249
|
+
try {
|
|
250
|
+
const result = await execute({
|
|
251
|
+
attachedFiles: [],
|
|
252
|
+
modelConfig: resolveDagPiModelConfig(modelRef),
|
|
253
|
+
prompt,
|
|
254
|
+
repoRoot: options.repoRoot,
|
|
255
|
+
step: "analyze",
|
|
256
|
+
toolNames: [...SOURCE_FIDELITY_PI_TOOL_NAMES],
|
|
257
|
+
timeoutMs,
|
|
258
|
+
stallTimeoutMs: timeoutMs,
|
|
259
|
+
userMessage: "Reconcile the source fragment into the required JSON only.",
|
|
260
|
+
validateOutput: (text) => {
|
|
261
|
+
try {
|
|
262
|
+
parseAndValidate(text);
|
|
263
|
+
return [];
|
|
264
|
+
}
|
|
265
|
+
catch (error) {
|
|
266
|
+
return [error instanceof Error ? error.message : String(error)];
|
|
267
|
+
}
|
|
268
|
+
},
|
|
269
|
+
});
|
|
270
|
+
if (!result.ok) {
|
|
271
|
+
lastFailure =
|
|
272
|
+
`${result.failureCategory}: ${(result.stderr || result.assistantText).slice(0, 2000)}`;
|
|
273
|
+
continue;
|
|
274
|
+
}
|
|
275
|
+
try {
|
|
276
|
+
return parseAndValidate(result.assistantText);
|
|
277
|
+
}
|
|
278
|
+
catch (error) {
|
|
279
|
+
// 模型返回成功但输出未通过严格校验:视为 invalid-output,尝试下一模型。
|
|
280
|
+
lastFailure =
|
|
281
|
+
`invalid-output: ${error instanceof Error ? error.message : String(error)}`;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
catch (error) {
|
|
285
|
+
lastFailure =
|
|
286
|
+
`unknown: ${error instanceof Error ? error.message : String(error)}`.slice(0, 2000);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
throw new LedgerReconciliationError(`source fidelity reconciliation pi failed after ${queue.length} model(s): ${lastFailure || "no attempt produced valid output"}`);
|
|
290
|
+
}
|
|
291
|
+
/**
|
|
292
|
+
* 执行一次生产独立 review:模型队列串行尝试(默认真实 executePiStep),
|
|
293
|
+
* 输出经 validateOutput + zod 严格校验,全部失败抛 LedgerReviewError
|
|
294
|
+
* (稳定 SOURCE_FIDELITY_RECONCILIATION_FAILED,绝不回退默认 reviewer)。
|
|
295
|
+
*/
|
|
296
|
+
export async function runProductionLedgerReview(request, options) {
|
|
297
|
+
const parseAndValidate = (text) => validateReviewOutput(text, { fragmentId: request.fragment.id });
|
|
298
|
+
const queue = options.modelQueue ??
|
|
299
|
+
(await buildProductionModelQueue(options.repoRoot, options.listAvailableModels));
|
|
300
|
+
if (queue.length === 0) {
|
|
301
|
+
throw new LedgerReviewError("source fidelity review has no MED/HIGH/LOW or Pi-supported models to try (fail closed)");
|
|
302
|
+
}
|
|
303
|
+
const prompt = buildReviewPrompt(request);
|
|
304
|
+
const execute = options.executePi ?? executePiStep;
|
|
305
|
+
const timeoutMs = options.timeoutMs ?? SOURCE_FIDELITY_ATTEMPT_TIMEOUT_MS;
|
|
306
|
+
let lastFailure = "";
|
|
307
|
+
for (const modelRef of queue) {
|
|
308
|
+
try {
|
|
309
|
+
const result = await execute({
|
|
310
|
+
attachedFiles: [],
|
|
311
|
+
modelConfig: resolveDagPiModelConfig(modelRef),
|
|
312
|
+
prompt,
|
|
313
|
+
repoRoot: options.repoRoot,
|
|
314
|
+
step: "analyze",
|
|
315
|
+
toolNames: [...SOURCE_FIDELITY_PI_TOOL_NAMES],
|
|
316
|
+
timeoutMs,
|
|
317
|
+
stallTimeoutMs: timeoutMs,
|
|
318
|
+
userMessage: "Review the fragment disposition into the required JSON only.",
|
|
319
|
+
validateOutput: (text) => {
|
|
320
|
+
try {
|
|
321
|
+
parseAndValidate(text);
|
|
322
|
+
return [];
|
|
323
|
+
}
|
|
324
|
+
catch (error) {
|
|
325
|
+
return [error instanceof Error ? error.message : String(error)];
|
|
326
|
+
}
|
|
327
|
+
},
|
|
328
|
+
});
|
|
329
|
+
if (!result.ok) {
|
|
330
|
+
lastFailure =
|
|
331
|
+
`${result.failureCategory}: ${(result.stderr || result.assistantText).slice(0, 2000)}`;
|
|
332
|
+
continue;
|
|
333
|
+
}
|
|
334
|
+
try {
|
|
335
|
+
return parseAndValidate(result.assistantText);
|
|
336
|
+
}
|
|
337
|
+
catch (error) {
|
|
338
|
+
lastFailure =
|
|
339
|
+
`invalid-output: ${error instanceof Error ? error.message : String(error)}`;
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
catch (error) {
|
|
343
|
+
lastFailure =
|
|
344
|
+
`unknown: ${error instanceof Error ? error.message : String(error)}`.slice(0, 2000);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
throw new LedgerReviewError(`source fidelity review pi failed after ${queue.length} model(s): ${lastFailure || "no attempt produced valid output"}`);
|
|
348
|
+
}
|
|
349
|
+
/**
|
|
350
|
+
* 生产适配器工厂:返回 prepare 可注入的 reconcile executor(budgetChars=4000)
|
|
351
|
+
* 与独立 reviewer 两个不同 Pi 调用面。
|
|
352
|
+
*/
|
|
353
|
+
export function createProductionSourceFidelityPi(options) {
|
|
354
|
+
const reconcileExecutor = (request) => runProductionLedgerReconciliation(request, options);
|
|
355
|
+
const reviewer = (request) => runProductionLedgerReview(request, options);
|
|
356
|
+
return {
|
|
357
|
+
reconcile: {
|
|
358
|
+
executor: reconcileExecutor,
|
|
359
|
+
budgetChars: LEDGER_RECONCILIATION_DEFAULT_BUDGET_CHARS,
|
|
360
|
+
},
|
|
361
|
+
reviewer,
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
/**
|
|
365
|
+
* 接线侧门(AC-PROD-005):taskKind === 'frontend-implementation'(input.taskKind
|
|
366
|
+
* 优先,缺省回退 loadTaskConfig(repoRoot, taskId).taskKind)。ineligible 返回
|
|
367
|
+
* undefined 不接线;配合 prepare.ts 的 parseable requirement/acceptance 前置
|
|
368
|
+
* 双重保证非 eligible 路径零 source-fidelity Pi 调用。
|
|
369
|
+
*/
|
|
370
|
+
export async function resolveProductionSourceFidelityPi(input) {
|
|
371
|
+
let effectiveTaskKind = input.taskKind?.trim();
|
|
372
|
+
if (!effectiveTaskKind) {
|
|
373
|
+
try {
|
|
374
|
+
const config = await loadTaskConfig(input.repoRoot, input.taskId);
|
|
375
|
+
effectiveTaskKind = config.taskKind?.trim();
|
|
376
|
+
}
|
|
377
|
+
catch {
|
|
378
|
+
effectiveTaskKind = undefined;
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
if (effectiveTaskKind !== SOURCE_FIDELITY_TASK_KIND)
|
|
382
|
+
return undefined;
|
|
383
|
+
return createProductionSourceFidelityPi({ repoRoot: input.repoRoot });
|
|
384
|
+
}
|
|
@@ -5,3 +5,25 @@ export const PREPARE_DEFAULT_FORBIDDEN_PATHS = [
|
|
|
5
5
|
".harness/**",
|
|
6
6
|
"node_modules/**",
|
|
7
7
|
];
|
|
8
|
+
// ---------------------------------------------------------------------------
|
|
9
|
+
// Source Fidelity Ledger (20260824-frontend-source-fidelity-ledger-v1)
|
|
10
|
+
// ---------------------------------------------------------------------------
|
|
11
|
+
// 原始 requirement/acceptance 文档保持语义权威;派生 source/需求.md 仅作导航视图。
|
|
12
|
+
// ledger 是二者之间确定性、可审计的映射账本:完整性由可审计账本、来源绑定和
|
|
13
|
+
// writer 前确定性门禁证明,摘要遗漏不再静默通过。
|
|
14
|
+
/** 稳定机器可判读错误码(SOURCE_FIDELITY_* 类目),writer 前门禁/修复路由消费。 */
|
|
15
|
+
export const SOURCE_FIDELITY_ERROR_CODES = [
|
|
16
|
+
"SOURCE_FIDELITY_LEDGER_MISSING",
|
|
17
|
+
"SOURCE_FIDELITY_LEDGER_STALE",
|
|
18
|
+
"SOURCE_FIDELITY_UNRESOLVED",
|
|
19
|
+
"SOURCE_FIDELITY_CONFLICT",
|
|
20
|
+
"SOURCE_FIDELITY_HIGH_RISK_UNCOVERED",
|
|
21
|
+
"SOURCE_FIDELITY_CONTEXT_ONLY_UNREVIEWED",
|
|
22
|
+
"SOURCE_FIDELITY_REQUIREMENT_NOT_COVERED",
|
|
23
|
+
"SOURCE_FIDELITY_SOURCE_REF_INVALID",
|
|
24
|
+
"SOURCE_FIDELITY_FRAGMENT_INVALID",
|
|
25
|
+
"SOURCE_FIDELITY_RECONCILIATION_FAILED",
|
|
26
|
+
];
|
|
27
|
+
export const LEDGER_SCHEMA_VERSION = 1;
|
|
28
|
+
export const LEDGER_SCHEMA_RECONCILER_VERSION = "ledger-reconciler-v1";
|
|
29
|
+
export const REQUIREMENT_LEDGER_FILE_NAME = "requirement-ledger.json";
|
|
@@ -22,6 +22,18 @@ function slugifyName(value) {
|
|
|
22
22
|
.replace(/-{2,}/g, "-");
|
|
23
23
|
return slug || "requirement";
|
|
24
24
|
}
|
|
25
|
+
/** Stable identity of a manifest document for change detection. Timestamps and
|
|
26
|
+
* byte sizes are excluded: identical content re-imported later must not appear
|
|
27
|
+
* "changed" (the canonical hash includes manifest bytes). */
|
|
28
|
+
function documentIdentity(doc) {
|
|
29
|
+
return `${doc.role}|${doc.name}|${doc.materializedPath}|${doc.sha256}`;
|
|
30
|
+
}
|
|
31
|
+
function sameDocumentSet(left, right) {
|
|
32
|
+
if (left.length !== right.length)
|
|
33
|
+
return false;
|
|
34
|
+
const rightIds = new Set(right.map(documentIdentity));
|
|
35
|
+
return left.every((doc) => rightIds.has(documentIdentity(doc)));
|
|
36
|
+
}
|
|
25
37
|
function ensureUniqueReferenceName(existing, baseName, extension) {
|
|
26
38
|
const used = new Set(existing);
|
|
27
39
|
const candidate = `${baseName}${extension}`;
|
|
@@ -149,10 +161,19 @@ export async function importPrdDocument(input) {
|
|
|
149
161
|
const documents = [...(existingManifest?.documents ?? [])].filter((entry) => entry.materializedPath !== materializedRelativePath);
|
|
150
162
|
documents.push(document);
|
|
151
163
|
documents.sort((left, right) => left.materializedPath.localeCompare(right.materializedPath));
|
|
164
|
+
// Keep `updatedAt` stable across re-imports of the same content: the
|
|
165
|
+
// canonical contract hash includes the manifest bytes, so a fresh
|
|
166
|
+
// timestamp on every identical re-import would make the observed
|
|
167
|
+
// canonical hash drift and fail task verification. Only bump it when the
|
|
168
|
+
// document set actually changed (a new/changed materialized file).
|
|
169
|
+
const documentSetChanged = !existingManifest ||
|
|
170
|
+
!sameDocumentSet(existingManifest.documents, documents);
|
|
152
171
|
const manifest = {
|
|
153
172
|
schemaVersion: 1,
|
|
154
173
|
taskId: input.taskId,
|
|
155
|
-
updatedAt:
|
|
174
|
+
updatedAt: documentSetChanged
|
|
175
|
+
? importedAt
|
|
176
|
+
: (existingManifest?.updatedAt ?? importedAt),
|
|
156
177
|
documents,
|
|
157
178
|
};
|
|
158
179
|
await writeSourceManifest(paths.sourceDir, manifest);
|
package/dist/worker/cli.js
CHANGED
|
@@ -838,17 +838,29 @@ function parseOptionalTimeoutMs(raw) {
|
|
|
838
838
|
return value;
|
|
839
839
|
}
|
|
840
840
|
/** Console HTTP entry for bare `agent-worker console` (listen first, then optional browser open). */
|
|
841
|
-
async function runConsoleServe(options) {
|
|
841
|
+
export async function runConsoleServe(options) {
|
|
842
842
|
const { createConsoleServer } = await import("./console/server.js");
|
|
843
843
|
const { maybeOpenConsoleBrowser } = await import("./console/open-browser.js");
|
|
844
|
+
const { readConsoleHandoff, waitForOldConsoleExit } = await import("./console/console-handoff.js");
|
|
844
845
|
try {
|
|
846
|
+
const handoff = readConsoleHandoff(process.env);
|
|
847
|
+
const appDataRoot = options.appDataRoot ?? handoff?.appDataRoot;
|
|
848
|
+
if (handoff) {
|
|
849
|
+
if (path.resolve(handoff.repoRoot) !== path.resolve(options.repoRoot) ||
|
|
850
|
+
handoff.host !== options.host ||
|
|
851
|
+
handoff.port !== options.port) {
|
|
852
|
+
throw new Error("console handoff does not match requested repo/host/port");
|
|
853
|
+
}
|
|
854
|
+
await waitForOldConsoleExit(handoff.oldPid);
|
|
855
|
+
}
|
|
845
856
|
const server = await createConsoleServer({
|
|
846
857
|
repoRoot: options.repoRoot,
|
|
847
858
|
host: options.host,
|
|
848
859
|
port: options.port,
|
|
849
860
|
debug: options.debug === true,
|
|
850
|
-
appDataRoot
|
|
861
|
+
appDataRoot,
|
|
851
862
|
defaultTimeoutMs: parseOptionalTimeoutMs(options.defaultTimeoutMsRaw),
|
|
863
|
+
...(handoff ? { consoleInstanceId: handoff.consoleInstanceId } : {}),
|
|
852
864
|
});
|
|
853
865
|
// Keep stdout bind URL contract (may include 0.0.0.0 / ::); browser URL is normalized separately.
|
|
854
866
|
process.stdout.write(`${server.url}
|
|
@@ -857,6 +869,10 @@ async function runConsoleServe(options) {
|
|
|
857
869
|
noOpen: options.noOpen === true,
|
|
858
870
|
serverUrl: server.url,
|
|
859
871
|
});
|
|
872
|
+
if (options.holdUntilSignal === false) {
|
|
873
|
+
await server.close();
|
|
874
|
+
return;
|
|
875
|
+
}
|
|
860
876
|
await new Promise((resolve) => {
|
|
861
877
|
const shutdown = () => resolve();
|
|
862
878
|
process.once("SIGINT", shutdown);
|
|
@@ -433,6 +433,20 @@ function safeSessionId(sessionId) {
|
|
|
433
433
|
* Link durable Chat refs to the existing canonical operation store/event ring.
|
|
434
434
|
* This owns no operation state and schedules no work; it only projects facts.
|
|
435
435
|
*/
|
|
436
|
+
/** R8 AC-R8-001: operationIds whose operation-ref has been overshadowed by
|
|
437
|
+
* a later operation-unlinked tombstone in the same ring. A tombstoned
|
|
438
|
+
* association must never be restored by recoverSession / replay / fork. */
|
|
439
|
+
function tombstonedOperationIds(events) {
|
|
440
|
+
const tombstones = new Set();
|
|
441
|
+
for (const event of events) {
|
|
442
|
+
if (event.kind !== "operation-unlinked")
|
|
443
|
+
continue;
|
|
444
|
+
const operationId = event.data.operationId;
|
|
445
|
+
if (typeof operationId === "string" && operationId)
|
|
446
|
+
tombstones.add(operationId);
|
|
447
|
+
}
|
|
448
|
+
return tombstones;
|
|
449
|
+
}
|
|
436
450
|
export function createChatOperationLinker(options) {
|
|
437
451
|
const followers = new Map();
|
|
438
452
|
const keyFor = (sessionId, operationId) => `${sessionId}\u0000${operationId}`;
|
|
@@ -535,14 +549,37 @@ export function createChatOperationLinker(options) {
|
|
|
535
549
|
}
|
|
536
550
|
return {
|
|
537
551
|
link,
|
|
552
|
+
async unlink(input) {
|
|
553
|
+
// Idempotent removal (AC-R7-001): drop the live follower and the
|
|
554
|
+
// persisted operation-ref event so the association is truly gone.
|
|
555
|
+
const key = keyFor(input.sessionId, input.operationId);
|
|
556
|
+
followers.get(key)?.();
|
|
557
|
+
followers.delete(key);
|
|
558
|
+
const ring = options.chatEvents.snapshot(input.sessionId);
|
|
559
|
+
const refs = ring.filter((event) => event.kind === "operation-ref" &&
|
|
560
|
+
event.data.operationId ===
|
|
561
|
+
input.operationId);
|
|
562
|
+
for (const event of refs) {
|
|
563
|
+
options.chatEvents.append(input.sessionId, event.turnId, {
|
|
564
|
+
kind: "operation-unlinked",
|
|
565
|
+
data: { operationId: input.operationId, unlinkedAt: new Date().toISOString() },
|
|
566
|
+
});
|
|
567
|
+
}
|
|
568
|
+
return refs.length > 0;
|
|
569
|
+
},
|
|
538
570
|
async recoverSession(sessionId) {
|
|
539
|
-
const
|
|
540
|
-
|
|
541
|
-
|
|
571
|
+
const snapshot = options.chatEvents.snapshot(sessionId);
|
|
572
|
+
// R8 AC-R8-001: a tombstoned (unlinked) operationId must NOT be
|
|
573
|
+
// re-linked on recovery — the persisted operation-unlinked event
|
|
574
|
+
// overshadows any earlier operation-ref in the same ring.
|
|
575
|
+
const tombstones = tombstonedOperationIds(snapshot);
|
|
576
|
+
const refs = snapshot.filter((event) => event.kind === "operation-ref");
|
|
542
577
|
for (const event of refs) {
|
|
543
578
|
const data = event.data;
|
|
544
579
|
if (!data.operationId || !data.toolCallId)
|
|
545
580
|
continue;
|
|
581
|
+
if (tombstones.has(data.operationId))
|
|
582
|
+
continue;
|
|
546
583
|
await link({
|
|
547
584
|
sessionId,
|
|
548
585
|
turnId: event.turnId,
|
|
@@ -566,6 +603,19 @@ export function createChatOperationLinker(options) {
|
|
|
566
603
|
},
|
|
567
604
|
};
|
|
568
605
|
}
|
|
606
|
+
/** R8 AC-R8-001 (fork): same tombstone scan as the linker — kept local to
|
|
607
|
+
* the store so forkContextRefs needs no linker dependency. */
|
|
608
|
+
function forkTombstonedOperationIds(events) {
|
|
609
|
+
const tombstones = new Set();
|
|
610
|
+
for (const event of events) {
|
|
611
|
+
if (event.kind !== "operation-unlinked")
|
|
612
|
+
continue;
|
|
613
|
+
const operationId = event.data.operationId;
|
|
614
|
+
if (typeof operationId === "string" && operationId)
|
|
615
|
+
tombstones.add(operationId);
|
|
616
|
+
}
|
|
617
|
+
return tombstones;
|
|
618
|
+
}
|
|
569
619
|
export function createChatEventStore(options) {
|
|
570
620
|
const maxEvents = options?.maxEventsPerSession ?? DEFAULT_MAX_EVENTS;
|
|
571
621
|
const maxBytes = options?.maxBytesPerSession ?? DEFAULT_MAX_BYTES;
|
|
@@ -739,9 +789,17 @@ export function createChatEventStore(options) {
|
|
|
739
789
|
"artifact-ref",
|
|
740
790
|
]);
|
|
741
791
|
let copiedCount = 0;
|
|
742
|
-
|
|
792
|
+
const source = this.snapshot(input.sourceSessionId);
|
|
793
|
+
// R8 AC-R8-001: fork excludes tombstoned associations — an unlinked
|
|
794
|
+
// operationId must not leak its operation-ref into a forked session.
|
|
795
|
+
const tombstones = forkTombstonedOperationIds(source);
|
|
796
|
+
for (const event of source) {
|
|
743
797
|
if (!allowed.has(event.kind))
|
|
744
798
|
continue;
|
|
799
|
+
if ((event.kind === "operation-ref" ||
|
|
800
|
+
event.kind === "operation-status") &&
|
|
801
|
+
tombstones.has(event.data.operationId ?? ""))
|
|
802
|
+
continue;
|
|
745
803
|
const data = structuredClone(event.data);
|
|
746
804
|
if (event.kind === "human-gate-card") {
|
|
747
805
|
delete data.humanGateToken;
|
|
@@ -38,6 +38,13 @@ export function projectTaskContext(input) {
|
|
|
38
38
|
const previous = gates.get(card.cardId);
|
|
39
39
|
gates.set(card.cardId, safeGate({ ...card, ...(previous?.dagSpine && !card.dagSpine ? { dagSpine: previous.dagSpine } : {}) }));
|
|
40
40
|
}
|
|
41
|
+
else if (event.kind === "operation-unlinked") {
|
|
42
|
+
// R8 AC-001/AC-R8-001: tombstone — an operation-unlinked event appended
|
|
43
|
+
// after its operation-ref removes the card from the projection for good.
|
|
44
|
+
const operationId = typeof data.operationId === "string" ? data.operationId : "";
|
|
45
|
+
if (operationId)
|
|
46
|
+
view.operations = view.operations.filter((operation) => operation.operationId !== operationId);
|
|
47
|
+
}
|
|
41
48
|
else if (event.kind === "operation-ref" || event.kind === "operation-status") {
|
|
42
49
|
const operation = operationFromEventPayload(data);
|
|
43
50
|
if (operation && (!activeTaskId || !operation.taskId || operation.taskId === activeTaskId))
|
|
@@ -81,11 +81,23 @@ export function projectDeferredTurnSummary(deferredTurn) {
|
|
|
81
81
|
blockedByTurnId: deferredTurn.blockedByTurnId,
|
|
82
82
|
state: deferredTurn.state,
|
|
83
83
|
updatedAt: deferredTurn.updatedAt,
|
|
84
|
+
text: deferredTurn.text,
|
|
84
85
|
textPreview: deferredTurn.text.replace(/\s+/g, " ").trim().slice(0, 120),
|
|
85
86
|
imageCount: deferredTurn.images.length,
|
|
87
|
+
images: structuredClone(deferredTurn.images),
|
|
86
88
|
...(deferredTurn.acceptedTurnId
|
|
87
89
|
? { acceptedTurnId: deferredTurn.acceptedTurnId }
|
|
88
90
|
: {}),
|
|
89
91
|
...(deferredTurn.lastError ? { lastError: deferredTurn.lastError } : {}),
|
|
90
92
|
};
|
|
91
93
|
}
|
|
94
|
+
export function parseDeferredTurns(value) {
|
|
95
|
+
if (!Array.isArray(value))
|
|
96
|
+
return [];
|
|
97
|
+
return value
|
|
98
|
+
.map((item) => parseDeferredTurn(item))
|
|
99
|
+
.filter((item) => item !== undefined);
|
|
100
|
+
}
|
|
101
|
+
export function projectDeferredTurnSummaries(deferredTurns) {
|
|
102
|
+
return deferredTurns.map((item) => projectDeferredTurnSummary(item));
|
|
103
|
+
}
|
|
@@ -64,7 +64,14 @@ function safeRunSummary(value) {
|
|
|
64
64
|
const status = String(node.status ?? "");
|
|
65
65
|
if (!id || !status)
|
|
66
66
|
return [];
|
|
67
|
-
|
|
67
|
+
const failureCategory = String(node.failureCategory ?? "");
|
|
68
|
+
return [
|
|
69
|
+
{
|
|
70
|
+
id,
|
|
71
|
+
status,
|
|
72
|
+
...(failureCategory ? { failureCategory } : {}),
|
|
73
|
+
},
|
|
74
|
+
];
|
|
68
75
|
})
|
|
69
76
|
.slice(0, 20);
|
|
70
77
|
}
|