@cloudcli-ai/cloudcli 1.37.1 → 1.37.3

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 (402) hide show
  1. package/README.md +1 -1
  2. package/dist/assets/abnfDiagram-N423BO3Z-CJntskwA.js +1 -0
  3. package/dist/assets/arc-CIfH4uMN.js +1 -0
  4. package/dist/assets/architectureDiagram-T3A2C74G-_IpEkukg.js +36 -0
  5. package/dist/assets/blockDiagram-VBNYF7ZC-BttxrpgZ.js +132 -0
  6. package/dist/assets/c4Diagram-5PPSVZJV-g51dSRr1.js +10 -0
  7. package/dist/assets/channel-C5xU9jMw.js +1 -0
  8. package/dist/assets/chunk-2GRJ4B5K-D_-LbQUa.js +1 -0
  9. package/dist/assets/chunk-2Q5K7J3B-BtlHzwaO.js +1 -0
  10. package/dist/assets/chunk-5RXB4S5H-UzkpvpAT.js +231 -0
  11. package/dist/assets/chunk-5VM5RSS4-BKwx5Gag.js +15 -0
  12. package/dist/assets/chunk-6Q2QTUOP-D_KEBH5f.js +88 -0
  13. package/dist/assets/chunk-GF5L2VYU-rK2DGyto.js +206 -0
  14. package/dist/assets/chunk-JWPE2WC7-CeBl_N9i.js +1 -0
  15. package/dist/assets/chunk-KBJHAD2P-BiQLWVIr.js +1 -0
  16. package/dist/assets/chunk-RYQCIY6F-C1knVGV5.js +1 -0
  17. package/dist/assets/chunk-XXDRQBXY-CtKbJkvF.js +1 -0
  18. package/dist/assets/classDiagram-JCYQIIEL-CRrL3vp7.js +1 -0
  19. package/dist/assets/classDiagram-v2-OCEON4UE-CRrL3vp7.js +1 -0
  20. package/dist/assets/cose-bilkent-JH36ORCC-DjX8y40Z.js +1 -0
  21. package/dist/assets/cynefin-VYW2F7L2-DM6YeRba.js +178 -0
  22. package/dist/assets/cynefinDiagram-MW4NZA55-wZJ-vy5B.js +62 -0
  23. package/dist/assets/cytoscape.esm-C10L9Icf.js +331 -0
  24. package/dist/assets/dagre-VZM6K2ZE-BzP0ejkH.js +4 -0
  25. package/dist/assets/defaultLocale-DX6XiGOO.js +1 -0
  26. package/dist/assets/diagram-7IWD3JNH-BTL_j23J.js +30 -0
  27. package/dist/assets/diagram-B4RE2ZJO-Cf1Norgu.js +3 -0
  28. package/dist/assets/diagram-LBJQPF4R-DPiUEKr-.js +24 -0
  29. package/dist/assets/diagram-Q27KOJAE-CTcTpiz5.js +24 -0
  30. package/dist/assets/diagram-UB23O5K3-DPMSdYm0.js +41 -0
  31. package/dist/assets/ebnfDiagram-BXEA7PRR-7yvD6uiF.js +1 -0
  32. package/dist/assets/erDiagram-JOGREHBK-CfgeDoV6.js +85 -0
  33. package/dist/assets/flowDiagram-UKHOOZJN-B27ng5zu.js +156 -0
  34. package/dist/assets/ganttDiagram-PKOTCBZU-CcC6b_7t.js +292 -0
  35. package/dist/assets/gitGraphDiagram-DS77QQ5N-BX5kPGrO.js +106 -0
  36. package/dist/assets/graph-Cx-EqisW.js +1 -0
  37. package/dist/assets/index-BTDHQ6Id.css +32 -0
  38. package/dist/assets/index-sFfz8iBR.js +4931 -0
  39. package/dist/assets/infoDiagram-6WML65LV-CUP-SlN4.js +2 -0
  40. package/dist/assets/init-Gi6I4Gst.js +1 -0
  41. package/dist/assets/ishikawaDiagram-WSZJBQD7-BTTDhOpX.js +70 -0
  42. package/dist/assets/journeyDiagram-NVQOT4AX-ARXcc61m.js +139 -0
  43. package/dist/assets/kanban-definition-27J2QSJJ-BieMpqMA.js +89 -0
  44. package/dist/assets/layout-D6BstSzF.js +1 -0
  45. package/dist/assets/linear-61vxjRnP.js +1 -0
  46. package/dist/assets/map-CV9uif0g.js +1 -0
  47. package/dist/assets/mermaid.core-nhnwkfQx.js +312 -0
  48. package/dist/assets/mindmap-definition-FAOFIHXS-Cm6x5vT1.js +96 -0
  49. package/dist/assets/ordinal-Cboi1Yqb.js +1 -0
  50. package/dist/assets/pegDiagram-VL7TDLO6-CcYBq_9H.js +1 -0
  51. package/dist/assets/pieDiagram-7S7Q4E2Y-CfvIjfzo.js +39 -0
  52. package/dist/assets/quadrantDiagram-CIZ2JOQS-SgYoWPNb.js +7 -0
  53. package/dist/assets/railroadDiagram-AXF67PYL-D2Mu5E_f.js +1 -0
  54. package/dist/assets/requirementDiagram-LRYGKXZP-Azwe_k4Q.js +84 -0
  55. package/dist/assets/sankeyDiagram-W5VNT64P-DN1Ju3PA.js +40 -0
  56. package/dist/assets/sequenceDiagram-SI44F4Z6-Bj4qC34S.js +162 -0
  57. package/dist/assets/server.browser-CtX3Hdl5.js +27 -0
  58. package/dist/assets/sizeCapture-X5ZJPWSS-Co4PdklG.js +1 -0
  59. package/dist/assets/stateDiagram-OKZ733FA-nOvL9dRC.js +1 -0
  60. package/dist/assets/stateDiagram-v2-UEYNNEHI-DNv8ppw7.js +1 -0
  61. package/dist/assets/swimlanes-SLNWSIFB-CCK3wMid.js +2 -0
  62. package/dist/assets/swimlanesDiagram-ULZ7WXOC-DnfbNQEp.js +8 -0
  63. package/dist/assets/timeline-definition-Z64GVDOM-BC29RNFm.js +120 -0
  64. package/dist/assets/{vendor-codemirror-OwyKSvPE.js → vendor-codemirror-CYrSt7-U.js} +18 -18
  65. package/dist/assets/vennDiagram-T6HMQDX7-pZlZCtEo.js +34 -0
  66. package/dist/assets/wardleyDiagram-T6FBY63Y-De8k2-zM.js +78 -0
  67. package/dist/assets/xychartDiagram-ELKLHX3M-Bk8ykH0w.js +7 -0
  68. package/dist/index.html +3 -3
  69. package/dist-server/server/index.js +8 -5
  70. package/dist-server/server/index.js.map +1 -1
  71. package/dist-server/server/modules/agent/agent.module.js +2 -1
  72. package/dist-server/server/modules/agent/agent.module.js.map +1 -1
  73. package/dist-server/server/modules/agent/agent.routes.js +2 -2
  74. package/dist-server/server/modules/agent/agent.routes.js.map +1 -1
  75. package/dist-server/server/modules/agent/tests/agent.routes.test.js +30 -1
  76. package/dist-server/server/modules/agent/tests/agent.routes.test.js.map +1 -1
  77. package/dist-server/server/modules/auth/auth.middleware.js +1 -1
  78. package/dist-server/server/modules/auth/auth.middleware.js.map +1 -1
  79. package/dist-server/server/modules/browser-use/browser-use-runtime.js +10 -0
  80. package/dist-server/server/modules/browser-use/browser-use-runtime.js.map +1 -0
  81. package/dist-server/server/modules/browser-use/browser-use.routes.js +39 -33
  82. package/dist-server/server/modules/browser-use/browser-use.routes.js.map +1 -1
  83. package/dist-server/server/modules/browser-use/browser-use.service.js +3 -6
  84. package/dist-server/server/modules/browser-use/browser-use.service.js.map +1 -1
  85. package/dist-server/server/modules/browser-use/index.js +2 -0
  86. package/dist-server/server/modules/browser-use/index.js.map +1 -1
  87. package/dist-server/server/modules/cli/cli.js +9 -3
  88. package/dist-server/server/modules/cli/cli.js.map +1 -1
  89. package/dist-server/server/modules/cli/cli.module.js +0 -1
  90. package/dist-server/server/modules/cli/cli.module.js.map +1 -1
  91. package/dist-server/server/modules/cli/cli.service.js +0 -18
  92. package/dist-server/server/modules/cli/cli.service.js.map +1 -1
  93. package/dist-server/server/modules/cli/tests/cli-environment-bootstrap.test.js +154 -0
  94. package/dist-server/server/modules/cli/tests/cli-environment-bootstrap.test.js.map +1 -0
  95. package/dist-server/server/modules/cli/tests/cli.service.test.js +0 -3
  96. package/dist-server/server/modules/cli/tests/cli.service.test.js.map +1 -1
  97. package/dist-server/server/modules/database/connection.js +1 -1
  98. package/dist-server/server/modules/database/connection.js.map +1 -1
  99. package/dist-server/server/modules/database/index.js +5 -0
  100. package/dist-server/server/modules/database/index.js.map +1 -1
  101. package/dist-server/server/modules/database/migrations.js +32 -1
  102. package/dist-server/server/modules/database/migrations.js.map +1 -1
  103. package/dist-server/server/modules/database/repositories/scheduled-messages.db.js +71 -0
  104. package/dist-server/server/modules/database/repositories/scheduled-messages.db.js.map +1 -0
  105. package/dist-server/server/modules/database/repositories/session-drafts.db.js +105 -0
  106. package/dist-server/server/modules/database/repositories/session-drafts.db.js.map +1 -0
  107. package/dist-server/server/modules/database/repositories/sessions.db.js +137 -5
  108. package/dist-server/server/modules/database/repositories/sessions.db.js.map +1 -1
  109. package/dist-server/server/modules/database/repositories/user-preferences.db.js +65 -0
  110. package/dist-server/server/modules/database/repositories/user-preferences.db.js.map +1 -0
  111. package/dist-server/server/modules/database/schema.js +92 -0
  112. package/dist-server/server/modules/database/schema.js.map +1 -1
  113. package/dist-server/server/modules/database/tests/sessions.db.integration.test.js +39 -0
  114. package/dist-server/server/modules/database/tests/sessions.db.integration.test.js.map +1 -1
  115. package/dist-server/server/modules/database/tests/user-preferences.db.integration.test.js +105 -0
  116. package/dist-server/server/modules/database/tests/user-preferences.db.integration.test.js.map +1 -0
  117. package/dist-server/server/modules/file-tree/file-tree.service.js +18 -10
  118. package/dist-server/server/modules/file-tree/file-tree.service.js.map +1 -1
  119. package/dist-server/server/modules/file-tree/tests/file-tree.service.test.js +62 -11
  120. package/dist-server/server/modules/file-tree/tests/file-tree.service.test.js.map +1 -1
  121. package/dist-server/server/modules/git/git-branch.service.js +15 -0
  122. package/dist-server/server/modules/git/git-branch.service.js.map +1 -0
  123. package/dist-server/server/modules/git/git.routes.js +15 -7
  124. package/dist-server/server/modules/git/git.routes.js.map +1 -1
  125. package/dist-server/server/modules/git/tests/git-branch.service.test.js +52 -0
  126. package/dist-server/server/modules/git/tests/git-branch.service.test.js.map +1 -0
  127. package/dist-server/server/modules/git/tests/git-init.routes.test.js +84 -0
  128. package/dist-server/server/modules/git/tests/git-init.routes.test.js.map +1 -1
  129. package/dist-server/server/modules/notifications/index.js +3 -1
  130. package/dist-server/server/modules/notifications/index.js.map +1 -1
  131. package/dist-server/server/modules/notifications/services/notification-orchestrator.service.js +22 -1
  132. package/dist-server/server/modules/notifications/services/notification-orchestrator.service.js.map +1 -1
  133. package/dist-server/server/modules/projects/services/project-delete.service.js +12 -1
  134. package/dist-server/server/modules/projects/services/project-delete.service.js.map +1 -1
  135. package/dist-server/server/modules/projects/services/project-star.service.js +2 -2
  136. package/dist-server/server/modules/projects/services/project-star.service.js.map +1 -1
  137. package/dist-server/server/modules/providers/index.js +3 -0
  138. package/dist-server/server/modules/providers/index.js.map +1 -1
  139. package/dist-server/server/modules/providers/list/claude/claude-auth.provider.js +20 -1
  140. package/dist-server/server/modules/providers/list/claude/claude-auth.provider.js.map +1 -1
  141. package/dist-server/server/modules/providers/list/claude/claude-fork.provider.js +46 -0
  142. package/dist-server/server/modules/providers/list/claude/claude-fork.provider.js.map +1 -0
  143. package/dist-server/server/modules/providers/list/claude/claude-models.provider.js +35 -6
  144. package/dist-server/server/modules/providers/list/claude/claude-models.provider.js.map +1 -1
  145. package/dist-server/server/modules/providers/list/claude/claude-runtime.provider.js +422 -86
  146. package/dist-server/server/modules/providers/list/claude/claude-runtime.provider.js.map +1 -1
  147. package/dist-server/server/modules/providers/list/claude/claude-sessions.provider.js +388 -50
  148. package/dist-server/server/modules/providers/list/claude/claude-sessions.provider.js.map +1 -1
  149. package/dist-server/server/modules/providers/list/claude/claude-skills.provider.js +4 -1
  150. package/dist-server/server/modules/providers/list/claude/claude-skills.provider.js.map +1 -1
  151. package/dist-server/server/modules/providers/list/claude/claude.provider.js +2 -0
  152. package/dist-server/server/modules/providers/list/claude/claude.provider.js.map +1 -1
  153. package/dist-server/server/modules/providers/list/codex/codex-app-server.client.js +204 -0
  154. package/dist-server/server/modules/providers/list/codex/codex-app-server.client.js.map +1 -0
  155. package/dist-server/server/modules/providers/list/codex/codex-fork.provider.js +32 -0
  156. package/dist-server/server/modules/providers/list/codex/codex-fork.provider.js.map +1 -0
  157. package/dist-server/server/modules/providers/list/codex/codex-models.provider.js +16 -0
  158. package/dist-server/server/modules/providers/list/codex/codex-models.provider.js.map +1 -1
  159. package/dist-server/server/modules/providers/list/codex/codex-runtime.provider.js +60 -49
  160. package/dist-server/server/modules/providers/list/codex/codex-runtime.provider.js.map +1 -1
  161. package/dist-server/server/modules/providers/list/codex/codex-session-synchronizer.provider.js +8 -0
  162. package/dist-server/server/modules/providers/list/codex/codex-session-synchronizer.provider.js.map +1 -1
  163. package/dist-server/server/modules/providers/list/codex/codex-sessions.provider.js +1565 -432
  164. package/dist-server/server/modules/providers/list/codex/codex-sessions.provider.js.map +1 -1
  165. package/dist-server/server/modules/providers/list/codex/codex.provider.js +2 -0
  166. package/dist-server/server/modules/providers/list/codex/codex.provider.js.map +1 -1
  167. package/dist-server/server/modules/providers/list/cursor/cursor-runtime.provider.js +8 -6
  168. package/dist-server/server/modules/providers/list/cursor/cursor-runtime.provider.js.map +1 -1
  169. package/dist-server/server/modules/providers/list/cursor/cursor-session-synchronizer.provider.js.map +1 -1
  170. package/dist-server/server/modules/providers/list/opencode/opencode-models.provider.js +258 -6
  171. package/dist-server/server/modules/providers/list/opencode/opencode-models.provider.js.map +1 -1
  172. package/dist-server/server/modules/providers/provider.routes.js +29 -0
  173. package/dist-server/server/modules/providers/provider.routes.js.map +1 -1
  174. package/dist-server/server/modules/providers/services/provider-capabilities.service.js +14 -0
  175. package/dist-server/server/modules/providers/services/provider-capabilities.service.js.map +1 -1
  176. package/dist-server/server/modules/providers/services/provider-token-usage.service.js +139 -35
  177. package/dist-server/server/modules/providers/services/provider-token-usage.service.js.map +1 -1
  178. package/dist-server/server/modules/providers/services/session-conversations-search.service.js +63 -7
  179. package/dist-server/server/modules/providers/services/session-conversations-search.service.js.map +1 -1
  180. package/dist-server/server/modules/providers/services/session-history-cache.service.js +87 -0
  181. package/dist-server/server/modules/providers/services/session-history-cache.service.js.map +1 -0
  182. package/dist-server/server/modules/providers/services/session-synchronizer.service.js +105 -32
  183. package/dist-server/server/modules/providers/services/session-synchronizer.service.js.map +1 -1
  184. package/dist-server/server/modules/providers/services/sessions-watcher.service.js +3 -60
  185. package/dist-server/server/modules/providers/services/sessions-watcher.service.js.map +1 -1
  186. package/dist-server/server/modules/providers/services/sessions.service.js +179 -9
  187. package/dist-server/server/modules/providers/services/sessions.service.js.map +1 -1
  188. package/dist-server/server/modules/providers/tests/claude-auth.test.js +52 -0
  189. package/dist-server/server/modules/providers/tests/claude-auth.test.js.map +1 -1
  190. package/dist-server/server/modules/providers/tests/claude-models.test.js +39 -0
  191. package/dist-server/server/modules/providers/tests/claude-models.test.js.map +1 -0
  192. package/dist-server/server/modules/providers/tests/claude-sessions.test.js +465 -39
  193. package/dist-server/server/modules/providers/tests/claude-sessions.test.js.map +1 -1
  194. package/dist-server/server/modules/providers/tests/claude-subagent-echo.test.js +16 -0
  195. package/dist-server/server/modules/providers/tests/claude-subagent-echo.test.js.map +1 -0
  196. package/dist-server/server/modules/providers/tests/claude-token-budget.test.js +85 -0
  197. package/dist-server/server/modules/providers/tests/claude-token-budget.test.js.map +1 -0
  198. package/dist-server/server/modules/providers/tests/codex-message-editing.test.js +338 -0
  199. package/dist-server/server/modules/providers/tests/codex-message-editing.test.js.map +1 -0
  200. package/dist-server/server/modules/providers/tests/codex-runtime.test.js +51 -0
  201. package/dist-server/server/modules/providers/tests/codex-runtime.test.js.map +1 -0
  202. package/dist-server/server/modules/providers/tests/codex-session-fork.test.js +54 -0
  203. package/dist-server/server/modules/providers/tests/codex-session-fork.test.js.map +1 -0
  204. package/dist-server/server/modules/providers/tests/codex-sessions.test.js +304 -15
  205. package/dist-server/server/modules/providers/tests/codex-sessions.test.js.map +1 -1
  206. package/dist-server/server/modules/providers/tests/opencode-models.test.js +117 -4
  207. package/dist-server/server/modules/providers/tests/opencode-models.test.js.map +1 -1
  208. package/dist-server/server/modules/providers/tests/provider-token-usage.service.test.js +164 -1
  209. package/dist-server/server/modules/providers/tests/provider-token-usage.service.test.js.map +1 -1
  210. package/dist-server/server/modules/providers/tests/provider.routes.test.js +35 -0
  211. package/dist-server/server/modules/providers/tests/provider.routes.test.js.map +1 -1
  212. package/dist-server/server/modules/providers/tests/session-fork.test.js +113 -0
  213. package/dist-server/server/modules/providers/tests/session-fork.test.js.map +1 -0
  214. package/dist-server/server/modules/providers/tests/session-history-cache.service.test.js +120 -0
  215. package/dist-server/server/modules/providers/tests/session-history-cache.service.test.js.map +1 -0
  216. package/dist-server/server/modules/providers/tests/session-orphan-prune.test.js +90 -0
  217. package/dist-server/server/modules/providers/tests/session-orphan-prune.test.js.map +1 -0
  218. package/dist-server/server/modules/providers/tests/session-synchronizer-coalescing.test.js +122 -0
  219. package/dist-server/server/modules/providers/tests/session-synchronizer-coalescing.test.js.map +1 -0
  220. package/dist-server/server/modules/providers/tests/sessions.service.test.js +45 -1
  221. package/dist-server/server/modules/providers/tests/sessions.service.test.js.map +1 -1
  222. package/dist-server/server/modules/providers/tests/skills.test.js +15 -3
  223. package/dist-server/server/modules/providers/tests/skills.test.js.map +1 -1
  224. package/dist-server/server/modules/scheduled-messages/index.js +5 -0
  225. package/dist-server/server/modules/scheduled-messages/index.js.map +1 -0
  226. package/dist-server/server/modules/scheduled-messages/scheduled-messages.routes.js +47 -0
  227. package/dist-server/server/modules/scheduled-messages/scheduled-messages.routes.js.map +1 -0
  228. package/dist-server/server/modules/scheduled-messages/services/scheduled-message-dispatcher.service.js +163 -0
  229. package/dist-server/server/modules/scheduled-messages/services/scheduled-message-dispatcher.service.js.map +1 -0
  230. package/dist-server/server/modules/scheduled-messages/services/scheduled-messages.service.js +95 -0
  231. package/dist-server/server/modules/scheduled-messages/services/scheduled-messages.service.js.map +1 -0
  232. package/dist-server/server/modules/scheduled-messages/tests/scheduled-messages.test.js +255 -0
  233. package/dist-server/server/modules/scheduled-messages/tests/scheduled-messages.test.js.map +1 -0
  234. package/dist-server/server/modules/taskmaster/taskmaster.routes.js +23 -23
  235. package/dist-server/server/modules/taskmaster/taskmaster.routes.js.map +1 -1
  236. package/dist-server/server/modules/taskmaster/tests/taskmaster.routes.test.js +48 -0
  237. package/dist-server/server/modules/taskmaster/tests/taskmaster.routes.test.js.map +1 -1
  238. package/dist-server/server/modules/user/tests/user.service.test.js +70 -0
  239. package/dist-server/server/modules/user/tests/user.service.test.js.map +1 -1
  240. package/dist-server/server/modules/user/user.module.js +10 -1
  241. package/dist-server/server/modules/user/user.module.js.map +1 -1
  242. package/dist-server/server/modules/user/user.routes.js +44 -0
  243. package/dist-server/server/modules/user/user.routes.js.map +1 -1
  244. package/dist-server/server/modules/user/user.service.js +75 -0
  245. package/dist-server/server/modules/user/user.service.js.map +1 -1
  246. package/dist-server/server/modules/voice/tests/voice.service.test.js +3 -2
  247. package/dist-server/server/modules/voice/tests/voice.service.test.js.map +1 -1
  248. package/dist-server/server/modules/websocket/index.js +7 -0
  249. package/dist-server/server/modules/websocket/index.js.map +1 -1
  250. package/dist-server/server/modules/websocket/services/chat-run-registry.service.js +10 -44
  251. package/dist-server/server/modules/websocket/services/chat-run-registry.service.js.map +1 -1
  252. package/dist-server/server/modules/websocket/services/chat-session-writer.service.js +35 -5
  253. package/dist-server/server/modules/websocket/services/chat-session-writer.service.js.map +1 -1
  254. package/dist-server/server/modules/websocket/services/chat-websocket.service.js +166 -9
  255. package/dist-server/server/modules/websocket/services/chat-websocket.service.js.map +1 -1
  256. package/dist-server/server/modules/websocket/services/session-upsert-broadcast.service.js +94 -0
  257. package/dist-server/server/modules/websocket/services/session-upsert-broadcast.service.js.map +1 -0
  258. package/dist-server/server/modules/websocket/services/shell-websocket.service.js +9 -3
  259. package/dist-server/server/modules/websocket/services/shell-websocket.service.js.map +1 -1
  260. package/dist-server/server/modules/websocket/tests/chat-edit-send.test.js +263 -0
  261. package/dist-server/server/modules/websocket/tests/chat-edit-send.test.js.map +1 -0
  262. package/dist-server/server/modules/websocket/tests/chat-run-registry.test.js +29 -3
  263. package/dist-server/server/modules/websocket/tests/chat-run-registry.test.js.map +1 -1
  264. package/dist-server/server/modules/websocket/tests/session-upsert-broadcast.test.js +106 -0
  265. package/dist-server/server/modules/websocket/tests/session-upsert-broadcast.test.js.map +1 -0
  266. package/dist-server/server/modules/websocket/tests/shell-websocket.service.test.js +80 -0
  267. package/dist-server/server/modules/websocket/tests/shell-websocket.service.test.js.map +1 -1
  268. package/dist-server/server/shared/claude-cli-path.js +22 -5
  269. package/dist-server/server/shared/claude-cli-path.js.map +1 -1
  270. package/dist-server/server/shared/index.js +3 -0
  271. package/dist-server/server/shared/index.js.map +1 -0
  272. package/dist-server/server/shared/message-unification.js +376 -0
  273. package/dist-server/server/shared/message-unification.js.map +1 -0
  274. package/dist-server/server/shared/tests/claude-cli-path.test.js +45 -1
  275. package/dist-server/server/shared/tests/claude-cli-path.test.js.map +1 -1
  276. package/dist-server/server/shared/tests/message-unification.test.js +174 -0
  277. package/dist-server/server/shared/tests/message-unification.test.js.map +1 -0
  278. package/dist-server/server/shared/utils.js +42 -0
  279. package/dist-server/server/shared/utils.js.map +1 -1
  280. package/package.json +23 -16
  281. package/scripts/promote-dist-server.mjs +50 -0
  282. package/server/index.ts +12 -6
  283. package/server/modules/agent/agent.module.ts +2 -1
  284. package/server/modules/agent/agent.routes.ts +2 -2
  285. package/server/modules/agent/tests/agent.routes.test.ts +33 -1
  286. package/server/modules/auth/auth.middleware.ts +2 -2
  287. package/server/modules/browser-use/browser-use-runtime.ts +10 -0
  288. package/server/modules/browser-use/browser-use.routes.ts +96 -33
  289. package/server/modules/browser-use/browser-use.service.ts +5 -8
  290. package/server/modules/browser-use/index.ts +3 -0
  291. package/server/modules/cli/cli.module.ts +0 -1
  292. package/server/modules/cli/cli.service.ts +0 -20
  293. package/server/modules/cli/cli.ts +9 -3
  294. package/server/modules/cli/tests/cli-environment-bootstrap.test.ts +186 -0
  295. package/server/modules/cli/tests/cli.service.test.ts +0 -3
  296. package/server/modules/database/connection.ts +2 -1
  297. package/server/modules/database/index.ts +10 -0
  298. package/server/modules/database/migrations.ts +37 -0
  299. package/server/modules/database/repositories/scheduled-messages.db.ts +127 -0
  300. package/server/modules/database/repositories/session-drafts.db.ts +171 -0
  301. package/server/modules/database/repositories/sessions.db.ts +196 -3
  302. package/server/modules/database/repositories/user-preferences.db.ts +80 -0
  303. package/server/modules/database/schema.ts +96 -0
  304. package/server/modules/database/tests/sessions.db.integration.test.ts +52 -0
  305. package/server/modules/database/tests/user-preferences.db.integration.test.ts +135 -0
  306. package/server/modules/file-tree/file-tree.service.ts +21 -10
  307. package/server/modules/file-tree/tests/file-tree.service.test.ts +65 -11
  308. package/server/modules/git/git-branch.service.ts +43 -0
  309. package/server/modules/git/git.routes.ts +16 -9
  310. package/server/modules/git/tests/git-branch.service.test.ts +60 -0
  311. package/server/modules/git/tests/git-init.routes.test.ts +83 -0
  312. package/server/modules/notifications/index.ts +2 -0
  313. package/server/modules/notifications/services/notification-orchestrator.service.js +24 -1
  314. package/server/modules/projects/services/project-delete.service.ts +13 -1
  315. package/server/modules/projects/services/project-star.service.ts +2 -2
  316. package/server/modules/providers/index.ts +4 -0
  317. package/server/modules/providers/list/claude/claude-auth.provider.ts +21 -1
  318. package/server/modules/providers/list/claude/claude-fork.provider.ts +57 -0
  319. package/server/modules/providers/list/claude/claude-models.provider.ts +38 -6
  320. package/server/modules/providers/list/claude/claude-runtime.provider.js +463 -93
  321. package/server/modules/providers/list/claude/claude-sessions.provider.ts +478 -54
  322. package/server/modules/providers/list/claude/claude-skills.provider.ts +4 -1
  323. package/server/modules/providers/list/claude/claude.provider.ts +3 -0
  324. package/server/modules/providers/list/codex/codex-app-server.client.ts +243 -0
  325. package/server/modules/providers/list/codex/codex-fork.provider.ts +40 -0
  326. package/server/modules/providers/list/codex/codex-models.provider.ts +16 -0
  327. package/server/modules/providers/list/codex/{codex-runtime.provider.js → codex-runtime.provider.ts} +93 -66
  328. package/server/modules/providers/list/codex/codex-session-synchronizer.provider.ts +9 -0
  329. package/server/modules/providers/list/codex/codex-sessions.provider.ts +1806 -477
  330. package/server/modules/providers/list/codex/codex.provider.ts +3 -0
  331. package/server/modules/providers/list/cursor/cursor-runtime.provider.js +8 -6
  332. package/server/modules/providers/list/cursor/cursor-session-synchronizer.provider.ts +0 -1
  333. package/server/modules/providers/list/opencode/opencode-models.provider.ts +279 -6
  334. package/server/modules/providers/provider.routes.ts +36 -0
  335. package/server/modules/providers/services/provider-capabilities.service.ts +23 -0
  336. package/server/modules/providers/services/provider-token-usage.service.ts +166 -39
  337. package/server/modules/providers/services/session-conversations-search.service.ts +94 -7
  338. package/server/modules/providers/services/session-history-cache.service.ts +139 -0
  339. package/server/modules/providers/services/session-synchronizer.service.ts +125 -38
  340. package/server/modules/providers/services/sessions-watcher.service.ts +3 -64
  341. package/server/modules/providers/services/sessions.service.ts +201 -9
  342. package/server/modules/providers/tests/claude-auth.test.ts +58 -0
  343. package/server/modules/providers/tests/claude-models.test.ts +87 -0
  344. package/server/modules/providers/tests/claude-sessions.test.ts +552 -40
  345. package/server/modules/providers/tests/claude-subagent-echo.test.ts +25 -0
  346. package/server/modules/providers/tests/claude-token-budget.test.ts +104 -0
  347. package/server/modules/providers/tests/codex-message-editing.test.ts +419 -0
  348. package/server/modules/providers/tests/codex-runtime.test.ts +58 -0
  349. package/server/modules/providers/tests/codex-session-fork.test.ts +59 -0
  350. package/server/modules/providers/tests/codex-sessions.test.ts +337 -15
  351. package/server/modules/providers/tests/opencode-models.test.ts +166 -8
  352. package/server/modules/providers/tests/provider-token-usage.service.test.ts +188 -1
  353. package/server/modules/providers/tests/provider.routes.test.ts +61 -0
  354. package/server/modules/providers/tests/session-fork.test.ts +173 -0
  355. package/server/modules/providers/tests/session-history-cache.service.test.ts +143 -0
  356. package/server/modules/providers/tests/session-orphan-prune.test.ts +107 -0
  357. package/server/modules/providers/tests/session-synchronizer-coalescing.test.ts +136 -0
  358. package/server/modules/providers/tests/sessions.service.test.ts +67 -1
  359. package/server/modules/providers/tests/skills.test.ts +21 -5
  360. package/server/modules/scheduled-messages/index.ts +8 -0
  361. package/server/modules/scheduled-messages/scheduled-messages.routes.ts +68 -0
  362. package/server/modules/scheduled-messages/services/scheduled-message-dispatcher.service.ts +203 -0
  363. package/server/modules/scheduled-messages/services/scheduled-messages.service.ts +121 -0
  364. package/server/modules/scheduled-messages/tests/scheduled-messages.test.ts +310 -0
  365. package/server/modules/taskmaster/taskmaster.routes.ts +23 -33
  366. package/server/modules/taskmaster/tests/taskmaster.routes.test.ts +57 -0
  367. package/server/modules/user/tests/user.service.test.ts +90 -0
  368. package/server/modules/user/user.module.ts +10 -1
  369. package/server/modules/user/user.routes.ts +44 -0
  370. package/server/modules/user/user.service.ts +105 -0
  371. package/server/modules/voice/tests/voice.service.test.ts +3 -2
  372. package/server/modules/websocket/index.ts +8 -0
  373. package/server/modules/websocket/services/chat-run-registry.service.ts +17 -50
  374. package/server/modules/websocket/services/chat-session-writer.service.ts +36 -6
  375. package/server/modules/websocket/services/chat-websocket.service.ts +244 -15
  376. package/server/modules/websocket/services/session-upsert-broadcast.service.ts +108 -0
  377. package/server/modules/websocket/services/shell-websocket.service.ts +10 -3
  378. package/server/modules/websocket/tests/chat-edit-send.test.ts +309 -0
  379. package/server/modules/websocket/tests/chat-run-registry.test.ts +34 -3
  380. package/server/modules/websocket/tests/session-upsert-broadcast.test.ts +138 -0
  381. package/server/modules/websocket/tests/shell-websocket.service.test.ts +108 -0
  382. package/server/shared/claude-cli-path.ts +24 -6
  383. package/server/shared/index.ts +3 -0
  384. package/server/shared/interfaces.ts +62 -0
  385. package/server/shared/message-unification.ts +449 -0
  386. package/server/shared/tests/claude-cli-path.test.ts +58 -1
  387. package/server/shared/tests/message-unification.test.ts +218 -0
  388. package/server/shared/types.ts +123 -3
  389. package/server/shared/utils.ts +50 -0
  390. package/server/tsconfig.json +6 -2
  391. package/README.de.md +0 -257
  392. package/README.ja.md +0 -249
  393. package/README.ko.md +0 -249
  394. package/README.ru.md +0 -257
  395. package/README.tr.md +0 -258
  396. package/README.zh-CN.md +0 -249
  397. package/README.zh-TW.md +0 -249
  398. package/dist/assets/index-D3jfzuKN.css +0 -32
  399. package/dist/assets/index-DTMWjJj8.js +0 -1550
  400. package/dist-server/server/constants/config.js +0 -6
  401. package/dist-server/server/constants/config.js.map +0 -1
  402. package/server/constants/config.ts +0 -5
@@ -4,17 +4,41 @@ import path from 'node:path';
4
4
  import readline from 'node:readline';
5
5
 
6
6
  import type { IProviderSessions } from '@/shared/interfaces.js';
7
- import type { AnyRecord, FetchHistoryOptions, FetchHistoryResult, NormalizedMessage } from '@/shared/types.js';
7
+ import type {
8
+ AnyRecord,
9
+ FetchHistoryOptions,
10
+ FetchHistoryResult,
11
+ NormalizedMessage,
12
+ SubagentActivity,
13
+ SubagentInfo,
14
+ } from '@/shared/types.js';
8
15
  import { parseFilesInputTag } from '@/shared/image-attachments.js';
9
- import { createNormalizedMessage, generateMessageId, readObjectRecord, sliceTailPage } from '@/shared/utils.js';
16
+ import { prepareTranscriptMessages } from '@/shared/message-unification.js';
17
+ import {
18
+ createNormalizedMessage,
19
+ generateMessageId,
20
+ readObjectRecord,
21
+ sliceTailPage,
22
+ truncateSubagentActivity,
23
+ } from '@/shared/utils.js';
10
24
  import { sessionsDb } from '@/modules/database/index.js';
25
+ import { summarizeClaudeTokenUsage } from '@/modules/providers/services/provider-token-usage.service.js';
11
26
 
12
27
  const PROVIDER = 'claude';
13
28
 
29
+ /**
30
+ * Upper bound on how much of a subagent's timeline is sent to the client. A
31
+ * long-running agent can record hundreds of tool calls, and the transcript only
32
+ * ever shows them behind a collapsed header, so shipping the whole history on
33
+ * every load costs far more than it shows.
34
+ */
35
+ const MAX_TRANSMITTED_SUBAGENT_ACTIVITIES = 200;
36
+
14
37
  type ClaudeToolResult = {
15
38
  content: unknown;
16
39
  isError: boolean;
17
- subagentTools?: unknown;
40
+ subagentTools?: SubagentActivity[];
41
+ subagent?: SubagentInfo;
18
42
  toolUseResult?: unknown;
19
43
  };
20
44
 
@@ -36,8 +60,27 @@ type ClaudeHistoryMessagesResult =
36
60
  limit?: number | null;
37
61
  };
38
62
 
39
- async function parseAgentTools(filePath: string): Promise<AnyRecord[]> {
40
- const tools: AnyRecord[] = [];
63
+ type ClaudeSubagentTranscript = {
64
+ activity: SubagentActivity[];
65
+ model?: string;
66
+ /**
67
+ * True when the transcript's last tool call never received a result, which
68
+ * is the only evidence in the file that the agent stopped mid-flight.
69
+ */
70
+ endedMidToolCall: boolean;
71
+ };
72
+
73
+ /**
74
+ * Flattens one subagent transcript into the shared activity timeline.
75
+ *
76
+ * Assistant prose and reasoning are kept alongside the tool calls so the
77
+ * transcript can replay what the agent actually did rather than listing tool
78
+ * names with no narrative.
79
+ */
80
+ async function readClaudeSubagentTranscript(filePath: string): Promise<ClaudeSubagentTranscript> {
81
+ const activity: SubagentActivity[] = [];
82
+ const transcript: ClaudeSubagentTranscript = { activity, endedMidToolCall: false };
83
+ const toolsById = new Map<string, SubagentActivity>();
41
84
 
42
85
  try {
43
86
  const fileStream = fs.createReadStream(filePath);
@@ -53,16 +96,34 @@ async function parseAgentTools(filePath: string): Promise<AnyRecord[]> {
53
96
 
54
97
  try {
55
98
  const entry = JSON.parse(line) as AnyRecord;
99
+ const timestamp = typeof entry.timestamp === 'string' ? entry.timestamp : undefined;
56
100
 
57
101
  if (entry.message?.role === 'assistant' && Array.isArray(entry.message?.content)) {
102
+ if (typeof entry.message.model === 'string') {
103
+ transcript.model = entry.message.model;
104
+ }
105
+
58
106
  for (const part of entry.message.content as AnyRecord[]) {
59
107
  if (part.type === 'tool_use') {
60
- tools.push({
61
- toolId: part.id,
62
- toolName: part.name,
108
+ const tool: SubagentActivity = {
109
+ kind: 'tool',
110
+ toolId: String(part.id ?? ''),
111
+ toolName: String(part.name ?? 'Tool'),
63
112
  toolInput: part.input,
64
- timestamp: entry.timestamp,
65
- });
113
+ timestamp,
114
+ };
115
+ activity.push(tool);
116
+ if (tool.toolId) {
117
+ toolsById.set(tool.toolId, tool);
118
+ }
119
+ continue;
120
+ }
121
+ if (part.type === 'text' && typeof part.text === 'string' && part.text.trim()) {
122
+ activity.push({ kind: 'text', content: part.text, timestamp });
123
+ continue;
124
+ }
125
+ if (part.type === 'thinking' && typeof part.thinking === 'string' && part.thinking.trim()) {
126
+ activity.push({ kind: 'thinking', content: part.thinking, timestamp });
66
127
  }
67
128
  }
68
129
  }
@@ -73,7 +134,7 @@ async function parseAgentTools(filePath: string): Promise<AnyRecord[]> {
73
134
  continue;
74
135
  }
75
136
 
76
- const tool = tools.find((candidate) => candidate.toolId === part.tool_use_id);
137
+ const tool = toolsById.get(String(part.tool_use_id ?? ''));
77
138
  if (!tool) {
78
139
  continue;
79
140
  }
@@ -99,7 +160,249 @@ async function parseAgentTools(filePath: string): Promise<AnyRecord[]> {
99
160
  console.warn(`Error parsing agent file ${filePath}:`, message);
100
161
  }
101
162
 
102
- return tools;
163
+ const lastActivity = activity[activity.length - 1];
164
+ transcript.endedMidToolCall = lastActivity?.kind === 'tool' && !lastActivity.toolResult;
165
+
166
+ return transcript;
167
+ }
168
+
169
+ type ClaudeSubagentMeta = {
170
+ agentType?: string;
171
+ description?: string;
172
+ };
173
+
174
+ /** Reads the sidecar `.meta.json` Claude writes next to a subagent transcript. */
175
+ async function readClaudeSubagentMeta(metaPath: string): Promise<ClaudeSubagentMeta> {
176
+ try {
177
+ const parsed = JSON.parse(await fsp.readFile(metaPath, 'utf8')) as AnyRecord;
178
+ return {
179
+ agentType: typeof parsed.agentType === 'string' ? parsed.agentType : undefined,
180
+ description: typeof parsed.description === 'string' ? parsed.description : undefined,
181
+ };
182
+ } catch {
183
+ return {};
184
+ }
185
+ }
186
+
187
+ /**
188
+ * Resolves where a subagent's transcript lives.
189
+ *
190
+ * Current Claude versions write it to
191
+ * `<projectDir>/<providerSessionId>/subagents/agent-<agentId>.jsonl`; older
192
+ * ones dropped it next to the parent transcript. Both are checked, newest
193
+ * layout first, because a project directory usually holds sessions from
194
+ * several CLI versions.
195
+ */
196
+ async function findClaudeSubagentTranscript(
197
+ projectDirectory: string,
198
+ providerSessionId: string,
199
+ agentId: string,
200
+ ): Promise<{ transcriptPath: string; metaPath: string } | null> {
201
+ const candidates = [
202
+ path.join(projectDirectory, providerSessionId, 'subagents', `agent-${agentId}.jsonl`),
203
+ path.join(projectDirectory, `agent-${agentId}.jsonl`),
204
+ ];
205
+
206
+ for (const transcriptPath of candidates) {
207
+ try {
208
+ await fsp.access(transcriptPath);
209
+ return { transcriptPath, metaPath: transcriptPath.replace(/\.jsonl$/, '.meta.json') };
210
+ } catch {
211
+ // Try the next layout.
212
+ }
213
+ }
214
+
215
+ return null;
216
+ }
217
+
218
+ type ClaudeTaskNotification = {
219
+ /** `uuid` of the transcript row the notification came from, so it can be dropped. */
220
+ sourceUuid: string;
221
+ toolUseId: string;
222
+ status: string;
223
+ summary: string;
224
+ result: string;
225
+ };
226
+
227
+ function readTaggedValue(content: string, tagName: string): string {
228
+ const match = new RegExp(`<${tagName}>([\\s\\S]*?)<\\/${tagName}>`).exec(content);
229
+ return match ? match[1].trim() : '';
230
+ }
231
+
232
+ /**
233
+ * Collects every `<task-notification>` turn keyed by the tool call that
234
+ * spawned the agent it reports on.
235
+ *
236
+ * Only notifications that name a tool-use id are collected: without one there
237
+ * is no card to fold them into, and they must keep rendering on their own.
238
+ */
239
+ function collectTaskNotifications(messages: AnyRecord[]): Map<string, ClaudeTaskNotification> {
240
+ const notifications = new Map<string, ClaudeTaskNotification>();
241
+
242
+ for (const message of messages) {
243
+ if (message.message?.role !== 'user') {
244
+ continue;
245
+ }
246
+
247
+ const content = message.message.content;
248
+ const texts: string[] = typeof content === 'string'
249
+ ? [content]
250
+ : Array.isArray(content)
251
+ ? content.filter((part: AnyRecord) => part?.type === 'text').map((part: AnyRecord) => String(part.text ?? ''))
252
+ : [];
253
+
254
+ for (const text of texts) {
255
+ if (!text.trimStart().startsWith('<task-notification>')) {
256
+ continue;
257
+ }
258
+
259
+ const toolUseId = readTaggedValue(text, 'tool-use-id');
260
+ if (!toolUseId) {
261
+ continue;
262
+ }
263
+
264
+ // A resumed agent notifies more than once; the last word wins.
265
+ notifications.set(toolUseId, {
266
+ sourceUuid: String(message.uuid ?? ''),
267
+ toolUseId,
268
+ status: readTaggedValue(text, 'status') || 'completed',
269
+ summary: readTaggedValue(text, 'summary'),
270
+ result: readTaggedValue(text, 'result'),
271
+ });
272
+ }
273
+ }
274
+
275
+ return notifications;
276
+ }
277
+
278
+ /** Reads the `tool_use_id` off the tool-result row that launched an agent. */
279
+ function readAgentToolUseId(message: AnyRecord): string | null {
280
+ const content = message.message?.content;
281
+ if (!Array.isArray(content)) {
282
+ return null;
283
+ }
284
+
285
+ for (const part of content) {
286
+ if (part?.type === 'tool_result' && typeof part.tool_use_id === 'string') {
287
+ return part.tool_use_id;
288
+ }
289
+ }
290
+
291
+ return null;
292
+ }
293
+
294
+ /** Swaps an agent launch acknowledgement for the agent's actual answer. */
295
+ function replaceAgentToolResultContent(message: AnyRecord, replacement: string): void {
296
+ const content = message.message?.content;
297
+ if (!Array.isArray(content)) {
298
+ return;
299
+ }
300
+
301
+ for (const part of content) {
302
+ if (part?.type === 'tool_result') {
303
+ part.content = replacement;
304
+ }
305
+ }
306
+ }
307
+
308
+ /**
309
+ * Reads a Claude transcript's rows as a graph keyed by `uuid`.
310
+ *
311
+ * The file is append-only and every row names its predecessor in `parentUuid`,
312
+ * so a conversation is a path through it rather than the whole file. Editing a
313
+ * sent message makes a second path appear alongside the first.
314
+ */
315
+ async function readTranscriptRows(jsonlPath: string, providerSessionId: string): Promise<AnyRecord[]> {
316
+ const rows: AnyRecord[] = [];
317
+ const fileStream = fs.createReadStream(jsonlPath);
318
+ const rl = readline.createInterface({ input: fileStream, crlfDelay: Infinity });
319
+
320
+ for await (const line of rl) {
321
+ if (!line.trim()) {
322
+ continue;
323
+ }
324
+ try {
325
+ const entry = JSON.parse(line) as AnyRecord;
326
+ if (entry.sessionId === providerSessionId) {
327
+ rows.push(entry);
328
+ }
329
+ } catch {
330
+ // A row can be half-written while the CLI is streaming into the file.
331
+ }
332
+ }
333
+
334
+ return rows;
335
+ }
336
+
337
+ /** True for a row the user typed, as opposed to a tool result or an injected note. */
338
+ function isUserPromptRow(row: AnyRecord): boolean {
339
+ if (row.type !== 'user' || row.isMeta === true || row.isCompactSummary === true) {
340
+ return false;
341
+ }
342
+
343
+ const content = row.message?.content;
344
+ if (Array.isArray(content)) {
345
+ return content.some((part: AnyRecord) => part?.type === 'text' || part?.type === 'image');
346
+ }
347
+
348
+ return typeof content === 'string' && content.length > 0;
349
+ }
350
+
351
+ /**
352
+ * Drops the rows belonging to prompts that were replaced by an edit.
353
+ *
354
+ * When a message is edited, Claude resumes the conversation partway and appends
355
+ * the replacement, so two prompts end up sharing one parent and the file holds
356
+ * both the abandoned attempt and the live one. A flat read would show them
357
+ * stacked, which reads as the app having sent the message twice.
358
+ *
359
+ * Only sibling *prompts* are treated as a fork. Branch points made by parallel
360
+ * tool calls are extremely common — one assistant turn writes several chained
361
+ * rows and each tool result parents onto its own — and pruning those would
362
+ * delete tool output from every transcript in the app.
363
+ */
364
+ function dropSupersededPromptBranches(rows: AnyRecord[]): AnyRecord[] {
365
+ const promptSiblings = new Map<string, AnyRecord[]>();
366
+ for (const row of rows) {
367
+ if (typeof row.parentUuid !== 'string' || !isUserPromptRow(row)) {
368
+ continue;
369
+ }
370
+ const siblings = promptSiblings.get(row.parentUuid);
371
+ if (siblings) {
372
+ siblings.push(row);
373
+ } else {
374
+ promptSiblings.set(row.parentUuid, [row]);
375
+ }
376
+ }
377
+
378
+ const supersededRoots = new Set<string>();
379
+ for (const siblings of promptSiblings.values()) {
380
+ if (siblings.length < 2) {
381
+ continue;
382
+ }
383
+ // The transcript is append-only, so the last prompt written under a parent
384
+ // is the one that replaced the others.
385
+ for (const row of siblings.slice(0, -1)) {
386
+ if (typeof row.uuid === 'string') {
387
+ supersededRoots.add(row.uuid);
388
+ }
389
+ }
390
+ }
391
+
392
+ if (supersededRoots.size === 0) {
393
+ return rows;
394
+ }
395
+
396
+ const abandoned = new Set(supersededRoots);
397
+ // Rows are appended in order, so one forward pass propagates each superseded
398
+ // root to its whole subtree.
399
+ for (const row of rows) {
400
+ if (typeof row.parentUuid === 'string' && abandoned.has(row.parentUuid) && typeof row.uuid === 'string') {
401
+ abandoned.add(row.uuid);
402
+ }
403
+ }
404
+
405
+ return rows.filter((row) => typeof row.uuid !== 'string' || !abandoned.has(row.uuid));
103
406
  }
104
407
 
105
408
  async function getSessionMessages(
@@ -118,32 +421,10 @@ async function getSessionMessages(
118
421
  }
119
422
 
120
423
  const projectDir = path.dirname(jsonLPath);
121
- const files = await fsp.readdir(projectDir);
122
- const agentFiles = files.filter((file) => file.endsWith('.jsonl') && file.startsWith('agent-'));
123
-
124
- const messages: AnyRecord[] = [];
125
- const agentToolsCache = new Map<string, AnyRecord[]>();
126
-
127
- const fileStream = fs.createReadStream(jsonLPath);
128
- const rl = readline.createInterface({
129
- input: fileStream,
130
- crlfDelay: Infinity,
131
- });
132
-
133
- for await (const line of rl) {
134
- if (!line.trim()) {
135
- continue;
136
- }
137
424
 
138
- try {
139
- const entry = JSON.parse(line) as AnyRecord;
140
- if (entry.sessionId === providerSessionId) {
141
- messages.push(entry);
142
- }
143
- } catch {
144
- // Skip malformed JSONL lines that can happen during concurrent writes.
145
- }
146
- }
425
+ const messages = dropSupersededPromptBranches(
426
+ await readTranscriptRows(jsonLPath, providerSessionId),
427
+ );
147
428
 
148
429
  const agentIds = new Set<string>();
149
430
  for (const message of messages) {
@@ -153,30 +434,97 @@ async function getSessionMessages(
153
434
  }
154
435
  }
155
436
 
437
+ // Read each spawned agent's own transcript once, then hang it off every
438
+ // row that references it.
439
+ const subagentsById = new Map<string, {
440
+ activity: SubagentActivity[];
441
+ info: SubagentInfo;
442
+ endedMidToolCall: boolean;
443
+ }>();
156
444
  for (const agentId of agentIds) {
157
- const agentFileName = `agent-${agentId}.jsonl`;
158
- if (!agentFiles.includes(agentFileName)) {
445
+ const located = await findClaudeSubagentTranscript(projectDir, providerSessionId, agentId);
446
+ if (!located) {
159
447
  continue;
160
448
  }
161
449
 
162
- const agentFilePath = path.join(projectDir, agentFileName);
163
- const tools = await parseAgentTools(agentFilePath);
164
- agentToolsCache.set(agentId, tools);
450
+ const [transcript, meta] = await Promise.all([
451
+ readClaudeSubagentTranscript(located.transcriptPath),
452
+ readClaudeSubagentMeta(located.metaPath),
453
+ ]);
454
+
455
+ subagentsById.set(agentId, {
456
+ endedMidToolCall: transcript.endedMidToolCall,
457
+ activity: transcript.activity
458
+ .slice(0, MAX_TRANSMITTED_SUBAGENT_ACTIVITIES)
459
+ .map(truncateSubagentActivity),
460
+ info: {
461
+ id: agentId,
462
+ name: meta.agentType,
463
+ type: meta.agentType,
464
+ description: meta.description,
465
+ model: transcript.model,
466
+ status: 'completed',
467
+ activityCount: transcript.activity.length,
468
+ },
469
+ });
165
470
  }
166
471
 
472
+ // An async agent's launch result is internal bookkeeping ("Async agent
473
+ // launched successfully…"); its real answer arrives later as a separate
474
+ // `<task-notification>` turn. Folding the notification back onto the tool
475
+ // call that started the agent keeps one card per agent instead of a card,
476
+ // an unrelated status line, and a stray markdown reply.
477
+ const notificationsByToolUseId = collectTaskNotifications(messages);
478
+ const foldedNotificationUuids = new Set<string>();
479
+
167
480
  for (const message of messages) {
168
481
  const agentId = message.toolUseResult?.agentId;
169
482
  if (!agentId) {
170
483
  continue;
171
484
  }
172
485
 
173
- const agentTools = agentToolsCache.get(String(agentId));
174
- if (agentTools && agentTools.length > 0) {
175
- message.subagentTools = agentTools;
486
+ const subagent = subagentsById.get(String(agentId));
487
+ const toolUseId = readAgentToolUseId(message);
488
+ const notification = toolUseId ? notificationsByToolUseId.get(toolUseId) : undefined;
489
+ // An async agent's launch row never tells you it finished — only the
490
+ // later notification does. When that notification is missing (a live run,
491
+ // or one compacted out of the transcript), the agent's own transcript is
492
+ // the evidence: a timeline that does not stop mid-tool-call is done.
493
+ const isAwaitingAsyncAgent = message.toolUseResult?.isAsync === true
494
+ && !notification
495
+ && (!subagent || subagent.endedMidToolCall);
496
+
497
+ if (subagent) {
498
+ if (subagent.activity.length > 0) {
499
+ message.subagentTools = subagent.activity;
500
+ }
501
+ message.subagent = {
502
+ ...subagent.info,
503
+ description: subagent.info.description
504
+ ?? (typeof message.toolUseResult?.description === 'string' ? message.toolUseResult.description : undefined),
505
+ model: subagent.info.model
506
+ ?? (typeof message.toolUseResult?.resolvedModel === 'string' ? message.toolUseResult.resolvedModel : undefined),
507
+ status: isAwaitingAsyncAgent
508
+ ? 'running'
509
+ : notification && notification.status !== 'completed'
510
+ ? 'failed'
511
+ : 'completed',
512
+ };
513
+ }
514
+
515
+ if (notification) {
516
+ replaceAgentToolResultContent(message, notification.result || notification.summary);
517
+ foldedNotificationUuids.add(notification.sourceUuid);
518
+ } else if (message.toolUseResult?.isAsync === true) {
519
+ // Without a notification there is no answer to show, and the launch
520
+ // acknowledgement is internal bookkeeping the user must never read.
521
+ replaceAgentToolResultContent(message, '');
176
522
  }
177
523
  }
178
524
 
179
- const sortedMessages = messages.sort(
525
+ const sortedMessages = messages
526
+ .filter((message) => !foldedNotificationUuids.has(String(message.uuid ?? '')))
527
+ .sort(
180
528
  (a, b) => new Date(a.timestamp || 0).getTime() - new Date(b.timestamp || 0).getTime(),
181
529
  );
182
530
  const total = sortedMessages.length;
@@ -302,8 +650,31 @@ export class ClaudeSessionsProvider implements IProviderSessions {
302
650
  /**
303
651
  * Normalizes one Claude JSONL entry or live SDK stream event into the shared
304
652
  * message shape consumed by REST and WebSocket clients.
653
+ *
654
+ * Every user turn it produces is stamped with the transcript row's `uuid`,
655
+ * which is the anchor "edit this message" and "fork from here" address. It is
656
+ * applied here rather than at each `createNormalizedMessage` call because the
657
+ * row-shape branches below have several exits, and an anchor missing from one
658
+ * of them would show up as a message the user silently cannot edit.
305
659
  */
306
660
  normalizeMessage(rawMessage: unknown, sessionId: string | null): NormalizedMessage[] {
661
+ const messages = this.normalizeMessageRows(rawMessage, sessionId);
662
+ // A synthesized id is useless as an anchor — it changes on every read — so
663
+ // a row without its own uuid produces messages with no anchor at all.
664
+ const raw = readObjectRecord(rawMessage);
665
+ const anchorId = typeof raw?.uuid === 'string' && raw.uuid ? raw.uuid : null;
666
+ if (anchorId) {
667
+ for (const message of messages) {
668
+ if (message.role === 'user') {
669
+ message.transcriptAnchorId = anchorId;
670
+ }
671
+ }
672
+ }
673
+
674
+ return messages;
675
+ }
676
+
677
+ private normalizeMessageRows(rawMessage: unknown, sessionId: string | null): NormalizedMessage[] {
307
678
  const raw = readObjectRecord(rawMessage);
308
679
  if (!raw) {
309
680
  return [];
@@ -347,7 +718,6 @@ export class ClaudeSessionsProvider implements IProviderSessions {
347
718
  toolId: part.tool_use_id,
348
719
  content: typeof part.content === 'string' ? part.content : JSON.stringify(part.content),
349
720
  isError: Boolean(part.is_error),
350
- subagentTools: raw.subagentTools,
351
721
  toolUseResult: raw.toolUseResult,
352
722
  }));
353
723
  } else if (part.type === 'text') {
@@ -600,6 +970,56 @@ export class ClaudeSessionsProvider implements IProviderSessions {
600
970
  return messages;
601
971
  }
602
972
 
973
+ /**
974
+ * Finds the row to resume *through* so that `anchorId`'s turn is replaced.
975
+ *
976
+ * Walks up the `parentUuid` chain to the nearest assistant row, because the
977
+ * SDK's `resumeSessionAt` is documented against assistant message ids — the
978
+ * user row's immediate parent can be an attachment or an injected note.
979
+ * Returns `null` when nothing precedes the edited prompt, which means the
980
+ * conversation should start over rather than resume.
981
+ */
982
+ async resolveEditAnchor(
983
+ sessionId: string,
984
+ anchorId: string,
985
+ ): Promise<{ found: boolean; resumeThroughId: string | null }> {
986
+ const session = sessionsDb.getSessionById(sessionId);
987
+ const jsonlPath = session?.jsonl_path;
988
+ const providerSessionId = session?.provider_session_id;
989
+ if (!jsonlPath || !providerSessionId) {
990
+ return { found: false, resumeThroughId: null };
991
+ }
992
+
993
+ const rows = await readTranscriptRows(jsonlPath, providerSessionId);
994
+ const byUuid = new Map<string, AnyRecord>();
995
+ for (const row of rows) {
996
+ if (typeof row.uuid === 'string') {
997
+ byUuid.set(row.uuid, row);
998
+ }
999
+ }
1000
+
1001
+ const target = byUuid.get(anchorId);
1002
+ if (!target) {
1003
+ return { found: false, resumeThroughId: null };
1004
+ }
1005
+
1006
+ const visited = new Set<string>([anchorId]);
1007
+ let parentUuid: unknown = target.parentUuid;
1008
+ while (typeof parentUuid === 'string' && !visited.has(parentUuid)) {
1009
+ visited.add(parentUuid);
1010
+ const parent = byUuid.get(parentUuid);
1011
+ if (!parent) {
1012
+ break;
1013
+ }
1014
+ if (parent.type === 'assistant') {
1015
+ return { found: true, resumeThroughId: parentUuid };
1016
+ }
1017
+ parentUuid = parent.parentUuid;
1018
+ }
1019
+
1020
+ return { found: true, resumeThroughId: null };
1021
+ }
1022
+
603
1023
  /**
604
1024
  * Loads Claude JSONL history for a project/session and returns normalized
605
1025
  * messages, preserving the existing pagination behavior from projects.js.
@@ -633,6 +1053,7 @@ export class ClaudeSessionsProvider implements IProviderSessions {
633
1053
  content: part.content,
634
1054
  isError: Boolean(part.is_error),
635
1055
  subagentTools: raw.subagentTools,
1056
+ subagent: raw.subagent,
636
1057
  toolUseResult: raw.toolUseResult,
637
1058
  });
638
1059
  }
@@ -660,18 +1081,17 @@ export class ClaudeSessionsProvider implements IProviderSessions {
660
1081
  toolUseResult: toolResult.toolUseResult,
661
1082
  };
662
1083
  msg.subagentTools = toolResult.subagentTools;
1084
+ msg.subagent = toolResult.subagent;
663
1085
  }
664
1086
  }
665
1087
 
666
- let total = 0;
667
- for (const msg of normalized) {
668
- if (msg.kind !== 'tool_result') {
669
- total += 1;
670
- }
671
- }
1088
+ // Everything the transcript draws, and nothing else — so a page of N rows
1089
+ // is N rows the user sees, and `total` counts the same thing.
1090
+ const transcript = prepareTranscriptMessages(normalized);
1091
+ const total = transcript.length;
672
1092
  const normalizedOffset = Math.max(0, offset);
673
1093
  const normalizedLimit = limit === null ? null : Math.max(0, limit);
674
- const { page, hasMore } = sliceTailPage(normalized, normalizedLimit, normalizedOffset);
1094
+ const { page, hasMore } = sliceTailPage(transcript, normalizedLimit, normalizedOffset);
675
1095
 
676
1096
  return {
677
1097
  messages: page,
@@ -679,6 +1099,10 @@ export class ClaudeSessionsProvider implements IProviderSessions {
679
1099
  hasMore,
680
1100
  offset: normalizedOffset,
681
1101
  limit: normalizedLimit,
1102
+ // Carried on every page, like the Codex and OpenCode readers do, so the
1103
+ // composer's counter tracks the conversation instead of being frozen at
1104
+ // whatever it was when the session was opened.
1105
+ tokenUsage: summarizeClaudeTokenUsage(rawMessages),
682
1106
  };
683
1107
  }
684
1108
  }
@@ -158,12 +158,15 @@ export class ClaudeSkillsProvider extends SkillsProvider {
158
158
  continue;
159
159
  }
160
160
 
161
+ // A plugin may ship commands, skills, or both, and the CLI offers
162
+ // both halves. Reading only the first folder found hid every skill
163
+ // that sits beside a commands folder -- and hid the whole plugin when
164
+ // its commands are in a format this reader does not take.
161
165
  const commandsPath = path.join(pluginFolder, 'commands');
162
166
  if (await pathExistsAsDirectory(commandsPath)) {
163
167
  skills.push(
164
168
  ...(await this.listPluginCommandSkills(commandsPath, pluginId, pluginName)),
165
169
  );
166
- continue;
167
170
  }
168
171
 
169
172
  const skillsPath = path.join(pluginFolder, 'skills');
@@ -2,12 +2,14 @@ import { AbstractProvider } from '@/modules/providers/shared/base/abstract.provi
2
2
  import { ClaudeProviderAuth } from '@/modules/providers/list/claude/claude-auth.provider.js';
3
3
  import { ClaudeProviderModels } from '@/modules/providers/list/claude/claude-models.provider.js';
4
4
  import { claudeRuntime } from '@/modules/providers/list/claude/claude-runtime.provider.js';
5
+ import { ClaudeForkProvider } from '@/modules/providers/list/claude/claude-fork.provider.js';
5
6
  import { ClaudeMcpProvider } from '@/modules/providers/list/claude/claude-mcp.provider.js';
6
7
  import { ClaudeSessionSynchronizer } from '@/modules/providers/list/claude/claude-session-synchronizer.provider.js';
7
8
  import { ClaudeSessionsProvider } from '@/modules/providers/list/claude/claude-sessions.provider.js';
8
9
  import { ClaudeSkillsProvider } from '@/modules/providers/list/claude/claude-skills.provider.js';
9
10
  import type {
10
11
  IProviderAuth,
12
+ IProviderFork,
11
13
  IProviderModels,
12
14
  IProviderRuntime,
13
15
  IProviderSessionSynchronizer,
@@ -23,6 +25,7 @@ export class ClaudeProvider extends AbstractProvider {
23
25
  readonly skills: IProviderSkills = new ClaudeSkillsProvider();
24
26
  readonly sessions: IProviderSessions = new ClaudeSessionsProvider();
25
27
  readonly sessionSynchronizer: IProviderSessionSynchronizer = new ClaudeSessionSynchronizer();
28
+ readonly fork: IProviderFork = new ClaudeForkProvider();
26
29
 
27
30
  constructor() {
28
31
  super('claude');