@aragon-agent/cli 0.6.4 → 0.6.8

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 (119) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/README.md +163 -37
  3. package/dist/agent/controller.d.ts +49 -8
  4. package/dist/agent/controller.js +160 -48
  5. package/dist/agent/headless.d.ts +2 -1
  6. package/dist/agent/prompt-options.d.ts +15 -0
  7. package/dist/agent/prompt-options.js +1 -0
  8. package/dist/agent/reducer.d.ts +33 -19
  9. package/dist/agent/reducer.js +62 -71
  10. package/dist/agent/usage.d.ts +34 -0
  11. package/dist/agent/usage.js +43 -0
  12. package/dist/cli.js +131 -54
  13. package/dist/commands/builtins.js +12 -1
  14. package/dist/commands/registry.d.ts +2 -3
  15. package/dist/compaction/compactor.d.ts +60 -24
  16. package/dist/compaction/compactor.js +89 -61
  17. package/dist/compaction/context-command.d.ts +33 -0
  18. package/dist/compaction/context-command.js +166 -0
  19. package/dist/compaction/meter.d.ts +251 -0
  20. package/dist/compaction/meter.js +428 -0
  21. package/dist/compaction/pressure.d.ts +6 -0
  22. package/dist/compaction/pressure.js +4 -0
  23. package/dist/compaction/types.d.ts +41 -0
  24. package/dist/compaction/wiring.d.ts +47 -0
  25. package/dist/compaction/wiring.js +88 -27
  26. package/dist/config/cli-commands.js +5 -0
  27. package/dist/config/env.js +15 -1
  28. package/dist/config/load.d.ts +17 -0
  29. package/dist/config/load.js +45 -1
  30. package/dist/config/schema.d.ts +44 -0
  31. package/dist/config/schema.js +36 -0
  32. package/dist/config/store.js +27 -1
  33. package/dist/exec/events.d.ts +138 -1
  34. package/dist/exec/events.js +42 -0
  35. package/dist/exec/index.js +43 -6
  36. package/dist/exec/progress-reporter.d.ts +23 -0
  37. package/dist/exec/progress-reporter.js +88 -0
  38. package/dist/exec/runner.d.ts +34 -0
  39. package/dist/exec/runner.js +137 -25
  40. package/dist/fast/reviewer.d.ts +19 -1
  41. package/dist/fast/reviewer.js +22 -0
  42. package/dist/fast/types.d.ts +15 -0
  43. package/dist/fast/wiring.d.ts +10 -1
  44. package/dist/fast/wiring.js +18 -1
  45. package/dist/input/interrupt-gesture.d.ts +18 -0
  46. package/dist/input/interrupt-gesture.js +19 -0
  47. package/dist/input/pointer-router.d.ts +11 -0
  48. package/dist/input/pointer-router.js +14 -0
  49. package/dist/todo/limits.d.ts +4 -17
  50. package/dist/todo/limits.js +4 -17
  51. package/dist/todo/panel-rows.d.ts +8 -6
  52. package/dist/todo/panel-rows.js +19 -6
  53. package/dist/todo/store.d.ts +8 -13
  54. package/dist/todo/store.js +13 -14
  55. package/dist/todo/types.d.ts +2 -1
  56. package/dist/ui/App.d.ts +2 -0
  57. package/dist/ui/App.js +233 -183
  58. package/dist/ui/AutocompletePopup.d.ts +3 -1
  59. package/dist/ui/AutocompletePopup.js +9 -11
  60. package/dist/ui/BottomStatusRow.d.ts +1 -1
  61. package/dist/ui/Composer.d.ts +13 -5
  62. package/dist/ui/Composer.js +7 -11
  63. package/dist/ui/PromptCaret.d.ts +11 -0
  64. package/dist/ui/PromptCaret.js +31 -0
  65. package/dist/ui/PromptInput.d.ts +12 -6
  66. package/dist/ui/PromptInput.js +50 -16
  67. package/dist/ui/StatusBar.d.ts +22 -15
  68. package/dist/ui/StatusBar.js +61 -16
  69. package/dist/ui/TeamPanel.d.ts +3 -1
  70. package/dist/ui/TeamPanel.js +13 -9
  71. package/dist/ui/TodoPanel.d.ts +5 -5
  72. package/dist/ui/TodoPanel.js +56 -24
  73. package/dist/ui/Transcript.js +1 -0
  74. package/dist/ui/UpdateLine.d.ts +1 -1
  75. package/dist/ui/capabilities.js +5 -5
  76. package/dist/ui/composer-rows.js +50 -9
  77. package/dist/ui/exit-snapshot.d.ts +9 -0
  78. package/dist/ui/frame-differ.js +2 -19
  79. package/dist/ui/frame-observer.d.ts +17 -0
  80. package/dist/ui/frame-observer.js +81 -0
  81. package/dist/ui/frame-parser.d.ts +7 -0
  82. package/dist/ui/frame-parser.js +18 -0
  83. package/dist/ui/layout/AppShell.d.ts +7 -52
  84. package/dist/ui/layout/AppShell.js +2 -2
  85. package/dist/ui/layout/ScrollIndicator.d.ts +4 -3
  86. package/dist/ui/layout/ScrollIndicator.js +4 -4
  87. package/dist/ui/layout/ScrollViewport.d.ts +13 -66
  88. package/dist/ui/layout/ScrollViewport.js +183 -185
  89. package/dist/ui/layout/autocomplete.d.ts +18 -0
  90. package/dist/ui/layout/autocomplete.js +26 -0
  91. package/dist/ui/layout/budget.d.ts +3 -51
  92. package/dist/ui/layout/budget.js +4 -53
  93. package/dist/ui/layout/document-layout.d.ts +13 -0
  94. package/dist/ui/layout/document-layout.js +11 -0
  95. package/dist/ui/layout/follow-state.d.ts +1 -0
  96. package/dist/ui/layout/follow-state.js +1 -3
  97. package/dist/ui/layout/rail.d.ts +4 -53
  98. package/dist/ui/layout/rail.js +10 -62
  99. package/dist/ui/layout/scroll-indicator.d.ts +1 -1
  100. package/dist/ui/layout/scroll-indicator.js +3 -2
  101. package/dist/ui/layout/scrollbar-geometry.d.ts +17 -0
  102. package/dist/ui/layout/scrollbar-geometry.js +13 -0
  103. package/dist/ui/layout/team-panel.d.ts +18 -0
  104. package/dist/ui/layout/team-panel.js +22 -0
  105. package/dist/ui/layout/todo-layout.d.ts +25 -0
  106. package/dist/ui/layout/todo-layout.js +28 -0
  107. package/dist/ui/layout/viewport-geometry.d.ts +1 -0
  108. package/dist/ui/layout/viewport-geometry.js +1 -0
  109. package/dist/ui/layout/virtual-window.d.ts +1 -0
  110. package/dist/ui/layout/virtual-window.js +1 -1
  111. package/dist/ui/overlays/HelpOverlay.js +5 -2
  112. package/dist/ui/screen.d.ts +1 -1
  113. package/dist/ui/screen.js +4 -6
  114. package/dist/ui/scrollbar-controller.d.ts +30 -0
  115. package/dist/ui/scrollbar-controller.js +124 -0
  116. package/dist/ui/transcript-text.js +6 -2
  117. package/dist/ui/use-wheel-routing.d.ts +2 -0
  118. package/dist/ui/use-wheel-routing.js +6 -0
  119. package/package.json +2 -2
package/CHANGELOG.md CHANGED
@@ -9,6 +9,20 @@ were written.
9
9
 
10
10
  ### Added
11
11
 
12
+ - **`system/init` now lists what this build can do.** A new optional
13
+ `capabilities: string[]` field, carrying `["interrupt"]` for this release.
14
+
15
+ A wrapper cannot tell by observation whether an interrupt will settle
16
+ gracefully or kill the process outright, and guessing wrong costs the user
17
+ their conversation. Rather than have callers compare version numbers — which
18
+ breaks on forks, prereleases, and version shapes nobody anticipated — the
19
+ build now says so itself, in an event that already arrives on every start.
20
+ Absent on older builds, and absence should be read as "no", never as
21
+ "probably".
22
+
23
+ Additive, so `schemaVersion` does not move. Consumers that do not know the
24
+ field ignore it, exactly as the schema contract asks.
25
+
12
26
  - **The composer understands pasting.** Until now it did not have the concept at
13
27
  all: every byte the terminal delivered on stdin was a keystroke, and a
14
28
  clipboard block is thousands of bytes arriving in an unpredictable number of
@@ -58,6 +72,29 @@ were written.
58
72
 
59
73
  ### Fixed
60
74
 
75
+ - **`{"type":"interrupt"}` now really does settle the run — on every platform.**
76
+ The stdin interrupt frame used to raise a SIGINT at our own process. On POSIX
77
+ that reaches the runner's terminator and the run winds down properly: it
78
+ breaks out of the loop, persists the session, and emits `result` with the
79
+ usage and cost. On Windows, Node does not deliver a self-directed SIGINT to
80
+ its own handlers — the process simply terminated with exit code 1, having run
81
+ none of it.
82
+
83
+ For anyone using `aragon exec` interactively that difference was invisible,
84
+ because an interrupt there is usually the last thing that happens. It is not
85
+ invisible to a **wrapper** that interrupts a turn and then respawns on the same
86
+ `--session-id` to keep the conversation going: on Windows it silently got back
87
+ a session missing everything since the previous save. `exec/index.ts` has
88
+ carried a comment about that caller for some time ("PERSIST FIRST, ANNOUNCE
89
+ SECOND … a caller that reads `result` and immediately respawns with the same
90
+ `--session-id` must not race the writer"), so the intent was there; the path
91
+ just could not be reached on Windows.
92
+
93
+ The frame now calls the runner directly instead of going through a signal.
94
+ POSIX behaviour is byte-for-byte unchanged — the same code runs, with the same
95
+ `stopReason`, the same exit code 130, and the same two-second window in which a
96
+ second signal still forces an immediate exit.
97
+
61
98
  - **A pasted API key is stored correctly again.** Ink broadcasts each stdin chunk
62
99
  to every mounted input handler, so the settings screen, the question overlay
63
100
  and the plan-review feedback field all see what the composer sees. Without
package/README.md CHANGED
@@ -413,6 +413,7 @@ signal ends the run (`130` SIGINT, `143` SIGTERM, `129` SIGHUP).
413
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 |
414
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
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 |
416
+ | `/context` | What the context gauge is showing: occupancy split into measured and estimated, the window and **where it came from**, how stale the measurement is, compaction's actual state, and what the session-spend readout includes |
416
417
  | `/bg [list\|logs <id> [n]\|stop <id\|all>\|status]` | Background services: what is running, its log tail, and how to stop it. `list` is the default |
417
418
  | `/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 |
418
419
  | `/theme <auto\|warm\|cool\|light>` | Switch the color theme live (persisted) |
@@ -437,8 +438,9 @@ completes, `Up` / `Down` moves the selection, `Esc` closes it.
437
438
  | `Alt+Enter` / `Shift+Enter` | Insert a newline |
438
439
  | `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) |
439
440
  | `Ctrl+P` | The same toggle, over a channel no Windows console can swallow. Use it if `Shift+Tab` does nothing |
440
- | `Esc` | Abort the run / close an overlay / close a popup |
441
- | `Esc` ×2 | **Force-stop** a run that did not abort: kills the shell command it is blocked on and returns the view to `idle`. The second press only does anything while the run is *still* running, so in a healthy run it is a no-op |
441
+ | `Esc` | Close an overlay or popup; otherwise ask for interruption confirmation. While idle, cancel pending auto-continuation |
442
+ | `Esc` ×2 | Interrupt the run when pressed within 1.5 seconds. Other keys or closing a menu cancel the first press |
443
+ | `Esc` again after interruption | **Force-stop** a run that is still stopping, kill foreground shell commands and return to `idle`; background services remain running |
442
444
  | `Ctrl+C` | Stop the background **services** the agent started, when there are any. It does not arm exit |
443
445
  | `Ctrl+C` ×2 | Exit (first press warns), once no services are running |
444
446
  | `Ctrl+L` | Redraw the frame (clear the screen in inline mode) |
@@ -459,8 +461,11 @@ completes, `Up` / `Down` moves the selection, `Esc` closes it.
459
461
  In **full-screen mode** the transcript is scrolled by the app itself: `PgUp` /
460
462
  `PgDn` and `Shift+↑` / `Shift+↓`. While pinned to the bottom the viewport follows
461
463
  new output automatically; once you scroll away the status bar shows `↑N`, a slim
462
- rail on the right edge shows where you are, and a hint counts the lines below
463
- you. Submitting a message always re-pins to the newest output. In **inline mode**
464
+ scrollbar occupies the terminal's last column (including 40-column windows). Drag its thumb
465
+ to move through history, or click the track to move one page. Messages, the team panel
466
+ and the input area scroll together; typing or editing returns to the input without
467
+ losing the draft. The fixed status row shows `PgDn down` while reading history.
468
+ `PgDn` moves one page down; editing or submitting returns directly to the end. In **inline mode**
464
469
  (`--no-fullscreen`) history lives in your terminal's native scrollback, printed
465
470
  once via Ink `<Static>`.
466
471
 
@@ -473,9 +478,10 @@ open. It never touches your draft, and it never recalls prompt history: that is
473
478
  Two things follow from the app owning the mouse, and they are worth knowing
474
479
  before you meet them:
475
480
 
476
- - **Selecting text now needs `Shift`+drag** in most terminals. That is the
477
- terminal's convention for "an application is tracking the mouse", not
478
- something this CLI can opt out of while still reading wheel events.
481
+ - **Dragging text selects and copies it when mouse selection is enabled.**
482
+ Dragging the last-column scrollbar scrolls instead. `--no-mouse-select` disables
483
+ text selection while preserving scrollbar dragging. `Shift`+drag uses the
484
+ terminal's native selection on hosts that support that convention.
479
485
  - **`tmux` with `mouse on` keeps the wheel for itself**, so the app never sees
480
486
  it. `set -g mouse off` hands it back.
481
487
  - **A Windows console on Node below 22.17.0 cannot report the wheel at all**,
@@ -511,7 +517,7 @@ Density and hints are settable the same three ways: `--compact` / `--no-hints`
511
517
  for one run, `aragon config set density compact` / `aragon config set hints false`
512
518
  to persist. The composer hint row also shortens to `? help` on its own after a
513
519
  few sessions — except while a run is in progress, when it always spells out
514
- `esc abort` in full.
520
+ `esc ×2 interrupt` in full.
515
521
 
516
522
  ## What you see while it runs
517
523
 
@@ -623,8 +629,9 @@ near-identical added ones.
623
629
 
624
630
  - Your previous shell output is **covered, not erased**, and returns untouched
625
631
  when you exit. Nothing in your scrollback is destroyed.
626
- - The frame is fixed at `rows - 1` tall, which is what keeps the composer and the
627
- status bar at the bottom of the screen even on an empty session.
632
+ - The frame is fixed at `rows - 1` tall. The header, activity row and status bar
633
+ stay visible. The input belongs to the scrolling document; short sessions fill
634
+ the space above it, and browsing history moves it out of view.
628
635
 
629
636
  On exit the session is replayed into the normal buffer as plain text so the
630
637
  conversation survives leaving the screen (`--no-exit-transcript` opts out;
@@ -637,10 +644,13 @@ writing screen-control sequences into a pipe or a redirected file is never safe.
637
644
  `--no-fullscreen`, `ARAGON_FULLSCREEN=0`, or `aragon config set fullscreen false`
638
645
  opt out permanently.
639
646
 
640
- The terminal's native scrollback and mouse wheel do not scroll the transcript in
641
- this mode, and mouse tracking is deliberately left off because enabling it costs
642
- text selection and copy in most terminals. If a crash ever strands your terminal
643
- on the alternate screen, `reset` restores it.
647
+ The alternate screen uses an application scrollbar rather than the terminal's
648
+ native scrollback. Windows Terminal and conhost have different mouse/VT capabilities;
649
+ the PowerShell version alone does not determine dragging support. If the host cannot
650
+ deliver SGR mouse reports, the scrollbar remains visible and keyboard scrolling works.
651
+ `--no-mouse` provides that keyboard-only path; `--no-diff-render` disables output
652
+ differencing without disabling the scrollbar. A mid-session resize below 40 columns
653
+ or 12 rows displays a size hint and preserves the draft for restoration.
644
654
 
645
655
  ## Render performance
646
656
 
@@ -741,8 +751,8 @@ assumptions rather than to retry.
741
751
 
742
752
  **Dismissing a plan is not how you stop.** `Esc` on a plan card asks for a
743
753
  *better* plan — the agent is told to refine it and submit again — so a model that
744
- keeps submitting keeps getting cards. To stop the run itself, **press `Esc`
745
- twice**: the first closes the card, the second aborts the run. `Ctrl+C` twice
754
+ keeps submitting keeps getting cards. To stop the run itself, close the card with `Esc`, then **press `Esc`
755
+ twice within 1.5 seconds** to interrupt. Closing the card never counts as the first press. `Ctrl+C` twice
746
756
  still exits the session outright.
747
757
 
748
758
  ### Where the mode is shown
@@ -799,7 +809,8 @@ ask — and the agent writes the finished plan as its final message in markdown.
799
809
  For work with three or more distinct steps the agent keeps a **visible plan**:
800
810
  it writes a checklist with the `todo_write` tool, marks exactly one item as in
801
811
  progress, and ticks it off before starting the next. In full-screen mode that
802
- list is rendered as a right-hand rail, about a fifth of the terminal wide.
812
+ list is rendered as a right-hand rail, targeting 15% of the terminal width
813
+ (14–36 columns, including its separator and padding).
803
814
 
804
815
  ```
805
816
  transcript ... | TODO 2/7
@@ -807,8 +818,7 @@ list is rendered as a right-hand rail, about a fifth of the terminal wide.
807
818
  |
808
819
  | v 1 Read the reducer
809
820
  | v 2 Design the store
810
- | > 3 Wiring the rail into
811
- | AppShell
821
+ | > 3 Wiring the rail...
812
822
  | o 4 Add the panel-rows test
813
823
  | o 5 Update the README
814
824
  | +2 more
@@ -820,8 +830,8 @@ for a question, or for a search with no work attached — and a one-item list
820
830
  against an empty plan is refused outright. A session that never plans has
821
831
  byte-identical layout to a build without this feature.
822
832
 
823
- **The list is a projection of what the model believes**, so nothing that does
824
- not also tell the model may change it — except you, saying so:
833
+ **The list shows the current task's plan.** Starting a new task or explicitly
834
+ clearing the list removes this live view while preserving conversation history:
825
835
 
826
836
  - `/clear` and `/todo clear` take the plan off the screen. Neither touches the
827
837
  conversation, so the model still knows what it was doing and its next update
@@ -830,10 +840,14 @@ not also tell the model may change it — except you, saying so:
830
840
  - `/save` writes the list and `/resume` restores it. Resuming a session that has
831
841
  no list clears the current one, rather than leaving a plan on screen whose
832
842
  conversation has just been replaced.
833
- - A finished plan is dropped at the START of your next message, so you still get
834
- to see `7/7 done`. An unfinished one survives "continue" / "now do the rest",
835
- and is dropped after three unrelated turns rather than holding a fifth of the
836
- screen for the rest of the session.
843
+ - A normal new task clears the previous plan, including unfinished steps, only
844
+ when it starts successfully. The completed run keeps its final plan visible.
845
+ `/todo continue` and automatic continuation preserve the plan and its timestamp;
846
+ ordinary text such as "continue" starts a new task. Steering keeps the current
847
+ plan. Until a valid new `todo_write`, the rail, inline strip and count stay hidden.
848
+ Startup failure or cancellation keeps the old plan. History cards remain available.
849
+ - During a live run, interrupt before `/reset` or `/resume`. A conversation switch
850
+ while startup is waiting cancels that pending request and returns the view to idle.
837
851
 
838
852
  ### When a run ends with steps left
839
853
 
@@ -856,7 +870,7 @@ setting:
856
870
 
857
871
  `Esc` during the grace window cancels; so does typing anything. A run that ended
858
872
  with an **error** never auto-continues in any mode — you get a warning instead.
859
- A run **you** aborted with `Esc` now says nothing at all, where it used to
873
+ A run **you** interrupted with two `Esc` presses now says nothing at all, where it used to
860
874
  report what you had just interrupted.
861
875
 
862
876
  `/todo follow off` is the third mode: no notice, no continuation, silence.
@@ -888,11 +902,19 @@ planning discipline and hides the column — which is what you want with a scree
888
902
  reader, and what `--no-todo-panel` does for a whole session. Under `-p` the plan
889
903
  is written to stderr as `[todo] 3/7 <step>` lines, suppressed by `--quiet`.
890
904
 
891
- The rail is not mounted below 80 columns, while an overlay is open, in inline
892
- mode, or when the viewport is too short to say anything useful; the transcript
893
- keeps at least 62 columns at every width. In those cases the counter appears in
894
- the status bar instead, and the checklist is still written into the transcript
895
- as one card per turn.
905
+ 右栏从 76 列开始显示,正文至少保留 62 列;100 列终端使用 15 列右栏。
906
+ 已有任务时,团队面板按实际行数占用空间,补全菜单的边框和提示也计入预算。
907
+ 例如 100×20、团队占 8 行时,TODO 仍以 4 行紧凑视图显示。
908
+
909
+ 剩余 3–5 行时保留标题、完成计数、当前任务与合并提示 `-A +B`:
910
+ `-9 +10` 表示上方隐藏 9 项、下方隐藏 10 项。6 行起使用常规视图;
911
+ 所有任务各占一行,全文仍可在正文任务卡片中查看。进度条需要至少 20 列、8 行。
912
+ 输入增长导致空间不足时团队先折叠为摘要;菜单不足 3 行时隐藏,其候选不会拦截输入。
913
+
914
+ 低于 76 列、共享区域不足 3 行、打开 overlay、显式关闭面板、没有任务或
915
+ inline 模式时不显示右栏。低于 12 行使用原有小终端提示;扩大窗口后自动恢复。
916
+ inline 仍使用上文的单行任务条。`/todo status` 中的 `Panel: on` 仅表示配置开启,
917
+ 实际可见性还取决于数据和窗口空间;可以通过该命令区分未生成计划与暂时隐藏。
896
918
 
897
919
  ## Built-in tools
898
920
 
@@ -998,7 +1020,8 @@ than features. `bash` now always settles: it returns when the process exits
998
1020
  rather than when its output stream closes, so a command that detaches a
999
1021
  grandchild can no longer leave the turn pending forever, and it is handed a
1000
1022
  closed stdin so a command that prompts fails instead of blocking on a read
1001
- nobody will answer. And `Esc` twice always force-stops, whatever the flag says.
1023
+ nobody will answer. Two `Esc` presses request interruption; another press force-stops a stuck run,
1024
+ whatever the flag says.
1002
1025
 
1003
1026
  ## Team subagents
1004
1027
 
@@ -1070,7 +1093,7 @@ children's tokens are folded into the `[usage]` footer.
1070
1093
  `debug`. A `team_send` subject is recorded, never its body; a subagent's brief
1071
1094
  and summary only at `trace`.
1072
1095
 
1073
- **`Esc`** aborts the whole dispatch — every subagent within a couple of seconds
1096
+ **Two `Esc` presses within 1.5 seconds** abort the whole dispatch — every subagent within a couple of seconds
1074
1097
  — and the report comes back marked `ABORTED` with whatever was finished.
1075
1098
 
1076
1099
  ## Fast model tier
@@ -1211,6 +1234,84 @@ it for a background advisory is how an unrequested call ends up competing with
1211
1234
  you for a rate-limited provider's quota. The review's own wall-clock bound is
1212
1235
  20 s.
1213
1236
 
1237
+ ## The context gauge
1238
+
1239
+ The right end of the status bar answers one question: **how full is the context,
1240
+ out of how much.**
1241
+
1242
+ ```
1243
+ [####----] 43% 86.0k/200.0k total 1.2M^ 48.0k v $3.21 12 tok/s 1m02s
1244
+ ^ ^ ^
1245
+ | | +-- session spend, prompt side / output side
1246
+ | +-- occupied / window
1247
+ +-- the two together, as a percentage
1248
+ ```
1249
+
1250
+ The readouts drop as the terminal narrows, in that order of priority:
1251
+
1252
+ | Columns | Right cluster |
1253
+ |---|---|
1254
+ | >= 96 | `[####----] 43% 86.0k/200.0k total 1.2M^ 48.0k v $3.21` |
1255
+ | 72-95 | `[####----] 43% 86.0k/200.0k $3.21` |
1256
+ | 60-71 | `[####----] 43% $3.21` |
1257
+ | < 60 | `43% $3.21` |
1258
+
1259
+ **Two different markers, because there are two different things to doubt.**
1260
+
1261
+ - `~` sits on the **percentage** and means the *numerator* is partly a guess:
1262
+ either no completed turn has reported usage yet (right after `/resume`, or
1263
+ right after a compaction), or tool results have been appended since the last
1264
+ measurement and their size is estimated.
1265
+ - `?` sits on the **window** and means the *denominator* is a guess: the model is
1266
+ not in the built-in table, so a flat 128 000 was substituted. See
1267
+ `contextWindow` below.
1268
+
1269
+ **The percentage keeps moving during a turn.** Every tool result nudges it,
1270
+ whether or not compaction is enabled — it is not a compaction feature, and a
1271
+ session started with `--no-compaction` gets the same live reading. After
1272
+ `/resume` the gauge shows the restored session's real occupancy immediately,
1273
+ without sending anything.
1274
+
1275
+ ### `/context`
1276
+
1277
+ The gauge is a bar and a rounded percentage; `/context` is the full answer, at
1278
+ any width, in any mode:
1279
+
1280
+ ```
1281
+ Context
1282
+ Occupancy 43% 86.2k of 200.0k [measured 85.1k + 1.1k estimated]
1283
+ Window 200000 from the model table
1284
+ Since measured 1.1k estimated tokens appended since the last provider-reported usage
1285
+ Compaction on - triggers at 90% (amber at 75%), 2 this session, 118.0k reclaimed
1286
+ Session spend 1.2M in (incl. 940.0k cache read, 12.0k cache write) / 48.2k out, $3.21
1287
+ includes subagent, fast-tier and compaction spend,
1288
+ not just this conversation
1289
+ ```
1290
+
1291
+ Two lines are worth reading closely. **`Window`** always names where the number
1292
+ came from — the model table, your own `contextWindow`, or the invented
1293
+ placeholder — because those are indistinguishable everywhere else. And
1294
+ **`Compaction`** reports what is actually true of this session, including
1295
+ `off - not registered for this session (started with --no-compaction)`; it never
1296
+ promises a threshold that nothing will act on.
1297
+
1298
+ ### `contextWindow`
1299
+
1300
+ When the model is not in the built-in table — a custom `baseUrl`, a self-hosted
1301
+ id, something released last week — the denominator is a **fabricated** 128 000
1302
+ and the percentage is meaningless. Correct it:
1303
+
1304
+ ```bash
1305
+ aragon config set contextWindow 1000000 # persisted; prints "Set contextWindow = 1000000"
1306
+ aragon config set contextWindow auto # back to the model table / placeholder
1307
+ ARAGON_CONTEXT_WINDOW=1000000 aragon # one session
1308
+ ```
1309
+
1310
+ Values are clamped to `[8000, 5000000]`; `auto` (the default) is stored as
1311
+ `null`. Setting it removes the `?` marker, because you have asserted the number —
1312
+ and `/context` still shows that it came from you, which is what makes a wrong
1313
+ value findable.
1314
+
1214
1315
  ## Context compaction
1215
1316
 
1216
1317
  A long agentic run fills the model's context window. Before this existed, the
@@ -1307,7 +1408,10 @@ Three surfaces, each answering something the others cannot:
1307
1408
  - **As state** — a `compacting` chip in the status bar, and the context gauge's
1308
1409
  colours aligned to your own thresholds. The gauge **falls immediately** after a
1309
1410
  compaction rather than waiting for the next turn, with a `~` while the value is
1310
- derived rather than measured.
1411
+ derived rather than measured. The colours follow your configured thresholds
1412
+ whenever compaction is **enabled**, even if no summarizer model currently
1413
+ resolves; after `/compact off` they fall back to the generic 60 / 85, because
1414
+ colouring by a rescue that is not coming is worse than not colouring at all.
1311
1415
  - **As a record** — a transcript card with the before/after message and token
1312
1416
  counts, the summarizer, the duration, and the summary itself. `Ctrl+O` expands
1313
1417
  it.
@@ -1320,7 +1424,9 @@ self-disabled, or the ladder ran out.
1320
1424
 
1321
1425
  `/compact status` is the guaranteed surface: the chip drops on a narrow terminal
1322
1426
  and the card scrolls away, but the command answers at any width, in any mode,
1323
- after the fact.
1427
+ after the fact. For the gauge specifically, [`/context`](#the-context-gauge) is
1428
+ the equivalent — and it works in a session that never registered compaction at
1429
+ all.
1324
1430
 
1325
1431
  ### When the RECENT turns are what does not fit
1326
1432
 
@@ -1701,6 +1807,8 @@ env / `.env` → CLI flags**.
1701
1807
  reasoning blocks), `ARAGON_LIVE_TOOL_OUTPUT` (`0` keeps a running tool card to
1702
1808
  one line),
1703
1809
  `ARAGON_MAX_TOKENS` (a number, or `auto` / `0` for the per-model ceiling),
1810
+ `ARAGON_CONTEXT_WINDOW` (a number, or `auto`; the denominator the context
1811
+ gauge measures against),
1704
1812
  `ARAGON_THEME`,
1705
1813
  `ARAGON_FULLSCREEN`, `ARAGON_PLAN` (`1` starts in plan mode),
1706
1814
  `ARAGON_TEAM` (`0` disables team subagents), `ARAGON_TEAM_MAX` (fan-out width),
@@ -1728,6 +1836,7 @@ env / `.env` → CLI flags**.
1728
1836
  | Config key | Default | Meaning |
1729
1837
  | --- | --- | --- |
1730
1838
  | `maxTokens` | `64000` | Output token cap, clamped to `[256, 200000]`. `null` means **auto** — use each model's own ceiling, never above 64000. Absent means the default. See [Output token limits](#output-token-limits). |
1839
+ | `contextWindow` | `null` | The window the context gauge measures against, clamped to `[8000, 5000000]`. `null` means **auto** — the built-in model table, or a fabricated 128000 for a model it has never seen. Set it when the gauge shows `?` on the denominator. See [The context gauge](#the-context-gauge). |
1731
1840
  | `showThinking` | `false` | Draw the reasoning the model returns. `thinkingLevel` is the effort the provider is asked to **spend**; this is whether the terminal **shows** it. Off by default: a settled turn that thought leaves one muted `thought for 12s` row in its place, so nothing is hidden silently. |
1732
1841
  | `liveToolOutput` | `true` | Draw up to eight sanitised rows of a **running** tool's output on its card, plus a `no output for Ns` row when the child goes quiet. On by default, unlike `showThinking`: this ADDS the information a long `bash` call otherwise hides, and its cost is bounded by construction — eight rows per call, sixteen calls, whatever the command emits. Turn it off and the card is a single `running` row again, with no store allocated and no recorder attached. |
1733
1842
  | `fullscreen` | `true` | Use the full-screen TUI (still subject to the automatic downgrades). Setting it to `false` opts out permanently; leaving it `true` is *not* a force — only `--fullscreen` / `ARAGON_FULLSCREEN=1` override the heuristics. |
@@ -1805,6 +1914,8 @@ ARAGON_MAX_TOKENS=32000 aragon # env layer, between the flag and the file
1805
1914
  aragon config set maxTokens 32000 # persisted; prints "Set maxTokens = 32000"
1806
1915
  aragon config set maxTokens auto # persisted as null
1807
1916
  aragon config set maxTokens default # back to 64000
1917
+ aragon config set contextWindow 1000000 # the gauge's denominator
1918
+ aragon config set contextWindow auto # back to the model table
1808
1919
  aragon config get maxTokens # "64000" | "auto"
1809
1920
  ```
1810
1921
 
@@ -2137,7 +2248,7 @@ node packages/cli/dist/cli.js --version
2137
2248
  shebang; `node packages/cli/dist/cli.js --version` prints the version.
2138
2249
  - `npm pack -w packages/cli --dry-run --json` lists `dist/cli.js` under `files`.
2139
2250
  - With `ANTHROPIC_API_KEY` set: `aragon` streams a reply, a tool card renders,
2140
- `Esc` aborts mid-run, `/model` switches models, `/settings` saves a key,
2251
+ `Esc` twice interrupts mid-run, `/model` switches models, `/settings` saves a key,
2141
2252
  `Ctrl+C` twice exits.
2142
2253
  - `echo "list files" | node dist/cli.js -p` prints an answer and exits `0`.
2143
2254
  - A missing/invalid key produces a visible, actionable message (never a blank
@@ -2155,7 +2266,7 @@ node packages/cli/dist/cli.js --version
2155
2266
  nothing is truncated to a single line.
2156
2267
  - `--no-hints` on a 20-row terminal in plan mode - the status bar still says
2157
2268
  `PLAN` with no chip on screen.
2158
- - Dismiss a plan card with `Esc`, let the agent resubmit, then press `Esc` twice:
2269
+ - Dismiss a plan card with `Esc`, let the agent resubmit, then close the card and press `Esc` twice within 1.5 seconds:
2159
2270
  the run aborts and the session returns to an idle prompt. Do this one BY HAND
2160
2271
  as well as in a test - the point of the check is whether the exit is findable,
2161
2272
  which no assertion can tell you.
@@ -2163,3 +2274,18 @@ node packages/cli/dist/cli.js --version
2163
2274
  ## License
2164
2275
 
2165
2276
  MIT
2277
+
2278
+
2279
+ ### 输入光标与任务切换验收
2280
+
2281
+ 输入获得应用内焦点时,光标每 500 ms 明暗切换;编辑、移动和恢复焦点后立即亮起。
2282
+ `reducedMotion` 开启时保持常亮,弹层打开时暂停。fullscreen 和 inline 使用相同规则。
2283
+ 单色终端用等列宽的下划线标记,原文相同时改用 `^`,零宽附加码点保留以兼容 Ink。
2284
+ 标记不进入草稿、历史或提交文本;屏幕选择复制会包含当时可见的标记。
2285
+ UTF-16 编辑和 ZWJ/组合字形仍有既有限制,本次修复保证视觉光标定位及相位布局稳定。
2286
+
2287
+ 在 Windows Terminal/PowerShell 与 POSIX TTY 分别验证两种模式,窗口覆盖
2288
+ 120×30、100×20、80×24、60×16:观察空输入、英文、中文、emoji、多行和组合附加符至少三个周期。
2289
+ 检查流式输出不阻止闪烁;单 ESC 不停止任务,两击中断,卡住时第三击强停;菜单关闭不计入两击。
2290
+ 任务结束后保留最终计划,下一条普通消息启动时旧计划消失,新有效计划出现才恢复;显式续跑保留计划。
2291
+ 强停后立即提交,在等待中双 ESC 取消,旧引擎退出后不得自行执行已取消的消息。
@@ -8,6 +8,7 @@
8
8
  * success — that promise resolves even on failure. Success/failure is derived
9
9
  * from the event stream by the reducer (TUI) and the headless writer.
10
10
  */
11
+ import type { PromptOptions, PromptOutcome } from './prompt-options.js';
11
12
  import { ModelRegistry, type AgentEvent, type AgentTool, type Message, type ModelInfo, type ModelRef, type ProviderRegistry, type ThinkingLevel } from '@aragon-agent/core';
12
13
  import type { CliConfig, CompactionConfig, FastConfig, RetryConfig, TeamConfig, ThemeName, TodoConfig } from '../config/schema.js';
13
14
  import { type ConfirmRequest } from '../tools/index.js';
@@ -18,8 +19,9 @@ import type { TeamEvent, TeamSnapshot } from '../team/types.js';
18
19
  import { ProcSupervisor } from '../proc/supervisor.js';
19
20
  import type { ProcEventListener, ServiceSnapshot } from '../proc/types.js';
20
21
  import { type FastStatus } from '../fast/wiring.js';
21
- import type { FastEventListener } from '../fast/types.js';
22
- import type { CompactionEventListener, CompactionSnapshot } from '../compaction/types.js';
22
+ import type { FastEventListener, FastSnapshot } from '../fast/types.js';
23
+ import { ContextMeter, type ContextUsageListener } from '../compaction/meter.js';
24
+ import type { CompactionEventListener, CompactionSnapshot, ContextUsageSnapshot } from '../compaction/types.js';
23
25
  import type { TodoEventListener, TodoSnapshot } from '../todo/types.js';
24
26
  import { type HumanInputGate } from '../tools/human-input.js';
25
27
  import { SkillService, type ApprovalGate } from '../skills/service.js';
@@ -178,6 +180,7 @@ export declare class AgentController {
178
180
  private readonly outputListeners;
179
181
  private readonly fast;
180
182
  private readonly compaction;
183
+ private readonly contextMeter;
181
184
  private readonly procs;
182
185
  /**
183
186
  * The host's notice channel, kept so `prompt()`'s totality has somewhere to
@@ -205,6 +208,7 @@ export declare class AgentController {
205
208
  * abort lands.
206
209
  */
207
210
  private runGen;
211
+ private startupSequence;
208
212
  /**
209
213
  * Set SYNCHRONOUSLY by `abort()` before `agent.abort()` (§3.5.4a guard 1).
210
214
  * Cleared at the next `agent_start`.
@@ -262,21 +266,23 @@ export declare class AgentController {
262
266
  * underlying promise so headless mode can await completion. `agent.prompt()`
263
267
  * never rejects, so callers must not treat resolution as success.
264
268
  */
265
- prompt(text: string): Promise<void>;
269
+ prompt(text: string, options?: PromptOptions): Promise<PromptOutcome>;
266
270
  /**
267
271
  * Wait for the engine to unwind, bounded.
268
272
  *
269
273
  * BOUNDED BECAUSE THE UNBOUNDED CASE IS THE ONE THIS FEATURE IS ABOUT: a tool
270
- * that never settles is exactly why rung two exists, and `Agent.waitForIdle()`
274
+ * that never settles is exactly why the force-stop rung exists, and `Agent.waitForIdle()`
271
275
  * alone would hang here for as long as it hangs there. The budget is the two
272
276
  * graces a wedged `bash` can legitimately spend, plus the executor's own.
273
277
  */
274
278
  private waitForEngineIdle;
275
- /** The pre-round body of `prompt`, unchanged apart from being private. */
279
+ /** Prepare before clearing the plan, then enter the engine without yielding. */
276
280
  private startPrompt;
281
+ /** Cancel pending startup as well as any engine run already in progress. */
277
282
  abort(): void;
283
+ private stopEngine;
278
284
  /**
279
- * Rung two of the Esc ladder: stop the AGENT, whatever it is blocked on
285
+ * Final rung of the Esc ladder: stop the AGENT, whatever it is blocked on
280
286
  * (§3.6 / G3).
281
287
  *
282
288
  * FOUR STEPS, AND THE FOURTH IS AN ABSENCE.
@@ -284,7 +290,7 @@ export declare class AgentController {
284
290
  * 1. `abort()` - everything it does today, unchanged.
285
291
  * 2. `killForeground('force')` - hard-kill every tracked foreground `bash`
286
292
  * child. THIS is what actually unblocks a wedged `await tool.execute`;
287
- * an abort signal a tool ignores is advisory, and rung two exists for
293
+ * an abort signal a tool ignores is advisory, and the force-stop rung exists for
288
294
  * precisely the tools that ignore it.
289
295
  * 3. `runGen += 1` - so `App` can drop the events the engine will keep
290
296
  * emitting while it unwinds (I-7). Without it the view bounces straight
@@ -293,7 +299,7 @@ export declare class AgentController {
293
299
  * `Ctrl+C` stops the SERVICES. That asymmetry is the user's own two
294
300
  * sentences and it is exactly the kind of thing a later reader will
295
301
  * "fix" by making this kill everything - which would mean a user who
296
- * pressed Esc twice to stop a runaway turn also lost the dev server they
302
+ * confirmed a force-stop of a runaway turn also lost the dev server they
297
303
  * had been working against for an hour. It is invariant I-4.
298
304
  *
299
305
  * UNCONDITIONAL ON `bash.background` (P1-6 / D-9). The supervisor is always
@@ -612,6 +618,41 @@ export declare class AgentController {
612
618
  * `subscribeTodos` already set.
613
619
  */
614
620
  subscribeFast(listener: FastEventListener): () => void;
621
+ /**
622
+ * The FIFTH thin forwarder: the current fast-tier snapshot, or `null` when
623
+ * this session never registered a tier at all
624
+ * (web-use-tier-cooperation-and-control-closure §4.2.1).
625
+ *
626
+ * `null` and "a snapshot with `live: false`" are different answers and callers
627
+ * depend on the difference: the first means "there is no tier here", the
628
+ * second means "there is one and it is not usable right now".
629
+ *
630
+ * Deliberately NOT folded into the existing `review_end` event: that shape is
631
+ * consumed by `App` too, and this round promised to be purely additive.
632
+ */
633
+ fastSnapshot(): FastSnapshot | null;
634
+ /**
635
+ * Subscribe to occupancy publications. Returns an unsubscribe function.
636
+ *
637
+ * THE SOLE UPSTREAM OF `ViewState.context` (I-1). The gauge used to have two
638
+ * writers - `turnEnd` and a `contextTokensEstimated` dispatch fired from two
639
+ * places in `App` - which is the entire cause of P0-1: both wrote in the same
640
+ * synchronous fan-out and the second one won, restoring the pre-compaction
641
+ * figure one statement after the bar had correctly fallen. One writer makes
642
+ * that class of bug unrepresentable rather than merely fixed.
643
+ */
644
+ subscribeContextUsage(listener: ContextUsageListener): () => void;
645
+ /**
646
+ * Occupancy right now, re-measuring if the history has moved.
647
+ *
648
+ * `/context` READS THIS AND NOT `getCompactionSnapshot().pressure` (RV-8). The
649
+ * latter returns `offCompactionSnapshot()`'s hardcoded zero pressure when
650
+ * compaction was never registered, so a report built on it would read 0 % for
651
+ * precisely the sessions this feature exists to fix.
652
+ */
653
+ getContextUsage(): ContextUsageSnapshot;
654
+ /** The meter itself, for the wiring's single-instance assertion and tests. */
655
+ getContextMeter(): ContextMeter;
615
656
  /** Whether a `ContextManager` exists at all. Fixed at construction. */
616
657
  isCompactionRegistered(): boolean;
617
658
  /** Whether it will actually fire. Flipped live by `/compact on|off`. */