@cloudcli-ai/cloudcli 1.37.1 → 1.37.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/assets/abnfDiagram-N423BO3Z-CJntskwA.js +1 -0
- package/dist/assets/arc-CIfH4uMN.js +1 -0
- package/dist/assets/architectureDiagram-T3A2C74G-_IpEkukg.js +36 -0
- package/dist/assets/blockDiagram-VBNYF7ZC-BttxrpgZ.js +132 -0
- package/dist/assets/c4Diagram-5PPSVZJV-g51dSRr1.js +10 -0
- package/dist/assets/channel-C5xU9jMw.js +1 -0
- package/dist/assets/chunk-2GRJ4B5K-D_-LbQUa.js +1 -0
- package/dist/assets/chunk-2Q5K7J3B-BtlHzwaO.js +1 -0
- package/dist/assets/chunk-5RXB4S5H-UzkpvpAT.js +231 -0
- package/dist/assets/chunk-5VM5RSS4-BKwx5Gag.js +15 -0
- package/dist/assets/chunk-6Q2QTUOP-D_KEBH5f.js +88 -0
- package/dist/assets/chunk-GF5L2VYU-rK2DGyto.js +206 -0
- package/dist/assets/chunk-JWPE2WC7-CeBl_N9i.js +1 -0
- package/dist/assets/chunk-KBJHAD2P-BiQLWVIr.js +1 -0
- package/dist/assets/chunk-RYQCIY6F-C1knVGV5.js +1 -0
- package/dist/assets/chunk-XXDRQBXY-CtKbJkvF.js +1 -0
- package/dist/assets/classDiagram-JCYQIIEL-CRrL3vp7.js +1 -0
- package/dist/assets/classDiagram-v2-OCEON4UE-CRrL3vp7.js +1 -0
- package/dist/assets/cose-bilkent-JH36ORCC-DjX8y40Z.js +1 -0
- package/dist/assets/cynefin-VYW2F7L2-DM6YeRba.js +178 -0
- package/dist/assets/cynefinDiagram-MW4NZA55-wZJ-vy5B.js +62 -0
- package/dist/assets/cytoscape.esm-C10L9Icf.js +331 -0
- package/dist/assets/dagre-VZM6K2ZE-BzP0ejkH.js +4 -0
- package/dist/assets/defaultLocale-DX6XiGOO.js +1 -0
- package/dist/assets/diagram-7IWD3JNH-BTL_j23J.js +30 -0
- package/dist/assets/diagram-B4RE2ZJO-Cf1Norgu.js +3 -0
- package/dist/assets/diagram-LBJQPF4R-DPiUEKr-.js +24 -0
- package/dist/assets/diagram-Q27KOJAE-CTcTpiz5.js +24 -0
- package/dist/assets/diagram-UB23O5K3-DPMSdYm0.js +41 -0
- package/dist/assets/ebnfDiagram-BXEA7PRR-7yvD6uiF.js +1 -0
- package/dist/assets/erDiagram-JOGREHBK-CfgeDoV6.js +85 -0
- package/dist/assets/flowDiagram-UKHOOZJN-B27ng5zu.js +156 -0
- package/dist/assets/ganttDiagram-PKOTCBZU-CcC6b_7t.js +292 -0
- package/dist/assets/gitGraphDiagram-DS77QQ5N-BX5kPGrO.js +106 -0
- package/dist/assets/graph-Cx-EqisW.js +1 -0
- package/dist/assets/index-BTDHQ6Id.css +32 -0
- package/dist/assets/index-sFfz8iBR.js +4931 -0
- package/dist/assets/infoDiagram-6WML65LV-CUP-SlN4.js +2 -0
- package/dist/assets/init-Gi6I4Gst.js +1 -0
- package/dist/assets/ishikawaDiagram-WSZJBQD7-BTTDhOpX.js +70 -0
- package/dist/assets/journeyDiagram-NVQOT4AX-ARXcc61m.js +139 -0
- package/dist/assets/kanban-definition-27J2QSJJ-BieMpqMA.js +89 -0
- package/dist/assets/layout-D6BstSzF.js +1 -0
- package/dist/assets/linear-61vxjRnP.js +1 -0
- package/dist/assets/map-CV9uif0g.js +1 -0
- package/dist/assets/mermaid.core-nhnwkfQx.js +312 -0
- package/dist/assets/mindmap-definition-FAOFIHXS-Cm6x5vT1.js +96 -0
- package/dist/assets/ordinal-Cboi1Yqb.js +1 -0
- package/dist/assets/pegDiagram-VL7TDLO6-CcYBq_9H.js +1 -0
- package/dist/assets/pieDiagram-7S7Q4E2Y-CfvIjfzo.js +39 -0
- package/dist/assets/quadrantDiagram-CIZ2JOQS-SgYoWPNb.js +7 -0
- package/dist/assets/railroadDiagram-AXF67PYL-D2Mu5E_f.js +1 -0
- package/dist/assets/requirementDiagram-LRYGKXZP-Azwe_k4Q.js +84 -0
- package/dist/assets/sankeyDiagram-W5VNT64P-DN1Ju3PA.js +40 -0
- package/dist/assets/sequenceDiagram-SI44F4Z6-Bj4qC34S.js +162 -0
- package/dist/assets/server.browser-CtX3Hdl5.js +27 -0
- package/dist/assets/sizeCapture-X5ZJPWSS-Co4PdklG.js +1 -0
- package/dist/assets/stateDiagram-OKZ733FA-nOvL9dRC.js +1 -0
- package/dist/assets/stateDiagram-v2-UEYNNEHI-DNv8ppw7.js +1 -0
- package/dist/assets/swimlanes-SLNWSIFB-CCK3wMid.js +2 -0
- package/dist/assets/swimlanesDiagram-ULZ7WXOC-DnfbNQEp.js +8 -0
- package/dist/assets/timeline-definition-Z64GVDOM-BC29RNFm.js +120 -0
- package/dist/assets/{vendor-codemirror-OwyKSvPE.js → vendor-codemirror-CYrSt7-U.js} +18 -18
- package/dist/assets/vennDiagram-T6HMQDX7-pZlZCtEo.js +34 -0
- package/dist/assets/wardleyDiagram-T6FBY63Y-De8k2-zM.js +78 -0
- package/dist/assets/xychartDiagram-ELKLHX3M-Bk8ykH0w.js +7 -0
- package/dist/index.html +3 -3
- package/dist-server/server/index.js +8 -5
- package/dist-server/server/index.js.map +1 -1
- package/dist-server/server/modules/agent/agent.module.js +2 -1
- package/dist-server/server/modules/agent/agent.module.js.map +1 -1
- package/dist-server/server/modules/agent/agent.routes.js +2 -2
- package/dist-server/server/modules/agent/agent.routes.js.map +1 -1
- package/dist-server/server/modules/agent/tests/agent.routes.test.js +30 -1
- package/dist-server/server/modules/agent/tests/agent.routes.test.js.map +1 -1
- package/dist-server/server/modules/auth/auth.middleware.js +1 -1
- package/dist-server/server/modules/auth/auth.middleware.js.map +1 -1
- package/dist-server/server/modules/browser-use/browser-use-runtime.js +10 -0
- package/dist-server/server/modules/browser-use/browser-use-runtime.js.map +1 -0
- 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/browser-use/browser-use.service.js +3 -6
- package/dist-server/server/modules/browser-use/browser-use.service.js.map +1 -1
- package/dist-server/server/modules/browser-use/index.js +2 -0
- package/dist-server/server/modules/browser-use/index.js.map +1 -1
- package/dist-server/server/modules/cli/cli.js +9 -3
- package/dist-server/server/modules/cli/cli.js.map +1 -1
- package/dist-server/server/modules/cli/cli.module.js +0 -1
- package/dist-server/server/modules/cli/cli.module.js.map +1 -1
- package/dist-server/server/modules/cli/cli.service.js +0 -18
- package/dist-server/server/modules/cli/cli.service.js.map +1 -1
- package/dist-server/server/modules/cli/tests/cli-environment-bootstrap.test.js +154 -0
- package/dist-server/server/modules/cli/tests/cli-environment-bootstrap.test.js.map +1 -0
- package/dist-server/server/modules/cli/tests/cli.service.test.js +0 -3
- package/dist-server/server/modules/cli/tests/cli.service.test.js.map +1 -1
- package/dist-server/server/modules/database/connection.js +1 -1
- package/dist-server/server/modules/database/connection.js.map +1 -1
- package/dist-server/server/modules/database/index.js +5 -0
- package/dist-server/server/modules/database/index.js.map +1 -1
- package/dist-server/server/modules/database/migrations.js +32 -1
- package/dist-server/server/modules/database/migrations.js.map +1 -1
- package/dist-server/server/modules/database/repositories/scheduled-messages.db.js +71 -0
- package/dist-server/server/modules/database/repositories/scheduled-messages.db.js.map +1 -0
- package/dist-server/server/modules/database/repositories/session-drafts.db.js +105 -0
- package/dist-server/server/modules/database/repositories/session-drafts.db.js.map +1 -0
- package/dist-server/server/modules/database/repositories/sessions.db.js +137 -5
- package/dist-server/server/modules/database/repositories/sessions.db.js.map +1 -1
- package/dist-server/server/modules/database/repositories/user-preferences.db.js +65 -0
- package/dist-server/server/modules/database/repositories/user-preferences.db.js.map +1 -0
- package/dist-server/server/modules/database/schema.js +92 -0
- package/dist-server/server/modules/database/schema.js.map +1 -1
- package/dist-server/server/modules/database/tests/sessions.db.integration.test.js +39 -0
- package/dist-server/server/modules/database/tests/sessions.db.integration.test.js.map +1 -1
- package/dist-server/server/modules/database/tests/user-preferences.db.integration.test.js +105 -0
- package/dist-server/server/modules/database/tests/user-preferences.db.integration.test.js.map +1 -0
- package/dist-server/server/modules/file-tree/file-tree.service.js +18 -10
- 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 +62 -11
- package/dist-server/server/modules/file-tree/tests/file-tree.service.test.js.map +1 -1
- package/dist-server/server/modules/git/git-branch.service.js +15 -0
- package/dist-server/server/modules/git/git-branch.service.js.map +1 -0
- package/dist-server/server/modules/git/git.routes.js +15 -7
- package/dist-server/server/modules/git/git.routes.js.map +1 -1
- package/dist-server/server/modules/git/tests/git-branch.service.test.js +52 -0
- package/dist-server/server/modules/git/tests/git-branch.service.test.js.map +1 -0
- package/dist-server/server/modules/git/tests/git-init.routes.test.js +84 -0
- package/dist-server/server/modules/git/tests/git-init.routes.test.js.map +1 -1
- package/dist-server/server/modules/notifications/index.js +3 -1
- package/dist-server/server/modules/notifications/index.js.map +1 -1
- package/dist-server/server/modules/notifications/services/notification-orchestrator.service.js +22 -1
- package/dist-server/server/modules/notifications/services/notification-orchestrator.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/providers/index.js +3 -0
- package/dist-server/server/modules/providers/index.js.map +1 -1
- package/dist-server/server/modules/providers/list/claude/claude-auth.provider.js +20 -1
- package/dist-server/server/modules/providers/list/claude/claude-auth.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/claude/claude-fork.provider.js +46 -0
- package/dist-server/server/modules/providers/list/claude/claude-fork.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/claude/claude-models.provider.js +35 -6
- package/dist-server/server/modules/providers/list/claude/claude-models.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/claude/claude-runtime.provider.js +422 -86
- package/dist-server/server/modules/providers/list/claude/claude-runtime.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/claude/claude-sessions.provider.js +388 -50
- package/dist-server/server/modules/providers/list/claude/claude-sessions.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/claude/claude-skills.provider.js +4 -1
- package/dist-server/server/modules/providers/list/claude/claude-skills.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/claude/claude.provider.js +2 -0
- package/dist-server/server/modules/providers/list/claude/claude.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/codex/codex-app-server.client.js +204 -0
- package/dist-server/server/modules/providers/list/codex/codex-app-server.client.js.map +1 -0
- package/dist-server/server/modules/providers/list/codex/codex-fork.provider.js +32 -0
- package/dist-server/server/modules/providers/list/codex/codex-fork.provider.js.map +1 -0
- package/dist-server/server/modules/providers/list/codex/codex-models.provider.js +16 -0
- package/dist-server/server/modules/providers/list/codex/codex-models.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/codex/codex-runtime.provider.js +60 -49
- package/dist-server/server/modules/providers/list/codex/codex-runtime.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/codex/codex-session-synchronizer.provider.js +8 -0
- package/dist-server/server/modules/providers/list/codex/codex-session-synchronizer.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/codex/codex-sessions.provider.js +1565 -432
- package/dist-server/server/modules/providers/list/codex/codex-sessions.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/codex/codex.provider.js +2 -0
- package/dist-server/server/modules/providers/list/codex/codex.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/cursor/cursor-runtime.provider.js +8 -6
- package/dist-server/server/modules/providers/list/cursor/cursor-runtime.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/cursor/cursor-session-synchronizer.provider.js.map +1 -1
- package/dist-server/server/modules/providers/list/opencode/opencode-models.provider.js +258 -6
- package/dist-server/server/modules/providers/list/opencode/opencode-models.provider.js.map +1 -1
- package/dist-server/server/modules/providers/provider.routes.js +29 -0
- package/dist-server/server/modules/providers/provider.routes.js.map +1 -1
- package/dist-server/server/modules/providers/services/provider-capabilities.service.js +14 -0
- package/dist-server/server/modules/providers/services/provider-capabilities.service.js.map +1 -1
- package/dist-server/server/modules/providers/services/provider-token-usage.service.js +139 -35
- package/dist-server/server/modules/providers/services/provider-token-usage.service.js.map +1 -1
- package/dist-server/server/modules/providers/services/session-conversations-search.service.js +63 -7
- 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 +87 -0
- package/dist-server/server/modules/providers/services/session-history-cache.service.js.map +1 -0
- package/dist-server/server/modules/providers/services/session-synchronizer.service.js +105 -32
- package/dist-server/server/modules/providers/services/session-synchronizer.service.js.map +1 -1
- package/dist-server/server/modules/providers/services/sessions-watcher.service.js +3 -60
- package/dist-server/server/modules/providers/services/sessions-watcher.service.js.map +1 -1
- package/dist-server/server/modules/providers/services/sessions.service.js +179 -9
- package/dist-server/server/modules/providers/services/sessions.service.js.map +1 -1
- package/dist-server/server/modules/providers/tests/claude-auth.test.js +52 -0
- package/dist-server/server/modules/providers/tests/claude-auth.test.js.map +1 -1
- package/dist-server/server/modules/providers/tests/claude-models.test.js +39 -0
- package/dist-server/server/modules/providers/tests/claude-models.test.js.map +1 -0
- package/dist-server/server/modules/providers/tests/claude-sessions.test.js +465 -39
- package/dist-server/server/modules/providers/tests/claude-sessions.test.js.map +1 -1
- package/dist-server/server/modules/providers/tests/claude-subagent-echo.test.js +16 -0
- package/dist-server/server/modules/providers/tests/claude-subagent-echo.test.js.map +1 -0
- package/dist-server/server/modules/providers/tests/claude-token-budget.test.js +85 -0
- package/dist-server/server/modules/providers/tests/claude-token-budget.test.js.map +1 -0
- package/dist-server/server/modules/providers/tests/codex-message-editing.test.js +338 -0
- package/dist-server/server/modules/providers/tests/codex-message-editing.test.js.map +1 -0
- package/dist-server/server/modules/providers/tests/codex-runtime.test.js +51 -0
- package/dist-server/server/modules/providers/tests/codex-runtime.test.js.map +1 -0
- package/dist-server/server/modules/providers/tests/codex-session-fork.test.js +54 -0
- package/dist-server/server/modules/providers/tests/codex-session-fork.test.js.map +1 -0
- package/dist-server/server/modules/providers/tests/codex-sessions.test.js +304 -15
- package/dist-server/server/modules/providers/tests/codex-sessions.test.js.map +1 -1
- package/dist-server/server/modules/providers/tests/opencode-models.test.js +117 -4
- package/dist-server/server/modules/providers/tests/opencode-models.test.js.map +1 -1
- package/dist-server/server/modules/providers/tests/provider-token-usage.service.test.js +164 -1
- package/dist-server/server/modules/providers/tests/provider-token-usage.service.test.js.map +1 -1
- package/dist-server/server/modules/providers/tests/provider.routes.test.js +35 -0
- 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 +120 -0
- package/dist-server/server/modules/providers/tests/session-history-cache.service.test.js.map +1 -0
- package/dist-server/server/modules/providers/tests/session-orphan-prune.test.js +90 -0
- package/dist-server/server/modules/providers/tests/session-orphan-prune.test.js.map +1 -0
- package/dist-server/server/modules/providers/tests/session-synchronizer-coalescing.test.js +122 -0
- package/dist-server/server/modules/providers/tests/session-synchronizer-coalescing.test.js.map +1 -0
- package/dist-server/server/modules/providers/tests/sessions.service.test.js +45 -1
- package/dist-server/server/modules/providers/tests/sessions.service.test.js.map +1 -1
- package/dist-server/server/modules/providers/tests/skills.test.js +15 -3
- package/dist-server/server/modules/providers/tests/skills.test.js.map +1 -1
- package/dist-server/server/modules/scheduled-messages/index.js +5 -0
- package/dist-server/server/modules/scheduled-messages/index.js.map +1 -0
- package/dist-server/server/modules/scheduled-messages/scheduled-messages.routes.js +47 -0
- package/dist-server/server/modules/scheduled-messages/scheduled-messages.routes.js.map +1 -0
- package/dist-server/server/modules/scheduled-messages/services/scheduled-message-dispatcher.service.js +163 -0
- package/dist-server/server/modules/scheduled-messages/services/scheduled-message-dispatcher.service.js.map +1 -0
- package/dist-server/server/modules/scheduled-messages/services/scheduled-messages.service.js +95 -0
- package/dist-server/server/modules/scheduled-messages/services/scheduled-messages.service.js.map +1 -0
- package/dist-server/server/modules/scheduled-messages/tests/scheduled-messages.test.js +255 -0
- package/dist-server/server/modules/scheduled-messages/tests/scheduled-messages.test.js.map +1 -0
- package/dist-server/server/modules/taskmaster/taskmaster.routes.js +23 -23
- package/dist-server/server/modules/taskmaster/taskmaster.routes.js.map +1 -1
- package/dist-server/server/modules/taskmaster/tests/taskmaster.routes.test.js +48 -0
- package/dist-server/server/modules/taskmaster/tests/taskmaster.routes.test.js.map +1 -1
- package/dist-server/server/modules/user/tests/user.service.test.js +70 -0
- package/dist-server/server/modules/user/tests/user.service.test.js.map +1 -1
- package/dist-server/server/modules/user/user.module.js +10 -1
- package/dist-server/server/modules/user/user.module.js.map +1 -1
- package/dist-server/server/modules/user/user.routes.js +44 -0
- package/dist-server/server/modules/user/user.routes.js.map +1 -1
- package/dist-server/server/modules/user/user.service.js +75 -0
- package/dist-server/server/modules/user/user.service.js.map +1 -1
- package/dist-server/server/modules/voice/tests/voice.service.test.js +3 -2
- package/dist-server/server/modules/voice/tests/voice.service.test.js.map +1 -1
- package/dist-server/server/modules/websocket/index.js +7 -0
- package/dist-server/server/modules/websocket/index.js.map +1 -1
- package/dist-server/server/modules/websocket/services/chat-run-registry.service.js +10 -44
- package/dist-server/server/modules/websocket/services/chat-run-registry.service.js.map +1 -1
- package/dist-server/server/modules/websocket/services/chat-session-writer.service.js +35 -5
- package/dist-server/server/modules/websocket/services/chat-session-writer.service.js.map +1 -1
- package/dist-server/server/modules/websocket/services/chat-websocket.service.js +166 -9
- package/dist-server/server/modules/websocket/services/chat-websocket.service.js.map +1 -1
- package/dist-server/server/modules/websocket/services/session-upsert-broadcast.service.js +94 -0
- package/dist-server/server/modules/websocket/services/session-upsert-broadcast.service.js.map +1 -0
- package/dist-server/server/modules/websocket/services/shell-websocket.service.js +9 -3
- package/dist-server/server/modules/websocket/services/shell-websocket.service.js.map +1 -1
- package/dist-server/server/modules/websocket/tests/chat-edit-send.test.js +263 -0
- package/dist-server/server/modules/websocket/tests/chat-edit-send.test.js.map +1 -0
- package/dist-server/server/modules/websocket/tests/chat-run-registry.test.js +29 -3
- package/dist-server/server/modules/websocket/tests/chat-run-registry.test.js.map +1 -1
- package/dist-server/server/modules/websocket/tests/session-upsert-broadcast.test.js +106 -0
- package/dist-server/server/modules/websocket/tests/session-upsert-broadcast.test.js.map +1 -0
- package/dist-server/server/modules/websocket/tests/shell-websocket.service.test.js +80 -0
- package/dist-server/server/modules/websocket/tests/shell-websocket.service.test.js.map +1 -1
- package/dist-server/server/shared/claude-cli-path.js +22 -5
- package/dist-server/server/shared/claude-cli-path.js.map +1 -1
- package/dist-server/server/shared/index.js +3 -0
- package/dist-server/server/shared/index.js.map +1 -0
- package/dist-server/server/shared/message-unification.js +376 -0
- package/dist-server/server/shared/message-unification.js.map +1 -0
- package/dist-server/server/shared/tests/claude-cli-path.test.js +45 -1
- package/dist-server/server/shared/tests/claude-cli-path.test.js.map +1 -1
- package/dist-server/server/shared/tests/message-unification.test.js +174 -0
- package/dist-server/server/shared/tests/message-unification.test.js.map +1 -0
- package/dist-server/server/shared/utils.js +42 -0
- package/dist-server/server/shared/utils.js.map +1 -1
- package/package.json +23 -16
- package/scripts/promote-dist-server.mjs +50 -0
- package/server/index.ts +12 -6
- package/server/modules/agent/agent.module.ts +2 -1
- package/server/modules/agent/agent.routes.ts +2 -2
- package/server/modules/agent/tests/agent.routes.test.ts +33 -1
- package/server/modules/auth/auth.middleware.ts +2 -2
- package/server/modules/browser-use/browser-use-runtime.ts +10 -0
- package/server/modules/browser-use/browser-use.routes.ts +96 -33
- package/server/modules/browser-use/browser-use.service.ts +5 -8
- package/server/modules/browser-use/index.ts +3 -0
- package/server/modules/cli/cli.module.ts +0 -1
- package/server/modules/cli/cli.service.ts +0 -20
- package/server/modules/cli/cli.ts +9 -3
- package/server/modules/cli/tests/cli-environment-bootstrap.test.ts +186 -0
- package/server/modules/cli/tests/cli.service.test.ts +0 -3
- package/server/modules/database/connection.ts +2 -1
- package/server/modules/database/index.ts +10 -0
- package/server/modules/database/migrations.ts +37 -0
- package/server/modules/database/repositories/scheduled-messages.db.ts +127 -0
- package/server/modules/database/repositories/session-drafts.db.ts +171 -0
- package/server/modules/database/repositories/sessions.db.ts +196 -3
- package/server/modules/database/repositories/user-preferences.db.ts +80 -0
- package/server/modules/database/schema.ts +96 -0
- package/server/modules/database/tests/sessions.db.integration.test.ts +52 -0
- package/server/modules/database/tests/user-preferences.db.integration.test.ts +135 -0
- package/server/modules/file-tree/file-tree.service.ts +21 -10
- package/server/modules/file-tree/tests/file-tree.service.test.ts +65 -11
- package/server/modules/git/git-branch.service.ts +43 -0
- package/server/modules/git/git.routes.ts +16 -9
- package/server/modules/git/tests/git-branch.service.test.ts +60 -0
- package/server/modules/git/tests/git-init.routes.test.ts +83 -0
- package/server/modules/notifications/index.ts +2 -0
- package/server/modules/notifications/services/notification-orchestrator.service.js +24 -1
- package/server/modules/projects/services/project-delete.service.ts +13 -1
- package/server/modules/projects/services/project-star.service.ts +2 -2
- package/server/modules/providers/index.ts +4 -0
- package/server/modules/providers/list/claude/claude-auth.provider.ts +21 -1
- package/server/modules/providers/list/claude/claude-fork.provider.ts +57 -0
- package/server/modules/providers/list/claude/claude-models.provider.ts +38 -6
- package/server/modules/providers/list/claude/claude-runtime.provider.js +463 -93
- package/server/modules/providers/list/claude/claude-sessions.provider.ts +478 -54
- package/server/modules/providers/list/claude/claude-skills.provider.ts +4 -1
- package/server/modules/providers/list/claude/claude.provider.ts +3 -0
- package/server/modules/providers/list/codex/codex-app-server.client.ts +243 -0
- package/server/modules/providers/list/codex/codex-fork.provider.ts +40 -0
- package/server/modules/providers/list/codex/codex-models.provider.ts +16 -0
- package/server/modules/providers/list/codex/{codex-runtime.provider.js → codex-runtime.provider.ts} +93 -66
- package/server/modules/providers/list/codex/codex-session-synchronizer.provider.ts +9 -0
- package/server/modules/providers/list/codex/codex-sessions.provider.ts +1806 -477
- package/server/modules/providers/list/codex/codex.provider.ts +3 -0
- package/server/modules/providers/list/cursor/cursor-runtime.provider.js +8 -6
- package/server/modules/providers/list/cursor/cursor-session-synchronizer.provider.ts +0 -1
- package/server/modules/providers/list/opencode/opencode-models.provider.ts +279 -6
- package/server/modules/providers/provider.routes.ts +36 -0
- package/server/modules/providers/services/provider-capabilities.service.ts +23 -0
- package/server/modules/providers/services/provider-token-usage.service.ts +166 -39
- package/server/modules/providers/services/session-conversations-search.service.ts +94 -7
- package/server/modules/providers/services/session-history-cache.service.ts +139 -0
- package/server/modules/providers/services/session-synchronizer.service.ts +125 -38
- package/server/modules/providers/services/sessions-watcher.service.ts +3 -64
- package/server/modules/providers/services/sessions.service.ts +201 -9
- package/server/modules/providers/tests/claude-auth.test.ts +58 -0
- package/server/modules/providers/tests/claude-models.test.ts +87 -0
- package/server/modules/providers/tests/claude-sessions.test.ts +552 -40
- package/server/modules/providers/tests/claude-subagent-echo.test.ts +25 -0
- package/server/modules/providers/tests/claude-token-budget.test.ts +104 -0
- package/server/modules/providers/tests/codex-message-editing.test.ts +419 -0
- package/server/modules/providers/tests/codex-runtime.test.ts +58 -0
- package/server/modules/providers/tests/codex-session-fork.test.ts +59 -0
- package/server/modules/providers/tests/codex-sessions.test.ts +337 -15
- package/server/modules/providers/tests/opencode-models.test.ts +166 -8
- package/server/modules/providers/tests/provider-token-usage.service.test.ts +188 -1
- package/server/modules/providers/tests/provider.routes.test.ts +61 -0
- package/server/modules/providers/tests/session-fork.test.ts +173 -0
- package/server/modules/providers/tests/session-history-cache.service.test.ts +143 -0
- package/server/modules/providers/tests/session-orphan-prune.test.ts +107 -0
- package/server/modules/providers/tests/session-synchronizer-coalescing.test.ts +136 -0
- package/server/modules/providers/tests/sessions.service.test.ts +67 -1
- package/server/modules/providers/tests/skills.test.ts +21 -5
- package/server/modules/scheduled-messages/index.ts +8 -0
- package/server/modules/scheduled-messages/scheduled-messages.routes.ts +68 -0
- package/server/modules/scheduled-messages/services/scheduled-message-dispatcher.service.ts +203 -0
- package/server/modules/scheduled-messages/services/scheduled-messages.service.ts +121 -0
- package/server/modules/scheduled-messages/tests/scheduled-messages.test.ts +310 -0
- package/server/modules/taskmaster/taskmaster.routes.ts +23 -33
- package/server/modules/taskmaster/tests/taskmaster.routes.test.ts +57 -0
- package/server/modules/user/tests/user.service.test.ts +90 -0
- package/server/modules/user/user.module.ts +10 -1
- package/server/modules/user/user.routes.ts +44 -0
- package/server/modules/user/user.service.ts +105 -0
- package/server/modules/voice/tests/voice.service.test.ts +3 -2
- package/server/modules/websocket/index.ts +8 -0
- package/server/modules/websocket/services/chat-run-registry.service.ts +17 -50
- package/server/modules/websocket/services/chat-session-writer.service.ts +36 -6
- package/server/modules/websocket/services/chat-websocket.service.ts +244 -15
- package/server/modules/websocket/services/session-upsert-broadcast.service.ts +108 -0
- package/server/modules/websocket/services/shell-websocket.service.ts +10 -3
- package/server/modules/websocket/tests/chat-edit-send.test.ts +309 -0
- package/server/modules/websocket/tests/chat-run-registry.test.ts +34 -3
- package/server/modules/websocket/tests/session-upsert-broadcast.test.ts +138 -0
- package/server/modules/websocket/tests/shell-websocket.service.test.ts +108 -0
- package/server/shared/claude-cli-path.ts +24 -6
- package/server/shared/index.ts +3 -0
- package/server/shared/interfaces.ts +62 -0
- package/server/shared/message-unification.ts +449 -0
- package/server/shared/tests/claude-cli-path.test.ts +58 -1
- package/server/shared/tests/message-unification.test.ts +218 -0
- package/server/shared/types.ts +123 -3
- package/server/shared/utils.ts +50 -0
- package/server/tsconfig.json +6 -2
- package/README.de.md +0 -257
- package/README.ja.md +0 -249
- package/README.ko.md +0 -249
- package/README.ru.md +0 -257
- package/README.tr.md +0 -258
- package/README.zh-CN.md +0 -249
- package/README.zh-TW.md +0 -249
- package/dist/assets/index-D3jfzuKN.css +0 -32
- package/dist/assets/index-DTMWjJj8.js +0 -1550
- package/dist-server/server/constants/config.js +0 -6
- package/dist-server/server/constants/config.js.map +0 -1
- package/server/constants/config.ts +0 -5
|
@@ -1,61 +1,573 @@
|
|
|
1
1
|
import assert from 'node:assert/strict';
|
|
2
|
+
import { mkdir, mkdtemp, readFile, rm, writeFile } from 'node:fs/promises';
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
import path from 'node:path';
|
|
2
5
|
import test from 'node:test';
|
|
3
6
|
|
|
7
|
+
import { closeConnection, initializeDatabase, sessionsDb } from '@/modules/database/index.js';
|
|
4
8
|
import { ClaudeSessionsProvider } from '@/modules/providers/list/claude/claude-sessions.provider.js';
|
|
5
9
|
|
|
6
|
-
|
|
10
|
+
async function withIsolatedDatabase(runTest: () => void | Promise<void>): Promise<void> {
|
|
11
|
+
const previousDatabasePath = process.env.DATABASE_PATH;
|
|
12
|
+
const tempDirectory = await mkdtemp(path.join(os.tmpdir(), 'claude-provider-db-'));
|
|
7
13
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
'# Building LLM-Powered Applications with Claude',
|
|
12
|
-
'',
|
|
13
|
-
'This skill helps you build LLM-powered applications with Claude.',
|
|
14
|
-
].join('\n');
|
|
14
|
+
closeConnection();
|
|
15
|
+
process.env.DATABASE_PATH = path.join(tempDirectory, 'auth.db');
|
|
16
|
+
await initializeDatabase();
|
|
15
17
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
+
try {
|
|
19
|
+
await runTest();
|
|
20
|
+
} finally {
|
|
21
|
+
closeConnection();
|
|
22
|
+
if (previousDatabasePath === undefined) {
|
|
23
|
+
delete process.env.DATABASE_PATH;
|
|
24
|
+
} else {
|
|
25
|
+
process.env.DATABASE_PATH = previousDatabasePath;
|
|
26
|
+
}
|
|
27
|
+
await rm(tempDirectory, { recursive: true, force: true });
|
|
28
|
+
}
|
|
29
|
+
}
|
|
18
30
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
31
|
+
const SESSION_ID = 'claude-session-1';
|
|
32
|
+
const AGENT_ID = 'a1b2c3d4e5f60718';
|
|
33
|
+
const AGENT_TOOL_USE_ID = 'toolu_agent_1';
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Writes the transcript pair current Claude versions produce for one async
|
|
37
|
+
* subagent: the parent session, and the agent's own transcript plus sidecar
|
|
38
|
+
* metadata under `<session>/subagents/`.
|
|
39
|
+
*/
|
|
40
|
+
async function writeClaudeSubagentSession(projectDirectory: string): Promise<string> {
|
|
41
|
+
const parentPath = path.join(projectDirectory, `${SESSION_ID}.jsonl`);
|
|
42
|
+
const subagentDirectory = path.join(projectDirectory, SESSION_ID, 'subagents');
|
|
43
|
+
await mkdir(subagentDirectory, { recursive: true });
|
|
44
|
+
|
|
45
|
+
const parentLines = [
|
|
22
46
|
{
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
47
|
+
type: 'assistant',
|
|
48
|
+
uuid: 'assistant-1',
|
|
49
|
+
sessionId: SESSION_ID,
|
|
50
|
+
timestamp: '2026-08-21T10:00:00.000Z',
|
|
51
|
+
message: {
|
|
52
|
+
role: 'assistant',
|
|
53
|
+
content: [{
|
|
54
|
+
type: 'tool_use',
|
|
55
|
+
id: AGENT_TOOL_USE_ID,
|
|
56
|
+
name: 'Agent',
|
|
57
|
+
input: { subagent_type: 'Explore', description: 'Survey the repo', prompt: 'Look around' },
|
|
58
|
+
}],
|
|
59
|
+
},
|
|
26
60
|
},
|
|
27
|
-
SESSION_ID,
|
|
28
|
-
);
|
|
29
|
-
assert.deepEqual(live, []);
|
|
30
|
-
|
|
31
|
-
const persisted = provider.normalizeMessage(
|
|
32
61
|
{
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
62
|
+
type: 'user',
|
|
63
|
+
uuid: 'launch-ack-1',
|
|
64
|
+
sessionId: SESSION_ID,
|
|
65
|
+
timestamp: '2026-08-21T10:00:01.000Z',
|
|
66
|
+
message: {
|
|
67
|
+
role: 'user',
|
|
68
|
+
content: [{
|
|
69
|
+
type: 'tool_result',
|
|
70
|
+
tool_use_id: AGENT_TOOL_USE_ID,
|
|
71
|
+
content: 'Async agent launched successfully. agentId: internal bookkeeping',
|
|
72
|
+
}],
|
|
73
|
+
},
|
|
74
|
+
toolUseResult: {
|
|
75
|
+
isAsync: true,
|
|
76
|
+
status: 'async_launched',
|
|
77
|
+
agentId: AGENT_ID,
|
|
78
|
+
description: 'Survey the repo',
|
|
79
|
+
resolvedModel: 'claude-opus-5',
|
|
80
|
+
},
|
|
37
81
|
},
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
82
|
+
{
|
|
83
|
+
type: 'user',
|
|
84
|
+
uuid: 'notification-1',
|
|
85
|
+
sessionId: SESSION_ID,
|
|
86
|
+
timestamp: '2026-08-21T10:05:00.000Z',
|
|
87
|
+
message: {
|
|
88
|
+
role: 'user',
|
|
89
|
+
content: [{
|
|
90
|
+
type: 'text',
|
|
91
|
+
text: [
|
|
92
|
+
'<task-notification>',
|
|
93
|
+
`<task-id>${AGENT_ID}</task-id>`,
|
|
94
|
+
`<tool-use-id>${AGENT_TOOL_USE_ID}</tool-use-id>`,
|
|
95
|
+
'<status>completed</status>',
|
|
96
|
+
'<summary>Agent "Survey the repo" finished</summary>',
|
|
97
|
+
'<result>The repo has two packages.</result>',
|
|
98
|
+
'</task-notification>',
|
|
99
|
+
].join('\n'),
|
|
100
|
+
}],
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
];
|
|
104
|
+
await writeFile(parentPath, `${parentLines.map((line) => JSON.stringify(line)).join('\n')}\n`, 'utf8');
|
|
45
105
|
|
|
46
|
-
const
|
|
106
|
+
const agentLines = [
|
|
107
|
+
{
|
|
108
|
+
type: 'assistant',
|
|
109
|
+
isSidechain: true,
|
|
110
|
+
agentId: AGENT_ID,
|
|
111
|
+
timestamp: '2026-08-21T10:00:30.000Z',
|
|
112
|
+
message: {
|
|
113
|
+
role: 'assistant',
|
|
114
|
+
model: 'claude-opus-5',
|
|
115
|
+
content: [
|
|
116
|
+
{ type: 'text', text: 'Starting the survey.' },
|
|
117
|
+
{ type: 'tool_use', id: 'toolu_child_1', name: 'Read', input: { file_path: '/repo/package.json' } },
|
|
118
|
+
],
|
|
119
|
+
},
|
|
120
|
+
},
|
|
47
121
|
{
|
|
48
|
-
|
|
49
|
-
|
|
122
|
+
type: 'user',
|
|
123
|
+
isSidechain: true,
|
|
124
|
+
agentId: AGENT_ID,
|
|
125
|
+
timestamp: '2026-08-21T10:00:31.000Z',
|
|
50
126
|
message: {
|
|
51
127
|
role: 'user',
|
|
52
|
-
content: [{ type: 'tool_result', tool_use_id: '
|
|
128
|
+
content: [{ type: 'tool_result', tool_use_id: 'toolu_child_1', content: '{"name":"repo"}' }],
|
|
53
129
|
},
|
|
54
130
|
},
|
|
55
|
-
|
|
131
|
+
];
|
|
132
|
+
await writeFile(
|
|
133
|
+
path.join(subagentDirectory, `agent-${AGENT_ID}.jsonl`),
|
|
134
|
+
`${agentLines.map((line) => JSON.stringify(line)).join('\n')}\n`,
|
|
135
|
+
'utf8',
|
|
136
|
+
);
|
|
137
|
+
await writeFile(
|
|
138
|
+
path.join(subagentDirectory, `agent-${AGENT_ID}.meta.json`),
|
|
139
|
+
JSON.stringify({ agentType: 'Explore', description: 'Survey the repo', toolUseId: AGENT_TOOL_USE_ID, spawnDepth: 1 }),
|
|
140
|
+
'utf8',
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
return parentPath;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
test('Claude history attaches a subagent transcript stored under the session directory', { concurrency: false }, async () => {
|
|
147
|
+
const tempRoot = await mkdtemp(path.join(os.tmpdir(), 'claude-subagent-'));
|
|
148
|
+
|
|
149
|
+
try {
|
|
150
|
+
const parentPath = await writeClaudeSubagentSession(tempRoot);
|
|
151
|
+
|
|
152
|
+
await withIsolatedDatabase(async () => {
|
|
153
|
+
const now = new Date().toISOString();
|
|
154
|
+
sessionsDb.createSession(SESSION_ID, 'claude', tempRoot, 'Subagent session', now, now, parentPath);
|
|
155
|
+
|
|
156
|
+
const history = await new ClaudeSessionsProvider().fetchHistory(SESSION_ID, {
|
|
157
|
+
providerSessionId: SESSION_ID,
|
|
158
|
+
});
|
|
159
|
+
const agentRow = history.messages.find(
|
|
160
|
+
(message) => message.kind === 'tool_use' && message.toolId === AGENT_TOOL_USE_ID,
|
|
161
|
+
);
|
|
162
|
+
|
|
163
|
+
assert.ok(agentRow, 'the Agent call must be in the transcript');
|
|
164
|
+
assert.equal(agentRow.subagent?.id, AGENT_ID);
|
|
165
|
+
assert.equal(agentRow.subagent?.type, 'Explore');
|
|
166
|
+
assert.equal(agentRow.subagent?.description, 'Survey the repo');
|
|
167
|
+
assert.equal(agentRow.subagent?.status, 'completed');
|
|
168
|
+
|
|
169
|
+
// The agent's own work — prose and tool calls — comes from its separate
|
|
170
|
+
// transcript, which is the file the previous lookup never found.
|
|
171
|
+
assert.equal(agentRow.subagentTools?.length, 2);
|
|
172
|
+
assert.equal(agentRow.subagentTools?.[0].kind, 'text');
|
|
173
|
+
assert.equal(agentRow.subagentTools?.[1].toolName, 'Read');
|
|
174
|
+
assert.equal(agentRow.subagentTools?.[1].toolResult?.content, '{"name":"repo"}');
|
|
175
|
+
});
|
|
176
|
+
} finally {
|
|
177
|
+
await rm(tempRoot, { recursive: true, force: true });
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
test('Claude history folds an agent task notification into the call that spawned it', { concurrency: false }, async () => {
|
|
182
|
+
const tempRoot = await mkdtemp(path.join(os.tmpdir(), 'claude-notification-'));
|
|
183
|
+
|
|
184
|
+
try {
|
|
185
|
+
const parentPath = await writeClaudeSubagentSession(tempRoot);
|
|
186
|
+
|
|
187
|
+
await withIsolatedDatabase(async () => {
|
|
188
|
+
const now = new Date().toISOString();
|
|
189
|
+
sessionsDb.createSession(SESSION_ID, 'claude', tempRoot, 'Subagent session', now, now, parentPath);
|
|
190
|
+
|
|
191
|
+
const history = await new ClaudeSessionsProvider().fetchHistory(SESSION_ID, {
|
|
192
|
+
providerSessionId: SESSION_ID,
|
|
193
|
+
});
|
|
194
|
+
const agentRow = history.messages.find(
|
|
195
|
+
(message) => message.kind === 'tool_use' && message.toolId === AGENT_TOOL_USE_ID,
|
|
196
|
+
);
|
|
197
|
+
|
|
198
|
+
// The launch acknowledgement is internal bookkeeping; the agent's answer
|
|
199
|
+
// is what belongs on its card.
|
|
200
|
+
assert.equal(agentRow?.toolResult?.content, 'The repo has two packages.');
|
|
201
|
+
|
|
202
|
+
const strayNotification = history.messages.find(
|
|
203
|
+
(message) => typeof message.content === 'string' && message.content.includes('<task-notification>'),
|
|
204
|
+
);
|
|
205
|
+
assert.equal(strayNotification, undefined, 'the folded notification must not also render on its own');
|
|
206
|
+
});
|
|
207
|
+
} finally {
|
|
208
|
+
await rm(tempRoot, { recursive: true, force: true });
|
|
209
|
+
}
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
/** Strips the `<task-notification>` turn so the agent has no reported outcome. */
|
|
213
|
+
async function dropTaskNotification(parentPath: string): Promise<void> {
|
|
214
|
+
const raw = await readFile(parentPath, 'utf8');
|
|
215
|
+
await writeFile(
|
|
216
|
+
parentPath,
|
|
217
|
+
`${raw.split('\n').filter((line) => line && !line.includes('task-notification')).join('\n')}\n`,
|
|
218
|
+
'utf8',
|
|
56
219
|
);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
test('Claude history reads a missing notification off the agent\'s own transcript', { concurrency: false }, async () => {
|
|
223
|
+
const tempRoot = await mkdtemp(path.join(os.tmpdir(), 'claude-finished-agent-'));
|
|
224
|
+
|
|
225
|
+
try {
|
|
226
|
+
const parentPath = await writeClaudeSubagentSession(tempRoot);
|
|
227
|
+
await dropTaskNotification(parentPath);
|
|
228
|
+
|
|
229
|
+
await withIsolatedDatabase(async () => {
|
|
230
|
+
const now = new Date().toISOString();
|
|
231
|
+
sessionsDb.createSession(SESSION_ID, 'claude', tempRoot, 'Subagent session', now, now, parentPath);
|
|
232
|
+
|
|
233
|
+
const history = await new ClaudeSessionsProvider().fetchHistory(SESSION_ID, {
|
|
234
|
+
providerSessionId: SESSION_ID,
|
|
235
|
+
});
|
|
236
|
+
const agentRow = history.messages.find(
|
|
237
|
+
(message) => message.kind === 'tool_use' && message.toolId === AGENT_TOOL_USE_ID,
|
|
238
|
+
);
|
|
239
|
+
|
|
240
|
+
// The notification can be compacted out of a long session. The agent's
|
|
241
|
+
// transcript ends on a resolved tool call, so it finished — reporting it
|
|
242
|
+
// as still running would leave a spinner on the card forever.
|
|
243
|
+
assert.equal(agentRow?.subagent?.status, 'completed');
|
|
244
|
+
assert.equal(agentRow?.toolResult?.content, '', 'the launch acknowledgement must never show as a result');
|
|
245
|
+
});
|
|
246
|
+
} finally {
|
|
247
|
+
await rm(tempRoot, { recursive: true, force: true });
|
|
248
|
+
}
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
test('Claude history keeps an agent running when its transcript stops mid tool call', { concurrency: false }, async () => {
|
|
252
|
+
const tempRoot = await mkdtemp(path.join(os.tmpdir(), 'claude-running-agent-'));
|
|
253
|
+
|
|
254
|
+
try {
|
|
255
|
+
const parentPath = await writeClaudeSubagentSession(tempRoot);
|
|
256
|
+
await dropTaskNotification(parentPath);
|
|
257
|
+
|
|
258
|
+
// Drop the child tool result: the agent is mid-call, which is the only
|
|
259
|
+
// in-file evidence that it is still working.
|
|
260
|
+
const agentPath = path.join(tempRoot, SESSION_ID, 'subagents', `agent-${AGENT_ID}.jsonl`);
|
|
261
|
+
const agentRaw = await readFile(agentPath, 'utf8');
|
|
262
|
+
await writeFile(
|
|
263
|
+
agentPath,
|
|
264
|
+
`${agentRaw.split('\n').filter((line) => line && !line.includes('tool_result')).join('\n')}\n`,
|
|
265
|
+
'utf8',
|
|
266
|
+
);
|
|
267
|
+
|
|
268
|
+
await withIsolatedDatabase(async () => {
|
|
269
|
+
const now = new Date().toISOString();
|
|
270
|
+
sessionsDb.createSession(SESSION_ID, 'claude', tempRoot, 'Subagent session', now, now, parentPath);
|
|
271
|
+
|
|
272
|
+
const history = await new ClaudeSessionsProvider().fetchHistory(SESSION_ID, {
|
|
273
|
+
providerSessionId: SESSION_ID,
|
|
274
|
+
});
|
|
275
|
+
const agentRow = history.messages.find(
|
|
276
|
+
(message) => message.kind === 'tool_use' && message.toolId === AGENT_TOOL_USE_ID,
|
|
277
|
+
);
|
|
278
|
+
|
|
279
|
+
assert.equal(agentRow?.subagent?.status, 'running');
|
|
280
|
+
});
|
|
281
|
+
} finally {
|
|
282
|
+
await rm(tempRoot, { recursive: true, force: true });
|
|
283
|
+
}
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
test('Claude history trims a subagent timeline down to a preview', { concurrency: false }, async () => {
|
|
287
|
+
const tempRoot = await mkdtemp(path.join(os.tmpdir(), 'claude-big-agent-'));
|
|
288
|
+
|
|
289
|
+
try {
|
|
290
|
+
const parentPath = await writeClaudeSubagentSession(tempRoot);
|
|
291
|
+
|
|
292
|
+
// One child command with a very large output, which is what makes an
|
|
293
|
+
// agent-heavy session's history payload balloon.
|
|
294
|
+
const hugeOutput = 'x'.repeat(50_000);
|
|
295
|
+
const agentPath = path.join(tempRoot, SESSION_ID, 'subagents', `agent-${AGENT_ID}.jsonl`);
|
|
296
|
+
const agentRaw = await readFile(agentPath, 'utf8');
|
|
297
|
+
const enlarged = agentRaw
|
|
298
|
+
.split('\n')
|
|
299
|
+
.filter(Boolean)
|
|
300
|
+
.map((line) => {
|
|
301
|
+
const entry = JSON.parse(line) as { message?: { content?: Array<{ type?: string; content?: string }> } };
|
|
302
|
+
for (const part of entry.message?.content ?? []) {
|
|
303
|
+
if (part.type === 'tool_result') {
|
|
304
|
+
part.content = hugeOutput;
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
return JSON.stringify(entry);
|
|
308
|
+
})
|
|
309
|
+
.join('\n');
|
|
310
|
+
await writeFile(agentPath, `${enlarged}\n`, 'utf8');
|
|
311
|
+
|
|
312
|
+
await withIsolatedDatabase(async () => {
|
|
313
|
+
const now = new Date().toISOString();
|
|
314
|
+
sessionsDb.createSession(SESSION_ID, 'claude', tempRoot, 'Subagent session', now, now, parentPath);
|
|
315
|
+
|
|
316
|
+
const history = await new ClaudeSessionsProvider().fetchHistory(SESSION_ID, {
|
|
317
|
+
providerSessionId: SESSION_ID,
|
|
318
|
+
});
|
|
319
|
+
const agentRow = history.messages.find(
|
|
320
|
+
(message) => message.kind === 'tool_use' && message.toolId === AGENT_TOOL_USE_ID,
|
|
321
|
+
);
|
|
322
|
+
const childResult = String(agentRow?.subagentTools?.[1].toolResult?.content ?? '');
|
|
323
|
+
|
|
324
|
+
assert.ok(childResult.length < 6000, `nested output must be trimmed, got ${childResult.length}`);
|
|
325
|
+
assert.match(childResult, /more characters$/, 'the trim must say how much was omitted');
|
|
326
|
+
});
|
|
327
|
+
} finally {
|
|
328
|
+
await rm(tempRoot, { recursive: true, force: true });
|
|
329
|
+
}
|
|
330
|
+
});
|
|
331
|
+
|
|
332
|
+
const EDIT_SESSION_ID = 'claude-edit-session';
|
|
333
|
+
|
|
334
|
+
/**
|
|
335
|
+
* Writes a transcript where one prompt was edited: the replacement shares a
|
|
336
|
+
* parent with the original, which is the shape Claude's resume-partway leaves
|
|
337
|
+
* behind. Nothing is deleted from the file.
|
|
338
|
+
*/
|
|
339
|
+
async function writeEditedTranscript(projectDirectory: string): Promise<string> {
|
|
340
|
+
const transcriptPath = path.join(projectDirectory, `${EDIT_SESSION_ID}.jsonl`);
|
|
341
|
+
const rows = [
|
|
342
|
+
{
|
|
343
|
+
type: 'user', uuid: 'u1', parentUuid: null, sessionId: EDIT_SESSION_ID,
|
|
344
|
+
timestamp: '2026-08-23T10:00:00.000Z',
|
|
345
|
+
message: { role: 'user', content: [{ type: 'text', text: 'first prompt' }] },
|
|
346
|
+
},
|
|
347
|
+
{
|
|
348
|
+
type: 'assistant', uuid: 'a1', parentUuid: 'u1', sessionId: EDIT_SESSION_ID,
|
|
349
|
+
timestamp: '2026-08-23T10:00:01.000Z',
|
|
350
|
+
message: { role: 'assistant', model: 'claude-opus-5', content: [{ type: 'text', text: 'first answer' }] },
|
|
351
|
+
},
|
|
352
|
+
{
|
|
353
|
+
type: 'user', uuid: 'u2', parentUuid: 'a1', sessionId: EDIT_SESSION_ID,
|
|
354
|
+
timestamp: '2026-08-23T10:00:02.000Z',
|
|
355
|
+
message: { role: 'user', content: [{ type: 'text', text: 'original second prompt' }] },
|
|
356
|
+
},
|
|
357
|
+
{
|
|
358
|
+
type: 'assistant', uuid: 'a2', parentUuid: 'u2', sessionId: EDIT_SESSION_ID,
|
|
359
|
+
timestamp: '2026-08-23T10:00:03.000Z',
|
|
360
|
+
message: { role: 'assistant', model: 'claude-opus-5', content: [{ type: 'text', text: 'answer to be replaced' }] },
|
|
361
|
+
},
|
|
362
|
+
// The edit: same parent as u2, written later.
|
|
363
|
+
{
|
|
364
|
+
type: 'user', uuid: 'u2b', parentUuid: 'a1', sessionId: EDIT_SESSION_ID,
|
|
365
|
+
timestamp: '2026-08-23T10:00:04.000Z',
|
|
366
|
+
message: { role: 'user', content: [{ type: 'text', text: 'edited second prompt' }] },
|
|
367
|
+
},
|
|
368
|
+
{
|
|
369
|
+
type: 'assistant', uuid: 'a2b', parentUuid: 'u2b', sessionId: EDIT_SESSION_ID,
|
|
370
|
+
timestamp: '2026-08-23T10:00:05.000Z',
|
|
371
|
+
message: { role: 'assistant', model: 'claude-opus-5', content: [{ type: 'text', text: 'answer to the edit' }] },
|
|
372
|
+
},
|
|
373
|
+
];
|
|
374
|
+
|
|
375
|
+
await writeFile(transcriptPath, `${rows.map((row) => JSON.stringify(row)).join('\n')}\n`, 'utf8');
|
|
376
|
+
return transcriptPath;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
test('an edited prompt replaces the one it superseded instead of stacking on it', { concurrency: false }, async () => {
|
|
380
|
+
const tempRoot = await mkdtemp(path.join(os.tmpdir(), 'claude-edit-history-'));
|
|
381
|
+
|
|
382
|
+
try {
|
|
383
|
+
const transcriptPath = await writeEditedTranscript(tempRoot);
|
|
384
|
+
|
|
385
|
+
await withIsolatedDatabase(async () => {
|
|
386
|
+
const now = new Date().toISOString();
|
|
387
|
+
sessionsDb.createSession(EDIT_SESSION_ID, 'claude', tempRoot, 'Edited session', now, now, transcriptPath);
|
|
388
|
+
|
|
389
|
+
const history = await new ClaudeSessionsProvider().fetchHistory(EDIT_SESSION_ID, {
|
|
390
|
+
providerSessionId: EDIT_SESSION_ID,
|
|
391
|
+
});
|
|
392
|
+
const texts = history.messages.map((message) => message.content);
|
|
393
|
+
|
|
394
|
+
assert.deepEqual(texts, [
|
|
395
|
+
'first prompt',
|
|
396
|
+
'first answer',
|
|
397
|
+
'edited second prompt',
|
|
398
|
+
'answer to the edit',
|
|
399
|
+
]);
|
|
400
|
+
});
|
|
401
|
+
} finally {
|
|
402
|
+
await rm(tempRoot, { recursive: true, force: true });
|
|
403
|
+
}
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
test('parallel tool calls are not mistaken for an edit', { concurrency: false }, async () => {
|
|
407
|
+
const tempRoot = await mkdtemp(path.join(os.tmpdir(), 'claude-parallel-tools-'));
|
|
408
|
+
const sessionId = 'claude-parallel-session';
|
|
409
|
+
|
|
410
|
+
try {
|
|
411
|
+
const transcriptPath = path.join(tempRoot, `${sessionId}.jsonl`);
|
|
412
|
+
// One assistant turn issuing two tools: each tool_result parents onto the
|
|
413
|
+
// same row, so this row has two children — a branch point that must not be
|
|
414
|
+
// pruned, or tool output disappears from every transcript in the app.
|
|
415
|
+
const rows = [
|
|
416
|
+
{
|
|
417
|
+
type: 'user', uuid: 'p1', parentUuid: null, sessionId,
|
|
418
|
+
timestamp: '2026-08-23T10:00:00.000Z',
|
|
419
|
+
message: { role: 'user', content: [{ type: 'text', text: 'do two things' }] },
|
|
420
|
+
},
|
|
421
|
+
{
|
|
422
|
+
type: 'assistant', uuid: 'pa1', parentUuid: 'p1', sessionId,
|
|
423
|
+
timestamp: '2026-08-23T10:00:01.000Z',
|
|
424
|
+
message: {
|
|
425
|
+
role: 'assistant', model: 'claude-opus-5',
|
|
426
|
+
content: [{ type: 'tool_use', id: 'tool-1', name: 'Read', input: { file_path: '/a' } }],
|
|
427
|
+
},
|
|
428
|
+
},
|
|
429
|
+
{
|
|
430
|
+
type: 'user', uuid: 'pr1', parentUuid: 'pa1', sessionId,
|
|
431
|
+
timestamp: '2026-08-23T10:00:02.000Z',
|
|
432
|
+
message: { role: 'user', content: [{ type: 'tool_result', tool_use_id: 'tool-1', content: 'contents of a' }] },
|
|
433
|
+
},
|
|
434
|
+
{
|
|
435
|
+
type: 'user', uuid: 'pr2', parentUuid: 'pa1', sessionId,
|
|
436
|
+
timestamp: '2026-08-23T10:00:03.000Z',
|
|
437
|
+
message: { role: 'user', content: [{ type: 'tool_result', tool_use_id: 'tool-2', content: 'contents of b' }] },
|
|
438
|
+
},
|
|
439
|
+
];
|
|
440
|
+
await writeFile(transcriptPath, `${rows.map((row) => JSON.stringify(row)).join('\n')}\n`, 'utf8');
|
|
441
|
+
|
|
442
|
+
await withIsolatedDatabase(async () => {
|
|
443
|
+
const now = new Date().toISOString();
|
|
444
|
+
sessionsDb.createSession(sessionId, 'claude', tempRoot, 'Parallel tools', now, now, transcriptPath);
|
|
445
|
+
|
|
446
|
+
const history = await new ClaudeSessionsProvider().fetchHistory(sessionId, {
|
|
447
|
+
providerSessionId: sessionId,
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
assert.equal(
|
|
451
|
+
history.messages.some((message) => message.content === 'do two things'),
|
|
452
|
+
true,
|
|
453
|
+
);
|
|
454
|
+
const toolRow = history.messages.find((message) => message.kind === 'tool_use');
|
|
455
|
+
assert.ok(toolRow, 'the tool call survives');
|
|
456
|
+
assert.equal(toolRow?.toolResult?.content, 'contents of a');
|
|
457
|
+
});
|
|
458
|
+
} finally {
|
|
459
|
+
await rm(tempRoot, { recursive: true, force: true });
|
|
460
|
+
}
|
|
461
|
+
});
|
|
462
|
+
|
|
463
|
+
test('resolving an edit anchor returns the assistant turn before it', { concurrency: false }, async () => {
|
|
464
|
+
const tempRoot = await mkdtemp(path.join(os.tmpdir(), 'claude-edit-anchor-'));
|
|
465
|
+
|
|
466
|
+
try {
|
|
467
|
+
const transcriptPath = await writeEditedTranscript(tempRoot);
|
|
468
|
+
|
|
469
|
+
await withIsolatedDatabase(async () => {
|
|
470
|
+
const now = new Date().toISOString();
|
|
471
|
+
sessionsDb.createSession(EDIT_SESSION_ID, 'claude', tempRoot, 'Edited session', now, now, transcriptPath);
|
|
472
|
+
const provider = new ClaudeSessionsProvider();
|
|
473
|
+
|
|
474
|
+
// Resuming is inclusive of the row it names, so replacing `u2b` must
|
|
475
|
+
// resume through `a1` — naming `u2b` itself would leave the prompt being
|
|
476
|
+
// replaced in context.
|
|
477
|
+
assert.deepEqual(
|
|
478
|
+
await provider.resolveEditAnchor(EDIT_SESSION_ID, 'u2b'),
|
|
479
|
+
{ found: true, resumeThroughId: 'a1' },
|
|
480
|
+
);
|
|
481
|
+
|
|
482
|
+
// Nothing precedes the first prompt, so the conversation starts over.
|
|
483
|
+
assert.deepEqual(
|
|
484
|
+
await provider.resolveEditAnchor(EDIT_SESSION_ID, 'u1'),
|
|
485
|
+
{ found: true, resumeThroughId: null },
|
|
486
|
+
);
|
|
487
|
+
|
|
488
|
+
assert.deepEqual(
|
|
489
|
+
await provider.resolveEditAnchor(EDIT_SESSION_ID, 'not-in-transcript'),
|
|
490
|
+
{ found: false, resumeThroughId: null },
|
|
491
|
+
);
|
|
492
|
+
});
|
|
493
|
+
} finally {
|
|
494
|
+
await rm(tempRoot, { recursive: true, force: true });
|
|
495
|
+
}
|
|
496
|
+
});
|
|
497
|
+
|
|
498
|
+
test('user turns carry the transcript uuid so they can be edited', { concurrency: false }, async () => {
|
|
499
|
+
const tempRoot = await mkdtemp(path.join(os.tmpdir(), 'claude-anchor-ids-'));
|
|
500
|
+
|
|
501
|
+
try {
|
|
502
|
+
const transcriptPath = await writeEditedTranscript(tempRoot);
|
|
503
|
+
|
|
504
|
+
await withIsolatedDatabase(async () => {
|
|
505
|
+
const now = new Date().toISOString();
|
|
506
|
+
sessionsDb.createSession(EDIT_SESSION_ID, 'claude', tempRoot, 'Edited session', now, now, transcriptPath);
|
|
507
|
+
|
|
508
|
+
const history = await new ClaudeSessionsProvider().fetchHistory(EDIT_SESSION_ID, {
|
|
509
|
+
providerSessionId: EDIT_SESSION_ID,
|
|
510
|
+
});
|
|
511
|
+
|
|
512
|
+
const userRows = history.messages.filter((message) => message.role === 'user');
|
|
513
|
+
assert.deepEqual(
|
|
514
|
+
userRows.map((message) => message.transcriptAnchorId),
|
|
515
|
+
['u1', 'u2b'],
|
|
516
|
+
);
|
|
517
|
+
// Assistant rows are never an anchor: the UI only offers editing on a
|
|
518
|
+
// turn the user typed.
|
|
519
|
+
assert.equal(
|
|
520
|
+
history.messages.some((message) => message.role !== 'user' && message.transcriptAnchorId),
|
|
521
|
+
false,
|
|
522
|
+
);
|
|
523
|
+
});
|
|
524
|
+
} finally {
|
|
525
|
+
await rm(tempRoot, { recursive: true, force: true });
|
|
526
|
+
}
|
|
527
|
+
});
|
|
528
|
+
|
|
529
|
+
test('resolving an edit anchor skips rows that are not conversation turns', { concurrency: false }, async () => {
|
|
530
|
+
const tempRoot = await mkdtemp(path.join(os.tmpdir(), 'claude-anchor-skip-'));
|
|
531
|
+
const sessionId = 'claude-anchor-skip-session';
|
|
532
|
+
|
|
533
|
+
try {
|
|
534
|
+
const transcriptPath = path.join(tempRoot, `${sessionId}.jsonl`);
|
|
535
|
+
// An attachment row sits between the assistant turn and the next prompt.
|
|
536
|
+
// Resuming names an assistant message, so the walk has to pass over it —
|
|
537
|
+
// naming the attachment would resume at something the SDK cannot address.
|
|
538
|
+
const rows = [
|
|
539
|
+
{
|
|
540
|
+
type: 'user', uuid: 'su1', parentUuid: null, sessionId,
|
|
541
|
+
timestamp: '2026-08-23T10:00:00.000Z',
|
|
542
|
+
message: { role: 'user', content: [{ type: 'text', text: 'hello' }] },
|
|
543
|
+
},
|
|
544
|
+
{
|
|
545
|
+
type: 'assistant', uuid: 'sa1', parentUuid: 'su1', sessionId,
|
|
546
|
+
timestamp: '2026-08-23T10:00:01.000Z',
|
|
547
|
+
message: { role: 'assistant', model: 'claude-opus-5', content: [{ type: 'text', text: 'hi' }] },
|
|
548
|
+
},
|
|
549
|
+
{
|
|
550
|
+
type: 'attachment', uuid: 'sat1', parentUuid: 'sa1', sessionId,
|
|
551
|
+
timestamp: '2026-08-23T10:00:02.000Z',
|
|
552
|
+
},
|
|
553
|
+
{
|
|
554
|
+
type: 'user', uuid: 'su2', parentUuid: 'sat1', sessionId,
|
|
555
|
+
timestamp: '2026-08-23T10:00:03.000Z',
|
|
556
|
+
message: { role: 'user', content: [{ type: 'text', text: 'second prompt' }] },
|
|
557
|
+
},
|
|
558
|
+
];
|
|
559
|
+
await writeFile(transcriptPath, `${rows.map((row) => JSON.stringify(row)).join('\n')}\n`, 'utf8');
|
|
560
|
+
|
|
561
|
+
await withIsolatedDatabase(async () => {
|
|
562
|
+
const now = new Date().toISOString();
|
|
563
|
+
sessionsDb.createSession(sessionId, 'claude', tempRoot, 'Anchor skip', now, now, transcriptPath);
|
|
57
564
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
565
|
+
assert.deepEqual(
|
|
566
|
+
await new ClaudeSessionsProvider().resolveEditAnchor(sessionId, 'su2'),
|
|
567
|
+
{ found: true, resumeThroughId: 'sa1' },
|
|
568
|
+
);
|
|
569
|
+
});
|
|
570
|
+
} finally {
|
|
571
|
+
await rm(tempRoot, { recursive: true, force: true });
|
|
572
|
+
}
|
|
61
573
|
});
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import test from 'node:test';
|
|
3
|
+
|
|
4
|
+
import { isSubagentPromptEcho } from '@/modules/providers/list/claude/claude-runtime.provider.js';
|
|
5
|
+
|
|
6
|
+
test('a subagent prompt echo is suppressed', () => {
|
|
7
|
+
// The Agent tool card already shows this prompt, and the transcript keeps the
|
|
8
|
+
// turn in the subagent sidechain, so streaming it stacked a duplicate user
|
|
9
|
+
// bubble that vanished on reload.
|
|
10
|
+
assert.equal(
|
|
11
|
+
isSubagentPromptEcho({ kind: 'text', role: 'user', parentToolUseId: 'toolu_1', content: 'Investigate' }),
|
|
12
|
+
true,
|
|
13
|
+
);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
test('the session prompt is kept', () => {
|
|
17
|
+
assert.equal(isSubagentPromptEcho({ kind: 'text', role: 'user', content: 'Hello' }), false);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
test('subagent tool traffic is kept', () => {
|
|
21
|
+
assert.equal(
|
|
22
|
+
isSubagentPromptEcho({ kind: 'tool_result', toolId: 'toolu_2', parentToolUseId: 'toolu_1' }),
|
|
23
|
+
false,
|
|
24
|
+
);
|
|
25
|
+
});
|