factory-factory 0.4.4 → 0.4.5

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 (302) hide show
  1. package/dist/client/assets/{abnfDiagram-VRR7QNED-CtUj9J56.js → abnfDiagram-VRR7QNED-DygwVN25.js} +1 -1
  2. package/dist/client/assets/{arc-2QeawLbF.js → arc-CcnMw5av.js} +1 -1
  3. package/dist/client/assets/{architectureDiagram-ZJ3FMSHR-Cz-V9xRh.js → architectureDiagram-ZJ3FMSHR-BZgeaNrI.js} +1 -1
  4. package/dist/client/assets/{blockDiagram-677ZJIJ3-BN6MDejN.js → blockDiagram-677ZJIJ3-DQ77xvqH.js} +1 -1
  5. package/dist/client/assets/{c4Diagram-LMCZKHZV-ZxcmUt9u.js → c4Diagram-LMCZKHZV-B-9QZJ1e.js} +1 -1
  6. package/dist/client/assets/channel-zAL33ya-.js +1 -0
  7. package/dist/client/assets/{chunk-2Q5K7J3B-CyvdNBvs.js → chunk-2Q5K7J3B-DhfBY-Wd.js} +1 -1
  8. package/dist/client/assets/{chunk-32BRIVSS-BXA35oY0.js → chunk-32BRIVSS-CV9UmSkI.js} +1 -1
  9. package/dist/client/assets/{chunk-5VM5RSS4-6lY9D3Us.js → chunk-5VM5RSS4-BUNj3iDY.js} +1 -1
  10. package/dist/client/assets/{chunk-EX3LRPZG-BRYEhwoK.js → chunk-EX3LRPZG-Dj63JdET.js} +1 -1
  11. package/dist/client/assets/{chunk-JWPE2WC7-DBeDDTf3.js → chunk-JWPE2WC7-i6Ud3VAu.js} +1 -1
  12. package/dist/client/assets/{chunk-MOJQB5TN-MIX8YUEz.js → chunk-MOJQB5TN-PY5GLOxr.js} +1 -1
  13. package/dist/client/assets/{chunk-RYQCIY6F-CT_f1sbt.js → chunk-RYQCIY6F-pIEuQy1L.js} +1 -1
  14. package/dist/client/assets/{chunk-V7JOEXUC-DpMacC1A.js → chunk-V7JOEXUC--zp7Z0xS.js} +1 -1
  15. package/dist/client/assets/{chunk-VR4S4FIN-Bd6MGh78.js → chunk-VR4S4FIN-JstmsazP.js} +1 -1
  16. package/dist/client/assets/{chunk-XXDRQBXY-BYg-oILv.js → chunk-XXDRQBXY-C66lsFmy.js} +1 -1
  17. package/dist/client/assets/classDiagram-OUVF2IWQ-Dksz7rAA.js +1 -0
  18. package/dist/client/assets/classDiagram-v2-EOCWNBFH-Dksz7rAA.js +1 -0
  19. package/dist/client/assets/{cose-bilkent-JH36ORCC-BGhjYi6z.js → cose-bilkent-JH36ORCC-D3jSuAHn.js} +1 -1
  20. package/dist/client/assets/{cynefin-VYW2F7L2-BouObN4T.js → cynefin-VYW2F7L2-Dx3xfGS7.js} +1 -1
  21. package/dist/client/assets/{cynefinDiagram-TSTJHNR4-DrbscNyk.js → cynefinDiagram-TSTJHNR4-BcGYZcSJ.js} +1 -1
  22. package/dist/client/assets/{dagre-VKFMJZFB-CpHBhtab.js → dagre-VKFMJZFB-CLZSDf9x.js} +1 -1
  23. package/dist/client/assets/{diagram-FQU43EPY-C_pXymmj.js → diagram-FQU43EPY-Nosv2FJf.js} +1 -1
  24. package/dist/client/assets/{diagram-G47NLZAW-DNPz9Qrh.js → diagram-G47NLZAW-CPjqONaO.js} +1 -1
  25. package/dist/client/assets/{diagram-NH7WQ7WH-DWHcerSm.js → diagram-NH7WQ7WH-BmRiUIAJ.js} +1 -1
  26. package/dist/client/assets/{diagram-OA4YK3LP-C6Jnb8nm.js → diagram-OA4YK3LP-C7WEtC2B.js} +1 -1
  27. package/dist/client/assets/{diagram-WEI45ONY-DF667tXW.js → diagram-WEI45ONY-up5rQ6My.js} +1 -1
  28. package/dist/client/assets/{ebnfDiagram-CCIWWBDH-_Wt4C-Lm.js → ebnfDiagram-CCIWWBDH-D2Ye87k4.js} +1 -1
  29. package/dist/client/assets/{erDiagram-Q63AITRT-D9F9IIZ7.js → erDiagram-Q63AITRT-CgZF8eG-.js} +1 -1
  30. package/dist/client/assets/{flowDiagram-23GEKE2U-BzTUtnLY.js → flowDiagram-23GEKE2U-bxoO3Eep.js} +1 -1
  31. package/dist/client/assets/{ganttDiagram-NO4QXBWP-lmFnKYE5.js → ganttDiagram-NO4QXBWP-BAtqPw6P.js} +1 -1
  32. package/dist/client/assets/{gitGraphDiagram-IHSO6WYX-DiIQa_m6.js → gitGraphDiagram-IHSO6WYX-DYQqeJ8w.js} +1 -1
  33. package/dist/client/assets/{index-CpvT2M5m.js → index-BZQlu-dZ.js} +155 -150
  34. package/dist/client/assets/{infoDiagram-FWYZ7A6U-BXtmiN5g.js → infoDiagram-FWYZ7A6U-CZQCerFQ.js} +1 -1
  35. package/dist/client/assets/{ishikawaDiagram-FXEZZL3T-Ce8fzasD.js → ishikawaDiagram-FXEZZL3T-Bc7wE25N.js} +1 -1
  36. package/dist/client/assets/{journeyDiagram-5HDEW3XC-CAhJE-OU.js → journeyDiagram-5HDEW3XC-D5qqjKJz.js} +1 -1
  37. package/dist/client/assets/{kanban-definition-HUTT4EX6-B8br5xew.js → kanban-definition-HUTT4EX6-42kkaHBo.js} +1 -1
  38. package/dist/client/assets/{linear-BPVVEns_.js → linear-Dg3NQEM3.js} +1 -1
  39. package/dist/client/assets/{mindmap-definition-LN4V7U3C-lX6h9H-N.js → mindmap-definition-LN4V7U3C-CRANKUcD.js} +1 -1
  40. package/dist/client/assets/{mobile-baseline-C45NSkzw.js → mobile-baseline-D6bQ8WTB.js} +1 -1
  41. package/dist/client/assets/{pegDiagram-2B236MQR-CiFWwnk4.js → pegDiagram-2B236MQR-qJQPOd8H.js} +1 -1
  42. package/dist/client/assets/{pieDiagram-ENE6RG2P-CvkwWtzQ.js → pieDiagram-ENE6RG2P-B5wB7Ccf.js} +1 -1
  43. package/dist/client/assets/{quadrantDiagram-ABIIQ3AL-B40anMyK.js → quadrantDiagram-ABIIQ3AL-CCZfXF8m.js} +1 -1
  44. package/dist/client/assets/{railroadDiagram-RFXS5EU6-WVYsdkDC.js → railroadDiagram-RFXS5EU6-BuIrSJUW.js} +1 -1
  45. package/dist/client/assets/{requirementDiagram-TGXJPOKE-DBTIsXpA.js → requirementDiagram-TGXJPOKE-DfIr0zZ3.js} +1 -1
  46. package/dist/client/assets/{sankeyDiagram-HTMAVEWB-Dx8y47pZ.js → sankeyDiagram-HTMAVEWB-CJoUXRWq.js} +1 -1
  47. package/dist/client/assets/{sequenceDiagram-DBY2YBRQ-C6ZZMZJm.js → sequenceDiagram-DBY2YBRQ-Cq1lOU1b.js} +1 -1
  48. package/dist/client/assets/{sizeCapture-X5ZJPWSS-DLy8loNR.js → sizeCapture-X5ZJPWSS-XHOsOkor.js} +1 -1
  49. package/dist/client/assets/{stateDiagram-2N3HPSRC-RoTcqZkX.js → stateDiagram-2N3HPSRC-BLVzzete.js} +1 -1
  50. package/dist/client/assets/stateDiagram-v2-6OUMAXLB-DA02HYvU.js +1 -0
  51. package/dist/client/assets/{swimlanes-5IMT3BWC-CFmoSHiT.js → swimlanes-5IMT3BWC-C20mYIPR.js} +2 -2
  52. package/dist/client/assets/swimlanesDiagram-G3AALYLV-CbfcpXpQ.js +8 -0
  53. package/dist/client/assets/{terminal-instance-Br0jXsXE.js → terminal-instance-Dw2gcPib.js} +1 -1
  54. package/dist/client/assets/{timeline-definition-FHXFAJF6-Bh5XA09y.js → timeline-definition-FHXFAJF6-DlceJAxJ.js} +1 -1
  55. package/dist/client/assets/{vennDiagram-L72KCM5P-BC754zGO.js → vennDiagram-L72KCM5P-CFl-Eo1Y.js} +1 -1
  56. package/dist/client/assets/{wardleyDiagram-EHGQE667-xJdUzWCg.js → wardleyDiagram-EHGQE667-eBpeLOoN.js} +1 -1
  57. package/dist/client/assets/{xychartDiagram-FW5EYKEG-wxQH1S7i.js → xychartDiagram-FW5EYKEG-B8RBkKiX.js} +1 -1
  58. package/dist/client/index.html +1 -1
  59. package/dist/prisma/generated/browser.d.ts +1 -0
  60. package/dist/prisma/generated/browser.d.ts.map +1 -1
  61. package/dist/prisma/generated/client.d.ts +1 -0
  62. package/dist/prisma/generated/client.d.ts.map +1 -1
  63. package/dist/prisma/generated/commonInputTypes.d.ts +60 -0
  64. package/dist/prisma/generated/commonInputTypes.d.ts.map +1 -1
  65. package/dist/prisma/generated/enums.d.ts +15 -0
  66. package/dist/prisma/generated/enums.d.ts.map +1 -1
  67. package/dist/prisma/generated/enums.js +13 -0
  68. package/dist/prisma/generated/enums.js.map +1 -1
  69. package/dist/prisma/generated/internal/class.d.ts +3 -0
  70. package/dist/prisma/generated/internal/class.d.ts.map +1 -1
  71. package/dist/prisma/generated/internal/class.js +4 -4
  72. package/dist/prisma/generated/internal/class.js.map +1 -1
  73. package/dist/prisma/generated/internal/prismaNamespace.d.ts +90 -1
  74. package/dist/prisma/generated/internal/prismaNamespace.d.ts.map +1 -1
  75. package/dist/prisma/generated/internal/prismaNamespace.js +11 -0
  76. package/dist/prisma/generated/internal/prismaNamespace.js.map +1 -1
  77. package/dist/prisma/generated/internal/prismaNamespaceBrowser.d.ts +12 -0
  78. package/dist/prisma/generated/internal/prismaNamespaceBrowser.d.ts.map +1 -1
  79. package/dist/prisma/generated/internal/prismaNamespaceBrowser.js +11 -0
  80. package/dist/prisma/generated/internal/prismaNamespaceBrowser.js.map +1 -1
  81. package/dist/prisma/generated/models/SessionLifecycleEvent.d.ts +657 -0
  82. package/dist/prisma/generated/models/SessionLifecycleEvent.d.ts.map +1 -0
  83. package/dist/prisma/generated/models/SessionLifecycleEvent.js +2 -0
  84. package/dist/prisma/generated/models/SessionLifecycleEvent.js.map +1 -0
  85. package/dist/prisma/generated/models/Workspace.d.ts +264 -0
  86. package/dist/prisma/generated/models/Workspace.d.ts.map +1 -1
  87. package/dist/prisma/generated/models.d.ts +1 -0
  88. package/dist/prisma/generated/models.d.ts.map +1 -1
  89. package/dist/src/backend/app-context.d.ts.map +1 -1
  90. package/dist/src/backend/app-context.js +2 -1
  91. package/dist/src/backend/app-context.js.map +1 -1
  92. package/dist/src/backend/interceptors/branch-naming.interceptor.d.ts.map +1 -1
  93. package/dist/src/backend/interceptors/branch-naming.interceptor.js +0 -4
  94. package/dist/src/backend/interceptors/branch-naming.interceptor.js.map +1 -1
  95. package/dist/src/backend/interceptors/branch-naming.interceptor.test.js +26 -0
  96. package/dist/src/backend/interceptors/branch-naming.interceptor.test.js.map +1 -1
  97. package/dist/src/backend/orchestration/domain-bridges.orchestrator.d.ts +2 -1
  98. package/dist/src/backend/orchestration/domain-bridges.orchestrator.d.ts.map +1 -1
  99. package/dist/src/backend/orchestration/domain-bridges.orchestrator.js +6 -6
  100. package/dist/src/backend/orchestration/domain-bridges.orchestrator.js.map +1 -1
  101. package/dist/src/backend/orchestration/domain-bridges.orchestrator.test.js +18 -8
  102. package/dist/src/backend/orchestration/domain-bridges.orchestrator.test.js.map +1 -1
  103. package/dist/src/backend/orchestration/event-collector.orchestrator.d.ts.map +1 -1
  104. package/dist/src/backend/orchestration/event-collector.orchestrator.js +3 -1
  105. package/dist/src/backend/orchestration/event-collector.orchestrator.js.map +1 -1
  106. package/dist/src/backend/orchestration/event-collector.orchestrator.test.js +3 -1
  107. package/dist/src/backend/orchestration/event-collector.orchestrator.test.js.map +1 -1
  108. package/dist/src/backend/orchestration/reconciliation.service.d.ts +11 -8
  109. package/dist/src/backend/orchestration/reconciliation.service.d.ts.map +1 -1
  110. package/dist/src/backend/orchestration/reconciliation.service.js +41 -33
  111. package/dist/src/backend/orchestration/reconciliation.service.js.map +1 -1
  112. package/dist/src/backend/orchestration/reconciliation.service.test.js +9 -42
  113. package/dist/src/backend/orchestration/reconciliation.service.test.js.map +1 -1
  114. package/dist/src/backend/orchestration/workspace-archive.orchestrator.d.ts +4 -1
  115. package/dist/src/backend/orchestration/workspace-archive.orchestrator.d.ts.map +1 -1
  116. package/dist/src/backend/orchestration/workspace-archive.orchestrator.js +4 -1
  117. package/dist/src/backend/orchestration/workspace-archive.orchestrator.js.map +1 -1
  118. package/dist/src/backend/orchestration/workspace-archive.orchestrator.test.js +15 -2
  119. package/dist/src/backend/orchestration/workspace-archive.orchestrator.test.js.map +1 -1
  120. package/dist/src/backend/orchestration/workspace-init-issue-prompts.d.ts.map +1 -1
  121. package/dist/src/backend/orchestration/workspace-init-issue-prompts.js +3 -1
  122. package/dist/src/backend/orchestration/workspace-init-issue-prompts.js.map +1 -1
  123. package/dist/src/backend/orchestration/workspace-init-issue-prompts.test.d.ts +2 -0
  124. package/dist/src/backend/orchestration/workspace-init-issue-prompts.test.d.ts.map +1 -0
  125. package/dist/src/backend/orchestration/workspace-init-issue-prompts.test.js +75 -0
  126. package/dist/src/backend/orchestration/workspace-init-issue-prompts.test.js.map +1 -0
  127. package/dist/src/backend/orchestration/workspace-init.orchestrator.d.ts +7 -4
  128. package/dist/src/backend/orchestration/workspace-init.orchestrator.d.ts.map +1 -1
  129. package/dist/src/backend/orchestration/workspace-init.orchestrator.js +36 -1
  130. package/dist/src/backend/orchestration/workspace-init.orchestrator.js.map +1 -1
  131. package/dist/src/backend/orchestration/workspace-init.orchestrator.test.js +117 -8
  132. package/dist/src/backend/orchestration/workspace-init.orchestrator.test.js.map +1 -1
  133. package/dist/src/backend/services/periodic-task/resources/periodic-task.accessor.d.ts.map +1 -1
  134. package/dist/src/backend/services/periodic-task/resources/periodic-task.accessor.js +27 -6
  135. package/dist/src/backend/services/periodic-task/resources/periodic-task.accessor.js.map +1 -1
  136. package/dist/src/backend/services/periodic-task/resources/periodic-task.accessor.test.js +7 -0
  137. package/dist/src/backend/services/periodic-task/resources/periodic-task.accessor.test.js.map +1 -1
  138. package/dist/src/backend/services/port.service.d.ts.map +1 -1
  139. package/dist/src/backend/services/port.service.js +4 -0
  140. package/dist/src/backend/services/port.service.js.map +1 -1
  141. package/dist/src/backend/services/port.service.test.js +8 -0
  142. package/dist/src/backend/services/port.service.test.js.map +1 -1
  143. package/dist/src/backend/services/registry.d.ts +2 -2
  144. package/dist/src/backend/services/registry.d.ts.map +1 -1
  145. package/dist/src/backend/services/registry.js +2 -1
  146. package/dist/src/backend/services/registry.js.map +1 -1
  147. package/dist/src/backend/services/session/resources/session-lifecycle-event.accessor.d.ts +23 -0
  148. package/dist/src/backend/services/session/resources/session-lifecycle-event.accessor.d.ts.map +1 -0
  149. package/dist/src/backend/services/session/resources/session-lifecycle-event.accessor.js +23 -0
  150. package/dist/src/backend/services/session/resources/session-lifecycle-event.accessor.js.map +1 -0
  151. package/dist/src/backend/services/session/resources/session-lifecycle-event.accessor.test.d.ts +2 -0
  152. package/dist/src/backend/services/session/resources/session-lifecycle-event.accessor.test.d.ts.map +1 -0
  153. package/dist/src/backend/services/session/resources/session-lifecycle-event.accessor.test.js +62 -0
  154. package/dist/src/backend/services/session/resources/session-lifecycle-event.accessor.test.js.map +1 -0
  155. package/dist/src/backend/services/session/service/acp/acp-runtime-manager.d.ts +2 -1
  156. package/dist/src/backend/services/session/service/acp/acp-runtime-manager.d.ts.map +1 -1
  157. package/dist/src/backend/services/session/service/acp/acp-runtime-manager.js +14 -7
  158. package/dist/src/backend/services/session/service/acp/acp-runtime-manager.js.map +1 -1
  159. package/dist/src/backend/services/session/service/acp/acp-runtime-manager.test.js +33 -0
  160. package/dist/src/backend/services/session/service/acp/acp-runtime-manager.test.js.map +1 -1
  161. package/dist/src/backend/services/session/service/chat/chat-message-handlers/attachment-processing.test.js +21 -0
  162. package/dist/src/backend/services/session/service/chat/chat-message-handlers/attachment-processing.test.js.map +1 -1
  163. package/dist/src/backend/services/session/service/chat/chat-message-handlers/handlers/load-session.handler.d.ts.map +1 -1
  164. package/dist/src/backend/services/session/service/chat/chat-message-handlers/handlers/load-session.handler.js +3 -279
  165. package/dist/src/backend/services/session/service/chat/chat-message-handlers/handlers/load-session.handler.js.map +1 -1
  166. package/dist/src/backend/services/session/service/chat/chat-message-handlers/handlers/load-session.handler.test.js +154 -7
  167. package/dist/src/backend/services/session/service/chat/chat-message-handlers/handlers/load-session.handler.test.js.map +1 -1
  168. package/dist/src/backend/services/session/service/chat/chat-message-handlers/handlers/stop.handler.js +1 -1
  169. package/dist/src/backend/services/session/service/chat/chat-message-handlers/handlers/stop.handler.js.map +1 -1
  170. package/dist/src/backend/services/session/service/chat/chat-message-handlers/handlers/stop.handler.test.js +1 -1
  171. package/dist/src/backend/services/session/service/chat/chat-message-handlers/handlers/stop.handler.test.js.map +1 -1
  172. package/dist/src/backend/services/session/service/chat/chat-message-handlers/image-format-validation.js +1 -1
  173. package/dist/src/backend/services/session/service/chat/chat-message-handlers/image-format-validation.js.map +1 -1
  174. package/dist/src/backend/services/session/service/chat/chat-message-handlers.service.d.ts.map +1 -1
  175. package/dist/src/backend/services/session/service/chat/chat-message-handlers.service.js +4 -1
  176. package/dist/src/backend/services/session/service/chat/chat-message-handlers.service.js.map +1 -1
  177. package/dist/src/backend/services/session/service/chat/chat-message-handlers.service.test.js +49 -0
  178. package/dist/src/backend/services/session/service/chat/chat-message-handlers.service.test.js.map +1 -1
  179. package/dist/src/backend/services/session/service/index.d.ts +1 -1
  180. package/dist/src/backend/services/session/service/index.d.ts.map +1 -1
  181. package/dist/src/backend/services/session/service/index.js +1 -1
  182. package/dist/src/backend/services/session/service/index.js.map +1 -1
  183. package/dist/src/backend/services/session/service/lifecycle/acp-event-processor.d.ts +5 -1
  184. package/dist/src/backend/services/session/service/lifecycle/acp-event-processor.d.ts.map +1 -1
  185. package/dist/src/backend/services/session/service/lifecycle/acp-event-processor.js +23 -4
  186. package/dist/src/backend/services/session/service/lifecycle/acp-event-processor.js.map +1 -1
  187. package/dist/src/backend/services/session/service/lifecycle/acp-event-processor.text-streaming.test.js +28 -0
  188. package/dist/src/backend/services/session/service/lifecycle/acp-event-processor.text-streaming.test.js.map +1 -1
  189. package/dist/src/backend/services/session/service/lifecycle/session-lifecycle-event.service.d.ts +26 -0
  190. package/dist/src/backend/services/session/service/lifecycle/session-lifecycle-event.service.d.ts.map +1 -0
  191. package/dist/src/backend/services/session/service/lifecycle/session-lifecycle-event.service.js +84 -0
  192. package/dist/src/backend/services/session/service/lifecycle/session-lifecycle-event.service.js.map +1 -0
  193. package/dist/src/backend/services/session/service/lifecycle/session-lifecycle-event.service.test.d.ts +2 -0
  194. package/dist/src/backend/services/session/service/lifecycle/session-lifecycle-event.service.test.d.ts.map +1 -0
  195. package/dist/src/backend/services/session/service/lifecycle/session-lifecycle-event.service.test.js +283 -0
  196. package/dist/src/backend/services/session/service/lifecycle/session-lifecycle-event.service.test.js.map +1 -0
  197. package/dist/src/backend/services/session/service/lifecycle/session-provider-history-hydrator.d.ts +10 -0
  198. package/dist/src/backend/services/session/service/lifecycle/session-provider-history-hydrator.d.ts.map +1 -0
  199. package/dist/src/backend/services/session/service/lifecycle/session-provider-history-hydrator.js +299 -0
  200. package/dist/src/backend/services/session/service/lifecycle/session-provider-history-hydrator.js.map +1 -0
  201. package/dist/src/backend/services/session/service/lifecycle/session-services.d.ts +2 -0
  202. package/dist/src/backend/services/session/service/lifecycle/session-services.d.ts.map +1 -1
  203. package/dist/src/backend/services/session/service/lifecycle/session-services.js +11 -0
  204. package/dist/src/backend/services/session/service/lifecycle/session-services.js.map +1 -1
  205. package/dist/src/backend/services/session/service/lifecycle/session.config.service.d.ts +1 -0
  206. package/dist/src/backend/services/session/service/lifecycle/session.config.service.d.ts.map +1 -1
  207. package/dist/src/backend/services/session/service/lifecycle/session.config.service.js +17 -0
  208. package/dist/src/backend/services/session/service/lifecycle/session.config.service.js.map +1 -1
  209. package/dist/src/backend/services/session/service/lifecycle/session.config.service.test.js +42 -0
  210. package/dist/src/backend/services/session/service/lifecycle/session.config.service.test.js.map +1 -1
  211. package/dist/src/backend/services/session/service/lifecycle/session.error-message.d.ts +2 -0
  212. package/dist/src/backend/services/session/service/lifecycle/session.error-message.d.ts.map +1 -1
  213. package/dist/src/backend/services/session/service/lifecycle/session.error-message.js +21 -0
  214. package/dist/src/backend/services/session/service/lifecycle/session.error-message.js.map +1 -1
  215. package/dist/src/backend/services/session/service/lifecycle/session.error-message.test.js +26 -1
  216. package/dist/src/backend/services/session/service/lifecycle/session.error-message.test.js.map +1 -1
  217. package/dist/src/backend/services/session/service/lifecycle/session.lifecycle.service.d.ts +31 -2
  218. package/dist/src/backend/services/session/service/lifecycle/session.lifecycle.service.d.ts.map +1 -1
  219. package/dist/src/backend/services/session/service/lifecycle/session.lifecycle.service.js +279 -109
  220. package/dist/src/backend/services/session/service/lifecycle/session.lifecycle.service.js.map +1 -1
  221. package/dist/src/backend/services/session/service/lifecycle/session.lifecycle.service.test.js +465 -8
  222. package/dist/src/backend/services/session/service/lifecycle/session.lifecycle.service.test.js.map +1 -1
  223. package/dist/src/backend/services/session/service/lifecycle/session.prompt.service.test.js +206 -5
  224. package/dist/src/backend/services/session/service/lifecycle/session.prompt.service.test.js.map +1 -1
  225. package/dist/src/backend/services/session/service/lifecycle/session.service.d.ts +15 -2
  226. package/dist/src/backend/services/session/service/lifecycle/session.service.d.ts.map +1 -1
  227. package/dist/src/backend/services/session/service/lifecycle/session.service.js +87 -14
  228. package/dist/src/backend/services/session/service/lifecycle/session.service.js.map +1 -1
  229. package/dist/src/backend/services/session/service/lifecycle/session.service.test.js +1 -0
  230. package/dist/src/backend/services/session/service/lifecycle/session.service.test.js.map +1 -1
  231. package/dist/src/backend/services/session/service/session-domain.service.d.ts +1 -0
  232. package/dist/src/backend/services/session/service/session-domain.service.d.ts.map +1 -1
  233. package/dist/src/backend/services/session/service/session-domain.service.js +9 -0
  234. package/dist/src/backend/services/session/service/session-domain.service.js.map +1 -1
  235. package/dist/src/backend/services/session/service/session-domain.service.test.js +58 -0
  236. package/dist/src/backend/services/session/service/session-domain.service.test.js.map +1 -1
  237. package/dist/src/backend/services/session/service/store/session-lifecycle-transcript.d.ts +6 -0
  238. package/dist/src/backend/services/session/service/store/session-lifecycle-transcript.d.ts.map +1 -0
  239. package/dist/src/backend/services/session/service/store/session-lifecycle-transcript.js +114 -0
  240. package/dist/src/backend/services/session/service/store/session-lifecycle-transcript.js.map +1 -0
  241. package/dist/src/backend/services/session/service/store/session-lifecycle-transcript.test.d.ts +2 -0
  242. package/dist/src/backend/services/session/service/store/session-lifecycle-transcript.test.d.ts.map +1 -0
  243. package/dist/src/backend/services/session/service/store/session-lifecycle-transcript.test.js +129 -0
  244. package/dist/src/backend/services/session/service/store/session-lifecycle-transcript.test.js.map +1 -0
  245. package/dist/src/backend/services/session/service/store/session-transcript.d.ts.map +1 -1
  246. package/dist/src/backend/services/session/service/store/session-transcript.js +7 -4
  247. package/dist/src/backend/services/session/service/store/session-transcript.js.map +1 -1
  248. package/dist/src/backend/services/workspace/service/lifecycle/creation.service.d.ts.map +1 -1
  249. package/dist/src/backend/services/workspace/service/lifecycle/creation.service.js +1 -0
  250. package/dist/src/backend/services/workspace/service/lifecycle/creation.service.js.map +1 -1
  251. package/dist/src/backend/services/workspace/service/lifecycle/creation.service.test.js +12 -0
  252. package/dist/src/backend/services/workspace/service/lifecycle/creation.service.test.js.map +1 -1
  253. package/dist/src/backend/services/workspace/service/worktree/worktree-lifecycle.service.d.ts +1 -0
  254. package/dist/src/backend/services/workspace/service/worktree/worktree-lifecycle.service.d.ts.map +1 -1
  255. package/dist/src/backend/services/workspace/service/worktree/worktree-lifecycle.service.js +14 -0
  256. package/dist/src/backend/services/workspace/service/worktree/worktree-lifecycle.service.js.map +1 -1
  257. package/dist/src/backend/services/workspace/service/worktree/worktree-lifecycle.service.test.js +54 -0
  258. package/dist/src/backend/services/workspace/service/worktree/worktree-lifecycle.service.test.js.map +1 -1
  259. package/dist/src/backend/trpc/project.router.test.js +19 -0
  260. package/dist/src/backend/trpc/project.router.test.js.map +1 -1
  261. package/dist/src/backend/trpc/project.trpc.js +1 -1
  262. package/dist/src/backend/trpc/project.trpc.js.map +1 -1
  263. package/dist/src/backend/trpc/session.router.test.js +11 -0
  264. package/dist/src/backend/trpc/session.router.test.js.map +1 -1
  265. package/dist/src/backend/trpc/session.trpc.d.ts.map +1 -1
  266. package/dist/src/backend/trpc/session.trpc.js +4 -0
  267. package/dist/src/backend/trpc/session.trpc.js.map +1 -1
  268. package/dist/src/cli/runtime-utils.d.ts.map +1 -1
  269. package/dist/src/cli/runtime-utils.js +4 -0
  270. package/dist/src/cli/runtime-utils.js.map +1 -1
  271. package/dist/src/cli/runtime-utils.test.js +7 -0
  272. package/dist/src/cli/runtime-utils.test.js.map +1 -1
  273. package/dist/src/shared/acp-protocol/protocol/messages.d.ts +18 -2
  274. package/dist/src/shared/acp-protocol/protocol/messages.d.ts.map +1 -1
  275. package/dist/src/shared/acp-protocol/protocol/messages.js +1 -0
  276. package/dist/src/shared/acp-protocol/protocol/messages.js.map +1 -1
  277. package/dist/src/shared/acp-protocol/protocol/renderer-window.d.ts.map +1 -1
  278. package/dist/src/shared/acp-protocol/protocol/renderer-window.js +3 -0
  279. package/dist/src/shared/acp-protocol/protocol/renderer-window.js.map +1 -1
  280. package/dist/src/shared/core/enums.d.ts +15 -0
  281. package/dist/src/shared/core/enums.d.ts.map +1 -1
  282. package/dist/src/shared/core/enums.js +13 -0
  283. package/dist/src/shared/core/enums.js.map +1 -1
  284. package/dist/src/shared/core/index.d.ts +1 -1
  285. package/dist/src/shared/core/index.d.ts.map +1 -1
  286. package/dist/src/shared/core/index.js +1 -1
  287. package/dist/src/shared/core/index.js.map +1 -1
  288. package/dist/src/shared/issue-start-prompt.d.ts.map +1 -1
  289. package/dist/src/shared/issue-start-prompt.js +8 -3
  290. package/dist/src/shared/issue-start-prompt.js.map +1 -1
  291. package/dist/src/test-utils/application-graph.d.ts.map +1 -1
  292. package/dist/src/test-utils/application-graph.js +3 -1
  293. package/dist/src/test-utils/application-graph.js.map +1 -1
  294. package/dist/tsconfig.backend.tsbuildinfo +1 -1
  295. package/package.json +1 -1
  296. package/prisma/migrations/20260730140000_add_session_lifecycle_events/migration.sql +20 -0
  297. package/prisma/schema.prisma +32 -0
  298. package/dist/client/assets/channel-DVMObwOq.js +0 -1
  299. package/dist/client/assets/classDiagram-OUVF2IWQ-wHGXcbfd.js +0 -1
  300. package/dist/client/assets/classDiagram-v2-EOCWNBFH-wHGXcbfd.js +0 -1
  301. package/dist/client/assets/stateDiagram-v2-6OUMAXLB-CGxDgHho.js +0 -1
  302. package/dist/client/assets/swimlanesDiagram-G3AALYLV-1u_1g3u4.js +0 -8
@@ -1,3 +1,4 @@
1
+ import { randomUUID } from 'node:crypto';
1
2
  import { configService } from '../../../../../backend/services/config.service.js';
2
3
  import { createLogger } from '../../../../../backend/services/logger.service.js';
3
4
  import { getChildWorkspaceMcpServerConfig } from '../../../../../backend/services/session/service/acp/child-workspace-mcp-server.js';
@@ -5,7 +6,7 @@ import { acpTraceLogger } from '../../../../../backend/services/session/service/
5
6
  import { sessionEventBus } from '../../../../../backend/services/session/service/session-event-bus.js';
6
7
  import { userSettingsService } from '../../../../../backend/services/settings/index.js';
7
8
  import { workspaceDataService, workspaceNotificationService } from '../../../../../backend/services/workspace/index.js';
8
- import { SessionStatus } from '../../../../../shared/core/index.js';
9
+ import { SessionLifecycleEventKind, SessionLifecycleEventReason, SessionStatus, } from '../../../../../shared/core/index.js';
9
10
  import { createInitialSessionRuntimeState, } from '../../../../../shared/session-runtime.js';
10
11
  import { buildWorkspaceNotificationMessageText, WORKSPACE_NOTIFICATION_MESSAGE_ID_PREFIX, workspaceNotificationMessageId, } from '../../../../../shared/workspace-notifications.js';
11
12
  import { closedSessionPersistenceService } from './closed-session-persistence.service.js';
@@ -16,6 +17,13 @@ const logger = createLogger('session');
16
17
  function getPersistedStatusForExitCode(exitCode) {
17
18
  return exitCode === 0 ? SessionStatus.COMPLETED : SessionStatus.FAILED;
18
19
  }
20
+ const SESSION_STOP_MESSAGES = {
21
+ USER_STOP: 'Session stopped by you.',
22
+ SESSION_CLOSED: 'Session closed by you.',
23
+ WORKSPACE_ARCHIVED: 'Session stopped because the workspace was archived.',
24
+ SYSTEM_STOP: 'Session stopped by the system.',
25
+ };
26
+ const SHUTDOWN_LIFECYCLE_RECORD_TIMEOUT_MS = 1000;
19
27
  export class SessionLifecycleService {
20
28
  repository;
21
29
  promptBuilder;
@@ -26,11 +34,16 @@ export class SessionLifecycleService {
26
34
  acpEventProcessor;
27
35
  promptTurnCompletionService;
28
36
  retryService;
37
+ lifecycleEventService;
38
+ hydrateProviderHistory;
29
39
  sendSessionMessage;
30
40
  onBeforeStopSession;
31
41
  onSessionExit;
32
42
  stoppingSessions = new Set();
43
+ shutdownSessions = new Set();
44
+ stopGenerationCounter = 0;
33
45
  stopGenerations = new Map();
46
+ startupGenerationReferences = new Map();
34
47
  clientCreationOperations = new Map();
35
48
  workspaceBridge = null;
36
49
  messageQueueBridge = null;
@@ -45,6 +58,9 @@ export class SessionLifecycleService {
45
58
  this.acpEventProcessor = options.acpEventProcessor;
46
59
  this.promptTurnCompletionService = options.promptTurnCompletionService;
47
60
  this.retryService = options.retryService;
61
+ this.lifecycleEventService = options.lifecycleEventService;
62
+ this.hydrateProviderHistory =
63
+ options.hydrateProviderHistory ?? (async () => undefined);
48
64
  this.sendSessionMessage = options.sendSessionMessage;
49
65
  this.onBeforeStopSession = options.onBeforeStopSession;
50
66
  this.onSessionExit = options.onSessionExit;
@@ -55,32 +71,34 @@ export class SessionLifecycleService {
55
71
  this.autoIterationExitBridge = bridges.autoIterationExit ?? null;
56
72
  }
57
73
  async startSession(sessionId, options) {
58
- const stopGeneration = this.getStopGeneration(sessionId);
59
- const session = await this.repository.getSessionById(sessionId);
60
- if (!session) {
61
- throw new Error(`Session not found: ${sessionId}`);
62
- }
63
- this.assertStartupAllowed(sessionId, stopGeneration);
64
- const existingClient = this.runtimeManager.getClient(sessionId);
65
- if (existingClient) {
66
- throw new Error('Session is already running');
67
- }
68
- const startupModePreset = options?.startupModePreset;
69
- const { handle, resolvedPreset, dispatchableNotificationCount } = await this.getOrCreateAcpSessionClient(sessionId, {}, session, stopGeneration);
70
- this.assertStartupAllowed(sessionId, stopGeneration);
71
- await this.applyStartupModePreset(sessionId, handle, startupModePreset, session.workflow);
72
- this.assertStartupAllowed(sessionId, stopGeneration);
73
- await this.applyConfiguredPermissionPreset(sessionId, session, handle, resolvedPreset);
74
- this.assertStartupAllowed(sessionId, stopGeneration);
75
- await this.dispatchQueuedNotificationsIfNeeded(sessionId, dispatchableNotificationCount);
76
- this.assertStartupAllowed(sessionId, stopGeneration);
77
- const initialPrompt = options?.initialPrompt ?? 'Continue with the task.';
78
- const shouldSendInitialPrompt = dispatchableNotificationCount === 0 ||
79
- (typeof options?.initialPrompt === 'string' && !options.initialPromptIsDefault);
80
- if (shouldSendInitialPrompt && initialPrompt) {
81
- await this.sendSessionMessage(sessionId, initialPrompt);
82
- }
83
- logger.info('Session started', { sessionId, provider: session.provider });
74
+ await this.runStartupOperation(sessionId, async (stopGeneration) => {
75
+ const session = await this.repository.getSessionById(sessionId);
76
+ if (!session) {
77
+ throw new Error(`Session not found: ${sessionId}`);
78
+ }
79
+ this.assertStartupAllowed(sessionId, stopGeneration);
80
+ const existingClient = this.runtimeManager.getClient(sessionId);
81
+ if (existingClient) {
82
+ throw new Error('Session is already running');
83
+ }
84
+ const startupModePreset = options?.startupModePreset;
85
+ const { handle, resolvedPreset, dispatchableNotificationCount } = await this.getOrCreateAcpSessionClient(sessionId, {}, session, stopGeneration);
86
+ this.assertStartupAllowed(sessionId, stopGeneration);
87
+ await this.applyStartupModePreset(sessionId, handle, startupModePreset, session.workflow);
88
+ this.assertStartupAllowed(sessionId, stopGeneration);
89
+ await this.applyConfiguredPermissionPreset(sessionId, session, handle, resolvedPreset);
90
+ this.assertStartupAllowed(sessionId, stopGeneration);
91
+ await this.dispatchQueuedNotificationsIfNeeded(sessionId, dispatchableNotificationCount);
92
+ this.assertStartupAllowed(sessionId, stopGeneration);
93
+ const initialPrompt = options?.initialPrompt ?? 'Continue with the task.';
94
+ const shouldSendInitialPrompt = dispatchableNotificationCount === 0 ||
95
+ (typeof options?.initialPrompt === 'string' && !options.initialPromptIsDefault);
96
+ if (shouldSendInitialPrompt && initialPrompt) {
97
+ await this.sendSessionMessage(sessionId, initialPrompt);
98
+ }
99
+ this.assertStartupAllowed(sessionId, stopGeneration);
100
+ logger.info('Session started', { sessionId, provider: session.provider });
101
+ });
84
102
  }
85
103
  async restartSession(sessionId, options) {
86
104
  const isRunning = this.runtimeManager.isSessionRunning(sessionId);
@@ -110,25 +128,38 @@ export class SessionLifecycleService {
110
128
  logger.debug('Session stop already in progress', { sessionId });
111
129
  return;
112
130
  }
113
- this.stopGenerations.set(sessionId, this.getStopGeneration(sessionId) + 1);
131
+ this.advanceStopGeneration(sessionId);
114
132
  this.stoppingSessions.add(sessionId);
133
+ const stopInvocationId = randomUUID();
115
134
  try {
116
- await this.stopSessionWithBarrier(sessionId, options);
135
+ await this.stopSessionWithBarrier(sessionId, stopInvocationId, options);
117
136
  }
118
137
  finally {
119
138
  this.stoppingSessions.delete(sessionId);
139
+ this.stopGenerations.delete(sessionId);
120
140
  }
121
141
  }
122
- async stopSessionWithBarrier(sessionId, options) {
142
+ async stopSessionWithBarrier(sessionId, stopInvocationId, options) {
123
143
  this.promptTurnCompletionService.clearSession(sessionId);
124
144
  this.onBeforeStopSession?.(sessionId);
125
145
  this.sessionDomainService.clearQueuedWork(sessionId, { emitSnapshot: true });
126
146
  const session = await this.loadSessionForStop(sessionId);
127
147
  const workspaceId = session?.workspaceId ?? this.acpEventProcessor.getWorkspaceId(sessionId);
148
+ const reason = options?.reason ?? 'SYSTEM_STOP';
128
149
  if (this.runtimeManager.isStopInProgress(sessionId)) {
129
150
  logger.debug('Session stop already in progress', { sessionId });
130
151
  return;
131
152
  }
153
+ if (workspaceId && options?.recordLifecycleEvent !== false) {
154
+ await this.lifecycleEventService.record({
155
+ workspaceId,
156
+ sessionId,
157
+ kind: SessionLifecycleEventKind.SESSION_STOPPED,
158
+ reason,
159
+ message: SESSION_STOP_MESSAGES[reason],
160
+ dedupeKey: `session-stop:${stopInvocationId}`,
161
+ });
162
+ }
132
163
  const current = this.getRuntimeSnapshot(sessionId);
133
164
  this.sessionDomainService.setRuntimeSnapshot(sessionId, {
134
165
  ...current,
@@ -190,14 +221,14 @@ export class SessionLifecycleService {
190
221
  await this.runtimeManager.stopClient(sessionId);
191
222
  }
192
223
  }
193
- async stopWorkspaceSessions(workspaceId) {
224
+ async stopWorkspaceSessions(workspaceId, options) {
194
225
  const sessions = await this.repository.getSessionsByWorkspaceId(workspaceId);
195
226
  const stopErrors = [];
196
227
  for (const session of sessions) {
197
228
  if (session.status === SessionStatus.RUNNING ||
198
229
  this.runtimeManager.isSessionRunning(session.id)) {
199
230
  try {
200
- await this.stopSession(session.id);
231
+ await this.stopSession(session.id, { reason: options?.reason ?? 'SYSTEM_STOP' });
201
232
  }
202
233
  catch (error) {
203
234
  logger.error('Failed to stop workspace session', {
@@ -215,36 +246,38 @@ export class SessionLifecycleService {
215
246
  logger.info('Stopped all workspace sessions', { workspaceId, count: sessions.length });
216
247
  }
217
248
  async getOrCreateSessionClient(sessionId, options) {
218
- const stopGeneration = this.getStopGeneration(sessionId);
219
- const session = await this.repository.getSessionById(sessionId);
220
- if (!session) {
221
- throw new Error(`Session not found: ${sessionId}`);
222
- }
223
- this.assertStartupAllowed(sessionId, stopGeneration);
224
- const hadClient = !!this.runtimeManager.getClient(sessionId);
225
- const { handle, resolvedPreset, dispatchableNotificationCount } = await this.getOrCreateAcpSessionClient(sessionId, options ?? {}, session, stopGeneration);
226
- this.assertStartupAllowed(sessionId, stopGeneration);
227
- if (!hadClient) {
228
- await this.applyConfiguredPermissionPreset(sessionId, session, handle, resolvedPreset);
249
+ return await this.runStartupOperation(sessionId, async (stopGeneration) => {
250
+ const session = await this.repository.getSessionById(sessionId);
251
+ if (!session) {
252
+ throw new Error(`Session not found: ${sessionId}`);
253
+ }
229
254
  this.assertStartupAllowed(sessionId, stopGeneration);
230
- await this.dispatchQueuedNotificationsIfNeeded(sessionId, dispatchableNotificationCount);
255
+ const hadClient = !!this.runtimeManager.getClient(sessionId);
256
+ const { handle, resolvedPreset, dispatchableNotificationCount } = await this.getOrCreateAcpSessionClient(sessionId, options ?? {}, session, stopGeneration);
231
257
  this.assertStartupAllowed(sessionId, stopGeneration);
232
- }
233
- return handle;
258
+ if (!hadClient) {
259
+ await this.applyConfiguredPermissionPreset(sessionId, session, handle, resolvedPreset);
260
+ this.assertStartupAllowed(sessionId, stopGeneration);
261
+ await this.dispatchQueuedNotificationsIfNeeded(sessionId, dispatchableNotificationCount);
262
+ this.assertStartupAllowed(sessionId, stopGeneration);
263
+ }
264
+ return handle;
265
+ });
234
266
  }
235
267
  async getOrCreateSessionClientFromRecord(session, options) {
236
- const stopGeneration = this.getStopGeneration(session.id);
237
- this.assertStartupAllowed(session.id, stopGeneration);
238
- const hadClient = !!this.runtimeManager.getClient(session.id);
239
- const { handle, resolvedPreset, dispatchableNotificationCount } = await this.getOrCreateAcpSessionClient(session.id, options ?? {}, session, stopGeneration);
240
- this.assertStartupAllowed(session.id, stopGeneration);
241
- if (!hadClient) {
242
- await this.applyConfiguredPermissionPreset(session.id, session, handle, resolvedPreset);
268
+ return await this.runStartupOperation(session.id, async (stopGeneration) => {
243
269
  this.assertStartupAllowed(session.id, stopGeneration);
244
- await this.dispatchQueuedNotificationsIfNeeded(session.id, dispatchableNotificationCount);
270
+ const hadClient = !!this.runtimeManager.getClient(session.id);
271
+ const { handle, resolvedPreset, dispatchableNotificationCount } = await this.getOrCreateAcpSessionClient(session.id, options ?? {}, session, stopGeneration);
245
272
  this.assertStartupAllowed(session.id, stopGeneration);
246
- }
247
- return handle;
273
+ if (!hadClient) {
274
+ await this.applyConfiguredPermissionPreset(session.id, session, handle, resolvedPreset);
275
+ this.assertStartupAllowed(session.id, stopGeneration);
276
+ await this.dispatchQueuedNotificationsIfNeeded(session.id, dispatchableNotificationCount);
277
+ this.assertStartupAllowed(session.id, stopGeneration);
278
+ }
279
+ return handle;
280
+ });
248
281
  }
249
282
  getSessionClient(sessionId) {
250
283
  return this.runtimeManager.getClient(sessionId);
@@ -285,10 +318,49 @@ export class SessionLifecycleService {
285
318
  return this.stoppingSessions.has(sessionId) || this.runtimeManager.isStopInProgress(sessionId);
286
319
  }
287
320
  getStopGeneration(sessionId) {
288
- return this.stopGenerations.get(sessionId) ?? 0;
321
+ return this.stopGenerations.get(sessionId) ?? this.advanceStopGeneration(sessionId);
322
+ }
323
+ async runStartupOperation(sessionId, operation) {
324
+ const stopGeneration = this.getStopGeneration(sessionId);
325
+ this.startupGenerationReferences.set(stopGeneration, (this.startupGenerationReferences.get(stopGeneration) ?? 0) + 1);
326
+ let succeeded = false;
327
+ try {
328
+ const result = await operation(stopGeneration);
329
+ succeeded = true;
330
+ return result;
331
+ }
332
+ finally {
333
+ this.releaseStartupGeneration(sessionId, stopGeneration, succeeded);
334
+ }
335
+ }
336
+ releaseStartupGeneration(sessionId, stopGeneration, succeeded) {
337
+ const referenceCount = this.startupGenerationReferences.get(stopGeneration);
338
+ if (referenceCount === undefined) {
339
+ return;
340
+ }
341
+ if (referenceCount > 1) {
342
+ this.startupGenerationReferences.set(stopGeneration, referenceCount - 1);
343
+ return;
344
+ }
345
+ this.startupGenerationReferences.delete(stopGeneration);
346
+ if (!(succeeded ||
347
+ this.isSessionStopping(sessionId) ||
348
+ this.runtimeManager.getClient(sessionId)) &&
349
+ this.isStopGenerationCurrent(sessionId, stopGeneration)) {
350
+ this.stopGenerations.delete(sessionId);
351
+ }
352
+ }
353
+ isStopGenerationCurrent(sessionId, stopGeneration) {
354
+ return this.stopGenerations.get(sessionId) === stopGeneration;
355
+ }
356
+ advanceStopGeneration(sessionId) {
357
+ this.stopGenerationCounter += 1;
358
+ this.stopGenerations.set(sessionId, this.stopGenerationCounter);
359
+ return this.stopGenerationCounter;
289
360
  }
290
361
  assertStartupAllowed(sessionId, stopGeneration) {
291
- if (this.isSessionStopping(sessionId) || this.getStopGeneration(sessionId) !== stopGeneration) {
362
+ if (this.isSessionStopping(sessionId) ||
363
+ !this.isStopGenerationCurrent(sessionId, stopGeneration)) {
292
364
  throw new Error('Session is currently being stopped');
293
365
  }
294
366
  }
@@ -307,6 +379,11 @@ export class SessionLifecycleService {
307
379
  }
308
380
  async stopAllClients(timeoutMs = 5000) {
309
381
  this.promptTurnCompletionService.clearAll();
382
+ const shutdownSessionIds = this.runtimeManager.beginShutdown();
383
+ for (const sessionId of shutdownSessionIds) {
384
+ this.shutdownSessions.add(sessionId);
385
+ }
386
+ await this.recordShutdownLifecycleEvents(shutdownSessionIds);
310
387
  try {
311
388
  await this.runtimeManager.stopAllClients(timeoutMs);
312
389
  }
@@ -317,8 +394,52 @@ export class SessionLifecycleService {
317
394
  throw error;
318
395
  }
319
396
  }
397
+ async recordShutdownLifecycleEvent(sessionId) {
398
+ const session = await this.loadSessionForStop(sessionId);
399
+ const workspaceId = session?.workspaceId ?? this.acpEventProcessor.getWorkspaceId(sessionId);
400
+ if (!workspaceId) {
401
+ logger.warn('Skipped shutdown lifecycle event without workspace owner', { sessionId });
402
+ return;
403
+ }
404
+ await this.lifecycleEventService.record({
405
+ workspaceId,
406
+ sessionId,
407
+ kind: SessionLifecycleEventKind.SESSION_STOPPED,
408
+ reason: SessionLifecycleEventReason.SYSTEM_STOP,
409
+ message: SESSION_STOP_MESSAGES.SYSTEM_STOP,
410
+ dedupeKey: `session-stop:${randomUUID()}`,
411
+ });
412
+ }
413
+ async recordShutdownLifecycleEvents(sessionIds) {
414
+ const resultsPromise = Promise.allSettled(sessionIds.map((sessionId) => this.recordShutdownLifecycleEvent(sessionId)));
415
+ let timeout;
416
+ const results = await Promise.race([
417
+ resultsPromise,
418
+ new Promise((resolve) => {
419
+ timeout = setTimeout(() => resolve(null), SHUTDOWN_LIFECYCLE_RECORD_TIMEOUT_MS);
420
+ }),
421
+ ]);
422
+ if (timeout) {
423
+ clearTimeout(timeout);
424
+ }
425
+ if (results === null) {
426
+ logger.warn('Timed out recording shutdown lifecycle events; continuing shutdown', {
427
+ timeoutMs: SHUTDOWN_LIFECYCLE_RECORD_TIMEOUT_MS,
428
+ });
429
+ return;
430
+ }
431
+ for (const [index, result] of results.entries()) {
432
+ if (result.status === 'rejected') {
433
+ logger.warn('Failed recording shutdown lifecycle event; continuing shutdown', {
434
+ sessionId: sessionIds[index],
435
+ error: result.reason instanceof Error ? result.reason.message : String(result.reason),
436
+ });
437
+ }
438
+ }
439
+ }
320
440
  setupAcpEventHandler(sessionId) {
321
441
  const runtimeEventHandler = this.acpEventProcessor.createRuntimeEventHandler(sessionId);
442
+ const runtimeIncarnationId = randomUUID();
322
443
  return {
323
444
  ...runtimeEventHandler,
324
445
  onSessionId: async (sid, providerSessionId) => {
@@ -342,51 +463,12 @@ export class SessionLifecycleService {
342
463
  }
343
464
  },
344
465
  onExit: async (sid, exitCode) => {
345
- this.promptTurnCompletionService.clearSession(sid);
346
- this.onSessionExit?.(sid);
347
- this.finalizeOrphanedToolCalls(sid, 'runtime_exit');
348
- this.acpEventProcessor.clearSessionState(sid);
349
- this.sessionPermissionService.cancelPendingRequests(sid);
350
- acpTraceLogger.log(sid, 'runtime_exit', { exitCode });
351
- try {
352
- this.sessionDomainService.markProcessExit(sid, exitCode);
353
- const persistedStatus = getPersistedStatusForExitCode(exitCode);
354
- const session = await this.repository.updateSession(sid, {
355
- status: persistedStatus,
356
- });
357
- logger.debug('Updated ACP session status on exit', {
358
- sessionId: sid,
359
- exitCode,
360
- status: persistedStatus,
361
- });
362
- await this.recordRatchetSessionEndOnExit(session.workspaceId, sid, exitCode);
363
- void this.maybeDiscoverPROnSessionEnd(session.workspaceId);
364
- if (session.workflow === 'ratchet') {
365
- await this.persistRatchetTranscript(sid, session);
366
- await this.repository.deleteSession(sid);
367
- this.sessionDomainService.clearSession(sid);
368
- logger.debug('Deleted transient ratchet ACP session', { sessionId: sid });
369
- }
370
- if (session.workflow === 'auto-iteration' && this.autoIterationExitBridge) {
371
- if (!this.runtimeManager.isStopInProgress(sid)) {
372
- this.autoIterationExitBridge.onAutoIterationSessionExit(session.workspaceId, sid);
373
- }
374
- }
375
- }
376
- catch (error) {
377
- logger.warn('Failed to update ACP session status on exit', {
378
- sessionId: sid,
379
- error: error instanceof Error ? error.message : String(error),
380
- });
381
- }
382
- finally {
383
- try {
384
- this.clearSessionStoreIfInactive(sid, 'runtime_exit');
385
- }
386
- finally {
387
- acpTraceLogger.closeSession(sid);
388
- }
389
- }
466
+ this.stopGenerations.delete(sid);
467
+ const wasDeliberateStop = this.stoppingSessions.has(sid) ||
468
+ this.shutdownSessions.has(sid) ||
469
+ this.runtimeManager.isStopInProgress(sid);
470
+ this.prepareAcpRuntimeExit(sid, exitCode);
471
+ await this.handleAcpRuntimeExit(sid, exitCode, wasDeliberateStop, runtimeIncarnationId);
390
472
  },
391
473
  onError: (sid, error) => {
392
474
  acpTraceLogger.log(sid, 'runtime_error', {
@@ -405,6 +487,85 @@ export class SessionLifecycleService {
405
487
  },
406
488
  };
407
489
  }
490
+ prepareAcpRuntimeExit(sessionId, exitCode) {
491
+ this.promptTurnCompletionService.clearSession(sessionId);
492
+ this.onSessionExit?.(sessionId);
493
+ this.finalizeOrphanedToolCalls(sessionId, 'runtime_exit');
494
+ this.acpEventProcessor.clearSessionState(sessionId);
495
+ this.sessionPermissionService.cancelPendingRequests(sessionId);
496
+ acpTraceLogger.log(sessionId, 'runtime_exit', { exitCode });
497
+ }
498
+ async handleAcpRuntimeExit(sessionId, exitCode, wasDeliberateStop, runtimeIncarnationId) {
499
+ try {
500
+ this.sessionDomainService.markProcessExit(sessionId, exitCode);
501
+ const session = await this.repository.getSessionById(sessionId);
502
+ if (!session) {
503
+ logger.warn('Failed to find ACP session on exit', { sessionId });
504
+ return;
505
+ }
506
+ const persistedStatus = getPersistedStatusForExitCode(exitCode);
507
+ try {
508
+ await this.repository.updateSession(sessionId, { status: persistedStatus });
509
+ logger.debug('Updated ACP session status on exit', {
510
+ sessionId,
511
+ exitCode,
512
+ status: persistedStatus,
513
+ });
514
+ }
515
+ catch (error) {
516
+ logger.warn('Failed to update ACP session status on exit', {
517
+ sessionId,
518
+ error: error instanceof Error ? error.message : String(error),
519
+ });
520
+ }
521
+ await this.recordUnexpectedExitIfNeeded(session, sessionId, exitCode, wasDeliberateStop, runtimeIncarnationId);
522
+ await this.recordRatchetSessionEndOnExit(session.workspaceId, sessionId, exitCode);
523
+ void this.maybeDiscoverPROnSessionEnd(session.workspaceId);
524
+ await this.cleanupExitedWorkflow(session, sessionId, wasDeliberateStop);
525
+ }
526
+ catch (error) {
527
+ logger.warn('Failed to process ACP session exit', {
528
+ sessionId,
529
+ error: error instanceof Error ? error.message : String(error),
530
+ });
531
+ }
532
+ finally {
533
+ try {
534
+ this.clearSessionStoreIfInactive(sessionId, 'runtime_exit');
535
+ }
536
+ finally {
537
+ acpTraceLogger.closeSession(sessionId);
538
+ }
539
+ }
540
+ }
541
+ async recordUnexpectedExitIfNeeded(session, sessionId, exitCode, wasDeliberateStop, runtimeIncarnationId) {
542
+ if (wasDeliberateStop) {
543
+ return;
544
+ }
545
+ await this.lifecycleEventService.record({
546
+ workspaceId: session.workspaceId,
547
+ sessionId,
548
+ kind: SessionLifecycleEventKind.SESSION_STOPPED,
549
+ reason: SessionLifecycleEventReason.UNEXPECTED_EXIT,
550
+ message: exitCode === null
551
+ ? 'Session stopped: agent process exited unexpectedly.'
552
+ : `Session stopped: agent process exited unexpectedly (code ${exitCode}).`,
553
+ dedupeKey: `process-exit:${runtimeIncarnationId}:${exitCode ?? 'signal'}`,
554
+ });
555
+ }
556
+ async cleanupExitedWorkflow(session, sessionId, wasDeliberateStop) {
557
+ if (session.workflow === 'ratchet') {
558
+ await this.persistRatchetTranscript(sessionId);
559
+ await this.repository.deleteSession(sessionId);
560
+ this.sessionDomainService.clearSession(sessionId);
561
+ logger.debug('Deleted transient ratchet ACP session', { sessionId });
562
+ }
563
+ if (session.workflow === 'auto-iteration' &&
564
+ this.autoIterationExitBridge &&
565
+ !wasDeliberateStop) {
566
+ this.autoIterationExitBridge.onAutoIterationSessionExit(session.workspaceId, sessionId);
567
+ }
568
+ }
408
569
  async createAcpClient(sessionId, options, session, permissionPreset, stopGeneration = this.getStopGeneration(sessionId)) {
409
570
  const sessionContext = await this.loadSessionContext(sessionId, session);
410
571
  if (!sessionContext) {
@@ -581,7 +742,7 @@ export class SessionLifecycleService {
581
742
  return;
582
743
  }
583
744
  try {
584
- await this.persistRatchetTranscript(sessionId, session);
745
+ await this.persistRatchetTranscript(sessionId);
585
746
  await this.repository.deleteSession(sessionId);
586
747
  this.sessionDomainService.clearSession(sessionId);
587
748
  logger.debug('Deleted transient ratchet session after stop', { sessionId });
@@ -617,20 +778,26 @@ export class SessionLifecycleService {
617
778
  this.sessionDomainService.clearSession(sessionId, { preserveRejections: true });
618
779
  logger.debug('Cleared inactive in-memory session state', { sessionId, reason });
619
780
  }
620
- async persistRatchetTranscript(sessionId, session) {
621
- if (!this.workspaceBridge) {
622
- logger.warn('Cannot persist ratchet transcript: no workspace bridge', { sessionId });
781
+ async persistClosedSession(sessionId) {
782
+ const session = await this.repository.getSessionById(sessionId);
783
+ if (!session) {
784
+ logger.warn('Cannot persist closed session: session not found', { sessionId });
623
785
  return;
624
786
  }
625
- const transcript = this.sessionDomainService.getTranscriptSnapshot(sessionId);
626
787
  const workspace = await workspaceDataService.findById(session.workspaceId);
627
788
  if (!workspace?.worktreePath) {
628
- logger.warn('Cannot persist ratchet transcript: no worktree path', {
789
+ logger.warn('Cannot persist closed session: no worktree path', {
629
790
  sessionId,
630
791
  workspaceId: session.workspaceId,
631
792
  });
632
793
  return;
633
794
  }
795
+ await this.hydrateProviderHistory(sessionId, {
796
+ ...session,
797
+ workspace: { worktreePath: workspace.worktreePath },
798
+ });
799
+ await this.lifecycleEventService.hydrate(sessionId);
800
+ const transcript = this.sessionDomainService.getTranscriptSnapshot(sessionId);
634
801
  await closedSessionPersistenceService.persistClosedSession({
635
802
  sessionId,
636
803
  workspaceId: session.workspaceId,
@@ -643,6 +810,9 @@ export class SessionLifecycleService {
643
810
  messages: transcript,
644
811
  });
645
812
  }
813
+ async persistRatchetTranscript(sessionId) {
814
+ await this.persistClosedSession(sessionId);
815
+ }
646
816
  async getOrCreateAcpSessionClient(sessionId, options, session, stopGeneration) {
647
817
  this.assertStartupAllowed(sessionId, stopGeneration);
648
818
  const existingAcp = this.runtimeManager.getClient(sessionId);