@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,73 @@
1
+ /**
2
+ * 交互界面的第五波接线(计划审批、子 Agent、外部 Agent、剪贴板图片、harness 提示),从 interactive-mode.ts
3
+ * 拆出来,保持装配文件在 600 行以内。[W5-U]
4
+ *
5
+ * - 事件:`subagent_start / update / end` → 折叠视图(subagent-view.ts)并刷新对应的 task 工具行,后台任务
6
+ * 结束给一行提示;`limit_reached`、`model_fallback`、`background_job` → 一行友好提示。
7
+ * - 会话:`attach(session)` 接管计划审批(plan-flow.ts)、把外部 Agent 的 notice 转到消息区。
8
+ * - 命令:`/plan`(无参数:面板,有待审批的计划时打开审批框)、`/tasks`(无参数:选择任务,查看输出或停止)、
9
+ * `/agents`、`/paste`;其余参数形式交给 commands-core(与 line 模式同一语义)。
10
+ * - `Ctrl+V` / `/paste`:剪贴板图片写进数据目录,输入框光标处插入 `@<路径>`。
11
+ *
12
+ * 外部 Agent 的 notice(W5-E runner 的 `{type:"notice"}`)由任务注册表写进会话日志(`[task tN] …`),没有
13
+ * 事件;这里在会话的 `options.log` 外面套一层,只把这类行转到消息区,其余照原样写日志。
14
+ */
15
+ import type { AgentSession, SessionEvent } from "../../agent/types.js";
16
+ import type { SwitchRequest } from "../../cli/compose-session.js";
17
+ import type { ClipboardDeps } from "../../tools/clipboard-image.js";
18
+ import type { Component, Editor, TUI, Theme } from "../../tui.js";
19
+ import { FirstRunMerge } from "./approval-merge.js";
20
+ import type { NoticeLevel } from "./message-view.js";
21
+ import type { PickerSpec } from "./pickers.js";
22
+ import { PlanFlow } from "./plan-flow.js";
23
+ import type { PlanDialogHost } from "./plan-dialog.js";
24
+ import { SubagentTracker } from "./subagent-view.js";
25
+ import type { ToolTracker } from "./tool-view.js";
26
+ export interface AgentUiDeps {
27
+ theme: Theme;
28
+ tui: TUI;
29
+ editor: Editor;
30
+ tools(): ToolTracker;
31
+ subagents: SubagentTracker;
32
+ session(): AgentSession;
33
+ dataDir: string;
34
+ env: Readonly<Record<string, string | undefined>>;
35
+ now(): number;
36
+ notice(level: NoticeLevel, text: string): void;
37
+ panel(component: Component): void;
38
+ pick(spec: PickerSpec): Promise<{
39
+ value: string;
40
+ } | undefined>;
41
+ hint(text: string): void;
42
+ render(): void;
43
+ switchSession(request: SwitchRequest): Promise<AgentSession>;
44
+ prompt(text: string): void;
45
+ dialog: Omit<PlanDialogHost, "editExternal" | "render">;
46
+ /** 路径显示(缩写家目录)。 */
47
+ displayPath(path: string): string;
48
+ /** 剪贴板读取的注入(测试)。 */
49
+ clipboard?: ClipboardDeps;
50
+ }
51
+ export declare class AgentUi {
52
+ private readonly deps;
53
+ readonly plan: PlanFlow;
54
+ readonly merge: FirstRunMerge;
55
+ private restoreLog;
56
+ /** 后台子 Agent 运行中:每秒重画跟随行的耗时(主会话空闲时 Loader 不转)。 */
57
+ private ticker;
58
+ constructor(deps: AgentUiDeps);
59
+ /** 新会话(启动、/new /resume /fork):接管计划审批,转发外部 Agent notice。 */
60
+ attach(session: AgentSession): void;
61
+ detach(): void;
62
+ private followBackground;
63
+ private stopTicker;
64
+ private routeNotices;
65
+ /** 返回 true:事件已处理(调用方只需重画)。 */
66
+ onEvent(event: SessionEvent): boolean;
67
+ /** 交互界面自己处理的第五波命令;不是返回 false。 */
68
+ command(name: string, args: string): Promise<boolean>;
69
+ private showPlan;
70
+ private tasks;
71
+ /** `Ctrl+V` / `/paste`:光标处插入 `@<路径>`。 */
72
+ paste(): Promise<void>;
73
+ }
@@ -0,0 +1,234 @@
1
+ /**
2
+ * 交互界面的第五波接线(计划审批、子 Agent、外部 Agent、剪贴板图片、harness 提示),从 interactive-mode.ts
3
+ * 拆出来,保持装配文件在 600 行以内。[W5-U]
4
+ *
5
+ * - 事件:`subagent_start / update / end` → 折叠视图(subagent-view.ts)并刷新对应的 task 工具行,后台任务
6
+ * 结束给一行提示;`limit_reached`、`model_fallback`、`background_job` → 一行友好提示。
7
+ * - 会话:`attach(session)` 接管计划审批(plan-flow.ts)、把外部 Agent 的 notice 转到消息区。
8
+ * - 命令:`/plan`(无参数:面板,有待审批的计划时打开审批框)、`/tasks`(无参数:选择任务,查看输出或停止)、
9
+ * `/agents`、`/paste`;其余参数形式交给 commands-core(与 line 模式同一语义)。
10
+ * - `Ctrl+V` / `/paste`:剪贴板图片写进数据目录,输入框光标处插入 `@<路径>`。
11
+ *
12
+ * 外部 Agent 的 notice(W5-E runner 的 `{type:"notice"}`)由任务注册表写进会话日志(`[task tN] …`),没有
13
+ * 事件;这里在会话的 `options.log` 外面套一层,只把这类行转到消息区,其余照原样写日志。
14
+ */
15
+ import { taskRegistryView } from "../../agent/subagent-registry.js";
16
+ import { planController } from "../../plan/controller.js";
17
+ import { agentsPanel, planPanel, taskOutputPanel } from "./agent-panels.js";
18
+ import { FirstRunMerge } from "./approval-merge.js";
19
+ import { pasteImage } from "./clipboard-paste.js";
20
+ import { backgroundJobText, limitReachedText, modelFallbackText } from "./event-notices.js";
21
+ import { editExternally } from "./external-editor.js";
22
+ import { PlanFlow } from "./plan-flow.js";
23
+ import { backgroundEndText, SubagentTracker } from "./subagent-view.js";
24
+ import { listTasks, stopTask, taskLine, taskOutput } from "./tasks-report.js";
25
+ const TASK_NOTICE = /^\[task (t\d+)\] ([\s\S]*)$/;
26
+ export class AgentUi {
27
+ deps;
28
+ plan;
29
+ merge;
30
+ restoreLog;
31
+ /** 后台子 Agent 运行中:每秒重画跟随行的耗时(主会话空闲时 Loader 不转)。 */
32
+ ticker;
33
+ constructor(deps) {
34
+ this.deps = deps;
35
+ this.merge = new FirstRunMerge({ sessionId: () => deps.session().state.sessionId });
36
+ this.plan = new PlanFlow({
37
+ dialog: {
38
+ ...deps.dialog,
39
+ displayPath: (path) => deps.displayPath(path),
40
+ render: () => deps.render(),
41
+ editExternal: (text, kind) => editExternally(text, kind === "plan" ? "plan.md" : "feedback.md", {
42
+ env: deps.env,
43
+ suspend: () => deps.tui.stop(),
44
+ resume: () => {
45
+ deps.tui.start();
46
+ deps.tui.forceFullRedraw();
47
+ },
48
+ }),
49
+ },
50
+ switchSession: (request) => deps.switchSession(request),
51
+ notice: (level, text) => deps.notice(level, text),
52
+ prompt: (text) => deps.prompt(text),
53
+ });
54
+ }
55
+ /** 新会话(启动、/new /resume /fork):接管计划审批,转发外部 Agent notice。 */
56
+ attach(session) {
57
+ this.deps.subagents.clear();
58
+ this.stopTicker();
59
+ this.plan.attach(session);
60
+ this.routeNotices(session);
61
+ }
62
+ detach() {
63
+ this.restoreLog?.();
64
+ this.restoreLog = undefined;
65
+ this.stopTicker();
66
+ }
67
+ followBackground() {
68
+ const running = this.deps.subagents.running().filter((s) => s.background);
69
+ if (running.length === 0)
70
+ return this.stopTicker();
71
+ if (this.ticker !== undefined)
72
+ return;
73
+ this.ticker = setInterval(() => {
74
+ const live = this.deps.subagents.running().filter((s) => s.background);
75
+ if (live.length === 0)
76
+ return this.stopTicker();
77
+ for (const state of live)
78
+ this.deps.tools().get(state.parentToolCallId)?.refresh();
79
+ this.deps.render();
80
+ }, 1000);
81
+ this.ticker.unref?.();
82
+ }
83
+ stopTicker() {
84
+ if (this.ticker !== undefined)
85
+ clearInterval(this.ticker);
86
+ this.ticker = undefined;
87
+ }
88
+ routeNotices(session) {
89
+ this.detach();
90
+ const holder = session.options;
91
+ if (holder === undefined)
92
+ return;
93
+ const original = holder.log;
94
+ const routed = (level, message) => {
95
+ const match = TASK_NOTICE.exec(message);
96
+ if (match !== null && (level === "info" || level === "warn")) {
97
+ const taskId = match[1];
98
+ const agent = taskRegistryView(session.state.sessionId)?.get(taskId)?.agent ?? "task";
99
+ this.deps.notice(level === "warn" ? "warn" : "info", `[${agent} · ${taskId}] ${match[2]}`);
100
+ return;
101
+ }
102
+ original?.call(holder, level, message);
103
+ };
104
+ holder.log = routed;
105
+ this.restoreLog = () => {
106
+ if (holder.log !== routed)
107
+ return;
108
+ if (original === undefined)
109
+ delete holder.log;
110
+ else
111
+ holder.log = original;
112
+ };
113
+ }
114
+ /** 返回 true:事件已处理(调用方只需重画)。 */
115
+ onEvent(event) {
116
+ switch (event.type) {
117
+ case "subagent_start":
118
+ case "subagent_update":
119
+ case "subagent_end": {
120
+ const state = this.deps.subagents.onEvent(event);
121
+ if (state === undefined)
122
+ return true;
123
+ this.deps.tools().get(state.parentToolCallId)?.refresh();
124
+ // 后台任务:完成时 <task-notification> 那一行就是提示;失败 / 停止另给一行
125
+ if (event.type === "subagent_end" && state.background && state.status !== "completed")
126
+ this.deps.notice("warn", backgroundEndText(state));
127
+ this.followBackground();
128
+ return true;
129
+ }
130
+ case "limit_reached":
131
+ this.deps.notice("warn", limitReachedText(event));
132
+ return true;
133
+ case "model_fallback":
134
+ this.deps.notice("warn", modelFallbackText(event));
135
+ return false;
136
+ case "background_job":
137
+ this.deps.notice("info", backgroundJobText(event));
138
+ return true;
139
+ default:
140
+ return false;
141
+ }
142
+ }
143
+ /** 交互界面自己处理的第五波命令;不是返回 false。 */
144
+ async command(name, args) {
145
+ if (args !== "")
146
+ return false;
147
+ switch (name) {
148
+ case "plan":
149
+ await this.showPlan();
150
+ return true;
151
+ case "tasks":
152
+ await this.tasks();
153
+ return true;
154
+ case "agents":
155
+ this.deps.panel(agentsPanel(this.deps.session().state.sessionId, this.deps.theme));
156
+ return true;
157
+ case "paste":
158
+ await this.paste();
159
+ return true;
160
+ default:
161
+ return false;
162
+ }
163
+ }
164
+ async showPlan() {
165
+ const session = this.deps.session();
166
+ const controller = planController(session);
167
+ if (controller === undefined) {
168
+ this.deps.notice("info", "当前会话没有 Plan 能力");
169
+ return;
170
+ }
171
+ this.deps.panel(planPanel(controller, session, this.deps.theme, this.deps.displayPath));
172
+ const pending = controller.pending();
173
+ if (pending !== undefined && !session.state.isStreaming)
174
+ await this.plan.ask(session, pending);
175
+ }
176
+ async tasks() {
177
+ const sessionId = this.deps.session().state.sessionId;
178
+ const tasks = listTasks(sessionId);
179
+ if (tasks.length === 0) {
180
+ this.deps.notice("info", "还没有子 Agent 任务");
181
+ return;
182
+ }
183
+ const now = this.deps.now();
184
+ const picked = await this.deps.pick({
185
+ title: "子 Agent 任务",
186
+ items: [...tasks]
187
+ .reverse()
188
+ .map((task) => ({ value: task.taskId, label: taskLine(task, now) })),
189
+ footer: "↑↓ 选择 · Enter 查看 · Esc 取消",
190
+ });
191
+ if (picked === undefined)
192
+ return;
193
+ const task = taskRegistryView(sessionId)?.get(picked.value);
194
+ if (task === undefined)
195
+ return;
196
+ if (task.status === "running") {
197
+ const action = await this.deps.pick({
198
+ title: `任务 ${task.taskId}`,
199
+ items: [
200
+ { value: "output", label: "查看已有输出" },
201
+ { value: "stop", label: "停止任务" },
202
+ ],
203
+ numberKeys: true,
204
+ });
205
+ if (action === undefined)
206
+ return;
207
+ if (action.value === "stop") {
208
+ await stopTask(sessionId, task.taskId);
209
+ this.deps.notice("info", `已停止 ${task.taskId}`);
210
+ return;
211
+ }
212
+ }
213
+ const fresh = taskRegistryView(sessionId)?.get(task.taskId) ?? task;
214
+ const output = taskOutput(sessionId, task.taskId);
215
+ const { theme, displayPath } = this.deps;
216
+ this.deps.panel(taskOutputPanel(fresh, output, theme, this.deps.now(), displayPath));
217
+ }
218
+ /** `Ctrl+V` / `/paste`:光标处插入 `@<路径>`。 */
219
+ async paste() {
220
+ this.deps.hint("读取剪贴板…");
221
+ const pasted = await pasteImage(this.deps.dataDir, this.deps.clipboard);
222
+ if (!pasted.ok) {
223
+ this.deps.hint("");
224
+ this.deps.notice("info", pasted.message);
225
+ return;
226
+ }
227
+ const editor = this.deps.editor;
228
+ const before = editor.getText();
229
+ const lead = before === "" || /\s$/.test(before) ? "" : " ";
230
+ editor.insertText(`${lead}${pasted.ref} `);
231
+ this.deps.hint("已插入剪贴板图片");
232
+ this.deps.render();
233
+ }
234
+ }
@@ -1,7 +1,11 @@
1
1
  /**
2
2
  * 审批对话框(设计 §12.6):模式层的 UI `ApprovalBroker`,底部覆盖层。[B7]
3
3
  *
4
- * - 标题行:`[task]`(`request.context.depth > 0`,子 Agent 发起)+ 工具名 + 原因标签;
4
+ * - 标题行:来源标注 + 工具名 + 原因标签。来源(W5-U):外部 Agent 的请求(`context.origin`)标
5
+ * `[claude · 会话 abc12345]`,正文取 `origin.toolCall` 的标题 / 种类 / 路径;task 子 Agent(`depth > 0`)标
6
+ * `[task:<agent>]`(类型名经 `ApprovalHost.taskAgent` 查注册表,查不到为 `[task]`);外部 Agent 首次运行确认
7
+ * (`toolName: "task"` 且输入带 `note`)标题为「首次运行外部 Agent」,正文是说明与模式;选项始终是
8
+ * 允许 / 本会话允许 / 拒绝三项(外部 Agent 的 `reject_always` 不由这里选);
5
9
  * - 输入:bash 显示命令全文,write 显示路径与行数,edit 显示路径与每处修改的 −/+ 摘要,其它显示
6
10
  * 一行摘要;`v` 展开 / 收起完整输入(JSON);
7
11
  * - 执行前预览(W3-B9a-2,`request.preview`):输入摘要之后列出会碰到的路径与规模,danger 红、
@@ -21,6 +25,10 @@ import type { ApprovalBroker, ApprovalDecision, ApprovalRequest } from "../../pe
21
25
  import { type Component, type Keybindings, type OverlayHandle, type Theme } from "../../tui.js";
22
26
  export interface ApprovalHost {
23
27
  theme: Theme;
28
+ /** task 子 Agent 的类型名(`[task:<agent>]`);查不到返回 undefined。 */
29
+ taskAgent?(taskId: string): string | undefined;
30
+ /** `task` 调用的 agent 是外部 Agent 时返回 runner(见 approval-merge.ts)。 */
31
+ externalRunner?(agent: string): string | undefined;
24
32
  keybindings?: Keybindings;
25
33
  /** 路径显示的基准。 */
26
34
  cwd?: string;
@@ -39,9 +47,18 @@ export interface DescribeOptions {
39
47
  expanded?: boolean;
40
48
  /** 紧凑(窄屏):首行只放工具名(原因已在标题上)。 */
41
49
  compact?: boolean;
50
+ /** task 子 Agent 的类型名(来源标注)。 */
51
+ taskAgent?: TaskAgentLookup;
52
+ /** `task` 调用的 agent 是外部 Agent 时返回 runner(正文写明首次运行一并确认)。 */
53
+ externalRunner?(agent: string): string | undefined;
42
54
  }
43
- /** 原因 → 标题文字(子 Agent 发起加 `[task] `)。 */
44
- export declare function approvalTitle(request: ApprovalRequest): string;
55
+ export type TaskAgentLookup = (taskId: string) => string | undefined;
56
+ /** 外部 Agent 首次运行确认(`requestApproval` 发来的 `task` 请求,输入带 `note`)。 */
57
+ export declare function isFirstRunRequest(request: ApprovalRequest): boolean;
58
+ /** 来源标注:`[claude · 会话 abc12345]` / `[task:explore]` / `[task]`;主会话发起为 undefined。 */
59
+ export declare function sourceLabel(request: ApprovalRequest, taskAgent?: TaskAgentLookup): string | undefined;
60
+ /** 原因 → 标题文字(带来源标注)。 */
61
+ export declare function approvalTitle(request: ApprovalRequest, taskAgent?: TaskAgentLookup): string;
45
62
  /** 对话框正文(不含选项与按键行)。 */
46
63
  export declare function describeRequest(request: ApprovalRequest, theme: Theme, options?: DescribeOptions): string[];
47
64
  interface ApprovalOption {
@@ -60,5 +77,5 @@ export declare class ApprovalDialogBroker implements ApprovalBroker {
60
77
  ask(request: ApprovalRequest, signal: AbortSignal): Promise<ApprovalDecision | undefined>;
61
78
  }
62
79
  /** 结果的一行说明(消息区)。 */
63
- export declare function approvalOutcomeText(request: ApprovalRequest, outcome: ApprovalDecision | "cancelled"): string;
80
+ export declare function approvalOutcomeText(request: ApprovalRequest, outcome: ApprovalDecision | "cancelled", taskAgent?: TaskAgentLookup): string;
64
81
  export {};
@@ -1,7 +1,11 @@
1
1
  /**
2
2
  * 审批对话框(设计 §12.6):模式层的 UI `ApprovalBroker`,底部覆盖层。[B7]
3
3
  *
4
- * - 标题行:`[task]`(`request.context.depth > 0`,子 Agent 发起)+ 工具名 + 原因标签;
4
+ * - 标题行:来源标注 + 工具名 + 原因标签。来源(W5-U):外部 Agent 的请求(`context.origin`)标
5
+ * `[claude · 会话 abc12345]`,正文取 `origin.toolCall` 的标题 / 种类 / 路径;task 子 Agent(`depth > 0`)标
6
+ * `[task:<agent>]`(类型名经 `ApprovalHost.taskAgent` 查注册表,查不到为 `[task]`);外部 Agent 首次运行确认
7
+ * (`toolName: "task"` 且输入带 `note`)标题为「首次运行外部 Agent」,正文是说明与模式;选项始终是
8
+ * 允许 / 本会话允许 / 拒绝三项(外部 Agent 的 `reject_always` 不由这里选);
5
9
  * - 输入:bash 显示命令全文,write 显示路径与行数,edit 显示路径与每处修改的 −/+ 摘要,其它显示
6
10
  * 一行摘要;`v` 展开 / 收起完整输入(JSON);
7
11
  * - 执行前预览(W3-B9a-2,`request.preview`):输入摘要之后列出会碰到的路径与规模,danger 红、
@@ -20,6 +24,7 @@
20
24
  import { AUTO_LAYER_TEXT, isPermissionMode, permissionModeLabel } from "../../permissions/modes.js";
21
25
  import { previewDisplayLines } from "../../permissions/preview.js";
22
26
  import { Box, defaultKeybindings, matchesKey, padToWidth, truncateToWidth, visibleWidth, wrapTextWithAnsi, } from "../../tui.js";
27
+ import { displayPath, flat } from "./tool-summary.js";
23
28
  import { toolSummary } from "./tool-view.js";
24
29
  const COMMAND_LINES = 8;
25
30
  const EDIT_LINES = 3;
@@ -33,30 +38,83 @@ function lines(text, max) {
33
38
  function record(input) {
34
39
  return typeof input === "object" && input !== null ? input : {};
35
40
  }
36
- /** 原因 → 标题文字(子 Agent 发起加 `[task] `)。 */
37
- export function approvalTitle(request) {
38
- const task = (request.context?.depth ?? 0) > 0 ? "[task] " : "";
39
- const reason = request.reason === "dangerous"
40
- ? "危险命令"
41
- : request.reason === "hook"
42
- ? "Hook 要求确认"
43
- : "需要确认";
44
- return task + reason;
41
+ /** 外部 Agent 首次运行确认(`requestApproval` 发来的 `task` 请求,输入带 `note`)。 */
42
+ export function isFirstRunRequest(request) {
43
+ return request.toolName === "task" && typeof record(request.input)["note"] === "string";
44
+ }
45
+ /** 来源标注:`[claude · 会话 abc12345]` / `[task:explore]` / `[task]`;主会话发起为 undefined。 */
46
+ export function sourceLabel(request, taskAgent) {
47
+ const context = request.context;
48
+ const origin = context?.origin;
49
+ if (origin !== undefined)
50
+ return `[${origin.agent} · 会话 ${origin.sessionId.slice(0, 8)}]`;
51
+ if ((context?.depth ?? 0) <= 0)
52
+ return undefined;
53
+ const taskId = context?.taskId;
54
+ const agent = taskId === undefined ? undefined : taskAgent?.(taskId);
55
+ return agent === undefined ? "[task]" : `[task:${agent}]`;
56
+ }
57
+ /** 原因 → 标题文字(带来源标注)。 */
58
+ export function approvalTitle(request, taskAgent) {
59
+ const label = sourceLabel(request, taskAgent);
60
+ const reason = isFirstRunRequest(request)
61
+ ? "首次运行外部 Agent"
62
+ : request.reason === "dangerous"
63
+ ? "危险命令"
64
+ : request.reason === "hook"
65
+ ? "Hook 要求确认"
66
+ : "需要确认";
67
+ return label === undefined ? reason : `${label} ${reason}`;
68
+ }
69
+ /** 外部 Agent 请求的正文:标题 + 种类、涉及路径、输入摘要。 */
70
+ function originLines(origin, theme, cwd) {
71
+ const out = [];
72
+ for (const path of (origin.toolCall.locations ?? []).slice(0, 3))
73
+ out.push(theme.fg("muted", displayPath(path, cwd)));
74
+ const hidden = (origin.toolCall.locations?.length ?? 0) - 3;
75
+ if (hidden > 0)
76
+ out.push(theme.fg("dim", `… 另 ${hidden} 个路径`));
77
+ const summary = origin.toolCall.inputSummary;
78
+ if (summary !== undefined && summary.trim() !== "") {
79
+ const { shown, hidden: more } = lines(summary, COMMAND_LINES);
80
+ out.push(...shown.map((l) => theme.fg("code", l)));
81
+ if (more > 0)
82
+ out.push(theme.fg("dim", `… 另 ${more} 行`));
83
+ }
84
+ return out;
45
85
  }
46
86
  /** 对话框正文(不含选项与按键行)。 */
47
87
  export function describeRequest(request, theme, options = {}) {
48
88
  const out = [];
49
- const task = (request.context?.depth ?? 0) > 0 ? theme.fg("warning", "[task] ") : "";
89
+ const label = sourceLabel(request, options.taskAgent);
90
+ const task = label === undefined ? "" : theme.fg("warning", `${label} `);
91
+ const origin = request.context?.origin;
92
+ const firstRun = isFirstRunRequest(request);
50
93
  const tag = request.reason === "dangerous"
51
94
  ? theme.fg("error", "危险命令")
52
95
  : request.reason === "hook"
53
96
  ? theme.fg("warning", "Hook 要求确认")
54
- : theme.fg("dim", "需要确认");
55
- const name = theme.bold(theme.fg("tool", request.toolName));
56
- out.push(options.compact === true ? `${task}${name}` : `${task}${name} ${tag}`);
97
+ : origin !== undefined
98
+ ? theme.fg("dim", origin.toolCall.kind)
99
+ : theme.fg("dim", firstRun ? "首次运行" : "需要确认");
57
100
  const input = record(request.input);
101
+ const name = origin !== undefined
102
+ ? theme.bold(theme.fg("tool", flat(origin.toolCall.title)))
103
+ : firstRun
104
+ ? theme.bold(theme.fg("tool", `task ${String(input["agent"] ?? "")}`.trim()))
105
+ : theme.bold(theme.fg("tool", request.toolName));
106
+ out.push(options.compact === true ? `${task}${name}` : `${task}${name} ${tag}`);
58
107
  const more = (n) => theme.fg("dim", `… 另 ${n} 行(v 查看)`);
59
- if (options.expanded === true) {
108
+ if (origin !== undefined && options.expanded !== true) {
109
+ out.push(...originLines(origin, theme, options.cwd));
110
+ }
111
+ else if (firstRun && options.expanded !== true) {
112
+ out.push(String(input["note"]));
113
+ const mode = input["mode"];
114
+ if (typeof mode === "string")
115
+ out.push(theme.fg("dim", `模式 ${isPermissionMode(mode) ? permissionModeLabel(mode) : mode}`));
116
+ }
117
+ else if (options.expanded === true) {
60
118
  const json = JSON.stringify(request.input, null, 2) ?? String(request.input);
61
119
  const { shown, hidden } = lines(json, FULL_INPUT_LINES);
62
120
  out.push(...shown.map((l) => theme.fg("code", l)));
@@ -96,12 +154,26 @@ export function describeRequest(request, theme, options = {}) {
96
154
  const summary = toolSummary(request.toolName, input, options.cwd);
97
155
  if (summary !== "")
98
156
  out.push(summary);
157
+ const agent = input["agent"];
158
+ const runner = request.toolName === "task" && typeof agent === "string"
159
+ ? options.externalRunner?.(agent)
160
+ : undefined;
161
+ // 与首次运行确认合并(approval-merge.ts):允许即同意本会话以你的登录运行该 CLI
162
+ if (runner !== undefined)
163
+ out.push(theme.fg("warning", `外部 Agent ${agent}:以你在 ${runner} CLI 的登录运行(含本会话首次运行确认)`));
99
164
  }
100
165
  const severity = request.preview?.severity;
101
166
  const color = severity === "danger" ? "error" : severity === "warn" ? "warning" : "dim";
102
167
  out.push(...previewDisplayLines(request.preview).map((l) => theme.fg(color, l)));
103
168
  const auto = request.autoDecision;
104
- if (request.reason === "hook") {
169
+ if (origin !== undefined || firstRun) {
170
+ // 外部 Agent 的请求由它自己的策略决定要问;首次运行确认的说明已在正文
171
+ if (origin !== undefined)
172
+ out.push(theme.fg("dim", `${origin.agent} 请求确认`));
173
+ else
174
+ out.push(theme.fg("dim", "本会话首次以你的登录运行该 CLI"));
175
+ }
176
+ else if (request.reason === "hook") {
105
177
  out.push(theme.fg("warning", `Hook:${request.hookReason ?? "(无说明)"}`));
106
178
  }
107
179
  else if (request.reason === "dangerous") {
@@ -174,6 +246,10 @@ class ApprovalDialog {
174
246
  };
175
247
  if (this.host.cwd !== undefined)
176
248
  options.cwd = this.host.cwd;
249
+ if (this.host.taskAgent !== undefined)
250
+ options.taskAgent = this.host.taskAgent;
251
+ if (this.host.externalRunner !== undefined)
252
+ options.externalRunner = this.host.externalRunner;
177
253
  const body = describeRequest(this.request, theme, options);
178
254
  const lines = [];
179
255
  // 输入摘要(标题 + 命令)与预览 / 原因之间空一行
@@ -252,7 +328,7 @@ export class ApprovalDialogBroker {
252
328
  this.host.onOpen?.();
253
329
  const severity = request.preview?.severity;
254
330
  handle = this.host.showOverlay(new Box(dialog, {
255
- title: approvalTitle(request),
331
+ title: approvalTitle(request, this.host.taskAgent),
256
332
  theme: this.host.theme,
257
333
  ...(severity === "danger"
258
334
  ? { borderColor: "error" }
@@ -265,9 +341,11 @@ export class ApprovalDialogBroker {
265
341
  }
266
342
  }
267
343
  /** 结果的一行说明(消息区)。 */
268
- export function approvalOutcomeText(request, outcome) {
269
- const task = (request.context?.depth ?? 0) > 0 ? "[task] " : "";
270
- const what = `${task}${request.toolName}`;
344
+ export function approvalOutcomeText(request, outcome, taskAgent) {
345
+ const label = sourceLabel(request, taskAgent);
346
+ const origin = request.context?.origin;
347
+ const tool = origin !== undefined ? flat(origin.toolCall.title, 60) : request.toolName;
348
+ const what = `${label === undefined ? "" : `${label} `}${tool}`;
271
349
  switch (outcome) {
272
350
  case "allow":
273
351
  return `已允许 ${what}`;
@@ -0,0 +1,41 @@
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 type { ApprovalBroker, ApprovalDecision, ApprovalRequest } from "../../permissions/types.js";
14
+ export declare const MERGE_WINDOW_MS = 60000;
15
+ export interface FirstRunMergeDeps {
16
+ sessionId(): string;
17
+ now?(): number;
18
+ /** 类型名 → runner(`ama` / `claude` / …);缺省查会话的类型表。 */
19
+ runnerOf?(agent: string): string | undefined;
20
+ /** 任务 id → 类型名与建立时刻;缺省查注册表。 */
21
+ taskOf?(taskId: string): {
22
+ agent: string;
23
+ startedAt: number;
24
+ } | undefined;
25
+ windowMs?: number;
26
+ }
27
+ export declare class FirstRunMerge {
28
+ private readonly deps;
29
+ private pending;
30
+ constructor(deps: FirstRunMergeDeps);
31
+ private now;
32
+ /** `task` 调用的 agent 是外部 Agent 时返回它的 runner。 */
33
+ externalRunner(agent: string): string | undefined;
34
+ private taskOf;
35
+ /** 请求到来:命中合并返回 `allow`(不弹框);否则返回 undefined,并作废记录。 */
36
+ intercept(request: ApprovalRequest): ApprovalDecision | undefined;
37
+ /** 用户答复之后:允许了外部 Agent 的 task 调用就记下。 */
38
+ record(request: ApprovalRequest, decision: ApprovalDecision | undefined): void;
39
+ }
40
+ /** 把合并规则套在界面 broker 外面;自动答复时调 `onMerged`(消息区一行说明)。 */
41
+ export declare function mergingBroker(inner: ApprovalBroker, merge: FirstRunMerge, onMerged?: (request: ApprovalRequest) => void): ApprovalBroker;