@aragon-agent/cli 0.6.3 → 0.6.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +105 -0
- package/README.md +95 -2
- package/dist/agent/controller.d.ts +119 -0
- package/dist/agent/controller.js +275 -1
- package/dist/agent/reducer.d.ts +69 -0
- package/dist/agent/reducer.js +100 -0
- package/dist/agent/system-prompt.d.ts +14 -0
- package/dist/agent/system-prompt.js +2 -0
- package/dist/cli.js +84 -22
- package/dist/commands/builtins.js +92 -1
- package/dist/compaction/child.js +5 -0
- package/dist/compaction/command.js +14 -1
- package/dist/compaction/compactor.d.ts +34 -0
- package/dist/compaction/compactor.js +158 -37
- package/dist/compaction/types.d.ts +36 -0
- package/dist/compaction/wiring.d.ts +9 -0
- package/dist/compaction/wiring.js +22 -1
- package/dist/config/env.d.ts +1 -1
- package/dist/config/env.js +48 -1
- package/dist/config/load.d.ts +11 -0
- package/dist/config/load.js +39 -1
- package/dist/config/schema.d.ts +85 -0
- package/dist/config/schema.js +33 -0
- package/dist/config/store.js +21 -1
- package/dist/input/limits.d.ts +72 -0
- package/dist/input/limits.js +63 -0
- package/dist/input/paste-parse.d.ts +71 -0
- package/dist/input/paste-parse.js +95 -0
- package/dist/input/stdin-filter.d.ts +81 -0
- package/dist/input/stdin-filter.js +379 -0
- package/dist/logging/install.d.ts +2 -0
- package/dist/logging/install.js +53 -1
- package/dist/proc/classify.d.ts +52 -0
- package/dist/proc/classify.js +143 -0
- package/dist/proc/kill-tree.d.ts +42 -0
- package/dist/proc/kill-tree.js +119 -0
- package/dist/proc/limits.d.ts +84 -0
- package/dist/proc/limits.js +84 -0
- package/dist/proc/log-ring.d.ts +68 -0
- package/dist/proc/log-ring.js +102 -0
- package/dist/proc/prompt.d.ts +24 -0
- package/dist/proc/prompt.js +49 -0
- package/dist/proc/readiness.d.ts +102 -0
- package/dist/proc/readiness.js +219 -0
- package/dist/proc/supervisor.d.ts +103 -0
- package/dist/proc/supervisor.js +465 -0
- package/dist/proc/types.d.ts +175 -0
- package/dist/proc/types.js +15 -0
- package/dist/session/persist.js +36 -1
- package/dist/tools/bash-tool.d.ts +51 -2
- package/dist/tools/bash-tool.js +321 -51
- package/dist/tools/fs-tools.d.ts +17 -0
- package/dist/tools/index.d.ts +55 -2
- package/dist/tools/index.js +39 -1
- package/dist/tools/proc-tools.d.ts +25 -0
- package/dist/tools/proc-tools.js +102 -0
- package/dist/ui/App.d.ts +15 -2
- package/dist/ui/App.js +256 -7
- package/dist/ui/Composer.d.ts +54 -1
- package/dist/ui/Composer.js +50 -6
- package/dist/ui/PromptInput.d.ts +49 -3
- package/dist/ui/PromptInput.js +184 -52
- package/dist/ui/StatusBar.d.ts +19 -0
- package/dist/ui/StatusBar.js +5 -2
- package/dist/ui/Transcript.js +29 -1
- package/dist/ui/composer-limits.d.ts +31 -0
- package/dist/ui/composer-limits.js +50 -0
- package/dist/ui/composer-rows.d.ts +73 -0
- package/dist/ui/composer-rows.js +201 -0
- package/dist/ui/editor-reducer.d.ts +37 -0
- package/dist/ui/editor-reducer.js +88 -12
- package/dist/ui/entries/ServiceCard.d.ts +66 -0
- package/dist/ui/entries/ServiceCard.js +116 -0
- package/dist/ui/entries/UserEntry.d.ts +17 -3
- package/dist/ui/entries/UserEntry.js +34 -5
- package/dist/ui/layout/budget.d.ts +34 -6
- package/dist/ui/layout/budget.js +32 -7
- package/dist/ui/layout/virtual-window.js +51 -1
- package/dist/ui/overlays/HelpOverlay.js +3 -0
- package/dist/ui/overlays/PlanReviewOverlay.js +10 -2
- package/dist/ui/overlays/QuestionOverlay.js +5 -1
- package/dist/ui/overlays/SettingsScreen.js +14 -1
- package/dist/ui/paste-frames.d.ts +87 -0
- package/dist/ui/paste-frames.js +112 -0
- package/dist/ui/paste-tokens.d.ts +90 -0
- package/dist/ui/paste-tokens.js +147 -0
- package/dist/ui/screen.d.ts +16 -0
- package/dist/ui/screen.js +45 -2
- package/dist/ui/selection/selection-controller.d.ts +1 -1
- package/dist/ui/transcript-text.js +49 -0
- package/dist/ui/use-wheel-routing.d.ts +1 -1
- package/package.json +2 -2
- package/dist/input/stdin-mouse-filter.d.ts +0 -47
- package/dist/input/stdin-mouse-filter.js +0 -153
package/CHANGELOG.md
CHANGED
|
@@ -9,6 +9,111 @@ were written.
|
|
|
9
9
|
|
|
10
10
|
### Added
|
|
11
11
|
|
|
12
|
+
- **The composer understands pasting.** Until now it did not have the concept at
|
|
13
|
+
all: every byte the terminal delivered on stdin was a keystroke, and a
|
|
14
|
+
clipboard block is thousands of bytes arriving in an unpredictable number of
|
|
15
|
+
chunks. Three separate failures fell out of that one omission, and on Windows
|
|
16
|
+
you hit all three at once.
|
|
17
|
+
|
|
18
|
+
A carriage return that landed alone in a chunk **sent the half-finished
|
|
19
|
+
message** — a fifteen-line paste could arrive as a dozen separate messages,
|
|
20
|
+
interleaved with the agent's replies. A carriage return in the middle of a
|
|
21
|
+
chunk was inserted into the draft as a literal `\r`, which the terminal obeyed
|
|
22
|
+
by **overwriting the row it was drawn on**. And a paste that survived both grew
|
|
23
|
+
the composer to the height of the pasted text, pushing the transcript out of a
|
|
24
|
+
frame whose row accounting still believed the composer was three rows tall.
|
|
25
|
+
|
|
26
|
+
Pasting now works the way it reads:
|
|
27
|
+
|
|
28
|
+
- **A paste never submits.** Line breaks become buffer newlines, exactly one
|
|
29
|
+
per source line break, `CRLF` and lone `CR` alike.
|
|
30
|
+
- **A short paste appears verbatim.** At or below 6 lines and 400 characters it
|
|
31
|
+
is inserted as-is, indentation intact, and renders exactly like typed text.
|
|
32
|
+
- **A large paste collapses to a placeholder** — `[Pasted text #1 +218 lines]`
|
|
33
|
+
— which occupies one line, carries its own size in its label, deletes as a
|
|
34
|
+
single unit (one `Backspace`, and `Ctrl+W` / `Ctrl+U` / `Ctrl+K` cannot leave
|
|
35
|
+
half of one behind), and expands back to the full text the moment you send.
|
|
36
|
+
The model receives every byte; the composer never has to draw them.
|
|
37
|
+
- **No pasted byte is executed as a terminal control.** `\r`, `\x1b`, NUL, DEL
|
|
38
|
+
and the C1 range are stripped from the payload — never from your keystrokes,
|
|
39
|
+
so `Ctrl+A` and Escape are untouched.
|
|
40
|
+
- **The composer has a ceiling.** A draft of any length occupies at most 3 / 6 /
|
|
41
|
+
10 rows depending on terminal height, scrolls internally to keep the caret
|
|
42
|
+
visible, shows how many rows are hidden above and below, and reports its true
|
|
43
|
+
height to the layout budget. Four hundred presses of `Shift+Enter` were
|
|
44
|
+
already able to destroy the frame; they no longer are.
|
|
45
|
+
- **A submitted message cannot make the transcript unresponsive.** A user entry
|
|
46
|
+
renders at most 40 rows plus a `... +N more lines` tail. The full text is kept
|
|
47
|
+
and the exit transcript is uncapped.
|
|
48
|
+
|
|
49
|
+
On terminals that support it, detection is **exact**: aragon asks for bracketed
|
|
50
|
+
paste (`DECSET 2004`) in full-screen mode and reads the markers the terminal
|
|
51
|
+
wraps your paste in. Where that is unavailable — inline mode, an old Windows
|
|
52
|
+
console — a heuristic on chunk shape catches the same cases, and it needs no
|
|
53
|
+
cooperation from the terminal at all. Both feed one code path.
|
|
54
|
+
|
|
55
|
+
Turn it off with `--no-paste`, `ARAGON_PASTE=0`, or
|
|
56
|
+
`aragon config set paste false`; the input path is then byte-for-byte what
|
|
57
|
+
0.6.2 did.
|
|
58
|
+
|
|
59
|
+
### Fixed
|
|
60
|
+
|
|
61
|
+
- **A pasted API key is stored correctly again.** Ink broadcasts each stdin chunk
|
|
62
|
+
to every mounted input handler, so the settings screen, the question overlay
|
|
63
|
+
and the plan-review feedback field all see what the composer sees. Without
|
|
64
|
+
this, a ~100-character key pasted into Settings would have been stored wrapped
|
|
65
|
+
in invisible framing bytes, masked on screen by the password dots, and would
|
|
66
|
+
have failed to authenticate on every request afterwards with nothing anywhere
|
|
67
|
+
explaining why.
|
|
68
|
+
|
|
69
|
+
- **`--no-mouse` still writes no mouse sequence.** Paste support installs a
|
|
70
|
+
stdin filter, and `paste` defaults to on, so the filter now exists in sessions
|
|
71
|
+
that disabled the mouse. Whether the mouse is on is a separate question from
|
|
72
|
+
whether the stream is wrapped, and the four places that used to conflate them
|
|
73
|
+
now ask the right one: `aragon --no-mouse` writes no `?1000h`, no `?1006h` and
|
|
74
|
+
no `?1002h`, leaves your terminal's own click-drag selection alone, and reports
|
|
75
|
+
`/mouse` as off — while pasting still works.
|
|
76
|
+
|
|
77
|
+
### Changed
|
|
78
|
+
|
|
79
|
+
- **Auto-compaction no longer reports compactions that did not happen.** A long
|
|
80
|
+
unattended run used to print a red `context not compacted #2: nothing_to_drop`
|
|
81
|
+
card into the transcript. The card was accurate; nobody had asked for it.
|
|
82
|
+
Auto-compaction evaluates itself at every turn boundary, and a checkpoint that
|
|
83
|
+
looks at the history and finds nothing worth dropping has changed nothing,
|
|
84
|
+
called nothing and spent nothing — a smoke alarm announcing "no fire" twice an
|
|
85
|
+
hour, in error colours, with a machine reason attached.
|
|
86
|
+
|
|
87
|
+
A compaction now announces itself **if and only if it commits to doing work**.
|
|
88
|
+
That is a position in the control flow, not a list of reason strings: everything
|
|
89
|
+
decided before the first spend is silent, and everything decided after it —
|
|
90
|
+
including every failure, the truncation fallback, an Esc, and a host fault — is
|
|
91
|
+
as loud as it ever was. A declined `pressure` checkpoint is now
|
|
92
|
+
indistinguishable from a turn on which the threshold was never crossed.
|
|
93
|
+
|
|
94
|
+
**`/compact` and overflow recovery are unchanged.** A compaction you asked for
|
|
95
|
+
is owed an answer, and a reactive compaction running after the provider has
|
|
96
|
+
already refused your request is the one moment the diagnosis matters most. Both
|
|
97
|
+
still report on every outcome.
|
|
98
|
+
|
|
99
|
+
**Quiet, not unrecorded.** Every attempt is still written to the diagnostic log
|
|
100
|
+
(its `compaction` line gains an `announced` field, so `announced: false` is one
|
|
101
|
+
grep), still charged to the anti-loop guards, and still counted by
|
|
102
|
+
`/compact status` — which now reads
|
|
103
|
+
`This session: 1 compaction (2 checkpoints declined), 94.2k tokens reclaimed`
|
|
104
|
+
when checkpoints declined, and is byte-identical to before when none did. If
|
|
105
|
+
declines keep happening, the self-disable notice still says so out loud, once,
|
|
106
|
+
naming the cause and the remedies. Card numbers are also contiguous again: `#N`
|
|
107
|
+
counts compactions that ran, not checkpoints that fired.
|
|
108
|
+
|
|
109
|
+
**Behaviour change for wrapper scripts.** A declined `pressure` compaction no
|
|
110
|
+
longer emits a `{ type: 'compaction', subtype: 'start' | 'end' }` pair on the
|
|
111
|
+
`--output-format json` stream, and no longer prints `[compaction] skipped:` on
|
|
112
|
+
`-p` stderr. The pairs stay balanced, so only consumers counting *attempts* are
|
|
113
|
+
affected; the diagnostic log reports those more accurately.
|
|
114
|
+
|
|
115
|
+
### Added
|
|
116
|
+
|
|
12
117
|
- **Context compaction, hardened — the trigger now counts the whole history, the
|
|
13
118
|
recent turns can no longer wedge a run, sub-agents get the same protection, and
|
|
14
119
|
a compaction is no longer irreversible.**
|
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
|
+
| `/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 |
|
|
416
417
|
| `/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 |
|
|
417
418
|
| `/theme <auto\|warm\|cool\|light>` | Switch the color theme live (persisted) |
|
|
418
419
|
| `/expand [n]` | Expand / collapse the n-th-from-last (default last) tool card |
|
|
@@ -437,10 +438,12 @@ completes, `Up` / `Down` moves the selection, `Esc` closes it.
|
|
|
437
438
|
| `Shift+Tab` | Toggle **plan mode** (`BUILD` <-> `PLAN`). Equivalent: `/plan`. On Windows this needs a console flag Node < 22.17 does not set; aragon sets it at startup — see [Requirements](#requirements) |
|
|
438
439
|
| `Ctrl+P` | The same toggle, over a channel no Windows console can swallow. Use it if `Shift+Tab` does nothing |
|
|
439
440
|
| `Esc` | Abort the run / close an overlay / close a popup |
|
|
440
|
-
| `
|
|
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 |
|
|
442
|
+
| `Ctrl+C` | Stop the background **services** the agent started, when there are any. It does not arm exit |
|
|
443
|
+
| `Ctrl+C` ×2 | Exit (first press warns), once no services are running |
|
|
441
444
|
| `Ctrl+L` | Redraw the frame (clear the screen in inline mode) |
|
|
442
445
|
| `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 |
|
|
443
|
-
| `Ctrl+O` | Expand / collapse the most recent tool card — including
|
|
446
|
+
| `Ctrl+O` | Expand / collapse the most recent tool, team, compaction or **service** card — including a diff or a service's log tail |
|
|
444
447
|
| `PgUp` / `PgDn` | Scroll the transcript a page (full-screen mode) |
|
|
445
448
|
| `Shift+↑` / `Shift+↓` | Scroll the transcript a line (full-screen mode) |
|
|
446
449
|
| Mouse wheel | Scroll the transcript (or the open overlay). On Windows needs Node ≥ 22.17 — see [Requirements](#requirements) |
|
|
@@ -450,6 +453,7 @@ completes, `Up` / `Down` moves the selection, `Esc` closes it.
|
|
|
450
453
|
| `Ctrl+W`, `Alt+Backspace` | Delete the previous word |
|
|
451
454
|
| `Ctrl+U` / `Ctrl+K` | Kill to line start / end |
|
|
452
455
|
| `Up` / `Down` | Move between draft lines; recall prompt history at the edges |
|
|
456
|
+
| Paste | Inserted as-is up to 6 lines / 400 chars; larger collapses to `[Pasted text #1 +15 lines]`, which deletes as one unit and expands in full when you send. `--no-paste` restores the pre-0.6.3 behaviour |
|
|
453
457
|
| `?` | Open help (empty input) |
|
|
454
458
|
|
|
455
459
|
In **full-screen mode** the transcript is scrolled by the app itself: `PgUp` /
|
|
@@ -908,6 +912,94 @@ delegation tool (see below). `--no-team` removes it again.
|
|
|
908
912
|
With todo planning on — also the default — one more is added: `todo_write`, the
|
|
909
913
|
planning tool (see above). `--no-todo` removes it again.
|
|
910
914
|
|
|
915
|
+
With background services on — also the default — two more are added:
|
|
916
|
+
`bash_output` and `bash_kill` (see below), and `bash` itself gains a `background`
|
|
917
|
+
parameter. `bash.background: false` removes all three again.
|
|
918
|
+
|
|
919
|
+
## Background services
|
|
920
|
+
|
|
921
|
+
The agent can write a web app; it also has to be able to **run** one. A command
|
|
922
|
+
that never exits on its own — `npm run dev`, `uvicorn main:app`, `vite`,
|
|
923
|
+
`docker compose up`, `tsc --watch` — is not a hung tool, it is a server, and
|
|
924
|
+
running it in the foreground stalls the turn until the tool timeout kills it
|
|
925
|
+
three minutes later.
|
|
926
|
+
|
|
927
|
+
So those commands are **supervised** instead. `bash` gains one optional
|
|
928
|
+
parameter:
|
|
929
|
+
|
|
930
|
+
```jsonc
|
|
931
|
+
{ "command": "npm run dev", "background": true }
|
|
932
|
+
```
|
|
933
|
+
|
|
934
|
+
`true` supervises it, `false` never does, and **omitting it lets the runtime
|
|
935
|
+
decide** from the command itself — a closed allowlist over the leading token and
|
|
936
|
+
the script name (`npm|pnpm|yarn|bun run dev|start|serve|watch|preview`,
|
|
937
|
+
`next dev`, `vite`, `nuxt dev`, `ng serve`, `nodemon`, `tsc --watch`, `uvicorn`,
|
|
938
|
+
`gunicorn`, `flask run`, `manage.py runserver`, `rails server`, `php -S`,
|
|
939
|
+
`cargo watch`, `docker compose up` without `-d`, `tail -f`, `watch `). It never
|
|
940
|
+
fires on a chained command, because a chain's tail is what you actually wanted
|
|
941
|
+
the result of, and `npm run build` and `npm test` are deliberately not on it.
|
|
942
|
+
|
|
943
|
+
A supervised launch returns as soon as there is something true to say — the
|
|
944
|
+
**first** of ready, exited, or four seconds — so a server that crashes on boot is
|
|
945
|
+
reported in 400 ms with the stack in the result rather than after a three-minute
|
|
946
|
+
stall:
|
|
947
|
+
|
|
948
|
+
```
|
|
949
|
+
$ npm run dev
|
|
950
|
+
[background] service s1 started (pid 24188)
|
|
951
|
+
[ready] http://localhost:3000 (detected from output after 1.4s)
|
|
952
|
+
[log tail]
|
|
953
|
+
> next dev
|
|
954
|
+
- Local: http://localhost:3000
|
|
955
|
+
Read more with bash_output({ service: "s1" }); stop it with bash_kill({ service: "s1" }).
|
|
956
|
+
```
|
|
957
|
+
|
|
958
|
+
Readiness is a URL the server printed, or one TCP connect to a port the command
|
|
959
|
+
named — and if that port was **already answering before the launch**, the probe
|
|
960
|
+
is switched off and the card says so, rather than calling somebody else's server
|
|
961
|
+
yours. A process that stays up without listening on anything (`tsc --watch`) is
|
|
962
|
+
reported as `running`, which is the honest third answer.
|
|
963
|
+
|
|
964
|
+
Each service gets a transcript card and a `svc N` chip in the status bar. Two
|
|
965
|
+
tools read and stop them:
|
|
966
|
+
|
|
967
|
+
- `bash_output({ service, since? })` — the log tail and the current status.
|
|
968
|
+
`since` is the cursor the previous call returned, so a watcher can be read
|
|
969
|
+
incrementally. The tail is a bounded in-memory ring of 200 rows; there are no
|
|
970
|
+
log files on disk.
|
|
971
|
+
- `bash_kill({ service })` — stop one, or `"all"`.
|
|
972
|
+
|
|
973
|
+
**Services do not survive the CLI.** Quitting, `Ctrl+C`, `SIGTERM` and a crash
|
|
974
|
+
all reap the tree on a best-effort basis; there is no daemon, no PID file and no
|
|
975
|
+
reattach across launches. A `SIGKILL` of aragon itself is unrecoverable by
|
|
976
|
+
construction, and if a stop cannot confirm the process is gone it says
|
|
977
|
+
`may have left a detached child` rather than claiming success.
|
|
978
|
+
|
|
979
|
+
From your side there are two keys and one command: `Ctrl+C` stops every live
|
|
980
|
+
service (and does *not* arm exit), `/bg` lists them, and `/bg logs s1` prints a
|
|
981
|
+
tail without going through the model.
|
|
982
|
+
|
|
983
|
+
### Turning it off
|
|
984
|
+
|
|
985
|
+
```jsonc
|
|
986
|
+
// ~/.aragon/config.json
|
|
987
|
+
{ "bash": { "background": false } }
|
|
988
|
+
```
|
|
989
|
+
|
|
990
|
+
`ARAGON_BASH_BACKGROUND=0` does the same for one run;
|
|
991
|
+
`ARAGON_BASH_AUTO_BACKGROUND=0` keeps the tools but stops the classifier firing
|
|
992
|
+
on a command that did not ask. With `background: false` the build behaves exactly
|
|
993
|
+
as it did before this feature existed — no `background` parameter, no extra
|
|
994
|
+
tools, no prompt block, no chip.
|
|
995
|
+
|
|
996
|
+
Two things are **not** governed by that switch, because they are fixes rather
|
|
997
|
+
than features. `bash` now always settles: it returns when the process exits
|
|
998
|
+
rather than when its output stream closes, so a command that detaches a
|
|
999
|
+
grandchild can no longer leave the turn pending forever, and it is handed a
|
|
1000
|
+
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.
|
|
1002
|
+
|
|
911
1003
|
## Team subagents
|
|
912
1004
|
|
|
913
1005
|
The agent can hand parts of a job to short-lived **subagents** that run in
|
|
@@ -1647,6 +1739,7 @@ env / `.env` → CLI flags**.
|
|
|
1647
1739
|
| `planModeDefault` | `false` | Start every session in plan mode. `--no-plan` still overrides it for a single run. |
|
|
1648
1740
|
| `planModeMaxAskRounds` | `4` | `ask_user` rounds allowed per user message, clamped to `[1, 10]`. The counter resets on your next message; steering does **not** reset it. |
|
|
1649
1741
|
| `planModeHumanTimeoutMs` | `1800000` | Ceiling on a single question / plan wait, clamped to `[60000, 7200000]`. Thirty minutes is far outside "went to get coffee", but a ceiling has to exist so an unattended terminal cannot wedge a CI job forever. |
|
|
1742
|
+
| `paste` | `true` | Recognise pasted text before Ink's key parser sees it: line breaks become buffer newlines instead of fifteen separate messages, control bytes are stripped instead of overwriting the row they land on, and a paste above 6 lines or 400 characters collapses to a one-line placeholder that expands when you send. `false` treats pasted bytes as keystrokes, which is what 0.6.2 did. |
|
|
1650
1743
|
| `historyEnabled` | `true` | Record submitted prompts to `prompt-history.jsonl` for `↑` recall. Setting it to `false` stops new writes; it does not delete what is already stored (that is `aragon history clear --yes`) and does not stop reading. See [Prompt history](#prompt-history). |
|
|
1651
1744
|
| `team.enabled` | `true` | Master switch for team subagents. `false` means the `task` tool is never registered — `/team on` in such a session saves the setting for next launch and says so, rather than advertising a tool that is not there. |
|
|
1652
1745
|
| `team.maxSubagents` | `5` | Subagents per dispatch, clamped to `[1, 10]`. The 10 is the hard ceiling and is enforced twice — on the config value and again on what the model asks for — so hand-editing this file cannot raise it. |
|
|
@@ -15,6 +15,8 @@ import type { ToolPermission } from '../exec/permission.js';
|
|
|
15
15
|
import { type ToolOutputListener } from '../tools/tool-output-store.js';
|
|
16
16
|
import type { FilePatch } from '../tools/patch.js';
|
|
17
17
|
import type { TeamEvent, TeamSnapshot } from '../team/types.js';
|
|
18
|
+
import { ProcSupervisor } from '../proc/supervisor.js';
|
|
19
|
+
import type { ProcEventListener, ServiceSnapshot } from '../proc/types.js';
|
|
18
20
|
import { type FastStatus } from '../fast/wiring.js';
|
|
19
21
|
import type { FastEventListener } from '../fast/types.js';
|
|
20
22
|
import type { CompactionEventListener, CompactionSnapshot } from '../compaction/types.js';
|
|
@@ -176,6 +178,33 @@ export declare class AgentController {
|
|
|
176
178
|
private readonly outputListeners;
|
|
177
179
|
private readonly fast;
|
|
178
180
|
private readonly compaction;
|
|
181
|
+
private readonly procs;
|
|
182
|
+
/**
|
|
183
|
+
* The host's notice channel, kept so `prompt()`'s totality has somewhere to
|
|
184
|
+
* report to (I-8). Every other consumer reads `deps.notify` through a closure
|
|
185
|
+
* built in the constructor; this one is called from a method, which is why it
|
|
186
|
+
* needs a field.
|
|
187
|
+
*/
|
|
188
|
+
private readonly notifyHostFn;
|
|
189
|
+
/** Whether the SERVICE half is on: tools, prompt block, `background` param. */
|
|
190
|
+
private readonly bashBackgroundRegistered;
|
|
191
|
+
/** Release the exit reaper. Called by `dispose()`. */
|
|
192
|
+
private readonly releaseSignalHook;
|
|
193
|
+
/**
|
|
194
|
+
* Bumped by `forceStop()`; captured by `App` when it subscribes to a run.
|
|
195
|
+
*
|
|
196
|
+
* THE VIEW AND THE ENGINE DELIBERATELY DISAGREE AFTER A FORCE-STOP (D-10 /
|
|
197
|
+
* I-7). Rung two dispatches `runEnd` locally so the composer is usable
|
|
198
|
+
* immediately, but the engine keeps unwinding and keeps EMITTING -
|
|
199
|
+
* `turn_start`, `tool_call_start`, `turn_end`, `agent_end` - and
|
|
200
|
+
* `runStart`/`turnStart` would set `status: 'running'` again, bouncing the
|
|
201
|
+
* view straight back out of the `idle` the user was just given and appending
|
|
202
|
+
* entries to a transcript they believe is finished. `App` drops every event
|
|
203
|
+
* whose generation is stale, which is the same shape as `abortRequested`
|
|
204
|
+
* above and exists for the same reason: the loop legitimately emits after an
|
|
205
|
+
* abort lands.
|
|
206
|
+
*/
|
|
207
|
+
private runGen;
|
|
179
208
|
/**
|
|
180
209
|
* Set SYNCHRONOUSLY by `abort()` before `agent.abort()` (§3.5.4a guard 1).
|
|
181
210
|
* Cleared at the next `agent_start`.
|
|
@@ -234,7 +263,97 @@ export declare class AgentController {
|
|
|
234
263
|
* never rejects, so callers must not treat resolution as success.
|
|
235
264
|
*/
|
|
236
265
|
prompt(text: string): Promise<void>;
|
|
266
|
+
/**
|
|
267
|
+
* Wait for the engine to unwind, bounded.
|
|
268
|
+
*
|
|
269
|
+
* 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()`
|
|
271
|
+
* alone would hang here for as long as it hangs there. The budget is the two
|
|
272
|
+
* graces a wedged `bash` can legitimately spend, plus the executor's own.
|
|
273
|
+
*/
|
|
274
|
+
private waitForEngineIdle;
|
|
275
|
+
/** The pre-round body of `prompt`, unchanged apart from being private. */
|
|
276
|
+
private startPrompt;
|
|
237
277
|
abort(): void;
|
|
278
|
+
/**
|
|
279
|
+
* Rung two of the Esc ladder: stop the AGENT, whatever it is blocked on
|
|
280
|
+
* (§3.6 / G3).
|
|
281
|
+
*
|
|
282
|
+
* FOUR STEPS, AND THE FOURTH IS AN ABSENCE.
|
|
283
|
+
*
|
|
284
|
+
* 1. `abort()` - everything it does today, unchanged.
|
|
285
|
+
* 2. `killForeground('force')` - hard-kill every tracked foreground `bash`
|
|
286
|
+
* 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
|
|
288
|
+
* precisely the tools that ignore it.
|
|
289
|
+
* 3. `runGen += 1` - so `App` can drop the events the engine will keep
|
|
290
|
+
* emitting while it unwinds (I-7). Without it the view bounces straight
|
|
291
|
+
* back out of the `idle` the user was just given.
|
|
292
|
+
* 4. IT DOES NOT TOUCH BACKGROUND SERVICES. `Esc` interrupts the AGENT;
|
|
293
|
+
* `Ctrl+C` stops the SERVICES. That asymmetry is the user's own two
|
|
294
|
+
* sentences and it is exactly the kind of thing a later reader will
|
|
295
|
+
* "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
|
|
297
|
+
* had been working against for an hour. It is invariant I-4.
|
|
298
|
+
*
|
|
299
|
+
* UNCONDITIONAL ON `bash.background` (P1-6 / D-9). The supervisor is always
|
|
300
|
+
* constructed; with services off its foreground registry is all there is, and
|
|
301
|
+
* that is the half this method uses.
|
|
302
|
+
*/
|
|
303
|
+
forceStop(): void;
|
|
304
|
+
/**
|
|
305
|
+
* The current run generation (I-7).
|
|
306
|
+
*
|
|
307
|
+
* `App` captures it when it subscribes to a run and drops every engine event
|
|
308
|
+
* whose generation is stale. Same shape as the `abortRequested` guard the fast
|
|
309
|
+
* reviewer already uses, and for the same reason: the loop legitimately emits
|
|
310
|
+
* after an abort lands.
|
|
311
|
+
*/
|
|
312
|
+
get runGeneration(): number;
|
|
313
|
+
/** Whether `bash_output` / `bash_kill` are in `this.tools`. Fixed at construction. */
|
|
314
|
+
isBackgroundRegistered(): boolean;
|
|
315
|
+
/**
|
|
316
|
+
* Subscribe to the CLI-local `ProcEvent` stream.
|
|
317
|
+
*
|
|
318
|
+
* Returns a live unsubscribe even when services are off, because the
|
|
319
|
+
* supervisor exists either way - the contract `subscribeTeam` /
|
|
320
|
+
* `subscribeTodos` / `subscribeFast` / `subscribeToolOutput` already set. With
|
|
321
|
+
* services off nothing is ever emitted, so an unconditional subscription in
|
|
322
|
+
* `App` costs one `Set` entry.
|
|
323
|
+
*/
|
|
324
|
+
subscribeProc(listener: ProcEventListener): () => void;
|
|
325
|
+
getServiceSnapshot(id: string): ServiceSnapshot | undefined;
|
|
326
|
+
listServices(): ServiceSnapshot[];
|
|
327
|
+
/** `starting | ready | running`. Drives the status chip and the Ctrl+C rung. */
|
|
328
|
+
liveServiceCount(): number;
|
|
329
|
+
readServiceLog(id: string, since?: number): ReturnType<ProcSupervisor['read']>;
|
|
330
|
+
/**
|
|
331
|
+
* Stop one service, or every service when `id` is `'all'`.
|
|
332
|
+
*
|
|
333
|
+
* The GRACEFUL ladder (`SIGTERM` -> `SIGKILL` after `stopGraceMs`), because
|
|
334
|
+
* this path has a live event loop to run it on. `reapServicesSync` is the
|
|
335
|
+
* other one, and it is the only one an exit path may use.
|
|
336
|
+
*/
|
|
337
|
+
stopService(id: string): Promise<ServiceSnapshot[]>;
|
|
338
|
+
/**
|
|
339
|
+
* Rung one of `Ctrl+C`: stop every live service (§3.6 / G4).
|
|
340
|
+
*
|
|
341
|
+
* `force: true` means `reapSync()` SEMANTICS - immediate `SIGKILL`, no
|
|
342
|
+
* `stopGraceMs` - and is what `doExit` uses, because `doExit` awaits nothing
|
|
343
|
+
* and must not start (§3.6 step 3). The graceful ladder belongs to `Ctrl+C`,
|
|
344
|
+
* `bash_kill` and `/bg stop`.
|
|
345
|
+
*/
|
|
346
|
+
stopAllServices(opts?: {
|
|
347
|
+
force?: boolean;
|
|
348
|
+
}): Promise<ServiceSnapshot[]>;
|
|
349
|
+
/**
|
|
350
|
+
* The synchronous reaper (I-9).
|
|
351
|
+
*
|
|
352
|
+
* Exposed so `doExit` can call it without awaiting anything. It is also what
|
|
353
|
+
* the signal hook registered in the constructor calls, and it is idempotent -
|
|
354
|
+
* which it has to be, since a normal quit reaches it twice.
|
|
355
|
+
*/
|
|
356
|
+
reapServicesSync(): void;
|
|
238
357
|
steer(text: string): void;
|
|
239
358
|
followUp(text: string): void;
|
|
240
359
|
isRunning(): boolean;
|