@aragon-agent/cli 0.6.8 → 0.6.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 (78) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/README.md +38 -37
  3. package/dist/agent/controller.d.ts +1 -1
  4. package/dist/agent/reducer.d.ts +7 -113
  5. package/dist/agent/reducer.js +0 -62
  6. package/dist/cli.js +48 -149
  7. package/dist/commands/perf.d.ts +32 -2
  8. package/dist/commands/perf.js +32 -2
  9. package/dist/commands/registry.d.ts +2 -2
  10. package/dist/config/env.js +0 -130
  11. package/dist/config/load.d.ts +0 -2
  12. package/dist/config/load.js +2 -31
  13. package/dist/config/schema.d.ts +1 -14
  14. package/dist/config/schema.js +0 -1
  15. package/dist/config/store.js +2 -0
  16. package/dist/logging/install.d.ts +2 -2
  17. package/dist/logging/install.js +2 -2
  18. package/dist/todo/follow-through.d.ts +0 -4
  19. package/dist/todo/follow-through.js +0 -4
  20. package/dist/todo/limits.d.ts +0 -10
  21. package/dist/todo/limits.js +0 -10
  22. package/dist/todo/prompt.d.ts +0 -15
  23. package/dist/ui/ActivityLine.d.ts +26 -34
  24. package/dist/ui/ActivityLine.js +30 -19
  25. package/dist/ui/App.d.ts +1 -41
  26. package/dist/ui/App.js +66 -128
  27. package/dist/ui/BottomStatusRow.d.ts +1 -3
  28. package/dist/ui/BottomStatusRow.js +4 -8
  29. package/dist/ui/Composer.d.ts +28 -1
  30. package/dist/ui/Composer.js +43 -17
  31. package/dist/ui/Header.d.ts +2 -16
  32. package/dist/ui/Header.js +3 -18
  33. package/dist/ui/Logo.d.ts +1 -17
  34. package/dist/ui/Logo.js +0 -16
  35. package/dist/ui/ModeChip.d.ts +7 -18
  36. package/dist/ui/ModeChip.js +9 -0
  37. package/dist/ui/PromptInput.d.ts +2 -28
  38. package/dist/ui/PromptInput.js +4 -7
  39. package/dist/ui/StatusBar.d.ts +0 -11
  40. package/dist/ui/ToastStack.d.ts +2 -18
  41. package/dist/ui/ToastStack.js +4 -11
  42. package/dist/ui/Transcript.d.ts +5 -71
  43. package/dist/ui/Transcript.js +9 -199
  44. package/dist/ui/entries/AssistantEntry.d.ts +0 -23
  45. package/dist/ui/entries/AssistantEntry.js +3 -4
  46. package/dist/ui/entries/DiffView.d.ts +1 -7
  47. package/dist/ui/entries/DiffView.js +2 -2
  48. package/dist/ui/entries/ToolCard.d.ts +0 -6
  49. package/dist/ui/entries/ToolCard.js +4 -13
  50. package/dist/ui/frame-differ.d.ts +13 -0
  51. package/dist/ui/frame-differ.js +36 -6
  52. package/dist/ui/ink-runtime.d.ts +1 -0
  53. package/dist/ui/ink-runtime.js +21 -0
  54. package/dist/ui/layout/AppShell.d.ts +1 -10
  55. package/dist/ui/layout/AppShell.js +2 -5
  56. package/dist/ui/layout/OverlayFrame.d.ts +1 -25
  57. package/dist/ui/layout/OverlayFrame.js +0 -24
  58. package/dist/ui/layout/ScrollViewport.d.ts +3 -0
  59. package/dist/ui/layout/ScrollViewport.js +17 -5
  60. package/dist/ui/layout/frame.d.ts +0 -22
  61. package/dist/ui/layout/frame.js +0 -36
  62. package/dist/ui/layout/todo-layout.d.ts +1 -3
  63. package/dist/ui/layout/todo-layout.js +7 -8
  64. package/dist/ui/layout/viewport-geometry.d.ts +1 -1
  65. package/dist/ui/layout/viewport-geometry.js +1 -1
  66. package/dist/ui/live-clamp.d.ts +1 -29
  67. package/dist/ui/live-clamp.js +1 -29
  68. package/dist/ui/overlays/HelpOverlay.js +1 -0
  69. package/dist/ui/run-status-row.d.ts +61 -0
  70. package/dist/ui/run-status-row.js +70 -0
  71. package/dist/ui/transcript-text.d.ts +0 -8
  72. package/dist/ui/transcript-text.js +0 -8
  73. package/dist/ui/use-startup-notices.d.ts +1 -57
  74. package/dist/ui/use-startup-notices.js +3 -54
  75. package/dist/ui/use-wheel-routing.d.ts +1 -1
  76. package/package.json +2 -2
  77. package/dist/ui/TodoStrip.d.ts +0 -36
  78. package/dist/ui/TodoStrip.js +0 -32
package/CHANGELOG.md CHANGED
@@ -7,6 +7,15 @@ were written.
7
7
 
8
8
  ## Unreleased
9
9
 
10
+ ### Removed
11
+
12
+ - Removed the inline terminal UI and its automatic downgrade rules. Interactive
13
+ sessions now always use the full-screen UI; small windows show a resize notice.
14
+ - Removed `--fullscreen`, `--no-fullscreen`, `ARAGON_FULLSCREEN`, and the
15
+ `fullscreen` config setting. Old flags are rejected and old stored preferences
16
+ are ignored. Headless commands and piped input retain their existing behavior.
17
+ - Interactive-only screens now reject non-TTY input/output with exit code 2.
18
+
10
19
  ### Added
11
20
 
12
21
  - **`system/init` now lists what this build can do.** A new optional
@@ -72,6 +81,16 @@ were written.
72
81
 
73
82
  ### Fixed
74
83
 
84
+ - **The right-edge scrollbar no longer disappears while output streams.** The
85
+ differential renderer ended every repainted row with `CSI K` (erase to end of
86
+ line). The scrollbar fills the last column of every viewport row, and on
87
+ terminals that keep the cursor on the last column after writing it (Windows
88
+ console hosts) that erase blanked the cell that had just been drawn, so the
89
+ scrollbar vanished whenever a row was redrawn. Rows that already fill the
90
+ terminal width are now repainted without the erase; narrower rows are
91
+ unchanged. `--no-diff-render` was never affected. `/perf` gained a `scrollbar:`
92
+ line for diagnosing visibility and drag problems.
93
+
75
94
  - **`{"type":"interrupt"}` now really does settle the run — on every platform.**
76
95
  The stdin interrupt frame used to raise a SIGINT at our own process. On POSIX
77
96
  that reaches the runner's terminator and the run winds down properly: it
@@ -113,6 +132,14 @@ were written.
113
132
 
114
133
  ### Changed
115
134
 
135
+ - **The run status row moved above the input box and carries the `steer` hint.**
136
+ While a run is active, the spinner, the phrase and `steer / interrupt / exit`
137
+ share one row directly above the input, in place of the idle hint row below it
138
+ (the footer keeps its height). It scrolls with the input; when it leaves the
139
+ viewport the fixed bottom row shows the animation instead, so there is never
140
+ more than one spinner. Terminals under 20 rows and `--no-hints` keep the old
141
+ fixed row. The update notice is also held back for the whole run.
142
+
116
143
  - **Auto-compaction no longer reports compactions that did not happen.** A long
117
144
  unattended run used to print a red `context not compacted #2: nothing_to_drop`
118
145
  card into the transcript. The card was accurate; nobody had asked for it.
package/README.md CHANGED
@@ -362,8 +362,6 @@ below works there too, in either position — `aragon --model m exec "…"` and
362
362
  | `--hints` / `--no-hints` | Show or hide the composer hint row |
363
363
  | `--show-thinking` / `--no-show-thinking` | Draw the model's reasoning blocks in the transcript. Off by default — see [What you see while it runs](#what-you-see-while-it-runs) |
364
364
  | `--live-tool-output` / `--no-live-tool-output` | Show a bounded tail of a running command's output on its card. **On** by default — see [What you see while it runs](#what-you-see-while-it-runs) |
365
- | `--fullscreen` | Force the full-screen TUI, overriding the automatic downgrades |
366
- | `--no-fullscreen` | Force the inline renderer (the 0.2.0 behavior) |
367
365
  | `--no-exit-transcript` | Do not replay the session summary after exiting |
368
366
  | `--plan` / `--no-plan` | Start the session in PLAN mode (read-only research + review) or force BUILD |
369
367
  | `--team` / `--no-team` | Enable or disable team subagents. `--no-team` does not register the `task` tool at all |
@@ -443,8 +441,8 @@ completes, `Up` / `Down` moves the selection, `Esc` closes it.
443
441
  | `Esc` again after interruption | **Force-stop** a run that is still stopping, kill foreground shell commands and return to `idle`; background services remain running |
444
442
  | `Ctrl+C` | Stop the background **services** the agent started, when there are any. It does not arm exit |
445
443
  | `Ctrl+C` ×2 | Exit (first press warns), once no services are running |
446
- | `Ctrl+L` | Redraw the frame (clear the screen in inline mode) |
447
- | `Ctrl+T` | Show / hide thinking (**off by default**). In inline mode it applies to the live entry and everything drawn after it — settled entries are already in your terminal's scrollback and cannot be repainted |
444
+ | `Ctrl+L` | Redraw the frame |
445
+ | `Ctrl+T` | Show / hide thinking (**off by default**) for the entire session |
448
446
  | `Ctrl+O` | Expand / collapse the most recent tool, team, compaction or **service** card — including a diff or a service's log tail |
449
447
  | `PgUp` / `PgDn` | Scroll the transcript a page (full-screen mode) |
450
448
  | `Shift+↑` / `Shift+↓` | Scroll the transcript a line (full-screen mode) |
@@ -465,9 +463,7 @@ scrollbar occupies the terminal's last column (including 40-column windows). Dra
465
463
  to move through history, or click the track to move one page. Messages, the team panel
466
464
  and the input area scroll together; typing or editing returns to the input without
467
465
  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**
469
- (`--no-fullscreen`) history lives in your terminal's native scrollback, printed
470
- once via Ink `<Static>`.
466
+ `PgDn` moves one page down; editing or submitting returns directly to the end.
471
467
 
472
468
  **The mouse wheel is a viewport gesture and nothing else.** Wherever the pointer
473
469
  happens to be — including right on top of the composer — a notch scrolls the
@@ -538,19 +534,22 @@ session, `showThinking: true` (or `--show-thinking`, or `ARAGON_SHOW_THINKING=1`
538
534
  shows them permanently, and `/settings` has a **Show thinking** row next to
539
535
  **Thinking**.
540
536
 
541
- In **inline** mode (`--no-fullscreen`) the marker reads `✱ thought for 12s` with
542
- no key hint, and the toast on `Ctrl+T` says `Thinking shown for new output.`
543
- Settled entries there have already been printed into your terminal's own
544
- scrollback, which cannot be repainted — so the key applies to the live entry and
545
- to everything drawn after it, and the row does not offer what it cannot deliver.
546
-
547
- **There is something to look at while it thinks.** One row above the composer,
548
- for as long as a run is in flight:
537
+ **There is something to look at while it thinks.** One row directly above the
538
+ input box, for as long as a run is in flight, with the keys that act on the run
539
+ beside it (it takes the place of the idle hint row below the box, so starting a
540
+ run never moves the layout):
549
541
 
550
542
  ```
551
- ⠋ Percolating…
543
+ ⠋ Percolating… · ⏎ steer · esc×2 interrupt · ctrl+c×2 exit PLAN
552
544
  ```
553
545
 
546
+ The row scrolls with the input box. On a narrow terminal whole clauses are dropped
547
+ from the end (`exit` first) and only the phrase is shortened with an ellipsis, so
548
+ `steer` and `interrupt` are never cut mid-word. When you scroll history until the
549
+ input box is out of view, the animation moves to the fixed row at the bottom, so
550
+ there is always exactly one spinner on screen. Terminals shorter than 20 rows, and
551
+ `--no-hints`, keep the animation on that fixed row instead.
552
+
554
553
  The word rotates every four seconds. It carries no clock and no token count on
555
554
  purpose: the status bar one row below already has both, under the same
556
555
  condition, driven by the same ticker. Under `reducedMotion` the spinner is a
@@ -637,17 +636,28 @@ On exit the session is replayed into the normal buffer as plain text so the
637
636
  conversation survives leaving the screen (`--no-exit-transcript` opts out;
638
637
  `/save` still exports the full JSON).
639
638
 
640
- It downgrades to the inline renderer automatically when stdout is not a TTY,
641
- `TERM=dumb`, a CI environment variable is set, or the terminal is under 12 rows
642
- or 40 columns. `--fullscreen` overrides all of those except the non-TTY check —
643
- writing screen-control sequences into a pipe or a redirected file is never safe.
644
- `--no-fullscreen`, `ARAGON_FULLSCREEN=0`, or `aragon config set fullscreen false`
645
- opt out permanently.
639
+ Interactive sessions always use the full-screen TUI, including under `TERM=dumb`
640
+ or CI. Windows smaller than 40 columns or 12 rows show a size notice and recover
641
+ when enlarged, preserving the session and draft.
642
+
643
+ The layout switches `--fullscreen`, `--no-fullscreen`, `ARAGON_FULLSCREEN` and
644
+ `fullscreen` configuration have been removed. Old command-line flags are errors;
645
+ old config fields and environment variables are ignored.
646
+
647
+ Interactive screens require both stdin and stdout to be TTYs. `aragon config`
648
+ without a TTY exits with code 2; use `aragon config list` or `aragon config set`.
649
+ `-p`, `exec`, piped input and prompt-based redirected output retain their
650
+ headless behavior and never enter the alternate screen.
646
651
 
647
652
  The alternate screen uses an application scrollbar rather than the terminal's
648
653
  native scrollback. Windows Terminal and conhost have different mouse/VT capabilities;
649
654
  the PowerShell version alone does not determine dragging support. If the host cannot
650
655
  deliver SGR mouse reports, the scrollbar remains visible and keyboard scrolling works.
656
+ The differential renderer never erases the last column: a row that already fills the
657
+ terminal width is repainted without a trailing erase-to-end-of-line, which on Windows
658
+ console hosts would otherwise wipe the scrollbar cell on every redrawn row. `/perf`
659
+ prints a `scrollbar:` line (column, mouse state, whether a full frame was confirmed,
660
+ thumb position, glyph tier) to tell "cannot see it" from "cannot drag it".
651
661
  `--no-mouse` provides that keyboard-only path; `--no-diff-render` disables output
652
662
  differencing without disabling the scrollbar. A mid-session resize below 40 columns
653
663
  or 12 rows displays a size hint and preserves the draft for restoration.
@@ -694,13 +704,6 @@ rung. `--no-render-governor` (or `renderGovernor: false`, or
694
704
  `ARAGON_RENDER_GOVERNOR=0`) turns the adaptation off entirely; the output is
695
705
  identical, just heavier under load.
696
706
 
697
- **Inline mode** (`--no-fullscreen`, `TERM=dumb`, CI, a very small terminal) gets
698
- one extra guarantee. Ink repaints the *entire session history* on every frame
699
- once a live entry grows taller than the terminal, which is a genuine freeze
700
- rather than a slowdown. The live region is now clamped to stay below that
701
- threshold, with a `... N earlier lines` marker naming what is deferred; the
702
- entry prints **in full** into your scrollback the moment it finishes.
703
-
704
707
  ## Plan mode
705
708
 
706
709
  `aragon` has two postures, and `Shift+Tab` switches between them. `/plan` does
@@ -758,8 +761,7 @@ still exits the session outright.
758
761
  ### Where the mode is shown
759
762
 
760
763
  The status bar always names a non-default mode (`PLAN`, or `PLAN → BUILD` while
761
- a switch is pending), including on short terminals, with `--no-hints`, and in
762
- inline mode. The composer additionally shows a `PLAN` chip on its hint row and
764
+ a switch is pending), including on short terminals and with `--no-hints`. The composer additionally shows a `PLAN` chip on its hint row and
763
765
  tints its border, where there is room for it.
764
766
 
765
767
  ### Switching mid-run
@@ -830,8 +832,8 @@ for a question, or for a search with no work attached — and a one-item list
830
832
  against an empty plan is refused outright. A session that never plans has
831
833
  byte-identical layout to a build without this feature.
832
834
 
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:
835
+ **The list shows the current task's plan.** Starting a new task or explicitly
836
+ clearing the list removes this live view while preserving conversation history:
835
837
 
836
838
  - `/clear` and `/todo clear` take the plan off the screen. Neither touches the
837
839
  conversation, so the model still knows what it was doing and its next update
@@ -844,7 +846,7 @@ clearing the list removes this live view while preserving conversation history:
844
846
  when it starts successfully. The completed run keeps its final plan visible.
845
847
  `/todo continue` and automatic continuation preserve the plan and its timestamp;
846
848
  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.
849
+ plan. Until a valid new `todo_write`, the rail and count stay hidden.
848
850
  Startup failure or cancellation keeps the old plan. History cards remain available.
849
851
  - During a live run, interrupt before `/reset` or `/resume`. A conversation switch
850
852
  while startup is waiting cancels that pending request and returns the view to idle.
@@ -1810,7 +1812,7 @@ env / `.env` → CLI flags**.
1810
1812
  `ARAGON_CONTEXT_WINDOW` (a number, or `auto`; the denominator the context
1811
1813
  gauge measures against),
1812
1814
  `ARAGON_THEME`,
1813
- `ARAGON_FULLSCREEN`, `ARAGON_PLAN` (`1` starts in plan mode),
1815
+ `ARAGON_PLAN` (`1` starts in plan mode),
1814
1816
  `ARAGON_TEAM` (`0` disables team subagents), `ARAGON_TEAM_MAX` (fan-out width),
1815
1817
  `ARAGON_TODO` (`0` disables todo planning entirely),
1816
1818
  `ARAGON_TODO_FOLLOW` (`notify` / `auto` / `off`),
@@ -1839,7 +1841,6 @@ env / `.env` → CLI flags**.
1839
1841
  | `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). |
1840
1842
  | `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. |
1841
1843
  | `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. |
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. |
1843
1844
  | `exitTranscript` | `true` | Replay a plain-text session summary after exiting (full-screen only). |
1844
1845
  | `transcriptWindow` | `1000` | How far back you can scroll, clamped to `[50, 20000]`. Entries beyond it collapse into one line. It is no longer a rendering budget: the viewport is virtualised, so off-screen entries are not laid out at all. |
1845
1846
  | `transcriptRetain` | `1000` | Entries kept in memory, clamped to `[200, 20000]` and raised to `transcriptWindow` if the two conflict — with a startup notice saying so, because retaining less than you can scroll to would otherwise make part of the horizon quietly unreachable. Older entries are dropped, counted, and reported by `/perf` and by the exit replay. |
@@ -1857,7 +1858,7 @@ env / `.env` → CLI flags**.
1857
1858
  | `team.dispatchTimeoutMs` | `900000` | The whole dispatch's wall clock, clamped to `[60000, 3600000]`. |
1858
1859
  | `team.maxTurnsPerSubagent` | `24` | Runaway-loop cap, clamped to `[4, 100]`. A subagent stopped here is reported as `stopped: turn cap reached`, with whatever it had produced. |
1859
1860
  | `todo.enabled` | `true` | Master switch for todo planning. `false` means the `todo_write` tool is never registered — `/todo on` in such a session saves the setting for next launch and says so, rather than advertising a tool that is not there. |
1860
- | `todo.panel` | `true` | Render the right-hand rail (and, in inline mode, the one-row plan strip). Independent of `todo.enabled` on purpose: a screen-reader user wants the planning discipline without the column, and the system prompt varies one sentence accordingly. |
1861
+ | `todo.panel` | `true` | Render the right-hand rail. Independent of `todo.enabled` on purpose: a screen-reader user wants the planning discipline without the column, and the system prompt varies one sentence accordingly. |
1861
1862
  | `todo.followThrough` | `"notify"` | What happens when a run ends with steps left. `notify` says so and stops; `auto` continues the plan after a 3-second grace window, bounded by two structural limits (one fruitless attempt, 25 continuations per plan) that are deliberately **not** settings; `off` is silent. An unrecognized value falls back to `notify`. |
1862
1863
  | `retry.enabled` | `true` | Retry a failed provider call at all. Unlike `team.enabled` / `todo.enabled` this decides nothing at construction — the policy lives on the provider registry, so `/retry on` / `/retry off` takes effect **in the running session**, subagents included. |
1863
1864
  | `retry.maxRetries` | `10` | Retries **after** the first attempt, clamped to `[0, 20]`. `0` is the kill switch and is a real value here, not a typo for the default. The 20 is a hard ceiling enforced twice — on the config value and again inside the engine — so hand-editing this file cannot raise it. |
@@ -61,7 +61,7 @@ export interface ControllerDeps {
61
61
  humanInput?: HumanInputGate;
62
62
  /**
63
63
  * Whether this session could EVER render the todo rail (todo-plan-execution
64
- * §3.7 / P1-6). Full-screen and interactive; `-p` and inline mode are false.
64
+ * §3.7 / P1-6). Full-screen and interactive; headless calls are false.
65
65
  *
66
66
  * It varies ONE SENTENCE of the `<todo_planning>` block, and nothing else.
67
67
  * Telling the model "the user sees this list in a panel beside the
@@ -115,20 +115,7 @@ export type Entry = {
115
115
  kind: 'notice';
116
116
  level: NoticeLevel;
117
117
  text: string;
118
- }
119
- /**
120
- * A team dispatch (team-subagents §5.3). JSON-SERIALIZABLE by construction, so
121
- * `/save` and `/resume` need no format change.
122
- *
123
- * `active: true` is normalized away ON LOAD, in `session/persist.ts`, not here
124
- * (P1-5). A session saved mid-dispatch would otherwise resume with a card that
125
- * claims to be running while nothing is: it spins forever, and because
126
- * `Transcript`'s settled boundary is MONOTONIC an entry that never settles
127
- * never reaches `<Static>` and is re-rendered on every frame for the rest of
128
- * the session. The reducer never legitimately sees a stale-active entry — only
129
- * the file does — which is why the fix belongs in the load path.
130
- */
131
- | {
118
+ } | {
132
119
  id: string;
133
120
  kind: 'team';
134
121
  dispatchId: string;
@@ -137,20 +124,7 @@ export type Entry = {
137
124
  aborted: boolean;
138
125
  durationMs?: number;
139
126
  active: boolean;
140
- }
141
- /**
142
- * The turn's todo checklist (todo-plan-execution §3.8). ONE per user turn,
143
- * rewritten in place — a 7-item list updated twice per item would otherwise be
144
- * 14 near-identical tool cards in the one surface the user reads most.
145
- *
146
- * JSON-SERIALIZABLE by construction, so `/save` needs no format change.
147
- *
148
- * `live: true` is normalized away ON LOAD, in `session/persist.ts`, for the
149
- * same reason the team entry's `active` is (C-5): `Transcript`'s settled
150
- * boundary is MONOTONIC, so an entry that never settles never reaches
151
- * `<Static>` and is re-rendered on every frame for the rest of the session.
152
- */
153
- | {
127
+ } | {
154
128
  id: string;
155
129
  kind: 'todo';
156
130
  items: TodoItem[];
@@ -165,21 +139,7 @@ export type Entry = {
165
139
  * card this session produced.
166
140
  */
167
141
  interrupted?: boolean;
168
- }
169
- /**
170
- * The turn's retry story (llm-api-retry-backoff §6.4). ONE per turn, REWRITTEN
171
- * IN PLACE — ten retries rendered as ten notices would bury the transcript in
172
- * the one moment the user most needs it legible.
173
- *
174
- * JSON-SERIALIZABLE by construction, so `/save` needs no format change.
175
- *
176
- * `phase: 'waiting' | 'retrying'` is normalized away ON LOAD, in
177
- * `session/persist.ts`, exactly as the team entry's `active` and the todo
178
- * card's `live` are (R-10): `Transcript`'s settled boundary is MONOTONIC, so a
179
- * card that never settles never reaches `<Static>` and is re-rendered on every
180
- * frame for the rest of the session.
181
- */
182
- | {
142
+ } | {
183
143
  id: string;
184
144
  kind: 'retry';
185
145
  /** 1-based retry index of the LATEST attempt this card has seen. */
@@ -195,22 +155,7 @@ export type Entry = {
195
155
  /** Filled on settle. */
196
156
  totalRetries?: number;
197
157
  elapsedMs?: number;
198
- }
199
- /**
200
- * One fast-model review (fast-model-tier §5.4). ONE CARD PER REVIEW, never
201
- * rewritten across reviews — a critique is a single event, unlike the todo
202
- * card's per-turn checklist.
203
- *
204
- * JSON-SERIALIZABLE by construction, so `/save` needs no format change.
205
- *
206
- * `live: true` is normalized away ON LOAD, in `session/persist.ts`, next to
207
- * `team.active`, `todo.live` and the retry card's phase, and for the identical
208
- * reason (C-5): `Transcript`'s settled boundary is MONOTONIC, so an entry that
209
- * never settles never reaches `<Static>` and is re-rendered on every frame for
210
- * the rest of the session. The reducer never legitimately sees a stale-live
211
- * entry — only the file does.
212
- */
213
- | {
158
+ } | {
214
159
  id: string;
215
160
  kind: 'fast';
216
161
  /** 1-based within the session, so the card can name itself. */
@@ -224,27 +169,7 @@ export type Entry = {
224
169
  turn: number;
225
170
  durationMs?: number;
226
171
  live: boolean;
227
- }
228
- /**
229
- * One context compaction (context-auto-compaction §5.5). ONE CARD PER
230
- * COMPACTION, rewritten once when the call returns — like the fast card, and
231
- * unlike the todo card's per-turn checklist.
232
- *
233
- * JSON-SERIALIZABLE by construction, so `/save` and `/resume` need no format
234
- * change.
235
- *
236
- * `live: true` is normalized away ON LOAD, in `session/persist.ts`, next to
237
- * `team.active`, `todo.live`, the retry card's phase and `fast.live`, and for
238
- * the identical reason (C-8): `Transcript`'s settled boundary is MONOTONIC, so
239
- * an entry that never settles never reaches `<Static>` and is re-rendered on
240
- * every frame for the rest of the session.
241
- *
242
- * EVERY STRING ON THIS CARD DESCRIBES THE HISTORY, NEVER THE SCREEN (P2-9).
243
- * `/clear` empties the transcript view but not `messages`, so a compaction can
244
- * legitimately fire against a visually empty transcript and report
245
- * "112 -> 9 messages" with nine visible rows.
246
- */
247
- | {
172
+ } | {
248
173
  id: string;
249
174
  kind: 'compaction';
250
175
  /** 1-based within the session, so the card can name itself. */
@@ -275,29 +200,7 @@ export type Entry = {
275
200
  charsRemoved: number;
276
201
  };
277
202
  live: boolean;
278
- }
279
- /**
280
- * One supervised background service (background-service-supervision §3.10).
281
- *
282
- * JSON-SERIALIZABLE by construction, so `/save` needs no format change.
283
- *
284
- * IT IS THE ONE LIVE CARD THAT DOES **NOT** BLOCK THE SETTLED BOUNDARY ONCE IT
285
- * IS UP (D-11 / P0-4). Every other live clause in
286
- * `Transcript.tsx::computeSettledCount` is bounded by an OPERATION — a
287
- * dispatch, a turn, one LLM call, one countdown — and each carries a comment
288
- * saying that an entry which never settles is re-rendered on every frame for
289
- * the rest of the session. A service is bounded by the USER'S INTENT: a dev
290
- * server left up for an hour is the normal case. So only `starting` blocks
291
- * (bounded by `readyTimeoutMs`), a `ready`/`running` card commits its body to
292
- * `<Static>`, and a later terminal transition appends a NEW one-row entry
293
- * rather than trying to rewrite a card `<Static>` has already printed.
294
- *
295
- * `terminal: true` marks that second entry. `session/persist.ts` rewrites any
296
- * restored non-terminal card to `stopped`, next to `team.active`, `todo.live`,
297
- * the retry phase, `fast.live` and `compaction.live`, and for the identical
298
- * reason.
299
- */
300
- | {
203
+ } | {
301
204
  id: string;
302
205
  kind: 'service';
303
206
  /** `s1`, `s2`, ... — the id the model quotes back. */
@@ -677,16 +580,7 @@ export type ViewAction = {
677
580
  } | {
678
581
  type: 'serviceUpdate';
679
582
  service: ServiceSnapshot;
680
- }
681
- /**
682
- * A service reached a terminal state.
683
- *
684
- * IT APPENDS A NEW ONE-ROW ENTRY rather than rewriting the card (D-11). The
685
- * card may already have been printed into `<Static>`, and `<Static>` cannot
686
- * un-print — so any design that rewrites a printed card is wrong whatever it
687
- * claims. A second event at a second time is also simply more honest.
688
- */
689
- | {
583
+ } | {
690
584
  type: 'serviceEnd';
691
585
  service: ServiceSnapshot;
692
586
  };
@@ -382,31 +382,6 @@ function settleRetryCard(state, entries, phase) {
382
382
  retryEntryId: undefined,
383
383
  };
384
384
  }
385
- /**
386
- * EVERY PATH THAT REACHES `status: 'idle'` RELEASES THE LIVE TAIL (D-35 / AC-40).
387
- *
388
- * `runEnd` finalizes only the streaming ASSISTANT entry and `abortMark` maps only
389
- * assistant entries, so neither touches a tool entry stranded at
390
- * `status: 'running'` -- the engine's own idle watchdog, a provider error mid-call
391
- * and `Esc` all produce one. Today that costs a single spinner row. With a tail
392
- * attached it costs a permanent multi-row card whose stall counter is frozen at
393
- * its last value, because the 200 ms ticker that feeds `nowSec` is torn down the
394
- * instant `state.status` leaves `running` -- and, worse, `computeSettledCount`
395
- * breaks on a non-settled tool entry, so that card pins `Transcript`'s MONOTONIC
396
- * settled boundary and re-renders the tail every frame for the rest of the
397
- * session. The rule and the stakes are `settleRetryCard`'s, verbatim.
398
- *
399
- * THE STATUS IS DELIBERATELY NOT CHANGED. That is a different decision with
400
- * different consequences for `bashBadge` and for the run's own bookkeeping, and
401
- * it is not this round's to make. Only the tail is released.
402
- *
403
- * `liveSeq` is BUMPED rather than cleared, and that is not bookkeeping: clearing
404
- * `live` is a non-append mutation, and with the status untouched nothing else in
405
- * the tool revision moves -- so without the bump the height cache would keep
406
- * serving the multi-row measurement for a card that now draws one row (I-L3-1).
407
- * Entries with no tail are returned by identity, which is what keeps
408
- * `EntryView`'s memo boundary intact for the rest of the transcript.
409
- */
410
385
  function releaseLiveTails(entries) {
411
386
  let touched = false;
412
387
  const next = entries.map((e) => {
@@ -1218,22 +1193,6 @@ export function viewReducer(state, action) {
1218
1193
  entries: mapEntry(state.entries, known, (e) => e.kind === 'retry' ? { ...e, phase: 'retrying', resumeAt: undefined } : e),
1219
1194
  };
1220
1195
  }
1221
- /**
1222
- * Rewind the view for a mid-stream restart.
1223
- *
1224
- * THE ONLY ACTION IN THIS REDUCER THAT SHRINKS `entries`, and it is safe for
1225
- * one specific reason (§6.4 / AC-22). `Transcript` clamps its settled boundary
1226
- * with a MONOTONIC high-water mark: if `highWater` ever exceeded
1227
- * `entries.length` the clamp would mark EVERYTHING settled — including the
1228
- * still-streaming assistant entry — and `<Static>` would duplicate it.
1229
- *
1230
- * It cannot, and the ORDERING is why: the entries removed here are
1231
- * `kind: 'tool'` cards created by `toolCallStart` AFTER `turnStart` created the
1232
- * streaming assistant entry, so they sit strictly above it in the array; and
1233
- * the boundary scan already breaks at that entry. Removing only entries above
1234
- * the boundary is the invariant. A future action that removed a SETTLED entry
1235
- * would break `<Static>` and nothing would report it.
1236
- */
1237
1196
  case 'streamRestart': {
1238
1197
  const discarded = new Set(action.discardedToolCallIds);
1239
1198
  const entries = state.entries.filter((e) => !(e.kind === 'tool' && discarded.has(e.toolCallId)));
@@ -1326,18 +1285,6 @@ export function viewReducer(state, action) {
1326
1285
  droppedEntries: appended.droppedEntries,
1327
1286
  };
1328
1287
  }
1329
- /**
1330
- * Rewrite the LIVE card in place.
1331
- *
1332
- * SAFE ONLY BECAUSE OF D-11'S OTHER HALF: a `ready` / `running` card has
1333
- * reached `<Static>` and cannot be re-printed, so `serviceEnd` appends a new
1334
- * entry instead of routing through here. What this handles is the window in
1335
- * which the card is still in the live region — `starting`, and the one
1336
- * transition out of it — plus the output tail while it is there.
1337
- *
1338
- * The lookup is by `serviceId` and SKIPS terminal entries, so a stray update
1339
- * cannot rewrite the one-row record `serviceEnd` appended.
1340
- */
1341
1288
  case 'serviceUpdate': {
1342
1289
  const target = findServiceEntryId(state, action.service.id);
1343
1290
  if (!target)
@@ -1347,15 +1294,6 @@ export function viewReducer(state, action) {
1347
1294
  entries: mapEntry(state.entries, target, (e) => e.kind === 'service' ? { ...e, ...serviceFields(action.service) } : e),
1348
1295
  };
1349
1296
  }
1350
- /**
1351
- * A service reached a terminal state.
1352
- *
1353
- * TWO WRITES, AND BOTH ARE REQUIRED. The live card is settled in place so
1354
- * `computeSettledCount` can move past it and `estimateEntryRows` stops
1355
- * charging for a tail nothing will add to; the NEW one-row entry is what the
1356
- * user actually reads, because the card may already be printed into
1357
- * `<Static>` and `<Static>` cannot un-print (D-11 / R-13).
1358
- */
1359
1297
  case 'serviceEnd': {
1360
1298
  const target = findServiceEntryId(state, action.service.id);
1361
1299
  const settled = target