@aragon-agent/cli 0.5.8 → 0.5.9

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 (123) hide show
  1. package/CHANGELOG.md +279 -0
  2. package/README.md +1429 -1163
  3. package/dist/agent/coalesce.d.ts +13 -0
  4. package/dist/agent/coalesce.js +50 -11
  5. package/dist/agent/controller.d.ts +141 -2
  6. package/dist/agent/controller.js +396 -4
  7. package/dist/agent/headless.d.ts +8 -0
  8. package/dist/agent/headless.js +40 -0
  9. package/dist/agent/reducer.d.ts +245 -3
  10. package/dist/agent/reducer.js +490 -17
  11. package/dist/agent/retry-view.d.ts +68 -0
  12. package/dist/agent/retry-view.js +95 -0
  13. package/dist/agent/system-prompt.d.ts +11 -0
  14. package/dist/agent/system-prompt.js +2 -0
  15. package/dist/cli.js +86 -2
  16. package/dist/commands/builtins.d.ts +1 -1
  17. package/dist/commands/builtins.js +308 -2
  18. package/dist/config/cli-commands.d.ts +53 -2
  19. package/dist/config/cli-commands.js +136 -3
  20. package/dist/config/env.d.ts +6 -3
  21. package/dist/config/env.js +103 -3
  22. package/dist/config/load.d.ts +49 -0
  23. package/dist/config/load.js +83 -1
  24. package/dist/config/schema.d.ts +212 -2
  25. package/dist/config/schema.js +207 -1
  26. package/dist/config/store.js +20 -1
  27. package/dist/fast/critique.d.ts +43 -0
  28. package/dist/fast/critique.js +62 -0
  29. package/dist/fast/digest.d.ts +42 -0
  30. package/dist/fast/digest.js +85 -0
  31. package/dist/fast/frames.d.ts +99 -0
  32. package/dist/fast/frames.js +180 -0
  33. package/dist/fast/limits.d.ts +98 -0
  34. package/dist/fast/limits.js +98 -0
  35. package/dist/fast/prompt.d.ts +72 -0
  36. package/dist/fast/prompt.js +94 -0
  37. package/dist/fast/resolve.d.ts +43 -0
  38. package/dist/fast/resolve.js +105 -0
  39. package/dist/fast/review-call.d.ts +99 -0
  40. package/dist/fast/review-call.js +130 -0
  41. package/dist/fast/reviewer.d.ts +248 -0
  42. package/dist/fast/reviewer.js +664 -0
  43. package/dist/fast/types.d.ts +128 -0
  44. package/dist/fast/types.js +14 -0
  45. package/dist/fast/wiring.d.ts +170 -0
  46. package/dist/fast/wiring.js +316 -0
  47. package/dist/logging/install.d.ts +15 -1
  48. package/dist/logging/install.js +37 -4
  49. package/dist/logging/logger.d.ts +1 -1
  50. package/dist/session/persist.js +57 -1
  51. package/dist/team/normalize.d.ts +26 -1
  52. package/dist/team/normalize.js +23 -3
  53. package/dist/team/prompt.d.ts +11 -0
  54. package/dist/team/prompt.js +10 -0
  55. package/dist/team/report.d.ts +18 -0
  56. package/dist/team/report.js +27 -1
  57. package/dist/team/runtime.d.ts +13 -2
  58. package/dist/team/runtime.js +33 -6
  59. package/dist/team/subagent.d.ts +19 -1
  60. package/dist/team/subagent.js +72 -10
  61. package/dist/team/task-tool.d.ts +26 -0
  62. package/dist/team/task-tool.js +48 -4
  63. package/dist/team/types.d.ts +80 -1
  64. package/dist/tools/bash-tool.js +19 -3
  65. package/dist/tools/diff.d.ts +25 -5
  66. package/dist/tools/diff.js +72 -41
  67. package/dist/tools/file-change-store.d.ts +34 -0
  68. package/dist/tools/file-change-store.js +57 -0
  69. package/dist/tools/fs-tools.d.ts +26 -0
  70. package/dist/tools/fs-tools.js +62 -5
  71. package/dist/tools/index.d.ts +31 -0
  72. package/dist/tools/index.js +7 -1
  73. package/dist/tools/patch.d.ts +115 -0
  74. package/dist/tools/patch.js +453 -0
  75. package/dist/tools/terminal-output.d.ts +107 -0
  76. package/dist/tools/terminal-output.js +198 -0
  77. package/dist/tools/tool-output-store.d.ts +68 -0
  78. package/dist/tools/tool-output-store.js +102 -0
  79. package/dist/ui/ActivityLine.d.ts +48 -0
  80. package/dist/ui/ActivityLine.js +22 -0
  81. package/dist/ui/App.d.ts +1 -4
  82. package/dist/ui/App.js +273 -37
  83. package/dist/ui/BottomStatusRow.d.ts +43 -0
  84. package/dist/ui/BottomStatusRow.js +14 -0
  85. package/dist/ui/Composer.d.ts +1 -6
  86. package/dist/ui/Composer.js +2 -2
  87. package/dist/ui/PromptInput.d.ts +1 -15
  88. package/dist/ui/PromptInput.js +1 -38
  89. package/dist/ui/StatusBar.d.ts +37 -0
  90. package/dist/ui/StatusBar.js +5 -3
  91. package/dist/ui/TeamPanel.d.ts +5 -4
  92. package/dist/ui/TeamPanel.js +1 -1
  93. package/dist/ui/TodoPanel.d.ts +5 -4
  94. package/dist/ui/Transcript.d.ts +26 -3
  95. package/dist/ui/Transcript.js +76 -10
  96. package/dist/ui/activity-phrases.d.ts +33 -0
  97. package/dist/ui/activity-phrases.js +50 -0
  98. package/dist/ui/entries/AssistantEntry.d.ts +20 -0
  99. package/dist/ui/entries/AssistantEntry.js +3 -2
  100. package/dist/ui/entries/DiffView.d.ts +67 -0
  101. package/dist/ui/entries/DiffView.js +103 -0
  102. package/dist/ui/entries/FastCard.d.ts +53 -0
  103. package/dist/ui/entries/FastCard.js +87 -0
  104. package/dist/ui/entries/RetryCard.d.ts +56 -0
  105. package/dist/ui/entries/RetryCard.js +60 -0
  106. package/dist/ui/entries/TeamCard.js +6 -1
  107. package/dist/ui/entries/ToolCard.d.ts +38 -1
  108. package/dist/ui/entries/ToolCard.js +68 -5
  109. package/dist/ui/glyphs.d.ts +9 -0
  110. package/dist/ui/glyphs.js +2 -0
  111. package/dist/ui/layout/AppShell.d.ts +13 -22
  112. package/dist/ui/layout/AppShell.js +3 -38
  113. package/dist/ui/layout/virtual-window.d.ts +1 -1
  114. package/dist/ui/layout/virtual-window.js +109 -5
  115. package/dist/ui/overlays/HelpOverlay.js +16 -2
  116. package/dist/ui/overlays/SettingsScreen.d.ts +82 -1
  117. package/dist/ui/overlays/SettingsScreen.js +147 -1
  118. package/dist/ui/transcript-text.js +27 -0
  119. package/dist/ui/use-wheel-routing.d.ts +16 -32
  120. package/dist/ui/use-wheel-routing.js +21 -38
  121. package/package.json +2 -2
  122. package/dist/ui/layout/regions.d.ts +0 -55
  123. package/dist/ui/layout/regions.js +0 -52
package/CHANGELOG.md CHANGED
@@ -7,8 +7,147 @@ were written.
7
7
 
8
8
  ## Unreleased
9
9
 
10
+ ### Added
11
+
12
+ - **A running command shows what it is doing.** `bash` is the only tool here that
13
+ can run for minutes — `npm test`, a `git clone`, a training job — and while it
14
+ ran, its card was one line of text that never changed. Nothing in that frame
15
+ distinguished a test suite at 40% from a child process blocked forever on a
16
+ password prompt this CLI will never show. Its card now carries the last eight
17
+ lines of what the command is printing, plus one footer row that reads
18
+ `(running)` or, once the child has been quiet for ten seconds,
19
+ `no output for 45s` — with the seconds advancing.
20
+
21
+ A `\r`-driven progress bar (npm, pip, curl, docker) is *one* line rewritten a
22
+ thousand times, and it renders as **one row counting up in place** rather than
23
+ a thousand rows of nearly the same string — including the ordinary case where
24
+ the bar emits no newline at all for its entire run.
25
+
26
+ Everything the tail shows is **stripped, not interpreted**: ANSI colour, cursor
27
+ motion, `\r`, `\b`, tabs and every other control byte are removed at the
28
+ boundary before a row is stored, so nothing can move a cursor inside a frame
29
+ the renderer believes it owns. It is bounded twice by construction — eight rows
30
+ per call, sixteen concurrent calls — so a command that writes 400 MB to stdout
31
+ costs a fixed number of retained rows.
32
+
33
+ It is a **display and nothing else**. The text the model receives is
34
+ byte-for-byte what it was; `Ctrl+O` does nothing while a command runs, because
35
+ expansion is a promise about stored content and there is no stored rest of a
36
+ 400 MB stream; `/save` during a run writes no tail, since a resumed one would
37
+ describe a process that died with the session; and at settle the card is
38
+ replaced by the authoritative result exactly as before.
39
+
40
+ New config key `liveToolOutput`, defaulting to **`true`** — unlike
41
+ `showThinking` below, this adds information rather than hiding it.
42
+ `aragon config set liveToolOutput false` (or `--no-live-tool-output`, or
43
+ `ARAGON_LIVE_TOOL_OUTPUT=0`, or the **Live output** row in `/settings`) turns it
44
+ off, and with it off no store is allocated and no recorder is attached: `bash`
45
+ is byte-identical to the build before this entry.
46
+
47
+ - **The working line stops guessing while a tool runs.** It reads
48
+ `⠙ Running bash` instead of rotating thinking-words. `running` covers the whole
49
+ turn, including the minutes the model is idle and a child process is doing the
50
+ work, so "Pondering" during a build was not calm — it was wrong. One row, no
51
+ digits, no clock: everything the row already promised still holds.
52
+
53
+ ### Changed
54
+
55
+ - **Reasoning blocks are now hidden by default.** `showThinking` is a new
56
+ top-level config key and it defaults to `false`. If you have been reading
57
+ thinking every day, this is the entry that concerns you: `Ctrl+T` still shows
58
+ it for the session exactly as before, `aragon config set showThinking true`
59
+ (or `--show-thinking`, or `ARAGON_SHOW_THINKING=1`) restores the old behaviour
60
+ permanently, and `/settings` has a **Show thinking** row directly under
61
+ **Thinking**.
62
+
63
+ Nothing is hidden silently. A settled turn that thought leaves one muted row
64
+ where the block used to be — `✱ thought for 12s · ctrl+t to show` — so the fact
65
+ that reasoning happened, how long it took, and how to see it are all still on
66
+ screen. In **inline** mode that row omits the key hint and the toast reads
67
+ `Thinking shown for new output.`, because a settled entry there has already
68
+ been printed into your terminal's scrollback and cannot be repainted; the
69
+ toggle applies to the live entry and to everything drawn after it.
70
+
71
+ `thinkingLevel` is untouched and means what it always did — the effort the
72
+ provider is asked to spend. `showThinking` is only whether the terminal draws
73
+ what came back. They are one word apart in the config file, which is why the
74
+ flag is `--show-thinking` rather than the shorter name.
75
+
76
+ ### Added
77
+
78
+ - **A live working line while a run is in flight.** One row above the composer,
79
+ carrying a spinner and a word that rotates every four seconds:
80
+ `⠋ Percolating…`. Take reasoning off the screen and the gap between submitting
81
+ and the first token is otherwise completely blank, which reads as dead rather
82
+ than as busy — the two changes are one change.
83
+
84
+ It carries no clock and no token count, deliberately: the status bar one row
85
+ below already renders both, under the same running condition and driven by the
86
+ same 200 ms ticker this row reuses. Under `reducedMotion` the spinner is a
87
+ static glyph and the word is chosen once and holds, because rotating text is
88
+ motion too. An idle frame is unchanged, and so is the viewport's row budget —
89
+ the line shares the row the toast strip already owned rather than adding one.
90
+
91
+ - **`write_file` and `edit_file` render a real diff.** Hunks with old/new line
92
+ numbers, a `+N -M` summary, add/remove/context colouring, and `(truncated)` or
93
+ a `binary` / `too-large` / `unreadable` note when the old side could not be
94
+ read. Diffs collapse at 12 rows behind the existing `Ctrl+O` — no new
95
+ keybinding. A resumed session written by an older build, or a card whose patch
96
+ the bounded store already evicted, falls back to the previous text preview;
97
+ nothing renders blank.
98
+
99
+ `write_file` previously reported creating a 300-line file as
100
+ `Wrote 8214 bytes to path`, and `edit_file`'s diff trimmed the common prefix
101
+ and suffix and rendered *everything* in between as one removed block followed
102
+ by one added block — for a `replace_all` touching line 40 and line 900, that
103
+ was 860 removed rows followed by 860 nearly identical added ones.
104
+
105
+ **The model's input got smaller, not larger.** The structured patch reaches the
106
+ screen on a CLI-local channel rather than through the tool result, and the text
107
+ the model reads gained a 4 000-character ceiling it did not have before (its
108
+ only previous bound was the executor's 100 KB backstop). A single-region edit
109
+ is byte-for-byte what it was plus one `@@` header; a multi-region edit is
110
+ strictly smaller. `write_file`'s result text is unchanged.
111
+
10
112
  ### Changed
11
113
 
114
+ - **A stream that stops mid-answer is now a loud failure instead of a silent
115
+ truncated one.** All three provider adapters used to end their SSE loop by
116
+ yielding "done" with whatever had arrived, so a connection closed cleanly
117
+ half-way through an answer — a proxy idle cut, a load-balancer timeout, an
118
+ HTTP/2 GOAWAY — was indistinguishable from a completed reply. You got a
119
+ truncated answer and were told nothing.
120
+
121
+ Each adapter now reports the truncation, which is what lets it be retried and
122
+ recovered. An **abort** is explicitly excluded and still completes normally, and
123
+ a model that legitimately says nothing and stops is still a success.
124
+
125
+ This is deliberately **not** behind `--no-retry`: an adapter that reports a
126
+ truncated stream honestly should do so whether or not retry is enabled. A switch
127
+ that also disabled telling you the truth would be a mute button, not a kill
128
+ switch.
129
+
130
+ - **The mouse wheel now only ever scrolls.** A notch scrolls the transcript
131
+ three rows (a page with `Shift`), or the open overlay when there is one, no
132
+ matter where the pointer is sitting. Prompt history is recalled by `↑` / `↓`
133
+ alone, exactly as in Claude Code and Codex.
134
+
135
+ **This supersedes the 0.5.0 entry below**, which said the wheel "scrolls what
136
+ is under the pointer" and stepped prompt history one entry per notch when the
137
+ pointer was over the input box. That turned out to be wrong in use rather than
138
+ in principle: the pointer is not an instrument you aim in a TUI — it rests
139
+ wherever you last left it, which in a chat-shaped app is next to the thing you
140
+ type into. So the ordinary "scroll back and see what just happened" gesture
141
+ landed on the composer and replaced the draft with an old prompt, with no
142
+ error, no undo and nothing visibly connecting the gesture to the outcome. The
143
+ band that decided this was invisible too: its boundary was a measured row that
144
+ moved as the toast row, the hint row and the completion popup came and went, so
145
+ the same gesture could do two different things a second apart.
146
+
147
+ Nothing else about the wheel changes — same three rows per notch, same
148
+ `Shift`+wheel page, same `--no-mouse` / `ARAGON_MOUSE=0` / `mouse: false`
149
+ escape hatches, same `Shift`+drag for selecting text.
150
+
12
151
  - **A long session stays as fast as a short one.** The TUI used to get slower as
13
152
  a session grew and, past a threshold, stopped making progress at all. That was
14
153
  structural rather than incidental: Ink has no output caching, so every frame
@@ -68,6 +207,146 @@ were written.
68
207
 
69
208
  ### Added
70
209
 
210
+ - **A fast model tier: a second, cheaper model you can delegate to and be
211
+ reviewed by.** Off by default — with `fast.enabled: false` the system prompt,
212
+ the `task` schema, the transcript and the request payloads are byte-identical
213
+ to a build without it — and it does two things once you turn it on.
214
+
215
+ **Delegation.** `task` gains one optional field per subagent, `model: "fast"`.
216
+ A child marked that way runs on the fast model and is otherwise identical:
217
+ same tools, same working directory, same `--confirm` gate, same plan-mode
218
+ gate, same skills ceiling. Delegating to a cheaper model is a *model* choice,
219
+ never a permission boundary. It is where "not complicated, but very expensive
220
+ in context" gets paid for — a child that reads nine files and reports four
221
+ sentences moves the file bodies out of the lead's window and onto a model that
222
+ costs a fraction as much per token.
223
+
224
+ **Periodic review.** Every few turns a small digest of what the lead has been
225
+ doing goes to the fast model with one question — is this still on track? — and
226
+ a critique that is not "on track" is injected into the running loop as a
227
+ `<fast_review>` block, so the lead sees a second opinion while it can still
228
+ act on it:
229
+
230
+ ```
231
+ fast review #2 · claude-haiku-4-5 · turn 12 · 1.4s
232
+ You have edited config/schema.ts three times without running the tests.
233
+ ```
234
+
235
+ The digest is built from a bounded ring of recent turns and **never** from the
236
+ message history: sending a session's accumulated file bodies to the "cheap"
237
+ model would make a review cost more than the turn it reviews, which is the
238
+ inversion the whole feature exists to prevent.
239
+
240
+ Spend is attributed **per tier** — a fast child's tokens are priced at the
241
+ fast model's table, not the lead's, and the dispatch report states the two
242
+ separately. A model the built-in price table has never seen is reported as
243
+ `unknown` rather than as `$0.00`, because a feature that looks free while it
244
+ is spending money is worse than one that admits it does not know.
245
+
246
+ Turn it on with `/fast model <id>` then `/fast on`, with
247
+ `aragon config set fast.enabled true`, with `--fast --fast-model <id>`, or with
248
+ `ARAGON_FAST=1 ARAGON_FAST_MODEL=<id>`. `/fast status` is the guaranteed
249
+ readout: the resolved model, the budget, and this session's reviews, delegated
250
+ children, tokens and cost. Eleven `fast.*` config keys, four flags, four
251
+ environment variables and five settings-screen rows; see the README.
252
+
253
+ **The review has a session budget.** A session is not one run — it is one run
254
+ per message you send — so a cadence bounds the pace and nothing bounds the
255
+ total. `fast.reviewMaxPerSession` (default **40**, raisable live with
256
+ `/fast budget <n>`) is the number of reviews a session may *start*; reaching it
257
+ prints one `info` notice that names the remedy and says delegation is
258
+ unaffected, and `/fast status` reads `12/40` from the first review rather than a
259
+ bare count you can only size after the fact. It is denominated in **reviews,
260
+ not currency**, because the fast tier is exactly where a model the price table
261
+ has never seen lives — a dollar ceiling would silently never fire for the
262
+ configuration that most needs one. Delegation is never budgeted: it is spend the
263
+ lead chose in service of a message you sent, it is already bounded by `team.*`,
264
+ and it exists to *reduce* total cost.
265
+
266
+ **The review also fails fast, so it cannot compete with you.** It calls through
267
+ its own provider registry with **one** retry rather than the ten
268
+ `retry.maxRetries` defaults to for the lead, and it observes neither `retry.*`
269
+ nor `/retry` — that policy says how hard to fight for *your* answer, and
270
+ inheriting it for an unrequested background call is how an advisory ends up
271
+ spending a rate-limited provider's quota on itself. Its own wall-clock bound
272
+ is 20 s.
273
+
274
+ Two failure modes are handled out loud rather than silently. If the tier stops
275
+ resolving mid-session — you switch the main provider and `fast.provider` was
276
+ inheriting it — `model:"fast"` is downgraded to the main model, **counted, and
277
+ stated in the report**, because an agent that silently gets an expensive child
278
+ has no way to learn its cost model is wrong. And three consecutive
279
+ *misconfiguration* failures disable the reviewer for the session with exactly
280
+ one warning, while a merely busy provider (rate-limited or overloaded) is
281
+ treated as transient and never counts — that condition fixes itself, and a
282
+ session-long silent shutdown over it would not. A review you *cancelled* is
283
+ neither: Esc, a run that ends first, or quitting leaves a `cancelled` card and
284
+ no strike against the tier.
285
+
286
+ Reviews log under their own `fast` scope rather than under `agent`, so
287
+ `fast_review_*` records filter cleanly away from the lead's.
288
+
289
+ - **API calls are retried with backoff, and the waiting is legible.** A transient
290
+ provider failure — HTTP 429, 529, 5xx, 408, or a dropped connection — used to end
291
+ the turn. One red notice, an idle prompt, and forty tool calls of work thrown
292
+ away. It is now retried up to **10 times** with an increasing interval
293
+ (1 s → 2 → 4 → 8 → 16 → 30 s, equal-jittered, 30 s per wait at most) under a
294
+ 4-minute budget measured from the first failure.
295
+
296
+ What you see while it waits is **one line, rewritten in place** — never ten
297
+ notices — carrying what failed, which attempt this is, and a countdown to the
298
+ next one:
299
+
300
+ ```
301
+ ↻ Provider overloaded · retry 3/10 in 7s Esc to cancel
302
+ ↻ recovered after 3 retries · 12.4s
303
+ ```
304
+
305
+ plus a `retry 3/10 7s` chip on the status bar (`[r3]` on a narrow terminal). The
306
+ card settles into the transcript as history — `recovered`, `gave up`, or
307
+ `interrupted` — and shows up in the exit replay, because a turn that spent three
308
+ minutes retrying is exactly the kind of cost an exported transcript should
309
+ account for.
310
+
311
+ **Esc ends the wait within a frame**, not at the end of the countdown, and it
312
+ never produces a fabricated error: an aborted request is silent. That is the one
313
+ detail everything else here depends on, because the provider layer marks an
314
+ `AbortError` as retryable — so the signal is checked *before* the error, and a
315
+ user pressing Esc can never be mistaken for a timeout worth retrying.
316
+
317
+ Never retried: `auth_error` (401/403), `invalid_request`, `context_overflow`.
318
+ Ten retries of a bad API key is three minutes of looking broken.
319
+
320
+ `Retry-After` is honoured as a **floor**, never a replacement — a `Retry-After: 1`
321
+ on the ninth retry does not undo eight retries of backoff — and a stated wait
322
+ longer than a minute is surfaced immediately with the provider's own number
323
+ rather than silently wedging the terminal.
324
+
325
+ A connection that dies **mid-answer** is also covered, and this is the case that
326
+ needed more than a retry loop: the partial text is discarded, the affected tool
327
+ cards are removed, and the answer restarts from the top. Nothing is duplicated,
328
+ because the engine only writes history on a completed turn.
329
+
330
+ - **`/retry [show|on|off|max <n>]`, `--retry` / `--no-retry`, `--retry-max <n>`,
331
+ `ARAGON_RETRY`, `ARAGON_RETRY_MAX`, and a `retry` config section** with nine
332
+ keys (`aragon config set retry.*`). `/retry off` takes effect **in the running
333
+ session** — subagents included, since they share the lead's provider registry —
334
+ not only at the next launch. `retry.maxRetries: 0` is the kill switch and is a
335
+ real value at every one of those entry points. `/settings` gains one read-only
336
+ `API retries` row.
337
+
338
+ - **A subagent's retry is visible on the team card.** A child in backoff reports
339
+ `retry 3/10` on its own row. Children inherit the retry policy for free, but
340
+ their events never reach the lead's view, so without this a subagent spending
341
+ three minutes waiting looked exactly like a hung dispatch.
342
+
343
+ - **Retry lines in `-p` mode go to stderr, never stdout.** `[retry 3/10]
344
+ overloaded — waiting 7s` and `[retry] recovered after 3 retries` are on stderr,
345
+ so `aragon -p … | jq` stays valid; a discarded partial answer is announced there
346
+ too, because a piped consumer otherwise cannot tell a replay from a model that
347
+ repeated itself. They are not suppressed by `--quiet`: a three-minute pause with
348
+ no explanation is not a compact run, it is a hang.
349
+
71
350
  - **`/perf`.** Reports what the renderer is actually doing: the frame interval
72
351
  and the last commit cost, how many entries exist / are retained / were dropped
73
352
  / are mounted, height-cache and render-cache occupancy, and the viewport