@tea-agent/loop-agent 0.41.1 → 0.42.0-next.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +1 -1
- package/CHANGELOG.md +397 -5
- package/dist/adapters/context-transfer/optional-pi-handoff.js +31 -0
- package/dist/adapters/context-transfer/pi-session.js +61 -0
- package/dist/application/dag/generate-task-dag.js +75 -19
- package/dist/application/dag/run-dag.js +41 -0
- package/dist/application/task-lifecycle/advance.js +24 -5
- package/dist/application/task-lifecycle/observe.js +171 -17
- package/dist/application/task-lifecycle/plan-transitions.js +42 -7
- package/dist/application/task-lifecycle/recommendations.js +13 -2
- package/dist/build-stamp.json +3 -3
- package/dist/cli/command-definitions.js +19 -0
- package/dist/cli/program.js +29 -2
- package/dist/commands/client-recovery.js +3 -0
- package/dist/commands/dag-artifact.js +284 -0
- package/dist/commands/dag-context.js +184 -0
- package/dist/commands/dag-follow-up.js +138 -0
- package/dist/commands/dag-rerun-task.js +2 -0
- package/dist/commands/dag-rerun.js +206 -1
- package/dist/commands/init.js +27 -1
- package/dist/commands/task-advance.js +19 -0
- package/dist/executors/dag-pi-executor.js +4013 -86
- package/dist/executors/pi-executor.js +15 -4
- package/dist/executors/pi-read-budget-policy.js +239 -0
- package/dist/executors/pi-sdk-executor.js +3 -0
- package/dist/executors/shell-executor.js +978 -188
- package/dist/executors/shell-write-guard.js +7 -0
- package/dist/{worker → infrastructure}/console/app-data.js +4 -0
- package/dist/infrastructure/console/artifact-revision-store.js +430 -0
- package/dist/infrastructure/console/context-export-store.js +160 -0
- package/dist/infrastructure/console/dir-lock.js +132 -0
- package/dist/infrastructure/console/operation-store.js +417 -0
- package/dist/infrastructure/harness/artifact-store.js +10 -1
- package/dist/infrastructure/harness/atomic-write.js +12 -2
- package/dist/shared/context-transfer/artifact-revision.js +172 -0
- package/dist/shared/context-transfer.js +418 -0
- package/dist/shared/dag-failure-category.js +12 -0
- package/dist/shared/openspec-spec.js +70 -4
- package/dist/shared/operator/capabilities.js +21 -0
- package/dist/shared/operator/safe-run-summary.js +1 -0
- package/dist/shared/path-safety.js +93 -0
- package/dist/shared/preview.js +28 -4
- package/dist/task/config-types.js +107 -6
- package/dist/task/contract/adopt.js +4 -0
- package/dist/task/contract/import-revision.js +4 -0
- package/dist/task/contract/project.js +3 -0
- package/dist/task/contract/schema.js +2 -1
- package/dist/task/frontend-project-capability.js +203 -20
- package/dist/task/runtime.js +5 -2
- package/dist/task/source-prepare/build-draft.js +3 -3
- package/dist/task/source-prepare/fragment-inventory.js +64 -25
- package/dist/task/source-prepare/prepare.js +126 -21
- package/dist/task/source-prepare/semantic-intake.js +6 -2
- package/dist/task/source-references.js +22 -1
- package/dist/task/task-demand-routing.js +3 -0
- package/dist/worker/console/chat/chat-event-store.js +2 -2
- package/dist/worker/console/chat/model-resolver.js +7 -4
- package/dist/worker/console/chat/pi-runtime.js +86 -5
- package/dist/worker/console/chat/resource-preferences-store.js +1 -1
- package/dist/worker/console/chat/routes.js +136 -53
- package/dist/worker/console/chat/sdd-data-alignment.js +222 -0
- package/dist/worker/console/chat/session-catalog.js +2 -0
- package/dist/worker/console/chat/session-store.js +43 -1
- package/dist/worker/console/chat/shortcuts.js +18 -2
- package/dist/worker/console/chat/user-questions.js +1 -1
- package/dist/worker/console/console-update-runtime.js +1 -1
- package/dist/worker/console/context-transfer-diagnostics.js +198 -0
- package/dist/worker/console/dag-confirmation.js +1 -1
- package/dist/worker/console/dag-execution-receipt.js +1 -1
- package/dist/worker/console/doctor.js +1 -1
- package/dist/worker/console/draft-store.js +1 -1
- package/dist/worker/console/frontend-human-decision-adapter.js +19 -0
- package/dist/worker/console/frontend-split-operation-adapter.js +20 -0
- package/dist/worker/console/human-gate-token.js +1 -1
- package/dist/worker/console/index.js +6 -3
- package/dist/worker/console/interview/assessment.js +1 -1
- package/dist/worker/console/interview/session.js +1 -1
- package/dist/worker/console/operation-runner.js +45 -4
- package/dist/worker/console/operation-sse.js +1 -1
- package/dist/worker/console/operation-wait.js +1 -1
- package/dist/worker/console/operator-actions.js +273 -10
- package/dist/worker/console/operator-surface-health.js +1 -1
- package/dist/worker/console/operator-user-error.js +169 -0
- package/dist/worker/console/pi-plugins.js +57 -0
- package/dist/worker/console/pi-readiness.js +3 -2
- package/dist/worker/console/routes.js +637 -8
- package/dist/worker/console/security.js +35 -0
- package/dist/worker/console/server.js +29 -4
- package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-D7oU983t.js → abnfDiagram-N423BO3Z-CcS17TBr.js} +1 -1
- package/dist/worker/console/static/assets/{arc-BrKSRdFS.js → arc-COptKq2S.js} +1 -1
- package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-CiXV7L56.js → architectureDiagram-T3A2C74G-h4LKMHjP.js} +1 -1
- package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-D_Hs_gIG.js → blockDiagram-VBNYF7ZC-COA1MOH0.js} +1 -1
- package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-DbM-Lqqh.js → c4Diagram-5PPSVZJV-DJUf0QPm.js} +1 -1
- package/dist/worker/console/static/assets/channel-DdBCaOJ6.js +1 -0
- package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-D1M8uqJw.js → chunk-2GRJ4B5K-mtWfKrUX.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-4D3RFHT6.js → chunk-2Q5K7J3B-B8pXsxDQ.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5RXB4S5H-CdwpY_n9.js → chunk-5RXB4S5H-ipKzByl1.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5VM5RSS4-BLeLZ9PG.js → chunk-5VM5RSS4-DYi3Ald_.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-CeRNEMvc.js → chunk-6Q2QTUOP-DQtGYoty.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-GF5L2VYU-CjkHP8Vy.js → chunk-GF5L2VYU-BU0qS2YV.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-JWPE2WC7-B54ZUQY-.js → chunk-JWPE2WC7-DVH9dIGE.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-KBJHAD2P-4bKaMVmx.js → chunk-KBJHAD2P-Dt60SmgM.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-RYQCIY6F-Cyjfznu_.js → chunk-RYQCIY6F-BoaUGuEY.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-XXDRQBXY-CDN-zGFV.js → chunk-XXDRQBXY-_WHFDTp2.js} +1 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-DZFra1GO.js +1 -0
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-DZFra1GO.js +1 -0
- package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-HxJmz411.js → cose-bilkent-JH36ORCC-BxkbTIRd.js} +1 -1
- package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-DtSt5K2w.js → cynefin-VYW2F7L2-Bf2UVnoG.js} +1 -1
- package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-VtkSpRmG.js → cynefinDiagram-MW4NZA55-Bo23q1J_.js} +1 -1
- package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-Bj4DT4du.js → dagre-VZM6K2ZE-Dd_UU49i.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-7IWD3JNH-Bpuk6BmP.js → diagram-7IWD3JNH-ebUa1a9y.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-BHYGv_yO.js → diagram-B4RE2ZJO-BucthU8r.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-LBJQPF4R-DEx3WwnT.js → diagram-LBJQPF4R-BgqNpAkm.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-Q27KOJAE-Cj0dS5e8.js → diagram-Q27KOJAE-DC4q5NGa.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-UB23O5K3-B97h8l21.js → diagram-UB23O5K3-CBqCaMeb.js} +1 -1
- package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-DMSFAhBd.js → ebnfDiagram-BXEA7PRR-CGtnbQ3-.js} +1 -1
- package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-DhVOCbt6.js → erDiagram-JOGREHBK-CG3LUao5.js} +1 -1
- package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-GlakITWL.js → flowDiagram-UKHOOZJN-D2ZVgoFS.js} +1 -1
- package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-VxPpu9eQ.js → ganttDiagram-PKOTCBZU-DoYFZiKt.js} +1 -1
- package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-Bs_e5DRT.js → gitGraphDiagram-DS77QQ5N-CKoP1s6j.js} +1 -1
- package/dist/worker/console/static/assets/index-CAZ2fC_X.css +1 -0
- package/dist/worker/console/static/assets/index-vbTcFnFs.js +449 -0
- package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-BXYuU4Uw.js → infoDiagram-6WML65LV-Duofv8p2.js} +1 -1
- package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-C34erCbU.js → ishikawaDiagram-WSZJBQD7-D2nlCkA1.js} +1 -1
- package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-DZCAiOU3.js → journeyDiagram-NVQOT4AX-Dd4IHdus.js} +1 -1
- package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-C9gaTdUm.js → kanban-definition-27J2QSJJ-Bi20AOdt.js} +1 -1
- package/dist/worker/console/static/assets/{linear-CvI0Z17U.js → linear-D59YJ9kB.js} +1 -1
- package/dist/worker/console/static/assets/{mermaid.core-DMMSIx7j.js → mermaid.core-Bl13LpNa.js} +5 -5
- package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-BKKP0EO6.js → mindmap-definition-FAOFIHXS-C6DLP5eY.js} +1 -1
- package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-C3LiFbl5.js → pegDiagram-VL7TDLO6-BjO4cy64.js} +1 -1
- package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-Bb3Om2Dz.js → pieDiagram-7S7Q4E2Y-D7i2qZTG.js} +1 -1
- package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-DNthMjzc.js → quadrantDiagram-CIZ2JOQS-Dx8o_h0y.js} +1 -1
- package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-miFsr4Tv.js → railroadDiagram-AXF67PYL-B55orI_t.js} +1 -1
- package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-yXUncmn6.js → requirementDiagram-LRYGKXZP-zWaehp6f.js} +1 -1
- package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-DLeWKLak.js → sankeyDiagram-W5VNT64P-CcA-pjvD.js} +1 -1
- package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-CI3he1R3.js → sequenceDiagram-SI44F4Z6-BOFbzNHI.js} +1 -1
- package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-B7jfDkS5.js → sizeCapture-X5ZJPWSS-BjAejah1.js} +1 -1
- package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-BmrZod7J.js → stateDiagram-OKZ733FA-DXYwxJwZ.js} +1 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-Clg3V9t1.js +1 -0
- package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-CDOlHVSY.js → swimlanes-SLNWSIFB-OYiOch8n.js} +2 -2
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-DX1dxAAW.js +8 -0
- package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-Cz-XX1gy.js → timeline-definition-Z64GVDOM-cZfH3nmU.js} +1 -1
- package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-caV4cI8C.js → vennDiagram-T6HMQDX7-BDE7b3E1.js} +1 -1
- package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-dImPlnIO.js → wardleyDiagram-T6FBY63Y-kyyGy9WJ.js} +1 -1
- package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-CtRYVJcn.js → xychartDiagram-ELKLHX3M-CJj6VTog.js} +1 -1
- package/dist/worker/console/static/index.html +7 -2
- package/dist/worker/console/static-src/active-run-badge.js +17 -0
- package/dist/worker/console/static-src/app/useOperatorActions.js +3 -2
- package/dist/worker/console/static-src/app/useRecoveryConsole.js +5 -5
- package/dist/worker/console/static-src/operator-chat/input-history.js +8 -6
- package/dist/worker/console/static-src/operator-chat/runtime-snapshot-store.js +10 -0
- package/dist/worker/console/static-src/operator-chat/useChatSessions.js +258 -23
- package/dist/worker/console/static-src/operator-chat/useChatThread.js +112 -4
- package/dist/worker/console/static-src/operator-chat/useComposer.js +13 -4
- package/dist/worker/console/static-src/operator-chat/useRuntimeControls.js +29 -6
- package/dist/worker/console/static-src/operator-chat/useRuntimeSnapshot.js +8 -2
- package/dist/worker/console/static-src/shell/console-update-reload.js +52 -0
- package/dist/worker/console/static-src/shell/workspace-route.js +11 -0
- package/dist/worker/console/workspace-context.js +142 -2
- package/dist/worker/console/workspace-registry.js +2 -2
- package/dist/worker/continuation/worker-continuation.js +278 -0
- package/dist/worker/materialize/frontend-split-task-materializer.js +72 -0
- package/dist/worker/observe/health.js +1 -1
- package/dist/worker/observe/node-transparency.js +572 -0
- package/dist/worker/observe/routes.js +55 -1
- package/dist/worker/observe/static/api.js +69 -5
- package/dist/worker/observe/static/dag-context-reason-labels.d.ts +9 -0
- package/dist/worker/observe/static/dag-context-reason-labels.js +120 -0
- package/dist/worker/observe/static/dag-helpers.js +4 -2
- package/dist/worker/observe/static/dag-node-purpose.js +5 -0
- package/dist/worker/observe/static/inspect-workspace.js +23 -0
- package/dist/worker/observe/static/kpi.js +2 -2
- package/dist/worker/observe/static/operator-chrome.d.ts +10 -2
- package/dist/worker/observe/static/operator-chrome.js +37 -23
- package/dist/worker/observe/static/relations.js +2 -2
- package/dist/worker/observe/static/router.d.ts +12 -1
- package/dist/worker/observe/static/router.js +48 -6
- package/dist/worker/observe/static/run-processing.js +4 -2
- package/dist/worker/observe/static/shell-chrome.js +2 -2
- package/dist/worker/observe/static/state.js +8 -1
- package/dist/worker/observe/static/styles.css +1092 -86
- package/dist/worker/observe/static/views/dag-inspector.js +1228 -64
- package/dist/worker/observe/static/views/dag.js +10 -2
- package/dist/worker/observe/static/views/dags.js +3 -1
- package/dist/worker/observe/static/views/dashboard.js +14 -6
- package/dist/worker/observe/static/views/pool.js +7 -2
- package/dist/worker/observe/static/views/run.js +12 -3
- package/dist/worker/observe/static/views/session-timeline.js +142 -18
- package/dist/worker/observe/static/views/task.js +7 -2
- package/dist/worker/outcomes/adapters.js +19 -0
- package/dist/worker/outcomes/types.js +1 -0
- package/dist/worker/pool/attempt-lease.js +97 -66
- package/dist/worker/pool/begin-attempt-with-lease.js +1 -0
- package/dist/worker/pool/reconcile.js +46 -1
- package/dist/worker/pool/run-store.js +2 -0
- package/dist/worker/pool/state-projection.js +2 -0
- package/dist/worker/task-spec/workflow-routing.js +8 -3
- package/dist/workflows/dag/artifact-bindings.js +149 -0
- package/dist/workflows/dag/artifact-revision-schema-registry.js +31 -0
- package/dist/workflows/dag/backend-test-case-coverage-analysis.js +215 -28
- package/dist/workflows/dag/backend-test-markdown-workflow.js +13 -1
- package/dist/workflows/dag/backend-test-plan-protocol.js +106 -0
- package/dist/workflows/dag/backend-test-scenario-param.js +172 -25
- package/dist/workflows/dag/backend-test-scenario-partitions.js +62 -1
- package/dist/workflows/dag/backend-test-writer-completeness.js +104 -12
- package/dist/workflows/dag/budget-enforcement.js +10 -0
- package/dist/workflows/dag/context-receipt.js +305 -0
- package/dist/workflows/dag/context-transfer/context-bundle.js +423 -0
- package/dist/workflows/dag/context-transfer/operator-actions.js +61 -0
- package/dist/workflows/dag/context-transfer/renderers.js +93 -0
- package/dist/workflows/dag/contract-validator-registrations.js +1 -2
- package/dist/workflows/dag/dag-retry-schema.js +138 -0
- package/dist/workflows/dag/frontend-closeout.js +221 -0
- package/dist/workflows/dag/frontend-design-policy.js +400 -0
- package/dist/workflows/dag/frontend-human-decision.js +182 -0
- package/dist/workflows/dag/frontend-implementation-contract.js +1237 -192
- package/dist/workflows/dag/frontend-plan-render.js +2 -1
- package/dist/workflows/dag/frontend-prewrite-gate.js +256 -350
- package/dist/workflows/dag/frontend-provider-capability-matrix.js +159 -0
- package/dist/workflows/dag/frontend-recovery-capsule.js +455 -0
- package/dist/workflows/dag/frontend-recovery-controller.js +226 -0
- package/dist/workflows/dag/frontend-recovery-lineage.js +178 -0
- package/dist/workflows/dag/frontend-recovery-plan.js +21 -10
- package/dist/workflows/dag/frontend-recovery-run.js +166 -34
- package/dist/workflows/dag/frontend-repair.js +1 -432
- package/dist/workflows/dag/frontend-review-context.js +261 -15
- package/dist/workflows/dag/frontend-review-findings.js +270 -0
- package/dist/workflows/dag/frontend-shadow-dual-write.js +941 -0
- package/dist/workflows/dag/frontend-shape-capsule-store.js +191 -0
- package/dist/workflows/dag/frontend-shape-facts.js +419 -0
- package/dist/workflows/dag/frontend-shape.js +435 -0
- package/dist/workflows/dag/frontend-source-fidelity-ledger.js +108 -0
- package/dist/workflows/dag/frontend-split-application-service.js +203 -0
- package/dist/workflows/dag/frontend-split-orchestrator.js +899 -0
- package/dist/workflows/dag/frontend-typed-event-store.js +452 -0
- package/dist/workflows/dag/frontend-typed-event-transaction.js +180 -0
- package/dist/workflows/dag/frontend-verification-trace.js +252 -25
- package/dist/workflows/dag/frontend-worktree-diff.js +250 -17
- package/dist/workflows/dag/frontend-writer-admission.js +319 -0
- package/dist/workflows/dag/frontend-writer-rollback.js +32 -0
- package/dist/workflows/dag/frontend-writer-status.js +256 -0
- package/dist/workflows/dag/init-hybrid.js +1174 -562
- package/dist/workflows/dag/interrupt-request.js +7 -0
- package/dist/workflows/dag/node-execution.js +940 -31
- package/dist/workflows/dag/path-safety.js +1 -0
- package/dist/workflows/dag/prompt.js +99 -7
- package/dist/workflows/dag/recovery-lease.js +170 -0
- package/dist/workflows/dag/report.js +37 -1
- package/dist/workflows/dag/rerun-feedback.js +315 -1
- package/dist/workflows/dag/rerun-plan.js +568 -3
- package/dist/workflows/dag/rerun-run.js +642 -18
- package/dist/workflows/dag/rerun-task.js +251 -13
- package/dist/workflows/dag/retry-policy.js +219 -104
- package/dist/workflows/dag/runner.js +596 -121
- package/dist/workflows/dag/scheduler.js +133 -20
- package/dist/workflows/dag/skill-snapshot.js +17 -0
- package/dist/workflows/dag/types.js +426 -22
- package/dist/workflows/dag/validate.js +28 -12
- package/docs/architecture/runtime-boundaries.md +6 -6
- package/docs/architecture/worker-and-feature.md +1 -1
- package/docs/examples/README.md +5 -0
- package/docs/init-surface.manifest.json +30 -12
- package/docs/skills/vetted-skill-registry.md +4 -2
- package/docs/templates/README.md +2 -0
- package/docs/templates/agent-dag-report.schema.json +8 -2
- package/docs/templates/agent-dag.schema.json +33 -1
- package/docs/templates/backend-test-dag.json +23 -20
- package/docs/templates/frontend-implementation-contract.schema.json +4 -1
- package/docs/templates/frontend-implementation-dag.json +89 -0
- package/docs/templates/product-line/task.yaml +1 -1
- package/docs/templates/spec-registry.schema.json +45 -0
- package/harness.json +1 -1
- package/package.json +6 -3
- package/skills/frontend-bounded-implement/SKILL.md +15 -14
- package/skills/frontend-bounded-implement/references/code-standards.md +19 -0
- package/skills/frontend-contract/SKILL.md +23 -0
- package/skills/frontend-contract/references/contract-protocol.md +34 -0
- package/skills/frontend-design-review/SKILL.md +22 -41
- package/skills/frontend-plan/SKILL.md +26 -0
- package/skills/frontend-plan/references/decision-contract.md +37 -0
- package/skills/frontend-plan/references/design-decisions.md +17 -0
- package/skills/frontend-review/SKILL.md +20 -15
- package/skills/frontend-review/references/review-findings.md +6 -7
- package/skills/frontend-scout/SKILL.md +25 -0
- package/skills/frontend-scout/references/design-evidence.md +16 -0
- package/skills/frontend-scout/references/scout-evidence.md +23 -0
- package/skills/frontend-verification/SKILL.md +1 -1
- package/skills/loop-agent/references/command-reference.md +7 -0
- package/skills/loop-agent/references/hybrid-dag.md +2 -2
- package/dist/worker/console/operation-store.js +0 -169
- package/dist/worker/console/static/assets/channel-D1xajGpe.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-BmuGTfRr.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-BmuGTfRr.js +0 -1
- package/dist/worker/console/static/assets/index-BLzcAykx.js +0 -451
- package/dist/worker/console/static/assets/index-Cs9JUSPs.css +0 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-D9ykKEWP.js +0 -1
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-BvPOXALT.js +0 -8
- package/skills/frontend-implementation/SKILL.md +0 -52
- package/skills/frontend-implementation/references/code-standards.md +0 -33
- package/skills/frontend-implementation/references/design-spec.md +0 -56
- package/skills/frontend-implementation/references/node-contracts.md +0 -31
- /package/dist/{worker → infrastructure}/console/repo-fingerprint.js +0 -0
|
@@ -35,7 +35,12 @@ import {
|
|
|
35
35
|
parseMarkdownBlocks,
|
|
36
36
|
markdownLinkHref,
|
|
37
37
|
} from "../format.js";
|
|
38
|
-
import {
|
|
38
|
+
import {
|
|
39
|
+
parseRoute,
|
|
40
|
+
navigate,
|
|
41
|
+
buildHashPath,
|
|
42
|
+
withWorkspaceQuery,
|
|
43
|
+
} from "../router.js";
|
|
39
44
|
import {
|
|
40
45
|
fetchJson,
|
|
41
46
|
artifactUrl,
|
|
@@ -248,6 +253,7 @@ export async function renderDagDetail(dagRunId, initial = true) {
|
|
|
248
253
|
uiState.dagInspectorAttempt = null;
|
|
249
254
|
uiState.dagGraphViewportState = null;
|
|
250
255
|
uiState.dagTimelineViewportState = null;
|
|
256
|
+
uiState.dagTimelineAutoScrollEnabled = true;
|
|
251
257
|
}
|
|
252
258
|
// AC-001:同会话内重新进入同一 dagRunId 保持当前图视图模式(不切回逻辑拓扑)。
|
|
253
259
|
uiState.dagGraphViewMode = dagGraphViewModes.get(dagRunId) ?? "topology";
|
|
@@ -364,7 +370,9 @@ export async function renderDagDetail(dagRunId, initial = true) {
|
|
|
364
370
|
"page-action-btn",
|
|
365
371
|
"在操作台中止此运行 →",
|
|
366
372
|
);
|
|
367
|
-
interruptLink.href =
|
|
373
|
+
interruptLink.href = withWorkspaceQuery(
|
|
374
|
+
`/#/operate/recovery?dagRunId=${encodeURIComponent(dag.dagRunId)}&intent=interrupt`,
|
|
375
|
+
);
|
|
368
376
|
interruptLink.setAttribute("data-testid", "inspect-dag-interrupt-link");
|
|
369
377
|
interruptLink.title = "只跳转到操作台 Recovery,不会在观测页执行中止";
|
|
370
378
|
primaryEntries.push(["协作中止", interruptLink]);
|
|
@@ -327,7 +327,9 @@ function buildRow(run) {
|
|
|
327
327
|
|
|
328
328
|
const nameTd = document.createElement("td");
|
|
329
329
|
const nameLink = el("a", "dags-name-link", run.title || run.dagRunId);
|
|
330
|
-
nameLink.href =
|
|
330
|
+
nameLink.href = inspectPathToCanonicalHref(
|
|
331
|
+
`/dag/${encodeURIComponent(run.dagRunId)}`,
|
|
332
|
+
);
|
|
331
333
|
nameLink.addEventListener("click", (event) => {
|
|
332
334
|
event.preventDefault();
|
|
333
335
|
event.stopPropagation();
|
|
@@ -35,7 +35,13 @@ import {
|
|
|
35
35
|
parseMarkdownBlocks,
|
|
36
36
|
markdownLinkHref,
|
|
37
37
|
} from "../format.js";
|
|
38
|
-
import {
|
|
38
|
+
import {
|
|
39
|
+
parseRoute,
|
|
40
|
+
navigate,
|
|
41
|
+
buildHashPath,
|
|
42
|
+
inspectPathToCanonicalHref,
|
|
43
|
+
withWorkspaceQuery,
|
|
44
|
+
} from "../router.js";
|
|
39
45
|
import { fetchJson, artifactUrl, parseArtifactPreviewResponse } from "../api.js";
|
|
40
46
|
import {
|
|
41
47
|
objectRouteLink,
|
|
@@ -118,14 +124,14 @@ async function renderNightStrip() {
|
|
|
118
124
|
|
|
119
125
|
const actions = el("div", "night-strip-actions");
|
|
120
126
|
const detail = el("a", "page-action-btn", "夜间详情");
|
|
121
|
-
detail.href = "
|
|
127
|
+
detail.href = inspectPathToCanonicalHref("/night");
|
|
122
128
|
detail.addEventListener("click", (e) => {
|
|
123
129
|
e.preventDefault();
|
|
124
130
|
navigate("/night");
|
|
125
131
|
});
|
|
126
132
|
actions.appendChild(detail);
|
|
127
133
|
const manage = el("a", "page-action-btn night-strip-manage", "晨审处置 →");
|
|
128
|
-
manage.href = "/#/operate/night";
|
|
134
|
+
manage.href = withWorkspaceQuery("/#/operate/night");
|
|
129
135
|
manage.title = "夜间计划的管理与晨审处置在操作台完成(Inspect 保持只读)";
|
|
130
136
|
actions.appendChild(manage);
|
|
131
137
|
strip.appendChild(actions);
|
|
@@ -528,7 +534,7 @@ export async function renderDashboard(scrollTo) {
|
|
|
528
534
|
workerBlock.appendChild(el("div", "risk-total", String(block.total)));
|
|
529
535
|
workerBlock.appendChild(el("p", "risk-summary", block.summary));
|
|
530
536
|
const link = el("a", "risk-link", "查看异常 Task →");
|
|
531
|
-
link.href = "
|
|
537
|
+
link.href = inspectPathToCanonicalHref("/failures");
|
|
532
538
|
link.addEventListener("click", (event) => {
|
|
533
539
|
event.preventDefault();
|
|
534
540
|
navigate("/failures");
|
|
@@ -556,7 +562,7 @@ export async function renderDashboard(scrollTo) {
|
|
|
556
562
|
const head = el("div", "section-heading-row");
|
|
557
563
|
head.appendChild(el("h3", "section-heading", UI_TEXT.recentDags));
|
|
558
564
|
const allLink = el("a", "section-heading-action", UI_TEXT.viewAllDags);
|
|
559
|
-
allLink.href = "
|
|
565
|
+
allLink.href = inspectPathToCanonicalHref("/dags?page=1&pageSize=20");
|
|
560
566
|
allLink.setAttribute("aria-label", "查看全部 DAG 运行历史");
|
|
561
567
|
allLink.addEventListener("click", (event) => {
|
|
562
568
|
event.preventDefault();
|
|
@@ -686,7 +692,9 @@ function syncTableRow(row, cells) {
|
|
|
686
692
|
function buildDagTimelineItem(dag) {
|
|
687
693
|
const progress = dagProgress(dag);
|
|
688
694
|
const item = el("a", "dag-run-timeline-item");
|
|
689
|
-
item.href =
|
|
695
|
+
item.href = inspectPathToCanonicalHref(
|
|
696
|
+
`/dag/${encodeURIComponent(dag.dagRunId)}`,
|
|
697
|
+
);
|
|
690
698
|
item.title = dag.title || dag.dagRunId;
|
|
691
699
|
item.addEventListener("click", (event) => {
|
|
692
700
|
event.preventDefault();
|
|
@@ -8,7 +8,12 @@ import {
|
|
|
8
8
|
livenessBadge,
|
|
9
9
|
truncateText,
|
|
10
10
|
} from "../format.js";
|
|
11
|
-
import {
|
|
11
|
+
import {
|
|
12
|
+
parseRoute,
|
|
13
|
+
navigate,
|
|
14
|
+
buildHashPath,
|
|
15
|
+
inspectPathToCanonicalHref,
|
|
16
|
+
} from "../router.js";
|
|
12
17
|
import { fetchJson } from "../api.js";
|
|
13
18
|
import { objectRouteLink } from "../relations.js";
|
|
14
19
|
import {
|
|
@@ -118,7 +123,7 @@ function taskPath(task) {
|
|
|
118
123
|
|
|
119
124
|
function linkTo(path, label, className) {
|
|
120
125
|
const a = el("a", className || "object-link", label);
|
|
121
|
-
a.href =
|
|
126
|
+
a.href = inspectPathToCanonicalHref(path);
|
|
122
127
|
a.addEventListener("click", (e) => {
|
|
123
128
|
e.preventDefault();
|
|
124
129
|
e.stopPropagation();
|
|
@@ -35,7 +35,12 @@ import {
|
|
|
35
35
|
parseMarkdownBlocks,
|
|
36
36
|
markdownLinkHref,
|
|
37
37
|
} from "../format.js";
|
|
38
|
-
import {
|
|
38
|
+
import {
|
|
39
|
+
parseRoute,
|
|
40
|
+
navigate,
|
|
41
|
+
buildHashPath,
|
|
42
|
+
inspectPathToCanonicalHref,
|
|
43
|
+
} from "../router.js";
|
|
39
44
|
import {
|
|
40
45
|
fetchJson,
|
|
41
46
|
artifactUrl,
|
|
@@ -176,7 +181,9 @@ export function renderRunDagProgress(task, snapshot) {
|
|
|
176
181
|
"详情",
|
|
177
182
|
(() => {
|
|
178
183
|
const a = el("a", "link", "查看 DAG 详情 →");
|
|
179
|
-
a.href =
|
|
184
|
+
a.href = inspectPathToCanonicalHref(
|
|
185
|
+
`/dag/${encodeURIComponent(dag.dagRunId)}`,
|
|
186
|
+
);
|
|
180
187
|
a.addEventListener("click", (e) => {
|
|
181
188
|
e.preventDefault();
|
|
182
189
|
navigate(`/dag/${encodeURIComponent(dag.dagRunId)}`);
|
|
@@ -295,7 +302,9 @@ export function renderRunPanels(task, events, snapshot) {
|
|
|
295
302
|
li.appendChild(marker);
|
|
296
303
|
if (step.label === "run-dag" && dag?.dagRunId) {
|
|
297
304
|
const link = document.createElement("a");
|
|
298
|
-
link.href =
|
|
305
|
+
link.href = inspectPathToCanonicalHref(
|
|
306
|
+
`/dag/${encodeURIComponent(dag.dagRunId)}`,
|
|
307
|
+
);
|
|
299
308
|
link.className = "step-link";
|
|
300
309
|
link.textContent = "DAG →";
|
|
301
310
|
link.addEventListener("click", (e) => {
|
|
@@ -7,12 +7,72 @@ import { fetchJson } from "../api.js";
|
|
|
7
7
|
import {
|
|
8
8
|
uiState,
|
|
9
9
|
updateDagTimelineViewportState,
|
|
10
|
-
computeFollowLatest,
|
|
11
10
|
} from "../state.js";
|
|
12
11
|
|
|
13
12
|
/** Default visible lines for tool result preview before expand. */
|
|
14
13
|
export const TOOL_RESULT_PREVIEW_LINES = 20;
|
|
15
14
|
|
|
15
|
+
function isTimelineAutoScrollEnabled() {
|
|
16
|
+
return uiState.dagTimelineAutoScrollEnabled !== false;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function scrollTimelineToLatest(content) {
|
|
20
|
+
content.scrollLeft = 0;
|
|
21
|
+
content.scrollTop = content.scrollHeight;
|
|
22
|
+
uiState.dagTimelineViewportState = updateDagTimelineViewportState(
|
|
23
|
+
uiState.dagTimelineViewportState,
|
|
24
|
+
uiState.currentDagRunId,
|
|
25
|
+
content.dataset.nodeId,
|
|
26
|
+
content.scrollLeft,
|
|
27
|
+
content.scrollTop,
|
|
28
|
+
true,
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** Floating, explicit live-follow control for the execution-record viewport. */
|
|
33
|
+
export function createTimelineAutoScrollControl(content) {
|
|
34
|
+
const dock = el("div", "process-timeline-autoscroll-dock");
|
|
35
|
+
const button = document.createElement("button");
|
|
36
|
+
button.type = "button";
|
|
37
|
+
const signal = el("span", "process-timeline-autoscroll-signal");
|
|
38
|
+
signal.setAttribute("aria-hidden", "true");
|
|
39
|
+
const icon = el("i", "ri-arrow-down-line");
|
|
40
|
+
icon.setAttribute("aria-hidden", "true");
|
|
41
|
+
button.appendChild(signal);
|
|
42
|
+
button.appendChild(icon);
|
|
43
|
+
|
|
44
|
+
const sync = () => {
|
|
45
|
+
const enabled = isTimelineAutoScrollEnabled();
|
|
46
|
+
const actionLabel = enabled ? "关闭自动滚动" : "自动滚动";
|
|
47
|
+
button.className = `process-timeline-autoscroll${enabled ? " is-active" : ""}`;
|
|
48
|
+
button.setAttribute("aria-pressed", String(enabled));
|
|
49
|
+
button.setAttribute("aria-label", actionLabel);
|
|
50
|
+
button.setAttribute("data-tooltip", actionLabel);
|
|
51
|
+
icon.className = enabled ? "ri-arrow-down-line" : "ri-pause-mini-fill";
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
button.addEventListener("click", () => {
|
|
55
|
+
const enabled = !isTimelineAutoScrollEnabled();
|
|
56
|
+
uiState.dagTimelineAutoScrollEnabled = enabled;
|
|
57
|
+
if (enabled) scrollTimelineToLatest(content);
|
|
58
|
+
else {
|
|
59
|
+
uiState.dagTimelineViewportState = updateDagTimelineViewportState(
|
|
60
|
+
uiState.dagTimelineViewportState,
|
|
61
|
+
uiState.currentDagRunId,
|
|
62
|
+
content.dataset.nodeId,
|
|
63
|
+
content.scrollLeft,
|
|
64
|
+
content.scrollTop,
|
|
65
|
+
false,
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
sync();
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
sync();
|
|
72
|
+
dock.appendChild(button);
|
|
73
|
+
return dock;
|
|
74
|
+
}
|
|
75
|
+
|
|
16
76
|
export function normalizeSessionEventFields(event) {
|
|
17
77
|
if (!event || typeof event !== "object") return {};
|
|
18
78
|
const args = event.args ?? event.input ?? event.toolInput;
|
|
@@ -623,6 +683,35 @@ export function buildRichTimelineItems(events, options = {}) {
|
|
|
623
683
|
return items;
|
|
624
684
|
}
|
|
625
685
|
|
|
686
|
+
const EXECUTION_RECORD_FILTERS = Object.freeze([
|
|
687
|
+
["all", "全部"],
|
|
688
|
+
["model", "模型消息"],
|
|
689
|
+
["tools", "工具与进程"],
|
|
690
|
+
]);
|
|
691
|
+
|
|
692
|
+
function normalizeExecutionRecordFilter(value) {
|
|
693
|
+
return value === "model" || value === "tools" ? value : "all";
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
export function filterExecutionRecordItems(items, filter) {
|
|
697
|
+
const normalized = normalizeExecutionRecordFilter(filter);
|
|
698
|
+
if (normalized === "all") return items;
|
|
699
|
+
return items.filter((item) => {
|
|
700
|
+
const protocolEvents = Array.isArray(item.protocolEvents)
|
|
701
|
+
? item.protocolEvents
|
|
702
|
+
: [item.event];
|
|
703
|
+
const toolProtocol =
|
|
704
|
+
item.kind === "protocol" &&
|
|
705
|
+
protocolEvents.some((event) => {
|
|
706
|
+
const role = event?.message?.role ?? event?.role;
|
|
707
|
+
return role === "tool" || role === "toolResult";
|
|
708
|
+
});
|
|
709
|
+
const modelFact =
|
|
710
|
+
item.kind === "turn" || (item.kind === "protocol" && !toolProtocol);
|
|
711
|
+
return normalized === "model" ? modelFact : !modelFact;
|
|
712
|
+
});
|
|
713
|
+
}
|
|
714
|
+
|
|
626
715
|
function ensureOpenDetailsSet() {
|
|
627
716
|
if (!(uiState.sessionTimelineOpenDetails instanceof Set)) {
|
|
628
717
|
uiState.sessionTimelineOpenDetails = new Set();
|
|
@@ -866,7 +955,26 @@ export function renderSessionTimeline(content, nodeId, options = {}) {
|
|
|
866
955
|
}
|
|
867
956
|
|
|
868
957
|
const showProtocol = Boolean(uiState.sessionTimelineShowProtocol);
|
|
958
|
+
const activeFilter = normalizeExecutionRecordFilter(
|
|
959
|
+
uiState.executionRecordFilter,
|
|
960
|
+
);
|
|
869
961
|
const toolbar = el("div", "process-timeline-toolbar");
|
|
962
|
+
const filterGroup = el("div", "process-timeline-filter-group");
|
|
963
|
+
filterGroup.setAttribute("role", "group");
|
|
964
|
+
filterGroup.setAttribute("aria-label", "筛选执行记录");
|
|
965
|
+
for (const [filter, label] of EXECUTION_RECORD_FILTERS) {
|
|
966
|
+
const button = document.createElement("button");
|
|
967
|
+
button.type = "button";
|
|
968
|
+
button.className = "process-timeline-toggle process-timeline-filter";
|
|
969
|
+
button.setAttribute("aria-pressed", String(activeFilter === filter));
|
|
970
|
+
button.textContent = label;
|
|
971
|
+
button.addEventListener("click", () => {
|
|
972
|
+
uiState.executionRecordFilter = filter;
|
|
973
|
+
rerenderSessionTimeline(content, nodeId, options);
|
|
974
|
+
});
|
|
975
|
+
filterGroup.appendChild(button);
|
|
976
|
+
}
|
|
977
|
+
toolbar.appendChild(filterGroup);
|
|
870
978
|
const protocolToggle = document.createElement("button");
|
|
871
979
|
protocolToggle.type = "button";
|
|
872
980
|
protocolToggle.className = "process-timeline-toggle";
|
|
@@ -876,13 +984,7 @@ export function renderSessionTimeline(content, nodeId, options = {}) {
|
|
|
876
984
|
: "展开原始协议事件";
|
|
877
985
|
protocolToggle.addEventListener("click", () => {
|
|
878
986
|
uiState.sessionTimelineShowProtocol = !showProtocol;
|
|
879
|
-
|
|
880
|
-
const scrollTop = parent.scrollTop;
|
|
881
|
-
const scrollLeft = parent.scrollLeft;
|
|
882
|
-
while (parent.firstChild) parent.removeChild(parent.firstChild);
|
|
883
|
-
renderSessionTimeline(parent, nodeId, options);
|
|
884
|
-
parent.scrollTop = scrollTop;
|
|
885
|
-
parent.scrollLeft = scrollLeft;
|
|
987
|
+
rerenderSessionTimeline(content, nodeId, options);
|
|
886
988
|
});
|
|
887
989
|
toolbar.appendChild(protocolToggle);
|
|
888
990
|
const exportBtn = el("button", "btn btn-secondary", "导出 HTML");
|
|
@@ -909,10 +1011,12 @@ export function renderSessionTimeline(content, nodeId, options = {}) {
|
|
|
909
1011
|
});
|
|
910
1012
|
toolbar.appendChild(exportBtn);
|
|
911
1013
|
content.appendChild(toolbar);
|
|
1014
|
+
content.appendChild(createTimelineAutoScrollControl(content));
|
|
912
1015
|
|
|
913
|
-
const timelineItems =
|
|
914
|
-
showProtocol,
|
|
915
|
-
|
|
1016
|
+
const timelineItems = filterExecutionRecordItems(
|
|
1017
|
+
buildRichTimelineItems(visible.events, { showProtocol }),
|
|
1018
|
+
activeFilter,
|
|
1019
|
+
);
|
|
916
1020
|
const hasEventTimes = visible.events.some((event) =>
|
|
917
1021
|
Boolean(sessionEventTimestamp(event)),
|
|
918
1022
|
);
|
|
@@ -926,6 +1030,13 @@ export function renderSessionTimeline(content, nodeId, options = {}) {
|
|
|
926
1030
|
);
|
|
927
1031
|
}
|
|
928
1032
|
|
|
1033
|
+
if (timelineItems.length === 0) {
|
|
1034
|
+
content.appendChild(
|
|
1035
|
+
el("p", "empty", "当前筛选下没有可显示的执行记录。"),
|
|
1036
|
+
);
|
|
1037
|
+
return;
|
|
1038
|
+
}
|
|
1039
|
+
|
|
929
1040
|
const ul = el("ul", "process-timeline process-timeline-rich");
|
|
930
1041
|
for (let index = 0; index < timelineItems.length; index += 1) {
|
|
931
1042
|
const item = timelineItems[index];
|
|
@@ -980,23 +1091,18 @@ export function renderSessionTimeline(content, nodeId, options = {}) {
|
|
|
980
1091
|
content.addEventListener(
|
|
981
1092
|
"scroll",
|
|
982
1093
|
() => {
|
|
983
|
-
const followLatest = computeFollowLatest(
|
|
984
|
-
content.scrollTop,
|
|
985
|
-
content.scrollHeight,
|
|
986
|
-
content.clientHeight,
|
|
987
|
-
);
|
|
988
1094
|
uiState.dagTimelineViewportState = updateDagTimelineViewportState(
|
|
989
1095
|
uiState.dagTimelineViewportState,
|
|
990
1096
|
uiState.currentDagRunId,
|
|
991
1097
|
nodeId,
|
|
992
1098
|
content.scrollLeft,
|
|
993
1099
|
content.scrollTop,
|
|
994
|
-
|
|
1100
|
+
isTimelineAutoScrollEnabled(),
|
|
995
1101
|
);
|
|
996
1102
|
},
|
|
997
1103
|
{ passive: true },
|
|
998
1104
|
);
|
|
999
|
-
if (
|
|
1105
|
+
if (isTimelineAutoScrollEnabled()) {
|
|
1000
1106
|
content.scrollLeft = timelineState?.scrollLeft ?? 0;
|
|
1001
1107
|
content.scrollTop = content.scrollHeight;
|
|
1002
1108
|
} else {
|
|
@@ -1004,3 +1110,21 @@ export function renderSessionTimeline(content, nodeId, options = {}) {
|
|
|
1004
1110
|
content.scrollTop = timelineState.scrollTop;
|
|
1005
1111
|
}
|
|
1006
1112
|
}
|
|
1113
|
+
|
|
1114
|
+
function rerenderSessionTimeline(content, nodeId, options) {
|
|
1115
|
+
const scrollTop = content.scrollTop;
|
|
1116
|
+
const scrollLeft = content.scrollLeft;
|
|
1117
|
+
const autoScrollEnabled = isTimelineAutoScrollEnabled();
|
|
1118
|
+
const preserved = [...content.children].filter((child) =>
|
|
1119
|
+
String(child.className ?? "")
|
|
1120
|
+
.split(/\s+/)
|
|
1121
|
+
.includes("dag-inspector-attempt-bar"),
|
|
1122
|
+
);
|
|
1123
|
+
while (content.firstChild) content.removeChild(content.firstChild);
|
|
1124
|
+
for (const child of preserved) content.appendChild(child);
|
|
1125
|
+
renderSessionTimeline(content, nodeId, options);
|
|
1126
|
+
if (!autoScrollEnabled) {
|
|
1127
|
+
content.scrollTop = scrollTop;
|
|
1128
|
+
content.scrollLeft = scrollLeft;
|
|
1129
|
+
}
|
|
1130
|
+
}
|
|
@@ -36,7 +36,12 @@ import {
|
|
|
36
36
|
markdownLinkHref,
|
|
37
37
|
formatLedgerSummary,
|
|
38
38
|
} from "../format.js";
|
|
39
|
-
import {
|
|
39
|
+
import {
|
|
40
|
+
parseRoute,
|
|
41
|
+
navigate,
|
|
42
|
+
buildHashPath,
|
|
43
|
+
inspectPathToCanonicalHref,
|
|
44
|
+
} from "../router.js";
|
|
40
45
|
import {
|
|
41
46
|
fetchJson,
|
|
42
47
|
fetchJsonResult,
|
|
@@ -133,7 +138,7 @@ export async function renderTaskDetail(featureId, taskId) {
|
|
|
133
138
|
if (!c?.featureId || !c?.taskId) continue;
|
|
134
139
|
const path = `/feature/${encodeURIComponent(c.featureId)}/task/${encodeURIComponent(c.taskId)}`;
|
|
135
140
|
const a = el("a", "object-link", `${c.featureId}::${c.taskId}`);
|
|
136
|
-
a.href =
|
|
141
|
+
a.href = inspectPathToCanonicalHref(path);
|
|
137
142
|
a.addEventListener("click", (e) => {
|
|
138
143
|
e.preventDefault();
|
|
139
144
|
navigate(path);
|
|
@@ -98,6 +98,24 @@ class FrontendImplementationAdapter {
|
|
|
98
98
|
};
|
|
99
99
|
}
|
|
100
100
|
}
|
|
101
|
+
/**
|
|
102
|
+
* `backend-test-analysis` — read-only requirements/API analysis. It produces
|
|
103
|
+
* the strict Backend Test Analysis v2 contract without claiming that tests or
|
|
104
|
+
* a real integration ran.
|
|
105
|
+
*/
|
|
106
|
+
class BackendTestAnalysisAdapter {
|
|
107
|
+
workflow = "backend-test-analysis";
|
|
108
|
+
project(input) {
|
|
109
|
+
return {
|
|
110
|
+
artifacts: [
|
|
111
|
+
...canonicalArtifacts(input),
|
|
112
|
+
...structuredArtifactsFromReport(input.dagReportRun),
|
|
113
|
+
],
|
|
114
|
+
acceptanceCoverage: [...input.acceptanceRefs],
|
|
115
|
+
integrationStatus: { mock: false, real: false },
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
}
|
|
101
119
|
/**
|
|
102
120
|
* `backend-test` — generates + executes backend tests. Integration status is
|
|
103
121
|
* `real: true` only when the report completed (a real test execution); mock is
|
|
@@ -139,6 +157,7 @@ class FrontendTestAdapter {
|
|
|
139
157
|
const ADAPTERS = new Map([
|
|
140
158
|
["agent-dag", new AgentDagAdapter()],
|
|
141
159
|
["frontend-implementation", new FrontendImplementationAdapter()],
|
|
160
|
+
["backend-test-analysis", new BackendTestAnalysisAdapter()],
|
|
142
161
|
["backend-test", new BackendTestAdapter()],
|
|
143
162
|
["frontend-test", new FrontendTestAdapter()],
|
|
144
163
|
]);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { randomBytes } from "node:crypto";
|
|
2
2
|
import { mkdir, readFile, readdir, unlink } from "node:fs/promises";
|
|
3
3
|
import path from "node:path";
|
|
4
|
+
import { acquireDirLock } from "../../infrastructure/console/dir-lock.js";
|
|
4
5
|
import { writeJsonAtomic } from "../../infrastructure/harness/atomic-write.js";
|
|
5
6
|
import { assertSafeTaskPoolId, getTaskPoolRoot, } from "./run-store.js";
|
|
6
7
|
import { TASK_POOL_STATE_ERROR_CODES, TaskPoolStateError } from "./types.js";
|
|
@@ -19,6 +20,20 @@ export function getAttemptLeasePath(repoRoot, featureId, taskId) {
|
|
|
19
20
|
}
|
|
20
21
|
return leasePath;
|
|
21
22
|
}
|
|
23
|
+
function getAttemptLeaseMutationLockPath(repoRoot, featureId, taskId) {
|
|
24
|
+
assertSafeTaskPoolId(featureId, "featureId");
|
|
25
|
+
assertSafeTaskPoolId(taskId, "taskId");
|
|
26
|
+
return path.join(getAttemptLeasesRoot(repoRoot), "_locks", `${featureId}__${taskId}.lock`);
|
|
27
|
+
}
|
|
28
|
+
async function withAttemptLeaseMutationLock(repoRoot, featureId, taskId, operation) {
|
|
29
|
+
const lock = await acquireDirLock(getAttemptLeaseMutationLockPath(repoRoot, featureId, taskId));
|
|
30
|
+
try {
|
|
31
|
+
return await operation();
|
|
32
|
+
}
|
|
33
|
+
finally {
|
|
34
|
+
await lock.release();
|
|
35
|
+
}
|
|
36
|
+
}
|
|
22
37
|
function isLeaseRecord(value) {
|
|
23
38
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
24
39
|
return false;
|
|
@@ -30,7 +45,11 @@ function isLeaseRecord(value) {
|
|
|
30
45
|
typeof record.batchRunId === "string" &&
|
|
31
46
|
typeof record.token === "string" &&
|
|
32
47
|
typeof record.acquiredAt === "string" &&
|
|
33
|
-
typeof record.renewedAt === "string"
|
|
48
|
+
typeof record.renewedAt === "string" &&
|
|
49
|
+
(record.revisionId === undefined || typeof record.revisionId === "string") &&
|
|
50
|
+
(record.controllerAnchor === undefined ||
|
|
51
|
+
typeof record.controllerAnchor === "string") &&
|
|
52
|
+
(record.claimId === undefined || typeof record.claimId === "string"));
|
|
34
53
|
}
|
|
35
54
|
export async function readAttemptLease(repoRoot, featureId, taskId) {
|
|
36
55
|
const leasePath = getAttemptLeasePath(repoRoot, featureId, taskId);
|
|
@@ -47,82 +66,94 @@ export async function readAttemptLease(repoRoot, featureId, taskId) {
|
|
|
47
66
|
}
|
|
48
67
|
/**
|
|
49
68
|
* Acquire Task-scoped attempt lease before beginWorkerAttempt.
|
|
50
|
-
* Same identity+
|
|
69
|
+
* Same identity+claim reuse is idempotent; a competing admission or active
|
|
70
|
+
* identity conflicts. The Task-scoped mutation lock closes cross-process
|
|
71
|
+
* read-before-rename and release-vs-successor races.
|
|
51
72
|
*/
|
|
52
73
|
export async function acquireAttemptLease(input) {
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
if (existing
|
|
57
|
-
existing.
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
74
|
+
return withAttemptLeaseMutationLock(input.repoRoot, input.featureId, input.taskId, async () => {
|
|
75
|
+
const now = input.now ?? new Date();
|
|
76
|
+
const existing = await readAttemptLease(input.repoRoot, input.featureId, input.taskId);
|
|
77
|
+
if (existing) {
|
|
78
|
+
if (existing.workerRunId === input.workerRunId &&
|
|
79
|
+
existing.batchRunId === input.batchRunId &&
|
|
80
|
+
existing.featureId === input.featureId &&
|
|
81
|
+
existing.taskId === input.taskId &&
|
|
82
|
+
existing.revisionId === input.revisionId &&
|
|
83
|
+
existing.controllerAnchor === input.controllerAnchor &&
|
|
84
|
+
existing.claimId === input.claimId) {
|
|
85
|
+
return { status: "idempotent-reuse", lease: existing };
|
|
86
|
+
}
|
|
87
|
+
throw new TaskPoolStateError(TASK_POOL_STATE_ERROR_CODES.CONFLICT, `attempt-lease-conflict for ${input.featureId}/${input.taskId}: held by ${existing.workerRunId}`);
|
|
61
88
|
}
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
? {
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
89
|
+
const lease = {
|
|
90
|
+
schemaVersion: 1,
|
|
91
|
+
featureId: input.featureId,
|
|
92
|
+
taskId: input.taskId,
|
|
93
|
+
workerRunId: input.workerRunId,
|
|
94
|
+
batchRunId: input.batchRunId,
|
|
95
|
+
acquiredAt: now.toISOString(),
|
|
96
|
+
renewedAt: now.toISOString(),
|
|
97
|
+
token: `atk-${randomBytes(8).toString("hex")}`,
|
|
98
|
+
ownerProcess: { pid: process.pid },
|
|
99
|
+
...(input.revisionId ? { revisionId: input.revisionId } : {}),
|
|
100
|
+
...(input.controllerAnchor
|
|
101
|
+
? { controllerAnchor: input.controllerAnchor }
|
|
102
|
+
: {}),
|
|
103
|
+
...(input.claimId ? { claimId: input.claimId } : {}),
|
|
104
|
+
};
|
|
105
|
+
await mkdir(path.dirname(getAttemptLeasePath(input.repoRoot, input.featureId, input.taskId)), {
|
|
106
|
+
recursive: true,
|
|
107
|
+
});
|
|
108
|
+
await writeJsonAtomic(getAttemptLeasePath(input.repoRoot, input.featureId, input.taskId), lease, { repoRoot: input.repoRoot });
|
|
109
|
+
const confirmed = await readAttemptLease(input.repoRoot, input.featureId, input.taskId);
|
|
110
|
+
if (!confirmed) {
|
|
111
|
+
throw new Error(`failed to persist attempt lease for ${input.featureId}/${input.taskId}`);
|
|
112
|
+
}
|
|
113
|
+
if (confirmed.token !== lease.token) {
|
|
114
|
+
throw new TaskPoolStateError(TASK_POOL_STATE_ERROR_CODES.CONFLICT, `attempt-lease-conflict race for ${input.featureId}/${input.taskId}`);
|
|
115
|
+
}
|
|
116
|
+
return { status: "acquired", lease: confirmed };
|
|
81
117
|
});
|
|
82
|
-
await writeJsonAtomic(getAttemptLeasePath(input.repoRoot, input.featureId, input.taskId), lease, { repoRoot: input.repoRoot });
|
|
83
|
-
const confirmed = await readAttemptLease(input.repoRoot, input.featureId, input.taskId);
|
|
84
|
-
if (!confirmed) {
|
|
85
|
-
throw new Error(`failed to persist attempt lease for ${input.featureId}/${input.taskId}`);
|
|
86
|
-
}
|
|
87
|
-
if (confirmed.workerRunId !== input.workerRunId) {
|
|
88
|
-
throw new TaskPoolStateError(TASK_POOL_STATE_ERROR_CODES.CONFLICT, `attempt-lease-conflict race for ${input.featureId}/${input.taskId}`);
|
|
89
|
-
}
|
|
90
|
-
return { status: "acquired", lease: confirmed };
|
|
91
118
|
}
|
|
92
119
|
export async function renewAttemptLease(input) {
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
120
|
+
return withAttemptLeaseMutationLock(input.repoRoot, input.featureId, input.taskId, async () => {
|
|
121
|
+
const existing = await readAttemptLease(input.repoRoot, input.featureId, input.taskId);
|
|
122
|
+
if (!existing) {
|
|
123
|
+
throw new TaskPoolStateError(TASK_POOL_STATE_ERROR_CODES.CONFLICT, `cannot renew missing lease for ${input.featureId}/${input.taskId}`);
|
|
124
|
+
}
|
|
125
|
+
if (existing.token !== input.token || existing.workerRunId !== input.workerRunId) {
|
|
126
|
+
throw new TaskPoolStateError(TASK_POOL_STATE_ERROR_CODES.CONFLICT, `cannot renew lease: CAS token/identity mismatch for ${input.featureId}/${input.taskId}`);
|
|
127
|
+
}
|
|
128
|
+
const renewed = {
|
|
129
|
+
...existing,
|
|
130
|
+
renewedAt: (input.now ?? new Date()).toISOString(),
|
|
131
|
+
};
|
|
132
|
+
await writeJsonAtomic(getAttemptLeasePath(input.repoRoot, input.featureId, input.taskId), renewed, { repoRoot: input.repoRoot });
|
|
133
|
+
return renewed;
|
|
134
|
+
});
|
|
106
135
|
}
|
|
107
136
|
/**
|
|
108
137
|
* Release active lease after terminal/recovery evidence. Moves to lease-history then deletes active locator.
|
|
109
138
|
*/
|
|
110
139
|
export async function releaseAttemptLease(input) {
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
140
|
+
await withAttemptLeaseMutationLock(input.repoRoot, input.featureId, input.taskId, async () => {
|
|
141
|
+
const existing = await readAttemptLease(input.repoRoot, input.featureId, input.taskId);
|
|
142
|
+
if (!existing)
|
|
143
|
+
return;
|
|
144
|
+
if (existing.token !== input.token || existing.workerRunId !== input.workerRunId) {
|
|
145
|
+
throw new TaskPoolStateError(TASK_POOL_STATE_ERROR_CODES.CONFLICT, `cannot release lease: CAS token/identity mismatch for ${input.featureId}/${input.taskId}`);
|
|
146
|
+
}
|
|
147
|
+
const historyRoot = path.join(getAttemptLeasesRoot(input.repoRoot), "_history");
|
|
148
|
+
await mkdir(historyRoot, { recursive: true });
|
|
149
|
+
const historyPath = path.join(historyRoot, `${input.featureId}__${input.taskId}__${existing.workerRunId}.json`);
|
|
150
|
+
const activePath = getAttemptLeasePath(input.repoRoot, input.featureId, input.taskId);
|
|
151
|
+
await writeJsonAtomic(historyPath, {
|
|
152
|
+
...existing,
|
|
153
|
+
releasedAt: (input.now ?? new Date()).toISOString(),
|
|
154
|
+
}, { repoRoot: input.repoRoot });
|
|
155
|
+
await unlink(activePath).catch(() => { });
|
|
156
|
+
});
|
|
126
157
|
}
|
|
127
158
|
export function decideAttemptLeaseReclaim(input) {
|
|
128
159
|
if (input.hasTerminalEvidence) {
|