@aragon-agent/cli 0.6.25 → 0.6.27

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 (48) hide show
  1. package/CHANGELOG.md +104 -0
  2. package/README.md +194 -13
  3. package/dist/agent/reducer.d.ts +22 -0
  4. package/dist/agent/reducer.js +4 -1
  5. package/dist/cli.js +214 -2
  6. package/dist/commands/branch-command.d.ts +61 -0
  7. package/dist/commands/branch-command.js +117 -0
  8. package/dist/commands/builtins.js +48 -6
  9. package/dist/commands/registry.d.ts +28 -0
  10. package/dist/commands/session-command.d.ts +54 -0
  11. package/dist/commands/session-command.js +109 -0
  12. package/dist/config/load.d.ts +15 -0
  13. package/dist/exec/index.js +10 -1
  14. package/dist/logging/logger.d.ts +1 -1
  15. package/dist/proc/terminal-plan.d.ts +173 -0
  16. package/dist/proc/terminal-plan.js +393 -0
  17. package/dist/session/branch-invocation.d.ts +127 -0
  18. package/dist/session/branch-invocation.js +153 -0
  19. package/dist/session/branch-launch.d.ts +79 -0
  20. package/dist/session/branch-launch.js +265 -0
  21. package/dist/session/checkpoint.d.ts +257 -0
  22. package/dist/session/checkpoint.js +512 -0
  23. package/dist/session/cli-commands.d.ts +2 -0
  24. package/dist/session/cli-commands.js +36 -5
  25. package/dist/session/identity.d.ts +72 -0
  26. package/dist/session/identity.js +148 -0
  27. package/dist/session/limits.d.ts +66 -0
  28. package/dist/session/limits.js +66 -0
  29. package/dist/session/persist.d.ts +30 -5
  30. package/dist/session/startup-session.d.ts +107 -0
  31. package/dist/session/startup-session.js +315 -0
  32. package/dist/session/store.d.ts +60 -1
  33. package/dist/session/store.js +95 -6
  34. package/dist/ui/App.d.ts +54 -2
  35. package/dist/ui/App.js +115 -21
  36. package/dist/ui/Header.js +8 -16
  37. package/dist/ui/StatusBar.d.ts +8 -0
  38. package/dist/ui/StatusBar.js +12 -3
  39. package/dist/ui/layout/header-hints.d.ts +68 -0
  40. package/dist/ui/layout/header-hints.js +138 -0
  41. package/dist/ui/layout/session-chip.d.ts +52 -0
  42. package/dist/ui/layout/session-chip.js +64 -0
  43. package/dist/ui/layout/status-layout.d.ts +10 -0
  44. package/dist/ui/layout/status-layout.js +8 -1
  45. package/dist/ui/overlays/HelpOverlay.js +9 -3
  46. package/dist/ui/session-bridge.d.ts +26 -0
  47. package/dist/ui/session-bridge.js +32 -0
  48. package/package.json +2 -2
package/CHANGELOG.md CHANGED
@@ -8,6 +8,46 @@ were written.
8
8
  ## Unreleased
9
9
 
10
10
  ### Added
11
+ - **`Ctrl+L` branches this session into a new terminal - and `Ctrl+R` now
12
+ redraws (KEY BINDING CHANGE).** One keystroke opens a PowerShell (Windows) or
13
+ bash (macOS/Linux) window in the session's current `/cwd`, running
14
+ `aragon --resume <id>` there: the child derives its own id and only ever writes
15
+ its own file, so the TUI you branched from keeps running untouched. The session
16
+ is force-flushed to disk BEFORE anything is spawned, because the child reads
17
+ that file first and the normal checkpoint is debounced. Works mid-run too - the
18
+ child inherits the last settled turn. Platforms: a new Windows Terminal tab
19
+ (`wt -d <cwd> ... --`), a new console window (`cmd /c start`), a Terminal.app
20
+ window via `osascript`, or the first available Linux emulator; each rung carries
21
+ the working directory itself, because `--resume` does not restore one. No
22
+ terminal (SSH, container) prints a pasteable `aragon --resume <id>` instead of
23
+ failing silently. `ARAGON_BRANCH_COMMAND` / `ARAGON_BRANCH_SHELL` override what
24
+ runs and which shell; presses are throttled to one window per two seconds and a
25
+ failed attempt never starts the cooldown. New `/branch` is the same code path.
26
+ The header (top right) now shows `^L branch` next to `^I Index` and `^G more`,
27
+ spending a fixed cell budget: `^I` therefore appears from 52 columns
28
+ (collapsed) / 62 columns (expanded) instead of 72, and on a very narrow terminal
29
+ the branch hint degrades to the bare `^L` before it is dropped.
30
+ **Breaking:** the redraw previously bound to `Ctrl+L` moved to `Ctrl+R` - same
31
+ behaviour, same overlay guard (the settings screen keeps its own `Ctrl+R`).
32
+
33
+ - **A TUI session now has an id, and another terminal can continue it.** Every
34
+ interactive session is written to `~/.aragon-agent/sessions/<id>.json` as it
35
+ runs and shows that id at the right end of the status bar (`id a4k2m9`, or
36
+ `a4k2m9` on a narrow terminal - the id itself is never truncated). Checkpoints
37
+ are written after the first frame, after every settled turn (debounced), on
38
+ `/session pin`, and once on exit; each one is an atomic replace, because a
39
+ second terminal may be reading. `aragon --resume <id|path>` continues a saved
40
+ session under a NEW id and never writes the file it came from, so branching
41
+ while the original TUI keeps running is safe - including
42
+ `aragon exec --resume <tui-id>`, which now writes a session of its own instead
43
+ of overwriting yours. `aragon --save <id>` names this run's id and pins it
44
+ against `sessions prune`. New `/session [copy|pin]` reports the id, the file and
45
+ the branch parent, copies the resume command through the same clipboard door as
46
+ `/copy` (OSC 52 included), and pins the file. A checkpoint taken mid-tool is
47
+ repaired on the saved copy only, so resuming it cannot produce an illegal
48
+ history, and a tool card that was still running is settled so nothing spins
49
+ after a resume. `aragon -p` writes one checkpoint at exit: context only, no
50
+ transcript, because nothing was displayed.
11
51
 
12
52
  - **A model profile can be verified where it is written.** The profile editor
13
53
  gains two action rows: **Test connection** (`Ctrl+R`) and **Discover models**
@@ -23,8 +63,72 @@ were written.
23
63
  describe a model, endpoint or key the user has since edited. The progress line
24
64
  is static text: no new animated site, so `single-spinner` still holds.
25
65
 
66
+ ### Changed
67
+
68
+ - **`aragon sessions list` gained an `origin` column** and shows TUI checkpoints,
69
+ which are managed files now. `sessions prune` defaults to `--origin exec`, so it
70
+ behaves exactly as before for every file that existed before this release; the
71
+ new `--origin tui|all` selects the other writer. `prune` never deletes a pinned
72
+ session or one whose owner process is still alive, and `--all` now means only
73
+ "also consider files with no session metadata" - it can no longer reach a TUI
74
+ checkpoint. `/resume` inside the TUI moves the session to a new id and restarts
75
+ the spend counter, because a new id's file must not bill the previous
76
+ conversation.
77
+
26
78
  ### Fixed
27
79
 
80
+ - **`Ctrl+L` opened a window that failed with a PowerShell `ParserError` on
81
+ Windows.** The script handed to `-EncodedCommand` was the argv joined with
82
+ quotes but with no call operator, and PowerShell reads a quoted string in command
83
+ position as an *expression*: with node installed under `C:\Program Files\...`
84
+ the child was never created - `UnexpectedToken` at column 36 - while the parent
85
+ TUI still reported that the window opened. `powerShellScript` now emits
86
+ `& <argv...>`, which is the only form that runs a word of any shape as a
87
+ command. The failure fallback follows: it is now two labelled lines
88
+ (`PowerShell:` / `cmd.exe :`), because one line cannot be correct for both
89
+ parsers - and the `PowerShell:` line is byte-for-byte the script the window
90
+ receives. A leading `@` can no longer be left bare either, since `@name` is
91
+ PowerShell's splat operator and silently swallows an argument. New
92
+ `powershell-script-parse.test.ts` hands the generated scripts to the real
93
+ Windows PowerShell parser (and pins the old shape to its exact error), so this
94
+ class of defect has a guard instead of a green suite.
95
+ - **A conversation saved mid-tool could be resumed into an illegal history.**
96
+ A `tool_call` with no matching `tool_result` makes the next request fail. The
97
+ checkpoint writer closes every dangling call on the copy it writes and re-checks
98
+ sendability before replacing the file; a history it cannot make sendable is not
99
+ written at all, so the previous good checkpoint survives.
100
+ - **A checkpoint could carry a tool card that never stopped spinning.** A card
101
+ saved while its tool was running kept `ToolCard` animated and kept the
102
+ transcript's settled boundary from ever passing it, which re-rendered the tail
103
+ on every frame for the rest of the resumed session. Cards are now settled on the
104
+ saved copy only, which leaves `/save` (an explicit export you are watching) and
105
+ its documented behaviour untouched.
106
+ - **`aragon exec --resume <tui-id>` overwrote the session it resumed.** The
107
+ write-back id preferred the file's own metadata, so exec took the TUI's id and
108
+ replaced the checkpoint - silent, unrecoverable loss of the conversation still
109
+ running in the other terminal. A TUI file now always yields a fresh id, and the
110
+ same rule covers `--session-id`, which used to return the typed id verbatim.
111
+ - **`sessions prune --origin tui --yes` could delete a running session's
112
+ checkpoint.** The `live` marker was written by the TUI but the process lock it
113
+ is checked against was only ever taken by `exec`, so the liveness guard answered
114
+ "nobody owns this" for exactly the files it was written for. The TUI checkpoint
115
+ writer now holds the session lock for as long as it owns the file, releases it
116
+ on exit, and moves it when `/resume` rotates the id.
117
+ - **`aragon --save <id>` said "already exists" for an id that does not exist.**
118
+ A session directory that cannot be written (read-only home, full disk) was
119
+ reported as a name collision, which sends the user to `--resume <id>` and then
120
+ to "no such session". It now names the directory instead.
121
+ - **A hand-written `/save` file resumed as if it were a branch of itself.**
122
+ `--resume ./notes.json` recorded `notes` as `meta.branchOf` and announced it as
123
+ the parent session, though it is not a session id. Lineage is now recorded only
124
+ for ids this CLI minted, and a collision while deriving a branch id re-mints the
125
+ suffix instead of dropping the branch shape.
126
+ - **`/session` answered `pinned no` right after `/session pin`.** The file was
127
+ marked and the report was not. The identity is now updated with the pin.
128
+ - **An id rotation could write one turn into the old id's file.** For the window
129
+ between `/resume` claiming a new id and the next render, the writer still aimed
130
+ at the previous file while recording the new parent. The rotation now republishes
131
+ the writer's id in the same step.
28
132
  - **`/clear` did not clear the context.** It reset the visible transcript and the
29
133
  todo panel and left `agent.state.messages` - the history that is actually sent,
30
134
  billed, measured by `/context` and compacted - completely untouched. A long
package/README.md CHANGED
@@ -197,7 +197,7 @@ supplying it is frequently the least trusted input in the pipeline.
197
197
  aragon sessions list [--json] [-n 20] [--all]
198
198
  aragon sessions show <id> [--json]
199
199
  aragon sessions rm <id> --yes
200
- aragon sessions prune [--older-than 7] [--dry-run] [--all] --yes
200
+ aragon sessions prune [--older-than 7] [--dry-run] [--all] [--origin exec|tui|all] --yes
201
201
  aragon sessions path
202
202
  ```
203
203
 
@@ -312,19 +312,25 @@ section is the one that would be misled by their omission.
312
312
  **On Windows only Ctrl-C does** — Node does not deliver the other two to
313
313
  listeners, so `taskkill` ends the process outright. Resolve on process exit as
314
314
  well as on `result`, or a killed run hangs your parent process.
315
- 3. **A session created by `aragon exec` and resumed with `aragon --resume`
316
- carries the model's full memory and shows no scrollback** for the turns that
317
- ran headlessly. The reverse direction is fully faithful: a TUI `/save` resumed
318
- by `aragon exec --resume <id>` keeps everything.
319
- 4. **`aragon sessions prune` shares a directory with the TUI's `/save`**, so it
320
- only ever touches sessions `exec` created — files that carry an exec `meta`
321
- block — unless you pass `--all`. `sessions show` and `sessions rm` accept any
322
- id, because you named it.
315
+ 3. **Resuming carries the model's full context, and only the scrollback that
316
+ was actually recorded.** `aragon --resume <id>` continues any saved session
317
+ — a TUI checkpoint, a TUI `/save` export, an `exec` session — under a NEW id,
318
+ with the whole conversation and no transcript replay for turns that ran
319
+ headlessly (`aragon -p`). A TUI checkpoint replays its screen faithfully.
320
+ 4. **`aragon sessions prune` never deletes what you are still using.** It only
321
+ touches sessions this CLI wrote (files with a session metadata block), and it
322
+ defaults to `--origin exec`, so a TUI checkpoint is out of scope unless you
323
+ ask for it with `--origin tui`. `--all` widens "which rows exist" to
324
+ hand-written `/save` files and never reaches a TUI checkpoint; `pinned`
325
+ sessions and files whose owner process is still alive are never deleted.
326
+ `sessions show` and `sessions rm` accept any id, because you named it.
323
327
 
324
328
  ## Usage
325
329
 
326
330
  ```
327
331
  aragon [prompt] Start the interactive TUI (or one-shot with -p / piped stdin)
332
+ aragon --resume <id|path> Continue a saved session under a NEW id (TUI or -p)
333
+ aragon --save <id> Name this session's id and pin it against `sessions prune`
328
334
  aragon -p, --print [prompt] Headless: stream the answer to stdout, then exit
329
335
  aragon exec [prompt] Machine-facing run: JSON events, sessions, permissions, budgets
330
336
  aragon sessions <sub> [id] Manage exec sessions: list | show | rm | prune | path
@@ -397,6 +403,76 @@ Exit codes: `0` success · `1` agent/runtime error · `2` config/usage error ·
397
403
  `3` stopped by a caller-set budget (`aragon exec` only) · `128 + signo` when a
398
404
  signal ends the run (`130` SIGINT, `143` SIGTERM, `129` SIGHUP).
399
405
 
406
+ ## Session identity, resume and branching
407
+
408
+ Every TUI session has a short id - six characters from an alphabet that omits
409
+ `0 o 1 l i`, so it can be read off a screen without a transcription mistake - and
410
+ that id is shown at the right end of the status bar. It is also written to
411
+ `~/.aragon-agent/sessions/<id>.json` as the conversation proceeds, which is what
412
+ lets a second terminal continue the same conversation.
413
+
414
+ **The chip.** `id a4k2m9` when there is room (72 columns and up), `a4k2m9` below
415
+ that, and nothing at all when even the id would crowd the status fields. An id is
416
+ never truncated: half an id is a `--resume` command that cannot work, which is
417
+ worse than no id, and `/session` reports the full id at any width. In the one
418
+ layout where the status row itself is not drawn (a terminal too short for a single
419
+ status line), the chip is absent by design and `/session` is the way to read it.
420
+
421
+ **When it is written.** After the first frame (so the id on screen is resumable
422
+ before the first message), after every settled turn (debounced, so a run that
423
+ restarts itself does not write twice), on `/session pin`, and once on the way out.
424
+ `aragon -p` has no screen and no turn events, so it writes exactly once, at exit.
425
+ Every write replaces the file atomically - this is the first session file in this
426
+ CLI that a reader may be looking at while it is rewritten.
427
+
428
+ **What a checkpoint contains.** The full conversation, the todo list, the model
429
+ and the transcript, plus a `meta` block naming the writer. A turn interrupted
430
+ mid-tool is repaired ON THE COPY: the dangling `tool_call` gets an `isError`
431
+ `tool_result` so the next request is legal, and any tool card still running is
432
+ settled, so a resumed session has nothing left spinning. The live conversation is
433
+ never edited to achieve this.
434
+
435
+ **Branching.** `aragon --resume <id|path>` reads the named session and continues
436
+ it under a **new** id (`a4k2m9-x7f2`, or a fresh id when the file was a
437
+ hand-written `/save` export). The session it came from is not written again by
438
+ this process - not its contents, not its `savedAt` - and `--continue` never points
439
+ at it. That is what makes it safe to branch while the original TUI keeps running.
440
+ (`aragon exec --resume <id>` follows the same rule: it reads your session and
441
+ writes one of its own. `--continue` then names the newest **exec** session, which
442
+ is the new one - it never names the TUI session it read.)
443
+
444
+ Three entry points, three different behaviours, on purpose:
445
+
446
+ | Command | Reads | Writes |
447
+ | --- | --- | --- |
448
+ | `aragon --resume <id\|path>` | the session you name | a NEW branch session; the parent is never touched |
449
+ | `/resume [file]` (inside the TUI) | the file you name | this process moves to a NEW id, and the spend counter restarts |
450
+ | `aragon exec --resume <id\|path>` | the session you name | a new exec session of its own - including when the session you named was written by the TUI |
451
+
452
+ **Naming a session.** `aragon --save <id>` names this run's persistent id and pins
453
+ it: `sessions prune` will never delete it. The value must be an id, not a path
454
+ (`--save ./notes.json` exits 2 and says so); use `/save` inside the TUI to export
455
+ to a file. `aragon --save <id>` refuses an id that already exists, and refuses the
456
+ id you are resuming - either would mean writing over a session you still have.
457
+
458
+ **`/session`.** Bare `/session` reports the id, the file it is written to, the
459
+ branch parent (if any), whether it is pinned, and the two commands worth pasting.
460
+ `/session copy` puts `aragon --resume <id>` on the clipboard through the same door
461
+ `/copy` uses (OSC 52 included, so it works over SSH). `/session pin` writes once
462
+ and marks the file as never-prunable.
463
+
464
+ **Why there is no switch.** Sessions are recorded by default, exactly as
465
+ `aragon exec` already wrote them by default, and there is deliberately no
466
+ "show the id but do not save it" mode: it would leave an id you can copy and
467
+ cannot use. `sessions rm <id> --yes` is the explicit way to delete one, and
468
+ nothing is ever deleted automatically.
469
+
470
+ **Headless checkpoints carry context, not scrollback.** A session continued with
471
+ `aragon -p` records the conversation but no transcript, because nothing was ever
472
+ displayed. `aragon --resume` of such a file inherits the full context and shows an
473
+ empty screen until the next turn - the alternative, inventing entries, would put
474
+ a conversation the user never saw onto their screen.
475
+
400
476
  ## Slash commands (interactive)
401
477
 
402
478
  | Command | Action |
@@ -410,8 +486,9 @@ signal ends the run (`130` SIGINT, `143` SIGTERM, `129` SIGHUP).
410
486
  | `/clear` | Start a new conversation: interrupt the run, archive the old session, clear the context, transcript, queues and todo panel. `/clear screen` only clears the screen |
411
487
  | `/reset` | Alias of `/clear` - one implementation, so the two cannot drift apart |
412
488
  | `/cwd [dir]` | Show / change the tool working directory |
413
- | `/save [file]` | Save the session to JSON |
414
- | `/resume [file]` | Load a saved session |
489
+ | `/save [file]` | Export the session to JSON - a file you keep, unchanged by this feature |
490
+ | `/resume [file]` | Load a saved session. This session moves to a NEW id, and the spend counter restarts |
491
+ | `/session [copy\|pin]` | Report this session's id, file and branch parent; `copy` puts `aragon --resume <id>` on the clipboard; `pin` makes `sessions prune` skip the file forever |
415
492
  | `/plan [on\|off\|status]` | Toggle plan mode - the keyboard-free equivalent of `Shift+Tab` |
416
493
  | `/team [on\|off\|max <n>\|overseer on\|off\|model [main\|fast\|<profile-name>]]` | Team subagents: status and the live roster, the on/off switch, the fan-out width, the dispatch supervisor |
417
494
  | `/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 |
@@ -449,7 +526,8 @@ completes, `Up` / `Down` moves the selection, `Esc` closes it.
449
526
  | `Esc` again after interruption | **Force-stop** a run that is still stopping, kill foreground shell commands and return to `idle`; background services remain running |
450
527
  | `Ctrl+C` | **Copy the selection** when a drag-selection is waiting (release no longer copies by itself). Otherwise: stop the background **services** the agent started, when there are any. It does not arm exit |
451
528
  | `Ctrl+C` ×2 | Exit (first press warns), once no services are running |
452
- | `Ctrl+L` | Redraw the frame |
529
+ | `Ctrl+L` | **Branch this session into a new terminal**: opens a PowerShell (Windows) or bash (macOS/Linux) window at the current `/cwd` and runs `aragon --resume <id>` there, so the branch gets its own id and the session you branched from is untouched. Works while the agent is running - the child inherits the last settled turn. Same code path as `/branch` |
530
+ | `Ctrl+R` | Redraw the frame (moved here from `Ctrl+L`, which is now the branch key) |
453
531
  | `Ctrl+T` | Show / hide thinking (**off by default**) for the entire session |
454
532
  | `Ctrl+O` | Expand / collapse the most recent tool, team, compaction or **service** card — including a diff or a service's log tail |
455
533
  | `PgUp` / `PgDn` | Scroll the transcript a page (full-screen mode) |
@@ -1103,6 +1181,10 @@ all reap the tree on a best-effort basis; there is no daemon, no PID file and no
1103
1181
  reattach across launches. A `SIGKILL` of aragon itself is unrecoverable by
1104
1182
  construction, and if a stop cannot confirm the process is gone it says
1105
1183
  `may have left a detached child` rather than claiming success.
1184
+ **A branch window is the deliberate exception.** `Ctrl+L` (see [Branch into a
1185
+ new terminal](#branch-into-a-new-terminal---ctrll)) starts a process that is
1186
+ not registered with the supervisor, so it is not a service: it is another
1187
+ session the user opened, and it keeps running after this window exits.
1106
1188
 
1107
1189
  From your side there are two keys and one command: `Ctrl+C` stops every live
1108
1190
  service (and does *not* arm exit), `/bg` lists them, and `/bg logs s1` prints a
@@ -1720,6 +1802,99 @@ running, `Ctrl+I` warns instead of queueing. `/skill:project-indexer` and
1720
1802
  > is distinguishable and the binding works. On a terminal that implements
1721
1803
  > neither, `Ctrl+I` simply behaves as `Tab`.
1722
1804
 
1805
+ ### Branch into a new terminal - `Ctrl+L`
1806
+
1807
+ Handing a conversation to a second window used to mean reading six characters off
1808
+ the status bar, switching windows, opening a shell and typing the command - and
1809
+ copying the id by hand is the one step of that chain which reliably goes wrong.
1810
+ `Ctrl+L` replaces it with one keystroke:
1811
+
1812
+ 1. The session is **flushed to disk first**. A child process reads that file as its
1813
+ first act, and the parent's checkpoint is debounced, so the write is forced
1814
+ before anything is spawned (`--resume` reads the file, never the running process).
1815
+ 2. A new terminal opens **in the current `/cwd`** - not in `$HOME` and not in the
1816
+ directory aragon was launched from: `/cwd` can change during a session, and
1817
+ `--resume` does not restore a working directory. Each platform carries the path
1818
+ itself: `wt -d <cwd>` for a Windows Terminal tab, `cmd /c start` for a plain
1819
+ console (which inherits it), and a script that begins with `cd '<cwd>' &&` for
1820
+ macOS Terminal and the Linux emulators.
1821
+ 3. It runs `aragon --resume <id>` there. The child derives its own id, prints
1822
+ `Resumed <id> as a new branch: session <new-id>`, and writes only its own file.
1823
+
1824
+ Both windows are independent from that point: closing either one does not touch the
1825
+ other, `Ctrl+C` in the parent does not stop the branch, and the two transcripts
1826
+ never meet. **A branch window is another session and does not close when this one
1827
+ exits** (the reverse of the rule for supervised services below).
1828
+
1829
+ The platform ladder, in order:
1830
+
1831
+ | Platform | What is tried | Window |
1832
+ | --- | --- | --- |
1833
+ | Windows, inside Windows Terminal (`WT_SESSION` is set) | `wt -w 0 nt -d <cwd> --title "aragon <id>" -- <shell> -NoExit -EncodedCommand <base64>` | a new tab |
1834
+ | Windows, anywhere else | `cmd /d /s /c start "aragon <id>" <shell> -NoExit -EncodedCommand <base64>` | a new console window |
1835
+ | macOS | `osascript` driving Terminal.app | a new Terminal window |
1836
+ | Linux / other POSIX | the first of `$TERMINAL`, `x-terminal-emulator`, `gnome-terminal`, `konsole`, `xfce4-terminal`, `kitty`, `wezterm`, `alacritty`, `xterm` that exists | a new window |
1837
+
1838
+ The shell is `pwsh.exe` then `powershell.exe` on Windows, and `$SHELL` then `bash`
1839
+ then `/bin/sh` elsewhere. Windows receives the command as `-EncodedCommand` (base64
1840
+ of UTF-16LE), because that alphabet has no spaces, quotes or `cmd` metacharacters -
1841
+ it survives `cmd` -> `start` -> PowerShell without a quoting argument. The payload
1842
+ that gets encoded is `& <argv...>`, and **the leading `&` is load-bearing**: in
1843
+ command position PowerShell reads a quoted string as an *expression*, so with a node
1844
+ install under `C:\Program Files\...` the script `'C:\Program Files\nodejs\node.exe'
1845
+ <script>` is a `ParserError ... UnexpectedToken` and no child is ever created - while
1846
+ the parent still reports that the window opened. `&` is PowerShell's call operator:
1847
+ it takes the next word as a command name whatever its shape.
1848
+
1849
+ When no terminal can be opened - an SSH session, a container, a machine with no
1850
+ graphical session - the command opens nothing and prints the fallback instead. On
1851
+ Windows that fallback is **two labelled lines**, because one line cannot be correct
1852
+ for both parsers (`& 'x'` is right for PowerShell and wrong for `cmd.exe`, which
1853
+ keeps the single quotes; `"x"` is right for `cmd.exe` and an expression to
1854
+ PowerShell):
1855
+
1856
+ ```
1857
+ No terminal window could be opened here. Run this in another terminal:
1858
+ PowerShell: & aragon --resume a4k2m9
1859
+ cmd.exe : aragon --resume a4k2m9
1860
+ ```
1861
+
1862
+ Copy the line that matches the terminal you opened. macOS and Linux keep the single
1863
+ line they have always printed:
1864
+
1865
+ ```
1866
+ No terminal window could be opened here. Run this in another terminal:
1867
+ aragon --resume a4k2m9
1868
+ ```
1869
+
1870
+ A `node <dist/cli.js>` build prints the same two lines with the full paths, quoted:
1871
+
1872
+ ```
1873
+ PowerShell: & 'C:\Program Files\nodejs\node.exe' C:\Users\me\AppData\Roaming\npm\node_modules\@aragon-agent\cli\dist\launcher.js --resume a4k2m9
1874
+ cmd.exe : "C:\Program Files\nodejs\node.exe" C:\Users\me\AppData\Roaming\npm\node_modules\@aragon-agent\cli\dist\launcher.js --resume a4k2m9
1875
+ ```
1876
+
1877
+ The `PowerShell:` line is byte-for-byte the script the new window receives, so the
1878
+ fallback can never drift from what the feature actually runs.
1879
+
1880
+ Two environment variables exist because neither belongs in a config file:
1881
+
1882
+ | Variable | Effect |
1883
+ | --- | --- |
1884
+ | `ARAGON_BRANCH_COMMAND` | What the new window executes, as **one executable path** (no arguments - wrap a script in a `.cmd` / `.sh` if you need some). Defaults to the running build: `node <dist/cli.js>`, or `aragon` from `PATH` |
1885
+ | `ARAGON_BRANCH_SHELL` | The shell the new window runs. `C:\Program Files\PowerShell\7\pwsh.exe`, `/usr/bin/fish`, ... |
1886
+
1887
+ Notes worth knowing:
1888
+
1889
+ - The key leaves **one** line in the transcript, `Command: /branch`, and it joins
1890
+ the prompt history. That is the same behaviour as `Ctrl+I` and it is what makes
1891
+ the gesture a real command: `Esc` never has to cancel it, `/help` lists it, and
1892
+ `/branch` typed by hand is byte-for-byte the same path (cooldown included).
1893
+ - Repeated presses are throttled to one window every two seconds; a press that
1894
+ **failed** does not start the cooldown, so the retry is immediate.
1895
+ - `/session copy` is still there for the case this key cannot cover: copying the
1896
+ command for a machine that is not this one.
1897
+
1723
1898
  ### Progressive disclosure
1724
1899
 
1725
1900
  This is the part that makes skills cheap enough to install a lot of:
@@ -2289,7 +2464,7 @@ Everything AragonAgent keeps for you lives under one directory in your home:
2289
2464
  ├── logs/
2290
2465
  │ ├── aragon-2026-07-27.log today, JSON Lines
2291
2466
  │ └── aragon-2026-07-27.1.log rotated at log.maxFileBytes
2292
- ├── sessions/ /save and /resume
2467
+ ├── sessions/ session checkpoints, /save exports, exec sessions
2293
2468
  ├── skills/ aragon skills install
2294
2469
  └── skill-usage.json
2295
2470
  ```
@@ -2519,6 +2694,12 @@ Queue 面板逐条显示待处理消息,数量在展开状态详情中按空
2519
2694
  费用是含子代理、快速模型及压缩的会话估算 USD,恢复会话沿用累计,缺价格按零计,零值不保证免费。
2520
2695
  本轮均速只累计主 Agent 已报告的 output tokens,再除以本轮总耗时(含工具与等待);排除辅助调用,并非瞬时生成速度。结束后保留耗时,速度显示 --。
2521
2696
  `/context` 提供统计范围和完整值。
2697
+ Ctrl+L 把当前会话分支到一个新终端:先强制落盘,再在父 TUI 的 `/cwd` 下打开 PowerShell(Windows)
2698
+ 或 bash(macOS/Linux)窗口,在其中执行 `aragon --resume <id>`,子会话派生自己的新 id,原会话的 id、
2699
+ 历史与文件都不受影响;运行中同样可用,子会话继承到上一个已落定的轮次。重绘迁到 Ctrl+R,
2700
+ 右上角常驻 `^L branch` 提示(窄终端先丢 `^I`、再只留 `^L`)。每次按键会在转录里留下一行
2701
+ `Command: /branch`,并进入提示历史,与 Ctrl+I 完全同构。没有可用终端时只打印一条可粘贴的
2702
+ `aragon --resume <id>`,不会静默失败。
2522
2703
 
2523
2704
  Windows Terminal:在设置 JSON **根级**添加 `"copyOnSelect": false`,把以下条目合并到已有数组:
2524
2705
 
@@ -385,6 +385,14 @@ export interface ViewState {
385
385
  */
386
386
  droppedEntries: number;
387
387
  }
388
+ /**
389
+ * The zero session total.
390
+ *
391
+ * ONE LITERAL FOR TWO SITES (`initialViewState` and `resetConversation`). They
392
+ * used to spell the object out twice, which is how `cacheReadTokens` would have
393
+ * been added to one of them and not the other.
394
+ */
395
+ export declare const EMPTY_USAGE_TOTAL: UsageTotal;
388
396
  export interface ViewStateSeed {
389
397
  /** Start with thinking blocks expanded. Defaults to FALSE, the product default. */
390
398
  thinkingVisible?: boolean;
@@ -490,6 +498,20 @@ export type ViewAction = {
490
498
  type: 'clearTranscript';
491
499
  } | {
492
500
  type: 'resetConversation';
501
+ }
502
+ /**
503
+ * Zero the session's spend WITHOUT touching the conversation
504
+ * (tui-session-id-resume D-10 / R-9 / T-45).
505
+ *
506
+ * A separate action rather than `resetConversation`, because the two mean
507
+ * different things: `/clear` ends the conversation and the spend together,
508
+ * while an in-TUI `/resume` adopts another conversation and starts a NEW id -
509
+ * so the counter must restart, but the entries that were just restored must
510
+ * not be thrown away with it. `context` is left untouched for the reason
511
+ * `resetConversation` records: the meter, not the reducer, owns occupancy.
512
+ */
513
+ | {
514
+ type: 'resetUsage';
493
515
  } | {
494
516
  type: 'setOverlay';
495
517
  overlay: Overlay;
@@ -24,7 +24,7 @@ export const DEFAULT_TOAST_TTL_MS = 2500;
24
24
  * used to spell the object out twice, which is how `cacheReadTokens` would have
25
25
  * been added to one of them and not the other.
26
26
  */
27
- const EMPTY_USAGE_TOTAL = {
27
+ export const EMPTY_USAGE_TOTAL = {
28
28
  inputTokens: 0,
29
29
  outputTokens: 0,
30
30
  cacheReadTokens: 0,
@@ -809,6 +809,9 @@ export function viewReducer(state, action) {
809
809
  // still running. `/clear screen` clears the screen, not the session.
810
810
  compactionEntryId: undefined,
811
811
  };
812
+ case 'resetUsage':
813
+ // The one field, for the one reason: a new id bills from zero (D-10).
814
+ return { ...state, usageTotal: EMPTY_USAGE_TOTAL };
812
815
  case 'resetConversation':
813
816
  return {
814
817
  ...state,