@cloudcli-ai/cloudcli 1.37.3 → 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 +1 -1
- package/dist/assets/{abnfDiagram-N423BO3Z-CJntskwA.js → abnfDiagram-N423BO3Z-Dmkwx38H.js} +1 -1
- package/dist/assets/{arc-CIfH4uMN.js → arc-DLe9AYuO.js} +1 -1
- package/dist/assets/{architectureDiagram-T3A2C74G-_IpEkukg.js → architectureDiagram-T3A2C74G-CBbbbNUW.js} +1 -1
- package/dist/assets/{blockDiagram-VBNYF7ZC-BttxrpgZ.js → blockDiagram-VBNYF7ZC-CaMEaWu4.js} +1 -1
- package/dist/assets/{c4Diagram-5PPSVZJV-g51dSRr1.js → c4Diagram-5PPSVZJV-DhjMgNnR.js} +1 -1
- package/dist/assets/channel-CWrmwsir.js +1 -0
- package/dist/assets/{chunk-2GRJ4B5K-D_-LbQUa.js → chunk-2GRJ4B5K-yjl3-F-i.js} +1 -1
- package/dist/assets/{chunk-2Q5K7J3B-BtlHzwaO.js → chunk-2Q5K7J3B-DsAYaWEP.js} +1 -1
- package/dist/assets/{chunk-5RXB4S5H-UzkpvpAT.js → chunk-5RXB4S5H-Bt5qmqOX.js} +1 -1
- package/dist/assets/{chunk-5VM5RSS4-BKwx5Gag.js → chunk-5VM5RSS4-yWrWVvUt.js} +1 -1
- package/dist/assets/{chunk-6Q2QTUOP-D_KEBH5f.js → chunk-6Q2QTUOP-Dt66K5xb.js} +1 -1
- package/dist/assets/{chunk-GF5L2VYU-rK2DGyto.js → chunk-GF5L2VYU-DA0vzaj4.js} +1 -1
- package/dist/assets/{chunk-JWPE2WC7-CeBl_N9i.js → chunk-JWPE2WC7-CUkbx96V.js} +1 -1
- package/dist/assets/{chunk-KBJHAD2P-BiQLWVIr.js → chunk-KBJHAD2P-DWKxx1WH.js} +1 -1
- package/dist/assets/{chunk-RYQCIY6F-C1knVGV5.js → chunk-RYQCIY6F-BqluplN0.js} +1 -1
- package/dist/assets/{chunk-XXDRQBXY-CtKbJkvF.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-DjX8y40Z.js → cose-bilkent-JH36ORCC-DHV93-Em.js} +1 -1
- package/dist/assets/{cynefin-VYW2F7L2-DM6YeRba.js → cynefin-VYW2F7L2-B9HZgoGX.js} +1 -1
- package/dist/assets/{cynefinDiagram-MW4NZA55-wZJ-vy5B.js → cynefinDiagram-MW4NZA55-hFjjEQg8.js} +1 -1
- package/dist/assets/{dagre-VZM6K2ZE-BzP0ejkH.js → dagre-VZM6K2ZE-CpeVOC7V.js} +1 -1
- package/dist/assets/{diagram-7IWD3JNH-BTL_j23J.js → diagram-7IWD3JNH-D0H1UoRA.js} +1 -1
- package/dist/assets/{diagram-B4RE2ZJO-Cf1Norgu.js → diagram-B4RE2ZJO-6Y7BQUup.js} +1 -1
- package/dist/assets/{diagram-LBJQPF4R-DPiUEKr-.js → diagram-LBJQPF4R-C0nNOwht.js} +1 -1
- package/dist/assets/{diagram-Q27KOJAE-CTcTpiz5.js → diagram-Q27KOJAE-En6QlOVX.js} +1 -1
- package/dist/assets/{diagram-UB23O5K3-DPMSdYm0.js → diagram-UB23O5K3-CmPiNeGD.js} +1 -1
- package/dist/assets/{ebnfDiagram-BXEA7PRR-7yvD6uiF.js → ebnfDiagram-BXEA7PRR-Bygzp1Qt.js} +1 -1
- package/dist/assets/{erDiagram-JOGREHBK-CfgeDoV6.js → erDiagram-JOGREHBK-CwScRfzl.js} +1 -1
- package/dist/assets/{flowDiagram-UKHOOZJN-B27ng5zu.js → flowDiagram-UKHOOZJN-bKX-0xP6.js} +1 -1
- package/dist/assets/{ganttDiagram-PKOTCBZU-CcC6b_7t.js → ganttDiagram-PKOTCBZU-DtZPKXGd.js} +1 -1
- package/dist/assets/{gitGraphDiagram-DS77QQ5N-BX5kPGrO.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-CUP-SlN4.js → infoDiagram-6WML65LV-ciRQTS0h.js} +1 -1
- package/dist/assets/{ishikawaDiagram-WSZJBQD7-BTTDhOpX.js → ishikawaDiagram-WSZJBQD7-CgjhQZ84.js} +1 -1
- package/dist/assets/{journeyDiagram-NVQOT4AX-ARXcc61m.js → journeyDiagram-NVQOT4AX-BsbQSeIW.js} +1 -1
- package/dist/assets/{kanban-definition-27J2QSJJ-BieMpqMA.js → kanban-definition-27J2QSJJ-CrwzY9Jw.js} +1 -1
- package/dist/assets/{linear-61vxjRnP.js → linear-BUaCDr6l.js} +1 -1
- package/dist/assets/{mermaid.core-nhnwkfQx.js → mermaid.core-ClgdUU0j.js} +5 -5
- package/dist/assets/{mindmap-definition-FAOFIHXS-Cm6x5vT1.js → mindmap-definition-FAOFIHXS-BictljY1.js} +1 -1
- package/dist/assets/{pegDiagram-VL7TDLO6-CcYBq_9H.js → pegDiagram-VL7TDLO6-BXdByy_U.js} +1 -1
- package/dist/assets/{pieDiagram-7S7Q4E2Y-CfvIjfzo.js → pieDiagram-7S7Q4E2Y-uZsMfkRX.js} +1 -1
- package/dist/assets/{quadrantDiagram-CIZ2JOQS-SgYoWPNb.js → quadrantDiagram-CIZ2JOQS-7lCv4QIj.js} +1 -1
- package/dist/assets/{railroadDiagram-AXF67PYL-D2Mu5E_f.js → railroadDiagram-AXF67PYL-07dxIWbi.js} +1 -1
- package/dist/assets/{requirementDiagram-LRYGKXZP-Azwe_k4Q.js → requirementDiagram-LRYGKXZP-Cb31Yv1_.js} +1 -1
- package/dist/assets/{sankeyDiagram-W5VNT64P-DN1Ju3PA.js → sankeyDiagram-W5VNT64P-X0YPSyF9.js} +1 -1
- package/dist/assets/{sequenceDiagram-SI44F4Z6-Bj4qC34S.js → sequenceDiagram-SI44F4Z6-BdBxSIlP.js} +1 -1
- package/dist/assets/{sizeCapture-X5ZJPWSS-Co4PdklG.js → sizeCapture-X5ZJPWSS-DQM5xAyD.js} +1 -1
- package/dist/assets/{stateDiagram-OKZ733FA-nOvL9dRC.js → stateDiagram-OKZ733FA-DJ3xkLUF.js} +1 -1
- package/dist/assets/{stateDiagram-v2-UEYNNEHI-DNv8ppw7.js → stateDiagram-v2-UEYNNEHI-vAaE4zNo.js} +1 -1
- package/dist/assets/{swimlanes-SLNWSIFB-CCK3wMid.js → swimlanes-SLNWSIFB-rjfYdW0w.js} +2 -2
- package/dist/assets/swimlanesDiagram-ULZ7WXOC-TV2IF4Nq.js +8 -0
- package/dist/assets/{timeline-definition-Z64GVDOM-BC29RNFm.js → timeline-definition-Z64GVDOM-DZMra5do.js} +1 -1
- package/dist/assets/{vennDiagram-T6HMQDX7-pZlZCtEo.js → vennDiagram-T6HMQDX7-BevD8uu0.js} +1 -1
- package/dist/assets/{wardleyDiagram-T6FBY63Y-De8k2-zM.js → wardleyDiagram-T6FBY63Y-DsIyXB7z.js} +1 -1
- package/dist/assets/{xychartDiagram-ELKLHX3M-Bk8ykH0w.js → xychartDiagram-ELKLHX3M-G3PlF3ko.js} +1 -1
- package/dist/index.html +2 -2
- package/dist-server/server/index.js +4 -1
- 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 +175 -128
- 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 +18 -1
- package/dist-server/server/modules/agent/tests/agent.routes.test.js.map +1 -1
- package/dist-server/server/modules/database/migrations.js +13 -0
- 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/sessions.db.js +7 -0
- package/dist-server/server/modules/database/repositories/sessions.db.js.map +1 -1
- package/dist-server/server/modules/database/schema.js +7 -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/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/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/list/claude/claude-auth.provider.js +42 -8
- package/dist-server/server/modules/providers/list/claude/claude-auth.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/claude/claude-models.provider.js +6 -16
- 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 +305 -16
- 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 +686 -77
- package/dist-server/server/modules/providers/list/claude/claude-sessions.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/codex/codex-models.provider.js +31 -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 +11 -4
- package/dist-server/server/modules/providers/list/codex/codex-runtime.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/codex/codex-sessions.provider.js +57 -0
- package/dist-server/server/modules/providers/list/codex/codex-sessions.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 +8 -2
- package/dist-server/server/modules/providers/list/cursor/cursor-runtime.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/opencode/opencode-models.provider.js +16 -3
- 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 +110 -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 +9 -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/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 +4 -0
- package/dist-server/server/modules/providers/services/session-history-cache.service.js.map +1 -1
- package/dist-server/server/modules/providers/services/sessions.service.js +73 -3
- package/dist-server/server/modules/providers/services/sessions.service.js.map +1 -1
- package/dist-server/server/modules/providers/tests/claude-auth.test.js +131 -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-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 +1368 -12
- 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/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 +32 -0
- package/dist-server/server/modules/providers/tests/codex-runtime.test.js.map +1 -1
- package/dist-server/server/modules/providers/tests/codex-sessions.test.js +50 -0
- 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 +79 -0
- 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.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-history-cache.service.test.js +19 -0
- package/dist-server/server/modules/providers/tests/session-history-cache.service.test.js.map +1 -1
- package/dist-server/server/modules/providers/tests/sessions.service.test.js +175 -1
- package/dist-server/server/modules/providers/tests/sessions.service.test.js.map +1 -1
- package/dist-server/server/modules/scheduled-messages/services/scheduled-message-dispatcher.service.js +13 -1
- package/dist-server/server/modules/scheduled-messages/services/scheduled-message-dispatcher.service.js.map +1 -1
- package/dist-server/server/modules/scheduled-messages/tests/scheduled-messages.test.js +36 -1
- package/dist-server/server/modules/scheduled-messages/tests/scheduled-messages.test.js.map +1 -1
- package/dist-server/server/modules/websocket/services/chat-run-registry.service.js +27 -5
- package/dist-server/server/modules/websocket/services/chat-run-registry.service.js.map +1 -1
- package/dist-server/server/modules/websocket/services/chat-websocket.service.js +38 -1
- package/dist-server/server/modules/websocket/services/chat-websocket.service.js.map +1 -1
- package/dist-server/server/modules/websocket/services/shell-websocket.service.js +230 -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-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/shell-websocket.service.test.js +420 -0
- package/dist-server/server/modules/websocket/tests/shell-websocket.service.test.js.map +1 -1
- package/dist-server/server/shared/message-unification.js +1 -1
- package/dist-server/server/shared/message-unification.js.map +1 -1
- 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 +126 -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 +4 -4
- package/server/index.ts +5 -1
- package/server/modules/agent/agent.module.ts +11 -1
- package/server/modules/agent/agent.routes.ts +198 -140
- package/server/modules/agent/tests/agent-run-registration.test.ts +348 -0
- package/server/modules/agent/tests/agent.routes.test.ts +18 -1
- package/server/modules/database/migrations.ts +14 -0
- package/server/modules/database/repositories/provider-models.ts +79 -6
- package/server/modules/database/repositories/sessions.db.ts +8 -0
- package/server/modules/database/schema.ts +7 -0
- package/server/modules/database/tests/provider-models.db.integration.test.ts +113 -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/tests/project-clone.routes.test.ts +246 -0
- package/server/modules/projects/tests/project-clone.service.test.ts +376 -0
- package/server/modules/providers/list/claude/claude-auth.provider.ts +60 -10
- package/server/modules/providers/list/claude/claude-models.provider.ts +10 -22
- package/server/modules/providers/list/claude/claude-runtime.provider.js +322 -15
- package/server/modules/providers/list/claude/claude-session-synchronizer.provider.ts +40 -14
- package/server/modules/providers/list/claude/claude-sessions.provider.ts +796 -88
- package/server/modules/providers/list/codex/codex-models.provider.ts +31 -0
- package/server/modules/providers/list/codex/codex-runtime.provider.ts +12 -4
- package/server/modules/providers/list/codex/codex-sessions.provider.ts +63 -0
- package/server/modules/providers/list/cursor/cursor-auth.provider.ts +10 -2
- package/server/modules/providers/list/cursor/cursor-runtime.provider.js +9 -2
- package/server/modules/providers/list/opencode/opencode-models.provider.ts +18 -3
- 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 +131 -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 +17 -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/session-conversations-search.service.ts +2 -5
- package/server/modules/providers/services/session-history-cache.service.ts +13 -2
- package/server/modules/providers/services/sessions.service.ts +108 -8
- package/server/modules/providers/tests/claude-auth.test.ts +150 -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-runtime-hold.test.ts +309 -0
- package/server/modules/providers/tests/claude-sessions.test.ts +1630 -12
- package/server/modules/providers/tests/claude-settings-permissions.test.ts +271 -0
- package/server/modules/providers/tests/codex-models.test.ts +37 -0
- package/server/modules/providers/tests/codex-runtime.test.ts +36 -0
- package/server/modules/providers/tests/codex-sessions.test.ts +59 -0
- package/server/modules/providers/tests/cursor-auth.test.ts +187 -0
- package/server/modules/providers/tests/opencode-models.test.ts +100 -0
- 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.routes.test.ts +230 -1
- package/server/modules/providers/tests/session-history-cache.service.test.ts +22 -0
- package/server/modules/providers/tests/sessions.service.test.ts +237 -1
- package/server/modules/scheduled-messages/services/scheduled-message-dispatcher.service.ts +14 -1
- package/server/modules/scheduled-messages/tests/scheduled-messages.test.ts +45 -1
- package/server/modules/websocket/README.md +4 -2
- package/server/modules/websocket/services/chat-run-registry.service.ts +29 -5
- package/server/modules/websocket/services/chat-websocket.service.ts +58 -1
- package/server/modules/websocket/services/shell-websocket.service.ts +269 -22
- package/server/modules/websocket/tests/chat-background-subscribe.test.ts +167 -0
- package/server/modules/websocket/tests/chat-stop-task.test.ts +127 -0
- package/server/modules/websocket/tests/shell-websocket.service.test.ts +521 -0
- package/server/shared/interfaces.ts +29 -0
- package/server/shared/message-unification.ts +1 -1
- 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 +261 -7
- package/server/shared/utils.ts +136 -0
- package/dist/assets/channel-C5xU9jMw.js +0 -1
- package/dist/assets/classDiagram-JCYQIIEL-CRrL3vp7.js +0 -1
- package/dist/assets/classDiagram-v2-OCEON4UE-CRrL3vp7.js +0 -1
- package/dist/assets/index-BTDHQ6Id.css +0 -32
- package/dist/assets/index-sFfz8iBR.js +0 -4931
- package/dist/assets/swimlanesDiagram-ULZ7WXOC-DnfbNQEp.js +0 -8
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import { readFile, stat } from 'node:fs/promises';
|
|
2
|
+
import os from 'node:os';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
|
|
5
|
+
import { readObjectRecord, readStringArray } from '@/shared/utils.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* The permission rules one Claude settings file contributes.
|
|
9
|
+
*
|
|
10
|
+
* `status` says whether the rules could be read at all: `missing` means there
|
|
11
|
+
* is no such file, `invalid` means it exists but the CLI would not load it
|
|
12
|
+
* either (unreadable, over its size cap, or not a JSON object). Both report
|
|
13
|
+
* empty rule lists rather than failing the request, so one bad file never hides
|
|
14
|
+
* the others.
|
|
15
|
+
*/
|
|
16
|
+
type ClaudeSettingsPermissionSource = {
|
|
17
|
+
scope: 'user' | 'managed';
|
|
18
|
+
path: string;
|
|
19
|
+
status: 'ok' | 'missing' | 'invalid';
|
|
20
|
+
allow: string[];
|
|
21
|
+
deny: string[];
|
|
22
|
+
ask: string[];
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* The user's own settings file. The CLI reads it from `CLAUDE_CONFIG_DIR` when
|
|
27
|
+
* that is set, and the server forwards its whole env to every CLI it spawns,
|
|
28
|
+
* so the same override has to apply here.
|
|
29
|
+
*/
|
|
30
|
+
const userSettingsPath = (): string => {
|
|
31
|
+
const configDirectory = process.env.CLAUDE_CONFIG_DIR?.trim();
|
|
32
|
+
const baseDirectory = configDirectory && path.isAbsolute(configDirectory)
|
|
33
|
+
? configDirectory
|
|
34
|
+
: path.join(os.homedir(), '.claude');
|
|
35
|
+
return path.join(baseDirectory, 'settings.json');
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
/** The administrator-managed settings file, at the fixed path the CLI uses per OS. */
|
|
39
|
+
const managedSettingsPath = (): string => {
|
|
40
|
+
if (process.platform === 'darwin') {
|
|
41
|
+
return '/Library/Application Support/ClaudeCode/managed-settings.json';
|
|
42
|
+
}
|
|
43
|
+
if (process.platform === 'win32') {
|
|
44
|
+
return 'C:\\Program Files\\ClaudeCode\\managed-settings.json';
|
|
45
|
+
}
|
|
46
|
+
return '/etc/claude-code/managed-settings.json';
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
// The CLI refuses to load a settings file larger than this (2 MB).
|
|
50
|
+
const MAX_SETTINGS_FILE_BYTES = 2 * 1024 * 1024;
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Decodes a settings file the way the CLI does: UTF-16LE when it starts with
|
|
54
|
+
* that byte order mark, UTF-8 otherwise, without the leading BOM. Windows
|
|
55
|
+
* editors and PowerShell often save one, and the CLI still applies such files.
|
|
56
|
+
*/
|
|
57
|
+
const decodeSettingsFile = (bytes: Buffer): string => {
|
|
58
|
+
const isUtf16LittleEndian = bytes.length >= 2 && bytes[0] === 0xff && bytes[1] === 0xfe;
|
|
59
|
+
return bytes.toString(isUtf16LittleEndian ? 'utf16le' : 'utf8').replace(/^\uFEFF/, '');
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
/** Keeps non-empty string rules only, without duplicates, in file order. */
|
|
63
|
+
const readRuleList = (value: unknown): string[] => [
|
|
64
|
+
...new Set((readStringArray(value) ?? []).map((rule) => rule.trim()).filter(Boolean)),
|
|
65
|
+
];
|
|
66
|
+
|
|
67
|
+
async function readPermissionSource(
|
|
68
|
+
scope: ClaudeSettingsPermissionSource['scope'],
|
|
69
|
+
filePath: string,
|
|
70
|
+
): Promise<ClaudeSettingsPermissionSource> {
|
|
71
|
+
const empty = { scope, path: filePath, allow: [], deny: [], ask: [] };
|
|
72
|
+
|
|
73
|
+
let content: string;
|
|
74
|
+
try {
|
|
75
|
+
if ((await stat(filePath)).size > MAX_SETTINGS_FILE_BYTES) {
|
|
76
|
+
return { ...empty, status: 'invalid' };
|
|
77
|
+
}
|
|
78
|
+
content = decodeSettingsFile(await readFile(filePath));
|
|
79
|
+
} catch (error) {
|
|
80
|
+
const code = (error as NodeJS.ErrnoException).code;
|
|
81
|
+
return { ...empty, status: code === 'ENOENT' || code === 'ENOTDIR' ? 'missing' : 'invalid' };
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// The CLI loads an empty or whitespace-only file as `{}`.
|
|
85
|
+
if (!content.trim()) {
|
|
86
|
+
return { ...empty, status: 'ok' };
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
let settings: Record<string, unknown> | null;
|
|
90
|
+
try {
|
|
91
|
+
settings = readObjectRecord(JSON.parse(content));
|
|
92
|
+
} catch {
|
|
93
|
+
settings = null;
|
|
94
|
+
}
|
|
95
|
+
if (!settings) {
|
|
96
|
+
return { ...empty, status: 'invalid' };
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// A file without a `permissions` block is perfectly valid; it just adds no rules.
|
|
100
|
+
const permissions = readObjectRecord(settings.permissions) ?? {};
|
|
101
|
+
return {
|
|
102
|
+
...empty,
|
|
103
|
+
status: 'ok',
|
|
104
|
+
allow: readRuleList(permissions.allow),
|
|
105
|
+
deny: readRuleList(permissions.deny),
|
|
106
|
+
ask: readRuleList(permissions.ask),
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Read-only view of the permission rules the Claude CLI applies from its own
|
|
112
|
+
* settings files, on top of the allowed/blocked lists CloudCLI passes it.
|
|
113
|
+
*
|
|
114
|
+
* Used by provider.routes.ts for the Claude permissions panel in Settings.
|
|
115
|
+
* Project-level files are left out on purpose: the panel has no project, and
|
|
116
|
+
* those files only apply to chats started inside their project. CloudCLI never
|
|
117
|
+
* writes to any of these files.
|
|
118
|
+
*/
|
|
119
|
+
export const claudeSettingsPermissionsService = {
|
|
120
|
+
/** `managedPath` is only overridden by tests, which cannot write to the system path. */
|
|
121
|
+
async listRuleSources(managedPath: string = managedSettingsPath()): Promise<ClaudeSettingsPermissionSource[]> {
|
|
122
|
+
return Promise.all([
|
|
123
|
+
readPermissionSource('user', userSettingsPath()),
|
|
124
|
+
readPermissionSource('managed', managedPath),
|
|
125
|
+
]);
|
|
126
|
+
},
|
|
127
|
+
};
|
|
@@ -25,6 +25,14 @@ type ProviderCapabilities = {
|
|
|
25
25
|
supportsTokenUsage: boolean;
|
|
26
26
|
/** Whether the provider runtime can accept model-level reasoning effort. */
|
|
27
27
|
supportsEffort: boolean;
|
|
28
|
+
/**
|
|
29
|
+
* Reasoning-effort levels the runtime can pass on, weakest first, and so the
|
|
30
|
+
* levels a custom model-library entry may declare. Empty exactly when
|
|
31
|
+
* `supportsEffort` is false. Covers every level a predefined model declares
|
|
32
|
+
* (a providers test enforces this) and, unlike the model catalog, does not
|
|
33
|
+
* depend on which upstream providers this machine has connected.
|
|
34
|
+
*/
|
|
35
|
+
effortLevels: string[];
|
|
28
36
|
/**
|
|
29
37
|
* Whether an already-sent message can be replaced, which requires the
|
|
30
38
|
* provider to re-run a conversation truncated at a chosen point.
|
|
@@ -53,6 +61,7 @@ const PROVIDER_CAPABILITIES: Record<LLMProvider, ProviderCapabilities> = {
|
|
|
53
61
|
supportsPermissionRequests: true,
|
|
54
62
|
supportsTokenUsage: true,
|
|
55
63
|
supportsEffort: true,
|
|
64
|
+
effortLevels: ['low', 'medium', 'high', 'xhigh', 'max', 'ultracode'],
|
|
56
65
|
// `resumeSessionAt` re-runs a conversation truncated at a message, and
|
|
57
66
|
// `forkSession` copies a transcript prefix into a new session file.
|
|
58
67
|
supportsMessageEditing: true,
|
|
@@ -68,6 +77,7 @@ const PROVIDER_CAPABILITIES: Record<LLMProvider, ProviderCapabilities> = {
|
|
|
68
77
|
supportsPermissionRequests: false,
|
|
69
78
|
supportsTokenUsage: false,
|
|
70
79
|
supportsEffort: false,
|
|
80
|
+
effortLevels: [],
|
|
71
81
|
supportsMessageEditing: false,
|
|
72
82
|
supportsSessionForking: false,
|
|
73
83
|
},
|
|
@@ -81,6 +91,7 @@ const PROVIDER_CAPABILITIES: Record<LLMProvider, ProviderCapabilities> = {
|
|
|
81
91
|
supportsPermissionRequests: false,
|
|
82
92
|
supportsTokenUsage: true,
|
|
83
93
|
supportsEffort: true,
|
|
94
|
+
effortLevels: ['low', 'medium', 'high', 'xhigh', 'max', 'ultra'],
|
|
84
95
|
// Not from the Codex SDK, which only starts and resumes threads: both ride
|
|
85
96
|
// the same CLI's `app-server` protocol, whose `thread/fork` copies a
|
|
86
97
|
// thread up to a chosen turn. Editing is that fork plus a new prompt,
|
|
@@ -101,13 +112,18 @@ const PROVIDER_CAPABILITIES: Record<LLMProvider, ProviderCapabilities> = {
|
|
|
101
112
|
supportsPermissionRequests: false,
|
|
102
113
|
supportsTokenUsage: true,
|
|
103
114
|
supportsEffort: true,
|
|
115
|
+
// Passed on as OpenCode's `--variant`. `thinking` (MiniMax's on/off
|
|
116
|
+
// variant, paired with `none`) is not a strength, so it sorts last.
|
|
117
|
+
effortLevels: ['none', 'minimal', 'low', 'medium', 'high', 'xhigh', 'max', 'thinking'],
|
|
104
118
|
supportsMessageEditing: false,
|
|
105
119
|
supportsSessionForking: false,
|
|
106
120
|
},
|
|
107
121
|
};
|
|
108
122
|
|
|
109
123
|
/**
|
|
110
|
-
* Application service exposing the provider capability matrix.
|
|
124
|
+
* Application service exposing the provider capability matrix. Used by the
|
|
125
|
+
* Providers routes (the capabilities endpoints) and by the Providers model
|
|
126
|
+
* service, which reads `effortLevels` to validate custom-model effort.
|
|
111
127
|
*/
|
|
112
128
|
export const providerCapabilitiesService = {
|
|
113
129
|
getProviderCapabilities(provider: LLMProvider): ProviderCapabilities {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { providerModelsDb, sessionsDb } from '@/modules/database/index.js';
|
|
2
2
|
import { providerRegistry } from '@/modules/providers/provider.registry.js';
|
|
3
|
+
import { providerCapabilitiesService } from '@/modules/providers/services/provider-capabilities.service.js';
|
|
3
4
|
import type { IProvider } from '@/shared/interfaces.js';
|
|
4
5
|
import type {
|
|
5
6
|
CustomProviderModelInput,
|
|
@@ -36,6 +37,15 @@ type ProviderModelsServiceDependencies = {
|
|
|
36
37
|
sessions?: ProviderModelsSessionStore;
|
|
37
38
|
};
|
|
38
39
|
|
|
40
|
+
/**
|
|
41
|
+
* Converts a stored custom row into a catalog option.
|
|
42
|
+
*
|
|
43
|
+
* `effort` is emitted in the same shape predefined models use, so the composer
|
|
44
|
+
* and every runtime's effort validation treat declared levels identically.
|
|
45
|
+
* Rows without declared levels omit `effort` on purpose: the composer then
|
|
46
|
+
* hides Reasoning and runtimes send no effort, instead of guessing levels the
|
|
47
|
+
* model might reject.
|
|
48
|
+
*/
|
|
39
49
|
const toCustomProviderModelOption = (
|
|
40
50
|
record: CustomProviderModelRecord,
|
|
41
51
|
): ProviderModelOption => ({
|
|
@@ -43,9 +53,32 @@ const toCustomProviderModelOption = (
|
|
|
43
53
|
label: record.model,
|
|
44
54
|
recordId: record.recordId,
|
|
45
55
|
isCustom: true,
|
|
56
|
+
...(record.effort
|
|
57
|
+
? {
|
|
58
|
+
effort: {
|
|
59
|
+
...(record.effort.default ? { default: record.effort.default } : {}),
|
|
60
|
+
values: record.effort.values.map((value) => ({ value })),
|
|
61
|
+
},
|
|
62
|
+
}
|
|
63
|
+
: {}),
|
|
46
64
|
});
|
|
47
65
|
|
|
66
|
+
/**
|
|
67
|
+
* Effort levels a custom model of this provider may declare, weakest first:
|
|
68
|
+
* the levels its runtime can pass on, from the capability matrix. Empty
|
|
69
|
+
* (Cursor) means no effort support at all.
|
|
70
|
+
*
|
|
71
|
+
* Deliberately not derived from the predefined models: OpenCode narrows its
|
|
72
|
+
* catalog to the upstream providers this machine has connected, and only its
|
|
73
|
+
* OpenCode Go models declare effort, so a catalog-derived set would vanish for
|
|
74
|
+
* Zen, Anthropic, or OpenAI users and change with unrelated credentials.
|
|
75
|
+
*/
|
|
76
|
+
const readCustomModelEffortLevels = (provider: LLMProvider): string[] => (
|
|
77
|
+
providerCapabilitiesService.getProviderCapabilities(provider).effortLevels
|
|
78
|
+
);
|
|
79
|
+
|
|
48
80
|
const mergeProviderModels = (
|
|
81
|
+
provider: LLMProvider,
|
|
49
82
|
predefined: ProviderModelsDefinition,
|
|
50
83
|
custom: CustomProviderModelRecord[],
|
|
51
84
|
): ProviderModelsDefinition => {
|
|
@@ -55,14 +88,46 @@ const mergeProviderModels = (
|
|
|
55
88
|
...custom.map(toCustomProviderModelOption),
|
|
56
89
|
],
|
|
57
90
|
DEFAULT: predefined.DEFAULT,
|
|
91
|
+
// Copied so no caller can mutate the shared capability matrix.
|
|
92
|
+
EFFORT_LEVELS: [...readCustomModelEffortLevels(provider)],
|
|
58
93
|
};
|
|
59
94
|
};
|
|
60
95
|
|
|
61
96
|
const normalizeCustomModelInput = (input: CustomProviderModelInput): CustomProviderModelInput => ({
|
|
62
97
|
id: input.id.trim(),
|
|
63
98
|
model: input.model.trim(),
|
|
99
|
+
...(input.effort === undefined ? {} : { effort: input.effort }),
|
|
64
100
|
});
|
|
65
101
|
|
|
102
|
+
const assertEffortLevelsSupported = (
|
|
103
|
+
provider: LLMProvider,
|
|
104
|
+
input: CustomProviderModelInput,
|
|
105
|
+
): void => {
|
|
106
|
+
const declaredLevels = input.effort?.values ?? [];
|
|
107
|
+
if (declaredLevels.length === 0) {
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const supportedLevels = readCustomModelEffortLevels(provider);
|
|
112
|
+
if (supportedLevels.length === 0) {
|
|
113
|
+
throw new AppError(`${provider} models do not support reasoning effort.`, {
|
|
114
|
+
code: 'MODEL_EFFORT_NOT_SUPPORTED',
|
|
115
|
+
statusCode: 400,
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const unsupportedLevel = declaredLevels.find((level) => !supportedLevels.includes(level));
|
|
120
|
+
if (unsupportedLevel) {
|
|
121
|
+
throw new AppError(
|
|
122
|
+
`"${unsupportedLevel}" is not a ${provider} effort level. Use one of: ${supportedLevels.join(', ')}.`,
|
|
123
|
+
{
|
|
124
|
+
code: 'INVALID_MODEL_EFFORT',
|
|
125
|
+
statusCode: 400,
|
|
126
|
+
},
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
};
|
|
130
|
+
|
|
66
131
|
const isUniqueConstraintError = (error: unknown): boolean => (
|
|
67
132
|
error !== null
|
|
68
133
|
&& error !== undefined
|
|
@@ -87,7 +152,7 @@ export const createProviderModelsService = (dependencies: ProviderModelsServiceD
|
|
|
87
152
|
|
|
88
153
|
const getProviderModels = async (provider: LLMProvider): Promise<ProviderModelsDefinition> => {
|
|
89
154
|
const predefined = await resolveProvider(provider).models.getSupportedModels();
|
|
90
|
-
return mergeProviderModels(predefined, catalog.listCustomProviderModels(provider));
|
|
155
|
+
return mergeProviderModels(provider, predefined, catalog.listCustomProviderModels(provider));
|
|
91
156
|
};
|
|
92
157
|
|
|
93
158
|
const getCurrentActiveModel = async (
|
|
@@ -139,12 +204,13 @@ export const createProviderModelsService = (dependencies: ProviderModelsServiceD
|
|
|
139
204
|
const predefined = await resolveProvider(provider).models.getSupportedModels();
|
|
140
205
|
const normalized = normalizeCustomModelInput(input);
|
|
141
206
|
assertModelIdAvailable(provider, predefined, normalized.id);
|
|
207
|
+
assertEffortLevelsSupported(provider, normalized);
|
|
142
208
|
|
|
143
209
|
try {
|
|
144
210
|
const created = catalog.createCustomProviderModel(provider, normalized);
|
|
145
211
|
return {
|
|
146
212
|
model: toCustomProviderModelOption(created),
|
|
147
|
-
models: mergeProviderModels(predefined, catalog.listCustomProviderModels(provider)),
|
|
213
|
+
models: mergeProviderModels(provider, predefined, catalog.listCustomProviderModels(provider)),
|
|
148
214
|
};
|
|
149
215
|
} catch (error) {
|
|
150
216
|
if (isUniqueConstraintError(error)) {
|
|
@@ -166,6 +232,7 @@ export const createProviderModelsService = (dependencies: ProviderModelsServiceD
|
|
|
166
232
|
readCustomModel(provider, recordId);
|
|
167
233
|
const normalized = normalizeCustomModelInput(input);
|
|
168
234
|
assertModelIdAvailable(provider, predefined, normalized.id, recordId);
|
|
235
|
+
assertEffortLevelsSupported(provider, normalized);
|
|
169
236
|
|
|
170
237
|
try {
|
|
171
238
|
const updated = catalog.updateCustomProviderModel(provider, recordId, normalized);
|
|
@@ -178,7 +245,7 @@ export const createProviderModelsService = (dependencies: ProviderModelsServiceD
|
|
|
178
245
|
|
|
179
246
|
return {
|
|
180
247
|
model: toCustomProviderModelOption(updated),
|
|
181
|
-
models: mergeProviderModels(predefined, catalog.listCustomProviderModels(provider)),
|
|
248
|
+
models: mergeProviderModels(provider, predefined, catalog.listCustomProviderModels(provider)),
|
|
182
249
|
};
|
|
183
250
|
} catch (error) {
|
|
184
251
|
if (isUniqueConstraintError(error)) {
|
|
@@ -207,7 +274,7 @@ export const createProviderModelsService = (dependencies: ProviderModelsServiceD
|
|
|
207
274
|
|
|
208
275
|
return {
|
|
209
276
|
model: toCustomProviderModelOption(removed),
|
|
210
|
-
models: mergeProviderModels(predefined, catalog.listCustomProviderModels(provider)),
|
|
277
|
+
models: mergeProviderModels(provider, predefined, catalog.listCustomProviderModels(provider)),
|
|
211
278
|
};
|
|
212
279
|
};
|
|
213
280
|
|
|
@@ -91,6 +91,17 @@ export function createProviderRuntimeService(
|
|
|
91
91
|
return Boolean(await dependencies.resolveProvider(providerName).runtime.abort(sessionId));
|
|
92
92
|
},
|
|
93
93
|
|
|
94
|
+
async stopBackgroundTask(providerName: LLMProvider, sessionId: string, taskId: string): Promise<boolean> {
|
|
95
|
+
// A runtime that never holds background work has no task to stop.
|
|
96
|
+
const { runtime } = dependencies.resolveProvider(providerName);
|
|
97
|
+
return Boolean(await runtime.stopBackgroundTask?.(sessionId, taskId));
|
|
98
|
+
},
|
|
99
|
+
|
|
100
|
+
hasBackgroundWork(sessionId: string): boolean {
|
|
101
|
+
return dependencies.listProviders().some((provider) =>
|
|
102
|
+
(provider.runtime.listBackgroundWork?.() ?? []).some((entry) => entry.sessionId === sessionId));
|
|
103
|
+
},
|
|
104
|
+
|
|
94
105
|
resolveToolApproval(requestId: string, decision: ProviderPermissionDecision): void {
|
|
95
106
|
for (const provider of dependencies.listProviders()) {
|
|
96
107
|
provider.runtime.permissions?.resolve(requestId, decision);
|
|
@@ -5,6 +5,7 @@ import readline from 'node:readline';
|
|
|
5
5
|
import { spawn } from 'cross-spawn';
|
|
6
6
|
import { rgPath } from '@vscode/ripgrep';
|
|
7
7
|
|
|
8
|
+
import { stripAnsiSequences } from '@/shared/utils.js';
|
|
8
9
|
import { projectsDb, sessionsDb } from '@/modules/database/index.js';
|
|
9
10
|
|
|
10
11
|
type AnyRecord = Record<string, any>;
|
|
@@ -409,10 +410,6 @@ function buildClaudeLocalCommandDisplayText(payload: ClaudeLocalCommandPayload):
|
|
|
409
410
|
return commandArgs ? `${baseCommand} ${commandArgs}` : baseCommand;
|
|
410
411
|
}
|
|
411
412
|
|
|
412
|
-
function stripAnsiFormatting(text: string): string {
|
|
413
|
-
return text.replace(/\u001B\[[0-9;?]*[ -/]*[@-~]/g, '');
|
|
414
|
-
}
|
|
415
|
-
|
|
416
413
|
type ClaudeSearchableMessage = {
|
|
417
414
|
text: string;
|
|
418
415
|
role: 'user' | 'assistant';
|
|
@@ -456,7 +453,7 @@ function extractClaudeSearchableMessage(entry: AnyRecord): ClaudeSearchableMessa
|
|
|
456
453
|
|
|
457
454
|
const localCommandStdout = extractTaggedContent(content, 'local-command-stdout');
|
|
458
455
|
if (localCommandStdout !== null) {
|
|
459
|
-
const stdoutText =
|
|
456
|
+
const stdoutText = stripAnsiSequences(localCommandStdout).trim();
|
|
460
457
|
return stdoutText
|
|
461
458
|
? {
|
|
462
459
|
text: stdoutText,
|
|
@@ -16,8 +16,14 @@ import type { FetchHistoryResult } from '@/shared/types.js';
|
|
|
16
16
|
* request against the transcript file's identity (path + mtime + size), so
|
|
17
17
|
* only the first read after the file changes pays the parse. Anything that
|
|
18
18
|
* rewrites history (a new turn, an edit, a rewind, a fork) touches the file
|
|
19
|
-
* and invalidates naturally
|
|
20
|
-
*
|
|
19
|
+
* and invalidates naturally.
|
|
20
|
+
*
|
|
21
|
+
* One thing the file cannot tell: whether the CLI process behind the session
|
|
22
|
+
* is still running. Claude's history reader answers a background agent's
|
|
23
|
+
* status from that, so a session's entry is also dropped explicitly when its
|
|
24
|
+
* process starts or ends (`invalidate`) — otherwise a page read while the
|
|
25
|
+
* process was up would keep reporting `running` after it had gone, until the
|
|
26
|
+
* next row happened to land in the file.
|
|
21
27
|
*
|
|
22
28
|
* Only history readers that read `jsonl_path` itself may use this cache —
|
|
23
29
|
* callers pass `transcriptPath: null` for providers whose messages live
|
|
@@ -133,6 +139,11 @@ export function createSessionHistoryCache(
|
|
|
133
139
|
pendingLoads.delete(sessionId);
|
|
134
140
|
}
|
|
135
141
|
},
|
|
142
|
+
|
|
143
|
+
/** Forgets a session's entry so the next read re-parses its transcript. */
|
|
144
|
+
invalidate(sessionId: string): void {
|
|
145
|
+
entries.delete(sessionId);
|
|
146
|
+
},
|
|
136
147
|
};
|
|
137
148
|
}
|
|
138
149
|
|
|
@@ -5,15 +5,44 @@ import path from 'node:path';
|
|
|
5
5
|
import { projectsDb, sessionsDb } from '@/modules/database/index.js';
|
|
6
6
|
import { broadcastSessionUpserted, chatRunRegistry } from '@/modules/websocket/index.js';
|
|
7
7
|
import { providerRegistry } from '@/modules/providers/provider.registry.js';
|
|
8
|
+
import { listBusyClaudeCliSessions } from '@/modules/providers/services/claude-cli-liveness.service.js';
|
|
8
9
|
import { sessionHistoryCache } from '@/modules/providers/services/session-history-cache.service.js';
|
|
9
10
|
import type {
|
|
11
|
+
BackgroundTaskSummary,
|
|
10
12
|
FetchHistoryOptions,
|
|
11
13
|
FetchHistoryResult,
|
|
12
14
|
LLMProvider,
|
|
13
15
|
NormalizedMessage,
|
|
16
|
+
WorkflowAgentActivity,
|
|
14
17
|
} from '@/shared/types.js';
|
|
15
18
|
import { AppError, sliceTailPage } from '@/shared/utils.js';
|
|
16
19
|
|
|
20
|
+
/**
|
|
21
|
+
* One session the running-sessions poll reports as busy.
|
|
22
|
+
*
|
|
23
|
+
* A chat run in progress is listed as before. A session whose turn has ended
|
|
24
|
+
* but whose background tasks are still outstanding is listed with
|
|
25
|
+
* `background: true` and `canInterrupt: false`: the composer stays usable
|
|
26
|
+
* (the runtime accepts a new turn while the work runs) and there is no run to
|
|
27
|
+
* abort — a task is stopped by id through `chat.stop-task` instead. `tasks`
|
|
28
|
+
* rides along on both kinds whenever the session has any.
|
|
29
|
+
*
|
|
30
|
+
* A turn driven outside CloudCLI — the Claude CLI in the Shell view, or a
|
|
31
|
+
* terminal the user opened themselves — is listed the same way, with
|
|
32
|
+
* `canInterrupt: false` because there is no run here to abort, and a
|
|
33
|
+
* `statusText` saying where the work is happening.
|
|
34
|
+
*/
|
|
35
|
+
type RunningSessionEntry = {
|
|
36
|
+
sessionId: string;
|
|
37
|
+
provider: LLMProvider;
|
|
38
|
+
startedAt: number;
|
|
39
|
+
lastSeq: number;
|
|
40
|
+
background?: true;
|
|
41
|
+
canInterrupt?: false;
|
|
42
|
+
statusText?: string;
|
|
43
|
+
tasks?: BackgroundTaskSummary[];
|
|
44
|
+
};
|
|
45
|
+
|
|
17
46
|
type CreateAppSessionResult = {
|
|
18
47
|
sessionId: string;
|
|
19
48
|
provider: LLMProvider;
|
|
@@ -125,18 +154,61 @@ export const sessionsService = {
|
|
|
125
154
|
},
|
|
126
155
|
|
|
127
156
|
/**
|
|
128
|
-
* Returns app-facing ids for provider runs that are currently processing
|
|
157
|
+
* Returns app-facing ids for provider runs that are currently processing,
|
|
158
|
+
* plus every session whose background work outlived its turn.
|
|
129
159
|
*
|
|
130
160
|
* This is intentionally status-only: callers that only need sidebar activity
|
|
131
161
|
* indicators should not attach to chat streams or request replayed messages.
|
|
132
162
|
*/
|
|
133
|
-
listRunningSessions():
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
163
|
+
async listRunningSessions(): Promise<RunningSessionEntry[]> {
|
|
164
|
+
const entries: RunningSessionEntry[] = chatRunRegistry.listRunningRuns();
|
|
165
|
+
const runningById = new Map(entries.map((entry) => [entry.sessionId, entry]));
|
|
166
|
+
|
|
167
|
+
for (const provider of providerRegistry.listProviders()) {
|
|
168
|
+
for (const { sessionId, tasks } of provider.runtime.listBackgroundWork?.() ?? []) {
|
|
169
|
+
const running = runningById.get(sessionId);
|
|
170
|
+
if (running) {
|
|
171
|
+
running.tasks = tasks;
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
entries.push({
|
|
175
|
+
sessionId,
|
|
176
|
+
provider: provider.id,
|
|
177
|
+
startedAt: Math.min(...tasks.map((task) => task.startedAt)),
|
|
178
|
+
// The completed run stays in the registry for a while, and a client
|
|
179
|
+
// that subscribes with its lastSeq replays the tail it missed.
|
|
180
|
+
lastSeq: chatRunRegistry.getRun(sessionId)?.lastSeq ?? 0,
|
|
181
|
+
background: true,
|
|
182
|
+
canInterrupt: false,
|
|
183
|
+
tasks,
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
// The chat-run registry only knows about turns CloudCLI drives itself, so
|
|
189
|
+
// without this a session working under the Claude CLI — including one
|
|
190
|
+
// CloudCLI spawned for its own Shell view — looks idle. A session already
|
|
191
|
+
// listed above keeps its richer entry.
|
|
192
|
+
for (const live of await listBusyClaudeCliSessions()) {
|
|
193
|
+
const session = sessionsDb.getSessionByProviderSessionId(live.providerSessionId);
|
|
194
|
+
if (!session || runningById.has(session.session_id)) {
|
|
195
|
+
continue;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
entries.push({
|
|
199
|
+
sessionId: session.session_id,
|
|
200
|
+
provider: session.provider as LLMProvider,
|
|
201
|
+
startedAt: live.startedAt,
|
|
202
|
+
lastSeq: chatRunRegistry.getRun(session.session_id)?.lastSeq ?? 0,
|
|
203
|
+
// There is no run here to abort: the turn belongs to a CLI process
|
|
204
|
+
// CloudCLI does not own, so it can be reported but not interrupted.
|
|
205
|
+
canInterrupt: false,
|
|
206
|
+
statusText: 'Running in the Claude CLI',
|
|
207
|
+
});
|
|
208
|
+
runningById.set(session.session_id, entries[entries.length - 1]);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return entries;
|
|
140
212
|
},
|
|
141
213
|
|
|
142
214
|
/**
|
|
@@ -489,6 +561,34 @@ export const sessionsService = {
|
|
|
489
561
|
};
|
|
490
562
|
},
|
|
491
563
|
|
|
564
|
+
/**
|
|
565
|
+
* Reads what one agent of a workflow run did, for the card that opened it.
|
|
566
|
+
*
|
|
567
|
+
* Not found covers both a provider that spawns no workflow agents and a run
|
|
568
|
+
* that left no transcript for this agent: either way there is nothing to
|
|
569
|
+
* show, and the card says so in one line.
|
|
570
|
+
*/
|
|
571
|
+
async readWorkflowAgentActivity(sessionId: string, runId: string, agentId: string): Promise<WorkflowAgentActivity> {
|
|
572
|
+
const session = sessionsDb.getSessionById(sessionId);
|
|
573
|
+
if (!session) {
|
|
574
|
+
throw new AppError(`Session "${sessionId}" was not found.`, {
|
|
575
|
+
code: 'SESSION_NOT_FOUND',
|
|
576
|
+
statusCode: 404,
|
|
577
|
+
});
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
const sessions = providerRegistry.resolveProvider(session.provider as LLMProvider).sessions;
|
|
581
|
+
const activity = await sessions.readWorkflowAgentActivity?.(sessionId, runId, agentId);
|
|
582
|
+
if (!activity) {
|
|
583
|
+
throw new AppError(`Workflow agent "${agentId}" was not found.`, {
|
|
584
|
+
code: 'WORKFLOW_AGENT_NOT_FOUND',
|
|
585
|
+
statusCode: 404,
|
|
586
|
+
});
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
return activity;
|
|
590
|
+
},
|
|
591
|
+
|
|
492
592
|
/**
|
|
493
593
|
* Resolves one session (by app id, falling back to the provider-native id)
|
|
494
594
|
* to its metadata plus the owning project.
|