@cloudcli-ai/cloudcli 1.37.2 → 1.37.4
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/README.md +269 -0
- package/dist/assets/{abnfDiagram-N423BO3Z-C4UAoHJb.js → abnfDiagram-N423BO3Z-Dmkwx38H.js} +1 -1
- package/dist/assets/{arc-B8jF-ja9.js → arc-DLe9AYuO.js} +1 -1
- package/dist/assets/{architectureDiagram-T3A2C74G-C_Wur_CU.js → architectureDiagram-T3A2C74G-CBbbbNUW.js} +1 -1
- package/dist/assets/{blockDiagram-VBNYF7ZC-Cq4P1i8t.js → blockDiagram-VBNYF7ZC-CaMEaWu4.js} +1 -1
- package/dist/assets/{c4Diagram-5PPSVZJV-C1wG4eUj.js → c4Diagram-5PPSVZJV-DhjMgNnR.js} +1 -1
- package/dist/assets/channel-CWrmwsir.js +1 -0
- package/dist/assets/{chunk-2GRJ4B5K-B8fnBG6E.js → chunk-2GRJ4B5K-yjl3-F-i.js} +1 -1
- package/dist/assets/{chunk-2Q5K7J3B-WdfPooPJ.js → chunk-2Q5K7J3B-DsAYaWEP.js} +1 -1
- package/dist/assets/{chunk-5RXB4S5H-DhZJuG2E.js → chunk-5RXB4S5H-Bt5qmqOX.js} +1 -1
- package/dist/assets/{chunk-5VM5RSS4-B0rjslal.js → chunk-5VM5RSS4-yWrWVvUt.js} +1 -1
- package/dist/assets/{chunk-6Q2QTUOP-CG2Oj4jL.js → chunk-6Q2QTUOP-Dt66K5xb.js} +1 -1
- package/dist/assets/{chunk-GF5L2VYU-rrl2lRxY.js → chunk-GF5L2VYU-DA0vzaj4.js} +1 -1
- package/dist/assets/{chunk-JWPE2WC7-CjVbrVMA.js → chunk-JWPE2WC7-CUkbx96V.js} +1 -1
- package/dist/assets/{chunk-KBJHAD2P-CRzKgU8S.js → chunk-KBJHAD2P-DWKxx1WH.js} +1 -1
- package/dist/assets/{chunk-RYQCIY6F-DBz4GTZF.js → chunk-RYQCIY6F-BqluplN0.js} +1 -1
- package/dist/assets/{chunk-XXDRQBXY-wHxalsXk.js → chunk-XXDRQBXY-DelKg3Xf.js} +1 -1
- package/dist/assets/classDiagram-JCYQIIEL-COAGCC6w.js +1 -0
- package/dist/assets/classDiagram-v2-OCEON4UE-COAGCC6w.js +1 -0
- package/dist/assets/{cose-bilkent-JH36ORCC-s1hygY8e.js → cose-bilkent-JH36ORCC-DHV93-Em.js} +1 -1
- package/dist/assets/{cynefin-VYW2F7L2-C4LGV15i.js → cynefin-VYW2F7L2-B9HZgoGX.js} +1 -1
- package/dist/assets/{cynefinDiagram-MW4NZA55-C5uhu7B3.js → cynefinDiagram-MW4NZA55-hFjjEQg8.js} +1 -1
- package/dist/assets/{dagre-VZM6K2ZE-B-OBTewm.js → dagre-VZM6K2ZE-CpeVOC7V.js} +1 -1
- package/dist/assets/{diagram-7IWD3JNH-BjGKQxih.js → diagram-7IWD3JNH-D0H1UoRA.js} +1 -1
- package/dist/assets/{diagram-B4RE2ZJO-BMKz1X2L.js → diagram-B4RE2ZJO-6Y7BQUup.js} +1 -1
- package/dist/assets/{diagram-LBJQPF4R-DNh-GhKC.js → diagram-LBJQPF4R-C0nNOwht.js} +1 -1
- package/dist/assets/{diagram-Q27KOJAE-BaTG9jPT.js → diagram-Q27KOJAE-En6QlOVX.js} +1 -1
- package/dist/assets/{diagram-UB23O5K3-BUwo2OvJ.js → diagram-UB23O5K3-CmPiNeGD.js} +1 -1
- package/dist/assets/{ebnfDiagram-BXEA7PRR-DAZgfzX2.js → ebnfDiagram-BXEA7PRR-Bygzp1Qt.js} +1 -1
- package/dist/assets/{erDiagram-JOGREHBK-1w1CcGEb.js → erDiagram-JOGREHBK-CwScRfzl.js} +1 -1
- package/dist/assets/{flowDiagram-UKHOOZJN-D4EII8Jj.js → flowDiagram-UKHOOZJN-bKX-0xP6.js} +1 -1
- package/dist/assets/{ganttDiagram-PKOTCBZU-ChvJZAgB.js → ganttDiagram-PKOTCBZU-DtZPKXGd.js} +1 -1
- package/dist/assets/{gitGraphDiagram-DS77QQ5N-DtOn41S8.js → gitGraphDiagram-DS77QQ5N-CUOtdL1o.js} +1 -1
- package/dist/assets/index-CdjIFxM2.js +5004 -0
- package/dist/assets/index-DgafhGTF.css +32 -0
- package/dist/assets/{infoDiagram-6WML65LV-DN_PVEJZ.js → infoDiagram-6WML65LV-ciRQTS0h.js} +1 -1
- package/dist/assets/{ishikawaDiagram-WSZJBQD7-DbkF1kXb.js → ishikawaDiagram-WSZJBQD7-CgjhQZ84.js} +1 -1
- package/dist/assets/{journeyDiagram-NVQOT4AX-pJwmhddE.js → journeyDiagram-NVQOT4AX-BsbQSeIW.js} +1 -1
- package/dist/assets/{kanban-definition-27J2QSJJ-PPAPgNp5.js → kanban-definition-27J2QSJJ-CrwzY9Jw.js} +1 -1
- package/dist/assets/{linear-iPR-J9f7.js → linear-BUaCDr6l.js} +1 -1
- package/dist/assets/{mermaid.core-BEUL86Z6.js → mermaid.core-ClgdUU0j.js} +5 -5
- package/dist/assets/{mindmap-definition-FAOFIHXS-BvZoBib7.js → mindmap-definition-FAOFIHXS-BictljY1.js} +1 -1
- package/dist/assets/{pegDiagram-VL7TDLO6-DhYreaUd.js → pegDiagram-VL7TDLO6-BXdByy_U.js} +1 -1
- package/dist/assets/{pieDiagram-7S7Q4E2Y-CrOHlyD0.js → pieDiagram-7S7Q4E2Y-uZsMfkRX.js} +1 -1
- package/dist/assets/{quadrantDiagram-CIZ2JOQS-D4g5yH3t.js → quadrantDiagram-CIZ2JOQS-7lCv4QIj.js} +1 -1
- package/dist/assets/{railroadDiagram-AXF67PYL-Dj39lHlf.js → railroadDiagram-AXF67PYL-07dxIWbi.js} +1 -1
- package/dist/assets/{requirementDiagram-LRYGKXZP-1AS4Zgm_.js → requirementDiagram-LRYGKXZP-Cb31Yv1_.js} +1 -1
- package/dist/assets/{sankeyDiagram-W5VNT64P-CKlMn19P.js → sankeyDiagram-W5VNT64P-X0YPSyF9.js} +1 -1
- package/dist/assets/{sequenceDiagram-SI44F4Z6-B_j3qyvo.js → sequenceDiagram-SI44F4Z6-BdBxSIlP.js} +1 -1
- package/dist/assets/server.browser-CtX3Hdl5.js +27 -0
- package/dist/assets/{sizeCapture-X5ZJPWSS-Bp2Op16v.js → sizeCapture-X5ZJPWSS-DQM5xAyD.js} +1 -1
- package/dist/assets/{stateDiagram-OKZ733FA-C0cN6DoY.js → stateDiagram-OKZ733FA-DJ3xkLUF.js} +1 -1
- package/dist/assets/stateDiagram-v2-UEYNNEHI-vAaE4zNo.js +1 -0
- package/dist/assets/{swimlanes-SLNWSIFB-BEvXOV9b.js → swimlanes-SLNWSIFB-rjfYdW0w.js} +2 -2
- package/dist/assets/swimlanesDiagram-ULZ7WXOC-TV2IF4Nq.js +8 -0
- package/dist/assets/{timeline-definition-Z64GVDOM-zdJQinvZ.js → timeline-definition-Z64GVDOM-DZMra5do.js} +1 -1
- package/dist/assets/{vendor-codemirror-OwyKSvPE.js → vendor-codemirror-CYrSt7-U.js} +18 -18
- package/dist/assets/{vennDiagram-T6HMQDX7-DuF1uZMY.js → vennDiagram-T6HMQDX7-BevD8uu0.js} +1 -1
- package/dist/assets/{wardleyDiagram-T6FBY63Y-B9AlLQup.js → wardleyDiagram-T6FBY63Y-DsIyXB7z.js} +1 -1
- package/dist/assets/{xychartDiagram-ELKLHX3M-DTJX48Qg.js → xychartDiagram-ELKLHX3M-G3PlF3ko.js} +1 -1
- package/dist/index.html +3 -3
- package/dist-server/server/index.js +11 -4
- package/dist-server/server/index.js.map +1 -1
- package/dist-server/server/modules/agent/agent.module.js +9 -2
- package/dist-server/server/modules/agent/agent.module.js.map +1 -1
- package/dist-server/server/modules/agent/agent.routes.js +177 -130
- package/dist-server/server/modules/agent/agent.routes.js.map +1 -1
- package/dist-server/server/modules/agent/tests/agent-run-registration.test.js +306 -0
- package/dist-server/server/modules/agent/tests/agent-run-registration.test.js.map +1 -0
- package/dist-server/server/modules/agent/tests/agent.routes.test.js +48 -2
- package/dist-server/server/modules/agent/tests/agent.routes.test.js.map +1 -1
- package/dist-server/server/modules/browser-use/browser-use.routes.js +39 -33
- package/dist-server/server/modules/browser-use/browser-use.routes.js.map +1 -1
- package/dist-server/server/modules/database/connection.js +1 -1
- package/dist-server/server/modules/database/connection.js.map +1 -1
- package/dist-server/server/modules/database/index.js +5 -0
- package/dist-server/server/modules/database/index.js.map +1 -1
- package/dist-server/server/modules/database/migrations.js +45 -1
- package/dist-server/server/modules/database/migrations.js.map +1 -1
- package/dist-server/server/modules/database/repositories/provider-models.js +60 -6
- package/dist-server/server/modules/database/repositories/provider-models.js.map +1 -1
- package/dist-server/server/modules/database/repositories/scheduled-messages.db.js +71 -0
- package/dist-server/server/modules/database/repositories/scheduled-messages.db.js.map +1 -0
- package/dist-server/server/modules/database/repositories/session-drafts.db.js +105 -0
- package/dist-server/server/modules/database/repositories/session-drafts.db.js.map +1 -0
- package/dist-server/server/modules/database/repositories/sessions.db.js +144 -5
- package/dist-server/server/modules/database/repositories/sessions.db.js.map +1 -1
- package/dist-server/server/modules/database/repositories/user-preferences.db.js +65 -0
- package/dist-server/server/modules/database/repositories/user-preferences.db.js.map +1 -0
- package/dist-server/server/modules/database/schema.js +99 -0
- package/dist-server/server/modules/database/schema.js.map +1 -1
- package/dist-server/server/modules/database/tests/provider-models.db.integration.test.js +99 -0
- package/dist-server/server/modules/database/tests/provider-models.db.integration.test.js.map +1 -1
- package/dist-server/server/modules/database/tests/sessions.db.integration.test.js +39 -0
- package/dist-server/server/modules/database/tests/sessions.db.integration.test.js.map +1 -1
- package/dist-server/server/modules/database/tests/user-preferences.db.integration.test.js +105 -0
- package/dist-server/server/modules/database/tests/user-preferences.db.integration.test.js.map +1 -0
- package/dist-server/server/modules/file-tree/file-tree.module.js +2 -1
- package/dist-server/server/modules/file-tree/file-tree.module.js.map +1 -1
- package/dist-server/server/modules/file-tree/file-tree.service.js +44 -7
- package/dist-server/server/modules/file-tree/file-tree.service.js.map +1 -1
- package/dist-server/server/modules/file-tree/tests/file-tree.service.test.js +126 -1
- package/dist-server/server/modules/file-tree/tests/file-tree.service.test.js.map +1 -1
- package/dist-server/server/modules/git/git-branch-diff.service.js +142 -0
- package/dist-server/server/modules/git/git-branch-diff.service.js.map +1 -0
- package/dist-server/server/modules/git/git-branch.service.js.map +1 -1
- package/dist-server/server/modules/git/git.routes.js +92 -5
- package/dist-server/server/modules/git/git.routes.js.map +1 -1
- package/dist-server/server/modules/git/tests/git-branch-diff.service.test.js +160 -0
- package/dist-server/server/modules/git/tests/git-branch-diff.service.test.js.map +1 -0
- package/dist-server/server/modules/projects/project-clone.routes.js +108 -0
- package/dist-server/server/modules/projects/project-clone.routes.js.map +1 -0
- package/dist-server/server/modules/projects/projects.routes.js +13 -66
- package/dist-server/server/modules/projects/projects.routes.js.map +1 -1
- package/dist-server/server/modules/projects/services/project-clone-request.service.js +36 -0
- package/dist-server/server/modules/projects/services/project-clone-request.service.js.map +1 -0
- package/dist-server/server/modules/projects/services/project-clone.service.js +86 -32
- package/dist-server/server/modules/projects/services/project-clone.service.js.map +1 -1
- package/dist-server/server/modules/projects/services/project-delete.service.js +12 -1
- package/dist-server/server/modules/projects/services/project-delete.service.js.map +1 -1
- package/dist-server/server/modules/projects/services/project-star.service.js +2 -2
- package/dist-server/server/modules/projects/services/project-star.service.js.map +1 -1
- package/dist-server/server/modules/projects/tests/project-clone.routes.test.js +191 -0
- package/dist-server/server/modules/projects/tests/project-clone.routes.test.js.map +1 -0
- package/dist-server/server/modules/projects/tests/project-clone.service.test.js +275 -0
- package/dist-server/server/modules/projects/tests/project-clone.service.test.js.map +1 -1
- package/dist-server/server/modules/providers/index.js +3 -0
- package/dist-server/server/modules/providers/index.js.map +1 -1
- package/dist-server/server/modules/providers/list/claude/claude-auth.provider.js +62 -9
- package/dist-server/server/modules/providers/list/claude/claude-auth.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/claude/claude-fork.provider.js +46 -0
- package/dist-server/server/modules/providers/list/claude/claude-fork.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/claude/claude-models.provider.js +31 -12
- package/dist-server/server/modules/providers/list/claude/claude-models.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/claude/claude-runtime.provider.js +472 -53
- package/dist-server/server/modules/providers/list/claude/claude-runtime.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/claude/claude-session-synchronizer.provider.js +38 -11
- package/dist-server/server/modules/providers/list/claude/claude-session-synchronizer.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/claude/claude-sessions.provider.js +1007 -60
- package/dist-server/server/modules/providers/list/claude/claude-sessions.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/claude/claude-skills.provider.js +4 -1
- package/dist-server/server/modules/providers/list/claude/claude-skills.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/claude/claude.provider.js +2 -0
- package/dist-server/server/modules/providers/list/claude/claude.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/codex/codex-app-server.client.js +204 -0
- package/dist-server/server/modules/providers/list/codex/codex-app-server.client.js.map +1 -0
- package/dist-server/server/modules/providers/list/codex/codex-fork.provider.js +32 -0
- package/dist-server/server/modules/providers/list/codex/codex-fork.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/codex/codex-models.provider.js +47 -0
- package/dist-server/server/modules/providers/list/codex/codex-models.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/codex/codex-runtime.provider.js +71 -53
- package/dist-server/server/modules/providers/list/codex/codex-runtime.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/codex/codex-session-synchronizer.provider.js +8 -0
- package/dist-server/server/modules/providers/list/codex/codex-session-synchronizer.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/codex/codex-sessions.provider.js +1621 -431
- package/dist-server/server/modules/providers/list/codex/codex-sessions.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/codex/codex.provider.js +2 -0
- package/dist-server/server/modules/providers/list/codex/codex.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/cursor/cursor-auth.provider.js +9 -2
- package/dist-server/server/modules/providers/list/cursor/cursor-auth.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/cursor/cursor-runtime.provider.js +16 -8
- package/dist-server/server/modules/providers/list/cursor/cursor-runtime.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/cursor/cursor-session-synchronizer.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/opencode/opencode-models.provider.js +274 -9
- package/dist-server/server/modules/providers/list/opencode/opencode-models.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/opencode/opencode-runtime.provider.js +5 -2
- package/dist-server/server/modules/providers/list/opencode/opencode-runtime.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/opencode/opencode-session-synchronizer.provider.js +19 -2
- package/dist-server/server/modules/providers/list/opencode/opencode-session-synchronizer.provider.js.map +1 -1
- package/dist-server/server/modules/providers/provider.routes.js +134 -2
- package/dist-server/server/modules/providers/provider.routes.js.map +1 -1
- package/dist-server/server/modules/providers/services/claude-cli-liveness.service.js +100 -0
- package/dist-server/server/modules/providers/services/claude-cli-liveness.service.js.map +1 -0
- package/dist-server/server/modules/providers/services/claude-settings-permissions.service.js +97 -0
- package/dist-server/server/modules/providers/services/claude-settings-permissions.service.js.map +1 -0
- package/dist-server/server/modules/providers/services/provider-capabilities.service.js +23 -1
- package/dist-server/server/modules/providers/services/provider-capabilities.service.js.map +1 -1
- package/dist-server/server/modules/providers/services/provider-models.service.js +59 -5
- package/dist-server/server/modules/providers/services/provider-models.service.js.map +1 -1
- package/dist-server/server/modules/providers/services/provider-runtime.service.js +8 -0
- package/dist-server/server/modules/providers/services/provider-runtime.service.js.map +1 -1
- package/dist-server/server/modules/providers/services/provider-token-usage.service.js +139 -35
- package/dist-server/server/modules/providers/services/provider-token-usage.service.js.map +1 -1
- package/dist-server/server/modules/providers/services/session-conversations-search.service.js +2 -4
- package/dist-server/server/modules/providers/services/session-conversations-search.service.js.map +1 -1
- package/dist-server/server/modules/providers/services/session-history-cache.service.js +91 -0
- package/dist-server/server/modules/providers/services/session-history-cache.service.js.map +1 -0
- package/dist-server/server/modules/providers/services/session-synchronizer.service.js +105 -32
- package/dist-server/server/modules/providers/services/session-synchronizer.service.js.map +1 -1
- package/dist-server/server/modules/providers/services/sessions-watcher.service.js +3 -60
- package/dist-server/server/modules/providers/services/sessions-watcher.service.js.map +1 -1
- package/dist-server/server/modules/providers/services/sessions.service.js +252 -12
- package/dist-server/server/modules/providers/services/sessions.service.js.map +1 -1
- package/dist-server/server/modules/providers/tests/claude-auth.test.js +183 -0
- package/dist-server/server/modules/providers/tests/claude-auth.test.js.map +1 -1
- package/dist-server/server/modules/providers/tests/claude-background-work.test.js +172 -0
- package/dist-server/server/modules/providers/tests/claude-background-work.test.js.map +1 -0
- package/dist-server/server/modules/providers/tests/claude-compaction.test.js +84 -0
- package/dist-server/server/modules/providers/tests/claude-compaction.test.js.map +1 -0
- package/dist-server/server/modules/providers/tests/claude-message-model.test.js +123 -0
- package/dist-server/server/modules/providers/tests/claude-message-model.test.js.map +1 -0
- package/dist-server/server/modules/providers/tests/claude-models.test.js +39 -0
- package/dist-server/server/modules/providers/tests/claude-models.test.js.map +1 -0
- package/dist-server/server/modules/providers/tests/claude-runtime-hold.test.js +265 -0
- package/dist-server/server/modules/providers/tests/claude-runtime-hold.test.js.map +1 -0
- package/dist-server/server/modules/providers/tests/claude-sessions.test.js +1815 -33
- package/dist-server/server/modules/providers/tests/claude-sessions.test.js.map +1 -1
- package/dist-server/server/modules/providers/tests/claude-settings-permissions.test.js +228 -0
- package/dist-server/server/modules/providers/tests/claude-settings-permissions.test.js.map +1 -0
- package/dist-server/server/modules/providers/tests/claude-subagent-echo.test.js +16 -0
- package/dist-server/server/modules/providers/tests/claude-subagent-echo.test.js.map +1 -0
- package/dist-server/server/modules/providers/tests/claude-token-budget.test.js +85 -0
- package/dist-server/server/modules/providers/tests/claude-token-budget.test.js.map +1 -0
- package/dist-server/server/modules/providers/tests/codex-message-editing.test.js +338 -0
- package/dist-server/server/modules/providers/tests/codex-message-editing.test.js.map +1 -0
- package/dist-server/server/modules/providers/tests/codex-models.test.js +25 -0
- package/dist-server/server/modules/providers/tests/codex-models.test.js.map +1 -0
- package/dist-server/server/modules/providers/tests/codex-runtime.test.js +83 -0
- package/dist-server/server/modules/providers/tests/codex-runtime.test.js.map +1 -0
- package/dist-server/server/modules/providers/tests/codex-session-fork.test.js +54 -0
- package/dist-server/server/modules/providers/tests/codex-session-fork.test.js.map +1 -0
- package/dist-server/server/modules/providers/tests/codex-sessions.test.js +354 -15
- package/dist-server/server/modules/providers/tests/codex-sessions.test.js.map +1 -1
- package/dist-server/server/modules/providers/tests/cursor-auth.test.js +167 -0
- package/dist-server/server/modules/providers/tests/cursor-auth.test.js.map +1 -0
- package/dist-server/server/modules/providers/tests/opencode-models.test.js +196 -4
- package/dist-server/server/modules/providers/tests/opencode-models.test.js.map +1 -1
- package/dist-server/server/modules/providers/tests/opencode-sessions.test.js +19 -1
- package/dist-server/server/modules/providers/tests/opencode-sessions.test.js.map +1 -1
- package/dist-server/server/modules/providers/tests/provider-capabilities.service.test.js +30 -0
- package/dist-server/server/modules/providers/tests/provider-capabilities.service.test.js.map +1 -0
- package/dist-server/server/modules/providers/tests/provider-models.service.test.js +79 -1
- package/dist-server/server/modules/providers/tests/provider-models.service.test.js.map +1 -1
- package/dist-server/server/modules/providers/tests/provider-token-usage.service.test.js +164 -1
- package/dist-server/server/modules/providers/tests/provider-token-usage.service.test.js.map +1 -1
- package/dist-server/server/modules/providers/tests/provider.routes.test.js +190 -1
- package/dist-server/server/modules/providers/tests/provider.routes.test.js.map +1 -1
- package/dist-server/server/modules/providers/tests/session-fork.test.js +113 -0
- package/dist-server/server/modules/providers/tests/session-fork.test.js.map +1 -0
- package/dist-server/server/modules/providers/tests/session-history-cache.service.test.js +139 -0
- package/dist-server/server/modules/providers/tests/session-history-cache.service.test.js.map +1 -0
- package/dist-server/server/modules/providers/tests/session-orphan-prune.test.js +90 -0
- package/dist-server/server/modules/providers/tests/session-orphan-prune.test.js.map +1 -0
- package/dist-server/server/modules/providers/tests/session-synchronizer-coalescing.test.js +122 -0
- package/dist-server/server/modules/providers/tests/session-synchronizer-coalescing.test.js.map +1 -0
- package/dist-server/server/modules/providers/tests/sessions.service.test.js +219 -1
- package/dist-server/server/modules/providers/tests/sessions.service.test.js.map +1 -1
- package/dist-server/server/modules/providers/tests/skills.test.js +15 -3
- package/dist-server/server/modules/providers/tests/skills.test.js.map +1 -1
- package/dist-server/server/modules/scheduled-messages/index.js +5 -0
- package/dist-server/server/modules/scheduled-messages/index.js.map +1 -0
- package/dist-server/server/modules/scheduled-messages/scheduled-messages.routes.js +47 -0
- package/dist-server/server/modules/scheduled-messages/scheduled-messages.routes.js.map +1 -0
- package/dist-server/server/modules/scheduled-messages/services/scheduled-message-dispatcher.service.js +175 -0
- package/dist-server/server/modules/scheduled-messages/services/scheduled-message-dispatcher.service.js.map +1 -0
- package/dist-server/server/modules/scheduled-messages/services/scheduled-messages.service.js +95 -0
- package/dist-server/server/modules/scheduled-messages/services/scheduled-messages.service.js.map +1 -0
- package/dist-server/server/modules/scheduled-messages/tests/scheduled-messages.test.js +290 -0
- package/dist-server/server/modules/scheduled-messages/tests/scheduled-messages.test.js.map +1 -0
- package/dist-server/server/modules/taskmaster/taskmaster.routes.js +23 -23
- package/dist-server/server/modules/taskmaster/taskmaster.routes.js.map +1 -1
- package/dist-server/server/modules/taskmaster/tests/taskmaster.routes.test.js +48 -0
- package/dist-server/server/modules/taskmaster/tests/taskmaster.routes.test.js.map +1 -1
- package/dist-server/server/modules/user/tests/user.service.test.js +70 -0
- package/dist-server/server/modules/user/tests/user.service.test.js.map +1 -1
- package/dist-server/server/modules/user/user.module.js +10 -1
- package/dist-server/server/modules/user/user.module.js.map +1 -1
- package/dist-server/server/modules/user/user.routes.js +44 -0
- package/dist-server/server/modules/user/user.routes.js.map +1 -1
- package/dist-server/server/modules/user/user.service.js +75 -0
- package/dist-server/server/modules/user/user.service.js.map +1 -1
- package/dist-server/server/modules/voice/tests/voice.service.test.js +3 -2
- package/dist-server/server/modules/voice/tests/voice.service.test.js.map +1 -1
- package/dist-server/server/modules/websocket/index.js +7 -0
- package/dist-server/server/modules/websocket/index.js.map +1 -1
- package/dist-server/server/modules/websocket/services/chat-run-registry.service.js +37 -49
- package/dist-server/server/modules/websocket/services/chat-run-registry.service.js.map +1 -1
- package/dist-server/server/modules/websocket/services/chat-session-writer.service.js +35 -5
- package/dist-server/server/modules/websocket/services/chat-session-writer.service.js.map +1 -1
- package/dist-server/server/modules/websocket/services/chat-websocket.service.js +204 -10
- package/dist-server/server/modules/websocket/services/chat-websocket.service.js.map +1 -1
- package/dist-server/server/modules/websocket/services/session-upsert-broadcast.service.js +94 -0
- package/dist-server/server/modules/websocket/services/session-upsert-broadcast.service.js.map +1 -0
- package/dist-server/server/modules/websocket/services/shell-websocket.service.js +236 -20
- package/dist-server/server/modules/websocket/services/shell-websocket.service.js.map +1 -1
- package/dist-server/server/modules/websocket/tests/chat-background-subscribe.test.js +137 -0
- package/dist-server/server/modules/websocket/tests/chat-background-subscribe.test.js.map +1 -0
- package/dist-server/server/modules/websocket/tests/chat-edit-send.test.js +263 -0
- package/dist-server/server/modules/websocket/tests/chat-edit-send.test.js.map +1 -0
- package/dist-server/server/modules/websocket/tests/chat-run-registry.test.js +29 -3
- package/dist-server/server/modules/websocket/tests/chat-run-registry.test.js.map +1 -1
- package/dist-server/server/modules/websocket/tests/chat-stop-task.test.js +96 -0
- package/dist-server/server/modules/websocket/tests/chat-stop-task.test.js.map +1 -0
- package/dist-server/server/modules/websocket/tests/session-upsert-broadcast.test.js +106 -0
- package/dist-server/server/modules/websocket/tests/session-upsert-broadcast.test.js.map +1 -0
- package/dist-server/server/modules/websocket/tests/shell-websocket.service.test.js +500 -0
- package/dist-server/server/modules/websocket/tests/shell-websocket.service.test.js.map +1 -1
- package/dist-server/server/shared/claude-cli-path.js +22 -5
- package/dist-server/server/shared/claude-cli-path.js.map +1 -1
- package/dist-server/server/shared/index.js +3 -0
- package/dist-server/server/shared/index.js.map +1 -0
- package/dist-server/server/shared/message-unification.js +376 -0
- package/dist-server/server/shared/message-unification.js.map +1 -0
- package/dist-server/server/shared/tests/claude-cli-path.test.js +45 -1
- package/dist-server/server/shared/tests/claude-cli-path.test.js.map +1 -1
- package/dist-server/server/shared/tests/message-unification.test.js +174 -0
- package/dist-server/server/shared/tests/message-unification.test.js.map +1 -0
- package/dist-server/server/shared/tests/read-only-roots.test.js +124 -0
- package/dist-server/server/shared/tests/read-only-roots.test.js.map +1 -0
- package/dist-server/server/shared/tests/strip-ansi-sequences.test.js +31 -0
- package/dist-server/server/shared/tests/strip-ansi-sequences.test.js.map +1 -0
- package/dist-server/server/shared/utils.js +160 -0
- package/dist-server/server/shared/utils.js.map +1 -1
- package/electron/desktopWindow.js +6 -0
- package/electron/viewHost.js +25 -2
- package/package.json +20 -17
- package/server/index.ts +16 -5
- package/server/modules/agent/agent.module.ts +11 -1
- package/server/modules/agent/agent.routes.ts +200 -142
- package/server/modules/agent/tests/agent-run-registration.test.ts +348 -0
- package/server/modules/agent/tests/agent.routes.test.ts +51 -2
- package/server/modules/browser-use/browser-use.routes.ts +96 -33
- package/server/modules/database/connection.ts +2 -1
- package/server/modules/database/index.ts +10 -0
- package/server/modules/database/migrations.ts +51 -0
- package/server/modules/database/repositories/provider-models.ts +79 -6
- package/server/modules/database/repositories/scheduled-messages.db.ts +127 -0
- package/server/modules/database/repositories/session-drafts.db.ts +171 -0
- package/server/modules/database/repositories/sessions.db.ts +204 -3
- package/server/modules/database/repositories/user-preferences.db.ts +80 -0
- package/server/modules/database/schema.ts +103 -0
- package/server/modules/database/tests/provider-models.db.integration.test.ts +113 -0
- package/server/modules/database/tests/sessions.db.integration.test.ts +52 -0
- package/server/modules/database/tests/user-preferences.db.integration.test.ts +135 -0
- package/server/modules/file-tree/file-tree.module.ts +2 -1
- package/server/modules/file-tree/file-tree.service.ts +49 -7
- package/server/modules/file-tree/tests/file-tree.service.test.ts +163 -1
- package/server/modules/git/git-branch-diff.service.ts +204 -0
- package/server/modules/git/git-branch.service.ts +2 -12
- package/server/modules/git/git.routes.ts +100 -6
- package/server/modules/git/tests/git-branch-diff.service.test.ts +210 -0
- package/server/modules/projects/project-clone.routes.ts +142 -0
- package/server/modules/projects/projects.routes.ts +16 -81
- package/server/modules/projects/services/project-clone-request.service.ts +56 -0
- package/server/modules/projects/services/project-clone.service.ts +102 -34
- package/server/modules/projects/services/project-delete.service.ts +13 -1
- package/server/modules/projects/services/project-star.service.ts +2 -2
- package/server/modules/projects/tests/project-clone.routes.test.ts +246 -0
- package/server/modules/projects/tests/project-clone.service.test.ts +376 -0
- package/server/modules/providers/index.ts +4 -0
- package/server/modules/providers/list/claude/claude-auth.provider.ts +81 -11
- package/server/modules/providers/list/claude/claude-fork.provider.ts +57 -0
- package/server/modules/providers/list/claude/claude-models.provider.ts +37 -17
- package/server/modules/providers/list/claude/claude-runtime.provider.js +510 -54
- package/server/modules/providers/list/claude/claude-session-synchronizer.provider.ts +40 -14
- package/server/modules/providers/list/claude/claude-sessions.provider.ts +1195 -63
- package/server/modules/providers/list/claude/claude-skills.provider.ts +4 -1
- package/server/modules/providers/list/claude/claude.provider.ts +3 -0
- package/server/modules/providers/list/codex/codex-app-server.client.ts +243 -0
- package/server/modules/providers/list/codex/codex-fork.provider.ts +40 -0
- package/server/modules/providers/list/codex/codex-models.provider.ts +47 -0
- package/server/modules/providers/list/codex/{codex-runtime.provider.js → codex-runtime.provider.ts} +105 -70
- package/server/modules/providers/list/codex/codex-session-synchronizer.provider.ts +9 -0
- package/server/modules/providers/list/codex/codex-sessions.provider.ts +1847 -455
- package/server/modules/providers/list/codex/codex.provider.ts +3 -0
- package/server/modules/providers/list/cursor/cursor-auth.provider.ts +10 -2
- package/server/modules/providers/list/cursor/cursor-runtime.provider.js +17 -8
- package/server/modules/providers/list/cursor/cursor-session-synchronizer.provider.ts +0 -1
- package/server/modules/providers/list/opencode/opencode-models.provider.ts +297 -9
- package/server/modules/providers/list/opencode/opencode-runtime.provider.js +5 -2
- package/server/modules/providers/list/opencode/opencode-session-synchronizer.provider.ts +30 -2
- package/server/modules/providers/provider.routes.ts +162 -2
- package/server/modules/providers/services/claude-cli-liveness.service.ts +121 -0
- package/server/modules/providers/services/claude-settings-permissions.service.ts +127 -0
- package/server/modules/providers/services/provider-capabilities.service.ts +40 -1
- package/server/modules/providers/services/provider-models.service.ts +71 -4
- package/server/modules/providers/services/provider-runtime.service.ts +11 -0
- package/server/modules/providers/services/provider-token-usage.service.ts +166 -39
- package/server/modules/providers/services/session-conversations-search.service.ts +2 -5
- package/server/modules/providers/services/session-history-cache.service.ts +150 -0
- package/server/modules/providers/services/session-synchronizer.service.ts +125 -38
- package/server/modules/providers/services/sessions-watcher.service.ts +3 -64
- package/server/modules/providers/services/sessions.service.ts +309 -17
- package/server/modules/providers/tests/claude-auth.test.ts +208 -0
- package/server/modules/providers/tests/claude-background-work.test.ts +213 -0
- package/server/modules/providers/tests/claude-compaction.test.ts +101 -0
- package/server/modules/providers/tests/claude-message-model.test.ts +160 -0
- package/server/modules/providers/tests/claude-models.test.ts +87 -0
- package/server/modules/providers/tests/claude-runtime-hold.test.ts +309 -0
- package/server/modules/providers/tests/claude-sessions.test.ts +2167 -37
- package/server/modules/providers/tests/claude-settings-permissions.test.ts +271 -0
- package/server/modules/providers/tests/claude-subagent-echo.test.ts +25 -0
- package/server/modules/providers/tests/claude-token-budget.test.ts +104 -0
- package/server/modules/providers/tests/codex-message-editing.test.ts +419 -0
- package/server/modules/providers/tests/codex-models.test.ts +37 -0
- package/server/modules/providers/tests/codex-runtime.test.ts +94 -0
- package/server/modules/providers/tests/codex-session-fork.test.ts +59 -0
- package/server/modules/providers/tests/codex-sessions.test.ts +396 -15
- package/server/modules/providers/tests/cursor-auth.test.ts +187 -0
- package/server/modules/providers/tests/opencode-models.test.ts +266 -8
- package/server/modules/providers/tests/opencode-sessions.test.ts +40 -1
- package/server/modules/providers/tests/provider-capabilities.service.test.ts +37 -0
- package/server/modules/providers/tests/provider-models.service.test.ts +99 -1
- package/server/modules/providers/tests/provider-token-usage.service.test.ts +188 -1
- package/server/modules/providers/tests/provider.routes.test.ts +230 -1
- package/server/modules/providers/tests/session-fork.test.ts +173 -0
- package/server/modules/providers/tests/session-history-cache.service.test.ts +165 -0
- package/server/modules/providers/tests/session-orphan-prune.test.ts +107 -0
- package/server/modules/providers/tests/session-synchronizer-coalescing.test.ts +136 -0
- package/server/modules/providers/tests/sessions.service.test.ts +303 -1
- package/server/modules/providers/tests/skills.test.ts +21 -5
- package/server/modules/scheduled-messages/index.ts +8 -0
- package/server/modules/scheduled-messages/scheduled-messages.routes.ts +68 -0
- package/server/modules/scheduled-messages/services/scheduled-message-dispatcher.service.ts +216 -0
- package/server/modules/scheduled-messages/services/scheduled-messages.service.ts +121 -0
- package/server/modules/scheduled-messages/tests/scheduled-messages.test.ts +354 -0
- package/server/modules/taskmaster/taskmaster.routes.ts +23 -33
- package/server/modules/taskmaster/tests/taskmaster.routes.test.ts +57 -0
- package/server/modules/user/tests/user.service.test.ts +90 -0
- package/server/modules/user/user.module.ts +10 -1
- package/server/modules/user/user.routes.ts +44 -0
- package/server/modules/user/user.service.ts +105 -0
- package/server/modules/voice/tests/voice.service.test.ts +3 -2
- package/server/modules/websocket/README.md +4 -2
- package/server/modules/websocket/index.ts +8 -0
- package/server/modules/websocket/services/chat-run-registry.service.ts +45 -54
- package/server/modules/websocket/services/chat-session-writer.service.ts +36 -6
- package/server/modules/websocket/services/chat-websocket.service.ts +302 -16
- package/server/modules/websocket/services/session-upsert-broadcast.service.ts +108 -0
- package/server/modules/websocket/services/shell-websocket.service.ts +276 -22
- package/server/modules/websocket/tests/chat-background-subscribe.test.ts +167 -0
- package/server/modules/websocket/tests/chat-edit-send.test.ts +309 -0
- package/server/modules/websocket/tests/chat-run-registry.test.ts +34 -3
- package/server/modules/websocket/tests/chat-stop-task.test.ts +127 -0
- package/server/modules/websocket/tests/session-upsert-broadcast.test.ts +138 -0
- package/server/modules/websocket/tests/shell-websocket.service.test.ts +629 -0
- package/server/shared/claude-cli-path.ts +24 -6
- package/server/shared/index.ts +3 -0
- package/server/shared/interfaces.ts +91 -0
- package/server/shared/message-unification.ts +449 -0
- package/server/shared/tests/claude-cli-path.test.ts +58 -1
- package/server/shared/tests/message-unification.test.ts +218 -0
- package/server/shared/tests/read-only-roots.test.ts +139 -0
- package/server/shared/tests/strip-ansi-sequences.test.ts +42 -0
- package/server/shared/types.ts +382 -7
- package/server/shared/utils.ts +176 -0
- package/dist/assets/channel-_falYbU1.js +0 -1
- package/dist/assets/classDiagram-JCYQIIEL-Dn-3CfpR.js +0 -1
- package/dist/assets/classDiagram-v2-OCEON4UE-Dn-3CfpR.js +0 -1
- package/dist/assets/index-CzPLBvXT.css +0 -32
- package/dist/assets/index-DFkBBuSA.js +0 -1549
- package/dist/assets/stateDiagram-v2-UEYNNEHI-BvBxUUrZ.js +0 -1
- package/dist/assets/swimlanesDiagram-ULZ7WXOC-CsE-x4YK.js +0 -8
|
@@ -24,7 +24,10 @@ import {
|
|
|
24
24
|
buildClaudeUserContent,
|
|
25
25
|
normalizeImageDescriptors
|
|
26
26
|
} from '@/shared/image-attachments.js';
|
|
27
|
-
import {
|
|
27
|
+
import {
|
|
28
|
+
CLAUDE_PREDEFINED_MODELS,
|
|
29
|
+
CLAUDE_ULTRACODE_EFFORT
|
|
30
|
+
} from '@/modules/providers/list/claude/claude-models.provider.js';
|
|
28
31
|
import { resolveClaudeCodeExecutablePath } from '@/shared/claude-cli-path.js';
|
|
29
32
|
import {
|
|
30
33
|
createNotificationEvent,
|
|
@@ -33,9 +36,14 @@ import {
|
|
|
33
36
|
notifyRunStopped,
|
|
34
37
|
notifyUserIfEnabled
|
|
35
38
|
} from '@/modules/notifications/index.js';
|
|
39
|
+
import { sessionHistoryCache } from '@/modules/providers/services/session-history-cache.service.js';
|
|
36
40
|
import { createCompleteMessage, createNormalizedMessage } from '@/shared/utils.js';
|
|
37
41
|
|
|
38
42
|
const activeSessions = new Map();
|
|
43
|
+
// Outstanding background tasks per live session, keyed like activeSessions. An
|
|
44
|
+
// entry lives exactly as long as the map entry it shadows: cleared when the
|
|
45
|
+
// session is removed, and reset when a newer run takes the key over.
|
|
46
|
+
const backgroundWork = createBackgroundWorkTracker();
|
|
39
47
|
const pendingToolApprovals = new Map();
|
|
40
48
|
// Sessions cancelled via abort-session. The abort handler already sent the
|
|
41
49
|
// terminal `complete` (aborted: true) to the client, so the run loop must not
|
|
@@ -69,6 +77,12 @@ const BG_WAIT_CEILING_MS = 30 * 60 * 1000;
|
|
|
69
77
|
|
|
70
78
|
const TOOLS_REQUIRING_INTERACTION = new Set(['AskUserQuestion', 'ExitPlanMode']);
|
|
71
79
|
|
|
80
|
+
// Ultracode is a session-scoped setting rather than an SDK effort level: it pairs xhigh
|
|
81
|
+
// effort with standing dynamic-workflow orchestration, and the CLI only honours it when
|
|
82
|
+
// Workflows are enabled. The catalog offers it as an effort choice for the picker, so the
|
|
83
|
+
// selection is translated back into the two options the SDK actually understands here.
|
|
84
|
+
const ULTRACODE_SDK_EFFORT = 'xhigh';
|
|
85
|
+
|
|
72
86
|
function resolveClaudeEffort(model, effort, modelsDefinition = CLAUDE_PREDEFINED_MODELS) {
|
|
73
87
|
const selectedModel = modelsDefinition?.OPTIONS?.find((option) => option.value === model) || null;
|
|
74
88
|
const allowedEfforts = selectedModel?.effort?.values
|
|
@@ -78,6 +92,30 @@ function resolveClaudeEffort(model, effort, modelsDefinition = CLAUDE_PREDEFINED
|
|
|
78
92
|
: undefined;
|
|
79
93
|
}
|
|
80
94
|
|
|
95
|
+
/**
|
|
96
|
+
* Writes the resolved effort choice onto the SDK options, expanding `ultracode` into the
|
|
97
|
+
* xhigh effort level plus the session-scoped settings it requires.
|
|
98
|
+
* @param {Object} sdkOptions - SDK options being built
|
|
99
|
+
* @param {string|undefined} resolvedEffort - Catalog-validated effort selection
|
|
100
|
+
*/
|
|
101
|
+
function applyClaudeEffort(sdkOptions, resolvedEffort) {
|
|
102
|
+
if (!resolvedEffort) {
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (resolvedEffort !== CLAUDE_ULTRACODE_EFFORT) {
|
|
107
|
+
sdkOptions.effort = resolvedEffort;
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
sdkOptions.effort = ULTRACODE_SDK_EFFORT;
|
|
112
|
+
sdkOptions.settings = {
|
|
113
|
+
...(sdkOptions.settings || {}),
|
|
114
|
+
ultracode: true,
|
|
115
|
+
enableWorkflows: true
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
81
119
|
function createRequestId() {
|
|
82
120
|
if (typeof crypto.randomUUID === 'function') {
|
|
83
121
|
return crypto.randomUUID();
|
|
@@ -183,7 +221,7 @@ function matchesToolPermission(entry, toolName, input) {
|
|
|
183
221
|
}
|
|
184
222
|
|
|
185
223
|
function mapCliOptionsToSDK(options = {}) {
|
|
186
|
-
const { providerSessionId, cwd, toolsSettings, permissionMode, effort } = options;
|
|
224
|
+
const { providerSessionId, cwd, toolsSettings, permissionMode, effort, resumeAnchorId, resumeFromScratch } = options;
|
|
187
225
|
|
|
188
226
|
const sdkOptions = {};
|
|
189
227
|
|
|
@@ -193,7 +231,12 @@ function mapCliOptionsToSDK(options = {}) {
|
|
|
193
231
|
|
|
194
232
|
// Resolve the executable eagerly on Windows because the SDK uses raw child_process.spawn,
|
|
195
233
|
// which does not reliably follow npm's shell wrappers like cross-spawn does.
|
|
196
|
-
|
|
234
|
+
// When nothing resolves the option stays unset on purpose: the SDK then falls back to the
|
|
235
|
+
// binary it ships, which beats handing it a bare `claude` that raw spawn can never launch.
|
|
236
|
+
const claudeExecutablePath = resolveClaudeCodeExecutablePath(process.env.CLAUDE_CLI_PATH);
|
|
237
|
+
if (claudeExecutablePath) {
|
|
238
|
+
sdkOptions.pathToClaudeCodeExecutable = claudeExecutablePath;
|
|
239
|
+
}
|
|
197
240
|
|
|
198
241
|
if (cwd) {
|
|
199
242
|
sdkOptions.cwd = cwd;
|
|
@@ -235,14 +278,11 @@ function mapCliOptionsToSDK(options = {}) {
|
|
|
235
278
|
|
|
236
279
|
sdkOptions.model = options.model || CLAUDE_PREDEFINED_MODELS.DEFAULT;
|
|
237
280
|
|
|
238
|
-
|
|
281
|
+
applyClaudeEffort(sdkOptions, resolveClaudeEffort(
|
|
239
282
|
sdkOptions.model,
|
|
240
283
|
effort,
|
|
241
284
|
options.effortModels || CLAUDE_PREDEFINED_MODELS,
|
|
242
|
-
);
|
|
243
|
-
if (resolvedEffort) {
|
|
244
|
-
sdkOptions.effort = resolvedEffort;
|
|
245
|
-
}
|
|
285
|
+
));
|
|
246
286
|
|
|
247
287
|
sdkOptions.systemPrompt = {
|
|
248
288
|
type: 'preset',
|
|
@@ -252,8 +292,19 @@ function mapCliOptionsToSDK(options = {}) {
|
|
|
252
292
|
sdkOptions.settingSources = ['project', 'user', 'local'];
|
|
253
293
|
|
|
254
294
|
// The SDK resumes with the provider-native session id, never the app id.
|
|
255
|
-
|
|
295
|
+
// `resumeFromScratch` is set when the very first prompt of a conversation was
|
|
296
|
+
// edited: there is nothing before it to resume through, so the turn has to
|
|
297
|
+
// start the conversation over instead.
|
|
298
|
+
if (providerSessionId && !resumeFromScratch) {
|
|
256
299
|
sdkOptions.resume = providerSessionId;
|
|
300
|
+
|
|
301
|
+
// Editing an already-sent message re-runs the conversation truncated just
|
|
302
|
+
// before it. `resumeSessionAt` is inclusive of the uuid it names, so the
|
|
303
|
+
// caller resolves the last row to KEEP and passes that — never the edited
|
|
304
|
+
// turn itself, which would leave the original prompt in context.
|
|
305
|
+
if (resumeAnchorId) {
|
|
306
|
+
sdkOptions.resumeSessionAt = resumeAnchorId;
|
|
307
|
+
}
|
|
257
308
|
}
|
|
258
309
|
|
|
259
310
|
return sdkOptions;
|
|
@@ -286,6 +337,9 @@ function addSession(sessionId, queryInstance, writer = null, releaseInput = null
|
|
|
286
337
|
console.error(`Error interrupting superseded run for session ${sessionId}:`, error?.message || error);
|
|
287
338
|
});
|
|
288
339
|
existing.releaseInput?.();
|
|
340
|
+
// Whatever the superseded process had outstanding dies with it and will
|
|
341
|
+
// never report, so the new run starts from an empty task set.
|
|
342
|
+
backgroundWork.clear(sessionId);
|
|
289
343
|
}
|
|
290
344
|
const carried = superseding ? null : existing;
|
|
291
345
|
activeSessions.set(sessionId, {
|
|
@@ -296,6 +350,44 @@ function addSession(sessionId, queryInstance, writer = null, releaseInput = null
|
|
|
296
350
|
// Re-registered mid-run once the provider session id lands; keep the closer.
|
|
297
351
|
releaseInput: releaseInput || carried?.releaseInput || null
|
|
298
352
|
});
|
|
353
|
+
// The history reader reports a background agent as running or stopped by
|
|
354
|
+
// whether this entry exists, and the cached history does not see this map.
|
|
355
|
+
sessionHistoryCache.invalidate(sessionId);
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* Lets go of the process an earlier run left holding a session open, so the
|
|
360
|
+
* next turn can take the session over.
|
|
361
|
+
*
|
|
362
|
+
* Closing stdin is not enough on its own: the CLI's wind-down waits for the
|
|
363
|
+
* background agents the process still has going — for up to the ceiling it
|
|
364
|
+
* was started with (BG_WAIT_CEILING_MS) — so an abandoned process with an
|
|
365
|
+
* agent outstanding would run next to the process that replaced it for as
|
|
366
|
+
* long as the agent does, both appending to the same session file. Stop the
|
|
367
|
+
* tasks first, while stdin is still open: the SDK drops control requests
|
|
368
|
+
* written after stdin has ended, which is also why the interrupt a later
|
|
369
|
+
* supersede sends to this process cannot do the job.
|
|
370
|
+
*
|
|
371
|
+
* The stops are not awaited — each request is written to the CLI before this
|
|
372
|
+
* returns, and a reply is only missing when the process has already gone.
|
|
373
|
+
* @param {string} sessionId - Session identifier
|
|
374
|
+
*/
|
|
375
|
+
function releaseHeldSession(sessionId) {
|
|
376
|
+
const held = getSession(sessionId);
|
|
377
|
+
if (!held) {
|
|
378
|
+
return;
|
|
379
|
+
}
|
|
380
|
+
for (const task of backgroundWork.outstanding(sessionId)) {
|
|
381
|
+
// A stop that cannot be delivered changes nothing about what happens
|
|
382
|
+
// next: the process is on its way out either way. Guard the synchronous
|
|
383
|
+
// call too, so a failing stop can never skip the release below.
|
|
384
|
+
try {
|
|
385
|
+
Promise.resolve(held.instance?.stopTask?.(task.taskId)).catch(() => {});
|
|
386
|
+
} catch {
|
|
387
|
+
// Same as a rejected stop.
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
held.releaseInput?.();
|
|
299
391
|
}
|
|
300
392
|
|
|
301
393
|
/**
|
|
@@ -304,6 +396,11 @@ function addSession(sessionId, queryInstance, writer = null, releaseInput = null
|
|
|
304
396
|
*/
|
|
305
397
|
function removeSession(sessionId) {
|
|
306
398
|
activeSessions.delete(sessionId);
|
|
399
|
+
// No process, no background work: anything still tracked was killed with it.
|
|
400
|
+
backgroundWork.clear(sessionId);
|
|
401
|
+
// See addSession: a page cached while the process was up still says
|
|
402
|
+
// `running` for any agent that never reported back.
|
|
403
|
+
sessionHistoryCache.invalidate(sessionId);
|
|
307
404
|
}
|
|
308
405
|
|
|
309
406
|
/**
|
|
@@ -339,47 +436,129 @@ function transformMessage(sdkMessage) {
|
|
|
339
436
|
return sdkMessage;
|
|
340
437
|
}
|
|
341
438
|
|
|
439
|
+
/**
|
|
440
|
+
* True for the user bubble the SDK echoes for a subagent's own prompt.
|
|
441
|
+
*
|
|
442
|
+
* Subagent traffic carries `parent_tool_use_id`, so this echo lands in the main
|
|
443
|
+
* thread and stacks a second copy of the prompt right below the Agent tool card
|
|
444
|
+
* that already displays it. It also disappears on reload, because the transcript
|
|
445
|
+
* keeps that turn in the subagent's sidechain rather than the session file.
|
|
446
|
+
* @param {Object} message - Normalized message about to be sent to the client
|
|
447
|
+
* @returns {boolean}
|
|
448
|
+
*/
|
|
449
|
+
export function isSubagentPromptEcho(message) {
|
|
450
|
+
return Boolean(message?.parentToolUseId) && message.role === 'user' && message.kind === 'text';
|
|
451
|
+
}
|
|
452
|
+
|
|
342
453
|
function readNumber(value) {
|
|
343
454
|
const parsed = Number(value);
|
|
344
455
|
return Number.isFinite(parsed) ? parsed : 0;
|
|
345
456
|
}
|
|
346
457
|
|
|
347
458
|
/**
|
|
348
|
-
*
|
|
349
|
-
*
|
|
350
|
-
*
|
|
459
|
+
* @typedef {Object} TokenBudget
|
|
460
|
+
* @property {number} used
|
|
461
|
+
* @property {number} total
|
|
462
|
+
* @property {number} inputTokens
|
|
463
|
+
* @property {number} outputTokens
|
|
464
|
+
* @property {number} [cacheReadTokens]
|
|
465
|
+
* @property {number} [cacheCreationTokens]
|
|
466
|
+
* @property {number} [cacheTokens]
|
|
467
|
+
* @property {{ input: number, output: number }} breakdown
|
|
468
|
+
*/
|
|
469
|
+
|
|
470
|
+
/**
|
|
471
|
+
* Builds a context-window budget from an Anthropic-shaped usage payload.
|
|
472
|
+
*
|
|
473
|
+
* `input_tokens + cache_read + cache_creation` is one request's whole prompt,
|
|
474
|
+
* which is exactly what the context window holds at that moment.
|
|
475
|
+
* @param {Object} messageUsage - Anthropic usage payload
|
|
476
|
+
* @returns {TokenBudget} Token budget object
|
|
477
|
+
*/
|
|
478
|
+
function buildTokenBudget(messageUsage) {
|
|
479
|
+
const directInputTokens = readNumber(messageUsage.input_tokens ?? messageUsage.inputTokens);
|
|
480
|
+
const cacheCreationTokens = readNumber(messageUsage.cache_creation_input_tokens ?? messageUsage.cacheCreationInputTokens ?? messageUsage.cacheCreationTokens);
|
|
481
|
+
const cacheReadTokens = readNumber(messageUsage.cache_read_input_tokens ?? messageUsage.cacheReadInputTokens ?? messageUsage.cacheReadTokens);
|
|
482
|
+
const cacheTokens = cacheCreationTokens + cacheReadTokens;
|
|
483
|
+
const inputTokens = directInputTokens + cacheTokens;
|
|
484
|
+
const outputTokens = readNumber(messageUsage.output_tokens ?? messageUsage.outputTokens);
|
|
485
|
+
const contextWindow = parseInt(process.env.CONTEXT_WINDOW, 10) || 160000;
|
|
486
|
+
|
|
487
|
+
return {
|
|
488
|
+
used: inputTokens + outputTokens,
|
|
489
|
+
total: contextWindow,
|
|
490
|
+
inputTokens,
|
|
491
|
+
outputTokens,
|
|
492
|
+
cacheReadTokens,
|
|
493
|
+
cacheCreationTokens,
|
|
494
|
+
cacheTokens,
|
|
495
|
+
breakdown: {
|
|
496
|
+
input: inputTokens,
|
|
497
|
+
output: outputTokens,
|
|
498
|
+
},
|
|
499
|
+
};
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
/**
|
|
503
|
+
* Extracts the session's context-window usage from an SDK stream message.
|
|
504
|
+
*
|
|
505
|
+
* Only assistant messages describe the context window: each one reports the
|
|
506
|
+
* prompt its own request carried. The turn-ending `result` is deliberately not
|
|
507
|
+
* a source here — see `extractCumulativeTokenBudget`.
|
|
351
508
|
* @param {Object} sdkMessage - SDK stream message
|
|
352
|
-
* @returns {
|
|
509
|
+
* @returns {TokenBudget|null} Token budget object or null
|
|
353
510
|
*/
|
|
354
511
|
function extractTokenBudget(sdkMessage) {
|
|
355
512
|
if (!sdkMessage || typeof sdkMessage !== 'object') {
|
|
356
513
|
return null;
|
|
357
514
|
}
|
|
358
515
|
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
516
|
+
// Subagent traffic (parent_tool_use_id set) reports the subagent's own
|
|
517
|
+
// context window, not this session's — surfacing it makes the counter drop
|
|
518
|
+
// to the subagent's number and bounce back on the next main-thread event.
|
|
519
|
+
if (sdkMessage.parent_tool_use_id) {
|
|
520
|
+
return null;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
// Only assistant messages carry Anthropic-shaped usage. System
|
|
524
|
+
// task_progress/task_notification events have a top-level `usage` too, but
|
|
525
|
+
// shaped {total_tokens, tool_uses, duration_ms} — reading Anthropic keys
|
|
526
|
+
// off it yields an all-zero budget that flashes "0" in the composer.
|
|
527
|
+
if (sdkMessage.type !== 'assistant') {
|
|
528
|
+
return null;
|
|
529
|
+
}
|
|
369
530
|
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
531
|
+
const messageUsage = sdkMessage.message?.usage;
|
|
532
|
+
if (!messageUsage || typeof messageUsage !== 'object') {
|
|
533
|
+
return null;
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
return buildTokenBudget(messageUsage);
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
/**
|
|
540
|
+
* Last-resort budget read from a turn's `result` message.
|
|
541
|
+
*
|
|
542
|
+
* `result.usage` and `result.modelUsage` are the turn's *bill*: every request
|
|
543
|
+
* the turn made, summed, including each subagent's. A turn that made four
|
|
544
|
+
* requests therefore reports roughly four times the context the conversation
|
|
545
|
+
* actually holds, so publishing it made the counter leap at the end of a turn
|
|
546
|
+
* and fall back on the next assistant message — worst with subagents running,
|
|
547
|
+
* whose requests inflate the sum without ever entering this session's context.
|
|
548
|
+
*
|
|
549
|
+
* It is still the only usage an SDK build that reports none per assistant
|
|
550
|
+
* message ever emits, so it stays available for the caller to use when a turn
|
|
551
|
+
* produced no assistant budget at all.
|
|
552
|
+
* @param {Object} sdkMessage - SDK stream message
|
|
553
|
+
* @returns {TokenBudget|null} Token budget object or null
|
|
554
|
+
*/
|
|
555
|
+
function extractCumulativeTokenBudget(sdkMessage) {
|
|
556
|
+
if (!sdkMessage || typeof sdkMessage !== 'object' || sdkMessage.type !== 'result') {
|
|
557
|
+
return null;
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
if (sdkMessage.usage && typeof sdkMessage.usage === 'object') {
|
|
561
|
+
return buildTokenBudget(sdkMessage.usage);
|
|
383
562
|
}
|
|
384
563
|
|
|
385
564
|
if (!sdkMessage.modelUsage || typeof sdkMessage.modelUsage !== 'object') {
|
|
@@ -411,9 +590,12 @@ function extractTokenBudget(sdkMessage) {
|
|
|
411
590
|
};
|
|
412
591
|
}
|
|
413
592
|
|
|
414
|
-
// Tool calls that leave work running past the end of a turn. Bash only
|
|
415
|
-
// when
|
|
416
|
-
|
|
593
|
+
// Tool calls that leave work running past the end of a turn. Bash and Agent only
|
|
594
|
+
// count when they are backgrounded; the rest defer or watch work by nature.
|
|
595
|
+
// Workflow belongs here rather than in a branch of its own: its input schema has
|
|
596
|
+
// no foreground option at all, so every call returns a task id immediately and
|
|
597
|
+
// reports back in a later turn.
|
|
598
|
+
const DEFERRED_WORK_TOOLS = new Set(['Monitor', 'ScheduleWakeup', 'CronCreate', 'TaskCreate', 'Workflow']);
|
|
417
599
|
|
|
418
600
|
/**
|
|
419
601
|
* Detects tool calls that keep working after the turn's `result` arrives.
|
|
@@ -421,10 +603,14 @@ const DEFERRED_WORK_TOOLS = new Set(['Monitor', 'ScheduleWakeup', 'CronCreate',
|
|
|
421
603
|
* Only turns that start background work need their CLI process held open; every
|
|
422
604
|
* other turn can let it exit immediately, as it did before the hold existed.
|
|
423
605
|
*
|
|
606
|
+
* Used by the providers module's tests, which pin the tool matching directly:
|
|
607
|
+
* the alternative is driving a whole SDK run to observe whether stdin was held,
|
|
608
|
+
* and the cost of getting this wrong is silently killed background work.
|
|
609
|
+
*
|
|
424
610
|
* @param {Object} sdkMessage - SDK stream message
|
|
425
611
|
* @returns {boolean} True when the message launches work that outlives the turn
|
|
426
612
|
*/
|
|
427
|
-
function startsBackgroundWork(sdkMessage) {
|
|
613
|
+
export function startsBackgroundWork(sdkMessage) {
|
|
428
614
|
const content = sdkMessage?.message?.content;
|
|
429
615
|
if (!Array.isArray(content)) {
|
|
430
616
|
return false;
|
|
@@ -437,10 +623,162 @@ function startsBackgroundWork(sdkMessage) {
|
|
|
437
623
|
if (block.name === 'Bash') {
|
|
438
624
|
return block.input?.run_in_background === true;
|
|
439
625
|
}
|
|
626
|
+
// A backgrounded subagent outlives the turn exactly like a backgrounded
|
|
627
|
+
// Bash does, so the process has to be held open for it to report back.
|
|
628
|
+
// Agents background by default — `run_in_background` is optional and only
|
|
629
|
+
// an explicit `false` opts out — hence `!== false` rather than `=== true`.
|
|
630
|
+
// A foreground agent must stay out of DEFERRED_WORK_TOOLS: it never pushes
|
|
631
|
+
// a follow-up turn, so it would pin the process for the full ceiling.
|
|
632
|
+
if (block.name === 'Agent') {
|
|
633
|
+
return block.input?.run_in_background !== false;
|
|
634
|
+
}
|
|
440
635
|
return DEFERRED_WORK_TOOLS.has(block.name);
|
|
441
636
|
});
|
|
442
637
|
}
|
|
443
638
|
|
|
639
|
+
// `task_updated` patch statuses after which a task is gone for good. `pending`,
|
|
640
|
+
// `running` and `paused` are still outstanding; `killed` is what the CLI
|
|
641
|
+
// writes when it stops a task itself (the task notification spells it
|
|
642
|
+
// `stopped`).
|
|
643
|
+
const TERMINAL_TASK_STATUSES = new Set(['completed', 'failed', 'killed']);
|
|
644
|
+
|
|
645
|
+
/**
|
|
646
|
+
* Tracks the background tasks each live session still has outstanding, folded
|
|
647
|
+
* from the `system` task events the SDK stream already carries.
|
|
648
|
+
*
|
|
649
|
+
* `startsBackgroundWork` above only knows that a turn *launched* something
|
|
650
|
+
* lasting; this knows what is still running and which task ids it answers to,
|
|
651
|
+
* which is what the running-sessions list and a stop request need once the
|
|
652
|
+
* turn's `result` has gone out and nothing else remembers the session is busy.
|
|
653
|
+
*
|
|
654
|
+
* Verified against a real query (SDK 0.3.165): `task_started` carries
|
|
655
|
+
* `task_id`, `tool_use_id`, `task_type` and `description` for every agent,
|
|
656
|
+
* workflow and backgrounded command (a foreground Bash emits nothing);
|
|
657
|
+
* `task_notification` settles a task with any status; `task_updated` carries
|
|
658
|
+
* only `task_id` and a patch, and is terminal when the patch's `status` is.
|
|
659
|
+
* Housekeeping tasks the CLI starts on its own have no `tool_use_id` and are
|
|
660
|
+
* not tracked — nothing in the transcript could show them.
|
|
661
|
+
*
|
|
662
|
+
* Exported so the folding can be driven with the four event shapes directly;
|
|
663
|
+
* the runtime keeps one instance keyed like `activeSessions`.
|
|
664
|
+
*
|
|
665
|
+
* @returns {{
|
|
666
|
+
* apply: (sessionKey: string, message: Object) => void,
|
|
667
|
+
* hasOutstanding: (sessionKey: string) => boolean,
|
|
668
|
+
* has: (sessionKey: string, taskId: string) => boolean,
|
|
669
|
+
* outstanding: (sessionKey: string) => Array<import('@/shared/types.js').BackgroundTaskSummary>,
|
|
670
|
+
* clear: (sessionKey: string) => void,
|
|
671
|
+
* list: () => Array<{ sessionId: string, tasks: Array<import('@/shared/types.js').BackgroundTaskSummary> }>
|
|
672
|
+
* }}
|
|
673
|
+
*/
|
|
674
|
+
export function createBackgroundWorkTracker() {
|
|
675
|
+
/** @type {Map<string, Map<string, import('@/shared/types.js').BackgroundTaskSummary>>} */
|
|
676
|
+
const sessions = new Map();
|
|
677
|
+
/**
|
|
678
|
+
* Tool-use ids the session's own turns issued. A task started for a call an
|
|
679
|
+
* agent made inside its own transcript — a workflow agent's backgrounded
|
|
680
|
+
* command, say — reaches this stream too, and nothing in the parent
|
|
681
|
+
* transcript could show it; it is kept for stopping but flagged `nested`.
|
|
682
|
+
* @type {Map<string, Set<string>>}
|
|
683
|
+
*/
|
|
684
|
+
const ownToolUseIds = new Map();
|
|
685
|
+
|
|
686
|
+
const remove = (sessionKey, taskId) => {
|
|
687
|
+
const tasks = sessions.get(sessionKey);
|
|
688
|
+
if (!tasks) {
|
|
689
|
+
return;
|
|
690
|
+
}
|
|
691
|
+
tasks.delete(taskId);
|
|
692
|
+
if (tasks.size === 0) {
|
|
693
|
+
sessions.delete(sessionKey);
|
|
694
|
+
}
|
|
695
|
+
};
|
|
696
|
+
|
|
697
|
+
return {
|
|
698
|
+
apply(sessionKey, message) {
|
|
699
|
+
if (message?.type === 'assistant' && !message.parent_tool_use_id) {
|
|
700
|
+
const content = message.message?.content;
|
|
701
|
+
if (Array.isArray(content)) {
|
|
702
|
+
for (const block of content) {
|
|
703
|
+
if (block?.type === 'tool_use' && typeof block.id === 'string') {
|
|
704
|
+
let ids = ownToolUseIds.get(sessionKey);
|
|
705
|
+
if (!ids) {
|
|
706
|
+
ids = new Set();
|
|
707
|
+
ownToolUseIds.set(sessionKey, ids);
|
|
708
|
+
}
|
|
709
|
+
ids.add(block.id);
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
}
|
|
713
|
+
return;
|
|
714
|
+
}
|
|
715
|
+
if (message?.type !== 'system' || typeof message.task_id !== 'string') {
|
|
716
|
+
return;
|
|
717
|
+
}
|
|
718
|
+
switch (message.subtype) {
|
|
719
|
+
case 'task_started': {
|
|
720
|
+
if (typeof message.tool_use_id !== 'string') {
|
|
721
|
+
return;
|
|
722
|
+
}
|
|
723
|
+
const task = {
|
|
724
|
+
taskId: message.task_id,
|
|
725
|
+
toolUseId: message.tool_use_id,
|
|
726
|
+
taskType: message.task_type,
|
|
727
|
+
description: message.description,
|
|
728
|
+
startedAt: Date.now()
|
|
729
|
+
};
|
|
730
|
+
if (typeof message.workflow_name === 'string') {
|
|
731
|
+
task.workflowName = message.workflow_name;
|
|
732
|
+
}
|
|
733
|
+
if (!ownToolUseIds.get(sessionKey)?.has(message.tool_use_id)) {
|
|
734
|
+
task.nested = true;
|
|
735
|
+
}
|
|
736
|
+
let tasks = sessions.get(sessionKey);
|
|
737
|
+
if (!tasks) {
|
|
738
|
+
tasks = new Map();
|
|
739
|
+
sessions.set(sessionKey, tasks);
|
|
740
|
+
}
|
|
741
|
+
tasks.set(task.taskId, task);
|
|
742
|
+
return;
|
|
743
|
+
}
|
|
744
|
+
case 'task_notification':
|
|
745
|
+
remove(sessionKey, message.task_id);
|
|
746
|
+
return;
|
|
747
|
+
case 'task_updated':
|
|
748
|
+
if (TERMINAL_TASK_STATUSES.has(message.patch?.status)) {
|
|
749
|
+
remove(sessionKey, message.task_id);
|
|
750
|
+
}
|
|
751
|
+
return;
|
|
752
|
+
default:
|
|
753
|
+
}
|
|
754
|
+
},
|
|
755
|
+
|
|
756
|
+
hasOutstanding(sessionKey) {
|
|
757
|
+
return sessions.has(sessionKey);
|
|
758
|
+
},
|
|
759
|
+
|
|
760
|
+
has(sessionKey, taskId) {
|
|
761
|
+
return Boolean(sessions.get(sessionKey)?.has(taskId));
|
|
762
|
+
},
|
|
763
|
+
|
|
764
|
+
outstanding(sessionKey) {
|
|
765
|
+
return Array.from(sessions.get(sessionKey)?.values() ?? []);
|
|
766
|
+
},
|
|
767
|
+
|
|
768
|
+
clear(sessionKey) {
|
|
769
|
+
sessions.delete(sessionKey);
|
|
770
|
+
ownToolUseIds.delete(sessionKey);
|
|
771
|
+
},
|
|
772
|
+
|
|
773
|
+
list() {
|
|
774
|
+
return Array.from(sessions, ([sessionId, tasks]) => ({
|
|
775
|
+
sessionId,
|
|
776
|
+
tasks: Array.from(tasks.values())
|
|
777
|
+
}));
|
|
778
|
+
}
|
|
779
|
+
};
|
|
780
|
+
}
|
|
781
|
+
|
|
444
782
|
/**
|
|
445
783
|
* Builds the SDK user messages for one turn.
|
|
446
784
|
*
|
|
@@ -596,14 +934,23 @@ async function queryClaudeSDK(command, options = {}, ws, context) {
|
|
|
596
934
|
// Set when a turn starts background work, cleared when the next `result`
|
|
597
935
|
// arrives — only turns with work still outstanding hold their process open.
|
|
598
936
|
let backgroundWorkPending = false;
|
|
937
|
+
// Set when the stream reports a task starting during this turn. Task events
|
|
938
|
+
// are the exact word on what is still running, so when the turn produced
|
|
939
|
+
// any, the tracker decides the hold; `startsBackgroundWork` is the fallback
|
|
940
|
+
// for tools that emit none (Monitor, ScheduleWakeup, CronCreate, TaskCreate)
|
|
941
|
+
// and for an SDK that does not report tasks at all.
|
|
942
|
+
let sawTaskEventThisTurn = false;
|
|
599
943
|
// True while the process is being held open for background work, so a later
|
|
600
944
|
// `result` can be recognised as that work reporting back.
|
|
601
945
|
let heldForBackgroundWork = false;
|
|
946
|
+
// Set once a turn publishes a budget read from an assistant message, so the
|
|
947
|
+
// turn-ending `result` is only mined for usage when nothing better arrived.
|
|
948
|
+
let assistantBudgetSent = false;
|
|
602
949
|
|
|
603
950
|
// A new turn supersedes any earlier one still holding this session's process
|
|
604
951
|
// open, so held runs cannot stack up across a conversation.
|
|
605
952
|
if (sessionKey()) {
|
|
606
|
-
|
|
953
|
+
releaseHeldSession(sessionKey());
|
|
607
954
|
}
|
|
608
955
|
|
|
609
956
|
// Arms (or re-arms) the idle countdown that eventually closes stdin.
|
|
@@ -736,6 +1083,13 @@ async function queryClaudeSDK(command, options = {}, ws, context) {
|
|
|
736
1083
|
return { behavior: 'deny', message: 'Permission request cancelled' };
|
|
737
1084
|
}
|
|
738
1085
|
|
|
1086
|
+
// A client answered. Announce it on the run stream so the replay buffer
|
|
1087
|
+
// and every other attached tab drop the prompt — resolving happens over
|
|
1088
|
+
// the inbound socket only, so without this a mid-run page refresh
|
|
1089
|
+
// replays the `permission_request` with nothing to retract it and the
|
|
1090
|
+
// already-answered prompt resurrects.
|
|
1091
|
+
ws.send(createNormalizedMessage({ kind: 'permission_resolved', requestId, sessionId: capturedSessionId || sessionId || null, provider: 'claude' }));
|
|
1092
|
+
|
|
739
1093
|
if (decision.allow) {
|
|
740
1094
|
if (decision.rememberEntry && typeof decision.rememberEntry === 'string') {
|
|
741
1095
|
if (!sdkOptions.allowedTools.includes(decision.rememberEntry)) {
|
|
@@ -751,10 +1105,13 @@ async function queryClaudeSDK(command, options = {}, ws, context) {
|
|
|
751
1105
|
return { behavior: 'deny', message: decision.message ?? 'User denied tool use' };
|
|
752
1106
|
};
|
|
753
1107
|
|
|
1108
|
+
// The SDK's own `query`, unless the caller supplies one (tests script the
|
|
1109
|
+
// stream to drive the hold logic below without a CLI process).
|
|
1110
|
+
const createQuery = context.createQuery ?? query;
|
|
754
1111
|
let heldPrompt = createHeldPromptStream(promptMessages);
|
|
755
1112
|
releasePromptStream = heldPrompt.release;
|
|
756
1113
|
try {
|
|
757
|
-
queryInstance =
|
|
1114
|
+
queryInstance = createQuery({
|
|
758
1115
|
prompt: heldPrompt.stream,
|
|
759
1116
|
options: sdkOptions
|
|
760
1117
|
});
|
|
@@ -767,7 +1124,7 @@ async function queryClaudeSDK(command, options = {}, ws, context) {
|
|
|
767
1124
|
heldPrompt.release();
|
|
768
1125
|
heldPrompt = createHeldPromptStream(promptMessages);
|
|
769
1126
|
releasePromptStream = heldPrompt.release;
|
|
770
|
-
queryInstance =
|
|
1127
|
+
queryInstance = createQuery({
|
|
771
1128
|
prompt: heldPrompt.stream,
|
|
772
1129
|
options: sdkOptions
|
|
773
1130
|
});
|
|
@@ -812,22 +1169,53 @@ async function queryClaudeSDK(command, options = {}, ws, context) {
|
|
|
812
1169
|
if (transformedMessage.parentToolUseId && !msg.parentToolUseId) {
|
|
813
1170
|
msg.parentToolUseId = transformedMessage.parentToolUseId;
|
|
814
1171
|
}
|
|
1172
|
+
if (isSubagentPromptEcho(msg)) {
|
|
1173
|
+
continue;
|
|
1174
|
+
}
|
|
815
1175
|
ws.send(msg);
|
|
816
1176
|
}
|
|
817
1177
|
|
|
818
|
-
// Extract and send token budget updates from assistant
|
|
819
|
-
|
|
1178
|
+
// Extract and send token budget updates from assistant usage payloads,
|
|
1179
|
+
// falling back to the turn's cumulative bill only for SDK builds that
|
|
1180
|
+
// report no per-assistant usage at all.
|
|
1181
|
+
const tokenBudgetData = extractTokenBudget(message)
|
|
1182
|
+
|| (assistantBudgetSent ? null : extractCumulativeTokenBudget(message));
|
|
820
1183
|
if (tokenBudgetData) {
|
|
1184
|
+
if (message.type === 'assistant') {
|
|
1185
|
+
assistantBudgetSent = true;
|
|
1186
|
+
}
|
|
821
1187
|
ws.send(createNormalizedMessage({ kind: 'status', text: 'token_budget', tokenBudget: tokenBudgetData, sessionId: capturedSessionId || sessionId || null, provider: 'claude' }));
|
|
822
1188
|
}
|
|
823
1189
|
|
|
824
1190
|
if (startsBackgroundWork(message)) {
|
|
825
1191
|
backgroundWorkPending = true;
|
|
826
1192
|
}
|
|
1193
|
+
if (message.type === 'system' && message.subtype === 'task_started') {
|
|
1194
|
+
sawTaskEventThisTurn = true;
|
|
1195
|
+
}
|
|
1196
|
+
backgroundWork.apply(sessionKey(), message);
|
|
1197
|
+
|
|
1198
|
+
// A task the user stopped gets no follow-up turn from the CLI — only its
|
|
1199
|
+
// `stopped` notification — so when that was the last outstanding task
|
|
1200
|
+
// nothing will ever push the `result` the release below waits for, and
|
|
1201
|
+
// the process would sit until the idle ceiling. Release it here. A
|
|
1202
|
+
// completed task is different: the CLI relays its result in a turn of
|
|
1203
|
+
// its own, which closing stdin now would cut short.
|
|
1204
|
+
if (
|
|
1205
|
+
heldForBackgroundWork
|
|
1206
|
+
&& message.type === 'system'
|
|
1207
|
+
&& message.subtype === 'task_notification'
|
|
1208
|
+
&& message.status === 'stopped'
|
|
1209
|
+
&& !backgroundWork.hasOutstanding(sessionKey())
|
|
1210
|
+
) {
|
|
1211
|
+
heldForBackgroundWork = false;
|
|
1212
|
+
releasePromptStream();
|
|
1213
|
+
}
|
|
827
1214
|
|
|
828
1215
|
if (message.type === 'result') {
|
|
829
1216
|
// The turn is done as far as the client is concerned.
|
|
830
1217
|
const abortPending = sessionKey() ? abortedSessionIds.has(sessionKey()) : false;
|
|
1218
|
+
const stillOutstanding = backgroundWork.hasOutstanding(sessionKey());
|
|
831
1219
|
if (!turnCompleteSent && !abortPending) {
|
|
832
1220
|
turnCompleteSent = true;
|
|
833
1221
|
ws.send(createCompleteMessage({ provider: 'claude', sessionId: capturedSessionId || sessionId || null, exitCode: 0 }));
|
|
@@ -838,9 +1226,10 @@ async function queryClaudeSDK(command, options = {}, ws, context) {
|
|
|
838
1226
|
sessionName: sessionSummary,
|
|
839
1227
|
stopReason: 'completed'
|
|
840
1228
|
});
|
|
841
|
-
} else if (heldForBackgroundWork && !abortPending) {
|
|
1229
|
+
} else if (heldForBackgroundWork && !abortPending && !stillOutstanding) {
|
|
842
1230
|
// A result after the turn already reported complete means the work we
|
|
843
|
-
// held the process open for has finished and pushed a follow-up turn
|
|
1231
|
+
// held the process open for has finished and pushed a follow-up turn
|
|
1232
|
+
// — the last of it, when nothing else is still running.
|
|
844
1233
|
notifyBackgroundWorkCompleted({
|
|
845
1234
|
userId: ws?.userId || null,
|
|
846
1235
|
provider: 'claude',
|
|
@@ -848,11 +1237,26 @@ async function queryClaudeSDK(command, options = {}, ws, context) {
|
|
|
848
1237
|
sessionName: sessionSummary
|
|
849
1238
|
});
|
|
850
1239
|
}
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
1240
|
+
// Work started during this turn, or work from an earlier turn that
|
|
1241
|
+
// has not settled yet (a follow-up turn reports one task in while
|
|
1242
|
+
// another is still going), is still running. Hold the process open
|
|
1243
|
+
// so it can finish and report back in a follow-up turn; the ceiling
|
|
1244
|
+
// is only a backstop for work that never reports.
|
|
1245
|
+
//
|
|
1246
|
+
// The release when the last task settles is this same branch on the
|
|
1247
|
+
// follow-up turn the CLI pushes for it, not the settling event
|
|
1248
|
+
// itself: closing stdin at that moment would cut the turn that
|
|
1249
|
+
// relays the task's result.
|
|
1250
|
+
//
|
|
1251
|
+
// When the turn reported its tasks, the tracker is the whole truth: an
|
|
1252
|
+
// Agent call without `run_in_background` is scored as background by
|
|
1253
|
+
// `startsBackgroundWork`, but the CLI runs it in the foreground and
|
|
1254
|
+
// it has settled before this `result` — holding for it kept a process
|
|
1255
|
+
// alive for the full ceiling with nothing outstanding.
|
|
1256
|
+
const holdForTurn = sawTaskEventThisTurn ? stillOutstanding : backgroundWorkPending || stillOutstanding;
|
|
1257
|
+
backgroundWorkPending = false;
|
|
1258
|
+
sawTaskEventThisTurn = false;
|
|
1259
|
+
if (holdForTurn) {
|
|
856
1260
|
heldForBackgroundWork = true;
|
|
857
1261
|
scheduleRelease();
|
|
858
1262
|
} else {
|
|
@@ -992,6 +1396,36 @@ async function abortClaudeSDKSession(sessionId) {
|
|
|
992
1396
|
}
|
|
993
1397
|
}
|
|
994
1398
|
|
|
1399
|
+
/**
|
|
1400
|
+
* Sessions whose background tasks are still outstanding, with the tasks.
|
|
1401
|
+
*
|
|
1402
|
+
* A session stays here after its turn's `result` for as long as the process
|
|
1403
|
+
* is held open for the work — which is exactly the window in which nothing
|
|
1404
|
+
* else (the chat run registry marks the run completed at `result`) knows the
|
|
1405
|
+
* session is still busy.
|
|
1406
|
+
* @returns {Array<{ sessionId: string, tasks: Array<import('@/shared/types.js').BackgroundTaskSummary> }>}
|
|
1407
|
+
*/
|
|
1408
|
+
function listClaudeSDKBackgroundWork() {
|
|
1409
|
+
return backgroundWork.list();
|
|
1410
|
+
}
|
|
1411
|
+
|
|
1412
|
+
/**
|
|
1413
|
+
* Stops one outstanding background task through the SDK, which then emits a
|
|
1414
|
+
* `task_notification` with status `stopped` — the same event that drops the
|
|
1415
|
+
* task from the tracker and settles its card.
|
|
1416
|
+
* @param {string} sessionId - Session identifier
|
|
1417
|
+
* @param {string} taskId - The task's `task_id` as reported on `task_started`
|
|
1418
|
+
* @returns {Promise<boolean>} False when no live process is tracking the task
|
|
1419
|
+
*/
|
|
1420
|
+
async function stopClaudeSDKTask(sessionId, taskId) {
|
|
1421
|
+
const session = getSession(sessionId);
|
|
1422
|
+
if (!session || !backgroundWork.has(sessionId, taskId)) {
|
|
1423
|
+
return false;
|
|
1424
|
+
}
|
|
1425
|
+
await session.instance.stopTask(taskId);
|
|
1426
|
+
return true;
|
|
1427
|
+
}
|
|
1428
|
+
|
|
995
1429
|
/**
|
|
996
1430
|
* Checks if an SDK session is currently active
|
|
997
1431
|
* @param {string} sessionId - Session identifier
|
|
@@ -999,7 +1433,22 @@ async function abortClaudeSDKSession(sessionId) {
|
|
|
999
1433
|
*/
|
|
1000
1434
|
function isClaudeSDKSessionActive(sessionId) {
|
|
1001
1435
|
const session = getSession(sessionId);
|
|
1002
|
-
return session && session.status === 'active';
|
|
1436
|
+
return Boolean(session && session.status === 'active');
|
|
1437
|
+
}
|
|
1438
|
+
|
|
1439
|
+
/**
|
|
1440
|
+
* When the run behind a session started, or null when no run is up.
|
|
1441
|
+
*
|
|
1442
|
+
* The history reader uses this to tell a background agent launched by the
|
|
1443
|
+
* live process (still able to report back) from one launched by an earlier
|
|
1444
|
+
* process that has since exited (never will): a launch row older than the
|
|
1445
|
+
* live run cannot belong to it.
|
|
1446
|
+
* @param {string} sessionId - Session identifier
|
|
1447
|
+
* @returns {number|null} Epoch milliseconds the live run started, or null
|
|
1448
|
+
*/
|
|
1449
|
+
function getClaudeSDKSessionStartTime(sessionId) {
|
|
1450
|
+
const session = getSession(sessionId);
|
|
1451
|
+
return session && session.status === 'active' ? session.startTime : null;
|
|
1003
1452
|
}
|
|
1004
1453
|
|
|
1005
1454
|
/**
|
|
@@ -1054,15 +1503,22 @@ export const claudeRuntime = {
|
|
|
1054
1503
|
resolve: resolveToolApproval,
|
|
1055
1504
|
listPending: getPendingApprovalsForSession,
|
|
1056
1505
|
},
|
|
1506
|
+
listBackgroundWork: listClaudeSDKBackgroundWork,
|
|
1507
|
+
stopBackgroundTask: stopClaudeSDKTask,
|
|
1057
1508
|
};
|
|
1058
1509
|
|
|
1059
1510
|
// Export public API
|
|
1060
1511
|
export {
|
|
1061
1512
|
queryClaudeSDK,
|
|
1062
1513
|
abortClaudeSDKSession,
|
|
1514
|
+
listClaudeSDKBackgroundWork,
|
|
1515
|
+
stopClaudeSDKTask,
|
|
1063
1516
|
isClaudeSDKSessionActive,
|
|
1517
|
+
getClaudeSDKSessionStartTime,
|
|
1064
1518
|
getActiveClaudeSDKSessions,
|
|
1065
1519
|
resolveToolApproval,
|
|
1066
1520
|
getPendingApprovalsForSession,
|
|
1067
|
-
reconnectSessionWriter
|
|
1521
|
+
reconnectSessionWriter,
|
|
1522
|
+
extractTokenBudget,
|
|
1523
|
+
extractCumulativeTokenBudget
|
|
1068
1524
|
};
|