@staix/agent-hub 0.12.20 → 0.12.22
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 +32 -0
- package/README.md +17 -9
- package/docs/agent-notes/adapters.md +13 -3
- package/docs/agent-notes/benchmarks.md +1 -1
- package/docs/agent-notes/bus.md +7 -3
- package/docs/agent-notes/daemon.md +12 -2
- package/docs/agent-notes/local-worker.md +5 -1
- package/docs/agent-notes/tasks.md +4 -2
- package/docs/agent-notes/tests.md +5 -2
- package/docs/bench.md +143 -0
- package/docs/events.md +15 -5
- package/docs/operations.md +510 -54
- package/docs/quickstart.md +16 -11
- package/docs/research.md +92 -0
- package/docs/security.md +49 -11
- package/docs/smoke.md +138 -0
- package/docs/specs/2026-09-19-agent-hub-design.md +227 -20
- package/docs/specs/2026-09-20-upgrade-recovery-design.md +152 -18
- package/docs/verified.json +34 -17
- package/package.json +1 -1
- package/plugins/agent-hub/.claude-plugin/plugin.json +1 -1
- package/plugins/agent-hub/server.js +3 -2
- package/src/adapters/acp.ts +77 -10
- package/src/adapters/codex-appserver.ts +72 -2
- package/src/adapters/local-worker.ts +46 -11
- package/src/adapters/pi.ts +215 -22
- package/src/cli/bench.ts +287 -0
- package/src/cli/console-state.ts +226 -28
- package/src/cli/console.ts +16 -4
- package/src/cli/facts-hook.ts +50 -8
- package/src/cli/help.ts +16 -5
- package/src/cli/identity.ts +10 -4
- package/src/cli/launch.ts +272 -18
- package/src/cli/main.ts +290 -30
- package/src/cli/preview.ts +13 -3
- package/src/cli/recovery-package.ts +17 -0
- package/src/cli/status-lines.ts +2 -1
- package/src/cli/terminal-recovery.ts +83 -31
- package/src/cli/upgrade-interactive.ts +297 -0
- package/src/cli/upgrade-runtime.ts +123 -20
- package/src/cli/upgrade.ts +108 -19
- package/src/hub/bench.ts +211 -0
- package/src/hub/bus.ts +24 -7
- package/src/hub/conductor.ts +22 -21
- package/src/hub/config-trust.ts +1 -0
- package/src/hub/control-client.ts +4 -1
- package/src/hub/daemon.ts +477 -86
- package/src/hub/delivery-journal.ts +6 -6
- package/src/hub/events.ts +4 -2
- package/src/hub/lifecycle.ts +8 -13
- package/src/hub/manager.ts +16 -14
- package/src/hub/peers.ts +2 -2
- package/src/hub/permission-mode.ts +44 -0
- package/src/hub/recovery-store.ts +59 -9
- package/src/hub/registry.ts +36 -22
- package/src/hub/research.ts +255 -0
- package/src/hub/tasks.ts +99 -55
- package/src/hub/tool-refusal.ts +7 -0
- package/src/hub/ui.ts +4 -1
- package/src/local/deny.ts +48 -1
- package/src/local/sandbox.ts +6 -3
- package/src/local/tools.ts +34 -11
- package/src/pi/extension.ts +43 -20
- package/src/pi/process-signature.ts +23 -0
- package/src/pi/tool-receipts.ts +24 -2
- package/src/ui/index.html +98 -17
- package/src/ui/task-progress.ts +33 -0
package/docs/operations.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Operations guide
|
|
2
2
|
|
|
3
|
-
This guide describes ahub 0.12.
|
|
3
|
+
This guide describes ahub 0.12.22 and control protocol 16. Live verification
|
|
4
4
|
results and remaining prerequisites are recorded separately in [the smoke ledger](smoke.md).
|
|
5
5
|
|
|
6
6
|
## Command help
|
|
@@ -49,15 +49,17 @@ counts `needs_review` deliveries, in the attention and failure tones.
|
|
|
49
49
|
|
|
50
50
|
Peers, Approvals, Tasks and Queue are tables with a header row. A column is as
|
|
51
51
|
wide as its widest cell, up to a third of the terminal; the last column takes
|
|
52
|
-
the rest and is cut with `...`.
|
|
52
|
+
the rest and is cut with `...`. In Tasks, TITLE takes the rest and the STAGE
|
|
53
|
+
meter keeps a fixed column at the right edge.
|
|
53
54
|
|
|
54
55
|
| Panel | Columns |
|
|
55
56
|
|---|---|
|
|
56
|
-
| Peers | PEER STATE LINK Q ! REVIEW PAUSE QUOTA MODEL |
|
|
57
|
+
| Peers | PEER STATE MODE LINK Q ! REVIEW PAUSE QUOTA MODEL |
|
|
57
58
|
| Approvals | ID PEER LEFT TITLE |
|
|
58
|
-
| Tasks | ID STATE OWNER REVIEWER CLASS AGE TITLE |
|
|
59
|
+
| Tasks | ID STATE OWNER REVIEWER CLASS AGE TITLE STAGE |
|
|
59
60
|
| Queue | ID PEER STATE REV AGE |
|
|
60
61
|
|
|
62
|
+
The MODE column appears only when at least one peer has a non-`ask` mode.
|
|
61
63
|
Zero counters and unknown values read `-`. Durations read `45s`, `12m`, `3h05m`
|
|
62
64
|
or `2d03h`; quota resets and budget pauses read `in 2h13m`, and a pause by the
|
|
63
65
|
console user reads `user`. When a peer's status carries #205's optional
|
|
@@ -83,14 +85,43 @@ showed it. The footer's last line lists only the keys that act in the current
|
|
|
83
85
|
mode, panel and state. A notice shows on the footer's second line until the next
|
|
84
86
|
key or for about ten seconds: errors and refusals (a refused command, a request
|
|
85
87
|
error, the size refusal) in the failure tone, notices that only inform (a closed
|
|
86
|
-
or missing selection) in the attention tone. With a detail open, `p`, `r` and `a`
|
|
88
|
+
or missing selection) in the attention tone. With a detail open, `p`, `r`, `m` and `a`
|
|
87
89
|
act on the item the detail shows. `:` enters a command; its output lines
|
|
88
|
-
start at column 4, as message bodies do. Assignment, delivery resolution
|
|
89
|
-
|
|
90
|
-
80 columns by 24 rows; smaller terminals stay in stream mode.
|
|
91
|
-
|
|
90
|
+
start at column 4, as message bodies do. Assignment, delivery resolution, allow
|
|
91
|
+
decisions and a never-ask permission mode require confirmation; delivery resolution requires a reason. Tasks use the same public redaction as the board. Panels need at least
|
|
92
|
+
80 columns by 24 rows; smaller terminals stay in stream mode. Full task and queue reads run only in panels; stream progress uses the existing
|
|
93
|
+
status state counts and never fetches the board. Leaving restores
|
|
92
94
|
the terminal and returning from panels replays the bounded stream buffer.
|
|
93
95
|
|
|
96
|
+
The Tasks panel summarizes the whole public board with an approved/all fraction,
|
|
97
|
+
a 20-cell ASCII bar and state counts. Proposed tasks with unapproved or missing
|
|
98
|
+
`after` dependencies count as waiting; PII stubs count normally. Each task has a
|
|
99
|
+
four-cell stage meter (proposed, in progress, review, approved). Changes requested
|
|
100
|
+
returns to in progress and uses `!` plus the failure tone. Narrow panels retain
|
|
101
|
+
the fraction/bar before optional counts, which appear in fixed importance order:
|
|
102
|
+
changes requested, review, waiting, in progress, proposed, then unknown when nonzero. Narrow widths drop
|
|
103
|
+
only the suffix. Bar cells and percentages round down so open tasks never imply
|
|
104
|
+
completion. Stream `tasks N/M approved` lives on the approvals footer line, leaving the
|
|
105
|
+
peer/queue footer line intact. Operator notices or an already-full approvals line
|
|
106
|
+
take precedence over this optional count. A brief notice hides it only until the
|
|
107
|
+
notice's ten-second TTL expires, then the retained known count returns on redraw; the scroll
|
|
108
|
+
region starts at row 1 to preserve terminal scrollback. All existing status state
|
|
109
|
+
counts contribute to its total, including an unfamiliar state. Until a successful
|
|
110
|
+
status read it says loading; later failures preserve the prior count with a notice.
|
|
111
|
+
Panels retain their existing full-board refresh and loading/error handling.
|
|
112
|
+
|
|
113
|
+
The dashboard Task board shows the same shared public-board progress model,
|
|
114
|
+
including state counts, waiting, stacked segments and a labeled four-stage track
|
|
115
|
+
per row. Filtering rows does not filter overall progress. The existing signature guard
|
|
116
|
+
updates the live region only when progress changes. The stacked SVG fills the
|
|
117
|
+
section width; its text legend carries every state count. Theme offers System,
|
|
118
|
+
Light and Dark; it is applied before first paint and stored in a host-scoped loopback cookie
|
|
119
|
+
(`Path=/`, `SameSite=Strict`, one-year lifetime), shared across dashboard ports.
|
|
120
|
+
Blocked cookie access defaults to System on reload and never prevents operation. System follows
|
|
121
|
+
OS color preference through CSS. Both inline scripts and the style retain CSP
|
|
122
|
+
hashes; there are no external chart/theme assets. Live theme/readability inspection
|
|
123
|
+
is tracked in `docs/smoke.md` and remains unverified until performed.
|
|
124
|
+
|
|
94
125
|
Console color policy is `--color=auto|always|never`, with `auto` as the default.
|
|
95
126
|
Auto enables color only when both input and output are TTYs, `TERM` is not
|
|
96
127
|
`dumb`, and `NO_COLOR` is empty or absent. Explicit `always` overrides these
|
|
@@ -105,11 +136,30 @@ management or the final reset. Invalid values fail before connecting.
|
|
|
105
136
|
| Waiting and attention | Yellow | Busy/paused peer, pending approval, confirmation, review/ready task, important priority |
|
|
106
137
|
| Failure and intervention | Red | Failed/check-failed task, undeliverable/overflow, `needs_review` queue, denial request or expired/cancelled approval |
|
|
107
138
|
| Metadata | Bright black | Ages, remaining approval time and rules |
|
|
139
|
+
| Stream peer names | Claude bright blue (94), Codex bright cyan (96); all others default | Fixed map, no hashing or themes |
|
|
140
|
+
| Stream `task` keyword | Magenta | Task event keyword |
|
|
141
|
+
| Stream numbers | Bold default foreground (1) | Percentages, durations and measured times |
|
|
142
|
+
| Stream issue references | Underlined default foreground (4) | `#232` |
|
|
143
|
+
| Stream task references | Magenta underlined (4;35) | `#1` following `task`/`Task`, `[task #1]`, `[review #1]` |
|
|
108
144
|
|
|
109
145
|
Offline peers, primary titles, action details and message body lines keep the
|
|
110
146
|
terminal's default foreground. Labels and prompts remain readable without
|
|
111
|
-
color. Stream styling applies only to the
|
|
112
|
-
|
|
147
|
+
color. Stream styling applies only to meaningful tokens in the first line: peer names,
|
|
148
|
+
`task`, numbers and references. Ordinary words keep the default foreground.
|
|
149
|
+
Task prefixes and bracketed `[review #N]` identify task references
|
|
150
|
+
case-insensitively; prose `review #N` and other `#N` tokens are issue references. Legacy assign/which notices lack typed reference
|
|
151
|
+
ranges and use the issue tone until structured spans identify them. The dated
|
|
152
|
+
#239 palette reserves green/yellow/red for states, cyan for navigation and
|
|
153
|
+
blue/cyan for Claude/Codex. Tokens use plain magenta, underlining and bold default
|
|
154
|
+
foreground; no token SGR equals a state or peer SGR. The `task` keyword is never
|
|
155
|
+
bold. Only hub-written peer slots are colored, not title words or decline reasons.
|
|
156
|
+
State words and structural markers retain their event tone. Numeric-looking
|
|
157
|
+
identifiers stay plain. Wrapped headers normalize tabs before tokenization; plain headers preserve
|
|
158
|
+
their original tabs with color enabled or disabled.
|
|
159
|
+
A projection miss on the first physical header keeps only structural tones.
|
|
160
|
+
A missed continuation and every following continuation are fully plain; their
|
|
161
|
+
text cannot supply structural tones. Projection carries its span index forward
|
|
162
|
+
instead of rescanning earlier spans for every line. Bodies and command output stay plain. A local denial is shown as requested, not
|
|
113
163
|
as a confirmed receipt; a remote answered closure has no option-kind metadata
|
|
114
164
|
and is not guessed to be a denial. A fixed palette is applied after terminal
|
|
115
165
|
control sanitization. Width, clipping, wrapping and cursor placement use plain
|
|
@@ -129,8 +179,8 @@ recorded separately in the smoke ledger; fake-terminal tests establish policy,
|
|
|
129
179
|
geometry, sanitization and restoration only.
|
|
130
180
|
|
|
131
181
|
The command input accepts existing status, board, task, review, say, pause,
|
|
132
|
-
resume, budget, queue, permit, ask, remember, route, turns, undo,
|
|
133
|
-
report operations. It executes an argument vector with closed stdin. Lifecycle,
|
|
182
|
+
resume, budget, queue, permit, permission, ask, remember, route, turns, undo,
|
|
183
|
+
check-path and report operations. It executes an argument vector with closed stdin. Lifecycle,
|
|
134
184
|
launch, nested console, setup, UI, logs and tail commands are refused.
|
|
135
185
|
|
|
136
186
|
## Conducting a team from Claude Code or Codex
|
|
@@ -163,7 +213,8 @@ final response in the native TUI. Broadcasting implementation instructions can
|
|
|
163
213
|
cause an otherwise unassigned owner to claim duplicate work.
|
|
164
214
|
|
|
165
215
|
The person answers approvals in the console, resolves `needs_review` deliveries
|
|
166
|
-
with `ahub queue resolve`,
|
|
216
|
+
with `ahub queue resolve`, stops a headless peer with `ahub stop`, and handles
|
|
217
|
+
budget overrides and hub lifecycle.
|
|
167
218
|
Running these commands from an agent shell is refused; the CLI retains the
|
|
168
219
|
agent's identity even when invoked through a shell tool.
|
|
169
220
|
|
|
@@ -177,9 +228,11 @@ off withdraws pending feed notices.
|
|
|
177
228
|
|
|
178
229
|
For a Claude conductor, `ahub claude` also observes native session and turn
|
|
179
230
|
boundaries when facts injection and task-idle sweeps are off. Keep the managed
|
|
180
|
-
hooks enabled to measure completion and supervision usage.
|
|
181
|
-
`--settings`
|
|
182
|
-
|
|
231
|
+
hooks enabled to measure completion and supervision usage. Your own
|
|
232
|
+
`--settings` (given once, a JSON object or file) keeps that observation: the
|
|
233
|
+
launcher adds the hub's hooks to its hooks and warns only that the status line
|
|
234
|
+
tee is off; one that sets `disableAllHooks` is refused. The launcher records its
|
|
235
|
+
private session identity in ordinary
|
|
183
236
|
terminals too; this does not grant terminal-recovery authority.
|
|
184
237
|
Ordinary Claude sessions with turn-free facts or task-idle sweeps enabled get the
|
|
185
238
|
same session/start observation. Other ordinary launches remain non-opt-in.
|
|
@@ -216,7 +269,8 @@ may request an approval.
|
|
|
216
269
|
|
|
217
270
|
`.agenthub/config.json` can be committed and shared. The fields that choose
|
|
218
271
|
what the hub runs, which files it sends as credentials, where task text goes,
|
|
219
|
-
|
|
272
|
+
how far the local worker's sandbox reaches, or how often a peer asks before it
|
|
273
|
+
acts (`kimi_cmd`, `permission_modes`, `codex_bin`,
|
|
220
274
|
`pi.cmd`, `checks`, `mlx.bin`, `mlx.runtimeDir`, `mlx.modelPath`, `omniroute.urls`,
|
|
221
275
|
`omniroute.access_hosts`, the `omniroute` key files, `memory.worker_url`,
|
|
222
276
|
`local.read_allow`, `local.bash_network`, `local.network_allow`) are machine-local:
|
|
@@ -233,6 +287,19 @@ project and a temp dir of their own (`TMPDIR`, made for each command and
|
|
|
233
287
|
removed when it ends; one left by a hub crash, named `ahub-cmd-*`, goes with
|
|
234
288
|
the OS temp cleanup), and nothing else; the shared temp dirs are closed.
|
|
235
289
|
|
|
290
|
+
Some names stay closed to those commands, and to Pi's, wherever they may
|
|
291
|
+
otherwise write: anything named `.git` at any depth, the `config` and `hooks`
|
|
292
|
+
directly under a `.git`, `commondir` below it, and `.agenthub` anywhere in the
|
|
293
|
+
project. A submodule's git directory under `.git/modules/` is not covered yet
|
|
294
|
+
(#281). A command can therefore not create, rename or remove a `.git`:
|
|
295
|
+
`git init`, `git clone` and `git worktree add` do not run inside the sandbox,
|
|
296
|
+
and neither do `git worktree remove`, `git worktree prune` of a stale record,
|
|
297
|
+
`git clean -ffd` over a nested repository or `rm -rf` of a directory that
|
|
298
|
+
holds a `.git`. `git add`, `commit`, `stash`, `checkout` and `gc` work, and
|
|
299
|
+
`.gitignore` and `.github/` stay writable; a commit that your git configuration
|
|
300
|
+
signs with a key under `~/.ssh` or `~/.gnupg` fails, because the sandbox cannot
|
|
301
|
+
read those directories. Run a refused command yourself, in a terminal.
|
|
302
|
+
|
|
236
303
|
Network is off unless `local.bash_network` says otherwise. With `true` it goes
|
|
237
304
|
only through the hub's egress proxy on a loopback port (0.11): commands get
|
|
238
305
|
`HTTPS_PROXY` and the other proxy variables, the proxy opens HTTPS (`CONNECT`,
|
|
@@ -399,7 +466,10 @@ offered to nobody, cannot be claimed, accepted or marked done, and `ahub route
|
|
|
399
466
|
explain <id>` says what it waits for. When the last one is approved, the task
|
|
400
467
|
goes through assignment like a new one; if the hub stopped before it got that
|
|
401
468
|
far, the task is offered within a minute of a peer that can take it attaching
|
|
402
|
-
to the next run.
|
|
469
|
+
to the next run. An offer is used up only by an assignment, after an approval
|
|
470
|
+
and on the timer alike. If no peer can take it or a ready/assignment write fails,
|
|
471
|
+
the task stays eligible; the timer adds no history or notices while nobody can
|
|
472
|
+
take it.
|
|
403
473
|
`ahub board --ready` and
|
|
404
474
|
`hub_task_list {ready: true}` list the proposed tasks with nothing left to wait
|
|
405
475
|
for. Dependencies are fixed when a task is proposed and can only name tasks that
|
|
@@ -498,6 +568,37 @@ turn, otherwise the peer's only `in_progress` task. Everything else is reported
|
|
|
498
568
|
unattributed, and records from before 0.12.18 as a separate bucket; neither is
|
|
499
569
|
redistributed (rules: [events](events.md#per-task-usage-reports)).
|
|
500
570
|
|
|
571
|
+
## Research records
|
|
572
|
+
|
|
573
|
+
With `"research": { "enabled": true }` in `.agenthub/config.json` (off by default), the hub keeps one record per
|
|
574
|
+
approved task in `~/.agenthub/research/`, shared by every project that opts in: outcome, review rounds, rework, failed
|
|
575
|
+
checks, tokens and time per peer and model, never task or message text ([schema](research.md)).
|
|
576
|
+
|
|
577
|
+
```bash
|
|
578
|
+
ahub research --since 30d # success, first-pass, rework and check-failure rates; tokens and wall time per task
|
|
579
|
+
ahub research --all --json # every opted-in project on this machine
|
|
580
|
+
ahub research export --format csv # the records, for your own analysis
|
|
581
|
+
ahub research backfill # build this project's records once from events.jsonl
|
|
582
|
+
ahub task label 12 reverted # a later verdict (ok, regressed, reverted, incomplete, wrong, abandoned)
|
|
583
|
+
```
|
|
584
|
+
|
|
585
|
+
## Benchmarks
|
|
586
|
+
|
|
587
|
+
In a git repository kept for benchmarks (`"bench": { "enabled": true }` in its `.agenthub/config.json`, a clean tree),
|
|
588
|
+
`ahub bench` runs a suite of tasks against the peers attached to its hub and compares configurations: pass rate, first
|
|
589
|
+
pass, tokens and wall time per attempt, with bootstrap intervals between arms. Each attempt resets the tree to the
|
|
590
|
+
task's commit, so never enable it in a project you work in. The project must be the root of its repository with
|
|
591
|
+
nothing tracked under `.agenthub/`, and the suite file lives outside its tree ([suite format and store](bench.md)).
|
|
592
|
+
|
|
593
|
+
```bash
|
|
594
|
+
ahub bench run ../suites/suite.json --arm baseline --repeat 5 # a person's: resets the tree, proposes the tasks
|
|
595
|
+
ahub bench status # the run in progress
|
|
596
|
+
ahub bench compare baseline ripwire-on # arms side by side, inconclusive below 5 attempts each
|
|
597
|
+
ahub bench export --format csv
|
|
598
|
+
```
|
|
599
|
+
|
|
600
|
+
The dashboard's Benchmarks section shows the latest runs and, per suite with two or more arms, the comparison.
|
|
601
|
+
|
|
501
602
|
## Turns and undo
|
|
502
603
|
|
|
503
604
|
In a git work tree the hub snapshots the project's tracked and unignored files (the
|
|
@@ -607,8 +708,9 @@ stays the default until an evaluation says otherwise (`docs/cooperbench.md`).
|
|
|
607
708
|
held.
|
|
608
709
|
- Verified context path. A hub-launched Claude session (`ahub claude`) gets the
|
|
609
710
|
hub's hook before and after every tool call and at the end of each turn, in
|
|
610
|
-
its `--settings` next to the status line tee (a `--settings` of your own
|
|
611
|
-
them
|
|
711
|
+
its `--settings` next to the status line tee (a `--settings` of your own keeps
|
|
712
|
+
them, added to its own hooks, and loses only the tee). Codex gets context by
|
|
713
|
+
steer into its running turn. Until a fact or a
|
|
612
714
|
one-line probe has been read back, the peer counts as unverified: for Claude
|
|
613
715
|
the row Claude Code writes in its transcript for the hook's additional
|
|
614
716
|
context, matched by tool use id and the offer's id; for Codex the steered
|
|
@@ -753,6 +855,107 @@ stays the default until an evaluation says otherwise (`docs/cooperbench.md`).
|
|
|
753
855
|
turns the stale-notice drop off, for a controlled comparison only (the #106
|
|
754
856
|
ablation in `docs/cooperbench.md`); the hub logs it at start.
|
|
755
857
|
|
|
858
|
+
## Permission modes for running peers
|
|
859
|
+
|
|
860
|
+
Use a plain terminal to inspect or change a running peer:
|
|
861
|
+
|
|
862
|
+
```bash
|
|
863
|
+
ahub permission
|
|
864
|
+
ahub permission local ask-when-needed
|
|
865
|
+
ahub permission kimi never-ask --yes
|
|
866
|
+
ahub permission kimi ask
|
|
867
|
+
```
|
|
868
|
+
|
|
869
|
+
In the console Peers panel, `m` opens the three modes. Never-ask requires `y`,
|
|
870
|
+
even for a typed command carrying --yes. Its confirmation says whether the peer
|
|
871
|
+
uses its own tools outside the hub sandbox (Kimi), the hub sandbox/path guard/
|
|
872
|
+
denylist (Pi and local), or native vendor boundaries (Claude and Codex).
|
|
873
|
+
|
|
874
|
+
| Peer | ask-when-needed | never-ask | ask |
|
|
875
|
+
| --- | --- | --- | --- |
|
|
876
|
+
| Kimi Code CLI | ACP yolo | ACP auto | ACP default |
|
|
877
|
+
| Codex | on-request for every outgoing turn/start | never for every outgoing turn/start | Restore the known native policy once, then stop overriding |
|
|
878
|
+
| Claude | Allow resolving project file tools outside .agenthub/.git/.claude/.codex/.qwen/.kimi/.pi/.mcp.json | Hook allow for every tool | No hook decision |
|
|
879
|
+
| Pi and local | Grant write/edit outside native agent configuration paths; read never asks; shell/git writes ask | Grant each tool once within the existing sandbox/guard | Normal approval handling |
|
|
880
|
+
|
|
881
|
+
Kimi's map applies only to the actual Kimi Code CLI identity, not another ACP
|
|
882
|
+
agent configured in kimi_cmd. Qwen and other ACP agents start fresh/resumed
|
|
883
|
+
unmanaged without mode changes or mode-based startup refusals; runtime changes
|
|
884
|
+
are refused until their own map exists.
|
|
885
|
+
A set_mode timeout leaves the peer offline with mode unknown; inspect and
|
|
886
|
+
restart before using it. Missing ids and refused startup changes name the mode.
|
|
887
|
+
The benign Kimi live check verifies file edits and harmless shells in both
|
|
888
|
+
modes without requests; risky-action behavior remains unverified.
|
|
889
|
+
|
|
890
|
+
Claude's hook is installed in every managed launch. File grants use canonical
|
|
891
|
+
real paths under the authenticated project root. Missing targets, symlink
|
|
892
|
+
escapes, metadata directories and uncertain wildcard targets leave the native
|
|
893
|
+
rules in charge. Grep/Glob path-bearing filters cannot bypass the exclusions.
|
|
894
|
+
Other tools retain native decisions, which may already allow a harmless shell.
|
|
895
|
+
Permission-only PreToolUse never marks Claude busy; former observation hooks
|
|
896
|
+
retain their native-turn role. Settings are passed as a 0600 state file retained
|
|
897
|
+
until that native launch exits. Live/unknown previous launchers keep their files;
|
|
898
|
+
verified dead wrapper and native identities permit crash cleanup. Managed direct
|
|
899
|
+
binary launches record their native PID while running; crash cleanup also requires
|
|
900
|
+
that PID to be absent from the process table. Scripts/interpreters and unavailable
|
|
901
|
+
process evidence retain the settings file. Every launch atomically replaces
|
|
902
|
+
`claude-launch.json` with its current managed hook/session identity. Native PID
|
|
903
|
+
proof is separate in a private `claude-native-<binding-hash>.json` file, so an old
|
|
904
|
+
callback cannot overwrite a newer launch record. Only that native proof uses
|
|
905
|
+
`claude-launch.lock`, published with its signed owner already written. It waits
|
|
906
|
+
up to 250 ms for a verified live owner. An empty, unsigned or unverifiable lock
|
|
907
|
+
is retained and disables native identity/crash cleanup for this launch; the
|
|
908
|
+
warning names the path and what is off. Managed hooks and session observations
|
|
909
|
+
still use the newly published launch record. Inspect the named lock and verify
|
|
910
|
+
its owner has ended before removing it; a signed dead lock is recovered. Without
|
|
911
|
+
separate native identity proof, old settings remain for manual inspection. Settings carry
|
|
912
|
+
no inline caller values in argv. Status reports an unverified Claude hook as
|
|
913
|
+
unverified. Unattended native sessions cannot take a runtime mode; restart
|
|
914
|
+
without that native flag. Codex must be behind the hub proxy, and returning to
|
|
915
|
+
ask while attached requires a known native approval baseline. If unavailable,
|
|
916
|
+
close the TUI, then run `ahub permission codex ask` before starting `ahub codex`.
|
|
917
|
+
For a known baseline, a detached proxy keeps restoration owed per thread and sends
|
|
918
|
+
that policy once on the resumed thread's next accepted turn. Visiting another thread
|
|
919
|
+
does not clear the debt. The restoring turn uses that kept baseline even if the
|
|
920
|
+
TUI echoes the resume response's sticky override, whether ask was selected before
|
|
921
|
+
or after that resume. Rejected turns retain it. The protection covers that one
|
|
922
|
+
restoring turn. A later TUI turn that repeats the policy looks exactly like a
|
|
923
|
+
person's explicit native policy choice and is passed through; the proxy cannot
|
|
924
|
+
resolve that ambiguity. An explicit choice on the restoring turn is delayed
|
|
925
|
+
until the following turn. A baseline that was never reported
|
|
926
|
+
is explained in the CLI reply, and that old thread keeps its last policy.
|
|
927
|
+
An offline or absent peer accepts
|
|
928
|
+
`ahub permission <peer> ask` to clear its hub choice. Codex --unattended launch
|
|
929
|
+
is refused under a non-ask choice; clear it with `ahub permission codex ask`
|
|
930
|
+
first. A pending never-ask default cannot be confirmed for an unattended launch.
|
|
931
|
+
|
|
932
|
+
Project defaults use permission_modes in .agenthub/config.json or
|
|
933
|
+
.agenthub/config.local.json. Keys merge per peer; a local ask overrides the
|
|
934
|
+
same peer's earlier default, while an empty local block preserves it. Every
|
|
935
|
+
non-ask default start logs its mode and contributing filename. Automatic
|
|
936
|
+
config effects stop at ask-when-needed. Git-tracked defaults are ignored and never
|
|
937
|
+
offered for confirmation. Never-ask from an applied untracked file starts in ask
|
|
938
|
+
and appears as a pending source-labelled console question at hub start.
|
|
939
|
+
Only its separate console y enables that default; there is no boot --yes
|
|
940
|
+
bypass. Only explicit n declines and keeps ask for that hub. Esc defers without declining
|
|
941
|
+
and leaves approval keys usable; a refused y is not reoffered until its state changes. A successful runtime change removes that pending default. Runtime choices expire when the hub
|
|
942
|
+
stops, and a later restart asks again for a never-ask config default.
|
|
943
|
+
|
|
944
|
+
The CLI marker and console-role checks are operating policy, not a hostile-agent
|
|
945
|
+
boundary. An agent able to run unrestricted shells may clear its markers,
|
|
946
|
+
use --yes, or read the control token and act as the console. Hub tools,
|
|
947
|
+
conductor operations and ordinary agent messages have no direct mode-changing
|
|
948
|
+
or startup-confirmation operation, but those checks do not contain such a
|
|
949
|
+
shell. Review the boundaries in [security](security.md) before choosing a mode.
|
|
950
|
+
Modes never alter native sandboxes, hub path guards or denylists.
|
|
951
|
+
|
|
952
|
+
Permission mode starts reconcile the current operator choice after native startup before bus delivery. Each non-ask start logs the mode and its config or runtime source. Dropped git-tracked defaults never appear as startup confirmations. Non-Kimi ACP peers run unmanaged, preserving their native fresh/resumed mode; permission commands refuse changes until a vendor map exists.
|
|
953
|
+
|
|
954
|
+
Status shows non-ask modes and pending defaults, and each runtime change emits
|
|
955
|
+
permission_mode (peer/from/to) with no tool arguments under events schema 1.
|
|
956
|
+
The optional control requests preserve PROTOCOL; an older hub answers unknown
|
|
957
|
+
and the CLI advises upgrading.
|
|
958
|
+
|
|
756
959
|
## Approvals and pauses
|
|
757
960
|
|
|
758
961
|
Inspect permission requests in the terminal:
|
|
@@ -771,9 +974,11 @@ ahub permit <request-id> allow
|
|
|
771
974
|
|
|
772
975
|
Use the exact option shown by `ahub tail`; do not approve an unresolved or
|
|
773
976
|
unexpected request. On macOS a waiting request also raises a desktop
|
|
774
|
-
notification that names the peer and, for Kimi, the
|
|
775
|
-
run. An unanswered request is cancelled after `approvals.timeout_s` (default
|
|
977
|
+
notification that names the peer and, for Kimi, Pi and the local worker, the tool,
|
|
978
|
+
never what it would run. An unanswered request is cancelled after `approvals.timeout_s` (default
|
|
776
979
|
120, 30 to 3600) in `.agenthub/config.json`, and the console and log say so.
|
|
980
|
+
Pi and the local worker are told that nobody answered, and two such requests in
|
|
981
|
+
a row end their turn ([Pi/local unanswered approvals](#pilocal-unanswered-approvals)).
|
|
777
982
|
Set `approvals.notify` to `false` to turn notifications off; a hub started
|
|
778
983
|
without a project config file raises none. While Kimi waits for an answer its
|
|
779
984
|
turn is kept alive, so a timeout longer than the inactivity watchdog does not
|
|
@@ -832,6 +1037,9 @@ has only `ahub status`, `ahub tail` and the task board.
|
|
|
832
1037
|
|
|
833
1038
|
Resolution requires the current observed revision. A retry closes the old
|
|
834
1039
|
record and creates one linked attempt. Stale or conflicting resolutions fail.
|
|
1040
|
+
A refused or failed resolution puts back only that peer's queue and preface
|
|
1041
|
+
and keeps every pause, including budget and conductor holds. A storage failure
|
|
1042
|
+
stops the bus; a validation refusal leaves it running under the existing holds.
|
|
835
1043
|
If dispatching or accepted work has an uncertain outcome after a crash,
|
|
836
1044
|
the system marks it `needs_review`. Establish evidence before choosing
|
|
837
1045
|
`completed`, `retry`, or `discard`. Timeouts,
|
|
@@ -876,11 +1084,15 @@ The daemon waits for shutdown work that must survive termination. Afterward,
|
|
|
876
1084
|
run `ahub status` or `ahub up` and read the project state before restarting
|
|
877
1085
|
peers. A stopped daemon does not delete task records or the durable journal.
|
|
878
1086
|
Do not kill a native terminal by PID or start a replacement while ownership is
|
|
879
|
-
uncertain.
|
|
1087
|
+
uncertain. To stop one headless peer and keep the hub running, use
|
|
1088
|
+
`ahub stop <peer>` ([Stop one headless peer](#stop-one-headless-peer)).
|
|
880
1089
|
|
|
881
1090
|
Shutdown is bounded: once it begins, a daemon that cannot finish within
|
|
882
1091
|
15 seconds exits anyway, and a peer that refuses to stop is logged rather than
|
|
883
|
-
allowed to block state cleanup. A
|
|
1092
|
+
allowed to block state cleanup. A managed Pi tool that does not settle within
|
|
1093
|
+
the stop grace does not block the teardown either: the stop goes on
|
|
1094
|
+
(shutdown, owner teardown, process stop) and reports the failure at the end.
|
|
1095
|
+
A native Pi TUI owner that does not exit
|
|
884
1096
|
gracefully is terminated by its verified process identity (never a bare PID),
|
|
885
1097
|
so a restarted hub never launches beside a survivor. A hub whose project root
|
|
886
1098
|
or state directory was deleted stops itself within about 10 seconds; the
|
|
@@ -910,8 +1122,10 @@ The runtime reset (the default):
|
|
|
910
1122
|
- drops the agent session resume pointers `sessions.json`,
|
|
911
1123
|
`claude-session.json` and `claude-context.json`, so the next launch starts
|
|
912
1124
|
new agent sessions. Pi transcripts under `pi-sessions/` stay;
|
|
913
|
-
- removes the manifest (`status.json`, `control-token`, `hub.pid`
|
|
914
|
-
when it did not stop cleanly, once no
|
|
1125
|
+
- removes the manifest (`status.json`, `control-token`, and `hub.pid` from a
|
|
1126
|
+
hub of 0.12.20 or older) a hub left when it did not stop cleanly, once no
|
|
1127
|
+
process behind it is alive (a pid that now belongs to another process counts
|
|
1128
|
+
as gone when the manifest carries the hub's signature, from 0.12.21);
|
|
915
1129
|
- keeps the board (tasks, reviews, outcomes, turns, touches), `hub.log`,
|
|
916
1130
|
`events.jsonl` and `cli-audit/`, recovery records, execution budgets,
|
|
917
1131
|
configuration and files the hub does not own.
|
|
@@ -999,23 +1213,38 @@ Rows without a live process are stale registrations; forget them with
|
|
|
999
1213
|
|
|
1000
1214
|
## Upgrade and crash recovery
|
|
1001
1215
|
|
|
1216
|
+
On a terminal, one command reviews, applies and follows an upgrade:
|
|
1217
|
+
|
|
1218
|
+
```bash
|
|
1219
|
+
ahub upgrade
|
|
1220
|
+
```
|
|
1221
|
+
|
|
1222
|
+
Without `--to` the target is the registry's latest release. When that release is
|
|
1223
|
+
newer than the installed CLI, the CLI names the release's own coordinator
|
|
1224
|
+
(`bun x --package @staix/agent-hub@<version> ahub upgrade --to <version>`), asks,
|
|
1225
|
+
and hands over to it, so the version is never typed twice and an older CLI never
|
|
1226
|
+
coordinates. From an installed CLI older than 0.12.22, start it once by hand:
|
|
1227
|
+
`bunx --package @staix/agent-hub@<version> ahub upgrade`. The screens are
|
|
1228
|
+
described under [Interactive upgrade](#interactive-upgrade); the rest of this
|
|
1229
|
+
section is what they run.
|
|
1230
|
+
|
|
1002
1231
|
Upgrade running projects with the target release's own coordinator. It accepts
|
|
1003
1232
|
a running source on control protocol 9 (0.6.x), 10 (0.7.0 through 0.12.0),
|
|
1004
|
-
11 (0.12.1 and 0.12.2), 12 (0.12.3), 13 (0.12.4 through 0.12.15), 14 (0.12.16), 15 (0.12.17 through 0.12.19) or 16 (0.12.20), and only
|
|
1233
|
+
11 (0.12.1 and 0.12.2), 12 (0.12.3), 13 (0.12.4 through 0.12.15), 14 (0.12.16), 15 (0.12.17 through 0.12.19) or 16 (0.12.20 through 0.12.22), and only
|
|
1005
1234
|
a target on its own protocol, so the target's coordinator fits every supported
|
|
1006
1235
|
source and carries every recovery fix released up to it. Protocol 8 and older
|
|
1007
1236
|
(0.5.x and earlier) are refused as `manual-bootstrap-required`. Run from the
|
|
1008
1237
|
project directory, without replacing the global CLI first:
|
|
1009
1238
|
|
|
1010
1239
|
```bash
|
|
1011
|
-
bunx --package @staix/agent-hub@0.12.
|
|
1012
|
-
bunx --package @staix/agent-hub@0.12.
|
|
1240
|
+
bunx --package @staix/agent-hub@0.12.22 ahub upgrade --to 0.12.22 --dry-run
|
|
1241
|
+
bunx --package @staix/agent-hub@0.12.22 ahub upgrade --to 0.12.22 --yes
|
|
1013
1242
|
```
|
|
1014
1243
|
|
|
1015
1244
|
| Running now | Coordinator to use |
|
|
1016
1245
|
| --- | --- |
|
|
1017
1246
|
| 0.6.x (protocol 9) | the target's, through `bunx` as above |
|
|
1018
|
-
| 0.7.0 through 0.12.0 (protocol 10), 0.12.1 and 0.12.2 (protocol 11), 0.12.3 (protocol 12), 0.12.4 through 0.12.15 (protocol 13), 0.12.16 (protocol 14), 0.12.17 through 0.12.19 (protocol 15), 0.12.20 (protocol 16) | the target's, through `bunx` as above |
|
|
1247
|
+
| 0.7.0 through 0.12.0 (protocol 10), 0.12.1 and 0.12.2 (protocol 11), 0.12.3 (protocol 12), 0.12.4 through 0.12.15 (protocol 13), 0.12.16 (protocol 14), 0.12.17 through 0.12.19 (protocol 15), 0.12.20 through 0.12.22 (protocol 16) | the target's, through `bunx` as above |
|
|
1019
1248
|
| any supported source, with the installed CLI already at the target | `ahub upgrade` below, which is the same coordinator |
|
|
1020
1249
|
| 0.5.x or earlier (protocol 8 and older) | not supported: bootstrap by hand with the matching CLI |
|
|
1021
1250
|
|
|
@@ -1047,21 +1276,27 @@ projects first:
|
|
|
1047
1276
|
|
|
1048
1277
|
```bash
|
|
1049
1278
|
ahub restart --dry-run
|
|
1050
|
-
ahub upgrade --to 0.12.
|
|
1279
|
+
ahub upgrade --to 0.12.22 --dry-run
|
|
1051
1280
|
```
|
|
1052
1281
|
|
|
1053
1282
|
Apply only after reviewing the plan:
|
|
1054
1283
|
|
|
1055
1284
|
```bash
|
|
1056
1285
|
ahub restart --yes
|
|
1057
|
-
ahub upgrade --to 0.12.
|
|
1058
|
-
ahub recovery status <operation-id>
|
|
1059
|
-
ahub recovery resume <operation-id>
|
|
1060
|
-
ahub recovery abort <operation-id>
|
|
1061
|
-
ahub recovery dispose <operation-id> --fresh-session <peer> --reason <text>
|
|
1062
|
-
ahub recovery dispose <operation-id> --stop-and-archive --reason <text>
|
|
1286
|
+
ahub upgrade --to 0.12.22 --yes
|
|
1287
|
+
ahub recovery status [<operation-id>]
|
|
1288
|
+
ahub recovery resume [<operation-id>]
|
|
1289
|
+
ahub recovery abort [<operation-id>]
|
|
1290
|
+
ahub recovery dispose [<operation-id>] --fresh-session <peer> --reason <text>
|
|
1291
|
+
ahub recovery dispose [<operation-id>] --stop-and-archive --reason <text>
|
|
1063
1292
|
```
|
|
1064
1293
|
|
|
1294
|
+
`--yes` needs `--to`; a dry run without `--to` plans the latest release. A target
|
|
1295
|
+
newer than the CLI is handed to that release's coordinator with the same flags,
|
|
1296
|
+
and the command is printed on stderr first. The `recovery` commands take the
|
|
1297
|
+
operation that holds the machine's lock when the id is left out, and say `no
|
|
1298
|
+
recovery operation holds the lock` when none does.
|
|
1299
|
+
|
|
1065
1300
|
The coordinator commits only once the source is quiet: no turn running, no
|
|
1066
1301
|
approval pending, no completion check queued or running. It waits up to 10
|
|
1067
1302
|
minutes and then aborts, leaving the source running; upgrade between long
|
|
@@ -1101,6 +1336,10 @@ runs under the deny-default sandbox, and `hub.log` and `ahub doctor` say so.
|
|
|
1101
1336
|
`local.bash_network: "direct"` still works until 0.13.0, and both name it while
|
|
1102
1337
|
it is set.
|
|
1103
1338
|
|
|
1339
|
+
0.12.22 closes the name `.git` to the local worker's and Pi's commands at any
|
|
1340
|
+
depth: `git init`, `git clone` and `git worktree add` no longer run inside the
|
|
1341
|
+
sandbox (the full list is under Install and start).
|
|
1342
|
+
|
|
1104
1343
|
The 0.7.0 transition stages the verified package and runs a retained
|
|
1105
1344
|
coordinator from the source tree. It accepts a verified protocol-9 source and
|
|
1106
1345
|
moves to a protocol-10 target. The source journal, queued envelopes, tasks,
|
|
@@ -1136,6 +1375,93 @@ Do not run an upgrade with an incompatible active protocol, an unverified
|
|
|
1136
1375
|
terminal binding, or an unresolved operation lock. Dry-run performs no package,
|
|
1137
1376
|
plugin, daemon, or terminal mutation.
|
|
1138
1377
|
|
|
1378
|
+
### Interactive upgrade
|
|
1379
|
+
|
|
1380
|
+
`ahub upgrade` and `ahub restart` are interactive when standard input and output
|
|
1381
|
+
are a terminal and neither `--dry-run` nor `--yes` is given. With either flag, or
|
|
1382
|
+
through a pipe, the JSON plan, the `ahub: blocker:` lines and the exit codes are
|
|
1383
|
+
unchanged. The screens add no way to change an operation: every choice runs one
|
|
1384
|
+
of the commands in this section for you, so that command's refusals decide.
|
|
1385
|
+
|
|
1386
|
+
The plan screen lists each project with its hub version and, per attached peer,
|
|
1387
|
+
its state, how it runs (`TUI` in a terminal the plan bound to its session,
|
|
1388
|
+
`headless` as the hub's own process, `own` for a session the hub did not
|
|
1389
|
+
launch or whose terminal the plan could not bind) and what happens to it: resumed in a new terminal (the old one is
|
|
1390
|
+
named), reconnecting by itself, restarted as a new session, restarted headless,
|
|
1391
|
+
or left offline. `in progress:` repeats what the running hub says keeps it from
|
|
1392
|
+
being quiet (busy peers, pending approvals, a peer starting, a budget
|
|
1393
|
+
transition; a hub of 0.12.22 or newer also names a task command, completion
|
|
1394
|
+
checks or Pi tool calls in flight, ready tasks being released and a Pi that has
|
|
1395
|
+
not settled); apply waits up to 10 minutes for it. Blockers follow, each with its
|
|
1396
|
+
next action. The choices are `[a] apply` (absent while a blocker stands), `[r]
|
|
1397
|
+
refresh`, `[k] end agents` (shown when an attached agent can be ended), `[j]`
|
|
1398
|
+
the plan as JSON, `[x] reset a project's hub` (shown when the plan has a running
|
|
1399
|
+
project) and `[q] quit`.
|
|
1400
|
+
|
|
1401
|
+
`[k] end agents` ends attached agents before the upgrade, so they are neither
|
|
1402
|
+
waited for nor restored: `t` for the TUI agents, `h` for the headless ones
|
|
1403
|
+
(Kimi, Pi in headless mode, the local worker), or their names, written
|
|
1404
|
+
`<project id>/<name>` when the plan covers more than one project. After a
|
|
1405
|
+
confirmation a TUI agent's terminal is closed, which cuts a turn in progress,
|
|
1406
|
+
and a headless agent is stopped by its hub through the `peer_stop` request
|
|
1407
|
+
(#278). A delivery that a busy agent had not settled is then held as
|
|
1408
|
+
`needs_review`, and its queue stays held until `ahub queue resolve` settles it. Each agent is ended only
|
|
1409
|
+
if the lock is free and its hub, its session and its terminal are still the
|
|
1410
|
+
ones the plan read; otherwise the screen says why it was left. A hub that does
|
|
1411
|
+
not know `peer_stop` (0.12.21 and older) cannot end a headless agent: the
|
|
1412
|
+
screen says so, the agent stops with the old hub at the upgrade, and the new
|
|
1413
|
+
hub starts it again. A session marked `own` is ended where it runs. The next
|
|
1414
|
+
plan shows the ended agents as offline.
|
|
1415
|
+
|
|
1416
|
+
After apply the command stays attached and prints each step as the receipt
|
|
1417
|
+
records it, and while a source is being prepared, what it still waits for.
|
|
1418
|
+
Enter opens the operation's screen while the runner works on. Ctrl+C leaves the
|
|
1419
|
+
command, at any prompt and while following, and never stops the runner: the
|
|
1420
|
+
`left:` line names the command that comes back to the screen, with the
|
|
1421
|
+
operation's own coordinator, because the installed `ahub` is the older release
|
|
1422
|
+
until the upgrade ends. A Ctrl+C between `a` and the start of the operation
|
|
1423
|
+
starts nothing. A prompt for a reason that is answered with
|
|
1424
|
+
Ctrl+C or the end of input ends nothing.
|
|
1425
|
+
|
|
1426
|
+
The operation's screen also opens first whenever an operation holds the lock,
|
|
1427
|
+
and for bare `ahub recovery`. It shows the phase and step, each project's phase
|
|
1428
|
+
and effect receipts and the error, then the choices that `status` lists under
|
|
1429
|
+
`next` for the same receipt and live state:
|
|
1430
|
+
|
|
1431
|
+
| Key | Runs | Offered when |
|
|
1432
|
+
| --- | --- | --- |
|
|
1433
|
+
| `r` | `recovery resume`, then follows | resume can get past what is live |
|
|
1434
|
+
| `c` | `recovery abort` | no runtime was stopped and no terminal effect or commit request is recorded |
|
|
1435
|
+
| `f` | `recovery dispose --fresh-session <peer>`, after a reason | a Codex or Claude restoration failed |
|
|
1436
|
+
| `e` | `recovery dispose --stop-and-archive`, after a confirmation and a reason | every unverified project reads as running, stopped or missing |
|
|
1437
|
+
| `x` | `e`, then the reset below | as `e` |
|
|
1438
|
+
| `w` | follows the runner | a runner holds the operation |
|
|
1439
|
+
|
|
1440
|
+
When more than one restoration failed, the second and later peers get
|
|
1441
|
+
`f-<peer>`. `s` refreshes the screen, `j` prints the receipt as JSON and `q`
|
|
1442
|
+
leaves the operation as it is.
|
|
1443
|
+
|
|
1444
|
+
While a runner is only waiting for its source to get quiet (step `prepare`, no
|
|
1445
|
+
effect recorded), `c` stops that runner and then aborts. It is offered only for
|
|
1446
|
+
a runner whose claim carries a process signature that still matches; a claim
|
|
1447
|
+
from a coordinator of 0.12.20 or older has none, so its pid is never signalled,
|
|
1448
|
+
and such a runner gives up its wait after 10 minutes. The receipt is read again
|
|
1449
|
+
when the key is pressed, and a runner that has moved on is left alone. If it
|
|
1450
|
+
records an effect in the instant between that read and the signal, the abort is
|
|
1451
|
+
refused and resume is offered instead. Past that step a running operation
|
|
1452
|
+
offers only `w`.
|
|
1453
|
+
|
|
1454
|
+
A reset is offered from the plan screen for a project whose hub is running, and
|
|
1455
|
+
with one question right after an operation was cancelled or ended from its
|
|
1456
|
+
screen: it asks for the project (when there are several) and the scope, prints
|
|
1457
|
+
the dry run of `ahub reset` or `ahub reset --all`, and applies it with `--yes`
|
|
1458
|
+
only after a second confirmation. While an operation holds the lock it refuses,
|
|
1459
|
+
as `ahub reset` does.
|
|
1460
|
+
|
|
1461
|
+
Ending a peer's running turn is not part of these screens: finish or interrupt
|
|
1462
|
+
it in that peer's terminal, answer approvals with `ahub permit` or the console,
|
|
1463
|
+
or cancel and upgrade later.
|
|
1464
|
+
|
|
1139
1465
|
### A partial operation
|
|
1140
1466
|
|
|
1141
1467
|
`ahub recovery status <operation-id>` reads the receipt without changing it:
|
|
@@ -1179,14 +1505,22 @@ next actions").
|
|
|
1179
1505
|
get past what is live: not past a replaced or missing source, a source that
|
|
1180
1506
|
crashed before any commit request, a refusal at staging (the target release
|
|
1181
1507
|
or the preserved source can never change back), a hub of another control
|
|
1182
|
-
protocol, a target that stopped after it started
|
|
1183
|
-
the operation's
|
|
1508
|
+
protocol, a target that stopped after it started when the operation's
|
|
1509
|
+
coordinator is older than 0.12.21, or a stopped hub without the operation's
|
|
1510
|
+
restart snapshot.
|
|
1184
1511
|
A second `resume` while its own hold still stands checks the peers again
|
|
1185
1512
|
before it closes anything.
|
|
1186
|
-
- When a target hub dies after it started (a crash or a reboot),
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1513
|
+
- When a target hub dies after it started (a crash or a reboot), `resume`
|
|
1514
|
+
starts it again from the operation's snapshot while the project's state
|
|
1515
|
+
directory still holds it: it records the restart (status shows `restarts`),
|
|
1516
|
+
closes the terminals the dead hub's peers were restored into (or finds them
|
|
1517
|
+
gone), and relaunches those peers against the new hub, a session you had
|
|
1518
|
+
accepted instead of the original included. A launcher still running from
|
|
1519
|
+
the dead hub, or a session attached without an id, stops it until you end
|
|
1520
|
+
that session. A target that dies again in the same `resume` stops it with the
|
|
1521
|
+
restart count; look at its `hub.log` before resuming again. Without the
|
|
1522
|
+
snapshot, or for an operation started by a coordinator older than 0.12.21,
|
|
1523
|
+
the way out is stop-and-archive.
|
|
1190
1524
|
- A Codex conversation comes back only when a rollout file naming its thread
|
|
1191
1525
|
exists under `sessions/` of the store the restored terminal uses (the
|
|
1192
1526
|
recorded `CODEX_HOME`, else `~/.codex`). Codex writes that file with the
|
|
@@ -1220,7 +1554,13 @@ next actions").
|
|
|
1220
1554
|
be read counts as unknown, never as "nothing attached", and so does a launcher
|
|
1221
1555
|
record file that cannot be read: `resume` then stops without changing any
|
|
1222
1556
|
receipt; when the record file is what cannot be read, inspect it and move it
|
|
1223
|
-
aside, then `resume`.
|
|
1557
|
+
aside, then `resume`. A record file that parses but holds a row that cannot
|
|
1558
|
+
be evaluated (a hand edit, a damaged or a future layout) is read the same way
|
|
1559
|
+
for the peer that row may belong to, and `ahub codex`, `ahub claude` and
|
|
1560
|
+
`ahub pi --mode tui` write such rows back unchanged: fix or remove that row,
|
|
1561
|
+
then `resume`. A launcher recorded by 0.12.12 or older is read with its own
|
|
1562
|
+
unpinned signature too, so it counts as running when this command runs in
|
|
1563
|
+
the same time zone and locale as the launcher did. `resume` is refused while a stop-and-archive is partway. A lifecycle command refused by the lock names
|
|
1224
1564
|
the operation's own `status` command, which lists what to do next.
|
|
1225
1565
|
- `ahub recovery dispose <operation-id> --fresh-session <peer> --reason <text>`
|
|
1226
1566
|
applies to a Codex or Claude peer whose restoration failed (Pi is refused: a
|
|
@@ -1234,12 +1574,22 @@ next actions").
|
|
|
1234
1574
|
<id>` instead of offering it. A hub that never answers (its pid is alive,
|
|
1235
1575
|
nothing replies): take the `pid` from that project's
|
|
1236
1576
|
`.agenthub/state/status.json`, check with `ps -p <pid> -o command=` that it
|
|
1237
|
-
is that project's hub daemon, end it by hand, then run `status` again.
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1577
|
+
is that project's hub daemon, end it by hand, then run `status` again. From
|
|
1578
|
+
0.12.21 a hub's manifest and registry claim and a runner's record carry the
|
|
1579
|
+
owner's process signature, so a pid that a reboot handed to another process
|
|
1580
|
+
reads as gone by itself. Records written by 0.12.20 or older carry none and
|
|
1581
|
+
are judged by their pid alone: if that pid now belongs to another process,
|
|
1582
|
+
move the stale `status.json` and `hub.pid` aside together (with only one of
|
|
1583
|
+
them gone the other still reads as an owner), then run `status` again;
|
|
1584
|
+
likewise when `next` says `wait: runner <pid>` and that pid is not an `ahub
|
|
1585
|
+
recovery-run` process, move the operation's
|
|
1586
|
+
`~/.agenthub/recovery/<id>.json.runner.db` aside, then run `status` again.
|
|
1587
|
+
An unsigned registry claim of such a hub, whose pid now belongs to another
|
|
1588
|
+
process, still reads as starting, and no command clears it: after `ps -p
|
|
1589
|
+
<pid> -o command=` shows that the process is not an `ahub ... daemon` of that
|
|
1590
|
+
project, clear it with `sqlite3 ~/.agenthub/registry.db "UPDATE projects SET
|
|
1591
|
+
instance_id = NULL, pid = NULL, claimed_at = NULL WHERE id = '<project id>'
|
|
1592
|
+
AND pid = <pid>"` (`ahub projects --json` shows the id). A
|
|
1243
1593
|
project whose directory is
|
|
1244
1594
|
gone is only recorded (`ahub doctor --orphans` lists a hub left running
|
|
1245
1595
|
there). It records its decision before it acts. It releases a source hold of
|
|
@@ -1249,7 +1599,13 @@ next actions").
|
|
|
1249
1599
|
`restart.json` aside as `restart.abandoned.<hash>.json` and leaves any other
|
|
1250
1600
|
hub running; only then is the operation recorded `cancelled` and the lock
|
|
1251
1601
|
released. If it fails partway it keeps the lock, records what it did, and
|
|
1252
|
-
`resume` refuses until you run it again.
|
|
1602
|
+
`resume` refuses until you run it again. Once a stop-and-archive is recorded,
|
|
1603
|
+
only the command `next` names continues it. On an operation whose
|
|
1604
|
+
coordinator predates these commands it also marks the receipt `schema: 2`,
|
|
1605
|
+
which every older release refuses before it acts: an older global `ahub
|
|
1606
|
+
recovery resume` still prints "scheduled", but its runner exits without
|
|
1607
|
+
touching the operation, and an older `ahub recovery abort` refuses with
|
|
1608
|
+
"unsupported operation receipt". Queued messages are not delivered
|
|
1253
1609
|
from the archived file; a 0.7.0 or later hub reloads the queues it kept in
|
|
1254
1610
|
`hub.db` when it starts again. Terminals it closed stay closed: start those
|
|
1255
1611
|
sessions again by hand. Replacement terminals it opened stay open but have
|
|
@@ -1508,8 +1864,8 @@ The hooks return no facts in advisory mode. A native Stop establishes an idle
|
|
|
1508
1864
|
boundary; a subsequent PreToolUse marks activity. A delivery acknowledgement or
|
|
1509
1865
|
task approval does not establish native idle. Restart the daemon and relaunch
|
|
1510
1866
|
Claude after enabling the sweep so its session receives the hooks. A caller's
|
|
1511
|
-
`--settings`
|
|
1512
|
-
|
|
1867
|
+
`--settings` keeps them: the launcher adds the hub's hooks to the caller's and
|
|
1868
|
+
warns only that the status line tee is off.
|
|
1513
1869
|
|
|
1514
1870
|
`auto_reassign: true` explicitly allows an available alternative owner selected
|
|
1515
1871
|
under the existing routing, role and PII constraints to receive the task at step
|
|
@@ -1634,3 +1990,103 @@ Quota pause and task handoff are separate; a context checkpoint neither pauses
|
|
|
1634
1990
|
nor hands work over. Continue normally or deliberately restart into a fresh
|
|
1635
1991
|
session with your chosen checkpoint as preface. No automatic restart or native
|
|
1636
1992
|
compaction override is performed.
|
|
1993
|
+
|
|
1994
|
+
## Pi tool refusals, exit causes and idle automatic restart
|
|
1995
|
+
|
|
1996
|
+
Pi hub-task/conductor refusals proven before any task or board effect return
|
|
1997
|
+
their original `error:` text and are recorded as completed tool receipts.
|
|
1998
|
+
PII screening and its telemetry record may precede the refusal. An uncertain
|
|
1999
|
+
post-effect or storage failure remains pending: inspect it before repeating an
|
|
2000
|
+
action. Old pending receipts carry no proof of a past refusal and stay pending
|
|
2001
|
+
across startup. Conductor assignment and escalation entry checks explicitly opt
|
|
2002
|
+
into refusal typing; internal escalation after a saved review remains generic.
|
|
2003
|
+
|
|
2004
|
+
Pi exit records show code or signal, turn/tool activity, whether teardown was
|
|
2005
|
+
expected, startup status, and the last tool's name without arguments. An
|
|
2006
|
+
unobserved native-terminal exit records unknown OS status. The console notice
|
|
2007
|
+
gives the next action, usually `ahub pi`, or says why automatic start is held.
|
|
2008
|
+
A replacement says the owner stopped for a new Pi owner, including when the
|
|
2009
|
+
replacement is refused. Internal failure teardown directs session inspection
|
|
2010
|
+
rather than claiming a person requested it. Replacement, failed-start and
|
|
2011
|
+
active-turn guidance appears before automatic-start-disabled guidance.
|
|
2012
|
+
|
|
2013
|
+
With `pi.auto_start`, an unexpectedly idle headless Pi is resumed on its
|
|
2014
|
+
verified persisted session once. The 60-second retry window rearms when the
|
|
2015
|
+
attempt settles; a second exit inside that window stays offline. Missing or
|
|
2016
|
+
invalid session history has no fresh fallback; inspect it before running
|
|
2017
|
+
`ahub pi`. Active turns/tools, failed startup, a superseded owner, native
|
|
2018
|
+
terminal exits, a requested stop (`ahub stop pi`), requested shutdown and
|
|
2019
|
+
recovery operations suppress this restart. Crash recovery's #66
|
|
2020
|
+
recorded-session/fresh-start choices are unchanged.
|
|
2021
|
+
|
|
2022
|
+
### Pi/local unanswered approvals
|
|
2023
|
+
|
|
2024
|
+
An expired Pi/local request says "approval expired: no person answered". The
|
|
2025
|
+
worker must not retry that call; hand the task off or stop. Two consecutive
|
|
2026
|
+
unanswered requests end the current turn and produce a console notice. An
|
|
2027
|
+
expiry counts only when no person answered a request of that peer while it
|
|
2028
|
+
was pending, so a batch of parallel requests does not stop a worker whose
|
|
2029
|
+
person is answering; a grant served from the always-allow cache or a permission
|
|
2030
|
+
mode is not an answer and does not reset the count. A new turn also resets it. The turn ends
|
|
2031
|
+
through the same abort path an execution-budget stop uses: Pi stays attached
|
|
2032
|
+
and the sender is told the approval reason. Supported Pi and local settle that
|
|
2033
|
+
delivery after reporting the reason, so the next queued message runs without
|
|
2034
|
+
an operator release. Pi clears turn-only approval state at settlement; the
|
|
2035
|
+
person's idle `!command` remains usable and a later failure keeps its own reason.
|
|
2036
|
+
A Pi whose extension cannot abort
|
|
2037
|
+
a turn (an older hub extension or a runtime without native abort support) goes
|
|
2038
|
+
offline instead. Inspect prior work, restart with `ahub pi`, find the held
|
|
2039
|
+
delivery with `ahub queue list --peer pi`, inspect it with `ahub queue show <id>`,
|
|
2040
|
+
then explicitly settle it with `ahub queue resolve <id> --action completed|retry|discard --reason "<text>"`.
|
|
2041
|
+
A cancelled tool withdraws
|
|
2042
|
+
its request immediately; a later `ahub permit` answer is refused and cannot
|
|
2043
|
+
create an always-allow grant. Tool requests from a previous turn or an older
|
|
2044
|
+
extension missing session/turn identity are refused before execution; restart
|
|
2045
|
+
Pi with the current hub extension rather than retrying that old call.
|
|
2046
|
+
|
|
2047
|
+
The dashboard shows the number of waiting Pi/local requests and where to
|
|
2048
|
+
answer them. It remains deny-only for those private rows. Use the console or
|
|
2049
|
+
`ahub tail` to inspect the request, then `ahub permit` to answer it. Recovery
|
|
2050
|
+
stops waiting for an approval when that request is withdrawn.
|
|
2051
|
+
|
|
2052
|
+
A person's `!command` in the managed Pi TUI runs through the managed sandbox
|
|
2053
|
+
without asking for another hub approval. Its reported exit code comes from
|
|
2054
|
+
the process, including a nonzero exit; text printed by the command does not
|
|
2055
|
+
set its status. This exception requires the current idle TUI owner's reserved
|
|
2056
|
+
request and does not apply to model-origin bash calls.
|
|
2057
|
+
|
|
2058
|
+
### Stop one headless peer
|
|
2059
|
+
|
|
2060
|
+
`ahub stop kimi`, `ahub stop pi` (headless mode), or `ahub stop local` stops
|
|
2061
|
+
that hub-owned runtime through its adapter and prints `<peer>: offline`.
|
|
2062
|
+
The hub and its other peers keep running. The command is human-only;
|
|
2063
|
+
agent shells and tools-role connections cannot use the `peer_stop` request.
|
|
2064
|
+
An older hub that does not know the request needs an upgrade before this
|
|
2065
|
+
command is available.
|
|
2066
|
+
|
|
2067
|
+
Claude, Codex and Pi in TUI mode belong to their native terminals: end them
|
|
2068
|
+
in those terminals. The upgrade plan's end-agents action closes a TUI through
|
|
2069
|
+
its verified terminal binding and uses `peer_stop` for a headless runtime.
|
|
2070
|
+
Unknown and already-offline peers are refused with a next action. A recovery
|
|
2071
|
+
operation or shutdown holds stop mutations; finish that operation first.
|
|
2072
|
+
A peer's concurrent start, stop or permission-mode change must also finish
|
|
2073
|
+
before another lifecycle change is admitted, and an upgrade or restart does not
|
|
2074
|
+
prepare the hub while a stop is in progress.
|
|
2075
|
+
|
|
2076
|
+
A requested stop does not automatically restart a peer. A person or an authorized
|
|
2077
|
+
conductor can explicitly start it again; conductor starts remain permitted. Start it with
|
|
2078
|
+
`ahub kimi`, `ahub pi` or `ahub local`. Pending approvals are withdrawn before
|
|
2079
|
+
teardown. An in-flight delivery becomes `needs_review`, with a reason naming
|
|
2080
|
+
the requested stop; it is neither completed nor automatically replayed.
|
|
2081
|
+
Inspect partial effects with `ahub queue list --peer <peer>` and
|
|
2082
|
+
`ahub queue show <delivery-id>`, then explicitly choose `completed`, `retry`
|
|
2083
|
+
or `discard` with `ahub queue resolve`. Restarting alone does not release
|
|
2084
|
+
that held delivery. Work queued behind an idle stopped peer is retained and
|
|
2085
|
+
runs when it is started again, subject to its existing pauses and holds. New work
|
|
2086
|
+
arriving during teardown stays queued too, even if a console resume runs meanwhile.
|
|
2087
|
+
An idle Pi with no message/file yet records its live empty-session proof before
|
|
2088
|
+
stopping, so its next start resumes that identity. A Pi stopped after its first
|
|
2089
|
+
prompt was accepted but before history was persisted is not treated as empty:
|
|
2090
|
+
its delivery is held, and another start refuses until resume history is verified.
|
|
2091
|
+
Inspect and settle the held delivery before deliberately replacing that unpersisted
|
|
2092
|
+
session by restarting the hub; the stop never silently chooses a fresh session.
|