factory-factory 0.4.6 → 0.4.7

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 (527) hide show
  1. package/dist/client/assets/{abnfDiagram-N423BO3Z-BcUuFtOj.js → abnfDiagram-N423BO3Z-BZQ2vUo6.js} +1 -1
  2. package/dist/client/assets/{arc-Cp8_E6ha.js → arc-XI8Jpfl3.js} +1 -1
  3. package/dist/client/assets/{architectureDiagram-T3A2C74G-C3_2J0tC.js → architectureDiagram-T3A2C74G-Dt7Y1pyP.js} +1 -1
  4. package/dist/client/assets/{blockDiagram-VBNYF7ZC-Dy0jc6lP.js → blockDiagram-VBNYF7ZC-DldefwGh.js} +1 -1
  5. package/dist/client/assets/{c4Diagram-5PPSVZJV-97TkD6w-.js → c4Diagram-5PPSVZJV-Ba08x7Ef.js} +1 -1
  6. package/dist/client/assets/channel-CRP5IiHm.js +1 -0
  7. package/dist/client/assets/{chunk-2GRJ4B5K-Dc0I7ume.js → chunk-2GRJ4B5K-B9S0F-4G.js} +1 -1
  8. package/dist/client/assets/{chunk-2Q5K7J3B-DbFPWZwu.js → chunk-2Q5K7J3B-CZXgC_Jx.js} +1 -1
  9. package/dist/client/assets/{chunk-5RXB4S5H-Dci_7ZDD.js → chunk-5RXB4S5H-3z0Q5lZX.js} +1 -1
  10. package/dist/client/assets/{chunk-5VM5RSS4-Ca82Rzl0.js → chunk-5VM5RSS4-D2hwZzSy.js} +1 -1
  11. package/dist/client/assets/{chunk-6Q2QTUOP-Be2f5Q2t.js → chunk-6Q2QTUOP-DhgyUVVB.js} +1 -1
  12. package/dist/client/assets/{chunk-GF5L2VYU-C4VRs6xS.js → chunk-GF5L2VYU-B9CpXn1z.js} +1 -1
  13. package/dist/client/assets/{chunk-JWPE2WC7-DVlW0c7f.js → chunk-JWPE2WC7-C3uO_aKI.js} +1 -1
  14. package/dist/client/assets/{chunk-KBJHAD2P-BKRpW89b.js → chunk-KBJHAD2P-BclkmYso.js} +1 -1
  15. package/dist/client/assets/{chunk-RYQCIY6F-BwMw9oh9.js → chunk-RYQCIY6F-D6aZFqMZ.js} +1 -1
  16. package/dist/client/assets/{chunk-XXDRQBXY-py-FQquv.js → chunk-XXDRQBXY-BQb7-SCi.js} +1 -1
  17. package/dist/client/assets/classDiagram-JCYQIIEL-CB4-8RDd.js +1 -0
  18. package/dist/client/assets/classDiagram-v2-OCEON4UE-CB4-8RDd.js +1 -0
  19. package/dist/client/assets/{cose-bilkent-JH36ORCC-BMCtQCZ8.js → cose-bilkent-JH36ORCC-WLzzMmoC.js} +1 -1
  20. package/dist/client/assets/{cynefin-VYW2F7L2-BTHAy6wD.js → cynefin-VYW2F7L2-D2EF0q2Q.js} +1 -1
  21. package/dist/client/assets/{cynefinDiagram-MW4NZA55-pQCQD0Ih.js → cynefinDiagram-MW4NZA55-CwoCQCrP.js} +1 -1
  22. package/dist/client/assets/{dagre-VZM6K2ZE-CZcoEFVf.js → dagre-VZM6K2ZE-Cg4AkVGB.js} +1 -1
  23. package/dist/client/assets/{diagram-7IWD3JNH-DgQv_208.js → diagram-7IWD3JNH-D9lnDmSl.js} +1 -1
  24. package/dist/client/assets/{diagram-B4RE2ZJO-DoFotcsB.js → diagram-B4RE2ZJO-AhUFMusY.js} +1 -1
  25. package/dist/client/assets/{diagram-LBJQPF4R-C5zkL1ml.js → diagram-LBJQPF4R-C81JtkuS.js} +1 -1
  26. package/dist/client/assets/{diagram-Q27KOJAE-ERKqnoHP.js → diagram-Q27KOJAE-BOzaYlQ2.js} +1 -1
  27. package/dist/client/assets/{diagram-UB23O5K3-Dyt6sy3Z.js → diagram-UB23O5K3-uXHocDB0.js} +1 -1
  28. package/dist/client/assets/{ebnfDiagram-BXEA7PRR-DqsFEAw2.js → ebnfDiagram-BXEA7PRR-Du4yOTiI.js} +1 -1
  29. package/dist/client/assets/{erDiagram-JOGREHBK-Dn8Aqkgn.js → erDiagram-JOGREHBK-BxBwtYA0.js} +1 -1
  30. package/dist/client/assets/{flowDiagram-UKHOOZJN-DcArGyH-.js → flowDiagram-UKHOOZJN-YQc_Us9R.js} +1 -1
  31. package/dist/client/assets/{ganttDiagram-PKOTCBZU-BuHaeolc.js → ganttDiagram-PKOTCBZU-CO08NXgj.js} +1 -1
  32. package/dist/client/assets/{gitGraphDiagram-DS77QQ5N-iKWWNsQK.js → gitGraphDiagram-DS77QQ5N-NRBrIkNv.js} +1 -1
  33. package/dist/client/assets/{index-DY0TryY7.js → index-CrLTUCBe.js} +83 -83
  34. package/dist/client/assets/{infoDiagram-6WML65LV-C7IulVvo.js → infoDiagram-6WML65LV-CcOh5qQM.js} +1 -1
  35. package/dist/client/assets/{ishikawaDiagram-WSZJBQD7-DW4cB_Nk.js → ishikawaDiagram-WSZJBQD7-Ci4gKk1n.js} +1 -1
  36. package/dist/client/assets/{journeyDiagram-NVQOT4AX-Nzsiy4H7.js → journeyDiagram-NVQOT4AX-CpW1inrw.js} +1 -1
  37. package/dist/client/assets/{kanban-definition-27J2QSJJ-BUsK9dj9.js → kanban-definition-27J2QSJJ-jez_FwZT.js} +1 -1
  38. package/dist/client/assets/{linear-CyoW-usj.js → linear-6SNETQpS.js} +1 -1
  39. package/dist/client/assets/{mindmap-definition-FAOFIHXS-Bd9cSJjQ.js → mindmap-definition-FAOFIHXS-SmrP71Hb.js} +1 -1
  40. package/dist/client/assets/{mobile-baseline-K78aE4nt.js → mobile-baseline-8VVGaFwv.js} +1 -1
  41. package/dist/client/assets/{pegDiagram-VL7TDLO6-BrmAKdiE.js → pegDiagram-VL7TDLO6-BdZpPP9I.js} +1 -1
  42. package/dist/client/assets/{pieDiagram-7S7Q4E2Y-Ce0k6LI1.js → pieDiagram-7S7Q4E2Y-JiNW5VtU.js} +1 -1
  43. package/dist/client/assets/{quadrantDiagram-CIZ2JOQS-_9HxOOGa.js → quadrantDiagram-CIZ2JOQS-C2VhzPor.js} +1 -1
  44. package/dist/client/assets/{railroadDiagram-AXF67PYL-ChdF1CBw.js → railroadDiagram-AXF67PYL-BdfHLYrf.js} +1 -1
  45. package/dist/client/assets/{requirementDiagram-LRYGKXZP-Bm2gdYTE.js → requirementDiagram-LRYGKXZP-BRxDcjdJ.js} +1 -1
  46. package/dist/client/assets/{sankeyDiagram-W5VNT64P-Kf7zUiC-.js → sankeyDiagram-W5VNT64P-BvibpHKV.js} +1 -1
  47. package/dist/client/assets/{sequenceDiagram-SI44F4Z6-DyPqsgh1.js → sequenceDiagram-SI44F4Z6-CstrIf8Q.js} +1 -1
  48. package/dist/client/assets/{sizeCapture-X5ZJPWSS-Cgn8Hxr2.js → sizeCapture-X5ZJPWSS-fplvtb2v.js} +1 -1
  49. package/dist/client/assets/{stateDiagram-OKZ733FA-CJxSzxmg.js → stateDiagram-OKZ733FA-Cb0qJfhN.js} +1 -1
  50. package/dist/client/assets/stateDiagram-v2-UEYNNEHI-C_Uzjhvg.js +1 -0
  51. package/dist/client/assets/{swimlanes-SLNWSIFB-Ckb_izEZ.js → swimlanes-SLNWSIFB-BAqCm7Gx.js} +2 -2
  52. package/dist/client/assets/swimlanesDiagram-ULZ7WXOC-CO4t8Utk.js +8 -0
  53. package/dist/client/assets/{terminal-instance-DWySt2_t.js → terminal-instance-B0prRfF6.js} +1 -1
  54. package/dist/client/assets/{timeline-definition-Z64GVDOM-BWx_NLrK.js → timeline-definition-Z64GVDOM-BZbQ0pZK.js} +1 -1
  55. package/dist/client/assets/{vennDiagram-T6HMQDX7-BgN2-lO-.js → vennDiagram-T6HMQDX7-B3YQXfS4.js} +1 -1
  56. package/dist/client/assets/{wardleyDiagram-T6FBY63Y-C9iwoeki.js → wardleyDiagram-T6FBY63Y-Cufi2KwO.js} +1 -1
  57. package/dist/client/assets/{xychartDiagram-ELKLHX3M-B3AYy5ZN.js → xychartDiagram-ELKLHX3M-CHGxh5zQ.js} +1 -1
  58. package/dist/client/index.html +1 -1
  59. package/dist/src/backend/app-context.d.ts.map +1 -1
  60. package/dist/src/backend/app-context.js +2 -2
  61. package/dist/src/backend/app-context.js.map +1 -1
  62. package/dist/src/backend/app-context.test.js +6 -1
  63. package/dist/src/backend/app-context.test.js.map +1 -1
  64. package/dist/src/backend/clients/git.client.d.ts +0 -8
  65. package/dist/src/backend/clients/git.client.d.ts.map +1 -1
  66. package/dist/src/backend/clients/git.client.js +0 -70
  67. package/dist/src/backend/clients/git.client.js.map +1 -1
  68. package/dist/src/backend/clients/git.client.test.js +2 -178
  69. package/dist/src/backend/clients/git.client.test.js.map +1 -1
  70. package/dist/src/backend/interceptors/branch-naming.interceptor.test.js +3 -2
  71. package/dist/src/backend/interceptors/branch-naming.interceptor.test.js.map +1 -1
  72. package/dist/src/backend/lib/file-helpers.d.ts +0 -1
  73. package/dist/src/backend/lib/file-helpers.d.ts.map +1 -1
  74. package/dist/src/backend/lib/file-helpers.js +0 -27
  75. package/dist/src/backend/lib/file-helpers.js.map +1 -1
  76. package/dist/src/backend/lib/shell.d.ts +0 -4
  77. package/dist/src/backend/lib/shell.d.ts.map +1 -1
  78. package/dist/src/backend/lib/shell.js +0 -25
  79. package/dist/src/backend/lib/shell.js.map +1 -1
  80. package/dist/src/backend/routers/websocket/chat.handler.d.ts.map +1 -1
  81. package/dist/src/backend/routers/websocket/chat.handler.js +1 -17
  82. package/dist/src/backend/routers/websocket/chat.handler.js.map +1 -1
  83. package/dist/src/backend/routers/websocket/chat.handler.test.js +2 -8
  84. package/dist/src/backend/routers/websocket/chat.handler.test.js.map +1 -1
  85. package/dist/src/backend/routers/websocket/websocket.integration.test.js +0 -4
  86. package/dist/src/backend/routers/websocket/websocket.integration.test.js.map +1 -1
  87. package/dist/src/backend/server.d.ts.map +1 -1
  88. package/dist/src/backend/server.js +2 -2
  89. package/dist/src/backend/server.js.map +1 -1
  90. package/dist/src/backend/server.upgrade.test.js +10 -4
  91. package/dist/src/backend/server.upgrade.test.js.map +1 -1
  92. package/dist/src/backend/services/decision-log/resources/decision-log.accessor.d.ts +0 -2
  93. package/dist/src/backend/services/decision-log/resources/decision-log.accessor.d.ts.map +1 -1
  94. package/dist/src/backend/services/decision-log/resources/decision-log.accessor.js +0 -6
  95. package/dist/src/backend/services/decision-log/resources/decision-log.accessor.js.map +1 -1
  96. package/dist/src/backend/services/github/service/github-cli/schemas.d.ts +0 -23
  97. package/dist/src/backend/services/github/service/github-cli/schemas.d.ts.map +1 -1
  98. package/dist/src/backend/services/github/service/github-cli/schemas.js +0 -18
  99. package/dist/src/backend/services/github/service/github-cli/schemas.js.map +1 -1
  100. package/dist/src/backend/services/github/service/github-cli.service.d.ts +0 -2
  101. package/dist/src/backend/services/github/service/github-cli.service.d.ts.map +1 -1
  102. package/dist/src/backend/services/github/service/github-cli.service.js +0 -37
  103. package/dist/src/backend/services/github/service/github-cli.service.js.map +1 -1
  104. package/dist/src/backend/services/github/service/github-cli.service.test.js +3 -13
  105. package/dist/src/backend/services/github/service/github-cli.service.test.js.map +1 -1
  106. package/dist/src/backend/services/session/resources/agent-session.accessor.d.ts +0 -1
  107. package/dist/src/backend/services/session/resources/agent-session.accessor.d.ts.map +1 -1
  108. package/dist/src/backend/services/session/resources/agent-session.accessor.js +0 -8
  109. package/dist/src/backend/services/session/resources/agent-session.accessor.js.map +1 -1
  110. package/dist/src/backend/services/session/resources/agent-session.accessor.test.js +0 -10
  111. package/dist/src/backend/services/session/resources/agent-session.accessor.test.js.map +1 -1
  112. package/dist/src/backend/services/session/service/acp/acp-client-factory.d.ts +33 -0
  113. package/dist/src/backend/services/session/service/acp/acp-client-factory.d.ts.map +1 -0
  114. package/dist/src/backend/services/session/service/acp/acp-client-factory.js +274 -0
  115. package/dist/src/backend/services/session/service/acp/acp-client-factory.js.map +1 -0
  116. package/dist/src/backend/services/session/service/acp/acp-client-factory.test.d.ts +2 -0
  117. package/dist/src/backend/services/session/service/acp/acp-client-factory.test.d.ts.map +1 -0
  118. package/dist/src/backend/services/session/service/acp/acp-client-factory.test.js +454 -0
  119. package/dist/src/backend/services/session/service/acp/acp-client-factory.test.js.map +1 -0
  120. package/dist/src/backend/services/session/service/acp/acp-client-handler.d.ts.map +1 -1
  121. package/dist/src/backend/services/session/service/acp/acp-client-handler.js +17 -0
  122. package/dist/src/backend/services/session/service/acp/acp-client-handler.js.map +1 -1
  123. package/dist/src/backend/services/session/service/acp/acp-client-handler.test.js +171 -0
  124. package/dist/src/backend/services/session/service/acp/acp-client-handler.test.js.map +1 -1
  125. package/dist/src/backend/services/session/service/acp/acp-permission-bridge.d.ts +0 -1
  126. package/dist/src/backend/services/session/service/acp/acp-permission-bridge.d.ts.map +1 -1
  127. package/dist/src/backend/services/session/service/acp/acp-permission-bridge.js +0 -3
  128. package/dist/src/backend/services/session/service/acp/acp-permission-bridge.js.map +1 -1
  129. package/dist/src/backend/services/session/service/acp/acp-permission-bridge.test.js +0 -10
  130. package/dist/src/backend/services/session/service/acp/acp-permission-bridge.test.js.map +1 -1
  131. package/dist/src/backend/services/session/service/acp/acp-process-handle.test.js +17 -0
  132. package/dist/src/backend/services/session/service/acp/acp-process-handle.test.js.map +1 -1
  133. package/dist/src/backend/services/session/service/acp/acp-prompt-controller.d.ts +17 -0
  134. package/dist/src/backend/services/session/service/acp/acp-prompt-controller.d.ts.map +1 -0
  135. package/dist/src/backend/services/session/service/acp/acp-prompt-controller.js +96 -0
  136. package/dist/src/backend/services/session/service/acp/acp-prompt-controller.js.map +1 -0
  137. package/dist/src/backend/services/session/service/acp/acp-prompt-controller.test.d.ts +2 -0
  138. package/dist/src/backend/services/session/service/acp/acp-prompt-controller.test.d.ts.map +1 -0
  139. package/dist/src/backend/services/session/service/acp/acp-prompt-controller.test.js +140 -0
  140. package/dist/src/backend/services/session/service/acp/acp-prompt-controller.test.js.map +1 -0
  141. package/dist/src/backend/services/session/service/acp/acp-runtime-config-controller.d.ts +8 -0
  142. package/dist/src/backend/services/session/service/acp/acp-runtime-config-controller.d.ts.map +1 -0
  143. package/dist/src/backend/services/session/service/acp/acp-runtime-config-controller.js +81 -0
  144. package/dist/src/backend/services/session/service/acp/acp-runtime-config-controller.js.map +1 -0
  145. package/dist/src/backend/services/session/service/acp/acp-runtime-config-controller.test.d.ts +2 -0
  146. package/dist/src/backend/services/session/service/acp/acp-runtime-config-controller.test.d.ts.map +1 -0
  147. package/dist/src/backend/services/session/service/acp/acp-runtime-config-controller.test.js +208 -0
  148. package/dist/src/backend/services/session/service/acp/acp-runtime-config-controller.test.js.map +1 -0
  149. package/dist/src/backend/services/session/service/acp/acp-runtime-contracts.d.ts +25 -0
  150. package/dist/src/backend/services/session/service/acp/acp-runtime-contracts.d.ts.map +1 -0
  151. package/dist/src/backend/services/session/service/acp/acp-runtime-contracts.js +2 -0
  152. package/dist/src/backend/services/session/service/acp/acp-runtime-contracts.js.map +1 -0
  153. package/dist/src/backend/services/session/service/acp/acp-runtime-error-handler.d.ts +5 -0
  154. package/dist/src/backend/services/session/service/acp/acp-runtime-error-handler.d.ts.map +1 -0
  155. package/dist/src/backend/services/session/service/acp/acp-runtime-error-handler.js +39 -0
  156. package/dist/src/backend/services/session/service/acp/acp-runtime-error-handler.js.map +1 -0
  157. package/dist/src/backend/services/session/service/acp/acp-runtime-errors.d.ts +20 -0
  158. package/dist/src/backend/services/session/service/acp/acp-runtime-errors.d.ts.map +1 -0
  159. package/dist/src/backend/services/session/service/acp/acp-runtime-errors.js +68 -0
  160. package/dist/src/backend/services/session/service/acp/acp-runtime-errors.js.map +1 -0
  161. package/dist/src/backend/services/session/service/acp/acp-runtime-errors.test.d.ts +2 -0
  162. package/dist/src/backend/services/session/service/acp/acp-runtime-errors.test.d.ts.map +1 -0
  163. package/dist/src/backend/services/session/service/acp/acp-runtime-errors.test.js +17 -0
  164. package/dist/src/backend/services/session/service/acp/acp-runtime-errors.test.js.map +1 -0
  165. package/dist/src/backend/services/session/service/acp/acp-runtime-events.d.ts +30 -1
  166. package/dist/src/backend/services/session/service/acp/acp-runtime-events.d.ts.map +1 -1
  167. package/dist/src/backend/services/session/service/acp/acp-runtime-exit-handler.d.ts +5 -0
  168. package/dist/src/backend/services/session/service/acp/acp-runtime-exit-handler.d.ts.map +1 -0
  169. package/dist/src/backend/services/session/service/acp/acp-runtime-exit-handler.js +35 -0
  170. package/dist/src/backend/services/session/service/acp/acp-runtime-exit-handler.js.map +1 -0
  171. package/dist/src/backend/services/session/service/acp/acp-runtime-manager.browsing.test.d.ts +2 -0
  172. package/dist/src/backend/services/session/service/acp/acp-runtime-manager.browsing.test.d.ts.map +1 -0
  173. package/dist/src/backend/services/session/service/acp/acp-runtime-manager.browsing.test.js +310 -0
  174. package/dist/src/backend/services/session/service/acp/acp-runtime-manager.browsing.test.js.map +1 -0
  175. package/dist/src/backend/services/session/service/acp/acp-runtime-manager.config.test.d.ts +2 -0
  176. package/dist/src/backend/services/session/service/acp/acp-runtime-manager.config.test.d.ts.map +1 -0
  177. package/dist/src/backend/services/session/service/acp/acp-runtime-manager.config.test.js +162 -0
  178. package/dist/src/backend/services/session/service/acp/acp-runtime-manager.config.test.js.map +1 -0
  179. package/dist/src/backend/services/session/service/acp/acp-runtime-manager.creation.test.d.ts +2 -0
  180. package/dist/src/backend/services/session/service/acp/acp-runtime-manager.creation.test.d.ts.map +1 -0
  181. package/dist/src/backend/services/session/service/acp/acp-runtime-manager.creation.test.js +390 -0
  182. package/dist/src/backend/services/session/service/acp/acp-runtime-manager.creation.test.js.map +1 -0
  183. package/dist/src/backend/services/session/service/acp/acp-runtime-manager.d.ts +18 -68
  184. package/dist/src/backend/services/session/service/acp/acp-runtime-manager.d.ts.map +1 -1
  185. package/dist/src/backend/services/session/service/acp/acp-runtime-manager.facade.test.d.ts +2 -0
  186. package/dist/src/backend/services/session/service/acp/acp-runtime-manager.facade.test.d.ts.map +1 -0
  187. package/dist/src/backend/services/session/service/acp/acp-runtime-manager.facade.test.js +59 -0
  188. package/dist/src/backend/services/session/service/acp/acp-runtime-manager.facade.test.js.map +1 -0
  189. package/dist/src/backend/services/session/service/acp/acp-runtime-manager.js +65 -993
  190. package/dist/src/backend/services/session/service/acp/acp-runtime-manager.js.map +1 -1
  191. package/dist/src/backend/services/session/service/acp/acp-runtime-manager.prompt.test.d.ts +2 -0
  192. package/dist/src/backend/services/session/service/acp/acp-runtime-manager.prompt.test.d.ts.map +1 -0
  193. package/dist/src/backend/services/session/service/acp/acp-runtime-manager.prompt.test.js +185 -0
  194. package/dist/src/backend/services/session/service/acp/acp-runtime-manager.prompt.test.js.map +1 -0
  195. package/dist/src/backend/services/session/service/acp/acp-runtime-manager.task-status.test.d.ts +2 -0
  196. package/dist/src/backend/services/session/service/acp/acp-runtime-manager.task-status.test.d.ts.map +1 -0
  197. package/dist/src/backend/services/session/service/acp/acp-runtime-manager.task-status.test.js +57 -0
  198. package/dist/src/backend/services/session/service/acp/acp-runtime-manager.task-status.test.js.map +1 -0
  199. package/dist/src/backend/services/session/service/acp/acp-runtime-manager.termination.test.d.ts +2 -0
  200. package/dist/src/backend/services/session/service/acp/acp-runtime-manager.termination.test.d.ts.map +1 -0
  201. package/dist/src/backend/services/session/service/acp/acp-runtime-manager.termination.test.js +392 -0
  202. package/dist/src/backend/services/session/service/acp/acp-runtime-manager.termination.test.js.map +1 -0
  203. package/dist/src/backend/services/session/service/acp/acp-runtime-manager.test-harness.d.ts +27 -0
  204. package/dist/src/backend/services/session/service/acp/acp-runtime-manager.test-harness.d.ts.map +1 -0
  205. package/dist/src/backend/services/session/service/acp/acp-runtime-manager.test-harness.js +96 -0
  206. package/dist/src/backend/services/session/service/acp/acp-runtime-manager.test-harness.js.map +1 -0
  207. package/dist/src/backend/services/session/service/acp/acp-runtime-manager.test-helpers.d.ts +59 -0
  208. package/dist/src/backend/services/session/service/acp/acp-runtime-manager.test-helpers.d.ts.map +1 -0
  209. package/dist/src/backend/services/session/service/acp/acp-runtime-manager.test-helpers.js +142 -0
  210. package/dist/src/backend/services/session/service/acp/acp-runtime-manager.test-helpers.js.map +1 -0
  211. package/dist/src/backend/services/session/service/acp/acp-runtime-quiescence.d.ts +21 -0
  212. package/dist/src/backend/services/session/service/acp/acp-runtime-quiescence.d.ts.map +1 -0
  213. package/dist/src/backend/services/session/service/acp/acp-runtime-quiescence.js +112 -0
  214. package/dist/src/backend/services/session/service/acp/acp-runtime-quiescence.js.map +1 -0
  215. package/dist/src/backend/services/session/service/acp/acp-runtime-quiescence.test.d.ts +2 -0
  216. package/dist/src/backend/services/session/service/acp/acp-runtime-quiescence.test.d.ts.map +1 -0
  217. package/dist/src/backend/services/session/service/acp/acp-runtime-quiescence.test.js +142 -0
  218. package/dist/src/backend/services/session/service/acp/acp-runtime-quiescence.test.js.map +1 -0
  219. package/dist/src/backend/services/session/service/acp/acp-runtime-supervisor.creation.test.d.ts +2 -0
  220. package/dist/src/backend/services/session/service/acp/acp-runtime-supervisor.creation.test.d.ts.map +1 -0
  221. package/dist/src/backend/services/session/service/acp/acp-runtime-supervisor.creation.test.js +333 -0
  222. package/dist/src/backend/services/session/service/acp/acp-runtime-supervisor.creation.test.js.map +1 -0
  223. package/dist/src/backend/services/session/service/acp/acp-runtime-supervisor.d.ts +85 -0
  224. package/dist/src/backend/services/session/service/acp/acp-runtime-supervisor.d.ts.map +1 -0
  225. package/dist/src/backend/services/session/service/acp/acp-runtime-supervisor.js +633 -0
  226. package/dist/src/backend/services/session/service/acp/acp-runtime-supervisor.js.map +1 -0
  227. package/dist/src/backend/services/session/service/acp/acp-runtime-supervisor.status.test.d.ts +2 -0
  228. package/dist/src/backend/services/session/service/acp/acp-runtime-supervisor.status.test.d.ts.map +1 -0
  229. package/dist/src/backend/services/session/service/acp/acp-runtime-supervisor.status.test.js +99 -0
  230. package/dist/src/backend/services/session/service/acp/acp-runtime-supervisor.status.test.js.map +1 -0
  231. package/dist/src/backend/services/session/service/acp/acp-runtime-supervisor.termination.test.d.ts +2 -0
  232. package/dist/src/backend/services/session/service/acp/acp-runtime-supervisor.termination.test.d.ts.map +1 -0
  233. package/dist/src/backend/services/session/service/acp/acp-runtime-supervisor.termination.test.js +490 -0
  234. package/dist/src/backend/services/session/service/acp/acp-runtime-supervisor.termination.test.js.map +1 -0
  235. package/dist/src/backend/services/session/service/acp/acp-subagent-browser.d.ts +9 -0
  236. package/dist/src/backend/services/session/service/acp/acp-subagent-browser.d.ts.map +1 -0
  237. package/dist/src/backend/services/session/service/acp/acp-subagent-browser.js +96 -0
  238. package/dist/src/backend/services/session/service/acp/acp-subagent-browser.js.map +1 -0
  239. package/dist/src/backend/services/session/service/acp/acp-subagent-browser.test.d.ts +2 -0
  240. package/dist/src/backend/services/session/service/acp/acp-subagent-browser.test.d.ts.map +1 -0
  241. package/dist/src/backend/services/session/service/acp/acp-subagent-browser.test.js +171 -0
  242. package/dist/src/backend/services/session/service/acp/acp-subagent-browser.test.js.map +1 -0
  243. package/dist/src/backend/services/session/service/acp/codex-app-server-adapter/codex-app-server-acp-adapter.d.ts +1 -1
  244. package/dist/src/backend/services/session/service/acp/codex-app-server-adapter/codex-app-server-acp-adapter.d.ts.map +1 -1
  245. package/dist/src/backend/services/session/service/acp/codex-app-server-adapter/codex-app-server-acp-adapter.js +14 -6
  246. package/dist/src/backend/services/session/service/acp/codex-app-server-adapter/codex-app-server-acp-adapter.js.map +1 -1
  247. package/dist/src/backend/services/session/service/acp/codex-app-server-adapter/codex-app-server-acp-adapter.notification-ordering.test.d.ts +2 -0
  248. package/dist/src/backend/services/session/service/acp/codex-app-server-adapter/codex-app-server-acp-adapter.notification-ordering.test.d.ts.map +1 -0
  249. package/dist/src/backend/services/session/service/acp/codex-app-server-adapter/codex-app-server-acp-adapter.notification-ordering.test.js +265 -0
  250. package/dist/src/backend/services/session/service/acp/codex-app-server-adapter/codex-app-server-acp-adapter.notification-ordering.test.js.map +1 -0
  251. package/dist/src/backend/services/session/service/acp/codex-app-server-adapter/codex-app-server-acp-adapter.test.js +32 -14
  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-notification-queue.d.ts +7 -0
  254. package/dist/src/backend/services/session/service/acp/codex-app-server-adapter/codex-notification-queue.d.ts.map +1 -0
  255. package/dist/src/backend/services/session/service/acp/codex-app-server-adapter/codex-notification-queue.js +98 -0
  256. package/dist/src/backend/services/session/service/acp/codex-app-server-adapter/codex-notification-queue.js.map +1 -0
  257. package/dist/src/backend/services/session/service/acp/codex-app-server-adapter/codex-notification-queue.test.d.ts +2 -0
  258. package/dist/src/backend/services/session/service/acp/codex-app-server-adapter/codex-notification-queue.test.d.ts.map +1 -0
  259. package/dist/src/backend/services/session/service/acp/codex-app-server-adapter/codex-notification-queue.test.js +148 -0
  260. package/dist/src/backend/services/session/service/acp/codex-app-server-adapter/codex-notification-queue.test.js.map +1 -0
  261. package/dist/src/backend/services/session/service/acp/codex-app-server-adapter/codex-subagent-mapper.test.js +10 -0
  262. package/dist/src/backend/services/session/service/acp/codex-app-server-adapter/codex-subagent-mapper.test.js.map +1 -1
  263. package/dist/src/backend/services/session/service/acp/codex-app-server-adapter/codex-zod.d.ts +72 -0
  264. package/dist/src/backend/services/session/service/acp/codex-app-server-adapter/codex-zod.d.ts.map +1 -1
  265. package/dist/src/backend/services/session/service/acp/codex-app-server-adapter/codex-zod.js +46 -1
  266. package/dist/src/backend/services/session/service/acp/codex-app-server-adapter/codex-zod.js.map +1 -1
  267. package/dist/src/backend/services/session/service/acp/codex-app-server-adapter/codex-zod.test.js +39 -0
  268. package/dist/src/backend/services/session/service/acp/codex-app-server-adapter/codex-zod.test.js.map +1 -1
  269. package/dist/src/backend/services/session/service/acp/codex-app-server-adapter/stream-event-handler.d.ts +10 -1
  270. package/dist/src/backend/services/session/service/acp/codex-app-server-adapter/stream-event-handler.d.ts.map +1 -1
  271. package/dist/src/backend/services/session/service/acp/codex-app-server-adapter/stream-event-handler.js +149 -17
  272. package/dist/src/backend/services/session/service/acp/codex-app-server-adapter/stream-event-handler.js.map +1 -1
  273. package/dist/src/backend/services/session/service/acp/codex-app-server-adapter/stream-event-handler.test.js +348 -19
  274. package/dist/src/backend/services/session/service/acp/codex-app-server-adapter/stream-event-handler.test.js.map +1 -1
  275. package/dist/src/backend/services/session/service/acp/codex-app-server-adapter/task-status-protocol.d.ts +8 -0
  276. package/dist/src/backend/services/session/service/acp/codex-app-server-adapter/task-status-protocol.d.ts.map +1 -0
  277. package/dist/src/backend/services/session/service/acp/codex-app-server-adapter/task-status-protocol.js +9 -0
  278. package/dist/src/backend/services/session/service/acp/codex-app-server-adapter/task-status-protocol.js.map +1 -0
  279. package/dist/src/backend/services/session/service/acp/index.d.ts +2 -2
  280. package/dist/src/backend/services/session/service/acp/index.d.ts.map +1 -1
  281. package/dist/src/backend/services/session/service/acp/index.js +1 -0
  282. package/dist/src/backend/services/session/service/acp/index.js.map +1 -1
  283. package/dist/src/backend/services/session/service/chat/chat-message-handlers/handlers/load-session.handler.d.ts.map +1 -1
  284. package/dist/src/backend/services/session/service/chat/chat-message-handlers/handlers/load-session.handler.js +9 -1
  285. package/dist/src/backend/services/session/service/chat/chat-message-handlers/handlers/load-session.handler.js.map +1 -1
  286. package/dist/src/backend/services/session/service/chat/chat-message-handlers/handlers/load-session.handler.recovery.test.d.ts +2 -0
  287. package/dist/src/backend/services/session/service/chat/chat-message-handlers/handlers/load-session.handler.recovery.test.d.ts.map +1 -0
  288. package/dist/src/backend/services/session/service/chat/chat-message-handlers/handlers/load-session.handler.recovery.test.js +195 -0
  289. package/dist/src/backend/services/session/service/chat/chat-message-handlers/handlers/load-session.handler.recovery.test.js.map +1 -0
  290. package/dist/src/backend/services/session/service/chat/chat-message-handlers/handlers/load-session.handler.test.js +1 -23
  291. package/dist/src/backend/services/session/service/chat/chat-message-handlers/handlers/load-session.handler.test.js.map +1 -1
  292. package/dist/src/backend/services/session/service/chat/chat-message-handlers/handlers/queue-message.handler.test.js +0 -6
  293. package/dist/src/backend/services/session/service/chat/chat-message-handlers/handlers/queue-message.handler.test.js.map +1 -1
  294. package/dist/src/backend/services/session/service/chat/chat-message-handlers/handlers/set-config-option.handler.js +1 -1
  295. package/dist/src/backend/services/session/service/chat/chat-message-handlers/handlers/set-config-option.handler.js.map +1 -1
  296. package/dist/src/backend/services/session/service/chat/chat-message-handlers/handlers/set-config-option.handler.test.js +1 -1
  297. package/dist/src/backend/services/session/service/chat/chat-message-handlers/handlers/set-config-option.handler.test.js.map +1 -1
  298. package/dist/src/backend/services/session/service/chat/chat-message-handlers/handlers/set-thinking-budget.handler.js +1 -1
  299. package/dist/src/backend/services/session/service/chat/chat-message-handlers/handlers/set-thinking-budget.handler.js.map +1 -1
  300. package/dist/src/backend/services/session/service/chat/chat-message-handlers/handlers/set-thinking-budget.handler.test.js +1 -1
  301. package/dist/src/backend/services/session/service/chat/chat-message-handlers/handlers/set-thinking-budget.handler.test.js.map +1 -1
  302. package/dist/src/backend/services/session/service/chat/chat-message-handlers/handlers/start.handler.d.ts +4 -2
  303. package/dist/src/backend/services/session/service/chat/chat-message-handlers/handlers/start.handler.d.ts.map +1 -1
  304. package/dist/src/backend/services/session/service/chat/chat-message-handlers/handlers/start.handler.js +2 -7
  305. package/dist/src/backend/services/session/service/chat/chat-message-handlers/handlers/start.handler.js.map +1 -1
  306. package/dist/src/backend/services/session/service/chat/chat-message-handlers/handlers/start.handler.test.js +9 -11
  307. package/dist/src/backend/services/session/service/chat/chat-message-handlers/handlers/start.handler.test.js.map +1 -1
  308. package/dist/src/backend/services/session/service/chat/chat-message-handlers/handlers/stop.handler.js +1 -1
  309. package/dist/src/backend/services/session/service/chat/chat-message-handlers/handlers/stop.handler.js.map +1 -1
  310. package/dist/src/backend/services/session/service/chat/chat-message-handlers/handlers/stop.handler.test.js +1 -3
  311. package/dist/src/backend/services/session/service/chat/chat-message-handlers/handlers/stop.handler.test.js.map +1 -1
  312. package/dist/src/backend/services/session/service/chat/chat-message-handlers/registry.d.ts.map +1 -1
  313. package/dist/src/backend/services/session/service/chat/chat-message-handlers/registry.js +3 -2
  314. package/dist/src/backend/services/session/service/chat/chat-message-handlers/registry.js.map +1 -1
  315. package/dist/src/backend/services/session/service/chat/chat-message-handlers/types.d.ts +24 -8
  316. package/dist/src/backend/services/session/service/chat/chat-message-handlers/types.d.ts.map +1 -1
  317. package/dist/src/backend/services/session/service/chat/chat-message-handlers.service.d.ts +14 -15
  318. package/dist/src/backend/services/session/service/chat/chat-message-handlers.service.d.ts.map +1 -1
  319. package/dist/src/backend/services/session/service/chat/chat-message-handlers.service.js +56 -114
  320. package/dist/src/backend/services/session/service/chat/chat-message-handlers.service.js.map +1 -1
  321. package/dist/src/backend/services/session/service/chat/chat-message-handlers.service.test.js +49 -218
  322. package/dist/src/backend/services/session/service/chat/chat-message-handlers.service.test.js.map +1 -1
  323. package/dist/src/backend/services/session/service/data/session-data.service.d.ts +0 -2
  324. package/dist/src/backend/services/session/service/data/session-data.service.d.ts.map +1 -1
  325. package/dist/src/backend/services/session/service/data/session-data.service.js +0 -6
  326. package/dist/src/backend/services/session/service/data/session-data.service.js.map +1 -1
  327. package/dist/src/backend/services/session/service/data/session-data.service.test.js +0 -6
  328. package/dist/src/backend/services/session/service/data/session-data.service.test.js.map +1 -1
  329. package/dist/src/backend/services/session/service/index.d.ts +3 -4
  330. package/dist/src/backend/services/session/service/index.d.ts.map +1 -1
  331. package/dist/src/backend/services/session/service/index.js +2 -2
  332. package/dist/src/backend/services/session/service/index.js.map +1 -1
  333. package/dist/src/backend/services/session/service/lifecycle/acp-event-processor.d.ts.map +1 -1
  334. package/dist/src/backend/services/session/service/lifecycle/acp-event-processor.js +9 -0
  335. package/dist/src/backend/services/session/service/lifecycle/acp-event-processor.js.map +1 -1
  336. package/dist/src/backend/services/session/service/lifecycle/acp-event-processor.subagents.test.js +47 -0
  337. package/dist/src/backend/services/session/service/lifecycle/acp-event-processor.subagents.test.js.map +1 -1
  338. package/dist/src/backend/services/session/service/lifecycle/session-context.service.d.ts +33 -0
  339. package/dist/src/backend/services/session/service/lifecycle/session-context.service.d.ts.map +1 -0
  340. package/dist/src/backend/services/session/service/lifecycle/session-context.service.js +120 -0
  341. package/dist/src/backend/services/session/service/lifecycle/session-context.service.js.map +1 -0
  342. package/dist/src/backend/services/session/service/lifecycle/session-context.service.test.d.ts +2 -0
  343. package/dist/src/backend/services/session/service/lifecycle/session-context.service.test.d.ts.map +1 -0
  344. package/dist/src/backend/services/session/service/lifecycle/session-context.service.test.js +174 -0
  345. package/dist/src/backend/services/session/service/lifecycle/session-context.service.test.js.map +1 -0
  346. package/dist/src/backend/services/session/service/lifecycle/session-core-services.d.ts +23 -0
  347. package/dist/src/backend/services/session/service/lifecycle/session-core-services.d.ts.map +1 -0
  348. package/dist/src/backend/services/session/service/lifecycle/session-core-services.js +150 -0
  349. package/dist/src/backend/services/session/service/lifecycle/session-core-services.js.map +1 -0
  350. package/dist/src/backend/services/session/service/lifecycle/session-lifecycle-external-ports.d.ts +5 -0
  351. package/dist/src/backend/services/session/service/lifecycle/session-lifecycle-external-ports.d.ts.map +1 -0
  352. package/dist/src/backend/services/session/service/lifecycle/session-lifecycle-external-ports.js +38 -0
  353. package/dist/src/backend/services/session/service/lifecycle/session-lifecycle-external-ports.js.map +1 -0
  354. package/dist/src/backend/services/session/service/lifecycle/session-lifecycle-external-ports.test.d.ts +2 -0
  355. package/dist/src/backend/services/session/service/lifecycle/session-lifecycle-external-ports.test.d.ts.map +1 -0
  356. package/dist/src/backend/services/session/service/lifecycle/session-lifecycle-external-ports.test.js +52 -0
  357. package/dist/src/backend/services/session/service/lifecycle/session-lifecycle-external-ports.test.js.map +1 -0
  358. package/dist/src/backend/services/session/service/lifecycle/session-lifecycle-gate.d.ts +35 -0
  359. package/dist/src/backend/services/session/service/lifecycle/session-lifecycle-gate.d.ts.map +1 -0
  360. package/dist/src/backend/services/session/service/lifecycle/session-lifecycle-gate.js +120 -0
  361. package/dist/src/backend/services/session/service/lifecycle/session-lifecycle-gate.js.map +1 -0
  362. package/dist/src/backend/services/session/service/lifecycle/session-lifecycle-gate.test.d.ts +2 -0
  363. package/dist/src/backend/services/session/service/lifecycle/session-lifecycle-gate.test.d.ts.map +1 -0
  364. package/dist/src/backend/services/session/service/lifecycle/session-lifecycle-gate.test.js +192 -0
  365. package/dist/src/backend/services/session/service/lifecycle/session-lifecycle-gate.test.js.map +1 -0
  366. package/dist/src/backend/services/session/service/lifecycle/session-lifecycle.test-helpers.d.ts +115 -0
  367. package/dist/src/backend/services/session/service/lifecycle/session-lifecycle.test-helpers.d.ts.map +1 -0
  368. package/dist/src/backend/services/session/service/lifecycle/session-lifecycle.test-helpers.js +434 -0
  369. package/dist/src/backend/services/session/service/lifecycle/session-lifecycle.test-helpers.js.map +1 -0
  370. package/dist/src/backend/services/session/service/lifecycle/session-lifecycle.types.d.ts +13 -0
  371. package/dist/src/backend/services/session/service/lifecycle/session-lifecycle.types.d.ts.map +1 -0
  372. package/dist/src/backend/services/session/service/lifecycle/session-lifecycle.types.js +2 -0
  373. package/dist/src/backend/services/session/service/lifecycle/session-lifecycle.types.js.map +1 -0
  374. package/dist/src/backend/services/session/service/lifecycle/session-notification-delivery.service.d.ts +81 -0
  375. package/dist/src/backend/services/session/service/lifecycle/session-notification-delivery.service.d.ts.map +1 -0
  376. package/dist/src/backend/services/session/service/lifecycle/session-notification-delivery.service.js +242 -0
  377. package/dist/src/backend/services/session/service/lifecycle/session-notification-delivery.service.js.map +1 -0
  378. package/dist/src/backend/services/session/service/lifecycle/session-notification-delivery.service.test.d.ts +2 -0
  379. package/dist/src/backend/services/session/service/lifecycle/session-notification-delivery.service.test.d.ts.map +1 -0
  380. package/dist/src/backend/services/session/service/lifecycle/session-notification-delivery.service.test.js +528 -0
  381. package/dist/src/backend/services/session/service/lifecycle/session-notification-delivery.service.test.js.map +1 -0
  382. package/dist/src/backend/services/session/service/lifecycle/session-runtime-exit.coordinator.d.ts +39 -0
  383. package/dist/src/backend/services/session/service/lifecycle/session-runtime-exit.coordinator.d.ts.map +1 -0
  384. package/dist/src/backend/services/session/service/lifecycle/session-runtime-exit.coordinator.js +208 -0
  385. package/dist/src/backend/services/session/service/lifecycle/session-runtime-exit.coordinator.js.map +1 -0
  386. package/dist/src/backend/services/session/service/lifecycle/session-runtime-exit.coordinator.test.d.ts +2 -0
  387. package/dist/src/backend/services/session/service/lifecycle/session-runtime-exit.coordinator.test.d.ts.map +1 -0
  388. package/dist/src/backend/services/session/service/lifecycle/session-runtime-exit.coordinator.test.js +274 -0
  389. package/dist/src/backend/services/session/service/lifecycle/session-runtime-exit.coordinator.test.js.map +1 -0
  390. package/dist/src/backend/services/session/service/lifecycle/session-services.composition.test.d.ts +2 -0
  391. package/dist/src/backend/services/session/service/lifecycle/session-services.composition.test.d.ts.map +1 -0
  392. package/dist/src/backend/services/session/service/lifecycle/session-services.composition.test.js +105 -0
  393. package/dist/src/backend/services/session/service/lifecycle/session-services.composition.test.js.map +1 -0
  394. package/dist/src/backend/services/session/service/lifecycle/session-services.d.ts +5 -17
  395. package/dist/src/backend/services/session/service/lifecycle/session-services.d.ts.map +1 -1
  396. package/dist/src/backend/services/session/service/lifecycle/session-services.js +14 -87
  397. package/dist/src/backend/services/session/service/lifecycle/session-services.js.map +1 -1
  398. package/dist/src/backend/services/session/service/lifecycle/session-startup.coordinator.d.ts +64 -0
  399. package/dist/src/backend/services/session/service/lifecycle/session-startup.coordinator.d.ts.map +1 -0
  400. package/dist/src/backend/services/session/service/lifecycle/session-startup.coordinator.js +352 -0
  401. package/dist/src/backend/services/session/service/lifecycle/session-startup.coordinator.js.map +1 -0
  402. package/dist/src/backend/services/session/service/lifecycle/session-startup.coordinator.test.d.ts +2 -0
  403. package/dist/src/backend/services/session/service/lifecycle/session-startup.coordinator.test.d.ts.map +1 -0
  404. package/dist/src/backend/services/session/service/lifecycle/session-startup.coordinator.test.js +702 -0
  405. package/dist/src/backend/services/session/service/lifecycle/session-startup.coordinator.test.js.map +1 -0
  406. package/dist/src/backend/services/session/service/lifecycle/session-termination.coordinator.d.ts +54 -0
  407. package/dist/src/backend/services/session/service/lifecycle/session-termination.coordinator.d.ts.map +1 -0
  408. package/dist/src/backend/services/session/service/lifecycle/session-termination.coordinator.js +265 -0
  409. package/dist/src/backend/services/session/service/lifecycle/session-termination.coordinator.js.map +1 -0
  410. package/dist/src/backend/services/session/service/lifecycle/session-termination.coordinator.test.d.ts +2 -0
  411. package/dist/src/backend/services/session/service/lifecycle/session-termination.coordinator.test.d.ts.map +1 -0
  412. package/dist/src/backend/services/session/service/lifecycle/session-termination.coordinator.test.js +514 -0
  413. package/dist/src/backend/services/session/service/lifecycle/session-termination.coordinator.test.js.map +1 -0
  414. package/dist/src/backend/services/session/service/lifecycle/session-workflow-finalizer.d.ts +62 -0
  415. package/dist/src/backend/services/session/service/lifecycle/session-workflow-finalizer.d.ts.map +1 -0
  416. package/dist/src/backend/services/session/service/lifecycle/session-workflow-finalizer.js +175 -0
  417. package/dist/src/backend/services/session/service/lifecycle/session-workflow-finalizer.js.map +1 -0
  418. package/dist/src/backend/services/session/service/lifecycle/session-workflow-finalizer.test.d.ts +2 -0
  419. package/dist/src/backend/services/session/service/lifecycle/session-workflow-finalizer.test.d.ts.map +1 -0
  420. package/dist/src/backend/services/session/service/lifecycle/session-workflow-finalizer.test.js +294 -0
  421. package/dist/src/backend/services/session/service/lifecycle/session-workflow-finalizer.test.js.map +1 -0
  422. package/dist/src/backend/services/session/service/lifecycle/session.lifecycle.facade.test.d.ts +2 -0
  423. package/dist/src/backend/services/session/service/lifecycle/session.lifecycle.facade.test.d.ts.map +1 -0
  424. package/dist/src/backend/services/session/service/lifecycle/session.lifecycle.facade.test.js +431 -0
  425. package/dist/src/backend/services/session/service/lifecycle/session.lifecycle.facade.test.js.map +1 -0
  426. package/dist/src/backend/services/session/service/lifecycle/session.lifecycle.service.d.ts +24 -116
  427. package/dist/src/backend/services/session/service/lifecycle/session.lifecycle.service.d.ts.map +1 -1
  428. package/dist/src/backend/services/session/service/lifecycle/session.lifecycle.service.js +47 -1168
  429. package/dist/src/backend/services/session/service/lifecycle/session.lifecycle.service.js.map +1 -1
  430. package/dist/src/backend/services/session/service/lifecycle/session.prompt.service.test.js +11 -6
  431. package/dist/src/backend/services/session/service/lifecycle/session.prompt.service.test.js.map +1 -1
  432. package/dist/src/backend/services/session/service/lifecycle/session.service.d.ts +3 -6
  433. package/dist/src/backend/services/session/service/lifecycle/session.service.d.ts.map +1 -1
  434. package/dist/src/backend/services/session/service/lifecycle/session.service.js +9 -12
  435. package/dist/src/backend/services/session/service/lifecycle/session.service.js.map +1 -1
  436. package/dist/src/backend/services/session/service/lifecycle/session.service.test.js +44 -35
  437. package/dist/src/backend/services/session/service/lifecycle/session.service.test.js.map +1 -1
  438. package/dist/src/backend/services/session/service/store/session-tool-call-recovery.d.ts +3 -0
  439. package/dist/src/backend/services/session/service/store/session-tool-call-recovery.d.ts.map +1 -0
  440. package/dist/src/backend/services/session/service/store/session-tool-call-recovery.js +92 -0
  441. package/dist/src/backend/services/session/service/store/session-tool-call-recovery.js.map +1 -0
  442. package/dist/src/backend/services/session/service/store/session-tool-call-recovery.test.d.ts +2 -0
  443. package/dist/src/backend/services/session/service/store/session-tool-call-recovery.test.d.ts.map +1 -0
  444. package/dist/src/backend/services/session/service/store/session-tool-call-recovery.test.js +81 -0
  445. package/dist/src/backend/services/session/service/store/session-tool-call-recovery.test.js.map +1 -0
  446. package/dist/src/backend/services/workspace/resources/project.accessor.d.ts +0 -1
  447. package/dist/src/backend/services/workspace/resources/project.accessor.d.ts.map +1 -1
  448. package/dist/src/backend/services/workspace/resources/project.accessor.js +0 -13
  449. package/dist/src/backend/services/workspace/resources/project.accessor.js.map +1 -1
  450. package/dist/src/backend/services/workspace/resources/workspace.accessor.d.ts +0 -1
  451. package/dist/src/backend/services/workspace/resources/workspace.accessor.d.ts.map +1 -1
  452. package/dist/src/backend/services/workspace/resources/workspace.accessor.js +0 -10
  453. package/dist/src/backend/services/workspace/resources/workspace.accessor.js.map +1 -1
  454. package/dist/src/backend/services/workspace/resources/workspace.accessor.test.js +3 -8
  455. package/dist/src/backend/services/workspace/resources/workspace.accessor.test.js.map +1 -1
  456. package/dist/src/backend/services/workspace/service/index.d.ts +9 -10
  457. package/dist/src/backend/services/workspace/service/index.d.ts.map +1 -1
  458. package/dist/src/backend/services/workspace/service/index.js +5 -5
  459. package/dist/src/backend/services/workspace/service/index.js.map +1 -1
  460. package/dist/src/backend/services/workspace/service/lifecycle/data.service.d.ts +4 -4
  461. package/dist/src/backend/services/workspace/service/workspace-domain-exports.test.js +1 -7
  462. package/dist/src/backend/services/workspace/service/workspace-domain-exports.test.js.map +1 -1
  463. package/dist/src/backend/services/workspace/service/worktree/git-ops.service.test.js +0 -1
  464. package/dist/src/backend/services/workspace/service/worktree/git-ops.service.test.js.map +1 -1
  465. package/dist/src/backend/testing/npm-publish-workflow.test.d.ts +2 -0
  466. package/dist/src/backend/testing/npm-publish-workflow.test.d.ts.map +1 -0
  467. package/dist/src/backend/testing/npm-publish-workflow.test.js +205 -0
  468. package/dist/src/backend/testing/npm-publish-workflow.test.js.map +1 -0
  469. package/dist/src/backend/trpc/index.d.ts +2 -18
  470. package/dist/src/backend/trpc/index.d.ts.map +1 -1
  471. package/dist/src/backend/trpc/pr-review.router.test.js +1 -12
  472. package/dist/src/backend/trpc/pr-review.router.test.js.map +1 -1
  473. package/dist/src/backend/trpc/pr-review.trpc.d.ts +0 -16
  474. package/dist/src/backend/trpc/pr-review.trpc.d.ts.map +1 -1
  475. package/dist/src/backend/trpc/pr-review.trpc.js +0 -14
  476. package/dist/src/backend/trpc/pr-review.trpc.js.map +1 -1
  477. package/dist/src/backend/trpc/session.router.test.js +13 -37
  478. package/dist/src/backend/trpc/session.router.test.js.map +1 -1
  479. package/dist/src/backend/trpc/workspace.router.test.js +41 -2
  480. package/dist/src/backend/trpc/workspace.router.test.js.map +1 -1
  481. package/dist/src/backend/trpc/workspace.trpc.d.ts +4 -4
  482. package/dist/src/backend/trpc/workspace.trpc.d.ts.map +1 -1
  483. package/dist/src/backend/trpc/workspace.trpc.js +12 -1
  484. package/dist/src/backend/trpc/workspace.trpc.js.map +1 -1
  485. package/dist/src/lib/chat-protocol.d.ts +72 -0
  486. package/dist/src/lib/chat-protocol.d.ts.map +1 -0
  487. package/dist/src/lib/chat-protocol.js +514 -0
  488. package/dist/src/lib/chat-protocol.js.map +1 -0
  489. package/dist/src/test-utils/application-graph.d.ts.map +1 -1
  490. package/dist/src/test-utils/application-graph.js +1 -0
  491. package/dist/src/test-utils/application-graph.js.map +1 -1
  492. package/dist/tsconfig.backend.tsbuildinfo +1 -1
  493. package/package.json +6 -3
  494. package/scripts/postinstall.mjs +6 -2
  495. package/dist/client/assets/channel-DLBBfejH.js +0 -1
  496. package/dist/client/assets/classDiagram-JCYQIIEL-Ds5wVQR3.js +0 -1
  497. package/dist/client/assets/classDiagram-v2-OCEON4UE-Ds5wVQR3.js +0 -1
  498. package/dist/client/assets/stateDiagram-v2-UEYNNEHI-BwUaUW2J.js +0 -1
  499. package/dist/client/assets/swimlanesDiagram-ULZ7WXOC-D87lLmuW.js +0 -8
  500. package/dist/src/backend/services/session/service/acp/acp-runtime-manager.test.d.ts +0 -2
  501. package/dist/src/backend/services/session/service/acp/acp-runtime-manager.test.d.ts.map +0 -1
  502. package/dist/src/backend/services/session/service/acp/acp-runtime-manager.test.js +0 -1736
  503. package/dist/src/backend/services/session/service/acp/acp-runtime-manager.test.js.map +0 -1
  504. package/dist/src/backend/services/session/service/lifecycle/session.lifecycle.service.test.d.ts +0 -2
  505. package/dist/src/backend/services/session/service/lifecycle/session.lifecycle.service.test.d.ts.map +0 -1
  506. package/dist/src/backend/services/session/service/lifecycle/session.lifecycle.service.test.js +0 -1520
  507. package/dist/src/backend/services/session/service/lifecycle/session.lifecycle.service.test.js.map +0 -1
  508. package/dist/src/backend/services/session/service/runtime/index.d.ts +0 -5
  509. package/dist/src/backend/services/session/service/runtime/index.d.ts.map +0 -1
  510. package/dist/src/backend/services/session/service/runtime/index.js +0 -2
  511. package/dist/src/backend/services/session/service/runtime/index.js.map +0 -1
  512. package/dist/src/backend/services/session/service/runtime/provider-runtime-manager.d.ts +0 -21
  513. package/dist/src/backend/services/session/service/runtime/provider-runtime-manager.d.ts.map +0 -1
  514. package/dist/src/backend/services/session/service/runtime/provider-runtime-manager.js +0 -2
  515. package/dist/src/backend/services/session/service/runtime/provider-runtime-manager.js.map +0 -1
  516. package/dist/src/backend/services/session/service/store/file-lock.service.d.ts +0 -97
  517. package/dist/src/backend/services/session/service/store/file-lock.service.d.ts.map +0 -1
  518. package/dist/src/backend/services/session/service/store/file-lock.service.js +0 -429
  519. package/dist/src/backend/services/session/service/store/file-lock.service.js.map +0 -1
  520. package/dist/src/backend/services/session/service/store/file-lock.service.test.d.ts +0 -2
  521. package/dist/src/backend/services/session/service/store/file-lock.service.test.d.ts.map +0 -1
  522. package/dist/src/backend/services/session/service/store/file-lock.service.test.js +0 -672
  523. package/dist/src/backend/services/session/service/store/file-lock.service.test.js.map +0 -1
  524. package/dist/src/shared/workspace-words.d.ts +0 -5
  525. package/dist/src/shared/workspace-words.d.ts.map +0 -1
  526. package/dist/src/shared/workspace-words.js +0 -146
  527. package/dist/src/shared/workspace-words.js.map +0 -1
@@ -1,1520 +0,0 @@
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';
5
- import { userSettingsService } from '../../../../../backend/services/settings/index.js';
6
- import { workspaceDataService, workspaceNotificationService } from '../../../../../backend/services/workspace/index.js';
7
- import { SessionStatus } from '../../../../../shared/core/index.js';
8
- import { unsafeCoerce } from '../../../../../test-utils/unsafe-coerce.js';
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';
17
- vi.mock('@/backend/services/logger.service', () => ({
18
- createLogger: () => ({
19
- debug: vi.fn(),
20
- info: vi.fn(),
21
- warn: vi.fn(),
22
- error: vi.fn(),
23
- }),
24
- getCurrentProcessEnv: () => ({ ...process.env }),
25
- }));
26
- vi.mock('@/backend/services/workspace', () => ({
27
- workspaceDataService: { findById: vi.fn() },
28
- workspaceNotificationService: {
29
- listPendingForDelivery: vi.fn(),
30
- markDelivered: vi.fn(),
31
- },
32
- }));
33
- vi.mock('@/backend/services/settings', () => ({
34
- userSettingsService: {
35
- get: vi.fn(async () => ({
36
- defaultWorkspacePermissions: 'STRICT',
37
- ratchetPermissions: 'YOLO',
38
- })),
39
- },
40
- }));
41
- function createLifecycleService(options) {
42
- const sessionDomainService = {
43
- appendClaudeEvent: vi.fn((_sessionId, _message) => 1),
44
- emitDelta: vi.fn(),
45
- hasQueuedMessage: vi.fn((_sessionId, _messageId) => false),
46
- enqueue: options?.enqueue ??
47
- vi.fn((_sessionId, _message) => ({ position: 0 })),
48
- getTranscriptSnapshot: vi.fn(() => options?.transcript ?? []),
49
- getHistoryHydrationSource: vi.fn(() => options?.historyHydrationSource ?? 'none'),
50
- };
51
- const tryDispatchNextMessage = options?.tryDispatchNextMessage ?? vi.fn(async () => undefined);
52
- const service = new SessionLifecycleService({
53
- repository: {},
54
- promptBuilder: {},
55
- runtimeManager: { isStopInProgress: vi.fn(() => false) },
56
- sessionDomainService: sessionDomainService,
57
- sessionPermissionService: {},
58
- sessionConfigService: {},
59
- acpEventProcessor: {},
60
- promptTurnCompletionService: {},
61
- retryService: {},
62
- sendSessionMessage: vi.fn(async () => undefined),
63
- lifecycleEventService: { hydrate: vi.fn(async () => undefined) },
64
- });
65
- service.configure({
66
- workspace: {
67
- markSessionRunning: vi.fn(),
68
- markSessionIdle: vi.fn(),
69
- recordRatchetSessionEnd: vi.fn(async () => undefined),
70
- resetPRDiscoveryBackoff: vi.fn(async () => true),
71
- },
72
- messageQueue: { tryDispatchNextMessage },
73
- });
74
- return { service, sessionDomainService, tryDispatchNextMessage };
75
- }
76
- async function deliverPendingChildNotifications(service, sessionId = 'session-1', workspaceId = 'workspace-1') {
77
- return await service.deliverPendingChildNotifications(sessionId, workspaceId);
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
- }
91
- function createStoppableLifecycleService() {
92
- const repository = {
93
- getSessionsByWorkspaceId: vi.fn(async () => [
94
- { id: 'session-running', status: SessionStatus.RUNNING },
95
- { id: 'session-runtime-only', status: SessionStatus.COMPLETED },
96
- { id: 'session-browse-only', status: SessionStatus.IDLE },
97
- { id: 'session-idle', status: SessionStatus.IDLE },
98
- ]),
99
- };
100
- const runtimeManager = {
101
- isSessionRunning: vi.fn((sessionId) => sessionId === 'session-runtime-only'),
102
- getBrowseClient: vi.fn(() => undefined),
103
- isBrowseOnlySession: vi.fn((sessionId) => sessionId === 'session-browse-only'),
104
- };
105
- const service = new SessionLifecycleService({
106
- repository: repository,
107
- promptBuilder: {},
108
- runtimeManager: runtimeManager,
109
- sessionDomainService: {},
110
- sessionPermissionService: {},
111
- sessionConfigService: {},
112
- acpEventProcessor: {},
113
- promptTurnCompletionService: {},
114
- retryService: {},
115
- sendSessionMessage: vi.fn(async () => undefined),
116
- lifecycleEventService: { hydrate: vi.fn(async () => undefined) },
117
- });
118
- const stopSession = vi.fn((_sessionId, _options) => Promise.resolve());
119
- service.stopSession = stopSession;
120
- return { service, repository, runtimeManager, stopSession };
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
- }
188
- describe('SessionLifecycleService stopWorkspaceSessions', () => {
189
- beforeEach(() => {
190
- vi.clearAllMocks();
191
- });
192
- it('stops running sessions and runtime-only sessions', async () => {
193
- const { service, repository, runtimeManager, stopSession } = createStoppableLifecycleService();
194
- await service.stopWorkspaceSessions('workspace-1');
195
- expect(repository.getSessionsByWorkspaceId).toHaveBeenCalledWith('workspace-1');
196
- expect(runtimeManager.isSessionRunning).toHaveBeenCalledWith('session-runtime-only');
197
- expect(runtimeManager.isSessionRunning).toHaveBeenCalledWith('session-idle');
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
- });
204
- expect(stopSession).not.toHaveBeenCalledWith('session-idle');
205
- });
206
- it('attempts every running session and throws when any stop fails', async () => {
207
- const { service, stopSession } = createStoppableLifecycleService();
208
- stopSession.mockImplementation((sessionId) => {
209
- if (sessionId === 'session-running') {
210
- return Promise.reject(new Error('stop failed'));
211
- }
212
- return Promise.resolve();
213
- });
214
- await expect(service.stopWorkspaceSessions('workspace-1')).rejects.toThrow('Failed to stop 1 workspace session');
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
- }
475
- });
476
- });
477
- describe('SessionLifecycleService pending workspace notifications', () => {
478
- beforeEach(() => {
479
- vi.clearAllMocks();
480
- });
481
- it('adds pending workspace notifications to the UI transcript and ACP dispatch queue', async () => {
482
- const createdAt = new Date('2026-06-22T10:30:00.000Z');
483
- vi.mocked(workspaceNotificationService.listPendingForDelivery).mockResolvedValue([
484
- {
485
- id: 'notif-parent',
486
- workspaceId: 'workspace-1',
487
- sourceWorkspaceId: 'parent-workspace',
488
- sourceWorkspaceName: 'Parent Workspace',
489
- sourceProjectName: 'Parent Project',
490
- message: 'Please check the failing test.',
491
- direction: 'PARENT_TO_CHILD',
492
- deliveredAt: null,
493
- createdAt,
494
- },
495
- {
496
- id: 'notif-child',
497
- workspaceId: 'workspace-1',
498
- sourceWorkspaceId: 'child-workspace',
499
- sourceWorkspaceName: 'Child Workspace',
500
- sourceProjectName: 'Child Project',
501
- message: 'The branch is ready for review.',
502
- direction: 'CHILD_TO_PARENT',
503
- deliveredAt: null,
504
- createdAt,
505
- },
506
- ]);
507
- vi.mocked(workspaceNotificationService.markDelivered).mockResolvedValue();
508
- const { service, sessionDomainService, tryDispatchNextMessage } = createLifecycleService();
509
- const enqueuedCount = await deliverPendingChildNotifications(service);
510
- expect(sessionDomainService.appendClaudeEvent).toHaveBeenNthCalledWith(1, 'session-1', expect.objectContaining({
511
- type: 'parent_workspace_update',
512
- parentWorkspaceId: 'parent-workspace',
513
- parentWorkspaceName: 'Parent Workspace',
514
- parentProjectName: 'Parent Project',
515
- text: 'Please check the failing test.',
516
- timestamp: '2026-06-22T10:30:00.000Z',
517
- }));
518
- expect(sessionDomainService.appendClaudeEvent).toHaveBeenNthCalledWith(2, 'session-1', expect.objectContaining({
519
- type: 'child_workspace_update',
520
- childWorkspaceId: 'child-workspace',
521
- childWorkspaceName: 'Child Workspace',
522
- childProjectName: 'Child Project',
523
- text: 'The branch is ready for review.',
524
- timestamp: '2026-06-22T10:30:00.000Z',
525
- }));
526
- expect(sessionDomainService.enqueue).toHaveBeenNthCalledWith(1, 'session-1', expect.objectContaining({
527
- id: 'workspace-notification-notif-parent',
528
- text: '[Message from parent workspace "Parent Workspace"]: Please check the failing test.\n\n<!-- factory-factory-workspace-notification:notif-parent -->',
529
- timestamp: '2026-06-22T10:30:00.000Z',
530
- settings: {
531
- selectedModel: null,
532
- reasoningEffort: null,
533
- thinkingEnabled: false,
534
- planModeEnabled: false,
535
- },
536
- }));
537
- expect(sessionDomainService.enqueue).toHaveBeenNthCalledWith(2, 'session-1', expect.objectContaining({
538
- id: 'workspace-notification-notif-child',
539
- text: '[Message from child workspace "Child Workspace"]: The branch is ready for review.\n\n<!-- factory-factory-workspace-notification:notif-child -->',
540
- }));
541
- expect(enqueuedCount).toBe(2);
542
- expect(tryDispatchNextMessage).not.toHaveBeenCalled();
543
- expect(workspaceNotificationService.markDelivered).not.toHaveBeenCalled();
544
- });
545
- it('does not enqueue notifications found after the startup stop generation changes', async () => {
546
- let resolvePending;
547
- vi.mocked(workspaceNotificationService.listPendingForDelivery).mockReturnValue(new Promise((resolve) => {
548
- resolvePending = resolve;
549
- }));
550
- const { service, sessionDomainService } = createLifecycleService();
551
- const deliveryPromise = deliverPendingChildNotifications(service);
552
- await vi.waitFor(() => {
553
- expect(workspaceNotificationService.listPendingForDelivery).toHaveBeenCalledWith('workspace-1');
554
- });
555
- getStopGenerations(service).set('session-1', service.getStopGeneration('session-1') + 1);
556
- resolvePending([
557
- {
558
- id: 'notif-parent',
559
- workspaceId: 'workspace-1',
560
- sourceWorkspaceId: 'parent-workspace',
561
- sourceWorkspaceName: 'Parent Workspace',
562
- sourceProjectName: 'Parent Project',
563
- message: 'Please check the failing test.',
564
- direction: 'PARENT_TO_CHILD',
565
- deliveredAt: null,
566
- createdAt: new Date('2026-06-22T10:30:00.000Z'),
567
- },
568
- ]);
569
- await expect(deliveryPromise).resolves.toBe(0);
570
- expect(sessionDomainService.enqueue).not.toHaveBeenCalled();
571
- expect(sessionDomainService.appendClaudeEvent).not.toHaveBeenCalled();
572
- expect(sessionDomainService.emitDelta).not.toHaveBeenCalled();
573
- });
574
- it('leaves notifications pending when enqueue fails', async () => {
575
- vi.mocked(workspaceNotificationService.listPendingForDelivery).mockResolvedValue([
576
- {
577
- id: 'notif-parent',
578
- workspaceId: 'workspace-1',
579
- sourceWorkspaceId: 'parent-workspace',
580
- sourceWorkspaceName: 'Parent Workspace',
581
- sourceProjectName: 'Parent Project',
582
- message: 'Please check the failing test.',
583
- direction: 'PARENT_TO_CHILD',
584
- deliveredAt: null,
585
- createdAt: new Date('2026-06-22T10:30:00.000Z'),
586
- },
587
- ]);
588
- const { service, sessionDomainService, tryDispatchNextMessage } = createLifecycleService({
589
- enqueue: vi.fn(() => ({ error: 'Queue full' })),
590
- });
591
- const enqueuedCount = await deliverPendingChildNotifications(service);
592
- expect(enqueuedCount).toBe(0);
593
- expect(sessionDomainService.appendClaudeEvent).not.toHaveBeenCalled();
594
- expect(sessionDomainService.emitDelta).not.toHaveBeenCalled();
595
- expect(tryDispatchNextMessage).not.toHaveBeenCalled();
596
- expect(workspaceNotificationService.markDelivered).not.toHaveBeenCalled();
597
- });
598
- it('does not dispatch pending notifications ahead of an existing queued message', async () => {
599
- vi.mocked(workspaceNotificationService.listPendingForDelivery).mockResolvedValue([
600
- {
601
- id: 'notif-parent',
602
- workspaceId: 'workspace-1',
603
- sourceWorkspaceId: 'parent-workspace',
604
- sourceWorkspaceName: 'Parent Workspace',
605
- sourceProjectName: 'Parent Project',
606
- message: 'Please check the failing test.',
607
- direction: 'PARENT_TO_CHILD',
608
- deliveredAt: null,
609
- createdAt: new Date('2026-06-22T10:30:00.000Z'),
610
- },
611
- ]);
612
- vi.mocked(workspaceNotificationService.markDelivered).mockResolvedValue();
613
- const { service, sessionDomainService, tryDispatchNextMessage } = createLifecycleService();
614
- const enqueuedCount = await deliverPendingChildNotifications(service);
615
- expect(enqueuedCount).toBe(1);
616
- expect(sessionDomainService.enqueue).toHaveBeenCalledWith('session-1', expect.objectContaining({
617
- id: 'workspace-notification-notif-parent',
618
- }));
619
- expect(tryDispatchNextMessage).not.toHaveBeenCalled();
620
- expect(workspaceNotificationService.markDelivered).not.toHaveBeenCalled();
621
- });
622
- it('reports a pending workspace notification that is already queued as dispatchable', async () => {
623
- vi.mocked(workspaceNotificationService.listPendingForDelivery).mockResolvedValue([
624
- {
625
- id: 'notif-parent',
626
- workspaceId: 'workspace-1',
627
- sourceWorkspaceId: 'parent-workspace',
628
- sourceWorkspaceName: 'Parent Workspace',
629
- sourceProjectName: 'Parent Project',
630
- message: 'Please check the failing test.',
631
- direction: 'PARENT_TO_CHILD',
632
- deliveredAt: null,
633
- createdAt: new Date('2026-06-22T10:30:00.000Z'),
634
- },
635
- ]);
636
- const { service, sessionDomainService, tryDispatchNextMessage } = createLifecycleService();
637
- sessionDomainService.hasQueuedMessage.mockReturnValue(true);
638
- const enqueuedCount = await deliverPendingChildNotifications(service);
639
- expect(enqueuedCount).toBe(1);
640
- expect(sessionDomainService.enqueue).not.toHaveBeenCalled();
641
- expect(sessionDomainService.appendClaudeEvent).not.toHaveBeenCalled();
642
- expect(sessionDomainService.emitDelta).not.toHaveBeenCalled();
643
- expect(tryDispatchNextMessage).not.toHaveBeenCalled();
644
- expect(workspaceNotificationService.markDelivered).not.toHaveBeenCalled();
645
- });
646
- it('does not re-enqueue a notification queued during transcript reconciliation', async () => {
647
- vi.mocked(workspaceNotificationService.listPendingForDelivery).mockResolvedValue([
648
- {
649
- id: 'notif-parent',
650
- workspaceId: 'workspace-1',
651
- sourceWorkspaceId: 'parent-workspace',
652
- sourceWorkspaceName: 'Parent Workspace',
653
- sourceProjectName: 'Parent Project',
654
- message: 'Please check the failing test.',
655
- direction: 'PARENT_TO_CHILD',
656
- deliveredAt: null,
657
- createdAt: new Date('2026-06-22T10:30:00.000Z'),
658
- },
659
- ]);
660
- const { service, sessionDomainService } = createLifecycleService();
661
- let queuedByLiveDelivery = false;
662
- sessionDomainService.hasQueuedMessage.mockImplementation(() => queuedByLiveDelivery);
663
- sessionDomainService.getTranscriptSnapshot.mockImplementation(() => {
664
- queuedByLiveDelivery = true;
665
- return [];
666
- });
667
- const dispatchableCount = await deliverPendingChildNotifications(service);
668
- expect(dispatchableCount).toBe(1);
669
- expect(sessionDomainService.hasQueuedMessage).toHaveBeenCalledTimes(2);
670
- expect(sessionDomainService.enqueue).not.toHaveBeenCalled();
671
- expect(sessionDomainService.appendClaudeEvent).not.toHaveBeenCalled();
672
- expect(sessionDomainService.emitDelta).not.toHaveBeenCalled();
673
- });
674
- it('marks an already-committed pending notification delivered without requeueing it', async () => {
675
- const createdAt = new Date('2026-06-22T10:30:00.000Z');
676
- vi.mocked(workspaceNotificationService.listPendingForDelivery).mockResolvedValue([
677
- {
678
- id: 'notif-parent',
679
- workspaceId: 'workspace-1',
680
- sourceWorkspaceId: 'parent-workspace',
681
- sourceWorkspaceName: 'Parent Workspace',
682
- sourceProjectName: 'Parent Project',
683
- message: 'Please check the failing test.',
684
- direction: 'PARENT_TO_CHILD',
685
- deliveredAt: null,
686
- createdAt,
687
- },
688
- ]);
689
- vi.mocked(workspaceNotificationService.markDelivered).mockResolvedValue();
690
- const { service, sessionDomainService, tryDispatchNextMessage } = createLifecycleService({
691
- transcript: [
692
- {
693
- id: 'workspace-notification-notif-parent',
694
- source: 'user',
695
- text: '[Message from parent workspace "Parent Workspace"]: Please check the failing test.',
696
- timestamp: createdAt.toISOString(),
697
- order: 1,
698
- },
699
- ],
700
- });
701
- const enqueuedCount = await deliverPendingChildNotifications(service);
702
- expect(enqueuedCount).toBe(0);
703
- expect(sessionDomainService.enqueue).not.toHaveBeenCalled();
704
- expect(sessionDomainService.appendClaudeEvent).not.toHaveBeenCalled();
705
- expect(sessionDomainService.emitDelta).not.toHaveBeenCalled();
706
- expect(tryDispatchNextMessage).not.toHaveBeenCalled();
707
- expect(workspaceNotificationService.markDelivered).toHaveBeenCalledWith(['notif-parent']);
708
- });
709
- it('matches an already-committed pending notification with a provider-generated ID', async () => {
710
- const createdAt = new Date('2026-06-22T10:30:00.000Z');
711
- vi.mocked(workspaceNotificationService.listPendingForDelivery).mockResolvedValue([
712
- {
713
- id: 'notif-parent',
714
- workspaceId: 'workspace-1',
715
- sourceWorkspaceId: 'parent-workspace',
716
- sourceWorkspaceName: 'Parent Workspace',
717
- sourceProjectName: 'Parent Project',
718
- message: 'Please check the failing test.',
719
- direction: 'PARENT_TO_CHILD',
720
- deliveredAt: null,
721
- createdAt,
722
- },
723
- ]);
724
- vi.mocked(workspaceNotificationService.markDelivered).mockResolvedValue();
725
- const { service, sessionDomainService } = createLifecycleService({
726
- historyHydrationSource: 'jsonl',
727
- transcript: [
728
- {
729
- id: '550e8400-e29b-41d4-a716-446655440000-0',
730
- source: 'user',
731
- text: '[Message from parent workspace "Parent Workspace"]: Please check the failing test.\n\n<!-- factory-factory-workspace-notification:notif-parent -->',
732
- timestamp: createdAt.toISOString(),
733
- order: 0,
734
- },
735
- ],
736
- });
737
- const enqueuedCount = await deliverPendingChildNotifications(service);
738
- expect(enqueuedCount).toBe(0);
739
- expect(sessionDomainService.enqueue).not.toHaveBeenCalled();
740
- expect(sessionDomainService.appendClaudeEvent).not.toHaveBeenCalled();
741
- expect(sessionDomainService.emitDelta).not.toHaveBeenCalled();
742
- expect(workspaceNotificationService.markDelivered).toHaveBeenCalledWith(['notif-parent']);
743
- });
744
- it('does not match identical user text without a notification marker', async () => {
745
- const createdAt = new Date('2026-06-22T10:30:00.000Z');
746
- vi.mocked(workspaceNotificationService.listPendingForDelivery).mockResolvedValue([
747
- {
748
- id: 'notif-parent',
749
- workspaceId: 'workspace-1',
750
- sourceWorkspaceId: 'parent-workspace',
751
- sourceWorkspaceName: 'Parent Workspace',
752
- sourceProjectName: 'Parent Project',
753
- message: 'Please check the failing test.',
754
- direction: 'PARENT_TO_CHILD',
755
- deliveredAt: null,
756
- createdAt,
757
- },
758
- ]);
759
- const { service, sessionDomainService } = createLifecycleService({
760
- historyHydrationSource: 'jsonl',
761
- transcript: [
762
- {
763
- id: '550e8400-e29b-41d4-a716-446655440000-0',
764
- source: 'user',
765
- text: '[Message from parent workspace "Parent Workspace"]: Please check the failing test.',
766
- timestamp: createdAt.toISOString(),
767
- order: 0,
768
- },
769
- ],
770
- });
771
- const enqueuedCount = await deliverPendingChildNotifications(service);
772
- expect(enqueuedCount).toBe(1);
773
- expect(sessionDomainService.enqueue).toHaveBeenCalledOnce();
774
- expect(workspaceNotificationService.markDelivered).not.toHaveBeenCalled();
775
- });
776
- it('consumes one provider-generated transcript entry once for duplicate pending notifications', async () => {
777
- const oldestCreatedAt = new Date('2026-06-22T10:30:00.000Z');
778
- const newestCreatedAt = new Date('2026-06-22T10:31:00.000Z');
779
- vi.mocked(workspaceNotificationService.listPendingForDelivery).mockResolvedValue([
780
- {
781
- id: 'notif-parent-oldest',
782
- workspaceId: 'workspace-1',
783
- sourceWorkspaceId: 'parent-workspace',
784
- sourceWorkspaceName: 'Parent Workspace',
785
- sourceProjectName: 'Parent Project',
786
- message: 'Please check the failing test.',
787
- direction: 'PARENT_TO_CHILD',
788
- deliveredAt: null,
789
- createdAt: oldestCreatedAt,
790
- },
791
- {
792
- id: 'notif-parent-newest',
793
- workspaceId: 'workspace-1',
794
- sourceWorkspaceId: 'parent-workspace',
795
- sourceWorkspaceName: 'Parent Workspace',
796
- sourceProjectName: 'Parent Project',
797
- message: 'Please check the failing test.',
798
- direction: 'PARENT_TO_CHILD',
799
- deliveredAt: null,
800
- createdAt: newestCreatedAt,
801
- },
802
- ]);
803
- vi.mocked(workspaceNotificationService.markDelivered).mockResolvedValue();
804
- const { service, sessionDomainService } = createLifecycleService({
805
- historyHydrationSource: 'jsonl',
806
- transcript: [
807
- {
808
- id: '550e8400-e29b-41d4-a716-446655440000-0',
809
- source: 'user',
810
- text: '[Message from parent workspace "Parent Workspace"]: Please check the failing test.\n\n<!-- factory-factory-workspace-notification:notif-parent-oldest -->',
811
- timestamp: oldestCreatedAt.toISOString(),
812
- order: 0,
813
- },
814
- ],
815
- });
816
- const enqueuedCount = await deliverPendingChildNotifications(service);
817
- expect(enqueuedCount).toBe(1);
818
- expect(sessionDomainService.enqueue).toHaveBeenCalledOnce();
819
- expect(workspaceNotificationService.markDelivered).toHaveBeenCalledTimes(1);
820
- expect(workspaceNotificationService.markDelivered).toHaveBeenCalledWith([
821
- 'notif-parent-oldest',
822
- ]);
823
- });
824
- it("does not let an older duplicate consume a later notification's exact transcript entry", async () => {
825
- const oldestCreatedAt = new Date('2026-06-22T10:30:00.000Z');
826
- const newestCreatedAt = new Date('2026-06-22T10:31:00.000Z');
827
- vi.mocked(workspaceNotificationService.listPendingForDelivery).mockResolvedValue([
828
- {
829
- id: 'notif-parent-A',
830
- workspaceId: 'workspace-1',
831
- sourceWorkspaceId: 'parent-workspace',
832
- sourceWorkspaceName: 'Parent Workspace',
833
- sourceProjectName: 'Parent Project',
834
- message: 'Please check the failing test.',
835
- direction: 'PARENT_TO_CHILD',
836
- deliveredAt: null,
837
- createdAt: oldestCreatedAt,
838
- },
839
- {
840
- id: 'notif-parent-B',
841
- workspaceId: 'workspace-1',
842
- sourceWorkspaceId: 'parent-workspace',
843
- sourceWorkspaceName: 'Parent Workspace',
844
- sourceProjectName: 'Parent Project',
845
- message: 'Please check the failing test.',
846
- direction: 'PARENT_TO_CHILD',
847
- deliveredAt: null,
848
- createdAt: newestCreatedAt,
849
- },
850
- ]);
851
- vi.mocked(workspaceNotificationService.markDelivered).mockResolvedValue();
852
- const { service, sessionDomainService } = createLifecycleService({
853
- historyHydrationSource: 'jsonl',
854
- transcript: [
855
- {
856
- id: 'workspace-notification-notif-parent-B',
857
- source: 'user',
858
- text: '[Message from parent workspace "Parent Workspace"]: Please check the failing test.',
859
- timestamp: newestCreatedAt.toISOString(),
860
- order: 0,
861
- },
862
- ],
863
- });
864
- const enqueuedCount = await deliverPendingChildNotifications(service);
865
- expect(enqueuedCount).toBe(1);
866
- expect(sessionDomainService.enqueue).toHaveBeenCalledOnce();
867
- expect(sessionDomainService.enqueue).toHaveBeenCalledWith('session-1', expect.objectContaining({ id: 'workspace-notification-notif-parent-A' }));
868
- expect(workspaceNotificationService.markDelivered).toHaveBeenCalledTimes(1);
869
- expect(workspaceNotificationService.markDelivered).toHaveBeenCalledWith(['notif-parent-B']);
870
- });
871
- it('does not content-match a normal live user entry with canonical notification text', async () => {
872
- const createdAt = new Date('2026-06-22T10:30:00.000Z');
873
- vi.mocked(workspaceNotificationService.listPendingForDelivery).mockResolvedValue([
874
- {
875
- id: 'notif-parent',
876
- workspaceId: 'workspace-1',
877
- sourceWorkspaceId: 'parent-workspace',
878
- sourceWorkspaceName: 'Parent Workspace',
879
- sourceProjectName: 'Parent Project',
880
- message: 'Please check the failing test.',
881
- direction: 'PARENT_TO_CHILD',
882
- deliveredAt: null,
883
- createdAt,
884
- },
885
- ]);
886
- const { service, sessionDomainService } = createLifecycleService({
887
- transcript: [
888
- {
889
- id: 'session-1-42',
890
- source: 'user',
891
- text: '[Message from parent workspace "Parent Workspace"]: Please check the failing test.',
892
- timestamp: createdAt.toISOString(),
893
- order: 0,
894
- },
895
- ],
896
- });
897
- const enqueuedCount = await deliverPendingChildNotifications(service);
898
- expect(enqueuedCount).toBe(1);
899
- expect(sessionDomainService.enqueue).toHaveBeenCalledOnce();
900
- expect(workspaceNotificationService.markDelivered).not.toHaveBeenCalled();
901
- });
902
- it('does not requeue an already-committed pending notification when delivery retry fails', async () => {
903
- const createdAt = new Date('2026-06-22T10:30:00.000Z');
904
- vi.mocked(workspaceNotificationService.listPendingForDelivery).mockResolvedValue([
905
- {
906
- id: 'notif-parent',
907
- workspaceId: 'workspace-1',
908
- sourceWorkspaceId: 'parent-workspace',
909
- sourceWorkspaceName: 'Parent Workspace',
910
- sourceProjectName: 'Parent Project',
911
- message: 'Please check the failing test.',
912
- direction: 'PARENT_TO_CHILD',
913
- deliveredAt: null,
914
- createdAt,
915
- },
916
- ]);
917
- vi.mocked(workspaceNotificationService.markDelivered).mockRejectedValue(new Error('database unavailable'));
918
- const { service, sessionDomainService } = createLifecycleService({
919
- transcript: [
920
- {
921
- id: 'workspace-notification-notif-parent',
922
- source: 'user',
923
- text: '[Message from parent workspace "Parent Workspace"]: Please check the failing test.',
924
- timestamp: createdAt.toISOString(),
925
- order: 1,
926
- },
927
- ],
928
- });
929
- const enqueuedCount = await deliverPendingChildNotifications(service);
930
- expect(enqueuedCount).toBe(0);
931
- expect(sessionDomainService.enqueue).not.toHaveBeenCalled();
932
- expect(sessionDomainService.appendClaudeEvent).not.toHaveBeenCalled();
933
- expect(sessionDomainService.emitDelta).not.toHaveBeenCalled();
934
- expect(workspaceNotificationService.markDelivered).toHaveBeenCalledWith(['notif-parent']);
935
- });
936
- it('does not treat UI-only workspace update cards as delivered user messages', async () => {
937
- const createdAt = new Date('2026-06-22T10:30:00.000Z');
938
- vi.mocked(workspaceNotificationService.listPendingForDelivery).mockResolvedValue([
939
- {
940
- id: 'notif-parent',
941
- workspaceId: 'workspace-1',
942
- sourceWorkspaceId: 'parent-workspace',
943
- sourceWorkspaceName: 'Parent Workspace',
944
- sourceProjectName: 'Parent Project',
945
- message: 'Please check the failing test.',
946
- direction: 'PARENT_TO_CHILD',
947
- deliveredAt: null,
948
- createdAt,
949
- },
950
- ]);
951
- vi.mocked(workspaceNotificationService.markDelivered).mockResolvedValue();
952
- const { service, sessionDomainService } = createLifecycleService({
953
- transcript: [
954
- {
955
- id: 'session-1-1',
956
- source: 'agent',
957
- message: {
958
- type: 'parent_workspace_update',
959
- parentWorkspaceId: 'parent-workspace',
960
- parentWorkspaceName: 'Parent Workspace',
961
- parentProjectName: 'Parent Project',
962
- text: 'Please check the failing test.',
963
- timestamp: createdAt.toISOString(),
964
- },
965
- timestamp: createdAt.toISOString(),
966
- order: 1,
967
- },
968
- ],
969
- });
970
- const enqueuedCount = await deliverPendingChildNotifications(service);
971
- expect(enqueuedCount).toBe(1);
972
- expect(sessionDomainService.enqueue).toHaveBeenCalledWith('session-1', expect.objectContaining({
973
- id: 'workspace-notification-notif-parent',
974
- }));
975
- expect(workspaceNotificationService.markDelivered).not.toHaveBeenCalled();
976
- });
977
- });
978
- function createStartableLifecycleService(options) {
979
- const session = {
980
- id: 'session-1',
981
- workspaceId: 'workspace-1',
982
- workflow: 'code',
983
- provider: options?.provider ?? 'CLAUDE',
984
- providerSessionId: options?.providerSessionId ?? null,
985
- providerMetadata: null,
986
- model: 'claude-sonnet',
987
- };
988
- const workspace = {
989
- id: 'workspace-1',
990
- name: 'Workspace',
991
- description: null,
992
- projectId: 'project-1',
993
- worktreePath: options?.worktreePath === undefined ? '/tmp/workspace' : options.worktreePath,
994
- branchName: 'feature/test',
995
- isAutoGeneratedBranch: false,
996
- hasHadSessions: false,
997
- runScriptPort: null,
998
- parentWorkspaceId: null,
999
- creationMetadata: null,
1000
- };
1001
- const handle = {
1002
- provider: options?.provider ?? 'CLAUDE',
1003
- providerSessionId: options?.providerSessionId ?? 'provider-session-1',
1004
- configOptions: [],
1005
- isPromptInFlight: false,
1006
- getSubagentBrowseCapability: vi.fn(() => ({
1007
- version: 1,
1008
- list: true,
1009
- read: true,
1010
- notifications: true,
1011
- })),
1012
- };
1013
- const repository = {
1014
- getSessionById: vi.fn(async () => session),
1015
- getWorkspaceById: vi.fn(async () => workspace),
1016
- getProjectById: vi.fn(),
1017
- markWorkspaceHasHadSessions: vi.fn(async () => undefined),
1018
- updateSession: vi.fn(async () => session),
1019
- updateSessionIfStatus: vi.fn(async () => null),
1020
- };
1021
- const promptBuilder = {
1022
- shouldInjectBranchRename: vi.fn(() => false),
1023
- buildSystemPrompt: vi.fn(() => ({
1024
- workflowPrompt: undefined,
1025
- systemPrompt: 'system prompt',
1026
- injectedBranchRename: false,
1027
- })),
1028
- };
1029
- const runtimeManager = {
1030
- isStopInProgress: vi.fn(() => false),
1031
- isSessionRunning: vi.fn(() => false),
1032
- isBrowseOnlySession: vi.fn(() => false),
1033
- getClient: vi.fn(() => undefined),
1034
- getBrowseClient: vi.fn(() => undefined),
1035
- getPendingClient: vi.fn(() => undefined),
1036
- getSubagentBrowseCapability: vi.fn(() => null),
1037
- getOrCreateClient: vi.fn(async (_sessionId, _options) => handle),
1038
- stopClient: vi.fn(async () => undefined),
1039
- isSessionWorking: vi.fn(() => false),
1040
- };
1041
- const sessionDomainService = {
1042
- setRuntimeSnapshot: vi.fn(),
1043
- emitDelta: vi.fn(),
1044
- isHistoryHydrated: vi.fn(() => false),
1045
- getTranscriptSnapshot: vi.fn(() => []),
1046
- getRuntimeSnapshot: vi.fn(() => ({
1047
- phase: 'idle',
1048
- processState: 'stopped',
1049
- activity: 'IDLE',
1050
- updatedAt: '2026-07-15T00:00:00.000Z',
1051
- })),
1052
- clearQueuedWork: vi.fn(),
1053
- clearSession: vi.fn(),
1054
- markProcessExit: vi.fn(),
1055
- };
1056
- const sessionConfigService = {
1057
- applyConfiguredReasoningEffort: vi.fn(async () => undefined),
1058
- applyStartupModePreset: vi.fn(async () => undefined),
1059
- applyConfiguredPermissionPreset: vi.fn(async () => undefined),
1060
- persistAcpConfigSnapshot: vi.fn(async () => undefined),
1061
- buildAcpChatBarCapabilities: vi.fn(() => ({})),
1062
- };
1063
- const acpEventProcessor = {
1064
- createRuntimeEventHandler: vi.fn(() => ({})),
1065
- registerSessionContext: vi.fn(),
1066
- setReplaySuppression: vi.fn(),
1067
- clearSessionState: vi.fn(),
1068
- clearStreamingState: vi.fn(),
1069
- clearReplaySuppression: vi.fn(),
1070
- finalizeOrphanedToolCalls: vi.fn(),
1071
- clearSessionContext: vi.fn(),
1072
- };
1073
- const tryDispatchNextMessage = vi.fn(options?.tryDispatchNextMessage ?? (async () => undefined));
1074
- const sendSessionMessage = vi.fn(async () => undefined);
1075
- const service = new SessionLifecycleService({
1076
- repository: repository,
1077
- promptBuilder: promptBuilder,
1078
- runtimeManager: runtimeManager,
1079
- sessionDomainService: sessionDomainService,
1080
- sessionPermissionService: { cancelPendingRequests: vi.fn() },
1081
- sessionConfigService: sessionConfigService,
1082
- acpEventProcessor: acpEventProcessor,
1083
- promptTurnCompletionService: { clearSession: vi.fn() },
1084
- retryService: {
1085
- run: vi.fn(async (operation) => await operation()),
1086
- },
1087
- sendSessionMessage,
1088
- lifecycleEventService: { hydrate: vi.fn(async () => undefined), record: vi.fn() },
1089
- });
1090
- service.configure({
1091
- workspace: {
1092
- markSessionRunning: vi.fn(),
1093
- markSessionIdle: vi.fn(),
1094
- recordRatchetSessionEnd: vi.fn(async () => undefined),
1095
- resetPRDiscoveryBackoff: vi.fn(async () => true),
1096
- },
1097
- messageQueue: { tryDispatchNextMessage },
1098
- });
1099
- const deliverPendingChildNotifications = vi.fn(async () => options?.pendingNotificationCount ?? 0);
1100
- service.deliverPendingChildNotifications = deliverPendingChildNotifications;
1101
- return {
1102
- service,
1103
- session,
1104
- handle,
1105
- repository,
1106
- sendSessionMessage,
1107
- tryDispatchNextMessage,
1108
- sessionConfigService,
1109
- runtimeManager,
1110
- sessionDomainService,
1111
- acpEventProcessor,
1112
- deliverPendingChildNotifications,
1113
- };
1114
- }
1115
- describe('SessionLifecycleService startSession pending workspace notifications', () => {
1116
- beforeEach(() => {
1117
- vi.clearAllMocks();
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
- });
1360
- it('dispatches queued notifications after startup presets and skips the default continue prompt', async () => {
1361
- const { service, sendSessionMessage, tryDispatchNextMessage, sessionConfigService } = createStartableLifecycleService({ pendingNotificationCount: 2 });
1362
- await service.startSession('session-1');
1363
- expect(tryDispatchNextMessage).toHaveBeenCalledWith('session-1');
1364
- expect(sendSessionMessage).not.toHaveBeenCalled();
1365
- const startupPresetOrder = sessionConfigService.applyStartupModePreset.mock.invocationCallOrder[0];
1366
- const permissionPresetOrder = sessionConfigService.applyConfiguredPermissionPreset.mock.invocationCallOrder[0];
1367
- const dispatchOrder = tryDispatchNextMessage.mock.invocationCallOrder[0];
1368
- expect(startupPresetOrder).toBeDefined();
1369
- expect(permissionPresetOrder).toBeDefined();
1370
- expect(dispatchOrder).toBeDefined();
1371
- expect(startupPresetOrder).toBeLessThan(dispatchOrder);
1372
- expect(permissionPresetOrder).toBeLessThan(dispatchOrder);
1373
- });
1374
- it('still sends an explicit initial prompt after queued notification dispatch starts', async () => {
1375
- const { service, sendSessionMessage, tryDispatchNextMessage } = createStartableLifecycleService({
1376
- pendingNotificationCount: 1,
1377
- });
1378
- await service.startSession('session-1', { initialPrompt: 'Follow up' });
1379
- expect(tryDispatchNextMessage).toHaveBeenCalledWith('session-1');
1380
- expect(sendSessionMessage).toHaveBeenCalledWith('session-1', 'Follow up');
1381
- const dispatchOrder = tryDispatchNextMessage.mock.invocationCallOrder[0];
1382
- const sendOrder = sendSessionMessage.mock.invocationCallOrder[0];
1383
- expect(dispatchOrder).toBeDefined();
1384
- expect(sendOrder).toBeDefined();
1385
- expect(dispatchOrder).toBeLessThan(sendOrder);
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
- });
1403
- it('does not create a client after stop completes during permission resolution', async () => {
1404
- let resolveSettings;
1405
- const pendingSettings = new Promise((resolve) => {
1406
- resolveSettings = resolve;
1407
- });
1408
- vi.mocked(userSettingsService.get).mockReturnValueOnce(pendingSettings);
1409
- const { service, sendSessionMessage, runtimeManager } = createStartableLifecycleService();
1410
- const startResult = service.startSession('session-1').catch((error) => error);
1411
- await vi.waitFor(() => {
1412
- expect(userSettingsService.get).toHaveBeenCalled();
1413
- });
1414
- await service.stopSession('session-1');
1415
- expect(getStopGenerations(service).has('session-1')).toBe(false);
1416
- resolveSettings(unsafeCoerce({
1417
- defaultWorkspacePermissions: 'STRICT',
1418
- ratchetPermissions: 'YOLO',
1419
- }));
1420
- await expect(startResult).resolves.toEqual(expect.objectContaining({ message: 'Session is currently being stopped' }));
1421
- expect(runtimeManager.getOrCreateClient).not.toHaveBeenCalled();
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);
1459
- });
1460
- it('waits for a registered client creation and stops the resulting runtime', async () => {
1461
- const { service, sendSessionMessage, runtimeManager } = createStartableLifecycleService();
1462
- let resolveClient;
1463
- const pendingClient = new Promise((resolve) => {
1464
- resolveClient = resolve;
1465
- });
1466
- runtimeManager.getOrCreateClient.mockReturnValueOnce(pendingClient);
1467
- const startResult = service.startSession('session-1').catch((error) => error);
1468
- await vi.waitFor(() => {
1469
- expect(runtimeManager.getOrCreateClient).toHaveBeenCalled();
1470
- });
1471
- const stopPromise = service.stopSession('session-1');
1472
- await vi.waitFor(() => {
1473
- expect(runtimeManager.stopClient).toHaveBeenCalledTimes(1);
1474
- });
1475
- resolveClient(unsafeCoerce({
1476
- provider: 'CLAUDE',
1477
- providerSessionId: 'provider-session-1',
1478
- configOptions: [],
1479
- isPromptInFlight: false,
1480
- }));
1481
- await stopPromise;
1482
- await expect(startResult).resolves.toEqual(expect.objectContaining({ message: 'Session is currently being stopped' }));
1483
- expect(runtimeManager.stopClient).toHaveBeenCalledTimes(2);
1484
- expect(sendSessionMessage).not.toHaveBeenCalled();
1485
- });
1486
- it('skips the restart default continue prompt when notifications are queued', async () => {
1487
- const { service, sendSessionMessage, tryDispatchNextMessage } = createStartableLifecycleService({
1488
- pendingNotificationCount: 1,
1489
- });
1490
- await service.restartSession('session-1');
1491
- expect(tryDispatchNextMessage).toHaveBeenCalledWith('session-1');
1492
- expect(sendSessionMessage).not.toHaveBeenCalled();
1493
- });
1494
- it('sends an explicit restart prompt after queued notification dispatch starts', async () => {
1495
- const { service, sendSessionMessage, tryDispatchNextMessage } = createStartableLifecycleService({
1496
- pendingNotificationCount: 1,
1497
- });
1498
- await service.restartSession('session-1', {
1499
- initialPrompt: 'Fix the failing checks',
1500
- startupModePreset: 'non_interactive',
1501
- });
1502
- expect(tryDispatchNextMessage).toHaveBeenCalledWith('session-1');
1503
- expect(sendSessionMessage).toHaveBeenCalledWith('session-1', 'Fix the failing checks');
1504
- const dispatchOrder = tryDispatchNextMessage.mock.invocationCallOrder[0];
1505
- const sendOrder = sendSessionMessage.mock.invocationCallOrder[0];
1506
- expect(dispatchOrder).toBeDefined();
1507
- expect(sendOrder).toBeDefined();
1508
- expect(dispatchOrder).toBeLessThan(sendOrder);
1509
- });
1510
- it('does not fail startup when queued notification dispatch fails', async () => {
1511
- const { service, sendSessionMessage, tryDispatchNextMessage } = createStartableLifecycleService({
1512
- pendingNotificationCount: 1,
1513
- tryDispatchNextMessage: () => Promise.reject(new Error('dispatch failed')),
1514
- });
1515
- await expect(service.startSession('session-1')).resolves.toBeUndefined();
1516
- expect(tryDispatchNextMessage).toHaveBeenCalledWith('session-1');
1517
- expect(sendSessionMessage).not.toHaveBeenCalled();
1518
- });
1519
- });
1520
- //# sourceMappingURL=session.lifecycle.service.test.js.map