@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,38 @@
1
+ /**
2
+ * shell 选择与参数拼装(设计 §5.2 bash)。[B3]
3
+ *
4
+ * - POSIX:`AMA_SHELL` → `/bin/bash` → `sh`;参数 `-c <command>`。
5
+ * - Windows:`AMA_SHELL` → Git Bash 已知路径 → `powershell -NoProfile -NonInteractive -Command`;
6
+ * PowerShell 回退时在命令末尾追加 `$LASTEXITCODE` 透传,使 exit_code 取自它。
7
+ * - `AMA_SHELL` 按文件名判类型:`powershell` / `pwsh` → PowerShell,`cmd` → `cmd /d /s /c`,其它按 POSIX。
8
+ * - 工具环境:继承进程环境并注入 `AMA_SESSION_ID / AMA_SESSION_FILE / AMA_PROVIDER / AMA_MODEL /
9
+ * AMA_THINKING / AMA_DEPTH`(每条命令启动时取值;缺值则删掉继承来的旧值)。
10
+ */
11
+ export type ShellKind = "posix" | "powershell" | "cmd";
12
+ export interface ShellConfig {
13
+ shell: string;
14
+ kind: ShellKind;
15
+ }
16
+ export interface ShellDeps {
17
+ platform?: NodeJS.Platform;
18
+ env?: NodeJS.ProcessEnv;
19
+ exists?(path: string): boolean;
20
+ }
21
+ export declare const POWERSHELL_ARGS: readonly ["-NoProfile", "-NonInteractive", "-Command"];
22
+ export declare function shellKindOf(shell: string): ShellKind;
23
+ /** Windows 上 Git Bash 的已知安装位置(按优先级)。 */
24
+ export declare function gitBashCandidates(env: NodeJS.ProcessEnv): string[];
25
+ export declare function resolveShell(deps?: ShellDeps): ShellConfig;
26
+ /** PowerShell:命令后透传原生程序的 `$LASTEXITCODE`;cmdlet 失败(`$?` 为假)给 1。 */
27
+ export declare function wrapPowerShellCommand(command: string): string;
28
+ export declare function buildShellArgs(config: ShellConfig, command: string): string[];
29
+ export interface ToolEnvInput {
30
+ sessionId: string;
31
+ sessionFile?: string | undefined;
32
+ provider?: string | undefined;
33
+ model?: string | undefined;
34
+ thinkingLevel?: string | undefined;
35
+ depth: number;
36
+ }
37
+ export declare const INJECTED_ENV_VARS: readonly ["AMA_SESSION_ID", "AMA_SESSION_FILE", "AMA_PROVIDER", "AMA_MODEL", "AMA_THINKING", "AMA_DEPTH"];
38
+ export declare function buildToolEnv(base: NodeJS.ProcessEnv, info: ToolEnvInput): NodeJS.ProcessEnv;
@@ -0,0 +1,91 @@
1
+ /**
2
+ * shell 选择与参数拼装(设计 §5.2 bash)。[B3]
3
+ *
4
+ * - POSIX:`AMA_SHELL` → `/bin/bash` → `sh`;参数 `-c <command>`。
5
+ * - Windows:`AMA_SHELL` → Git Bash 已知路径 → `powershell -NoProfile -NonInteractive -Command`;
6
+ * PowerShell 回退时在命令末尾追加 `$LASTEXITCODE` 透传,使 exit_code 取自它。
7
+ * - `AMA_SHELL` 按文件名判类型:`powershell` / `pwsh` → PowerShell,`cmd` → `cmd /d /s /c`,其它按 POSIX。
8
+ * - 工具环境:继承进程环境并注入 `AMA_SESSION_ID / AMA_SESSION_FILE / AMA_PROVIDER / AMA_MODEL /
9
+ * AMA_THINKING / AMA_DEPTH`(每条命令启动时取值;缺值则删掉继承来的旧值)。
10
+ */
11
+ import { existsSync } from "node:fs";
12
+ export const POWERSHELL_ARGS = ["-NoProfile", "-NonInteractive", "-Command"];
13
+ export function shellKindOf(shell) {
14
+ const base = (shell.split(/[\\/]/).pop() ?? shell).toLowerCase().replace(/\.exe$/, "");
15
+ if (base === "powershell" || base === "pwsh")
16
+ return "powershell";
17
+ if (base === "cmd")
18
+ return "cmd";
19
+ return "posix";
20
+ }
21
+ /** Windows 上 Git Bash 的已知安装位置(按优先级)。 */
22
+ export function gitBashCandidates(env) {
23
+ const out = [];
24
+ const add = (root, rest) => {
25
+ if (root)
26
+ out.push(`${root.replace(/[\\/]+$/, "")}\\${rest}`);
27
+ };
28
+ add(env["ProgramFiles"], "Git\\bin\\bash.exe");
29
+ add(env["ProgramW6432"], "Git\\bin\\bash.exe");
30
+ add(env["ProgramFiles(x86)"], "Git\\bin\\bash.exe");
31
+ add(env["LOCALAPPDATA"], "Programs\\Git\\bin\\bash.exe");
32
+ out.push("C:\\Program Files\\Git\\bin\\bash.exe");
33
+ return [...new Set(out)];
34
+ }
35
+ export function resolveShell(deps = {}) {
36
+ const platform = deps.platform ?? process.platform;
37
+ const env = deps.env ?? process.env;
38
+ const exists = deps.exists ?? existsSync;
39
+ const custom = env["AMA_SHELL"]?.trim();
40
+ if (custom)
41
+ return { shell: custom, kind: shellKindOf(custom) };
42
+ if (platform === "win32") {
43
+ for (const candidate of gitBashCandidates(env)) {
44
+ if (exists(candidate))
45
+ return { shell: candidate, kind: "posix" };
46
+ }
47
+ return { shell: "powershell.exe", kind: "powershell" };
48
+ }
49
+ if (exists("/bin/bash"))
50
+ return { shell: "/bin/bash", kind: "posix" };
51
+ return { shell: "sh", kind: "posix" };
52
+ }
53
+ /** PowerShell:命令后透传原生程序的 `$LASTEXITCODE`;cmdlet 失败(`$?` 为假)给 1。 */
54
+ export function wrapPowerShellCommand(command) {
55
+ return (`${command}\n` +
56
+ "if ($null -ne $LASTEXITCODE) { exit $LASTEXITCODE } elseif (-not $?) { exit 1 } else { exit 0 }");
57
+ }
58
+ export function buildShellArgs(config, command) {
59
+ switch (config.kind) {
60
+ case "powershell":
61
+ return [...POWERSHELL_ARGS, wrapPowerShellCommand(command)];
62
+ case "cmd":
63
+ return ["/d", "/s", "/c", command];
64
+ default:
65
+ return ["-c", command];
66
+ }
67
+ }
68
+ export const INJECTED_ENV_VARS = [
69
+ "AMA_SESSION_ID",
70
+ "AMA_SESSION_FILE",
71
+ "AMA_PROVIDER",
72
+ "AMA_MODEL",
73
+ "AMA_THINKING",
74
+ "AMA_DEPTH",
75
+ ];
76
+ export function buildToolEnv(base, info) {
77
+ const env = { ...base };
78
+ for (const key of INJECTED_ENV_VARS)
79
+ delete env[key];
80
+ const set = (key, value) => {
81
+ if (value !== undefined && value !== "")
82
+ env[key] = value;
83
+ };
84
+ set("AMA_SESSION_ID", info.sessionId);
85
+ set("AMA_SESSION_FILE", info.sessionFile);
86
+ set("AMA_PROVIDER", info.provider);
87
+ set("AMA_MODEL", info.model);
88
+ set("AMA_THINKING", info.thinkingLevel);
89
+ set("AMA_DEPTH", String(info.depth));
90
+ return env;
91
+ }
@@ -0,0 +1,29 @@
1
+ /**
2
+ * `task` 工具:同进程子 Agent(设计 §5.2、D14)。[B3]
3
+ *
4
+ * 子会话由 `ctx.spawnSubagent`(B2 的 AgentSession 提供)创建:独立 JSONL、继承权限模式与 broker、
5
+ * 审批串到父。本工具负责:
6
+ * - 深度 ≤ 1:`ctx.depth ≥ 1` 或无 `spawnSubagent` → 错误(子 Agent 里没有 task);
7
+ * - 并发 ≤ 4:由会话的 `SubagentPool`(agent/session-subagent.ts,`subagents.maxConcurrent`)排队,
8
+ * 本工具不再自带一层信号量;排队中父 abort → 子会话抛 aborted,这里返回 aborted;
9
+ * - 工具子集:`tools` 里去掉 `task`(缺省交给 spawnSubagent = 父活动集去掉 task);
10
+ * - 父 abort 级联:把 `ctx.signal` 传给子;
11
+ * - 结果 = 子的最后助手文本 + `details{ sessionFile, usage, stopReason }`。
12
+ */
13
+ import type { ModelThinkingLevel } from "../ai/types.js";
14
+ import type { SubagentRequest, ToolContext, ToolDefinition } from "./types.js";
15
+ export declare const MAX_TASK_DEPTH = 1;
16
+ export declare const DEFAULT_TASK_MAX_TURNS = 30;
17
+ export interface TaskInput {
18
+ prompt: string;
19
+ description?: string;
20
+ tools?: string[];
21
+ model?: string;
22
+ thinkingLevel?: ModelThinkingLevel;
23
+ maxTurns?: number;
24
+ }
25
+ /** 预留(并发上限在会话的 SubagentPool,见文件头)。 */
26
+ export interface TaskToolOptions {
27
+ }
28
+ export declare function buildSubagentRequest(input: TaskInput, ctx: ToolContext): SubagentRequest | string;
29
+ export declare function createTaskTool(_options?: TaskToolOptions): ToolDefinition<TaskInput>;
@@ -0,0 +1,107 @@
1
+ /**
2
+ * `task` 工具:同进程子 Agent(设计 §5.2、D14)。[B3]
3
+ *
4
+ * 子会话由 `ctx.spawnSubagent`(B2 的 AgentSession 提供)创建:独立 JSONL、继承权限模式与 broker、
5
+ * 审批串到父。本工具负责:
6
+ * - 深度 ≤ 1:`ctx.depth ≥ 1` 或无 `spawnSubagent` → 错误(子 Agent 里没有 task);
7
+ * - 并发 ≤ 4:由会话的 `SubagentPool`(agent/session-subagent.ts,`subagents.maxConcurrent`)排队,
8
+ * 本工具不再自带一层信号量;排队中父 abort → 子会话抛 aborted,这里返回 aborted;
9
+ * - 工具子集:`tools` 里去掉 `task`(缺省交给 spawnSubagent = 父活动集去掉 task);
10
+ * - 父 abort 级联:把 `ctx.signal` 传给子;
11
+ * - 结果 = 子的最后助手文本 + `details{ sessionFile, usage, stopReason }`。
12
+ */
13
+ export const MAX_TASK_DEPTH = 1;
14
+ export const DEFAULT_TASK_MAX_TURNS = 30;
15
+ const THINKING = [
16
+ "off",
17
+ "minimal",
18
+ "low",
19
+ "medium",
20
+ "high",
21
+ "xhigh",
22
+ ];
23
+ function fail(message) {
24
+ return { content: message, isError: true };
25
+ }
26
+ export function buildSubagentRequest(input, ctx) {
27
+ if (typeof input.prompt !== "string" || input.prompt.trim() === "") {
28
+ return "prompt must be a non-empty string";
29
+ }
30
+ const maxTurns = input.maxTurns ?? DEFAULT_TASK_MAX_TURNS;
31
+ if (!Number.isInteger(maxTurns) || maxTurns < 1)
32
+ return "maxTurns must be an integer ≥ 1";
33
+ if (input.thinkingLevel !== undefined && !THINKING.includes(input.thinkingLevel)) {
34
+ return `thinkingLevel must be one of ${THINKING.join(", ")}`;
35
+ }
36
+ const request = {
37
+ prompt: input.prompt,
38
+ maxTurns,
39
+ parentToolCallId: ctx.toolCallId,
40
+ signal: ctx.signal,
41
+ onUpdate: (partial) => ctx.onUpdate(partial),
42
+ };
43
+ if (input.description !== undefined)
44
+ request.description = input.description;
45
+ if (input.tools !== undefined)
46
+ request.tools = input.tools.filter((t) => t !== "task");
47
+ if (input.model !== undefined)
48
+ request.model = input.model;
49
+ if (input.thinkingLevel !== undefined)
50
+ request.thinkingLevel = input.thinkingLevel;
51
+ return request;
52
+ }
53
+ export function createTaskTool(_options = {}) {
54
+ return {
55
+ name: "task",
56
+ label: "Task",
57
+ description: "Delegate to a fresh sub-agent. It cannot see this chat: give full instructions. " +
58
+ "Returns its final answer.",
59
+ parameters: {
60
+ type: "object",
61
+ properties: {
62
+ prompt: { type: "string" },
63
+ description: { type: "string", description: "Label" },
64
+ tools: {
65
+ type: "array",
66
+ items: { type: "string" },
67
+ description: "Default: all but task",
68
+ },
69
+ model: { type: "string", description: "provider/model" },
70
+ thinkingLevel: { type: "string", enum: [...THINKING] },
71
+ maxTurns: { type: "integer", description: `Default ${DEFAULT_TASK_MAX_TURNS}` },
72
+ },
73
+ required: ["prompt"],
74
+ additionalProperties: false,
75
+ },
76
+ permission: "execute",
77
+ executionMode: "sequential",
78
+ promptSnippet: "task: run a sub-agent",
79
+ async execute(input, ctx) {
80
+ if (ctx.depth >= MAX_TASK_DEPTH || ctx.spawnSubagent === undefined) {
81
+ return fail("Sub-agents are not available here (nested tasks are not allowed).");
82
+ }
83
+ const request = buildSubagentRequest(input, ctx);
84
+ if (typeof request === "string")
85
+ return fail(request);
86
+ if (ctx.signal.aborted)
87
+ return fail("aborted by user");
88
+ try {
89
+ const result = await ctx.spawnSubagent(request);
90
+ const details = {
91
+ ...(result.sessionFile !== undefined ? { sessionFile: result.sessionFile } : {}),
92
+ usage: result.usage,
93
+ stopReason: result.stopReason,
94
+ };
95
+ if (ctx.signal.aborted)
96
+ return { content: "aborted by user", isError: true, details };
97
+ const text = result.text.trim() === "" ? "(the sub-agent returned no text)" : result.text;
98
+ return { content: text, isError: result.isError, details };
99
+ }
100
+ catch (err) {
101
+ if (ctx.signal.aborted)
102
+ return fail("aborted by user");
103
+ return fail(`Sub-agent failed: ${err.message}`);
104
+ }
105
+ },
106
+ };
107
+ }
@@ -0,0 +1,28 @@
1
+ /**
2
+ * `todo` 工具(设计 §5.2)。[B3]
3
+ *
4
+ * 会话内任务清单:`set` 整表替换并写 `custom{customType:"ama.todo"}` 条目(不进上下文),`get`
5
+ * 读活动分支上最近一条。`renderTodoList()` 给 TUI / 结果文本共用。待定项「todo 是否进系统提示」:
6
+ * 第一期不进(只有 promptSnippet 一行说明工具存在)。
7
+ */
8
+ import type { ToolContext, ToolDefinition, ToolResult } from "./types.js";
9
+ export declare const TODO_CUSTOM_TYPE = "ama.todo";
10
+ export type TodoStatus = "pending" | "in_progress" | "done";
11
+ export interface TodoItem {
12
+ id: string;
13
+ text: string;
14
+ status: TodoStatus;
15
+ }
16
+ export interface TodoState {
17
+ items: TodoItem[];
18
+ }
19
+ export interface TodoInput {
20
+ action: "set" | "get";
21
+ items?: TodoItem[];
22
+ }
23
+ export declare function validateTodoItems(items: unknown): string | undefined;
24
+ /** 读当前清单;无记录或形状不对返回空表。 */
25
+ export declare function readTodoState(ctx: Pick<ToolContext, "session">): TodoState;
26
+ export declare function renderTodoList(items: readonly TodoItem[]): string[];
27
+ export declare function executeTodo(input: TodoInput, ctx: ToolContext): Promise<ToolResult>;
28
+ export declare function createTodoTool(): ToolDefinition<TodoInput>;
@@ -0,0 +1,107 @@
1
+ /**
2
+ * `todo` 工具(设计 §5.2)。[B3]
3
+ *
4
+ * 会话内任务清单:`set` 整表替换并写 `custom{customType:"ama.todo"}` 条目(不进上下文),`get`
5
+ * 读活动分支上最近一条。`renderTodoList()` 给 TUI / 结果文本共用。待定项「todo 是否进系统提示」:
6
+ * 第一期不进(只有 promptSnippet 一行说明工具存在)。
7
+ */
8
+ export const TODO_CUSTOM_TYPE = "ama.todo";
9
+ const STATUSES = ["pending", "in_progress", "done"];
10
+ const MARK = { pending: "[ ]", in_progress: "[~]", done: "[x]" };
11
+ export function validateTodoItems(items) {
12
+ if (!Array.isArray(items))
13
+ return "items must be an array";
14
+ const ids = new Set();
15
+ for (const [i, item] of items.entries()) {
16
+ if (typeof item !== "object" || item === null)
17
+ return `items[${i}] must be an object`;
18
+ const { id, text, status } = item;
19
+ if (typeof id !== "string" || id === "")
20
+ return `items[${i}].id must be a non-empty string`;
21
+ if (typeof text !== "string" || text === "")
22
+ return `items[${i}].text must be a non-empty string`;
23
+ if (!STATUSES.includes(status)) {
24
+ return `items[${i}].status must be one of ${STATUSES.join(", ")}`;
25
+ }
26
+ if (ids.has(id))
27
+ return `duplicate id "${id}"`;
28
+ ids.add(id);
29
+ }
30
+ return undefined;
31
+ }
32
+ /** 读当前清单;无记录或形状不对返回空表。 */
33
+ export function readTodoState(ctx) {
34
+ const data = ctx.session.lastCustom(TODO_CUSTOM_TYPE);
35
+ if (typeof data === "object" && data !== null && Array.isArray(data.items)) {
36
+ const items = data.items;
37
+ if (validateTodoItems(items) === undefined)
38
+ return { items: items.map((it) => ({ ...it })) };
39
+ }
40
+ return { items: [] };
41
+ }
42
+ export function renderTodoList(items) {
43
+ if (items.length === 0)
44
+ return ["(no tasks)"];
45
+ return items.map((it) => `${MARK[it.status]} ${it.id}. ${it.text}`);
46
+ }
47
+ function summary(items) {
48
+ const done = items.filter((it) => it.status === "done").length;
49
+ return `${done}/${items.length} done`;
50
+ }
51
+ export async function executeTodo(input, ctx) {
52
+ if (input.action === "get") {
53
+ const state = readTodoState(ctx);
54
+ return {
55
+ content: [`Tasks (${summary(state.items)}):`, ...renderTodoList(state.items)].join("\n"),
56
+ details: state,
57
+ };
58
+ }
59
+ if (input.action !== "set")
60
+ return { content: 'action must be "set" or "get"', isError: true };
61
+ const problem = validateTodoItems(input.items ?? []);
62
+ if (problem !== undefined)
63
+ return { content: problem, isError: true };
64
+ const state = { items: (input.items ?? []).map((it) => ({ ...it })) };
65
+ ctx.session.appendCustom(TODO_CUSTOM_TYPE, state);
66
+ return {
67
+ content: [`Updated tasks (${summary(state.items)}):`, ...renderTodoList(state.items)].join("\n"),
68
+ details: state,
69
+ };
70
+ }
71
+ export function createTodoTool() {
72
+ return {
73
+ name: "todo",
74
+ label: "Todo",
75
+ description: "Session task list for multi-step work: `set` replaces the whole list, `get` returns it.",
76
+ parameters: {
77
+ type: "object",
78
+ properties: {
79
+ action: { type: "string", enum: ["set", "get"] },
80
+ items: {
81
+ type: "array",
82
+ description: "Full list (set)",
83
+ items: {
84
+ type: "object",
85
+ properties: {
86
+ id: { type: "string" },
87
+ text: { type: "string" },
88
+ status: { type: "string", enum: ["pending", "in_progress", "done"] },
89
+ },
90
+ required: ["id", "text", "status"],
91
+ additionalProperties: false,
92
+ },
93
+ },
94
+ },
95
+ required: ["action"],
96
+ additionalProperties: false,
97
+ },
98
+ permission: "read",
99
+ executionMode: "parallel",
100
+ promptSnippet: "todo: track multi-step work",
101
+ execute: executeTodo,
102
+ renderResult: (result) => {
103
+ const state = result.details;
104
+ return state && Array.isArray(state.items) ? renderTodoList(state.items) : [];
105
+ },
106
+ };
107
+ }
@@ -0,0 +1,44 @@
1
+ /**
2
+ * 头 / 尾截断(行数 + 字节双阈值,先到者为准)与全文落盘(设计 §5.2、§4.4)。[B3]
3
+ *
4
+ * - read / grep 用**头截断**(保留开头,提示 offset 续读);bash 用**尾截断**(保留结尾)。
5
+ * - 字节按 UTF-8 计;不切断多字节字符;单行超过字节上限时按字符边界截该行。
6
+ */
7
+ export declare const DEFAULT_MAX_LINES = 2000;
8
+ export declare const DEFAULT_MAX_BYTES: number;
9
+ /** grep 匹配行的字符上限。 */
10
+ export declare const GREP_MAX_LINE_CHARS = 500;
11
+ export interface TruncateOptions {
12
+ maxLines?: number;
13
+ maxBytes?: number;
14
+ }
15
+ export interface TruncateResult {
16
+ content: string;
17
+ truncated: boolean;
18
+ /** 哪个阈值先到;未截断为 null。 */
19
+ truncatedBy: "lines" | "bytes" | null;
20
+ totalLines: number;
21
+ totalBytes: number;
22
+ outputLines: number;
23
+ outputBytes: number;
24
+ }
25
+ export declare function byteLength(text: string): number;
26
+ /** 末尾换行不算一行:`"a\nb\n"` 是 2 行。 */
27
+ export declare function splitLines(text: string): string[];
28
+ /** 按 UTF-8 字节数从头截取,不切断字符。 */
29
+ export declare function sliceHeadBytes(text: string, maxBytes: number): string;
30
+ /** 按 UTF-8 字节数从尾截取,不切断字符。 */
31
+ export declare function sliceTailBytes(text: string, maxBytes: number): string;
32
+ /** 保留开头。 */
33
+ export declare function truncateHead(text: string, options?: TruncateOptions): TruncateResult;
34
+ /** 保留结尾。 */
35
+ export declare function truncateTail(text: string, options?: TruncateOptions): TruncateResult;
36
+ /** 单行按字符截断,附省略提示。 */
37
+ export declare function truncateLine(line: string, maxChars?: number): string;
38
+ export declare function formatSize(bytes: number): string;
39
+ /** 截断全文的落盘目录:`ctx.outputDir`,内存会话落到系统临时目录。 */
40
+ export declare function resolveOutputDir(outputDir: string | undefined): string;
41
+ /** 文件名里只保留安全字符。 */
42
+ export declare function safeFileName(name: string): string;
43
+ /** 写全文,返回绝对路径。 */
44
+ export declare function writeFullOutput(outputDir: string | undefined, fileName: string, content: string | Buffer): string;
@@ -0,0 +1,175 @@
1
+ /**
2
+ * 头 / 尾截断(行数 + 字节双阈值,先到者为准)与全文落盘(设计 §5.2、§4.4)。[B3]
3
+ *
4
+ * - read / grep 用**头截断**(保留开头,提示 offset 续读);bash 用**尾截断**(保留结尾)。
5
+ * - 字节按 UTF-8 计;不切断多字节字符;单行超过字节上限时按字符边界截该行。
6
+ */
7
+ import { mkdirSync, writeFileSync } from "node:fs";
8
+ import { tmpdir } from "node:os";
9
+ import { join } from "node:path";
10
+ export const DEFAULT_MAX_LINES = 2000;
11
+ export const DEFAULT_MAX_BYTES = 50 * 1024;
12
+ /** grep 匹配行的字符上限。 */
13
+ export const GREP_MAX_LINE_CHARS = 500;
14
+ export function byteLength(text) {
15
+ return Buffer.byteLength(text, "utf8");
16
+ }
17
+ /** 末尾换行不算一行:`"a\nb\n"` 是 2 行。 */
18
+ export function splitLines(text) {
19
+ if (text === "")
20
+ return [];
21
+ const lines = text.split("\n");
22
+ if (lines[lines.length - 1] === "")
23
+ lines.pop();
24
+ return lines;
25
+ }
26
+ /** 按 UTF-8 字节数从头截取,不切断字符。 */
27
+ export function sliceHeadBytes(text, maxBytes) {
28
+ const buf = Buffer.from(text, "utf8");
29
+ if (buf.length <= maxBytes)
30
+ return text;
31
+ let end = maxBytes;
32
+ // 回退到字符起始字节(10xxxxxx 是续字节)。
33
+ while (end > 0 && ((buf[end] ?? 0) & 0xc0) === 0x80)
34
+ end--;
35
+ return buf.subarray(0, end).toString("utf8");
36
+ }
37
+ /** 按 UTF-8 字节数从尾截取,不切断字符。 */
38
+ export function sliceTailBytes(text, maxBytes) {
39
+ const buf = Buffer.from(text, "utf8");
40
+ if (buf.length <= maxBytes)
41
+ return text;
42
+ let start = buf.length - maxBytes;
43
+ while (start < buf.length && ((buf[start] ?? 0) & 0xc0) === 0x80)
44
+ start++;
45
+ return buf.subarray(start).toString("utf8");
46
+ }
47
+ function limits(options) {
48
+ return {
49
+ maxLines: options.maxLines ?? DEFAULT_MAX_LINES,
50
+ maxBytes: options.maxBytes ?? DEFAULT_MAX_BYTES,
51
+ };
52
+ }
53
+ /** 保留开头。 */
54
+ export function truncateHead(text, options = {}) {
55
+ const { maxLines, maxBytes } = limits(options);
56
+ const lines = splitLines(text);
57
+ const totalBytes = byteLength(text);
58
+ if (lines.length <= maxLines && totalBytes <= maxBytes) {
59
+ return {
60
+ content: text,
61
+ truncated: false,
62
+ truncatedBy: null,
63
+ totalLines: lines.length,
64
+ totalBytes,
65
+ outputLines: lines.length,
66
+ outputBytes: totalBytes,
67
+ };
68
+ }
69
+ const out = [];
70
+ let bytes = 0;
71
+ let truncatedBy = "lines";
72
+ for (const line of lines) {
73
+ if (out.length >= maxLines) {
74
+ truncatedBy = "lines";
75
+ break;
76
+ }
77
+ const cost = byteLength(line) + (out.length > 0 ? 1 : 0);
78
+ if (bytes + cost > maxBytes) {
79
+ truncatedBy = "bytes";
80
+ if (out.length === 0)
81
+ out.push(sliceHeadBytes(line, maxBytes));
82
+ break;
83
+ }
84
+ out.push(line);
85
+ bytes += cost;
86
+ }
87
+ const content = out.join("\n");
88
+ return {
89
+ content,
90
+ truncated: true,
91
+ truncatedBy,
92
+ totalLines: lines.length,
93
+ totalBytes,
94
+ outputLines: out.length,
95
+ outputBytes: byteLength(content),
96
+ };
97
+ }
98
+ /** 保留结尾。 */
99
+ export function truncateTail(text, options = {}) {
100
+ const { maxLines, maxBytes } = limits(options);
101
+ const lines = splitLines(text);
102
+ const totalBytes = byteLength(text);
103
+ if (lines.length <= maxLines && totalBytes <= maxBytes) {
104
+ return {
105
+ content: text,
106
+ truncated: false,
107
+ truncatedBy: null,
108
+ totalLines: lines.length,
109
+ totalBytes,
110
+ outputLines: lines.length,
111
+ outputBytes: totalBytes,
112
+ };
113
+ }
114
+ const out = [];
115
+ let bytes = 0;
116
+ let truncatedBy = "lines";
117
+ for (let i = lines.length - 1; i >= 0; i--) {
118
+ if (out.length >= maxLines) {
119
+ truncatedBy = "lines";
120
+ break;
121
+ }
122
+ const line = lines[i] ?? "";
123
+ const cost = byteLength(line) + (out.length > 0 ? 1 : 0);
124
+ if (bytes + cost > maxBytes) {
125
+ truncatedBy = "bytes";
126
+ if (out.length === 0)
127
+ out.push(sliceTailBytes(line, maxBytes));
128
+ break;
129
+ }
130
+ out.push(line);
131
+ bytes += cost;
132
+ }
133
+ out.reverse();
134
+ const content = out.join("\n");
135
+ return {
136
+ content,
137
+ truncated: true,
138
+ truncatedBy,
139
+ totalLines: lines.length,
140
+ totalBytes,
141
+ outputLines: out.length,
142
+ outputBytes: byteLength(content),
143
+ };
144
+ }
145
+ /** 单行按字符截断,附省略提示。 */
146
+ export function truncateLine(line, maxChars = GREP_MAX_LINE_CHARS) {
147
+ const chars = Array.from(line);
148
+ if (chars.length <= maxChars)
149
+ return line;
150
+ return `${chars.slice(0, maxChars).join("")}… [${chars.length - maxChars} more chars]`;
151
+ }
152
+ export function formatSize(bytes) {
153
+ if (bytes < 1024)
154
+ return `${bytes} B`;
155
+ if (bytes < 1024 * 1024)
156
+ return `${(bytes / 1024).toFixed(1)} KB`;
157
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
158
+ }
159
+ /** 截断全文的落盘目录:`ctx.outputDir`,内存会话落到系统临时目录。 */
160
+ export function resolveOutputDir(outputDir) {
161
+ return outputDir ?? join(tmpdir(), "ama-outputs");
162
+ }
163
+ /** 文件名里只保留安全字符。 */
164
+ export function safeFileName(name) {
165
+ const cleaned = name.replace(/[^A-Za-z0-9._-]/g, "_");
166
+ return cleaned === "" ? "output" : cleaned.slice(0, 120);
167
+ }
168
+ /** 写全文,返回绝对路径。 */
169
+ export function writeFullOutput(outputDir, fileName, content) {
170
+ const dir = resolveOutputDir(outputDir);
171
+ mkdirSync(dir, { recursive: true });
172
+ const target = join(dir, safeFileName(fileName));
173
+ writeFileSync(target, content);
174
+ return target;
175
+ }