agent-tower 0.5.4 → 0.6.0-beta.0

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 (383) hide show
  1. package/README.md +1 -1
  2. package/dist/core/event-bus.d.ts +4 -1
  3. package/dist/core/event-bus.d.ts.map +1 -1
  4. package/dist/core/event-bus.js.map +1 -1
  5. package/dist/executors/__tests__/codex.executor.test.js +27 -21
  6. package/dist/executors/__tests__/codex.executor.test.js.map +1 -1
  7. package/dist/executors/__tests__/providers-availability.test.js +23 -0
  8. package/dist/executors/__tests__/providers-availability.test.js.map +1 -1
  9. package/dist/executors/__tests__/providers.test.js +34 -1
  10. package/dist/executors/__tests__/providers.test.js.map +1 -1
  11. package/dist/executors/codex.executor.d.ts.map +1 -1
  12. package/dist/executors/codex.executor.js +4 -0
  13. package/dist/executors/codex.executor.js.map +1 -1
  14. package/dist/executors/default-providers.json +106 -0
  15. package/dist/executors/index.d.ts +1 -1
  16. package/dist/executors/index.d.ts.map +1 -1
  17. package/dist/executors/index.js +32 -13
  18. package/dist/executors/index.js.map +1 -1
  19. package/dist/executors/providers.d.ts +12 -3
  20. package/dist/executors/providers.d.ts.map +1 -1
  21. package/dist/executors/providers.js +45 -18
  22. package/dist/executors/providers.js.map +1 -1
  23. package/dist/output/types.d.ts +32 -2
  24. package/dist/output/types.d.ts.map +1 -1
  25. package/dist/output/types.js +10 -0
  26. package/dist/output/types.js.map +1 -1
  27. package/dist/routes/__tests__/providers.test.js +181 -1
  28. package/dist/routes/__tests__/providers.test.js.map +1 -1
  29. package/dist/routes/__tests__/sessions-runtime.test.d.ts +2 -0
  30. package/dist/routes/__tests__/sessions-runtime.test.d.ts.map +1 -0
  31. package/dist/routes/__tests__/sessions-runtime.test.js +107 -0
  32. package/dist/routes/__tests__/sessions-runtime.test.js.map +1 -0
  33. package/dist/routes/providers.d.ts.map +1 -1
  34. package/dist/routes/providers.js +3 -1
  35. package/dist/routes/providers.js.map +1 -1
  36. package/dist/routes/sessions.d.ts.map +1 -1
  37. package/dist/routes/sessions.js +50 -3
  38. package/dist/routes/sessions.js.map +1 -1
  39. package/dist/routes/system.d.ts.map +1 -1
  40. package/dist/routes/system.js +8 -0
  41. package/dist/routes/system.js.map +1 -1
  42. package/dist/runtime/__tests__/acp-agent-definitions.test.d.ts +2 -0
  43. package/dist/runtime/__tests__/acp-agent-definitions.test.d.ts.map +1 -0
  44. package/dist/runtime/__tests__/acp-agent-definitions.test.js +360 -0
  45. package/dist/runtime/__tests__/acp-agent-definitions.test.js.map +1 -0
  46. package/dist/runtime/__tests__/acp-driver-cleanup.test.d.ts +2 -0
  47. package/dist/runtime/__tests__/acp-driver-cleanup.test.d.ts.map +1 -0
  48. package/dist/runtime/__tests__/acp-driver-cleanup.test.js +39 -0
  49. package/dist/runtime/__tests__/acp-driver-cleanup.test.js.map +1 -0
  50. package/dist/runtime/__tests__/acp-driver.test.d.ts +2 -0
  51. package/dist/runtime/__tests__/acp-driver.test.d.ts.map +1 -0
  52. package/dist/runtime/__tests__/acp-driver.test.js +122 -0
  53. package/dist/runtime/__tests__/acp-driver.test.js.map +1 -0
  54. package/dist/runtime/__tests__/acp-process-manager.test.d.ts +2 -0
  55. package/dist/runtime/__tests__/acp-process-manager.test.d.ts.map +1 -0
  56. package/dist/runtime/__tests__/acp-process-manager.test.js +51 -0
  57. package/dist/runtime/__tests__/acp-process-manager.test.js.map +1 -0
  58. package/dist/runtime/__tests__/acp-projector.test.d.ts +2 -0
  59. package/dist/runtime/__tests__/acp-projector.test.d.ts.map +1 -0
  60. package/dist/runtime/__tests__/acp-projector.test.js +195 -0
  61. package/dist/runtime/__tests__/acp-projector.test.js.map +1 -0
  62. package/dist/runtime/__tests__/runtime-coordinator.test.d.ts +2 -0
  63. package/dist/runtime/__tests__/runtime-coordinator.test.d.ts.map +1 -0
  64. package/dist/runtime/__tests__/runtime-coordinator.test.js +134 -0
  65. package/dist/runtime/__tests__/runtime-coordinator.test.js.map +1 -0
  66. package/dist/runtime/acp/acp-driver.d.ts +7 -0
  67. package/dist/runtime/acp/acp-driver.d.ts.map +1 -0
  68. package/dist/runtime/acp/acp-driver.js +406 -0
  69. package/dist/runtime/acp/acp-driver.js.map +1 -0
  70. package/dist/runtime/acp/agents/claude-code.d.ts +3 -0
  71. package/dist/runtime/acp/agents/claude-code.d.ts.map +1 -0
  72. package/dist/runtime/acp/agents/claude-code.js +147 -0
  73. package/dist/runtime/acp/agents/claude-code.js.map +1 -0
  74. package/dist/runtime/acp/agents/codex.d.ts +3 -0
  75. package/dist/runtime/acp/agents/codex.d.ts.map +1 -0
  76. package/dist/runtime/acp/agents/codex.js +68 -0
  77. package/dist/runtime/acp/agents/codex.js.map +1 -0
  78. package/dist/runtime/acp/agents/cursor-agent.d.ts +2 -0
  79. package/dist/runtime/acp/agents/cursor-agent.d.ts.map +1 -0
  80. package/dist/runtime/acp/agents/cursor-agent.js +12 -0
  81. package/dist/runtime/acp/agents/cursor-agent.js.map +1 -0
  82. package/dist/runtime/acp/agents/executable-resolution.d.ts +6 -0
  83. package/dist/runtime/acp/agents/executable-resolution.d.ts.map +1 -0
  84. package/dist/runtime/acp/agents/executable-resolution.js +72 -0
  85. package/dist/runtime/acp/agents/executable-resolution.js.map +1 -0
  86. package/dist/runtime/acp/agents/gemini-cli.d.ts +2 -0
  87. package/dist/runtime/acp/agents/gemini-cli.d.ts.map +1 -0
  88. package/dist/runtime/acp/agents/gemini-cli.js +13 -0
  89. package/dist/runtime/acp/agents/gemini-cli.js.map +1 -0
  90. package/dist/runtime/acp/agents/grok-build.d.ts +2 -0
  91. package/dist/runtime/acp/agents/grok-build.d.ts.map +1 -0
  92. package/dist/runtime/acp/agents/grok-build.js +26 -0
  93. package/dist/runtime/acp/agents/grok-build.js.map +1 -0
  94. package/dist/runtime/acp/agents/kiro-cli.d.ts +2 -0
  95. package/dist/runtime/acp/agents/kiro-cli.d.ts.map +1 -0
  96. package/dist/runtime/acp/agents/kiro-cli.js +18 -0
  97. package/dist/runtime/acp/agents/kiro-cli.js.map +1 -0
  98. package/dist/runtime/acp/agents/managed-directory.d.ts +6 -0
  99. package/dist/runtime/acp/agents/managed-directory.d.ts.map +1 -0
  100. package/dist/runtime/acp/agents/managed-directory.js +31 -0
  101. package/dist/runtime/acp/agents/managed-directory.js.map +1 -0
  102. package/dist/runtime/acp/agents/minion-acp-sitecustomize.d.ts +2 -0
  103. package/dist/runtime/acp/agents/minion-acp-sitecustomize.d.ts.map +1 -0
  104. package/dist/runtime/acp/agents/minion-acp-sitecustomize.js +118 -0
  105. package/dist/runtime/acp/agents/minion-acp-sitecustomize.js.map +1 -0
  106. package/dist/runtime/acp/agents/minion-code.d.ts +2 -0
  107. package/dist/runtime/acp/agents/minion-code.d.ts.map +1 -0
  108. package/dist/runtime/acp/agents/minion-code.js +53 -0
  109. package/dist/runtime/acp/agents/minion-code.js.map +1 -0
  110. package/dist/runtime/acp/agents/native-agent.d.ts +23 -0
  111. package/dist/runtime/acp/agents/native-agent.d.ts.map +1 -0
  112. package/dist/runtime/acp/agents/native-agent.js +158 -0
  113. package/dist/runtime/acp/agents/native-agent.js.map +1 -0
  114. package/dist/runtime/acp/agents/opencode.d.ts +2 -0
  115. package/dist/runtime/acp/agents/opencode.d.ts.map +1 -0
  116. package/dist/runtime/acp/agents/opencode.js +35 -0
  117. package/dist/runtime/acp/agents/opencode.js.map +1 -0
  118. package/dist/runtime/acp/agents/pi-coding-agent.d.ts +3 -0
  119. package/dist/runtime/acp/agents/pi-coding-agent.d.ts.map +1 -0
  120. package/dist/runtime/acp/agents/pi-coding-agent.js +178 -0
  121. package/dist/runtime/acp/agents/pi-coding-agent.js.map +1 -0
  122. package/dist/runtime/acp/agents/qwen-code.d.ts +3 -0
  123. package/dist/runtime/acp/agents/qwen-code.d.ts.map +1 -0
  124. package/dist/runtime/acp/agents/qwen-code.js +67 -0
  125. package/dist/runtime/acp/agents/qwen-code.js.map +1 -0
  126. package/dist/runtime/acp/agents/registry.d.ts +5 -0
  127. package/dist/runtime/acp/agents/registry.d.ts.map +1 -0
  128. package/dist/runtime/acp/agents/registry.js +34 -0
  129. package/dist/runtime/acp/agents/registry.js.map +1 -0
  130. package/dist/runtime/acp/agents/types.d.ts +35 -0
  131. package/dist/runtime/acp/agents/types.d.ts.map +1 -0
  132. package/dist/runtime/acp/agents/types.js +2 -0
  133. package/dist/runtime/acp/agents/types.js.map +1 -0
  134. package/dist/runtime/acp/codex-provider-config.d.ts +9 -0
  135. package/dist/runtime/acp/codex-provider-config.d.ts.map +1 -0
  136. package/dist/runtime/acp/codex-provider-config.js +107 -0
  137. package/dist/runtime/acp/codex-provider-config.js.map +1 -0
  138. package/dist/runtime/acp/process-manager.d.ts +33 -0
  139. package/dist/runtime/acp/process-manager.d.ts.map +1 -0
  140. package/dist/runtime/acp/process-manager.js +204 -0
  141. package/dist/runtime/acp/process-manager.js.map +1 -0
  142. package/dist/runtime/acp/projector.d.ts +23 -0
  143. package/dist/runtime/acp/projector.d.ts.map +1 -0
  144. package/dist/runtime/acp/projector.js +354 -0
  145. package/dist/runtime/acp/projector.js.map +1 -0
  146. package/dist/runtime/cli-driver.d.ts +7 -0
  147. package/dist/runtime/cli-driver.d.ts.map +1 -0
  148. package/dist/runtime/cli-driver.js +282 -0
  149. package/dist/runtime/cli-driver.js.map +1 -0
  150. package/dist/runtime/cli-parser.d.ts +5 -0
  151. package/dist/runtime/cli-parser.d.ts.map +1 -0
  152. package/dist/runtime/cli-parser.js +15 -0
  153. package/dist/runtime/cli-parser.js.map +1 -0
  154. package/dist/runtime/contracts.d.ts +109 -0
  155. package/dist/runtime/contracts.d.ts.map +1 -0
  156. package/dist/runtime/contracts.js +2 -0
  157. package/dist/runtime/contracts.js.map +1 -0
  158. package/dist/runtime/errors.d.ts +9 -0
  159. package/dist/runtime/errors.d.ts.map +1 -0
  160. package/dist/runtime/errors.js +29 -0
  161. package/dist/runtime/errors.js.map +1 -0
  162. package/dist/runtime/index.d.ts +9 -0
  163. package/dist/runtime/index.d.ts.map +1 -0
  164. package/dist/runtime/index.js +9 -0
  165. package/dist/runtime/index.js.map +1 -0
  166. package/dist/runtime/runtime-coordinator.d.ts +32 -0
  167. package/dist/runtime/runtime-coordinator.d.ts.map +1 -0
  168. package/dist/runtime/runtime-coordinator.js +327 -0
  169. package/dist/runtime/runtime-coordinator.js.map +1 -0
  170. package/dist/runtime/runtime-registry.d.ts +8 -0
  171. package/dist/runtime/runtime-registry.d.ts.map +1 -0
  172. package/dist/runtime/runtime-registry.js +17 -0
  173. package/dist/runtime/runtime-registry.js.map +1 -0
  174. package/dist/runtime/runtime-state-view.d.ts +5 -0
  175. package/dist/runtime/runtime-state-view.d.ts.map +1 -0
  176. package/dist/runtime/runtime-state-view.js +15 -0
  177. package/dist/runtime/runtime-state-view.js.map +1 -0
  178. package/dist/services/__tests__/provider-config.service.test.js +31 -2
  179. package/dist/services/__tests__/provider-config.service.test.js.map +1 -1
  180. package/dist/services/__tests__/session-manager.lifecycle.test.js +105 -2
  181. package/dist/services/__tests__/session-manager.lifecycle.test.js.map +1 -1
  182. package/dist/services/__tests__/session-manager.team-run.test.js +16 -14
  183. package/dist/services/__tests__/session-manager.team-run.test.js.map +1 -1
  184. package/dist/services/conversation.service.d.ts.map +1 -1
  185. package/dist/services/conversation.service.js +3 -0
  186. package/dist/services/conversation.service.js.map +1 -1
  187. package/dist/services/provider-config.service.d.ts.map +1 -1
  188. package/dist/services/provider-config.service.js +34 -5
  189. package/dist/services/provider-config.service.js.map +1 -1
  190. package/dist/services/provider-effective-connection.service.d.ts +7 -1
  191. package/dist/services/provider-effective-connection.service.d.ts.map +1 -1
  192. package/dist/services/provider-effective-connection.service.js +56 -1
  193. package/dist/services/provider-effective-connection.service.js.map +1 -1
  194. package/dist/services/session-manager.d.ts +32 -24
  195. package/dist/services/session-manager.d.ts.map +1 -1
  196. package/dist/services/session-manager.js +265 -448
  197. package/dist/services/session-manager.js.map +1 -1
  198. package/dist/services/team-reconciler.service.d.ts +3 -0
  199. package/dist/services/team-reconciler.service.d.ts.map +1 -1
  200. package/dist/services/team-reconciler.service.js +14 -3
  201. package/dist/services/team-reconciler.service.js.map +1 -1
  202. package/dist/services/team-run.service.d.ts.map +1 -1
  203. package/dist/services/team-run.service.js +5 -1
  204. package/dist/services/team-run.service.js.map +1 -1
  205. package/dist/services/workspace.service.d.ts +2 -0
  206. package/dist/services/workspace.service.d.ts.map +1 -1
  207. package/dist/socket/__tests__/socket-gateway.test.js +42 -0
  208. package/dist/socket/__tests__/socket-gateway.test.js.map +1 -1
  209. package/dist/socket/events.d.ts +1 -1
  210. package/dist/socket/events.d.ts.map +1 -1
  211. package/dist/socket/events.js.map +1 -1
  212. package/dist/socket/index.js +1 -1
  213. package/dist/socket/index.js.map +1 -1
  214. package/dist/socket/socket-gateway.d.ts.map +1 -1
  215. package/dist/socket/socket-gateway.js +13 -1
  216. package/dist/socket/socket-gateway.js.map +1 -1
  217. package/dist/types/index.d.ts +7 -1
  218. package/dist/types/index.d.ts.map +1 -1
  219. package/dist/types/index.js +6 -0
  220. package/dist/types/index.js.map +1 -1
  221. package/dist/utils/process-launch.d.ts.map +1 -1
  222. package/dist/utils/process-launch.js +3 -1
  223. package/dist/utils/process-launch.js.map +1 -1
  224. package/dist/utils/process-launch.test.js +17 -2
  225. package/dist/utils/process-launch.test.js.map +1 -1
  226. package/dist/web/assets/{AgentDemoPage-C3SHOCDR.js → AgentDemoPage-BpUO2gR6.js} +1 -1
  227. package/dist/web/assets/{AgentEnvironmentSettingsPage-D-BWLcC0.js → AgentEnvironmentSettingsPage-CSBMaAvL.js} +1 -1
  228. package/dist/web/assets/AgentLogo-C190Z6bw.js +1 -0
  229. package/dist/web/assets/ConversationPage-Cer_WtdC.js +3 -0
  230. package/dist/web/assets/CreateTaskInput-ByeWDjc8.js +1 -0
  231. package/dist/web/assets/{DemoPage-Cd1ggnZY.js → DemoPage-Dpb9aAKX.js} +1 -1
  232. package/dist/web/assets/{GeneralSettingsPage-B9D9wA_9.js → GeneralSettingsPage-TqbAsCZM.js} +1 -1
  233. package/dist/web/assets/{Icons-CM6Yg2lw.js → Icons-CyNNz4Z5.js} +1 -1
  234. package/dist/web/assets/{LoadingPreviewPage-BJ_Se_T_.js → LoadingPreviewPage-DmH1TGln.js} +1 -1
  235. package/dist/web/assets/{McpSettingsPage-B5GtqNme.js → McpSettingsPage-z1_NcsMR.js} +1 -1
  236. package/dist/web/assets/{MemberAvatar-yC8O30hv.js → MemberAvatar-BPoYS6Il.js} +1 -1
  237. package/dist/web/assets/{NotificationSettingsPage-BaJpYHLD.js → NotificationSettingsPage-CNsRJ6nN.js} +1 -1
  238. package/dist/web/assets/{ProfileSettingsPage-DYXew66J.js → ProfileSettingsPage-g1RKcEbo.js} +1 -1
  239. package/dist/web/assets/{ProjectKanbanPage-DbgIwiUC.js → ProjectKanbanPage-j2v5fLfg.js} +37 -36
  240. package/dist/web/assets/{ProjectSettingsPage-B2K8Kw_S.js → ProjectSettingsPage-Cp89NvwE.js} +1 -1
  241. package/dist/web/assets/ProviderSettingsPage-B-bBs2qN.js +93 -0
  242. package/dist/web/assets/{SettingsMasterDetail-ByKe_zcK.js → SettingsMasterDetail-BYB-1oyf.js} +1 -1
  243. package/dist/web/assets/{TeamSettingsPage-4sHo9Wpb.js → TeamSettingsPage-Y2zCVbVD.js} +1 -1
  244. package/dist/web/assets/agent-tower-lockup-DPAcheCt.png +0 -0
  245. package/dist/web/assets/{arc-g6Y4qqcK.js → arc-BmDnqN7Q.js} +1 -1
  246. package/dist/web/assets/{architectureDiagram-3BPJPVTR-ILRcqNPe.js → architectureDiagram-3BPJPVTR-BIG4K9gT.js} +1 -1
  247. package/dist/web/assets/{arrow-left-D0MkHwKU.js → arrow-left-CB4qTjQs.js} +1 -1
  248. package/dist/web/assets/{blockDiagram-GPEHLZMM-ZTMQ6CTT.js → blockDiagram-GPEHLZMM-CowveYw0.js} +1 -1
  249. package/dist/web/assets/{c4Diagram-AAUBKEIU-CAW8PcM8.js → c4Diagram-AAUBKEIU-CLjbYgNG.js} +1 -1
  250. package/dist/web/assets/channel-BsOpjSZw.js +1 -0
  251. package/dist/web/assets/check-BVOtZt28.js +1 -0
  252. package/dist/web/assets/{chevron-down-CmGXPki1.js → chevron-down-BXYCSpWd.js} +1 -1
  253. package/dist/web/assets/{chevron-right-jrkLz9qG.js → chevron-right-CkcSaX8x.js} +1 -1
  254. package/dist/web/assets/{chevron-up-UwBybVMK.js → chevron-up-Y8L6pbzc.js} +1 -1
  255. package/dist/web/assets/{chunk-2J33WTMH-BvuLBX1d.js → chunk-2J33WTMH-DRf_miP4.js} +1 -1
  256. package/dist/web/assets/{chunk-4BX2VUAB-DJO5Yx2A.js → chunk-4BX2VUAB-C6Ne_vwR.js} +1 -1
  257. package/dist/web/assets/{chunk-55IACEB6-Dc117Qwf.js → chunk-55IACEB6-DejCglMr.js} +1 -1
  258. package/dist/web/assets/{chunk-727SXJPM-DsZmTw67.js → chunk-727SXJPM-B0MC9Ojc.js} +1 -1
  259. package/dist/web/assets/{chunk-AQP2D5EJ-DgmF7gD6.js → chunk-AQP2D5EJ-D9f4hbMX.js} +1 -1
  260. package/dist/web/assets/{chunk-FMBD7UC4-vgh3Erl0.js → chunk-FMBD7UC4-k90hkOQq.js} +1 -1
  261. package/dist/web/assets/{chunk-ND2GUHAM-Ogtl4wwd.js → chunk-ND2GUHAM-CX2-qxPx.js} +1 -1
  262. package/dist/web/assets/{chunk-QZHKN3VN-Cif7bPpF.js → chunk-QZHKN3VN-CpFs7UD0.js} +1 -1
  263. package/dist/web/assets/{circle-alert-B6ZM3dSn.js → circle-alert-ClX4sQ2K.js} +1 -1
  264. package/dist/web/assets/classDiagram-4FO5ZUOK-KkLqUSdB.js +1 -0
  265. package/dist/web/assets/classDiagram-v2-Q7XG4LA2-KkLqUSdB.js +1 -0
  266. package/dist/web/assets/{code-block-OCS4YCEC-BQnKZly0.js → code-block-OCS4YCEC-_lkU1VUC.js} +1 -1
  267. package/dist/web/assets/{confirm-dialog-BZeaAl5j.js → confirm-dialog-BJwzwwOk.js} +1 -1
  268. package/dist/web/assets/{copy-DqNqPI8d.js → copy-jQAntc98.js} +1 -1
  269. package/dist/web/assets/{cose-bilkent-S5V4N54A-rxJB-TPw.js → cose-bilkent-S5V4N54A-CLWAfEVg.js} +1 -1
  270. package/dist/web/assets/cssMode-q0rL0Hjc.js +1 -0
  271. package/dist/web/assets/{dagre-BM42HDAG-BkB6VpXn.js → dagre-BM42HDAG-DKviWU-X.js} +1 -1
  272. package/dist/web/assets/{diagram-2AECGRRQ-BH2e-eqy.js → diagram-2AECGRRQ-L6emXRJ1.js} +1 -1
  273. package/dist/web/assets/{diagram-5GNKFQAL-B0I47Yhs.js → diagram-5GNKFQAL-CXGUerks.js} +1 -1
  274. package/dist/web/assets/{diagram-KO2AKTUF-BVXUYRvI.js → diagram-KO2AKTUF-C8FHXqeH.js} +1 -1
  275. package/dist/web/assets/{diagram-LMA3HP47-C1fEOLke.js → diagram-LMA3HP47-Cng_aa5j.js} +1 -1
  276. package/dist/web/assets/{diagram-OG6HWLK6-CVE8cOcO.js → diagram-OG6HWLK6-BEC6IDf4.js} +1 -1
  277. package/dist/web/assets/{erDiagram-TEJ5UH35-qCfnXmfp.js → erDiagram-TEJ5UH35-BWMCTdYK.js} +1 -1
  278. package/dist/web/assets/{flowDiagram-I6XJVG4X-KMhbF5QC.js → flowDiagram-I6XJVG4X-aAEYIyx2.js} +1 -1
  279. package/dist/web/assets/{folder-picker-C3BgqamQ.js → folder-picker-CioQjSzV.js} +1 -1
  280. package/dist/web/assets/{freemarker2-tYgVOcFl.js → freemarker2-DZgAIr6s.js} +2 -2
  281. package/dist/web/assets/{ganttDiagram-6RSMTGT7-CaX3qYV1.js → ganttDiagram-6RSMTGT7-DVqWnwcH.js} +1 -1
  282. package/dist/web/assets/{gitGraphDiagram-PVQCEYII-CBAlZX7C.js → gitGraphDiagram-PVQCEYII-BVWrTXBv.js} +1 -1
  283. package/dist/web/assets/handlebars-9RRQrLUb.js +1 -0
  284. package/dist/web/assets/html-BVI4F9TT.js +1 -0
  285. package/dist/web/assets/htmlMode-t0djrjSv.js +1 -0
  286. package/dist/web/assets/index-CjAJoGg5.css +1 -0
  287. package/dist/web/assets/{index-BuQDZjol.js → index-Da9mzleR.js} +4 -4
  288. package/dist/web/assets/{index-DHKgc87I.js → index-RYlllU6n.js} +18 -18
  289. package/dist/web/assets/{infoDiagram-5YYISTIA-Bp_GeFtA.js → infoDiagram-5YYISTIA-DYG1ew4x.js} +1 -1
  290. package/dist/web/assets/{ishikawaDiagram-YF4QCWOH-BfBNEV_D.js → ishikawaDiagram-YF4QCWOH-COBW1j3-.js} +1 -1
  291. package/dist/web/assets/javascript-DaUCpDvL.js +1 -0
  292. package/dist/web/assets/{journeyDiagram-JHISSGLW-DJkUQG4Z.js → journeyDiagram-JHISSGLW-BI_FlncW.js} +1 -1
  293. package/dist/web/assets/{jsonMode-D4_2geMa.js → jsonMode-A5wuW-PC.js} +2 -2
  294. package/dist/web/assets/{kanban-definition-UN3LZRKU-Bk7sA26p.js → kanban-definition-UN3LZRKU-BKrHEd7r.js} +1 -1
  295. package/dist/web/assets/{layers-BFkjcml2.js → layers-BZMh9pqm.js} +1 -1
  296. package/dist/web/assets/{linear-CmC68WUf.js → linear-DyoMQJGq.js} +1 -1
  297. package/dist/web/assets/liquid-HXqG_RIQ.js +1 -0
  298. package/dist/web/assets/{lspLanguageFeatures-vY541IS0.js → lspLanguageFeatures-s3xWnrXc.js} +1 -1
  299. package/dist/web/assets/mdx-k_7JTYp2.js +1 -0
  300. package/dist/web/assets/{mermaid-NOHMQCX5-C6wHDXti.js → mermaid-NOHMQCX5-orMHhlAj.js} +59 -61
  301. package/dist/web/assets/{message-square-Wwixl9YA.js → message-square-CStQg7sr.js} +1 -1
  302. package/dist/web/assets/{mindmap-definition-RKZ34NQL-Ck1je2Hf.js → mindmap-definition-RKZ34NQL-CKOIIjUJ.js} +1 -1
  303. package/dist/web/assets/{modal-kFSArLJi.js → modal-ZYdX5Krr.js} +1 -1
  304. package/dist/web/assets/{monaco-D3FVQqPj.js → monaco-DX9iObLO.js} +3 -3
  305. package/dist/web/assets/{pencil-5Kegi5_H.js → pencil-DoB4p30x.js} +1 -1
  306. package/dist/web/assets/{pieDiagram-4H26LBE5-6Ao8YaoY.js → pieDiagram-4H26LBE5-BW18kL8l.js} +1 -1
  307. package/dist/web/assets/python-abx8p9WS.js +1 -0
  308. package/dist/web/assets/{quadrantDiagram-W4KKPZXB-md1eBgjS.js → quadrantDiagram-W4KKPZXB-Df_F1INR.js} +1 -1
  309. package/dist/web/assets/razor-DGdDlX86.js +1 -0
  310. package/dist/web/assets/{requirementDiagram-4Y6WPE33-DJOb4wf-.js → requirementDiagram-4Y6WPE33-De6R8t8O.js} +1 -1
  311. package/dist/web/assets/{sankeyDiagram-5OEKKPKP-B7srXhHb.js → sankeyDiagram-5OEKKPKP-ChlLeN5m.js} +1 -1
  312. package/dist/web/assets/{select-C2v0aFwN.js → select-B9kjO67b.js} +1 -1
  313. package/dist/web/assets/{sequenceDiagram-3UESZ5HK-L0qwgf37.js → sequenceDiagram-3UESZ5HK-CPVnbcvz.js} +1 -1
  314. package/dist/web/assets/{stateDiagram-AJRCARHV-DGbH9dJ5.js → stateDiagram-AJRCARHV-BFS1G-aJ.js} +1 -1
  315. package/dist/web/assets/stateDiagram-v2-BHNVJYJU-DSxC8AfE.js +1 -0
  316. package/dist/web/assets/{switch-DjnF5A3R.js → switch-C2r5pofB.js} +1 -1
  317. package/dist/web/assets/{textarea-CbZnd5PO.js → textarea-BB3UoYxg.js} +1 -1
  318. package/dist/web/assets/{timeline-definition-PNZ67QCA-Dku_Yjmk.js → timeline-definition-PNZ67QCA-C1Rgs32y.js} +1 -1
  319. package/dist/web/assets/{trash-2-DA--Uikt.js → trash-2-BNiAEW-6.js} +1 -1
  320. package/dist/web/assets/{tsMode-CRNPFR24.js → tsMode-DHruHjGh.js} +2 -2
  321. package/dist/web/assets/typescript-SLYiKRhJ.js +1 -0
  322. package/dist/web/assets/{upload-hWnFHuhE.js → upload-BLLmywZY.js} +1 -1
  323. package/dist/web/assets/{use-profiles-roHXR8He.js → use-profiles-OJaV4lOv.js} +1 -1
  324. package/dist/web/assets/{use-projects-DiJMGduU.js → use-projects-D_Sq7ahS.js} +1 -1
  325. package/dist/web/assets/{use-providers-efX4HCcr.js → use-providers-SSHcslI-.js} +1 -1
  326. package/dist/web/assets/{useNormalizedLogs-xEobyuGF.js → useNormalizedLogs-DeA-fso_.js} +1 -1
  327. package/dist/web/assets/{vennDiagram-CIIHVFJN-_ufI1hxk.js → vennDiagram-CIIHVFJN-DLGPZTKg.js} +1 -1
  328. package/dist/web/assets/{wardley-L42UT6IY-Dve662aX.js → wardley-L42UT6IY-BVMPWfq9.js} +1 -1
  329. package/dist/web/assets/{wardleyDiagram-YWT4CUSO-Dn2x3M_Q.js → wardleyDiagram-YWT4CUSO-_KXuEg6G.js} +1 -1
  330. package/dist/web/assets/xml-BmreQojr.js +1 -0
  331. package/dist/web/assets/{xychartDiagram-2RQKCTM6-BMe6JPre.js → xychartDiagram-2RQKCTM6-a-Do3v5G.js} +1 -1
  332. package/dist/web/assets/yaml-CJoLHuqv.js +1 -0
  333. package/dist/web/index.html +2 -2
  334. package/node_modules/@agent-tower/shared/dist/agent-runtime-support.d.ts +9 -0
  335. package/node_modules/@agent-tower/shared/dist/agent-runtime-support.d.ts.map +1 -0
  336. package/node_modules/@agent-tower/shared/dist/agent-runtime-support.js +21 -0
  337. package/node_modules/@agent-tower/shared/dist/agent-runtime-support.js.map +1 -0
  338. package/node_modules/@agent-tower/shared/dist/index.d.ts +1 -0
  339. package/node_modules/@agent-tower/shared/dist/index.d.ts.map +1 -1
  340. package/node_modules/@agent-tower/shared/dist/index.js +1 -0
  341. package/node_modules/@agent-tower/shared/dist/index.js.map +1 -1
  342. package/node_modules/@agent-tower/shared/dist/log-adapter.d.ts +38 -3
  343. package/node_modules/@agent-tower/shared/dist/log-adapter.d.ts.map +1 -1
  344. package/node_modules/@agent-tower/shared/dist/log-adapter.js +21 -2
  345. package/node_modules/@agent-tower/shared/dist/log-adapter.js.map +1 -1
  346. package/node_modules/@agent-tower/shared/dist/provider-capabilities.d.ts.map +1 -1
  347. package/node_modules/@agent-tower/shared/dist/provider-capabilities.js +58 -0
  348. package/node_modules/@agent-tower/shared/dist/provider-capabilities.js.map +1 -1
  349. package/node_modules/@agent-tower/shared/dist/socket/events.d.ts +17 -0
  350. package/node_modules/@agent-tower/shared/dist/socket/events.d.ts.map +1 -1
  351. package/node_modules/@agent-tower/shared/dist/socket/events.js +3 -0
  352. package/node_modules/@agent-tower/shared/dist/socket/events.js.map +1 -1
  353. package/node_modules/@agent-tower/shared/dist/types.d.ts +63 -2
  354. package/node_modules/@agent-tower/shared/dist/types.d.ts.map +1 -1
  355. package/node_modules/@agent-tower/shared/dist/types.js +12 -0
  356. package/node_modules/@agent-tower/shared/dist/types.js.map +1 -1
  357. package/package.json +9 -3
  358. package/prisma/migrations/20260723000000_add_agent_runtime/migration.sql +2 -0
  359. package/prisma/schema.prisma +2 -0
  360. package/scripts/patch-claude-agent-acp.mjs +77 -0
  361. package/scripts/postinstall.js +5 -0
  362. package/dist/web/assets/AgentLogo-DOc8QQHe.js +0 -1
  363. package/dist/web/assets/ConversationPage-sknzMK-I.js +0 -3
  364. package/dist/web/assets/CreateTaskInput-Cn0k5D8K.js +0 -1
  365. package/dist/web/assets/ProviderSettingsPage-n2AFrtkZ.js +0 -93
  366. package/dist/web/assets/channel-DpisCjBV.js +0 -1
  367. package/dist/web/assets/check-VMcYyBr4.js +0 -1
  368. package/dist/web/assets/classDiagram-4FO5ZUOK-ByrAXg7x.js +0 -1
  369. package/dist/web/assets/classDiagram-v2-Q7XG4LA2-ByrAXg7x.js +0 -1
  370. package/dist/web/assets/cssMode-BvIKUSIl.js +0 -1
  371. package/dist/web/assets/handlebars-O23dzm6n.js +0 -1
  372. package/dist/web/assets/html-QaPgl834.js +0 -1
  373. package/dist/web/assets/htmlMode-CWyii3pY.js +0 -1
  374. package/dist/web/assets/index-BeEbDvc9.css +0 -1
  375. package/dist/web/assets/javascript-C_IpQDkn.js +0 -1
  376. package/dist/web/assets/liquid-C6FS7ekL.js +0 -1
  377. package/dist/web/assets/mdx-LQJToB8m.js +0 -1
  378. package/dist/web/assets/python-HMxhKAEt.js +0 -1
  379. package/dist/web/assets/razor-CfDc2DcT.js +0 -1
  380. package/dist/web/assets/stateDiagram-v2-BHNVJYJU-Bq1LxgY_.js +0 -1
  381. package/dist/web/assets/typescript-CNopspEc.js +0 -1
  382. package/dist/web/assets/xml-dqhfYLW3.js +0 -1
  383. package/dist/web/assets/yaml-BOxNX83z.js +0 -1
@@ -1,18 +1,20 @@
1
1
  import { prisma } from '../utils/index.js';
2
- import { AgentType, SessionStatus, SessionPurpose, TaskStatus, SessionContext } from '../types/index.js';
3
- import { getExecutor, getExecutorByProvider, getProviderById, ExecutionEnv, ExecutorConfigurationError, ExecutorNotFoundError, normalizeExecutorStartError, } from '../executors/index.js';
2
+ import { SessionStatus, SessionPurpose, TaskStatus, SessionContext } from '../types/index.js';
3
+ import { getProviderById, ExecutionEnv, normalizeExecutorStartError, } from '../executors/index.js';
4
4
  import { filterAgentSubprocessExternalEnv } from '../executors/execution-env.js';
5
- import { sessionMsgStoreManager, createClaudeCodeParser, createCursorAgentParser, createCodexParser, createUserMessage, addNormalizedEntry, } from '../output/index.js';
6
- import { AgentPipeline } from '../pipeline/agent-pipeline.js';
5
+ import { sessionMsgStoreManager, createUserMessage, addNormalizedEntry, } from '../output/index.js';
7
6
  import { execGit } from '../git/git-cli.js';
8
7
  import { getCommitMessageService } from '../core/container.js';
9
8
  import { TeamReconcilerService } from './team-reconciler.service.js';
10
- import { NotFoundError } from '../errors.js';
9
+ import { NotFoundError, ValidationError } from '../errors.js';
11
10
  import { ensureTaskNotDeleted } from './deleted-task-guard.js';
12
11
  import { getWorkspaceWorkingDir, isMainDirectoryWorkspace, } from './workspace-kind.js';
13
12
  import { writeErrorLog } from '../utils/error-log.js';
14
13
  import { INTERNAL_API_TOKEN_ENV, readInternalApiTokenFromEnv } from '../utils/internal-api-token.js';
15
14
  import { createHash } from 'node:crypto';
15
+ import { RuntimeType, supportsAgentRuntime } from '@agent-tower/shared';
16
+ import { getProviderRuntimeType } from '../executors/providers.js';
17
+ import { CliRuntimeDriver, AcpRuntimeDriver, RuntimeCoordinator, StaticRuntimeRegistry, setRuntimeStateSnapshot, } from '../runtime/index.js';
16
18
  const DEBUG_SNAPSHOT = process.env.DEBUG_SNAPSHOT === 'true';
17
19
  function hashForLog(value) {
18
20
  return createHash('sha256').update(value).digest('hex').slice(0, 12);
@@ -46,12 +48,9 @@ function isAgentProgressPatch(patch) {
46
48
  }
47
49
  export class SessionManager {
48
50
  eventBus;
49
- pipelines = new Map();
50
- pendingSpawns = new Map();
51
- cancelTokens = new Map();
52
51
  snapshotFlushTimers = new Map();
53
52
  snapshotFlushChains = new Map();
54
- snapshotWriterChain = Promise.resolve();
53
+ sessionPersistenceWriterChain = Promise.resolve();
55
54
  dirtySnapshots = new Set();
56
55
  persistedSnapshotHashes = new Map();
57
56
  pendingSnapshotStatus = new Map();
@@ -70,13 +69,15 @@ export class SessionManager {
70
69
  followUpReservationReleases = new Set();
71
70
  /** Incremented for every start/send cycle so late post-processing cannot affect a new turn. */
72
71
  sessionGenerations = new Map();
73
- logicalCompletionCleanupTimers = new Map();
74
72
  // 每个 session 上次写入 TeamRun 心跳时间戳的时刻,用于节流 lastHeartbeatAt 落库。
75
73
  heartbeatThrottle = new Map();
76
74
  teamReconciler;
75
+ runtimeCoordinator;
76
+ runtimeProcessIds = new Map();
77
+ runtimePermissionStates = new Map();
78
+ externalSessionPersistence = new Map();
77
79
  static SNAPSHOT_CHECKPOINT_MS = 15_000;
78
80
  static HEARTBEAT_THROTTLE_MS = 30_000;
79
- static CODEX_TURN_COMPLETION_GRACE_MS = 250;
80
81
  constructor(eventBus, teamReconciler) {
81
82
  this.eventBus = eventBus;
82
83
  this.teamReconciler = teamReconciler ?? new TeamReconcilerService({
@@ -86,6 +87,14 @@ export class SessionManager {
86
87
  // session 退出时的首次 reconcile(COMPLETED 判定 / 首次补催)仍即时执行,不依赖该定时器。
87
88
  scheduleReminders: false,
88
89
  });
90
+ this.runtimeCoordinator = new RuntimeCoordinator(new StaticRuntimeRegistry([
91
+ new CliRuntimeDriver(),
92
+ new AcpRuntimeDriver(),
93
+ ]), {
94
+ onTurnEvent: (event) => this.handleRuntimeTurnEvent(event),
95
+ onRuntimeState: (state) => this.handleRuntimeState(state),
96
+ onProcessEvent: (event) => this.handleRuntimeProcessEvent(event),
97
+ });
89
98
  // Patches only mark the snapshot dirty. A low-frequency checkpoint keeps the
90
99
  // hot stream away from SQLite while terminal paths still force a final flush.
91
100
  this.eventBus.on('session:patch', ({ sessionId, patch }) => {
@@ -105,7 +114,6 @@ export class SessionManager {
105
114
  // The parser has already written raw stdout, the final assistant entry,
106
115
  // usage and all other state from the turn.completed chunk at this point.
107
116
  this.terminalSessions.set(sessionId, SessionStatus.COMPLETED);
108
- this.scheduleLogicalCompletionCleanup(sessionId);
109
117
  this.startSessionFinalization(sessionId, 0, { logicalCompletion: true });
110
118
  });
111
119
  this.eventBus.on('session:turn-failed', ({ sessionId }) => {
@@ -115,30 +123,8 @@ export class SessionManager {
115
123
  // without an exit code. Use a synthetic non-zero code for the shared
116
124
  // finalization path so success-only post-processing cannot run.
117
125
  this.terminalSessions.set(sessionId, SessionStatus.FAILED);
118
- this.scheduleLogicalCompletionCleanup(sessionId);
119
126
  this.startSessionFinalization(sessionId, 1, { logicalCompletion: true });
120
127
  });
121
- this.eventBus.on('session:exit', ({ sessionId, exitCode }) => {
122
- const terminalStatus = this.terminalSessions.get(sessionId);
123
- this.clearLogicalCompletionCleanup(sessionId);
124
- const pipeline = this.pipelines.get(sessionId);
125
- if (DEBUG_SNAPSHOT) {
126
- console.log(`[SessionManager:snapshot] session:exit sessionId=${sessionId} exitCode=${exitCode} hasPipeline=${Boolean(pipeline)}`);
127
- }
128
- if (pipeline) {
129
- // Must call destroy() to remove MsgStore onPatch/onSessionId listeners.
130
- // Without this, stale listeners accumulate across send/exit cycles and
131
- // each subsequent pushPatch() forwards the same patch N times.
132
- pipeline.destroy();
133
- this.pipelines.delete(sessionId);
134
- }
135
- this.cancelTokens.delete(sessionId);
136
- this.heartbeatThrottle.delete(sessionId);
137
- if (terminalStatus)
138
- return;
139
- this.terminalSessions.set(sessionId, typeof exitCode === 'number' && exitCode !== 0 ? SessionStatus.FAILED : SessionStatus.COMPLETED);
140
- this.startSessionFinalization(sessionId, exitCode);
141
- });
142
128
  // NOTE: checkTaskAutoRevert is called directly (awaited) inside start()
143
129
  // and sendMessage() to guarantee the task status is updated before the
144
130
  // HTTP response is sent. A fire-and-forget EventBus listener here caused
@@ -151,6 +137,12 @@ export class SessionManager {
151
137
  include: { processes: true, workspace: true, conversation: true },
152
138
  });
153
139
  }
140
+ getRuntimeState(sessionId, runtimeType = RuntimeType.CLI) {
141
+ return this.runtimeCoordinator.getState(sessionId, runtimeType);
142
+ }
143
+ async resolveRuntimePermission(sessionId, requestId, optionId) {
144
+ await this.runtimeCoordinator.resolvePermission(sessionId, requestId, optionId);
145
+ }
154
146
  async create(workspaceId, agentType, prompt, variant = 'DEFAULT', providerId) {
155
147
  const workspace = await prisma.workspace.findUnique({
156
148
  where: { id: workspaceId },
@@ -160,11 +152,23 @@ export class SessionManager {
160
152
  throw new NotFoundError('Workspace', workspaceId);
161
153
  }
162
154
  ensureTaskNotDeleted(workspace.task);
155
+ const provider = providerId ? getProviderById(providerId) : null;
156
+ if (providerId && !provider) {
157
+ throw new ValidationError(`Provider not found: ${providerId}`);
158
+ }
159
+ if (provider && provider.agentType !== agentType) {
160
+ throw new ValidationError(`Provider '${provider.name}' belongs to agent '${provider.agentType}', not '${agentType}'`);
161
+ }
162
+ const runtimeType = provider ? getProviderRuntimeType(provider) : RuntimeType.CLI;
163
+ if (!supportsAgentRuntime(agentType, runtimeType)) {
164
+ throw new ValidationError(`Agent '${agentType}' does not support the '${runtimeType}' runtime`);
165
+ }
163
166
  return prisma.session.create({
164
167
  data: {
165
168
  workspaceId,
166
169
  context: SessionContext.WORKSPACE,
167
170
  agentType,
171
+ runtimeType,
168
172
  variant,
169
173
  providerId: providerId ?? null,
170
174
  prompt,
@@ -190,39 +194,7 @@ export class SessionManager {
190
194
  prompt: summarizeTextForLog(session.prompt),
191
195
  workingDir,
192
196
  });
193
- const agentType = session.agentType;
194
- const executor = this.resolveExecutor(agentType, session.variant, session.providerId);
195
- console.log('[SessionManager] ✅ Executor found, spawning process...');
196
- const env = ExecutionEnv.default(workingDir);
197
- // 如果有 provider,注入 provider 的环境变量
198
- if (session.providerId) {
199
- const provider = getProviderById(session.providerId);
200
- if (provider && Object.keys(provider.env).length > 0) {
201
- env.merge(filterAgentSubprocessExternalEnv(provider.env));
202
- }
203
- }
204
- this.injectAgentTowerMcpServiceEnv(env);
205
- if (!this.isConversationSession(session)) {
206
- await this.injectTeamRunInvocationEnv(id, env);
207
- }
208
- let spawnResult;
209
- try {
210
- spawnResult = await executor.spawn({
211
- workingDir,
212
- prompt: session.prompt,
213
- env,
214
- });
215
- }
216
- catch (error) {
217
- this.logSessionError('session.spawn', error, {
218
- sessionId: id,
219
- agentType: session.agentType,
220
- providerId: session.providerId,
221
- workingDir,
222
- });
223
- throw normalizeExecutorStartError(error);
224
- }
225
- await this.activateSpawnedSession(id, agentType, workingDir, spawnResult);
197
+ await this.startRuntimeTurn(session, session.prompt, session.externalSessionId);
226
198
  return session;
227
199
  }
228
200
  async startFollowUp(id, resumeFromSessionId) {
@@ -238,10 +210,11 @@ export class SessionManager {
238
210
  this.ensureExecutionRecordIsLive(session);
239
211
  const resumeFromSession = await prisma.session.findUnique({
240
212
  where: { id: resumeFromSessionId },
241
- select: { logSnapshot: true },
213
+ select: { logSnapshot: true, externalSessionId: true },
242
214
  });
243
215
  const agentSessionId = resumeFromSession
244
- ? this.resolveAgentSessionId(resumeFromSessionId, resumeFromSession.logSnapshot)
216
+ ? resumeFromSession.externalSessionId
217
+ ?? this.resolveAgentSessionId(resumeFromSessionId, resumeFromSession.logSnapshot)
245
218
  : null;
246
219
  console.log('[SessionManager] Follow-up session details:', {
247
220
  id: session.id,
@@ -252,76 +225,7 @@ export class SessionManager {
252
225
  prompt: summarizeTextForLog(session.prompt),
253
226
  workingDir: this.getExecutionWorkingDir(session),
254
227
  });
255
- const agentType = session.agentType;
256
- const executor = this.resolveExecutor(agentType, session.variant, session.providerId);
257
- const workingDir = this.getExecutionWorkingDir(session);
258
- const env = ExecutionEnv.default(workingDir);
259
- if (session.providerId) {
260
- const provider = getProviderById(session.providerId);
261
- if (provider && Object.keys(provider.env).length > 0) {
262
- env.merge(filterAgentSubprocessExternalEnv(provider.env));
263
- }
264
- }
265
- this.injectAgentTowerMcpServiceEnv(env);
266
- if (!this.isConversationSession(session)) {
267
- await this.injectTeamRunInvocationEnv(id, env);
268
- }
269
- const spawnConfig = {
270
- workingDir,
271
- prompt: session.prompt,
272
- env,
273
- };
274
- let spawnResult;
275
- if (agentSessionId && executor.spawnFollowUp) {
276
- try {
277
- spawnResult = await executor.spawnFollowUp(spawnConfig, agentSessionId);
278
- }
279
- catch (error) {
280
- console.warn(`[SessionManager] Follow-up spawn failed for ${id}, falling back to a new agent session:`, error instanceof Error ? error.message : error);
281
- writeErrorLog({
282
- level: 'warn',
283
- source: 'session.spawnFollowUpResume',
284
- message: `Follow-up spawn failed for session ${id}; falling back to a new agent session`,
285
- error,
286
- metadata: {
287
- sessionId: id,
288
- resumeFromSessionId,
289
- agentType: session.agentType,
290
- providerId: session.providerId,
291
- workingDir,
292
- },
293
- });
294
- try {
295
- spawnResult = await executor.spawn(spawnConfig);
296
- }
297
- catch (fallbackError) {
298
- this.logSessionError('session.spawnFollowUpFallback', fallbackError, {
299
- sessionId: id,
300
- resumeFromSessionId,
301
- agentType: session.agentType,
302
- providerId: session.providerId,
303
- workingDir,
304
- });
305
- throw normalizeExecutorStartError(fallbackError);
306
- }
307
- }
308
- }
309
- else {
310
- try {
311
- spawnResult = await executor.spawn(spawnConfig);
312
- }
313
- catch (error) {
314
- this.logSessionError('session.spawnFollowUp', error, {
315
- sessionId: id,
316
- resumeFromSessionId,
317
- agentType: session.agentType,
318
- providerId: session.providerId,
319
- workingDir,
320
- });
321
- throw normalizeExecutorStartError(error);
322
- }
323
- }
324
- await this.activateSpawnedSession(id, agentType, workingDir, spawnResult);
228
+ await this.startRuntimeTurn(session, session.prompt, agentSessionId);
325
229
  return session;
326
230
  }
327
231
  async sendMessage(id, message, providerId) {
@@ -353,6 +257,9 @@ export class SessionManager {
353
257
  if (String(effectiveProvider.agentType) !== session.agentType) {
354
258
  throw new Error(`Cannot switch provider: agentType mismatch. Session uses '${session.agentType}', but provider '${effectiveProvider.name}' is for '${effectiveProvider.agentType}'`);
355
259
  }
260
+ if (getProviderRuntimeType(effectiveProvider) !== this.normalizeRuntimeType(session.runtimeType)) {
261
+ throw new Error(`Cannot switch provider: runtimeType mismatch. Session uses '${session.runtimeType}', but provider '${effectiveProvider.name}' uses '${getProviderRuntimeType(effectiveProvider)}'`);
262
+ }
356
263
  }
357
264
  if (providerId && providerId !== session.providerId) {
358
265
  const switchedProvider = getProviderById(providerId);
@@ -368,18 +275,15 @@ export class SessionManager {
368
275
  // the old execution with the new one.
369
276
  this.invalidateSessionGeneration(id);
370
277
  this.beginSessionExecution(id);
371
- // Destroying the residual PTY before waiting for the completed generation's
372
- // auto-commit prevents a late exit event from being mistaken for the new
373
- // generation while the Git boundary is in flight.
374
- const existing = this.pipelines.get(id);
375
- if (existing) {
278
+ // Stop the previous turn before waiting for its auto-commit boundary. The
279
+ // coordinator suppresses that superseded turn's terminal event so it cannot
280
+ // finalize the newly reserved generation.
281
+ if (this.runtimeCoordinator.hasActiveTurn(id)) {
376
282
  if (DEBUG_SNAPSHOT) {
377
- console.log(`[SessionManager:snapshot] sendMessage checkpoint before pipeline replace sessionId=${id}`);
283
+ console.log(`[SessionManager:snapshot] sendMessage checkpoint before runtime turn replace sessionId=${id}`);
378
284
  }
379
285
  await this.flushSnapshotPersist(id);
380
- existing.destroy();
381
- this.pipelines.delete(id);
382
- this.cancelTokens.delete(id);
286
+ await this.runtimeCoordinator.abandonTurn(id);
383
287
  }
384
288
  await this.waitForPendingAutoCommit(id);
385
289
  const isNewStore = !sessionMsgStoreManager.has(id);
@@ -416,74 +320,12 @@ export class SessionManager {
416
320
  // MsgStore's patchListeners are empty at this point. Without this line
417
321
  // the user-message patch would never reach WebSocket subscribers.
418
322
  this.eventBus.emit('session:patch', { sessionId: id, patch: userPatch, seq: userPatchSeq });
419
- const agentSessionId = this.resolveAgentSessionId(id, session.logSnapshot);
420
- const agentType = session.agentType;
421
- const executor = this.resolveExecutor(agentType, session.variant, effectiveProviderId);
422
- const workingDir = this.getExecutionWorkingDir(session);
423
- const env = ExecutionEnv.default(workingDir);
424
- // 如果有 provider,注入 provider 的环境变量
425
- if (effectiveProviderId) {
426
- const provider = getProviderById(effectiveProviderId);
427
- if (provider && Object.keys(provider.env).length > 0) {
428
- env.merge(filterAgentSubprocessExternalEnv(provider.env));
429
- }
430
- }
431
- this.injectAgentTowerMcpServiceEnv(env);
432
- if (!this.isConversationSession(session)) {
433
- await this.injectTeamRunInvocationEnv(id, env);
434
- }
435
- const spawnConfig = {
436
- workingDir,
437
- prompt: message,
438
- env,
439
- };
440
- let spawnResult;
441
- if (agentSessionId && executor.spawnFollowUp) {
442
- try {
443
- spawnResult = await executor.spawnFollowUp(spawnConfig, agentSessionId);
444
- }
445
- catch (resumeError) {
446
- writeErrorLog({
447
- level: 'warn',
448
- source: 'session.messageSpawnResume',
449
- message: `Message follow-up spawn failed for session ${id}; falling back to a new agent session`,
450
- error: resumeError,
451
- metadata: {
452
- sessionId: id,
453
- agentType: session.agentType,
454
- providerId: effectiveProviderId,
455
- workingDir,
456
- },
457
- });
458
- try {
459
- spawnResult = await executor.spawn(spawnConfig);
460
- }
461
- catch (error) {
462
- this.logSessionError('session.messageSpawnFallback', error, {
463
- sessionId: id,
464
- agentType: session.agentType,
465
- providerId: effectiveProviderId,
466
- workingDir,
467
- });
468
- throw normalizeExecutorStartError(error);
469
- }
470
- }
471
- }
472
- else {
473
- try {
474
- spawnResult = await executor.spawn(spawnConfig);
475
- }
476
- catch (error) {
477
- this.logSessionError('session.messageSpawn', error, {
478
- sessionId: id,
479
- agentType: session.agentType,
480
- providerId: effectiveProviderId,
481
- workingDir,
482
- });
483
- throw normalizeExecutorStartError(error);
484
- }
323
+ const agentSessionId = session.externalSessionId
324
+ ?? this.resolveAgentSessionId(id, session.logSnapshot);
325
+ if (providerId && providerId !== session.providerId) {
326
+ await this.runtimeCoordinator.disposeSession(id);
485
327
  }
486
- await this.activateSpawnedSession(id, agentType, workingDir, spawnResult);
328
+ await this.startRuntimeTurn(session, message, agentSessionId, effectiveProviderId);
487
329
  return session;
488
330
  }
489
331
  finally {
@@ -495,19 +337,19 @@ export class SessionManager {
495
337
  if (!session)
496
338
  return null;
497
339
  const terminalStatus = this.terminalSessions.get(id);
340
+ const hasActiveTurn = this.runtimeCoordinator.hasActiveTurn(id);
498
341
  const persistedTerminal = [
499
342
  SessionStatus.COMPLETED,
500
343
  SessionStatus.FAILED,
501
344
  SessionStatus.CANCELLED,
502
345
  ].includes(session.status);
503
- if (terminalStatus || persistedTerminal) {
346
+ if (!hasActiveTurn && (terminalStatus || persistedTerminal)) {
504
347
  const pendingFinalization = this.sessionFinalizations.get(id);
505
348
  // A terminal transition already won the race. A late user stop may
506
349
  // clean up the PTY, but it must not regress the persisted status. The
507
350
  // backing TeamRun invocation may still be waiting for a room reply, so
508
351
  // it must still pass through the cancellation reconciler.
509
- this.clearLogicalCompletionCleanup(id);
510
- this.destroyActivePipeline(id);
352
+ await this.runtimeCoordinator.disposeSession(id);
511
353
  this.maybeClearTerminalState(id);
512
354
  await pendingFinalization;
513
355
  if (!options.skipTeamRunReconcile && !this.isConversationSession(session)) {
@@ -515,35 +357,13 @@ export class SessionManager {
515
357
  }
516
358
  return session;
517
359
  }
518
- this.clearLogicalCompletionCleanup(id);
519
360
  this.terminalSessions.set(id, SessionStatus.CANCELLED);
520
- const pendingSpawn = this.pendingSpawns.get(id);
521
- if (pendingSpawn) {
522
- this.pendingSpawns.delete(id);
523
- try {
524
- pendingSpawn.cancel?.cancel();
525
- }
526
- catch {
527
- // ignore cancellation failures for pending spawns
528
- }
529
- try {
530
- pendingSpawn.pty.kill();
531
- }
532
- catch {
533
- // ignore kill errors for pending spawns
534
- }
535
- }
536
- const pipeline = this.pipelines.get(id);
537
- if (pipeline) {
538
- // Try graceful shutdown via SIGINT first
539
- const cancel = this.cancelTokens.get(id);
540
- if (cancel) {
541
- cancel.cancel();
542
- }
543
- pipeline.destroy();
544
- this.pipelines.delete(id);
545
- this.cancelTokens.delete(id);
546
- }
361
+ await this.runtimeCoordinator.cancelTurn(id).catch((error) => {
362
+ this.logSessionError('session.runtimeCancel', error, { sessionId: id });
363
+ });
364
+ await this.runtimeCoordinator.disposeSession(id).catch((error) => {
365
+ this.logSessionError('session.runtimeDispose', error, { sessionId: id });
366
+ });
547
367
  const msgStore = sessionMsgStoreManager.get(id);
548
368
  if (msgStore) {
549
369
  msgStore.pushFinished();
@@ -568,19 +388,21 @@ export class SessionManager {
568
388
  await this.teamReconciler.handleSessionStopped(id);
569
389
  }
570
390
  this.eventBus.emit('session:stopped', { sessionId: id });
571
- // stop() 路径不会触发 session:exit(pipeline.destroy 先于 PTY 退出,
572
- // onExit destroyed 标志短路),handleSessionExit 不会执行,
573
- // 因此在这里释放 MsgStore。快照已在上方持久化(CANCELLED)。
391
+ // Cancellation does not run normal terminal finalization, so release the
392
+ // store after the CANCELLED snapshot has been persisted above.
574
393
  sessionMsgStoreManager.delete(id);
575
394
  this.releaseSnapshotPersistenceState(id);
576
395
  return session;
577
396
  }
578
- /**
579
- * 是否仍持有该 session 的活跃 PTY pipeline。
580
- * 供 TeamRun 心跳 watchdog 判断 invocation 是真卡死(pipeline 存活)还是孤儿(进程已脱管)。
581
- */
397
+ /** @deprecated Use hasActiveTurn(). */
582
398
  hasActivePipeline(sessionId) {
583
- return this.pipelines.has(sessionId);
399
+ return this.runtimeCoordinator.hasActiveTurn(sessionId);
400
+ }
401
+ hasActiveTurn(sessionId) {
402
+ return this.runtimeCoordinator.hasActiveTurn(sessionId);
403
+ }
404
+ isAwaitingPermission(sessionId) {
405
+ return this.runtimeCoordinator.isAwaitingPermission(sessionId);
584
406
  }
585
407
  /**
586
408
  * 节流写入 TeamRun invocation 的心跳时间戳。非 TeamRun session 无对应 invocation,updateMany 命中 0 行无副作用。
@@ -601,37 +423,16 @@ export class SessionManager {
601
423
  });
602
424
  }
603
425
  writeInput(sessionId, data) {
604
- const pipeline = this.pipelines.get(sessionId);
605
- if (!pipeline)
606
- return;
607
- pipeline.write(data);
426
+ this.runtimeCoordinator.writeInput(sessionId, data);
608
427
  }
609
428
  resize(sessionId, cols, rows) {
610
- const pipeline = this.pipelines.get(sessionId);
611
- if (!pipeline)
612
- return;
613
- pipeline.resize(cols, rows);
429
+ this.runtimeCoordinator.resize(sessionId, cols, rows);
614
430
  }
615
- /**
616
- * Destroy all active pipelines. Called on graceful server shutdown.
617
- */
618
- destroyAll() {
619
- if (this.pipelines.size > 0) {
620
- console.log(`[SessionManager] Destroying all ${this.pipelines.size} active pipelines`);
621
- }
622
- for (const [sessionId, pipeline] of this.pipelines) {
623
- const cancel = this.cancelTokens.get(sessionId);
624
- if (cancel) {
625
- cancel.cancel();
626
- }
627
- pipeline.destroy();
628
- }
629
- this.pipelines.clear();
630
- this.cancelTokens.clear();
631
- for (const timer of this.logicalCompletionCleanupTimers.values()) {
632
- clearTimeout(timer);
633
- }
634
- this.logicalCompletionCleanupTimers.clear();
431
+ /** Close all runtime driver sessions during graceful server shutdown. */
432
+ async destroyAll() {
433
+ await this.runtimeCoordinator.destroyAll();
434
+ await Promise.allSettled(this.externalSessionPersistence.values());
435
+ this.externalSessionPersistence.clear();
635
436
  this.terminalSessions.clear();
636
437
  for (const resolve of this.pendingAutoCommitResolvers.values())
637
438
  resolve();
@@ -661,163 +462,207 @@ export class SessionManager {
661
462
  }
662
463
  return null;
663
464
  }
664
- resolveExecutor(agentType, variant, providerId) {
665
- let executor;
666
- try {
667
- executor = providerId
668
- ? getExecutorByProvider(providerId)
669
- : getExecutor(agentType, variant ?? 'DEFAULT');
465
+ async startRuntimeTurn(session, prompt, resumeExternalSessionId, providerId = session.providerId) {
466
+ const workingDir = this.getExecutionWorkingDir(session);
467
+ const env = ExecutionEnv.default(workingDir);
468
+ if (providerId) {
469
+ const provider = getProviderById(providerId);
470
+ if (provider && Object.keys(provider.env).length > 0) {
471
+ env.merge(filterAgentSubprocessExternalEnv(provider.env));
472
+ }
670
473
  }
671
- catch (error) {
672
- throw new ExecutorConfigurationError(agentType, error, providerId);
474
+ this.injectAgentTowerMcpServiceEnv(env);
475
+ if (!this.isConversationSession(session)) {
476
+ await this.injectTeamRunInvocationEnv(session.id, env);
673
477
  }
674
- if (!executor) {
675
- throw new ExecutorNotFoundError(agentType, providerId);
478
+ const isNewStore = !sessionMsgStoreManager.has(session.id);
479
+ const msgStore = sessionMsgStoreManager.getOrCreate(session.id);
480
+ if (isNewStore && session.logSnapshot) {
481
+ try {
482
+ msgStore.restoreFromSnapshot(JSON.parse(session.logSnapshot));
483
+ }
484
+ catch (error) {
485
+ this.logSessionError('session.snapshotRestore', error, { sessionId: session.id });
486
+ }
676
487
  }
677
- return executor;
678
- }
679
- async activateSpawnedSession(sessionId, agentType, workingDir, spawnResult) {
680
488
  try {
681
- await this.registerSpawnedSessionIfTaskLive(sessionId, spawnResult);
682
- this.attachPipeline(sessionId, agentType, workingDir, spawnResult);
683
- this.pendingSpawns.delete(sessionId);
684
- this.eventBus.emit('session:started', { sessionId });
685
- await this.checkTaskAutoRevert(sessionId);
489
+ // Session status follows the logical Runtime turn, not the lifetime of
490
+ // its backing OS process. ACP reuses one adapter process across turns,
491
+ // so a follow-up must become RUNNING even when no process starts.
492
+ await prisma.session.update({
493
+ where: { id: session.id },
494
+ data: { status: SessionStatus.RUNNING },
495
+ });
496
+ const handle = await this.runtimeCoordinator.startTurn({
497
+ towerSessionId: session.id,
498
+ agentType: session.agentType,
499
+ runtimeType: this.normalizeRuntimeType(session.runtimeType),
500
+ variant: session.variant ?? 'DEFAULT',
501
+ providerId,
502
+ workingDir,
503
+ env,
504
+ externalSessionId: session.externalSessionId,
505
+ msgStore,
506
+ prompt,
507
+ resumeExternalSessionId,
508
+ });
509
+ // Terminal persistence is driven by Runtime turn events. Attach a catch
510
+ // so the public start/message methods do not leave a rejected handle
511
+ // unobserved after they have returned to the HTTP caller.
512
+ void handle.completion.catch(() => undefined);
513
+ this.eventBus.emit('session:started', { sessionId: session.id });
514
+ await this.checkTaskAutoRevert(session.id);
686
515
  }
687
516
  catch (error) {
688
- await this.cancelSpawnedSession(sessionId, spawnResult);
689
- throw error;
517
+ await this.runtimeCoordinator.disposeSession(session.id).catch(() => undefined);
518
+ await prisma.session.update({
519
+ where: { id: session.id },
520
+ data: { status: SessionStatus.CANCELLED },
521
+ }).catch(() => undefined);
522
+ sessionMsgStoreManager.delete(session.id);
523
+ this.releaseSnapshotPersistenceState(session.id);
524
+ this.logSessionError('session.runtimeStart', error, {
525
+ sessionId: session.id,
526
+ agentType: session.agentType,
527
+ runtimeType: session.runtimeType,
528
+ providerId,
529
+ workingDir,
530
+ });
531
+ throw normalizeExecutorStartError(error);
690
532
  }
691
533
  }
692
- attachPipeline(sessionId, agentType, workingDir, spawnResult) {
693
- const msgStore = sessionMsgStoreManager.getOrCreate(sessionId);
694
- const parser = this.createParser(agentType, workingDir, msgStore);
695
- // takeEarlyEvents 交回 spawn→attach 窗口内触发的 PTY data/exit 事件;
696
- // AgentPipeline 构造时同步重放,避免快速退出的进程丢失 exit(session 永久 RUNNING)。
697
- const pipeline = new AgentPipeline(sessionId, spawnResult.pty, parser, msgStore, this.eventBus, spawnResult.takeEarlyEvents?.());
698
- // 重放的 exit 会让 pipeline 在构造期间就自毁;已死的 pipeline 不能进 map,
699
- // 否则 hasActivePipeline 会误报存活。
700
- if (pipeline.isAlive) {
701
- this.pipelines.set(sessionId, pipeline);
702
- if (spawnResult.cancel) {
703
- this.cancelTokens.set(sessionId, spawnResult.cancel);
704
- }
534
+ handleRuntimeTurnEvent(envelope) {
535
+ const sessionId = envelope.towerSessionId;
536
+ const event = envelope.event;
537
+ if (event.type === 'stdout') {
538
+ this.eventBus.emit('session:stdout', { sessionId, data: event.data });
539
+ return;
705
540
  }
706
- }
707
- async registerSpawnedSessionIfTaskLive(sessionId, spawnResult) {
708
- this.pendingSpawns.set(sessionId, spawnResult);
709
- try {
710
- await prisma.$transaction(async (tx) => {
711
- const session = await tx.session.findUnique({
541
+ if (event.type === 'conversation_patch') {
542
+ this.eventBus.emit('session:patch', { sessionId, patch: event.patch, seq: event.seq });
543
+ return;
544
+ }
545
+ if (event.type === 'external_session_id') {
546
+ const previous = this.externalSessionPersistence.get(sessionId) ?? Promise.resolve();
547
+ const persistence = previous
548
+ .then(() => this.enqueueSessionPersistenceWrite(async () => {
549
+ await prisma.session.update({
712
550
  where: { id: sessionId },
713
- include: { workspace: { include: { task: true } }, conversation: true },
551
+ data: { externalSessionId: event.externalSessionId },
714
552
  });
715
- if (!session) {
716
- throw new NotFoundError('Session', sessionId);
717
- }
718
- if (this.isConversationSession(session)) {
719
- if (!session.conversation || session.conversation.deletedAt) {
720
- throw new NotFoundError('Conversation', session.conversationId ?? sessionId);
721
- }
722
- }
723
- else {
724
- if (!session.workspace) {
725
- throw new NotFoundError('Workspace', session.workspaceId ?? sessionId);
726
- }
727
- ensureTaskNotDeleted(session.workspace.task);
553
+ }))
554
+ .then(() => undefined)
555
+ .catch((error) => {
556
+ this.logSessionError('session.externalSessionId', error, { sessionId });
557
+ });
558
+ this.externalSessionPersistence.set(sessionId, persistence);
559
+ void persistence.finally(() => {
560
+ if (this.externalSessionPersistence.get(sessionId) === persistence) {
561
+ this.externalSessionPersistence.delete(sessionId);
728
562
  }
729
- await tx.session.update({
730
- where: { id: sessionId },
731
- data: { status: SessionStatus.RUNNING },
732
- });
733
- await tx.executionProcess.create({
734
- data: {
735
- sessionId,
736
- pid: spawnResult.pid,
737
- },
738
- });
739
563
  });
740
- const session = await prisma.session.findUnique({
741
- where: { id: sessionId },
742
- include: { workspace: { include: { task: true } }, conversation: true },
564
+ this.eventBus.emit('session:sessionId', {
565
+ sessionId,
566
+ agentSessionId: event.externalSessionId,
743
567
  });
744
- if (!session) {
745
- throw new NotFoundError('Session', sessionId);
746
- }
747
- if (this.isConversationSession(session)) {
748
- if (!session.conversation || session.conversation.deletedAt) {
749
- throw new NotFoundError('Conversation', session.conversationId ?? sessionId);
750
- }
751
- }
752
- else {
753
- if (!session.workspace) {
754
- throw new NotFoundError('Workspace', session.workspaceId ?? sessionId);
755
- }
756
- ensureTaskNotDeleted(session.workspace.task);
757
- }
758
- if (this.pendingSpawns.get(sessionId) !== spawnResult) {
759
- throw new NotFoundError(this.isConversationSession(session) ? 'Conversation' : 'Task', this.isConversationSession(session)
760
- ? (session.conversationId ?? sessionId)
761
- : (session.workspace?.task.id ?? sessionId));
762
- }
568
+ return;
763
569
  }
764
- catch (error) {
765
- await this.cancelSpawnedSession(sessionId, spawnResult);
766
- throw error;
570
+ if (event.type === 'permission_requested') {
571
+ this.eventBus.emit('session:permission_requested', {
572
+ sessionId,
573
+ permission: event.request,
574
+ });
575
+ return;
767
576
  }
768
- }
769
- async cancelSpawnedSession(sessionId, spawnResult) {
770
- if (this.pendingSpawns.get(sessionId) === spawnResult) {
771
- this.pendingSpawns.delete(sessionId);
577
+ if (event.type === 'permission_invalidated') {
578
+ this.eventBus.emit('session:permission_invalidated', {
579
+ sessionId,
580
+ turnId: envelope.turnId,
581
+ requestId: event.requestId,
582
+ });
583
+ return;
772
584
  }
773
- const pipeline = this.pipelines.get(sessionId);
774
- if (pipeline) {
775
- try {
776
- pipeline.destroy();
777
- }
778
- catch {
779
- // Continue with explicit PTY cancellation even if pipeline teardown fails.
780
- }
781
- this.pipelines.delete(sessionId);
782
- this.cancelTokens.delete(sessionId);
585
+ if (event.type === 'completed') {
586
+ this.eventBus.emit('session:turn-completed', { sessionId });
587
+ this.eventBus.emit('session:exit', { sessionId, exitCode: 0 });
588
+ return;
783
589
  }
784
- try {
785
- spawnResult.cancel?.cancel();
590
+ if (event.type === 'failed') {
591
+ this.eventBus.emit('session:turn-failed', { sessionId });
592
+ this.eventBus.emit('session:exit', { sessionId, exitCode: 1 });
786
593
  }
787
- catch {
788
- // ignore cancellation failures while compensating a deleted task race
594
+ }
595
+ handleRuntimeState(state) {
596
+ setRuntimeStateSnapshot(state);
597
+ this.eventBus.emit('session:runtime_state_changed', {
598
+ sessionId: state.sessionId,
599
+ state,
600
+ });
601
+ const awaitingPermission = state.turnState === 'AWAITING_PERMISSION';
602
+ const previous = this.runtimePermissionStates.get(state.sessionId) ?? false;
603
+ if (state.turnState === 'DISPOSED') {
604
+ this.runtimePermissionStates.delete(state.sessionId);
789
605
  }
790
- try {
791
- spawnResult.pty.kill();
606
+ else {
607
+ this.runtimePermissionStates.set(state.sessionId, awaitingPermission);
792
608
  }
793
- catch {
794
- // ignore kill failures while compensating a deleted task race
609
+ if (previous !== awaitingPermission) {
610
+ void this.invalidateTeamRunRuntimeState(state.sessionId);
795
611
  }
796
- await prisma.session.update({
797
- where: { id: sessionId },
798
- data: { status: SessionStatus.CANCELLED },
799
- }).catch(() => {
800
- // session may already have been removed by cleanup
612
+ }
613
+ async invalidateTeamRunRuntimeState(sessionId) {
614
+ const invocation = await prisma.agentInvocation.findFirst({
615
+ where: { sessionId },
616
+ select: {
617
+ teamRunId: true,
618
+ teamRun: { select: { taskId: true, task: { select: { projectId: true } } } },
619
+ },
801
620
  });
802
- await prisma.executionProcess.deleteMany({
803
- where: { sessionId, pid: spawnResult.pid },
804
- }).catch(() => {
805
- // process row may not have been created yet
621
+ if (!invocation)
622
+ return;
623
+ this.eventBus.emit('team-run:invalidated', {
624
+ teamRunId: invocation.teamRunId,
625
+ taskId: invocation.teamRun.taskId,
626
+ projectId: invocation.teamRun.task.projectId,
627
+ scopes: ['team-members', 'agent-invocations', 'team-run'],
628
+ reason: 'agent-invocation-updated',
806
629
  });
807
- // 补偿路径同样释放 MsgStore(sendMessage 在 spawn 前已 getOrCreate)
808
- sessionMsgStoreManager.delete(sessionId);
809
630
  }
810
- createParser(agentType, workingDir, msgStore) {
811
- if (agentType === AgentType.CLAUDE_CODE) {
812
- return createClaudeCodeParser(msgStore);
813
- }
814
- if (agentType === AgentType.CURSOR_AGENT) {
815
- return createCursorAgentParser(msgStore, workingDir);
816
- }
817
- if (agentType === AgentType.CODEX) {
818
- return createCodexParser(msgStore);
631
+ async handleRuntimeProcessEvent(event) {
632
+ if (event.type === 'started') {
633
+ const processRecord = await prisma.$transaction(async (tx) => {
634
+ const session = await tx.session.findUnique({
635
+ where: { id: event.towerSessionId },
636
+ include: { workspace: { include: { task: true } }, conversation: true },
637
+ });
638
+ if (!session)
639
+ throw new NotFoundError('Session', event.towerSessionId);
640
+ this.ensureExecutionRecordIsLive(session);
641
+ return tx.executionProcess.create({
642
+ data: { sessionId: event.towerSessionId, pid: event.pid },
643
+ select: { id: true },
644
+ });
645
+ });
646
+ this.runtimeProcessIds.set(event.runtimeInstanceId, processRecord.id);
647
+ return;
819
648
  }
820
- return null;
649
+ const processId = this.runtimeProcessIds.get(event.runtimeInstanceId);
650
+ if (!processId)
651
+ return;
652
+ this.runtimeProcessIds.delete(event.runtimeInstanceId);
653
+ await prisma.executionProcess.update({
654
+ where: { id: processId },
655
+ data: { exitCode: event.exitCode },
656
+ }).catch((error) => {
657
+ this.logSessionError('session.processExit', error, {
658
+ sessionId: event.towerSessionId,
659
+ processId,
660
+ exitCode: event.exitCode,
661
+ });
662
+ });
663
+ }
664
+ normalizeRuntimeType(value) {
665
+ return value === RuntimeType.ACP ? RuntimeType.ACP : RuntimeType.CLI;
821
666
  }
822
667
  injectAgentTowerMcpServiceEnv(env) {
823
668
  const serviceEnv = {};
@@ -954,7 +799,7 @@ export class SessionManager {
954
799
  .catch(() => {
955
800
  // Keep the chain alive even if previous flush failed.
956
801
  })
957
- .then(() => this.enqueueSnapshotWrite(() => this.persistSnapshot(sessionId)));
802
+ .then(() => this.enqueueSessionPersistenceWrite(() => this.persistSnapshot(sessionId)));
958
803
  this.snapshotFlushChains.set(sessionId, current);
959
804
  try {
960
805
  await current;
@@ -965,19 +810,19 @@ export class SessionManager {
965
810
  }
966
811
  }
967
812
  }
968
- async enqueueSnapshotWrite(write) {
969
- const queued = this.snapshotWriterChain
813
+ async enqueueSessionPersistenceWrite(write) {
814
+ const queued = this.sessionPersistenceWriterChain
970
815
  .catch(() => {
971
816
  // Keep the global writer alive after an isolated persistence failure.
972
817
  })
973
818
  .then(write);
974
- this.snapshotWriterChain = queued;
819
+ this.sessionPersistenceWriterChain = queued;
975
820
  try {
976
821
  await queued;
977
822
  }
978
823
  finally {
979
- if (this.snapshotWriterChain === queued) {
980
- this.snapshotWriterChain = Promise.resolve();
824
+ if (this.sessionPersistenceWriterChain === queued) {
825
+ this.sessionPersistenceWriterChain = Promise.resolve();
981
826
  }
982
827
  }
983
828
  }
@@ -1201,6 +1046,7 @@ export class SessionManager {
1201
1046
  */
1202
1047
  async handleSessionExit(sessionId, exitCode, options = {}) {
1203
1048
  const generation = options.generation ?? this.sessionGenerations.get(sessionId);
1049
+ await this.externalSessionPersistence.get(sessionId);
1204
1050
  const session = await prisma.session.findUnique({
1205
1051
  where: { id: sessionId },
1206
1052
  select: { purpose: true, context: true, conversationId: true },
@@ -1333,7 +1179,6 @@ export class SessionManager {
1333
1179
  }
1334
1180
  clearTerminalState(sessionId) {
1335
1181
  this.terminalSessions.delete(sessionId);
1336
- this.clearLogicalCompletionCleanup(sessionId);
1337
1182
  }
1338
1183
  startSessionFinalization(sessionId, exitCode, options = {}) {
1339
1184
  if (options.logicalCompletion) {
@@ -1362,10 +1207,6 @@ export class SessionManager {
1362
1207
  this.sessionFinalizations.set(sessionId, finalization);
1363
1208
  }
1364
1209
  maybeClearTerminalState(sessionId) {
1365
- if (this.pipelines.has(sessionId))
1366
- return;
1367
- if (this.logicalCompletionCleanupTimers.has(sessionId))
1368
- return;
1369
1210
  if (this.sessionFinalizations.has(sessionId))
1370
1211
  return;
1371
1212
  this.terminalSessions.delete(sessionId);
@@ -1426,30 +1267,6 @@ export class SessionManager {
1426
1267
  return;
1427
1268
  }
1428
1269
  }
1429
- destroyActivePipeline(sessionId) {
1430
- const pipeline = this.pipelines.get(sessionId);
1431
- if (pipeline) {
1432
- pipeline.destroy();
1433
- this.pipelines.delete(sessionId);
1434
- }
1435
- this.cancelTokens.delete(sessionId);
1436
- this.heartbeatThrottle.delete(sessionId);
1437
- }
1438
- clearLogicalCompletionCleanup(sessionId) {
1439
- const timer = this.logicalCompletionCleanupTimers.get(sessionId);
1440
- if (timer)
1441
- clearTimeout(timer);
1442
- this.logicalCompletionCleanupTimers.delete(sessionId);
1443
- }
1444
- scheduleLogicalCompletionCleanup(sessionId) {
1445
- this.clearLogicalCompletionCleanup(sessionId);
1446
- const timer = setTimeout(() => {
1447
- this.logicalCompletionCleanupTimers.delete(sessionId);
1448
- this.destroyActivePipeline(sessionId);
1449
- this.maybeClearTerminalState(sessionId);
1450
- }, SessionManager.CODEX_TURN_COMPLETION_GRACE_MS);
1451
- this.logicalCompletionCleanupTimers.set(sessionId, timer);
1452
- }
1453
1270
  logSessionError(source, error, metadata) {
1454
1271
  writeErrorLog({
1455
1272
  level: 'error',