@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.
Files changed (453) hide show
  1. package/README.md +269 -0
  2. package/dist/assets/{abnfDiagram-N423BO3Z-C4UAoHJb.js → abnfDiagram-N423BO3Z-Dmkwx38H.js} +1 -1
  3. package/dist/assets/{arc-B8jF-ja9.js → arc-DLe9AYuO.js} +1 -1
  4. package/dist/assets/{architectureDiagram-T3A2C74G-C_Wur_CU.js → architectureDiagram-T3A2C74G-CBbbbNUW.js} +1 -1
  5. package/dist/assets/{blockDiagram-VBNYF7ZC-Cq4P1i8t.js → blockDiagram-VBNYF7ZC-CaMEaWu4.js} +1 -1
  6. package/dist/assets/{c4Diagram-5PPSVZJV-C1wG4eUj.js → c4Diagram-5PPSVZJV-DhjMgNnR.js} +1 -1
  7. package/dist/assets/channel-CWrmwsir.js +1 -0
  8. package/dist/assets/{chunk-2GRJ4B5K-B8fnBG6E.js → chunk-2GRJ4B5K-yjl3-F-i.js} +1 -1
  9. package/dist/assets/{chunk-2Q5K7J3B-WdfPooPJ.js → chunk-2Q5K7J3B-DsAYaWEP.js} +1 -1
  10. package/dist/assets/{chunk-5RXB4S5H-DhZJuG2E.js → chunk-5RXB4S5H-Bt5qmqOX.js} +1 -1
  11. package/dist/assets/{chunk-5VM5RSS4-B0rjslal.js → chunk-5VM5RSS4-yWrWVvUt.js} +1 -1
  12. package/dist/assets/{chunk-6Q2QTUOP-CG2Oj4jL.js → chunk-6Q2QTUOP-Dt66K5xb.js} +1 -1
  13. package/dist/assets/{chunk-GF5L2VYU-rrl2lRxY.js → chunk-GF5L2VYU-DA0vzaj4.js} +1 -1
  14. package/dist/assets/{chunk-JWPE2WC7-CjVbrVMA.js → chunk-JWPE2WC7-CUkbx96V.js} +1 -1
  15. package/dist/assets/{chunk-KBJHAD2P-CRzKgU8S.js → chunk-KBJHAD2P-DWKxx1WH.js} +1 -1
  16. package/dist/assets/{chunk-RYQCIY6F-DBz4GTZF.js → chunk-RYQCIY6F-BqluplN0.js} +1 -1
  17. package/dist/assets/{chunk-XXDRQBXY-wHxalsXk.js → chunk-XXDRQBXY-DelKg3Xf.js} +1 -1
  18. package/dist/assets/classDiagram-JCYQIIEL-COAGCC6w.js +1 -0
  19. package/dist/assets/classDiagram-v2-OCEON4UE-COAGCC6w.js +1 -0
  20. package/dist/assets/{cose-bilkent-JH36ORCC-s1hygY8e.js → cose-bilkent-JH36ORCC-DHV93-Em.js} +1 -1
  21. package/dist/assets/{cynefin-VYW2F7L2-C4LGV15i.js → cynefin-VYW2F7L2-B9HZgoGX.js} +1 -1
  22. package/dist/assets/{cynefinDiagram-MW4NZA55-C5uhu7B3.js → cynefinDiagram-MW4NZA55-hFjjEQg8.js} +1 -1
  23. package/dist/assets/{dagre-VZM6K2ZE-B-OBTewm.js → dagre-VZM6K2ZE-CpeVOC7V.js} +1 -1
  24. package/dist/assets/{diagram-7IWD3JNH-BjGKQxih.js → diagram-7IWD3JNH-D0H1UoRA.js} +1 -1
  25. package/dist/assets/{diagram-B4RE2ZJO-BMKz1X2L.js → diagram-B4RE2ZJO-6Y7BQUup.js} +1 -1
  26. package/dist/assets/{diagram-LBJQPF4R-DNh-GhKC.js → diagram-LBJQPF4R-C0nNOwht.js} +1 -1
  27. package/dist/assets/{diagram-Q27KOJAE-BaTG9jPT.js → diagram-Q27KOJAE-En6QlOVX.js} +1 -1
  28. package/dist/assets/{diagram-UB23O5K3-BUwo2OvJ.js → diagram-UB23O5K3-CmPiNeGD.js} +1 -1
  29. package/dist/assets/{ebnfDiagram-BXEA7PRR-DAZgfzX2.js → ebnfDiagram-BXEA7PRR-Bygzp1Qt.js} +1 -1
  30. package/dist/assets/{erDiagram-JOGREHBK-1w1CcGEb.js → erDiagram-JOGREHBK-CwScRfzl.js} +1 -1
  31. package/dist/assets/{flowDiagram-UKHOOZJN-D4EII8Jj.js → flowDiagram-UKHOOZJN-bKX-0xP6.js} +1 -1
  32. package/dist/assets/{ganttDiagram-PKOTCBZU-ChvJZAgB.js → ganttDiagram-PKOTCBZU-DtZPKXGd.js} +1 -1
  33. package/dist/assets/{gitGraphDiagram-DS77QQ5N-DtOn41S8.js → gitGraphDiagram-DS77QQ5N-CUOtdL1o.js} +1 -1
  34. package/dist/assets/index-CdjIFxM2.js +5004 -0
  35. package/dist/assets/index-DgafhGTF.css +32 -0
  36. package/dist/assets/{infoDiagram-6WML65LV-DN_PVEJZ.js → infoDiagram-6WML65LV-ciRQTS0h.js} +1 -1
  37. package/dist/assets/{ishikawaDiagram-WSZJBQD7-DbkF1kXb.js → ishikawaDiagram-WSZJBQD7-CgjhQZ84.js} +1 -1
  38. package/dist/assets/{journeyDiagram-NVQOT4AX-pJwmhddE.js → journeyDiagram-NVQOT4AX-BsbQSeIW.js} +1 -1
  39. package/dist/assets/{kanban-definition-27J2QSJJ-PPAPgNp5.js → kanban-definition-27J2QSJJ-CrwzY9Jw.js} +1 -1
  40. package/dist/assets/{linear-iPR-J9f7.js → linear-BUaCDr6l.js} +1 -1
  41. package/dist/assets/{mermaid.core-BEUL86Z6.js → mermaid.core-ClgdUU0j.js} +5 -5
  42. package/dist/assets/{mindmap-definition-FAOFIHXS-BvZoBib7.js → mindmap-definition-FAOFIHXS-BictljY1.js} +1 -1
  43. package/dist/assets/{pegDiagram-VL7TDLO6-DhYreaUd.js → pegDiagram-VL7TDLO6-BXdByy_U.js} +1 -1
  44. package/dist/assets/{pieDiagram-7S7Q4E2Y-CrOHlyD0.js → pieDiagram-7S7Q4E2Y-uZsMfkRX.js} +1 -1
  45. package/dist/assets/{quadrantDiagram-CIZ2JOQS-D4g5yH3t.js → quadrantDiagram-CIZ2JOQS-7lCv4QIj.js} +1 -1
  46. package/dist/assets/{railroadDiagram-AXF67PYL-Dj39lHlf.js → railroadDiagram-AXF67PYL-07dxIWbi.js} +1 -1
  47. package/dist/assets/{requirementDiagram-LRYGKXZP-1AS4Zgm_.js → requirementDiagram-LRYGKXZP-Cb31Yv1_.js} +1 -1
  48. package/dist/assets/{sankeyDiagram-W5VNT64P-CKlMn19P.js → sankeyDiagram-W5VNT64P-X0YPSyF9.js} +1 -1
  49. package/dist/assets/{sequenceDiagram-SI44F4Z6-B_j3qyvo.js → sequenceDiagram-SI44F4Z6-BdBxSIlP.js} +1 -1
  50. package/dist/assets/server.browser-CtX3Hdl5.js +27 -0
  51. package/dist/assets/{sizeCapture-X5ZJPWSS-Bp2Op16v.js → sizeCapture-X5ZJPWSS-DQM5xAyD.js} +1 -1
  52. package/dist/assets/{stateDiagram-OKZ733FA-C0cN6DoY.js → stateDiagram-OKZ733FA-DJ3xkLUF.js} +1 -1
  53. package/dist/assets/stateDiagram-v2-UEYNNEHI-vAaE4zNo.js +1 -0
  54. package/dist/assets/{swimlanes-SLNWSIFB-BEvXOV9b.js → swimlanes-SLNWSIFB-rjfYdW0w.js} +2 -2
  55. package/dist/assets/swimlanesDiagram-ULZ7WXOC-TV2IF4Nq.js +8 -0
  56. package/dist/assets/{timeline-definition-Z64GVDOM-zdJQinvZ.js → timeline-definition-Z64GVDOM-DZMra5do.js} +1 -1
  57. package/dist/assets/{vendor-codemirror-OwyKSvPE.js → vendor-codemirror-CYrSt7-U.js} +18 -18
  58. package/dist/assets/{vennDiagram-T6HMQDX7-DuF1uZMY.js → vennDiagram-T6HMQDX7-BevD8uu0.js} +1 -1
  59. package/dist/assets/{wardleyDiagram-T6FBY63Y-B9AlLQup.js → wardleyDiagram-T6FBY63Y-DsIyXB7z.js} +1 -1
  60. package/dist/assets/{xychartDiagram-ELKLHX3M-DTJX48Qg.js → xychartDiagram-ELKLHX3M-G3PlF3ko.js} +1 -1
  61. package/dist/index.html +3 -3
  62. package/dist-server/server/index.js +11 -4
  63. package/dist-server/server/index.js.map +1 -1
  64. package/dist-server/server/modules/agent/agent.module.js +9 -2
  65. package/dist-server/server/modules/agent/agent.module.js.map +1 -1
  66. package/dist-server/server/modules/agent/agent.routes.js +177 -130
  67. package/dist-server/server/modules/agent/agent.routes.js.map +1 -1
  68. package/dist-server/server/modules/agent/tests/agent-run-registration.test.js +306 -0
  69. package/dist-server/server/modules/agent/tests/agent-run-registration.test.js.map +1 -0
  70. package/dist-server/server/modules/agent/tests/agent.routes.test.js +48 -2
  71. package/dist-server/server/modules/agent/tests/agent.routes.test.js.map +1 -1
  72. package/dist-server/server/modules/browser-use/browser-use.routes.js +39 -33
  73. package/dist-server/server/modules/browser-use/browser-use.routes.js.map +1 -1
  74. package/dist-server/server/modules/database/connection.js +1 -1
  75. package/dist-server/server/modules/database/connection.js.map +1 -1
  76. package/dist-server/server/modules/database/index.js +5 -0
  77. package/dist-server/server/modules/database/index.js.map +1 -1
  78. package/dist-server/server/modules/database/migrations.js +45 -1
  79. package/dist-server/server/modules/database/migrations.js.map +1 -1
  80. package/dist-server/server/modules/database/repositories/provider-models.js +60 -6
  81. package/dist-server/server/modules/database/repositories/provider-models.js.map +1 -1
  82. package/dist-server/server/modules/database/repositories/scheduled-messages.db.js +71 -0
  83. package/dist-server/server/modules/database/repositories/scheduled-messages.db.js.map +1 -0
  84. package/dist-server/server/modules/database/repositories/session-drafts.db.js +105 -0
  85. package/dist-server/server/modules/database/repositories/session-drafts.db.js.map +1 -0
  86. package/dist-server/server/modules/database/repositories/sessions.db.js +144 -5
  87. package/dist-server/server/modules/database/repositories/sessions.db.js.map +1 -1
  88. package/dist-server/server/modules/database/repositories/user-preferences.db.js +65 -0
  89. package/dist-server/server/modules/database/repositories/user-preferences.db.js.map +1 -0
  90. package/dist-server/server/modules/database/schema.js +99 -0
  91. package/dist-server/server/modules/database/schema.js.map +1 -1
  92. package/dist-server/server/modules/database/tests/provider-models.db.integration.test.js +99 -0
  93. package/dist-server/server/modules/database/tests/provider-models.db.integration.test.js.map +1 -1
  94. package/dist-server/server/modules/database/tests/sessions.db.integration.test.js +39 -0
  95. package/dist-server/server/modules/database/tests/sessions.db.integration.test.js.map +1 -1
  96. package/dist-server/server/modules/database/tests/user-preferences.db.integration.test.js +105 -0
  97. package/dist-server/server/modules/database/tests/user-preferences.db.integration.test.js.map +1 -0
  98. package/dist-server/server/modules/file-tree/file-tree.module.js +2 -1
  99. package/dist-server/server/modules/file-tree/file-tree.module.js.map +1 -1
  100. package/dist-server/server/modules/file-tree/file-tree.service.js +44 -7
  101. package/dist-server/server/modules/file-tree/file-tree.service.js.map +1 -1
  102. package/dist-server/server/modules/file-tree/tests/file-tree.service.test.js +126 -1
  103. package/dist-server/server/modules/file-tree/tests/file-tree.service.test.js.map +1 -1
  104. package/dist-server/server/modules/git/git-branch-diff.service.js +142 -0
  105. package/dist-server/server/modules/git/git-branch-diff.service.js.map +1 -0
  106. package/dist-server/server/modules/git/git-branch.service.js.map +1 -1
  107. package/dist-server/server/modules/git/git.routes.js +92 -5
  108. package/dist-server/server/modules/git/git.routes.js.map +1 -1
  109. package/dist-server/server/modules/git/tests/git-branch-diff.service.test.js +160 -0
  110. package/dist-server/server/modules/git/tests/git-branch-diff.service.test.js.map +1 -0
  111. package/dist-server/server/modules/projects/project-clone.routes.js +108 -0
  112. package/dist-server/server/modules/projects/project-clone.routes.js.map +1 -0
  113. package/dist-server/server/modules/projects/projects.routes.js +13 -66
  114. package/dist-server/server/modules/projects/projects.routes.js.map +1 -1
  115. package/dist-server/server/modules/projects/services/project-clone-request.service.js +36 -0
  116. package/dist-server/server/modules/projects/services/project-clone-request.service.js.map +1 -0
  117. package/dist-server/server/modules/projects/services/project-clone.service.js +86 -32
  118. package/dist-server/server/modules/projects/services/project-clone.service.js.map +1 -1
  119. package/dist-server/server/modules/projects/services/project-delete.service.js +12 -1
  120. package/dist-server/server/modules/projects/services/project-delete.service.js.map +1 -1
  121. package/dist-server/server/modules/projects/services/project-star.service.js +2 -2
  122. package/dist-server/server/modules/projects/services/project-star.service.js.map +1 -1
  123. package/dist-server/server/modules/projects/tests/project-clone.routes.test.js +191 -0
  124. package/dist-server/server/modules/projects/tests/project-clone.routes.test.js.map +1 -0
  125. package/dist-server/server/modules/projects/tests/project-clone.service.test.js +275 -0
  126. package/dist-server/server/modules/projects/tests/project-clone.service.test.js.map +1 -1
  127. package/dist-server/server/modules/providers/index.js +3 -0
  128. package/dist-server/server/modules/providers/index.js.map +1 -1
  129. package/dist-server/server/modules/providers/list/claude/claude-auth.provider.js +62 -9
  130. package/dist-server/server/modules/providers/list/claude/claude-auth.provider.js.map +1 -1
  131. package/dist-server/server/modules/providers/list/claude/claude-fork.provider.js +46 -0
  132. package/dist-server/server/modules/providers/list/claude/claude-fork.provider.js.map +1 -0
  133. package/dist-server/server/modules/providers/list/claude/claude-models.provider.js +31 -12
  134. package/dist-server/server/modules/providers/list/claude/claude-models.provider.js.map +1 -1
  135. package/dist-server/server/modules/providers/list/claude/claude-runtime.provider.js +472 -53
  136. package/dist-server/server/modules/providers/list/claude/claude-runtime.provider.js.map +1 -1
  137. package/dist-server/server/modules/providers/list/claude/claude-session-synchronizer.provider.js +38 -11
  138. package/dist-server/server/modules/providers/list/claude/claude-session-synchronizer.provider.js.map +1 -1
  139. package/dist-server/server/modules/providers/list/claude/claude-sessions.provider.js +1007 -60
  140. package/dist-server/server/modules/providers/list/claude/claude-sessions.provider.js.map +1 -1
  141. package/dist-server/server/modules/providers/list/claude/claude-skills.provider.js +4 -1
  142. package/dist-server/server/modules/providers/list/claude/claude-skills.provider.js.map +1 -1
  143. package/dist-server/server/modules/providers/list/claude/claude.provider.js +2 -0
  144. package/dist-server/server/modules/providers/list/claude/claude.provider.js.map +1 -1
  145. package/dist-server/server/modules/providers/list/codex/codex-app-server.client.js +204 -0
  146. package/dist-server/server/modules/providers/list/codex/codex-app-server.client.js.map +1 -0
  147. package/dist-server/server/modules/providers/list/codex/codex-fork.provider.js +32 -0
  148. package/dist-server/server/modules/providers/list/codex/codex-fork.provider.js.map +1 -0
  149. package/dist-server/server/modules/providers/list/codex/codex-models.provider.js +47 -0
  150. package/dist-server/server/modules/providers/list/codex/codex-models.provider.js.map +1 -1
  151. package/dist-server/server/modules/providers/list/codex/codex-runtime.provider.js +71 -53
  152. package/dist-server/server/modules/providers/list/codex/codex-runtime.provider.js.map +1 -1
  153. package/dist-server/server/modules/providers/list/codex/codex-session-synchronizer.provider.js +8 -0
  154. package/dist-server/server/modules/providers/list/codex/codex-session-synchronizer.provider.js.map +1 -1
  155. package/dist-server/server/modules/providers/list/codex/codex-sessions.provider.js +1621 -431
  156. package/dist-server/server/modules/providers/list/codex/codex-sessions.provider.js.map +1 -1
  157. package/dist-server/server/modules/providers/list/codex/codex.provider.js +2 -0
  158. package/dist-server/server/modules/providers/list/codex/codex.provider.js.map +1 -1
  159. package/dist-server/server/modules/providers/list/cursor/cursor-auth.provider.js +9 -2
  160. package/dist-server/server/modules/providers/list/cursor/cursor-auth.provider.js.map +1 -1
  161. package/dist-server/server/modules/providers/list/cursor/cursor-runtime.provider.js +16 -8
  162. package/dist-server/server/modules/providers/list/cursor/cursor-runtime.provider.js.map +1 -1
  163. package/dist-server/server/modules/providers/list/cursor/cursor-session-synchronizer.provider.js.map +1 -1
  164. package/dist-server/server/modules/providers/list/opencode/opencode-models.provider.js +274 -9
  165. package/dist-server/server/modules/providers/list/opencode/opencode-models.provider.js.map +1 -1
  166. package/dist-server/server/modules/providers/list/opencode/opencode-runtime.provider.js +5 -2
  167. package/dist-server/server/modules/providers/list/opencode/opencode-runtime.provider.js.map +1 -1
  168. package/dist-server/server/modules/providers/list/opencode/opencode-session-synchronizer.provider.js +19 -2
  169. package/dist-server/server/modules/providers/list/opencode/opencode-session-synchronizer.provider.js.map +1 -1
  170. package/dist-server/server/modules/providers/provider.routes.js +134 -2
  171. package/dist-server/server/modules/providers/provider.routes.js.map +1 -1
  172. package/dist-server/server/modules/providers/services/claude-cli-liveness.service.js +100 -0
  173. package/dist-server/server/modules/providers/services/claude-cli-liveness.service.js.map +1 -0
  174. package/dist-server/server/modules/providers/services/claude-settings-permissions.service.js +97 -0
  175. package/dist-server/server/modules/providers/services/claude-settings-permissions.service.js.map +1 -0
  176. package/dist-server/server/modules/providers/services/provider-capabilities.service.js +23 -1
  177. package/dist-server/server/modules/providers/services/provider-capabilities.service.js.map +1 -1
  178. package/dist-server/server/modules/providers/services/provider-models.service.js +59 -5
  179. package/dist-server/server/modules/providers/services/provider-models.service.js.map +1 -1
  180. package/dist-server/server/modules/providers/services/provider-runtime.service.js +8 -0
  181. package/dist-server/server/modules/providers/services/provider-runtime.service.js.map +1 -1
  182. package/dist-server/server/modules/providers/services/provider-token-usage.service.js +139 -35
  183. package/dist-server/server/modules/providers/services/provider-token-usage.service.js.map +1 -1
  184. package/dist-server/server/modules/providers/services/session-conversations-search.service.js +2 -4
  185. package/dist-server/server/modules/providers/services/session-conversations-search.service.js.map +1 -1
  186. package/dist-server/server/modules/providers/services/session-history-cache.service.js +91 -0
  187. package/dist-server/server/modules/providers/services/session-history-cache.service.js.map +1 -0
  188. package/dist-server/server/modules/providers/services/session-synchronizer.service.js +105 -32
  189. package/dist-server/server/modules/providers/services/session-synchronizer.service.js.map +1 -1
  190. package/dist-server/server/modules/providers/services/sessions-watcher.service.js +3 -60
  191. package/dist-server/server/modules/providers/services/sessions-watcher.service.js.map +1 -1
  192. package/dist-server/server/modules/providers/services/sessions.service.js +252 -12
  193. package/dist-server/server/modules/providers/services/sessions.service.js.map +1 -1
  194. package/dist-server/server/modules/providers/tests/claude-auth.test.js +183 -0
  195. package/dist-server/server/modules/providers/tests/claude-auth.test.js.map +1 -1
  196. package/dist-server/server/modules/providers/tests/claude-background-work.test.js +172 -0
  197. package/dist-server/server/modules/providers/tests/claude-background-work.test.js.map +1 -0
  198. package/dist-server/server/modules/providers/tests/claude-compaction.test.js +84 -0
  199. package/dist-server/server/modules/providers/tests/claude-compaction.test.js.map +1 -0
  200. package/dist-server/server/modules/providers/tests/claude-message-model.test.js +123 -0
  201. package/dist-server/server/modules/providers/tests/claude-message-model.test.js.map +1 -0
  202. package/dist-server/server/modules/providers/tests/claude-models.test.js +39 -0
  203. package/dist-server/server/modules/providers/tests/claude-models.test.js.map +1 -0
  204. package/dist-server/server/modules/providers/tests/claude-runtime-hold.test.js +265 -0
  205. package/dist-server/server/modules/providers/tests/claude-runtime-hold.test.js.map +1 -0
  206. package/dist-server/server/modules/providers/tests/claude-sessions.test.js +1815 -33
  207. package/dist-server/server/modules/providers/tests/claude-sessions.test.js.map +1 -1
  208. package/dist-server/server/modules/providers/tests/claude-settings-permissions.test.js +228 -0
  209. package/dist-server/server/modules/providers/tests/claude-settings-permissions.test.js.map +1 -0
  210. package/dist-server/server/modules/providers/tests/claude-subagent-echo.test.js +16 -0
  211. package/dist-server/server/modules/providers/tests/claude-subagent-echo.test.js.map +1 -0
  212. package/dist-server/server/modules/providers/tests/claude-token-budget.test.js +85 -0
  213. package/dist-server/server/modules/providers/tests/claude-token-budget.test.js.map +1 -0
  214. package/dist-server/server/modules/providers/tests/codex-message-editing.test.js +338 -0
  215. package/dist-server/server/modules/providers/tests/codex-message-editing.test.js.map +1 -0
  216. package/dist-server/server/modules/providers/tests/codex-models.test.js +25 -0
  217. package/dist-server/server/modules/providers/tests/codex-models.test.js.map +1 -0
  218. package/dist-server/server/modules/providers/tests/codex-runtime.test.js +83 -0
  219. package/dist-server/server/modules/providers/tests/codex-runtime.test.js.map +1 -0
  220. package/dist-server/server/modules/providers/tests/codex-session-fork.test.js +54 -0
  221. package/dist-server/server/modules/providers/tests/codex-session-fork.test.js.map +1 -0
  222. package/dist-server/server/modules/providers/tests/codex-sessions.test.js +354 -15
  223. package/dist-server/server/modules/providers/tests/codex-sessions.test.js.map +1 -1
  224. package/dist-server/server/modules/providers/tests/cursor-auth.test.js +167 -0
  225. package/dist-server/server/modules/providers/tests/cursor-auth.test.js.map +1 -0
  226. package/dist-server/server/modules/providers/tests/opencode-models.test.js +196 -4
  227. package/dist-server/server/modules/providers/tests/opencode-models.test.js.map +1 -1
  228. package/dist-server/server/modules/providers/tests/opencode-sessions.test.js +19 -1
  229. package/dist-server/server/modules/providers/tests/opencode-sessions.test.js.map +1 -1
  230. package/dist-server/server/modules/providers/tests/provider-capabilities.service.test.js +30 -0
  231. package/dist-server/server/modules/providers/tests/provider-capabilities.service.test.js.map +1 -0
  232. package/dist-server/server/modules/providers/tests/provider-models.service.test.js +79 -1
  233. package/dist-server/server/modules/providers/tests/provider-models.service.test.js.map +1 -1
  234. package/dist-server/server/modules/providers/tests/provider-token-usage.service.test.js +164 -1
  235. package/dist-server/server/modules/providers/tests/provider-token-usage.service.test.js.map +1 -1
  236. package/dist-server/server/modules/providers/tests/provider.routes.test.js +190 -1
  237. package/dist-server/server/modules/providers/tests/provider.routes.test.js.map +1 -1
  238. package/dist-server/server/modules/providers/tests/session-fork.test.js +113 -0
  239. package/dist-server/server/modules/providers/tests/session-fork.test.js.map +1 -0
  240. package/dist-server/server/modules/providers/tests/session-history-cache.service.test.js +139 -0
  241. package/dist-server/server/modules/providers/tests/session-history-cache.service.test.js.map +1 -0
  242. package/dist-server/server/modules/providers/tests/session-orphan-prune.test.js +90 -0
  243. package/dist-server/server/modules/providers/tests/session-orphan-prune.test.js.map +1 -0
  244. package/dist-server/server/modules/providers/tests/session-synchronizer-coalescing.test.js +122 -0
  245. package/dist-server/server/modules/providers/tests/session-synchronizer-coalescing.test.js.map +1 -0
  246. package/dist-server/server/modules/providers/tests/sessions.service.test.js +219 -1
  247. package/dist-server/server/modules/providers/tests/sessions.service.test.js.map +1 -1
  248. package/dist-server/server/modules/providers/tests/skills.test.js +15 -3
  249. package/dist-server/server/modules/providers/tests/skills.test.js.map +1 -1
  250. package/dist-server/server/modules/scheduled-messages/index.js +5 -0
  251. package/dist-server/server/modules/scheduled-messages/index.js.map +1 -0
  252. package/dist-server/server/modules/scheduled-messages/scheduled-messages.routes.js +47 -0
  253. package/dist-server/server/modules/scheduled-messages/scheduled-messages.routes.js.map +1 -0
  254. package/dist-server/server/modules/scheduled-messages/services/scheduled-message-dispatcher.service.js +175 -0
  255. package/dist-server/server/modules/scheduled-messages/services/scheduled-message-dispatcher.service.js.map +1 -0
  256. package/dist-server/server/modules/scheduled-messages/services/scheduled-messages.service.js +95 -0
  257. package/dist-server/server/modules/scheduled-messages/services/scheduled-messages.service.js.map +1 -0
  258. package/dist-server/server/modules/scheduled-messages/tests/scheduled-messages.test.js +290 -0
  259. package/dist-server/server/modules/scheduled-messages/tests/scheduled-messages.test.js.map +1 -0
  260. package/dist-server/server/modules/taskmaster/taskmaster.routes.js +23 -23
  261. package/dist-server/server/modules/taskmaster/taskmaster.routes.js.map +1 -1
  262. package/dist-server/server/modules/taskmaster/tests/taskmaster.routes.test.js +48 -0
  263. package/dist-server/server/modules/taskmaster/tests/taskmaster.routes.test.js.map +1 -1
  264. package/dist-server/server/modules/user/tests/user.service.test.js +70 -0
  265. package/dist-server/server/modules/user/tests/user.service.test.js.map +1 -1
  266. package/dist-server/server/modules/user/user.module.js +10 -1
  267. package/dist-server/server/modules/user/user.module.js.map +1 -1
  268. package/dist-server/server/modules/user/user.routes.js +44 -0
  269. package/dist-server/server/modules/user/user.routes.js.map +1 -1
  270. package/dist-server/server/modules/user/user.service.js +75 -0
  271. package/dist-server/server/modules/user/user.service.js.map +1 -1
  272. package/dist-server/server/modules/voice/tests/voice.service.test.js +3 -2
  273. package/dist-server/server/modules/voice/tests/voice.service.test.js.map +1 -1
  274. package/dist-server/server/modules/websocket/index.js +7 -0
  275. package/dist-server/server/modules/websocket/index.js.map +1 -1
  276. package/dist-server/server/modules/websocket/services/chat-run-registry.service.js +37 -49
  277. package/dist-server/server/modules/websocket/services/chat-run-registry.service.js.map +1 -1
  278. package/dist-server/server/modules/websocket/services/chat-session-writer.service.js +35 -5
  279. package/dist-server/server/modules/websocket/services/chat-session-writer.service.js.map +1 -1
  280. package/dist-server/server/modules/websocket/services/chat-websocket.service.js +204 -10
  281. package/dist-server/server/modules/websocket/services/chat-websocket.service.js.map +1 -1
  282. package/dist-server/server/modules/websocket/services/session-upsert-broadcast.service.js +94 -0
  283. package/dist-server/server/modules/websocket/services/session-upsert-broadcast.service.js.map +1 -0
  284. package/dist-server/server/modules/websocket/services/shell-websocket.service.js +236 -20
  285. package/dist-server/server/modules/websocket/services/shell-websocket.service.js.map +1 -1
  286. package/dist-server/server/modules/websocket/tests/chat-background-subscribe.test.js +137 -0
  287. package/dist-server/server/modules/websocket/tests/chat-background-subscribe.test.js.map +1 -0
  288. package/dist-server/server/modules/websocket/tests/chat-edit-send.test.js +263 -0
  289. package/dist-server/server/modules/websocket/tests/chat-edit-send.test.js.map +1 -0
  290. package/dist-server/server/modules/websocket/tests/chat-run-registry.test.js +29 -3
  291. package/dist-server/server/modules/websocket/tests/chat-run-registry.test.js.map +1 -1
  292. package/dist-server/server/modules/websocket/tests/chat-stop-task.test.js +96 -0
  293. package/dist-server/server/modules/websocket/tests/chat-stop-task.test.js.map +1 -0
  294. package/dist-server/server/modules/websocket/tests/session-upsert-broadcast.test.js +106 -0
  295. package/dist-server/server/modules/websocket/tests/session-upsert-broadcast.test.js.map +1 -0
  296. package/dist-server/server/modules/websocket/tests/shell-websocket.service.test.js +500 -0
  297. package/dist-server/server/modules/websocket/tests/shell-websocket.service.test.js.map +1 -1
  298. package/dist-server/server/shared/claude-cli-path.js +22 -5
  299. package/dist-server/server/shared/claude-cli-path.js.map +1 -1
  300. package/dist-server/server/shared/index.js +3 -0
  301. package/dist-server/server/shared/index.js.map +1 -0
  302. package/dist-server/server/shared/message-unification.js +376 -0
  303. package/dist-server/server/shared/message-unification.js.map +1 -0
  304. package/dist-server/server/shared/tests/claude-cli-path.test.js +45 -1
  305. package/dist-server/server/shared/tests/claude-cli-path.test.js.map +1 -1
  306. package/dist-server/server/shared/tests/message-unification.test.js +174 -0
  307. package/dist-server/server/shared/tests/message-unification.test.js.map +1 -0
  308. package/dist-server/server/shared/tests/read-only-roots.test.js +124 -0
  309. package/dist-server/server/shared/tests/read-only-roots.test.js.map +1 -0
  310. package/dist-server/server/shared/tests/strip-ansi-sequences.test.js +31 -0
  311. package/dist-server/server/shared/tests/strip-ansi-sequences.test.js.map +1 -0
  312. package/dist-server/server/shared/utils.js +160 -0
  313. package/dist-server/server/shared/utils.js.map +1 -1
  314. package/electron/desktopWindow.js +6 -0
  315. package/electron/viewHost.js +25 -2
  316. package/package.json +20 -17
  317. package/server/index.ts +16 -5
  318. package/server/modules/agent/agent.module.ts +11 -1
  319. package/server/modules/agent/agent.routes.ts +200 -142
  320. package/server/modules/agent/tests/agent-run-registration.test.ts +348 -0
  321. package/server/modules/agent/tests/agent.routes.test.ts +51 -2
  322. package/server/modules/browser-use/browser-use.routes.ts +96 -33
  323. package/server/modules/database/connection.ts +2 -1
  324. package/server/modules/database/index.ts +10 -0
  325. package/server/modules/database/migrations.ts +51 -0
  326. package/server/modules/database/repositories/provider-models.ts +79 -6
  327. package/server/modules/database/repositories/scheduled-messages.db.ts +127 -0
  328. package/server/modules/database/repositories/session-drafts.db.ts +171 -0
  329. package/server/modules/database/repositories/sessions.db.ts +204 -3
  330. package/server/modules/database/repositories/user-preferences.db.ts +80 -0
  331. package/server/modules/database/schema.ts +103 -0
  332. package/server/modules/database/tests/provider-models.db.integration.test.ts +113 -0
  333. package/server/modules/database/tests/sessions.db.integration.test.ts +52 -0
  334. package/server/modules/database/tests/user-preferences.db.integration.test.ts +135 -0
  335. package/server/modules/file-tree/file-tree.module.ts +2 -1
  336. package/server/modules/file-tree/file-tree.service.ts +49 -7
  337. package/server/modules/file-tree/tests/file-tree.service.test.ts +163 -1
  338. package/server/modules/git/git-branch-diff.service.ts +204 -0
  339. package/server/modules/git/git-branch.service.ts +2 -12
  340. package/server/modules/git/git.routes.ts +100 -6
  341. package/server/modules/git/tests/git-branch-diff.service.test.ts +210 -0
  342. package/server/modules/projects/project-clone.routes.ts +142 -0
  343. package/server/modules/projects/projects.routes.ts +16 -81
  344. package/server/modules/projects/services/project-clone-request.service.ts +56 -0
  345. package/server/modules/projects/services/project-clone.service.ts +102 -34
  346. package/server/modules/projects/services/project-delete.service.ts +13 -1
  347. package/server/modules/projects/services/project-star.service.ts +2 -2
  348. package/server/modules/projects/tests/project-clone.routes.test.ts +246 -0
  349. package/server/modules/projects/tests/project-clone.service.test.ts +376 -0
  350. package/server/modules/providers/index.ts +4 -0
  351. package/server/modules/providers/list/claude/claude-auth.provider.ts +81 -11
  352. package/server/modules/providers/list/claude/claude-fork.provider.ts +57 -0
  353. package/server/modules/providers/list/claude/claude-models.provider.ts +37 -17
  354. package/server/modules/providers/list/claude/claude-runtime.provider.js +510 -54
  355. package/server/modules/providers/list/claude/claude-session-synchronizer.provider.ts +40 -14
  356. package/server/modules/providers/list/claude/claude-sessions.provider.ts +1195 -63
  357. package/server/modules/providers/list/claude/claude-skills.provider.ts +4 -1
  358. package/server/modules/providers/list/claude/claude.provider.ts +3 -0
  359. package/server/modules/providers/list/codex/codex-app-server.client.ts +243 -0
  360. package/server/modules/providers/list/codex/codex-fork.provider.ts +40 -0
  361. package/server/modules/providers/list/codex/codex-models.provider.ts +47 -0
  362. package/server/modules/providers/list/codex/{codex-runtime.provider.js → codex-runtime.provider.ts} +105 -70
  363. package/server/modules/providers/list/codex/codex-session-synchronizer.provider.ts +9 -0
  364. package/server/modules/providers/list/codex/codex-sessions.provider.ts +1847 -455
  365. package/server/modules/providers/list/codex/codex.provider.ts +3 -0
  366. package/server/modules/providers/list/cursor/cursor-auth.provider.ts +10 -2
  367. package/server/modules/providers/list/cursor/cursor-runtime.provider.js +17 -8
  368. package/server/modules/providers/list/cursor/cursor-session-synchronizer.provider.ts +0 -1
  369. package/server/modules/providers/list/opencode/opencode-models.provider.ts +297 -9
  370. package/server/modules/providers/list/opencode/opencode-runtime.provider.js +5 -2
  371. package/server/modules/providers/list/opencode/opencode-session-synchronizer.provider.ts +30 -2
  372. package/server/modules/providers/provider.routes.ts +162 -2
  373. package/server/modules/providers/services/claude-cli-liveness.service.ts +121 -0
  374. package/server/modules/providers/services/claude-settings-permissions.service.ts +127 -0
  375. package/server/modules/providers/services/provider-capabilities.service.ts +40 -1
  376. package/server/modules/providers/services/provider-models.service.ts +71 -4
  377. package/server/modules/providers/services/provider-runtime.service.ts +11 -0
  378. package/server/modules/providers/services/provider-token-usage.service.ts +166 -39
  379. package/server/modules/providers/services/session-conversations-search.service.ts +2 -5
  380. package/server/modules/providers/services/session-history-cache.service.ts +150 -0
  381. package/server/modules/providers/services/session-synchronizer.service.ts +125 -38
  382. package/server/modules/providers/services/sessions-watcher.service.ts +3 -64
  383. package/server/modules/providers/services/sessions.service.ts +309 -17
  384. package/server/modules/providers/tests/claude-auth.test.ts +208 -0
  385. package/server/modules/providers/tests/claude-background-work.test.ts +213 -0
  386. package/server/modules/providers/tests/claude-compaction.test.ts +101 -0
  387. package/server/modules/providers/tests/claude-message-model.test.ts +160 -0
  388. package/server/modules/providers/tests/claude-models.test.ts +87 -0
  389. package/server/modules/providers/tests/claude-runtime-hold.test.ts +309 -0
  390. package/server/modules/providers/tests/claude-sessions.test.ts +2167 -37
  391. package/server/modules/providers/tests/claude-settings-permissions.test.ts +271 -0
  392. package/server/modules/providers/tests/claude-subagent-echo.test.ts +25 -0
  393. package/server/modules/providers/tests/claude-token-budget.test.ts +104 -0
  394. package/server/modules/providers/tests/codex-message-editing.test.ts +419 -0
  395. package/server/modules/providers/tests/codex-models.test.ts +37 -0
  396. package/server/modules/providers/tests/codex-runtime.test.ts +94 -0
  397. package/server/modules/providers/tests/codex-session-fork.test.ts +59 -0
  398. package/server/modules/providers/tests/codex-sessions.test.ts +396 -15
  399. package/server/modules/providers/tests/cursor-auth.test.ts +187 -0
  400. package/server/modules/providers/tests/opencode-models.test.ts +266 -8
  401. package/server/modules/providers/tests/opencode-sessions.test.ts +40 -1
  402. package/server/modules/providers/tests/provider-capabilities.service.test.ts +37 -0
  403. package/server/modules/providers/tests/provider-models.service.test.ts +99 -1
  404. package/server/modules/providers/tests/provider-token-usage.service.test.ts +188 -1
  405. package/server/modules/providers/tests/provider.routes.test.ts +230 -1
  406. package/server/modules/providers/tests/session-fork.test.ts +173 -0
  407. package/server/modules/providers/tests/session-history-cache.service.test.ts +165 -0
  408. package/server/modules/providers/tests/session-orphan-prune.test.ts +107 -0
  409. package/server/modules/providers/tests/session-synchronizer-coalescing.test.ts +136 -0
  410. package/server/modules/providers/tests/sessions.service.test.ts +303 -1
  411. package/server/modules/providers/tests/skills.test.ts +21 -5
  412. package/server/modules/scheduled-messages/index.ts +8 -0
  413. package/server/modules/scheduled-messages/scheduled-messages.routes.ts +68 -0
  414. package/server/modules/scheduled-messages/services/scheduled-message-dispatcher.service.ts +216 -0
  415. package/server/modules/scheduled-messages/services/scheduled-messages.service.ts +121 -0
  416. package/server/modules/scheduled-messages/tests/scheduled-messages.test.ts +354 -0
  417. package/server/modules/taskmaster/taskmaster.routes.ts +23 -33
  418. package/server/modules/taskmaster/tests/taskmaster.routes.test.ts +57 -0
  419. package/server/modules/user/tests/user.service.test.ts +90 -0
  420. package/server/modules/user/user.module.ts +10 -1
  421. package/server/modules/user/user.routes.ts +44 -0
  422. package/server/modules/user/user.service.ts +105 -0
  423. package/server/modules/voice/tests/voice.service.test.ts +3 -2
  424. package/server/modules/websocket/README.md +4 -2
  425. package/server/modules/websocket/index.ts +8 -0
  426. package/server/modules/websocket/services/chat-run-registry.service.ts +45 -54
  427. package/server/modules/websocket/services/chat-session-writer.service.ts +36 -6
  428. package/server/modules/websocket/services/chat-websocket.service.ts +302 -16
  429. package/server/modules/websocket/services/session-upsert-broadcast.service.ts +108 -0
  430. package/server/modules/websocket/services/shell-websocket.service.ts +276 -22
  431. package/server/modules/websocket/tests/chat-background-subscribe.test.ts +167 -0
  432. package/server/modules/websocket/tests/chat-edit-send.test.ts +309 -0
  433. package/server/modules/websocket/tests/chat-run-registry.test.ts +34 -3
  434. package/server/modules/websocket/tests/chat-stop-task.test.ts +127 -0
  435. package/server/modules/websocket/tests/session-upsert-broadcast.test.ts +138 -0
  436. package/server/modules/websocket/tests/shell-websocket.service.test.ts +629 -0
  437. package/server/shared/claude-cli-path.ts +24 -6
  438. package/server/shared/index.ts +3 -0
  439. package/server/shared/interfaces.ts +91 -0
  440. package/server/shared/message-unification.ts +449 -0
  441. package/server/shared/tests/claude-cli-path.test.ts +58 -1
  442. package/server/shared/tests/message-unification.test.ts +218 -0
  443. package/server/shared/tests/read-only-roots.test.ts +139 -0
  444. package/server/shared/tests/strip-ansi-sequences.test.ts +42 -0
  445. package/server/shared/types.ts +382 -7
  446. package/server/shared/utils.ts +176 -0
  447. package/dist/assets/channel-_falYbU1.js +0 -1
  448. package/dist/assets/classDiagram-JCYQIIEL-Dn-3CfpR.js +0 -1
  449. package/dist/assets/classDiagram-v2-OCEON4UE-Dn-3CfpR.js +0 -1
  450. package/dist/assets/index-CzPLBvXT.css +0 -32
  451. package/dist/assets/index-DFkBBuSA.js +0 -1549
  452. package/dist/assets/stateDiagram-v2-UEYNNEHI-BvBxUUrZ.js +0 -1
  453. package/dist/assets/swimlanesDiagram-ULZ7WXOC-CsE-x4YK.js +0 -8
@@ -1,25 +1,75 @@
1
1
  import assert from 'node:assert/strict';
2
+ import { mkdir, mkdtemp, rm, writeFile } from 'node:fs/promises';
3
+ import os from 'node:os';
4
+ import path from 'node:path';
2
5
  import test from 'node:test';
3
6
 
7
+ import Database from 'better-sqlite3';
8
+
9
+ import { closeConnection, initializeDatabase } from '@/modules/database/index.js';
4
10
  import {
5
11
  OpenCodeProviderModels,
6
12
  OPENCODE_PREDEFINED_MODELS,
7
13
  } from '@/modules/providers/list/opencode/opencode-models.provider.js';
8
14
 
9
- test('OpenCode exposes only the curated predefined catalog', async () => {
10
- const adapter = new OpenCodeProviderModels();
15
+ const OPENCODE_ENV_KEYS = ['OPENCODE_API_KEY', 'ANTHROPIC_API_KEY', 'OPENAI_API_KEY'];
11
16
 
12
- assert.deepEqual(await adapter.getSupportedModels(), OPENCODE_PREDEFINED_MODELS);
13
- assert.equal(
14
- (await adapter.getCurrentActiveModel()).model,
15
- OPENCODE_PREDEFINED_MODELS.DEFAULT,
16
- );
17
+ /**
18
+ * Runs one case against a throwaway OpenCode home, so the catalog the adapter
19
+ * reports depends on the fixture rather than on the providers the machine
20
+ * running the suite happens to be logged into.
21
+ */
22
+ const withOpenCodeHome = async (
23
+ setUp: (homeDir: string) => Promise<void>,
24
+ runTest: (adapter: OpenCodeProviderModels) => Promise<void>,
25
+ ): Promise<void> => {
26
+ const homeDir = await mkdtemp(path.join(os.tmpdir(), 'opencode-catalog-'));
27
+ const originalHomedir = os.homedir;
28
+ const originalEnv = OPENCODE_ENV_KEYS.map((key) => [key, process.env[key]] as const);
29
+
30
+ (os as any).homedir = () => homeDir;
31
+ for (const key of OPENCODE_ENV_KEYS) {
32
+ delete process.env[key];
33
+ }
34
+
35
+ try {
36
+ await setUp(homeDir);
37
+ await runTest(new OpenCodeProviderModels());
38
+ } finally {
39
+ (os as any).homedir = originalHomedir;
40
+ for (const [key, value] of originalEnv) {
41
+ if (value === undefined) {
42
+ delete process.env[key];
43
+ } else {
44
+ process.env[key] = value;
45
+ }
46
+ }
47
+ await rm(homeDir, { recursive: true, force: true });
48
+ }
49
+ };
50
+
51
+ const writeOpenCodeAuth = async (homeDir: string, auth: Record<string, unknown>): Promise<void> => {
52
+ const authDir = path.join(homeDir, '.local', 'share', 'opencode');
53
+ await mkdir(authDir, { recursive: true });
54
+ await writeFile(path.join(authDir, 'auth.json'), JSON.stringify(auth), 'utf8');
55
+ };
56
+
57
+ test('OpenCode exposes only the curated predefined catalog', async () => {
58
+ await withOpenCodeHome(async () => {}, async (adapter) => {
59
+ // Nothing readable about this install, so the picker keeps every option
60
+ // rather than coming up empty.
61
+ assert.deepEqual(await adapter.getSupportedModels(), OPENCODE_PREDEFINED_MODELS);
62
+ assert.equal(
63
+ (await adapter.getCurrentActiveModel()).model,
64
+ OPENCODE_PREDEFINED_MODELS.DEFAULT,
65
+ );
66
+ });
17
67
  // OpenCode routes by `<providerID>/<modelID>`, so every option has to carry a
18
68
  // provider prefix that `opencode models --verbose` reports.
19
69
  const providerIds = new Set(
20
70
  OPENCODE_PREDEFINED_MODELS.OPTIONS.map((option) => option.value.split('/')[0]),
21
71
  );
22
- assert.deepEqual([...providerIds].sort(), ['anthropic', 'opencode', 'openai'].sort());
72
+ assert.deepEqual([...providerIds].sort(), ['anthropic', 'opencode', 'opencode-go', 'openai'].sort());
23
73
  assert.equal(
24
74
  OPENCODE_PREDEFINED_MODELS.OPTIONS.every((option) => /^[a-z0-9-]+\/.+/.test(option.value)),
25
75
  true,
@@ -38,4 +88,212 @@ test('OpenCode exposes only the curated predefined catalog', async () => {
38
88
  assert.ok(
39
89
  OPENCODE_PREDEFINED_MODELS.OPTIONS.some((option) => option.value === 'openai/gpt-5.6'),
40
90
  );
91
+ // The Go gateway carries its own provider id, so its models must be curated
92
+ // too - a Go subscriber otherwise authenticates while the picker offers
93
+ // nothing they can run.
94
+ const opencodeGoOptions = OPENCODE_PREDEFINED_MODELS.OPTIONS.filter(
95
+ (option) => option.value.startsWith('opencode-go/'),
96
+ );
97
+ assert.equal(opencodeGoOptions.length, 27);
98
+ assert.ok(opencodeGoOptions.every((option) => option.description === 'OpenCode Go'));
99
+ const glmFlash = opencodeGoOptions.find(
100
+ (option) => option.value === 'opencode-go/glm-5.3-flash',
101
+ );
102
+ assert.ok(glmFlash);
103
+ // Effort choices come from `opencode models --verbose` variants; the runtime
104
+ // turns a selected value into `--variant`, so the values have to match the
105
+ // CLI's exactly.
106
+ assert.deepEqual(
107
+ glmFlash?.effort?.values.map((value) => value.value),
108
+ ['low', 'high', 'max'],
109
+ );
110
+ assert.ok(
111
+ opencodeGoOptions
112
+ .filter((option) => !option.effort)
113
+ .every((option) =>
114
+ ['glm-5.1', 'kimi-k2.6', 'kimi-k2.7-code', 'mimo-v2.5', 'mimo-v2.5-pro',
115
+ 'minimax-m2.7', 'qwen3.6-plus', 'qwen3.7-max', 'qwen3.7-plus']
116
+ .includes(option.value.slice('opencode-go/'.length)),
117
+ ),
118
+ );
119
+ });
120
+
121
+ test('OpenCode offers only models the install can route to', async () => {
122
+ // Asking for a provider the user never connected fails the whole run with
123
+ // "Model <id> is not valid", so an OpenCode Zen model must not be offered -
124
+ // or defaulted to - on a machine that only holds an Anthropic key.
125
+ await withOpenCodeHome(
126
+ (homeDir) => writeOpenCodeAuth(homeDir, { anthropic: { type: 'api', key: 'test' } }),
127
+ async (adapter) => {
128
+ const catalog = await adapter.getSupportedModels();
129
+ const providerIds = new Set(catalog.OPTIONS.map((option) => option.value.split('/')[0]));
130
+
131
+ assert.deepEqual([...providerIds], ['anthropic']);
132
+ assert.ok(catalog.OPTIONS.length > 0);
133
+ assert.equal(catalog.DEFAULT.startsWith('anthropic/'), true);
134
+ assert.ok(catalog.OPTIONS.some((option) => option.value === catalog.DEFAULT));
135
+ assert.equal((await adapter.getCurrentActiveModel()).model, catalog.DEFAULT);
136
+ },
137
+ );
138
+
139
+ // A Go subscriber's auth store holds only `opencode-go`, so the whole catalog
140
+ // has to resolve to Go models and the default has to move onto one of them
141
+ // instead of the unreachable Zen default.
142
+ await withOpenCodeHome(
143
+ (homeDir) => writeOpenCodeAuth(homeDir, { 'opencode-go': { type: 'api', key: 'test' } }),
144
+ async (adapter) => {
145
+ const catalog = await adapter.getSupportedModels();
146
+ const providerIds = new Set(catalog.OPTIONS.map((option) => option.value.split('/')[0]));
147
+
148
+ assert.deepEqual([...providerIds], ['opencode-go']);
149
+ assert.equal(catalog.OPTIONS.length, 27);
150
+ assert.equal(catalog.DEFAULT, 'opencode-go/grok-4.6');
151
+ assert.ok(catalog.OPTIONS.some((option) => option.value === catalog.DEFAULT));
152
+ assert.equal((await adapter.getCurrentActiveModel()).model, catalog.DEFAULT);
153
+ },
154
+ );
155
+
156
+ // The catalog default survives whenever its own provider is connected.
157
+ await withOpenCodeHome(
158
+ (homeDir) => writeOpenCodeAuth(homeDir, {
159
+ opencode: { type: 'api', key: 'test' },
160
+ openai: { type: 'oauth' },
161
+ }),
162
+ async (adapter) => {
163
+ const catalog = await adapter.getSupportedModels();
164
+ const providerIds = new Set(catalog.OPTIONS.map((option) => option.value.split('/')[0]));
165
+
166
+ assert.deepEqual([...providerIds].sort(), ['opencode', 'openai'].sort());
167
+ assert.equal(catalog.DEFAULT, OPENCODE_PREDEFINED_MODELS.DEFAULT);
168
+ },
169
+ );
170
+
171
+ // Providers configured rather than logged into count too.
172
+ await withOpenCodeHome(
173
+ async (homeDir) => {
174
+ const configDir = path.join(homeDir, '.config', 'opencode');
175
+ await mkdir(configDir, { recursive: true });
176
+ await writeFile(
177
+ path.join(configDir, 'opencode.json'),
178
+ JSON.stringify({ provider: { anthropic: { options: {} } } }),
179
+ 'utf8',
180
+ );
181
+ },
182
+ async (adapter) => {
183
+ const catalog = await adapter.getSupportedModels();
184
+ const providerIds = new Set(catalog.OPTIONS.map((option) => option.value.split('/')[0]));
185
+
186
+ assert.deepEqual([...providerIds], ['anthropic']);
187
+ },
188
+ );
189
+
190
+ // An API key in the environment is enough on its own.
191
+ await withOpenCodeHome(
192
+ async () => {
193
+ process.env.OPENAI_API_KEY = 'test';
194
+ },
195
+ async (adapter) => {
196
+ const catalog = await adapter.getSupportedModels();
197
+ const providerIds = new Set(catalog.OPTIONS.map((option) => option.value.split('/')[0]));
198
+
199
+ assert.deepEqual([...providerIds], ['openai']);
200
+ },
201
+ );
202
+ });
203
+
204
+ const writeOpenCodeSessionDatabase = async (homeDir: string, rows: Array<Record<string, unknown>>) => {
205
+ const dbPath = path.join(homeDir, '.local', 'share', 'opencode', 'opencode.db');
206
+ await mkdir(path.dirname(dbPath), { recursive: true });
207
+ const db = new Database(dbPath);
208
+ try {
209
+ db.exec(`
210
+ CREATE TABLE session (
211
+ id text PRIMARY KEY,
212
+ model text,
213
+ agent text,
214
+ directory text,
215
+ time_created integer NOT NULL,
216
+ time_updated integer NOT NULL
217
+ );
218
+ `);
219
+ const insert = db.prepare(
220
+ 'INSERT INTO session (id, model, agent, directory, time_created, time_updated) VALUES (?, ?, ?, ?, ?, ?)',
221
+ );
222
+ for (const [index, row] of rows.entries()) {
223
+ insert.run(
224
+ row.id,
225
+ row.model,
226
+ row.agent ?? null,
227
+ row.directory ?? '/tmp/project',
228
+ row.timeCreated ?? 1700000000000,
229
+ row.timeUpdated ?? 1700000001000 + index,
230
+ );
231
+ }
232
+ } finally {
233
+ db.close();
234
+ }
235
+ };
236
+
237
+ test('OpenCode composes the provider prefix into session model ids', async () => {
238
+ // The lookup translates the app session id through the sessions database,
239
+ // so point it at a throwaway file rather than the developer's real one.
240
+ const tempSessionsDirectory = await mkdtemp(path.join(os.tmpdir(), 'opencode-session-model-'));
241
+ const previousDatabasePath = process.env.DATABASE_PATH;
242
+ try {
243
+ closeConnection();
244
+ process.env.DATABASE_PATH = path.join(tempSessionsDirectory, 'auth.db');
245
+ await initializeDatabase();
246
+ // OpenCode stores the provider and model id in separate fields of the
247
+ // session row ({"id":"z-ai/glm-5.3-flash","providerID":"openrouter"}), but
248
+ // the CLI's `--model` flag only resolves the composed
249
+ // `<providerID>/<modelID>` form. Resuming such a session without the prefix
250
+ // fails with an unknown-model server error, which hits OpenRouter users
251
+ // hardest because their model ids carry a slash themselves.
252
+ await withOpenCodeHome(
253
+ (homeDir) => writeOpenCodeSessionDatabase(homeDir, [
254
+ {
255
+ id: 'ses_openrouter',
256
+ model: JSON.stringify({
257
+ id: 'z-ai/glm-5.3-flash',
258
+ providerID: 'openrouter',
259
+ variant: 'default',
260
+ }),
261
+ },
262
+ {
263
+ id: 'ses_qualified',
264
+ // An id that already carries the provider prefix must survive as-is.
265
+ model: JSON.stringify({
266
+ id: 'openrouter/z-ai/glm-5.3-flash',
267
+ providerID: 'openrouter',
268
+ }),
269
+ },
270
+ {
271
+ id: 'ses_plain_string',
272
+ model: 'opencode/gpt-5.6-terra',
273
+ },
274
+ ]),
275
+ async (adapter) => {
276
+ assert.equal(
277
+ (await adapter.getCurrentActiveModel('ses_openrouter')).model,
278
+ 'openrouter/z-ai/glm-5.3-flash',
279
+ );
280
+ assert.equal(
281
+ (await adapter.getCurrentActiveModel('ses_qualified')).model,
282
+ 'openrouter/z-ai/glm-5.3-flash',
283
+ );
284
+ assert.equal(
285
+ (await adapter.getCurrentActiveModel('ses_plain_string')).model,
286
+ 'opencode/gpt-5.6-terra',
287
+ );
288
+ },
289
+ );
290
+ } finally {
291
+ closeConnection();
292
+ if (previousDatabasePath === undefined) {
293
+ delete process.env.DATABASE_PATH;
294
+ } else {
295
+ process.env.DATABASE_PATH = previousDatabasePath;
296
+ }
297
+ await rm(tempSessionsDirectory, { recursive: true, force: true });
298
+ }
41
299
  });
@@ -151,6 +151,30 @@ const createOpenCodeDatabase = async (homeDir: string, workspacePath: string): P
151
151
  3,
152
152
  2,
153
153
  );
154
+ db.prepare(`
155
+ INSERT INTO session (
156
+ id, project_id, parent_id, slug, directory, title, version, time_created, time_updated,
157
+ time_archived, tokens_input, tokens_output, tokens_reasoning, tokens_cache_read,
158
+ tokens_cache_write
159
+ )
160
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
161
+ `).run(
162
+ 'open-child-session-1',
163
+ 'project-1',
164
+ 'open-session-1',
165
+ 'open-child-session-1',
166
+ workspacePath,
167
+ 'OpenCode child session',
168
+ '0.0.0',
169
+ 1_700_000_002_000,
170
+ 1_700_000_005_000,
171
+ null,
172
+ 5,
173
+ 10,
174
+ 2,
175
+ 1,
176
+ 1,
177
+ );
154
178
 
155
179
  const userMessageData = JSON.stringify({
156
180
  role: 'user',
@@ -255,16 +279,29 @@ test('OpenCode session synchronizer indexes sqlite sessions without deletable tr
255
279
  try {
256
280
  await createOpenCodeDatabase(tempRoot, workspacePath);
257
281
  await withIsolatedDatabase(() => {
282
+ sessionsDb.createAppSession('app-child-session-1', 'opencode', workspacePath);
283
+ sessionsDb.assignProviderSessionId('app-child-session-1', 'open-child-session-1');
284
+
258
285
  const synchronizer = new OpenCodeSessionSynchronizer();
259
286
  const processed = synchronizer.synchronize();
260
287
 
261
- return Promise.resolve(processed).then((count) => {
288
+ return Promise.resolve(processed).then(async (count) => {
262
289
  assert.equal(count, 1);
263
290
  const indexed = sessionsDb.getSessionById('open-session-1');
264
291
  assert.equal(indexed?.provider, 'opencode');
265
292
  assert.equal(indexed?.project_path, workspacePath);
266
293
  assert.equal(indexed?.custom_name, 'OpenCode indexed title');
267
294
  assert.equal(indexed?.jsonl_path, null);
295
+ assert.equal(sessionsDb.getSessionById('app-child-session-1'), null);
296
+ assert.equal(sessionsDb.getSessionByProviderSessionId('open-child-session-1'), null);
297
+
298
+ sessionsDb.createAppSession('app-child-session-2', 'opencode', workspacePath);
299
+ sessionsDb.assignProviderSessionId('app-child-session-2', 'open-child-session-1');
300
+ await synchronizer.synchronize(new Date(1_700_000_004_500));
301
+ assert.equal(
302
+ sessionsDb.getSessionById('app-child-session-2')?.provider_session_id,
303
+ 'open-child-session-1',
304
+ );
268
305
  });
269
306
  });
270
307
  } finally {
@@ -290,6 +327,7 @@ test('OpenCode session synchronizer returns the app session id once provider map
290
327
  assert.equal(sessionId, 'app-session-1');
291
328
  assert.equal(sessionsDb.getAllSessions().length, 1);
292
329
  assert.equal(sessionsDb.getSessionById('app-session-1')?.provider_session_id, 'open-session-1');
330
+ assert.equal(sessionsDb.getSessionById('open-child-session-1'), null);
293
331
  });
294
332
  });
295
333
  } finally {
@@ -438,6 +476,7 @@ const seedOpenCodeSession = async (
438
476
  CREATE TABLE session (
439
477
  id TEXT PRIMARY KEY,
440
478
  project_id TEXT,
479
+ parent_id TEXT,
441
480
  directory TEXT,
442
481
  title TEXT,
443
482
  time_created INTEGER,
@@ -0,0 +1,37 @@
1
+ import assert from 'node:assert/strict';
2
+ import test from 'node:test';
3
+
4
+ import { CLAUDE_PREDEFINED_MODELS } from '@/modules/providers/list/claude/claude-models.provider.js';
5
+ import { CODEX_PREDEFINED_MODELS } from '@/modules/providers/list/codex/codex-models.provider.js';
6
+ import { CURSOR_PREDEFINED_MODELS } from '@/modules/providers/list/cursor/cursor-models.provider.js';
7
+ import { OPENCODE_PREDEFINED_MODELS } from '@/modules/providers/list/opencode/opencode-models.provider.js';
8
+ import { providerCapabilitiesService } from '@/modules/providers/services/provider-capabilities.service.js';
9
+ import type { LLMProvider, ProviderModelsDefinition } from '@/shared/types.js';
10
+
11
+ /**
12
+ * The full curated catalogs, before OpenCode narrows its list to the upstream
13
+ * providers a machine has connected.
14
+ */
15
+ const CURATED_CATALOGS: Record<LLMProvider, ProviderModelsDefinition> = {
16
+ claude: CLAUDE_PREDEFINED_MODELS,
17
+ codex: CODEX_PREDEFINED_MODELS,
18
+ cursor: CURSOR_PREDEFINED_MODELS,
19
+ opencode: OPENCODE_PREDEFINED_MODELS,
20
+ };
21
+
22
+ test('capability effort levels cover every level a curated model declares', () => {
23
+ // Custom models may declare only these levels, so a level added to a curated
24
+ // model has to be added here too, or users could not give it to their own.
25
+ for (const [provider, catalog] of Object.entries(CURATED_CATALOGS) as Array<[LLMProvider, ProviderModelsDefinition]>) {
26
+ const { effortLevels, supportsEffort } = providerCapabilitiesService.getProviderCapabilities(provider);
27
+
28
+ assert.equal(supportsEffort, effortLevels.length > 0, `${provider}: supportsEffort disagrees with effortLevels`);
29
+ assert.equal(new Set(effortLevels).size, effortLevels.length, `${provider}: effortLevels lists a level twice`);
30
+
31
+ const declaredLevels = new Set(
32
+ catalog.OPTIONS.flatMap((option) => option.effort?.values.map((level) => level.value) ?? []),
33
+ );
34
+ const missingLevels = [...declaredLevels].filter((level) => !effortLevels.includes(level));
35
+ assert.deepEqual(missingLevels, [], `${provider}: effortLevels misses curated levels`);
36
+ }
37
+ });
@@ -69,6 +69,7 @@ const createCatalogStore = () => {
69
69
  modelId: input.id,
70
70
  model: input.model,
71
71
  sortOrder: readRows(provider).length,
72
+ effort: input.effort ?? null,
72
73
  };
73
74
  rows.set(provider, [...readRows(provider), record]);
74
75
  return record;
@@ -82,7 +83,13 @@ const createCatalogStore = () => {
82
83
  if (!existing) {
83
84
  return null;
84
85
  }
85
- const updated = { ...existing, modelId: input.id, model: input.model };
86
+ const updated = {
87
+ ...existing,
88
+ modelId: input.id,
89
+ model: input.model,
90
+ // Mirrors the repository: an omitted effort keeps the stored levels.
91
+ effort: input.effort === undefined ? existing.effort : input.effort,
92
+ };
86
93
  rows.set(provider, readRows(provider).map((record) => (
87
94
  record.recordId === recordId ? updated : record
88
95
  )));
@@ -165,6 +172,97 @@ test('custom models can be created, edited, and deleted', async () => {
165
172
  assert.equal(removed.models.OPTIONS.some((option) => option.recordId === recordId), false);
166
173
  });
167
174
 
175
+ test('custom models carry declared effort levels into the merged catalog', async () => {
176
+ const { service } = createTestService();
177
+
178
+ const created = await service.createCustomModel('claude', {
179
+ model: 'My Custom Model',
180
+ id: 'my-custom-model',
181
+ effort: { values: ['low', 'max'], default: 'max' },
182
+ });
183
+ const expectedEffort = { default: 'max', values: [{ value: 'low' }, { value: 'max' }] };
184
+ assert.deepEqual(created.model.effort, expectedEffort);
185
+
186
+ const catalog = await service.getProviderModels('claude');
187
+ const merged = catalog.OPTIONS.find((option) => option.value === 'my-custom-model');
188
+ assert.deepEqual(merged?.effort, expectedEffort);
189
+
190
+ // A rename that does not mention effort (an older client) keeps the levels.
191
+ const renamed = await service.updateCustomModel('claude', created.model.recordId as number, {
192
+ model: 'Renamed',
193
+ id: 'my-custom-model',
194
+ });
195
+ assert.deepEqual(renamed.model.effort, expectedEffort);
196
+
197
+ const cleared = await service.updateCustomModel('claude', created.model.recordId as number, {
198
+ model: 'Renamed',
199
+ id: 'my-custom-model',
200
+ effort: null,
201
+ });
202
+ assert.equal('effort' in cleared.model, false);
203
+ });
204
+
205
+ test('custom models without declared effort levels expose no effort', async () => {
206
+ const { service } = createTestService();
207
+
208
+ const created = await service.createCustomModel('codex', { model: 'Plain', id: 'plain-model' });
209
+
210
+ assert.equal('effort' in created.model, false);
211
+ });
212
+
213
+ test('custom effort levels must be levels the provider supports', async () => {
214
+ const { service } = createTestService();
215
+
216
+ await assert.rejects(
217
+ () => service.createCustomModel('codex', {
218
+ model: 'Unknown level',
219
+ id: 'unknown-level',
220
+ effort: { values: ['low', 'turbo'] },
221
+ }),
222
+ (error) => error instanceof AppError
223
+ && error.code === 'INVALID_MODEL_EFFORT'
224
+ && error.statusCode === 400,
225
+ );
226
+
227
+ await assert.rejects(
228
+ () => service.createCustomModel('cursor', {
229
+ model: 'Cursor effort',
230
+ id: 'cursor-effort',
231
+ effort: { values: ['low'] },
232
+ }),
233
+ (error) => error instanceof AppError
234
+ && error.code === 'MODEL_EFFORT_NOT_SUPPORTED'
235
+ && error.statusCode === 400,
236
+ );
237
+ });
238
+
239
+ test('allowed effort levels come from the provider, not from the built-in models this machine sees', async () => {
240
+ // The fake adapters' built-in models declare no effort at all, which is what
241
+ // the real OpenCode adapter returns once it narrows its catalog to OpenCode
242
+ // Zen, Anthropic, or OpenAI: only its OpenCode Go models declare effort.
243
+ const { service } = createTestService();
244
+
245
+ const created = await service.createCustomModel('opencode', {
246
+ model: 'Router model',
247
+ id: 'openrouter/router-model',
248
+ effort: { values: ['none', 'high'] },
249
+ });
250
+ assert.deepEqual(created.model.effort, { values: [{ value: 'none' }, { value: 'high' }] });
251
+
252
+ // Every merged catalog carries the provider's levels for the model library,
253
+ // weakest first, and an empty list where effort is unsupported.
254
+ const expectedLevels: Record<LLMProvider, string[]> = {
255
+ claude: ['low', 'medium', 'high', 'xhigh', 'max', 'ultracode'],
256
+ codex: ['low', 'medium', 'high', 'xhigh', 'max', 'ultra'],
257
+ cursor: [],
258
+ opencode: ['none', 'minimal', 'low', 'medium', 'high', 'xhigh', 'max', 'thinking'],
259
+ };
260
+ for (const [provider, levels] of Object.entries(expectedLevels) as Array<[LLMProvider, string[]]>) {
261
+ assert.deepEqual((await service.getProviderModels(provider)).EFFORT_LEVELS, levels, provider);
262
+ }
263
+ assert.deepEqual(created.models.EFFORT_LEVELS, expectedLevels.opencode);
264
+ });
265
+
168
266
  test('duplicate model ids are rejected within one provider', async () => {
169
267
  const { service } = createTestService();
170
268
  await service.createCustomModel('cursor', { model: 'First', id: 'custom-id' });