@seawork/server 1.0.22-rc.3 → 2.0.2-rc.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (349) hide show
  1. package/dist/scripts/supervisor-entrypoint.js +48 -8
  2. package/dist/scripts/supervisor-entrypoint.js.map +1 -1
  3. package/dist/scripts/supervisor-native-classifier.js +77 -5
  4. package/dist/scripts/supervisor-native-classifier.js.map +1 -1
  5. package/dist/scripts/supervisor-stdio-tail.js +27 -0
  6. package/dist/scripts/supervisor-stdio-tail.js.map +1 -0
  7. package/dist/scripts/supervisor.js +12 -0
  8. package/dist/scripts/supervisor.js.map +1 -1
  9. package/dist/server/client/daemon-client.d.ts +142 -2
  10. package/dist/server/client/daemon-client.d.ts.map +1 -1
  11. package/dist/server/client/daemon-client.js +384 -3
  12. package/dist/server/client/daemon-client.js.map +1 -1
  13. package/dist/server/server/agent/agent-manager.d.ts +55 -3
  14. package/dist/server/server/agent/agent-manager.d.ts.map +1 -1
  15. package/dist/server/server/agent/agent-manager.js +324 -45
  16. package/dist/server/server/agent/agent-manager.js.map +1 -1
  17. package/dist/server/server/agent/agent-metadata-generator.d.ts +1 -0
  18. package/dist/server/server/agent/agent-metadata-generator.d.ts.map +1 -1
  19. package/dist/server/server/agent/agent-metadata-generator.js +8 -0
  20. package/dist/server/server/agent/agent-metadata-generator.js.map +1 -1
  21. package/dist/server/server/agent/agent-projections.js +7 -2
  22. package/dist/server/server/agent/agent-projections.js.map +1 -1
  23. package/dist/server/server/agent/agent-response-loop.d.ts +3 -1
  24. package/dist/server/server/agent/agent-response-loop.d.ts.map +1 -1
  25. package/dist/server/server/agent/agent-response-loop.js +33 -6
  26. package/dist/server/server/agent/agent-response-loop.js.map +1 -1
  27. package/dist/server/server/agent/agent-sdk-types.d.ts +43 -1
  28. package/dist/server/server/agent/agent-sdk-types.d.ts.map +1 -1
  29. package/dist/server/server/agent/claude-memory.d.ts +4 -0
  30. package/dist/server/server/agent/claude-memory.d.ts.map +1 -0
  31. package/dist/server/server/agent/claude-memory.js +97 -0
  32. package/dist/server/server/agent/claude-memory.js.map +1 -0
  33. package/dist/server/server/agent/mcp-server.d.ts.map +1 -1
  34. package/dist/server/server/agent/mcp-server.js +247 -0
  35. package/dist/server/server/agent/mcp-server.js.map +1 -1
  36. package/dist/server/server/agent/mcp-shared.d.ts +2 -0
  37. package/dist/server/server/agent/mcp-shared.d.ts.map +1 -1
  38. package/dist/server/server/agent/provider-launch-config.d.ts +6 -139
  39. package/dist/server/server/agent/provider-launch-config.d.ts.map +1 -1
  40. package/dist/server/server/agent/provider-launch-config.js +65 -33
  41. package/dist/server/server/agent/provider-launch-config.js.map +1 -1
  42. package/dist/server/server/agent/provider-manifest.d.ts +1 -0
  43. package/dist/server/server/agent/provider-manifest.d.ts.map +1 -1
  44. package/dist/server/server/agent/provider-manifest.js +36 -0
  45. package/dist/server/server/agent/provider-manifest.js.map +1 -1
  46. package/dist/server/server/agent/provider-registry.d.ts.map +1 -1
  47. package/dist/server/server/agent/provider-registry.js +4 -0
  48. package/dist/server/server/agent/provider-registry.js.map +1 -1
  49. package/dist/server/server/agent/provider-snapshot-manager.d.ts +3 -1
  50. package/dist/server/server/agent/provider-snapshot-manager.d.ts.map +1 -1
  51. package/dist/server/server/agent/provider-snapshot-manager.js +13 -0
  52. package/dist/server/server/agent/provider-snapshot-manager.js.map +1 -1
  53. package/dist/server/server/agent/providers/claude-agent.d.ts.map +1 -1
  54. package/dist/server/server/agent/providers/claude-agent.js +141 -27
  55. package/dist/server/server/agent/providers/claude-agent.js.map +1 -1
  56. package/dist/server/server/agent/providers/codex/tool-call-mapper.d.ts.map +1 -1
  57. package/dist/server/server/agent/providers/codex/tool-call-mapper.js +14 -1
  58. package/dist/server/server/agent/providers/codex/tool-call-mapper.js.map +1 -1
  59. package/dist/server/server/agent/providers/codex-app-server-agent.d.ts +132 -4
  60. package/dist/server/server/agent/providers/codex-app-server-agent.d.ts.map +1 -1
  61. package/dist/server/server/agent/providers/codex-app-server-agent.js +2233 -163
  62. package/dist/server/server/agent/providers/codex-app-server-agent.js.map +1 -1
  63. package/dist/server/server/agent/providers/codex-binary-resolver.d.ts +9 -0
  64. package/dist/server/server/agent/providers/codex-binary-resolver.d.ts.map +1 -1
  65. package/dist/server/server/agent/providers/codex-binary-resolver.js +35 -14
  66. package/dist/server/server/agent/providers/codex-binary-resolver.js.map +1 -1
  67. package/dist/server/server/agent/providers/codex-health-probe.js +1 -1
  68. package/dist/server/server/agent/providers/codex-health-probe.js.map +1 -1
  69. package/dist/server/server/agent/providers/deepseek/constants.d.ts +4 -0
  70. package/dist/server/server/agent/providers/deepseek/constants.d.ts.map +1 -0
  71. package/dist/server/server/agent/providers/deepseek/constants.js +11 -0
  72. package/dist/server/server/agent/providers/deepseek/constants.js.map +1 -0
  73. package/dist/server/server/agent/providers/deepseek/event-mapper.d.ts +21 -0
  74. package/dist/server/server/agent/providers/deepseek/event-mapper.d.ts.map +1 -0
  75. package/dist/server/server/agent/providers/deepseek/event-mapper.js +286 -0
  76. package/dist/server/server/agent/providers/deepseek/event-mapper.js.map +1 -0
  77. package/dist/server/server/agent/providers/deepseek/serve-client.d.ts +94 -0
  78. package/dist/server/server/agent/providers/deepseek/serve-client.d.ts.map +1 -0
  79. package/dist/server/server/agent/providers/deepseek/serve-client.js +142 -0
  80. package/dist/server/server/agent/providers/deepseek/serve-client.js.map +1 -0
  81. package/dist/server/server/agent/providers/deepseek/serve-process.d.ts +18 -0
  82. package/dist/server/server/agent/providers/deepseek/serve-process.d.ts.map +1 -0
  83. package/dist/server/server/agent/providers/deepseek/serve-process.js +93 -0
  84. package/dist/server/server/agent/providers/deepseek/serve-process.js.map +1 -0
  85. package/dist/server/server/agent/providers/deepseek-agent.d.ts +94 -0
  86. package/dist/server/server/agent/providers/deepseek-agent.d.ts.map +1 -0
  87. package/dist/server/server/agent/providers/deepseek-agent.js +811 -0
  88. package/dist/server/server/agent/providers/deepseek-agent.js.map +1 -0
  89. package/dist/server/server/agent/providers/gateway-telemetry.d.ts +9 -0
  90. package/dist/server/server/agent/providers/gateway-telemetry.d.ts.map +1 -0
  91. package/dist/server/server/agent/providers/gateway-telemetry.js +36 -0
  92. package/dist/server/server/agent/providers/gateway-telemetry.js.map +1 -0
  93. package/dist/server/server/agent/providers/seaagent/constants.d.ts +3 -0
  94. package/dist/server/server/agent/providers/seaagent/constants.d.ts.map +1 -0
  95. package/dist/server/server/agent/providers/seaagent/constants.js +3 -0
  96. package/dist/server/server/agent/providers/seaagent/constants.js.map +1 -0
  97. package/dist/server/server/agent/providers/seaagent/event-mapper.d.ts +3 -0
  98. package/dist/server/server/agent/providers/seaagent/event-mapper.d.ts.map +1 -0
  99. package/dist/server/server/agent/providers/seaagent/event-mapper.js +69 -0
  100. package/dist/server/server/agent/providers/seaagent/event-mapper.js.map +1 -0
  101. package/dist/server/server/agent/providers/seaagent/rpc-client.d.ts +23 -0
  102. package/dist/server/server/agent/providers/seaagent/rpc-client.d.ts.map +1 -0
  103. package/dist/server/server/agent/providers/seaagent/rpc-client.js +139 -0
  104. package/dist/server/server/agent/providers/seaagent/rpc-client.js.map +1 -0
  105. package/dist/server/server/agent/providers/seaagent/tool-call-mapper.d.ts +3 -0
  106. package/dist/server/server/agent/providers/seaagent/tool-call-mapper.d.ts.map +1 -0
  107. package/dist/server/server/agent/providers/seaagent/tool-call-mapper.js +38 -0
  108. package/dist/server/server/agent/providers/seaagent/tool-call-mapper.js.map +1 -0
  109. package/dist/server/server/agent/providers/seaagent-agent.d.ts +81 -0
  110. package/dist/server/server/agent/providers/seaagent-agent.d.ts.map +1 -0
  111. package/dist/server/server/agent/providers/seaagent-agent.js +502 -0
  112. package/dist/server/server/agent/providers/seaagent-agent.js.map +1 -0
  113. package/dist/server/server/agent/providers/seaagent-binary-resolver.d.ts +18 -0
  114. package/dist/server/server/agent/providers/seaagent-binary-resolver.d.ts.map +1 -0
  115. package/dist/server/server/agent/providers/seaagent-binary-resolver.js +46 -0
  116. package/dist/server/server/agent/providers/seaagent-binary-resolver.js.map +1 -0
  117. package/dist/server/server/agent/providers/seaagent-health-probe.d.ts +11 -0
  118. package/dist/server/server/agent/providers/seaagent-health-probe.d.ts.map +1 -0
  119. package/dist/server/server/agent/providers/seaagent-health-probe.js +49 -0
  120. package/dist/server/server/agent/providers/seaagent-health-probe.js.map +1 -0
  121. package/dist/server/server/agent/providers/seawork-models.d.ts +8 -0
  122. package/dist/server/server/agent/providers/seawork-models.d.ts.map +1 -1
  123. package/dist/server/server/agent/providers/seawork-models.js +118 -74
  124. package/dist/server/server/agent/providers/seawork-models.js.map +1 -1
  125. package/dist/server/server/agent/providers/tool-call-detail-primitives.d.ts +2 -2
  126. package/dist/server/server/agent/timeline-projection.d.ts +5 -1
  127. package/dist/server/server/agent/timeline-projection.d.ts.map +1 -1
  128. package/dist/server/server/agent/timeline-projection.js +20 -4
  129. package/dist/server/server/agent/timeline-projection.js.map +1 -1
  130. package/dist/server/server/agent-attention-policy.d.ts +1 -0
  131. package/dist/server/server/agent-attention-policy.d.ts.map +1 -1
  132. package/dist/server/server/agent-attention-policy.js +6 -0
  133. package/dist/server/server/agent-attention-policy.js.map +1 -1
  134. package/dist/server/server/allowed-hosts.d.ts +13 -0
  135. package/dist/server/server/allowed-hosts.d.ts.map +1 -1
  136. package/dist/server/server/allowed-hosts.js +33 -0
  137. package/dist/server/server/allowed-hosts.js.map +1 -1
  138. package/dist/server/server/bootstrap.d.ts +2 -0
  139. package/dist/server/server/bootstrap.d.ts.map +1 -1
  140. package/dist/server/server/bootstrap.js +200 -14
  141. package/dist/server/server/bootstrap.js.map +1 -1
  142. package/dist/server/server/browser-extension-token.d.ts +23 -0
  143. package/dist/server/server/browser-extension-token.d.ts.map +1 -0
  144. package/dist/server/server/browser-extension-token.js +114 -0
  145. package/dist/server/server/browser-extension-token.js.map +1 -0
  146. package/dist/server/server/bug-report-handler.d.ts +7 -1
  147. package/dist/server/server/bug-report-handler.d.ts.map +1 -1
  148. package/dist/server/server/bug-report-handler.js +73 -5
  149. package/dist/server/server/bug-report-handler.js.map +1 -1
  150. package/dist/server/server/bug-report-redact.d.ts +25 -1
  151. package/dist/server/server/bug-report-redact.d.ts.map +1 -1
  152. package/dist/server/server/bug-report-redact.js +42 -5
  153. package/dist/server/server/bug-report-redact.js.map +1 -1
  154. package/dist/server/server/config.d.ts +1 -0
  155. package/dist/server/server/config.d.ts.map +1 -1
  156. package/dist/server/server/config.js +51 -1
  157. package/dist/server/server/config.js.map +1 -1
  158. package/dist/server/server/crash-report.d.ts.map +1 -1
  159. package/dist/server/server/crash-report.js +18 -0
  160. package/dist/server/server/crash-report.js.map +1 -1
  161. package/dist/server/server/daemon-config-store.d.ts.map +1 -1
  162. package/dist/server/server/daemon-config-store.js +94 -3
  163. package/dist/server/server/daemon-config-store.js.map +1 -1
  164. package/dist/server/server/disk-full.d.ts +4 -0
  165. package/dist/server/server/disk-full.d.ts.map +1 -0
  166. package/dist/server/server/disk-full.js +46 -0
  167. package/dist/server/server/disk-full.js.map +1 -0
  168. package/dist/server/server/exports.d.ts +3 -2
  169. package/dist/server/server/exports.d.ts.map +1 -1
  170. package/dist/server/server/exports.js +2 -1
  171. package/dist/server/server/exports.js.map +1 -1
  172. package/dist/server/server/git-forge/github-client.d.ts +18 -0
  173. package/dist/server/server/git-forge/github-client.d.ts.map +1 -1
  174. package/dist/server/server/git-forge/github-client.js +88 -0
  175. package/dist/server/server/git-forge/github-client.js.map +1 -1
  176. package/dist/server/server/git-forge/parse-remote.d.ts +2 -0
  177. package/dist/server/server/git-forge/parse-remote.d.ts.map +1 -1
  178. package/dist/server/server/git-forge/parse-remote.js +71 -6
  179. package/dist/server/server/git-forge/parse-remote.js.map +1 -1
  180. package/dist/server/server/git-forge/service.d.ts +87 -0
  181. package/dist/server/server/git-forge/service.d.ts.map +1 -1
  182. package/dist/server/server/git-forge/service.js +198 -4
  183. package/dist/server/server/git-forge/service.js.map +1 -1
  184. package/dist/server/server/index.js +72 -0
  185. package/dist/server/server/index.js.map +1 -1
  186. package/dist/server/server/integrations/wecom-openclaw/bridge.d.ts +88 -0
  187. package/dist/server/server/integrations/wecom-openclaw/bridge.d.ts.map +1 -0
  188. package/dist/server/server/integrations/wecom-openclaw/bridge.js +1229 -0
  189. package/dist/server/server/integrations/wecom-openclaw/bridge.js.map +1 -0
  190. package/dist/server/server/integrations/wecom-openclaw/qr.d.ts +38 -0
  191. package/dist/server/server/integrations/wecom-openclaw/qr.d.ts.map +1 -0
  192. package/dist/server/server/integrations/wecom-openclaw/qr.js +101 -0
  193. package/dist/server/server/integrations/wecom-openclaw/qr.js.map +1 -0
  194. package/dist/server/server/integrations/wecom-openclaw/workspace.d.ts +5 -0
  195. package/dist/server/server/integrations/wecom-openclaw/workspace.d.ts.map +1 -0
  196. package/dist/server/server/integrations/wecom-openclaw/workspace.js +40 -0
  197. package/dist/server/server/integrations/wecom-openclaw/workspace.js.map +1 -0
  198. package/dist/server/server/latency-proxy.d.ts.map +1 -1
  199. package/dist/server/server/latency-proxy.js +45 -5
  200. package/dist/server/server/latency-proxy.js.map +1 -1
  201. package/dist/server/server/library/codex-skill-discovery.d.ts +9 -0
  202. package/dist/server/server/library/codex-skill-discovery.d.ts.map +1 -0
  203. package/dist/server/server/library/codex-skill-discovery.js +49 -0
  204. package/dist/server/server/library/codex-skill-discovery.js.map +1 -0
  205. package/dist/server/server/library/hub-install.d.ts +79 -0
  206. package/dist/server/server/library/hub-install.d.ts.map +1 -0
  207. package/dist/server/server/library/hub-install.js +263 -0
  208. package/dist/server/server/library/hub-install.js.map +1 -0
  209. package/dist/server/server/library/hub-test-run.d.ts +81 -0
  210. package/dist/server/server/library/hub-test-run.d.ts.map +1 -0
  211. package/dist/server/server/library/hub-test-run.js +237 -0
  212. package/dist/server/server/library/hub-test-run.js.map +1 -0
  213. package/dist/server/server/library/library-import.d.ts +27 -0
  214. package/dist/server/server/library/library-import.d.ts.map +1 -0
  215. package/dist/server/server/library/library-import.js +227 -0
  216. package/dist/server/server/library/library-import.js.map +1 -0
  217. package/dist/server/server/library/library-injection.d.ts +16 -0
  218. package/dist/server/server/library/library-injection.d.ts.map +1 -0
  219. package/dist/server/server/library/library-injection.js +49 -0
  220. package/dist/server/server/library/library-injection.js.map +1 -0
  221. package/dist/server/server/library/library-rpc.d.ts +73 -0
  222. package/dist/server/server/library/library-rpc.d.ts.map +1 -0
  223. package/dist/server/server/library/library-rpc.js +239 -0
  224. package/dist/server/server/library/library-rpc.js.map +1 -0
  225. package/dist/server/server/library/library-store.d.ts +35 -0
  226. package/dist/server/server/library/library-store.d.ts.map +1 -0
  227. package/dist/server/server/library/library-store.js +169 -0
  228. package/dist/server/server/library/library-store.js.map +1 -0
  229. package/dist/server/server/library/library-sync.d.ts +46 -0
  230. package/dist/server/server/library/library-sync.d.ts.map +1 -0
  231. package/dist/server/server/library/library-sync.js +235 -0
  232. package/dist/server/server/library/library-sync.js.map +1 -0
  233. package/dist/server/server/library/library-types.d.ts +756 -0
  234. package/dist/server/server/library/library-types.d.ts.map +1 -0
  235. package/dist/server/server/library/library-types.js +99 -0
  236. package/dist/server/server/library/library-types.js.map +1 -0
  237. package/dist/server/server/library/worktree-dev.d.ts +14 -0
  238. package/dist/server/server/library/worktree-dev.d.ts.map +1 -0
  239. package/dist/server/server/library/worktree-dev.js +24 -0
  240. package/dist/server/server/library/worktree-dev.js.map +1 -0
  241. package/dist/server/server/log-stream-error.d.ts +2 -0
  242. package/dist/server/server/log-stream-error.d.ts.map +1 -0
  243. package/dist/server/server/log-stream-error.js +33 -0
  244. package/dist/server/server/log-stream-error.js.map +1 -0
  245. package/dist/server/server/logger.d.ts +1 -0
  246. package/dist/server/server/logger.d.ts.map +1 -1
  247. package/dist/server/server/logger.js +32 -0
  248. package/dist/server/server/logger.js.map +1 -1
  249. package/dist/server/server/loop/rpc-schemas.d.ts +96 -96
  250. package/dist/server/server/loop-service.d.ts +18 -18
  251. package/dist/server/server/messages.d.ts +4 -1
  252. package/dist/server/server/messages.d.ts.map +1 -1
  253. package/dist/server/server/messages.js +40 -2
  254. package/dist/server/server/messages.js.map +1 -1
  255. package/dist/server/server/node-pty-error.d.ts +2 -0
  256. package/dist/server/server/node-pty-error.d.ts.map +1 -0
  257. package/dist/server/server/node-pty-error.js +19 -0
  258. package/dist/server/server/node-pty-error.js.map +1 -0
  259. package/dist/server/server/persisted-config.d.ts +219 -135
  260. package/dist/server/server/persisted-config.d.ts.map +1 -1
  261. package/dist/server/server/persisted-config.js +35 -1
  262. package/dist/server/server/persisted-config.js.map +1 -1
  263. package/dist/server/server/port-in-use.d.ts +4 -0
  264. package/dist/server/server/port-in-use.d.ts.map +1 -0
  265. package/dist/server/server/port-in-use.js +35 -0
  266. package/dist/server/server/port-in-use.js.map +1 -0
  267. package/dist/server/server/provider-runtime-settings-mask.d.ts +7 -0
  268. package/dist/server/server/provider-runtime-settings-mask.d.ts.map +1 -0
  269. package/dist/server/server/provider-runtime-settings-mask.js +65 -0
  270. package/dist/server/server/provider-runtime-settings-mask.js.map +1 -0
  271. package/dist/server/server/sac/auth.d.ts +12 -0
  272. package/dist/server/server/sac/auth.d.ts.map +1 -1
  273. package/dist/server/server/sac/auth.js +19 -1
  274. package/dist/server/server/sac/auth.js.map +1 -1
  275. package/dist/server/server/sac/index.d.ts +2 -2
  276. package/dist/server/server/sac/index.d.ts.map +1 -1
  277. package/dist/server/server/sac/index.js +2 -2
  278. package/dist/server/server/sac/index.js.map +1 -1
  279. package/dist/server/server/sac/poll.d.ts +2 -0
  280. package/dist/server/server/sac/poll.d.ts.map +1 -1
  281. package/dist/server/server/sac/poll.js +7 -2
  282. package/dist/server/server/sac/poll.js.map +1 -1
  283. package/dist/server/server/schedule/cron.d.ts.map +1 -1
  284. package/dist/server/server/schedule/cron.js +6 -6
  285. package/dist/server/server/schedule/cron.js.map +1 -1
  286. package/dist/server/server/schedule/rpc-schemas.d.ts +895 -0
  287. package/dist/server/server/schedule/rpc-schemas.d.ts.map +1 -1
  288. package/dist/server/server/schedule/rpc-schemas.js +34 -0
  289. package/dist/server/server/schedule/rpc-schemas.js.map +1 -1
  290. package/dist/server/server/schedule/service.d.ts +5 -1
  291. package/dist/server/server/schedule/service.d.ts.map +1 -1
  292. package/dist/server/server/schedule/service.js +97 -14
  293. package/dist/server/server/schedule/service.js.map +1 -1
  294. package/dist/server/server/schedule/types.d.ts +19 -0
  295. package/dist/server/server/schedule/types.d.ts.map +1 -1
  296. package/dist/server/server/schedule/types.js +1 -0
  297. package/dist/server/server/schedule/types.js.map +1 -1
  298. package/dist/server/server/session.d.ts +83 -2
  299. package/dist/server/server/session.d.ts.map +1 -1
  300. package/dist/server/server/session.js +895 -82
  301. package/dist/server/server/session.js.map +1 -1
  302. package/dist/server/server/speech/native-runtime-guard.d.ts +1 -0
  303. package/dist/server/server/speech/native-runtime-guard.d.ts.map +1 -1
  304. package/dist/server/server/speech/native-runtime-guard.js +10 -4
  305. package/dist/server/server/speech/native-runtime-guard.js.map +1 -1
  306. package/dist/server/server/websocket-server.d.ts +6 -1
  307. package/dist/server/server/websocket-server.d.ts.map +1 -1
  308. package/dist/server/server/websocket-server.js +79 -7
  309. package/dist/server/server/websocket-server.js.map +1 -1
  310. package/dist/server/server/workspace-git-service.d.ts +2 -1
  311. package/dist/server/server/workspace-git-service.d.ts.map +1 -1
  312. package/dist/server/server/workspace-git-service.js +7 -3
  313. package/dist/server/server/workspace-git-service.js.map +1 -1
  314. package/dist/server/server/workspace-registry-model.d.ts +1 -0
  315. package/dist/server/server/workspace-registry-model.d.ts.map +1 -1
  316. package/dist/server/server/workspace-registry-model.js +18 -0
  317. package/dist/server/server/workspace-registry-model.js.map +1 -1
  318. package/dist/server/server/worktree-session.d.ts +3 -3
  319. package/dist/server/server/worktree-session.d.ts.map +1 -1
  320. package/dist/server/server/worktree-session.js +1 -3
  321. package/dist/server/server/worktree-session.js.map +1 -1
  322. package/dist/server/shared/messages.d.ts +59658 -21927
  323. package/dist/server/shared/messages.d.ts.map +1 -1
  324. package/dist/server/shared/messages.js +531 -3
  325. package/dist/server/shared/messages.js.map +1 -1
  326. package/dist/server/shared/provider-runtime-settings.d.ts +87 -0
  327. package/dist/server/shared/provider-runtime-settings.d.ts.map +1 -0
  328. package/dist/server/shared/provider-runtime-settings.js +33 -0
  329. package/dist/server/shared/provider-runtime-settings.js.map +1 -0
  330. package/dist/server/terminal/terminal.d.ts +9 -0
  331. package/dist/server/terminal/terminal.d.ts.map +1 -1
  332. package/dist/server/terminal/terminal.js +100 -3
  333. package/dist/server/terminal/terminal.js.map +1 -1
  334. package/dist/server/utils/checkout-git.d.ts +23 -1
  335. package/dist/server/utils/checkout-git.d.ts.map +1 -1
  336. package/dist/server/utils/checkout-git.js +182 -21
  337. package/dist/server/utils/checkout-git.js.map +1 -1
  338. package/dist/server/utils/directory-suggestions.d.ts.map +1 -1
  339. package/dist/server/utils/directory-suggestions.js +57 -9
  340. package/dist/server/utils/directory-suggestions.js.map +1 -1
  341. package/dist/src/server/bug-report-redact.js +42 -5
  342. package/dist/src/server/bug-report-redact.js.map +1 -1
  343. package/dist/src/server/crash-report.js +18 -0
  344. package/dist/src/server/crash-report.js.map +1 -1
  345. package/dist/src/server/speech/native-runtime-guard.js +177 -0
  346. package/dist/src/server/speech/native-runtime-guard.js.map +1 -0
  347. package/dist/src/server/speech/speech-types.js +8 -0
  348. package/dist/src/server/speech/speech-types.js.map +1 -0
  349. package/package.json +16 -4
@@ -0,0 +1,811 @@
1
+ import { applyProviderEnv, resolveProviderCommandPrefix, } from "../provider-launch-config.js";
2
+ import { selectCodexBinary, selectEffectiveCodexBinary, verifyCommandAvailable, } from "./codex-binary-resolver.js";
3
+ import { getSeaworkModels } from "./seawork-models.js";
4
+ import { DEEPSEEK_BINARY, DEEPSEEK_PROVIDER } from "./deepseek/constants.js";
5
+ import { mapHistoryItems, mapServeEvent, newAccumulator } from "./deepseek/event-mapper.js";
6
+ import { DeepseekServeClient } from "./deepseek/serve-client.js";
7
+ import { startDeepseekServe } from "./deepseek/serve-process.js";
8
+ import { buildSeaworkGatewayHeaders, formatDeepseekHttpHeaders } from "./gateway-telemetry.js";
9
+ export { DEEPSEEK_PROVIDER };
10
+ const AVAILABILITY_TTL_MS = 30000;
11
+ const DEEPSEEK_CAPABILITIES = {
12
+ supportsStreaming: true,
13
+ supportsSessionPersistence: true,
14
+ supportsDynamicModes: false,
15
+ supportsMcpServers: false,
16
+ supportsReasoningStream: true,
17
+ supportsToolInvocations: true,
18
+ };
19
+ // Modes mirror the codex auto/full-access split. CodeWhale's serve runtime
20
+ // gates tool execution on `auto_approve`: false => emit approval.required and
21
+ // block (Ask); true => run without prompting (Full Access).
22
+ const DEEPSEEK_MODES = [
23
+ {
24
+ id: "auto",
25
+ label: "Ask First",
26
+ description: "Edit files and run commands after you approve each action.",
27
+ },
28
+ {
29
+ id: "full-access",
30
+ label: "Full Access",
31
+ description: "Edit files and run commands without per-action approval.",
32
+ },
33
+ ];
34
+ const DEFAULT_DEEPSEEK_MODE = "auto";
35
+ function readEnvString(env, key) {
36
+ const value = env[key];
37
+ return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
38
+ }
39
+ /**
40
+ * Resolve the LLM gateway the CodeWhale serve subprocess should hit.
41
+ *
42
+ * serve runs in `DEEPSEEK_PROVIDER=openai` mode and reads `OPENAI_BASE_URL` /
43
+ * `OPENAI_API_KEY`, then appends `/v1/chat/completions` itself. The LLM gateway
44
+ * lives in `SEAWORK_LLM_BASE_URL` (auth.json base_url, e.g.
45
+ * `https://api.seawork.ai/llm`); the desktop sets `OPENAI_BASE_URL` to the
46
+ * *image* gateway, which has no chat-completions route. Reading OPENAI_BASE_URL
47
+ * verbatim is what produced `404 Cannot POST /v1/chat/completions`. So we take
48
+ * the gateway ONLY from SEAWORK_LLM_BASE_URL and pass it as the gateway root
49
+ * (do NOT append `/v1` — serve does that).
50
+ */
51
+ export function resolveDeepseekGatewayEnv(env) {
52
+ return {
53
+ baseURL: readEnvString(env, "SEAWORK_LLM_BASE_URL"),
54
+ apiKey: readEnvString(env, "SEAWORK_API_KEY") ?? readEnvString(env, "OPENAI_API_KEY"),
55
+ };
56
+ }
57
+ function modeToApproval(modeId) {
58
+ // Full access => auto-approve everything; otherwise require approvals.
59
+ const full = modeId === "full-access";
60
+ return { auto_approve: full, allow_shell: true };
61
+ }
62
+ function promptToText(prompt) {
63
+ if (typeof prompt === "string")
64
+ return prompt;
65
+ const parts = [];
66
+ for (const block of prompt) {
67
+ if (block.type === "text")
68
+ parts.push(block.text);
69
+ else if (block.type === "image")
70
+ throw new Error("deepseek provider does not support image inputs");
71
+ else
72
+ throw new Error(`deepseek provider does not support content block "${block.type}"`);
73
+ }
74
+ return parts.join("\n");
75
+ }
76
+ function hasInvalidPersistedChatMessages(items) {
77
+ return items.some((item) => {
78
+ if (item.kind !== "user_message" && item.kind !== "agent_message") {
79
+ return false;
80
+ }
81
+ const text = item.detail ?? item.summary ?? "";
82
+ return text.trim().length === 0;
83
+ });
84
+ }
85
+ function isActiveServeTurnStatus(status) {
86
+ if (!status)
87
+ return false;
88
+ const normalized = status.trim().toLowerCase();
89
+ return (normalized === "in_progress" ||
90
+ normalized === "in-progress" ||
91
+ normalized === "running" ||
92
+ normalized === "active" ||
93
+ normalized === "pending");
94
+ }
95
+ export class DeepseekAgentClient {
96
+ constructor(logger, runtimeSettings) {
97
+ this.logger = logger;
98
+ this.runtimeSettings = runtimeSettings;
99
+ this.provider = DEEPSEEK_PROVIDER;
100
+ this.capabilities = DEEPSEEK_CAPABILITIES;
101
+ this.availabilityCache = null;
102
+ }
103
+ async createSession(config, launchContext) {
104
+ return this.openSession(config, null, launchContext);
105
+ }
106
+ async resumeSession(handle, overrides, launchContext) {
107
+ const cwd = overrides?.cwd ??
108
+ (typeof handle.metadata?.cwd === "string" ? handle.metadata.cwd : process.cwd());
109
+ const config = {
110
+ provider: DEEPSEEK_PROVIDER,
111
+ cwd,
112
+ model: overrides?.model ?? handle.metadata?.model,
113
+ modeId: overrides?.modeId ?? handle.metadata?.modeId,
114
+ ...overrides,
115
+ };
116
+ return this.openSession(config, handle, launchContext);
117
+ }
118
+ async listModels(_options) {
119
+ return getSeaworkModels(DEEPSEEK_PROVIDER);
120
+ }
121
+ async listModes() {
122
+ return DEEPSEEK_MODES;
123
+ }
124
+ async isAvailable() {
125
+ const now = Date.now();
126
+ if (this.availabilityCache && now - this.availabilityCache.at < AVAILABILITY_TTL_MS) {
127
+ return this.availabilityCache.value;
128
+ }
129
+ let value = false;
130
+ try {
131
+ const selection = await selectEffectiveCodexBinary(this.runtimeSettings, {
132
+ spawnEnv: this.spawnEnv(),
133
+ binary: DEEPSEEK_BINARY,
134
+ });
135
+ value = await verifyCommandAvailable(selection.binary, { spawnEnv: this.spawnEnv() });
136
+ }
137
+ catch {
138
+ value = false;
139
+ }
140
+ this.availabilityCache = { value, at: now };
141
+ return value;
142
+ }
143
+ async getDiagnostic() {
144
+ try {
145
+ const spawnEnv = this.spawnEnv();
146
+ const selection = await selectEffectiveCodexBinary(this.runtimeSettings, {
147
+ spawnEnv,
148
+ binary: DEEPSEEK_BINARY,
149
+ });
150
+ // Report the LLM gateway the session actually spawns with — resolved the
151
+ // same way openSession() does (SEAWORK_LLM_BASE_URL only), so UI
152
+ // availability and the real serve spawn agree on the gateway.
153
+ const baseURL = resolveDeepseekGatewayEnv(spawnEnv).baseURL ?? "(unset)";
154
+ return {
155
+ diagnostic: [
156
+ `Binary: ${selection.binary} (${selection.source})`,
157
+ `Gateway: ${baseURL}`,
158
+ `Transport: codewhale serve --http`,
159
+ ].join("\n"),
160
+ };
161
+ }
162
+ catch (error) {
163
+ return {
164
+ diagnostic: `DeepSeek binary not found — ${error instanceof Error ? error.message : String(error)}`,
165
+ };
166
+ }
167
+ }
168
+ spawnEnv() {
169
+ return applyProviderEnv(process.env, this.runtimeSettings);
170
+ }
171
+ async openSession(config, resumeHandle, launchContext) {
172
+ // Resolve the launch command through the provider runtime command config
173
+ // (command.replace / command.append), exactly like isAvailable() and
174
+ // getDiagnostic() do — otherwise a user's custom wrapper would show as
175
+ // "available" in the UI but be ignored when actually spawning a session.
176
+ // The default command is the resolved codewhale/deepseek binary; any
177
+ // command.replace argv or command.append args ride along as prefix args.
178
+ const prefix = await resolveProviderCommandPrefix(this.runtimeSettings?.command, async () => {
179
+ const selection = await selectCodexBinary({
180
+ logger: this.logger,
181
+ spawnEnv: this.spawnEnv(),
182
+ binary: DEEPSEEK_BINARY,
183
+ });
184
+ return selection.binary;
185
+ });
186
+ // Route CodeWhale at the Seawork LLM gateway via its OpenAI-compatible
187
+ // provider. serve reads OPENAI_BASE_URL / OPENAI_API_KEY, so we OVERRIDE
188
+ // them with the LLM gateway from SEAWORK_LLM_BASE_URL (auth.json base_url):
189
+ // spawnEnv()'s OPENAI_BASE_URL is the desktop image gateway and would 404.
190
+ //
191
+ // Merge launchContext.env FIRST (into mergedEnv), then resolve the gateway
192
+ // from that merged view and apply the OPENAI_* override LAST — otherwise a
193
+ // launchContext-supplied OPENAI_BASE_URL (image gateway) would spread in
194
+ // after the override and re-introduce the 404, and a launchContext-supplied
195
+ // SEAWORK_LLM_BASE_URL would be missed. spawnEnv() already merges provider
196
+ // runtimeSettings.env over process.env (applyProviderEnv), so a per-provider
197
+ // override survives. Same env basis isAvailable()/getDiagnostic() use.
198
+ const mergedEnv = {
199
+ ...this.spawnEnv(),
200
+ ...(launchContext?.env ?? {}),
201
+ DEEPSEEK_PROVIDER: "openai",
202
+ ...(config.model ? { DEEPSEEK_MODEL: config.model } : {}),
203
+ DEEPSEEK_HTTP_HEADERS: formatDeepseekHttpHeaders(buildSeaworkGatewayHeaders(DEEPSEEK_PROVIDER)),
204
+ };
205
+ const gateway = resolveDeepseekGatewayEnv(mergedEnv);
206
+ const env = { ...mergedEnv };
207
+ // When SEAWORK_LLM_BASE_URL is missing there is NO LLM gateway. Don't leave
208
+ // the inherited OPENAI_BASE_URL (desktop's image gateway) in place — serve
209
+ // would silently POST to the wrong host and 404. Strip it so DeepSeek fails
210
+ // as "not logged in" instead. When present, OVERRIDE serve's OPENAI_* with
211
+ // the LLM gateway so the image gateway can never win.
212
+ if (gateway.baseURL) {
213
+ env.OPENAI_BASE_URL = gateway.baseURL;
214
+ }
215
+ else {
216
+ delete env.OPENAI_BASE_URL;
217
+ }
218
+ if (gateway.apiKey) {
219
+ env.OPENAI_API_KEY = gateway.apiKey;
220
+ }
221
+ const serve = await startDeepseekServe({
222
+ binary: prefix.command,
223
+ prefixArgs: prefix.args,
224
+ env,
225
+ logger: this.logger,
226
+ });
227
+ // If the handshake (createThread / SSE) fails, tear the serve subprocess
228
+ // down before propagating — otherwise the orphaned process leaks.
229
+ try {
230
+ const session = new DeepseekSession(config, resumeHandle, this.logger, serve);
231
+ await session.connect();
232
+ return session;
233
+ }
234
+ catch (err) {
235
+ serve.close();
236
+ throw err;
237
+ }
238
+ }
239
+ }
240
+ export class DeepseekSession {
241
+ constructor(config, resumeHandle, logger, serve) {
242
+ this.config = config;
243
+ this.resumeHandle = resumeHandle;
244
+ this.logger = logger;
245
+ this.serve = serve;
246
+ this.provider = DEEPSEEK_PROVIDER;
247
+ this.capabilities = DEEPSEEK_CAPABILITIES;
248
+ this.subscribers = new Set();
249
+ this.history = [];
250
+ // Timeline items rebuilt from the persisted thread on resume; streamHistory()
251
+ // replays these once so a restarted daemon shows past turns.
252
+ this.persistedHistory = [];
253
+ this.pendingPermissions = new Map();
254
+ // approvalId -> turnId, so terminal events can drop a turn's stale approvals.
255
+ this.pendingPermissionTurn = new Map();
256
+ this.threadId = null;
257
+ this.sseAbort = null;
258
+ this.eventStreamReady = null;
259
+ this.closed = false;
260
+ // SSE turn id from the most recent turn.started frame — the SAME id run()
261
+ // pins as ourTurnId — and the id of the last turn that reached a terminal
262
+ // event. If the stream dies after lastStartedTurnId but before that turn is
263
+ // terminated, we synthesize turn_failed with this exact id so run()'s turnId
264
+ // filter doesn't drop it.
265
+ this.lastStartedTurnId = null;
266
+ this.lastTerminatedTurnId = null;
267
+ // True from just before the POST /turns until that turn reaches a terminal
268
+ // event. Lets the SSE finally{} emit a last-resort turn_failed if the stream
269
+ // dies in the narrow window after connect but before turn.started AND before
270
+ // the POST response set activeTurnId — otherwise run() would hang forever.
271
+ this.turnStartInFlight = false;
272
+ // turnId -> ordered authoritative assistant text per completed item
273
+ // (itemId -> full text), populated from every completed agent_message item.
274
+ // run() joins these in insertion order for a correct finalText across
275
+ // multi-segment turns (tool rounds), independent of any lossy delta stream.
276
+ this.assistantTextByTurn = new Map();
277
+ this.activeTurnId = null;
278
+ this.recoveringConflict = false;
279
+ this.client = new DeepseekServeClient(serve.baseUrl, serve.token, logger);
280
+ this.model = config.model ?? null;
281
+ this.currentMode = config.modeId ?? DEFAULT_DEEPSEEK_MODE;
282
+ }
283
+ get id() {
284
+ return this.threadId;
285
+ }
286
+ async connect() {
287
+ const approval = modeToApproval(this.currentMode);
288
+ if (this.resumeHandle?.sessionId) {
289
+ const resumeThreadId = this.resumeHandle.sessionId;
290
+ let persistedItems = [];
291
+ let inspectedPersistedThread = false;
292
+ try {
293
+ const detail = await this.client.getThreadDetail(resumeThreadId);
294
+ persistedItems = detail.items ?? [];
295
+ inspectedPersistedThread = true;
296
+ }
297
+ catch (err) {
298
+ this.logger.warn({ err }, "deepseek: failed to inspect persisted thread before resume");
299
+ }
300
+ this.persistedHistory = mapHistoryItems(persistedItems);
301
+ if (hasInvalidPersistedChatMessages(persistedItems)) {
302
+ this.logger.warn({ threadId: resumeThreadId }, "deepseek: persisted thread contains empty chat messages; starting a fresh thread to avoid gateway 400s");
303
+ const thread = await this.client.createThread({
304
+ model: this.model ?? undefined,
305
+ workspace: this.config.cwd,
306
+ mode: "agent",
307
+ system_prompt: this.config.systemPrompt,
308
+ ...approval,
309
+ });
310
+ this.threadId = thread.id;
311
+ }
312
+ else {
313
+ this.threadId = resumeThreadId;
314
+ await this.client.resumeThread(this.threadId).catch((err) => {
315
+ this.logger.warn({ err }, "deepseek: resume thread failed; continuing");
316
+ });
317
+ if (!inspectedPersistedThread) {
318
+ await this.loadPersistedHistory();
319
+ }
320
+ }
321
+ }
322
+ else {
323
+ const thread = await this.client.createThread({
324
+ model: this.model ?? undefined,
325
+ workspace: this.config.cwd,
326
+ mode: "agent",
327
+ system_prompt: this.config.systemPrompt,
328
+ ...approval,
329
+ });
330
+ this.threadId = thread.id;
331
+ }
332
+ this.emit({ type: "thread_started", sessionId: this.threadId, provider: DEEPSEEK_PROVIDER });
333
+ }
334
+ async run(prompt, options) {
335
+ const finalTexts = [];
336
+ const timeline = [];
337
+ return new Promise((resolve, reject) => {
338
+ let ourTurnId = null;
339
+ let settled = false;
340
+ const finalize = (o) => {
341
+ if (settled)
342
+ return;
343
+ settled = true;
344
+ unsubscribe();
345
+ if (o.ok) {
346
+ // Prefer the authoritative per-item assistant text (joined across all
347
+ // completed agent_message segments of the turn) — correct even with
348
+ // tool rounds or a lossy delta in any one segment. Fall back to the
349
+ // concatenated streamed chunks if we somehow recorded none.
350
+ const byItem = ourTurnId ? this.assistantTextByTurn.get(ourTurnId) : undefined;
351
+ if (ourTurnId)
352
+ this.assistantTextByTurn.delete(ourTurnId);
353
+ const authoritative = byItem && byItem.size > 0 ? [...byItem.values()].join("") : undefined;
354
+ resolve({
355
+ sessionId: this.threadId ?? "",
356
+ finalText: authoritative ?? finalTexts.join(""),
357
+ timeline,
358
+ canceled: o.canceled,
359
+ });
360
+ }
361
+ else
362
+ reject(o.error);
363
+ };
364
+ const unsubscribe = this.subscribe((event) => {
365
+ if (settled)
366
+ return;
367
+ const evTurn = event.turnId;
368
+ if (event.type === "turn_started" && ourTurnId === null) {
369
+ ourTurnId = evTurn ?? null;
370
+ return;
371
+ }
372
+ if (ourTurnId !== null && evTurn && evTurn !== ourTurnId)
373
+ return;
374
+ if (event.type === "timeline") {
375
+ timeline.push(event.item);
376
+ if (event.item.type === "assistant_message")
377
+ finalTexts.push(event.item.text);
378
+ return;
379
+ }
380
+ if (event.type === "turn_completed")
381
+ finalize({ ok: true, canceled: false });
382
+ else if (event.type === "turn_canceled")
383
+ finalize({ ok: true, canceled: true });
384
+ else if (event.type === "turn_failed")
385
+ finalize({ ok: false, error: new Error(event.error || "deepseek turn failed") });
386
+ });
387
+ this.startTurn(prompt, options).catch((err) => finalize({ ok: false, error: err instanceof Error ? err : new Error(String(err)) }));
388
+ });
389
+ }
390
+ async startTurn(prompt, _options) {
391
+ if (!this.threadId)
392
+ throw new Error("deepseek session is not connected");
393
+ const text = promptToText(prompt);
394
+ const approval = modeToApproval(this.currentMode);
395
+ // Reset the per-turn SSE-start marker BEFORE anything for this turn can
396
+ // arrive on the stream. CodeWhale can emit turn.started within milliseconds
397
+ // — possibly before the POST /turns response returns — and the detached SSE
398
+ // consumer sets lastStartedTurnId from that frame. If we cleared it AFTER
399
+ // the POST, we'd wipe the real SSE turn id the consumer just recorded, so a
400
+ // later mid-turn stream death would fall back to the HTTP turn id (which can
401
+ // differ from the SSE id run() pinned) and run()'s turnId filter would drop
402
+ // the synthetic turn_failed, hanging the call. Clear first; the consumer
403
+ // then repopulates it from this turn's turn.started.
404
+ this.lastStartedTurnId = null;
405
+ // Await the SSE subscription BEFORE starting the turn. CodeWhale can emit
406
+ // turn.started/item.delta/turn.completed within milliseconds for a short
407
+ // reply; if the /events connection weren't established first, run() could
408
+ // miss turn_completed and hang. ensureEventStream resolves only after the
409
+ // HTTP connection is open.
410
+ await this.ensureEventStream();
411
+ // Mark a turn-start as in flight BEFORE the POST. If the SSE stream dies in
412
+ // the window between here and the POST returning — with no turn.started and
413
+ // no activeTurnId yet — the stream finally{} uses this flag to emit a
414
+ // last-resort turn_failed so a waiting run() settles instead of hanging.
415
+ this.turnStartInFlight = true;
416
+ let res;
417
+ try {
418
+ res = await this.client.startTurn(this.threadId, {
419
+ prompt: text,
420
+ model: this.model ?? undefined,
421
+ mode: "agent",
422
+ ...approval,
423
+ });
424
+ }
425
+ catch (err) {
426
+ const recovered = await this.tryRecoverActiveTurnConflict(err);
427
+ if (recovered) {
428
+ try {
429
+ // Recovery reopened the SSE stream and cleared the old turn state.
430
+ // Re-arm the start window before the retry POST so a new stream EOF
431
+ // still triggers the keyless turn_failed protection until this turn
432
+ // either emits turn.started or the POST returns an HTTP turn id.
433
+ this.turnStartInFlight = true;
434
+ res = await this.client.startTurn(this.threadId, {
435
+ prompt: text,
436
+ model: this.model ?? undefined,
437
+ mode: "agent",
438
+ ...approval,
439
+ });
440
+ }
441
+ catch (retryErr) {
442
+ this.turnStartInFlight = false;
443
+ throw retryErr;
444
+ }
445
+ }
446
+ else {
447
+ // The POST itself failed. run() settles via its own startTurn().catch(),
448
+ // so clear the in-flight flag here — otherwise a later unrelated stream
449
+ // EOF would trip the keyless synthetic-failure branch and emit a spurious
450
+ // turn_failed for a turn that never started.
451
+ this.turnStartInFlight = false;
452
+ throw err;
453
+ }
454
+ }
455
+ const turnId = res.turn?.id ?? `turn-${Date.now()}`;
456
+ this.activeTurnId = turnId;
457
+ return { turnId };
458
+ }
459
+ async tryRecoverActiveTurnConflict(error) {
460
+ if (!this.threadId)
461
+ return false;
462
+ if (this.recoveringConflict)
463
+ return false;
464
+ const message = error instanceof Error ? error.message : String(error);
465
+ if (!/\/turns -> 409\b/.test(message) || !/active turn/i.test(message)) {
466
+ return false;
467
+ }
468
+ this.recoveringConflict = true;
469
+ try {
470
+ const conflictTurnId = await this.resolveConflictingTurnId();
471
+ if (!conflictTurnId)
472
+ return false;
473
+ this.logger.warn({ threadId: this.threadId, conflictTurnId }, "deepseek: canceling conflicting active turn before retrying");
474
+ await this.client.cancelTurn(this.threadId, conflictTurnId);
475
+ this.lastStartedTurnId = null;
476
+ this.lastTerminatedTurnId = conflictTurnId;
477
+ this.clearPendingPermissionsForTurn(conflictTurnId);
478
+ this.turnStartInFlight = false;
479
+ this.sseAbort?.abort();
480
+ this.eventStreamReady = null;
481
+ await this.ensureEventStream();
482
+ return true;
483
+ }
484
+ catch (recoverErr) {
485
+ this.logger.warn({ err: recoverErr }, "deepseek: failed to recover from active turn conflict");
486
+ return false;
487
+ }
488
+ finally {
489
+ this.recoveringConflict = false;
490
+ }
491
+ }
492
+ async resolveConflictingTurnId() {
493
+ try {
494
+ const detail = await this.client.getThreadDetail(this.threadId);
495
+ const detailTurn = detail.turn;
496
+ return (detail.active_turn_id ??
497
+ detail.activeTurnId ??
498
+ detail.active_turn?.id ??
499
+ detail.activeTurn?.id ??
500
+ detail.current_turn?.id ??
501
+ detail.currentTurn?.id ??
502
+ (isActiveServeTurnStatus(detailTurn?.status) ? (detailTurn?.id ?? null) : null) ??
503
+ detail.turns?.find((turn) => isActiveServeTurnStatus(turn?.status))?.id ??
504
+ this.getRecoverableLocalActiveTurnId());
505
+ }
506
+ catch (error) {
507
+ const fallbackTurnId = this.getRecoverableLocalActiveTurnId();
508
+ if (fallbackTurnId) {
509
+ this.logger.warn({ err: error, threadId: this.threadId, conflictTurnId: fallbackTurnId }, "deepseek: getThreadDetail failed during conflict recovery; falling back to local activeTurnId");
510
+ return fallbackTurnId;
511
+ }
512
+ throw error;
513
+ }
514
+ }
515
+ getRecoverableLocalActiveTurnId() {
516
+ if (!this.activeTurnId)
517
+ return null;
518
+ if (this.activeTurnId === this.lastTerminatedTurnId)
519
+ return null;
520
+ return this.activeTurnId;
521
+ }
522
+ // Establish the per-thread SSE subscription and resolve only after the
523
+ // connection is open. Idempotent while a healthy stream is live: concurrent
524
+ // and later callers await the same promise. RECOVERABLE: if the connection
525
+ // fails to open, or the stream later ends/errors, `eventStreamReady` is
526
+ // cleared so the next startTurn reopens `/events` instead of reusing a stale
527
+ // (rejected or dead) promise — which would otherwise hang the next turn.
528
+ ensureEventStream() {
529
+ if (this.eventStreamReady)
530
+ return this.eventStreamReady;
531
+ if (this.closed)
532
+ return Promise.reject(new Error("deepseek session is closed"));
533
+ const abort = new AbortController();
534
+ this.sseAbort = abort;
535
+ const acc = newAccumulator();
536
+ const ready = (async () => {
537
+ const { frames } = await this.client.openEventStream(this.threadId, abort.signal);
538
+ // Connection is open here; consume frames detached so the caller can
539
+ // proceed to POST /turns without blocking on the (endless) stream.
540
+ (async () => {
541
+ try {
542
+ for await (const frame of frames) {
543
+ const { events, pendingApproval, divergentFinalText, completedAssistantItem } = mapServeEvent(frame, acc);
544
+ // Track the SSE turn id (what run() pins) so a mid-turn stream death
545
+ // can synthesize turn_failed with the matching id.
546
+ if (frame.event === "turn.started" && frame.data?.turn_id) {
547
+ this.lastStartedTurnId = frame.data.turn_id;
548
+ }
549
+ if (pendingApproval) {
550
+ this.pendingPermissions.set(pendingApproval.id, pendingApproval);
551
+ const apprTurn = frame.data?.turn_id ?? this.activeTurnId;
552
+ if (apprTurn)
553
+ this.pendingPermissionTurn.set(pendingApproval.id, apprTurn);
554
+ }
555
+ if (divergentFinalText) {
556
+ this.logger.warn("deepseek: completed item text diverged from streamed deltas; live timeline keeps the streamed text");
557
+ }
558
+ // Record the authoritative per-item assistant text so run() can
559
+ // rebuild finalText from ALL segments of the turn. Key by the SSE
560
+ // frame's turn id — the SAME id run() pins from turn.started — so
561
+ // write/read keys can't drift from activeTurnId (the HTTP response).
562
+ if (completedAssistantItem) {
563
+ const frameTurnId = frame.data?.turn_id ?? this.activeTurnId;
564
+ if (frameTurnId) {
565
+ let byItem = this.assistantTextByTurn.get(frameTurnId);
566
+ if (!byItem) {
567
+ byItem = new Map();
568
+ this.assistantTextByTurn.set(frameTurnId, byItem);
569
+ }
570
+ byItem.set(completedAssistantItem.itemId, completedAssistantItem.text);
571
+ }
572
+ }
573
+ for (const ev of events)
574
+ this.emit(ev);
575
+ // Terminal-event cleanup. The accumulator and per-turn maps only
576
+ // matter WITHIN a turn (delta coalescing + finalText rebuild), so
577
+ // reset them when the turn ends. This bounds memory on a long-lived
578
+ // session and prevents a non-unique server item id from a later
579
+ // turn colliding with a stale entry from an earlier one.
580
+ if (frame.event === "turn.completed" ||
581
+ frame.event === "turn.failed" ||
582
+ frame.event === "turn.canceled") {
583
+ acc.items.clear();
584
+ const tid = frame.data?.turn_id;
585
+ // Record that this turn reached a terminal event, so the stream's
586
+ // finally{} doesn't synthesize a duplicate turn_failed for it.
587
+ this.lastTerminatedTurnId = tid ?? this.activeTurnId;
588
+ this.activeTurnId = null;
589
+ this.turnStartInFlight = false;
590
+ if (tid) {
591
+ // emit() ran run()'s finalize synchronously above, so it has
592
+ // already read+deleted this entry on the normal path; deleting
593
+ // again reclaims it if no run() was waiting (e.g. a turn that
594
+ // failed before turn_started was observed).
595
+ this.assistantTextByTurn.delete(tid);
596
+ // Drop any approvals still pending for this turn — the server
597
+ // won't act on them once the turn is terminal, and the UI
598
+ // shouldn't keep showing a stale prompt.
599
+ this.clearPendingPermissionsForTurn(tid);
600
+ }
601
+ }
602
+ }
603
+ }
604
+ catch (err) {
605
+ if (!abort.signal.aborted) {
606
+ this.logger.warn({ err }, "deepseek: event stream ended unexpectedly");
607
+ }
608
+ }
609
+ finally {
610
+ // If the stream died (EOF/error, not a deliberate abort) while a turn
611
+ // was in flight, synthesize turn_failed so a waiting run() settles and
612
+ // the UI leaves the running state instead of hanging forever. Use the
613
+ // SSE turn id run() pinned (lastStartedTurnId) so its turnId filter
614
+ // accepts the event; fall back to activeTurnId when turn.started was
615
+ // never observed (then run()'s ourTurnId is still null and accepts any
616
+ // turn_failed).
617
+ const inFlightTurnId = this.lastStartedTurnId ?? this.activeTurnId;
618
+ if (!abort.signal.aborted &&
619
+ inFlightTurnId &&
620
+ inFlightTurnId !== this.lastTerminatedTurnId) {
621
+ this.emit({
622
+ type: "turn_failed",
623
+ provider: DEEPSEEK_PROVIDER,
624
+ error: "deepseek event stream ended before the turn completed",
625
+ turnId: inFlightTurnId,
626
+ });
627
+ this.activeTurnId = null;
628
+ this.lastTerminatedTurnId = inFlightTurnId;
629
+ this.turnStartInFlight = false;
630
+ acc.items.clear();
631
+ this.assistantTextByTurn.delete(inFlightTurnId);
632
+ this.clearPendingPermissionsForTurn(inFlightTurnId);
633
+ }
634
+ else if (!abort.signal.aborted && this.turnStartInFlight) {
635
+ // Edge race: the stream died after connect but BEFORE turn.started
636
+ // and before the POST set activeTurnId, so we have no turn id to key
637
+ // on. Emit a turnId-less turn_failed — run() accepts it while its
638
+ // ourTurnId is still null — so a turn whose start is in flight still
639
+ // settles instead of hanging once the POST later resolves.
640
+ this.turnStartInFlight = false;
641
+ this.emit({
642
+ type: "turn_failed",
643
+ provider: DEEPSEEK_PROVIDER,
644
+ error: "deepseek event stream ended before the turn started",
645
+ });
646
+ }
647
+ // The reader is exhausted — allow a future turn to reopen the stream
648
+ // (unless we resumed/closed). Only clear if this is still the active
649
+ // stream (guard against a newer ensureEventStream having replaced it).
650
+ if (this.sseAbort === abort && !this.closed) {
651
+ this.eventStreamReady = null;
652
+ }
653
+ }
654
+ })();
655
+ })();
656
+ // On connect failure, clear so the next call retries rather than reusing a
657
+ // permanently-rejected promise.
658
+ this.eventStreamReady = ready.catch((err) => {
659
+ if (this.sseAbort === abort)
660
+ this.eventStreamReady = null;
661
+ throw err;
662
+ });
663
+ return this.eventStreamReady;
664
+ }
665
+ subscribe(callback) {
666
+ this.subscribers.add(callback);
667
+ return () => this.subscribers.delete(callback);
668
+ }
669
+ async *streamHistory() {
670
+ // Replay persisted turns first (oldest → newest), then live events from
671
+ // this process. Drained so a second call doesn't double up.
672
+ if (this.persistedHistory.length > 0) {
673
+ const items = this.persistedHistory;
674
+ this.persistedHistory = [];
675
+ for (const item of items) {
676
+ yield { type: "timeline", provider: DEEPSEEK_PROVIDER, item };
677
+ }
678
+ }
679
+ for (const event of this.history)
680
+ yield event;
681
+ }
682
+ // Fetch the persisted thread items and rebuild them as timeline items so the
683
+ // UI shows prior turns after a resume. Best-effort: a failure here must not
684
+ // block the session from connecting.
685
+ async loadPersistedHistory() {
686
+ if (!this.threadId)
687
+ return;
688
+ try {
689
+ const detail = await this.client.getThreadDetail(this.threadId);
690
+ this.persistedHistory = mapHistoryItems(detail.items ?? []);
691
+ }
692
+ catch (err) {
693
+ this.logger.warn({ err }, "deepseek: failed to load persisted history");
694
+ }
695
+ }
696
+ async getRuntimeInfo() {
697
+ return { provider: DEEPSEEK_PROVIDER, sessionId: this.threadId, model: this.model };
698
+ }
699
+ async getAvailableModes() {
700
+ return DEEPSEEK_MODES;
701
+ }
702
+ async getCurrentMode() {
703
+ return this.currentMode;
704
+ }
705
+ async setMode(modeId) {
706
+ this.currentMode = modeId;
707
+ }
708
+ getPendingPermissions() {
709
+ return [...this.pendingPermissions.values()];
710
+ }
711
+ async respondToPermission(requestId, response) {
712
+ const pending = this.pendingPermissions.get(requestId);
713
+ if (!pending)
714
+ return;
715
+ this.pendingPermissions.delete(requestId);
716
+ this.pendingPermissionTurn.delete(requestId);
717
+ await this.client.decideApproval(requestId, response.behavior === "allow" ? "allow" : "deny");
718
+ this.emit({
719
+ type: "permission_resolved",
720
+ provider: DEEPSEEK_PROVIDER,
721
+ requestId,
722
+ resolution: response,
723
+ turnId: this.activeTurnId ?? undefined,
724
+ });
725
+ }
726
+ // Drop approvals still pending for a turn that just ended (completed/failed/
727
+ // canceled). The server won't act on them anymore, so emit a deny resolution
728
+ // for each so the UI stops showing the stale prompt.
729
+ clearPendingPermissionsForTurn(turnId) {
730
+ for (const [approvalId, ownerTurn] of [...this.pendingPermissionTurn]) {
731
+ if (ownerTurn !== turnId)
732
+ continue;
733
+ this.pendingPermissionTurn.delete(approvalId);
734
+ if (this.pendingPermissions.delete(approvalId)) {
735
+ this.emit({
736
+ type: "permission_resolved",
737
+ provider: DEEPSEEK_PROVIDER,
738
+ requestId: approvalId,
739
+ resolution: { behavior: "deny", message: "turn ended before approval" },
740
+ turnId,
741
+ });
742
+ }
743
+ }
744
+ }
745
+ describePersistence() {
746
+ if (!this.threadId)
747
+ return null;
748
+ return {
749
+ provider: DEEPSEEK_PROVIDER,
750
+ sessionId: this.threadId,
751
+ nativeHandle: this.threadId,
752
+ metadata: {
753
+ cwd: this.config.cwd,
754
+ modeId: this.currentMode,
755
+ ...(this.model ? { model: this.model } : {}),
756
+ },
757
+ };
758
+ }
759
+ async interrupt() {
760
+ // The server-side cancel addresses the turn by its HTTP id...
761
+ if (this.threadId && this.activeTurnId) {
762
+ await this.client.cancelTurn(this.threadId, this.activeTurnId, { suppressErrors: true });
763
+ }
764
+ // ...but the locally-emitted terminal event must use the id run() pinned
765
+ // from turn.started (lastStartedTurnId), since run()'s turnId filter drops
766
+ // events whose turnId != ourTurnId. The SSE turn id can differ from the
767
+ // HTTP startTurn() id; using activeTurnId here would let an interrupt fail
768
+ // to settle a waiting run(), leaving the UI stuck if the server's terminal
769
+ // SSE frame is then lost/delayed. Fall back to activeTurnId only when
770
+ // turn.started hasn't been seen (then ourTurnId is null and accepts any id).
771
+ const runTurnId = this.lastStartedTurnId ?? this.activeTurnId;
772
+ // Mark the turn terminated so a subsequent mid-turn stream death does NOT
773
+ // synthesize a duplicate turn_failed for it.
774
+ this.lastTerminatedTurnId = runTurnId;
775
+ this.activeTurnId = null;
776
+ this.turnStartInFlight = false;
777
+ this.emit({
778
+ type: "turn_canceled",
779
+ provider: DEEPSEEK_PROVIDER,
780
+ reason: "interrupted",
781
+ turnId: runTurnId ?? undefined,
782
+ });
783
+ }
784
+ async close() {
785
+ this.closed = true;
786
+ this.sseAbort?.abort();
787
+ this.eventStreamReady = null;
788
+ this.assistantTextByTurn.clear();
789
+ this.pendingPermissions.clear();
790
+ this.pendingPermissionTurn.clear();
791
+ this.serve.close();
792
+ }
793
+ getRecentStderrTail() {
794
+ return this.serve.stderrTail() || undefined;
795
+ }
796
+ async setModel(modelId) {
797
+ this.model = modelId;
798
+ }
799
+ emit(event) {
800
+ this.history.push(event);
801
+ for (const subscriber of this.subscribers) {
802
+ try {
803
+ subscriber(event);
804
+ }
805
+ catch (error) {
806
+ this.logger.warn({ error }, "deepseek subscriber threw");
807
+ }
808
+ }
809
+ }
810
+ }
811
+ //# sourceMappingURL=deepseek-agent.js.map