@aragon-agent/cli 0.6.1 → 0.6.3
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.
- package/CHANGELOG.md +216 -0
- package/README.md +241 -18
- package/dist/agent/agent-mode.d.ts +39 -0
- package/dist/agent/agent-mode.js +36 -0
- package/dist/agent/controller.d.ts +74 -1
- package/dist/agent/controller.js +200 -1
- package/dist/agent/headless.d.ts +8 -0
- package/dist/agent/headless.js +47 -0
- package/dist/agent/reducer.d.ts +120 -0
- package/dist/agent/reducer.js +201 -2
- package/dist/cli.js +297 -6
- package/dist/commands/builtins.js +73 -23
- package/dist/commands/registry.d.ts +27 -0
- package/dist/compaction/archive.d.ts +175 -0
- package/dist/compaction/archive.js +267 -0
- package/dist/compaction/child.d.ts +97 -0
- package/dist/compaction/child.js +113 -0
- package/dist/compaction/command.d.ts +41 -0
- package/dist/compaction/command.js +342 -0
- package/dist/compaction/compactor.d.ts +268 -0
- package/dist/compaction/compactor.js +689 -0
- package/dist/compaction/digest.d.ts +62 -0
- package/dist/compaction/digest.js +164 -0
- package/dist/compaction/limits.d.ts +165 -0
- package/dist/compaction/limits.js +172 -0
- package/dist/compaction/pressure.d.ts +135 -0
- package/dist/compaction/pressure.js +167 -0
- package/dist/compaction/summarize-call.d.ts +70 -0
- package/dist/compaction/summarize-call.js +86 -0
- package/dist/compaction/summary-prompt.d.ts +85 -0
- package/dist/compaction/summary-prompt.js +197 -0
- package/dist/compaction/tail-budget.d.ts +52 -0
- package/dist/compaction/tail-budget.js +67 -0
- package/dist/compaction/types.d.ts +179 -0
- package/dist/compaction/types.js +22 -0
- package/dist/compaction/wiring.d.ts +255 -0
- package/dist/compaction/wiring.js +556 -0
- package/dist/config/app-paths.d.ts +14 -0
- package/dist/config/app-paths.js +16 -0
- package/dist/config/cli-commands.d.ts +41 -0
- package/dist/config/cli-commands.js +77 -1
- package/dist/config/env.d.ts +4 -1
- package/dist/config/env.js +92 -2
- package/dist/config/load.d.ts +31 -0
- package/dist/config/load.js +63 -1
- package/dist/config/schema.d.ts +159 -0
- package/dist/config/schema.js +137 -0
- package/dist/config/store.js +17 -1
- package/dist/config/ui-state.d.ts +44 -6
- package/dist/config/ui-state.js +21 -12
- package/dist/diagnostics/doctor.js +41 -1
- package/dist/exec/events.d.ts +50 -1
- package/dist/exec/index.js +8 -4
- package/dist/exec/runner.js +44 -2
- package/dist/input/mouse-events.d.ts +31 -5
- package/dist/input/mouse-events.js +86 -27
- package/dist/input/stdin-mouse-filter.d.ts +8 -3
- package/dist/logging/install.d.ts +51 -1
- package/dist/logging/install.js +28 -0
- package/dist/logging/logger.d.ts +1 -1
- package/dist/session/cli-commands.js +23 -1
- package/dist/session/persist.js +19 -0
- package/dist/session/store.d.ts +18 -0
- package/dist/session/store.js +59 -2
- package/dist/team/report.js +7 -1
- package/dist/team/runtime.d.ts +10 -0
- package/dist/team/runtime.js +7 -0
- package/dist/team/subagent.d.ts +31 -1
- package/dist/team/subagent.js +42 -15
- package/dist/team/types.d.ts +8 -0
- package/dist/ui/ActivityLine.d.ts +19 -1
- package/dist/ui/ActivityLine.js +10 -2
- package/dist/ui/App.d.ts +48 -1
- package/dist/ui/App.js +317 -17
- package/dist/ui/Composer.d.ts +26 -1
- package/dist/ui/Composer.js +6 -6
- package/dist/ui/PromptInput.d.ts +34 -1
- package/dist/ui/PromptInput.js +54 -3
- package/dist/ui/StatusBar.d.ts +40 -0
- package/dist/ui/StatusBar.js +13 -4
- package/dist/ui/TeamPanel.js +7 -1
- package/dist/ui/Transcript.d.ts +21 -1
- package/dist/ui/Transcript.js +32 -2
- package/dist/ui/clipboard.d.ts +52 -0
- package/dist/ui/clipboard.js +91 -0
- package/dist/ui/entries/CompactionCard.d.ts +75 -0
- package/dist/ui/entries/CompactionCard.js +158 -0
- package/dist/ui/frame-differ.d.ts +46 -0
- package/dist/ui/frame-differ.js +94 -10
- package/dist/ui/gauge.d.ts +16 -1
- package/dist/ui/gauge.js +25 -6
- package/dist/ui/glyphs.d.ts +11 -0
- package/dist/ui/glyphs.js +2 -0
- package/dist/ui/layout/ScrollViewport.d.ts +43 -6
- package/dist/ui/layout/ScrollViewport.js +128 -21
- package/dist/ui/layout/budget.d.ts +10 -3
- package/dist/ui/layout/budget.js +10 -3
- package/dist/ui/layout/follow-state.d.ts +137 -0
- package/dist/ui/layout/follow-state.js +150 -0
- package/dist/ui/layout/virtual-window.d.ts +11 -0
- package/dist/ui/layout/virtual-window.js +34 -0
- package/dist/ui/overlays/HelpOverlay.js +18 -2
- package/dist/ui/overlays/SettingsScreen.d.ts +47 -1
- package/dist/ui/overlays/SettingsScreen.js +60 -1
- package/dist/ui/palettes.d.ts +16 -0
- package/dist/ui/palettes.js +6 -0
- package/dist/ui/screen.d.ts +22 -0
- package/dist/ui/screen.js +70 -8
- package/dist/ui/selection/highlight.d.ts +39 -0
- package/dist/ui/selection/highlight.js +64 -0
- package/dist/ui/selection/screen-mirror.d.ts +24 -0
- package/dist/ui/selection/screen-mirror.js +35 -0
- package/dist/ui/selection/selection-controller.d.ts +88 -0
- package/dist/ui/selection/selection-controller.js +282 -0
- package/dist/ui/selection/selection.d.ts +90 -0
- package/dist/ui/selection/selection.js +223 -0
- package/dist/ui/stdout-frame-writer.d.ts +33 -0
- package/dist/ui/stdout-frame-writer.js +15 -0
- package/dist/ui/theme.d.ts +11 -0
- package/dist/ui/theme.js +2 -0
- package/dist/ui/transcript-text.js +23 -0
- package/dist/ui/use-startup-notices.d.ts +62 -5
- package/dist/ui/use-startup-notices.js +81 -17
- package/dist/ui/use-wheel-routing.js +8 -0
- package/dist/ui/win-vt-force.d.ts +136 -0
- package/dist/ui/win-vt-force.js +237 -0
- package/package.json +4 -2
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,154 @@ All notable changes to `@aragon-agent/cli` are documented here. Entries at
|
|
|
5
5
|
— those releases really were published under that name, so they are left as they
|
|
6
6
|
were written.
|
|
7
7
|
|
|
8
|
+
## Unreleased
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- **Context compaction, hardened — the trigger now counts the whole history, the
|
|
13
|
+
recent turns can no longer wedge a run, sub-agents get the same protection, and
|
|
14
|
+
a compaction is no longer irreversible.**
|
|
15
|
+
|
|
16
|
+
**The trigger was systematically late.** It fired on the provider's reported
|
|
17
|
+
usage for the PREVIOUS request, read at a checkpoint that runs AFTER the current
|
|
18
|
+
turn's tool results were appended — and tool results are the largest single
|
|
19
|
+
increment in this loop (100 KB per call, several per turn, ~25 k tokens each).
|
|
20
|
+
A session reading 88 % could be sending 110 %. Occupancy is now the measured
|
|
21
|
+
base PLUS an estimate of everything appended since that measurement, and the
|
|
22
|
+
status gauge reads the same number the trigger does, on every turn — including
|
|
23
|
+
the turns where a guard declines to compact, which is exactly when the number
|
|
24
|
+
matters most.
|
|
25
|
+
|
|
26
|
+
**The recent turns can no longer wedge a run.** Every previous rung of the
|
|
27
|
+
ladder operated on the head of the conversation. When the RETAINED tail was what
|
|
28
|
+
did not fit, each rung reported success or "nothing to drop" while the history
|
|
29
|
+
stayed un-sendable and the run died. Oversized `tool_result` bodies inside the
|
|
30
|
+
retained turns are now clipped, oldest first, only when the alternative is a
|
|
31
|
+
request the provider cannot accept. Nothing is removed — no message, no
|
|
32
|
+
tool-call id, no role — so a valid history stays valid. Every clip is announced
|
|
33
|
+
in the text the model reads, at the exact place the data went, and on the card,
|
|
34
|
+
in `/compact status`, on the JSON stream and in the archive.
|
|
35
|
+
|
|
36
|
+
**Sub-agents compact their own history** (`compaction.subagents`, default
|
|
37
|
+
`true`). A `task` child is bounded by 24 turns and fifteen minutes, and when one
|
|
38
|
+
overflowed its entire history was discarded and the dispatch reached the lead as
|
|
39
|
+
one partial sentence. Children run under tighter bounds than the lead: two
|
|
40
|
+
retained turns, at most two compactions, the fast tier preferred, and failure
|
|
41
|
+
forced to truncate because nobody is watching a background worker. No transcript
|
|
42
|
+
card — the lead's transcript describes the lead's context. Set
|
|
43
|
+
`compaction.subagents: false` to restore the previous behaviour exactly.
|
|
44
|
+
|
|
45
|
+
**A compaction is no longer irreversible** (`compaction.archive`, default
|
|
46
|
+
`true`). The dropped messages are written verbatim to `~/.aragon/compaction/`
|
|
47
|
+
before the splice is adopted. `/compact history` lists this run's archives,
|
|
48
|
+
`/compact show <n>` prints one's metadata and summary — never message bodies.
|
|
49
|
+
The directory is shared by every `aragon` on the machine, so files carry a
|
|
50
|
+
per-process run id and retention is two-tier: 20 files per run by count, plus a
|
|
51
|
+
seven-day sweep across all runs, so one session can never prune a live
|
|
52
|
+
neighbour's work. Writing is best-effort: a failure is logged and never affects
|
|
53
|
+
the compaction. This is deliberately **not** undo — restoring an over-full
|
|
54
|
+
history restores the condition that triggered the compaction.
|
|
55
|
+
|
|
56
|
+
**The long moment is legible.** The live card counts elapsed seconds and says
|
|
57
|
+
`esc to cancel`, which `AgentController.abort()` has always made true and no
|
|
58
|
+
surface has ever stated. A compaction that could only clip says so and names no
|
|
59
|
+
model, because none was called.
|
|
60
|
+
|
|
61
|
+
Two new config keys (`compaction.subagents`, `compaction.archive`), two new
|
|
62
|
+
environment variables (`ARAGON_COMPACTION_SUBAGENTS`,
|
|
63
|
+
`ARAGON_COMPACTION_ARCHIVE`), two new `/compact` verbs (`history`, `show <n>`),
|
|
64
|
+
and two new settings-screen rows. No flags were added.
|
|
65
|
+
|
|
66
|
+
- **Context compaction — a long run no longer dies when the window fills, and it
|
|
67
|
+
is ON by default.** A session's conversation only grows: nothing anywhere
|
|
68
|
+
removed a message, so a 40-turn tool-heavy run had exactly one ending available
|
|
69
|
+
to it once the context window filled — an HTTP 400 and a banner reading
|
|
70
|
+
*"Context length exceeded — start a new conversation with /reset."* The
|
|
71
|
+
product's answer to running out of context was "throw away everything you were
|
|
72
|
+
doing."
|
|
73
|
+
|
|
74
|
+
Now, at **90 %** occupancy, the run pauses at a turn boundary, hands the older
|
|
75
|
+
part of the history to a summarizer, and swaps it for a structured record. Your
|
|
76
|
+
original task stays **verbatim**, the most recent turns stay **verbatim**, and
|
|
77
|
+
the run continues in the same loop with the same tools, the same todo list and
|
|
78
|
+
the same working directory.
|
|
79
|
+
|
|
80
|
+
**This changes behaviour for existing users, in the direction of not dying.**
|
|
81
|
+
`--no-compaction`, or `compaction.enabled: false`, restores the old behaviour
|
|
82
|
+
exactly: no manager is constructed, nothing is subscribed, no registry is
|
|
83
|
+
allocated, and a full window ends the run as it did before. Both flag forms are
|
|
84
|
+
declared, so a stored `false` survives a run that passes no flags.
|
|
85
|
+
|
|
86
|
+
Three ways it fires. **Proactively** on measured occupancy — the case that
|
|
87
|
+
happens almost every time. **Reactively** when the provider returns
|
|
88
|
+
`context_overflow` anyway, because the model is not in the static window table
|
|
89
|
+
or you are pointed at a proxy in front of a smaller one: one compaction, one
|
|
90
|
+
re-send, run continues. And **manually**, via `/compact`, which runs
|
|
91
|
+
immediately when idle and queues to the next turn boundary when running.
|
|
92
|
+
|
|
93
|
+
The trigger has two terms. 90 % of a 200 k window leaves 20 k, which is plenty;
|
|
94
|
+
90 % of a 32 k window leaves 3.2 k, which is less than a single `max_tokens` of
|
|
95
|
+
8192 — the request is already impossible. So compaction also fires whenever the
|
|
96
|
+
remaining headroom drops below one full response.
|
|
97
|
+
|
|
98
|
+
**The default summarizer is your own model, and that costs real money.**
|
|
99
|
+
`compaction.useFastTier` is `true`, but it is a *preference*: the fast tier is
|
|
100
|
+
off unless you configured it, so out of the box the first compaction is a
|
|
101
|
+
~30 k-token call on the session's own model. It happens at most 5 times per
|
|
102
|
+
run, the alternative is losing the run entirely, and the spend is visible — in
|
|
103
|
+
the session total, priced with the summarizer's own table, and reported as
|
|
104
|
+
`pricing unknown` rather than `$0.00` when that table has never seen the model.
|
|
105
|
+
Configure the fast tier to make it cheap.
|
|
106
|
+
|
|
107
|
+
Summarization fails sometimes. The ladder is one retry (on your **main** model
|
|
108
|
+
if the first attempt used the fast tier), then a **truncation** that says so on
|
|
109
|
+
the card, on the JSON event stream, and inside the block the model itself
|
|
110
|
+
reads. `compaction.onFailure: "stop"` makes the other trade.
|
|
111
|
+
|
|
112
|
+
Four guards stop it looping on your money: never two on consecutive turns,
|
|
113
|
+
never more than 5 per run, a progress requirement, and a self-disable after two
|
|
114
|
+
no-progress attempts with a notice naming the cause and the fix. The guards are
|
|
115
|
+
**trigger-aware** — the reactive path stays armed even after a self-disable,
|
|
116
|
+
because a compaction answering a request the provider has already refused is
|
|
117
|
+
the difference between a live run and a dead one.
|
|
118
|
+
|
|
119
|
+
Three UI surfaces: `Compacting context…` on the activity row while it runs, a
|
|
120
|
+
`compacting` chip plus threshold-aligned gauge colours as state, and a
|
|
121
|
+
transcript card with the before/after counts, the summarizer, the duration and
|
|
122
|
+
an expandable summary as the record. The gauge falls **immediately** rather
|
|
123
|
+
than waiting for the next turn. `/compact status` is the guaranteed surface at
|
|
124
|
+
any width.
|
|
125
|
+
|
|
126
|
+
New: `compaction` config section (6 keys), `--compaction` / `--no-compaction` /
|
|
127
|
+
`--compaction-threshold`, `ARAGON_COMPACTION*` (3 vars), `/compact` (6 forms),
|
|
128
|
+
three settings-screen rows, a `compaction` event on the `aragon exec` JSON
|
|
129
|
+
stream (`EXEC_SCHEMA_VERSION` **unchanged** — adding an event type never bumps
|
|
130
|
+
it), and two stderr lines under `-p`.
|
|
131
|
+
|
|
132
|
+
### Changed
|
|
133
|
+
|
|
134
|
+
- **The context gauge now counts cache tokens**, through the same
|
|
135
|
+
`occupiedTokens` the compaction trigger reads. Anthropic reports `input_tokens`
|
|
136
|
+
*excluding* cached tokens and puts them in two separate fields, so the old
|
|
137
|
+
`inputTokens + outputTokens` silently under-reported occupancy for anyone
|
|
138
|
+
behind a caching gateway. That was already a defect in the number on the status
|
|
139
|
+
bar; it becomes a much worse one the moment a trigger fires on a different
|
|
140
|
+
number from the one you are watching.
|
|
141
|
+
|
|
142
|
+
- **`context_overflow` now names `/compact` alongside `/reset`.** That remedy
|
|
143
|
+
list was complete until this release, because throwing the conversation away
|
|
144
|
+
really was the only exit.
|
|
145
|
+
|
|
146
|
+
### Known limitation
|
|
147
|
+
|
|
148
|
+
- **A summarizer that is 90 % faithful will, eventually, drop something the run
|
|
149
|
+
needed,** and no amount of prompt engineering closes that. The design bounds it
|
|
150
|
+
— verbatim task, verbatim recent turns, a block that tells the model the record
|
|
151
|
+
is partial, and three surfaces that make it visible so a human can step in —
|
|
152
|
+
but it does not eliminate it. That is inherent to compaction as a technique, it
|
|
153
|
+
is the same trade Claude Code and Codex make, and it is preferable by a wide
|
|
154
|
+
margin to the previous behaviour, which was to lose the entire run.
|
|
155
|
+
|
|
8
156
|
## 0.6.0
|
|
9
157
|
|
|
10
158
|
### Added
|
|
@@ -84,6 +232,74 @@ were written.
|
|
|
84
232
|
|
|
85
233
|
## Unreleased
|
|
86
234
|
|
|
235
|
+
### Added
|
|
236
|
+
|
|
237
|
+
- **Drag the mouse to select text, and release to copy it.** Full-screen mode
|
|
238
|
+
takes the alternate screen, turns on mouse reporting so the wheel can scroll a
|
|
239
|
+
transcript the terminal no longer knows how to scroll, and draws every row
|
|
240
|
+
itself. Each of those is right on its own; together they took your terminal's
|
|
241
|
+
own drag-to-select away and put nothing in its place. The only thing shipped
|
|
242
|
+
in its place was a one-line notice naming a bypass key that is Shift on some
|
|
243
|
+
emulators, Fn on others and a preference elsewhere — a workaround, told once,
|
|
244
|
+
in a line that scrolls away.
|
|
245
|
+
|
|
246
|
+
Press the left button and drag: the cells you drag across are highlighted as
|
|
247
|
+
you go, and releasing copies exactly them. The copy goes out over **OSC 52**
|
|
248
|
+
first, so it reaches the clipboard of the machine you are *sitting at* rather
|
|
249
|
+
than the one you are ssh'd into, with the platform clipboard binary attempted
|
|
250
|
+
alongside it for terminals that ignore the sequence. Neither mechanism is
|
|
251
|
+
detectable, so the toast names what was sent rather than claiming a success it
|
|
252
|
+
cannot verify. `/copy` now goes through the same path and gained ssh support on
|
|
253
|
+
the way.
|
|
254
|
+
|
|
255
|
+
The highlight is painted in the frame pipeline rather than in React, so a drag
|
|
256
|
+
costs one to three re-addressed rows instead of a re-render of the whole tree,
|
|
257
|
+
and the text that is copied is read from the same mirrored rows the highlight
|
|
258
|
+
was painted over — what you see is what you get by construction, not by a
|
|
259
|
+
staleness check that can be wrong.
|
|
260
|
+
|
|
261
|
+
Two switches, both reversible without a restart: `/mouse off` hands the mouse
|
|
262
|
+
back to your terminal for the session (and says plainly that the wheel goes
|
|
263
|
+
inert while it is off), `/mouse on` takes it back, and
|
|
264
|
+
`aragon config set mouseSelect false` keeps wheel scrolling while leaving
|
|
265
|
+
drag-select off — which also stops the CLI asking the terminal for motion
|
|
266
|
+
reports at all, so that session puts exactly the bytes on the wire that it did
|
|
267
|
+
before this release. `--no-mouse` remains the switch that captures nothing.
|
|
268
|
+
|
|
269
|
+
### Changed
|
|
270
|
+
|
|
271
|
+
- **Scrolling up now actually stops the transcript.** The viewport followed the
|
|
272
|
+
newest output by holding a *fixed distance from the bottom*, so scrolling up
|
|
273
|
+
during a run did not pause anything: twelve new rows of output moved the
|
|
274
|
+
window twelve rows down, and whatever you had scrolled up to read slid off the
|
|
275
|
+
top while you were reading it. The faster the agent talked, the faster your
|
|
276
|
+
reading position ran away.
|
|
277
|
+
|
|
278
|
+
While you are scrolled up, the transcript is now anchored to the **content**:
|
|
279
|
+
new output accumulates below the window instead of dragging the window along
|
|
280
|
+
with it, and the rows on screen hold still. After five seconds with no
|
|
281
|
+
scrolling the viewport returns to the newest line by itself — but only if
|
|
282
|
+
something actually arrived while you were paused, so reading a *finished*
|
|
283
|
+
transcript is never interrupted by a jump to a bottom that has not moved.
|
|
284
|
+
Every gesture restarts the countdown, a selection drag suspends it, and
|
|
285
|
+
`aragon config set scrollResumeMs 0` turns the return off entirely
|
|
286
|
+
(`ARAGON_SCROLL_RESUME_MS` sets it too).
|
|
287
|
+
|
|
288
|
+
The number the hint shows means something sharper as a result: it is now
|
|
289
|
+
literally "rows of newer output below your reading position", which is what
|
|
290
|
+
`PgDn` traverses and what the status bar's `↑N` has always shown.
|
|
291
|
+
|
|
292
|
+
- **The "N new lines · PgDn" hint moved inside the input box.** It used to be
|
|
293
|
+
rendered as the viewport's own last row, which spent a line of your transcript
|
|
294
|
+
on chrome at exactly the moment the transcript was longest — and, because the
|
|
295
|
+
hint's presence shrank the viewport, it fed back into the very number it was
|
|
296
|
+
displaying. It is now a right-aligned chip on the composer's input row, inside
|
|
297
|
+
the border, where it costs no rows at all. The chip sits in a fixed-width cell
|
|
298
|
+
so the number gaining a digit cannot re-wrap a draft, it never truncates (the
|
|
299
|
+
draft wraps first), and unlike the teaching row below the box it neither fades
|
|
300
|
+
with experience nor disappears on a short terminal: how far behind you are is
|
|
301
|
+
state, not a tutorial.
|
|
302
|
+
|
|
87
303
|
### Fixed
|
|
88
304
|
|
|
89
305
|
- **The working spinner no longer disappears when an ack takes its row.** Steer a
|
package/README.md
CHANGED
|
@@ -14,10 +14,14 @@ steer / configure the session — all without leaving the terminal.
|
|
|
14
14
|
|
|
15
15
|
Node **≥ 18**, any platform.
|
|
16
16
|
|
|
17
|
-
**On Windows
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
17
|
+
**On Windows with Node < 22.17.0 (or 24.0/24.1), aragon turns the missing
|
|
18
|
+
console flag on itself at startup**, which restores `Shift+Tab` on most
|
|
19
|
+
machines. Where that is blocked — a locked-down PowerShell, an old console
|
|
20
|
+
host — **`Ctrl+P` toggles the mode instead**; it is bound on every platform and
|
|
21
|
+
does not depend on the console mode, the Node version, or anything else.
|
|
22
|
+
Everything else — the agent loop, every tool, skills, teams, `aragon -p` — works
|
|
23
|
+
exactly the same on Node 18 and 20, which is why `engines` stays at `>=18`
|
|
24
|
+
rather than locking those users out.
|
|
21
25
|
|
|
22
26
|
The reason is not the terminal emulator and not a setting: on Windows both
|
|
23
27
|
`Shift+Tab` (`CSI Z`) and mouse reporting exist only while the console input
|
|
@@ -27,9 +31,14 @@ itself, and that translation has no entry for `Shift+Tab` (it arrives as a
|
|
|
27
31
|
plain `Tab`) and discards mouse records outright. Node 20 is the current
|
|
28
32
|
Windows default LTS, so this is a common configuration rather than an exotic
|
|
29
33
|
one — it is why the same build behaves differently on two machines. Below that
|
|
30
|
-
version aragon
|
|
31
|
-
|
|
32
|
-
|
|
34
|
+
version aragon sets `ENABLE_VIRTUAL_TERMINAL_INPUT` itself, from a short
|
|
35
|
+
PowerShell helper, right after it puts the console into raw mode — the flag is a
|
|
36
|
+
property of the console, not of Node, so any process attached to it can turn it
|
|
37
|
+
on. When that works, `Shift+Tab` arrives as `CSI Z` again and mouse reporting
|
|
38
|
+
goes back on with it — the wheel should follow by the same route, though only
|
|
39
|
+
the key has been measured. When it does not work, aragon says so at startup and
|
|
40
|
+
turns mouse reporting off rather than asking for reports that can never arrive;
|
|
41
|
+
`Ctrl+P` and `/plan` switch mode, and `Shift+↑` / `Shift+↓` scroll.
|
|
33
42
|
|
|
34
43
|
## Install
|
|
35
44
|
|
|
@@ -368,6 +377,8 @@ below works there too, in either position — `aragon --model m exec "…"` and
|
|
|
368
377
|
| `--fast-model <id>` | The fast model id. Implies nothing about `--fast` on its own |
|
|
369
378
|
| `--fast-provider <id>` | Provider for the fast model. Absent inherits the main provider |
|
|
370
379
|
| `--fast-review <n\|off>` | Turns between automatic fast reviews, or `off` |
|
|
380
|
+
| `--compaction` / `--no-compaction` | Compact the conversation when the context window fills, or never. **On** by default — see **Context compaction** |
|
|
381
|
+
| `--compaction-threshold <n>` | Occupancy that triggers compaction for this run: `0.9` or `90%`, clamped to `[0.5, 0.95]` |
|
|
371
382
|
| `--no-skills` | Disable the skill system entirely (no catalog, no skill tools) |
|
|
372
383
|
| `--skill <name>` | Force-load a skill's full body for this run (repeatable) |
|
|
373
384
|
| `--skills-yes` | Approve skill installs for this run (CI / headless) |
|
|
@@ -401,6 +412,7 @@ signal ends the run (`130` SIGINT, `143` SIGTERM, `129` SIGHUP).
|
|
|
401
412
|
| `/todo [on\|off\|panel on\|off\|follow <mode>\|clear\|continue]` | Todo planning: status, the on/off switch, the rail, follow-through, and picking an unfinished plan back up |
|
|
402
413
|
| `/retry [show\|on\|off\|max <n>]` | API retry: the effective ladder, the on/off switch, the retry count. Takes effect **in the running session**, children included |
|
|
403
414
|
| `/fast [on\|off\|model <id>\|provider <id>\|same\|review <n\|off>\|delegate on\|off]` | Fast model tier: status and this session's totals, the on/off switch, which model, the review cadence |
|
|
415
|
+
| `/compact [status\|on\|off\|threshold <n>\|keep <n>\|history\|show <n>\|<instructions>]` | Context compaction: occupancy and this session's totals, the on/off switch, when it fires, how much it keeps. With free text, compacts now and tells the summarizer what to pay attention to |
|
|
404
416
|
| `/update [status\|now\|skip\|off]` | Auto-update: what is running, what is available, where it came from, when it looks again. `now` forces a check past the throttle; `skip` declines the offered version until a newer one appears |
|
|
405
417
|
| `/theme <auto\|warm\|cool\|light>` | Switch the color theme live (persisted) |
|
|
406
418
|
| `/expand [n]` | Expand / collapse the n-th-from-last (default last) tool card |
|
|
@@ -422,7 +434,8 @@ completes, `Up` / `Down` moves the selection, `Esc` closes it.
|
|
|
422
434
|
| --- | --- |
|
|
423
435
|
| `Enter` | Submit (idle) / queue a steering message (running) |
|
|
424
436
|
| `Alt+Enter` / `Shift+Enter` | Insert a newline |
|
|
425
|
-
| `Shift+Tab` | Toggle **plan mode** (`BUILD` <-> `PLAN`). Equivalent: `/plan`. On Windows needs Node
|
|
437
|
+
| `Shift+Tab` | Toggle **plan mode** (`BUILD` <-> `PLAN`). Equivalent: `/plan`. On Windows this needs a console flag Node < 22.17 does not set; aragon sets it at startup — see [Requirements](#requirements) |
|
|
438
|
+
| `Ctrl+P` | The same toggle, over a channel no Windows console can swallow. Use it if `Shift+Tab` does nothing |
|
|
426
439
|
| `Esc` | Abort the run / close an overlay / close a popup |
|
|
427
440
|
| `Ctrl+C` ×2 | Exit (first press warns) |
|
|
428
441
|
| `Ctrl+L` | Redraw the frame (clear the screen in inline mode) |
|
|
@@ -747,17 +760,28 @@ it is an informed act rather than a stray keypress.
|
|
|
747
760
|
|
|
748
761
|
### If `Shift+Tab` does nothing
|
|
749
762
|
|
|
750
|
-
**
|
|
751
|
-
|
|
752
|
-
ordinary `Tab`, so it does not switch mode, and with the `/` palette or an `@`
|
|
753
|
-
popup open it completes that entry over your draft instead. The mouse wheel is
|
|
754
|
-
dead for the same reason. See [Requirements](#requirements); aragon reports this
|
|
755
|
-
once at startup when it detects it.
|
|
763
|
+
**Press `Ctrl+P`.** It is the same toggle and it does not depend on any of the
|
|
764
|
+
machinery below.
|
|
756
765
|
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
766
|
+
To find out why, run this in the same terminal, press `Shift+Tab` once, then
|
|
767
|
+
`Ctrl+C`:
|
|
768
|
+
|
|
769
|
+
```powershell
|
|
770
|
+
node -e "process.stdin.setRawMode(true);process.stdin.resume();process.stdin.on('data',b=>{console.log(b.toString('hex'));if(b[0]===3)process.exit(0)})"
|
|
771
|
+
```
|
|
772
|
+
|
|
773
|
+
| It prints | What it means |
|
|
774
|
+
| --- | --- |
|
|
775
|
+
| `09` | The console is swallowing the modifier — the key is arriving as an ordinary `Tab`. Check `node -v`: below 22.17.0 (or on 24.0/24.1) this is expected, and aragon's own attempt to fix it was blocked. See [Requirements](#requirements) |
|
|
776
|
+
| `1b5b5a` | The key reaches the process fine, so something inside aragon is eating it — most often an overlay is open, or the run is still going and the switch is pending (the status bar shows `BUILD → PLAN`) |
|
|
777
|
+
| nothing | Your terminal, multiplexer or remote-desktop stack never sent it; some never send `CSI Z` at all |
|
|
778
|
+
|
|
779
|
+
On the first row the wheel is dead for the same reason, and with the `/` palette
|
|
780
|
+
or an `@` popup open the stray `Tab` completes that entry over your draft.
|
|
781
|
+
|
|
782
|
+
`/plan`, `/plan on`, `/plan off` and `/plan status` are first-class equivalents
|
|
783
|
+
in every case, and `--plan` / `ARAGON_PLAN=1` / `planModeDefault` cover the
|
|
784
|
+
startup case. `aragon doctor` reports this machine's answer as `vt-input`.
|
|
761
785
|
|
|
762
786
|
### Headless
|
|
763
787
|
|
|
@@ -1095,6 +1119,205 @@ it for a background advisory is how an unrequested call ends up competing with
|
|
|
1095
1119
|
you for a rate-limited provider's quota. The review's own wall-clock bound is
|
|
1096
1120
|
20 s.
|
|
1097
1121
|
|
|
1122
|
+
## Context compaction
|
|
1123
|
+
|
|
1124
|
+
A long agentic run fills the model's context window. Before this existed, the
|
|
1125
|
+
only ending available to it at that point was an HTTP 400 and a banner telling
|
|
1126
|
+
you to start over — which threw away everything the run had done.
|
|
1127
|
+
|
|
1128
|
+
Now the CLI **compacts** instead. When the conversation occupies **90 %** of the
|
|
1129
|
+
window, the run pauses at a turn boundary, hands the older part of the history to
|
|
1130
|
+
a summarizer, and swaps it for a structured record. Your original task stays
|
|
1131
|
+
**verbatim**, the most recent turns stay **verbatim**, and the run continues in
|
|
1132
|
+
the same loop with the same tools, the same todo list and the same working
|
|
1133
|
+
directory. Nothing about the session restarts.
|
|
1134
|
+
|
|
1135
|
+
**It is on by default**, unlike every other optional subsystem here, and that is
|
|
1136
|
+
deliberate: the fast tier and team mode *add* behaviour you did not ask for,
|
|
1137
|
+
while this one *removes* a failure you did not ask for. `--no-compaction` or
|
|
1138
|
+
`compaction.enabled: false` restores the old behaviour exactly — no manager is
|
|
1139
|
+
constructed, nothing is subscribed, and a full window ends the run as it did
|
|
1140
|
+
before.
|
|
1141
|
+
|
|
1142
|
+
### What actually happens
|
|
1143
|
+
|
|
1144
|
+
1. **Proactively**, when measured occupancy crosses the threshold. This is the
|
|
1145
|
+
case that fires almost every time.
|
|
1146
|
+
2. **Reactively**, when the provider returns `context_overflow` anyway — because
|
|
1147
|
+
the model's context window is not in the static table, or you are pointed at a
|
|
1148
|
+
proxy in front of a smaller model. One compaction, one re-send, run continues.
|
|
1149
|
+
The engine allows exactly one of these per turn.
|
|
1150
|
+
3. **Manually**, with `/compact`. While the agent is idle it runs immediately;
|
|
1151
|
+
while it is running it queues and fires at the next turn boundary.
|
|
1152
|
+
|
|
1153
|
+
The trigger has **two** terms, and the second one is not belt-and-braces: 90 % of
|
|
1154
|
+
a 200 k window leaves 20 k, which is plenty, but 90 % of a 32 k window leaves
|
|
1155
|
+
3.2 k — less than a single `max_tokens` of 8192, so the request is already
|
|
1156
|
+
impossible. Compaction therefore also fires whenever the remaining headroom drops
|
|
1157
|
+
below one full response.
|
|
1158
|
+
|
|
1159
|
+
### Which model summarizes, and what it costs
|
|
1160
|
+
|
|
1161
|
+
**By default, your own model.** `compaction.useFastTier` is `true`, but it
|
|
1162
|
+
describes a *preference*: the fast tier only resolves when you have configured
|
|
1163
|
+
it, and it is off by default. So out of the box the first compaction is a
|
|
1164
|
+
~30 k-token call **on the session's own model** — real money on a frontier model,
|
|
1165
|
+
spent by a feature that is on by default.
|
|
1166
|
+
|
|
1167
|
+
Three things make that the right trade rather than a hidden cost. It happens at
|
|
1168
|
+
most 5 times per run. The alternative is losing the entire run. And the spend is
|
|
1169
|
+
visible: it goes into the session total, it is priced with the *summarizer's* own
|
|
1170
|
+
table, and `/compact status` reports `pricing unknown` rather than `$0.00` when
|
|
1171
|
+
the model is not in that table.
|
|
1172
|
+
|
|
1173
|
+
To make it cheap, configure the fast tier (`/fast on`, `/fast model <cheap-id>`).
|
|
1174
|
+
The transcript card then names the cheap model.
|
|
1175
|
+
|
|
1176
|
+
### When it cannot summarize
|
|
1177
|
+
|
|
1178
|
+
Summarization is a network call and it will sometimes fail. The ladder is: one
|
|
1179
|
+
retry — on your **main** model if the first attempt used the fast tier, because
|
|
1180
|
+
"a fast model that cannot summarize" is the likeliest single failure — and then,
|
|
1181
|
+
by default, a **truncation**: the same cut, the same verbatim task, and a block
|
|
1182
|
+
that says plainly that the dropped messages were not summarized. That is
|
|
1183
|
+
announced on the card, in the transcript, on the JSON event stream and inside the
|
|
1184
|
+
block the model itself reads.
|
|
1185
|
+
|
|
1186
|
+
Truncation loses information, and it is still the default, because an unattended
|
|
1187
|
+
`aragon exec` that wedges on an unrecoverable 400 loses the whole run.
|
|
1188
|
+
`compaction.onFailure: "stop"` makes the other trade: the history is left alone,
|
|
1189
|
+
an error names your exits, and the next request will probably fail — which is
|
|
1190
|
+
what you asked for by setting it.
|
|
1191
|
+
|
|
1192
|
+
### It will not loop on your money
|
|
1193
|
+
|
|
1194
|
+
Four independent guards: never two compactions on consecutive turns, never more
|
|
1195
|
+
than 5 per run, a splice that does not project a real reclaim does not count as
|
|
1196
|
+
progress, and two consecutive no-progress compactions **switch the proactive
|
|
1197
|
+
trigger off for the session** with a notice naming the cause and the fix.
|
|
1198
|
+
|
|
1199
|
+
The guards are trigger-aware. A *pressure* compaction that reclaims little has
|
|
1200
|
+
spent money to buy two turns and should stop. An *overflow* compaction that
|
|
1201
|
+
reclaims little is the difference between a live run and a dead one, because the
|
|
1202
|
+
provider has already refused the request — so the reactive path stays armed even
|
|
1203
|
+
after a self-disable, and an overflow attempt may halve `keepRecentTurns` for
|
|
1204
|
+
itself, which is the one lever that can make a too-large tail fit.
|
|
1205
|
+
|
|
1206
|
+
### What you see
|
|
1207
|
+
|
|
1208
|
+
Three surfaces, each answering something the others cannot:
|
|
1209
|
+
|
|
1210
|
+
- **While it runs** — the activity row reads `Compacting context…`. It outranks
|
|
1211
|
+
the tool name and the rotating phrase, because compaction happens between
|
|
1212
|
+
turns and the phrase would otherwise claim the model is thinking. The
|
|
1213
|
+
transcript card counts the seconds and tells you `esc to cancel`, which has
|
|
1214
|
+
always been true and was never said.
|
|
1215
|
+
- **As state** — a `compacting` chip in the status bar, and the context gauge's
|
|
1216
|
+
colours aligned to your own thresholds. The gauge **falls immediately** after a
|
|
1217
|
+
compaction rather than waiting for the next turn, with a `~` while the value is
|
|
1218
|
+
derived rather than measured.
|
|
1219
|
+
- **As a record** — a transcript card with the before/after message and token
|
|
1220
|
+
counts, the summarizer, the duration, and the summary itself. `Ctrl+O` expands
|
|
1221
|
+
it.
|
|
1222
|
+
|
|
1223
|
+
**What a red gauge means:** the trigger is `>= threshold` while the red band is
|
|
1224
|
+
`> threshold`, so any occupancy that would paint red has already triggered
|
|
1225
|
+
compaction and the bar repaints downward in the same frame. Red is therefore only
|
|
1226
|
+
ever visible when compaction did **not** or **could not** run — it is off, it
|
|
1227
|
+
self-disabled, or the ladder ran out.
|
|
1228
|
+
|
|
1229
|
+
`/compact status` is the guaranteed surface: the chip drops on a narrow terminal
|
|
1230
|
+
and the card scrolls away, but the command answers at any width, in any mode,
|
|
1231
|
+
after the fact.
|
|
1232
|
+
|
|
1233
|
+
### When the RECENT turns are what does not fit
|
|
1234
|
+
|
|
1235
|
+
Every rung above operates on the **head** of the conversation. When the retained
|
|
1236
|
+
tail is the problem — a few turns each carrying several 100 KB tool results —
|
|
1237
|
+
each of those rungs reports success or "nothing to drop" while the history stays
|
|
1238
|
+
un-sendable.
|
|
1239
|
+
|
|
1240
|
+
The last rung clips oversized `tool_result` bodies **inside the retained turns**,
|
|
1241
|
+
oldest first, stopping the moment the request fits. Nothing is removed: no
|
|
1242
|
+
message, no tool-call id, no role — so a history that was structurally valid
|
|
1243
|
+
before is still valid after. Every clip is announced **in the text the model
|
|
1244
|
+
reads**, at the exact place the data went:
|
|
1245
|
+
|
|
1246
|
+
```
|
|
1247
|
+
[... 214003 characters removed by context compaction ...]
|
|
1248
|
+
```
|
|
1249
|
+
|
|
1250
|
+
A model that sees that knows the output is partial and can re-run the tool. It is
|
|
1251
|
+
also stated on the transcript card, in `/compact status`, on the JSON event
|
|
1252
|
+
stream and in the archive. When clipping is the **only** thing that happened, the
|
|
1253
|
+
card says so and names no model, because none was called.
|
|
1254
|
+
|
|
1255
|
+
### Sub-agents get the same protection
|
|
1256
|
+
|
|
1257
|
+
A `task` child is bounded by `team.maxTurnsPerSubagent` (24) and
|
|
1258
|
+
`team.dispatchTimeoutMs` (15 minutes) — the same profile as a lead run that fills
|
|
1259
|
+
a window. When a child overflowed, its history was discarded and the whole
|
|
1260
|
+
dispatch reached you as one partial sentence.
|
|
1261
|
+
|
|
1262
|
+
Children now compact their own history, under tighter bounds: **two** retained
|
|
1263
|
+
turns instead of four, at most **two** compactions per child instead of five, the
|
|
1264
|
+
fast tier preferred whenever it resolves, and `onFailure` forced to `truncate`
|
|
1265
|
+
because nobody is watching a background worker. There is no transcript card for a
|
|
1266
|
+
child — the lead's transcript describes the lead's context. Turn it off with
|
|
1267
|
+
`compaction.subagents: false`.
|
|
1268
|
+
|
|
1269
|
+
### A compaction is no longer irreversible
|
|
1270
|
+
|
|
1271
|
+
Before a splice is adopted, the dropped messages are written verbatim to
|
|
1272
|
+
`~/.aragon/compaction/` — metadata, the summary, and the messages themselves.
|
|
1273
|
+
|
|
1274
|
+
`/compact history` lists **this run's** archives, newest first, with the path of
|
|
1275
|
+
each. `/compact show <n>` prints one archive's metadata and its stored summary;
|
|
1276
|
+
it never prints message bodies, because a single archive can be megabytes and the
|
|
1277
|
+
transcript is not a pager. The directory is shared by every `aragon` on the
|
|
1278
|
+
machine, so files carry a per-process run id: another terminal's compactions are
|
|
1279
|
+
counted in one trailing line rather than listed as yours.
|
|
1280
|
+
|
|
1281
|
+
Retention is two-tier and bounded: the newest 20 files **of this run**, plus a
|
|
1282
|
+
seven-day sweep across all runs. Writing is best-effort — a failure is logged and
|
|
1283
|
+
never affects the compaction. `compaction.archive: false` turns it off and writes
|
|
1284
|
+
nothing.
|
|
1285
|
+
|
|
1286
|
+
This is **not undo**: restoring an over-full history restores the condition that
|
|
1287
|
+
triggered the compaction, and the next request fails. It is fidelity on disk, so
|
|
1288
|
+
"the summary was wrong and the agent proceeded on a false record" is something
|
|
1289
|
+
you can check rather than something you have to accept.
|
|
1290
|
+
|
|
1291
|
+
### Configuration
|
|
1292
|
+
|
|
1293
|
+
```jsonc
|
|
1294
|
+
{
|
|
1295
|
+
"compaction": {
|
|
1296
|
+
"enabled": true, // false restores the pre-feature behaviour exactly
|
|
1297
|
+
"threshold": 0.9, // [0.5, 0.95]
|
|
1298
|
+
"warnThreshold": 0.75, // [0.4, threshold - 0.05]; where the gauge turns amber
|
|
1299
|
+
"keepRecentTurns": 4, // [1, 20] complete turns kept verbatim
|
|
1300
|
+
"useFastTier": true, // summarize with the fast tier WHEN it resolves
|
|
1301
|
+
"onFailure": "truncate", // or "stop"
|
|
1302
|
+
"subagents": true, // give `task` children their own compaction
|
|
1303
|
+
"archive": true // write the dropped messages to ~/.aragon/compaction
|
|
1304
|
+
}
|
|
1305
|
+
}
|
|
1306
|
+
```
|
|
1307
|
+
|
|
1308
|
+
Environment: `ARAGON_COMPACTION=0|1`, `ARAGON_COMPACTION_THRESHOLD`,
|
|
1309
|
+
`ARAGON_COMPACTION_KEEP_TURNS`, `ARAGON_COMPACTION_SUBAGENTS`,
|
|
1310
|
+
`ARAGON_COMPACTION_ARCHIVE`.
|
|
1311
|
+
|
|
1312
|
+
### The part that cannot be fixed
|
|
1313
|
+
|
|
1314
|
+
A summarizer that is 90 % faithful will, eventually, drop something the run
|
|
1315
|
+
needed. No amount of prompt engineering closes that. What this design does is
|
|
1316
|
+
bound it: your task is verbatim, the recent turns are verbatim, the block tells
|
|
1317
|
+
the model the record is partial, and three surfaces make it visible so you can
|
|
1318
|
+
step in. That is the same trade Claude Code and Codex make, and it is preferable
|
|
1319
|
+
by a wide margin to the alternative, which is losing the entire run.
|
|
1320
|
+
|
|
1098
1321
|
## Skills
|
|
1099
1322
|
|
|
1100
1323
|
A skill is a reusable expert procedure stored on disk: a directory containing a
|
|
@@ -28,6 +28,45 @@ export declare const MODE_LABEL: Record<AgentMode, string>;
|
|
|
28
28
|
* and nothing else. Two ship today (§1 non-goals).
|
|
29
29
|
*/
|
|
30
30
|
export declare function nextMode(current: AgentMode): AgentMode;
|
|
31
|
+
/**
|
|
32
|
+
* The keys that toggle the mode, as the user sees them written
|
|
33
|
+
* (shift-tab-mode-toggle-still-dead-on-windows, C2).
|
|
34
|
+
*
|
|
35
|
+
* `fallback` exists because `primary` CANNOT be made to work everywhere. On a
|
|
36
|
+
* Windows console without `ENABLE_VIRTUAL_TERMINAL_INPUT`, libuv takes the
|
|
37
|
+
* character branch for `VK_TAB` and drops the modifier wholesale, so `Shift+Tab`
|
|
38
|
+
* and `Ctrl+Tab` both arrive as a bare `0x09` - byte-identical to `Tab`. C1
|
|
39
|
+
* turns that bit back on where it can, but it has three known ways to fail that
|
|
40
|
+
* are outside this package (an old conhost refusing the bit, a locked-down
|
|
41
|
+
* PowerShell, a foreign process resetting the mode), so a rung that depends on
|
|
42
|
+
* NOTHING has to exist underneath it.
|
|
43
|
+
*
|
|
44
|
+
* `ctrl+p` IS MEASURED, not guessed. On the lossy path it arrives as `0x10`,
|
|
45
|
+
* because Windows generates a real control character for `Ctrl+<letter>` and
|
|
46
|
+
* libuv's character branch forwards it untouched; `Alt+M` produces no bytes at
|
|
47
|
+
* all and `Ctrl+Tab` collapses exactly like `Shift+Tab` (analysis section 3.6).
|
|
48
|
+
* Any idle `Ctrl+<letter>` in the `0x01-0x1A` range would work; this one is free
|
|
49
|
+
* throughout the package - `keymap.ts` claims a/e/w/u/k, `App` claims c/l/t/o,
|
|
50
|
+
* and `PromptInput` returns early on every `key.ctrl` - so binding it takes no
|
|
51
|
+
* behaviour away from anyone. `Ctrl+B` survives the same path but is tmux's
|
|
52
|
+
* default prefix, and this binding is unconditional and cross-platform, so it
|
|
53
|
+
* would be silently dead for a whole class of macOS and Linux users.
|
|
54
|
+
*
|
|
55
|
+
* BOUND UNCONDITIONALLY, on every platform. A binding that only exists where it
|
|
56
|
+
* is needed cannot be documented in one place, cannot be taught by muscle
|
|
57
|
+
* memory, and is missing on precisely the machine whose capability probe was
|
|
58
|
+
* wrong.
|
|
59
|
+
*
|
|
60
|
+
* THESE ARE THE LABELS, NOT THE BINDING. Editing a value here changes only what
|
|
61
|
+
* the user is TOLD to press - the hint row, the startup notice, `doctor`. The
|
|
62
|
+
* key that is actually listened for is `input === 'p'` in `App`'s `useInput`,
|
|
63
|
+
* and the help overlay and README spell both keys out in their own title case.
|
|
64
|
+
* Change one without the others and the product advertises a key it ignores.
|
|
65
|
+
*/
|
|
66
|
+
export declare const MODE_TOGGLE_KEYS: {
|
|
67
|
+
readonly primary: "shift+tab";
|
|
68
|
+
readonly fallback: "ctrl+p";
|
|
69
|
+
};
|
|
31
70
|
/** The two tools that only exist because a human is attached. */
|
|
32
71
|
export declare const PLAN_TOOL_NAMES: readonly ["ask_user", "submit_plan"];
|
|
33
72
|
/**
|
package/dist/agent/agent-mode.js
CHANGED
|
@@ -30,6 +30,42 @@ export function nextMode(current) {
|
|
|
30
30
|
const index = AGENT_MODES.indexOf(current);
|
|
31
31
|
return AGENT_MODES[(index + 1) % AGENT_MODES.length];
|
|
32
32
|
}
|
|
33
|
+
/**
|
|
34
|
+
* The keys that toggle the mode, as the user sees them written
|
|
35
|
+
* (shift-tab-mode-toggle-still-dead-on-windows, C2).
|
|
36
|
+
*
|
|
37
|
+
* `fallback` exists because `primary` CANNOT be made to work everywhere. On a
|
|
38
|
+
* Windows console without `ENABLE_VIRTUAL_TERMINAL_INPUT`, libuv takes the
|
|
39
|
+
* character branch for `VK_TAB` and drops the modifier wholesale, so `Shift+Tab`
|
|
40
|
+
* and `Ctrl+Tab` both arrive as a bare `0x09` - byte-identical to `Tab`. C1
|
|
41
|
+
* turns that bit back on where it can, but it has three known ways to fail that
|
|
42
|
+
* are outside this package (an old conhost refusing the bit, a locked-down
|
|
43
|
+
* PowerShell, a foreign process resetting the mode), so a rung that depends on
|
|
44
|
+
* NOTHING has to exist underneath it.
|
|
45
|
+
*
|
|
46
|
+
* `ctrl+p` IS MEASURED, not guessed. On the lossy path it arrives as `0x10`,
|
|
47
|
+
* because Windows generates a real control character for `Ctrl+<letter>` and
|
|
48
|
+
* libuv's character branch forwards it untouched; `Alt+M` produces no bytes at
|
|
49
|
+
* all and `Ctrl+Tab` collapses exactly like `Shift+Tab` (analysis section 3.6).
|
|
50
|
+
* Any idle `Ctrl+<letter>` in the `0x01-0x1A` range would work; this one is free
|
|
51
|
+
* throughout the package - `keymap.ts` claims a/e/w/u/k, `App` claims c/l/t/o,
|
|
52
|
+
* and `PromptInput` returns early on every `key.ctrl` - so binding it takes no
|
|
53
|
+
* behaviour away from anyone. `Ctrl+B` survives the same path but is tmux's
|
|
54
|
+
* default prefix, and this binding is unconditional and cross-platform, so it
|
|
55
|
+
* would be silently dead for a whole class of macOS and Linux users.
|
|
56
|
+
*
|
|
57
|
+
* BOUND UNCONDITIONALLY, on every platform. A binding that only exists where it
|
|
58
|
+
* is needed cannot be documented in one place, cannot be taught by muscle
|
|
59
|
+
* memory, and is missing on precisely the machine whose capability probe was
|
|
60
|
+
* wrong.
|
|
61
|
+
*
|
|
62
|
+
* THESE ARE THE LABELS, NOT THE BINDING. Editing a value here changes only what
|
|
63
|
+
* the user is TOLD to press - the hint row, the startup notice, `doctor`. The
|
|
64
|
+
* key that is actually listened for is `input === 'p'` in `App`'s `useInput`,
|
|
65
|
+
* and the help overlay and README spell both keys out in their own title case.
|
|
66
|
+
* Change one without the others and the product advertises a key it ignores.
|
|
67
|
+
*/
|
|
68
|
+
export const MODE_TOGGLE_KEYS = { primary: 'shift+tab', fallback: 'ctrl+p' };
|
|
33
69
|
/** The two tools that only exist because a human is attached. */
|
|
34
70
|
export const PLAN_TOOL_NAMES = ['ask_user', 'submit_plan'];
|
|
35
71
|
/**
|