@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
|
@@ -13,6 +13,8 @@ type SessionRow = {
|
|
|
13
13
|
model: string | null;
|
|
14
14
|
/** Reasoning effort this session runs with; NULL until the app records one. */
|
|
15
15
|
effort: string | null;
|
|
16
|
+
/** The app session this one was branched from; NULL unless it is a fork. */
|
|
17
|
+
forked_from_session_id: string | null;
|
|
16
18
|
isArchived: number;
|
|
17
19
|
created_at: string;
|
|
18
20
|
updated_at: string;
|
|
@@ -24,7 +26,7 @@ type RecentSessionsPage = {
|
|
|
24
26
|
};
|
|
25
27
|
|
|
26
28
|
const SESSION_ROW_COLUMNS =
|
|
27
|
-
'session_id, provider, provider_session_id, project_path, jsonl_path, custom_name, model, effort, isArchived, created_at, updated_at';
|
|
29
|
+
'session_id, provider, provider_session_id, project_path, jsonl_path, custom_name, model, effort, forked_from_session_id, isArchived, created_at, updated_at';
|
|
28
30
|
|
|
29
31
|
const SQLITE_UTC_TIMESTAMP_REGEX = /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/;
|
|
30
32
|
|
|
@@ -111,7 +113,7 @@ export const sessionsDb = {
|
|
|
111
113
|
updated_at = COALESCE(?, CURRENT_TIMESTAMP),
|
|
112
114
|
project_path = ?,
|
|
113
115
|
jsonl_path = ?,
|
|
114
|
-
isArchived = 0,
|
|
116
|
+
isArchived = CASE WHEN ? IS NULL OR julianday(?) > julianday(updated_at) THEN 0 ELSE isArchived END,
|
|
115
117
|
custom_name = CASE
|
|
116
118
|
WHEN session_id <> provider_session_id AND custom_name IS NOT NULL THEN custom_name
|
|
117
119
|
ELSE COALESCE(?, custom_name)
|
|
@@ -122,6 +124,8 @@ export const sessionsDb = {
|
|
|
122
124
|
updatedAtValue,
|
|
123
125
|
normalizedProjectPath,
|
|
124
126
|
jsonlPath ?? null,
|
|
127
|
+
updatedAtValue,
|
|
128
|
+
updatedAtValue,
|
|
125
129
|
customName ?? null,
|
|
126
130
|
existing.session_id
|
|
127
131
|
);
|
|
@@ -141,7 +145,7 @@ export const sessionsDb = {
|
|
|
141
145
|
updated_at = excluded.updated_at,
|
|
142
146
|
project_path = excluded.project_path,
|
|
143
147
|
jsonl_path = excluded.jsonl_path,
|
|
144
|
-
isArchived = 0,
|
|
148
|
+
isArchived = CASE WHEN ? IS NULL OR julianday(excluded.updated_at) > julianday(sessions.updated_at) THEN 0 ELSE sessions.isArchived END,
|
|
145
149
|
custom_name = CASE
|
|
146
150
|
WHEN sessions.session_id <> sessions.provider_session_id AND sessions.custom_name IS NOT NULL
|
|
147
151
|
THEN sessions.custom_name
|
|
@@ -155,6 +159,7 @@ export const sessionsDb = {
|
|
|
155
159
|
normalizedProjectPath,
|
|
156
160
|
jsonlPath ?? null,
|
|
157
161
|
createdAtValue,
|
|
162
|
+
updatedAtValue,
|
|
158
163
|
updatedAtValue
|
|
159
164
|
);
|
|
160
165
|
|
|
@@ -189,6 +194,55 @@ export const sessionsDb = {
|
|
|
189
194
|
return sessionId;
|
|
190
195
|
},
|
|
191
196
|
|
|
197
|
+
/**
|
|
198
|
+
* Inserts a session that already has its provider artifact on disk.
|
|
199
|
+
*
|
|
200
|
+
* Unlike `createAppSession` this writes `provider_session_id` and
|
|
201
|
+
* `jsonl_path` immediately, because a fork's transcript file exists before
|
|
202
|
+
* the row does — and the filesystem watcher would otherwise index it as an
|
|
203
|
+
* unrelated session under its own id.
|
|
204
|
+
*/
|
|
205
|
+
createForkedSession(input: {
|
|
206
|
+
sessionId: string;
|
|
207
|
+
provider: string;
|
|
208
|
+
projectPath: string;
|
|
209
|
+
customName: string | null;
|
|
210
|
+
providerSessionId: string;
|
|
211
|
+
jsonlPath: string;
|
|
212
|
+
forkedFromSessionId: string;
|
|
213
|
+
model: string | null;
|
|
214
|
+
effort: string | null;
|
|
215
|
+
}): string {
|
|
216
|
+
const db = getConnection();
|
|
217
|
+
const normalizedProjectPath = normalizeProjectPathForProvider(input.provider, input.projectPath);
|
|
218
|
+
|
|
219
|
+
projectsDb.createProjectPath(normalizedProjectPath);
|
|
220
|
+
|
|
221
|
+
// The watcher may already have created a row for the new transcript. Its
|
|
222
|
+
// id is the provider-native one, which is what this row claims, so replace
|
|
223
|
+
// it rather than leaving two sidebar entries for one conversation.
|
|
224
|
+
db.transaction(() => {
|
|
225
|
+
db.prepare('DELETE FROM sessions WHERE session_id = ? AND session_id <> ?')
|
|
226
|
+
.run(input.providerSessionId, input.sessionId);
|
|
227
|
+
db.prepare(
|
|
228
|
+
`INSERT INTO sessions (session_id, provider, provider_session_id, custom_name, project_path, jsonl_path, model, effort, forked_from_session_id, isArchived, created_at, updated_at)
|
|
229
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, 0, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)`
|
|
230
|
+
).run(
|
|
231
|
+
input.sessionId,
|
|
232
|
+
input.provider,
|
|
233
|
+
input.providerSessionId,
|
|
234
|
+
input.customName,
|
|
235
|
+
normalizedProjectPath,
|
|
236
|
+
input.jsonlPath,
|
|
237
|
+
input.model,
|
|
238
|
+
input.effort,
|
|
239
|
+
input.forkedFromSessionId,
|
|
240
|
+
);
|
|
241
|
+
})();
|
|
242
|
+
|
|
243
|
+
return input.sessionId;
|
|
244
|
+
},
|
|
245
|
+
|
|
192
246
|
/**
|
|
193
247
|
* Records the provider-native session id for one app-allocated session.
|
|
194
248
|
*
|
|
@@ -235,6 +289,125 @@ export const sessionsDb = {
|
|
|
235
289
|
merge();
|
|
236
290
|
},
|
|
237
291
|
|
|
292
|
+
/**
|
|
293
|
+
* Moves one session onto a different provider session and transcript.
|
|
294
|
+
*
|
|
295
|
+
* Only editing a message on a provider that has to branch to rewind (Codex)
|
|
296
|
+
* does this — an ordinary run keeps the same provider session for its whole
|
|
297
|
+
* life. `assignProviderSessionId` cannot be used for it: that one keeps the
|
|
298
|
+
* existing `jsonl_path` on purpose, so a session repointed with it would
|
|
299
|
+
* claim the new thread while still reading the old transcript.
|
|
300
|
+
*
|
|
301
|
+
* The watcher may already have indexed the new transcript under its own id.
|
|
302
|
+
* That row is the same conversation this one is about to become, so it is
|
|
303
|
+
* replaced rather than left behind as a second sidebar entry.
|
|
304
|
+
*/
|
|
305
|
+
repointSessionToProviderSession(
|
|
306
|
+
sessionId: string,
|
|
307
|
+
input: { providerSessionId: string; jsonlPath: string },
|
|
308
|
+
): void {
|
|
309
|
+
const db = getConnection();
|
|
310
|
+
|
|
311
|
+
db.transaction(() => {
|
|
312
|
+
db.prepare('DELETE FROM sessions WHERE session_id = ? AND session_id <> ?')
|
|
313
|
+
.run(input.providerSessionId, sessionId);
|
|
314
|
+
db.prepare(
|
|
315
|
+
`UPDATE sessions SET
|
|
316
|
+
provider_session_id = ?,
|
|
317
|
+
jsonl_path = ?,
|
|
318
|
+
updated_at = CURRENT_TIMESTAMP
|
|
319
|
+
WHERE session_id = ?`
|
|
320
|
+
).run(input.providerSessionId, input.jsonlPath, sessionId);
|
|
321
|
+
})();
|
|
322
|
+
},
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Detaches a session from its provider session so the next run starts a new
|
|
326
|
+
* one.
|
|
327
|
+
*
|
|
328
|
+
* Used when an edit replaces the very first prompt: there is no conversation
|
|
329
|
+
* left to branch from, so the session starts over instead.
|
|
330
|
+
*/
|
|
331
|
+
detachProviderSession(sessionId: string): void {
|
|
332
|
+
const db = getConnection();
|
|
333
|
+
db.prepare(
|
|
334
|
+
`UPDATE sessions SET
|
|
335
|
+
provider_session_id = NULL,
|
|
336
|
+
jsonl_path = NULL,
|
|
337
|
+
updated_at = CURRENT_TIMESTAMP
|
|
338
|
+
WHERE session_id = ?`
|
|
339
|
+
).run(sessionId);
|
|
340
|
+
},
|
|
341
|
+
|
|
342
|
+
/**
|
|
343
|
+
* Records that a session has left a provider session behind for good.
|
|
344
|
+
*
|
|
345
|
+
* The transcript stays on disk, which is deliberate — the abandoned attempt
|
|
346
|
+
* is recoverable — but the indexer must not offer it back, and on a session
|
|
347
|
+
* discovered from disk (whose app id *is* the provider id) rediscovering it
|
|
348
|
+
* would repoint the row at the conversation the user edited away from.
|
|
349
|
+
*/
|
|
350
|
+
markProviderSessionSuperseded(input: {
|
|
351
|
+
providerSessionId: string;
|
|
352
|
+
provider: string;
|
|
353
|
+
sessionId: string;
|
|
354
|
+
jsonlPath: string | null;
|
|
355
|
+
}): void {
|
|
356
|
+
const db = getConnection();
|
|
357
|
+
db.prepare(
|
|
358
|
+
`INSERT INTO superseded_provider_sessions (provider_session_id, provider, session_id, jsonl_path)
|
|
359
|
+
VALUES (?, ?, ?, ?)
|
|
360
|
+
ON CONFLICT(provider_session_id, provider) DO UPDATE SET
|
|
361
|
+
session_id = excluded.session_id,
|
|
362
|
+
jsonl_path = excluded.jsonl_path,
|
|
363
|
+
created_at = CURRENT_TIMESTAMP`
|
|
364
|
+
).run(input.providerSessionId, input.provider, input.sessionId, input.jsonlPath);
|
|
365
|
+
},
|
|
366
|
+
|
|
367
|
+
isProviderSessionSuperseded(providerSessionId: string, provider: string): boolean {
|
|
368
|
+
const db = getConnection();
|
|
369
|
+
const row = db
|
|
370
|
+
.prepare(
|
|
371
|
+
`SELECT 1 AS found FROM superseded_provider_sessions
|
|
372
|
+
WHERE provider_session_id = ? AND provider = ?
|
|
373
|
+
LIMIT 1`
|
|
374
|
+
)
|
|
375
|
+
.get(providerSessionId, provider) as { found: number } | undefined;
|
|
376
|
+
|
|
377
|
+
return Boolean(row);
|
|
378
|
+
},
|
|
379
|
+
|
|
380
|
+
/**
|
|
381
|
+
* Transcripts one session has left behind, for the caller that deletes a
|
|
382
|
+
* conversation from disk.
|
|
383
|
+
*
|
|
384
|
+
* A conversation edited more than once has lived in more than one file, and
|
|
385
|
+
* the session row only ever points at the newest.
|
|
386
|
+
*/
|
|
387
|
+
getSupersededTranscriptPaths(sessionId: string): string[] {
|
|
388
|
+
const db = getConnection();
|
|
389
|
+
const rows = db
|
|
390
|
+
.prepare(
|
|
391
|
+
`SELECT jsonl_path FROM superseded_provider_sessions
|
|
392
|
+
WHERE session_id = ? AND jsonl_path IS NOT NULL`
|
|
393
|
+
)
|
|
394
|
+
.all(sessionId) as Array<{ jsonl_path: string }>;
|
|
395
|
+
|
|
396
|
+
return rows.map((row) => row.jsonl_path);
|
|
397
|
+
},
|
|
398
|
+
|
|
399
|
+
/**
|
|
400
|
+
* Forgets what a session left behind, once the session itself is gone.
|
|
401
|
+
*
|
|
402
|
+
* Without this the record outlives the row it was written for and keeps the
|
|
403
|
+
* indexer refusing a transcript that no longer belongs to anything — a
|
|
404
|
+
* conversation invisible to the app and impossible to delete through it.
|
|
405
|
+
*/
|
|
406
|
+
clearSupersededProviderSessions(sessionId: string): void {
|
|
407
|
+
const db = getConnection();
|
|
408
|
+
db.prepare('DELETE FROM superseded_provider_sessions WHERE session_id = ?').run(sessionId);
|
|
409
|
+
},
|
|
410
|
+
|
|
238
411
|
/**
|
|
239
412
|
* Records the model one session runs with.
|
|
240
413
|
*
|
|
@@ -521,4 +694,32 @@ export const sessionsDb = {
|
|
|
521
694
|
const db = getConnection();
|
|
522
695
|
return db.prepare('DELETE FROM sessions WHERE session_id = ?').run(sessionId).changes > 0;
|
|
523
696
|
},
|
|
697
|
+
|
|
698
|
+
/** Used by the OpenCode synchronizer to remove indexed child sessions by their native id. */
|
|
699
|
+
deleteSessionByProviderSessionId(providerSessionId: string, provider: string): boolean {
|
|
700
|
+
const db = getConnection();
|
|
701
|
+
return db
|
|
702
|
+
.prepare('DELETE FROM sessions WHERE provider_session_id = ? AND provider = ?')
|
|
703
|
+
.run(providerSessionId, provider).changes > 0;
|
|
704
|
+
},
|
|
705
|
+
|
|
706
|
+
/**
|
|
707
|
+
* Lists every indexed session that claims a transcript file on disk.
|
|
708
|
+
*
|
|
709
|
+
* Only rows with a `jsonl_path` are returned, which deliberately excludes
|
|
710
|
+
* app-created sessions still waiting for their first provider write and
|
|
711
|
+
* OpenCode rows (whose transcripts all live inside one shared sqlite file).
|
|
712
|
+
* Used by the session synchronizer to find rows whose transcript has been
|
|
713
|
+
* deleted underneath the index.
|
|
714
|
+
*/
|
|
715
|
+
getSessionsWithTranscriptPath(): Array<{ session_id: string; jsonl_path: string }> {
|
|
716
|
+
const db = getConnection();
|
|
717
|
+
return db
|
|
718
|
+
.prepare(
|
|
719
|
+
`SELECT session_id, jsonl_path
|
|
720
|
+
FROM sessions
|
|
721
|
+
WHERE jsonl_path IS NOT NULL AND jsonl_path <> ''`
|
|
722
|
+
)
|
|
723
|
+
.all() as Array<{ session_id: string; jsonl_path: string }>;
|
|
724
|
+
},
|
|
524
725
|
};
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { getConnection } from '@/modules/database/connection.js';
|
|
2
|
+
|
|
3
|
+
type PreferenceRow = {
|
|
4
|
+
preference_key: string;
|
|
5
|
+
preference_value: string;
|
|
6
|
+
};
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Decodes one stored preference, dropping any row whose JSON no longer parses
|
|
10
|
+
* rather than failing the whole read. A single corrupted value must not cost
|
|
11
|
+
* the user every other setting they have.
|
|
12
|
+
*/
|
|
13
|
+
function decodePreference(row: PreferenceRow): [string, unknown] | null {
|
|
14
|
+
try {
|
|
15
|
+
return [row.preference_key, JSON.parse(row.preference_value) as unknown];
|
|
16
|
+
} catch {
|
|
17
|
+
console.warn(`[UserPreferences] Dropping unreadable value for "${row.preference_key}"`);
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export const userPreferencesDb = {
|
|
23
|
+
/**
|
|
24
|
+
* Returns every preference the user has ever set, as one object.
|
|
25
|
+
*
|
|
26
|
+
* The client fetches this once on start-up, so absent keys simply mean "the
|
|
27
|
+
* user never changed this" and the client applies its own default.
|
|
28
|
+
*/
|
|
29
|
+
getPreferences(userId: number): Record<string, unknown> {
|
|
30
|
+
const db = getConnection();
|
|
31
|
+
const rows = db
|
|
32
|
+
.prepare(
|
|
33
|
+
`SELECT preference_key, preference_value
|
|
34
|
+
FROM user_preferences
|
|
35
|
+
WHERE user_id = ?`
|
|
36
|
+
)
|
|
37
|
+
.all(userId) as PreferenceRow[];
|
|
38
|
+
|
|
39
|
+
const preferences: Record<string, unknown> = {};
|
|
40
|
+
for (const row of rows) {
|
|
41
|
+
const decoded = decodePreference(row);
|
|
42
|
+
if (decoded) {
|
|
43
|
+
preferences[decoded[0]] = decoded[1];
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return preferences;
|
|
48
|
+
},
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Merge-patches preferences: keys present in `updates` are written, keys
|
|
52
|
+
* absent are left alone, and a key given as `undefined` is deleted.
|
|
53
|
+
*
|
|
54
|
+
* Runs in one transaction so a multi-key save from the settings dialog can
|
|
55
|
+
* never be observed half-applied.
|
|
56
|
+
*/
|
|
57
|
+
savePreferences(userId: number, updates: Record<string, unknown>): void {
|
|
58
|
+
const db = getConnection();
|
|
59
|
+
const upsert = db.prepare(
|
|
60
|
+
`INSERT INTO user_preferences (user_id, preference_key, preference_value, updated_at)
|
|
61
|
+
VALUES (?, ?, ?, CURRENT_TIMESTAMP)
|
|
62
|
+
ON CONFLICT(user_id, preference_key) DO UPDATE SET
|
|
63
|
+
preference_value = excluded.preference_value,
|
|
64
|
+
updated_at = CURRENT_TIMESTAMP`
|
|
65
|
+
);
|
|
66
|
+
const remove = db.prepare(
|
|
67
|
+
'DELETE FROM user_preferences WHERE user_id = ? AND preference_key = ?'
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
db.transaction(() => {
|
|
71
|
+
for (const [key, value] of Object.entries(updates)) {
|
|
72
|
+
if (value === undefined) {
|
|
73
|
+
remove.run(userId, key);
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
upsert.run(userId, key, JSON.stringify(value));
|
|
77
|
+
}
|
|
78
|
+
})();
|
|
79
|
+
},
|
|
80
|
+
};
|
|
@@ -96,6 +96,30 @@ CREATE TABLE IF NOT EXISTS projects (
|
|
|
96
96
|
);
|
|
97
97
|
`;
|
|
98
98
|
|
|
99
|
+
export const SCHEDULED_MESSAGES_TABLE_SCHEMA_SQL = `
|
|
100
|
+
CREATE TABLE IF NOT EXISTS scheduled_messages (
|
|
101
|
+
id TEXT PRIMARY KEY,
|
|
102
|
+
user_id INTEGER NOT NULL,
|
|
103
|
+
session_id TEXT NOT NULL,
|
|
104
|
+
content TEXT NOT NULL,
|
|
105
|
+
-- Composer preferences (model, effort, permission mode, attachments) as
|
|
106
|
+
-- they were when the message was scheduled, so it runs the way the user
|
|
107
|
+
-- set it up rather than however the session is configured hours later.
|
|
108
|
+
options TEXT NOT NULL DEFAULT '{}',
|
|
109
|
+
-- UTC. The client sends an absolute instant so the schedule does not move
|
|
110
|
+
-- when the user changes time zone between scheduling and firing.
|
|
111
|
+
scheduled_for DATETIME NOT NULL,
|
|
112
|
+
-- pending | sent | failed | cancelled
|
|
113
|
+
status TEXT NOT NULL DEFAULT 'pending',
|
|
114
|
+
-- Why a failed one failed, shown next to it in the composer.
|
|
115
|
+
failure_reason TEXT,
|
|
116
|
+
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
117
|
+
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
118
|
+
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
|
|
119
|
+
FOREIGN KEY (session_id) REFERENCES sessions(session_id) ON DELETE CASCADE
|
|
120
|
+
);
|
|
121
|
+
`;
|
|
122
|
+
|
|
99
123
|
export const SESSIONS_TABLE_SCHEMA_SQL = `
|
|
100
124
|
CREATE TABLE IF NOT EXISTS sessions (
|
|
101
125
|
session_id TEXT NOT NULL,
|
|
@@ -114,6 +138,10 @@ CREATE TABLE IF NOT EXISTS sessions (
|
|
|
114
138
|
-- restores its exact runtime configuration instead of provider defaults.
|
|
115
139
|
model TEXT,
|
|
116
140
|
effort TEXT,
|
|
141
|
+
-- The app session this one was branched from, NULL for sessions created
|
|
142
|
+
-- normally. Informational only: a fork is a fully independent provider
|
|
143
|
+
-- session, and deleting the source does not affect it.
|
|
144
|
+
forked_from_session_id TEXT,
|
|
117
145
|
isArchived BOOLEAN DEFAULT 0,
|
|
118
146
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
119
147
|
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
@@ -146,6 +174,11 @@ CREATE TABLE IF NOT EXISTS app_config (
|
|
|
146
174
|
* controlled in each provider's `-models.provider.ts` adapter so they can be
|
|
147
175
|
* updated without migrating application data. `model_id` is unique only within
|
|
148
176
|
* a provider because different CLIs can accept the same identifier.
|
|
177
|
+
*
|
|
178
|
+
* `effort_values` (JSON array of level ids) and `effort_default` hold the
|
|
179
|
+
* reasoning-effort levels the user declared for the model; both stay NULL when
|
|
180
|
+
* none were declared. They sit after the timestamps so fresh installs match the
|
|
181
|
+
* column order that `runMigrations` produces on upgraded databases.
|
|
149
182
|
*/
|
|
150
183
|
export const PROVIDER_MODELS_TABLE_SCHEMA_SQL = `
|
|
151
184
|
CREATE TABLE IF NOT EXISTS provider_models (
|
|
@@ -156,10 +189,74 @@ CREATE TABLE IF NOT EXISTS provider_models (
|
|
|
156
189
|
sort_order INTEGER NOT NULL DEFAULT 0,
|
|
157
190
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
158
191
|
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
192
|
+
effort_values TEXT DEFAULT NULL,
|
|
193
|
+
effort_default TEXT DEFAULT NULL,
|
|
159
194
|
UNIQUE(provider, model_id)
|
|
160
195
|
);
|
|
161
196
|
`;
|
|
162
197
|
|
|
198
|
+
/**
|
|
199
|
+
* Per-user application preferences that used to live in browser localStorage.
|
|
200
|
+
*
|
|
201
|
+
* One row per (user, key); `preference_value` is always a JSON document so a
|
|
202
|
+
* key can hold a scalar (`"dark"`), a flag (`false`) or a whole settings blob
|
|
203
|
+
* without the schema having to know which. Keeping them server-side is what
|
|
204
|
+
* makes a preference follow the user from one device to another.
|
|
205
|
+
*/
|
|
206
|
+
export const USER_PREFERENCES_TABLE_SCHEMA_SQL = `
|
|
207
|
+
CREATE TABLE IF NOT EXISTS user_preferences (
|
|
208
|
+
user_id INTEGER NOT NULL,
|
|
209
|
+
preference_key TEXT NOT NULL,
|
|
210
|
+
preference_value TEXT NOT NULL,
|
|
211
|
+
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
212
|
+
PRIMARY KEY (user_id, preference_key),
|
|
213
|
+
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
|
|
214
|
+
);
|
|
215
|
+
`;
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Unsent composer text and queued messages, per user and per chat scope.
|
|
219
|
+
*
|
|
220
|
+
* `draft_scope` is a session id, or `project:<projectId>` for a chat that has
|
|
221
|
+
* not been sent yet and therefore has no session. Storing this server-side is
|
|
222
|
+
* what lets a message typed on a laptop be finished on a phone.
|
|
223
|
+
*/
|
|
224
|
+
export const SESSION_DRAFTS_TABLE_SCHEMA_SQL = `
|
|
225
|
+
CREATE TABLE IF NOT EXISTS session_drafts (
|
|
226
|
+
user_id INTEGER NOT NULL,
|
|
227
|
+
draft_scope TEXT NOT NULL,
|
|
228
|
+
draft_text TEXT NOT NULL DEFAULT '',
|
|
229
|
+
queued_message TEXT,
|
|
230
|
+
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
231
|
+
PRIMARY KEY (user_id, draft_scope),
|
|
232
|
+
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
|
|
233
|
+
);
|
|
234
|
+
`;
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Provider sessions an app session has moved off, and must never move back to.
|
|
238
|
+
*
|
|
239
|
+
* Editing a message on a provider that cannot resume a transcript partway
|
|
240
|
+
* (Codex) is done by branching: the conversation is copied up to the edited
|
|
241
|
+
* turn and the app session follows the copy. The original transcript is left
|
|
242
|
+
* on disk untouched — nothing is deleted — but it is no longer the session's,
|
|
243
|
+
* and the indexer would otherwise rediscover it on its next full scan and hand
|
|
244
|
+
* the session back to the version the user edited away from.
|
|
245
|
+
*/
|
|
246
|
+
export const SUPERSEDED_PROVIDER_SESSIONS_TABLE_SCHEMA_SQL = `
|
|
247
|
+
CREATE TABLE IF NOT EXISTS superseded_provider_sessions (
|
|
248
|
+
provider_session_id TEXT NOT NULL,
|
|
249
|
+
provider TEXT NOT NULL,
|
|
250
|
+
session_id TEXT NOT NULL,
|
|
251
|
+
-- The transcript the session left behind. Recorded because the session row
|
|
252
|
+
-- stops pointing at it, and "delete this conversation from disk" has to
|
|
253
|
+
-- reach every file the conversation ever lived in.
|
|
254
|
+
jsonl_path TEXT,
|
|
255
|
+
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
256
|
+
PRIMARY KEY (provider_session_id, provider)
|
|
257
|
+
);
|
|
258
|
+
`;
|
|
259
|
+
|
|
163
260
|
export const INIT_SCHEMA_SQL = `
|
|
164
261
|
-- Initialize authentication database
|
|
165
262
|
PRAGMA foreign_keys = ON;
|
|
@@ -207,4 +304,10 @@ ${APP_CONFIG_TABLE_SCHEMA_SQL}
|
|
|
207
304
|
${PROVIDER_MODELS_TABLE_SCHEMA_SQL}
|
|
208
305
|
CREATE INDEX IF NOT EXISTS idx_provider_models_provider_order
|
|
209
306
|
ON provider_models(provider, sort_order, id);
|
|
307
|
+
|
|
308
|
+
${USER_PREFERENCES_TABLE_SCHEMA_SQL}
|
|
309
|
+
|
|
310
|
+
${SESSION_DRAFTS_TABLE_SCHEMA_SQL}
|
|
311
|
+
|
|
312
|
+
${SUPERSEDED_PROVIDER_SESSIONS_TABLE_SCHEMA_SQL}
|
|
210
313
|
`;
|
|
@@ -4,6 +4,8 @@ import os from 'node:os';
|
|
|
4
4
|
import path from 'node:path';
|
|
5
5
|
import test from 'node:test';
|
|
6
6
|
|
|
7
|
+
import Database from 'better-sqlite3';
|
|
8
|
+
|
|
7
9
|
import {
|
|
8
10
|
closeConnection,
|
|
9
11
|
getConnection,
|
|
@@ -35,6 +37,8 @@ test('provider model repository stores custom rows only and maintains session re
|
|
|
35
37
|
'sort_order',
|
|
36
38
|
'created_at',
|
|
37
39
|
'updated_at',
|
|
40
|
+
'effort_values',
|
|
41
|
+
'effort_default',
|
|
38
42
|
]);
|
|
39
43
|
assert.deepEqual(providerModelsDb.listCustomProviderModels('codex'), []);
|
|
40
44
|
|
|
@@ -43,6 +47,7 @@ test('provider model repository stores custom rows only and maintains session re
|
|
|
43
47
|
id: 'gateway/model-v1',
|
|
44
48
|
});
|
|
45
49
|
assert.equal(custom.modelId, 'gateway/model-v1');
|
|
50
|
+
assert.equal(custom.effort, null);
|
|
46
51
|
assert.equal(
|
|
47
52
|
providerModelsDb.findCustomProviderModelByModelId('codex', 'gateway/model-v1')?.recordId,
|
|
48
53
|
custom.recordId,
|
|
@@ -123,3 +128,111 @@ test('migrations create the provider model index on an install that lacks it', a
|
|
|
123
128
|
await rm(tempDirectory, { recursive: true, force: true });
|
|
124
129
|
}
|
|
125
130
|
});
|
|
131
|
+
|
|
132
|
+
test('provider model repository persists declared effort levels', async () => {
|
|
133
|
+
const previousDatabasePath = process.env.DATABASE_PATH;
|
|
134
|
+
const tempDirectory = await mkdtemp(path.join(os.tmpdir(), 'provider-model-effort-'));
|
|
135
|
+
const databasePath = path.join(tempDirectory, 'auth.db');
|
|
136
|
+
|
|
137
|
+
closeConnection();
|
|
138
|
+
process.env.DATABASE_PATH = databasePath;
|
|
139
|
+
await writeFile(databasePath, '');
|
|
140
|
+
await initializeDatabase();
|
|
141
|
+
|
|
142
|
+
try {
|
|
143
|
+
const custom = providerModelsDb.createCustomProviderModel('claude', {
|
|
144
|
+
model: 'My Custom Model',
|
|
145
|
+
id: 'my-custom-model',
|
|
146
|
+
effort: { values: ['low', 'high'], default: 'high' },
|
|
147
|
+
});
|
|
148
|
+
assert.deepEqual(custom.effort, { values: ['low', 'high'], default: 'high' });
|
|
149
|
+
assert.deepEqual(
|
|
150
|
+
providerModelsDb.listCustomProviderModels('claude')[0]?.effort,
|
|
151
|
+
{ values: ['low', 'high'], default: 'high' },
|
|
152
|
+
);
|
|
153
|
+
|
|
154
|
+
// Omitting effort on update keeps the stored levels.
|
|
155
|
+
const renamed = providerModelsDb.updateCustomProviderModel('claude', custom.recordId, {
|
|
156
|
+
model: 'Renamed',
|
|
157
|
+
id: 'my-custom-model',
|
|
158
|
+
});
|
|
159
|
+
assert.deepEqual(renamed?.effort, { values: ['low', 'high'], default: 'high' });
|
|
160
|
+
|
|
161
|
+
const narrowed = providerModelsDb.updateCustomProviderModel('claude', custom.recordId, {
|
|
162
|
+
model: 'Renamed',
|
|
163
|
+
id: 'my-custom-model',
|
|
164
|
+
effort: { values: ['medium'] },
|
|
165
|
+
});
|
|
166
|
+
assert.deepEqual(narrowed?.effort, { values: ['medium'] });
|
|
167
|
+
|
|
168
|
+
const cleared = providerModelsDb.updateCustomProviderModel('claude', custom.recordId, {
|
|
169
|
+
model: 'Renamed',
|
|
170
|
+
id: 'my-custom-model',
|
|
171
|
+
effort: null,
|
|
172
|
+
});
|
|
173
|
+
assert.equal(cleared?.effort, null);
|
|
174
|
+
const row = getConnection()
|
|
175
|
+
.prepare('SELECT effort_values, effort_default FROM provider_models WHERE id = ?')
|
|
176
|
+
.get(custom.recordId);
|
|
177
|
+
assert.deepEqual(row, { effort_values: null, effort_default: null });
|
|
178
|
+
} finally {
|
|
179
|
+
closeConnection();
|
|
180
|
+
if (previousDatabasePath === undefined) {
|
|
181
|
+
delete process.env.DATABASE_PATH;
|
|
182
|
+
} else {
|
|
183
|
+
process.env.DATABASE_PATH = previousDatabasePath;
|
|
184
|
+
}
|
|
185
|
+
await rm(tempDirectory, { recursive: true, force: true });
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
test('migrations add effort columns to an existing provider model table and keep old rows NULL', async () => {
|
|
190
|
+
const previousDatabasePath = process.env.DATABASE_PATH;
|
|
191
|
+
const tempDirectory = await mkdtemp(path.join(os.tmpdir(), 'provider-model-effort-migration-'));
|
|
192
|
+
const databasePath = path.join(tempDirectory, 'auth.db');
|
|
193
|
+
|
|
194
|
+
// The table as it shipped before effort metadata existed, with one user row.
|
|
195
|
+
const legacyDb = new Database(databasePath);
|
|
196
|
+
legacyDb.exec(`
|
|
197
|
+
CREATE TABLE provider_models (
|
|
198
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
199
|
+
provider TEXT NOT NULL CHECK (provider IN ('claude', 'cursor', 'codex', 'opencode')),
|
|
200
|
+
model_id TEXT NOT NULL,
|
|
201
|
+
model_name TEXT NOT NULL,
|
|
202
|
+
sort_order INTEGER NOT NULL DEFAULT 0,
|
|
203
|
+
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
204
|
+
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
|
|
205
|
+
UNIQUE(provider, model_id)
|
|
206
|
+
);
|
|
207
|
+
INSERT INTO provider_models (provider, model_id, model_name, sort_order)
|
|
208
|
+
VALUES ('codex', 'legacy-model', 'Legacy Model', 0);
|
|
209
|
+
`);
|
|
210
|
+
legacyDb.close();
|
|
211
|
+
|
|
212
|
+
closeConnection();
|
|
213
|
+
process.env.DATABASE_PATH = databasePath;
|
|
214
|
+
await initializeDatabase();
|
|
215
|
+
|
|
216
|
+
try {
|
|
217
|
+
const columnNames = (getConnection().prepare('PRAGMA table_info(provider_models)').all() as Array<{
|
|
218
|
+
name: string;
|
|
219
|
+
}>).map((column) => column.name);
|
|
220
|
+
assert.deepEqual(columnNames.slice(-2), ['effort_values', 'effort_default']);
|
|
221
|
+
|
|
222
|
+
const [legacy] = providerModelsDb.listCustomProviderModels('codex');
|
|
223
|
+
assert.equal(legacy?.modelId, 'legacy-model');
|
|
224
|
+
assert.equal(legacy?.effort, null);
|
|
225
|
+
|
|
226
|
+
// Running the migrations again is a no-op.
|
|
227
|
+
runMigrations(getConnection());
|
|
228
|
+
assert.equal(providerModelsDb.listCustomProviderModels('codex')[0]?.effort, null);
|
|
229
|
+
} finally {
|
|
230
|
+
closeConnection();
|
|
231
|
+
if (previousDatabasePath === undefined) {
|
|
232
|
+
delete process.env.DATABASE_PATH;
|
|
233
|
+
} else {
|
|
234
|
+
process.env.DATABASE_PATH = previousDatabasePath;
|
|
235
|
+
}
|
|
236
|
+
await rm(tempDirectory, { recursive: true, force: true });
|
|
237
|
+
}
|
|
238
|
+
});
|
|
@@ -72,6 +72,58 @@ test('createSession reactivates archived rows when the session becomes active ag
|
|
|
72
72
|
});
|
|
73
73
|
});
|
|
74
74
|
|
|
75
|
+
test("createSession leaves an archived row archived when the transcript has not changed", async () => {
|
|
76
|
+
await withIsolatedDatabase(() => {
|
|
77
|
+
const createdAt = "2026-07-18T09:00:00.000Z";
|
|
78
|
+
const updatedAt = "2026-07-18T10:00:00.000Z";
|
|
79
|
+
const jsonlPath = "/transcripts/session-untouched.jsonl";
|
|
80
|
+
|
|
81
|
+
sessionsDb.createSession("session-untouched", "claude", "/workspace/demo-project", "A Name", createdAt, updatedAt, jsonlPath);
|
|
82
|
+
sessionsDb.updateSessionIsArchived("session-untouched", true);
|
|
83
|
+
|
|
84
|
+
// A full rescan re-indexes every transcript created since the last scan,
|
|
85
|
+
// changed or not, and hands over the timestamps the file still carries.
|
|
86
|
+
sessionsDb.createSession("session-untouched", "claude", "/workspace/demo-project", "A Name", createdAt, updatedAt, jsonlPath);
|
|
87
|
+
|
|
88
|
+
assert.equal(sessionsDb.getSessionById("session-untouched")?.isArchived, 1);
|
|
89
|
+
assert.equal(sessionsDb.getArchivedSessions().length, 1);
|
|
90
|
+
assert.equal(sessionsDb.getAllSessions().length, 0);
|
|
91
|
+
|
|
92
|
+
// Actually writing to the session again still brings it back.
|
|
93
|
+
sessionsDb.createSession("session-untouched", "claude", "/workspace/demo-project", "A Name", createdAt, "2026-07-18T11:00:00.000Z", jsonlPath);
|
|
94
|
+
|
|
95
|
+
assert.equal(sessionsDb.getSessionById("session-untouched")?.isArchived, 0);
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
test("the upsert path counts an omitted timestamp as activity", async () => {
|
|
100
|
+
await withIsolatedDatabase(() => {
|
|
101
|
+
// An app-created row carries no provider id, so indexing it takes the
|
|
102
|
+
// INSERT ... ON CONFLICT branch rather than the UPDATE above. Its
|
|
103
|
+
// updated_at is CURRENT_TIMESTAMP, which resolves to whole seconds, so a
|
|
104
|
+
// call in the same second is not *newer* -- the omitted timestamp itself
|
|
105
|
+
// has to be what reactivates the row.
|
|
106
|
+
sessionsDb.createAppSession("session-legacy", "claude", "/workspace/demo-project");
|
|
107
|
+
sessionsDb.updateSessionIsArchived("session-legacy", true);
|
|
108
|
+
|
|
109
|
+
sessionsDb.createSession("session-legacy", "claude", "/workspace/demo-project", "Indexed Name");
|
|
110
|
+
|
|
111
|
+
assert.equal(sessionsDb.getSessionById("session-legacy")?.isArchived, 0);
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
test("the upsert path leaves an archived row alone for a transcript older than it", async () => {
|
|
116
|
+
await withIsolatedDatabase(() => {
|
|
117
|
+
sessionsDb.createAppSession("session-stale", "claude", "/workspace/demo-project");
|
|
118
|
+
sessionsDb.updateSessionIsArchived("session-stale", true);
|
|
119
|
+
|
|
120
|
+
sessionsDb.createSession("session-stale", "claude", "/workspace/demo-project", "Indexed Name", "2026-07-18T09:00:00.000Z", "2026-07-18T10:00:00.000Z", "/transcripts/session-stale.jsonl");
|
|
121
|
+
|
|
122
|
+
assert.equal(sessionsDb.getSessionById("session-stale")?.isArchived, 1);
|
|
123
|
+
});
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
|
|
75
127
|
test('repository reads normalize SQLite UTC timestamps to ISO strings', async () => {
|
|
76
128
|
await withIsolatedDatabase(() => {
|
|
77
129
|
sessionsDb.createAppSession('session-timezone', 'claude', '/workspace/demo-project');
|