@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,160 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import { mkdtemp, rm, writeFile } from 'node:fs/promises';
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import test from 'node:test';
|
|
6
|
+
|
|
7
|
+
import { closeConnection, initializeDatabase, sessionsDb } from '@/modules/database/index.js';
|
|
8
|
+
import { ClaudeSessionsProvider } from '@/modules/providers/list/claude/claude-sessions.provider.js';
|
|
9
|
+
|
|
10
|
+
const SESSION_ID = 'claude-model-session';
|
|
11
|
+
|
|
12
|
+
async function withIsolatedDatabase(runTest: () => void | Promise<void>): Promise<void> {
|
|
13
|
+
const previousDatabasePath = process.env.DATABASE_PATH;
|
|
14
|
+
const tempDirectory = await mkdtemp(path.join(os.tmpdir(), 'claude-model-db-'));
|
|
15
|
+
|
|
16
|
+
closeConnection();
|
|
17
|
+
process.env.DATABASE_PATH = path.join(tempDirectory, 'auth.db');
|
|
18
|
+
await initializeDatabase();
|
|
19
|
+
|
|
20
|
+
try {
|
|
21
|
+
await runTest();
|
|
22
|
+
} finally {
|
|
23
|
+
closeConnection();
|
|
24
|
+
if (previousDatabasePath === undefined) {
|
|
25
|
+
delete process.env.DATABASE_PATH;
|
|
26
|
+
} else {
|
|
27
|
+
process.env.DATABASE_PATH = previousDatabasePath;
|
|
28
|
+
}
|
|
29
|
+
await rm(tempDirectory, { recursive: true, force: true });
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* One assistant transcript row in the shape Claude Code writes it: the model
|
|
35
|
+
* that answered sits on `message.model`, beside the content parts.
|
|
36
|
+
*/
|
|
37
|
+
const assistantRow = (uuid: string, model: string | undefined, content: unknown) => ({
|
|
38
|
+
parentUuid: 'user-1',
|
|
39
|
+
isSidechain: false,
|
|
40
|
+
type: 'assistant',
|
|
41
|
+
uuid,
|
|
42
|
+
timestamp: '2026-07-10T00:00:01.000Z',
|
|
43
|
+
cwd: '/workspace/demo',
|
|
44
|
+
sessionId: SESSION_ID,
|
|
45
|
+
message: { role: 'assistant', ...(model === undefined ? {} : { model }), content },
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
test('an assistant reply carries the model that answered it', () => {
|
|
49
|
+
const messages = new ClaudeSessionsProvider().normalizeMessage(
|
|
50
|
+
assistantRow('asst-1', 'claude-opus-5', [
|
|
51
|
+
{ type: 'thinking', thinking: 'weighing the options' },
|
|
52
|
+
{ type: 'text', text: 'Here is the plan.' },
|
|
53
|
+
{ type: 'tool_use', id: 'toolu_1', name: 'Read', input: { file_path: '/repo/a.ts' } },
|
|
54
|
+
]),
|
|
55
|
+
SESSION_ID,
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
const prose = messages.find((message) => message.kind === 'text');
|
|
59
|
+
assert.equal(prose?.model, 'claude-opus-5');
|
|
60
|
+
|
|
61
|
+
// Only prose has a footer to show it in; a tool call and a reasoning block
|
|
62
|
+
// are drawn as cards, so stamping them would carry a model nothing renders.
|
|
63
|
+
assert.equal(messages.find((message) => message.kind === 'tool_use')?.model, undefined);
|
|
64
|
+
assert.equal(messages.find((message) => message.kind === 'thinking')?.model, undefined);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
test('an assistant reply whose content is a bare string carries the model too', () => {
|
|
68
|
+
const [prose] = new ClaudeSessionsProvider().normalizeMessage(
|
|
69
|
+
assistantRow('asst-2', 'claude-haiku-4-5-20251001', 'Done.'),
|
|
70
|
+
SESSION_ID,
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
assert.equal(prose?.kind, 'text');
|
|
74
|
+
assert.equal(prose?.model, 'claude-haiku-4-5-20251001');
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
test('the reported model id is passed through verbatim, suffix and all', () => {
|
|
78
|
+
// Claude Code writes the base id on assistant rows today, even in 1M-context
|
|
79
|
+
// sessions. The normalizer must still never rewrite what the provider wrote:
|
|
80
|
+
// if a `[1m]` id ever appears, it has to reach the client as itself.
|
|
81
|
+
const [prose] = new ClaudeSessionsProvider().normalizeMessage(
|
|
82
|
+
assistantRow('asst-3', 'claude-opus-5[1m]', [{ type: 'text', text: 'Reading the whole repo.' }]),
|
|
83
|
+
SESSION_ID,
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
assert.equal(prose?.model, 'claude-opus-5[1m]');
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
test('a locally fabricated row reports no model at all', () => {
|
|
90
|
+
// Claude Code stamps rows it synthesized itself — the usage-limit notice, an
|
|
91
|
+
// API-error placeholder — with `<synthetic>`. No request ran, so there is no
|
|
92
|
+
// model to name, and a guessed one would be a lie about what was charged.
|
|
93
|
+
const [prose] = new ClaudeSessionsProvider().normalizeMessage(
|
|
94
|
+
assistantRow('asst-4', '<synthetic>', [{ type: 'text', text: 'Claude usage limit reached.' }]),
|
|
95
|
+
SESSION_ID,
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
assert.equal(prose?.kind, 'text');
|
|
99
|
+
assert.equal(prose?.model, undefined);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
test('a user turn carries no model, because no transcript records one', () => {
|
|
103
|
+
const [turn] = new ClaudeSessionsProvider().normalizeMessage(
|
|
104
|
+
{
|
|
105
|
+
type: 'user',
|
|
106
|
+
uuid: 'user-1',
|
|
107
|
+
timestamp: '2026-07-10T00:00:00.000Z',
|
|
108
|
+
sessionId: SESSION_ID,
|
|
109
|
+
message: { role: 'user', content: 'Plan the refactor.' },
|
|
110
|
+
},
|
|
111
|
+
SESSION_ID,
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
assert.equal(turn?.role, 'user');
|
|
115
|
+
assert.equal(turn?.model, undefined);
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
test('Claude history replays the model of every assistant turn from the transcript', { concurrency: false }, async () => {
|
|
119
|
+
const tempRoot = await mkdtemp(path.join(os.tmpdir(), 'claude-model-history-'));
|
|
120
|
+
|
|
121
|
+
try {
|
|
122
|
+
const transcriptPath = path.join(tempRoot, `${SESSION_ID}.jsonl`);
|
|
123
|
+
await writeFile(
|
|
124
|
+
transcriptPath,
|
|
125
|
+
[
|
|
126
|
+
JSON.stringify({
|
|
127
|
+
parentUuid: null,
|
|
128
|
+
isSidechain: false,
|
|
129
|
+
type: 'user',
|
|
130
|
+
uuid: 'user-1',
|
|
131
|
+
timestamp: '2026-07-10T00:00:00.000Z',
|
|
132
|
+
cwd: '/workspace/demo',
|
|
133
|
+
sessionId: SESSION_ID,
|
|
134
|
+
message: { role: 'user', content: 'Plan the refactor.' },
|
|
135
|
+
}),
|
|
136
|
+
JSON.stringify(assistantRow('asst-1', 'claude-opus-5', [{ type: 'text', text: 'Here is the plan.' }])),
|
|
137
|
+
JSON.stringify(assistantRow('asst-2', '<synthetic>', [{ type: 'text', text: 'Claude usage limit reached.' }])),
|
|
138
|
+
'',
|
|
139
|
+
].join('\n'),
|
|
140
|
+
'utf8',
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
await withIsolatedDatabase(async () => {
|
|
144
|
+
const now = new Date().toISOString();
|
|
145
|
+
sessionsDb.createSession(SESSION_ID, 'claude', tempRoot, 'Model session', now, now, transcriptPath);
|
|
146
|
+
|
|
147
|
+
const history = await new ClaudeSessionsProvider().fetchHistory(SESSION_ID, {
|
|
148
|
+
providerSessionId: SESSION_ID,
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
const replies = history.messages.filter((message) => message.kind === 'text' && message.role === 'assistant');
|
|
152
|
+
assert.equal(replies.length, 2);
|
|
153
|
+
assert.equal(replies[0]?.model, 'claude-opus-5');
|
|
154
|
+
assert.equal(replies[1]?.model, undefined);
|
|
155
|
+
assert.equal(history.messages.find((message) => message.role === 'user')?.model, undefined);
|
|
156
|
+
});
|
|
157
|
+
} finally {
|
|
158
|
+
await rm(tempRoot, { recursive: true, force: true });
|
|
159
|
+
}
|
|
160
|
+
});
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import test from 'node:test';
|
|
3
|
+
|
|
4
|
+
import { extractClaudeEventModel } from '@/modules/providers/list/claude/claude-models.provider.js';
|
|
5
|
+
|
|
6
|
+
const SESSION_ID = 'session-1';
|
|
7
|
+
|
|
8
|
+
test('ignores the <synthetic> placeholder Claude Code stamps on synthesized rows', () => {
|
|
9
|
+
assert.equal(
|
|
10
|
+
extractClaudeEventModel(
|
|
11
|
+
{ sessionId: SESSION_ID, message: { model: '<synthetic>' } },
|
|
12
|
+
SESSION_ID,
|
|
13
|
+
),
|
|
14
|
+
null,
|
|
15
|
+
);
|
|
16
|
+
assert.equal(
|
|
17
|
+
extractClaudeEventModel({ sessionId: SESSION_ID, model: '<synthetic>' }, SESSION_ID),
|
|
18
|
+
null,
|
|
19
|
+
);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
test('still surfaces real model ids from message and event fields', () => {
|
|
23
|
+
assert.equal(
|
|
24
|
+
extractClaudeEventModel(
|
|
25
|
+
{ sessionId: SESSION_ID, message: { model: 'claude-sonnet-5' } },
|
|
26
|
+
SESSION_ID,
|
|
27
|
+
),
|
|
28
|
+
'claude-sonnet-5',
|
|
29
|
+
);
|
|
30
|
+
assert.equal(
|
|
31
|
+
extractClaudeEventModel({ sessionId: SESSION_ID, model: 'opus' }, SESSION_ID),
|
|
32
|
+
'opus',
|
|
33
|
+
);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
test('skips a placeholder content part so a later real model tag still wins', () => {
|
|
37
|
+
assert.equal(
|
|
38
|
+
extractClaudeEventModel(
|
|
39
|
+
{
|
|
40
|
+
sessionId: SESSION_ID,
|
|
41
|
+
message: {
|
|
42
|
+
content: [
|
|
43
|
+
{ text: '<model><synthetic></model>' },
|
|
44
|
+
{ text: '<model>claude-sonnet-5</model>' },
|
|
45
|
+
],
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
SESSION_ID,
|
|
49
|
+
),
|
|
50
|
+
'claude-sonnet-5',
|
|
51
|
+
);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
test('a placeholder stdout hit does not shadow a real <model> tag in the same text', () => {
|
|
55
|
+
const text = '<local-command-stdout>Set model to <synthetic></local-command-stdout>'
|
|
56
|
+
+ '<model>claude-sonnet-5</model>';
|
|
57
|
+
assert.equal(
|
|
58
|
+
extractClaudeEventModel(
|
|
59
|
+
{ sessionId: SESSION_ID, message: { content: text } },
|
|
60
|
+
SESSION_ID,
|
|
61
|
+
),
|
|
62
|
+
'claude-sonnet-5',
|
|
63
|
+
);
|
|
64
|
+
assert.equal(
|
|
65
|
+
extractClaudeEventModel(
|
|
66
|
+
{ sessionId: SESSION_ID, message: { content: [{ text }] } },
|
|
67
|
+
SESSION_ID,
|
|
68
|
+
),
|
|
69
|
+
'claude-sonnet-5',
|
|
70
|
+
);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
test('falls back to the message model when every content hit is a placeholder', () => {
|
|
74
|
+
assert.equal(
|
|
75
|
+
extractClaudeEventModel(
|
|
76
|
+
{
|
|
77
|
+
sessionId: SESSION_ID,
|
|
78
|
+
message: {
|
|
79
|
+
content: '<model><synthetic></model>',
|
|
80
|
+
model: 'claude-sonnet-5',
|
|
81
|
+
},
|
|
82
|
+
},
|
|
83
|
+
SESSION_ID,
|
|
84
|
+
),
|
|
85
|
+
'claude-sonnet-5',
|
|
86
|
+
);
|
|
87
|
+
});
|
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import { mkdtemp, rm } from 'node:fs/promises';
|
|
3
|
+
import os from 'node:os';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import test from 'node:test';
|
|
6
|
+
|
|
7
|
+
import { ClaudeSessionsProvider } from '@/modules/providers/list/claude/claude-sessions.provider.js';
|
|
8
|
+
import { CLAUDE_PREDEFINED_MODELS } from '@/modules/providers/list/claude/claude-models.provider.js';
|
|
9
|
+
import {
|
|
10
|
+
listClaudeSDKBackgroundWork,
|
|
11
|
+
queryClaudeSDK,
|
|
12
|
+
stopClaudeSDKTask,
|
|
13
|
+
} from '@/modules/providers/list/claude/claude-runtime.provider.js';
|
|
14
|
+
import type { NormalizedMessage, ProviderRuntimeContext } from '@/shared/types.js';
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* The runtime keeps the CLI's stdin open after a turn's `result` while the
|
|
18
|
+
* turn's background work is outstanding, and lets go when that work has
|
|
19
|
+
* reported. These drive `queryClaudeSDK` with a scripted SDK stream — the
|
|
20
|
+
* seam is `context.createQuery` — and watch the held prompt stream: the CLI
|
|
21
|
+
* exits when it ends, so "released" is the whole outcome.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
const SESSION_ID = 'app-hold-session';
|
|
25
|
+
const NATIVE_ID = 'native-hold-session';
|
|
26
|
+
|
|
27
|
+
type Scripted = {
|
|
28
|
+
emit: (message: Record<string, unknown>) => void;
|
|
29
|
+
end: () => void;
|
|
30
|
+
released: () => boolean;
|
|
31
|
+
stopped: string[];
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
/** A stand-in for the SDK query: yields what the test emits, and reads the held prompt to notice its release. */
|
|
35
|
+
function createScriptedQuery(): { createQuery: NonNullable<ProviderRuntimeContext['createQuery']>; script: Scripted } {
|
|
36
|
+
const queue: Array<Record<string, unknown> | null> = [];
|
|
37
|
+
let wake: (() => void) | null = null;
|
|
38
|
+
let released = false;
|
|
39
|
+
const stopped: string[] = [];
|
|
40
|
+
|
|
41
|
+
const script: Scripted = {
|
|
42
|
+
emit: (message) => { queue.push(message); wake?.(); },
|
|
43
|
+
end: () => { queue.push(null); wake?.(); },
|
|
44
|
+
released: () => released,
|
|
45
|
+
stopped,
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const createQuery: NonNullable<ProviderRuntimeContext['createQuery']> = ({ prompt }) => {
|
|
49
|
+
void (async () => {
|
|
50
|
+
for await (const _message of prompt) { /* the CLI reads its stdin */ }
|
|
51
|
+
released = true;
|
|
52
|
+
})();
|
|
53
|
+
|
|
54
|
+
const iterator = (async function* () {
|
|
55
|
+
for (;;) {
|
|
56
|
+
if (queue.length === 0) {
|
|
57
|
+
await new Promise<void>((resolve) => { wake = resolve; });
|
|
58
|
+
wake = null;
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
const next = queue.shift();
|
|
62
|
+
if (next === null || next === undefined) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
yield next;
|
|
66
|
+
}
|
|
67
|
+
})();
|
|
68
|
+
|
|
69
|
+
return Object.assign(iterator, {
|
|
70
|
+
interrupt: async () => {},
|
|
71
|
+
stopTask: async (taskId: string) => { stopped.push(taskId); },
|
|
72
|
+
});
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
return { createQuery, script };
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
async function withRun(
|
|
79
|
+
runTest: (context: { script: Scripted; sent: NormalizedMessage[]; done: Promise<unknown> }) => Promise<void>,
|
|
80
|
+
): Promise<void> {
|
|
81
|
+
const cwd = await mkdtemp(path.join(os.tmpdir(), 'claude-runtime-hold-'));
|
|
82
|
+
const { createQuery, script } = createScriptedQuery();
|
|
83
|
+
const sent: NormalizedMessage[] = [];
|
|
84
|
+
const writer = { send: (message: NormalizedMessage) => { sent.push(message); }, userId: null };
|
|
85
|
+
const sessions = new ClaudeSessionsProvider({ getLiveRunStartTime: () => null });
|
|
86
|
+
const context: ProviderRuntimeContext = {
|
|
87
|
+
resolveProviderSessionId: () => null,
|
|
88
|
+
resolveResumeModel: async () => undefined,
|
|
89
|
+
getProviderModels: async () => CLAUDE_PREDEFINED_MODELS as never,
|
|
90
|
+
normalizeMessage: (raw, sessionId) => sessions.normalizeMessage(raw, sessionId),
|
|
91
|
+
isProviderInstalled: async () => true,
|
|
92
|
+
createQuery,
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
try {
|
|
96
|
+
const done = queryClaudeSDK('hello', { sessionId: SESSION_ID, cwd }, writer as never, context);
|
|
97
|
+
await runTest({ script, sent, done });
|
|
98
|
+
script.end();
|
|
99
|
+
await done;
|
|
100
|
+
} finally {
|
|
101
|
+
await rm(cwd, { recursive: true, force: true });
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const settle = () => new Promise((resolve) => { setTimeout(resolve, 25); });
|
|
106
|
+
|
|
107
|
+
// A fixed settle can be too short on a loaded machine; the supersede tests
|
|
108
|
+
// wait for the run to have actually processed its turn instead.
|
|
109
|
+
async function waitFor(condition: () => boolean): Promise<void> {
|
|
110
|
+
for (let attempt = 0; attempt < 200 && !condition(); attempt += 1) {
|
|
111
|
+
await settle();
|
|
112
|
+
}
|
|
113
|
+
assert.ok(condition(), 'condition never became true');
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const init = () => ({ type: 'system', subtype: 'init', session_id: NATIVE_ID });
|
|
117
|
+
const toolUse = (id: string, name: string, input: Record<string, unknown>) => ({
|
|
118
|
+
type: 'assistant', session_id: NATIVE_ID, parent_tool_use_id: null,
|
|
119
|
+
message: { role: 'assistant', content: [{ type: 'tool_use', id, name, input }] },
|
|
120
|
+
});
|
|
121
|
+
const ack = (id: string, text: string, toolUseResult: Record<string, unknown>) => ({
|
|
122
|
+
type: 'user', session_id: NATIVE_ID, parent_tool_use_id: null,
|
|
123
|
+
message: { role: 'user', content: [{ type: 'tool_result', tool_use_id: id, content: text }] },
|
|
124
|
+
tool_use_result: toolUseResult,
|
|
125
|
+
});
|
|
126
|
+
const taskStarted = (taskId: string, toolUseId: string, taskType: string) => ({
|
|
127
|
+
type: 'system', subtype: 'task_started', session_id: NATIVE_ID, task_id: taskId, tool_use_id: toolUseId, description: `Task ${taskId}`, task_type: taskType,
|
|
128
|
+
});
|
|
129
|
+
const taskNotification = (taskId: string, toolUseId: string, status: string) => ({
|
|
130
|
+
type: 'system', subtype: 'task_notification', session_id: NATIVE_ID, task_id: taskId, tool_use_id: toolUseId, status, summary: `Task ${taskId} ${status}`, output_file: '',
|
|
131
|
+
});
|
|
132
|
+
const result = () => ({ type: 'result', subtype: 'success', session_id: NATIVE_ID, result: 'launched', duration_ms: 1, num_turns: 1 });
|
|
133
|
+
|
|
134
|
+
test('stopping the last outstanding task releases the held process', async () => {
|
|
135
|
+
await withRun(async ({ script, sent, done }) => {
|
|
136
|
+
script.emit(init());
|
|
137
|
+
script.emit(toolUse('toolu_wf', 'Workflow', { script: 'export const meta = {}' }));
|
|
138
|
+
script.emit(taskStarted('wf1', 'toolu_wf', 'local_workflow'));
|
|
139
|
+
script.emit(ack('toolu_wf', 'Workflow launched in background. Task ID: wf1', { status: 'async_launched', taskId: 'wf1', taskType: 'local_workflow' }));
|
|
140
|
+
script.emit(result());
|
|
141
|
+
await settle();
|
|
142
|
+
|
|
143
|
+
// The turn is over for the client, the process is held for the workflow.
|
|
144
|
+
assert.ok(sent.some((message) => message.kind === 'complete'));
|
|
145
|
+
assert.deepEqual(listClaudeSDKBackgroundWork().map((entry) => [entry.sessionId, entry.tasks.map((task) => task.taskId)]), [[SESSION_ID, ['wf1']]]);
|
|
146
|
+
assert.equal(script.released(), false, 'stdin stays open while the workflow runs');
|
|
147
|
+
|
|
148
|
+
// The user stops it. The CLI answers with a `stopped` notification and
|
|
149
|
+
// pushes no follow-up turn, so nothing else would ever end the hold.
|
|
150
|
+
assert.equal(await stopClaudeSDKTask(SESSION_ID, 'wf1'), true);
|
|
151
|
+
assert.deepEqual(script.stopped, ['wf1']);
|
|
152
|
+
script.emit(taskNotification('wf1', 'toolu_wf', 'stopped'));
|
|
153
|
+
await settle();
|
|
154
|
+
|
|
155
|
+
assert.deepEqual(listClaudeSDKBackgroundWork(), []);
|
|
156
|
+
assert.equal(script.released(), true, 'the process is let go once nothing is outstanding');
|
|
157
|
+
void done;
|
|
158
|
+
});
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
test('a task that reported completed keeps the hold for the turn that relays its result', async () => {
|
|
162
|
+
await withRun(async ({ script }) => {
|
|
163
|
+
script.emit(init());
|
|
164
|
+
script.emit(toolUse('toolu_wf', 'Workflow', { script: 'export const meta = {}' }));
|
|
165
|
+
script.emit(taskStarted('wf1', 'toolu_wf', 'local_workflow'));
|
|
166
|
+
script.emit(ack('toolu_wf', 'Workflow launched in background. Task ID: wf1', { status: 'async_launched', taskId: 'wf1', taskType: 'local_workflow' }));
|
|
167
|
+
script.emit(result());
|
|
168
|
+
await settle();
|
|
169
|
+
|
|
170
|
+
// Completed, unlike stopped, is followed by a turn the CLI pushes to relay
|
|
171
|
+
// the result; closing stdin at the notification would cut it short.
|
|
172
|
+
script.emit(taskNotification('wf1', 'toolu_wf', 'completed'));
|
|
173
|
+
await settle();
|
|
174
|
+
assert.equal(script.released(), false);
|
|
175
|
+
|
|
176
|
+
script.emit(result());
|
|
177
|
+
await settle();
|
|
178
|
+
assert.equal(script.released(), true, 'the follow-up turn\'s result ends the hold');
|
|
179
|
+
});
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
test('an agent that ran in the foreground and settled before the result does not hold the process', async () => {
|
|
183
|
+
await withRun(async ({ script }) => {
|
|
184
|
+
// An Agent call without `run_in_background` is scored as background by
|
|
185
|
+
// the static rule, but the CLI ran it in the foreground: its task started
|
|
186
|
+
// and settled before the turn ended. The task events know that.
|
|
187
|
+
script.emit(init());
|
|
188
|
+
script.emit(toolUse('toolu_agent', 'Agent', { prompt: 'Return the word FOUR', subagent_type: 'general-purpose' }));
|
|
189
|
+
script.emit(taskStarted('a1', 'toolu_agent', 'local_agent'));
|
|
190
|
+
script.emit(taskNotification('a1', 'toolu_agent', 'completed'));
|
|
191
|
+
script.emit(ack('toolu_agent', 'FOUR', { status: 'completed', agentId: 'a1' }));
|
|
192
|
+
script.emit(result());
|
|
193
|
+
await settle();
|
|
194
|
+
|
|
195
|
+
assert.deepEqual(listClaudeSDKBackgroundWork(), []);
|
|
196
|
+
assert.equal(script.released(), true, 'nothing is outstanding, so nothing to hold for');
|
|
197
|
+
});
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
test('a new turn stops the tasks the held process still has going before letting it go', async () => {
|
|
201
|
+
// Two runs on one session, each with its own scripted CLI: the first is
|
|
202
|
+
// held for a background agent, the second is the user's next message.
|
|
203
|
+
const cwd = await mkdtemp(path.join(os.tmpdir(), 'claude-runtime-hold-'));
|
|
204
|
+
const first = createScriptedQuery();
|
|
205
|
+
const second = createScriptedQuery();
|
|
206
|
+
const sent: NormalizedMessage[] = [];
|
|
207
|
+
const writer = { send: (message: NormalizedMessage) => { sent.push(message); }, userId: null };
|
|
208
|
+
const sessions = new ClaudeSessionsProvider({ getLiveRunStartTime: () => null });
|
|
209
|
+
const context: Omit<ProviderRuntimeContext, 'createQuery'> = {
|
|
210
|
+
resolveProviderSessionId: () => null,
|
|
211
|
+
resolveResumeModel: async () => undefined,
|
|
212
|
+
getProviderModels: async () => CLAUDE_PREDEFINED_MODELS as never,
|
|
213
|
+
normalizeMessage: (raw, sessionId) => sessions.normalizeMessage(raw, sessionId),
|
|
214
|
+
isProviderInstalled: async () => true,
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
try {
|
|
218
|
+
const firstRun = queryClaudeSDK('start an agent', { sessionId: SESSION_ID, cwd }, writer as never, { ...context, createQuery: first.createQuery });
|
|
219
|
+
await settle();
|
|
220
|
+
first.script.emit(init());
|
|
221
|
+
first.script.emit(toolUse('toolu_agent', 'Agent', { prompt: 'sleep 90', subagent_type: 'general-purpose', run_in_background: true }));
|
|
222
|
+
first.script.emit(taskStarted('a1', 'toolu_agent', 'local_agent'));
|
|
223
|
+
first.script.emit(ack('toolu_agent', 'Async agent launched successfully.', { status: 'async_launched', agentId: 'a1' }));
|
|
224
|
+
first.script.emit(result());
|
|
225
|
+
await waitFor(() => sent.some((message) => message.kind === 'complete'));
|
|
226
|
+
assert.equal(first.script.released(), false, 'the first process is held for its agent');
|
|
227
|
+
|
|
228
|
+
// Closing stdin alone would leave the CLI waiting for the agent, so the
|
|
229
|
+
// superseded process would live on next to the new one for as long as
|
|
230
|
+
// the agent runs. The stop has to reach it before stdin ends, because the
|
|
231
|
+
// SDK drops control requests written to an ended stdin.
|
|
232
|
+
const secondRun = queryClaudeSDK('Continue', { sessionId: SESSION_ID, cwd }, writer as never, { ...context, createQuery: second.createQuery });
|
|
233
|
+
await settle();
|
|
234
|
+
assert.deepEqual(first.script.stopped, ['a1'], 'the held process is told to stop its agent');
|
|
235
|
+
assert.equal(first.script.released(), true, 'and is then let go');
|
|
236
|
+
assert.deepEqual(second.script.stopped, [], 'the new process is untouched');
|
|
237
|
+
|
|
238
|
+
first.script.emit(taskNotification('a1', 'toolu_agent', 'stopped'));
|
|
239
|
+
first.script.end();
|
|
240
|
+
await firstRun;
|
|
241
|
+
second.script.emit(init());
|
|
242
|
+
second.script.emit(result());
|
|
243
|
+
second.script.end();
|
|
244
|
+
await secondRun;
|
|
245
|
+
assert.deepEqual(listClaudeSDKBackgroundWork(), []);
|
|
246
|
+
} finally {
|
|
247
|
+
await rm(cwd, { recursive: true, force: true });
|
|
248
|
+
}
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
test('a held process is still let go when stopping its tasks throws', async () => {
|
|
252
|
+
// Stopping is best effort; a stop that throws on the spot must not keep the
|
|
253
|
+
// held process alive or take the new turn down with it.
|
|
254
|
+
const cwd = await mkdtemp(path.join(os.tmpdir(), 'claude-runtime-hold-'));
|
|
255
|
+
const first = createScriptedQuery();
|
|
256
|
+
const second = createScriptedQuery();
|
|
257
|
+
const sent: NormalizedMessage[] = [];
|
|
258
|
+
const writer = { send: (message: NormalizedMessage) => { sent.push(message); }, userId: null };
|
|
259
|
+
const sessions = new ClaudeSessionsProvider({ getLiveRunStartTime: () => null });
|
|
260
|
+
const context: Omit<ProviderRuntimeContext, 'createQuery'> = {
|
|
261
|
+
resolveProviderSessionId: () => null,
|
|
262
|
+
resolveResumeModel: async () => undefined,
|
|
263
|
+
getProviderModels: async () => CLAUDE_PREDEFINED_MODELS as never,
|
|
264
|
+
normalizeMessage: (raw, sessionId) => sessions.normalizeMessage(raw, sessionId),
|
|
265
|
+
isProviderInstalled: async () => true,
|
|
266
|
+
};
|
|
267
|
+
const throwingStop: NonNullable<ProviderRuntimeContext['createQuery']> = (options) =>
|
|
268
|
+
Object.assign(first.createQuery(options), {
|
|
269
|
+
stopTask: () => { throw new Error('stop failed'); },
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
try {
|
|
273
|
+
const firstRun = queryClaudeSDK('start an agent', { sessionId: SESSION_ID, cwd }, writer as never, { ...context, createQuery: throwingStop });
|
|
274
|
+
await settle();
|
|
275
|
+
first.script.emit(init());
|
|
276
|
+
first.script.emit(toolUse('toolu_agent', 'Agent', { prompt: 'sleep 90', subagent_type: 'general-purpose', run_in_background: true }));
|
|
277
|
+
first.script.emit(taskStarted('a1', 'toolu_agent', 'local_agent'));
|
|
278
|
+
first.script.emit(ack('toolu_agent', 'Async agent launched successfully.', { status: 'async_launched', agentId: 'a1' }));
|
|
279
|
+
first.script.emit(result());
|
|
280
|
+
await waitFor(() => sent.some((message) => message.kind === 'complete'));
|
|
281
|
+
assert.equal(first.script.released(), false, 'the first process is held for its agent');
|
|
282
|
+
|
|
283
|
+
const secondRun = queryClaudeSDK('Continue', { sessionId: SESSION_ID, cwd }, writer as never, { ...context, createQuery: second.createQuery });
|
|
284
|
+
await settle();
|
|
285
|
+
assert.equal(first.script.released(), true, 'the held process is let go anyway');
|
|
286
|
+
|
|
287
|
+
first.script.end();
|
|
288
|
+
await firstRun;
|
|
289
|
+
second.script.emit(init());
|
|
290
|
+
second.script.emit(result());
|
|
291
|
+
second.script.end();
|
|
292
|
+
await secondRun;
|
|
293
|
+
assert.equal(second.script.released(), true, 'the new turn ran to completion');
|
|
294
|
+
} finally {
|
|
295
|
+
await rm(cwd, { recursive: true, force: true });
|
|
296
|
+
}
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
test('a turn whose tool emits no task events still holds on the static rule', async () => {
|
|
300
|
+
await withRun(async ({ script }) => {
|
|
301
|
+
script.emit(init());
|
|
302
|
+
script.emit(toolUse('toolu_monitor', 'Monitor', { command: 'tail -f x', description: 'watch', timeout_ms: 1000 }));
|
|
303
|
+
script.emit(ack('toolu_monitor', 'Monitor started', {}));
|
|
304
|
+
script.emit(result());
|
|
305
|
+
await settle();
|
|
306
|
+
|
|
307
|
+
assert.equal(script.released(), false, 'Monitor reports no task, so the launch rule decides');
|
|
308
|
+
});
|
|
309
|
+
});
|