@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
@@ -0,0 +1,348 @@
1
+ import assert from 'node:assert/strict';
2
+ import * as nodeCrypto from 'node:crypto';
3
+ import { once } from 'node:events';
4
+ import { mkdtemp, rm } from 'node:fs/promises';
5
+ import type { AddressInfo } from 'node:net';
6
+ import { tmpdir } from 'node:os';
7
+ import path from 'node:path';
8
+ import test from 'node:test';
9
+
10
+ import express from 'express';
11
+
12
+ import { closeConnection, initializeDatabase, sessionsDb } from '@/modules/database/index.js';
13
+ import { chatRunRegistry, connectedClients } from '@/modules/websocket/index.js';
14
+ import type { NormalizedMessage } from '@/shared/types.js';
15
+ import { createNormalizedMessage } from '@/shared/utils.js';
16
+
17
+ import { createAgentRouter } from '../agent.routes.js';
18
+
19
+ /**
20
+ * `POST /api/agent` runs a provider for an API-key caller. Until now it wrote
21
+ * the run's events straight to the HTTP response and nothing else knew: the
22
+ * session was not on the running-sessions list, no tab could subscribe to
23
+ * it, and a second request could start a second run on the same session. It
24
+ * now registers the run with the chat run registry the way a `chat.send`
25
+ * does, with the HTTP response as the run's first audience.
26
+ */
27
+
28
+ type AgentDependencies = Parameters<typeof createAgentRouter>[0];
29
+ type RunFunction = AgentDependencies['queryClaude'];
30
+
31
+ async function withIsolatedDatabase(run: () => Promise<void>): Promise<void> {
32
+ const previousDatabasePath = process.env.DATABASE_PATH;
33
+ const tempDirectory = await mkdtemp(path.join(tmpdir(), 'agent-run-registration-'));
34
+ closeConnection();
35
+ process.env.DATABASE_PATH = path.join(tempDirectory, 'auth.db');
36
+ await initializeDatabase();
37
+ try {
38
+ await run();
39
+ } finally {
40
+ connectedClients.clear();
41
+ chatRunRegistry.clearAll();
42
+ closeConnection();
43
+ if (previousDatabasePath === undefined) {
44
+ delete process.env.DATABASE_PATH;
45
+ } else {
46
+ process.env.DATABASE_PATH = previousDatabasePath;
47
+ }
48
+ await rm(tempDirectory, { recursive: true, force: true });
49
+ }
50
+ }
51
+
52
+ function createDependencies(queryClaude: RunFunction): AgentDependencies {
53
+ const unexpected = async (): Promise<never> => { throw new Error('unexpected provider call'); };
54
+ return {
55
+ fileSystem: { access: async () => undefined } as unknown as AgentDependencies['fileSystem'],
56
+ crypto: nodeCrypto,
57
+ homeDirectory: () => '/home/test',
58
+ spawnProcess: (() => { throw new Error('spawn should not run'); }) as unknown as AgentDependencies['spawnProcess'],
59
+ platformMode: true,
60
+ users: { getFirstUser: () => ({ id: 1, username: 'test-user' }) },
61
+ apiKeys: { validateApiKey: () => undefined },
62
+ githubTokens: { getActiveGithubToken: () => null },
63
+ projects: { createProjectPath: () => ({ outcome: 'created' }) },
64
+ models: { getProviderModels: async () => ({ OPTIONS: [], DEFAULT: 'default-model' }) } as unknown as AgentDependencies['models'],
65
+ sessions: {
66
+ getSessionById: (sessionId) => sessionsDb.getSessionById(sessionId),
67
+ getSessionByProviderSessionId: (providerSessionId) => sessionsDb.getSessionByProviderSessionId(providerSessionId),
68
+ createAppSession: (provider, projectPath, initialMessage) => {
69
+ const sessionId = `app-${initialMessage.length}-${nodeCrypto.randomUUID()}`;
70
+ sessionsDb.createAppSession(sessionId, provider, projectPath, initialMessage);
71
+ return { sessionId };
72
+ },
73
+ },
74
+ runs: chatRunRegistry,
75
+ queryClaude,
76
+ queryCursor: unexpected as RunFunction,
77
+ queryCodex: unexpected as RunFunction,
78
+ queryOpenCode: unexpected as RunFunction,
79
+ GithubClient: class {} as unknown as AgentDependencies['GithubClient'],
80
+ };
81
+ }
82
+
83
+ async function withAgentServer(dependencies: AgentDependencies, run: (baseUrl: string) => Promise<void>): Promise<void> {
84
+ const app = express();
85
+ app.use(express.json());
86
+ app.use('/api/agent', createAgentRouter(dependencies));
87
+ const server = app.listen(0, '127.0.0.1');
88
+ await once(server, 'listening');
89
+ try {
90
+ const address = server.address() as AddressInfo;
91
+ await run(`http://127.0.0.1:${address.port}`);
92
+ } finally {
93
+ await new Promise<void>((resolve) => server.close(() => resolve()));
94
+ }
95
+ }
96
+
97
+ const post = (baseUrl: string, body: Record<string, unknown>) =>
98
+ fetch(`${baseUrl}/api/agent`, { method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify(body) });
99
+
100
+ const readEvents = async (response: Response): Promise<Array<Record<string, unknown>>> =>
101
+ (await response.text())
102
+ .split('\n\n')
103
+ .filter((frame) => frame.startsWith('data: '))
104
+ .map((frame) => JSON.parse(frame.slice('data: '.length)) as Record<string, unknown>);
105
+
106
+ /** A runtime that announces its native session id, streams one text event, and waits to be released before completing. */
107
+ function createHeldRuntime(nativeSessionId = 'native-1') {
108
+ let release: () => void = () => undefined;
109
+ const released = new Promise<void>((resolve) => { release = resolve; });
110
+ const seen: Array<{ sessionId: unknown; writerIsGateway: boolean }> = [];
111
+ const queryClaude: RunFunction = async (_command, options, writer) => {
112
+ seen.push({ sessionId: (options as { sessionId?: unknown }).sessionId, writerIsGateway: Boolean((writer as { isWebSocketWriter?: boolean }).isWebSocketWriter) });
113
+ // A resumed session announces the id it resumed under; a new one, a fresh id.
114
+ const announced = (writer as { getSessionId?: () => string | null }).getSessionId?.() ?? nativeSessionId;
115
+ writer.send(createNormalizedMessage({ kind: 'session_created', provider: 'claude', sessionId: announced, newSessionId: announced }));
116
+ writer.send(createNormalizedMessage({ kind: 'text', provider: 'claude', sessionId: announced, role: 'assistant', content: 'hello' }));
117
+ await released;
118
+ writer.send(createNormalizedMessage({ kind: 'complete', provider: 'claude', sessionId: announced, exitCode: 0 }));
119
+ };
120
+ return { queryClaude, release, seen };
121
+ }
122
+
123
+ test('an API run is registered like a chat send: listed while running, streamed decorated, mapped to its native id', async () => {
124
+ await withIsolatedDatabase(async () => {
125
+ const runtime = createHeldRuntime();
126
+ await withAgentServer(createDependencies(runtime.queryClaude), async (baseUrl) => {
127
+ const responsePromise = post(baseUrl, { projectPath: '/home/test/project', message: 'Run it' });
128
+ // Wait for the runtime to be entered.
129
+ for (let i = 0; i < 50 && runtime.seen.length === 0; i += 1) {
130
+ await new Promise((resolve) => { setTimeout(resolve, 20); });
131
+ }
132
+
133
+ const [running] = chatRunRegistry.listRunningRuns();
134
+ assert.ok(running, 'the run is on the running-sessions list while the provider works');
135
+ assert.equal(running.provider, 'claude');
136
+ assert.match(running.sessionId, /^app-/, 'listed under the app session id the route allocated');
137
+ assert.deepEqual(runtime.seen, [{ sessionId: running.sessionId, writerIsGateway: true }], 'the runtime gets the app id and the run\'s gateway writer');
138
+ // A tab that opens the session mid-run replays what it missed.
139
+ assert.deepEqual(chatRunRegistry.replayEvents(running.sessionId, 0).map((event) => event.kind), ['text']);
140
+
141
+ runtime.release();
142
+ const response = await responsePromise;
143
+ assert.equal(response.status, 200);
144
+ assert.match(response.headers.get('content-type') ?? '', /^text\/event-stream/);
145
+ const events = await readEvents(response);
146
+ assert.deepEqual(events.map((event) => event.type ?? event.kind), ['status', 'session-id', 'text', 'complete', 'done']);
147
+ assert.equal(events[1]?.sessionId, running.sessionId, 'session-id names the app session, not the provider\'s');
148
+ // Provider events reach the stream as a tab would see them: app session id, sequenced, no session_created.
149
+ const text = events[2] as NormalizedMessage;
150
+ assert.equal(text.sessionId, running.sessionId);
151
+ assert.equal(text.seq, 1);
152
+
153
+ assert.equal(chatRunRegistry.isProcessing(running.sessionId), false, 'the terminal complete ends the run');
154
+ assert.equal(sessionsDb.getSessionById(running.sessionId)?.provider_session_id, 'native-1', 'the native id the runtime announced is mapped onto the row');
155
+ });
156
+ });
157
+ });
158
+
159
+ test('a second API request on a session mid-run is refused with 409, as the chat socket refuses it', async () => {
160
+ await withIsolatedDatabase(async () => {
161
+ const runtime = createHeldRuntime();
162
+ await withAgentServer(createDependencies(runtime.queryClaude), async (baseUrl) => {
163
+ const first = post(baseUrl, { projectPath: '/home/test/project', message: 'Run it' });
164
+ for (let i = 0; i < 50 && runtime.seen.length === 0; i += 1) {
165
+ await new Promise((resolve) => { setTimeout(resolve, 20); });
166
+ }
167
+ const [running] = chatRunRegistry.listRunningRuns();
168
+ assert.ok(running);
169
+
170
+ const second = await post(baseUrl, { projectPath: '/home/test/project', message: 'Again', sessionId: running.sessionId, stream: false });
171
+ assert.equal(second.status, 409);
172
+ assert.equal(runtime.seen.length, 1, 'the provider is not entered twice');
173
+
174
+ runtime.release();
175
+ await first;
176
+ });
177
+ });
178
+ });
179
+
180
+ test('a non-streaming API run answers with the app session id and is off the list once done', async () => {
181
+ await withIsolatedDatabase(async () => {
182
+ const runtime = createHeldRuntime();
183
+ runtime.release();
184
+ await withAgentServer(createDependencies(runtime.queryClaude), async (baseUrl) => {
185
+ const response = await post(baseUrl, { projectPath: '/home/test/project', message: 'Run it', stream: false });
186
+ assert.equal(response.status, 200);
187
+ const body = await response.json() as { success: boolean; sessionId: string };
188
+ assert.equal(body.success, true);
189
+ assert.match(body.sessionId, /^app-/);
190
+ assert.deepEqual(chatRunRegistry.listRunningRuns(), []);
191
+ });
192
+ });
193
+ });
194
+
195
+ test('an API run continues a session the caller names by either id, and refuses one that does not exist', async () => {
196
+ await withIsolatedDatabase(async () => {
197
+ sessionsDb.createAppSession('app-existing', 'claude', '/home/test/project', 'Existing');
198
+ sessionsDb.assignProviderSessionId('app-existing', 'native-existing');
199
+ const runtime = createHeldRuntime();
200
+ runtime.release();
201
+ await withAgentServer(createDependencies(runtime.queryClaude), async (baseUrl) => {
202
+ const byAppId = await post(baseUrl, { projectPath: '/home/test/project', message: 'More', sessionId: 'app-existing', stream: false });
203
+ assert.equal(byAppId.status, 200);
204
+ assert.equal((await byAppId.json() as { sessionId: string }).sessionId, 'app-existing');
205
+
206
+ // A caller that stored the provider-native id an earlier response gave it.
207
+ const byNativeId = await post(baseUrl, { projectPath: '/home/test/project', message: 'More', sessionId: 'native-existing', stream: false });
208
+ assert.equal(byNativeId.status, 200);
209
+ assert.equal((await byNativeId.json() as { sessionId: string }).sessionId, 'app-existing');
210
+ assert.deepEqual(runtime.seen.map((call) => call.sessionId), ['app-existing', 'app-existing']);
211
+
212
+ const unknown = await post(baseUrl, { projectPath: '/home/test/project', message: 'More', sessionId: 'nope', stream: false });
213
+ assert.equal(unknown.status, 404);
214
+ assert.equal(runtime.seen.length, 2);
215
+ });
216
+ });
217
+ });
218
+
219
+ test('a runtime that throws leaves no run behind', async () => {
220
+ await withIsolatedDatabase(async () => {
221
+ const queryClaude: RunFunction = async () => { throw new Error('provider exploded'); };
222
+ await withAgentServer(createDependencies(queryClaude), async (baseUrl) => {
223
+ const response = await post(baseUrl, { projectPath: '/home/test/project', message: 'Run it', stream: false });
224
+ assert.equal(response.status, 500);
225
+ assert.deepEqual(chatRunRegistry.listRunningRuns(), [], 'the safety net completes the run');
226
+ });
227
+ });
228
+ });
229
+
230
+ test('a sessionId that is not a string is refused, not bound to a query', async () => {
231
+ // The lookup runs before the handler's try: a throw there is an unhandled
232
+ // rejection, and better-sqlite3 throws on binding an object.
233
+ await withIsolatedDatabase(async () => {
234
+ const runtime = createHeldRuntime();
235
+ await withAgentServer(createDependencies(runtime.queryClaude), async (baseUrl) => {
236
+ const response = await post(baseUrl, { projectPath: '/home/test/project', message: 'Run', sessionId: { $ne: null }, stream: false });
237
+ assert.equal(response.status, 400);
238
+ assert.equal(runtime.seen.length, 0);
239
+ });
240
+ });
241
+ });
242
+
243
+ test('a continued session runs under its own provider and project, as a chat send does', async () => {
244
+ await withIsolatedDatabase(async () => {
245
+ sessionsDb.createAppSession('app-codex', 'codex', '/home/test/project', 'Codex one');
246
+ const claudeCalls: unknown[] = [];
247
+ const codexCalls: unknown[] = [];
248
+ const dependencies = createDependencies(async (_command, options, writer) => {
249
+ claudeCalls.push(options);
250
+ writer.send(createNormalizedMessage({ kind: 'complete', provider: 'claude', sessionId: null, exitCode: 0 }));
251
+ });
252
+ dependencies.queryCodex = async (_command, options, writer) => {
253
+ codexCalls.push(options);
254
+ writer.send(createNormalizedMessage({ kind: 'complete', provider: 'codex', sessionId: null, exitCode: 0 }));
255
+ };
256
+ await withAgentServer(dependencies, async (baseUrl) => {
257
+ // No provider named: the row's, not the default.
258
+ const continued = await post(baseUrl, { projectPath: '/home/test/project', message: 'More', sessionId: 'app-codex', stream: false });
259
+ assert.equal(continued.status, 200);
260
+ assert.deepEqual([claudeCalls.length, codexCalls.length], [0, 1]);
261
+
262
+ // Another provider named: refused, not re-homed.
263
+ const mismatched = await post(baseUrl, { projectPath: '/home/test/project', message: 'More', sessionId: 'app-codex', provider: 'claude', stream: false });
264
+ assert.equal(mismatched.status, 400);
265
+ assert.equal(claudeCalls.length, 0);
266
+
267
+ // Another directory named: not that session.
268
+ const elsewhere = await post(baseUrl, { projectPath: '/home/test/other', message: 'More', sessionId: 'app-codex', stream: false });
269
+ assert.equal(elsewhere.status, 500);
270
+ assert.match((await elsewhere.json() as { error: string }).error, /belongs to project/);
271
+ assert.equal(codexCalls.length, 1);
272
+ });
273
+ });
274
+ });
275
+
276
+ test('a busy session is refused before anything is cloned', async () => {
277
+ await withIsolatedDatabase(async () => {
278
+ const runtime = createHeldRuntime();
279
+ const dependencies = createDependencies(runtime.queryClaude);
280
+ let clones = 0;
281
+ dependencies.spawnProcess = (() => { clones += 1; throw new Error('no clone expected'); }) as unknown as AgentDependencies['spawnProcess'];
282
+ await withAgentServer(dependencies, async (baseUrl) => {
283
+ const first = post(baseUrl, { projectPath: '/home/test/project', message: 'Run it' });
284
+ for (let i = 0; i < 50 && runtime.seen.length === 0; i += 1) {
285
+ await new Promise((resolve) => { setTimeout(resolve, 20); });
286
+ }
287
+ const [running] = chatRunRegistry.listRunningRuns();
288
+ assert.ok(running);
289
+
290
+ const retry = await post(baseUrl, { githubUrl: 'https://github.com/owner/repo.git', projectPath: '/home/test/project', message: 'Again', sessionId: running.sessionId, stream: false });
291
+ assert.equal(retry.status, 409);
292
+ assert.equal(clones, 0, 'the refusal comes before the clone step');
293
+
294
+ runtime.release();
295
+ await first;
296
+ });
297
+ });
298
+ });
299
+
300
+ test('a run a tab aborted ends without branching or opening a PR, and says so', async () => {
301
+ await withIsolatedDatabase(async () => {
302
+ const runtime = createHeldRuntime();
303
+ let gitCalls = 0;
304
+ const dependencies = createDependencies(async (command, options, writer) => {
305
+ await runtime.queryClaude(command, options, writer);
306
+ });
307
+ dependencies.spawnProcess = (() => { gitCalls += 1; throw new Error('git should not run after an abort'); }) as unknown as AgentDependencies['spawnProcess'];
308
+ await withAgentServer(dependencies, async (baseUrl) => {
309
+ const responsePromise = post(baseUrl, { projectPath: '/home/test/project', message: 'Run it', createBranch: true, stream: false });
310
+ for (let i = 0; i < 50 && runtime.seen.length === 0; i += 1) {
311
+ await new Promise((resolve) => { setTimeout(resolve, 20); });
312
+ }
313
+ const [running] = chatRunRegistry.listRunningRuns();
314
+ assert.ok(running);
315
+ // What chat.abort does: the run completes as aborted; the runtime then returns.
316
+ chatRunRegistry.completeRun(running.sessionId, { exitCode: 1, aborted: true });
317
+ runtime.release();
318
+
319
+ const response = await responsePromise;
320
+ const body = await response.json() as { success: boolean; aborted?: boolean; branch?: unknown };
321
+ assert.equal(body.success, false);
322
+ assert.equal(body.aborted, true);
323
+ assert.equal(body.branch, undefined);
324
+ assert.equal(gitCalls, 0);
325
+ });
326
+ });
327
+ });
328
+
329
+ test('a non-streaming run answers with the assistant\'s replies, its token usage and the provider id', async () => {
330
+ await withIsolatedDatabase(async () => {
331
+ const dependencies = createDependencies(async (_command, _options, writer) => {
332
+ writer.send(createNormalizedMessage({ kind: 'session_created', provider: 'claude', sessionId: 'native-7', newSessionId: 'native-7' }));
333
+ writer.send(createNormalizedMessage({ kind: 'text', provider: 'claude', sessionId: 'native-7', role: 'assistant', content: 'pong' }));
334
+ writer.send(createNormalizedMessage({
335
+ kind: 'status', text: 'token_budget', provider: 'claude', sessionId: 'native-7',
336
+ tokenBudget: { used: 130, total: 160_000, inputTokens: 100, outputTokens: 30, cacheReadTokens: 60, cacheCreationTokens: 10, cacheTokens: 70, breakdown: { input: 100, output: 30 } },
337
+ }));
338
+ writer.send(createNormalizedMessage({ kind: 'complete', provider: 'claude', sessionId: 'native-7', exitCode: 0 }));
339
+ });
340
+ await withAgentServer(dependencies, async (baseUrl) => {
341
+ const response = await post(baseUrl, { projectPath: '/home/test/project', message: 'ping', stream: false });
342
+ const body = await response.json() as { providerSessionId: string; messages: Array<{ content: string }>; tokens: Record<string, number> };
343
+ assert.equal(body.providerSessionId, 'native-7');
344
+ assert.deepEqual(body.messages.map((message) => message.content), ['pong']);
345
+ assert.deepEqual(body.tokens, { inputTokens: 100, outputTokens: 30, cacheReadTokens: 60, cacheCreationTokens: 10, totalTokens: 130 });
346
+ });
347
+ });
348
+ });
@@ -30,6 +30,23 @@ function createDependencies(
30
30
  githubTokens: { getActiveGithubToken: () => null },
31
31
  projects: { createProjectPath: () => ({ outcome: 'created' }) },
32
32
  models: {} as AgentDependencies['models'],
33
+ sessions: {
34
+ getSessionById: () => null,
35
+ getSessionByProviderSessionId: () => null,
36
+ createAppSession: () => ({ sessionId: 'app-session-1' }),
37
+ },
38
+ // A registry stand-in: the runtime writes straight to the audience, which
39
+ // is all the tests above this file's registration tests need.
40
+ runs: {
41
+ startRun: ((input: { connection: { send(data: string): void } | null }) => ({
42
+ // The gateway writer's shape: a runtime that feature-detects it (codex)
43
+ // hands it objects to serialize, as the real one expects.
44
+ writer: { isWebSocketWriter: true, send: (data: unknown) => input.connection?.send(JSON.stringify(data)), getSessionId: () => null },
45
+ events: [],
46
+ })) as unknown as AgentDependencies['runs']['startRun'],
47
+ completeRunIfCurrent: () => undefined,
48
+ isProcessing: () => false,
49
+ },
33
50
  queryClaude: unexpectedProviderCall as AgentDependencies['queryClaude'],
34
51
  queryCursor: unexpectedProviderCall as AgentDependencies['queryCursor'],
35
52
  queryCodex: unexpectedProviderCall as AgentDependencies['queryCodex'],
@@ -154,7 +171,7 @@ test('GitHub cloning keeps credentials out of arguments and remote URL', async (
154
171
  assert.equal(cloneEnvironment?.CLOUDCLI_GITHUB_TOKEN, token);
155
172
  assert.equal(cloneEnvironment?.GIT_CONFIG_KEY_0, 'credential.helper');
156
173
  assert.equal(cloneEnvironment?.GIT_CONFIG_VALUE_0, '');
157
- assert.equal(cloneEnvironment?.GIT_CONFIG_KEY_1, 'credential.helper');
174
+ assert.equal(cloneEnvironment?.GIT_CONFIG_KEY_1, 'credential.https://github.com.helper');
158
175
  });
159
176
 
160
177
  test('Agent route reuses a matching checkout without cloning or deleting it', async () => {
@@ -182,7 +199,7 @@ test('Agent route reuses a matching checkout without cloning or deleting it', as
182
199
  } as unknown as AgentDependencies['fileSystem'],
183
200
  spawnProcess,
184
201
  models: {
185
- getProviderModels: async () => ({ models: { DEFAULT: 'default-model' } }),
202
+ getProviderModels: async () => ({ OPTIONS: [], DEFAULT: 'default-model' }),
186
203
  } as unknown as AgentDependencies['models'],
187
204
  queryClaude: (async () => undefined) as AgentDependencies['queryClaude'],
188
205
  }), async (baseUrl) => {
@@ -203,3 +220,35 @@ test('Agent route reuses a matching checkout without cloning or deleting it', as
203
220
  assert.deepEqual(spawnedArguments, [['config', '--get', 'remote.origin.url']]);
204
221
  assert.deepEqual(removedPaths, []);
205
222
  });
223
+
224
+ test('Agent route starts codex on the catalog default when the request names no model', async () => {
225
+ const codexCalls: { model?: string }[] = [];
226
+
227
+ await withAgentServer(createDependencies({
228
+ fileSystem: {
229
+ access: async () => undefined,
230
+ } as unknown as AgentDependencies['fileSystem'],
231
+ models: {
232
+ // `getProviderModels` resolves to a `ProviderModelsDefinition` - `OPTIONS`
233
+ // and `DEFAULT`, with nothing wrapped around it.
234
+ getProviderModels: async () => ({ OPTIONS: [], DEFAULT: 'catalog-default' }),
235
+ } as unknown as AgentDependencies['models'],
236
+ queryCodex: (async (_prompt: string, options: { model?: string }) => {
237
+ codexCalls.push(options);
238
+ }) as unknown as AgentDependencies['queryCodex'],
239
+ }), async (baseUrl) => {
240
+ const response = await fetch(`${baseUrl}/api/agent`, {
241
+ method: 'POST',
242
+ headers: { 'content-type': 'application/json' },
243
+ body: JSON.stringify({
244
+ projectPath: '/home/test/project',
245
+ message: 'Run',
246
+ provider: 'codex',
247
+ stream: false,
248
+ }),
249
+ });
250
+ assert.equal(response.status, 200);
251
+ });
252
+
253
+ assert.deepEqual(codexCalls.map((call) => call.model), ['catalog-default']);
254
+ });
@@ -1,9 +1,45 @@
1
1
  import express from 'express';
2
+ import type { Response } from 'express';
2
3
 
3
4
  import { browserUseService } from '@/modules/browser-use/browser-use.service.js';
4
5
 
5
6
  const router = express.Router();
6
7
 
8
+ /**
9
+ * Stable machine-readable error codes for the browser-use API.
10
+ *
11
+ * The server always replies with the structured envelope used by AppError
12
+ * (`{ success: false, error: { code, message, details } }`). Clients map
13
+ * `code` to localized copy; `message` is an English fallback and `details`
14
+ * may carry the underlying error text for diagnostics.
15
+ */
16
+ export const BROWSER_USE_ERROR_CODES = {
17
+ STATUS_LOAD_FAILED: 'BROWSER_USE_STATUS_LOAD_FAILED',
18
+ SETTINGS_LOAD_FAILED: 'BROWSER_USE_SETTINGS_LOAD_FAILED',
19
+ SETTINGS_SAVE_FAILED: 'BROWSER_USE_SETTINGS_SAVE_FAILED',
20
+ RUNTIME_INSTALL_FAILED: 'BROWSER_USE_RUNTIME_INSTALL_FAILED',
21
+ SESSIONS_LOAD_FAILED: 'BROWSER_USE_SESSIONS_LOAD_FAILED',
22
+ SESSION_STOP_FAILED: 'BROWSER_USE_SESSION_STOP_FAILED',
23
+ SESSION_DELETE_FAILED: 'BROWSER_USE_SESSION_DELETE_FAILED',
24
+ } as const;
25
+
26
+ function sendError(
27
+ res: Response,
28
+ statusCode: number,
29
+ code: string,
30
+ message: string,
31
+ details?: unknown,
32
+ ): void {
33
+ res.status(statusCode).json({
34
+ success: false,
35
+ error: {
36
+ code,
37
+ message,
38
+ details,
39
+ },
40
+ });
41
+ }
42
+
7
43
  function readParam(value: string | string[] | undefined): string {
8
44
  return Array.isArray(value) ? value[0] || '' : value || '';
9
45
  }
@@ -12,10 +48,13 @@ router.get('/status', async (_req, res) => {
12
48
  try {
13
49
  res.json({ success: true, data: await browserUseService.getStatus() });
14
50
  } catch (error) {
15
- res.status(500).json({
16
- success: false,
17
- error: error instanceof Error ? error.message : 'Failed to load Browser status.',
18
- });
51
+ sendError(
52
+ res,
53
+ 500,
54
+ BROWSER_USE_ERROR_CODES.STATUS_LOAD_FAILED,
55
+ 'Failed to load Browser status.',
56
+ error instanceof Error ? error.message : undefined,
57
+ );
19
58
  }
20
59
  });
21
60
 
@@ -23,10 +62,13 @@ router.get('/settings', async (_req, res) => {
23
62
  try {
24
63
  res.json({ success: true, data: { settings: await browserUseService.getSettings() } });
25
64
  } catch (error) {
26
- res.status(500).json({
27
- success: false,
28
- error: error instanceof Error ? error.message : 'Failed to load Browser settings.',
29
- });
65
+ sendError(
66
+ res,
67
+ 500,
68
+ BROWSER_USE_ERROR_CODES.SETTINGS_LOAD_FAILED,
69
+ 'Failed to load Browser settings.',
70
+ error instanceof Error ? error.message : undefined,
71
+ );
30
72
  }
31
73
  });
32
74
 
@@ -35,26 +77,38 @@ router.put('/settings', async (req, res) => {
35
77
  const settings = await browserUseService.updateSettings(req.body || {});
36
78
  res.json({ success: true, data: { settings } });
37
79
  } catch (error) {
38
- res.status(400).json({
39
- success: false,
40
- error: error instanceof Error ? error.message : 'Failed to save Browser settings.',
41
- });
80
+ sendError(
81
+ res,
82
+ 400,
83
+ BROWSER_USE_ERROR_CODES.SETTINGS_SAVE_FAILED,
84
+ 'Failed to save Browser settings.',
85
+ error instanceof Error ? error.message : undefined,
86
+ );
42
87
  }
43
88
  });
44
89
 
45
90
  router.post('/runtime/install', async (_req, res) => {
46
91
  try {
47
92
  const result = await browserUseService.installRuntime();
48
- res.status(result.success ? 200 : 500).json({
49
- success: result.success,
50
- data: result,
51
- error: result.success ? undefined : result.message,
52
- });
93
+ if (result.success) {
94
+ res.json({ success: true, data: result });
95
+ return;
96
+ }
97
+ sendError(
98
+ res,
99
+ 500,
100
+ BROWSER_USE_ERROR_CODES.RUNTIME_INSTALL_FAILED,
101
+ 'Failed to install Browser runtime.',
102
+ result.message,
103
+ );
53
104
  } catch (error) {
54
- res.status(500).json({
55
- success: false,
56
- error: error instanceof Error ? error.message : 'Failed to install Browser runtime.',
57
- });
105
+ sendError(
106
+ res,
107
+ 500,
108
+ BROWSER_USE_ERROR_CODES.RUNTIME_INSTALL_FAILED,
109
+ 'Failed to install Browser runtime.',
110
+ error instanceof Error ? error.message : undefined,
111
+ );
58
112
  }
59
113
  });
60
114
 
@@ -62,10 +116,13 @@ router.get('/sessions', async (_req, res) => {
62
116
  try {
63
117
  res.json({ success: true, data: { sessions: await browserUseService.listSessions() } });
64
118
  } catch (error) {
65
- res.status(401).json({
66
- success: false,
67
- error: error instanceof Error ? error.message : 'Failed to list browser sessions.',
68
- });
119
+ sendError(
120
+ res,
121
+ 401,
122
+ BROWSER_USE_ERROR_CODES.SESSIONS_LOAD_FAILED,
123
+ 'Failed to list browser sessions.',
124
+ error instanceof Error ? error.message : undefined,
125
+ );
69
126
  }
70
127
  });
71
128
 
@@ -74,10 +131,13 @@ router.post('/sessions/:sessionId/stop', async (req, res) => {
74
131
  const result = await browserUseService.stopSession(readParam(req.params.sessionId));
75
132
  res.json({ success: true, data: result });
76
133
  } catch (error) {
77
- res.status(400).json({
78
- success: false,
79
- error: error instanceof Error ? error.message : 'Failed to stop browser session.',
80
- });
134
+ sendError(
135
+ res,
136
+ 400,
137
+ BROWSER_USE_ERROR_CODES.SESSION_STOP_FAILED,
138
+ 'Failed to stop browser session.',
139
+ error instanceof Error ? error.message : undefined,
140
+ );
81
141
  }
82
142
  });
83
143
 
@@ -86,10 +146,13 @@ router.delete('/sessions/:sessionId', async (req, res) => {
86
146
  const result = await browserUseService.deleteSession(readParam(req.params.sessionId));
87
147
  res.json({ success: true, data: result });
88
148
  } catch (error) {
89
- res.status(400).json({
90
- success: false,
91
- error: error instanceof Error ? error.message : 'Failed to delete browser session.',
92
- });
149
+ sendError(
150
+ res,
151
+ 400,
152
+ BROWSER_USE_ERROR_CODES.SESSION_DELETE_FAILED,
153
+ 'Failed to delete browser session.',
154
+ error instanceof Error ? error.message : undefined,
155
+ );
93
156
  }
94
157
  });
95
158
 
@@ -10,11 +10,12 @@
10
10
  * `getConnection()` to obtain the shared singleton.
11
11
  */
12
12
 
13
- import Database from 'better-sqlite3';
14
13
  import fs from 'fs';
15
14
  import path from 'path';
16
15
  import { fileURLToPath } from 'url';
17
16
 
17
+ import Database from 'better-sqlite3';
18
+
18
19
  import { APP_CONFIG_TABLE_SCHEMA_SQL } from '@/modules/database/schema.js';
19
20
 
20
21
  const __filename = fileURLToPath(import.meta.url);
@@ -12,6 +12,16 @@ export { providerModelsDb } from '@/modules/database/repositories/provider-model
12
12
  export { projectsDb } from '@/modules/database/repositories/projects.db.js';
13
13
  export { pushSubscriptionsDb } from '@/modules/database/repositories/push-subscriptions.js';
14
14
  export { scanStateDb } from '@/modules/database/repositories/scan-state.db.js';
15
+ // sessionDraftsDb: used by User for drafts and Scheduled Messages for server-owned queued turns.
16
+ export { sessionDraftsDb } from '@/modules/database/repositories/session-drafts.db.js';
17
+ export type {
18
+ QueuedSessionMessageRecord,
19
+ SessionDraftRecord,
20
+ } from '@/modules/database/repositories/session-drafts.db.js';
15
21
  export { sessionsDb } from '@/modules/database/repositories/sessions.db.js';
16
22
  export { userDb } from '@/modules/database/repositories/users.js';
23
+ // userPreferencesDb: used by the User module to persist the settings that used to live in browser localStorage.
24
+ export { userPreferencesDb } from '@/modules/database/repositories/user-preferences.db.js';
17
25
  export { vapidKeysDb } from '@/modules/database/repositories/vapid-keys.js';
26
+ export { scheduledMessagesDb } from './repositories/scheduled-messages.db.js';
27
+ export type { ScheduledMessageRow, ScheduledMessageStatus } from './repositories/scheduled-messages.db.js';