@armadra/agent 0.4.0 → 0.5.1

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 (432) hide show
  1. package/CHANGELOG.md +184 -1
  2. package/README.md +202 -83
  3. package/THIRD_PARTY_NOTICES.md +34 -0
  4. package/dist/acp.d.ts +18 -0
  5. package/dist/acp.js +19 -0
  6. package/dist/agent/limits.d.ts +38 -0
  7. package/dist/agent/limits.js +127 -0
  8. package/dist/agent/loop-guard.d.ts +38 -0
  9. package/dist/agent/loop-guard.js +95 -0
  10. package/dist/agent/loop.d.ts +4 -0
  11. package/dist/agent/loop.js +7 -0
  12. package/dist/agent/prompt-rules.d.ts +12 -0
  13. package/dist/agent/prompt-rules.js +18 -0
  14. package/dist/agent/reminders.d.ts +59 -0
  15. package/dist/agent/reminders.js +327 -0
  16. package/dist/agent/session-cache.d.ts +5 -0
  17. package/dist/agent/session-cache.js +17 -1
  18. package/dist/agent/session-classifier.d.ts +1 -1
  19. package/dist/agent/session-classifier.js +3 -1
  20. package/dist/agent/session-compaction.d.ts +35 -6
  21. package/dist/agent/session-compaction.js +157 -31
  22. package/dist/agent/session-core.d.ts +27 -1
  23. package/dist/agent/session-extensions.d.ts +61 -0
  24. package/dist/agent/session-extensions.js +129 -0
  25. package/dist/agent/session-images.d.ts +23 -0
  26. package/dist/agent/session-images.js +97 -0
  27. package/dist/agent/session-plan.d.ts +21 -0
  28. package/dist/agent/session-plan.js +524 -0
  29. package/dist/agent/session-rewind.d.ts +81 -0
  30. package/dist/agent/session-rewind.js +354 -0
  31. package/dist/agent/session-run.d.ts +28 -1
  32. package/dist/agent/session-run.js +157 -6
  33. package/dist/agent/session-settings.d.ts +49 -0
  34. package/dist/agent/session-settings.js +95 -0
  35. package/dist/agent/session-subagent.d.ts +36 -24
  36. package/dist/agent/session-subagent.js +259 -129
  37. package/dist/agent/session-sync.d.ts +10 -3
  38. package/dist/agent/session-sync.js +37 -8
  39. package/dist/agent/session-telemetry.d.ts +39 -0
  40. package/dist/agent/session-telemetry.js +231 -0
  41. package/dist/agent/session-tools.js +40 -1
  42. package/dist/agent/session.d.ts +30 -29
  43. package/dist/agent/session.js +79 -123
  44. package/dist/agent/subagent-registry.d.ts +91 -0
  45. package/dist/agent/subagent-registry.js +494 -0
  46. package/dist/agent/system-prompt.d.ts +6 -3
  47. package/dist/agent/system-prompt.js +7 -2
  48. package/dist/agent/tool-runner.d.ts +8 -1
  49. package/dist/agent/tool-runner.js +57 -4
  50. package/dist/agent/types-w5.d.ts +192 -0
  51. package/dist/agent/types-w5.js +10 -0
  52. package/dist/agent/types.d.ts +47 -4
  53. package/dist/agent/types.js +2 -0
  54. package/dist/agent/worktree.d.ts +36 -0
  55. package/dist/agent/worktree.js +86 -0
  56. package/dist/agents/builtin.d.ts +14 -0
  57. package/dist/agents/builtin.js +39 -0
  58. package/dist/agents/catalog.d.ts +43 -0
  59. package/dist/agents/catalog.js +82 -0
  60. package/dist/agents/discover.d.ts +35 -0
  61. package/dist/agents/discover.js +83 -0
  62. package/dist/agents/external.d.ts +82 -0
  63. package/dist/agents/external.js +326 -0
  64. package/dist/agents/parse.d.ts +18 -0
  65. package/dist/agents/parse.js +116 -0
  66. package/dist/agents/result.d.ts +32 -0
  67. package/dist/agents/result.js +67 -0
  68. package/dist/agents/task-control.d.ts +17 -0
  69. package/dist/agents/task-control.js +17 -0
  70. package/dist/agents/task-record.d.ts +78 -0
  71. package/dist/agents/task-record.js +141 -0
  72. package/dist/agents/types.d.ts +46 -0
  73. package/dist/agents/types.js +8 -0
  74. package/dist/ai/apis/anthropic-compat.d.ts +36 -0
  75. package/dist/ai/apis/anthropic-compat.js +92 -0
  76. package/dist/ai/apis/anthropic-request.d.ts +7 -6
  77. package/dist/ai/apis/anthropic-request.js +10 -28
  78. package/dist/ai/apis/cache-params.d.ts +11 -2
  79. package/dist/ai/apis/cache-params.js +25 -6
  80. package/dist/ai/apis/openai-compat.d.ts +4 -1
  81. package/dist/ai/apis/openai-compat.js +8 -1
  82. package/dist/ai/image-limits.d.ts +45 -0
  83. package/dist/ai/image-limits.js +68 -0
  84. package/dist/ai/providers/builtin.d.ts +18 -6
  85. package/dist/ai/providers/builtin.js +135 -6
  86. package/dist/ai/providers/catalog-data.js +17 -13
  87. package/dist/ai/providers/catalog.d.ts +60 -7
  88. package/dist/ai/providers/catalog.js +197 -38
  89. package/dist/ai/providers/channels.d.ts +33 -3
  90. package/dist/ai/providers/channels.js +108 -7
  91. package/dist/ai/providers/enrich.d.ts +6 -3
  92. package/dist/ai/providers/enrich.js +19 -4
  93. package/dist/ai/providers/models-dev-cache.d.ts +28 -24
  94. package/dist/ai/providers/models-dev-cache.js +92 -78
  95. package/dist/ai/providers/models-dev-data.d.ts +7 -0
  96. package/dist/ai/providers/models-dev-data.js +32 -0
  97. package/dist/ai/providers/models-dev-snapshot.d.ts +50 -0
  98. package/dist/ai/providers/models-dev-snapshot.js +137 -0
  99. package/dist/ai/providers/models-dev.d.ts +51 -10
  100. package/dist/ai/providers/models-dev.js +132 -39
  101. package/dist/ai/providers/registry.d.ts +20 -10
  102. package/dist/ai/providers/registry.js +82 -43
  103. package/dist/ai/types.d.ts +28 -2
  104. package/dist/ai/types.js +5 -0
  105. package/dist/bundle/ama.cjs +37605 -21619
  106. package/dist/checkpoints/backend.d.ts +61 -0
  107. package/dist/checkpoints/backend.js +135 -0
  108. package/dist/checkpoints/blobs.d.ts +60 -0
  109. package/dist/checkpoints/blobs.js +203 -0
  110. package/dist/checkpoints/gc.d.ts +47 -0
  111. package/dist/checkpoints/gc.js +144 -0
  112. package/dist/checkpoints/git-head.d.ts +15 -0
  113. package/dist/checkpoints/git-head.js +89 -0
  114. package/dist/checkpoints/index.d.ts +17 -0
  115. package/dist/checkpoints/index.js +17 -0
  116. package/dist/checkpoints/replay.d.ts +48 -0
  117. package/dist/checkpoints/replay.js +154 -0
  118. package/dist/checkpoints/restore.d.ts +86 -0
  119. package/dist/checkpoints/restore.js +263 -0
  120. package/dist/checkpoints/settings.d.ts +14 -0
  121. package/dist/checkpoints/settings.js +23 -0
  122. package/dist/checkpoints/shadow-git.d.ts +108 -0
  123. package/dist/checkpoints/shadow-git.js +491 -0
  124. package/dist/checkpoints/shadow-restore.d.ts +25 -0
  125. package/dist/checkpoints/shadow-restore.js +128 -0
  126. package/dist/checkpoints/tracker.d.ts +64 -0
  127. package/dist/checkpoints/tracker.js +192 -0
  128. package/dist/checkpoints/types.d.ts +99 -0
  129. package/dist/checkpoints/types.js +12 -0
  130. package/dist/cli/args.d.ts +8 -4
  131. package/dist/cli/args.js +17 -97
  132. package/dist/cli/bootstrap.js +13 -1
  133. package/dist/cli/choice-prompt.d.ts +58 -0
  134. package/dist/cli/choice-prompt.js +140 -0
  135. package/dist/cli/codemode-notice.d.ts +3 -2
  136. package/dist/cli/codemode-notice.js +3 -2
  137. package/dist/cli/compose-agents.d.ts +27 -0
  138. package/dist/cli/compose-agents.js +111 -0
  139. package/dist/cli/compose-extensions.d.ts +29 -0
  140. package/dist/cli/compose-extensions.js +44 -0
  141. package/dist/cli/compose-session.d.ts +3 -0
  142. package/dist/cli/compose-session.js +47 -4
  143. package/dist/cli/compose.d.ts +3 -0
  144. package/dist/cli/compose.js +33 -6
  145. package/dist/cli/deps.d.ts +6 -0
  146. package/dist/cli/exit-codes.d.ts +10 -0
  147. package/dist/cli/exit-codes.js +12 -0
  148. package/dist/cli/help-text.d.ts +4 -0
  149. package/dist/cli/help-text.js +102 -0
  150. package/dist/cli/startup-screen.js +5 -3
  151. package/dist/cli/startup-steps.js +7 -1
  152. package/dist/cli/subcommands/config.d.ts +4 -1
  153. package/dist/cli/subcommands/config.js +17 -6
  154. package/dist/cli/subcommands/doctor.js +21 -0
  155. package/dist/cli/subcommands/models-cache-probe.d.ts +1 -1
  156. package/dist/cli/subcommands/models-cache-probe.js +3 -12
  157. package/dist/cli/subcommands/models-discover.js +5 -6
  158. package/dist/cli/subcommands/models.d.ts +2 -0
  159. package/dist/cli/subcommands/models.js +33 -14
  160. package/dist/cli/subcommands/providers.d.ts +1 -1
  161. package/dist/cli/subcommands/providers.js +8 -15
  162. package/dist/cli/subcommands/sessions.d.ts +3 -1
  163. package/dist/cli/subcommands/sessions.js +36 -2
  164. package/dist/codemode/capability.d.ts +24 -7
  165. package/dist/codemode/capability.js +38 -10
  166. package/dist/codemode/host-side.d.ts +14 -0
  167. package/dist/codemode/host-side.js +24 -1
  168. package/dist/codemode/tool.d.ts +1 -1
  169. package/dist/codemode/tool.js +6 -4
  170. package/dist/compaction/breaker.d.ts +25 -9
  171. package/dist/compaction/breaker.js +47 -17
  172. package/dist/compaction/estimate.d.ts +11 -3
  173. package/dist/compaction/estimate.js +64 -22
  174. package/dist/compaction/image-budget.d.ts +45 -0
  175. package/dist/compaction/image-budget.js +115 -0
  176. package/dist/compaction/post-compact.d.ts +38 -0
  177. package/dist/compaction/post-compact.js +152 -0
  178. package/dist/compaction/protect.d.ts +27 -0
  179. package/dist/compaction/protect.js +56 -0
  180. package/dist/compaction/prune-tier.d.ts +58 -12
  181. package/dist/compaction/prune-tier.js +120 -54
  182. package/dist/compaction/summarize-tier.d.ts +23 -4
  183. package/dist/compaction/summarize-tier.js +100 -29
  184. package/dist/config/checker.d.ts +28 -0
  185. package/dist/config/checker.js +97 -0
  186. package/dist/config/json-schema.js +63 -2
  187. package/dist/config/key-docs.js +72 -3
  188. package/dist/config/merge.d.ts +5 -3
  189. package/dist/config/merge.js +81 -7
  190. package/dist/config/profile.d.ts +2 -0
  191. package/dist/config/profile.js +2 -0
  192. package/dist/config/schema-w5.d.ts +18 -0
  193. package/dist/config/schema-w5.js +109 -0
  194. package/dist/config/schema.d.ts +5 -9
  195. package/dist/config/schema.js +68 -114
  196. package/dist/config/types-w5.d.ts +118 -0
  197. package/dist/config/types-w5.js +20 -0
  198. package/dist/config/types.d.ts +50 -1
  199. package/dist/config/types.js +12 -0
  200. package/dist/drivers/acp/client.d.ts +57 -0
  201. package/dist/drivers/acp/client.js +160 -0
  202. package/dist/drivers/acp/driver.d.ts +31 -0
  203. package/dist/drivers/acp/driver.js +274 -0
  204. package/dist/drivers/acp/testing/fake-agent-main.d.ts +5 -0
  205. package/dist/drivers/acp/testing/fake-agent-main.js +7 -0
  206. package/dist/drivers/acp/testing/fake-agent.d.ts +27 -0
  207. package/dist/drivers/acp/testing/fake-agent.js +211 -0
  208. package/dist/drivers/acp/types.d.ts +285 -0
  209. package/dist/drivers/acp/types.js +36 -0
  210. package/dist/drivers/agents.d.ts +58 -0
  211. package/dist/drivers/agents.js +142 -0
  212. package/dist/drivers/base.d.ts +35 -0
  213. package/dist/drivers/base.js +59 -0
  214. package/dist/drivers/catalog.d.ts +46 -0
  215. package/dist/drivers/catalog.js +167 -0
  216. package/dist/drivers/env.d.ts +20 -0
  217. package/dist/drivers/env.js +46 -0
  218. package/dist/drivers/host-runners.d.ts +18 -0
  219. package/dist/drivers/host-runners.js +45 -0
  220. package/dist/drivers/jsonrpc.d.ts +58 -0
  221. package/dist/drivers/jsonrpc.js +180 -0
  222. package/dist/drivers/native/claude-normalize.d.ts +46 -0
  223. package/dist/drivers/native/claude-normalize.js +106 -0
  224. package/dist/drivers/native/claude-stream.d.ts +29 -0
  225. package/dist/drivers/native/claude-stream.js +456 -0
  226. package/dist/drivers/native/codex-app-server.d.ts +26 -0
  227. package/dist/drivers/native/codex-app-server.js +407 -0
  228. package/dist/drivers/native/codex-normalize.d.ts +38 -0
  229. package/dist/drivers/native/codex-normalize.js +124 -0
  230. package/dist/drivers/native/oneshot.d.ts +28 -0
  231. package/dist/drivers/native/oneshot.js +312 -0
  232. package/dist/drivers/permissions.d.ts +40 -0
  233. package/dist/drivers/permissions.js +92 -0
  234. package/dist/drivers/pids.d.ts +32 -0
  235. package/dist/drivers/pids.js +93 -0
  236. package/dist/drivers/pool.d.ts +28 -0
  237. package/dist/drivers/pool.js +96 -0
  238. package/dist/drivers/probe.d.ts +47 -0
  239. package/dist/drivers/probe.js +172 -0
  240. package/dist/drivers/process.d.ts +48 -0
  241. package/dist/drivers/process.js +100 -0
  242. package/dist/drivers/runner.d.ts +61 -0
  243. package/dist/drivers/runner.js +388 -0
  244. package/dist/drivers/store.d.ts +49 -0
  245. package/dist/drivers/store.js +91 -0
  246. package/dist/drivers/turn.d.ts +41 -0
  247. package/dist/drivers/turn.js +99 -0
  248. package/dist/drivers/types.d.ts +122 -0
  249. package/dist/drivers/types.js +11 -0
  250. package/dist/git/info.d.ts +75 -0
  251. package/dist/git/info.js +191 -0
  252. package/dist/hooks/protocol.js +6 -1
  253. package/dist/hooks/types.d.ts +10 -1
  254. package/dist/hooks/types.js +2 -0
  255. package/dist/host/api-impl.d.ts +7 -0
  256. package/dist/host/api-impl.js +15 -0
  257. package/dist/host/types.d.ts +24 -2
  258. package/dist/host/types.js +4 -0
  259. package/dist/index.d.ts +3 -0
  260. package/dist/index.js +1 -0
  261. package/dist/modes/acp/acp-events.d.ts +46 -0
  262. package/dist/modes/acp/acp-events.js +233 -0
  263. package/dist/modes/acp/acp-mode.d.ts +17 -0
  264. package/dist/modes/acp/acp-mode.js +47 -0
  265. package/dist/modes/acp/acp-server.d.ts +79 -0
  266. package/dist/modes/acp/acp-server.js +315 -0
  267. package/dist/modes/commands-core.d.ts +13 -1
  268. package/dist/modes/commands-core.js +64 -0
  269. package/dist/modes/image-input.d.ts +12 -4
  270. package/dist/modes/image-input.js +22 -5
  271. package/dist/modes/interactive/agent-panels.d.ts +14 -0
  272. package/dist/modes/interactive/agent-panels.js +103 -0
  273. package/dist/modes/interactive/agent-ui.d.ts +73 -0
  274. package/dist/modes/interactive/agent-ui.js +234 -0
  275. package/dist/modes/interactive/approval-dialog.d.ts +21 -4
  276. package/dist/modes/interactive/approval-dialog.js +98 -20
  277. package/dist/modes/interactive/approval-merge.d.ts +41 -0
  278. package/dist/modes/interactive/approval-merge.js +89 -0
  279. package/dist/modes/interactive/clipboard-paste.d.ts +18 -0
  280. package/dist/modes/interactive/clipboard-paste.js +28 -0
  281. package/dist/modes/interactive/commands.d.ts +12 -1
  282. package/dist/modes/interactive/commands.js +31 -1
  283. package/dist/modes/interactive/confirm-dialog.d.ts +49 -0
  284. package/dist/modes/interactive/confirm-dialog.js +121 -0
  285. package/dist/modes/interactive/double-esc.d.ts +20 -0
  286. package/dist/modes/interactive/double-esc.js +34 -0
  287. package/dist/modes/interactive/event-notices.d.ts +32 -0
  288. package/dist/modes/interactive/event-notices.js +79 -0
  289. package/dist/modes/interactive/external-editor.d.ts +16 -0
  290. package/dist/modes/interactive/external-editor.js +39 -0
  291. package/dist/modes/interactive/interactive-mode.d.ts +14 -1
  292. package/dist/modes/interactive/interactive-mode.js +140 -136
  293. package/dist/modes/interactive/key-dispatch.d.ts +22 -2
  294. package/dist/modes/interactive/key-dispatch.js +66 -7
  295. package/dist/modes/interactive/line/line-mode.d.ts +4 -1
  296. package/dist/modes/interactive/line/line-mode.js +31 -5
  297. package/dist/modes/interactive/line/line-render.d.ts +2 -1
  298. package/dist/modes/interactive/line/line-render.js +40 -4
  299. package/dist/modes/interactive/message-view.d.ts +1 -1
  300. package/dist/modes/interactive/message-view.js +24 -2
  301. package/dist/modes/interactive/panels.d.ts +18 -1
  302. package/dist/modes/interactive/panels.js +19 -5
  303. package/dist/modes/interactive/plan-command.d.ts +40 -0
  304. package/dist/modes/interactive/plan-command.js +129 -0
  305. package/dist/modes/interactive/plan-dialog.d.ts +98 -0
  306. package/dist/modes/interactive/plan-dialog.js +290 -0
  307. package/dist/modes/interactive/plan-flow.d.ts +34 -0
  308. package/dist/modes/interactive/plan-flow.js +98 -0
  309. package/dist/modes/interactive/rewind-command.d.ts +31 -0
  310. package/dist/modes/interactive/rewind-command.js +106 -0
  311. package/dist/modes/interactive/rewind-flow.d.ts +37 -0
  312. package/dist/modes/interactive/rewind-flow.js +151 -0
  313. package/dist/modes/interactive/rewind-list.d.ts +36 -0
  314. package/dist/modes/interactive/rewind-list.js +123 -0
  315. package/dist/modes/interactive/rewind-panel.d.ts +77 -0
  316. package/dist/modes/interactive/rewind-panel.js +309 -0
  317. package/dist/modes/interactive/rewind-text.d.ts +40 -0
  318. package/dist/modes/interactive/rewind-text.js +139 -0
  319. package/dist/modes/interactive/session-events.d.ts +24 -0
  320. package/dist/modes/interactive/session-events.js +96 -0
  321. package/dist/modes/interactive/startup-ui.js +5 -1
  322. package/dist/modes/interactive/status-area.d.ts +73 -0
  323. package/dist/modes/interactive/status-area.js +190 -0
  324. package/dist/modes/interactive/status-bar.d.ts +99 -11
  325. package/dist/modes/interactive/status-bar.js +273 -90
  326. package/dist/modes/interactive/status-line.d.ts +33 -0
  327. package/dist/modes/interactive/status-line.js +113 -0
  328. package/dist/modes/interactive/subagent-view.d.ts +56 -0
  329. package/dist/modes/interactive/subagent-view.js +154 -0
  330. package/dist/modes/interactive/tasks-report.d.ts +27 -0
  331. package/dist/modes/interactive/tasks-report.js +114 -0
  332. package/dist/modes/interactive/tool-view.d.ts +12 -2
  333. package/dist/modes/interactive/tool-view.js +34 -4
  334. package/dist/modes/print/print-mode.d.ts +13 -4
  335. package/dist/modes/print/print-mode.js +56 -12
  336. package/dist/modes/rpc/commands.d.ts +9 -0
  337. package/dist/modes/rpc/commands.js +44 -0
  338. package/dist/modes/rpc/rpc-mode.js +7 -1
  339. package/dist/modes/session-report.d.ts +11 -0
  340. package/dist/modes/session-report.js +48 -1
  341. package/dist/permissions/auto-safe.d.ts +5 -0
  342. package/dist/permissions/auto-safe.js +18 -0
  343. package/dist/permissions/bypass.d.ts +32 -0
  344. package/dist/permissions/bypass.js +36 -0
  345. package/dist/permissions/classifier.d.ts +4 -0
  346. package/dist/permissions/classifier.js +2 -0
  347. package/dist/permissions/modes.js +1 -1
  348. package/dist/permissions/pipeline.d.ts +43 -4
  349. package/dist/permissions/pipeline.js +114 -9
  350. package/dist/permissions/readonly-bash.d.ts +30 -0
  351. package/dist/permissions/readonly-bash.js +118 -0
  352. package/dist/permissions/types.d.ts +42 -0
  353. package/dist/plan/compose.d.ts +9 -0
  354. package/dist/plan/compose.js +19 -0
  355. package/dist/plan/controller.d.ts +71 -0
  356. package/dist/plan/controller.js +27 -0
  357. package/dist/plan/done-markers.d.ts +17 -0
  358. package/dist/plan/done-markers.js +51 -0
  359. package/dist/plan/extract.d.ts +29 -0
  360. package/dist/plan/extract.js +160 -0
  361. package/dist/plan/prompts.d.ts +36 -0
  362. package/dist/plan/prompts.js +89 -0
  363. package/dist/plan/store.d.ts +37 -0
  364. package/dist/plan/store.js +90 -0
  365. package/dist/rpc.d.ts +58 -2
  366. package/dist/rpc.js +7 -0
  367. package/dist/sandbox/bash.d.ts +74 -0
  368. package/dist/sandbox/bash.js +126 -0
  369. package/dist/sandbox/detect.d.ts +48 -0
  370. package/dist/sandbox/detect.js +149 -0
  371. package/dist/sandbox/index.d.ts +5 -0
  372. package/dist/sandbox/index.js +5 -0
  373. package/dist/sandbox/profile.d.ts +33 -0
  374. package/dist/sandbox/profile.js +76 -0
  375. package/dist/sandbox/wrap.d.ts +29 -0
  376. package/dist/sandbox/wrap.js +117 -0
  377. package/dist/sdk.d.ts +33 -2
  378. package/dist/sdk.js +24 -2
  379. package/dist/session/types.d.ts +3 -1
  380. package/dist/session/types.js +1 -0
  381. package/dist/skills/builtin.d.ts +31 -0
  382. package/dist/skills/builtin.js +109 -0
  383. package/dist/skills/discover.d.ts +2 -1
  384. package/dist/skills/index-prompt.js +5 -8
  385. package/dist/tools/background-jobs.d.ts +71 -0
  386. package/dist/tools/background-jobs.js +198 -0
  387. package/dist/tools/bash.d.ts +53 -1
  388. package/dist/tools/bash.js +158 -7
  389. package/dist/tools/clipboard-image.d.ts +44 -0
  390. package/dist/tools/clipboard-image.js +148 -0
  391. package/dist/tools/edit.d.ts +2 -0
  392. package/dist/tools/edit.js +18 -3
  393. package/dist/tools/image-file.d.ts +38 -3
  394. package/dist/tools/image-file.js +81 -10
  395. package/dist/tools/image-resize.d.ts +58 -0
  396. package/dist/tools/image-resize.js +97 -0
  397. package/dist/tools/presets.d.ts +7 -2
  398. package/dist/tools/presets.js +11 -4
  399. package/dist/tools/read.d.ts +5 -2
  400. package/dist/tools/read.js +13 -10
  401. package/dist/tools/registry.d.ts +7 -1
  402. package/dist/tools/registry.js +27 -4
  403. package/dist/tools/task-ctl.d.ts +22 -0
  404. package/dist/tools/task-ctl.js +116 -0
  405. package/dist/tools/task.d.ts +22 -10
  406. package/dist/tools/task.js +66 -26
  407. package/dist/tools/todo.d.ts +22 -2
  408. package/dist/tools/todo.js +58 -14
  409. package/dist/tools/truncate.d.ts +15 -0
  410. package/dist/tools/truncate.js +25 -0
  411. package/dist/tools/types.d.ts +113 -0
  412. package/dist/tools/types.js +5 -0
  413. package/dist/tools/write.js +3 -0
  414. package/dist/tui/glyphs.d.ts +4 -0
  415. package/dist/tui/glyphs.js +4 -0
  416. package/dist/tui/keybindings.d.ts +6 -1
  417. package/dist/tui/keybindings.js +6 -1
  418. package/docs/acp.md +78 -0
  419. package/docs/agents.md +250 -0
  420. package/docs/codemode.md +12 -8
  421. package/docs/hooks.md +26 -22
  422. package/docs/permissions.md +78 -13
  423. package/docs/plan.md +107 -0
  424. package/docs/providers.md +228 -52
  425. package/docs/rewind-plan.md +181 -0
  426. package/docs/rpc.md +92 -5
  427. package/docs/sandbox.md +215 -0
  428. package/docs/session-format.md +41 -21
  429. package/docs/sessions.md +41 -1
  430. package/docs/tui-design.md +751 -0
  431. package/docs/tui.md +179 -21
  432. package/package.json +13 -2
@@ -0,0 +1,388 @@
1
+ /**
2
+ * ProcessRunner:把 AgentDriver 适配成 SubagentRunner(docs/wave5-plan.md §7.6、§5.3–§5.4)。[W5-E]
3
+ *
4
+ * 外部 CLI Agent 由此与 ama 子 Agent 共用 `task(agent=…)` 入口(D13)。`start()`:
5
+ * 1. 只在已信任目录里起(§5.4 信任;`claude -p` 会跳过目录信任对话框并执行项目 hooks);
6
+ * 2. 模式夹到父会话当前模式(`agents.<id>.maxMode` 例外),再取驱动支持的不更宽的模式;
7
+ * 按候选顺序选第一个已安装且支持该模式的驱动(D14 优先级;oneshot 只在只读下可选);
8
+ * 3. 会话级美元预算 `agents.sessionBudgetUsd` 用尽则不再起;
9
+ * 4. 子进程环境按 D16 清理;并发池(总 3 / claude 2)每回合取一个名额,排队可被 abort;
10
+ * 5. 权限请求只交给人({@link askHuman});中断 / stop / 超时时挂起的请求回 cancelled;
11
+ * 6. 每回合写 `custom{ama.agent-usage}`,会话建立写 `custom{ama.agent-session}`;
12
+ * 只回 finalText + 工具摘要(原始事件不落盘,§5.4 敏感数据);
13
+ * 7. 看门狗:单回合缺省 30 min;空闲 10 min 关进程(能续接的下次 `send` 以 resume 重开)。
14
+ */
15
+ import { agentEntry } from "../config/types-w5.js";
16
+ import { AmaError } from "../errors.js";
17
+ import { defaultSetTimer } from "./base.js";
18
+ import { buildChildEnv } from "./env.js";
19
+ import { askHuman, clampMode, supportedMode } from "./permissions.js";
20
+ export const RUN_TIMEOUT_MS = 30 * 60 * 1000;
21
+ export const IDLE_CLOSE_MS = 10 * 60 * 1000;
22
+ function toUsage(turn) {
23
+ const u = turn.usage ?? {};
24
+ const input = u.input ?? 0;
25
+ const output = u.output ?? 0;
26
+ const cacheRead = u.cacheRead ?? 0;
27
+ const usage = {
28
+ input,
29
+ output,
30
+ cacheRead,
31
+ cacheWrite: 0,
32
+ totalTokens: input + output + cacheRead,
33
+ };
34
+ if (u.costUsd !== undefined)
35
+ usage.cost = { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, total: u.costUsd };
36
+ return usage;
37
+ }
38
+ function statusOf(turn, timedOut, stopped) {
39
+ if (timedOut)
40
+ return "failed";
41
+ switch (turn.stopReason) {
42
+ case "end_turn":
43
+ case "max_tokens":
44
+ return "completed";
45
+ case "max_turn_requests":
46
+ return "max_turns";
47
+ case "cancelled":
48
+ return stopped ? "aborted" : "failed";
49
+ case "refusal":
50
+ return "failed";
51
+ }
52
+ }
53
+ /** 最终报告:最终文本 + 工具摘要 / 触及文件(给协调者当资料,不是指令)。 */
54
+ export function reportText(turn) {
55
+ const parts = [turn.finalText.trim()];
56
+ if (turn.toolSummary.length > 0)
57
+ parts.push(`工具调用:\n${turn.toolSummary.map((l) => `- ${l}`).join("\n")}`);
58
+ if (turn.filesTouched.length > 0)
59
+ parts.push(`修改的文件:\n${turn.filesTouched.map((f) => `- ${f}`).join("\n")}`);
60
+ return parts.filter((p) => p !== "").join("\n\n");
61
+ }
62
+ function failed(text, sessionId, runner) {
63
+ return {
64
+ text,
65
+ usage: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, totalTokens: 0 },
66
+ stopReason: "error",
67
+ isError: true,
68
+ status: "failed",
69
+ ...(sessionId !== "" ? { sessionRef: { runner, sessionId } } : {}),
70
+ };
71
+ }
72
+ export class ProcessRunner {
73
+ deps;
74
+ id;
75
+ drivers;
76
+ constructor(driver, deps) {
77
+ this.deps = deps;
78
+ this.drivers = Array.isArray(driver) ? driver : [driver];
79
+ if (this.drivers.length === 0)
80
+ throw new AmaError("invalid_arguments", "ProcessRunner 需要至少一个驱动");
81
+ this.id = deps.id ?? this.drivers[0].agentId;
82
+ }
83
+ get agentId() {
84
+ return this.drivers[0].agentId;
85
+ }
86
+ unattended() {
87
+ const u = this.deps.unattended;
88
+ return typeof u === "function" ? u() : u === true;
89
+ }
90
+ /** 选驱动:第一个已安装、且支持(不宽于)目标模式的候选。 */
91
+ async choose(mode) {
92
+ const reasons = [];
93
+ for (const driver of this.drivers) {
94
+ const probe = await driver.probe();
95
+ if (!probe.installed) {
96
+ reasons.push(`${driver.kind} 未安装`);
97
+ continue;
98
+ }
99
+ const usable = supportedMode(mode, probe.capabilities.modes);
100
+ if (usable === undefined) {
101
+ reasons.push(`${driver.kind} 不支持「${mode}」`);
102
+ continue;
103
+ }
104
+ const warning = probe.warning;
105
+ return {
106
+ driver,
107
+ mode: usable,
108
+ capabilities: probe.capabilities,
109
+ ...(warning !== undefined ? { warning } : {}),
110
+ };
111
+ }
112
+ throw new AmaError("agent_unavailable", `外部 Agent ${this.id} 不可用(${reasons.join(";")})`);
113
+ }
114
+ async start(request) {
115
+ if (!this.deps.trusted(request.cwd))
116
+ throw new AmaError("agent_untrusted", `目录 ${request.cwd} 尚未被 ama 信任,不能在这里启动外部 Agent(它会执行项目里的 hooks 与配置);先运行 ama trust`);
117
+ const budget = this.deps.config?.sessionBudgetUsd;
118
+ if (budget !== undefined && (this.deps.store?.totalUsd() ?? 0) >= budget)
119
+ throw new AmaError("budget_exhausted", `本会话外部 Agent 预算 $${budget} 已用尽`);
120
+ const entry = agentEntry(this.deps.config, this.agentId);
121
+ const mode = clampMode(request.mode, this.deps.parentMode?.(), entry?.maxMode);
122
+ const chosen = await this.choose(mode);
123
+ const handle = new ProcessHandle(this, chosen, request, {
124
+ ...this.deps,
125
+ model: request.model ?? entry?.model,
126
+ unattended: this.unattended(),
127
+ });
128
+ await handle.open(request.resume);
129
+ handle.begin();
130
+ return handle;
131
+ }
132
+ /** ProcessHandle 用。 */
133
+ get options() {
134
+ return this.deps;
135
+ }
136
+ }
137
+ class ProcessHandle {
138
+ runner;
139
+ chosen;
140
+ request;
141
+ deps;
142
+ session;
143
+ sessionId = "";
144
+ chain = Promise.resolve(failed("尚未运行", "", ""));
145
+ busy = false;
146
+ turns = 0;
147
+ stopped = false;
148
+ lifetime = new AbortController();
149
+ clearIdle;
150
+ agent;
151
+ constructor(runner, chosen, request, deps) {
152
+ this.runner = runner;
153
+ this.chosen = chosen;
154
+ this.request = request;
155
+ this.deps = deps;
156
+ this.agent = runner.agentId;
157
+ const onAbort = () => void this.stop();
158
+ if (request.signal.aborted)
159
+ this.lifetime.abort();
160
+ else
161
+ request.signal.addEventListener("abort", onAbort, { once: true });
162
+ if (chosen.warning !== undefined)
163
+ request.onEvent({ type: "notice", level: "warn", text: chosen.warning });
164
+ if (chosen.mode !== request.mode)
165
+ request.onEvent({
166
+ type: "notice",
167
+ level: "info",
168
+ text: `${this.agent} 以「${chosen.mode}」模式运行(请求的是「${request.mode}」,外部 Agent 不得比 ama 当前模式宽)`,
169
+ });
170
+ }
171
+ get id() {
172
+ return this.sessionId;
173
+ }
174
+ async open(resume) {
175
+ const env = buildChildEnv(this.deps.env, this.agent, this.deps.config);
176
+ const session = await this.chosen.driver.open({
177
+ cwd: this.request.cwd,
178
+ mode: this.chosen.mode,
179
+ env,
180
+ signal: this.lifetime.signal,
181
+ unattended: this.deps.unattended,
182
+ ...(this.deps.model !== undefined ? { model: this.deps.model } : {}),
183
+ ...(resume !== undefined ? { resume } : {}),
184
+ ...(this.request.budgetUsd !== undefined ? { budgetUsd: this.request.budgetUsd } : {}),
185
+ });
186
+ this.session = session;
187
+ this.sessionId = session.sessionId;
188
+ this.deps.store?.recordSession({
189
+ agent: this.agent,
190
+ runner: this.chosen.driver.kind,
191
+ sessionId: session.sessionId,
192
+ cwd: this.request.cwd,
193
+ ...(this.request.taskId !== undefined ? { taskId: this.request.taskId } : {}),
194
+ });
195
+ }
196
+ /** 第一回合。 */
197
+ begin() {
198
+ this.chain = this.runTurn(this.request.prompt);
199
+ }
200
+ async send(text) {
201
+ if (this.stopped)
202
+ throw new AmaError("agent_closed", `${this.agent} 已停止`);
203
+ if (this.busy && this.session?.steer !== undefined && this.chosen.capabilities.steer) {
204
+ await this.session.steer([{ type: "text", text }]);
205
+ return;
206
+ }
207
+ this.chain = this.chain.then(() => this.runTurn(text));
208
+ }
209
+ wait() {
210
+ return this.chain;
211
+ }
212
+ async stop() {
213
+ if (this.stopped)
214
+ return;
215
+ this.stopped = true;
216
+ this.lifetime.abort();
217
+ this.clearIdle?.();
218
+ const session = this.session;
219
+ this.session = undefined;
220
+ if (session !== undefined) {
221
+ await session.cancel().catch(() => undefined);
222
+ await session.close().catch(() => undefined);
223
+ }
224
+ }
225
+ emit(event) {
226
+ try {
227
+ this.request.onEvent(event);
228
+ }
229
+ catch (error) {
230
+ this.deps.log?.("warn", `subagent onEvent failed: ${String(error)}`);
231
+ }
232
+ }
233
+ onDriverEvent(event, turnCost, cancel) {
234
+ switch (event.type) {
235
+ case "message_delta":
236
+ this.emit({ type: "text", delta: event.text });
237
+ return;
238
+ case "thought_delta":
239
+ this.emit({ type: "thought", delta: event.text });
240
+ return;
241
+ case "tool_call":
242
+ if (event.status === "pending")
243
+ return;
244
+ this.emit({
245
+ type: "tool",
246
+ toolName: event.title,
247
+ status: event.status === "in_progress"
248
+ ? "started"
249
+ : event.status === "failed"
250
+ ? "failed"
251
+ : "completed",
252
+ });
253
+ return;
254
+ case "notice":
255
+ this.emit({ type: "notice", level: event.level, text: event.text });
256
+ return;
257
+ case "usage":
258
+ if (event.costUsd !== undefined) {
259
+ turnCost.usd = event.costUsd;
260
+ const budget = this.request.budgetUsd;
261
+ const session = this.deps.config?.sessionBudgetUsd;
262
+ const spent = this.deps.store?.totalUsd() ?? 0;
263
+ if ((budget !== undefined && turnCost.usd > budget) ||
264
+ (session !== undefined && spent + turnCost.usd > session)) {
265
+ this.emit({
266
+ type: "notice",
267
+ level: "warn",
268
+ text: `${this.agent} 超出美元预算,已中断`,
269
+ });
270
+ cancel();
271
+ }
272
+ }
273
+ return;
274
+ default:
275
+ return;
276
+ }
277
+ }
278
+ /** 关掉空闲进程后再续聊:能续接的以 resume 重开。 */
279
+ async ensureSession() {
280
+ if (this.session !== undefined)
281
+ return this.session;
282
+ if (this.chosen.capabilities.resume === "none" || this.sessionId === "")
283
+ throw new AmaError("agent_closed", `${this.agent} 会话已因空闲关闭且不支持续接`);
284
+ await this.open(this.sessionId);
285
+ return this.session;
286
+ }
287
+ async runTurn(text) {
288
+ if (this.stopped)
289
+ return failed(`${this.agent} 已停止`, this.sessionId, this.runner.id);
290
+ this.clearIdle?.();
291
+ let release;
292
+ const setTimer = this.deps.setTimer ?? defaultSetTimer;
293
+ let timedOut = false;
294
+ try {
295
+ release = await this.deps.pool.acquire(this.agent, this.lifetime.signal);
296
+ const session = this.session ?? (await this.ensureSession());
297
+ this.busy = true;
298
+ this.turns += 1;
299
+ this.emit({ type: "turn", turn: this.turns });
300
+ const turnCost = { usd: 0 };
301
+ const cancel = () => void session.cancel();
302
+ const clearTimeout = setTimer(() => {
303
+ timedOut = true;
304
+ this.emit({ type: "notice", level: "warn", text: `${this.agent} 运行超时,已中断` });
305
+ cancel();
306
+ }, this.deps.timeoutMs ?? RUN_TIMEOUT_MS);
307
+ const onAbort = () => cancel();
308
+ this.lifetime.signal.addEventListener("abort", onAbort, { once: true });
309
+ let turn;
310
+ try {
311
+ turn = await session.prompt([{ type: "text", text }], {
312
+ onEvent: (e) => this.onDriverEvent(e, turnCost, cancel),
313
+ onPermission: (req, signal) => askHuman(req, {
314
+ agent: this.agent,
315
+ sessionId: session.sessionId,
316
+ unattended: this.deps.unattended,
317
+ approve: this.deps.approve,
318
+ signal: AbortSignal.any([signal, this.lifetime.signal]),
319
+ ...(this.request.taskId !== undefined ? { taskId: this.request.taskId } : {}),
320
+ }),
321
+ });
322
+ }
323
+ finally {
324
+ clearTimeout();
325
+ this.lifetime.signal.removeEventListener("abort", onAbort);
326
+ this.busy = false;
327
+ }
328
+ this.sessionId = session.sessionId;
329
+ const usage = toUsage(turn);
330
+ const unit = this.chosen.capabilities.usage;
331
+ if (unit !== "none") {
332
+ const amount = unit === "usd"
333
+ ? (turn.usage?.costUsd ?? turnCost.usd)
334
+ : unit === "tokens"
335
+ ? usage.input + usage.output
336
+ : 1;
337
+ this.deps.store?.recordUsage({
338
+ agent: this.agent,
339
+ sessionId: this.sessionId,
340
+ unit,
341
+ amount,
342
+ ...(usage.totalTokens > 0 ? { tokens: usage.totalTokens } : {}),
343
+ });
344
+ this.emit({ type: "usage", usage, unit, amount });
345
+ }
346
+ const status = statusOf(turn, timedOut, this.stopped);
347
+ this.armIdle();
348
+ return {
349
+ text: timedOut
350
+ ? `${this.agent} 运行超时被中断。${turn.finalText !== "" ? `\n\n${reportText(turn)}` : ""}`
351
+ : reportText(turn),
352
+ usage,
353
+ stopReason: timedOut ? "timeout" : turn.stopReason,
354
+ isError: status === "failed",
355
+ status,
356
+ sessionRef: { runner: this.runner.id, sessionId: this.sessionId },
357
+ };
358
+ }
359
+ catch (error) {
360
+ if (this.stopped)
361
+ return {
362
+ ...failed(`${this.agent} 已停止`, this.sessionId, this.runner.id),
363
+ status: "aborted",
364
+ };
365
+ return failed(`${this.agent} 失败:${error.message}`, this.sessionId, this.runner.id);
366
+ }
367
+ finally {
368
+ release?.();
369
+ }
370
+ }
371
+ /** 空闲一段时间关进程(不占资源);能续接的下次 send 再起。 */
372
+ armIdle() {
373
+ if (this.chosen.capabilities.resume === "none")
374
+ return;
375
+ const setTimer = this.deps.setTimer ?? defaultSetTimer;
376
+ this.clearIdle = setTimer(() => {
377
+ if (this.busy || this.stopped)
378
+ return;
379
+ const session = this.session;
380
+ this.session = undefined;
381
+ void session?.close().catch(() => undefined);
382
+ }, this.deps.idleMs ?? IDLE_CLOSE_MS);
383
+ }
384
+ }
385
+ /** §10.2 的签名:`createProcessRunner(driver, { approve, pool, store, … })`。 */
386
+ export function createProcessRunner(driver, deps) {
387
+ return new ProcessRunner(driver, deps);
388
+ }
@@ -0,0 +1,49 @@
1
+ /**
2
+ * 外部 Agent 的会话引用与记账(docs/wave5-plan.md §5.4 记账 / 敏感数据,session-format 登记)。[W5-E]
3
+ *
4
+ * - `custom{ama.agent-session}`:`{ agent, runner, sessionId, cwd?, taskId? }`——外部 CLI 自己的
5
+ * 会话 id,续聊(`taskId` / resume)用;不含原始事件与转录。
6
+ * - `custom{ama.agent-usage}`:每个回合结束一条 `{ agent, sessionId, unit, amount, tokens? }`;
7
+ * 无美元单位的按各自单位记,不换算。
8
+ * - `SessionStats.external` 由 {@link createExternalStatsExtension} 从活动分支汇总(扩展点
9
+ * `contributeStats`,主会话才装)。
10
+ */
11
+ import type { SessionExtension } from "../agent/session-extensions.js";
12
+ import type { ExternalAgentStats, ExternalUsageUnit } from "../agent/types-w5.js";
13
+ import type { SessionEntry } from "../session/types.js";
14
+ export declare const AGENT_SESSION_CUSTOM = "ama.agent-session";
15
+ export declare const AGENT_USAGE_CUSTOM = "ama.agent-usage";
16
+ export interface AgentSessionRecord {
17
+ agent: string;
18
+ /** 驱动种类(`claude-stream` / `codex-app-server` / `acp` …)或宿主 runner id。 */
19
+ runner: string;
20
+ sessionId: string;
21
+ cwd?: string;
22
+ taskId?: string;
23
+ }
24
+ export interface AgentUsageRecord {
25
+ agent: string;
26
+ sessionId: string;
27
+ unit: ExternalUsageUnit;
28
+ amount: number;
29
+ tokens?: number;
30
+ }
31
+ /** 会话一侧的最小接口(`ToolContext.session` 或 SessionCore 都能提供)。 */
32
+ export interface AgentStoreBackend {
33
+ appendCustom(customType: string, data: unknown): void;
34
+ /** 活动分支上的条目(求会话级美元合计;不给则只算本进程记下的)。 */
35
+ entries?(): readonly SessionEntry[];
36
+ }
37
+ export interface AgentStore {
38
+ recordSession(record: AgentSessionRecord): void;
39
+ recordUsage(record: AgentUsageRecord): void;
40
+ /** 本会话外部 Agent 的美元合计(`agents.sessionBudgetUsd`)。 */
41
+ totalUsd(): number;
42
+ /** 最近一条该 Agent(可按 taskId 过滤)的会话引用。 */
43
+ lastSession(agent: string, taskId?: string): AgentSessionRecord | undefined;
44
+ }
45
+ export declare function createAgentStore(backend: AgentStoreBackend): AgentStore;
46
+ /** 活动分支上的 `ama.agent-usage` → `SessionStats.external`;没有外部运行时 undefined。 */
47
+ export declare function aggregateExternal(entries: readonly SessionEntry[]): ExternalAgentStats | undefined;
48
+ /** 会话扩展:`getStats()` 时补 `external`(只装在主会话)。 */
49
+ export declare function createExternalStatsExtension(entries: () => readonly SessionEntry[]): SessionExtension;
@@ -0,0 +1,91 @@
1
+ /**
2
+ * 外部 Agent 的会话引用与记账(docs/wave5-plan.md §5.4 记账 / 敏感数据,session-format 登记)。[W5-E]
3
+ *
4
+ * - `custom{ama.agent-session}`:`{ agent, runner, sessionId, cwd?, taskId? }`——外部 CLI 自己的
5
+ * 会话 id,续聊(`taskId` / resume)用;不含原始事件与转录。
6
+ * - `custom{ama.agent-usage}`:每个回合结束一条 `{ agent, sessionId, unit, amount, tokens? }`;
7
+ * 无美元单位的按各自单位记,不换算。
8
+ * - `SessionStats.external` 由 {@link createExternalStatsExtension} 从活动分支汇总(扩展点
9
+ * `contributeStats`,主会话才装)。
10
+ */
11
+ export const AGENT_SESSION_CUSTOM = "ama.agent-session";
12
+ export const AGENT_USAGE_CUSTOM = "ama.agent-usage";
13
+ function customs(entries, type) {
14
+ const out = [];
15
+ for (const entry of entries)
16
+ if (entry.type === "custom" && entry.customType === type && typeof entry.data === "object")
17
+ out.push(entry.data);
18
+ return out;
19
+ }
20
+ export function createAgentStore(backend) {
21
+ const sessions = [];
22
+ let usd = 0;
23
+ const all = () => {
24
+ const entries = backend.entries?.();
25
+ if (entries === undefined)
26
+ return undefined;
27
+ return {
28
+ sessions: customs(entries, AGENT_SESSION_CUSTOM),
29
+ usage: customs(entries, AGENT_USAGE_CUSTOM),
30
+ };
31
+ };
32
+ return {
33
+ recordSession(record) {
34
+ sessions.push(record);
35
+ backend.appendCustom(AGENT_SESSION_CUSTOM, record);
36
+ },
37
+ recordUsage(record) {
38
+ if (record.unit === "usd")
39
+ usd += record.amount;
40
+ backend.appendCustom(AGENT_USAGE_CUSTOM, record);
41
+ },
42
+ totalUsd() {
43
+ const persisted = all();
44
+ if (persisted === undefined)
45
+ return usd;
46
+ return persisted.usage.reduce((sum, u) => (u.unit === "usd" ? sum + u.amount : sum), 0);
47
+ },
48
+ lastSession(agent, taskId) {
49
+ const list = all()?.sessions ?? sessions;
50
+ for (let i = list.length - 1; i >= 0; i--) {
51
+ const r = list[i];
52
+ if (r.agent === agent && (taskId === undefined || r.taskId === taskId))
53
+ return r;
54
+ }
55
+ return undefined;
56
+ },
57
+ };
58
+ }
59
+ /** 活动分支上的 `ama.agent-usage` → `SessionStats.external`;没有外部运行时 undefined。 */
60
+ export function aggregateExternal(entries) {
61
+ const usage = customs(entries, AGENT_USAGE_CUSTOM);
62
+ if (usage.length === 0)
63
+ return undefined;
64
+ const byAgent = {};
65
+ for (const u of usage) {
66
+ if (typeof u.agent !== "string" || typeof u.amount !== "number")
67
+ continue;
68
+ const row = (byAgent[u.agent] ??= { runs: 0, unit: u.unit, amount: 0 });
69
+ row.runs += 1;
70
+ // 同一 Agent 换了单位(换了驱动)时以最新单位重新计
71
+ if (row.unit !== u.unit) {
72
+ row.unit = u.unit;
73
+ row.amount = 0;
74
+ }
75
+ row.amount += u.amount;
76
+ if (typeof u.tokens === "number")
77
+ row.tokens = (row.tokens ?? 0) + u.tokens;
78
+ }
79
+ return { byAgent };
80
+ }
81
+ /** 会话扩展:`getStats()` 时补 `external`(只装在主会话)。 */
82
+ export function createExternalStatsExtension(entries) {
83
+ return {
84
+ id: "external-agents",
85
+ contributeStats(stats) {
86
+ const external = aggregateExternal(entries());
87
+ if (external !== undefined)
88
+ stats.external = external;
89
+ },
90
+ };
91
+ }
@@ -0,0 +1,41 @@
1
+ /**
2
+ * 一个回合的结果汇总(各驱动共用)。[W5-E]
3
+ *
4
+ * 驱动把归一化后的 {@link DriverEvent} 交给收集器:它转发给 `onEvent`,同时累计最终文本、
5
+ * 工具摘要(≤ 20 行,§5.2)、触及的文件(edit / delete / move 类工具的 locations)与用量。
6
+ * 原始事件只在内存里流过,不落盘(§5.4 敏感数据)。
7
+ */
8
+ import type { AcpToolKind, DriverEvent, DriverTurnResult } from "./types.js";
9
+ export declare const TOOL_SUMMARY_LINES = 20;
10
+ interface ToolState {
11
+ title: string;
12
+ kind: AcpToolKind;
13
+ status: "pending" | "in_progress" | "completed" | "failed";
14
+ locations: string[];
15
+ }
16
+ export declare class TurnCollector {
17
+ private readonly onEvent;
18
+ private text;
19
+ private readonly tools;
20
+ private readonly order;
21
+ private usage;
22
+ constructor(onEvent: (event: DriverEvent) => void);
23
+ /** 归一化事件:累计后转发给 onEvent。 */
24
+ push(event: DriverEvent): void;
25
+ /** 工具调用的最近状态(驱动做 tool_call_update 时补全标题与种类)。 */
26
+ tool(id: string): Readonly<ToolState> | undefined;
27
+ /** 回合结束时的累计用量合并(`result.usage` 等)。 */
28
+ mergeUsage(usage: {
29
+ input?: number;
30
+ output?: number;
31
+ cacheRead?: number;
32
+ costUsd?: number;
33
+ }): void;
34
+ get finalText(): string;
35
+ /** 原生协议以整条消息给出最终文本时覆盖(流式增量与整条只取其一)。 */
36
+ setFinalText(text: string): void;
37
+ result(stopReason: DriverTurnResult["stopReason"]): DriverTurnResult;
38
+ }
39
+ /** 单行、截断(标题与摘要用)。 */
40
+ export declare function oneLine(text: string, max?: number): string;
41
+ export {};
@@ -0,0 +1,99 @@
1
+ /**
2
+ * 一个回合的结果汇总(各驱动共用)。[W5-E]
3
+ *
4
+ * 驱动把归一化后的 {@link DriverEvent} 交给收集器:它转发给 `onEvent`,同时累计最终文本、
5
+ * 工具摘要(≤ 20 行,§5.2)、触及的文件(edit / delete / move 类工具的 locations)与用量。
6
+ * 原始事件只在内存里流过,不落盘(§5.4 敏感数据)。
7
+ */
8
+ export const TOOL_SUMMARY_LINES = 20;
9
+ const WRITE_KINDS = new Set(["edit", "delete", "move"]);
10
+ export class TurnCollector {
11
+ onEvent;
12
+ text = "";
13
+ tools = new Map();
14
+ order = [];
15
+ usage = {};
16
+ constructor(onEvent) {
17
+ this.onEvent = onEvent;
18
+ }
19
+ /** 归一化事件:累计后转发给 onEvent。 */
20
+ push(event) {
21
+ switch (event.type) {
22
+ case "message_delta":
23
+ this.text += event.text;
24
+ break;
25
+ case "tool_call": {
26
+ const known = this.tools.get(event.id);
27
+ const next = {
28
+ title: event.title !== "" ? event.title : (known?.title ?? event.id),
29
+ kind: event.kind,
30
+ status: event.status,
31
+ locations: event.locations ?? known?.locations ?? [],
32
+ };
33
+ if (known === undefined)
34
+ this.order.push(event.id);
35
+ this.tools.set(event.id, next);
36
+ break;
37
+ }
38
+ case "usage":
39
+ this.mergeUsage(event);
40
+ break;
41
+ default:
42
+ break;
43
+ }
44
+ this.onEvent(event);
45
+ }
46
+ /** 工具调用的最近状态(驱动做 tool_call_update 时补全标题与种类)。 */
47
+ tool(id) {
48
+ return this.tools.get(id);
49
+ }
50
+ /** 回合结束时的累计用量合并(`result.usage` 等)。 */
51
+ mergeUsage(usage) {
52
+ if (usage.input !== undefined)
53
+ this.usage.input = usage.input;
54
+ if (usage.output !== undefined)
55
+ this.usage.output = usage.output;
56
+ if (usage.cacheRead !== undefined)
57
+ this.usage.cacheRead = usage.cacheRead;
58
+ if (usage.costUsd !== undefined)
59
+ this.usage.costUsd = usage.costUsd;
60
+ }
61
+ get finalText() {
62
+ return this.text;
63
+ }
64
+ /** 原生协议以整条消息给出最终文本时覆盖(流式增量与整条只取其一)。 */
65
+ setFinalText(text) {
66
+ this.text = text;
67
+ }
68
+ result(stopReason) {
69
+ const files = new Set();
70
+ for (const tool of this.tools.values())
71
+ if (WRITE_KINDS.has(tool.kind) && tool.status === "completed")
72
+ for (const path of tool.locations)
73
+ files.add(path);
74
+ const lines = this.order.map((id) => {
75
+ const tool = this.tools.get(id);
76
+ return `${tool.status === "failed" ? "✗" : tool.status === "completed" ? "✓" : "…"} ${tool.kind} ${tool.title}`;
77
+ });
78
+ const summary = lines.length > TOOL_SUMMARY_LINES
79
+ ? [
80
+ ...lines.slice(0, TOOL_SUMMARY_LINES - 1),
81
+ `… 另有 ${lines.length - TOOL_SUMMARY_LINES + 1} 次工具调用`,
82
+ ]
83
+ : lines;
84
+ const result = {
85
+ stopReason,
86
+ finalText: this.text,
87
+ filesTouched: [...files],
88
+ toolSummary: summary,
89
+ };
90
+ if (Object.keys(this.usage).length > 0)
91
+ result.usage = { ...this.usage };
92
+ return result;
93
+ }
94
+ }
95
+ /** 单行、截断(标题与摘要用)。 */
96
+ export function oneLine(text, max = 120) {
97
+ const flat = text.replace(/\s+/g, " ").trim();
98
+ return flat.length > max ? `${flat.slice(0, max - 1)}…` : flat;
99
+ }