@xyit/wellspring 0.0.0-stage → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (459) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +829 -2
  3. package/dist/agent/pending-asks.d.ts +20 -0
  4. package/dist/agent/pending-asks.js +70 -0
  5. package/dist/agent/pending-asks.js.map +1 -0
  6. package/dist/agent/task-registry.d.ts +14 -0
  7. package/dist/agent/task-registry.js +80 -0
  8. package/dist/agent/task-registry.js.map +1 -0
  9. package/dist/agent/tool-context.d.ts +30 -0
  10. package/dist/agent/tool-context.js +10 -0
  11. package/dist/agent/tool-context.js.map +1 -0
  12. package/dist/agent/usage.d.ts +40 -0
  13. package/dist/agent/usage.js +122 -0
  14. package/dist/agent/usage.js.map +1 -0
  15. package/dist/api/host/agent.d.ts +165 -0
  16. package/dist/api/host/agent.js +5 -0
  17. package/dist/api/host/agent.js.map +1 -0
  18. package/dist/api/host/events.d.ts +2 -0
  19. package/dist/api/host/events.js +6 -0
  20. package/dist/api/host/events.js.map +1 -0
  21. package/dist/api/host/exec.d.ts +36 -0
  22. package/dist/api/host/exec.js +10 -0
  23. package/dist/api/host/exec.js.map +1 -0
  24. package/dist/api/host/fs.d.ts +24 -0
  25. package/dist/api/host/fs.js +7 -0
  26. package/dist/api/host/fs.js.map +1 -0
  27. package/dist/api/host/hooks.d.ts +210 -0
  28. package/dist/api/host/hooks.js +401 -0
  29. package/dist/api/host/hooks.js.map +1 -0
  30. package/dist/api/host/host.d.ts +36 -0
  31. package/dist/api/host/host.js +53 -0
  32. package/dist/api/host/host.js.map +1 -0
  33. package/dist/api/host/http.d.ts +58 -0
  34. package/dist/api/host/http.js +6 -0
  35. package/dist/api/host/http.js.map +1 -0
  36. package/dist/api/host/index.d.ts +15 -0
  37. package/dist/api/host/index.js +20 -0
  38. package/dist/api/host/index.js.map +1 -0
  39. package/dist/api/host/models.d.ts +65 -0
  40. package/dist/api/host/models.js +8 -0
  41. package/dist/api/host/models.js.map +1 -0
  42. package/dist/api/host/network.d.ts +22 -0
  43. package/dist/api/host/network.js +7 -0
  44. package/dist/api/host/network.js.map +1 -0
  45. package/dist/api/host/plugin.d.ts +138 -0
  46. package/dist/api/host/plugin.js +6 -0
  47. package/dist/api/host/plugin.js.map +1 -0
  48. package/dist/api/host/plugins.d.ts +45 -0
  49. package/dist/api/host/plugins.js +6 -0
  50. package/dist/api/host/plugins.js.map +1 -0
  51. package/dist/api/host/pty.d.ts +55 -0
  52. package/dist/api/host/pty.js +21 -0
  53. package/dist/api/host/pty.js.map +1 -0
  54. package/dist/api/host/session.d.ts +5 -0
  55. package/dist/api/host/session.js +52 -0
  56. package/dist/api/host/session.js.map +1 -0
  57. package/dist/api/host/version.d.ts +2 -0
  58. package/dist/api/host/version.js +3 -0
  59. package/dist/api/host/version.js.map +1 -0
  60. package/dist/api/host/workspace.d.ts +38 -0
  61. package/dist/api/host/workspace.js +9 -0
  62. package/dist/api/host/workspace.js.map +1 -0
  63. package/dist/api/plugin/http-client.d.ts +42 -0
  64. package/dist/api/plugin/http-client.js +364 -0
  65. package/dist/api/plugin/http-client.js.map +1 -0
  66. package/dist/api/plugin/http-protocol.d.ts +152 -0
  67. package/dist/api/plugin/http-protocol.js +49 -0
  68. package/dist/api/plugin/http-protocol.js.map +1 -0
  69. package/dist/api/result/result.d.ts +16 -0
  70. package/dist/api/result/result.js +43 -0
  71. package/dist/api/result/result.js.map +1 -0
  72. package/dist/commands/agent-turn.d.ts +63 -0
  73. package/dist/commands/agent-turn.js +687 -0
  74. package/dist/commands/agent-turn.js.map +1 -0
  75. package/dist/commands/ask-prompt.d.ts +6 -0
  76. package/dist/commands/ask-prompt.js +49 -0
  77. package/dist/commands/ask-prompt.js.map +1 -0
  78. package/dist/commands/config.d.ts +2 -0
  79. package/dist/commands/config.js +283 -0
  80. package/dist/commands/config.js.map +1 -0
  81. package/dist/commands/conversation.d.ts +51 -0
  82. package/dist/commands/conversation.js +630 -0
  83. package/dist/commands/conversation.js.map +1 -0
  84. package/dist/commands/doctor.d.ts +2 -0
  85. package/dist/commands/doctor.js +77 -0
  86. package/dist/commands/doctor.js.map +1 -0
  87. package/dist/commands/exec-approval.d.ts +3 -0
  88. package/dist/commands/exec-approval.js +80 -0
  89. package/dist/commands/exec-approval.js.map +1 -0
  90. package/dist/commands/help.d.ts +2 -0
  91. package/dist/commands/help.js +35 -0
  92. package/dist/commands/help.js.map +1 -0
  93. package/dist/commands/key.d.ts +2 -0
  94. package/dist/commands/key.js +226 -0
  95. package/dist/commands/key.js.map +1 -0
  96. package/dist/commands/plugin.d.ts +2 -0
  97. package/dist/commands/plugin.js +188 -0
  98. package/dist/commands/plugin.js.map +1 -0
  99. package/dist/commands/session.d.ts +2 -0
  100. package/dist/commands/session.js +167 -0
  101. package/dist/commands/session.js.map +1 -0
  102. package/dist/commands/shared.d.ts +35 -0
  103. package/dist/commands/shared.js +66 -0
  104. package/dist/commands/shared.js.map +1 -0
  105. package/dist/commands/start.d.ts +2 -0
  106. package/dist/commands/start.js +75 -0
  107. package/dist/commands/start.js.map +1 -0
  108. package/dist/commands/stop.d.ts +2 -0
  109. package/dist/commands/stop.js +24 -0
  110. package/dist/commands/stop.js.map +1 -0
  111. package/dist/commands/version.d.ts +2 -0
  112. package/dist/commands/version.js +33 -0
  113. package/dist/commands/version.js.map +1 -0
  114. package/dist/config/codes.d.ts +13 -0
  115. package/dist/config/codes.js +15 -0
  116. package/dist/config/codes.js.map +1 -0
  117. package/dist/config/credentials.d.ts +67 -0
  118. package/dist/config/credentials.js +214 -0
  119. package/dist/config/credentials.js.map +1 -0
  120. package/dist/config/defaults.d.ts +5 -0
  121. package/dist/config/defaults.js +31 -0
  122. package/dist/config/defaults.js.map +1 -0
  123. package/dist/config/index.d.ts +5 -0
  124. package/dist/config/index.js +10 -0
  125. package/dist/config/index.js.map +1 -0
  126. package/dist/config/loader.d.ts +10 -0
  127. package/dist/config/loader.js +68 -0
  128. package/dist/config/loader.js.map +1 -0
  129. package/dist/config/providers.d.ts +30 -0
  130. package/dist/config/providers.js +97 -0
  131. package/dist/config/providers.js.map +1 -0
  132. package/dist/config/types.d.ts +75 -0
  133. package/dist/config/types.js +4 -0
  134. package/dist/config/types.js.map +1 -0
  135. package/dist/config/validate.d.ts +4 -0
  136. package/dist/config/validate.js +391 -0
  137. package/dist/config/validate.js.map +1 -0
  138. package/dist/display/markdown.d.ts +7 -0
  139. package/dist/display/markdown.js +116 -0
  140. package/dist/display/markdown.js.map +1 -0
  141. package/dist/display/tool-label.d.ts +6 -0
  142. package/dist/display/tool-label.js +81 -0
  143. package/dist/display/tool-label.js.map +1 -0
  144. package/dist/event/event.d.ts +27 -0
  145. package/dist/event/event.js +152 -0
  146. package/dist/event/event.js.map +1 -0
  147. package/dist/index.d.ts +2 -0
  148. package/dist/index.js +308 -0
  149. package/dist/index.js.map +1 -0
  150. package/dist/input/codes.d.ts +15 -0
  151. package/dist/input/codes.js +20 -0
  152. package/dist/input/codes.js.map +1 -0
  153. package/dist/input/commands.d.ts +10 -0
  154. package/dist/input/commands.js +65 -0
  155. package/dist/input/commands.js.map +1 -0
  156. package/dist/input/help.d.ts +8 -0
  157. package/dist/input/help.js +51 -0
  158. package/dist/input/help.js.map +1 -0
  159. package/dist/input/index.d.ts +6 -0
  160. package/dist/input/index.js +10 -0
  161. package/dist/input/index.js.map +1 -0
  162. package/dist/input/parse-options.d.ts +11 -0
  163. package/dist/input/parse-options.js +51 -0
  164. package/dist/input/parse-options.js.map +1 -0
  165. package/dist/input/parse.d.ts +3 -0
  166. package/dist/input/parse.js +119 -0
  167. package/dist/input/parse.js.map +1 -0
  168. package/dist/input/types.d.ts +17 -0
  169. package/dist/input/types.js +2 -0
  170. package/dist/input/types.js.map +1 -0
  171. package/dist/logger/elog4js.d.ts +65 -0
  172. package/dist/logger/elog4js.js +305 -0
  173. package/dist/logger/elog4js.js.map +1 -0
  174. package/dist/logger/logger.d.ts +37 -0
  175. package/dist/logger/logger.js +196 -0
  176. package/dist/logger/logger.js.map +1 -0
  177. package/dist/network/index.d.ts +2 -0
  178. package/dist/network/index.js +6 -0
  179. package/dist/network/index.js.map +1 -0
  180. package/dist/network/openai.d.ts +117 -0
  181. package/dist/network/openai.js +443 -0
  182. package/dist/network/openai.js.map +1 -0
  183. package/dist/network/proxy.d.ts +9 -0
  184. package/dist/network/proxy.js +72 -0
  185. package/dist/network/proxy.js.map +1 -0
  186. package/dist/network/web.d.ts +34 -0
  187. package/dist/network/web.js +152 -0
  188. package/dist/network/web.js.map +1 -0
  189. package/dist/platform/clipboard.d.ts +15 -0
  190. package/dist/platform/clipboard.js +82 -0
  191. package/dist/platform/clipboard.js.map +1 -0
  192. package/dist/platform/exec.d.ts +28 -0
  193. package/dist/platform/exec.js +95 -0
  194. package/dist/platform/exec.js.map +1 -0
  195. package/dist/platform/file.d.ts +38 -0
  196. package/dist/platform/file.js +197 -0
  197. package/dist/platform/file.js.map +1 -0
  198. package/dist/platform/index.d.ts +7 -0
  199. package/dist/platform/index.js +12 -0
  200. package/dist/platform/index.js.map +1 -0
  201. package/dist/platform/notification.d.ts +14 -0
  202. package/dist/platform/notification.js +82 -0
  203. package/dist/platform/notification.js.map +1 -0
  204. package/dist/platform/open-external.d.ts +10 -0
  205. package/dist/platform/open-external.js +105 -0
  206. package/dist/platform/open-external.js.map +1 -0
  207. package/dist/platform/os.d.ts +20 -0
  208. package/dist/platform/os.js +86 -0
  209. package/dist/platform/os.js.map +1 -0
  210. package/dist/platform/path.d.ts +32 -0
  211. package/dist/platform/path.js +154 -0
  212. package/dist/platform/path.js.map +1 -0
  213. package/dist/plugin/access-guard.d.ts +1 -0
  214. package/dist/plugin/access-guard.js +40 -0
  215. package/dist/plugin/access-guard.js.map +1 -0
  216. package/dist/plugin/agent-service.d.ts +25 -0
  217. package/dist/plugin/agent-service.js +809 -0
  218. package/dist/plugin/agent-service.js.map +1 -0
  219. package/dist/plugin/agent-tools.d.ts +12 -0
  220. package/dist/plugin/agent-tools.js +31 -0
  221. package/dist/plugin/agent-tools.js.map +1 -0
  222. package/dist/plugin/capabilities.d.ts +26 -0
  223. package/dist/plugin/capabilities.js +192 -0
  224. package/dist/plugin/capabilities.js.map +1 -0
  225. package/dist/plugin/console-guard.d.ts +2 -0
  226. package/dist/plugin/console-guard.js +52 -0
  227. package/dist/plugin/console-guard.js.map +1 -0
  228. package/dist/plugin/data-access.d.ts +15 -0
  229. package/dist/plugin/data-access.js +57 -0
  230. package/dist/plugin/data-access.js.map +1 -0
  231. package/dist/plugin/fs.d.ts +3 -0
  232. package/dist/plugin/fs.js +93 -0
  233. package/dist/plugin/fs.js.map +1 -0
  234. package/dist/plugin/host-alias.d.ts +2 -0
  235. package/dist/plugin/host-alias.js +57 -0
  236. package/dist/plugin/host-alias.js.map +1 -0
  237. package/dist/plugin/index.d.ts +7 -0
  238. package/dist/plugin/index.js +11 -0
  239. package/dist/plugin/index.js.map +1 -0
  240. package/dist/plugin/install.d.ts +12 -0
  241. package/dist/plugin/install.js +114 -0
  242. package/dist/plugin/install.js.map +1 -0
  243. package/dist/plugin/loader.d.ts +45 -0
  244. package/dist/plugin/loader.js +739 -0
  245. package/dist/plugin/loader.js.map +1 -0
  246. package/dist/plugin/manage.d.ts +31 -0
  247. package/dist/plugin/manage.js +265 -0
  248. package/dist/plugin/manage.js.map +1 -0
  249. package/dist/plugin/models-service.d.ts +15 -0
  250. package/dist/plugin/models-service.js +317 -0
  251. package/dist/plugin/models-service.js.map +1 -0
  252. package/dist/plugin/network-service.d.ts +10 -0
  253. package/dist/plugin/network-service.js +87 -0
  254. package/dist/plugin/network-service.js.map +1 -0
  255. package/dist/plugin/paths.d.ts +6 -0
  256. package/dist/plugin/paths.js +15 -0
  257. package/dist/plugin/paths.js.map +1 -0
  258. package/dist/plugin/registry.d.ts +17 -0
  259. package/dist/plugin/registry.js +82 -0
  260. package/dist/plugin/registry.js.map +1 -0
  261. package/dist/plugin/runtime.d.ts +9 -0
  262. package/dist/plugin/runtime.js +43 -0
  263. package/dist/plugin/runtime.js.map +1 -0
  264. package/dist/plugin/scaffold.d.ts +13 -0
  265. package/dist/plugin/scaffold.js +126 -0
  266. package/dist/plugin/scaffold.js.map +1 -0
  267. package/dist/plugin/state.d.ts +6 -0
  268. package/dist/plugin/state.js +38 -0
  269. package/dist/plugin/state.js.map +1 -0
  270. package/dist/plugin/zip.d.ts +7 -0
  271. package/dist/plugin/zip.js +138 -0
  272. package/dist/plugin/zip.js.map +1 -0
  273. package/dist/sandbox/audit.d.ts +12 -0
  274. package/dist/sandbox/audit.js +20 -0
  275. package/dist/sandbox/audit.js.map +1 -0
  276. package/dist/sandbox/backends/bwrap.d.ts +28 -0
  277. package/dist/sandbox/backends/bwrap.js +197 -0
  278. package/dist/sandbox/backends/bwrap.js.map +1 -0
  279. package/dist/sandbox/backends/direct.d.ts +7 -0
  280. package/dist/sandbox/backends/direct.js +32 -0
  281. package/dist/sandbox/backends/direct.js.map +1 -0
  282. package/dist/sandbox/bridge-state.d.ts +13 -0
  283. package/dist/sandbox/bridge-state.js +12 -0
  284. package/dist/sandbox/bridge-state.js.map +1 -0
  285. package/dist/sandbox/index.d.ts +5 -0
  286. package/dist/sandbox/index.js +6 -0
  287. package/dist/sandbox/index.js.map +1 -0
  288. package/dist/sandbox/manager.d.ts +8 -0
  289. package/dist/sandbox/manager.js +325 -0
  290. package/dist/sandbox/manager.js.map +1 -0
  291. package/dist/sandbox/memory-limit.d.ts +44 -0
  292. package/dist/sandbox/memory-limit.js +193 -0
  293. package/dist/sandbox/memory-limit.js.map +1 -0
  294. package/dist/sandbox/plugin-client.d.ts +80 -0
  295. package/dist/sandbox/plugin-client.js +1601 -0
  296. package/dist/sandbox/plugin-client.js.map +1 -0
  297. package/dist/sandbox/plugin-host.d.ts +1 -0
  298. package/dist/sandbox/plugin-host.js +1001 -0
  299. package/dist/sandbox/plugin-host.js.map +1 -0
  300. package/dist/sandbox/protocol.d.ts +43 -0
  301. package/dist/sandbox/protocol.js +96 -0
  302. package/dist/sandbox/protocol.js.map +1 -0
  303. package/dist/sandbox/rpc-timeouts.d.ts +28 -0
  304. package/dist/sandbox/rpc-timeouts.js +15 -0
  305. package/dist/sandbox/rpc-timeouts.js.map +1 -0
  306. package/dist/sandbox/supervisor.d.ts +31 -0
  307. package/dist/sandbox/supervisor.js +114 -0
  308. package/dist/sandbox/supervisor.js.map +1 -0
  309. package/dist/sandbox/types.d.ts +124 -0
  310. package/dist/sandbox/types.js +17 -0
  311. package/dist/sandbox/types.js.map +1 -0
  312. package/dist/service/control.d.ts +32 -0
  313. package/dist/service/control.js +117 -0
  314. package/dist/service/control.js.map +1 -0
  315. package/dist/session/compress.d.ts +3 -0
  316. package/dist/session/compress.js +42 -0
  317. package/dist/session/compress.js.map +1 -0
  318. package/dist/session/index.d.ts +4 -0
  319. package/dist/session/index.js +8 -0
  320. package/dist/session/index.js.map +1 -0
  321. package/dist/session/registry.d.ts +4 -0
  322. package/dist/session/registry.js +12 -0
  323. package/dist/session/registry.js.map +1 -0
  324. package/dist/session/replay.d.ts +10 -0
  325. package/dist/session/replay.js +109 -0
  326. package/dist/session/replay.js.map +1 -0
  327. package/dist/session/session.d.ts +40 -0
  328. package/dist/session/session.js +108 -0
  329. package/dist/session/session.js.map +1 -0
  330. package/dist/session/store.d.ts +58 -0
  331. package/dist/session/store.js +382 -0
  332. package/dist/session/store.js.map +1 -0
  333. package/dist/session/types.d.ts +56 -0
  334. package/dist/session/types.js +3 -0
  335. package/dist/session/types.js.map +1 -0
  336. package/dist/workspace/index.d.ts +7 -0
  337. package/dist/workspace/index.js +17 -0
  338. package/dist/workspace/index.js.map +1 -0
  339. package/dist/workspace/registry.d.ts +25 -0
  340. package/dist/workspace/registry.js +293 -0
  341. package/dist/workspace/registry.js.map +1 -0
  342. package/package.json +73 -5
  343. package/scripts/build-node-pty.mjs +55 -0
  344. package/scripts/rewrite-dts.mjs +36 -0
  345. package/scripts/verify-sandbox.ts +98 -0
  346. package/src/agent/pending-asks.ts +88 -0
  347. package/src/agent/task-registry.ts +108 -0
  348. package/src/agent/tool-context.ts +47 -0
  349. package/src/agent/usage.ts +170 -0
  350. package/src/api/host/agent.ts +162 -0
  351. package/src/api/host/events.ts +7 -0
  352. package/src/api/host/exec.ts +47 -0
  353. package/src/api/host/fs.ts +33 -0
  354. package/src/api/host/hooks.ts +635 -0
  355. package/src/api/host/host.ts +99 -0
  356. package/src/api/host/http.ts +79 -0
  357. package/src/api/host/index.ts +20 -0
  358. package/src/api/host/models.ts +81 -0
  359. package/src/api/host/network.ts +32 -0
  360. package/src/api/host/plugin.ts +158 -0
  361. package/src/api/host/plugins.ts +55 -0
  362. package/src/api/host/pty.ts +73 -0
  363. package/src/api/host/session.ts +61 -0
  364. package/src/api/host/version.ts +2 -0
  365. package/src/api/host/workspace.ts +51 -0
  366. package/src/api/plugin/http-client.ts +492 -0
  367. package/src/api/plugin/http-protocol.ts +231 -0
  368. package/src/api/result/result.ts +52 -0
  369. package/src/commands/agent-turn.ts +817 -0
  370. package/src/commands/ask-prompt.ts +52 -0
  371. package/src/commands/config.ts +306 -0
  372. package/src/commands/conversation.ts +777 -0
  373. package/src/commands/doctor.ts +92 -0
  374. package/src/commands/exec-approval.ts +91 -0
  375. package/src/commands/help.ts +41 -0
  376. package/src/commands/key.ts +281 -0
  377. package/src/commands/plugin.ts +218 -0
  378. package/src/commands/session.ts +179 -0
  379. package/src/commands/shared.ts +103 -0
  380. package/src/commands/start.ts +88 -0
  381. package/src/commands/stop.ts +24 -0
  382. package/src/commands/version.ts +40 -0
  383. package/src/config/codes.ts +17 -0
  384. package/src/config/credentials.ts +302 -0
  385. package/src/config/defaults.ts +32 -0
  386. package/src/config/index.ts +10 -0
  387. package/src/config/loader.ts +80 -0
  388. package/src/config/providers.ts +133 -0
  389. package/src/config/types.ts +88 -0
  390. package/src/config/validate.ts +458 -0
  391. package/src/display/markdown.ts +137 -0
  392. package/src/display/tool-label.ts +87 -0
  393. package/src/event/event.ts +171 -0
  394. package/src/index.ts +344 -0
  395. package/src/input/codes.ts +21 -0
  396. package/src/input/commands.ts +76 -0
  397. package/src/input/help.ts +60 -0
  398. package/src/input/index.ts +10 -0
  399. package/src/input/parse-options.ts +66 -0
  400. package/src/input/parse.ts +138 -0
  401. package/src/input/types.ts +19 -0
  402. package/src/logger/elog4js.ts +366 -0
  403. package/src/logger/logger.ts +251 -0
  404. package/src/network/index.ts +6 -0
  405. package/src/network/openai.ts +571 -0
  406. package/src/network/proxy.ts +80 -0
  407. package/src/network/web.ts +206 -0
  408. package/src/platform/clipboard.ts +107 -0
  409. package/src/platform/exec.ts +128 -0
  410. package/src/platform/file.ts +235 -0
  411. package/src/platform/index.ts +12 -0
  412. package/src/platform/notification.ts +108 -0
  413. package/src/platform/open-external.ts +132 -0
  414. package/src/platform/os.ts +101 -0
  415. package/src/platform/path.ts +214 -0
  416. package/src/plugin/access-guard.ts +46 -0
  417. package/src/plugin/agent-service.ts +969 -0
  418. package/src/plugin/agent-tools.ts +43 -0
  419. package/src/plugin/capabilities.ts +240 -0
  420. package/src/plugin/console-guard.ts +66 -0
  421. package/src/plugin/data-access.ts +70 -0
  422. package/src/plugin/fs.ts +109 -0
  423. package/src/plugin/host-alias.ts +61 -0
  424. package/src/plugin/index.ts +11 -0
  425. package/src/plugin/install.ts +149 -0
  426. package/src/plugin/loader.ts +952 -0
  427. package/src/plugin/manage.ts +329 -0
  428. package/src/plugin/models-service.ts +383 -0
  429. package/src/plugin/network-service.ts +102 -0
  430. package/src/plugin/paths.ts +19 -0
  431. package/src/plugin/registry.ts +103 -0
  432. package/src/plugin/runtime.ts +57 -0
  433. package/src/plugin/scaffold.ts +152 -0
  434. package/src/plugin/state.ts +52 -0
  435. package/src/plugin/zip.ts +156 -0
  436. package/src/sandbox/audit.ts +38 -0
  437. package/src/sandbox/backends/bwrap.ts +251 -0
  438. package/src/sandbox/backends/direct.ts +43 -0
  439. package/src/sandbox/bridge-state.ts +29 -0
  440. package/src/sandbox/index.ts +5 -0
  441. package/src/sandbox/manager.ts +406 -0
  442. package/src/sandbox/memory-limit.ts +245 -0
  443. package/src/sandbox/plugin-client.ts +1881 -0
  444. package/src/sandbox/plugin-host.ts +1282 -0
  445. package/src/sandbox/protocol.ts +142 -0
  446. package/src/sandbox/pty-bridge.py +154 -0
  447. package/src/sandbox/rpc-timeouts.ts +43 -0
  448. package/src/sandbox/supervisor.ts +172 -0
  449. package/src/sandbox/types.ts +149 -0
  450. package/src/service/control.ts +155 -0
  451. package/src/session/compress.ts +47 -0
  452. package/src/session/index.ts +8 -0
  453. package/src/session/registry.ts +17 -0
  454. package/src/session/replay.ts +132 -0
  455. package/src/session/session.ts +134 -0
  456. package/src/session/store.ts +468 -0
  457. package/src/session/types.ts +61 -0
  458. package/src/workspace/index.ts +21 -0
  459. package/src/workspace/registry.ts +356 -0
@@ -0,0 +1,777 @@
1
+ /*
2
+ * chat / run 命令。
3
+ * 无 context 且 TTY 时进入交互式会话;否则单轮执行。
4
+ * 所有轮次都作为 zstd JSONL 段写入会话,流式 chunk 一并保存。
5
+ */
6
+
7
+ import { randomUUID } from "node:crypto"
8
+ import { createInterface } from "node:readline"
9
+
10
+ import type { ChatCompletionMessageParam } from "openai/resources/chat/completions"
11
+
12
+ import {
13
+ emitAfterStep,
14
+ emitAfterTurn,
15
+ emitBeforeStep,
16
+ emitBeforeTurn,
17
+ emitOnMessage,
18
+ emitSessionStart,
19
+ } from "../api/host/hooks.ts"
20
+ import { Result } from "../api/result/result.ts"
21
+ import { createMarkdownStream, renderMarkdown } from "../display/markdown.ts"
22
+ import { USAGE_CODES } from "../input/codes.ts"
23
+ import { booleanValue, parseCommandArgs, stringValue } from "../input/parse-options.ts"
24
+ import { usageOf, type TokenUsage } from "../agent/usage.ts"
25
+ import type { OpenAIClient, StreamReply } from "../network/openai.ts"
26
+ import type { SandboxAccess } from "../config/types.ts"
27
+ import { registerSession } from "../session/registry.ts"
28
+ import { lastSeq, lastTurnNumber, toChatMessages } from "../session/replay.ts"
29
+ import { Session } from "../session/session.ts"
30
+ import {
31
+ SessionAmbiguousError,
32
+ SessionLockedError,
33
+ SessionNotFoundError,
34
+ SessionStore,
35
+ isValidSessionId,
36
+ type SessionData,
37
+ } from "../session/store.ts"
38
+ import type { SessionEvent, SessionMessageRecord, SessionMode } from "../session/types.ts"
39
+ import type { HostAgentAskRequest, HostAgentToolEvent } from "../api/host/agent.ts"
40
+ import { executeAgentTurn, type ExecApprovalRequest } from "./agent-turn.ts"
41
+ import { promptAgentAsk } from "./ask-prompt.ts"
42
+ import { promptExecApproval } from "./exec-approval.ts"
43
+ import {
44
+ errorMessage,
45
+ failure,
46
+ success,
47
+ type CommandContext,
48
+ type CommandOutcome,
49
+ } from "./shared.ts"
50
+
51
+ interface ConversationOptions {
52
+ readonly sessionId?: string
53
+ readonly newSession: boolean
54
+ readonly model: string
55
+ readonly reasoning?: string
56
+ readonly system?: string
57
+ readonly agent?: string
58
+ readonly json: boolean
59
+ readonly stream: boolean
60
+ readonly allowExec: boolean
61
+ readonly permission: SandboxAccess
62
+ readonly maxSteps?: number
63
+ readonly context: readonly string[]
64
+ }
65
+
66
+ export interface TurnData {
67
+ readonly sessionId: string
68
+ readonly mode: SessionMode
69
+ /** 会话内第几轮 */
70
+ readonly turn: number
71
+ /** 本轮执行的步数 */
72
+ readonly steps: number
73
+ readonly reply: string
74
+ readonly model: string
75
+ /** 本轮 token 用量;供应商没有报告时为 null */
76
+ readonly usage: TokenUsage | null
77
+ }
78
+
79
+ function parseConversationOptions(
80
+ ctx: CommandContext,
81
+ argv: readonly string[],
82
+ ): Result<ConversationOptions> {
83
+ const parsed = parseCommandArgs(argv, {
84
+ "session-id": { type: "string" },
85
+ new: { type: "boolean" },
86
+ model: { type: "string" },
87
+ system: { type: "string" },
88
+ agent: { type: "string" },
89
+ json: { type: "boolean" },
90
+ stream: { type: "boolean" },
91
+ "allow-exec": { type: "boolean" },
92
+ permission: { type: "string" },
93
+ "max-steps": { type: "string" },
94
+ "no-stream": { type: "boolean" },
95
+ reasoning: { type: "string" },
96
+ })
97
+ if (!parsed.isSuccess()) {
98
+ return Result.error<ConversationOptions>(parsed.getCode() ?? USAGE_CODES.invalidValue)
99
+ }
100
+
101
+ const line = parsed.getData()
102
+ if (line === null) {
103
+ return Result.error<ConversationOptions>(USAGE_CODES.invalidValue)
104
+ }
105
+ const { values, positionals } = line
106
+
107
+ const sessionId = stringValue(values, "session-id")
108
+ const newSession = booleanValue(values, "new")
109
+ if (sessionId !== undefined && newSession) {
110
+ return Result.error<ConversationOptions>(USAGE_CODES.conflictingOptions)
111
+ }
112
+ if (sessionId !== undefined && !isValidSessionId(sessionId)) {
113
+ return Result.error<ConversationOptions>(USAGE_CODES.invalidSessionId)
114
+ }
115
+ const streamFlag = booleanValue(values, "stream")
116
+ const noStream = booleanValue(values, "no-stream")
117
+ if (streamFlag && noStream) {
118
+ return Result.error<ConversationOptions>(USAGE_CODES.conflictingOptions)
119
+ }
120
+
121
+ const json = ctx.global.json || booleanValue(values, "json")
122
+ const stream =
123
+ streamFlag || noStream ? streamFlag : json === false && process.stdout.isTTY === true
124
+
125
+ const rawPermission = stringValue(values, "permission")
126
+ let permission = ctx.settings.sandbox.access
127
+ if (rawPermission !== undefined) {
128
+ if (
129
+ rawPermission !== "read-only" &&
130
+ rawPermission !== "workspace-write" &&
131
+ rawPermission !== "danger-full-access"
132
+ ) {
133
+ return Result.error<ConversationOptions>(USAGE_CODES.invalidValue)
134
+ }
135
+ permission = rawPermission
136
+ }
137
+
138
+ const rawMaxSteps = stringValue(values, "max-steps")
139
+ let maxSteps: number | undefined
140
+ if (rawMaxSteps !== undefined) {
141
+ const parsedSteps = Number(rawMaxSteps)
142
+ if (!Number.isInteger(parsedSteps) || parsedSteps < 1 || parsedSteps > 32) {
143
+ return Result.error<ConversationOptions>(USAGE_CODES.invalidValue)
144
+ }
145
+ maxSteps = parsedSteps
146
+ }
147
+
148
+ return Result.successWithData<ConversationOptions>({
149
+ sessionId,
150
+ newSession,
151
+ model: stringValue(values, "model") ?? ctx.activeModel ?? ctx.settings.openai.model,
152
+ system: stringValue(values, "system"),
153
+ agent: stringValue(values, "agent"),
154
+ json,
155
+ stream,
156
+ allowExec: booleanValue(values, "allow-exec"),
157
+ permission,
158
+ maxSteps,
159
+ reasoning: stringValue(values, "reasoning") ?? ctx.activeReasoning,
160
+ context: positionals,
161
+ })
162
+ }
163
+
164
+ function sessionCode(error: unknown): string {
165
+ if (error instanceof SessionAmbiguousError) {
166
+ return "session.ambiguous-id"
167
+ }
168
+ if (error instanceof SessionNotFoundError) {
169
+ return "session.not-found"
170
+ }
171
+ if (error instanceof SessionLockedError) {
172
+ return "session.locked"
173
+ }
174
+ if (error instanceof Error && error.message.includes("会话 id 不合法")) {
175
+ return USAGE_CODES.invalidSessionId
176
+ }
177
+ return "session.corrupted"
178
+ }
179
+
180
+ async function resolveSession(
181
+ store: SessionStore,
182
+ cwd: string,
183
+ options: ConversationOptions,
184
+ ): Promise<{ session: SessionData; created: boolean }> {
185
+ if (options.newSession) {
186
+ return { session: await store.create({ cwd, model: options.model }), created: true }
187
+ }
188
+
189
+ if (options.sessionId !== undefined) {
190
+ try {
191
+ return { session: await store.open(options.sessionId, cwd), created: false }
192
+ } catch (error) {
193
+ if (error instanceof SessionNotFoundError) {
194
+ return {
195
+ session: await store.create({
196
+ cwd,
197
+ model: options.model,
198
+ id: options.sessionId,
199
+ }),
200
+ created: true,
201
+ }
202
+ }
203
+ throw error
204
+ }
205
+ }
206
+
207
+ const last = await store.lastId(cwd)
208
+ if (last !== null) {
209
+ try {
210
+ return { session: await store.open(last, cwd), created: false }
211
+ } catch {
212
+ // 落到新建
213
+ }
214
+ }
215
+ return { session: await store.create({ cwd, model: options.model }), created: true }
216
+ }
217
+
218
+ async function readStdin(): Promise<string> {
219
+ process.stdin.setEncoding("utf8")
220
+ let text = ""
221
+ for await (const chunk of process.stdin) {
222
+ text += String(chunk)
223
+ }
224
+ return text
225
+ }
226
+
227
+ /** 执行一轮对话并写入会话;失败也会提交 failed 轮次 */
228
+ export async function executeTurn(params: {
229
+ readonly ctx: CommandContext
230
+ readonly client: OpenAIClient
231
+ readonly store: SessionStore
232
+ readonly session: SessionData
233
+ readonly mode: SessionMode
234
+ readonly input: string
235
+ readonly model: string
236
+ readonly reasoning?: string
237
+ readonly system?: string
238
+ readonly stream: boolean
239
+ readonly display: boolean
240
+ readonly allowExec?: boolean
241
+ readonly permission?: SandboxAccess
242
+ readonly maxSteps?: number
243
+ readonly approveExec?: (request: ExecApprovalRequest) => Promise<boolean>
244
+ readonly onDelta?: (text: string) => void
245
+ readonly onReasoning?: (text: string) => void
246
+ readonly onTool?: (event: HostAgentToolEvent) => void
247
+ readonly onAsk?: (request: HostAgentAskRequest) => void
248
+ readonly askUser?: (request: HostAgentAskRequest) => Promise<string | null>
249
+ readonly onStep?: (event: { readonly turn: number; readonly step: number }) => void
250
+ /** 中断信号:用户停止时打断在途的模型调用(只对 run 模式有效) */
251
+ readonly signal?: AbortSignal
252
+ }): Promise<Result<TurnData>> {
253
+ const { ctx, client, store, session, mode, input, model, system, stream, display } = params
254
+ if (mode === "run") {
255
+ return await executeAgentTurn({
256
+ ctx,
257
+ client,
258
+ store,
259
+ session,
260
+ input,
261
+ model,
262
+ reasoning: params.reasoning,
263
+ system,
264
+ stream,
265
+ display,
266
+ allowExec: params.allowExec,
267
+ permission: params.permission,
268
+ maxSteps: params.maxSteps,
269
+ approveExec: params.approveExec,
270
+ onDelta: params.onDelta,
271
+ onReasoning: params.onReasoning,
272
+ onTool: params.onTool,
273
+ onAsk: params.onAsk,
274
+ askUser: params.askUser,
275
+ onStep: params.onStep,
276
+ signal: params.signal,
277
+ })
278
+ }
279
+ const turn = lastTurnNumber(session.events) + 1
280
+ let seq = lastSeq(session.events) + 1
281
+ const startedAt = Date.now()
282
+
283
+ await emitBeforeTurn({
284
+ sessionId: session.meta.id,
285
+ mode,
286
+ turn,
287
+ input,
288
+ cwd: session.meta.workspace ?? session.meta.cwd,
289
+ })
290
+ const events: SessionEvent[] = [
291
+ { type: "turn/start", seq: seq++, time: startedAt, data: { turn, mode } },
292
+ {
293
+ type: "user/message",
294
+ seq: seq++,
295
+ time: Date.now(),
296
+ data: {
297
+ message: {
298
+ role: "user",
299
+ content: [{ type: "text", text: input }],
300
+ source: { kind: "cli" },
301
+ id: randomUUID(),
302
+ } satisfies SessionMessageRecord,
303
+ surfaceOp: "append",
304
+ },
305
+ },
306
+ ]
307
+
308
+ /*
309
+ * 用户消息先落盘:进程在模型流式过程中退出时,这一轮的问题不会从会话里消失。
310
+ */
311
+ let persisted = 0
312
+ const persistPending = async (): Promise<void> => {
313
+ if (events.length <= persisted) {
314
+ return
315
+ }
316
+ await store.appendTurn(session, events.slice(persisted))
317
+ persisted = events.length
318
+ }
319
+
320
+ // onSessionStart 只在新会话的第一轮触发;onMessage 的 before 阶段每轮都可以追加系统提示
321
+ const prompt: string[] = []
322
+ if (system !== undefined && system.trim() !== "") {
323
+ prompt.push(system)
324
+ }
325
+ if (turn === 1) {
326
+ await emitSessionStart({
327
+ sessionId: session.meta.id,
328
+ mode,
329
+ turn,
330
+ input,
331
+ cwd: session.meta.workspace ?? session.meta.cwd,
332
+ time: startedAt,
333
+ timeZone: Intl.DateTimeFormat().resolvedOptions().timeZone,
334
+ model,
335
+ permission: params.permission ?? ctx.settings.sandbox.access,
336
+ // chat 模式没有工具循环
337
+ tools: [],
338
+ prompt,
339
+ })
340
+ }
341
+ await emitOnMessage({
342
+ sessionId: session.meta.id,
343
+ mode,
344
+ turn,
345
+ role: "user",
346
+ phase: "before",
347
+ content: input,
348
+ prompt,
349
+ })
350
+
351
+ const messages: ChatCompletionMessageParam[] = []
352
+ const systemText = prompt
353
+ .map((item) => item.trim())
354
+ .filter((item) => item !== "")
355
+ .join("\n\n")
356
+ if (systemText !== "") {
357
+ messages.push({ role: "system", content: systemText })
358
+ }
359
+ messages.push(...toChatMessages(session.events), { role: "user", content: input })
360
+
361
+ // 模型入参已经拼好,再把开头事件落盘,避免本轮用户消息被重复算进历史
362
+ await persistPending()
363
+
364
+ await emitBeforeStep({
365
+ sessionId: session.meta.id,
366
+ mode,
367
+ turn,
368
+ step: 1,
369
+ input,
370
+ })
371
+
372
+ const streamed = display && stream
373
+ const markdownEnabled = display && process.stdout.isTTY === true
374
+ const markdown = markdownEnabled && streamed ? createMarkdownStream() : null
375
+ const streamResult = await client.chatStream(messages, {
376
+ model,
377
+ reasoning: params.reasoning,
378
+ onText: streamed
379
+ ? (text) => {
380
+ ctx.elog.stream(markdown === null ? text : markdown.push(text))
381
+ }
382
+ : undefined,
383
+ })
384
+
385
+ if (!streamResult.isSuccess()) {
386
+ const code = streamResult.getCode() ?? "openai.request-failed"
387
+ events.push({
388
+ type: "turn/end",
389
+ seq: seq++,
390
+ time: Date.now(),
391
+ data: { turn, reason: { kind: "failed", code } },
392
+ })
393
+ await persistPending()
394
+ await emitAfterTurn({
395
+ sessionId: session.meta.id,
396
+ mode,
397
+ turn,
398
+ reason: "failed",
399
+ code,
400
+ })
401
+ if (streamed) {
402
+ if (markdown !== null) {
403
+ const tail = markdown.finish().replace(/\n+$/, "")
404
+ if (tail !== "") {
405
+ ctx.elog.stream(tail)
406
+ }
407
+ }
408
+ ctx.elog.line()
409
+ }
410
+ return Result.error<TurnData>(code)
411
+ }
412
+
413
+ const data = streamResult.getData() as StreamReply
414
+ const assistant: SessionMessageRecord = {
415
+ role: "assistant",
416
+ content: [{ type: "text", text: data.reply.text }],
417
+ source: { kind: "model", provider: "openai", model: data.reply.model },
418
+ id: data.reply.id,
419
+ }
420
+ events.push({
421
+ type: "assistant/message",
422
+ seq: seq++,
423
+ time: Date.now(),
424
+ data: {
425
+ turn,
426
+ step: 1,
427
+ message: assistant,
428
+ usage: { ...usageOf(data.reply) },
429
+ stream: data.chunks,
430
+ },
431
+ })
432
+ events.push({
433
+ type: "turn/end",
434
+ seq,
435
+ time: Date.now(),
436
+ data: { turn, reason: { kind: "completed" } },
437
+ })
438
+ await persistPending()
439
+
440
+ await emitOnMessage({
441
+ sessionId: session.meta.id,
442
+ mode,
443
+ turn,
444
+ role: "assistant",
445
+ phase: "after",
446
+ content: data.reply.text,
447
+ prompt,
448
+ })
449
+ await emitAfterStep({
450
+ sessionId: session.meta.id,
451
+ mode,
452
+ turn,
453
+ step: 1,
454
+ reply: data.reply.text,
455
+ })
456
+ await emitAfterTurn({
457
+ sessionId: session.meta.id,
458
+ mode,
459
+ turn,
460
+ reason: "completed",
461
+ code: null,
462
+ })
463
+
464
+ if (streamed) {
465
+ if (markdown !== null) {
466
+ const tail = markdown.finish().replace(/\n+$/, "")
467
+ if (tail !== "") {
468
+ ctx.elog.stream(tail)
469
+ }
470
+ }
471
+ ctx.elog.line()
472
+ } else if (display) {
473
+ ctx.elog.print(markdownEnabled ? renderMarkdown(data.reply.text) : data.reply.text)
474
+ }
475
+
476
+ return Result.successWithData<TurnData>({
477
+ sessionId: session.meta.id,
478
+ mode,
479
+ turn,
480
+ steps: 1,
481
+ reply: data.reply.text,
482
+ model: data.reply.model,
483
+ usage:
484
+ data.reply.inputTokens === null &&
485
+ data.reply.outputTokens === null &&
486
+ data.reply.cacheReadTokens === undefined &&
487
+ data.reply.cacheWriteTokens === undefined
488
+ ? null
489
+ : usageOf(data.reply),
490
+ })
491
+ }
492
+
493
+ function promptText(session: SessionData, mode: SessionMode): string {
494
+ const name = session.meta.title ?? session.meta.id.slice(-4)
495
+ return `${name}${mode === "run" ? " [run]" : ""} > `
496
+ }
497
+
498
+ async function runRepl(
499
+ ctx: CommandContext,
500
+ params: {
501
+ readonly store: SessionStore
502
+ readonly session: SessionData
503
+ readonly mode: SessionMode
504
+ readonly options: ConversationOptions
505
+ },
506
+ ): Promise<CommandOutcome> {
507
+ const cwd = process.cwd()
508
+ const rl = createInterface({
509
+ input: process.stdin,
510
+ output: process.stdout,
511
+ terminal: process.stdin.isTTY === true,
512
+ historySize: 100,
513
+ })
514
+ let session = params.session
515
+ registerSession(new Session(session, params.store))
516
+ let model = params.options.model
517
+ let permission = params.options.permission
518
+ let closed = false
519
+
520
+ const refresh = (): void => {
521
+ const name = session.meta.title ?? session.meta.id.slice(-4)
522
+ ctx.elog.setSessionName(name)
523
+ }
524
+ refresh()
525
+
526
+ // 逐次审批
527
+ const approveExec = async (request: ExecApprovalRequest): Promise<boolean> => {
528
+ rl.pause()
529
+ try {
530
+ return await promptExecApproval(request)
531
+ } finally {
532
+ rl.resume()
533
+ }
534
+ }
535
+
536
+ // ask 工具:在当前终端里提问
537
+ const askUser = async (request: HostAgentAskRequest): Promise<string | null> => {
538
+ rl.pause()
539
+ try {
540
+ return await promptAgentAsk(request)
541
+ } finally {
542
+ rl.resume()
543
+ }
544
+ }
545
+
546
+ const handleSlash = async (line: string): Promise<"exit" | "continue"> => {
547
+ const [command, ...args] = line.split(/\s+/)
548
+ switch (command) {
549
+ case "/exit":
550
+ case "/quit":
551
+ return "exit"
552
+ case "/help":
553
+ ctx.elog.info("命令:/exit /help /new /session [/list|id] /clear /model <name>")
554
+ return "continue"
555
+ case "/new":
556
+ session = await params.store.create({ cwd, model })
557
+ registerSession(new Session(session, params.store))
558
+ ctx.elog.notice(`session: ${session.meta.id}`)
559
+ return "continue"
560
+ case "/session": {
561
+ if (args[0] === "list") {
562
+ const list = await params.store.list({ cwd, limit: 20 })
563
+ for (const item of list) {
564
+ ctx.elog.print(
565
+ `${item.id}${item.last ? " *" : ""} turns=${item.turns} ${item.cwd}`,
566
+ )
567
+ }
568
+ return "continue"
569
+ }
570
+ if (args[0] !== undefined) {
571
+ session = await params.store.open(args[0], cwd)
572
+ registerSession(new Session(session, params.store))
573
+ return "continue"
574
+ }
575
+ ctx.elog.print(
576
+ `${session.meta.id} turns=${session.turns.length} ${session.meta.workspace ?? session.meta.cwd}`,
577
+ )
578
+ return "continue"
579
+ }
580
+ case "/clear":
581
+ ctx.elog.clear()
582
+ return "continue"
583
+ case "/permission":
584
+ if (args[0] !== undefined) {
585
+ if (
586
+ args[0] !== "read-only" &&
587
+ args[0] !== "workspace-write" &&
588
+ args[0] !== "danger-full-access"
589
+ ) {
590
+ ctx.elog.error(
591
+ "用法:/permission read-only|workspace-write|danger-full-access",
592
+ )
593
+ return "continue"
594
+ }
595
+ permission = args[0]
596
+ }
597
+ ctx.elog.info(`permission: ${permission}`)
598
+ return "continue"
599
+ case "/model":
600
+ if (args[0] !== undefined && args[0].trim() !== "") {
601
+ model = args[0].trim()
602
+ ctx.elog.info(`model: ${model}`)
603
+ } else {
604
+ ctx.elog.error("用法:/model <name>")
605
+ }
606
+ return "continue"
607
+ default:
608
+ ctx.elog.error(`未知命令:${command ?? ""}`)
609
+ return "continue"
610
+ }
611
+ }
612
+
613
+ const ask = (): void => {
614
+ rl.question(promptText(session, params.mode), (line) => {
615
+ void handleLine(line)
616
+ })
617
+ }
618
+
619
+ const handleLine = async (line: string): Promise<void> => {
620
+ if (closed) {
621
+ return
622
+ }
623
+ const text = line.trim()
624
+ if (text === "") {
625
+ ask()
626
+ return
627
+ }
628
+ if (text.startsWith("/") && !text.startsWith("//")) {
629
+ try {
630
+ if ((await handleSlash(text)) === "exit") {
631
+ rl.close()
632
+ return
633
+ }
634
+ } catch (error) {
635
+ ctx.elog.error(errorMessage(sessionCode(error)))
636
+ }
637
+ refresh()
638
+ ask()
639
+ return
640
+ }
641
+
642
+ const input = text.startsWith("//") ? text.slice(1) : text
643
+ try {
644
+ const result = await executeTurn({
645
+ ctx,
646
+ client: ctx.openai as OpenAIClient,
647
+ store: params.store,
648
+ session,
649
+ mode: params.mode,
650
+ input,
651
+ model,
652
+ reasoning: params.options.reasoning,
653
+ system: params.options.system,
654
+ stream: params.options.stream,
655
+ display: true,
656
+ allowExec: params.options.allowExec,
657
+ permission,
658
+ maxSteps: params.options.maxSteps,
659
+ approveExec,
660
+ askUser,
661
+ })
662
+ if (!result.isSuccess()) {
663
+ ctx.elog.error(errorMessage(result.getCode() ?? "openai.request-failed"))
664
+ }
665
+ } catch (error) {
666
+ ctx.elog.error(errorMessage(sessionCode(error)))
667
+ }
668
+ refresh()
669
+ ask()
670
+ }
671
+
672
+ rl.on("SIGINT", () => {
673
+ rl.close()
674
+ })
675
+ rl.on("close", () => {
676
+ closed = true
677
+ })
678
+ ask()
679
+ await new Promise<void>((resolve) => rl.once("close", resolve))
680
+ return success(null)
681
+ }
682
+
683
+ export async function runConversationCommand(
684
+ ctx: CommandContext,
685
+ argv: readonly string[],
686
+ mode: SessionMode,
687
+ ): Promise<CommandOutcome> {
688
+ if (ctx.openai === null) {
689
+ return failure("openai.missing-api-key")
690
+ }
691
+
692
+ const parsed = parseConversationOptions(ctx, argv)
693
+ if (!parsed.isSuccess()) {
694
+ return { result: Result.error<unknown>(parsed.getCode() ?? USAGE_CODES.invalidValue) }
695
+ }
696
+ const options = parsed.getData()
697
+ if (options === null) {
698
+ return failure(USAGE_CODES.invalidValue)
699
+ }
700
+
701
+ const store = new SessionStore()
702
+ const cwd = process.cwd()
703
+ let session: SessionData
704
+ let created = false
705
+ try {
706
+ const resolved = await resolveSession(store, cwd, options)
707
+ session = resolved.session
708
+ created = resolved.created
709
+ } catch (error) {
710
+ return failure(sessionCode(error))
711
+ }
712
+
713
+ const interactive =
714
+ options.json === false &&
715
+ process.stdin.isTTY === true &&
716
+ process.stdout.isTTY === true &&
717
+ options.context.length === 0
718
+
719
+ if (interactive) {
720
+ if (created) {
721
+ ctx.elog.notice(`session: ${session.meta.id}`)
722
+ }
723
+ return await runRepl(ctx, { store, session, mode, options })
724
+ }
725
+
726
+ let input = options.context.join(" ").trim()
727
+ if (input === "") {
728
+ if (process.stdin.isTTY === true) {
729
+ return failure(USAGE_CODES.missingContext)
730
+ }
731
+ input = (await readStdin()).trim()
732
+ }
733
+ if (input === "") {
734
+ return failure(USAGE_CODES.missingContext)
735
+ }
736
+
737
+ if (created) {
738
+ ctx.elog.notice(`session: ${session.meta.id}`)
739
+ }
740
+ registerSession(new Session(session, store))
741
+
742
+ const result = await executeTurn({
743
+ ctx,
744
+ client: ctx.openai,
745
+ store,
746
+ session,
747
+ mode,
748
+ input,
749
+ model: options.model,
750
+ reasoning: options.reasoning,
751
+ system: options.system,
752
+ stream: options.stream,
753
+ display: options.json === false,
754
+ allowExec: options.allowExec,
755
+ permission: options.permission,
756
+ maxSteps: options.maxSteps,
757
+ approveExec:
758
+ options.json === false && process.stdin.isTTY === true && process.stdout.isTTY === true
759
+ ? promptExecApproval
760
+ : undefined,
761
+ askUser:
762
+ options.json === false && process.stdin.isTTY === true && process.stdout.isTTY === true
763
+ ? promptAgentAsk
764
+ : async () => null,
765
+ })
766
+ if (!result.isSuccess()) {
767
+ return { result: Result.error<unknown>(result.getCode() ?? "openai.request-failed") }
768
+ }
769
+ const data = result.getData()
770
+ return success({
771
+ sessionId: data?.sessionId,
772
+ mode,
773
+ reply: data?.reply,
774
+ model: data?.model,
775
+ usage: data?.usage,
776
+ })
777
+ }