@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
@@ -7,6 +7,14 @@ import type { ProviderRunFunction } from '@/shared/types.js';
7
7
 
8
8
  import { normalizeProjectPath } from '../../shared/utils.js';
9
9
 
10
+ /** What the route reads off a session row it continues: the row, not the request, says which provider and project a session belongs to. */
11
+ type AgentSessionRow = {
12
+ session_id: string;
13
+ provider: string;
14
+ provider_session_id: string | null;
15
+ project_path: string | null;
16
+ };
17
+
10
18
  type AgentRouterDependencies = {
11
19
  fileSystem: typeof import('node:fs/promises');
12
20
  crypto: typeof import('node:crypto');
@@ -18,6 +26,14 @@ type AgentRouterDependencies = {
18
26
  githubTokens: { getActiveGithubToken(userId: number): string | null };
19
27
  projects: { createProjectPath(projectPath: string, customName: string | null): unknown };
20
28
  models: typeof import('../providers/index.js').providerModelsService;
29
+ /** The session gateway: an API run gets an app session row like a chat send, so the UI can list, open and subscribe to it. */
30
+ sessions: {
31
+ getSessionById(sessionId: string): AgentSessionRow | null;
32
+ getSessionByProviderSessionId(providerSessionId: string): AgentSessionRow | null;
33
+ createAppSession(provider: string, projectPath: string, initialMessage: string): { sessionId: string };
34
+ };
35
+ /** The live-run registry the chat socket uses; registering here is what puts an API run on the running-sessions list. */
36
+ runs: Pick<typeof import('../websocket/index.js').chatRunRegistry, 'startRun' | 'completeRunIfCurrent' | 'isProcessing'>;
21
37
  queryClaude: ProviderRunFunction;
22
38
  queryCursor: ProviderRunFunction;
23
39
  queryCodex: ProviderRunFunction;
@@ -40,6 +56,8 @@ export function createAgentRouter(dependencies: AgentRouterDependencies): expres
40
56
  const githubTokensDb = dependencies.githubTokens;
41
57
  const projectsDb = dependencies.projects;
42
58
  const providerModelsService = dependencies.models;
59
+ const sessionGateway = dependencies.sessions;
60
+ const runRegistry = dependencies.runs;
43
61
  const queryClaudeSDK = dependencies.queryClaude;
44
62
  const spawnCursor = dependencies.queryCursor;
45
63
  const queryCodex = dependencies.queryCodex;
@@ -384,13 +402,15 @@ export function createAgentRouter(dependencies: AgentRouterDependencies): expres
384
402
  console.log('🔄 Cloning repository:', githubUrl);
385
403
  console.log('📁 Destination:', cloneDir);
386
404
 
387
- // Execute git clone
405
+ // Execute git clone. The host was validated above; the helper is
406
+ // still scoped to github.com over https so the token can answer no
407
+ // other challenge (a redirect, say).
388
408
  const gitEnvironment = githubToken ? {
389
409
  ...process.env,
390
410
  GIT_CONFIG_COUNT: '2',
391
411
  GIT_CONFIG_KEY_0: 'credential.helper',
392
412
  GIT_CONFIG_VALUE_0: '',
393
- GIT_CONFIG_KEY_1: 'credential.helper',
413
+ GIT_CONFIG_KEY_1: 'credential.https://github.com.helper',
394
414
  GIT_CONFIG_VALUE_1: '!f() { echo username=x-access-token; echo "password=$CLOUDCLI_GITHUB_TOKEN"; }; f',
395
415
  CLOUDCLI_GITHUB_TOKEN: githubToken,
396
416
  GIT_TERMINAL_PROMPT: '0'
@@ -474,14 +494,22 @@ export function createAgentRouter(dependencies: AgentRouterDependencies): expres
474
494
  }
475
495
 
476
496
  /**
477
- * SSE Stream Writer - Adapts SDK/CLI output to Server-Sent Events
497
+ * SSE Stream Writer - the HTTP response as one audience of a registered run.
498
+ *
499
+ * The provider runtime no longer writes here directly: it writes to the
500
+ * run's gateway writer, which remaps, sequences and buffers every event
501
+ * and forwards it — as a JSON string, the way it reaches a websocket — to
502
+ * each connection watching the run, this one included. The route's own
503
+ * events (status, session-id, GitHub results, done) are objects.
478
504
  */
479
505
  class SSEStreamWriter {
480
- constructor(res, userId = null) {
506
+ constructor(res) {
481
507
  this.res = res;
482
- this.sessionId = null;
483
- this.userId = userId;
484
- this.isSSEStreamWriter = true; // Marker for transport detection
508
+ }
509
+
510
+ /** What the gateway writer checks before forwarding: open until the response has ended. */
511
+ get readyState() {
512
+ return this.res.writableEnded ? 3 : 1;
485
513
  }
486
514
 
487
515
  send(data) {
@@ -489,8 +517,7 @@ export function createAgentRouter(dependencies: AgentRouterDependencies): expres
489
517
  return;
490
518
  }
491
519
 
492
- // Format as SSE - providers send raw objects, we stringify
493
- this.res.write(`data: ${JSON.stringify(data)}\n\n`);
520
+ this.res.write(`data: ${typeof data === 'string' ? data : JSON.stringify(data)}\n\n`);
494
521
  }
495
522
 
496
523
  end() {
@@ -499,133 +526,72 @@ export function createAgentRouter(dependencies: AgentRouterDependencies): expres
499
526
  this.res.end();
500
527
  }
501
528
  }
502
-
503
- setSessionId(sessionId) {
504
- this.sessionId = sessionId;
505
- this.send({ type: 'session-id', sessionId });
506
- }
507
-
508
- getSessionId() {
509
- return this.sessionId;
510
- }
511
529
  }
512
530
 
513
531
  /**
514
- * Non-streaming response collector
532
+ * Non-streaming response collector - the same audience role, kept in
533
+ * memory until the run ends.
515
534
  */
516
535
  class ResponseCollector {
517
- constructor(userId = null) {
536
+ constructor() {
518
537
  this.messages = [];
519
- this.sessionId = null;
520
- this.userId = userId;
521
538
  }
522
539
 
523
- send(data) {
524
- // Store ALL messages for now - we'll filter when returning
525
- this.messages.push(data);
540
+ readyState = 1;
526
541
 
527
- // Extract sessionId if present
542
+ send(data) {
543
+ // The run's events arrive as JSON strings; the route's own as objects.
544
+ // Stored as objects either way so the filters below read one shape.
545
+ let record = data;
528
546
  if (typeof data === 'string') {
529
547
  try {
530
- const parsed = JSON.parse(data);
531
- if (parsed.sessionId) {
532
- this.sessionId = parsed.sessionId;
533
- }
548
+ record = JSON.parse(data);
534
549
  } catch (e) {
535
- // Not JSON, ignore
550
+ // Not JSON, keep as is
536
551
  }
537
- } else if (data && data.sessionId) {
538
- this.sessionId = data.sessionId;
539
552
  }
553
+ this.messages.push(record);
540
554
  }
541
555
 
542
556
  end() {
543
557
  // Do nothing - we'll collect all messages
544
558
  }
545
559
 
546
- setSessionId(sessionId) {
547
- this.sessionId = sessionId;
548
- }
549
-
550
- getSessionId() {
551
- return this.sessionId;
552
- }
553
-
554
560
  getMessages() {
555
561
  return this.messages;
556
562
  }
557
563
 
558
564
  /**
559
- * Get filtered assistant messages only
565
+ * The assistant's replies, as the normalized `text` events the run
566
+ * produced. (Earlier versions filtered a `claude-response` shape no
567
+ * runtime has emitted since the providers were unified, so this was
568
+ * always empty.)
560
569
  */
561
570
  getAssistantMessages() {
562
- const assistantMessages = [];
563
-
564
- for (const msg of this.messages) {
565
- // Skip initial status message
566
- if (msg && msg.type === 'status') {
567
- continue;
568
- }
569
-
570
- // Handle JSON strings
571
- if (typeof msg === 'string') {
572
- try {
573
- const parsed = JSON.parse(msg);
574
- // Only include claude-response messages with assistant type
575
- if (parsed.type === 'claude-response' && parsed.data && parsed.data.type === 'assistant') {
576
- assistantMessages.push(parsed.data);
577
- }
578
- } catch (e) {
579
- // Not JSON, skip
580
- }
581
- }
582
- }
583
-
584
- return assistantMessages;
571
+ return this.messages.filter((msg) => msg && msg.kind === 'text' && msg.role === 'assistant');
585
572
  }
586
573
 
587
574
  /**
588
- * Calculate total tokens from all messages
575
+ * The run's token usage, from the last context-window report the
576
+ * runtime streamed (`status` / `token_budget`, which the Claude and Codex
577
+ * runtimes emit per assistant message); zeros for a runtime that
578
+ * reports none.
589
579
  */
590
580
  getTotalTokens() {
591
- let totalInput = 0;
592
- let totalOutput = 0;
593
- let totalCacheRead = 0;
594
- let totalCacheCreation = 0;
595
-
581
+ let budget = null;
596
582
  for (const msg of this.messages) {
597
- let data = msg;
598
-
599
- // Parse if string
600
- if (typeof msg === 'string') {
601
- try {
602
- data = JSON.parse(msg);
603
- } catch (e) {
604
- continue;
605
- }
606
- }
607
-
608
- // Extract usage from claude-response messages
609
- if (data && data.type === 'claude-response' && data.data) {
610
- const msgData = data.data;
611
- if (msgData.message && msgData.message.usage) {
612
- const usage = msgData.message.usage;
613
- totalInput += usage.input_tokens || 0;
614
- totalOutput += usage.output_tokens || 0;
615
- totalCacheRead += usage.cache_read_input_tokens || 0;
616
- totalCacheCreation += usage.cache_creation_input_tokens || 0;
617
- }
583
+ if (msg && msg.kind === 'status' && msg.text === 'token_budget' && msg.tokenBudget) {
584
+ budget = msg.tokenBudget;
618
585
  }
619
586
  }
620
-
621
- const inputTokens = totalInput + totalCacheRead + totalCacheCreation;
622
-
587
+ const inputTokens = budget?.inputTokens ?? 0;
588
+ const outputTokens = budget?.outputTokens ?? 0;
623
589
  return {
624
590
  inputTokens,
625
- outputTokens: totalOutput,
626
- cacheReadTokens: totalCacheRead,
627
- cacheCreationTokens: totalCacheCreation,
628
- totalTokens: inputTokens + totalOutput
591
+ outputTokens,
592
+ cacheReadTokens: budget?.cacheReadTokens ?? 0,
593
+ cacheCreationTokens: budget?.cacheCreationTokens ?? 0,
594
+ totalTokens: inputTokens + outputTokens
629
595
  };
630
596
  }
631
597
  }
@@ -670,6 +636,11 @@ export function createAgentRouter(dependencies: AgentRouterDependencies): expres
670
636
  * - true: Returns text/event-stream with incremental updates
671
637
  * - false: Returns complete JSON response after completion
672
638
  *
639
+ * @param {string} sessionId - (Optional) Continue an existing session: the id an earlier
640
+ * response reported as `sessionId` / in its `session-id` event.
641
+ * A session already mid-run is refused (409); an unknown id, 404.
642
+ * Omitted: a new session is created for the run.
643
+ *
673
644
  * @param {string} model - (Optional) Model identifier for providers.
674
645
  *
675
646
  * Claude models: 'default', 'sonnet', 'opus', 'haiku', 'sonnet[1m]', 'opus[1m]', 'fable'
@@ -807,11 +778,21 @@ export function createAgentRouter(dependencies: AgentRouterDependencies): expres
807
778
  * RESPONSE FORMATS
808
779
  * ================================================================================================
809
780
  *
781
+ * Every run is registered with the chat run registry like a message sent
782
+ * from the UI: it is on GET /api/providers/sessions/running while it goes,
783
+ * a tab that opens the session subscribes to it live, and its events
784
+ * reach this response the way they reach a tab — `sessionId` is the app
785
+ * session id, each event carries a `seq`, and the provider's own
786
+ * `session_created` is folded into the session row instead of forwarded.
787
+ *
810
788
  * Streaming Response (stream=true):
811
789
  * Content-Type: text/event-stream
812
790
  * Events:
813
791
  * - { type: "status", message: "...", projectPath: "..." }
814
- * - { type: "claude-response", data: {...} }
792
+ * - { type: "session-id", sessionId: "..." } // the app session id, usable as `sessionId` later
793
+ * - { kind: "text" | "tool_use" | ... , sessionId, seq, ... } // the provider's normalized events
794
+ * - { kind: "complete", ... }
795
+ * - { type: "status", message: "Run aborted", aborted: true } // only when a tab aborted the run; no branch/PR follows
815
796
  * - { type: "github-branch", branch: { name: "...", url: "..." } }
816
797
  * - { type: "github-pr", pullRequest: { number: 42, url: "..." } }
817
798
  * - { type: "github-error", error: "..." }
@@ -820,9 +801,10 @@ export function createAgentRouter(dependencies: AgentRouterDependencies): expres
820
801
  * Non-Streaming Response (stream=false):
821
802
  * Content-Type: application/json
822
803
  * {
823
- * success: true,
824
- * sessionId: "session-123",
825
- * messages: [...], // Assistant messages only (filtered)
804
+ * success: true, // false, with aborted: true, when a tab aborted the run
805
+ * sessionId: "session-123", // the app session id; pass it back as `sessionId` to continue
806
+ * providerSessionId: "native-id", // the provider's own id, for tooling that drives the CLI directly
807
+ * messages: [...], // The assistant's replies: the run's normalized `text` events
826
808
  * tokens: {
827
809
  * inputTokens: 150,
828
810
  * outputTokens: 50,
@@ -842,7 +824,7 @@ export function createAgentRouter(dependencies: AgentRouterDependencies): expres
842
824
  * }
843
825
  *
844
826
  * Error Response:
845
- * HTTP Status: 400, 401, 500
827
+ * HTTP Status: 400, 401, 404 (unknown sessionId), 409 (session already mid-run), 500
846
828
  * Content-Type: application/json
847
829
  * { success: false, error: "Error description" }
848
830
  *
@@ -874,7 +856,15 @@ export function createAgentRouter(dependencies: AgentRouterDependencies): expres
874
856
  * }
875
857
  */
876
858
  router.post('/', validateExternalApiKey, async (req, res) => {
877
- const { githubUrl, projectPath, message, provider = 'claude', model, githubToken, branchName, sessionId } = req.body;
859
+ const { githubUrl, projectPath, message, model, githubToken, branchName } = req.body;
860
+ // Transport input is typed here, before anything binds it to a query:
861
+ // the lookup below runs outside the try, where a throw would be an
862
+ // unhandled rejection.
863
+ if (req.body.sessionId !== undefined && req.body.sessionId !== null && typeof req.body.sessionId !== 'string') {
864
+ return res.status(400).json({ error: 'sessionId must be a string' });
865
+ }
866
+ const sessionId = typeof req.body.sessionId === 'string' && req.body.sessionId.trim() ? req.body.sessionId.trim() : null;
867
+ const requestedProvider = req.body.provider === undefined || req.body.provider === null ? null : req.body.provider;
878
868
  const effort = typeof req.body.effort === 'string' && req.body.effort.trim()
879
869
  ? req.body.effort.trim()
880
870
  : undefined;
@@ -896,7 +886,7 @@ export function createAgentRouter(dependencies: AgentRouterDependencies): expres
896
886
  return res.status(400).json({ error: 'message is required' });
897
887
  }
898
888
 
899
- if (!['claude', 'cursor', 'codex', 'opencode'].includes(provider)) {
889
+ if (requestedProvider !== null && !['claude', 'cursor', 'codex', 'opencode'].includes(requestedProvider)) {
900
890
  return res.status(400).json({ error: 'provider must be "claude", "cursor", "codex", or "opencode"' });
901
891
  }
902
892
 
@@ -906,9 +896,34 @@ export function createAgentRouter(dependencies: AgentRouterDependencies): expres
906
896
  return res.status(400).json({ error: 'createBranch and createPR require either githubUrl or projectPath with a GitHub remote' });
907
897
  }
908
898
 
899
+ // A run continues a session the caller names, by its app id or — for a
900
+ // caller that stored what an earlier response called `sessionId` — the
901
+ // provider-native one, which the row is also keyed by once indexed. The
902
+ // row, not the request, says which provider the session belongs to, as
903
+ // it does for a chat send; a request that names another is refused.
904
+ let sessionRow = null;
905
+ if (sessionId) {
906
+ sessionRow = sessionGateway.getSessionById(sessionId) ?? sessionGateway.getSessionByProviderSessionId(sessionId);
907
+ if (!sessionRow) {
908
+ return res.status(404).json({ error: `Session "${sessionId}" was not found` });
909
+ }
910
+ if (requestedProvider !== null && requestedProvider !== sessionRow.provider) {
911
+ return res.status(400).json({ error: `Session "${sessionRow.session_id}" belongs to provider "${sessionRow.provider}"` });
912
+ }
913
+ // Refused before any side effect — a clone per retry would pile up
914
+ // under external-projects. startRun below still has the last word.
915
+ if (runRegistry.isProcessing(sessionRow.session_id)) {
916
+ return res.status(409).json({ error: `Session "${sessionRow.session_id}" already has a run in progress` });
917
+ }
918
+ }
919
+ const provider = sessionRow?.provider ?? requestedProvider ?? 'claude';
920
+
909
921
  let finalProjectPath = null;
910
922
  let clonedProjectCreated = false;
911
923
  let writer = null;
924
+ // The run as the chat socket registers one: on the running-sessions
925
+ // list, subscribable from any tab, and completed on every exit path.
926
+ let run = null;
912
927
 
913
928
  try {
914
929
  // Determine the final project path
@@ -942,6 +957,12 @@ export function createAgentRouter(dependencies: AgentRouterDependencies): expres
942
957
 
943
958
  finalProjectPath = normalizeProjectPath(finalProjectPath);
944
959
 
960
+ // A continued session runs where it lives, as a chat send does; a
961
+ // request that resolved to another directory is not that session.
962
+ if (sessionRow && sessionRow.project_path && normalizeProjectPath(sessionRow.project_path) !== finalProjectPath) {
963
+ throw new Error(`Session "${sessionRow.session_id}" belongs to project ${sessionRow.project_path}, not ${finalProjectPath}`);
964
+ }
965
+
945
966
  // Register project path in DB (or reuse existing active registration)
946
967
  const registrationResult = projectsDb.createProjectPath(finalProjectPath, null);
947
968
  if (registrationResult.outcome === 'active_conflict') {
@@ -950,36 +971,53 @@ export function createAgentRouter(dependencies: AgentRouterDependencies): expres
950
971
  console.log('Project registered:', registrationResult.project);
951
972
  }
952
973
 
953
- // Set up writer based on streaming mode
974
+ // A brand-new run gets its app session row first, as a chat send does:
975
+ // the id is stable for the conversation, the sidebar can list it, and
976
+ // the provider-native id is mapped onto it when the runtime announces it.
977
+ const appSessionId = sessionRow
978
+ ? sessionRow.session_id
979
+ : sessionGateway.createAppSession(provider, finalProjectPath, message.trim()).sessionId;
980
+
981
+ // Registered before any header goes out, so a session already mid-run
982
+ // is refused with a plain 409 rather than an empty stream. The HTTP
983
+ // response is the run's first audience; a tab that opens the session
984
+ // subscribes as another, and replays what it missed.
985
+ const audience = stream ? new SSEStreamWriter(res) : new ResponseCollector();
986
+ run = runRegistry.startRun({
987
+ appSessionId,
988
+ provider,
989
+ providerSessionId: sessionRow?.provider_session_id ?? null,
990
+ connection: audience,
991
+ userId: req.user.id,
992
+ });
993
+ if (!run) {
994
+ if (cleanup && githubUrl && clonedProjectCreated) {
995
+ cleanupProject(finalProjectPath, null);
996
+ }
997
+ return res.status(409).json({ error: `Session "${appSessionId}" already has a run in progress` });
998
+ }
999
+ writer = audience;
1000
+
954
1001
  if (stream) {
955
1002
  // Set up SSE headers for streaming
956
1003
  res.setHeader('Content-Type', 'text/event-stream');
957
1004
  res.setHeader('Cache-Control', 'no-cache');
958
1005
  res.setHeader('Connection', 'keep-alive');
959
1006
  res.setHeader('X-Accel-Buffering', 'no'); // Disable nginx buffering
960
-
961
- writer = new SSEStreamWriter(res, req.user.id);
962
-
963
- // Send initial status
964
- writer.send({
965
- type: 'status',
966
- message: githubUrl ? 'Repository cloned and session started' : 'Session started',
967
- projectPath: finalProjectPath
968
- });
969
- } else {
970
- // Non-streaming mode: collect messages
971
- writer = new ResponseCollector(req.user.id);
972
-
973
- // Collect initial status message
974
- writer.send({
975
- type: 'status',
976
- message: githubUrl ? 'Repository cloned and session started' : 'Session started',
977
- projectPath: finalProjectPath
978
- });
979
1007
  }
980
1008
 
981
- const codexModels = (await providerModelsService.getProviderModels('codex')).models;
982
- const opencodeModels = (await providerModelsService.getProviderModels('opencode')).models;
1009
+ // The route's own opening events. `session-id` is the app session id:
1010
+ // what the UI opens the conversation under, and what a later request
1011
+ // passes back as `sessionId` to continue it.
1012
+ writer.send({
1013
+ type: 'status',
1014
+ message: githubUrl ? 'Repository cloned and session started' : 'Session started',
1015
+ projectPath: finalProjectPath
1016
+ });
1017
+ writer.send({ type: 'session-id', sessionId: appSessionId });
1018
+
1019
+ const codexModels = await providerModelsService.getProviderModels('codex');
1020
+ const opencodeModels = await providerModelsService.getProviderModels('opencode');
983
1021
 
984
1022
  // Start the appropriate session
985
1023
  if (provider === 'claude') {
@@ -988,11 +1026,11 @@ export function createAgentRouter(dependencies: AgentRouterDependencies): expres
988
1026
  await queryClaudeSDK(message.trim(), {
989
1027
  projectPath: finalProjectPath,
990
1028
  cwd: finalProjectPath,
991
- sessionId: sessionId || null,
1029
+ sessionId: appSessionId,
992
1030
  model: model,
993
1031
  effort,
994
1032
  permissionMode: 'bypassPermissions' // Bypass all permissions for API calls
995
- }, writer);
1033
+ }, run.writer);
996
1034
 
997
1035
  } else if (provider === 'cursor') {
998
1036
  console.log('🖱️ Starting Cursor CLI session');
@@ -1000,39 +1038,47 @@ export function createAgentRouter(dependencies: AgentRouterDependencies): expres
1000
1038
  await spawnCursor(message.trim(), {
1001
1039
  projectPath: finalProjectPath,
1002
1040
  cwd: finalProjectPath,
1003
- sessionId: sessionId || null,
1041
+ sessionId: appSessionId,
1004
1042
  model: model || undefined,
1005
1043
  skipPermissions: true // Bypass permissions for Cursor
1006
- }, writer);
1044
+ }, run.writer);
1007
1045
  } else if (provider === 'codex') {
1008
1046
  console.log('🤖 Starting Codex SDK session');
1009
1047
 
1010
1048
  await queryCodex(message.trim(), {
1011
1049
  projectPath: finalProjectPath,
1012
1050
  cwd: finalProjectPath,
1013
- sessionId: sessionId || null,
1051
+ sessionId: appSessionId,
1014
1052
  model: model || codexModels.DEFAULT,
1015
1053
  effort,
1016
1054
  permissionMode: 'bypassPermissions'
1017
- }, writer);
1055
+ }, run.writer);
1018
1056
  } else if (provider === 'opencode') {
1019
1057
  console.log('Starting OpenCode CLI session');
1020
1058
 
1021
1059
  await spawnOpenCode(message.trim(), {
1022
1060
  projectPath: finalProjectPath,
1023
1061
  cwd: finalProjectPath,
1024
- sessionId: sessionId || null,
1062
+ sessionId: appSessionId,
1025
1063
  model: model || opencodeModels.DEFAULT,
1026
1064
  effort,
1027
1065
  permissionMode: 'bypassPermissions' // Agent runs are non-interactive, like the other providers above
1028
- }, writer);
1066
+ }, run.writer);
1067
+ }
1068
+
1069
+ // A tab can now abort the run (`chat.abort`), on which the runtime
1070
+ // returns as it does on completion. What the interrupted agent left
1071
+ // behind is not a result to branch or open a PR from.
1072
+ const aborted = run.events.some((event) => event.kind === 'complete' && event.aborted === true);
1073
+ if (aborted) {
1074
+ writer.send({ type: 'status', message: 'Run aborted', aborted: true });
1029
1075
  }
1030
1076
 
1031
1077
  // Handle GitHub branch and PR creation after successful agent completion
1032
1078
  let branchInfo = null;
1033
1079
  let prInfo = null;
1034
1080
 
1035
- if (createBranch || createPR) {
1081
+ if (!aborted && (createBranch || createPR)) {
1036
1082
  try {
1037
1083
  console.log('🔄 Starting GitHub branch/PR creation workflow...');
1038
1084
 
@@ -1219,8 +1265,12 @@ export function createAgentRouter(dependencies: AgentRouterDependencies): expres
1219
1265
  const tokenSummary = writer.getTotalTokens();
1220
1266
 
1221
1267
  const response = {
1222
- success: true,
1223
- sessionId: writer.getSessionId(),
1268
+ success: !aborted,
1269
+ ...(aborted ? { aborted: true } : {}),
1270
+ sessionId: appSessionId,
1271
+ // The provider-native id, for a caller that drives the CLI itself
1272
+ // (`claude --resume`); `sessionId` is what this API and the UI use.
1273
+ providerSessionId: run.writer.getSessionId(),
1224
1274
  messages: assistantMessages,
1225
1275
  tokens: tokenSummary,
1226
1276
  projectPath: finalProjectPath
@@ -1240,7 +1290,7 @@ export function createAgentRouter(dependencies: AgentRouterDependencies): expres
1240
1290
  // Clean up if requested
1241
1291
  if (cleanup && githubUrl && clonedProjectCreated) {
1242
1292
  // Only cleanup if we cloned a repo (not for existing project paths)
1243
- const sessionIdForCleanup = writer.getSessionId();
1293
+ const sessionIdForCleanup = run.writer.getSessionId();
1244
1294
  setTimeout(() => {
1245
1295
  cleanupProject(finalProjectPath, sessionIdForCleanup);
1246
1296
  }, 5000);
@@ -1251,7 +1301,7 @@ export function createAgentRouter(dependencies: AgentRouterDependencies): expres
1251
1301
 
1252
1302
  // Clean up on error
1253
1303
  if (finalProjectPath && cleanup && githubUrl && clonedProjectCreated) {
1254
- const sessionIdForCleanup = writer ? writer.getSessionId() : null;
1304
+ const sessionIdForCleanup = run ? run.writer.getSessionId() : null;
1255
1305
  cleanupProject(finalProjectPath, sessionIdForCleanup);
1256
1306
  }
1257
1307
 
@@ -1263,7 +1313,7 @@ export function createAgentRouter(dependencies: AgentRouterDependencies): expres
1263
1313
  res.setHeader('Cache-Control', 'no-cache');
1264
1314
  res.setHeader('Connection', 'keep-alive');
1265
1315
  res.setHeader('X-Accel-Buffering', 'no');
1266
- writer = new SSEStreamWriter(res, req.user.id);
1316
+ writer = new SSEStreamWriter(res);
1267
1317
  }
1268
1318
 
1269
1319
  if (!res.writableEnded) {
@@ -1280,6 +1330,14 @@ export function createAgentRouter(dependencies: AgentRouterDependencies): expres
1280
1330
  error: error.message
1281
1331
  });
1282
1332
  }
1333
+ } finally {
1334
+ // Safety net, as for a chat send: a runtime that threw or resolved
1335
+ // without its terminal `complete` would leave the session listed as
1336
+ // running in every tab. Scoped to this run, so a run the session
1337
+ // started meanwhile is left alone.
1338
+ if (run) {
1339
+ runRegistry.completeRunIfCurrent(run, { exitCode: 1 });
1340
+ }
1283
1341
  }
1284
1342
  });
1285
1343