@armadra/agent 0.4.0 → 0.5.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 (422) hide show
  1. package/CHANGELOG.md +174 -1
  2. package/README.md +201 -82
  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 +36203 -20499
  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/codemode-notice.d.ts +3 -2
  134. package/dist/cli/codemode-notice.js +3 -2
  135. package/dist/cli/compose-agents.d.ts +27 -0
  136. package/dist/cli/compose-agents.js +111 -0
  137. package/dist/cli/compose-extensions.d.ts +29 -0
  138. package/dist/cli/compose-extensions.js +44 -0
  139. package/dist/cli/compose-session.d.ts +3 -0
  140. package/dist/cli/compose-session.js +47 -4
  141. package/dist/cli/compose.d.ts +3 -0
  142. package/dist/cli/compose.js +33 -6
  143. package/dist/cli/deps.d.ts +6 -0
  144. package/dist/cli/exit-codes.d.ts +10 -0
  145. package/dist/cli/exit-codes.js +12 -0
  146. package/dist/cli/help-text.d.ts +4 -0
  147. package/dist/cli/help-text.js +102 -0
  148. package/dist/cli/startup-screen.js +5 -3
  149. package/dist/cli/startup-steps.js +7 -1
  150. package/dist/cli/subcommands/config.d.ts +4 -1
  151. package/dist/cli/subcommands/config.js +17 -6
  152. package/dist/cli/subcommands/doctor.js +21 -0
  153. package/dist/cli/subcommands/models-discover.js +5 -6
  154. package/dist/cli/subcommands/models.d.ts +2 -0
  155. package/dist/cli/subcommands/models.js +33 -14
  156. package/dist/cli/subcommands/providers.js +5 -6
  157. package/dist/cli/subcommands/sessions.d.ts +3 -1
  158. package/dist/cli/subcommands/sessions.js +36 -2
  159. package/dist/codemode/capability.d.ts +24 -7
  160. package/dist/codemode/capability.js +38 -10
  161. package/dist/codemode/host-side.d.ts +14 -0
  162. package/dist/codemode/host-side.js +24 -1
  163. package/dist/codemode/tool.d.ts +1 -1
  164. package/dist/codemode/tool.js +6 -4
  165. package/dist/compaction/breaker.d.ts +25 -9
  166. package/dist/compaction/breaker.js +47 -17
  167. package/dist/compaction/estimate.d.ts +11 -3
  168. package/dist/compaction/estimate.js +64 -22
  169. package/dist/compaction/image-budget.d.ts +45 -0
  170. package/dist/compaction/image-budget.js +115 -0
  171. package/dist/compaction/post-compact.d.ts +38 -0
  172. package/dist/compaction/post-compact.js +152 -0
  173. package/dist/compaction/protect.d.ts +27 -0
  174. package/dist/compaction/protect.js +56 -0
  175. package/dist/compaction/prune-tier.d.ts +58 -12
  176. package/dist/compaction/prune-tier.js +120 -54
  177. package/dist/compaction/summarize-tier.d.ts +23 -4
  178. package/dist/compaction/summarize-tier.js +100 -29
  179. package/dist/config/checker.d.ts +28 -0
  180. package/dist/config/checker.js +97 -0
  181. package/dist/config/json-schema.js +63 -2
  182. package/dist/config/key-docs.js +72 -3
  183. package/dist/config/merge.d.ts +5 -3
  184. package/dist/config/merge.js +81 -7
  185. package/dist/config/profile.d.ts +2 -0
  186. package/dist/config/profile.js +2 -0
  187. package/dist/config/schema-w5.d.ts +18 -0
  188. package/dist/config/schema-w5.js +109 -0
  189. package/dist/config/schema.d.ts +5 -9
  190. package/dist/config/schema.js +68 -114
  191. package/dist/config/types-w5.d.ts +118 -0
  192. package/dist/config/types-w5.js +20 -0
  193. package/dist/config/types.d.ts +50 -1
  194. package/dist/config/types.js +12 -0
  195. package/dist/drivers/acp/client.d.ts +57 -0
  196. package/dist/drivers/acp/client.js +160 -0
  197. package/dist/drivers/acp/driver.d.ts +31 -0
  198. package/dist/drivers/acp/driver.js +274 -0
  199. package/dist/drivers/acp/testing/fake-agent-main.d.ts +5 -0
  200. package/dist/drivers/acp/testing/fake-agent-main.js +7 -0
  201. package/dist/drivers/acp/testing/fake-agent.d.ts +27 -0
  202. package/dist/drivers/acp/testing/fake-agent.js +211 -0
  203. package/dist/drivers/acp/types.d.ts +285 -0
  204. package/dist/drivers/acp/types.js +36 -0
  205. package/dist/drivers/agents.d.ts +58 -0
  206. package/dist/drivers/agents.js +142 -0
  207. package/dist/drivers/base.d.ts +35 -0
  208. package/dist/drivers/base.js +59 -0
  209. package/dist/drivers/catalog.d.ts +46 -0
  210. package/dist/drivers/catalog.js +167 -0
  211. package/dist/drivers/env.d.ts +20 -0
  212. package/dist/drivers/env.js +46 -0
  213. package/dist/drivers/host-runners.d.ts +18 -0
  214. package/dist/drivers/host-runners.js +45 -0
  215. package/dist/drivers/jsonrpc.d.ts +58 -0
  216. package/dist/drivers/jsonrpc.js +180 -0
  217. package/dist/drivers/native/claude-normalize.d.ts +46 -0
  218. package/dist/drivers/native/claude-normalize.js +106 -0
  219. package/dist/drivers/native/claude-stream.d.ts +29 -0
  220. package/dist/drivers/native/claude-stream.js +456 -0
  221. package/dist/drivers/native/codex-app-server.d.ts +26 -0
  222. package/dist/drivers/native/codex-app-server.js +407 -0
  223. package/dist/drivers/native/codex-normalize.d.ts +38 -0
  224. package/dist/drivers/native/codex-normalize.js +124 -0
  225. package/dist/drivers/native/oneshot.d.ts +28 -0
  226. package/dist/drivers/native/oneshot.js +312 -0
  227. package/dist/drivers/permissions.d.ts +40 -0
  228. package/dist/drivers/permissions.js +92 -0
  229. package/dist/drivers/pids.d.ts +32 -0
  230. package/dist/drivers/pids.js +93 -0
  231. package/dist/drivers/pool.d.ts +28 -0
  232. package/dist/drivers/pool.js +96 -0
  233. package/dist/drivers/probe.d.ts +47 -0
  234. package/dist/drivers/probe.js +172 -0
  235. package/dist/drivers/process.d.ts +48 -0
  236. package/dist/drivers/process.js +100 -0
  237. package/dist/drivers/runner.d.ts +61 -0
  238. package/dist/drivers/runner.js +388 -0
  239. package/dist/drivers/store.d.ts +49 -0
  240. package/dist/drivers/store.js +91 -0
  241. package/dist/drivers/turn.d.ts +41 -0
  242. package/dist/drivers/turn.js +99 -0
  243. package/dist/drivers/types.d.ts +122 -0
  244. package/dist/drivers/types.js +11 -0
  245. package/dist/git/info.d.ts +75 -0
  246. package/dist/git/info.js +191 -0
  247. package/dist/hooks/protocol.js +6 -1
  248. package/dist/hooks/types.d.ts +10 -1
  249. package/dist/hooks/types.js +2 -0
  250. package/dist/host/api-impl.d.ts +7 -0
  251. package/dist/host/api-impl.js +15 -0
  252. package/dist/host/types.d.ts +24 -2
  253. package/dist/host/types.js +4 -0
  254. package/dist/index.d.ts +3 -0
  255. package/dist/index.js +1 -0
  256. package/dist/modes/acp/acp-events.d.ts +46 -0
  257. package/dist/modes/acp/acp-events.js +233 -0
  258. package/dist/modes/acp/acp-mode.d.ts +17 -0
  259. package/dist/modes/acp/acp-mode.js +47 -0
  260. package/dist/modes/acp/acp-server.d.ts +79 -0
  261. package/dist/modes/acp/acp-server.js +315 -0
  262. package/dist/modes/commands-core.d.ts +7 -1
  263. package/dist/modes/commands-core.js +55 -0
  264. package/dist/modes/image-input.d.ts +12 -4
  265. package/dist/modes/image-input.js +22 -5
  266. package/dist/modes/interactive/agent-panels.d.ts +14 -0
  267. package/dist/modes/interactive/agent-panels.js +103 -0
  268. package/dist/modes/interactive/agent-ui.d.ts +73 -0
  269. package/dist/modes/interactive/agent-ui.js +234 -0
  270. package/dist/modes/interactive/approval-dialog.d.ts +21 -4
  271. package/dist/modes/interactive/approval-dialog.js +98 -20
  272. package/dist/modes/interactive/approval-merge.d.ts +41 -0
  273. package/dist/modes/interactive/approval-merge.js +89 -0
  274. package/dist/modes/interactive/clipboard-paste.d.ts +18 -0
  275. package/dist/modes/interactive/clipboard-paste.js +28 -0
  276. package/dist/modes/interactive/commands.d.ts +9 -1
  277. package/dist/modes/interactive/commands.js +19 -0
  278. package/dist/modes/interactive/double-esc.d.ts +20 -0
  279. package/dist/modes/interactive/double-esc.js +34 -0
  280. package/dist/modes/interactive/event-notices.d.ts +32 -0
  281. package/dist/modes/interactive/event-notices.js +79 -0
  282. package/dist/modes/interactive/external-editor.d.ts +16 -0
  283. package/dist/modes/interactive/external-editor.js +39 -0
  284. package/dist/modes/interactive/interactive-mode.d.ts +14 -1
  285. package/dist/modes/interactive/interactive-mode.js +134 -136
  286. package/dist/modes/interactive/key-dispatch.d.ts +16 -2
  287. package/dist/modes/interactive/key-dispatch.js +49 -1
  288. package/dist/modes/interactive/line/line-mode.d.ts +2 -1
  289. package/dist/modes/interactive/line/line-mode.js +22 -5
  290. package/dist/modes/interactive/line/line-render.d.ts +2 -1
  291. package/dist/modes/interactive/line/line-render.js +40 -4
  292. package/dist/modes/interactive/message-view.d.ts +1 -1
  293. package/dist/modes/interactive/message-view.js +24 -2
  294. package/dist/modes/interactive/panels.d.ts +18 -1
  295. package/dist/modes/interactive/panels.js +19 -5
  296. package/dist/modes/interactive/plan-command.d.ts +40 -0
  297. package/dist/modes/interactive/plan-command.js +129 -0
  298. package/dist/modes/interactive/plan-dialog.d.ts +98 -0
  299. package/dist/modes/interactive/plan-dialog.js +290 -0
  300. package/dist/modes/interactive/plan-flow.d.ts +34 -0
  301. package/dist/modes/interactive/plan-flow.js +98 -0
  302. package/dist/modes/interactive/rewind-command.d.ts +31 -0
  303. package/dist/modes/interactive/rewind-command.js +106 -0
  304. package/dist/modes/interactive/rewind-flow.d.ts +37 -0
  305. package/dist/modes/interactive/rewind-flow.js +151 -0
  306. package/dist/modes/interactive/rewind-list.d.ts +36 -0
  307. package/dist/modes/interactive/rewind-list.js +123 -0
  308. package/dist/modes/interactive/rewind-panel.d.ts +77 -0
  309. package/dist/modes/interactive/rewind-panel.js +309 -0
  310. package/dist/modes/interactive/rewind-text.d.ts +40 -0
  311. package/dist/modes/interactive/rewind-text.js +139 -0
  312. package/dist/modes/interactive/session-events.d.ts +24 -0
  313. package/dist/modes/interactive/session-events.js +96 -0
  314. package/dist/modes/interactive/status-area.d.ts +73 -0
  315. package/dist/modes/interactive/status-area.js +190 -0
  316. package/dist/modes/interactive/status-bar.d.ts +99 -11
  317. package/dist/modes/interactive/status-bar.js +273 -90
  318. package/dist/modes/interactive/status-line.d.ts +33 -0
  319. package/dist/modes/interactive/status-line.js +113 -0
  320. package/dist/modes/interactive/subagent-view.d.ts +56 -0
  321. package/dist/modes/interactive/subagent-view.js +154 -0
  322. package/dist/modes/interactive/tasks-report.d.ts +27 -0
  323. package/dist/modes/interactive/tasks-report.js +114 -0
  324. package/dist/modes/interactive/tool-view.d.ts +12 -2
  325. package/dist/modes/interactive/tool-view.js +34 -4
  326. package/dist/modes/print/print-mode.d.ts +13 -4
  327. package/dist/modes/print/print-mode.js +56 -12
  328. package/dist/modes/rpc/commands.d.ts +9 -0
  329. package/dist/modes/rpc/commands.js +44 -0
  330. package/dist/modes/rpc/rpc-mode.js +7 -1
  331. package/dist/modes/session-report.d.ts +11 -0
  332. package/dist/modes/session-report.js +48 -1
  333. package/dist/permissions/auto-safe.d.ts +5 -0
  334. package/dist/permissions/auto-safe.js +18 -0
  335. package/dist/permissions/classifier.d.ts +4 -0
  336. package/dist/permissions/classifier.js +2 -0
  337. package/dist/permissions/modes.js +1 -1
  338. package/dist/permissions/pipeline.d.ts +43 -4
  339. package/dist/permissions/pipeline.js +114 -9
  340. package/dist/permissions/readonly-bash.d.ts +30 -0
  341. package/dist/permissions/readonly-bash.js +118 -0
  342. package/dist/permissions/types.d.ts +42 -0
  343. package/dist/plan/compose.d.ts +9 -0
  344. package/dist/plan/compose.js +19 -0
  345. package/dist/plan/controller.d.ts +71 -0
  346. package/dist/plan/controller.js +27 -0
  347. package/dist/plan/done-markers.d.ts +17 -0
  348. package/dist/plan/done-markers.js +51 -0
  349. package/dist/plan/extract.d.ts +29 -0
  350. package/dist/plan/extract.js +160 -0
  351. package/dist/plan/prompts.d.ts +36 -0
  352. package/dist/plan/prompts.js +89 -0
  353. package/dist/plan/store.d.ts +37 -0
  354. package/dist/plan/store.js +90 -0
  355. package/dist/rpc.d.ts +58 -2
  356. package/dist/rpc.js +7 -0
  357. package/dist/sandbox/bash.d.ts +74 -0
  358. package/dist/sandbox/bash.js +126 -0
  359. package/dist/sandbox/detect.d.ts +48 -0
  360. package/dist/sandbox/detect.js +149 -0
  361. package/dist/sandbox/index.d.ts +5 -0
  362. package/dist/sandbox/index.js +5 -0
  363. package/dist/sandbox/profile.d.ts +33 -0
  364. package/dist/sandbox/profile.js +76 -0
  365. package/dist/sandbox/wrap.d.ts +29 -0
  366. package/dist/sandbox/wrap.js +117 -0
  367. package/dist/sdk.d.ts +33 -2
  368. package/dist/sdk.js +24 -2
  369. package/dist/session/types.d.ts +3 -1
  370. package/dist/session/types.js +1 -0
  371. package/dist/skills/builtin.d.ts +31 -0
  372. package/dist/skills/builtin.js +109 -0
  373. package/dist/skills/discover.d.ts +2 -1
  374. package/dist/skills/index-prompt.js +5 -8
  375. package/dist/tools/background-jobs.d.ts +71 -0
  376. package/dist/tools/background-jobs.js +198 -0
  377. package/dist/tools/bash.d.ts +53 -1
  378. package/dist/tools/bash.js +158 -7
  379. package/dist/tools/clipboard-image.d.ts +44 -0
  380. package/dist/tools/clipboard-image.js +148 -0
  381. package/dist/tools/edit.d.ts +2 -0
  382. package/dist/tools/edit.js +18 -3
  383. package/dist/tools/image-file.d.ts +38 -3
  384. package/dist/tools/image-file.js +81 -10
  385. package/dist/tools/image-resize.d.ts +58 -0
  386. package/dist/tools/image-resize.js +97 -0
  387. package/dist/tools/presets.d.ts +7 -2
  388. package/dist/tools/presets.js +11 -4
  389. package/dist/tools/read.d.ts +5 -2
  390. package/dist/tools/read.js +13 -10
  391. package/dist/tools/registry.d.ts +7 -1
  392. package/dist/tools/registry.js +27 -4
  393. package/dist/tools/task-ctl.d.ts +22 -0
  394. package/dist/tools/task-ctl.js +116 -0
  395. package/dist/tools/task.d.ts +22 -10
  396. package/dist/tools/task.js +66 -26
  397. package/dist/tools/todo.d.ts +22 -2
  398. package/dist/tools/todo.js +58 -14
  399. package/dist/tools/truncate.d.ts +15 -0
  400. package/dist/tools/truncate.js +25 -0
  401. package/dist/tools/types.d.ts +113 -0
  402. package/dist/tools/types.js +5 -0
  403. package/dist/tools/write.js +3 -0
  404. package/dist/tui/glyphs.d.ts +4 -0
  405. package/dist/tui/glyphs.js +4 -0
  406. package/dist/tui/keybindings.d.ts +5 -0
  407. package/dist/tui/keybindings.js +5 -0
  408. package/docs/acp.md +78 -0
  409. package/docs/agents.md +250 -0
  410. package/docs/codemode.md +12 -8
  411. package/docs/hooks.md +26 -22
  412. package/docs/permissions.md +77 -13
  413. package/docs/plan.md +107 -0
  414. package/docs/providers.md +228 -52
  415. package/docs/rewind-plan.md +181 -0
  416. package/docs/rpc.md +92 -5
  417. package/docs/sandbox.md +215 -0
  418. package/docs/session-format.md +41 -21
  419. package/docs/sessions.md +41 -1
  420. package/docs/tui-design.md +751 -0
  421. package/docs/tui.md +125 -6
  422. package/package.json +13 -2
@@ -0,0 +1,315 @@
1
+ /**
2
+ * `ama --mode acp`:把 ama 的会话暴露为 ACP Agent(docs/wave5-plan.md §5.6,D14)。[W5-E]
3
+ *
4
+ * 与 `--mode rpc` 同一引擎(Runtime + 组装根的会话切换),一次只有一个活动会话:
5
+ * - `session/new` 新开(启动时那个空会话第一次直接认领);`session/load` 切到该会话并回放历史;
6
+ * `session/resume` 切换不回放;`session/list` 列本 cwd 的会话;`session/close` 中断并释放;
7
+ * 对非活动会话发 `session/prompt` 时(空闲)先切过去。
8
+ * - `session/prompt` → `prompt`(文本 + 图片;资源链接以 `@uri` 文本给出,嵌入资源取文本);
9
+ * 回合结束按会话状态给 stopReason:中断 → cancelled,输出截断 → max_tokens,出错 → JSON-RPC 错误。
10
+ * - `session/cancel` → `abort`;`session/set_mode` → `setPermissionMode`(模式 id = ama 权限模式)。
11
+ * - 审批:ama 的审批请求经 `session/request_permission` 交给客户端(允许 / 本会话允许 / 拒绝);
12
+ * 客户端回 cancelled、连接断开或回合被中断 → 交给链上下一个回答者(无人 → 拒绝)。
13
+ * - 不声明 / 不使用客户端的 `fs`、`terminal` 能力(Armadra Q5);ama 自己读写、自己跑命令。
14
+ * - cwd 固定为启动目录(信任与项目配置都按它判定);客户端给别的 cwd → invalid params。
15
+ */
16
+ import { realpathSync } from "node:fs";
17
+ import { resolve } from "node:path";
18
+ import { currentSession, switchSession } from "../../cli/compose-session.js";
19
+ import { listSessions } from "../../cli/compose-store.js";
20
+ import { AgentSessionImpl } from "../../agent/session.js";
21
+ import { JsonRpcPeer, RpcError } from "../../drivers/jsonrpc.js";
22
+ import { ACP_METHODS, ACP_PROTOCOL_VERSION, RPC_ERRORS, } from "../../drivers/acp/types.js";
23
+ import { isPermissionMode } from "../../permissions/modes.js";
24
+ import { AMA_VERSION } from "../../version.js";
25
+ import { errorText } from "../shared.js";
26
+ import { AcpEventMapper, permissionModes, toolKind, toolLocations, toolTitle, } from "./acp-events.js";
27
+ export const ACP_AGENT_CAPABILITIES = {
28
+ loadSession: true,
29
+ promptCapabilities: { image: true, audio: false, embeddedContext: true },
30
+ mcpCapabilities: { http: false, sse: false },
31
+ sessionCapabilities: { list: {}, resume: {}, close: {} },
32
+ };
33
+ /** 解析符号链接后的绝对路径(macOS 的 /var → /private/var 等);不存在时退回 resolve。 */
34
+ function canonical(path) {
35
+ try {
36
+ return realpathSync.native(resolve(path));
37
+ }
38
+ catch {
39
+ return resolve(path);
40
+ }
41
+ }
42
+ function str(params, key) {
43
+ const value = params[key];
44
+ if (typeof value !== "string" || value === "")
45
+ throw new RpcError(RPC_ERRORS.invalidParams, `缺少 ${key}`);
46
+ return value;
47
+ }
48
+ /** ACP 提示 → ama 的文本与图片。 */
49
+ export function promptOf(blocks) {
50
+ if (!Array.isArray(blocks))
51
+ throw new RpcError(RPC_ERRORS.invalidParams, "prompt 应为内容块数组");
52
+ const parts = [];
53
+ const images = [];
54
+ for (const block of blocks) {
55
+ if (block.type === "text")
56
+ parts.push(block.text);
57
+ else if (block.type === "image")
58
+ images.push({ type: "image", data: block.data, mimeType: block.mimeType });
59
+ else if (block.type === "resource_link")
60
+ parts.push(`@${block.uri}`);
61
+ else if (block.type === "resource" && typeof block.resource.text === "string")
62
+ parts.push(`<resource uri="${block.resource.uri}">\n${block.resource.text}\n</resource>`);
63
+ }
64
+ return { text: parts.join("\n"), images };
65
+ }
66
+ export class AcpServer {
67
+ runtime;
68
+ log;
69
+ peer;
70
+ activeId;
71
+ /** 启动时的空会话还没被 session/new 认领。 */
72
+ freshUnclaimed = true;
73
+ mapper;
74
+ unsubscribe = () => undefined;
75
+ cancelRequested = false;
76
+ cwd;
77
+ constructor(runtime, streams, log = () => undefined) {
78
+ this.runtime = runtime;
79
+ this.log = log;
80
+ this.cwd = canonical(runtime.paths.cwd);
81
+ this.mapper = this.makeMapper();
82
+ this.peer = new JsonRpcPeer({
83
+ input: streams.input,
84
+ output: streams.output,
85
+ onRequest: (method, params) => this.handle(method, (params ?? {})),
86
+ onNotification: (method, params) => {
87
+ if (method === ACP_METHODS.sessionCancel)
88
+ void this.cancel((params ?? {}));
89
+ },
90
+ onProtocolError: (_line, reason) => this.log(`ACP:无法解析的输入(${reason})`),
91
+ });
92
+ this.subscribe();
93
+ }
94
+ session() {
95
+ return currentSession(this.runtime);
96
+ }
97
+ /** 审批交给客户端:ama 的 UI broker。 */
98
+ broker = {
99
+ ask: (request, signal) => this.askClient(request, signal),
100
+ };
101
+ makeMapper() {
102
+ return new AcpEventMapper(this.cwd, (update) => this.emit(update), () => this.session());
103
+ }
104
+ emit(update) {
105
+ if (this.activeId === undefined)
106
+ return;
107
+ void this.peer.notify(ACP_METHODS.sessionUpdate, { sessionId: this.activeId, update });
108
+ }
109
+ subscribe() {
110
+ this.unsubscribe();
111
+ const mapper = this.mapper;
112
+ this.unsubscribe = this.session().subscribe((event) => mapper.onEvent(event));
113
+ }
114
+ dispose() {
115
+ this.unsubscribe();
116
+ }
117
+ async handle(method, params) {
118
+ switch (method) {
119
+ case ACP_METHODS.initialize:
120
+ return this.initialize(params);
121
+ case ACP_METHODS.sessionNew:
122
+ return this.newSession(params);
123
+ case ACP_METHODS.sessionLoad:
124
+ return this.openSession(params, true);
125
+ case ACP_METHODS.sessionResume:
126
+ return this.openSession(params, false);
127
+ case ACP_METHODS.sessionList:
128
+ return this.list();
129
+ case ACP_METHODS.sessionClose:
130
+ return this.close(params);
131
+ case ACP_METHODS.sessionPrompt:
132
+ return this.prompt(params);
133
+ case ACP_METHODS.sessionSetMode:
134
+ return this.setMode(params);
135
+ default:
136
+ throw new RpcError(RPC_ERRORS.methodNotFound, `method not found: ${method}`);
137
+ }
138
+ }
139
+ initialize(params) {
140
+ if (typeof params["protocolVersion"] !== "number")
141
+ throw new RpcError(RPC_ERRORS.invalidParams, "缺少 protocolVersion");
142
+ return {
143
+ protocolVersion: ACP_PROTOCOL_VERSION,
144
+ agentCapabilities: ACP_AGENT_CAPABILITIES,
145
+ authMethods: [],
146
+ agentInfo: { name: "ama", title: "ama", version: AMA_VERSION },
147
+ };
148
+ }
149
+ checkCwd(params) {
150
+ const cwd = params["cwd"];
151
+ if (cwd === undefined)
152
+ return;
153
+ if (typeof cwd !== "string" || canonical(cwd) !== this.cwd)
154
+ throw new RpcError(RPC_ERRORS.invalidParams, `ama --mode acp 的会话目录固定为启动目录 ${this.cwd}(收到 ${String(cwd)})`);
155
+ }
156
+ modes() {
157
+ return permissionModes(this.session().state.permissionMode);
158
+ }
159
+ async switchTo(next, reason) {
160
+ const session = await next;
161
+ this.mapper = this.makeMapper();
162
+ this.subscribe();
163
+ if (session instanceof AgentSessionImpl)
164
+ session.announceStart(reason);
165
+ }
166
+ async newSession(params) {
167
+ this.checkCwd(params);
168
+ const current = this.session();
169
+ if (this.freshUnclaimed && current.messages.length === 0) {
170
+ this.freshUnclaimed = false;
171
+ }
172
+ else {
173
+ this.ensureIdle();
174
+ await this.switchTo(switchSession(this.runtime, { kind: "new" }), "new");
175
+ }
176
+ this.activeId = this.session().state.sessionId;
177
+ return { sessionId: this.activeId, modes: this.modes() };
178
+ }
179
+ async openSession(params, replay) {
180
+ const id = str(params, "sessionId");
181
+ this.checkCwd(params);
182
+ if (this.session().state.sessionId !== id) {
183
+ this.ensureIdle();
184
+ try {
185
+ await this.switchTo(switchSession(this.runtime, { kind: "resume", id }), "resume");
186
+ }
187
+ catch (error) {
188
+ throw new RpcError(RPC_ERRORS.resourceNotFound, `找不到会话 ${id}:${errorText(error)}`);
189
+ }
190
+ }
191
+ this.freshUnclaimed = false;
192
+ this.activeId = id;
193
+ if (replay) {
194
+ this.mapper.replay(this.session().messages);
195
+ await this.peer.flush();
196
+ }
197
+ return { modes: this.modes() };
198
+ }
199
+ list() {
200
+ // 会话目录按 ama 启动时的 cwd 字串分组(不是 realpath)
201
+ const items = listSessions({
202
+ sessionDir: this.runtime.paths.sessionDir,
203
+ cwd: this.runtime.paths.cwd,
204
+ });
205
+ return {
206
+ sessions: items.map((item) => ({
207
+ sessionId: item.id,
208
+ cwd: item.cwd,
209
+ title: item.name ?? item.firstPrompt ?? null,
210
+ updatedAt: item.modifiedAt,
211
+ })),
212
+ };
213
+ }
214
+ async close(params) {
215
+ const id = str(params, "sessionId");
216
+ if (id === this.activeId) {
217
+ if (this.session().state.isStreaming)
218
+ await this.session().abort();
219
+ this.activeId = undefined;
220
+ }
221
+ return {};
222
+ }
223
+ ensureIdle() {
224
+ if (this.session().state.isStreaming)
225
+ throw new RpcError(RPC_ERRORS.invalidRequest, "当前会话正在运行,先 session/cancel");
226
+ }
227
+ async prompt(params) {
228
+ const id = str(params, "sessionId");
229
+ if (id !== this.activeId)
230
+ await this.openSession({ sessionId: id }, false);
231
+ const { text, images } = promptOf(params["prompt"]);
232
+ const session = this.session();
233
+ const before = session.getStats().tokens;
234
+ this.cancelRequested = false;
235
+ try {
236
+ await session.prompt(text, images.length > 0 ? { images } : {});
237
+ }
238
+ catch (error) {
239
+ throw new RpcError(RPC_ERRORS.internalError, errorText(error));
240
+ }
241
+ await this.peer.flush();
242
+ const after = session.getStats().tokens;
243
+ const usage = {
244
+ inputTokens: after.input - before.input,
245
+ outputTokens: after.output - before.output,
246
+ cachedReadTokens: after.cacheRead - before.cacheRead,
247
+ cachedWriteTokens: after.cacheWrite - before.cacheWrite,
248
+ totalTokens: after.total - before.total,
249
+ };
250
+ const last = [...session.messages].reverse().find((m) => "role" in m && m.role === "assistant");
251
+ const reason = last !== undefined && "stopReason" in last ? last.stopReason : "stop";
252
+ if (this.cancelRequested || reason === "aborted")
253
+ return { stopReason: "cancelled", usage };
254
+ if (reason === "error") {
255
+ const message = last !== undefined && "errorMessage" in last ? last.errorMessage : undefined;
256
+ throw new RpcError(RPC_ERRORS.internalError, message ?? "模型请求失败");
257
+ }
258
+ return { stopReason: reason === "length" ? "max_tokens" : "end_turn", usage };
259
+ }
260
+ async cancel(params) {
261
+ if (params["sessionId"] !== this.activeId)
262
+ return;
263
+ this.cancelRequested = true;
264
+ await this.session()
265
+ .abort()
266
+ .catch(() => undefined);
267
+ }
268
+ setMode(params) {
269
+ str(params, "sessionId");
270
+ const mode = params["modeId"];
271
+ if (!isPermissionMode(mode))
272
+ throw new RpcError(RPC_ERRORS.invalidParams, `未知模式:${String(mode)}`);
273
+ this.session().setPermissionMode(mode);
274
+ return {};
275
+ }
276
+ async askClient(request, signal) {
277
+ const sessionId = this.activeId;
278
+ if (sessionId === undefined || !this.peer.isOpen)
279
+ return undefined;
280
+ const toolCallId = this.mapper.toolCallIdFor(request.toolName, request.input) ?? request.requestId;
281
+ const locations = toolLocations(request.input, this.cwd);
282
+ let answer;
283
+ try {
284
+ answer = await this.peer.request(ACP_METHODS.requestPermission, {
285
+ sessionId,
286
+ toolCall: {
287
+ toolCallId,
288
+ title: toolTitle(request.toolName, request.input),
289
+ kind: toolKind(request.toolName),
290
+ status: "pending",
291
+ rawInput: request.input,
292
+ ...(locations !== undefined ? { locations } : {}),
293
+ },
294
+ options: [
295
+ { optionId: "allow_once", name: "允许", kind: "allow_once" },
296
+ { optionId: "allow_always", name: "本会话允许", kind: "allow_always" },
297
+ { optionId: "reject_once", name: "拒绝", kind: "reject_once" },
298
+ ],
299
+ }, signal);
300
+ }
301
+ catch {
302
+ return undefined;
303
+ }
304
+ const outcome = answer?.outcome;
305
+ if (outcome?.outcome !== "selected")
306
+ return undefined;
307
+ if (outcome.optionId === "allow_once")
308
+ return "allow";
309
+ if (outcome.optionId === "allow_always")
310
+ return "allow_session";
311
+ if (outcome.optionId === "reject_once")
312
+ return "deny";
313
+ return undefined;
314
+ }
315
+ }
@@ -8,14 +8,20 @@
8
8
  * `pick`(需要界面弹选择器,参数缺省时)、`exit`。
9
9
  *
10
10
  * 会话切换(/new /resume /fork)经 `ctx.switchSession`,调用方据返回的新会话重新订阅事件。
11
+ * `/rewind`(RW-C):无参数列出回滚点,带参数执行(interactive/rewind-command.ts);对话变了时
12
+ * `handled` 带 `draft`(原消息)与 `reload`,界面据此回填输入框、重画消息区。
11
13
  */
12
- import type { AgentSession } from "../agent/types.js";
14
+ import type { AgentSession, RewindDraftText } from "../agent/types.js";
13
15
  import type { SwitchRequest } from "../cli/compose-session.js";
14
16
  import type { Runtime } from "../cli/runtime.js";
15
17
  export { describeSession } from "./session-report.js";
16
18
  export type CommandResult = {
17
19
  kind: "handled";
18
20
  message?: string;
21
+ draft?: RewindDraftText;
22
+ reload?: boolean;
23
+ /** [W5-U] 命令开了一个新回合(`/plan approve`):line 模式等它跑完再收下一行。 */
24
+ wait?: boolean;
19
25
  } | {
20
26
  kind: "prompt";
21
27
  text: string;
@@ -8,12 +8,18 @@
8
8
  * `pick`(需要界面弹选择器,参数缺省时)、`exit`。
9
9
  *
10
10
  * 会话切换(/new /resume /fork)经 `ctx.switchSession`,调用方据返回的新会话重新订阅事件。
11
+ * `/rewind`(RW-C):无参数列出回滚点,带参数执行(interactive/rewind-command.ts);对话变了时
12
+ * `handled` 带 `draft`(原消息)与 `reload`,界面据此回填输入框、重画消息区。
11
13
  */
12
14
  import { AgentSessionImpl } from "../agent/session.js";
13
15
  import { WARMING_MODES } from "../ai/cache/types.js";
14
16
  import { THINKING_LEVELS } from "../ai/thinking.js";
15
17
  import { AmaError } from "../errors.js";
16
18
  import { PERMISSION_MODE_ORDER, parsePermissionMode, permissionModeLabel, } from "../permissions/modes.js";
19
+ import { pasteImage } from "./interactive/clipboard-paste.js";
20
+ import { planCommand } from "./interactive/plan-command.js";
21
+ import { rewindCommand } from "./interactive/rewind-command.js";
22
+ import { describeAgents, describeTaskOutput, describeTasks, stopTask, } from "./interactive/tasks-report.js";
17
23
  import { describeCache, describeFingerprint, describeSession } from "./session-report.js";
18
24
  export { describeSession } from "./session-report.js";
19
25
  export const BUILTIN_COMMANDS = [
@@ -22,6 +28,11 @@ export const BUILTIN_COMMANDS = [
22
28
  { name: "resume", args: "[id]", description: "恢复会话(无 id 时选择)" },
23
29
  { name: "fork", args: "[条目 id]", description: "从某条目分叉出新会话" },
24
30
  { name: "compact", args: "[说明]", description: "压缩上下文" },
31
+ {
32
+ name: "rewind",
33
+ args: "[n] [both|conversation|code|summarize-from|summarize-up-to]",
34
+ description: "回滚对话与代码到某条消息之前",
35
+ },
25
36
  { name: "model", args: "[provider/id]", description: "切换模型" },
26
37
  {
27
38
  name: "thinking",
@@ -41,6 +52,15 @@ export const BUILTIN_COMMANDS = [
41
52
  args: "[warm off|streaming|idle | fingerprint]",
42
53
  description: "缓存统计;切换本会话保温;打印前缀指纹",
43
54
  },
55
+ { name: "statusline", args: "[full|compact]", description: "底部信息行两行 / 一行(Ctrl+G)" },
56
+ {
57
+ name: "plan",
58
+ args: "[目标] | approve [模式|fresh] | reject",
59
+ description: "查看计划与状态;带目标进入 Plan 模式;批准 / 放弃待审批的计划",
60
+ },
61
+ { name: "tasks", args: "[id] | stop <id>", description: "子 Agent 任务:状态、输出、停止" },
62
+ { name: "agents", description: "子 Agent 类型与外部 Agent(安装状态、版本)" },
63
+ { name: "paste", description: "粘贴剪贴板里的图片(Ctrl+V)" },
44
64
  { name: "exit", description: "退出" },
45
65
  ];
46
66
  const ALIASES = { quit: "exit", q: "exit", "?": "help" };
@@ -80,6 +100,20 @@ function cacheCommand(session, args) {
80
100
  }
81
101
  throw new AmaError("invalid_arguments", "用法:/cache [warm off|streaming|idle | fingerprint]");
82
102
  }
103
+ /** `/tasks`、`/tasks <id>`、`/tasks stop <id>`。 */
104
+ async function tasksCommand(sessionId, args) {
105
+ const [first, second, extra] = args.split(/\s+/).filter((s) => s !== "");
106
+ const now = Date.now();
107
+ if (first === undefined)
108
+ return describeTasks(sessionId, now);
109
+ if (first === "stop" && second !== undefined && extra === undefined) {
110
+ await stopTask(sessionId, second);
111
+ return `已停止 ${second}`;
112
+ }
113
+ if (second === undefined)
114
+ return describeTaskOutput(sessionId, first, now);
115
+ throw new AmaError("invalid_arguments", "用法:/tasks [id] | /tasks stop <id>");
116
+ }
83
117
  export async function runSlashCommand(line, ctx) {
84
118
  const parsed = parseSlash(line);
85
119
  if (parsed === undefined)
@@ -166,6 +200,27 @@ export async function runSlashCommand(line, ctx) {
166
200
  return { kind: "handled", message: describeSession(session) };
167
201
  case "cache":
168
202
  return { kind: "handled", message: cacheCommand(session, args) };
203
+ case "rewind":
204
+ return { kind: "handled", ...(await rewindCommand(session, args)) };
205
+ case "plan":
206
+ return planCommand(session, args, ctx);
207
+ case "tasks":
208
+ return { kind: "handled", message: await tasksCommand(session.state.sessionId, args) };
209
+ case "agents":
210
+ return { kind: "handled", message: describeAgents(session.state.sessionId) };
211
+ case "paste": {
212
+ const pasted = await pasteImage(ctx.runtime.paths.dataDir);
213
+ return pasted.ok
214
+ ? {
215
+ kind: "handled",
216
+ message: `已粘贴图片:${pasted.path}`,
217
+ draft: { text: `${pasted.ref} ` },
218
+ }
219
+ : { kind: "handled", message: pasted.message };
220
+ }
221
+ case "statusline":
222
+ // [W5-A] 交互界面在 commands-core 之前自己处理;line 模式没有底部信息行
223
+ return { kind: "handled", message: "/statusline 只在交互界面可用" };
169
224
  default:
170
225
  return undefined;
171
226
  }
@@ -6,10 +6,13 @@
6
6
  * → 报错,不发请求;
7
7
  * - 不带 `@` 的词只在「以图片扩展名结尾且文件存在」时才算附件,当前模型不收图片时静默忽略(用户可能
8
8
  * 只是在文字里提到一个文件);
9
- * - 与 read 工具共用 image-file.ts 的 MIME 检测与 5 MB 上限。提示文字原样保留。
9
+ * - 与 read 工具共用 image-file.ts 的 MIME 检测与单图上限([W5-I] 按当前模型的端点分档,base64 后
10
+ * 计算,见 ai/image-limits.ts)。提示文字原样保留。
10
11
  */
11
12
  import type { AgentSession } from "../agent/types.js";
12
- import type { ImageBlock, Model, ProviderRegistryApi } from "../ai/types.js";
13
+ import type { ImageBlock, Model, ModelRef, ProviderRegistryApi } from "../ai/types.js";
14
+ import type { ImagesConfig } from "../config/types-w5.js";
15
+ import { type ImageFitOptions } from "../tools/image-file.js";
13
16
  export interface ImageRef {
14
17
  path: string;
15
18
  /** `@路径` 或 `--image`:显式附件。 */
@@ -22,6 +25,11 @@ export declare function sessionModel(providers: ProviderRegistryApi, session: Ag
22
25
  /**
23
26
  * 读出附件:显式附件遇到模型不收图片 → AmaError(提示换模型);隐式附件此时忽略。
24
27
  */
25
- export declare function loadPromptImages(refs: readonly ImageRef[], model: Model | undefined): Promise<ImageBlock[]>;
28
+ export declare function loadPromptImages(refs: readonly ImageRef[], model: Model | undefined, options?: ImageFitOptions): Promise<ImageBlock[]>;
26
29
  /** 提示 + 额外的显式图片路径(`--image`)→ 附件。 */
27
- export declare function promptImages(text: string, extra: readonly string[], cwd: string, model: Model | undefined): Promise<ImageBlock[]>;
30
+ export declare function promptImages(text: string, extra: readonly string[], cwd: string, model: Model | undefined, options?: ImageFitOptions): Promise<ImageBlock[]>;
31
+ /**
32
+ * read 工具的单图选项(compose.ts 接线):按会话当前模型的端点分档(查不到模型时用缺省 5 MB),
33
+ * 缩放按 `images.resize`。
34
+ */
35
+ export declare function imageFitOptionsFor(providers: ProviderRegistryApi | undefined, ref: ModelRef | undefined, images?: ImagesConfig): ImageFitOptions;
@@ -6,13 +6,15 @@
6
6
  * → 报错,不发请求;
7
7
  * - 不带 `@` 的词只在「以图片扩展名结尾且文件存在」时才算附件,当前模型不收图片时静默忽略(用户可能
8
8
  * 只是在文字里提到一个文件);
9
- * - 与 read 工具共用 image-file.ts 的 MIME 检测与 5 MB 上限。提示文字原样保留。
9
+ * - 与 read 工具共用 image-file.ts 的 MIME 检测与单图上限([W5-I] 按当前模型的端点分档,base64 后
10
+ * 计算,见 ai/image-limits.ts)。提示文字原样保留。
10
11
  */
11
12
  import { existsSync } from "node:fs";
12
13
  import { homedir } from "node:os";
13
14
  import { resolve } from "node:path";
14
15
  import { formatModelRef } from "../ai/providers/channels.js";
15
16
  import { AmaError } from "../errors.js";
17
+ import { imageLimits } from "../ai/image-limits.js";
16
18
  import { imageMimeFromPath, loadImageFile } from "../tools/image-file.js";
17
19
  const TOKEN = /@?"([^"]+)"|@?'([^']+)'|((?:\\ |\S)+)/g;
18
20
  function expand(path, cwd) {
@@ -52,7 +54,7 @@ export function sessionModel(providers, session) {
52
54
  /**
53
55
  * 读出附件:显式附件遇到模型不收图片 → AmaError(提示换模型);隐式附件此时忽略。
54
56
  */
55
- export async function loadPromptImages(refs, model) {
57
+ export async function loadPromptImages(refs, model, options = {}) {
56
58
  const accepts = model === undefined || model.input.includes("image");
57
59
  const wanted = refs.filter((ref) => ref.explicit || accepts);
58
60
  if (wanted.length === 0)
@@ -62,17 +64,32 @@ export async function loadPromptImages(refs, model) {
62
64
  throw new AmaError("invalid_arguments", `${name} 不接受图片输入(模型 input 没有 image);换一个支持图像的模型再试` +
63
65
  `(ama models list 里标「图片」的,或在配置里给该模型写 "input": ["text", "image"])`);
64
66
  }
67
+ const fit = {
68
+ ...(model !== undefined ? { maxBase64Bytes: imageLimits(model).perImageBase64 } : {}),
69
+ ...options,
70
+ };
65
71
  const blocks = [];
66
72
  for (const ref of wanted)
67
- blocks.push((await loadImageFile(ref.path)).block);
73
+ blocks.push((await loadImageFile(ref.path, fit)).block);
68
74
  return blocks;
69
75
  }
70
76
  /** 提示 + 额外的显式图片路径(`--image`)→ 附件。 */
71
- export async function promptImages(text, extra, cwd, model) {
77
+ export async function promptImages(text, extra, cwd, model, options = {}) {
72
78
  const refs = [
73
79
  ...extra.map((path) => ({ path: expand(path, cwd), explicit: true })),
74
80
  ...findImageRefs(text, cwd),
75
81
  ];
76
82
  const unique = refs.filter((ref, i) => refs.findIndex((r) => r.path === ref.path) === i);
77
- return loadPromptImages(unique, model);
83
+ return loadPromptImages(unique, model, options);
84
+ }
85
+ /**
86
+ * read 工具的单图选项(compose.ts 接线):按会话当前模型的端点分档(查不到模型时用缺省 5 MB),
87
+ * 缩放按 `images.resize`。
88
+ */
89
+ export function imageFitOptionsFor(providers, ref, images) {
90
+ const resize = images?.resize !== undefined ? { resize: images.resize } : {};
91
+ if (providers === undefined || ref === undefined)
92
+ return resize;
93
+ const found = providers.findModel(formatModelRef(ref));
94
+ return found.ok ? { maxBase64Bytes: imageLimits(found.model).perImageBase64, ...resize } : resize;
78
95
  }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * 第五波的消息区面板(左竖条卡片,与 `/session` 同一手法):`/plan`、`/agents`、`/tasks` 展开的任务输出。
3
+ * [W5-U] line 模式用 plan-command.ts / tasks-report.ts 的文本版。
4
+ */
5
+ import type { AgentSession } from "../../agent/types.js";
6
+ import type { PlanController } from "../../plan/controller.js";
7
+ import type { TaskInfo } from "../../tools/types.js";
8
+ import { type Component, type Theme } from "../../tui.js";
9
+ /** `/plan`:当前计划、状态、模式、步骤与待办进度。 */
10
+ export declare function planPanel(controller: PlanController, session: AgentSession, theme: Theme, displayPath?: (path: string) => string): Component;
11
+ /** `/agents`:类型、runner、来源、安装状态与版本、说明。 */
12
+ export declare function agentsPanel(sessionId: string, theme: Theme): Component;
13
+ /** `/tasks` 选中一个任务:状态行 + 输出全文(运行中为已有输出)。 */
14
+ export declare function taskOutputPanel(task: TaskInfo, output: string | undefined, theme: Theme, now: number, displayPath?: (path: string) => string): Component;
@@ -0,0 +1,103 @@
1
+ /**
2
+ * 第五波的消息区面板(左竖条卡片,与 `/session` 同一手法):`/plan`、`/agents`、`/tasks` 展开的任务输出。
3
+ * [W5-U] line 模式用 plan-command.ts / tasks-report.ts 的文本版。
4
+ */
5
+ import { permissionModeLabel } from "../../permissions/modes.js";
6
+ import { executionMode } from "../../plan/store.js";
7
+ import { Card, wrapTextWithAnsi } from "../../tui.js";
8
+ import { Indent, Stack, keyValue } from "./panels.js";
9
+ import { planStatusText, planTitle } from "./plan-command.js";
10
+ import { agentFacts, listAgents, taskFacts } from "./tasks-report.js";
11
+ import { cleanLines } from "./tool-summary.js";
12
+ /** 折行显示的若干行。 */
13
+ class Lines {
14
+ lines;
15
+ constructor(lines) {
16
+ this.lines = lines;
17
+ }
18
+ render(width) {
19
+ return this.lines.flatMap((line) => (line === "" ? [""] : wrapTextWithAnsi(line, width)));
20
+ }
21
+ invalidate() { }
22
+ }
23
+ const STATUS_COLOR = {
24
+ proposed: "warning",
25
+ approved: "success",
26
+ rejected: "dim",
27
+ superseded: "dim",
28
+ };
29
+ /** `/plan`:当前计划、状态、模式、步骤与待办进度。 */
30
+ export function planPanel(controller, session, theme, displayPath = (p) => p) {
31
+ const plan = controller.current();
32
+ const mode = session.state.permissionMode;
33
+ const modeText = mode === "plan"
34
+ ? `Plan${theme.fg("dim", `(批准后回到 ${permissionModeLabel(executionMode(undefined, controller.prePlanMode()))})`)}`
35
+ : permissionModeLabel(mode);
36
+ if (plan === null) {
37
+ const hint = mode === "plan"
38
+ ? "还没有计划:模型给出 <proposed_plan> 后在这里审批"
39
+ : "没有计划。/plan <目标> 进入 Plan 模式";
40
+ return new Card(new Lines([theme.fg("muted", hint)]), { theme, title: "计划" });
41
+ }
42
+ const rows = [
43
+ { key: "状态", value: theme.fg(STATUS_COLOR[plan.status], planStatusText(plan.status)) },
44
+ { key: "模式", value: modeText },
45
+ ];
46
+ const todos = controller.todos();
47
+ if (todos.length > 0) {
48
+ const done = todos.filter((t) => t.status === "done").length;
49
+ const current = todos.find((t) => t.status === "in_progress");
50
+ rows.push({
51
+ key: "待办",
52
+ value: `${done}/${todos.length} 完成${current !== undefined ? theme.fg("dim", ` · 进行中 ${current.text}`) : ""}`,
53
+ });
54
+ }
55
+ const parts = [theme.bold(planTitle(plan)), keyValue(rows, theme)];
56
+ if (plan.steps.length > 0) {
57
+ parts.push(theme.bold(`步骤(${plan.steps.length})`));
58
+ parts.push(new Indent(new Lines(plan.steps.map((s) => `${theme.fg("dim", s.id)} ${s.text}`)), 2));
59
+ }
60
+ if (plan.status === "proposed")
61
+ parts.push(new Lines([
62
+ theme.fg("dim", "/plan approve [模式|fresh] 批准 · /plan reject 放弃 · 直接输入修改意见"),
63
+ ]));
64
+ return new Card(new Stack(parts), {
65
+ theme,
66
+ title: `计划 v${plan.version}`,
67
+ ...(plan.filePath !== undefined ? { subtitle: displayPath(plan.filePath) } : {}),
68
+ });
69
+ }
70
+ /** `/agents`:类型、runner、来源、安装状态与版本、说明。 */
71
+ export function agentsPanel(sessionId, theme) {
72
+ const agents = listAgents(sessionId);
73
+ if (agents.length === 0)
74
+ return new Card(new Lines([theme.fg("muted", "当前会话没有子 Agent(task 工具未启用)")]), {
75
+ theme,
76
+ title: "子 Agent",
77
+ });
78
+ const rows = agents.map((agent) => {
79
+ const facts = agentFacts(agent);
80
+ const status = facts.map((f) => f === "未安装" ? theme.fg("warning", f) : f.startsWith("已安装") ? theme.fg("success", f) : f);
81
+ return {
82
+ key: agent.name,
83
+ value: `${status.join(theme.fg("dim", " · "))} ${theme.fg("muted", agent.description.replace(/\s+/g, " "))}`,
84
+ };
85
+ });
86
+ return new Card(keyValue(rows, theme, true), { theme, title: `子 Agent(${agents.length})` });
87
+ }
88
+ /** `/tasks` 选中一个任务:状态行 + 输出全文(运行中为已有输出)。 */
89
+ export function taskOutputPanel(task, output, theme, now, displayPath = (p) => p) {
90
+ const body = output === undefined || output.trim() === "" ? [] : cleanLines(output.trimEnd());
91
+ const lines = [
92
+ ...(task.description.trim() !== "" ? [theme.fg("muted", task.description.trim())] : []),
93
+ ...(body.length === 0 ? [theme.fg("dim", "(还没有输出)")] : body),
94
+ ];
95
+ if (task.outputFile !== undefined)
96
+ lines.push(theme.fg("dim", `全文:${displayPath(task.outputFile)}`));
97
+ const facts = [task.agent, ...taskFacts(task, now), ...(task.background ? ["后台"] : [])];
98
+ return new Card(new Lines(lines), {
99
+ theme,
100
+ title: `任务 ${task.taskId}`,
101
+ subtitle: facts.join(" · "),
102
+ });
103
+ }