@armadra/agent 0.0.0-stage → 0.3.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 (433) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/LICENSE +21 -0
  3. package/README.md +470 -2
  4. package/dist/agent/agent.d.ts +56 -0
  5. package/dist/agent/agent.js +129 -0
  6. package/dist/agent/loop.d.ts +51 -0
  7. package/dist/agent/loop.js +158 -0
  8. package/dist/agent/queue.d.ts +25 -0
  9. package/dist/agent/queue.js +55 -0
  10. package/dist/agent/retry.d.ts +22 -0
  11. package/dist/agent/retry.js +92 -0
  12. package/dist/agent/schema.d.ts +27 -0
  13. package/dist/agent/schema.js +186 -0
  14. package/dist/agent/session-cache.d.ts +100 -0
  15. package/dist/agent/session-cache.js +514 -0
  16. package/dist/agent/session-compaction.d.ts +49 -0
  17. package/dist/agent/session-compaction.js +251 -0
  18. package/dist/agent/session-core.d.ts +103 -0
  19. package/dist/agent/session-core.js +7 -0
  20. package/dist/agent/session-run.d.ts +55 -0
  21. package/dist/agent/session-run.js +246 -0
  22. package/dist/agent/session-state.d.ts +39 -0
  23. package/dist/agent/session-state.js +119 -0
  24. package/dist/agent/session-subagent.d.ts +54 -0
  25. package/dist/agent/session-subagent.js +157 -0
  26. package/dist/agent/session-sync.d.ts +20 -0
  27. package/dist/agent/session-sync.js +111 -0
  28. package/dist/agent/session-tools.d.ts +24 -0
  29. package/dist/agent/session-tools.js +277 -0
  30. package/dist/agent/session.d.ts +117 -0
  31. package/dist/agent/session.js +485 -0
  32. package/dist/agent/system-prompt.d.ts +49 -0
  33. package/dist/agent/system-prompt.js +121 -0
  34. package/dist/agent/tool-runner.d.ts +64 -0
  35. package/dist/agent/tool-runner.js +309 -0
  36. package/dist/agent/transform.d.ts +26 -0
  37. package/dist/agent/transform.js +171 -0
  38. package/dist/agent/types.d.ts +378 -0
  39. package/dist/agent/types.js +21 -0
  40. package/dist/ai/apis/anthropic-messages.d.ts +26 -0
  41. package/dist/ai/apis/anthropic-messages.js +214 -0
  42. package/dist/ai/apis/anthropic-request.d.ts +42 -0
  43. package/dist/ai/apis/anthropic-request.js +282 -0
  44. package/dist/ai/apis/api.d.ts +28 -0
  45. package/dist/ai/apis/api.js +102 -0
  46. package/dist/ai/apis/cache-params.d.ts +48 -0
  47. package/dist/ai/apis/cache-params.js +163 -0
  48. package/dist/ai/apis/google-generative-ai.d.ts +32 -0
  49. package/dist/ai/apis/google-generative-ai.js +249 -0
  50. package/dist/ai/apis/google-request.d.ts +40 -0
  51. package/dist/ai/apis/google-request.js +255 -0
  52. package/dist/ai/apis/openai-compat.d.ts +35 -0
  53. package/dist/ai/apis/openai-compat.js +142 -0
  54. package/dist/ai/apis/openai-completions.d.ts +28 -0
  55. package/dist/ai/apis/openai-completions.js +246 -0
  56. package/dist/ai/apis/openai-request.d.ts +35 -0
  57. package/dist/ai/apis/openai-request.js +303 -0
  58. package/dist/ai/apis/openai-responses-request.d.ts +52 -0
  59. package/dist/ai/apis/openai-responses-request.js +266 -0
  60. package/dist/ai/apis/openai-responses.d.ts +28 -0
  61. package/dist/ai/apis/openai-responses.js +304 -0
  62. package/dist/ai/apis/shared.d.ts +56 -0
  63. package/dist/ai/apis/shared.js +179 -0
  64. package/dist/ai/cache/economics.d.ts +24 -0
  65. package/dist/ai/cache/economics.js +69 -0
  66. package/dist/ai/cache/fingerprint.d.ts +17 -0
  67. package/dist/ai/cache/fingerprint.js +39 -0
  68. package/dist/ai/cache/miss.d.ts +49 -0
  69. package/dist/ai/cache/miss.js +96 -0
  70. package/dist/ai/cache/reporting.d.ts +39 -0
  71. package/dist/ai/cache/reporting.js +103 -0
  72. package/dist/ai/cache/types.d.ts +94 -0
  73. package/dist/ai/cache/types.js +19 -0
  74. package/dist/ai/cache/warmer.d.ts +94 -0
  75. package/dist/ai/cache/warmer.js +224 -0
  76. package/dist/ai/context.d.ts +35 -0
  77. package/dist/ai/context.js +114 -0
  78. package/dist/ai/cost.d.ts +21 -0
  79. package/dist/ai/cost.js +60 -0
  80. package/dist/ai/event-stream.d.ts +33 -0
  81. package/dist/ai/event-stream.js +101 -0
  82. package/dist/ai/fake/fake-provider.d.ts +68 -0
  83. package/dist/ai/fake/fake-provider.js +278 -0
  84. package/dist/ai/fake/fake-script.d.ts +69 -0
  85. package/dist/ai/fake/fake-script.js +142 -0
  86. package/dist/ai/http.d.ts +54 -0
  87. package/dist/ai/http.js +212 -0
  88. package/dist/ai/json-partial.d.ts +17 -0
  89. package/dist/ai/json-partial.js +243 -0
  90. package/dist/ai/overflow.d.ts +22 -0
  91. package/dist/ai/overflow.js +61 -0
  92. package/dist/ai/providers/auth.d.ts +71 -0
  93. package/dist/ai/providers/auth.js +202 -0
  94. package/dist/ai/providers/builtin.d.ts +23 -0
  95. package/dist/ai/providers/builtin.js +148 -0
  96. package/dist/ai/providers/catalog-data.d.ts +5 -0
  97. package/dist/ai/providers/catalog-data.js +19 -0
  98. package/dist/ai/providers/catalog.d.ts +28 -0
  99. package/dist/ai/providers/catalog.js +166 -0
  100. package/dist/ai/providers/channels.d.ts +51 -0
  101. package/dist/ai/providers/channels.js +96 -0
  102. package/dist/ai/providers/enrich.d.ts +34 -0
  103. package/dist/ai/providers/enrich.js +86 -0
  104. package/dist/ai/providers/models-dev-cache.d.ts +53 -0
  105. package/dist/ai/providers/models-dev-cache.js +147 -0
  106. package/dist/ai/providers/models-dev.d.ts +99 -0
  107. package/dist/ai/providers/models-dev.js +315 -0
  108. package/dist/ai/providers/registry.d.ts +114 -0
  109. package/dist/ai/providers/registry.js +458 -0
  110. package/dist/ai/sse.d.ts +32 -0
  111. package/dist/ai/sse.js +140 -0
  112. package/dist/ai/thinking.d.ts +42 -0
  113. package/dist/ai/thinking.js +101 -0
  114. package/dist/ai/types.d.ts +448 -0
  115. package/dist/ai/types.js +19 -0
  116. package/dist/bundle/ama-sandbox.cjs +324 -0
  117. package/dist/bundle/ama.cjs +27837 -0
  118. package/dist/bundle.d.ts +9 -0
  119. package/dist/bundle.js +15 -0
  120. package/dist/cli/args.d.ts +85 -0
  121. package/dist/cli/args.js +408 -0
  122. package/dist/cli/bootstrap.d.ts +18 -0
  123. package/dist/cli/bootstrap.js +399 -0
  124. package/dist/cli/compose-providers.d.ts +37 -0
  125. package/dist/cli/compose-providers.js +107 -0
  126. package/dist/cli/compose-session.d.ts +72 -0
  127. package/dist/cli/compose-session.js +370 -0
  128. package/dist/cli/compose-store.d.ts +24 -0
  129. package/dist/cli/compose-store.js +125 -0
  130. package/dist/cli/compose.d.ts +108 -0
  131. package/dist/cli/compose.js +221 -0
  132. package/dist/cli/default-model.d.ts +16 -0
  133. package/dist/cli/default-model.js +24 -0
  134. package/dist/cli/deps.d.ts +207 -0
  135. package/dist/cli/deps.js +12 -0
  136. package/dist/cli/exit-codes.d.ts +29 -0
  137. package/dist/cli/exit-codes.js +40 -0
  138. package/dist/cli/main.d.ts +27 -0
  139. package/dist/cli/main.js +177 -0
  140. package/dist/cli/runtime.d.ts +90 -0
  141. package/dist/cli/runtime.js +8 -0
  142. package/dist/cli/startup-screen.d.ts +15 -0
  143. package/dist/cli/startup-screen.js +56 -0
  144. package/dist/cli/startup-steps.d.ts +35 -0
  145. package/dist/cli/startup-steps.js +202 -0
  146. package/dist/cli/subcommands/auth.d.ts +12 -0
  147. package/dist/cli/subcommands/auth.js +91 -0
  148. package/dist/cli/subcommands/config.d.ts +53 -0
  149. package/dist/cli/subcommands/config.js +277 -0
  150. package/dist/cli/subcommands/context.d.ts +21 -0
  151. package/dist/cli/subcommands/context.js +42 -0
  152. package/dist/cli/subcommands/doctor.d.ts +11 -0
  153. package/dist/cli/subcommands/doctor.js +246 -0
  154. package/dist/cli/subcommands/init.d.ts +6 -0
  155. package/dist/cli/subcommands/init.js +22 -0
  156. package/dist/cli/subcommands/model-meta.d.ts +16 -0
  157. package/dist/cli/subcommands/model-meta.js +54 -0
  158. package/dist/cli/subcommands/models-cache-probe.d.ts +34 -0
  159. package/dist/cli/subcommands/models-cache-probe.js +190 -0
  160. package/dist/cli/subcommands/models-discover.d.ts +46 -0
  161. package/dist/cli/subcommands/models-discover.js +232 -0
  162. package/dist/cli/subcommands/models.d.ts +40 -0
  163. package/dist/cli/subcommands/models.js +152 -0
  164. package/dist/cli/subcommands/providers-list.d.ts +8 -0
  165. package/dist/cli/subcommands/providers-list.js +97 -0
  166. package/dist/cli/subcommands/providers-plan.d.ts +79 -0
  167. package/dist/cli/subcommands/providers-plan.js +215 -0
  168. package/dist/cli/subcommands/providers.d.ts +27 -0
  169. package/dist/cli/subcommands/providers.js +433 -0
  170. package/dist/cli/subcommands/sessions.d.ts +11 -0
  171. package/dist/cli/subcommands/sessions.js +100 -0
  172. package/dist/codemode/capability.d.ts +37 -0
  173. package/dist/codemode/capability.js +55 -0
  174. package/dist/codemode/declarations.d.ts +44 -0
  175. package/dist/codemode/declarations.js +111 -0
  176. package/dist/codemode/host-side.d.ts +59 -0
  177. package/dist/codemode/host-side.js +253 -0
  178. package/dist/codemode/modes.d.ts +23 -0
  179. package/dist/codemode/modes.js +40 -0
  180. package/dist/codemode/protocol.d.ts +91 -0
  181. package/dist/codemode/protocol.js +114 -0
  182. package/dist/codemode/sandbox-entry.d.ts +25 -0
  183. package/dist/codemode/sandbox-entry.js +323 -0
  184. package/dist/codemode/store.d.ts +26 -0
  185. package/dist/codemode/store.js +53 -0
  186. package/dist/codemode/tool.d.ts +105 -0
  187. package/dist/codemode/tool.js +283 -0
  188. package/dist/compaction/branch-summary.d.ts +30 -0
  189. package/dist/compaction/branch-summary.js +79 -0
  190. package/dist/compaction/breaker.d.ts +32 -0
  191. package/dist/compaction/breaker.js +65 -0
  192. package/dist/compaction/cut-point.d.ts +23 -0
  193. package/dist/compaction/cut-point.js +70 -0
  194. package/dist/compaction/estimate.d.ts +30 -0
  195. package/dist/compaction/estimate.js +114 -0
  196. package/dist/compaction/prune-tier.d.ts +31 -0
  197. package/dist/compaction/prune-tier.js +83 -0
  198. package/dist/compaction/serialize.d.ts +20 -0
  199. package/dist/compaction/serialize.js +115 -0
  200. package/dist/compaction/summarize-tier.d.ts +111 -0
  201. package/dist/compaction/summarize-tier.js +305 -0
  202. package/dist/config/auth-file.d.ts +39 -0
  203. package/dist/config/auth-file.js +92 -0
  204. package/dist/config/context-files.d.ts +30 -0
  205. package/dist/config/context-files.js +77 -0
  206. package/dist/config/init.d.ts +30 -0
  207. package/dist/config/init.js +92 -0
  208. package/dist/config/json-schema.d.ts +14 -0
  209. package/dist/config/json-schema.js +182 -0
  210. package/dist/config/load.d.ts +45 -0
  211. package/dist/config/load.js +97 -0
  212. package/dist/config/merge.d.ts +73 -0
  213. package/dist/config/merge.js +248 -0
  214. package/dist/config/paths.d.ts +38 -0
  215. package/dist/config/paths.js +108 -0
  216. package/dist/config/profile.d.ts +27 -0
  217. package/dist/config/profile.js +65 -0
  218. package/dist/config/schema.d.ts +34 -0
  219. package/dist/config/schema.js +476 -0
  220. package/dist/config/trust.d.ts +42 -0
  221. package/dist/config/trust.js +122 -0
  222. package/dist/config/types.d.ts +190 -0
  223. package/dist/config/types.js +29 -0
  224. package/dist/config/write.d.ts +10 -0
  225. package/dist/config/write.js +25 -0
  226. package/dist/errors.d.ts +28 -0
  227. package/dist/errors.js +30 -0
  228. package/dist/hooks/config.d.ts +49 -0
  229. package/dist/hooks/config.js +102 -0
  230. package/dist/hooks/dispatcher.d.ts +41 -0
  231. package/dist/hooks/dispatcher.js +104 -0
  232. package/dist/hooks/matcher.d.ts +22 -0
  233. package/dist/hooks/matcher.js +108 -0
  234. package/dist/hooks/protocol.d.ts +33 -0
  235. package/dist/hooks/protocol.js +240 -0
  236. package/dist/hooks/runner.d.ts +30 -0
  237. package/dist/hooks/runner.js +143 -0
  238. package/dist/hooks/types.d.ts +147 -0
  239. package/dist/hooks/types.js +20 -0
  240. package/dist/host/api-impl.d.ts +75 -0
  241. package/dist/host/api-impl.js +173 -0
  242. package/dist/host/loader.d.ts +31 -0
  243. package/dist/host/loader.js +147 -0
  244. package/dist/host/types.d.ts +169 -0
  245. package/dist/host/types.js +14 -0
  246. package/dist/host.d.ts +5 -0
  247. package/dist/host.js +4 -0
  248. package/dist/index.d.ts +41 -0
  249. package/dist/index.js +28 -0
  250. package/dist/modes/commands-core.d.ts +44 -0
  251. package/dist/modes/commands-core.js +171 -0
  252. package/dist/modes/image-input.d.ts +27 -0
  253. package/dist/modes/image-input.js +78 -0
  254. package/dist/modes/interactive/approval-dialog.d.ts +48 -0
  255. package/dist/modes/interactive/approval-dialog.js +202 -0
  256. package/dist/modes/interactive/commands.d.ts +41 -0
  257. package/dist/modes/interactive/commands.js +201 -0
  258. package/dist/modes/interactive/completion.d.ts +53 -0
  259. package/dist/modes/interactive/completion.js +157 -0
  260. package/dist/modes/interactive/interactive-mode.d.ts +46 -0
  261. package/dist/modes/interactive/interactive-mode.js +491 -0
  262. package/dist/modes/interactive/key-dispatch.d.ts +29 -0
  263. package/dist/modes/interactive/key-dispatch.js +107 -0
  264. package/dist/modes/interactive/line/line-editor.d.ts +50 -0
  265. package/dist/modes/interactive/line/line-editor.js +232 -0
  266. package/dist/modes/interactive/line/line-mode.d.ts +20 -0
  267. package/dist/modes/interactive/line/line-mode.js +218 -0
  268. package/dist/modes/interactive/line/line-render.d.ts +35 -0
  269. package/dist/modes/interactive/line/line-render.js +171 -0
  270. package/dist/modes/interactive/line/paste-state.d.ts +26 -0
  271. package/dist/modes/interactive/line/paste-state.js +72 -0
  272. package/dist/modes/interactive/message-view.d.ts +77 -0
  273. package/dist/modes/interactive/message-view.js +269 -0
  274. package/dist/modes/interactive/pickers.d.ts +42 -0
  275. package/dist/modes/interactive/pickers.js +115 -0
  276. package/dist/modes/interactive/startup-ui.d.ts +33 -0
  277. package/dist/modes/interactive/startup-ui.js +268 -0
  278. package/dist/modes/interactive/status-bar.d.ts +48 -0
  279. package/dist/modes/interactive/status-bar.js +139 -0
  280. package/dist/modes/interactive/tool-view.d.ts +95 -0
  281. package/dist/modes/interactive/tool-view.js +344 -0
  282. package/dist/modes/print/json-event.d.ts +15 -0
  283. package/dist/modes/print/json-event.js +39 -0
  284. package/dist/modes/print/print-mode.d.ts +17 -0
  285. package/dist/modes/print/print-mode.js +107 -0
  286. package/dist/modes/rpc/commands.d.ts +40 -0
  287. package/dist/modes/rpc/commands.js +263 -0
  288. package/dist/modes/rpc/jsonl.d.ts +16 -0
  289. package/dist/modes/rpc/jsonl.js +55 -0
  290. package/dist/modes/rpc/rpc-mode.d.ts +22 -0
  291. package/dist/modes/rpc/rpc-mode.js +153 -0
  292. package/dist/modes/session-report.d.ts +55 -0
  293. package/dist/modes/session-report.js +278 -0
  294. package/dist/modes/shared.d.ts +13 -0
  295. package/dist/modes/shared.js +52 -0
  296. package/dist/modes/startup-ui-text.d.ts +16 -0
  297. package/dist/modes/startup-ui-text.js +103 -0
  298. package/dist/permissions/broker.d.ts +47 -0
  299. package/dist/permissions/broker.js +107 -0
  300. package/dist/permissions/dangerous.d.ts +47 -0
  301. package/dist/permissions/dangerous.js +370 -0
  302. package/dist/permissions/pipeline.d.ts +51 -0
  303. package/dist/permissions/pipeline.js +181 -0
  304. package/dist/permissions/preview.d.ts +43 -0
  305. package/dist/permissions/preview.js +463 -0
  306. package/dist/permissions/rules.d.ts +58 -0
  307. package/dist/permissions/rules.js +249 -0
  308. package/dist/permissions/types.d.ts +100 -0
  309. package/dist/permissions/types.js +18 -0
  310. package/dist/rpc.d.ts +173 -0
  311. package/dist/rpc.js +13 -0
  312. package/dist/sdk.d.ts +98 -0
  313. package/dist/sdk.js +195 -0
  314. package/dist/session/manager.d.ts +70 -0
  315. package/dist/session/manager.js +278 -0
  316. package/dist/session/migrate.d.ts +19 -0
  317. package/dist/session/migrate.js +53 -0
  318. package/dist/session/projection.d.ts +50 -0
  319. package/dist/session/projection.js +184 -0
  320. package/dist/session/store.d.ts +47 -0
  321. package/dist/session/store.js +210 -0
  322. package/dist/session/tree.d.ts +21 -0
  323. package/dist/session/tree.js +88 -0
  324. package/dist/session/types.d.ts +201 -0
  325. package/dist/session/types.js +18 -0
  326. package/dist/skills/discover.d.ts +60 -0
  327. package/dist/skills/discover.js +160 -0
  328. package/dist/skills/expand.d.ts +35 -0
  329. package/dist/skills/expand.js +46 -0
  330. package/dist/skills/frontmatter.d.ts +27 -0
  331. package/dist/skills/frontmatter.js +157 -0
  332. package/dist/skills/index-prompt.d.ts +13 -0
  333. package/dist/skills/index-prompt.js +42 -0
  334. package/dist/skills/templates.d.ts +52 -0
  335. package/dist/skills/templates.js +177 -0
  336. package/dist/tools/bash.d.ts +49 -0
  337. package/dist/tools/bash.js +222 -0
  338. package/dist/tools/edit-fuzzy.d.ts +36 -0
  339. package/dist/tools/edit-fuzzy.js +129 -0
  340. package/dist/tools/edit.d.ts +38 -0
  341. package/dist/tools/edit.js +241 -0
  342. package/dist/tools/file-mutex.d.ts +8 -0
  343. package/dist/tools/file-mutex.js +29 -0
  344. package/dist/tools/glob.d.ts +44 -0
  345. package/dist/tools/glob.js +217 -0
  346. package/dist/tools/grep.d.ts +39 -0
  347. package/dist/tools/grep.js +179 -0
  348. package/dist/tools/ignore.d.ts +40 -0
  349. package/dist/tools/ignore.js +174 -0
  350. package/dist/tools/image-file.d.ts +31 -0
  351. package/dist/tools/image-file.js +114 -0
  352. package/dist/tools/ls.d.ts +21 -0
  353. package/dist/tools/ls.js +96 -0
  354. package/dist/tools/output-accumulator.d.ts +48 -0
  355. package/dist/tools/output-accumulator.js +106 -0
  356. package/dist/tools/paths.d.ts +20 -0
  357. package/dist/tools/paths.js +58 -0
  358. package/dist/tools/presets.d.ts +71 -0
  359. package/dist/tools/presets.js +128 -0
  360. package/dist/tools/process-tree.d.ts +35 -0
  361. package/dist/tools/process-tree.js +125 -0
  362. package/dist/tools/read.d.ts +24 -0
  363. package/dist/tools/read.js +132 -0
  364. package/dist/tools/registry.d.ts +43 -0
  365. package/dist/tools/registry.js +106 -0
  366. package/dist/tools/shell.d.ts +38 -0
  367. package/dist/tools/shell.js +91 -0
  368. package/dist/tools/task.d.ts +29 -0
  369. package/dist/tools/task.js +107 -0
  370. package/dist/tools/todo.d.ts +28 -0
  371. package/dist/tools/todo.js +107 -0
  372. package/dist/tools/truncate.d.ts +44 -0
  373. package/dist/tools/truncate.js +175 -0
  374. package/dist/tools/types.d.ts +135 -0
  375. package/dist/tools/types.js +19 -0
  376. package/dist/tools/write.d.ts +20 -0
  377. package/dist/tools/write.js +84 -0
  378. package/dist/tui/ansi.d.ts +29 -0
  379. package/dist/tui/ansi.js +420 -0
  380. package/dist/tui/component.d.ts +38 -0
  381. package/dist/tui/component.js +13 -0
  382. package/dist/tui/components/box.d.ts +26 -0
  383. package/dist/tui/components/box.js +65 -0
  384. package/dist/tui/components/container.d.ts +14 -0
  385. package/dist/tui/components/container.js +39 -0
  386. package/dist/tui/components/editor-buffer.d.ts +67 -0
  387. package/dist/tui/components/editor-buffer.js +300 -0
  388. package/dist/tui/components/editor-paste.d.ts +29 -0
  389. package/dist/tui/components/editor-paste.js +64 -0
  390. package/dist/tui/components/editor.d.ts +114 -0
  391. package/dist/tui/components/editor.js +530 -0
  392. package/dist/tui/components/key-value.d.ts +29 -0
  393. package/dist/tui/components/key-value.js +48 -0
  394. package/dist/tui/components/loader.d.ts +35 -0
  395. package/dist/tui/components/loader.js +71 -0
  396. package/dist/tui/components/markdown.d.ts +90 -0
  397. package/dist/tui/components/markdown.js +462 -0
  398. package/dist/tui/components/meter.d.ts +29 -0
  399. package/dist/tui/components/meter.js +47 -0
  400. package/dist/tui/components/overlay.d.ts +24 -0
  401. package/dist/tui/components/overlay.js +52 -0
  402. package/dist/tui/components/select-list.d.ts +57 -0
  403. package/dist/tui/components/select-list.js +179 -0
  404. package/dist/tui/components/spacer.d.ts +11 -0
  405. package/dist/tui/components/spacer.js +16 -0
  406. package/dist/tui/components/text.d.ts +30 -0
  407. package/dist/tui/components/text.js +65 -0
  408. package/dist/tui/keybindings.d.ts +66 -0
  409. package/dist/tui/keybindings.js +120 -0
  410. package/dist/tui/keys.d.ts +38 -0
  411. package/dist/tui/keys.js +275 -0
  412. package/dist/tui/stdin-buffer.d.ts +41 -0
  413. package/dist/tui/stdin-buffer.js +206 -0
  414. package/dist/tui/terminal.d.ts +79 -0
  415. package/dist/tui/terminal.js +153 -0
  416. package/dist/tui/theme.d.ts +31 -0
  417. package/dist/tui/theme.js +199 -0
  418. package/dist/tui/tui.d.ts +103 -0
  419. package/dist/tui/tui.js +363 -0
  420. package/dist/tui/vt-screen.d.ts +46 -0
  421. package/dist/tui/vt-screen.js +251 -0
  422. package/dist/tui.d.ts +30 -0
  423. package/dist/tui.js +31 -0
  424. package/dist/version.d.ts +8 -0
  425. package/dist/version.js +24 -0
  426. package/docs/codemode.md +102 -0
  427. package/docs/hooks.md +196 -0
  428. package/docs/host-api.md +161 -0
  429. package/docs/providers.md +531 -0
  430. package/docs/rpc.md +253 -0
  431. package/docs/session-format.md +119 -0
  432. package/docs/tui.md +170 -0
  433. package/package.json +101 -4
@@ -0,0 +1,11 @@
1
+ /**
2
+ * `ama sessions list|show|prune`(设计 §1.2、§8)。[B5]
3
+ *
4
+ * 会话存储是 B2 的实现,经 RuntimeDeps.sessions 注入;这里只做参数、目录解析与输出格式。
5
+ * - list [--all]:缺省只列当前目录的会话;`--all` 列全部。
6
+ * - show <id>:头信息 + 条目类型统计 + 首条提示。
7
+ * - prune [--older-than <天>] [--dry-run]:缺省 30 天,移到 trash(不删除)。
8
+ */
9
+ import type { CliIo, RuntimeDeps } from "../deps.js";
10
+ export declare const SESSIONS_USAGE = "\u7528\u6CD5\uFF1Aama sessions list [--all] [--session-dir <\u76EE\u5F55>]\n ama sessions show <id> [--session-dir <\u76EE\u5F55>]\n ama sessions prune [--older-than <\u5929>] [--dry-run] [--all] [--session-dir <\u76EE\u5F55>]\n";
11
+ export declare function runSessions(argv: readonly string[], io: CliIo, deps: Pick<RuntimeDeps, "sessions"> | undefined): Promise<number>;
@@ -0,0 +1,100 @@
1
+ /**
2
+ * `ama sessions list|show|prune`(设计 §1.2、§8)。[B5]
3
+ *
4
+ * 会话存储是 B2 的实现,经 RuntimeDeps.sessions 注入;这里只做参数、目录解析与输出格式。
5
+ * - list [--all]:缺省只列当前目录的会话;`--all` 列全部。
6
+ * - show <id>:头信息 + 条目类型统计 + 首条提示。
7
+ * - prune [--older-than <天>] [--dry-run]:缺省 30 天,移到 trash(不删除)。
8
+ */
9
+ import { resolve } from "node:path";
10
+ import { expandHome, resolveDataDir } from "../../config/paths.js";
11
+ import { parseSubArgs, UsageError } from "../args.js";
12
+ import { ExitCode } from "../exit-codes.js";
13
+ export const SESSIONS_USAGE = `用法:ama sessions list [--all] [--session-dir <目录>]
14
+ ama sessions show <id> [--session-dir <目录>]
15
+ ama sessions prune [--older-than <天>] [--dry-run] [--all] [--session-dir <目录>]
16
+ `;
17
+ function shortTime(iso) {
18
+ return iso.replace("T", " ").replace(/\.\d+Z$|Z$/, "");
19
+ }
20
+ function oneLine(text, max = 60) {
21
+ if (text === undefined)
22
+ return "";
23
+ const flat = text.replace(/\s+/g, " ").trim();
24
+ return flat.length > max ? `${flat.slice(0, max - 1)}…` : flat;
25
+ }
26
+ export async function runSessions(argv, io, deps) {
27
+ const { positionals, values, flags } = parseSubArgs(argv, ["session-dir", "older-than"], ["all", "dry-run"]);
28
+ const action = positionals[0];
29
+ if (flags.has("help") || action === undefined) {
30
+ io.stdout(SESSIONS_USAGE);
31
+ return flags.has("help") ? ExitCode.Ok : ExitCode.Usage;
32
+ }
33
+ const dirFlag = values.get("session-dir");
34
+ const sessionDir = dirFlag !== undefined
35
+ ? resolve(io.cwd, expandHome(dirFlag, { env: io.env }))
36
+ : resolve(resolveDataDir({ env: io.env }), "sessions");
37
+ const sessions = deps?.sessions;
38
+ const scope = flags.has("all") ? {} : { cwd: io.cwd };
39
+ switch (action) {
40
+ case "list": {
41
+ if (sessions?.list === undefined)
42
+ return notWired(io, "sessions list");
43
+ const items = await sessions.list({ sessionDir, ...scope });
44
+ if (items.length === 0) {
45
+ io.stdout("没有会话\n");
46
+ return ExitCode.Ok;
47
+ }
48
+ for (const item of items) {
49
+ const name = item.name ?? oneLine(item.firstPrompt);
50
+ io.stdout(`${item.id.slice(0, 8)} ${shortTime(item.modifiedAt)} ${String(item.messageCount).padStart(4)} 条 ${name}\n`);
51
+ }
52
+ return ExitCode.Ok;
53
+ }
54
+ case "show": {
55
+ const id = positionals[1];
56
+ if (id === undefined)
57
+ throw new UsageError("ama sessions show 需要 <id>");
58
+ if (sessions?.show === undefined)
59
+ return notWired(io, "sessions show");
60
+ const { item, entries } = await sessions.show(id, { sessionDir });
61
+ const counts = new Map();
62
+ for (const entry of entries)
63
+ counts.set(entry.type, (counts.get(entry.type) ?? 0) + 1);
64
+ io.stdout([
65
+ `id:${item.id}`,
66
+ `文件:${item.file}`,
67
+ `目录:${item.cwd}`,
68
+ ...(item.name !== undefined ? [`名称:${item.name}`] : []),
69
+ `创建:${shortTime(item.createdAt)} · 修改:${shortTime(item.modifiedAt)}`,
70
+ `消息:${item.messageCount}`,
71
+ `条目:${[...counts].map(([type, n]) => `${type} ${n}`).join(" · ")}`,
72
+ ...(item.firstPrompt !== undefined
73
+ ? [`首条提示:${oneLine(item.firstPrompt, 200)}`]
74
+ : []),
75
+ ].join("\n") + "\n");
76
+ return ExitCode.Ok;
77
+ }
78
+ case "prune": {
79
+ const raw = values.get("older-than") ?? "30";
80
+ const olderThanDays = Number(raw);
81
+ if (!Number.isFinite(olderThanDays) || olderThanDays < 0) {
82
+ throw new UsageError(`--older-than 应为非负天数(收到 ${raw})`);
83
+ }
84
+ if (sessions?.prune === undefined)
85
+ return notWired(io, "sessions prune");
86
+ const dryRun = flags.has("dry-run");
87
+ const { moved } = await sessions.prune({ sessionDir, olderThanDays, dryRun, ...scope });
88
+ for (const file of moved)
89
+ io.stdout(`${dryRun ? "将移到 trash" : "已移到 trash"}:${file}\n`);
90
+ io.stdout(`${moved.length} 个会话${dryRun ? "(演练,未改动)" : ""}\n`);
91
+ return ExitCode.Ok;
92
+ }
93
+ default:
94
+ throw new UsageError(`未知的 sessions 子命令:${action}`);
95
+ }
96
+ }
97
+ function notWired(io, what) {
98
+ io.stderr(`ama: ${what} 尚未装配(会话存储由集成批次注入)\n`);
99
+ return ExitCode.RuntimeError;
100
+ }
@@ -0,0 +1,37 @@
1
+ /**
2
+ * 沙箱能力探测(设计 §5.5 沙箱第 4 条)。[B10]
3
+ *
4
+ * Node 的权限模型(`--permission`)在 Node ≥ 25 同时拒绝网络;Node 22 / 24 只管文件、子进程、
5
+ * worker 与 addon,不管网络——脚本若逃出 `vm` 就能联网。所以:
6
+ * - Node ≥ 25 → `strict`;
7
+ * - Node 22 / 24 → 仍可用,但工具描述与状态栏标注「网络未隔离」;`codemode.requireStrict: true`
8
+ * 时直接禁用 codemode 工具并 warning(`codemodeAvailability`)。
9
+ *
10
+ * 状态栏由 B7 直接调用 `detectSandboxCapability()` 判断(不经宿主 status)。
11
+ */
12
+ export interface SandboxCapability {
13
+ /** 权限模型同时隔离文件、子进程与网络。 */
14
+ strict: boolean;
15
+ /** 运行时 Node 的主版本。 */
16
+ nodeMajor: number;
17
+ /** 人读说明(英文,进工具描述;不含完整版本号以保证同一主版本下字节稳定)。 */
18
+ reason: string;
19
+ /** 运行时支持的权限开关:Node ≥ 22.13 / 23.5 为 `--permission`,更早为 `--experimental-permission`。 */
20
+ permissionFlag: "--permission" | "--experimental-permission";
21
+ }
22
+ export declare const NETWORK_NOT_ISOLATED = "network not isolated";
23
+ export declare function parseNodeMajor(version: string): number;
24
+ /** `--permission` 在 Node 22.13 / 23.5 之后才是稳定名字;之前只有 `--experimental-permission`。 */
25
+ export declare function permissionFlagFor(allowedFlags?: ReadonlySet<string>): SandboxCapability["permissionFlag"];
26
+ export declare function detectSandboxCapability(nodeVersion?: string, allowedFlags?: ReadonlySet<string>): SandboxCapability;
27
+ export type CodemodeAvailability = {
28
+ available: true;
29
+ capability: SandboxCapability;
30
+ warning?: string;
31
+ } | {
32
+ available: false;
33
+ capability: SandboxCapability;
34
+ warning: string;
35
+ };
36
+ /** `requireStrict` 且运行时不是 strict → 禁用并给出 warning;非 strict 但允许 → 可用并提示。 */
37
+ export declare function codemodeAvailability(requireStrict: boolean | undefined, capability?: SandboxCapability): CodemodeAvailability;
@@ -0,0 +1,55 @@
1
+ /**
2
+ * 沙箱能力探测(设计 §5.5 沙箱第 4 条)。[B10]
3
+ *
4
+ * Node 的权限模型(`--permission`)在 Node ≥ 25 同时拒绝网络;Node 22 / 24 只管文件、子进程、
5
+ * worker 与 addon,不管网络——脚本若逃出 `vm` 就能联网。所以:
6
+ * - Node ≥ 25 → `strict`;
7
+ * - Node 22 / 24 → 仍可用,但工具描述与状态栏标注「网络未隔离」;`codemode.requireStrict: true`
8
+ * 时直接禁用 codemode 工具并 warning(`codemodeAvailability`)。
9
+ *
10
+ * 状态栏由 B7 直接调用 `detectSandboxCapability()` 判断(不经宿主 status)。
11
+ */
12
+ export const NETWORK_NOT_ISOLATED = "network not isolated";
13
+ export function parseNodeMajor(version) {
14
+ const major = Number.parseInt(version.replace(/^v/, "").split(".")[0] ?? "", 10);
15
+ return Number.isFinite(major) ? major : 0;
16
+ }
17
+ /** `--permission` 在 Node 22.13 / 23.5 之后才是稳定名字;之前只有 `--experimental-permission`。 */
18
+ export function permissionFlagFor(allowedFlags = process.allowedNodeEnvironmentFlags) {
19
+ return allowedFlags.has("--permission") ? "--permission" : "--experimental-permission";
20
+ }
21
+ export function detectSandboxCapability(nodeVersion = process.versions.node, allowedFlags) {
22
+ const nodeMajor = parseNodeMajor(nodeVersion);
23
+ const permissionFlag = permissionFlagFor(allowedFlags);
24
+ if (nodeMajor >= 25) {
25
+ return {
26
+ strict: true,
27
+ nodeMajor,
28
+ reason: `Node ${nodeMajor}: the permission model blocks file access, child processes and network`,
29
+ permissionFlag,
30
+ };
31
+ }
32
+ return {
33
+ strict: false,
34
+ nodeMajor,
35
+ reason: `Node ${nodeMajor}: ${NETWORK_NOT_ISOLATED} (the permission model blocks file access and child processes only)`,
36
+ permissionFlag,
37
+ };
38
+ }
39
+ /** `requireStrict` 且运行时不是 strict → 禁用并给出 warning;非 strict 但允许 → 可用并提示。 */
40
+ export function codemodeAvailability(requireStrict, capability = detectSandboxCapability()) {
41
+ if (capability.strict)
42
+ return { available: true, capability };
43
+ if (requireStrict === true) {
44
+ return {
45
+ available: false,
46
+ capability,
47
+ warning: `codemode 已禁用:Node ${capability.nodeMajor} 的权限模型不隔离网络(codemode.requireStrict 为 true;Node ≥ 25 才隔离)`,
48
+ };
49
+ }
50
+ return {
51
+ available: true,
52
+ capability,
53
+ warning: `codemode:Node ${capability.nodeMajor} 下网络未隔离(Node ≥ 25 才隔离;要求隔离可设 codemode.requireStrict)`,
54
+ };
55
+ }
@@ -0,0 +1,44 @@
1
+ /**
2
+ * 工具声明:JSON Schema 子集(`ai/types.ts` 的 JsonSchema)→ TypeScript 声明字符串(设计 §5.5)。[B10]
3
+ *
4
+ * - 每个工具一段:JSDoc 描述 + `name(args: {...}): Promise<R>;`,属性按 schema 原序,必填无 `?`,
5
+ * 属性的 description 作为行内 JSDoc;`items` → `T[]`,`enum` → 字面量联合,空 object → `Record`。
6
+ * - 返回类型:`bash` 解析为 `BashResult`(脚本里不受 50 KB 截断的结构化值),其余内置工具为文本,
7
+ * 宿主 / SDK 工具可能返回结构化值,声明为 `unknown`。
8
+ * - `inlineBudget`(估算 token = 字符 / 4):按名排序依次内联,放不下的工具只列名字,脚本用
9
+ * `describeTool(name)` 取完整声明。输出只依赖输入,字节稳定(设计 §9.1)。
10
+ */
11
+ import type { JsonSchema } from "../ai/types.js";
12
+ export declare const DEFAULT_INLINE_BUDGET = 3000;
13
+ /** 脚本里 `tools.bash()` 的解析值(设计 §5.5)。 */
14
+ export declare const BASH_RESULT_DECLARATION = "interface BashResult { output: string; truncated: boolean; fullOutputPath?: string; exitCode: number; wallTimeMs: number }";
15
+ export interface DeclarableTool {
16
+ name: string;
17
+ description: string;
18
+ parameters: JsonSchema;
19
+ }
20
+ export interface DeclarationOptions {
21
+ /** 内置工具(非 bash)返回文本;其它来源返回 unknown。缺省 true。 */
22
+ textResult?: boolean;
23
+ }
24
+ export declare function estimateTokens(text: string): number;
25
+ /** 单个 schema → TypeScript 类型表达式。 */
26
+ export declare function schemaToType(schema: JsonSchema | undefined, indent?: string): string;
27
+ /** 单个工具的声明(describeTool 返回它)。 */
28
+ export declare function toolDeclaration(tool: DeclarableTool, options?: DeclarationOptions): string;
29
+ export interface DeclarationBlock {
30
+ /** 内联进 codemode 描述的声明(按名排序)。 */
31
+ inline: string[];
32
+ /** 超出预算、只列名字的工具。 */
33
+ namesOnly: string[];
34
+ /** 拼好的文本。 */
35
+ text: string;
36
+ }
37
+ /**
38
+ * 按预算拼工具声明。工具按名排序;预算按估算 token 累计,第一个放不下的之后全部只列名字
39
+ * (保持按名顺序、不跳着塞小的,便于模型理解「后面的去 describeTool 查」)。
40
+ */
41
+ export declare function buildDeclarationBlock(tools: readonly {
42
+ tool: DeclarableTool;
43
+ textResult?: boolean;
44
+ }[], inlineBudget?: number): DeclarationBlock;
@@ -0,0 +1,111 @@
1
+ /**
2
+ * 工具声明:JSON Schema 子集(`ai/types.ts` 的 JsonSchema)→ TypeScript 声明字符串(设计 §5.5)。[B10]
3
+ *
4
+ * - 每个工具一段:JSDoc 描述 + `name(args: {...}): Promise<R>;`,属性按 schema 原序,必填无 `?`,
5
+ * 属性的 description 作为行内 JSDoc;`items` → `T[]`,`enum` → 字面量联合,空 object → `Record`。
6
+ * - 返回类型:`bash` 解析为 `BashResult`(脚本里不受 50 KB 截断的结构化值),其余内置工具为文本,
7
+ * 宿主 / SDK 工具可能返回结构化值,声明为 `unknown`。
8
+ * - `inlineBudget`(估算 token = 字符 / 4):按名排序依次内联,放不下的工具只列名字,脚本用
9
+ * `describeTool(name)` 取完整声明。输出只依赖输入,字节稳定(设计 §9.1)。
10
+ */
11
+ export const DEFAULT_INLINE_BUDGET = 3000;
12
+ /** 脚本里 `tools.bash()` 的解析值(设计 §5.5)。 */
13
+ export const BASH_RESULT_DECLARATION = "interface BashResult { output: string; truncated: boolean; fullOutputPath?: string; exitCode: number; wallTimeMs: number }";
14
+ export function estimateTokens(text) {
15
+ return Math.ceil(text.length / 4);
16
+ }
17
+ const IDENT_RE = /^[A-Za-z_$][A-Za-z0-9_$]*$/;
18
+ function propertyKey(name) {
19
+ return IDENT_RE.test(name) ? name : JSON.stringify(name);
20
+ }
21
+ function jsdoc(text, indent) {
22
+ if (text === undefined || text.trim() === "")
23
+ return "";
24
+ const clean = text.replace(/\*\//g, "*\\/").trim();
25
+ if (!clean.includes("\n"))
26
+ return `${indent}/** ${clean} */\n`;
27
+ const body = clean
28
+ .split("\n")
29
+ .map((line) => `${indent} * ${line}`.trimEnd())
30
+ .join("\n");
31
+ return `${indent}/**\n${body}\n${indent} */\n`;
32
+ }
33
+ /** 单个 schema → TypeScript 类型表达式。 */
34
+ export function schemaToType(schema, indent = "") {
35
+ if (schema === undefined)
36
+ return "unknown";
37
+ if (schema.enum !== undefined && schema.enum.length > 0) {
38
+ return schema.enum.map((value) => JSON.stringify(value)).join(" | ");
39
+ }
40
+ switch (schema.type) {
41
+ case "string":
42
+ return "string";
43
+ case "number":
44
+ case "integer":
45
+ return "number";
46
+ case "boolean":
47
+ return "boolean";
48
+ case "array": {
49
+ const item = schemaToType(schema.items, indent);
50
+ return /[|&]/.test(item) && !item.startsWith("{") ? `(${item})[]` : `${item}[]`;
51
+ }
52
+ case "object":
53
+ return objectType(schema, indent);
54
+ default:
55
+ return schema.properties !== undefined ? objectType(schema, indent) : "unknown";
56
+ }
57
+ }
58
+ function objectType(schema, indent) {
59
+ const properties = Object.entries(schema.properties ?? {});
60
+ if (properties.length === 0)
61
+ return "Record<string, unknown>";
62
+ const required = new Set(schema.required ?? []);
63
+ const inner = `${indent} `;
64
+ const lines = properties.map(([name, prop]) => {
65
+ const optional = required.has(name) ? "" : "?";
66
+ return `${jsdoc(prop.description, inner)}${inner}${propertyKey(name)}${optional}: ${schemaToType(prop, inner)};`;
67
+ });
68
+ return `{\n${lines.join("\n")}\n${indent}}`;
69
+ }
70
+ /** 单个工具的声明(describeTool 返回它)。 */
71
+ export function toolDeclaration(tool, options = {}) {
72
+ const returns = tool.name === "bash" ? "BashResult" : options.textResult === false ? "unknown" : "string";
73
+ const args = schemaToType(tool.parameters);
74
+ return `${jsdoc(tool.description, "")}${tool.name}(args: ${args}): Promise<${returns}>;`;
75
+ }
76
+ /**
77
+ * 按预算拼工具声明。工具按名排序;预算按估算 token 累计,第一个放不下的之后全部只列名字
78
+ * (保持按名顺序、不跳着塞小的,便于模型理解「后面的去 describeTool 查」)。
79
+ */
80
+ export function buildDeclarationBlock(tools, inlineBudget = DEFAULT_INLINE_BUDGET) {
81
+ const sorted = [...tools].sort((a, b) => (a.tool.name < b.tool.name ? -1 : 1));
82
+ const inline = [];
83
+ const namesOnly = [];
84
+ let used = 0;
85
+ for (const { tool, textResult } of sorted) {
86
+ const decl = toolDeclaration(tool, textResult === false ? { textResult } : {});
87
+ const cost = estimateTokens(decl);
88
+ if (namesOnly.length === 0 && used + cost <= inlineBudget) {
89
+ inline.push(decl);
90
+ used += cost;
91
+ }
92
+ else
93
+ namesOnly.push(tool.name);
94
+ }
95
+ const parts = [];
96
+ if (sorted.some(({ tool }) => tool.name === "bash"))
97
+ parts.push(BASH_RESULT_DECLARATION);
98
+ if (inline.length > 0) {
99
+ parts.push(`declare const tools: {\n${indent(inline.join("\n"))}\n};`);
100
+ }
101
+ if (namesOnly.length > 0) {
102
+ parts.push(`// Also callable (use describeTool(name) for the signature): ${namesOnly.join(", ")}`);
103
+ }
104
+ return { inline, namesOnly, text: parts.join("\n") };
105
+ }
106
+ function indent(text) {
107
+ return text
108
+ .split("\n")
109
+ .map((line) => (line === "" ? line : ` ${line}`))
110
+ .join("\n");
111
+ }
@@ -0,0 +1,59 @@
1
+ /**
2
+ * codemode 父进程侧:起子进程、转发工具调用、超时杀树(设计 §5.5 沙箱第 1–4 条)。[B10]
3
+ *
4
+ * - 命令行:`<node> --permission --allow-fs-read=<入口> --disallow-code-generation-from-strings
5
+ * <入口> --ama-codemode-sandbox`;Node 22.0–22.12 用 `--experimental-permission`。不授予文件写、
6
+ * 子进程、worker、addon、inspector 权限;以空环境启动(拿不到密钥与会话路径);嵌入 Electron
7
+ * 时设 `ELECTRON_RUN_AS_NODE=1` 运行同一可执行文件。
8
+ * - 入口:bundle 里是 ama.cjs 同目录的 `ama-sandbox.cjs`(构建脚本定义 `__AMA_SANDBOX_ENTRY__`);
9
+ * tsc 产物里是同目录的 `sandbox-entry.js`;源码树里是 `sandbox-entry.ts`;取 realpath(权限模型
10
+ * 按真实路径比对,macOS 的 /tmp、/var 是符号链接)。
11
+ * - `tool_call` → `callTool(name, input, signal)`:由 tool.ts 接到 `ToolContext.tools.executeTool`
12
+ * (完整门禁);名字是 `codemode` 直接拒绝。脚本结束时 abort 仍在跑的调用。
13
+ * - 超时:父进程计时,到点杀子进程树(POSIX 进程组 / Windows taskkill);外层 abort:先发
14
+ * `abort`,宽限后杀树。
15
+ */
16
+ import { type SandboxCapability } from "./capability.js";
17
+ import { type ScriptOptions, type StoreSnapshot, type ToolDecl } from "./protocol.js";
18
+ /** 父进程累积的输出上限(字符);超出后丢弃中段(给模型的再按 max_output_tokens 截)。 */
19
+ export declare const MAX_COLLECTED_OUTPUT_CHARS: number;
20
+ /** 外层 abort 后等子进程自行结束的宽限。 */
21
+ export declare const ABORT_GRACE_MS = 500;
22
+ /** 子进程入口的绝对路径(realpath);找不到 → undefined。 */
23
+ export declare function resolveSandboxEntry(moduleUrl?: string): string | undefined;
24
+ /** 子进程参数(不含可执行文件)。 */
25
+ export declare function sandboxArgs(entry: string, capability?: Pick<SandboxCapability, "permissionFlag">): string[];
26
+ /** 子进程环境:空;Electron 下以 Node 方式运行同一可执行文件;Windows 保留 SystemRoot。 */
27
+ export declare function sandboxEnv(versions?: NodeJS.ProcessVersions, platform?: NodeJS.Platform, parentEnv?: NodeJS.ProcessEnv): NodeJS.ProcessEnv;
28
+ export interface SandboxRunRequest {
29
+ script: string;
30
+ options: ScriptOptions;
31
+ tools: ToolDecl[];
32
+ store: StoreSnapshot;
33
+ /** 执行一次内层工具调用:解析为脚本拿到的值,失败抛 Error(消息给脚本)。 */
34
+ callTool(name: string, input: unknown, signal: AbortSignal): Promise<unknown>;
35
+ onOutput?(text: string): void;
36
+ signal: AbortSignal;
37
+ /** 缺省 resolveSandboxEntry()。 */
38
+ entry?: string;
39
+ /** 缺省 process.execPath。 */
40
+ nodePath?: string;
41
+ capability?: Pick<SandboxCapability, "permissionFlag">;
42
+ }
43
+ export interface SandboxRunResult {
44
+ ok: boolean;
45
+ error?: string;
46
+ outputs: string[];
47
+ /** 脚本成功且写过 store 时的完整快照。 */
48
+ store?: StoreSnapshot;
49
+ elapsedMs: number;
50
+ timedOut: boolean;
51
+ aborted: boolean;
52
+ /** 内层调用次数。 */
53
+ toolCalls: number;
54
+ /** 输出超过 MAX_COLLECTED_OUTPUT_CHARS 被丢弃的字符数。 */
55
+ droppedChars: number;
56
+ /** 子进程 pid(POSIX 下也是进程组 id)。 */
57
+ pid?: number;
58
+ }
59
+ export declare function runSandbox(request: SandboxRunRequest): Promise<SandboxRunResult>;
@@ -0,0 +1,253 @@
1
+ /**
2
+ * codemode 父进程侧:起子进程、转发工具调用、超时杀树(设计 §5.5 沙箱第 1–4 条)。[B10]
3
+ *
4
+ * - 命令行:`<node> --permission --allow-fs-read=<入口> --disallow-code-generation-from-strings
5
+ * <入口> --ama-codemode-sandbox`;Node 22.0–22.12 用 `--experimental-permission`。不授予文件写、
6
+ * 子进程、worker、addon、inspector 权限;以空环境启动(拿不到密钥与会话路径);嵌入 Electron
7
+ * 时设 `ELECTRON_RUN_AS_NODE=1` 运行同一可执行文件。
8
+ * - 入口:bundle 里是 ama.cjs 同目录的 `ama-sandbox.cjs`(构建脚本定义 `__AMA_SANDBOX_ENTRY__`);
9
+ * tsc 产物里是同目录的 `sandbox-entry.js`;源码树里是 `sandbox-entry.ts`;取 realpath(权限模型
10
+ * 按真实路径比对,macOS 的 /tmp、/var 是符号链接)。
11
+ * - `tool_call` → `callTool(name, input, signal)`:由 tool.ts 接到 `ToolContext.tools.executeTool`
12
+ * (完整门禁);名字是 `codemode` 直接拒绝。脚本结束时 abort 仍在跑的调用。
13
+ * - 超时:父进程计时,到点杀子进程树(POSIX 进程组 / Windows taskkill);外层 abort:先发
14
+ * `abort`,宽限后杀树。
15
+ */
16
+ import { spawn } from "node:child_process";
17
+ import { existsSync, realpathSync } from "node:fs";
18
+ import { dirname, join } from "node:path";
19
+ import { fileURLToPath } from "node:url";
20
+ import { killProcessTree, trackProcessGroup, untrackProcessGroup } from "../tools/process-tree.js";
21
+ import { detectSandboxCapability } from "./capability.js";
22
+ import { LineSplitter, SANDBOX_MAIN_FLAG, decodeChildLine, encodeLine, } from "./protocol.js";
23
+ /** 父进程累积的输出上限(字符);超出后丢弃中段(给模型的再按 max_output_tokens 截)。 */
24
+ export const MAX_COLLECTED_OUTPUT_CHARS = 16 * 1024 * 1024;
25
+ /** 外层 abort 后等子进程自行结束的宽限。 */
26
+ export const ABORT_GRACE_MS = 500;
27
+ /** 子进程入口的绝对路径(realpath);找不到 → undefined。 */
28
+ export function resolveSandboxEntry(moduleUrl = import.meta.url) {
29
+ const here = dirname(fileURLToPath(moduleUrl));
30
+ const candidates = [];
31
+ if (typeof __AMA_SANDBOX_ENTRY__ !== "undefined")
32
+ candidates.push(join(here, __AMA_SANDBOX_ENTRY__));
33
+ candidates.push(join(here, "sandbox-entry.js"));
34
+ // 源码树(vitest / 开发):Node ≥ 22.18 直接剥离类型运行 .ts(入口只 import node: 与类型)。
35
+ candidates.push(join(here, "sandbox-entry.ts"));
36
+ for (const candidate of candidates) {
37
+ if (existsSync(candidate))
38
+ return realpathSync(candidate);
39
+ }
40
+ return undefined;
41
+ }
42
+ /** 子进程参数(不含可执行文件)。 */
43
+ export function sandboxArgs(entry, capability = detectSandboxCapability()) {
44
+ return [
45
+ capability.permissionFlag,
46
+ `--allow-fs-read=${entry}`,
47
+ "--disallow-code-generation-from-strings",
48
+ entry,
49
+ SANDBOX_MAIN_FLAG,
50
+ ];
51
+ }
52
+ /** 子进程环境:空;Electron 下以 Node 方式运行同一可执行文件;Windows 保留 SystemRoot。 */
53
+ export function sandboxEnv(versions = process.versions, platform = process.platform, parentEnv = process.env) {
54
+ const env = {};
55
+ if (versions.electron !== undefined)
56
+ env["ELECTRON_RUN_AS_NODE"] = "1";
57
+ if (platform === "win32") {
58
+ const root = parentEnv["SystemRoot"] ?? parentEnv["SYSTEMROOT"];
59
+ if (root !== undefined)
60
+ env["SystemRoot"] = root;
61
+ }
62
+ return env;
63
+ }
64
+ export async function runSandbox(request) {
65
+ const started = Date.now();
66
+ const entry = request.entry ?? resolveSandboxEntry();
67
+ const base = {
68
+ outputs: [],
69
+ timedOut: false,
70
+ aborted: false,
71
+ toolCalls: 0,
72
+ droppedChars: 0,
73
+ };
74
+ if (entry === undefined) {
75
+ return {
76
+ ...base,
77
+ ok: false,
78
+ error: "codemode sandbox entry not found (build the package first)",
79
+ elapsedMs: 0,
80
+ };
81
+ }
82
+ if (request.signal.aborted) {
83
+ return { ...base, ok: false, error: "Script aborted", aborted: true, elapsedMs: 0 };
84
+ }
85
+ const isWindows = process.platform === "win32";
86
+ const child = spawn(request.nodePath ?? process.execPath, sandboxArgs(entry, request.capability), {
87
+ env: sandboxEnv(),
88
+ stdio: ["pipe", "pipe", "pipe"],
89
+ detached: !isWindows,
90
+ windowsHide: true,
91
+ });
92
+ const pid = child.pid;
93
+ if (pid !== undefined && !isWindows)
94
+ trackProcessGroup(pid);
95
+ const calls = new AbortController();
96
+ const result = { ...base, ok: false, elapsedMs: 0 };
97
+ if (pid !== undefined)
98
+ result.pid = pid;
99
+ let collected = 0;
100
+ let stderr = "";
101
+ let settled = false;
102
+ let finish;
103
+ const finished = new Promise((resolve) => (finish = resolve));
104
+ const write = (message) => {
105
+ if (child.stdin.destroyed || !child.stdin.writable)
106
+ return;
107
+ child.stdin.write(encodeLine(message));
108
+ };
109
+ const kill = () => {
110
+ if (pid === undefined)
111
+ return;
112
+ void killProcessTree(pid, { graceMs: 200 }).finally(() => {
113
+ if (!isWindows)
114
+ untrackProcessGroup(pid);
115
+ });
116
+ };
117
+ const settle = (patch) => {
118
+ if (settled)
119
+ return;
120
+ settled = true;
121
+ Object.assign(result, patch);
122
+ result.elapsedMs = patch.elapsedMs ?? Date.now() - started;
123
+ calls.abort();
124
+ finish();
125
+ };
126
+ const timer = setTimeout(() => {
127
+ settle({
128
+ ok: false,
129
+ timedOut: true,
130
+ error: `Script timed out after ${request.options.timeoutMs} ms`,
131
+ });
132
+ kill();
133
+ }, request.options.timeoutMs);
134
+ let graceTimer;
135
+ const onAbort = () => {
136
+ write({ type: "abort" });
137
+ graceTimer = setTimeout(() => {
138
+ settle({ ok: false, aborted: true, error: "Script aborted" });
139
+ kill();
140
+ }, ABORT_GRACE_MS);
141
+ };
142
+ request.signal.addEventListener("abort", onAbort, { once: true });
143
+ child.stdin.on("error", () => { });
144
+ child.stderr.setEncoding("utf8");
145
+ child.stderr.on("data", (chunk) => {
146
+ stderr = (stderr + chunk).slice(-4000);
147
+ });
148
+ const splitter = new LineSplitter();
149
+ const onLine = (line) => {
150
+ if (settled)
151
+ return;
152
+ let message;
153
+ try {
154
+ message = decodeChildLine(line);
155
+ }
156
+ catch {
157
+ return;
158
+ }
159
+ switch (message.type) {
160
+ case "output": {
161
+ const text = message.text;
162
+ if (collected + text.length > MAX_COLLECTED_OUTPUT_CHARS) {
163
+ result.droppedChars += text.length;
164
+ return;
165
+ }
166
+ collected += text.length;
167
+ result.outputs.push(text);
168
+ request.onOutput?.(text);
169
+ return;
170
+ }
171
+ case "tool_call": {
172
+ const { id, name, input } = message;
173
+ result.toolCalls++;
174
+ if (name === "codemode") {
175
+ write({
176
+ type: "tool_result",
177
+ id,
178
+ ok: false,
179
+ error: "codemode cannot be called from a codemode script",
180
+ });
181
+ return;
182
+ }
183
+ request.callTool(name, input, calls.signal).then((value) => {
184
+ if (!settled)
185
+ write({ type: "tool_result", id, ok: true, value: value ?? null });
186
+ }, (error) => {
187
+ if (!settled) {
188
+ write({
189
+ type: "tool_result",
190
+ id,
191
+ ok: false,
192
+ error: error instanceof Error ? error.message : String(error),
193
+ });
194
+ }
195
+ });
196
+ return;
197
+ }
198
+ case "store":
199
+ result.store = message.entries;
200
+ return;
201
+ case "done": {
202
+ const patch = { ok: message.ok, elapsedMs: message.elapsedMs };
203
+ if (message.error !== undefined)
204
+ patch.error = message.error;
205
+ if (request.signal.aborted) {
206
+ patch.ok = false;
207
+ patch.aborted = true;
208
+ }
209
+ if (patch.ok !== true)
210
+ delete result.store;
211
+ settle(patch);
212
+ return;
213
+ }
214
+ }
215
+ };
216
+ child.stdout.setEncoding("utf8");
217
+ child.stdout.on("data", (chunk) => {
218
+ for (const line of splitter.push(chunk))
219
+ onLine(line);
220
+ });
221
+ child.on("error", (error) => settle({ ok: false, error: `Failed to start codemode sandbox: ${error.message}` }));
222
+ child.on("close", (code, signal) => {
223
+ for (const line of splitter.flush())
224
+ onLine(line);
225
+ if (pid !== undefined && !isWindows)
226
+ untrackProcessGroup(pid);
227
+ const detail = stderr.trim().split("\n").slice(-3).join(" | ");
228
+ settle({
229
+ ok: false,
230
+ error: `codemode sandbox exited unexpectedly (${signal ?? `code ${code}`})${detail === "" ? "" : `: ${detail}`}`,
231
+ });
232
+ });
233
+ write({
234
+ type: "run",
235
+ script: request.script,
236
+ options: request.options,
237
+ tools: request.tools,
238
+ store: request.store,
239
+ });
240
+ await finished;
241
+ clearTimeout(timer);
242
+ if (graceTimer !== undefined)
243
+ clearTimeout(graceTimer);
244
+ request.signal.removeEventListener("abort", onAbort);
245
+ if (child.exitCode === null && child.signalCode === null) {
246
+ // 正常结束时子进程会自行退出;兜底:稍后仍在就杀掉。
247
+ const reaper = setTimeout(kill, 1000);
248
+ reaper.unref();
249
+ child.once("close", () => clearTimeout(reaper));
250
+ }
251
+ child.stdin.end();
252
+ return result;
253
+ }