@cloudcli-ai/cloudcli 1.37.2 → 1.37.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +269 -0
- package/dist/assets/{abnfDiagram-N423BO3Z-C4UAoHJb.js → abnfDiagram-N423BO3Z-Dmkwx38H.js} +1 -1
- package/dist/assets/{arc-B8jF-ja9.js → arc-DLe9AYuO.js} +1 -1
- package/dist/assets/{architectureDiagram-T3A2C74G-C_Wur_CU.js → architectureDiagram-T3A2C74G-CBbbbNUW.js} +1 -1
- package/dist/assets/{blockDiagram-VBNYF7ZC-Cq4P1i8t.js → blockDiagram-VBNYF7ZC-CaMEaWu4.js} +1 -1
- package/dist/assets/{c4Diagram-5PPSVZJV-C1wG4eUj.js → c4Diagram-5PPSVZJV-DhjMgNnR.js} +1 -1
- package/dist/assets/channel-CWrmwsir.js +1 -0
- package/dist/assets/{chunk-2GRJ4B5K-B8fnBG6E.js → chunk-2GRJ4B5K-yjl3-F-i.js} +1 -1
- package/dist/assets/{chunk-2Q5K7J3B-WdfPooPJ.js → chunk-2Q5K7J3B-DsAYaWEP.js} +1 -1
- package/dist/assets/{chunk-5RXB4S5H-DhZJuG2E.js → chunk-5RXB4S5H-Bt5qmqOX.js} +1 -1
- package/dist/assets/{chunk-5VM5RSS4-B0rjslal.js → chunk-5VM5RSS4-yWrWVvUt.js} +1 -1
- package/dist/assets/{chunk-6Q2QTUOP-CG2Oj4jL.js → chunk-6Q2QTUOP-Dt66K5xb.js} +1 -1
- package/dist/assets/{chunk-GF5L2VYU-rrl2lRxY.js → chunk-GF5L2VYU-DA0vzaj4.js} +1 -1
- package/dist/assets/{chunk-JWPE2WC7-CjVbrVMA.js → chunk-JWPE2WC7-CUkbx96V.js} +1 -1
- package/dist/assets/{chunk-KBJHAD2P-CRzKgU8S.js → chunk-KBJHAD2P-DWKxx1WH.js} +1 -1
- package/dist/assets/{chunk-RYQCIY6F-DBz4GTZF.js → chunk-RYQCIY6F-BqluplN0.js} +1 -1
- package/dist/assets/{chunk-XXDRQBXY-wHxalsXk.js → chunk-XXDRQBXY-DelKg3Xf.js} +1 -1
- package/dist/assets/classDiagram-JCYQIIEL-COAGCC6w.js +1 -0
- package/dist/assets/classDiagram-v2-OCEON4UE-COAGCC6w.js +1 -0
- package/dist/assets/{cose-bilkent-JH36ORCC-s1hygY8e.js → cose-bilkent-JH36ORCC-DHV93-Em.js} +1 -1
- package/dist/assets/{cynefin-VYW2F7L2-C4LGV15i.js → cynefin-VYW2F7L2-B9HZgoGX.js} +1 -1
- package/dist/assets/{cynefinDiagram-MW4NZA55-C5uhu7B3.js → cynefinDiagram-MW4NZA55-hFjjEQg8.js} +1 -1
- package/dist/assets/{dagre-VZM6K2ZE-B-OBTewm.js → dagre-VZM6K2ZE-CpeVOC7V.js} +1 -1
- package/dist/assets/{diagram-7IWD3JNH-BjGKQxih.js → diagram-7IWD3JNH-D0H1UoRA.js} +1 -1
- package/dist/assets/{diagram-B4RE2ZJO-BMKz1X2L.js → diagram-B4RE2ZJO-6Y7BQUup.js} +1 -1
- package/dist/assets/{diagram-LBJQPF4R-DNh-GhKC.js → diagram-LBJQPF4R-C0nNOwht.js} +1 -1
- package/dist/assets/{diagram-Q27KOJAE-BaTG9jPT.js → diagram-Q27KOJAE-En6QlOVX.js} +1 -1
- package/dist/assets/{diagram-UB23O5K3-BUwo2OvJ.js → diagram-UB23O5K3-CmPiNeGD.js} +1 -1
- package/dist/assets/{ebnfDiagram-BXEA7PRR-DAZgfzX2.js → ebnfDiagram-BXEA7PRR-Bygzp1Qt.js} +1 -1
- package/dist/assets/{erDiagram-JOGREHBK-1w1CcGEb.js → erDiagram-JOGREHBK-CwScRfzl.js} +1 -1
- package/dist/assets/{flowDiagram-UKHOOZJN-D4EII8Jj.js → flowDiagram-UKHOOZJN-bKX-0xP6.js} +1 -1
- package/dist/assets/{ganttDiagram-PKOTCBZU-ChvJZAgB.js → ganttDiagram-PKOTCBZU-DtZPKXGd.js} +1 -1
- package/dist/assets/{gitGraphDiagram-DS77QQ5N-DtOn41S8.js → gitGraphDiagram-DS77QQ5N-CUOtdL1o.js} +1 -1
- package/dist/assets/index-CdjIFxM2.js +5004 -0
- package/dist/assets/index-DgafhGTF.css +32 -0
- package/dist/assets/{infoDiagram-6WML65LV-DN_PVEJZ.js → infoDiagram-6WML65LV-ciRQTS0h.js} +1 -1
- package/dist/assets/{ishikawaDiagram-WSZJBQD7-DbkF1kXb.js → ishikawaDiagram-WSZJBQD7-CgjhQZ84.js} +1 -1
- package/dist/assets/{journeyDiagram-NVQOT4AX-pJwmhddE.js → journeyDiagram-NVQOT4AX-BsbQSeIW.js} +1 -1
- package/dist/assets/{kanban-definition-27J2QSJJ-PPAPgNp5.js → kanban-definition-27J2QSJJ-CrwzY9Jw.js} +1 -1
- package/dist/assets/{linear-iPR-J9f7.js → linear-BUaCDr6l.js} +1 -1
- package/dist/assets/{mermaid.core-BEUL86Z6.js → mermaid.core-ClgdUU0j.js} +5 -5
- package/dist/assets/{mindmap-definition-FAOFIHXS-BvZoBib7.js → mindmap-definition-FAOFIHXS-BictljY1.js} +1 -1
- package/dist/assets/{pegDiagram-VL7TDLO6-DhYreaUd.js → pegDiagram-VL7TDLO6-BXdByy_U.js} +1 -1
- package/dist/assets/{pieDiagram-7S7Q4E2Y-CrOHlyD0.js → pieDiagram-7S7Q4E2Y-uZsMfkRX.js} +1 -1
- package/dist/assets/{quadrantDiagram-CIZ2JOQS-D4g5yH3t.js → quadrantDiagram-CIZ2JOQS-7lCv4QIj.js} +1 -1
- package/dist/assets/{railroadDiagram-AXF67PYL-Dj39lHlf.js → railroadDiagram-AXF67PYL-07dxIWbi.js} +1 -1
- package/dist/assets/{requirementDiagram-LRYGKXZP-1AS4Zgm_.js → requirementDiagram-LRYGKXZP-Cb31Yv1_.js} +1 -1
- package/dist/assets/{sankeyDiagram-W5VNT64P-CKlMn19P.js → sankeyDiagram-W5VNT64P-X0YPSyF9.js} +1 -1
- package/dist/assets/{sequenceDiagram-SI44F4Z6-B_j3qyvo.js → sequenceDiagram-SI44F4Z6-BdBxSIlP.js} +1 -1
- package/dist/assets/server.browser-CtX3Hdl5.js +27 -0
- package/dist/assets/{sizeCapture-X5ZJPWSS-Bp2Op16v.js → sizeCapture-X5ZJPWSS-DQM5xAyD.js} +1 -1
- package/dist/assets/{stateDiagram-OKZ733FA-C0cN6DoY.js → stateDiagram-OKZ733FA-DJ3xkLUF.js} +1 -1
- package/dist/assets/stateDiagram-v2-UEYNNEHI-vAaE4zNo.js +1 -0
- package/dist/assets/{swimlanes-SLNWSIFB-BEvXOV9b.js → swimlanes-SLNWSIFB-rjfYdW0w.js} +2 -2
- package/dist/assets/swimlanesDiagram-ULZ7WXOC-TV2IF4Nq.js +8 -0
- package/dist/assets/{timeline-definition-Z64GVDOM-zdJQinvZ.js → timeline-definition-Z64GVDOM-DZMra5do.js} +1 -1
- package/dist/assets/{vendor-codemirror-OwyKSvPE.js → vendor-codemirror-CYrSt7-U.js} +18 -18
- package/dist/assets/{vennDiagram-T6HMQDX7-DuF1uZMY.js → vennDiagram-T6HMQDX7-BevD8uu0.js} +1 -1
- package/dist/assets/{wardleyDiagram-T6FBY63Y-B9AlLQup.js → wardleyDiagram-T6FBY63Y-DsIyXB7z.js} +1 -1
- package/dist/assets/{xychartDiagram-ELKLHX3M-DTJX48Qg.js → xychartDiagram-ELKLHX3M-G3PlF3ko.js} +1 -1
- package/dist/index.html +3 -3
- package/dist-server/server/index.js +11 -4
- package/dist-server/server/index.js.map +1 -1
- package/dist-server/server/modules/agent/agent.module.js +9 -2
- package/dist-server/server/modules/agent/agent.module.js.map +1 -1
- package/dist-server/server/modules/agent/agent.routes.js +177 -130
- package/dist-server/server/modules/agent/agent.routes.js.map +1 -1
- package/dist-server/server/modules/agent/tests/agent-run-registration.test.js +306 -0
- package/dist-server/server/modules/agent/tests/agent-run-registration.test.js.map +1 -0
- package/dist-server/server/modules/agent/tests/agent.routes.test.js +48 -2
- package/dist-server/server/modules/agent/tests/agent.routes.test.js.map +1 -1
- package/dist-server/server/modules/browser-use/browser-use.routes.js +39 -33
- package/dist-server/server/modules/browser-use/browser-use.routes.js.map +1 -1
- package/dist-server/server/modules/database/connection.js +1 -1
- package/dist-server/server/modules/database/connection.js.map +1 -1
- package/dist-server/server/modules/database/index.js +5 -0
- package/dist-server/server/modules/database/index.js.map +1 -1
- package/dist-server/server/modules/database/migrations.js +45 -1
- package/dist-server/server/modules/database/migrations.js.map +1 -1
- package/dist-server/server/modules/database/repositories/provider-models.js +60 -6
- package/dist-server/server/modules/database/repositories/provider-models.js.map +1 -1
- package/dist-server/server/modules/database/repositories/scheduled-messages.db.js +71 -0
- package/dist-server/server/modules/database/repositories/scheduled-messages.db.js.map +1 -0
- package/dist-server/server/modules/database/repositories/session-drafts.db.js +105 -0
- package/dist-server/server/modules/database/repositories/session-drafts.db.js.map +1 -0
- package/dist-server/server/modules/database/repositories/sessions.db.js +144 -5
- package/dist-server/server/modules/database/repositories/sessions.db.js.map +1 -1
- package/dist-server/server/modules/database/repositories/user-preferences.db.js +65 -0
- package/dist-server/server/modules/database/repositories/user-preferences.db.js.map +1 -0
- package/dist-server/server/modules/database/schema.js +99 -0
- package/dist-server/server/modules/database/schema.js.map +1 -1
- package/dist-server/server/modules/database/tests/provider-models.db.integration.test.js +99 -0
- package/dist-server/server/modules/database/tests/provider-models.db.integration.test.js.map +1 -1
- package/dist-server/server/modules/database/tests/sessions.db.integration.test.js +39 -0
- package/dist-server/server/modules/database/tests/sessions.db.integration.test.js.map +1 -1
- package/dist-server/server/modules/database/tests/user-preferences.db.integration.test.js +105 -0
- package/dist-server/server/modules/database/tests/user-preferences.db.integration.test.js.map +1 -0
- package/dist-server/server/modules/file-tree/file-tree.module.js +2 -1
- package/dist-server/server/modules/file-tree/file-tree.module.js.map +1 -1
- package/dist-server/server/modules/file-tree/file-tree.service.js +44 -7
- package/dist-server/server/modules/file-tree/file-tree.service.js.map +1 -1
- package/dist-server/server/modules/file-tree/tests/file-tree.service.test.js +126 -1
- package/dist-server/server/modules/file-tree/tests/file-tree.service.test.js.map +1 -1
- package/dist-server/server/modules/git/git-branch-diff.service.js +142 -0
- package/dist-server/server/modules/git/git-branch-diff.service.js.map +1 -0
- package/dist-server/server/modules/git/git-branch.service.js.map +1 -1
- package/dist-server/server/modules/git/git.routes.js +92 -5
- package/dist-server/server/modules/git/git.routes.js.map +1 -1
- package/dist-server/server/modules/git/tests/git-branch-diff.service.test.js +160 -0
- package/dist-server/server/modules/git/tests/git-branch-diff.service.test.js.map +1 -0
- package/dist-server/server/modules/projects/project-clone.routes.js +108 -0
- package/dist-server/server/modules/projects/project-clone.routes.js.map +1 -0
- package/dist-server/server/modules/projects/projects.routes.js +13 -66
- package/dist-server/server/modules/projects/projects.routes.js.map +1 -1
- package/dist-server/server/modules/projects/services/project-clone-request.service.js +36 -0
- package/dist-server/server/modules/projects/services/project-clone-request.service.js.map +1 -0
- package/dist-server/server/modules/projects/services/project-clone.service.js +86 -32
- package/dist-server/server/modules/projects/services/project-clone.service.js.map +1 -1
- package/dist-server/server/modules/projects/services/project-delete.service.js +12 -1
- package/dist-server/server/modules/projects/services/project-delete.service.js.map +1 -1
- package/dist-server/server/modules/projects/services/project-star.service.js +2 -2
- package/dist-server/server/modules/projects/services/project-star.service.js.map +1 -1
- package/dist-server/server/modules/projects/tests/project-clone.routes.test.js +191 -0
- package/dist-server/server/modules/projects/tests/project-clone.routes.test.js.map +1 -0
- package/dist-server/server/modules/projects/tests/project-clone.service.test.js +275 -0
- package/dist-server/server/modules/projects/tests/project-clone.service.test.js.map +1 -1
- package/dist-server/server/modules/providers/index.js +3 -0
- package/dist-server/server/modules/providers/index.js.map +1 -1
- package/dist-server/server/modules/providers/list/claude/claude-auth.provider.js +62 -9
- package/dist-server/server/modules/providers/list/claude/claude-auth.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/claude/claude-fork.provider.js +46 -0
- package/dist-server/server/modules/providers/list/claude/claude-fork.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/claude/claude-models.provider.js +31 -12
- package/dist-server/server/modules/providers/list/claude/claude-models.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/claude/claude-runtime.provider.js +472 -53
- package/dist-server/server/modules/providers/list/claude/claude-runtime.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/claude/claude-session-synchronizer.provider.js +38 -11
- package/dist-server/server/modules/providers/list/claude/claude-session-synchronizer.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/claude/claude-sessions.provider.js +1007 -60
- package/dist-server/server/modules/providers/list/claude/claude-sessions.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/claude/claude-skills.provider.js +4 -1
- package/dist-server/server/modules/providers/list/claude/claude-skills.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/claude/claude.provider.js +2 -0
- package/dist-server/server/modules/providers/list/claude/claude.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/codex/codex-app-server.client.js +204 -0
- package/dist-server/server/modules/providers/list/codex/codex-app-server.client.js.map +1 -0
- package/dist-server/server/modules/providers/list/codex/codex-fork.provider.js +32 -0
- package/dist-server/server/modules/providers/list/codex/codex-fork.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/codex/codex-models.provider.js +47 -0
- package/dist-server/server/modules/providers/list/codex/codex-models.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/codex/codex-runtime.provider.js +71 -53
- package/dist-server/server/modules/providers/list/codex/codex-runtime.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/codex/codex-session-synchronizer.provider.js +8 -0
- package/dist-server/server/modules/providers/list/codex/codex-session-synchronizer.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/codex/codex-sessions.provider.js +1621 -431
- package/dist-server/server/modules/providers/list/codex/codex-sessions.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/codex/codex.provider.js +2 -0
- package/dist-server/server/modules/providers/list/codex/codex.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/cursor/cursor-auth.provider.js +9 -2
- package/dist-server/server/modules/providers/list/cursor/cursor-auth.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/cursor/cursor-runtime.provider.js +16 -8
- package/dist-server/server/modules/providers/list/cursor/cursor-runtime.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/cursor/cursor-session-synchronizer.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/opencode/opencode-models.provider.js +274 -9
- package/dist-server/server/modules/providers/list/opencode/opencode-models.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/opencode/opencode-runtime.provider.js +5 -2
- package/dist-server/server/modules/providers/list/opencode/opencode-runtime.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/opencode/opencode-session-synchronizer.provider.js +19 -2
- package/dist-server/server/modules/providers/list/opencode/opencode-session-synchronizer.provider.js.map +1 -1
- package/dist-server/server/modules/providers/provider.routes.js +134 -2
- package/dist-server/server/modules/providers/provider.routes.js.map +1 -1
- package/dist-server/server/modules/providers/services/claude-cli-liveness.service.js +100 -0
- package/dist-server/server/modules/providers/services/claude-cli-liveness.service.js.map +1 -0
- package/dist-server/server/modules/providers/services/claude-settings-permissions.service.js +97 -0
- package/dist-server/server/modules/providers/services/claude-settings-permissions.service.js.map +1 -0
- package/dist-server/server/modules/providers/services/provider-capabilities.service.js +23 -1
- package/dist-server/server/modules/providers/services/provider-capabilities.service.js.map +1 -1
- package/dist-server/server/modules/providers/services/provider-models.service.js +59 -5
- package/dist-server/server/modules/providers/services/provider-models.service.js.map +1 -1
- package/dist-server/server/modules/providers/services/provider-runtime.service.js +8 -0
- package/dist-server/server/modules/providers/services/provider-runtime.service.js.map +1 -1
- package/dist-server/server/modules/providers/services/provider-token-usage.service.js +139 -35
- package/dist-server/server/modules/providers/services/provider-token-usage.service.js.map +1 -1
- package/dist-server/server/modules/providers/services/session-conversations-search.service.js +2 -4
- package/dist-server/server/modules/providers/services/session-conversations-search.service.js.map +1 -1
- package/dist-server/server/modules/providers/services/session-history-cache.service.js +91 -0
- package/dist-server/server/modules/providers/services/session-history-cache.service.js.map +1 -0
- package/dist-server/server/modules/providers/services/session-synchronizer.service.js +105 -32
- package/dist-server/server/modules/providers/services/session-synchronizer.service.js.map +1 -1
- package/dist-server/server/modules/providers/services/sessions-watcher.service.js +3 -60
- package/dist-server/server/modules/providers/services/sessions-watcher.service.js.map +1 -1
- package/dist-server/server/modules/providers/services/sessions.service.js +252 -12
- package/dist-server/server/modules/providers/services/sessions.service.js.map +1 -1
- package/dist-server/server/modules/providers/tests/claude-auth.test.js +183 -0
- package/dist-server/server/modules/providers/tests/claude-auth.test.js.map +1 -1
- package/dist-server/server/modules/providers/tests/claude-background-work.test.js +172 -0
- package/dist-server/server/modules/providers/tests/claude-background-work.test.js.map +1 -0
- package/dist-server/server/modules/providers/tests/claude-compaction.test.js +84 -0
- package/dist-server/server/modules/providers/tests/claude-compaction.test.js.map +1 -0
- package/dist-server/server/modules/providers/tests/claude-message-model.test.js +123 -0
- package/dist-server/server/modules/providers/tests/claude-message-model.test.js.map +1 -0
- package/dist-server/server/modules/providers/tests/claude-models.test.js +39 -0
- package/dist-server/server/modules/providers/tests/claude-models.test.js.map +1 -0
- package/dist-server/server/modules/providers/tests/claude-runtime-hold.test.js +265 -0
- package/dist-server/server/modules/providers/tests/claude-runtime-hold.test.js.map +1 -0
- package/dist-server/server/modules/providers/tests/claude-sessions.test.js +1815 -33
- package/dist-server/server/modules/providers/tests/claude-sessions.test.js.map +1 -1
- package/dist-server/server/modules/providers/tests/claude-settings-permissions.test.js +228 -0
- package/dist-server/server/modules/providers/tests/claude-settings-permissions.test.js.map +1 -0
- package/dist-server/server/modules/providers/tests/claude-subagent-echo.test.js +16 -0
- package/dist-server/server/modules/providers/tests/claude-subagent-echo.test.js.map +1 -0
- package/dist-server/server/modules/providers/tests/claude-token-budget.test.js +85 -0
- package/dist-server/server/modules/providers/tests/claude-token-budget.test.js.map +1 -0
- package/dist-server/server/modules/providers/tests/codex-message-editing.test.js +338 -0
- package/dist-server/server/modules/providers/tests/codex-message-editing.test.js.map +1 -0
- package/dist-server/server/modules/providers/tests/codex-models.test.js +25 -0
- package/dist-server/server/modules/providers/tests/codex-models.test.js.map +1 -0
- package/dist-server/server/modules/providers/tests/codex-runtime.test.js +83 -0
- package/dist-server/server/modules/providers/tests/codex-runtime.test.js.map +1 -0
- package/dist-server/server/modules/providers/tests/codex-session-fork.test.js +54 -0
- package/dist-server/server/modules/providers/tests/codex-session-fork.test.js.map +1 -0
- package/dist-server/server/modules/providers/tests/codex-sessions.test.js +354 -15
- package/dist-server/server/modules/providers/tests/codex-sessions.test.js.map +1 -1
- package/dist-server/server/modules/providers/tests/cursor-auth.test.js +167 -0
- package/dist-server/server/modules/providers/tests/cursor-auth.test.js.map +1 -0
- package/dist-server/server/modules/providers/tests/opencode-models.test.js +196 -4
- package/dist-server/server/modules/providers/tests/opencode-models.test.js.map +1 -1
- package/dist-server/server/modules/providers/tests/opencode-sessions.test.js +19 -1
- package/dist-server/server/modules/providers/tests/opencode-sessions.test.js.map +1 -1
- package/dist-server/server/modules/providers/tests/provider-capabilities.service.test.js +30 -0
- package/dist-server/server/modules/providers/tests/provider-capabilities.service.test.js.map +1 -0
- package/dist-server/server/modules/providers/tests/provider-models.service.test.js +79 -1
- package/dist-server/server/modules/providers/tests/provider-models.service.test.js.map +1 -1
- package/dist-server/server/modules/providers/tests/provider-token-usage.service.test.js +164 -1
- package/dist-server/server/modules/providers/tests/provider-token-usage.service.test.js.map +1 -1
- package/dist-server/server/modules/providers/tests/provider.routes.test.js +190 -1
- package/dist-server/server/modules/providers/tests/provider.routes.test.js.map +1 -1
- package/dist-server/server/modules/providers/tests/session-fork.test.js +113 -0
- package/dist-server/server/modules/providers/tests/session-fork.test.js.map +1 -0
- package/dist-server/server/modules/providers/tests/session-history-cache.service.test.js +139 -0
- package/dist-server/server/modules/providers/tests/session-history-cache.service.test.js.map +1 -0
- package/dist-server/server/modules/providers/tests/session-orphan-prune.test.js +90 -0
- package/dist-server/server/modules/providers/tests/session-orphan-prune.test.js.map +1 -0
- package/dist-server/server/modules/providers/tests/session-synchronizer-coalescing.test.js +122 -0
- package/dist-server/server/modules/providers/tests/session-synchronizer-coalescing.test.js.map +1 -0
- package/dist-server/server/modules/providers/tests/sessions.service.test.js +219 -1
- package/dist-server/server/modules/providers/tests/sessions.service.test.js.map +1 -1
- package/dist-server/server/modules/providers/tests/skills.test.js +15 -3
- package/dist-server/server/modules/providers/tests/skills.test.js.map +1 -1
- package/dist-server/server/modules/scheduled-messages/index.js +5 -0
- package/dist-server/server/modules/scheduled-messages/index.js.map +1 -0
- package/dist-server/server/modules/scheduled-messages/scheduled-messages.routes.js +47 -0
- package/dist-server/server/modules/scheduled-messages/scheduled-messages.routes.js.map +1 -0
- package/dist-server/server/modules/scheduled-messages/services/scheduled-message-dispatcher.service.js +175 -0
- package/dist-server/server/modules/scheduled-messages/services/scheduled-message-dispatcher.service.js.map +1 -0
- package/dist-server/server/modules/scheduled-messages/services/scheduled-messages.service.js +95 -0
- package/dist-server/server/modules/scheduled-messages/services/scheduled-messages.service.js.map +1 -0
- package/dist-server/server/modules/scheduled-messages/tests/scheduled-messages.test.js +290 -0
- package/dist-server/server/modules/scheduled-messages/tests/scheduled-messages.test.js.map +1 -0
- package/dist-server/server/modules/taskmaster/taskmaster.routes.js +23 -23
- package/dist-server/server/modules/taskmaster/taskmaster.routes.js.map +1 -1
- package/dist-server/server/modules/taskmaster/tests/taskmaster.routes.test.js +48 -0
- package/dist-server/server/modules/taskmaster/tests/taskmaster.routes.test.js.map +1 -1
- package/dist-server/server/modules/user/tests/user.service.test.js +70 -0
- package/dist-server/server/modules/user/tests/user.service.test.js.map +1 -1
- package/dist-server/server/modules/user/user.module.js +10 -1
- package/dist-server/server/modules/user/user.module.js.map +1 -1
- package/dist-server/server/modules/user/user.routes.js +44 -0
- package/dist-server/server/modules/user/user.routes.js.map +1 -1
- package/dist-server/server/modules/user/user.service.js +75 -0
- package/dist-server/server/modules/user/user.service.js.map +1 -1
- package/dist-server/server/modules/voice/tests/voice.service.test.js +3 -2
- package/dist-server/server/modules/voice/tests/voice.service.test.js.map +1 -1
- package/dist-server/server/modules/websocket/index.js +7 -0
- package/dist-server/server/modules/websocket/index.js.map +1 -1
- package/dist-server/server/modules/websocket/services/chat-run-registry.service.js +37 -49
- package/dist-server/server/modules/websocket/services/chat-run-registry.service.js.map +1 -1
- package/dist-server/server/modules/websocket/services/chat-session-writer.service.js +35 -5
- package/dist-server/server/modules/websocket/services/chat-session-writer.service.js.map +1 -1
- package/dist-server/server/modules/websocket/services/chat-websocket.service.js +204 -10
- package/dist-server/server/modules/websocket/services/chat-websocket.service.js.map +1 -1
- package/dist-server/server/modules/websocket/services/session-upsert-broadcast.service.js +94 -0
- package/dist-server/server/modules/websocket/services/session-upsert-broadcast.service.js.map +1 -0
- package/dist-server/server/modules/websocket/services/shell-websocket.service.js +236 -20
- package/dist-server/server/modules/websocket/services/shell-websocket.service.js.map +1 -1
- package/dist-server/server/modules/websocket/tests/chat-background-subscribe.test.js +137 -0
- package/dist-server/server/modules/websocket/tests/chat-background-subscribe.test.js.map +1 -0
- package/dist-server/server/modules/websocket/tests/chat-edit-send.test.js +263 -0
- package/dist-server/server/modules/websocket/tests/chat-edit-send.test.js.map +1 -0
- package/dist-server/server/modules/websocket/tests/chat-run-registry.test.js +29 -3
- package/dist-server/server/modules/websocket/tests/chat-run-registry.test.js.map +1 -1
- package/dist-server/server/modules/websocket/tests/chat-stop-task.test.js +96 -0
- package/dist-server/server/modules/websocket/tests/chat-stop-task.test.js.map +1 -0
- package/dist-server/server/modules/websocket/tests/session-upsert-broadcast.test.js +106 -0
- package/dist-server/server/modules/websocket/tests/session-upsert-broadcast.test.js.map +1 -0
- package/dist-server/server/modules/websocket/tests/shell-websocket.service.test.js +500 -0
- package/dist-server/server/modules/websocket/tests/shell-websocket.service.test.js.map +1 -1
- package/dist-server/server/shared/claude-cli-path.js +22 -5
- package/dist-server/server/shared/claude-cli-path.js.map +1 -1
- package/dist-server/server/shared/index.js +3 -0
- package/dist-server/server/shared/index.js.map +1 -0
- package/dist-server/server/shared/message-unification.js +376 -0
- package/dist-server/server/shared/message-unification.js.map +1 -0
- package/dist-server/server/shared/tests/claude-cli-path.test.js +45 -1
- package/dist-server/server/shared/tests/claude-cli-path.test.js.map +1 -1
- package/dist-server/server/shared/tests/message-unification.test.js +174 -0
- package/dist-server/server/shared/tests/message-unification.test.js.map +1 -0
- package/dist-server/server/shared/tests/read-only-roots.test.js +124 -0
- package/dist-server/server/shared/tests/read-only-roots.test.js.map +1 -0
- package/dist-server/server/shared/tests/strip-ansi-sequences.test.js +31 -0
- package/dist-server/server/shared/tests/strip-ansi-sequences.test.js.map +1 -0
- package/dist-server/server/shared/utils.js +160 -0
- package/dist-server/server/shared/utils.js.map +1 -1
- package/electron/desktopWindow.js +6 -0
- package/electron/viewHost.js +25 -2
- package/package.json +20 -17
- package/server/index.ts +16 -5
- package/server/modules/agent/agent.module.ts +11 -1
- package/server/modules/agent/agent.routes.ts +200 -142
- package/server/modules/agent/tests/agent-run-registration.test.ts +348 -0
- package/server/modules/agent/tests/agent.routes.test.ts +51 -2
- package/server/modules/browser-use/browser-use.routes.ts +96 -33
- package/server/modules/database/connection.ts +2 -1
- package/server/modules/database/index.ts +10 -0
- package/server/modules/database/migrations.ts +51 -0
- package/server/modules/database/repositories/provider-models.ts +79 -6
- package/server/modules/database/repositories/scheduled-messages.db.ts +127 -0
- package/server/modules/database/repositories/session-drafts.db.ts +171 -0
- package/server/modules/database/repositories/sessions.db.ts +204 -3
- package/server/modules/database/repositories/user-preferences.db.ts +80 -0
- package/server/modules/database/schema.ts +103 -0
- package/server/modules/database/tests/provider-models.db.integration.test.ts +113 -0
- package/server/modules/database/tests/sessions.db.integration.test.ts +52 -0
- package/server/modules/database/tests/user-preferences.db.integration.test.ts +135 -0
- package/server/modules/file-tree/file-tree.module.ts +2 -1
- package/server/modules/file-tree/file-tree.service.ts +49 -7
- package/server/modules/file-tree/tests/file-tree.service.test.ts +163 -1
- package/server/modules/git/git-branch-diff.service.ts +204 -0
- package/server/modules/git/git-branch.service.ts +2 -12
- package/server/modules/git/git.routes.ts +100 -6
- package/server/modules/git/tests/git-branch-diff.service.test.ts +210 -0
- package/server/modules/projects/project-clone.routes.ts +142 -0
- package/server/modules/projects/projects.routes.ts +16 -81
- package/server/modules/projects/services/project-clone-request.service.ts +56 -0
- package/server/modules/projects/services/project-clone.service.ts +102 -34
- package/server/modules/projects/services/project-delete.service.ts +13 -1
- package/server/modules/projects/services/project-star.service.ts +2 -2
- package/server/modules/projects/tests/project-clone.routes.test.ts +246 -0
- package/server/modules/projects/tests/project-clone.service.test.ts +376 -0
- package/server/modules/providers/index.ts +4 -0
- package/server/modules/providers/list/claude/claude-auth.provider.ts +81 -11
- package/server/modules/providers/list/claude/claude-fork.provider.ts +57 -0
- package/server/modules/providers/list/claude/claude-models.provider.ts +37 -17
- package/server/modules/providers/list/claude/claude-runtime.provider.js +510 -54
- package/server/modules/providers/list/claude/claude-session-synchronizer.provider.ts +40 -14
- package/server/modules/providers/list/claude/claude-sessions.provider.ts +1195 -63
- package/server/modules/providers/list/claude/claude-skills.provider.ts +4 -1
- package/server/modules/providers/list/claude/claude.provider.ts +3 -0
- package/server/modules/providers/list/codex/codex-app-server.client.ts +243 -0
- package/server/modules/providers/list/codex/codex-fork.provider.ts +40 -0
- package/server/modules/providers/list/codex/codex-models.provider.ts +47 -0
- package/server/modules/providers/list/codex/{codex-runtime.provider.js → codex-runtime.provider.ts} +105 -70
- package/server/modules/providers/list/codex/codex-session-synchronizer.provider.ts +9 -0
- package/server/modules/providers/list/codex/codex-sessions.provider.ts +1847 -455
- package/server/modules/providers/list/codex/codex.provider.ts +3 -0
- package/server/modules/providers/list/cursor/cursor-auth.provider.ts +10 -2
- package/server/modules/providers/list/cursor/cursor-runtime.provider.js +17 -8
- package/server/modules/providers/list/cursor/cursor-session-synchronizer.provider.ts +0 -1
- package/server/modules/providers/list/opencode/opencode-models.provider.ts +297 -9
- package/server/modules/providers/list/opencode/opencode-runtime.provider.js +5 -2
- package/server/modules/providers/list/opencode/opencode-session-synchronizer.provider.ts +30 -2
- package/server/modules/providers/provider.routes.ts +162 -2
- package/server/modules/providers/services/claude-cli-liveness.service.ts +121 -0
- package/server/modules/providers/services/claude-settings-permissions.service.ts +127 -0
- package/server/modules/providers/services/provider-capabilities.service.ts +40 -1
- package/server/modules/providers/services/provider-models.service.ts +71 -4
- package/server/modules/providers/services/provider-runtime.service.ts +11 -0
- package/server/modules/providers/services/provider-token-usage.service.ts +166 -39
- package/server/modules/providers/services/session-conversations-search.service.ts +2 -5
- package/server/modules/providers/services/session-history-cache.service.ts +150 -0
- package/server/modules/providers/services/session-synchronizer.service.ts +125 -38
- package/server/modules/providers/services/sessions-watcher.service.ts +3 -64
- package/server/modules/providers/services/sessions.service.ts +309 -17
- package/server/modules/providers/tests/claude-auth.test.ts +208 -0
- package/server/modules/providers/tests/claude-background-work.test.ts +213 -0
- package/server/modules/providers/tests/claude-compaction.test.ts +101 -0
- package/server/modules/providers/tests/claude-message-model.test.ts +160 -0
- package/server/modules/providers/tests/claude-models.test.ts +87 -0
- package/server/modules/providers/tests/claude-runtime-hold.test.ts +309 -0
- package/server/modules/providers/tests/claude-sessions.test.ts +2167 -37
- package/server/modules/providers/tests/claude-settings-permissions.test.ts +271 -0
- package/server/modules/providers/tests/claude-subagent-echo.test.ts +25 -0
- package/server/modules/providers/tests/claude-token-budget.test.ts +104 -0
- package/server/modules/providers/tests/codex-message-editing.test.ts +419 -0
- package/server/modules/providers/tests/codex-models.test.ts +37 -0
- package/server/modules/providers/tests/codex-runtime.test.ts +94 -0
- package/server/modules/providers/tests/codex-session-fork.test.ts +59 -0
- package/server/modules/providers/tests/codex-sessions.test.ts +396 -15
- package/server/modules/providers/tests/cursor-auth.test.ts +187 -0
- package/server/modules/providers/tests/opencode-models.test.ts +266 -8
- package/server/modules/providers/tests/opencode-sessions.test.ts +40 -1
- package/server/modules/providers/tests/provider-capabilities.service.test.ts +37 -0
- package/server/modules/providers/tests/provider-models.service.test.ts +99 -1
- package/server/modules/providers/tests/provider-token-usage.service.test.ts +188 -1
- package/server/modules/providers/tests/provider.routes.test.ts +230 -1
- package/server/modules/providers/tests/session-fork.test.ts +173 -0
- package/server/modules/providers/tests/session-history-cache.service.test.ts +165 -0
- package/server/modules/providers/tests/session-orphan-prune.test.ts +107 -0
- package/server/modules/providers/tests/session-synchronizer-coalescing.test.ts +136 -0
- package/server/modules/providers/tests/sessions.service.test.ts +303 -1
- package/server/modules/providers/tests/skills.test.ts +21 -5
- package/server/modules/scheduled-messages/index.ts +8 -0
- package/server/modules/scheduled-messages/scheduled-messages.routes.ts +68 -0
- package/server/modules/scheduled-messages/services/scheduled-message-dispatcher.service.ts +216 -0
- package/server/modules/scheduled-messages/services/scheduled-messages.service.ts +121 -0
- package/server/modules/scheduled-messages/tests/scheduled-messages.test.ts +354 -0
- package/server/modules/taskmaster/taskmaster.routes.ts +23 -33
- package/server/modules/taskmaster/tests/taskmaster.routes.test.ts +57 -0
- package/server/modules/user/tests/user.service.test.ts +90 -0
- package/server/modules/user/user.module.ts +10 -1
- package/server/modules/user/user.routes.ts +44 -0
- package/server/modules/user/user.service.ts +105 -0
- package/server/modules/voice/tests/voice.service.test.ts +3 -2
- package/server/modules/websocket/README.md +4 -2
- package/server/modules/websocket/index.ts +8 -0
- package/server/modules/websocket/services/chat-run-registry.service.ts +45 -54
- package/server/modules/websocket/services/chat-session-writer.service.ts +36 -6
- package/server/modules/websocket/services/chat-websocket.service.ts +302 -16
- package/server/modules/websocket/services/session-upsert-broadcast.service.ts +108 -0
- package/server/modules/websocket/services/shell-websocket.service.ts +276 -22
- package/server/modules/websocket/tests/chat-background-subscribe.test.ts +167 -0
- package/server/modules/websocket/tests/chat-edit-send.test.ts +309 -0
- package/server/modules/websocket/tests/chat-run-registry.test.ts +34 -3
- package/server/modules/websocket/tests/chat-stop-task.test.ts +127 -0
- package/server/modules/websocket/tests/session-upsert-broadcast.test.ts +138 -0
- package/server/modules/websocket/tests/shell-websocket.service.test.ts +629 -0
- package/server/shared/claude-cli-path.ts +24 -6
- package/server/shared/index.ts +3 -0
- package/server/shared/interfaces.ts +91 -0
- package/server/shared/message-unification.ts +449 -0
- package/server/shared/tests/claude-cli-path.test.ts +58 -1
- package/server/shared/tests/message-unification.test.ts +218 -0
- package/server/shared/tests/read-only-roots.test.ts +139 -0
- package/server/shared/tests/strip-ansi-sequences.test.ts +42 -0
- package/server/shared/types.ts +382 -7
- package/server/shared/utils.ts +176 -0
- package/dist/assets/channel-_falYbU1.js +0 -1
- package/dist/assets/classDiagram-JCYQIIEL-Dn-3CfpR.js +0 -1
- package/dist/assets/classDiagram-v2-OCEON4UE-Dn-3CfpR.js +0 -1
- package/dist/assets/index-CzPLBvXT.css +0 -32
- package/dist/assets/index-DFkBBuSA.js +0 -1549
- package/dist/assets/stateDiagram-v2-UEYNNEHI-BvBxUUrZ.js +0 -1
- package/dist/assets/swimlanesDiagram-ULZ7WXOC-CsE-x4YK.js +0 -8
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import test from 'node:test';
|
|
3
|
+
import { prepareTranscriptMessages } from '../../shared/message-unification.js';
|
|
4
|
+
let nextId = 0;
|
|
5
|
+
const message = (fields) => ({
|
|
6
|
+
id: `m-${(nextId += 1)}`,
|
|
7
|
+
sessionId: 'session',
|
|
8
|
+
timestamp: '2026-08-22T00:00:00.000Z',
|
|
9
|
+
provider: 'claude',
|
|
10
|
+
kind: 'text',
|
|
11
|
+
...fields,
|
|
12
|
+
});
|
|
13
|
+
const prose = () => message({ kind: 'text', role: 'assistant', content: 'Working on it.' });
|
|
14
|
+
const taskCall = (toolName, toolId, toolInput, toolUseResult) => message({ kind: 'tool_use', toolName, toolId, toolInput, toolResult: { content: 'ok', toolUseResult } });
|
|
15
|
+
const todosOf = (entry) => entry.toolInput.todos
|
|
16
|
+
.map((todo) => `${todo.status}:${todo.content}`);
|
|
17
|
+
test('Claude task-tracker calls become one evolving checklist', () => {
|
|
18
|
+
const unified = prepareTranscriptMessages([
|
|
19
|
+
taskCall('TaskCreate', 't1', { subject: 'Read notes', activeForm: 'Reading notes' }, { task: { id: '1' } }),
|
|
20
|
+
taskCall('TaskCreate', 't2', { subject: 'Run tests', activeForm: 'Running tests' }, { task: { id: '2' } }),
|
|
21
|
+
prose(),
|
|
22
|
+
taskCall('TaskUpdate', 't3', { taskId: '1', status: 'completed' }, { success: true }),
|
|
23
|
+
]);
|
|
24
|
+
const snapshots = unified.filter((entry) => entry.toolName === 'TodoWrite');
|
|
25
|
+
assert.equal(snapshots.length, 2, 'the two adjacent creates collapse into one snapshot');
|
|
26
|
+
assert.deepEqual(todosOf(snapshots[0]), ['pending:Read notes', 'pending:Run tests']);
|
|
27
|
+
assert.deepEqual(todosOf(snapshots[1]), ['completed:Read notes', 'pending:Run tests']);
|
|
28
|
+
assert.equal(snapshots[0].toolInput.todos[0].activeForm, 'Reading notes');
|
|
29
|
+
});
|
|
30
|
+
test('a task listing keeps the wording each task was created with', () => {
|
|
31
|
+
const unified = prepareTranscriptMessages([
|
|
32
|
+
taskCall('TaskCreate', 't1', { subject: 'Read notes', activeForm: 'Reading notes' }, { task: { id: '1' } }),
|
|
33
|
+
prose(),
|
|
34
|
+
taskCall('TaskList', 't2', {}, { tasks: [{ id: '1', subject: 'Read notes', status: 'in_progress' }] }),
|
|
35
|
+
]);
|
|
36
|
+
const listed = unified.filter((entry) => entry.toolName === 'TodoWrite').at(-1);
|
|
37
|
+
assert.deepEqual(todosOf(listed), ['in_progress:Read notes']);
|
|
38
|
+
assert.equal(listed.toolInput.todos[0].activeForm, 'Reading notes');
|
|
39
|
+
});
|
|
40
|
+
test('a checklist that has not changed since it was last drawn is dropped', () => {
|
|
41
|
+
const unified = prepareTranscriptMessages([
|
|
42
|
+
message({ kind: 'tool_use', toolName: 'TodoWrite', toolId: 'w1', toolInput: { todos: [{ content: 'Ship it', status: 'pending' }] } }),
|
|
43
|
+
prose(),
|
|
44
|
+
message({ kind: 'tool_use', toolName: 'TodoWrite', toolId: 'w2', toolInput: { todos: [{ content: 'Ship it', status: 'pending' }] } }),
|
|
45
|
+
]);
|
|
46
|
+
assert.deepEqual(unified.map((entry) => entry.toolId), ['w1', undefined]);
|
|
47
|
+
});
|
|
48
|
+
test('a superseded checklist and its result row both go', () => {
|
|
49
|
+
const unified = prepareTranscriptMessages([
|
|
50
|
+
message({ kind: 'tool_use', toolName: 'TodoWrite', toolId: 'w1', toolInput: { todos: [{ content: 'One', status: 'pending' }] } }),
|
|
51
|
+
message({ kind: 'tool_result', toolId: 'w1', content: 'stale' }),
|
|
52
|
+
message({ kind: 'tool_use', toolName: 'TodoWrite', toolId: 'w2', toolInput: { todos: [{ content: 'One', status: 'completed' }] } }),
|
|
53
|
+
message({ kind: 'tool_result', toolId: 'w2', content: 'fresh' }),
|
|
54
|
+
]);
|
|
55
|
+
assert.deepEqual(unified.map((entry) => entry.toolId), ['w2']);
|
|
56
|
+
});
|
|
57
|
+
test('a result that names its call is not shipped alongside it', () => {
|
|
58
|
+
const unified = prepareTranscriptMessages([
|
|
59
|
+
message({ kind: 'tool_use', toolName: 'Bash', toolId: 'b1', toolInput: { command: 'ls' }, toolResult: { content: 'a\nb' } }),
|
|
60
|
+
message({ kind: 'tool_result', toolId: 'b1', content: 'a\nb' }),
|
|
61
|
+
]);
|
|
62
|
+
assert.equal(unified.length, 1, 'the result already rides on the call it belongs to');
|
|
63
|
+
assert.equal(unified[0].kind, 'tool_use');
|
|
64
|
+
assert.equal(unified[0].toolResult?.content, 'a\nb');
|
|
65
|
+
});
|
|
66
|
+
test('a result with no call to attach to is kept', () => {
|
|
67
|
+
const unified = prepareTranscriptMessages([
|
|
68
|
+
message({ kind: 'tool_result', content: 'orphaned output' }),
|
|
69
|
+
]);
|
|
70
|
+
assert.deepEqual(unified.map((entry) => entry.kind), ['tool_result']);
|
|
71
|
+
});
|
|
72
|
+
test("Codex's request_user_input becomes an answered AskUserQuestion", () => {
|
|
73
|
+
const [unified] = prepareTranscriptMessages([
|
|
74
|
+
message({
|
|
75
|
+
provider: 'codex',
|
|
76
|
+
kind: 'tool_use',
|
|
77
|
+
toolName: 'request_user_input',
|
|
78
|
+
toolId: 'call-1',
|
|
79
|
+
toolInput: JSON.stringify({
|
|
80
|
+
questions: [{ id: 'season', header: 'Quick test', question: 'Which season?', options: [{ label: 'Summer' }] }],
|
|
81
|
+
}),
|
|
82
|
+
toolResult: { content: '{"answers":{"season":{"answers":["Summer"]}}}' },
|
|
83
|
+
}),
|
|
84
|
+
]);
|
|
85
|
+
assert.equal(unified.toolName, 'AskUserQuestion');
|
|
86
|
+
assert.deepEqual(unified.toolInput.answers, { 'Which season?': 'Summer' });
|
|
87
|
+
});
|
|
88
|
+
test('a multi-select Codex answer keeps every label it picked', () => {
|
|
89
|
+
const [unified] = prepareTranscriptMessages([
|
|
90
|
+
message({
|
|
91
|
+
provider: 'codex',
|
|
92
|
+
kind: 'tool_use',
|
|
93
|
+
toolName: 'request_user_input',
|
|
94
|
+
toolId: 'call-1',
|
|
95
|
+
toolInput: JSON.stringify({ questions: [{ id: 'scope', question: 'Which areas?', options: [] }] }),
|
|
96
|
+
toolResult: { content: '{"answers":{"scope":{"answers":["Providers","Tests"]}}}' },
|
|
97
|
+
}),
|
|
98
|
+
]);
|
|
99
|
+
assert.deepEqual(unified.toolInput.answers, { 'Which areas?': 'Providers, Tests' });
|
|
100
|
+
});
|
|
101
|
+
test("Claude's AskUserQuestion answer is folded into the question it answers", () => {
|
|
102
|
+
const [unified] = prepareTranscriptMessages([
|
|
103
|
+
message({
|
|
104
|
+
kind: 'tool_use',
|
|
105
|
+
toolName: 'AskUserQuestion',
|
|
106
|
+
toolId: 'call-1',
|
|
107
|
+
toolInput: { questions: [{ question: 'What next?', header: 'Focus', options: [{ label: 'Branch changes' }] }] },
|
|
108
|
+
toolResult: {
|
|
109
|
+
content: 'Your questions have been answered.',
|
|
110
|
+
toolUseResult: { answers: { 'What next?': 'Branch changes' } },
|
|
111
|
+
},
|
|
112
|
+
}),
|
|
113
|
+
]);
|
|
114
|
+
assert.deepEqual(unified.toolInput.answers, { 'What next?': 'Branch changes' });
|
|
115
|
+
});
|
|
116
|
+
test('a live Claude answer is recovered from the acknowledgement sentence', () => {
|
|
117
|
+
const [unified] = prepareTranscriptMessages([
|
|
118
|
+
message({
|
|
119
|
+
kind: 'tool_use',
|
|
120
|
+
toolName: 'AskUserQuestion',
|
|
121
|
+
toolId: 'call-1',
|
|
122
|
+
toolInput: { questions: [{ question: 'What next?', options: [] }] },
|
|
123
|
+
toolResult: {
|
|
124
|
+
content: 'Your questions have been answered: "What next?"="Branch changes". You can now continue.',
|
|
125
|
+
},
|
|
126
|
+
}),
|
|
127
|
+
]);
|
|
128
|
+
assert.deepEqual(unified.toolInput.answers, { 'What next?': 'Branch changes' });
|
|
129
|
+
});
|
|
130
|
+
test('a question that was never answered carries no answers', () => {
|
|
131
|
+
const [unified] = prepareTranscriptMessages([
|
|
132
|
+
message({
|
|
133
|
+
kind: 'tool_use',
|
|
134
|
+
toolName: 'AskUserQuestion',
|
|
135
|
+
toolId: 'call-1',
|
|
136
|
+
toolInput: { questions: [{ question: 'What next?', options: [] }] },
|
|
137
|
+
toolResult: { content: '<tool_use_error>InputValidationError</tool_use_error>', isError: true },
|
|
138
|
+
}),
|
|
139
|
+
]);
|
|
140
|
+
assert.equal(unified.toolInput.answers, undefined);
|
|
141
|
+
});
|
|
142
|
+
test('a huge tool output is capped, and says how much it dropped', () => {
|
|
143
|
+
const body = 'x'.repeat(120_000);
|
|
144
|
+
const [unified] = prepareTranscriptMessages([
|
|
145
|
+
message({
|
|
146
|
+
kind: 'tool_use',
|
|
147
|
+
toolName: 'Read',
|
|
148
|
+
toolId: 'r1',
|
|
149
|
+
toolInput: { file_path: '/tmp/shot.png' },
|
|
150
|
+
toolResult: { content: body, toolUseResult: { file: { base64: body } } },
|
|
151
|
+
}),
|
|
152
|
+
]);
|
|
153
|
+
const content = String(unified.toolResult?.content);
|
|
154
|
+
assert.ok(content.length < body.length / 2, 'the output must not survive at full size');
|
|
155
|
+
assert.match(content, /… 80000 more characters$/);
|
|
156
|
+
const nested = (unified.toolResult?.toolUseResult).file.base64;
|
|
157
|
+
assert.equal(nested, content, 'the structured copy is capped the same way');
|
|
158
|
+
});
|
|
159
|
+
test('a search result keeps every file it found', () => {
|
|
160
|
+
const filenames = Array.from({ length: 900 }, (_, index) => `/repo/src/file-${index}.ts`);
|
|
161
|
+
const [unified] = prepareTranscriptMessages([
|
|
162
|
+
message({
|
|
163
|
+
kind: 'tool_use',
|
|
164
|
+
toolName: 'Glob',
|
|
165
|
+
toolId: 'g1',
|
|
166
|
+
toolInput: { pattern: '**/*.ts' },
|
|
167
|
+
toolResult: { content: 'ok', toolUseResult: { numFiles: filenames.length, filenames } },
|
|
168
|
+
}),
|
|
169
|
+
]);
|
|
170
|
+
const result = unified.toolResult?.toolUseResult;
|
|
171
|
+
assert.equal(result.numFiles, 900);
|
|
172
|
+
assert.deepEqual(result.filenames, filenames);
|
|
173
|
+
});
|
|
174
|
+
//# sourceMappingURL=message-unification.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"message-unification.test.js","sourceRoot":"","sources":["../../../../server/shared/tests/message-unification.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import { mkdir, mkdtemp, realpath, rm, symlink, writeFile } from 'node:fs/promises';
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import test, { after } from 'node:test';
|
|
6
|
+
import { fileURLToPath } from 'node:url';
|
|
7
|
+
// The Claude projects root is derived from `os.homedir()` when the utils module
|
|
8
|
+
// is first imported, so HOME has to point at the fixture home *before* that
|
|
9
|
+
// import runs. The fixture lives beside this file rather than under the temp
|
|
10
|
+
// directory: a home under `/tmp` would put everything under a read-only root
|
|
11
|
+
// and make the escape tests pass for the wrong reason.
|
|
12
|
+
const testDirectory = path.dirname(fileURLToPath(import.meta.url));
|
|
13
|
+
const fixtureRoot = await realpath(await mkdtemp(path.join(testDirectory, 'read-only-roots-fixture-')));
|
|
14
|
+
const fixtureHome = path.join(fixtureRoot, 'home');
|
|
15
|
+
const claudeProjectsRoot = path.join(fixtureHome, '.claude', 'projects');
|
|
16
|
+
// Under no read-only root: not the temp directory, not the Claude projects dir.
|
|
17
|
+
const outsideDirectory = path.join(fixtureRoot, 'outside');
|
|
18
|
+
await mkdir(claudeProjectsRoot, { recursive: true });
|
|
19
|
+
await mkdir(outsideDirectory);
|
|
20
|
+
const previousHome = process.env.HOME;
|
|
21
|
+
const previousUserProfile = process.env.USERPROFILE;
|
|
22
|
+
process.env.HOME = fixtureHome;
|
|
23
|
+
process.env.USERPROFILE = fixtureHome;
|
|
24
|
+
const { resolvePathUnderRoots, resolveReadOnlyRootPath, validateWorkspacePath } = await import('../../shared/utils.js');
|
|
25
|
+
after(async () => {
|
|
26
|
+
if (previousHome === undefined) {
|
|
27
|
+
delete process.env.HOME;
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
process.env.HOME = previousHome;
|
|
31
|
+
}
|
|
32
|
+
if (previousUserProfile === undefined) {
|
|
33
|
+
delete process.env.USERPROFILE;
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
process.env.USERPROFILE = previousUserProfile;
|
|
37
|
+
}
|
|
38
|
+
await rm(fixtureRoot, { recursive: true, force: true });
|
|
39
|
+
});
|
|
40
|
+
test('a path under the system temp directory resolves as readable', async () => {
|
|
41
|
+
const temporaryDirectory = await mkdtemp(path.join(os.tmpdir(), 'read-only-root-'));
|
|
42
|
+
try {
|
|
43
|
+
const filePath = path.join(temporaryDirectory, 'agent.output');
|
|
44
|
+
await writeFile(filePath, 'agent output', 'utf8');
|
|
45
|
+
// A background command's log is quoted straight out of the transcript, so
|
|
46
|
+
// this exact shape has to resolve.
|
|
47
|
+
const resolvedDirectory = await resolveReadOnlyRootPath(temporaryDirectory);
|
|
48
|
+
assert.ok(resolvedDirectory);
|
|
49
|
+
assert.equal(await resolveReadOnlyRootPath(filePath), path.join(resolvedDirectory, 'agent.output'));
|
|
50
|
+
}
|
|
51
|
+
finally {
|
|
52
|
+
await rm(temporaryDirectory, { recursive: true, force: true });
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
test('a background agent output link resolves to its transcript under the Claude projects directory', async () => {
|
|
56
|
+
const temporaryDirectory = await mkdtemp(path.join(os.tmpdir(), 'read-only-root-'));
|
|
57
|
+
try {
|
|
58
|
+
// Claude writes `/tmp/claude-<uid>/<project>/<session>/tasks/<task>.output`
|
|
59
|
+
// as a symlink to the agent's transcript under `~/.claude/projects`.
|
|
60
|
+
const transcriptDirectory = path.join(claudeProjectsRoot, '-home-user-project', 'session-1', 'subagents');
|
|
61
|
+
await mkdir(transcriptDirectory, { recursive: true });
|
|
62
|
+
const transcriptPath = path.join(transcriptDirectory, 'agent-1.jsonl');
|
|
63
|
+
await writeFile(transcriptPath, '{"type":"assistant"}\n', 'utf8');
|
|
64
|
+
const tasksDirectory = path.join(temporaryDirectory, 'tasks');
|
|
65
|
+
await mkdir(tasksDirectory);
|
|
66
|
+
const outputLink = path.join(tasksDirectory, 'task-1.output');
|
|
67
|
+
await symlink(transcriptPath, outputLink);
|
|
68
|
+
assert.equal(await resolveReadOnlyRootPath(outputLink), transcriptPath);
|
|
69
|
+
assert.equal(await resolveReadOnlyRootPath(transcriptPath), transcriptPath);
|
|
70
|
+
}
|
|
71
|
+
finally {
|
|
72
|
+
await rm(temporaryDirectory, { recursive: true, force: true });
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
test('the temp directory stays read-only: it is still not a valid workspace location', async () => {
|
|
76
|
+
const temporaryDirectory = await mkdtemp(path.join(os.tmpdir(), 'read-only-root-'));
|
|
77
|
+
try {
|
|
78
|
+
// Being browsable must not make it writable — the write policy is
|
|
79
|
+
// `validateWorkspacePath` and it does not consult the read-only roots.
|
|
80
|
+
const validation = await validateWorkspacePath(temporaryDirectory);
|
|
81
|
+
assert.equal(validation.valid, false);
|
|
82
|
+
}
|
|
83
|
+
finally {
|
|
84
|
+
await rm(temporaryDirectory, { recursive: true, force: true });
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
test('paths outside the read-only roots do not resolve', async () => {
|
|
88
|
+
assert.equal(await resolveReadOnlyRootPath(outsideDirectory), null);
|
|
89
|
+
assert.equal(await resolveReadOnlyRootPath('/etc/passwd'), null);
|
|
90
|
+
assert.equal(await resolveReadOnlyRootPath('relative/path'), null);
|
|
91
|
+
assert.equal(await resolveReadOnlyRootPath(''), null);
|
|
92
|
+
});
|
|
93
|
+
test('a symlink planted in the temp directory cannot read outside every root', async () => {
|
|
94
|
+
const temporaryDirectory = await mkdtemp(path.join(os.tmpdir(), 'read-only-root-'));
|
|
95
|
+
try {
|
|
96
|
+
await writeFile(path.join(outsideDirectory, 'secret.txt'), 'secret', 'utf8');
|
|
97
|
+
const escapeLink = path.join(temporaryDirectory, 'escape');
|
|
98
|
+
await symlink(outsideDirectory, escapeLink);
|
|
99
|
+
// The name is under a read-only root but the file is not, so resolving the
|
|
100
|
+
// link before comparing is what keeps this closed.
|
|
101
|
+
assert.equal(await resolveReadOnlyRootPath(escapeLink), null);
|
|
102
|
+
assert.equal(await resolveReadOnlyRootPath(path.join(escapeLink, 'secret.txt')), null);
|
|
103
|
+
}
|
|
104
|
+
finally {
|
|
105
|
+
await rm(temporaryDirectory, { recursive: true, force: true });
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
test('a root that does not exist does not stop later roots from matching', async () => {
|
|
109
|
+
const temporaryDirectory = await mkdtemp(path.join(os.tmpdir(), 'read-only-root-'));
|
|
110
|
+
try {
|
|
111
|
+
// `/tmp` does not exist on Windows; the temp directory listed after it
|
|
112
|
+
// must still be checked rather than the whole search giving up.
|
|
113
|
+
const missingRoot = path.join(fixtureRoot, 'missing');
|
|
114
|
+
assert.equal(await resolvePathUnderRoots(temporaryDirectory, [missingRoot]), null);
|
|
115
|
+
assert.equal(await resolvePathUnderRoots(temporaryDirectory, [missingRoot, os.tmpdir()]), await realpath(temporaryDirectory));
|
|
116
|
+
}
|
|
117
|
+
finally {
|
|
118
|
+
await rm(temporaryDirectory, { recursive: true, force: true });
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
test('traversal out of the temp directory does not resolve', async () => {
|
|
122
|
+
assert.equal(await resolveReadOnlyRootPath(`${os.tmpdir()}/../etc/passwd`), null);
|
|
123
|
+
});
|
|
124
|
+
//# sourceMappingURL=read-only-roots.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"read-only-roots.test.js","sourceRoot":"","sources":["../../../../server/shared/tests/read-only-roots.test.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACpF,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,IAAI,EAAE,EAAE,KAAK,EAAE,MAAM,WAAW,CAAC;AACxC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,gFAAgF;AAChF,4EAA4E;AAC5E,6EAA6E;AAC7E,6EAA6E;AAC7E,uDAAuD;AACvD,MAAM,aAAa,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;AACnE,MAAM,WAAW,GAAG,MAAM,QAAQ,CAAC,MAAM,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,0BAA0B,CAAC,CAAC,CAAC,CAAC;AACxG,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,MAAM,CAAC,CAAC;AACnD,MAAM,kBAAkB,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,SAAS,EAAE,UAAU,CAAC,CAAC;AACzE,gFAAgF;AAChF,MAAM,gBAAgB,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC;AAC3D,MAAM,KAAK,CAAC,kBAAkB,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;AACrD,MAAM,KAAK,CAAC,gBAAgB,CAAC,CAAC;AAE9B,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC;AACtC,MAAM,mBAAmB,GAAG,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC;AACpD,OAAO,CAAC,GAAG,CAAC,IAAI,GAAG,WAAW,CAAC;AAC/B,OAAO,CAAC,GAAG,CAAC,WAAW,GAAG,WAAW,CAAC;AAEtC,MAAM,EAAE,qBAAqB,EAAE,uBAAuB,EAAE,qBAAqB,EAAE,GAAG,MAAM,MAAM,CAAC,mBAAmB,CAAC,CAAC;AAEpH,KAAK,CAAC,KAAK,IAAI,EAAE;IACf,IAAI,YAAY,KAAK,SAAS,EAAE,CAAC;QAC/B,OAAO,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC;IAC1B,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,GAAG,CAAC,IAAI,GAAG,YAAY,CAAC;IAClC,CAAC;IACD,IAAI,mBAAmB,KAAK,SAAS,EAAE,CAAC;QACtC,OAAO,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC;IACjC,CAAC;SAAM,CAAC;QACN,OAAO,CAAC,GAAG,CAAC,WAAW,GAAG,mBAAmB,CAAC;IAChD,CAAC;IACD,MAAM,EAAE,CAAC,WAAW,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;AAC1D,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,6DAA6D,EAAE,KAAK,IAAI,EAAE;IAC7E,MAAM,kBAAkB,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,iBAAiB,CAAC,CAAC,CAAC;IAEpF,IAAI,CAAC;QACH,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,kBAAkB,EAAE,cAAc,CAAC,CAAC;QAC/D,MAAM,SAAS,CAAC,QAAQ,EAAE,cAAc,EAAE,MAAM,CAAC,CAAC;QAElD,0EAA0E;QAC1E,mCAAmC;QACnC,MAAM,iBAAiB,GAAG,MAAM,uBAAuB,CAAC,kBAAkB,CAAC,CAAC;QAC5E,MAAM,CAAC,EAAE,CAAC,iBAAiB,CAAC,CAAC;QAC7B,MAAM,CAAC,KAAK,CAAC,MAAM,uBAAuB,CAAC,QAAQ,CAAC,EAAE,IAAI,CAAC,IAAI,CAAC,iBAAiB,EAAE,cAAc,CAAC,CAAC,CAAC;IACtG,CAAC;YAAS,CAAC;QACT,MAAM,EAAE,CAAC,kBAAkB,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACjE,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,+FAA+F,EAAE,KAAK,IAAI,EAAE;IAC/G,MAAM,kBAAkB,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,iBAAiB,CAAC,CAAC,CAAC;IAEpF,IAAI,CAAC;QACH,4EAA4E;QAC5E,qEAAqE;QACrE,MAAM,mBAAmB,GAAG,IAAI,CAAC,IAAI,CAAC,kBAAkB,EAAE,oBAAoB,EAAE,WAAW,EAAE,WAAW,CAAC,CAAC;QAC1G,MAAM,KAAK,CAAC,mBAAmB,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACtD,MAAM,cAAc,GAAG,IAAI,CAAC,IAAI,CAAC,mBAAmB,EAAE,eAAe,CAAC,CAAC;QACvE,MAAM,SAAS,CAAC,cAAc,EAAE,wBAAwB,EAAE,MAAM,CAAC,CAAC;QAElE,MAAM,cAAc,GAAG,IAAI,CAAC,IAAI,CAAC,kBAAkB,EAAE,OAAO,CAAC,CAAC;QAC9D,MAAM,KAAK,CAAC,cAAc,CAAC,CAAC;QAC5B,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,eAAe,CAAC,CAAC;QAC9D,MAAM,OAAO,CAAC,cAAc,EAAE,UAAU,CAAC,CAAC;QAE1C,MAAM,CAAC,KAAK,CAAC,MAAM,uBAAuB,CAAC,UAAU,CAAC,EAAE,cAAc,CAAC,CAAC;QACxE,MAAM,CAAC,KAAK,CAAC,MAAM,uBAAuB,CAAC,cAAc,CAAC,EAAE,cAAc,CAAC,CAAC;IAC9E,CAAC;YAAS,CAAC;QACT,MAAM,EAAE,CAAC,kBAAkB,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACjE,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,gFAAgF,EAAE,KAAK,IAAI,EAAE;IAChG,MAAM,kBAAkB,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,iBAAiB,CAAC,CAAC,CAAC;IAEpF,IAAI,CAAC;QACH,kEAAkE;QAClE,uEAAuE;QACvE,MAAM,UAAU,GAAG,MAAM,qBAAqB,CAAC,kBAAkB,CAAC,CAAC;QACnE,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;IACxC,CAAC;YAAS,CAAC;QACT,MAAM,EAAE,CAAC,kBAAkB,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACjE,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,kDAAkD,EAAE,KAAK,IAAI,EAAE;IAClE,MAAM,CAAC,KAAK,CAAC,MAAM,uBAAuB,CAAC,gBAAgB,CAAC,EAAE,IAAI,CAAC,CAAC;IACpE,MAAM,CAAC,KAAK,CAAC,MAAM,uBAAuB,CAAC,aAAa,CAAC,EAAE,IAAI,CAAC,CAAC;IACjE,MAAM,CAAC,KAAK,CAAC,MAAM,uBAAuB,CAAC,eAAe,CAAC,EAAE,IAAI,CAAC,CAAC;IACnE,MAAM,CAAC,KAAK,CAAC,MAAM,uBAAuB,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,CAAC;AACxD,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,wEAAwE,EAAE,KAAK,IAAI,EAAE;IACxF,MAAM,kBAAkB,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,iBAAiB,CAAC,CAAC,CAAC;IAEpF,IAAI,CAAC;QACH,MAAM,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,gBAAgB,EAAE,YAAY,CAAC,EAAE,QAAQ,EAAE,MAAM,CAAC,CAAC;QAC7E,MAAM,UAAU,GAAG,IAAI,CAAC,IAAI,CAAC,kBAAkB,EAAE,QAAQ,CAAC,CAAC;QAC3D,MAAM,OAAO,CAAC,gBAAgB,EAAE,UAAU,CAAC,CAAC;QAE5C,2EAA2E;QAC3E,mDAAmD;QACnD,MAAM,CAAC,KAAK,CAAC,MAAM,uBAAuB,CAAC,UAAU,CAAC,EAAE,IAAI,CAAC,CAAC;QAC9D,MAAM,CAAC,KAAK,CAAC,MAAM,uBAAuB,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,YAAY,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;IACzF,CAAC;YAAS,CAAC;QACT,MAAM,EAAE,CAAC,kBAAkB,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACjE,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,oEAAoE,EAAE,KAAK,IAAI,EAAE;IACpF,MAAM,kBAAkB,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,iBAAiB,CAAC,CAAC,CAAC;IAEpF,IAAI,CAAC;QACH,uEAAuE;QACvE,gEAAgE;QAChE,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,SAAS,CAAC,CAAC;QACtD,MAAM,CAAC,KAAK,CAAC,MAAM,qBAAqB,CAAC,kBAAkB,EAAE,CAAC,WAAW,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC;QACnF,MAAM,CAAC,KAAK,CACV,MAAM,qBAAqB,CAAC,kBAAkB,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC,MAAM,EAAE,CAAC,CAAC,EAC3E,MAAM,QAAQ,CAAC,kBAAkB,CAAC,CACnC,CAAC;IACJ,CAAC;YAAS,CAAC;QACT,MAAM,EAAE,CAAC,kBAAkB,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IACjE,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,sDAAsD,EAAE,KAAK,IAAI,EAAE;IACtE,MAAM,CAAC,KAAK,CAAC,MAAM,uBAAuB,CAAC,GAAG,EAAE,CAAC,MAAM,EAAE,gBAAgB,CAAC,EAAE,IAAI,CAAC,CAAC;AACpF,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import test from 'node:test';
|
|
3
|
+
import { stripAnsiSequences } from '../../shared/utils.js';
|
|
4
|
+
test('strips the SGR colors a CLI writes around its own warnings', () => {
|
|
5
|
+
const styled = '\u001B[93m\u001B[1m! \u001B[0m\u001B[0mpermission requested: external_directory';
|
|
6
|
+
assert.equal(stripAnsiSequences(styled), '! permission requested: external_directory');
|
|
7
|
+
});
|
|
8
|
+
test('strips CSI cursor control, including the 8-bit introducer', () => {
|
|
9
|
+
assert.equal(stripAnsiSequences('a\u001B[2Kb\u001B[?25lc'), 'abc');
|
|
10
|
+
assert.equal(stripAnsiSequences('a\u009B31mb'), 'ab');
|
|
11
|
+
});
|
|
12
|
+
test('strips an OSC sequence with either terminator, without eating the rest', () => {
|
|
13
|
+
assert.equal(stripAnsiSequences('\u001B]0;title\u0007done'), 'done');
|
|
14
|
+
assert.equal(stripAnsiSequences('\u001B]8;;https://example.com\u001B\\link'), 'link');
|
|
15
|
+
});
|
|
16
|
+
test('strips other ECMA-48 escape sequences such as charset selection', () => {
|
|
17
|
+
assert.equal(stripAnsiSequences('\u001B(Bplain'), 'plain');
|
|
18
|
+
});
|
|
19
|
+
test('leaves text without escape sequences untouched', () => {
|
|
20
|
+
assert.equal(stripAnsiSequences('plain [93m-looking text'), 'plain [93m-looking text');
|
|
21
|
+
});
|
|
22
|
+
test('strips a CSI sequence that carries a private parameter byte', () => {
|
|
23
|
+
assert.equal(stripAnsiSequences('a\u001B[>cb'), 'ab');
|
|
24
|
+
});
|
|
25
|
+
test('stops an unterminated OSC at the next escape, so later styling still strips', () => {
|
|
26
|
+
assert.equal(stripAnsiSequences('\u001B]0;unterminated\u001B[31mred\u001B[0m'), '0;unterminatedred');
|
|
27
|
+
});
|
|
28
|
+
test('reduces a styling-only chunk to an empty string', () => {
|
|
29
|
+
assert.equal(stripAnsiSequences('\u001B[0m\u001B[2K'), '');
|
|
30
|
+
});
|
|
31
|
+
//# sourceMappingURL=strip-ansi-sequences.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"strip-ansi-sequences.test.js","sourceRoot":"","sources":["../../../../server/shared/tests/strip-ansi-sequences.test.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAEvD,IAAI,CAAC,4DAA4D,EAAE,GAAG,EAAE;IACtE,MAAM,MAAM,GAAG,iFAAiF,CAAC;IACjG,MAAM,CAAC,KAAK,CAAC,kBAAkB,CAAC,MAAM,CAAC,EAAE,4CAA4C,CAAC,CAAC;AACzF,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,2DAA2D,EAAE,GAAG,EAAE;IACrE,MAAM,CAAC,KAAK,CAAC,kBAAkB,CAAC,yBAAyB,CAAC,EAAE,KAAK,CAAC,CAAC;IACnE,MAAM,CAAC,KAAK,CAAC,kBAAkB,CAAC,aAAa,CAAC,EAAE,IAAI,CAAC,CAAC;AACxD,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,wEAAwE,EAAE,GAAG,EAAE;IAClF,MAAM,CAAC,KAAK,CAAC,kBAAkB,CAAC,0BAA0B,CAAC,EAAE,MAAM,CAAC,CAAC;IACrE,MAAM,CAAC,KAAK,CAAC,kBAAkB,CAAC,2CAA2C,CAAC,EAAE,MAAM,CAAC,CAAC;AACxF,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,iEAAiE,EAAE,GAAG,EAAE;IAC3E,MAAM,CAAC,KAAK,CAAC,kBAAkB,CAAC,eAAe,CAAC,EAAE,OAAO,CAAC,CAAC;AAC7D,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,gDAAgD,EAAE,GAAG,EAAE;IAC1D,MAAM,CAAC,KAAK,CAAC,kBAAkB,CAAC,yBAAyB,CAAC,EAAE,yBAAyB,CAAC,CAAC;AACzF,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,6DAA6D,EAAE,GAAG,EAAE;IACvE,MAAM,CAAC,KAAK,CAAC,kBAAkB,CAAC,aAAa,CAAC,EAAE,IAAI,CAAC,CAAC;AACxD,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,6EAA6E,EAAE,GAAG,EAAE;IACvF,MAAM,CAAC,KAAK,CACV,kBAAkB,CAAC,6CAA6C,CAAC,EACjE,mBAAmB,CACpB,CAAC;AACJ,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,iDAAiD,EAAE,GAAG,EAAE;IAC3D,MAAM,CAAC,KAAK,CAAC,kBAAkB,CAAC,oBAAoB,CAAC,EAAE,EAAE,CAAC,CAAC;AAC7D,CAAC,CAAC,CAAC"}
|
|
@@ -59,6 +59,19 @@ export class AppError extends Error {
|
|
|
59
59
|
this.details = options.details;
|
|
60
60
|
}
|
|
61
61
|
}
|
|
62
|
+
/**
|
|
63
|
+
* Flattens a failed `git` invocation into one searchable string: the error
|
|
64
|
+
* message followed by whatever stderr/stdout the process runner attached.
|
|
65
|
+
*
|
|
66
|
+
* Used by the Git routes and the branch compare service to classify git
|
|
67
|
+
* failures (e.g. "not a git repository", "unknown revision") and to surface
|
|
68
|
+
* git's own explanation as error `details`. Accepts any thrown value; missing
|
|
69
|
+
* fields contribute nothing, so the result may be empty.
|
|
70
|
+
*/
|
|
71
|
+
export function getGitErrorDetails(error) {
|
|
72
|
+
const details = error;
|
|
73
|
+
return `${details?.message ?? ''} ${details?.stderr ?? ''} ${details?.stdout ?? ''}`.trim();
|
|
74
|
+
}
|
|
62
75
|
// ---------------------------
|
|
63
76
|
//----------------- WORKSPACE PATH VALIDATION UTILITIES ------------
|
|
64
77
|
/**
|
|
@@ -100,6 +113,78 @@ export const FORBIDDEN_WORKSPACE_PATHS = [
|
|
|
100
113
|
'C:\\System Volume Information',
|
|
101
114
|
'C:\\$Recycle.Bin',
|
|
102
115
|
];
|
|
116
|
+
/**
|
|
117
|
+
* Roots the file browser and viewer may read from even though they are outside
|
|
118
|
+
* every project.
|
|
119
|
+
*
|
|
120
|
+
* Claude writes a background agent's output file and a background command's log
|
|
121
|
+
* under the system temp directory, and a transcript quotes those paths
|
|
122
|
+
* verbatim, so the file browser and the file viewer have to be able to follow
|
|
123
|
+
* them. `/tmp` is listed literally as well as via `os.tmpdir()` because the two
|
|
124
|
+
* differ on macOS, where the temp directory is under `/var/folders`.
|
|
125
|
+
*
|
|
126
|
+
* A background agent's `.output` file is only a symlink to the agent's
|
|
127
|
+
* transcript, `~/.claude/projects/<project>/<session>/subagents/agent-<id>.jsonl`.
|
|
128
|
+
* Symlinks are resolved before the root comparison, so following it needs the
|
|
129
|
+
* Claude projects directory to be a root as well. Those are the user's own
|
|
130
|
+
* transcripts, which the sessions API already serves; the directory is located
|
|
131
|
+
* the same way the session watcher and synchronizer locate it.
|
|
132
|
+
*
|
|
133
|
+
* Being a read-only root grants reads only: the file-tree write paths resolve
|
|
134
|
+
* against the project root alone, so nothing under these can be changed
|
|
135
|
+
* through the file API. Whether one may become a workspace is decided
|
|
136
|
+
* separately by `validateWorkspacePath` — the temp directories are on
|
|
137
|
+
* `FORBIDDEN_WORKSPACE_PATHS`; the Claude projects directory is not, it is
|
|
138
|
+
* simply wherever `WORKSPACES_ROOT` puts it.
|
|
139
|
+
*/
|
|
140
|
+
const READ_ONLY_ROOTS = [...new Set([
|
|
141
|
+
'/tmp',
|
|
142
|
+
os.tmpdir(),
|
|
143
|
+
path.join(os.homedir(), '.claude', 'projects'),
|
|
144
|
+
])];
|
|
145
|
+
/**
|
|
146
|
+
* Resolves `targetPath` when it lives under one of `roots`, or `null` when it
|
|
147
|
+
* does not.
|
|
148
|
+
*
|
|
149
|
+
* Symlinks are resolved before the comparison, so a link planted under a root
|
|
150
|
+
* cannot be used to read somewhere else through it. Each root is resolved on
|
|
151
|
+
* its own, so one that does not exist on this machine — `/tmp` on Windows — is
|
|
152
|
+
* skipped and the roots after it are still checked.
|
|
153
|
+
*/
|
|
154
|
+
export async function resolvePathUnderRoots(targetPath, roots) {
|
|
155
|
+
const normalizedTarget = normalizeProjectPath(targetPath);
|
|
156
|
+
if (!normalizedTarget || !path.isAbsolute(normalizedTarget)) {
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
let resolvedPath;
|
|
160
|
+
try {
|
|
161
|
+
resolvedPath = normalizeProjectPath(await realpath(path.resolve(normalizedTarget)));
|
|
162
|
+
}
|
|
163
|
+
catch {
|
|
164
|
+
// A path that cannot be resolved is not readable through here either.
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
for (const root of roots) {
|
|
168
|
+
let resolvedRoot;
|
|
169
|
+
try {
|
|
170
|
+
resolvedRoot = normalizeProjectPath(await realpath(root));
|
|
171
|
+
}
|
|
172
|
+
catch {
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
if (resolvedPath === resolvedRoot || resolvedPath.startsWith(`${resolvedRoot}${path.sep}`)) {
|
|
176
|
+
return resolvedPath;
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
return null;
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Resolves a path that is readable because it lives under a read-only root,
|
|
183
|
+
* or `null` when it does not.
|
|
184
|
+
*/
|
|
185
|
+
export function resolveReadOnlyRootPath(targetPath) {
|
|
186
|
+
return resolvePathUnderRoots(targetPath, READ_ONLY_ROOTS);
|
|
187
|
+
}
|
|
103
188
|
function stripWindowsLongPathPrefix(inputPath) {
|
|
104
189
|
if (inputPath.startsWith('\\\\?\\UNC\\')) {
|
|
105
190
|
return `\\\\${inputPath.slice('\\\\?\\UNC\\'.length)}`;
|
|
@@ -304,6 +389,40 @@ export function createCompleteMessage(opts) {
|
|
|
304
389
|
});
|
|
305
390
|
}
|
|
306
391
|
// ---------------------------
|
|
392
|
+
//----------------- SUBAGENT TIMELINE UTILITIES ------------
|
|
393
|
+
/**
|
|
394
|
+
* Longest tool output kept on one subagent activity.
|
|
395
|
+
*
|
|
396
|
+
* A subagent's timeline is nested inside a collapsed panel, so it is a preview
|
|
397
|
+
* of what the agent did, never the primary place its output is read. Sending
|
|
398
|
+
* every child command's full output made the history payload of an
|
|
399
|
+
* agent-heavy session grow by megabytes for content almost nobody expands.
|
|
400
|
+
*/
|
|
401
|
+
const MAX_SUBAGENT_ACTIVITY_CONTENT = 4000;
|
|
402
|
+
function truncateForPreview(value) {
|
|
403
|
+
if (typeof value !== 'string' || value.length <= MAX_SUBAGENT_ACTIVITY_CONTENT) {
|
|
404
|
+
return value;
|
|
405
|
+
}
|
|
406
|
+
const omitted = value.length - MAX_SUBAGENT_ACTIVITY_CONTENT;
|
|
407
|
+
return `${value.slice(0, MAX_SUBAGENT_ACTIVITY_CONTENT)}\n… ${omitted} more characters`;
|
|
408
|
+
}
|
|
409
|
+
/**
|
|
410
|
+
* Trims one subagent activity down to what its nested preview can show.
|
|
411
|
+
*
|
|
412
|
+
* Used by both provider session adapters so a Claude agent's timeline and a
|
|
413
|
+
* Codex agent's timeline cost the same to transport.
|
|
414
|
+
*/
|
|
415
|
+
export function truncateSubagentActivity(activity) {
|
|
416
|
+
const truncatedContent = truncateForPreview(activity.content);
|
|
417
|
+
const truncatedResult = activity.toolResult
|
|
418
|
+
? { ...activity.toolResult, content: truncateForPreview(activity.toolResult.content) }
|
|
419
|
+
: activity.toolResult;
|
|
420
|
+
if (truncatedContent === activity.content && truncatedResult === activity.toolResult) {
|
|
421
|
+
return activity;
|
|
422
|
+
}
|
|
423
|
+
return { ...activity, content: truncatedContent, toolResult: truncatedResult };
|
|
424
|
+
}
|
|
425
|
+
// ---------------------------
|
|
307
426
|
//----------------- CONVERSATION HISTORY PAGINATION UTILITIES ------------
|
|
308
427
|
/**
|
|
309
428
|
* Slices one page from the END of a chronologically ordered message list.
|
|
@@ -416,6 +535,21 @@ export function buildDefaultProviderCurrentActiveModel(models) {
|
|
|
416
535
|
model: models.DEFAULT,
|
|
417
536
|
};
|
|
418
537
|
}
|
|
538
|
+
/**
|
|
539
|
+
* True for a model name a provider fabricated rather than actually ran.
|
|
540
|
+
*
|
|
541
|
+
* Claude Code stamps locally-synthesized transcript rows (API-error notices,
|
|
542
|
+
* usage-limit messages) with `model: "<synthetic>"`. Angle-bracketed values are
|
|
543
|
+
* placeholders, never real model ids, so neither the active-model lookup nor
|
|
544
|
+
* the per-message model a reply is labelled with may adopt one.
|
|
545
|
+
*
|
|
546
|
+
* Used by the Claude models provider (which walks the transcript backwards for
|
|
547
|
+
* the session's model) and by the Claude sessions provider (which stamps each
|
|
548
|
+
* assistant message with the model that answered it).
|
|
549
|
+
*/
|
|
550
|
+
export function isPlaceholderProviderModel(model) {
|
|
551
|
+
return model.startsWith('<') && model.endsWith('>');
|
|
552
|
+
}
|
|
419
553
|
// ---------------------------
|
|
420
554
|
//----------------- WEBSOCKET PAYLOAD PARSING UTILITIES ------------
|
|
421
555
|
/**
|
|
@@ -902,6 +1036,32 @@ export function flattenPromptForWindowsShell(prompt) {
|
|
|
902
1036
|
}
|
|
903
1037
|
// ---------------------------
|
|
904
1038
|
//----------------- TERMINAL OUTPUT UTILITIES ------------
|
|
1039
|
+
/**
|
|
1040
|
+
* Matches the escape sequences a CLI emits when it believes it is writing to a
|
|
1041
|
+
* terminal, in the three shapes those tools actually produce:
|
|
1042
|
+
* - OSC (`ESC ]` … terminated by BEL or ST), used for titles and hyperlinks.
|
|
1043
|
+
* - CSI (`ESC [`, or the 8-bit `\u009B` introducer that stands in for both
|
|
1044
|
+
* bytes), used for SGR colors and cursor control.
|
|
1045
|
+
* - any other ECMA-48 escape sequence: `ESC`, optional intermediate bytes
|
|
1046
|
+
* (`0x20`-`0x2F`), one final byte (`0x30`-`0x7E`), such as `ESC ( B`.
|
|
1047
|
+
*
|
|
1048
|
+
* OSC and CSI are listed first so their terminators are consumed by the
|
|
1049
|
+
* specific alternative rather than by the generic one.
|
|
1050
|
+
*/
|
|
1051
|
+
const ANSI_ESCAPE_SEQUENCE_REGEX = /\u001B\][^\u0007\u001B]*(?:\u0007|\u001B\\)|(?:\u001B\[|\u009B)[0-?]*[ -/]*[@-~]|\u001B[ -/]*[0-~]/g;
|
|
1052
|
+
/**
|
|
1053
|
+
* Removes ANSI escape sequences from text captured off a CLI's stdout or
|
|
1054
|
+
* stderr. Provider runtimes, session readers, and the shell WebSocket share
|
|
1055
|
+
* this because every one of them forwards captured process output to a web
|
|
1056
|
+
* client that renders plain text: left in, the escapes show up verbatim
|
|
1057
|
+
* (`[93m[1m!`) instead of as styling.
|
|
1058
|
+
*
|
|
1059
|
+
* The result can be empty when the input was styling only, so callers that
|
|
1060
|
+
* forward the text should re-check for emptiness after cleaning.
|
|
1061
|
+
*/
|
|
1062
|
+
export function stripAnsiSequences(value) {
|
|
1063
|
+
return value.replace(ANSI_ESCAPE_SEQUENCE_REGEX, '');
|
|
1064
|
+
}
|
|
905
1065
|
const ANSI_TERMINAL_STYLES = {
|
|
906
1066
|
reset: '\x1b[0m',
|
|
907
1067
|
bright: '\x1b[1m',
|