@armadra/agent 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (422) hide show
  1. package/CHANGELOG.md +174 -1
  2. package/README.md +201 -82
  3. package/THIRD_PARTY_NOTICES.md +34 -0
  4. package/dist/acp.d.ts +18 -0
  5. package/dist/acp.js +19 -0
  6. package/dist/agent/limits.d.ts +38 -0
  7. package/dist/agent/limits.js +127 -0
  8. package/dist/agent/loop-guard.d.ts +38 -0
  9. package/dist/agent/loop-guard.js +95 -0
  10. package/dist/agent/loop.d.ts +4 -0
  11. package/dist/agent/loop.js +7 -0
  12. package/dist/agent/prompt-rules.d.ts +12 -0
  13. package/dist/agent/prompt-rules.js +18 -0
  14. package/dist/agent/reminders.d.ts +59 -0
  15. package/dist/agent/reminders.js +327 -0
  16. package/dist/agent/session-cache.d.ts +5 -0
  17. package/dist/agent/session-cache.js +17 -1
  18. package/dist/agent/session-classifier.d.ts +1 -1
  19. package/dist/agent/session-classifier.js +3 -1
  20. package/dist/agent/session-compaction.d.ts +35 -6
  21. package/dist/agent/session-compaction.js +157 -31
  22. package/dist/agent/session-core.d.ts +27 -1
  23. package/dist/agent/session-extensions.d.ts +61 -0
  24. package/dist/agent/session-extensions.js +129 -0
  25. package/dist/agent/session-images.d.ts +23 -0
  26. package/dist/agent/session-images.js +97 -0
  27. package/dist/agent/session-plan.d.ts +21 -0
  28. package/dist/agent/session-plan.js +524 -0
  29. package/dist/agent/session-rewind.d.ts +81 -0
  30. package/dist/agent/session-rewind.js +354 -0
  31. package/dist/agent/session-run.d.ts +28 -1
  32. package/dist/agent/session-run.js +157 -6
  33. package/dist/agent/session-settings.d.ts +49 -0
  34. package/dist/agent/session-settings.js +95 -0
  35. package/dist/agent/session-subagent.d.ts +36 -24
  36. package/dist/agent/session-subagent.js +259 -129
  37. package/dist/agent/session-sync.d.ts +10 -3
  38. package/dist/agent/session-sync.js +37 -8
  39. package/dist/agent/session-telemetry.d.ts +39 -0
  40. package/dist/agent/session-telemetry.js +231 -0
  41. package/dist/agent/session-tools.js +40 -1
  42. package/dist/agent/session.d.ts +30 -29
  43. package/dist/agent/session.js +79 -123
  44. package/dist/agent/subagent-registry.d.ts +91 -0
  45. package/dist/agent/subagent-registry.js +494 -0
  46. package/dist/agent/system-prompt.d.ts +6 -3
  47. package/dist/agent/system-prompt.js +7 -2
  48. package/dist/agent/tool-runner.d.ts +8 -1
  49. package/dist/agent/tool-runner.js +57 -4
  50. package/dist/agent/types-w5.d.ts +192 -0
  51. package/dist/agent/types-w5.js +10 -0
  52. package/dist/agent/types.d.ts +47 -4
  53. package/dist/agent/types.js +2 -0
  54. package/dist/agent/worktree.d.ts +36 -0
  55. package/dist/agent/worktree.js +86 -0
  56. package/dist/agents/builtin.d.ts +14 -0
  57. package/dist/agents/builtin.js +39 -0
  58. package/dist/agents/catalog.d.ts +43 -0
  59. package/dist/agents/catalog.js +82 -0
  60. package/dist/agents/discover.d.ts +35 -0
  61. package/dist/agents/discover.js +83 -0
  62. package/dist/agents/external.d.ts +82 -0
  63. package/dist/agents/external.js +326 -0
  64. package/dist/agents/parse.d.ts +18 -0
  65. package/dist/agents/parse.js +116 -0
  66. package/dist/agents/result.d.ts +32 -0
  67. package/dist/agents/result.js +67 -0
  68. package/dist/agents/task-control.d.ts +17 -0
  69. package/dist/agents/task-control.js +17 -0
  70. package/dist/agents/task-record.d.ts +78 -0
  71. package/dist/agents/task-record.js +141 -0
  72. package/dist/agents/types.d.ts +46 -0
  73. package/dist/agents/types.js +8 -0
  74. package/dist/ai/apis/anthropic-compat.d.ts +36 -0
  75. package/dist/ai/apis/anthropic-compat.js +92 -0
  76. package/dist/ai/apis/anthropic-request.d.ts +7 -6
  77. package/dist/ai/apis/anthropic-request.js +10 -28
  78. package/dist/ai/apis/cache-params.d.ts +11 -2
  79. package/dist/ai/apis/cache-params.js +25 -6
  80. package/dist/ai/apis/openai-compat.d.ts +4 -1
  81. package/dist/ai/apis/openai-compat.js +8 -1
  82. package/dist/ai/image-limits.d.ts +45 -0
  83. package/dist/ai/image-limits.js +68 -0
  84. package/dist/ai/providers/builtin.d.ts +18 -6
  85. package/dist/ai/providers/builtin.js +135 -6
  86. package/dist/ai/providers/catalog-data.js +17 -13
  87. package/dist/ai/providers/catalog.d.ts +60 -7
  88. package/dist/ai/providers/catalog.js +197 -38
  89. package/dist/ai/providers/channels.d.ts +33 -3
  90. package/dist/ai/providers/channels.js +108 -7
  91. package/dist/ai/providers/enrich.d.ts +6 -3
  92. package/dist/ai/providers/enrich.js +19 -4
  93. package/dist/ai/providers/models-dev-cache.d.ts +28 -24
  94. package/dist/ai/providers/models-dev-cache.js +92 -78
  95. package/dist/ai/providers/models-dev-data.d.ts +7 -0
  96. package/dist/ai/providers/models-dev-data.js +32 -0
  97. package/dist/ai/providers/models-dev-snapshot.d.ts +50 -0
  98. package/dist/ai/providers/models-dev-snapshot.js +137 -0
  99. package/dist/ai/providers/models-dev.d.ts +51 -10
  100. package/dist/ai/providers/models-dev.js +132 -39
  101. package/dist/ai/providers/registry.d.ts +20 -10
  102. package/dist/ai/providers/registry.js +82 -43
  103. package/dist/ai/types.d.ts +28 -2
  104. package/dist/ai/types.js +5 -0
  105. package/dist/bundle/ama.cjs +36203 -20499
  106. package/dist/checkpoints/backend.d.ts +61 -0
  107. package/dist/checkpoints/backend.js +135 -0
  108. package/dist/checkpoints/blobs.d.ts +60 -0
  109. package/dist/checkpoints/blobs.js +203 -0
  110. package/dist/checkpoints/gc.d.ts +47 -0
  111. package/dist/checkpoints/gc.js +144 -0
  112. package/dist/checkpoints/git-head.d.ts +15 -0
  113. package/dist/checkpoints/git-head.js +89 -0
  114. package/dist/checkpoints/index.d.ts +17 -0
  115. package/dist/checkpoints/index.js +17 -0
  116. package/dist/checkpoints/replay.d.ts +48 -0
  117. package/dist/checkpoints/replay.js +154 -0
  118. package/dist/checkpoints/restore.d.ts +86 -0
  119. package/dist/checkpoints/restore.js +263 -0
  120. package/dist/checkpoints/settings.d.ts +14 -0
  121. package/dist/checkpoints/settings.js +23 -0
  122. package/dist/checkpoints/shadow-git.d.ts +108 -0
  123. package/dist/checkpoints/shadow-git.js +491 -0
  124. package/dist/checkpoints/shadow-restore.d.ts +25 -0
  125. package/dist/checkpoints/shadow-restore.js +128 -0
  126. package/dist/checkpoints/tracker.d.ts +64 -0
  127. package/dist/checkpoints/tracker.js +192 -0
  128. package/dist/checkpoints/types.d.ts +99 -0
  129. package/dist/checkpoints/types.js +12 -0
  130. package/dist/cli/args.d.ts +8 -4
  131. package/dist/cli/args.js +17 -97
  132. package/dist/cli/bootstrap.js +13 -1
  133. package/dist/cli/codemode-notice.d.ts +3 -2
  134. package/dist/cli/codemode-notice.js +3 -2
  135. package/dist/cli/compose-agents.d.ts +27 -0
  136. package/dist/cli/compose-agents.js +111 -0
  137. package/dist/cli/compose-extensions.d.ts +29 -0
  138. package/dist/cli/compose-extensions.js +44 -0
  139. package/dist/cli/compose-session.d.ts +3 -0
  140. package/dist/cli/compose-session.js +47 -4
  141. package/dist/cli/compose.d.ts +3 -0
  142. package/dist/cli/compose.js +33 -6
  143. package/dist/cli/deps.d.ts +6 -0
  144. package/dist/cli/exit-codes.d.ts +10 -0
  145. package/dist/cli/exit-codes.js +12 -0
  146. package/dist/cli/help-text.d.ts +4 -0
  147. package/dist/cli/help-text.js +102 -0
  148. package/dist/cli/startup-screen.js +5 -3
  149. package/dist/cli/startup-steps.js +7 -1
  150. package/dist/cli/subcommands/config.d.ts +4 -1
  151. package/dist/cli/subcommands/config.js +17 -6
  152. package/dist/cli/subcommands/doctor.js +21 -0
  153. package/dist/cli/subcommands/models-discover.js +5 -6
  154. package/dist/cli/subcommands/models.d.ts +2 -0
  155. package/dist/cli/subcommands/models.js +33 -14
  156. package/dist/cli/subcommands/providers.js +5 -6
  157. package/dist/cli/subcommands/sessions.d.ts +3 -1
  158. package/dist/cli/subcommands/sessions.js +36 -2
  159. package/dist/codemode/capability.d.ts +24 -7
  160. package/dist/codemode/capability.js +38 -10
  161. package/dist/codemode/host-side.d.ts +14 -0
  162. package/dist/codemode/host-side.js +24 -1
  163. package/dist/codemode/tool.d.ts +1 -1
  164. package/dist/codemode/tool.js +6 -4
  165. package/dist/compaction/breaker.d.ts +25 -9
  166. package/dist/compaction/breaker.js +47 -17
  167. package/dist/compaction/estimate.d.ts +11 -3
  168. package/dist/compaction/estimate.js +64 -22
  169. package/dist/compaction/image-budget.d.ts +45 -0
  170. package/dist/compaction/image-budget.js +115 -0
  171. package/dist/compaction/post-compact.d.ts +38 -0
  172. package/dist/compaction/post-compact.js +152 -0
  173. package/dist/compaction/protect.d.ts +27 -0
  174. package/dist/compaction/protect.js +56 -0
  175. package/dist/compaction/prune-tier.d.ts +58 -12
  176. package/dist/compaction/prune-tier.js +120 -54
  177. package/dist/compaction/summarize-tier.d.ts +23 -4
  178. package/dist/compaction/summarize-tier.js +100 -29
  179. package/dist/config/checker.d.ts +28 -0
  180. package/dist/config/checker.js +97 -0
  181. package/dist/config/json-schema.js +63 -2
  182. package/dist/config/key-docs.js +72 -3
  183. package/dist/config/merge.d.ts +5 -3
  184. package/dist/config/merge.js +81 -7
  185. package/dist/config/profile.d.ts +2 -0
  186. package/dist/config/profile.js +2 -0
  187. package/dist/config/schema-w5.d.ts +18 -0
  188. package/dist/config/schema-w5.js +109 -0
  189. package/dist/config/schema.d.ts +5 -9
  190. package/dist/config/schema.js +68 -114
  191. package/dist/config/types-w5.d.ts +118 -0
  192. package/dist/config/types-w5.js +20 -0
  193. package/dist/config/types.d.ts +50 -1
  194. package/dist/config/types.js +12 -0
  195. package/dist/drivers/acp/client.d.ts +57 -0
  196. package/dist/drivers/acp/client.js +160 -0
  197. package/dist/drivers/acp/driver.d.ts +31 -0
  198. package/dist/drivers/acp/driver.js +274 -0
  199. package/dist/drivers/acp/testing/fake-agent-main.d.ts +5 -0
  200. package/dist/drivers/acp/testing/fake-agent-main.js +7 -0
  201. package/dist/drivers/acp/testing/fake-agent.d.ts +27 -0
  202. package/dist/drivers/acp/testing/fake-agent.js +211 -0
  203. package/dist/drivers/acp/types.d.ts +285 -0
  204. package/dist/drivers/acp/types.js +36 -0
  205. package/dist/drivers/agents.d.ts +58 -0
  206. package/dist/drivers/agents.js +142 -0
  207. package/dist/drivers/base.d.ts +35 -0
  208. package/dist/drivers/base.js +59 -0
  209. package/dist/drivers/catalog.d.ts +46 -0
  210. package/dist/drivers/catalog.js +167 -0
  211. package/dist/drivers/env.d.ts +20 -0
  212. package/dist/drivers/env.js +46 -0
  213. package/dist/drivers/host-runners.d.ts +18 -0
  214. package/dist/drivers/host-runners.js +45 -0
  215. package/dist/drivers/jsonrpc.d.ts +58 -0
  216. package/dist/drivers/jsonrpc.js +180 -0
  217. package/dist/drivers/native/claude-normalize.d.ts +46 -0
  218. package/dist/drivers/native/claude-normalize.js +106 -0
  219. package/dist/drivers/native/claude-stream.d.ts +29 -0
  220. package/dist/drivers/native/claude-stream.js +456 -0
  221. package/dist/drivers/native/codex-app-server.d.ts +26 -0
  222. package/dist/drivers/native/codex-app-server.js +407 -0
  223. package/dist/drivers/native/codex-normalize.d.ts +38 -0
  224. package/dist/drivers/native/codex-normalize.js +124 -0
  225. package/dist/drivers/native/oneshot.d.ts +28 -0
  226. package/dist/drivers/native/oneshot.js +312 -0
  227. package/dist/drivers/permissions.d.ts +40 -0
  228. package/dist/drivers/permissions.js +92 -0
  229. package/dist/drivers/pids.d.ts +32 -0
  230. package/dist/drivers/pids.js +93 -0
  231. package/dist/drivers/pool.d.ts +28 -0
  232. package/dist/drivers/pool.js +96 -0
  233. package/dist/drivers/probe.d.ts +47 -0
  234. package/dist/drivers/probe.js +172 -0
  235. package/dist/drivers/process.d.ts +48 -0
  236. package/dist/drivers/process.js +100 -0
  237. package/dist/drivers/runner.d.ts +61 -0
  238. package/dist/drivers/runner.js +388 -0
  239. package/dist/drivers/store.d.ts +49 -0
  240. package/dist/drivers/store.js +91 -0
  241. package/dist/drivers/turn.d.ts +41 -0
  242. package/dist/drivers/turn.js +99 -0
  243. package/dist/drivers/types.d.ts +122 -0
  244. package/dist/drivers/types.js +11 -0
  245. package/dist/git/info.d.ts +75 -0
  246. package/dist/git/info.js +191 -0
  247. package/dist/hooks/protocol.js +6 -1
  248. package/dist/hooks/types.d.ts +10 -1
  249. package/dist/hooks/types.js +2 -0
  250. package/dist/host/api-impl.d.ts +7 -0
  251. package/dist/host/api-impl.js +15 -0
  252. package/dist/host/types.d.ts +24 -2
  253. package/dist/host/types.js +4 -0
  254. package/dist/index.d.ts +3 -0
  255. package/dist/index.js +1 -0
  256. package/dist/modes/acp/acp-events.d.ts +46 -0
  257. package/dist/modes/acp/acp-events.js +233 -0
  258. package/dist/modes/acp/acp-mode.d.ts +17 -0
  259. package/dist/modes/acp/acp-mode.js +47 -0
  260. package/dist/modes/acp/acp-server.d.ts +79 -0
  261. package/dist/modes/acp/acp-server.js +315 -0
  262. package/dist/modes/commands-core.d.ts +7 -1
  263. package/dist/modes/commands-core.js +55 -0
  264. package/dist/modes/image-input.d.ts +12 -4
  265. package/dist/modes/image-input.js +22 -5
  266. package/dist/modes/interactive/agent-panels.d.ts +14 -0
  267. package/dist/modes/interactive/agent-panels.js +103 -0
  268. package/dist/modes/interactive/agent-ui.d.ts +73 -0
  269. package/dist/modes/interactive/agent-ui.js +234 -0
  270. package/dist/modes/interactive/approval-dialog.d.ts +21 -4
  271. package/dist/modes/interactive/approval-dialog.js +98 -20
  272. package/dist/modes/interactive/approval-merge.d.ts +41 -0
  273. package/dist/modes/interactive/approval-merge.js +89 -0
  274. package/dist/modes/interactive/clipboard-paste.d.ts +18 -0
  275. package/dist/modes/interactive/clipboard-paste.js +28 -0
  276. package/dist/modes/interactive/commands.d.ts +9 -1
  277. package/dist/modes/interactive/commands.js +19 -0
  278. package/dist/modes/interactive/double-esc.d.ts +20 -0
  279. package/dist/modes/interactive/double-esc.js +34 -0
  280. package/dist/modes/interactive/event-notices.d.ts +32 -0
  281. package/dist/modes/interactive/event-notices.js +79 -0
  282. package/dist/modes/interactive/external-editor.d.ts +16 -0
  283. package/dist/modes/interactive/external-editor.js +39 -0
  284. package/dist/modes/interactive/interactive-mode.d.ts +14 -1
  285. package/dist/modes/interactive/interactive-mode.js +134 -136
  286. package/dist/modes/interactive/key-dispatch.d.ts +16 -2
  287. package/dist/modes/interactive/key-dispatch.js +49 -1
  288. package/dist/modes/interactive/line/line-mode.d.ts +2 -1
  289. package/dist/modes/interactive/line/line-mode.js +22 -5
  290. package/dist/modes/interactive/line/line-render.d.ts +2 -1
  291. package/dist/modes/interactive/line/line-render.js +40 -4
  292. package/dist/modes/interactive/message-view.d.ts +1 -1
  293. package/dist/modes/interactive/message-view.js +24 -2
  294. package/dist/modes/interactive/panels.d.ts +18 -1
  295. package/dist/modes/interactive/panels.js +19 -5
  296. package/dist/modes/interactive/plan-command.d.ts +40 -0
  297. package/dist/modes/interactive/plan-command.js +129 -0
  298. package/dist/modes/interactive/plan-dialog.d.ts +98 -0
  299. package/dist/modes/interactive/plan-dialog.js +290 -0
  300. package/dist/modes/interactive/plan-flow.d.ts +34 -0
  301. package/dist/modes/interactive/plan-flow.js +98 -0
  302. package/dist/modes/interactive/rewind-command.d.ts +31 -0
  303. package/dist/modes/interactive/rewind-command.js +106 -0
  304. package/dist/modes/interactive/rewind-flow.d.ts +37 -0
  305. package/dist/modes/interactive/rewind-flow.js +151 -0
  306. package/dist/modes/interactive/rewind-list.d.ts +36 -0
  307. package/dist/modes/interactive/rewind-list.js +123 -0
  308. package/dist/modes/interactive/rewind-panel.d.ts +77 -0
  309. package/dist/modes/interactive/rewind-panel.js +309 -0
  310. package/dist/modes/interactive/rewind-text.d.ts +40 -0
  311. package/dist/modes/interactive/rewind-text.js +139 -0
  312. package/dist/modes/interactive/session-events.d.ts +24 -0
  313. package/dist/modes/interactive/session-events.js +96 -0
  314. package/dist/modes/interactive/status-area.d.ts +73 -0
  315. package/dist/modes/interactive/status-area.js +190 -0
  316. package/dist/modes/interactive/status-bar.d.ts +99 -11
  317. package/dist/modes/interactive/status-bar.js +273 -90
  318. package/dist/modes/interactive/status-line.d.ts +33 -0
  319. package/dist/modes/interactive/status-line.js +113 -0
  320. package/dist/modes/interactive/subagent-view.d.ts +56 -0
  321. package/dist/modes/interactive/subagent-view.js +154 -0
  322. package/dist/modes/interactive/tasks-report.d.ts +27 -0
  323. package/dist/modes/interactive/tasks-report.js +114 -0
  324. package/dist/modes/interactive/tool-view.d.ts +12 -2
  325. package/dist/modes/interactive/tool-view.js +34 -4
  326. package/dist/modes/print/print-mode.d.ts +13 -4
  327. package/dist/modes/print/print-mode.js +56 -12
  328. package/dist/modes/rpc/commands.d.ts +9 -0
  329. package/dist/modes/rpc/commands.js +44 -0
  330. package/dist/modes/rpc/rpc-mode.js +7 -1
  331. package/dist/modes/session-report.d.ts +11 -0
  332. package/dist/modes/session-report.js +48 -1
  333. package/dist/permissions/auto-safe.d.ts +5 -0
  334. package/dist/permissions/auto-safe.js +18 -0
  335. package/dist/permissions/classifier.d.ts +4 -0
  336. package/dist/permissions/classifier.js +2 -0
  337. package/dist/permissions/modes.js +1 -1
  338. package/dist/permissions/pipeline.d.ts +43 -4
  339. package/dist/permissions/pipeline.js +114 -9
  340. package/dist/permissions/readonly-bash.d.ts +30 -0
  341. package/dist/permissions/readonly-bash.js +118 -0
  342. package/dist/permissions/types.d.ts +42 -0
  343. package/dist/plan/compose.d.ts +9 -0
  344. package/dist/plan/compose.js +19 -0
  345. package/dist/plan/controller.d.ts +71 -0
  346. package/dist/plan/controller.js +27 -0
  347. package/dist/plan/done-markers.d.ts +17 -0
  348. package/dist/plan/done-markers.js +51 -0
  349. package/dist/plan/extract.d.ts +29 -0
  350. package/dist/plan/extract.js +160 -0
  351. package/dist/plan/prompts.d.ts +36 -0
  352. package/dist/plan/prompts.js +89 -0
  353. package/dist/plan/store.d.ts +37 -0
  354. package/dist/plan/store.js +90 -0
  355. package/dist/rpc.d.ts +58 -2
  356. package/dist/rpc.js +7 -0
  357. package/dist/sandbox/bash.d.ts +74 -0
  358. package/dist/sandbox/bash.js +126 -0
  359. package/dist/sandbox/detect.d.ts +48 -0
  360. package/dist/sandbox/detect.js +149 -0
  361. package/dist/sandbox/index.d.ts +5 -0
  362. package/dist/sandbox/index.js +5 -0
  363. package/dist/sandbox/profile.d.ts +33 -0
  364. package/dist/sandbox/profile.js +76 -0
  365. package/dist/sandbox/wrap.d.ts +29 -0
  366. package/dist/sandbox/wrap.js +117 -0
  367. package/dist/sdk.d.ts +33 -2
  368. package/dist/sdk.js +24 -2
  369. package/dist/session/types.d.ts +3 -1
  370. package/dist/session/types.js +1 -0
  371. package/dist/skills/builtin.d.ts +31 -0
  372. package/dist/skills/builtin.js +109 -0
  373. package/dist/skills/discover.d.ts +2 -1
  374. package/dist/skills/index-prompt.js +5 -8
  375. package/dist/tools/background-jobs.d.ts +71 -0
  376. package/dist/tools/background-jobs.js +198 -0
  377. package/dist/tools/bash.d.ts +53 -1
  378. package/dist/tools/bash.js +158 -7
  379. package/dist/tools/clipboard-image.d.ts +44 -0
  380. package/dist/tools/clipboard-image.js +148 -0
  381. package/dist/tools/edit.d.ts +2 -0
  382. package/dist/tools/edit.js +18 -3
  383. package/dist/tools/image-file.d.ts +38 -3
  384. package/dist/tools/image-file.js +81 -10
  385. package/dist/tools/image-resize.d.ts +58 -0
  386. package/dist/tools/image-resize.js +97 -0
  387. package/dist/tools/presets.d.ts +7 -2
  388. package/dist/tools/presets.js +11 -4
  389. package/dist/tools/read.d.ts +5 -2
  390. package/dist/tools/read.js +13 -10
  391. package/dist/tools/registry.d.ts +7 -1
  392. package/dist/tools/registry.js +27 -4
  393. package/dist/tools/task-ctl.d.ts +22 -0
  394. package/dist/tools/task-ctl.js +116 -0
  395. package/dist/tools/task.d.ts +22 -10
  396. package/dist/tools/task.js +66 -26
  397. package/dist/tools/todo.d.ts +22 -2
  398. package/dist/tools/todo.js +58 -14
  399. package/dist/tools/truncate.d.ts +15 -0
  400. package/dist/tools/truncate.js +25 -0
  401. package/dist/tools/types.d.ts +113 -0
  402. package/dist/tools/types.js +5 -0
  403. package/dist/tools/write.js +3 -0
  404. package/dist/tui/glyphs.d.ts +4 -0
  405. package/dist/tui/glyphs.js +4 -0
  406. package/dist/tui/keybindings.d.ts +5 -0
  407. package/dist/tui/keybindings.js +5 -0
  408. package/docs/acp.md +78 -0
  409. package/docs/agents.md +250 -0
  410. package/docs/codemode.md +12 -8
  411. package/docs/hooks.md +26 -22
  412. package/docs/permissions.md +77 -13
  413. package/docs/plan.md +107 -0
  414. package/docs/providers.md +228 -52
  415. package/docs/rewind-plan.md +181 -0
  416. package/docs/rpc.md +92 -5
  417. package/docs/sandbox.md +215 -0
  418. package/docs/session-format.md +41 -21
  419. package/docs/sessions.md +41 -1
  420. package/docs/tui-design.md +751 -0
  421. package/docs/tui.md +125 -6
  422. package/package.json +13 -2
@@ -0,0 +1,97 @@
1
+ /**
2
+ * 图片预算扩展(docs/wave5-plan.md §4、D11、D30)。[W5-I]
3
+ *
4
+ * 每次请求前检查图片总量:超预算(或有超单图上限的旧图、> 20 张时的大图)就按
5
+ * `planImageBudget` 把旧图换成占位文本,写成 `context_edit{reason:"image_budget"}` 持久化、
6
+ * 重建上下文,并通知缓存控制器「下一条是重置点」(前缀只在这里变一次,之后稳定)。
7
+ *
8
+ * 两个检查点:
9
+ * - `beforePrompts`:新回合投递前;本回合提示里的附件计入总量但不降级;
10
+ * - `wrapStream`:回合内(工具结果里新读的图)每次 `turn` 请求前;已发出的消息都已落盘时才降级,
11
+ * 并把本次请求的转录换成降级后的(就地改 context,缓存控制器看到的也是降级后的请求)。
12
+ *
13
+ * 模型不收图片时不处理:`normalizeContext` 已把图片换成占位(ai/context.ts)。
14
+ */
15
+ import { MANY_IMAGES_COUNT, imageLimits } from "../ai/image-limits.js";
16
+ import { planImageBudget } from "../compaction/image-budget.js";
17
+ import { buildProjection } from "../session/projection.js";
18
+ import { convertToLlm } from "./transform.js";
19
+ function imagesIn(message) {
20
+ if (!("content" in message) || !Array.isArray(message.content))
21
+ return [];
22
+ return message.content.filter((block) => block.type === "image");
23
+ }
24
+ /** 粗查:总量、单张、张数都没越线就不必建投影。 */
25
+ function withinLimits(messages, reserve, limits) {
26
+ let total = 0;
27
+ let count = 0;
28
+ for (const message of [...messages.map(imagesIn), reserve]) {
29
+ for (const image of message) {
30
+ if (image.data.length > limits.perImageBase64)
31
+ return false;
32
+ total += image.data.length;
33
+ count++;
34
+ }
35
+ }
36
+ return total <= limits.perRequestBase64 && count <= MANY_IMAGES_COUNT;
37
+ }
38
+ export function createImageBudgetExtension(core, deps = {}) {
39
+ const limitsOf = deps.limits ?? ((model) => imageLimits(model));
40
+ /** 返回写下的降级条目;`synced`:要求 Agent 的消息与落盘的投影一致(回合内)。 */
41
+ const enforce = (model, reserve, synced) => {
42
+ if (!model.input.includes("image"))
43
+ return [];
44
+ const limits = limitsOf(model);
45
+ if (withinLimits(core.agent.messages, reserve, limits))
46
+ return [];
47
+ const projection = buildProjection(core.manager.branch());
48
+ if (synced && projection.messages.length !== core.agent.messages.length)
49
+ return [];
50
+ const plan = planImageBudget(projection.items, limits.perRequestBase64, {
51
+ perImageBytes: limits.perImageBase64,
52
+ reserve,
53
+ });
54
+ if (plan.length === 0)
55
+ return [];
56
+ for (const edit of plan) {
57
+ core.appendEntry({
58
+ type: "context_edit",
59
+ targetId: edit.targetId,
60
+ replacement: edit.replacement,
61
+ reason: "image_budget",
62
+ });
63
+ }
64
+ core.reloadMessages();
65
+ core.cache?.onContextChanged();
66
+ const images = plan.reduce((sum, edit) => sum + edit.images, 0);
67
+ core.log("info", `image budget: omitted ${images} earlier image(s) in ${plan.length} message(s) to fit the request size`);
68
+ return plan;
69
+ };
70
+ return {
71
+ id: "images",
72
+ beforePrompts(_ctx, prompts) {
73
+ const reserve = prompts.flatMap(imagesIn);
74
+ enforce(core.model(), reserve, false);
75
+ return [];
76
+ },
77
+ wrapStream(stream) {
78
+ return (model, context, options) => {
79
+ if ((options.purpose ?? "turn") === "turn") {
80
+ try {
81
+ if (enforce(model, [], true).length > 0) {
82
+ const messages = convertToLlm(core.agent.messages, {
83
+ provider: model.provider,
84
+ model: model.id,
85
+ });
86
+ context.messages = messages;
87
+ }
88
+ }
89
+ catch (error) {
90
+ core.log("warn", `image budget check failed: ${String(error)}`);
91
+ }
92
+ }
93
+ return stream(model, context, options);
94
+ };
95
+ },
96
+ };
97
+ }
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Plan 扩展:模式说明注入、`plan_state` 持久化、计划提取与审批编排、交接(docs/wave5-plan.md §6、
3
+ * D18–D21)。[W5-F] 只装在根会话(task 子会话的计划块作为 task 结果返回给父会话)。
4
+ *
5
+ * - 进入 plan(任何来源的 `permission_mode_changed`,或会话以 plan 启动 / resume 回到 plan):记
6
+ * prePlanMode,落 `custom{ama.plan_state{active:true}}`;之后的提示前按节奏追加
7
+ * `custom_message{ama.plan_mode}`(第 1 个提示完整版,之后每 5 个提示一次简版,每第 5 次完整版;
8
+ * 压缩后的首个提示补完整版)。手动退出:下一个提示前追加 `ama.plan_mode_exit`。全部只追加在尾部,
9
+ * 不改 system 与工具表(§9.1)。
10
+ * - 提取:plan 下一回合以纯文本结束(无工具调用)时,从这条回复里取 `<proposed_plan>`,落
11
+ * `custom{ama.plan{status:"proposed"}}` 与计划文件,发 `plan_proposed`。
12
+ * - 审批(谁来答,见 {@link PlanAttendance}):客户端 / SDK 回调 / 交互模式文本回复;无人值守按
13
+ * `plan.unattended`(缺省 stop:落盘后停下,**不替人批准**;approve 才自动批准)。
14
+ * - 批准:落 approved、步骤转 `ama.todo`(首项 in_progress)、切回执行模式(缺省进入前的模式)、
15
+ * `plan.model` 配置时切回执行模型,然后以 `custom_message{ama.plan_approved}`(计划全文 + 文件路径
16
+ * + 按 todo 推进)开启新回合。「新上下文执行」由调用方新建会话,用 `adopt()` + `freshPrompt`。
17
+ */
18
+ import { type PlanExtensionOptions } from "../plan/controller.js";
19
+ import type { SessionExtensionFactory } from "./session-extensions.js";
20
+ export declare function createPlanExtensionFactory(options: PlanExtensionOptions): SessionExtensionFactory;
21
+ export { planController, type PlanAttendance, type PlanController, type PlanDecision, type PlanDecisionHandler, type PlanExtensionOptions, type PlanResponse, type PlanResponseResult, } from "../plan/controller.js";
@@ -0,0 +1,524 @@
1
+ /**
2
+ * Plan 扩展:模式说明注入、`plan_state` 持久化、计划提取与审批编排、交接(docs/wave5-plan.md §6、
3
+ * D18–D21)。[W5-F] 只装在根会话(task 子会话的计划块作为 task 结果返回给父会话)。
4
+ *
5
+ * - 进入 plan(任何来源的 `permission_mode_changed`,或会话以 plan 启动 / resume 回到 plan):记
6
+ * prePlanMode,落 `custom{ama.plan_state{active:true}}`;之后的提示前按节奏追加
7
+ * `custom_message{ama.plan_mode}`(第 1 个提示完整版,之后每 5 个提示一次简版,每第 5 次完整版;
8
+ * 压缩后的首个提示补完整版)。手动退出:下一个提示前追加 `ama.plan_mode_exit`。全部只追加在尾部,
9
+ * 不改 system 与工具表(§9.1)。
10
+ * - 提取:plan 下一回合以纯文本结束(无工具调用)时,从这条回复里取 `<proposed_plan>`,落
11
+ * `custom{ama.plan{status:"proposed"}}` 与计划文件,发 `plan_proposed`。
12
+ * - 审批(谁来答,见 {@link PlanAttendance}):客户端 / SDK 回调 / 交互模式文本回复;无人值守按
13
+ * `plan.unattended`(缺省 stop:落盘后停下,**不替人批准**;approve 才自动批准)。
14
+ * - 批准:落 approved、步骤转 `ama.todo`(首项 in_progress)、切回执行模式(缺省进入前的模式)、
15
+ * `plan.model` 配置时切回执行模型,然后以 `custom_message{ama.plan_approved}`(计划全文 + 文件路径
16
+ * + 按 todo 推进)开启新回合。「新上下文执行」由调用方新建会话,用 `adopt()` + `freshPrompt`。
17
+ */
18
+ import { randomUUID } from "node:crypto";
19
+ import { formatModelRef } from "../ai/providers/channels.js";
20
+ import { AmaError } from "../errors.js";
21
+ import { permissionModeLabel } from "../permissions/modes.js";
22
+ import { todosFromDoneMarkers } from "../plan/done-markers.js";
23
+ import { extractProposedPlan, planFromMarkdown } from "../plan/extract.js";
24
+ import { PLAN_APPROVED_PROMPT, PLAN_APPROVED_TYPE, PLAN_MODE_BRIEF, PLAN_MODE_EXIT, PLAN_MODE_EXIT_TYPE, PLAN_MODE_FULL, PLAN_MODE_TYPE, freshContextPrompt, planApprovedText, planMessage, reminderKind, } from "../plan/prompts.js";
25
+ import { PLAN_CUSTOM_TYPE, PLAN_STATE_CUSTOM_TYPE, currentTodos, executionMode, latestPlan, latestPlanState, maxPlanVersion, resolvePlanDirectory, writePlanFile, } from "../plan/store.js";
26
+ import { APPROVE_REPLY, PLAN_NOTICE_TYPE, registerPlanController, } from "../plan/controller.js";
27
+ import { TODO_CUSTOM_TYPE } from "../tools/todo.js";
28
+ export function createPlanExtensionFactory(options) {
29
+ return ({ core }) => (core.depth > 0 ? undefined : new PlanExtension(core, options));
30
+ }
31
+ class PlanExtension {
32
+ core;
33
+ options;
34
+ id = "plan";
35
+ permission;
36
+ attendance;
37
+ handler;
38
+ lastMode;
39
+ inPlan = false;
40
+ pre = "default";
41
+ planId;
42
+ promptIndex = 0;
43
+ forceFull = false;
44
+ pendingExit = false;
45
+ pendingHandoff;
46
+ stateDirty = false;
47
+ approving = false;
48
+ lastSource;
49
+ justProposed;
50
+ executionModel;
51
+ executionThinking;
52
+ constructor(core, options) {
53
+ this.core = core;
54
+ this.options = options;
55
+ this.permission = core.options.permission;
56
+ this.attendance = options.attendance ?? "unattended";
57
+ const bash = options.config?.bash;
58
+ const pipeline = this.permission;
59
+ if (bash !== undefined)
60
+ pipeline?.setPlanBash?.(bash);
61
+ this.lastMode = this.permission?.mode ?? "default";
62
+ this.restore();
63
+ registerPlanController(core, this);
64
+ }
65
+ // -------------------------------------------------------------------------
66
+ // 恢复与模式跟踪
67
+ // -------------------------------------------------------------------------
68
+ restore() {
69
+ const branch = this.core.manager.branch();
70
+ const state = latestPlanState(branch);
71
+ const pending = latestPlan(branch);
72
+ if (pending !== undefined)
73
+ this.lastSource = pending.sourceEntryId;
74
+ if (state?.active === true) {
75
+ this.inPlan = true;
76
+ this.pre = state.prePlanMode;
77
+ this.planId = state.planId;
78
+ if (state.executionModel !== undefined)
79
+ this.executionModel = state.executionModel;
80
+ if (state.executionThinking !== undefined)
81
+ this.executionThinking = state.executionThinking;
82
+ if (this.permission !== undefined && this.permission.mode !== "plan")
83
+ this.permission.setMode("plan");
84
+ this.lastMode = "plan";
85
+ // 已投递过的提示按 plan_state 之后的 user 消息计数,提醒节奏接着走
86
+ const at = branch.findLastIndex((e) => e.type === "custom" && e.customType === PLAN_STATE_CUSTOM_TYPE);
87
+ this.promptIndex = branch
88
+ .slice(at + 1)
89
+ .filter((e) => e.type === "message" && e.message.role === "user").length;
90
+ }
91
+ else if (this.lastMode === "plan") {
92
+ this.inPlan = true;
93
+ this.planId = randomUUID();
94
+ this.stateDirty = true;
95
+ }
96
+ }
97
+ handle() {
98
+ const candidate = this.core;
99
+ return typeof candidate.prompt === "function" &&
100
+ typeof candidate.setPermissionMode === "function"
101
+ ? candidate
102
+ : undefined;
103
+ }
104
+ writeState(active) {
105
+ const data = { active, prePlanMode: this.pre };
106
+ if (this.planId !== undefined)
107
+ data.planId = this.planId;
108
+ if (active && this.executionModel !== undefined)
109
+ data.executionModel = this.executionModel;
110
+ if (active && this.executionThinking !== undefined)
111
+ data.executionThinking = this.executionThinking;
112
+ this.core.appendEntry({ type: "custom", customType: PLAN_STATE_CUSTOM_TYPE, data });
113
+ }
114
+ onModeChanged(mode) {
115
+ const previous = this.lastMode;
116
+ this.lastMode = mode;
117
+ if (mode === "plan" && !this.inPlan) {
118
+ this.inPlan = true;
119
+ this.pre = previous === "plan" ? "default" : previous;
120
+ this.planId = randomUUID();
121
+ this.promptIndex = 0;
122
+ this.pendingExit = false;
123
+ this.stateDirty = false;
124
+ this.writeState(true);
125
+ }
126
+ else if (mode !== "plan" && this.inPlan) {
127
+ this.inPlan = false;
128
+ if (!this.approving)
129
+ this.pendingExit = true;
130
+ this.stateDirty = false;
131
+ this.writeState(false);
132
+ }
133
+ }
134
+ // -------------------------------------------------------------------------
135
+ // 扩展钩子
136
+ // -------------------------------------------------------------------------
137
+ onEvent(event) {
138
+ switch (event.type) {
139
+ case "permission_mode_changed":
140
+ this.onModeChanged(event.mode);
141
+ break;
142
+ case "compaction_end":
143
+ if (event.result !== undefined)
144
+ this.forceFull = true;
145
+ break;
146
+ case "entry_appended":
147
+ if (event.entry.type === "custom" && event.entry.customType === TODO_CUSTOM_TYPE)
148
+ this.core.emit({ type: "todo_updated", items: currentTodos(this.core.manager.branch()) });
149
+ break;
150
+ case "turn_end":
151
+ if (this.inPlan && event.toolResults.length === 0 && event.message.stopReason === "stop")
152
+ this.proposeFromLastAssistant(true);
153
+ else if (!this.inPlan)
154
+ this.writeTodoItems(todosFromDoneMarkers(event.message, this.todos()));
155
+ break;
156
+ default:
157
+ break;
158
+ }
159
+ }
160
+ beforePrompts(_ctx, prompts) {
161
+ const out = [];
162
+ if (this.stateDirty) {
163
+ this.stateDirty = false;
164
+ this.writeState(true);
165
+ }
166
+ const reply = this.textApproval(prompts);
167
+ if (reply !== undefined) {
168
+ out.push(planMessage(PLAN_APPROVED_TYPE, reply));
169
+ return out;
170
+ }
171
+ if (this.pendingHandoff !== undefined) {
172
+ out.push(planMessage(PLAN_APPROVED_TYPE, this.pendingHandoff));
173
+ this.pendingHandoff = undefined;
174
+ this.pendingExit = false;
175
+ return out;
176
+ }
177
+ if (this.inPlan) {
178
+ this.usePlanningModel();
179
+ this.promptIndex++;
180
+ const kind = this.forceFull ? "full" : reminderKind(this.promptIndex);
181
+ this.forceFull = false;
182
+ if (kind !== undefined)
183
+ out.push(planMessage(PLAN_MODE_TYPE, kind === "full" ? PLAN_MODE_FULL : PLAN_MODE_BRIEF));
184
+ return out;
185
+ }
186
+ this.restoreExecutionModel();
187
+ if (this.pendingExit) {
188
+ this.pendingExit = false;
189
+ out.push(planMessage(PLAN_MODE_EXIT_TYPE, PLAN_MODE_EXIT));
190
+ }
191
+ return out;
192
+ }
193
+ onAgentSettled() {
194
+ if (this.inPlan && this.lastSourceChanged())
195
+ this.proposeFromLastAssistant(false);
196
+ const plan = this.justProposed;
197
+ this.justProposed = undefined;
198
+ if (plan === undefined)
199
+ return;
200
+ if (this.attendance === "text")
201
+ this.showNotice(this.noticeText(plan));
202
+ else if (this.attendance === "callback" && this.handler !== undefined) {
203
+ const handler = this.handler;
204
+ void (async () => {
205
+ await this.handle()?.waitForIdle();
206
+ const answer = await handler(plan);
207
+ if (answer !== undefined)
208
+ await this.respond({ ...answer, planId: plan.id });
209
+ })().catch((error) => this.log("warn", `plan approval failed: ${String(error)}`));
210
+ }
211
+ else if (this.attendance === "unattended" && this.unattendedPolicy() === "stop") {
212
+ this.log("info", `plan v${plan.version} awaits approval: ${plan.filePath ?? plan.id}`);
213
+ }
214
+ }
215
+ dispose() {
216
+ registerPlanController(this.core, undefined);
217
+ }
218
+ // -------------------------------------------------------------------------
219
+ // 提取与提交审批
220
+ // -------------------------------------------------------------------------
221
+ lastAssistantEntry() {
222
+ const branch = this.core.manager.branch();
223
+ for (let i = branch.length - 1; i >= 0; i--) {
224
+ const entry = branch[i];
225
+ if (entry.type !== "message" || entry.message.role !== "assistant")
226
+ continue;
227
+ const message = entry.message;
228
+ if (message.stopReason === "error" || message.stopReason === "aborted")
229
+ return undefined;
230
+ const text = message.content.map((b) => (b.type === "text" ? b.text : "")).join("");
231
+ return { id: entry.id, text };
232
+ }
233
+ return undefined;
234
+ }
235
+ lastSourceChanged() {
236
+ const last = this.lastAssistantEntry();
237
+ return last !== undefined && last.id !== this.lastSource;
238
+ }
239
+ proposeFromLastAssistant(inRun) {
240
+ const last = this.lastAssistantEntry();
241
+ if (last === undefined || last.id === this.lastSource)
242
+ return;
243
+ this.lastSource = last.id;
244
+ const extracted = extractProposedPlan(last.text);
245
+ if (extracted === undefined)
246
+ return;
247
+ const plan = this.propose(extracted, last.id);
248
+ if (this.attendance === "unattended" && this.unattendedPolicy() === "approve") {
249
+ const handoff = this.approve(plan, executionMode(undefined, this.pre), "approve");
250
+ if (inRun)
251
+ this.core.agent.followUp(planMessage(PLAN_APPROVED_TYPE, handoff));
252
+ else {
253
+ this.pendingHandoff = handoff;
254
+ void this.deliver(PLAN_APPROVED_PROMPT, "plan");
255
+ }
256
+ return;
257
+ }
258
+ this.justProposed = plan;
259
+ }
260
+ proposeFromLastReply() {
261
+ const pending = this.pending();
262
+ if (pending !== undefined)
263
+ return pending;
264
+ const last = this.lastAssistantEntry();
265
+ if (last === undefined || last.text.trim() === "")
266
+ return undefined;
267
+ this.lastSource = last.id;
268
+ return this.propose(extractProposedPlan(last.text) ?? planFromMarkdown(last.text), last.id);
269
+ }
270
+ propose(extracted, sourceEntryId) {
271
+ const branch = this.core.manager.branch();
272
+ const previous = latestPlan(branch);
273
+ if (previous?.status === "proposed")
274
+ this.appendPlan({ ...previous, status: "superseded" });
275
+ if (extracted.truncated === true)
276
+ this.log("warn", "plan has more than 30 steps; only the first 30 become todo items");
277
+ this.planId ??= randomUUID();
278
+ const plan = {
279
+ id: this.planId,
280
+ version: maxPlanVersion(branch) + 1,
281
+ status: "proposed",
282
+ markdown: extracted.markdown,
283
+ steps: extracted.steps,
284
+ sourceEntryId,
285
+ };
286
+ const filePath = this.writeFile(plan);
287
+ if (filePath !== undefined)
288
+ plan.filePath = filePath;
289
+ this.appendPlan(plan);
290
+ const event = {
291
+ type: "plan_proposed",
292
+ planId: plan.id,
293
+ version: plan.version,
294
+ markdown: plan.markdown,
295
+ steps: plan.steps,
296
+ };
297
+ if (filePath !== undefined)
298
+ event.filePath = filePath;
299
+ this.core.emit(event);
300
+ return plan;
301
+ }
302
+ writeFile(plan) {
303
+ const root = this.permission?.projectRoot ?? this.core.cwd;
304
+ const { dataDir } = this.options;
305
+ const configured = this.options.config?.directory;
306
+ if (dataDir === undefined && configured === undefined)
307
+ return undefined;
308
+ const target = resolvePlanDirectory(configured, root, dataDir ?? root);
309
+ if (target.warning !== undefined)
310
+ this.log("warn", target.warning);
311
+ try {
312
+ return writePlanFile(target.dir, this.core.manager.id, plan.version, plan.markdown);
313
+ }
314
+ catch (error) {
315
+ this.log("warn", `cannot write plan file: ${String(error)}`);
316
+ return undefined;
317
+ }
318
+ }
319
+ appendPlan(plan) {
320
+ this.core.appendEntry({ type: "custom", customType: PLAN_CUSTOM_TYPE, data: plan });
321
+ }
322
+ unattendedPolicy() {
323
+ return this.options.config?.unattended ?? "stop";
324
+ }
325
+ showNotice(text) {
326
+ if (this.options.notice !== true)
327
+ return;
328
+ const message = { ...planMessage(PLAN_NOTICE_TYPE, text), display: true };
329
+ this.core.emit({ type: "message_start", message });
330
+ }
331
+ noticeText(plan) {
332
+ const target = permissionModeLabel(executionMode(undefined, this.pre));
333
+ const where = plan.filePath === undefined ? "" : `(${plan.filePath})`;
334
+ return `计划 v${plan.version} 待审批${where}:回复 1 批准并执行(${target})· 2 以 Accept edits 执行 · 3 以 Auto 执行;回复其它内容作为修改意见,留在 Plan 模式。`;
335
+ }
336
+ // -------------------------------------------------------------------------
337
+ // 审批与交接
338
+ // -------------------------------------------------------------------------
339
+ current() {
340
+ return latestPlan(this.core.manager.branch()) ?? null;
341
+ }
342
+ get(planId) {
343
+ return latestPlan(this.core.manager.branch(), planId) ?? null;
344
+ }
345
+ pending() {
346
+ const plan = latestPlan(this.core.manager.branch());
347
+ return plan?.status === "proposed" ? plan : undefined;
348
+ }
349
+ todos() {
350
+ return currentTodos(this.core.manager.branch());
351
+ }
352
+ prePlanMode() {
353
+ return this.inPlan ? this.pre : undefined;
354
+ }
355
+ setAttendance(kind, handler) {
356
+ this.attendance = kind;
357
+ this.handler = handler;
358
+ }
359
+ /** 交互模式:计划待审批时用户回复 1 / 2 / 3(等)即批准;返回交接文本。 */
360
+ textApproval(prompts) {
361
+ if (this.attendance !== "text" || !this.inPlan)
362
+ return undefined;
363
+ const plan = this.pending();
364
+ const first = prompts[0];
365
+ if (plan === undefined || first?.role !== "user")
366
+ return undefined;
367
+ const text = typeof first.content === "string"
368
+ ? first.content
369
+ : first.content.map((b) => (b.type === "text" ? b.text : "")).join("");
370
+ const choice = APPROVE_REPLY[text.trim().toLowerCase()];
371
+ if (choice === undefined)
372
+ return undefined;
373
+ const mode = choice === "pre" ? executionMode(undefined, this.pre) : choice;
374
+ return this.approve(plan, mode, "approve");
375
+ }
376
+ async respond(response) {
377
+ let plan = this.pending();
378
+ if (plan === undefined || plan.id !== response.planId)
379
+ throw new AmaError("plan_not_found", `no plan ${response.planId} is waiting for approval`);
380
+ const { decision } = response;
381
+ if (decision === "reject") {
382
+ this.appendPlan({ ...plan, status: "rejected" });
383
+ this.core.emit({ type: "plan_resolved", planId: plan.id, decision });
384
+ return { planId: plan.id, decision, plan: { ...plan, status: "rejected" } };
385
+ }
386
+ if (decision === "revise") {
387
+ this.core.emit({ type: "plan_resolved", planId: plan.id, decision });
388
+ if (response.feedback !== undefined && response.feedback.trim() !== "")
389
+ await this.deliver(response.feedback, undefined);
390
+ return { planId: plan.id, decision, plan };
391
+ }
392
+ if (response.editedMarkdown !== undefined && response.editedMarkdown !== plan.markdown) {
393
+ const edited = extractProposedPlan(response.editedMarkdown) ?? planFromMarkdown(response.editedMarkdown);
394
+ plan = this.propose(edited, plan.sourceEntryId);
395
+ this.justProposed = undefined;
396
+ }
397
+ const mode = executionMode(response.mode, this.inPlan ? this.pre : this.lastMode);
398
+ if (decision === "approve_fresh") {
399
+ this.approve(plan, mode, decision, false);
400
+ return {
401
+ planId: plan.id,
402
+ decision,
403
+ mode,
404
+ plan: { ...plan, status: "approved" },
405
+ freshPrompt: freshContextPrompt(plan, { todoTool: this.todoTool() }),
406
+ };
407
+ }
408
+ this.pendingHandoff = this.approve(plan, mode, decision);
409
+ await this.deliver(PLAN_APPROVED_PROMPT, "plan");
410
+ return { planId: plan.id, decision, mode, plan: { ...plan, status: "approved" } };
411
+ }
412
+ /** 落 approved、生成 todo、发 plan_resolved、切模式与执行模型;返回交接文本。 */
413
+ approve(plan, mode, decision, todos = true) {
414
+ const approved = { ...plan, status: "approved" };
415
+ this.appendPlan(approved);
416
+ if (todos)
417
+ this.writeTodos(plan);
418
+ this.core.emit({ type: "plan_resolved", planId: plan.id, decision, mode });
419
+ this.approving = true;
420
+ try {
421
+ if (this.inPlan) {
422
+ const session = this.handle();
423
+ if (session !== undefined)
424
+ session.setPermissionMode(mode);
425
+ else {
426
+ this.permission?.setMode(mode);
427
+ this.core.emit({ type: "permission_mode_changed", mode });
428
+ }
429
+ }
430
+ }
431
+ finally {
432
+ this.approving = false;
433
+ }
434
+ this.pendingExit = false;
435
+ this.restoreExecutionModel();
436
+ return planApprovedText(plan, { todoTool: this.todoTool() });
437
+ }
438
+ /** [W5-Z] 活动工具集里没有 todo 时交接改用 `[DONE:n]` 文本标记(plan/done-markers.ts)。 */
439
+ todoTool() {
440
+ return this.core.activeToolNames().includes("todo");
441
+ }
442
+ writeTodos(plan) {
443
+ if (plan.steps.length === 0)
444
+ return;
445
+ const items = plan.steps.map((step, i) => ({
446
+ id: step.id,
447
+ text: step.text,
448
+ status: i === 0 ? "in_progress" : "pending",
449
+ planStep: step.id,
450
+ }));
451
+ this.writeTodoItems(items);
452
+ }
453
+ writeTodoItems(items) {
454
+ if (items !== undefined)
455
+ this.core.appendEntry({ type: "custom", customType: TODO_CUSTOM_TYPE, data: { items } });
456
+ }
457
+ adopt(plan) {
458
+ this.appendPlan({ ...plan, status: "approved" });
459
+ this.lastSource = plan.sourceEntryId;
460
+ this.writeTodos(plan);
461
+ }
462
+ /** 空闲时开一个新回合(运行中则等它结束);新回合开始即返回。 */
463
+ async deliver(text, origin) {
464
+ const session = this.handle();
465
+ if (session === undefined)
466
+ return;
467
+ for (let attempt = 0; attempt < 20; attempt++) {
468
+ await session.waitForIdle();
469
+ let failure;
470
+ session.prompt(text, origin === undefined ? {} : { origin }).catch((error) => {
471
+ failure = error;
472
+ });
473
+ for (let i = 0; i < 3; i++)
474
+ await Promise.resolve();
475
+ if (failure instanceof AmaError && failure.code === "busy")
476
+ continue;
477
+ if (failure !== undefined)
478
+ this.log("warn", `plan turn failed: ${String(failure)}`);
479
+ return;
480
+ }
481
+ this.log("warn", "plan turn not started: session stayed busy");
482
+ }
483
+ // -------------------------------------------------------------------------
484
+ // 规划 / 执行分模型(§6.4,缺省关)
485
+ // -------------------------------------------------------------------------
486
+ usePlanningModel() {
487
+ const ref = this.options.config?.model;
488
+ const level = this.options.config?.thinkingLevel;
489
+ if ((ref === undefined && level === undefined) || this.executionModel !== undefined)
490
+ return;
491
+ const session = this.handle();
492
+ if (session === undefined)
493
+ return;
494
+ this.executionModel = formatModelRef(this.core.model());
495
+ this.executionThinking = this.core.thinkingLevel();
496
+ const failed = (error) => this.log("warn", `cannot switch to plan.model ${ref ?? ""}: ${String(error)}`);
497
+ if (ref !== undefined && ref !== this.executionModel)
498
+ session.setModel(ref).catch(failed);
499
+ if (level !== undefined && level !== this.executionThinking)
500
+ session.setThinkingLevel(level);
501
+ this.writeState(true);
502
+ }
503
+ restoreExecutionModel() {
504
+ const ref = this.executionModel;
505
+ const level = this.executionThinking;
506
+ if (ref === undefined)
507
+ return;
508
+ this.executionModel = undefined;
509
+ this.executionThinking = undefined;
510
+ const session = this.handle();
511
+ if (session === undefined)
512
+ return;
513
+ if (ref !== formatModelRef(this.core.model()))
514
+ session
515
+ .setModel(ref)
516
+ .catch((error) => this.log("warn", `cannot switch back to ${ref}: ${String(error)}`));
517
+ if (level !== undefined && level !== this.core.thinkingLevel())
518
+ session.setThinkingLevel(level);
519
+ }
520
+ log(level, message) {
521
+ (this.options.log ?? this.core.log.bind(this.core))(level, message);
522
+ }
523
+ }
524
+ export { planController, } from "../plan/controller.js";