@cloudcli-ai/cloudcli 1.37.2 → 1.37.3
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-CJntskwA.js} +1 -1
- package/dist/assets/{arc-B8jF-ja9.js → arc-CIfH4uMN.js} +1 -1
- package/dist/assets/{architectureDiagram-T3A2C74G-C_Wur_CU.js → architectureDiagram-T3A2C74G-_IpEkukg.js} +1 -1
- package/dist/assets/{blockDiagram-VBNYF7ZC-Cq4P1i8t.js → blockDiagram-VBNYF7ZC-BttxrpgZ.js} +1 -1
- package/dist/assets/{c4Diagram-5PPSVZJV-C1wG4eUj.js → c4Diagram-5PPSVZJV-g51dSRr1.js} +1 -1
- package/dist/assets/channel-C5xU9jMw.js +1 -0
- package/dist/assets/{chunk-2GRJ4B5K-B8fnBG6E.js → chunk-2GRJ4B5K-D_-LbQUa.js} +1 -1
- package/dist/assets/{chunk-2Q5K7J3B-WdfPooPJ.js → chunk-2Q5K7J3B-BtlHzwaO.js} +1 -1
- package/dist/assets/{chunk-5RXB4S5H-DhZJuG2E.js → chunk-5RXB4S5H-UzkpvpAT.js} +1 -1
- package/dist/assets/{chunk-5VM5RSS4-B0rjslal.js → chunk-5VM5RSS4-BKwx5Gag.js} +1 -1
- package/dist/assets/{chunk-6Q2QTUOP-CG2Oj4jL.js → chunk-6Q2QTUOP-D_KEBH5f.js} +1 -1
- package/dist/assets/{chunk-GF5L2VYU-rrl2lRxY.js → chunk-GF5L2VYU-rK2DGyto.js} +1 -1
- package/dist/assets/{chunk-JWPE2WC7-CjVbrVMA.js → chunk-JWPE2WC7-CeBl_N9i.js} +1 -1
- package/dist/assets/{chunk-KBJHAD2P-CRzKgU8S.js → chunk-KBJHAD2P-BiQLWVIr.js} +1 -1
- package/dist/assets/{chunk-RYQCIY6F-DBz4GTZF.js → chunk-RYQCIY6F-C1knVGV5.js} +1 -1
- package/dist/assets/{chunk-XXDRQBXY-wHxalsXk.js → chunk-XXDRQBXY-CtKbJkvF.js} +1 -1
- package/dist/assets/classDiagram-JCYQIIEL-CRrL3vp7.js +1 -0
- package/dist/assets/classDiagram-v2-OCEON4UE-CRrL3vp7.js +1 -0
- package/dist/assets/{cose-bilkent-JH36ORCC-s1hygY8e.js → cose-bilkent-JH36ORCC-DjX8y40Z.js} +1 -1
- package/dist/assets/{cynefin-VYW2F7L2-C4LGV15i.js → cynefin-VYW2F7L2-DM6YeRba.js} +1 -1
- package/dist/assets/{cynefinDiagram-MW4NZA55-C5uhu7B3.js → cynefinDiagram-MW4NZA55-wZJ-vy5B.js} +1 -1
- package/dist/assets/{dagre-VZM6K2ZE-B-OBTewm.js → dagre-VZM6K2ZE-BzP0ejkH.js} +1 -1
- package/dist/assets/{diagram-7IWD3JNH-BjGKQxih.js → diagram-7IWD3JNH-BTL_j23J.js} +1 -1
- package/dist/assets/{diagram-B4RE2ZJO-BMKz1X2L.js → diagram-B4RE2ZJO-Cf1Norgu.js} +1 -1
- package/dist/assets/{diagram-LBJQPF4R-DNh-GhKC.js → diagram-LBJQPF4R-DPiUEKr-.js} +1 -1
- package/dist/assets/{diagram-Q27KOJAE-BaTG9jPT.js → diagram-Q27KOJAE-CTcTpiz5.js} +1 -1
- package/dist/assets/{diagram-UB23O5K3-BUwo2OvJ.js → diagram-UB23O5K3-DPMSdYm0.js} +1 -1
- package/dist/assets/{ebnfDiagram-BXEA7PRR-DAZgfzX2.js → ebnfDiagram-BXEA7PRR-7yvD6uiF.js} +1 -1
- package/dist/assets/{erDiagram-JOGREHBK-1w1CcGEb.js → erDiagram-JOGREHBK-CfgeDoV6.js} +1 -1
- package/dist/assets/{flowDiagram-UKHOOZJN-D4EII8Jj.js → flowDiagram-UKHOOZJN-B27ng5zu.js} +1 -1
- package/dist/assets/{ganttDiagram-PKOTCBZU-ChvJZAgB.js → ganttDiagram-PKOTCBZU-CcC6b_7t.js} +1 -1
- package/dist/assets/{gitGraphDiagram-DS77QQ5N-DtOn41S8.js → gitGraphDiagram-DS77QQ5N-BX5kPGrO.js} +1 -1
- package/dist/assets/index-BTDHQ6Id.css +32 -0
- package/dist/assets/index-sFfz8iBR.js +4931 -0
- package/dist/assets/{infoDiagram-6WML65LV-DN_PVEJZ.js → infoDiagram-6WML65LV-CUP-SlN4.js} +1 -1
- package/dist/assets/{ishikawaDiagram-WSZJBQD7-DbkF1kXb.js → ishikawaDiagram-WSZJBQD7-BTTDhOpX.js} +1 -1
- package/dist/assets/{journeyDiagram-NVQOT4AX-pJwmhddE.js → journeyDiagram-NVQOT4AX-ARXcc61m.js} +1 -1
- package/dist/assets/{kanban-definition-27J2QSJJ-PPAPgNp5.js → kanban-definition-27J2QSJJ-BieMpqMA.js} +1 -1
- package/dist/assets/{linear-iPR-J9f7.js → linear-61vxjRnP.js} +1 -1
- package/dist/assets/{mermaid.core-BEUL86Z6.js → mermaid.core-nhnwkfQx.js} +5 -5
- package/dist/assets/{mindmap-definition-FAOFIHXS-BvZoBib7.js → mindmap-definition-FAOFIHXS-Cm6x5vT1.js} +1 -1
- package/dist/assets/{pegDiagram-VL7TDLO6-DhYreaUd.js → pegDiagram-VL7TDLO6-CcYBq_9H.js} +1 -1
- package/dist/assets/{pieDiagram-7S7Q4E2Y-CrOHlyD0.js → pieDiagram-7S7Q4E2Y-CfvIjfzo.js} +1 -1
- package/dist/assets/{quadrantDiagram-CIZ2JOQS-D4g5yH3t.js → quadrantDiagram-CIZ2JOQS-SgYoWPNb.js} +1 -1
- package/dist/assets/{railroadDiagram-AXF67PYL-Dj39lHlf.js → railroadDiagram-AXF67PYL-D2Mu5E_f.js} +1 -1
- package/dist/assets/{requirementDiagram-LRYGKXZP-1AS4Zgm_.js → requirementDiagram-LRYGKXZP-Azwe_k4Q.js} +1 -1
- package/dist/assets/{sankeyDiagram-W5VNT64P-CKlMn19P.js → sankeyDiagram-W5VNT64P-DN1Ju3PA.js} +1 -1
- package/dist/assets/{sequenceDiagram-SI44F4Z6-B_j3qyvo.js → sequenceDiagram-SI44F4Z6-Bj4qC34S.js} +1 -1
- package/dist/assets/server.browser-CtX3Hdl5.js +27 -0
- package/dist/assets/{sizeCapture-X5ZJPWSS-Bp2Op16v.js → sizeCapture-X5ZJPWSS-Co4PdklG.js} +1 -1
- package/dist/assets/{stateDiagram-OKZ733FA-C0cN6DoY.js → stateDiagram-OKZ733FA-nOvL9dRC.js} +1 -1
- package/dist/assets/stateDiagram-v2-UEYNNEHI-DNv8ppw7.js +1 -0
- package/dist/assets/{swimlanes-SLNWSIFB-BEvXOV9b.js → swimlanes-SLNWSIFB-CCK3wMid.js} +2 -2
- package/dist/assets/swimlanesDiagram-ULZ7WXOC-DnfbNQEp.js +8 -0
- package/dist/assets/{timeline-definition-Z64GVDOM-zdJQinvZ.js → timeline-definition-Z64GVDOM-BC29RNFm.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-pZlZCtEo.js} +1 -1
- package/dist/assets/{wardleyDiagram-T6FBY63Y-B9AlLQup.js → wardleyDiagram-T6FBY63Y-De8k2-zM.js} +1 -1
- package/dist/assets/{xychartDiagram-ELKLHX3M-DTJX48Qg.js → xychartDiagram-ELKLHX3M-Bk8ykH0w.js} +1 -1
- package/dist/index.html +3 -3
- package/dist-server/server/index.js +7 -3
- package/dist-server/server/index.js.map +1 -1
- package/dist-server/server/modules/agent/agent.routes.js +2 -2
- package/dist-server/server/modules/agent/agent.routes.js.map +1 -1
- package/dist-server/server/modules/agent/tests/agent.routes.test.js +30 -1
- 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 +32 -1
- package/dist-server/server/modules/database/migrations.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 +137 -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 +92 -0
- package/dist-server/server/modules/database/schema.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/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/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 +20 -1
- 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 +35 -6
- 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 +168 -38
- package/dist-server/server/modules/providers/list/claude/claude-runtime.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/claude/claude-sessions.provider.js +388 -50
- 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 +16 -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 +60 -49
- 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 +1565 -432
- 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-runtime.provider.js +8 -6
- 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 +258 -6
- package/dist-server/server/modules/providers/list/opencode/opencode-models.provider.js.map +1 -1
- package/dist-server/server/modules/providers/provider.routes.js +24 -0
- package/dist-server/server/modules/providers/provider.routes.js.map +1 -1
- package/dist-server/server/modules/providers/services/provider-capabilities.service.js +14 -0
- package/dist-server/server/modules/providers/services/provider-capabilities.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-history-cache.service.js +87 -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 +179 -9
- package/dist-server/server/modules/providers/services/sessions.service.js.map +1 -1
- package/dist-server/server/modules/providers/tests/claude-auth.test.js +52 -0
- package/dist-server/server/modules/providers/tests/claude-auth.test.js.map +1 -1
- 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-sessions.test.js +465 -39
- package/dist-server/server/modules/providers/tests/claude-sessions.test.js.map +1 -1
- 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-runtime.test.js +51 -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 +304 -15
- package/dist-server/server/modules/providers/tests/codex-sessions.test.js.map +1 -1
- package/dist-server/server/modules/providers/tests/opencode-models.test.js +117 -4
- package/dist-server/server/modules/providers/tests/opencode-models.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/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 +120 -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 +45 -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 +163 -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 +255 -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 +10 -44
- 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 +166 -9
- 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 +9 -3
- package/dist-server/server/modules/websocket/services/shell-websocket.service.js.map +1 -1
- 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/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 +80 -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/utils.js +34 -0
- package/dist-server/server/shared/utils.js.map +1 -1
- package/package.json +19 -16
- package/server/index.ts +11 -4
- package/server/modules/agent/agent.routes.ts +2 -2
- package/server/modules/agent/tests/agent.routes.test.ts +33 -1
- 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 +37 -0
- 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 +196 -3
- package/server/modules/database/repositories/user-preferences.db.ts +80 -0
- package/server/modules/database/schema.ts +96 -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/projects/services/project-delete.service.ts +13 -1
- package/server/modules/projects/services/project-star.service.ts +2 -2
- package/server/modules/providers/index.ts +4 -0
- package/server/modules/providers/list/claude/claude-auth.provider.ts +21 -1
- package/server/modules/providers/list/claude/claude-fork.provider.ts +57 -0
- package/server/modules/providers/list/claude/claude-models.provider.ts +38 -6
- package/server/modules/providers/list/claude/claude-runtime.provider.js +188 -39
- package/server/modules/providers/list/claude/claude-sessions.provider.ts +478 -54
- 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 +16 -0
- package/server/modules/providers/list/codex/{codex-runtime.provider.js → codex-runtime.provider.ts} +93 -66
- package/server/modules/providers/list/codex/codex-session-synchronizer.provider.ts +9 -0
- package/server/modules/providers/list/codex/codex-sessions.provider.ts +1806 -477
- package/server/modules/providers/list/codex/codex.provider.ts +3 -0
- package/server/modules/providers/list/cursor/cursor-runtime.provider.js +8 -6
- package/server/modules/providers/list/cursor/cursor-session-synchronizer.provider.ts +0 -1
- package/server/modules/providers/list/opencode/opencode-models.provider.ts +279 -6
- package/server/modules/providers/provider.routes.ts +31 -0
- package/server/modules/providers/services/provider-capabilities.service.ts +23 -0
- package/server/modules/providers/services/provider-token-usage.service.ts +166 -39
- package/server/modules/providers/services/session-history-cache.service.ts +139 -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 +201 -9
- package/server/modules/providers/tests/claude-auth.test.ts +58 -0
- package/server/modules/providers/tests/claude-models.test.ts +87 -0
- package/server/modules/providers/tests/claude-sessions.test.ts +552 -40
- 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-runtime.test.ts +58 -0
- package/server/modules/providers/tests/codex-session-fork.test.ts +59 -0
- package/server/modules/providers/tests/codex-sessions.test.ts +337 -15
- package/server/modules/providers/tests/opencode-models.test.ts +166 -8
- package/server/modules/providers/tests/provider-token-usage.service.test.ts +188 -1
- package/server/modules/providers/tests/session-fork.test.ts +173 -0
- package/server/modules/providers/tests/session-history-cache.service.test.ts +143 -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 +67 -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 +203 -0
- package/server/modules/scheduled-messages/services/scheduled-messages.service.ts +121 -0
- package/server/modules/scheduled-messages/tests/scheduled-messages.test.ts +310 -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/index.ts +8 -0
- package/server/modules/websocket/services/chat-run-registry.service.ts +17 -50
- package/server/modules/websocket/services/chat-session-writer.service.ts +36 -6
- package/server/modules/websocket/services/chat-websocket.service.ts +244 -15
- package/server/modules/websocket/services/session-upsert-broadcast.service.ts +108 -0
- package/server/modules/websocket/services/shell-websocket.service.ts +10 -3
- 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/session-upsert-broadcast.test.ts +138 -0
- package/server/modules/websocket/tests/shell-websocket.service.test.ts +108 -0
- package/server/shared/claude-cli-path.ts +24 -6
- package/server/shared/index.ts +3 -0
- package/server/shared/interfaces.ts +62 -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/types.ts +123 -2
- package/server/shared/utils.ts +40 -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
|
@@ -40,7 +40,7 @@ export function applyLegacyStarredProjectIds(projectIds: string[]): ApplyLegacyS
|
|
|
40
40
|
continue;
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
if (
|
|
43
|
+
if (project.isStarred) {
|
|
44
44
|
continue;
|
|
45
45
|
}
|
|
46
46
|
|
|
@@ -71,7 +71,7 @@ export function toggleProjectStar(projectId: string): ToggleProjectStarResult {
|
|
|
71
71
|
});
|
|
72
72
|
}
|
|
73
73
|
|
|
74
|
-
const nextStarredState = !
|
|
74
|
+
const nextStarredState = !project.isStarred;
|
|
75
75
|
projectsDb.updateProjectIsStarredById(normalizedProjectId, nextStarredState);
|
|
76
76
|
|
|
77
77
|
return { isStarred: nextStarredState };
|
|
@@ -6,5 +6,9 @@ export { providerRuntimeService } from './services/provider-runtime.service.js';
|
|
|
6
6
|
// providerModelsService: used by Commands to list models and resolve the active session model.
|
|
7
7
|
export { providerModelsService } from './services/provider-models.service.js';
|
|
8
8
|
|
|
9
|
+
// sessionsService: used by the websocket module's chat gateway to resolve an
|
|
10
|
+
// edited message's resume point, which only the providers module can read.
|
|
11
|
+
export { sessionsService } from './services/sessions.service.js';
|
|
12
|
+
|
|
9
13
|
export { initializeSessionsWatcher } from './services/sessions-watcher.service.js';
|
|
10
14
|
export { closeSessionsWatcher } from './services/sessions-watcher.service.js';
|
|
@@ -25,7 +25,9 @@ export class ClaudeProviderAuth implements IProviderAuth {
|
|
|
25
25
|
* Checks whether the Claude Code CLI is available on this host.
|
|
26
26
|
*/
|
|
27
27
|
private checkInstalled(): boolean {
|
|
28
|
-
|
|
28
|
+
// cross-spawn resolves shims and PATHEXT itself, so the bare command is a
|
|
29
|
+
// usable fallback here even where the SDK's raw spawn could not use it.
|
|
30
|
+
const cliPath = resolveClaudeCodeExecutablePath(process.env.CLAUDE_CLI_PATH) ?? 'claude';
|
|
29
31
|
try {
|
|
30
32
|
spawn.sync(cliPath, ['--version'], { stdio: 'ignore', timeout: 5000 });
|
|
31
33
|
return true;
|
|
@@ -126,6 +128,24 @@ export class ClaudeProviderAuth implements IProviderAuth {
|
|
|
126
128
|
};
|
|
127
129
|
}
|
|
128
130
|
|
|
131
|
+
// `accessToken` is short-lived (hours). Claude Code renews it silently
|
|
132
|
+
// from `refreshToken` on the next CLI invocation, so an expired access
|
|
133
|
+
// token alongside a live refresh token is still a working login. Before
|
|
134
|
+
// this check, a still-signed-in account read as "login has expired"
|
|
135
|
+
// until something else happened to run the CLI — which is why opening
|
|
136
|
+
// the Shell tab and coming back made Settings flip to Connected.
|
|
137
|
+
const refreshToken = readOptionalString(oauth?.refreshToken);
|
|
138
|
+
const refreshTokenExpiresAt = typeof oauth?.refreshTokenExpiresAt === 'number'
|
|
139
|
+
? oauth.refreshTokenExpiresAt
|
|
140
|
+
: undefined;
|
|
141
|
+
if (refreshToken && (!refreshTokenExpiresAt || Date.now() < refreshTokenExpiresAt)) {
|
|
142
|
+
return {
|
|
143
|
+
authenticated: true,
|
|
144
|
+
email,
|
|
145
|
+
method: 'credentials_file',
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
|
|
129
149
|
return {
|
|
130
150
|
authenticated: false,
|
|
131
151
|
email: null,
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { stat } from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
|
|
4
|
+
import { forkSession as forkClaudeSession } from '@anthropic-ai/claude-agent-sdk';
|
|
5
|
+
|
|
6
|
+
import type { IProviderFork } from '@/shared/interfaces.js';
|
|
7
|
+
import { AppError } from '@/shared/utils.js';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Branches a Claude conversation by copying its transcript into a new session
|
|
11
|
+
* file.
|
|
12
|
+
*
|
|
13
|
+
* The SDK owns this: it remaps every message uuid and rewrites the parentUuid
|
|
14
|
+
* chain, which is what makes the copy resumable rather than just a duplicate
|
|
15
|
+
* file. `upToMessageId` is inclusive of the row it names.
|
|
16
|
+
*/
|
|
17
|
+
export class ClaudeForkProvider implements IProviderFork {
|
|
18
|
+
async forkSession(input: {
|
|
19
|
+
providerSessionId: string;
|
|
20
|
+
jsonlPath: string;
|
|
21
|
+
projectPath: string;
|
|
22
|
+
upToAnchorId?: string;
|
|
23
|
+
title?: string;
|
|
24
|
+
}): Promise<{ providerSessionId: string; jsonlPath: string }> {
|
|
25
|
+
// `dir` is the session's working directory, which the SDK encodes into the
|
|
26
|
+
// `~/.claude/projects/<encoded>` folder name itself — passing that folder
|
|
27
|
+
// makes it encode an already-encoded path and find nothing.
|
|
28
|
+
const { sessionId } = await forkClaudeSession(input.providerSessionId, {
|
|
29
|
+
dir: input.projectPath,
|
|
30
|
+
upToMessageId: input.upToAnchorId,
|
|
31
|
+
title: input.title,
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
if (!sessionId) {
|
|
35
|
+
throw new AppError('Claude did not return a session id for the fork.', {
|
|
36
|
+
code: 'FORK_FAILED',
|
|
37
|
+
statusCode: 502,
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Confirmed rather than assumed: the caller is about to write a database
|
|
42
|
+
// row claiming this file exists, and a half-created row would show up in
|
|
43
|
+
// the sidebar as a session that can never be opened.
|
|
44
|
+
// The fork lands beside the transcript it was copied from.
|
|
45
|
+
const forkedPath = path.join(path.dirname(input.jsonlPath), `${sessionId}.jsonl`);
|
|
46
|
+
try {
|
|
47
|
+
await stat(forkedPath);
|
|
48
|
+
} catch {
|
|
49
|
+
throw new AppError('Claude reported a fork but wrote no transcript for it.', {
|
|
50
|
+
code: 'FORK_FAILED',
|
|
51
|
+
statusCode: 502,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return { providerSessionId: sessionId, jsonlPath: forkedPath };
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -9,6 +9,19 @@ import type {
|
|
|
9
9
|
} from '@/shared/types.js';
|
|
10
10
|
import { buildDefaultProviderCurrentActiveModel } from '@/shared/utils.js';
|
|
11
11
|
|
|
12
|
+
/**
|
|
13
|
+
* Ultracode is not one of the SDK's reasoning-effort levels. Selecting it runs the turn at
|
|
14
|
+
* `xhigh` effort with standing dynamic-workflow orchestration, which the Claude runtime
|
|
15
|
+
* translates into the session-scoped `ultracode` setting. It is therefore only offered on
|
|
16
|
+
* models this catalog already marks as xhigh-capable.
|
|
17
|
+
*/
|
|
18
|
+
export const CLAUDE_ULTRACODE_EFFORT = 'ultracode';
|
|
19
|
+
|
|
20
|
+
const ULTRACODE_EFFORT_OPTION = {
|
|
21
|
+
value: CLAUDE_ULTRACODE_EFFORT,
|
|
22
|
+
description: 'Highest effort plus standing workflow orchestration.',
|
|
23
|
+
};
|
|
24
|
+
|
|
12
25
|
export const CLAUDE_PREDEFINED_MODELS: ProviderModelsDefinition = {
|
|
13
26
|
OPTIONS: [
|
|
14
27
|
{
|
|
@@ -37,6 +50,7 @@ export const CLAUDE_PREDEFINED_MODELS: ProviderModelsDefinition = {
|
|
|
37
50
|
{ value: 'high' },
|
|
38
51
|
{ value: 'xhigh' },
|
|
39
52
|
{ value: 'max' },
|
|
53
|
+
ULTRACODE_EFFORT_OPTION,
|
|
40
54
|
],
|
|
41
55
|
},
|
|
42
56
|
},
|
|
@@ -52,6 +66,7 @@ export const CLAUDE_PREDEFINED_MODELS: ProviderModelsDefinition = {
|
|
|
52
66
|
{ value: 'high' },
|
|
53
67
|
{ value: 'xhigh' },
|
|
54
68
|
{ value: 'max' },
|
|
69
|
+
ULTRACODE_EFFORT_OPTION,
|
|
55
70
|
],
|
|
56
71
|
},
|
|
57
72
|
},
|
|
@@ -67,6 +82,7 @@ export const CLAUDE_PREDEFINED_MODELS: ProviderModelsDefinition = {
|
|
|
67
82
|
{ value: 'high' },
|
|
68
83
|
{ value: 'xhigh' },
|
|
69
84
|
{ value: 'max' },
|
|
85
|
+
ULTRACODE_EFFORT_OPTION,
|
|
70
86
|
],
|
|
71
87
|
},
|
|
72
88
|
},
|
|
@@ -82,6 +98,7 @@ export const CLAUDE_PREDEFINED_MODELS: ProviderModelsDefinition = {
|
|
|
82
98
|
{ value: 'high' },
|
|
83
99
|
{ value: 'xhigh' },
|
|
84
100
|
{ value: 'max' },
|
|
101
|
+
ULTRACODE_EFFORT_OPTION,
|
|
85
102
|
],
|
|
86
103
|
},
|
|
87
104
|
},
|
|
@@ -97,6 +114,7 @@ export const CLAUDE_PREDEFINED_MODELS: ProviderModelsDefinition = {
|
|
|
97
114
|
{ value: 'high' },
|
|
98
115
|
{ value: 'xhigh' },
|
|
99
116
|
{ value: 'max' },
|
|
117
|
+
ULTRACODE_EFFORT_OPTION,
|
|
100
118
|
],
|
|
101
119
|
},
|
|
102
120
|
},
|
|
@@ -112,6 +130,7 @@ export const CLAUDE_PREDEFINED_MODELS: ProviderModelsDefinition = {
|
|
|
112
130
|
{ value: 'high' },
|
|
113
131
|
{ value: 'xhigh' },
|
|
114
132
|
{ value: 'max' },
|
|
133
|
+
ULTRACODE_EFFORT_OPTION,
|
|
115
134
|
],
|
|
116
135
|
},
|
|
117
136
|
},
|
|
@@ -132,6 +151,7 @@ export const CLAUDE_PREDEFINED_MODELS: ProviderModelsDefinition = {
|
|
|
132
151
|
{ value: 'high' },
|
|
133
152
|
{ value: 'xhigh' },
|
|
134
153
|
{ value: 'max' },
|
|
154
|
+
ULTRACODE_EFFORT_OPTION,
|
|
135
155
|
],
|
|
136
156
|
},
|
|
137
157
|
},
|
|
@@ -166,7 +186,15 @@ const ANSI_PATTERN = new RegExp(
|
|
|
166
186
|
'g',
|
|
167
187
|
);
|
|
168
188
|
|
|
169
|
-
|
|
189
|
+
/**
|
|
190
|
+
* Claude Code stamps locally-synthesized rows (API-error placeholders and the
|
|
191
|
+
* like) with `model: "<synthetic>"`. Angle-bracketed values are placeholders,
|
|
192
|
+
* never real model ids, and must not be surfaced as the session's model.
|
|
193
|
+
*/
|
|
194
|
+
const isPlaceholderModel = (model: string): boolean => model.startsWith('<') && model.endsWith('>');
|
|
195
|
+
|
|
196
|
+
/** Exported for tests. */
|
|
197
|
+
export const extractClaudeEventModel = (event: ClaudeInitEvent, sessionId: string): string | null => {
|
|
170
198
|
const eventSessionId = event.sessionId ?? event.session_id;
|
|
171
199
|
if (eventSessionId && eventSessionId !== sessionId) {
|
|
172
200
|
return null;
|
|
@@ -178,12 +206,12 @@ const extractClaudeEventModel = (event: ClaudeInitEvent, sessionId: string): str
|
|
|
178
206
|
}
|
|
179
207
|
|
|
180
208
|
const directModel = event.model?.trim();
|
|
181
|
-
if (directModel) {
|
|
209
|
+
if (directModel && !isPlaceholderModel(directModel)) {
|
|
182
210
|
return directModel;
|
|
183
211
|
}
|
|
184
212
|
|
|
185
213
|
const messageModel = event.message?.model?.trim();
|
|
186
|
-
return messageModel
|
|
214
|
+
return messageModel && !isPlaceholderModel(messageModel) ? messageModel : null;
|
|
187
215
|
};
|
|
188
216
|
|
|
189
217
|
const stripAnsi = (value: string): string => value.replace(ANSI_PATTERN, '');
|
|
@@ -199,13 +227,15 @@ const extractClaudeModelFromTextContent = (content: string): string | null => {
|
|
|
199
227
|
if (localCommandStdout !== null) {
|
|
200
228
|
const cleanedStdout = stripAnsi(localCommandStdout).replace(/\s+/g, ' ').trim();
|
|
201
229
|
const changedModel = /(?:set|changed|switched)\s+model\s+to\s+(.+?)\.?$/i.exec(cleanedStdout);
|
|
202
|
-
|
|
203
|
-
|
|
230
|
+
const stdoutModel = changedModel?.[1]?.trim();
|
|
231
|
+
// A placeholder stdout hit must not shadow a real <model> tag further down.
|
|
232
|
+
if (stdoutModel && !isPlaceholderModel(stdoutModel)) {
|
|
233
|
+
return stdoutModel;
|
|
204
234
|
}
|
|
205
235
|
}
|
|
206
236
|
|
|
207
237
|
const modelTag = extractTaggedContent(content, 'model')?.trim();
|
|
208
|
-
return modelTag
|
|
238
|
+
return modelTag && !isPlaceholderModel(modelTag) ? modelTag : null;
|
|
209
239
|
};
|
|
210
240
|
|
|
211
241
|
const extractClaudeModelFromMessageContent = (content: unknown): string | null => {
|
|
@@ -222,6 +252,8 @@ const extractClaudeModelFromMessageContent = (content: unknown): string | null =
|
|
|
222
252
|
continue;
|
|
223
253
|
}
|
|
224
254
|
|
|
255
|
+
// extractClaudeModelFromTextContent rejects placeholders, so a placeholder
|
|
256
|
+
// part yields null here and a later part can still supply the real model.
|
|
225
257
|
const model = extractClaudeModelFromTextContent(part.text);
|
|
226
258
|
if (model) {
|
|
227
259
|
return model;
|
|
@@ -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,
|
|
@@ -69,6 +72,12 @@ const BG_WAIT_CEILING_MS = 30 * 60 * 1000;
|
|
|
69
72
|
|
|
70
73
|
const TOOLS_REQUIRING_INTERACTION = new Set(['AskUserQuestion', 'ExitPlanMode']);
|
|
71
74
|
|
|
75
|
+
// Ultracode is a session-scoped setting rather than an SDK effort level: it pairs xhigh
|
|
76
|
+
// effort with standing dynamic-workflow orchestration, and the CLI only honours it when
|
|
77
|
+
// Workflows are enabled. The catalog offers it as an effort choice for the picker, so the
|
|
78
|
+
// selection is translated back into the two options the SDK actually understands here.
|
|
79
|
+
const ULTRACODE_SDK_EFFORT = 'xhigh';
|
|
80
|
+
|
|
72
81
|
function resolveClaudeEffort(model, effort, modelsDefinition = CLAUDE_PREDEFINED_MODELS) {
|
|
73
82
|
const selectedModel = modelsDefinition?.OPTIONS?.find((option) => option.value === model) || null;
|
|
74
83
|
const allowedEfforts = selectedModel?.effort?.values
|
|
@@ -78,6 +87,30 @@ function resolveClaudeEffort(model, effort, modelsDefinition = CLAUDE_PREDEFINED
|
|
|
78
87
|
: undefined;
|
|
79
88
|
}
|
|
80
89
|
|
|
90
|
+
/**
|
|
91
|
+
* Writes the resolved effort choice onto the SDK options, expanding `ultracode` into the
|
|
92
|
+
* xhigh effort level plus the session-scoped settings it requires.
|
|
93
|
+
* @param {Object} sdkOptions - SDK options being built
|
|
94
|
+
* @param {string|undefined} resolvedEffort - Catalog-validated effort selection
|
|
95
|
+
*/
|
|
96
|
+
function applyClaudeEffort(sdkOptions, resolvedEffort) {
|
|
97
|
+
if (!resolvedEffort) {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (resolvedEffort !== CLAUDE_ULTRACODE_EFFORT) {
|
|
102
|
+
sdkOptions.effort = resolvedEffort;
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
sdkOptions.effort = ULTRACODE_SDK_EFFORT;
|
|
107
|
+
sdkOptions.settings = {
|
|
108
|
+
...(sdkOptions.settings || {}),
|
|
109
|
+
ultracode: true,
|
|
110
|
+
enableWorkflows: true
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
81
114
|
function createRequestId() {
|
|
82
115
|
if (typeof crypto.randomUUID === 'function') {
|
|
83
116
|
return crypto.randomUUID();
|
|
@@ -183,7 +216,7 @@ function matchesToolPermission(entry, toolName, input) {
|
|
|
183
216
|
}
|
|
184
217
|
|
|
185
218
|
function mapCliOptionsToSDK(options = {}) {
|
|
186
|
-
const { providerSessionId, cwd, toolsSettings, permissionMode, effort } = options;
|
|
219
|
+
const { providerSessionId, cwd, toolsSettings, permissionMode, effort, resumeAnchorId, resumeFromScratch } = options;
|
|
187
220
|
|
|
188
221
|
const sdkOptions = {};
|
|
189
222
|
|
|
@@ -193,7 +226,12 @@ function mapCliOptionsToSDK(options = {}) {
|
|
|
193
226
|
|
|
194
227
|
// Resolve the executable eagerly on Windows because the SDK uses raw child_process.spawn,
|
|
195
228
|
// which does not reliably follow npm's shell wrappers like cross-spawn does.
|
|
196
|
-
|
|
229
|
+
// When nothing resolves the option stays unset on purpose: the SDK then falls back to the
|
|
230
|
+
// binary it ships, which beats handing it a bare `claude` that raw spawn can never launch.
|
|
231
|
+
const claudeExecutablePath = resolveClaudeCodeExecutablePath(process.env.CLAUDE_CLI_PATH);
|
|
232
|
+
if (claudeExecutablePath) {
|
|
233
|
+
sdkOptions.pathToClaudeCodeExecutable = claudeExecutablePath;
|
|
234
|
+
}
|
|
197
235
|
|
|
198
236
|
if (cwd) {
|
|
199
237
|
sdkOptions.cwd = cwd;
|
|
@@ -235,14 +273,11 @@ function mapCliOptionsToSDK(options = {}) {
|
|
|
235
273
|
|
|
236
274
|
sdkOptions.model = options.model || CLAUDE_PREDEFINED_MODELS.DEFAULT;
|
|
237
275
|
|
|
238
|
-
|
|
276
|
+
applyClaudeEffort(sdkOptions, resolveClaudeEffort(
|
|
239
277
|
sdkOptions.model,
|
|
240
278
|
effort,
|
|
241
279
|
options.effortModels || CLAUDE_PREDEFINED_MODELS,
|
|
242
|
-
);
|
|
243
|
-
if (resolvedEffort) {
|
|
244
|
-
sdkOptions.effort = resolvedEffort;
|
|
245
|
-
}
|
|
280
|
+
));
|
|
246
281
|
|
|
247
282
|
sdkOptions.systemPrompt = {
|
|
248
283
|
type: 'preset',
|
|
@@ -252,8 +287,19 @@ function mapCliOptionsToSDK(options = {}) {
|
|
|
252
287
|
sdkOptions.settingSources = ['project', 'user', 'local'];
|
|
253
288
|
|
|
254
289
|
// The SDK resumes with the provider-native session id, never the app id.
|
|
255
|
-
|
|
290
|
+
// `resumeFromScratch` is set when the very first prompt of a conversation was
|
|
291
|
+
// edited: there is nothing before it to resume through, so the turn has to
|
|
292
|
+
// start the conversation over instead.
|
|
293
|
+
if (providerSessionId && !resumeFromScratch) {
|
|
256
294
|
sdkOptions.resume = providerSessionId;
|
|
295
|
+
|
|
296
|
+
// Editing an already-sent message re-runs the conversation truncated just
|
|
297
|
+
// before it. `resumeSessionAt` is inclusive of the uuid it names, so the
|
|
298
|
+
// caller resolves the last row to KEEP and passes that — never the edited
|
|
299
|
+
// turn itself, which would leave the original prompt in context.
|
|
300
|
+
if (resumeAnchorId) {
|
|
301
|
+
sdkOptions.resumeSessionAt = resumeAnchorId;
|
|
302
|
+
}
|
|
257
303
|
}
|
|
258
304
|
|
|
259
305
|
return sdkOptions;
|
|
@@ -339,47 +385,129 @@ function transformMessage(sdkMessage) {
|
|
|
339
385
|
return sdkMessage;
|
|
340
386
|
}
|
|
341
387
|
|
|
388
|
+
/**
|
|
389
|
+
* True for the user bubble the SDK echoes for a subagent's own prompt.
|
|
390
|
+
*
|
|
391
|
+
* Subagent traffic carries `parent_tool_use_id`, so this echo lands in the main
|
|
392
|
+
* thread and stacks a second copy of the prompt right below the Agent tool card
|
|
393
|
+
* that already displays it. It also disappears on reload, because the transcript
|
|
394
|
+
* keeps that turn in the subagent's sidechain rather than the session file.
|
|
395
|
+
* @param {Object} message - Normalized message about to be sent to the client
|
|
396
|
+
* @returns {boolean}
|
|
397
|
+
*/
|
|
398
|
+
export function isSubagentPromptEcho(message) {
|
|
399
|
+
return Boolean(message?.parentToolUseId) && message.role === 'user' && message.kind === 'text';
|
|
400
|
+
}
|
|
401
|
+
|
|
342
402
|
function readNumber(value) {
|
|
343
403
|
const parsed = Number(value);
|
|
344
404
|
return Number.isFinite(parsed) ? parsed : 0;
|
|
345
405
|
}
|
|
346
406
|
|
|
347
407
|
/**
|
|
348
|
-
*
|
|
349
|
-
*
|
|
350
|
-
*
|
|
408
|
+
* @typedef {Object} TokenBudget
|
|
409
|
+
* @property {number} used
|
|
410
|
+
* @property {number} total
|
|
411
|
+
* @property {number} inputTokens
|
|
412
|
+
* @property {number} outputTokens
|
|
413
|
+
* @property {number} [cacheReadTokens]
|
|
414
|
+
* @property {number} [cacheCreationTokens]
|
|
415
|
+
* @property {number} [cacheTokens]
|
|
416
|
+
* @property {{ input: number, output: number }} breakdown
|
|
417
|
+
*/
|
|
418
|
+
|
|
419
|
+
/**
|
|
420
|
+
* Builds a context-window budget from an Anthropic-shaped usage payload.
|
|
421
|
+
*
|
|
422
|
+
* `input_tokens + cache_read + cache_creation` is one request's whole prompt,
|
|
423
|
+
* which is exactly what the context window holds at that moment.
|
|
424
|
+
* @param {Object} messageUsage - Anthropic usage payload
|
|
425
|
+
* @returns {TokenBudget} Token budget object
|
|
426
|
+
*/
|
|
427
|
+
function buildTokenBudget(messageUsage) {
|
|
428
|
+
const directInputTokens = readNumber(messageUsage.input_tokens ?? messageUsage.inputTokens);
|
|
429
|
+
const cacheCreationTokens = readNumber(messageUsage.cache_creation_input_tokens ?? messageUsage.cacheCreationInputTokens ?? messageUsage.cacheCreationTokens);
|
|
430
|
+
const cacheReadTokens = readNumber(messageUsage.cache_read_input_tokens ?? messageUsage.cacheReadInputTokens ?? messageUsage.cacheReadTokens);
|
|
431
|
+
const cacheTokens = cacheCreationTokens + cacheReadTokens;
|
|
432
|
+
const inputTokens = directInputTokens + cacheTokens;
|
|
433
|
+
const outputTokens = readNumber(messageUsage.output_tokens ?? messageUsage.outputTokens);
|
|
434
|
+
const contextWindow = parseInt(process.env.CONTEXT_WINDOW, 10) || 160000;
|
|
435
|
+
|
|
436
|
+
return {
|
|
437
|
+
used: inputTokens + outputTokens,
|
|
438
|
+
total: contextWindow,
|
|
439
|
+
inputTokens,
|
|
440
|
+
outputTokens,
|
|
441
|
+
cacheReadTokens,
|
|
442
|
+
cacheCreationTokens,
|
|
443
|
+
cacheTokens,
|
|
444
|
+
breakdown: {
|
|
445
|
+
input: inputTokens,
|
|
446
|
+
output: outputTokens,
|
|
447
|
+
},
|
|
448
|
+
};
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
/**
|
|
452
|
+
* Extracts the session's context-window usage from an SDK stream message.
|
|
453
|
+
*
|
|
454
|
+
* Only assistant messages describe the context window: each one reports the
|
|
455
|
+
* prompt its own request carried. The turn-ending `result` is deliberately not
|
|
456
|
+
* a source here — see `extractCumulativeTokenBudget`.
|
|
351
457
|
* @param {Object} sdkMessage - SDK stream message
|
|
352
|
-
* @returns {
|
|
458
|
+
* @returns {TokenBudget|null} Token budget object or null
|
|
353
459
|
*/
|
|
354
460
|
function extractTokenBudget(sdkMessage) {
|
|
355
461
|
if (!sdkMessage || typeof sdkMessage !== 'object') {
|
|
356
462
|
return null;
|
|
357
463
|
}
|
|
358
464
|
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
const inputTokens = directInputTokens + cacheTokens;
|
|
366
|
-
const outputTokens = readNumber(messageUsage.output_tokens ?? messageUsage.outputTokens);
|
|
367
|
-
const totalUsed = inputTokens + outputTokens;
|
|
368
|
-
const contextWindow = parseInt(process.env.CONTEXT_WINDOW, 10) || 160000;
|
|
465
|
+
// Subagent traffic (parent_tool_use_id set) reports the subagent's own
|
|
466
|
+
// context window, not this session's — surfacing it makes the counter drop
|
|
467
|
+
// to the subagent's number and bounce back on the next main-thread event.
|
|
468
|
+
if (sdkMessage.parent_tool_use_id) {
|
|
469
|
+
return null;
|
|
470
|
+
}
|
|
369
471
|
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
472
|
+
// Only assistant messages carry Anthropic-shaped usage. System
|
|
473
|
+
// task_progress/task_notification events have a top-level `usage` too, but
|
|
474
|
+
// shaped {total_tokens, tool_uses, duration_ms} — reading Anthropic keys
|
|
475
|
+
// off it yields an all-zero budget that flashes "0" in the composer.
|
|
476
|
+
if (sdkMessage.type !== 'assistant') {
|
|
477
|
+
return null;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
const messageUsage = sdkMessage.message?.usage;
|
|
481
|
+
if (!messageUsage || typeof messageUsage !== 'object') {
|
|
482
|
+
return null;
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
return buildTokenBudget(messageUsage);
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
/**
|
|
489
|
+
* Last-resort budget read from a turn's `result` message.
|
|
490
|
+
*
|
|
491
|
+
* `result.usage` and `result.modelUsage` are the turn's *bill*: every request
|
|
492
|
+
* the turn made, summed, including each subagent's. A turn that made four
|
|
493
|
+
* requests therefore reports roughly four times the context the conversation
|
|
494
|
+
* actually holds, so publishing it made the counter leap at the end of a turn
|
|
495
|
+
* and fall back on the next assistant message — worst with subagents running,
|
|
496
|
+
* whose requests inflate the sum without ever entering this session's context.
|
|
497
|
+
*
|
|
498
|
+
* It is still the only usage an SDK build that reports none per assistant
|
|
499
|
+
* message ever emits, so it stays available for the caller to use when a turn
|
|
500
|
+
* produced no assistant budget at all.
|
|
501
|
+
* @param {Object} sdkMessage - SDK stream message
|
|
502
|
+
* @returns {TokenBudget|null} Token budget object or null
|
|
503
|
+
*/
|
|
504
|
+
function extractCumulativeTokenBudget(sdkMessage) {
|
|
505
|
+
if (!sdkMessage || typeof sdkMessage !== 'object' || sdkMessage.type !== 'result') {
|
|
506
|
+
return null;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
if (sdkMessage.usage && typeof sdkMessage.usage === 'object') {
|
|
510
|
+
return buildTokenBudget(sdkMessage.usage);
|
|
383
511
|
}
|
|
384
512
|
|
|
385
513
|
if (!sdkMessage.modelUsage || typeof sdkMessage.modelUsage !== 'object') {
|
|
@@ -599,6 +727,9 @@ async function queryClaudeSDK(command, options = {}, ws, context) {
|
|
|
599
727
|
// True while the process is being held open for background work, so a later
|
|
600
728
|
// `result` can be recognised as that work reporting back.
|
|
601
729
|
let heldForBackgroundWork = false;
|
|
730
|
+
// Set once a turn publishes a budget read from an assistant message, so the
|
|
731
|
+
// turn-ending `result` is only mined for usage when nothing better arrived.
|
|
732
|
+
let assistantBudgetSent = false;
|
|
602
733
|
|
|
603
734
|
// A new turn supersedes any earlier one still holding this session's process
|
|
604
735
|
// open, so held runs cannot stack up across a conversation.
|
|
@@ -736,6 +867,13 @@ async function queryClaudeSDK(command, options = {}, ws, context) {
|
|
|
736
867
|
return { behavior: 'deny', message: 'Permission request cancelled' };
|
|
737
868
|
}
|
|
738
869
|
|
|
870
|
+
// A client answered. Announce it on the run stream so the replay buffer
|
|
871
|
+
// and every other attached tab drop the prompt — resolving happens over
|
|
872
|
+
// the inbound socket only, so without this a mid-run page refresh
|
|
873
|
+
// replays the `permission_request` with nothing to retract it and the
|
|
874
|
+
// already-answered prompt resurrects.
|
|
875
|
+
ws.send(createNormalizedMessage({ kind: 'permission_resolved', requestId, sessionId: capturedSessionId || sessionId || null, provider: 'claude' }));
|
|
876
|
+
|
|
739
877
|
if (decision.allow) {
|
|
740
878
|
if (decision.rememberEntry && typeof decision.rememberEntry === 'string') {
|
|
741
879
|
if (!sdkOptions.allowedTools.includes(decision.rememberEntry)) {
|
|
@@ -812,12 +950,21 @@ async function queryClaudeSDK(command, options = {}, ws, context) {
|
|
|
812
950
|
if (transformedMessage.parentToolUseId && !msg.parentToolUseId) {
|
|
813
951
|
msg.parentToolUseId = transformedMessage.parentToolUseId;
|
|
814
952
|
}
|
|
953
|
+
if (isSubagentPromptEcho(msg)) {
|
|
954
|
+
continue;
|
|
955
|
+
}
|
|
815
956
|
ws.send(msg);
|
|
816
957
|
}
|
|
817
958
|
|
|
818
|
-
// Extract and send token budget updates from assistant
|
|
819
|
-
|
|
959
|
+
// Extract and send token budget updates from assistant usage payloads,
|
|
960
|
+
// falling back to the turn's cumulative bill only for SDK builds that
|
|
961
|
+
// report no per-assistant usage at all.
|
|
962
|
+
const tokenBudgetData = extractTokenBudget(message)
|
|
963
|
+
|| (assistantBudgetSent ? null : extractCumulativeTokenBudget(message));
|
|
820
964
|
if (tokenBudgetData) {
|
|
965
|
+
if (message.type === 'assistant') {
|
|
966
|
+
assistantBudgetSent = true;
|
|
967
|
+
}
|
|
821
968
|
ws.send(createNormalizedMessage({ kind: 'status', text: 'token_budget', tokenBudget: tokenBudgetData, sessionId: capturedSessionId || sessionId || null, provider: 'claude' }));
|
|
822
969
|
}
|
|
823
970
|
|
|
@@ -1064,5 +1211,7 @@ export {
|
|
|
1064
1211
|
getActiveClaudeSDKSessions,
|
|
1065
1212
|
resolveToolApproval,
|
|
1066
1213
|
getPendingApprovalsForSession,
|
|
1067
|
-
reconnectSessionWriter
|
|
1214
|
+
reconnectSessionWriter,
|
|
1215
|
+
extractTokenBudget,
|
|
1216
|
+
extractCumulativeTokenBudget
|
|
1068
1217
|
};
|