@tea-agent/loop-agent 0.41.1-beta.0 → 0.42.0-next.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +78 -206
- 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 +19 -75
- package/dist/application/task-lifecycle/advance.js +5 -24
- package/dist/application/task-lifecycle/observe.js +17 -171
- package/dist/application/task-lifecycle/plan-transitions.js +7 -42
- package/dist/application/task-lifecycle/recommendations.js +2 -13
- package/dist/build-stamp.json +3 -3
- package/dist/cli/command-definitions.js +12 -7
- package/dist/cli/program.js +23 -6
- package/dist/commands/client-recovery.js +0 -3
- package/dist/commands/dag-artifact.js +284 -0
- package/dist/commands/dag-context.js +184 -0
- package/dist/commands/dag-rerun.js +206 -1
- package/dist/commands/init.js +1 -27
- package/dist/commands/task-advance.js +0 -19
- package/dist/executors/dag-pi-executor.js +266 -3792
- package/dist/executors/pi-executor.js +4 -15
- package/dist/executors/pi-sdk-executor.js +3 -0
- package/dist/executors/shell-executor.js +217 -945
- package/dist/executors/shell-write-guard.js +0 -7
- 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 +197 -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 +0 -12
- package/dist/shared/openspec-spec.js +4 -70
- package/dist/shared/operator/capabilities.js +14 -7
- 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 +80 -35
- package/dist/task/contract/adopt.js +0 -4
- package/dist/task/contract/import-revision.js +0 -4
- package/dist/task/contract/project.js +0 -3
- package/dist/task/contract/schema.js +1 -2
- package/dist/task/frontend-project-capability.js +20 -203
- package/dist/task/runtime.js +2 -5
- package/dist/task/source-prepare/build-draft.js +3 -3
- package/dist/task/source-prepare/fragment-inventory.js +9 -15
- package/dist/task/source-prepare/prepare.js +1 -89
- package/dist/task/source-prepare/semantic-intake.js +2 -6
- package/dist/task/source-references.js +1 -22
- 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/pi-runtime.js +3 -2
- package/dist/worker/console/chat/resource-preferences-store.js +1 -1
- package/dist/worker/console/chat/routes.js +13 -4
- package/dist/worker/console/chat/semantic-activity.js +12 -11
- package/dist/worker/console/chat/session-stats.js +96 -0
- package/dist/worker/console/chat/session-store.js +1 -1
- package/dist/worker/console/chat/turn-process.js +28 -4
- package/dist/worker/console/chat/user-questions.js +1 -1
- package/dist/worker/console/console-update-runtime.js +1 -1
- package/dist/worker/console/context-transfer-diagnostics.js +198 -0
- package/dist/worker/console/dag-confirmation.js +1 -1
- package/dist/worker/console/dag-execution-receipt.js +1 -1
- package/dist/worker/console/doctor.js +1 -1
- package/dist/worker/console/draft-store.js +1 -1
- package/dist/worker/console/human-gate-token.js +1 -1
- package/dist/worker/console/index.js +3 -6
- 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 +107 -168
- 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 +60 -1
- package/dist/worker/console/routes.js +637 -8
- package/dist/worker/console/security.js +35 -0
- package/dist/worker/console/server.js +6 -6
- package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-Bho4we4c.js → abnfDiagram-N423BO3Z-DZZ8m3PO.js} +1 -1
- package/dist/worker/console/static/assets/{arc-TBDkTeK0.js → arc-D6PvaVd-.js} +1 -1
- package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-BF_fZrn2.js → architectureDiagram-T3A2C74G-B_OTOiI8.js} +1 -1
- package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-CCnPgUe2.js → blockDiagram-VBNYF7ZC-Bv6rqHBg.js} +1 -1
- package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-BCYZJLAT.js → c4Diagram-5PPSVZJV-B8eHr0oz.js} +1 -1
- package/dist/worker/console/static/assets/channel-BU5gOilw.js +1 -0
- package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-D8K6adhU.js → chunk-2GRJ4B5K-DYwR0im2.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-DkT1Q78b.js → chunk-2Q5K7J3B-D2WPGqXt.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5RXB4S5H-DuhvpIr7.js → chunk-5RXB4S5H-CQISJ_I7.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5VM5RSS4-BnK6dC49.js → chunk-5VM5RSS4-C0o2Du1e.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-DPz__z7r.js → chunk-6Q2QTUOP-4f8kr-U7.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-GF5L2VYU-0kkppM2j.js → chunk-GF5L2VYU-D_OWvXzX.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-JWPE2WC7-BB41iK5t.js → chunk-JWPE2WC7-CasdPz5X.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-KBJHAD2P-CiVrxKHU.js → chunk-KBJHAD2P-Cj8lRrla.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-RYQCIY6F-C2h9Xje1.js → chunk-RYQCIY6F-CUvD1FSd.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-XXDRQBXY-3iOSHdbM.js → chunk-XXDRQBXY-CqLw_eqB.js} +1 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-DIzKJGHr.js +1 -0
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-DIzKJGHr.js +1 -0
- package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-BN3A7tkk.js → cose-bilkent-JH36ORCC-ku-WqJPx.js} +1 -1
- package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-CTuBy0Or.js → cynefin-VYW2F7L2-Bq_PYDhl.js} +1 -1
- package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-wb6tUlMN.js → cynefinDiagram-MW4NZA55-BiGZV641.js} +1 -1
- package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-GP1Ivx8B.js → dagre-VZM6K2ZE-C4JRlglF.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-7IWD3JNH-ejrcK6L0.js → diagram-7IWD3JNH-B0dNeWYG.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-YFkCCvUU.js → diagram-B4RE2ZJO-Cj35YvYM.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-LBJQPF4R-B4rrvIaL.js → diagram-LBJQPF4R-uzQoJ2-8.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-Q27KOJAE-C-YIZcUf.js → diagram-Q27KOJAE-D1a-Buoz.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-UB23O5K3-BPF5wcL1.js → diagram-UB23O5K3-XjRrLRSs.js} +1 -1
- package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-CU5SzXjO.js → ebnfDiagram-BXEA7PRR-Da_O24cW.js} +1 -1
- package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-CpL414DE.js → erDiagram-JOGREHBK-BLJ8jrYU.js} +1 -1
- package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-MrhLzNau.js → flowDiagram-UKHOOZJN-B9GrLjM0.js} +1 -1
- package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-Bg4qFuUo.js → ganttDiagram-PKOTCBZU-CeJ0TqiK.js} +1 -1
- package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-ColxSXLR.js → gitGraphDiagram-DS77QQ5N-Bm2eNNFX.js} +1 -1
- package/dist/worker/console/static/assets/index-H9rFJiGL.css +1 -0
- package/dist/worker/console/static/assets/index-xwu9GxEc.js +451 -0
- package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-Czvtm6gc.js → infoDiagram-6WML65LV-DB26i3d8.js} +1 -1
- package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-B7Xp8HyJ.js → ishikawaDiagram-WSZJBQD7-BOGRyeZT.js} +1 -1
- package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-DWYZcvph.js → journeyDiagram-NVQOT4AX-CZRoBO_6.js} +1 -1
- package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-KTLZRUjx.js → kanban-definition-27J2QSJJ-qlWhJyoB.js} +1 -1
- package/dist/worker/console/static/assets/{linear-_Exn7bfl.js → linear-CfUiDDB3.js} +1 -1
- package/dist/worker/console/static/assets/{mermaid.core-Cxg8kX8V.js → mermaid.core-BQe6fpqj.js} +5 -5
- package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-oKrlo2Bt.js → mindmap-definition-FAOFIHXS-zFzWHw64.js} +1 -1
- package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-XAeteiNd.js → pegDiagram-VL7TDLO6-DFGUqjZO.js} +1 -1
- package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-BTf1K-lp.js → pieDiagram-7S7Q4E2Y-BB5i0l8Z.js} +1 -1
- package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-C0IlY0Jf.js → quadrantDiagram-CIZ2JOQS-obbVZ_X5.js} +1 -1
- package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-BVbKtZhW.js → railroadDiagram-AXF67PYL-BwdaNzc1.js} +1 -1
- package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-C5e4JFcr.js → requirementDiagram-LRYGKXZP-CkTJaugh.js} +1 -1
- package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-Cz30hc29.js → sankeyDiagram-W5VNT64P-BwJ-hIgq.js} +1 -1
- package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-DS33n6d0.js → sequenceDiagram-SI44F4Z6-BzSgmm7w.js} +1 -1
- package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-46w7v-9T.js → sizeCapture-X5ZJPWSS-Bn3oj5Q1.js} +1 -1
- package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-pbcD5SsZ.js → stateDiagram-OKZ733FA-dxhGtL8K.js} +1 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-Cld2qK9v.js +1 -0
- package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-DuivYBSj.js → swimlanes-SLNWSIFB-BLpEInya.js} +2 -2
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-TIHpiT7w.js +8 -0
- package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-Cb9VZVIl.js → timeline-definition-Z64GVDOM-ChJGYcXy.js} +1 -1
- package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-D2-mfOgR.js → vennDiagram-T6HMQDX7-DK-qjRer.js} +1 -1
- package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-DeS2aC76.js → wardleyDiagram-T6FBY63Y-3qeaWAg-.js} +1 -1
- package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-WGsrFjxH.js → xychartDiagram-ELKLHX3M-CootlyP9.js} +1 -1
- package/dist/worker/console/static/index.html +7 -2
- package/dist/worker/console/static-src/app/useOperatorActions.js +3 -2
- package/dist/worker/console/static-src/app/useRunProgress.js +9 -2
- package/dist/worker/console/static-src/operator-chat/dag-progress-link.js +22 -0
- package/dist/worker/console/static-src/operator-chat/useChatSessions.js +2 -0
- package/dist/worker/console/static-src/operator-chat/useChatThread.js +6 -0
- package/dist/worker/console/static-src/operator-chat/useRuntimeControls.js +28 -3
- package/dist/worker/console/static-src/pages/tasks/run-panel-progress.js +106 -0
- package/dist/worker/console/static-src/shell/console-update-reload.js +25 -0
- package/dist/worker/console/workspace-context.js +28 -112
- package/dist/worker/console/workspace-registry.js +2 -2
- package/dist/worker/continuation/worker-continuation.js +278 -0
- package/dist/worker/observability/read-model.js +4 -0
- package/dist/worker/observe/health.js +1 -1
- package/dist/worker/observe/node-transparency.js +572 -0
- package/dist/worker/observe/routes.js +51 -1
- package/dist/worker/observe/static/api.js +69 -5
- package/dist/worker/observe/static/dag-context-reason-labels.d.ts +9 -0
- package/dist/worker/observe/static/dag-context-reason-labels.js +120 -0
- package/dist/worker/observe/static/dag-node-purpose.js +0 -5
- package/dist/worker/observe/static/state.js +3 -1
- package/dist/worker/observe/static/styles.css +1037 -126
- package/dist/worker/observe/static/views/dag-inspector.js +1217 -65
- package/dist/worker/observe/static/views/session-timeline.js +121 -25
- 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 +99 -4
- package/dist/workflows/dag/backend-test-markdown-workflow.js +13 -1
- package/dist/workflows/dag/backend-test-result-contract.js +1 -1
- package/dist/workflows/dag/backend-test-writer-completeness.js +45 -3
- 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 +2 -1
- package/dist/workflows/dag/frontend-implementation-contract.js +162 -932
- package/dist/workflows/dag/frontend-plan-render.js +1 -2
- package/dist/workflows/dag/frontend-prewrite-gate.js +349 -255
- package/dist/workflows/dag/frontend-recovery-plan.js +10 -17
- package/dist/workflows/dag/frontend-recovery-run.js +20 -33
- package/dist/workflows/dag/frontend-repair.js +432 -1
- package/dist/workflows/dag/frontend-review-context.js +15 -261
- package/dist/workflows/dag/frontend-verification-trace.js +24 -249
- package/dist/workflows/dag/frontend-worktree-diff.js +17 -250
- package/dist/workflows/dag/frontend-writer-rollback.js +32 -0
- package/dist/workflows/dag/init-hybrid.js +740 -718
- package/dist/workflows/dag/interrupt-request.js +0 -7
- package/dist/workflows/dag/node-execution.js +136 -759
- package/dist/workflows/dag/path-safety.js +1 -0
- package/dist/workflows/dag/prompt.js +30 -38
- package/dist/workflows/dag/report.js +1 -37
- package/dist/workflows/dag/rerun-feedback.js +1 -256
- package/dist/workflows/dag/rerun-plan.js +557 -11
- package/dist/workflows/dag/rerun-run.js +601 -38
- package/dist/workflows/dag/rerun-task.js +0 -29
- package/dist/workflows/dag/retry-policy.js +109 -214
- package/dist/workflows/dag/runner.js +142 -430
- package/dist/workflows/dag/scheduler.js +16 -114
- package/dist/workflows/dag/skill-snapshot.js +17 -0
- package/dist/workflows/dag/types.js +195 -246
- package/dist/workflows/dag/validate.js +12 -28
- package/docs/architecture/runtime-boundaries.md +6 -6
- package/docs/architecture/worker-and-feature.md +1 -1
- package/docs/init-surface.manifest.json +12 -30
- package/docs/skills/vetted-skill-registry.md +2 -4
- package/docs/templates/README.md +0 -2
- package/docs/templates/agent-dag-report.schema.json +2 -8
- package/docs/templates/agent-dag.schema.json +33 -1
- package/docs/templates/backend-test-dag.json +18 -18
- package/docs/templates/frontend-implementation-contract.schema.json +1 -4
- package/docs/templates/product-line/task.yaml +1 -1
- package/harness.json +3 -3
- package/package.json +3 -1
- package/skills/frontend-bounded-implement/SKILL.md +14 -15
- package/skills/frontend-design-review/SKILL.md +41 -22
- package/skills/frontend-implementation/SKILL.md +52 -0
- package/skills/frontend-implementation/references/code-standards.md +33 -0
- package/skills/frontend-implementation/references/design-spec.md +56 -0
- package/skills/frontend-implementation/references/node-contracts.md +31 -0
- package/skills/frontend-review/SKILL.md +15 -20
- package/skills/frontend-review/references/review-findings.md +7 -6
- package/skills/frontend-verification/SKILL.md +1 -1
- package/skills/loop-agent/references/command-reference.md +6 -1
- package/skills/loop-agent/references/hybrid-dag.md +2 -2
- package/dist/commands/dag-follow-up.js +0 -138
- package/dist/executors/pi-read-budget-policy.js +0 -239
- package/dist/worker/console/frontend-human-decision-adapter.js +0 -19
- package/dist/worker/console/frontend-split-operation-adapter.js +0 -20
- package/dist/worker/console/operation-store.js +0 -389
- package/dist/worker/console/static/assets/channel-bnXW0U3C.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-rZpq_twR.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-rZpq_twR.js +0 -1
- package/dist/worker/console/static/assets/index-D3CC4eUz.css +0 -1
- package/dist/worker/console/static/assets/index-DLt4ZFvD.js +0 -437
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-zBMYiUyi.js +0 -1
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-d_rms-5i.js +0 -8
- package/dist/worker/console/static-src/active-run-badge.js +0 -17
- package/dist/worker/materialize/frontend-split-task-materializer.js +0 -72
- package/dist/workflows/dag/dag-retry-schema.js +0 -138
- package/dist/workflows/dag/frontend-closeout.js +0 -221
- package/dist/workflows/dag/frontend-design-policy.js +0 -400
- package/dist/workflows/dag/frontend-human-decision.js +0 -182
- package/dist/workflows/dag/frontend-provider-capability-matrix.js +0 -159
- package/dist/workflows/dag/frontend-read-budgets.js +0 -12
- package/dist/workflows/dag/frontend-recovery-capsule.js +0 -455
- package/dist/workflows/dag/frontend-recovery-controller.js +0 -202
- package/dist/workflows/dag/frontend-recovery-lineage.js +0 -178
- package/dist/workflows/dag/frontend-review-findings.js +0 -270
- package/dist/workflows/dag/frontend-shadow-dual-write.js +0 -914
- package/dist/workflows/dag/frontend-shape-capsule-store.js +0 -191
- package/dist/workflows/dag/frontend-shape-facts.js +0 -409
- package/dist/workflows/dag/frontend-shape.js +0 -427
- package/dist/workflows/dag/frontend-source-fidelity-ledger.js +0 -108
- package/dist/workflows/dag/frontend-split-application-service.js +0 -203
- package/dist/workflows/dag/frontend-split-orchestrator.js +0 -899
- package/dist/workflows/dag/frontend-typed-event-store.js +0 -452
- package/dist/workflows/dag/frontend-typed-event-transaction.js +0 -180
- package/dist/workflows/dag/frontend-writer-admission.js +0 -285
- package/dist/workflows/dag/frontend-writer-status.js +0 -256
- package/dist/workflows/dag/recovery-lease.js +0 -80
- package/docs/templates/frontend-implementation-dag.json +0 -89
- package/docs/templates/spec-registry.schema.json +0 -45
- package/skills/frontend-bounded-implement/references/code-standards.md +0 -19
- package/skills/frontend-contract/SKILL.md +0 -23
- package/skills/frontend-contract/references/contract-protocol.md +0 -34
- package/skills/frontend-plan/SKILL.md +0 -26
- package/skills/frontend-plan/references/decision-contract.md +0 -37
- package/skills/frontend-plan/references/design-decisions.md +0 -17
- package/skills/frontend-scout/SKILL.md +0 -25
- package/skills/frontend-scout/references/design-evidence.md +0 -16
- package/skills/frontend-scout/references/scout-evidence.md +0 -23
- /package/dist/{worker → infrastructure}/console/repo-fingerprint.js +0 -0
|
@@ -0,0 +1,418 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
export const CONTEXT_TRANSFER_REASON_CODES = [
|
|
4
|
+
"CONTEXT_RECEIPT_MISSING",
|
|
5
|
+
"CONTEXT_COMPONENT_UNAVAILABLE",
|
|
6
|
+
"CONTEXT_BUNDLE_INEXACT",
|
|
7
|
+
"CONTEXT_BUNDLE_TOO_LARGE",
|
|
8
|
+
"RAW_EXPORT_ACKNOWLEDGEMENT_REQUIRED",
|
|
9
|
+
"SECRET_SCAN_CONFIRMATION_REQUIRED",
|
|
10
|
+
"ARTIFACT_BINDING_UNAVAILABLE",
|
|
11
|
+
"ARTIFACT_NOT_REVISION_ELIGIBLE",
|
|
12
|
+
"ARTIFACT_IDENTITY_AMBIGUOUS",
|
|
13
|
+
"ARTIFACT_REVISION_SCHEMA_INVALID",
|
|
14
|
+
"ARTIFACT_REVISION_PATH_INVALID",
|
|
15
|
+
"ARTIFACT_REVISION_EXPIRED",
|
|
16
|
+
"ARTIFACT_REVISION_DRIFT",
|
|
17
|
+
"ARTIFACT_REVISION_QUOTA_EXCEEDED",
|
|
18
|
+
"ARTIFACT_CONSUMER_UNKNOWN",
|
|
19
|
+
"ARTIFACT_IMPACT_UNSAFE",
|
|
20
|
+
"RERUN_PLAN_DRIFT",
|
|
21
|
+
"WORKER_MANAGED_CONTINUATION_UNSUPPORTED",
|
|
22
|
+
"WORKER_CONTINUATION_AUTHORITY_MISMATCH",
|
|
23
|
+
"WORKER_CONTINUATION_NEEDS_RECONCILE",
|
|
24
|
+
];
|
|
25
|
+
export const contextTransferReasonCodeSchema = z.enum(CONTEXT_TRANSFER_REASON_CODES);
|
|
26
|
+
export const CONTEXT_BUNDLE_LIMITS = Object.freeze({
|
|
27
|
+
previewBytes: 64 * 1024,
|
|
28
|
+
singleFileBytes: 16 * 1024 * 1024,
|
|
29
|
+
totalUncompressedBytes: 64 * 1024 * 1024,
|
|
30
|
+
fileCount: 2_048,
|
|
31
|
+
});
|
|
32
|
+
const sha256Schema = z.string().regex(/^[a-f0-9]{64}$/);
|
|
33
|
+
const safeRelativePosixPathSchema = z
|
|
34
|
+
.string()
|
|
35
|
+
.min(1)
|
|
36
|
+
.refine((value) => !value.includes("\0"), "path must not contain NUL")
|
|
37
|
+
.refine((value) => !value.includes("\\"), "path must use POSIX separators")
|
|
38
|
+
.refine((value) => !value.startsWith("/") &&
|
|
39
|
+
!value.startsWith("//") &&
|
|
40
|
+
!/^[A-Za-z]:/.test(value), "path must be relative")
|
|
41
|
+
.refine((value) => value.split("/").every((segment) => segment !== ".." && segment !== ""), "path must not escape or contain empty segments");
|
|
42
|
+
const contextComponentSchema = z
|
|
43
|
+
.object({
|
|
44
|
+
index: z.number().int().nonnegative(),
|
|
45
|
+
kind: z.enum([
|
|
46
|
+
"system-prompt",
|
|
47
|
+
"developer-prompt",
|
|
48
|
+
"user-prompt",
|
|
49
|
+
"task-contract",
|
|
50
|
+
"execution-constraint",
|
|
51
|
+
"context-file",
|
|
52
|
+
"upstream-artifact",
|
|
53
|
+
"tool-contract",
|
|
54
|
+
"runtime-fact",
|
|
55
|
+
]),
|
|
56
|
+
logicalName: z.string().min(1),
|
|
57
|
+
source: z
|
|
58
|
+
.object({
|
|
59
|
+
type: z.string().min(1),
|
|
60
|
+
identity: z.string().min(1),
|
|
61
|
+
revision: z.string().min(1).optional(),
|
|
62
|
+
})
|
|
63
|
+
.strict(),
|
|
64
|
+
materializedPath: safeRelativePosixPathSchema.optional(),
|
|
65
|
+
sha256: sha256Schema,
|
|
66
|
+
bytes: z.number().int().nonnegative(),
|
|
67
|
+
mediaType: z.string().min(1),
|
|
68
|
+
inclusion: z.enum([
|
|
69
|
+
"full",
|
|
70
|
+
"excerpt",
|
|
71
|
+
"summary",
|
|
72
|
+
"reference-only",
|
|
73
|
+
"omitted",
|
|
74
|
+
]),
|
|
75
|
+
authoritativeSource: z
|
|
76
|
+
.object({ identity: z.string().min(1), sha256: sha256Schema.optional() })
|
|
77
|
+
.strict()
|
|
78
|
+
.optional(),
|
|
79
|
+
omissionReason: z.string().min(1).optional(),
|
|
80
|
+
})
|
|
81
|
+
.strict();
|
|
82
|
+
export const effectiveNodeContextV1Schema = z
|
|
83
|
+
.object({
|
|
84
|
+
schemaVersion: z.literal(1),
|
|
85
|
+
identity: z
|
|
86
|
+
.object({
|
|
87
|
+
runId: z.string().min(1),
|
|
88
|
+
nodeId: z.string().min(1),
|
|
89
|
+
attempt: z.number().int().positive(),
|
|
90
|
+
capturedAt: z.string().min(1),
|
|
91
|
+
})
|
|
92
|
+
.strict(),
|
|
93
|
+
orderedComponents: z.array(contextComponentSchema),
|
|
94
|
+
promptAssembly: z
|
|
95
|
+
.object({
|
|
96
|
+
strategy: z.string().min(1),
|
|
97
|
+
finalPromptSha256: sha256Schema.optional(),
|
|
98
|
+
tokenEstimate: z.number().int().nonnegative().optional(),
|
|
99
|
+
truncations: z.array(z
|
|
100
|
+
.object({
|
|
101
|
+
componentIndex: z.number().int().nonnegative(),
|
|
102
|
+
reason: z.string().min(1),
|
|
103
|
+
retainedBytes: z.number().int().nonnegative().optional(),
|
|
104
|
+
})
|
|
105
|
+
.strict()),
|
|
106
|
+
})
|
|
107
|
+
.strict(),
|
|
108
|
+
boundaries: z
|
|
109
|
+
.object({
|
|
110
|
+
allowedPaths: z.array(z.string()),
|
|
111
|
+
forbiddenPaths: z.array(z.string()),
|
|
112
|
+
writeSet: z.array(z.string()),
|
|
113
|
+
})
|
|
114
|
+
.strict(),
|
|
115
|
+
runtime: z
|
|
116
|
+
.object({
|
|
117
|
+
executor: z.string().min(1),
|
|
118
|
+
modelHint: z.string().min(1).optional(),
|
|
119
|
+
resolvedModel: z.string().min(1).optional(),
|
|
120
|
+
skills: z.array(z.string()),
|
|
121
|
+
extensions: z.array(z.string()),
|
|
122
|
+
activeTools: z.array(z.string()),
|
|
123
|
+
})
|
|
124
|
+
.strict(),
|
|
125
|
+
contextDigest: sha256Schema,
|
|
126
|
+
})
|
|
127
|
+
.strict()
|
|
128
|
+
.superRefine((value, ctx) => {
|
|
129
|
+
value.orderedComponents.forEach((component, index) => {
|
|
130
|
+
if (component.index !== index) {
|
|
131
|
+
ctx.addIssue({
|
|
132
|
+
code: z.ZodIssueCode.custom,
|
|
133
|
+
path: ["orderedComponents", index, "index"],
|
|
134
|
+
message: "component indexes must be contiguous and ordered",
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
});
|
|
138
|
+
});
|
|
139
|
+
const bundleFileSchema = z
|
|
140
|
+
.object({
|
|
141
|
+
path: safeRelativePosixPathSchema,
|
|
142
|
+
sha256: sha256Schema,
|
|
143
|
+
bytes: z.number().int().nonnegative(),
|
|
144
|
+
mediaType: z.string().min(1),
|
|
145
|
+
origin: z.string().min(1),
|
|
146
|
+
redaction: z.enum(["none", "redacted", "omitted"]),
|
|
147
|
+
sourceSha256: sha256Schema.optional(),
|
|
148
|
+
})
|
|
149
|
+
.strict();
|
|
150
|
+
export const contextBundleManifestV1Schema = z
|
|
151
|
+
.object({
|
|
152
|
+
schemaVersion: z.literal(1),
|
|
153
|
+
bundleId: z.string().min(1),
|
|
154
|
+
createdAt: z.string().min(1),
|
|
155
|
+
source: z
|
|
156
|
+
.object({
|
|
157
|
+
repoIdentity: z.string().min(1),
|
|
158
|
+
taskId: z.string().min(1).optional(),
|
|
159
|
+
runId: z.string().min(1),
|
|
160
|
+
nodeId: z.string().min(1),
|
|
161
|
+
attempt: z.number().int().positive(),
|
|
162
|
+
pass: z.number().int().positive().optional(),
|
|
163
|
+
parentRunId: z.string().min(1).optional(),
|
|
164
|
+
})
|
|
165
|
+
.strict(),
|
|
166
|
+
profile: z.enum(["redacted", "raw"]),
|
|
167
|
+
target: z.enum(["generic", "pi-session", "pi-handoff", "readable"]),
|
|
168
|
+
reproducibility: z
|
|
169
|
+
.object({
|
|
170
|
+
level: z.enum([
|
|
171
|
+
"exact-pi-session",
|
|
172
|
+
"semantic-handoff",
|
|
173
|
+
"readable-transcript",
|
|
174
|
+
]),
|
|
175
|
+
reasons: z.array(z.string()),
|
|
176
|
+
limitations: z.array(z.string()),
|
|
177
|
+
})
|
|
178
|
+
.strict(),
|
|
179
|
+
bindings: z
|
|
180
|
+
.object({
|
|
181
|
+
frozenRunSpecSha256: sha256Schema,
|
|
182
|
+
contextReceiptSha256: sha256Schema,
|
|
183
|
+
contextDigest: sha256Schema,
|
|
184
|
+
taskContractSha256: sha256Schema.optional(),
|
|
185
|
+
sourceBindingSha256: sha256Schema.optional(),
|
|
186
|
+
})
|
|
187
|
+
.strict(),
|
|
188
|
+
runtime: z
|
|
189
|
+
.object({
|
|
190
|
+
executor: z.string().min(1),
|
|
191
|
+
model: z.string().min(1).optional(),
|
|
192
|
+
provider: z.string().min(1).optional(),
|
|
193
|
+
piVersion: z.string().min(1).optional(),
|
|
194
|
+
skills: z.array(z
|
|
195
|
+
.object({
|
|
196
|
+
id: z.string().min(1),
|
|
197
|
+
version: z.string().min(1).optional(),
|
|
198
|
+
sha256: sha256Schema.optional(),
|
|
199
|
+
})
|
|
200
|
+
.strict()),
|
|
201
|
+
extensions: z.array(z
|
|
202
|
+
.object({
|
|
203
|
+
id: z.string().min(1),
|
|
204
|
+
packageName: z.string().min(1).optional(),
|
|
205
|
+
version: z.string().min(1).optional(),
|
|
206
|
+
status: z.string().min(1),
|
|
207
|
+
})
|
|
208
|
+
.strict()),
|
|
209
|
+
tools: z.array(z
|
|
210
|
+
.object({
|
|
211
|
+
name: z.string().min(1),
|
|
212
|
+
contractSha256: sha256Schema.optional(),
|
|
213
|
+
available: z.boolean(),
|
|
214
|
+
})
|
|
215
|
+
.strict()),
|
|
216
|
+
})
|
|
217
|
+
.strict(),
|
|
218
|
+
files: z.array(bundleFileSchema),
|
|
219
|
+
omissions: z.array(z
|
|
220
|
+
.object({
|
|
221
|
+
subject: z.string().min(1),
|
|
222
|
+
reasonCode: z.string().min(1),
|
|
223
|
+
detail: z.string().min(1).optional(),
|
|
224
|
+
})
|
|
225
|
+
.strict()),
|
|
226
|
+
security: z
|
|
227
|
+
.object({
|
|
228
|
+
secretScan: z.enum(["pass", "warning", "not-run"]),
|
|
229
|
+
findingCounts: z.record(z.string(), z.number().int().nonnegative()),
|
|
230
|
+
rawAcknowledgement: z
|
|
231
|
+
.object({
|
|
232
|
+
requestId: z.string().min(1),
|
|
233
|
+
acknowledgedAt: z.string().min(1),
|
|
234
|
+
})
|
|
235
|
+
.strict()
|
|
236
|
+
.optional(),
|
|
237
|
+
})
|
|
238
|
+
.strict(),
|
|
239
|
+
bundleSha256: sha256Schema,
|
|
240
|
+
})
|
|
241
|
+
.strict()
|
|
242
|
+
.superRefine((value, ctx) => {
|
|
243
|
+
const seen = new Set();
|
|
244
|
+
for (const [index, file] of value.files.entries()) {
|
|
245
|
+
const folded = file.path.normalize("NFC").toLocaleLowerCase("en-US");
|
|
246
|
+
if (seen.has(folded)) {
|
|
247
|
+
ctx.addIssue({
|
|
248
|
+
code: z.ZodIssueCode.custom,
|
|
249
|
+
path: ["files", index, "path"],
|
|
250
|
+
message: "bundle file paths must be unique under case folding",
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
seen.add(folded);
|
|
254
|
+
}
|
|
255
|
+
if (value.profile === "raw" && !value.security.rawAcknowledgement) {
|
|
256
|
+
ctx.addIssue({
|
|
257
|
+
code: z.ZodIssueCode.custom,
|
|
258
|
+
path: ["security", "rawAcknowledgement"],
|
|
259
|
+
message: "raw bundle requires explicit acknowledgement",
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
});
|
|
263
|
+
export const artifactRevisionV1Schema = z
|
|
264
|
+
.object({
|
|
265
|
+
schemaVersion: z.literal(1),
|
|
266
|
+
revisionId: z.string().min(1),
|
|
267
|
+
state: z.enum([
|
|
268
|
+
"staged",
|
|
269
|
+
"validated",
|
|
270
|
+
"rejected",
|
|
271
|
+
"expired",
|
|
272
|
+
"consumed",
|
|
273
|
+
"discarded",
|
|
274
|
+
]),
|
|
275
|
+
parent: z
|
|
276
|
+
.object({
|
|
277
|
+
runId: z.string().min(1),
|
|
278
|
+
nodeId: z.string().min(1),
|
|
279
|
+
artifactPath: safeRelativePosixPathSchema,
|
|
280
|
+
artifactSha256: sha256Schema,
|
|
281
|
+
artifactSchemaId: z.string().min(1).optional(),
|
|
282
|
+
})
|
|
283
|
+
.strict(),
|
|
284
|
+
candidate: z
|
|
285
|
+
.object({
|
|
286
|
+
stagingPath: z.string().min(1),
|
|
287
|
+
sha256: sha256Schema,
|
|
288
|
+
bytes: z.number().int().nonnegative(),
|
|
289
|
+
mediaType: z.string().min(1),
|
|
290
|
+
uploadedAt: z.string().min(1),
|
|
291
|
+
uploadedBy: z.literal("operator"),
|
|
292
|
+
})
|
|
293
|
+
.strict(),
|
|
294
|
+
validation: z
|
|
295
|
+
.object({
|
|
296
|
+
status: z.enum(["pending", "pass", "fail"]),
|
|
297
|
+
checks: z.array(z
|
|
298
|
+
.object({
|
|
299
|
+
id: z.string().min(1),
|
|
300
|
+
status: z.enum(["pass", "warning", "fail"]),
|
|
301
|
+
detail: z.string().min(1).optional(),
|
|
302
|
+
})
|
|
303
|
+
.strict()),
|
|
304
|
+
})
|
|
305
|
+
.strict(),
|
|
306
|
+
diff: z
|
|
307
|
+
.object({
|
|
308
|
+
kind: z.enum(["text", "json", "binary"]),
|
|
309
|
+
summary: z.string(),
|
|
310
|
+
previewPath: safeRelativePosixPathSchema.optional(),
|
|
311
|
+
truncated: z.boolean(),
|
|
312
|
+
})
|
|
313
|
+
.strict(),
|
|
314
|
+
expiresAt: z.string().min(1),
|
|
315
|
+
/** Explicit staging lease (M4c): fresh lease blocks conflicting holders. */
|
|
316
|
+
lease: z
|
|
317
|
+
.object({
|
|
318
|
+
holder: z.string().min(1),
|
|
319
|
+
acquiredAt: z.string().min(1),
|
|
320
|
+
expiresAt: z.string().min(1),
|
|
321
|
+
})
|
|
322
|
+
.strict()
|
|
323
|
+
.optional(),
|
|
324
|
+
})
|
|
325
|
+
.strict();
|
|
326
|
+
export const artifactOverrideSetV1Schema = z
|
|
327
|
+
.object({
|
|
328
|
+
schemaVersion: z.literal(1),
|
|
329
|
+
overrides: z
|
|
330
|
+
.array(z
|
|
331
|
+
.object({
|
|
332
|
+
revisionId: z.string().min(1),
|
|
333
|
+
parentArtifact: z
|
|
334
|
+
.object({
|
|
335
|
+
runId: z.string().min(1),
|
|
336
|
+
nodeId: z.string().min(1),
|
|
337
|
+
path: safeRelativePosixPathSchema,
|
|
338
|
+
sha256: sha256Schema,
|
|
339
|
+
})
|
|
340
|
+
.strict(),
|
|
341
|
+
replacement: z
|
|
342
|
+
.object({
|
|
343
|
+
sha256: sha256Schema,
|
|
344
|
+
bytes: z.number().int().nonnegative(),
|
|
345
|
+
mediaType: z.string().min(1),
|
|
346
|
+
})
|
|
347
|
+
.strict(),
|
|
348
|
+
supersedes: z.string().min(1),
|
|
349
|
+
})
|
|
350
|
+
.strict())
|
|
351
|
+
.length(1),
|
|
352
|
+
overrideSetHash: sha256Schema,
|
|
353
|
+
})
|
|
354
|
+
.strict();
|
|
355
|
+
const workerContinuationIdSchema = z
|
|
356
|
+
.string()
|
|
357
|
+
.min(1)
|
|
358
|
+
.regex(/^[A-Za-z0-9._-]+$/);
|
|
359
|
+
/**
|
|
360
|
+
* Console-to-kernel authority for a Worker-owned artifact continuation.
|
|
361
|
+
* Every field is immutable and plan-hash-bound; the child identity is derived
|
|
362
|
+
* from the parent Worker run plus the single-use revision id.
|
|
363
|
+
*/
|
|
364
|
+
export const workerContinuationAuthorityV1Schema = z
|
|
365
|
+
.object({
|
|
366
|
+
schemaVersion: z.literal(1),
|
|
367
|
+
parentWorkerRunId: workerContinuationIdSchema,
|
|
368
|
+
childWorkerRunId: workerContinuationIdSchema,
|
|
369
|
+
featureId: workerContinuationIdSchema,
|
|
370
|
+
taskId: workerContinuationIdSchema,
|
|
371
|
+
batchRunId: workerContinuationIdSchema,
|
|
372
|
+
revisionId: workerContinuationIdSchema,
|
|
373
|
+
})
|
|
374
|
+
.strict()
|
|
375
|
+
.superRefine((authority, ctx) => {
|
|
376
|
+
const expected = deriveWorkerContinuationRunId(authority.parentWorkerRunId, authority.revisionId);
|
|
377
|
+
if (authority.childWorkerRunId !== expected) {
|
|
378
|
+
ctx.addIssue({
|
|
379
|
+
code: z.ZodIssueCode.custom,
|
|
380
|
+
path: ["childWorkerRunId"],
|
|
381
|
+
message: `childWorkerRunId must equal ${expected}`,
|
|
382
|
+
});
|
|
383
|
+
}
|
|
384
|
+
});
|
|
385
|
+
export function deriveWorkerContinuationRunId(parentWorkerRunId, revisionId) {
|
|
386
|
+
const digest = createHash("sha256")
|
|
387
|
+
.update(`worker-continuation-v1\0${parentWorkerRunId}\0${revisionId}`)
|
|
388
|
+
.digest("hex")
|
|
389
|
+
.slice(0, 32);
|
|
390
|
+
return `wrc-${digest}`;
|
|
391
|
+
}
|
|
392
|
+
export function sortKeysDeep(value) {
|
|
393
|
+
if (Array.isArray(value))
|
|
394
|
+
return value.map(sortKeysDeep);
|
|
395
|
+
if (value && typeof value === "object") {
|
|
396
|
+
return Object.fromEntries(Object.entries(value)
|
|
397
|
+
.sort(([a], [b]) => a.localeCompare(b))
|
|
398
|
+
.map(([key, child]) => [key, sortKeysDeep(child)]));
|
|
399
|
+
}
|
|
400
|
+
return value;
|
|
401
|
+
}
|
|
402
|
+
export function canonicalSha256(value) {
|
|
403
|
+
return createHash("sha256")
|
|
404
|
+
.update(JSON.stringify(sortKeysDeep(value)))
|
|
405
|
+
.digest("hex");
|
|
406
|
+
}
|
|
407
|
+
export function computeContextDigest(context) {
|
|
408
|
+
return canonicalSha256(context);
|
|
409
|
+
}
|
|
410
|
+
export function computeBundleManifestSha256(manifest) {
|
|
411
|
+
return canonicalSha256(manifest);
|
|
412
|
+
}
|
|
413
|
+
export function computeOverrideSetHash(overrides) {
|
|
414
|
+
return canonicalSha256({ schemaVersion: 1, overrides });
|
|
415
|
+
}
|
|
416
|
+
export function isKnownContextTransferReasonCode(value) {
|
|
417
|
+
return contextTransferReasonCodeSchema.safeParse(value).success;
|
|
418
|
+
}
|
|
@@ -47,22 +47,10 @@ const RAW_TO_NORMALIZED = {
|
|
|
47
47
|
"verify-failure": "validation",
|
|
48
48
|
"test-failure": "validation",
|
|
49
49
|
"verification-plan-stale": "validation",
|
|
50
|
-
"read-burst": "validation",
|
|
51
|
-
// Writer persisted real file changes but the final provider call hit a
|
|
52
|
-
// context overflow; verification never ran. Distinct from empty-output so
|
|
53
|
-
// the report shows partial success instead of a spec issue.
|
|
54
|
-
"partial-success-with-context-overflow": "validation",
|
|
55
|
-
// Writer burned an excessive token budget (unconverged read-edit-test loop)
|
|
56
|
-
// and still failed; distinct from empty-output for accurate reporting.
|
|
57
|
-
"writer-budget-exhausted": "validation",
|
|
58
50
|
network: "executor",
|
|
59
51
|
quota: "executor",
|
|
60
52
|
"rate-limit": "executor",
|
|
61
53
|
unavailable: "executor",
|
|
62
|
-
// Context window overflow (400 request-too-large): the session needs
|
|
63
|
-
// compaction / fresh context before retrying — an executor-side
|
|
64
|
-
// environment condition, not a plan-quality defect.
|
|
65
|
-
"context-overflow": "executor",
|
|
66
54
|
// Deterministic capability/policy refusal: missing verified launcher JS
|
|
67
55
|
// entry, capability allowlist rejection, or missing bounded write tool
|
|
68
56
|
// profile. These are environment/config defects the operator repairs
|
|
@@ -12,19 +12,6 @@ export const OPENSPEC_SPEC_DIRS = [
|
|
|
12
12
|
"openspec/project-specs",
|
|
13
13
|
"ai_workspace",
|
|
14
14
|
];
|
|
15
|
-
/** Default roots used when a frontend task does not declare specRoots. */
|
|
16
|
-
export const DEFAULT_FRONTEND_SPEC_ROOTS = OPENSPEC_SPEC_DIRS;
|
|
17
|
-
/** A task-owned spec root must be a normalized, repo-relative POSIX directory. */
|
|
18
|
-
export function isValidFrontendSpecRoot(value) {
|
|
19
|
-
return (Boolean(value) &&
|
|
20
|
-
!value.includes("\\") &&
|
|
21
|
-
!value.startsWith("/") &&
|
|
22
|
-
!value.includes("//") &&
|
|
23
|
-
!value.split("/").some((segment) => !segment ||
|
|
24
|
-
segment === "." ||
|
|
25
|
-
segment === ".." ||
|
|
26
|
-
/\s/.test(segment)));
|
|
27
|
-
}
|
|
28
15
|
/** Allowed spec file extensions (case-insensitive). */
|
|
29
16
|
export const OPENSPEC_SPEC_EXT_RE = /\.(md|mdx|json|yaml|yml)$/i;
|
|
30
17
|
/**
|
|
@@ -56,17 +43,14 @@ export function isGovernanceSubtreePath(filePath, governanceRoot) {
|
|
|
56
43
|
* directories. `filePath` must be a repo-relative POSIX path (backslashes are
|
|
57
44
|
* normalised internally). Directory names are case-sensitive.
|
|
58
45
|
*/
|
|
59
|
-
export function
|
|
46
|
+
export function isOpenspecSpecPath(filePath) {
|
|
60
47
|
const normalized = filePath.replaceAll("\\", "/");
|
|
61
|
-
for (const dir of
|
|
48
|
+
for (const dir of OPENSPEC_SPEC_DIRS) {
|
|
62
49
|
if (normalized === dir || normalized.startsWith(dir + "/"))
|
|
63
50
|
return true;
|
|
64
51
|
}
|
|
65
52
|
return false;
|
|
66
53
|
}
|
|
67
|
-
export function isOpenspecSpecPath(filePath) {
|
|
68
|
-
return isFrontendSpecPath(filePath);
|
|
69
|
-
}
|
|
70
54
|
/**
|
|
71
55
|
* Returns `true` when `filename` (the basename only, not a full path) has an
|
|
72
56
|
* allowed frontend specification extension.
|
|
@@ -75,12 +59,9 @@ export function isValidOpenspecExtension(filename) {
|
|
|
75
59
|
return OPENSPEC_SPEC_EXT_RE.test(filename);
|
|
76
60
|
}
|
|
77
61
|
/** Returns `true` for a supported file inside a canonical openspec spec dir. */
|
|
78
|
-
export function isFrontendSpecFilePath(filePath, specRoots = DEFAULT_FRONTEND_SPEC_ROOTS) {
|
|
79
|
-
return (isFrontendSpecPath(filePath, specRoots) &&
|
|
80
|
-
isValidOpenspecExtension(filePath.replaceAll("\\", "/")));
|
|
81
|
-
}
|
|
82
62
|
export function isOpenspecSpecFilePath(filePath) {
|
|
83
|
-
return
|
|
63
|
+
return (isOpenspecSpecPath(filePath) &&
|
|
64
|
+
isValidOpenspecExtension(filePath.replaceAll("\\", "/")));
|
|
84
65
|
}
|
|
85
66
|
/**
|
|
86
67
|
* Returns `true` when free-form search input references a canonical frontend
|
|
@@ -142,50 +123,3 @@ export function extractTaskSourceOpenspecPaths(markdown, governanceRoot = DEFAUL
|
|
|
142
123
|
!isGovernanceSubtreePath(token, governanceRoot))
|
|
143
124
|
.sort();
|
|
144
125
|
}
|
|
145
|
-
/**
|
|
146
|
-
* Extract explicit specification-file citations for a task's frozen spec roots.
|
|
147
|
-
* This is intentionally path-only: source preparation decides which roots are
|
|
148
|
-
* normative, while DAG execution only consumes that frozen configuration.
|
|
149
|
-
*/
|
|
150
|
-
export function extractTaskSourceFrontendSpecPaths(markdown, specRoots, governanceRoot = DEFAULT_OPENSPEC_GOVERNANCE_ROOT) {
|
|
151
|
-
if (!markdown)
|
|
152
|
-
return [];
|
|
153
|
-
const raw = new Set();
|
|
154
|
-
const collect = (token) => {
|
|
155
|
-
const normalized = normalizeCitationToken(token);
|
|
156
|
-
if (normalized)
|
|
157
|
-
raw.add(normalized);
|
|
158
|
-
};
|
|
159
|
-
for (const match of markdown.matchAll(/`([^`\n]+)`/g))
|
|
160
|
-
collect(match[1] ?? "");
|
|
161
|
-
for (const match of markdown.matchAll(/\[[^\]]*\]\(([^)\s]+)\)/g))
|
|
162
|
-
collect(match[1] ?? "");
|
|
163
|
-
for (const root of specRoots) {
|
|
164
|
-
const escaped = root.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
165
|
-
for (const match of markdown.matchAll(new RegExp(`(?:^|[\\s\"'([>])(${escaped}/[A-Za-z0-9._/-]+)`, "g"))) {
|
|
166
|
-
collect(match[1] ?? "");
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
return [...raw]
|
|
170
|
-
.filter((token) => isFrontendSpecFilePath(token, specRoots) && !isGovernanceSubtreePath(token, governanceRoot))
|
|
171
|
-
.sort();
|
|
172
|
-
}
|
|
173
|
-
/**
|
|
174
|
-
* Source-preparation helper for explicitly named normative directories. A root
|
|
175
|
-
* is inferred only on a line that declares a spec/规范/design-system intent;
|
|
176
|
-
* arbitrary Markdown links never silently broaden task configuration.
|
|
177
|
-
*/
|
|
178
|
-
export function extractDeclaredFrontendSpecRoots(markdown) {
|
|
179
|
-
const roots = new Set();
|
|
180
|
-
for (const line of markdown.split(/\r?\n/)) {
|
|
181
|
-
if (!/(?:规范|规约|标准|设计系统|\bspec(?:ification)?\b|\bdesign[ -]?system\b|\bguidelines?\b|\brules?\b)/i.test(line))
|
|
182
|
-
continue;
|
|
183
|
-
for (const match of line.matchAll(/`([^`]+)`|\[[^\]]+\]\(([^)\s]+)\)/g)) {
|
|
184
|
-
const raw = (match[1] ?? match[2] ?? "").replaceAll("\\", "/").replace(/\/+$/, "");
|
|
185
|
-
if (!raw || !isValidFrontendSpecRoot(raw))
|
|
186
|
-
continue;
|
|
187
|
-
roots.add(isValidOpenspecExtension(raw) ? raw.split("/").slice(0, -1).join("/") : raw);
|
|
188
|
-
}
|
|
189
|
-
}
|
|
190
|
-
return [...roots].filter(isValidFrontendSpecRoot).sort();
|
|
191
|
-
}
|
|
@@ -2240,13 +2240,6 @@ export const OPERATOR_COMMAND_COVERAGE = Object.freeze([
|
|
|
2240
2240
|
source: "loop-agent",
|
|
2241
2241
|
note: "server-side read-only run-facts check (ineligible plan / explicit rerun-task recommendation) replaces the browser Human Gate (2026-08-11).",
|
|
2242
2242
|
},
|
|
2243
|
-
{
|
|
2244
|
-
command: "loop-agent dag follow-up",
|
|
2245
|
-
coverage: "excluded",
|
|
2246
|
-
action: null,
|
|
2247
|
-
source: "loop-agent",
|
|
2248
|
-
exclusionReason: "Frontend review recovery helper is a direct advanced CLI composition (create/import task and optional advance), not an Operator action endpoint.",
|
|
2249
|
-
},
|
|
2250
2243
|
{
|
|
2251
2244
|
command: "loop-agent dag reconcile-tasks",
|
|
2252
2245
|
coverage: "human-gated-required",
|
|
@@ -2259,6 +2252,20 @@ export const OPERATOR_COMMAND_COVERAGE = Object.freeze([
|
|
|
2259
2252
|
action: "dagFinalVerification",
|
|
2260
2253
|
source: "loop-agent",
|
|
2261
2254
|
},
|
|
2255
|
+
{
|
|
2256
|
+
command: "loop-agent dag context",
|
|
2257
|
+
coverage: "excluded",
|
|
2258
|
+
action: null,
|
|
2259
|
+
source: "loop-agent",
|
|
2260
|
+
exclusionReason: "Context export can disclose raw execution bytes and requires a human-selected output path; Console uses dedicated gated actions instead of the generic CLI wrapper.",
|
|
2261
|
+
},
|
|
2262
|
+
{
|
|
2263
|
+
command: "loop-agent dag artifact",
|
|
2264
|
+
coverage: "excluded",
|
|
2265
|
+
action: null,
|
|
2266
|
+
source: "loop-agent",
|
|
2267
|
+
exclusionReason: "Artifact revision staging consumes a human-selected local file and remains on dedicated Console/CLI mutation gates.",
|
|
2268
|
+
},
|
|
2262
2269
|
{
|
|
2263
2270
|
command: "loop-agent workflow list",
|
|
2264
2271
|
coverage: "advanced",
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { constants as fsConstants } from "node:fs";
|
|
2
|
+
import { lstat, open, realpath } from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
function assertContained(root, target, label) {
|
|
5
|
+
if (target === root || !target.startsWith(`${root}${path.sep}`)) {
|
|
6
|
+
throw new Error(`${label}: path escapes boundary: ${target}`);
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Reject any symlink on the path between `boundaryRoot` and `targetPath`.
|
|
11
|
+
* This is the lexical/path-walk phase; callers that consume file bytes must
|
|
12
|
+
* still use readFileBoundarySafe so the checked object is the opened object.
|
|
13
|
+
*/
|
|
14
|
+
export async function assertRealPathInsideBoundary(input) {
|
|
15
|
+
const root = path.resolve(input.boundaryRoot);
|
|
16
|
+
const target = path.resolve(input.targetPath);
|
|
17
|
+
assertContained(root, target, input.label);
|
|
18
|
+
const rootStat = await lstat(root).catch(() => undefined);
|
|
19
|
+
if (!rootStat) {
|
|
20
|
+
throw new Error(`${input.label}: boundary root is not a real directory`);
|
|
21
|
+
}
|
|
22
|
+
if (rootStat.isSymbolicLink()) {
|
|
23
|
+
throw new Error(`${input.label}: boundary root must not be a symlink`);
|
|
24
|
+
}
|
|
25
|
+
if (!rootStat.isDirectory()) {
|
|
26
|
+
throw new Error(`${input.label}: boundary root is not a real directory`);
|
|
27
|
+
}
|
|
28
|
+
const segments = path.relative(root, target).split(path.sep).filter(Boolean);
|
|
29
|
+
let current = root;
|
|
30
|
+
for (let index = 0; index < segments.length; index += 1) {
|
|
31
|
+
current = path.join(current, segments[index]);
|
|
32
|
+
const stat = await lstat(current).catch(() => undefined);
|
|
33
|
+
if (!stat) {
|
|
34
|
+
throw new Error(`${input.label}: path component missing: ${segments[index]}`);
|
|
35
|
+
}
|
|
36
|
+
if (stat.isSymbolicLink()) {
|
|
37
|
+
throw new Error(`${input.label}: symlink is forbidden on path: ${segments[index]}`);
|
|
38
|
+
}
|
|
39
|
+
if (index < segments.length - 1 && !stat.isDirectory()) {
|
|
40
|
+
throw new Error(`${input.label}: path ancestor is not a directory: ${segments[index]}`);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return target;
|
|
44
|
+
}
|
|
45
|
+
/** Reject a non-regular, symlinked, or hardlinked final entry before opening. */
|
|
46
|
+
export async function assertReadableRegularFile(input) {
|
|
47
|
+
const target = await assertRealPathInsideBoundary(input);
|
|
48
|
+
const stat = await lstat(target).catch(() => undefined);
|
|
49
|
+
if (!stat)
|
|
50
|
+
throw new Error(`${input.label}: path component missing`);
|
|
51
|
+
if (!stat.isFile() || stat.isSymbolicLink() || stat.nlink !== 1) {
|
|
52
|
+
throw new Error(`${input.label}: path must be a regular single-link file`);
|
|
53
|
+
}
|
|
54
|
+
return target;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Read one owned regular file without reopening an unchecked pathname.
|
|
58
|
+
*
|
|
59
|
+
* The preflight rejects normal FIFO/device/hardlink cases without opening
|
|
60
|
+
* them. O_NOFOLLOW rejects a final symlink introduced after preflight and
|
|
61
|
+
* O_NONBLOCK prevents a swapped FIFO from blocking. The post-open identity and
|
|
62
|
+
* realpath checks make an ancestor swap detectable before bytes are consumed.
|
|
63
|
+
*/
|
|
64
|
+
export async function readFileBoundarySafe(input) {
|
|
65
|
+
const target = await assertReadableRegularFile(input);
|
|
66
|
+
const flags = fsConstants.O_RDONLY |
|
|
67
|
+
(fsConstants.O_NOFOLLOW ?? 0) |
|
|
68
|
+
(fsConstants.O_NONBLOCK ?? 0);
|
|
69
|
+
const handle = await open(target, flags);
|
|
70
|
+
try {
|
|
71
|
+
const opened = await handle.stat();
|
|
72
|
+
if (!opened.isFile() || opened.nlink !== 1) {
|
|
73
|
+
throw new Error(`${input.label}: path must be a regular single-link file`);
|
|
74
|
+
}
|
|
75
|
+
const [resolvedRoot, resolvedTarget, current] = await Promise.all([
|
|
76
|
+
realpath(path.resolve(input.boundaryRoot)),
|
|
77
|
+
realpath(target),
|
|
78
|
+
lstat(target),
|
|
79
|
+
]);
|
|
80
|
+
assertContained(resolvedRoot, resolvedTarget, input.label);
|
|
81
|
+
if (current.isSymbolicLink() ||
|
|
82
|
+
!current.isFile() ||
|
|
83
|
+
current.nlink !== 1 ||
|
|
84
|
+
current.dev !== opened.dev ||
|
|
85
|
+
current.ino !== opened.ino) {
|
|
86
|
+
throw new Error(`${input.label}: path changed while opening`);
|
|
87
|
+
}
|
|
88
|
+
return await handle.readFile();
|
|
89
|
+
}
|
|
90
|
+
finally {
|
|
91
|
+
await handle.close().catch(() => { });
|
|
92
|
+
}
|
|
93
|
+
}
|