@staix/agent-hub 0.12.31 → 0.12.33

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/CHANGELOG.md +20 -1
  2. package/README.md +14 -7
  3. package/docs/agent-notes/adapters.md +7 -4
  4. package/docs/agent-notes/bus.md +3 -0
  5. package/docs/agent-notes/daemon.md +13 -8
  6. package/docs/agent-notes/local-worker.md +1 -1
  7. package/docs/agent-notes/tasks.md +4 -3
  8. package/docs/agent-notes/tests.md +1 -0
  9. package/docs/cooperbench.md +1 -1
  10. package/docs/gsd-collaboration-analysis.md +1 -1
  11. package/docs/memory.md +1 -1
  12. package/docs/operations.md +141 -25
  13. package/docs/quickstart.md +10 -6
  14. package/docs/security.md +18 -9
  15. package/docs/smoke.md +34 -0
  16. package/docs/specs/2026-09-19-agent-hub-design.md +204 -0
  17. package/docs/specs/2026-09-20-upgrade-recovery-design.md +211 -0
  18. package/docs/verification/2026-10-18-0.12.32.md +54 -0
  19. package/docs/verification/2026-10-18-0.12.33.md +42 -0
  20. package/docs/verified.json +24 -16
  21. package/package.json +2 -2
  22. package/plugins/agent-hub/.claude-plugin/plugin.json +1 -1
  23. package/plugins/agent-hub/server.js +1296 -26
  24. package/src/adapters/acp.ts +5 -6
  25. package/src/adapters/definitions/builtin-metadata.ts +121 -0
  26. package/src/adapters/definitions/kimi-native.ts +4 -0
  27. package/src/adapters/definitions/qwen-native.ts +24 -0
  28. package/src/adapters/definitions/qwen.ts +71 -0
  29. package/src/adapters/pi.ts +6 -2
  30. package/src/adapters/registry-types.ts +174 -0
  31. package/src/adapters/registry.ts +36 -0
  32. package/src/cli/console-state.ts +74 -5
  33. package/src/cli/console.ts +35 -2
  34. package/src/cli/help.ts +3 -1
  35. package/src/cli/identity.ts +13 -3
  36. package/src/cli/launch.ts +1 -6
  37. package/src/cli/main.ts +200 -39
  38. package/src/cli/output.ts +2 -0
  39. package/src/cli/preview.ts +8 -5
  40. package/src/cli/recovery-guidance.ts +137 -0
  41. package/src/cli/recovery-presentation-options.ts +8 -0
  42. package/src/cli/recovery-session-file.ts +18 -0
  43. package/src/cli/reset.ts +13 -1
  44. package/src/cli/terminal-recovery.ts +48 -0
  45. package/src/cli/upgrade-interactive.ts +129 -75
  46. package/src/cli/upgrade-labels.ts +30 -0
  47. package/src/cli/upgrade-presentation-data.ts +41 -0
  48. package/src/cli/upgrade-presentation-flags.ts +31 -0
  49. package/src/cli/upgrade-presentation.ts +227 -0
  50. package/src/cli/upgrade-restore-state.ts +6 -2
  51. package/src/cli/upgrade-runtime.ts +360 -199
  52. package/src/cli/upgrade.ts +278 -25
  53. package/src/hub/bus.ts +144 -14
  54. package/src/hub/cohorts.ts +2 -2
  55. package/src/hub/config-apply.ts +242 -0
  56. package/src/hub/config-reload.ts +120 -0
  57. package/src/hub/config-trust.ts +11 -2
  58. package/src/hub/control-client.ts +1 -1
  59. package/src/hub/crash.ts +8 -6
  60. package/src/hub/daemon.ts +577 -179
  61. package/src/hub/deferred-hub.ts +60 -0
  62. package/src/hub/delivery-journal.ts +6 -0
  63. package/src/hub/events.ts +2 -2
  64. package/src/hub/manager.ts +1 -1
  65. package/src/hub/peer-catalog.ts +10 -13
  66. package/src/hub/peers.ts +12 -2
  67. package/src/hub/permission-mode.ts +5 -3
  68. package/src/hub/recovery-continuity.ts +152 -0
  69. package/src/hub/recovery-store.ts +10 -4
  70. package/src/hub/restart.ts +12 -1
  71. package/src/hub/sdlc.ts +5 -1
  72. package/src/hub/settings.ts +50 -36
  73. package/src/hub/settled-roles.ts +35 -0
  74. package/src/hub/supervision.ts +3 -2
  75. package/src/hub/tasks.ts +29 -9
  76. package/src/hub/ui.ts +1 -1
  77. package/src/memory/brief.ts +6 -5
  78. package/src/pi/session.ts +23 -4
  79. package/src/ui/index.html +23 -7
@@ -1,6 +1,6 @@
1
1
  # Operations guide
2
2
 
3
- This guide describes ahub 0.12.31 and control protocol 16. Live verification
3
+ This guide describes ahub 0.12.33 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
  ## Agent-operated live verification
@@ -120,7 +120,7 @@ sounds on an action-count transition from zero, at most once per minute, never
120
120
  for a message alone. Plain mode prints the line only when the action count changes.
121
121
  Messages addressed to `user` remain attention items until the next key. Drawing
122
122
  adds no timer, network request or writable settings path.
123
- Tabs change with the digits 1 to 5 or with Left and Right (wrapping at both
123
+ The operational tabs change with digits 1 to 5 or Left and Right (wrapping at both
124
124
  ends), never while a command is being edited or a choice is open; under a
125
125
  confirmation an arrow dismisses it like any key but y, without moving a tab. A pasted
126
126
  chunk can only edit the command line, but a read that holds nothing but arrow or
@@ -135,7 +135,7 @@ the stream; only the header is tab-flattened before tokenizing. Body lines retai
135
135
  the previous rendering and stay plain, as does command output; colour never changes
136
136
  what the panel's text says.
137
137
 
138
- Peers, Approvals, Tasks and Queue are tables with a header row. A column is as
138
+ Peers, Approvals, Tasks, Queue and Settings are tables with a header row. A column is as
139
139
  wide as its widest cell, up to a third of the terminal; the last column takes
140
140
  the rest and is cut with `...`. In Tasks, TITLE takes the rest and the STAGE
141
141
  meter keeps a fixed column at the right edge.
@@ -146,6 +146,7 @@ meter keeps a fixed column at the right edge.
146
146
  | Approvals | ID PEER LEFT TITLE |
147
147
  | Tasks | ID STATE OWNER REVIEWER CLASS AGE TITLE STAGE |
148
148
  | Queue | ID PEER STATE REV AGE |
149
+ | Settings | GROUP KEY VALUE SOURCE APPLIES |
149
150
 
150
151
  The MODE column appears only when at least one peer has a non-`ask` mode.
151
152
  Zero counters and unknown values read `-`. Durations read `45s`, `12m`, `3h05m`
@@ -1436,6 +1437,16 @@ override them.
1436
1437
 
1437
1438
  ## Settings from the dashboard and the terminal
1438
1439
 
1440
+ In `ahub console`, press `6` for Settings. Existing operational-tab arrow cycling
1441
+ is unchanged; from Settings, Left returns to Events and Right to Peers. Select a
1442
+ row and press `e` to edit: value, daemon preview, nonempty reason, then `y`. Press
1443
+ `u` to preview and confirm undo. Undo binds the key and exact undo-record digest;
1444
+ if another write replaces that record, preview again. For busy roster removal,
1445
+ the panel names `ahub roster remove <peer> --now`, which explicitly stops an owned
1446
+ runtime or detaches a native session during its active turn; the panel does not force it.
1447
+ Esc cancels, and pasted confirmation text cannot
1448
+ write. This panel uses the current authenticated daemon connection.
1449
+
1439
1450
  The dashboard's Settings section and `ahub settings` show and change the same
1440
1451
  closed list of settings (issue #269). Run them from a plain terminal:
1441
1452
 
@@ -1447,17 +1458,21 @@ ahub settings set routing.classes.implement.peers codex,kimi,pi
1447
1458
  ahub settings set permission_modes.kimi ask-when-needed
1448
1459
  ahub settings set research.enabled inherit # remove the machine-local value
1449
1460
  ahub settings undo # put back the file the last write changed
1461
+ ahub settings reload # validate and apply the complete four-file candidate
1450
1462
  ahub ui --settings # dashboard with a 15-minute settings session
1451
1463
  ```
1452
1464
 
1453
1465
  | Group | Keys | Stored in | Applies |
1454
1466
  | --- | --- | --- | --- |
1455
1467
  | Permissions | `permission.<peer>` (the mode now) | the running hub only | at once |
1456
- | Permissions | `permission_modes.<peer>` (the mode at hub start) | `config.local.json` | next hub start |
1468
+ | Permissions | `permission_modes.<peer>` (the default mode) | `config.local.json` | next peer start |
1457
1469
  | Start | `peers.pi.start_mode`, `peers.codex.start_mode`, `peers.kimi.start_mode` (`tui` or `headless`) | `config.local.json` | that peer's next start |
1458
- | Start | `pi.auto_start` | `config.local.json` | next hub start |
1470
+ | Start | `pi.auto_start` | `config.local.json` | next startup/recovery auto-start decision; never starts a peer merely on reload |
1459
1471
  | Routing | `routing.stay_switch`, `routing.classes.<class>.peers`, `.escalate_to`, `.route`, `.pi_backend` | `routing.local.toml` | at once |
1460
- | Switches | `research.enabled`, `approvals.notify`, `snapshots.enabled`, `coordination` | `config.local.json` | next hub start |
1472
+ | Switches | `research.enabled`, `approvals.notify`, `snapshots.enabled` | `config.local.json` | live, without disturbing an active turn |
1473
+ | Switches | `coordination` | `config.local.json` | newly created tasks only |
1474
+ | Switches | `task_sweep.enabled`, `.auto_reassign`, `.interval_s`, `.unaccepted_min`, `.idle_min`, `.review_min`, `.ready_min`, `.ladder_min` | `config.local.json` | live; reschedules the sweep timer |
1475
+ | Routing | `tasks.wip_limits.<peer>` | `config.local.json` | live |
1461
1476
  | Roles | `planner` (the one peer that plans) | `config.local.json` | at once |
1462
1477
 
1463
1478
  - Writes go only to `.agenthub/config.local.json` and `.agenthub/routing.local.toml`,
@@ -1470,8 +1485,28 @@ ahub ui --settings # dashboard with a 15-minute settings session
1470
1485
  file each value comes from. `--preview`, and the dashboard's Preview button,
1471
1486
  show `ahub route explain` for the open tasks before and after, or what a
1472
1487
  permission mode grants, and save nothing.
1473
- - A row shows the value in force, its source file, when it applies, and for a
1474
- value read at hub start what the next start will read (`Pending restart`).
1488
+ - A row shows running and file values, source and apply class (`live`, `next use`,
1489
+ or `restart`). Pending restart keys and reasons also appear in status, console
1490
+ and dashboard, including startup-only blocks outside the editable registry.
1491
+ - Settings writes and undo, the dashboard Reload configuration button and
1492
+ `ahub settings reload` use one complete validation/apply path. Debounced changes
1493
+ of config.json, config.local.json, routing.toml and routing.local.toml use it too,
1494
+ including atomic file replacement. Invalid candidates apply nothing and emit
1495
+ one bounded error notice. Valid but unauthorized keys retain their accepted value and are reported per key; other eligible changes still apply. Recovery/shutdown hold reloads.
1496
+ - Individual writes and undo publish only the named setting. Other file edits remain unapplied.
1497
+ - `ahub settings reload` lists guarded keys with complete before -> after values (including full argv) and any planner/conductor holder effect. It requires an interactive terminal and exact `y` within 30 seconds before adopting them. Oversized previews remain unapplied and must be split or reduced. Candidate bytes or accepted state changes invalidate that confirmation. Watchers and ordinary dashboard reloads never confirm a raise.
1498
+ - Privacy tightening (local PII constraints/screening, added PII patterns, nonholder role reductions and explicit capability-list narrowing) is automatic, including tracked files. A planner/conductor holder change is guarded even when a role list shrinks; deleting a restricted capability entry restores all capabilities and is guarded. Reload checks the actual files' Git provenance. Changed machine-local or raising
1499
+ values from tracked/unverifiable files are refused; an ordinary dashboard or
1500
+ watcher cannot adopt unrelated raising local edits. A settings session cannot
1501
+ adopt routing fields outside its editable registry. Existing confirmation and
1502
+ roster lifecycle fences remain; use the confirmed roster setting for membership.
1503
+ Pi relay model aliases (`pi.dgx_coding`, `pi.dgx_fast`) and `routing.pi` retain their running values until restart, because existing native sessions use the published relay catalogue and context contract. Routing policy changes apply to future decisions; local model/target changes apply at the next peer or sidecar creation.
1504
+
1505
+ - Runtime/client ownership still requires restart for memory handles and sync,
1506
+ gateway/MLX, runner/check commands, profile/tool contracts, converters, existing
1507
+ session privacy/sandbox policies, the terminal provider and benchmark environment.
1508
+ The exhaustive reasons are in the issue #439 spec amendment. A pending file value
1509
+ is not applied to the running consumer. Coordination creation pins last for the daemon lifetime; a restart re-pins retained rows to its startup mode.
1475
1510
  - `ahub settings undo` (the dashboard's "Undo last write") puts back the one
1476
1511
  previous version of the file the last write changed. It refuses when that
1477
1512
  file was edited since, and when it would put a stored never-ask back (set it
@@ -1484,10 +1519,11 @@ ahub ui --settings # dashboard with a 15-minute settings session
1484
1519
  `.agenthub/routing.local.toml`: run `ahub init` again, or add it. A
1485
1520
  `permission_modes` value is read only from a file git confirms nobody
1486
1521
  committed (config trust); where the hub will not read what was written, the
1487
- reply and the Settings row say so.
1522
+ reload refuses the guarded candidate and the Settings row identifies the file.
1488
1523
  - Authority: a dashboard opened with `ahub ui` reads settings, lowers a mode to
1489
- `ask`, turns `pi.auto_start` off, sets a start mode back to `tui` and changes
1490
- the Switches group. Everything
1524
+ `ask`, turns `pi.auto_start` off, sets a start mode back to `tui`, sets a WIP
1525
+ limit to 0, turns `task_sweep.enabled` or `.auto_reassign` off and changes
1526
+ the other Switches. Everything
1491
1527
  else needs `ahub ui --settings`, whose session may raise for 15 minutes, or
1492
1528
  `ahub settings` in a terminal. `never-ask` needs the peer id typed in the
1493
1529
  dashboard, or `--yes` in the terminal, and a stored never-ask default still
@@ -1801,6 +1837,9 @@ The runtime reset (the default):
1801
1837
  through the delivery journal, one `resolution_history` entry each. Work
1802
1838
  that was dispatching or accepted when the hub stopped is held as
1803
1839
  `needs_review` first, so it is discarded too;
1840
+ - discards hub notices a recovery hold kept back the same way, one entry per
1841
+ recipient, without delivering them; the dry run lists them as
1842
+ `q:<peer>:<id>`;
1804
1843
  - clears manual peer holds (`ahub pause`), budget pauses and conductor holds;
1805
1844
  - drops the agent session resume pointers `sessions.json`,
1806
1845
  `claude-session.json` and `claude-context.json`, so the next launch starts
@@ -1913,21 +1952,21 @@ section is what they run.
1913
1952
 
1914
1953
  Upgrade running projects with the target release's own coordinator. It accepts
1915
1954
  a running source on control protocol 9 (0.6.x), 10 (0.7.0 through 0.12.0),
1916
- 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.31), and only
1955
+ 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.33), and only
1917
1956
  a target on its own protocol, so the target's coordinator fits every supported
1918
1957
  source and carries every recovery fix released up to it. Protocol 8 and older
1919
1958
  (0.5.x and earlier) are refused as `manual-bootstrap-required`. Run from the
1920
1959
  project directory, without replacing the global CLI first:
1921
1960
 
1922
1961
  ```bash
1923
- bunx --package @staix/agent-hub@0.12.31 ahub upgrade --to 0.12.31 --dry-run
1924
- bunx --package @staix/agent-hub@0.12.31 ahub upgrade --to 0.12.31 --yes
1962
+ bunx --package @staix/agent-hub@0.12.33 ahub upgrade --to 0.12.33 --dry-run
1963
+ bunx --package @staix/agent-hub@0.12.33 ahub upgrade --to 0.12.33 --yes
1925
1964
  ```
1926
1965
 
1927
1966
  | Running now | Coordinator to use |
1928
1967
  | --- | --- |
1929
1968
  | 0.6.x (protocol 9) | the target's, through `bunx` as above |
1930
- | 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.31 (protocol 16) | the target's, through `bunx` as above |
1969
+ | 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.33 (protocol 16) | the target's, through `bunx` as above |
1931
1970
  | any supported source, with the installed CLI already at the target | `ahub upgrade` below, which is the same coordinator |
1932
1971
  | 0.5.x or earlier (protocol 8 and older) | not supported: bootstrap by hand with the matching CLI |
1933
1972
 
@@ -1959,17 +1998,18 @@ projects first:
1959
1998
 
1960
1999
  ```bash
1961
2000
  ahub restart --dry-run
1962
- ahub upgrade --to 0.12.31 --dry-run
2001
+ ahub upgrade --to 0.12.33 --dry-run
1963
2002
  ```
1964
2003
 
1965
2004
  Apply only after reviewing the plan:
1966
2005
 
1967
2006
  ```bash
1968
2007
  ahub restart --yes
1969
- ahub upgrade --to 0.12.31 --yes
2008
+ ahub upgrade --to 0.12.33 --yes
1970
2009
  ahub recovery status [<operation-id>]
1971
2010
  ahub recovery resume [<operation-id>]
1972
2011
  ahub recovery abort [<operation-id>]
2012
+ ahub recovery reattach <operation-id> codex|claude|pi
1973
2013
  ahub recovery dispose [<operation-id>] --fresh-session <peer> --reason <text>
1974
2014
  ahub recovery dispose [<operation-id>] --stop-and-archive --reason <text>
1975
2015
  ```
@@ -1992,6 +2032,15 @@ line of its own to `hub.log`), and until no task command or dashboard action is
1992
2032
  still running. A check the commit's stop kills writes to the board after the
1993
2033
  commit has recorded it, and the operation stays blocked.
1994
2034
 
2035
+ From 0.12.33, while an operation holds a source of that release or later, and
2036
+ while a restored target waits for release, the notices the hub writes itself
2037
+ (other than `[FYI]` ones) are kept apart from the carried queues, durably in
2038
+ the delivery journal and the restart snapshot, so the queue comparison still
2039
+ matches exactly. Release queues them in order and saves each move before
2040
+ anything is delivered. A target that does not advertise `recovery_deferred_hub`
2041
+ gets them in its ordinary queues before commit instead. At most 2048 such
2042
+ notices wait; a further one is refused, and none already kept is dropped.
2043
+
1995
2044
  0.9.0 turns on for every project with a config file, whether or not it has the
1996
2045
  block: `limits` (12 messages a minute per sender, 6 per recipient, 6
1997
2046
  `[IMPORTANT]` an hour, 120 s repeats) and `budget.wait_max_min` (30). Set them
@@ -2070,6 +2119,16 @@ through a pipe, the JSON plan, the `ahub: blocker:` lines and the exit codes are
2070
2119
  unchanged. The screens add no way to change an operation: every choice runs one
2071
2120
  of the commands in this section for you, so that command's refusals decide.
2072
2121
 
2122
+ The screens use the console palette and `--color=auto|always|never`, with the
2123
+ same `NO_COLOR` and `TERM=dumb` precedence. Auto colours a suitable TTY; explicit
2124
+ always has the console's override behaviour. Raw JSON bypasses colour and width
2125
+ handling. Default and never preserve the existing pipe, dry-run, status and
2126
+ dispose output byte for byte. Presentation flags go after the command, for
2127
+ example `ahub recovery --color=never` or
2128
+ `ahub recovery status <operation-id> --json`. An explicit `--json` returns only
2129
+ the raw document, without a human suffix. `upgrade --json` reviews a JSON plan;
2130
+ `--yes` is still needed to apply it without the interactive review.
2131
+
2073
2132
  The plan screen lists each project with its hub version and, per attached peer,
2074
2133
  its state, how it runs (`TUI` in a terminal the plan bound to its session,
2075
2134
  `headless` as the hub's own process, `own` for a session the hub did not
@@ -2102,23 +2161,35 @@ plan shows the ended agents as offline.
2102
2161
 
2103
2162
  After apply the command stays attached and prints each step as the receipt
2104
2163
  records it, and while a source is being prepared, what it still waits for.
2164
+ Each reported step has a status mark and elapsed observation time. Resume lists
2165
+ only steps observed on that attempt and says `resuming from ...` once. Its stage
2166
+ step really runs again and is labelled `checking retained release`. Waits use
2167
+ the coordinator's actual recorded start and deadline, for example
2168
+ `[waiting] alpha: restoring Codex and Pi sessions (waiting 2m10s of 10m)`.
2169
+ Older receipts without timing metadata say `timing unknown`; updatedAt is never
2170
+ used as a substitute. Timing-only save failures log a warning and do not fail
2171
+ the operation. Stable waits print only on change and at most once per minute.
2172
+ Approval waits point to `ahub console`; native attachment waits point to the
2173
+ recorded launch and conversation. Korean text and
2174
+ long paths wrap to the measured width on plan, progress and menu screens.
2105
2175
  Enter opens the operation's screen while the runner works on. Ctrl+C leaves the
2106
2176
  command, at any prompt and while following, and never stops the runner: the
2107
- `left:` line names the command that comes back to the screen, with the
2108
- operation's own coordinator, because the installed `ahub` is the older release
2109
- until the upgrade ends. A Ctrl+C between `a` and the start of the operation
2177
+ `left:` line names `ahub recovery status <operation-id>`. Use `--verbose` for
2178
+ the internal command with the operation's retained coordinator, because the
2179
+ installed `ahub` may still be the older release until the upgrade ends. A Ctrl+C between `a` and the start of the operation
2110
2180
  starts nothing. A prompt for a reason that is answered with
2111
2181
  Ctrl+C or the end of input ends nothing.
2112
2182
 
2113
2183
  The operation's screen also opens first whenever an operation holds the lock,
2114
2184
  and for bare `ahub recovery`. It shows the phase and step, each project's phase
2115
- and effect receipts and the error, then the choices that `status` lists under
2185
+ and human outcome labels, then What happened / Why / What to do and the choices that `status` lists under
2116
2186
  `next` for the same receipt and live state:
2117
2187
 
2118
2188
  | Key | Runs | Offered when |
2119
2189
  | --- | --- | --- |
2120
2190
  | `r` | `recovery resume`, then follows | resume can get past what is live |
2121
2191
  | `c` | `recovery abort` | no runtime was stopped and no terminal effect or commit request is recorded |
2192
+ | `a` | `recovery reattach <id> <peer>` | the recorded detached session is eligible for reattachment |
2122
2193
  | `f` | `recovery dispose --fresh-session <peer>`, after a reason | a Codex or Claude restoration failed |
2123
2194
  | `e` | `recovery dispose --stop-and-archive`, after a confirmation and a reason | every unverified project reads as running, stopped or missing |
2124
2195
  | `x` | `e`, then the reset below | as `e` |
@@ -2128,6 +2199,31 @@ When more than one restoration failed, the second and later peers get
2128
2199
  `f-<peer>`. `s` refreshes the screen, `j` prints the receipt as JSON and `q`
2129
2200
  leaves the operation as it is.
2130
2201
 
2202
+ The marked recommendation is a read-only Enter default: refresh the plan, follow
2203
+ an active runner, read the receipt or refresh the operation. Apply, resume and
2204
+ reattach require their explicit keys; buffered Enter never starts them. Unknown failures recommend
2205
+ reading `j` before retrying effects. A wait whose runner cannot be verified is
2206
+ shown disabled with its reason, and cannot run a command. Stop, archive, reset
2207
+ and fresh-session choices carry a warning and name the consequence before the
2208
+ person confirms. Raw receipt keys and the original recorded error are available
2209
+ with `j` or `--verbose`. The four preservation failures name the actual failed
2210
+ queue, manual-pause, board or budget comparison; an older untyped cause remains
2211
+ unknown rather than being guessed from its text. When nothing more specific
2212
+ applies (no typed cause, reattachment or `database_busy`), Why shows the first
2213
+ line of the recorded error, with control characters removed and cut at 512
2214
+ characters, marked `typed cause unknown`. What to do then says to resolve that
2215
+ error, then resume or choose an offered action. Nothing is classified from that
2216
+ text, and JSON and piped output are unchanged.
2217
+
2218
+ Completion and stop-and-archive use a human summary on a TTY. It separates the
2219
+ outcome, recorded plugin/global installation effects, remaining work and the
2220
+ per-peer session commands. An optional fresh read-only inspection names the
2221
+ current hub version; a failed reading says `runtime unknown`, never a guessed
2222
+ version from the plan. The summary performs no lifecycle action. Use `j` or
2223
+ `--json` for the full receipt; JSON is intentionally exempt from human wrapping.
2224
+ For a long command, copy the exact single-line value from `j` or the status JSON;
2225
+ the width-wrapped human preview is for reading.
2226
+
2131
2227
  While a runner is only waiting for its source to get quiet (step `prepare`, no
2132
2228
  effect recorded), `c` stops that runner and then aborts. It is offered only for
2133
2229
  a runner whose claim carries a process signature that still matches; a claim
@@ -2177,7 +2273,10 @@ each receipt allows, given what is live, is tabulated in the recovery spec
2177
2273
  (`docs/specs/2026-09-20-upgrade-recovery-design.md`, "Receipts, evidence and
2178
2274
  next actions").
2179
2275
 
2180
- - The source hold lapses after 10 minutes. A later `resume` prepares the same
2276
+ - The source hold lapses after 10 minutes. If releasing it fails because the
2277
+ source's delivery storage fails, the hold stays and the expiry is retried
2278
+ every second until storage recovers; restore the storage before `abort`.
2279
+ A later `resume` prepares the same
2181
2280
  source again and checks its peers again, keeping the receipts, so no terminal
2182
2281
  is closed twice. It refuses, naming the next step, when the source daemon was
2183
2282
  replaced, when another operation holds it, or when a peer changed: a peer whose
@@ -2239,7 +2338,11 @@ next actions").
2239
2338
  that session. A target that dies again in the same `resume` stops it with the
2240
2339
  restart count; look at its `hub.log` before resuming again. Without the
2241
2340
  snapshot, or for an operation started by a coordinator older than 0.12.21,
2242
- the way out is stop-and-archive.
2341
+ the way out is stop-and-archive. A snapshot whose kept hub notices fail
2342
+ validation blocks that restart with `restart snapshot deferred hub notices
2343
+ are invalid; restore is blocked`. The hub still reads as stopped, so
2344
+ stop-and-archive (and abort, where it applies) stays offered; nothing
2345
+ repairs the snapshot or offers to resume from it.
2243
2346
  - A Codex conversation comes back only when a rollout file naming its thread
2244
2347
  exists under `sessions/` of the store the restored terminal uses (the
2245
2348
  recorded `CODEX_HOME`, else `~/.codex`). Codex writes that file with the
@@ -2281,11 +2384,23 @@ next actions").
2281
2384
  unpinned signature too, so it counts as running when this command runs in
2282
2385
  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
2283
2386
  the operation's own `status` command, which lists what to do next.
2387
+ - `ahub recovery reattach <operation-id> codex|claude|pi` is person-only, including during live verification: an agent shell cannot delegate it. Name the operation and exactly one peer; session, backend, model and terminal overrides are not accepted. Use the exact operation-coordinator command printed by status or its blocked guidance. It repairs only that peer and leaves the operation blocked; run the offered `recovery resume` command to continue the remaining steps.
2388
+ The CLI delegates only when the retained coordinator supports reattach. An existing older coordinator without that action uses this release's implementation instead; a missing receipt, retained coordinator path or coordinator file prints inspection/recovery guidance and starts no session.
2389
+ A peer that attached and later went offline can be re-restored once when its saved terminal is positively orphaned or missing and no current or older operation launcher may still run. The recorded session id and Pi session file stay fixed; the new terminal binding is saved before waiting for authenticated attachment. A second detach blocks. An unreadable terminal, changed incarnation or worktree, disconnected-only evidence, truncated inventory or live/unknown launcher does not authorize another terminal.
2390
+ Recovery status names the saved Codex thread, Pi session file and Claude session, the terminal evidence, and the applicable reattach command. A different attached conversation remains a strict refusal.
2284
2391
  - `ahub recovery dispose <operation-id> --fresh-session <peer> --reason <text>`
2285
2392
  applies to a Codex or Claude peer whose restoration failed (Pi is refused: a
2286
2393
  restored hub resumes Pi's recorded session). It records the lost session or
2287
2394
  thread id (status shows it under `lostContinuity`) and resumes: the peer
2288
2395
  starts without its old conversation, and the rest is verified as usual.
2396
+ A session the operation already reattached keeps that repaired provenance
2397
+ across target restarts and is never replaced automatically. Its
2398
+ loss can be accepted only after the restoration reported the recorded Claude
2399
+ transcript or Codex rollout as positively missing, naming the checked path;
2400
+ dispose checks that path again before saving the choice and otherwise refuses
2401
+ with "recorded conversation is not positively missing". Under `--live-test`
2402
+ this repaired-session choice is refused even where an ordinary fresh session
2403
+ may be delegated.
2289
2404
  - `ahub recovery dispose <operation-id> --stop-and-archive --reason <text>`
2290
2405
  abandons the operation. It checks every project first and refuses while a
2291
2406
  hub is unreachable, starting, stopping or of another control protocol; `next`
@@ -2332,7 +2447,8 @@ next actions").
2332
2447
  again.
2333
2448
  Start a project whose target ran with that release's CLI (`bunx --package
2334
2449
  @staix/agent-hub@<version> ahub up`), since it may have changed the task
2335
- database. The global CLI was not promoted.
2450
+ database. The global CLI was not promoted. The archive summary distinguishes completed target startup and plugin installation from remaining global CLI installation, and prints recorded-session commands: `ahub codex resume <thread-id>`, `ahub pi --session-file <path>`, and the Claude reconnect command. On the next `ahub up`, `pi.auto_start` resumes the archived operation's recorded Pi session rather than choosing a fresh conversation. Unknown or conflicting archived identity refuses that automatic resume.
2451
+ That session is kept as a bookmark, `.agenthub/state/recovery-continuity.json`. When its session file can no longer be resumed or does not match it, the hub archives the bookmark and starts no fresh session in that start; the error names `ahub pi --mode <mode> --session-file <managed-file>` for choosing one. An invalid or unreadable bookmark, or session storage that cannot be verified, starts nothing, keeps the bookmark and prints the `mv -n` command that archives it after inspection. The bookmark is retired once Pi attaches with a different authenticated session.
2336
2452
  - Both choices are for a person in a terminal; an agent shell is refused, and
2337
2453
  the reason is kept in the operation's audit. Run each with the command
2338
2454
  `next` or the error prints: the operation's own coordinator, or the release
@@ -2556,7 +2672,7 @@ ahub task cancel <id> --reason <text> [--superseded-by <ref>] [--revision N]
2556
2672
 
2557
2673
  The console also offers cancellation with the selected task's flow revision. Approved tasks cannot be cancelled. The planner's `hub_task_cancel` tool is limited to its own unaccepted backlog/proposed work; previously accepted work requires the person. Cancellation sends the former owner/reviewer a stop notice, releases overlap/reservation participation and keeps dependents blocked with a cancelled-dependency notice. It does not stop a native turn, delete a worktree or write an outcome memory note. Cancelled work is closed history, counted separately from approved work; it does not satisfy an approval dependency. A still-running private turn keeps its privacy fence after cancellation. Use the displayed revision to avoid acting on a task that changed while it was selected.
2558
2674
 
2559
- The daemon advertises `task_cancel`; a current CLI refuses cancellation against older daemons. Recovery retains verified target features beside its immutable root/digest, reuses them on resume and checks only sources not yet past commit; a legacy receipt without that evidence needs a target probe only before commit. Upgrade/restart refuses a target without this feature while cancelled rows exist and names their ids. No cancelled row is rewritten as approved. An old binary launched by hand remains unsupported: it can count cancelled work as open or fail to move it. Use a compatible coordinator instead. Live task cancellation and sweep enablement are separate post-release operations.
2675
+ The daemon advertises `task_cancel`; a current CLI refuses cancellation against older daemons. Recovery retains verified target features beside its immutable root/digest, reuses them on resume and on each lease renewal before a terminal is closed, and checks only sources not yet past commit; a legacy receipt without that evidence needs a target probe only before commit. Upgrade/restart refuses a target without this feature while cancelled rows exist and names their ids. No cancelled row is rewritten as approved. An old binary launched by hand remains unsupported: it can count cancelled work as open or fail to move it. Use a compatible coordinator instead. Live task cancellation and sweep enablement are separate post-release operations.
2560
2676
 
2561
2677
  ## Task idle sweep
2562
2678
 
@@ -17,7 +17,7 @@ ahub setup # installs the Claude Code channel plu
17
17
  Or use the matching GitHub release:
18
18
 
19
19
  ```bash
20
- bun add -g github:STAIxBWLB/agent-hub#v0.12.31
20
+ bun add -g github:STAIxBWLB/agent-hub#v0.12.33
21
21
  ahub setup
22
22
  ```
23
23
 
@@ -30,9 +30,11 @@ and upgrade or crash recovery, see the [operations guide](operations.md). To
30
30
  move running projects to a newer release later, run `ahub upgrade` in a
31
31
  terminal: it reviews the plan with you, applies it and follows it.
32
32
 
33
- ## Upgrading to 0.12.31
33
+ ## Upgrading to 0.12.33
34
34
 
35
- Existing Jcode project MCP entries need one refresh before conductor or dashboard starts. Once per project, the person runs:
35
+ The upgrade runs with the target release's own coordinator, so an upgrade from 0.12.32 with cancelled tasks and a managed terminal, or with a notice the target sends right after it starts, no longer blocks (#478). If the operation blocks because a restored Codex, Claude or Pi terminal disappeared, the person runs `ahub recovery reattach <operation-id> codex|claude|pi` to relaunch that peer with its recorded session (one relaunch per receipt), then `ahub recovery resume` to finish.
36
+
37
+ From 0.12.30 or earlier, existing Jcode project MCP entries need one refresh before conductor or dashboard starts. Once per project, the person runs:
36
38
 
37
39
  ```bash
38
40
  ahub jcode --mode headless
@@ -83,6 +85,7 @@ or set `OMNIROUTE_API_KEY`. Name the model in `.agenthub/routing.toml` (`[local]
83
85
  | `ahub board` | who has what; `ahub task show <id>` for one task |
84
86
  | `ahub task cancel <id> --reason <text>` | close obsolete work without approving it or stopping a native turn |
85
87
  | `ahub settings set planner <peer>` | pin the planner; status reports whether it came from settings, roles or the automatic choice |
88
+ | `ahub settings reload` | apply edited config files without a restart; guarded values wait for your `y` in this terminal, and restart-only keys stay pending |
86
89
  | `ahub status` | peers, queues, pauses; `ahub budget` for quota windows |
87
90
  | `ahub memory profile-check` | a person's read-only check of running memory bindings in the current registry |
88
91
  | `ahub ask <question>` | what the board, shared memory and the log say, with the ids the answer rests on |
@@ -93,7 +96,7 @@ More: `ahub help`. How it is built and why: [`docs/specs/2026-09-19-agent-hub-de
93
96
 
94
97
  ## Shared memory
95
98
 
96
- The hub exact-pins the first-party `@staix/agent-mem` 0.3.0 core. Existing shared-v1 memory remains the default; upgrading does not migrate it. Managed agents use screened `hub_memory_search`, `hub_memory_show` and `hub_memory_graph` tools, or propose a correction with `hub_memory_correct`; only a person accepts candidates through the supported `amem review` path. Generation candidate acceptance is still refused.
99
+ The hub exact-pins the first-party `@staix/agent-mem` 0.3.1 core. Existing shared-v1 memory remains the default; upgrading does not migrate it. Managed agents use screened `hub_memory_search`, `hub_memory_show` and `hub_memory_graph` tools, or propose a correction with `hub_memory_correct`; only a person accepts candidates through the supported `amem review` path. Generation candidate acceptance is still refused.
97
100
 
98
101
  Before any real generation migration, upgrade every hub sharing that memory home to 0.12.30 or later and check its authenticated binding with `ahub memory profile-check` in each custom registry. Ready bindings prove compatibility, not an atomic live cutover: quiesce consumers and close their handles before migrating, then reopen through the supported session-preserving lifecycle. Invalid or changed profiles disable native memory without falling back to legacy writes; the bus and board continue. See [memory operations](operations.md#hub-memory) for the remaining rollout gates.
99
102
 
@@ -110,8 +113,9 @@ contents: inspect them with `ahub tail`, then use `ahub permit <id> <option>` to
110
113
  allow. They can be denied on the page. PII task details remain available only via
111
114
  `ahub task show <id>` in the terminal. The page also starts a peer in its start
112
115
  mode (a TUI opens in a terminal when the hub has a terminal provider) and shows
113
- the settings; raising one needs `ahub ui --settings` or `ahub settings` in a
114
- terminal. See the operations guide, "Settings from the dashboard and the
116
+ the settings with a Reload configuration button; raising one needs `ahub ui --settings`
117
+ or `ahub settings` in a terminal, and guarded values from an edited file need
118
+ `ahub settings reload` there. See the operations guide, "Settings from the dashboard and the
115
119
  terminal" and "Start modes".
116
120
 
117
121
  `ahub ui --no-open` prints a one-time link instead of launching a browser. Open it
package/docs/security.md CHANGED
@@ -11,7 +11,7 @@ agent-hub connects agents that can each run commands. This page says what the hu
11
11
  - **Telemetry holds no bodies.** `.agenthub/state/events.jsonl` (issue #40) records envelope ids, routing and sizes, task ids and states, overlapping paths and token counts, and the task id (with a PII flag) that each usage and token record is attributed to. It never records a message body, a task title or detail, and marks private (PII) envelopes and tasks as such. It stays on the machine; `ahub export` only prints it. Two opt-in stores copy numbers from it into your home directory, shared by every project on the machine (directories mode 700, files 600): research records (issue #247, `research.enabled`) in `~/.agenthub/research/` hold per approved task the ids, states, counts, tokens, times and PII flag the events hold, a person's label from a closed list, and a hash in place of the project id; benchmark runs (issue #251) in `~/.agenthub/bench/` hold suite name and hash, arm label, the attached peers' ids, models and permission modes, task ids, outcomes, exit codes, counts, tokens and times, never suite text or a command's output.
12
12
  - **Snapshots stay in your repository.** Per-turn snapshots (issue #33) are git objects in the project's own object store, written through a temporary index; nothing is referenced, pushed or copied elsewhere, and `git gc` prunes them. They hold what the work tree held, including untracked files that are not ignored, so keep secrets in ignored files. They carry the repository's own permissions, and nothing caps their disk use but `git gc`. A turn of a peer holding an open PII task is not snapshotted; a PII file left in the project is snapshotted by later turns like any other file. `ahub undo` restores only files whose current content is exactly what the turn left.
13
13
  - **The edit hook reads, never decides.** `ahub check-path --hook` (issue #32) reads hub.db and returns context for Claude and a line for you; it sets no permission decision, so your permission rules stay in charge. It names other owners' task ids, titles and states, which then reach Claude's model; PII tasks are left out.
14
- - **The session record holds identities only.** `.agenthub/state/sessions.json` (issue #37, mode 600) keeps each attached peer's recovery metadata: launch options, session and thread ids, Pi's session file path. No message or task text; loss notices name deliveries by id, sender and public task title.
14
+ - **The session record holds identities only.** `.agenthub/state/sessions.json` (issue #37, mode 600) keeps each attached peer's recovery metadata: launch options, session and thread ids, Pi's session file path. No message or task text; loss notices name deliveries by id, sender and public task title. The Pi continuity bookmark that recovery keeps across stop and archive (`.agenthub/state/recovery-continuity.json`, mode 600, at most 16 KiB) holds identities only too: operation and project ids, the project root, and Pi's session id, session file, backend and model name; a bookmark that is not a private regular file, is larger than that or names another project or operation reads as invalid, which is kept distinct from no bookmark.
15
15
  - **Context checkpoints have a separate text record.** Native occupancy readings carry numeric metadata only. An optional context checkpoint persists its summary in `.agenthub/state/context-checkpoint-<peer>.json` with mode 0600 and attempts shared-memory storage when enabled. Completion requires the outstanding request id, current peer/native session and transport generation; new connection claims invalidate old requests. Private turns, every open PII task associated with the peer as owner or reviewer (including pending review), and PII-pattern summaries are refused before persistence or cloud memory. Its text is never broadcast, and context pressure does not pause, reassign or replace the session.
16
16
  - **The local worker is boxed in.** Paths are resolved through symlinks and must stay inside the project; a secrets denylist (`.env*`, keys, credential files, the hub's own state and the state archives `ahub reset --all` leaves) applies to its file tools, its git arguments and its memory capture alike; `.git` and `.agenthub` are not writable. Writes, edits, shell commands and mutating git wait for approval unless the operator's permission mode grants them (writes and edits inside the grant filter in `ask-when-needed`, all four in `never-ask`; see below), and the approver sees what will be written or run, with control characters escaped. Everything it executes runs under the macOS sandbox, attended or not. Since 0.10 the profile starts from deny default (issue #39): commands run and read only the system, toolchain and project directories (and the project's git dir and the selected developer dir; with network on, the public CA bundles); no writes outside the project, its git dir and a temp dir of its own (`TMPDIR`, made for each command and removed when it ends; issue #63); no `.git` name at any depth, closing the creation, rename, symlink and gitfile routes to a planted repository, no `config` or `hooks` directly under one nor below the discovered modules root of the project's own `.git` (a submodule's git directory, nested submodules included; every term is anchored below that root, so a directory named `refs`, `config` or `hooks` above the project neither disables the rule nor trips it; a `/refs/` or `/logs/` segment below it keeps refs named `config` or `hooks` writable, and leaves a submodule whose own path contains such a segment uncovered; a submodule whose path has `hooks` as a whole component (`tools/hooks` included) fares worse: the rule matches its whole git directory, so every write there is refused; the discovered git directories, the modules root and every component in between are also refused as targets, closing the rename, replace and symlink routes to the same files), no `commondir` at any depth below one, with every such name matched folded against the code points HFS+ ignores (the fold covers the security-relevant names; on a volume that folds names, folded spellings of the components between `.git` and a submodule's git directory are not matched — the 1024-byte SBPL literal cannot hold a folded path — the same residual the external git dir rules carry since #270), and no `.agenthub` anywhere in the project (in an external git dir the `config`/`hooks` rules cover only its own children and the hooks directory's contents, so refs named `config` or `hooks` still work); a nested repository in bare layout stays open — the directory is the git dir, with no `.git` name to anchor a rule on, and the hub never runs git there, whatever the project root is: its calls are all `-C <project root>` or `--no-index` on temp files, and none descends into a nested directory's git layout, so a planted hook fires only if a person or another tool runs git in that directory themselves; the rules are probed on HFS+, which folds the 16 code points the patterns tolerate, and on a case-sensitive APFS image, which folds nothing (a folded spelling is an inert new file there and the real names stay refused); the cost is that `git init`, `git clone` and `git worktree add` do not run inside the sandbox (add, commit, stash, checkout and gc still do), `git submodule update --init` does not either (its git directory's `config` write is refused), and neither do `git worktree remove`, `git worktree prune` of a stale record, `git clean -ffd` in a project with a nested repository, or `rm -rf` of a directory that holds a `.git` (refusing the unlink is what stops the rename-out route); no network, loopback included, unless `local.bash_network`; with it, only through the hub's egress proxy to the hosts in `local.network_allow` (issue #65), which refuses names that resolve to internal addresses (behind NAT64 with the well-known prefix, by the IPv4 address the answer carries; a network-specific prefix is not recognised), so claude-mem and the Codex app-server stay out of reach (`"direct"` opens everything, until 0.13.0). One channel stays open: `trustd`, which TLS clients need, can fetch a certificate's AIA or OCSP URL on a command's behalf, outside the proxy. The allow-default profile of 0.9 and earlier was removed in 0.12.0 (issue #83); a `local.sandbox` setting is ignored with a note. The shared temp dirs (the user's and `/private/tmp`) are closed. Without the sandbox there is no `bash` tool.
17
17
  - **Capabilities are enforced, not suggested.** `capabilities` (issue #39) is checked by the daemon where task operations and messages arrive, so a peer cannot get round it by phrasing. A peer can never answer a permission request: the control link takes `permit` from the console role only, and a message that quotes a permit command is just text. Both bind the hub's own tool paths: a vendor agent with its own shell in the project (Codex, Claude, Kimi) can read `.agenthub/state/control-token` and connect as the console, which only the local worker's and Pi's sandbox prevents.
@@ -23,7 +23,7 @@ agent-hub connects agents that can each run commands. This page says what the hu
23
23
 
24
24
  ## Native memory authority and profile selection
25
25
 
26
- The portable core is the exact-pinned first-party `@staix/agent-mem` 0.3.0 package; it does not replace the hub's caller identity, PII policy or task-write authority. Deny, hardened git, real paths and process identity are single-sourced in its Node-safe platform entry. Current project privacy is applied by the hub before promotion and again before model-facing reads. Standalone person reads are unscreened, and the standalone `--policy-free` acknowledgement grants no hub or peer authority.
26
+ The portable core is the exact-pinned first-party `@staix/agent-mem` 0.3.1 package; it does not replace the hub's caller identity, PII policy or task-write authority. Deny, hardened git, real paths and process identity are single-sourced in its Node-safe platform entry. Current project privacy is applied by the hub before promotion and again before model-facing reads. Standalone person reads are unscreened, and the standalone `--policy-free` acknowledgement grants no hub or peer authority.
27
27
 
28
28
  `hub_memory_search`, `hub_memory_show` and `hub_memory_graph` take the project from the daemon, require `memory_read`, refuse private turns and validate UTF-8 input before logs or storage work. Complete records and graph metadata are screened before counts and truncation. Search is at most 20 lines/4 KiB (default 8); show accepts at most 8 ids and returns at most 8 KiB; graph is at most 120 nodes/240 edges/6 KiB (default 60 nodes), with no edge lacking an endpoint. Unknown, ambiguous and withheld ids share one neutral refusal. Arguments and results are excluded from hub-managed capture; this does not govern a vendor's independent transcript or memory hooks.
29
29
 
@@ -98,8 +98,11 @@ if that audit cannot be written, the command refuses. The current daemon emits
98
98
  `agent_cli` with `delegated: true` and `refused: false`. Allowed-class commands retain the peer identity and capability checks, including
99
99
  messages, proposals and reviews. Peer starts, pause and resume may delegate
100
100
  operational console authority; task assign/escalate under the flag refuse. `--as-user`, person task labels,
101
- person-authored planning/release/memory/goal writes, SDLC record, benchmark run
102
- and the pet composer are refused. No argument text is copied into this audit.
101
+ person-authored planning/release/memory/goal writes, SDLC record, benchmark run,
102
+ `ahub recovery reattach`, a `--fresh-session` dispose that would replace a repaired
103
+ session's conversation (or whose scope cannot be read), and the pet
104
+ composer are refused; presentation flags such as `--color` or `--verbose` before
105
+ the recovery action do not hide it from this check. No argument text is copied into this audit.
103
106
  Delegated dashboard restrictions are bound to its server-issued ticket/session:
104
107
  operational settings/start/stop controls remain, but message, proposal and
105
108
  workflow-history actions refuse even if the body tries to clear the restriction.
@@ -116,7 +119,12 @@ The operator uses a plain terminal or `ahub console`. A Claude `!` command that
116
119
  inherits the agent markers follows the same rule. The console strips control
117
120
  sequences from agent and daemon text before it quotes forged headers, and its
118
121
  colors come only from a fixed palette applied afterwards, so message text cannot
119
- set styles or operate the terminal.
122
+ set styles or operate the terminal. The recovery blocked screen shows a recorded
123
+ error only when no typed cause exists: its first line, passed through the
124
+ console's terminal sanitizer with control characters removed, capped at 512
125
+ code points and marked as an untyped recorded error. A restart snapshot whose
126
+ kept hub notices fail validation is reported with a fixed message, never with
127
+ its content.
120
128
 
121
129
  This is an honest default against accidental impersonation and injected commands.
122
130
  It does not make the token inaccessible to an agent with unrestricted project
@@ -150,16 +158,17 @@ Please report vulnerabilities privately through GitHub's "Report a vulnerability
150
158
  - Every request must have the exact listener Host. Every data or action request, including the ticket exchange and snapshot polling, must be POST with the exact listener Origin and JSON content type. Foreign and missing origins, missing or expired sessions, oversized bodies and unknown actions are rejected. There is no CORS support. The only unauthenticated GET is the fixed, data-free HTML shell. No files or paths are served dynamically.
151
159
  - Responses are non-cacheable, cannot be framed, suppress referrers, and use a content security policy restricting scripts/styles to the shipped inline content and connections to this origin. Browser text is rendered with textContent, never interpreted as HTML.
152
160
  - The browser receives a bounded redacted event stream, peer states and queue counts, public task views, budget windows, pending approvals, the settings rows (closed values and ids, the file each comes from) and each startable peer's start plan (issue #269), benchmark run summaries (issue #251): the newest runs in `~/.agenthub/bench/`, whichever project on this machine ran them, by suite name, arm label, state, the task id in progress, counts, tokens and times, never suite text, and the office view's optional fields (issue #264): each peer's open turn (start time, attributed task id, attribution kind), a `checking` flag on tasks whose check is running, and warning `links` of overlapping, conflicting or cohort tasks (task and peer ids only, never paths, symbols or reasons; a PII task never joins a link, and a private or PII-attributed turn exposes no task id). Private envelope bodies and PII task text never cross this endpoint. Local-worker and Pi approval titles can contain PII, so their details remain terminal-only; the dashboard identifies the request and directs the operator to `ahub tail` before allowing it. Budget checkpoint summaries are also omitted.
153
- - The closed action list includes permission response, peer pause/resume, console message, task proposal/assignment and the settings actions below (set, undo, preview). Project-board actions add revision-fenced planning, Backlog pull/return/split/undo and release planning/state changes through Tasks; they do not tag, publish or write GitHub state. These use the existing daemon/task paths and budget pause rules. `start_peer` (issue #269) starts a peer from a closed list in the mode a person set, with a command the hub assembles: the request carries a peer id and nothing else. There is no generic control proxy, task-detail read, shell, file access, free-form configuration edit, budget override, launch of a caller-chosen command or daemon shutdown API.
161
+ - The closed action list includes permission response, peer pause/resume, console message, task proposal/assignment and the settings actions below (set, undo, preview, reload). Project-board actions add revision-fenced planning, Backlog pull/return/split/undo and release planning/state changes through Tasks; they do not tag, publish or write GitHub state. These use the existing daemon/task paths and budget pause rules. `start_peer` (issue #269) starts a peer from a closed list in the mode a person set, with a command the hub assembles: the request carries a peer id and nothing else. There is no generic control proxy, task-detail read, shell, file access, free-form configuration edit, budget override, launch of a caller-chosen command or daemon shutdown API.
154
162
  - The listener stops with the daemon. Expired sessions must be reopened with `ahub ui`; the page does not silently obtain new credentials.
155
163
 
156
164
  ## Dashboard settings and the settings session (issue #269)
157
165
 
158
166
  - Settings are a closed registry (`src/hub/settings.ts`). The dashboard and `ahub settings` can write only its keys, each checked against its own closed values, and only to `.agenthub/config.local.json` and `.agenthub/routing.local.toml`. A file git tracks is never written, so the shared `config.json` and `routing.toml` stay byte-identical; in a repository where git cannot answer, nothing is written either. Commands and binaries (`kimi_cmd`, `codex_bin`, `pi.cmd`, `checks`, `mlx`), credential files and gateway URLs (`omniroute`, `memory.worker_url`), the local worker's reach (`local.read_allow`, `local.bash_network`, `local.network_allow`), `bench.enabled` and `terminal` are refused by name from every surface: they choose what the hub runs or where secrets and task text go.
159
167
  - `routing.local.toml` holds `stay_switch` and, per class, `peers`, `escalate_to`, `route` (an id `routing.toml` defines) and `pi_backend`, and nothing else: targets, routes, signals and the PII constraint cannot be set through it. A write is read by the hub's own loader before it replaces the file (temp file and rename), and one previous version is kept for undo.
160
- - An ordinary dashboard session (`ahub ui`) reads settings, lowers a mode to `ask`, turns `pi.auto_start` off and changes the safe switches (`research.enabled`, `approvals.notify`, `snapshots.enabled`, `coordination`). Whatever can widen what agents do without asking needs a settings session: a non-ask mode now or at hub start, routing, starting Pi with the hub, removing a machine-local value, and undo. `ahub ui --settings` opens one: its ticket marks the session for 15 minutes, after which it is an ordinary session. `never-ask` also needs the peer's id typed as the confirmation, and a stored never-ask default still waits for the console's `y` at each hub start. An undo never puts never-ask back, and removing a machine-local value that uncovers a never-ask in the shared file needs the same confirmation. "Lowering to `ask`" means handing the decision back to the agent's own rules: for Codex that is the approval policy the person configured natively, which the hub does not judge.
168
+ - An ordinary dashboard session (`ahub ui`) reads settings, lowers a mode to `ask`, turns `pi.auto_start`, `task_sweep.enabled` or `task_sweep.auto_reassign` off, sets a start mode back to `tui` or a WIP limit to 0, and changes the safe switches (`research.enabled`, `approvals.notify`, `snapshots.enabled`, `coordination`). Whatever can widen what agents do without asking needs a settings session: a non-ask mode now or at hub start, routing, starting Pi with the hub, removing a machine-local value, and undo. `ahub ui --settings` opens one: its ticket marks the session for 15 minutes, after which it is an ordinary session. `never-ask` also needs the peer's id typed as the confirmation, and a stored never-ask default still waits for the console's `y` at each hub start. An undo never puts never-ask back, and removing a machine-local value that uncovers a never-ask in the shared file needs the same confirmation. "Lowering to `ask`" means handing the decision back to the agent's own rules: for Codex that is the approval policy the person configured natively, which the hub does not judge.
161
169
  - A settings session is as strong as the console for these settings and no stronger elsewhere. A mode change is the console's own request with the same refusals; tool approvals stay deny-only for Pi and local; there is still no shell, file, task-detail or launch-command API. `ahub ui --settings` and `ahub settings` pass the same identity gate as `ahub permission`, which is an operating policy, not authentication. The hostile-agent boundary above applies unchanged: an agent with an unrestricted shell as the same user can impersonate the console, and so can open a settings session.
162
170
  - Every change writes a `settings` event (the key, the value before and after, the source `dashboard` or `terminal`) and a line in `hub.log` and open consoles; opening a settings session is logged too. The unified dashboard (`ahub ui --all`) forwards settings actions as an ordinary session and opens no settings session.
171
+ - Configuration reload (issue #439). The daemon watches `config.json`, `config.local.json`, `routing.toml` and `routing.local.toml` and re-reads the complete candidate after a change, as the dashboard's Reload configuration and `ahub settings reload` do; an invalid candidate applies nothing. Each changed key is then authorized on its own against its file's actual git provenance. A privacy tightening (local-only PII, the model screen on, added PII patterns, narrower roles or capability lists, unless it moves the planner or conductor) and a safe registered setting apply from any file. Every other change, machine-local fields included, keeps its running value when its file is tracked or git cannot answer. From an untracked file such a guarded change is adopted only by `ahub settings reload` in an interactive terminal, after it lists each guarded key with its full before and after values and the person types `y` within 30 seconds; the confirmation is bound to the file contents and accepted state it was shown for. File watches and dashboard reloads, a settings session's included, never confirm, and no reload changes the roster: membership moves only through the roster setting. Keys whose consumers are bound at start (memory, the gateway and MLX, checks, the privacy and sandbox policy of existing sessions, the terminal provider and others) stay pending until a restart.
163
172
  - Start modes. A start the hub makes itself (the dashboard's Start control, `peer_start` from a console child, the conductor's `hub_peer_start`, `pi.auto_start`) takes the peer's start mode. Through settings only a settings session or the terminal can set it to headless; a config file can also set `peers.<id>.start_mode`, the shared one included (it is not a machine-local field: headless was the only form for Pi, Kimi and the local worker before this change, a hub-made headless Codex start is the app-server the hub already ran for it, and a start mode grants nothing a permission mode or a capability controls). For a TUI the hub opens a terminal running `<runtime> <its own entry point> --project <this project> <peer>` (and `--mode tui` for Pi): every word is the hub's own, the command line is those words each quoted for a POSIX shell, and no request field, tool argument or agent message is part of it. In a `terminal.open` template `{command}` is that quoted line and `{title}` and `{cwd}` are raw text for argv elements of their own; each placeholder is substituted once, so a project path that itself contains a placeholder, a quote or `$` stays one word. A provider's own error text goes to `hub.log`, not to the caller. The terminal comes from `terminal.open`, a machine-local command template that config trust refuses from a committed file and that no settings surface can write, or from Orca. The opened terminal's environment carries no agent marker, so `ahub` there has the console's identity. Without a provider the start is refused with the command to run by hand; it is never downgraded to headless. A conductor can start a peer but cannot choose its mode.
164
173
 
165
174
  ## Multi-project manager (protocol 7)
@@ -192,7 +201,7 @@ conductor holder is refused. Local and Pi scoped tools cannot write `.agenthub`.
192
201
  Claude, Codex and Kimi still follow their own file permissions and can edit a
193
202
  config file; such an edit changes participation only at the next hub start. A
194
203
  live settings write or undo applies its own roster key and does not adopt other
195
- file edits. External ids outside the known native catalog keep their prior
204
+ file edits, and a configuration reload refuses roster changes. External ids outside the known native catalog keep their prior
196
205
  admission rules.
197
206
 
198
207
  Kimi's pull-only TUI owns its tool approvals and session. The person's launcher
@@ -215,7 +224,7 @@ are in [the configuration inventory](verification/2026-10-11-agent-config-invent
215
224
  Instruction files `AGENTS.md`, `CLAUDE.md` and `QWEN.md` remain grantable by default.
216
225
  A person can set `scoped_grants.protect_instructions` to true to require approval
217
226
  for scoped writes and edits through original or resolved spellings. It applies
218
- live after a settings write or undo, never changes reads or never-ask, and cannot
227
+ live after a settings write, undo or reload (turning it off from an edited file needs the confirmed terminal reload), never changes reads or never-ask, and cannot
219
228
  be set through an agent, tools or conductor surface. Claude's daemon response
220
229
  withholds the scoped mode on these writes so an older hook grants nothing too.
221
230
 
package/docs/smoke.md CHANGED
@@ -1901,3 +1901,37 @@ The current source fixture at 6d311a51dccd41d3703079175cac65a487189b15 used the
1901
1901
  This supersedes earlier phone-fixture pending statements only for this source. The old 1bbced04 captures remain historical; no later byte-identity premise is used. Native planner/two-release, accounts, model ingestion and measured effects remain unverified.
1902
1902
 
1903
1903
  The later bounded release-audit preview text is verified by DOM tests, not by a refreshed phone capture. The6d311a51 PNGs remain evidence for their pinned source; see the later-audit note in the phone verification report.
1904
+
1905
+
1906
+ ## Settings reload and console fixture (#439, 2026-10-17)
1907
+
1908
+ Native real-terminal smoke is **UNVERIFIED**. The admitted remote install/test
1909
+ attempt exited 75 before running because Python pid 6672 (parent 6671) had no
1910
+ verified repository owner. No retry, process kill, user-hub settings change,
1911
+ installation or restart was performed. A scripted ConsoleTerminal is fixture
1912
+ evidence, not a native PTY or account result.
1913
+
1914
+ The implementation fixtures cover the following in disposable projects and temp
1915
+ memory homes:
1916
+
1917
+ - Console Settings tab, daemon preview, bounded reason, explicit y, undo, stale
1918
+ preview fencing and pasted-y refusal.
1919
+ - Dashboard reload/context, running/file values, pending restart reasons and
1920
+ stable drafts; older snapshots remain readable.
1921
+ - Real fixture daemon: live switches/WIP and undo, retained pause/queue/instance,
1922
+ actual sweep timer cancellation/rescheduling, atomic rename watcher, snapshot
1923
+ capture on a later turn and pending memory restart without opening a consumer.
1924
+ - Whole-candidate JSON/TOML/unknown-key refusal, one error notice, per-key retention
1925
+ of valid unauthorized edits, automatic privacy tightening, scoped write/undo,
1926
+ fingerprint-bound guarded reload confirmation and agent-role request refusals.
1927
+
1928
+ To complete the native leg, use a disposable project from a person terminal (or
1929
+ an explicitly authorized per-invocation delegation under docs/security.md).
1930
+ Open `ahub console`, select Settings with 6, edit a safe switch, inspect its
1931
+ preview, enter a reason, confirm y and verify running/file values. Preview and
1932
+ confirm undo; verify the restored value through `ahub settings get`. Paste y at
1933
+ confirmation and verify no write. Change a sweep interval and confirm the same
1934
+ hub instance stays running with its queue and pauses intact. Record the source
1935
+ head, terminal and daemon readbacks separately before claiming AC5 native smoke.
1936
+
1937
+ For the guarded CLI leg, edit a disposable untracked WIP limit, run `ahub settings reload`, inspect its complete before -> after preview, decline and verify the running limit is retained. Repeat and confirm y, then verify the same instance and queue/pause state. Record this separately from the scripted CLI fixture; the token proves freshness within console authority, and the CLI supplies the person-presence checks.