@tea-agent/loop-agent 0.39.0-beta.1 → 0.39.0-beta.11
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 +349 -97
- 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 +2370 -64
- package/dist/executors/pi-executor.js +11 -2
- 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 +225 -33
- package/dist/executors/pi-writer-tool-policy.js +57 -1
- package/dist/executors/shell-executor.js +1258 -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 +75 -5
- 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 +331 -18
- package/dist/worker/console/chat/provider-error.js +98 -0
- package/dist/worker/console/chat/routes.js +395 -59
- 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 +79 -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-CXj_GnSb.js} +1 -1
- package/dist/worker/console/static/assets/{arc-Bj2M8iO5.js → arc-BZp6JAp7.js} +1 -1
- package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-CLlXe4-6.js → architectureDiagram-T3A2C74G-DfpcEuYU.js} +1 -1
- package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-psEp5xH0.js → blockDiagram-VBNYF7ZC-_Gf0xadb.js} +1 -1
- package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-DeKcOCGJ.js → c4Diagram-5PPSVZJV-Ct2QPCmv.js} +1 -1
- package/dist/worker/console/static/assets/channel-3TxJgYaH.js +1 -0
- package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-K0C744p1.js → chunk-2GRJ4B5K-BfklkzKl.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-xbNw4J9-.js → chunk-2Q5K7J3B-Dy25vZJV.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5RXB4S5H-DjLaSDUO.js → chunk-5RXB4S5H-BFlCRZep.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5VM5RSS4-CtiPlKel.js → chunk-5VM5RSS4-op3oVxIE.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-DZtolir7.js → chunk-6Q2QTUOP-C0R7rzF2.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-GF5L2VYU-CPcdNeew.js → chunk-GF5L2VYU-Bt44TCGy.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-JWPE2WC7-NgHc6V37.js → chunk-JWPE2WC7-_uEE_XFx.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-KBJHAD2P-DQ_T-hg8.js → chunk-KBJHAD2P-C3TOYGZ9.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-RYQCIY6F-DgLsxcVP.js → chunk-RYQCIY6F-Cz60oBPV.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-XXDRQBXY-UrVoM9z1.js → chunk-XXDRQBXY-8Bik0qis.js} +1 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-BHIkXpp3.js +1 -0
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-BHIkXpp3.js +1 -0
- package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-CLkYvTb3.js → cose-bilkent-JH36ORCC-C2QIOA4a.js} +1 -1
- package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-BwT_xKrE.js → cynefin-VYW2F7L2-CSH_yUUd.js} +1 -1
- package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-Bi9tuzif.js → cynefinDiagram-MW4NZA55-BDLw2XFM.js} +1 -1
- package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-BCIqkWBV.js → dagre-VZM6K2ZE-CcD9ZtF4.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-7IWD3JNH-Dip6l9_Z.js → diagram-7IWD3JNH-DqwtkBsI.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-B-xkh_wK.js → diagram-B4RE2ZJO-BWVEcqsC.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-LBJQPF4R-DZO0kTt_.js → diagram-LBJQPF4R-M-WAbtQi.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-Q27KOJAE-D6ZplNbZ.js → diagram-Q27KOJAE-DKW7SixP.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-UB23O5K3-BPekbhoS.js → diagram-UB23O5K3-PHHPPtrj.js} +1 -1
- package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-BtmaJpK5.js → ebnfDiagram-BXEA7PRR-BQD-B4RX.js} +1 -1
- package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-6DS0Xc44.js → erDiagram-JOGREHBK-BFvULb51.js} +1 -1
- package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-CihnROSm.js → flowDiagram-UKHOOZJN-Bw-aXTCb.js} +1 -1
- package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-CZC4zOE2.js → ganttDiagram-PKOTCBZU-BGKw04Qx.js} +1 -1
- package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-Dq1fhzGN.js → gitGraphDiagram-DS77QQ5N-RqKaPR-I.js} +1 -1
- package/dist/worker/console/static/assets/index-B_D8rbWc.js +407 -0
- package/dist/worker/console/static/assets/index-BdNx6fj0.css +1 -0
- package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-DlSl4BGx.js → infoDiagram-6WML65LV-YO5dnzrJ.js} +1 -1
- package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-snnzFl_U.js → ishikawaDiagram-WSZJBQD7-Bi1VZHMq.js} +1 -1
- package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-BDE7qpMx.js → journeyDiagram-NVQOT4AX-Bszls1DD.js} +1 -1
- package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-CD2Ci04G.js → kanban-definition-27J2QSJJ-PhzeaZ09.js} +1 -1
- package/dist/worker/console/static/assets/{linear-DdnapKIH.js → linear-BsjbDoXi.js} +1 -1
- package/dist/worker/console/static/assets/{mermaid.core-BVjAT9b8.js → mermaid.core-0B7NnWKk.js} +5 -5
- package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-D0yJk-zA.js → mindmap-definition-FAOFIHXS-BJr4Fj-q.js} +1 -1
- package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-BVo9tFP-.js → pegDiagram-VL7TDLO6-moC4fpGB.js} +1 -1
- package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-DnOV-mZ_.js → pieDiagram-7S7Q4E2Y-BEw37-2c.js} +1 -1
- package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-CzUJo58i.js → quadrantDiagram-CIZ2JOQS-Cq6LyasU.js} +1 -1
- package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-Bvikrolh.js → railroadDiagram-AXF67PYL-DUCMcK0D.js} +1 -1
- package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-DtaSVCap.js → requirementDiagram-LRYGKXZP-C3upTZm7.js} +1 -1
- package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-C2c9A0wW.js → sankeyDiagram-W5VNT64P-BI_gMsCW.js} +1 -1
- package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-DmXJcU7r.js → sequenceDiagram-SI44F4Z6-YFOIRzfN.js} +1 -1
- package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-B4GUFW92.js → sizeCapture-X5ZJPWSS-dOnB7UDD.js} +1 -1
- package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-Dsad2MXf.js → stateDiagram-OKZ733FA-BXUniaIh.js} +1 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-Cdi6UhLa.js +1 -0
- package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-DGq48Fbi.js → swimlanes-SLNWSIFB-2tA4wTNu.js} +2 -2
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-D-RJBbb0.js +8 -0
- package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-CSSi6OFf.js → timeline-definition-Z64GVDOM-DO0HkJXC.js} +1 -1
- package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-BqyzPrWv.js → vennDiagram-T6HMQDX7-DvIOzixv.js} +1 -1
- package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-DlznVb6S.js → wardleyDiagram-T6FBY63Y-DXDZ0cTj.js} +1 -1
- package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-CfBcgJ_K.js → xychartDiagram-ELKLHX3M-B32Ark0D.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 +141 -8
- package/dist/worker/console/static-src/operator-chat/compaction-message.js +79 -0
- 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/spatial-overlay.js +1 -2
- package/dist/worker/console/static-src/operator-chat/turn-stream-controller.js +25 -0
- package/dist/worker/console/static-src/operator-chat/turn-submission.js +16 -3
- package/dist/worker/console/static-src/operator-chat/useChatSessions.js +181 -87
- package/dist/worker/console/static-src/operator-chat/useChatStream.js +183 -48
- package/dist/worker/console/static-src/operator-chat/useChatThread.js +177 -8
- 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 +339 -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 +17 -17
- 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 +2 -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 +538 -166
- package/dist/workflows/dag/frontend-plan-render.js +24 -0
- package/dist/workflows/dag/frontend-prewrite-gate.js +388 -392
- 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 +1072 -660
- package/dist/workflows/dag/interrupt-request.js +10 -3
- package/dist/workflows/dag/lifecycle.js +3 -3
- package/dist/workflows/dag/node-execution.js +552 -58
- 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 +338 -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 +63 -1
- package/docs/templates/backend-test-dag.json +265 -237
- 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 +1 -1
- 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
- package/dist/worker/console/static-src/operator-chat/activity-rail-presentation.js +0 -73
- package/dist/worker/console/static-src/operator-chat/useActivityRailTransition.js +0 -59
|
@@ -2,7 +2,7 @@ import { useCallback, useEffect } from "react";
|
|
|
2
2
|
import { formatOperatorUserError } from "../../operator-user-error.js";
|
|
3
3
|
import { confirmationToken, requestId, sleep } from "./format.js";
|
|
4
4
|
import { classifyCreateTurnNetworkFailure, classifyCreateTurnResponse, createPendingSubmission, decideSubmissionRoute, isRuntimeBusyUnion, isSubmissionCurrent, isTerminalTurnState, keepAmbiguousAfterRetry, reconcileDeferredSubmissionFromState, reconcilePendingSubmissionFromState, resolveAmbiguousFromState, } from "./turn-submission.js";
|
|
5
|
-
import { applyAbortDecision, applyPendingReconcileDecision, classifyAbortResponse, markStopRequested, operatorChatAbortUrl, operatorChatEventsUrl, operatorChatStateUrl, operatorChatTurnsUrl, releaseOwnershipOnTerminalEvidence, releaseSubmitOwnership as releaseOwnershipBag, requestStopOnOwnership, settleCreateTurnOutcome, turnPhaseFromOwnership, } from "./turn-stream-controller.js";
|
|
5
|
+
import { applyAbortDecision, applyPendingReconcileDecision, classifyAbortResponse, markStopRequested, operatorChatAbortUrl, operatorChatEventsUrl, operatorChatStateUrl, operatorChatTurnsUrl, releaseOwnershipOnTerminalEvidence, releaseSubmitOwnership as releaseOwnershipBag, requestStopOnOwnership, settleCreateTurnOutcome, turnPhaseFromOwnership, visualTurnPhaseFromOwnership, } from "./turn-stream-controller.js";
|
|
6
6
|
/** Project ChatRefs into the executable ownership bag used by the controller. */
|
|
7
7
|
function ownershipBagFromRefs(refs) {
|
|
8
8
|
return {
|
|
@@ -46,6 +46,7 @@ function sseEventName(block) {
|
|
|
46
46
|
function sleepBriefly() {
|
|
47
47
|
return new Promise((resolve) => window.setTimeout(resolve, 25));
|
|
48
48
|
}
|
|
49
|
+
const STOP_RECONCILE_DELAYS_MS = [100, 250, 500, 1_000, 2_000];
|
|
49
50
|
function clearEmptyAssistant(setMessages, assistantId) {
|
|
50
51
|
if (!assistantId)
|
|
51
52
|
return;
|
|
@@ -57,8 +58,8 @@ function clearEmptyAssistant(setMessages, assistantId) {
|
|
|
57
58
|
* detached Turn resource only; it never opens a second inline SSE consumer. */
|
|
58
59
|
export function useChatStream(params) {
|
|
59
60
|
const { origin, refs, session, thread, composer, setError, setAutoScroll, setNotice, recoveryVersion = 0, watchSessionTitle, } = params;
|
|
60
|
-
const { applySseBlock, setMessages, setStreaming, setStreamingAssistantId, streaming, setTurnPhase, setDeferredTurn, } = thread;
|
|
61
|
-
const { setInput, setPendingImages } = composer;
|
|
61
|
+
const { applySseBlock, setMessages, setStreaming, setStreamingAssistantId, streaming, setTurnPhase, setDeferredTurn, setDeferredTurns, } = thread;
|
|
62
|
+
const { setInput, setPendingImages, recordHistory } = composer;
|
|
62
63
|
const showNotice = useCallback((message) => {
|
|
63
64
|
if (!setNotice)
|
|
64
65
|
return;
|
|
@@ -73,15 +74,26 @@ export function useChatStream(params) {
|
|
|
73
74
|
if (refs.sessionRef.current?.sessionId !== sessionId ||
|
|
74
75
|
refs.sessionGenerationRef.current !== generation)
|
|
75
76
|
return;
|
|
77
|
+
const pendingClientRequestId = refs.pendingSubmissionRef.current?.clientRequestId;
|
|
78
|
+
if (pendingClientRequestId)
|
|
79
|
+
releaseSubmitOwnership(pendingClientRequestId);
|
|
76
80
|
refs.streamingRef.current = false;
|
|
77
81
|
refs.activeTurnIdRef.current = null;
|
|
78
82
|
refs.assistantIdRef.current = null;
|
|
79
83
|
refs.stoppingTurnIdRef.current = null;
|
|
84
|
+
refs.stopRequestedRef.current = false;
|
|
80
85
|
setStreaming(false);
|
|
81
86
|
setTurnPhase("idle");
|
|
82
87
|
setStreamingAssistantId(null);
|
|
83
88
|
clearEmptyAssistant(setMessages, assistantId);
|
|
84
|
-
}, [
|
|
89
|
+
}, [
|
|
90
|
+
refs,
|
|
91
|
+
releaseSubmitOwnership,
|
|
92
|
+
setStreaming,
|
|
93
|
+
setTurnPhase,
|
|
94
|
+
setStreamingAssistantId,
|
|
95
|
+
setMessages,
|
|
96
|
+
]);
|
|
85
97
|
/** Follow one session's event stream until it is explicitly aborted. The
|
|
86
98
|
* server's `stream_ready` marker separates replay from live events; this
|
|
87
99
|
* means replay cannot render duplicate messages while live text remains
|
|
@@ -243,12 +255,12 @@ export function useChatStream(params) {
|
|
|
243
255
|
/** Issue real POST /abort for a known turnId (Stop never disposes Session). */
|
|
244
256
|
const postAbortForTurn = useCallback(async (sessionId, generation, turnId) => {
|
|
245
257
|
refs.stoppingTurnIdRef.current = turnId;
|
|
246
|
-
//
|
|
247
|
-
//
|
|
248
|
-
//
|
|
258
|
+
// DSH-style visual stop: the composer becomes idle immediately. Hidden
|
|
259
|
+
// ownership remains stopping so a new message routes to the durable queue
|
|
260
|
+
// until terminal evidence proves the old Turn is gone.
|
|
249
261
|
refs.streamingRef.current = false;
|
|
250
262
|
setStreaming(false);
|
|
251
|
-
setTurnPhase("
|
|
263
|
+
setTurnPhase("idle");
|
|
252
264
|
setStreamingAssistantId(null);
|
|
253
265
|
try {
|
|
254
266
|
const res = await fetch(operatorChatAbortUrl(origin, sessionId, turnId), {
|
|
@@ -277,13 +289,17 @@ export function useChatStream(params) {
|
|
|
277
289
|
const state = await fetchSessionState(sessionId);
|
|
278
290
|
const stillCurrent = refs.sessionRef.current?.sessionId === sessionId &&
|
|
279
291
|
refs.sessionGenerationRef.current === generation;
|
|
292
|
+
// Terminal SSE may settle the Turn while /state is in flight. Do not
|
|
293
|
+
// write the pre-request ownership snapshot back after that newer fact.
|
|
294
|
+
if (!stillCurrent || refs.stoppingTurnIdRef.current !== turnId)
|
|
295
|
+
return;
|
|
280
296
|
applyAbortDecision(bag, decision, noticeSink(), {
|
|
281
|
-
sessionStillCurrent:
|
|
297
|
+
sessionStillCurrent: true,
|
|
282
298
|
runtimeBusy: isRuntimeBusyUnion(state ?? undefined),
|
|
283
299
|
markIdle: () => markIdleIfCurrent(sessionId, generation, refs.assistantIdRef.current),
|
|
284
300
|
});
|
|
285
301
|
applyOwnershipBagToRefs(refs, bag);
|
|
286
|
-
setTurnPhase(
|
|
302
|
+
setTurnPhase(visualTurnPhaseFromOwnership(bag));
|
|
287
303
|
return;
|
|
288
304
|
}
|
|
289
305
|
applyAbortDecision(bag, decision, noticeSink(), {
|
|
@@ -291,11 +307,7 @@ export function useChatStream(params) {
|
|
|
291
307
|
markIdle: () => markIdleIfCurrent(sessionId, generation, refs.assistantIdRef.current),
|
|
292
308
|
});
|
|
293
309
|
applyOwnershipBagToRefs(refs, bag);
|
|
294
|
-
setTurnPhase(
|
|
295
|
-
// Sync the post-decision busy state back to React: optimistic stop
|
|
296
|
-
// flipped it idle up front; pending/keep-busy outcomes restore it.
|
|
297
|
-
if (bag.streaming)
|
|
298
|
-
setStreaming(true);
|
|
310
|
+
setTurnPhase(visualTurnPhaseFromOwnership(bag));
|
|
299
311
|
}
|
|
300
312
|
catch {
|
|
301
313
|
// Abort network failure: keep busy (UIS-abort-network-fail).
|
|
@@ -308,11 +320,11 @@ export function useChatStream(params) {
|
|
|
308
320
|
networkError: true,
|
|
309
321
|
}), noticeSink());
|
|
310
322
|
applyOwnershipBagToRefs(refs, bag);
|
|
311
|
-
//
|
|
312
|
-
//
|
|
313
|
-
refs.streamingRef.current =
|
|
314
|
-
setStreaming(
|
|
315
|
-
setTurnPhase("
|
|
323
|
+
// Transport failure is ambiguous: keep the visual composer available,
|
|
324
|
+
// retain hidden ownership, and let the short /state backoff converge.
|
|
325
|
+
refs.streamingRef.current = false;
|
|
326
|
+
setStreaming(false);
|
|
327
|
+
setTurnPhase("idle");
|
|
316
328
|
}
|
|
317
329
|
}, [
|
|
318
330
|
origin,
|
|
@@ -368,7 +380,13 @@ export function useChatStream(params) {
|
|
|
368
380
|
},
|
|
369
381
|
});
|
|
370
382
|
applyOwnershipBagToRefs(refs, bag);
|
|
371
|
-
|
|
383
|
+
const ownershipPhase = turnPhaseFromOwnership(bag);
|
|
384
|
+
setTurnPhase(visualTurnPhaseFromOwnership(bag));
|
|
385
|
+
if (ownershipPhase === "stopping") {
|
|
386
|
+
refs.streamingRef.current = false;
|
|
387
|
+
setStreaming(false);
|
|
388
|
+
setStreamingAssistantId(null);
|
|
389
|
+
}
|
|
372
390
|
}, [
|
|
373
391
|
refs,
|
|
374
392
|
noticeSink,
|
|
@@ -386,17 +404,18 @@ export function useChatStream(params) {
|
|
|
386
404
|
const activeSession = refs.sessionRef.current;
|
|
387
405
|
if (!activeSession || (!input.text.trim() && input.images.length === 0))
|
|
388
406
|
return { action: "handled" };
|
|
389
|
-
const existing = thread.deferredTurn;
|
|
390
407
|
showNotice("正在排队…");
|
|
391
|
-
const acceptDeferred = (deferredTurn) => {
|
|
392
|
-
setDeferredTurn(deferredTurn);
|
|
408
|
+
const acceptDeferred = (deferredTurn, deferredTurns) => {
|
|
409
|
+
setDeferredTurn(deferredTurns?.[0] ?? deferredTurn);
|
|
410
|
+
setDeferredTurns(deferredTurns ?? [deferredTurn]);
|
|
393
411
|
setInput("");
|
|
394
412
|
setPendingImages([]);
|
|
395
413
|
setError(null);
|
|
396
|
-
showNotice(
|
|
414
|
+
showNotice("已排队,将在当前回合结束后自动发送");
|
|
397
415
|
};
|
|
398
416
|
const acceptDispatched = () => {
|
|
399
417
|
setDeferredTurn(null);
|
|
418
|
+
setDeferredTurns([]);
|
|
400
419
|
setInput("");
|
|
401
420
|
setPendingImages([]);
|
|
402
421
|
setError(null);
|
|
@@ -415,7 +434,6 @@ export function useChatStream(params) {
|
|
|
415
434
|
blockedByTurnId: input.blockedByTurnId,
|
|
416
435
|
text: input.text,
|
|
417
436
|
images: input.images,
|
|
418
|
-
...(existing ? { expectedRevision: existing.revision } : {}),
|
|
419
437
|
}),
|
|
420
438
|
});
|
|
421
439
|
const body = (await response.json().catch(() => ({})));
|
|
@@ -425,12 +443,14 @@ export function useChatStream(params) {
|
|
|
425
443
|
for (let attempt = 0; attempt < 2; attempt += 1) {
|
|
426
444
|
const { response, body } = await putOnce();
|
|
427
445
|
if (response.ok && body.accepted && body.deferredTurn) {
|
|
428
|
-
acceptDeferred(body.deferredTurn);
|
|
446
|
+
acceptDeferred(body.deferredTurn, body.deferredTurns);
|
|
429
447
|
return { action: "handled" };
|
|
430
448
|
}
|
|
449
|
+
if (body.deferredTurns)
|
|
450
|
+
setDeferredTurns(body.deferredTurns);
|
|
431
451
|
if (body.deferredTurn)
|
|
432
452
|
setDeferredTurn(body.deferredTurn);
|
|
433
|
-
if (body.error?.code !== "
|
|
453
|
+
if (body.error?.code !== "TURN_NOT_ACTIVE") {
|
|
434
454
|
setError(body.error?.message ??
|
|
435
455
|
`待发送消息未被服务端接受(HTTP ${response.status})`);
|
|
436
456
|
return { action: "handled" };
|
|
@@ -442,7 +462,7 @@ export function useChatStream(params) {
|
|
|
442
462
|
state: canonical ?? undefined,
|
|
443
463
|
});
|
|
444
464
|
if (decision.action === "deferred") {
|
|
445
|
-
acceptDeferred(decision.deferredTurn);
|
|
465
|
+
acceptDeferred(decision.deferredTurn, canonical?.deferredTurns);
|
|
446
466
|
return { action: "handled" };
|
|
447
467
|
}
|
|
448
468
|
if (decision.action === "accepted") {
|
|
@@ -451,7 +471,7 @@ export function useChatStream(params) {
|
|
|
451
471
|
}
|
|
452
472
|
if (decision.action === "retry-deferred" && attempt === 0)
|
|
453
473
|
continue;
|
|
454
|
-
if (decision.action === "send-now"
|
|
474
|
+
if (decision.action === "send-now") {
|
|
455
475
|
showNotice("上一轮已停止,正在发送当前消息…");
|
|
456
476
|
return { action: "send-now" };
|
|
457
477
|
}
|
|
@@ -468,14 +488,14 @@ export function useChatStream(params) {
|
|
|
468
488
|
state: canonical ?? undefined,
|
|
469
489
|
});
|
|
470
490
|
if (decision.action === "deferred") {
|
|
471
|
-
acceptDeferred(decision.deferredTurn);
|
|
491
|
+
acceptDeferred(decision.deferredTurn, canonical?.deferredTurns);
|
|
472
492
|
return { action: "handled" };
|
|
473
493
|
}
|
|
474
494
|
if (decision.action === "accepted") {
|
|
475
495
|
acceptDispatched();
|
|
476
496
|
return { action: "handled" };
|
|
477
497
|
}
|
|
478
|
-
if (decision.action === "send-now"
|
|
498
|
+
if (decision.action === "send-now") {
|
|
479
499
|
showNotice("上一轮已停止,正在发送当前消息…");
|
|
480
500
|
return { action: "send-now" };
|
|
481
501
|
}
|
|
@@ -489,15 +509,20 @@ export function useChatStream(params) {
|
|
|
489
509
|
refs,
|
|
490
510
|
thread.deferredTurn,
|
|
491
511
|
setDeferredTurn,
|
|
512
|
+
setDeferredTurns,
|
|
492
513
|
setInput,
|
|
493
514
|
setPendingImages,
|
|
494
515
|
setError,
|
|
495
516
|
showNotice,
|
|
496
517
|
fetchSessionState,
|
|
497
518
|
]);
|
|
498
|
-
|
|
519
|
+
/** DELETE the queued turn; "restore" moves its text back into the composer,
|
|
520
|
+
* "discard" drops it entirely (2026-08-23 queue chip actions). */
|
|
521
|
+
const requestDeferredTurnDelete = useCallback(async (mode, target) => {
|
|
499
522
|
const activeSession = refs.sessionRef.current;
|
|
500
|
-
const deferred =
|
|
523
|
+
const deferred = target
|
|
524
|
+
? thread.deferredTurns.find((item) => item.clientRequestId === target.clientRequestId)
|
|
525
|
+
: thread.deferredTurns[0] ?? thread.deferredTurn;
|
|
501
526
|
if (!activeSession || !deferred)
|
|
502
527
|
return;
|
|
503
528
|
try {
|
|
@@ -518,11 +543,18 @@ export function useChatStream(params) {
|
|
|
518
543
|
setError(body.error?.message ?? "待发送消息撤回失败");
|
|
519
544
|
return;
|
|
520
545
|
}
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
546
|
+
const remaining = thread.deferredTurns.filter((item) => item.clientRequestId !== deferred.clientRequestId);
|
|
547
|
+
setDeferredTurns(remaining);
|
|
548
|
+
setDeferredTurn(remaining[0] ?? null);
|
|
549
|
+
if (mode === "restore") {
|
|
550
|
+
setInput(deferred.text ?? body.deferredTurn?.text ?? "");
|
|
551
|
+
setPendingImages(deferred.images ?? body.deferredTurn?.images ?? []);
|
|
552
|
+
showNotice("待发送消息已移回输入框");
|
|
553
|
+
}
|
|
554
|
+
else {
|
|
555
|
+
showNotice("已删除排队消息");
|
|
556
|
+
}
|
|
524
557
|
setError(null);
|
|
525
|
-
showNotice("待发送消息已移回输入框");
|
|
526
558
|
}
|
|
527
559
|
catch (error) {
|
|
528
560
|
setError(error instanceof Error ? error.message : String(error));
|
|
@@ -531,12 +563,66 @@ export function useChatStream(params) {
|
|
|
531
563
|
origin,
|
|
532
564
|
refs,
|
|
533
565
|
thread.deferredTurn,
|
|
566
|
+
thread.deferredTurns,
|
|
534
567
|
setDeferredTurn,
|
|
568
|
+
setDeferredTurns,
|
|
535
569
|
setInput,
|
|
536
570
|
setPendingImages,
|
|
537
571
|
setError,
|
|
538
572
|
showNotice,
|
|
539
573
|
]);
|
|
574
|
+
const cancelDeferredTurn = useCallback(() => requestDeferredTurnDelete("restore"), [requestDeferredTurnDelete]);
|
|
575
|
+
const discardDeferredTurn = useCallback((target) => requestDeferredTurnDelete("discard", target), [requestDeferredTurnDelete]);
|
|
576
|
+
const editDeferredTurn = useCallback(async (target, text) => {
|
|
577
|
+
const activeSession = refs.sessionRef.current;
|
|
578
|
+
if (!activeSession)
|
|
579
|
+
return;
|
|
580
|
+
const item = thread.deferredTurns.find((entry) => entry.clientRequestId === target.clientRequestId);
|
|
581
|
+
if (!item)
|
|
582
|
+
return;
|
|
583
|
+
try {
|
|
584
|
+
const response = await fetch(`${origin}/api/operator/v1/chat/sessions/${encodeURIComponent(activeSession.sessionId)}/deferred-turn`, {
|
|
585
|
+
method: "PUT",
|
|
586
|
+
headers: { "Content-Type": "application/json", "x-loop-console-confirmation": confirmationToken() },
|
|
587
|
+
credentials: "include",
|
|
588
|
+
body: JSON.stringify({ clientRequestId: target.clientRequestId, blockedByTurnId: item.blockedByTurnId, text, images: item.images ?? [], expectedRevision: target.revision }),
|
|
589
|
+
});
|
|
590
|
+
const body = (await response.json().catch(() => ({})));
|
|
591
|
+
if (!response.ok)
|
|
592
|
+
throw new Error(body.error?.message ?? "编辑排队消息失败");
|
|
593
|
+
if (body.deferredTurns)
|
|
594
|
+
setDeferredTurns(body.deferredTurns);
|
|
595
|
+
if (body.deferredTurn)
|
|
596
|
+
setDeferredTurn(body.deferredTurn);
|
|
597
|
+
setError(null);
|
|
598
|
+
}
|
|
599
|
+
catch (error) {
|
|
600
|
+
setError(error instanceof Error ? error.message : "编辑排队消息失败");
|
|
601
|
+
}
|
|
602
|
+
}, [origin, refs, thread.deferredTurns, setDeferredTurn, setDeferredTurns, setError]);
|
|
603
|
+
const steerDeferredTurn = useCallback(async (target) => {
|
|
604
|
+
const activeSession = refs.sessionRef.current;
|
|
605
|
+
if (!activeSession)
|
|
606
|
+
return;
|
|
607
|
+
try {
|
|
608
|
+
const response = await fetch(`${origin}/api/operator/v1/chat/sessions/${encodeURIComponent(activeSession.sessionId)}/deferred-turn/${encodeURIComponent(target.clientRequestId)}/steer`, {
|
|
609
|
+
method: "POST",
|
|
610
|
+
headers: { "Content-Type": "application/json", "x-loop-console-confirmation": confirmationToken() },
|
|
611
|
+
credentials: "include",
|
|
612
|
+
body: JSON.stringify({ expectedRevision: target.revision }),
|
|
613
|
+
});
|
|
614
|
+
const body = (await response.json().catch(() => ({})));
|
|
615
|
+
if (!response.ok)
|
|
616
|
+
throw new Error(body.error?.message ?? "插话发送失败");
|
|
617
|
+
const remaining = body.deferredTurns ?? thread.deferredTurns.filter((item) => item.clientRequestId !== target.clientRequestId);
|
|
618
|
+
setDeferredTurns(remaining);
|
|
619
|
+
setDeferredTurn(remaining[0] ?? null);
|
|
620
|
+
setError(null);
|
|
621
|
+
}
|
|
622
|
+
catch (error) {
|
|
623
|
+
setError(error instanceof Error ? error.message : "插话发送失败");
|
|
624
|
+
}
|
|
625
|
+
}, [origin, refs, thread.deferredTurns, setDeferredTurn, setDeferredTurns, setError]);
|
|
540
626
|
const sendPrompt = useCallback(async (text, images) => {
|
|
541
627
|
const initialSession = refs.sessionRef.current;
|
|
542
628
|
if (!initialSession || (!text.trim() && images.length === 0))
|
|
@@ -548,10 +634,14 @@ export function useChatStream(params) {
|
|
|
548
634
|
});
|
|
549
635
|
const clientRequestId = requestId("chat-submit");
|
|
550
636
|
if (route.action === "hold") {
|
|
551
|
-
|
|
637
|
+
const phase = turnPhaseFromOwnership(ownership);
|
|
638
|
+
if (phase === "stopping")
|
|
552
639
|
showNotice("正在确认上一轮停止状态,当前消息已保留");
|
|
640
|
+
else if (phase === "running")
|
|
641
|
+
showNotice("正在确认当前回合状态,当前消息已保留");
|
|
553
642
|
return;
|
|
554
643
|
}
|
|
644
|
+
recordHistory?.(text);
|
|
555
645
|
if (route.action === "queue-deferred") {
|
|
556
646
|
const deferred = await queueDeferredTurn({
|
|
557
647
|
text,
|
|
@@ -721,6 +811,7 @@ export function useChatStream(params) {
|
|
|
721
811
|
setTurnPhase,
|
|
722
812
|
setStreamingAssistantId,
|
|
723
813
|
queueDeferredTurn,
|
|
814
|
+
recordHistory,
|
|
724
815
|
]);
|
|
725
816
|
const handleStop = useCallback(async () => {
|
|
726
817
|
// AC-R5-001: durable stopRequested is set synchronously before any await so
|
|
@@ -733,10 +824,12 @@ export function useChatStream(params) {
|
|
|
733
824
|
// Decide + mark intent/coalesce only; POST is issued once below.
|
|
734
825
|
const stopResult = requestStopOnOwnership(bag, {
|
|
735
826
|
postAbort: () => { },
|
|
736
|
-
notice: noticeSink(),
|
|
737
827
|
});
|
|
738
828
|
applyOwnershipBagToRefs(refs, bag);
|
|
739
|
-
|
|
829
|
+
refs.streamingRef.current = false;
|
|
830
|
+
setStreaming(false);
|
|
831
|
+
setStreamingAssistantId(null);
|
|
832
|
+
setTurnPhase("idle");
|
|
740
833
|
// Immediate abort already scheduled for a known turnId — coalesce via
|
|
741
834
|
// abortPostedTurnIds; stopInFlight only gates concurrent in-flight POSTs.
|
|
742
835
|
if (stopResult.abortedTurnId) {
|
|
@@ -786,7 +879,6 @@ export function useChatStream(params) {
|
|
|
786
879
|
markStopRequested(lateBag);
|
|
787
880
|
const late = requestStopOnOwnership(lateBag, {
|
|
788
881
|
postAbort: () => { },
|
|
789
|
-
notice: noticeSink(),
|
|
790
882
|
});
|
|
791
883
|
applyOwnershipBagToRefs(refs, lateBag);
|
|
792
884
|
if (late.abortedTurnId) {
|
|
@@ -796,7 +888,13 @@ export function useChatStream(params) {
|
|
|
796
888
|
finally {
|
|
797
889
|
refs.stopInFlightRef.current = false;
|
|
798
890
|
}
|
|
799
|
-
}, [
|
|
891
|
+
}, [
|
|
892
|
+
refs,
|
|
893
|
+
postAbortForTurn,
|
|
894
|
+
setStreaming,
|
|
895
|
+
setStreamingAssistantId,
|
|
896
|
+
setTurnPhase,
|
|
897
|
+
]);
|
|
800
898
|
// State reconciliation is only a status fence: it never consumes/creates
|
|
801
899
|
// message events, and a generation check discards late responses (M5.3).
|
|
802
900
|
// Busy truth = durable activeTurn union runtime busy facts (AC-003/AC-006).
|
|
@@ -816,7 +914,8 @@ export function useChatStream(params) {
|
|
|
816
914
|
const sessionId = session.sessionId;
|
|
817
915
|
const generation = refs.sessionGenerationRef.current;
|
|
818
916
|
let cancelled = false;
|
|
819
|
-
|
|
917
|
+
let syncInFlight = null;
|
|
918
|
+
const runSync = async () => {
|
|
820
919
|
try {
|
|
821
920
|
const res = await fetch(operatorChatStateUrl(origin, sessionId), {
|
|
822
921
|
credentials: "include",
|
|
@@ -845,10 +944,16 @@ export function useChatStream(params) {
|
|
|
845
944
|
},
|
|
846
945
|
});
|
|
847
946
|
applyOwnershipBagToRefs(refs, bag);
|
|
848
|
-
|
|
947
|
+
const ownershipPhase = turnPhaseFromOwnership(bag);
|
|
948
|
+
setTurnPhase(visualTurnPhaseFromOwnership(bag));
|
|
949
|
+
if (ownershipPhase === "stopping") {
|
|
950
|
+
refs.streamingRef.current = false;
|
|
951
|
+
setStreaming(false);
|
|
952
|
+
setStreamingAssistantId(null);
|
|
953
|
+
}
|
|
849
954
|
if (state && "deferredTurn" in state)
|
|
850
955
|
setDeferredTurn(state.deferredTurn ?? null);
|
|
851
|
-
if (result === "bound") {
|
|
956
|
+
if (result === "bound" && ownershipPhase !== "stopping") {
|
|
852
957
|
// US-BUSY-ACTIVE-BOUND: keep streaming/busy with same id.
|
|
853
958
|
setStreaming(true);
|
|
854
959
|
return;
|
|
@@ -926,19 +1031,41 @@ export function useChatStream(params) {
|
|
|
926
1031
|
// offline/background case: interval, visibility and online retry.
|
|
927
1032
|
}
|
|
928
1033
|
};
|
|
1034
|
+
const sync = () => {
|
|
1035
|
+
if (syncInFlight)
|
|
1036
|
+
return syncInFlight;
|
|
1037
|
+
syncInFlight = runSync().finally(() => {
|
|
1038
|
+
syncInFlight = null;
|
|
1039
|
+
});
|
|
1040
|
+
return syncInFlight;
|
|
1041
|
+
};
|
|
929
1042
|
const onVisible = () => {
|
|
930
1043
|
if (document.visibilityState === "visible")
|
|
931
1044
|
void sync();
|
|
932
1045
|
};
|
|
933
1046
|
const onOnline = () => void sync();
|
|
934
1047
|
const interval = window.setInterval(() => void sync(), 15_000);
|
|
1048
|
+
let stopRetryTimer;
|
|
1049
|
+
let stopRetryIndex = 0;
|
|
1050
|
+
const scheduleStopRetry = () => {
|
|
1051
|
+
if (cancelled ||
|
|
1052
|
+
stopRetryIndex >= STOP_RECONCILE_DELAYS_MS.length ||
|
|
1053
|
+
(!refs.stopRequestedRef.current && !refs.stoppingTurnIdRef.current))
|
|
1054
|
+
return;
|
|
1055
|
+
const delay = STOP_RECONCILE_DELAYS_MS[stopRetryIndex++];
|
|
1056
|
+
stopRetryTimer = window.setTimeout(() => {
|
|
1057
|
+
void sync().finally(scheduleStopRetry);
|
|
1058
|
+
}, delay);
|
|
1059
|
+
};
|
|
935
1060
|
document.addEventListener("visibilitychange", onVisible);
|
|
936
1061
|
window.addEventListener("online", onOnline);
|
|
937
1062
|
// Immediate tick so terminal-before-streaming-false races converge promptly.
|
|
938
|
-
void sync();
|
|
1063
|
+
void sync().finally(scheduleStopRetry);
|
|
939
1064
|
return () => {
|
|
940
1065
|
cancelled = true;
|
|
941
1066
|
window.clearInterval(interval);
|
|
1067
|
+
if (stopRetryTimer !== undefined)
|
|
1068
|
+
window.clearTimeout(stopRetryTimer);
|
|
942
1069
|
document.removeEventListener("visibilitychange", onVisible);
|
|
943
1070
|
window.removeEventListener("online", onOnline);
|
|
944
1071
|
};
|
|
@@ -969,5 +1096,13 @@ export function useChatStream(params) {
|
|
|
969
1096
|
}
|
|
970
1097
|
};
|
|
971
1098
|
}, [session, recoveryVersion, refs, startEventsStream]);
|
|
972
|
-
return {
|
|
1099
|
+
return {
|
|
1100
|
+
sendPrompt,
|
|
1101
|
+
handleStop,
|
|
1102
|
+
cancelDeferredTurn,
|
|
1103
|
+
discardDeferredTurn,
|
|
1104
|
+
editDeferredTurn,
|
|
1105
|
+
steerDeferredTurn,
|
|
1106
|
+
openEventsStream,
|
|
1107
|
+
};
|
|
973
1108
|
}
|