@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
@@ -17,6 +17,8 @@ export function createAgentRouter(dependencies) {
17
17
  const githubTokensDb = dependencies.githubTokens;
18
18
  const projectsDb = dependencies.projects;
19
19
  const providerModelsService = dependencies.models;
20
+ const sessionGateway = dependencies.sessions;
21
+ const runRegistry = dependencies.runs;
20
22
  const queryClaudeSDK = dependencies.queryClaude;
21
23
  const spawnCursor = dependencies.queryCursor;
22
24
  const queryCodex = dependencies.queryCodex;
@@ -321,13 +323,15 @@ export function createAgentRouter(dependencies) {
321
323
  await fs.mkdir(path.dirname(cloneDir), { recursive: true });
322
324
  console.log('🔄 Cloning repository:', githubUrl);
323
325
  console.log('📁 Destination:', cloneDir);
324
- // Execute git clone
326
+ // Execute git clone. The host was validated above; the helper is
327
+ // still scoped to github.com over https so the token can answer no
328
+ // other challenge (a redirect, say).
325
329
  const gitEnvironment = githubToken ? {
326
330
  ...process.env,
327
331
  GIT_CONFIG_COUNT: '2',
328
332
  GIT_CONFIG_KEY_0: 'credential.helper',
329
333
  GIT_CONFIG_VALUE_0: '',
330
- GIT_CONFIG_KEY_1: 'credential.helper',
334
+ GIT_CONFIG_KEY_1: 'credential.https://github.com.helper',
331
335
  GIT_CONFIG_VALUE_1: '!f() { echo username=x-access-token; echo "password=$CLOUDCLI_GITHUB_TOKEN"; }; f',
332
336
  CLOUDCLI_GITHUB_TOKEN: githubToken,
333
337
  GIT_TERMINAL_PROMPT: '0'
@@ -403,21 +407,27 @@ export function createAgentRouter(dependencies) {
403
407
  }
404
408
  }
405
409
  /**
406
- * SSE Stream Writer - Adapts SDK/CLI output to Server-Sent Events
410
+ * SSE Stream Writer - the HTTP response as one audience of a registered run.
411
+ *
412
+ * The provider runtime no longer writes here directly: it writes to the
413
+ * run's gateway writer, which remaps, sequences and buffers every event
414
+ * and forwards it — as a JSON string, the way it reaches a websocket — to
415
+ * each connection watching the run, this one included. The route's own
416
+ * events (status, session-id, GitHub results, done) are objects.
407
417
  */
408
418
  class SSEStreamWriter {
409
- constructor(res, userId = null) {
419
+ constructor(res) {
410
420
  this.res = res;
411
- this.sessionId = null;
412
- this.userId = userId;
413
- this.isSSEStreamWriter = true; // Marker for transport detection
421
+ }
422
+ /** What the gateway writer checks before forwarding: open until the response has ended. */
423
+ get readyState() {
424
+ return this.res.writableEnded ? 3 : 1;
414
425
  }
415
426
  send(data) {
416
427
  if (this.res.writableEnded) {
417
428
  return;
418
429
  }
419
- // Format as SSE - providers send raw objects, we stringify
420
- this.res.write(`data: ${JSON.stringify(data)}\n\n`);
430
+ this.res.write(`data: ${typeof data === 'string' ? data : JSON.stringify(data)}\n\n`);
421
431
  }
422
432
  end() {
423
433
  if (!this.res.writableEnded) {
@@ -425,118 +435,66 @@ export function createAgentRouter(dependencies) {
425
435
  this.res.end();
426
436
  }
427
437
  }
428
- setSessionId(sessionId) {
429
- this.sessionId = sessionId;
430
- this.send({ type: 'session-id', sessionId });
431
- }
432
- getSessionId() {
433
- return this.sessionId;
434
- }
435
438
  }
436
439
  /**
437
- * Non-streaming response collector
440
+ * Non-streaming response collector - the same audience role, kept in
441
+ * memory until the run ends.
438
442
  */
439
443
  class ResponseCollector {
440
- constructor(userId = null) {
444
+ constructor() {
441
445
  this.messages = [];
442
- this.sessionId = null;
443
- this.userId = userId;
444
446
  }
447
+ readyState = 1;
445
448
  send(data) {
446
- // Store ALL messages for now - we'll filter when returning
447
- this.messages.push(data);
448
- // Extract sessionId if present
449
+ // The run's events arrive as JSON strings; the route's own as objects.
450
+ // Stored as objects either way so the filters below read one shape.
451
+ let record = data;
449
452
  if (typeof data === 'string') {
450
453
  try {
451
- const parsed = JSON.parse(data);
452
- if (parsed.sessionId) {
453
- this.sessionId = parsed.sessionId;
454
- }
454
+ record = JSON.parse(data);
455
455
  }
456
456
  catch (e) {
457
- // Not JSON, ignore
457
+ // Not JSON, keep as is
458
458
  }
459
459
  }
460
- else if (data && data.sessionId) {
461
- this.sessionId = data.sessionId;
462
- }
460
+ this.messages.push(record);
463
461
  }
464
462
  end() {
465
463
  // Do nothing - we'll collect all messages
466
464
  }
467
- setSessionId(sessionId) {
468
- this.sessionId = sessionId;
469
- }
470
- getSessionId() {
471
- return this.sessionId;
472
- }
473
465
  getMessages() {
474
466
  return this.messages;
475
467
  }
476
468
  /**
477
- * Get filtered assistant messages only
469
+ * The assistant's replies, as the normalized `text` events the run
470
+ * produced. (Earlier versions filtered a `claude-response` shape no
471
+ * runtime has emitted since the providers were unified, so this was
472
+ * always empty.)
478
473
  */
479
474
  getAssistantMessages() {
480
- const assistantMessages = [];
481
- for (const msg of this.messages) {
482
- // Skip initial status message
483
- if (msg && msg.type === 'status') {
484
- continue;
485
- }
486
- // Handle JSON strings
487
- if (typeof msg === 'string') {
488
- try {
489
- const parsed = JSON.parse(msg);
490
- // Only include claude-response messages with assistant type
491
- if (parsed.type === 'claude-response' && parsed.data && parsed.data.type === 'assistant') {
492
- assistantMessages.push(parsed.data);
493
- }
494
- }
495
- catch (e) {
496
- // Not JSON, skip
497
- }
498
- }
499
- }
500
- return assistantMessages;
475
+ return this.messages.filter((msg) => msg && msg.kind === 'text' && msg.role === 'assistant');
501
476
  }
502
477
  /**
503
- * Calculate total tokens from all messages
478
+ * The run's token usage, from the last context-window report the
479
+ * runtime streamed (`status` / `token_budget`, which the Claude and Codex
480
+ * runtimes emit per assistant message); zeros for a runtime that
481
+ * reports none.
504
482
  */
505
483
  getTotalTokens() {
506
- let totalInput = 0;
507
- let totalOutput = 0;
508
- let totalCacheRead = 0;
509
- let totalCacheCreation = 0;
484
+ let budget = null;
510
485
  for (const msg of this.messages) {
511
- let data = msg;
512
- // Parse if string
513
- if (typeof msg === 'string') {
514
- try {
515
- data = JSON.parse(msg);
516
- }
517
- catch (e) {
518
- continue;
519
- }
520
- }
521
- // Extract usage from claude-response messages
522
- if (data && data.type === 'claude-response' && data.data) {
523
- const msgData = data.data;
524
- if (msgData.message && msgData.message.usage) {
525
- const usage = msgData.message.usage;
526
- totalInput += usage.input_tokens || 0;
527
- totalOutput += usage.output_tokens || 0;
528
- totalCacheRead += usage.cache_read_input_tokens || 0;
529
- totalCacheCreation += usage.cache_creation_input_tokens || 0;
530
- }
486
+ if (msg && msg.kind === 'status' && msg.text === 'token_budget' && msg.tokenBudget) {
487
+ budget = msg.tokenBudget;
531
488
  }
532
489
  }
533
- const inputTokens = totalInput + totalCacheRead + totalCacheCreation;
490
+ const inputTokens = budget?.inputTokens ?? 0;
491
+ const outputTokens = budget?.outputTokens ?? 0;
534
492
  return {
535
493
  inputTokens,
536
- outputTokens: totalOutput,
537
- cacheReadTokens: totalCacheRead,
538
- cacheCreationTokens: totalCacheCreation,
539
- totalTokens: inputTokens + totalOutput
494
+ outputTokens,
495
+ cacheReadTokens: budget?.cacheReadTokens ?? 0,
496
+ cacheCreationTokens: budget?.cacheCreationTokens ?? 0,
497
+ totalTokens: inputTokens + outputTokens
540
498
  };
541
499
  }
542
500
  }
@@ -579,6 +537,11 @@ export function createAgentRouter(dependencies) {
579
537
  * - true: Returns text/event-stream with incremental updates
580
538
  * - false: Returns complete JSON response after completion
581
539
  *
540
+ * @param {string} sessionId - (Optional) Continue an existing session: the id an earlier
541
+ * response reported as `sessionId` / in its `session-id` event.
542
+ * A session already mid-run is refused (409); an unknown id, 404.
543
+ * Omitted: a new session is created for the run.
544
+ *
582
545
  * @param {string} model - (Optional) Model identifier for providers.
583
546
  *
584
547
  * Claude models: 'default', 'sonnet', 'opus', 'haiku', 'sonnet[1m]', 'opus[1m]', 'fable'
@@ -716,11 +679,21 @@ export function createAgentRouter(dependencies) {
716
679
  * RESPONSE FORMATS
717
680
  * ================================================================================================
718
681
  *
682
+ * Every run is registered with the chat run registry like a message sent
683
+ * from the UI: it is on GET /api/providers/sessions/running while it goes,
684
+ * a tab that opens the session subscribes to it live, and its events
685
+ * reach this response the way they reach a tab — `sessionId` is the app
686
+ * session id, each event carries a `seq`, and the provider's own
687
+ * `session_created` is folded into the session row instead of forwarded.
688
+ *
719
689
  * Streaming Response (stream=true):
720
690
  * Content-Type: text/event-stream
721
691
  * Events:
722
692
  * - { type: "status", message: "...", projectPath: "..." }
723
- * - { type: "claude-response", data: {...} }
693
+ * - { type: "session-id", sessionId: "..." } // the app session id, usable as `sessionId` later
694
+ * - { kind: "text" | "tool_use" | ... , sessionId, seq, ... } // the provider's normalized events
695
+ * - { kind: "complete", ... }
696
+ * - { type: "status", message: "Run aborted", aborted: true } // only when a tab aborted the run; no branch/PR follows
724
697
  * - { type: "github-branch", branch: { name: "...", url: "..." } }
725
698
  * - { type: "github-pr", pullRequest: { number: 42, url: "..." } }
726
699
  * - { type: "github-error", error: "..." }
@@ -729,9 +702,10 @@ export function createAgentRouter(dependencies) {
729
702
  * Non-Streaming Response (stream=false):
730
703
  * Content-Type: application/json
731
704
  * {
732
- * success: true,
733
- * sessionId: "session-123",
734
- * messages: [...], // Assistant messages only (filtered)
705
+ * success: true, // false, with aborted: true, when a tab aborted the run
706
+ * sessionId: "session-123", // the app session id; pass it back as `sessionId` to continue
707
+ * providerSessionId: "native-id", // the provider's own id, for tooling that drives the CLI directly
708
+ * messages: [...], // The assistant's replies: the run's normalized `text` events
735
709
  * tokens: {
736
710
  * inputTokens: 150,
737
711
  * outputTokens: 50,
@@ -751,7 +725,7 @@ export function createAgentRouter(dependencies) {
751
725
  * }
752
726
  *
753
727
  * Error Response:
754
- * HTTP Status: 400, 401, 500
728
+ * HTTP Status: 400, 401, 404 (unknown sessionId), 409 (session already mid-run), 500
755
729
  * Content-Type: application/json
756
730
  * { success: false, error: "Error description" }
757
731
  *
@@ -783,7 +757,15 @@ export function createAgentRouter(dependencies) {
783
757
  * }
784
758
  */
785
759
  router.post('/', validateExternalApiKey, async (req, res) => {
786
- const { githubUrl, projectPath, message, provider = 'claude', model, githubToken, branchName, sessionId } = req.body;
760
+ const { githubUrl, projectPath, message, model, githubToken, branchName } = req.body;
761
+ // Transport input is typed here, before anything binds it to a query:
762
+ // the lookup below runs outside the try, where a throw would be an
763
+ // unhandled rejection.
764
+ if (req.body.sessionId !== undefined && req.body.sessionId !== null && typeof req.body.sessionId !== 'string') {
765
+ return res.status(400).json({ error: 'sessionId must be a string' });
766
+ }
767
+ const sessionId = typeof req.body.sessionId === 'string' && req.body.sessionId.trim() ? req.body.sessionId.trim() : null;
768
+ const requestedProvider = req.body.provider === undefined || req.body.provider === null ? null : req.body.provider;
787
769
  const effort = typeof req.body.effort === 'string' && req.body.effort.trim()
788
770
  ? req.body.effort.trim()
789
771
  : undefined;
@@ -800,7 +782,7 @@ export function createAgentRouter(dependencies) {
800
782
  if (!message || !message.trim()) {
801
783
  return res.status(400).json({ error: 'message is required' });
802
784
  }
803
- if (!['claude', 'cursor', 'codex', 'opencode'].includes(provider)) {
785
+ if (requestedProvider !== null && !['claude', 'cursor', 'codex', 'opencode'].includes(requestedProvider)) {
804
786
  return res.status(400).json({ error: 'provider must be "claude", "cursor", "codex", or "opencode"' });
805
787
  }
806
788
  // Validate GitHub branch/PR creation requirements
@@ -808,9 +790,33 @@ export function createAgentRouter(dependencies) {
808
790
  if ((createBranch || createPR) && !githubUrl && !projectPath) {
809
791
  return res.status(400).json({ error: 'createBranch and createPR require either githubUrl or projectPath with a GitHub remote' });
810
792
  }
793
+ // A run continues a session the caller names, by its app id or — for a
794
+ // caller that stored what an earlier response called `sessionId` — the
795
+ // provider-native one, which the row is also keyed by once indexed. The
796
+ // row, not the request, says which provider the session belongs to, as
797
+ // it does for a chat send; a request that names another is refused.
798
+ let sessionRow = null;
799
+ if (sessionId) {
800
+ sessionRow = sessionGateway.getSessionById(sessionId) ?? sessionGateway.getSessionByProviderSessionId(sessionId);
801
+ if (!sessionRow) {
802
+ return res.status(404).json({ error: `Session "${sessionId}" was not found` });
803
+ }
804
+ if (requestedProvider !== null && requestedProvider !== sessionRow.provider) {
805
+ return res.status(400).json({ error: `Session "${sessionRow.session_id}" belongs to provider "${sessionRow.provider}"` });
806
+ }
807
+ // Refused before any side effect — a clone per retry would pile up
808
+ // under external-projects. startRun below still has the last word.
809
+ if (runRegistry.isProcessing(sessionRow.session_id)) {
810
+ return res.status(409).json({ error: `Session "${sessionRow.session_id}" already has a run in progress` });
811
+ }
812
+ }
813
+ const provider = sessionRow?.provider ?? requestedProvider ?? 'claude';
811
814
  let finalProjectPath = null;
812
815
  let clonedProjectCreated = false;
813
816
  let writer = null;
817
+ // The run as the chat socket registers one: on the running-sessions
818
+ // list, subscribable from any tab, and completed on every exit path.
819
+ let run = null;
814
820
  try {
815
821
  // Determine the final project path
816
822
  if (githubUrl) {
@@ -841,6 +847,11 @@ export function createAgentRouter(dependencies) {
841
847
  }
842
848
  }
843
849
  finalProjectPath = normalizeProjectPath(finalProjectPath);
850
+ // A continued session runs where it lives, as a chat send does; a
851
+ // request that resolved to another directory is not that session.
852
+ if (sessionRow && sessionRow.project_path && normalizeProjectPath(sessionRow.project_path) !== finalProjectPath) {
853
+ throw new Error(`Session "${sessionRow.session_id}" belongs to project ${sessionRow.project_path}, not ${finalProjectPath}`);
854
+ }
844
855
  // Register project path in DB (or reuse existing active registration)
845
856
  const registrationResult = projectsDb.createProjectPath(finalProjectPath, null);
846
857
  if (registrationResult.outcome === 'active_conflict') {
@@ -849,81 +860,104 @@ export function createAgentRouter(dependencies) {
849
860
  else {
850
861
  console.log('Project registered:', registrationResult.project);
851
862
  }
852
- // Set up writer based on streaming mode
863
+ // A brand-new run gets its app session row first, as a chat send does:
864
+ // the id is stable for the conversation, the sidebar can list it, and
865
+ // the provider-native id is mapped onto it when the runtime announces it.
866
+ const appSessionId = sessionRow
867
+ ? sessionRow.session_id
868
+ : sessionGateway.createAppSession(provider, finalProjectPath, message.trim()).sessionId;
869
+ // Registered before any header goes out, so a session already mid-run
870
+ // is refused with a plain 409 rather than an empty stream. The HTTP
871
+ // response is the run's first audience; a tab that opens the session
872
+ // subscribes as another, and replays what it missed.
873
+ const audience = stream ? new SSEStreamWriter(res) : new ResponseCollector();
874
+ run = runRegistry.startRun({
875
+ appSessionId,
876
+ provider,
877
+ providerSessionId: sessionRow?.provider_session_id ?? null,
878
+ connection: audience,
879
+ userId: req.user.id,
880
+ });
881
+ if (!run) {
882
+ if (cleanup && githubUrl && clonedProjectCreated) {
883
+ cleanupProject(finalProjectPath, null);
884
+ }
885
+ return res.status(409).json({ error: `Session "${appSessionId}" already has a run in progress` });
886
+ }
887
+ writer = audience;
853
888
  if (stream) {
854
889
  // Set up SSE headers for streaming
855
890
  res.setHeader('Content-Type', 'text/event-stream');
856
891
  res.setHeader('Cache-Control', 'no-cache');
857
892
  res.setHeader('Connection', 'keep-alive');
858
893
  res.setHeader('X-Accel-Buffering', 'no'); // Disable nginx buffering
859
- writer = new SSEStreamWriter(res, req.user.id);
860
- // Send initial status
861
- writer.send({
862
- type: 'status',
863
- message: githubUrl ? 'Repository cloned and session started' : 'Session started',
864
- projectPath: finalProjectPath
865
- });
866
- }
867
- else {
868
- // Non-streaming mode: collect messages
869
- writer = new ResponseCollector(req.user.id);
870
- // Collect initial status message
871
- writer.send({
872
- type: 'status',
873
- message: githubUrl ? 'Repository cloned and session started' : 'Session started',
874
- projectPath: finalProjectPath
875
- });
876
894
  }
877
- const codexModels = (await providerModelsService.getProviderModels('codex')).models;
878
- const opencodeModels = (await providerModelsService.getProviderModels('opencode')).models;
895
+ // The route's own opening events. `session-id` is the app session id:
896
+ // what the UI opens the conversation under, and what a later request
897
+ // passes back as `sessionId` to continue it.
898
+ writer.send({
899
+ type: 'status',
900
+ message: githubUrl ? 'Repository cloned and session started' : 'Session started',
901
+ projectPath: finalProjectPath
902
+ });
903
+ writer.send({ type: 'session-id', sessionId: appSessionId });
904
+ const codexModels = await providerModelsService.getProviderModels('codex');
905
+ const opencodeModels = await providerModelsService.getProviderModels('opencode');
879
906
  // Start the appropriate session
880
907
  if (provider === 'claude') {
881
908
  console.log('🤖 Starting Claude SDK session');
882
909
  await queryClaudeSDK(message.trim(), {
883
910
  projectPath: finalProjectPath,
884
911
  cwd: finalProjectPath,
885
- sessionId: sessionId || null,
912
+ sessionId: appSessionId,
886
913
  model: model,
887
914
  effort,
888
915
  permissionMode: 'bypassPermissions' // Bypass all permissions for API calls
889
- }, writer);
916
+ }, run.writer);
890
917
  }
891
918
  else if (provider === 'cursor') {
892
919
  console.log('🖱️ Starting Cursor CLI session');
893
920
  await spawnCursor(message.trim(), {
894
921
  projectPath: finalProjectPath,
895
922
  cwd: finalProjectPath,
896
- sessionId: sessionId || null,
923
+ sessionId: appSessionId,
897
924
  model: model || undefined,
898
925
  skipPermissions: true // Bypass permissions for Cursor
899
- }, writer);
926
+ }, run.writer);
900
927
  }
901
928
  else if (provider === 'codex') {
902
929
  console.log('🤖 Starting Codex SDK session');
903
930
  await queryCodex(message.trim(), {
904
931
  projectPath: finalProjectPath,
905
932
  cwd: finalProjectPath,
906
- sessionId: sessionId || null,
933
+ sessionId: appSessionId,
907
934
  model: model || codexModels.DEFAULT,
908
935
  effort,
909
936
  permissionMode: 'bypassPermissions'
910
- }, writer);
937
+ }, run.writer);
911
938
  }
912
939
  else if (provider === 'opencode') {
913
940
  console.log('Starting OpenCode CLI session');
914
941
  await spawnOpenCode(message.trim(), {
915
942
  projectPath: finalProjectPath,
916
943
  cwd: finalProjectPath,
917
- sessionId: sessionId || null,
944
+ sessionId: appSessionId,
918
945
  model: model || opencodeModels.DEFAULT,
919
946
  effort,
920
947
  permissionMode: 'bypassPermissions' // Agent runs are non-interactive, like the other providers above
921
- }, writer);
948
+ }, run.writer);
949
+ }
950
+ // A tab can now abort the run (`chat.abort`), on which the runtime
951
+ // returns as it does on completion. What the interrupted agent left
952
+ // behind is not a result to branch or open a PR from.
953
+ const aborted = run.events.some((event) => event.kind === 'complete' && event.aborted === true);
954
+ if (aborted) {
955
+ writer.send({ type: 'status', message: 'Run aborted', aborted: true });
922
956
  }
923
957
  // Handle GitHub branch and PR creation after successful agent completion
924
958
  let branchInfo = null;
925
959
  let prInfo = null;
926
- if (createBranch || createPR) {
960
+ if (!aborted && (createBranch || createPR)) {
927
961
  try {
928
962
  console.log('🔄 Starting GitHub branch/PR creation workflow...');
929
963
  // Get GitHub token
@@ -1098,8 +1132,12 @@ export function createAgentRouter(dependencies) {
1098
1132
  const assistantMessages = writer.getAssistantMessages();
1099
1133
  const tokenSummary = writer.getTotalTokens();
1100
1134
  const response = {
1101
- success: true,
1102
- sessionId: writer.getSessionId(),
1135
+ success: !aborted,
1136
+ ...(aborted ? { aborted: true } : {}),
1137
+ sessionId: appSessionId,
1138
+ // The provider-native id, for a caller that drives the CLI itself
1139
+ // (`claude --resume`); `sessionId` is what this API and the UI use.
1140
+ providerSessionId: run.writer.getSessionId(),
1103
1141
  messages: assistantMessages,
1104
1142
  tokens: tokenSummary,
1105
1143
  projectPath: finalProjectPath
@@ -1116,7 +1154,7 @@ export function createAgentRouter(dependencies) {
1116
1154
  // Clean up if requested
1117
1155
  if (cleanup && githubUrl && clonedProjectCreated) {
1118
1156
  // Only cleanup if we cloned a repo (not for existing project paths)
1119
- const sessionIdForCleanup = writer.getSessionId();
1157
+ const sessionIdForCleanup = run.writer.getSessionId();
1120
1158
  setTimeout(() => {
1121
1159
  cleanupProject(finalProjectPath, sessionIdForCleanup);
1122
1160
  }, 5000);
@@ -1126,7 +1164,7 @@ export function createAgentRouter(dependencies) {
1126
1164
  console.error('❌ External session error:', error);
1127
1165
  // Clean up on error
1128
1166
  if (finalProjectPath && cleanup && githubUrl && clonedProjectCreated) {
1129
- const sessionIdForCleanup = writer ? writer.getSessionId() : null;
1167
+ const sessionIdForCleanup = run ? run.writer.getSessionId() : null;
1130
1168
  cleanupProject(finalProjectPath, sessionIdForCleanup);
1131
1169
  }
1132
1170
  if (stream) {
@@ -1137,7 +1175,7 @@ export function createAgentRouter(dependencies) {
1137
1175
  res.setHeader('Cache-Control', 'no-cache');
1138
1176
  res.setHeader('Connection', 'keep-alive');
1139
1177
  res.setHeader('X-Accel-Buffering', 'no');
1140
- writer = new SSEStreamWriter(res, req.user.id);
1178
+ writer = new SSEStreamWriter(res);
1141
1179
  }
1142
1180
  if (!res.writableEnded) {
1143
1181
  writer.send({
@@ -1155,6 +1193,15 @@ export function createAgentRouter(dependencies) {
1155
1193
  });
1156
1194
  }
1157
1195
  }
1196
+ finally {
1197
+ // Safety net, as for a chat send: a runtime that threw or resolved
1198
+ // without its terminal `complete` would leave the session listed as
1199
+ // running in every tab. Scoped to this run, so a run the session
1200
+ // started meanwhile is left alone.
1201
+ if (run) {
1202
+ runRegistry.completeRunIfCurrent(run, { exitCode: 1 });
1203
+ }
1204
+ }
1158
1205
  });
1159
1206
  return router;
1160
1207
  }