@xyit/wellspring 0.0.0-stage → 1.1.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 (464) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +853 -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 +49 -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 +190 -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 +17 -0
  241. package/dist/plugin/install.js +163 -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 +271 -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/package-manager.d.ts +46 -0
  256. package/dist/plugin/package-manager.js +148 -0
  257. package/dist/plugin/package-manager.js.map +1 -0
  258. package/dist/plugin/paths.d.ts +6 -0
  259. package/dist/plugin/paths.js +15 -0
  260. package/dist/plugin/paths.js.map +1 -0
  261. package/dist/plugin/registry.d.ts +17 -0
  262. package/dist/plugin/registry.js +82 -0
  263. package/dist/plugin/registry.js.map +1 -0
  264. package/dist/plugin/runtime.d.ts +9 -0
  265. package/dist/plugin/runtime.js +43 -0
  266. package/dist/plugin/runtime.js.map +1 -0
  267. package/dist/plugin/scaffold.d.ts +13 -0
  268. package/dist/plugin/scaffold.js +132 -0
  269. package/dist/plugin/scaffold.js.map +1 -0
  270. package/dist/plugin/state.d.ts +6 -0
  271. package/dist/plugin/state.js +38 -0
  272. package/dist/plugin/state.js.map +1 -0
  273. package/dist/plugin/zip.d.ts +7 -0
  274. package/dist/plugin/zip.js +138 -0
  275. package/dist/plugin/zip.js.map +1 -0
  276. package/dist/sandbox/audit.d.ts +12 -0
  277. package/dist/sandbox/audit.js +20 -0
  278. package/dist/sandbox/audit.js.map +1 -0
  279. package/dist/sandbox/backends/bwrap.d.ts +28 -0
  280. package/dist/sandbox/backends/bwrap.js +197 -0
  281. package/dist/sandbox/backends/bwrap.js.map +1 -0
  282. package/dist/sandbox/backends/direct.d.ts +7 -0
  283. package/dist/sandbox/backends/direct.js +32 -0
  284. package/dist/sandbox/backends/direct.js.map +1 -0
  285. package/dist/sandbox/bridge-state.d.ts +13 -0
  286. package/dist/sandbox/bridge-state.js +12 -0
  287. package/dist/sandbox/bridge-state.js.map +1 -0
  288. package/dist/sandbox/index.d.ts +5 -0
  289. package/dist/sandbox/index.js +6 -0
  290. package/dist/sandbox/index.js.map +1 -0
  291. package/dist/sandbox/manager.d.ts +8 -0
  292. package/dist/sandbox/manager.js +325 -0
  293. package/dist/sandbox/manager.js.map +1 -0
  294. package/dist/sandbox/memory-limit.d.ts +44 -0
  295. package/dist/sandbox/memory-limit.js +193 -0
  296. package/dist/sandbox/memory-limit.js.map +1 -0
  297. package/dist/sandbox/plugin-client.d.ts +80 -0
  298. package/dist/sandbox/plugin-client.js +1601 -0
  299. package/dist/sandbox/plugin-client.js.map +1 -0
  300. package/dist/sandbox/plugin-host.d.ts +1 -0
  301. package/dist/sandbox/plugin-host.js +1001 -0
  302. package/dist/sandbox/plugin-host.js.map +1 -0
  303. package/dist/sandbox/protocol.d.ts +43 -0
  304. package/dist/sandbox/protocol.js +96 -0
  305. package/dist/sandbox/protocol.js.map +1 -0
  306. package/dist/sandbox/rpc-timeouts.d.ts +28 -0
  307. package/dist/sandbox/rpc-timeouts.js +15 -0
  308. package/dist/sandbox/rpc-timeouts.js.map +1 -0
  309. package/dist/sandbox/supervisor.d.ts +31 -0
  310. package/dist/sandbox/supervisor.js +114 -0
  311. package/dist/sandbox/supervisor.js.map +1 -0
  312. package/dist/sandbox/types.d.ts +124 -0
  313. package/dist/sandbox/types.js +17 -0
  314. package/dist/sandbox/types.js.map +1 -0
  315. package/dist/service/control.d.ts +32 -0
  316. package/dist/service/control.js +117 -0
  317. package/dist/service/control.js.map +1 -0
  318. package/dist/session/compress.d.ts +3 -0
  319. package/dist/session/compress.js +42 -0
  320. package/dist/session/compress.js.map +1 -0
  321. package/dist/session/index.d.ts +4 -0
  322. package/dist/session/index.js +8 -0
  323. package/dist/session/index.js.map +1 -0
  324. package/dist/session/registry.d.ts +4 -0
  325. package/dist/session/registry.js +12 -0
  326. package/dist/session/registry.js.map +1 -0
  327. package/dist/session/replay.d.ts +10 -0
  328. package/dist/session/replay.js +109 -0
  329. package/dist/session/replay.js.map +1 -0
  330. package/dist/session/session.d.ts +40 -0
  331. package/dist/session/session.js +108 -0
  332. package/dist/session/session.js.map +1 -0
  333. package/dist/session/store.d.ts +58 -0
  334. package/dist/session/store.js +382 -0
  335. package/dist/session/store.js.map +1 -0
  336. package/dist/session/types.d.ts +56 -0
  337. package/dist/session/types.js +3 -0
  338. package/dist/session/types.js.map +1 -0
  339. package/dist/workspace/index.d.ts +7 -0
  340. package/dist/workspace/index.js +17 -0
  341. package/dist/workspace/index.js.map +1 -0
  342. package/dist/workspace/registry.d.ts +25 -0
  343. package/dist/workspace/registry.js +293 -0
  344. package/dist/workspace/registry.js.map +1 -0
  345. package/package.json +73 -5
  346. package/scripts/build-node-pty.mjs +55 -0
  347. package/scripts/rewrite-dts.mjs +36 -0
  348. package/scripts/run-tests.mjs +41 -0
  349. package/scripts/verify-sandbox.ts +98 -0
  350. package/src/agent/pending-asks.ts +88 -0
  351. package/src/agent/task-registry.ts +108 -0
  352. package/src/agent/tool-context.ts +47 -0
  353. package/src/agent/usage.ts +170 -0
  354. package/src/api/host/agent.ts +162 -0
  355. package/src/api/host/events.ts +7 -0
  356. package/src/api/host/exec.ts +47 -0
  357. package/src/api/host/fs.ts +33 -0
  358. package/src/api/host/hooks.ts +635 -0
  359. package/src/api/host/host.ts +99 -0
  360. package/src/api/host/http.ts +79 -0
  361. package/src/api/host/index.ts +20 -0
  362. package/src/api/host/models.ts +81 -0
  363. package/src/api/host/network.ts +32 -0
  364. package/src/api/host/plugin.ts +158 -0
  365. package/src/api/host/plugins.ts +59 -0
  366. package/src/api/host/pty.ts +73 -0
  367. package/src/api/host/session.ts +61 -0
  368. package/src/api/host/version.ts +2 -0
  369. package/src/api/host/workspace.ts +51 -0
  370. package/src/api/plugin/http-client.ts +492 -0
  371. package/src/api/plugin/http-protocol.ts +231 -0
  372. package/src/api/result/result.ts +52 -0
  373. package/src/commands/agent-turn.ts +817 -0
  374. package/src/commands/ask-prompt.ts +52 -0
  375. package/src/commands/config.ts +306 -0
  376. package/src/commands/conversation.ts +777 -0
  377. package/src/commands/doctor.ts +92 -0
  378. package/src/commands/exec-approval.ts +91 -0
  379. package/src/commands/help.ts +41 -0
  380. package/src/commands/key.ts +281 -0
  381. package/src/commands/plugin.ts +222 -0
  382. package/src/commands/session.ts +179 -0
  383. package/src/commands/shared.ts +103 -0
  384. package/src/commands/start.ts +88 -0
  385. package/src/commands/stop.ts +24 -0
  386. package/src/commands/version.ts +40 -0
  387. package/src/config/codes.ts +17 -0
  388. package/src/config/credentials.ts +302 -0
  389. package/src/config/defaults.ts +32 -0
  390. package/src/config/index.ts +10 -0
  391. package/src/config/loader.ts +80 -0
  392. package/src/config/providers.ts +133 -0
  393. package/src/config/types.ts +88 -0
  394. package/src/config/validate.ts +458 -0
  395. package/src/display/markdown.ts +137 -0
  396. package/src/display/tool-label.ts +87 -0
  397. package/src/event/event.ts +171 -0
  398. package/src/index.ts +344 -0
  399. package/src/input/codes.ts +21 -0
  400. package/src/input/commands.ts +76 -0
  401. package/src/input/help.ts +60 -0
  402. package/src/input/index.ts +10 -0
  403. package/src/input/parse-options.ts +66 -0
  404. package/src/input/parse.ts +138 -0
  405. package/src/input/types.ts +19 -0
  406. package/src/logger/elog4js.ts +366 -0
  407. package/src/logger/logger.ts +251 -0
  408. package/src/network/index.ts +6 -0
  409. package/src/network/openai.ts +571 -0
  410. package/src/network/proxy.ts +80 -0
  411. package/src/network/web.ts +206 -0
  412. package/src/platform/clipboard.ts +107 -0
  413. package/src/platform/exec.ts +128 -0
  414. package/src/platform/file.ts +235 -0
  415. package/src/platform/index.ts +12 -0
  416. package/src/platform/notification.ts +108 -0
  417. package/src/platform/open-external.ts +132 -0
  418. package/src/platform/os.ts +101 -0
  419. package/src/platform/path.ts +214 -0
  420. package/src/plugin/access-guard.ts +46 -0
  421. package/src/plugin/agent-service.ts +969 -0
  422. package/src/plugin/agent-tools.ts +43 -0
  423. package/src/plugin/capabilities.ts +240 -0
  424. package/src/plugin/console-guard.ts +66 -0
  425. package/src/plugin/data-access.ts +70 -0
  426. package/src/plugin/fs.ts +109 -0
  427. package/src/plugin/host-alias.ts +61 -0
  428. package/src/plugin/index.ts +11 -0
  429. package/src/plugin/install.ts +213 -0
  430. package/src/plugin/loader.ts +952 -0
  431. package/src/plugin/manage.ts +338 -0
  432. package/src/plugin/models-service.ts +383 -0
  433. package/src/plugin/network-service.ts +102 -0
  434. package/src/plugin/package-manager.ts +214 -0
  435. package/src/plugin/paths.ts +19 -0
  436. package/src/plugin/registry.ts +103 -0
  437. package/src/plugin/runtime.ts +57 -0
  438. package/src/plugin/scaffold.ts +158 -0
  439. package/src/plugin/state.ts +52 -0
  440. package/src/plugin/zip.ts +156 -0
  441. package/src/sandbox/audit.ts +38 -0
  442. package/src/sandbox/backends/bwrap.ts +251 -0
  443. package/src/sandbox/backends/direct.ts +43 -0
  444. package/src/sandbox/bridge-state.ts +29 -0
  445. package/src/sandbox/index.ts +5 -0
  446. package/src/sandbox/manager.ts +406 -0
  447. package/src/sandbox/memory-limit.ts +245 -0
  448. package/src/sandbox/plugin-client.ts +1881 -0
  449. package/src/sandbox/plugin-host.ts +1282 -0
  450. package/src/sandbox/protocol.ts +142 -0
  451. package/src/sandbox/pty-bridge.py +154 -0
  452. package/src/sandbox/rpc-timeouts.ts +43 -0
  453. package/src/sandbox/supervisor.ts +172 -0
  454. package/src/sandbox/types.ts +149 -0
  455. package/src/service/control.ts +155 -0
  456. package/src/session/compress.ts +47 -0
  457. package/src/session/index.ts +8 -0
  458. package/src/session/registry.ts +17 -0
  459. package/src/session/replay.ts +132 -0
  460. package/src/session/session.ts +134 -0
  461. package/src/session/store.ts +468 -0
  462. package/src/session/types.ts +61 -0
  463. package/src/workspace/index.ts +21 -0
  464. package/src/workspace/registry.ts +356 -0
@@ -0,0 +1,458 @@
1
+ import type { LevelWithSilent } from "pino"
2
+
3
+ import { Result } from "../api/result/result.ts"
4
+ import { CONFIG_CODES, invalidCode } from "./codes.ts"
5
+ import { DEFAULT_MODELS, DEFAULT_SETTINGS } from "./defaults.ts"
6
+ import {
7
+ REASONING_LEVELS,
8
+ SETTING_VERSION,
9
+ type AppSetting,
10
+ type ModelProviderSetting,
11
+ type ModelsSetting,
12
+ type NetworkSetting,
13
+ type OpenAISetting,
14
+ type ProxySetting,
15
+ type SandboxAccess,
16
+ type SandboxBackend,
17
+ type SandboxMode,
18
+ type SandboxSetting,
19
+ } from "./types.ts"
20
+
21
+ const LEVELS: ReadonlySet<string> = new Set<LevelWithSilent>([
22
+ "fatal",
23
+ "error",
24
+ "warn",
25
+ "info",
26
+ "debug",
27
+ "trace",
28
+ "silent",
29
+ ])
30
+
31
+ function isPlainObject(value: unknown): value is Record<string, unknown> {
32
+ return typeof value === "object" && value !== null && !Array.isArray(value)
33
+ }
34
+
35
+ function isLevel(value: unknown): value is LevelWithSilent {
36
+ return typeof value === "string" && LEVELS.has(value)
37
+ }
38
+
39
+ function isBaseUrl(value: unknown): value is string {
40
+ if (typeof value !== "string" || value.trim() === "") {
41
+ return false
42
+ }
43
+ try {
44
+ const url = new URL(value)
45
+ return url.protocol === "http:" || url.protocol === "https:"
46
+ } catch {
47
+ return false
48
+ }
49
+ }
50
+
51
+ function isPositiveInteger(value: unknown): value is number {
52
+ return typeof value === "number" && Number.isInteger(value) && value > 0
53
+ }
54
+
55
+ function isNonNegativeInteger(value: unknown): value is number {
56
+ return typeof value === "number" && Number.isInteger(value) && value >= 0
57
+ }
58
+
59
+ function validateOpenAI(input: Record<string, unknown>): Result<OpenAISetting> {
60
+ let baseUrl = DEFAULT_SETTINGS.openai.baseUrl
61
+ const rawBaseUrl = input["baseUrl"]
62
+ if (rawBaseUrl !== undefined && rawBaseUrl !== null) {
63
+ if (!isBaseUrl(rawBaseUrl)) {
64
+ return Result.error<OpenAISetting>(invalidCode("openai.baseUrl"))
65
+ }
66
+ baseUrl = rawBaseUrl
67
+ }
68
+
69
+ let model = DEFAULT_SETTINGS.openai.model
70
+ const rawModel = input["model"]
71
+ if (rawModel !== undefined) {
72
+ if (typeof rawModel !== "string" || rawModel.trim() === "") {
73
+ return Result.error<OpenAISetting>(invalidCode("openai.model"))
74
+ }
75
+ model = rawModel.trim()
76
+ }
77
+
78
+ let timeoutMs = DEFAULT_SETTINGS.openai.timeoutMs
79
+ const rawTimeout = input["timeoutMs"]
80
+ if (rawTimeout !== undefined) {
81
+ if (!isPositiveInteger(rawTimeout)) {
82
+ return Result.error<OpenAISetting>(invalidCode("openai.timeoutMs"))
83
+ }
84
+ timeoutMs = rawTimeout
85
+ }
86
+
87
+ let maxRetries = DEFAULT_SETTINGS.openai.maxRetries
88
+ const rawRetries = input["maxRetries"]
89
+ if (rawRetries !== undefined) {
90
+ if (!isNonNegativeInteger(rawRetries)) {
91
+ return Result.error<OpenAISetting>(invalidCode("openai.maxRetries"))
92
+ }
93
+ maxRetries = rawRetries
94
+ }
95
+
96
+ return Result.successWithData<OpenAISetting>({ baseUrl, model, timeoutMs, maxRetries })
97
+ }
98
+
99
+ const PROVIDER_ID = /^[a-z0-9][a-z0-9._-]{0,31}$/
100
+ const ENV_NAME = /^[A-Za-z_][A-Za-z0-9_]*$/
101
+ const BUILTIN_PROVIDER_ID = "openai"
102
+ const MAX_PROVIDERS = 20
103
+ const MAX_MODELS_PER_PROVIDER = 200
104
+
105
+ function validateProvider(
106
+ input: Record<string, unknown>,
107
+ seen: ReadonlySet<string>,
108
+ ): Result<ModelProviderSetting> {
109
+ const rawId = input["id"]
110
+ const id = typeof rawId === "string" && PROVIDER_ID.test(rawId.trim()) ? rawId.trim() : null
111
+ if (id === null || id === BUILTIN_PROVIDER_ID) {
112
+ return Result.error<ModelProviderSetting>(invalidCode("models.providers.id"))
113
+ }
114
+ if (seen.has(id)) {
115
+ return Result.error<ModelProviderSetting>(invalidCode(`models.providers.${id}`))
116
+ }
117
+ const rawName = input["name"]
118
+ const name =
119
+ typeof rawName === "string" && rawName.trim() !== "" && rawName.trim().length <= 60
120
+ ? rawName.trim()
121
+ : null
122
+ if (name === null) {
123
+ return Result.error<ModelProviderSetting>(invalidCode(`models.providers.${id}.name`))
124
+ }
125
+ let baseUrl: string | null = null
126
+ const rawBaseUrl = input["baseUrl"]
127
+ if (rawBaseUrl !== undefined && rawBaseUrl !== null) {
128
+ if (!isBaseUrl(rawBaseUrl)) {
129
+ return Result.error<ModelProviderSetting>(invalidCode(`models.providers.${id}.baseUrl`))
130
+ }
131
+ baseUrl = rawBaseUrl.trim()
132
+ }
133
+ const models: string[] = []
134
+ const rawModels = input["models"]
135
+ if (rawModels !== undefined) {
136
+ if (!Array.isArray(rawModels) || rawModels.length > MAX_MODELS_PER_PROVIDER) {
137
+ return Result.error<ModelProviderSetting>(invalidCode(`models.providers.${id}.models`))
138
+ }
139
+ for (const item of rawModels) {
140
+ if (typeof item !== "string" || item.trim() === "" || item.trim().length > 120) {
141
+ return Result.error<ModelProviderSetting>(
142
+ invalidCode(`models.providers.${id}.models`),
143
+ )
144
+ }
145
+ const model = item.trim()
146
+ if (!models.includes(model)) {
147
+ models.push(model)
148
+ }
149
+ }
150
+ }
151
+ let defaultModel: string | null = null
152
+ const rawDefault = input["defaultModel"]
153
+ if (rawDefault !== undefined && rawDefault !== null) {
154
+ if (typeof rawDefault !== "string" || rawDefault.trim() === "") {
155
+ return Result.error<ModelProviderSetting>(
156
+ invalidCode(`models.providers.${id}.defaultModel`),
157
+ )
158
+ }
159
+ defaultModel = rawDefault.trim()
160
+ }
161
+ let apiKeyEnv: string | null = null
162
+ const rawEnv = input["apiKeyEnv"]
163
+ if (rawEnv !== undefined && rawEnv !== null) {
164
+ if (typeof rawEnv !== "string" || !ENV_NAME.test(rawEnv.trim())) {
165
+ return Result.error<ModelProviderSetting>(
166
+ invalidCode(`models.providers.${id}.apiKeyEnv`),
167
+ )
168
+ }
169
+ apiKeyEnv = rawEnv.trim()
170
+ }
171
+ return Result.successWithData<ModelProviderSetting>({
172
+ id,
173
+ name,
174
+ baseUrl,
175
+ models,
176
+ defaultModel,
177
+ apiKeyEnv,
178
+ })
179
+ }
180
+
181
+ function validateModels(input: Record<string, unknown>): Result<ModelsSetting> {
182
+ const providers: ModelProviderSetting[] = []
183
+ const rawProviders = input["providers"]
184
+ if (rawProviders !== undefined && rawProviders !== null) {
185
+ if (!Array.isArray(rawProviders) || rawProviders.length > MAX_PROVIDERS) {
186
+ return Result.error<ModelsSetting>(invalidCode("models.providers"))
187
+ }
188
+ const seen = new Set<string>()
189
+ for (const item of rawProviders) {
190
+ if (!isPlainObject(item)) {
191
+ return Result.error<ModelsSetting>(invalidCode("models.providers"))
192
+ }
193
+ const validated = validateProvider(item, seen)
194
+ if (!validated.isSuccess()) {
195
+ return Result.error<ModelsSetting>(validated.getCode() ?? CONFIG_CODES.invalid)
196
+ }
197
+ const provider = validated.getData()
198
+ if (provider === null) {
199
+ return Result.error<ModelsSetting>(invalidCode("models.providers"))
200
+ }
201
+ seen.add(provider.id)
202
+ providers.push(provider)
203
+ }
204
+ }
205
+
206
+ let activeProvider = DEFAULT_MODELS.activeProvider
207
+ const rawActiveProvider = input["activeProvider"]
208
+ if (rawActiveProvider !== undefined && rawActiveProvider !== null) {
209
+ if (typeof rawActiveProvider !== "string" || rawActiveProvider.trim() === "") {
210
+ return Result.error<ModelsSetting>(invalidCode("models.activeProvider"))
211
+ }
212
+ activeProvider = rawActiveProvider.trim()
213
+ }
214
+ const known =
215
+ activeProvider === BUILTIN_PROVIDER_ID || providers.some((p) => p.id === activeProvider)
216
+ if (!known) {
217
+ return Result.error<ModelsSetting>(invalidCode("models.activeProvider"))
218
+ }
219
+
220
+ let activeModel: string | null = DEFAULT_MODELS.activeModel
221
+ const rawActiveModel = input["activeModel"]
222
+ if (rawActiveModel !== undefined && rawActiveModel !== null) {
223
+ if (typeof rawActiveModel !== "string" || rawActiveModel.trim() === "") {
224
+ return Result.error<ModelsSetting>(invalidCode("models.activeModel"))
225
+ }
226
+ activeModel = rawActiveModel.trim()
227
+ }
228
+
229
+ let activeReasoning = DEFAULT_MODELS.activeReasoning
230
+ const rawActiveReasoning = input["activeReasoning"]
231
+ if (rawActiveReasoning !== undefined && rawActiveReasoning !== null) {
232
+ if (
233
+ typeof rawActiveReasoning !== "string" ||
234
+ !(REASONING_LEVELS as readonly string[]).includes(rawActiveReasoning)
235
+ ) {
236
+ return Result.error<ModelsSetting>(invalidCode("models.activeReasoning"))
237
+ }
238
+ activeReasoning = rawActiveReasoning as ModelsSetting["activeReasoning"]
239
+ }
240
+
241
+ return Result.successWithData<ModelsSetting>({
242
+ providers,
243
+ activeProvider,
244
+ activeModel,
245
+ activeReasoning,
246
+ })
247
+ }
248
+
249
+ function validateProxy(input: Record<string, unknown>): Result<ProxySetting> {
250
+ const rawType = input["type"]
251
+ if (rawType !== undefined && rawType !== "http") {
252
+ return Result.error<ProxySetting>(invalidCode("network.proxy.type"))
253
+ }
254
+
255
+ const rawUrl = input["url"]
256
+ if (!isBaseUrl(rawUrl)) {
257
+ return Result.error<ProxySetting>(invalidCode("network.proxy.url"))
258
+ }
259
+
260
+ const noProxy: string[] = []
261
+ const rawNoProxy = input["noProxy"]
262
+ if (rawNoProxy !== undefined) {
263
+ if (!Array.isArray(rawNoProxy)) {
264
+ return Result.error<ProxySetting>(invalidCode("network.proxy.noProxy"))
265
+ }
266
+ for (const item of rawNoProxy) {
267
+ if (typeof item !== "string" || item.trim() === "") {
268
+ return Result.error<ProxySetting>(invalidCode("network.proxy.noProxy"))
269
+ }
270
+ noProxy.push(item.trim())
271
+ }
272
+ }
273
+
274
+ return Result.successWithData<ProxySetting>({ type: "http", url: rawUrl, noProxy })
275
+ }
276
+
277
+ function validateNetwork(input: Record<string, unknown>): Result<NetworkSetting> {
278
+ const rawProxy = input["proxy"]
279
+ if (rawProxy === undefined || rawProxy === null) {
280
+ return Result.successWithData<NetworkSetting>({ proxy: null })
281
+ }
282
+ if (!isPlainObject(rawProxy)) {
283
+ return Result.error<NetworkSetting>(invalidCode("network.proxy"))
284
+ }
285
+ const validated = validateProxy(rawProxy)
286
+ if (!validated.isSuccess()) {
287
+ return Result.error<NetworkSetting>(validated.getCode() ?? CONFIG_CODES.invalid)
288
+ }
289
+ return Result.successWithData<NetworkSetting>({
290
+ proxy: validated.getData() ?? null,
291
+ })
292
+ }
293
+
294
+ const SANDBOX_MODES: ReadonlySet<string> = new Set<SandboxMode>(["off", "strict", "best-effort"])
295
+ const SANDBOX_BACKENDS: ReadonlySet<string> = new Set<SandboxBackend>(["auto", "bwrap", "nsjail"])
296
+ const SANDBOX_ACCESS: ReadonlySet<string> = new Set<SandboxAccess>([
297
+ "read-only",
298
+ "workspace-write",
299
+ "danger-full-access",
300
+ ])
301
+
302
+ function validateSandbox(input: Record<string, unknown>): Result<SandboxSetting> {
303
+ let mode = DEFAULT_SETTINGS.sandbox.mode
304
+ const rawMode = input["mode"]
305
+ if (rawMode !== undefined) {
306
+ if (typeof rawMode !== "string" || !SANDBOX_MODES.has(rawMode)) {
307
+ return Result.error<SandboxSetting>(invalidCode("sandbox.mode"))
308
+ }
309
+ mode = rawMode as SandboxMode
310
+ }
311
+
312
+ let backend = DEFAULT_SETTINGS.sandbox.backend
313
+ const rawBackend = input["backend"]
314
+ if (rawBackend !== undefined) {
315
+ if (typeof rawBackend !== "string" || !SANDBOX_BACKENDS.has(rawBackend)) {
316
+ return Result.error<SandboxSetting>(invalidCode("sandbox.backend"))
317
+ }
318
+ backend = rawBackend as SandboxBackend
319
+ }
320
+
321
+ let access = DEFAULT_SETTINGS.sandbox.access
322
+ const rawAccess = input["access"]
323
+ if (rawAccess !== undefined) {
324
+ if (typeof rawAccess !== "string" || !SANDBOX_ACCESS.has(rawAccess)) {
325
+ return Result.error<SandboxSetting>(invalidCode("sandbox.access"))
326
+ }
327
+ access = rawAccess as SandboxAccess
328
+ }
329
+
330
+ let maxConcurrent = DEFAULT_SETTINGS.sandbox.maxConcurrent
331
+ const rawMaxConcurrent = input["maxConcurrent"]
332
+ if (rawMaxConcurrent !== undefined) {
333
+ if (!isPositiveInteger(rawMaxConcurrent) || rawMaxConcurrent > 32) {
334
+ return Result.error<SandboxSetting>(invalidCode("sandbox.maxConcurrent"))
335
+ }
336
+ maxConcurrent = rawMaxConcurrent
337
+ }
338
+
339
+ let defaultTimeoutMs = DEFAULT_SETTINGS.sandbox.defaultTimeoutMs
340
+ const rawTimeout = input["defaultTimeoutMs"]
341
+ if (rawTimeout !== undefined) {
342
+ if (!isPositiveInteger(rawTimeout) || rawTimeout > 600_000) {
343
+ return Result.error<SandboxSetting>(invalidCode("sandbox.defaultTimeoutMs"))
344
+ }
345
+ defaultTimeoutMs = rawTimeout
346
+ }
347
+
348
+ let defaultMemoryMB = DEFAULT_SETTINGS.sandbox.defaultMemoryMB
349
+ const rawMemory = input["defaultMemoryMB"]
350
+ if (rawMemory !== undefined) {
351
+ if (!isPositiveInteger(rawMemory) || rawMemory > 8192) {
352
+ return Result.error<SandboxSetting>(invalidCode("sandbox.defaultMemoryMB"))
353
+ }
354
+ defaultMemoryMB = rawMemory
355
+ }
356
+
357
+ let outputLimitBytes = DEFAULT_SETTINGS.sandbox.outputLimitBytes
358
+ const rawOutput = input["outputLimitBytes"]
359
+ if (rawOutput !== undefined) {
360
+ if (!isPositiveInteger(rawOutput) || rawOutput < 1024 || rawOutput > 64 * 1024 * 1024) {
361
+ return Result.error<SandboxSetting>(invalidCode("sandbox.outputLimitBytes"))
362
+ }
363
+ outputLimitBytes = rawOutput
364
+ }
365
+
366
+ return Result.successWithData<SandboxSetting>({
367
+ mode,
368
+ backend,
369
+ access,
370
+ maxConcurrent,
371
+ defaultTimeoutMs,
372
+ defaultMemoryMB,
373
+ outputLimitBytes,
374
+ })
375
+ }
376
+
377
+ /** 校验并补齐默认值;未声明字段忽略,不做保留 */
378
+ export function validateSettings(input: unknown): Result<AppSetting> {
379
+ if (!isPlainObject(input)) {
380
+ return Result.error<AppSetting>(invalidCode("root"))
381
+ }
382
+
383
+ const version = input["version"]
384
+ if (version !== undefined && version !== SETTING_VERSION) {
385
+ return Result.error<AppSetting>(`${CONFIG_CODES.versionUnsupported}:${String(version)}`)
386
+ }
387
+
388
+ const logger = input["logger"]
389
+ if (logger !== undefined && !isPlainObject(logger)) {
390
+ return Result.error<AppSetting>(invalidCode("logger"))
391
+ }
392
+
393
+ const rawLevel = logger === undefined ? undefined : logger["level"]
394
+ if (rawLevel !== undefined && !isLevel(rawLevel)) {
395
+ return Result.error<AppSetting>(invalidCode("logger.level"))
396
+ }
397
+
398
+ const openai = input["openai"]
399
+ if (openai !== undefined && !isPlainObject(openai)) {
400
+ return Result.error<AppSetting>(invalidCode("openai"))
401
+ }
402
+ let openaiSetting = DEFAULT_SETTINGS.openai
403
+ if (openai !== undefined) {
404
+ const validated = validateOpenAI(openai)
405
+ if (!validated.isSuccess()) {
406
+ return Result.error<AppSetting>(validated.getCode() ?? CONFIG_CODES.invalid)
407
+ }
408
+ openaiSetting = validated.getData() ?? DEFAULT_SETTINGS.openai
409
+ }
410
+
411
+ const models = input["models"]
412
+ if (models !== undefined && models !== null && !isPlainObject(models)) {
413
+ return Result.error<AppSetting>(invalidCode("models"))
414
+ }
415
+ let modelsSetting = DEFAULT_MODELS
416
+ if (models !== undefined && models !== null) {
417
+ const validated = validateModels(models)
418
+ if (!validated.isSuccess()) {
419
+ return Result.error<AppSetting>(validated.getCode() ?? CONFIG_CODES.invalid)
420
+ }
421
+ modelsSetting = validated.getData() ?? DEFAULT_MODELS
422
+ }
423
+
424
+ const network = input["network"]
425
+ if (network !== undefined && !isPlainObject(network)) {
426
+ return Result.error<AppSetting>(invalidCode("network"))
427
+ }
428
+ let networkSetting = DEFAULT_SETTINGS.network
429
+ if (network !== undefined) {
430
+ const validated = validateNetwork(network)
431
+ if (!validated.isSuccess()) {
432
+ return Result.error<AppSetting>(validated.getCode() ?? CONFIG_CODES.invalid)
433
+ }
434
+ networkSetting = validated.getData() ?? DEFAULT_SETTINGS.network
435
+ }
436
+
437
+ const sandbox = input["sandbox"]
438
+ if (sandbox !== undefined && !isPlainObject(sandbox)) {
439
+ return Result.error<AppSetting>(invalidCode("sandbox"))
440
+ }
441
+ let sandboxSetting = DEFAULT_SETTINGS.sandbox
442
+ if (sandbox !== undefined) {
443
+ const validated = validateSandbox(sandbox)
444
+ if (!validated.isSuccess()) {
445
+ return Result.error<AppSetting>(validated.getCode() ?? CONFIG_CODES.invalid)
446
+ }
447
+ sandboxSetting = validated.getData() ?? DEFAULT_SETTINGS.sandbox
448
+ }
449
+
450
+ return Result.successWithData<AppSetting>({
451
+ version: SETTING_VERSION,
452
+ logger: { level: rawLevel ?? DEFAULT_SETTINGS.logger.level },
453
+ openai: openaiSetting,
454
+ models: modelsSetting,
455
+ network: networkSetting,
456
+ sandbox: sandboxSetting,
457
+ })
458
+ }
@@ -0,0 +1,137 @@
1
+ // TTY Markdown 渲染
2
+
3
+ import { highlight, supportsLanguage } from "cli-highlight"
4
+ import { createMarkdownStreamer, render } from "markdansi"
5
+
6
+ export interface MarkdownStream {
7
+ push(text: string): string
8
+ finish(): string
9
+ }
10
+
11
+ // 终端宽度
12
+ function terminalWidth(): number {
13
+ return Math.max(40, (process.stdout.columns ?? 80) - 2)
14
+ }
15
+
16
+ // 语言别名
17
+ const LANGUAGE_ALIASES: Readonly<Record<string, string>> = {
18
+ "c++": "cpp",
19
+ asm: "x86asm",
20
+ proto: "protobuf",
21
+ shell: "bash",
22
+ sh: "bash",
23
+ vimscript: "vim",
24
+ }
25
+
26
+ // 代码块高亮
27
+ function highlightCode(code: string, language?: string): string {
28
+ if (!markdownColor() || language === undefined) {
29
+ return code
30
+ }
31
+ const resolved = LANGUAGE_ALIASES[language.toLowerCase()] ?? language
32
+ if (!supportsLanguage(resolved)) {
33
+ return code
34
+ }
35
+ try {
36
+ return highlight(code, { language: resolved, ignoreIllegals: true })
37
+ } catch {
38
+ return code
39
+ }
40
+ }
41
+
42
+ // 单行代码块补一行,markdansi 才会画框
43
+ function ensureCodeBox(block: string): string {
44
+ const match = /^(`{3,}|~{3,})([^\n]*)\n([\s\S]*?)\n\1$/.exec(block)
45
+ if (match === null) {
46
+ return block
47
+ }
48
+ const [, fence, info, content] = match
49
+ if (content === undefined || content.includes("\n")) {
50
+ return block
51
+ }
52
+ return `${fence}${info ?? ""}\n${content}\n\u200B\n${fence}`
53
+ }
54
+
55
+ // 去掉单行代码框的占位行
56
+ function stripCodePad(output: string): string {
57
+ return output.replace(/^.*\u200B.*\n/m, "")
58
+ }
59
+
60
+ // 代码块外的 Markdown 预处理
61
+ function prepareMarkdown(markdown: string): string {
62
+ const parts = markdown.split(/(```[\s\S]*?```|~~~[\s\S]*?~~~)/g)
63
+ return parts
64
+ .map((part, index) => {
65
+ if (index % 2 === 1) {
66
+ return ensureCodeBox(part)
67
+ }
68
+ return part
69
+ .replace(
70
+ /!\[([^\]]*)\]\(([^)\s]+)(?:\s+"[^"]*")?\)/g,
71
+ (_match, alt: string, url: string) =>
72
+ alt === "" ? `图片(${url})` : `图片:${alt}(${url})`,
73
+ )
74
+ .replace(/<summary>([\s\S]*?)<\/summary>/gi, "▶ $1")
75
+ .replace(/<\/?details[^>]*>/gi, "")
76
+ .replace(/^(\s*[-*+]\s+)\[x\]\s*/gim, "$1☑ ")
77
+ .replace(/^(\s*[-*+]\s+)\[ \]\s*/gim, "$1☐ ")
78
+ })
79
+ .join("")
80
+ }
81
+
82
+ // 是否启用颜色
83
+ export function markdownColor(): boolean {
84
+ return (
85
+ process.stdout.isTTY === true &&
86
+ process.env["NO_COLOR"] === undefined &&
87
+ process.env["TERM"] !== "dumb"
88
+ )
89
+ }
90
+
91
+ // 渲染完整 Markdown
92
+ export function renderMarkdown(markdown: string): string {
93
+ try {
94
+ return stripCodePad(
95
+ render(prepareMarkdown(markdown), {
96
+ width: terminalWidth(),
97
+ color: markdownColor(),
98
+ hyperlinks: markdownColor(),
99
+ highlighter: highlightCode,
100
+ }),
101
+ )
102
+ } catch {
103
+ return markdown
104
+ }
105
+ }
106
+
107
+ // 流式渲染 Markdown
108
+ export function createMarkdownStream(): MarkdownStream {
109
+ const streamer = createMarkdownStreamer({
110
+ render: (markdown) =>
111
+ stripCodePad(
112
+ render(prepareMarkdown(markdown), {
113
+ width: terminalWidth(),
114
+ color: markdownColor(),
115
+ hyperlinks: markdownColor(),
116
+ highlighter: highlightCode,
117
+ }),
118
+ ),
119
+ spacing: "single",
120
+ })
121
+ return {
122
+ push(text) {
123
+ try {
124
+ return streamer.push(text)
125
+ } catch {
126
+ return text
127
+ }
128
+ },
129
+ finish() {
130
+ try {
131
+ return streamer.finish()
132
+ } catch {
133
+ return ""
134
+ }
135
+ },
136
+ }
137
+ }
@@ -0,0 +1,87 @@
1
+ /*
2
+ * 工具调用的终端展示:中文动作名 + 一行参数摘要。
3
+ * 与网页里的工具行保持一致(执行命令 / 读取文件 / 搜索网页 …)。
4
+ */
5
+
6
+ const TOOL_LABELS: Readonly<Record<string, string>> = {
7
+ execute_code: "执行命令",
8
+ read: "读取文件",
9
+ delete_file: "移入回收站",
10
+ web_search: "搜索网页",
11
+ web_fetch: "抓取网页",
12
+ query_task: "查询任务",
13
+ stop_task: "停止任务",
14
+ ask: "询问用户",
15
+ skill_list: "列出技能",
16
+ skill_read: "读取技能",
17
+ skill_install: "安装技能",
18
+ skill_modify: "修改技能",
19
+ }
20
+
21
+ const SUMMARY_LIMIT = 160
22
+
23
+ function isRecord(value: unknown): value is Record<string, unknown> {
24
+ return typeof value === "object" && value !== null && !Array.isArray(value)
25
+ }
26
+
27
+ function text(value: unknown): string {
28
+ return typeof value === "string" ? value.trim() : ""
29
+ }
30
+
31
+ function truncate(value: string): string {
32
+ const line = value.split(/\r?\n/)[0]?.trim() ?? ""
33
+ return line.length > SUMMARY_LIMIT ? `${line.slice(0, SUMMARY_LIMIT)}…` : line
34
+ }
35
+
36
+ /** 工具名 → 中文动作名;未知工具直接显示原名 */
37
+ export function toolLabel(name: string): string {
38
+ return TOOL_LABELS[name] ?? name
39
+ }
40
+
41
+ /** 工具参数 → 一行摘要(命令、路径、搜索词等) */
42
+ export function toolSummary(name: string, args: Record<string, unknown>): string {
43
+ if (name === "execute_code") {
44
+ const command = text(args["command"])
45
+ const rawArgv = args["argv"]
46
+ const argv = Array.isArray(rawArgv)
47
+ ? rawArgv.filter((item): item is string => typeof item === "string")
48
+ : []
49
+ return truncate([command, ...argv].filter((item) => item !== "").join(" "))
50
+ }
51
+ if (name === "delete_file") {
52
+ return text(args["path"])
53
+ }
54
+ if (name === "read") {
55
+ return truncate(text(args["file_path"]))
56
+ }
57
+ if (name === "web_search") {
58
+ return truncate(text(args["query"]))
59
+ }
60
+ if (name === "web_fetch") {
61
+ return truncate(text(args["url"]))
62
+ }
63
+ if (name === "query_task") {
64
+ return truncate(text(args["task_id"]))
65
+ }
66
+ if (name === "stop_task") {
67
+ return truncate([text(args["task_id"]), text(args["type"])].filter(Boolean).join(" "))
68
+ }
69
+ if (name === "ask") {
70
+ return truncate(text(args["question"]))
71
+ }
72
+ if (name === "skill_read" || name === "skill_install" || name === "skill_modify") {
73
+ return truncate(text(args["name"]) || text(args["path"]))
74
+ }
75
+ const first = Object.values(args).find((value): value is string => typeof value === "string")
76
+ return truncate(first ?? "")
77
+ }
78
+
79
+ /** 参数是 JSON 字符串时先解析,失败按空对象处理 */
80
+ export function toolArgs(raw: string): Record<string, unknown> {
81
+ try {
82
+ const value: unknown = JSON.parse(raw)
83
+ return isRecord(value) ? value : {}
84
+ } catch {
85
+ return {}
86
+ }
87
+ }