@aragon-agent/cli 0.6.7 → 0.6.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +72 -43
- package/dist/agent/controller.d.ts +10 -6
- package/dist/agent/controller.js +35 -48
- package/dist/agent/headless.d.ts +2 -1
- package/dist/agent/prompt-options.d.ts +15 -0
- package/dist/agent/prompt-options.js +1 -0
- package/dist/cli.js +83 -53
- package/dist/commands/builtins.js +3 -1
- package/dist/commands/registry.d.ts +2 -3
- package/dist/input/interrupt-gesture.d.ts +18 -0
- package/dist/input/interrupt-gesture.js +19 -0
- package/dist/input/pointer-router.d.ts +11 -0
- package/dist/input/pointer-router.js +14 -0
- package/dist/todo/store.d.ts +8 -13
- package/dist/todo/store.js +13 -14
- package/dist/todo/types.d.ts +2 -1
- package/dist/ui/App.d.ts +2 -0
- package/dist/ui/App.js +166 -128
- package/dist/ui/Composer.d.ts +11 -5
- package/dist/ui/Composer.js +7 -11
- package/dist/ui/PromptCaret.d.ts +11 -0
- package/dist/ui/PromptCaret.js +31 -0
- package/dist/ui/PromptInput.d.ts +9 -6
- package/dist/ui/PromptInput.js +34 -14
- package/dist/ui/StatusBar.d.ts +1 -0
- package/dist/ui/StatusBar.js +14 -8
- package/dist/ui/Transcript.js +1 -0
- package/dist/ui/composer-rows.js +50 -9
- package/dist/ui/frame-differ.js +2 -19
- package/dist/ui/frame-observer.d.ts +17 -0
- package/dist/ui/frame-observer.js +81 -0
- package/dist/ui/frame-parser.d.ts +7 -0
- package/dist/ui/frame-parser.js +18 -0
- package/dist/ui/layout/AppShell.d.ts +7 -52
- package/dist/ui/layout/AppShell.js +2 -2
- package/dist/ui/layout/ScrollIndicator.d.ts +4 -3
- package/dist/ui/layout/ScrollIndicator.js +4 -4
- package/dist/ui/layout/ScrollViewport.d.ts +13 -66
- package/dist/ui/layout/ScrollViewport.js +183 -185
- package/dist/ui/layout/budget.d.ts +3 -51
- package/dist/ui/layout/budget.js +4 -53
- package/dist/ui/layout/document-layout.d.ts +13 -0
- package/dist/ui/layout/document-layout.js +11 -0
- package/dist/ui/layout/follow-state.d.ts +1 -0
- package/dist/ui/layout/follow-state.js +1 -3
- package/dist/ui/layout/scroll-indicator.d.ts +1 -1
- package/dist/ui/layout/scroll-indicator.js +3 -2
- package/dist/ui/layout/scrollbar-geometry.d.ts +17 -0
- package/dist/ui/layout/scrollbar-geometry.js +13 -0
- package/dist/ui/layout/todo-layout.d.ts +1 -0
- package/dist/ui/layout/todo-layout.js +3 -2
- package/dist/ui/layout/viewport-geometry.d.ts +1 -0
- package/dist/ui/layout/viewport-geometry.js +1 -0
- package/dist/ui/layout/virtual-window.d.ts +1 -0
- package/dist/ui/layout/virtual-window.js +1 -1
- package/dist/ui/overlays/HelpOverlay.js +5 -2
- package/dist/ui/screen.d.ts +1 -1
- package/dist/ui/screen.js +4 -6
- package/dist/ui/scrollbar-controller.d.ts +30 -0
- package/dist/ui/scrollbar-controller.js +124 -0
- package/dist/ui/use-wheel-routing.d.ts +2 -0
- package/dist/ui/use-wheel-routing.js +6 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -438,8 +438,9 @@ completes, `Up` / `Down` moves the selection, `Esc` closes it.
|
|
|
438
438
|
| `Alt+Enter` / `Shift+Enter` | Insert a newline |
|
|
439
439
|
| `Shift+Tab` | Toggle **plan mode** (`BUILD` <-> `PLAN`). Equivalent: `/plan`. On Windows this needs a console flag Node < 22.17 does not set; aragon sets it at startup — see [Requirements](#requirements) |
|
|
440
440
|
| `Ctrl+P` | The same toggle, over a channel no Windows console can swallow. Use it if `Shift+Tab` does nothing |
|
|
441
|
-
| `Esc` |
|
|
442
|
-
| `Esc` ×2 |
|
|
441
|
+
| `Esc` | Close an overlay or popup; otherwise ask for interruption confirmation. While idle, cancel pending auto-continuation |
|
|
442
|
+
| `Esc` ×2 | Interrupt the run when pressed within 1.5 seconds. Other keys or closing a menu cancel the first press |
|
|
443
|
+
| `Esc` again after interruption | **Force-stop** a run that is still stopping, kill foreground shell commands and return to `idle`; background services remain running |
|
|
443
444
|
| `Ctrl+C` | Stop the background **services** the agent started, when there are any. It does not arm exit |
|
|
444
445
|
| `Ctrl+C` ×2 | Exit (first press warns), once no services are running |
|
|
445
446
|
| `Ctrl+L` | Redraw the frame (clear the screen in inline mode) |
|
|
@@ -460,8 +461,11 @@ completes, `Up` / `Down` moves the selection, `Esc` closes it.
|
|
|
460
461
|
In **full-screen mode** the transcript is scrolled by the app itself: `PgUp` /
|
|
461
462
|
`PgDn` and `Shift+↑` / `Shift+↓`. While pinned to the bottom the viewport follows
|
|
462
463
|
new output automatically; once you scroll away the status bar shows `↑N`, a slim
|
|
463
|
-
|
|
464
|
-
|
|
464
|
+
scrollbar occupies the terminal's last column (including 40-column windows). Drag its thumb
|
|
465
|
+
to move through history, or click the track to move one page. Messages, the team panel
|
|
466
|
+
and the input area scroll together; typing or editing returns to the input without
|
|
467
|
+
losing the draft. The fixed status row shows `PgDn down` while reading history.
|
|
468
|
+
`PgDn` moves one page down; editing or submitting returns directly to the end. In **inline mode**
|
|
465
469
|
(`--no-fullscreen`) history lives in your terminal's native scrollback, printed
|
|
466
470
|
once via Ink `<Static>`.
|
|
467
471
|
|
|
@@ -474,9 +478,10 @@ open. It never touches your draft, and it never recalls prompt history: that is
|
|
|
474
478
|
Two things follow from the app owning the mouse, and they are worth knowing
|
|
475
479
|
before you meet them:
|
|
476
480
|
|
|
477
|
-
- **
|
|
478
|
-
|
|
479
|
-
|
|
481
|
+
- **Dragging text selects and copies it when mouse selection is enabled.**
|
|
482
|
+
Dragging the last-column scrollbar scrolls instead. `--no-mouse-select` disables
|
|
483
|
+
text selection while preserving scrollbar dragging. `Shift`+drag uses the
|
|
484
|
+
terminal's native selection on hosts that support that convention.
|
|
480
485
|
- **`tmux` with `mouse on` keeps the wheel for itself**, so the app never sees
|
|
481
486
|
it. `set -g mouse off` hands it back.
|
|
482
487
|
- **A Windows console on Node below 22.17.0 cannot report the wheel at all**,
|
|
@@ -512,7 +517,7 @@ Density and hints are settable the same three ways: `--compact` / `--no-hints`
|
|
|
512
517
|
for one run, `aragon config set density compact` / `aragon config set hints false`
|
|
513
518
|
to persist. The composer hint row also shortens to `? help` on its own after a
|
|
514
519
|
few sessions — except while a run is in progress, when it always spells out
|
|
515
|
-
`esc
|
|
520
|
+
`esc ×2 interrupt` in full.
|
|
516
521
|
|
|
517
522
|
## What you see while it runs
|
|
518
523
|
|
|
@@ -624,8 +629,9 @@ near-identical added ones.
|
|
|
624
629
|
|
|
625
630
|
- Your previous shell output is **covered, not erased**, and returns untouched
|
|
626
631
|
when you exit. Nothing in your scrollback is destroyed.
|
|
627
|
-
- The frame is fixed at `rows - 1` tall
|
|
628
|
-
|
|
632
|
+
- The frame is fixed at `rows - 1` tall. The header, activity row and status bar
|
|
633
|
+
stay visible. The input belongs to the scrolling document; short sessions fill
|
|
634
|
+
the space above it, and browsing history moves it out of view.
|
|
629
635
|
|
|
630
636
|
On exit the session is replayed into the normal buffer as plain text so the
|
|
631
637
|
conversation survives leaving the screen (`--no-exit-transcript` opts out;
|
|
@@ -638,10 +644,13 @@ writing screen-control sequences into a pipe or a redirected file is never safe.
|
|
|
638
644
|
`--no-fullscreen`, `ARAGON_FULLSCREEN=0`, or `aragon config set fullscreen false`
|
|
639
645
|
opt out permanently.
|
|
640
646
|
|
|
641
|
-
The
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
647
|
+
The alternate screen uses an application scrollbar rather than the terminal's
|
|
648
|
+
native scrollback. Windows Terminal and conhost have different mouse/VT capabilities;
|
|
649
|
+
the PowerShell version alone does not determine dragging support. If the host cannot
|
|
650
|
+
deliver SGR mouse reports, the scrollbar remains visible and keyboard scrolling works.
|
|
651
|
+
`--no-mouse` provides that keyboard-only path; `--no-diff-render` disables output
|
|
652
|
+
differencing without disabling the scrollbar. A mid-session resize below 40 columns
|
|
653
|
+
or 12 rows displays a size hint and preserves the draft for restoration.
|
|
645
654
|
|
|
646
655
|
## Render performance
|
|
647
656
|
|
|
@@ -742,8 +751,8 @@ assumptions rather than to retry.
|
|
|
742
751
|
|
|
743
752
|
**Dismissing a plan is not how you stop.** `Esc` on a plan card asks for a
|
|
744
753
|
*better* plan — the agent is told to refine it and submit again — so a model that
|
|
745
|
-
keeps submitting keeps getting cards. To stop the run itself, **press `Esc`
|
|
746
|
-
twice
|
|
754
|
+
keeps submitting keeps getting cards. To stop the run itself, close the card with `Esc`, then **press `Esc`
|
|
755
|
+
twice within 1.5 seconds** to interrupt. Closing the card never counts as the first press. `Ctrl+C` twice
|
|
747
756
|
still exits the session outright.
|
|
748
757
|
|
|
749
758
|
### Where the mode is shown
|
|
@@ -800,8 +809,8 @@ ask — and the agent writes the finished plan as its final message in markdown.
|
|
|
800
809
|
For work with three or more distinct steps the agent keeps a **visible plan**:
|
|
801
810
|
it writes a checklist with the `todo_write` tool, marks exactly one item as in
|
|
802
811
|
progress, and ticks it off before starting the next. In full-screen mode that
|
|
803
|
-
list is rendered as a right-hand rail, targeting 15% of the terminal width
|
|
804
|
-
(14–36 columns, including its separator and padding).
|
|
812
|
+
list is rendered as a right-hand rail, targeting 15% of the terminal width
|
|
813
|
+
(14–36 columns, including its separator and padding).
|
|
805
814
|
|
|
806
815
|
```
|
|
807
816
|
transcript ... | TODO 2/7
|
|
@@ -809,7 +818,7 @@ list is rendered as a right-hand rail, targeting 15% of the terminal width
|
|
|
809
818
|
|
|
|
810
819
|
| v 1 Read the reducer
|
|
811
820
|
| v 2 Design the store
|
|
812
|
-
| > 3 Wiring the rail...
|
|
821
|
+
| > 3 Wiring the rail...
|
|
813
822
|
| o 4 Add the panel-rows test
|
|
814
823
|
| o 5 Update the README
|
|
815
824
|
| +2 more
|
|
@@ -821,8 +830,8 @@ for a question, or for a search with no work attached — and a one-item list
|
|
|
821
830
|
against an empty plan is refused outright. A session that never plans has
|
|
822
831
|
byte-identical layout to a build without this feature.
|
|
823
832
|
|
|
824
|
-
**The list
|
|
825
|
-
|
|
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:
|
|
826
835
|
|
|
827
836
|
- `/clear` and `/todo clear` take the plan off the screen. Neither touches the
|
|
828
837
|
conversation, so the model still knows what it was doing and its next update
|
|
@@ -831,10 +840,14 @@ not also tell the model may change it — except you, saying so:
|
|
|
831
840
|
- `/save` writes the list and `/resume` restores it. Resuming a session that has
|
|
832
841
|
no list clears the current one, rather than leaving a plan on screen whose
|
|
833
842
|
conversation has just been replaced.
|
|
834
|
-
- A
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
843
|
+
- A normal new task clears the previous plan, including unfinished steps, only
|
|
844
|
+
when it starts successfully. The completed run keeps its final plan visible.
|
|
845
|
+
`/todo continue` and automatic continuation preserve the plan and its timestamp;
|
|
846
|
+
ordinary text such as "continue" starts a new task. Steering keeps the current
|
|
847
|
+
plan. Until a valid new `todo_write`, the rail, inline strip and count stay hidden.
|
|
848
|
+
Startup failure or cancellation keeps the old plan. History cards remain available.
|
|
849
|
+
- During a live run, interrupt before `/reset` or `/resume`. A conversation switch
|
|
850
|
+
while startup is waiting cancels that pending request and returns the view to idle.
|
|
838
851
|
|
|
839
852
|
### When a run ends with steps left
|
|
840
853
|
|
|
@@ -857,7 +870,7 @@ setting:
|
|
|
857
870
|
|
|
858
871
|
`Esc` during the grace window cancels; so does typing anything. A run that ended
|
|
859
872
|
with an **error** never auto-continues in any mode — you get a warning instead.
|
|
860
|
-
A run **you**
|
|
873
|
+
A run **you** interrupted with two `Esc` presses now says nothing at all, where it used to
|
|
861
874
|
report what you had just interrupted.
|
|
862
875
|
|
|
863
876
|
`/todo follow off` is the third mode: no notice, no continuation, silence.
|
|
@@ -889,19 +902,19 @@ planning discipline and hides the column — which is what you want with a scree
|
|
|
889
902
|
reader, and what `--no-todo-panel` does for a whole session. Under `-p` the plan
|
|
890
903
|
is written to stderr as `[todo] 3/7 <step>` lines, suppressed by `--quiet`.
|
|
891
904
|
|
|
892
|
-
右栏从 76 列开始显示,正文至少保留 62 列;100 列终端使用 15 列右栏。
|
|
893
|
-
已有任务时,团队面板按实际行数占用空间,补全菜单的边框和提示也计入预算。
|
|
894
|
-
例如 100×20、团队占 8 行时,TODO 仍以 4 行紧凑视图显示。
|
|
895
|
-
|
|
896
|
-
剩余 3–5 行时保留标题、完成计数、当前任务与合并提示 `-A +B`:
|
|
897
|
-
`-9 +10` 表示上方隐藏 9 项、下方隐藏 10 项。6 行起使用常规视图;
|
|
898
|
-
所有任务各占一行,全文仍可在正文任务卡片中查看。进度条需要至少 20 列、8 行。
|
|
899
|
-
输入增长导致空间不足时团队先折叠为摘要;菜单不足 3 行时隐藏,其候选不会拦截输入。
|
|
900
|
-
|
|
901
|
-
低于 76 列、共享区域不足 3 行、打开 overlay、显式关闭面板、没有任务或
|
|
902
|
-
inline 模式时不显示右栏。低于 12 行使用原有小终端提示;扩大窗口后自动恢复。
|
|
903
|
-
inline 仍使用上文的单行任务条。`/todo status` 中的 `Panel: on` 仅表示配置开启,
|
|
904
|
-
实际可见性还取决于数据和窗口空间;可以通过该命令区分未生成计划与暂时隐藏。
|
|
905
|
+
右栏从 76 列开始显示,正文至少保留 62 列;100 列终端使用 15 列右栏。
|
|
906
|
+
已有任务时,团队面板按实际行数占用空间,补全菜单的边框和提示也计入预算。
|
|
907
|
+
例如 100×20、团队占 8 行时,TODO 仍以 4 行紧凑视图显示。
|
|
908
|
+
|
|
909
|
+
剩余 3–5 行时保留标题、完成计数、当前任务与合并提示 `-A +B`:
|
|
910
|
+
`-9 +10` 表示上方隐藏 9 项、下方隐藏 10 项。6 行起使用常规视图;
|
|
911
|
+
所有任务各占一行,全文仍可在正文任务卡片中查看。进度条需要至少 20 列、8 行。
|
|
912
|
+
输入增长导致空间不足时团队先折叠为摘要;菜单不足 3 行时隐藏,其候选不会拦截输入。
|
|
913
|
+
|
|
914
|
+
低于 76 列、共享区域不足 3 行、打开 overlay、显式关闭面板、没有任务或
|
|
915
|
+
inline 模式时不显示右栏。低于 12 行使用原有小终端提示;扩大窗口后自动恢复。
|
|
916
|
+
inline 仍使用上文的单行任务条。`/todo status` 中的 `Panel: on` 仅表示配置开启,
|
|
917
|
+
实际可见性还取决于数据和窗口空间;可以通过该命令区分未生成计划与暂时隐藏。
|
|
905
918
|
|
|
906
919
|
## Built-in tools
|
|
907
920
|
|
|
@@ -1007,7 +1020,8 @@ than features. `bash` now always settles: it returns when the process exits
|
|
|
1007
1020
|
rather than when its output stream closes, so a command that detaches a
|
|
1008
1021
|
grandchild can no longer leave the turn pending forever, and it is handed a
|
|
1009
1022
|
closed stdin so a command that prompts fails instead of blocking on a read
|
|
1010
|
-
nobody will answer.
|
|
1023
|
+
nobody will answer. Two `Esc` presses request interruption; another press force-stops a stuck run,
|
|
1024
|
+
whatever the flag says.
|
|
1011
1025
|
|
|
1012
1026
|
## Team subagents
|
|
1013
1027
|
|
|
@@ -1079,7 +1093,7 @@ children's tokens are folded into the `[usage]` footer.
|
|
|
1079
1093
|
`debug`. A `team_send` subject is recorded, never its body; a subagent's brief
|
|
1080
1094
|
and summary only at `trace`.
|
|
1081
1095
|
|
|
1082
|
-
|
|
1096
|
+
**Two `Esc` presses within 1.5 seconds** abort the whole dispatch — every subagent within a couple of seconds
|
|
1083
1097
|
— and the report comes back marked `ABORTED` with whatever was finished.
|
|
1084
1098
|
|
|
1085
1099
|
## Fast model tier
|
|
@@ -2234,7 +2248,7 @@ node packages/cli/dist/cli.js --version
|
|
|
2234
2248
|
shebang; `node packages/cli/dist/cli.js --version` prints the version.
|
|
2235
2249
|
- `npm pack -w packages/cli --dry-run --json` lists `dist/cli.js` under `files`.
|
|
2236
2250
|
- With `ANTHROPIC_API_KEY` set: `aragon` streams a reply, a tool card renders,
|
|
2237
|
-
`Esc`
|
|
2251
|
+
`Esc` twice interrupts mid-run, `/model` switches models, `/settings` saves a key,
|
|
2238
2252
|
`Ctrl+C` twice exits.
|
|
2239
2253
|
- `echo "list files" | node dist/cli.js -p` prints an answer and exits `0`.
|
|
2240
2254
|
- A missing/invalid key produces a visible, actionable message (never a blank
|
|
@@ -2252,7 +2266,7 @@ node packages/cli/dist/cli.js --version
|
|
|
2252
2266
|
nothing is truncated to a single line.
|
|
2253
2267
|
- `--no-hints` on a 20-row terminal in plan mode - the status bar still says
|
|
2254
2268
|
`PLAN` with no chip on screen.
|
|
2255
|
-
- Dismiss a plan card with `Esc`, let the agent resubmit, then press `Esc` twice:
|
|
2269
|
+
- Dismiss a plan card with `Esc`, let the agent resubmit, then close the card and press `Esc` twice within 1.5 seconds:
|
|
2256
2270
|
the run aborts and the session returns to an idle prompt. Do this one BY HAND
|
|
2257
2271
|
as well as in a test - the point of the check is whether the exit is findable,
|
|
2258
2272
|
which no assertion can tell you.
|
|
@@ -2260,3 +2274,18 @@ node packages/cli/dist/cli.js --version
|
|
|
2260
2274
|
## License
|
|
2261
2275
|
|
|
2262
2276
|
MIT
|
|
2277
|
+
|
|
2278
|
+
|
|
2279
|
+
### 输入光标与任务切换验收
|
|
2280
|
+
|
|
2281
|
+
输入获得应用内焦点时,光标每 500 ms 明暗切换;编辑、移动和恢复焦点后立即亮起。
|
|
2282
|
+
`reducedMotion` 开启时保持常亮,弹层打开时暂停。fullscreen 和 inline 使用相同规则。
|
|
2283
|
+
单色终端用等列宽的下划线标记,原文相同时改用 `^`,零宽附加码点保留以兼容 Ink。
|
|
2284
|
+
标记不进入草稿、历史或提交文本;屏幕选择复制会包含当时可见的标记。
|
|
2285
|
+
UTF-16 编辑和 ZWJ/组合字形仍有既有限制,本次修复保证视觉光标定位及相位布局稳定。
|
|
2286
|
+
|
|
2287
|
+
在 Windows Terminal/PowerShell 与 POSIX TTY 分别验证两种模式,窗口覆盖
|
|
2288
|
+
120×30、100×20、80×24、60×16:观察空输入、英文、中文、emoji、多行和组合附加符至少三个周期。
|
|
2289
|
+
检查流式输出不阻止闪烁;单 ESC 不停止任务,两击中断,卡住时第三击强停;菜单关闭不计入两击。
|
|
2290
|
+
任务结束后保留最终计划,下一条普通消息启动时旧计划消失,新有效计划出现才恢复;显式续跑保留计划。
|
|
2291
|
+
强停后立即提交,在等待中双 ESC 取消,旧引擎退出后不得自行执行已取消的消息。
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
* success — that promise resolves even on failure. Success/failure is derived
|
|
9
9
|
* from the event stream by the reducer (TUI) and the headless writer.
|
|
10
10
|
*/
|
|
11
|
+
import type { PromptOptions, PromptOutcome } from './prompt-options.js';
|
|
11
12
|
import { ModelRegistry, type AgentEvent, type AgentTool, type Message, type ModelInfo, type ModelRef, type ProviderRegistry, type ThinkingLevel } from '@aragon-agent/core';
|
|
12
13
|
import type { CliConfig, CompactionConfig, FastConfig, RetryConfig, TeamConfig, ThemeName, TodoConfig } from '../config/schema.js';
|
|
13
14
|
import { type ConfirmRequest } from '../tools/index.js';
|
|
@@ -207,6 +208,7 @@ export declare class AgentController {
|
|
|
207
208
|
* abort lands.
|
|
208
209
|
*/
|
|
209
210
|
private runGen;
|
|
211
|
+
private startupSequence;
|
|
210
212
|
/**
|
|
211
213
|
* Set SYNCHRONOUSLY by `abort()` before `agent.abort()` (§3.5.4a guard 1).
|
|
212
214
|
* Cleared at the next `agent_start`.
|
|
@@ -264,21 +266,23 @@ export declare class AgentController {
|
|
|
264
266
|
* underlying promise so headless mode can await completion. `agent.prompt()`
|
|
265
267
|
* never rejects, so callers must not treat resolution as success.
|
|
266
268
|
*/
|
|
267
|
-
prompt(text: string): Promise<
|
|
269
|
+
prompt(text: string, options?: PromptOptions): Promise<PromptOutcome>;
|
|
268
270
|
/**
|
|
269
271
|
* Wait for the engine to unwind, bounded.
|
|
270
272
|
*
|
|
271
273
|
* BOUNDED BECAUSE THE UNBOUNDED CASE IS THE ONE THIS FEATURE IS ABOUT: a tool
|
|
272
|
-
* that never settles is exactly why rung
|
|
274
|
+
* that never settles is exactly why the force-stop rung exists, and `Agent.waitForIdle()`
|
|
273
275
|
* alone would hang here for as long as it hangs there. The budget is the two
|
|
274
276
|
* graces a wedged `bash` can legitimately spend, plus the executor's own.
|
|
275
277
|
*/
|
|
276
278
|
private waitForEngineIdle;
|
|
277
|
-
/**
|
|
279
|
+
/** Prepare before clearing the plan, then enter the engine without yielding. */
|
|
278
280
|
private startPrompt;
|
|
281
|
+
/** Cancel pending startup as well as any engine run already in progress. */
|
|
279
282
|
abort(): void;
|
|
283
|
+
private stopEngine;
|
|
280
284
|
/**
|
|
281
|
-
*
|
|
285
|
+
* Final rung of the Esc ladder: stop the AGENT, whatever it is blocked on
|
|
282
286
|
* (§3.6 / G3).
|
|
283
287
|
*
|
|
284
288
|
* FOUR STEPS, AND THE FOURTH IS AN ABSENCE.
|
|
@@ -286,7 +290,7 @@ export declare class AgentController {
|
|
|
286
290
|
* 1. `abort()` - everything it does today, unchanged.
|
|
287
291
|
* 2. `killForeground('force')` - hard-kill every tracked foreground `bash`
|
|
288
292
|
* child. THIS is what actually unblocks a wedged `await tool.execute`;
|
|
289
|
-
* an abort signal a tool ignores is advisory, and rung
|
|
293
|
+
* an abort signal a tool ignores is advisory, and the force-stop rung exists for
|
|
290
294
|
* precisely the tools that ignore it.
|
|
291
295
|
* 3. `runGen += 1` - so `App` can drop the events the engine will keep
|
|
292
296
|
* emitting while it unwinds (I-7). Without it the view bounces straight
|
|
@@ -295,7 +299,7 @@ export declare class AgentController {
|
|
|
295
299
|
* `Ctrl+C` stops the SERVICES. That asymmetry is the user's own two
|
|
296
300
|
* sentences and it is exactly the kind of thing a later reader will
|
|
297
301
|
* "fix" by making this kill everything - which would mean a user who
|
|
298
|
-
*
|
|
302
|
+
* confirmed a force-stop of a runaway turn also lost the dev server they
|
|
299
303
|
* had been working against for an hour. It is invariant I-4.
|
|
300
304
|
*
|
|
301
305
|
* UNCONDITIONAL ON `bash.background` (P1-6 / D-9). The supervisor is always
|
package/dist/agent/controller.js
CHANGED
|
@@ -45,7 +45,7 @@ import { buildSystemPrompt } from './system-prompt.js';
|
|
|
45
45
|
*
|
|
46
46
|
* `bash`'s own kill grace is what a wedged foreground command costs; this covers
|
|
47
47
|
* the loop's own unwinding on top of it. Waiting FOREVER is not an option - a
|
|
48
|
-
* tool that never settles is exactly why rung
|
|
48
|
+
* tool that never settles is exactly why the force-stop rung exists.
|
|
49
49
|
*/
|
|
50
50
|
const ENGINE_UNWIND_GRACE_MS = 3000;
|
|
51
51
|
/** Approval gate used whenever there is provably no human to ask (headless). */
|
|
@@ -246,6 +246,7 @@ export class AgentController {
|
|
|
246
246
|
* abort lands.
|
|
247
247
|
*/
|
|
248
248
|
runGen = 0;
|
|
249
|
+
startupSequence = 0;
|
|
249
250
|
/**
|
|
250
251
|
* Set SYNCHRONOUSLY by `abort()` before `agent.abort()` (§3.5.4a guard 1).
|
|
251
252
|
* Cleared at the next `agent_start`.
|
|
@@ -855,43 +856,33 @@ export class AgentController {
|
|
|
855
856
|
* underlying promise so headless mode can await completion. `agent.prompt()`
|
|
856
857
|
* never rejects, so callers must not treat resolution as success.
|
|
857
858
|
*/
|
|
858
|
-
async prompt(text) {
|
|
859
|
-
//
|
|
860
|
-
|
|
861
|
-
//
|
|
862
|
-
// `App` calls this as `void controller.prompt(...)`, and
|
|
863
|
-
// `logging/install.ts` routes an `unhandledRejection` into `handleFatal`,
|
|
864
|
-
// which EXITS THE PROCESS. `Agent.prompt()` throws SYNCHRONOUSLY while
|
|
865
|
-
// `Agent.running` is true - so before the force-stop ladder existed this was
|
|
866
|
-
// merely nearly-true (the view was only ever idle when the engine was), and
|
|
867
|
-
// rung two makes the view idle while the engine is still unwinding. Without
|
|
868
|
-
// the wait and the guard below, "Esc, Esc, then type" would kill the CLI on
|
|
869
|
-
// the very screen this feature exists to rescue.
|
|
870
|
-
//
|
|
871
|
-
// TWO PARTS, BOTH REQUIRED. First WAIT the engine out, bounded, so the
|
|
872
|
-
// common case (the engine unwinds in a few hundred ms) starts a real run
|
|
873
|
-
// rather than reporting a failure. Then swallow anything that still escapes
|
|
874
|
-
// and surface it through the existing notify channel, because a message that
|
|
875
|
-
// cannot be started must produce a visible sentence - never a crash, and
|
|
876
|
-
// never silence.
|
|
877
|
-
if (this.agent.state.isRunning) {
|
|
878
|
-
this.abort();
|
|
879
|
-
await this.waitForEngineIdle();
|
|
880
|
-
}
|
|
859
|
+
async prompt(text, options = {}) {
|
|
860
|
+
// Capture BEFORE stopping: synchronous abort listeners may cancel this request.
|
|
861
|
+
const request = ++this.startupSequence;
|
|
881
862
|
try {
|
|
882
|
-
|
|
863
|
+
if (this.agent.state.isRunning) {
|
|
864
|
+
this.stopEngine();
|
|
865
|
+
await this.waitForEngineIdle();
|
|
866
|
+
}
|
|
867
|
+
if (request !== this.startupSequence) {
|
|
868
|
+
return { status: 'not-started', reason: 'cancelled' };
|
|
869
|
+
}
|
|
870
|
+
if (this.agent.state.isRunning)
|
|
871
|
+
throw new Error('The previous run is still stopping.');
|
|
872
|
+
return await this.startPrompt(text, options, request);
|
|
883
873
|
}
|
|
884
874
|
catch (err) {
|
|
885
875
|
const reason = err instanceof Error ? err.message : String(err);
|
|
886
876
|
getLogger().warn('agent', 'prompt_failed', { reason });
|
|
887
877
|
this.notifyHostFn?.('error', `Could not start the run: ${reason}`);
|
|
878
|
+
return { status: 'not-started', reason: 'failed' };
|
|
888
879
|
}
|
|
889
880
|
}
|
|
890
881
|
/**
|
|
891
882
|
* Wait for the engine to unwind, bounded.
|
|
892
883
|
*
|
|
893
884
|
* BOUNDED BECAUSE THE UNBOUNDED CASE IS THE ONE THIS FEATURE IS ABOUT: a tool
|
|
894
|
-
* that never settles is exactly why rung
|
|
885
|
+
* that never settles is exactly why the force-stop rung exists, and `Agent.waitForIdle()`
|
|
895
886
|
* alone would hang here for as long as it hangs there. The budget is the two
|
|
896
887
|
* graces a wedged `bash` can legitimately spend, plus the executor's own.
|
|
897
888
|
*/
|
|
@@ -911,31 +902,25 @@ export class AgentController {
|
|
|
911
902
|
void this.agent.waitForIdle().then(finish, finish);
|
|
912
903
|
});
|
|
913
904
|
}
|
|
914
|
-
/**
|
|
915
|
-
startPrompt(text) {
|
|
916
|
-
// A new user message is a new intent, so the previous turn's tool ceiling
|
|
917
|
-
// expires here and anything a slash command queued is promoted (D-G1 / D-G2).
|
|
918
|
-
// This and `steer()` below are the ONLY two places a user message enters the
|
|
919
|
-
// engine, which is what makes a per-turn scope implementable at all (FG9).
|
|
905
|
+
/** Prepare before clearing the plan, then enter the engine without yielding. */
|
|
906
|
+
async startPrompt(text, options, request) {
|
|
920
907
|
if (this.skillsEnabled)
|
|
921
908
|
this.skills.beginUserTurn();
|
|
922
|
-
// The ask-round budget is per USER TURN and resets here for the same reason
|
|
923
|
-
// the skill frame does. `steer()` deliberately does NOT reset it: a user
|
|
924
|
-
// steering "actually, use Postgres" into a running plan is answering the
|
|
925
|
-
// questions, not buying four more rounds of them.
|
|
926
909
|
this.askRounds = 0;
|
|
927
|
-
// Same asymmetry, same reason (§3.5): a completed plan is dropped HERE
|
|
928
|
-
// rather than at `agent_end`, so the user gets their `7/7 done` moment, and
|
|
929
|
-
// an unfinished one survives "continue" / "now do the rest" but not four
|
|
930
|
-
// unrelated turns.
|
|
931
|
-
this.todos?.beginUserTurn();
|
|
932
|
-
// The fast reviewer is asked whether the run is on track FOR THIS (§3.5.2).
|
|
933
|
-
// Without it the reviewer judges progress without being told the goal, which
|
|
934
|
-
// is the one input it cannot reconstruct from the event stream.
|
|
935
910
|
this.fast?.setGoal(text);
|
|
936
|
-
|
|
911
|
+
if (request !== this.startupSequence) {
|
|
912
|
+
return { status: 'not-started', reason: 'cancelled' };
|
|
913
|
+
}
|
|
914
|
+
this.todos?.beginUserTurn(options.todoPolicy);
|
|
915
|
+
await this.agent.prompt(text);
|
|
916
|
+
return { status: 'finished' };
|
|
937
917
|
}
|
|
918
|
+
/** Cancel pending startup as well as any engine run already in progress. */
|
|
938
919
|
abort() {
|
|
920
|
+
this.startupSequence += 1;
|
|
921
|
+
this.stopEngine();
|
|
922
|
+
}
|
|
923
|
+
stopEngine() {
|
|
939
924
|
// SET BEFORE `agent.abort()`, SYNCHRONOUSLY — guard 1 of §3.5.4a. The loop
|
|
940
925
|
// still emits the batch's final `tool_execution_end` (`agent-loop.ts:249`)
|
|
941
926
|
// after an Esc lands inside the last tool's `await`, and the reviewer's
|
|
@@ -959,7 +944,7 @@ export class AgentController {
|
|
|
959
944
|
this.teamRuntime?.abortAll();
|
|
960
945
|
}
|
|
961
946
|
/**
|
|
962
|
-
*
|
|
947
|
+
* Final rung of the Esc ladder: stop the AGENT, whatever it is blocked on
|
|
963
948
|
* (§3.6 / G3).
|
|
964
949
|
*
|
|
965
950
|
* FOUR STEPS, AND THE FOURTH IS AN ABSENCE.
|
|
@@ -967,7 +952,7 @@ export class AgentController {
|
|
|
967
952
|
* 1. `abort()` - everything it does today, unchanged.
|
|
968
953
|
* 2. `killForeground('force')` - hard-kill every tracked foreground `bash`
|
|
969
954
|
* child. THIS is what actually unblocks a wedged `await tool.execute`;
|
|
970
|
-
* an abort signal a tool ignores is advisory, and rung
|
|
955
|
+
* an abort signal a tool ignores is advisory, and the force-stop rung exists for
|
|
971
956
|
* precisely the tools that ignore it.
|
|
972
957
|
* 3. `runGen += 1` - so `App` can drop the events the engine will keep
|
|
973
958
|
* emitting while it unwinds (I-7). Without it the view bounces straight
|
|
@@ -976,7 +961,7 @@ export class AgentController {
|
|
|
976
961
|
* `Ctrl+C` stops the SERVICES. That asymmetry is the user's own two
|
|
977
962
|
* sentences and it is exactly the kind of thing a later reader will
|
|
978
963
|
* "fix" by making this kill everything - which would mean a user who
|
|
979
|
-
*
|
|
964
|
+
* confirmed a force-stop of a runaway turn also lost the dev server they
|
|
980
965
|
* had been working against for an hour. It is invariant I-4.
|
|
981
966
|
*
|
|
982
967
|
* UNCONDITIONAL ON `bash.background` (P1-6 / D-9). The supervisor is always
|
|
@@ -1492,6 +1477,7 @@ export class AgentController {
|
|
|
1492
1477
|
}
|
|
1493
1478
|
clearMessages() {
|
|
1494
1479
|
this.agent.clearMessages();
|
|
1480
|
+
this.startupSequence += 1;
|
|
1495
1481
|
// INVALIDATION SITE 3 (context-auto-compaction-hardening §3.2.3). The history
|
|
1496
1482
|
// was CLEARED, not appended to, so the prefix length the last measurement
|
|
1497
1483
|
// covered indexes an array that no longer exists.
|
|
@@ -1519,6 +1505,7 @@ export class AgentController {
|
|
|
1519
1505
|
}
|
|
1520
1506
|
replaceMessages(messages) {
|
|
1521
1507
|
this.agent.replaceMessages(messages);
|
|
1508
|
+
this.startupSequence += 1;
|
|
1522
1509
|
// INVALIDATION SITE 4 (§3.2.3) - `/resume`, and any future host-side rewrite.
|
|
1523
1510
|
// `estimateAppendedTokens` bounds-checks as well, so a MISSED site costs
|
|
1524
1511
|
// accuracy and never correctness; that belt does not make this call optional.
|
package/dist/agent/headless.d.ts
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* derived from the event stream (R2), not from the resolved promise:
|
|
5
5
|
* 0 = success, 1 = agent error (incl. a silently-swallowed throw), 2 = config.
|
|
6
6
|
*/
|
|
7
|
+
import type { PromptOutcome } from './prompt-options.js';
|
|
7
8
|
import type { AgentEvent, ModelInfo } from '@aragon-agent/core';
|
|
8
9
|
import type { PreflightResult } from './controller.js';
|
|
9
10
|
import type { TeamEvent } from '../team/types.js';
|
|
@@ -15,7 +16,7 @@ import { type FollowThroughMode } from '../todo/follow-through.js';
|
|
|
15
16
|
export interface HeadlessController {
|
|
16
17
|
preflight(): PreflightResult;
|
|
17
18
|
subscribe(listener: (event: AgentEvent) => void): () => void;
|
|
18
|
-
prompt(text: string): Promise<void>;
|
|
19
|
+
prompt(text: string): Promise<void | PromptOutcome>;
|
|
19
20
|
getModelInfo(): ModelInfo;
|
|
20
21
|
/**
|
|
21
22
|
* The CLI-local team stream — OPTIONAL, deliberately (team-subagents §3.12 /
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/** Explicit TUI task boundary; omission retains legacy programmatic behavior. */
|
|
2
|
+
export interface PromptOptions {
|
|
3
|
+
todoPolicy?: 'new-task' | 'continue';
|
|
4
|
+
}
|
|
5
|
+
/** History recording is independent of whether this message continues a plan. */
|
|
6
|
+
export interface SubmitMessageOptions extends PromptOptions {
|
|
7
|
+
userInitiated?: boolean;
|
|
8
|
+
}
|
|
9
|
+
/** Startup outcome only; engine events still describe the result of the run. */
|
|
10
|
+
export type PromptOutcome = {
|
|
11
|
+
status: 'finished';
|
|
12
|
+
} | {
|
|
13
|
+
status: 'not-started';
|
|
14
|
+
reason: 'cancelled' | 'failed';
|
|
15
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|