@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
@@ -69,13 +69,24 @@ function resolveClaudeWrapperBinary(
69
69
  return null;
70
70
  }
71
71
 
72
+ /**
73
+ * Windows has no PATH lookup to fall back on: the SDK spawns the path we hand
74
+ * it with a raw `child_process.spawn`, which never consults PATH or PATHEXT.
75
+ * A bare `claude` therefore fails with "native binary not found at claude"
76
+ * even on a machine where the CLI is installed and on PATH, so an unresolved
77
+ * default returns undefined and lets the SDK use its own bundled binary.
78
+ */
72
79
  function resolveWindowsClaudeExecutablePath(
73
80
  configuredPath: string,
81
+ configuredExplicitly: boolean,
74
82
  deps: Required<ResolveClaudeCodeExecutablePathDependencies>,
75
- ): string {
83
+ ): string | undefined {
76
84
  const pathApi = getPathApi(deps.platform);
77
85
  const extension = pathApi.extname(configuredPath).toLowerCase();
78
86
  const explicitPath = isPathLike(configuredPath) || pathApi.isAbsolute(configuredPath);
87
+ // An explicit CLAUDE_CLI_PATH is the operator's call even when we cannot
88
+ // verify it; only our own `claude` default defers to the SDK.
89
+ const unresolved = configuredExplicitly ? configuredPath : undefined;
79
90
 
80
91
  if (CLAUDE_SCRIPT_EXTENSIONS.has(extension)) {
81
92
  return configuredPath;
@@ -86,7 +97,7 @@ function resolveWindowsClaudeExecutablePath(
86
97
  }
87
98
 
88
99
  if (explicitPath) {
89
- return resolveClaudeWrapperBinary(configuredPath, deps) ?? configuredPath;
100
+ return resolveClaudeWrapperBinary(configuredPath, deps) ?? unresolved;
90
101
  }
91
102
 
92
103
  try {
@@ -113,16 +124,22 @@ function resolveWindowsClaudeExecutablePath(
113
124
  }
114
125
  }
115
126
  } catch {
116
- return configuredPath;
127
+ return unresolved;
117
128
  }
118
129
 
119
- return configuredPath;
130
+ return unresolved;
120
131
  }
121
132
 
133
+ /**
134
+ * Resolves the Claude Code executable to hand the SDK.
135
+ *
136
+ * Returns undefined when no real executable could be found and the caller did
137
+ * not configure one, which means "let the SDK pick its own bundled binary".
138
+ */
122
139
  export function resolveClaudeCodeExecutablePath(
123
140
  configuredPath: string | undefined = process.env.CLAUDE_CLI_PATH,
124
141
  dependencies: ResolveClaudeCodeExecutablePathDependencies = {},
125
- ): string {
142
+ ): string | undefined {
126
143
  const deps: Required<ResolveClaudeCodeExecutablePathDependencies> = {
127
144
  execFileSync: dependencies.execFileSync ?? execFileSync,
128
145
  existsSync: dependencies.existsSync ?? fs.existsSync,
@@ -130,10 +147,11 @@ export function resolveClaudeCodeExecutablePath(
130
147
  readFileSync: dependencies.readFileSync ?? fs.readFileSync,
131
148
  };
132
149
 
150
+ const configuredExplicitly = Boolean(stripWrappingQuotes(configuredPath || ''));
133
151
  const normalizedPath = stripWrappingQuotes(configuredPath || DEFAULT_CLAUDE_COMMAND);
134
152
  if (deps.platform !== 'win32') {
135
153
  return normalizedPath;
136
154
  }
137
155
 
138
- return resolveWindowsClaudeExecutablePath(normalizedPath, deps);
156
+ return resolveWindowsClaudeExecutablePath(normalizedPath, configuredExplicitly, deps);
139
157
  }
@@ -0,0 +1,3 @@
1
+ export { appendFilesInputTag, buildCodexInputItems, normalizeImageDescriptors } from './image-attachments.js';
2
+ export { createCompleteMessage, createNormalizedMessage } from './utils.js';
3
+ export type { AnyRecord, ProviderRuntimeContext, ProviderRuntimeWriter } from './types.js';
@@ -53,6 +53,36 @@ export interface IProvider {
53
53
  readonly skills: IProviderSkills;
54
54
  readonly sessions: IProviderSessions;
55
55
  readonly sessionSynchronizer: IProviderSessionSynchronizer;
56
+ /**
57
+ * Transcript branching. Present only for providers that can materialise a
58
+ * prefix of one conversation as an independent, resumable provider session;
59
+ * its absence is what makes "fork session" unavailable.
60
+ */
61
+ readonly fork?: IProviderFork;
62
+ }
63
+
64
+ // ---------------------------
65
+ //----------------- PROVIDER FORK INTERFACE ------------
66
+ /**
67
+ * Transcript-branching contract for one provider.
68
+ */
69
+ export interface IProviderFork {
70
+ /**
71
+ * Copies a session's transcript, up to and including `upToAnchorId` (the
72
+ * whole conversation when omitted), into a brand-new provider session.
73
+ *
74
+ * Returns the new provider-native id and the path of the artifact it wrote,
75
+ * so the caller can insert the database row before the filesystem watcher
76
+ * notices the file and indexes it as an unrelated session.
77
+ */
78
+ forkSession(input: {
79
+ providerSessionId: string;
80
+ jsonlPath: string;
81
+ /** The session's working directory — how providers scope a session lookup. */
82
+ projectPath: string;
83
+ upToAnchorId?: string;
84
+ title?: string;
85
+ }): Promise<{ providerSessionId: string; jsonlPath: string }>;
56
86
  }
57
87
 
58
88
  // ---------------------------
@@ -154,6 +184,38 @@ export interface IProviderMcp {
154
184
  export interface IProviderSessions {
155
185
  normalizeMessage(raw: unknown, sessionId: string | null): NormalizedMessage[];
156
186
  fetchHistory(sessionId: string, options?: FetchHistoryOptions): Promise<FetchHistoryResult>;
187
+
188
+ /**
189
+ * Resolves where a conversation must resume from so that the turn identified
190
+ * by `anchorId`, and everything after it, is replaced.
191
+ *
192
+ * `resumeThroughId` is the last transcript row to KEEP — resuming is
193
+ * inclusive of it — or `null` when the edited turn is the first prompt and
194
+ * the conversation should start over. `found` is false when `anchorId` is not
195
+ * in the transcript, which the caller must report rather than guess at.
196
+ *
197
+ * Implemented only by providers whose transcripts have stable per-row
198
+ * identity; its absence is what makes "edit this message" unavailable.
199
+ */
200
+ resolveEditAnchor?(
201
+ sessionId: string,
202
+ anchorId: string,
203
+ ): Promise<{ found: boolean; resumeThroughId: string | null }>;
204
+
205
+ /**
206
+ * Rewinds the session so `keepThroughId` is the last row of its
207
+ * conversation, and points the session at whatever provider session now
208
+ * holds it. `null` keeps nothing: the conversation starts over.
209
+ *
210
+ * Implemented only by providers whose runtime cannot resume a transcript
211
+ * partway and have to branch instead. Codex is one — it has no
212
+ * resume-at-a-row, so the rewind is a `thread/fork` that leaves the pre-edit
213
+ * thread on disk and moves the session onto the copy. Providers that can
214
+ * resume partway leave this undefined, and the anchor is handed to their
215
+ * runtime as a resume option instead; that is the difference the edit
216
+ * gateway branches on.
217
+ */
218
+ rewindSession?(sessionId: string, keepThroughId: string | null): Promise<void>;
157
219
  }
158
220
 
159
221
  // ---------------------------
@@ -0,0 +1,449 @@
1
+ import type { AnyRecord, NormalizedMessage } from '@/shared/types.js';
2
+ import { readObjectRecord } from '@/shared/utils.js';
3
+
4
+ /**
5
+ * Cross-provider unification of the two interaction surfaces that Claude and
6
+ * Codex both have but spell differently: the running checklist and the
7
+ * ask-the-user round trip.
8
+ *
9
+ * Each provider parses its own transcript format first; this pass runs once the
10
+ * tool results have been attached and rewrites those rows onto a single
11
+ * canonical shape, so the chat needs exactly one renderer per surface:
12
+ *
13
+ * - checklist → a `TodoWrite` call whose input is the full list at that point
14
+ * - question → an `AskUserQuestion` call whose input carries the chosen answers
15
+ *
16
+ * Doing it here rather than in each provider is what keeps the two feeling like
17
+ * one product: Claude's incremental `TaskCreate`/`TaskUpdate` calls and Codex's
18
+ * whole-list `update_plan` calls both come out as the same evolving checklist.
19
+ */
20
+
21
+ //----------------- CANONICAL TOOL NAMES ------------
22
+
23
+ /** The one tool name every provider's checklist is rewritten onto. */
24
+ const CHECKLIST_TOOL = 'TodoWrite';
25
+
26
+ /** The one tool name every provider's user-question call is rewritten onto. */
27
+ const ASK_TOOL = 'AskUserQuestion';
28
+
29
+ /** Provider spellings of the ask-the-user tool. Codex calls it `request_user_input`. */
30
+ const ASK_TOOL_ALIASES = new Set([ASK_TOOL, 'request_user_input']);
31
+
32
+ /**
33
+ * Claude's incremental task tracker. Each call mutates one entry of a list the
34
+ * transcript never states in full, so the list is rebuilt here and every call
35
+ * is replaced by the resulting snapshot.
36
+ */
37
+ const TASK_TOOLS = new Set(['TaskCreate', 'TaskUpdate', 'TaskList', 'TaskGet']);
38
+
39
+ //----------------- SHARED PAYLOAD READERS ------------
40
+
41
+ /**
42
+ * Reads a tool payload that providers store either as an object (Claude) or as
43
+ * the raw JSON string it arrived as (Codex).
44
+ */
45
+ function readToolPayload(value: unknown): AnyRecord | null {
46
+ if (typeof value === 'string') {
47
+ const trimmed = value.trim();
48
+ if (!trimmed.startsWith('{')) {
49
+ return null;
50
+ }
51
+ try {
52
+ return readObjectRecord(JSON.parse(trimmed));
53
+ } catch {
54
+ return null;
55
+ }
56
+ }
57
+
58
+ return readObjectRecord(value);
59
+ }
60
+
61
+ function readNonEmptyString(value: unknown): string {
62
+ return typeof value === 'string' && value.trim() ? value : '';
63
+ }
64
+
65
+ //----------------- ASK-THE-USER ANSWERS ------------
66
+
67
+ /**
68
+ * Flattens one answer value into the comma-joined label list the question view
69
+ * renders. Claude stores the labels already joined, Codex stores them as
70
+ * `{ answers: [...] }`, and a multi-select answer is an array either way.
71
+ */
72
+ function readAnswerLabels(value: unknown): string {
73
+ if (typeof value === 'string') {
74
+ return value;
75
+ }
76
+
77
+ if (Array.isArray(value)) {
78
+ return value.filter((entry): entry is string => typeof entry === 'string').join(', ');
79
+ }
80
+
81
+ const record = readObjectRecord(value);
82
+ return record ? readAnswerLabels(record.answers) : '';
83
+ }
84
+
85
+ /**
86
+ * Recovers the answers from Claude's acknowledgement sentence.
87
+ *
88
+ * A live turn only carries the prose result — `Your questions have been
89
+ * answered: "<question>"="<answer>".` — because the structured `toolUseResult`
90
+ * is written when the transcript is persisted, not when the tool returns. The
91
+ * selection would otherwise be invisible for the whole run.
92
+ */
93
+ function readAnswersFromAcknowledgement(content: string): Record<string, string> {
94
+ if (!content.includes('questions have been answered')) {
95
+ return {};
96
+ }
97
+
98
+ const answers: Record<string, string> = {};
99
+ const pattern = /"([^"]+)"\s*=\s*"([^"]*)"/g;
100
+ let match = pattern.exec(content);
101
+ while (match) {
102
+ answers[match[1]] = match[2];
103
+ match = pattern.exec(content);
104
+ }
105
+
106
+ return answers;
107
+ }
108
+
109
+ /**
110
+ * Rewrites answers onto the question text they belong to.
111
+ *
112
+ * Codex keys its answers by the question's `id`; Claude keys them by the
113
+ * question text. Settling on the text means the renderer needs no provider
114
+ * knowledge to pair a question with its answer.
115
+ */
116
+ function keyAnswersByQuestion(questions: unknown, rawAnswers: AnyRecord): Record<string, string> {
117
+ const questionById = new Map<string, string>();
118
+ if (Array.isArray(questions)) {
119
+ for (const entry of questions) {
120
+ const question = readObjectRecord(entry);
121
+ const id = readNonEmptyString(question?.id);
122
+ const text = readNonEmptyString(question?.question);
123
+ if (id && text) {
124
+ questionById.set(id, text);
125
+ }
126
+ }
127
+ }
128
+
129
+ const answers: Record<string, string> = {};
130
+ for (const [key, value] of Object.entries(rawAnswers)) {
131
+ const labels = readAnswerLabels(value);
132
+ if (labels) {
133
+ answers[questionById.get(key) ?? key] = labels;
134
+ }
135
+ }
136
+
137
+ return answers;
138
+ }
139
+
140
+ /** Pulls the answer map out of whichever result shape the provider produced. */
141
+ function readAskAnswers(message: NormalizedMessage): AnyRecord | null {
142
+ const structured = readObjectRecord(readObjectRecord(message.toolResult?.toolUseResult)?.answers);
143
+ if (structured) {
144
+ return structured;
145
+ }
146
+
147
+ const content = readNonEmptyString(message.toolResult?.content);
148
+ if (!content) {
149
+ return null;
150
+ }
151
+
152
+ const fromPayload = readObjectRecord(readToolPayload(content)?.answers);
153
+ if (fromPayload) {
154
+ return fromPayload;
155
+ }
156
+
157
+ const fromSentence = readAnswersFromAcknowledgement(content);
158
+ return Object.keys(fromSentence).length > 0 ? fromSentence : null;
159
+ }
160
+
161
+ /**
162
+ * Normalizes one ask-the-user call: canonical name, and the answers folded into
163
+ * the input so the question and what was picked render as one card instead of a
164
+ * question card followed by an unreadable result blob.
165
+ */
166
+ function unifyAskCall(message: NormalizedMessage): void {
167
+ message.toolName = ASK_TOOL;
168
+
169
+ const input = readToolPayload(message.toolInput);
170
+ if (!input || !Array.isArray(input.questions)) {
171
+ return;
172
+ }
173
+
174
+ const rawAnswers = readAskAnswers(message);
175
+ message.toolInput = rawAnswers
176
+ ? { ...input, answers: keyAnswersByQuestion(input.questions, rawAnswers) }
177
+ : input;
178
+ }
179
+
180
+ //----------------- RUNNING CHECKLIST ------------
181
+
182
+ type ChecklistEntry = {
183
+ id: string;
184
+ content: string;
185
+ status: string;
186
+ activeForm?: string;
187
+ };
188
+
189
+ /** Claude's task statuses already match the todo vocabulary; anything else is pending. */
190
+ function readTaskStatus(value: unknown): string {
191
+ const status = readNonEmptyString(value);
192
+ return status === 'in_progress' || status === 'completed' ? status : 'pending';
193
+ }
194
+
195
+ /**
196
+ * Replays Claude's task-tracker calls into the list they describe.
197
+ *
198
+ * `TaskCreate` returns the id the tracker assigned, so entries are keyed by it
199
+ * and later `TaskUpdate` calls land on the right row. A create whose result has
200
+ * not arrived yet is keyed by its tool call id, which is unique for the same
201
+ * reason an assigned id would be.
202
+ */
203
+ class ChecklistState {
204
+ private readonly entries = new Map<string, ChecklistEntry>();
205
+
206
+ applyTaskCall(message: NormalizedMessage): void {
207
+ const input = readToolPayload(message.toolInput) ?? {};
208
+ const result = readObjectRecord(message.toolResult?.toolUseResult);
209
+
210
+ // A listing restates the whole tracker, which is the most reliable source
211
+ // there is — adopt it wholesale. It omits the present-tense wording each
212
+ // task was created with, so that is carried over from what is already known.
213
+ const listed = result?.tasks;
214
+ if (Array.isArray(listed)) {
215
+ const known = new Map(this.entries);
216
+ this.entries.clear();
217
+ for (const entry of listed) {
218
+ const task = readObjectRecord(entry);
219
+ const activeForm = known.get(readNonEmptyString(task?.id))?.activeForm ?? '';
220
+ this.upsertTask(task, { content: '', activeForm });
221
+ }
222
+ return;
223
+ }
224
+
225
+ const single = readObjectRecord(result?.task);
226
+ if (single) {
227
+ this.upsertTask(single, {
228
+ content: readNonEmptyString(input.subject),
229
+ activeForm: readNonEmptyString(input.activeForm),
230
+ });
231
+ return;
232
+ }
233
+
234
+ const id = readNonEmptyString(input.taskId) || readNonEmptyString(message.toolId);
235
+ if (!id) {
236
+ return;
237
+ }
238
+
239
+ const existing = this.entries.get(id);
240
+ this.entries.set(id, {
241
+ id,
242
+ content: readNonEmptyString(input.subject) || existing?.content || id,
243
+ status: input.status === undefined ? existing?.status ?? 'pending' : readTaskStatus(input.status),
244
+ activeForm: readNonEmptyString(input.activeForm) || existing?.activeForm,
245
+ });
246
+ }
247
+
248
+ private upsertTask(task: AnyRecord | null, defaults: { content: string; activeForm: string } = { content: '', activeForm: '' }): void {
249
+ if (!task) {
250
+ return;
251
+ }
252
+
253
+ const id = readNonEmptyString(task.id);
254
+ if (!id) {
255
+ return;
256
+ }
257
+
258
+ const existing = this.entries.get(id);
259
+ this.entries.set(id, {
260
+ id,
261
+ content: readNonEmptyString(task.subject) || defaults.content || existing?.content || id,
262
+ status: task.status === undefined ? existing?.status ?? 'pending' : readTaskStatus(task.status),
263
+ activeForm: defaults.activeForm || existing?.activeForm,
264
+ });
265
+ }
266
+
267
+ snapshot(): ChecklistEntry[] {
268
+ return [...this.entries.values()].map((entry) => ({ ...entry }));
269
+ }
270
+ }
271
+
272
+ /** True for a tool row that already carries a whole checklist in its input. */
273
+ function isChecklistSnapshot(message: NormalizedMessage): boolean {
274
+ return message.kind === 'tool_use' && message.toolName === CHECKLIST_TOOL;
275
+ }
276
+
277
+ /**
278
+ * Identity of a checklist as it is drawn: the steps and their states, and
279
+ * nothing else. Two snapshots with the same signature look identical on screen,
280
+ * so only the first is worth keeping.
281
+ */
282
+ function readChecklistSignature(message: NormalizedMessage): string {
283
+ const todos = readToolPayload(message.toolInput)?.todos;
284
+ if (!Array.isArray(todos)) {
285
+ return '';
286
+ }
287
+
288
+ return JSON.stringify(todos.map((todo) => {
289
+ const entry = readObjectRecord(todo);
290
+ return [readNonEmptyString(entry?.content), readTaskStatus(entry?.status)];
291
+ }));
292
+ }
293
+
294
+ //----------------- TOOL OUTPUT SIZE ------------
295
+
296
+ /**
297
+ * Longest tool output one transcript row carries.
298
+ *
299
+ * A provider records whatever the tool returned, which for a `Read` of a
300
+ * screenshot is a base64 image and for a long test run is the whole log. The
301
+ * transcript shows that output inside a collapsed section, so past roughly a
302
+ * screenful of scrolling nobody reads further — but every byte is parsed,
303
+ * stored and held in memory for the life of the session. Real Claude sessions
304
+ * ship megabytes per page this way.
305
+ */
306
+ const MAX_TOOL_RESULT_CONTENT = 40_000;
307
+
308
+ function truncateOutput(value: string): string {
309
+ if (value.length <= MAX_TOOL_RESULT_CONTENT) {
310
+ return value;
311
+ }
312
+
313
+ const omitted = value.length - MAX_TOOL_RESULT_CONTENT;
314
+ return `${value.slice(0, MAX_TOOL_RESULT_CONTENT)}\n… ${omitted} more characters`;
315
+ }
316
+
317
+ /**
318
+ * Applies the same cap to every string inside a provider's structured result.
319
+ *
320
+ * The structure is preserved rather than dropped wholesale because the
321
+ * transcript reads small fields out of it (a search's file list, for one), and
322
+ * only a single oversized string — a file body or an encoded image — is ever
323
+ * the thing making it large.
324
+ */
325
+ function truncateNestedOutput(value: unknown): unknown {
326
+ if (typeof value === 'string') {
327
+ return truncateOutput(value);
328
+ }
329
+
330
+ if (Array.isArray(value)) {
331
+ return value.map(truncateNestedOutput);
332
+ }
333
+
334
+ const record = readObjectRecord(value);
335
+ if (!record) {
336
+ return value;
337
+ }
338
+
339
+ const truncated: AnyRecord = {};
340
+ for (const [key, entry] of Object.entries(record)) {
341
+ truncated[key] = truncateNestedOutput(entry);
342
+ }
343
+ return truncated;
344
+ }
345
+
346
+ function capToolResult(message: NormalizedMessage): void {
347
+ const result = message.toolResult;
348
+ if (!result) {
349
+ return;
350
+ }
351
+
352
+ if (typeof result.content === 'string') {
353
+ result.content = truncateOutput(result.content);
354
+ }
355
+ if (result.toolUseResult !== undefined) {
356
+ result.toolUseResult = truncateNestedOutput(result.toolUseResult);
357
+ }
358
+ }
359
+
360
+ //----------------- TRANSCRIPT PASS ------------
361
+
362
+ /**
363
+ * Turns one loaded history into exactly the rows the transcript draws.
364
+ *
365
+ * Three things happen here:
366
+ *
367
+ * 1. Provider-specific interaction rows are rewritten onto the canonical
368
+ * shapes described above.
369
+ * 2. Checklist snapshots that a later snapshot supersedes are dropped.
370
+ * Consecutive snapshots are the normal case once incremental task calls are
371
+ * replayed — three `TaskCreate` calls in a row describe one list, not three
372
+ * — and keeping only the last of a run turns a wall of near-identical cards
373
+ * into the single checklist the model was actually building. Snapshots
374
+ * separated by real work are all kept, so the list still reappears wherever
375
+ * it changed.
376
+ * 3. Standalone tool-result rows are dropped, because the provider has already
377
+ * attached each result to the call it belongs to and the chat draws it
378
+ * there. Shipping the row twice is half of everything a tool-heavy Claude
379
+ * session sends.
380
+ * 4. Tool output is capped, so one `Read` of an image cannot cost the session
381
+ * a megabyte of memory for content nothing renders.
382
+ *
383
+ * Mutates and returns the given array; callers own it.
384
+ */
385
+ export function prepareTranscriptMessages(messages: NormalizedMessage[]): NormalizedMessage[] {
386
+ const checklist = new ChecklistState();
387
+
388
+ for (const message of messages) {
389
+ if (message.kind !== 'tool_use' || !message.toolName) {
390
+ continue;
391
+ }
392
+
393
+ if (ASK_TOOL_ALIASES.has(message.toolName)) {
394
+ unifyAskCall(message);
395
+ continue;
396
+ }
397
+
398
+ if (TASK_TOOLS.has(message.toolName)) {
399
+ checklist.applyTaskCall(message);
400
+ message.toolName = CHECKLIST_TOOL;
401
+ message.toolInput = { todos: checklist.snapshot() };
402
+ }
403
+ }
404
+
405
+ // After the rewrites, so a folded-in answer is read from the full result.
406
+ for (const message of messages) {
407
+ capToolResult(message);
408
+ }
409
+
410
+ const superseded = new Set<number>();
411
+ let openSnapshotIndex = -1;
412
+ let keptSignature: string | null = null;
413
+
414
+ messages.forEach((message, index) => {
415
+ // A result row renders as part of its call, so it must not break a run of
416
+ // snapshots apart — the results of the calls being folded away sit exactly
417
+ // between them.
418
+ if (message.kind === 'tool_result') {
419
+ return;
420
+ }
421
+
422
+ if (!isChecklistSnapshot(message)) {
423
+ openSnapshotIndex = -1;
424
+ return;
425
+ }
426
+
427
+ // Restating a checklist that has not moved since it was last drawn adds
428
+ // nothing, however far apart the two calls are.
429
+ const signature = readChecklistSignature(message);
430
+ if (signature === keptSignature) {
431
+ superseded.add(index);
432
+ return;
433
+ }
434
+
435
+ if (openSnapshotIndex !== -1) {
436
+ superseded.add(openSnapshotIndex);
437
+ }
438
+ openSnapshotIndex = index;
439
+ keptSignature = signature;
440
+ });
441
+
442
+ // A result that names its call is only ever drawn through that call. The one
443
+ // shape worth keeping is a result with no call to attach to, which the chat
444
+ // renders on its own.
445
+ return messages.filter((message, index) => (
446
+ !superseded.has(index)
447
+ && !(message.kind === 'tool_result' && message.toolId)
448
+ ));
449
+ }
@@ -47,7 +47,7 @@ test('resolveClaudeCodeExecutablePath can parse a wrapper file path containing l
47
47
  assert.equal(resolved, nativePath);
48
48
  });
49
49
 
50
- test('resolveClaudeCodeExecutablePath falls back to the configured command when PATH lookup fails', () => {
50
+ test('resolveClaudeCodeExecutablePath keeps an explicitly configured command when PATH lookup fails', () => {
51
51
  const execFileSync = (() => {
52
52
  throw new Error('not found');
53
53
  }) as unknown as ResolveClaudeCodeExecutablePathDependencies['execFileSync'];
@@ -59,3 +59,60 @@ test('resolveClaudeCodeExecutablePath falls back to the configured command when
59
59
 
60
60
  assert.equal(resolved, 'claude');
61
61
  });
62
+
63
+ test('resolveClaudeCodeExecutablePath returns undefined on Windows when the default resolves to nothing', () => {
64
+ // The SDK spawns this path directly, so a bare `claude` would fail with
65
+ // "native binary not found at claude"; undefined means "use your own binary".
66
+ const execFileSync = (() => {
67
+ throw new Error('not found');
68
+ }) as unknown as ResolveClaudeCodeExecutablePathDependencies['execFileSync'];
69
+
70
+ const resolved = resolveClaudeCodeExecutablePath(undefined, {
71
+ platform: 'win32',
72
+ execFileSync,
73
+ });
74
+
75
+ assert.equal(resolved, undefined);
76
+ });
77
+
78
+ test('resolveClaudeCodeExecutablePath returns undefined when every wrapper on PATH is a JavaScript launcher', () => {
79
+ // An older global install ships cli.js and no bin/claude.exe, so nothing on
80
+ // PATH maps to a native binary the SDK can spawn.
81
+ const wrapperDir = 'C:\\Users\\dev\\AppData\\Roaming\\npm';
82
+ const execFileSync =
83
+ (() => `${wrapperDir}\\claude\r\n${wrapperDir}\\claude.cmd\r\n`) as unknown as ResolveClaudeCodeExecutablePathDependencies['execFileSync'];
84
+ const readFileSync = (() => 'exec node "$basedir/node_modules/@anthropic-ai/claude-code/cli.js" "$@"') as unknown as ResolveClaudeCodeExecutablePathDependencies['readFileSync'];
85
+
86
+ const resolved = resolveClaudeCodeExecutablePath(undefined, {
87
+ platform: 'win32',
88
+ execFileSync,
89
+ existsSync: () => false,
90
+ readFileSync,
91
+ });
92
+
93
+ assert.equal(resolved, undefined);
94
+ });
95
+
96
+ test('resolveClaudeCodeExecutablePath still resolves the native exe when both installs are on PATH', () => {
97
+ const staleDir = 'C:\\Users\\dev\\AppData\\Roaming\\npm';
98
+ const nativeDir = 'C:\\nvm4w\\nodejs';
99
+ const nativePath = `${nativeDir}\\node_modules\\@anthropic-ai\\claude-code\\bin\\claude.exe`;
100
+ const execFileSync =
101
+ (() => `${staleDir}\\claude\r\n${nativeDir}\\claude\r\n`) as unknown as ResolveClaudeCodeExecutablePathDependencies['execFileSync'];
102
+ const readFileSync = (() => 'exec node "$basedir/node_modules/@anthropic-ai/claude-code/cli.js" "$@"') as unknown as ResolveClaudeCodeExecutablePathDependencies['readFileSync'];
103
+
104
+ const resolved = resolveClaudeCodeExecutablePath(undefined, {
105
+ platform: 'win32',
106
+ execFileSync,
107
+ existsSync: (candidate) => candidate === nativePath,
108
+ readFileSync,
109
+ });
110
+
111
+ assert.equal(resolved, nativePath);
112
+ });
113
+
114
+ test('resolveClaudeCodeExecutablePath leaves non-Windows platforms on the bare command', () => {
115
+ const resolved = resolveClaudeCodeExecutablePath(undefined, { platform: 'linux' });
116
+
117
+ assert.equal(resolved, 'claude');
118
+ });