@tea-agent/loop-agent 0.39.0-beta.0 → 0.39.0-beta.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +4 -1
- package/CHANGELOG.md +248 -95
- package/README.md +9 -5
- package/bin/loop-agent.js +7 -3
- package/dist/application/task-lifecycle/advance.js +12 -0
- package/dist/application/task-lifecycle/recommendations.js +13 -2
- package/dist/build-stamp.json +3 -3
- package/dist/cli/command-definitions.js +23 -5
- package/dist/cli/program.js +21 -2
- package/dist/cli/update/notifier.js +2 -2
- package/dist/cli/update/runtime-activity.js +1 -29
- package/dist/cli.js +2 -1
- package/dist/commands/client-recovery.js +3 -0
- package/dist/commands/dag-follow-up.js +138 -0
- package/dist/commands/dag-rerun.js +55 -1
- package/dist/commands/init-model-catalog.js +464 -0
- package/dist/commands/init-upgrade.js +265 -97
- package/dist/commands/init.js +455 -28
- package/dist/commands/inspect-next.js +8 -0
- package/dist/commands/task-advance.js +19 -0
- package/dist/executors/dag-pi-executor.js +2306 -61
- package/dist/executors/pi-executor.js +7 -0
- package/dist/executors/pi-extension-resolver.js +233 -0
- package/dist/executors/pi-playwright-cli-tool.js +74 -28
- package/dist/executors/pi-read-budget-policy.js +239 -0
- package/dist/executors/pi-sdk-executor.js +180 -33
- package/dist/executors/shell-executor.js +1249 -224
- package/dist/executors/shell-write-guard.js +14 -0
- package/dist/executors/workspace-write-snapshot.js +68 -0
- package/dist/infrastructure/harness/atomic-write.js +23 -0
- package/dist/shared/dag-failure-category.js +150 -0
- package/dist/shared/dag-prompt-override.js +27 -0
- package/dist/shared/openspec-spec.js +70 -4
- package/dist/shared/operator/capabilities.js +39 -0
- package/dist/shared/playwright-cli-command-policy.js +15 -0
- package/dist/shared/update/console-notifier.js +100 -0
- package/dist/{cli → shared}/update/npm-client.js +70 -15
- package/dist/{cli → shared}/update/state.js +41 -13
- package/dist/task/config-types.js +50 -5
- package/dist/task/contract/apply.js +11 -1
- package/dist/task/contract/constants.js +2 -0
- package/dist/task/contract/hash.js +3 -0
- package/dist/task/contract/observe.js +25 -4
- package/dist/task/contract/paths.js +2 -1
- package/dist/task/contract/project.js +18 -1
- package/dist/task/contract/schema.js +4 -1
- package/dist/task/contract/transaction.js +12 -0
- package/dist/task/frontend-project-capability.js +199 -18
- package/dist/task/source-prepare/completeness.js +188 -0
- package/dist/task/source-prepare/fragment-inventory.js +477 -0
- package/dist/task/source-prepare/index.js +3 -0
- package/dist/task/source-prepare/ledger-reconciliation.js +127 -0
- package/dist/task/source-prepare/ledger-review.js +214 -0
- package/dist/task/source-prepare/ledger.js +545 -0
- package/dist/task/source-prepare/parse-intent.js +24 -4
- package/dist/task/source-prepare/prepare.js +298 -1
- package/dist/task/source-prepare/semantic-intake.js +25 -5
- package/dist/task/source-prepare/source-fidelity-pi.js +384 -0
- package/dist/task/source-prepare/types.js +22 -0
- package/dist/task/source-references.js +22 -1
- package/dist/worker/cli.js +18 -2
- package/dist/worker/console/chat/chat-event-store.js +62 -4
- package/dist/worker/console/chat/context-panel.js +7 -0
- package/dist/worker/console/chat/deferred-turn.js +12 -0
- package/dist/worker/console/chat/operation-card.js +8 -1
- package/dist/worker/console/chat/pi-console-config.js +81 -10
- package/dist/worker/console/chat/pi-runtime.js +264 -16
- package/dist/worker/console/chat/provider-error.js +98 -0
- package/dist/worker/console/chat/routes.js +298 -32
- package/dist/worker/console/chat/session-stats.js +179 -0
- package/dist/worker/console/chat/session-store.js +93 -63
- package/dist/worker/console/chat/todos.js +115 -0
- package/dist/worker/console/chat/tool-preview.js +42 -0
- package/dist/worker/console/chat/turn-process.js +20 -55
- package/dist/worker/console/chat/user-questions.js +266 -0
- package/dist/worker/console/chat/workspace-landing.js +14 -0
- package/dist/worker/console/console-handoff.js +82 -0
- package/dist/worker/console/console-update-and-init.js +115 -0
- package/dist/worker/console/console-update-runtime.js +84 -0
- package/dist/worker/console/dag-execution-receipt.js +33 -0
- package/dist/worker/console/draft-store.js +49 -0
- package/dist/worker/console/frontend-human-decision-adapter.js +19 -0
- package/dist/worker/console/frontend-split-operation-adapter.js +20 -0
- package/dist/worker/console/index.js +3 -0
- package/dist/worker/console/inspect-split.js +18 -0
- package/dist/worker/console/native-directory-picker.js +13 -0
- package/dist/worker/console/operation-run-facts.js +19 -3
- package/dist/worker/console/operation-store.js +227 -7
- package/dist/worker/console/operator-actions.js +388 -0
- package/dist/worker/console/operator-surface-health.js +1 -0
- package/dist/worker/console/operator-user-error.js +2 -2
- package/dist/worker/console/prd-intake-bridge.js +13 -0
- package/dist/worker/console/routes.js +355 -3
- package/dist/worker/console/security.js +67 -13
- package/dist/worker/console/server.js +609 -164
- package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-C4kVYweA.js → abnfDiagram-N423BO3Z-BAsjg7ew.js} +1 -1
- package/dist/worker/console/static/assets/{arc-Bj2M8iO5.js → arc-CyIlrwZw.js} +1 -1
- package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-CLlXe4-6.js → architectureDiagram-T3A2C74G-Bo4tobhH.js} +1 -1
- package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-psEp5xH0.js → blockDiagram-VBNYF7ZC-Dfpow9gu.js} +1 -1
- package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-DeKcOCGJ.js → c4Diagram-5PPSVZJV-DPmzG_B4.js} +1 -1
- package/dist/worker/console/static/assets/channel-DSrJAn0-.js +1 -0
- package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-K0C744p1.js → chunk-2GRJ4B5K-pKQpB1UW.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-xbNw4J9-.js → chunk-2Q5K7J3B-5n9YYquZ.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5RXB4S5H-DjLaSDUO.js → chunk-5RXB4S5H-DP3CtA2a.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5VM5RSS4-CtiPlKel.js → chunk-5VM5RSS4-DYxMJPUy.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-DZtolir7.js → chunk-6Q2QTUOP-CS3D50Io.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-GF5L2VYU-CPcdNeew.js → chunk-GF5L2VYU-gSdDfo5_.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-JWPE2WC7-NgHc6V37.js → chunk-JWPE2WC7-A48q0ZNt.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-KBJHAD2P-DQ_T-hg8.js → chunk-KBJHAD2P-ow8Mm3DY.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-RYQCIY6F-DgLsxcVP.js → chunk-RYQCIY6F-CMKfLWYE.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-XXDRQBXY-UrVoM9z1.js → chunk-XXDRQBXY-BiTBdQPX.js} +1 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-DeMmPl0_.js +1 -0
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-DeMmPl0_.js +1 -0
- package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-CLkYvTb3.js → cose-bilkent-JH36ORCC-Cm7exRz_.js} +1 -1
- package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-BwT_xKrE.js → cynefin-VYW2F7L2-D9O5qGOu.js} +1 -1
- package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-Bi9tuzif.js → cynefinDiagram-MW4NZA55-CkvQnako.js} +1 -1
- package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-BCIqkWBV.js → dagre-VZM6K2ZE-B1rzp7CB.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-7IWD3JNH-Dip6l9_Z.js → diagram-7IWD3JNH-BP07Ipcc.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-B-xkh_wK.js → diagram-B4RE2ZJO-BlPzweQb.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-LBJQPF4R-DZO0kTt_.js → diagram-LBJQPF4R-CxPa7n0S.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-Q27KOJAE-D6ZplNbZ.js → diagram-Q27KOJAE-CMTZs53A.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-UB23O5K3-BPekbhoS.js → diagram-UB23O5K3-CxG_X52r.js} +1 -1
- package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-BtmaJpK5.js → ebnfDiagram-BXEA7PRR-D8axtJM0.js} +1 -1
- package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-6DS0Xc44.js → erDiagram-JOGREHBK-DIsGzn7b.js} +1 -1
- package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-CihnROSm.js → flowDiagram-UKHOOZJN-CU9zkDG0.js} +1 -1
- package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-CZC4zOE2.js → ganttDiagram-PKOTCBZU-DCjTmz5P.js} +1 -1
- package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-Dq1fhzGN.js → gitGraphDiagram-DS77QQ5N-BksdmFzg.js} +1 -1
- package/dist/worker/console/static/assets/index-B-O5ChYq.css +1 -0
- package/dist/worker/console/static/assets/index-CfnY8AQx.js +407 -0
- package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-DlSl4BGx.js → infoDiagram-6WML65LV-CGzzzJtL.js} +1 -1
- package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-snnzFl_U.js → ishikawaDiagram-WSZJBQD7-BdseB80N.js} +1 -1
- package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-BDE7qpMx.js → journeyDiagram-NVQOT4AX-DM9gsgha.js} +1 -1
- package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-CD2Ci04G.js → kanban-definition-27J2QSJJ-3q-_9R2b.js} +1 -1
- package/dist/worker/console/static/assets/{linear-DdnapKIH.js → linear-B8N-Mxsd.js} +1 -1
- package/dist/worker/console/static/assets/{mermaid.core-BVjAT9b8.js → mermaid.core-EPIHUUsu.js} +5 -5
- package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-D0yJk-zA.js → mindmap-definition-FAOFIHXS-CxwO12aa.js} +1 -1
- package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-BVo9tFP-.js → pegDiagram-VL7TDLO6-bPqbVX6Y.js} +1 -1
- package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-DnOV-mZ_.js → pieDiagram-7S7Q4E2Y-KzqIvzeX.js} +1 -1
- package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-CzUJo58i.js → quadrantDiagram-CIZ2JOQS-CyofpWiV.js} +1 -1
- package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-Bvikrolh.js → railroadDiagram-AXF67PYL-cTWJvLTP.js} +1 -1
- package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-DtaSVCap.js → requirementDiagram-LRYGKXZP-BK--q27B.js} +1 -1
- package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-C2c9A0wW.js → sankeyDiagram-W5VNT64P-C0SDOHXK.js} +1 -1
- package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-DmXJcU7r.js → sequenceDiagram-SI44F4Z6-Cong6YIo.js} +1 -1
- package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-B4GUFW92.js → sizeCapture-X5ZJPWSS-DUVkgZx8.js} +1 -1
- package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-Dsad2MXf.js → stateDiagram-OKZ733FA-C7XgxuZg.js} +1 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-Dzbnd5oX.js +1 -0
- package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-DGq48Fbi.js → swimlanes-SLNWSIFB-D9Ayvwcy.js} +2 -2
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-BY0hHk6G.js +8 -0
- package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-CSSi6OFf.js → timeline-definition-Z64GVDOM-Byb000cH.js} +1 -1
- package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-BqyzPrWv.js → vennDiagram-T6HMQDX7-DMLegPt4.js} +1 -1
- package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-DlznVb6S.js → wardleyDiagram-T6FBY63Y-ChMMeOsP.js} +1 -1
- package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-CfBcgJ_K.js → xychartDiagram-ELKLHX3M-bEMEPisW.js} +1 -1
- package/dist/worker/console/static/index.html +2 -2
- package/dist/worker/console/static-src/active-run-badge.js +17 -0
- package/dist/worker/console/static-src/app/console-types.js +68 -4
- package/dist/worker/console/static-src/app/useConsoleShell.js +30 -12
- package/dist/worker/console/static-src/app/useOperatorActions.js +41 -6
- package/dist/worker/console/static-src/app/useRecoveryConsole.js +45 -10
- package/dist/worker/console/static-src/app/useRunProgress.js +47 -0
- package/dist/worker/console/static-src/app/useTaskWizard.js +74 -2
- package/dist/worker/console/static-src/night/useNightBoard.js +8 -5
- package/dist/worker/console/static-src/operator-chat/cards/failure-category-advice.js +25 -0
- package/dist/worker/console/static-src/operator-chat/chat-sse-events.js +140 -8
- package/dist/worker/console/static-src/operator-chat/details-dag-actions.js +122 -0
- package/dist/worker/console/static-src/operator-chat/input-history.js +76 -0
- package/dist/worker/console/static-src/operator-chat/mutation-gate.js +1 -1
- package/dist/worker/console/static-src/operator-chat/refs.js +3 -0
- package/dist/worker/console/static-src/operator-chat/runtime-selection-labels.js +27 -0
- package/dist/worker/console/static-src/operator-chat/turn-submission.js +16 -3
- package/dist/worker/console/static-src/operator-chat/useChatSessions.js +168 -72
- package/dist/worker/console/static-src/operator-chat/useChatStream.js +102 -21
- package/dist/worker/console/static-src/operator-chat/useChatThread.js +184 -4
- package/dist/worker/console/static-src/operator-chat/useComposer.js +32 -0
- package/dist/worker/console/static-src/operator-chat/useRepoBrowser.js +63 -9
- package/dist/worker/console/static-src/operator-chat/useWorkspaceBrowserGate.js +4 -0
- package/dist/worker/console/static-src/operator-chat/workspace-layout-mode.js +3 -3
- package/dist/worker/console/static-src/pages/tasks/run-id-resolution.js +79 -0
- package/dist/worker/console/static-src/pages/tasks/run-ownership-verify.js +23 -0
- package/dist/worker/console/static-src/pages/tasks/run-panel-progress.js +110 -0
- package/dist/worker/console/static-src/shell/useWorkspaces.js +299 -0
- package/dist/worker/console/static-src/shell/workspace-route.js +337 -0
- package/dist/worker/console/workspace-context.js +344 -0
- package/dist/worker/console/workspace-registry.js +214 -0
- package/dist/worker/loop-agent/loop-agent-client.js +7 -3
- package/dist/worker/materialize/frontend-split-task-materializer.js +72 -0
- package/dist/worker/materialize/harness-task-lineage.js +5 -2
- package/dist/worker/observability/init-runtime-activity.js +26 -0
- package/dist/worker/observability/read-model.js +22 -0
- package/dist/worker/observe/node-input.js +160 -3
- package/dist/worker/observe/node-process.js +377 -0
- package/dist/worker/observe/routes.js +224 -3
- package/dist/worker/observe/server.js +4 -0
- package/dist/worker/observe/shell-handler-keys.js +34 -0
- package/dist/worker/observe/static/api.js +90 -4
- package/dist/worker/observe/static/app.js +18 -70
- package/dist/worker/observe/static/constants.js +8 -1
- package/dist/worker/observe/static/dag-history-labels.js +4 -0
- package/dist/worker/observe/static/dag-node-purpose.d.ts +6 -0
- package/dist/worker/observe/static/dag-node-purpose.js +208 -0
- package/dist/worker/observe/static/format.js +60 -0
- package/dist/worker/observe/static/index.html +6 -1
- package/dist/worker/observe/static/inspect-workspace.js +307 -0
- package/dist/worker/observe/static/markdown-render.js +20 -1
- package/dist/worker/observe/static/operator-chrome.css +145 -20
- package/dist/worker/observe/static/operator-chrome.d.ts +20 -3
- package/dist/worker/observe/static/operator-chrome.js +330 -96
- package/dist/worker/observe/static/prompt-restart-candidates.d.ts +18 -0
- package/dist/worker/observe/static/prompt-restart-candidates.js +36 -0
- package/dist/worker/observe/static/router.d.ts +46 -0
- package/dist/worker/observe/static/router.js +25 -1
- package/dist/worker/observe/static/state.d.ts +50 -0
- package/dist/worker/observe/static/state.js +184 -1
- package/dist/worker/observe/static/styles.css +395 -0
- package/dist/worker/observe/static/views/dag-graph.js +4 -2
- package/dist/worker/observe/static/views/dag-inspector.js +1067 -9
- package/dist/worker/observe/static/views/dag.d.ts +13 -0
- package/dist/worker/observe/static/views/dag.js +14 -5
- package/dist/worker/observe/static/views/dashboard.js +6 -4
- package/dist/worker/observe/static/views/failures.js +5 -2
- package/dist/worker/observe/static/views/night.js +2 -2
- package/dist/worker/observe/static/views/pool.js +1 -0
- package/dist/worker/observe/static/views/run.js +3 -1
- package/dist/worker/observe/static/views/session-timeline.js +78 -9
- package/dist/worker/observe/static/views/task.js +56 -1
- package/dist/workflows/dag/backend-test-case-coverage-analysis.js +49 -3
- package/dist/workflows/dag/backend-test-markdown-workflow.js +176 -1
- package/dist/workflows/dag/backend-test-pytest-collection.js +98 -14
- package/dist/workflows/dag/backend-test-writer-completeness.js +10 -5
- package/dist/workflows/dag/contract-output-registry.js +15 -0
- package/dist/workflows/dag/contract-validator-registrations.js +1 -2
- package/dist/workflows/dag/dynamic-runtime/loop-until.js +1 -1
- package/dist/workflows/dag/dynamic-runtime/map.js +1 -1
- package/dist/workflows/dag/failure-category.js +7 -116
- package/dist/workflows/dag/frontend-closeout.js +221 -0
- package/dist/workflows/dag/frontend-design-policy.js +400 -0
- package/dist/workflows/dag/frontend-human-decision.js +182 -0
- package/dist/workflows/dag/frontend-implementation-contract.js +534 -178
- package/dist/workflows/dag/frontend-plan-render.js +24 -0
- package/dist/workflows/dag/frontend-prewrite-gate.js +371 -393
- package/dist/workflows/dag/frontend-provider-capability-matrix.js +159 -0
- package/dist/workflows/dag/frontend-recovery-capsule.js +455 -0
- package/dist/workflows/dag/frontend-recovery-controller.js +202 -0
- package/dist/workflows/dag/frontend-recovery-lineage.js +178 -0
- package/dist/workflows/dag/frontend-recovery-plan.js +17 -10
- package/dist/workflows/dag/frontend-recovery-run.js +33 -20
- package/dist/workflows/dag/frontend-repair.js +7 -424
- package/dist/workflows/dag/frontend-review-context.js +288 -14
- package/dist/workflows/dag/frontend-review-findings.js +270 -0
- package/dist/workflows/dag/frontend-risk.js +15 -2
- package/dist/workflows/dag/frontend-shadow-dual-write.js +814 -0
- package/dist/workflows/dag/frontend-shape-capsule-store.js +191 -0
- package/dist/workflows/dag/frontend-shape-facts.js +409 -0
- package/dist/workflows/dag/frontend-shape.js +427 -0
- package/dist/workflows/dag/frontend-source-fidelity-ledger.js +108 -0
- package/dist/workflows/dag/frontend-split-application-service.js +203 -0
- package/dist/workflows/dag/frontend-split-orchestrator.js +899 -0
- package/dist/workflows/dag/frontend-test-case-checklist.js +30 -4
- package/dist/workflows/dag/frontend-test-case-manifest.js +11 -4
- package/dist/workflows/dag/frontend-test-case-quality.js +11 -16
- package/dist/workflows/dag/frontend-test-environment-probe.js +230 -0
- package/dist/workflows/dag/frontend-test-html-report.js +3 -1
- package/dist/workflows/dag/frontend-test-l5-report.js +3 -1
- package/dist/workflows/dag/frontend-test-layout.js +159 -0
- package/dist/workflows/dag/frontend-test-markdown.js +61 -0
- package/dist/workflows/dag/frontend-test-result-contract.js +188 -42
- package/dist/workflows/dag/frontend-test-standard-scenarios.js +70 -0
- package/dist/workflows/dag/frontend-typed-event-store.js +452 -0
- package/dist/workflows/dag/frontend-typed-event-transaction.js +180 -0
- package/dist/workflows/dag/frontend-verification-trace.js +52 -17
- package/dist/workflows/dag/frontend-worktree-diff.js +250 -17
- package/dist/workflows/dag/frontend-writer-admission.js +272 -0
- package/dist/workflows/dag/frontend-writer-status.js +244 -0
- package/dist/workflows/dag/init-hybrid.js +984 -618
- package/dist/workflows/dag/interrupt-request.js +10 -3
- package/dist/workflows/dag/lifecycle.js +3 -3
- package/dist/workflows/dag/node-execution.js +468 -34
- package/dist/workflows/dag/prompt.js +44 -19
- package/dist/workflows/dag/recovery-lease.js +80 -0
- package/dist/workflows/dag/report.js +37 -1
- package/dist/workflows/dag/rerun-feedback.js +124 -1
- package/dist/workflows/dag/rerun-plan.js +144 -5
- package/dist/workflows/dag/rerun-run.js +81 -6
- package/dist/workflows/dag/rerun-task.js +29 -0
- package/dist/workflows/dag/retry-policy.js +318 -8
- package/dist/workflows/dag/runner.js +429 -122
- package/dist/workflows/dag/scheduler.js +114 -16
- package/dist/workflows/dag/structured-output-repair.js +712 -0
- package/dist/workflows/dag/types.js +321 -31
- package/dist/workflows/dag/upstream-artifacts.js +0 -4
- package/dist/workflows/dag/validate.js +92 -32
- package/docs/README.md +2 -0
- package/docs/architecture/evolution.md +45 -0
- package/docs/init-surface.manifest.json +9 -0
- package/docs/operations/README.md +1 -1
- package/docs/operations/local-development-environment.md +21 -0
- package/docs/templates/README.md +2 -1
- package/docs/templates/agent-dag-report.schema.json +8 -2
- package/docs/templates/agent-dag.schema.json +45 -1
- package/docs/templates/frontend-implementation-contract.schema.json +684 -24
- package/docs/templates/frontend-test-case-checklist.md +2 -2
- package/docs/templates/frontend-test-dag.json +8 -10
- package/docs/templates/frontend-test-dag.retrieve-context.prompt.md +1 -1
- package/docs/templates/init-managed-agents.md +13 -3
- package/docs/templates/spec-registry.schema.json +45 -0
- package/harness.json +3 -3
- package/package.json +7 -1
- package/scripts/next-info.mjs +356 -0
- package/scripts/next-publish-gate.mjs +238 -0
- package/scripts/release-source-binding.mjs +251 -0
- package/skills/codebase-scout/SKILL.md +1 -1
- package/skills/fe-test-ui-scout/SKILL.md +65 -0
- package/skills/fe-test-ui-scout/references/ledger-schema.md +62 -0
- package/skills/fe-test-ui-scout/references/recon-protocol.md +54 -0
- package/skills/frontend-bounded-implement/SKILL.md +8 -14
- package/skills/frontend-design-review/SKILL.md +20 -41
- package/skills/frontend-implementation/SKILL.md +3 -4
- package/skills/frontend-implementation/references/design-spec.md +15 -10
- package/skills/frontend-implementation/references/node-contracts.md +23 -13
- package/skills/frontend-review/SKILL.md +22 -14
- package/skills/frontend-review/references/review-findings.md +6 -7
- package/skills/loop-agent/references/command-reference.md +12 -4
- package/skills/loop-agent/references/hybrid-dag.md +8 -1
- package/skills/playwright-cli/SKILL.md +23 -1
- package/skills/playwright-cli-case-generator/SKILL.md +29 -8
- package/dist/worker/console/static/assets/channel-CPF4N7pf.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-C2DwA_Y1.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-C2DwA_Y1.js +0 -1
- package/dist/worker/console/static/assets/index-DTZOKgAn.js +0 -404
- package/dist/worker/console/static/assets/index-Ya5FE7cD.css +0 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-B8FB_cNk.js +0 -1
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-CrZYaWfx.js +0 -8
|
@@ -0,0 +1,337 @@
|
|
|
1
|
+
export function resolveRouteWorkspaceSelection(workspaceId, knownWorkspaceIds) {
|
|
2
|
+
const id = workspaceId?.trim();
|
|
3
|
+
if (!id)
|
|
4
|
+
return { status: "ok", workspaceId: undefined };
|
|
5
|
+
if (knownWorkspaceIds.includes(id))
|
|
6
|
+
return { status: "ok", workspaceId: id };
|
|
7
|
+
return { status: "unknown-workspace", workspaceId: id };
|
|
8
|
+
}
|
|
9
|
+
export const OPERATE_TAB_IDS = [
|
|
10
|
+
"tasks",
|
|
11
|
+
"recovery",
|
|
12
|
+
"night",
|
|
13
|
+
"models",
|
|
14
|
+
"settings",
|
|
15
|
+
];
|
|
16
|
+
export const LAST_OPERATE_TAB_STORAGE_KEY = "loop-console.operator-shell.last-operate-tab.v1";
|
|
17
|
+
export const LAST_INSPECT_HASH_STORAGE_KEY = "loop-console.operator-shell.last-inspect-hash.v1";
|
|
18
|
+
export const DEFAULT_OPERATE_TAB = "tasks";
|
|
19
|
+
export const CHAT_HREF = "/#/chat";
|
|
20
|
+
export const DEFAULT_INSPECT_HREF = "/#/inspect";
|
|
21
|
+
export const DEFAULT_OPERATE_HREF = "/#/operate/tasks";
|
|
22
|
+
const INSPECT_COLLECTION_OR_OBJECT = /^(dag|run|task|feature|batch|night|pool|failures|dags)(\/|$)/;
|
|
23
|
+
export function splitHash(hashValue) {
|
|
24
|
+
const raw = String(hashValue ?? "").replace(/^#/, "");
|
|
25
|
+
const qIndex = raw.indexOf("?");
|
|
26
|
+
const pathPart = qIndex >= 0 ? raw.slice(0, qIndex) : raw;
|
|
27
|
+
const path = pathPart
|
|
28
|
+
? pathPart.startsWith("/")
|
|
29
|
+
? pathPart
|
|
30
|
+
: `/${pathPart}`
|
|
31
|
+
: "/";
|
|
32
|
+
const query = new URLSearchParams(qIndex >= 0 ? raw.slice(qIndex + 1) : "");
|
|
33
|
+
return { path, query };
|
|
34
|
+
}
|
|
35
|
+
export function stripInspectPrefix(path) {
|
|
36
|
+
if (path === "/inspect" || path === "/inspect/")
|
|
37
|
+
return "/";
|
|
38
|
+
if (path.startsWith("/inspect/"))
|
|
39
|
+
return path.slice("/inspect".length) || "/";
|
|
40
|
+
return path;
|
|
41
|
+
}
|
|
42
|
+
function isOperateTab(value) {
|
|
43
|
+
return (value === "tasks" ||
|
|
44
|
+
value === "recovery" ||
|
|
45
|
+
value === "night" ||
|
|
46
|
+
value === "models" ||
|
|
47
|
+
value === "settings");
|
|
48
|
+
}
|
|
49
|
+
function readParam(query, key) {
|
|
50
|
+
const value = query.get(key)?.trim();
|
|
51
|
+
return value || undefined;
|
|
52
|
+
}
|
|
53
|
+
function readWorkspaceParams(query) {
|
|
54
|
+
return {
|
|
55
|
+
...(readParam(query, "taskId") ? { taskId: readParam(query, "taskId") } : {}),
|
|
56
|
+
...(readParam(query, "dagRunId")
|
|
57
|
+
? { dagRunId: readParam(query, "dagRunId") }
|
|
58
|
+
: {}),
|
|
59
|
+
...(readParam(query, "fromNodeId")
|
|
60
|
+
? { fromNodeId: readParam(query, "fromNodeId") }
|
|
61
|
+
: {}),
|
|
62
|
+
...(readParam(query, "featureId")
|
|
63
|
+
? { featureId: readParam(query, "featureId") }
|
|
64
|
+
: {}),
|
|
65
|
+
...(readParam(query, "intent") ? { intent: readParam(query, "intent") } : {}),
|
|
66
|
+
...(readParam(query, "workspaceId")
|
|
67
|
+
? { workspaceId: readParam(query, "workspaceId") }
|
|
68
|
+
: {}),
|
|
69
|
+
...(readParam(query, "sessionId")
|
|
70
|
+
? { sessionId: readParam(query, "sessionId") }
|
|
71
|
+
: {}),
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
function paramsQuery(params) {
|
|
75
|
+
const query = new URLSearchParams();
|
|
76
|
+
for (const key of [
|
|
77
|
+
"taskId",
|
|
78
|
+
"dagRunId",
|
|
79
|
+
"fromNodeId",
|
|
80
|
+
"featureId",
|
|
81
|
+
"intent",
|
|
82
|
+
"workspaceId",
|
|
83
|
+
"sessionId",
|
|
84
|
+
]) {
|
|
85
|
+
const value = params[key]?.trim();
|
|
86
|
+
if (value)
|
|
87
|
+
query.set(key, value);
|
|
88
|
+
}
|
|
89
|
+
return query.toString();
|
|
90
|
+
}
|
|
91
|
+
export function readLastOperateTab(storage) {
|
|
92
|
+
try {
|
|
93
|
+
const raw = storage?.getItem(LAST_OPERATE_TAB_STORAGE_KEY);
|
|
94
|
+
if (isOperateTab(raw))
|
|
95
|
+
return raw;
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
/* ignore */
|
|
99
|
+
}
|
|
100
|
+
return DEFAULT_OPERATE_TAB;
|
|
101
|
+
}
|
|
102
|
+
export function rememberOperateTab(tab, storage) {
|
|
103
|
+
try {
|
|
104
|
+
storage?.setItem(LAST_OPERATE_TAB_STORAGE_KEY, tab);
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
/* ignore */
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
export function readLastInspectHref(storage) {
|
|
111
|
+
try {
|
|
112
|
+
const raw = storage?.getItem(LAST_INSPECT_HASH_STORAGE_KEY)?.trim();
|
|
113
|
+
if (raw?.startsWith("/#/inspect"))
|
|
114
|
+
return raw;
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
/* ignore */
|
|
118
|
+
}
|
|
119
|
+
return DEFAULT_INSPECT_HREF;
|
|
120
|
+
}
|
|
121
|
+
export function rememberInspectHref(href, storage) {
|
|
122
|
+
const canonical = href.startsWith("/#/inspect")
|
|
123
|
+
? href
|
|
124
|
+
: rewriteInspectHrefToCanonical(href);
|
|
125
|
+
if (!canonical.startsWith("/#/inspect"))
|
|
126
|
+
return;
|
|
127
|
+
try {
|
|
128
|
+
storage?.setItem(LAST_INSPECT_HASH_STORAGE_KEY, canonical);
|
|
129
|
+
}
|
|
130
|
+
catch {
|
|
131
|
+
/* ignore */
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
export function buildOperateHref(tab = DEFAULT_OPERATE_TAB, params = {}) {
|
|
135
|
+
const qs = paramsQuery(params);
|
|
136
|
+
return `/#/operate/${tab}${qs ? `?${qs}` : ""}`;
|
|
137
|
+
}
|
|
138
|
+
export function buildInspectHref(inspectPath = "/", query) {
|
|
139
|
+
const normalized = stripInspectPrefix(inspectPath.startsWith("/") ? inspectPath : `/${inspectPath}`);
|
|
140
|
+
const path = normalized === "/" || normalized === ""
|
|
141
|
+
? "/inspect"
|
|
142
|
+
: `/inspect${normalized}`;
|
|
143
|
+
const qs = typeof query === "string"
|
|
144
|
+
? query.replace(/^\?/, "")
|
|
145
|
+
: query?.toString() ?? "";
|
|
146
|
+
return `/#${path}${qs ? `?${qs}` : ""}`;
|
|
147
|
+
}
|
|
148
|
+
export function buildChatHref(params = {}) {
|
|
149
|
+
const qs = paramsQuery(params);
|
|
150
|
+
return qs ? `${CHAT_HREF}?${qs}` : CHAT_HREF;
|
|
151
|
+
}
|
|
152
|
+
/** Keep the selected Workspace on any canonical Console destination without
|
|
153
|
+
* changing its page-specific path or query state. */
|
|
154
|
+
export function withWorkspaceId(href, workspaceId) {
|
|
155
|
+
const canonical = href.startsWith("/#")
|
|
156
|
+
? href
|
|
157
|
+
: href.startsWith("#")
|
|
158
|
+
? `/${href}`
|
|
159
|
+
: rewriteInspectHrefToCanonical(href);
|
|
160
|
+
const { path, query } = splitHash(canonical.slice(2));
|
|
161
|
+
const id = workspaceId?.trim();
|
|
162
|
+
if (id)
|
|
163
|
+
query.set("workspaceId", id);
|
|
164
|
+
else
|
|
165
|
+
query.delete("workspaceId");
|
|
166
|
+
const qs = query.toString();
|
|
167
|
+
return `/#${path}${qs ? `?${qs}` : ""}`;
|
|
168
|
+
}
|
|
169
|
+
export function buildCanonicalHref(input) {
|
|
170
|
+
if (input.workspace === "chat")
|
|
171
|
+
return buildChatHref(input);
|
|
172
|
+
if (input.workspace === "operate") {
|
|
173
|
+
return buildOperateHref(input.operateTab ?? DEFAULT_OPERATE_TAB, input);
|
|
174
|
+
}
|
|
175
|
+
return buildInspectHref(input.inspectPath ?? "/", input.inspectQuery);
|
|
176
|
+
}
|
|
177
|
+
function finishInspect(inspectPath, query, replace) {
|
|
178
|
+
const qs = query.toString();
|
|
179
|
+
const canonicalHref = buildInspectHref(inspectPath, qs);
|
|
180
|
+
return {
|
|
181
|
+
workspace: "inspect",
|
|
182
|
+
operateTab: DEFAULT_OPERATE_TAB,
|
|
183
|
+
inspectPath: stripInspectPrefix(inspectPath),
|
|
184
|
+
canonicalHash: canonicalHref.slice(2),
|
|
185
|
+
canonicalHref,
|
|
186
|
+
replace,
|
|
187
|
+
...readWorkspaceParams(query),
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
function finishOperate(tab, query, replace) {
|
|
191
|
+
const canonicalHref = buildOperateHref(tab, readWorkspaceParams(query));
|
|
192
|
+
return {
|
|
193
|
+
workspace: "operate",
|
|
194
|
+
operateTab: tab,
|
|
195
|
+
inspectPath: "/",
|
|
196
|
+
canonicalHash: canonicalHref.slice(2),
|
|
197
|
+
canonicalHref,
|
|
198
|
+
replace,
|
|
199
|
+
...readWorkspaceParams(query),
|
|
200
|
+
};
|
|
201
|
+
}
|
|
202
|
+
function finishChat(replace, params) {
|
|
203
|
+
const effective = params ?? {};
|
|
204
|
+
const canonicalHref = buildChatHref(effective);
|
|
205
|
+
return {
|
|
206
|
+
workspace: "chat",
|
|
207
|
+
operateTab: DEFAULT_OPERATE_TAB,
|
|
208
|
+
inspectPath: "/",
|
|
209
|
+
canonicalHash: canonicalHref.slice(2),
|
|
210
|
+
canonicalHref,
|
|
211
|
+
replace,
|
|
212
|
+
...effective,
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
export function parseWorkspaceRoute(location, options = {}) {
|
|
216
|
+
const pathname = String(location.pathname ?? "/").replace(/\/+$/, "") || "/";
|
|
217
|
+
const lastTab = options.lastOperateTab ?? DEFAULT_OPERATE_TAB;
|
|
218
|
+
if (pathname === "/inspect") {
|
|
219
|
+
const { path, query } = splitHash(location.hash ?? "#/");
|
|
220
|
+
return finishInspect(path, query, true);
|
|
221
|
+
}
|
|
222
|
+
const { path, query } = splitHash(location.hash ?? "");
|
|
223
|
+
if (!location.hash || path === "/")
|
|
224
|
+
return null;
|
|
225
|
+
if (path === "/inspect" || path.startsWith("/inspect/")) {
|
|
226
|
+
return finishInspect(stripInspectPrefix(path), query, false);
|
|
227
|
+
}
|
|
228
|
+
if (path === "/chat")
|
|
229
|
+
return finishChat(false, readWorkspaceParams(query));
|
|
230
|
+
if (path === "/operate") {
|
|
231
|
+
return finishOperate(lastTab, query, true);
|
|
232
|
+
}
|
|
233
|
+
if (path.startsWith("/operate/")) {
|
|
234
|
+
const tab = path.slice("/operate/".length).split("/")[0] ?? "";
|
|
235
|
+
if (isOperateTab(tab))
|
|
236
|
+
return finishOperate(tab, query, false);
|
|
237
|
+
return finishOperate(lastTab, query, true);
|
|
238
|
+
}
|
|
239
|
+
if (isOperateTab(path.replace(/^\//, "").split("/")[0] ?? "")) {
|
|
240
|
+
const tab = path.replace(/^\//, "").split("/")[0];
|
|
241
|
+
return finishOperate(tab, query, true);
|
|
242
|
+
}
|
|
243
|
+
const inspectInternal = stripInspectPrefix(path).replace(/^\//, "");
|
|
244
|
+
if (INSPECT_COLLECTION_OR_OBJECT.test(inspectInternal)) {
|
|
245
|
+
return finishInspect(path, query, true);
|
|
246
|
+
}
|
|
247
|
+
return null;
|
|
248
|
+
}
|
|
249
|
+
export function rewriteInspectHrefToCanonical(href, base = typeof globalThis.location?.href === "string"
|
|
250
|
+
? globalThis.location.href
|
|
251
|
+
: "http://local.invalid/") {
|
|
252
|
+
const raw = String(href ?? "").trim();
|
|
253
|
+
if (!raw)
|
|
254
|
+
return DEFAULT_INSPECT_HREF;
|
|
255
|
+
if (raw.startsWith("/#/inspect"))
|
|
256
|
+
return raw;
|
|
257
|
+
if (raw.startsWith("#/inspect"))
|
|
258
|
+
return `/${raw}`;
|
|
259
|
+
if (raw.startsWith("/#/operate") || raw.startsWith("/#/chat"))
|
|
260
|
+
return raw;
|
|
261
|
+
if (raw.startsWith("#/operate") || raw.startsWith("#/chat"))
|
|
262
|
+
return `/${raw}`;
|
|
263
|
+
try {
|
|
264
|
+
const url = new URL(raw, base);
|
|
265
|
+
const pathname = url.pathname.replace(/\/+$/, "") || "/";
|
|
266
|
+
if (pathname === "/inspect") {
|
|
267
|
+
const { path, query } = splitHash(url.hash || "#/");
|
|
268
|
+
const merged = new URLSearchParams(url.search);
|
|
269
|
+
for (const [key, value] of query)
|
|
270
|
+
merged.set(key, value);
|
|
271
|
+
return buildInspectHref(path, merged);
|
|
272
|
+
}
|
|
273
|
+
const { path, query } = splitHash(url.hash || "");
|
|
274
|
+
if (path === "/inspect" || path.startsWith("/inspect/")) {
|
|
275
|
+
return buildInspectHref(stripInspectPrefix(path), query);
|
|
276
|
+
}
|
|
277
|
+
const internal = path.replace(/^\//, "");
|
|
278
|
+
if (INSPECT_COLLECTION_OR_OBJECT.test(internal)) {
|
|
279
|
+
return buildInspectHref(path, query);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
catch {
|
|
283
|
+
/* fall through */
|
|
284
|
+
}
|
|
285
|
+
if (raw.startsWith("#/") && INSPECT_COLLECTION_OR_OBJECT.test(raw.slice(2))) {
|
|
286
|
+
return buildInspectHref(raw.slice(1));
|
|
287
|
+
}
|
|
288
|
+
return DEFAULT_INSPECT_HREF;
|
|
289
|
+
}
|
|
290
|
+
export function isInspectDocumentHref(href, base = typeof globalThis.location?.href === "string"
|
|
291
|
+
? globalThis.location.href
|
|
292
|
+
: "http://local.invalid/") {
|
|
293
|
+
try {
|
|
294
|
+
const url = new URL(String(href ?? ""), base);
|
|
295
|
+
const pathname = url.pathname.replace(/\/+$/, "") || "/";
|
|
296
|
+
if (pathname !== "/inspect")
|
|
297
|
+
return false;
|
|
298
|
+
return !/\.[a-z0-9]+$/i.test(url.pathname);
|
|
299
|
+
}
|
|
300
|
+
catch {
|
|
301
|
+
return false;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
export function hrefToLocationHash(href) {
|
|
305
|
+
const canonical = href.startsWith("/#")
|
|
306
|
+
? href
|
|
307
|
+
: href.startsWith("#")
|
|
308
|
+
? `/${href}`
|
|
309
|
+
: rewriteInspectHrefToCanonical(href);
|
|
310
|
+
return canonical.startsWith("/#") ? canonical.slice(1) : canonical;
|
|
311
|
+
}
|
|
312
|
+
export function applyCanonicalHref(href) {
|
|
313
|
+
const hash = hrefToLocationHash(href);
|
|
314
|
+
if (typeof window === "undefined")
|
|
315
|
+
return hash;
|
|
316
|
+
const current = window.location.hash || "";
|
|
317
|
+
const normalizedCurrent = current.startsWith("#") ? current : `#${current}`;
|
|
318
|
+
const normalizedNext = hash.startsWith("#") ? hash : `#${hash}`;
|
|
319
|
+
if (normalizedCurrent !== normalizedNext) {
|
|
320
|
+
window.location.hash = normalizedNext;
|
|
321
|
+
}
|
|
322
|
+
return normalizedNext;
|
|
323
|
+
}
|
|
324
|
+
export function shouldCanonicalizeInspectDocument(location) {
|
|
325
|
+
const pathname = String(location.pathname ?? "/").replace(/\/+$/, "") || "/";
|
|
326
|
+
return pathname === "/inspect";
|
|
327
|
+
}
|
|
328
|
+
export function canonicalConsoleInspectHref(location) {
|
|
329
|
+
const { path, query } = splitHash(location.hash ?? "#/");
|
|
330
|
+
return buildInspectHref(path, query);
|
|
331
|
+
}
|
|
332
|
+
export function inspectInternalHash(hashValue) {
|
|
333
|
+
const { path, query } = splitHash(hashValue);
|
|
334
|
+
const internal = stripInspectPrefix(path);
|
|
335
|
+
const qs = query.toString();
|
|
336
|
+
return `${internal}${qs ? `?${qs}` : ""}`;
|
|
337
|
+
}
|
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Repo-scoped Workspace Context (2026-08-22 multi-workspace foundation, W1).
|
|
3
|
+
*
|
|
4
|
+
* One lazily-created ConsoleContext per workspaceId. Each context INDEPENDENTLY
|
|
5
|
+
* owns the full operator runtime for its repo root: fingerprint-isolated
|
|
6
|
+
* appData (via openConsoleAppData), operation/event stores, draft /
|
|
7
|
+
* confirmation / receipt / interview / assessment stores, Chat session store +
|
|
8
|
+
* events + deferred Turn registry + ConsolePiRuntime(cwd=repoRoot),
|
|
9
|
+
* OperatorActionContext, Observe route context, and readiness.
|
|
10
|
+
*
|
|
11
|
+
* Invariants:
|
|
12
|
+
* - Selecting (viewing) another workspace NEVER disposes a context; running
|
|
13
|
+
* Turns / Operations / DAGs continue in the background.
|
|
14
|
+
* - dispose() is invoked exactly once per context by the owning server's
|
|
15
|
+
* close().
|
|
16
|
+
* - bootToken is shared per Console boot: it binds the startup fingerprint
|
|
17
|
+
* and bind host, and its confirmationToken signs human-gate tokens for all
|
|
18
|
+
* workspaces in this process (single Console security domain).
|
|
19
|
+
*/
|
|
20
|
+
import { randomBytes } from "node:crypto";
|
|
21
|
+
import { existsSync } from "node:fs";
|
|
22
|
+
import { rm } from "node:fs/promises";
|
|
23
|
+
import path from "node:path";
|
|
24
|
+
import { LoopAgentClient } from "../loop-agent/loop-agent-client.js";
|
|
25
|
+
import { openConsoleAppData, sha256Json, writeSecureJson, } from "./app-data.js";
|
|
26
|
+
import { DagConfirmationStore } from "./dag-confirmation.js";
|
|
27
|
+
import { DagExecutionReceiptStore } from "./dag-execution-receipt.js";
|
|
28
|
+
import { DraftStore } from "./draft-store.js";
|
|
29
|
+
import { AssessmentStore } from "./interview/assessment.js";
|
|
30
|
+
import { InterviewSessionStore } from "./interview/session.js";
|
|
31
|
+
import { ChatSessionStore } from "./chat/session-store.js";
|
|
32
|
+
import { createChatEventStore, createChatOperationLinker, } from "./chat/chat-event-store.js";
|
|
33
|
+
import { createChatTurnExecutionRegistry } from "./chat/turn-execution-registry.js";
|
|
34
|
+
import { ConsolePiRuntime } from "./chat/pi-runtime.js";
|
|
35
|
+
import { UserQuestionRegistry } from "./chat/user-questions.js";
|
|
36
|
+
import { TodoListRegistry } from "./chat/todos.js";
|
|
37
|
+
import { createOperationEventStore, } from "./operation-sse.js";
|
|
38
|
+
import { OperationStore } from "./operation-store.js";
|
|
39
|
+
import { createFrontendSplitOperationAdapter, } from "./frontend-split-operation-adapter.js";
|
|
40
|
+
import { createFrontendHumanDecisionAdapter } from "./frontend-human-decision-adapter.js";
|
|
41
|
+
import { FrontendSplitTaskMaterializer, } from "../materialize/frontend-split-task-materializer.js";
|
|
42
|
+
import { probePiReadiness, } from "./pi-readiness.js";
|
|
43
|
+
import { resolveSiblingLoopAgentBin } from "./sibling-controller.js";
|
|
44
|
+
import { NightAuxTicker } from "./night-aux-ticker.js";
|
|
45
|
+
import { createObserveRouteContext, } from "../observe/routes.js";
|
|
46
|
+
import { WorkspaceRegistry } from "./workspace-registry.js";
|
|
47
|
+
import { resolveConsoleDefaultTimeoutMs } from "./server.js";
|
|
48
|
+
/**
|
|
49
|
+
* Lazily create the full repo-scoped Console context for a registered
|
|
50
|
+
* workspace record. Pure factory — no global registry; the caller owns
|
|
51
|
+
* lifecycle (single creation per workspaceId, dispose on server close).
|
|
52
|
+
*/
|
|
53
|
+
export async function createWorkspaceContext(record, deps) {
|
|
54
|
+
const repoRoot = record.repoRoot;
|
|
55
|
+
const appData = openConsoleAppData({
|
|
56
|
+
repoRoot,
|
|
57
|
+
...(deps.appDataRoot ? { appDataRoot: deps.appDataRoot } : {}),
|
|
58
|
+
});
|
|
59
|
+
const registry = new WorkspaceRegistry(appData);
|
|
60
|
+
const readiness = await probePiReadiness({
|
|
61
|
+
override: deps.piReadinessProbe,
|
|
62
|
+
repoRoot,
|
|
63
|
+
});
|
|
64
|
+
let readinessCache = readiness;
|
|
65
|
+
const artifactRoot = path.join(appData.root, "client-artifacts", appData.fingerprint);
|
|
66
|
+
const client = deps.createClient?.(artifactRoot) ??
|
|
67
|
+
new LoopAgentClient({
|
|
68
|
+
loopAgentBin: resolveSiblingLoopAgentBin(),
|
|
69
|
+
artifactRoot,
|
|
70
|
+
resolveIdentity: true,
|
|
71
|
+
defaultTimeoutMs: resolveConsoleDefaultTimeoutMs(deps.defaultTimeoutMs),
|
|
72
|
+
});
|
|
73
|
+
const operations = new OperationStore(appData);
|
|
74
|
+
const events = createOperationEventStore({
|
|
75
|
+
persistenceDir: path.join(appData.operations, "events"),
|
|
76
|
+
});
|
|
77
|
+
// Frontend split runtime is repo-scoped: materialization, durable schedule
|
|
78
|
+
// intents, and aggregate closeout must use this workspace's app-data and
|
|
79
|
+
// OperationStore rather than the process startup workspace.
|
|
80
|
+
const splitScheduleDir = path.join(appData.root, "frontend-split-schedule");
|
|
81
|
+
const splitAuditDir = path.join(appData.root, "frontend-split-audit");
|
|
82
|
+
const splitAggregateDir = path.join(appData.root, "frontend-split-aggregate");
|
|
83
|
+
const splitMaterializationBackend = {
|
|
84
|
+
async ensureTask({ taskId }) {
|
|
85
|
+
return { taskId, complete: true };
|
|
86
|
+
},
|
|
87
|
+
async ensureOperation({ taskId, operationId, payloadDigest, request }) {
|
|
88
|
+
const action = request.kind === "child"
|
|
89
|
+
? "frontendSplitChild"
|
|
90
|
+
: "frontendSplitIntegration";
|
|
91
|
+
const { operation } = await operations.ensureSplitMaterializedOperation({
|
|
92
|
+
operationId,
|
|
93
|
+
taskId,
|
|
94
|
+
action,
|
|
95
|
+
actionParams: request,
|
|
96
|
+
payloadHash: payloadDigest,
|
|
97
|
+
});
|
|
98
|
+
return {
|
|
99
|
+
taskId: operation.taskId ?? taskId,
|
|
100
|
+
operationId: operation.operationId,
|
|
101
|
+
complete: true,
|
|
102
|
+
};
|
|
103
|
+
},
|
|
104
|
+
};
|
|
105
|
+
const materializer = new FrontendSplitTaskMaterializer(splitMaterializationBackend);
|
|
106
|
+
const schedule = async (input) => {
|
|
107
|
+
const fact = {
|
|
108
|
+
schemaVersion: 1,
|
|
109
|
+
kind: "frontend-split-schedule-intent",
|
|
110
|
+
operationId: input.operationId,
|
|
111
|
+
generation: input.generation,
|
|
112
|
+
bindingDigest: input.bindingDigest,
|
|
113
|
+
recordedAt: new Date().toISOString(),
|
|
114
|
+
};
|
|
115
|
+
const name = `${sha256Json({
|
|
116
|
+
operationId: input.operationId,
|
|
117
|
+
generation: input.generation,
|
|
118
|
+
bindingDigest: input.bindingDigest,
|
|
119
|
+
})}.json`;
|
|
120
|
+
await writeSecureJson(path.join(splitScheduleDir, name), fact);
|
|
121
|
+
};
|
|
122
|
+
const auditStale = async (input) => {
|
|
123
|
+
const fact = {
|
|
124
|
+
schemaVersion: 1,
|
|
125
|
+
kind: "frontend-split-stale-audit",
|
|
126
|
+
operationId: input.operationId,
|
|
127
|
+
reason: input.reason,
|
|
128
|
+
fact: input.fact,
|
|
129
|
+
recordedAt: new Date().toISOString(),
|
|
130
|
+
};
|
|
131
|
+
await writeSecureJson(path.join(splitAuditDir, `${sha256Json(fact)}.json`), fact);
|
|
132
|
+
};
|
|
133
|
+
const prepareAggregate = async ({ operationId, aggregate, }) => {
|
|
134
|
+
const before = await operations.get(operationId);
|
|
135
|
+
if (!before) {
|
|
136
|
+
throw new Error(`cannot prepare aggregate: parent operation not found: ${operationId}`);
|
|
137
|
+
}
|
|
138
|
+
const aggregateFile = path.join(splitAggregateDir, `${aggregate.digest}.json`);
|
|
139
|
+
let parentRecordPath = "";
|
|
140
|
+
let wroteAggregate = false;
|
|
141
|
+
let wroteParent = false;
|
|
142
|
+
return {
|
|
143
|
+
committedRef: {
|
|
144
|
+
ref: aggregateFile,
|
|
145
|
+
digest: aggregate.digest,
|
|
146
|
+
commitState: "committed",
|
|
147
|
+
},
|
|
148
|
+
async commit(input) {
|
|
149
|
+
parentRecordPath = input.parentRecordPath;
|
|
150
|
+
await writeSecureJson(aggregateFile, {
|
|
151
|
+
schemaVersion: 1,
|
|
152
|
+
kind: "frontend-split-aggregate-closeout",
|
|
153
|
+
operationId,
|
|
154
|
+
aggregate,
|
|
155
|
+
});
|
|
156
|
+
wroteAggregate = true;
|
|
157
|
+
await writeSecureJson(input.parentRecordPath, input.parentRecord);
|
|
158
|
+
wroteParent = true;
|
|
159
|
+
},
|
|
160
|
+
async rollback() {
|
|
161
|
+
if (wroteAggregate)
|
|
162
|
+
await rm(aggregateFile, { force: true });
|
|
163
|
+
if (wroteParent && parentRecordPath) {
|
|
164
|
+
await writeSecureJson(parentRecordPath, before);
|
|
165
|
+
}
|
|
166
|
+
},
|
|
167
|
+
};
|
|
168
|
+
};
|
|
169
|
+
const splitOrchestrator = createFrontendSplitOperationAdapter({
|
|
170
|
+
store: operations,
|
|
171
|
+
materializer,
|
|
172
|
+
schedule,
|
|
173
|
+
auditStale,
|
|
174
|
+
prepareAggregate,
|
|
175
|
+
});
|
|
176
|
+
const humanDecisionAdapter = createFrontendHumanDecisionAdapter(operations);
|
|
177
|
+
const drafts = new DraftStore(appData);
|
|
178
|
+
const confirmations = new DagConfirmationStore(appData, deps.bootToken.confirmationToken);
|
|
179
|
+
const dagExecutionReceipts = new DagExecutionReceiptStore(appData);
|
|
180
|
+
const interviews = new InterviewSessionStore(appData);
|
|
181
|
+
const assessments = new AssessmentStore(appData);
|
|
182
|
+
if (!deps.skipReconcile) {
|
|
183
|
+
await operations.reconcileOnBoot();
|
|
184
|
+
}
|
|
185
|
+
const getReadiness = () => readinessCache;
|
|
186
|
+
const refreshReadiness = async () => {
|
|
187
|
+
readinessCache = await probePiReadiness({
|
|
188
|
+
override: deps.piReadinessProbe,
|
|
189
|
+
repoRoot,
|
|
190
|
+
});
|
|
191
|
+
actionContext.readiness = readinessCache;
|
|
192
|
+
return readinessCache;
|
|
193
|
+
};
|
|
194
|
+
const actionContext = {
|
|
195
|
+
repoRoot,
|
|
196
|
+
appData,
|
|
197
|
+
operatorSessionId: deps.operatorSessionId,
|
|
198
|
+
client,
|
|
199
|
+
operations,
|
|
200
|
+
events,
|
|
201
|
+
splitOrchestrator,
|
|
202
|
+
humanDecisionAdapter,
|
|
203
|
+
materializer,
|
|
204
|
+
drafts,
|
|
205
|
+
confirmations,
|
|
206
|
+
dagExecutionReceipts,
|
|
207
|
+
humanGateSecret: deps.bootToken.confirmationToken,
|
|
208
|
+
interviews,
|
|
209
|
+
assessments,
|
|
210
|
+
readiness: readinessCache,
|
|
211
|
+
getReadiness,
|
|
212
|
+
refreshReadiness,
|
|
213
|
+
observeBaseUrl: deps.observeBaseUrl,
|
|
214
|
+
fetchImpl: deps.fetchImpl,
|
|
215
|
+
getConsoleOrigin: deps.getConsoleOrigin,
|
|
216
|
+
};
|
|
217
|
+
const chatStore = new ChatSessionStore(appData);
|
|
218
|
+
const chatEventEpoch = randomBytes(12).toString("hex");
|
|
219
|
+
const chatEvents = createChatEventStore({
|
|
220
|
+
appData,
|
|
221
|
+
epoch: chatEventEpoch,
|
|
222
|
+
});
|
|
223
|
+
const chatTurnExecutions = createChatTurnExecutionRegistry();
|
|
224
|
+
const questionRegistry = new UserQuestionRegistry(path.join(appData.chats, "user-questions"));
|
|
225
|
+
const todoRegistry = new TodoListRegistry();
|
|
226
|
+
questionRegistry.subscribe((change) => {
|
|
227
|
+
const question = change.question;
|
|
228
|
+
chatEvents.append(question.sessionId, question.turnId ?? `${question.sessionId}:question`, {
|
|
229
|
+
kind: `question_${change.type}`,
|
|
230
|
+
data: { question },
|
|
231
|
+
});
|
|
232
|
+
});
|
|
233
|
+
todoRegistry.subscribe((change) => {
|
|
234
|
+
chatEvents.append(change.sessionId, change.turnId ?? `${change.sessionId}:todo`, {
|
|
235
|
+
kind: change.type === "updated" ? "todo_updated" : "todo_cleared",
|
|
236
|
+
data: {
|
|
237
|
+
todos: change.todos,
|
|
238
|
+
...(change.toolCallId ? { toolCallId: change.toolCallId } : {}),
|
|
239
|
+
},
|
|
240
|
+
});
|
|
241
|
+
});
|
|
242
|
+
const chatOperationLinker = createChatOperationLinker({
|
|
243
|
+
chatEvents,
|
|
244
|
+
operations,
|
|
245
|
+
operationEvents: events,
|
|
246
|
+
});
|
|
247
|
+
const chatRuntime = new ConsolePiRuntime({
|
|
248
|
+
cwd: repoRoot,
|
|
249
|
+
sessionDir: appData.chatSessions,
|
|
250
|
+
resourcePreferencesPath: appData.resourcePreferences,
|
|
251
|
+
actionContext,
|
|
252
|
+
questionRegistry,
|
|
253
|
+
todoRegistry,
|
|
254
|
+
});
|
|
255
|
+
const chatRouteDeps = {
|
|
256
|
+
runtime: chatRuntime,
|
|
257
|
+
store: chatStore,
|
|
258
|
+
events: chatEvents,
|
|
259
|
+
turnExecutions: chatTurnExecutions,
|
|
260
|
+
questionRegistry,
|
|
261
|
+
todoRegistry,
|
|
262
|
+
operationLinker: chatOperationLinker,
|
|
263
|
+
appData,
|
|
264
|
+
bootToken: deps.bootToken,
|
|
265
|
+
getConsoleOrigin: deps.getConsoleOrigin,
|
|
266
|
+
allowNonLoopbackAccess: deps.allowNonLoopbackAccess,
|
|
267
|
+
};
|
|
268
|
+
const { recoverDeferredTurns } = await import("./chat/routes.js");
|
|
269
|
+
const nightAuxTicker = new NightAuxTicker({ repoRoot, client });
|
|
270
|
+
const observeRouteContext = createObserveRouteContext(repoRoot, {
|
|
271
|
+
staticDir: deps.inspectStaticDir,
|
|
272
|
+
});
|
|
273
|
+
let disposed = false;
|
|
274
|
+
const context = {
|
|
275
|
+
workspaceId: record.workspaceId,
|
|
276
|
+
record,
|
|
277
|
+
repoRoot,
|
|
278
|
+
appData,
|
|
279
|
+
registry,
|
|
280
|
+
client,
|
|
281
|
+
operations,
|
|
282
|
+
events,
|
|
283
|
+
splitOrchestrator,
|
|
284
|
+
humanDecisionAdapter,
|
|
285
|
+
materializer,
|
|
286
|
+
drafts,
|
|
287
|
+
confirmations,
|
|
288
|
+
dagExecutionReceipts,
|
|
289
|
+
interviews,
|
|
290
|
+
assessments,
|
|
291
|
+
chatRouteDeps,
|
|
292
|
+
chatOperationLinker,
|
|
293
|
+
chatRuntime,
|
|
294
|
+
nightAuxTicker,
|
|
295
|
+
actionContext,
|
|
296
|
+
observeRouteContext,
|
|
297
|
+
getReadiness,
|
|
298
|
+
refreshReadiness,
|
|
299
|
+
recoverDeferredTurns: () => recoverDeferredTurns(chatRouteDeps),
|
|
300
|
+
dispose: () => {
|
|
301
|
+
if (disposed)
|
|
302
|
+
return;
|
|
303
|
+
disposed = true;
|
|
304
|
+
try {
|
|
305
|
+
chatOperationLinker.dispose();
|
|
306
|
+
}
|
|
307
|
+
catch {
|
|
308
|
+
// best-effort
|
|
309
|
+
}
|
|
310
|
+
try {
|
|
311
|
+
chatRuntime.disposeAll();
|
|
312
|
+
}
|
|
313
|
+
catch {
|
|
314
|
+
// best-effort
|
|
315
|
+
}
|
|
316
|
+
try {
|
|
317
|
+
nightAuxTicker.dispose();
|
|
318
|
+
}
|
|
319
|
+
catch {
|
|
320
|
+
// best-effort
|
|
321
|
+
}
|
|
322
|
+
},
|
|
323
|
+
};
|
|
324
|
+
return context;
|
|
325
|
+
}
|
|
326
|
+
/** Inspect-facing readiness/capability summary for a registry record. */
|
|
327
|
+
export async function summarizeWorkspaceRecord(record, deps) {
|
|
328
|
+
let state;
|
|
329
|
+
try {
|
|
330
|
+
const report = await probePiReadiness({
|
|
331
|
+
override: deps.piReadinessProbe,
|
|
332
|
+
repoRoot: record.repoRoot,
|
|
333
|
+
});
|
|
334
|
+
state = report.state;
|
|
335
|
+
}
|
|
336
|
+
catch {
|
|
337
|
+
state = "setup-required";
|
|
338
|
+
}
|
|
339
|
+
return {
|
|
340
|
+
state,
|
|
341
|
+
summary: `pi=${state}; inspect=${deps.inspectReady ? "ready" : "unavailable"}`,
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
void existsSync;
|