@cloudcli-ai/cloudcli 1.37.2 → 1.37.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (453) hide show
  1. package/README.md +269 -0
  2. package/dist/assets/{abnfDiagram-N423BO3Z-C4UAoHJb.js → abnfDiagram-N423BO3Z-Dmkwx38H.js} +1 -1
  3. package/dist/assets/{arc-B8jF-ja9.js → arc-DLe9AYuO.js} +1 -1
  4. package/dist/assets/{architectureDiagram-T3A2C74G-C_Wur_CU.js → architectureDiagram-T3A2C74G-CBbbbNUW.js} +1 -1
  5. package/dist/assets/{blockDiagram-VBNYF7ZC-Cq4P1i8t.js → blockDiagram-VBNYF7ZC-CaMEaWu4.js} +1 -1
  6. package/dist/assets/{c4Diagram-5PPSVZJV-C1wG4eUj.js → c4Diagram-5PPSVZJV-DhjMgNnR.js} +1 -1
  7. package/dist/assets/channel-CWrmwsir.js +1 -0
  8. package/dist/assets/{chunk-2GRJ4B5K-B8fnBG6E.js → chunk-2GRJ4B5K-yjl3-F-i.js} +1 -1
  9. package/dist/assets/{chunk-2Q5K7J3B-WdfPooPJ.js → chunk-2Q5K7J3B-DsAYaWEP.js} +1 -1
  10. package/dist/assets/{chunk-5RXB4S5H-DhZJuG2E.js → chunk-5RXB4S5H-Bt5qmqOX.js} +1 -1
  11. package/dist/assets/{chunk-5VM5RSS4-B0rjslal.js → chunk-5VM5RSS4-yWrWVvUt.js} +1 -1
  12. package/dist/assets/{chunk-6Q2QTUOP-CG2Oj4jL.js → chunk-6Q2QTUOP-Dt66K5xb.js} +1 -1
  13. package/dist/assets/{chunk-GF5L2VYU-rrl2lRxY.js → chunk-GF5L2VYU-DA0vzaj4.js} +1 -1
  14. package/dist/assets/{chunk-JWPE2WC7-CjVbrVMA.js → chunk-JWPE2WC7-CUkbx96V.js} +1 -1
  15. package/dist/assets/{chunk-KBJHAD2P-CRzKgU8S.js → chunk-KBJHAD2P-DWKxx1WH.js} +1 -1
  16. package/dist/assets/{chunk-RYQCIY6F-DBz4GTZF.js → chunk-RYQCIY6F-BqluplN0.js} +1 -1
  17. package/dist/assets/{chunk-XXDRQBXY-wHxalsXk.js → chunk-XXDRQBXY-DelKg3Xf.js} +1 -1
  18. package/dist/assets/classDiagram-JCYQIIEL-COAGCC6w.js +1 -0
  19. package/dist/assets/classDiagram-v2-OCEON4UE-COAGCC6w.js +1 -0
  20. package/dist/assets/{cose-bilkent-JH36ORCC-s1hygY8e.js → cose-bilkent-JH36ORCC-DHV93-Em.js} +1 -1
  21. package/dist/assets/{cynefin-VYW2F7L2-C4LGV15i.js → cynefin-VYW2F7L2-B9HZgoGX.js} +1 -1
  22. package/dist/assets/{cynefinDiagram-MW4NZA55-C5uhu7B3.js → cynefinDiagram-MW4NZA55-hFjjEQg8.js} +1 -1
  23. package/dist/assets/{dagre-VZM6K2ZE-B-OBTewm.js → dagre-VZM6K2ZE-CpeVOC7V.js} +1 -1
  24. package/dist/assets/{diagram-7IWD3JNH-BjGKQxih.js → diagram-7IWD3JNH-D0H1UoRA.js} +1 -1
  25. package/dist/assets/{diagram-B4RE2ZJO-BMKz1X2L.js → diagram-B4RE2ZJO-6Y7BQUup.js} +1 -1
  26. package/dist/assets/{diagram-LBJQPF4R-DNh-GhKC.js → diagram-LBJQPF4R-C0nNOwht.js} +1 -1
  27. package/dist/assets/{diagram-Q27KOJAE-BaTG9jPT.js → diagram-Q27KOJAE-En6QlOVX.js} +1 -1
  28. package/dist/assets/{diagram-UB23O5K3-BUwo2OvJ.js → diagram-UB23O5K3-CmPiNeGD.js} +1 -1
  29. package/dist/assets/{ebnfDiagram-BXEA7PRR-DAZgfzX2.js → ebnfDiagram-BXEA7PRR-Bygzp1Qt.js} +1 -1
  30. package/dist/assets/{erDiagram-JOGREHBK-1w1CcGEb.js → erDiagram-JOGREHBK-CwScRfzl.js} +1 -1
  31. package/dist/assets/{flowDiagram-UKHOOZJN-D4EII8Jj.js → flowDiagram-UKHOOZJN-bKX-0xP6.js} +1 -1
  32. package/dist/assets/{ganttDiagram-PKOTCBZU-ChvJZAgB.js → ganttDiagram-PKOTCBZU-DtZPKXGd.js} +1 -1
  33. package/dist/assets/{gitGraphDiagram-DS77QQ5N-DtOn41S8.js → gitGraphDiagram-DS77QQ5N-CUOtdL1o.js} +1 -1
  34. package/dist/assets/index-CdjIFxM2.js +5004 -0
  35. package/dist/assets/index-DgafhGTF.css +32 -0
  36. package/dist/assets/{infoDiagram-6WML65LV-DN_PVEJZ.js → infoDiagram-6WML65LV-ciRQTS0h.js} +1 -1
  37. package/dist/assets/{ishikawaDiagram-WSZJBQD7-DbkF1kXb.js → ishikawaDiagram-WSZJBQD7-CgjhQZ84.js} +1 -1
  38. package/dist/assets/{journeyDiagram-NVQOT4AX-pJwmhddE.js → journeyDiagram-NVQOT4AX-BsbQSeIW.js} +1 -1
  39. package/dist/assets/{kanban-definition-27J2QSJJ-PPAPgNp5.js → kanban-definition-27J2QSJJ-CrwzY9Jw.js} +1 -1
  40. package/dist/assets/{linear-iPR-J9f7.js → linear-BUaCDr6l.js} +1 -1
  41. package/dist/assets/{mermaid.core-BEUL86Z6.js → mermaid.core-ClgdUU0j.js} +5 -5
  42. package/dist/assets/{mindmap-definition-FAOFIHXS-BvZoBib7.js → mindmap-definition-FAOFIHXS-BictljY1.js} +1 -1
  43. package/dist/assets/{pegDiagram-VL7TDLO6-DhYreaUd.js → pegDiagram-VL7TDLO6-BXdByy_U.js} +1 -1
  44. package/dist/assets/{pieDiagram-7S7Q4E2Y-CrOHlyD0.js → pieDiagram-7S7Q4E2Y-uZsMfkRX.js} +1 -1
  45. package/dist/assets/{quadrantDiagram-CIZ2JOQS-D4g5yH3t.js → quadrantDiagram-CIZ2JOQS-7lCv4QIj.js} +1 -1
  46. package/dist/assets/{railroadDiagram-AXF67PYL-Dj39lHlf.js → railroadDiagram-AXF67PYL-07dxIWbi.js} +1 -1
  47. package/dist/assets/{requirementDiagram-LRYGKXZP-1AS4Zgm_.js → requirementDiagram-LRYGKXZP-Cb31Yv1_.js} +1 -1
  48. package/dist/assets/{sankeyDiagram-W5VNT64P-CKlMn19P.js → sankeyDiagram-W5VNT64P-X0YPSyF9.js} +1 -1
  49. package/dist/assets/{sequenceDiagram-SI44F4Z6-B_j3qyvo.js → sequenceDiagram-SI44F4Z6-BdBxSIlP.js} +1 -1
  50. package/dist/assets/server.browser-CtX3Hdl5.js +27 -0
  51. package/dist/assets/{sizeCapture-X5ZJPWSS-Bp2Op16v.js → sizeCapture-X5ZJPWSS-DQM5xAyD.js} +1 -1
  52. package/dist/assets/{stateDiagram-OKZ733FA-C0cN6DoY.js → stateDiagram-OKZ733FA-DJ3xkLUF.js} +1 -1
  53. package/dist/assets/stateDiagram-v2-UEYNNEHI-vAaE4zNo.js +1 -0
  54. package/dist/assets/{swimlanes-SLNWSIFB-BEvXOV9b.js → swimlanes-SLNWSIFB-rjfYdW0w.js} +2 -2
  55. package/dist/assets/swimlanesDiagram-ULZ7WXOC-TV2IF4Nq.js +8 -0
  56. package/dist/assets/{timeline-definition-Z64GVDOM-zdJQinvZ.js → timeline-definition-Z64GVDOM-DZMra5do.js} +1 -1
  57. package/dist/assets/{vendor-codemirror-OwyKSvPE.js → vendor-codemirror-CYrSt7-U.js} +18 -18
  58. package/dist/assets/{vennDiagram-T6HMQDX7-DuF1uZMY.js → vennDiagram-T6HMQDX7-BevD8uu0.js} +1 -1
  59. package/dist/assets/{wardleyDiagram-T6FBY63Y-B9AlLQup.js → wardleyDiagram-T6FBY63Y-DsIyXB7z.js} +1 -1
  60. package/dist/assets/{xychartDiagram-ELKLHX3M-DTJX48Qg.js → xychartDiagram-ELKLHX3M-G3PlF3ko.js} +1 -1
  61. package/dist/index.html +3 -3
  62. package/dist-server/server/index.js +11 -4
  63. package/dist-server/server/index.js.map +1 -1
  64. package/dist-server/server/modules/agent/agent.module.js +9 -2
  65. package/dist-server/server/modules/agent/agent.module.js.map +1 -1
  66. package/dist-server/server/modules/agent/agent.routes.js +177 -130
  67. package/dist-server/server/modules/agent/agent.routes.js.map +1 -1
  68. package/dist-server/server/modules/agent/tests/agent-run-registration.test.js +306 -0
  69. package/dist-server/server/modules/agent/tests/agent-run-registration.test.js.map +1 -0
  70. package/dist-server/server/modules/agent/tests/agent.routes.test.js +48 -2
  71. package/dist-server/server/modules/agent/tests/agent.routes.test.js.map +1 -1
  72. package/dist-server/server/modules/browser-use/browser-use.routes.js +39 -33
  73. package/dist-server/server/modules/browser-use/browser-use.routes.js.map +1 -1
  74. package/dist-server/server/modules/database/connection.js +1 -1
  75. package/dist-server/server/modules/database/connection.js.map +1 -1
  76. package/dist-server/server/modules/database/index.js +5 -0
  77. package/dist-server/server/modules/database/index.js.map +1 -1
  78. package/dist-server/server/modules/database/migrations.js +45 -1
  79. package/dist-server/server/modules/database/migrations.js.map +1 -1
  80. package/dist-server/server/modules/database/repositories/provider-models.js +60 -6
  81. package/dist-server/server/modules/database/repositories/provider-models.js.map +1 -1
  82. package/dist-server/server/modules/database/repositories/scheduled-messages.db.js +71 -0
  83. package/dist-server/server/modules/database/repositories/scheduled-messages.db.js.map +1 -0
  84. package/dist-server/server/modules/database/repositories/session-drafts.db.js +105 -0
  85. package/dist-server/server/modules/database/repositories/session-drafts.db.js.map +1 -0
  86. package/dist-server/server/modules/database/repositories/sessions.db.js +144 -5
  87. package/dist-server/server/modules/database/repositories/sessions.db.js.map +1 -1
  88. package/dist-server/server/modules/database/repositories/user-preferences.db.js +65 -0
  89. package/dist-server/server/modules/database/repositories/user-preferences.db.js.map +1 -0
  90. package/dist-server/server/modules/database/schema.js +99 -0
  91. package/dist-server/server/modules/database/schema.js.map +1 -1
  92. package/dist-server/server/modules/database/tests/provider-models.db.integration.test.js +99 -0
  93. package/dist-server/server/modules/database/tests/provider-models.db.integration.test.js.map +1 -1
  94. package/dist-server/server/modules/database/tests/sessions.db.integration.test.js +39 -0
  95. package/dist-server/server/modules/database/tests/sessions.db.integration.test.js.map +1 -1
  96. package/dist-server/server/modules/database/tests/user-preferences.db.integration.test.js +105 -0
  97. package/dist-server/server/modules/database/tests/user-preferences.db.integration.test.js.map +1 -0
  98. package/dist-server/server/modules/file-tree/file-tree.module.js +2 -1
  99. package/dist-server/server/modules/file-tree/file-tree.module.js.map +1 -1
  100. package/dist-server/server/modules/file-tree/file-tree.service.js +44 -7
  101. package/dist-server/server/modules/file-tree/file-tree.service.js.map +1 -1
  102. package/dist-server/server/modules/file-tree/tests/file-tree.service.test.js +126 -1
  103. package/dist-server/server/modules/file-tree/tests/file-tree.service.test.js.map +1 -1
  104. package/dist-server/server/modules/git/git-branch-diff.service.js +142 -0
  105. package/dist-server/server/modules/git/git-branch-diff.service.js.map +1 -0
  106. package/dist-server/server/modules/git/git-branch.service.js.map +1 -1
  107. package/dist-server/server/modules/git/git.routes.js +92 -5
  108. package/dist-server/server/modules/git/git.routes.js.map +1 -1
  109. package/dist-server/server/modules/git/tests/git-branch-diff.service.test.js +160 -0
  110. package/dist-server/server/modules/git/tests/git-branch-diff.service.test.js.map +1 -0
  111. package/dist-server/server/modules/projects/project-clone.routes.js +108 -0
  112. package/dist-server/server/modules/projects/project-clone.routes.js.map +1 -0
  113. package/dist-server/server/modules/projects/projects.routes.js +13 -66
  114. package/dist-server/server/modules/projects/projects.routes.js.map +1 -1
  115. package/dist-server/server/modules/projects/services/project-clone-request.service.js +36 -0
  116. package/dist-server/server/modules/projects/services/project-clone-request.service.js.map +1 -0
  117. package/dist-server/server/modules/projects/services/project-clone.service.js +86 -32
  118. package/dist-server/server/modules/projects/services/project-clone.service.js.map +1 -1
  119. package/dist-server/server/modules/projects/services/project-delete.service.js +12 -1
  120. package/dist-server/server/modules/projects/services/project-delete.service.js.map +1 -1
  121. package/dist-server/server/modules/projects/services/project-star.service.js +2 -2
  122. package/dist-server/server/modules/projects/services/project-star.service.js.map +1 -1
  123. package/dist-server/server/modules/projects/tests/project-clone.routes.test.js +191 -0
  124. package/dist-server/server/modules/projects/tests/project-clone.routes.test.js.map +1 -0
  125. package/dist-server/server/modules/projects/tests/project-clone.service.test.js +275 -0
  126. package/dist-server/server/modules/projects/tests/project-clone.service.test.js.map +1 -1
  127. package/dist-server/server/modules/providers/index.js +3 -0
  128. package/dist-server/server/modules/providers/index.js.map +1 -1
  129. package/dist-server/server/modules/providers/list/claude/claude-auth.provider.js +62 -9
  130. package/dist-server/server/modules/providers/list/claude/claude-auth.provider.js.map +1 -1
  131. package/dist-server/server/modules/providers/list/claude/claude-fork.provider.js +46 -0
  132. package/dist-server/server/modules/providers/list/claude/claude-fork.provider.js.map +1 -0
  133. package/dist-server/server/modules/providers/list/claude/claude-models.provider.js +31 -12
  134. package/dist-server/server/modules/providers/list/claude/claude-models.provider.js.map +1 -1
  135. package/dist-server/server/modules/providers/list/claude/claude-runtime.provider.js +472 -53
  136. package/dist-server/server/modules/providers/list/claude/claude-runtime.provider.js.map +1 -1
  137. package/dist-server/server/modules/providers/list/claude/claude-session-synchronizer.provider.js +38 -11
  138. package/dist-server/server/modules/providers/list/claude/claude-session-synchronizer.provider.js.map +1 -1
  139. package/dist-server/server/modules/providers/list/claude/claude-sessions.provider.js +1007 -60
  140. package/dist-server/server/modules/providers/list/claude/claude-sessions.provider.js.map +1 -1
  141. package/dist-server/server/modules/providers/list/claude/claude-skills.provider.js +4 -1
  142. package/dist-server/server/modules/providers/list/claude/claude-skills.provider.js.map +1 -1
  143. package/dist-server/server/modules/providers/list/claude/claude.provider.js +2 -0
  144. package/dist-server/server/modules/providers/list/claude/claude.provider.js.map +1 -1
  145. package/dist-server/server/modules/providers/list/codex/codex-app-server.client.js +204 -0
  146. package/dist-server/server/modules/providers/list/codex/codex-app-server.client.js.map +1 -0
  147. package/dist-server/server/modules/providers/list/codex/codex-fork.provider.js +32 -0
  148. package/dist-server/server/modules/providers/list/codex/codex-fork.provider.js.map +1 -0
  149. package/dist-server/server/modules/providers/list/codex/codex-models.provider.js +47 -0
  150. package/dist-server/server/modules/providers/list/codex/codex-models.provider.js.map +1 -1
  151. package/dist-server/server/modules/providers/list/codex/codex-runtime.provider.js +71 -53
  152. package/dist-server/server/modules/providers/list/codex/codex-runtime.provider.js.map +1 -1
  153. package/dist-server/server/modules/providers/list/codex/codex-session-synchronizer.provider.js +8 -0
  154. package/dist-server/server/modules/providers/list/codex/codex-session-synchronizer.provider.js.map +1 -1
  155. package/dist-server/server/modules/providers/list/codex/codex-sessions.provider.js +1621 -431
  156. package/dist-server/server/modules/providers/list/codex/codex-sessions.provider.js.map +1 -1
  157. package/dist-server/server/modules/providers/list/codex/codex.provider.js +2 -0
  158. package/dist-server/server/modules/providers/list/codex/codex.provider.js.map +1 -1
  159. package/dist-server/server/modules/providers/list/cursor/cursor-auth.provider.js +9 -2
  160. package/dist-server/server/modules/providers/list/cursor/cursor-auth.provider.js.map +1 -1
  161. package/dist-server/server/modules/providers/list/cursor/cursor-runtime.provider.js +16 -8
  162. package/dist-server/server/modules/providers/list/cursor/cursor-runtime.provider.js.map +1 -1
  163. package/dist-server/server/modules/providers/list/cursor/cursor-session-synchronizer.provider.js.map +1 -1
  164. package/dist-server/server/modules/providers/list/opencode/opencode-models.provider.js +274 -9
  165. package/dist-server/server/modules/providers/list/opencode/opencode-models.provider.js.map +1 -1
  166. package/dist-server/server/modules/providers/list/opencode/opencode-runtime.provider.js +5 -2
  167. package/dist-server/server/modules/providers/list/opencode/opencode-runtime.provider.js.map +1 -1
  168. package/dist-server/server/modules/providers/list/opencode/opencode-session-synchronizer.provider.js +19 -2
  169. package/dist-server/server/modules/providers/list/opencode/opencode-session-synchronizer.provider.js.map +1 -1
  170. package/dist-server/server/modules/providers/provider.routes.js +134 -2
  171. package/dist-server/server/modules/providers/provider.routes.js.map +1 -1
  172. package/dist-server/server/modules/providers/services/claude-cli-liveness.service.js +100 -0
  173. package/dist-server/server/modules/providers/services/claude-cli-liveness.service.js.map +1 -0
  174. package/dist-server/server/modules/providers/services/claude-settings-permissions.service.js +97 -0
  175. package/dist-server/server/modules/providers/services/claude-settings-permissions.service.js.map +1 -0
  176. package/dist-server/server/modules/providers/services/provider-capabilities.service.js +23 -1
  177. package/dist-server/server/modules/providers/services/provider-capabilities.service.js.map +1 -1
  178. package/dist-server/server/modules/providers/services/provider-models.service.js +59 -5
  179. package/dist-server/server/modules/providers/services/provider-models.service.js.map +1 -1
  180. package/dist-server/server/modules/providers/services/provider-runtime.service.js +8 -0
  181. package/dist-server/server/modules/providers/services/provider-runtime.service.js.map +1 -1
  182. package/dist-server/server/modules/providers/services/provider-token-usage.service.js +139 -35
  183. package/dist-server/server/modules/providers/services/provider-token-usage.service.js.map +1 -1
  184. package/dist-server/server/modules/providers/services/session-conversations-search.service.js +2 -4
  185. package/dist-server/server/modules/providers/services/session-conversations-search.service.js.map +1 -1
  186. package/dist-server/server/modules/providers/services/session-history-cache.service.js +91 -0
  187. package/dist-server/server/modules/providers/services/session-history-cache.service.js.map +1 -0
  188. package/dist-server/server/modules/providers/services/session-synchronizer.service.js +105 -32
  189. package/dist-server/server/modules/providers/services/session-synchronizer.service.js.map +1 -1
  190. package/dist-server/server/modules/providers/services/sessions-watcher.service.js +3 -60
  191. package/dist-server/server/modules/providers/services/sessions-watcher.service.js.map +1 -1
  192. package/dist-server/server/modules/providers/services/sessions.service.js +252 -12
  193. package/dist-server/server/modules/providers/services/sessions.service.js.map +1 -1
  194. package/dist-server/server/modules/providers/tests/claude-auth.test.js +183 -0
  195. package/dist-server/server/modules/providers/tests/claude-auth.test.js.map +1 -1
  196. package/dist-server/server/modules/providers/tests/claude-background-work.test.js +172 -0
  197. package/dist-server/server/modules/providers/tests/claude-background-work.test.js.map +1 -0
  198. package/dist-server/server/modules/providers/tests/claude-compaction.test.js +84 -0
  199. package/dist-server/server/modules/providers/tests/claude-compaction.test.js.map +1 -0
  200. package/dist-server/server/modules/providers/tests/claude-message-model.test.js +123 -0
  201. package/dist-server/server/modules/providers/tests/claude-message-model.test.js.map +1 -0
  202. package/dist-server/server/modules/providers/tests/claude-models.test.js +39 -0
  203. package/dist-server/server/modules/providers/tests/claude-models.test.js.map +1 -0
  204. package/dist-server/server/modules/providers/tests/claude-runtime-hold.test.js +265 -0
  205. package/dist-server/server/modules/providers/tests/claude-runtime-hold.test.js.map +1 -0
  206. package/dist-server/server/modules/providers/tests/claude-sessions.test.js +1815 -33
  207. package/dist-server/server/modules/providers/tests/claude-sessions.test.js.map +1 -1
  208. package/dist-server/server/modules/providers/tests/claude-settings-permissions.test.js +228 -0
  209. package/dist-server/server/modules/providers/tests/claude-settings-permissions.test.js.map +1 -0
  210. package/dist-server/server/modules/providers/tests/claude-subagent-echo.test.js +16 -0
  211. package/dist-server/server/modules/providers/tests/claude-subagent-echo.test.js.map +1 -0
  212. package/dist-server/server/modules/providers/tests/claude-token-budget.test.js +85 -0
  213. package/dist-server/server/modules/providers/tests/claude-token-budget.test.js.map +1 -0
  214. package/dist-server/server/modules/providers/tests/codex-message-editing.test.js +338 -0
  215. package/dist-server/server/modules/providers/tests/codex-message-editing.test.js.map +1 -0
  216. package/dist-server/server/modules/providers/tests/codex-models.test.js +25 -0
  217. package/dist-server/server/modules/providers/tests/codex-models.test.js.map +1 -0
  218. package/dist-server/server/modules/providers/tests/codex-runtime.test.js +83 -0
  219. package/dist-server/server/modules/providers/tests/codex-runtime.test.js.map +1 -0
  220. package/dist-server/server/modules/providers/tests/codex-session-fork.test.js +54 -0
  221. package/dist-server/server/modules/providers/tests/codex-session-fork.test.js.map +1 -0
  222. package/dist-server/server/modules/providers/tests/codex-sessions.test.js +354 -15
  223. package/dist-server/server/modules/providers/tests/codex-sessions.test.js.map +1 -1
  224. package/dist-server/server/modules/providers/tests/cursor-auth.test.js +167 -0
  225. package/dist-server/server/modules/providers/tests/cursor-auth.test.js.map +1 -0
  226. package/dist-server/server/modules/providers/tests/opencode-models.test.js +196 -4
  227. package/dist-server/server/modules/providers/tests/opencode-models.test.js.map +1 -1
  228. package/dist-server/server/modules/providers/tests/opencode-sessions.test.js +19 -1
  229. package/dist-server/server/modules/providers/tests/opencode-sessions.test.js.map +1 -1
  230. package/dist-server/server/modules/providers/tests/provider-capabilities.service.test.js +30 -0
  231. package/dist-server/server/modules/providers/tests/provider-capabilities.service.test.js.map +1 -0
  232. package/dist-server/server/modules/providers/tests/provider-models.service.test.js +79 -1
  233. package/dist-server/server/modules/providers/tests/provider-models.service.test.js.map +1 -1
  234. package/dist-server/server/modules/providers/tests/provider-token-usage.service.test.js +164 -1
  235. package/dist-server/server/modules/providers/tests/provider-token-usage.service.test.js.map +1 -1
  236. package/dist-server/server/modules/providers/tests/provider.routes.test.js +190 -1
  237. package/dist-server/server/modules/providers/tests/provider.routes.test.js.map +1 -1
  238. package/dist-server/server/modules/providers/tests/session-fork.test.js +113 -0
  239. package/dist-server/server/modules/providers/tests/session-fork.test.js.map +1 -0
  240. package/dist-server/server/modules/providers/tests/session-history-cache.service.test.js +139 -0
  241. package/dist-server/server/modules/providers/tests/session-history-cache.service.test.js.map +1 -0
  242. package/dist-server/server/modules/providers/tests/session-orphan-prune.test.js +90 -0
  243. package/dist-server/server/modules/providers/tests/session-orphan-prune.test.js.map +1 -0
  244. package/dist-server/server/modules/providers/tests/session-synchronizer-coalescing.test.js +122 -0
  245. package/dist-server/server/modules/providers/tests/session-synchronizer-coalescing.test.js.map +1 -0
  246. package/dist-server/server/modules/providers/tests/sessions.service.test.js +219 -1
  247. package/dist-server/server/modules/providers/tests/sessions.service.test.js.map +1 -1
  248. package/dist-server/server/modules/providers/tests/skills.test.js +15 -3
  249. package/dist-server/server/modules/providers/tests/skills.test.js.map +1 -1
  250. package/dist-server/server/modules/scheduled-messages/index.js +5 -0
  251. package/dist-server/server/modules/scheduled-messages/index.js.map +1 -0
  252. package/dist-server/server/modules/scheduled-messages/scheduled-messages.routes.js +47 -0
  253. package/dist-server/server/modules/scheduled-messages/scheduled-messages.routes.js.map +1 -0
  254. package/dist-server/server/modules/scheduled-messages/services/scheduled-message-dispatcher.service.js +175 -0
  255. package/dist-server/server/modules/scheduled-messages/services/scheduled-message-dispatcher.service.js.map +1 -0
  256. package/dist-server/server/modules/scheduled-messages/services/scheduled-messages.service.js +95 -0
  257. package/dist-server/server/modules/scheduled-messages/services/scheduled-messages.service.js.map +1 -0
  258. package/dist-server/server/modules/scheduled-messages/tests/scheduled-messages.test.js +290 -0
  259. package/dist-server/server/modules/scheduled-messages/tests/scheduled-messages.test.js.map +1 -0
  260. package/dist-server/server/modules/taskmaster/taskmaster.routes.js +23 -23
  261. package/dist-server/server/modules/taskmaster/taskmaster.routes.js.map +1 -1
  262. package/dist-server/server/modules/taskmaster/tests/taskmaster.routes.test.js +48 -0
  263. package/dist-server/server/modules/taskmaster/tests/taskmaster.routes.test.js.map +1 -1
  264. package/dist-server/server/modules/user/tests/user.service.test.js +70 -0
  265. package/dist-server/server/modules/user/tests/user.service.test.js.map +1 -1
  266. package/dist-server/server/modules/user/user.module.js +10 -1
  267. package/dist-server/server/modules/user/user.module.js.map +1 -1
  268. package/dist-server/server/modules/user/user.routes.js +44 -0
  269. package/dist-server/server/modules/user/user.routes.js.map +1 -1
  270. package/dist-server/server/modules/user/user.service.js +75 -0
  271. package/dist-server/server/modules/user/user.service.js.map +1 -1
  272. package/dist-server/server/modules/voice/tests/voice.service.test.js +3 -2
  273. package/dist-server/server/modules/voice/tests/voice.service.test.js.map +1 -1
  274. package/dist-server/server/modules/websocket/index.js +7 -0
  275. package/dist-server/server/modules/websocket/index.js.map +1 -1
  276. package/dist-server/server/modules/websocket/services/chat-run-registry.service.js +37 -49
  277. package/dist-server/server/modules/websocket/services/chat-run-registry.service.js.map +1 -1
  278. package/dist-server/server/modules/websocket/services/chat-session-writer.service.js +35 -5
  279. package/dist-server/server/modules/websocket/services/chat-session-writer.service.js.map +1 -1
  280. package/dist-server/server/modules/websocket/services/chat-websocket.service.js +204 -10
  281. package/dist-server/server/modules/websocket/services/chat-websocket.service.js.map +1 -1
  282. package/dist-server/server/modules/websocket/services/session-upsert-broadcast.service.js +94 -0
  283. package/dist-server/server/modules/websocket/services/session-upsert-broadcast.service.js.map +1 -0
  284. package/dist-server/server/modules/websocket/services/shell-websocket.service.js +236 -20
  285. package/dist-server/server/modules/websocket/services/shell-websocket.service.js.map +1 -1
  286. package/dist-server/server/modules/websocket/tests/chat-background-subscribe.test.js +137 -0
  287. package/dist-server/server/modules/websocket/tests/chat-background-subscribe.test.js.map +1 -0
  288. package/dist-server/server/modules/websocket/tests/chat-edit-send.test.js +263 -0
  289. package/dist-server/server/modules/websocket/tests/chat-edit-send.test.js.map +1 -0
  290. package/dist-server/server/modules/websocket/tests/chat-run-registry.test.js +29 -3
  291. package/dist-server/server/modules/websocket/tests/chat-run-registry.test.js.map +1 -1
  292. package/dist-server/server/modules/websocket/tests/chat-stop-task.test.js +96 -0
  293. package/dist-server/server/modules/websocket/tests/chat-stop-task.test.js.map +1 -0
  294. package/dist-server/server/modules/websocket/tests/session-upsert-broadcast.test.js +106 -0
  295. package/dist-server/server/modules/websocket/tests/session-upsert-broadcast.test.js.map +1 -0
  296. package/dist-server/server/modules/websocket/tests/shell-websocket.service.test.js +500 -0
  297. package/dist-server/server/modules/websocket/tests/shell-websocket.service.test.js.map +1 -1
  298. package/dist-server/server/shared/claude-cli-path.js +22 -5
  299. package/dist-server/server/shared/claude-cli-path.js.map +1 -1
  300. package/dist-server/server/shared/index.js +3 -0
  301. package/dist-server/server/shared/index.js.map +1 -0
  302. package/dist-server/server/shared/message-unification.js +376 -0
  303. package/dist-server/server/shared/message-unification.js.map +1 -0
  304. package/dist-server/server/shared/tests/claude-cli-path.test.js +45 -1
  305. package/dist-server/server/shared/tests/claude-cli-path.test.js.map +1 -1
  306. package/dist-server/server/shared/tests/message-unification.test.js +174 -0
  307. package/dist-server/server/shared/tests/message-unification.test.js.map +1 -0
  308. package/dist-server/server/shared/tests/read-only-roots.test.js +124 -0
  309. package/dist-server/server/shared/tests/read-only-roots.test.js.map +1 -0
  310. package/dist-server/server/shared/tests/strip-ansi-sequences.test.js +31 -0
  311. package/dist-server/server/shared/tests/strip-ansi-sequences.test.js.map +1 -0
  312. package/dist-server/server/shared/utils.js +160 -0
  313. package/dist-server/server/shared/utils.js.map +1 -1
  314. package/electron/desktopWindow.js +6 -0
  315. package/electron/viewHost.js +25 -2
  316. package/package.json +20 -17
  317. package/server/index.ts +16 -5
  318. package/server/modules/agent/agent.module.ts +11 -1
  319. package/server/modules/agent/agent.routes.ts +200 -142
  320. package/server/modules/agent/tests/agent-run-registration.test.ts +348 -0
  321. package/server/modules/agent/tests/agent.routes.test.ts +51 -2
  322. package/server/modules/browser-use/browser-use.routes.ts +96 -33
  323. package/server/modules/database/connection.ts +2 -1
  324. package/server/modules/database/index.ts +10 -0
  325. package/server/modules/database/migrations.ts +51 -0
  326. package/server/modules/database/repositories/provider-models.ts +79 -6
  327. package/server/modules/database/repositories/scheduled-messages.db.ts +127 -0
  328. package/server/modules/database/repositories/session-drafts.db.ts +171 -0
  329. package/server/modules/database/repositories/sessions.db.ts +204 -3
  330. package/server/modules/database/repositories/user-preferences.db.ts +80 -0
  331. package/server/modules/database/schema.ts +103 -0
  332. package/server/modules/database/tests/provider-models.db.integration.test.ts +113 -0
  333. package/server/modules/database/tests/sessions.db.integration.test.ts +52 -0
  334. package/server/modules/database/tests/user-preferences.db.integration.test.ts +135 -0
  335. package/server/modules/file-tree/file-tree.module.ts +2 -1
  336. package/server/modules/file-tree/file-tree.service.ts +49 -7
  337. package/server/modules/file-tree/tests/file-tree.service.test.ts +163 -1
  338. package/server/modules/git/git-branch-diff.service.ts +204 -0
  339. package/server/modules/git/git-branch.service.ts +2 -12
  340. package/server/modules/git/git.routes.ts +100 -6
  341. package/server/modules/git/tests/git-branch-diff.service.test.ts +210 -0
  342. package/server/modules/projects/project-clone.routes.ts +142 -0
  343. package/server/modules/projects/projects.routes.ts +16 -81
  344. package/server/modules/projects/services/project-clone-request.service.ts +56 -0
  345. package/server/modules/projects/services/project-clone.service.ts +102 -34
  346. package/server/modules/projects/services/project-delete.service.ts +13 -1
  347. package/server/modules/projects/services/project-star.service.ts +2 -2
  348. package/server/modules/projects/tests/project-clone.routes.test.ts +246 -0
  349. package/server/modules/projects/tests/project-clone.service.test.ts +376 -0
  350. package/server/modules/providers/index.ts +4 -0
  351. package/server/modules/providers/list/claude/claude-auth.provider.ts +81 -11
  352. package/server/modules/providers/list/claude/claude-fork.provider.ts +57 -0
  353. package/server/modules/providers/list/claude/claude-models.provider.ts +37 -17
  354. package/server/modules/providers/list/claude/claude-runtime.provider.js +510 -54
  355. package/server/modules/providers/list/claude/claude-session-synchronizer.provider.ts +40 -14
  356. package/server/modules/providers/list/claude/claude-sessions.provider.ts +1195 -63
  357. package/server/modules/providers/list/claude/claude-skills.provider.ts +4 -1
  358. package/server/modules/providers/list/claude/claude.provider.ts +3 -0
  359. package/server/modules/providers/list/codex/codex-app-server.client.ts +243 -0
  360. package/server/modules/providers/list/codex/codex-fork.provider.ts +40 -0
  361. package/server/modules/providers/list/codex/codex-models.provider.ts +47 -0
  362. package/server/modules/providers/list/codex/{codex-runtime.provider.js → codex-runtime.provider.ts} +105 -70
  363. package/server/modules/providers/list/codex/codex-session-synchronizer.provider.ts +9 -0
  364. package/server/modules/providers/list/codex/codex-sessions.provider.ts +1847 -455
  365. package/server/modules/providers/list/codex/codex.provider.ts +3 -0
  366. package/server/modules/providers/list/cursor/cursor-auth.provider.ts +10 -2
  367. package/server/modules/providers/list/cursor/cursor-runtime.provider.js +17 -8
  368. package/server/modules/providers/list/cursor/cursor-session-synchronizer.provider.ts +0 -1
  369. package/server/modules/providers/list/opencode/opencode-models.provider.ts +297 -9
  370. package/server/modules/providers/list/opencode/opencode-runtime.provider.js +5 -2
  371. package/server/modules/providers/list/opencode/opencode-session-synchronizer.provider.ts +30 -2
  372. package/server/modules/providers/provider.routes.ts +162 -2
  373. package/server/modules/providers/services/claude-cli-liveness.service.ts +121 -0
  374. package/server/modules/providers/services/claude-settings-permissions.service.ts +127 -0
  375. package/server/modules/providers/services/provider-capabilities.service.ts +40 -1
  376. package/server/modules/providers/services/provider-models.service.ts +71 -4
  377. package/server/modules/providers/services/provider-runtime.service.ts +11 -0
  378. package/server/modules/providers/services/provider-token-usage.service.ts +166 -39
  379. package/server/modules/providers/services/session-conversations-search.service.ts +2 -5
  380. package/server/modules/providers/services/session-history-cache.service.ts +150 -0
  381. package/server/modules/providers/services/session-synchronizer.service.ts +125 -38
  382. package/server/modules/providers/services/sessions-watcher.service.ts +3 -64
  383. package/server/modules/providers/services/sessions.service.ts +309 -17
  384. package/server/modules/providers/tests/claude-auth.test.ts +208 -0
  385. package/server/modules/providers/tests/claude-background-work.test.ts +213 -0
  386. package/server/modules/providers/tests/claude-compaction.test.ts +101 -0
  387. package/server/modules/providers/tests/claude-message-model.test.ts +160 -0
  388. package/server/modules/providers/tests/claude-models.test.ts +87 -0
  389. package/server/modules/providers/tests/claude-runtime-hold.test.ts +309 -0
  390. package/server/modules/providers/tests/claude-sessions.test.ts +2167 -37
  391. package/server/modules/providers/tests/claude-settings-permissions.test.ts +271 -0
  392. package/server/modules/providers/tests/claude-subagent-echo.test.ts +25 -0
  393. package/server/modules/providers/tests/claude-token-budget.test.ts +104 -0
  394. package/server/modules/providers/tests/codex-message-editing.test.ts +419 -0
  395. package/server/modules/providers/tests/codex-models.test.ts +37 -0
  396. package/server/modules/providers/tests/codex-runtime.test.ts +94 -0
  397. package/server/modules/providers/tests/codex-session-fork.test.ts +59 -0
  398. package/server/modules/providers/tests/codex-sessions.test.ts +396 -15
  399. package/server/modules/providers/tests/cursor-auth.test.ts +187 -0
  400. package/server/modules/providers/tests/opencode-models.test.ts +266 -8
  401. package/server/modules/providers/tests/opencode-sessions.test.ts +40 -1
  402. package/server/modules/providers/tests/provider-capabilities.service.test.ts +37 -0
  403. package/server/modules/providers/tests/provider-models.service.test.ts +99 -1
  404. package/server/modules/providers/tests/provider-token-usage.service.test.ts +188 -1
  405. package/server/modules/providers/tests/provider.routes.test.ts +230 -1
  406. package/server/modules/providers/tests/session-fork.test.ts +173 -0
  407. package/server/modules/providers/tests/session-history-cache.service.test.ts +165 -0
  408. package/server/modules/providers/tests/session-orphan-prune.test.ts +107 -0
  409. package/server/modules/providers/tests/session-synchronizer-coalescing.test.ts +136 -0
  410. package/server/modules/providers/tests/sessions.service.test.ts +303 -1
  411. package/server/modules/providers/tests/skills.test.ts +21 -5
  412. package/server/modules/scheduled-messages/index.ts +8 -0
  413. package/server/modules/scheduled-messages/scheduled-messages.routes.ts +68 -0
  414. package/server/modules/scheduled-messages/services/scheduled-message-dispatcher.service.ts +216 -0
  415. package/server/modules/scheduled-messages/services/scheduled-messages.service.ts +121 -0
  416. package/server/modules/scheduled-messages/tests/scheduled-messages.test.ts +354 -0
  417. package/server/modules/taskmaster/taskmaster.routes.ts +23 -33
  418. package/server/modules/taskmaster/tests/taskmaster.routes.test.ts +57 -0
  419. package/server/modules/user/tests/user.service.test.ts +90 -0
  420. package/server/modules/user/user.module.ts +10 -1
  421. package/server/modules/user/user.routes.ts +44 -0
  422. package/server/modules/user/user.service.ts +105 -0
  423. package/server/modules/voice/tests/voice.service.test.ts +3 -2
  424. package/server/modules/websocket/README.md +4 -2
  425. package/server/modules/websocket/index.ts +8 -0
  426. package/server/modules/websocket/services/chat-run-registry.service.ts +45 -54
  427. package/server/modules/websocket/services/chat-session-writer.service.ts +36 -6
  428. package/server/modules/websocket/services/chat-websocket.service.ts +302 -16
  429. package/server/modules/websocket/services/session-upsert-broadcast.service.ts +108 -0
  430. package/server/modules/websocket/services/shell-websocket.service.ts +276 -22
  431. package/server/modules/websocket/tests/chat-background-subscribe.test.ts +167 -0
  432. package/server/modules/websocket/tests/chat-edit-send.test.ts +309 -0
  433. package/server/modules/websocket/tests/chat-run-registry.test.ts +34 -3
  434. package/server/modules/websocket/tests/chat-stop-task.test.ts +127 -0
  435. package/server/modules/websocket/tests/session-upsert-broadcast.test.ts +138 -0
  436. package/server/modules/websocket/tests/shell-websocket.service.test.ts +629 -0
  437. package/server/shared/claude-cli-path.ts +24 -6
  438. package/server/shared/index.ts +3 -0
  439. package/server/shared/interfaces.ts +91 -0
  440. package/server/shared/message-unification.ts +449 -0
  441. package/server/shared/tests/claude-cli-path.test.ts +58 -1
  442. package/server/shared/tests/message-unification.test.ts +218 -0
  443. package/server/shared/tests/read-only-roots.test.ts +139 -0
  444. package/server/shared/tests/strip-ansi-sequences.test.ts +42 -0
  445. package/server/shared/types.ts +382 -7
  446. package/server/shared/utils.ts +176 -0
  447. package/dist/assets/channel-_falYbU1.js +0 -1
  448. package/dist/assets/classDiagram-JCYQIIEL-Dn-3CfpR.js +0 -1
  449. package/dist/assets/classDiagram-v2-OCEON4UE-Dn-3CfpR.js +0 -1
  450. package/dist/assets/index-CzPLBvXT.css +0 -32
  451. package/dist/assets/index-DFkBBuSA.js +0 -1549
  452. package/dist/assets/stateDiagram-v2-UEYNNEHI-BvBxUUrZ.js +0 -1
  453. package/dist/assets/swimlanesDiagram-ULZ7WXOC-CsE-x4YK.js +0 -8
@@ -6,7 +6,7 @@ import test from 'node:test';
6
6
 
7
7
  import { closeConnection, initializeDatabase, sessionsDb } from '@/modules/database/index.js';
8
8
  import { CodexSessionSynchronizer } from '@/modules/providers/list/codex/codex-session-synchronizer.provider.js';
9
- import { CodexSessionsProvider } from '@/modules/providers/list/codex/codex-sessions.provider.js';
9
+ import { CodexSessionsProvider, parseCodexExecScript, readCodexMemoryCitations } from '@/modules/providers/list/codex/codex-sessions.provider.js';
10
10
 
11
11
  const patchHomeDir = (nextHomeDir: string) => {
12
12
  const original = os.homedir;
@@ -152,7 +152,7 @@ test('Codex synchronizer leaves indexed sessions untitled when no name is availa
152
152
  }
153
153
  });
154
154
 
155
- test('Codex history preserves wrapped exec tool calls and results', { concurrency: false }, async () => {
155
+ test('Codex history translates wrapped exec scripts into the tools they ran', { concurrency: false }, async () => {
156
156
  const tempRoot = await mkdtemp(path.join(os.tmpdir(), 'codex-exec-history-'));
157
157
  const workspacePath = path.join(tempRoot, 'workspace');
158
158
  await mkdir(workspacePath, { recursive: true });
@@ -161,6 +161,8 @@ test('Codex history preserves wrapped exec tool calls and results', { concurrenc
161
161
  try {
162
162
  const providerSessionId = 'codex-exec-1';
163
163
  const transcriptPath = await writeCodexTranscript(tempRoot, providerSessionId, workspacePath);
164
+ // Every shape Codex's "code mode" emits. Only a wrapper nothing recognizes
165
+ // may stay labelled `exec`; the rest must name the work they performed.
164
166
  const wrappedCalls = [
165
167
  {
166
168
  callId: 'shell-command-1',
@@ -176,14 +178,22 @@ test('Codex history preserves wrapped exec tool calls and results', { concurrenc
176
178
  },
177
179
  {
178
180
  callId: 'exec-command-1',
179
- input: 'await tools.exec_command({"command":"echo current"});',
181
+ input: 'await tools.exec_command({"cmd":"npm test","workdir":"/repo","yield_time_ms":10000});',
180
182
  expectedToolName: 'Bash',
181
- expectedToolInput: JSON.stringify({ command: 'echo current' }),
183
+ expectedToolInput: JSON.stringify({ command: 'npm test' }),
184
+ },
185
+ {
186
+ callId: 'web-run-1',
187
+ input: 'await tools.web__run({ search_query: [{ q: "Codex" }, { q: "rollout format" }] });',
188
+ expectedToolName: 'WebSearch',
189
+ expectedToolInput: JSON.stringify({ query: 'Codex | rollout format' }),
190
+ },
191
+ {
192
+ callId: 'unknown-1',
193
+ input: 'await tools.unknown_wrapper({ value: true });',
194
+ expectedToolName: 'exec',
195
+ expectedToolInput: 'await tools.unknown_wrapper({ value: true });',
182
196
  },
183
- { callId: 'apply-patch-1', input: 'await tools.apply_patch("*** Begin Patch\\n*** End Patch");' },
184
- { callId: 'web-run-1', input: 'await tools.web__run({ search_query: [{ q: "Codex" }] });' },
185
- { callId: 'update-plan-1', input: 'await tools.update_plan({ plan: [] });' },
186
- { callId: 'unknown-1', input: 'await tools.unknown_wrapper({ value: true });' },
187
197
  ];
188
198
  const transcriptLines = [
189
199
  JSON.stringify({ type: 'session_meta', payload: { id: providerSessionId, cwd: workspacePath } }),
@@ -209,19 +219,15 @@ test('Codex history preserves wrapped exec tool calls and results', { concurrenc
209
219
 
210
220
  const history = await new CodexSessionsProvider().fetchHistory('app-exec-1');
211
221
  const toolUses = history.messages.filter((message) => message.kind === 'tool_use');
212
- const toolResults = history.messages.filter((message) => message.kind === 'tool_result');
213
222
  const toolUsesById = new Map(toolUses.map((message) => [message.toolId, message]));
214
- const toolResultsById = new Map(toolResults.map((message) => [message.toolId, message]));
215
223
 
216
224
  assert.equal(toolUses.length, wrappedCalls.length);
217
- assert.equal(toolResults.length, wrappedCalls.length);
218
225
  for (const call of wrappedCalls) {
219
226
  const toolUse = toolUsesById.get(call.callId);
220
- assert.ok(toolUse);
221
- assert.equal(toolUse.toolName, call.expectedToolName || 'exec');
222
- assert.equal(toolUse.toolInput, call.expectedToolInput || call.input);
227
+ assert.ok(toolUse, `missing row for ${call.callId}`);
228
+ assert.equal(toolUse.toolName, call.expectedToolName);
229
+ assert.equal(toolUse.toolInput, call.expectedToolInput);
223
230
  assert.equal(toolUse.toolResult?.content, `result:${call.callId}`);
224
- assert.equal(toolResultsById.get(call.callId)?.content, `result:${call.callId}`);
225
231
  }
226
232
  });
227
233
  } finally {
@@ -229,3 +235,378 @@ test('Codex history preserves wrapped exec tool calls and results', { concurrenc
229
235
  await rm(tempRoot, { recursive: true, force: true });
230
236
  }
231
237
  });
238
+
239
+ test('Codex history strips the sandbox envelopes from shell output', { concurrency: false }, async () => {
240
+ const tempRoot = await mkdtemp(path.join(os.tmpdir(), 'codex-exec-output-'));
241
+ const workspacePath = path.join(tempRoot, 'workspace');
242
+ await mkdir(workspacePath, { recursive: true });
243
+ const restoreHomeDir = patchHomeDir(tempRoot);
244
+
245
+ try {
246
+ const providerSessionId = 'codex-output-1';
247
+ const transcriptPath = await writeCodexTranscript(tempRoot, providerSessionId, workspacePath);
248
+ await writeFile(transcriptPath, `${[
249
+ JSON.stringify({ type: 'session_meta', payload: { id: providerSessionId, cwd: workspacePath } }),
250
+ JSON.stringify({
251
+ type: 'response_item',
252
+ payload: {
253
+ type: 'custom_tool_call',
254
+ name: 'exec',
255
+ call_id: 'failing-1',
256
+ input: 'await tools.shell_command({ command: "npm test" });',
257
+ },
258
+ }),
259
+ // Codex nests two report headers: one for the sandbox script and one for
260
+ // the command it ran. Neither belongs in a chat transcript.
261
+ JSON.stringify({
262
+ type: 'response_item',
263
+ payload: {
264
+ type: 'custom_tool_call_output',
265
+ call_id: 'failing-1',
266
+ output: [
267
+ { type: 'input_text', text: 'Script failed\nWall time 1.2 seconds\nOutput:\n' },
268
+ { type: 'input_text', text: 'Script error:\nExit code: 1\nWall time: 1.1 seconds\nOutput:\n1 test failed\n' },
269
+ ],
270
+ },
271
+ }),
272
+ ].join('\n')}\n`, 'utf8');
273
+
274
+ await withIsolatedDatabase(async () => {
275
+ sessionsDb.createAppSession('app-output-1', 'codex', workspacePath);
276
+ sessionsDb.assignProviderSessionId('app-output-1', providerSessionId);
277
+ await new CodexSessionSynchronizer().synchronize();
278
+
279
+ const history = await new CodexSessionsProvider().fetchHistory('app-output-1');
280
+ const bash = history.messages.find((message) => message.kind === 'tool_use');
281
+
282
+ assert.equal(bash?.toolName, 'Bash');
283
+ assert.equal(bash?.toolResult?.content, '1 test failed\n');
284
+ assert.equal(bash?.toolResult?.isError, true, 'a non-zero exit code must mark the row failed');
285
+ });
286
+ } finally {
287
+ restoreHomeDir();
288
+ await rm(tempRoot, { recursive: true, force: true });
289
+ }
290
+ });
291
+
292
+ test('Codex history renders one file row per patched file, from the applied diff', { concurrency: false }, async () => {
293
+ const tempRoot = await mkdtemp(path.join(os.tmpdir(), 'codex-patch-history-'));
294
+ const workspacePath = path.join(tempRoot, 'workspace');
295
+ await mkdir(workspacePath, { recursive: true });
296
+ const restoreHomeDir = patchHomeDir(tempRoot);
297
+
298
+ try {
299
+ const providerSessionId = 'codex-patch-1';
300
+ const transcriptPath = await writeCodexTranscript(tempRoot, providerSessionId, workspacePath);
301
+ const patch = [
302
+ '*** Begin Patch',
303
+ '*** Update File: /repo/a.ts',
304
+ '@@',
305
+ '-const a = 1;',
306
+ '+const a = 2;',
307
+ '*** Update File: /repo/b.ts',
308
+ '@@',
309
+ '-const b = 1;',
310
+ '+const b = 2;',
311
+ '*** End Patch',
312
+ ].join('\n');
313
+
314
+ await writeFile(transcriptPath, `${[
315
+ JSON.stringify({ type: 'session_meta', payload: { id: providerSessionId, cwd: workspacePath } }),
316
+ JSON.stringify({
317
+ type: 'response_item',
318
+ payload: { type: 'custom_tool_call', name: 'apply_patch', call_id: 'patch-1', input: patch },
319
+ }),
320
+ // The out-of-band report carries the real diffs, keyed by path in an
321
+ // order that does not follow the patch body.
322
+ JSON.stringify({
323
+ type: 'event_msg',
324
+ payload: {
325
+ type: 'patch_apply_end',
326
+ call_id: 'patch-1',
327
+ success: true,
328
+ changes: {
329
+ '/repo/b.ts': { type: 'update', unified_diff: '@@\n-const b = 1;\n+const b = 22;\n' },
330
+ '/repo/a.ts': { type: 'update', unified_diff: '@@\n-const a = 1;\n+const a = 22;\n' },
331
+ },
332
+ },
333
+ }),
334
+ JSON.stringify({
335
+ type: 'response_item',
336
+ payload: { type: 'custom_tool_call_output', call_id: 'patch-1', output: '{}' },
337
+ }),
338
+ ].join('\n')}\n`, 'utf8');
339
+
340
+ await withIsolatedDatabase(async () => {
341
+ sessionsDb.createAppSession('app-patch-1', 'codex', workspacePath);
342
+ sessionsDb.assignProviderSessionId('app-patch-1', providerSessionId);
343
+ await new CodexSessionSynchronizer().synchronize();
344
+
345
+ const history = await new CodexSessionsProvider().fetchHistory('app-patch-1');
346
+ const edits = history.messages.filter((message) => message.kind === 'tool_use');
347
+
348
+ assert.equal(edits.length, 2, 'the patch must not be rendered twice');
349
+ const byPath = new Map(edits.map((edit) => {
350
+ const input = JSON.parse(String(edit.toolInput)) as { file_path: string; new_string: string };
351
+ return [input.file_path, { edit, input }];
352
+ }));
353
+
354
+ assert.equal(byPath.get('/repo/a.ts')?.edit.toolName, 'Edit');
355
+ // The applied diff wins over the one reconstructed from the call input,
356
+ // and it must land on the row for its own file.
357
+ assert.equal(byPath.get('/repo/a.ts')?.input.new_string, 'const a = 22;');
358
+ assert.equal(byPath.get('/repo/b.ts')?.input.new_string, 'const b = 22;');
359
+ for (const edit of edits) {
360
+ assert.equal(edit.toolResult?.isError, false, 'a successful patch must resolve its row');
361
+ }
362
+ });
363
+ } finally {
364
+ restoreHomeDir();
365
+ await rm(tempRoot, { recursive: true, force: true });
366
+ }
367
+ });
368
+
369
+ test('Codex history attaches a spawned agent\'s own transcript to the Task row', { concurrency: false }, async () => {
370
+ const tempRoot = await mkdtemp(path.join(os.tmpdir(), 'codex-subagent-history-'));
371
+ const workspacePath = path.join(tempRoot, 'workspace');
372
+ await mkdir(workspacePath, { recursive: true });
373
+ const restoreHomeDir = patchHomeDir(tempRoot);
374
+
375
+ try {
376
+ const providerSessionId = 'codex-parent-2';
377
+ const agentThreadId = 'codex-agent-thread-2';
378
+ const transcriptPath = await writeCodexTranscript(tempRoot, providerSessionId, workspacePath);
379
+
380
+ await writeFile(transcriptPath, `${[
381
+ JSON.stringify({ type: 'session_meta', payload: { id: providerSessionId, cwd: workspacePath } }),
382
+ JSON.stringify({
383
+ type: 'response_item',
384
+ payload: {
385
+ type: 'function_call',
386
+ name: 'spawn_agent',
387
+ call_id: 'spawn-1',
388
+ arguments: JSON.stringify({ task_name: 'test_agent_1' }),
389
+ },
390
+ }),
391
+ JSON.stringify({
392
+ type: 'event_msg',
393
+ payload: {
394
+ type: 'sub_agent_activity',
395
+ kind: 'started',
396
+ event_id: 'spawn-1',
397
+ agent_thread_id: agentThreadId,
398
+ agent_path: '/root/test_agent_1',
399
+ },
400
+ }),
401
+ JSON.stringify({
402
+ type: 'response_item',
403
+ payload: {
404
+ type: 'agent_message',
405
+ author: '/root/test_agent_1',
406
+ content: [{ type: 'input_text', text: 'Message Type: FINAL_ANSWER\nSender: /root/test_agent_1\nPayload:\nAll done.' }],
407
+ },
408
+ }),
409
+ ].join('\n')}\n`, 'utf8');
410
+
411
+ // Codex writes a spawned agent's rollout next to its parent's, named after
412
+ // the agent thread id.
413
+ await writeFile(
414
+ path.join(path.dirname(transcriptPath), `rollout-2026-07-07T00-00-00-${agentThreadId}.jsonl`),
415
+ `${[
416
+ JSON.stringify({
417
+ type: 'session_meta',
418
+ payload: { id: agentThreadId, cwd: workspacePath, thread_source: 'subagent', agent_nickname: 'Hegel' },
419
+ }),
420
+ JSON.stringify({
421
+ type: 'response_item',
422
+ payload: {
423
+ type: 'custom_tool_call',
424
+ name: 'exec',
425
+ call_id: 'agent-call-1',
426
+ input: 'await tools.shell_command({ command: "ls" });',
427
+ },
428
+ }),
429
+ JSON.stringify({
430
+ type: 'response_item',
431
+ payload: { type: 'custom_tool_call_output', call_id: 'agent-call-1', output: 'Exit code: 0\nOutput:\nREADME.md\n' },
432
+ }),
433
+ ].join('\n')}\n`,
434
+ 'utf8',
435
+ );
436
+
437
+ await withIsolatedDatabase(async () => {
438
+ sessionsDb.createAppSession('app-parent-2', 'codex', workspacePath);
439
+ sessionsDb.assignProviderSessionId('app-parent-2', providerSessionId);
440
+ await new CodexSessionSynchronizer().synchronize();
441
+
442
+ const history = await new CodexSessionsProvider().fetchHistory('app-parent-2');
443
+ const task = history.messages.find((message) => message.kind === 'tool_use' && message.toolName === 'Task');
444
+
445
+ assert.ok(task, 'a spawned agent must produce a Task row');
446
+ assert.equal(task.subagent?.id, agentThreadId);
447
+ assert.equal(task.subagent?.name, 'Hegel');
448
+ assert.equal(task.subagent?.status, 'completed');
449
+ assert.equal(task.toolResult?.content, 'All done.');
450
+
451
+ // The agent's own shell call is translated exactly like the parent's.
452
+ assert.equal(task.subagentTools?.length, 1);
453
+ assert.equal(task.subagentTools?.[0].kind, 'tool');
454
+ assert.equal(task.subagentTools?.[0].toolName, 'Bash');
455
+ assert.equal(task.subagentTools?.[0].toolResult?.content, 'README.md\n');
456
+ });
457
+ } finally {
458
+ restoreHomeDir();
459
+ await rm(tempRoot, { recursive: true, force: true });
460
+ }
461
+ });
462
+
463
+ test('Codex memory citations are lifted out of the reply they trail', () => {
464
+ const reply = [
465
+ 'Here is the answer.',
466
+ '',
467
+ '<oai-mem-citation>',
468
+ '<citation_entries>',
469
+ 'MEMORY.md:137-142|note=[used verified container provisioning details]',
470
+ 'notes/deploy.md:4-9',
471
+ '</citation_entries>',
472
+ '<rollout_ids>',
473
+ '019eda6d-c2f7-70c1-8b42-bf03c44a1f35',
474
+ '</rollout_ids>',
475
+ '</oai-mem-citation>',
476
+ ].join('\n');
477
+
478
+ const { text, memoryCitations } = readCodexMemoryCitations(reply);
479
+
480
+ assert.equal(text, 'Here is the answer.');
481
+ assert.deepEqual(memoryCitations, [
482
+ { source: 'MEMORY.md:137-142', note: 'used verified container provisioning details' },
483
+ { source: 'notes/deploy.md:4-9' },
484
+ ]);
485
+ });
486
+
487
+ test('a plan followed by a memory citation is still recognized as a plan', async () => {
488
+ const tempRoot = await mkdtemp(path.join(os.tmpdir(), 'codex-plan-citation-'));
489
+ const workspacePath = path.join(tempRoot, 'workspace');
490
+ await mkdir(workspacePath, { recursive: true });
491
+ const restoreHomeDir = patchHomeDir(tempRoot);
492
+
493
+ try {
494
+ const providerSessionId = 'codex-plan-citation-1';
495
+ const sessionsDir = path.join(tempRoot, '.codex', 'sessions', '2026', '07', '07');
496
+ await mkdir(sessionsDir, { recursive: true });
497
+ await writeFile(path.join(sessionsDir, `rollout-${providerSessionId}.jsonl`), `${[
498
+ JSON.stringify({ type: 'session_meta', payload: { id: providerSessionId, cwd: workspacePath } }),
499
+ JSON.stringify({
500
+ type: 'response_item',
501
+ payload: {
502
+ type: 'message',
503
+ role: 'assistant',
504
+ id: 'plan-1',
505
+ content: [{
506
+ type: 'output_text',
507
+ text: '<proposed_plan>\n# Ship it\n</proposed_plan>\n\n<oai-mem-citation>\n<citation_entries>\nMEMORY.md:1-2|note=[prior deploy steps]\n</citation_entries>\n</oai-mem-citation>',
508
+ }],
509
+ },
510
+ }),
511
+ ].join('\n')}\n`, 'utf8');
512
+
513
+ await withIsolatedDatabase(async () => {
514
+ sessionsDb.createAppSession('app-plan-citation-1', 'codex', workspacePath);
515
+ sessionsDb.assignProviderSessionId('app-plan-citation-1', providerSessionId);
516
+ await new CodexSessionSynchronizer().synchronize();
517
+
518
+ const history = await new CodexSessionsProvider().fetchHistory('app-plan-citation-1');
519
+ const plan = history.messages.find((message) => message.toolName === 'ExitPlanMode');
520
+
521
+ assert.ok(plan, 'the trailing citation block must not hide the plan envelope');
522
+ assert.equal(JSON.parse(String(plan.toolInput)).plan, '# Ship it');
523
+ assert.deepEqual(plan.memoryCitations, [{ source: 'MEMORY.md:1-2', note: 'prior deploy steps' }]);
524
+ assert.ok(
525
+ !history.messages.some((message) => JSON.stringify(message).includes('oai-mem-citation')),
526
+ 'no transcript row may still carry the raw citation markup',
527
+ );
528
+ });
529
+ } finally {
530
+ restoreHomeDir();
531
+ await rm(tempRoot, { recursive: true, force: true });
532
+ }
533
+ });
534
+
535
+ test('an exec script that updates the plan yields the steps it set', () => {
536
+ const operations = parseCodexExecScript([
537
+ 'const p = await tools.update_plan({plan:[',
538
+ ' {step:"Confirm the current workspace",status:"completed"},',
539
+ ' {step:"Check the Git working-tree status",status:"in_progress"},',
540
+ ' {step:"Identify the project",status:"pending"}',
541
+ ']});',
542
+ 'text(p);',
543
+ ].join('\n'));
544
+
545
+ assert.deepEqual(operations, [{
546
+ kind: 'plan',
547
+ todos: [
548
+ { content: 'Confirm the current workspace', status: 'completed' },
549
+ { content: 'Check the Git working-tree status', status: 'in_progress' },
550
+ { content: 'Identify the project', status: 'pending' },
551
+ ],
552
+ }]);
553
+ });
554
+
555
+
556
+ test('Codex history restores user prompts from typed item_completed rows', { concurrency: false }, async () => {
557
+ const tempRoot = await mkdtemp(path.join(os.tmpdir(), 'codex-typed-user-history-'));
558
+ const workspacePath = path.join(tempRoot, 'workspace');
559
+ await mkdir(workspacePath, { recursive: true });
560
+ const restoreHomeDir = patchHomeDir(tempRoot);
561
+
562
+ try {
563
+ const providerSessionId = 'codex-typed-1';
564
+ const prompt = 'Typed prompt that must survive a refresh';
565
+ const transcriptPath = path.join(tempRoot, '.codex', 'sessions', '2026', '07', '07', 'rollout-' + providerSessionId + '.jsonl');
566
+ await mkdir(path.dirname(transcriptPath), { recursive: true });
567
+ const lineEnd = String.fromCharCode(10);
568
+ const typedLine = JSON.stringify({
569
+ timestamp: '2026-09-07T17:59:17.000Z',
570
+ type: 'event_msg',
571
+ payload: {
572
+ type: 'item_completed',
573
+ turn_id: 'typed-turn-1',
574
+ item: {
575
+ type: 'UserMessage',
576
+ id: 'item-user-1',
577
+ content: [
578
+ { type: 'text', text: prompt, text_elements: [] },
579
+ { type: 'local_image', path: '/tmp/prompt-picture.png', detail: null },
580
+ ],
581
+ },
582
+ },
583
+ });
584
+ const wireUserEcho = JSON.stringify({
585
+ timestamp: '2026-09-07T17:59:19.000Z',
586
+ type: 'response_item',
587
+ payload: { type: 'message', role: 'user', content: [{ type: 'input_text', text: prompt }] },
588
+ });
589
+ await writeFile(
590
+ transcriptPath,
591
+ [JSON.stringify({ type: 'session_meta', payload: { id: providerSessionId, cwd: workspacePath } }), typedLine, wireUserEcho].join(lineEnd) + lineEnd,
592
+ 'utf8',
593
+ );
594
+
595
+ await withIsolatedDatabase(async () => {
596
+ sessionsDb.createAppSession('app-typed-1', 'codex', workspacePath);
597
+ sessionsDb.assignProviderSessionId('app-typed-1', providerSessionId);
598
+ await new CodexSessionSynchronizer().synchronize();
599
+
600
+ const history = await new CodexSessionsProvider().fetchHistory('app-typed-1');
601
+ const users = history.messages.filter((message) => message.role === 'user');
602
+ const images = users[0]?.images as Array<{ path?: string; data?: string }> | undefined;
603
+
604
+ assert.equal(users.length, 1, 'the wire user echo must not duplicate the typed prompt');
605
+ assert.equal(users[0]?.content, prompt);
606
+ assert.equal(images?.[0]?.path, '/tmp/prompt-picture.png');
607
+ });
608
+ } finally {
609
+ restoreHomeDir();
610
+ await rm(tempRoot, { recursive: true, force: true });
611
+ }
612
+ });
@@ -0,0 +1,187 @@
1
+ import assert from 'node:assert/strict';
2
+ import { existsSync } from 'node:fs';
3
+ import { chmod, mkdtemp, rm, writeFile } from 'node:fs/promises';
4
+ import os from 'node:os';
5
+ import path from 'node:path';
6
+ import test, { mock } from 'node:test';
7
+
8
+ import { CursorProviderAuth } from '@/modules/providers/list/cursor/cursor-auth.provider.js';
9
+
10
+ const findEnvKey = (name: string) =>
11
+ Object.keys(process.env).find((key) => key.toLowerCase() === name.toLowerCase()) || name;
12
+
13
+ // What the real CLI prints when it is logged in and the account request succeeded.
14
+ const LOGGED_IN_OUTPUT = '✓ Logged in as cursor-user@example.com';
15
+
16
+ // A stand-in for the real CLI: `--version` answers at once, while `status` drops a started
17
+ // marker and keeps running (like the real one does while it fetches the account details from
18
+ // Cursor's API) until the test creates the release file, then prints the configured output.
19
+ // It records a SIGTERM so the tests can check that a timed-out check kills it.
20
+ const FAKE_CURSOR_AGENT_SCRIPT = `
21
+ const fs = require('node:fs');
22
+ const path = require('node:path');
23
+ const command = process.argv[2];
24
+ const markerDir = process.env.CURSOR_FAKE_MARKER_DIR;
25
+ if (command === '--version') {
26
+ console.log('2026.07.20-test');
27
+ process.exit(0);
28
+ }
29
+ if (command === 'status') {
30
+ process.on('SIGTERM', () => {
31
+ fs.writeFileSync(path.join(markerDir, 'status-killed'), '');
32
+ process.exit(143);
33
+ });
34
+ fs.writeFileSync(path.join(markerDir, 'status-started'), '');
35
+ // Never outlive a broken test run (its cleanup deletes the marker dir).
36
+ const giveUpAt = Date.now() + 30000;
37
+ const poll = () => {
38
+ if (!fs.existsSync(markerDir) || Date.now() > giveUpAt) {
39
+ process.exit(1);
40
+ }
41
+ if (fs.existsSync(path.join(markerDir, 'release-status'))) {
42
+ process.stdout.write(fs.readFileSync(path.join(markerDir, 'status-output'), 'utf8') + '\\n');
43
+ return;
44
+ }
45
+ setTimeout(poll, 20);
46
+ };
47
+ poll();
48
+ }
49
+ `;
50
+
51
+ async function createFakeCursorAgent(binDir: string) {
52
+ await writeFile(path.join(binDir, 'cursor-agent.cjs'), FAKE_CURSOR_AGENT_SCRIPT, 'utf8');
53
+
54
+ if (process.platform === 'win32') {
55
+ const commandPath = path.join(binDir, 'cursor-agent.cmd');
56
+ await writeFile(commandPath, '@echo off\r\nnode "%~dp0cursor-agent.cjs" %*\r\n', 'utf8');
57
+ return;
58
+ }
59
+
60
+ // exec so the provider's kill() on timeout reaches the node process itself.
61
+ const commandPath = path.join(binDir, 'cursor-agent');
62
+ await writeFile(commandPath, '#!/bin/sh\nexec node "$(dirname "$0")/cursor-agent.cjs" "$@"\n', 'utf8');
63
+ await chmod(commandPath, 0o755);
64
+ }
65
+
66
+ // Waits in real time (the tests mock only setTimeout, not setImmediate or Date) and fails
67
+ // instead of hanging when the fake never gets there.
68
+ async function waitForFile(filePath: string, description: string) {
69
+ const giveUpAt = Date.now() + 15_000;
70
+ while (!existsSync(filePath)) {
71
+ if (Date.now() > giveUpAt) {
72
+ assert.fail(`timed out waiting for ${description}`);
73
+ }
74
+ await new Promise<void>((resolve) => setImmediate(resolve));
75
+ }
76
+ }
77
+
78
+ type FakeCursorAgent = {
79
+ releaseStatus: () => Promise<void>;
80
+ waitForStatusStart: () => Promise<void>;
81
+ waitForStatusKill: () => Promise<void>;
82
+ };
83
+
84
+ const withFakeCursorAgent = async (statusOutput: string, fn: (fake: FakeCursorAgent) => Promise<void>) => {
85
+ const binDir = await mkdtemp(path.join(os.tmpdir(), 'cursor-auth-test-'));
86
+ const fake: FakeCursorAgent = {
87
+ releaseStatus: () => writeFile(path.join(binDir, 'release-status'), '', 'utf8'),
88
+ waitForStatusStart: () => waitForFile(path.join(binDir, 'status-started'), 'the fake `status` to start'),
89
+ waitForStatusKill: () => waitForFile(path.join(binDir, 'status-killed'), 'the fake `status` to get SIGTERM'),
90
+ };
91
+ const pathKey = findEnvKey('PATH');
92
+ const pathExtKey = findEnvKey('PATHEXT');
93
+ const previousPath = process.env[pathKey];
94
+ const previousPathExt = process.env[pathExtKey];
95
+ const previousMarkerDir = process.env.CURSOR_FAKE_MARKER_DIR;
96
+
97
+ try {
98
+ await createFakeCursorAgent(binDir);
99
+ await writeFile(path.join(binDir, 'status-output'), statusOutput, 'utf8');
100
+ process.env[pathKey] = `${binDir}${path.delimiter}${previousPath || ''}`;
101
+ process.env.CURSOR_FAKE_MARKER_DIR = binDir;
102
+ if (process.platform === 'win32') {
103
+ process.env[pathExtKey] = previousPathExt?.toUpperCase().includes('.CMD')
104
+ ? previousPathExt
105
+ : `.COM;.EXE;.BAT;.CMD${previousPathExt ? `;${previousPathExt}` : ''}`;
106
+ }
107
+
108
+ await fn(fake);
109
+ } finally {
110
+ // Lets a fake `status` that is still polling exit right away.
111
+ await fake.releaseStatus().catch(() => {});
112
+ process.env[pathKey] = previousPath;
113
+ if (previousPathExt === undefined) {
114
+ delete process.env[pathExtKey];
115
+ } else {
116
+ process.env[pathExtKey] = previousPathExt;
117
+ }
118
+ if (previousMarkerDir === undefined) {
119
+ delete process.env.CURSOR_FAKE_MARKER_DIR;
120
+ } else {
121
+ process.env.CURSOR_FAKE_MARKER_DIR = previousMarkerDir;
122
+ }
123
+ await rm(binDir, { recursive: true, force: true });
124
+ }
125
+ };
126
+
127
+ test('getStatus waits for a logged-in `cursor-agent status` that takes longer than 5 s (#551)', async () => {
128
+ await withFakeCursorAgent(LOGGED_IN_OUTPUT, async (fake) => {
129
+ // getStatus() arms its timeout before it spawns `status`, so it must see the mocked setTimeout.
130
+ mock.timers.enable({ apis: ['setTimeout'] });
131
+ try {
132
+ const pending = new CursorProviderAuth().getStatus();
133
+ // Move the clock only once `status` runs, i.e. once the timeout is armed.
134
+ await fake.waitForStatusStart();
135
+
136
+ // CLI startup alone took 3-6 s on a busy host; the account request adds a network round trip.
137
+ mock.timers.tick(10_000);
138
+ await fake.releaseStatus();
139
+
140
+ const status = await pending;
141
+ assert.equal(status.error, undefined);
142
+ assert.equal(status.authenticated, true);
143
+ assert.equal(status.email, 'cursor-user@example.com');
144
+ assert.equal(status.method, 'cli');
145
+ } finally {
146
+ mock.timers.reset();
147
+ }
148
+ });
149
+ });
150
+
151
+ test('getStatus still gives up on, and kills, a `cursor-agent status` that never answers', async () => {
152
+ await withFakeCursorAgent(LOGGED_IN_OUTPUT, async (fake) => {
153
+ mock.timers.enable({ apis: ['setTimeout'] });
154
+ try {
155
+ const pending = new CursorProviderAuth().getStatus();
156
+ await fake.waitForStatusStart();
157
+
158
+ mock.timers.tick(60_000);
159
+
160
+ const status = await pending;
161
+ assert.equal(status.installed, true);
162
+ assert.equal(status.authenticated, false);
163
+ assert.equal(status.error, 'Command timeout');
164
+ // On Windows kill() ends the .cmd shim's cmd.exe, which does not signal node behind it.
165
+ if (process.platform !== 'win32') {
166
+ await fake.waitForStatusKill();
167
+ }
168
+ } finally {
169
+ mock.timers.reset();
170
+ }
171
+ });
172
+ });
173
+
174
+ test('getStatus reports no email when `cursor-agent status` could not fetch the account', async () => {
175
+ // What the real CLI prints when its account request fails: the stored login is still valid.
176
+ const output = '✓ Login successful!\nLogged in (unable to fetch user details)';
177
+ await withFakeCursorAgent(output, async (fake) => {
178
+ await fake.releaseStatus();
179
+
180
+ const status = await new CursorProviderAuth().getStatus();
181
+ assert.equal(status.error, undefined);
182
+ assert.equal(status.authenticated, true);
183
+ // The old 'Logged in' placeholder made the Settings card read "Logged in as Logged in".
184
+ assert.equal(status.email, null);
185
+ assert.equal(status.method, 'cli');
186
+ });
187
+ });