@staix/agent-hub 0.12.45 → 0.13.0

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 (99) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/README.md +83 -116
  3. package/docs/agent-notes/README.md +6 -0
  4. package/docs/agent-notes/adapters.md +10 -7
  5. package/docs/agent-notes/budget.md +6 -1
  6. package/docs/agent-notes/bus.md +6 -4
  7. package/docs/agent-notes/daemon.md +6 -1
  8. package/docs/agent-notes/local-worker.md +2 -1
  9. package/docs/agent-notes/tasks.md +10 -0
  10. package/docs/agent-notes/tests.md +4 -2
  11. package/docs/dogfood.md +92 -0
  12. package/docs/events.md +14 -1
  13. package/docs/operations.md +250 -19
  14. package/docs/profiles.md +2 -2
  15. package/docs/quickstart.md +8 -2
  16. package/docs/sdlc.md +36 -6
  17. package/docs/security.md +20 -11
  18. package/docs/smoke.md +9 -4
  19. package/docs/specs/2026-09-19-agent-hub-design.md +759 -8
  20. package/docs/verification/2026-10-19-reviewer-isolation.md +2 -0
  21. package/docs/verification/2026-10-22-code-facts.md +78 -0
  22. package/docs/verification/2026-10-22-codex-reviewer-isolation.md +47 -0
  23. package/docs/verification/2026-10-22-kimi-qwen-compat.md +79 -0
  24. package/docs/verification/2026-10-23-0.13.0.md +89 -0
  25. package/docs/verification/dogfood/2026-10-22.md +3109 -0
  26. package/docs/verified.json +14 -14
  27. package/package.json +1 -1
  28. package/plugins/agent-hub/.claude-plugin/plugin.json +1 -1
  29. package/plugins/agent-hub/server.js +613 -329
  30. package/src/adapters/agy/entry.ts +5 -4
  31. package/src/adapters/claude-channel.ts +20 -9
  32. package/src/adapters/codex-appserver.ts +18 -2
  33. package/src/adapters/codex-review-isolation.ts +12 -5
  34. package/src/adapters/compat.ts +140 -0
  35. package/src/adapters/definitions/builtin-metadata.ts +2 -1
  36. package/src/adapters/definitions/native-cli-doctor.ts +12 -5
  37. package/src/adapters/definitions/native-cli-entry-jcode.ts +9 -6
  38. package/src/adapters/definitions/native-cli-jcode-mcp.ts +9 -2
  39. package/src/adapters/definitions/native-cli-preview-common.ts +2 -0
  40. package/src/adapters/definitions/native-cli-preview-jcode.ts +8 -2
  41. package/src/adapters/definitions/native-cli-preview-qwen.ts +2 -1
  42. package/src/adapters/definitions/qwen-native.ts +7 -3
  43. package/src/adapters/definitions/qwen.ts +2 -1
  44. package/src/adapters/jcode.ts +72 -31
  45. package/src/adapters/kimi-review.ts +6 -3
  46. package/src/adapters/registry-types.ts +3 -1
  47. package/src/adapters/registry.ts +1 -0
  48. package/src/cli/agents.ts +40 -0
  49. package/src/cli/console-review.ts +54 -0
  50. package/src/cli/console-state.ts +124 -29
  51. package/src/cli/console-symbols.ts +74 -0
  52. package/src/cli/console-usage.ts +89 -0
  53. package/src/cli/console.ts +26 -1
  54. package/src/cli/help.ts +16 -6
  55. package/src/cli/identity.ts +29 -6
  56. package/src/cli/init.ts +109 -11
  57. package/src/cli/main.ts +122 -11
  58. package/src/cli/output.ts +44 -1
  59. package/src/cli/preview.ts +1 -1
  60. package/src/cli/sdlc-release-guard.ts +82 -0
  61. package/src/hub/action-class.ts +362 -0
  62. package/src/hub/action-policy.ts +106 -0
  63. package/src/hub/board.ts +43 -8
  64. package/src/hub/budget.ts +71 -2
  65. package/src/hub/bus.ts +43 -3
  66. package/src/hub/checks.ts +80 -31
  67. package/src/hub/code-facts.ts +223 -0
  68. package/src/hub/config-apply.ts +10 -1
  69. package/src/hub/config-trust.ts +7 -4
  70. package/src/hub/daemon.ts +237 -22
  71. package/src/hub/dogfood-read.ts +199 -0
  72. package/src/hub/dogfood.ts +163 -0
  73. package/src/hub/envelope.ts +1 -1
  74. package/src/hub/events.ts +11 -2
  75. package/src/hub/hub-tools.ts +24 -2
  76. package/src/hub/inbox-polling.ts +25 -0
  77. package/src/hub/instances.ts +73 -1
  78. package/src/hub/profiles.ts +7 -2
  79. package/src/hub/release-worktree.ts +56 -0
  80. package/src/hub/review-brief.ts +36 -0
  81. package/src/hub/sdlc-queue.ts +31 -9
  82. package/src/hub/sdlc-release-authorization.ts +173 -0
  83. package/src/hub/sdlc.ts +49 -11
  84. package/src/hub/settings.ts +5 -2
  85. package/src/hub/snapshots.ts +93 -8
  86. package/src/hub/symbol-normalizer.ts +26 -0
  87. package/src/hub/task-code-symbols.ts +90 -0
  88. package/src/hub/task-sweep.ts +3 -2
  89. package/src/hub/tasks.ts +292 -54
  90. package/src/hub/test-integrity.ts +162 -0
  91. package/src/hub/usage-rollup.ts +361 -0
  92. package/src/local/sandbox.ts +5 -3
  93. package/src/ui/index.html +1 -0
  94. package/templates/AGENTS.block.md +1 -1
  95. package/templates/profiles/dev.json +6 -1
  96. package/templates/profiles/general.json +6 -1
  97. package/templates/profiles/research.json +6 -1
  98. package/templates/profiles/writing.json +6 -1
  99. package/templates/release-guard.block.md +6 -0
package/docs/security.md CHANGED
@@ -7,8 +7,8 @@ agent-hub connects agents that can each run commands. This page says what the hu
7
7
  - **Other agents' text is untrusted.** Every message that crosses from one peer to another is framed as untrusted input: a channel tag with `meta.source` for Claude started with `ahub claude`; otherwise a fixed header line (`frame()`, `[agent-hub message from ...]`) with an instruction that says it is untrusted, the plugin's server instructions for a tools-only Claude reading `hub_inbox` and a standing instruction for the others. A message body cannot forge the hub's own headers: such lines are quoted (`sanitize`). Replies inherit a hop count capped at 3, so agents cannot ping-pong forever; neither a digest nor a steer can reset it.
8
8
  - **The control link is loopback plus a secret.** The daemon and the Codex proxy bind 127.0.0.1 only. The control WebSocket requires a per-run token (`.agenthub/state/control-token`, mode 600), and both servers refuse any request that carries an `Origin` header: any web page can open a WebSocket to localhost, and browsers always send `Origin`. The bearer header a Codex TUI sends to the proxy carries only the terminal launch id (#586): it binds terminal ownership and is not a credential. External clients cannot claim the console user's id or a hub-managed peer's id.
9
9
  - **Permission prompts stay on unless an operator selects a permission mode.** `ask` leaves native decisions unchanged. `ask-when-needed` grants scoped Claude file tools or Pi/local writes and edits; Kimi and Codex use their own native policies. `never-ask` requires explicit runtime confirmation or a person's console `y` for a config default at hub start. Until that config confirmation the peer runs in `ask`. Kimi's and `local`'s permission requests are relayed to the console and cancelled after `approvals.timeout_s` (default 120 s) of silence; the macOS notification for a waiting request carries the peer and the tool name only. The one exception is the hub's own tools (`hub_send` and the task tools, matched by exact name): Kimi's requests for them are approved once without a prompt and logged by name, the same trust Codex gets through `approval_mode` in the hub's config. They invoke hub-owned operations rather than arbitrary file or shell tools, and every call passes the hub's own checks. Identity comes from the exact permission title, or from the earlier tool-call title bound to the same call id and resolved against the configured MCP servers when the permission title contains argument JSON (Qwen). Payload text and unrelated display titles never establish identity. Configured ACP kinds (#451) never get this exception: a request whose title spells a hub tool exactly still goes to the person approval path, and only a mapped native mode changes what the agent itself asks. For ACP agents, a payload longer than the console shows is marked as cut and never offers a session-wide grant. Pi's write, edit, shell and git write requests are relayed the same way and can be answered `Always allow <tool> until Pi restarts`: later calls of that tool by the same Pi start skip the prompt but stay inside the path guard and sandbox. The grant covers every later call of that tool whatever its payload, is kept in memory only, cannot be given from the dashboard, and each call it allows is logged by tool name. A Pi or `local` request whose call was cancelled or whose turn ended is withdrawn at once: a later answer is refused and grants nothing, an `Always allow` included. One shell command runs without a hub approval: the `!command` a person types in the managed Pi TUI. It stays inside the same sandbox and denylist, and the hub takes it only from the verified owner process of a Pi in TUI mode, while Pi is idle, on a reservation that is used once; a model's own bash call never takes that path (issue #253). `--unattended` turns prompts off, says so loudly, and is never the default.
10
- - **A committed config cannot choose launch commands, credential files, data endpoints, permission modes or a wider sandbox.** The machine-local fields (`kimi_cmd`, `permission_modes`, `codex_bin`, `pi.cmd`, `checks`, `mlx.bin`, `mlx.runtimeDir`, `mlx.modelPath`, `omniroute.urls`, `omniroute.access_hosts`, the `omniroute` key files, `memory.worker_url`, `local.read_allow`, `local.bash_network`, `local.network_allow`, `terminal.open`, `instances`) apply only from a config file git confirms nobody committed (`instances` is read only from an untracked `.agenthub/config.local.json`; in `config.json` it is ignored even when untracked): `.agenthub/config.json` or `.agenthub/config.local.json`, matched by file identity so no other spelling the file system accepts slips past, and `.agenthub` itself not a committed symlink or submodule. Without a repository, or when git fails, they keep their defaults; an empty value always means the default. "Untracked" is answered by the repository that contains the project: a checkout copied or extracted into an unrelated repository, or into an ignored directory of one, is trusted like your own files. So a cloned repository cannot choose a launch command, a completion check, a gateway to send a key file to, a memory endpoint, a permission mode, the command that opens a terminal, or a wider sandbox. A command in `checks` runs as you, outside the local worker's sandbox, like a git hook. So do the `setup` and `verify` commands of a benchmark suite (issue #251): `ahub bench run <suite.json>` is a person's command, refused from an agent session, and it resets the work tree for each attempt, so it runs only with `bench.enabled`, at the root of a clean repository that tracks nothing under `.agenthub/`. Nothing in `routing.toml` or in task text is ever run. `routing.toml` and the other shared fields still come from the checkout, and they matter: `routing.toml` picks the models the local worker and the hub's inference use at your gateway and can turn the PII constraint off, and roles and budget shape who does what. Review them in a repository you do not trust. The hub's own git calls outside the sandbox (snapshots, facts, config trust, project discovery, memory recall, the bench runner) all use the single hardened implementation in the exact-pinned first-party `@staix/agent-mem/platform` package, re-exported by `src/hub/git.ts`, which disables the programs a repository's configuration can name — the file-system monitor, hooks, external diff, the pager, and the credential and askpass helpers, with prompts failing closed — so a config that ever does get written cannot run code with the hub's rights. Of those, the monitor, hooks and external diff are reachable by commands the hub issues (the planted-program test exercises all three; a `diff --no-index` reads the repo's config, so that call also passes `--no-ext-diff` and `--no-textconv`); the pager is not (every call pipes stdout). Credential and askpass helpers are disabled for project repositories; memory sync alone may use credentials for its own memory repository, without prompts. The cost: `core.fsmonitor=false` also turns off a person's enabled built-in monitor for the hub's own calls, so the per-turn snapshot's `add -A` scans the whole tree (the snapshot Git calls of one burst share a 250 ms budget, each call at most 10 s; a boundary that runs out is recorded as unknown, never as a stale baseline) (#576). git has no global switch for filter drivers, and of the hub's commands only the snapshot's `add`/`restore` would run one (its driver command lives in the protected `.git/config`).
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.
10
+ - **A committed config cannot choose launch commands, credential files, data endpoints, permission modes or a wider sandbox.** The machine-local fields (`kimi_cmd`, `permission_modes`, `codex_bin`, `pi.cmd`, `checks`, `mlx.bin`, `mlx.runtimeDir`, `mlx.modelPath`, `omniroute.urls`, `omniroute.access_hosts`, the `omniroute` key files, `memory.worker_url`, `local.read_allow`, `local.bash_network`, `local.network_allow`, `terminal.open`, `instances`, `action_policy`) apply only from a config file git confirms nobody committed (`instances` and `action_policy` are read only from an untracked `.agenthub/config.local.json`; in `config.json` they are ignored even when untracked): `.agenthub/config.json` or `.agenthub/config.local.json`, matched by file identity so no other spelling the file system accepts slips past, and `.agenthub` itself not a committed symlink or submodule. Without a repository, or when git fails, they keep their defaults; an empty value always means the default. "Untracked" is answered by the repository that contains the project: a checkout copied or extracted into an unrelated repository, or into an ignored directory of one, is trusted like your own files. So a cloned repository cannot choose a launch command, a completion check, a gateway to send a key file to, a memory endpoint, a permission mode, the command that opens a terminal, or a wider sandbox. A command in `checks` runs as you, outside the local worker's sandbox, like a git hook. So do the `setup` and `verify` commands of a benchmark suite (issue #251): `ahub bench run <suite.json>` is a person's command, refused from an agent session, and it resets the work tree for each attempt, so it runs only with `bench.enabled`, at the root of a clean repository that tracks nothing under `.agenthub/`. Nothing in `routing.toml` or in task text is ever run. `routing.toml` and the other shared fields still come from the checkout, and they matter: `routing.toml` picks the models the local worker and the hub's inference use at your gateway and can turn the PII constraint off, and roles and budget shape who does what. Review them in a repository you do not trust. The hub's own git calls outside the sandbox (snapshots, facts, config trust, project discovery, memory recall, the bench runner) all use the single hardened implementation in the exact-pinned first-party `@staix/agent-mem/platform` package, re-exported by `src/hub/git.ts`, which disables the programs a repository's configuration can name — the file-system monitor, hooks, external diff, the pager, and the credential and askpass helpers, with prompts failing closed — so a config that ever does get written cannot run code with the hub's rights. Of those, the monitor, hooks and external diff are reachable by commands the hub issues (the planted-program test exercises all three; a `diff --no-index` reads the repo's config, so that call also passes `--no-ext-diff` and `--no-textconv`); the pager is not (every call pipes stdout). Credential and askpass helpers are disabled for project repositories; memory sync alone may use credentials for its own memory repository, without prompts. The cost: `core.fsmonitor=false` also turns off a person's enabled built-in monitor for the hub's own calls, so the per-turn snapshot's `add -A` scans the whole tree (the snapshot Git calls of one burst share a 250 ms budget, each call at most 10 s; a boundary that runs out is recorded as unknown, never as a stale baseline) (#576). git has no global switch for filter drivers, and of the hub's commands only the snapshot's `add`/`restore` would run one (its driver command lives in the protected `.git/config`).
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. Budget pauses, resumes and quota resets carry the peer, window, times and the hub's own reason text, and a person's release authorization its commit, version and docs digest. The usage roll-up (#683) folds the usage, token and limit records into `hub.db` and joins issue, PR and release from the board when read; `ahub usage` and the console's Usage panel answer only the console and a configured planner, and a planner not during a private turn. 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
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.
@@ -59,6 +59,11 @@ is refused before write/edit effects.
59
59
  Kimi executes its own tools outside that sandbox; Claude and Codex retain their
60
60
  vendor boundaries. The never-ask confirmation names this difference. No mode
61
61
  turns off the sandbox or grants a session-wide allow-always option.
62
+ The always-ask action policy (#636) is a foundation only: its classifier and
63
+ session budgets exist, `action_policy` is read only from an untracked
64
+ `.agenthub/config.local.json`, and no adapter enforces it yet, so it adds no
65
+ protection; each peer's status says `always-ask not enforceable: adapter
66
+ integration pending`.
62
67
 
63
68
  Config defaults apply automatically only up to ask-when-needed. A never-ask
64
69
  request from an applied untracked config is held as ask until the person
@@ -86,7 +91,7 @@ as that peer in tools mode. Messages and task changes retain that actor;
86
91
  `say` defaults to status priority and important messages still require the peer's
87
92
  capability. Conflicting or malformed markers fail closed. Refusals name only the marker variable names actually present, never their values. There is no `--as-user`.
88
93
  By default, console operations are refused before connecting, including permission answers
89
- and permission modes, queue resolution, budget overrides, benchmark runs, research
94
+ and permission modes, queue resolution and bulk discard, agent version trust, budget overrides, benchmark runs, research
90
95
  backfill and task labels, stopping a peer (`ahub stop`), lifecycle and recovery
91
96
  operations, and `ask`.
92
97
  For live verification explicitly authorized by the user, an agent may invoke
@@ -112,7 +117,7 @@ and flag. The current daemon emits `agent_cli` with `delegated: true` and
112
117
  `refused: false` for the negotiated outcome. Allowed-class commands retain the peer identity and capability checks, including
113
118
  messages, proposals and reviews. Peer starts, pause and resume may delegate
114
119
  operational console authority; task assign/escalate under the flag refuse. `--as-user`, person task labels,
115
- person-authored planning/release/memory/goal writes, SDLC record, spec and plan-departure acceptance, benchmark run,
120
+ person-authored planning/release/memory/goal writes, SDLC record, spec and plan-departure acceptance, release authorization, agent version trust, benchmark run,
116
121
  `ahub recovery reattach`, a `--fresh-session` dispose that would replace a repaired
117
122
  session's conversation (or whose scope cannot be read), and the pet
118
123
  composer are refused; presentation flags such as `--color` or `--verbose` before
@@ -171,7 +176,7 @@ Please report vulnerabilities privately through GitHub's "Report a vulnerability
171
176
  - The exchange creates a separate random session, valid for one hour without renewal, in an `HttpOnly; SameSite=Strict; Path=/` cookie named for this listener's port. Sessions and tickets live only in memory and die with the daemon. The page itself keeps one more cookie, `agent-hub-theme` (issue #246): script-set, `SameSite=Strict`, kept for a year for the whole `127.0.0.1` host, and read back only as one of `system`, `light` or `dark`. A second script-set cookie, `agent-hub-office` (issue #264), keeps the Office view choice under the same attributes and is read back only as `off`, `flat` or `isometric`. The #391 view split adds two more script-set cookies under the same attributes: `agent-hub-view`, read back only as one of the seven view names, and `agent-hub-column-focus`, read back only as one of the five board columns or empty. HTTP is loopback-only; the cookie is not a substitute for the origin checks.
172
177
  - 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.
173
178
  - 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.
174
- - 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.
179
+ - The browser receives a bounded redacted event stream, peer states and queue counts, public task views (with a task's screened changed-symbol observation, #684: ids, paths, names and kinds, never source or line spans), 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.
175
180
  - 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.
176
181
  - The listener stops with the daemon. Expired sessions must be reopened with `ahub ui`; the page does not silently obtain new credentials.
177
182
 
@@ -246,16 +251,18 @@ live after a settings write, undo or reload (turning it off from an edited file
246
251
  be set through an agent, tools or conductor surface. Claude's daemon response
247
252
  withholds the scoped mode on these writes so an older hook grants nothing too.
248
253
 
249
- Qwen Code's optional production ACP peer requires qwen-code 0.25.0 native identity
254
+ Agent CLI versions (#691). One policy gates the Codex and Kimi fresh reviewers, Jcode, agy and Qwen: a receipt-verified version passes, Jcode and agy also start an untested version inside their compatible line with a note, and any other version is refused unless the person trusts that exact version on this machine with `ahub agents trust <id> <version>`. The record is `agent-compat.json` in the hub home (`~/.agenthub`), written atomically with mode 600 and at most 64 entries; an unreadable, linked, oversized or invalid record trusts nothing. Agent shells and `--live-test` may read `ahub agents` but cannot trust or revoke. Trust lifts only the version pin: the fresh reviewers' runtime proofs still run before every child, and an untested version's native contract stays unverified.
255
+
256
+ Qwen Code's optional production ACP peer requires the qwen-code native identity, a compat-admitted version (receipt-verified 0.25.0 and 0.26.0)
250
257
  and advertised mode ids before setting a policy or starting work. Its own tools
251
258
  have no hub sandbox; auto-edit approves file edits, yolo every tool. Configured
252
259
  never-ask still requires the person's confirmation. qwen_cmd is machine-local and
253
260
  never editable through settings. Legacy benchmark adapters remain unmanaged by
254
261
  this production table. Startup diagnostics withhold native host/payload details.
255
262
 
256
- Jcode is optional, source/version-fenced and runs native tools without a hub sandbox or hub permission-mode mapping. .jcode joins the scoped-grant exclusion, including lexical/resolved aliases. Only a person writes its unique project MCP entry; foreign/override/symlink entries refuse. The hub owns a private daemon/socket and ACP group, never the global daemon, and copies no credentials. It accepts only a plain jcode release from 0.98.0 up to, not including, 1.0.0 (#529), and runs every owned jcode process (probe, serve, ACP and TUI) with `JCODE_NO_TELEMETRY=1`, because jcode's telemetry can report the provider account's email. Native diagnostics withhold payloads/hosts. Unproved conversation loads refuse; no fresh replacement is called a restore. A finished task's advisory about the project root checkout names the branch only after the hub's whole public-text screen (`Tasks.publicMetadataText`: a PII pattern, a secret shape and a project-private path) passes it and it is a plain ref name; otherwise a generic sentence carries no part of it (#572). Native TUI launch exposes its private socket/session tuple only to the authenticated console, bound to the same live owned socket and session after read-only native persistence proof. Tools/status do not receive it. The TUI has no single-writer guard: person input may interleave with ACP. Native policy remains unmanaged and closing the frontend does not stop ACP. An `important` message reaches a busy Jcode as a soft interrupt written to the hub's own private server socket for the owned session (#572); the hub never cancels a turn to deliver, and a steer that was sent but not acknowledged, or whose turn ended first, is held as `needs_review`, never replayed. Jcode has no switch to choose its MCP servers, so the owned serve and ACP processes load every server named in `<JCODE_HOME>/mcp.json` and the project's `.jcode/mcp.json`, `.mcp.json` and `.claude/mcp.json`, besides the hub's own; `ahub doctor` lists their names only. Point `jcode_home` at a separate home, or remove a server from those files, to keep it out.
263
+ Jcode is optional, source/version-fenced and runs native tools without a hub sandbox or hub permission-mode mapping. .jcode joins the scoped-grant exclusion, including lexical/resolved aliases. Only a person writes its unique project MCP entry; foreign/override/symlink entries refuse. The hub owns a private daemon/socket and ACP group, never the global daemon, and copies no credentials. It accepts a plain jcode release from 0.98.0 up to, not including, 1.0.0 (#529; only 0.98.1 and 0.99.0 are verified, and another version in that line starts with a note), or an exact version the person trusts (#691), and runs every owned jcode process (probe, serve, ACP and TUI) with `JCODE_NO_TELEMETRY=1`, because jcode's telemetry can report the provider account's email. Native diagnostics withhold payloads/hosts. Hub tools are bound to an owned launch (#368): the shared project entry names no peer, each owned Jcode process carries its own peer id and launch id, the daemon admits a hub-tools connection for a Jcode peer only with the id of that peer's current owned launch, and a tools-mode server started without a peer id refuses every tool. Extra Jcode instances (#685, at most 8) are declared only through `instances` in an untracked `.agenthub/config.local.json`, start headless, and each needs its own `jcode_home`, which may not be the base home or another instance's, nor nested in or around one, however it is spelled. Unproved conversation loads refuse; no fresh replacement is called a restore. A finished task's advisory about the project root checkout names the branch only after the hub's whole public-text screen (`Tasks.publicMetadataText`: a PII pattern, a secret shape and a project-private path) passes it and it is a plain ref name; otherwise a generic sentence carries no part of it (#572). Native TUI launch exposes its private socket/session tuple only to the authenticated console, bound to the same live owned socket and session after read-only native persistence proof. Tools/status do not receive it. The TUI has no single-writer guard: person input may interleave with ACP. Native policy remains unmanaged and closing the frontend does not stop ACP. An `important` message reaches a busy Jcode as a soft interrupt written to the hub's own private server socket for the owned session (#572); the hub never cancels a turn to deliver, and a steer that was sent but not acknowledged, or whose turn ended first, is held as `needs_review`, never replayed. Jcode has no switch to choose its MCP servers, so the owned serve and ACP processes load every server named in `<JCODE_HOME>/mcp.json` and the project's `.jcode/mcp.json`, `.mcp.json` and `.claude/mcp.json`, besides the hub's own; `ahub doctor` lists their names only. Point `jcode_home` at a separate home, or remove a server from those files, to keep it out.
257
264
 
258
- Antigravity (`agy`, #485) is optional, cloud and off roster by default. The launcher accepts the Antigravity CLI 1.4.x line; only 1.4.2 has a native receipt, so another 1.4.x patch launches with a warning that it is unverified, and every other version is refused before a hub connection or plugin write (#589). It runs native tools under its own unmanaged permissions, without a hub sandbox or hub permission-mode mapping; headless, resume, unattended and native permission flags are refused. Its roles are capped at implementer and verifier, so it never plans, reviews or receives PII work. Only the person's `ahub agy` launcher writes its three plugin files in `.agents/plugins/agent-hub/` (`plugin.json`, `mcp_config.json`, `hooks.json`): linked or foreign entries refuse before any write, cleanup removes only that launch's matching files, and it never writes project-level `.agents/hooks.json` or the person's `~/.gemini` settings. Conductor starts are refused, and a declared agy extra instance is refused at config load (#625). Hook authority (`native_hook_launch`) comes only from that launcher, belongs to the console socket that began it and ends when that socket closes; hook input over 1 MiB is refused, and observations are bound to the daemon, launch, native session and project.
265
+ Antigravity (`agy`, #485) is optional, cloud and off roster by default. The launcher accepts the Antigravity CLI 1.4.x line; only 1.4.2 has a native receipt, so another 1.4.x patch launches with a warning that it is unverified, and every other version is refused before a hub connection or plugin write (#589) unless the person trusts that exact version (#691). It runs native tools under its own unmanaged permissions, without a hub sandbox or hub permission-mode mapping; headless, resume, unattended and native permission flags are refused. Its roles are capped at implementer and verifier, so it never plans, reviews or receives PII work. Only the person's `ahub agy` launcher writes its three plugin files in `.agents/plugins/agent-hub/` (`plugin.json`, `mcp_config.json`, `hooks.json`): linked or foreign entries refuse before any write, cleanup removes only that launch's matching files, and it never writes project-level `.agents/hooks.json` or the person's `~/.gemini` settings. Conductor starts are refused, and a declared agy extra instance is refused at config load (#625). Hook authority (`native_hook_launch`) comes only from that launcher, belongs to the console socket that began it and ends when that socket closes; hook input over 1 MiB is refused, and observations are bound to the daemon, launch, native session and project.
259
266
 
260
267
  Configured ACP adapters (#451 A3) are declared only by the person, in a Git-confirmed untracked `.agenthub/config.local.json`. The whole declaration (command, argv, identity, roles and mode maps) is machine-local authority: a shared or tracked declaration is ignored with a notice, settings never edit it, and changes need a hub restart. The hub runs only the declared argv, never a module or callback. Argv that selects a leader, attach, connect or remote mode is refused, and a Grok declaration needs `--no-leader`. Each start requires the pinned native identity, an exact `session/load` echo on resume, and positive ownership of the spawned process and its original process group; unknown ownership refuses, and stop signals only that verified group. An agent that hands its work to a process outside that group is outside this boundary. Without a mode map native policy is unmanaged, and there is no hub sandbox either way. Configured kinds are cloud peers (never PII), headless only and off roster by default, and the hub makes no quota, context or controlled-recovery claims for them. A read-only instance needs the native read-only mode acknowledged before a prompt and is limited to read and review hub tools, both in the published MCP list and at the host; its native non-read tool requests are rejected, and no setting or runtime change can raise it. These agents' stderr and error text are withheld from logs, and previews redact the executable, argv and session id.
261
268
 
@@ -364,11 +371,13 @@ Stall causes are persisted and deduplicated by cause and replanning revision. Pl
364
371
 
365
372
  A declared instance whose only role is reviewer gets review requests, task messages addressed to it and the person's messages; agent chat, digests and presence are discarded and journaled, and it gets no memory recall (#223). The headless Claude reviewer runs one new `claude -p` per review with `--setting-sources ""`, `--strict-mcp-config`, `--no-session-persistence`, tools limited to Read, Grep, Glob, `hub_review` and `hub_task_show`, and `dontAsk`. Its prompt holds hub facts and a bounded diff, never the builder's summary, and its `hub_task_show` drops the builder's notes. Changes touching `local.deny` or `pii.paths` are withheld and passed as `Read` deny rules, which Claude enforces, not the hub. Private tasks are never given to it. Its `hub_review` counts only while the child started for that task still runs; a stop or exit revokes it first, and surviving processes hold the delivery as `needs_review` and keep the reviewer busy.
366
373
 
367
- The fresh Codex reviewer (#519) needs `review_mode: "fresh"` and an installed Codex 0.146.0; any other version is unavailable. Before each review it requires a model-free isolation receipt: binary and config unchanged, only the two hub tools in the configured MCP inventory, plugins, hooks and Apps disabled (Apps by a separate feature readback), and a named filesystem profile whose fixtures prove public reads, private-path denial (exact, glob and symlink), standard host credential denial and write denial. Credential and tool-home roots are denied even with an empty `local.deny`; that list does not cover every personal file. A dangling or unreadable credential root refuses the reviewer, and the reason names only its path class (`~/.docker`, `$DOCKER_CONFIG`), never the path or its contents. Its shell is sandboxed read-only, not Claude's Read/Grep/Glob whitelist. Network and web search are configured disabled but not observed: managed requirements can still select cached web search, and no run has shown the review selecting the profile through `default_permissions`. A failed receipt or start never falls back to a continuing native session, and a child that ends without a saved verdict is never approval. The agy reviewer slot is an unavailable placeholder and never runs.
374
+ The fresh Codex reviewer (#519) needs `review_mode: "fresh"` and a compat-admitted Codex (receipt-verified 0.146.0 and 0.163.0; `ahub agents` shows the current policy); an unverified/untrusted version is unavailable. Before each review it requires a model-free isolation receipt: binary and config unchanged, only the two hub tools in the configured MCP inventory, plugins, hooks and Apps disabled (Apps by a separate feature readback), and a named filesystem profile whose fixtures prove public reads, private-path denial (exact, glob and symlink), standard host credential denial and write denial. Credential and tool-home roots are denied even with an empty `local.deny`; that list does not cover every personal file. A dangling or unreadable credential root refuses the reviewer, and the reason names only its path class (`~/.docker`, `$DOCKER_CONFIG`), never the path or its contents. Its shell is sandboxed read-only, not Claude's Read/Grep/Glob whitelist. Network and web search are configured disabled but not observed: managed requirements can still select cached web search, and no run has shown the review selecting the profile through `default_permissions`. A failed receipt or start never falls back to a continuing native session, and a child that ends without a saved verdict is never approval. The agy reviewer slot is an unavailable placeholder and never runs.
375
+
376
+ The fresh Kimi reviewer (#533) needs `review_mode: "fresh"` and a compat-admitted Kimi Code (receipt-verified 2.2.0 and 2.3.0; `ahub agents` shows the current policy) with its native OAuth login; an unverified/untrusted version, or a failed preparation, is unavailable. The [2.3.0 receipt](verification/2026-10-22-kimi-qwen-compat.md) is model-free and does not prove real login or a model review. It is a native tool-authority boundary, not a sandbox: no Seatbelt wraps it, and same-user native code is not isolated from the filesystem or network. Each review starts a new child in a private `KIMI_CODE_HOME` with managed-provider-only config, a clean temporary working directory and an empty skills directory. Its agent may use only `hub_review` and `hub_task_show`, served by a loopback MCP session bound to the task, head and review revision that never carries the hub's control token; it has no native read, file or shell tool and sees only the screened review brief and host facts. Its `credentials/` and `oauth/` directories and `device_id` link to the person's native ones, so refresh keeps Kimi's own lock and atomic rename; no token is copied and `KIMI_DISABLE_OAUTH_LOCK` is never set. A stop revokes its verdict authority before termination; killing it mid-refresh may require a native re-login. Private tasks are never given to it, and a child that ends without a saved verdict is never approval.
368
377
 
369
- The fresh Kimi reviewer (#533) needs `review_mode: "fresh"` and Kimi Code 2.2.0 with its native OAuth login; any other version, or a failed preparation, is unavailable. It is a native tool-authority boundary, not a sandbox: no Seatbelt wraps it, and same-user native code is not isolated from the filesystem or network. Each review starts a new child in a private `KIMI_CODE_HOME` with managed-provider-only config, a clean temporary working directory and an empty skills directory. Its agent may use only `hub_review` and `hub_task_show`, served by a loopback MCP session bound to the task, head and review revision that never carries the hub's control token; it has no native read, file or shell tool and sees only the screened review brief and host facts. Its `credentials/` and `oauth/` directories and `device_id` link to the person's native ones, so refresh keeps Kimi's own lock and atomic rename; no token is copied and `KIMI_DISABLE_OAUTH_LOCK` is never set. A stop revokes its verdict authority before termination; killing it mid-refresh may require a native re-login. Private tasks are never given to it, and a child that ends without a saved verdict is never approval.
378
+ The review brief (#520) withholds private changes whole (both sides of a move) and counts them; a rule file that fails the private-data screen is named, not shown. Attributes and rules are read at the merge base, so a change cannot loosen its own review. Optional changed symbols (#684) come from ripwire 0.6.7 on the daemon's host PATH, run as an owned process on a disposable snapshot of the task's commit; each path, name and kind passes the hub's public metadata screen before the brief, the console's task detail or the dashboard shows it, a private task stores and shows none, and a failed or missing read is `unknown`, never a gate. Spec acceptance, plan review, plan conformance, premature-review, cost, test-integrity (#481 S4), issue and PR identity linking (#694) and dogfood scorecard (#687) reads fetch GitHub metadata only (edit times, check states, thread authorship and resolution, changed file names, open PR heads, workflow run conclusions), never issue bodies, comments or patches. The CI workflow keeps `contents: read` and `actions: read`.
370
379
 
371
- The review brief (#520) withholds private changes whole (both sides of a move) and counts them; a rule file that fails the private-data screen is named, not shown. Attributes and rules are read at the merge base, so a change cannot loosen its own review. Spec acceptance, plan review, plan conformance, premature-review and cost reads fetch GitHub metadata only (edit times, check states, thread authorship and resolution, changed file names), never issue bodies, comments or patches. The CI workflow keeps `contents: read` and `actions: read`.
380
+ Person release authorization (#481 S6). `ahub sdlc authorize-release <version>` is taken from the console role only and refused under `--live-test`. It binds the daemon checkout's commit, package version, push remote identity and a fresh-docs verdict, lives only in daemon memory (a restart revokes it) and is logged with its commit, version and docs digest. `ahub sdlc release-guard -- <argv>` is a read-only preflight that agents may run, except during a private or PII turn; it never executes the command and refuses ambient target selectors (`GIT_DIR`, `GH_REPO`, npm prefix and registry settings and the like). The pre-push hook that `ahub init --release-guard` installs refuses an unauthorized or unknown `v*` tag push, also with the daemon down, and passes other pushes through. It and the release block in `AGENTS.md` are a cooperative guard, not an operating-system boundary: `git push --no-verify`, a custom `core.hooksPath` or another Git invocation bypasses the hook, and `gh release create` and `npm publish` are checked only when the preflight is run (#312, #313).
372
381
 
373
382
  ## Bounded task specs and isolated checks (#418)
374
383
 
package/docs/smoke.md CHANGED
@@ -1697,7 +1697,7 @@ Source is complete: head-bound gates (#333), gsd-lite checklists (#336), bounded
1697
1697
 
1698
1698
  Unverified (tracked in #273): one real issue on this repository taken through merge and release under `sdlc.mode = "advise"`. Record the issue, PR, heads, each gate's blocker readback and `ahub sdlc release` output here. No live cycle or automatic merge is claimed.
1699
1699
 
1700
- ## Qwen Code 0.25.0 production peer (#256)
1700
+ ## Qwen Code production peer (#256)
1701
1701
 
1702
1702
  Unverified: real provider handshake, a native model/tool turn, context occupancy,
1703
1703
  owned stop/recovery and the first authoritative quota refusal. Fake ACP tests are
@@ -1713,6 +1713,11 @@ Qwen recovery source/fake proof: saved ids pass through the driver and CLI to AC
1713
1713
 
1714
1714
  Source/fixture proof is recorded in verification/2026-10-11-jcode-peer-contract.md; the live owned-daemon and ACP handshake on 0.94.0 and 0.98.1 in verification/2026-10-19-jcode-0.98.1.md. Unverified: real provider/model, native project MCP discovery and hub-tool receipt, native tool policy and stop/child teardown. Native automatic restoration is withheld because the pinned ACP load does not prove the id. No benchmark comparison or default replacement ran.
1715
1715
 
1716
+ ### Extra Jcode instances (#685, #368)
1717
+
1718
+ Source and fixture coverage: `test/jcode-instances.test.ts` (declaration refusals, the bound of 8, home uniqueness, no relay or credential fields), `test/jcode-instance-peer.test.ts` (separate groups, sockets and homes, stopping one leaves the other, argv is the base argv plus the instance's `-p`, `--model` and `--provider-profile`, launch id per owned process, launcher, doctor) and `test/jcode-instance-daemon.test.ts` (home preflight, no file written under the home, status, hub-tools admission only for an owned launch, a forged `AGENTHUB_PEER_ID` refused, the MCP server with no peer id refusing every tool).
1719
+
1720
+ Unverified, a person's leg: sign in to Jcode against each instance's own `jcode_home` (including an OpenAI-compatible profile for the on-prem gateway), declare the instances in `.agenthub/config.local.json`, run `ahub jcode --as <id>`, send each one a task, and read back that `ahub status` lists each with its provider, model and profile, that `ahub stop <id>` leaves the others running, and that a Jcode started by hand in the project reports that its hub tools are refused. Observed on the real jcode 0.99.0 binary (#368, isolated `HOME` and `JCODE_HOME`, own `jcode serve` socket and `jcode acp --mcp-tools eager`, a probe MCP entry that names neither id): its MCP child received `AGENTHUB_PEER_ID` and `AGENTHUB_LAUNCH_ID` from the owned process env, next to the entry's own variables. An `acp` that attaches to an already running shared server instead hands its child that server's env, which is why the hub always starts its own private serve first. Then the whole path on the real binary (#368, isolated `HOME` and `JCODE_HOME`, a real daemon, the committed `plugins/agent-hub/server.js` as the MCP child, an extra instance `jcode-local` with a declared home): the project entry named no peer, `start` returned ok, the peer reached `idle`, and the one bundle child under that Jcode ran with `AGENTHUB_PEER_ID=jcode-local`, `AGENTHUB_MODE=tools` and its own `AGENTHUB_LAUNCH_ID` and held one established TCP connection to the hub, which the daemon admitted only because of that launch id. A first attempt with a profile name that Jcode did not know failed the start with the bounded refusal and left no process. Still not exercised: any real Jcode account, provider or model, a model turn that calls a hub tool, and a second instance running beside the base peer under the real binary.
1716
1721
 
1717
1722
  ## Project board (#263)
1718
1723
 
@@ -1948,16 +1953,16 @@ Complete these legs in a disposable project with authorized accounts after sourc
1948
1953
  - Request a delta review and confirm the same eligible independent reviewing family receives a new session with prior findings. After an earlier same-family fallback, a newly available independent family still takes precedence. Exercise configured re-rotation separately. Stop or exhaust one reviewer and confirm the pure routing walk chooses the next eligible candidate; no failed start or child ending without a saved verdict becomes approval. Check the next candidate even when the missing verdict followed a vendor quota error; no text-based quota classifier is assumed.
1949
1954
  - Make all pool candidates unavailable and confirm a blocked review awaiting planner/person action. Then test a different fresh session in same-family fallback only when no other family can run. Verify stale-child, changed-head and private-review verdict refusals.
1950
1955
  - Verify compatibility separately: a Codex/Kimi headless reviewer-role declaration without `review_mode` keeps its native app-server/ACP behavior and existing worktree/permission options, and stays outside the fresh pool. An explicit fresh declaration must refuse an unsupported backend without downgrading.
1951
- - The default logical pool is Claude, Codex, Kimi. Kimi is runnable only for covered 2.2.0 when runtime preparation passes; unsupported or failed preparation is unavailable with its reason. Seed modulo includes only kinds with a declared candidate, even when blocked; undeclared kinds remain trailing trace entries. Verify seeds 0..5 with only Claude/Codex declared alternate 3/3, and an offline declared kind still retains its slot. Agy stays unavailable as a reviewer with exact reason `agy is an implementer peer (#485)`, including explicit pool requests. #234 adds no agy reviewer launch or live leg.
1956
+ - The default logical pool is Claude, Codex, Kimi. Kimi is runnable for a compat-admitted version when runtime preparation passes (receipt-verified 2.2.0 and 2.3.0; inspect `ahub agents`); an unverified/untrusted version or failed preparation is unavailable with its reason. Seed modulo includes only kinds with a declared candidate, even when blocked; undeclared kinds remain trailing trace entries. Verify seeds 0..5 with only Claude/Codex declared alternate 3/3, and an offline declared kind still retains its slot. Agy stays unavailable as a reviewer with exact reason `agy is an implementer peer (#485)`, including explicit pool requests. #234 adds no agy reviewer launch or live leg.
1952
1957
 
1953
1958
  Record source head, exact executable version/hash, isolation receipt, task/head identities, route walk, distinct native session evidence and persisted verdict separately. Do not record credentials or raw transport environments. Codex sandboxed shell must not be described as Claude's Read/Grep/Glob-only whitelist. Network policy is configured disabled; no network-denial probe or native LLM isolation result has been recorded by this investigation.
1954
1959
 
1955
1960
  ## Kimi reviewer and task-bound MCP (#533)
1956
1961
 
1957
- Kimi 2.2.0 is available when runtime preparation passes; a failed or unsupported preparation remains unavailable. Completed evidence includes account-free two-tool/native OAuth race checks, host/fake lifecycle refusals and authenticated fresh `changes_requested` verdicts. The complete-collector live run observed project parity/canary absence, authenticated exact-two hub inventory/calls, zero decoy requests, cleanup and unchanged real config hashes. Its original aggregate receipt remains failed because of an unattributed extra observer error; the later account-free GET reproduction/correction is not a new live pass. These separate outcomes are preserved in the R0/live report. The remaining actual interactive-TUI/coexistence and usage-observation checks are **UNVERIFIED**; they do not turn completed live/fake evidence into an unrun test. Person ruling 2026-10-19 (8281ec35) limits #234 to Kimi and task-bound MCP and supersedes all earlier agy reviewer implementation/admission/live-leg rulings. Agy is an implementer under #485; its retained reviewer slot remains unavailable with exact reason `agy is an implementer peer (#485)`. Earlier agy probes and unresolved meta-tool/resource findings remain historical in the [R0 report](verification/2026-10-19-kimi-agy-reviewer-isolation.md), not active smoke requirements here. Kimi's native-permission/shared-account ruling supersedes its earlier R0-only hold and Seatbelt requirement. The [spec amendment](specs/2026-09-19-agent-hub-design.md#kimi-reviewer-and-task-bound-mcp-amendment-533-hub-task-234) defines the active contract and residuals.
1962
+ Kimi receipt-verified 2.2.0 and 2.3.0 are available when runtime preparation passes; a failed or unsupported preparation remains unavailable. The [2.3.0 receipt](verification/2026-10-22-kimi-qwen-compat.md) is model-free; the following authenticated evidence belongs to 2.2.0. Completed evidence includes account-free two-tool/native OAuth race checks, host/fake lifecycle refusals and authenticated fresh `changes_requested` verdicts. The complete-collector live run observed project parity/canary absence, authenticated exact-two hub inventory/calls, zero decoy requests, cleanup and unchanged real config hashes. Its original aggregate receipt remains failed because of an unattributed extra observer error; the later account-free GET reproduction/correction is not a new live pass. These separate outcomes are preserved in the R0/live report. The remaining actual interactive-TUI/coexistence and usage-observation checks are **UNVERIFIED**; they do not turn completed live/fake evidence into an unrun test. Person ruling 2026-10-19 (8281ec35) limits #234 to Kimi and task-bound MCP and supersedes all earlier agy reviewer implementation/admission/live-leg rulings. Agy is an implementer under #485; its retained reviewer slot remains unavailable with exact reason `agy is an implementer peer (#485)`. Earlier agy probes and unresolved meta-tool/resource findings remain historical in the [R0 report](verification/2026-10-19-kimi-agy-reviewer-isolation.md), not active smoke requirements here. Kimi's native-permission/shared-account ruling supersedes its earlier R0-only hold and Seatbelt requirement. The [spec amendment](specs/2026-09-19-agent-hub-design.md#kimi-reviewer-and-task-bound-mcp-amendment-533-hub-task-234) defines the active contract and residuals.
1958
1963
 
1959
1964
  - For Kimi regression smoke, confirm a fresh child per review, no resume, source evidence supplied only by screened `hub_task_show`/review brief and host facts, and no native Read/file/shell tool. Confirm the in-process host MCP exposes only the task-bound `hub_review` and `hub_task_show` capability, without the full hub control token. Exercise wrong task/head/review revision, stale/replaced child, private review, malformed arguments, unknown tool and stopped-child refusals; verify no saved approval on any failure.
1960
- - Kimi 2.2.0: confirm no Seatbelt wrapper, a private `KIMI_CODE_HOME` with managed-provider-only config, clean temporary cwd and explicit empty skills directory. Verify exactly the two `mcp__agent-hub__...` tools in model disclosure and invocation; reject native read/write/command and extra MCP calls. Confirm `credentials/` and `oauth/` are directory symlinks to the native locations, `device_id` links the native identity, no token copy/extraction/static provider is used, and `KIMI_DISABLE_OAUTH_LOCK` is absent. The concurrent account-free OAuth print stand-in proved two expired generations serialize refresh through shared directory links, but it was not an attached interactive TUI. Actual TUI refresh/atomic rename/lock coordination and shared-account quota/pause fanout remain separate live legs. Existing project MCP/config byte-parity observations remain recorded in the R0/live report. Record identities/metadata without token values.
1965
+ - For each compat-admitted Kimi version, confirm no Seatbelt wrapper, a private `KIMI_CODE_HOME` with managed-provider-only config, clean temporary cwd and explicit empty skills directory. Verify exactly the two `mcp__agent-hub__...` tools in model disclosure and invocation; reject native read/write/command and extra MCP calls. Confirm `credentials/` and `oauth/` are directory symlinks to the native locations, `device_id` links the native identity, no token copy/extraction/static provider is used, and `KIMI_DISABLE_OAUTH_LOCK` is absent. The concurrent account-free OAuth print stand-in proved two expired generations serialize refresh through shared directory links, but it was not an attached interactive TUI. Actual TUI refresh/atomic rename/lock coordination and shared-account quota/pause fanout remain separate live legs. Existing project MCP/config byte-parity observations remain recorded in the R0/live report. Record identities/metadata without token values.
1961
1966
  - Kimi cancellation: revoke review authority before TERM, observe bounded grace while the OAuth lock is held, then confirm owned-group teardown and temporary-state cleanup. Use synthetic auth for forced mid-refresh kill checks; report the accepted possibility of re-login rather than repair auth or count an interrupted turn as approval. Keep real-account refresh/coexistence evidence separate from synthetic cancellation evidence.
1962
1967
  - Recheck common routing, independent-family preference, recorded same-family fallback, no-verdict retry/exhaustion, head-bound persisted verdict, person-stop hold and shared-account budgets. Keep Kimi unavailable on a failed or missing receipt; agy remains unavailable under #485 regardless of historical reviewer probe results. Record executable version/hash, source head, tool/permission evidence and actual account/model results separately; source/configuration alone is not a live pass.
1963
1968