@coralai/sps-cli 0.63.6 → 0.65.1

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 (458) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/README-CN.md +2 -0
  3. package/README.md +2 -0
  4. package/dist/commands/agentCommand.d.ts.map +1 -1
  5. package/dist/commands/agentCommand.js +4 -3
  6. package/dist/commands/agentCommand.js.map +1 -1
  7. package/dist/commands/agentRenderer.js +3 -3
  8. package/dist/commands/agentRenderer.js.map +1 -1
  9. package/dist/commands/cardDashboard.d.ts.map +1 -1
  10. package/dist/commands/cardDashboard.js +2 -2
  11. package/dist/commands/cardDashboard.js.map +1 -1
  12. package/dist/commands/doctor.d.ts.map +1 -1
  13. package/dist/commands/doctor.js +0 -3
  14. package/dist/commands/doctor.js.map +1 -1
  15. package/dist/commands/logs.d.ts.map +1 -1
  16. package/dist/commands/logs.js +15 -15
  17. package/dist/commands/logs.js.map +1 -1
  18. package/dist/commands/openaiAgent.d.ts +3 -0
  19. package/dist/commands/openaiAgent.d.ts.map +1 -0
  20. package/dist/commands/openaiAgent.js +81 -0
  21. package/dist/commands/openaiAgent.js.map +1 -0
  22. package/dist/commands/pipelineRunner.js.map +1 -1
  23. package/dist/commands/pipelineTick.js +1 -1
  24. package/dist/commands/pipelineTick.js.map +1 -1
  25. package/dist/commands/projectInit.d.ts +11 -1
  26. package/dist/commands/projectInit.d.ts.map +1 -1
  27. package/dist/commands/projectInit.js +38 -7
  28. package/dist/commands/projectInit.js.map +1 -1
  29. package/dist/commands/prune.d.ts +6 -0
  30. package/dist/commands/prune.d.ts.map +1 -0
  31. package/dist/commands/prune.js +87 -0
  32. package/dist/commands/prune.js.map +1 -0
  33. package/dist/commands/qaTick.js +1 -1
  34. package/dist/commands/qaTick.js.map +1 -1
  35. package/dist/commands/reset.d.ts.map +1 -1
  36. package/dist/commands/reset.js +3 -2
  37. package/dist/commands/reset.js.map +1 -1
  38. package/dist/commands/setup.d.ts.map +1 -1
  39. package/dist/commands/setup.js +8 -1
  40. package/dist/commands/setup.js.map +1 -1
  41. package/dist/commands/skillCommand.d.ts.map +1 -1
  42. package/dist/commands/skillCommand.js +2 -3
  43. package/dist/commands/skillCommand.js.map +1 -1
  44. package/dist/commands/status.js +2 -2
  45. package/dist/commands/status.js.map +1 -1
  46. package/dist/commands/stop.js +3 -3
  47. package/dist/commands/stop.js.map +1 -1
  48. package/dist/commands/tick.d.ts.map +1 -1
  49. package/dist/commands/tick.js +5 -2
  50. package/dist/commands/tick.js.map +1 -1
  51. package/dist/commands/workerDashboard.d.ts.map +1 -1
  52. package/dist/commands/workerDashboard.js +27 -26
  53. package/dist/commands/workerDashboard.js.map +1 -1
  54. package/dist/commands/workerLaunch.js +1 -1
  55. package/dist/commands/workerLaunch.js.map +1 -1
  56. package/dist/console/routes/chat.d.ts +18 -1
  57. package/dist/console/routes/chat.d.ts.map +1 -1
  58. package/dist/console/routes/chat.js +529 -40
  59. package/dist/console/routes/chat.js.map +1 -1
  60. package/dist/console/routes/fs.d.ts.map +1 -1
  61. package/dist/console/routes/fs.js +2 -1
  62. package/dist/console/routes/fs.js.map +1 -1
  63. package/dist/console/routes/logs.d.ts.map +1 -1
  64. package/dist/console/routes/logs.js +17 -5
  65. package/dist/console/routes/logs.js.map +1 -1
  66. package/dist/console/routes/projects.d.ts.map +1 -1
  67. package/dist/console/routes/projects.js +25 -2
  68. package/dist/console/routes/projects.js.map +1 -1
  69. package/dist/console/routes/workers.js +3 -3
  70. package/dist/console/routes/workers.js.map +1 -1
  71. package/dist/console-assets/assets/{ShowcasePage-BWHyDQhw.js → ShowcasePage-DFRO5bAO.js} +1 -1
  72. package/dist/console-assets/assets/{index-rQ_2k-G9.css → index-DZ28qAcd.css} +1 -1
  73. package/dist/console-assets/assets/{index-DH_TQHY4.js → index-De5GjRjY.js} +220 -215
  74. package/dist/console-assets/index.html +2 -2
  75. package/dist/core/agents/AgentBackend.d.ts +2 -2
  76. package/dist/core/agents/AgentBackend.d.ts.map +1 -1
  77. package/dist/core/agents/AgentBackend.js.map +1 -1
  78. package/dist/core/agents/DaemonBackend.d.ts +12 -2
  79. package/dist/core/agents/DaemonBackend.d.ts.map +1 -1
  80. package/dist/core/agents/DaemonBackend.js +59 -12
  81. package/dist/core/agents/DaemonBackend.js.map +1 -1
  82. package/dist/core/agents/resolveBackend.d.ts +4 -1
  83. package/dist/core/agents/resolveBackend.d.ts.map +1 -1
  84. package/dist/core/agents/resolveBackend.js +31 -1
  85. package/dist/core/agents/resolveBackend.js.map +1 -1
  86. package/dist/core/auditLog.d.ts +1 -1
  87. package/dist/core/auditLog.d.ts.map +1 -1
  88. package/dist/core/config.d.ts +5 -8
  89. package/dist/core/config.d.ts.map +1 -1
  90. package/dist/core/config.js +3 -9
  91. package/dist/core/config.js.map +1 -1
  92. package/dist/core/intel/agentsConfig.d.ts +9 -0
  93. package/dist/core/intel/agentsConfig.d.ts.map +1 -1
  94. package/dist/core/intel/agentsConfig.js +42 -0
  95. package/dist/core/intel/agentsConfig.js.map +1 -1
  96. package/dist/core/intel/types.d.ts +1 -0
  97. package/dist/core/intel/types.d.ts.map +1 -1
  98. package/dist/core/markerFile.d.ts.map +1 -1
  99. package/dist/core/markerFile.js +2 -2
  100. package/dist/core/markerFile.js.map +1 -1
  101. package/dist/core/openaiWorkerLog.d.ts +7 -0
  102. package/dist/core/openaiWorkerLog.d.ts.map +1 -0
  103. package/dist/core/openaiWorkerLog.js +98 -0
  104. package/dist/core/openaiWorkerLog.js.map +1 -0
  105. package/dist/core/paths.d.ts +1 -1
  106. package/dist/core/paths.d.ts.map +1 -1
  107. package/dist/core/paths.js +9 -7
  108. package/dist/core/paths.js.map +1 -1
  109. package/dist/core/pipelineConfig.js +7 -7
  110. package/dist/core/pipelineConfig.js.map +1 -1
  111. package/dist/core/pipelineSession.d.ts +3 -1
  112. package/dist/core/pipelineSession.d.ts.map +1 -1
  113. package/dist/core/pipelineSession.js +8 -2
  114. package/dist/core/pipelineSession.js.map +1 -1
  115. package/dist/core/processInspect.d.ts +5 -0
  116. package/dist/core/processInspect.d.ts.map +1 -1
  117. package/dist/core/processInspect.js +41 -11
  118. package/dist/core/processInspect.js.map +1 -1
  119. package/dist/core/projectPipelineAdapter.d.ts.map +1 -1
  120. package/dist/core/projectPipelineAdapter.js +4 -4
  121. package/dist/core/projectPipelineAdapter.js.map +1 -1
  122. package/dist/core/runtimeSnapshot.d.ts +2 -2
  123. package/dist/core/runtimeSnapshot.d.ts.map +1 -1
  124. package/dist/core/runtimeSnapshot.js +2 -2
  125. package/dist/core/runtimeSnapshot.js.map +1 -1
  126. package/dist/core/runtimeStore.d.ts +12 -12
  127. package/dist/core/runtimeStore.d.ts.map +1 -1
  128. package/dist/core/runtimeStore.js +16 -16
  129. package/dist/core/runtimeStore.js.map +1 -1
  130. package/dist/core/runtimeTypes.d.ts +109 -24
  131. package/dist/core/runtimeTypes.d.ts.map +1 -1
  132. package/dist/core/runtimeTypes.js +1 -1
  133. package/dist/core/sessionCleanup.d.ts +1 -1
  134. package/dist/core/sessionCleanup.d.ts.map +1 -1
  135. package/dist/core/sessionCleanup.js +2 -2
  136. package/dist/core/sessionCleanup.js.map +1 -1
  137. package/dist/core/sessionContext.d.ts +2 -2
  138. package/dist/core/sessionContext.d.ts.map +1 -1
  139. package/dist/core/sessionContext.js +1 -1
  140. package/dist/core/sessionContext.js.map +1 -1
  141. package/dist/core/sessionLiveness.d.ts +7 -6
  142. package/dist/core/sessionLiveness.d.ts.map +1 -1
  143. package/dist/core/sessionLiveness.js +3 -4
  144. package/dist/core/sessionLiveness.js.map +1 -1
  145. package/dist/core/state.d.ts +13 -12
  146. package/dist/core/state.d.ts.map +1 -1
  147. package/dist/core/state.js +9 -1
  148. package/dist/core/state.js.map +1 -1
  149. package/dist/core/workerRuntimeSummary.js +2 -2
  150. package/dist/core/workerRuntimeSummary.js.map +1 -1
  151. package/dist/core/workspacePaths.d.ts +22 -0
  152. package/dist/core/workspacePaths.d.ts.map +1 -0
  153. package/dist/core/workspacePaths.js +73 -0
  154. package/dist/core/workspacePaths.js.map +1 -0
  155. package/dist/daemon/daemonClient.d.ts +19 -22
  156. package/dist/daemon/daemonClient.d.ts.map +1 -1
  157. package/dist/daemon/daemonClient.js +82 -2
  158. package/dist/daemon/daemonClient.js.map +1 -1
  159. package/dist/daemon/sessionDaemon.d.ts +52 -7
  160. package/dist/daemon/sessionDaemon.d.ts.map +1 -1
  161. package/dist/daemon/sessionDaemon.js +40 -24
  162. package/dist/daemon/sessionDaemon.js.map +1 -1
  163. package/dist/engines/EventHandler.js +1 -1
  164. package/dist/engines/EventHandler.js.map +1 -1
  165. package/dist/engines/MonitorEngine.d.ts +2 -2
  166. package/dist/engines/MonitorEngine.d.ts.map +1 -1
  167. package/dist/engines/MonitorEngine.js +15 -17
  168. package/dist/engines/MonitorEngine.js.map +1 -1
  169. package/dist/engines/StageEngine.d.ts +5 -3
  170. package/dist/engines/StageEngine.d.ts.map +1 -1
  171. package/dist/engines/StageEngine.js +33 -18
  172. package/dist/engines/StageEngine.js.map +1 -1
  173. package/dist/main.js +41 -2
  174. package/dist/main.js.map +1 -1
  175. package/dist/manager/makeBackendFactory.d.ts +11 -4
  176. package/dist/manager/makeBackendFactory.d.ts.map +1 -1
  177. package/dist/manager/makeBackendFactory.js +28 -15
  178. package/dist/manager/makeBackendFactory.js.map +1 -1
  179. package/dist/manager/runtime-coordinator.js.map +1 -1
  180. package/dist/manager/supervisor.d.ts +7 -7
  181. package/dist/manager/supervisor.d.ts.map +1 -1
  182. package/dist/manager/supervisor.js +7 -7
  183. package/dist/manager/supervisor.js.map +1 -1
  184. package/dist/manager/worker-manager-impl.d.ts +4 -4
  185. package/dist/manager/worker-manager-impl.d.ts.map +1 -1
  186. package/dist/manager/worker-manager-impl.js +56 -24
  187. package/dist/manager/worker-manager-impl.js.map +1 -1
  188. package/dist/manager/worker-manager.d.ts +6 -6
  189. package/dist/manager/worker-manager.d.ts.map +1 -1
  190. package/dist/openai-agent/agentFactory.d.ts +10 -0
  191. package/dist/openai-agent/agentFactory.d.ts.map +1 -0
  192. package/dist/openai-agent/agentFactory.js +26 -0
  193. package/dist/openai-agent/agentFactory.js.map +1 -0
  194. package/dist/openai-agent/eventMap.d.ts +11 -0
  195. package/dist/openai-agent/eventMap.d.ts.map +1 -0
  196. package/dist/openai-agent/eventMap.js +191 -0
  197. package/dist/openai-agent/eventMap.js.map +1 -0
  198. package/dist/openai-agent/hooks.d.ts +13 -0
  199. package/dist/openai-agent/hooks.d.ts.map +1 -0
  200. package/dist/openai-agent/hooks.js +63 -0
  201. package/dist/openai-agent/hooks.js.map +1 -0
  202. package/dist/openai-agent/instructions.d.ts +3 -0
  203. package/dist/openai-agent/instructions.d.ts.map +1 -0
  204. package/dist/openai-agent/instructions.js +63 -0
  205. package/dist/openai-agent/instructions.js.map +1 -0
  206. package/dist/openai-agent/interactions/broker.d.ts +23 -0
  207. package/dist/openai-agent/interactions/broker.d.ts.map +1 -0
  208. package/dist/openai-agent/interactions/broker.js +316 -0
  209. package/dist/openai-agent/interactions/broker.js.map +1 -0
  210. package/dist/openai-agent/mcp/managedMcp.d.ts +31 -0
  211. package/dist/openai-agent/mcp/managedMcp.d.ts.map +1 -0
  212. package/dist/openai-agent/mcp/managedMcp.js +95 -0
  213. package/dist/openai-agent/mcp/managedMcp.js.map +1 -0
  214. package/dist/openai-agent/protocolWriter.d.ts +29 -0
  215. package/dist/openai-agent/protocolWriter.d.ts.map +1 -0
  216. package/dist/openai-agent/protocolWriter.js +74 -0
  217. package/dist/openai-agent/protocolWriter.js.map +1 -0
  218. package/dist/openai-agent/providerCapabilities.d.ts +31 -0
  219. package/dist/openai-agent/providerCapabilities.d.ts.map +1 -0
  220. package/dist/openai-agent/providerCapabilities.js +49 -0
  221. package/dist/openai-agent/providerCapabilities.js.map +1 -0
  222. package/dist/openai-agent/providerFactory.d.ts +18 -0
  223. package/dist/openai-agent/providerFactory.d.ts.map +1 -0
  224. package/dist/openai-agent/providerFactory.js +38 -0
  225. package/dist/openai-agent/providerFactory.js.map +1 -0
  226. package/dist/openai-agent/request.d.ts +22 -0
  227. package/dist/openai-agent/request.d.ts.map +1 -0
  228. package/dist/openai-agent/request.js +89 -0
  229. package/dist/openai-agent/request.js.map +1 -0
  230. package/dist/openai-agent/runWorker.d.ts +25 -0
  231. package/dist/openai-agent/runWorker.d.ts.map +1 -0
  232. package/dist/openai-agent/runWorker.js +635 -0
  233. package/dist/openai-agent/runWorker.js.map +1 -0
  234. package/dist/openai-agent/sandbox/execution.d.ts +36 -0
  235. package/dist/openai-agent/sandbox/execution.d.ts.map +1 -0
  236. package/dist/openai-agent/sandbox/execution.js +239 -0
  237. package/dist/openai-agent/sandbox/execution.js.map +1 -0
  238. package/dist/openai-agent/sandbox/productionDocker.d.ts +17 -0
  239. package/dist/openai-agent/sandbox/productionDocker.d.ts.map +1 -0
  240. package/dist/openai-agent/sandbox/productionDocker.js +76 -0
  241. package/dist/openai-agent/sandbox/productionDocker.js.map +1 -0
  242. package/dist/openai-agent/sdkContract.d.ts +18 -0
  243. package/dist/openai-agent/sdkContract.d.ts.map +1 -0
  244. package/dist/openai-agent/sdkContract.js +2 -0
  245. package/dist/openai-agent/sdkContract.js.map +1 -0
  246. package/dist/openai-agent/storage/atomic.d.ts +10 -0
  247. package/dist/openai-agent/storage/atomic.d.ts.map +1 -0
  248. package/dist/openai-agent/storage/atomic.js +112 -0
  249. package/dist/openai-agent/storage/atomic.js.map +1 -0
  250. package/dist/openai-agent/storage/checkpoint.d.ts +38 -0
  251. package/dist/openai-agent/storage/checkpoint.d.ts.map +1 -0
  252. package/dist/openai-agent/storage/checkpoint.js +141 -0
  253. package/dist/openai-agent/storage/checkpoint.js.map +1 -0
  254. package/dist/openai-agent/storage/errors.d.ts +8 -0
  255. package/dist/openai-agent/storage/errors.d.ts.map +1 -0
  256. package/dist/openai-agent/storage/errors.js +11 -0
  257. package/dist/openai-agent/storage/errors.js.map +1 -0
  258. package/dist/openai-agent/storage/index.d.ts +12 -0
  259. package/dist/openai-agent/storage/index.d.ts.map +1 -0
  260. package/dist/openai-agent/storage/index.js +12 -0
  261. package/dist/openai-agent/storage/index.js.map +1 -0
  262. package/dist/openai-agent/storage/interaction.d.ts +45 -0
  263. package/dist/openai-agent/storage/interaction.d.ts.map +1 -0
  264. package/dist/openai-agent/storage/interaction.js +372 -0
  265. package/dist/openai-agent/storage/interaction.js.map +1 -0
  266. package/dist/openai-agent/storage/lock.d.ts +13 -0
  267. package/dist/openai-agent/storage/lock.d.ts.map +1 -0
  268. package/dist/openai-agent/storage/lock.js +95 -0
  269. package/dist/openai-agent/storage/lock.js.map +1 -0
  270. package/dist/openai-agent/storage/metrics.d.ts +34 -0
  271. package/dist/openai-agent/storage/metrics.d.ts.map +1 -0
  272. package/dist/openai-agent/storage/metrics.js +74 -0
  273. package/dist/openai-agent/storage/metrics.js.map +1 -0
  274. package/dist/openai-agent/storage/output.d.ts +22 -0
  275. package/dist/openai-agent/storage/output.d.ts.map +1 -0
  276. package/dist/openai-agent/storage/output.js +175 -0
  277. package/dist/openai-agent/storage/output.js.map +1 -0
  278. package/dist/openai-agent/storage/session.d.ts +38 -0
  279. package/dist/openai-agent/storage/session.d.ts.map +1 -0
  280. package/dist/openai-agent/storage/session.js +216 -0
  281. package/dist/openai-agent/storage/session.js.map +1 -0
  282. package/dist/openai-agent/storage/toolLedger.d.ts +64 -0
  283. package/dist/openai-agent/storage/toolLedger.d.ts.map +1 -0
  284. package/dist/openai-agent/storage/toolLedger.js +281 -0
  285. package/dist/openai-agent/storage/toolLedger.js.map +1 -0
  286. package/dist/openai-agent/storage/workerStorage.d.ts +21 -0
  287. package/dist/openai-agent/storage/workerStorage.d.ts.map +1 -0
  288. package/dist/openai-agent/storage/workerStorage.js +38 -0
  289. package/dist/openai-agent/storage/workerStorage.js.map +1 -0
  290. package/dist/openai-agent/storage/workspace.d.ts +17 -0
  291. package/dist/openai-agent/storage/workspace.d.ts.map +1 -0
  292. package/dist/openai-agent/storage/workspace.js +67 -0
  293. package/dist/openai-agent/storage/workspace.js.map +1 -0
  294. package/dist/openai-agent/testHelpers.d.ts +9 -0
  295. package/dist/openai-agent/testHelpers.d.ts.map +1 -0
  296. package/dist/openai-agent/testHelpers.js +35 -0
  297. package/dist/openai-agent/testHelpers.js.map +1 -0
  298. package/dist/openai-agent/tools/ledgerTools.d.ts +12 -0
  299. package/dist/openai-agent/tools/ledgerTools.d.ts.map +1 -0
  300. package/dist/openai-agent/tools/ledgerTools.js +151 -0
  301. package/dist/openai-agent/tools/ledgerTools.js.map +1 -0
  302. package/dist/providers/MarkdownTaskBackend.d.ts.map +1 -1
  303. package/dist/providers/MarkdownTaskBackend.js +2 -2
  304. package/dist/providers/MarkdownTaskBackend.js.map +1 -1
  305. package/dist/providers/RuntimeSessionRuntime.d.ts +35 -15
  306. package/dist/providers/RuntimeSessionRuntime.d.ts.map +1 -1
  307. package/dist/providers/RuntimeSessionRuntime.js +239 -22
  308. package/dist/providers/RuntimeSessionRuntime.js.map +1 -1
  309. package/dist/providers/llm/chatTools.d.ts.map +1 -1
  310. package/dist/providers/llm/chatTools.js +3 -3
  311. package/dist/providers/llm/chatTools.js.map +1 -1
  312. package/dist/providers/llm/codexAuth.d.ts +6 -1
  313. package/dist/providers/llm/codexAuth.d.ts.map +1 -1
  314. package/dist/providers/llm/codexAuth.js +45 -9
  315. package/dist/providers/llm/codexAuth.js.map +1 -1
  316. package/dist/providers/llm/codexModel.d.ts +4 -1
  317. package/dist/providers/llm/codexModel.d.ts.map +1 -1
  318. package/dist/providers/llm/codexModel.js +13 -3
  319. package/dist/providers/llm/codexModel.js.map +1 -1
  320. package/dist/providers/llm/localSubscription.d.ts +1 -1
  321. package/dist/providers/llm/localSubscription.d.ts.map +1 -1
  322. package/dist/providers/llm/localSubscription.js +2 -2
  323. package/dist/providers/llm/localSubscription.js.map +1 -1
  324. package/dist/providers/runtimeEventMap.d.ts +54 -5
  325. package/dist/providers/runtimeEventMap.d.ts.map +1 -1
  326. package/dist/providers/runtimeEventMap.js +110 -11
  327. package/dist/providers/runtimeEventMap.js.map +1 -1
  328. package/dist/runtime/agentd/client.d.ts +29 -0
  329. package/dist/runtime/agentd/client.d.ts.map +1 -0
  330. package/dist/runtime/agentd/client.js +131 -0
  331. package/dist/runtime/agentd/client.js.map +1 -0
  332. package/dist/runtime/agentd/main.d.ts +10 -0
  333. package/dist/runtime/agentd/main.d.ts.map +1 -0
  334. package/dist/runtime/agentd/main.js +49 -0
  335. package/dist/runtime/agentd/main.js.map +1 -0
  336. package/dist/runtime/agentd/protocol.d.ts +74 -0
  337. package/dist/runtime/agentd/protocol.d.ts.map +1 -0
  338. package/dist/runtime/agentd/protocol.js +21 -0
  339. package/dist/runtime/agentd/protocol.js.map +1 -0
  340. package/dist/runtime/agentd/server.d.ts +15 -0
  341. package/dist/runtime/agentd/server.d.ts.map +1 -0
  342. package/dist/runtime/agentd/server.js +116 -0
  343. package/dist/runtime/agentd/server.js.map +1 -0
  344. package/dist/runtime/backends/claude/ClaudeBackend.d.ts +155 -0
  345. package/dist/runtime/backends/claude/ClaudeBackend.d.ts.map +1 -0
  346. package/dist/runtime/backends/claude/ClaudeBackend.js +398 -0
  347. package/dist/runtime/backends/claude/ClaudeBackend.js.map +1 -0
  348. package/dist/runtime/backends/claude/ClaudeSessions.d.ts +23 -0
  349. package/dist/runtime/backends/claude/ClaudeSessions.d.ts.map +1 -0
  350. package/dist/runtime/backends/claude/ClaudeSessions.js +45 -0
  351. package/dist/runtime/backends/claude/ClaudeSessions.js.map +1 -0
  352. package/dist/runtime/backends/claude/inprocTools.d.ts +17 -0
  353. package/dist/runtime/backends/claude/inprocTools.d.ts.map +1 -0
  354. package/dist/runtime/backends/claude/inprocTools.js +14 -0
  355. package/dist/runtime/backends/claude/inprocTools.js.map +1 -0
  356. package/dist/runtime/backends/process/ExternalProcessBackend.d.ts +83 -0
  357. package/dist/runtime/backends/process/ExternalProcessBackend.d.ts.map +1 -0
  358. package/dist/runtime/backends/process/ExternalProcessBackend.js +642 -0
  359. package/dist/runtime/backends/process/ExternalProcessBackend.js.map +1 -0
  360. package/dist/runtime/contract/AgentBackend.d.ts +199 -0
  361. package/dist/runtime/contract/AgentBackend.d.ts.map +1 -0
  362. package/dist/runtime/contract/AgentBackend.js +2 -0
  363. package/dist/runtime/contract/AgentBackend.js.map +1 -0
  364. package/dist/runtime/contract/events.d.ts +96 -0
  365. package/dist/runtime/contract/events.d.ts.map +1 -0
  366. package/dist/runtime/contract/events.js +2 -0
  367. package/dist/runtime/contract/events.js.map +1 -0
  368. package/dist/runtime/contract/index.d.ts +13 -0
  369. package/dist/runtime/contract/index.d.ts.map +1 -0
  370. package/dist/runtime/contract/index.js +2 -0
  371. package/dist/runtime/contract/index.js.map +1 -0
  372. package/dist/runtime/contract/lifecycle.d.ts +26 -0
  373. package/dist/runtime/contract/lifecycle.d.ts.map +1 -0
  374. package/dist/runtime/contract/lifecycle.js +6 -0
  375. package/dist/runtime/contract/lifecycle.js.map +1 -0
  376. package/dist/runtime/contract/processProtocol.d.ts +263 -0
  377. package/dist/runtime/contract/processProtocol.d.ts.map +1 -0
  378. package/dist/runtime/contract/processProtocol.js +779 -0
  379. package/dist/runtime/contract/processProtocol.js.map +1 -0
  380. package/dist/runtime/contract/service.d.ts +85 -0
  381. package/dist/runtime/contract/service.d.ts.map +1 -0
  382. package/dist/runtime/contract/service.js +2 -0
  383. package/dist/runtime/contract/service.js.map +1 -0
  384. package/dist/runtime/contract/versioning.d.ts +44 -0
  385. package/dist/runtime/contract/versioning.d.ts.map +1 -0
  386. package/dist/runtime/contract/versioning.js +10 -0
  387. package/dist/runtime/contract/versioning.js.map +1 -0
  388. package/dist/runtime/index.d.ts +21 -0
  389. package/dist/runtime/index.d.ts.map +1 -0
  390. package/dist/runtime/index.js +26 -0
  391. package/dist/runtime/index.js.map +1 -0
  392. package/dist/runtime/runtime.d.ts +21 -0
  393. package/dist/runtime/runtime.d.ts.map +1 -0
  394. package/dist/runtime/runtime.js +34 -0
  395. package/dist/runtime/runtime.js.map +1 -0
  396. package/dist/runtime/service/AgentRuntime.d.ts +60 -0
  397. package/dist/runtime/service/AgentRuntime.d.ts.map +1 -0
  398. package/dist/runtime/service/AgentRuntime.js +396 -0
  399. package/dist/runtime/service/AgentRuntime.js.map +1 -0
  400. package/dist/runtime/service/FileRecordStore.d.ts +12 -0
  401. package/dist/runtime/service/FileRecordStore.d.ts.map +1 -0
  402. package/dist/runtime/service/FileRecordStore.js +48 -0
  403. package/dist/runtime/service/FileRecordStore.js.map +1 -0
  404. package/dist/runtime/service/backendFactory.d.ts +8 -0
  405. package/dist/runtime/service/backendFactory.d.ts.map +1 -0
  406. package/dist/runtime/service/backendFactory.js +20 -0
  407. package/dist/runtime/service/backendFactory.js.map +1 -0
  408. package/dist/runtime/stub/NoopBackend.d.ts +29 -0
  409. package/dist/runtime/stub/NoopBackend.d.ts.map +1 -0
  410. package/dist/runtime/stub/NoopBackend.js +56 -0
  411. package/dist/runtime/stub/NoopBackend.js.map +1 -0
  412. package/dist/runtime/stub/NoopService.d.ts +23 -0
  413. package/dist/runtime/stub/NoopService.d.ts.map +1 -0
  414. package/dist/runtime/stub/NoopService.js +136 -0
  415. package/dist/runtime/stub/NoopService.js.map +1 -0
  416. package/dist/runtime/stub/index.d.ts +8 -0
  417. package/dist/runtime/stub/index.d.ts.map +1 -0
  418. package/dist/runtime/stub/index.js +8 -0
  419. package/dist/runtime/stub/index.js.map +1 -0
  420. package/dist/runtime/versioning/VersionManager.d.ts +31 -0
  421. package/dist/runtime/versioning/VersionManager.d.ts.map +1 -0
  422. package/dist/runtime/versioning/VersionManager.js +142 -0
  423. package/dist/runtime/versioning/VersionManager.js.map +1 -0
  424. package/dist/services/ChatService.d.ts +11 -0
  425. package/dist/services/ChatService.d.ts.map +1 -1
  426. package/dist/services/ChatService.js +35 -5
  427. package/dist/services/ChatService.js.map +1 -1
  428. package/dist/services/LogService.d.ts +10 -4
  429. package/dist/services/LogService.d.ts.map +1 -1
  430. package/dist/services/LogService.js +26 -5
  431. package/dist/services/LogService.js.map +1 -1
  432. package/dist/services/ProjectService.d.ts +56 -0
  433. package/dist/services/ProjectService.d.ts.map +1 -1
  434. package/dist/services/ProjectService.js +182 -15
  435. package/dist/services/ProjectService.js.map +1 -1
  436. package/dist/services/WorkerService.d.ts +1 -0
  437. package/dist/services/WorkerService.d.ts.map +1 -1
  438. package/dist/services/WorkerService.js +72 -0
  439. package/dist/services/WorkerService.js.map +1 -1
  440. package/dist/services/executors.d.ts +1 -0
  441. package/dist/services/executors.d.ts.map +1 -1
  442. package/dist/services/executors.js +9 -1
  443. package/dist/services/executors.js.map +1 -1
  444. package/dist/services/promptPreview.d.ts.map +1 -1
  445. package/dist/services/promptPreview.js +4 -3
  446. package/dist/services/promptPreview.js.map +1 -1
  447. package/dist/shared/runtimePaths.d.ts +4 -0
  448. package/dist/shared/runtimePaths.d.ts.map +1 -1
  449. package/dist/shared/runtimePaths.js +8 -0
  450. package/dist/shared/runtimePaths.js.map +1 -1
  451. package/dist/shared/runtimeSchemas.d.ts +6 -2
  452. package/dist/shared/runtimeSchemas.d.ts.map +1 -1
  453. package/dist/shared/runtimeSchemas.js +3 -2
  454. package/dist/shared/runtimeSchemas.js.map +1 -1
  455. package/docs/openai-daemon-agent.md +144 -0
  456. package/package.json +15 -9
  457. package/project-template/.claude/hooks/openai/on-start.sh +6 -0
  458. package/project-template/.claude/hooks/openai/on-stop.sh +6 -0
@@ -32,9 +32,11 @@ import { resolveEligibleSkillNames, SkillsManager } from '../../core/skills/inde
32
32
  import { chatTools } from '../../providers/llm/chatTools.js';
33
33
  import { skillTools } from '../../providers/llm/skillTools.js';
34
34
  import { createReviewerModel } from '../../providers/llm/modelFactory.js';
35
+ import { getAgentProvider } from '../../providers/llm/providers.js';
35
36
  import { workerTools } from '../../providers/llm/workerTools.js';
36
37
  import { toHttpStatus, toProblemJson } from '../../shared/errors.js';
37
- import { chatAttachmentsDirFor, chatSessionsDir, chatWorkspaceDir, chatWorkspacesRoot } from '../../shared/runtimePaths.js';
38
+ import { chatAttachmentsDirFor, chatSessionsDir, chatWorkspacesRoot } from '../../shared/runtimePaths.js';
39
+ import { workspacesRoot } from '../../core/workspacePaths.js';
38
40
  import { listArtifacts } from '../../shared/artifacts.js';
39
41
  import { sendResult } from '../lib/resultToJson.js';
40
42
  const MAX_CONCURRENT_SESSIONS = 5;
@@ -168,6 +170,35 @@ function endTurn(sessionId) {
168
170
  clearInterval(t.heartbeat);
169
171
  activeStreams.delete(sessionId);
170
172
  }
173
+ function appendInteractionBlock(sessionId, assistantId, blocks, event) {
174
+ const block = {
175
+ type: 'interaction',
176
+ id: event.interactionId,
177
+ kind: event.kind,
178
+ title: event.title,
179
+ schema: event.schema,
180
+ revision: event.revision ?? 1,
181
+ status: 'pending',
182
+ };
183
+ blocks.push(block);
184
+ chatBus.emit('chat.message.chunk.interaction', {
185
+ sessionId,
186
+ assistantId,
187
+ turnId: assistantId,
188
+ seq: bumpSeq(sessionId),
189
+ ...block,
190
+ });
191
+ return block;
192
+ }
193
+ function findInteractionBlock(session, interactionId) {
194
+ for (let index = session.messages.length - 1; index >= 0; index--) {
195
+ const message = session.messages[index];
196
+ const block = message.blocks?.find((candidate) => candidate.type === 'interaction' && candidate.id === interactionId);
197
+ if (block)
198
+ return { message, block };
199
+ }
200
+ return null;
201
+ }
171
202
  /** 取"最后一轮"状态:优先持久 lastTurn;旧文件无则从末条 assistant/error 消息推导(按已结束)。 */
172
203
  function deriveLastTurn(s) {
173
204
  if (s.lastTurn)
@@ -207,13 +238,17 @@ function reconcileStaleTurns(log) {
207
238
  /* non-fatal */
208
239
  }
209
240
  }
210
- async function streamAssistantResponse(sessionId, assistantId, userContent, log, attachments) {
211
- // ── ChatBackend 分发:openai 走 in-process Agents SDK;否则(claude)走下面原有 daemon/ACP 路径。──
212
- if (readSession(sessionId)?.agentKind === 'openai') {
241
+ async function streamAssistantResponse(sessionId, assistantId, userContent, log, attachments, dependencies = {}) {
242
+ const selected = readSession(sessionId);
243
+ // OpenAI 会话没有 agentMode,继续走历史 SDK 会话文件;只有显式 daemon 的
244
+ // 新会话才迁入统一宿主,避免升级后丢掉既有上下文。
245
+ if (selected?.agentKind === 'openai' &&
246
+ (selected.agentMode ?? 'sdk') === 'sdk') {
213
247
  await streamOpenaiChat(sessionId, assistantId, userContent, log);
214
248
  return;
215
249
  }
216
- if (!(await ensureDaemon())) {
250
+ const backend = selected?.agentKind === 'openai' ? 'openai' : 'claude';
251
+ if (!(await (dependencies.ensureDaemon ?? ensureDaemon)())) {
217
252
  persistAndEmitComplete(sessionId, {
218
253
  id: assistantId,
219
254
  role: 'error',
@@ -223,11 +258,11 @@ async function streamAssistantResponse(sessionId, assistantId, userContent, log,
223
258
  }, log);
224
259
  return;
225
260
  }
226
- const client = new DaemonClient();
261
+ const client = dependencies.createDaemonClient?.() ?? new DaemonClient();
227
262
  const slot = chatSlot(sessionId);
228
263
  activeSessions.add(slot);
229
264
  // v0.51.4: read per-session cwd; falls back to daemon's startup cwd when unset.
230
- // ACPWorkerRuntime resets the slot's session if cwdOverride differs from
265
+ // RuntimeSessionRuntime resets the slot's session if cwdOverride differs from
231
266
  // existing.cwd, so switching cwd between sessions is safe.
232
267
  const persisted = readSession(sessionId);
233
268
  const sessionCwd = persisted?.cwd ?? undefined;
@@ -240,12 +275,17 @@ async function streamAssistantResponse(sessionId, assistantId, userContent, log,
240
275
  // · daemon/宿主重启过 → 适配器重 spawn 并 loadSession 重载完整历史
241
276
  // B) transcript 不在 / loadSession 失败(SESSION_LOST)/ 老会话无 id 且底层已 offline
242
277
  // → 用已持久 messages 把历史重建进 prompt 兜底。
243
- const savedId = persisted?.claudeSessionId;
278
+ const savedId = backend === 'openai'
279
+ ? persisted?.runtimeSessionId
280
+ : persisted?.claudeSessionId;
244
281
  let resumeSessionId;
245
282
  let replayHistory = false;
246
283
  if (savedId) {
247
- if (sessionCwd && !jsonlExists(sessionCwd, savedId))
284
+ if (backend === 'claude' &&
285
+ sessionCwd &&
286
+ !jsonlExists(sessionCwd, savedId)) {
248
287
  replayHistory = true;
288
+ }
249
289
  else
250
290
  resumeSessionId = savedId;
251
291
  }
@@ -274,10 +314,20 @@ async function streamAssistantResponse(sessionId, assistantId, userContent, log,
274
314
  const hasCwdModelSettings = cwdModel !== undefined;
275
315
  let effModel = persisted?.model;
276
316
  let effProviderId = persisted?.providerId;
277
- if (!effModel && !effProviderId && !hasCwdModelSettings) {
278
- const w = loadAgentsConfig().worker;
279
- effModel = w.model;
280
- effProviderId = w.providerId;
317
+ const agentsConfig = loadAgentsConfig();
318
+ if (backend === 'openai') {
319
+ const openaiDefaults = agentsConfig.worker.agent === 'openai'
320
+ ? agentsConfig.worker
321
+ : {
322
+ model: agentsConfig.reviewer.model,
323
+ providerId: agentsConfig.reviewer.providerId,
324
+ };
325
+ effModel ||= openaiDefaults.model;
326
+ effProviderId ||= openaiDefaults.providerId;
327
+ }
328
+ else if (!effModel && !effProviderId && !hasCwdModelSettings) {
329
+ effModel = agentsConfig.worker.model;
330
+ effProviderId = agentsConfig.worker.providerId;
281
331
  }
282
332
  // 🔴 端点走「写文件」这条稳路:cwd 无模型配置或同模型需补配置,且用非官方 provider(codex-proxy/三方)——**无论是
283
333
  // 前端显式选的还是这里回落的**——都把 model+端点写进 cwd 的 .claude/settings.local.json,让 claude
@@ -287,7 +337,11 @@ async function streamAssistantResponse(sessionId, assistantId, userContent, log,
287
337
  // 同一模型也重写一次以补齐 provider env(例如 Ollama 无 key 时所需的占位 token)。
288
338
  // cwd 明确配置了其它模型时仍保持项目优先,不用全局/会话默认覆盖。
289
339
  const mayWriteProviderSettings = !cwdModel || !effModel || cwdModel === effModel.trim();
290
- if (sessionCwd && mayWriteProviderSettings && effProviderId && effProviderId !== 'official') {
340
+ if (backend === 'claude' &&
341
+ sessionCwd &&
342
+ mayWriteProviderSettings &&
343
+ effProviderId &&
344
+ effProviderId !== 'official') {
291
345
  try {
292
346
  writeModelSettingsFile(sessionCwd, effModel, effProviderId);
293
347
  }
@@ -295,13 +349,17 @@ async function streamAssistantResponse(sessionId, assistantId, userContent, log,
295
349
  /* 写不了(只读目录等)→ 退回 extraEnv 注入兜底 */
296
350
  }
297
351
  }
298
- const modelEnv = resolveWorkerModelEnv(effModel, effProviderId);
352
+ const modelEnv = backend === 'claude'
353
+ ? resolveWorkerModelEnv(effModel, effProviderId)
354
+ : {};
299
355
  // codex-proxy:选它(或回落到它) → 先确保 sidecar 在跑;否则按 cwd 的 settings.local.json 探测(逐项目)。
300
356
  try {
301
- if (effProviderId === CODEX_PROXY_ID)
302
- await ensureCodexProxyRunning();
303
- else
304
- await ensureCodexProxyForCwd(sessionCwd);
357
+ if (backend === 'claude') {
358
+ if (effProviderId === CODEX_PROXY_ID)
359
+ await ensureCodexProxyRunning();
360
+ else
361
+ await ensureCodexProxyForCwd(sessionCwd);
362
+ }
305
363
  }
306
364
  catch (err) {
307
365
  emitEnsureError(err);
@@ -323,6 +381,21 @@ async function streamAssistantResponse(sessionId, assistantId, userContent, log,
323
381
  // 🔴 模型不在此——留 settings.local.json 原生读(跨平台稳,不走 env)。没配=不设 outputStyle。
324
382
  const chatCfg = sessionCwd ? readProjectAgentConfig(sessionCwd, 'chat') : {};
325
383
  const chatEnv = { SPS_MANAGED: '1', ...modelEnv };
384
+ const openaiProvider = getAgentProvider(effProviderId ?? '');
385
+ const provider = backend === 'openai'
386
+ ? {
387
+ providerId: effProviderId ?? 'official',
388
+ model: effModel ?? 'gpt-5.4',
389
+ transport: effProviderId === 'codex'
390
+ ? 'codex'
391
+ : effProviderId === 'official' || !effProviderId
392
+ ? 'responses'
393
+ : 'chat_completions',
394
+ ...(openaiProvider?.baseUrl
395
+ ? { baseUrl: openaiProvider.baseUrl }
396
+ : {}),
397
+ }
398
+ : undefined;
326
399
  const ensureOpts = { mcpServers, extraEnv: chatEnv };
327
400
  if (resumeSessionId)
328
401
  ensureOpts.resumeSessionId = resumeSessionId;
@@ -330,6 +403,8 @@ async function streamAssistantResponse(sessionId, assistantId, userContent, log,
330
403
  ensureOpts.skills = skillWhitelist;
331
404
  if (chatCfg.outputStyle)
332
405
  ensureOpts.outputStyle = chatCfg.outputStyle;
406
+ if (provider)
407
+ ensureOpts.provider = provider;
333
408
  const ensureArg = ensureOpts;
334
409
  // resume 失败退回全新会话时:丢 resumeSessionId,保留 skills + 模型 env + MCP。
335
410
  const retryOpts = { mcpServers, extraEnv: chatEnv };
@@ -337,10 +412,12 @@ async function streamAssistantResponse(sessionId, assistantId, userContent, log,
337
412
  retryOpts.skills = skillWhitelist;
338
413
  if (chatCfg.outputStyle)
339
414
  retryOpts.outputStyle = chatCfg.outputStyle;
415
+ if (provider)
416
+ retryOpts.provider = provider;
340
417
  const retryArg = retryOpts;
341
418
  let rec;
342
419
  try {
343
- rec = await client.ensureSession(slot, 'claude', sessionCwd, ensureArg);
420
+ rec = await client.ensureSession(slot, backend, sessionCwd, ensureArg);
344
421
  }
345
422
  catch (err) {
346
423
  // 可恢复失败 → 退回全新会话 + 重放历史兜底:
@@ -360,7 +437,7 @@ async function streamAssistantResponse(sessionId, assistantId, userContent, log,
360
437
  }
361
438
  replayHistory = true;
362
439
  try {
363
- rec = await client.ensureSession(slot, 'claude', sessionCwd, retryArg);
440
+ rec = await client.ensureSession(slot, backend, sessionCwd, retryArg);
364
441
  }
365
442
  catch (err2) {
366
443
  emitEnsureError(err2);
@@ -372,11 +449,14 @@ async function streamAssistantResponse(sessionId, assistantId, userContent, log,
372
449
  return;
373
450
  }
374
451
  }
375
- // 持久化底层 claude sessionId(首轮 / resume 后变更)→ 后续(含跨重启)可 resume。
452
+ // 持久化底层 sessionId(首轮 / resume 后变更)→ 后续(含跨重启)可 resume。
376
453
  if (rec?.sessionId && rec.sessionId !== savedId) {
377
454
  const s = readSession(sessionId);
378
455
  if (s) {
379
- s.claudeSessionId = rec.sessionId;
456
+ if (backend === 'openai')
457
+ s.runtimeSessionId = rec.sessionId;
458
+ else
459
+ s.claudeSessionId = rec.sessionId;
380
460
  writeSession(s);
381
461
  }
382
462
  }
@@ -395,7 +475,25 @@ async function streamAssistantResponse(sessionId, assistantId, userContent, log,
395
475
  if (replayHistory) {
396
476
  log.info(`chat[${sessionId}]: durable resume 兜底 —— 用 ${persisted?.messages?.length ?? 0} 条持久消息重建上下文`);
397
477
  }
398
- const subscription = client.subscribeRun(slot);
478
+ // OpenAI 输出已 durable 化:先 startRun 确立新 runId,再按 cursor 订阅回放,
479
+ // 避免订阅时重放上一轮 terminal。Claude 无 durable sequence,仍先订阅再派活。
480
+ if (backend === 'openai') {
481
+ try {
482
+ await client.startRun(slot, fullPrompt, backend, sessionCwd);
483
+ }
484
+ catch (err) {
485
+ activeSessions.delete(slot);
486
+ persistAndEmitComplete(sessionId, {
487
+ id: assistantId,
488
+ role: 'error',
489
+ content: `startRun failed: ${err instanceof Error ? err.message : String(err)}`,
490
+ ts: new Date().toISOString(),
491
+ status: 'error',
492
+ }, log);
493
+ return;
494
+ }
495
+ }
496
+ const subscription = client.subscribeRunResilient(slot);
399
497
  // 复用 POST 时 startTurn 预登记的进行中回合(blocks 引用,后续就地追加);未预登记则退化自建(防御)。
400
498
  const blocks = activeStreams.get(sessionId)?.blocks ?? startTurn(sessionId, assistantId);
401
499
  let accumulatedText = '';
@@ -407,8 +505,14 @@ async function streamAssistantResponse(sessionId, assistantId, userContent, log,
407
505
  // 据此写回 claudeSessionId = durable resume 锚(ACP 路在 ensureSession 已拿到,不发此事件)。
408
506
  try {
409
507
  const s = readSession(sessionId);
410
- if (s && s.claudeSessionId !== evt.sessionId) {
411
- s.claudeSessionId = evt.sessionId;
508
+ const currentId = backend === 'openai'
509
+ ? s?.runtimeSessionId
510
+ : s?.claudeSessionId;
511
+ if (s && currentId !== evt.sessionId) {
512
+ if (backend === 'openai')
513
+ s.runtimeSessionId = evt.sessionId;
514
+ else
515
+ s.claudeSessionId = evt.sessionId;
412
516
  writeSession(s);
413
517
  }
414
518
  }
@@ -493,15 +597,27 @@ async function streamAssistantResponse(sessionId, assistantId, userContent, log,
493
597
  locations: evt.locations,
494
598
  });
495
599
  }
600
+ else if (evt.event === 'interaction') {
601
+ appendInteractionBlock(sessionId, assistantId, blocks, evt);
602
+ }
496
603
  else if (evt.event === 'complete') {
497
604
  const ts = new Date().toISOString();
498
605
  const isError = evt.stopReason === 'failed';
499
606
  const isCancelled = evt.stopReason === 'cancelled';
607
+ const isWaitingInput = evt.stopReason === 'waiting_input';
608
+ if (isWaitingInput) {
609
+ const pendingInteraction = [...blocks]
610
+ .reverse()
611
+ .find((block) => block.type === 'interaction' && block.status === 'pending');
612
+ if (pendingInteraction && typeof evt.sequence === 'number') {
613
+ pendingInteraction.resumeAfterSequence = evt.sequence;
614
+ }
615
+ }
500
616
  // 兜底:正常收尾(end_turn)但一段 text 都没流到,且本回合有过工具调用 —— 大概率是
501
617
  // claude-agent-acp 丢了「工具后收尾文本」的 agent_message_chunk 推流(见 recoverTrailingText)。
502
618
  // 从转录按「最后一次工具调用之后」精确捞回那段文本,补进 content + 尾随一个 text block。
503
619
  if (!isError && !isCancelled && accumulatedText.trim() === '' &&
504
- sessionCwd && rec?.sessionId) {
620
+ backend === 'claude' && sessionCwd && rec?.sessionId) {
505
621
  const lastTool = [...blocks].reverse().find((b) => b.type === 'tool_use');
506
622
  if (lastTool && lastTool.type === 'tool_use') {
507
623
  try {
@@ -519,7 +635,13 @@ async function streamAssistantResponse(sessionId, assistantId, userContent, log,
519
635
  id: assistantId,
520
636
  role: isError ? 'error' : 'assistant',
521
637
  content: accumulatedText.trim() ||
522
- (isError ? `stopped: ${evt.stopReason}` : isCancelled ? '(已中断)' : ''),
638
+ (isError
639
+ ? evt.error ?? `stopped: ${evt.stopReason}`
640
+ : isCancelled
641
+ ? '(已中断)'
642
+ : isWaitingInput
643
+ ? '等待用户输入'
644
+ : ''),
523
645
  blocks: blocks.length > 0 ? blocks : undefined,
524
646
  truncated: truncated || undefined,
525
647
  ts,
@@ -531,7 +653,9 @@ async function streamAssistantResponse(sessionId, assistantId, userContent, log,
531
653
  }
532
654
  })();
533
655
  try {
534
- await client.startRun(slot, fullPrompt, 'claude', sessionCwd);
656
+ if (backend === 'claude') {
657
+ await client.startRun(slot, fullPrompt, backend, sessionCwd);
658
+ }
535
659
  }
536
660
  catch (err) {
537
661
  subscription.cancel();
@@ -579,6 +703,197 @@ async function streamAssistantResponse(sessionId, assistantId, userContent, log,
579
703
  endTurn(sessionId); // 安全网:completepath 已 endTurn;此处兜底停心跳/撤快照(重复调用无害)
580
704
  }
581
705
  }
706
+ function openaiResumeEnsureArgs(sessionId, session) {
707
+ if (!session.runtimeSessionId) {
708
+ throw new Error('OpenAI runtime session id is missing');
709
+ }
710
+ const cwd = session.cwd ?? undefined;
711
+ const config = loadAgentsConfig();
712
+ const defaults = config.worker.agent === 'openai'
713
+ ? config.worker
714
+ : {
715
+ model: config.reviewer.model,
716
+ providerId: config.reviewer.providerId,
717
+ };
718
+ const model = session.model || defaults.model;
719
+ const providerId = session.providerId || defaults.providerId || 'official';
720
+ const configuredProvider = getAgentProvider(providerId);
721
+ const spsMcp = spsMcpStdioConfig({
722
+ cwd: cwd ?? process.cwd(),
723
+ project: cwd ? basename(cwd) : 'chat',
724
+ agentId: sessionId,
725
+ });
726
+ return {
727
+ cwd,
728
+ resumeSessionId: session.runtimeSessionId,
729
+ opts: {
730
+ resumeSessionId: session.runtimeSessionId,
731
+ mcpServers: [
732
+ { name: 'sps', command: spsMcp.command, args: spsMcp.args },
733
+ ],
734
+ extraEnv: { SPS_MANAGED: '1' },
735
+ provider: {
736
+ providerId,
737
+ model,
738
+ transport: providerId === 'codex'
739
+ ? 'codex'
740
+ : providerId === 'official'
741
+ ? 'responses'
742
+ : 'chat_completions',
743
+ ...(configuredProvider?.baseUrl
744
+ ? { baseUrl: configuredProvider.baseUrl }
745
+ : {}),
746
+ },
747
+ },
748
+ };
749
+ }
750
+ async function streamInteractionResponse(sessionId, assistantId, client, slot, afterSequence, log) {
751
+ const blocks = activeStreams.get(sessionId)?.blocks ??
752
+ startTurn(sessionId, assistantId);
753
+ const subscription = client.subscribeRunResilient(slot, afterSequence);
754
+ let accumulatedText = '';
755
+ let truncated = false;
756
+ try {
757
+ for await (const evt of subscription) {
758
+ if (evt.event === 'text') {
759
+ if (truncated)
760
+ continue;
761
+ const budget = MAX_ASSISTANT_CONTENT_BYTES -
762
+ Buffer.byteLength(accumulatedText, 'utf8');
763
+ if (budget <= 0) {
764
+ truncated = true;
765
+ continue;
766
+ }
767
+ const piece = Buffer.byteLength(evt.text, 'utf8') <= budget
768
+ ? evt.text
769
+ : Buffer.from(evt.text, 'utf8')
770
+ .subarray(0, budget)
771
+ .toString('utf8');
772
+ accumulatedText += piece;
773
+ const last = blocks[blocks.length - 1];
774
+ if (last?.type === 'text')
775
+ last.text += piece;
776
+ else
777
+ blocks.push({ type: 'text', text: piece });
778
+ chatBus.emit('chat.message.chunk.text', {
779
+ sessionId,
780
+ assistantId,
781
+ turnId: assistantId,
782
+ seq: bumpSeq(sessionId),
783
+ text: piece,
784
+ });
785
+ if (piece.length < evt.text.length)
786
+ truncated = true;
787
+ }
788
+ else if (evt.event === 'tool_use') {
789
+ const input = sanitizeToolInput(evt.input);
790
+ blocks.push({
791
+ type: 'tool_use',
792
+ id: evt.id,
793
+ title: evt.title,
794
+ kind: evt.kind,
795
+ status: evt.status,
796
+ input,
797
+ locations: evt.locations,
798
+ });
799
+ chatBus.emit('chat.message.chunk.tool_use', {
800
+ sessionId,
801
+ assistantId,
802
+ turnId: assistantId,
803
+ seq: bumpSeq(sessionId),
804
+ id: evt.id,
805
+ title: evt.title,
806
+ kind: evt.kind,
807
+ status: evt.status,
808
+ input,
809
+ locations: evt.locations,
810
+ });
811
+ }
812
+ else if (evt.event === 'tool_update') {
813
+ const tool = blocks.find((block) => block.type === 'tool_use' && block.id === evt.id);
814
+ const input = evt.input == null ? undefined : sanitizeToolInput(evt.input);
815
+ if (tool?.type === 'tool_use') {
816
+ tool.status = evt.status;
817
+ if (input !== undefined &&
818
+ !isEmptyToolInput(input) &&
819
+ isEmptyToolInput(tool.input)) {
820
+ tool.input = input;
821
+ }
822
+ if (evt.locations != null && tool.locations == null) {
823
+ tool.locations = evt.locations;
824
+ }
825
+ }
826
+ chatBus.emit('chat.message.chunk.tool_update', {
827
+ sessionId,
828
+ assistantId,
829
+ turnId: assistantId,
830
+ seq: bumpSeq(sessionId),
831
+ id: evt.id,
832
+ status: evt.status,
833
+ input,
834
+ locations: evt.locations,
835
+ });
836
+ }
837
+ else if (evt.event === 'interaction') {
838
+ appendInteractionBlock(sessionId, assistantId, blocks, evt);
839
+ }
840
+ else if (evt.event === 'complete') {
841
+ const waitingInput = evt.stopReason === 'waiting_input';
842
+ const failed = evt.stopReason === 'failed';
843
+ const cancelled = evt.stopReason === 'cancelled';
844
+ if (waitingInput) {
845
+ const pending = [...blocks]
846
+ .reverse()
847
+ .find((block) => block.type === 'interaction' &&
848
+ block.status === 'pending');
849
+ if (pending && typeof evt.sequence === 'number') {
850
+ pending.resumeAfterSequence = evt.sequence;
851
+ }
852
+ }
853
+ persistAndEmitComplete(sessionId, {
854
+ id: assistantId,
855
+ role: failed ? 'error' : 'assistant',
856
+ content: accumulatedText.trim() ||
857
+ (failed
858
+ ? evt.error ?? 'interaction resume failed'
859
+ : cancelled
860
+ ? '(已中断)'
861
+ : waitingInput
862
+ ? '等待用户输入'
863
+ : ''),
864
+ blocks: blocks.length ? blocks : undefined,
865
+ truncated: truncated || undefined,
866
+ ts: new Date().toISOString(),
867
+ status: failed ? 'error' : 'complete',
868
+ }, log);
869
+ return;
870
+ }
871
+ }
872
+ throw new Error('Interaction event stream ended without a terminal event');
873
+ }
874
+ catch (error) {
875
+ if (activeStreams.has(sessionId)) {
876
+ persistAndEmitComplete(sessionId, {
877
+ id: assistantId,
878
+ role: 'error',
879
+ content: `interaction resume failed: ${error instanceof Error ? error.message : String(error)}`,
880
+ ts: new Date().toISOString(),
881
+ status: 'error',
882
+ }, log);
883
+ }
884
+ }
885
+ finally {
886
+ subscription.cancel();
887
+ try {
888
+ await client.clearRun(slot);
889
+ }
890
+ catch {
891
+ /* best effort */
892
+ }
893
+ activeSessions.delete(slot);
894
+ endTurn(sessionId);
895
+ }
896
+ }
582
897
  // ─── openai ChatBackend 适配器 ──────────────────────────────────────
583
898
  // claude 走 daemon/ACP(上面);openai 走 in-process Agents SDK run(stream),
584
899
  // 把文本流映射成与 claude 一致的 chunk 事件 + 同样 persistAndEmitComplete 落库。
@@ -776,7 +1091,7 @@ function persistAndEmitComplete(sessionId, msg, log) {
776
1091
  }
777
1092
  endTurn(sessionId); // 停心跳 + 撤销进行中快照(始终执行,含 session 缺失兜底)
778
1093
  }
779
- export function createChatRoute(log, chat) {
1094
+ export function createChatRoute(log, chat, dependencies = {}) {
780
1095
  const app = new Hono();
781
1096
  // v0.57:进程启动即对账 —— 上个进程遗留的 lastTurn=running(死回合)翻成 error,
782
1097
  // 让轮询兜底(GET lastTurn)在宿主/进程重启后也讲真话,不永卡 running。
@@ -789,11 +1104,19 @@ export function createChatRoute(log, chat) {
789
1104
  });
790
1105
  app.post('/sessions', async (c) => {
791
1106
  const body = (await c.req.json().catch(() => ({})));
1107
+ const agentKind = body.agentKind === 'openai' ? 'openai' : 'claude';
1108
+ const configuredWorker = loadAgentsConfig().worker;
1109
+ // 新建会话默认 daemon:openai 走真正的独立进程 agent(sps openai-agent,带 sandbox + 工具 + 技能)。
1110
+ // 旧会话(已持久 agentMode='sdk')不受影响,dispatch 侧仍按各自持久值走。
1111
+ const agentMode = body.agentMode ??
1112
+ (configuredWorker.agent === agentKind ? configuredWorker.mode : 'daemon');
792
1113
  const r = await chat.create({
793
1114
  title: body.title,
794
1115
  project: body.project,
795
1116
  cwd: body.cwd,
796
- agentKind: body.agentKind === 'openai' ? 'openai' : 'claude',
1117
+ namespace: body.namespace,
1118
+ agentKind,
1119
+ agentMode,
797
1120
  model: body.model,
798
1121
  providerId: body.providerId,
799
1122
  dedicated: body.dedicated === true,
@@ -813,8 +1136,9 @@ export function createChatRoute(log, chat) {
813
1136
  ...r.value,
814
1137
  model: displayChatModel(c.req.param('id'), r.value.agentKind, cwd),
815
1138
  };
816
- // P1:对话专属工作目录(~/.coral/chats/<id>)才扫产物 —— 真项目 cwd 不扫(会列出整仓)。
817
- if (cwd && cwd.startsWith(chatWorkspacesRoot())) {
1139
+ // P1:对话专属工作目录才扫产物 —— 真项目 cwd 不扫(会列出整仓)。
1140
+ // 兼容新根 <WORKSPACES_ROOT> 与 legacy ~/.coral/chats(双读,不硬切)
1141
+ if (cwd && (cwd.startsWith(workspacesRoot()) || cwd.startsWith(chatWorkspacesRoot()))) {
818
1142
  detail.artifacts = listArtifacts(cwd);
819
1143
  }
820
1144
  return c.json(detail);
@@ -828,14 +1152,14 @@ export function createChatRoute(log, chat) {
828
1152
  const r = await chat.delete(id);
829
1153
  if (!r.ok)
830
1154
  return c.json(toProblemJson(r.error), toHttpStatus(r.error));
831
- // v0.51.8:删除该 session 的附件目录(best effort
832
- // P1:同时删除对话专属工作目录 ~/.coral/chats/<id>(仅 dedicated 会话有,按 id 键定,安全)
1155
+ // v0.51.8:删除该 session 的私有附件暂存目录(best effort)。
1156
+ // 🔴 只清附件,**绝不删工作区目录**——目录是用户产物,且同一 cwd 可被多个 chat 共用;
1157
+ // 工作区的删除只走"项目管理"里用户显式选择的完整删除,不由 chat 删除触发。
833
1158
  try {
834
1159
  const { rmSync } = await import('node:fs');
835
- for (const dir of [chatAttachmentsDirFor(id), chatWorkspaceDir(id)]) {
836
- if (existsSync(dir))
837
- rmSync(dir, { recursive: true, force: true });
838
- }
1160
+ const attachments = chatAttachmentsDirFor(id);
1161
+ if (existsSync(attachments))
1162
+ rmSync(attachments, { recursive: true, force: true });
839
1163
  }
840
1164
  catch {
841
1165
  /* non-fatal */
@@ -843,6 +1167,169 @@ export function createChatRoute(log, chat) {
843
1167
  chatBus.emit('chat.session.deleted', { sessionId: id });
844
1168
  return c.body(null, 204);
845
1169
  });
1170
+ app.post('/sessions/:id/interactions/:interactionId', async (c) => {
1171
+ const id = c.req.param('id');
1172
+ const interactionId = c.req.param('interactionId');
1173
+ const session = readSession(id);
1174
+ if (!session) {
1175
+ return c.json({ type: 'not-found', title: 'Session not found', status: 404 }, 404);
1176
+ }
1177
+ if (session.agentKind !== 'openai' ||
1178
+ (session.agentMode ?? 'sdk') !== 'daemon') {
1179
+ return c.json({
1180
+ type: 'validation',
1181
+ title: 'Session does not support durable interactions',
1182
+ status: 422,
1183
+ }, 422);
1184
+ }
1185
+ const found = findInteractionBlock(session, interactionId);
1186
+ if (!found) {
1187
+ return c.json({ type: 'not-found', title: 'Interaction not found', status: 404 }, 404);
1188
+ }
1189
+ if (found.block.status !== 'pending') {
1190
+ return c.json({
1191
+ type: 'conflict',
1192
+ title: `Interaction is already ${found.block.status}`,
1193
+ status: 409,
1194
+ }, 409);
1195
+ }
1196
+ const body = (await c.req.json().catch(() => null));
1197
+ if (!body ||
1198
+ !Object.prototype.hasOwnProperty.call(body, 'answer') ||
1199
+ !Number.isInteger(body.expectedRevision)) {
1200
+ return c.json({
1201
+ type: 'validation',
1202
+ title: 'answer and integer expectedRevision are required',
1203
+ status: 422,
1204
+ }, 422);
1205
+ }
1206
+ if (body.expectedRevision !== found.block.revision) {
1207
+ return c.json({
1208
+ type: 'conflict',
1209
+ title: 'Interaction revision changed',
1210
+ status: 409,
1211
+ detail: `expected ${body.expectedRevision}, current ${found.block.revision}`,
1212
+ }, 409);
1213
+ }
1214
+ const afterSequence = found.block.resumeAfterSequence;
1215
+ if (!Number.isInteger(afterSequence)) {
1216
+ return c.json({
1217
+ type: 'conflict',
1218
+ title: 'Interaction checkpoint cursor is unavailable',
1219
+ status: 409,
1220
+ }, 409);
1221
+ }
1222
+ const slot = chatSlot(id);
1223
+ if (activeSessions.has(slot)) {
1224
+ return c.json({ type: 'conflict', title: 'session busy', status: 409 }, 409);
1225
+ }
1226
+ if (!(await (dependencies.ensureDaemon ?? ensureDaemon)())) {
1227
+ return c.json({ type: 'unavailable', title: 'Agent daemon unavailable', status: 503 }, 503);
1228
+ }
1229
+ const client = dependencies.createDaemonClient?.() ?? new DaemonClient();
1230
+ try {
1231
+ const ensure = openaiResumeEnsureArgs(id, session);
1232
+ await client.ensureSession(slot, 'openai', ensure.cwd, ensure.opts);
1233
+ await client.submitInteraction(slot, interactionId, body.answer, body.expectedRevision);
1234
+ }
1235
+ catch (error) {
1236
+ return c.json({
1237
+ type: 'interaction-resume',
1238
+ title: 'Interaction could not be resumed',
1239
+ status: 409,
1240
+ detail: error instanceof Error ? error.message : String(error),
1241
+ }, 409);
1242
+ }
1243
+ const latest = readSession(id);
1244
+ const latestFound = latest
1245
+ ? findInteractionBlock(latest, interactionId)
1246
+ : null;
1247
+ if (latest && latestFound) {
1248
+ latestFound.block.status = 'answered';
1249
+ latestFound.block.answer = body.answer;
1250
+ latestFound.block.revision += 1;
1251
+ writeSession(latest);
1252
+ }
1253
+ chatBus.emit('chat.message.interaction.updated', {
1254
+ sessionId: id,
1255
+ interactionId,
1256
+ status: 'answered',
1257
+ revision: found.block.revision + 1,
1258
+ answer: body.answer,
1259
+ });
1260
+ const assistantId = randomUUID();
1261
+ activeSessions.add(slot);
1262
+ startTurn(id, assistantId);
1263
+ chatBus.emit('chat.message.pending', {
1264
+ sessionId: id,
1265
+ assistantId,
1266
+ turnId: assistantId,
1267
+ seq: 0,
1268
+ ts: new Date().toISOString(),
1269
+ });
1270
+ void streamInteractionResponse(id, assistantId, client, slot, afterSequence, log);
1271
+ return c.json({ assistantId }, 202);
1272
+ });
1273
+ app.post('/sessions/:id/interactions/:interactionId/cancel', async (c) => {
1274
+ const id = c.req.param('id');
1275
+ const interactionId = c.req.param('interactionId');
1276
+ const session = readSession(id);
1277
+ if (!session) {
1278
+ return c.json({ type: 'not-found', title: 'Session not found', status: 404 }, 404);
1279
+ }
1280
+ const found = findInteractionBlock(session, interactionId);
1281
+ if (!found) {
1282
+ return c.json({ type: 'not-found', title: 'Interaction not found', status: 404 }, 404);
1283
+ }
1284
+ const body = (await c.req.json().catch(() => null));
1285
+ if (found.block.status !== 'pending' ||
1286
+ !body ||
1287
+ !Number.isInteger(body.expectedRevision) ||
1288
+ body.expectedRevision !== found.block.revision) {
1289
+ return c.json({
1290
+ type: 'conflict',
1291
+ title: 'Interaction is no longer pending at this revision',
1292
+ status: 409,
1293
+ }, 409);
1294
+ }
1295
+ if (!(await (dependencies.ensureDaemon ?? ensureDaemon)())) {
1296
+ return c.json({
1297
+ type: 'unavailable',
1298
+ title: 'Agent daemon unavailable',
1299
+ status: 503,
1300
+ }, 503);
1301
+ }
1302
+ const client = dependencies.createDaemonClient?.() ?? new DaemonClient();
1303
+ try {
1304
+ const ensure = openaiResumeEnsureArgs(id, session);
1305
+ await client.ensureSession(chatSlot(id), 'openai', ensure.cwd, ensure.opts);
1306
+ await client.cancelInteraction(chatSlot(id), interactionId, body.expectedRevision);
1307
+ }
1308
+ catch (error) {
1309
+ return c.json({
1310
+ type: 'interaction-cancel',
1311
+ title: 'Interaction could not be cancelled',
1312
+ status: 409,
1313
+ detail: error instanceof Error ? error.message : String(error),
1314
+ }, 409);
1315
+ }
1316
+ const latest = readSession(id);
1317
+ const latestFound = latest
1318
+ ? findInteractionBlock(latest, interactionId)
1319
+ : null;
1320
+ if (latest && latestFound) {
1321
+ latestFound.block.status = 'cancelled';
1322
+ latestFound.block.revision += 1;
1323
+ writeSession(latest);
1324
+ }
1325
+ chatBus.emit('chat.message.interaction.updated', {
1326
+ sessionId: id,
1327
+ interactionId,
1328
+ status: 'cancelled',
1329
+ revision: found.block.revision + 1,
1330
+ });
1331
+ return c.body(null, 204);
1332
+ });
846
1333
  app.post('/sessions/:id/interrupt', async (c) => {
847
1334
  const r = await chat.interrupt(c.req.param('id'));
848
1335
  if (!r.ok)
@@ -933,7 +1420,7 @@ export function createChatRoute(log, chat) {
933
1420
  seq: 0,
934
1421
  ts: new Date().toISOString(),
935
1422
  });
936
- streamAssistantResponse(id, assistantId, body.content, log, attachments).catch((err) => {
1423
+ streamAssistantResponse(id, assistantId, body.content, log, attachments, dependencies).catch((err) => {
937
1424
  log.error(`chat[${id}] stream error: ${err instanceof Error ? err.message : String(err)}`);
938
1425
  // 极端:streamAssistantResponse 未走到任何 persistAndEmitComplete 就抛 → 兜底落错 + 收尾。
939
1426
  if (activeStreams.has(id)) {
@@ -968,6 +1455,8 @@ export function createChatStreamRoute() {
968
1455
  'chat.message.chunk.text',
969
1456
  'chat.message.chunk.tool_use',
970
1457
  'chat.message.chunk.tool_update',
1458
+ 'chat.message.chunk.interaction',
1459
+ 'chat.message.interaction.updated',
971
1460
  'chat.message.complete',
972
1461
  'chat.message.error', // v0.57:明确的回合异常终止(带 turnId)
973
1462
  'chat.message.heartbeat', // v0.57:流式进行中心跳(带 turnId),区分"还在生成"vs"卡死"