@tea-agent/loop-agent 0.41.1 → 0.42.0-next.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 +1 -1
- package/CHANGELOG.md +397 -5
- package/dist/adapters/context-transfer/optional-pi-handoff.js +31 -0
- package/dist/adapters/context-transfer/pi-session.js +61 -0
- package/dist/application/dag/generate-task-dag.js +75 -19
- package/dist/application/dag/run-dag.js +41 -0
- package/dist/application/task-lifecycle/advance.js +24 -5
- package/dist/application/task-lifecycle/observe.js +171 -17
- package/dist/application/task-lifecycle/plan-transitions.js +42 -7
- package/dist/application/task-lifecycle/recommendations.js +13 -2
- package/dist/build-stamp.json +3 -3
- package/dist/cli/command-definitions.js +19 -0
- package/dist/cli/program.js +29 -2
- package/dist/commands/client-recovery.js +3 -0
- package/dist/commands/dag-artifact.js +284 -0
- package/dist/commands/dag-context.js +184 -0
- package/dist/commands/dag-follow-up.js +138 -0
- package/dist/commands/dag-rerun-task.js +2 -0
- package/dist/commands/dag-rerun.js +206 -1
- package/dist/commands/init.js +27 -1
- package/dist/commands/task-advance.js +19 -0
- package/dist/executors/dag-pi-executor.js +4013 -86
- package/dist/executors/pi-executor.js +15 -4
- package/dist/executors/pi-read-budget-policy.js +239 -0
- package/dist/executors/pi-sdk-executor.js +3 -0
- package/dist/executors/shell-executor.js +978 -188
- package/dist/executors/shell-write-guard.js +7 -0
- package/dist/{worker → infrastructure}/console/app-data.js +4 -0
- package/dist/infrastructure/console/artifact-revision-store.js +430 -0
- package/dist/infrastructure/console/context-export-store.js +160 -0
- package/dist/infrastructure/console/dir-lock.js +132 -0
- package/dist/infrastructure/console/operation-store.js +417 -0
- package/dist/infrastructure/harness/artifact-store.js +10 -1
- package/dist/infrastructure/harness/atomic-write.js +12 -2
- package/dist/shared/context-transfer/artifact-revision.js +172 -0
- package/dist/shared/context-transfer.js +418 -0
- package/dist/shared/dag-failure-category.js +12 -0
- package/dist/shared/openspec-spec.js +70 -4
- package/dist/shared/operator/capabilities.js +21 -0
- package/dist/shared/operator/safe-run-summary.js +1 -0
- package/dist/shared/path-safety.js +93 -0
- package/dist/shared/preview.js +28 -4
- package/dist/task/config-types.js +107 -6
- package/dist/task/contract/adopt.js +4 -0
- package/dist/task/contract/import-revision.js +4 -0
- package/dist/task/contract/project.js +3 -0
- package/dist/task/contract/schema.js +2 -1
- package/dist/task/frontend-project-capability.js +203 -20
- package/dist/task/runtime.js +5 -2
- package/dist/task/source-prepare/build-draft.js +3 -3
- package/dist/task/source-prepare/fragment-inventory.js +64 -25
- package/dist/task/source-prepare/prepare.js +126 -21
- package/dist/task/source-prepare/semantic-intake.js +6 -2
- package/dist/task/source-references.js +22 -1
- package/dist/task/task-demand-routing.js +3 -0
- package/dist/worker/console/chat/chat-event-store.js +2 -2
- package/dist/worker/console/chat/model-resolver.js +7 -4
- package/dist/worker/console/chat/pi-runtime.js +86 -5
- package/dist/worker/console/chat/resource-preferences-store.js +1 -1
- package/dist/worker/console/chat/routes.js +136 -53
- package/dist/worker/console/chat/sdd-data-alignment.js +222 -0
- package/dist/worker/console/chat/session-catalog.js +2 -0
- package/dist/worker/console/chat/session-store.js +43 -1
- package/dist/worker/console/chat/shortcuts.js +18 -2
- package/dist/worker/console/chat/user-questions.js +1 -1
- package/dist/worker/console/console-update-runtime.js +1 -1
- package/dist/worker/console/context-transfer-diagnostics.js +198 -0
- package/dist/worker/console/dag-confirmation.js +1 -1
- package/dist/worker/console/dag-execution-receipt.js +1 -1
- package/dist/worker/console/doctor.js +1 -1
- package/dist/worker/console/draft-store.js +1 -1
- package/dist/worker/console/frontend-human-decision-adapter.js +19 -0
- package/dist/worker/console/frontend-split-operation-adapter.js +20 -0
- package/dist/worker/console/human-gate-token.js +1 -1
- package/dist/worker/console/index.js +6 -3
- package/dist/worker/console/interview/assessment.js +1 -1
- package/dist/worker/console/interview/session.js +1 -1
- package/dist/worker/console/operation-runner.js +45 -4
- package/dist/worker/console/operation-sse.js +1 -1
- package/dist/worker/console/operation-wait.js +1 -1
- package/dist/worker/console/operator-actions.js +273 -10
- package/dist/worker/console/operator-surface-health.js +1 -1
- package/dist/worker/console/operator-user-error.js +169 -0
- package/dist/worker/console/pi-plugins.js +57 -0
- package/dist/worker/console/pi-readiness.js +3 -2
- package/dist/worker/console/routes.js +637 -8
- package/dist/worker/console/security.js +35 -0
- package/dist/worker/console/server.js +29 -4
- package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-D7oU983t.js → abnfDiagram-N423BO3Z-CcS17TBr.js} +1 -1
- package/dist/worker/console/static/assets/{arc-BrKSRdFS.js → arc-COptKq2S.js} +1 -1
- package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-CiXV7L56.js → architectureDiagram-T3A2C74G-h4LKMHjP.js} +1 -1
- package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-D_Hs_gIG.js → blockDiagram-VBNYF7ZC-COA1MOH0.js} +1 -1
- package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-DbM-Lqqh.js → c4Diagram-5PPSVZJV-DJUf0QPm.js} +1 -1
- package/dist/worker/console/static/assets/channel-DdBCaOJ6.js +1 -0
- package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-D1M8uqJw.js → chunk-2GRJ4B5K-mtWfKrUX.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-4D3RFHT6.js → chunk-2Q5K7J3B-B8pXsxDQ.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5RXB4S5H-CdwpY_n9.js → chunk-5RXB4S5H-ipKzByl1.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5VM5RSS4-BLeLZ9PG.js → chunk-5VM5RSS4-DYi3Ald_.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-CeRNEMvc.js → chunk-6Q2QTUOP-DQtGYoty.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-GF5L2VYU-CjkHP8Vy.js → chunk-GF5L2VYU-BU0qS2YV.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-JWPE2WC7-B54ZUQY-.js → chunk-JWPE2WC7-DVH9dIGE.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-KBJHAD2P-4bKaMVmx.js → chunk-KBJHAD2P-Dt60SmgM.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-RYQCIY6F-Cyjfznu_.js → chunk-RYQCIY6F-BoaUGuEY.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-XXDRQBXY-CDN-zGFV.js → chunk-XXDRQBXY-_WHFDTp2.js} +1 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-DZFra1GO.js +1 -0
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-DZFra1GO.js +1 -0
- package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-HxJmz411.js → cose-bilkent-JH36ORCC-BxkbTIRd.js} +1 -1
- package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-DtSt5K2w.js → cynefin-VYW2F7L2-Bf2UVnoG.js} +1 -1
- package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-VtkSpRmG.js → cynefinDiagram-MW4NZA55-Bo23q1J_.js} +1 -1
- package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-Bj4DT4du.js → dagre-VZM6K2ZE-Dd_UU49i.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-7IWD3JNH-Bpuk6BmP.js → diagram-7IWD3JNH-ebUa1a9y.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-BHYGv_yO.js → diagram-B4RE2ZJO-BucthU8r.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-LBJQPF4R-DEx3WwnT.js → diagram-LBJQPF4R-BgqNpAkm.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-Q27KOJAE-Cj0dS5e8.js → diagram-Q27KOJAE-DC4q5NGa.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-UB23O5K3-B97h8l21.js → diagram-UB23O5K3-CBqCaMeb.js} +1 -1
- package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-DMSFAhBd.js → ebnfDiagram-BXEA7PRR-CGtnbQ3-.js} +1 -1
- package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-DhVOCbt6.js → erDiagram-JOGREHBK-CG3LUao5.js} +1 -1
- package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-GlakITWL.js → flowDiagram-UKHOOZJN-D2ZVgoFS.js} +1 -1
- package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-VxPpu9eQ.js → ganttDiagram-PKOTCBZU-DoYFZiKt.js} +1 -1
- package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-Bs_e5DRT.js → gitGraphDiagram-DS77QQ5N-CKoP1s6j.js} +1 -1
- package/dist/worker/console/static/assets/index-CAZ2fC_X.css +1 -0
- package/dist/worker/console/static/assets/index-vbTcFnFs.js +449 -0
- package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-BXYuU4Uw.js → infoDiagram-6WML65LV-Duofv8p2.js} +1 -1
- package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-C34erCbU.js → ishikawaDiagram-WSZJBQD7-D2nlCkA1.js} +1 -1
- package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-DZCAiOU3.js → journeyDiagram-NVQOT4AX-Dd4IHdus.js} +1 -1
- package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-C9gaTdUm.js → kanban-definition-27J2QSJJ-Bi20AOdt.js} +1 -1
- package/dist/worker/console/static/assets/{linear-CvI0Z17U.js → linear-D59YJ9kB.js} +1 -1
- package/dist/worker/console/static/assets/{mermaid.core-DMMSIx7j.js → mermaid.core-Bl13LpNa.js} +5 -5
- package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-BKKP0EO6.js → mindmap-definition-FAOFIHXS-C6DLP5eY.js} +1 -1
- package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-C3LiFbl5.js → pegDiagram-VL7TDLO6-BjO4cy64.js} +1 -1
- package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-Bb3Om2Dz.js → pieDiagram-7S7Q4E2Y-D7i2qZTG.js} +1 -1
- package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-DNthMjzc.js → quadrantDiagram-CIZ2JOQS-Dx8o_h0y.js} +1 -1
- package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-miFsr4Tv.js → railroadDiagram-AXF67PYL-B55orI_t.js} +1 -1
- package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-yXUncmn6.js → requirementDiagram-LRYGKXZP-zWaehp6f.js} +1 -1
- package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-DLeWKLak.js → sankeyDiagram-W5VNT64P-CcA-pjvD.js} +1 -1
- package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-CI3he1R3.js → sequenceDiagram-SI44F4Z6-BOFbzNHI.js} +1 -1
- package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-B7jfDkS5.js → sizeCapture-X5ZJPWSS-BjAejah1.js} +1 -1
- package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-BmrZod7J.js → stateDiagram-OKZ733FA-DXYwxJwZ.js} +1 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-Clg3V9t1.js +1 -0
- package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-CDOlHVSY.js → swimlanes-SLNWSIFB-OYiOch8n.js} +2 -2
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-DX1dxAAW.js +8 -0
- package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-Cz-XX1gy.js → timeline-definition-Z64GVDOM-cZfH3nmU.js} +1 -1
- package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-caV4cI8C.js → vennDiagram-T6HMQDX7-BDE7b3E1.js} +1 -1
- package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-dImPlnIO.js → wardleyDiagram-T6FBY63Y-kyyGy9WJ.js} +1 -1
- package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-CtRYVJcn.js → xychartDiagram-ELKLHX3M-CJj6VTog.js} +1 -1
- package/dist/worker/console/static/index.html +7 -2
- package/dist/worker/console/static-src/active-run-badge.js +17 -0
- package/dist/worker/console/static-src/app/useOperatorActions.js +3 -2
- package/dist/worker/console/static-src/app/useRecoveryConsole.js +5 -5
- package/dist/worker/console/static-src/operator-chat/input-history.js +8 -6
- package/dist/worker/console/static-src/operator-chat/runtime-snapshot-store.js +10 -0
- package/dist/worker/console/static-src/operator-chat/useChatSessions.js +258 -23
- package/dist/worker/console/static-src/operator-chat/useChatThread.js +112 -4
- package/dist/worker/console/static-src/operator-chat/useComposer.js +13 -4
- package/dist/worker/console/static-src/operator-chat/useRuntimeControls.js +29 -6
- package/dist/worker/console/static-src/operator-chat/useRuntimeSnapshot.js +8 -2
- package/dist/worker/console/static-src/shell/console-update-reload.js +52 -0
- package/dist/worker/console/static-src/shell/workspace-route.js +11 -0
- package/dist/worker/console/workspace-context.js +142 -2
- package/dist/worker/console/workspace-registry.js +2 -2
- package/dist/worker/continuation/worker-continuation.js +278 -0
- package/dist/worker/materialize/frontend-split-task-materializer.js +72 -0
- package/dist/worker/observe/health.js +1 -1
- package/dist/worker/observe/node-transparency.js +572 -0
- package/dist/worker/observe/routes.js +55 -1
- package/dist/worker/observe/static/api.js +69 -5
- package/dist/worker/observe/static/dag-context-reason-labels.d.ts +9 -0
- package/dist/worker/observe/static/dag-context-reason-labels.js +120 -0
- package/dist/worker/observe/static/dag-helpers.js +4 -2
- package/dist/worker/observe/static/dag-node-purpose.js +5 -0
- package/dist/worker/observe/static/inspect-workspace.js +23 -0
- package/dist/worker/observe/static/kpi.js +2 -2
- package/dist/worker/observe/static/operator-chrome.d.ts +10 -2
- package/dist/worker/observe/static/operator-chrome.js +37 -23
- package/dist/worker/observe/static/relations.js +2 -2
- package/dist/worker/observe/static/router.d.ts +12 -1
- package/dist/worker/observe/static/router.js +48 -6
- package/dist/worker/observe/static/run-processing.js +4 -2
- package/dist/worker/observe/static/shell-chrome.js +2 -2
- package/dist/worker/observe/static/state.js +8 -1
- package/dist/worker/observe/static/styles.css +1092 -86
- package/dist/worker/observe/static/views/dag-inspector.js +1228 -64
- package/dist/worker/observe/static/views/dag.js +10 -2
- package/dist/worker/observe/static/views/dags.js +3 -1
- package/dist/worker/observe/static/views/dashboard.js +14 -6
- package/dist/worker/observe/static/views/pool.js +7 -2
- package/dist/worker/observe/static/views/run.js +12 -3
- package/dist/worker/observe/static/views/session-timeline.js +142 -18
- package/dist/worker/observe/static/views/task.js +7 -2
- package/dist/worker/outcomes/adapters.js +19 -0
- package/dist/worker/outcomes/types.js +1 -0
- package/dist/worker/pool/attempt-lease.js +97 -66
- package/dist/worker/pool/begin-attempt-with-lease.js +1 -0
- package/dist/worker/pool/reconcile.js +46 -1
- package/dist/worker/pool/run-store.js +2 -0
- package/dist/worker/pool/state-projection.js +2 -0
- package/dist/worker/task-spec/workflow-routing.js +8 -3
- package/dist/workflows/dag/artifact-bindings.js +149 -0
- package/dist/workflows/dag/artifact-revision-schema-registry.js +31 -0
- package/dist/workflows/dag/backend-test-case-coverage-analysis.js +215 -28
- package/dist/workflows/dag/backend-test-markdown-workflow.js +13 -1
- package/dist/workflows/dag/backend-test-plan-protocol.js +106 -0
- package/dist/workflows/dag/backend-test-scenario-param.js +172 -25
- package/dist/workflows/dag/backend-test-scenario-partitions.js +62 -1
- package/dist/workflows/dag/backend-test-writer-completeness.js +104 -12
- package/dist/workflows/dag/budget-enforcement.js +10 -0
- package/dist/workflows/dag/context-receipt.js +305 -0
- package/dist/workflows/dag/context-transfer/context-bundle.js +423 -0
- package/dist/workflows/dag/context-transfer/operator-actions.js +61 -0
- package/dist/workflows/dag/context-transfer/renderers.js +93 -0
- package/dist/workflows/dag/contract-validator-registrations.js +1 -2
- package/dist/workflows/dag/dag-retry-schema.js +138 -0
- 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 +1237 -192
- package/dist/workflows/dag/frontend-plan-render.js +2 -1
- package/dist/workflows/dag/frontend-prewrite-gate.js +256 -350
- 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 +226 -0
- package/dist/workflows/dag/frontend-recovery-lineage.js +178 -0
- package/dist/workflows/dag/frontend-recovery-plan.js +21 -10
- package/dist/workflows/dag/frontend-recovery-run.js +166 -34
- package/dist/workflows/dag/frontend-repair.js +1 -432
- package/dist/workflows/dag/frontend-review-context.js +261 -15
- package/dist/workflows/dag/frontend-review-findings.js +270 -0
- package/dist/workflows/dag/frontend-shadow-dual-write.js +941 -0
- package/dist/workflows/dag/frontend-shape-capsule-store.js +191 -0
- package/dist/workflows/dag/frontend-shape-facts.js +419 -0
- package/dist/workflows/dag/frontend-shape.js +435 -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-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 +252 -25
- package/dist/workflows/dag/frontend-worktree-diff.js +250 -17
- package/dist/workflows/dag/frontend-writer-admission.js +319 -0
- package/dist/workflows/dag/frontend-writer-rollback.js +32 -0
- package/dist/workflows/dag/frontend-writer-status.js +256 -0
- package/dist/workflows/dag/init-hybrid.js +1174 -562
- package/dist/workflows/dag/interrupt-request.js +7 -0
- package/dist/workflows/dag/node-execution.js +940 -31
- package/dist/workflows/dag/path-safety.js +1 -0
- package/dist/workflows/dag/prompt.js +99 -7
- package/dist/workflows/dag/recovery-lease.js +170 -0
- package/dist/workflows/dag/report.js +37 -1
- package/dist/workflows/dag/rerun-feedback.js +315 -1
- package/dist/workflows/dag/rerun-plan.js +568 -3
- package/dist/workflows/dag/rerun-run.js +642 -18
- package/dist/workflows/dag/rerun-task.js +251 -13
- package/dist/workflows/dag/retry-policy.js +219 -104
- package/dist/workflows/dag/runner.js +596 -121
- package/dist/workflows/dag/scheduler.js +133 -20
- package/dist/workflows/dag/skill-snapshot.js +17 -0
- package/dist/workflows/dag/types.js +426 -22
- package/dist/workflows/dag/validate.js +28 -12
- package/docs/architecture/runtime-boundaries.md +6 -6
- package/docs/architecture/worker-and-feature.md +1 -1
- package/docs/examples/README.md +5 -0
- package/docs/init-surface.manifest.json +30 -12
- package/docs/skills/vetted-skill-registry.md +4 -2
- package/docs/templates/README.md +2 -0
- package/docs/templates/agent-dag-report.schema.json +8 -2
- package/docs/templates/agent-dag.schema.json +33 -1
- package/docs/templates/backend-test-dag.json +23 -20
- package/docs/templates/frontend-implementation-contract.schema.json +4 -1
- package/docs/templates/frontend-implementation-dag.json +89 -0
- package/docs/templates/product-line/task.yaml +1 -1
- package/docs/templates/spec-registry.schema.json +45 -0
- package/harness.json +1 -1
- package/package.json +6 -3
- package/skills/frontend-bounded-implement/SKILL.md +15 -14
- package/skills/frontend-bounded-implement/references/code-standards.md +19 -0
- package/skills/frontend-contract/SKILL.md +23 -0
- package/skills/frontend-contract/references/contract-protocol.md +34 -0
- package/skills/frontend-design-review/SKILL.md +22 -41
- package/skills/frontend-plan/SKILL.md +26 -0
- package/skills/frontend-plan/references/decision-contract.md +37 -0
- package/skills/frontend-plan/references/design-decisions.md +17 -0
- package/skills/frontend-review/SKILL.md +20 -15
- package/skills/frontend-review/references/review-findings.md +6 -7
- package/skills/frontend-scout/SKILL.md +25 -0
- package/skills/frontend-scout/references/design-evidence.md +16 -0
- package/skills/frontend-scout/references/scout-evidence.md +23 -0
- package/skills/frontend-verification/SKILL.md +1 -1
- package/skills/loop-agent/references/command-reference.md +7 -0
- package/skills/loop-agent/references/hybrid-dag.md +2 -2
- package/dist/worker/console/operation-store.js +0 -169
- package/dist/worker/console/static/assets/channel-D1xajGpe.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-BmuGTfRr.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-BmuGTfRr.js +0 -1
- package/dist/worker/console/static/assets/index-BLzcAykx.js +0 -451
- package/dist/worker/console/static/assets/index-Cs9JUSPs.css +0 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-D9ykKEWP.js +0 -1
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-BvPOXALT.js +0 -8
- package/skills/frontend-implementation/SKILL.md +0 -52
- package/skills/frontend-implementation/references/code-standards.md +0 -33
- package/skills/frontend-implementation/references/design-spec.md +0 -56
- package/skills/frontend-implementation/references/node-contracts.md +0 -31
- /package/dist/{worker → infrastructure}/console/repo-fingerprint.js +0 -0
|
@@ -46,6 +46,37 @@ export function planTransitions(input) {
|
|
|
46
46
|
actions.push({ type: "recover-contract" });
|
|
47
47
|
expectedTransitions.push("contract-recovered");
|
|
48
48
|
}
|
|
49
|
+
const startupStatus = snapshot.source?.sourceFidelityStartup?.status;
|
|
50
|
+
const startupRepairDetected = startupStatus === "missing" ||
|
|
51
|
+
startupStatus === "invalid" ||
|
|
52
|
+
startupStatus === "coverage-incomplete";
|
|
53
|
+
if (startupRepairDetected && input.startupRepairAttempted) {
|
|
54
|
+
actions.push({ type: "stop", reason: "blocker" });
|
|
55
|
+
return {
|
|
56
|
+
actions,
|
|
57
|
+
gate: null,
|
|
58
|
+
blockers,
|
|
59
|
+
next: {
|
|
60
|
+
kind: "advance",
|
|
61
|
+
command: `loop-agent task advance ${snapshot.taskId} --json`,
|
|
62
|
+
},
|
|
63
|
+
expectedTransitions,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
const startupRepairRequired = startupRepairDetected;
|
|
67
|
+
if (startupStatus === "human-decision-blocked") {
|
|
68
|
+
actions.push({ type: "stop", reason: "blocker" });
|
|
69
|
+
return {
|
|
70
|
+
actions,
|
|
71
|
+
gate: null,
|
|
72
|
+
blockers,
|
|
73
|
+
next: {
|
|
74
|
+
kind: "external-action",
|
|
75
|
+
description: "Requirement ledger requires an existing human source decision; preserve sourceResolutions, resolve the blocker, then advance again.",
|
|
76
|
+
},
|
|
77
|
+
expectedTransitions,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
49
80
|
const sourceReady = snapshot.source?.requirementReady === true;
|
|
50
81
|
const contractManaged = snapshot.contract?.effectiveStatus === "managed" ||
|
|
51
82
|
Boolean(snapshot.contract?.revision && snapshot.contract.revision > 0);
|
|
@@ -53,7 +84,9 @@ export function planTransitions(input) {
|
|
|
53
84
|
// Prefer prepare once PRDs are on disk; re-import only when caller still
|
|
54
85
|
// signals hasPrdInput (advance.ts latches this after one import per call).
|
|
55
86
|
const shouldImportPrd = input.hasPrdInput;
|
|
56
|
-
if (input.forcePrepareContract ||
|
|
87
|
+
if (input.forcePrepareContract ||
|
|
88
|
+
input.refreshManagedContract ||
|
|
89
|
+
startupRepairRequired) {
|
|
57
90
|
if (shouldImportPrd) {
|
|
58
91
|
actions.push({ type: "import-prd" });
|
|
59
92
|
expectedTransitions.push("prd-imported");
|
|
@@ -124,7 +157,8 @@ export function planTransitions(input) {
|
|
|
124
157
|
const strictValidated = snapshot.dagDraft?.strictValidated === true;
|
|
125
158
|
const dagRefreshRequired = Boolean(input.forceDagRefresh ||
|
|
126
159
|
input.refreshManagedContract ||
|
|
127
|
-
input.forcePrepareContract
|
|
160
|
+
input.forcePrepareContract ||
|
|
161
|
+
startupRepairRequired);
|
|
128
162
|
if (!dagExists || dagRefreshRequired) {
|
|
129
163
|
actions.push({ type: "generate-dag" });
|
|
130
164
|
expectedTransitions.push("dag-generated");
|
|
@@ -145,10 +179,11 @@ export function planTransitions(input) {
|
|
|
145
179
|
snapshot.lifecycleState === "evidence-closed" ||
|
|
146
180
|
snapshot.lifecycleState === "needs-attention" ||
|
|
147
181
|
snapshot.lifecycleState === "awaiting-decision";
|
|
148
|
-
const
|
|
182
|
+
const approveCurrentGate = input.approveGate && !dagRefreshRequired;
|
|
183
|
+
const approvedForCurrent = postRunState || (approveCurrentGate && gate !== null);
|
|
149
184
|
if (!approvedForCurrent && (strictValidated || !dagExists || dagRefreshRequired)) {
|
|
150
185
|
// Gate opens after generate+validate in the advance loop; plan includes open.
|
|
151
|
-
if (!
|
|
186
|
+
if (!approveCurrentGate) {
|
|
152
187
|
actions.push({ type: "open-write-set-gate" });
|
|
153
188
|
expectedTransitions.push("write-set-gate-opened");
|
|
154
189
|
if (input.dryRun) {
|
|
@@ -177,14 +212,14 @@ export function planTransitions(input) {
|
|
|
177
212
|
(snapshot.lifecycleState === "run-failed" && !failedRecoverable) ||
|
|
178
213
|
snapshot.lifecycleState === "evidence-closed";
|
|
179
214
|
if (!alreadyTerminalRun) {
|
|
180
|
-
if (
|
|
215
|
+
if (approveCurrentGate && !postRunState) {
|
|
181
216
|
expectedTransitions.push("write-set-approved");
|
|
182
217
|
actions.push({ type: "start-or-monitor-run" });
|
|
183
218
|
expectedTransitions.push("dag-run-started", "dag-run-monitored");
|
|
184
219
|
}
|
|
185
220
|
else if (snapshot.lifecycleState === "running" ||
|
|
186
221
|
snapshot.activeRun ||
|
|
187
|
-
(
|
|
222
|
+
(approveCurrentGate && Boolean(snapshot.activeRun))) {
|
|
188
223
|
actions.push({ type: "start-or-monitor-run" });
|
|
189
224
|
expectedTransitions.push("dag-run-monitored");
|
|
190
225
|
}
|
|
@@ -218,7 +253,7 @@ export function planTransitions(input) {
|
|
|
218
253
|
actions,
|
|
219
254
|
gate,
|
|
220
255
|
blockers,
|
|
221
|
-
next: deriveNext(snapshot, gate,
|
|
256
|
+
next: deriveNext(snapshot, gate, approveCurrentGate),
|
|
222
257
|
expectedTransitions,
|
|
223
258
|
};
|
|
224
259
|
}
|
|
@@ -42,6 +42,7 @@ export async function buildTaskLifecycleRecommendations(input) {
|
|
|
42
42
|
const allowedPaths = [];
|
|
43
43
|
const verifyCommands = [];
|
|
44
44
|
// 1) managed task config
|
|
45
|
+
let hasDeclaredManagedVerify = false;
|
|
45
46
|
try {
|
|
46
47
|
const config = await loadTaskConfig(input.repoRoot, input.taskId);
|
|
47
48
|
for (const glob of config.allowedPaths ?? []) {
|
|
@@ -53,7 +54,9 @@ export async function buildTaskLifecycleRecommendations(input) {
|
|
|
53
54
|
source: "managed-task-config",
|
|
54
55
|
});
|
|
55
56
|
}
|
|
56
|
-
|
|
57
|
+
const managedVerify = (config.verifyCommands ?? []).filter((cmd) => Boolean(cmd.command?.trim()));
|
|
58
|
+
hasDeclaredManagedVerify = managedVerify.length > 0;
|
|
59
|
+
for (const cmd of managedVerify) {
|
|
57
60
|
const label = cmd.label?.trim() || "verify";
|
|
58
61
|
const command = cmd.command?.trim();
|
|
59
62
|
if (!command)
|
|
@@ -70,14 +73,22 @@ export async function buildTaskLifecycleRecommendations(input) {
|
|
|
70
73
|
// task may not exist yet
|
|
71
74
|
}
|
|
72
75
|
// 2) package.json scripts (only if not already covered by managed config)
|
|
76
|
+
// The full-suite `test` script is recommended only when the task declares
|
|
77
|
+
// NO managed verifyCommands at all. Once a task configures a targeted
|
|
78
|
+
// verify set (e.g. per-change typecheck + a scoped vitest run), the full
|
|
79
|
+
// `npm run test` must never be auto-added: it validates the whole repo,
|
|
80
|
+
// costs far more than the change under review, and can fail on unrelated
|
|
81
|
+
// pre-existing breakage, turning a targeted verify green into a hard fail.
|
|
73
82
|
const scripts = await readPackageScripts(input.repoRoot);
|
|
74
83
|
const hasVerifyLabel = (label) => verifyCommands.some((entry) => entry.label === label);
|
|
75
84
|
const scriptCandidates = [
|
|
76
85
|
{ label: "typecheck", script: "typecheck" },
|
|
77
|
-
{ label: "test", script: "test" },
|
|
78
86
|
{ label: "check-repo", script: "check:repo" },
|
|
79
87
|
{ label: "check-repo", script: "check-repo" },
|
|
80
88
|
];
|
|
89
|
+
if (!hasDeclaredManagedVerify) {
|
|
90
|
+
scriptCandidates.push({ label: "test", script: "test" });
|
|
91
|
+
}
|
|
81
92
|
for (const candidate of scriptCandidates) {
|
|
82
93
|
const scriptBody = scripts[candidate.script];
|
|
83
94
|
if (!scriptBody)
|
package/dist/build-stamp.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schemaVersion": 1,
|
|
3
|
-
"version": "0.
|
|
4
|
-
"gitSha": "
|
|
5
|
-
"builtAt": "2026-09-
|
|
3
|
+
"version": "0.42.0-next.10",
|
|
4
|
+
"gitSha": "610477f8b0bb4b8e4972bb540abdf5ad438c236a",
|
|
5
|
+
"builtAt": "2026-09-03T14:48:16.726Z"
|
|
6
6
|
}
|
|
@@ -31,17 +31,20 @@ import { runDagWorkflowValidate } from "../commands/dag-workflow-validate.js";
|
|
|
31
31
|
import { runDagWorkflowCompile } from "../commands/dag-workflow-compile.js";
|
|
32
32
|
import { runDagWorkflowPlan } from "../commands/dag-workflow-plan.js";
|
|
33
33
|
import { runDagApprove } from "../commands/dag-approve.js";
|
|
34
|
+
import { runDagArtifact } from "../commands/dag-artifact.js";
|
|
34
35
|
import { runDagReject } from "../commands/dag-reject.js";
|
|
35
36
|
import { runDagReconcileTasks } from "../commands/dag-reconcile-tasks.js";
|
|
36
37
|
import { runDagReconcileRun } from "../commands/dag-reconcile-run.js";
|
|
37
38
|
import { runDagRequestInterrupt } from "../commands/dag-request-interrupt.js";
|
|
38
39
|
import { runDagRerunTask } from "../commands/dag-rerun-task.js";
|
|
40
|
+
import { runDagFollowUp } from "../commands/dag-follow-up.js";
|
|
39
41
|
import { runDagRerun } from "../commands/dag-rerun.js";
|
|
40
42
|
import { runDagResume } from "../commands/dag-resume.js";
|
|
41
43
|
import { runDagDecisionInspect, runDagDecisionValidate, } from "../workflows/dag/decision-envelope.js";
|
|
42
44
|
import { runDagDoctor, runDagStatus } from "../workflows/dag/lifecycle.js";
|
|
43
45
|
import { runDagCloseoutDraft, runDagReport } from "../commands/dag-report.js";
|
|
44
46
|
import { runDagFinalVerification } from "../commands/dag-final-verification.js";
|
|
47
|
+
import { runDagContext } from "../commands/dag-context.js";
|
|
45
48
|
import { runKnowledge } from "../commands/knowledge.js";
|
|
46
49
|
import { runLoopBenchmark } from "../commands/loop-benchmark.js";
|
|
47
50
|
import { runPiReuseBenchmark } from "../commands/pi-reuse-benchmark.js";
|
|
@@ -135,8 +138,11 @@ const DAG_SUBCOMMANDS = [
|
|
|
135
138
|
"request-interrupt",
|
|
136
139
|
"rerun",
|
|
137
140
|
"rerun-task",
|
|
141
|
+
"follow-up",
|
|
138
142
|
"reconcile-tasks",
|
|
139
143
|
"final-verification",
|
|
144
|
+
"context",
|
|
145
|
+
"artifact",
|
|
140
146
|
"decision",
|
|
141
147
|
];
|
|
142
148
|
const TASK_SUBCOMMANDS = ["advance", "status", "source"];
|
|
@@ -581,6 +587,11 @@ export const COMMAND_DEFINITIONS = [
|
|
|
581
587
|
await runDagRerunTask(repoRoot, rerunTaskArgs);
|
|
582
588
|
return;
|
|
583
589
|
}
|
|
590
|
+
if (subcommand === "follow-up") {
|
|
591
|
+
const followUpArgs = rest.filter((arg) => Boolean(arg));
|
|
592
|
+
await runDagFollowUp(repoRoot, followUpArgs);
|
|
593
|
+
return;
|
|
594
|
+
}
|
|
584
595
|
if (subcommand === "reconcile-tasks") {
|
|
585
596
|
const reconcileArgs = rest.filter((arg) => Boolean(arg));
|
|
586
597
|
await runDagReconcileTasks(repoRoot, reconcileArgs);
|
|
@@ -591,6 +602,14 @@ export const COMMAND_DEFINITIONS = [
|
|
|
591
602
|
await runDagFinalVerification(repoRoot, finalVerificationArgs);
|
|
592
603
|
return;
|
|
593
604
|
}
|
|
605
|
+
if (subcommand === "context") {
|
|
606
|
+
await runDagContext(repoRoot, rest.filter((arg) => Boolean(arg)));
|
|
607
|
+
return;
|
|
608
|
+
}
|
|
609
|
+
if (subcommand === "artifact") {
|
|
610
|
+
await runDagArtifact(repoRoot, rest.filter((arg) => Boolean(arg)));
|
|
611
|
+
return;
|
|
612
|
+
}
|
|
594
613
|
if (subcommand === "decision") {
|
|
595
614
|
const decisionSubcommand = rest[0];
|
|
596
615
|
const decisionArgs = rest
|
package/dist/cli/program.js
CHANGED
|
@@ -7,12 +7,15 @@ import { runCoverageAudit } from "../commands/coverage-audit.js";
|
|
|
7
7
|
import { runCoverageReport } from "../commands/coverage-report.js";
|
|
8
8
|
import { parseCursorPromptArgs, printCursorPromptUsage, runCursorPrompt, } from "../commands/cursor-prompt.js";
|
|
9
9
|
import { runDagApprove } from "../commands/dag-approve.js";
|
|
10
|
+
import { runDagArtifact } from "../commands/dag-artifact.js";
|
|
10
11
|
import { runDagFinalVerification } from "../commands/dag-final-verification.js";
|
|
12
|
+
import { runDagContext } from "../commands/dag-context.js";
|
|
11
13
|
import { runDagInitHybrid } from "../commands/dag-init-hybrid.js";
|
|
12
14
|
import { runDagReconcileTasks } from "../commands/dag-reconcile-tasks.js";
|
|
13
15
|
import { runDagReconcileRun } from "../commands/dag-reconcile-run.js";
|
|
14
16
|
import { runDagRequestInterrupt } from "../commands/dag-request-interrupt.js";
|
|
15
17
|
import { runDagRerunTask } from "../commands/dag-rerun-task.js";
|
|
18
|
+
import { runDagFollowUp } from "../commands/dag-follow-up.js";
|
|
16
19
|
import { runDagRerun } from "../commands/dag-rerun.js";
|
|
17
20
|
import { runDagReject } from "../commands/dag-reject.js";
|
|
18
21
|
import { runDagCloseoutDraft, runDagReport } from "../commands/dag-report.js";
|
|
@@ -357,12 +360,21 @@ async function runDagAction(repoRoot, subcommand, rest) {
|
|
|
357
360
|
case "rerun-task":
|
|
358
361
|
await runDagRerunTask(repoRoot, args);
|
|
359
362
|
return;
|
|
363
|
+
case "follow-up":
|
|
364
|
+
await runDagFollowUp(repoRoot, args);
|
|
365
|
+
return;
|
|
360
366
|
case "reconcile-tasks":
|
|
361
367
|
await runDagReconcileTasks(repoRoot, args);
|
|
362
368
|
return;
|
|
363
369
|
case "final-verification":
|
|
364
370
|
await runDagFinalVerification(repoRoot, args);
|
|
365
371
|
return;
|
|
372
|
+
case "context":
|
|
373
|
+
await runDagContext(repoRoot, args);
|
|
374
|
+
return;
|
|
375
|
+
case "artifact":
|
|
376
|
+
await runDagArtifact(repoRoot, args);
|
|
377
|
+
return;
|
|
366
378
|
case "decision": {
|
|
367
379
|
const [decisionSubcommand, ...decisionArgs] = args;
|
|
368
380
|
if (decisionSubcommand === "inspect") {
|
|
@@ -376,7 +388,7 @@ async function runDagAction(repoRoot, subcommand, rest) {
|
|
|
376
388
|
throw new Error("usage: dag decision <inspect|validate> --run-id <id> [--node-id <node-id>]");
|
|
377
389
|
}
|
|
378
390
|
default:
|
|
379
|
-
throw new Error("usage: dag <init-hybrid|execute|validate|workflow-plan|workflow-validate|workflow-compile|approve|reject|resume|status|doctor|report|closeout-draft|reconcile-run|request-interrupt|rerun|rerun-task|reconcile-tasks|final-verification|decision> ...");
|
|
391
|
+
throw new Error("usage: dag <init-hybrid|execute|validate|workflow-plan|workflow-validate|workflow-compile|approve|reject|resume|status|doctor|report|closeout-draft|reconcile-run|request-interrupt|rerun|rerun-task|reconcile-tasks|final-verification|context|artifact|decision> ...");
|
|
380
392
|
}
|
|
381
393
|
}
|
|
382
394
|
function compactArgs(args) {
|
|
@@ -428,7 +440,7 @@ const RAW_SUBCOMMAND_USAGE = {
|
|
|
428
440
|
"dag approve": 'usage: dag approve --run-id <id> --option <A|B|...> [--notes "..."]',
|
|
429
441
|
"dag reject": 'usage: dag reject --run-id <id> --reason "..."',
|
|
430
442
|
"dag rerun": "usage: dag rerun --run-id <id> --from-node <node-id> --plan [--json] | dag rerun --run-id <id> --from-node <node-id> --plan-hash <hash> --request-id <key> --reason <text> [--json]",
|
|
431
|
-
"dag rerun-task": "usage: dag rerun-task --run-id <id> --reason <text> --request-id <key> [--profile auto|minimal|standard|reviewed|supervised] [--task-id <id>] [--json]",
|
|
443
|
+
"dag rerun-task": "usage: dag rerun-task --run-id <id> --reason <text> --request-id <key> [--profile auto|minimal|standard|reviewed|supervised] [--task-id <id>] [--frontend-recovery --recovery-operation-id <id>] [--json]",
|
|
432
444
|
"dag reconcile-run": "usage: dag reconcile-run --run-id <id> [--action supersede|abandon] [--reason <text>]",
|
|
433
445
|
"dag doctor": "usage: dag doctor [--run-id <id>]\nInspect health issues before approve/reject/resume; no --json (output is already structured JSON).",
|
|
434
446
|
"dag report": "usage: dag report [--run-id <id>] [--lifecycle active|paused|completed|all] [--json|--markdown] [--failed-only] [--latest] [--paused-latest]\nRead-only run report; --json gives structured facts (canonical: `dag report --run-id <id> --lifecycle completed --json`).",
|
|
@@ -545,9 +557,24 @@ function addDagCommand(program, defaultRepoRoot) {
|
|
|
545
557
|
"request-interrupt",
|
|
546
558
|
"rerun",
|
|
547
559
|
"rerun-task",
|
|
560
|
+
"follow-up",
|
|
548
561
|
"reconcile-tasks",
|
|
549
562
|
"final-verification",
|
|
563
|
+
"context",
|
|
564
|
+
"artifact",
|
|
550
565
|
], defaultRepoRoot);
|
|
566
|
+
const context = dag.commands.find((command) => command.name() === "context");
|
|
567
|
+
if (!context)
|
|
568
|
+
throw new Error("dag context command registration failed");
|
|
569
|
+
context
|
|
570
|
+
.description("Inspect or export a captured effective node context")
|
|
571
|
+
.addHelpText("after", "\nActions:\n show --run-id <id> --node-id <id> --attempt <n> [--json]\n export --run-id <id> --node-id <id> --attempt <n> --output <zip> [--target <generic|pi-session|pi-handoff|readable>] [--redaction <redacted|raw>] [--acknowledge-sensitive-data] [--json]");
|
|
572
|
+
const artifact = dag.commands.find((command) => command.name() === "artifact");
|
|
573
|
+
if (!artifact)
|
|
574
|
+
throw new Error("dag artifact command registration failed");
|
|
575
|
+
artifact
|
|
576
|
+
.description("Inspect artifact consumers or manage staged artifact revisions")
|
|
577
|
+
.addHelpText("after", "\nActions:\n consumers --run-id <id> --node-id <producer> --artifact <path> [--json]\n revision stage --run-id <id> --node-id <producer> --artifact <path> --file <replacement> --request-id <id> [--json]\n revision discard --revision-id <id> --request-id <id> [--json]");
|
|
551
578
|
const validate = createRawArgsCommand("validate", "Validate an Agent DAG spec", defaultRepoRoot);
|
|
552
579
|
configureDagValidate(validate);
|
|
553
580
|
replaceActionWithOptionAwareDispatch(validate, defaultRepoRoot);
|
|
@@ -1328,12 +1328,15 @@ export async function runClientRecovery(input) {
|
|
|
1328
1328
|
}
|
|
1329
1329
|
const plugin = await installProjectOpenCodePlugin({
|
|
1330
1330
|
repoRoot: input.repoRoot,
|
|
1331
|
+
onlyIfMissing: true,
|
|
1331
1332
|
});
|
|
1332
1333
|
const overflowPlugin = await installProjectOpenCodeOverflowCompactPlugin({
|
|
1333
1334
|
repoRoot: input.repoRoot,
|
|
1335
|
+
onlyIfMissing: true,
|
|
1334
1336
|
});
|
|
1335
1337
|
const piExtension = await installProjectPiContextOverflowExtension({
|
|
1336
1338
|
repoRoot: input.repoRoot,
|
|
1339
|
+
onlyIfMissing: true,
|
|
1337
1340
|
});
|
|
1338
1341
|
const pi = await applyProjectPiSettingsMerge({ repoRoot: input.repoRoot });
|
|
1339
1342
|
if (mode !== "user") {
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { lstat, readFile } from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { readRevisionParentArtifact } from "../workflows/dag/context-transfer/operator-actions.js";
|
|
5
|
+
import { readFileBoundarySafe } from "../shared/path-safety.js";
|
|
6
|
+
import { openConsoleAppData } from "../infrastructure/console/app-data.js";
|
|
7
|
+
import { artifactRevisionUploadPath, createArtifactRevision, discardArtifactRevision, acquireArtifactRevisionLease, releaseArtifactRevisionLease, } from "../infrastructure/console/artifact-revision-store.js";
|
|
8
|
+
import { OperationStore } from "../infrastructure/console/operation-store.js";
|
|
9
|
+
import { buildArtifactBindingGraph } from "../workflows/dag/artifact-bindings.js";
|
|
10
|
+
import { resolveArtifactRevisionSchema } from "../workflows/dag/artifact-revision-schema-registry.js";
|
|
11
|
+
import { locateDagRun, readDagRunSpec } from "../workflows/dag/lifecycle.js";
|
|
12
|
+
function parseArtifactArgs(args) {
|
|
13
|
+
const [first, second] = args;
|
|
14
|
+
const action = first === "consumers"
|
|
15
|
+
? "consumers"
|
|
16
|
+
: first === "revision" && second === "stage"
|
|
17
|
+
? "stage"
|
|
18
|
+
: first === "revision" && second === "discard"
|
|
19
|
+
? "discard"
|
|
20
|
+
: undefined;
|
|
21
|
+
if (!action) {
|
|
22
|
+
throw new Error("usage: dag artifact consumers --run-id <run> --node-id <producer> --artifact <path> [--json] | dag artifact revision <stage|discard> ...");
|
|
23
|
+
}
|
|
24
|
+
let runId;
|
|
25
|
+
let nodeId;
|
|
26
|
+
let artifactPath;
|
|
27
|
+
let artifactId;
|
|
28
|
+
let file;
|
|
29
|
+
let candidateUpload;
|
|
30
|
+
let revisionId;
|
|
31
|
+
let requestId;
|
|
32
|
+
let json = false;
|
|
33
|
+
for (let index = action === "consumers" ? 1 : 2; index < args.length; index += 1) {
|
|
34
|
+
const arg = args[index];
|
|
35
|
+
const next = () => args[++index];
|
|
36
|
+
if (arg === "--run-id")
|
|
37
|
+
runId = next();
|
|
38
|
+
else if (arg.startsWith("--run-id="))
|
|
39
|
+
runId = arg.slice(9);
|
|
40
|
+
else if (arg === "--node-id")
|
|
41
|
+
nodeId = next();
|
|
42
|
+
else if (arg.startsWith("--node-id="))
|
|
43
|
+
nodeId = arg.slice(10);
|
|
44
|
+
else if (arg === "--artifact")
|
|
45
|
+
artifactPath = next();
|
|
46
|
+
else if (arg.startsWith("--artifact="))
|
|
47
|
+
artifactPath = arg.slice(11);
|
|
48
|
+
else if (arg === "--artifact-id")
|
|
49
|
+
artifactId = next();
|
|
50
|
+
else if (arg.startsWith("--artifact-id="))
|
|
51
|
+
artifactId = arg.slice(14);
|
|
52
|
+
else if (arg === "--file")
|
|
53
|
+
file = next();
|
|
54
|
+
else if (arg.startsWith("--file="))
|
|
55
|
+
file = arg.slice(7);
|
|
56
|
+
else if (arg === "--candidate-upload")
|
|
57
|
+
candidateUpload = next();
|
|
58
|
+
else if (arg.startsWith("--candidate-upload="))
|
|
59
|
+
candidateUpload = arg.slice(19);
|
|
60
|
+
else if (arg === "--revision-id")
|
|
61
|
+
revisionId = next();
|
|
62
|
+
else if (arg.startsWith("--revision-id="))
|
|
63
|
+
revisionId = arg.slice(14);
|
|
64
|
+
else if (arg === "--request-id")
|
|
65
|
+
requestId = next();
|
|
66
|
+
else if (arg.startsWith("--request-id="))
|
|
67
|
+
requestId = arg.slice(13);
|
|
68
|
+
else if (arg === "--json")
|
|
69
|
+
json = true;
|
|
70
|
+
else
|
|
71
|
+
throw new Error(`unknown dag artifact flag: ${arg}`);
|
|
72
|
+
}
|
|
73
|
+
return { action, runId, nodeId, artifactPath, artifactId, file, candidateUpload, revisionId, requestId, json };
|
|
74
|
+
}
|
|
75
|
+
function sha256(bytes) {
|
|
76
|
+
return createHash("sha256").update(bytes).digest("hex");
|
|
77
|
+
}
|
|
78
|
+
function print(value, json, fallback) {
|
|
79
|
+
process.stdout.write(json ? `${JSON.stringify(value, null, 2)}\n` : `${fallback}\n`);
|
|
80
|
+
}
|
|
81
|
+
export async function runDagArtifactCore(repoRoot, args, cliOptions) {
|
|
82
|
+
const input = parseArtifactArgs(args);
|
|
83
|
+
if (input.action === "discard") {
|
|
84
|
+
if (!input.revisionId || !input.requestId) {
|
|
85
|
+
throw new Error("dag artifact revision discard requires --revision-id and --request-id");
|
|
86
|
+
}
|
|
87
|
+
const appData = openConsoleAppData({ repoRoot, ...(cliOptions?.appDataRoot ? { appDataRoot: cliOptions.appDataRoot } : {}) });
|
|
88
|
+
const operations = new OperationStore(appData);
|
|
89
|
+
const created = await operations.create({
|
|
90
|
+
clientRequestId: input.requestId,
|
|
91
|
+
action: "cliArtifactRevisionDiscard",
|
|
92
|
+
actionParams: { revisionId: input.revisionId },
|
|
93
|
+
});
|
|
94
|
+
if (created.kind === "conflict")
|
|
95
|
+
throw new Error(created.code);
|
|
96
|
+
if (created.kind === "idempotent" && created.operation.state === "succeeded") {
|
|
97
|
+
return asRecord(created.operation.result);
|
|
98
|
+
}
|
|
99
|
+
const leaseHolder = `cli-discard:${input.requestId}`;
|
|
100
|
+
const revision = await acquireArtifactRevisionLease({
|
|
101
|
+
appData,
|
|
102
|
+
revisionId: input.revisionId,
|
|
103
|
+
holder: leaseHolder,
|
|
104
|
+
})
|
|
105
|
+
.then(() => discardArtifactRevision({
|
|
106
|
+
appData,
|
|
107
|
+
revisionId: input.revisionId,
|
|
108
|
+
holder: leaseHolder,
|
|
109
|
+
}))
|
|
110
|
+
.finally(() => releaseArtifactRevisionLease({
|
|
111
|
+
appData,
|
|
112
|
+
revisionId: input.revisionId,
|
|
113
|
+
holder: leaseHolder,
|
|
114
|
+
}).catch(() => { }));
|
|
115
|
+
const result = { ok: true, revisionId: revision.revisionId, state: revision.state };
|
|
116
|
+
await operations.update(created.operation.operationId, {
|
|
117
|
+
state: "succeeded",
|
|
118
|
+
startedAt: created.operation.createdAt,
|
|
119
|
+
finishedAt: new Date().toISOString(),
|
|
120
|
+
result,
|
|
121
|
+
});
|
|
122
|
+
return result;
|
|
123
|
+
}
|
|
124
|
+
if (!input.runId || !input.nodeId || (!input.artifactPath && !input.artifactId)) {
|
|
125
|
+
throw new Error("dag artifact requires --run-id, --node-id, and --artifact <relative-path> or --artifact-id <id>");
|
|
126
|
+
}
|
|
127
|
+
const located = await locateDagRun(repoRoot, input.runId);
|
|
128
|
+
if (!located)
|
|
129
|
+
throw new Error(`dag run not found: ${input.runId}`);
|
|
130
|
+
const graph = buildArtifactBindingGraph(await readDagRunSpec(located.runDir));
|
|
131
|
+
const declaration = graph.artifacts.find((item) => item.producerNodeId === input.nodeId &&
|
|
132
|
+
(input.artifactId
|
|
133
|
+
? item.artifactId === input.artifactId
|
|
134
|
+
: item.path === input.artifactPath));
|
|
135
|
+
if (!declaration)
|
|
136
|
+
throw new Error("ARTIFACT_BINDING_UNAVAILABLE");
|
|
137
|
+
if (input.action === "consumers") {
|
|
138
|
+
return {
|
|
139
|
+
schemaVersion: 1,
|
|
140
|
+
runId: input.runId,
|
|
141
|
+
nodeId: input.nodeId,
|
|
142
|
+
artifactId: declaration.artifactId,
|
|
143
|
+
artifactPath: declaration.path,
|
|
144
|
+
directConsumerNodeIds: declaration.directConsumerNodeIds,
|
|
145
|
+
graphSha256: graph.artifactGraphSha256,
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
if (!input.requestId || (!input.file && !input.candidateUpload)) {
|
|
149
|
+
throw new Error("dag artifact revision stage requires --file or --candidate-upload, and --request-id");
|
|
150
|
+
}
|
|
151
|
+
const appData = openConsoleAppData({
|
|
152
|
+
repoRoot,
|
|
153
|
+
...(cliOptions?.appDataRoot ? { appDataRoot: cliOptions.appDataRoot } : {}),
|
|
154
|
+
});
|
|
155
|
+
// The candidate upload lives in app-data and is cross-process mutable;
|
|
156
|
+
// resolve + read it TOCTOU-safe (O_NOFOLLOW fd read) instead of
|
|
157
|
+
// lstat-then-readFile, so a swapped symlink can never feed an unintended
|
|
158
|
+
// file into the stage. Both the path resolution (assertSafeAppDataPath
|
|
159
|
+
// rejects symlinks/hardlinks) and the fd read map to the stable
|
|
160
|
+
// ARTIFACT_REVISION_PATH_INVALID code.
|
|
161
|
+
let candidateBytes;
|
|
162
|
+
if (input.candidateUpload) {
|
|
163
|
+
try {
|
|
164
|
+
if (!/^[a-f0-9]{64}$/.test(input.candidateUpload)) {
|
|
165
|
+
throw new Error("invalid --candidate-upload hash");
|
|
166
|
+
}
|
|
167
|
+
const candidatePath = artifactRevisionUploadPath(appData, input.candidateUpload);
|
|
168
|
+
candidateBytes = await readFileBoundarySafe({
|
|
169
|
+
boundaryRoot: path.dirname(candidatePath),
|
|
170
|
+
targetPath: candidatePath,
|
|
171
|
+
label: "candidate upload",
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
catch (error) {
|
|
175
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
176
|
+
if (/symlink|hardlink|single-link|regular|boundary|escap|app-data/i.test(message)) {
|
|
177
|
+
throw new Error("ARTIFACT_REVISION_PATH_INVALID");
|
|
178
|
+
}
|
|
179
|
+
throw error;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
else {
|
|
183
|
+
const candidatePath = path.resolve(repoRoot, input.file);
|
|
184
|
+
const stat = await lstat(candidatePath);
|
|
185
|
+
if (!stat.isFile() || stat.isSymbolicLink() || stat.nlink !== 1) {
|
|
186
|
+
throw new Error("candidate must be an owned regular file");
|
|
187
|
+
}
|
|
188
|
+
candidateBytes = await readFile(candidatePath);
|
|
189
|
+
}
|
|
190
|
+
if (candidateBytes.byteLength > 10 * 1024 * 1024)
|
|
191
|
+
throw new Error("candidate exceeds 10 MiB");
|
|
192
|
+
const operations = new OperationStore(appData);
|
|
193
|
+
const actionParams = {
|
|
194
|
+
runId: input.runId,
|
|
195
|
+
nodeId: input.nodeId,
|
|
196
|
+
artifactPath: declaration.path,
|
|
197
|
+
candidateSha256: sha256(candidateBytes),
|
|
198
|
+
candidateBytes: candidateBytes.byteLength,
|
|
199
|
+
};
|
|
200
|
+
const created = await operations.create({
|
|
201
|
+
clientRequestId: input.requestId,
|
|
202
|
+
action: "cliArtifactRevisionStage",
|
|
203
|
+
actionParams,
|
|
204
|
+
});
|
|
205
|
+
if (created.kind === "conflict")
|
|
206
|
+
throw new Error(created.code);
|
|
207
|
+
if (created.kind === "idempotent" && created.operation.state === "succeeded") {
|
|
208
|
+
return asRecord(created.operation.result);
|
|
209
|
+
}
|
|
210
|
+
const parent = await readRevisionParentArtifact({
|
|
211
|
+
cwd: repoRoot,
|
|
212
|
+
runId: input.runId,
|
|
213
|
+
producerNodeId: input.nodeId,
|
|
214
|
+
artifactId: declaration.artifactId,
|
|
215
|
+
});
|
|
216
|
+
const revision = await createArtifactRevision({
|
|
217
|
+
appData,
|
|
218
|
+
parentRunId: input.runId,
|
|
219
|
+
producerNodeId: input.nodeId,
|
|
220
|
+
declaration: parent.declaration,
|
|
221
|
+
parentSha256: parent.parentSha256,
|
|
222
|
+
parentBytes: parent.parentBytes,
|
|
223
|
+
candidateBytes,
|
|
224
|
+
mediaType: declaration.mediaType,
|
|
225
|
+
artifactSchemaResolver: resolveArtifactRevisionSchema,
|
|
226
|
+
});
|
|
227
|
+
const projected = {
|
|
228
|
+
revisionId: revision.revisionId,
|
|
229
|
+
state: revision.state,
|
|
230
|
+
parent: revision.parent,
|
|
231
|
+
candidate: {
|
|
232
|
+
sha256: revision.candidate.sha256,
|
|
233
|
+
bytes: revision.candidate.bytes,
|
|
234
|
+
mediaType: revision.candidate.mediaType,
|
|
235
|
+
uploadedAt: revision.candidate.uploadedAt,
|
|
236
|
+
},
|
|
237
|
+
validation: revision.validation,
|
|
238
|
+
diff: {
|
|
239
|
+
kind: revision.diff.kind,
|
|
240
|
+
summary: revision.diff.summary,
|
|
241
|
+
truncated: revision.diff.truncated,
|
|
242
|
+
},
|
|
243
|
+
expiresAt: revision.expiresAt,
|
|
244
|
+
};
|
|
245
|
+
const result = { ok: true, revision: projected };
|
|
246
|
+
await operations.update(created.operation.operationId, {
|
|
247
|
+
state: "succeeded",
|
|
248
|
+
startedAt: created.operation.createdAt,
|
|
249
|
+
finishedAt: new Date().toISOString(),
|
|
250
|
+
result,
|
|
251
|
+
});
|
|
252
|
+
return result;
|
|
253
|
+
}
|
|
254
|
+
function asRecord(value) {
|
|
255
|
+
return value && typeof value === "object" ? value : { ok: true };
|
|
256
|
+
}
|
|
257
|
+
export async function runDagArtifact(repoRoot, args) {
|
|
258
|
+
const input = parseArtifactArgs(args);
|
|
259
|
+
try {
|
|
260
|
+
const result = await runDagArtifactCore(repoRoot, args);
|
|
261
|
+
if (input.action === "discard") {
|
|
262
|
+
print(result, input.json, `Discarded ${String(result.revisionId ?? "")}`);
|
|
263
|
+
}
|
|
264
|
+
else if (input.action === "consumers") {
|
|
265
|
+
const consumers = result;
|
|
266
|
+
print(result, input.json, `${consumers.artifactPath}: ${(consumers.directConsumerNodeIds ?? []).join(", ") || "no consumers"}`);
|
|
267
|
+
}
|
|
268
|
+
else {
|
|
269
|
+
print(result, input.json, `Staged revision ${String(result.revisionId ?? "")}`);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
catch (error) {
|
|
273
|
+
if (!input.json)
|
|
274
|
+
throw error;
|
|
275
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
276
|
+
const candidateCode = message.split(":", 1)[0]?.trim() ?? "";
|
|
277
|
+
const code = /^[A-Z][A-Z0-9_]+$/.test(candidateCode)
|
|
278
|
+
? candidateCode
|
|
279
|
+
: "INTERNAL_ERROR";
|
|
280
|
+
process.stdout.write(`${JSON.stringify({ schemaVersion: 1, ok: false, error: { code, message } }, null, 2)}\n`);
|
|
281
|
+
process.stderr.write(`${message}\n`);
|
|
282
|
+
process.exitCode = 1;
|
|
283
|
+
}
|
|
284
|
+
}
|