@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
@@ -11,30 +11,38 @@
11
11
  * - 晚绑定:`runtime.approvals.setUiBroker`(审批对话框)与 `runtime.notifier.set`(宿主通知进消息区),
12
12
  * 退出时撤下。
13
13
  * - 键位:Enter 发送(运行中 = steer),其余应用级键位见 `key-dispatch.ts`。
14
+ * - 回滚(RW-C):`/rewind` 与空闲双击 Esc 走 rewind-flow.ts;回填的原消息图片暂存,随下一条消息发送。
15
+ * - 底部(W5-A,status-area.ts):`ui.statusLine` full 时状态栏上方多一行速率行;Ctrl+G / `/statusline`
16
+ * 切换(只影响本会话)。
17
+ * - 第五波(W5-U,agent-ui.ts):计划审批框、子 Agent 折叠视图、`/tasks` `/agents` `/paste` 与 Ctrl+V、审批来源
18
+ * 标注与外部 Agent 首次运行的合并确认(approval-merge.ts)、harness 提示。
14
19
  * - 启动头按 `ui.quietStartup`(startup-header.ts):normal 框 + 模型 / 目录 / 模式 / 资源清单(窄屏或
15
20
  * `ui.compact` 去框),header 一行,silent 不输出。
16
21
  */
17
22
  import { promptImages, sessionModel } from "../image-input.js";
18
23
  import { join } from "node:path";
19
24
  import { AgentSessionImpl } from "../../agent/session.js";
25
+ import { taskRegistryView } from "../../agent/subagent-registry.js";
20
26
  import { currentSession, switchSession } from "../../cli/compose-session.js";
21
- import { startupInfo, startupScreenLevel } from "../../cli/startup-screen.js";
27
+ import { startupInfo, startupScreenLevel, tildePath } from "../../cli/startup-screen.js";
22
28
  import { KEYBINDINGS_FILE } from "../../config/paths.js";
23
29
  import { AmaError, isAmaError } from "../../errors.js";
24
- import { detectSandboxCapability } from "../../codemode/capability.js";
25
- import { effectiveCodemodeMode } from "../../tools/presets.js";
26
- import { Container, Editor, Keybindings, Loader, ProcessTerminal, Spacer, TUI, Text, createTheme, detectCapabilities, resolveAscii, resolveThemeName, loadKeybindingsFile, truncateToWidth, } from "../../tui.js";
27
- import { cacheEventNotice, cacheNoticesEnabled } from "../session-report.js";
30
+ import { Container, Editor, Keybindings, Loader, ProcessTerminal, Spacer, TUI, Text, createTheme, detectCapabilities, resolveAscii, resolveThemeName, loadKeybindingsFile, } from "../../tui.js";
28
31
  import { onTerminationSignals } from "../shared.js";
32
+ import { AgentUi } from "./agent-ui.js";
29
33
  import { ApprovalDialogBroker, approvalOutcomeText } from "./approval-dialog.js";
34
+ import { mergingBroker } from "./approval-merge.js";
30
35
  import { ALL_COMMANDS, runInteractiveCommand } from "./commands.js";
31
36
  import { InteractiveCompletion } from "./completion.js";
32
37
  import { createKeyDispatch } from "./key-dispatch.js";
33
38
  import { MessageView, exitSummaryLines } from "./message-view.js";
34
39
  import { openPicker } from "./pickers.js";
40
+ import { createRewindFlow } from "./rewind-flow.js";
35
41
  import { StartupHeader } from "./startup-header.js";
36
42
  import { QueueView, RunIndicator } from "./run-indicator.js";
37
- import { StatusBar } from "./status-bar.js";
43
+ import { StatusArea, statusLineSlash } from "./status-area.js";
44
+ import { createSessionEventHandler } from "./session-events.js";
45
+ import { SubagentTracker } from "./subagent-view.js";
38
46
  import { ToolTracker } from "./tool-view.js";
39
47
  const HINT_MS = 2500;
40
48
  const EDITOR_PLACEHOLDER = "输入消息,/ 命令,@ 文件,Shift+Enter 换行";
@@ -51,17 +59,6 @@ function loadKeys(runtime, warn) {
51
59
  warn(`keybindings.json:${w}`);
52
60
  return new Keybindings(parsed.overrides);
53
61
  }
54
- /** 一行提示;空时不占行。 */
55
- class HintLine {
56
- text = "";
57
- setText(text) {
58
- this.text = text;
59
- }
60
- render(width) {
61
- return this.text === "" ? [] : [truncateToWidth(this.text, width)];
62
- }
63
- invalidate() { }
64
- }
65
62
  export function runInteractiveMode(runtime, context, options = {}) {
66
63
  const terminal = options.terminal ?? processTerminal();
67
64
  const ui = runtime.config.ui ?? {};
@@ -83,12 +80,14 @@ export function runInteractiveMode(runtime, context, options = {}) {
83
80
  ...(ui.markdown !== undefined ? { markdown: ui.markdown } : {}),
84
81
  ...(ui.compact === true ? { compact: true } : {}),
85
82
  });
83
+ const subagents = new SubagentTracker(now);
86
84
  const tools = new ToolTracker({
87
85
  theme,
88
86
  cwd: session.state.cwd,
89
87
  getTool: (name) => session.getTools().find((tool) => tool.name === name),
90
88
  now,
91
89
  spinner: () => loader.frame,
90
+ subagent: (toolCallId) => subagents.forToolCall(toolCallId),
92
91
  });
93
92
  const queueView = new QueueView(theme);
94
93
  const loaderSlot = new Container();
@@ -100,19 +99,17 @@ export function runInteractiveMode(runtime, context, options = {}) {
100
99
  ...(options.spinnerIntervalMs !== undefined ? { intervalMs: options.spinnerIntervalMs } : {}),
101
100
  });
102
101
  loader.onFrame(() => tools.tick());
103
- const hint = new HintLine();
104
- let sandboxStrict;
105
- const status = new StatusBar({
102
+ const area = new StatusArea({
103
+ runtime,
104
+ theme,
106
105
  session: () => session,
107
- preset: () => runtime.config.tools?.preset ?? "default",
108
- hostStatus: () => runtime.host?.status(),
109
- codemode: () => {
110
- const mode = effectiveCodemodeMode(runtime.config);
111
- const active = session.getTools().some((tool) => tool.name === "codemode");
112
- return mode === "off" || !active ? undefined : mode;
113
- },
114
- sandboxStrict: () => (sandboxStrict ??= detectSandboxCapability().strict),
115
- }, theme);
106
+ now,
107
+ render: () => tui.requestRender(),
108
+ redraw: () => tui.forceFullRedraw(),
109
+ env,
110
+ ...(options.statusLine !== undefined ? { layout: options.statusLine } : {}),
111
+ });
112
+ const { hint, bar: status } = area;
116
113
  const historyFile = options.historyFile === false
117
114
  ? undefined
118
115
  : (options.historyFile ?? join(runtime.paths.dataDir, "history"));
@@ -139,6 +136,7 @@ export function runInteractiveMode(runtime, context, options = {}) {
139
136
  tui.addChild(loaderSlot);
140
137
  tui.addChild(editor);
141
138
  tui.addChild(hint);
139
+ tui.addChild(area.rate);
142
140
  tui.addChild(status);
143
141
  // ---- 小工具 ---------------------------------------------------------------
144
142
  let finished = false;
@@ -149,7 +147,7 @@ export function runInteractiveMode(runtime, context, options = {}) {
149
147
  view.addNotice(level, text);
150
148
  render();
151
149
  };
152
- const showHint = (text) => {
150
+ const showHint = (text, ms = HINT_MS) => {
153
151
  hint.setText(text === "" ? "" : theme.fg("dim", text));
154
152
  if (hintTimer !== undefined)
155
153
  clearTimeout(hintTimer);
@@ -158,7 +156,7 @@ export function runInteractiveMode(runtime, context, options = {}) {
158
156
  hintTimer = setTimeout(() => {
159
157
  hint.setText("");
160
158
  render();
161
- }, HINT_MS);
159
+ }, ms);
162
160
  hintTimer.unref?.();
163
161
  }
164
162
  render();
@@ -170,88 +168,18 @@ export function runInteractiveMode(runtime, context, options = {}) {
170
168
  render();
171
169
  };
172
170
  // ---- 会话事件 -------------------------------------------------------------
173
- const onEvent = (event) => {
174
- switch (event.type) {
175
- case "agent_settled":
176
- if (event.warning !== undefined)
177
- view.addNotice("warn", event.warning);
178
- status.refresh();
179
- break;
180
- case "message_start": {
181
- const message = event.message;
182
- if (message.role === "user")
183
- view.addUser(message);
184
- else if (message.role === "assistant")
185
- view.startAssistant(message);
186
- else if (message.role === "custom" && message.display) {
187
- view.addNotice("info", typeof message.content === "string" ? message.content : "");
188
- }
189
- break;
190
- }
191
- case "message_update":
192
- view.updateAssistant(event.message);
193
- break;
194
- case "message_end":
195
- if (event.message.role === "assistant") {
196
- view.endAssistant(event.message);
197
- status.refresh();
198
- }
199
- break;
200
- case "tool_execution_start": {
201
- const started = tools.start(event);
202
- if (started.topLevel)
203
- view.addTool(started.view);
204
- break;
205
- }
206
- case "tool_execution_update":
207
- tools.update(event.toolCallId, event.partial);
208
- render();
209
- return;
210
- case "tool_execution_end":
211
- tools.end(event.toolCallId, event.result, event.isError);
212
- break;
213
- case "queue_update":
214
- setQueue(event.steering, event.followUp);
215
- return;
216
- case "compaction_end":
217
- if (event.result !== undefined)
218
- view.addCompaction(event.result);
219
- else if (event.error !== undefined)
220
- view.addNotice("error", `压缩失败:${event.error}`);
221
- else if (event.aborted)
222
- view.addNotice("info", "压缩已取消");
223
- status.refresh();
224
- break;
225
- case "auto_retry_start":
226
- view.addRetry(event.attempt, event.maxAttempts, event.delayMs, event.errorMessage);
227
- break;
228
- case "auto_retry_end":
229
- if (!event.success)
230
- view.addRetryFailed(event.finalError);
231
- break;
232
- case "cache_miss":
233
- case "context_pressure": {
234
- const shown = cacheEventNotice(event, cacheNoticesEnabled(session));
235
- if (shown !== undefined)
236
- view.addNotice(shown.level, shown.text);
237
- status.refresh();
238
- render();
239
- return;
240
- }
241
- case "cache_warm":
242
- case "permission_mode_changed":
243
- case "model_changed":
244
- case "thinking_level_changed":
245
- case "session_changed":
246
- status.refresh();
247
- render();
248
- return;
249
- default:
250
- break;
251
- }
252
- indicator.onEvent(event);
253
- render();
254
- };
171
+ const onEvent = createSessionEventHandler({
172
+ view,
173
+ tools,
174
+ status,
175
+ area,
176
+ agentUi: () => agentUi,
177
+ indicator,
178
+ session: () => session,
179
+ setQueue,
180
+ notice,
181
+ render,
182
+ });
255
183
  let unsubscribe = session.subscribe(onEvent);
256
184
  const startupLevel = startupScreenLevel(runtime);
257
185
  const home = env["HOME"] ?? env["USERPROFILE"];
@@ -282,7 +210,8 @@ export function runInteractiveMode(runtime, context, options = {}) {
282
210
  tools.clear();
283
211
  replay();
284
212
  setQueue([], []);
285
- status.refresh();
213
+ area.rebind();
214
+ agentUi.attach(next);
286
215
  indicator.reset();
287
216
  if (next instanceof AgentSessionImpl)
288
217
  next.announceStart(reason);
@@ -292,12 +221,20 @@ export function runInteractiveMode(runtime, context, options = {}) {
292
221
  * 空闲时发提示。agent_settled 之后会话周期还要一个微任务才结束,所以先 waitForIdle;
293
222
  * 仍然撞上运行中(极少)就排到本轮之后。
294
223
  */
224
+ let draftImages = [];
225
+ const resize = runtime.config.images?.resize;
226
+ const imageResize = resize !== undefined ? { resize } : {};
295
227
  const startPrompt = (text) => {
296
228
  const target = session;
229
+ const carried = draftImages;
230
+ draftImages = [];
297
231
  void target
298
232
  .waitForIdle()
299
233
  .then(async () => {
300
- const images = await promptImages(text, [], target.state.cwd, sessionModel(runtime.providers, target));
234
+ const images = [
235
+ ...carried,
236
+ ...(await promptImages(text, [], target.state.cwd, sessionModel(runtime.providers, target), imageResize)),
237
+ ];
301
238
  return target.prompt(text, images.length > 0 ? { images } : {});
302
239
  })
303
240
  .catch((error) => {
@@ -312,6 +249,38 @@ export function runInteractiveMode(runtime, context, options = {}) {
312
249
  render();
313
250
  });
314
251
  };
252
+ /** 对话换了路径(/tree、回滚):重画消息区。内容可能缩到视口顶以上,差分画不对,整屏重画。 */
253
+ const reloadView = () => {
254
+ resetView();
255
+ tools.clear();
256
+ replay();
257
+ status.refresh();
258
+ area.refreshGit();
259
+ tui.forceFullRedraw();
260
+ };
261
+ const setDraft = (draft) => {
262
+ editor.setText(draft.text);
263
+ draftImages = [...(draft.images ?? [])];
264
+ render();
265
+ };
266
+ const pickerHost = {
267
+ theme,
268
+ keybindings: keys,
269
+ showOverlay: (c, o) => tui.showOverlay(c, o),
270
+ columns: () => terminal.columns,
271
+ rows: () => terminal.rows,
272
+ };
273
+ const rewind = createRewindFlow({
274
+ ...pickerHost,
275
+ session: () => session,
276
+ now,
277
+ render,
278
+ notice,
279
+ hint: (text) => showHint(text),
280
+ reload: reloadView,
281
+ setDraft,
282
+ editorEmpty: () => editor.isEmpty(),
283
+ });
315
284
  const commandUi = {
316
285
  runtime,
317
286
  session: () => session,
@@ -320,12 +289,9 @@ export function runInteractiveMode(runtime, context, options = {}) {
320
289
  rebind(next, request.kind === "new" ? "new" : request.kind === "fork" ? "fork" : "resume");
321
290
  return next;
322
291
  },
323
- pick: (spec) => openPicker({
324
- theme,
325
- keybindings: keys,
326
- showOverlay: (c, o) => tui.showOverlay(c, o),
327
- columns: () => terminal.columns,
328
- }, spec),
292
+ pick: (spec) => openPicker(pickerHost, spec),
293
+ rewind: () => rewind.open(),
294
+ setDraft,
329
295
  notice,
330
296
  panel: (component) => {
331
297
  view.add(component);
@@ -339,17 +305,14 @@ export function runInteractiveMode(runtime, context, options = {}) {
339
305
  render();
340
306
  },
341
307
  prompt: (text) => startPrompt(text),
342
- reload: () => {
343
- resetView();
344
- tools.clear();
345
- replay();
346
- status.refresh();
347
- render();
348
- },
308
+ reload: reloadView,
349
309
  exit: (code) => exit(code),
350
310
  now,
311
+ extra: (name, args) => agentUi.command(name, args),
351
312
  };
352
- const runCommand = (line) => runInteractiveCommand(line, commandUi).finally(() => {
313
+ const runCommand = (line) => (statusLineSlash(area, line, notice)
314
+ ? Promise.resolve(true)
315
+ : runInteractiveCommand(line, commandUi)).finally(() => {
353
316
  status.refresh();
354
317
  render();
355
318
  });
@@ -385,16 +348,15 @@ export function runInteractiveMode(runtime, context, options = {}) {
385
348
  now,
386
349
  showHint,
387
350
  onExpandToggle: (expanded) => view.setThinkingExpanded(expanded),
351
+ onStatusLineToggle: () => area.toggle(),
352
+ onPasteImage: () => void agentUi.paste(),
388
353
  submit: (text, via) => submit(text, via),
389
354
  runCommand: (line) => void runCommand(line),
390
355
  exit: (code) => exit(code),
356
+ onInterrupted: (empty) => void rewind.afterInterrupt(empty),
391
357
  }));
392
358
  // ---- 晚绑定 ---------------------------------------------------------------
393
- const broker = new ApprovalDialogBroker({
394
- theme,
395
- keybindings: keys,
396
- cwd: session.state.cwd,
397
- permissionMode: () => session.state.permissionMode,
359
+ const overlayHooks = {
398
360
  showOverlay: (component) => tui.showOverlay(component, { anchor: "bottom" }),
399
361
  onOpen: () => {
400
362
  editor.disableSubmit = true;
@@ -406,12 +368,44 @@ export function runInteractiveMode(runtime, context, options = {}) {
406
368
  tools.setAwaiting(false);
407
369
  indicator.setApproval(false);
408
370
  },
371
+ };
372
+ const agentUi = new AgentUi({
373
+ theme,
374
+ tui,
375
+ editor,
376
+ tools: () => tools,
377
+ subagents,
378
+ session: () => session,
379
+ dataDir: runtime.paths.dataDir,
380
+ env,
381
+ now,
382
+ notice,
383
+ panel: (component) => commandUi.panel?.(component),
384
+ pick: (spec) => openPicker(pickerHost, spec),
385
+ hint: (text) => showHint(text),
386
+ render,
387
+ switchSession: (request) => commandUi.switchSession(request),
388
+ prompt: (text) => startPrompt(text),
389
+ dialog: { theme, keybindings: keys, ...overlayHooks },
390
+ displayPath: (path) => tildePath(path, home),
391
+ ...(options.clipboard !== undefined ? { clipboard: options.clipboard } : {}),
392
+ });
393
+ const taskAgent = (taskId) => taskRegistryView(session.state.sessionId)?.get(taskId)?.agent;
394
+ const broker = mergingBroker(new ApprovalDialogBroker({
395
+ theme,
396
+ keybindings: keys,
397
+ cwd: session.state.cwd,
398
+ permissionMode: () => session.state.permissionMode,
399
+ taskAgent,
400
+ externalRunner: (agent) => agentUi.merge.externalRunner(agent),
401
+ ...overlayHooks,
409
402
  report: (request, outcome) => {
410
403
  if (outcome === "deny" || outcome === "cancelled") {
411
- notice(outcome === "deny" ? "info" : "warn", approvalOutcomeText(request, outcome));
404
+ const text = approvalOutcomeText(request, outcome, taskAgent);
405
+ notice(outcome === "deny" ? "info" : "warn", text);
412
406
  }
413
407
  },
414
- });
408
+ }), agentUi.merge, (request) => notice("info", `${approvalOutcomeText(request, "allow", taskAgent)}(随上一次确认)`));
415
409
  // ---- 启动与退出 -----------------------------------------------------------
416
410
  let offSignals = () => undefined;
417
411
  function exit(code) {
@@ -421,11 +415,13 @@ export function runInteractiveMode(runtime, context, options = {}) {
421
415
  if (hintTimer !== undefined)
422
416
  clearTimeout(hintTimer);
423
417
  loader.stop();
418
+ area.dispose();
424
419
  // 退出摘要留在回滚里;输入框、状态栏等底部区域撤掉,屏幕停在摘要下面
425
420
  view.addExitSummary(exitSummaryLines(session.getStats(), now() - startedAt, theme));
426
421
  tui.clear();
427
422
  tui.addChild(view);
428
423
  offSignals();
424
+ agentUi.detach();
429
425
  runtime.approvals.setUiBroker(undefined);
430
426
  runtime.notifier.set(undefined);
431
427
  tui.stop();
@@ -456,6 +452,7 @@ export function runInteractiveMode(runtime, context, options = {}) {
456
452
  }
457
453
  runtime.approvals.setUiBroker(broker);
458
454
  runtime.notifier.set((message, level) => notice(level, message));
455
+ agentUi.attach(session);
459
456
  offSignals = onTerminationSignals((code) => exit(code));
460
457
  tui.setFocus(editor);
461
458
  options.onReady?.({
@@ -464,6 +461,7 @@ export function runInteractiveMode(runtime, context, options = {}) {
464
461
  view,
465
462
  tools,
466
463
  status,
464
+ area,
467
465
  session: () => session,
468
466
  exit,
469
467
  });
@@ -3,7 +3,12 @@
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 type { AgentSession } from "../../agent/types.js";
9
14
  import type { Editor, Keybindings } from "../../tui.js";
@@ -20,12 +25,21 @@ export interface KeyDispatchDeps {
20
25
  /** 运行或压缩中(Esc 才中断)。 */
21
26
  busy(): boolean;
22
27
  now(): number;
23
- showHint(text: string): void;
28
+ /** 底部提示;`ms` 是停留时间(缺省由调用方决定)。 */
29
+ showHint(text: string, ms?: number): void;
24
30
  /** Ctrl+O 之后(消息区思考块跟着展开 / 折叠)。 */
25
31
  onExpandToggle?(expanded: boolean): void;
32
+ /** Ctrl+G:切换底部信息行,返回切换后的布局。 */
33
+ onStatusLineToggle?(): "full" | "compact";
34
+ /** Ctrl+V:粘贴剪贴板图片(W5-U)。 */
35
+ onPasteImage?(): void;
26
36
  submit(text: string, via: "followUp"): void;
27
37
  runCommand(line: string): void;
28
38
  exit(code: number): void;
39
+ /** Esc 中断之后(参数:中断后输入框是否为空)。 */
40
+ onInterrupted?(editorWasEmpty: boolean): void;
41
+ /** 双击 Esc 的间隔(测试注入)。 */
42
+ doubleEscMs?: number;
29
43
  }
30
44
  /** 返回输入监听器:已处理返回 true,交给编辑器返回 false。 */
31
45
  export declare function createKeyDispatch(deps: KeyDispatchDeps): (data: string) => boolean;
@@ -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();
@@ -48,6 +79,8 @@ export function createKeyDispatch(deps) {
48
79
  if (deps.inactive())
49
80
  return false;
50
81
  const is = (action) => keys.matches(data, action);
82
+ if (!is("app.rewind"))
83
+ esc.reset();
51
84
  if (is("app.clear")) {
52
85
  if (!editor.isEmpty()) {
53
86
  editor.clear();
@@ -65,8 +98,14 @@ export function createKeyDispatch(deps) {
65
98
  }
66
99
  ctrlCArmedAt = Number.NEGATIVE_INFINITY;
67
100
  if (is("app.interrupt") && !editor.isCompletionOpen && deps.busy()) {
101
+ esc.reset();
68
102
  interrupt();
69
103
  deps.showHint("已中断");
104
+ deps.onInterrupted?.(editor.isEmpty());
105
+ return true;
106
+ }
107
+ if (is("app.rewind") && !editor.isCompletionOpen && !deps.busy()) {
108
+ doubleEscape();
70
109
  return true;
71
110
  }
72
111
  if (is("app.exit") && editor.isEmpty()) {
@@ -101,6 +140,15 @@ export function createKeyDispatch(deps) {
101
140
  deps.runCommand("/thinking");
102
141
  return true;
103
142
  }
143
+ if (is("app.paste.image") && deps.onPasteImage !== undefined) {
144
+ deps.onPasteImage();
145
+ return true;
146
+ }
147
+ if (is("app.statusLine.toggle") && deps.onStatusLineToggle !== undefined) {
148
+ const mode = deps.onStatusLineToggle();
149
+ deps.showHint(statusLineText(mode));
150
+ return true;
151
+ }
104
152
  return false;
105
153
  };
106
154
  }
@@ -6,7 +6,8 @@
6
6
  * 问句之前逐行打印执行前预览(`request.preview`,W3-B9a-2)。
7
7
  * - 否则(管道):逐行读 stdin,每行依次执行(等上一条运行结束);没有审批 UI,ask → deny。
8
8
  * 模型错误在运行结束时只打印一次(重试中只显示 ↻);有运行最终失败时退出码 1。
9
- * - 斜杠命令走 commands-core(与 B7 同一语义),`pick` 退化为列出候选。
9
+ * - 斜杠命令走 commands-core(与 B7 同一语义),`pick` 退化为列出候选;`/rewind` 列编号、
10
+ * `/rewind <n> …` 执行,回到的单行原消息放回编辑行。
10
11
  */
11
12
  import type { ModeContext } from "../../../cli/deps.js";
12
13
  import type { Runtime } from "../../../cli/runtime.js";
@@ -6,9 +6,11 @@
6
6
  * 问句之前逐行打印执行前预览(`request.preview`,W3-B9a-2)。
7
7
  * - 否则(管道):逐行读 stdin,每行依次执行(等上一条运行结束);没有审批 UI,ask → deny。
8
8
  * 模型错误在运行结束时只打印一次(重试中只显示 ↻);有运行最终失败时退出码 1。
9
- * - 斜杠命令走 commands-core(与 B7 同一语义),`pick` 退化为列出候选。
9
+ * - 斜杠命令走 commands-core(与 B7 同一语义),`pick` 退化为列出候选;`/rewind` 列编号、
10
+ * `/rewind <n> …` 执行,回到的单行原消息放回编辑行。
10
11
  */
11
12
  import { promptImages, sessionModel } from "../../image-input.js";
13
+ import { taskRegistryView } from "../../../agent/subagent-registry.js";
12
14
  import { currentSession, switchSession } from "../../../cli/compose-session.js";
13
15
  import { ExitCode } from "../../../cli/exit-codes.js";
14
16
  import { previewDisplayLines } from "../../../permissions/preview.js";
@@ -64,8 +66,21 @@ export async function runLineMode(runtime, context, options = {}) {
64
66
  await prompt(session, result.text);
65
67
  else if (result.kind === "pick")
66
68
  out(`${await pickHint(result.what, runtime, session)}\n`);
67
- else if (result.message !== undefined)
68
- out(`${result.message}\n`);
69
+ else {
70
+ if (result.message !== undefined)
71
+ out(`${result.message}\n`);
72
+ // /rewind 回到某条消息之前:单行原消息放回编辑行(多行只在输出里给出)
73
+ const draft = result.draft?.text;
74
+ if (editor !== undefined && draft !== undefined && !draft.includes("\n")) {
75
+ editor.buffer = draft;
76
+ editor.cursor = draft.length;
77
+ }
78
+ // /plan approve 开了新回合:跑完再收下一行(与直接发提示一致)
79
+ if (result.wait === true) {
80
+ await session.waitForIdle();
81
+ printer.endLine();
82
+ }
83
+ }
69
84
  }
70
85
  catch (error) {
71
86
  printer.endLine();
@@ -75,7 +90,8 @@ export async function runLineMode(runtime, context, options = {}) {
75
90
  return undefined;
76
91
  };
77
92
  const prompt = async (target, text) => {
78
- const images = await promptImages(text, [], target.state.cwd, sessionModel(runtime.providers, target));
93
+ const resize = runtime.config.images?.resize;
94
+ const images = await promptImages(text, [], target.state.cwd, sessionModel(runtime.providers, target), resize !== undefined ? { resize } : {});
79
95
  await target.prompt(text, images.length > 0 ? { images } : {});
80
96
  printer.endLine();
81
97
  };
@@ -95,7 +111,8 @@ export async function runLineMode(runtime, context, options = {}) {
95
111
  return resolve(undefined);
96
112
  signal.addEventListener("abort", () => resolve(undefined), { once: true });
97
113
  const preview = previewDisplayLines(request.preview).map((l) => ` ${l}\n`);
98
- void ed.ask(`\n${preview.join("")}${approvalQuestion(request)}`, "n").then((answer) => {
114
+ const question = approvalQuestion(request, (taskId) => taskRegistryView(session.state.sessionId)?.get(taskId)?.agent);
115
+ void ed.ask(`\n${preview.join("")}${question}`, "n").then((answer) => {
99
116
  resolve(answer === "y" ? "allow" : answer === "a" ? "allow_session" : "deny");
100
117
  if (!busy)
101
118
  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。 */