@armadra/agent 0.4.0 → 0.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (432) hide show
  1. package/CHANGELOG.md +184 -1
  2. package/README.md +202 -83
  3. package/THIRD_PARTY_NOTICES.md +34 -0
  4. package/dist/acp.d.ts +18 -0
  5. package/dist/acp.js +19 -0
  6. package/dist/agent/limits.d.ts +38 -0
  7. package/dist/agent/limits.js +127 -0
  8. package/dist/agent/loop-guard.d.ts +38 -0
  9. package/dist/agent/loop-guard.js +95 -0
  10. package/dist/agent/loop.d.ts +4 -0
  11. package/dist/agent/loop.js +7 -0
  12. package/dist/agent/prompt-rules.d.ts +12 -0
  13. package/dist/agent/prompt-rules.js +18 -0
  14. package/dist/agent/reminders.d.ts +59 -0
  15. package/dist/agent/reminders.js +327 -0
  16. package/dist/agent/session-cache.d.ts +5 -0
  17. package/dist/agent/session-cache.js +17 -1
  18. package/dist/agent/session-classifier.d.ts +1 -1
  19. package/dist/agent/session-classifier.js +3 -1
  20. package/dist/agent/session-compaction.d.ts +35 -6
  21. package/dist/agent/session-compaction.js +157 -31
  22. package/dist/agent/session-core.d.ts +27 -1
  23. package/dist/agent/session-extensions.d.ts +61 -0
  24. package/dist/agent/session-extensions.js +129 -0
  25. package/dist/agent/session-images.d.ts +23 -0
  26. package/dist/agent/session-images.js +97 -0
  27. package/dist/agent/session-plan.d.ts +21 -0
  28. package/dist/agent/session-plan.js +524 -0
  29. package/dist/agent/session-rewind.d.ts +81 -0
  30. package/dist/agent/session-rewind.js +354 -0
  31. package/dist/agent/session-run.d.ts +28 -1
  32. package/dist/agent/session-run.js +157 -6
  33. package/dist/agent/session-settings.d.ts +49 -0
  34. package/dist/agent/session-settings.js +95 -0
  35. package/dist/agent/session-subagent.d.ts +36 -24
  36. package/dist/agent/session-subagent.js +259 -129
  37. package/dist/agent/session-sync.d.ts +10 -3
  38. package/dist/agent/session-sync.js +37 -8
  39. package/dist/agent/session-telemetry.d.ts +39 -0
  40. package/dist/agent/session-telemetry.js +231 -0
  41. package/dist/agent/session-tools.js +40 -1
  42. package/dist/agent/session.d.ts +30 -29
  43. package/dist/agent/session.js +79 -123
  44. package/dist/agent/subagent-registry.d.ts +91 -0
  45. package/dist/agent/subagent-registry.js +494 -0
  46. package/dist/agent/system-prompt.d.ts +6 -3
  47. package/dist/agent/system-prompt.js +7 -2
  48. package/dist/agent/tool-runner.d.ts +8 -1
  49. package/dist/agent/tool-runner.js +57 -4
  50. package/dist/agent/types-w5.d.ts +192 -0
  51. package/dist/agent/types-w5.js +10 -0
  52. package/dist/agent/types.d.ts +47 -4
  53. package/dist/agent/types.js +2 -0
  54. package/dist/agent/worktree.d.ts +36 -0
  55. package/dist/agent/worktree.js +86 -0
  56. package/dist/agents/builtin.d.ts +14 -0
  57. package/dist/agents/builtin.js +39 -0
  58. package/dist/agents/catalog.d.ts +43 -0
  59. package/dist/agents/catalog.js +82 -0
  60. package/dist/agents/discover.d.ts +35 -0
  61. package/dist/agents/discover.js +83 -0
  62. package/dist/agents/external.d.ts +82 -0
  63. package/dist/agents/external.js +326 -0
  64. package/dist/agents/parse.d.ts +18 -0
  65. package/dist/agents/parse.js +116 -0
  66. package/dist/agents/result.d.ts +32 -0
  67. package/dist/agents/result.js +67 -0
  68. package/dist/agents/task-control.d.ts +17 -0
  69. package/dist/agents/task-control.js +17 -0
  70. package/dist/agents/task-record.d.ts +78 -0
  71. package/dist/agents/task-record.js +141 -0
  72. package/dist/agents/types.d.ts +46 -0
  73. package/dist/agents/types.js +8 -0
  74. package/dist/ai/apis/anthropic-compat.d.ts +36 -0
  75. package/dist/ai/apis/anthropic-compat.js +92 -0
  76. package/dist/ai/apis/anthropic-request.d.ts +7 -6
  77. package/dist/ai/apis/anthropic-request.js +10 -28
  78. package/dist/ai/apis/cache-params.d.ts +11 -2
  79. package/dist/ai/apis/cache-params.js +25 -6
  80. package/dist/ai/apis/openai-compat.d.ts +4 -1
  81. package/dist/ai/apis/openai-compat.js +8 -1
  82. package/dist/ai/image-limits.d.ts +45 -0
  83. package/dist/ai/image-limits.js +68 -0
  84. package/dist/ai/providers/builtin.d.ts +18 -6
  85. package/dist/ai/providers/builtin.js +135 -6
  86. package/dist/ai/providers/catalog-data.js +17 -13
  87. package/dist/ai/providers/catalog.d.ts +60 -7
  88. package/dist/ai/providers/catalog.js +197 -38
  89. package/dist/ai/providers/channels.d.ts +33 -3
  90. package/dist/ai/providers/channels.js +108 -7
  91. package/dist/ai/providers/enrich.d.ts +6 -3
  92. package/dist/ai/providers/enrich.js +19 -4
  93. package/dist/ai/providers/models-dev-cache.d.ts +28 -24
  94. package/dist/ai/providers/models-dev-cache.js +92 -78
  95. package/dist/ai/providers/models-dev-data.d.ts +7 -0
  96. package/dist/ai/providers/models-dev-data.js +32 -0
  97. package/dist/ai/providers/models-dev-snapshot.d.ts +50 -0
  98. package/dist/ai/providers/models-dev-snapshot.js +137 -0
  99. package/dist/ai/providers/models-dev.d.ts +51 -10
  100. package/dist/ai/providers/models-dev.js +132 -39
  101. package/dist/ai/providers/registry.d.ts +20 -10
  102. package/dist/ai/providers/registry.js +82 -43
  103. package/dist/ai/types.d.ts +28 -2
  104. package/dist/ai/types.js +5 -0
  105. package/dist/bundle/ama.cjs +37605 -21619
  106. package/dist/checkpoints/backend.d.ts +61 -0
  107. package/dist/checkpoints/backend.js +135 -0
  108. package/dist/checkpoints/blobs.d.ts +60 -0
  109. package/dist/checkpoints/blobs.js +203 -0
  110. package/dist/checkpoints/gc.d.ts +47 -0
  111. package/dist/checkpoints/gc.js +144 -0
  112. package/dist/checkpoints/git-head.d.ts +15 -0
  113. package/dist/checkpoints/git-head.js +89 -0
  114. package/dist/checkpoints/index.d.ts +17 -0
  115. package/dist/checkpoints/index.js +17 -0
  116. package/dist/checkpoints/replay.d.ts +48 -0
  117. package/dist/checkpoints/replay.js +154 -0
  118. package/dist/checkpoints/restore.d.ts +86 -0
  119. package/dist/checkpoints/restore.js +263 -0
  120. package/dist/checkpoints/settings.d.ts +14 -0
  121. package/dist/checkpoints/settings.js +23 -0
  122. package/dist/checkpoints/shadow-git.d.ts +108 -0
  123. package/dist/checkpoints/shadow-git.js +491 -0
  124. package/dist/checkpoints/shadow-restore.d.ts +25 -0
  125. package/dist/checkpoints/shadow-restore.js +128 -0
  126. package/dist/checkpoints/tracker.d.ts +64 -0
  127. package/dist/checkpoints/tracker.js +192 -0
  128. package/dist/checkpoints/types.d.ts +99 -0
  129. package/dist/checkpoints/types.js +12 -0
  130. package/dist/cli/args.d.ts +8 -4
  131. package/dist/cli/args.js +17 -97
  132. package/dist/cli/bootstrap.js +13 -1
  133. package/dist/cli/choice-prompt.d.ts +58 -0
  134. package/dist/cli/choice-prompt.js +140 -0
  135. package/dist/cli/codemode-notice.d.ts +3 -2
  136. package/dist/cli/codemode-notice.js +3 -2
  137. package/dist/cli/compose-agents.d.ts +27 -0
  138. package/dist/cli/compose-agents.js +111 -0
  139. package/dist/cli/compose-extensions.d.ts +29 -0
  140. package/dist/cli/compose-extensions.js +44 -0
  141. package/dist/cli/compose-session.d.ts +3 -0
  142. package/dist/cli/compose-session.js +47 -4
  143. package/dist/cli/compose.d.ts +3 -0
  144. package/dist/cli/compose.js +33 -6
  145. package/dist/cli/deps.d.ts +6 -0
  146. package/dist/cli/exit-codes.d.ts +10 -0
  147. package/dist/cli/exit-codes.js +12 -0
  148. package/dist/cli/help-text.d.ts +4 -0
  149. package/dist/cli/help-text.js +102 -0
  150. package/dist/cli/startup-screen.js +5 -3
  151. package/dist/cli/startup-steps.js +7 -1
  152. package/dist/cli/subcommands/config.d.ts +4 -1
  153. package/dist/cli/subcommands/config.js +17 -6
  154. package/dist/cli/subcommands/doctor.js +21 -0
  155. package/dist/cli/subcommands/models-cache-probe.d.ts +1 -1
  156. package/dist/cli/subcommands/models-cache-probe.js +3 -12
  157. package/dist/cli/subcommands/models-discover.js +5 -6
  158. package/dist/cli/subcommands/models.d.ts +2 -0
  159. package/dist/cli/subcommands/models.js +33 -14
  160. package/dist/cli/subcommands/providers.d.ts +1 -1
  161. package/dist/cli/subcommands/providers.js +8 -15
  162. package/dist/cli/subcommands/sessions.d.ts +3 -1
  163. package/dist/cli/subcommands/sessions.js +36 -2
  164. package/dist/codemode/capability.d.ts +24 -7
  165. package/dist/codemode/capability.js +38 -10
  166. package/dist/codemode/host-side.d.ts +14 -0
  167. package/dist/codemode/host-side.js +24 -1
  168. package/dist/codemode/tool.d.ts +1 -1
  169. package/dist/codemode/tool.js +6 -4
  170. package/dist/compaction/breaker.d.ts +25 -9
  171. package/dist/compaction/breaker.js +47 -17
  172. package/dist/compaction/estimate.d.ts +11 -3
  173. package/dist/compaction/estimate.js +64 -22
  174. package/dist/compaction/image-budget.d.ts +45 -0
  175. package/dist/compaction/image-budget.js +115 -0
  176. package/dist/compaction/post-compact.d.ts +38 -0
  177. package/dist/compaction/post-compact.js +152 -0
  178. package/dist/compaction/protect.d.ts +27 -0
  179. package/dist/compaction/protect.js +56 -0
  180. package/dist/compaction/prune-tier.d.ts +58 -12
  181. package/dist/compaction/prune-tier.js +120 -54
  182. package/dist/compaction/summarize-tier.d.ts +23 -4
  183. package/dist/compaction/summarize-tier.js +100 -29
  184. package/dist/config/checker.d.ts +28 -0
  185. package/dist/config/checker.js +97 -0
  186. package/dist/config/json-schema.js +63 -2
  187. package/dist/config/key-docs.js +72 -3
  188. package/dist/config/merge.d.ts +5 -3
  189. package/dist/config/merge.js +81 -7
  190. package/dist/config/profile.d.ts +2 -0
  191. package/dist/config/profile.js +2 -0
  192. package/dist/config/schema-w5.d.ts +18 -0
  193. package/dist/config/schema-w5.js +109 -0
  194. package/dist/config/schema.d.ts +5 -9
  195. package/dist/config/schema.js +68 -114
  196. package/dist/config/types-w5.d.ts +118 -0
  197. package/dist/config/types-w5.js +20 -0
  198. package/dist/config/types.d.ts +50 -1
  199. package/dist/config/types.js +12 -0
  200. package/dist/drivers/acp/client.d.ts +57 -0
  201. package/dist/drivers/acp/client.js +160 -0
  202. package/dist/drivers/acp/driver.d.ts +31 -0
  203. package/dist/drivers/acp/driver.js +274 -0
  204. package/dist/drivers/acp/testing/fake-agent-main.d.ts +5 -0
  205. package/dist/drivers/acp/testing/fake-agent-main.js +7 -0
  206. package/dist/drivers/acp/testing/fake-agent.d.ts +27 -0
  207. package/dist/drivers/acp/testing/fake-agent.js +211 -0
  208. package/dist/drivers/acp/types.d.ts +285 -0
  209. package/dist/drivers/acp/types.js +36 -0
  210. package/dist/drivers/agents.d.ts +58 -0
  211. package/dist/drivers/agents.js +142 -0
  212. package/dist/drivers/base.d.ts +35 -0
  213. package/dist/drivers/base.js +59 -0
  214. package/dist/drivers/catalog.d.ts +46 -0
  215. package/dist/drivers/catalog.js +167 -0
  216. package/dist/drivers/env.d.ts +20 -0
  217. package/dist/drivers/env.js +46 -0
  218. package/dist/drivers/host-runners.d.ts +18 -0
  219. package/dist/drivers/host-runners.js +45 -0
  220. package/dist/drivers/jsonrpc.d.ts +58 -0
  221. package/dist/drivers/jsonrpc.js +180 -0
  222. package/dist/drivers/native/claude-normalize.d.ts +46 -0
  223. package/dist/drivers/native/claude-normalize.js +106 -0
  224. package/dist/drivers/native/claude-stream.d.ts +29 -0
  225. package/dist/drivers/native/claude-stream.js +456 -0
  226. package/dist/drivers/native/codex-app-server.d.ts +26 -0
  227. package/dist/drivers/native/codex-app-server.js +407 -0
  228. package/dist/drivers/native/codex-normalize.d.ts +38 -0
  229. package/dist/drivers/native/codex-normalize.js +124 -0
  230. package/dist/drivers/native/oneshot.d.ts +28 -0
  231. package/dist/drivers/native/oneshot.js +312 -0
  232. package/dist/drivers/permissions.d.ts +40 -0
  233. package/dist/drivers/permissions.js +92 -0
  234. package/dist/drivers/pids.d.ts +32 -0
  235. package/dist/drivers/pids.js +93 -0
  236. package/dist/drivers/pool.d.ts +28 -0
  237. package/dist/drivers/pool.js +96 -0
  238. package/dist/drivers/probe.d.ts +47 -0
  239. package/dist/drivers/probe.js +172 -0
  240. package/dist/drivers/process.d.ts +48 -0
  241. package/dist/drivers/process.js +100 -0
  242. package/dist/drivers/runner.d.ts +61 -0
  243. package/dist/drivers/runner.js +388 -0
  244. package/dist/drivers/store.d.ts +49 -0
  245. package/dist/drivers/store.js +91 -0
  246. package/dist/drivers/turn.d.ts +41 -0
  247. package/dist/drivers/turn.js +99 -0
  248. package/dist/drivers/types.d.ts +122 -0
  249. package/dist/drivers/types.js +11 -0
  250. package/dist/git/info.d.ts +75 -0
  251. package/dist/git/info.js +191 -0
  252. package/dist/hooks/protocol.js +6 -1
  253. package/dist/hooks/types.d.ts +10 -1
  254. package/dist/hooks/types.js +2 -0
  255. package/dist/host/api-impl.d.ts +7 -0
  256. package/dist/host/api-impl.js +15 -0
  257. package/dist/host/types.d.ts +24 -2
  258. package/dist/host/types.js +4 -0
  259. package/dist/index.d.ts +3 -0
  260. package/dist/index.js +1 -0
  261. package/dist/modes/acp/acp-events.d.ts +46 -0
  262. package/dist/modes/acp/acp-events.js +233 -0
  263. package/dist/modes/acp/acp-mode.d.ts +17 -0
  264. package/dist/modes/acp/acp-mode.js +47 -0
  265. package/dist/modes/acp/acp-server.d.ts +79 -0
  266. package/dist/modes/acp/acp-server.js +315 -0
  267. package/dist/modes/commands-core.d.ts +13 -1
  268. package/dist/modes/commands-core.js +64 -0
  269. package/dist/modes/image-input.d.ts +12 -4
  270. package/dist/modes/image-input.js +22 -5
  271. package/dist/modes/interactive/agent-panels.d.ts +14 -0
  272. package/dist/modes/interactive/agent-panels.js +103 -0
  273. package/dist/modes/interactive/agent-ui.d.ts +73 -0
  274. package/dist/modes/interactive/agent-ui.js +234 -0
  275. package/dist/modes/interactive/approval-dialog.d.ts +21 -4
  276. package/dist/modes/interactive/approval-dialog.js +98 -20
  277. package/dist/modes/interactive/approval-merge.d.ts +41 -0
  278. package/dist/modes/interactive/approval-merge.js +89 -0
  279. package/dist/modes/interactive/clipboard-paste.d.ts +18 -0
  280. package/dist/modes/interactive/clipboard-paste.js +28 -0
  281. package/dist/modes/interactive/commands.d.ts +12 -1
  282. package/dist/modes/interactive/commands.js +31 -1
  283. package/dist/modes/interactive/confirm-dialog.d.ts +49 -0
  284. package/dist/modes/interactive/confirm-dialog.js +121 -0
  285. package/dist/modes/interactive/double-esc.d.ts +20 -0
  286. package/dist/modes/interactive/double-esc.js +34 -0
  287. package/dist/modes/interactive/event-notices.d.ts +32 -0
  288. package/dist/modes/interactive/event-notices.js +79 -0
  289. package/dist/modes/interactive/external-editor.d.ts +16 -0
  290. package/dist/modes/interactive/external-editor.js +39 -0
  291. package/dist/modes/interactive/interactive-mode.d.ts +14 -1
  292. package/dist/modes/interactive/interactive-mode.js +140 -136
  293. package/dist/modes/interactive/key-dispatch.d.ts +22 -2
  294. package/dist/modes/interactive/key-dispatch.js +66 -7
  295. package/dist/modes/interactive/line/line-mode.d.ts +4 -1
  296. package/dist/modes/interactive/line/line-mode.js +31 -5
  297. package/dist/modes/interactive/line/line-render.d.ts +2 -1
  298. package/dist/modes/interactive/line/line-render.js +40 -4
  299. package/dist/modes/interactive/message-view.d.ts +1 -1
  300. package/dist/modes/interactive/message-view.js +24 -2
  301. package/dist/modes/interactive/panels.d.ts +18 -1
  302. package/dist/modes/interactive/panels.js +19 -5
  303. package/dist/modes/interactive/plan-command.d.ts +40 -0
  304. package/dist/modes/interactive/plan-command.js +129 -0
  305. package/dist/modes/interactive/plan-dialog.d.ts +98 -0
  306. package/dist/modes/interactive/plan-dialog.js +290 -0
  307. package/dist/modes/interactive/plan-flow.d.ts +34 -0
  308. package/dist/modes/interactive/plan-flow.js +98 -0
  309. package/dist/modes/interactive/rewind-command.d.ts +31 -0
  310. package/dist/modes/interactive/rewind-command.js +106 -0
  311. package/dist/modes/interactive/rewind-flow.d.ts +37 -0
  312. package/dist/modes/interactive/rewind-flow.js +151 -0
  313. package/dist/modes/interactive/rewind-list.d.ts +36 -0
  314. package/dist/modes/interactive/rewind-list.js +123 -0
  315. package/dist/modes/interactive/rewind-panel.d.ts +77 -0
  316. package/dist/modes/interactive/rewind-panel.js +309 -0
  317. package/dist/modes/interactive/rewind-text.d.ts +40 -0
  318. package/dist/modes/interactive/rewind-text.js +139 -0
  319. package/dist/modes/interactive/session-events.d.ts +24 -0
  320. package/dist/modes/interactive/session-events.js +96 -0
  321. package/dist/modes/interactive/startup-ui.js +5 -1
  322. package/dist/modes/interactive/status-area.d.ts +73 -0
  323. package/dist/modes/interactive/status-area.js +190 -0
  324. package/dist/modes/interactive/status-bar.d.ts +99 -11
  325. package/dist/modes/interactive/status-bar.js +273 -90
  326. package/dist/modes/interactive/status-line.d.ts +33 -0
  327. package/dist/modes/interactive/status-line.js +113 -0
  328. package/dist/modes/interactive/subagent-view.d.ts +56 -0
  329. package/dist/modes/interactive/subagent-view.js +154 -0
  330. package/dist/modes/interactive/tasks-report.d.ts +27 -0
  331. package/dist/modes/interactive/tasks-report.js +114 -0
  332. package/dist/modes/interactive/tool-view.d.ts +12 -2
  333. package/dist/modes/interactive/tool-view.js +34 -4
  334. package/dist/modes/print/print-mode.d.ts +13 -4
  335. package/dist/modes/print/print-mode.js +56 -12
  336. package/dist/modes/rpc/commands.d.ts +9 -0
  337. package/dist/modes/rpc/commands.js +44 -0
  338. package/dist/modes/rpc/rpc-mode.js +7 -1
  339. package/dist/modes/session-report.d.ts +11 -0
  340. package/dist/modes/session-report.js +48 -1
  341. package/dist/permissions/auto-safe.d.ts +5 -0
  342. package/dist/permissions/auto-safe.js +18 -0
  343. package/dist/permissions/bypass.d.ts +32 -0
  344. package/dist/permissions/bypass.js +36 -0
  345. package/dist/permissions/classifier.d.ts +4 -0
  346. package/dist/permissions/classifier.js +2 -0
  347. package/dist/permissions/modes.js +1 -1
  348. package/dist/permissions/pipeline.d.ts +43 -4
  349. package/dist/permissions/pipeline.js +114 -9
  350. package/dist/permissions/readonly-bash.d.ts +30 -0
  351. package/dist/permissions/readonly-bash.js +118 -0
  352. package/dist/permissions/types.d.ts +42 -0
  353. package/dist/plan/compose.d.ts +9 -0
  354. package/dist/plan/compose.js +19 -0
  355. package/dist/plan/controller.d.ts +71 -0
  356. package/dist/plan/controller.js +27 -0
  357. package/dist/plan/done-markers.d.ts +17 -0
  358. package/dist/plan/done-markers.js +51 -0
  359. package/dist/plan/extract.d.ts +29 -0
  360. package/dist/plan/extract.js +160 -0
  361. package/dist/plan/prompts.d.ts +36 -0
  362. package/dist/plan/prompts.js +89 -0
  363. package/dist/plan/store.d.ts +37 -0
  364. package/dist/plan/store.js +90 -0
  365. package/dist/rpc.d.ts +58 -2
  366. package/dist/rpc.js +7 -0
  367. package/dist/sandbox/bash.d.ts +74 -0
  368. package/dist/sandbox/bash.js +126 -0
  369. package/dist/sandbox/detect.d.ts +48 -0
  370. package/dist/sandbox/detect.js +149 -0
  371. package/dist/sandbox/index.d.ts +5 -0
  372. package/dist/sandbox/index.js +5 -0
  373. package/dist/sandbox/profile.d.ts +33 -0
  374. package/dist/sandbox/profile.js +76 -0
  375. package/dist/sandbox/wrap.d.ts +29 -0
  376. package/dist/sandbox/wrap.js +117 -0
  377. package/dist/sdk.d.ts +33 -2
  378. package/dist/sdk.js +24 -2
  379. package/dist/session/types.d.ts +3 -1
  380. package/dist/session/types.js +1 -0
  381. package/dist/skills/builtin.d.ts +31 -0
  382. package/dist/skills/builtin.js +109 -0
  383. package/dist/skills/discover.d.ts +2 -1
  384. package/dist/skills/index-prompt.js +5 -8
  385. package/dist/tools/background-jobs.d.ts +71 -0
  386. package/dist/tools/background-jobs.js +198 -0
  387. package/dist/tools/bash.d.ts +53 -1
  388. package/dist/tools/bash.js +158 -7
  389. package/dist/tools/clipboard-image.d.ts +44 -0
  390. package/dist/tools/clipboard-image.js +148 -0
  391. package/dist/tools/edit.d.ts +2 -0
  392. package/dist/tools/edit.js +18 -3
  393. package/dist/tools/image-file.d.ts +38 -3
  394. package/dist/tools/image-file.js +81 -10
  395. package/dist/tools/image-resize.d.ts +58 -0
  396. package/dist/tools/image-resize.js +97 -0
  397. package/dist/tools/presets.d.ts +7 -2
  398. package/dist/tools/presets.js +11 -4
  399. package/dist/tools/read.d.ts +5 -2
  400. package/dist/tools/read.js +13 -10
  401. package/dist/tools/registry.d.ts +7 -1
  402. package/dist/tools/registry.js +27 -4
  403. package/dist/tools/task-ctl.d.ts +22 -0
  404. package/dist/tools/task-ctl.js +116 -0
  405. package/dist/tools/task.d.ts +22 -10
  406. package/dist/tools/task.js +66 -26
  407. package/dist/tools/todo.d.ts +22 -2
  408. package/dist/tools/todo.js +58 -14
  409. package/dist/tools/truncate.d.ts +15 -0
  410. package/dist/tools/truncate.js +25 -0
  411. package/dist/tools/types.d.ts +113 -0
  412. package/dist/tools/types.js +5 -0
  413. package/dist/tools/write.js +3 -0
  414. package/dist/tui/glyphs.d.ts +4 -0
  415. package/dist/tui/glyphs.js +4 -0
  416. package/dist/tui/keybindings.d.ts +6 -1
  417. package/dist/tui/keybindings.js +6 -1
  418. package/docs/acp.md +78 -0
  419. package/docs/agents.md +250 -0
  420. package/docs/codemode.md +12 -8
  421. package/docs/hooks.md +26 -22
  422. package/docs/permissions.md +78 -13
  423. package/docs/plan.md +107 -0
  424. package/docs/providers.md +228 -52
  425. package/docs/rewind-plan.md +181 -0
  426. package/docs/rpc.md +92 -5
  427. package/docs/sandbox.md +215 -0
  428. package/docs/session-format.md +41 -21
  429. package/docs/sessions.md +41 -1
  430. package/docs/tui-design.md +751 -0
  431. package/docs/tui.md +179 -21
  432. package/package.json +13 -2
@@ -0,0 +1,89 @@
1
+ /**
2
+ * default 模式下 `task(agent="claude")` 会问两次:task 工具调用一次,外部 Agent 本会话首次运行再一次
3
+ * (agents/external.ts 的 `firstRun`)。界面层把它们合并成一次确认。[W5-U]
4
+ *
5
+ * 规则(不放宽权限语义):
6
+ * - 用户在对话框里**允许**了一次 `task` 工具调用,且它的 `agent` 是外部 Agent(类型的 runner 不是
7
+ * ama)——对话框已把「以你在该 CLI 的登录运行」写在正文里——记下 `{agent, 时刻}`;
8
+ * - **紧接着**的下一个审批请求若是同一 Agent 的首次运行确认(`toolName: "task"`、输入带 `note`),其
9
+ * `context.taskId` 对应的任务是该 Agent、且在允许之后才建立(同一次调用产生的任务),并在 60 s 内
10
+ * 到达,就以「允许」自动答复,不再弹框;
11
+ * - 记录只用一次;中间夹了任何别的审批请求、拒绝、超时,都作废——首次运行确认照常弹出。
12
+ */
13
+ import { taskRegistryView } from "../../agent/subagent-registry.js";
14
+ import { isFirstRunRequest } from "./approval-dialog.js";
15
+ import { listAgents } from "./tasks-report.js";
16
+ export const MERGE_WINDOW_MS = 60_000;
17
+ function input(request) {
18
+ const value = request.input;
19
+ return typeof value === "object" && value !== null ? value : {};
20
+ }
21
+ export class FirstRunMerge {
22
+ deps;
23
+ pending;
24
+ constructor(deps) {
25
+ this.deps = deps;
26
+ }
27
+ now() {
28
+ return this.deps.now?.() ?? Date.now();
29
+ }
30
+ /** `task` 调用的 agent 是外部 Agent 时返回它的 runner。 */
31
+ externalRunner(agent) {
32
+ const runner = this.deps.runnerOf?.(agent) ??
33
+ listAgents(this.deps.sessionId()).find((a) => a.name === agent)?.runner;
34
+ return runner === undefined || runner === "ama" ? undefined : runner;
35
+ }
36
+ taskOf(taskId) {
37
+ if (this.deps.taskOf !== undefined)
38
+ return this.deps.taskOf(taskId);
39
+ return taskRegistryView(this.deps.sessionId())?.get(taskId);
40
+ }
41
+ /** 请求到来:命中合并返回 `allow`(不弹框);否则返回 undefined,并作废记录。 */
42
+ intercept(request) {
43
+ const pending = this.pending;
44
+ this.pending = undefined;
45
+ if (pending === undefined || !isFirstRunRequest(request))
46
+ return undefined;
47
+ if (this.now() - pending.at > (this.deps.windowMs ?? MERGE_WINDOW_MS))
48
+ return undefined;
49
+ const taskId = request.context?.taskId;
50
+ if (taskId === undefined || request.context?.origin !== undefined)
51
+ return undefined;
52
+ const task = this.taskOf(taskId);
53
+ if (task === undefined || task.agent !== pending.agent || task.startedAt < pending.at)
54
+ return undefined;
55
+ const runner = this.externalRunner(pending.agent);
56
+ if (runner === undefined || input(request)["agent"] !== runner)
57
+ return undefined;
58
+ return "allow";
59
+ }
60
+ /** 用户答复之后:允许了外部 Agent 的 task 调用就记下。 */
61
+ record(request, decision) {
62
+ this.pending = undefined;
63
+ if (decision !== "allow" && decision !== "allow_session")
64
+ return;
65
+ if (request.toolName !== "task" || isFirstRunRequest(request))
66
+ return;
67
+ if ((request.context?.depth ?? 0) > 0 || request.context?.origin !== undefined)
68
+ return;
69
+ const agent = input(request)["agent"];
70
+ if (typeof agent !== "string" || this.externalRunner(agent) === undefined)
71
+ return;
72
+ this.pending = { agent, at: this.now() };
73
+ }
74
+ }
75
+ /** 把合并规则套在界面 broker 外面;自动答复时调 `onMerged`(消息区一行说明)。 */
76
+ export function mergingBroker(inner, merge, onMerged) {
77
+ return {
78
+ async ask(request, signal) {
79
+ const merged = merge.intercept(request);
80
+ if (merged !== undefined) {
81
+ onMerged?.(request);
82
+ return merged;
83
+ }
84
+ const decision = await inner.ask(request, signal);
85
+ merge.record(request, decision);
86
+ return decision;
87
+ },
88
+ };
89
+ }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * `Ctrl+V` 与 `/paste`:剪贴板图片写进 `<dataDir>/clipboard/`,输入框插入 `@<路径>`(发送时按 `@图片`
3
+ * 附件读取)。[W5-U] 读取本身在 tools/clipboard-image.ts(W5-I)。
4
+ */
5
+ import { type ClipboardDeps } from "../../tools/clipboard-image.js";
6
+ export type PasteOutcome = {
7
+ ok: true;
8
+ ref: string;
9
+ path: string;
10
+ } | {
11
+ ok: false;
12
+ message: string;
13
+ };
14
+ export declare const NO_CLIPBOARD_TOOL = "\u8BFB\u4E0D\u4E86\u526A\u8D34\u677F\u56FE\u7247\uFF1A\u6CA1\u6709\u53EF\u7528\u7684\u7CFB\u7EDF\u547D\u4EE4\uFF08macOS osascript / pngpaste\uFF0CLinux wl-paste / xclip\uFF0CWindows PowerShell\uFF09";
15
+ export declare const NO_CLIPBOARD_IMAGE = "\u526A\u8D34\u677F\u91CC\u6CA1\u6709\u56FE\u7247";
16
+ /** `@路径`;路径带空白时加引号。 */
17
+ export declare function imageRef(path: string): string;
18
+ export declare function pasteImage(dataDir: string, deps?: ClipboardDeps): Promise<PasteOutcome>;
@@ -0,0 +1,28 @@
1
+ /**
2
+ * `Ctrl+V` 与 `/paste`:剪贴板图片写进 `<dataDir>/clipboard/`,输入框插入 `@<路径>`(发送时按 `@图片`
3
+ * 附件读取)。[W5-U] 读取本身在 tools/clipboard-image.ts(W5-I)。
4
+ */
5
+ import { readClipboardImage } from "../../tools/clipboard-image.js";
6
+ export const NO_CLIPBOARD_TOOL = "读不了剪贴板图片:没有可用的系统命令(macOS osascript / pngpaste,Linux wl-paste / xclip,Windows PowerShell)";
7
+ export const NO_CLIPBOARD_IMAGE = "剪贴板里没有图片";
8
+ /** `@路径`;路径带空白时加引号。 */
9
+ export function imageRef(path) {
10
+ return /\s/.test(path) ? `@"${path}"` : `@${path}`;
11
+ }
12
+ export async function pasteImage(dataDir, deps) {
13
+ try {
14
+ const result = await readClipboardImage(dataDir, deps);
15
+ if (result.ok)
16
+ return { ok: true, ref: imageRef(result.path), path: result.path };
17
+ return {
18
+ ok: false,
19
+ message: result.reason === "no_tool" ? NO_CLIPBOARD_TOOL : NO_CLIPBOARD_IMAGE,
20
+ };
21
+ }
22
+ catch (error) {
23
+ return {
24
+ ok: false,
25
+ message: `读取剪贴板失败:${error instanceof Error ? error.message : String(error)}`,
26
+ };
27
+ }
28
+ }
@@ -7,11 +7,14 @@
7
7
  * - 交互模式自有命令:`/tree`(同一文件内换叶子到选中消息之前,文本回填编辑器,可改后重发形成新分支)、
8
8
  * `/permissions`(当前模式、判定顺序与已加载规则);`/help` 追加这两条与按键说明。
9
9
  * - `/session`、`/cache`(无参数)与 `/permissions` 在消息区画左竖条面板(panels.ts),不再拍成文本。
10
+ * - `/rewind`(无参数)打开回滚列表与确认面板(rewind-flow.ts);带参数走 commands-core,对话变了时
11
+ * 重画消息区并回填原消息。
10
12
  * - 不是命令(含模板与 `/skill:`)返回 false,调用方把整行当提示发出。
11
13
  */
12
- import type { AgentSession } from "../../agent/types.js";
14
+ import type { AgentSession, RewindDraftText } from "../../agent/types.js";
13
15
  import type { SwitchRequest } from "../../cli/compose-session.js";
14
16
  import type { Runtime } from "../../cli/runtime.js";
17
+ import type { PermissionMode } from "../../permissions/types.js";
15
18
  import type { Component, SelectItem, Theme } from "../../tui.js";
16
19
  import { type CommandInfo } from "../commands-core.js";
17
20
  import { type NoticeLevel } from "./message-view.js";
@@ -34,6 +37,10 @@ export interface CommandUi {
34
37
  prompt(text: string): void;
35
38
  /** /tree 换叶子后重画消息区。 */
36
39
  reload(): void;
40
+ /** 回滚列表与确认面板(`/rewind` 无参数);没有时回落为文字列表。 */
41
+ rewind?(): Promise<void>;
42
+ /** 回填原消息(文本 + 图片);缺省只回填文本。 */
43
+ setDraft?(draft: RewindDraftText): void;
37
44
  exit(code: number): void;
38
45
  now(): number;
39
46
  /** 面板用的主题(与 panel 一起提供)。 */
@@ -42,6 +49,10 @@ export interface CommandUi {
42
49
  home?: string;
43
50
  /** 进程环境:给出时 /model 选择器按 fake-visibility 规则藏起测试供应商 fake。 */
44
51
  env?: Readonly<Record<string, string | undefined>>;
52
+ /** 切换权限模式前的确认(进入 Bypass 前弹确认框);返回 false 保持原模式。 */
53
+ confirmMode?(mode: PermissionMode): boolean | Promise<boolean>;
54
+ /** [W5-U] 界面自己处理的第五波命令(agent-ui.ts);处理了返回 true。 */
55
+ extra?(name: string, args: string): Promise<boolean>;
45
56
  }
46
57
  /** 当前分支上的条目 id(树选择器标 ●)。 */
47
58
  export declare function activeBranchIds(session: AgentSession): Set<string>;
@@ -7,10 +7,13 @@
7
7
  * - 交互模式自有命令:`/tree`(同一文件内换叶子到选中消息之前,文本回填编辑器,可改后重发形成新分支)、
8
8
  * `/permissions`(当前模式、判定顺序与已加载规则);`/help` 追加这两条与按键说明。
9
9
  * - `/session`、`/cache`(无参数)与 `/permissions` 在消息区画左竖条面板(panels.ts),不再拍成文本。
10
+ * - `/rewind`(无参数)打开回滚列表与确认面板(rewind-flow.ts);带参数走 commands-core,对话变了时
11
+ * 重画消息区并回填原消息。
10
12
  * - 不是命令(含模板与 `/skill:`)返回 false,调用方把整行当提示发出。
11
13
  */
12
14
  import { formatModelRef } from "../../ai/providers/channels.js";
13
15
  import { AgentSessionImpl } from "../../agent/session.js";
16
+ import { THINKING_LEVELS } from "../../ai/thinking.js";
14
17
  import { listSessions } from "../../cli/compose-store.js";
15
18
  import { hideFakeProvider } from "../../cli/fake-visibility.js";
16
19
  import { AUTO_LAYER_TEXT, permissionModeLabel } from "../../permissions/modes.js";
@@ -27,8 +30,11 @@ export const ALL_COMMANDS = [...BUILTIN_COMMANDS, ...INTERACTIVE_COMMANDS];
27
30
  export const KEY_HINTS = [
28
31
  "Enter 发送(运行中 = 插话) Alt+Enter 排到本轮之后 Shift+Enter / Ctrl+J 换行",
29
32
  "Esc 中断(排队消息回填编辑器) Alt+↑ 取回最后一条排队消息",
30
- "Shift+Tab 切换权限模式 Ctrl+L 模型 Ctrl+T 思考级别 Ctrl+O 展开工具输出与思考",
33
+ "空闲时 Esc Esc:输入框为空 = 回滚(/rewind),有字 = 清空(↑ 取回)",
34
+ "Shift+Tab / Tab(输入为空时)切换权限模式(进入 Bypass 前确认) Ctrl+L 模型 Ctrl+T 思考级别 Ctrl+O 展开工具输出与思考",
31
35
  "审批:1–3 或 ↑↓ Enter 选择,y 允许 a 本会话允许同类 n / Esc 拒绝 v 完整输入",
36
+ "计划审批:1 批准 2 新上下文执行 3 继续修改 4 放弃并退出 Plan e 编辑计划 Esc 留在 Plan",
37
+ "Ctrl+V 粘贴剪贴板图片(插入 @路径) Ctrl+G 底部信息行两行 / 一行",
32
38
  "Ctrl+C 清空输入(再按退出) Ctrl+D 空输入时退出 Tab 补全 @ 引用文件",
33
39
  ].join("\n");
34
40
  function homeOf(ui) {
@@ -159,6 +165,11 @@ async function handlePick(what, ui) {
159
165
  if (picked === undefined)
160
166
  return;
161
167
  const mode = picked.value;
168
+ const current = session.state.permissionMode;
169
+ if (mode !== current && ui.confirmMode !== undefined && !(await ui.confirmMode(mode))) {
170
+ ui.notice("info", `已取消,权限模式仍为 ${permissionModeLabel(current)}`);
171
+ return;
172
+ }
162
173
  session.setPermissionMode(mode);
163
174
  ui.notice("info", `权限模式:${permissionModeLabel(mode)}`);
164
175
  return;
@@ -171,6 +182,10 @@ async function handlePick(what, ui) {
171
182
  title: "思考级别",
172
183
  items: thinkingItems(reasoning),
173
184
  selected: session.state.thinkingLevel,
185
+ currentValue: session.state.thinkingLevel,
186
+ filterable: false,
187
+ numberKeys: true,
188
+ footer: `↑↓ 选择 · 1-${THINKING_LEVELS.length} 直接选 · Enter 确认 · Esc 取消`,
174
189
  });
175
190
  if (picked !== undefined)
176
191
  session.setThinkingLevel(picked.value);
@@ -184,10 +199,16 @@ export async function runInteractiveCommand(line, ui) {
184
199
  if (parsed === undefined)
185
200
  return false;
186
201
  try {
202
+ if (ui.extra !== undefined && (await ui.extra(parsed.name, parsed.args)))
203
+ return true;
187
204
  if (parsed.name === "tree") {
188
205
  await treeCommand(ui);
189
206
  return true;
190
207
  }
208
+ if (parsed.name === "rewind" && parsed.args === "" && ui.rewind !== undefined) {
209
+ await ui.rewind();
210
+ return true;
211
+ }
191
212
  const theme = ui.theme?.();
192
213
  if (parsed.name === "permissions") {
193
214
  if (ui.panel !== undefined && theme !== undefined) {
@@ -211,6 +232,7 @@ export async function runInteractiveCommand(line, ui) {
211
232
  runtime: ui.runtime,
212
233
  session: () => ui.session(),
213
234
  switchSession: (request) => ui.switchSession(request),
235
+ ...(ui.confirmMode !== undefined ? { confirmPermissionMode: ui.confirmMode } : {}),
214
236
  });
215
237
  if (result === undefined)
216
238
  return false;
@@ -225,12 +247,20 @@ export async function runInteractiveCommand(line, ui) {
225
247
  await handlePick(result.what, ui);
226
248
  break;
227
249
  case "handled":
250
+ if (result.reload === true)
251
+ ui.reload();
228
252
  if (parsed.name === "help") {
229
253
  const extra = INTERACTIVE_COMMANDS.map((c) => `/${c.name} ${c.description}`);
230
254
  ui.notice("info", [result.message ?? "", ...extra, "", KEY_HINTS].join("\n"));
231
255
  }
232
256
  else if (result.message !== undefined)
233
257
  ui.notice("info", result.message);
258
+ if (result.draft !== undefined) {
259
+ if (ui.setDraft !== undefined)
260
+ ui.setDraft(result.draft);
261
+ else
262
+ ui.setEditorText(result.draft.text);
263
+ }
234
264
  break;
235
265
  }
236
266
  }
@@ -0,0 +1,49 @@
1
+ /**
2
+ * 通用确认框:底部覆盖层 + `Box`,选项样式与审批对话框一致(终端界面视觉设计 v1 §3.11)。
3
+ *
4
+ * - 编号选项 `› 1. 进入 Bypass y`,选中行 `›` + `selection` 底色,右侧按键提示 `dim`;
5
+ * - 按键:↑↓ 移动(循环)、Enter 确认、数字直选、选项按键提示里的单个字母直选(如 `y` / `n`);
6
+ * Esc / Ctrl+C / `tui.select.cancel` 取消(返回 undefined);
7
+ * - 宽 < 56 列紧凑:去掉空行、按键提示只留第一个、底部提示缩短。字形取 `theme.glyphs`(ASCII 回退)。
8
+ *
9
+ * 用途:进入 Bypass permissions 前的确认(`confirmBypass`,permissions/bypass.ts)。
10
+ */
11
+ import { type Component, type Focusable, type Keybindings, type OverlayHandle, type OverlayOptions, type SemanticColor, type Theme } from "../../tui.js";
12
+ export interface ChoiceOption {
13
+ label: string;
14
+ /** 右侧按键提示(空格分隔);其中的单个字母同时是直选键。 */
15
+ keys: string;
16
+ }
17
+ export interface ChoiceSpec {
18
+ title: string;
19
+ /** 正文(已着色),选项之上。 */
20
+ body: readonly string[];
21
+ options: readonly ChoiceOption[];
22
+ /** 缺省选中的下标。 */
23
+ selected?: number;
24
+ borderColor?: SemanticColor;
25
+ }
26
+ export interface ChoiceHost {
27
+ theme: Theme;
28
+ keybindings?: Keybindings;
29
+ showOverlay(component: Component, options: OverlayOptions): OverlayHandle;
30
+ }
31
+ export declare class ChoiceDialog implements Component, Focusable {
32
+ private readonly spec;
33
+ private readonly host;
34
+ private readonly done;
35
+ focused: boolean;
36
+ private selected;
37
+ constructor(spec: ChoiceSpec, host: Omit<ChoiceHost, "showOverlay">, done: (index: number | undefined) => void);
38
+ get selectedIndex(): number;
39
+ handleInput(data: string): void;
40
+ render(width: number): string[];
41
+ private optionLines;
42
+ invalidate(): void;
43
+ }
44
+ /** 打开确认框(底部覆盖层):返回选中的下标,取消返回 undefined。 */
45
+ export declare function openChoice(host: ChoiceHost, spec: ChoiceSpec): Promise<number | undefined>;
46
+ /** Bypass 确认框的内容(缺省选中「取消」,边框 warning)。 */
47
+ export declare function bypassChoiceSpec(theme: Theme): ChoiceSpec;
48
+ /** 进入 Bypass 前问一次:选「进入 Bypass」返回 true,取消 / 选「取消」返回 false。 */
49
+ export declare function confirmBypass(host: ChoiceHost): Promise<boolean>;
@@ -0,0 +1,121 @@
1
+ /**
2
+ * 通用确认框:底部覆盖层 + `Box`,选项样式与审批对话框一致(终端界面视觉设计 v1 §3.11)。
3
+ *
4
+ * - 编号选项 `› 1. 进入 Bypass y`,选中行 `›` + `selection` 底色,右侧按键提示 `dim`;
5
+ * - 按键:↑↓ 移动(循环)、Enter 确认、数字直选、选项按键提示里的单个字母直选(如 `y` / `n`);
6
+ * Esc / Ctrl+C / `tui.select.cancel` 取消(返回 undefined);
7
+ * - 宽 < 56 列紧凑:去掉空行、按键提示只留第一个、底部提示缩短。字形取 `theme.glyphs`(ASCII 回退)。
8
+ *
9
+ * 用途:进入 Bypass permissions 前的确认(`confirmBypass`,permissions/bypass.ts)。
10
+ */
11
+ import { BYPASS_CHOICES, BYPASS_CONFIRM_TITLE, BYPASS_DEFAULT_CHOICE, BYPASS_RISK_LINES, } from "../../permissions/bypass.js";
12
+ import { Box, defaultKeybindings, matchesKey, padToWidth, truncateToWidth, visibleWidth, wrapTextWithAnsi, } from "../../tui.js";
13
+ /** 宽度低于它(Box 外宽)时用紧凑布局。 */
14
+ const COMPACT_WIDTH = 56;
15
+ export class ChoiceDialog {
16
+ spec;
17
+ host;
18
+ done;
19
+ focused = false;
20
+ selected;
21
+ constructor(spec, host, done) {
22
+ this.spec = spec;
23
+ this.host = host;
24
+ this.done = done;
25
+ this.selected = Math.max(0, Math.min(spec.options.length - 1, spec.selected ?? 0));
26
+ }
27
+ get selectedIndex() {
28
+ return this.selected;
29
+ }
30
+ handleInput(data) {
31
+ const keys = this.host.keybindings ?? defaultKeybindings;
32
+ const n = this.spec.options.length;
33
+ const letter = data.length === 1 ? data.toLowerCase() : "";
34
+ const byLetter = letter === "" || !/^[a-z]$/.test(letter)
35
+ ? -1
36
+ : this.spec.options.findIndex((o) => o.keys.split(/\s+/).includes(letter));
37
+ if (byLetter !== -1)
38
+ this.done(byLetter);
39
+ else if (matchesKey(data, "escape") ||
40
+ keys.matches(data, "app.clear") ||
41
+ keys.matches(data, "tui.select.cancel")) {
42
+ this.done(undefined);
43
+ }
44
+ else if (/^[1-9]$/.test(data) && Number(data) <= n)
45
+ this.done(Number(data) - 1);
46
+ else if (keys.matches(data, "tui.select.up"))
47
+ this.selected = (this.selected + n - 1) % n;
48
+ else if (keys.matches(data, "tui.select.down"))
49
+ this.selected = (this.selected + 1) % n;
50
+ else if (matchesKey(data, "enter"))
51
+ this.done(this.selected);
52
+ }
53
+ render(width) {
54
+ const { theme } = this.host;
55
+ const compact = width + 4 < COMPACT_WIDTH;
56
+ const lines = [];
57
+ for (const line of this.spec.body)
58
+ lines.push(...wrapTextWithAnsi(line, width));
59
+ if (!compact)
60
+ lines.push("");
61
+ lines.push(...this.optionLines(width, compact));
62
+ if (!compact)
63
+ lines.push("");
64
+ const g = theme.glyphs;
65
+ const arrows = g.arrowUp + g.arrowDown;
66
+ const n = this.spec.options.length;
67
+ lines.push(theme.fg("dim", compact
68
+ ? `${arrows} Enter · 1-${n} · Esc`
69
+ : `${arrows} 选择 · Enter 确认 · 1-${n} 直接选 · Esc 取消`));
70
+ return lines.map((l) => truncateToWidth(l, width));
71
+ }
72
+ optionLines(width, compact) {
73
+ const { theme } = this.host;
74
+ const g = theme.glyphs;
75
+ const shown = this.spec.options.map((o) => (compact ? o.keys.split(/\s+/)[0] : o.keys));
76
+ const keyWidth = Math.max(...shown.map((k) => visibleWidth(k)));
77
+ return this.spec.options.map((option, i) => {
78
+ const selected = i === this.selected;
79
+ const left = `${selected ? g.prompt : " "} ${i + 1}. ${option.label}`;
80
+ const right = padToWidth(shown[i], keyWidth);
81
+ const room = Math.max(1, width - keyWidth - 1);
82
+ const label = selected ? theme.fg("accent", theme.bold(left)) : left;
83
+ const line = padToWidth(truncateToWidth(label, room), room) + " " + theme.fg("dim", right);
84
+ return selected ? theme.bg("selection", line) : line;
85
+ });
86
+ }
87
+ invalidate() { }
88
+ }
89
+ /** 打开确认框(底部覆盖层):返回选中的下标,取消返回 undefined。 */
90
+ export function openChoice(host, spec) {
91
+ return new Promise((resolve) => {
92
+ let handle;
93
+ let settled = false;
94
+ const dialog = new ChoiceDialog(spec, host, (index) => {
95
+ if (settled)
96
+ return;
97
+ settled = true;
98
+ handle?.hide();
99
+ resolve(index);
100
+ });
101
+ handle = host.showOverlay(new Box(dialog, {
102
+ title: spec.title,
103
+ theme: host.theme,
104
+ ...(spec.borderColor !== undefined ? { borderColor: spec.borderColor } : {}),
105
+ }), { anchor: "bottom" });
106
+ });
107
+ }
108
+ /** Bypass 确认框的内容(缺省选中「取消」,边框 warning)。 */
109
+ export function bypassChoiceSpec(theme) {
110
+ return {
111
+ title: BYPASS_CONFIRM_TITLE,
112
+ body: BYPASS_RISK_LINES.map((line, i) => (i === 0 ? theme.fg("warning", line) : line)),
113
+ options: BYPASS_CHOICES,
114
+ selected: BYPASS_DEFAULT_CHOICE,
115
+ borderColor: "warning",
116
+ };
117
+ }
118
+ /** 进入 Bypass 前问一次:选「进入 Bypass」返回 true,取消 / 选「取消」返回 false。 */
119
+ export async function confirmBypass(host) {
120
+ return (await openChoice(host, bypassChoiceSpec(host.theme))) === 0;
121
+ }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * 空闲时的双击 Esc(rewind-plan §4):纯状态机,键位分派里用。[RW-C]
3
+ *
4
+ * - 输入框为空:第一次 `arm-rewind`(提示「再按 Esc 回滚」),间隔内第二次 `rewind`(打开回滚列表)。
5
+ * - 输入框有字:第一次 `arm-clear`(提示「再按 Esc 清空」),间隔内第二次 `clear`(清空并存进输入历史)。
6
+ * - 两次之间输入框状态变了(空 ↔ 有字)、超过间隔、或中间按了别的键(`reset()`)都重新计。
7
+ */
8
+ /** 两次 Esc 的最大间隔。 */
9
+ export declare const DOUBLE_ESC_MS = 800;
10
+ /** 第一次 Esc 的提示停留时间。 */
11
+ export declare const DOUBLE_ESC_HINT_MS = 1000;
12
+ export type EscAction = "arm-rewind" | "rewind" | "arm-clear" | "clear";
13
+ export declare class DoubleEscape {
14
+ private readonly windowMs;
15
+ private armed;
16
+ constructor(windowMs?: number);
17
+ press(now: number, editorEmpty: boolean): EscAction;
18
+ reset(): void;
19
+ get isArmed(): boolean;
20
+ }
@@ -0,0 +1,34 @@
1
+ /**
2
+ * 空闲时的双击 Esc(rewind-plan §4):纯状态机,键位分派里用。[RW-C]
3
+ *
4
+ * - 输入框为空:第一次 `arm-rewind`(提示「再按 Esc 回滚」),间隔内第二次 `rewind`(打开回滚列表)。
5
+ * - 输入框有字:第一次 `arm-clear`(提示「再按 Esc 清空」),间隔内第二次 `clear`(清空并存进输入历史)。
6
+ * - 两次之间输入框状态变了(空 ↔ 有字)、超过间隔、或中间按了别的键(`reset()`)都重新计。
7
+ */
8
+ /** 两次 Esc 的最大间隔。 */
9
+ export const DOUBLE_ESC_MS = 800;
10
+ /** 第一次 Esc 的提示停留时间。 */
11
+ export const DOUBLE_ESC_HINT_MS = 1000;
12
+ export class DoubleEscape {
13
+ windowMs;
14
+ armed;
15
+ constructor(windowMs = DOUBLE_ESC_MS) {
16
+ this.windowMs = windowMs;
17
+ }
18
+ press(now, editorEmpty) {
19
+ const kind = editorEmpty ? "rewind" : "clear";
20
+ const armed = this.armed;
21
+ if (armed !== undefined && armed.kind === kind && now - armed.at <= this.windowMs) {
22
+ this.armed = undefined;
23
+ return kind;
24
+ }
25
+ this.armed = { kind, at: now };
26
+ return kind === "rewind" ? "arm-rewind" : "arm-clear";
27
+ }
28
+ reset() {
29
+ this.armed = undefined;
30
+ }
31
+ get isArmed() {
32
+ return this.armed !== undefined;
33
+ }
34
+ }
@@ -0,0 +1,32 @@
1
+ /**
2
+ * 交互界面对几类会话事件的友好提示(第五波其它批次转来的显示项)。[W5-A]
3
+ *
4
+ * - W5-I:`entry_appended` 里 `reason: "image_budget"` 的 `context_edit`(一次降级会连写多条,每条一个消息)
5
+ * 合并成一条「已省略 N 张早期图片以符合请求上限」,N 按占位文本数;
6
+ * - W5-H1:`compaction_end.error` 的「compaction did not shrink」换成中文说明(其余错误原样)。
7
+ */
8
+ import type { SessionEvent } from "../../agent/types.js";
9
+ /** `compaction_end.error` 的显示文本。 */
10
+ export declare function compactionErrorText(error: string): string;
11
+ /** 同一轮同步写下的 image_budget 降级合并成一条提示。 */
12
+ export declare class ImageBudgetNotices {
13
+ private readonly notify;
14
+ private pending;
15
+ private scheduled;
16
+ constructor(notify: (text: string) => void);
17
+ onEvent(event: SessionEvent): void;
18
+ }
19
+ /** `agent_settled.warning` 为它时不再单独显示(`limit_reached` 事件已给出友好提示)。 */
20
+ export declare const LIMIT_WARNING = "limit_reached";
21
+ /** `limit_reached`:回合 / 费用到限(每次运行每类一次)。 */
22
+ export declare function limitReachedText(event: Extract<SessionEvent, {
23
+ type: "limit_reached";
24
+ }>): string;
25
+ /** `model_fallback`:主模型失败后改用回退模型重试一次。 */
26
+ export declare function modelFallbackText(event: Extract<SessionEvent, {
27
+ type: "model_fallback";
28
+ }>): string;
29
+ /** `background_job`:后台命令启动 / 退出 / 停止。 */
30
+ export declare function backgroundJobText(event: Extract<SessionEvent, {
31
+ type: "background_job";
32
+ }>): string;
@@ -0,0 +1,79 @@
1
+ /**
2
+ * 交互界面对几类会话事件的友好提示(第五波其它批次转来的显示项)。[W5-A]
3
+ *
4
+ * - W5-I:`entry_appended` 里 `reason: "image_budget"` 的 `context_edit`(一次降级会连写多条,每条一个消息)
5
+ * 合并成一条「已省略 N 张早期图片以符合请求上限」,N 按占位文本数;
6
+ * - W5-H1:`compaction_end.error` 的「compaction did not shrink」换成中文说明(其余错误原样)。
7
+ */
8
+ import { IMAGE_OMITTED_FOR_BUDGET, IMAGE_OMITTED_TOO_LARGE, } from "../../compaction/image-budget.js";
9
+ /** `compaction_end.error` 的显示文本。 */
10
+ export function compactionErrorText(error) {
11
+ if (/compaction did not shrink/i.test(error)) {
12
+ return "压缩后上下文没有变小,已保留原对话(不写摘要)";
13
+ }
14
+ return error;
15
+ }
16
+ function count(text, needle) {
17
+ let n = 0;
18
+ for (let at = text.indexOf(needle); at !== -1; at = text.indexOf(needle, at + needle.length))
19
+ n++;
20
+ return n;
21
+ }
22
+ /** 同一轮同步写下的 image_budget 降级合并成一条提示。 */
23
+ export class ImageBudgetNotices {
24
+ notify;
25
+ pending = 0;
26
+ scheduled = false;
27
+ constructor(notify) {
28
+ this.notify = notify;
29
+ }
30
+ onEvent(event) {
31
+ if (event.type !== "entry_appended")
32
+ return;
33
+ const entry = event.entry;
34
+ if (entry.type !== "context_edit" || entry.reason !== "image_budget")
35
+ return;
36
+ const text = entry.replacement ?? "";
37
+ const images = count(text, IMAGE_OMITTED_FOR_BUDGET) + count(text, IMAGE_OMITTED_TOO_LARGE);
38
+ this.pending += Math.max(1, images);
39
+ if (this.scheduled)
40
+ return;
41
+ this.scheduled = true;
42
+ queueMicrotask(() => {
43
+ const n = this.pending;
44
+ this.pending = 0;
45
+ this.scheduled = false;
46
+ this.notify(`已省略 ${n} 张早期图片以符合请求上限`);
47
+ });
48
+ }
49
+ }
50
+ // ---------------------------------------------------------------------------
51
+ // [W5-U] 第五波 harness 事件(W5-H2)的提示文本:交互界面与 line 模式共用
52
+ // ---------------------------------------------------------------------------
53
+ /** `agent_settled.warning` 为它时不再单独显示(`limit_reached` 事件已给出友好提示)。 */
54
+ export const LIMIT_WARNING = "limit_reached";
55
+ /** `limit_reached`:回合 / 费用到限(每次运行每类一次)。 */
56
+ export function limitReachedText(event) {
57
+ if (event.kind === "turns")
58
+ return `已到回合上限(${event.limit} 回合),本次运行停止;发新消息继续(--max-turns / limits.maxTurns)`;
59
+ const spent = `$${event.value.toFixed(2)}`;
60
+ return `已到费用上限 $${event.limit.toFixed(2)}(本次运行已用 ${spent}),本次运行停止;发新消息继续(--max-cost / limits.maxCostUsd)`;
61
+ }
62
+ /** `model_fallback`:主模型失败后改用回退模型重试一次。 */
63
+ export function modelFallbackText(event) {
64
+ const ref = (m) => `${m.provider}/${m.id}`;
65
+ return `${ref(event.from)} 不可用(${event.reason}),本次请求改用 ${ref(event.to)},回复后切回`;
66
+ }
67
+ /** `background_job`:后台命令启动 / 退出 / 停止。 */
68
+ export function backgroundJobText(event) {
69
+ const command = event.command.replace(/\s+/g, " ").trim();
70
+ const short = command.length > 60 ? `${command.slice(0, 59)}…` : command;
71
+ switch (event.phase) {
72
+ case "started":
73
+ return `后台命令 ${event.jobId} 已启动:${short}`;
74
+ case "exited":
75
+ return `后台命令 ${event.jobId} 已退出(码 ${event.exitCode ?? "?"}):${short}`;
76
+ case "stopped":
77
+ return `后台命令 ${event.jobId} 已停止:${short}`;
78
+ }
79
+ }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * 外部编辑器(计划审批框的「编辑计划」与「继续修改」):`$VISUAL` → `$EDITOR` → `vi`(Windows `notepad`)。
3
+ * [W5-U] 编辑期间 TUI 挂起(交还终端),回来后整屏重画。临时文件在系统临时目录,读完即删。
4
+ */
5
+ export interface ExternalEditorDeps {
6
+ env: Readonly<Record<string, string | undefined>>;
7
+ /** 挂起 / 恢复界面。 */
8
+ suspend(): void;
9
+ resume(): void;
10
+ platform?: NodeJS.Platform;
11
+ /** 测试注入:代替真实启动编辑器(参数:命令行、文件路径),返回退出码。 */
12
+ run?(command: string, file: string): number | null;
13
+ }
14
+ export declare function editorCommand(env: Readonly<Record<string, string | undefined>>, platform?: NodeJS.Platform): string;
15
+ /** 在外部编辑器里改 `text`;编辑器非零退出或读不回来返回 undefined。 */
16
+ export declare function editExternally(text: string, name: string, deps: ExternalEditorDeps): Promise<string | undefined>;