@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
package/dist/shared/preview.js
CHANGED
|
@@ -1,10 +1,34 @@
|
|
|
1
1
|
const ENCODER = new TextEncoder();
|
|
2
2
|
const DECODER = new TextDecoder();
|
|
3
|
+
export function redactSecretsWithFindings(text) {
|
|
4
|
+
let findingCount = 0;
|
|
5
|
+
const replaceChanged = (match, replacement) => {
|
|
6
|
+
if (match !== replacement)
|
|
7
|
+
findingCount += 1;
|
|
8
|
+
return replacement;
|
|
9
|
+
};
|
|
10
|
+
let redacted = text.replace(/(["']?(?:api[_-]?key|token|password|secret|authorization)["']?\s*[:=]\s*["']?)((?:Bearer\s+)?[^"'\s,}]+)/gi, (match, prefix) => replaceChanged(match, `${prefix}***`));
|
|
11
|
+
redacted = redacted.replace(/(\b[A-Z][A-Z0-9_]*=)[^\s]+/g, (match, prefix) => replaceChanged(match, `${prefix}***`));
|
|
12
|
+
redacted = redacted.replace(/\bBearer\s+[^\s"']+/gi, (match) => replaceChanged(match, "Bearer ***"));
|
|
13
|
+
// High-confidence provider-style tokens (OpenAI sk-, GitHub ghp_/gho_,
|
|
14
|
+
// Slack xox*, AWS AKIA) are credential-like even without a key=value
|
|
15
|
+
// prefix (exec-plan 2026-08-25 D4: credential-like 默认阻断).
|
|
16
|
+
redacted = redacted.replace(/\b(?:sk-[A-Za-z0-9_-]{16,}|gh[pos]_[A-Za-z0-9]{20,}|xox[baprs]-[A-Za-z0-9-]{10,}|AKIA[0-9A-Z]{16})\b/g, (match) => replaceChanged(match, "***"));
|
|
17
|
+
return { redacted, findingCount };
|
|
18
|
+
}
|
|
19
|
+
export function redactSensitivePathsWithFindings(text) {
|
|
20
|
+
let findingCount = 0;
|
|
21
|
+
const replaceHome = (match) => {
|
|
22
|
+
if (match !== "<redacted-home>")
|
|
23
|
+
findingCount += 1;
|
|
24
|
+
return "<redacted-home>";
|
|
25
|
+
};
|
|
26
|
+
let redacted = text.replace(/\/(?:Users|home)\/[^/\s"'\\]+/g, replaceHome);
|
|
27
|
+
redacted = redacted.replace(/\b[A-Za-z]:\\Users\\[^\\/\s"']+/gi, replaceHome);
|
|
28
|
+
return { redacted, findingCount };
|
|
29
|
+
}
|
|
3
30
|
export function redactSecrets(text) {
|
|
4
|
-
return text
|
|
5
|
-
.replace(/\bBearer\s+[^\s"']+/gi, "Bearer ***")
|
|
6
|
-
.replace(/(\b[A-Z][A-Z0-9_]*=)[^\s]+/g, "$1***")
|
|
7
|
-
.replace(/(["']?(?:api[_-]?key|token|password|secret|authorization)["']?\s*[:=]\s*["']?)([^"'\s,}]+)/gi, "$1***");
|
|
31
|
+
return redactSecretsWithFindings(text).redacted;
|
|
8
32
|
}
|
|
9
33
|
export function truncateUtf8Preview(text, maxBytes = 4096) {
|
|
10
34
|
if (maxBytes <= 0)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import {
|
|
2
|
+
import { isOpenspecSpecFilePath } from "../shared/openspec-spec.js";
|
|
3
3
|
export const taskFlowSchema = z.enum([
|
|
4
4
|
"auto",
|
|
5
5
|
"micro",
|
|
@@ -14,6 +14,7 @@ export const taskKindSchema = z.enum([
|
|
|
14
14
|
"frontend-implementation",
|
|
15
15
|
"frontend-test",
|
|
16
16
|
"backend-test",
|
|
17
|
+
"backend-test-analysis",
|
|
17
18
|
"knowledge-sync",
|
|
18
19
|
"knowledge-graph-bootstrap",
|
|
19
20
|
]);
|
|
@@ -76,27 +77,13 @@ export const frontendOpenspecPolicySchema = z.enum(["cited", "scan-strict"]);
|
|
|
76
77
|
*/
|
|
77
78
|
export const frontendOpenspecConfigSchema = z.object({
|
|
78
79
|
policy: frontendOpenspecPolicySchema.optional().default("cited"),
|
|
79
|
-
/**
|
|
80
|
-
* Repo-relative directories containing frontend normative files. These are
|
|
81
|
-
* frozen in task.json during contract preparation, before DAG generation.
|
|
82
|
-
*/
|
|
83
|
-
specRoots: z
|
|
84
|
-
.array(z.string().refine((candidate) => isValidFrontendSpecRoot(candidate), "frontendOpenspec.specRoots entries must be normalized repo-relative directories"))
|
|
85
|
-
.optional(),
|
|
86
80
|
requiredReadPaths: z
|
|
87
|
-
.array(z
|
|
81
|
+
.array(z
|
|
82
|
+
.string()
|
|
83
|
+
.refine((candidate) => !candidate.includes("\\") &&
|
|
84
|
+
isOpenspecSpecFilePath(candidate), "frontendOpenspec.requiredReadPaths entries must be repo-relative supported files under openspec/schemas/, openspec/project-specs/, or ai_workspace/"))
|
|
88
85
|
.optional()
|
|
89
86
|
.default([]),
|
|
90
|
-
}).superRefine((value, ctx) => {
|
|
91
|
-
const roots = value.specRoots ?? DEFAULT_FRONTEND_SPEC_ROOTS;
|
|
92
|
-
if (new Set(roots).size !== roots.length) {
|
|
93
|
-
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "frontendOpenspec.specRoots must not contain duplicates", path: ["specRoots"] });
|
|
94
|
-
}
|
|
95
|
-
for (const required of value.requiredReadPaths) {
|
|
96
|
-
if (!isFrontendSpecFilePath(required, roots)) {
|
|
97
|
-
ctx.addIssue({ code: z.ZodIssueCode.custom, message: "frontendOpenspec.requiredReadPaths entries must be repo-relative supported files under frontendOpenspec.specRoots", path: ["requiredReadPaths"] });
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
87
|
});
|
|
101
88
|
/** Batch limits and gate profile for the browser-driven frontend test DAG.
|
|
102
89
|
* Token fields are post-case stop thresholds, not model-provider hard caps. */
|
|
@@ -144,6 +131,75 @@ export const frontendTestConfigSchema = z.object({
|
|
|
144
131
|
.strict()
|
|
145
132
|
.optional(),
|
|
146
133
|
});
|
|
134
|
+
const backendTestBudgetProofSchema = z
|
|
135
|
+
.object({
|
|
136
|
+
groupId: z.string().min(1),
|
|
137
|
+
estimatedCaseCount: z.number().int().positive(),
|
|
138
|
+
estimatedTestPointCount: z.number().int().positive(),
|
|
139
|
+
estimatedOutputChars: z.number().int().positive(),
|
|
140
|
+
maxOutputCharsPerWriter: z.number().int().positive(),
|
|
141
|
+
estimatorVersion: z.literal("backend-test-output-estimator-v1"),
|
|
142
|
+
inputSha256: z.string().regex(/^[a-f0-9]{64}$/),
|
|
143
|
+
})
|
|
144
|
+
.strict();
|
|
145
|
+
const backendTestModuleLayoutEntrySchema = z
|
|
146
|
+
.object({
|
|
147
|
+
stem: z.string().regex(/^[a-z][a-z0-9_]*$/),
|
|
148
|
+
businessResource: z.string().min(1),
|
|
149
|
+
markdownPath: z.string().min(1),
|
|
150
|
+
pytestPath: z.string().min(1),
|
|
151
|
+
ownedOperations: z.array(z.string().regex(/^(?:GET|POST|PUT|PATCH|DELETE|HEAD|OPTIONS) \/\S+$/)).min(1),
|
|
152
|
+
splitReason: z.enum([
|
|
153
|
+
"explicit-user-layout",
|
|
154
|
+
"primary-business-resource",
|
|
155
|
+
"independent-business-resource",
|
|
156
|
+
"output-budget",
|
|
157
|
+
]),
|
|
158
|
+
budgetProof: backendTestBudgetProofSchema.optional(),
|
|
159
|
+
})
|
|
160
|
+
.strict()
|
|
161
|
+
.superRefine((entry, ctx) => {
|
|
162
|
+
const safe = (value) => !value.startsWith("/") &&
|
|
163
|
+
!value.startsWith("\\") &&
|
|
164
|
+
!value.includes("..") &&
|
|
165
|
+
!value.includes("\\");
|
|
166
|
+
if (!safe(entry.markdownPath) || !entry.markdownPath.endsWith(".md")) {
|
|
167
|
+
ctx.addIssue({ code: z.ZodIssueCode.custom, path: ["markdownPath"], message: "must be a safe repository-relative .md path" });
|
|
168
|
+
}
|
|
169
|
+
if (!safe(entry.pytestPath) || !entry.pytestPath.endsWith(".py")) {
|
|
170
|
+
ctx.addIssue({ code: z.ZodIssueCode.custom, path: ["pytestPath"], message: "must be a safe repository-relative .py path" });
|
|
171
|
+
}
|
|
172
|
+
if (entry.splitReason === "output-budget" && !entry.budgetProof) {
|
|
173
|
+
ctx.addIssue({ code: z.ZodIssueCode.custom, path: ["budgetProof"], message: "is required for output-budget split" });
|
|
174
|
+
}
|
|
175
|
+
if (entry.splitReason !== "output-budget" && entry.budgetProof) {
|
|
176
|
+
ctx.addIssue({ code: z.ZodIssueCode.custom, path: ["budgetProof"], message: "is only valid for output-budget split" });
|
|
177
|
+
}
|
|
178
|
+
});
|
|
179
|
+
const backendTestModuleLayoutSchema = z
|
|
180
|
+
.object({
|
|
181
|
+
schemaId: z.literal("backend-test-module-layout-v1"),
|
|
182
|
+
mode: z.enum(["explicit-file-layout", "business-resource-layout"]),
|
|
183
|
+
modules: z.array(backendTestModuleLayoutEntrySchema).min(1).max(8),
|
|
184
|
+
})
|
|
185
|
+
.strict()
|
|
186
|
+
.superRefine((layout, ctx) => {
|
|
187
|
+
for (const key of ["stem", "markdownPath", "pytestPath"]) {
|
|
188
|
+
const seen = new Set();
|
|
189
|
+
for (let index = 0; index < layout.modules.length; index += 1) {
|
|
190
|
+
const value = layout.modules[index][key];
|
|
191
|
+
if (seen.has(value))
|
|
192
|
+
ctx.addIssue({ code: z.ZodIssueCode.custom, path: ["modules", index, key], message: `duplicate ${key}` });
|
|
193
|
+
seen.add(value);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
if (layout.mode === "explicit-file-layout") {
|
|
197
|
+
layout.modules.forEach((entry, index) => {
|
|
198
|
+
if (entry.splitReason !== "explicit-user-layout")
|
|
199
|
+
ctx.addIssue({ code: z.ZodIssueCode.custom, path: ["modules", index, "splitReason"], message: "explicit-file-layout requires explicit-user-layout" });
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
});
|
|
147
203
|
/** Backend-test artifact layout and pipeline mode configuration (plan A/B/D). */
|
|
148
204
|
export const backendTestConfigSchema = z
|
|
149
205
|
.object({
|
|
@@ -163,6 +219,10 @@ export const backendTestConfigSchema = z
|
|
|
163
219
|
executeScope: z.enum(["affected", "all"]).optional(),
|
|
164
220
|
/** Backend-test-only workspace mutation control; omitted means Git guard. */
|
|
165
221
|
workspaceControl: z.enum(["git", "filesystem-only"]).optional(),
|
|
222
|
+
/** Per-command timeout for the readiness-authorized pytest/HTML execution node. */
|
|
223
|
+
executeTimeoutMs: z.number().int().min(60_000).max(7_200_000).optional(),
|
|
224
|
+
/** Optional strict physical/business module layout; authoritative over model-derived layout. */
|
|
225
|
+
moduleLayout: backendTestModuleLayoutSchema.optional(),
|
|
166
226
|
})
|
|
167
227
|
.strict()
|
|
168
228
|
.optional();
|
|
@@ -177,7 +237,7 @@ const taskConfigObjectSchema = z.object({
|
|
|
177
237
|
taskId: z.string(),
|
|
178
238
|
title: z.string(),
|
|
179
239
|
sourceFiles: z.array(z.string()),
|
|
180
|
-
/** standard: 本仓库需求实现;feature-study: 参考外部代码特性并在目标仓库落地;frontend-implementation: 前端实现 DAG;backend-test: 后端测试 DAG;knowledge-sync: 最终验证后回写测试知识库 DAG;knowledge-graph-bootstrap: AI 辅助业务知识图谱初始化 */
|
|
240
|
+
/** standard: 本仓库需求实现;feature-study: 参考外部代码特性并在目标仓库落地;frontend-implementation: 前端实现 DAG;frontend-test: 前端浏览器测试 DAG;backend-test-analysis: 只读后端测试分析 DAG;backend-test: 后端测试 DAG;knowledge-sync: 最终验证后回写测试知识库 DAG;knowledge-graph-bootstrap: AI 辅助业务知识图谱初始化 */
|
|
181
241
|
taskKind: taskKindSchema.optional().default("standard"),
|
|
182
242
|
/** true = taskKind 由人类显式选择(含显式 standard);DAG 路由必须尊重,不做语义改写。 */
|
|
183
243
|
taskKindExplicit: z.boolean().optional(),
|
|
@@ -221,21 +281,6 @@ const taskConfigObjectSchema = z.object({
|
|
|
221
281
|
maxFixLoops: z.number().int().min(0).optional().default(2),
|
|
222
282
|
/** Supervised DAG convergence is opt-in until runtime smoke evidence is stronger. */
|
|
223
283
|
convergence: convergenceConfigSchema.optional().default({ enabled: false }),
|
|
224
|
-
/** Frontend recovery continuation quota. Bounds how many times a failed
|
|
225
|
-
* frontend run may auto-recover via M6 recovery operations (1 = current
|
|
226
|
-
* default; raise to 2-3 for more tolerance of transient verify failures). */
|
|
227
|
-
frontendRecovery: z
|
|
228
|
-
.object({
|
|
229
|
-
maxContinuations: z
|
|
230
|
-
.number()
|
|
231
|
-
.int()
|
|
232
|
-
.min(1)
|
|
233
|
-
.max(5)
|
|
234
|
-
.optional()
|
|
235
|
-
.default(1),
|
|
236
|
-
})
|
|
237
|
-
.strict()
|
|
238
|
-
.optional(),
|
|
239
284
|
/** Outer loop auto mode never writes by default; DAG execute requires this policy plus path/writeSet gates. */
|
|
240
285
|
loopAutoExecutionPolicy: loopAutoExecutionPolicySchema
|
|
241
286
|
.optional()
|
|
@@ -90,10 +90,6 @@ export async function adoptTaskContract(input) {
|
|
|
90
90
|
requirementBytes: bytes.requirementBytes,
|
|
91
91
|
constraintsBytes: bytes.constraintsBytes,
|
|
92
92
|
referenceManifestBytes: bytes.referenceManifestBytes,
|
|
93
|
-
...(state.ref?.sourceHashes.requirementLedgerSha256 &&
|
|
94
|
-
bytes.requirementLedgerBytes !== null
|
|
95
|
-
? { requirementLedgerBytes: bytes.requirementLedgerBytes }
|
|
96
|
-
: {}),
|
|
97
93
|
});
|
|
98
94
|
const taskConfigSha256 = computeTaskConfigSha256(fullConfig);
|
|
99
95
|
const canonicalHash = computeCanonicalHash({
|
|
@@ -45,10 +45,6 @@ export async function refreshManagedContractAfterImport(input) {
|
|
|
45
45
|
requirementBytes: bytes.requirementBytes,
|
|
46
46
|
constraintsBytes: bytes.constraintsBytes,
|
|
47
47
|
referenceManifestBytes: bytes.referenceManifestBytes,
|
|
48
|
-
...(state2.ref.sourceHashes.requirementLedgerSha256 &&
|
|
49
|
-
bytes.requirementLedgerBytes !== null
|
|
50
|
-
? { requirementLedgerBytes: bytes.requirementLedgerBytes }
|
|
51
|
-
: {}),
|
|
52
48
|
});
|
|
53
49
|
const taskConfigSha256 = computeTaskConfigSha256(fullConfig);
|
|
54
50
|
const canonicalHash = computeCanonicalHash({
|
|
@@ -184,9 +184,6 @@ export function mergeManagedTaskConfigFields(existing, draft) {
|
|
|
184
184
|
command: cmd.command,
|
|
185
185
|
...(cmd.timeoutMs !== undefined ? { timeoutMs: cmd.timeoutMs } : {}),
|
|
186
186
|
})),
|
|
187
|
-
...(draft.frontendOpenspec !== undefined
|
|
188
|
-
? { frontendOpenspec: draft.frontendOpenspec }
|
|
189
|
-
: {}),
|
|
190
187
|
};
|
|
191
188
|
if (draft.featureId !== undefined) {
|
|
192
189
|
next.featureId = draft.featureId;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
-
import { backendTestConfigSchema,
|
|
2
|
+
import { backendTestConfigSchema, taskKindSchema, taskVerifyCommandSchema, } from "../config-types.js";
|
|
3
3
|
import { TASK_CONFIG_SCHEMA_VERSION, TASK_CONTRACT_CANONICALIZER_VERSION, TASK_CONTRACT_DRAFT_SCHEMA_VERSION, TASK_CONTRACT_PROJECTION_VERSION, TASK_CONTRACT_REF_SCHEMA_VERSION, } from "./constants.js";
|
|
4
4
|
const sha256Schema = z
|
|
5
5
|
.string()
|
|
@@ -74,7 +74,6 @@ export const taskContractDraftSchema = z
|
|
|
74
74
|
openQuestions: z.array(z.string()).optional(),
|
|
75
75
|
assumptions: z.array(z.string()).optional(),
|
|
76
76
|
hardConstraints: z.array(z.string()).optional(),
|
|
77
|
-
frontendOpenspec: frontendOpenspecConfigSchema.optional(),
|
|
78
77
|
interviewAssessmentRef: z
|
|
79
78
|
.object({
|
|
80
79
|
draftSha256: z.string().min(1),
|
|
@@ -3,13 +3,13 @@
|
|
|
3
3
|
* Strong evidence only: package.json deps, config files, source imports.
|
|
4
4
|
* Lockfile-only or directory-name-only must NOT activate adapters.
|
|
5
5
|
*/
|
|
6
|
-
import {
|
|
6
|
+
import { readFile, readdir, stat } from "node:fs/promises";
|
|
7
7
|
import path from "node:path";
|
|
8
8
|
import { z } from "zod";
|
|
9
|
-
import { DEFAULT_OPENSPEC_GOVERNANCE_ROOT, MAX_OPENSPEC_CANDIDATE_PATHS, OPENSPEC_SPEC_DIRS, OPENSPEC_SPEC_EXT_RE, isGovernanceSubtreePath,
|
|
9
|
+
import { DEFAULT_OPENSPEC_GOVERNANCE_ROOT, MAX_OPENSPEC_CANDIDATE_PATHS, OPENSPEC_SPEC_DIRS, OPENSPEC_SPEC_EXT_RE, isGovernanceSubtreePath, isOpenspecSpecFilePath, } from "../shared/openspec-spec.js";
|
|
10
10
|
const classifiedPathSchema = z
|
|
11
11
|
.string()
|
|
12
|
-
.refine((candidate) =>
|
|
12
|
+
.refine((candidate) => isOpenspecSpecFilePath(candidate), "classified path must be a repo-relative supported spec file under openspec/schemas/, openspec/project-specs/, or ai_workspace/");
|
|
13
13
|
/**
|
|
14
14
|
* Stable semantic partition of `designEvidence.normativePaths`. Keys are a
|
|
15
15
|
* generic directory convention (not project-specific file names); the same
|
|
@@ -81,105 +81,6 @@ async function exists(filePath) {
|
|
|
81
81
|
return false;
|
|
82
82
|
}
|
|
83
83
|
}
|
|
84
|
-
export const FRONTEND_SPEC_REGISTRY_PATH = "ai_workspace/loop-agent/spec-registry.json";
|
|
85
|
-
const SPEC_SIGNAL_RE = /(?:规范|规约|标准|约定|guideline|standard|convention|design[- ]?system|rule|token|component|style|theme)/i;
|
|
86
|
-
const SPEC_EXCLUDE_RE = /(?:^|\/)(?:archive|history|changelog|changes|proposals?|release[-_ ]notes?)(?:\/|$)/i;
|
|
87
|
-
/** Stable, bounded heuristic used only for candidate ordering/classification. */
|
|
88
|
-
export function scoreFrontendSpecCandidate(filePath, contentPreview = "", options = {}) {
|
|
89
|
-
const normalized = filePath.replaceAll("\\", "/");
|
|
90
|
-
const lower = normalized.toLowerCase();
|
|
91
|
-
const reasons = [];
|
|
92
|
-
let score = 0;
|
|
93
|
-
if (options.registryMode === "normative") {
|
|
94
|
-
score += 100;
|
|
95
|
-
reasons.push("registry-normative");
|
|
96
|
-
}
|
|
97
|
-
else if (options.registryMode === "advisory") {
|
|
98
|
-
score += 30;
|
|
99
|
-
reasons.push("registry-advisory");
|
|
100
|
-
}
|
|
101
|
-
if (options.taskRelated) {
|
|
102
|
-
score += 25;
|
|
103
|
-
reasons.push("task-related");
|
|
104
|
-
}
|
|
105
|
-
if (/(?:design-system|guidelines?|standards?|conventions?|rules?|specs?)(?:\/|$)/i.test(lower)) {
|
|
106
|
-
score += 20;
|
|
107
|
-
reasons.push("spec-directory");
|
|
108
|
-
}
|
|
109
|
-
if (/(?:guidelines?|standards?|conventions?|rules?|tokens?|components?|theme|style)/i.test(path.basename(lower))) {
|
|
110
|
-
score += 15;
|
|
111
|
-
reasons.push("spec-filename");
|
|
112
|
-
}
|
|
113
|
-
if (SPEC_SIGNAL_RE.test(contentPreview)) {
|
|
114
|
-
score += 20;
|
|
115
|
-
reasons.push("spec-content");
|
|
116
|
-
}
|
|
117
|
-
if (/(?:^|\n)\s*(?:#{1,6}\s*)?(?:rules?|do|don't|必须|禁止|约定|规范)\b/im.test(contentPreview)) {
|
|
118
|
-
score += 15;
|
|
119
|
-
reasons.push("normative-structure");
|
|
120
|
-
}
|
|
121
|
-
if (SPEC_EXCLUDE_RE.test(lower)) {
|
|
122
|
-
score -= 80;
|
|
123
|
-
reasons.push("excluded-history-path");
|
|
124
|
-
}
|
|
125
|
-
const kind = options.registryMode === "normative" || score >= 80
|
|
126
|
-
? "normative"
|
|
127
|
-
: score >= 35
|
|
128
|
-
? "advisory"
|
|
129
|
-
: "unknown";
|
|
130
|
-
return { path: normalized, kind, score, reasons, source: options.registryMode ? "registry" : "auto-discovered" };
|
|
131
|
-
}
|
|
132
|
-
/** Read the optional project-level registry without making it mandatory. */
|
|
133
|
-
export async function readFrontendSpecRegistry(repoRoot) {
|
|
134
|
-
const raw = await readJson(path.join(repoRoot, FRONTEND_SPEC_REGISTRY_PATH));
|
|
135
|
-
if (!raw || typeof raw !== "object")
|
|
136
|
-
return null;
|
|
137
|
-
const value = raw;
|
|
138
|
-
if (value.version !== 1 || !Array.isArray(value.roots))
|
|
139
|
-
return null;
|
|
140
|
-
const roots = value.roots
|
|
141
|
-
.filter((item) => Boolean(item) && typeof item === "object")
|
|
142
|
-
.map((item) => ({
|
|
143
|
-
path: typeof item.path === "string" ? item.path.replaceAll("\\", "/").replace(/\/+$/, "") : "",
|
|
144
|
-
scope: Array.isArray(item.scope) ? item.scope.filter((v) => typeof v === "string") : undefined,
|
|
145
|
-
mode: (item.mode === "normative" || item.mode === "advisory" ? item.mode : undefined),
|
|
146
|
-
description: typeof item.description === "string" ? item.description : undefined,
|
|
147
|
-
}))
|
|
148
|
-
.filter((item) => isValidFrontendSpecRoot(item.path));
|
|
149
|
-
return {
|
|
150
|
-
version: 1,
|
|
151
|
-
roots,
|
|
152
|
-
discovery: value.discovery && typeof value.discovery === "object" ? value.discovery : undefined,
|
|
153
|
-
};
|
|
154
|
-
}
|
|
155
|
-
/**
|
|
156
|
-
* Resolve only unambiguous symlink-root aliases. The physical target is never
|
|
157
|
-
* emitted as a candidate path: it merely lets task-source wording such as
|
|
158
|
-
* `ai_doc/rules.md` resolve to the stable repo-local path `ai_workspace/rules.md`.
|
|
159
|
-
*/
|
|
160
|
-
export async function resolveFrontendSpecRootAliases(repoRoot, specRoots) {
|
|
161
|
-
const candidates = [];
|
|
162
|
-
for (const logicalRoot of specRoots) {
|
|
163
|
-
const full = path.join(repoRoot, logicalRoot);
|
|
164
|
-
try {
|
|
165
|
-
if (!(await lstat(full)).isSymbolicLink())
|
|
166
|
-
continue;
|
|
167
|
-
const alias = path.basename(await realpath(full));
|
|
168
|
-
if (alias && alias !== path.basename(logicalRoot)) {
|
|
169
|
-
candidates.push({ alias, logicalRoot });
|
|
170
|
-
}
|
|
171
|
-
}
|
|
172
|
-
catch {
|
|
173
|
-
// Missing/broken links are handled by normal candidate discovery drift.
|
|
174
|
-
}
|
|
175
|
-
}
|
|
176
|
-
const counts = new Map();
|
|
177
|
-
for (const item of candidates)
|
|
178
|
-
counts.set(item.alias, (counts.get(item.alias) ?? 0) + 1);
|
|
179
|
-
return candidates
|
|
180
|
-
.filter((item) => counts.get(item.alias) === 1)
|
|
181
|
-
.sort((a, b) => a.alias.localeCompare(b.alias));
|
|
182
|
-
}
|
|
183
84
|
/**
|
|
184
85
|
* Resolve the governance subtree root from harness.json.governanceRoot.
|
|
185
86
|
* Falls back to DEFAULT_OPENSPEC_GOVERNANCE_ROOT when the manifest is missing,
|
|
@@ -204,54 +105,28 @@ export async function listOpenspecDiscovery(repoRoot, options = {}) {
|
|
|
204
105
|
.filter(Boolean);
|
|
205
106
|
const collected = [];
|
|
206
107
|
let excludedGovernanceCount = 0;
|
|
207
|
-
let scannedFiles = 0;
|
|
208
|
-
let maxFilesHit = false;
|
|
209
|
-
let scannedBytes = 0;
|
|
210
|
-
const warnings = [];
|
|
211
|
-
const maxDepth = options.maxDepth ?? 4;
|
|
212
|
-
const maxFiles = options.maxFiles ?? MAX_OPENSPEC_CANDIDATE_PATHS;
|
|
213
|
-
const maxBytes = options.maxBytes ?? 1024 * 1024;
|
|
214
|
-
const timeoutMs = options.timeoutMs ?? 2000;
|
|
215
|
-
const startedAt = Date.now();
|
|
216
|
-
// Metadata / bookkeeping directories that are not normative design specs:
|
|
217
|
-
// changelogs, ADR/history archives, proposals under review, and scratch
|
|
218
|
-
// state. Reading them inflates the candidate list (and the plan prompt)
|
|
219
|
-
// with files no component selection should cite. A task that genuinely
|
|
220
|
-
// needs one can still reference it explicitly — the prewrite gate
|
|
221
|
-
// candidates come from this discovery, but explicit task-source citations
|
|
222
|
-
// flow through openspecMandatoryPaths independently.
|
|
223
|
-
const DEFAULT_EXCLUDED_SPEC_PREFIXES = [
|
|
224
|
-
"openspec/changes",
|
|
225
|
-
"openspec/project-specs/changes",
|
|
226
|
-
"openspec/project-specs/changelog",
|
|
227
|
-
"openspec/project-specs/history",
|
|
228
|
-
"openspec/project-specs/archive",
|
|
229
|
-
"openspec/project-specs/decisions",
|
|
230
|
-
"openspec/project-specs/proposals",
|
|
231
|
-
];
|
|
232
108
|
const isExcluded = (rel) => {
|
|
233
109
|
if (isGovernanceSubtreePath(rel, governanceRoot))
|
|
234
110
|
return true;
|
|
235
|
-
if (DEFAULT_EXCLUDED_SPEC_PREFIXES.some((prefix) => rel === prefix || rel.startsWith(prefix + "/"))) {
|
|
236
|
-
return true;
|
|
237
|
-
}
|
|
238
111
|
return extraExcludedPrefixes.some((prefix) => rel === prefix || rel.startsWith(prefix + "/"));
|
|
239
112
|
};
|
|
240
|
-
for (const specDir of
|
|
113
|
+
for (const specDir of OPENSPEC_SPEC_DIRS) {
|
|
241
114
|
const root = path.join(repoRoot, specDir);
|
|
242
115
|
if (!(await exists(root)))
|
|
243
116
|
continue;
|
|
244
117
|
await walk(root, 0);
|
|
245
118
|
}
|
|
246
|
-
//
|
|
247
|
-
//
|
|
119
|
+
// openspec/schemas/** and openspec/project-specs/** sort first; remaining
|
|
120
|
+
// ai_workspace/** business files follow. Dedupe preserves discovery order.
|
|
248
121
|
const unique = [...new Set(collected)];
|
|
249
|
-
const
|
|
250
|
-
|
|
251
|
-
.
|
|
122
|
+
const openspecPaths = unique
|
|
123
|
+
.filter((candidate) => candidate.startsWith("openspec/"))
|
|
124
|
+
.sort();
|
|
125
|
+
const aiWorkspacePaths = unique
|
|
126
|
+
.filter((candidate) => candidate.startsWith("ai_workspace/"))
|
|
252
127
|
.sort();
|
|
253
|
-
const deduped = [...
|
|
254
|
-
const truncated =
|
|
128
|
+
const deduped = [...openspecPaths, ...aiWorkspacePaths];
|
|
129
|
+
const truncated = deduped.length > MAX_OPENSPEC_CANDIDATE_PATHS;
|
|
255
130
|
const paths = truncated
|
|
256
131
|
? deduped.slice(0, MAX_OPENSPEC_CANDIDATE_PATHS)
|
|
257
132
|
: deduped;
|
|
@@ -261,19 +136,10 @@ export async function listOpenspecDiscovery(repoRoot, options = {}) {
|
|
|
261
136
|
excludedGovernanceCount,
|
|
262
137
|
truncated,
|
|
263
138
|
maxCandidates: MAX_OPENSPEC_CANDIDATE_PATHS,
|
|
264
|
-
warnings,
|
|
265
139
|
};
|
|
266
140
|
async function walk(dir, depth) {
|
|
267
|
-
if (
|
|
268
|
-
if (!warnings.includes("timeout-exceeded"))
|
|
269
|
-
warnings.push("timeout-exceeded");
|
|
270
|
-
return;
|
|
271
|
-
}
|
|
272
|
-
if (depth > maxDepth) {
|
|
273
|
-
if (!warnings.includes("max-depth-exceeded"))
|
|
274
|
-
warnings.push("max-depth-exceeded");
|
|
141
|
+
if (depth > 4)
|
|
275
142
|
return;
|
|
276
|
-
}
|
|
277
143
|
let entries = [];
|
|
278
144
|
try {
|
|
279
145
|
entries = (await readdir(dir)).sort();
|
|
@@ -282,31 +148,12 @@ export async function listOpenspecDiscovery(repoRoot, options = {}) {
|
|
|
282
148
|
return;
|
|
283
149
|
}
|
|
284
150
|
for (const name of entries) {
|
|
285
|
-
if (Date.now() - startedAt >= timeoutMs) {
|
|
286
|
-
if (!warnings.includes("timeout-exceeded"))
|
|
287
|
-
warnings.push("timeout-exceeded");
|
|
288
|
-
return;
|
|
289
|
-
}
|
|
290
|
-
if (scannedFiles >= maxFiles) {
|
|
291
|
-
if (!warnings.includes("max-files-exceeded"))
|
|
292
|
-
warnings.push("max-files-exceeded");
|
|
293
|
-
maxFilesHit = true;
|
|
294
|
-
return;
|
|
295
|
-
}
|
|
296
151
|
const full = path.join(dir, name);
|
|
297
152
|
try {
|
|
298
153
|
const info = await stat(full);
|
|
299
154
|
if (info.isDirectory())
|
|
300
155
|
await walk(full, depth + 1);
|
|
301
156
|
else if (OPENSPEC_SPEC_EXT_RE.test(name)) {
|
|
302
|
-
const size = info.size;
|
|
303
|
-
if (scannedBytes + size > maxBytes) {
|
|
304
|
-
if (!warnings.includes("max-bytes-exceeded"))
|
|
305
|
-
warnings.push("max-bytes-exceeded");
|
|
306
|
-
return;
|
|
307
|
-
}
|
|
308
|
-
scannedFiles++;
|
|
309
|
-
scannedBytes += size;
|
|
310
157
|
const rel = path.relative(repoRoot, full).replace(/\\/g, "/");
|
|
311
158
|
if (isExcluded(rel)) {
|
|
312
159
|
excludedGovernanceCount++;
|
|
@@ -394,14 +241,6 @@ function emptyClassified() {
|
|
|
394
241
|
* project-specific file names. Basename stems use word-boundary matching so
|
|
395
242
|
* e.g. `*api*` (case-insensitive) maps to `rule.api` without hardcoding names.
|
|
396
243
|
*/
|
|
397
|
-
/** True when any path segment (other than the file itself) carries a
|
|
398
|
-
* component/theme kind marker. Directory-organized libraries such as
|
|
399
|
-
* `ui/ucp-components-md/AdvancedSearch.md` rely on this; templates/ and
|
|
400
|
-
* rules/ are handled by their own branches before this fallback runs. */
|
|
401
|
-
function segmentMatchesComponentKind(segments) {
|
|
402
|
-
const dirSegments = segments.slice(0, -1);
|
|
403
|
-
return dirSegments.some((segment) => /\bcomponents?\b/.test(segment.toLowerCase()));
|
|
404
|
-
}
|
|
405
244
|
function classifyOpenspecPaths(paths) {
|
|
406
245
|
const classified = emptyClassified();
|
|
407
246
|
const ruleStem = (basename) => {
|
|
@@ -421,7 +260,7 @@ function classifyOpenspecPaths(paths) {
|
|
|
421
260
|
return "other";
|
|
422
261
|
};
|
|
423
262
|
for (const candidate of paths) {
|
|
424
|
-
if (!
|
|
263
|
+
if (!isOpenspecSpecFilePath(candidate))
|
|
425
264
|
continue;
|
|
426
265
|
const segments = candidate.split("/");
|
|
427
266
|
const basename = segments[segments.length - 1] ?? "";
|
|
@@ -440,17 +279,8 @@ function classifyOpenspecPaths(paths) {
|
|
|
440
279
|
classified.theme.push(candidate);
|
|
441
280
|
else if (/\bcomponents?\b/.test(lower))
|
|
442
281
|
classified.component.push(candidate);
|
|
443
|
-
else
|
|
444
|
-
// Directory-organized component libraries (e.g.
|
|
445
|
-
// ui/ucp-components-md/AdvancedSearch.md) carry the kind in the
|
|
446
|
-
// parent directory name, not the file name. The parent-dir rule
|
|
447
|
-
// is a fallback so such layouts still land in the component
|
|
448
|
-
// bucket instead of being dropped into uiOther.
|
|
449
|
-
classified.component.push(candidate);
|
|
450
|
-
}
|
|
451
|
-
else {
|
|
282
|
+
else
|
|
452
283
|
classified.uiOther.push(candidate);
|
|
453
|
-
}
|
|
454
284
|
}
|
|
455
285
|
else {
|
|
456
286
|
classified.advisoryOther.push(candidate);
|
|
@@ -495,7 +325,6 @@ function toOpenspecDiscovery(discovery, designEvidence) {
|
|
|
495
325
|
excludedGovernanceCount: discovery.excludedGovernanceCount,
|
|
496
326
|
truncated: discovery.truncated,
|
|
497
327
|
maxCandidates: discovery.maxCandidates,
|
|
498
|
-
warnings: discovery.warnings,
|
|
499
328
|
};
|
|
500
329
|
}
|
|
501
330
|
export function buildAdapterGuidance(capability) {
|
|
@@ -515,7 +344,7 @@ export function buildAdapterGuidance(capability) {
|
|
|
515
344
|
lines.push("Vue adapter: prefer Composition API if project already uses it; reuse Pinia/Vue Router/Vue Query only when evidenced; Vitest + Vue Test Utils when present.");
|
|
516
345
|
}
|
|
517
346
|
else {
|
|
518
|
-
lines.push("Unknown framework: use
|
|
347
|
+
lines.push("Unknown framework: use generic frontend-implementation skill only; do not assume React/Vue/Next APIs.");
|
|
519
348
|
}
|
|
520
349
|
if (capability.a11y.status === "present") {
|
|
521
350
|
lines.push(`A11y tools present: ${capability.a11y.tools.join(", ")}. Report static/component-level evidence only; Browser a11y remains not-run.`);
|
|
@@ -532,25 +361,13 @@ export function buildAdapterGuidance(capability) {
|
|
|
532
361
|
* Discover frontend project capability from repo root.
|
|
533
362
|
* Strong evidence: package.json direct deps + config/bootstrap files.
|
|
534
363
|
*/
|
|
535
|
-
export async function discoverFrontendProjectCapability(repoRoot
|
|
536
|
-
const registry = await readFrontendSpecRegistry(repoRoot);
|
|
537
|
-
const registryRoots = registry?.roots
|
|
538
|
-
.filter((root) => !root.scope || root.scope.includes("frontend"))
|
|
539
|
-
.map((root) => root.path) ?? [];
|
|
540
|
-
const discoveryOptions = {
|
|
541
|
-
...options,
|
|
542
|
-
specRoots: [...new Set([...OPENSPEC_SPEC_DIRS, ...(options.specRoots ?? []), ...registryRoots])],
|
|
543
|
-
maxDepth: registry?.discovery?.maxDepth ?? options.maxDepth,
|
|
544
|
-
maxFiles: registry?.discovery?.maxFiles ?? options.maxFiles,
|
|
545
|
-
maxBytes: registry?.discovery?.maxBytes ?? options.maxBytes,
|
|
546
|
-
timeoutMs: registry?.discovery?.timeoutMs ?? options.timeoutMs,
|
|
547
|
-
};
|
|
364
|
+
export async function discoverFrontendProjectCapability(repoRoot) {
|
|
548
365
|
const evidencePaths = [];
|
|
549
366
|
const reasons = [];
|
|
550
367
|
const pkgPath = path.join(repoRoot, "package.json");
|
|
551
368
|
const pkgRaw = await readJson(pkgPath);
|
|
552
369
|
if (!pkgRaw || typeof pkgRaw !== "object") {
|
|
553
|
-
const discovery = await listOpenspecDiscovery(repoRoot
|
|
370
|
+
const discovery = await listOpenspecDiscovery(repoRoot);
|
|
554
371
|
const designEvidence = buildDesignEvidence(discovery.paths);
|
|
555
372
|
const base = {
|
|
556
373
|
schemaVersion: 1,
|
|
@@ -723,7 +540,7 @@ export async function discoverFrontendProjectCapability(repoRoot, options = {})
|
|
|
723
540
|
router = router ?? "next-router";
|
|
724
541
|
if (hasDep(deps, "vue-router"))
|
|
725
542
|
router = "vue-router";
|
|
726
|
-
const discovery = await listOpenspecDiscovery(repoRoot
|
|
543
|
+
const discovery = await listOpenspecDiscovery(repoRoot);
|
|
727
544
|
const designEvidence = buildDesignEvidence(discovery.paths);
|
|
728
545
|
if (discovery.paths.length)
|
|
729
546
|
evidencePaths.push(...discovery.paths.slice(0, 5));
|
package/dist/task/runtime.js
CHANGED
|
@@ -72,7 +72,7 @@ export function getTaskPaths(repoRoot, taskId) {
|
|
|
72
72
|
dagDraftPath: path.join(taskDir, 'dag.json'),
|
|
73
73
|
};
|
|
74
74
|
}
|
|
75
|
-
export async function createTask(repoRoot, taskId, title
|
|
75
|
+
export async function createTask(repoRoot, taskId, title) {
|
|
76
76
|
const validation = validateNewTaskId(taskId);
|
|
77
77
|
for (const warning of validation.warnings) {
|
|
78
78
|
console.warn(`[new-task] warning: ${warning}`);
|
|
@@ -97,10 +97,7 @@ export async function createTask(repoRoot, taskId, title, options) {
|
|
|
97
97
|
taskId,
|
|
98
98
|
title: title ?? taskId,
|
|
99
99
|
sourceFiles: [],
|
|
100
|
-
taskKind:
|
|
101
|
-
...(options?.taskKindExplicit !== undefined
|
|
102
|
-
? { taskKindExplicit: options.taskKindExplicit }
|
|
103
|
-
: {}),
|
|
100
|
+
taskKind: 'standard',
|
|
104
101
|
referenceRepos: [],
|
|
105
102
|
referenceDocs: [],
|
|
106
103
|
allowedPaths: [],
|
|
@@ -17,10 +17,10 @@ function resolveTaskKind(flags, baseDraft, existing) {
|
|
|
17
17
|
const parsed = taskKindSchema.safeParse(candidate);
|
|
18
18
|
return parsed.success ? parsed.data : String(candidate);
|
|
19
19
|
}
|
|
20
|
-
function resolveTaskKindExplicit(flags, baseDraft
|
|
20
|
+
function resolveTaskKindExplicit(flags, baseDraft) {
|
|
21
21
|
if (flags.taskKindExplicit !== undefined)
|
|
22
22
|
return flags.taskKindExplicit;
|
|
23
|
-
return
|
|
23
|
+
return baseDraft?.taskKindExplicit ?? false;
|
|
24
24
|
}
|
|
25
25
|
/**
|
|
26
26
|
* Resolve managed backend-test layout config.
|
|
@@ -122,7 +122,7 @@ export function buildPrepareDraft(input) {
|
|
|
122
122
|
const { taskId, existingTaskConfig, baseDraft, facts, flags, references } = input;
|
|
123
123
|
const title = resolveTitle(taskId, flags, facts, baseDraft, existingTaskConfig);
|
|
124
124
|
const taskKind = resolveTaskKind(flags, baseDraft, existingTaskConfig);
|
|
125
|
-
const taskKindExplicit = resolveTaskKindExplicit(flags, baseDraft
|
|
125
|
+
const taskKindExplicit = resolveTaskKindExplicit(flags, baseDraft);
|
|
126
126
|
const featureId = trimNonEmpty(flags.featureId) ??
|
|
127
127
|
trimNonEmpty(baseDraft?.featureId) ??
|
|
128
128
|
trimNonEmpty(existingTaskConfig?.featureId) ??
|