@aragon-agent/cli 0.6.28 → 0.6.31

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 (80) hide show
  1. package/CHANGELOG.md +2123 -1975
  2. package/README.md +81 -14
  3. package/dist/agent/project-guidance-prompt.js +20 -20
  4. package/dist/agent/reducer.js +47 -2
  5. package/dist/cli.js +169 -56
  6. package/dist/commands/builtins.js +4 -0
  7. package/dist/commands/perf.d.ts +25 -1
  8. package/dist/commands/perf.js +26 -2
  9. package/dist/commands/registry.d.ts +6 -0
  10. package/dist/config/env.js +4 -0
  11. package/dist/config/load.d.ts +3 -1
  12. package/dist/config/load.js +12 -1
  13. package/dist/config/schema.d.ts +27 -0
  14. package/dist/config/schema.js +9 -0
  15. package/dist/config/ui-state.d.ts +9 -0
  16. package/dist/config/ui-state.js +9 -0
  17. package/dist/input/limits.d.ts +20 -0
  18. package/dist/input/limits.js +20 -0
  19. package/dist/input/stdin-filter.js +68 -5
  20. package/dist/ui/App.d.ts +11 -1
  21. package/dist/ui/App.js +155 -67
  22. package/dist/ui/AutocompletePopup.js +1 -1
  23. package/dist/ui/Header.js +7 -2
  24. package/dist/ui/Markdown.js +30 -3
  25. package/dist/ui/PromptInput.js +41 -3
  26. package/dist/ui/ScrollbackShell.d.ts +28 -0
  27. package/dist/ui/ScrollbackShell.js +10 -0
  28. package/dist/ui/Transcript.js +7 -0
  29. package/dist/ui/frame-differ.d.ts +12 -0
  30. package/dist/ui/layout/Gutter.js +1 -1
  31. package/dist/ui/layout/OverlayFrame.d.ts +15 -0
  32. package/dist/ui/layout/OverlayFrame.js +18 -0
  33. package/dist/ui/layout/ScrollIndicator.js +10 -3
  34. package/dist/ui/layout/budget.d.ts +15 -0
  35. package/dist/ui/layout/budget.js +15 -0
  36. package/dist/ui/layout/header-hints.d.ts +9 -0
  37. package/dist/ui/layout/header-hints.js +14 -1
  38. package/dist/ui/layout/status-layout.js +63 -2
  39. package/dist/ui/markdown-blocks.d.ts +26 -5
  40. package/dist/ui/markdown-blocks.js +110 -33
  41. package/dist/ui/overlays/HelpOverlay.d.ts +1 -1
  42. package/dist/ui/overlays/HelpOverlay.js +21 -3
  43. package/dist/ui/screen.d.ts +16 -0
  44. package/dist/ui/screen.js +32 -0
  45. package/dist/ui/scrollback/budget.d.ts +43 -0
  46. package/dist/ui/scrollback/budget.js +44 -0
  47. package/dist/ui/scrollback/drain.d.ts +32 -0
  48. package/dist/ui/scrollback/drain.js +64 -0
  49. package/dist/ui/scrollback/exit-summary.d.ts +23 -0
  50. package/dist/ui/scrollback/exit-summary.js +25 -0
  51. package/dist/ui/scrollback/limits.d.ts +25 -0
  52. package/dist/ui/scrollback/limits.js +25 -0
  53. package/dist/ui/scrollback/live-preview.d.ts +26 -0
  54. package/dist/ui/scrollback/live-preview.js +106 -0
  55. package/dist/ui/scrollback/renderer.d.ts +38 -0
  56. package/dist/ui/scrollback/renderer.js +158 -0
  57. package/dist/ui/scrollback/scrollback-writer.d.ts +74 -0
  58. package/dist/ui/scrollback/scrollback-writer.js +263 -0
  59. package/dist/ui/scrollback/settled-boundary.d.ts +42 -0
  60. package/dist/ui/scrollback/settled-boundary.js +106 -0
  61. package/dist/ui/scrollback/use-scrollback-view.d.ts +92 -0
  62. package/dist/ui/scrollback/use-scrollback-view.js +263 -0
  63. package/dist/ui/scrollback/writer-shapes.d.ts +61 -0
  64. package/dist/ui/scrollback/writer-shapes.js +114 -0
  65. package/dist/ui/stdout-frame-writer.js +12 -2
  66. package/dist/ui/terminal-plan.d.ts +52 -0
  67. package/dist/ui/terminal-plan.js +49 -0
  68. package/dist/ui/transcript-text.d.ts +11 -0
  69. package/dist/ui/transcript-text.js +21 -7
  70. package/dist/ui/use-startup-notices.d.ts +12 -1
  71. package/dist/ui/use-startup-notices.js +28 -2
  72. package/dist/ui/use-wheel-routing.d.ts +10 -7
  73. package/dist/ui/use-wheel-routing.js +11 -6
  74. package/package.json +2 -2
  75. package/skills/project-indexer/SKILL.md +163 -163
  76. package/skills/project-indexer/templates/cleancode-overrides.md +46 -46
  77. package/skills/project-indexer/templates/cleancode-template.md +94 -94
  78. package/skills/project-indexer/templates/config-template.md +69 -69
  79. package/skills/project-indexer/templates/index-template.md +129 -129
  80. package/skills/skill-creator/SKILL.md +96 -96
package/CHANGELOG.md CHANGED
@@ -1,1975 +1,2123 @@
1
- # Changelog
2
-
3
- All notable changes to `@aragon-agent/cli` are documented here. Entries at
4
- `0.4.x` and earlier refer to the package by its former name, `@argon-agent/cli`
5
- — those releases really were published under that name, so they are left as they
6
- were written.
7
-
8
- ## Unreleased
9
-
10
- ### Added
11
- - **`Ctrl+L` branches this session into a new terminal - and `Ctrl+R` now
12
- redraws (KEY BINDING CHANGE).** One keystroke opens a PowerShell (Windows) or
13
- bash (macOS/Linux) window in the session's current `/cwd`, running
14
- `aragon --resume <id>` there: the child derives its own id and only ever writes
15
- its own file, so the TUI you branched from keeps running untouched. The session
16
- is force-flushed to disk BEFORE anything is spawned, because the child reads
17
- that file first and the normal checkpoint is debounced. Works mid-run too - the
18
- child inherits the last settled turn. Platforms: a new Windows Terminal tab
19
- (`wt -d <cwd> ... --`), a new console window (`cmd /c start`), a Terminal.app
20
- window via `osascript`, or the first available Linux emulator; each rung carries
21
- the working directory itself, because `--resume` does not restore one. No
22
- terminal (SSH, container) prints a pasteable `aragon --resume <id>` instead of
23
- failing silently. `ARAGON_BRANCH_COMMAND` / `ARAGON_BRANCH_SHELL` override what
24
- runs and which shell; presses are throttled to one window per two seconds and a
25
- failed attempt never starts the cooldown. New `/branch` is the same code path.
26
- The header (top right) now shows `^L branch` next to `^I Index` and `^G more`,
27
- spending a fixed cell budget: `^I` therefore appears from 52 columns
28
- (collapsed) / 62 columns (expanded) instead of 72, and on a very narrow terminal
29
- the branch hint degrades to the bare `^L` before it is dropped.
30
- **Breaking:** the redraw previously bound to `Ctrl+L` moved to `Ctrl+R` - same
31
- behaviour, same overlay guard (the settings screen keeps its own `Ctrl+R`).
32
-
33
- - **A TUI session now has an id, and another terminal can continue it.** Every
34
- interactive session is written to `~/.aragon-agent/sessions/<id>.json` as it
35
- runs and shows that id at the right end of the status bar (`id a4k2m9`, or
36
- `a4k2m9` on a narrow terminal - the id itself is never truncated). Checkpoints
37
- are written after the first frame, after every settled turn (debounced), on
38
- `/session pin`, and once on exit; each one is an atomic replace, because a
39
- second terminal may be reading. `aragon --resume <id|path>` continues a saved
40
- session under a NEW id and never writes the file it came from, so branching
41
- while the original TUI keeps running is safe - including
42
- `aragon exec --resume <tui-id>`, which now writes a session of its own instead
43
- of overwriting yours. `aragon --save <id>` names this run's id and pins it
44
- against `sessions prune`. New `/session [copy|pin]` reports the id, the file and
45
- the branch parent, copies the resume command through the same clipboard door as
46
- `/copy` (OSC 52 included), and pins the file. A checkpoint taken mid-tool is
47
- repaired on the saved copy only, so resuming it cannot produce an illegal
48
- history, and a tool card that was still running is settled so nothing spins
49
- after a resume. `aragon -p` writes one checkpoint at exit: context only, no
50
- transcript, because nothing was displayed.
51
-
52
- - **A model profile can be verified where it is written.** The profile editor
53
- gains two action rows: **Test connection** (`Ctrl+R`) and **Discover models**
54
- (`Ctrl+F`). A test is one real completion against the UNSAVED draft - the
55
- provider, model, base URL and the key `keep`/`replace`/`shared` actually
56
- resolves to - and it answers with a verdict (`OK 812 ms | 18 in / 4 out`) or a
57
- classified failure plus the one thing to fix (`Key rejected by anthropic (HTTP
58
- 401).` / `Fix: edit API key, or set Credential mode to Shared.`). A discovery
59
- lists what the key can really reach, so the model is picked instead of typed.
60
- Both run against the draft, cost at most one short request (discovery costs no
61
- tokens), never retry, and never write `ModelRegistry`'s five-minute discovery
62
- cache. Every result is bound to a target fingerprint, so an `OK` can never
63
- describe a model, endpoint or key the user has since edited. The progress line
64
- is static text: no new animated site, so `single-spinner` still holds.
65
-
66
- ### Changed
67
-
68
- - **`aragon sessions list` gained an `origin` column** and shows TUI checkpoints,
69
- which are managed files now. `sessions prune` defaults to `--origin exec`, so it
70
- behaves exactly as before for every file that existed before this release; the
71
- new `--origin tui|all` selects the other writer. `prune` never deletes a pinned
72
- session or one whose owner process is still alive, and `--all` now means only
73
- "also consider files with no session metadata" - it can no longer reach a TUI
74
- checkpoint. `/resume` inside the TUI moves the session to a new id and restarts
75
- the spend counter, because a new id's file must not bill the previous
76
- conversation.
77
-
78
- ### Fixed
79
-
80
- - **A large paste arrived as runs of digits the moment it was sent - on screen,
81
- in the model's input, in the prompt history and in the session file.**
82
- `sanitisePaste` deleted the C0/C1 *introducer byte* and kept the parameter
83
- bytes, so `ESC[38;2;18;52;86m` reached the transcript as the literal text
84
- `[38;2;18;52;86m`. The composer collapses a large paste into
85
- `[Pasted text #N +20 lines]`, so nothing was visible until submit expanded
86
- it: a measured 20-line coloured log produced 80 runs of digits, and the same
87
- string was what `/context`, the ↑ history and the session checkpoint stored.
88
- Cleaning is now SEQUENCE-based and has one owner (new
89
- `input/control-sequences.ts`, ASCII-only, no new dependency): a complete
90
- escape goes as a unit, a truncated one loses its introducer and parameters
91
- but never the character after them, an unterminated OSC/DCS/APC/PM/SOS goes
92
- to the end of its line and never across it, and bare text that merely looks
93
- like a sequence body (`[38;2m` with no ESC) is left exactly as it is.
94
- `tools/terminal-output.ts`'s `stripAnsi` becomes a thin wrapper over the same
95
- syntax, so a tool row and a paste cannot drift apart (both directions are
96
- pinned by a cross-check test, and `terminal-output.test.ts` is unchanged).
97
- A **single-line** paste carrying ESC - which `classifyChunk` sent down the
98
- keystroke path with no cleaning at all - is now framed and cleaned like any
99
- other paste; measured on the real component that path either put a live ESC
100
- plus `[38;2;18;52;86m` into the draft or threw an uncaught `TypeError` inside
101
- Ink's key parser. win32-input-mode records inside a frame are translated
102
- rather than deleted, so a `?9001h` paste yields its characters instead of 15
103
- visible digits per key (or, with cleaning but no translation, nothing at all).
104
- - **A pasted TAB-indented block or TSV table lined up differently before and
105
- after sending.** One byte had three readings: the composer painted
106
- four-column tab stops, the transcript handed the raw `\t` to the terminal
107
- (eight), and the exit replay counted code units. New `ui/display-tab.ts`
108
- expands a TAB by DISPLAY column (`string-width`, so a `CJK<TAB>X` line lands
109
- where the terminal paints it); the composer's stop is the terminal's eight, and
110
- `UserEntry`, `QueuedEntry` and the exit replay expand at paint time only -
111
- the stored payload keeps its `\t`, so the model still receives the original
112
- indentation. `wrappedRows` counts display columns too (with an ASCII fast
113
- path, without which a 10 000-entry transcript timed out), because an estimate
114
- that reads a TAB as one column keeps the row short while the painted row
115
- grows - the under-estimate that file names as the unsafe direction.
116
- - **`Ctrl+L` opened a window that failed with a PowerShell `ParserError` on
117
- Windows.** The script handed to `-EncodedCommand` was the argv joined with
118
- quotes but with no call operator, and PowerShell reads a quoted string in command
119
- position as an *expression*: with node installed under `C:\Program Files\...`
120
- the child was never created - `UnexpectedToken` at column 36 - while the parent
121
- TUI still reported that the window opened. `powerShellScript` now emits
122
- `& <argv...>`, which is the only form that runs a word of any shape as a
123
- command. The failure fallback follows: it is now two labelled lines
124
- (`PowerShell:` / `cmd.exe :`), because one line cannot be correct for both
125
- parsers - and the `PowerShell:` line is byte-for-byte the script the window
126
- receives. A leading `@` can no longer be left bare either, since `@name` is
127
- PowerShell's splat operator and silently swallows an argument. New
128
- `powershell-script-parse.test.ts` hands the generated scripts to the real
129
- Windows PowerShell parser (and pins the old shape to its exact error), so this
130
- class of defect has a guard instead of a green suite.
131
- - **A conversation saved mid-tool could be resumed into an illegal history.**
132
- A `tool_call` with no matching `tool_result` makes the next request fail. The
133
- checkpoint writer closes every dangling call on the copy it writes and re-checks
134
- sendability before replacing the file; a history it cannot make sendable is not
135
- written at all, so the previous good checkpoint survives.
136
- - **A checkpoint could carry a tool card that never stopped spinning.** A card
137
- saved while its tool was running kept `ToolCard` animated and kept the
138
- transcript's settled boundary from ever passing it, which re-rendered the tail
139
- on every frame for the rest of the resumed session. Cards are now settled on the
140
- saved copy only, which leaves `/save` (an explicit export you are watching) and
141
- its documented behaviour untouched.
142
- - **`aragon exec --resume <tui-id>` overwrote the session it resumed.** The
143
- write-back id preferred the file's own metadata, so exec took the TUI's id and
144
- replaced the checkpoint - silent, unrecoverable loss of the conversation still
145
- running in the other terminal. A TUI file now always yields a fresh id, and the
146
- same rule covers `--session-id`, which used to return the typed id verbatim.
147
- - **`sessions prune --origin tui --yes` could delete a running session's
148
- checkpoint.** The `live` marker was written by the TUI but the process lock it
149
- is checked against was only ever taken by `exec`, so the liveness guard answered
150
- "nobody owns this" for exactly the files it was written for. The TUI checkpoint
151
- writer now holds the session lock for as long as it owns the file, releases it
152
- on exit, and moves it when `/resume` rotates the id.
153
- - **`aragon --save <id>` said "already exists" for an id that does not exist.**
154
- A session directory that cannot be written (read-only home, full disk) was
155
- reported as a name collision, which sends the user to `--resume <id>` and then
156
- to "no such session". It now names the directory instead.
157
- - **A hand-written `/save` file resumed as if it were a branch of itself.**
158
- `--resume ./notes.json` recorded `notes` as `meta.branchOf` and announced it as
159
- the parent session, though it is not a session id. Lineage is now recorded only
160
- for ids this CLI minted, and a collision while deriving a branch id re-mints the
161
- suffix instead of dropping the branch shape.
162
- - **`/session` answered `pinned no` right after `/session pin`.** The file was
163
- marked and the report was not. The identity is now updated with the pin.
164
- - **An id rotation could write one turn into the old id's file.** For the window
165
- between `/resume` claiming a new id and the next render, the writer still aimed
166
- at the previous file while recording the new parent. The rotation now republishes
167
- the writer's id in the same step.
168
- - **`/clear` did not clear the context.** It reset the visible transcript and the
169
- todo panel and left `agent.state.messages` - the history that is actually sent,
170
- billed, measured by `/context` and compacted - completely untouched. A long
171
- session looked restarted and behaved like it had never been cleared: the next
172
- request still carried every old turn, so the model remembered the previous task
173
- and the occupancy gauge did not move. `/clear` now runs the one real
174
- new-conversation boundary (`controller.resetConversation()`) in the only safe
175
- order: interrupt the running turn (`forceStop`, which also drops the events the
176
- unwinding engine keeps emitting), wait for it to settle inside a bounded budget,
177
- archive the old conversation, drop both queues, then clear history, compaction
178
- identity, context measurement and skill activation frames. A reset that cannot
179
- stop the engine clears **nothing** and says so.
180
- - **`/reset`'s "cancelled N queued messages" notice is preserved.** `/reset` is now
181
- an alias of `/clear`, and the count is read **before** the queues are cleared -
182
- read afterwards it would always be zero, which is a notice that lies.
183
-
184
- ### Changed
185
-
186
- - **`/clear` means "start a new conversation"; the old behaviour moved to
187
- `/clear screen`.** The previous semantics (wipe the transcript and the todo
188
- panel, keep the history and the queue) are still available, spelled explicitly,
189
- and are byte-identical to what `/clear` used to do. `/clear` no longer refuses
190
- while a run is in flight: it interrupts, which removes the dead end where the
191
- only command that worked mid-run was the one that kept the context.
192
- - **`/reset` is an alias of `/clear`, one implementation.** Two bodies could drift
193
- back into "one clears the screen, one clears the session"; one body cannot.
194
- - **The conversation being cleared is archived first**, to
195
- `~/.aragon-agent/sessions/cleared/cleared-<yyyyMMdd>-<HHmmss>-<pid>.json`
196
- (newest 20 kept, `<ARAGON_HOME>` honoured). The path is reported in the
197
- transcript and restores with `/resume <path>`. The file carries no `meta`, so
198
- `sessions list` / `sessions prune` and `--continue` are unaffected; a failed
199
- archive only logs and never blocks the clear.
200
-
201
- - **`Ctrl+I` was dead on Windows.** The win32-input-mode translator only
202
- recognised the chord when the console reported it as `VK_TAB` (vk 9) with
203
- ctrl - a record shape no real console was ever probed for. Physical
204
- Ctrl+I keeps the LETTER vk (0x49, the same Ctrl+letter rule that reports
205
- Ctrl+A as vk 65 and Ctrl+J as vk 74 in the Shift+Enter probe captures)
206
- while `char` carries the Tab byte, so the record fell through to the
207
- generic Ctrl-combo branch, came out as plain Tab, and the keybinding
208
- silently did completion instead of opening the index confirm. The
209
- translator now tells Ctrl+I apart from Tab by `char === 0x09 && ctrl`
210
- whatever vk it rides, with Shift still mapping to Shift+Tab; both record
211
- shapes are pinned by regression rows in `win32-input-mode.test.ts`.
212
-
213
- ### Team dispatch supervisor (team-overseer)
214
-
215
- - **A time ceiling on a subagent is now an inspection trigger, not a death
216
- sentence.** With the supervisor on (new key `team.overseer`, default
217
- `true`, needs the fast tier), two soft timers per child - event silence at
218
- `idleTimeoutMs` and a wall clock at `subagentTimeoutMs` (or 5 minutes when
219
- no ceiling is configured) - fire one bounded fast-tier inspection. The
220
- supervisor decides wait / nudge / replace / abandon; the runtime applies,
221
- funnelling every lifecycle change through the worker loop that awaits the
222
- child, so a replacement is never built for nobody.
223
- - **A nudge is steering; a replace is a rebuild with an amended brief.**
224
- Guidance is queued at core's safe top-of-loop checkpoint; a replacement
225
- carries the supervisor's reason, the dead child's `filesTouched`, and the
226
- original brief bounded to the same prompt budget. A post-mortem inspection
227
- diagnoses a child that died silently (watchdog death, stream loss) and may
228
- re-assign it once.
229
- - **Budgets are structural, not settings**: 4 inspections and 1 replacement
230
- per child per dispatch; past them the child runs under its own (lengthened
231
- while supervised) idle watchdog and the supervisor goes quiet. An
232
- unparseable or failing supervision call always becomes `wait` -
233
- supervision must never kill a healthy child.
234
- - Every applied decision is visible three ways: the report's Supervisor
235
- section (`[silence 30s] a1: nudge - ...`), the team panel and the `-p`
236
- stderr line, and a `debug` log event. `aragon exec` emits it as an
237
- additive `team` subtype `overseer` (`EXEC_SCHEMA_VERSION` unchanged).
238
- - New command `/team overseer on|off` (refused mid-dispatch, like every
239
- other `/team` setting) and new config key `team.overseer`, also settable
240
- as `aragon config set team.overseer false`. With the supervisor off - or
241
- no fast tier - every path is byte-for-byte the pre-feature regime:
242
- `subagentTimeoutMs` is a hard abort and silence is the child's own
243
- watchdog.
244
-
245
- ### 启动与缩窗不再误报 Frame diff lost sync
246
-
247
- - 启动即现的 "Frame diffing lost sync with the terminal and fell back to a
248
- full repaint … `--no-diff-render`" 警告已消除:Ink 挂载/卸载经渲染 stdout 写出的
249
- 光标隐藏/恢复序列(`ESC[?25l` / `ESC[?25h`)由帧写入器直接透传——不进差分、
250
- 不失效缓存、不计 fallback,也不再消耗会话的首写豁免。启动序列恢复
251
- "种子帧透传 → 首帧全量 → 之后纯 diff"。
252
- - 终端高度缩小(拖小窗口、改字号、面板重排)不再触发同一条警告:Ink 的
253
- `clearTerminal` 整帧写入被差分器识别为 Ink 自身写法,透传并失效缓存但不计
254
- fallback;该控制写同样不消耗首写豁免,其后的首个 log-update 帧仍按种子帧豁免,
255
- 下一帧自然全量重绘。
256
- - `/perf` 的 `fallbacks` 重新从 0 起步,"健康会话 fallbacks === 0" 的诊断信号
257
- (AC-11)恢复可判。
258
-
259
- ### Consistent TUI styling
260
-
261
- - Interactive sessions always use Unicode and default to truecolor, including
262
- PowerShell sessions without terminal or UTF locale markers. Ink's actual color
263
- output follows the same policy, with renderer state restored on exit.
264
- - Explicit color opt-outs keep Unicode borders and icons; the opening logo now
265
- depends only on available space. Headless terminal detection is unchanged.
266
- - Reloading model settings preserves session display preferences. Syntax
267
- highlighting follows the same color policy, and status icons use consistent
268
- single-column glyphs to prevent narrow-frame overflow.
269
- - Hiding the editor caret while scrolling preserves draft geometry, preventing
270
- a layout update loop when entering text above the full startup logo.
271
- - Register exit cleanup before mounting the application, so immediate exits and
272
- initial render failures restore terminal and color state reliably. Failed
273
- mounts no longer mark an auto-installed build as healthy.
274
-
275
- ### Model context window
276
-
277
- - Discover context limits at startup and after connection changes without blocking
278
- input. The status bar and compaction use the discovered limit; `/context` names
279
- API versus catalog metadata. Unknown windows show `?`, not a fabricated 128k
280
- capacity or percentage. Manual `contextWindow` overrides retain precedence.
281
- - Window overrides now apply to tail relief and compaction progress as well as
282
- the gauge. Workers on different models/endpoints do not inherit the lead's
283
- override. API-key changes refresh metadata, and ending a session cancels its
284
- pending model queries. `/context` also hides unknown capacities/percentages.
285
-
286
- ### TUI 输入与排队可靠性
287
-
288
- - Shift+Enter 与粘贴按输入顺序处理;普通 Enter 保持弹层兼容,混合输入最多提交一次,
289
- 余稿及拒绝提交的正文保留。结束输入流时完整排空,不泄漏内部控制标记。
290
- - 超限粘贴拒绝同一外部输入块的正文和 Enter,避免误提交旧稿;未闭合粘贴继续按正文消费。
291
- - 拖选后仅 Ctrl+C 复制;文字变化与丢失鼠标释放时取消无效高亮,避免卡住视口。
292
- - 固定 Queue 状态行按消息 ID 回执更新,支持窄屏、暂停与多条消息计数。
293
- - 保存和退出保留所有未发送全文;恢复先验证存档,旧 queued 作为警告展示且不自动重放。
294
-
295
- ### Removed
296
-
297
- - Removed the inline terminal UI and its automatic downgrade rules. Interactive
298
- sessions now always use the full-screen UI; small windows show a resize notice.
299
- - Removed `--fullscreen`, `--no-fullscreen`, `ARAGON_FULLSCREEN`, and the
300
- `fullscreen` config setting. Old flags are rejected and old stored preferences
301
- are ignored. Headless commands and piped input retain their existing behavior.
302
- - Interactive-only screens now reject non-TTY input/output with exit code 2.
303
-
304
- ### Added
305
-
306
- - **`system/init` now lists what this build can do.** A new optional
307
- `capabilities: string[]` field, carrying `["interrupt"]` for this release.
308
-
309
- A wrapper cannot tell by observation whether an interrupt will settle
310
- gracefully or kill the process outright, and guessing wrong costs the user
311
- their conversation. Rather than have callers compare version numbers — which
312
- breaks on forks, prereleases, and version shapes nobody anticipated — the
313
- build now says so itself, in an event that already arrives on every start.
314
- Absent on older builds, and absence should be read as "no", never as
315
- "probably".
316
-
317
- Additive, so `schemaVersion` does not move. Consumers that do not know the
318
- field ignore it, exactly as the schema contract asks.
319
-
320
- - **The composer understands pasting.** Until now it did not have the concept at
321
- all: every byte the terminal delivered on stdin was a keystroke, and a
322
- clipboard block is thousands of bytes arriving in an unpredictable number of
323
- chunks. Three separate failures fell out of that one omission, and on Windows
324
- you hit all three at once.
325
-
326
- A carriage return that landed alone in a chunk **sent the half-finished
327
- message** — a fifteen-line paste could arrive as a dozen separate messages,
328
- interleaved with the agent's replies. A carriage return in the middle of a
329
- chunk was inserted into the draft as a literal `\r`, which the terminal obeyed
330
- by **overwriting the row it was drawn on**. And a paste that survived both grew
331
- the composer to the height of the pasted text, pushing the transcript out of a
332
- frame whose row accounting still believed the composer was three rows tall.
333
-
334
- Pasting now works the way it reads:
335
-
336
- - **A paste never submits.** Line breaks become buffer newlines, exactly one
337
- per source line break, `CRLF` and lone `CR` alike.
338
- - **A short paste appears verbatim.** At or below 6 lines and 400 characters it
339
- is inserted as-is, indentation intact, and renders exactly like typed text.
340
- - **A large paste collapses to a placeholder** — `[Pasted text #1 +218 lines]`
341
- — which occupies one line, carries its own size in its label, deletes as a
342
- single unit (one `Backspace`, and `Ctrl+W` / `Ctrl+U` / `Ctrl+K` cannot leave
343
- half of one behind), and expands back to the full text the moment you send.
344
- The model receives every byte; the composer never has to draw them.
345
- - **No pasted byte is executed as a terminal control.** `\r`, `\x1b`, NUL, DEL
346
- and the C1 range are stripped from the payload — never from your keystrokes,
347
- so `Ctrl+A` and Escape are untouched.
348
- - **The composer has a ceiling.** A draft of any length occupies at most 3 / 6 /
349
- 10 rows depending on terminal height, scrolls internally to keep the caret
350
- visible, shows how many rows are hidden above and below, and reports its true
351
- height to the layout budget. Four hundred presses of `Shift+Enter` were
352
- already able to destroy the frame; they no longer are.
353
- - **A submitted message cannot make the transcript unresponsive.** A user entry
354
- renders at most 40 rows plus a `... +N more lines` tail. The full text is kept
355
- and the exit transcript is uncapped.
356
-
357
- On terminals that support it, detection is **exact**: aragon asks for bracketed
358
- paste (`DECSET 2004`) in full-screen mode and reads the markers the terminal
359
- wraps your paste in. Where that is unavailable — inline mode, an old Windows
360
- console — a heuristic on chunk shape catches the same cases, and it needs no
361
- cooperation from the terminal at all. Both feed one code path.
362
-
363
- Turn it off with `--no-paste`, `ARAGON_PASTE=0`, or
364
- `aragon config set paste false`; the input path is then byte-for-byte what
365
- 0.6.2 did.
366
-
367
- ### Fixed
368
-
369
- - **The right-edge scrollbar no longer disappears while output streams.** The
370
- differential renderer ended every repainted row with `CSI K` (erase to end of
371
- line). The scrollbar fills the last column of every viewport row, and on
372
- terminals that keep the cursor on the last column after writing it (Windows
373
- console hosts) that erase blanked the cell that had just been drawn, so the
374
- scrollbar vanished whenever a row was redrawn. Rows that already fill the
375
- terminal width are now repainted without the erase; narrower rows are
376
- unchanged. `--no-diff-render` was never affected. `/perf` gained a `scrollbar:`
377
- line for diagnosing visibility and drag problems.
378
-
379
- - **`{"type":"interrupt"}` now really does settle the run — on every platform.**
380
- The stdin interrupt frame used to raise a SIGINT at our own process. On POSIX
381
- that reaches the runner's terminator and the run winds down properly: it
382
- breaks out of the loop, persists the session, and emits `result` with the
383
- usage and cost. On Windows, Node does not deliver a self-directed SIGINT to
384
- its own handlers — the process simply terminated with exit code 1, having run
385
- none of it.
386
-
387
- For anyone using `aragon exec` interactively that difference was invisible,
388
- because an interrupt there is usually the last thing that happens. It is not
389
- invisible to a **wrapper** that interrupts a turn and then respawns on the same
390
- `--session-id` to keep the conversation going: on Windows it silently got back
391
- a session missing everything since the previous save. `exec/index.ts` has
392
- carried a comment about that caller for some time ("PERSIST FIRST, ANNOUNCE
393
- SECOND … a caller that reads `result` and immediately respawns with the same
394
- `--session-id` must not race the writer"), so the intent was there; the path
395
- just could not be reached on Windows.
396
-
397
- The frame now calls the runner directly instead of going through a signal.
398
- POSIX behaviour is byte-for-byte unchanged — the same code runs, with the same
399
- `stopReason`, the same exit code 130, and the same two-second window in which a
400
- second signal still forces an immediate exit.
401
-
402
- - **A pasted API key is stored correctly again.** Ink broadcasts each stdin chunk
403
- to every mounted input handler, so the settings screen, the question overlay
404
- and the plan-review feedback field all see what the composer sees. Without
405
- this, a ~100-character key pasted into Settings would have been stored wrapped
406
- in invisible framing bytes, masked on screen by the password dots, and would
407
- have failed to authenticate on every request afterwards with nothing anywhere
408
- explaining why.
409
-
410
- - **`--no-mouse` still writes no mouse sequence.** Paste support installs a
411
- stdin filter, and `paste` defaults to on, so the filter now exists in sessions
412
- that disabled the mouse. Whether the mouse is on is a separate question from
413
- whether the stream is wrapped, and the four places that used to conflate them
414
- now ask the right one: `aragon --no-mouse` writes no `?1000h`, no `?1006h` and
415
- no `?1002h`, leaves your terminal's own click-drag selection alone, and reports
416
- `/mouse` as off — while pasting still works.
417
-
418
- ### Changed
419
-
420
- - **The run status row moved above the input box and carries the `steer` hint.**
421
- While a run is active, the spinner, the phrase and `steer / interrupt / exit`
422
- share one row directly above the input, in place of the idle hint row below it
423
- (the footer keeps its height). It scrolls with the input; when it leaves the
424
- viewport the fixed bottom row shows the animation instead, so there is never
425
- more than one spinner. Terminals under 20 rows and `--no-hints` keep the old
426
- fixed row. The update notice is also held back for the whole run.
427
-
428
- - **Auto-compaction no longer reports compactions that did not happen.** A long
429
- unattended run used to print a red `context not compacted #2: nothing_to_drop`
430
- card into the transcript. The card was accurate; nobody had asked for it.
431
- Auto-compaction evaluates itself at every turn boundary, and a checkpoint that
432
- looks at the history and finds nothing worth dropping has changed nothing,
433
- called nothing and spent nothing — a smoke alarm announcing "no fire" twice an
434
- hour, in error colours, with a machine reason attached.
435
-
436
- A compaction now announces itself **if and only if it commits to doing work**.
437
- That is a position in the control flow, not a list of reason strings: everything
438
- decided before the first spend is silent, and everything decided after it —
439
- including every failure, the truncation fallback, an Esc, and a host fault — is
440
- as loud as it ever was. A declined `pressure` checkpoint is now
441
- indistinguishable from a turn on which the threshold was never crossed.
442
-
443
- **`/compact` and overflow recovery are unchanged.** A compaction you asked for
444
- is owed an answer, and a reactive compaction running after the provider has
445
- already refused your request is the one moment the diagnosis matters most. Both
446
- still report on every outcome.
447
-
448
- **Quiet, not unrecorded.** Every attempt is still written to the diagnostic log
449
- (its `compaction` line gains an `announced` field, so `announced: false` is one
450
- grep), still charged to the anti-loop guards, and still counted by
451
- `/compact status` — which now reads
452
- `This session: 1 compaction (2 checkpoints declined), 94.2k tokens reclaimed`
453
- when checkpoints declined, and is byte-identical to before when none did. If
454
- declines keep happening, the self-disable notice still says so out loud, once,
455
- naming the cause and the remedies. Card numbers are also contiguous again: `#N`
456
- counts compactions that ran, not checkpoints that fired.
457
-
458
- **Behaviour change for wrapper scripts.** A declined `pressure` compaction no
459
- longer emits a `{ type: 'compaction', subtype: 'start' | 'end' }` pair on the
460
- `--output-format json` stream, and no longer prints `[compaction] skipped:` on
461
- `-p` stderr. The pairs stay balanced, so only consumers counting *attempts* are
462
- affected; the diagnostic log reports those more accurately.
463
-
464
- ### Added
465
-
466
- - **Context compaction, hardened — the trigger now counts the whole history, the
467
- recent turns can no longer wedge a run, sub-agents get the same protection, and
468
- a compaction is no longer irreversible.**
469
-
470
- **The trigger was systematically late.** It fired on the provider's reported
471
- usage for the PREVIOUS request, read at a checkpoint that runs AFTER the current
472
- turn's tool results were appended — and tool results are the largest single
473
- increment in this loop (100 KB per call, several per turn, ~25 k tokens each).
474
- A session reading 88 % could be sending 110 %. Occupancy is now the measured
475
- base PLUS an estimate of everything appended since that measurement, and the
476
- status gauge reads the same number the trigger does, on every turn — including
477
- the turns where a guard declines to compact, which is exactly when the number
478
- matters most.
479
-
480
- **The recent turns can no longer wedge a run.** Every previous rung of the
481
- ladder operated on the head of the conversation. When the RETAINED tail was what
482
- did not fit, each rung reported success or "nothing to drop" while the history
483
- stayed un-sendable and the run died. Oversized `tool_result` bodies inside the
484
- retained turns are now clipped, oldest first, only when the alternative is a
485
- request the provider cannot accept. Nothing is removed — no message, no
486
- tool-call id, no role — so a valid history stays valid. Every clip is announced
487
- in the text the model reads, at the exact place the data went, and on the card,
488
- in `/compact status`, on the JSON stream and in the archive.
489
-
490
- **Sub-agents compact their own history** (`compaction.subagents`, default
491
- `true`). A `task` child is bounded by 24 turns and fifteen minutes, and when one
492
- overflowed its entire history was discarded and the dispatch reached the lead as
493
- one partial sentence. Children run under tighter bounds than the lead: two
494
- retained turns, at most two compactions, the fast tier preferred, and failure
495
- forced to truncate because nobody is watching a background worker. No transcript
496
- card — the lead's transcript describes the lead's context. Set
497
- `compaction.subagents: false` to restore the previous behaviour exactly.
498
-
499
- **A compaction is no longer irreversible** (`compaction.archive`, default
500
- `true`). The dropped messages are written verbatim to `~/.aragon/compaction/`
501
- before the splice is adopted. `/compact history` lists this run's archives,
502
- `/compact show <n>` prints one's metadata and summary — never message bodies.
503
- The directory is shared by every `aragon` on the machine, so files carry a
504
- per-process run id and retention is two-tier: 20 files per run by count, plus a
505
- seven-day sweep across all runs, so one session can never prune a live
506
- neighbour's work. Writing is best-effort: a failure is logged and never affects
507
- the compaction. This is deliberately **not** undo — restoring an over-full
508
- history restores the condition that triggered the compaction.
509
-
510
- **The long moment is legible.** The live card counts elapsed seconds and says
511
- `esc to cancel`, which `AgentController.abort()` has always made true and no
512
- surface has ever stated. A compaction that could only clip says so and names no
513
- model, because none was called.
514
-
515
- Two new config keys (`compaction.subagents`, `compaction.archive`), two new
516
- environment variables (`ARAGON_COMPACTION_SUBAGENTS`,
517
- `ARAGON_COMPACTION_ARCHIVE`), two new `/compact` verbs (`history`, `show <n>`),
518
- and two new settings-screen rows. No flags were added.
519
-
520
- - **Context compaction — a long run no longer dies when the window fills, and it
521
- is ON by default.** A session's conversation only grows: nothing anywhere
522
- removed a message, so a 40-turn tool-heavy run had exactly one ending available
523
- to it once the context window filled — an HTTP 400 and a banner reading
524
- *"Context length exceeded — start a new conversation with /reset."* The
525
- product's answer to running out of context was "throw away everything you were
526
- doing."
527
-
528
- Now, at **90 %** occupancy, the run pauses at a turn boundary, hands the older
529
- part of the history to a summarizer, and swaps it for a structured record. Your
530
- original task stays **verbatim**, the most recent turns stay **verbatim**, and
531
- the run continues in the same loop with the same tools, the same todo list and
532
- the same working directory.
533
-
534
- **This changes behaviour for existing users, in the direction of not dying.**
535
- `--no-compaction`, or `compaction.enabled: false`, restores the old behaviour
536
- exactly: no manager is constructed, nothing is subscribed, no registry is
537
- allocated, and a full window ends the run as it did before. Both flag forms are
538
- declared, so a stored `false` survives a run that passes no flags.
539
-
540
- Three ways it fires. **Proactively** on measured occupancy — the case that
541
- happens almost every time. **Reactively** when the provider returns
542
- `context_overflow` anyway, because the model is not in the static window table
543
- or you are pointed at a proxy in front of a smaller one: one compaction, one
544
- re-send, run continues. And **manually**, via `/compact`, which runs
545
- immediately when idle and queues to the next turn boundary when running.
546
-
547
- The trigger has two terms. 90 % of a 200 k window leaves 20 k, which is plenty;
548
- 90 % of a 32 k window leaves 3.2 k, which is less than a single `max_tokens` of
549
- 8192 — the request is already impossible. So compaction also fires whenever the
550
- remaining headroom drops below one full response.
551
-
552
- **The default summarizer is your own model, and that costs real money.**
553
- `compaction.useFastTier` is `true`, but it is a *preference*: the fast tier is
554
- off unless you configured it, so out of the box the first compaction is a
555
- ~30 k-token call on the session's own model. It happens at most 5 times per
556
- run, the alternative is losing the run entirely, and the spend is visible — in
557
- the session total, priced with the summarizer's own table, and reported as
558
- `pricing unknown` rather than `$0.00` when that table has never seen the model.
559
- Configure the fast tier to make it cheap.
560
-
561
- Summarization fails sometimes. The ladder is one retry (on your **main** model
562
- if the first attempt used the fast tier), then a **truncation** that says so on
563
- the card, on the JSON event stream, and inside the block the model itself
564
- reads. `compaction.onFailure: "stop"` makes the other trade.
565
-
566
- Four guards stop it looping on your money: never two on consecutive turns,
567
- never more than 5 per run, a progress requirement, and a self-disable after two
568
- no-progress attempts with a notice naming the cause and the fix. The guards are
569
- **trigger-aware** — the reactive path stays armed even after a self-disable,
570
- because a compaction answering a request the provider has already refused is
571
- the difference between a live run and a dead one.
572
-
573
- Three UI surfaces: `Compacting context…` on the activity row while it runs, a
574
- `compacting` chip plus threshold-aligned gauge colours as state, and a
575
- transcript card with the before/after counts, the summarizer, the duration and
576
- an expandable summary as the record. The gauge falls **immediately** rather
577
- than waiting for the next turn. `/compact status` is the guaranteed surface at
578
- any width.
579
-
580
- New: `compaction` config section (6 keys), `--compaction` / `--no-compaction` /
581
- `--compaction-threshold`, `ARAGON_COMPACTION*` (3 vars), `/compact` (6 forms),
582
- three settings-screen rows, a `compaction` event on the `aragon exec` JSON
583
- stream (`EXEC_SCHEMA_VERSION` **unchanged** — adding an event type never bumps
584
- it), and two stderr lines under `-p`.
585
-
586
- ### Changed
587
-
588
- - **The context gauge now counts cache tokens**, through the same
589
- `occupiedTokens` the compaction trigger reads. Anthropic reports `input_tokens`
590
- *excluding* cached tokens and puts them in two separate fields, so the old
591
- `inputTokens + outputTokens` silently under-reported occupancy for anyone
592
- behind a caching gateway. That was already a defect in the number on the status
593
- bar; it becomes a much worse one the moment a trigger fires on a different
594
- number from the one you are watching.
595
-
596
- - **`context_overflow` now names `/compact` alongside `/reset`.** That remedy
597
- list was complete until this release, because throwing the conversation away
598
- really was the only exit.
599
-
600
- ### Known limitation
601
-
602
- - **A summarizer that is 90 % faithful will, eventually, drop something the run
603
- needed,** and no amount of prompt engineering closes that. The design bounds it
604
- — verbatim task, verbatim recent turns, a block that tells the model the record
605
- is partial, and three surfaces that make it visible so a human can step in —
606
- but it does not eliminate it. That is inherent to compaction as a technique, it
607
- is the same trade Claude Code and Codex make, and it is preferable by a wide
608
- margin to the previous behaviour, which was to lose the entire run.
609
-
610
- ## 0.6.0
611
-
612
- ### Added
613
-
614
- - **`aragon exec` — a second, machine-facing face on the same binary.** The
615
- package already installed from npm and already ran from a terminal, so "it has
616
- a CLI" was true. It was nonetheless not *callable* by another program:
617
- everything it exposed was shaped for a person, and a build script, a CI job, a
618
- web backend or a second agent had to spawn it, capture free text, and guess.
619
- There was no way to read the answer without also reading the narration, no way
620
- to carry a conversation across two invocations, no way to say "you may read but
621
- not write" when there is no human to answer `--confirm`, and no way to bound
622
- what a run may spend.
623
-
624
- `aragon exec` adds a **contract** rather than a feature: a versioned JSON event
625
- schema on stdout, a session identifier the caller may mint itself, a tool
626
- permission policy enforced at the tool boundary, caller-set budgets with their
627
- own exit code, and a discovery command so a wrapper can ask what it just
628
- installed before depending on it. Everything underneath is reused unchanged —
629
- the same controller, the same tools, the same config resolution, the same
630
- skills, teams, todo and fast-tier subsystems.
631
-
632
- - `--output-format text | json | stream-json`. `stream-json` is NDJSON: one
633
- object per line, `system/init` first, exactly one `result` last, on every
634
- exit path including error, budget and signal. `text` is byte-identical to
635
- `aragon -p`, because it **delegates** to the same renderer rather than
636
- reimplementing it.
637
- - `--session-id` / `--resume` / `--continue`, backed by `~/.aragon-agent/
638
- sessions/` and an exclusive lock, so two runs cannot interleave and lose a
639
- turn. A second run on a held id exits 2 with `session_busy` rather than
640
- blocking.
641
- - `--permission-mode auto|plan|strict` plus `--allow-tool` / `--deny-tool`.
642
- A denied tool is **not registered at all**, so the model never sees it and
643
- never plans around it — and the policy reaches subagents, so one `task` call
644
- cannot bypass it.
645
- - `--max-turns` / `--max-duration`, both aborting through the same path `Esc`
646
- uses, both reporting `isError: false` and exit **3**: a budget firing is not
647
- a malfunction, but it is not a complete answer either, and a shell script has
648
- to be able to tell without parsing JSON.
649
- - `--append-system-prompt` / `--append-system-prompt-file`. Appending only;
650
- there is deliberately no full `--system-prompt` replacement, because the
651
- builtin prompt carries tool discipline, todo planning, the skills catalog and
652
- the plan-mode block, and replacing it silently disables half the product.
653
- - `--input-format stream-json`, so one process can serve several turns from a
654
- pipe. A malformed line is skipped with a non-fatal `error` event; an unknown
655
- message type is ignored.
656
- - **`aragon sessions`** (`list` / `show` / `rm` / `prune` / `path`),
657
- **`aragon info [--json]`** and **`aragon doctor [--json] [--probe]`**.
658
-
659
- Documented in full under
660
- [Use it from another project](./README.md#use-it-from-another-project),
661
- including an honest Limits section: there is no filesystem sandbox, process
662
- exit is terminal on Windows whether or not a `result` arrived, an exec session
663
- resumed in the TUI shows no scrollback, and `sessions prune` shares a directory
664
- with the TUI's `/save`.
665
-
666
- ### Changed
667
-
668
- - **Exit code `3` is new, and the signal row is now stated as `128 + signo`.**
669
- `3` means "stopped by a budget you set" and is reachable only from
670
- `aragon exec`. The signal codes are unchanged in behaviour — this CLI has
671
- always exited `128 + signo`, so `SIGTERM` has always been `143` — but the
672
- README previously simplified all of them to `130`, which is fine for prose
673
- about a CLI a human interrupts and not good enough for a wire contract.
674
- - `interrupt` handling now settles the run before exiting when `aragon exec`
675
- owns it, so a wrapper reading NDJSON gets a final `result` on Ctrl-C instead of
676
- having to time out to learn the run ended. `aragon`, `aragon -p` and every
677
- other subcommand keep the previous bare-exit behaviour byte for byte.
678
-
679
- ### Unchanged, and asserted rather than claimed
680
-
681
- - `aragon`, `aragon -p`, every existing flag, every slash command and every byte
682
- of their output. `aragon exec --output-format text` and `runHeadless` are
683
- compared for byte equality on **both** stdout and stderr by a regression test,
684
- and `createBuiltinTools` with no permission policy is proven by object identity
685
- to return the tools it returned before.
686
-
687
- ## Unreleased
688
-
689
- ### Project index on Ctrl+I (bundled `project-indexer` skill)
690
-
691
- - The `project-indexer` skill now ships **bundled** with the package
692
- (`<pkg>/skills/project-indexer`, read-only, alongside `skill-creator`): it
693
- generates the `.claude-index/` navigation map and injects the Google-style
694
- Clean Code Guidelines section into `CLAUDE.md`, adapted from the standalone
695
- Claude Code skill (ask_user instead of AskUserQuestion; body condensed under
696
- the 30 KB invocation budget with the four templates bundled as files).
697
- - New keybinding `Ctrl+I`: opens a Yes/No confirm that names every write
698
- target, then dispatches `/skill:project-indexer` through the ordinary
699
- command path - the indexing run is a normal agent turn whose tool calls
700
- stream into the transcript. While a run is in flight the key warns instead
701
- of queueing; with skills disabled or the skill shadowed it explains instead
702
- of failing silently.
703
- - `Ctrl+I` is legacy-encoded as the Tab byte, so the stdin filter's two
704
- translators (win32-input-mode on Windows, kitty CSI-u / modifyOtherKeys
705
- elsewhere) now emit a distinct NUL-delimited `INDEX_KEY_FRAME` for it -
706
- the same framing technique `Shift+Enter` uses. On terminals with neither
707
- protocol the key keeps its legacy Tab meaning (completion).
708
- - The header names the key next to the detail toggle: `^I Index` collapsed,
709
- `^I build index` in the `Ctrl+G` expanded state; hidden below 72 columns and
710
- while an overlay is open. `/skill:project-indexer` and `/project-indexer`
711
- remain available as typed commands.
712
-
713
- ### Added
714
-
715
- - **Drag the mouse to select text, and release to copy it.** Full-screen mode
716
- takes the alternate screen, turns on mouse reporting so the wheel can scroll a
717
- transcript the terminal no longer knows how to scroll, and draws every row
718
- itself. Each of those is right on its own; together they took your terminal's
719
- own drag-to-select away and put nothing in its place. The only thing shipped
720
- in its place was a one-line notice naming a bypass key that is Shift on some
721
- emulators, Fn on others and a preference elsewhere — a workaround, told once,
722
- in a line that scrolls away.
723
-
724
- Press the left button and drag: the cells you drag across are highlighted as
725
- you go, and releasing copies exactly them. The copy goes out over **OSC 52**
726
- first, so it reaches the clipboard of the machine you are *sitting at* rather
727
- than the one you are ssh'd into, with the platform clipboard binary attempted
728
- alongside it for terminals that ignore the sequence. Neither mechanism is
729
- detectable, so the toast names what was sent rather than claiming a success it
730
- cannot verify. `/copy` now goes through the same path and gained ssh support on
731
- the way.
732
-
733
- The highlight is painted in the frame pipeline rather than in React, so a drag
734
- costs one to three re-addressed rows instead of a re-render of the whole tree,
735
- and the text that is copied is read from the same mirrored rows the highlight
736
- was painted over — what you see is what you get by construction, not by a
737
- staleness check that can be wrong.
738
-
739
- Two switches, both reversible without a restart: `/mouse off` hands the mouse
740
- back to your terminal for the session (and says plainly that the wheel goes
741
- inert while it is off), `/mouse on` takes it back, and
742
- `aragon config set mouseSelect false` keeps wheel scrolling while leaving
743
- drag-select off — which also stops the CLI asking the terminal for motion
744
- reports at all, so that session puts exactly the bytes on the wire that it did
745
- before this release. `--no-mouse` remains the switch that captures nothing.
746
-
747
- ### Changed
748
-
749
- - **Scrolling up now actually stops the transcript.** The viewport followed the
750
- newest output by holding a *fixed distance from the bottom*, so scrolling up
751
- during a run did not pause anything: twelve new rows of output moved the
752
- window twelve rows down, and whatever you had scrolled up to read slid off the
753
- top while you were reading it. The faster the agent talked, the faster your
754
- reading position ran away.
755
-
756
- While you are scrolled up, the transcript is now anchored to the **content**:
757
- new output accumulates below the window instead of dragging the window along
758
- with it, and the rows on screen hold still. After five seconds with no
759
- scrolling the viewport returns to the newest line by itself — but only if
760
- something actually arrived while you were paused, so reading a *finished*
761
- transcript is never interrupted by a jump to a bottom that has not moved.
762
- Every gesture restarts the countdown, a selection drag suspends it, and
763
- `aragon config set scrollResumeMs 0` turns the return off entirely
764
- (`ARAGON_SCROLL_RESUME_MS` sets it too).
765
-
766
- The number the hint shows means something sharper as a result: it is now
767
- literally "rows of newer output below your reading position", which is what
768
- `PgDn` traverses and what the status bar's `↑N` has always shown.
769
-
770
- - **The "N new lines · PgDn" hint moved inside the input box.** It used to be
771
- rendered as the viewport's own last row, which spent a line of your transcript
772
- on chrome at exactly the moment the transcript was longest — and, because the
773
- hint's presence shrank the viewport, it fed back into the very number it was
774
- displaying. It is now a right-aligned chip on the composer's input row, inside
775
- the border, where it costs no rows at all. The chip sits in a fixed-width cell
776
- so the number gaining a digit cannot re-wrap a draft, it never truncates (the
777
- draft wraps first), and unlike the teaching row below the box it neither fades
778
- with experience nor disappears on a short terminal: how far behind you are is
779
- state, not a tutorial.
780
-
781
- ### Fixed
782
-
783
- - **The working spinner no longer disappears when an ack takes its row.** Steer a
784
- run, approve a plan, or press `Ctrl+T` while the agent is working, and the row
785
- above the composer traded its spinner for the acknowledgement — and for that
786
- ack's full two and a half seconds, nothing anywhere on screen moved. The status
787
- bar still read `running` and the tool card still read `running`, so no
788
- information had left the frame; only the evidence that the process was alive
789
- had, during exactly the long tool calls where "is it stuck?" is the question you
790
- are already asking. Consecutive acks extended the window, and steering is
791
- something the composer invites on every run.
792
-
793
- That row was made the sole owner of animation in this same release, and
794
- everything else on screen holds still for as long as the row is believed to be
795
- showing one. Believed, not checked: an ack won the row outright, so the belief
796
- and the frame disagreed, and the disagreement was invisible to every test
797
- because no component was wrong on its own.
798
-
799
- The ack still owns the row and keeps every word of its text. The spinner now
800
- rides beside it as a bare glyph — no phrase, no clock — so there is exactly one
801
- animation at every instant of a run, with an ack up or without one: never none,
802
- and never the several this release set out to remove. Terminals without Unicode
803
- and anyone running with `reducedMotion` come out byte for byte unchanged; they
804
- had no animation to lose, and nothing is prefixed to their acks.
805
-
806
- - **`/clear` now takes the todo panel with it.** Finish a plan, type `/clear`,
807
- and the transcript emptied while the `7/7 done` rail stayed exactly where it
808
- was — on a screen you had just asked to be cleared. It stayed until your next
809
- real message, which is precisely the pause you take after clearing to decide
810
- what to ask next, so essentially everyone met it.
811
-
812
- This was deliberate once. The list is a projection of what the model believes,
813
- and `/clear` wipes the transcript rather than the conversation, so the model
814
- still believed in the plan and a panel contradicting the model was judged worse
815
- than no panel. That reasoning holds for everything the CLI does on its own; it
816
- does not hold against you typing the command. An explicit instruction was
817
- always this rule's exception — `/todo clear` has used the same door from the
818
- start — and `/clear` now goes through it.
819
-
820
- The list itself is cleared, not just the pixels, so `/save`, `/todo status`,
821
- `/todo continue` and auto follow-through all agree with the screen: no plan is
822
- written into a saved session, nothing offers to continue one, and nothing spends
823
- a follow-through on it. `/reset` is unchanged and still the only command that
824
- clears the conversation. Nothing is stranded either way — the model's next
825
- `todo_write` is a full replacement, so the panel comes back the moment there is
826
- a plan to show.
827
-
828
- ### Added
829
-
830
- - **An update that will not start is undone automatically.** The updater shipped
831
- in this same release could install a version that fails to *import* — an
832
- undeclared dependency, a Node API that moved, a syntax error past the transpile
833
- target — and the only recovery was to know the package name and type
834
- `npm i -g @aragon-agent/cli@<some older version>` from memory. None of those
835
- failures reach any line inside `main()`, so no amount of care there helps.
836
-
837
- The `aragon` command is now a small launcher that runs **before** the CLI. It
838
- reads one ~300-byte file, and on every launch except the first one after an
839
- auto-update that is all it does — no write at all. But if the version it is
840
- about to start is one **this updater installed**, and that version has already
841
- exited non-zero twice, it reinstalls the version that was running before the
842
- update and marks the bad one so nothing puts it back:
843
-
844
- ```
845
- aragon 0.6.0 failed to start twice; rolling back to 0.5.9.
846
- ```
847
-
848
- It boots anyway. If the crash was transient, refusing to start would lock you
849
- out of a working CLI on our guess; the reinstall is detached and finishes
850
- either way. The next session says once what happened and then stops. A **newer**
851
- release clears the mark and installs normally — you were never opted out of
852
- updates, only out of the loop.
853
-
854
- It never touches a version **you** installed yourself, and it does not try to
855
- detect a release that starts and is merely wrong. `aragon update --rollback` is
856
- the same mechanism run deliberately, for the case a human spots in one second
857
- and a heuristic would get wrong on a bad day. Exit `4` means there was nothing
858
- recorded to roll back to.
859
-
860
- **`update.mode: off` disables this too.** The guard is armed only by an
861
- auto-install, so a machine that never auto-installs never arms it — the kill
862
- switch turns off the whole subsystem, recovery included.
863
-
864
- - **A blocked install now says what is actually blocking it.** Every install
865
- failure used to print the same line: `update failed · npm i -g
866
- @aragon-agent/cli`. On Windows the commonest cause is that the shim which
867
- launched us is held open by the shell running it, so that advice hits the same
868
- handle and fails identically — the one visible failure was also a wrong
869
- instruction. Failures are now classified, and three of them name the real
870
- remedy and appear immediately rather than after three strikes, because each is
871
- something you can fix and none of them fixes itself:
872
-
873
- ```
874
- ! update blocked · close other aragon windows, then: npm i -g @aragon-agent/cli
875
- ! update failed · no disk space
876
- ! update needs write access · npm i -g @aragon-agent/cli
877
- ```
878
-
879
- Network failures are deliberately **not** in that set: you cannot fix the
880
- registry and the next check may well succeed, so they stay silent and back off.
881
-
882
- - **Updates work behind a corporate proxy.** Node's `fetch` ignores `HTTP_PROXY`,
883
- so on a proxied machine the check failed silently forever. When the direct
884
- request fails, `aragon` now asks the npm you already have instead, which
885
- honours `proxy`, `https-proxy` and your `.npmrc`. It runs *only* after a
886
- request that has already failed, so on an ordinary machine it never runs.
887
- `/update status` shows `probe: npm` when this is what answered. No credentials
888
- are read or forwarded — we run the command you could have typed.
889
-
890
- - `/update status` gained `rollback`, `lastgood` and `probe` lines, so what the
891
- guard did and what `--rollback` would reinstall are answerable without reading
892
- the log.
893
-
894
- ### Changed
895
-
896
- - **`aragon update` exit codes: a blocked install is now `2`, not `1`.** This is
897
- the one change here a script can notice. `1` means "try again later" and `2`
898
- means "this machine will never auto-update, run the command we printed" — and
899
- for `EPERM` / `EBUSY` / `ENOSPC` the second is the accurate one: waiting does
900
- not unblock a held file handle or an empty disk. A caller shaped like
901
- `aragon update || sleep 300 && retry` changes behaviour. `0`, and `1` for an
902
- ordinary failed check or install, are unchanged.
903
-
904
- - `bin.aragon` now points at `dist/launcher.js` rather than `dist/cli.js`.
905
- `dist/cli.js` is unchanged, keeps its shebang and stays executable, so
906
- `npm start` and any script invoking it directly keep working — only the mapping
907
- moved.
908
-
909
- - **`aragon` keeps itself up to date.** Until now the installed copy was frozen
910
- forever: nothing in the CLI ever looked at the registry, so someone who
911
- installed 0.4.x eighteen releases ago was still running it and the only signal
912
- that anything had moved was a README they were never going to re-read.
913
-
914
- There is now a background updater. At most once every four hours, and at most
915
- once per machine per interval, it asks npm what the `latest` dist-tag resolves
916
- to; if that version is genuinely newer, stable, not deprecated and runnable on
917
- this Node, it installs it silently into the same global installation you
918
- already have and says so on **one line above the composer**:
919
-
920
- ```
921
- * 0.6.0 installed - restart aragon to apply
922
- ```
923
-
924
- **The running process is never mutated in any way it can observe.** The new
925
- version lands on disk and takes effect on the *next* launch. A restart in the
926
- middle of a turn would destroy a live run, a subagent dispatch and an open plan
927
- review, so the line waits and the human decides when.
928
-
929
- The notice does not get its own row — it shares the row the toast stack and the
930
- working line already share, behind both of them. A toast is a response to
931
- something you did and wins outright; the working line is transient and would be
932
- lost if deferred; the update line is persistent and loses nothing by waiting.
933
- The practical effect is that **someone who is actually working never sees the
934
- updater until they stop.**
935
-
936
- It only ever runs `npm i -g` for one kind of installation: a global one where
937
- no project manifest claims to own the version. pnpm, yarn, bun and volta are
938
- each detected and get the correct command for *that* manager printed instead
939
- of a command run on their behalf; `npx`, a project's `node_modules` and a
940
- clone of this repo produce no line at all, because none of them is out of date
941
- in any sense the user can act on. A global directory that cannot be written is
942
- detected before anything is spawned, and npm exiting `0` into a *different*
943
- prefix — which happens on any machine with `prefix=` in `.npmrc` or two Node
944
- installations — is caught by re-reading the version from the directory we
945
- classified, rather than being believed.
946
-
947
- Every failure is silent: it logs, backs off exponentially, and says nothing
948
- until the third consecutive one, at which point it says something actionable.
949
- Four config keys (`update.mode`, `update.checkIntervalMs`, `update.registry`,
950
- `update.distTag`), the flags `--update` / `--no-update`, the environment
951
- variables `ARAGON_UPDATE` and `ARAGON_UPDATE_REGISTRY`, the slash command
952
- `/update [status|now|skip|off]`, and a foreground `aragon update
953
- [--check] [--to <version>] [--json]` for headless users. `update.mode: off`
954
- and `ARAGON_UPDATE=0` are kill switches, and with either set the CLI executes
955
- zero lines of update code.
956
-
957
- Auto-update widens the window in which a compromised release reaches users
958
- without a human decision. The README says so plainly, along with what is and
959
- is not mitigated. No credentials are ever attached to the check.
960
-
961
- - **A running command shows what it is doing.** `bash` is the only tool here that
962
- can run for minutes — `npm test`, a `git clone`, a training job — and while it
963
- ran, its card was one line of text that never changed. Nothing in that frame
964
- distinguished a test suite at 40% from a child process blocked forever on a
965
- password prompt this CLI will never show. Its card now carries the last eight
966
- lines of what the command is printing, plus one footer row that reads
967
- `(running)` or, once the child has been quiet for ten seconds,
968
- `no output for 45s` — with the seconds advancing.
969
-
970
- A `\r`-driven progress bar (npm, pip, curl, docker) is *one* line rewritten a
971
- thousand times, and it renders as **one row counting up in place** rather than
972
- a thousand rows of nearly the same string — including the ordinary case where
973
- the bar emits no newline at all for its entire run.
974
-
975
- Everything the tail shows is **stripped, not interpreted**: ANSI colour, cursor
976
- motion, `\r`, `\b`, tabs and every other control byte are removed at the
977
- boundary before a row is stored, so nothing can move a cursor inside a frame
978
- the renderer believes it owns. It is bounded twice by construction — eight rows
979
- per call, sixteen concurrent calls — so a command that writes 400 MB to stdout
980
- costs a fixed number of retained rows.
981
-
982
- It is a **display and nothing else**. The text the model receives is
983
- byte-for-byte what it was; `Ctrl+O` does nothing while a command runs, because
984
- expansion is a promise about stored content and there is no stored rest of a
985
- 400 MB stream; `/save` during a run writes no tail, since a resumed one would
986
- describe a process that died with the session; and at settle the card is
987
- replaced by the authoritative result exactly as before.
988
-
989
- New config key `liveToolOutput`, defaulting to **`true`** — unlike
990
- `showThinking` below, this adds information rather than hiding it.
991
- `aragon config set liveToolOutput false` (or `--no-live-tool-output`, or
992
- `ARAGON_LIVE_TOOL_OUTPUT=0`, or the **Live output** row in `/settings`) turns it
993
- off, and with it off no store is allocated and no recorder is attached: `bash`
994
- is byte-identical to the build before this entry.
995
-
996
- - **The working line stops guessing while a tool runs.** It reads
997
- `⠙ Running bash` instead of rotating thinking-words. `running` covers the whole
998
- turn, including the minutes the model is idle and a child process is doing the
999
- work, so "Pondering" during a build was not calm — it was wrong. One row, no
1000
- digits, no clock: everything the row already promised still holds.
1001
-
1002
- ### Changed
1003
-
1004
- - **Reasoning blocks are now hidden by default.** `showThinking` is a new
1005
- top-level config key and it defaults to `false`. If you have been reading
1006
- thinking every day, this is the entry that concerns you: `Ctrl+T` still shows
1007
- it for the session exactly as before, `aragon config set showThinking true`
1008
- (or `--show-thinking`, or `ARAGON_SHOW_THINKING=1`) restores the old behaviour
1009
- permanently, and `/settings` has a **Show thinking** row directly under
1010
- **Thinking**.
1011
-
1012
- Nothing is hidden silently. A settled turn that thought leaves one muted row
1013
- where the block used to be — `✱ thought for 12s · ctrl+t to show` — so the fact
1014
- that reasoning happened, how long it took, and how to see it are all still on
1015
- screen. In **inline** mode that row omits the key hint and the toast reads
1016
- `Thinking shown for new output.`, because a settled entry there has already
1017
- been printed into your terminal's scrollback and cannot be repainted; the
1018
- toggle applies to the live entry and to everything drawn after it.
1019
-
1020
- `thinkingLevel` is untouched and means what it always did — the effort the
1021
- provider is asked to spend. `showThinking` is only whether the terminal draws
1022
- what came back. They are one word apart in the config file, which is why the
1023
- flag is `--show-thinking` rather than the shorter name.
1024
-
1025
- ### Added
1026
-
1027
- - **A live working line while a run is in flight.** One row above the composer,
1028
- carrying a spinner and a word that rotates every four seconds:
1029
- `⠋ Percolating…`. Take reasoning off the screen and the gap between submitting
1030
- and the first token is otherwise completely blank, which reads as dead rather
1031
- than as busy — the two changes are one change.
1032
-
1033
- It carries no clock and no token count, deliberately: the status bar one row
1034
- below already renders both, under the same running condition and driven by the
1035
- same 200 ms ticker this row reuses. Under `reducedMotion` the spinner is a
1036
- static glyph and the word is chosen once and holds, because rotating text is
1037
- motion too. An idle frame is unchanged, and so is the viewport's row budget —
1038
- the line shares the row the toast strip already owned rather than adding one.
1039
-
1040
- - **`write_file` and `edit_file` render a real diff.** Hunks with old/new line
1041
- numbers, a `+N -M` summary, add/remove/context colouring, and `(truncated)` or
1042
- a `binary` / `too-large` / `unreadable` note when the old side could not be
1043
- read. Diffs collapse at 12 rows behind the existing `Ctrl+O` — no new
1044
- keybinding. A resumed session written by an older build, or a card whose patch
1045
- the bounded store already evicted, falls back to the previous text preview;
1046
- nothing renders blank.
1047
-
1048
- `write_file` previously reported creating a 300-line file as
1049
- `Wrote 8214 bytes to path`, and `edit_file`'s diff trimmed the common prefix
1050
- and suffix and rendered *everything* in between as one removed block followed
1051
- by one added block — for a `replace_all` touching line 40 and line 900, that
1052
- was 860 removed rows followed by 860 nearly identical added ones.
1053
-
1054
- **The model's input got smaller, not larger.** The structured patch reaches the
1055
- screen on a CLI-local channel rather than through the tool result, and the text
1056
- the model reads gained a 4 000-character ceiling it did not have before (its
1057
- only previous bound was the executor's 100 KB backstop). A single-region edit
1058
- is byte-for-byte what it was plus one `@@` header; a multi-region edit is
1059
- strictly smaller. `write_file`'s result text is unchanged.
1060
-
1061
- ### Changed
1062
-
1063
- - **A stream that stops mid-answer is now a loud failure instead of a silent
1064
- truncated one.** All three provider adapters used to end their SSE loop by
1065
- yielding "done" with whatever had arrived, so a connection closed cleanly
1066
- half-way through an answer — a proxy idle cut, a load-balancer timeout, an
1067
- HTTP/2 GOAWAY — was indistinguishable from a completed reply. You got a
1068
- truncated answer and were told nothing.
1069
-
1070
- Each adapter now reports the truncation, which is what lets it be retried and
1071
- recovered. An **abort** is explicitly excluded and still completes normally, and
1072
- a model that legitimately says nothing and stops is still a success.
1073
-
1074
- This is deliberately **not** behind `--no-retry`: an adapter that reports a
1075
- truncated stream honestly should do so whether or not retry is enabled. A switch
1076
- that also disabled telling you the truth would be a mute button, not a kill
1077
- switch.
1078
-
1079
- - **The mouse wheel now only ever scrolls.** A notch scrolls the transcript
1080
- three rows (a page with `Shift`), or the open overlay when there is one, no
1081
- matter where the pointer is sitting. Prompt history is recalled by `↑` / `↓`
1082
- alone, exactly as in Claude Code and Codex.
1083
-
1084
- **This supersedes the 0.5.0 entry below**, which said the wheel "scrolls what
1085
- is under the pointer" and stepped prompt history one entry per notch when the
1086
- pointer was over the input box. That turned out to be wrong in use rather than
1087
- in principle: the pointer is not an instrument you aim in a TUI — it rests
1088
- wherever you last left it, which in a chat-shaped app is next to the thing you
1089
- type into. So the ordinary "scroll back and see what just happened" gesture
1090
- landed on the composer and replaced the draft with an old prompt, with no
1091
- error, no undo and nothing visibly connecting the gesture to the outcome. The
1092
- band that decided this was invisible too: its boundary was a measured row that
1093
- moved as the toast row, the hint row and the completion popup came and went, so
1094
- the same gesture could do two different things a second apart.
1095
-
1096
- Nothing else about the wheel changes — same three rows per notch, same
1097
- `Shift`+wheel page, same `--no-mouse` / `ARAGON_MOUSE=0` / `mouse: false`
1098
- escape hatches, same `Shift`+drag for selecting text.
1099
-
1100
- - **A long session stays as fast as a short one.** The TUI used to get slower as
1101
- a session grew and, past a threshold, stopped making progress at all. That was
1102
- structural rather than incidental: Ink has no output caching, so every frame
1103
- re-walked the entire mounted tree, re-measured every text node and
1104
- re-serialised the frame. The per-frame cost was therefore proportional to every
1105
- character the session had ever produced — and `transcriptWindow` bounded the
1106
- entry *count*, which bounds nothing when one entry can be fifty thousand lines.
1107
-
1108
- Entries outside the visible band are no longer mounted. The viewport renders
1109
- what you can see plus a couple either side and replaces the rest with two
1110
- spacers of the right height, so the frame costs what the screen costs.
1111
- Scrolling, the `↑N` readout and the wheel routing are untouched. Because the
1112
- horizon is no longer a rendering budget, `transcriptWindow` rises from 300 to
1113
- 1000 and its ceiling from 2000 to 20000; an existing setting is still valid
1114
- and still clamps.
1115
-
1116
- Syntax highlighting and markdown parsing are memoised on their input, so a
1117
- 200-line code block is tokenised once rather than thirty times a second for as
1118
- long as it stays on screen. Only the *parsed* form is cached and never a themed
1119
- one, so `/theme` still takes effect on the next frame.
1120
-
1121
- Under genuine load the streaming interval widens — 33 ms up to a 320 ms
1122
- ceiling — so keystrokes and `Ctrl+C` do not queue behind renders. This is
1123
- visible rather than silent: a muted **`eco`** chip appears in the status bar
1124
- while it is in effect, because a user who cannot see why the stream got
1125
- chunkier will conclude the model got slower. `--no-render-governor`,
1126
- `renderGovernor: false` or `ARAGON_RENDER_GOVERNOR=0` turns it off;
1127
- `--max-render-interval 33` flattens the ladder without disabling the mechanism.
1128
-
1129
- - **A live thinking block no longer grows without limit.** While an answer is
1130
- streaming, visible thinking is clamped to its newest 24 rows behind a
1131
- `... N earlier lines` marker, and the whole block renders again the moment the
1132
- answer settles. Thinking is scratch reasoning you read from its newest end, and
1133
- an unbounded one was the easiest way for a single live entry to fill the
1134
- viewport on its own.
1135
-
1136
- ### Fixed
1137
-
1138
- - **Inline mode could hard-freeze the terminal, and now cannot.** Once a live
1139
- entry grew taller than the terminal, Ink took a branch that writes
1140
- `clearTerminal` plus the *entire accumulated session history* on every
1141
- subsequent frame — bypassing both of its own dedupe gates, and growing without
1142
- bound for the rest of the session. Any inline user (`--no-fullscreen`,
1143
- `TERM=dumb`, CI, a terminal under 12 rows or 40 columns) whose model wrote a
1144
- long answer could reach it. The live region is now clamped below that
1145
- threshold, with a `... N earlier lines` marker naming what is deferred. Nothing
1146
- is lost: the entry prints in full into the terminal's own scrollback the moment
1147
- it settles.
1148
-
1149
- - **The view state no longer grows without a ceiling.** A single assistant answer
1150
- is capped at 256 KiB in the view (thinking at 64 KiB, streamed tool arguments
1151
- at 16 KiB) by eliding the MIDDLE, so both the opening and the live cursor
1152
- survive; a marker states how many characters went. `transcriptRetain`
1153
- (default 1000) caps the entries kept in memory, and what it removed is counted
1154
- and reported both by `/perf` and at the top of the exit replay. A model that
1155
- emits five megabytes no longer makes every later frame a five-megabyte scan.
1156
-
1157
- ### Added
1158
-
1159
- - **A fast model tier: a second, cheaper model you can delegate to and be
1160
- reviewed by.** Off by default — with `fast.enabled: false` the system prompt,
1161
- the `task` schema, the transcript and the request payloads are byte-identical
1162
- to a build without it — and it does two things once you turn it on.
1163
-
1164
- **Delegation.** `task` gains one optional field per subagent, `model: "fast"`.
1165
- A child marked that way runs on the fast model and is otherwise identical:
1166
- same tools, same working directory, same `--confirm` gate, same plan-mode
1167
- gate, same skills ceiling. Delegating to a cheaper model is a *model* choice,
1168
- never a permission boundary. It is where "not complicated, but very expensive
1169
- in context" gets paid for — a child that reads nine files and reports four
1170
- sentences moves the file bodies out of the lead's window and onto a model that
1171
- costs a fraction as much per token.
1172
-
1173
- **Periodic review.** Every few turns a small digest of what the lead has been
1174
- doing goes to the fast model with one question — is this still on track? — and
1175
- a critique that is not "on track" is injected into the running loop as a
1176
- `<fast_review>` block, so the lead sees a second opinion while it can still
1177
- act on it:
1178
-
1179
- ```
1180
- fast review #2 · claude-haiku-4-5 · turn 12 · 1.4s
1181
- You have edited config/schema.ts three times without running the tests.
1182
- ```
1183
-
1184
- The digest is built from a bounded ring of recent turns and **never** from the
1185
- message history: sending a session's accumulated file bodies to the "cheap"
1186
- model would make a review cost more than the turn it reviews, which is the
1187
- inversion the whole feature exists to prevent.
1188
-
1189
- Spend is attributed **per tier** — a fast child's tokens are priced at the
1190
- fast model's table, not the lead's, and the dispatch report states the two
1191
- separately. A model the built-in price table has never seen is reported as
1192
- `unknown` rather than as `$0.00`, because a feature that looks free while it
1193
- is spending money is worse than one that admits it does not know.
1194
-
1195
- Turn it on with `/fast model <id>` then `/fast on`, with
1196
- `aragon config set fast.enabled true`, with `--fast --fast-model <id>`, or with
1197
- `ARAGON_FAST=1 ARAGON_FAST_MODEL=<id>`. `/fast status` is the guaranteed
1198
- readout: the resolved model, the budget, and this session's reviews, delegated
1199
- children, tokens and cost. Eleven `fast.*` config keys, four flags, four
1200
- environment variables and five settings-screen rows; see the README.
1201
-
1202
- **The review has a session budget.** A session is not one run — it is one run
1203
- per message you send — so a cadence bounds the pace and nothing bounds the
1204
- total. `fast.reviewMaxPerSession` (default **40**, raisable live with
1205
- `/fast budget <n>`) is the number of reviews a session may *start*; reaching it
1206
- prints one `info` notice that names the remedy and says delegation is
1207
- unaffected, and `/fast status` reads `12/40` from the first review rather than a
1208
- bare count you can only size after the fact. It is denominated in **reviews,
1209
- not currency**, because the fast tier is exactly where a model the price table
1210
- has never seen lives — a dollar ceiling would silently never fire for the
1211
- configuration that most needs one. Delegation is never budgeted: it is spend the
1212
- lead chose in service of a message you sent, it is already bounded by `team.*`,
1213
- and it exists to *reduce* total cost.
1214
-
1215
- **The review also fails fast, so it cannot compete with you.** It calls through
1216
- its own provider registry with **one** retry rather than the ten
1217
- `retry.maxRetries` defaults to for the lead, and it observes neither `retry.*`
1218
- nor `/retry` — that policy says how hard to fight for *your* answer, and
1219
- inheriting it for an unrequested background call is how an advisory ends up
1220
- spending a rate-limited provider's quota on itself. Its own wall-clock bound
1221
- is 20 s.
1222
-
1223
- Two failure modes are handled out loud rather than silently. If the tier stops
1224
- resolving mid-session — you switch the main provider and `fast.provider` was
1225
- inheriting it — `model:"fast"` is downgraded to the main model, **counted, and
1226
- stated in the report**, because an agent that silently gets an expensive child
1227
- has no way to learn its cost model is wrong. And three consecutive
1228
- *misconfiguration* failures disable the reviewer for the session with exactly
1229
- one warning, while a merely busy provider (rate-limited or overloaded) is
1230
- treated as transient and never counts — that condition fixes itself, and a
1231
- session-long silent shutdown over it would not. A review you *cancelled* is
1232
- neither: Esc, a run that ends first, or quitting leaves a `cancelled` card and
1233
- no strike against the tier.
1234
-
1235
- Reviews log under their own `fast` scope rather than under `agent`, so
1236
- `fast_review_*` records filter cleanly away from the lead's.
1237
-
1238
- - **API calls are retried with backoff, and the waiting is legible.** A transient
1239
- provider failure — HTTP 429, 529, 5xx, 408, or a dropped connection — used to end
1240
- the turn. One red notice, an idle prompt, and forty tool calls of work thrown
1241
- away. It is now retried up to **10 times** with an increasing interval
1242
- (1 s → 2 → 4 → 8 → 16 → 30 s, equal-jittered, 30 s per wait at most) under a
1243
- 4-minute budget measured from the first failure.
1244
-
1245
- What you see while it waits is **one line, rewritten in place** — never ten
1246
- notices — carrying what failed, which attempt this is, and a countdown to the
1247
- next one:
1248
-
1249
- ```
1250
- ↻ Provider overloaded · retry 3/10 in 7s Esc to cancel
1251
- ↻ recovered after 3 retries · 12.4s
1252
- ```
1253
-
1254
- plus a `retry 3/10 7s` chip on the status bar (`[r3]` on a narrow terminal). The
1255
- card settles into the transcript as history — `recovered`, `gave up`, or
1256
- `interrupted` — and shows up in the exit replay, because a turn that spent three
1257
- minutes retrying is exactly the kind of cost an exported transcript should
1258
- account for.
1259
-
1260
- **Esc ends the wait within a frame**, not at the end of the countdown, and it
1261
- never produces a fabricated error: an aborted request is silent. That is the one
1262
- detail everything else here depends on, because the provider layer marks an
1263
- `AbortError` as retryable — so the signal is checked *before* the error, and a
1264
- user pressing Esc can never be mistaken for a timeout worth retrying.
1265
-
1266
- Never retried: `auth_error` (401/403), `invalid_request`, `context_overflow`.
1267
- Ten retries of a bad API key is three minutes of looking broken.
1268
-
1269
- `Retry-After` is honoured as a **floor**, never a replacement — a `Retry-After: 1`
1270
- on the ninth retry does not undo eight retries of backoff — and a stated wait
1271
- longer than a minute is surfaced immediately with the provider's own number
1272
- rather than silently wedging the terminal.
1273
-
1274
- A connection that dies **mid-answer** is also covered, and this is the case that
1275
- needed more than a retry loop: the partial text is discarded, the affected tool
1276
- cards are removed, and the answer restarts from the top. Nothing is duplicated,
1277
- because the engine only writes history on a completed turn.
1278
-
1279
- - **`/retry [show|on|off|max <n>]`, `--retry` / `--no-retry`, `--retry-max <n>`,
1280
- `ARAGON_RETRY`, `ARAGON_RETRY_MAX`, and a `retry` config section** with nine
1281
- keys (`aragon config set retry.*`). `/retry off` takes effect **in the running
1282
- session** — subagents included, since they share the lead's provider registry —
1283
- not only at the next launch. `retry.maxRetries: 0` is the kill switch and is a
1284
- real value at every one of those entry points. `/settings` gains one read-only
1285
- `API retries` row.
1286
-
1287
- - **A subagent's retry is visible on the team card.** A child in backoff reports
1288
- `retry 3/10` on its own row. Children inherit the retry policy for free, but
1289
- their events never reach the lead's view, so without this a subagent spending
1290
- three minutes waiting looked exactly like a hung dispatch.
1291
-
1292
- - **Retry lines in `-p` mode go to stderr, never stdout.** `[retry 3/10]
1293
- overloaded — waiting 7s` and `[retry] recovered after 3 retries` are on stderr,
1294
- so `aragon -p … | jq` stays valid; a discarded partial answer is announced there
1295
- too, because a piped consumer otherwise cannot tell a replay from a model that
1296
- repeated itself. They are not suppressed by `--quiet`: a three-minute pause with
1297
- no explanation is not a compact run, it is a hang.
1298
-
1299
- - **`/perf`.** Reports what the renderer is actually doing: the frame interval
1300
- and the last commit cost, how many entries exist / are retained / were dropped
1301
- / are mounted, height-cache and render-cache occupancy, and the viewport
1302
- geometry. `/perf reset` clears the caches and returns the governor to its
1303
- fastest rung. Silent degradation is the one thing a claim of robustness cannot
1304
- afford, and this is the place that names it.
1305
-
1306
- - **Three configuration keys**, each with a flag and an environment override:
1307
- `transcriptRetain` (`--transcript-retain`, `ARAGON_TRANSCRIPT_RETAIN`),
1308
- `renderGovernor` (`--render-governor` / `--no-render-governor`,
1309
- `ARAGON_RENDER_GOVERNOR`) and `maxRenderIntervalMs`
1310
- (`--max-render-interval`, `ARAGON_MAX_RENDER_INTERVAL_MS`). No migration and no
1311
- config version bump: a file written before they existed loads unchanged.
1312
- `transcriptRetain` is raised to `transcriptWindow` when the two conflict —
1313
- retaining less than you can scroll to would make part of the horizon
1314
- unreachable — and a startup notice names both numbers, because overriding a
1315
- value you typed and saying nothing is the one thing none of this may do.
1316
-
1317
- - **Following the plan through, on a budget you can do arithmetic on.** A run
1318
- that ended with steps outstanding used to print one sentence and stop. It now
1319
- asks three questions the CLI already knew the answers to — did you abort it,
1320
- did it error, did the list move — and does one of three things.
1321
-
1322
- With the default `notify` the sentence is unchanged, except that a run **you**
1323
- cancelled with `Esc` now says nothing at all: being told what you just
1324
- interrupted is the CLI answering a question nobody asked.
1325
-
1326
- With `/todo follow auto` it continues the plan itself, after announcing
1327
- `Continuing with 4 remaining steps in 3s - Esc to stop.` Auto-continuation was
1328
- refused last release as "an unbounded cost loop wearing a helpful hat", and
1329
- that judgement stands — what changed is that the loop is now bounded first.
1330
- A continuation that completes no step buys exactly one more attempt; a single
1331
- plan is continued at most 25 times however productive it looks; and a re-plan
1332
- forgives the first counter but never the second, so a model that reshapes its
1333
- list every turn cannot lift the ceiling. A run that ended with an error never
1334
- auto-continues in any mode. `Esc` cancels, and so does typing anything.
1335
-
1336
- Whatever sends the continuation — the timer or `/todo continue` — now sends
1337
- the numbered list of open steps rather than the words "continue with the
1338
- remaining todo items", so a plan whose `todo_write` has scrolled out of the
1339
- context window is still followed exactly. Neither path records that text in
1340
- your prompt history any more; it is not something you typed.
1341
-
1342
- Under `-p` there is no grace window, because there is nobody there to use it,
1343
- which is precisely why the two counters rather than the human are the
1344
- protection there. `[todo] continuing (3 steps left)` and
1345
- `[todo] 3 steps unfinished` go to stderr, suppressed by `--quiet`, and an
1346
- unfinished plan still never changes the exit code.
1347
-
1348
- New surfaces: `/todo follow <notify|auto|off>`; `--todo-follow <mode>`;
1349
- `ARAGON_TODO_FOLLOW`; and `todo.followThrough` in `config.json`. The default
1350
- is `notify`, so a session that does not ask for any of this behaves as it did.
1351
-
1352
- - **A plan strip for inline mode.** Inline mode has no fixed frame and so no
1353
- rail, which left every pipe, every `CI=1`, every `TERM=dumb` terminal and
1354
- every window under twelve rows able to see `todo 3/7` but not *which* step was
1355
- running. There is now a one-row strip under the transcript —
1356
- `todo 3/7 > Adding the rail to AppShell +2 done` — windowed on the
1357
- same step the rail would have anchored on. It truncates rather than wrapping
1358
- and is exactly one row at every width, so the composer does not move.
1359
- `--no-todo-panel` hides it along with the rail.
1360
-
1361
- ### Changed
1362
-
1363
- - `npm run typecheck` now covers the test tree in both packages. `tsconfig.json`
1364
- excludes `__tests__` so the build never emits fixtures, and vitest transpiles
1365
- without typechecking — between the two, a widened required field could break
1366
- ten test files while `npm run build` and `npm test` were both green. Twelve
1367
- such errors had accumulated; all twelve were fixture drift and all twelve are
1368
- repaired, with no suppressions and no production type relaxed to accommodate a
1369
- fixture.
1370
-
1371
- - **TODO planning, and a rail to watch it in.** For work with three or more
1372
- distinct steps the agent now keeps a visible plan: it writes a checklist with
1373
- a new `todo_write` tool, marks exactly one item in progress, and ticks it off
1374
- before starting the next. In full-screen mode the list is rendered as a
1375
- right-hand rail about a fifth of the terminal wide, windowed around the step
1376
- being worked on rather than sliced from the top. Ask for something small and
1377
- nothing appears — the prompt tells the model not to plan work it can finish in
1378
- one or two steps, and a one-item list against an empty plan is refused
1379
- outright, so a session that never plans has byte-identical layout to a build
1380
- without this feature.
1381
-
1382
- The list is a projection of what the model believes, and that single rule
1383
- decides everything else: `/clear` keeps it (the conversation is still there),
1384
- `/reset` drops it, `/save` writes it and `/resume` restores it — and resuming
1385
- a session that carries no list CLEARS the current one rather than leaving a
1386
- plan on screen whose conversation has just been replaced. A finished plan
1387
- survives to the start of your next message so you get to see `7/7 done`; an
1388
- unfinished one survives "continue" and "now do the rest", then expires after
1389
- three unrelated turns rather than holding a fifth of the screen for the rest
1390
- of the session. A run that ends with items left over produces a notice and
1391
- `/todo continue`; nothing auto-continues on your behalf.
1392
-
1393
- "One step at a time" is enforced by structure rather than asked for in a
1394
- prompt: any payload with two in-progress items is repaired down to one, and a
1395
- list with none promotes the first unfinished item. Whatever the model sends,
1396
- the panel has exactly one current row.
1397
-
1398
- New surfaces: the `todo_write` tool; `/todo [status|on|off|panel on|off|clear|
1399
- continue]`; `--todo` / `--no-todo` and `--todo-panel` / `--no-todo-panel`;
1400
- `ARAGON_TODO=0`; and `todo.enabled` / `todo.panel` in `config.json` (a third
1401
- key, `todo.followThrough`, arrived with the follow-through entry above).
1402
- `panel` is
1403
- separate from `enabled` on purpose — a screen-reader user wants the planning
1404
- discipline without the column, and the system prompt varies one sentence to
1405
- match. Under `-p` the plan goes to stderr as `[todo] 3/7 <step>` lines,
1406
- suppressed by `--quiet`. Below 80 columns, behind an overlay, in inline mode
1407
- or on a short terminal the rail is not mounted and the counter appears in the
1408
- status bar instead; the transcript keeps at least 62 columns at every width.
1409
-
1410
- - **Output token limits that cannot themselves fail a run.** Every model and
1411
- every provider now defaults to 64000 output tokens, and that number is
1412
- configurable from the TUI, from a flag, from `ARAGON_MAX_TOKENS`, and by hand
1413
- in `~/.aragon-agent/config.json`. Leave it alone and it stays 64000.
1414
-
1415
- What is new is that the number is an *ambition*. `gpt-4o` caps at 16384 and
1416
- `gemini-1.5-pro` at 8192; sending 64000 to either is an HTTP 400 that ends the
1417
- turn with a raw provider string in the transcript. The cap is now clamped down
1418
- to whatever the target model actually accepts, from a per-model table plus
1419
- whatever the provider's own model list reports. For a model nothing knows — a
1420
- proxy, a `--base-url` endpoint, something that shipped last week — the request
1421
- goes out at 64000, and if the provider objects, `aragon` reads the real ceiling
1422
- out of the error text, repairs the request, sends it exactly once more, and
1423
- remembers the answer for the rest of the session. One warning line; the run
1424
- continues.
1425
-
1426
- Two combinations that were a guaranteed 400 on every request are fixed with
1427
- it: `--thinking xhigh` asks for a 65536-token budget while Anthropic requires
1428
- `max_tokens` to be strictly greater than it, and OpenAI's reasoning family
1429
- (`o1`, `o3`, `gpt-5`) rejects the `max_tokens` parameter name outright in
1430
- favour of `max_completion_tokens`.
1431
-
1432
- New surfaces: `/max-tokens [<n>|auto|default]`, which with no argument reports
1433
- the setting *and* the effective cap for the live model; an `Effective:` line
1434
- under the settings screen's `Max tokens` field, so a value the model will not
1435
- honour is visible before you save it; `--max-tokens auto`; and
1436
- `aragon config set maxTokens <n|auto|default>`, which echoes the value that was
1437
- actually stored rather than the one you typed.
1438
-
1439
- - **Team subagents.** The agent can now hand independent parts of a job to up to
1440
- five short-lived subagents that run in parallel, then read one combined report
1441
- of what each of them found. It decides for itself whether to delegate, so a
1442
- session that never calls the new `task` tool is byte-for-byte the session that
1443
- existed before. On by default; `--no-team` or `team.enabled: false` turns it
1444
- off, and `aragon config set team.maxSubagents <n>` sets the width (1-10; the
1445
- 10 is a hard ceiling no config file can raise).
1446
-
1447
- A subagent is a full agent with the same tools and working directory but no
1448
- memory of your conversation, so its brief has to be self-contained. It cannot
1449
- delegate further and it cannot ask you anything. Plan mode and a skill's
1450
- `allowed-tools` ceiling both reach one level down, so delegating is never a
1451
- way around either; `--confirm` queues subagent confirmations one at a time and
1452
- labels each with the subagent that is asking.
1453
-
1454
- Subagents can message each other with `team_send` / `team_wait`, delivered by
1455
- attaching the message to the recipient's next tool result rather than
1456
- interrupting it. The limits are deliberate — 6 messages each, one every 15
1457
- seconds, 24 per dispatch — and every refusal is a plain result naming the
1458
- limit rather than an error, because an error invites the retry the limits
1459
- exist to prevent.
1460
-
1461
- While a dispatch runs, a roster sits between the transcript and the composer
1462
- and the status bar carries `agents 3/5`; both disappear when it finishes and
1463
- the transcript keeps a card you can expand with `Ctrl+O`. `Esc` aborts every
1464
- subagent and returns the partial report. Under `-p` the transitions go to
1465
- stderr and the subagents' tokens are folded into the `[usage]` footer, so the
1466
- cost readout cannot under-report what the team spent.
1467
-
1468
- Full documentation, including the limits table and how team mode interacts
1469
- with plan mode, skills and `--confirm`, is in the README.
1470
-
1471
- - **The subagent roster now shows what each subagent is doing, and shows the
1472
- ones that are working.** Where every row used to say `thinking` for minutes at
1473
- a time, it now says `bash: npm test -w cli`, `read: src/api/routes.ts`,
1474
- `waiting for a2`, or the tail of the sentence the subagent is currently
1475
- writing. On a narrow terminal the same row falls back to a file name and a
1476
- program name rather than wrapping, and the elapsed clock keeps its place at
1477
- every width.
1478
-
1479
- The five visible rows are now the five that matter: whatever is running comes
1480
- first, then whatever is queued, then the most recent results. Above five
1481
- subagents the old panel showed the five that had finished first and hid
1482
- everything still working behind `+3 more`, which is the opposite of what the
1483
- roster is for. `+3 more` now says how many of the hidden ones are still going.
1484
-
1485
- - **A subagent no longer waits two minutes for a message nobody can send.**
1486
- `team_wait` now checks whether anyone could actually answer before it blocks:
1487
- if the teammate it named has already finished, does not exist, or is itself
1488
- waiting in a way that cannot be released in time, it returns straight away and
1489
- says which. Nothing is refused that could still have been answered — including
1490
- by a third teammate further down the chain — and the subagent is told what to
1491
- do instead rather than handed an error.
1492
-
1493
- - **A subagent whose very first request dies on a rate limit is retried once.**
1494
- Only a genuine cold start qualifies: no turn completed and no tool ran, so
1495
- there is nothing to undo. It restarts after two seconds, the roster says
1496
- `starting (retry)`, and the report notes `retried 1x`. Pressing `Esc`, a
1497
- timeout, or any subagent that had already begun work is never retried.
1498
-
1499
- ### Changed
1500
-
1501
- - **`"maxTokens": null` in `config.json` now means AUTO** — use each model's own
1502
- ceiling, never above 64000. It previously resolved to 64000 by accident (the
1503
- layered resolver skipped `null`), and there was no way to express AUTO through
1504
- any supported write path. If you have a `null` there today, the only difference
1505
- is a downward clamp on models whose real ceiling is below 64000 — the direction
1506
- that prevents failures rather than causing them. Absent still means 64000, and
1507
- an explicit number still means that number.
1508
-
1509
- - **`config.json` holds settings again.** The prompts you type are no longer
1510
- stored in it. They move to `~/.aragon-agent/prompt-history.jsonl` — one JSON
1511
- object per line, `0600` on POSIX — and the submit counter and the "notice
1512
- already shown" flag move to `~/.aragon-agent/state.json`. `↑` and `↓` behave
1513
- exactly as before.
1514
-
1515
- Two things get better. Opening `config.json` to change your model no longer
1516
- means scrolling past a transcript of your own typing, and copying it to
1517
- another machine no longer carries your prompts along. And submitting a message
1518
- no longer rewrites the file that holds your API keys: `config.json` is written
1519
- when you change a setting, and at no other time.
1520
-
1521
- **Your history and counters are migrated automatically, once, at the next
1522
- launch.** Nothing is printed, because nothing changes for you. The dead
1523
- `recentModels` key — which nothing has ever written — is dropped.
1524
-
1525
- If you go back to 0.5.x after upgrading, that version does not know about the
1526
- new file and `↑` will look empty. Nothing has been lost: the entries are still
1527
- in `prompt-history.jsonl`, and upgrading again brings them back.
1528
-
1529
- ### Added
1530
-
1531
- - **`aragon history`** — `path`, `list` (`-n`, `--json`) and `clear --yes` for
1532
- the prompt history, alongside the existing `aragon logs` commands. Entries are
1533
- stored verbatim, so `clear` is how you erase them; `aragon config set
1534
- historyEnabled false` stops new ones being recorded without deleting or hiding
1535
- what is already there.
1536
-
1537
- - **The mouse wheel scrolls what is under the pointer.** In full-screen mode the
1538
- wheel now scrolls the transcript three rows per notch (a page with `Shift`),
1539
- scrolls an open overlay when the pointer is over one, and steps prompt history
1540
- one entry per notch when the pointer is over the input box — the same code
1541
- path `↑` / `↓` use, so it inherits the rule that an in-progress draft is never
1542
- overwritten. A slim rail on the viewport's right edge shows where you are in
1543
- the conversation.
1544
-
1545
- **This fixes a destructive bug.** Previously the wheel recalled prompt history
1546
- no matter where the pointer was, because the alternate screen makes terminals
1547
- translate each notch into arrow keys. Wheeling while composing a message
1548
- silently replaced the draft with an old prompt, and nothing scrolled.
1549
-
1550
- **Selecting text with the mouse now requires `Shift`+drag** in most terminals.
1551
- That is the standard convention for "an application is tracking the mouse" and
1552
- it applies to everyone, including people who never scroll with the wheel — so
1553
- the first session says so once, in the transcript, and never again. Turn the
1554
- whole feature off with `--no-mouse`, `ARAGON_MOUSE=0`, or
1555
- `aragon config set mouse false`; the wheel then does nothing rather than
1556
- reverting to the old behaviour. Inline mode (`--no-fullscreen`) is untouched:
1557
- your terminal's own scrollback already does the right thing there.
1558
-
1559
- Under `tmux` with `mouse on`, tmux keeps the wheel for itself and the app
1560
- never sees it. `set -g mouse off` hands it back.
1561
-
1562
- ### Changed
1563
-
1564
- - **Your files moved to `~/.aragon-agent/`.** Config, sessions, installed skills
1565
- and the usage counters now live in one directory in your home — on Windows
1566
- `C:\Users\<you>\.aragon-agent\`, and the same path on macOS and Linux. They
1567
- used to be split across `%APPDATA%\aragon-agent-nodejs\Config` and
1568
- `%LOCALAPPDATA%\aragon-agent-nodejs\Data` (or the XDG equivalents), which are
1569
- correct locations and terrible answers to "where do I change the model?".
1570
-
1571
- **The move happens once, automatically, on first launch, and prints a line on
1572
- stderr when it does.** Nothing is deleted: `config.json` is COPIED rather than
1573
- moved, and the old directories are left where they are, so reinstalling 0.5.0
1574
- remains lossless. `aragon config home` prints the new location and names the
1575
- old ones. Once you are satisfied, you can delete them yourself.
1576
-
1577
- `ARAGON_HOME` relocates the whole tree. It has to be a real environment
1578
- variable — the path is resolved before any `.env` is read, so putting it in a
1579
- `.env` will not work. `aragon config home` always prints what is actually in
1580
- use.
1581
-
1582
- ### Added
1583
-
1584
- - **Logging.** Records go to `~/.aragon-agent/logs/aragon-<date>.log` as JSON
1585
- Lines: startup and exit, config writes, turn boundaries with token counts,
1586
- tool names with durations, skill loads, and every uncaught error. Files rotate
1587
- by day and by size (`log.maxFileBytes`, default 5 MiB) and the oldest beyond
1588
- `log.maxFiles` (default 10) are deleted.
1589
-
1590
- **`info`, the default, records no conversation content**, so attaching a log
1591
- to a bug report is safe. `--log-level debug` (or `--verbose`) adds prompts,
1592
- replies and tool arguments truncated to `log.previewChars`; `trace` adds them
1593
- in full. Those two write what you typed — check before sharing one.
1594
-
1595
- API keys are removed at every level by two independent passes: fields whose
1596
- name looks like a credential are blanked whatever they hold, and every string
1597
- is scanned both for known key formats and for the literal keys this process is
1598
- holding — which is what covers a custom endpoint whose key looks like nothing
1599
- in particular. `log.redactSecrets false` turns both off and warns you on
1600
- stdout when you set it.
1601
-
1602
- Nothing is ever uploaded. There is no network path out of this subsystem.
1603
-
1604
- New commands: `aragon logs path | list | tail [-n N] [--follow] [--level lv]
1605
- [--json] | clear --yes | open`, and `/logs` inside the TUI. New flags:
1606
- `--log-level`, `--verbose`, `--log-file` / `--no-log-file`, `--log-dir`. New
1607
- environment variables: `ARAGON_LOG_LEVEL`, `ARAGON_LOG_FILE`, `ARAGON_LOG_DIR`
1608
- — these three, unlike `ARAGON_HOME`, can come from a `.env`.
1609
-
1610
- - **`aragon config get <key>` / `list [--json]` / `edit` / `home`.** `get` and
1611
- `list` mask every secret, `--json` included. `edit` opens `config.json` in
1612
- `$VISUAL` / `$EDITOR` (`notepad` on Windows), takes a `config.json.bak` first,
1613
- and re-parses on exit — an invalid edit is reported immediately, and the
1614
- backup is named in the message.
1615
-
1616
- - **`/reload`** re-reads `config.json` into the running session, for when you
1617
- have edited it in another window. Refused while a run is in progress: swapping
1618
- the model mid-turn would let the second half of an answer come from different
1619
- settings than the first, with nothing afterwards able to explain the result.
1620
-
1621
- - **Log level in the settings screen**, alongside provider / model / thinking.
1622
- The rest of the log settings — and `redactSecrets` above all — stay
1623
- command-line-only, so the one switch that can put credentials on disk cannot
1624
- be flipped with an arrow key.
1625
-
1626
- ### Fixed
1627
-
1628
- - **A malformed `config.json` no longer resets everything in silence.** A stray
1629
- comma used to send the model, theme, timeouts, skills and (as far as you could
1630
- tell) your API key back to defaults, with nothing anywhere explaining it. The
1631
- fallback is unchanged — a bad config still never stops the CLI from starting —
1632
- but it now says so, and records why.
1633
-
1634
- - **Ctrl+C now flushes pending log records in every mode.** The signal handlers
1635
- only existed in the full-screen branch, so `-p`, the inline renderer and every
1636
- subcommand took Node's default termination, which does not run exit handlers.
1637
-
1638
- ### Added
1639
-
1640
- - **Plan mode.** `Shift+Tab` toggles between `BUILD` (everything as before) and
1641
- `PLAN`, a read-only research-and-design posture. `/plan [on|off|status]` is a
1642
- first-class equivalent for terminals that swallow `CSI Z`, and `--plan` /
1643
- `--no-plan` / `ARAGON_PLAN=1` / `planModeDefault` cover the startup case.
1644
-
1645
- In `PLAN`, `write_file`, `edit_file`, `bash`, `skill_install` and
1646
- `skill_create` are refused **at the tool boundary**, so read-only is a
1647
- property of the wiring rather than a promise in a prompt. `bash` is refused in
1648
- full, `git status` included: a shell string cannot be classified reliably, and
1649
- a gate that is right most of the time is worse than one that is always right,
1650
- because you stop trusting the badge. The refusal names `read_file` / `glob` /
1651
- `grep` as the alternatives.
1652
-
1653
- Two tools exist only while a human is attached. `ask_user` renders a
1654
- keyboard-driven wizard - 1-5 questions, 2-4 options each, exactly one marked
1655
- `RECOMMENDED`, plus an `Other...` free-text option - where pressing `Enter`
1656
- through the wizard accepts every recommendation. `submit_plan` renders the
1657
- plan as a scrollable card: `a` approves, `r` sends written feedback (`<-`
1658
- returns to the card), `Esc` dismisses. **Approving flips the session to
1659
- `BUILD` immediately and the same run continues into implementation.**
1660
-
1661
- Dismissing a plan asks for a better one rather than stopping - the agent is
1662
- told to refine and resubmit - so the way out of a run is `Esc` twice: the
1663
- first closes the card, the second aborts the run.
1664
-
1665
- Toggling mid-run is asymmetric on purpose: `build -> plan` applies at once,
1666
- `plan -> build` is deferred to the end of the run (the status bar shows
1667
- `PLAN -> BUILD` meanwhile) so a run launched under a read-only guarantee
1668
- cannot start writing files because of one stray keypress.
1669
-
1670
- `aragon -p --plan "..."` registers no question or plan tool - there is nobody
1671
- to ask - but the read-only gate still applies, so it means "tell me how you
1672
- would do this, and do not touch my repository".
1673
-
1674
- - Three flat config keys: `planModeDefault` (default `false`),
1675
- `planModeMaxAskRounds` (default `4`, clamped `[1, 10]`) and
1676
- `planModeHumanTimeoutMs` (default `1800000`, clamped `[60000, 7200000]`). All
1677
- three are settable with `aragon config set`. Additive, as always: a config
1678
- file written by an earlier version loads unchanged.
1679
-
1680
- ### Fixed
1681
-
1682
- - A `--confirm` prompt no longer races the idle watchdog. Taking longer than
1683
- `idleTimeoutMs` over `Proceed? (y/N)` used to abort the run with no
1684
- explanation; every human wait now suspends the watchdog and restarts it in a
1685
- `finally`.
1686
-
1687
- - Honor an explicit `NODE_TLS_REJECT_UNAUTHORIZED=0` setting in the CLI and the
1688
- official npm release workflow while hiding only Node's standard insecure-TLS
1689
- warning. Other process warnings remain visible.
1690
-
1691
- ## 0.5.0
1692
-
1693
- ### Breaking
1694
-
1695
- - **The package is renamed `@argon-agent/cli` → `@aragon-agent/cli`.** The brand
1696
- was always meant to be *Aragon*, matching AragonMesh; the missing `a` was a
1697
- typo that reached the registry. Install with:
1698
-
1699
- ```
1700
- npm uninstall -g @argon-agent/cli
1701
- npm install -g @aragon-agent/cli
1702
- ```
1703
-
1704
- `@argon-agent/cli` is deprecated and receives no further releases. **The
1705
- executable is still `aragon`** — the command you type does not change.
1706
-
1707
- - **Environment variables are `ARAGON_*`, not `ARGON_*`.** There is no
1708
- compatibility fallback: `ARGON_MODEL`, `ARGON_PROVIDER`, `ARGON_BASE_URL`,
1709
- `ARGON_THINKING`, `ARGON_MAX_TOKENS`, `ARGON_THEME`, `ARGON_FULLSCREEN`,
1710
- `ARGON_SKILLS`, `ARGON_SKILLS_DISABLED`, `ARGON_SKILLS_PATH` and
1711
- `ARGON_REDUCED_MOTION` are all ignored from this release on. Rename them in
1712
- your shell profile and CI. Provider keys (`ANTHROPIC_API_KEY`,
1713
- `OPENAI_API_KEY`, `GOOGLE_API_KEY`, `GEMINI_API_KEY`) are untouched.
1714
-
1715
- - **The project-level skills directory is `.aragon/skills`, not
1716
- `.argon/skills`.** This one lives in your repository, so rename it yourself:
1717
-
1718
- ```
1719
- git mv .argon .aragon
1720
- ```
1721
-
1722
- A config file that already persisted the old default keeps working — an
1723
- explicitly saved `skills.projectDirs` still wins over the default — but the
1724
- shipped default is now `['.aragon/skills', '.claude/skills']`. Update it with
1725
- `aragon config set` if you want the new default.
1726
-
1727
- ### Added
1728
-
1729
- - **Automatic migration of your user-level state.** On first run, 0.5.0 moves
1730
- the `env-paths` config and data directories from `argon-agent` to
1731
- `aragon-agent` — your API key, sessions and installed skills come with it, and
1732
- each installed skill's `.argon-skill.json` manifest is renamed to
1733
- `.aragon-skill.json` so `skills update` and `skills uninstall` still recognise
1734
- it. The migration prints one line to **stderr**, runs once, and is skipped
1735
- entirely if the new directory already exists. If it fails for any reason the
1736
- CLI still starts; the old directory is never deleted when a cross-volume copy
1737
- was needed.
1738
-
1739
- - The opening wordmark now reads `ARAGON` and is 52 columns wide (was 44).
1740
- Terminals between 52 and 55 columns show the single-line banner rather than a
1741
- clipped wordmark.
1742
-
1743
- - **Skills.** A skill is a directory holding a `SKILL.md` (YAML frontmatter +
1744
- Markdown body) plus optional `reference/`, `scripts/` and `assets/`. Skills
1745
- reach the model through three levels of progressive disclosure: a name +
1746
- one-line "when to use" catalog in the system prompt (≤ 6 000 bytes total),
1747
- the full body only when the model calls the `skill` tool, and bundled files
1748
- only when it reads them with `read_file` / `bash`. A skill therefore costs a
1749
- line of context rather than its whole body. How many fit in Level 1 depends on
1750
- description length and language — roughly 30 with terse English descriptions,
1751
- roughly 10 with Chinese ones at the character cap; past that the catalog
1752
- truncates and `skill_find` covers the remainder.
1753
- - Four model-facing tools: `skill` (load), `skill_find` (search installed
1754
- skills), `skill_install` (install from a local directory, git repo, or https
1755
- `SKILL.md` / `.zip`) and `skill_create` (author a new one from a procedure the
1756
- agent just worked out).
1757
- - **`aragon skills update <name> | --all`**, with `--dry-run`, `--force` and
1758
- `--check`. Re-fetches from the source recorded in the install manifest,
1759
- preserving `installedAt` and recording `updatedAt` / `previousVersion`. Refuses
1760
- before downloading anything when the installed copy has local edits, and
1761
- re-validates the source against the *current* `skills.allowedHosts` rather than
1762
- the one in force at install time. Not exposed to the model.
1763
- - **Catalog ranking.** Level 1 entries are ordered by scope, then by how recently
1764
- and often each skill was used, so truncation drops what you do not use rather
1765
- than what sorts last alphabetically. With no usage data the order is unchanged.
1766
- - **`skill_find`**, advertised in the prompt only when the catalog actually
1767
- truncated. Searches installed skills by name, description and keyword; never
1768
- reaches the network, never returns `activation: manual` skills, and tells the
1769
- model to ask for a source rather than invent one when nothing matches.
1770
- - **Cross-process install lock.** Install / create / remove / update take a
1771
- per-directory advisory lock, closing a race in which two concurrent installs
1772
- could interleave their atomic-replace steps and delete a skill with no error.
1773
- Self-expires after 60 s; waits at most 5 s; only ever released by its owner.
1774
- - **Load-time integrity.** Discovery re-checks `SKILL.md` against the sha256
1775
- recorded at install, at no extra I/O. `skills.integrity` selects
1776
- `off` / `warn` (default) / `strict`.
1777
- - **Local usage counters** at `<data>/skill-usage.json` — skill name, count and
1778
- timestamp only, never transmitted, disabled with `skills.usageTracking=false`.
1779
- Automatically injected `activation: always` skills are not counted.
1780
- - Config keys `skills.integrity` and `skills.usageTracking`; `aragon skills list
1781
- --sort=recent` and `/skills list --sort=recent`.
1782
- - Every usable skill also becomes a slash command: `/<skill-name> [args]`, with
1783
- `$ARGUMENTS` / `$1..$9` substitution. On a name clash the built-in command
1784
- always wins and the skill is reachable as `/skill:<name>`.
1785
- - `/skills` management: `list`, `info`, `install`, `remove`, `enable`,
1786
- `disable`, `reload`, `create`, `trust`, `untrust`.
1787
- - `aragon skills …` non-interactive equivalents plus `path` and `doctor`.
1788
- Exit codes follow `config set`: `0` ok, `1` run-time failure, `2` usage.
1789
- - Four discovery scopes in ascending precedence: bundled (`<pkg>/skills`), user
1790
- (`<data>/skills`), project (`<cwd>/.aragon/skills` and, read-only,
1791
- `<cwd>/.claude/skills` for Claude Code interop), and `ARAGON_SKILLS_PATH`.
1792
- - New flags `--no-skills`, `--skill <name>` (repeatable) and `--skills-yes`;
1793
- env `ARAGON_SKILLS`, `ARAGON_SKILLS_PATH`, `ARAGON_SKILLS_DISABLED`; config keys
1794
- `skills.enabled`, `skills.requireApproval`, `skills.catalogMaxBytes`,
1795
- `skills.bodyMaxBytes`.
1796
- - One bundled skill, `skill-creator`, documenting how to write a good one.
1797
- - New runtime dependency: `fflate` (~30 KB, MIT, zero transitive deps) for zip
1798
- extraction. `@aragon-agent/core` gains no dependency.
1799
- - **Release tooling for the scope change.** `npm run verify:brand` scans every
1800
- file that `npm pack` would put in the tarball — README and `dist/` included —
1801
- and blocks the release on any surviving `argon` reference, with only the four
1802
- documented exceptions (the two CHANGELOGs and the legacy-state migration
1803
- module, which hold the old names by definition). `packages/cli` also gains a
1804
- `prepublishOnly: npm run build`, so a publish that bypasses
1805
- `publish-latest.ps1` can no longer ship a `dist/` that is out of step with the
1806
- sources. `deprecate-legacy.ps1` retires `@argon-agent/*` on the registry, and
1807
- refuses to do so until the replacements are actually published.
1808
-
1809
- ### Changed
1810
-
1811
- - The globally installed executable is now `aragon`. The previous `argon` and
1812
- `argon-agent` aliases are no longer installed.
1813
- - `config/store.ts` now deep-merges the `skills` section on both read and write,
1814
- the same treatment `apiKeys` already received. Without it a partial patch such
1815
- as the one `/skills disable` sends would replace the whole section and
1816
- silently discard `trustedProjectDirs`, `allowedHosts` and `requireApproval`.
1817
- - `slashSuggestions` accepts `-` and `:`, so kebab-case and `skill:`-namespaced
1818
- commands autocomplete. Previously `/my-sk` produced no popup at all.
1819
- - `AgentController` now has a single `agent.setSystemPrompt()` call site, so a
1820
- `/cwd` change can no longer drop the skill catalog out of the prompt.
1821
- - With skills off or none installed, the system prompt and the 7-tool array are
1822
- byte-identical to the previous release.
1823
- - Added a repository-level PowerShell release workflow that automatically
1824
- updates CLI and Core versions, synchronizes their dependency and lockfile,
1825
- verifies both tarballs, and publishes them in dependency order.
1826
-
1827
- ### Security
1828
-
1829
- - Installing a skill requires an explicit human approval that **probes for a
1830
- human first and refuses when there is none**. Under `-p` there is no TUI and
1831
- therefore no approver, so `skill_install` is refused with an actionable
1832
- message rather than writing a third-party directory unattended. Use
1833
- `--skills-yes` (or `skills.requireApproval=false`) to opt out deliberately.
1834
- - Project skill directories require a one-time trust confirmation before they
1835
- are loaded — cloning an unfamiliar repository does not grant it the ability
1836
- to inject instructions into your session.
1837
- - Git is invoked only via `execFile` with an argv array and `shell: false`, and
1838
- owner / repo / ref / subdirectory fragments are whitelisted and refused if
1839
- they begin with `-`. A leading dash matters even with a pure argv array:
1840
- `--upload-pack=<program>` makes git execute that program.
1841
- - Zip extraction is streamed and gated on **actually written** bytes, per-entry
1842
- size, compression ratio and entry count, all of which can abort mid-archive.
1843
- The size a zip *declares* is written by whoever built it and is never trusted.
1844
- Path traversal, absolute entries, symlinks and Windows device names are
1845
- refused, and a rejected archive is removed rather than left half-extracted.
1846
- - Network fetches are `https:` only, restricted to `skills.allowedHosts`,
1847
- re-checked on every redirect (max 3), size-capped, and refuse IP literals and
1848
- loopback.
1849
- - Nothing a skill ships is executed at install time, and the executable bit is
1850
- not preserved. Scripts run only when the model invokes `bash` explicitly.
1851
- There is no sandbox: approval blocks *silent* installation, it does not make
1852
- a skill you approved safe.
1853
-
1854
- ## 0.3.0
1855
-
1856
- A full-screen TUI release. `@argon-agent/core` is untouched, no new runtime
1857
- dependency is added, and the headless/print contract, exit codes, and
1858
- slash-command semantics are unchanged. The 0.2.0 inline renderer remains
1859
- available in full via `--no-fullscreen`.
1860
-
1861
- ### Added
1862
-
1863
- - **Full-screen mode (default).** `aragon` now takes over the terminal's
1864
- alternate screen buffer — the same mechanism `vim` / `htop` / `lazygit` use.
1865
- Your shell history is covered, not erased, and comes back untouched on exit.
1866
- The frame is a fixed `rows - 1` tall, so **the composer and the status bar sit
1867
- at the physical bottom of the screen from the very first frame**: empty
1868
- session, long session, mid-scroll, or overlay open.
1869
- - **Brand region.** A six-row gradient ASCII wordmark on a roomy empty session,
1870
- degrading to a banner, then a single-line bar, then a bare wordmark as space
1871
- runs out. Row 1 always begins with the brand glyph.
1872
- - **Self-drawn scrolling.** `PgUp` / `PgDn` by the page, `Shift+↑` / `Shift+↓` by
1873
- the line. The viewport auto-follows new output while pinned to the bottom;
1874
- scrolling away shows `↑N` in the status bar plus a "N new lines" hint, and
1875
- submitting a message always re-pins.
1876
- - **Exit replay.** After leaving the alternate screen the session is replayed
1877
- into the normal buffer as plain, colorless text, so the conversation does not
1878
- evaporate with the screen. Turn it off with `--no-exit-transcript`.
1879
- - **New flags** `--fullscreen` / `--no-fullscreen` / `--no-exit-transcript`, the
1880
- env var `ARGON_FULLSCREEN=0|1`, and the config keys `fullscreen`,
1881
- `exitTranscript`, and `transcriptWindow`.
1882
- - **Automatic downgrade.** A non-TTY stdout, `TERM=dumb`, CI, and terminals under
1883
- 12 rows / 40 columns fall back to the inline renderer. Only the non-TTY gate is
1884
- un-overridable — `--fullscreen` beats the rest.
1885
-
1886
- ### Changed
1887
-
1888
- - The status bar lost its round border and is now exactly one row; it had been
1889
- spending an eighth of a 24-row terminal framing a single line of text.
1890
- - The toast strip holds a fixed row in full-screen mode, so the transcript no
1891
- longer jumps as toasts appear and expire.
1892
- - The composer is a rounded frame whose border color reports state (idle, has a
1893
- draft, running, blurred by an overlay), with the completion popup above it.
1894
- - `Ctrl+L` repaints the frame instead of writing a clear sequence. Ink dedupes
1895
- identical output at two separate gates, so under a fixed frame the old
1896
- approach erased the screen and then declined to redraw it.
1897
- - `Shift+↑` / `Shift+↓` no longer recall prompt history — they scroll.
1898
- - Full-screen mode routes `console.*` into the transcript as notices rather than
1899
- letting Ink write them straight to stdout, which corrupts the frame's line
1900
- accounting.
1901
-
1902
- ### Known trade-offs
1903
-
1904
- - The terminal's native scrollback and mouse wheel do not scroll the transcript
1905
- in full-screen mode; use `PgUp` / `PgDn`, or run with `--no-fullscreen`.
1906
- - Mouse tracking is deliberately left off: enabling it costs text selection and
1907
- copy in most terminals.
1908
- - Should a crash ever strand your terminal on the alternate screen, `reset`
1909
- restores it. Four independent restore paths exist to prevent that: normal
1910
- exit, `process.exit`, `SIGINT` / `SIGTERM` / `SIGHUP`, and Ink's own signal
1911
- handling.
1912
-
1913
- ## 0.2.0
1914
-
1915
- A design-elevation & HCI-hardening release. No breaking changes: `@argon-agent/core`
1916
- is untouched, no new runtime dependency is added, and the headless/print contract,
1917
- exit codes, config keys, and slash-command semantics from 0.1.0 are unchanged.
1918
-
1919
- ### Added
1920
-
1921
- - **Real theming engine.** Distinct `dark` / `light` palettes with an `auto`
1922
- resolution (`auto → dark`), color degradation across terminal depth
1923
- (truecolor → 256 → 16 → monochrome), and an ASCII glyph fallback for terminals
1924
- without Unicode. Honors `NO_COLOR`, `FORCE_COLOR`, `--no-color`, and
1925
- `TERM=dumb`. Switch live with `/theme <auto|dark|light>` (persisted).
1926
- - **Elegant chrome.** A gradient wordmark header that collapses to a slim sticky
1927
- bar after the first turn, a first-run **Welcome / empty-state** card, and a
1928
- status bar with a colored **context gauge** (threshold colors), a **tokens/sec**
1929
- read-out while running, and a contextual hint.
1930
- - **Legible tool output.** Colored unified diffs (green adds / red removes) and
1931
- per-tool rich previews (`edit_file`/`write_file`/`read_file`/`bash`/`list_dir`/
1932
- `glob`). Long previews collapse to 8 lines and expand with `Ctrl+O` or
1933
- `/expand [n]`.
1934
- - **Autocomplete.** A slash-command palette and `@file` path completion popup
1935
- (`Tab`/`→` to complete, `Up`/`Down` to move, `Esc` to close).
1936
- - **Full line editing.** Home/End, word jump/delete, kill-to-start/end, and
1937
- multi-line vertical cursor navigation, with prompt-history recall at the edges.
1938
- Unknown control/escape sequences are dropped instead of inserted.
1939
- - **Reliability under load.** Ink `<Static>` finalization for settled entries and
1940
- a streaming delta coalescer, so long/fast sessions stay flicker-free.
1941
- - **Calm feedback.** Ephemeral, auto-dismissing **toasts** for transient acks;
1942
- the transcript keeps only durable content (messages, tool cards, run errors).
1943
- - `reducedMotion` config (also `ARGON_REDUCED_MOTION`; implied by `--no-color`)
1944
- replaces spinners with a static glyph.
1945
-
1946
- ### Changed
1947
-
1948
- - History scrollback now uses the **native terminal scrollback** that `<Static>`
1949
- prints into (this supersedes the never-implemented `PgUp`/`PgDn` promise).
1950
-
1951
- ## 0.1.0
1952
-
1953
- Initial release — a Claude-Code / Codex-style interactive terminal UI (TUI) on
1954
- top of `@argon-agent/core`.
1955
-
1956
- ### Added
1957
-
1958
- - **Interactive TUI** (React + Ink): gradient header, streaming transcript with
1959
- markdown + syntax highlighting, collapsible thinking blocks, live tool-call
1960
- cards, a status bar (model / context % / tokens / cost / elapsed), and modal
1961
- overlays (settings, model picker, help, confirm).
1962
- - **Built-in toolset** wired into the core agent at full permission:
1963
- `read_file`, `write_file`, `edit_file`, `list_dir`, `glob`, `grep`, `bash`.
1964
- - **Layered configuration** (defaults → user file → env/`.env` → flags) with
1965
- live in-TUI editing of provider / model / base URL / API keys / thinking level
1966
- / max tokens. Secrets are masked and the config file is written `0600` on POSIX.
1967
- - **One-shot / print mode** (`argon -p`) for scripting and piping, plus
1968
- `argon config` / `argon config set` / `argon config path` / `argon models`.
1969
- - **Session control**: abort (Esc), steer-while-running, follow-up queue,
1970
- double-Ctrl-C exit, `/save` / `/resume` JSON sessions.
1971
- - **Timeout invariant** (`idleTimeout ≥ toolTimeout`) so a long single tool run
1972
- is never killed by the idle watchdog.
1973
- - **Never-silent failures**: pre-flight API-key validation, error-StreamEvent
1974
- rendering, and a guard that surfaces a swallowed agent error instead of a
1975
- blank no-op (headless exits `1`/`2` accordingly).
1
+ # Changelog
2
+
3
+ All notable changes to `@aragon-agent/cli` are documented here. Entries at
4
+ `0.4.x` and earlier refer to the package by its former name, `@argon-agent/cli`
5
+ — those releases really were published under that name, so they are left as they
6
+ were written.
7
+
8
+ ## Unreleased
9
+
10
+ ### Added
11
+ - **`Ctrl+L` branches this session into a new terminal - and `Ctrl+R` now
12
+ redraws (KEY BINDING CHANGE).** One keystroke opens a PowerShell (Windows) or
13
+ bash (macOS/Linux) window in the session's current `/cwd`, running
14
+ `aragon --resume <id>` there: the child derives its own id and only ever writes
15
+ its own file, so the TUI you branched from keeps running untouched. The session
16
+ is force-flushed to disk BEFORE anything is spawned, because the child reads
17
+ that file first and the normal checkpoint is debounced. Works mid-run too - the
18
+ child inherits the last settled turn. Platforms: a new Windows Terminal tab
19
+ (`wt -d <cwd> ... --`), a new console window (`cmd /c start`), a Terminal.app
20
+ window via `osascript`, or the first available Linux emulator; each rung carries
21
+ the working directory itself, because `--resume` does not restore one. No
22
+ terminal (SSH, container) prints a pasteable `aragon --resume <id>` instead of
23
+ failing silently. `ARAGON_BRANCH_COMMAND` / `ARAGON_BRANCH_SHELL` override what
24
+ runs and which shell; presses are throttled to one window per two seconds and a
25
+ failed attempt never starts the cooldown. New `/branch` is the same code path.
26
+ The header (top right) now shows `^L branch` next to `^I Index` and `^G more`,
27
+ spending a fixed cell budget: `^I` therefore appears from 52 columns
28
+ (collapsed) / 62 columns (expanded) instead of 72, and on a very narrow terminal
29
+ the branch hint degrades to the bare `^L` before it is dropped.
30
+ **Breaking:** the redraw previously bound to `Ctrl+L` moved to `Ctrl+R` - same
31
+ behaviour, same overlay guard (the settings screen keeps its own `Ctrl+R`).
32
+
33
+ - **A TUI session now has an id, and another terminal can continue it.** Every
34
+ interactive session is written to `~/.aragon-agent/sessions/<id>.json` as it
35
+ runs and shows that id at the right end of the status bar (`id a4k2m9`, or
36
+ `a4k2m9` on a narrow terminal - the id itself is never truncated). Checkpoints
37
+ are written after the first frame, after every settled turn (debounced), on
38
+ `/session pin`, and once on exit; each one is an atomic replace, because a
39
+ second terminal may be reading. `aragon --resume <id|path>` continues a saved
40
+ session under a NEW id and never writes the file it came from, so branching
41
+ while the original TUI keeps running is safe - including
42
+ `aragon exec --resume <tui-id>`, which now writes a session of its own instead
43
+ of overwriting yours. `aragon --save <id>` names this run's id and pins it
44
+ against `sessions prune`. New `/session [copy|pin]` reports the id, the file and
45
+ the branch parent, copies the resume command through the same clipboard door as
46
+ `/copy` (OSC 52 included), and pins the file. A checkpoint taken mid-tool is
47
+ repaired on the saved copy only, so resuming it cannot produce an illegal
48
+ history, and a tool card that was still running is settled so nothing spins
49
+ after a resume. `aragon -p` writes one checkpoint at exit: context only, no
50
+ transcript, because nothing was displayed.
51
+
52
+ - **A model profile can be verified where it is written.** The profile editor
53
+ gains two action rows: **Test connection** (`Ctrl+R`) and **Discover models**
54
+ (`Ctrl+F`). A test is one real completion against the UNSAVED draft - the
55
+ provider, model, base URL and the key `keep`/`replace`/`shared` actually
56
+ resolves to - and it answers with a verdict (`OK 812 ms | 18 in / 4 out`) or a
57
+ classified failure plus the one thing to fix (`Key rejected by anthropic (HTTP
58
+ 401).` / `Fix: edit API key, or set Credential mode to Shared.`). A discovery
59
+ lists what the key can really reach, so the model is picked instead of typed.
60
+ Both run against the draft, cost at most one short request (discovery costs no
61
+ tokens), never retry, and never write `ModelRegistry`'s five-minute discovery
62
+ cache. Every result is bound to a target fingerprint, so an `OK` can never
63
+ describe a model, endpoint or key the user has since edited. The progress line
64
+ is static text: no new animated site, so `single-spinner` still holds.
65
+
66
+ ### Changed
67
+
68
+ - **BREAKING DEFAULT: the TUI now scrolls with your terminal (`scrollMode: native`).**
69
+ Until now the interactive TUI ran in the terminal's alternate screen with its own
70
+ scrollbar, wheel handling and drag-selection. The default is now **native scroll
71
+ mode**: finished messages are printed into the host terminal's own scrollback
72
+ (PowerShell, `cmd`, Windows Terminal, `bash`, `zsh`, `tmux`, iTerm2, ...), above a
73
+ small live region holding the streaming preview, the input box and the status
74
+ line. The wheel, the scrollbar, `Shift+PgUp`, text selection, right-click menus
75
+ and in-terminal search are now the terminal's; `aragon` neither enters the
76
+ alternate screen nor turns on mouse reporting (so Windows needs no particular
77
+ Node version for scrolling). Exiting prints a short summary instead of replaying
78
+ the transcript, which is already in your scrollback (`exitTranscript` is ignored
79
+ in this mode). Printed rows are final: they keep the width they were printed at,
80
+ `/clear` prints a separator instead of erasing history, and `Ctrl+O` prints an
81
+ expanded copy of a tool card. The TODO side panel and team panel are not drawn
82
+ (status chips and `/queue` carry them). **To get the old behaviour back:**
83
+ `aragon config set scrollMode app` (or `--scroll-mode app` /
84
+ `ARAGON_SCROLL_MODE=app` for one run); the full-screen mode is unchanged. A
85
+ one-time notice explains this on first launch. `--fullscreen` / `ARAGON_FULLSCREEN`
86
+ stay removed - the new switch is `--scroll-mode`. See README, "Scrolling".
87
+
88
+ - **`aragon sessions list` gained an `origin` column** and shows TUI checkpoints,
89
+ which are managed files now. `sessions prune` defaults to `--origin exec`, so it
90
+ behaves exactly as before for every file that existed before this release; the
91
+ new `--origin tui|all` selects the other writer. `prune` never deletes a pinned
92
+ session or one whose owner process is still alive, and `--all` now means only
93
+ "also consider files with no session metadata" - it can no longer reach a TUI
94
+ checkpoint. `/resume` inside the TUI moves the session to a new id and restarts
95
+ the spend counter, because a new id's file must not bill the previous
96
+ conversation.
97
+
98
+ ### Fixed
99
+
100
+ - **A long answer lost two characters at every hard wrap point - silently, in
101
+ exactly the content a user copies out.** The transcript's content column had no
102
+ width of its own, so Yoga laid each line out at the width it computed from the
103
+ parent while `virtual-window` estimated heights against `cols - GUTTER_WIDTH`:
104
+ two authorities for one number, and the clip kept the difference. A 240-digit
105
+ run in a 100-column transcript rendered as 97 + 97 + 42 = 236 digits, dropping
106
+ the characters at column 97 and 194 - URLs, git hashes, base64, paths and code
107
+ lines broke where nothing looked broken, because a paragraph with spaces re-wraps
108
+ at word boundaries and hides it. The column now takes `flexBasis={0}
109
+ flexGrow={1}` and keeps `flexShrink={0}` (invariant I-2: without it
110
+ `measureElement` can squeeze the column to the viewport and scrolling dies with
111
+ no error), so the available width is computed once, by yoga, from the parent.
112
+ Measured on the shipped component: 97 + 97 + 46 = 240/240, first divergence at
113
+ index -1.
114
+ - **A bracketed paste could type its own markers into the draft as `[200~hello
115
+ world`.** The startup `ESC[?2004h` is correct, but when a read boundary landed
116
+ inside the six-byte marker - ordinary on a Windows console, over SSH, or in tmux
117
+ under load - the 12 ms pending flush released the lone `ESC` as the Esc key (it
118
+ must never be held hostage), and the `[200~` that followed had nothing left to
119
+ recognise: `indexOf` needs the ESC. The filter now remembers a marker head it has
120
+ just released and, on the NEXT chunk only, splices it back in front for one
121
+ re-scan inside the same `feed()` - so the marker is consumed before its bytes
122
+ become text, `PENDING_FLUSH_MS` stays at 12 ms, and a new
123
+ `PASTE_MARKER_RESCAN_TTL_MS` (250 ms, structural, in `input/limits.ts`) bounds
124
+ when that is still believable. The visible garbage is gone in all three draft
125
+ measurements (`> hello world_`); the one residual is stated rather than hidden -
126
+ a write cannot be retracted, so the Esc keypress the release already produced
127
+ still happens, and a marker torn into three parts still leaves its middle
128
+ fragment behind (both recorded in `docs/diagnoses/.../analysis.md` §D-2).
129
+ - **A turn that only called a tool left a bare `●` bubble behind - on screen, in
130
+ the height table and in the saved session.** `turnStart` always appends an
131
+ assistant entry, so a tool-only turn, or one the user interrupted mid-stream,
132
+ left a bubble that `AssistantEntry` then refused to draw: the row was estimated
133
+ anyway (two for an aborted one, of which nothing drew the second), the scroll
134
+ position counted it, and `/save` exported it, so `/resume` brought it back. The
135
+ reducer now prunes a settled assistant entry with no text and no thinking, at
136
+ the three settle points only (`turnEnd`, `runEnd`, `abortMark`) - pruning earlier
137
+ would delete the live target of every `textDelta`, and the entry a `streamRestart`
138
+ is still filling in must survive. `abortMark` is the one settle path that does not
139
+ clear `streamingId` itself, so the helper returns the id and the caller writes it
140
+ back (I-L1-1); `runEnd` and `turnEnd` clear it anyway. A render-layer guard stays
141
+ as the second line of defence for a session file that already contains one.
142
+ - **An empty transcript still drew a full-height scrollbar track.** One rail
143
+ column of track glyphs from the second row down told the user there was more
144
+ below when there was nothing, and cost a column of width. `thumb === null` now
145
+ means no cells at all - the one-column BOX is still rendered, because the
146
+ reserved column (not the glyphs) is what keeps the content width constant; hiding
147
+ the column instead would re-wrap every line, change the measured height and feed
148
+ that back into the decision to show it.
149
+ - **Under `--no-color` the command palette's selected row was invisible, and a
150
+ bare `@` never opened file completion.** The selection was expressed only as
151
+ `inverse`, which is the identity at colour level 0 - and every `theme.*` colour is
152
+ `undefined` there too - so six commands looked identical and Enter was a guess
153
+ (`/clear` sits next to `/cwd`). The selected row now carries the same
154
+ `glyphs.caret` prefix the `/model` picker uses (`>` in the ASCII tier), with two
155
+ cells of padding on the unselected rows so no row changes width. Separately,
156
+ `README` has always promised that `@` opens a file-path completion popup while
157
+ the code required one more character: the `>= 1` gate and the `< 1` early return
158
+ are gone, and an empty `@` query lists the shallowest paths first (depth, then
159
+ length, then alphabetical) instead of the first twenty the filesystem happened to
160
+ hand back.
161
+ - **The completion menu was pinned at six rows on every terminal.** `App` handed
162
+ the popup a literal `6` while the frame budget had already computed the slot
163
+ capacity: a 30-row terminal showed 6 of 57 commands and 14 more hidden, so
164
+ browsing meant holding an arrow key. The cap is now derived from
165
+ `frameBudget.popupMaxHeight` through `POPUP_CAPACITY_SHARE` (2) in
166
+ `layout/budget.ts` - 6 rows at 20 lines, 11 at 30, 16 at 40. Handing over the
167
+ whole capacity was measured and rejected: `popupCapacity` reserves exactly one
168
+ message row, so at 20 rows it grants 13, the transcript collapses to a single row
169
+ and the TODO rail disappears. A divisor keeps the menu derived from the frame
170
+ budget instead of becoming a second authority beside it, and since it does not
171
+ depend on `popupRows` it cannot oscillate with it.
172
+ - **A fenced code block's language tag rendered as a body row, indented one cell
173
+ past the code.** The chip background and its padding spaces drew ` | ts` above
174
+ code rows starting `| export ...`, so the fence header read as an extra line of
175
+ source. It is now a muted bold TITLE on the same rail, aligned with the code, and
176
+ it deliberately keeps its own row: the height estimator counts raw markdown
177
+ lines and has no code-block branch, so the fence line must still occupy exactly
178
+ one. The fence grammar was too narrow as well - `(\w*)` rejected `c++`,
179
+ `objective-c` and four-backtick fences, degrading the whole block to prose and
180
+ swallowing the closing fence, and `split('\n')` left a `\r` on every CRLF code
181
+ line; a block now opens for 3+ backticks with any non-backtick info string, only
182
+ a run at least as long as its opener closes it, and the document is split
183
+ CRLF-safe.
184
+ - **A Markdown table rendered as space-separated plain text.** The `|---|` divider
185
+ was consumed by the parser to read alignment and never drawn, so a table had no
186
+ header marker, no column boundary and no rule - and under `--no-color`, where
187
+ bold is dropped too, nothing at all distinguished the header. `formatTable` now
188
+ returns `[header, divider, ...body]`, draws the rule from the caller's glyph and
189
+ separates columns with a visible `|` (both injected by `Markdown.tsx` from
190
+ `pickGlyphs`, so the parser stays ASCII-only and the pure default is `|`/`-`),
191
+ and pads by DISPLAY columns rather than `String.length`, which misaligned every
192
+ CJK cell. The separator costs one column per boundary; a table wide enough to
193
+ exceed the transcript is truncated rather than wrapped, which is the deliberate
194
+ trade recorded in §D-6.
195
+ - **A command that succeeded said nothing, and its notice then displaced
196
+ `Idle` / `Running` until the next one.** `/theme light` changed the SGR, `/todo
197
+ panel on` changed the layout, `/expand 1` with nothing to expand did nothing -
198
+ and the only feedback was the echoed command, so "it worked", "the argument was
199
+ wrong" and "the key was never received" looked identical. Worse, the body had
200
+ been reaching `status-layout` all along but only rendered on the second status
201
+ row, behind Ctrl+G and off by default, while the phase word was REPLACED by the
202
+ feedback label: after `/copy` the row said `Copied` and never went back to
203
+ `Idle`, which is the one thing that row exists to answer. Notification kinds
204
+ (`notice`, `copied`, `copy-sent`, `copying`) now keep the phase word and add the
205
+ body as their own field, clipped to 32 display cells; action kinds (`confirm`,
206
+ `stopping`, `force-stop`, `exit`, `clear-input`, `copy-error`, `copy-cleanup`)
207
+ still take the phase slot, because "what the next key does" IS the phase. The
208
+ body is dropped before the phase word would be, and a copy error no longer paints
209
+ `Idle` red.
210
+ - **`/help` turned 8 rows per PgDn on a 19-row window - 42% of a page.** Reading
211
+ the 93-row shortcut list took twelve presses and overlapped eleven rows every
212
+ time, while PgUp/PgDn in the transcript moved a whole page. The step is now
213
+ `overlayPageSize(overlayBodyRows(...))` - one window minus the two context rows
214
+ `scroll.pageSize` already keeps - and it reaches BOTH consumers, the keyboard
215
+ branch and the Shift+wheel notch (`getOverlayPage`), from the one function
216
+ `OverlayFrame` renders its window with, so the two cannot disagree.
217
+ - **On a narrow terminal the header's right-hand hints collided with the
218
+ truncated caption.** The left half is `wrap="truncate"` and draws its ellipsis in
219
+ the last cell it owns, and the hint column started in the very next cell, so 52
220
+ columns read `aragon...Details off` as one string and 60 columns read
221
+ `glm-5.^L branch`. The row now spends a new `HEADER_HINT_LIMITS.separatorCells`
222
+ (2) between the halves, taken out of both the left half's width and the planner's
223
+ budget - a separate constant from `gapCells`, which spaces two hint clauses
224
+ inside the right column, because one constant carrying two meanings is how a
225
+ later spacing change silently re-lays-out the whole row. The `^I Index`
226
+ thresholds move with it: 54 columns collapsed and 64 expanded, two later than
227
+ before.
228
+ - **A large paste arrived as runs of digits the moment it was sent - on screen,
229
+ in the model's input, in the prompt history and in the session file.**
230
+ `sanitisePaste` deleted the C0/C1 *introducer byte* and kept the parameter
231
+ bytes, so `ESC[38;2;18;52;86m` reached the transcript as the literal text
232
+ `[38;2;18;52;86m`. The composer collapses a large paste into
233
+ `[Pasted text #N +20 lines]`, so nothing was visible until submit expanded
234
+ it: a measured 20-line coloured log produced 80 runs of digits, and the same
235
+ string was what `/context`, the ↑ history and the session checkpoint stored.
236
+ Cleaning is now SEQUENCE-based and has one owner (new
237
+ `input/control-sequences.ts`, ASCII-only, no new dependency): a complete
238
+ escape goes as a unit, a truncated one loses its introducer and parameters
239
+ but never the character after them, an unterminated OSC/DCS/APC/PM/SOS goes
240
+ to the end of its line and never across it, and bare text that merely looks
241
+ like a sequence body (`[38;2m` with no ESC) is left exactly as it is.
242
+ `tools/terminal-output.ts`'s `stripAnsi` becomes a thin wrapper over the same
243
+ syntax, so a tool row and a paste cannot drift apart (both directions are
244
+ pinned by a cross-check test, and `terminal-output.test.ts` is unchanged).
245
+ A **single-line** paste carrying ESC - which `classifyChunk` sent down the
246
+ keystroke path with no cleaning at all - is now framed and cleaned like any
247
+ other paste; measured on the real component that path either put a live ESC
248
+ plus `[38;2;18;52;86m` into the draft or threw an uncaught `TypeError` inside
249
+ Ink's key parser. win32-input-mode records inside a frame are translated
250
+ rather than deleted, so a `?9001h` paste yields its characters instead of 15
251
+ visible digits per key (or, with cleaning but no translation, nothing at all).
252
+ - **A pasted TAB-indented block or TSV table lined up differently before and
253
+ after sending.** One byte had three readings: the composer painted
254
+ four-column tab stops, the transcript handed the raw `\t` to the terminal
255
+ (eight), and the exit replay counted code units. New `ui/display-tab.ts`
256
+ expands a TAB by DISPLAY column (`string-width`, so a `CJK<TAB>X` line lands
257
+ where the terminal paints it); the composer's stop is the terminal's eight, and
258
+ `UserEntry`, `QueuedEntry` and the exit replay expand at paint time only -
259
+ the stored payload keeps its `\t`, so the model still receives the original
260
+ indentation. `wrappedRows` counts display columns too (with an ASCII fast
261
+ path, without which a 10 000-entry transcript timed out), because an estimate
262
+ that reads a TAB as one column keeps the row short while the painted row
263
+ grows - the under-estimate that file names as the unsafe direction.
264
+ - **`Ctrl+L` opened a window that failed with a PowerShell `ParserError` on
265
+ Windows.** The script handed to `-EncodedCommand` was the argv joined with
266
+ quotes but with no call operator, and PowerShell reads a quoted string in command
267
+ position as an *expression*: with node installed under `C:\Program Files\...`
268
+ the child was never created - `UnexpectedToken` at column 36 - while the parent
269
+ TUI still reported that the window opened. `powerShellScript` now emits
270
+ `& <argv...>`, which is the only form that runs a word of any shape as a
271
+ command. The failure fallback follows: it is now two labelled lines
272
+ (`PowerShell:` / `cmd.exe :`), because one line cannot be correct for both
273
+ parsers - and the `PowerShell:` line is byte-for-byte the script the window
274
+ receives. A leading `@` can no longer be left bare either, since `@name` is
275
+ PowerShell's splat operator and silently swallows an argument. New
276
+ `powershell-script-parse.test.ts` hands the generated scripts to the real
277
+ Windows PowerShell parser (and pins the old shape to its exact error), so this
278
+ class of defect has a guard instead of a green suite.
279
+ - **A conversation saved mid-tool could be resumed into an illegal history.**
280
+ A `tool_call` with no matching `tool_result` makes the next request fail. The
281
+ checkpoint writer closes every dangling call on the copy it writes and re-checks
282
+ sendability before replacing the file; a history it cannot make sendable is not
283
+ written at all, so the previous good checkpoint survives.
284
+ - **A checkpoint could carry a tool card that never stopped spinning.** A card
285
+ saved while its tool was running kept `ToolCard` animated and kept the
286
+ transcript's settled boundary from ever passing it, which re-rendered the tail
287
+ on every frame for the rest of the resumed session. Cards are now settled on the
288
+ saved copy only, which leaves `/save` (an explicit export you are watching) and
289
+ its documented behaviour untouched.
290
+ - **`aragon exec --resume <tui-id>` overwrote the session it resumed.** The
291
+ write-back id preferred the file's own metadata, so exec took the TUI's id and
292
+ replaced the checkpoint - silent, unrecoverable loss of the conversation still
293
+ running in the other terminal. A TUI file now always yields a fresh id, and the
294
+ same rule covers `--session-id`, which used to return the typed id verbatim.
295
+ - **`sessions prune --origin tui --yes` could delete a running session's
296
+ checkpoint.** The `live` marker was written by the TUI but the process lock it
297
+ is checked against was only ever taken by `exec`, so the liveness guard answered
298
+ "nobody owns this" for exactly the files it was written for. The TUI checkpoint
299
+ writer now holds the session lock for as long as it owns the file, releases it
300
+ on exit, and moves it when `/resume` rotates the id.
301
+ - **`aragon --save <id>` said "already exists" for an id that does not exist.**
302
+ A session directory that cannot be written (read-only home, full disk) was
303
+ reported as a name collision, which sends the user to `--resume <id>` and then
304
+ to "no such session". It now names the directory instead.
305
+ - **A hand-written `/save` file resumed as if it were a branch of itself.**
306
+ `--resume ./notes.json` recorded `notes` as `meta.branchOf` and announced it as
307
+ the parent session, though it is not a session id. Lineage is now recorded only
308
+ for ids this CLI minted, and a collision while deriving a branch id re-mints the
309
+ suffix instead of dropping the branch shape.
310
+ - **`/session` answered `pinned no` right after `/session pin`.** The file was
311
+ marked and the report was not. The identity is now updated with the pin.
312
+ - **An id rotation could write one turn into the old id's file.** For the window
313
+ between `/resume` claiming a new id and the next render, the writer still aimed
314
+ at the previous file while recording the new parent. The rotation now republishes
315
+ the writer's id in the same step.
316
+ - **`/clear` did not clear the context.** It reset the visible transcript and the
317
+ todo panel and left `agent.state.messages` - the history that is actually sent,
318
+ billed, measured by `/context` and compacted - completely untouched. A long
319
+ session looked restarted and behaved like it had never been cleared: the next
320
+ request still carried every old turn, so the model remembered the previous task
321
+ and the occupancy gauge did not move. `/clear` now runs the one real
322
+ new-conversation boundary (`controller.resetConversation()`) in the only safe
323
+ order: interrupt the running turn (`forceStop`, which also drops the events the
324
+ unwinding engine keeps emitting), wait for it to settle inside a bounded budget,
325
+ archive the old conversation, drop both queues, then clear history, compaction
326
+ identity, context measurement and skill activation frames. A reset that cannot
327
+ stop the engine clears **nothing** and says so.
328
+ - **`/reset`'s "cancelled N queued messages" notice is preserved.** `/reset` is now
329
+ an alias of `/clear`, and the count is read **before** the queues are cleared -
330
+ read afterwards it would always be zero, which is a notice that lies.
331
+
332
+ ### Changed
333
+
334
+ - **`/clear` means "start a new conversation"; the old behaviour moved to
335
+ `/clear screen`.** The previous semantics (wipe the transcript and the todo
336
+ panel, keep the history and the queue) are still available, spelled explicitly,
337
+ and are byte-identical to what `/clear` used to do. `/clear` no longer refuses
338
+ while a run is in flight: it interrupts, which removes the dead end where the
339
+ only command that worked mid-run was the one that kept the context.
340
+ - **`/reset` is an alias of `/clear`, one implementation.** Two bodies could drift
341
+ back into "one clears the screen, one clears the session"; one body cannot.
342
+ - **The conversation being cleared is archived first**, to
343
+ `~/.aragon-agent/sessions/cleared/cleared-<yyyyMMdd>-<HHmmss>-<pid>.json`
344
+ (newest 20 kept, `<ARAGON_HOME>` honoured). The path is reported in the
345
+ transcript and restores with `/resume <path>`. The file carries no `meta`, so
346
+ `sessions list` / `sessions prune` and `--continue` are unaffected; a failed
347
+ archive only logs and never blocks the clear.
348
+
349
+ - **`Ctrl+I` was dead on Windows.** The win32-input-mode translator only
350
+ recognised the chord when the console reported it as `VK_TAB` (vk 9) with
351
+ ctrl - a record shape no real console was ever probed for. Physical
352
+ Ctrl+I keeps the LETTER vk (0x49, the same Ctrl+letter rule that reports
353
+ Ctrl+A as vk 65 and Ctrl+J as vk 74 in the Shift+Enter probe captures)
354
+ while `char` carries the Tab byte, so the record fell through to the
355
+ generic Ctrl-combo branch, came out as plain Tab, and the keybinding
356
+ silently did completion instead of opening the index confirm. The
357
+ translator now tells Ctrl+I apart from Tab by `char === 0x09 && ctrl`
358
+ whatever vk it rides, with Shift still mapping to Shift+Tab; both record
359
+ shapes are pinned by regression rows in `win32-input-mode.test.ts`.
360
+
361
+ ### Team dispatch supervisor (team-overseer)
362
+
363
+ - **A time ceiling on a subagent is now an inspection trigger, not a death
364
+ sentence.** With the supervisor on (new key `team.overseer`, default
365
+ `true`, needs the fast tier), two soft timers per child - event silence at
366
+ `idleTimeoutMs` and a wall clock at `subagentTimeoutMs` (or 5 minutes when
367
+ no ceiling is configured) - fire one bounded fast-tier inspection. The
368
+ supervisor decides wait / nudge / replace / abandon; the runtime applies,
369
+ funnelling every lifecycle change through the worker loop that awaits the
370
+ child, so a replacement is never built for nobody.
371
+ - **A nudge is steering; a replace is a rebuild with an amended brief.**
372
+ Guidance is queued at core's safe top-of-loop checkpoint; a replacement
373
+ carries the supervisor's reason, the dead child's `filesTouched`, and the
374
+ original brief bounded to the same prompt budget. A post-mortem inspection
375
+ diagnoses a child that died silently (watchdog death, stream loss) and may
376
+ re-assign it once.
377
+ - **Budgets are structural, not settings**: 4 inspections and 1 replacement
378
+ per child per dispatch; past them the child runs under its own (lengthened
379
+ while supervised) idle watchdog and the supervisor goes quiet. An
380
+ unparseable or failing supervision call always becomes `wait` -
381
+ supervision must never kill a healthy child.
382
+ - Every applied decision is visible three ways: the report's Supervisor
383
+ section (`[silence 30s] a1: nudge - ...`), the team panel and the `-p`
384
+ stderr line, and a `debug` log event. `aragon exec` emits it as an
385
+ additive `team` subtype `overseer` (`EXEC_SCHEMA_VERSION` unchanged).
386
+ - New command `/team overseer on|off` (refused mid-dispatch, like every
387
+ other `/team` setting) and new config key `team.overseer`, also settable
388
+ as `aragon config set team.overseer false`. With the supervisor off - or
389
+ no fast tier - every path is byte-for-byte the pre-feature regime:
390
+ `subagentTimeoutMs` is a hard abort and silence is the child's own
391
+ watchdog.
392
+
393
+ ### 启动与缩窗不再误报 Frame diff lost sync
394
+
395
+ - 启动即现的 "Frame diffing lost sync with the terminal and fell back to a
396
+ full repaint … `--no-diff-render`" 警告已消除:Ink 挂载/卸载经渲染 stdout 写出的
397
+ 光标隐藏/恢复序列(`ESC[?25l` / `ESC[?25h`)由帧写入器直接透传——不进差分、
398
+ 不失效缓存、不计 fallback,也不再消耗会话的首写豁免。启动序列恢复
399
+ "种子帧透传 → 首帧全量 → 之后纯 diff"。
400
+ - 终端高度缩小(拖小窗口、改字号、面板重排)不再触发同一条警告:Ink 的
401
+ `clearTerminal` 整帧写入被差分器识别为 Ink 自身写法,透传并失效缓存但不计
402
+ fallback;该控制写同样不消耗首写豁免,其后的首个 log-update 帧仍按种子帧豁免,
403
+ 下一帧自然全量重绘。
404
+ - `/perf` 的 `fallbacks` 重新从 0 起步,"健康会话 fallbacks === 0" 的诊断信号
405
+ (AC-11)恢复可判。
406
+
407
+ ### Consistent TUI styling
408
+
409
+ - Interactive sessions always use Unicode and default to truecolor, including
410
+ PowerShell sessions without terminal or UTF locale markers. Ink's actual color
411
+ output follows the same policy, with renderer state restored on exit.
412
+ - Explicit color opt-outs keep Unicode borders and icons; the opening logo now
413
+ depends only on available space. Headless terminal detection is unchanged.
414
+ - Reloading model settings preserves session display preferences. Syntax
415
+ highlighting follows the same color policy, and status icons use consistent
416
+ single-column glyphs to prevent narrow-frame overflow.
417
+ - Hiding the editor caret while scrolling preserves draft geometry, preventing
418
+ a layout update loop when entering text above the full startup logo.
419
+ - Register exit cleanup before mounting the application, so immediate exits and
420
+ initial render failures restore terminal and color state reliably. Failed
421
+ mounts no longer mark an auto-installed build as healthy.
422
+
423
+ ### Model context window
424
+
425
+ - Discover context limits at startup and after connection changes without blocking
426
+ input. The status bar and compaction use the discovered limit; `/context` names
427
+ API versus catalog metadata. Unknown windows show `?`, not a fabricated 128k
428
+ capacity or percentage. Manual `contextWindow` overrides retain precedence.
429
+ - Window overrides now apply to tail relief and compaction progress as well as
430
+ the gauge. Workers on different models/endpoints do not inherit the lead's
431
+ override. API-key changes refresh metadata, and ending a session cancels its
432
+ pending model queries. `/context` also hides unknown capacities/percentages.
433
+
434
+ ### TUI 输入与排队可靠性
435
+
436
+ - Shift+Enter 与粘贴按输入顺序处理;普通 Enter 保持弹层兼容,混合输入最多提交一次,
437
+ 余稿及拒绝提交的正文保留。结束输入流时完整排空,不泄漏内部控制标记。
438
+ - 超限粘贴拒绝同一外部输入块的正文和 Enter,避免误提交旧稿;未闭合粘贴继续按正文消费。
439
+ - 拖选后仅 Ctrl+C 复制;文字变化与丢失鼠标释放时取消无效高亮,避免卡住视口。
440
+ - 固定 Queue 状态行按消息 ID 回执更新,支持窄屏、暂停与多条消息计数。
441
+ - 保存和退出保留所有未发送全文;恢复先验证存档,旧 queued 作为警告展示且不自动重放。
442
+
443
+ ### Removed
444
+
445
+ - Removed the inline terminal UI and its automatic downgrade rules. Interactive
446
+ sessions now always use the full-screen UI; small windows show a resize notice.
447
+ - Removed `--fullscreen`, `--no-fullscreen`, `ARAGON_FULLSCREEN`, and the
448
+ `fullscreen` config setting. Old flags are rejected and old stored preferences
449
+ are ignored. Headless commands and piped input retain their existing behavior.
450
+ - Interactive-only screens now reject non-TTY input/output with exit code 2.
451
+
452
+ ### Added
453
+
454
+ - **`system/init` now lists what this build can do.** A new optional
455
+ `capabilities: string[]` field, carrying `["interrupt"]` for this release.
456
+
457
+ A wrapper cannot tell by observation whether an interrupt will settle
458
+ gracefully or kill the process outright, and guessing wrong costs the user
459
+ their conversation. Rather than have callers compare version numbers — which
460
+ breaks on forks, prereleases, and version shapes nobody anticipated — the
461
+ build now says so itself, in an event that already arrives on every start.
462
+ Absent on older builds, and absence should be read as "no", never as
463
+ "probably".
464
+
465
+ Additive, so `schemaVersion` does not move. Consumers that do not know the
466
+ field ignore it, exactly as the schema contract asks.
467
+
468
+ - **The composer understands pasting.** Until now it did not have the concept at
469
+ all: every byte the terminal delivered on stdin was a keystroke, and a
470
+ clipboard block is thousands of bytes arriving in an unpredictable number of
471
+ chunks. Three separate failures fell out of that one omission, and on Windows
472
+ you hit all three at once.
473
+
474
+ A carriage return that landed alone in a chunk **sent the half-finished
475
+ message** — a fifteen-line paste could arrive as a dozen separate messages,
476
+ interleaved with the agent's replies. A carriage return in the middle of a
477
+ chunk was inserted into the draft as a literal `\r`, which the terminal obeyed
478
+ by **overwriting the row it was drawn on**. And a paste that survived both grew
479
+ the composer to the height of the pasted text, pushing the transcript out of a
480
+ frame whose row accounting still believed the composer was three rows tall.
481
+
482
+ Pasting now works the way it reads:
483
+
484
+ - **A paste never submits.** Line breaks become buffer newlines, exactly one
485
+ per source line break, `CRLF` and lone `CR` alike.
486
+ - **A short paste appears verbatim.** At or below 6 lines and 400 characters it
487
+ is inserted as-is, indentation intact, and renders exactly like typed text.
488
+ - **A large paste collapses to a placeholder** — `[Pasted text #1 +218 lines]`
489
+ — which occupies one line, carries its own size in its label, deletes as a
490
+ single unit (one `Backspace`, and `Ctrl+W` / `Ctrl+U` / `Ctrl+K` cannot leave
491
+ half of one behind), and expands back to the full text the moment you send.
492
+ The model receives every byte; the composer never has to draw them.
493
+ - **No pasted byte is executed as a terminal control.** `\r`, `\x1b`, NUL, DEL
494
+ and the C1 range are stripped from the payload — never from your keystrokes,
495
+ so `Ctrl+A` and Escape are untouched.
496
+ - **The composer has a ceiling.** A draft of any length occupies at most 3 / 6 /
497
+ 10 rows depending on terminal height, scrolls internally to keep the caret
498
+ visible, shows how many rows are hidden above and below, and reports its true
499
+ height to the layout budget. Four hundred presses of `Shift+Enter` were
500
+ already able to destroy the frame; they no longer are.
501
+ - **A submitted message cannot make the transcript unresponsive.** A user entry
502
+ renders at most 40 rows plus a `... +N more lines` tail. The full text is kept
503
+ and the exit transcript is uncapped.
504
+
505
+ On terminals that support it, detection is **exact**: aragon asks for bracketed
506
+ paste (`DECSET 2004`) in full-screen mode and reads the markers the terminal
507
+ wraps your paste in. Where that is unavailable — inline mode, an old Windows
508
+ console — a heuristic on chunk shape catches the same cases, and it needs no
509
+ cooperation from the terminal at all. Both feed one code path.
510
+
511
+ Turn it off with `--no-paste`, `ARAGON_PASTE=0`, or
512
+ `aragon config set paste false`; the input path is then byte-for-byte what
513
+ 0.6.2 did.
514
+
515
+ ### Fixed
516
+
517
+ - **The right-edge scrollbar no longer disappears while output streams.** The
518
+ differential renderer ended every repainted row with `CSI K` (erase to end of
519
+ line). The scrollbar fills the last column of every viewport row, and on
520
+ terminals that keep the cursor on the last column after writing it (Windows
521
+ console hosts) that erase blanked the cell that had just been drawn, so the
522
+ scrollbar vanished whenever a row was redrawn. Rows that already fill the
523
+ terminal width are now repainted without the erase; narrower rows are
524
+ unchanged. `--no-diff-render` was never affected. `/perf` gained a `scrollbar:`
525
+ line for diagnosing visibility and drag problems.
526
+
527
+ - **`{"type":"interrupt"}` now really does settle the run — on every platform.**
528
+ The stdin interrupt frame used to raise a SIGINT at our own process. On POSIX
529
+ that reaches the runner's terminator and the run winds down properly: it
530
+ breaks out of the loop, persists the session, and emits `result` with the
531
+ usage and cost. On Windows, Node does not deliver a self-directed SIGINT to
532
+ its own handlers — the process simply terminated with exit code 1, having run
533
+ none of it.
534
+
535
+ For anyone using `aragon exec` interactively that difference was invisible,
536
+ because an interrupt there is usually the last thing that happens. It is not
537
+ invisible to a **wrapper** that interrupts a turn and then respawns on the same
538
+ `--session-id` to keep the conversation going: on Windows it silently got back
539
+ a session missing everything since the previous save. `exec/index.ts` has
540
+ carried a comment about that caller for some time ("PERSIST FIRST, ANNOUNCE
541
+ SECOND … a caller that reads `result` and immediately respawns with the same
542
+ `--session-id` must not race the writer"), so the intent was there; the path
543
+ just could not be reached on Windows.
544
+
545
+ The frame now calls the runner directly instead of going through a signal.
546
+ POSIX behaviour is byte-for-byte unchanged — the same code runs, with the same
547
+ `stopReason`, the same exit code 130, and the same two-second window in which a
548
+ second signal still forces an immediate exit.
549
+
550
+ - **A pasted API key is stored correctly again.** Ink broadcasts each stdin chunk
551
+ to every mounted input handler, so the settings screen, the question overlay
552
+ and the plan-review feedback field all see what the composer sees. Without
553
+ this, a ~100-character key pasted into Settings would have been stored wrapped
554
+ in invisible framing bytes, masked on screen by the password dots, and would
555
+ have failed to authenticate on every request afterwards with nothing anywhere
556
+ explaining why.
557
+
558
+ - **`--no-mouse` still writes no mouse sequence.** Paste support installs a
559
+ stdin filter, and `paste` defaults to on, so the filter now exists in sessions
560
+ that disabled the mouse. Whether the mouse is on is a separate question from
561
+ whether the stream is wrapped, and the four places that used to conflate them
562
+ now ask the right one: `aragon --no-mouse` writes no `?1000h`, no `?1006h` and
563
+ no `?1002h`, leaves your terminal's own click-drag selection alone, and reports
564
+ `/mouse` as off — while pasting still works.
565
+
566
+ ### Changed
567
+
568
+ - **The run status row moved above the input box and carries the `steer` hint.**
569
+ While a run is active, the spinner, the phrase and `steer / interrupt / exit`
570
+ share one row directly above the input, in place of the idle hint row below it
571
+ (the footer keeps its height). It scrolls with the input; when it leaves the
572
+ viewport the fixed bottom row shows the animation instead, so there is never
573
+ more than one spinner. Terminals under 20 rows and `--no-hints` keep the old
574
+ fixed row. The update notice is also held back for the whole run.
575
+
576
+ - **Auto-compaction no longer reports compactions that did not happen.** A long
577
+ unattended run used to print a red `context not compacted #2: nothing_to_drop`
578
+ card into the transcript. The card was accurate; nobody had asked for it.
579
+ Auto-compaction evaluates itself at every turn boundary, and a checkpoint that
580
+ looks at the history and finds nothing worth dropping has changed nothing,
581
+ called nothing and spent nothing — a smoke alarm announcing "no fire" twice an
582
+ hour, in error colours, with a machine reason attached.
583
+
584
+ A compaction now announces itself **if and only if it commits to doing work**.
585
+ That is a position in the control flow, not a list of reason strings: everything
586
+ decided before the first spend is silent, and everything decided after it —
587
+ including every failure, the truncation fallback, an Esc, and a host fault — is
588
+ as loud as it ever was. A declined `pressure` checkpoint is now
589
+ indistinguishable from a turn on which the threshold was never crossed.
590
+
591
+ **`/compact` and overflow recovery are unchanged.** A compaction you asked for
592
+ is owed an answer, and a reactive compaction running after the provider has
593
+ already refused your request is the one moment the diagnosis matters most. Both
594
+ still report on every outcome.
595
+
596
+ **Quiet, not unrecorded.** Every attempt is still written to the diagnostic log
597
+ (its `compaction` line gains an `announced` field, so `announced: false` is one
598
+ grep), still charged to the anti-loop guards, and still counted by
599
+ `/compact status` — which now reads
600
+ `This session: 1 compaction (2 checkpoints declined), 94.2k tokens reclaimed`
601
+ when checkpoints declined, and is byte-identical to before when none did. If
602
+ declines keep happening, the self-disable notice still says so out loud, once,
603
+ naming the cause and the remedies. Card numbers are also contiguous again: `#N`
604
+ counts compactions that ran, not checkpoints that fired.
605
+
606
+ **Behaviour change for wrapper scripts.** A declined `pressure` compaction no
607
+ longer emits a `{ type: 'compaction', subtype: 'start' | 'end' }` pair on the
608
+ `--output-format json` stream, and no longer prints `[compaction] skipped:` on
609
+ `-p` stderr. The pairs stay balanced, so only consumers counting *attempts* are
610
+ affected; the diagnostic log reports those more accurately.
611
+
612
+ ### Added
613
+
614
+ - **Context compaction, hardened — the trigger now counts the whole history, the
615
+ recent turns can no longer wedge a run, sub-agents get the same protection, and
616
+ a compaction is no longer irreversible.**
617
+
618
+ **The trigger was systematically late.** It fired on the provider's reported
619
+ usage for the PREVIOUS request, read at a checkpoint that runs AFTER the current
620
+ turn's tool results were appended — and tool results are the largest single
621
+ increment in this loop (100 KB per call, several per turn, ~25 k tokens each).
622
+ A session reading 88 % could be sending 110 %. Occupancy is now the measured
623
+ base PLUS an estimate of everything appended since that measurement, and the
624
+ status gauge reads the same number the trigger does, on every turn — including
625
+ the turns where a guard declines to compact, which is exactly when the number
626
+ matters most.
627
+
628
+ **The recent turns can no longer wedge a run.** Every previous rung of the
629
+ ladder operated on the head of the conversation. When the RETAINED tail was what
630
+ did not fit, each rung reported success or "nothing to drop" while the history
631
+ stayed un-sendable and the run died. Oversized `tool_result` bodies inside the
632
+ retained turns are now clipped, oldest first, only when the alternative is a
633
+ request the provider cannot accept. Nothing is removed — no message, no
634
+ tool-call id, no role — so a valid history stays valid. Every clip is announced
635
+ in the text the model reads, at the exact place the data went, and on the card,
636
+ in `/compact status`, on the JSON stream and in the archive.
637
+
638
+ **Sub-agents compact their own history** (`compaction.subagents`, default
639
+ `true`). A `task` child is bounded by 24 turns and fifteen minutes, and when one
640
+ overflowed its entire history was discarded and the dispatch reached the lead as
641
+ one partial sentence. Children run under tighter bounds than the lead: two
642
+ retained turns, at most two compactions, the fast tier preferred, and failure
643
+ forced to truncate because nobody is watching a background worker. No transcript
644
+ card — the lead's transcript describes the lead's context. Set
645
+ `compaction.subagents: false` to restore the previous behaviour exactly.
646
+
647
+ **A compaction is no longer irreversible** (`compaction.archive`, default
648
+ `true`). The dropped messages are written verbatim to `~/.aragon/compaction/`
649
+ before the splice is adopted. `/compact history` lists this run's archives,
650
+ `/compact show <n>` prints one's metadata and summary — never message bodies.
651
+ The directory is shared by every `aragon` on the machine, so files carry a
652
+ per-process run id and retention is two-tier: 20 files per run by count, plus a
653
+ seven-day sweep across all runs, so one session can never prune a live
654
+ neighbour's work. Writing is best-effort: a failure is logged and never affects
655
+ the compaction. This is deliberately **not** undo — restoring an over-full
656
+ history restores the condition that triggered the compaction.
657
+
658
+ **The long moment is legible.** The live card counts elapsed seconds and says
659
+ `esc to cancel`, which `AgentController.abort()` has always made true and no
660
+ surface has ever stated. A compaction that could only clip says so and names no
661
+ model, because none was called.
662
+
663
+ Two new config keys (`compaction.subagents`, `compaction.archive`), two new
664
+ environment variables (`ARAGON_COMPACTION_SUBAGENTS`,
665
+ `ARAGON_COMPACTION_ARCHIVE`), two new `/compact` verbs (`history`, `show <n>`),
666
+ and two new settings-screen rows. No flags were added.
667
+
668
+ - **Context compaction — a long run no longer dies when the window fills, and it
669
+ is ON by default.** A session's conversation only grows: nothing anywhere
670
+ removed a message, so a 40-turn tool-heavy run had exactly one ending available
671
+ to it once the context window filled — an HTTP 400 and a banner reading
672
+ *"Context length exceeded — start a new conversation with /reset."* The
673
+ product's answer to running out of context was "throw away everything you were
674
+ doing."
675
+
676
+ Now, at **90 %** occupancy, the run pauses at a turn boundary, hands the older
677
+ part of the history to a summarizer, and swaps it for a structured record. Your
678
+ original task stays **verbatim**, the most recent turns stay **verbatim**, and
679
+ the run continues in the same loop with the same tools, the same todo list and
680
+ the same working directory.
681
+
682
+ **This changes behaviour for existing users, in the direction of not dying.**
683
+ `--no-compaction`, or `compaction.enabled: false`, restores the old behaviour
684
+ exactly: no manager is constructed, nothing is subscribed, no registry is
685
+ allocated, and a full window ends the run as it did before. Both flag forms are
686
+ declared, so a stored `false` survives a run that passes no flags.
687
+
688
+ Three ways it fires. **Proactively** on measured occupancy — the case that
689
+ happens almost every time. **Reactively** when the provider returns
690
+ `context_overflow` anyway, because the model is not in the static window table
691
+ or you are pointed at a proxy in front of a smaller one: one compaction, one
692
+ re-send, run continues. And **manually**, via `/compact`, which runs
693
+ immediately when idle and queues to the next turn boundary when running.
694
+
695
+ The trigger has two terms. 90 % of a 200 k window leaves 20 k, which is plenty;
696
+ 90 % of a 32 k window leaves 3.2 k, which is less than a single `max_tokens` of
697
+ 8192 — the request is already impossible. So compaction also fires whenever the
698
+ remaining headroom drops below one full response.
699
+
700
+ **The default summarizer is your own model, and that costs real money.**
701
+ `compaction.useFastTier` is `true`, but it is a *preference*: the fast tier is
702
+ off unless you configured it, so out of the box the first compaction is a
703
+ ~30 k-token call on the session's own model. It happens at most 5 times per
704
+ run, the alternative is losing the run entirely, and the spend is visible — in
705
+ the session total, priced with the summarizer's own table, and reported as
706
+ `pricing unknown` rather than `$0.00` when that table has never seen the model.
707
+ Configure the fast tier to make it cheap.
708
+
709
+ Summarization fails sometimes. The ladder is one retry (on your **main** model
710
+ if the first attempt used the fast tier), then a **truncation** that says so on
711
+ the card, on the JSON event stream, and inside the block the model itself
712
+ reads. `compaction.onFailure: "stop"` makes the other trade.
713
+
714
+ Four guards stop it looping on your money: never two on consecutive turns,
715
+ never more than 5 per run, a progress requirement, and a self-disable after two
716
+ no-progress attempts with a notice naming the cause and the fix. The guards are
717
+ **trigger-aware** — the reactive path stays armed even after a self-disable,
718
+ because a compaction answering a request the provider has already refused is
719
+ the difference between a live run and a dead one.
720
+
721
+ Three UI surfaces: `Compacting context…` on the activity row while it runs, a
722
+ `compacting` chip plus threshold-aligned gauge colours as state, and a
723
+ transcript card with the before/after counts, the summarizer, the duration and
724
+ an expandable summary as the record. The gauge falls **immediately** rather
725
+ than waiting for the next turn. `/compact status` is the guaranteed surface at
726
+ any width.
727
+
728
+ New: `compaction` config section (6 keys), `--compaction` / `--no-compaction` /
729
+ `--compaction-threshold`, `ARAGON_COMPACTION*` (3 vars), `/compact` (6 forms),
730
+ three settings-screen rows, a `compaction` event on the `aragon exec` JSON
731
+ stream (`EXEC_SCHEMA_VERSION` **unchanged** — adding an event type never bumps
732
+ it), and two stderr lines under `-p`.
733
+
734
+ ### Changed
735
+
736
+ - **The context gauge now counts cache tokens**, through the same
737
+ `occupiedTokens` the compaction trigger reads. Anthropic reports `input_tokens`
738
+ *excluding* cached tokens and puts them in two separate fields, so the old
739
+ `inputTokens + outputTokens` silently under-reported occupancy for anyone
740
+ behind a caching gateway. That was already a defect in the number on the status
741
+ bar; it becomes a much worse one the moment a trigger fires on a different
742
+ number from the one you are watching.
743
+
744
+ - **`context_overflow` now names `/compact` alongside `/reset`.** That remedy
745
+ list was complete until this release, because throwing the conversation away
746
+ really was the only exit.
747
+
748
+ ### Known limitation
749
+
750
+ - **A summarizer that is 90 % faithful will, eventually, drop something the run
751
+ needed,** and no amount of prompt engineering closes that. The design bounds it
752
+ — verbatim task, verbatim recent turns, a block that tells the model the record
753
+ is partial, and three surfaces that make it visible so a human can step in —
754
+ but it does not eliminate it. That is inherent to compaction as a technique, it
755
+ is the same trade Claude Code and Codex make, and it is preferable by a wide
756
+ margin to the previous behaviour, which was to lose the entire run.
757
+
758
+ ## 0.6.0
759
+
760
+ ### Added
761
+
762
+ - **`aragon exec` — a second, machine-facing face on the same binary.** The
763
+ package already installed from npm and already ran from a terminal, so "it has
764
+ a CLI" was true. It was nonetheless not *callable* by another program:
765
+ everything it exposed was shaped for a person, and a build script, a CI job, a
766
+ web backend or a second agent had to spawn it, capture free text, and guess.
767
+ There was no way to read the answer without also reading the narration, no way
768
+ to carry a conversation across two invocations, no way to say "you may read but
769
+ not write" when there is no human to answer `--confirm`, and no way to bound
770
+ what a run may spend.
771
+
772
+ `aragon exec` adds a **contract** rather than a feature: a versioned JSON event
773
+ schema on stdout, a session identifier the caller may mint itself, a tool
774
+ permission policy enforced at the tool boundary, caller-set budgets with their
775
+ own exit code, and a discovery command so a wrapper can ask what it just
776
+ installed before depending on it. Everything underneath is reused unchanged —
777
+ the same controller, the same tools, the same config resolution, the same
778
+ skills, teams, todo and fast-tier subsystems.
779
+
780
+ - `--output-format text | json | stream-json`. `stream-json` is NDJSON: one
781
+ object per line, `system/init` first, exactly one `result` last, on every
782
+ exit path including error, budget and signal. `text` is byte-identical to
783
+ `aragon -p`, because it **delegates** to the same renderer rather than
784
+ reimplementing it.
785
+ - `--session-id` / `--resume` / `--continue`, backed by `~/.aragon-agent/
786
+ sessions/` and an exclusive lock, so two runs cannot interleave and lose a
787
+ turn. A second run on a held id exits 2 with `session_busy` rather than
788
+ blocking.
789
+ - `--permission-mode auto|plan|strict` plus `--allow-tool` / `--deny-tool`.
790
+ A denied tool is **not registered at all**, so the model never sees it and
791
+ never plans around it — and the policy reaches subagents, so one `task` call
792
+ cannot bypass it.
793
+ - `--max-turns` / `--max-duration`, both aborting through the same path `Esc`
794
+ uses, both reporting `isError: false` and exit **3**: a budget firing is not
795
+ a malfunction, but it is not a complete answer either, and a shell script has
796
+ to be able to tell without parsing JSON.
797
+ - `--append-system-prompt` / `--append-system-prompt-file`. Appending only;
798
+ there is deliberately no full `--system-prompt` replacement, because the
799
+ builtin prompt carries tool discipline, todo planning, the skills catalog and
800
+ the plan-mode block, and replacing it silently disables half the product.
801
+ - `--input-format stream-json`, so one process can serve several turns from a
802
+ pipe. A malformed line is skipped with a non-fatal `error` event; an unknown
803
+ message type is ignored.
804
+ - **`aragon sessions`** (`list` / `show` / `rm` / `prune` / `path`),
805
+ **`aragon info [--json]`** and **`aragon doctor [--json] [--probe]`**.
806
+
807
+ Documented in full under
808
+ [Use it from another project](./README.md#use-it-from-another-project),
809
+ including an honest Limits section: there is no filesystem sandbox, process
810
+ exit is terminal on Windows whether or not a `result` arrived, an exec session
811
+ resumed in the TUI shows no scrollback, and `sessions prune` shares a directory
812
+ with the TUI's `/save`.
813
+
814
+ ### Changed
815
+
816
+ - **Exit code `3` is new, and the signal row is now stated as `128 + signo`.**
817
+ `3` means "stopped by a budget you set" and is reachable only from
818
+ `aragon exec`. The signal codes are unchanged in behaviour — this CLI has
819
+ always exited `128 + signo`, so `SIGTERM` has always been `143` — but the
820
+ README previously simplified all of them to `130`, which is fine for prose
821
+ about a CLI a human interrupts and not good enough for a wire contract.
822
+ - `interrupt` handling now settles the run before exiting when `aragon exec`
823
+ owns it, so a wrapper reading NDJSON gets a final `result` on Ctrl-C instead of
824
+ having to time out to learn the run ended. `aragon`, `aragon -p` and every
825
+ other subcommand keep the previous bare-exit behaviour byte for byte.
826
+
827
+ ### Unchanged, and asserted rather than claimed
828
+
829
+ - `aragon`, `aragon -p`, every existing flag, every slash command and every byte
830
+ of their output. `aragon exec --output-format text` and `runHeadless` are
831
+ compared for byte equality on **both** stdout and stderr by a regression test,
832
+ and `createBuiltinTools` with no permission policy is proven by object identity
833
+ to return the tools it returned before.
834
+
835
+ ## Unreleased
836
+
837
+ ### Project index on Ctrl+I (bundled `project-indexer` skill)
838
+
839
+ - The `project-indexer` skill now ships **bundled** with the package
840
+ (`<pkg>/skills/project-indexer`, read-only, alongside `skill-creator`): it
841
+ generates the `.claude-index/` navigation map and injects the Google-style
842
+ Clean Code Guidelines section into `CLAUDE.md`, adapted from the standalone
843
+ Claude Code skill (ask_user instead of AskUserQuestion; body condensed under
844
+ the 30 KB invocation budget with the four templates bundled as files).
845
+ - New keybinding `Ctrl+I`: opens a Yes/No confirm that names every write
846
+ target, then dispatches `/skill:project-indexer` through the ordinary
847
+ command path - the indexing run is a normal agent turn whose tool calls
848
+ stream into the transcript. While a run is in flight the key warns instead
849
+ of queueing; with skills disabled or the skill shadowed it explains instead
850
+ of failing silently.
851
+ - `Ctrl+I` is legacy-encoded as the Tab byte, so the stdin filter's two
852
+ translators (win32-input-mode on Windows, kitty CSI-u / modifyOtherKeys
853
+ elsewhere) now emit a distinct NUL-delimited `INDEX_KEY_FRAME` for it -
854
+ the same framing technique `Shift+Enter` uses. On terminals with neither
855
+ protocol the key keeps its legacy Tab meaning (completion).
856
+ - The header names the key next to the detail toggle: `^I Index` collapsed,
857
+ `^I build index` in the `Ctrl+G` expanded state; hidden below 72 columns and
858
+ while an overlay is open. `/skill:project-indexer` and `/project-indexer`
859
+ remain available as typed commands.
860
+
861
+ ### Added
862
+
863
+ - **Drag the mouse to select text, and release to copy it.** Full-screen mode
864
+ takes the alternate screen, turns on mouse reporting so the wheel can scroll a
865
+ transcript the terminal no longer knows how to scroll, and draws every row
866
+ itself. Each of those is right on its own; together they took your terminal's
867
+ own drag-to-select away and put nothing in its place. The only thing shipped
868
+ in its place was a one-line notice naming a bypass key that is Shift on some
869
+ emulators, Fn on others and a preference elsewhere — a workaround, told once,
870
+ in a line that scrolls away.
871
+
872
+ Press the left button and drag: the cells you drag across are highlighted as
873
+ you go, and releasing copies exactly them. The copy goes out over **OSC 52**
874
+ first, so it reaches the clipboard of the machine you are *sitting at* rather
875
+ than the one you are ssh'd into, with the platform clipboard binary attempted
876
+ alongside it for terminals that ignore the sequence. Neither mechanism is
877
+ detectable, so the toast names what was sent rather than claiming a success it
878
+ cannot verify. `/copy` now goes through the same path and gained ssh support on
879
+ the way.
880
+
881
+ The highlight is painted in the frame pipeline rather than in React, so a drag
882
+ costs one to three re-addressed rows instead of a re-render of the whole tree,
883
+ and the text that is copied is read from the same mirrored rows the highlight
884
+ was painted over — what you see is what you get by construction, not by a
885
+ staleness check that can be wrong.
886
+
887
+ Two switches, both reversible without a restart: `/mouse off` hands the mouse
888
+ back to your terminal for the session (and says plainly that the wheel goes
889
+ inert while it is off), `/mouse on` takes it back, and
890
+ `aragon config set mouseSelect false` keeps wheel scrolling while leaving
891
+ drag-select off — which also stops the CLI asking the terminal for motion
892
+ reports at all, so that session puts exactly the bytes on the wire that it did
893
+ before this release. `--no-mouse` remains the switch that captures nothing.
894
+
895
+ ### Changed
896
+
897
+ - **Scrolling up now actually stops the transcript.** The viewport followed the
898
+ newest output by holding a *fixed distance from the bottom*, so scrolling up
899
+ during a run did not pause anything: twelve new rows of output moved the
900
+ window twelve rows down, and whatever you had scrolled up to read slid off the
901
+ top while you were reading it. The faster the agent talked, the faster your
902
+ reading position ran away.
903
+
904
+ While you are scrolled up, the transcript is now anchored to the **content**:
905
+ new output accumulates below the window instead of dragging the window along
906
+ with it, and the rows on screen hold still. After five seconds with no
907
+ scrolling the viewport returns to the newest line by itself — but only if
908
+ something actually arrived while you were paused, so reading a *finished*
909
+ transcript is never interrupted by a jump to a bottom that has not moved.
910
+ Every gesture restarts the countdown, a selection drag suspends it, and
911
+ `aragon config set scrollResumeMs 0` turns the return off entirely
912
+ (`ARAGON_SCROLL_RESUME_MS` sets it too).
913
+
914
+ The number the hint shows means something sharper as a result: it is now
915
+ literally "rows of newer output below your reading position", which is what
916
+ `PgDn` traverses and what the status bar's `↑N` has always shown.
917
+
918
+ - **The "N new lines · PgDn" hint moved inside the input box.** It used to be
919
+ rendered as the viewport's own last row, which spent a line of your transcript
920
+ on chrome at exactly the moment the transcript was longest — and, because the
921
+ hint's presence shrank the viewport, it fed back into the very number it was
922
+ displaying. It is now a right-aligned chip on the composer's input row, inside
923
+ the border, where it costs no rows at all. The chip sits in a fixed-width cell
924
+ so the number gaining a digit cannot re-wrap a draft, it never truncates (the
925
+ draft wraps first), and unlike the teaching row below the box it neither fades
926
+ with experience nor disappears on a short terminal: how far behind you are is
927
+ state, not a tutorial.
928
+
929
+ ### Fixed
930
+
931
+ - **The working spinner no longer disappears when an ack takes its row.** Steer a
932
+ run, approve a plan, or press `Ctrl+T` while the agent is working, and the row
933
+ above the composer traded its spinner for the acknowledgement — and for that
934
+ ack's full two and a half seconds, nothing anywhere on screen moved. The status
935
+ bar still read `running` and the tool card still read `running`, so no
936
+ information had left the frame; only the evidence that the process was alive
937
+ had, during exactly the long tool calls where "is it stuck?" is the question you
938
+ are already asking. Consecutive acks extended the window, and steering is
939
+ something the composer invites on every run.
940
+
941
+ That row was made the sole owner of animation in this same release, and
942
+ everything else on screen holds still for as long as the row is believed to be
943
+ showing one. Believed, not checked: an ack won the row outright, so the belief
944
+ and the frame disagreed, and the disagreement was invisible to every test
945
+ because no component was wrong on its own.
946
+
947
+ The ack still owns the row and keeps every word of its text. The spinner now
948
+ rides beside it as a bare glyph — no phrase, no clock — so there is exactly one
949
+ animation at every instant of a run, with an ack up or without one: never none,
950
+ and never the several this release set out to remove. Terminals without Unicode
951
+ and anyone running with `reducedMotion` come out byte for byte unchanged; they
952
+ had no animation to lose, and nothing is prefixed to their acks.
953
+
954
+ - **`/clear` now takes the todo panel with it.** Finish a plan, type `/clear`,
955
+ and the transcript emptied while the `7/7 done` rail stayed exactly where it
956
+ was — on a screen you had just asked to be cleared. It stayed until your next
957
+ real message, which is precisely the pause you take after clearing to decide
958
+ what to ask next, so essentially everyone met it.
959
+
960
+ This was deliberate once. The list is a projection of what the model believes,
961
+ and `/clear` wipes the transcript rather than the conversation, so the model
962
+ still believed in the plan and a panel contradicting the model was judged worse
963
+ than no panel. That reasoning holds for everything the CLI does on its own; it
964
+ does not hold against you typing the command. An explicit instruction was
965
+ always this rule's exception — `/todo clear` has used the same door from the
966
+ start — and `/clear` now goes through it.
967
+
968
+ The list itself is cleared, not just the pixels, so `/save`, `/todo status`,
969
+ `/todo continue` and auto follow-through all agree with the screen: no plan is
970
+ written into a saved session, nothing offers to continue one, and nothing spends
971
+ a follow-through on it. `/reset` is unchanged and still the only command that
972
+ clears the conversation. Nothing is stranded either way — the model's next
973
+ `todo_write` is a full replacement, so the panel comes back the moment there is
974
+ a plan to show.
975
+
976
+ ### Added
977
+
978
+ - **An update that will not start is undone automatically.** The updater shipped
979
+ in this same release could install a version that fails to *import* — an
980
+ undeclared dependency, a Node API that moved, a syntax error past the transpile
981
+ target — and the only recovery was to know the package name and type
982
+ `npm i -g @aragon-agent/cli@<some older version>` from memory. None of those
983
+ failures reach any line inside `main()`, so no amount of care there helps.
984
+
985
+ The `aragon` command is now a small launcher that runs **before** the CLI. It
986
+ reads one ~300-byte file, and on every launch except the first one after an
987
+ auto-update that is all it does — no write at all. But if the version it is
988
+ about to start is one **this updater installed**, and that version has already
989
+ exited non-zero twice, it reinstalls the version that was running before the
990
+ update and marks the bad one so nothing puts it back:
991
+
992
+ ```
993
+ aragon 0.6.0 failed to start twice; rolling back to 0.5.9.
994
+ ```
995
+
996
+ It boots anyway. If the crash was transient, refusing to start would lock you
997
+ out of a working CLI on our guess; the reinstall is detached and finishes
998
+ either way. The next session says once what happened and then stops. A **newer**
999
+ release clears the mark and installs normally — you were never opted out of
1000
+ updates, only out of the loop.
1001
+
1002
+ It never touches a version **you** installed yourself, and it does not try to
1003
+ detect a release that starts and is merely wrong. `aragon update --rollback` is
1004
+ the same mechanism run deliberately, for the case a human spots in one second
1005
+ and a heuristic would get wrong on a bad day. Exit `4` means there was nothing
1006
+ recorded to roll back to.
1007
+
1008
+ **`update.mode: off` disables this too.** The guard is armed only by an
1009
+ auto-install, so a machine that never auto-installs never arms it — the kill
1010
+ switch turns off the whole subsystem, recovery included.
1011
+
1012
+ - **A blocked install now says what is actually blocking it.** Every install
1013
+ failure used to print the same line: `update failed · npm i -g
1014
+ @aragon-agent/cli`. On Windows the commonest cause is that the shim which
1015
+ launched us is held open by the shell running it, so that advice hits the same
1016
+ handle and fails identically — the one visible failure was also a wrong
1017
+ instruction. Failures are now classified, and three of them name the real
1018
+ remedy and appear immediately rather than after three strikes, because each is
1019
+ something you can fix and none of them fixes itself:
1020
+
1021
+ ```
1022
+ ! update blocked · close other aragon windows, then: npm i -g @aragon-agent/cli
1023
+ ! update failed · no disk space
1024
+ ! update needs write access · npm i -g @aragon-agent/cli
1025
+ ```
1026
+
1027
+ Network failures are deliberately **not** in that set: you cannot fix the
1028
+ registry and the next check may well succeed, so they stay silent and back off.
1029
+
1030
+ - **Updates work behind a corporate proxy.** Node's `fetch` ignores `HTTP_PROXY`,
1031
+ so on a proxied machine the check failed silently forever. When the direct
1032
+ request fails, `aragon` now asks the npm you already have instead, which
1033
+ honours `proxy`, `https-proxy` and your `.npmrc`. It runs *only* after a
1034
+ request that has already failed, so on an ordinary machine it never runs.
1035
+ `/update status` shows `probe: npm` when this is what answered. No credentials
1036
+ are read or forwarded — we run the command you could have typed.
1037
+
1038
+ - `/update status` gained `rollback`, `lastgood` and `probe` lines, so what the
1039
+ guard did and what `--rollback` would reinstall are answerable without reading
1040
+ the log.
1041
+
1042
+ ### Changed
1043
+
1044
+ - **`aragon update` exit codes: a blocked install is now `2`, not `1`.** This is
1045
+ the one change here a script can notice. `1` means "try again later" and `2`
1046
+ means "this machine will never auto-update, run the command we printed" — and
1047
+ for `EPERM` / `EBUSY` / `ENOSPC` the second is the accurate one: waiting does
1048
+ not unblock a held file handle or an empty disk. A caller shaped like
1049
+ `aragon update || sleep 300 && retry` changes behaviour. `0`, and `1` for an
1050
+ ordinary failed check or install, are unchanged.
1051
+
1052
+ - `bin.aragon` now points at `dist/launcher.js` rather than `dist/cli.js`.
1053
+ `dist/cli.js` is unchanged, keeps its shebang and stays executable, so
1054
+ `npm start` and any script invoking it directly keep working — only the mapping
1055
+ moved.
1056
+
1057
+ - **`aragon` keeps itself up to date.** Until now the installed copy was frozen
1058
+ forever: nothing in the CLI ever looked at the registry, so someone who
1059
+ installed 0.4.x eighteen releases ago was still running it and the only signal
1060
+ that anything had moved was a README they were never going to re-read.
1061
+
1062
+ There is now a background updater. At most once every four hours, and at most
1063
+ once per machine per interval, it asks npm what the `latest` dist-tag resolves
1064
+ to; if that version is genuinely newer, stable, not deprecated and runnable on
1065
+ this Node, it installs it silently into the same global installation you
1066
+ already have and says so on **one line above the composer**:
1067
+
1068
+ ```
1069
+ * 0.6.0 installed - restart aragon to apply
1070
+ ```
1071
+
1072
+ **The running process is never mutated in any way it can observe.** The new
1073
+ version lands on disk and takes effect on the *next* launch. A restart in the
1074
+ middle of a turn would destroy a live run, a subagent dispatch and an open plan
1075
+ review, so the line waits and the human decides when.
1076
+
1077
+ The notice does not get its own row — it shares the row the toast stack and the
1078
+ working line already share, behind both of them. A toast is a response to
1079
+ something you did and wins outright; the working line is transient and would be
1080
+ lost if deferred; the update line is persistent and loses nothing by waiting.
1081
+ The practical effect is that **someone who is actually working never sees the
1082
+ updater until they stop.**
1083
+
1084
+ It only ever runs `npm i -g` for one kind of installation: a global one where
1085
+ no project manifest claims to own the version. pnpm, yarn, bun and volta are
1086
+ each detected and get the correct command for *that* manager printed instead
1087
+ of a command run on their behalf; `npx`, a project's `node_modules` and a
1088
+ clone of this repo produce no line at all, because none of them is out of date
1089
+ in any sense the user can act on. A global directory that cannot be written is
1090
+ detected before anything is spawned, and npm exiting `0` into a *different*
1091
+ prefix — which happens on any machine with `prefix=` in `.npmrc` or two Node
1092
+ installations — is caught by re-reading the version from the directory we
1093
+ classified, rather than being believed.
1094
+
1095
+ Every failure is silent: it logs, backs off exponentially, and says nothing
1096
+ until the third consecutive one, at which point it says something actionable.
1097
+ Four config keys (`update.mode`, `update.checkIntervalMs`, `update.registry`,
1098
+ `update.distTag`), the flags `--update` / `--no-update`, the environment
1099
+ variables `ARAGON_UPDATE` and `ARAGON_UPDATE_REGISTRY`, the slash command
1100
+ `/update [status|now|skip|off]`, and a foreground `aragon update
1101
+ [--check] [--to <version>] [--json]` for headless users. `update.mode: off`
1102
+ and `ARAGON_UPDATE=0` are kill switches, and with either set the CLI executes
1103
+ zero lines of update code.
1104
+
1105
+ Auto-update widens the window in which a compromised release reaches users
1106
+ without a human decision. The README says so plainly, along with what is and
1107
+ is not mitigated. No credentials are ever attached to the check.
1108
+
1109
+ - **A running command shows what it is doing.** `bash` is the only tool here that
1110
+ can run for minutes — `npm test`, a `git clone`, a training job — and while it
1111
+ ran, its card was one line of text that never changed. Nothing in that frame
1112
+ distinguished a test suite at 40% from a child process blocked forever on a
1113
+ password prompt this CLI will never show. Its card now carries the last eight
1114
+ lines of what the command is printing, plus one footer row that reads
1115
+ `(running)` or, once the child has been quiet for ten seconds,
1116
+ `no output for 45s` — with the seconds advancing.
1117
+
1118
+ A `\r`-driven progress bar (npm, pip, curl, docker) is *one* line rewritten a
1119
+ thousand times, and it renders as **one row counting up in place** rather than
1120
+ a thousand rows of nearly the same string — including the ordinary case where
1121
+ the bar emits no newline at all for its entire run.
1122
+
1123
+ Everything the tail shows is **stripped, not interpreted**: ANSI colour, cursor
1124
+ motion, `\r`, `\b`, tabs and every other control byte are removed at the
1125
+ boundary before a row is stored, so nothing can move a cursor inside a frame
1126
+ the renderer believes it owns. It is bounded twice by construction — eight rows
1127
+ per call, sixteen concurrent calls — so a command that writes 400 MB to stdout
1128
+ costs a fixed number of retained rows.
1129
+
1130
+ It is a **display and nothing else**. The text the model receives is
1131
+ byte-for-byte what it was; `Ctrl+O` does nothing while a command runs, because
1132
+ expansion is a promise about stored content and there is no stored rest of a
1133
+ 400 MB stream; `/save` during a run writes no tail, since a resumed one would
1134
+ describe a process that died with the session; and at settle the card is
1135
+ replaced by the authoritative result exactly as before.
1136
+
1137
+ New config key `liveToolOutput`, defaulting to **`true`** — unlike
1138
+ `showThinking` below, this adds information rather than hiding it.
1139
+ `aragon config set liveToolOutput false` (or `--no-live-tool-output`, or
1140
+ `ARAGON_LIVE_TOOL_OUTPUT=0`, or the **Live output** row in `/settings`) turns it
1141
+ off, and with it off no store is allocated and no recorder is attached: `bash`
1142
+ is byte-identical to the build before this entry.
1143
+
1144
+ - **The working line stops guessing while a tool runs.** It reads
1145
+ `⠙ Running bash` instead of rotating thinking-words. `running` covers the whole
1146
+ turn, including the minutes the model is idle and a child process is doing the
1147
+ work, so "Pondering" during a build was not calm — it was wrong. One row, no
1148
+ digits, no clock: everything the row already promised still holds.
1149
+
1150
+ ### Changed
1151
+
1152
+ - **Reasoning blocks are now hidden by default.** `showThinking` is a new
1153
+ top-level config key and it defaults to `false`. If you have been reading
1154
+ thinking every day, this is the entry that concerns you: `Ctrl+T` still shows
1155
+ it for the session exactly as before, `aragon config set showThinking true`
1156
+ (or `--show-thinking`, or `ARAGON_SHOW_THINKING=1`) restores the old behaviour
1157
+ permanently, and `/settings` has a **Show thinking** row directly under
1158
+ **Thinking**.
1159
+
1160
+ Nothing is hidden silently. A settled turn that thought leaves one muted row
1161
+ where the block used to be — `✱ thought for 12s · ctrl+t to show` — so the fact
1162
+ that reasoning happened, how long it took, and how to see it are all still on
1163
+ screen. In **inline** mode that row omits the key hint and the toast reads
1164
+ `Thinking shown for new output.`, because a settled entry there has already
1165
+ been printed into your terminal's scrollback and cannot be repainted; the
1166
+ toggle applies to the live entry and to everything drawn after it.
1167
+
1168
+ `thinkingLevel` is untouched and means what it always did — the effort the
1169
+ provider is asked to spend. `showThinking` is only whether the terminal draws
1170
+ what came back. They are one word apart in the config file, which is why the
1171
+ flag is `--show-thinking` rather than the shorter name.
1172
+
1173
+ ### Added
1174
+
1175
+ - **A live working line while a run is in flight.** One row above the composer,
1176
+ carrying a spinner and a word that rotates every four seconds:
1177
+ `⠋ Percolating…`. Take reasoning off the screen and the gap between submitting
1178
+ and the first token is otherwise completely blank, which reads as dead rather
1179
+ than as busy — the two changes are one change.
1180
+
1181
+ It carries no clock and no token count, deliberately: the status bar one row
1182
+ below already renders both, under the same running condition and driven by the
1183
+ same 200 ms ticker this row reuses. Under `reducedMotion` the spinner is a
1184
+ static glyph and the word is chosen once and holds, because rotating text is
1185
+ motion too. An idle frame is unchanged, and so is the viewport's row budget —
1186
+ the line shares the row the toast strip already owned rather than adding one.
1187
+
1188
+ - **`write_file` and `edit_file` render a real diff.** Hunks with old/new line
1189
+ numbers, a `+N -M` summary, add/remove/context colouring, and `(truncated)` or
1190
+ a `binary` / `too-large` / `unreadable` note when the old side could not be
1191
+ read. Diffs collapse at 12 rows behind the existing `Ctrl+O` — no new
1192
+ keybinding. A resumed session written by an older build, or a card whose patch
1193
+ the bounded store already evicted, falls back to the previous text preview;
1194
+ nothing renders blank.
1195
+
1196
+ `write_file` previously reported creating a 300-line file as
1197
+ `Wrote 8214 bytes to path`, and `edit_file`'s diff trimmed the common prefix
1198
+ and suffix and rendered *everything* in between as one removed block followed
1199
+ by one added block — for a `replace_all` touching line 40 and line 900, that
1200
+ was 860 removed rows followed by 860 nearly identical added ones.
1201
+
1202
+ **The model's input got smaller, not larger.** The structured patch reaches the
1203
+ screen on a CLI-local channel rather than through the tool result, and the text
1204
+ the model reads gained a 4 000-character ceiling it did not have before (its
1205
+ only previous bound was the executor's 100 KB backstop). A single-region edit
1206
+ is byte-for-byte what it was plus one `@@` header; a multi-region edit is
1207
+ strictly smaller. `write_file`'s result text is unchanged.
1208
+
1209
+ ### Changed
1210
+
1211
+ - **A stream that stops mid-answer is now a loud failure instead of a silent
1212
+ truncated one.** All three provider adapters used to end their SSE loop by
1213
+ yielding "done" with whatever had arrived, so a connection closed cleanly
1214
+ half-way through an answer — a proxy idle cut, a load-balancer timeout, an
1215
+ HTTP/2 GOAWAY — was indistinguishable from a completed reply. You got a
1216
+ truncated answer and were told nothing.
1217
+
1218
+ Each adapter now reports the truncation, which is what lets it be retried and
1219
+ recovered. An **abort** is explicitly excluded and still completes normally, and
1220
+ a model that legitimately says nothing and stops is still a success.
1221
+
1222
+ This is deliberately **not** behind `--no-retry`: an adapter that reports a
1223
+ truncated stream honestly should do so whether or not retry is enabled. A switch
1224
+ that also disabled telling you the truth would be a mute button, not a kill
1225
+ switch.
1226
+
1227
+ - **The mouse wheel now only ever scrolls.** A notch scrolls the transcript
1228
+ three rows (a page with `Shift`), or the open overlay when there is one, no
1229
+ matter where the pointer is sitting. Prompt history is recalled by `↑` / `↓`
1230
+ alone, exactly as in Claude Code and Codex.
1231
+
1232
+ **This supersedes the 0.5.0 entry below**, which said the wheel "scrolls what
1233
+ is under the pointer" and stepped prompt history one entry per notch when the
1234
+ pointer was over the input box. That turned out to be wrong in use rather than
1235
+ in principle: the pointer is not an instrument you aim in a TUI — it rests
1236
+ wherever you last left it, which in a chat-shaped app is next to the thing you
1237
+ type into. So the ordinary "scroll back and see what just happened" gesture
1238
+ landed on the composer and replaced the draft with an old prompt, with no
1239
+ error, no undo and nothing visibly connecting the gesture to the outcome. The
1240
+ band that decided this was invisible too: its boundary was a measured row that
1241
+ moved as the toast row, the hint row and the completion popup came and went, so
1242
+ the same gesture could do two different things a second apart.
1243
+
1244
+ Nothing else about the wheel changes — same three rows per notch, same
1245
+ `Shift`+wheel page, same `--no-mouse` / `ARAGON_MOUSE=0` / `mouse: false`
1246
+ escape hatches, same `Shift`+drag for selecting text.
1247
+
1248
+ - **A long session stays as fast as a short one.** The TUI used to get slower as
1249
+ a session grew and, past a threshold, stopped making progress at all. That was
1250
+ structural rather than incidental: Ink has no output caching, so every frame
1251
+ re-walked the entire mounted tree, re-measured every text node and
1252
+ re-serialised the frame. The per-frame cost was therefore proportional to every
1253
+ character the session had ever produced — and `transcriptWindow` bounded the
1254
+ entry *count*, which bounds nothing when one entry can be fifty thousand lines.
1255
+
1256
+ Entries outside the visible band are no longer mounted. The viewport renders
1257
+ what you can see plus a couple either side and replaces the rest with two
1258
+ spacers of the right height, so the frame costs what the screen costs.
1259
+ Scrolling, the `↑N` readout and the wheel routing are untouched. Because the
1260
+ horizon is no longer a rendering budget, `transcriptWindow` rises from 300 to
1261
+ 1000 and its ceiling from 2000 to 20000; an existing setting is still valid
1262
+ and still clamps.
1263
+
1264
+ Syntax highlighting and markdown parsing are memoised on their input, so a
1265
+ 200-line code block is tokenised once rather than thirty times a second for as
1266
+ long as it stays on screen. Only the *parsed* form is cached and never a themed
1267
+ one, so `/theme` still takes effect on the next frame.
1268
+
1269
+ Under genuine load the streaming interval widens — 33 ms up to a 320 ms
1270
+ ceiling — so keystrokes and `Ctrl+C` do not queue behind renders. This is
1271
+ visible rather than silent: a muted **`eco`** chip appears in the status bar
1272
+ while it is in effect, because a user who cannot see why the stream got
1273
+ chunkier will conclude the model got slower. `--no-render-governor`,
1274
+ `renderGovernor: false` or `ARAGON_RENDER_GOVERNOR=0` turns it off;
1275
+ `--max-render-interval 33` flattens the ladder without disabling the mechanism.
1276
+
1277
+ - **A live thinking block no longer grows without limit.** While an answer is
1278
+ streaming, visible thinking is clamped to its newest 24 rows behind a
1279
+ `... N earlier lines` marker, and the whole block renders again the moment the
1280
+ answer settles. Thinking is scratch reasoning you read from its newest end, and
1281
+ an unbounded one was the easiest way for a single live entry to fill the
1282
+ viewport on its own.
1283
+
1284
+ ### Fixed
1285
+
1286
+ - **Inline mode could hard-freeze the terminal, and now cannot.** Once a live
1287
+ entry grew taller than the terminal, Ink took a branch that writes
1288
+ `clearTerminal` plus the *entire accumulated session history* on every
1289
+ subsequent frame — bypassing both of its own dedupe gates, and growing without
1290
+ bound for the rest of the session. Any inline user (`--no-fullscreen`,
1291
+ `TERM=dumb`, CI, a terminal under 12 rows or 40 columns) whose model wrote a
1292
+ long answer could reach it. The live region is now clamped below that
1293
+ threshold, with a `... N earlier lines` marker naming what is deferred. Nothing
1294
+ is lost: the entry prints in full into the terminal's own scrollback the moment
1295
+ it settles.
1296
+
1297
+ - **The view state no longer grows without a ceiling.** A single assistant answer
1298
+ is capped at 256 KiB in the view (thinking at 64 KiB, streamed tool arguments
1299
+ at 16 KiB) by eliding the MIDDLE, so both the opening and the live cursor
1300
+ survive; a marker states how many characters went. `transcriptRetain`
1301
+ (default 1000) caps the entries kept in memory, and what it removed is counted
1302
+ and reported both by `/perf` and at the top of the exit replay. A model that
1303
+ emits five megabytes no longer makes every later frame a five-megabyte scan.
1304
+
1305
+ ### Added
1306
+
1307
+ - **A fast model tier: a second, cheaper model you can delegate to and be
1308
+ reviewed by.** Off by default — with `fast.enabled: false` the system prompt,
1309
+ the `task` schema, the transcript and the request payloads are byte-identical
1310
+ to a build without it — and it does two things once you turn it on.
1311
+
1312
+ **Delegation.** `task` gains one optional field per subagent, `model: "fast"`.
1313
+ A child marked that way runs on the fast model and is otherwise identical:
1314
+ same tools, same working directory, same `--confirm` gate, same plan-mode
1315
+ gate, same skills ceiling. Delegating to a cheaper model is a *model* choice,
1316
+ never a permission boundary. It is where "not complicated, but very expensive
1317
+ in context" gets paid for — a child that reads nine files and reports four
1318
+ sentences moves the file bodies out of the lead's window and onto a model that
1319
+ costs a fraction as much per token.
1320
+
1321
+ **Periodic review.** Every few turns a small digest of what the lead has been
1322
+ doing goes to the fast model with one question — is this still on track? — and
1323
+ a critique that is not "on track" is injected into the running loop as a
1324
+ `<fast_review>` block, so the lead sees a second opinion while it can still
1325
+ act on it:
1326
+
1327
+ ```
1328
+ fast review #2 · claude-haiku-4-5 · turn 12 · 1.4s
1329
+ You have edited config/schema.ts three times without running the tests.
1330
+ ```
1331
+
1332
+ The digest is built from a bounded ring of recent turns and **never** from the
1333
+ message history: sending a session's accumulated file bodies to the "cheap"
1334
+ model would make a review cost more than the turn it reviews, which is the
1335
+ inversion the whole feature exists to prevent.
1336
+
1337
+ Spend is attributed **per tier** — a fast child's tokens are priced at the
1338
+ fast model's table, not the lead's, and the dispatch report states the two
1339
+ separately. A model the built-in price table has never seen is reported as
1340
+ `unknown` rather than as `$0.00`, because a feature that looks free while it
1341
+ is spending money is worse than one that admits it does not know.
1342
+
1343
+ Turn it on with `/fast model <id>` then `/fast on`, with
1344
+ `aragon config set fast.enabled true`, with `--fast --fast-model <id>`, or with
1345
+ `ARAGON_FAST=1 ARAGON_FAST_MODEL=<id>`. `/fast status` is the guaranteed
1346
+ readout: the resolved model, the budget, and this session's reviews, delegated
1347
+ children, tokens and cost. Eleven `fast.*` config keys, four flags, four
1348
+ environment variables and five settings-screen rows; see the README.
1349
+
1350
+ **The review has a session budget.** A session is not one run — it is one run
1351
+ per message you send — so a cadence bounds the pace and nothing bounds the
1352
+ total. `fast.reviewMaxPerSession` (default **40**, raisable live with
1353
+ `/fast budget <n>`) is the number of reviews a session may *start*; reaching it
1354
+ prints one `info` notice that names the remedy and says delegation is
1355
+ unaffected, and `/fast status` reads `12/40` from the first review rather than a
1356
+ bare count you can only size after the fact. It is denominated in **reviews,
1357
+ not currency**, because the fast tier is exactly where a model the price table
1358
+ has never seen lives — a dollar ceiling would silently never fire for the
1359
+ configuration that most needs one. Delegation is never budgeted: it is spend the
1360
+ lead chose in service of a message you sent, it is already bounded by `team.*`,
1361
+ and it exists to *reduce* total cost.
1362
+
1363
+ **The review also fails fast, so it cannot compete with you.** It calls through
1364
+ its own provider registry with **one** retry rather than the ten
1365
+ `retry.maxRetries` defaults to for the lead, and it observes neither `retry.*`
1366
+ nor `/retry` — that policy says how hard to fight for *your* answer, and
1367
+ inheriting it for an unrequested background call is how an advisory ends up
1368
+ spending a rate-limited provider's quota on itself. Its own wall-clock bound
1369
+ is 20 s.
1370
+
1371
+ Two failure modes are handled out loud rather than silently. If the tier stops
1372
+ resolving mid-session — you switch the main provider and `fast.provider` was
1373
+ inheriting it — `model:"fast"` is downgraded to the main model, **counted, and
1374
+ stated in the report**, because an agent that silently gets an expensive child
1375
+ has no way to learn its cost model is wrong. And three consecutive
1376
+ *misconfiguration* failures disable the reviewer for the session with exactly
1377
+ one warning, while a merely busy provider (rate-limited or overloaded) is
1378
+ treated as transient and never counts — that condition fixes itself, and a
1379
+ session-long silent shutdown over it would not. A review you *cancelled* is
1380
+ neither: Esc, a run that ends first, or quitting leaves a `cancelled` card and
1381
+ no strike against the tier.
1382
+
1383
+ Reviews log under their own `fast` scope rather than under `agent`, so
1384
+ `fast_review_*` records filter cleanly away from the lead's.
1385
+
1386
+ - **API calls are retried with backoff, and the waiting is legible.** A transient
1387
+ provider failure — HTTP 429, 529, 5xx, 408, or a dropped connection — used to end
1388
+ the turn. One red notice, an idle prompt, and forty tool calls of work thrown
1389
+ away. It is now retried up to **10 times** with an increasing interval
1390
+ (1 s → 2 → 4 → 8 → 16 → 30 s, equal-jittered, 30 s per wait at most) under a
1391
+ 4-minute budget measured from the first failure.
1392
+
1393
+ What you see while it waits is **one line, rewritten in place** — never ten
1394
+ notices — carrying what failed, which attempt this is, and a countdown to the
1395
+ next one:
1396
+
1397
+ ```
1398
+ ↻ Provider overloaded · retry 3/10 in 7s Esc to cancel
1399
+ ↻ recovered after 3 retries · 12.4s
1400
+ ```
1401
+
1402
+ plus a `retry 3/10 7s` chip on the status bar (`[r3]` on a narrow terminal). The
1403
+ card settles into the transcript as history — `recovered`, `gave up`, or
1404
+ `interrupted` — and shows up in the exit replay, because a turn that spent three
1405
+ minutes retrying is exactly the kind of cost an exported transcript should
1406
+ account for.
1407
+
1408
+ **Esc ends the wait within a frame**, not at the end of the countdown, and it
1409
+ never produces a fabricated error: an aborted request is silent. That is the one
1410
+ detail everything else here depends on, because the provider layer marks an
1411
+ `AbortError` as retryable — so the signal is checked *before* the error, and a
1412
+ user pressing Esc can never be mistaken for a timeout worth retrying.
1413
+
1414
+ Never retried: `auth_error` (401/403), `invalid_request`, `context_overflow`.
1415
+ Ten retries of a bad API key is three minutes of looking broken.
1416
+
1417
+ `Retry-After` is honoured as a **floor**, never a replacement — a `Retry-After: 1`
1418
+ on the ninth retry does not undo eight retries of backoff — and a stated wait
1419
+ longer than a minute is surfaced immediately with the provider's own number
1420
+ rather than silently wedging the terminal.
1421
+
1422
+ A connection that dies **mid-answer** is also covered, and this is the case that
1423
+ needed more than a retry loop: the partial text is discarded, the affected tool
1424
+ cards are removed, and the answer restarts from the top. Nothing is duplicated,
1425
+ because the engine only writes history on a completed turn.
1426
+
1427
+ - **`/retry [show|on|off|max <n>]`, `--retry` / `--no-retry`, `--retry-max <n>`,
1428
+ `ARAGON_RETRY`, `ARAGON_RETRY_MAX`, and a `retry` config section** with nine
1429
+ keys (`aragon config set retry.*`). `/retry off` takes effect **in the running
1430
+ session** — subagents included, since they share the lead's provider registry —
1431
+ not only at the next launch. `retry.maxRetries: 0` is the kill switch and is a
1432
+ real value at every one of those entry points. `/settings` gains one read-only
1433
+ `API retries` row.
1434
+
1435
+ - **A subagent's retry is visible on the team card.** A child in backoff reports
1436
+ `retry 3/10` on its own row. Children inherit the retry policy for free, but
1437
+ their events never reach the lead's view, so without this a subagent spending
1438
+ three minutes waiting looked exactly like a hung dispatch.
1439
+
1440
+ - **Retry lines in `-p` mode go to stderr, never stdout.** `[retry 3/10]
1441
+ overloaded — waiting 7s` and `[retry] recovered after 3 retries` are on stderr,
1442
+ so `aragon -p … | jq` stays valid; a discarded partial answer is announced there
1443
+ too, because a piped consumer otherwise cannot tell a replay from a model that
1444
+ repeated itself. They are not suppressed by `--quiet`: a three-minute pause with
1445
+ no explanation is not a compact run, it is a hang.
1446
+
1447
+ - **`/perf`.** Reports what the renderer is actually doing: the frame interval
1448
+ and the last commit cost, how many entries exist / are retained / were dropped
1449
+ / are mounted, height-cache and render-cache occupancy, and the viewport
1450
+ geometry. `/perf reset` clears the caches and returns the governor to its
1451
+ fastest rung. Silent degradation is the one thing a claim of robustness cannot
1452
+ afford, and this is the place that names it.
1453
+
1454
+ - **Three configuration keys**, each with a flag and an environment override:
1455
+ `transcriptRetain` (`--transcript-retain`, `ARAGON_TRANSCRIPT_RETAIN`),
1456
+ `renderGovernor` (`--render-governor` / `--no-render-governor`,
1457
+ `ARAGON_RENDER_GOVERNOR`) and `maxRenderIntervalMs`
1458
+ (`--max-render-interval`, `ARAGON_MAX_RENDER_INTERVAL_MS`). No migration and no
1459
+ config version bump: a file written before they existed loads unchanged.
1460
+ `transcriptRetain` is raised to `transcriptWindow` when the two conflict —
1461
+ retaining less than you can scroll to would make part of the horizon
1462
+ unreachable — and a startup notice names both numbers, because overriding a
1463
+ value you typed and saying nothing is the one thing none of this may do.
1464
+
1465
+ - **Following the plan through, on a budget you can do arithmetic on.** A run
1466
+ that ended with steps outstanding used to print one sentence and stop. It now
1467
+ asks three questions the CLI already knew the answers to — did you abort it,
1468
+ did it error, did the list move — and does one of three things.
1469
+
1470
+ With the default `notify` the sentence is unchanged, except that a run **you**
1471
+ cancelled with `Esc` now says nothing at all: being told what you just
1472
+ interrupted is the CLI answering a question nobody asked.
1473
+
1474
+ With `/todo follow auto` it continues the plan itself, after announcing
1475
+ `Continuing with 4 remaining steps in 3s - Esc to stop.` Auto-continuation was
1476
+ refused last release as "an unbounded cost loop wearing a helpful hat", and
1477
+ that judgement stands — what changed is that the loop is now bounded first.
1478
+ A continuation that completes no step buys exactly one more attempt; a single
1479
+ plan is continued at most 25 times however productive it looks; and a re-plan
1480
+ forgives the first counter but never the second, so a model that reshapes its
1481
+ list every turn cannot lift the ceiling. A run that ended with an error never
1482
+ auto-continues in any mode. `Esc` cancels, and so does typing anything.
1483
+
1484
+ Whatever sends the continuation — the timer or `/todo continue` — now sends
1485
+ the numbered list of open steps rather than the words "continue with the
1486
+ remaining todo items", so a plan whose `todo_write` has scrolled out of the
1487
+ context window is still followed exactly. Neither path records that text in
1488
+ your prompt history any more; it is not something you typed.
1489
+
1490
+ Under `-p` there is no grace window, because there is nobody there to use it,
1491
+ which is precisely why the two counters rather than the human are the
1492
+ protection there. `[todo] continuing (3 steps left)` and
1493
+ `[todo] 3 steps unfinished` go to stderr, suppressed by `--quiet`, and an
1494
+ unfinished plan still never changes the exit code.
1495
+
1496
+ New surfaces: `/todo follow <notify|auto|off>`; `--todo-follow <mode>`;
1497
+ `ARAGON_TODO_FOLLOW`; and `todo.followThrough` in `config.json`. The default
1498
+ is `notify`, so a session that does not ask for any of this behaves as it did.
1499
+
1500
+ - **A plan strip for inline mode.** Inline mode has no fixed frame and so no
1501
+ rail, which left every pipe, every `CI=1`, every `TERM=dumb` terminal and
1502
+ every window under twelve rows able to see `todo 3/7` but not *which* step was
1503
+ running. There is now a one-row strip under the transcript —
1504
+ `todo 3/7 > Adding the rail to AppShell +2 done` — windowed on the
1505
+ same step the rail would have anchored on. It truncates rather than wrapping
1506
+ and is exactly one row at every width, so the composer does not move.
1507
+ `--no-todo-panel` hides it along with the rail.
1508
+
1509
+ ### Changed
1510
+
1511
+ - `npm run typecheck` now covers the test tree in both packages. `tsconfig.json`
1512
+ excludes `__tests__` so the build never emits fixtures, and vitest transpiles
1513
+ without typechecking — between the two, a widened required field could break
1514
+ ten test files while `npm run build` and `npm test` were both green. Twelve
1515
+ such errors had accumulated; all twelve were fixture drift and all twelve are
1516
+ repaired, with no suppressions and no production type relaxed to accommodate a
1517
+ fixture.
1518
+
1519
+ - **TODO planning, and a rail to watch it in.** For work with three or more
1520
+ distinct steps the agent now keeps a visible plan: it writes a checklist with
1521
+ a new `todo_write` tool, marks exactly one item in progress, and ticks it off
1522
+ before starting the next. In full-screen mode the list is rendered as a
1523
+ right-hand rail about a fifth of the terminal wide, windowed around the step
1524
+ being worked on rather than sliced from the top. Ask for something small and
1525
+ nothing appears — the prompt tells the model not to plan work it can finish in
1526
+ one or two steps, and a one-item list against an empty plan is refused
1527
+ outright, so a session that never plans has byte-identical layout to a build
1528
+ without this feature.
1529
+
1530
+ The list is a projection of what the model believes, and that single rule
1531
+ decides everything else: `/clear` keeps it (the conversation is still there),
1532
+ `/reset` drops it, `/save` writes it and `/resume` restores it — and resuming
1533
+ a session that carries no list CLEARS the current one rather than leaving a
1534
+ plan on screen whose conversation has just been replaced. A finished plan
1535
+ survives to the start of your next message so you get to see `7/7 done`; an
1536
+ unfinished one survives "continue" and "now do the rest", then expires after
1537
+ three unrelated turns rather than holding a fifth of the screen for the rest
1538
+ of the session. A run that ends with items left over produces a notice and
1539
+ `/todo continue`; nothing auto-continues on your behalf.
1540
+
1541
+ "One step at a time" is enforced by structure rather than asked for in a
1542
+ prompt: any payload with two in-progress items is repaired down to one, and a
1543
+ list with none promotes the first unfinished item. Whatever the model sends,
1544
+ the panel has exactly one current row.
1545
+
1546
+ New surfaces: the `todo_write` tool; `/todo [status|on|off|panel on|off|clear|
1547
+ continue]`; `--todo` / `--no-todo` and `--todo-panel` / `--no-todo-panel`;
1548
+ `ARAGON_TODO=0`; and `todo.enabled` / `todo.panel` in `config.json` (a third
1549
+ key, `todo.followThrough`, arrived with the follow-through entry above).
1550
+ `panel` is
1551
+ separate from `enabled` on purpose — a screen-reader user wants the planning
1552
+ discipline without the column, and the system prompt varies one sentence to
1553
+ match. Under `-p` the plan goes to stderr as `[todo] 3/7 <step>` lines,
1554
+ suppressed by `--quiet`. Below 80 columns, behind an overlay, in inline mode
1555
+ or on a short terminal the rail is not mounted and the counter appears in the
1556
+ status bar instead; the transcript keeps at least 62 columns at every width.
1557
+
1558
+ - **Output token limits that cannot themselves fail a run.** Every model and
1559
+ every provider now defaults to 64000 output tokens, and that number is
1560
+ configurable from the TUI, from a flag, from `ARAGON_MAX_TOKENS`, and by hand
1561
+ in `~/.aragon-agent/config.json`. Leave it alone and it stays 64000.
1562
+
1563
+ What is new is that the number is an *ambition*. `gpt-4o` caps at 16384 and
1564
+ `gemini-1.5-pro` at 8192; sending 64000 to either is an HTTP 400 that ends the
1565
+ turn with a raw provider string in the transcript. The cap is now clamped down
1566
+ to whatever the target model actually accepts, from a per-model table plus
1567
+ whatever the provider's own model list reports. For a model nothing knows — a
1568
+ proxy, a `--base-url` endpoint, something that shipped last week — the request
1569
+ goes out at 64000, and if the provider objects, `aragon` reads the real ceiling
1570
+ out of the error text, repairs the request, sends it exactly once more, and
1571
+ remembers the answer for the rest of the session. One warning line; the run
1572
+ continues.
1573
+
1574
+ Two combinations that were a guaranteed 400 on every request are fixed with
1575
+ it: `--thinking xhigh` asks for a 65536-token budget while Anthropic requires
1576
+ `max_tokens` to be strictly greater than it, and OpenAI's reasoning family
1577
+ (`o1`, `o3`, `gpt-5`) rejects the `max_tokens` parameter name outright in
1578
+ favour of `max_completion_tokens`.
1579
+
1580
+ New surfaces: `/max-tokens [<n>|auto|default]`, which with no argument reports
1581
+ the setting *and* the effective cap for the live model; an `Effective:` line
1582
+ under the settings screen's `Max tokens` field, so a value the model will not
1583
+ honour is visible before you save it; `--max-tokens auto`; and
1584
+ `aragon config set maxTokens <n|auto|default>`, which echoes the value that was
1585
+ actually stored rather than the one you typed.
1586
+
1587
+ - **Team subagents.** The agent can now hand independent parts of a job to up to
1588
+ five short-lived subagents that run in parallel, then read one combined report
1589
+ of what each of them found. It decides for itself whether to delegate, so a
1590
+ session that never calls the new `task` tool is byte-for-byte the session that
1591
+ existed before. On by default; `--no-team` or `team.enabled: false` turns it
1592
+ off, and `aragon config set team.maxSubagents <n>` sets the width (1-10; the
1593
+ 10 is a hard ceiling no config file can raise).
1594
+
1595
+ A subagent is a full agent with the same tools and working directory but no
1596
+ memory of your conversation, so its brief has to be self-contained. It cannot
1597
+ delegate further and it cannot ask you anything. Plan mode and a skill's
1598
+ `allowed-tools` ceiling both reach one level down, so delegating is never a
1599
+ way around either; `--confirm` queues subagent confirmations one at a time and
1600
+ labels each with the subagent that is asking.
1601
+
1602
+ Subagents can message each other with `team_send` / `team_wait`, delivered by
1603
+ attaching the message to the recipient's next tool result rather than
1604
+ interrupting it. The limits are deliberate — 6 messages each, one every 15
1605
+ seconds, 24 per dispatch — and every refusal is a plain result naming the
1606
+ limit rather than an error, because an error invites the retry the limits
1607
+ exist to prevent.
1608
+
1609
+ While a dispatch runs, a roster sits between the transcript and the composer
1610
+ and the status bar carries `agents 3/5`; both disappear when it finishes and
1611
+ the transcript keeps a card you can expand with `Ctrl+O`. `Esc` aborts every
1612
+ subagent and returns the partial report. Under `-p` the transitions go to
1613
+ stderr and the subagents' tokens are folded into the `[usage]` footer, so the
1614
+ cost readout cannot under-report what the team spent.
1615
+
1616
+ Full documentation, including the limits table and how team mode interacts
1617
+ with plan mode, skills and `--confirm`, is in the README.
1618
+
1619
+ - **The subagent roster now shows what each subagent is doing, and shows the
1620
+ ones that are working.** Where every row used to say `thinking` for minutes at
1621
+ a time, it now says `bash: npm test -w cli`, `read: src/api/routes.ts`,
1622
+ `waiting for a2`, or the tail of the sentence the subagent is currently
1623
+ writing. On a narrow terminal the same row falls back to a file name and a
1624
+ program name rather than wrapping, and the elapsed clock keeps its place at
1625
+ every width.
1626
+
1627
+ The five visible rows are now the five that matter: whatever is running comes
1628
+ first, then whatever is queued, then the most recent results. Above five
1629
+ subagents the old panel showed the five that had finished first and hid
1630
+ everything still working behind `+3 more`, which is the opposite of what the
1631
+ roster is for. `+3 more` now says how many of the hidden ones are still going.
1632
+
1633
+ - **A subagent no longer waits two minutes for a message nobody can send.**
1634
+ `team_wait` now checks whether anyone could actually answer before it blocks:
1635
+ if the teammate it named has already finished, does not exist, or is itself
1636
+ waiting in a way that cannot be released in time, it returns straight away and
1637
+ says which. Nothing is refused that could still have been answered — including
1638
+ by a third teammate further down the chain — and the subagent is told what to
1639
+ do instead rather than handed an error.
1640
+
1641
+ - **A subagent whose very first request dies on a rate limit is retried once.**
1642
+ Only a genuine cold start qualifies: no turn completed and no tool ran, so
1643
+ there is nothing to undo. It restarts after two seconds, the roster says
1644
+ `starting (retry)`, and the report notes `retried 1x`. Pressing `Esc`, a
1645
+ timeout, or any subagent that had already begun work is never retried.
1646
+
1647
+ ### Changed
1648
+
1649
+ - **`"maxTokens": null` in `config.json` now means AUTO** — use each model's own
1650
+ ceiling, never above 64000. It previously resolved to 64000 by accident (the
1651
+ layered resolver skipped `null`), and there was no way to express AUTO through
1652
+ any supported write path. If you have a `null` there today, the only difference
1653
+ is a downward clamp on models whose real ceiling is below 64000 — the direction
1654
+ that prevents failures rather than causing them. Absent still means 64000, and
1655
+ an explicit number still means that number.
1656
+
1657
+ - **`config.json` holds settings again.** The prompts you type are no longer
1658
+ stored in it. They move to `~/.aragon-agent/prompt-history.jsonl` — one JSON
1659
+ object per line, `0600` on POSIX — and the submit counter and the "notice
1660
+ already shown" flag move to `~/.aragon-agent/state.json`. `↑` and `↓` behave
1661
+ exactly as before.
1662
+
1663
+ Two things get better. Opening `config.json` to change your model no longer
1664
+ means scrolling past a transcript of your own typing, and copying it to
1665
+ another machine no longer carries your prompts along. And submitting a message
1666
+ no longer rewrites the file that holds your API keys: `config.json` is written
1667
+ when you change a setting, and at no other time.
1668
+
1669
+ **Your history and counters are migrated automatically, once, at the next
1670
+ launch.** Nothing is printed, because nothing changes for you. The dead
1671
+ `recentModels` key — which nothing has ever written — is dropped.
1672
+
1673
+ If you go back to 0.5.x after upgrading, that version does not know about the
1674
+ new file and `↑` will look empty. Nothing has been lost: the entries are still
1675
+ in `prompt-history.jsonl`, and upgrading again brings them back.
1676
+
1677
+ ### Added
1678
+
1679
+ - **`aragon history`** — `path`, `list` (`-n`, `--json`) and `clear --yes` for
1680
+ the prompt history, alongside the existing `aragon logs` commands. Entries are
1681
+ stored verbatim, so `clear` is how you erase them; `aragon config set
1682
+ historyEnabled false` stops new ones being recorded without deleting or hiding
1683
+ what is already there.
1684
+
1685
+ - **The mouse wheel scrolls what is under the pointer.** In full-screen mode the
1686
+ wheel now scrolls the transcript three rows per notch (a page with `Shift`),
1687
+ scrolls an open overlay when the pointer is over one, and steps prompt history
1688
+ one entry per notch when the pointer is over the input box — the same code
1689
+ path `↑` / `↓` use, so it inherits the rule that an in-progress draft is never
1690
+ overwritten. A slim rail on the viewport's right edge shows where you are in
1691
+ the conversation.
1692
+
1693
+ **This fixes a destructive bug.** Previously the wheel recalled prompt history
1694
+ no matter where the pointer was, because the alternate screen makes terminals
1695
+ translate each notch into arrow keys. Wheeling while composing a message
1696
+ silently replaced the draft with an old prompt, and nothing scrolled.
1697
+
1698
+ **Selecting text with the mouse now requires `Shift`+drag** in most terminals.
1699
+ That is the standard convention for "an application is tracking the mouse" and
1700
+ it applies to everyone, including people who never scroll with the wheel — so
1701
+ the first session says so once, in the transcript, and never again. Turn the
1702
+ whole feature off with `--no-mouse`, `ARAGON_MOUSE=0`, or
1703
+ `aragon config set mouse false`; the wheel then does nothing rather than
1704
+ reverting to the old behaviour. Inline mode (`--no-fullscreen`) is untouched:
1705
+ your terminal's own scrollback already does the right thing there.
1706
+
1707
+ Under `tmux` with `mouse on`, tmux keeps the wheel for itself and the app
1708
+ never sees it. `set -g mouse off` hands it back.
1709
+
1710
+ ### Changed
1711
+
1712
+ - **Your files moved to `~/.aragon-agent/`.** Config, sessions, installed skills
1713
+ and the usage counters now live in one directory in your home — on Windows
1714
+ `C:\Users\<you>\.aragon-agent\`, and the same path on macOS and Linux. They
1715
+ used to be split across `%APPDATA%\aragon-agent-nodejs\Config` and
1716
+ `%LOCALAPPDATA%\aragon-agent-nodejs\Data` (or the XDG equivalents), which are
1717
+ correct locations and terrible answers to "where do I change the model?".
1718
+
1719
+ **The move happens once, automatically, on first launch, and prints a line on
1720
+ stderr when it does.** Nothing is deleted: `config.json` is COPIED rather than
1721
+ moved, and the old directories are left where they are, so reinstalling 0.5.0
1722
+ remains lossless. `aragon config home` prints the new location and names the
1723
+ old ones. Once you are satisfied, you can delete them yourself.
1724
+
1725
+ `ARAGON_HOME` relocates the whole tree. It has to be a real environment
1726
+ variable — the path is resolved before any `.env` is read, so putting it in a
1727
+ `.env` will not work. `aragon config home` always prints what is actually in
1728
+ use.
1729
+
1730
+ ### Added
1731
+
1732
+ - **Logging.** Records go to `~/.aragon-agent/logs/aragon-<date>.log` as JSON
1733
+ Lines: startup and exit, config writes, turn boundaries with token counts,
1734
+ tool names with durations, skill loads, and every uncaught error. Files rotate
1735
+ by day and by size (`log.maxFileBytes`, default 5 MiB) and the oldest beyond
1736
+ `log.maxFiles` (default 10) are deleted.
1737
+
1738
+ **`info`, the default, records no conversation content**, so attaching a log
1739
+ to a bug report is safe. `--log-level debug` (or `--verbose`) adds prompts,
1740
+ replies and tool arguments truncated to `log.previewChars`; `trace` adds them
1741
+ in full. Those two write what you typed — check before sharing one.
1742
+
1743
+ API keys are removed at every level by two independent passes: fields whose
1744
+ name looks like a credential are blanked whatever they hold, and every string
1745
+ is scanned both for known key formats and for the literal keys this process is
1746
+ holding — which is what covers a custom endpoint whose key looks like nothing
1747
+ in particular. `log.redactSecrets false` turns both off and warns you on
1748
+ stdout when you set it.
1749
+
1750
+ Nothing is ever uploaded. There is no network path out of this subsystem.
1751
+
1752
+ New commands: `aragon logs path | list | tail [-n N] [--follow] [--level lv]
1753
+ [--json] | clear --yes | open`, and `/logs` inside the TUI. New flags:
1754
+ `--log-level`, `--verbose`, `--log-file` / `--no-log-file`, `--log-dir`. New
1755
+ environment variables: `ARAGON_LOG_LEVEL`, `ARAGON_LOG_FILE`, `ARAGON_LOG_DIR`
1756
+ — these three, unlike `ARAGON_HOME`, can come from a `.env`.
1757
+
1758
+ - **`aragon config get <key>` / `list [--json]` / `edit` / `home`.** `get` and
1759
+ `list` mask every secret, `--json` included. `edit` opens `config.json` in
1760
+ `$VISUAL` / `$EDITOR` (`notepad` on Windows), takes a `config.json.bak` first,
1761
+ and re-parses on exit — an invalid edit is reported immediately, and the
1762
+ backup is named in the message.
1763
+
1764
+ - **`/reload`** re-reads `config.json` into the running session, for when you
1765
+ have edited it in another window. Refused while a run is in progress: swapping
1766
+ the model mid-turn would let the second half of an answer come from different
1767
+ settings than the first, with nothing afterwards able to explain the result.
1768
+
1769
+ - **Log level in the settings screen**, alongside provider / model / thinking.
1770
+ The rest of the log settings — and `redactSecrets` above all — stay
1771
+ command-line-only, so the one switch that can put credentials on disk cannot
1772
+ be flipped with an arrow key.
1773
+
1774
+ ### Fixed
1775
+
1776
+ - **A malformed `config.json` no longer resets everything in silence.** A stray
1777
+ comma used to send the model, theme, timeouts, skills and (as far as you could
1778
+ tell) your API key back to defaults, with nothing anywhere explaining it. The
1779
+ fallback is unchanged — a bad config still never stops the CLI from starting —
1780
+ but it now says so, and records why.
1781
+
1782
+ - **Ctrl+C now flushes pending log records in every mode.** The signal handlers
1783
+ only existed in the full-screen branch, so `-p`, the inline renderer and every
1784
+ subcommand took Node's default termination, which does not run exit handlers.
1785
+
1786
+ ### Added
1787
+
1788
+ - **Plan mode.** `Shift+Tab` toggles between `BUILD` (everything as before) and
1789
+ `PLAN`, a read-only research-and-design posture. `/plan [on|off|status]` is a
1790
+ first-class equivalent for terminals that swallow `CSI Z`, and `--plan` /
1791
+ `--no-plan` / `ARAGON_PLAN=1` / `planModeDefault` cover the startup case.
1792
+
1793
+ In `PLAN`, `write_file`, `edit_file`, `bash`, `skill_install` and
1794
+ `skill_create` are refused **at the tool boundary**, so read-only is a
1795
+ property of the wiring rather than a promise in a prompt. `bash` is refused in
1796
+ full, `git status` included: a shell string cannot be classified reliably, and
1797
+ a gate that is right most of the time is worse than one that is always right,
1798
+ because you stop trusting the badge. The refusal names `read_file` / `glob` /
1799
+ `grep` as the alternatives.
1800
+
1801
+ Two tools exist only while a human is attached. `ask_user` renders a
1802
+ keyboard-driven wizard - 1-5 questions, 2-4 options each, exactly one marked
1803
+ `RECOMMENDED`, plus an `Other...` free-text option - where pressing `Enter`
1804
+ through the wizard accepts every recommendation. `submit_plan` renders the
1805
+ plan as a scrollable card: `a` approves, `r` sends written feedback (`<-`
1806
+ returns to the card), `Esc` dismisses. **Approving flips the session to
1807
+ `BUILD` immediately and the same run continues into implementation.**
1808
+
1809
+ Dismissing a plan asks for a better one rather than stopping - the agent is
1810
+ told to refine and resubmit - so the way out of a run is `Esc` twice: the
1811
+ first closes the card, the second aborts the run.
1812
+
1813
+ Toggling mid-run is asymmetric on purpose: `build -> plan` applies at once,
1814
+ `plan -> build` is deferred to the end of the run (the status bar shows
1815
+ `PLAN -> BUILD` meanwhile) so a run launched under a read-only guarantee
1816
+ cannot start writing files because of one stray keypress.
1817
+
1818
+ `aragon -p --plan "..."` registers no question or plan tool - there is nobody
1819
+ to ask - but the read-only gate still applies, so it means "tell me how you
1820
+ would do this, and do not touch my repository".
1821
+
1822
+ - Three flat config keys: `planModeDefault` (default `false`),
1823
+ `planModeMaxAskRounds` (default `4`, clamped `[1, 10]`) and
1824
+ `planModeHumanTimeoutMs` (default `1800000`, clamped `[60000, 7200000]`). All
1825
+ three are settable with `aragon config set`. Additive, as always: a config
1826
+ file written by an earlier version loads unchanged.
1827
+
1828
+ ### Fixed
1829
+
1830
+ - A `--confirm` prompt no longer races the idle watchdog. Taking longer than
1831
+ `idleTimeoutMs` over `Proceed? (y/N)` used to abort the run with no
1832
+ explanation; every human wait now suspends the watchdog and restarts it in a
1833
+ `finally`.
1834
+
1835
+ - Honor an explicit `NODE_TLS_REJECT_UNAUTHORIZED=0` setting in the CLI and the
1836
+ official npm release workflow while hiding only Node's standard insecure-TLS
1837
+ warning. Other process warnings remain visible.
1838
+
1839
+ ## 0.5.0
1840
+
1841
+ ### Breaking
1842
+
1843
+ - **The package is renamed `@argon-agent/cli` → `@aragon-agent/cli`.** The brand
1844
+ was always meant to be *Aragon*, matching AragonMesh; the missing `a` was a
1845
+ typo that reached the registry. Install with:
1846
+
1847
+ ```
1848
+ npm uninstall -g @argon-agent/cli
1849
+ npm install -g @aragon-agent/cli
1850
+ ```
1851
+
1852
+ `@argon-agent/cli` is deprecated and receives no further releases. **The
1853
+ executable is still `aragon`** — the command you type does not change.
1854
+
1855
+ - **Environment variables are `ARAGON_*`, not `ARGON_*`.** There is no
1856
+ compatibility fallback: `ARGON_MODEL`, `ARGON_PROVIDER`, `ARGON_BASE_URL`,
1857
+ `ARGON_THINKING`, `ARGON_MAX_TOKENS`, `ARGON_THEME`, `ARGON_FULLSCREEN`,
1858
+ `ARGON_SKILLS`, `ARGON_SKILLS_DISABLED`, `ARGON_SKILLS_PATH` and
1859
+ `ARGON_REDUCED_MOTION` are all ignored from this release on. Rename them in
1860
+ your shell profile and CI. Provider keys (`ANTHROPIC_API_KEY`,
1861
+ `OPENAI_API_KEY`, `GOOGLE_API_KEY`, `GEMINI_API_KEY`) are untouched.
1862
+
1863
+ - **The project-level skills directory is `.aragon/skills`, not
1864
+ `.argon/skills`.** This one lives in your repository, so rename it yourself:
1865
+
1866
+ ```
1867
+ git mv .argon .aragon
1868
+ ```
1869
+
1870
+ A config file that already persisted the old default keeps working — an
1871
+ explicitly saved `skills.projectDirs` still wins over the default — but the
1872
+ shipped default is now `['.aragon/skills', '.claude/skills']`. Update it with
1873
+ `aragon config set` if you want the new default.
1874
+
1875
+ ### Added
1876
+
1877
+ - **Automatic migration of your user-level state.** On first run, 0.5.0 moves
1878
+ the `env-paths` config and data directories from `argon-agent` to
1879
+ `aragon-agent` — your API key, sessions and installed skills come with it, and
1880
+ each installed skill's `.argon-skill.json` manifest is renamed to
1881
+ `.aragon-skill.json` so `skills update` and `skills uninstall` still recognise
1882
+ it. The migration prints one line to **stderr**, runs once, and is skipped
1883
+ entirely if the new directory already exists. If it fails for any reason the
1884
+ CLI still starts; the old directory is never deleted when a cross-volume copy
1885
+ was needed.
1886
+
1887
+ - The opening wordmark now reads `ARAGON` and is 52 columns wide (was 44).
1888
+ Terminals between 52 and 55 columns show the single-line banner rather than a
1889
+ clipped wordmark.
1890
+
1891
+ - **Skills.** A skill is a directory holding a `SKILL.md` (YAML frontmatter +
1892
+ Markdown body) plus optional `reference/`, `scripts/` and `assets/`. Skills
1893
+ reach the model through three levels of progressive disclosure: a name +
1894
+ one-line "when to use" catalog in the system prompt (≤ 6 000 bytes total),
1895
+ the full body only when the model calls the `skill` tool, and bundled files
1896
+ only when it reads them with `read_file` / `bash`. A skill therefore costs a
1897
+ line of context rather than its whole body. How many fit in Level 1 depends on
1898
+ description length and language — roughly 30 with terse English descriptions,
1899
+ roughly 10 with Chinese ones at the character cap; past that the catalog
1900
+ truncates and `skill_find` covers the remainder.
1901
+ - Four model-facing tools: `skill` (load), `skill_find` (search installed
1902
+ skills), `skill_install` (install from a local directory, git repo, or https
1903
+ `SKILL.md` / `.zip`) and `skill_create` (author a new one from a procedure the
1904
+ agent just worked out).
1905
+ - **`aragon skills update <name> | --all`**, with `--dry-run`, `--force` and
1906
+ `--check`. Re-fetches from the source recorded in the install manifest,
1907
+ preserving `installedAt` and recording `updatedAt` / `previousVersion`. Refuses
1908
+ before downloading anything when the installed copy has local edits, and
1909
+ re-validates the source against the *current* `skills.allowedHosts` rather than
1910
+ the one in force at install time. Not exposed to the model.
1911
+ - **Catalog ranking.** Level 1 entries are ordered by scope, then by how recently
1912
+ and often each skill was used, so truncation drops what you do not use rather
1913
+ than what sorts last alphabetically. With no usage data the order is unchanged.
1914
+ - **`skill_find`**, advertised in the prompt only when the catalog actually
1915
+ truncated. Searches installed skills by name, description and keyword; never
1916
+ reaches the network, never returns `activation: manual` skills, and tells the
1917
+ model to ask for a source rather than invent one when nothing matches.
1918
+ - **Cross-process install lock.** Install / create / remove / update take a
1919
+ per-directory advisory lock, closing a race in which two concurrent installs
1920
+ could interleave their atomic-replace steps and delete a skill with no error.
1921
+ Self-expires after 60 s; waits at most 5 s; only ever released by its owner.
1922
+ - **Load-time integrity.** Discovery re-checks `SKILL.md` against the sha256
1923
+ recorded at install, at no extra I/O. `skills.integrity` selects
1924
+ `off` / `warn` (default) / `strict`.
1925
+ - **Local usage counters** at `<data>/skill-usage.json` — skill name, count and
1926
+ timestamp only, never transmitted, disabled with `skills.usageTracking=false`.
1927
+ Automatically injected `activation: always` skills are not counted.
1928
+ - Config keys `skills.integrity` and `skills.usageTracking`; `aragon skills list
1929
+ --sort=recent` and `/skills list --sort=recent`.
1930
+ - Every usable skill also becomes a slash command: `/<skill-name> [args]`, with
1931
+ `$ARGUMENTS` / `$1..$9` substitution. On a name clash the built-in command
1932
+ always wins and the skill is reachable as `/skill:<name>`.
1933
+ - `/skills` management: `list`, `info`, `install`, `remove`, `enable`,
1934
+ `disable`, `reload`, `create`, `trust`, `untrust`.
1935
+ - `aragon skills …` non-interactive equivalents plus `path` and `doctor`.
1936
+ Exit codes follow `config set`: `0` ok, `1` run-time failure, `2` usage.
1937
+ - Four discovery scopes in ascending precedence: bundled (`<pkg>/skills`), user
1938
+ (`<data>/skills`), project (`<cwd>/.aragon/skills` and, read-only,
1939
+ `<cwd>/.claude/skills` for Claude Code interop), and `ARAGON_SKILLS_PATH`.
1940
+ - New flags `--no-skills`, `--skill <name>` (repeatable) and `--skills-yes`;
1941
+ env `ARAGON_SKILLS`, `ARAGON_SKILLS_PATH`, `ARAGON_SKILLS_DISABLED`; config keys
1942
+ `skills.enabled`, `skills.requireApproval`, `skills.catalogMaxBytes`,
1943
+ `skills.bodyMaxBytes`.
1944
+ - One bundled skill, `skill-creator`, documenting how to write a good one.
1945
+ - New runtime dependency: `fflate` (~30 KB, MIT, zero transitive deps) for zip
1946
+ extraction. `@aragon-agent/core` gains no dependency.
1947
+ - **Release tooling for the scope change.** `npm run verify:brand` scans every
1948
+ file that `npm pack` would put in the tarball — README and `dist/` included —
1949
+ and blocks the release on any surviving `argon` reference, with only the four
1950
+ documented exceptions (the two CHANGELOGs and the legacy-state migration
1951
+ module, which hold the old names by definition). `packages/cli` also gains a
1952
+ `prepublishOnly: npm run build`, so a publish that bypasses
1953
+ `publish-latest.ps1` can no longer ship a `dist/` that is out of step with the
1954
+ sources. `deprecate-legacy.ps1` retires `@argon-agent/*` on the registry, and
1955
+ refuses to do so until the replacements are actually published.
1956
+
1957
+ ### Changed
1958
+
1959
+ - The globally installed executable is now `aragon`. The previous `argon` and
1960
+ `argon-agent` aliases are no longer installed.
1961
+ - `config/store.ts` now deep-merges the `skills` section on both read and write,
1962
+ the same treatment `apiKeys` already received. Without it a partial patch such
1963
+ as the one `/skills disable` sends would replace the whole section and
1964
+ silently discard `trustedProjectDirs`, `allowedHosts` and `requireApproval`.
1965
+ - `slashSuggestions` accepts `-` and `:`, so kebab-case and `skill:`-namespaced
1966
+ commands autocomplete. Previously `/my-sk` produced no popup at all.
1967
+ - `AgentController` now has a single `agent.setSystemPrompt()` call site, so a
1968
+ `/cwd` change can no longer drop the skill catalog out of the prompt.
1969
+ - With skills off or none installed, the system prompt and the 7-tool array are
1970
+ byte-identical to the previous release.
1971
+ - Added a repository-level PowerShell release workflow that automatically
1972
+ updates CLI and Core versions, synchronizes their dependency and lockfile,
1973
+ verifies both tarballs, and publishes them in dependency order.
1974
+
1975
+ ### Security
1976
+
1977
+ - Installing a skill requires an explicit human approval that **probes for a
1978
+ human first and refuses when there is none**. Under `-p` there is no TUI and
1979
+ therefore no approver, so `skill_install` is refused with an actionable
1980
+ message rather than writing a third-party directory unattended. Use
1981
+ `--skills-yes` (or `skills.requireApproval=false`) to opt out deliberately.
1982
+ - Project skill directories require a one-time trust confirmation before they
1983
+ are loaded — cloning an unfamiliar repository does not grant it the ability
1984
+ to inject instructions into your session.
1985
+ - Git is invoked only via `execFile` with an argv array and `shell: false`, and
1986
+ owner / repo / ref / subdirectory fragments are whitelisted and refused if
1987
+ they begin with `-`. A leading dash matters even with a pure argv array:
1988
+ `--upload-pack=<program>` makes git execute that program.
1989
+ - Zip extraction is streamed and gated on **actually written** bytes, per-entry
1990
+ size, compression ratio and entry count, all of which can abort mid-archive.
1991
+ The size a zip *declares* is written by whoever built it and is never trusted.
1992
+ Path traversal, absolute entries, symlinks and Windows device names are
1993
+ refused, and a rejected archive is removed rather than left half-extracted.
1994
+ - Network fetches are `https:` only, restricted to `skills.allowedHosts`,
1995
+ re-checked on every redirect (max 3), size-capped, and refuse IP literals and
1996
+ loopback.
1997
+ - Nothing a skill ships is executed at install time, and the executable bit is
1998
+ not preserved. Scripts run only when the model invokes `bash` explicitly.
1999
+ There is no sandbox: approval blocks *silent* installation, it does not make
2000
+ a skill you approved safe.
2001
+
2002
+ ## 0.3.0
2003
+
2004
+ A full-screen TUI release. `@argon-agent/core` is untouched, no new runtime
2005
+ dependency is added, and the headless/print contract, exit codes, and
2006
+ slash-command semantics are unchanged. The 0.2.0 inline renderer remains
2007
+ available in full via `--no-fullscreen`.
2008
+
2009
+ ### Added
2010
+
2011
+ - **Full-screen mode (default).** `aragon` now takes over the terminal's
2012
+ alternate screen buffer — the same mechanism `vim` / `htop` / `lazygit` use.
2013
+ Your shell history is covered, not erased, and comes back untouched on exit.
2014
+ The frame is a fixed `rows - 1` tall, so **the composer and the status bar sit
2015
+ at the physical bottom of the screen from the very first frame**: empty
2016
+ session, long session, mid-scroll, or overlay open.
2017
+ - **Brand region.** A six-row gradient ASCII wordmark on a roomy empty session,
2018
+ degrading to a banner, then a single-line bar, then a bare wordmark as space
2019
+ runs out. Row 1 always begins with the brand glyph.
2020
+ - **Self-drawn scrolling.** `PgUp` / `PgDn` by the page, `Shift+↑` / `Shift+↓` by
2021
+ the line. The viewport auto-follows new output while pinned to the bottom;
2022
+ scrolling away shows `↑N` in the status bar plus a "N new lines" hint, and
2023
+ submitting a message always re-pins.
2024
+ - **Exit replay.** After leaving the alternate screen the session is replayed
2025
+ into the normal buffer as plain, colorless text, so the conversation does not
2026
+ evaporate with the screen. Turn it off with `--no-exit-transcript`.
2027
+ - **New flags** `--fullscreen` / `--no-fullscreen` / `--no-exit-transcript`, the
2028
+ env var `ARGON_FULLSCREEN=0|1`, and the config keys `fullscreen`,
2029
+ `exitTranscript`, and `transcriptWindow`.
2030
+ - **Automatic downgrade.** A non-TTY stdout, `TERM=dumb`, CI, and terminals under
2031
+ 12 rows / 40 columns fall back to the inline renderer. Only the non-TTY gate is
2032
+ un-overridable — `--fullscreen` beats the rest.
2033
+
2034
+ ### Changed
2035
+
2036
+ - The status bar lost its round border and is now exactly one row; it had been
2037
+ spending an eighth of a 24-row terminal framing a single line of text.
2038
+ - The toast strip holds a fixed row in full-screen mode, so the transcript no
2039
+ longer jumps as toasts appear and expire.
2040
+ - The composer is a rounded frame whose border color reports state (idle, has a
2041
+ draft, running, blurred by an overlay), with the completion popup above it.
2042
+ - `Ctrl+L` repaints the frame instead of writing a clear sequence. Ink dedupes
2043
+ identical output at two separate gates, so under a fixed frame the old
2044
+ approach erased the screen and then declined to redraw it.
2045
+ - `Shift+↑` / `Shift+↓` no longer recall prompt history — they scroll.
2046
+ - Full-screen mode routes `console.*` into the transcript as notices rather than
2047
+ letting Ink write them straight to stdout, which corrupts the frame's line
2048
+ accounting.
2049
+
2050
+ ### Known trade-offs
2051
+
2052
+ - The terminal's native scrollback and mouse wheel do not scroll the transcript
2053
+ in full-screen mode; use `PgUp` / `PgDn`, or run with `--no-fullscreen`.
2054
+ - Mouse tracking is deliberately left off: enabling it costs text selection and
2055
+ copy in most terminals.
2056
+ - Should a crash ever strand your terminal on the alternate screen, `reset`
2057
+ restores it. Four independent restore paths exist to prevent that: normal
2058
+ exit, `process.exit`, `SIGINT` / `SIGTERM` / `SIGHUP`, and Ink's own signal
2059
+ handling.
2060
+
2061
+ ## 0.2.0
2062
+
2063
+ A design-elevation & HCI-hardening release. No breaking changes: `@argon-agent/core`
2064
+ is untouched, no new runtime dependency is added, and the headless/print contract,
2065
+ exit codes, config keys, and slash-command semantics from 0.1.0 are unchanged.
2066
+
2067
+ ### Added
2068
+
2069
+ - **Real theming engine.** Distinct `dark` / `light` palettes with an `auto`
2070
+ resolution (`auto → dark`), color degradation across terminal depth
2071
+ (truecolor → 256 → 16 → monochrome), and an ASCII glyph fallback for terminals
2072
+ without Unicode. Honors `NO_COLOR`, `FORCE_COLOR`, `--no-color`, and
2073
+ `TERM=dumb`. Switch live with `/theme <auto|dark|light>` (persisted).
2074
+ - **Elegant chrome.** A gradient wordmark header that collapses to a slim sticky
2075
+ bar after the first turn, a first-run **Welcome / empty-state** card, and a
2076
+ status bar with a colored **context gauge** (threshold colors), a **tokens/sec**
2077
+ read-out while running, and a contextual hint.
2078
+ - **Legible tool output.** Colored unified diffs (green adds / red removes) and
2079
+ per-tool rich previews (`edit_file`/`write_file`/`read_file`/`bash`/`list_dir`/
2080
+ `glob`). Long previews collapse to 8 lines and expand with `Ctrl+O` or
2081
+ `/expand [n]`.
2082
+ - **Autocomplete.** A slash-command palette and `@file` path completion popup
2083
+ (`Tab`/`→` to complete, `Up`/`Down` to move, `Esc` to close).
2084
+ - **Full line editing.** Home/End, word jump/delete, kill-to-start/end, and
2085
+ multi-line vertical cursor navigation, with prompt-history recall at the edges.
2086
+ Unknown control/escape sequences are dropped instead of inserted.
2087
+ - **Reliability under load.** Ink `<Static>` finalization for settled entries and
2088
+ a streaming delta coalescer, so long/fast sessions stay flicker-free.
2089
+ - **Calm feedback.** Ephemeral, auto-dismissing **toasts** for transient acks;
2090
+ the transcript keeps only durable content (messages, tool cards, run errors).
2091
+ - `reducedMotion` config (also `ARGON_REDUCED_MOTION`; implied by `--no-color`)
2092
+ replaces spinners with a static glyph.
2093
+
2094
+ ### Changed
2095
+
2096
+ - History scrollback now uses the **native terminal scrollback** that `<Static>`
2097
+ prints into (this supersedes the never-implemented `PgUp`/`PgDn` promise).
2098
+
2099
+ ## 0.1.0
2100
+
2101
+ Initial release — a Claude-Code / Codex-style interactive terminal UI (TUI) on
2102
+ top of `@argon-agent/core`.
2103
+
2104
+ ### Added
2105
+
2106
+ - **Interactive TUI** (React + Ink): gradient header, streaming transcript with
2107
+ markdown + syntax highlighting, collapsible thinking blocks, live tool-call
2108
+ cards, a status bar (model / context % / tokens / cost / elapsed), and modal
2109
+ overlays (settings, model picker, help, confirm).
2110
+ - **Built-in toolset** wired into the core agent at full permission:
2111
+ `read_file`, `write_file`, `edit_file`, `list_dir`, `glob`, `grep`, `bash`.
2112
+ - **Layered configuration** (defaults → user file → env/`.env` → flags) with
2113
+ live in-TUI editing of provider / model / base URL / API keys / thinking level
2114
+ / max tokens. Secrets are masked and the config file is written `0600` on POSIX.
2115
+ - **One-shot / print mode** (`argon -p`) for scripting and piping, plus
2116
+ `argon config` / `argon config set` / `argon config path` / `argon models`.
2117
+ - **Session control**: abort (Esc), steer-while-running, follow-up queue,
2118
+ double-Ctrl-C exit, `/save` / `/resume` JSON sessions.
2119
+ - **Timeout invariant** (`idleTimeout ≥ toolTimeout`) so a long single tool run
2120
+ is never killed by the idle watchdog.
2121
+ - **Never-silent failures**: pre-flight API-key validation, error-StreamEvent
2122
+ rendering, and a guard that surfaces a swallowed agent error instead of a
2123
+ blank no-op (headless exits `1`/`2` accordingly).