@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
@@ -3,15 +3,46 @@
3
3
  *
4
4
  * 编辑器之前的输入监听:Ctrl+C 清空输入 / 再按退出、Esc 中断(clearQueue 回填编辑器后 abort)、
5
5
  * Ctrl+D 空输入退出、Alt+Enter followUp、Alt+↑ 取回最后一条排队消息、Shift+Tab 循环权限模式、
6
- * Ctrl+O 展开工具输出与思考块、Ctrl+L 模型、Ctrl+T 思考级别。键位由 `keybindings.json` 覆盖。
6
+ * Ctrl+O 展开工具输出与思考块、Ctrl+L 模型、Ctrl+T 思考级别、Ctrl+G 底部信息行 full ↔ compact(W5-A)、
7
+ * Ctrl+V 粘贴剪贴板图片(`app.paste.image`,W5-U)。
8
+ * 键位由 `keybindings.json` 覆盖。
9
+ *
10
+ * [RW-C] 空闲时双击 Esc(`app.rewind`,double-esc.ts):输入框为空打开回滚列表,有字则清空并存进
11
+ * 输入历史;运行中 Esc 仍为中断,中断后交给 `onInterrupted`(中断即撤回)。
7
12
  */
8
13
  import { ExitCode } from "../../cli/exit-codes.js";
9
14
  import { nextCycleMode, permissionModeLabel } from "../../permissions/modes.js";
15
+ import { DOUBLE_ESC_HINT_MS, DoubleEscape } from "./double-esc.js";
16
+ import { statusLineText } from "./status-area.js";
10
17
  const DOUBLE_CTRL_C_MS = 1500;
11
18
  /** 返回输入监听器:已处理返回 true,交给编辑器返回 false。 */
12
19
  export function createKeyDispatch(deps) {
13
20
  const { keys, editor, tools, status } = deps;
14
21
  let ctrlCArmedAt = Number.NEGATIVE_INFINITY;
22
+ const esc = new DoubleEscape(deps.doubleEscMs);
23
+ const doubleEscape = () => {
24
+ switch (esc.press(deps.now(), editor.isEmpty())) {
25
+ case "arm-rewind":
26
+ deps.showHint("再按 Esc 回滚", DOUBLE_ESC_HINT_MS);
27
+ return;
28
+ case "arm-clear":
29
+ deps.showHint("再按 Esc 清空", DOUBLE_ESC_HINT_MS);
30
+ return;
31
+ case "rewind":
32
+ deps.showHint("");
33
+ deps.runCommand("/rewind");
34
+ return;
35
+ case "clear": {
36
+ // 记进输入历史再清空:↑ 可取回
37
+ const text = editor.getExpandedText();
38
+ if (text.trim() !== "")
39
+ editor.addToHistory(text);
40
+ editor.clear();
41
+ deps.showHint("已清空输入 · ↑ 取回");
42
+ return;
43
+ }
44
+ }
45
+ };
15
46
  const interrupt = () => {
16
47
  const session = deps.session();
17
48
  const queued = session.clearQueue();
@@ -37,17 +68,28 @@ export function createKeyDispatch(deps) {
37
68
  const current = editor.getText();
38
69
  editor.setText(current.trim() === "" ? last : `${last}\n${current}`);
39
70
  };
40
- const cyclePermission = () => {
41
- const session = deps.session();
42
- const next = nextCycleMode(session.state.permissionMode);
43
- session.setPermissionMode(next);
71
+ const applyMode = (mode, skipped) => {
72
+ deps.session().setPermissionMode(mode);
44
73
  status.refresh();
45
- deps.showHint(`权限模式:${permissionModeLabel(next)}`);
74
+ const label = `权限模式:${permissionModeLabel(mode)}`;
75
+ deps.showHint(skipped ? `未进入 Bypass · ${label}` : label);
76
+ };
77
+ const cyclePermission = () => {
78
+ const next = nextCycleMode(deps.session().state.permissionMode);
79
+ // 取消进入 Bypass:跳过它继续循环(回到起点 Manual),而不是停在原模式——否则不进 Bypass 就绕不回去
80
+ const settle = (ok) => ok ? applyMode(next, false) : applyMode(nextCycleMode(next), true);
81
+ const answer = deps.confirmMode?.(next) ?? true;
82
+ if (typeof answer === "boolean")
83
+ settle(answer);
84
+ else
85
+ void answer.then(settle);
46
86
  };
47
87
  return (data) => {
48
88
  if (deps.inactive())
49
89
  return false;
50
90
  const is = (action) => keys.matches(data, action);
91
+ if (!is("app.rewind"))
92
+ esc.reset();
51
93
  if (is("app.clear")) {
52
94
  if (!editor.isEmpty()) {
53
95
  editor.clear();
@@ -65,8 +107,14 @@ export function createKeyDispatch(deps) {
65
107
  }
66
108
  ctrlCArmedAt = Number.NEGATIVE_INFINITY;
67
109
  if (is("app.interrupt") && !editor.isCompletionOpen && deps.busy()) {
110
+ esc.reset();
68
111
  interrupt();
69
112
  deps.showHint("已中断");
113
+ deps.onInterrupted?.(editor.isEmpty());
114
+ return true;
115
+ }
116
+ if (is("app.rewind") && !editor.isCompletionOpen && !deps.busy()) {
117
+ doubleEscape();
70
118
  return true;
71
119
  }
72
120
  if (is("app.exit") && editor.isEmpty()) {
@@ -83,7 +131,9 @@ export function createKeyDispatch(deps) {
83
131
  dequeue();
84
132
  return true;
85
133
  }
86
- if (is("app.permission.cycle")) {
134
+ // 与补全共用的键(缺省 Tab)只在输入为空、补全未打开时切换模式;有输入时照常交给编辑器补全
135
+ if (is("app.permission.cycle") &&
136
+ (!is("tui.editor.complete") || (editor.isEmpty() && !editor.isCompletionOpen))) {
87
137
  cyclePermission();
88
138
  return true;
89
139
  }
@@ -101,6 +151,15 @@ export function createKeyDispatch(deps) {
101
151
  deps.runCommand("/thinking");
102
152
  return true;
103
153
  }
154
+ if (is("app.paste.image") && deps.onPasteImage !== undefined) {
155
+ deps.onPasteImage();
156
+ return true;
157
+ }
158
+ if (is("app.statusLine.toggle") && deps.onStatusLineToggle !== undefined) {
159
+ const mode = deps.onStatusLineToggle();
160
+ deps.showHint(statusLineText(mode));
161
+ return true;
162
+ }
104
163
  return false;
105
164
  };
106
165
  }
@@ -6,7 +6,10 @@
6
6
  * 问句之前逐行打印执行前预览(`request.preview`,W3-B9a-2)。
7
7
  * - 否则(管道):逐行读 stdin,每行依次执行(等上一条运行结束);没有审批 UI,ask → deny。
8
8
  * 模型错误在运行结束时只打印一次(重试中只显示 ↻);有运行最终失败时退出码 1。
9
- * - 斜杠命令走 commands-core(与 B7 同一语义),`pick` 退化为列出候选。
9
+ * - raw 终端里 `/permission full-auto` 进入 Bypass 前文本确认(`确认进入 Bypass?[y/N]`,本次运行确认过
10
+ * 一次后不再问;启动时已是 Bypass 视为已确认)。
11
+ * - 斜杠命令走 commands-core(与 B7 同一语义),`pick` 退化为列出候选;`/rewind` 列编号、
12
+ * `/rewind <n> …` 执行,回到的单行原消息放回编辑行。
10
13
  */
11
14
  import type { ModeContext } from "../../../cli/deps.js";
12
15
  import type { Runtime } from "../../../cli/runtime.js";
@@ -6,11 +6,16 @@
6
6
  * 问句之前逐行打印执行前预览(`request.preview`,W3-B9a-2)。
7
7
  * - 否则(管道):逐行读 stdin,每行依次执行(等上一条运行结束);没有审批 UI,ask → deny。
8
8
  * 模型错误在运行结束时只打印一次(重试中只显示 ↻);有运行最终失败时退出码 1。
9
- * - 斜杠命令走 commands-core(与 B7 同一语义),`pick` 退化为列出候选。
9
+ * - raw 终端里 `/permission full-auto` 进入 Bypass 前文本确认(`确认进入 Bypass?[y/N]`,本次运行确认过
10
+ * 一次后不再问;启动时已是 Bypass 视为已确认)。
11
+ * - 斜杠命令走 commands-core(与 B7 同一语义),`pick` 退化为列出候选;`/rewind` 列编号、
12
+ * `/rewind <n> …` 执行,回到的单行原消息放回编辑行。
10
13
  */
11
14
  import { promptImages, sessionModel } from "../../image-input.js";
15
+ import { taskRegistryView } from "../../../agent/subagent-registry.js";
12
16
  import { currentSession, switchSession } from "../../../cli/compose-session.js";
13
17
  import { ExitCode } from "../../../cli/exit-codes.js";
18
+ import { BYPASS_LINE_QUESTION, BYPASS_MODE, BYPASS_RISK_LINES, createBypassGate, } from "../../../permissions/bypass.js";
14
19
  import { previewDisplayLines } from "../../../permissions/preview.js";
15
20
  import { AMA_VERSION } from "../../../version.js";
16
21
  import { runSlashCommand } from "../../commands-core.js";
@@ -64,8 +69,21 @@ export async function runLineMode(runtime, context, options = {}) {
64
69
  await prompt(session, result.text);
65
70
  else if (result.kind === "pick")
66
71
  out(`${await pickHint(result.what, runtime, session)}\n`);
67
- else if (result.message !== undefined)
68
- out(`${result.message}\n`);
72
+ else {
73
+ if (result.message !== undefined)
74
+ out(`${result.message}\n`);
75
+ // /rewind 回到某条消息之前:单行原消息放回编辑行(多行只在输出里给出)
76
+ const draft = result.draft?.text;
77
+ if (editor !== undefined && draft !== undefined && !draft.includes("\n")) {
78
+ editor.buffer = draft;
79
+ editor.cursor = draft.length;
80
+ }
81
+ // /plan approve 开了新回合:跑完再收下一行(与直接发提示一致)
82
+ if (result.wait === true) {
83
+ await session.waitForIdle();
84
+ printer.endLine();
85
+ }
86
+ }
69
87
  }
70
88
  catch (error) {
71
89
  printer.endLine();
@@ -75,7 +93,8 @@ export async function runLineMode(runtime, context, options = {}) {
75
93
  return undefined;
76
94
  };
77
95
  const prompt = async (target, text) => {
78
- const images = await promptImages(text, [], target.state.cwd, sessionModel(runtime.providers, target));
96
+ const resize = runtime.config.images?.resize;
97
+ const images = await promptImages(text, [], target.state.cwd, sessionModel(runtime.providers, target), resize !== undefined ? { resize } : {});
79
98
  await target.prompt(text, images.length > 0 ? { images } : {});
80
99
  printer.endLine();
81
100
  };
@@ -89,13 +108,20 @@ export async function runLineMode(runtime, context, options = {}) {
89
108
  const model = session.state.model;
90
109
  io.stdout(`ama ${AMA_VERSION} · ${model?.provider}/${model?.id} · /help 查看命令,Ctrl+D 退出\n`);
91
110
  }
111
+ // /permission full-auto:进入 Bypass 前文本确认一次(管道里没有这一步,命令本身就是显式选择)
112
+ commands.confirmPermissionMode = createBypassGate(() => {
113
+ ed.hide();
114
+ io.stdout(`\n${BYPASS_RISK_LINES.map((l) => ` ${l}\n`).join("")}`);
115
+ return ed.ask(BYPASS_LINE_QUESTION, "n").then((answer) => answer === "y");
116
+ }, session.state.permissionMode === BYPASS_MODE);
92
117
  runtime.approvals.setUiBroker({
93
118
  ask: (request, signal) => new Promise((resolve) => {
94
119
  if (signal.aborted)
95
120
  return resolve(undefined);
96
121
  signal.addEventListener("abort", () => resolve(undefined), { once: true });
97
122
  const preview = previewDisplayLines(request.preview).map((l) => ` ${l}\n`);
98
- void ed.ask(`\n${preview.join("")}${approvalQuestion(request)}`, "n").then((answer) => {
123
+ const question = approvalQuestion(request, (taskId) => taskRegistryView(session.state.sessionId)?.get(taskId)?.agent);
124
+ void ed.ask(`\n${preview.join("")}${question}`, "n").then((answer) => {
99
125
  resolve(answer === "y" ? "allow" : answer === "a" ? "allow_session" : "deny");
100
126
  if (!busy)
101
127
  ed.render();
@@ -6,9 +6,10 @@ import type { AgentSession, SessionEvent } from "../../../agent/types.js";
6
6
  import type { Runtime } from "../../../cli/runtime.js";
7
7
  import type { ApprovalRequest } from "../../../permissions/types.js";
8
8
  import type { CommandResult } from "../../commands-core.js";
9
+ import { type TaskAgentLookup } from "../approval-dialog.js";
9
10
  /** 工具参数的一行摘要(第一个常见字段,折叠换行、截到 80 字符)。 */
10
11
  export declare function argsSummary(args: unknown): string;
11
- export declare function approvalQuestion(request: ApprovalRequest): string;
12
+ export declare function approvalQuestion(request: ApprovalRequest, taskAgent?: TaskAgentLookup): string;
12
13
  /** [W3-C2] 缓存提示的开关(line 模式由 runLineMode 传入)。 */
13
14
  export interface EventPrinterOptions {
14
15
  /** `cache.missNotices`(每次现取,切换会话后跟随新会话);缺省 true。 */
@@ -6,6 +6,9 @@ import { THINKING_LEVELS } from "../../../ai/thinking.js";
6
6
  import { listSessions } from "../../../cli/compose-store.js";
7
7
  import { AUTO_LAYER_TEXT, permissionModeLines } from "../../../permissions/modes.js";
8
8
  import { cacheEventNotice, warmSentNotice } from "../../session-report.js";
9
+ import { isFirstRunRequest, sourceLabel } from "../approval-dialog.js";
10
+ import { LIMIT_WARNING, backgroundJobText, limitReachedText, modelFallbackText, } from "../event-notices.js";
11
+ import { taskStatusText } from "../tasks-report.js";
9
12
  const SUMMARY_KEYS = ["command", "path", "pattern", "file_path", "url", "description", "name"];
10
13
  /** 工具参数的一行摘要(第一个常见字段,折叠换行、截到 80 字符)。 */
11
14
  export function argsSummary(args) {
@@ -21,8 +24,18 @@ export function argsSummary(args) {
21
24
  }
22
25
  return "";
23
26
  }
24
- export function approvalQuestion(request) {
25
- const task = (request.context?.depth ?? 0) > 0 ? "[task] " : "";
27
+ export function approvalQuestion(request, taskAgent) {
28
+ const label = sourceLabel(request, taskAgent);
29
+ const task = label === undefined ? "" : `${label} `;
30
+ const origin = request.context?.origin;
31
+ if (origin !== undefined) {
32
+ const where = origin.toolCall.locations?.[0];
33
+ return `${task}允许 ${origin.toolCall.title}${where !== undefined ? ` ${where}` : ""}?[y 允许 / a 本会话都允许 / N 拒绝] `;
34
+ }
35
+ if (isFirstRunRequest(request)) {
36
+ const input = request.input;
37
+ return `${task}${String(input["note"])} 允许?[y 允许 / N 拒绝] `;
38
+ }
26
39
  const summary = argsSummary(request.input);
27
40
  const auto = request.autoDecision;
28
41
  const why = request.reason === "dangerous"
@@ -131,11 +144,34 @@ export class EventPrinter {
131
144
  this.failures++;
132
145
  this.line(`ama: 错误:${error}`, true);
133
146
  }
134
- // 失败时 warning 就是同一条错误文本,不再重复打印
135
- if (event.warning !== undefined && event.warning !== error)
147
+ // 失败时 warning 就是同一条错误文本,不再重复打印;预算到限已由 limit_reached 说明
148
+ if (event.warning !== undefined &&
149
+ event.warning !== error &&
150
+ event.warning !== LIMIT_WARNING)
136
151
  this.line(`ama: ${event.warning}`, true);
137
152
  return;
138
153
  }
154
+ // [W5-U] 第五波事件
155
+ case "plan_proposed": {
156
+ const file = event.filePath !== undefined ? `(${event.filePath})` : "";
157
+ this.line(`◇ 计划 v${event.version} 待审批${file}:/plan approve [模式|fresh] 批准 · /plan reject 放弃 · 直接输入修改意见`);
158
+ return;
159
+ }
160
+ case "subagent_start":
161
+ this.line(` ↳ ${event.taskId} ${event.agent}${event.background ? "(后台)" : ""} 开始`);
162
+ return;
163
+ case "subagent_end":
164
+ this.line(` ↳ ${event.taskId} ${taskStatusText(event.status)}`);
165
+ return;
166
+ case "limit_reached":
167
+ this.line(`ama: ${limitReachedText(event)}`, true);
168
+ return;
169
+ case "model_fallback":
170
+ this.line(`ama: ${modelFallbackText(event)}`, true);
171
+ return;
172
+ case "background_job":
173
+ this.line(`ama: ${backgroundJobText(event)}`, true);
174
+ return;
139
175
  default:
140
176
  return;
141
177
  }
@@ -47,7 +47,7 @@ export declare class PrefixedText implements Component {
47
47
  render(width: number): string[];
48
48
  invalidate(): void;
49
49
  }
50
- /** 粗估 token(4 字符 ≈ 1 token),只用于思考块的流式计数。 */
50
+ export declare function displayText(text: string): string;
51
51
  export declare function estimateTokens(text: string): number;
52
52
  export declare function contentText(content: string | readonly ContentBlock[]): string;
53
53
  /** 一条助手消息:按内容块(思考 / 文本)同步子组件;工具调用块由 tool-view 单独显示。 */
@@ -16,6 +16,8 @@
16
16
  */
17
17
  import { Card, Container, Markdown, Spacer, Text, visibleWidth, wrapTextWithAnsi, } from "../../tui.js";
18
18
  import { formatCost, formatTokens } from "./status-bar.js";
19
+ import { notificationSummary } from "./subagent-view.js";
20
+ import { PLAN_APPROVED_PROMPT } from "../../plan/prompts.js";
19
21
  /** 退出摘要的时长:不足 1 分钟按秒,否则按分钟。 */
20
22
  function sessionDuration(ms) {
21
23
  const seconds = Math.max(0, Math.round(ms / 1000));
@@ -60,7 +62,11 @@ export const ORIGIN_LABELS = {
60
62
  steer: "插话",
61
63
  followUp: "之后",
62
64
  host: "宿主",
65
+ task: "子 Agent 通知",
66
+ plan: "计划",
63
67
  };
68
+ /** [W5-U] origin 为 plan 的交接消息(给模型的英文开场)在消息区的说法。 */
69
+ const PLAN_HANDOFF_TEXT = "按批准的计划开始执行";
64
70
  /** 首行带前缀、续行按 `indent` 缩进的文本(用户消息、排队消息)。 */
65
71
  export class PrefixedText {
66
72
  prefix;
@@ -155,6 +161,11 @@ class ThinkingView {
155
161
  }
156
162
  }
157
163
  /** 粗估 token(4 字符 ≈ 1 token),只用于思考块的流式计数。 */
164
+ /** [W5-U] 计划块的开闭标签(独占一行)只给模型与提取用,消息区不显示(计划正文照常渲染)。 */
165
+ const PLAN_TAG_LINE = /^[ \t]*<\/?proposed_plan>[ \t]*$/gm;
166
+ export function displayText(text) {
167
+ return text.includes("proposed_plan>") ? text.replace(PLAN_TAG_LINE, "") : text;
168
+ }
158
169
  export function estimateTokens(text) {
159
170
  return Math.ceil(text.length / 4);
160
171
  }
@@ -214,8 +225,9 @@ export class AssistantView extends Container {
214
225
  if (block.text.trim() === "")
215
226
  return;
216
227
  const view = this.part(index, () => this.options.markdown === false ? new Text("") : new Markdown("", { theme }));
217
- if (view.getText() !== block.text)
218
- view.setText(block.text);
228
+ const text = displayText(block.text);
229
+ if (view.getText() !== text)
230
+ view.setText(text);
219
231
  order.push(view);
220
232
  });
221
233
  const gap = this.options.compact !== true;
@@ -339,6 +351,16 @@ export class MessageView extends Container {
339
351
  return;
340
352
  }
341
353
  const label = ORIGIN_LABELS[origin] ?? origin;
354
+ // [W5-U] 后台子 Agent 的 <task-notification> 是给模型的全文,消息区只留一行
355
+ const summary = origin === "task"
356
+ ? notificationSummary(text)
357
+ : origin === "plan" && text === PLAN_APPROVED_PROMPT
358
+ ? PLAN_HANDOFF_TEXT
359
+ : undefined;
360
+ if (summary !== undefined) {
361
+ this.add(new PrefixedText(t.fg("dim", t.glyphs.queued) + " " + t.fg("muted", `${label} `), t.fg("muted", summary)));
362
+ return;
363
+ }
342
364
  this.add(new PrefixedText(t.fg("dim", t.glyphs.queued) + " " + t.fg("muted", `${label} `), t.fg("muted", text)));
343
365
  }
344
366
  newAssistant() {
@@ -4,12 +4,29 @@
4
4
  *
5
5
  * - `/session`:标题 `会话 <id 前 8 位> · <文件>`;模型 / 消息 / 用量 / 上下文(余量表,阈值着色),
6
6
  * 空一行后「缓存」段(与 `/cache` 共用 `cacheRows`);
7
+ * - [W5-U] `/session` 多「子 Agent」行(任务汇总)与「外部 Agent」段(按 Agent 的运行次数与用量,单位不换算);
7
8
  * - `/permissions`:权限模式、判定顺序(折行对齐值列)、规则(allow success、deny error、来源 dim)、
8
9
  * 最近的 auto 判定。
9
10
  */
10
11
  import type { AgentSession } from "../../agent/types.js";
11
12
  import type { Runtime } from "../../cli/runtime.js";
12
- import { type Component, type Theme } from "../../tui.js";
13
+ import { KeyValue, type Component, type KeyValueRow, type Theme } from "../../tui.js";
14
+ /** 多段内容按顺序拼起来(段之间不加空行,由调用方放空串)。 */
15
+ export declare class Stack implements Component {
16
+ private readonly parts;
17
+ constructor(parts: readonly (Component | string)[]);
18
+ render(width: number): string[];
19
+ invalidate(): void;
20
+ }
21
+ /** 缩进若干列的子组件。 */
22
+ export declare class Indent implements Component {
23
+ private readonly child;
24
+ private readonly columns;
25
+ constructor(child: Component, columns: number);
26
+ render(width: number): string[];
27
+ invalidate(): void;
28
+ }
29
+ export declare function keyValue(rows: readonly KeyValueRow[], theme: Theme, wrap?: boolean): KeyValue;
13
30
  export declare function sessionPanel(session: AgentSession, theme: Theme, options?: {
14
31
  now?: number;
15
32
  home?: string;
@@ -4,15 +4,16 @@
4
4
  *
5
5
  * - `/session`:标题 `会话 <id 前 8 位> · <文件>`;模型 / 消息 / 用量 / 上下文(余量表,阈值着色),
6
6
  * 空一行后「缓存」段(与 `/cache` 共用 `cacheRows`);
7
+ * - [W5-U] `/session` 多「子 Agent」行(任务汇总)与「外部 Agent」段(按 Agent 的运行次数与用量,单位不换算);
7
8
  * - `/permissions`:权限模式、判定顺序(折行对齐值列)、规则(allow success、deny error、来源 dim)、
8
9
  * 最近的 auto 判定。
9
10
  */
10
11
  import { AUTO_LAYER_TEXT, permissionModeLabel } from "../../permissions/modes.js";
11
12
  import { Card, KeyValue, Meter, } from "../../tui.js";
12
- import { cacheRows, formatTokenCount, formatUsd } from "../session-report.js";
13
+ import { cacheRows, externalRows, formatTokenCount, formatUsd, taskStatsText, } from "../session-report.js";
13
14
  import { tildePath } from "../../cli/startup-screen.js";
14
15
  /** 多段内容按顺序拼起来(段之间不加空行,由调用方放空串)。 */
15
- class Stack {
16
+ export class Stack {
16
17
  parts;
17
18
  constructor(parts) {
18
19
  this.parts = parts;
@@ -27,7 +28,7 @@ class Stack {
27
28
  }
28
29
  }
29
30
  /** 缩进若干列的子组件。 */
30
- class Indent {
31
+ export class Indent {
31
32
  child;
32
33
  columns;
33
34
  constructor(child, columns) {
@@ -44,7 +45,7 @@ class Indent {
44
45
  this.child.invalidate();
45
46
  }
46
47
  }
47
- function keyValue(rows, theme, wrap = false) {
48
+ export function keyValue(rows, theme, wrap = false) {
48
49
  return new KeyValue(rows, { theme, maxKeyRatio: 0.3, ...(wrap ? { wrap: true } : {}) });
49
50
  }
50
51
  function cacheSection(session, theme, now) {
@@ -93,8 +94,21 @@ export function sessionPanel(session, theme, options = {}) {
93
94
  },
94
95
  { key: "上下文", value: meter + window },
95
96
  ];
97
+ const tasks = taskStatsText(session);
98
+ if (tasks !== undefined)
99
+ rows.push({ key: "子 Agent", value: tasks });
100
+ const external = externalRows(session);
101
+ const externalSection = external.length > 0
102
+ ? ["", theme.bold("外部 Agent"), new Indent(keyValue(external, theme), 2)]
103
+ : [];
96
104
  const file = state.sessionFile === undefined ? "未落盘" : tildePath(state.sessionFile, options.home);
97
- return new Card(new Stack([keyValue(rows, theme), "", ...cacheSection(session, theme, now)]), {
105
+ const parts = [
106
+ keyValue(rows, theme),
107
+ "",
108
+ ...cacheSection(session, theme, now),
109
+ ...externalSection,
110
+ ];
111
+ return new Card(new Stack(parts), {
98
112
  theme,
99
113
  title: `会话 ${state.sessionId.slice(0, 8)}`,
100
114
  subtitle: file,
@@ -0,0 +1,40 @@
1
+ /**
2
+ * `/plan` 的语义(line 模式与交互模式共用,经 commands-core)。[W5-U]
3
+ *
4
+ * - `/plan`:当前计划与状态(版本、状态、文件、步骤、待办进度、所处模式);
5
+ * - `/plan approve [模式|fresh]`:批准待审批的计划(没有计划块时把上一条回复当作计划,docs/wave5-plan.md
6
+ * §6.3);模式缺省回到进入 plan 前的模式,`fresh` 在新会话里执行(新建会话 → `adopt(plan)` → 发计划
7
+ * 全文);
8
+ * - `/plan reject`:放弃待审批的计划(留在 Plan 模式);
9
+ * - `/plan <目标>`:进入 Plan 模式并把目标作为提示发出。
10
+ */
11
+ import type { AgentSession, PlanData } from "../../agent/types.js";
12
+ import type { SwitchRequest } from "../../cli/compose-session.js";
13
+ import type { PermissionMode } from "../../permissions/types.js";
14
+ import { type PlanController } from "../../plan/controller.js";
15
+ export declare const PLAN_USAGE = "\u7528\u6CD5\uFF1A/plan [\u76EE\u6807] | /plan approve [\u6A21\u5F0F|fresh] | /plan reject";
16
+ export declare function planStatusText(status: PlanData["status"]): string;
17
+ /** 计划的标题:第一个 `#` 标题,没有时取第一行非空文本。 */
18
+ export declare function planTitle(plan: Pick<PlanData, "markdown">): string;
19
+ /** 批准后的执行模式(缺省回到进入 plan 前的模式;进入前就是 plan 则 default)。 */
20
+ export declare function defaultExecutionMode(controller: PlanController): PermissionMode;
21
+ /** `/plan` 的纯文本说明。 */
22
+ export declare function describePlan(controller: PlanController, session: AgentSession): string;
23
+ export interface PlanCommandContext {
24
+ switchSession(request: SwitchRequest): Promise<AgentSession>;
25
+ }
26
+ export type PlanCommandResult = {
27
+ kind: "handled";
28
+ message: string;
29
+ wait?: boolean;
30
+ } | {
31
+ kind: "prompt";
32
+ text: string;
33
+ };
34
+ /** 「新上下文执行」:新建会话、记下计划与 todo、切到执行模式,返回首条消息。 */
35
+ export declare function approveFresh(controller: PlanController, plan: PlanData, mode: PermissionMode, ctx: PlanCommandContext, editedMarkdown?: string): Promise<{
36
+ session: AgentSession;
37
+ prompt: string;
38
+ plan: PlanData;
39
+ }>;
40
+ export declare function planCommand(session: AgentSession, args: string, ctx: PlanCommandContext): Promise<PlanCommandResult>;
@@ -0,0 +1,129 @@
1
+ /**
2
+ * `/plan` 的语义(line 模式与交互模式共用,经 commands-core)。[W5-U]
3
+ *
4
+ * - `/plan`:当前计划与状态(版本、状态、文件、步骤、待办进度、所处模式);
5
+ * - `/plan approve [模式|fresh]`:批准待审批的计划(没有计划块时把上一条回复当作计划,docs/wave5-plan.md
6
+ * §6.3);模式缺省回到进入 plan 前的模式,`fresh` 在新会话里执行(新建会话 → `adopt(plan)` → 发计划
7
+ * 全文);
8
+ * - `/plan reject`:放弃待审批的计划(留在 Plan 模式);
9
+ * - `/plan <目标>`:进入 Plan 模式并把目标作为提示发出。
10
+ */
11
+ import { AmaError } from "../../errors.js";
12
+ import { parsePermissionMode, permissionModeLabel } from "../../permissions/modes.js";
13
+ import { planController } from "../../plan/controller.js";
14
+ import { executionMode } from "../../plan/store.js";
15
+ export const PLAN_USAGE = "用法:/plan [目标] | /plan approve [模式|fresh] | /plan reject";
16
+ const STATUS_TEXT = {
17
+ proposed: "待审批",
18
+ approved: "已批准",
19
+ rejected: "已放弃",
20
+ superseded: "已被新版本取代",
21
+ };
22
+ export function planStatusText(status) {
23
+ return STATUS_TEXT[status];
24
+ }
25
+ /** 计划的标题:第一个 `#` 标题,没有时取第一行非空文本。 */
26
+ export function planTitle(plan) {
27
+ const lines = plan.markdown.split(/\r?\n/).map((l) => l.trim());
28
+ const heading = lines.find((l) => /^#{1,6}\s+\S/.test(l));
29
+ if (heading !== undefined)
30
+ return heading.replace(/^#{1,6}\s+/, "");
31
+ return lines.find((l) => l !== "" && !/^<\/?proposed_plan>$/.test(l)) ?? "(无标题)";
32
+ }
33
+ /** 批准后的执行模式(缺省回到进入 plan 前的模式;进入前就是 plan 则 default)。 */
34
+ export function defaultExecutionMode(controller) {
35
+ return executionMode(undefined, controller.prePlanMode());
36
+ }
37
+ /** `/plan` 的纯文本说明。 */
38
+ export function describePlan(controller, session) {
39
+ const plan = controller.current();
40
+ const mode = session.state.permissionMode;
41
+ const pre = controller.prePlanMode();
42
+ const modeLine = mode === "plan"
43
+ ? `模式:Plan(批准后回到 ${permissionModeLabel(executionMode(undefined, pre))})`
44
+ : `模式:${permissionModeLabel(mode)}`;
45
+ if (plan === null) {
46
+ return mode === "plan"
47
+ ? [modeLine, "还没有计划:模型给出 <proposed_plan> 后在这里审批"].join("\n")
48
+ : "没有计划。/plan <目标> 进入 Plan 模式";
49
+ }
50
+ const lines = [
51
+ `计划 v${plan.version} · ${planStatusText(plan.status)} · ${planTitle(plan)}`,
52
+ ...(plan.filePath !== undefined ? [`文件:${plan.filePath}`] : []),
53
+ modeLine,
54
+ ];
55
+ if (plan.steps.length > 0) {
56
+ lines.push(`步骤(${plan.steps.length}):`);
57
+ for (const step of plan.steps)
58
+ lines.push(` ${step.id} ${step.text}`);
59
+ }
60
+ const todos = controller.todos();
61
+ if (todos.length > 0) {
62
+ const done = todos.filter((t) => t.status === "done").length;
63
+ const current = todos.find((t) => t.status === "in_progress");
64
+ lines.push(`待办:${done}/${todos.length} 完成${current !== undefined ? ` · 进行中 ${current.text}` : ""}`);
65
+ }
66
+ if (plan.status === "proposed")
67
+ lines.push("/plan approve [模式|fresh] 批准 · /plan reject 放弃 · 直接输入修改意见");
68
+ return lines.join("\n");
69
+ }
70
+ /** 「新上下文执行」:新建会话、记下计划与 todo、切到执行模式,返回首条消息。 */
71
+ export async function approveFresh(controller, plan, mode, ctx, editedMarkdown) {
72
+ const result = await controller.respond({
73
+ planId: plan.id,
74
+ decision: "approve_fresh",
75
+ mode,
76
+ ...(editedMarkdown !== undefined ? { editedMarkdown } : {}),
77
+ });
78
+ const next = await ctx.switchSession({ kind: "new" });
79
+ planController(next)?.adopt(result.plan);
80
+ next.setPermissionMode(result.mode ?? mode);
81
+ return { session: next, prompt: result.freshPrompt ?? result.plan.markdown, plan: result.plan };
82
+ }
83
+ function pendingOrLastReply(controller) {
84
+ const plan = controller.pending() ?? controller.proposeFromLastReply();
85
+ if (plan === undefined)
86
+ throw new AmaError("plan_not_found", "没有待审批的计划");
87
+ return plan;
88
+ }
89
+ export async function planCommand(session, args, ctx) {
90
+ const controller = planController(session);
91
+ const [verb = "", ...rest] = args.split(/\s+/).filter((s) => s !== "");
92
+ if (controller === undefined) {
93
+ if (args === "")
94
+ return { kind: "handled", message: "当前会话没有 Plan 能力" };
95
+ throw new AmaError("invalid_arguments", "当前会话没有 Plan 能力");
96
+ }
97
+ if (verb === "")
98
+ return { kind: "handled", message: describePlan(controller, session) };
99
+ if (verb === "approve") {
100
+ if (rest.length > 1)
101
+ throw new AmaError("invalid_arguments", PLAN_USAGE);
102
+ const plan = pendingOrLastReply(controller);
103
+ const arg = rest[0];
104
+ if (arg === "fresh") {
105
+ const fresh = await approveFresh(controller, plan, defaultExecutionMode(controller), ctx);
106
+ return { kind: "prompt", text: fresh.prompt };
107
+ }
108
+ const mode = arg === undefined ? defaultExecutionMode(controller) : parsePermissionMode(arg);
109
+ if (mode === undefined || mode === "plan")
110
+ throw new AmaError("invalid_arguments", `执行模式无效:${arg ?? ""}(${PLAN_USAGE})`);
111
+ const result = await controller.respond({ planId: plan.id, decision: "approve", mode });
112
+ return {
113
+ kind: "handled",
114
+ message: `已批准计划 v${result.plan.version},以 ${permissionModeLabel(result.mode ?? mode)} 执行`,
115
+ wait: true,
116
+ };
117
+ }
118
+ if (verb === "reject" && rest.length === 0) {
119
+ const plan = controller.pending();
120
+ if (plan === undefined)
121
+ throw new AmaError("plan_not_found", "没有待审批的计划");
122
+ await controller.respond({ planId: plan.id, decision: "reject" });
123
+ return { kind: "handled", message: `已放弃计划 v${plan.version}(仍在 Plan 模式)` };
124
+ }
125
+ // 其余:目标文本
126
+ if (session.state.permissionMode !== "plan")
127
+ session.setPermissionMode("plan");
128
+ return { kind: "prompt", text: args };
129
+ }