@tea-agent/loop-agent 0.43.0-next.8 → 0.43.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +1 -1
- package/CHANGELOG.md +41 -2312
- package/README.md +7 -0
- package/dist/application/dag/args.js +8 -0
- package/dist/application/task-lifecycle/advance.js +38 -7
- package/dist/build-stamp.json +3 -3
- package/dist/cli/command-definitions.js +2 -0
- package/dist/cli/program.js +3 -1
- package/dist/cli/update/init-surface-notifier.js +2 -0
- package/dist/cli/update/policy.js +2 -0
- package/dist/commands/client-recovery.js +20 -55
- package/dist/commands/cursor-prompt.js +2 -0
- package/dist/commands/dag-approve.js +2 -0
- package/dist/commands/dag-final-verification.js +2 -0
- package/dist/commands/dag-init-hybrid.js +2 -0
- package/dist/commands/dag-reconcile-tasks.js +2 -0
- package/dist/commands/dag-reject.js +2 -0
- package/dist/commands/dag-report.js +2 -0
- package/dist/commands/dag-rerun.js +2 -0
- package/dist/commands/dag-resume.js +2 -0
- package/dist/commands/dag-workflow-compile.js +2 -0
- package/dist/commands/dag-workflow-plan.js +2 -0
- package/dist/commands/dag-workflow-validate.js +2 -0
- package/dist/commands/delegate.js +2 -0
- package/dist/commands/examples.js +2 -0
- package/dist/commands/import-prd.js +2 -0
- package/dist/commands/init-model-catalog.js +2 -0
- package/dist/commands/init.js +19 -13
- package/dist/commands/instructions.js +2 -0
- package/dist/commands/knowledge.js +2 -0
- package/dist/commands/loop-benchmark.js +2 -0
- package/dist/commands/pi-prompt.js +2 -0
- package/dist/commands/pi-reuse-benchmark.js +2 -0
- package/dist/commands/promote-run.js +2 -0
- package/dist/commands/stats.js +1 -1
- package/dist/commands/study-init.js +2 -0
- package/dist/commands/task-advance.js +34 -0
- package/dist/commands/task-source-prepare.js +2 -0
- package/dist/commands/worktree-create.js +2 -0
- package/dist/commands/worktree-remove.js +2 -0
- package/dist/executors/dag-pi-executor.js +1650 -198
- package/dist/executors/pi-executor.js +85 -3
- package/dist/executors/pi-sdk-executor.js +18 -0
- package/dist/executors/shell-executor.js +93 -7
- package/dist/executors/shell-verification.js +3 -0
- package/dist/executors/shell-write-guard.js +27 -32
- package/dist/governance/exec-plans.js +6 -3
- package/dist/infrastructure/console/app-data.js +6 -0
- package/dist/shared/artifacts-core.js +2 -0
- package/dist/shared/backend-dogfood-preflight.js +47 -0
- package/dist/shared/dag-failure-category.js +3 -0
- package/dist/shared/git-progress.js +2 -0
- package/dist/shared/one-shot-prompt-args.js +2 -0
- package/dist/shared/operator/capabilities.js +180 -0
- package/dist/shared/package-metadata.js +6 -4
- package/dist/shared/pi-context-pressure/extension.js +26 -16
- package/dist/shared/pi-context-pressure/sift-bridge.js +1 -1
- package/dist/shared/pi-provider-recovery.js +182 -0
- package/dist/shared/pi-retry-settings.js +40 -0
- package/dist/shared/reference-context.js +2 -0
- package/dist/task/config-types.js +29 -0
- package/dist/task/contract/project.js +9 -0
- package/dist/task/contract/recover.js +6 -0
- package/dist/task/contract/schema.js +17 -0
- package/dist/task/source-prepare/artifact-meta.js +9 -2
- package/dist/task/source-prepare/build-draft.js +60 -0
- package/dist/task/source-prepare/fragment-inventory.js +4 -1
- package/dist/task/source-prepare/parse-intent.js +36 -11
- package/dist/task/task-demand-routing.js +5 -2
- package/dist/worker/console/chat/browser-automation.js +523 -0
- package/dist/worker/console/chat/browser-policy.js +6 -36
- package/dist/worker/console/chat/browser-routes.js +188 -56
- package/dist/worker/console/chat/explore-tools.js +5 -2
- package/dist/worker/console/chat/pi-mode-loop-isolation.js +155 -0
- package/dist/worker/console/chat/pi-runtime/custom-tools/ask-user-question.js +44 -0
- package/dist/worker/console/chat/pi-runtime/custom-tools/browser-tools.js +184 -0
- package/dist/worker/console/chat/pi-runtime/custom-tools/explore-tools.js +202 -0
- package/dist/worker/console/chat/pi-runtime/custom-tools/goal-tools.js +79 -0
- package/dist/worker/console/chat/pi-runtime/custom-tools/operator-tools.js +94 -0
- package/dist/worker/console/chat/pi-runtime/custom-tools/scheduled-goal-tools.js +22 -0
- package/dist/worker/console/chat/pi-runtime/custom-tools/todo-write.js +38 -0
- package/dist/worker/console/chat/pi-runtime/inspection-types.js +28 -0
- package/dist/worker/console/chat/pi-runtime/inspection.js +439 -0
- package/dist/worker/console/chat/pi-runtime/progressive-tools.js +195 -0
- package/dist/worker/console/chat/pi-runtime/sdk-bindings.js +153 -0
- package/dist/worker/console/chat/pi-runtime/sdk-session.js +5 -0
- package/dist/worker/console/chat/pi-runtime.js +208 -1138
- package/dist/worker/console/chat/routes.js +59 -24
- package/dist/worker/console/chat/scheduled-goal-booking.js +59 -0
- package/dist/worker/console/chat/scheduled-goal-delivery.js +27 -0
- package/dist/worker/console/chat/scheduled-goal-request.js +190 -0
- package/dist/worker/console/chat/sdd-data-alignment.js +1 -1
- package/dist/worker/console/chat/session-mode-view.js +5 -4
- package/dist/worker/console/chat/session-mode.js +18 -12
- package/dist/worker/console/chat/session-store.js +42 -9
- package/dist/worker/console/chat/terminal-sessions.js +65 -1
- package/dist/worker/console/chat/terminal-tools.js +17 -5
- package/dist/worker/console/chat/tool-preview.js +115 -0
- package/dist/worker/console/chat/tools.js +10 -0
- package/dist/worker/console/chat/turn-order.js +13 -0
- package/dist/worker/console/chat/turn-process.js +32 -30
- package/dist/worker/console/console-update-and-init.js +1 -19
- package/dist/worker/console/console-update-runtime.js +5 -20
- package/dist/worker/console/operator-actions.js +63 -1
- package/dist/worker/console/prd-intake-bridge.js +54 -1
- package/dist/worker/console/routes.js +8 -0
- package/dist/worker/console/scheduled-goal-host.js +98 -0
- package/dist/worker/console/scheduled-goal-operation-adapter.js +127 -0
- package/dist/worker/console/server.js +24 -1
- package/dist/worker/console/static/assets/{abnfDiagram-N423BO3Z-CwAxHSAM.js → abnfDiagram-N423BO3Z-8-j6y-sd.js} +1 -1
- package/dist/worker/console/static/assets/{arc-Bozf-oeF.js → arc-eoQiMvuk.js} +1 -1
- package/dist/worker/console/static/assets/{architectureDiagram-T3A2C74G-DEypbEQv.js → architectureDiagram-T3A2C74G-C4A3uMcI.js} +1 -1
- package/dist/worker/console/static/assets/{blockDiagram-VBNYF7ZC-B1BQWCbE.js → blockDiagram-VBNYF7ZC-D4zD2F-Q.js} +1 -1
- package/dist/worker/console/static/assets/{c4Diagram-5PPSVZJV-B3u5-yZz.js → c4Diagram-5PPSVZJV-j1RkJziL.js} +1 -1
- package/dist/worker/console/static/assets/channel-ChE7y-cx.js +1 -0
- package/dist/worker/console/static/assets/{chunk-2GRJ4B5K-DSUc2NTh.js → chunk-2GRJ4B5K-YBHmhik1.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-2Q5K7J3B-DZouMIvT.js → chunk-2Q5K7J3B-COfWyo9P.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5RXB4S5H-B3bhB5hh.js → chunk-5RXB4S5H-I99OUkHY.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-5VM5RSS4-CZFP_BOz.js → chunk-5VM5RSS4-XKxoJNJ4.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-6Q2QTUOP-DnE_wb-X.js → chunk-6Q2QTUOP-DLT_cYx1.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-GF5L2VYU-C2OfoiWm.js → chunk-GF5L2VYU-CxcKZ9mV.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-JWPE2WC7-BKUTL8mm.js → chunk-JWPE2WC7-V1EqXdjY.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-KBJHAD2P-BT5oz0tg.js → chunk-KBJHAD2P-DebTtdFp.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-RYQCIY6F-D8vk-GJi.js → chunk-RYQCIY6F-B-ivNRDf.js} +1 -1
- package/dist/worker/console/static/assets/{chunk-XXDRQBXY-CkUIm3zu.js → chunk-XXDRQBXY-DC_Ds11b.js} +1 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-C01TCf2X.js +1 -0
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-C01TCf2X.js +1 -0
- package/dist/worker/console/static/assets/{cose-bilkent-JH36ORCC-CO9QNHmR.js → cose-bilkent-JH36ORCC-sWEqKwIb.js} +1 -1
- package/dist/worker/console/static/assets/{cynefin-VYW2F7L2-s52EPl2Z.js → cynefin-VYW2F7L2-BXa_dcu4.js} +1 -1
- package/dist/worker/console/static/assets/{cynefinDiagram-MW4NZA55-BHaseJfW.js → cynefinDiagram-MW4NZA55-C-Mle84F.js} +1 -1
- package/dist/worker/console/static/assets/{dagre-VZM6K2ZE-CTVhRG_R.js → dagre-VZM6K2ZE-CXPDBITe.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-7IWD3JNH-B6pYeOMR.js → diagram-7IWD3JNH-CC-WJQfa.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-B4RE2ZJO-CXXRzw5j.js → diagram-B4RE2ZJO-CDAV5Vs4.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-LBJQPF4R-BOeJHiLA.js → diagram-LBJQPF4R-CmFiAcNz.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-Q27KOJAE-DbEg6h_l.js → diagram-Q27KOJAE-DPBZHuyn.js} +1 -1
- package/dist/worker/console/static/assets/{diagram-UB23O5K3-CjlEcbX6.js → diagram-UB23O5K3-jUlm_Ds3.js} +1 -1
- package/dist/worker/console/static/assets/{ebnfDiagram-BXEA7PRR-BcqMPbD_.js → ebnfDiagram-BXEA7PRR-DWhQ3mfY.js} +1 -1
- package/dist/worker/console/static/assets/{erDiagram-JOGREHBK-DWfRyHe5.js → erDiagram-JOGREHBK-Tr2gMqet.js} +1 -1
- package/dist/worker/console/static/assets/{flowDiagram-UKHOOZJN-Bz7TeIc8.js → flowDiagram-UKHOOZJN-DJjVQHPA.js} +1 -1
- package/dist/worker/console/static/assets/{ganttDiagram-PKOTCBZU-BD-JxYjU.js → ganttDiagram-PKOTCBZU-D74dQ4u0.js} +1 -1
- package/dist/worker/console/static/assets/{gitGraphDiagram-DS77QQ5N-Cv16vMHK.js → gitGraphDiagram-DS77QQ5N-DwW0tZ0X.js} +1 -1
- package/dist/worker/console/static/assets/index-BWkIfcrK.css +1 -0
- package/dist/worker/console/static/assets/index-Cdkvw_H6.js +469 -0
- package/dist/worker/console/static/assets/{infoDiagram-6WML65LV-BlWO0HQQ.js → infoDiagram-6WML65LV-ILCbxJyb.js} +1 -1
- package/dist/worker/console/static/assets/{ishikawaDiagram-WSZJBQD7-C4mbsn97.js → ishikawaDiagram-WSZJBQD7-DeuWBQ89.js} +1 -1
- package/dist/worker/console/static/assets/{journeyDiagram-NVQOT4AX-CBPvVKjw.js → journeyDiagram-NVQOT4AX-CF5ih8Fk.js} +1 -1
- package/dist/worker/console/static/assets/{kanban-definition-27J2QSJJ-DPHLp54K.js → kanban-definition-27J2QSJJ-C7yOSuRO.js} +1 -1
- package/dist/worker/console/static/assets/{linear-CXx_9o6k.js → linear-BDZ9riWi.js} +1 -1
- package/dist/worker/console/static/assets/{mermaid.core-DU3IxMq9.js → mermaid.core-7pKqYtpZ.js} +5 -5
- package/dist/worker/console/static/assets/{mindmap-definition-FAOFIHXS-COm4a2ud.js → mindmap-definition-FAOFIHXS-LeJDybSU.js} +1 -1
- package/dist/worker/console/static/assets/{pegDiagram-VL7TDLO6-Cbt7OA5d.js → pegDiagram-VL7TDLO6-BJvT3pMD.js} +1 -1
- package/dist/worker/console/static/assets/{pieDiagram-7S7Q4E2Y-BiYV_4Wl.js → pieDiagram-7S7Q4E2Y-_rGqpMan.js} +1 -1
- package/dist/worker/console/static/assets/{quadrantDiagram-CIZ2JOQS-lg2zhvbp.js → quadrantDiagram-CIZ2JOQS-DPcSv5aA.js} +1 -1
- package/dist/worker/console/static/assets/{railroadDiagram-AXF67PYL-cBLvtf0p.js → railroadDiagram-AXF67PYL-Drxx4hkJ.js} +1 -1
- package/dist/worker/console/static/assets/{requirementDiagram-LRYGKXZP-BoekU3Bg.js → requirementDiagram-LRYGKXZP-BCTUdU4z.js} +1 -1
- package/dist/worker/console/static/assets/{sankeyDiagram-W5VNT64P-D_DMZYga.js → sankeyDiagram-W5VNT64P-B6wzbZmB.js} +1 -1
- package/dist/worker/console/static/assets/{sequenceDiagram-SI44F4Z6-Lzmzn8lz.js → sequenceDiagram-SI44F4Z6-BGt8d3QQ.js} +1 -1
- package/dist/worker/console/static/assets/{sizeCapture-X5ZJPWSS-Cos3HpIe.js → sizeCapture-X5ZJPWSS-7nlhJKo7.js} +1 -1
- package/dist/worker/console/static/assets/{stateDiagram-OKZ733FA-D-hFBGsx.js → stateDiagram-OKZ733FA-Cv2stqMA.js} +1 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-DC7V5vcq.js +1 -0
- package/dist/worker/console/static/assets/{swimlanes-SLNWSIFB-BY95sCVI.js → swimlanes-SLNWSIFB-qDAo4Yc1.js} +2 -2
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-Bwy4QUTO.js +8 -0
- package/dist/worker/console/static/assets/{timeline-definition-Z64GVDOM-D1iCgWHS.js → timeline-definition-Z64GVDOM-CKbDNKTr.js} +1 -1
- package/dist/worker/console/static/assets/{vennDiagram-T6HMQDX7-D9IYy4WK.js → vennDiagram-T6HMQDX7-DI-9EHic.js} +1 -1
- package/dist/worker/console/static/assets/{wardleyDiagram-T6FBY63Y-C9yJwO-R.js → wardleyDiagram-T6FBY63Y-BrzzRDpX.js} +1 -1
- package/dist/worker/console/static/assets/{xychartDiagram-ELKLHX3M-CC2llyou.js → xychartDiagram-ELKLHX3M-BclH5hGh.js} +1 -1
- package/dist/worker/console/static/index.html +2 -2
- package/dist/worker/console/static-src/operator-chat/active-turn-tail.js +10 -0
- package/dist/worker/console/static-src/operator-chat/chat-scroll-position.js +24 -0
- package/dist/worker/console/static-src/operator-chat/chat-sse-events.js +41 -11
- package/dist/worker/console/static-src/operator-chat/compaction-message.js +10 -17
- package/dist/worker/console/static-src/operator-chat/composer-session-ledger.js +192 -0
- package/dist/worker/console/static-src/operator-chat/pending-user-message.js +9 -1
- package/dist/worker/console/static-src/operator-chat/slash-palette-nav.js +15 -5
- package/dist/worker/console/static-src/operator-chat/timeline-merge.js +29 -0
- package/dist/worker/console/static-src/operator-chat/turn-process-disclosure.js +10 -0
- package/dist/worker/console/static-src/operator-chat/useChatSessions.js +105 -44
- package/dist/worker/console/static-src/operator-chat/useChatStream.js +145 -70
- package/dist/worker/console/static-src/operator-chat/useChatThread.js +38 -57
- package/dist/worker/console/static-src/operator-chat/useComposer.js +195 -73
- package/dist/worker/console/static-src/operator-chat/useRepoBrowser.js +2 -0
- package/dist/worker/console/static-src/prd-file-import.js +3 -1
- package/dist/worker/console/workspace-context.js +22 -0
- package/dist/worker/console/workspace-initialization.js +209 -0
- package/dist/worker/delivery/git-transaction.js +2 -0
- package/dist/worker/feature/fullstack-validate.js +2 -0
- package/dist/worker/loop-agent/loop-agent-client.js +8 -2
- package/dist/worker/materialize/harness-task-materializer.js +4 -1
- package/dist/worker/metrics/projector.js +1 -1
- package/dist/worker/observability/event-store.js +4 -1
- package/dist/worker/observability/read-model.js +4 -1
- package/dist/worker/observe/node-transparency.js +23 -1
- package/dist/worker/observe/routes.js +4 -0
- package/dist/worker/observe/static/operator-chrome.js +3 -1
- package/dist/worker/observe/static/styles.css +7 -4
- package/dist/worker/observe/static/views/dag-inspector.js +5 -19
- package/dist/worker/outcomes/declared-artifacts.js +2 -0
- package/dist/worker/preflight.js +3 -0
- package/dist/worker/scheduler/scheduled-goal-dispatch.js +117 -0
- package/dist/worker/scheduler/scheduled-goal-evidence.js +167 -0
- package/dist/worker/scheduler/scheduled-goal-recovery.js +62 -0
- package/dist/worker/scheduler/scheduled-goal-store.js +699 -0
- package/dist/worker/scheduler/scheduled-goal-supervisor.js +132 -0
- package/dist/worker/scheduler/scheduled-goal-time.js +102 -0
- package/dist/worker/scheduler/scheduled-goal-types.js +95 -0
- package/dist/worker/task-spec/validate.js +4 -1
- package/dist/workflows/dag/backend-test-markdown-workflow.js +6 -24
- package/dist/workflows/dag/backend-test-plan-protocol.js +82 -5
- package/dist/workflows/dag/dag-retry-schema.js +11 -0
- package/dist/workflows/dag/decision-envelope.js +2 -0
- package/dist/workflows/dag/dynamic-runtime/shared.js +2 -2
- package/dist/workflows/dag/frontend-closeout.js +3 -1
- package/dist/workflows/dag/frontend-committed-facts.js +461 -0
- package/dist/workflows/dag/frontend-durable-tools.js +15 -3
- package/dist/workflows/dag/frontend-implementation-contract.js +369 -12
- package/dist/workflows/dag/frontend-plan-canary.js +53 -0
- package/dist/workflows/dag/frontend-plan-decision-contract.js +803 -0
- package/dist/workflows/dag/frontend-plan-render.js +0 -2
- package/dist/workflows/dag/frontend-provider-capability-matrix.js +8 -61
- package/dist/workflows/dag/frontend-recovery-run.js +57 -0
- package/dist/workflows/dag/frontend-repair.js +55 -76
- package/dist/workflows/dag/frontend-review-context.js +42 -68
- package/dist/workflows/dag/frontend-review-scopes.js +2 -2
- package/dist/workflows/dag/frontend-risk.js +92 -10
- package/dist/workflows/dag/frontend-session-budget.js +117 -3
- package/dist/workflows/dag/frontend-shape.js +11 -55
- package/dist/workflows/dag/frontend-test-execution-evidence.js +35 -10
- package/dist/workflows/dag/frontend-typed-event-store.js +32 -25
- package/dist/workflows/dag/frontend-verification-trace.js +33 -54
- package/dist/workflows/dag/frontend-writer-admission.js +3 -47
- package/dist/workflows/dag/frontend-writer-status.js +0 -23
- package/dist/workflows/dag/init-hybrid.js +47 -19
- package/dist/workflows/dag/interrupt-request.js +2 -0
- package/dist/workflows/dag/lifecycle.js +11 -2
- package/dist/workflows/dag/node-execution.js +50 -13
- package/dist/workflows/dag/reconcile-run.js +2 -0
- package/dist/workflows/dag/repair-artifact.js +3 -1
- package/dist/workflows/dag/report.js +2 -0
- package/dist/workflows/dag/rerun-plan.js +10 -0
- package/dist/workflows/dag/rerun-task.js +79 -1
- package/dist/workflows/dag/retry-policy.js +18 -0
- package/dist/workflows/dag/scheduler.js +2 -4
- package/dist/workflows/dag/types.js +44 -6
- package/dist/workflows/dag/validate.js +4 -0
- package/docs/README.md +1 -0
- package/docs/architecture/runtime-boundaries.md +3 -3
- package/docs/governance/README.md +1 -0
- package/docs/init-surface.manifest.json +1 -0
- package/docs/operations/README.md +2 -0
- package/docs/templates/README.md +1 -1
- package/docs/templates/agent-dag.schema.json +2 -2
- package/docs/templates/backend-test-dag.json +14 -10
- package/docs/templates/frontend-implementation-contract.schema.json +0 -7
- package/package.json +9 -3
- package/skills/frontend-bounded-implement/references/code-standards.md +3 -3
- package/skills/frontend-contract/references/contract-protocol.md +3 -1
- package/skills/frontend-plan/SKILL.md +8 -6
- package/skills/frontend-plan/references/decision-contract.md +3 -3
- package/skills/frontend-review/SKILL.md +18 -28
- package/skills/frontend-review/references/review-findings.md +8 -3
- package/dist/commands/new-task.js +0 -5
- package/dist/commands/status.js +0 -57
- package/dist/commands/task-contract.js +0 -269
- package/dist/shared/prompts.js +0 -26
- package/dist/task/config.js +0 -2
- package/dist/task/contract/validate-draft.js +0 -106
- package/dist/task/goal.js +0 -3
- package/dist/task/index.js +0 -12
- package/dist/task/lifecycle.js +0 -1
- package/dist/task/operator/capabilities.js +0 -6
- package/dist/task/operator/envelope.js +0 -2
- package/dist/task/operator/index.js +0 -5
- package/dist/task/operator/registry.js +0 -2
- package/dist/task/operator/types.js +0 -1
- package/dist/task/paths.js +0 -1
- package/dist/task/source-state.js +0 -1
- package/dist/task/subagent-guidance.js +0 -1
- package/dist/worker/console/chat/sift-bridge.js +0 -1
- package/dist/worker/console/index.js +0 -20
- package/dist/worker/console/static/assets/channel-JM-I_b0s.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-JCYQIIEL-DuQQAYEg.js +0 -1
- package/dist/worker/console/static/assets/classDiagram-v2-OCEON4UE-DuQQAYEg.js +0 -1
- package/dist/worker/console/static/assets/index-B5H-giS2.js +0 -468
- package/dist/worker/console/static/assets/index-Bf7dxfm-.css +0 -1
- package/dist/worker/console/static/assets/stateDiagram-v2-UEYNNEHI-img0hVJ4.js +0 -1
- package/dist/worker/console/static/assets/swimlanesDiagram-ULZ7WXOC-B4acZJvk.js +0 -8
- package/dist/worker/scheduler/index.js +0 -25
- package/dist/workflows/dag/facts.js +0 -4
- package/dist/workflows/dag/frontend-shadow-dual-write.js +0 -975
- package/dist/workflows/dag/index.js +0 -6
- package/dist/workflows/dynamic/index.js +0 -7
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState, } from "react";
|
|
2
2
|
import { buildTurnProcessGroups, hydrateSessionThread, selectRecentTurnGroups, } from "../../chat/turn-process.js";
|
|
3
3
|
import { isToolFailure } from "../../chat/tool-preview.js";
|
|
4
|
+
import { compareChatOrder } from "../../chat/turn-order.js";
|
|
4
5
|
import { mergeOperationCardState, } from "../../chat/operation-card.js";
|
|
5
6
|
import { createChatSseEventApplier, } from "./chat-sse-events.js";
|
|
6
7
|
import { mergePendingSubmissionOnHydration } from "./pending-user-message.js";
|
|
@@ -130,7 +131,7 @@ export function useChatThread(params) {
|
|
|
130
131
|
const [historyHasMore, setHistoryHasMore] = useState(false);
|
|
131
132
|
const [historyNextCursor, setHistoryNextCursor] = useState(null);
|
|
132
133
|
const [historyLoading, setHistoryLoading] = useState(false);
|
|
133
|
-
const historyLoadingRef = useRef(
|
|
134
|
+
const historyLoadingRef = useRef(null);
|
|
134
135
|
const [unread, setUnread] = useState(0);
|
|
135
136
|
const [humanGateCards, setHumanGateCards] = useState([]);
|
|
136
137
|
const [interview, setInterview] = useState(null);
|
|
@@ -150,7 +151,6 @@ export function useChatThread(params) {
|
|
|
150
151
|
const [streamingAssistantId, setStreamingAssistantId] = useState(null);
|
|
151
152
|
const [showSystemInMessages] = useState(false);
|
|
152
153
|
const completedGroupsRef = useRef([]);
|
|
153
|
-
const previousTimelineRef = useRef(null);
|
|
154
154
|
const refreshSessionTelemetry = useCallback(async () => {
|
|
155
155
|
const targetSessionId = refs.sessionRef.current?.sessionId;
|
|
156
156
|
if (!targetSessionId)
|
|
@@ -317,42 +317,17 @@ export function useChatThread(params) {
|
|
|
317
317
|
};
|
|
318
318
|
}, [toolCalls]);
|
|
319
319
|
const turnGroups = useMemo(() => {
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
:
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
tailStart -= 1;
|
|
332
|
-
const tailMessages = timelineMessages.slice(tailStart);
|
|
333
|
-
const tailIds = new Set(tailMessages.map((message) => message.id));
|
|
334
|
-
const tailTurnIds = new Set(tailMessages.map((message) => message.turnId).filter(Boolean));
|
|
335
|
-
const tailTools = toolCalls.filter((tool) => tailIds.has(tool.assistantMessageId ?? "") ||
|
|
336
|
-
(Boolean(tool.turnId) && tailTurnIds.has(tool.turnId)) ||
|
|
337
|
-
(tool.createdAt >= (tailMessages[0]?.createdAt ?? Number.POSITIVE_INFINITY)));
|
|
338
|
-
const activeTail = buildTurnProcessGroups({
|
|
339
|
-
messages: tailMessages,
|
|
340
|
-
toolCalls: tailTools,
|
|
341
|
-
streaming,
|
|
342
|
-
activeAssistantId: streamingAssistantId,
|
|
343
|
-
});
|
|
344
|
-
built = [...previousTimeline.groups.filter((group) => !group.live), ...activeTail];
|
|
345
|
-
}
|
|
346
|
-
else {
|
|
347
|
-
built = buildTurnProcessGroups({
|
|
348
|
-
messages: timelineMessages,
|
|
349
|
-
toolCalls,
|
|
350
|
-
streaming,
|
|
351
|
-
activeAssistantId: streamingAssistantId,
|
|
352
|
-
});
|
|
353
|
-
}
|
|
354
|
-
// Preserve completed group objects across streaming tokens. The active
|
|
355
|
-
// tail is rebuilt, while stable historical groups keep their references so
|
|
320
|
+
// Server confirmation can change ordering without changing message ids.
|
|
321
|
+
// Rebuild membership from canonical messages; cache only finished row
|
|
322
|
+
// objects below so rich historical content still avoids re-rendering.
|
|
323
|
+
const built = buildTurnProcessGroups({
|
|
324
|
+
messages: timelineMessages,
|
|
325
|
+
toolCalls,
|
|
326
|
+
streaming,
|
|
327
|
+
activeAssistantId: streamingAssistantId,
|
|
328
|
+
});
|
|
329
|
+
// Preserve completed group objects across streaming tokens. Stable
|
|
330
|
+
// historical groups keep their references so
|
|
356
331
|
// memoized rows do not re-render or re-parse the full conversation.
|
|
357
332
|
const previous = new Map(completedGroupsRef.current.map((group) => [group.key, group]));
|
|
358
333
|
const all = built.map((group) => {
|
|
@@ -362,12 +337,6 @@ export function useChatThread(params) {
|
|
|
362
337
|
: group;
|
|
363
338
|
});
|
|
364
339
|
completedGroupsRef.current = all.filter((group) => !group.live);
|
|
365
|
-
previousTimelineRef.current = {
|
|
366
|
-
messages: timelineMessages,
|
|
367
|
-
toolCalls,
|
|
368
|
-
activeAssistantId: streamingAssistantId,
|
|
369
|
-
groups: all,
|
|
370
|
-
};
|
|
371
340
|
return selectRecentTurnGroups(all, historyLimit);
|
|
372
341
|
}, [
|
|
373
342
|
timelineMessages,
|
|
@@ -376,12 +345,14 @@ export function useChatThread(params) {
|
|
|
376
345
|
streamingAssistantId,
|
|
377
346
|
historyLimit,
|
|
378
347
|
]);
|
|
379
|
-
const loadEarlierMessages = useCallback(async () => {
|
|
348
|
+
const loadEarlierMessages = useCallback(async (beforePrepend) => {
|
|
380
349
|
const targetSessionId = refs.sessionRef.current?.sessionId;
|
|
381
350
|
const beforeId = historyNextCursor;
|
|
382
351
|
if (!targetSessionId || !historyHasMore || !beforeId || historyLoadingRef.current)
|
|
383
352
|
return false;
|
|
384
|
-
|
|
353
|
+
const generation = refs.sessionGenerationRef.current;
|
|
354
|
+
const request = Symbol();
|
|
355
|
+
historyLoadingRef.current = request;
|
|
385
356
|
setHistoryLoading(true);
|
|
386
357
|
try {
|
|
387
358
|
const query = new URLSearchParams({ limit: "50", beforeId });
|
|
@@ -389,7 +360,12 @@ export function useChatThread(params) {
|
|
|
389
360
|
if (!res.ok || refs.sessionRef.current?.sessionId !== targetSessionId)
|
|
390
361
|
return false;
|
|
391
362
|
const body = (await res.json());
|
|
363
|
+
if (refs.sessionRef.current?.sessionId !== targetSessionId ||
|
|
364
|
+
refs.sessionGenerationRef.current !== generation ||
|
|
365
|
+
historyLoadingRef.current !== request)
|
|
366
|
+
return false;
|
|
392
367
|
const page = hydrateSessionThread(body.messages ?? []);
|
|
368
|
+
beforePrepend?.();
|
|
393
369
|
setMessages((current) => {
|
|
394
370
|
const seen = new Set();
|
|
395
371
|
const merged = [...page.messages, ...current].filter((message) => {
|
|
@@ -398,11 +374,7 @@ export function useChatThread(params) {
|
|
|
398
374
|
seen.add(message.id);
|
|
399
375
|
return true;
|
|
400
376
|
});
|
|
401
|
-
return merged.sort(
|
|
402
|
-
if (a.eventSeq !== undefined && b.eventSeq !== undefined && a.eventSeq !== b.eventSeq)
|
|
403
|
-
return a.eventSeq - b.eventSeq;
|
|
404
|
-
return a.createdAt - b.createdAt;
|
|
405
|
-
});
|
|
377
|
+
return merged.sort(compareChatOrder);
|
|
406
378
|
});
|
|
407
379
|
setToolCalls((current) => {
|
|
408
380
|
const byId = new Map(current.map((tool) => [tool.id, tool]));
|
|
@@ -415,11 +387,19 @@ export function useChatThread(params) {
|
|
|
415
387
|
setHistoryLimit((value) => value + 50);
|
|
416
388
|
return true;
|
|
417
389
|
}
|
|
390
|
+
catch (error) {
|
|
391
|
+
if (refs.sessionRef.current?.sessionId === targetSessionId && refs.sessionGenerationRef.current === generation) {
|
|
392
|
+
setError(`加载历史消息失败:${error instanceof Error ? error.message : String(error)}`);
|
|
393
|
+
}
|
|
394
|
+
return false;
|
|
395
|
+
}
|
|
418
396
|
finally {
|
|
419
|
-
historyLoadingRef.current
|
|
420
|
-
|
|
397
|
+
if (historyLoadingRef.current === request) {
|
|
398
|
+
historyLoadingRef.current = null;
|
|
399
|
+
setHistoryLoading(false);
|
|
400
|
+
}
|
|
421
401
|
}
|
|
422
|
-
}, [historyHasMore, historyNextCursor, origin, refs]);
|
|
402
|
+
}, [historyHasMore, historyNextCursor, origin, refs, setError]);
|
|
423
403
|
const visibleMessages = useMemo(() => {
|
|
424
404
|
const next = [];
|
|
425
405
|
for (const group of turnGroups) {
|
|
@@ -453,13 +433,15 @@ export function useChatThread(params) {
|
|
|
453
433
|
setHistoryLimit(50);
|
|
454
434
|
setHistoryHasMore(false);
|
|
455
435
|
setHistoryNextCursor(null);
|
|
456
|
-
historyLoadingRef.current =
|
|
436
|
+
historyLoadingRef.current = null;
|
|
437
|
+
setHistoryLoading(false);
|
|
457
438
|
completedGroupsRef.current = [];
|
|
458
|
-
previousTimelineRef.current = null;
|
|
459
439
|
refs.usageResponseKeysRef.current = new Set();
|
|
460
440
|
}, [refs]);
|
|
461
441
|
/** Adopt a freshly activated session's hydrated thread + streaming flags. */
|
|
462
442
|
const applyActivatedSession = useCallback((next) => {
|
|
443
|
+
historyLoadingRef.current = null;
|
|
444
|
+
setHistoryLoading(false);
|
|
463
445
|
const visuallyStopped = next.turnPhase === "stopping";
|
|
464
446
|
// A submission still owned for THIS session keeps its optimistic bubble
|
|
465
447
|
// across an activation snapshot (refresh / reopen); a lease owned by
|
|
@@ -511,7 +493,6 @@ export function useChatThread(params) {
|
|
|
511
493
|
setHistoryHasMore(next.history?.hasMore === true);
|
|
512
494
|
setHistoryNextCursor(next.history?.nextCursor ?? null);
|
|
513
495
|
completedGroupsRef.current = [];
|
|
514
|
-
previousTimelineRef.current = null;
|
|
515
496
|
}, [refs]);
|
|
516
497
|
/** Reconcile recovery (AC-001/AC-002/AC-003): reload the canonical session
|
|
517
498
|
* facts (session-store messages + event-ring tail cursor) and REPLACE the
|
|
@@ -1,31 +1,70 @@
|
|
|
1
|
-
import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState, } from "react";
|
|
1
|
+
import { useCallback, useEffect, useLayoutEffect, useMemo, useReducer, useRef, useState, } from "react";
|
|
2
2
|
import { filterSlashCommands, mergeSlashCommands, } from "../../chat/shortcuts.js";
|
|
3
|
-
import {
|
|
3
|
+
import { ComposerSessionLedger, composerSessionKey, } from "./composer-session-ledger.js";
|
|
4
|
+
import { confirmationToken, requestId } from "./format.js";
|
|
4
5
|
import { clampShortcutIndex } from "./slash-palette-nav.js";
|
|
5
|
-
import { createInputHistoryState, navigateInputHistory, recordInputHistoryEntry, resetInputHistoryBrowsing, } from "./input-history.js";
|
|
6
6
|
import { attachRuntimeSnapshotController } from "./useRuntimeSnapshot.js";
|
|
7
|
+
function sameComposerSession(left, right) {
|
|
8
|
+
if (!left || !right)
|
|
9
|
+
return left === right;
|
|
10
|
+
return composerSessionKey(left) === composerSessionKey(right);
|
|
11
|
+
}
|
|
12
|
+
function readPendingImage(file) {
|
|
13
|
+
return new Promise((resolve, reject) => {
|
|
14
|
+
const reader = new FileReader();
|
|
15
|
+
reader.onerror = () => reject(reader.error ?? new Error("图片读取失败"));
|
|
16
|
+
reader.onload = () => {
|
|
17
|
+
const value = String(reader.result ?? "");
|
|
18
|
+
resolve({
|
|
19
|
+
name: file.name,
|
|
20
|
+
type: "image",
|
|
21
|
+
mimeType: file.type,
|
|
22
|
+
data: value.slice(value.indexOf(",") + 1),
|
|
23
|
+
});
|
|
24
|
+
};
|
|
25
|
+
reader.readAsDataURL(file);
|
|
26
|
+
});
|
|
27
|
+
}
|
|
7
28
|
/** Composer state: input text, @file palette, image attachments, /shortcuts,
|
|
8
29
|
* IME composition guards, auto-grow and server-side draft sync. */
|
|
9
30
|
export function useComposer(params) {
|
|
10
|
-
const { origin,
|
|
11
|
-
const
|
|
31
|
+
const { origin, sessionIdentity, sessionMode, sessionReady = true, refs, setError, onUnrecoverableSession, } = params;
|
|
32
|
+
const sessionId = sessionIdentity?.sessionId;
|
|
33
|
+
const ledgerRef = useRef(null);
|
|
34
|
+
if (!ledgerRef.current)
|
|
35
|
+
ledgerRef.current = new ComposerSessionLedger();
|
|
36
|
+
const ledger = ledgerRef.current;
|
|
37
|
+
const activeIdentityRef = useRef(sessionIdentity);
|
|
38
|
+
activeIdentityRef.current = sessionIdentity;
|
|
39
|
+
const [, forceRender] = useReducer((version) => version + 1, 0);
|
|
40
|
+
const activeSnapshot = sessionIdentity
|
|
41
|
+
? ledger.snapshot(sessionIdentity)
|
|
42
|
+
: { draft: "", images: [] };
|
|
43
|
+
const input = activeSnapshot.draft;
|
|
44
|
+
const pendingImages = activeSnapshot.images;
|
|
12
45
|
const [fileOptions, setFileOptions] = useState([]);
|
|
13
|
-
const [pendingImages, setPendingImages] = useState([]);
|
|
14
46
|
const [shortcutOpen, setShortcutOpen] = useState(false);
|
|
15
47
|
/** Any composer text that starts with `/` should reveal the palette,
|
|
16
48
|
* including draft restore. Escape can still close it without clearing. */
|
|
17
49
|
const setInput = useCallback((value) => {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
const next = value(prev);
|
|
21
|
-
setShortcutOpen(next.startsWith("/"));
|
|
22
|
-
return next;
|
|
23
|
-
});
|
|
50
|
+
const identity = activeIdentityRef.current;
|
|
51
|
+
if (!identity)
|
|
24
52
|
return;
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
53
|
+
const previous = ledger.snapshot(identity).draft;
|
|
54
|
+
const next = typeof value === "function" ? value(previous) : value;
|
|
55
|
+
if (ledger.setDraft(identity, next))
|
|
56
|
+
forceRender();
|
|
57
|
+
setShortcutOpen(next.startsWith("/"));
|
|
58
|
+
}, [ledger]);
|
|
59
|
+
const setPendingImages = useCallback((value) => {
|
|
60
|
+
const identity = activeIdentityRef.current;
|
|
61
|
+
if (!identity)
|
|
62
|
+
return;
|
|
63
|
+
const previous = ledger.snapshot(identity).images;
|
|
64
|
+
const next = typeof value === "function" ? value(previous) : value;
|
|
65
|
+
ledger.setImages(identity, next);
|
|
66
|
+
forceRender();
|
|
67
|
+
}, [ledger]);
|
|
29
68
|
const [shortcutIndex, setShortcutIndex] = useState(0);
|
|
30
69
|
const [dynamicCommands, setDynamicCommands] = useState([]);
|
|
31
70
|
const [commandCapabilities, setCommandCapabilities] = useState({
|
|
@@ -36,30 +75,29 @@ export function useComposer(params) {
|
|
|
36
75
|
const [dynamicCommandsError, setDynamicCommandsError] = useState(null);
|
|
37
76
|
const [commandsEpoch, setCommandsEpoch] = useState(0);
|
|
38
77
|
const textareaRef = useRef(null);
|
|
39
|
-
/** Arrow-key input history; unsent drafts survive history browsing. */
|
|
40
|
-
const historyRef = useRef(createInputHistoryState());
|
|
41
78
|
/** Active session generation for /commands; bumped immediately on switch (AC-2). */
|
|
42
79
|
const commandsSessionRef = useRef(sessionId);
|
|
43
80
|
const commandsGenerationRef = useRef(0);
|
|
44
81
|
const handleInputChange = useCallback((value) => {
|
|
45
82
|
setInput(value);
|
|
46
|
-
setShortcutOpen(value.startsWith("/"));
|
|
47
83
|
setShortcutIndex(0);
|
|
48
|
-
|
|
49
|
-
historyRef.current = resetInputHistoryBrowsing(historyRef.current);
|
|
50
|
-
}, []);
|
|
84
|
+
}, [setInput]);
|
|
51
85
|
/** Recall an earlier input via ↑/↓; returns true when the key was consumed. */
|
|
52
86
|
const navigateHistory = useCallback((direction) => {
|
|
87
|
+
const identity = activeIdentityRef.current;
|
|
88
|
+
if (!identity)
|
|
89
|
+
return false;
|
|
90
|
+
const current = ledger.snapshot(identity).draft;
|
|
53
91
|
const textarea = textareaRef.current;
|
|
54
|
-
const decision =
|
|
55
|
-
text:
|
|
56
|
-
caretStart: textarea?.selectionStart ??
|
|
57
|
-
caretEnd: textarea?.selectionEnd ??
|
|
92
|
+
const decision = ledger.navigateHistory(identity, direction, {
|
|
93
|
+
text: current,
|
|
94
|
+
caretStart: textarea?.selectionStart ?? current.length,
|
|
95
|
+
caretEnd: textarea?.selectionEnd ?? current.length,
|
|
58
96
|
});
|
|
59
97
|
if (!decision.handled)
|
|
60
98
|
return false;
|
|
61
|
-
|
|
62
|
-
|
|
99
|
+
forceRender();
|
|
100
|
+
setShortcutOpen(decision.text.startsWith("/"));
|
|
63
101
|
const caret = decision.text.length;
|
|
64
102
|
queueMicrotask(() => {
|
|
65
103
|
const el = textareaRef.current;
|
|
@@ -68,22 +106,76 @@ export function useComposer(params) {
|
|
|
68
106
|
}
|
|
69
107
|
});
|
|
70
108
|
return true;
|
|
71
|
-
}, [
|
|
72
|
-
/** Record a submitted text into
|
|
109
|
+
}, [ledger]);
|
|
110
|
+
/** Record a submitted text into the selected session's history. */
|
|
73
111
|
const recordHistory = useCallback((text) => {
|
|
74
|
-
|
|
75
|
-
|
|
112
|
+
const identity = activeIdentityRef.current;
|
|
113
|
+
if (identity)
|
|
114
|
+
ledger.recordHistory(identity, text);
|
|
115
|
+
}, [ledger]);
|
|
116
|
+
const claimSubmission = useCallback(() => {
|
|
117
|
+
const identity = activeIdentityRef.current;
|
|
118
|
+
if (!identity)
|
|
119
|
+
return null;
|
|
120
|
+
const attempt = ledger.beginSubmission(identity, refs.sessionGenerationRef.current, requestId("chat-submit"));
|
|
121
|
+
if (!attempt)
|
|
122
|
+
return null;
|
|
123
|
+
forceRender();
|
|
124
|
+
setShortcutOpen(false);
|
|
125
|
+
setShortcutIndex(0);
|
|
126
|
+
return attempt;
|
|
127
|
+
}, [ledger, refs]);
|
|
128
|
+
const claimSessionSubmission = useCallback((identity, text, images = []) => {
|
|
129
|
+
ledger.setDraft(identity, text);
|
|
130
|
+
ledger.setImages(identity, images);
|
|
131
|
+
const attempt = ledger.beginSubmission(identity, refs.sessionGenerationRef.current, requestId("chat-submit"));
|
|
132
|
+
if (sameComposerSession(activeIdentityRef.current, identity))
|
|
133
|
+
forceRender();
|
|
134
|
+
return attempt;
|
|
135
|
+
}, [ledger, refs]);
|
|
136
|
+
const settleSubmission = useCallback((attempt, outcome) => {
|
|
137
|
+
const result = ledger.settle(attempt, outcome);
|
|
138
|
+
if (sameComposerSession(activeIdentityRef.current, attempt.identity))
|
|
139
|
+
forceRender();
|
|
140
|
+
return result;
|
|
141
|
+
}, [ledger]);
|
|
142
|
+
const settleSubmissionByRequest = useCallback((identity, clientRequestId, outcome) => {
|
|
143
|
+
const result = ledger.settleByRequest(identity, clientRequestId, outcome);
|
|
144
|
+
if (sameComposerSession(activeIdentityRef.current, identity))
|
|
145
|
+
forceRender();
|
|
146
|
+
return result;
|
|
147
|
+
}, [ledger]);
|
|
148
|
+
const seedSession = useCallback((identity, draft) => {
|
|
149
|
+
const seeded = ledger.seedDraft(identity, draft);
|
|
150
|
+
if (seeded && sameComposerSession(activeIdentityRef.current, identity))
|
|
151
|
+
forceRender();
|
|
152
|
+
return seeded;
|
|
153
|
+
}, [ledger]);
|
|
154
|
+
const replaceSessionDraft = useCallback((identity, draft, images) => {
|
|
155
|
+
ledger.setDraft(identity, draft);
|
|
156
|
+
ledger.setImages(identity, images);
|
|
157
|
+
if (sameComposerSession(activeIdentityRef.current, identity))
|
|
158
|
+
forceRender();
|
|
159
|
+
}, [ledger]);
|
|
160
|
+
const releaseSession = useCallback((identity) => {
|
|
161
|
+
ledger.release(identity);
|
|
162
|
+
if (sameComposerSession(activeIdentityRef.current, identity))
|
|
163
|
+
forceRender();
|
|
164
|
+
}, [ledger]);
|
|
76
165
|
// Immediate invalidation on A→B even when B palette never opens (AC-2).
|
|
77
166
|
useLayoutEffect(() => {
|
|
78
167
|
commandsSessionRef.current = sessionId;
|
|
79
168
|
commandsGenerationRef.current += 1;
|
|
169
|
+
refs.isComposingRef.current = false;
|
|
170
|
+
setFileOptions([]);
|
|
171
|
+
setShortcutOpen(input.startsWith("/"));
|
|
80
172
|
setDynamicCommands([]);
|
|
81
173
|
setCommandCapabilities({ sddBingo: false, sessionMode });
|
|
82
174
|
setDynamicCommandsError(null);
|
|
83
175
|
setDynamicCommandsLoading(false);
|
|
84
176
|
}, [sessionId, sessionMode]);
|
|
85
177
|
const loadDynamicCommands = useCallback(async () => {
|
|
86
|
-
if (!sessionId) {
|
|
178
|
+
if (!sessionId || !sessionReady) {
|
|
87
179
|
setDynamicCommands([]);
|
|
88
180
|
setDynamicCommandsError(null);
|
|
89
181
|
setDynamicCommandsLoading(false);
|
|
@@ -147,21 +239,21 @@ export function useComposer(params) {
|
|
|
147
239
|
setDynamicCommandsLoading(false);
|
|
148
240
|
}
|
|
149
241
|
}
|
|
150
|
-
}, [origin, sessionId, sessionMode]);
|
|
242
|
+
}, [origin, sessionId, sessionMode, sessionReady]);
|
|
151
243
|
// Lazy-load dynamic commands when slash palette opens or session changes.
|
|
152
244
|
useEffect(() => {
|
|
153
|
-
if (!sessionId)
|
|
245
|
+
if (!sessionId || !sessionReady)
|
|
154
246
|
return;
|
|
155
247
|
if (!shortcutOpen && commandsEpoch === 0)
|
|
156
248
|
return;
|
|
157
249
|
void loadDynamicCommands();
|
|
158
|
-
}, [sessionId, shortcutOpen, commandsEpoch, loadDynamicCommands]);
|
|
250
|
+
}, [sessionId, sessionReady, shortcutOpen, commandsEpoch, loadDynamicCommands]);
|
|
159
251
|
const retryDynamicCommands = useCallback(() => {
|
|
160
252
|
setCommandsEpoch((n) => n + 1);
|
|
161
253
|
void loadDynamicCommands();
|
|
162
254
|
}, [loadDynamicCommands]);
|
|
163
255
|
useEffect(() => {
|
|
164
|
-
if (!sessionId)
|
|
256
|
+
if (!sessionId || !sessionIdentity || !sessionReady)
|
|
165
257
|
return;
|
|
166
258
|
const match = input
|
|
167
259
|
.slice(0, textareaRef.current?.selectionStart ?? input.length)
|
|
@@ -170,13 +262,29 @@ export function useComposer(params) {
|
|
|
170
262
|
setFileOptions([]);
|
|
171
263
|
return;
|
|
172
264
|
}
|
|
265
|
+
const owner = { ...sessionIdentity };
|
|
266
|
+
const ownerKey = composerSessionKey(owner);
|
|
267
|
+
const generation = refs.sessionGenerationRef.current;
|
|
268
|
+
const controller = new AbortController();
|
|
173
269
|
const timer = window.setTimeout(() => {
|
|
174
|
-
void fetch(`${origin}/api/operator/v1/chat/sessions/${encodeURIComponent(sessionId)}/files?q=${encodeURIComponent(match[1] ?? "")}
|
|
270
|
+
void fetch(`${origin}/api/operator/v1/chat/sessions/${encodeURIComponent(sessionId)}/files?q=${encodeURIComponent(match[1] ?? "")}`, { signal: controller.signal })
|
|
175
271
|
.then((r) => (r.ok ? r.json() : null))
|
|
176
|
-
.then((body) =>
|
|
272
|
+
.then((body) => {
|
|
273
|
+
const active = activeIdentityRef.current;
|
|
274
|
+
if (controller.signal.aborted ||
|
|
275
|
+
!active ||
|
|
276
|
+
composerSessionKey(active) !== ownerKey ||
|
|
277
|
+
refs.sessionGenerationRef.current !== generation)
|
|
278
|
+
return;
|
|
279
|
+
setFileOptions((body?.files ?? []).map((item) => item.path));
|
|
280
|
+
})
|
|
281
|
+
.catch(() => undefined);
|
|
177
282
|
}, 150);
|
|
178
|
-
return () =>
|
|
179
|
-
|
|
283
|
+
return () => {
|
|
284
|
+
window.clearTimeout(timer);
|
|
285
|
+
controller.abort();
|
|
286
|
+
};
|
|
287
|
+
}, [origin, sessionId, sessionIdentity, sessionReady, input, refs]);
|
|
180
288
|
const insertFileReference = useCallback((file) => {
|
|
181
289
|
const caret = textareaRef.current?.selectionStart ?? input.length;
|
|
182
290
|
const before = input.slice(0, caret);
|
|
@@ -185,44 +293,51 @@ export function useComposer(params) {
|
|
|
185
293
|
return;
|
|
186
294
|
setInput(`${input.slice(0, start)}@${file} ${input.slice(caret)}`);
|
|
187
295
|
setFileOptions([]);
|
|
188
|
-
}, [input]);
|
|
296
|
+
}, [input, setInput]);
|
|
189
297
|
const addImagesFromFiles = useCallback((files) => {
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
}
|
|
211
|
-
}, [setError]);
|
|
298
|
+
const owner = activeIdentityRef.current;
|
|
299
|
+
if (!owner)
|
|
300
|
+
return;
|
|
301
|
+
const ownerIdentity = { ...owner };
|
|
302
|
+
const selected = Array.from(files).slice(0, 4);
|
|
303
|
+
const valid = selected.filter((file) => file.type.startsWith("image/") && file.size <= 5 * 1024 * 1024);
|
|
304
|
+
if (valid.length !== selected.length)
|
|
305
|
+
setError("图片仅支持 PNG/JPEG/GIF/WebP,单张不超过 5 MiB");
|
|
306
|
+
void Promise.all(valid.map(readPendingImage)).then((loaded) => {
|
|
307
|
+
if (!ledger.has(ownerIdentity) || loaded.length === 0)
|
|
308
|
+
return;
|
|
309
|
+
const current = ledger.snapshot(ownerIdentity).images;
|
|
310
|
+
ledger.setImages(ownerIdentity, [...current, ...loaded].slice(0, 4));
|
|
311
|
+
if (sameComposerSession(activeIdentityRef.current, ownerIdentity))
|
|
312
|
+
forceRender();
|
|
313
|
+
}, (error) => {
|
|
314
|
+
if (sameComposerSession(activeIdentityRef.current, ownerIdentity))
|
|
315
|
+
setError(error instanceof Error ? error.message : "图片读取失败");
|
|
316
|
+
});
|
|
317
|
+
}, [ledger, setError]);
|
|
212
318
|
const removeImage = useCallback((image) => {
|
|
213
319
|
setPendingImages((items) => items.filter((item) => item !== image));
|
|
214
|
-
}, []);
|
|
215
|
-
//
|
|
216
|
-
|
|
320
|
+
}, [setPendingImages]);
|
|
321
|
+
// Chromium handles growth in CSS without forcing a layout read on every key.
|
|
322
|
+
// Keep a deferred fallback for browsers that do not support field-sizing.
|
|
323
|
+
useEffect(() => {
|
|
217
324
|
const ta = textareaRef.current;
|
|
218
325
|
if (!ta)
|
|
219
326
|
return;
|
|
220
|
-
|
|
221
|
-
|
|
327
|
+
if (typeof CSS !== "undefined" &&
|
|
328
|
+
CSS.supports("field-sizing", "content")) {
|
|
329
|
+
ta.style.removeProperty("height");
|
|
330
|
+
return;
|
|
331
|
+
}
|
|
332
|
+
const frame = window.requestAnimationFrame(() => {
|
|
333
|
+
ta.style.height = "auto";
|
|
334
|
+
ta.style.height = `${Math.min(ta.scrollHeight, 280)}px`;
|
|
335
|
+
});
|
|
336
|
+
return () => window.cancelAnimationFrame(frame);
|
|
222
337
|
}, [input]);
|
|
223
338
|
// Debounced server-side composer draft sync.
|
|
224
339
|
useEffect(() => {
|
|
225
|
-
if (!sessionId)
|
|
340
|
+
if (!sessionId || !sessionReady)
|
|
226
341
|
return;
|
|
227
342
|
const timer = window.setTimeout(() => {
|
|
228
343
|
void fetch(`${origin}/api/operator/v1/chat/sessions/${encodeURIComponent(sessionId)}/draft`, {
|
|
@@ -236,7 +351,7 @@ export function useComposer(params) {
|
|
|
236
351
|
});
|
|
237
352
|
}, 400);
|
|
238
353
|
return () => window.clearTimeout(timer);
|
|
239
|
-
}, [origin, sessionId, input]);
|
|
354
|
+
}, [origin, sessionId, sessionReady, input]);
|
|
240
355
|
const mergedCommands = useMemo(() => mergeSlashCommands(dynamicCommands, commandCapabilities), [dynamicCommands, commandCapabilities]);
|
|
241
356
|
const filteredShortcuts = useMemo(() => input.startsWith("/")
|
|
242
357
|
? filterSlashCommands(mergedCommands, input)
|
|
@@ -270,17 +385,24 @@ export function useComposer(params) {
|
|
|
270
385
|
useEffect(() => {
|
|
271
386
|
attachRuntimeSnapshotController({
|
|
272
387
|
origin,
|
|
273
|
-
sessionId,
|
|
388
|
+
sessionId: sessionReady ? sessionId : undefined,
|
|
274
389
|
refs,
|
|
275
390
|
onUnrecoverableSession,
|
|
276
391
|
});
|
|
277
|
-
}, [origin, sessionId, refs, onUnrecoverableSession]);
|
|
392
|
+
}, [origin, sessionId, sessionReady, refs, onUnrecoverableSession]);
|
|
278
393
|
return {
|
|
279
394
|
input,
|
|
280
395
|
setInput,
|
|
281
396
|
handleInputChange,
|
|
282
397
|
navigateHistory,
|
|
283
398
|
recordHistory,
|
|
399
|
+
claimSubmission,
|
|
400
|
+
claimSessionSubmission,
|
|
401
|
+
settleSubmission,
|
|
402
|
+
settleSubmissionByRequest,
|
|
403
|
+
seedSession,
|
|
404
|
+
replaceSessionDraft,
|
|
405
|
+
releaseSession,
|
|
284
406
|
fileOptions,
|
|
285
407
|
pendingImages,
|
|
286
408
|
setPendingImages,
|
|
@@ -149,6 +149,8 @@ export function useRepoBrowser(params) {
|
|
|
149
149
|
return;
|
|
150
150
|
const initial = rootFiles.find((entry) => /^readme(?:\.[^.]+)?$/i.test(entry.name)) ??
|
|
151
151
|
rootFiles[0];
|
|
152
|
+
if (!initial)
|
|
153
|
+
return;
|
|
152
154
|
autoPreviewedWorkspaceRef.current.add(workspaceKey);
|
|
153
155
|
void openPreview(initial.path);
|
|
154
156
|
}, [
|
|
@@ -181,10 +181,12 @@ export function composePrdDocuments(docs, options) {
|
|
|
181
181
|
if (docs.length === 0)
|
|
182
182
|
return "";
|
|
183
183
|
if (docs.length === 1) {
|
|
184
|
-
return docs[0]
|
|
184
|
+
return docs[0]?.text ?? "";
|
|
185
185
|
}
|
|
186
186
|
const primaryIndex = Math.min(Math.max(0, options?.primaryIndex ?? 0), docs.length - 1);
|
|
187
187
|
const primary = docs[primaryIndex];
|
|
188
|
+
if (!primary)
|
|
189
|
+
return docs[0]?.text ?? "";
|
|
188
190
|
const title = extractComposeTitle(primary.text, primary.fileName);
|
|
189
191
|
const names = docs.map((d) => d.fileName).join(", ");
|
|
190
192
|
const parts = [
|