@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
@@ -1,61 +1,573 @@
1
1
  import assert from 'node:assert/strict';
2
+ import { mkdir, mkdtemp, readFile, rm, writeFile } from 'node:fs/promises';
3
+ import os from 'node:os';
4
+ import path from 'node:path';
2
5
  import test from 'node:test';
3
6
 
7
+ import { closeConnection, initializeDatabase, sessionsDb } from '@/modules/database/index.js';
4
8
  import { ClaudeSessionsProvider } from '@/modules/providers/list/claude/claude-sessions.provider.js';
5
9
 
6
- const SESSION_ID = 'session-1';
10
+ async function withIsolatedDatabase(runTest: () => void | Promise<void>): Promise<void> {
11
+ const previousDatabasePath = process.env.DATABASE_PATH;
12
+ const tempDirectory = await mkdtemp(path.join(os.tmpdir(), 'claude-provider-db-'));
7
13
 
8
- const SKILL_BODY = [
9
- 'Base directory for this skill: /tmp/claude/bundled-skills/2.1.220/abc123/claude-api',
10
- '',
11
- '# Building LLM-Powered Applications with Claude',
12
- '',
13
- 'This skill helps you build LLM-powered applications with Claude.',
14
- ].join('\n');
14
+ closeConnection();
15
+ process.env.DATABASE_PATH = path.join(tempDirectory, 'auth.db');
16
+ await initializeDatabase();
15
17
 
16
- test('claude: injected skill bodies are hidden even without the isMeta flag', () => {
17
- const provider = new ClaudeSessionsProvider();
18
+ try {
19
+ await runTest();
20
+ } finally {
21
+ closeConnection();
22
+ if (previousDatabasePath === undefined) {
23
+ delete process.env.DATABASE_PATH;
24
+ } else {
25
+ process.env.DATABASE_PATH = previousDatabasePath;
26
+ }
27
+ await rm(tempDirectory, { recursive: true, force: true });
28
+ }
29
+ }
18
30
 
19
- // The live SDK stream omits `isMeta`, so the payload has to be recognised by
20
- // its content or it renders as a giant user bubble mid-run.
21
- const live = provider.normalizeMessage(
31
+ const SESSION_ID = 'claude-session-1';
32
+ const AGENT_ID = 'a1b2c3d4e5f60718';
33
+ const AGENT_TOOL_USE_ID = 'toolu_agent_1';
34
+
35
+ /**
36
+ * Writes the transcript pair current Claude versions produce for one async
37
+ * subagent: the parent session, and the agent's own transcript plus sidecar
38
+ * metadata under `<session>/subagents/`.
39
+ */
40
+ async function writeClaudeSubagentSession(projectDirectory: string): Promise<string> {
41
+ const parentPath = path.join(projectDirectory, `${SESSION_ID}.jsonl`);
42
+ const subagentDirectory = path.join(projectDirectory, SESSION_ID, 'subagents');
43
+ await mkdir(subagentDirectory, { recursive: true });
44
+
45
+ const parentLines = [
22
46
  {
23
- uuid: 'u1',
24
- timestamp: '2026-07-28T10:00:00.000Z',
25
- message: { role: 'user', content: [{ type: 'text', text: SKILL_BODY }] },
47
+ type: 'assistant',
48
+ uuid: 'assistant-1',
49
+ sessionId: SESSION_ID,
50
+ timestamp: '2026-08-21T10:00:00.000Z',
51
+ message: {
52
+ role: 'assistant',
53
+ content: [{
54
+ type: 'tool_use',
55
+ id: AGENT_TOOL_USE_ID,
56
+ name: 'Agent',
57
+ input: { subagent_type: 'Explore', description: 'Survey the repo', prompt: 'Look around' },
58
+ }],
59
+ },
26
60
  },
27
- SESSION_ID,
28
- );
29
- assert.deepEqual(live, []);
30
-
31
- const persisted = provider.normalizeMessage(
32
61
  {
33
- uuid: 'u2',
34
- timestamp: '2026-07-28T10:00:00.000Z',
35
- isMeta: true,
36
- message: { role: 'user', content: [{ type: 'text', text: SKILL_BODY }] },
62
+ type: 'user',
63
+ uuid: 'launch-ack-1',
64
+ sessionId: SESSION_ID,
65
+ timestamp: '2026-08-21T10:00:01.000Z',
66
+ message: {
67
+ role: 'user',
68
+ content: [{
69
+ type: 'tool_result',
70
+ tool_use_id: AGENT_TOOL_USE_ID,
71
+ content: 'Async agent launched successfully. agentId: internal bookkeeping',
72
+ }],
73
+ },
74
+ toolUseResult: {
75
+ isAsync: true,
76
+ status: 'async_launched',
77
+ agentId: AGENT_ID,
78
+ description: 'Survey the repo',
79
+ resolvedModel: 'claude-opus-5',
80
+ },
37
81
  },
38
- SESSION_ID,
39
- );
40
- assert.deepEqual(persisted, []);
41
- });
42
-
43
- test('claude: the Skill tool result itself still reaches the UI', () => {
44
- const provider = new ClaudeSessionsProvider();
82
+ {
83
+ type: 'user',
84
+ uuid: 'notification-1',
85
+ sessionId: SESSION_ID,
86
+ timestamp: '2026-08-21T10:05:00.000Z',
87
+ message: {
88
+ role: 'user',
89
+ content: [{
90
+ type: 'text',
91
+ text: [
92
+ '<task-notification>',
93
+ `<task-id>${AGENT_ID}</task-id>`,
94
+ `<tool-use-id>${AGENT_TOOL_USE_ID}</tool-use-id>`,
95
+ '<status>completed</status>',
96
+ '<summary>Agent "Survey the repo" finished</summary>',
97
+ '<result>The repo has two packages.</result>',
98
+ '</task-notification>',
99
+ ].join('\n'),
100
+ }],
101
+ },
102
+ },
103
+ ];
104
+ await writeFile(parentPath, `${parentLines.map((line) => JSON.stringify(line)).join('\n')}\n`, 'utf8');
45
105
 
46
- const messages = provider.normalizeMessage(
106
+ const agentLines = [
107
+ {
108
+ type: 'assistant',
109
+ isSidechain: true,
110
+ agentId: AGENT_ID,
111
+ timestamp: '2026-08-21T10:00:30.000Z',
112
+ message: {
113
+ role: 'assistant',
114
+ model: 'claude-opus-5',
115
+ content: [
116
+ { type: 'text', text: 'Starting the survey.' },
117
+ { type: 'tool_use', id: 'toolu_child_1', name: 'Read', input: { file_path: '/repo/package.json' } },
118
+ ],
119
+ },
120
+ },
47
121
  {
48
- uuid: 'u3',
49
- timestamp: '2026-07-28T10:00:00.000Z',
122
+ type: 'user',
123
+ isSidechain: true,
124
+ agentId: AGENT_ID,
125
+ timestamp: '2026-08-21T10:00:31.000Z',
50
126
  message: {
51
127
  role: 'user',
52
- content: [{ type: 'tool_result', tool_use_id: 'toolu_1', content: 'Launching skill: claude-api' }],
128
+ content: [{ type: 'tool_result', tool_use_id: 'toolu_child_1', content: '{"name":"repo"}' }],
53
129
  },
54
130
  },
55
- SESSION_ID,
131
+ ];
132
+ await writeFile(
133
+ path.join(subagentDirectory, `agent-${AGENT_ID}.jsonl`),
134
+ `${agentLines.map((line) => JSON.stringify(line)).join('\n')}\n`,
135
+ 'utf8',
136
+ );
137
+ await writeFile(
138
+ path.join(subagentDirectory, `agent-${AGENT_ID}.meta.json`),
139
+ JSON.stringify({ agentType: 'Explore', description: 'Survey the repo', toolUseId: AGENT_TOOL_USE_ID, spawnDepth: 1 }),
140
+ 'utf8',
141
+ );
142
+
143
+ return parentPath;
144
+ }
145
+
146
+ test('Claude history attaches a subagent transcript stored under the session directory', { concurrency: false }, async () => {
147
+ const tempRoot = await mkdtemp(path.join(os.tmpdir(), 'claude-subagent-'));
148
+
149
+ try {
150
+ const parentPath = await writeClaudeSubagentSession(tempRoot);
151
+
152
+ await withIsolatedDatabase(async () => {
153
+ const now = new Date().toISOString();
154
+ sessionsDb.createSession(SESSION_ID, 'claude', tempRoot, 'Subagent session', now, now, parentPath);
155
+
156
+ const history = await new ClaudeSessionsProvider().fetchHistory(SESSION_ID, {
157
+ providerSessionId: SESSION_ID,
158
+ });
159
+ const agentRow = history.messages.find(
160
+ (message) => message.kind === 'tool_use' && message.toolId === AGENT_TOOL_USE_ID,
161
+ );
162
+
163
+ assert.ok(agentRow, 'the Agent call must be in the transcript');
164
+ assert.equal(agentRow.subagent?.id, AGENT_ID);
165
+ assert.equal(agentRow.subagent?.type, 'Explore');
166
+ assert.equal(agentRow.subagent?.description, 'Survey the repo');
167
+ assert.equal(agentRow.subagent?.status, 'completed');
168
+
169
+ // The agent's own work — prose and tool calls — comes from its separate
170
+ // transcript, which is the file the previous lookup never found.
171
+ assert.equal(agentRow.subagentTools?.length, 2);
172
+ assert.equal(agentRow.subagentTools?.[0].kind, 'text');
173
+ assert.equal(agentRow.subagentTools?.[1].toolName, 'Read');
174
+ assert.equal(agentRow.subagentTools?.[1].toolResult?.content, '{"name":"repo"}');
175
+ });
176
+ } finally {
177
+ await rm(tempRoot, { recursive: true, force: true });
178
+ }
179
+ });
180
+
181
+ test('Claude history folds an agent task notification into the call that spawned it', { concurrency: false }, async () => {
182
+ const tempRoot = await mkdtemp(path.join(os.tmpdir(), 'claude-notification-'));
183
+
184
+ try {
185
+ const parentPath = await writeClaudeSubagentSession(tempRoot);
186
+
187
+ await withIsolatedDatabase(async () => {
188
+ const now = new Date().toISOString();
189
+ sessionsDb.createSession(SESSION_ID, 'claude', tempRoot, 'Subagent session', now, now, parentPath);
190
+
191
+ const history = await new ClaudeSessionsProvider().fetchHistory(SESSION_ID, {
192
+ providerSessionId: SESSION_ID,
193
+ });
194
+ const agentRow = history.messages.find(
195
+ (message) => message.kind === 'tool_use' && message.toolId === AGENT_TOOL_USE_ID,
196
+ );
197
+
198
+ // The launch acknowledgement is internal bookkeeping; the agent's answer
199
+ // is what belongs on its card.
200
+ assert.equal(agentRow?.toolResult?.content, 'The repo has two packages.');
201
+
202
+ const strayNotification = history.messages.find(
203
+ (message) => typeof message.content === 'string' && message.content.includes('<task-notification>'),
204
+ );
205
+ assert.equal(strayNotification, undefined, 'the folded notification must not also render on its own');
206
+ });
207
+ } finally {
208
+ await rm(tempRoot, { recursive: true, force: true });
209
+ }
210
+ });
211
+
212
+ /** Strips the `<task-notification>` turn so the agent has no reported outcome. */
213
+ async function dropTaskNotification(parentPath: string): Promise<void> {
214
+ const raw = await readFile(parentPath, 'utf8');
215
+ await writeFile(
216
+ parentPath,
217
+ `${raw.split('\n').filter((line) => line && !line.includes('task-notification')).join('\n')}\n`,
218
+ 'utf8',
56
219
  );
220
+ }
221
+
222
+ test('Claude history reads a missing notification off the agent\'s own transcript', { concurrency: false }, async () => {
223
+ const tempRoot = await mkdtemp(path.join(os.tmpdir(), 'claude-finished-agent-'));
224
+
225
+ try {
226
+ const parentPath = await writeClaudeSubagentSession(tempRoot);
227
+ await dropTaskNotification(parentPath);
228
+
229
+ await withIsolatedDatabase(async () => {
230
+ const now = new Date().toISOString();
231
+ sessionsDb.createSession(SESSION_ID, 'claude', tempRoot, 'Subagent session', now, now, parentPath);
232
+
233
+ const history = await new ClaudeSessionsProvider().fetchHistory(SESSION_ID, {
234
+ providerSessionId: SESSION_ID,
235
+ });
236
+ const agentRow = history.messages.find(
237
+ (message) => message.kind === 'tool_use' && message.toolId === AGENT_TOOL_USE_ID,
238
+ );
239
+
240
+ // The notification can be compacted out of a long session. The agent's
241
+ // transcript ends on a resolved tool call, so it finished — reporting it
242
+ // as still running would leave a spinner on the card forever.
243
+ assert.equal(agentRow?.subagent?.status, 'completed');
244
+ assert.equal(agentRow?.toolResult?.content, '', 'the launch acknowledgement must never show as a result');
245
+ });
246
+ } finally {
247
+ await rm(tempRoot, { recursive: true, force: true });
248
+ }
249
+ });
250
+
251
+ test('Claude history keeps an agent running when its transcript stops mid tool call', { concurrency: false }, async () => {
252
+ const tempRoot = await mkdtemp(path.join(os.tmpdir(), 'claude-running-agent-'));
253
+
254
+ try {
255
+ const parentPath = await writeClaudeSubagentSession(tempRoot);
256
+ await dropTaskNotification(parentPath);
257
+
258
+ // Drop the child tool result: the agent is mid-call, which is the only
259
+ // in-file evidence that it is still working.
260
+ const agentPath = path.join(tempRoot, SESSION_ID, 'subagents', `agent-${AGENT_ID}.jsonl`);
261
+ const agentRaw = await readFile(agentPath, 'utf8');
262
+ await writeFile(
263
+ agentPath,
264
+ `${agentRaw.split('\n').filter((line) => line && !line.includes('tool_result')).join('\n')}\n`,
265
+ 'utf8',
266
+ );
267
+
268
+ await withIsolatedDatabase(async () => {
269
+ const now = new Date().toISOString();
270
+ sessionsDb.createSession(SESSION_ID, 'claude', tempRoot, 'Subagent session', now, now, parentPath);
271
+
272
+ const history = await new ClaudeSessionsProvider().fetchHistory(SESSION_ID, {
273
+ providerSessionId: SESSION_ID,
274
+ });
275
+ const agentRow = history.messages.find(
276
+ (message) => message.kind === 'tool_use' && message.toolId === AGENT_TOOL_USE_ID,
277
+ );
278
+
279
+ assert.equal(agentRow?.subagent?.status, 'running');
280
+ });
281
+ } finally {
282
+ await rm(tempRoot, { recursive: true, force: true });
283
+ }
284
+ });
285
+
286
+ test('Claude history trims a subagent timeline down to a preview', { concurrency: false }, async () => {
287
+ const tempRoot = await mkdtemp(path.join(os.tmpdir(), 'claude-big-agent-'));
288
+
289
+ try {
290
+ const parentPath = await writeClaudeSubagentSession(tempRoot);
291
+
292
+ // One child command with a very large output, which is what makes an
293
+ // agent-heavy session's history payload balloon.
294
+ const hugeOutput = 'x'.repeat(50_000);
295
+ const agentPath = path.join(tempRoot, SESSION_ID, 'subagents', `agent-${AGENT_ID}.jsonl`);
296
+ const agentRaw = await readFile(agentPath, 'utf8');
297
+ const enlarged = agentRaw
298
+ .split('\n')
299
+ .filter(Boolean)
300
+ .map((line) => {
301
+ const entry = JSON.parse(line) as { message?: { content?: Array<{ type?: string; content?: string }> } };
302
+ for (const part of entry.message?.content ?? []) {
303
+ if (part.type === 'tool_result') {
304
+ part.content = hugeOutput;
305
+ }
306
+ }
307
+ return JSON.stringify(entry);
308
+ })
309
+ .join('\n');
310
+ await writeFile(agentPath, `${enlarged}\n`, 'utf8');
311
+
312
+ await withIsolatedDatabase(async () => {
313
+ const now = new Date().toISOString();
314
+ sessionsDb.createSession(SESSION_ID, 'claude', tempRoot, 'Subagent session', now, now, parentPath);
315
+
316
+ const history = await new ClaudeSessionsProvider().fetchHistory(SESSION_ID, {
317
+ providerSessionId: SESSION_ID,
318
+ });
319
+ const agentRow = history.messages.find(
320
+ (message) => message.kind === 'tool_use' && message.toolId === AGENT_TOOL_USE_ID,
321
+ );
322
+ const childResult = String(agentRow?.subagentTools?.[1].toolResult?.content ?? '');
323
+
324
+ assert.ok(childResult.length < 6000, `nested output must be trimmed, got ${childResult.length}`);
325
+ assert.match(childResult, /more characters$/, 'the trim must say how much was omitted');
326
+ });
327
+ } finally {
328
+ await rm(tempRoot, { recursive: true, force: true });
329
+ }
330
+ });
331
+
332
+ const EDIT_SESSION_ID = 'claude-edit-session';
333
+
334
+ /**
335
+ * Writes a transcript where one prompt was edited: the replacement shares a
336
+ * parent with the original, which is the shape Claude's resume-partway leaves
337
+ * behind. Nothing is deleted from the file.
338
+ */
339
+ async function writeEditedTranscript(projectDirectory: string): Promise<string> {
340
+ const transcriptPath = path.join(projectDirectory, `${EDIT_SESSION_ID}.jsonl`);
341
+ const rows = [
342
+ {
343
+ type: 'user', uuid: 'u1', parentUuid: null, sessionId: EDIT_SESSION_ID,
344
+ timestamp: '2026-08-23T10:00:00.000Z',
345
+ message: { role: 'user', content: [{ type: 'text', text: 'first prompt' }] },
346
+ },
347
+ {
348
+ type: 'assistant', uuid: 'a1', parentUuid: 'u1', sessionId: EDIT_SESSION_ID,
349
+ timestamp: '2026-08-23T10:00:01.000Z',
350
+ message: { role: 'assistant', model: 'claude-opus-5', content: [{ type: 'text', text: 'first answer' }] },
351
+ },
352
+ {
353
+ type: 'user', uuid: 'u2', parentUuid: 'a1', sessionId: EDIT_SESSION_ID,
354
+ timestamp: '2026-08-23T10:00:02.000Z',
355
+ message: { role: 'user', content: [{ type: 'text', text: 'original second prompt' }] },
356
+ },
357
+ {
358
+ type: 'assistant', uuid: 'a2', parentUuid: 'u2', sessionId: EDIT_SESSION_ID,
359
+ timestamp: '2026-08-23T10:00:03.000Z',
360
+ message: { role: 'assistant', model: 'claude-opus-5', content: [{ type: 'text', text: 'answer to be replaced' }] },
361
+ },
362
+ // The edit: same parent as u2, written later.
363
+ {
364
+ type: 'user', uuid: 'u2b', parentUuid: 'a1', sessionId: EDIT_SESSION_ID,
365
+ timestamp: '2026-08-23T10:00:04.000Z',
366
+ message: { role: 'user', content: [{ type: 'text', text: 'edited second prompt' }] },
367
+ },
368
+ {
369
+ type: 'assistant', uuid: 'a2b', parentUuid: 'u2b', sessionId: EDIT_SESSION_ID,
370
+ timestamp: '2026-08-23T10:00:05.000Z',
371
+ message: { role: 'assistant', model: 'claude-opus-5', content: [{ type: 'text', text: 'answer to the edit' }] },
372
+ },
373
+ ];
374
+
375
+ await writeFile(transcriptPath, `${rows.map((row) => JSON.stringify(row)).join('\n')}\n`, 'utf8');
376
+ return transcriptPath;
377
+ }
378
+
379
+ test('an edited prompt replaces the one it superseded instead of stacking on it', { concurrency: false }, async () => {
380
+ const tempRoot = await mkdtemp(path.join(os.tmpdir(), 'claude-edit-history-'));
381
+
382
+ try {
383
+ const transcriptPath = await writeEditedTranscript(tempRoot);
384
+
385
+ await withIsolatedDatabase(async () => {
386
+ const now = new Date().toISOString();
387
+ sessionsDb.createSession(EDIT_SESSION_ID, 'claude', tempRoot, 'Edited session', now, now, transcriptPath);
388
+
389
+ const history = await new ClaudeSessionsProvider().fetchHistory(EDIT_SESSION_ID, {
390
+ providerSessionId: EDIT_SESSION_ID,
391
+ });
392
+ const texts = history.messages.map((message) => message.content);
393
+
394
+ assert.deepEqual(texts, [
395
+ 'first prompt',
396
+ 'first answer',
397
+ 'edited second prompt',
398
+ 'answer to the edit',
399
+ ]);
400
+ });
401
+ } finally {
402
+ await rm(tempRoot, { recursive: true, force: true });
403
+ }
404
+ });
405
+
406
+ test('parallel tool calls are not mistaken for an edit', { concurrency: false }, async () => {
407
+ const tempRoot = await mkdtemp(path.join(os.tmpdir(), 'claude-parallel-tools-'));
408
+ const sessionId = 'claude-parallel-session';
409
+
410
+ try {
411
+ const transcriptPath = path.join(tempRoot, `${sessionId}.jsonl`);
412
+ // One assistant turn issuing two tools: each tool_result parents onto the
413
+ // same row, so this row has two children — a branch point that must not be
414
+ // pruned, or tool output disappears from every transcript in the app.
415
+ const rows = [
416
+ {
417
+ type: 'user', uuid: 'p1', parentUuid: null, sessionId,
418
+ timestamp: '2026-08-23T10:00:00.000Z',
419
+ message: { role: 'user', content: [{ type: 'text', text: 'do two things' }] },
420
+ },
421
+ {
422
+ type: 'assistant', uuid: 'pa1', parentUuid: 'p1', sessionId,
423
+ timestamp: '2026-08-23T10:00:01.000Z',
424
+ message: {
425
+ role: 'assistant', model: 'claude-opus-5',
426
+ content: [{ type: 'tool_use', id: 'tool-1', name: 'Read', input: { file_path: '/a' } }],
427
+ },
428
+ },
429
+ {
430
+ type: 'user', uuid: 'pr1', parentUuid: 'pa1', sessionId,
431
+ timestamp: '2026-08-23T10:00:02.000Z',
432
+ message: { role: 'user', content: [{ type: 'tool_result', tool_use_id: 'tool-1', content: 'contents of a' }] },
433
+ },
434
+ {
435
+ type: 'user', uuid: 'pr2', parentUuid: 'pa1', sessionId,
436
+ timestamp: '2026-08-23T10:00:03.000Z',
437
+ message: { role: 'user', content: [{ type: 'tool_result', tool_use_id: 'tool-2', content: 'contents of b' }] },
438
+ },
439
+ ];
440
+ await writeFile(transcriptPath, `${rows.map((row) => JSON.stringify(row)).join('\n')}\n`, 'utf8');
441
+
442
+ await withIsolatedDatabase(async () => {
443
+ const now = new Date().toISOString();
444
+ sessionsDb.createSession(sessionId, 'claude', tempRoot, 'Parallel tools', now, now, transcriptPath);
445
+
446
+ const history = await new ClaudeSessionsProvider().fetchHistory(sessionId, {
447
+ providerSessionId: sessionId,
448
+ });
449
+
450
+ assert.equal(
451
+ history.messages.some((message) => message.content === 'do two things'),
452
+ true,
453
+ );
454
+ const toolRow = history.messages.find((message) => message.kind === 'tool_use');
455
+ assert.ok(toolRow, 'the tool call survives');
456
+ assert.equal(toolRow?.toolResult?.content, 'contents of a');
457
+ });
458
+ } finally {
459
+ await rm(tempRoot, { recursive: true, force: true });
460
+ }
461
+ });
462
+
463
+ test('resolving an edit anchor returns the assistant turn before it', { concurrency: false }, async () => {
464
+ const tempRoot = await mkdtemp(path.join(os.tmpdir(), 'claude-edit-anchor-'));
465
+
466
+ try {
467
+ const transcriptPath = await writeEditedTranscript(tempRoot);
468
+
469
+ await withIsolatedDatabase(async () => {
470
+ const now = new Date().toISOString();
471
+ sessionsDb.createSession(EDIT_SESSION_ID, 'claude', tempRoot, 'Edited session', now, now, transcriptPath);
472
+ const provider = new ClaudeSessionsProvider();
473
+
474
+ // Resuming is inclusive of the row it names, so replacing `u2b` must
475
+ // resume through `a1` — naming `u2b` itself would leave the prompt being
476
+ // replaced in context.
477
+ assert.deepEqual(
478
+ await provider.resolveEditAnchor(EDIT_SESSION_ID, 'u2b'),
479
+ { found: true, resumeThroughId: 'a1' },
480
+ );
481
+
482
+ // Nothing precedes the first prompt, so the conversation starts over.
483
+ assert.deepEqual(
484
+ await provider.resolveEditAnchor(EDIT_SESSION_ID, 'u1'),
485
+ { found: true, resumeThroughId: null },
486
+ );
487
+
488
+ assert.deepEqual(
489
+ await provider.resolveEditAnchor(EDIT_SESSION_ID, 'not-in-transcript'),
490
+ { found: false, resumeThroughId: null },
491
+ );
492
+ });
493
+ } finally {
494
+ await rm(tempRoot, { recursive: true, force: true });
495
+ }
496
+ });
497
+
498
+ test('user turns carry the transcript uuid so they can be edited', { concurrency: false }, async () => {
499
+ const tempRoot = await mkdtemp(path.join(os.tmpdir(), 'claude-anchor-ids-'));
500
+
501
+ try {
502
+ const transcriptPath = await writeEditedTranscript(tempRoot);
503
+
504
+ await withIsolatedDatabase(async () => {
505
+ const now = new Date().toISOString();
506
+ sessionsDb.createSession(EDIT_SESSION_ID, 'claude', tempRoot, 'Edited session', now, now, transcriptPath);
507
+
508
+ const history = await new ClaudeSessionsProvider().fetchHistory(EDIT_SESSION_ID, {
509
+ providerSessionId: EDIT_SESSION_ID,
510
+ });
511
+
512
+ const userRows = history.messages.filter((message) => message.role === 'user');
513
+ assert.deepEqual(
514
+ userRows.map((message) => message.transcriptAnchorId),
515
+ ['u1', 'u2b'],
516
+ );
517
+ // Assistant rows are never an anchor: the UI only offers editing on a
518
+ // turn the user typed.
519
+ assert.equal(
520
+ history.messages.some((message) => message.role !== 'user' && message.transcriptAnchorId),
521
+ false,
522
+ );
523
+ });
524
+ } finally {
525
+ await rm(tempRoot, { recursive: true, force: true });
526
+ }
527
+ });
528
+
529
+ test('resolving an edit anchor skips rows that are not conversation turns', { concurrency: false }, async () => {
530
+ const tempRoot = await mkdtemp(path.join(os.tmpdir(), 'claude-anchor-skip-'));
531
+ const sessionId = 'claude-anchor-skip-session';
532
+
533
+ try {
534
+ const transcriptPath = path.join(tempRoot, `${sessionId}.jsonl`);
535
+ // An attachment row sits between the assistant turn and the next prompt.
536
+ // Resuming names an assistant message, so the walk has to pass over it —
537
+ // naming the attachment would resume at something the SDK cannot address.
538
+ const rows = [
539
+ {
540
+ type: 'user', uuid: 'su1', parentUuid: null, sessionId,
541
+ timestamp: '2026-08-23T10:00:00.000Z',
542
+ message: { role: 'user', content: [{ type: 'text', text: 'hello' }] },
543
+ },
544
+ {
545
+ type: 'assistant', uuid: 'sa1', parentUuid: 'su1', sessionId,
546
+ timestamp: '2026-08-23T10:00:01.000Z',
547
+ message: { role: 'assistant', model: 'claude-opus-5', content: [{ type: 'text', text: 'hi' }] },
548
+ },
549
+ {
550
+ type: 'attachment', uuid: 'sat1', parentUuid: 'sa1', sessionId,
551
+ timestamp: '2026-08-23T10:00:02.000Z',
552
+ },
553
+ {
554
+ type: 'user', uuid: 'su2', parentUuid: 'sat1', sessionId,
555
+ timestamp: '2026-08-23T10:00:03.000Z',
556
+ message: { role: 'user', content: [{ type: 'text', text: 'second prompt' }] },
557
+ },
558
+ ];
559
+ await writeFile(transcriptPath, `${rows.map((row) => JSON.stringify(row)).join('\n')}\n`, 'utf8');
560
+
561
+ await withIsolatedDatabase(async () => {
562
+ const now = new Date().toISOString();
563
+ sessionsDb.createSession(sessionId, 'claude', tempRoot, 'Anchor skip', now, now, transcriptPath);
57
564
 
58
- assert.equal(messages.length, 1);
59
- assert.equal(messages[0].kind, 'tool_result');
60
- assert.equal(messages[0].toolId, 'toolu_1');
565
+ assert.deepEqual(
566
+ await new ClaudeSessionsProvider().resolveEditAnchor(sessionId, 'su2'),
567
+ { found: true, resumeThroughId: 'sa1' },
568
+ );
569
+ });
570
+ } finally {
571
+ await rm(tempRoot, { recursive: true, force: true });
572
+ }
61
573
  });
@@ -0,0 +1,25 @@
1
+ import assert from 'node:assert/strict';
2
+ import test from 'node:test';
3
+
4
+ import { isSubagentPromptEcho } from '@/modules/providers/list/claude/claude-runtime.provider.js';
5
+
6
+ test('a subagent prompt echo is suppressed', () => {
7
+ // The Agent tool card already shows this prompt, and the transcript keeps the
8
+ // turn in the subagent sidechain, so streaming it stacked a duplicate user
9
+ // bubble that vanished on reload.
10
+ assert.equal(
11
+ isSubagentPromptEcho({ kind: 'text', role: 'user', parentToolUseId: 'toolu_1', content: 'Investigate' }),
12
+ true,
13
+ );
14
+ });
15
+
16
+ test('the session prompt is kept', () => {
17
+ assert.equal(isSubagentPromptEcho({ kind: 'text', role: 'user', content: 'Hello' }), false);
18
+ });
19
+
20
+ test('subagent tool traffic is kept', () => {
21
+ assert.equal(
22
+ isSubagentPromptEcho({ kind: 'tool_result', toolId: 'toolu_2', parentToolUseId: 'toolu_1' }),
23
+ false,
24
+ );
25
+ });