factory-factory 0.4.4 → 0.4.6

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 (541) hide show
  1. package/dist/client/assets/{abnfDiagram-VRR7QNED-CtUj9J56.js → abnfDiagram-N423BO3Z-BcUuFtOj.js} +1 -1
  2. package/dist/client/assets/{arc-2QeawLbF.js → arc-Cp8_E6ha.js} +1 -1
  3. package/dist/client/assets/architectureDiagram-T3A2C74G-C3_2J0tC.js +36 -0
  4. package/dist/client/assets/{blockDiagram-677ZJIJ3-BN6MDejN.js → blockDiagram-VBNYF7ZC-Dy0jc6lP.js} +1 -1
  5. package/dist/client/assets/{c4Diagram-LMCZKHZV-ZxcmUt9u.js → c4Diagram-5PPSVZJV-97TkD6w-.js} +1 -1
  6. package/dist/client/assets/channel-DLBBfejH.js +1 -0
  7. package/dist/client/assets/{chunk-32BRIVSS-BXA35oY0.js → chunk-2GRJ4B5K-Dc0I7ume.js} +1 -1
  8. package/dist/client/assets/{chunk-2Q5K7J3B-CyvdNBvs.js → chunk-2Q5K7J3B-DbFPWZwu.js} +1 -1
  9. package/dist/client/assets/{chunk-EX3LRPZG-BRYEhwoK.js → chunk-5RXB4S5H-Dci_7ZDD.js} +1 -1
  10. package/dist/client/assets/{chunk-5VM5RSS4-6lY9D3Us.js → chunk-5VM5RSS4-Ca82Rzl0.js} +1 -1
  11. package/dist/client/assets/{chunk-MOJQB5TN-MIX8YUEz.js → chunk-6Q2QTUOP-Be2f5Q2t.js} +1 -1
  12. package/dist/client/assets/{chunk-V7JOEXUC-DpMacC1A.js → chunk-GF5L2VYU-C4VRs6xS.js} +1 -1
  13. package/dist/client/assets/{chunk-JWPE2WC7-DBeDDTf3.js → chunk-JWPE2WC7-DVlW0c7f.js} +1 -1
  14. package/dist/client/assets/{chunk-VR4S4FIN-Bd6MGh78.js → chunk-KBJHAD2P-BKRpW89b.js} +1 -1
  15. package/dist/client/assets/{chunk-RYQCIY6F-CT_f1sbt.js → chunk-RYQCIY6F-BwMw9oh9.js} +1 -1
  16. package/dist/client/assets/{chunk-XXDRQBXY-BYg-oILv.js → chunk-XXDRQBXY-py-FQquv.js} +1 -1
  17. package/dist/client/assets/classDiagram-JCYQIIEL-Ds5wVQR3.js +1 -0
  18. package/dist/client/assets/classDiagram-v2-OCEON4UE-Ds5wVQR3.js +1 -0
  19. package/dist/client/assets/{cose-bilkent-JH36ORCC-BGhjYi6z.js → cose-bilkent-JH36ORCC-BMCtQCZ8.js} +1 -1
  20. package/dist/client/assets/{cynefin-VYW2F7L2-BouObN4T.js → cynefin-VYW2F7L2-BTHAy6wD.js} +1 -1
  21. package/dist/client/assets/{cynefinDiagram-TSTJHNR4-DrbscNyk.js → cynefinDiagram-MW4NZA55-pQCQD0Ih.js} +1 -1
  22. package/dist/client/assets/{dagre-VKFMJZFB-CpHBhtab.js → dagre-VZM6K2ZE-CZcoEFVf.js} +1 -1
  23. package/dist/client/assets/{diagram-OA4YK3LP-C6Jnb8nm.js → diagram-7IWD3JNH-DgQv_208.js} +1 -1
  24. package/dist/client/assets/{diagram-FQU43EPY-C_pXymmj.js → diagram-B4RE2ZJO-DoFotcsB.js} +1 -1
  25. package/dist/client/assets/{diagram-NH7WQ7WH-DWHcerSm.js → diagram-LBJQPF4R-C5zkL1ml.js} +1 -1
  26. package/dist/client/assets/{diagram-G47NLZAW-DNPz9Qrh.js → diagram-Q27KOJAE-ERKqnoHP.js} +1 -1
  27. package/dist/client/assets/diagram-UB23O5K3-Dyt6sy3Z.js +41 -0
  28. package/dist/client/assets/{ebnfDiagram-CCIWWBDH-_Wt4C-Lm.js → ebnfDiagram-BXEA7PRR-DqsFEAw2.js} +1 -1
  29. package/dist/client/assets/{erDiagram-Q63AITRT-D9F9IIZ7.js → erDiagram-JOGREHBK-Dn8Aqkgn.js} +1 -1
  30. package/dist/client/assets/{flowDiagram-23GEKE2U-BzTUtnLY.js → flowDiagram-UKHOOZJN-DcArGyH-.js} +6 -6
  31. package/dist/client/assets/{ganttDiagram-NO4QXBWP-lmFnKYE5.js → ganttDiagram-PKOTCBZU-BuHaeolc.js} +1 -1
  32. package/dist/client/assets/{gitGraphDiagram-IHSO6WYX-DiIQa_m6.js → gitGraphDiagram-DS77QQ5N-iKWWNsQK.js} +1 -1
  33. package/dist/client/assets/index-CVVo6xHd.css +1 -0
  34. package/dist/client/assets/index-DY0TryY7.js +695 -0
  35. package/dist/client/assets/{infoDiagram-FWYZ7A6U-BXtmiN5g.js → infoDiagram-6WML65LV-C7IulVvo.js} +1 -1
  36. package/dist/client/assets/{ishikawaDiagram-FXEZZL3T-Ce8fzasD.js → ishikawaDiagram-WSZJBQD7-DW4cB_Nk.js} +1 -1
  37. package/dist/client/assets/{journeyDiagram-5HDEW3XC-CAhJE-OU.js → journeyDiagram-NVQOT4AX-Nzsiy4H7.js} +1 -1
  38. package/dist/client/assets/{kanban-definition-HUTT4EX6-B8br5xew.js → kanban-definition-27J2QSJJ-BUsK9dj9.js} +1 -1
  39. package/dist/client/assets/{linear-BPVVEns_.js → linear-CyoW-usj.js} +1 -1
  40. package/dist/client/assets/{mindmap-definition-LN4V7U3C-lX6h9H-N.js → mindmap-definition-FAOFIHXS-Bd9cSJjQ.js} +1 -1
  41. package/dist/client/assets/{mobile-baseline-C45NSkzw.js → mobile-baseline-K78aE4nt.js} +1 -1
  42. package/dist/client/assets/{pegDiagram-2B236MQR-CiFWwnk4.js → pegDiagram-VL7TDLO6-BrmAKdiE.js} +1 -1
  43. package/dist/client/assets/{pieDiagram-ENE6RG2P-CvkwWtzQ.js → pieDiagram-7S7Q4E2Y-Ce0k6LI1.js} +1 -1
  44. package/dist/client/assets/{quadrantDiagram-ABIIQ3AL-B40anMyK.js → quadrantDiagram-CIZ2JOQS-_9HxOOGa.js} +1 -1
  45. package/dist/client/assets/{railroadDiagram-RFXS5EU6-WVYsdkDC.js → railroadDiagram-AXF67PYL-ChdF1CBw.js} +1 -1
  46. package/dist/client/assets/{requirementDiagram-TGXJPOKE-DBTIsXpA.js → requirementDiagram-LRYGKXZP-Bm2gdYTE.js} +1 -1
  47. package/dist/client/assets/{sankeyDiagram-HTMAVEWB-Dx8y47pZ.js → sankeyDiagram-W5VNT64P-Kf7zUiC-.js} +1 -1
  48. package/dist/client/assets/{sequenceDiagram-DBY2YBRQ-C6ZZMZJm.js → sequenceDiagram-SI44F4Z6-DyPqsgh1.js} +1 -1
  49. package/dist/client/assets/{sizeCapture-X5ZJPWSS-DLy8loNR.js → sizeCapture-X5ZJPWSS-Cgn8Hxr2.js} +1 -1
  50. package/dist/client/assets/{stateDiagram-2N3HPSRC-RoTcqZkX.js → stateDiagram-OKZ733FA-CJxSzxmg.js} +1 -1
  51. package/dist/client/assets/stateDiagram-v2-UEYNNEHI-BwUaUW2J.js +1 -0
  52. package/dist/client/assets/{swimlanes-5IMT3BWC-CFmoSHiT.js → swimlanes-SLNWSIFB-Ckb_izEZ.js} +2 -2
  53. package/dist/client/assets/swimlanesDiagram-ULZ7WXOC-D87lLmuW.js +8 -0
  54. package/dist/client/assets/{terminal-instance-Br0jXsXE.js → terminal-instance-DWySt2_t.js} +1 -1
  55. package/dist/client/assets/{timeline-definition-FHXFAJF6-Bh5XA09y.js → timeline-definition-Z64GVDOM-BWx_NLrK.js} +1 -1
  56. package/dist/client/assets/{vennDiagram-L72KCM5P-BC754zGO.js → vennDiagram-T6HMQDX7-BgN2-lO-.js} +1 -1
  57. package/dist/client/assets/{wardleyDiagram-EHGQE667-xJdUzWCg.js → wardleyDiagram-T6FBY63Y-C9iwoeki.js} +1 -1
  58. package/dist/client/assets/{xychartDiagram-FW5EYKEG-wxQH1S7i.js → xychartDiagram-ELKLHX3M-B3AYy5ZN.js} +4 -4
  59. package/dist/client/audio-worklets/pcm-capture-processor.js +67 -0
  60. package/dist/client/index.html +2 -2
  61. package/dist/client/sounds/voice-thinking-start.mp3 +0 -0
  62. package/dist/prisma/generated/browser.d.ts +1 -0
  63. package/dist/prisma/generated/browser.d.ts.map +1 -1
  64. package/dist/prisma/generated/client.d.ts +1 -0
  65. package/dist/prisma/generated/client.d.ts.map +1 -1
  66. package/dist/prisma/generated/commonInputTypes.d.ts +100 -0
  67. package/dist/prisma/generated/commonInputTypes.d.ts.map +1 -1
  68. package/dist/prisma/generated/enums.d.ts +16 -0
  69. package/dist/prisma/generated/enums.d.ts.map +1 -1
  70. package/dist/prisma/generated/enums.js +14 -0
  71. package/dist/prisma/generated/enums.js.map +1 -1
  72. package/dist/prisma/generated/internal/class.d.ts +3 -0
  73. package/dist/prisma/generated/internal/class.d.ts.map +1 -1
  74. package/dist/prisma/generated/internal/class.js +4 -4
  75. package/dist/prisma/generated/internal/class.js.map +1 -1
  76. package/dist/prisma/generated/internal/prismaNamespace.d.ts +97 -2
  77. package/dist/prisma/generated/internal/prismaNamespace.d.ts.map +1 -1
  78. package/dist/prisma/generated/internal/prismaNamespace.js +17 -0
  79. package/dist/prisma/generated/internal/prismaNamespace.js.map +1 -1
  80. package/dist/prisma/generated/internal/prismaNamespaceBrowser.d.ts +18 -0
  81. package/dist/prisma/generated/internal/prismaNamespaceBrowser.d.ts.map +1 -1
  82. package/dist/prisma/generated/internal/prismaNamespaceBrowser.js +17 -0
  83. package/dist/prisma/generated/internal/prismaNamespaceBrowser.js.map +1 -1
  84. package/dist/prisma/generated/models/SessionLifecycleEvent.d.ts +657 -0
  85. package/dist/prisma/generated/models/SessionLifecycleEvent.d.ts.map +1 -0
  86. package/dist/prisma/generated/models/SessionLifecycleEvent.js +2 -0
  87. package/dist/prisma/generated/models/SessionLifecycleEvent.js.map +1 -0
  88. package/dist/prisma/generated/models/UserSettings.d.ts +216 -1
  89. package/dist/prisma/generated/models/UserSettings.d.ts.map +1 -1
  90. package/dist/prisma/generated/models/Workspace.d.ts +264 -0
  91. package/dist/prisma/generated/models/Workspace.d.ts.map +1 -1
  92. package/dist/prisma/generated/models.d.ts +1 -0
  93. package/dist/prisma/generated/models.d.ts.map +1 -1
  94. package/dist/src/backend/app-context.d.ts +4 -1
  95. package/dist/src/backend/app-context.d.ts.map +1 -1
  96. package/dist/src/backend/app-context.js +6 -2
  97. package/dist/src/backend/app-context.js.map +1 -1
  98. package/dist/src/backend/interceptors/branch-naming.interceptor.d.ts.map +1 -1
  99. package/dist/src/backend/interceptors/branch-naming.interceptor.js +0 -4
  100. package/dist/src/backend/interceptors/branch-naming.interceptor.js.map +1 -1
  101. package/dist/src/backend/interceptors/branch-naming.interceptor.test.js +26 -0
  102. package/dist/src/backend/interceptors/branch-naming.interceptor.test.js.map +1 -1
  103. package/dist/src/backend/orchestration/data-backup.service.d.ts.map +1 -1
  104. package/dist/src/backend/orchestration/data-backup.service.js +10 -0
  105. package/dist/src/backend/orchestration/data-backup.service.js.map +1 -1
  106. package/dist/src/backend/orchestration/data-backup.service.test.js +23 -0
  107. package/dist/src/backend/orchestration/data-backup.service.test.js.map +1 -1
  108. package/dist/src/backend/orchestration/domain-bridges.orchestrator.d.ts +2 -1
  109. package/dist/src/backend/orchestration/domain-bridges.orchestrator.d.ts.map +1 -1
  110. package/dist/src/backend/orchestration/domain-bridges.orchestrator.js +6 -6
  111. package/dist/src/backend/orchestration/domain-bridges.orchestrator.js.map +1 -1
  112. package/dist/src/backend/orchestration/domain-bridges.orchestrator.test.js +18 -8
  113. package/dist/src/backend/orchestration/domain-bridges.orchestrator.test.js.map +1 -1
  114. package/dist/src/backend/orchestration/event-collector.orchestrator.d.ts.map +1 -1
  115. package/dist/src/backend/orchestration/event-collector.orchestrator.js +3 -1
  116. package/dist/src/backend/orchestration/event-collector.orchestrator.js.map +1 -1
  117. package/dist/src/backend/orchestration/event-collector.orchestrator.test.js +3 -1
  118. package/dist/src/backend/orchestration/event-collector.orchestrator.test.js.map +1 -1
  119. package/dist/src/backend/orchestration/reconciliation.service.d.ts +11 -8
  120. package/dist/src/backend/orchestration/reconciliation.service.d.ts.map +1 -1
  121. package/dist/src/backend/orchestration/reconciliation.service.js +45 -33
  122. package/dist/src/backend/orchestration/reconciliation.service.js.map +1 -1
  123. package/dist/src/backend/orchestration/reconciliation.service.test.js +48 -42
  124. package/dist/src/backend/orchestration/reconciliation.service.test.js.map +1 -1
  125. package/dist/src/backend/orchestration/workspace-archive.orchestrator.d.ts +4 -1
  126. package/dist/src/backend/orchestration/workspace-archive.orchestrator.d.ts.map +1 -1
  127. package/dist/src/backend/orchestration/workspace-archive.orchestrator.js +4 -1
  128. package/dist/src/backend/orchestration/workspace-archive.orchestrator.js.map +1 -1
  129. package/dist/src/backend/orchestration/workspace-archive.orchestrator.test.js +15 -2
  130. package/dist/src/backend/orchestration/workspace-archive.orchestrator.test.js.map +1 -1
  131. package/dist/src/backend/orchestration/workspace-init-issue-prompts.d.ts.map +1 -1
  132. package/dist/src/backend/orchestration/workspace-init-issue-prompts.js +3 -1
  133. package/dist/src/backend/orchestration/workspace-init-issue-prompts.js.map +1 -1
  134. package/dist/src/backend/orchestration/workspace-init-issue-prompts.test.d.ts +2 -0
  135. package/dist/src/backend/orchestration/workspace-init-issue-prompts.test.d.ts.map +1 -0
  136. package/dist/src/backend/orchestration/workspace-init-issue-prompts.test.js +75 -0
  137. package/dist/src/backend/orchestration/workspace-init-issue-prompts.test.js.map +1 -0
  138. package/dist/src/backend/orchestration/workspace-init.orchestrator.d.ts +7 -4
  139. package/dist/src/backend/orchestration/workspace-init.orchestrator.d.ts.map +1 -1
  140. package/dist/src/backend/orchestration/workspace-init.orchestrator.js +36 -1
  141. package/dist/src/backend/orchestration/workspace-init.orchestrator.js.map +1 -1
  142. package/dist/src/backend/orchestration/workspace-init.orchestrator.test.js +117 -8
  143. package/dist/src/backend/orchestration/workspace-init.orchestrator.test.js.map +1 -1
  144. package/dist/src/backend/routers/websocket/index.d.ts +1 -0
  145. package/dist/src/backend/routers/websocket/index.d.ts.map +1 -1
  146. package/dist/src/backend/routers/websocket/index.js +1 -0
  147. package/dist/src/backend/routers/websocket/index.js.map +1 -1
  148. package/dist/src/backend/routers/websocket/voice-soft-stop.handler.d.ts +11 -0
  149. package/dist/src/backend/routers/websocket/voice-soft-stop.handler.d.ts.map +1 -0
  150. package/dist/src/backend/routers/websocket/voice-soft-stop.handler.js +28 -0
  151. package/dist/src/backend/routers/websocket/voice-soft-stop.handler.js.map +1 -0
  152. package/dist/src/backend/routers/websocket/voice-soft-stop.handler.test.d.ts +2 -0
  153. package/dist/src/backend/routers/websocket/voice-soft-stop.handler.test.d.ts.map +1 -0
  154. package/dist/src/backend/routers/websocket/voice-soft-stop.handler.test.js +84 -0
  155. package/dist/src/backend/routers/websocket/voice-soft-stop.handler.test.js.map +1 -0
  156. package/dist/src/backend/routers/websocket/voice.handler.d.ts +3 -0
  157. package/dist/src/backend/routers/websocket/voice.handler.d.ts.map +1 -0
  158. package/dist/src/backend/routers/websocket/voice.handler.js +46 -0
  159. package/dist/src/backend/routers/websocket/voice.handler.js.map +1 -0
  160. package/dist/src/backend/server.d.ts.map +1 -1
  161. package/dist/src/backend/server.js +3 -1
  162. package/dist/src/backend/server.js.map +1 -1
  163. package/dist/src/backend/server.upgrade.test.js +16 -0
  164. package/dist/src/backend/server.upgrade.test.js.map +1 -1
  165. package/dist/src/backend/services/periodic-task/resources/periodic-task.accessor.d.ts.map +1 -1
  166. package/dist/src/backend/services/periodic-task/resources/periodic-task.accessor.js +27 -6
  167. package/dist/src/backend/services/periodic-task/resources/periodic-task.accessor.js.map +1 -1
  168. package/dist/src/backend/services/periodic-task/resources/periodic-task.accessor.test.js +7 -0
  169. package/dist/src/backend/services/periodic-task/resources/periodic-task.accessor.test.js.map +1 -1
  170. package/dist/src/backend/services/port.service.d.ts.map +1 -1
  171. package/dist/src/backend/services/port.service.js +4 -0
  172. package/dist/src/backend/services/port.service.js.map +1 -1
  173. package/dist/src/backend/services/port.service.test.js +8 -0
  174. package/dist/src/backend/services/port.service.test.js.map +1 -1
  175. package/dist/src/backend/services/ratchet/service/ratchet.service.test.js +6 -0
  176. package/dist/src/backend/services/ratchet/service/ratchet.service.test.js.map +1 -1
  177. package/dist/src/backend/services/registry.d.ts +2 -2
  178. package/dist/src/backend/services/registry.d.ts.map +1 -1
  179. package/dist/src/backend/services/registry.js +2 -1
  180. package/dist/src/backend/services/registry.js.map +1 -1
  181. package/dist/src/backend/services/session/resources/session-lifecycle-event.accessor.d.ts +23 -0
  182. package/dist/src/backend/services/session/resources/session-lifecycle-event.accessor.d.ts.map +1 -0
  183. package/dist/src/backend/services/session/resources/session-lifecycle-event.accessor.js +23 -0
  184. package/dist/src/backend/services/session/resources/session-lifecycle-event.accessor.js.map +1 -0
  185. package/dist/src/backend/services/session/resources/session-lifecycle-event.accessor.test.d.ts +2 -0
  186. package/dist/src/backend/services/session/resources/session-lifecycle-event.accessor.test.d.ts.map +1 -0
  187. package/dist/src/backend/services/session/resources/session-lifecycle-event.accessor.test.js +62 -0
  188. package/dist/src/backend/services/session/resources/session-lifecycle-event.accessor.test.js.map +1 -0
  189. package/dist/src/backend/services/session/service/acp/acp-client-handler.d.ts +1 -0
  190. package/dist/src/backend/services/session/service/acp/acp-client-handler.d.ts.map +1 -1
  191. package/dist/src/backend/services/session/service/acp/acp-client-handler.js +30 -0
  192. package/dist/src/backend/services/session/service/acp/acp-client-handler.js.map +1 -1
  193. package/dist/src/backend/services/session/service/acp/acp-client-handler.test.js +57 -0
  194. package/dist/src/backend/services/session/service/acp/acp-client-handler.test.js.map +1 -1
  195. package/dist/src/backend/services/session/service/acp/acp-event-translator.d.ts +1 -37
  196. package/dist/src/backend/services/session/service/acp/acp-event-translator.d.ts.map +1 -1
  197. package/dist/src/backend/services/session/service/acp/acp-event-translator.js +1 -334
  198. package/dist/src/backend/services/session/service/acp/acp-event-translator.js.map +1 -1
  199. package/dist/src/backend/services/session/service/acp/acp-event-translator.test.js +5 -484
  200. package/dist/src/backend/services/session/service/acp/acp-event-translator.test.js.map +1 -1
  201. package/dist/src/backend/services/session/service/acp/acp-process-handle.d.ts +2 -0
  202. package/dist/src/backend/services/session/service/acp/acp-process-handle.d.ts.map +1 -1
  203. package/dist/src/backend/services/session/service/acp/acp-process-handle.js +12 -0
  204. package/dist/src/backend/services/session/service/acp/acp-process-handle.js.map +1 -1
  205. package/dist/src/backend/services/session/service/acp/acp-process-handle.test.d.ts +2 -0
  206. package/dist/src/backend/services/session/service/acp/acp-process-handle.test.d.ts.map +1 -0
  207. package/dist/src/backend/services/session/service/acp/acp-process-handle.test.js +66 -0
  208. package/dist/src/backend/services/session/service/acp/acp-process-handle.test.js.map +1 -0
  209. package/dist/src/backend/services/session/service/acp/acp-runtime-events.d.ts +7 -1
  210. package/dist/src/backend/services/session/service/acp/acp-runtime-events.d.ts.map +1 -1
  211. package/dist/src/backend/services/session/service/acp/acp-runtime-manager.d.ts +21 -2
  212. package/dist/src/backend/services/session/service/acp/acp-runtime-manager.d.ts.map +1 -1
  213. package/dist/src/backend/services/session/service/acp/acp-runtime-manager.js +254 -61
  214. package/dist/src/backend/services/session/service/acp/acp-runtime-manager.js.map +1 -1
  215. package/dist/src/backend/services/session/service/acp/acp-runtime-manager.test.js +353 -22
  216. package/dist/src/backend/services/session/service/acp/acp-runtime-manager.test.js.map +1 -1
  217. package/dist/src/backend/services/session/service/acp/acp-session-config-options.d.ts +1 -0
  218. package/dist/src/backend/services/session/service/acp/acp-session-config-options.d.ts.map +1 -1
  219. package/dist/src/backend/services/session/service/acp/acp-session-config-options.js +4 -17
  220. package/dist/src/backend/services/session/service/acp/acp-session-config-options.js.map +1 -1
  221. package/dist/src/backend/services/session/service/acp/acp-session-config-options.test.d.ts +2 -0
  222. package/dist/src/backend/services/session/service/acp/acp-session-config-options.test.d.ts.map +1 -0
  223. package/dist/src/backend/services/session/service/acp/acp-session-config-options.test.js +121 -0
  224. package/dist/src/backend/services/session/service/acp/acp-session-config-options.test.js.map +1 -0
  225. package/dist/src/backend/services/session/service/acp/acp-session-negotiation.integration.test.js +1 -1
  226. package/dist/src/backend/services/session/service/acp/acp-session-negotiation.integration.test.js.map +1 -1
  227. package/dist/src/backend/services/session/service/acp/claude-model-catalog-loader.d.ts +7 -0
  228. package/dist/src/backend/services/session/service/acp/claude-model-catalog-loader.d.ts.map +1 -0
  229. package/dist/src/backend/services/session/service/acp/claude-model-catalog-loader.js +160 -0
  230. package/dist/src/backend/services/session/service/acp/claude-model-catalog-loader.js.map +1 -0
  231. package/dist/src/backend/services/session/service/acp/claude-model-catalog-loader.test.d.ts +2 -0
  232. package/dist/src/backend/services/session/service/acp/claude-model-catalog-loader.test.d.ts.map +1 -0
  233. package/dist/src/backend/services/session/service/acp/claude-model-catalog-loader.test.js +260 -0
  234. package/dist/src/backend/services/session/service/acp/claude-model-catalog-loader.test.js.map +1 -0
  235. package/dist/src/backend/services/session/service/acp/claude-model-options.d.ts +5 -0
  236. package/dist/src/backend/services/session/service/acp/claude-model-options.d.ts.map +1 -0
  237. package/dist/src/backend/services/session/service/acp/claude-model-options.js +25 -0
  238. package/dist/src/backend/services/session/service/acp/claude-model-options.js.map +1 -0
  239. package/dist/src/backend/services/session/service/acp/claude-model-options.test.d.ts +2 -0
  240. package/dist/src/backend/services/session/service/acp/claude-model-options.test.d.ts.map +1 -0
  241. package/dist/src/backend/services/session/service/acp/claude-model-options.test.js +49 -0
  242. package/dist/src/backend/services/session/service/acp/claude-model-options.test.js.map +1 -0
  243. package/dist/src/backend/services/session/service/acp/codex-app-server-adapter/adapter-state.d.ts +2 -0
  244. package/dist/src/backend/services/session/service/acp/codex-app-server-adapter/adapter-state.d.ts.map +1 -1
  245. package/dist/src/backend/services/session/service/acp/codex-app-server-adapter/codex-app-server-acp-adapter.d.ts +2 -0
  246. package/dist/src/backend/services/session/service/acp/codex-app-server-adapter/codex-app-server-acp-adapter.d.ts.map +1 -1
  247. package/dist/src/backend/services/session/service/acp/codex-app-server-adapter/codex-app-server-acp-adapter.js +64 -1
  248. package/dist/src/backend/services/session/service/acp/codex-app-server-adapter/codex-app-server-acp-adapter.js.map +1 -1
  249. package/dist/src/backend/services/session/service/acp/codex-app-server-adapter/codex-app-server-acp-adapter.manual.integration.test.js +169 -0
  250. package/dist/src/backend/services/session/service/acp/codex-app-server-adapter/codex-app-server-acp-adapter.manual.integration.test.js.map +1 -1
  251. package/dist/src/backend/services/session/service/acp/codex-app-server-adapter/codex-app-server-acp-adapter.test.js +251 -0
  252. package/dist/src/backend/services/session/service/acp/codex-app-server-adapter/codex-app-server-acp-adapter.test.js.map +1 -1
  253. package/dist/src/backend/services/session/service/acp/codex-app-server-adapter/codex-subagent-controller.d.ts +38 -0
  254. package/dist/src/backend/services/session/service/acp/codex-app-server-adapter/codex-subagent-controller.d.ts.map +1 -0
  255. package/dist/src/backend/services/session/service/acp/codex-app-server-adapter/codex-subagent-controller.js +287 -0
  256. package/dist/src/backend/services/session/service/acp/codex-app-server-adapter/codex-subagent-controller.js.map +1 -0
  257. package/dist/src/backend/services/session/service/acp/codex-app-server-adapter/codex-subagent-controller.test.d.ts +2 -0
  258. package/dist/src/backend/services/session/service/acp/codex-app-server-adapter/codex-subagent-controller.test.d.ts.map +1 -0
  259. package/dist/src/backend/services/session/service/acp/codex-app-server-adapter/codex-subagent-controller.test.js +648 -0
  260. package/dist/src/backend/services/session/service/acp/codex-app-server-adapter/codex-subagent-controller.test.js.map +1 -0
  261. package/dist/src/backend/services/session/service/acp/codex-app-server-adapter/codex-subagent-mapper.d.ts +12 -0
  262. package/dist/src/backend/services/session/service/acp/codex-app-server-adapter/codex-subagent-mapper.d.ts.map +1 -0
  263. package/dist/src/backend/services/session/service/acp/codex-app-server-adapter/codex-subagent-mapper.js +51 -0
  264. package/dist/src/backend/services/session/service/acp/codex-app-server-adapter/codex-subagent-mapper.js.map +1 -0
  265. package/dist/src/backend/services/session/service/acp/codex-app-server-adapter/codex-subagent-mapper.test.d.ts +2 -0
  266. package/dist/src/backend/services/session/service/acp/codex-app-server-adapter/codex-subagent-mapper.test.d.ts.map +1 -0
  267. package/dist/src/backend/services/session/service/acp/codex-app-server-adapter/codex-subagent-mapper.test.js +85 -0
  268. package/dist/src/backend/services/session/service/acp/codex-app-server-adapter/codex-subagent-mapper.test.js.map +1 -0
  269. package/dist/src/backend/services/session/service/acp/codex-app-server-adapter/codex-zod.d.ts +154 -19
  270. package/dist/src/backend/services/session/service/acp/codex-app-server-adapter/codex-zod.d.ts.map +1 -1
  271. package/dist/src/backend/services/session/service/acp/codex-app-server-adapter/codex-zod.js +73 -4
  272. package/dist/src/backend/services/session/service/acp/codex-app-server-adapter/codex-zod.js.map +1 -1
  273. package/dist/src/backend/services/session/service/acp/codex-app-server-adapter/codex-zod.test.js +86 -1
  274. package/dist/src/backend/services/session/service/acp/codex-app-server-adapter/codex-zod.test.js.map +1 -1
  275. package/dist/src/backend/services/session/service/acp/codex-app-server-adapter/stream-event-handler.d.ts +7 -0
  276. package/dist/src/backend/services/session/service/acp/codex-app-server-adapter/stream-event-handler.d.ts.map +1 -1
  277. package/dist/src/backend/services/session/service/acp/codex-app-server-adapter/stream-event-handler.js +85 -16
  278. package/dist/src/backend/services/session/service/acp/codex-app-server-adapter/stream-event-handler.js.map +1 -1
  279. package/dist/src/backend/services/session/service/acp/codex-app-server-adapter/stream-event-handler.test.js +356 -0
  280. package/dist/src/backend/services/session/service/acp/codex-app-server-adapter/stream-event-handler.test.js.map +1 -1
  281. package/dist/src/backend/services/session/service/acp/index.d.ts +2 -1
  282. package/dist/src/backend/services/session/service/acp/index.d.ts.map +1 -1
  283. package/dist/src/backend/services/session/service/acp/index.js +1 -0
  284. package/dist/src/backend/services/session/service/acp/index.js.map +1 -1
  285. package/dist/src/backend/services/session/service/acp/types.d.ts +1 -0
  286. package/dist/src/backend/services/session/service/acp/types.d.ts.map +1 -1
  287. package/dist/src/backend/services/session/service/chat/chat-event-forwarder.service.d.ts +3 -0
  288. package/dist/src/backend/services/session/service/chat/chat-event-forwarder.service.d.ts.map +1 -1
  289. package/dist/src/backend/services/session/service/chat/chat-event-forwarder.service.js +44 -2
  290. package/dist/src/backend/services/session/service/chat/chat-event-forwarder.service.js.map +1 -1
  291. package/dist/src/backend/services/session/service/chat/chat-event-forwarder.service.test.d.ts +2 -0
  292. package/dist/src/backend/services/session/service/chat/chat-event-forwarder.service.test.d.ts.map +1 -0
  293. package/dist/src/backend/services/session/service/chat/chat-event-forwarder.service.test.js +108 -0
  294. package/dist/src/backend/services/session/service/chat/chat-event-forwarder.service.test.js.map +1 -0
  295. package/dist/src/backend/services/session/service/chat/chat-message-handlers/attachment-processing.test.js +21 -0
  296. package/dist/src/backend/services/session/service/chat/chat-message-handlers/attachment-processing.test.js.map +1 -1
  297. package/dist/src/backend/services/session/service/chat/chat-message-handlers/handlers/load-session.handler.d.ts.map +1 -1
  298. package/dist/src/backend/services/session/service/chat/chat-message-handlers/handlers/load-session.handler.js +3 -279
  299. package/dist/src/backend/services/session/service/chat/chat-message-handlers/handlers/load-session.handler.js.map +1 -1
  300. package/dist/src/backend/services/session/service/chat/chat-message-handlers/handlers/load-session.handler.test.js +154 -7
  301. package/dist/src/backend/services/session/service/chat/chat-message-handlers/handlers/load-session.handler.test.js.map +1 -1
  302. package/dist/src/backend/services/session/service/chat/chat-message-handlers/handlers/queue-message.handler.d.ts.map +1 -1
  303. package/dist/src/backend/services/session/service/chat/chat-message-handlers/handlers/queue-message.handler.js +4 -0
  304. package/dist/src/backend/services/session/service/chat/chat-message-handlers/handlers/queue-message.handler.js.map +1 -1
  305. package/dist/src/backend/services/session/service/chat/chat-message-handlers/handlers/queue-message.handler.test.js +24 -0
  306. package/dist/src/backend/services/session/service/chat/chat-message-handlers/handlers/queue-message.handler.test.js.map +1 -1
  307. package/dist/src/backend/services/session/service/chat/chat-message-handlers/handlers/stop.handler.js +1 -1
  308. package/dist/src/backend/services/session/service/chat/chat-message-handlers/handlers/stop.handler.js.map +1 -1
  309. package/dist/src/backend/services/session/service/chat/chat-message-handlers/handlers/stop.handler.test.js +1 -1
  310. package/dist/src/backend/services/session/service/chat/chat-message-handlers/handlers/stop.handler.test.js.map +1 -1
  311. package/dist/src/backend/services/session/service/chat/chat-message-handlers/image-format-validation.js +1 -1
  312. package/dist/src/backend/services/session/service/chat/chat-message-handlers/image-format-validation.js.map +1 -1
  313. package/dist/src/backend/services/session/service/chat/chat-message-handlers/utils.d.ts.map +1 -1
  314. package/dist/src/backend/services/session/service/chat/chat-message-handlers/utils.js +1 -0
  315. package/dist/src/backend/services/session/service/chat/chat-message-handlers/utils.js.map +1 -1
  316. package/dist/src/backend/services/session/service/chat/chat-message-handlers/utils.test.js +17 -0
  317. package/dist/src/backend/services/session/service/chat/chat-message-handlers/utils.test.js.map +1 -1
  318. package/dist/src/backend/services/session/service/chat/chat-message-handlers.service.d.ts +2 -0
  319. package/dist/src/backend/services/session/service/chat/chat-message-handlers.service.d.ts.map +1 -1
  320. package/dist/src/backend/services/session/service/chat/chat-message-handlers.service.js +41 -9
  321. package/dist/src/backend/services/session/service/chat/chat-message-handlers.service.js.map +1 -1
  322. package/dist/src/backend/services/session/service/chat/chat-message-handlers.service.test.js +180 -2
  323. package/dist/src/backend/services/session/service/chat/chat-message-handlers.service.test.js.map +1 -1
  324. package/dist/src/backend/services/session/service/data/session-provider-resolver.service.test.js +6 -0
  325. package/dist/src/backend/services/session/service/data/session-provider-resolver.service.test.js.map +1 -1
  326. package/dist/src/backend/services/session/service/index.d.ts +5 -4
  327. package/dist/src/backend/services/session/service/index.d.ts.map +1 -1
  328. package/dist/src/backend/services/session/service/index.js +4 -3
  329. package/dist/src/backend/services/session/service/index.js.map +1 -1
  330. package/dist/src/backend/services/session/service/lifecycle/acp-event-processor.d.ts +6 -1
  331. package/dist/src/backend/services/session/service/lifecycle/acp-event-processor.d.ts.map +1 -1
  332. package/dist/src/backend/services/session/service/lifecycle/acp-event-processor.js +59 -30
  333. package/dist/src/backend/services/session/service/lifecycle/acp-event-processor.js.map +1 -1
  334. package/dist/src/backend/services/session/service/lifecycle/acp-event-processor.subagents.test.d.ts +2 -0
  335. package/dist/src/backend/services/session/service/lifecycle/acp-event-processor.subagents.test.d.ts.map +1 -0
  336. package/dist/src/backend/services/session/service/lifecycle/acp-event-processor.subagents.test.js +70 -0
  337. package/dist/src/backend/services/session/service/lifecycle/acp-event-processor.subagents.test.js.map +1 -0
  338. package/dist/src/backend/services/session/service/lifecycle/acp-event-processor.text-streaming.test.js +28 -0
  339. package/dist/src/backend/services/session/service/lifecycle/acp-event-processor.text-streaming.test.js.map +1 -1
  340. package/dist/src/backend/services/session/service/lifecycle/session-lifecycle-event.service.d.ts +26 -0
  341. package/dist/src/backend/services/session/service/lifecycle/session-lifecycle-event.service.d.ts.map +1 -0
  342. package/dist/src/backend/services/session/service/lifecycle/session-lifecycle-event.service.js +84 -0
  343. package/dist/src/backend/services/session/service/lifecycle/session-lifecycle-event.service.js.map +1 -0
  344. package/dist/src/backend/services/session/service/lifecycle/session-lifecycle-event.service.test.d.ts +2 -0
  345. package/dist/src/backend/services/session/service/lifecycle/session-lifecycle-event.service.test.d.ts.map +1 -0
  346. package/dist/src/backend/services/session/service/lifecycle/session-lifecycle-event.service.test.js +283 -0
  347. package/dist/src/backend/services/session/service/lifecycle/session-lifecycle-event.service.test.js.map +1 -0
  348. package/dist/src/backend/services/session/service/lifecycle/session-provider-history-hydrator.d.ts +10 -0
  349. package/dist/src/backend/services/session/service/lifecycle/session-provider-history-hydrator.d.ts.map +1 -0
  350. package/dist/src/backend/services/session/service/lifecycle/session-provider-history-hydrator.js +299 -0
  351. package/dist/src/backend/services/session/service/lifecycle/session-provider-history-hydrator.js.map +1 -0
  352. package/dist/src/backend/services/session/service/lifecycle/session-services.d.ts +2 -0
  353. package/dist/src/backend/services/session/service/lifecycle/session-services.d.ts.map +1 -1
  354. package/dist/src/backend/services/session/service/lifecycle/session-services.js +11 -0
  355. package/dist/src/backend/services/session/service/lifecycle/session-services.js.map +1 -1
  356. package/dist/src/backend/services/session/service/lifecycle/session.config.service.d.ts +1 -0
  357. package/dist/src/backend/services/session/service/lifecycle/session.config.service.d.ts.map +1 -1
  358. package/dist/src/backend/services/session/service/lifecycle/session.config.service.js +25 -4
  359. package/dist/src/backend/services/session/service/lifecycle/session.config.service.js.map +1 -1
  360. package/dist/src/backend/services/session/service/lifecycle/session.config.service.test.js +100 -5
  361. package/dist/src/backend/services/session/service/lifecycle/session.config.service.test.js.map +1 -1
  362. package/dist/src/backend/services/session/service/lifecycle/session.error-message.d.ts +2 -0
  363. package/dist/src/backend/services/session/service/lifecycle/session.error-message.d.ts.map +1 -1
  364. package/dist/src/backend/services/session/service/lifecycle/session.error-message.js +21 -0
  365. package/dist/src/backend/services/session/service/lifecycle/session.error-message.js.map +1 -1
  366. package/dist/src/backend/services/session/service/lifecycle/session.error-message.test.js +26 -1
  367. package/dist/src/backend/services/session/service/lifecycle/session.error-message.test.js.map +1 -1
  368. package/dist/src/backend/services/session/service/lifecycle/session.lifecycle.service.d.ts +35 -2
  369. package/dist/src/backend/services/session/service/lifecycle/session.lifecycle.service.d.ts.map +1 -1
  370. package/dist/src/backend/services/session/service/lifecycle/session.lifecycle.service.js +427 -156
  371. package/dist/src/backend/services/session/service/lifecycle/session.lifecycle.service.js.map +1 -1
  372. package/dist/src/backend/services/session/service/lifecycle/session.lifecycle.service.test.js +684 -15
  373. package/dist/src/backend/services/session/service/lifecycle/session.lifecycle.service.test.js.map +1 -1
  374. package/dist/src/backend/services/session/service/lifecycle/session.prompt.service.test.js +206 -5
  375. package/dist/src/backend/services/session/service/lifecycle/session.prompt.service.test.js.map +1 -1
  376. package/dist/src/backend/services/session/service/lifecycle/session.service.d.ts +15 -2
  377. package/dist/src/backend/services/session/service/lifecycle/session.service.d.ts.map +1 -1
  378. package/dist/src/backend/services/session/service/lifecycle/session.service.js +87 -14
  379. package/dist/src/backend/services/session/service/lifecycle/session.service.js.map +1 -1
  380. package/dist/src/backend/services/session/service/lifecycle/session.service.test.js +46 -7
  381. package/dist/src/backend/services/session/service/lifecycle/session.service.test.js.map +1 -1
  382. package/dist/src/backend/services/session/service/session-domain.service.d.ts +1 -0
  383. package/dist/src/backend/services/session/service/session-domain.service.d.ts.map +1 -1
  384. package/dist/src/backend/services/session/service/session-domain.service.js +10 -0
  385. package/dist/src/backend/services/session/service/session-domain.service.js.map +1 -1
  386. package/dist/src/backend/services/session/service/session-domain.service.test.js +58 -0
  387. package/dist/src/backend/services/session/service/session-domain.service.test.js.map +1 -1
  388. package/dist/src/backend/services/session/service/store/session-lifecycle-transcript.d.ts +6 -0
  389. package/dist/src/backend/services/session/service/store/session-lifecycle-transcript.d.ts.map +1 -0
  390. package/dist/src/backend/services/session/service/store/session-lifecycle-transcript.js +114 -0
  391. package/dist/src/backend/services/session/service/store/session-lifecycle-transcript.js.map +1 -0
  392. package/dist/src/backend/services/session/service/store/session-lifecycle-transcript.test.d.ts +2 -0
  393. package/dist/src/backend/services/session/service/store/session-lifecycle-transcript.test.d.ts.map +1 -0
  394. package/dist/src/backend/services/session/service/store/session-lifecycle-transcript.test.js +129 -0
  395. package/dist/src/backend/services/session/service/store/session-lifecycle-transcript.test.js.map +1 -0
  396. package/dist/src/backend/services/session/service/store/session-store.types.d.ts +1 -1
  397. package/dist/src/backend/services/session/service/store/session-store.types.d.ts.map +1 -1
  398. package/dist/src/backend/services/session/service/store/session-transcript.d.ts.map +1 -1
  399. package/dist/src/backend/services/session/service/store/session-transcript.js +7 -4
  400. package/dist/src/backend/services/session/service/store/session-transcript.js.map +1 -1
  401. package/dist/src/backend/services/session/service/voice/voice-mode-instructions.d.ts +2 -0
  402. package/dist/src/backend/services/session/service/voice/voice-mode-instructions.d.ts.map +1 -0
  403. package/dist/src/backend/services/session/service/voice/voice-mode-instructions.js +5 -0
  404. package/dist/src/backend/services/session/service/voice/voice-mode-instructions.js.map +1 -0
  405. package/dist/src/backend/services/session/service/voice/voice-narration.service.d.ts +28 -0
  406. package/dist/src/backend/services/session/service/voice/voice-narration.service.d.ts.map +1 -0
  407. package/dist/src/backend/services/session/service/voice/voice-narration.service.js +431 -0
  408. package/dist/src/backend/services/session/service/voice/voice-narration.service.js.map +1 -0
  409. package/dist/src/backend/services/session/service/voice/voice-narration.service.test.d.ts +2 -0
  410. package/dist/src/backend/services/session/service/voice/voice-narration.service.test.d.ts.map +1 -0
  411. package/dist/src/backend/services/session/service/voice/voice-narration.service.test.js +536 -0
  412. package/dist/src/backend/services/session/service/voice/voice-narration.service.test.js.map +1 -0
  413. package/dist/src/backend/services/settings/resources/user-settings.accessor.d.ts +6 -0
  414. package/dist/src/backend/services/settings/resources/user-settings.accessor.d.ts.map +1 -1
  415. package/dist/src/backend/services/settings/resources/user-settings.accessor.js +46 -17
  416. package/dist/src/backend/services/settings/resources/user-settings.accessor.js.map +1 -1
  417. package/dist/src/backend/services/settings/service/user-settings.service.d.ts +6 -0
  418. package/dist/src/backend/services/settings/service/user-settings.service.d.ts.map +1 -1
  419. package/dist/src/backend/services/settings/service/user-settings.service.js.map +1 -1
  420. package/dist/src/backend/services/workspace/service/lifecycle/creation.service.d.ts.map +1 -1
  421. package/dist/src/backend/services/workspace/service/lifecycle/creation.service.js +1 -0
  422. package/dist/src/backend/services/workspace/service/lifecycle/creation.service.js.map +1 -1
  423. package/dist/src/backend/services/workspace/service/lifecycle/creation.service.test.js +24 -0
  424. package/dist/src/backend/services/workspace/service/lifecycle/creation.service.test.js.map +1 -1
  425. package/dist/src/backend/services/workspace/service/worktree/worktree-lifecycle.service.d.ts +1 -0
  426. package/dist/src/backend/services/workspace/service/worktree/worktree-lifecycle.service.d.ts.map +1 -1
  427. package/dist/src/backend/services/workspace/service/worktree/worktree-lifecycle.service.js +23 -12
  428. package/dist/src/backend/services/workspace/service/worktree/worktree-lifecycle.service.js.map +1 -1
  429. package/dist/src/backend/services/workspace/service/worktree/worktree-lifecycle.service.test.js +59 -4
  430. package/dist/src/backend/services/workspace/service/worktree/worktree-lifecycle.service.test.js.map +1 -1
  431. package/dist/src/backend/trpc/admin.trpc.d.ts +10 -0
  432. package/dist/src/backend/trpc/admin.trpc.d.ts.map +1 -1
  433. package/dist/src/backend/trpc/index.d.ts +631 -4
  434. package/dist/src/backend/trpc/index.d.ts.map +1 -1
  435. package/dist/src/backend/trpc/index.js +2 -0
  436. package/dist/src/backend/trpc/index.js.map +1 -1
  437. package/dist/src/backend/trpc/project.router.test.js +19 -0
  438. package/dist/src/backend/trpc/project.router.test.js.map +1 -1
  439. package/dist/src/backend/trpc/project.trpc.js +1 -1
  440. package/dist/src/backend/trpc/project.trpc.js.map +1 -1
  441. package/dist/src/backend/trpc/session.router.test.js +204 -2
  442. package/dist/src/backend/trpc/session.router.test.js.map +1 -1
  443. package/dist/src/backend/trpc/session.trpc.d.ts +539 -0
  444. package/dist/src/backend/trpc/session.trpc.d.ts.map +1 -1
  445. package/dist/src/backend/trpc/session.trpc.js +57 -0
  446. package/dist/src/backend/trpc/session.trpc.js.map +1 -1
  447. package/dist/src/backend/trpc/user-settings.router.test.js +117 -6
  448. package/dist/src/backend/trpc/user-settings.router.test.js.map +1 -1
  449. package/dist/src/backend/trpc/user-settings.trpc.d.ts +16 -4
  450. package/dist/src/backend/trpc/user-settings.trpc.d.ts.map +1 -1
  451. package/dist/src/backend/trpc/user-settings.trpc.js +25 -2
  452. package/dist/src/backend/trpc/user-settings.trpc.js.map +1 -1
  453. package/dist/src/backend/trpc/voice.trpc.d.ts +67 -0
  454. package/dist/src/backend/trpc/voice.trpc.d.ts.map +1 -0
  455. package/dist/src/backend/trpc/voice.trpc.js +156 -0
  456. package/dist/src/backend/trpc/voice.trpc.js.map +1 -0
  457. package/dist/src/backend/trpc/voice.trpc.test.d.ts +2 -0
  458. package/dist/src/backend/trpc/voice.trpc.test.d.ts.map +1 -0
  459. package/dist/src/backend/trpc/voice.trpc.test.js +259 -0
  460. package/dist/src/backend/trpc/voice.trpc.test.js.map +1 -0
  461. package/dist/src/cli/runtime-utils.d.ts.map +1 -1
  462. package/dist/src/cli/runtime-utils.js +4 -0
  463. package/dist/src/cli/runtime-utils.js.map +1 -1
  464. package/dist/src/cli/runtime-utils.test.js +7 -0
  465. package/dist/src/cli/runtime-utils.test.js.map +1 -1
  466. package/dist/src/shared/acp-protocol/index.d.ts +2 -0
  467. package/dist/src/shared/acp-protocol/index.d.ts.map +1 -1
  468. package/dist/src/shared/acp-protocol/index.js +2 -0
  469. package/dist/src/shared/acp-protocol/index.js.map +1 -1
  470. package/dist/src/shared/acp-protocol/protocol/messages.d.ts +18 -2
  471. package/dist/src/shared/acp-protocol/protocol/messages.d.ts.map +1 -1
  472. package/dist/src/shared/acp-protocol/protocol/messages.js +1 -0
  473. package/dist/src/shared/acp-protocol/protocol/messages.js.map +1 -1
  474. package/dist/src/shared/acp-protocol/protocol/queued.d.ts +1 -0
  475. package/dist/src/shared/acp-protocol/protocol/queued.d.ts.map +1 -1
  476. package/dist/src/shared/acp-protocol/protocol/renderer-window.d.ts.map +1 -1
  477. package/dist/src/shared/acp-protocol/protocol/renderer-window.js +3 -0
  478. package/dist/src/shared/acp-protocol/protocol/renderer-window.js.map +1 -1
  479. package/dist/src/shared/acp-protocol/protocol/websocket.d.ts +7 -0
  480. package/dist/src/shared/acp-protocol/protocol/websocket.d.ts.map +1 -1
  481. package/dist/src/shared/acp-protocol/protocol/websocket.js +1 -0
  482. package/dist/src/shared/acp-protocol/protocol/websocket.js.map +1 -1
  483. package/dist/src/shared/acp-protocol/session-update-translator.d.ts +39 -0
  484. package/dist/src/shared/acp-protocol/session-update-translator.d.ts.map +1 -0
  485. package/dist/src/shared/acp-protocol/session-update-translator.js +356 -0
  486. package/dist/src/shared/acp-protocol/session-update-translator.js.map +1 -0
  487. package/dist/src/shared/acp-protocol/subagents.d.ts +1175 -0
  488. package/dist/src/shared/acp-protocol/subagents.d.ts.map +1 -0
  489. package/dist/src/shared/acp-protocol/subagents.js +259 -0
  490. package/dist/src/shared/acp-protocol/subagents.js.map +1 -0
  491. package/dist/src/shared/core/enums.d.ts +16 -0
  492. package/dist/src/shared/core/enums.d.ts.map +1 -1
  493. package/dist/src/shared/core/enums.js +14 -0
  494. package/dist/src/shared/core/enums.js.map +1 -1
  495. package/dist/src/shared/core/index.d.ts +1 -1
  496. package/dist/src/shared/core/index.d.ts.map +1 -1
  497. package/dist/src/shared/core/index.js +1 -1
  498. package/dist/src/shared/core/index.js.map +1 -1
  499. package/dist/src/shared/deepgram-voices.d.ts +12 -0
  500. package/dist/src/shared/deepgram-voices.d.ts.map +1 -0
  501. package/dist/src/shared/deepgram-voices.js +51 -0
  502. package/dist/src/shared/deepgram-voices.js.map +1 -0
  503. package/dist/src/shared/issue-start-prompt.d.ts.map +1 -1
  504. package/dist/src/shared/issue-start-prompt.js +8 -3
  505. package/dist/src/shared/issue-start-prompt.js.map +1 -1
  506. package/dist/src/shared/schemas/export-data.schema.d.ts +10 -0
  507. package/dist/src/shared/schemas/export-data.schema.d.ts.map +1 -1
  508. package/dist/src/shared/schemas/export-data.schema.js +19 -0
  509. package/dist/src/shared/schemas/export-data.schema.js.map +1 -1
  510. package/dist/src/shared/voice-vad.d.ts +11 -0
  511. package/dist/src/shared/voice-vad.d.ts.map +1 -0
  512. package/dist/src/shared/voice-vad.js +19 -0
  513. package/dist/src/shared/voice-vad.js.map +1 -0
  514. package/dist/src/shared/websocket/chat-message.schema.d.ts +1 -0
  515. package/dist/src/shared/websocket/chat-message.schema.d.ts.map +1 -1
  516. package/dist/src/shared/websocket/chat-message.schema.js +1 -0
  517. package/dist/src/shared/websocket/chat-message.schema.js.map +1 -1
  518. package/dist/src/shared/websocket/voice-message.schema.d.ts +25 -0
  519. package/dist/src/shared/websocket/voice-message.schema.d.ts.map +1 -0
  520. package/dist/src/shared/websocket/voice-message.schema.js +14 -0
  521. package/dist/src/shared/websocket/voice-message.schema.js.map +1 -0
  522. package/dist/src/shared/workspace-snapshot.d.ts +5 -5
  523. package/dist/src/test-utils/application-graph.d.ts.map +1 -1
  524. package/dist/src/test-utils/application-graph.js +10 -2
  525. package/dist/src/test-utils/application-graph.js.map +1 -1
  526. package/dist/tsconfig.backend.tsbuildinfo +1 -1
  527. package/package.json +6 -6
  528. package/prisma/migrations/20260730140000_add_session_lifecycle_events/migration.sql +20 -0
  529. package/prisma/migrations/20260802152257_add_voice_mode_settings/migration.sql +34 -0
  530. package/prisma/migrations/20260802200414_add_voice_tts_model_and_speed/migration.sql +36 -0
  531. package/prisma/migrations/20260807222320_voice_vad_aggressiveness_settings/migration.sql +38 -0
  532. package/prisma/schema.prisma +41 -0
  533. package/dist/client/assets/architectureDiagram-ZJ3FMSHR-Cz-V9xRh.js +0 -36
  534. package/dist/client/assets/channel-DVMObwOq.js +0 -1
  535. package/dist/client/assets/classDiagram-OUVF2IWQ-wHGXcbfd.js +0 -1
  536. package/dist/client/assets/classDiagram-v2-EOCWNBFH-wHGXcbfd.js +0 -1
  537. package/dist/client/assets/diagram-WEI45ONY-DF667tXW.js +0 -41
  538. package/dist/client/assets/index-CpvT2M5m.js +0 -689
  539. package/dist/client/assets/index-CwfNzqG5.css +0 -1
  540. package/dist/client/assets/stateDiagram-v2-6OUMAXLB-CGxDgHho.js +0 -1
  541. package/dist/client/assets/swimlanesDiagram-G3AALYLV-1u_1g3u4.js +0 -8
@@ -1,9 +1,19 @@
1
1
  import { beforeEach, describe, expect, it, vi } from 'vitest';
2
+ import { AcpBrowseSessionUnavailableError } from '../../../../../backend/services/session/service/acp/acp-runtime-manager.js';
3
+ import { SessionDomainService } from '../../../../../backend/services/session/service/session-domain.service.js';
4
+ import { sessionEventBus } from '../../../../../backend/services/session/service/session-event-bus.js';
2
5
  import { userSettingsService } from '../../../../../backend/services/settings/index.js';
3
- import { workspaceNotificationService } from '../../../../../backend/services/workspace/index.js';
6
+ import { workspaceDataService, workspaceNotificationService } from '../../../../../backend/services/workspace/index.js';
4
7
  import { SessionStatus } from '../../../../../shared/core/index.js';
5
8
  import { unsafeCoerce } from '../../../../../test-utils/unsafe-coerce.js';
6
9
  import { SessionLifecycleService } from './session.lifecycle.service.js';
10
+ import { SessionLifecycleEventService } from './session-lifecycle-event.service.js';
11
+ vi.mock('./closed-session-persistence.service', () => ({
12
+ closedSessionPersistenceService: {
13
+ persistClosedSession: vi.fn(async () => undefined),
14
+ },
15
+ }));
16
+ import { closedSessionPersistenceService } from './closed-session-persistence.service.js';
7
17
  vi.mock('@/backend/services/logger.service', () => ({
8
18
  createLogger: () => ({
9
19
  debug: vi.fn(),
@@ -11,6 +21,7 @@ vi.mock('@/backend/services/logger.service', () => ({
11
21
  warn: vi.fn(),
12
22
  error: vi.fn(),
13
23
  }),
24
+ getCurrentProcessEnv: () => ({ ...process.env }),
14
25
  }));
15
26
  vi.mock('@/backend/services/workspace', () => ({
16
27
  workspaceDataService: { findById: vi.fn() },
@@ -49,6 +60,7 @@ function createLifecycleService(options) {
49
60
  promptTurnCompletionService: {},
50
61
  retryService: {},
51
62
  sendSessionMessage: vi.fn(async () => undefined),
63
+ lifecycleEventService: { hydrate: vi.fn(async () => undefined) },
52
64
  });
53
65
  service.configure({
54
66
  workspace: {
@@ -64,16 +76,31 @@ function createLifecycleService(options) {
64
76
  async function deliverPendingChildNotifications(service, sessionId = 'session-1', workspaceId = 'workspace-1') {
65
77
  return await service.deliverPendingChildNotifications(sessionId, workspaceId);
66
78
  }
79
+ function getStopGenerations(service) {
80
+ return service.stopGenerations;
81
+ }
82
+ function createDeferred() {
83
+ let resolvePromise;
84
+ let rejectPromise;
85
+ const promise = new Promise((resolve, reject) => {
86
+ resolvePromise = resolve;
87
+ rejectPromise = reject;
88
+ });
89
+ return { promise, resolve: resolvePromise, reject: rejectPromise };
90
+ }
67
91
  function createStoppableLifecycleService() {
68
92
  const repository = {
69
93
  getSessionsByWorkspaceId: vi.fn(async () => [
70
94
  { id: 'session-running', status: SessionStatus.RUNNING },
71
95
  { id: 'session-runtime-only', status: SessionStatus.COMPLETED },
96
+ { id: 'session-browse-only', status: SessionStatus.IDLE },
72
97
  { id: 'session-idle', status: SessionStatus.IDLE },
73
98
  ]),
74
99
  };
75
100
  const runtimeManager = {
76
101
  isSessionRunning: vi.fn((sessionId) => sessionId === 'session-runtime-only'),
102
+ getBrowseClient: vi.fn(() => undefined),
103
+ isBrowseOnlySession: vi.fn((sessionId) => sessionId === 'session-browse-only'),
77
104
  };
78
105
  const service = new SessionLifecycleService({
79
106
  repository: repository,
@@ -86,11 +113,78 @@ function createStoppableLifecycleService() {
86
113
  promptTurnCompletionService: {},
87
114
  retryService: {},
88
115
  sendSessionMessage: vi.fn(async () => undefined),
116
+ lifecycleEventService: { hydrate: vi.fn(async () => undefined) },
89
117
  });
90
- const stopSession = vi.fn((_sessionId) => Promise.resolve());
118
+ const stopSession = vi.fn((_sessionId, _options) => Promise.resolve());
91
119
  service.stopSession = stopSession;
92
120
  return { service, repository, runtimeManager, stopSession };
93
121
  }
122
+ function createStopReasonLifecycleService(options) {
123
+ const session = {
124
+ id: 'session-1',
125
+ workspaceId: 'workspace-1',
126
+ workflow: 'code',
127
+ providerProcessPid: options?.providerProcessPid ?? 4242,
128
+ };
129
+ const repository = {
130
+ getSessionById: vi.fn(async () => session),
131
+ updateSessionIfStatus: vi.fn(async () => session),
132
+ updateSession: vi.fn(async () => session),
133
+ };
134
+ const runtimeManager = {
135
+ isStopInProgress: vi.fn(() => false),
136
+ isSessionRunning: vi.fn(() => false),
137
+ isBrowseOnlySession: vi.fn(() => false),
138
+ isSessionWorking: vi.fn(() => false),
139
+ getClient: vi.fn(() => undefined),
140
+ stopClient: vi.fn(async () => undefined),
141
+ };
142
+ const lifecycleEventService = {
143
+ record: vi.fn(async (_input) => null),
144
+ hydrate: vi.fn(),
145
+ };
146
+ const service = new SessionLifecycleService(unsafeCoerce({
147
+ repository,
148
+ promptBuilder: {},
149
+ runtimeManager,
150
+ sessionDomainService: {
151
+ clearQueuedWork: vi.fn(),
152
+ getRuntimeSnapshot: vi.fn(() => ({
153
+ phase: 'idle',
154
+ processState: 'stopped',
155
+ activity: 'IDLE',
156
+ updatedAt: '2026-07-30T00:00:00.000Z',
157
+ })),
158
+ setRuntimeSnapshot: vi.fn(),
159
+ markProcessExit: vi.fn(),
160
+ clearSession: vi.fn(),
161
+ },
162
+ sessionPermissionService: { cancelPendingRequests: vi.fn() },
163
+ sessionConfigService: {},
164
+ acpEventProcessor: {
165
+ createRuntimeEventHandler: vi.fn(() => ({})),
166
+ clearStreamingState: vi.fn(),
167
+ clearReplaySuppression: vi.fn(),
168
+ clearSessionContext: vi.fn(),
169
+ clearSessionState: vi.fn(),
170
+ finalizeOrphanedToolCalls: vi.fn(),
171
+ },
172
+ promptTurnCompletionService: { clearSession: vi.fn() },
173
+ retryService: { run: vi.fn(async (operation) => await operation()) },
174
+ lifecycleEventService,
175
+ sendSessionMessage: vi.fn(),
176
+ }));
177
+ const createRuntimeHandlers = () => service;
178
+ const runtimeHandlers = createRuntimeHandlers().setupAcpEventHandler('session-1');
179
+ return {
180
+ service,
181
+ repository,
182
+ lifecycleEventService,
183
+ runtimeManager,
184
+ runtimeHandlers,
185
+ createRuntimeHandlers: () => createRuntimeHandlers().setupAcpEventHandler('session-1'),
186
+ };
187
+ }
94
188
  describe('SessionLifecycleService stopWorkspaceSessions', () => {
95
189
  beforeEach(() => {
96
190
  vi.clearAllMocks();
@@ -101,8 +195,12 @@ describe('SessionLifecycleService stopWorkspaceSessions', () => {
101
195
  expect(repository.getSessionsByWorkspaceId).toHaveBeenCalledWith('workspace-1');
102
196
  expect(runtimeManager.isSessionRunning).toHaveBeenCalledWith('session-runtime-only');
103
197
  expect(runtimeManager.isSessionRunning).toHaveBeenCalledWith('session-idle');
104
- expect(stopSession).toHaveBeenCalledWith('session-running');
105
- expect(stopSession).toHaveBeenCalledWith('session-runtime-only');
198
+ expect(stopSession).toHaveBeenCalledWith('session-running', { reason: 'SYSTEM_STOP' });
199
+ expect(stopSession).toHaveBeenCalledWith('session-runtime-only', { reason: 'SYSTEM_STOP' });
200
+ expect(stopSession).toHaveBeenCalledWith('session-browse-only', {
201
+ reason: 'SYSTEM_STOP',
202
+ recordLifecycleEvent: false,
203
+ });
106
204
  expect(stopSession).not.toHaveBeenCalledWith('session-idle');
107
205
  });
108
206
  it('attempts every running session and throws when any stop fails', async () => {
@@ -114,8 +212,266 @@ describe('SessionLifecycleService stopWorkspaceSessions', () => {
114
212
  return Promise.resolve();
115
213
  });
116
214
  await expect(service.stopWorkspaceSessions('workspace-1')).rejects.toThrow('Failed to stop 1 workspace session');
117
- expect(stopSession).toHaveBeenCalledWith('session-running');
118
- expect(stopSession).toHaveBeenCalledWith('session-runtime-only');
215
+ expect(stopSession).toHaveBeenCalledWith('session-running', { reason: 'SYSTEM_STOP' });
216
+ expect(stopSession).toHaveBeenCalledWith('session-runtime-only', { reason: 'SYSTEM_STOP' });
217
+ });
218
+ });
219
+ describe('SessionLifecycleService stop causes', () => {
220
+ beforeEach(() => {
221
+ vi.clearAllMocks();
222
+ });
223
+ it.each([
224
+ ['USER_STOP', 'Session stopped by you.'],
225
+ ['SESSION_CLOSED', 'Session closed by you.'],
226
+ ['WORKSPACE_ARCHIVED', 'Session stopped because the workspace was archived.'],
227
+ ['SYSTEM_STOP', 'Session stopped by the system.'],
228
+ ])('records %s before stopping the runtime', async (reason, message) => {
229
+ const { service, lifecycleEventService, runtimeManager } = createStopReasonLifecycleService();
230
+ await service.stopSession('session-1', { reason });
231
+ expect(lifecycleEventService.record).toHaveBeenCalledWith(expect.objectContaining({
232
+ workspaceId: 'workspace-1',
233
+ sessionId: 'session-1',
234
+ kind: 'SESSION_STOPPED',
235
+ reason,
236
+ message,
237
+ dedupeKey: expect.stringMatching(/^session-stop:[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/),
238
+ }));
239
+ expect(lifecycleEventService.record.mock.invocationCallOrder[0]).toBeLessThan(runtimeManager.stopClient.mock.invocationCallOrder[0]);
240
+ });
241
+ it('uses a new durable stop identity after lifecycle-service reconstruction', async () => {
242
+ const first = createStopReasonLifecycleService();
243
+ const reconstructed = createStopReasonLifecycleService();
244
+ await first.service.stopSession('session-1', { reason: 'USER_STOP' });
245
+ await reconstructed.service.stopSession('session-1', { reason: 'USER_STOP' });
246
+ const firstKey = first.lifecycleEventService.record.mock.calls[0]?.[0]?.dedupeKey;
247
+ const reconstructedKey = reconstructed.lifecycleEventService.record.mock.calls[0]?.[0]?.dedupeKey;
248
+ expect(firstKey).toMatch(/^session-stop:/);
249
+ expect(reconstructedKey).toMatch(/^session-stop:/);
250
+ expect(reconstructedKey).not.toBe(firstKey);
251
+ });
252
+ it('records an unexpected process exit once with its runtime incarnation and exit code', async () => {
253
+ const { lifecycleEventService, runtimeHandlers } = createStopReasonLifecycleService();
254
+ await runtimeHandlers.onExit?.('session-1', 1);
255
+ expect(lifecycleEventService.record).toHaveBeenCalledWith(expect.objectContaining({
256
+ kind: 'SESSION_STOPPED',
257
+ reason: 'UNEXPECTED_EXIT',
258
+ message: 'Session stopped: agent process exited unexpectedly (code 1).',
259
+ dedupeKey: expect.stringMatching(/^process-exit:[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}:1$/),
260
+ }));
261
+ });
262
+ it('records an unexpected process exit when the status update fails', async () => {
263
+ const { lifecycleEventService, repository, runtimeHandlers } = createStopReasonLifecycleService();
264
+ repository.updateSession.mockRejectedValueOnce(new Error('database write failed'));
265
+ await runtimeHandlers.onExit?.('session-1', 1);
266
+ expect(lifecycleEventService.record).toHaveBeenCalledWith(expect.objectContaining({
267
+ reason: 'UNEXPECTED_EXIT',
268
+ dedupeKey: expect.stringMatching(/^process-exit:.+:1$/),
269
+ }));
270
+ });
271
+ it('does not collapse repeated null-PID exits from separate runtime incarnations', async () => {
272
+ const { lifecycleEventService, createRuntimeHandlers } = createStopReasonLifecycleService({
273
+ providerProcessPid: null,
274
+ });
275
+ await createRuntimeHandlers().onExit?.('session-1', 1);
276
+ await createRuntimeHandlers().onExit?.('session-1', 1);
277
+ const dedupeKeys = lifecycleEventService.record.mock.calls.map(([recordInput]) => recordInput.dedupeKey);
278
+ expect(dedupeKeys).toHaveLength(2);
279
+ expect(new Set(dedupeKeys).size).toBe(2);
280
+ expect(dedupeKeys).toEqual([
281
+ expect.stringMatching(/^process-exit:.+:1$/),
282
+ expect.stringMatching(/^process-exit:.+:1$/),
283
+ ]);
284
+ });
285
+ it('does not label a deliberate stop as an unexpected exit', async () => {
286
+ const { lifecycleEventService, runtimeManager, runtimeHandlers } = createStopReasonLifecycleService();
287
+ runtimeManager.isStopInProgress.mockReturnValue(true);
288
+ await runtimeHandlers.onExit?.('session-1', 0);
289
+ expect(lifecycleEventService.record).not.toHaveBeenCalledWith(expect.objectContaining({ reason: 'UNEXPECTED_EXIT' }));
290
+ });
291
+ it('can clean up a failed startup without recording a stop event', async () => {
292
+ const { service, lifecycleEventService, runtimeManager } = createStopReasonLifecycleService();
293
+ await service.stopSession('session-1', { recordLifecycleEvent: false });
294
+ expect(lifecycleEventService.record).not.toHaveBeenCalled();
295
+ expect(runtimeManager.stopClient).toHaveBeenCalledWith('session-1');
296
+ });
297
+ });
298
+ describe('SessionLifecycleService closed transcript persistence', () => {
299
+ beforeEach(() => {
300
+ vi.clearAllMocks();
301
+ });
302
+ it('hydrates durable lifecycle rows before persisting a closed transcript', async () => {
303
+ const domain = new SessionDomainService();
304
+ const hydrateProviderHistory = vi.fn(() => {
305
+ domain.replaceTranscript('session-1', [
306
+ {
307
+ id: 'provider-user-1',
308
+ source: 'user',
309
+ text: 'Original provider conversation',
310
+ timestamp: '2026-07-30T12:00:00.000Z',
311
+ order: 0,
312
+ },
313
+ ]);
314
+ return Promise.resolve();
315
+ });
316
+ const lifecycleEventService = new SessionLifecycleEventService({
317
+ store: {
318
+ upsert: vi.fn(),
319
+ findBySessionId: vi.fn(async () => [
320
+ {
321
+ id: 'event-1',
322
+ workspaceId: 'workspace-1',
323
+ sessionId: 'session-1',
324
+ kind: 'TURN_INTERRUPTED',
325
+ reason: 'PROMPT_TIMEOUT',
326
+ message: 'Turn stopped: reached the 4-hour limit.',
327
+ dedupeKey: 'prompt-timeout',
328
+ createdAt: new Date('2026-07-30T12:22:23.353Z'),
329
+ },
330
+ ]),
331
+ },
332
+ sessionDomainService: domain,
333
+ });
334
+ const hydrateLifecycleEvents = vi.spyOn(lifecycleEventService, 'hydrate');
335
+ domain.clearSession('session-1');
336
+ const repository = {
337
+ getSessionById: vi.fn(async () => ({
338
+ id: 'session-1',
339
+ workspaceId: 'workspace-1',
340
+ name: 'Chat',
341
+ workflow: 'user',
342
+ provider: 'CLAUDE',
343
+ model: 'claude-sonnet',
344
+ createdAt: new Date('2026-07-30T12:00:00.000Z'),
345
+ })),
346
+ };
347
+ vi.mocked(workspaceDataService.findById).mockResolvedValue({
348
+ id: 'workspace-1',
349
+ worktreePath: '/tmp/worktree',
350
+ });
351
+ const service = new SessionLifecycleService(unsafeCoerce({
352
+ repository,
353
+ promptBuilder: {},
354
+ runtimeManager: {},
355
+ sessionDomainService: domain,
356
+ sessionPermissionService: {},
357
+ sessionConfigService: {},
358
+ acpEventProcessor: {},
359
+ promptTurnCompletionService: {},
360
+ retryService: {},
361
+ sendSessionMessage: vi.fn(),
362
+ lifecycleEventService,
363
+ hydrateProviderHistory,
364
+ }));
365
+ await service.persistClosedSession('session-1');
366
+ expect(closedSessionPersistenceService.persistClosedSession).toHaveBeenCalledWith(expect.objectContaining({
367
+ sessionId: 'session-1',
368
+ messages: expect.arrayContaining([
369
+ expect.objectContaining({ id: 'provider-user-1' }),
370
+ expect.objectContaining({ id: 'session-lifecycle:event-1' }),
371
+ ]),
372
+ }));
373
+ expect(hydrateProviderHistory).toHaveBeenCalledWith('session-1', expect.objectContaining({ provider: 'CLAUDE' }));
374
+ expect(hydrateProviderHistory.mock.invocationCallOrder[0]).toBeLessThan(hydrateLifecycleEvents.mock.invocationCallOrder[0]);
375
+ });
376
+ });
377
+ describe('SessionLifecycleService graceful shutdown', () => {
378
+ it('records SYSTEM_STOP for active and pending runtimes before bounded shutdown', async () => {
379
+ const runtimeManager = {
380
+ beginShutdown: vi.fn(() => ['session-active', 'session-pending', 'session-browse-only']),
381
+ stopAllClients: vi.fn(async () => undefined),
382
+ isStopInProgress: vi.fn(() => false),
383
+ isBrowseOnlySession: vi.fn((sessionId) => sessionId === 'session-browse-only'),
384
+ };
385
+ const repository = {
386
+ getSessionById: vi.fn(async (sessionId) => ({
387
+ id: sessionId,
388
+ workspaceId: 'workspace-1',
389
+ workflow: 'code',
390
+ })),
391
+ };
392
+ const lifecycleEventService = {
393
+ record: vi.fn(async () => null),
394
+ hydrate: vi.fn(async () => undefined),
395
+ };
396
+ const promptTurnCompletionService = {
397
+ clearAll: vi.fn(),
398
+ };
399
+ const service = new SessionLifecycleService(unsafeCoerce({
400
+ repository,
401
+ promptBuilder: {},
402
+ runtimeManager,
403
+ sessionDomainService: {},
404
+ sessionPermissionService: {},
405
+ sessionConfigService: {},
406
+ acpEventProcessor: { getWorkspaceId: vi.fn() },
407
+ promptTurnCompletionService,
408
+ retryService: {
409
+ run: vi.fn(async (operation) => await operation()),
410
+ },
411
+ lifecycleEventService,
412
+ hydrateProviderHistory: vi.fn(),
413
+ sendSessionMessage: vi.fn(),
414
+ }));
415
+ await service.stopAllClients(4321);
416
+ expect(runtimeManager.beginShutdown).toHaveBeenCalledOnce();
417
+ expect(lifecycleEventService.record).toHaveBeenCalledTimes(2);
418
+ expect(lifecycleEventService.record).toHaveBeenCalledWith(expect.objectContaining({
419
+ sessionId: 'session-active',
420
+ reason: 'SYSTEM_STOP',
421
+ }));
422
+ expect(lifecycleEventService.record).not.toHaveBeenCalledWith(expect.objectContaining({ sessionId: 'session-browse-only' }));
423
+ expect(lifecycleEventService.record).toHaveBeenCalledWith(expect.objectContaining({
424
+ sessionId: 'session-pending',
425
+ reason: 'SYSTEM_STOP',
426
+ }));
427
+ expect(runtimeManager.beginShutdown.mock.invocationCallOrder[0]).toBeLessThan(lifecycleEventService.record.mock.invocationCallOrder[0]);
428
+ expect(lifecycleEventService.record.mock.invocationCallOrder.at(-1)).toBeLessThan(runtimeManager.stopAllClients.mock.invocationCallOrder[0]);
429
+ expect(runtimeManager.stopAllClients).toHaveBeenCalledWith(4321);
430
+ });
431
+ it('continues runtime shutdown when lifecycle recording stalls', async () => {
432
+ vi.useFakeTimers();
433
+ try {
434
+ const runtimeManager = {
435
+ beginShutdown: vi.fn(() => ['session-active']),
436
+ stopAllClients: vi.fn(async () => undefined),
437
+ isStopInProgress: vi.fn(() => false),
438
+ isBrowseOnlySession: vi.fn(() => false),
439
+ };
440
+ const service = new SessionLifecycleService(unsafeCoerce({
441
+ repository: {
442
+ getSessionById: vi.fn(async () => ({
443
+ id: 'session-active',
444
+ workspaceId: 'workspace-1',
445
+ workflow: 'code',
446
+ })),
447
+ },
448
+ promptBuilder: {},
449
+ runtimeManager,
450
+ sessionDomainService: {},
451
+ sessionPermissionService: {},
452
+ sessionConfigService: {},
453
+ acpEventProcessor: { getWorkspaceId: vi.fn() },
454
+ promptTurnCompletionService: { clearAll: vi.fn() },
455
+ retryService: {
456
+ run: vi.fn(async (operation) => await operation()),
457
+ },
458
+ lifecycleEventService: {
459
+ record: vi.fn(() => new Promise(() => undefined)),
460
+ hydrate: vi.fn(async () => undefined),
461
+ },
462
+ hydrateProviderHistory: vi.fn(),
463
+ sendSessionMessage: vi.fn(),
464
+ }));
465
+ const shutdown = service.stopAllClients(4321);
466
+ await vi.advanceTimersByTimeAsync(999);
467
+ expect(runtimeManager.stopAllClients).not.toHaveBeenCalled();
468
+ await vi.advanceTimersByTimeAsync(1);
469
+ await shutdown;
470
+ expect(runtimeManager.stopAllClients).toHaveBeenCalledWith(4321);
471
+ }
472
+ finally {
473
+ vi.useRealTimers();
474
+ }
119
475
  });
120
476
  });
121
477
  describe('SessionLifecycleService pending workspace notifications', () => {
@@ -196,7 +552,7 @@ describe('SessionLifecycleService pending workspace notifications', () => {
196
552
  await vi.waitFor(() => {
197
553
  expect(workspaceNotificationService.listPendingForDelivery).toHaveBeenCalledWith('workspace-1');
198
554
  });
199
- service.stopGenerations.set('session-1', 1);
555
+ getStopGenerations(service).set('session-1', service.getStopGeneration('session-1') + 1);
200
556
  resolvePending([
201
557
  {
202
558
  id: 'notif-parent',
@@ -624,8 +980,8 @@ function createStartableLifecycleService(options) {
624
980
  id: 'session-1',
625
981
  workspaceId: 'workspace-1',
626
982
  workflow: 'code',
627
- provider: 'CLAUDE',
628
- providerSessionId: null,
983
+ provider: options?.provider ?? 'CLAUDE',
984
+ providerSessionId: options?.providerSessionId ?? null,
629
985
  providerMetadata: null,
630
986
  model: 'claude-sonnet',
631
987
  };
@@ -634,7 +990,7 @@ function createStartableLifecycleService(options) {
634
990
  name: 'Workspace',
635
991
  description: null,
636
992
  projectId: 'project-1',
637
- worktreePath: '/tmp/workspace',
993
+ worktreePath: options?.worktreePath === undefined ? '/tmp/workspace' : options.worktreePath,
638
994
  branchName: 'feature/test',
639
995
  isAutoGeneratedBranch: false,
640
996
  hasHadSessions: false,
@@ -643,17 +999,23 @@ function createStartableLifecycleService(options) {
643
999
  creationMetadata: null,
644
1000
  };
645
1001
  const handle = {
646
- provider: 'CLAUDE',
647
- providerSessionId: 'provider-session-1',
1002
+ provider: options?.provider ?? 'CLAUDE',
1003
+ providerSessionId: options?.providerSessionId ?? 'provider-session-1',
648
1004
  configOptions: [],
649
1005
  isPromptInFlight: false,
1006
+ getSubagentBrowseCapability: vi.fn(() => ({
1007
+ version: 1,
1008
+ list: true,
1009
+ read: true,
1010
+ notifications: true,
1011
+ })),
650
1012
  };
651
1013
  const repository = {
652
1014
  getSessionById: vi.fn(async () => session),
653
1015
  getWorkspaceById: vi.fn(async () => workspace),
654
1016
  getProjectById: vi.fn(),
655
1017
  markWorkspaceHasHadSessions: vi.fn(async () => undefined),
656
- updateSession: vi.fn(async () => undefined),
1018
+ updateSession: vi.fn(async () => session),
657
1019
  updateSessionIfStatus: vi.fn(async () => null),
658
1020
  };
659
1021
  const promptBuilder = {
@@ -667,8 +1029,12 @@ function createStartableLifecycleService(options) {
667
1029
  const runtimeManager = {
668
1030
  isStopInProgress: vi.fn(() => false),
669
1031
  isSessionRunning: vi.fn(() => false),
1032
+ isBrowseOnlySession: vi.fn(() => false),
670
1033
  getClient: vi.fn(() => undefined),
671
- getOrCreateClient: vi.fn(async () => handle),
1034
+ getBrowseClient: vi.fn(() => undefined),
1035
+ getPendingClient: vi.fn(() => undefined),
1036
+ getSubagentBrowseCapability: vi.fn(() => null),
1037
+ getOrCreateClient: vi.fn(async (_sessionId, _options) => handle),
672
1038
  stopClient: vi.fn(async () => undefined),
673
1039
  isSessionWorking: vi.fn(() => false),
674
1040
  };
@@ -685,6 +1051,7 @@ function createStartableLifecycleService(options) {
685
1051
  })),
686
1052
  clearQueuedWork: vi.fn(),
687
1053
  clearSession: vi.fn(),
1054
+ markProcessExit: vi.fn(),
688
1055
  };
689
1056
  const sessionConfigService = {
690
1057
  applyConfiguredReasoningEffort: vi.fn(async () => undefined),
@@ -718,6 +1085,7 @@ function createStartableLifecycleService(options) {
718
1085
  run: vi.fn(async (operation) => await operation()),
719
1086
  },
720
1087
  sendSessionMessage,
1088
+ lifecycleEventService: { hydrate: vi.fn(async () => undefined), record: vi.fn() },
721
1089
  });
722
1090
  service.configure({
723
1091
  workspace: {
@@ -728,19 +1096,267 @@ function createStartableLifecycleService(options) {
728
1096
  },
729
1097
  messageQueue: { tryDispatchNextMessage },
730
1098
  });
731
- service.deliverPendingChildNotifications = vi.fn(async () => options?.pendingNotificationCount ?? 0);
1099
+ const deliverPendingChildNotifications = vi.fn(async () => options?.pendingNotificationCount ?? 0);
1100
+ service.deliverPendingChildNotifications = deliverPendingChildNotifications;
732
1101
  return {
733
1102
  service,
1103
+ session,
1104
+ handle,
1105
+ repository,
734
1106
  sendSessionMessage,
735
1107
  tryDispatchNextMessage,
736
1108
  sessionConfigService,
737
1109
  runtimeManager,
1110
+ sessionDomainService,
1111
+ acpEventProcessor,
1112
+ deliverPendingChildNotifications,
738
1113
  };
739
1114
  }
740
1115
  describe('SessionLifecycleService startSession pending workspace notifications', () => {
741
1116
  beforeEach(() => {
742
1117
  vi.clearAllMocks();
743
1118
  });
1119
+ it('restores a stopped Codex session for browsing without activating it', async () => {
1120
+ const { service, repository, runtimeManager, handle, sessionDomainService, deliverPendingChildNotifications, } = createStartableLifecycleService({
1121
+ provider: 'CODEX',
1122
+ providerSessionId: 'provider-session-existing',
1123
+ pendingNotificationCount: 2,
1124
+ });
1125
+ runtimeManager.getOrCreateClient.mockImplementationOnce((_id, options) => {
1126
+ expect(options).toMatchObject({
1127
+ purpose: 'browse',
1128
+ resumeProviderSessionId: 'provider-session-existing',
1129
+ });
1130
+ runtimeManager.getSubagentBrowseCapability.mockReturnValue({
1131
+ version: 1,
1132
+ list: true,
1133
+ read: true,
1134
+ notifications: true,
1135
+ });
1136
+ return Promise.resolve(handle);
1137
+ });
1138
+ await expect(service.ensureSubagentBrowseSession('session-1')).resolves.toBe(true);
1139
+ expect(repository.markWorkspaceHasHadSessions).not.toHaveBeenCalled();
1140
+ expect(repository.updateSession).not.toHaveBeenCalledWith('session-1', expect.objectContaining({ status: SessionStatus.RUNNING }));
1141
+ expect(deliverPendingChildNotifications).not.toHaveBeenCalled();
1142
+ expect(sessionDomainService.setRuntimeSnapshot).not.toHaveBeenCalled();
1143
+ });
1144
+ it('does not spawn a browse client without a stored provider session', async () => {
1145
+ const { service, runtimeManager } = createStartableLifecycleService({
1146
+ provider: 'CODEX',
1147
+ providerSessionId: null,
1148
+ });
1149
+ await expect(service.ensureSubagentBrowseSession('session-1')).resolves.toBe(false);
1150
+ expect(runtimeManager.getOrCreateClient).not.toHaveBeenCalled();
1151
+ });
1152
+ it('returns unsupported when the stopped session has no usable worktree', async () => {
1153
+ const { service, runtimeManager } = createStartableLifecycleService({
1154
+ provider: 'CODEX',
1155
+ providerSessionId: 'provider-session-existing',
1156
+ worktreePath: null,
1157
+ });
1158
+ await expect(service.ensureSubagentBrowseSession('session-1')).resolves.toBe(false);
1159
+ expect(runtimeManager.getOrCreateClient).not.toHaveBeenCalled();
1160
+ });
1161
+ it('returns unsupported when the provider cannot restore the stored session', async () => {
1162
+ const { service, runtimeManager } = createStartableLifecycleService({
1163
+ provider: 'CODEX',
1164
+ providerSessionId: 'provider-session-existing',
1165
+ });
1166
+ runtimeManager.getOrCreateClient.mockRejectedValueOnce(new AcpBrowseSessionUnavailableError('loadSession unsupported'));
1167
+ await expect(service.ensureSubagentBrowseSession('session-1')).resolves.toBe(false);
1168
+ });
1169
+ it('stops a browse-only client when the provider lacks sub-agent browsing', async () => {
1170
+ const { service, runtimeManager, acpEventProcessor } = createStartableLifecycleService({
1171
+ provider: 'CODEX',
1172
+ providerSessionId: 'provider-session-existing',
1173
+ });
1174
+ runtimeManager.isBrowseOnlySession.mockReturnValue(true);
1175
+ await expect(service.ensureSubagentBrowseSession('session-1')).resolves.toBe(false);
1176
+ expect(runtimeManager.stopClient).toHaveBeenCalledWith('session-1');
1177
+ expect(acpEventProcessor.clearSessionState).toHaveBeenCalledWith('session-1');
1178
+ });
1179
+ it('cancels an active startup that began before unsupported browse cleanup', async () => {
1180
+ const { service, session, repository, runtimeManager } = createStartableLifecycleService({
1181
+ provider: 'CODEX',
1182
+ providerSessionId: 'provider-session-existing',
1183
+ });
1184
+ const activeSessionLookup = createDeferred();
1185
+ const browseStop = createDeferred();
1186
+ repository.getSessionById.mockReturnValueOnce(activeSessionLookup.promise);
1187
+ runtimeManager.isBrowseOnlySession.mockReturnValue(true);
1188
+ runtimeManager.stopClient.mockReturnValueOnce(browseStop.promise);
1189
+ const activeStartup = service.getOrCreateSessionClient('session-1');
1190
+ await vi.waitFor(() => {
1191
+ expect(repository.getSessionById).toHaveBeenCalledTimes(1);
1192
+ });
1193
+ const browseResult = service.ensureSubagentBrowseSession('session-1');
1194
+ await vi.waitFor(() => {
1195
+ expect(runtimeManager.stopClient).toHaveBeenCalledWith('session-1');
1196
+ });
1197
+ activeSessionLookup.resolve(session);
1198
+ await expect(activeStartup).rejects.toThrow();
1199
+ browseStop.resolve(undefined);
1200
+ await expect(browseResult).resolves.toBe(false);
1201
+ expect(runtimeManager.getOrCreateClient).toHaveBeenCalledTimes(1);
1202
+ });
1203
+ it('returns unsupported when concurrent browse cleanup invalidates its startup', async () => {
1204
+ const { service, session, repository, runtimeManager } = createStartableLifecycleService({
1205
+ provider: 'CODEX',
1206
+ providerSessionId: 'provider-session-existing',
1207
+ });
1208
+ const firstSessionLookup = createDeferred();
1209
+ const secondSessionLookup = createDeferred();
1210
+ const browseStop = createDeferred();
1211
+ repository.getSessionById
1212
+ .mockReturnValueOnce(firstSessionLookup.promise)
1213
+ .mockReturnValueOnce(secondSessionLookup.promise);
1214
+ runtimeManager.isBrowseOnlySession.mockReturnValue(true);
1215
+ runtimeManager.stopClient.mockReturnValueOnce(browseStop.promise);
1216
+ const firstBrowse = service.ensureSubagentBrowseSession('session-1');
1217
+ const secondBrowse = service.ensureSubagentBrowseSession('session-1');
1218
+ await vi.waitFor(() => {
1219
+ expect(repository.getSessionById).toHaveBeenCalledTimes(2);
1220
+ });
1221
+ firstSessionLookup.resolve(session);
1222
+ await vi.waitFor(() => {
1223
+ expect(runtimeManager.stopClient).toHaveBeenCalledWith('session-1');
1224
+ });
1225
+ secondSessionLookup.resolve(session);
1226
+ await expect(secondBrowse).resolves.toBe(false);
1227
+ browseStop.resolve(undefined);
1228
+ await expect(firstBrowse).resolves.toBe(false);
1229
+ expect(runtimeManager.getOrCreateClient).toHaveBeenCalledTimes(1);
1230
+ });
1231
+ it('does not let a failed browse creation clear a concurrent active startup context', async () => {
1232
+ const { service, handle, runtimeManager, acpEventProcessor } = createStartableLifecycleService({
1233
+ provider: 'CODEX',
1234
+ providerSessionId: 'provider-session-existing',
1235
+ });
1236
+ const browseCreation = createDeferred();
1237
+ const activeCreation = createDeferred();
1238
+ runtimeManager.getOrCreateClient
1239
+ .mockReturnValueOnce(browseCreation.promise)
1240
+ .mockReturnValueOnce(activeCreation.promise);
1241
+ const browseResult = service
1242
+ .ensureSubagentBrowseSession('session-1')
1243
+ .catch((error) => error);
1244
+ await vi.waitFor(() => {
1245
+ expect(runtimeManager.getOrCreateClient).toHaveBeenCalledTimes(1);
1246
+ });
1247
+ const activeResult = service.getOrCreateSessionClient('session-1');
1248
+ await vi.waitFor(() => {
1249
+ expect(runtimeManager.getOrCreateClient).toHaveBeenCalledTimes(2);
1250
+ });
1251
+ browseCreation.reject(new Error('browse restore failed'));
1252
+ await expect(browseResult).resolves.toEqual(expect.objectContaining({ message: 'browse restore failed' }));
1253
+ expect(acpEventProcessor.clearSessionState).not.toHaveBeenCalled();
1254
+ activeCreation.resolve(handle);
1255
+ await expect(activeResult).resolves.toBe(handle);
1256
+ });
1257
+ it('omits provider-session persistence from browse-only runtime handlers', () => {
1258
+ const { service } = createStartableLifecycleService({
1259
+ provider: 'CODEX',
1260
+ providerSessionId: 'provider-session-existing',
1261
+ });
1262
+ const handlers = service.setupAcpEventHandler('session-1', { persistProviderSessionId: false });
1263
+ expect(handlers.onSessionId).toBeUndefined();
1264
+ });
1265
+ it('promotes a restored browse client through the normal active startup path', async () => {
1266
+ const { service, repository, runtimeManager, handle, deliverPendingChildNotifications, sessionConfigService, } = createStartableLifecycleService({
1267
+ provider: 'CODEX',
1268
+ providerSessionId: 'provider-session-existing',
1269
+ pendingNotificationCount: 1,
1270
+ });
1271
+ runtimeManager.getOrCreateClient.mockImplementation((_id, options) => {
1272
+ if (options.purpose === 'browse') {
1273
+ runtimeManager.getSubagentBrowseCapability.mockReturnValue({
1274
+ version: 1,
1275
+ list: true,
1276
+ read: true,
1277
+ notifications: true,
1278
+ });
1279
+ }
1280
+ return Promise.resolve(handle);
1281
+ });
1282
+ await service.ensureSubagentBrowseSession('session-1');
1283
+ await expect(service.getOrCreateSessionClient('session-1')).resolves.toBe(handle);
1284
+ expect(runtimeManager.getOrCreateClient).toHaveBeenCalledTimes(2);
1285
+ expect(runtimeManager.getOrCreateClient.mock.calls[0]?.[1]).toMatchObject({
1286
+ purpose: 'browse',
1287
+ });
1288
+ expect(runtimeManager.getOrCreateClient.mock.calls[1]?.[1].purpose).toBeUndefined();
1289
+ expect(repository.updateSession).toHaveBeenCalledWith('session-1', {
1290
+ status: SessionStatus.RUNNING,
1291
+ });
1292
+ expect(sessionConfigService.applyConfiguredPermissionPreset).toHaveBeenCalledTimes(1);
1293
+ expect(deliverPendingChildNotifications).toHaveBeenCalledTimes(1);
1294
+ });
1295
+ it('does not retain a stop generation for a missing session', async () => {
1296
+ const { service, repository } = createStartableLifecycleService();
1297
+ repository.getSessionById.mockResolvedValueOnce(null);
1298
+ await expect(service.startSession('missing-session')).rejects.toThrow('Session not found: missing-session');
1299
+ expect(getStopGenerations(service).has('missing-session')).toBe(false);
1300
+ });
1301
+ it('does not create a client when stop completes during the initial session lookup', async () => {
1302
+ const { service, session, repository, runtimeManager } = createStartableLifecycleService();
1303
+ let resolveSession;
1304
+ repository.getSessionById.mockReturnValueOnce(new Promise((resolve) => {
1305
+ resolveSession = resolve;
1306
+ }));
1307
+ const startResult = service.startSession('session-1').catch((error) => error);
1308
+ await vi.waitFor(() => {
1309
+ expect(repository.getSessionById).toHaveBeenCalledWith('session-1');
1310
+ });
1311
+ await service.stopSession('session-1');
1312
+ resolveSession(session);
1313
+ await expect(startResult).resolves.toEqual(expect.objectContaining({ message: 'Session is currently being stopped' }));
1314
+ expect(runtimeManager.getOrCreateClient).not.toHaveBeenCalled();
1315
+ expect(getStopGenerations(service).has('session-1')).toBe(false);
1316
+ });
1317
+ it('releases the stop generation when startup fails before creating a runtime', async () => {
1318
+ const { service, runtimeManager } = createStartableLifecycleService();
1319
+ runtimeManager.getOrCreateClient.mockRejectedValueOnce(new Error('spawn failed'));
1320
+ await expect(service.startSession('session-1')).rejects.toThrow('spawn failed');
1321
+ expect(getStopGenerations(service).has('session-1')).toBe(false);
1322
+ });
1323
+ it('does not retain a stop generation when client lookup cannot find the session', async () => {
1324
+ const { service, repository } = createStartableLifecycleService();
1325
+ repository.getSessionById.mockResolvedValueOnce(null);
1326
+ await expect(service.getOrCreateSessionClient('missing-session')).rejects.toThrow('Session not found: missing-session');
1327
+ expect(getStopGenerations(service).has('missing-session')).toBe(false);
1328
+ });
1329
+ it('releases the stop generation when record-based client creation fails', async () => {
1330
+ const { service, session, runtimeManager } = createStartableLifecycleService();
1331
+ runtimeManager.getOrCreateClient.mockRejectedValueOnce(new Error('spawn failed'));
1332
+ await expect(service.getOrCreateSessionClientFromRecord(session)).rejects.toThrow('spawn failed');
1333
+ expect(getStopGenerations(service).has('session-1')).toBe(false);
1334
+ });
1335
+ it('does not release a stop generation still owned by a concurrent startup', async () => {
1336
+ let resolveFirstSettings;
1337
+ const firstSettings = new Promise((resolve) => {
1338
+ resolveFirstSettings = resolve;
1339
+ });
1340
+ vi.mocked(userSettingsService.get)
1341
+ .mockReturnValueOnce(firstSettings)
1342
+ .mockResolvedValueOnce(unsafeCoerce({
1343
+ defaultWorkspacePermissions: 'STRICT',
1344
+ ratchetPermissions: 'YOLO',
1345
+ }));
1346
+ const { service, runtimeManager } = createStartableLifecycleService();
1347
+ const firstStart = service.startSession('session-1');
1348
+ await vi.waitFor(() => {
1349
+ expect(userSettingsService.get).toHaveBeenCalledTimes(1);
1350
+ });
1351
+ runtimeManager.getOrCreateClient.mockRejectedValueOnce(new Error('second spawn failed'));
1352
+ await expect(service.startSession('session-1')).rejects.toThrow('second spawn failed');
1353
+ resolveFirstSettings(unsafeCoerce({
1354
+ defaultWorkspacePermissions: 'STRICT',
1355
+ ratchetPermissions: 'YOLO',
1356
+ }));
1357
+ await expect(firstStart).resolves.toBeUndefined();
1358
+ expect(getStopGenerations(service).has('session-1')).toBe(true);
1359
+ });
744
1360
  it('dispatches queued notifications after startup presets and skips the default continue prompt', async () => {
745
1361
  const { service, sendSessionMessage, tryDispatchNextMessage, sessionConfigService } = createStartableLifecycleService({ pendingNotificationCount: 2 });
746
1362
  await service.startSession('session-1');
@@ -768,6 +1384,22 @@ describe('SessionLifecycleService startSession pending workspace notifications',
768
1384
  expect(sendOrder).toBeDefined();
769
1385
  expect(dispatchOrder).toBeLessThan(sendOrder);
770
1386
  });
1387
+ it('does not complete startup when stop finishes during the initial prompt', async () => {
1388
+ let resolvePrompt;
1389
+ const pendingPrompt = new Promise((resolve) => {
1390
+ resolvePrompt = resolve;
1391
+ });
1392
+ const { service, sendSessionMessage } = createStartableLifecycleService();
1393
+ sendSessionMessage.mockReturnValueOnce(pendingPrompt);
1394
+ const startResult = service.startSession('session-1').catch((error) => error);
1395
+ await vi.waitFor(() => {
1396
+ expect(sendSessionMessage).toHaveBeenCalledWith('session-1', 'Continue with the task.');
1397
+ });
1398
+ await service.stopSession('session-1');
1399
+ resolvePrompt(undefined);
1400
+ await expect(startResult).resolves.toEqual(expect.objectContaining({ message: 'Session is currently being stopped' }));
1401
+ expect(getStopGenerations(service).has('session-1')).toBe(false);
1402
+ });
771
1403
  it('does not create a client after stop completes during permission resolution', async () => {
772
1404
  let resolveSettings;
773
1405
  const pendingSettings = new Promise((resolve) => {
@@ -780,6 +1412,7 @@ describe('SessionLifecycleService startSession pending workspace notifications',
780
1412
  expect(userSettingsService.get).toHaveBeenCalled();
781
1413
  });
782
1414
  await service.stopSession('session-1');
1415
+ expect(getStopGenerations(service).has('session-1')).toBe(false);
783
1416
  resolveSettings(unsafeCoerce({
784
1417
  defaultWorkspacePermissions: 'STRICT',
785
1418
  ratchetPermissions: 'YOLO',
@@ -787,6 +1420,42 @@ describe('SessionLifecycleService startSession pending workspace notifications',
787
1420
  await expect(startResult).resolves.toEqual(expect.objectContaining({ message: 'Session is currently being stopped' }));
788
1421
  expect(runtimeManager.getOrCreateClient).not.toHaveBeenCalled();
789
1422
  expect(sendSessionMessage).not.toHaveBeenCalled();
1423
+ expect(getStopGenerations(service).has('session-1')).toBe(false);
1424
+ });
1425
+ it('releases the stop generation after a session stops', async () => {
1426
+ const { service } = createStartableLifecycleService();
1427
+ await service.stopSession('session-1');
1428
+ expect(getStopGenerations(service).has('session-1')).toBe(false);
1429
+ });
1430
+ it('releases the stop generation when viewers retain inactive session state', async () => {
1431
+ const { service, sessionDomainService } = createStartableLifecycleService();
1432
+ sessionEventBus.registerViewerCountProvider((sessionId) => (sessionId === 'session-1' ? 1 : 0));
1433
+ try {
1434
+ await service.stopSession('session-1');
1435
+ }
1436
+ finally {
1437
+ sessionEventBus.registerViewerCountProvider(null);
1438
+ }
1439
+ expect(sessionDomainService.clearSession).not.toHaveBeenCalled();
1440
+ expect(getStopGenerations(service).has('session-1')).toBe(false);
1441
+ });
1442
+ it('does not clear a restarted generation when an old runtime exit finishes', async () => {
1443
+ const { service, session, repository } = createStartableLifecycleService();
1444
+ let resolveUpdate;
1445
+ repository.updateSession.mockReturnValueOnce(new Promise((resolve) => {
1446
+ resolveUpdate = resolve;
1447
+ }));
1448
+ const oldGeneration = service.getStopGeneration('session-1');
1449
+ const handlers = service.setupAcpEventHandler('session-1');
1450
+ const exitPromise = handlers.onExit('session-1', 0);
1451
+ expect(getStopGenerations(service).has('session-1')).toBe(false);
1452
+ await service.startSession('session-1');
1453
+ const restartedGeneration = service.getStopGeneration('session-1');
1454
+ expect(restartedGeneration).not.toBe(oldGeneration);
1455
+ resolveUpdate(session);
1456
+ await exitPromise;
1457
+ expect(service.isStopGenerationCurrent('session-1', restartedGeneration)).toBe(true);
1458
+ expect(service.isStopGenerationCurrent('session-1', oldGeneration)).toBe(false);
790
1459
  });
791
1460
  it('waits for a registered client creation and stops the resulting runtime', async () => {
792
1461
  const { service, sendSessionMessage, runtimeManager } = createStartableLifecycleService();