@aragon-agent/cli 0.6.38 → 0.6.40

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 (209) hide show
  1. package/CHANGELOG.md +263 -0
  2. package/README.md +544 -11
  3. package/dist/agent/controller.d.ts +245 -10
  4. package/dist/agent/controller.js +367 -22
  5. package/dist/agent/model-profile-settings.js +12 -1
  6. package/dist/agent/plan-prompt.d.ts +1 -3
  7. package/dist/agent/plan-prompt.js +13 -7
  8. package/dist/agent/reducer.d.ts +70 -2
  9. package/dist/agent/reducer.js +59 -6
  10. package/dist/agent/skill-triage-prompt.d.ts +42 -0
  11. package/dist/agent/skill-triage-prompt.js +74 -0
  12. package/dist/agent/system-prompt.d.ts +28 -2
  13. package/dist/agent/system-prompt.js +12 -4
  14. package/dist/cli.js +106 -32
  15. package/dist/commands/builtins.js +24 -3
  16. package/dist/commands/keys-command.d.ts +23 -0
  17. package/dist/commands/keys-command.js +38 -0
  18. package/dist/commands/registry.d.ts +39 -0
  19. package/dist/commands/skills.js +86 -1
  20. package/dist/commands/subagent-command.d.ts +25 -0
  21. package/dist/commands/subagent-command.js +242 -0
  22. package/dist/commands/task-command.d.ts +39 -0
  23. package/dist/commands/task-command.js +102 -0
  24. package/dist/compaction/command.js +16 -3
  25. package/dist/compaction/compactor.d.ts +25 -0
  26. package/dist/compaction/compactor.js +111 -10
  27. package/dist/compaction/digest.d.ts +24 -1
  28. package/dist/compaction/digest.js +139 -17
  29. package/dist/compaction/limits.d.ts +21 -0
  30. package/dist/compaction/limits.js +21 -0
  31. package/dist/compaction/reason-text.d.ts +36 -0
  32. package/dist/compaction/reason-text.js +78 -0
  33. package/dist/compaction/types.d.ts +82 -14
  34. package/dist/compaction/wiring.d.ts +13 -8
  35. package/dist/compaction/wiring.js +1 -0
  36. package/dist/config/cli-commands.d.ts +23 -0
  37. package/dist/config/cli-commands.js +36 -0
  38. package/dist/config/env.js +17 -4
  39. package/dist/config/load.js +55 -3
  40. package/dist/config/schema.d.ts +125 -10
  41. package/dist/config/schema.js +138 -14
  42. package/dist/config/store.js +23 -3
  43. package/dist/diagnostics/doctor.d.ts +9 -0
  44. package/dist/diagnostics/doctor.js +34 -0
  45. package/dist/diagnostics/info.d.ts +71 -0
  46. package/dist/diagnostics/info.js +91 -5
  47. package/dist/exec/bridge/caller-tools.d.ts +96 -0
  48. package/dist/exec/bridge/caller-tools.js +213 -0
  49. package/dist/exec/bridge/decisions.d.ts +78 -0
  50. package/dist/exec/bridge/decisions.js +116 -0
  51. package/dist/exec/bridge/human-answers.d.ts +38 -0
  52. package/dist/exec/bridge/human-answers.js +164 -0
  53. package/dist/exec/bridge/limits.d.ts +68 -0
  54. package/dist/exec/bridge/limits.js +68 -0
  55. package/dist/exec/bridge/ndjson.d.ts +17 -0
  56. package/dist/exec/bridge/ndjson.js +83 -0
  57. package/dist/exec/bridge/pending.d.ts +70 -0
  58. package/dist/exec/bridge/pending.js +132 -0
  59. package/dist/exec/bridge/protocol.d.ts +346 -0
  60. package/dist/exec/bridge/protocol.js +256 -0
  61. package/dist/exec/bridge/server.d.ts +142 -0
  62. package/dist/exec/bridge/server.js +502 -0
  63. package/dist/exec/bridge/source.d.ts +58 -0
  64. package/dist/exec/bridge/source.js +145 -0
  65. package/dist/exec/bridge/writer.d.ts +26 -0
  66. package/dist/exec/bridge/writer.js +156 -0
  67. package/dist/exec/cli-commands.js +30 -2
  68. package/dist/exec/emitter.d.ts +16 -2
  69. package/dist/exec/emitter.js +26 -6
  70. package/dist/exec/events.d.ts +465 -10
  71. package/dist/exec/events.js +47 -1
  72. package/dist/exec/hooks/dispatch.d.ts +76 -0
  73. package/dist/exec/hooks/dispatch.js +169 -0
  74. package/dist/exec/hooks/index.d.ts +83 -0
  75. package/dist/exec/hooks/index.js +205 -0
  76. package/dist/exec/hooks/limits.d.ts +56 -0
  77. package/dist/exec/hooks/limits.js +49 -0
  78. package/dist/exec/hooks/registry.d.ts +49 -0
  79. package/dist/exec/hooks/registry.js +91 -0
  80. package/dist/exec/hooks/tool-wrapper.d.ts +94 -0
  81. package/dist/exec/hooks/tool-wrapper.js +154 -0
  82. package/dist/exec/hooks/turn.d.ts +61 -0
  83. package/dist/exec/hooks/turn.js +59 -0
  84. package/dist/exec/hooks/validate.d.ts +85 -0
  85. package/dist/exec/hooks/validate.js +310 -0
  86. package/dist/exec/index.d.ts +130 -4
  87. package/dist/exec/index.js +501 -10
  88. package/dist/exec/options.d.ts +124 -2
  89. package/dist/exec/options.js +413 -6
  90. package/dist/exec/permission.d.ts +9 -1
  91. package/dist/exec/permission.js +7 -1
  92. package/dist/exec/request/limits.d.ts +51 -0
  93. package/dist/exec/request/limits.js +49 -0
  94. package/dist/exec/request/scope.d.ts +106 -0
  95. package/dist/exec/request/scope.js +149 -0
  96. package/dist/exec/result/limits.d.ts +74 -0
  97. package/dist/exec/result/limits.js +72 -0
  98. package/dist/exec/result/prompt.d.ts +28 -0
  99. package/dist/exec/result/prompt.js +74 -0
  100. package/dist/exec/result/schema.d.ts +99 -0
  101. package/dist/exec/result/schema.js +464 -0
  102. package/dist/exec/result/sink.d.ts +56 -0
  103. package/dist/exec/result/sink.js +50 -0
  104. package/dist/exec/result/tool.d.ts +37 -0
  105. package/dist/exec/result/tool.js +118 -0
  106. package/dist/exec/runner.d.ts +395 -1
  107. package/dist/exec/runner.js +860 -8
  108. package/dist/exec/threads/aggregate.d.ts +34 -0
  109. package/dist/exec/threads/aggregate.js +30 -0
  110. package/dist/exec/threads/capacity.d.ts +23 -0
  111. package/dist/exec/threads/capacity.js +38 -0
  112. package/dist/exec/threads/dispatch-source.d.ts +60 -0
  113. package/dist/exec/threads/dispatch-source.js +106 -0
  114. package/dist/exec/threads/execute-threads.d.ts +53 -0
  115. package/dist/exec/threads/execute-threads.js +234 -0
  116. package/dist/exec/threads/gate.d.ts +42 -0
  117. package/dist/exec/threads/gate.js +81 -0
  118. package/dist/exec/threads/identity.d.ts +36 -0
  119. package/dist/exec/threads/identity.js +67 -0
  120. package/dist/exec/threads/interrupt.d.ts +29 -0
  121. package/dist/exec/threads/interrupt.js +79 -0
  122. package/dist/exec/threads/limits.d.ts +58 -0
  123. package/dist/exec/threads/limits.js +35 -0
  124. package/dist/exec/threads/outcome.d.ts +47 -0
  125. package/dist/exec/threads/outcome.js +26 -0
  126. package/dist/exec/threads/registry.d.ts +81 -0
  127. package/dist/exec/threads/registry.js +427 -0
  128. package/dist/exec/threads/runtime.d.ts +107 -0
  129. package/dist/exec/threads/runtime.js +523 -0
  130. package/dist/exec/threads/thread-source.d.ts +20 -0
  131. package/dist/exec/threads/thread-source.js +134 -0
  132. package/dist/fast/limits.d.ts +54 -0
  133. package/dist/fast/limits.js +54 -0
  134. package/dist/fast/summary-call.d.ts +72 -0
  135. package/dist/fast/summary-call.js +133 -0
  136. package/dist/fast/summary-prompt.d.ts +106 -0
  137. package/dist/fast/summary-prompt.js +320 -0
  138. package/dist/fast/task-summary-types.d.ts +66 -0
  139. package/dist/fast/task-summary-types.js +17 -0
  140. package/dist/logging/logger.d.ts +1 -1
  141. package/dist/proc/limits.d.ts +7 -0
  142. package/dist/proc/limits.js +7 -0
  143. package/dist/proc/log-mirror.d.ts +21 -4
  144. package/dist/proc/log-mirror.js +29 -7
  145. package/dist/proc/output-streams.d.ts +50 -0
  146. package/dist/proc/output-streams.js +54 -0
  147. package/dist/skills/service.d.ts +41 -0
  148. package/dist/skills/service.js +62 -0
  149. package/dist/skills/triage.d.ts +194 -0
  150. package/dist/skills/triage.js +393 -0
  151. package/dist/team/forced-dispatch.d.ts +95 -0
  152. package/dist/team/forced-dispatch.js +208 -0
  153. package/dist/team/limits.d.ts +21 -1
  154. package/dist/team/limits.js +21 -1
  155. package/dist/team/normalize.d.ts +15 -0
  156. package/dist/team/normalize.js +21 -4
  157. package/dist/team/prompt.d.ts +6 -3
  158. package/dist/team/prompt.js +9 -5
  159. package/dist/team/runtime.d.ts +9 -0
  160. package/dist/team/runtime.js +8 -0
  161. package/dist/team/subagent.d.ts +15 -0
  162. package/dist/team/subagent.js +45 -1
  163. package/dist/team/task-tool.js +11 -7
  164. package/dist/tools/bash-tool.d.ts +10 -0
  165. package/dist/tools/bash-tool.js +71 -6
  166. package/dist/tools/human-input.d.ts +35 -2
  167. package/dist/tools/human-input.js +38 -0
  168. package/dist/tools/index.d.ts +93 -1
  169. package/dist/tools/index.js +112 -3
  170. package/dist/tools/plan-tools.d.ts +20 -12
  171. package/dist/tools/plan-tools.js +24 -17
  172. package/dist/tools/tool-gate.d.ts +100 -0
  173. package/dist/tools/tool-gate.js +87 -0
  174. package/dist/ui/App.js +238 -12
  175. package/dist/ui/Header.d.ts +28 -16
  176. package/dist/ui/Header.js +21 -19
  177. package/dist/ui/StatusBar.d.ts +1 -0
  178. package/dist/ui/StatusBar.js +1 -0
  179. package/dist/ui/Transcript.js +1 -1
  180. package/dist/ui/entries/CompactionCard.d.ts +11 -1
  181. package/dist/ui/entries/CompactionCard.js +65 -4
  182. package/dist/ui/interaction-hints.js +6 -0
  183. package/dist/ui/layout/OverlayFrame.d.ts +18 -0
  184. package/dist/ui/layout/OverlayFrame.js +19 -4
  185. package/dist/ui/layout/header-hints.d.ts +55 -62
  186. package/dist/ui/layout/header-hints.js +59 -166
  187. package/dist/ui/layout/header-left.d.ts +115 -0
  188. package/dist/ui/layout/header-left.js +206 -0
  189. package/dist/ui/layout/shortcut-panel-layout.d.ts +191 -0
  190. package/dist/ui/layout/shortcut-panel-layout.js +321 -0
  191. package/dist/ui/layout/status-detail-layout.js +5 -1
  192. package/dist/ui/layout/status-layout.d.ts +10 -0
  193. package/dist/ui/layout/status-layout.js +18 -1
  194. package/dist/ui/layout/task-summary-cell.d.ts +61 -0
  195. package/dist/ui/layout/task-summary-cell.js +65 -0
  196. package/dist/ui/overlays/HelpOverlay.d.ts +12 -0
  197. package/dist/ui/overlays/HelpOverlay.js +29 -2
  198. package/dist/ui/overlays/SettingsScreen.js +29 -5
  199. package/dist/ui/overlays/ShortcutPanel.d.ts +39 -0
  200. package/dist/ui/overlays/ShortcutPanel.js +106 -0
  201. package/dist/ui/overlays/TeamFanoutPage.d.ts +35 -0
  202. package/dist/ui/overlays/TeamFanoutPage.js +78 -0
  203. package/dist/ui/overlays/model-profile-state.d.ts +19 -1
  204. package/dist/ui/overlays/model-profile-state.js +12 -0
  205. package/dist/ui/overlays/team-fanout-state.d.ts +73 -0
  206. package/dist/ui/overlays/team-fanout-state.js +97 -0
  207. package/dist/ui/shortcuts.d.ts +81 -0
  208. package/dist/ui/shortcuts.js +221 -0
  209. package/package.json +2 -2
package/CHANGELOG.md CHANGED
@@ -8,6 +8,247 @@ were written.
8
8
  ## Unreleased
9
9
 
10
10
  ### Added
11
+ - **Automatic skill triage, and no round limit on questions.** Two changes that
12
+ share one idea: replace a hard limit with guidance the agent can act on.
13
+ `ask_user` is no longer capped at four rounds per turn — the cap, its prompt
14
+ wording (`you may ask up to 4 rounds`), its tool-result counters
15
+ (`roundsUsed` / `roundsRemaining`) and its `/plan status` readout are all gone,
16
+ because an agent that is told it is running out of questions behaves like it.
17
+ What replaced it is a de-duplication guard: a question batch you have already
18
+ **answered** in this turn returns a note instead of reopening the overlay, while
19
+ a **cancelled** one records nothing, so an accidental `Esc` never retires a
20
+ question. Steering does not reset the guard, because a steer resumes the same
21
+ turn. `planModeMaxAskRounds` is retired rather than defaulted to a large number:
22
+ `config set` names the reason and exits `2`, and the key disappears from
23
+ `config.json` at the next write. The single-wait ceiling
24
+ (`planModeHumanTimeoutMs`) is untouched — it protects an unattended terminal, not
25
+ the agent's vocabulary.
26
+ Skill use now begins with a step instead of a hope. Every task gets a
27
+ `<skill_triage>` protocol fixing *when* to decide (after the project guidance and
28
+ the directory listing, before any plan or edit) plus up to three ranked
29
+ `<skill_candidates>` computed locally from your message against the installed
30
+ records — no model call, no file read, no `SKILL.md` body, so the disclosure
31
+ ladder keeps its lower levels untouched. When a strongly-matching skill was
32
+ available and the turn did real work without loading it, the run ends with one
33
+ `info` line and a pointer to `/skills route`; that fires at most once per skill
34
+ per session, because a line printed every turn is a line you learn to skip.
35
+ `/skills route <text>` explains any decision (including the runner-up and the
36
+ threshold on a miss), `/skills triage on|off` flips the feature live, and
37
+ `skills.triage` (default `true`) makes it permanent — off restores the previous
38
+ prompt byte for byte. Subagents are deliberately not triaged: a child has no
39
+ `skill` tool, so candidates would advertise a capability it does not have.
40
+ - **`/subagent`: force a parallel fan-out with one command.** The escape hatch for
41
+ "I want a team on this, not the model's judgement": `/subagent -n 6 review the error
42
+ handling in packages/cli/src/team`, `/subagent --parts api,ui,docs audit the request
43
+ path`, `/subagent --read-only profile the parser`. With no argument it reports live
44
+ capacity, the subagent model, the supervisor and the team switch, and it answers even in
45
+ a `--no-team` session rather than pretending. The command compiles your intent into ONE
46
+ user turn that tells the lead to call `task` once, so it shares the single dispatch path
47
+ (same Esc, same report, same spend accounting) - the forcing is instruction-level, and
48
+ the notice says what the next turn will do instead of claiming children have started.
49
+ A count above `team.maxSubagents` is narrowed AND disclosed (`asked for 12;
50
+ team.maxSubagents is 8`), a parts list is narrowed by both the structural 8 and the
51
+ configured width, and `-n 0` is an error rather than a silent 1.
52
+ - **`/settings` → `Subagent fan-out`.** A fourth root row holding `Max subagents`,
53
+ `Run at once` and `Supervisor`, adjusted with `Left` / `Right` and saved by the same
54
+ single transaction as every other settings row (`Ctrl+S` on the main page; the fan-out
55
+ page's footer says so instead of promising a key it does not listen for). Narrowing the
56
+ population narrows the pool with it, and the follow-down is committed, so widening again
57
+ does not silently restore a pool you did not choose.
58
+ - **`--threads`: one process, N isolated conversations.** A headless `aragon exec`
59
+ process used to be one conversation: the seventh tenant's messages appeared in the
60
+ third tenant's history, one `todo` list served everybody, and `interrupt` ended the
61
+ process for all of them. `--threads` (with `--input-format bridge` and
62
+ `--output-format stream-json`) makes a conversation an OBJECT: `thread_open` /
63
+ `thread_close` / `thread_list`, and every frame may carry a `threadId` (absent means
64
+ the default thread `main`, which is auto-opened, so a v1 caller is unchanged). Each
65
+ thread owns its controller, history, `todo`s, prompt queue, session file
66
+ (`<base>-<threadId>`) and account; three things are shared and only three - one
67
+ writer (so `seq` stays global), one emitter (one `result`), one registry.
68
+ `--max-open-threads` (default 4) bounds how many exist and `--thread-concurrency`
69
+ (default 2) how many hold a request in flight, released per request through a FIFO
70
+ gate. Isolation is an ownership fact rather than a promise: every line carries its own
71
+ `threadId`, a thread's provider error cannot end another thread or the process, and
72
+ `result.threads.items` reports each tenant's own `requests`/`turns`/`usage`/`cost`
73
+ while `result.usage` stays the sum. `--max-turns` is refused with `--threads` (use
74
+ `--request-max-turns`), and `interrupt` stays process-level:
75
+ `interrupt{threadId}` answers `interrupt_is_process_level` instead of silently becoming
76
+ "cancel one request", because the two have different exit codes and only one writes
77
+ `result`. See [Many conversations in one process](README.md#many-conversations-in-one-process).
78
+ - **Lifecycle hooks: the caller can now change what the model says and sees.**
79
+ `hook_register` installs programmatic middleware at four points - `prompt_pre`
80
+ (rewrite or refuse the caller's text), `turn_start` (inject context through the one
81
+ safe steering checkpoint), `tool_pre` (rewrite a tool's arguments, or refuse it) and
82
+ `tool_post` (replace, redact or withhold a tool's result). Every round trip is one
83
+ `hook_request` with its own `hookRequestId` and exactly one `hook_resolved`
84
+ (`answered` / `timeout` / `aborted` / `invalid` / `saturated` / `unregistered`), bounded
85
+ by `--hook-timeout` (default 5 s) and failing closed by default
86
+ (`--on-hook-timeout deny|allow|error`; `allow` clamps to 30 s so two round trips cannot
87
+ consume a tool's 120 s budget). `tool_pre` rewrites are re-validated against the tool's
88
+ own schema so a hook's bug is never reported as the model's; `tool_post` is the
89
+ outermost wrapper, so it also covers denial text, a tool's own error output and the
90
+ `<job_notices>` tail of a background job. Hooks are per thread - one tenant's redactor
91
+ never sees another's data - and they deliberately do not wrap caller-registered tools.
92
+ See [Intervene in the data flow](README.md#intervene-in-the-data-flow-lifecycle-hooks).
93
+ - `aragon info --json` reports `threadLimits` and `hookLimits` (both from their own
94
+ `*_LIMITS` constants) beside the existing bridge budgets, and advertises the new
95
+ `threads` / `hooks` capability strings from the same `execCapabilities()` derivation
96
+ point `system/init` uses - so a caller can read what the build accepts before deciding
97
+ which frames to send.
98
+ - `examples/bridge-host-threads.mjs` (in `packages/cli/examples`, beside `bridge-host.mjs`): a dependency-free reference host with two threads
99
+ and one redactor, including the five things a host gets wrong (back-to-back
100
+ `thread_open` + `prompt`, answering `hookRequestId`, closing threads so `result` can be
101
+ written, and reading per-tenant accounting). `--dry-run` prints the frames without
102
+ spawning anything.
103
+
104
+ ### Changed
105
+ - **The subagent fan-out default is now 8 per dispatch and 8 in flight at once** (it was
106
+ 5 and 3). The perceived "only 3 subagents" was the concurrency ceiling, not the
107
+ population - changing only the population would have shown nothing, because children
108
+ 4..8 would simply queue. `maxConcurrent` stays a separate knob precisely so a single API
109
+ key collecting 429s can be fixed by lowering `Run at once`; the settings row and the
110
+ README both say which number to lower. The hard ceiling is unchanged at 10: 8 is a
111
+ default you can move, 10 is the limit you cannot. **A persisted `5`/`3` pair from an
112
+ older install is migrated to `8`/`8` once**, with a `team.fanoutMigrated` marker so a
113
+ deliberate return to 5/3 sticks instead of being widened again on every launch. A
114
+ configuration whose values are anything else is never touched.
115
+ - `makeExecController` now separates PROCESS-LEVEL log mounts from PER-CONTROLLER ones:
116
+ `run_exec`, the `onFailure` handler and the untrusted-skill notices happen once per
117
+ process (selected by the new `ExecControllerDeps.processLevelMounts`, which only the
118
+ default thread sets), while `attachAgentEvents` / `attachTeamEvents` are attached for
119
+ **every** thread and disposed when it closes. Skipping the latter would have left every
120
+ tenant but the first invisible in the log - an observability regression, not an
121
+ optimisation. Deriving the first half from `threadId` instead would have been wrong in
122
+ the other direction: the default thread has an id too (`main`).
123
+ - `ExecRunner` gained `threadId`, `onRequestSettled`, `beforeTurn`, `hookPort` and
124
+ `installSignalTerminator`. All are optional; with none of them set the runner writes
125
+ the same bytes it wrote before.
126
+
127
+ ### Fixed
128
+ - Changing only the fan-out width no longer drops the subagent model a session kit
129
+ installed. The settings save read "the `team` section was written" as "the user
130
+ re-decided the subagent slot", so adjusting `Max subagents` silently released a slot the
131
+ kit held - with every value on screen still correct. Only an explicit `model` /
132
+ `modelProfileId` decision releases the slot now, and the fan-out page can express
133
+ nothing else.
134
+ - The `task` tool's description no longer states a fixed width (`2-5 independent
135
+ subagents`, `hard maximum 10`), which contradicted the configured maximum in the very
136
+ same request. It points at `<team_mode>`, which is generated from the live config.
137
+ - A process-level signal now has exactly ONE owner in thread mode (it used to be
138
+ installed by every runner into a single process-wide slot, so the last one won): the
139
+ terminator fans out to all threads, `result` is written before the process exits, and a
140
+ second signal still exits immediately - after attempting `result`.
141
+ - `--max-duration` is one process-level timer in thread mode instead of N timers with N
142
+ opinions, and `decideProcessOutcome` is the single place a stop reason and exit code
143
+ are decided.
144
+ - A thread's runner now really is given its own bridge channel: `runner.attachBridge(server,
145
+ source)` was missing from the per-thread assembly, so prompts got no `ack`, turns wrote no
146
+ `request_end`, `steer` answered `not_running` and `cancel_request` was dropped in silence.
147
+ The runtime now owns the queue it serves (`ThreadRuntimeDeps.source`) and wires it before
148
+ the first prompt can arrive.
149
+ - The admission slot is released by the queue that took it. It used to be released straight
150
+ on the gate, which left the queue believing it still held a slot - so from a thread's
151
+ second request onward `--thread-concurrency` silently stopped applying - and a prompt
152
+ cancelled while its thread waited for a slot left that slot held forever.
153
+ - `interrupt{requestId}` in thread mode no longer stops the process: it withdraws one queued
154
+ turn (exit code 0), which is what it has always meant; `interrupt{threadId}` is still
155
+ refused and `interrupt` without either is still process-level.
156
+ - Thread frames sent to a run WITHOUT `--threads` are answered `ack{accepted:false,
157
+ code:'threads_disabled'}` instead of being ignored, and `thread_close{mode}` rejects an
158
+ unknown mode instead of coercing it to `settle`.
159
+ - `--on-hook-timeout allow` no longer rewinds a chain: when a later hook stays silent, the
160
+ rewrites and redactions earlier hooks already made are kept instead of being replaced by
161
+ the original text. A `tool_post` `replace` whose content array holds a part without a
162
+ `type` is refused (`hook_invalid_answer`) rather than stored - it would have been an HTTP
163
+ 400 on the next request and on every resume of that session.
164
+ - The `turn_start` injection budget is per REQUEST again (it was effectively per thread,
165
+ because nothing reset it), a budget stop no longer reports a non-existent
166
+ `injection_stranded`, and `--on-hook-timeout error` now actually marks the request as
167
+ failed instead of only setting a code.
168
+ - A thread at `maxRequestsPerThread` answers `thread_capacity` (the ceiling existed as a
169
+ constant nothing consulted), and the per-point hook ceiling counts `"*"` as every tool.
170
+
171
+ - **`--result-schema`: the answer becomes a contract, not a paragraph.** The model
172
+ submits its result through a `submit_result` tool whose parameter schema IS your
173
+ JSON Schema, so a machine (not a parser you write and then distrust) decides
174
+ whether the answer conforms. A violation comes back as field paths the model can
175
+ fix inside the same prompt, and `--result-repair-turns` (default 1, max 2) bounds
176
+ how many times it may try. `result.structured` carries the accepted value,
177
+ `result.result` becomes its canonical JSON so the `| jq -r .result` idiom still
178
+ means "the answer", and the model's prose moves to `result.text`. Failure is a
179
+ shape rather than a mood: an explicit `null` plus
180
+ `error.code: "result_schema_unsatisfied"`, with `--on-result-unsatisfied
181
+ error|continue` deciding whether the run stops (exit 1) or keeps serving with
182
+ `result.isError: true`. Validation is two layers - Core's optional `ajv` plus a
183
+ bounded validator of this CLI's own that enforces `required`, nested
184
+ `properties`, `items` and `additionalProperties: false` on machines without it -
185
+ and `aragon doctor` / `aragon info --json` say which one is in force. See
186
+ [Hand back a typed result](README.md#hand-back-a-typed-result).
187
+ - **Requests are first class in bridge mode.** A long-lived `aragon exec
188
+ --input-format bridge` process used to have one set of budgets and one bill for
189
+ everything it ever did. Now every `prompt` ends with its own `request_end` -
190
+ `stopReason`, `turns`, `durationMs`, `usage`, `cost`, the last assistant text and
191
+ (under a schema) the accepted value - and the numbers are differences, so two
192
+ requests add up to the process total. `--request-max-turns` and
193
+ `--request-max-duration` bound ONE request without ending the process: its
194
+ `request_end` reports `max_turns` / `timeout` and the next caller is served
195
+ normally. `cancel_request{requestId?}` withdraws a single request - a queued one
196
+ is deleted and accounted as `cancelled` with `turns: 0`, an in-flight one is
197
+ aborted, and one that is already finished is refused with `unknown_turn` instead
198
+ of aborting a healthy run. `interrupt` still stops the whole run: two questions,
199
+ two frames. `aragon info --json` gained the `request_scope` capability and the
200
+ ceilings. See [One request at a time](README.md#one-request-at-a-time-budgets-billing-and-cancellation).
201
+ - **`--input-format bridge`: the headless mode can now be driven, not just piped.**
202
+ Four new surfaces on one switch, all additive - without the flag every event,
203
+ field and tool object is exactly what it was:
204
+ - **The caller decides.** `--permission-mode ask` makes `write_file` /
205
+ `edit_file` / `bash` / `skill_create` send a `decision_request` and wait for a
206
+ `decision` frame before they run (answer `allow`, `deny` with a reason, or
207
+ `allow` with `scope: "session"` to stop asking for that tool). `ask_user` and
208
+ `submit_plan` become reachable headlessly for the first time
209
+ (`decision_request{kind:'questions'|'plan'}`), and `skill_install` asks the
210
+ same way instead of being denied outright. A denial is a readable
211
+ `tool_result` the model can route around - never a crash, never a silent skip.
212
+ - **The caller serves tools.** `tool_register` declares up to 32 tools of your
213
+ own (`db_query`, `open_ticket`), full-replacement semantics, validated
214
+ (name, reserved names, JSON Schema, size) before anything is written; the
215
+ model then calls them and the CLI sends `tool_invoke` and waits for
216
+ `tool_result`, with the same timeout, cancellation and 100 KB clip the built-in
217
+ tools get. Registration lands at a turn boundary, never mid-iteration.
218
+ - **Requests are correlatable.** `prompt{id}` / `steer{id}` / `interrupt` in,
219
+ `ack` / `steering{steeringIds}` / `pong` out, plus `seq` on every stdout line
220
+ and `requestId` on `user` / `turn_state` / `assistant` / `tool_call` /
221
+ `tool_result`. `result` is still exactly once and last; `init` is still first.
222
+ - **Budgets for the new channel.** `--decision-timeout <ms>` (default 300000,
223
+ max 1800000), `--on-decision-timeout deny|error`, `--caller-tool-timeout <ms>`
224
+ (default 60000, max 600000). All three are usage errors without
225
+ `--input-format bridge`, as are a positional prompt and `--prompt-file`,
226
+ because bridge prompts come from stdin.
227
+
228
+ `aragon info --json` now reports `inputFormats` with `bridge`, `permissionModes`
229
+ with `ask`, and a `bridge` block (`protocolVersion`, `capabilities`,
230
+ `decisionKinds`, `gatedTools`, `limits`). `packages/cli/examples/bridge-host.mjs`
231
+ is a zero-dependency reference host. See
232
+ [Let the caller decide](README.md#let-the-caller-decide) and
233
+ [Serve your own tools](README.md#serve-your-own-tools).
234
+ - **The keyboard drawer (`Ctrl+G`), and a task label in the header.** The header now
235
+ teaches exactly ONE key: `^G more`. Pressing it slides a right-hand drawer in over
236
+ the current screen and lists EVERY binding top to bottom - the key, a one-or-two
237
+ word keyword, and the sentence that explains it on the selected row - grouped
238
+ COMPOSE / RUN / PANELS / SESSION / VIEW. The keyword column is DERIVED from the
239
+ table rather than fixed, so a keyword that cannot fit makes its row stack instead
240
+ of being cut; paging moves the cursor, so the selected row and its detail line can
241
+ never leave the screen. `d` toggles the status detail row and `r` regenerates the
242
+ label. The same list is reachable without a keyboard: `/keys [query]` opens it
243
+ pre-filtered. The header also says WHAT you are doing: the first prompt of a task is
244
+ compressed by the FAST tier into a few words in the language you wrote in, shown to
245
+ the right of the working directory, and cleared by `/clear` (and rebuilt from the
246
+ restored conversation after `/resume`). `/task` reports what the label says and
247
+ where it came from; `/task refresh` regenerates it. When the fast tier cannot run,
248
+ the label falls back to a prefix of your own words - which cannot lie - and
249
+ `taskSummary.fallback=false` shows nothing instead. New
250
+ `taskSummary.enabled` / `taskSummary.fallback` keys plus `ARAGON_TASK_SUMMARY`. See
251
+ [Keyboard drawer and task label](README.md#keyboard-drawer-and-task-label).
11
252
  - **Acceptance criteria: a plan now has to say how you would know it is done.**
12
253
  `todo_write` takes a second array in the SAME call - one checkable statement per
13
254
  line plus the check the model will run for it - so the steps and the criteria can
@@ -110,6 +351,28 @@ were written.
110
351
 
111
352
  ### Changed
112
353
 
354
+ - **`Ctrl+G` opens the keyboard drawer; the status detail row moved to `d`.** The key
355
+ used to expand the extra status line, and its header hint competed with `^L`, `^P`,
356
+ `^I` and `^K` for room - so what a user saw depended on the terminal width instead of
357
+ on what keys exist. Those five clauses are gone from the header (`^G more` is the
358
+ only one left) and the detail row is now toggled by `d` INSIDE the drawer, which
359
+ also shows its `on`/`off` state. The status detail row's own hint now reads `^G then
360
+ d` rather than `^G less`, because `^G less` no longer does anything. `/help` gained
361
+ the `Ctrl+O` row it had always been missing and now describes `Ctrl+G` as the drawer.
362
+ - **`Ctrl+G` does not take the keyboard from an overlay that is waiting for a
363
+ decision.** With any other overlay open it replaces that overlay with the drawer;
364
+ with `confirm`, `question` or `plan` it stays inert, because those three hold an
365
+ unresolved promise to the agent and only `Esc` settles it - taking the dialog away
366
+ would hide it and leave the run waiting for ever.
367
+ - **`prompts.hint` is retired.** Its only reader was the `^P prompts` header clause,
368
+ which the drawer replaced, and a config key that cannot reach any surface is a lie.
369
+ An older `config.json` still loads (unknown keys are ignored) and `config list` no
370
+ longer prints it. Hand-editing was already the only way to set it: it was never in
371
+ `config set`.
372
+ - **`/keys` and `/task` shadow same-named skills.** Commands registered by the CLI win
373
+ over skill names, so a user with a skill called `keys` or `task` now reaches it as
374
+ `/skill:keys` / `/skill:task`. Both names are deliberately unaliased - an alias would
375
+ multiply the same shadowing.
113
376
  - **BREAKING DEFAULT: the TUI now scrolls with your terminal (`scrollMode: native`).**
114
377
  Until now the interactive TUI ran in the terminal's alternate screen with its own
115
378
  scrollbar, wheel handling and drag-selection. The default is now **native scroll