@aragon-agent/cli 0.6.38 → 0.6.40
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 +263 -0
- package/README.md +544 -11
- package/dist/agent/controller.d.ts +245 -10
- package/dist/agent/controller.js +367 -22
- package/dist/agent/model-profile-settings.js +12 -1
- package/dist/agent/plan-prompt.d.ts +1 -3
- package/dist/agent/plan-prompt.js +13 -7
- package/dist/agent/reducer.d.ts +70 -2
- package/dist/agent/reducer.js +59 -6
- package/dist/agent/skill-triage-prompt.d.ts +42 -0
- package/dist/agent/skill-triage-prompt.js +74 -0
- package/dist/agent/system-prompt.d.ts +28 -2
- package/dist/agent/system-prompt.js +12 -4
- package/dist/cli.js +106 -32
- package/dist/commands/builtins.js +24 -3
- package/dist/commands/keys-command.d.ts +23 -0
- package/dist/commands/keys-command.js +38 -0
- package/dist/commands/registry.d.ts +39 -0
- package/dist/commands/skills.js +86 -1
- package/dist/commands/subagent-command.d.ts +25 -0
- package/dist/commands/subagent-command.js +242 -0
- package/dist/commands/task-command.d.ts +39 -0
- package/dist/commands/task-command.js +102 -0
- package/dist/compaction/command.js +16 -3
- package/dist/compaction/compactor.d.ts +25 -0
- package/dist/compaction/compactor.js +111 -10
- package/dist/compaction/digest.d.ts +24 -1
- package/dist/compaction/digest.js +139 -17
- package/dist/compaction/limits.d.ts +21 -0
- package/dist/compaction/limits.js +21 -0
- package/dist/compaction/reason-text.d.ts +36 -0
- package/dist/compaction/reason-text.js +78 -0
- package/dist/compaction/types.d.ts +82 -14
- package/dist/compaction/wiring.d.ts +13 -8
- package/dist/compaction/wiring.js +1 -0
- package/dist/config/cli-commands.d.ts +23 -0
- package/dist/config/cli-commands.js +36 -0
- package/dist/config/env.js +17 -4
- package/dist/config/load.js +55 -3
- package/dist/config/schema.d.ts +125 -10
- package/dist/config/schema.js +138 -14
- package/dist/config/store.js +23 -3
- package/dist/diagnostics/doctor.d.ts +9 -0
- package/dist/diagnostics/doctor.js +34 -0
- package/dist/diagnostics/info.d.ts +71 -0
- package/dist/diagnostics/info.js +91 -5
- package/dist/exec/bridge/caller-tools.d.ts +96 -0
- package/dist/exec/bridge/caller-tools.js +213 -0
- package/dist/exec/bridge/decisions.d.ts +78 -0
- package/dist/exec/bridge/decisions.js +116 -0
- package/dist/exec/bridge/human-answers.d.ts +38 -0
- package/dist/exec/bridge/human-answers.js +164 -0
- package/dist/exec/bridge/limits.d.ts +68 -0
- package/dist/exec/bridge/limits.js +68 -0
- package/dist/exec/bridge/ndjson.d.ts +17 -0
- package/dist/exec/bridge/ndjson.js +83 -0
- package/dist/exec/bridge/pending.d.ts +70 -0
- package/dist/exec/bridge/pending.js +132 -0
- package/dist/exec/bridge/protocol.d.ts +346 -0
- package/dist/exec/bridge/protocol.js +256 -0
- package/dist/exec/bridge/server.d.ts +142 -0
- package/dist/exec/bridge/server.js +502 -0
- package/dist/exec/bridge/source.d.ts +58 -0
- package/dist/exec/bridge/source.js +145 -0
- package/dist/exec/bridge/writer.d.ts +26 -0
- package/dist/exec/bridge/writer.js +156 -0
- package/dist/exec/cli-commands.js +30 -2
- package/dist/exec/emitter.d.ts +16 -2
- package/dist/exec/emitter.js +26 -6
- package/dist/exec/events.d.ts +465 -10
- package/dist/exec/events.js +47 -1
- package/dist/exec/hooks/dispatch.d.ts +76 -0
- package/dist/exec/hooks/dispatch.js +169 -0
- package/dist/exec/hooks/index.d.ts +83 -0
- package/dist/exec/hooks/index.js +205 -0
- package/dist/exec/hooks/limits.d.ts +56 -0
- package/dist/exec/hooks/limits.js +49 -0
- package/dist/exec/hooks/registry.d.ts +49 -0
- package/dist/exec/hooks/registry.js +91 -0
- package/dist/exec/hooks/tool-wrapper.d.ts +94 -0
- package/dist/exec/hooks/tool-wrapper.js +154 -0
- package/dist/exec/hooks/turn.d.ts +61 -0
- package/dist/exec/hooks/turn.js +59 -0
- package/dist/exec/hooks/validate.d.ts +85 -0
- package/dist/exec/hooks/validate.js +310 -0
- package/dist/exec/index.d.ts +130 -4
- package/dist/exec/index.js +501 -10
- package/dist/exec/options.d.ts +124 -2
- package/dist/exec/options.js +413 -6
- package/dist/exec/permission.d.ts +9 -1
- package/dist/exec/permission.js +7 -1
- package/dist/exec/request/limits.d.ts +51 -0
- package/dist/exec/request/limits.js +49 -0
- package/dist/exec/request/scope.d.ts +106 -0
- package/dist/exec/request/scope.js +149 -0
- package/dist/exec/result/limits.d.ts +74 -0
- package/dist/exec/result/limits.js +72 -0
- package/dist/exec/result/prompt.d.ts +28 -0
- package/dist/exec/result/prompt.js +74 -0
- package/dist/exec/result/schema.d.ts +99 -0
- package/dist/exec/result/schema.js +464 -0
- package/dist/exec/result/sink.d.ts +56 -0
- package/dist/exec/result/sink.js +50 -0
- package/dist/exec/result/tool.d.ts +37 -0
- package/dist/exec/result/tool.js +118 -0
- package/dist/exec/runner.d.ts +395 -1
- package/dist/exec/runner.js +860 -8
- package/dist/exec/threads/aggregate.d.ts +34 -0
- package/dist/exec/threads/aggregate.js +30 -0
- package/dist/exec/threads/capacity.d.ts +23 -0
- package/dist/exec/threads/capacity.js +38 -0
- package/dist/exec/threads/dispatch-source.d.ts +60 -0
- package/dist/exec/threads/dispatch-source.js +106 -0
- package/dist/exec/threads/execute-threads.d.ts +53 -0
- package/dist/exec/threads/execute-threads.js +234 -0
- package/dist/exec/threads/gate.d.ts +42 -0
- package/dist/exec/threads/gate.js +81 -0
- package/dist/exec/threads/identity.d.ts +36 -0
- package/dist/exec/threads/identity.js +67 -0
- package/dist/exec/threads/interrupt.d.ts +29 -0
- package/dist/exec/threads/interrupt.js +79 -0
- package/dist/exec/threads/limits.d.ts +58 -0
- package/dist/exec/threads/limits.js +35 -0
- package/dist/exec/threads/outcome.d.ts +47 -0
- package/dist/exec/threads/outcome.js +26 -0
- package/dist/exec/threads/registry.d.ts +81 -0
- package/dist/exec/threads/registry.js +427 -0
- package/dist/exec/threads/runtime.d.ts +107 -0
- package/dist/exec/threads/runtime.js +523 -0
- package/dist/exec/threads/thread-source.d.ts +20 -0
- package/dist/exec/threads/thread-source.js +134 -0
- package/dist/fast/limits.d.ts +54 -0
- package/dist/fast/limits.js +54 -0
- package/dist/fast/summary-call.d.ts +72 -0
- package/dist/fast/summary-call.js +133 -0
- package/dist/fast/summary-prompt.d.ts +106 -0
- package/dist/fast/summary-prompt.js +320 -0
- package/dist/fast/task-summary-types.d.ts +66 -0
- package/dist/fast/task-summary-types.js +17 -0
- package/dist/logging/logger.d.ts +1 -1
- package/dist/proc/limits.d.ts +7 -0
- package/dist/proc/limits.js +7 -0
- package/dist/proc/log-mirror.d.ts +21 -4
- package/dist/proc/log-mirror.js +29 -7
- package/dist/proc/output-streams.d.ts +50 -0
- package/dist/proc/output-streams.js +54 -0
- package/dist/skills/service.d.ts +41 -0
- package/dist/skills/service.js +62 -0
- package/dist/skills/triage.d.ts +194 -0
- package/dist/skills/triage.js +393 -0
- package/dist/team/forced-dispatch.d.ts +95 -0
- package/dist/team/forced-dispatch.js +208 -0
- package/dist/team/limits.d.ts +21 -1
- package/dist/team/limits.js +21 -1
- package/dist/team/normalize.d.ts +15 -0
- package/dist/team/normalize.js +21 -4
- package/dist/team/prompt.d.ts +6 -3
- package/dist/team/prompt.js +9 -5
- package/dist/team/runtime.d.ts +9 -0
- package/dist/team/runtime.js +8 -0
- package/dist/team/subagent.d.ts +15 -0
- package/dist/team/subagent.js +45 -1
- package/dist/team/task-tool.js +11 -7
- package/dist/tools/bash-tool.d.ts +10 -0
- package/dist/tools/bash-tool.js +71 -6
- package/dist/tools/human-input.d.ts +35 -2
- package/dist/tools/human-input.js +38 -0
- package/dist/tools/index.d.ts +93 -1
- package/dist/tools/index.js +112 -3
- package/dist/tools/plan-tools.d.ts +20 -12
- package/dist/tools/plan-tools.js +24 -17
- package/dist/tools/tool-gate.d.ts +100 -0
- package/dist/tools/tool-gate.js +87 -0
- package/dist/ui/App.js +238 -12
- package/dist/ui/Header.d.ts +28 -16
- package/dist/ui/Header.js +21 -19
- package/dist/ui/StatusBar.d.ts +1 -0
- package/dist/ui/StatusBar.js +1 -0
- package/dist/ui/Transcript.js +1 -1
- package/dist/ui/entries/CompactionCard.d.ts +11 -1
- package/dist/ui/entries/CompactionCard.js +65 -4
- package/dist/ui/interaction-hints.js +6 -0
- package/dist/ui/layout/OverlayFrame.d.ts +18 -0
- package/dist/ui/layout/OverlayFrame.js +19 -4
- package/dist/ui/layout/header-hints.d.ts +55 -62
- package/dist/ui/layout/header-hints.js +59 -166
- package/dist/ui/layout/header-left.d.ts +115 -0
- package/dist/ui/layout/header-left.js +206 -0
- package/dist/ui/layout/shortcut-panel-layout.d.ts +191 -0
- package/dist/ui/layout/shortcut-panel-layout.js +321 -0
- package/dist/ui/layout/status-detail-layout.js +5 -1
- package/dist/ui/layout/status-layout.d.ts +10 -0
- package/dist/ui/layout/status-layout.js +18 -1
- package/dist/ui/layout/task-summary-cell.d.ts +61 -0
- package/dist/ui/layout/task-summary-cell.js +65 -0
- package/dist/ui/overlays/HelpOverlay.d.ts +12 -0
- package/dist/ui/overlays/HelpOverlay.js +29 -2
- package/dist/ui/overlays/SettingsScreen.js +29 -5
- package/dist/ui/overlays/ShortcutPanel.d.ts +39 -0
- package/dist/ui/overlays/ShortcutPanel.js +106 -0
- package/dist/ui/overlays/TeamFanoutPage.d.ts +35 -0
- package/dist/ui/overlays/TeamFanoutPage.js +78 -0
- package/dist/ui/overlays/model-profile-state.d.ts +19 -1
- package/dist/ui/overlays/model-profile-state.js +12 -0
- package/dist/ui/overlays/team-fanout-state.d.ts +73 -0
- package/dist/ui/overlays/team-fanout-state.js +97 -0
- package/dist/ui/shortcuts.d.ts +81 -0
- package/dist/ui/shortcuts.js +221 -0
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,247 @@ were written.
|
|
|
8
8
|
## Unreleased
|
|
9
9
|
|
|
10
10
|
### Added
|
|
11
|
+
- **Automatic skill triage, and no round limit on questions.** Two changes that
|
|
12
|
+
share one idea: replace a hard limit with guidance the agent can act on.
|
|
13
|
+
`ask_user` is no longer capped at four rounds per turn — the cap, its prompt
|
|
14
|
+
wording (`you may ask up to 4 rounds`), its tool-result counters
|
|
15
|
+
(`roundsUsed` / `roundsRemaining`) and its `/plan status` readout are all gone,
|
|
16
|
+
because an agent that is told it is running out of questions behaves like it.
|
|
17
|
+
What replaced it is a de-duplication guard: a question batch you have already
|
|
18
|
+
**answered** in this turn returns a note instead of reopening the overlay, while
|
|
19
|
+
a **cancelled** one records nothing, so an accidental `Esc` never retires a
|
|
20
|
+
question. Steering does not reset the guard, because a steer resumes the same
|
|
21
|
+
turn. `planModeMaxAskRounds` is retired rather than defaulted to a large number:
|
|
22
|
+
`config set` names the reason and exits `2`, and the key disappears from
|
|
23
|
+
`config.json` at the next write. The single-wait ceiling
|
|
24
|
+
(`planModeHumanTimeoutMs`) is untouched — it protects an unattended terminal, not
|
|
25
|
+
the agent's vocabulary.
|
|
26
|
+
Skill use now begins with a step instead of a hope. Every task gets a
|
|
27
|
+
`<skill_triage>` protocol fixing *when* to decide (after the project guidance and
|
|
28
|
+
the directory listing, before any plan or edit) plus up to three ranked
|
|
29
|
+
`<skill_candidates>` computed locally from your message against the installed
|
|
30
|
+
records — no model call, no file read, no `SKILL.md` body, so the disclosure
|
|
31
|
+
ladder keeps its lower levels untouched. When a strongly-matching skill was
|
|
32
|
+
available and the turn did real work without loading it, the run ends with one
|
|
33
|
+
`info` line and a pointer to `/skills route`; that fires at most once per skill
|
|
34
|
+
per session, because a line printed every turn is a line you learn to skip.
|
|
35
|
+
`/skills route <text>` explains any decision (including the runner-up and the
|
|
36
|
+
threshold on a miss), `/skills triage on|off` flips the feature live, and
|
|
37
|
+
`skills.triage` (default `true`) makes it permanent — off restores the previous
|
|
38
|
+
prompt byte for byte. Subagents are deliberately not triaged: a child has no
|
|
39
|
+
`skill` tool, so candidates would advertise a capability it does not have.
|
|
40
|
+
- **`/subagent`: force a parallel fan-out with one command.** The escape hatch for
|
|
41
|
+
"I want a team on this, not the model's judgement": `/subagent -n 6 review the error
|
|
42
|
+
handling in packages/cli/src/team`, `/subagent --parts api,ui,docs audit the request
|
|
43
|
+
path`, `/subagent --read-only profile the parser`. With no argument it reports live
|
|
44
|
+
capacity, the subagent model, the supervisor and the team switch, and it answers even in
|
|
45
|
+
a `--no-team` session rather than pretending. The command compiles your intent into ONE
|
|
46
|
+
user turn that tells the lead to call `task` once, so it shares the single dispatch path
|
|
47
|
+
(same Esc, same report, same spend accounting) - the forcing is instruction-level, and
|
|
48
|
+
the notice says what the next turn will do instead of claiming children have started.
|
|
49
|
+
A count above `team.maxSubagents` is narrowed AND disclosed (`asked for 12;
|
|
50
|
+
team.maxSubagents is 8`), a parts list is narrowed by both the structural 8 and the
|
|
51
|
+
configured width, and `-n 0` is an error rather than a silent 1.
|
|
52
|
+
- **`/settings` → `Subagent fan-out`.** A fourth root row holding `Max subagents`,
|
|
53
|
+
`Run at once` and `Supervisor`, adjusted with `Left` / `Right` and saved by the same
|
|
54
|
+
single transaction as every other settings row (`Ctrl+S` on the main page; the fan-out
|
|
55
|
+
page's footer says so instead of promising a key it does not listen for). Narrowing the
|
|
56
|
+
population narrows the pool with it, and the follow-down is committed, so widening again
|
|
57
|
+
does not silently restore a pool you did not choose.
|
|
58
|
+
- **`--threads`: one process, N isolated conversations.** A headless `aragon exec`
|
|
59
|
+
process used to be one conversation: the seventh tenant's messages appeared in the
|
|
60
|
+
third tenant's history, one `todo` list served everybody, and `interrupt` ended the
|
|
61
|
+
process for all of them. `--threads` (with `--input-format bridge` and
|
|
62
|
+
`--output-format stream-json`) makes a conversation an OBJECT: `thread_open` /
|
|
63
|
+
`thread_close` / `thread_list`, and every frame may carry a `threadId` (absent means
|
|
64
|
+
the default thread `main`, which is auto-opened, so a v1 caller is unchanged). Each
|
|
65
|
+
thread owns its controller, history, `todo`s, prompt queue, session file
|
|
66
|
+
(`<base>-<threadId>`) and account; three things are shared and only three - one
|
|
67
|
+
writer (so `seq` stays global), one emitter (one `result`), one registry.
|
|
68
|
+
`--max-open-threads` (default 4) bounds how many exist and `--thread-concurrency`
|
|
69
|
+
(default 2) how many hold a request in flight, released per request through a FIFO
|
|
70
|
+
gate. Isolation is an ownership fact rather than a promise: every line carries its own
|
|
71
|
+
`threadId`, a thread's provider error cannot end another thread or the process, and
|
|
72
|
+
`result.threads.items` reports each tenant's own `requests`/`turns`/`usage`/`cost`
|
|
73
|
+
while `result.usage` stays the sum. `--max-turns` is refused with `--threads` (use
|
|
74
|
+
`--request-max-turns`), and `interrupt` stays process-level:
|
|
75
|
+
`interrupt{threadId}` answers `interrupt_is_process_level` instead of silently becoming
|
|
76
|
+
"cancel one request", because the two have different exit codes and only one writes
|
|
77
|
+
`result`. See [Many conversations in one process](README.md#many-conversations-in-one-process).
|
|
78
|
+
- **Lifecycle hooks: the caller can now change what the model says and sees.**
|
|
79
|
+
`hook_register` installs programmatic middleware at four points - `prompt_pre`
|
|
80
|
+
(rewrite or refuse the caller's text), `turn_start` (inject context through the one
|
|
81
|
+
safe steering checkpoint), `tool_pre` (rewrite a tool's arguments, or refuse it) and
|
|
82
|
+
`tool_post` (replace, redact or withhold a tool's result). Every round trip is one
|
|
83
|
+
`hook_request` with its own `hookRequestId` and exactly one `hook_resolved`
|
|
84
|
+
(`answered` / `timeout` / `aborted` / `invalid` / `saturated` / `unregistered`), bounded
|
|
85
|
+
by `--hook-timeout` (default 5 s) and failing closed by default
|
|
86
|
+
(`--on-hook-timeout deny|allow|error`; `allow` clamps to 30 s so two round trips cannot
|
|
87
|
+
consume a tool's 120 s budget). `tool_pre` rewrites are re-validated against the tool's
|
|
88
|
+
own schema so a hook's bug is never reported as the model's; `tool_post` is the
|
|
89
|
+
outermost wrapper, so it also covers denial text, a tool's own error output and the
|
|
90
|
+
`<job_notices>` tail of a background job. Hooks are per thread - one tenant's redactor
|
|
91
|
+
never sees another's data - and they deliberately do not wrap caller-registered tools.
|
|
92
|
+
See [Intervene in the data flow](README.md#intervene-in-the-data-flow-lifecycle-hooks).
|
|
93
|
+
- `aragon info --json` reports `threadLimits` and `hookLimits` (both from their own
|
|
94
|
+
`*_LIMITS` constants) beside the existing bridge budgets, and advertises the new
|
|
95
|
+
`threads` / `hooks` capability strings from the same `execCapabilities()` derivation
|
|
96
|
+
point `system/init` uses - so a caller can read what the build accepts before deciding
|
|
97
|
+
which frames to send.
|
|
98
|
+
- `examples/bridge-host-threads.mjs` (in `packages/cli/examples`, beside `bridge-host.mjs`): a dependency-free reference host with two threads
|
|
99
|
+
and one redactor, including the five things a host gets wrong (back-to-back
|
|
100
|
+
`thread_open` + `prompt`, answering `hookRequestId`, closing threads so `result` can be
|
|
101
|
+
written, and reading per-tenant accounting). `--dry-run` prints the frames without
|
|
102
|
+
spawning anything.
|
|
103
|
+
|
|
104
|
+
### Changed
|
|
105
|
+
- **The subagent fan-out default is now 8 per dispatch and 8 in flight at once** (it was
|
|
106
|
+
5 and 3). The perceived "only 3 subagents" was the concurrency ceiling, not the
|
|
107
|
+
population - changing only the population would have shown nothing, because children
|
|
108
|
+
4..8 would simply queue. `maxConcurrent` stays a separate knob precisely so a single API
|
|
109
|
+
key collecting 429s can be fixed by lowering `Run at once`; the settings row and the
|
|
110
|
+
README both say which number to lower. The hard ceiling is unchanged at 10: 8 is a
|
|
111
|
+
default you can move, 10 is the limit you cannot. **A persisted `5`/`3` pair from an
|
|
112
|
+
older install is migrated to `8`/`8` once**, with a `team.fanoutMigrated` marker so a
|
|
113
|
+
deliberate return to 5/3 sticks instead of being widened again on every launch. A
|
|
114
|
+
configuration whose values are anything else is never touched.
|
|
115
|
+
- `makeExecController` now separates PROCESS-LEVEL log mounts from PER-CONTROLLER ones:
|
|
116
|
+
`run_exec`, the `onFailure` handler and the untrusted-skill notices happen once per
|
|
117
|
+
process (selected by the new `ExecControllerDeps.processLevelMounts`, which only the
|
|
118
|
+
default thread sets), while `attachAgentEvents` / `attachTeamEvents` are attached for
|
|
119
|
+
**every** thread and disposed when it closes. Skipping the latter would have left every
|
|
120
|
+
tenant but the first invisible in the log - an observability regression, not an
|
|
121
|
+
optimisation. Deriving the first half from `threadId` instead would have been wrong in
|
|
122
|
+
the other direction: the default thread has an id too (`main`).
|
|
123
|
+
- `ExecRunner` gained `threadId`, `onRequestSettled`, `beforeTurn`, `hookPort` and
|
|
124
|
+
`installSignalTerminator`. All are optional; with none of them set the runner writes
|
|
125
|
+
the same bytes it wrote before.
|
|
126
|
+
|
|
127
|
+
### Fixed
|
|
128
|
+
- Changing only the fan-out width no longer drops the subagent model a session kit
|
|
129
|
+
installed. The settings save read "the `team` section was written" as "the user
|
|
130
|
+
re-decided the subagent slot", so adjusting `Max subagents` silently released a slot the
|
|
131
|
+
kit held - with every value on screen still correct. Only an explicit `model` /
|
|
132
|
+
`modelProfileId` decision releases the slot now, and the fan-out page can express
|
|
133
|
+
nothing else.
|
|
134
|
+
- The `task` tool's description no longer states a fixed width (`2-5 independent
|
|
135
|
+
subagents`, `hard maximum 10`), which contradicted the configured maximum in the very
|
|
136
|
+
same request. It points at `<team_mode>`, which is generated from the live config.
|
|
137
|
+
- A process-level signal now has exactly ONE owner in thread mode (it used to be
|
|
138
|
+
installed by every runner into a single process-wide slot, so the last one won): the
|
|
139
|
+
terminator fans out to all threads, `result` is written before the process exits, and a
|
|
140
|
+
second signal still exits immediately - after attempting `result`.
|
|
141
|
+
- `--max-duration` is one process-level timer in thread mode instead of N timers with N
|
|
142
|
+
opinions, and `decideProcessOutcome` is the single place a stop reason and exit code
|
|
143
|
+
are decided.
|
|
144
|
+
- A thread's runner now really is given its own bridge channel: `runner.attachBridge(server,
|
|
145
|
+
source)` was missing from the per-thread assembly, so prompts got no `ack`, turns wrote no
|
|
146
|
+
`request_end`, `steer` answered `not_running` and `cancel_request` was dropped in silence.
|
|
147
|
+
The runtime now owns the queue it serves (`ThreadRuntimeDeps.source`) and wires it before
|
|
148
|
+
the first prompt can arrive.
|
|
149
|
+
- The admission slot is released by the queue that took it. It used to be released straight
|
|
150
|
+
on the gate, which left the queue believing it still held a slot - so from a thread's
|
|
151
|
+
second request onward `--thread-concurrency` silently stopped applying - and a prompt
|
|
152
|
+
cancelled while its thread waited for a slot left that slot held forever.
|
|
153
|
+
- `interrupt{requestId}` in thread mode no longer stops the process: it withdraws one queued
|
|
154
|
+
turn (exit code 0), which is what it has always meant; `interrupt{threadId}` is still
|
|
155
|
+
refused and `interrupt` without either is still process-level.
|
|
156
|
+
- Thread frames sent to a run WITHOUT `--threads` are answered `ack{accepted:false,
|
|
157
|
+
code:'threads_disabled'}` instead of being ignored, and `thread_close{mode}` rejects an
|
|
158
|
+
unknown mode instead of coercing it to `settle`.
|
|
159
|
+
- `--on-hook-timeout allow` no longer rewinds a chain: when a later hook stays silent, the
|
|
160
|
+
rewrites and redactions earlier hooks already made are kept instead of being replaced by
|
|
161
|
+
the original text. A `tool_post` `replace` whose content array holds a part without a
|
|
162
|
+
`type` is refused (`hook_invalid_answer`) rather than stored - it would have been an HTTP
|
|
163
|
+
400 on the next request and on every resume of that session.
|
|
164
|
+
- The `turn_start` injection budget is per REQUEST again (it was effectively per thread,
|
|
165
|
+
because nothing reset it), a budget stop no longer reports a non-existent
|
|
166
|
+
`injection_stranded`, and `--on-hook-timeout error` now actually marks the request as
|
|
167
|
+
failed instead of only setting a code.
|
|
168
|
+
- A thread at `maxRequestsPerThread` answers `thread_capacity` (the ceiling existed as a
|
|
169
|
+
constant nothing consulted), and the per-point hook ceiling counts `"*"` as every tool.
|
|
170
|
+
|
|
171
|
+
- **`--result-schema`: the answer becomes a contract, not a paragraph.** The model
|
|
172
|
+
submits its result through a `submit_result` tool whose parameter schema IS your
|
|
173
|
+
JSON Schema, so a machine (not a parser you write and then distrust) decides
|
|
174
|
+
whether the answer conforms. A violation comes back as field paths the model can
|
|
175
|
+
fix inside the same prompt, and `--result-repair-turns` (default 1, max 2) bounds
|
|
176
|
+
how many times it may try. `result.structured` carries the accepted value,
|
|
177
|
+
`result.result` becomes its canonical JSON so the `| jq -r .result` idiom still
|
|
178
|
+
means "the answer", and the model's prose moves to `result.text`. Failure is a
|
|
179
|
+
shape rather than a mood: an explicit `null` plus
|
|
180
|
+
`error.code: "result_schema_unsatisfied"`, with `--on-result-unsatisfied
|
|
181
|
+
error|continue` deciding whether the run stops (exit 1) or keeps serving with
|
|
182
|
+
`result.isError: true`. Validation is two layers - Core's optional `ajv` plus a
|
|
183
|
+
bounded validator of this CLI's own that enforces `required`, nested
|
|
184
|
+
`properties`, `items` and `additionalProperties: false` on machines without it -
|
|
185
|
+
and `aragon doctor` / `aragon info --json` say which one is in force. See
|
|
186
|
+
[Hand back a typed result](README.md#hand-back-a-typed-result).
|
|
187
|
+
- **Requests are first class in bridge mode.** A long-lived `aragon exec
|
|
188
|
+
--input-format bridge` process used to have one set of budgets and one bill for
|
|
189
|
+
everything it ever did. Now every `prompt` ends with its own `request_end` -
|
|
190
|
+
`stopReason`, `turns`, `durationMs`, `usage`, `cost`, the last assistant text and
|
|
191
|
+
(under a schema) the accepted value - and the numbers are differences, so two
|
|
192
|
+
requests add up to the process total. `--request-max-turns` and
|
|
193
|
+
`--request-max-duration` bound ONE request without ending the process: its
|
|
194
|
+
`request_end` reports `max_turns` / `timeout` and the next caller is served
|
|
195
|
+
normally. `cancel_request{requestId?}` withdraws a single request - a queued one
|
|
196
|
+
is deleted and accounted as `cancelled` with `turns: 0`, an in-flight one is
|
|
197
|
+
aborted, and one that is already finished is refused with `unknown_turn` instead
|
|
198
|
+
of aborting a healthy run. `interrupt` still stops the whole run: two questions,
|
|
199
|
+
two frames. `aragon info --json` gained the `request_scope` capability and the
|
|
200
|
+
ceilings. See [One request at a time](README.md#one-request-at-a-time-budgets-billing-and-cancellation).
|
|
201
|
+
- **`--input-format bridge`: the headless mode can now be driven, not just piped.**
|
|
202
|
+
Four new surfaces on one switch, all additive - without the flag every event,
|
|
203
|
+
field and tool object is exactly what it was:
|
|
204
|
+
- **The caller decides.** `--permission-mode ask` makes `write_file` /
|
|
205
|
+
`edit_file` / `bash` / `skill_create` send a `decision_request` and wait for a
|
|
206
|
+
`decision` frame before they run (answer `allow`, `deny` with a reason, or
|
|
207
|
+
`allow` with `scope: "session"` to stop asking for that tool). `ask_user` and
|
|
208
|
+
`submit_plan` become reachable headlessly for the first time
|
|
209
|
+
(`decision_request{kind:'questions'|'plan'}`), and `skill_install` asks the
|
|
210
|
+
same way instead of being denied outright. A denial is a readable
|
|
211
|
+
`tool_result` the model can route around - never a crash, never a silent skip.
|
|
212
|
+
- **The caller serves tools.** `tool_register` declares up to 32 tools of your
|
|
213
|
+
own (`db_query`, `open_ticket`), full-replacement semantics, validated
|
|
214
|
+
(name, reserved names, JSON Schema, size) before anything is written; the
|
|
215
|
+
model then calls them and the CLI sends `tool_invoke` and waits for
|
|
216
|
+
`tool_result`, with the same timeout, cancellation and 100 KB clip the built-in
|
|
217
|
+
tools get. Registration lands at a turn boundary, never mid-iteration.
|
|
218
|
+
- **Requests are correlatable.** `prompt{id}` / `steer{id}` / `interrupt` in,
|
|
219
|
+
`ack` / `steering{steeringIds}` / `pong` out, plus `seq` on every stdout line
|
|
220
|
+
and `requestId` on `user` / `turn_state` / `assistant` / `tool_call` /
|
|
221
|
+
`tool_result`. `result` is still exactly once and last; `init` is still first.
|
|
222
|
+
- **Budgets for the new channel.** `--decision-timeout <ms>` (default 300000,
|
|
223
|
+
max 1800000), `--on-decision-timeout deny|error`, `--caller-tool-timeout <ms>`
|
|
224
|
+
(default 60000, max 600000). All three are usage errors without
|
|
225
|
+
`--input-format bridge`, as are a positional prompt and `--prompt-file`,
|
|
226
|
+
because bridge prompts come from stdin.
|
|
227
|
+
|
|
228
|
+
`aragon info --json` now reports `inputFormats` with `bridge`, `permissionModes`
|
|
229
|
+
with `ask`, and a `bridge` block (`protocolVersion`, `capabilities`,
|
|
230
|
+
`decisionKinds`, `gatedTools`, `limits`). `packages/cli/examples/bridge-host.mjs`
|
|
231
|
+
is a zero-dependency reference host. See
|
|
232
|
+
[Let the caller decide](README.md#let-the-caller-decide) and
|
|
233
|
+
[Serve your own tools](README.md#serve-your-own-tools).
|
|
234
|
+
- **The keyboard drawer (`Ctrl+G`), and a task label in the header.** The header now
|
|
235
|
+
teaches exactly ONE key: `^G more`. Pressing it slides a right-hand drawer in over
|
|
236
|
+
the current screen and lists EVERY binding top to bottom - the key, a one-or-two
|
|
237
|
+
word keyword, and the sentence that explains it on the selected row - grouped
|
|
238
|
+
COMPOSE / RUN / PANELS / SESSION / VIEW. The keyword column is DERIVED from the
|
|
239
|
+
table rather than fixed, so a keyword that cannot fit makes its row stack instead
|
|
240
|
+
of being cut; paging moves the cursor, so the selected row and its detail line can
|
|
241
|
+
never leave the screen. `d` toggles the status detail row and `r` regenerates the
|
|
242
|
+
label. The same list is reachable without a keyboard: `/keys [query]` opens it
|
|
243
|
+
pre-filtered. The header also says WHAT you are doing: the first prompt of a task is
|
|
244
|
+
compressed by the FAST tier into a few words in the language you wrote in, shown to
|
|
245
|
+
the right of the working directory, and cleared by `/clear` (and rebuilt from the
|
|
246
|
+
restored conversation after `/resume`). `/task` reports what the label says and
|
|
247
|
+
where it came from; `/task refresh` regenerates it. When the fast tier cannot run,
|
|
248
|
+
the label falls back to a prefix of your own words - which cannot lie - and
|
|
249
|
+
`taskSummary.fallback=false` shows nothing instead. New
|
|
250
|
+
`taskSummary.enabled` / `taskSummary.fallback` keys plus `ARAGON_TASK_SUMMARY`. See
|
|
251
|
+
[Keyboard drawer and task label](README.md#keyboard-drawer-and-task-label).
|
|
11
252
|
- **Acceptance criteria: a plan now has to say how you would know it is done.**
|
|
12
253
|
`todo_write` takes a second array in the SAME call - one checkable statement per
|
|
13
254
|
line plus the check the model will run for it - so the steps and the criteria can
|
|
@@ -110,6 +351,28 @@ were written.
|
|
|
110
351
|
|
|
111
352
|
### Changed
|
|
112
353
|
|
|
354
|
+
- **`Ctrl+G` opens the keyboard drawer; the status detail row moved to `d`.** The key
|
|
355
|
+
used to expand the extra status line, and its header hint competed with `^L`, `^P`,
|
|
356
|
+
`^I` and `^K` for room - so what a user saw depended on the terminal width instead of
|
|
357
|
+
on what keys exist. Those five clauses are gone from the header (`^G more` is the
|
|
358
|
+
only one left) and the detail row is now toggled by `d` INSIDE the drawer, which
|
|
359
|
+
also shows its `on`/`off` state. The status detail row's own hint now reads `^G then
|
|
360
|
+
d` rather than `^G less`, because `^G less` no longer does anything. `/help` gained
|
|
361
|
+
the `Ctrl+O` row it had always been missing and now describes `Ctrl+G` as the drawer.
|
|
362
|
+
- **`Ctrl+G` does not take the keyboard from an overlay that is waiting for a
|
|
363
|
+
decision.** With any other overlay open it replaces that overlay with the drawer;
|
|
364
|
+
with `confirm`, `question` or `plan` it stays inert, because those three hold an
|
|
365
|
+
unresolved promise to the agent and only `Esc` settles it - taking the dialog away
|
|
366
|
+
would hide it and leave the run waiting for ever.
|
|
367
|
+
- **`prompts.hint` is retired.** Its only reader was the `^P prompts` header clause,
|
|
368
|
+
which the drawer replaced, and a config key that cannot reach any surface is a lie.
|
|
369
|
+
An older `config.json` still loads (unknown keys are ignored) and `config list` no
|
|
370
|
+
longer prints it. Hand-editing was already the only way to set it: it was never in
|
|
371
|
+
`config set`.
|
|
372
|
+
- **`/keys` and `/task` shadow same-named skills.** Commands registered by the CLI win
|
|
373
|
+
over skill names, so a user with a skill called `keys` or `task` now reaches it as
|
|
374
|
+
`/skill:keys` / `/skill:task`. Both names are deliberately unaliased - an alias would
|
|
375
|
+
multiply the same shadowing.
|
|
113
376
|
- **BREAKING DEFAULT: the TUI now scrolls with your terminal (`scrollMode: native`).**
|
|
114
377
|
Until now the interactive TUI ran in the terminal's alternate screen with its own
|
|
115
378
|
scrollbar, wheel handling and drag-selection. The default is now **native scroll
|