@staix/agent-hub 0.22.0 → 0.23.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +19 -0
- package/README.md +2 -2
- package/docs/agent-notes/adapters.md +2 -1
- package/docs/agent-notes/budget.md +1 -1
- package/docs/agent-notes/bus.md +4 -1
- package/docs/agent-notes/daemon.md +3 -2
- package/docs/agent-notes/local-worker.md +1 -1
- package/docs/agent-notes/models.md +2 -1
- package/docs/agent-notes/tasks.md +1 -0
- package/docs/agent-notes/tests.md +1 -1
- package/docs/dogfood.md +7 -1
- package/docs/memory.md +1 -1
- package/docs/operations.md +154 -13
- package/docs/profiles.md +28 -3
- package/docs/quickstart.md +11 -3
- package/docs/security.md +11 -5
- package/docs/specs/2026-09-19-agent-hub-design.md +66 -4
- package/docs/verification/2026-10-30-0.23.0.md +70 -0
- package/docs/verification/2026-10-30-adapter-operational-settings.md +261 -0
- package/docs/verification/2026-10-30-model-operational-settings.md +211 -0
- package/docs/verification/2026-10-30-operational-settings-closure.md +1217 -0
- package/docs/verification/2026-10-30-ui-console-operational-settings.md +1845 -0
- package/docs/verification/2026-10-31-0.23.1.md +74 -0
- package/docs/verification/2026-10-31-claude-implementer-2.1.314.md +67 -0
- package/docs/verification/2026-10-31-codex-0166-reviewer-isolation.md +52 -0
- package/docs/verification/2026-10-31-codex-0166-reviewer-tool-call.md +49 -0
- package/docs/verification/2026-10-31-kimi-241-reviewer-compat.md +63 -0
- package/docs/verification/2026-10-31-qwen-0264-acp.md +57 -0
- package/docs/verification/2026-10-31-qwen-0264-dual-output.md +52 -0
- package/docs/verification/claude-shell-wrapper-native.py +1 -1
- package/docs/verified.json +15 -15
- package/package.json +2 -2
- package/plugins/agent-hub/.claude-plugin/plugin.json +1 -1
- package/plugins/agent-hub/ROLE-NOTICE.md +1 -1
- package/plugins/agent-hub/server.js +749 -241
- package/src/adapters/acp.ts +48 -2
- package/src/adapters/claude-channel.ts +6 -4
- package/src/adapters/codex-appserver.ts +19 -0
- package/src/adapters/compat.ts +11 -11
- package/src/adapters/jcode.ts +1 -1
- package/src/adapters/local-worker.ts +3 -3
- package/src/adapters/native-capabilities.ts +50 -0
- package/src/adapters/registry-types.ts +3 -10
- package/src/cli/capabilities-output.ts +30 -0
- package/src/cli/console-state.ts +8 -5
- package/src/cli/console.ts +27 -4
- package/src/cli/help.ts +2 -1
- package/src/cli/identity.ts +1 -0
- package/src/cli/main.ts +8 -0
- package/src/hub/board.ts +8 -2
- package/src/hub/bus.ts +100 -8
- package/src/hub/capabilities.ts +215 -0
- package/src/hub/config-apply.ts +6 -3
- package/src/hub/config-trust.ts +1 -0
- package/src/hub/control-client.ts +41 -8
- package/src/hub/daemon.ts +376 -20
- package/src/hub/deferred-hub.ts +2 -2
- package/src/hub/delivery-hints.ts +2 -2
- package/src/hub/delivery-journal.ts +2 -4
- package/src/hub/envelope.ts +12 -2
- package/src/hub/events.ts +2 -0
- package/src/hub/hub-tools.ts +16 -1
- package/src/hub/inference.ts +10 -5
- package/src/hub/invocations.ts +316 -0
- package/src/hub/operational-settings.ts +102 -1
- package/src/hub/peers.ts +3 -0
- package/src/hub/restart.ts +2 -2
- package/src/hub/room-invocations.ts +145 -0
- package/src/hub/settings.ts +1 -1
- package/src/hub/shipped-packs.ts +5 -2
- package/src/hub/shipped-roles.ts +13 -0
- package/src/hub/tasks.ts +14 -0
- package/src/memory/capture.ts +2 -2
- package/src/models/relay.ts +20 -9
- package/src/rooms/application.ts +67 -2
- package/src/rooms/boundary.ts +43 -11
- package/src/rooms/service.ts +40 -2
- package/templates/checks/analysis.mjs +59 -0
- package/templates/checks/fixtures/analysis-expected.json +1 -0
- package/templates/checks/fixtures/analysis-input.json +1 -0
- package/templates/checks/fixtures/analysis.mjs +7 -0
- package/templates/checks/fixtures/ops.json +4 -0
- package/templates/checks/fixtures/writing-rules.json +4 -0
- package/templates/checks/fixtures/writing.json +5 -0
- package/templates/checks/ops.mjs +40 -0
- package/templates/checks/writing.mjs +40 -0
- package/templates/packs/analysis.pack.json +14 -0
- package/templates/packs/ops.pack.json +14 -0
- package/templates/packs/writing-pack.pack.json +14 -0
- package/templates/roles/NOTICE.md +1 -1
- package/templates/roles/ops/meeting-actions-owner.md +26 -0
- package/templates/roles/ops/project-manager.md +26 -0
- package/templates/roles/writing/editor.md +26 -0
- package/templates/roles/writing/style-checker.md +26 -0
- package/templates/roles/writing/terminology-checker.md +26 -0
- package/templates/roles/writing/translator.md +26 -0
package/docs/operations.md
CHANGED
|
@@ -581,6 +581,32 @@ authority. All newly admitted reads use the ordinary tools connection and public
|
|
|
581
581
|
views, never console identity. The person's full readback stays unchanged.
|
|
582
582
|
Conductor operations are listed in [the operational command table](#conductor-operations-812-t2-933-900).
|
|
583
583
|
|
|
584
|
+
### Capability discovery (#445 C1)
|
|
585
|
+
|
|
586
|
+
`ahub capabilities [--json]` is Console only: an agent shell is refused, and so is the configured conductor. It lists, for each attached peer, the skills and commands the hub can read from that peer's own session, whether you share each one, its digest, and the class a call would count as. It lists and never calls anything; agents call what you share with `hub_invoke` (see below).
|
|
587
|
+
|
|
588
|
+
What the hub can read is deliberately narrow. A listing is read only from a version whose listing was probed (the [C0 receipt](verification/2026-10-30-445-capability-probes.md)): Kimi 2.3.2, Qwen 0.26.1 and Codex 0.166.0. Any other version lists nothing until a new receipt covers it, and so does a peer that is offline or whose list did not arrive. Codex's pinned 0.154.0 is not probed, so a pinned install lists nothing for now. Only skills are listed: Qwen by its `_meta.source` (`bundled-skill`, `skill-dir-command`), Kimi by the `skill:` prefix of its name, Codex by an enabled entry of `skills/list`. A builtin such as Kimi's `compact` or Qwen's `clear` changes session or host state and is never listed. A session the hub cannot read natively publishes its own list with `hub_capabilities_publish`; that list is bound to the connection that published it, forced to `invoke: prompt`, and gone when the connection ends.
|
|
589
|
+
|
|
590
|
+
Sharing is a positive allowlist in `.agenthub/config.local.json` only (`invocation` is machine-local: a committed copy is ignored and logged, and it is never editable from settings):
|
|
591
|
+
|
|
592
|
+
```json
|
|
593
|
+
{ "invocation": { "share": { "kimi": { "command:skill:review": { "digest": "sha256:<64 hex>", "class": "read" } } } } }
|
|
594
|
+
```
|
|
595
|
+
|
|
596
|
+
The key is the peer id (an instance `codex-2` never inherits `codex`'s entries). The digest is the one `ahub capabilities` prints, and covers the kind, name, input schema, declared class, source and invoke form, not the description: a change to any of those unlists the entry until you pin the new digest, and rewording the description does not. `class` is `read`, `write`, `external` or `destructive`, and the effective class is the higher of what the source declared and what you wrote. No native listing declares a class, so an entry you share with no `class` counts as `destructive`, which a later invocation refuses.
|
|
597
|
+
|
|
598
|
+
Agents see only what you shared. `hub_capabilities { peer? }` returns the shared entries (id, flat input schema, effective class, digest, source, invoke form and the target's permission mode) and nothing else; it grants no right to call anything. Descriptions are written by the peer that offers them: they are sanitized, cut at 300 characters and shown as untrusted. Neither `hub_capabilities` nor `hub_capabilities_publish` runs in a PII turn or for a peer holding a PII task, and neither call reaches memory capture.
|
|
599
|
+
|
|
600
|
+
### Invoking a shared capability (#445 C2/C3)
|
|
601
|
+
|
|
602
|
+
`hub_invoke { peer, capability, args?, task? }` asks another peer to run one capability you shared from it (see `hub_capabilities`). A `read` may run without approval. A `write` or `external` requires an open non-PII task owned or reviewed by the caller and your allow-once approval on every call; unattended refuses before prompting. One with no class counts as `destructive` and is never invoked. The approval prompt shows the screened, sanitized arguments (200 characters per value, marked when cut), capability/class/task bindings and argument digest. Argument previews are person-only; audit metadata keeps the digest. Client requests cover the supported approval window; cancelling the tool or closing its control connection withdraws pending admission and approval. A late allow on a withdrawn request is refused. An already admitted native turn is not cancelled. After admission and any required approval, the call returns `{ invocation, state: "queued" }` and the result arrives later as one message from the hub, addressed to the caller only, so a long target turn never holds the caller's turn.
|
|
603
|
+
|
|
604
|
+
What the target receives is one hub-written request that names the caller and the capability and says the arguments are not instructions. It answers as an ordinary turn under its own permissions and sandbox, and may start its reply with `[REFUSED]` and a reason to decline. The result is the target's text, sanitized (a forged hub header is quoted), cut to 8 KiB and given a hub-written first line that marks it as untrusted agent text. Nothing else is promised: a result is another model's words. The answer reaches only that one result: it is taken over before the bus builds anything from it, so no other peer, digest, log line, event, journal row or memory entry ever carries it. The full answer, including a refusal, passes the private-turn, PII-pattern, secret and private-path checks and any enabled model screen using the full answer rather than only its shortened reason, before forwarding any reason; unknown or a screen failure does not clear it, and privacy is rechecked after that await. An unsafe `[REFUSED]` answer keeps outcome `refused` and returns only `<target> declined; the reason was withheld`. An ordinary unsafe answer keeps outcome `withheld` and the existing fixed withheld text. No unsafe callee text is kept; a clean refusal retains its screened reason.
|
|
605
|
+
|
|
606
|
+
Every admission refusal happens before any envelope exists and names its reason: the PII context, nesting (a peer answering an invocation cannot start one), the target being offline, paused, held, off the roster or pull-only, the capability not shared or changed since you pinned it, a destructive class, a missing effect task or person approval, a task that is not the caller's open work or is a PII task, an argument that does not fit the capability's schema, one that the PII or secret screen does not clear, or too many open invocations (4 per caller, 8 per target). A target that is paused (by you, the conductor or the budget) is refused too; a pause that starts while the request waits leaves it waiting until its deadline. Each call counts against the caller's ordinary message rate limits to that peer. An entry you did not share and one that does not exist read the same to an agent.
|
|
607
|
+
|
|
608
|
+
`invocation.timeout_s` in `.agenthub/config.local.json` (10 to 86400 seconds, default 600) bounds the wait. A request still queued at the deadline is withdrawn and nothing runs (`expired`). One the target received with no result by then is a `timeout` for read or `needs_review` for write/external: it may still run, and a late answer changes nothing. Read may automatically replay once on a proven `failed_safe` receipt; write/external never automatically replay. An uncertain effect holds its journal delivery for your inspection. Recipient retry does not replay invocation requests. There is no caller cancel; `ahub queue` resolves a delivery by hand. Task-scoped calls keep queued and terminal ids/digests/class/approval/outcome history through Tasks, without changing task state. The `invocations` table in `hub.db` and the `invocation` event hold ids, the capability, its class, the outcome and the result's size, never an argument or a result. A hub older than this feature refuses `hub_invoke` with a message, and a restart or upgrade refuses a target without the feature while an invocation is open.
|
|
609
|
+
|
|
584
610
|
## Conducting a team from Claude Code or Codex
|
|
585
611
|
|
|
586
612
|
Start the daemon with `ahub up --no-console`, set exactly one conductor in the
|
|
@@ -760,6 +786,22 @@ Private envelopes and PII tasks stay redacted. Local-worker and Pi tool details
|
|
|
760
786
|
allow decisions stay in `ahub tail` / `ahub permit`; the page can deny them.
|
|
761
787
|
No dashboard port is opened until requested. See [the session security model](security.md#local-dashboard-sessions-issue-6).
|
|
762
788
|
|
|
789
|
+
### Cross-hub read invocations (#446 X6)
|
|
790
|
+
|
|
791
|
+
Both persons consent by pinning. On the target hub, use the existing `ahub capabilities` to obtain the local capability id and digest and pin its effective class to `read`. Transfer that digest out of band. On the source hub, add an exact remote entry in its machine-local file:
|
|
792
|
+
|
|
793
|
+
```json
|
|
794
|
+
{ "invocation": { "share": { "kimi@beta": { "command:review": { "digest": "sha256:<64 hex>", "class": "read" } } } } }
|
|
795
|
+
```
|
|
796
|
+
|
|
797
|
+
The target independently retains its own local `kimi` entry for the same capability/digest/read class. Room peer exposure alone grants no invocation. There is no inheritance between labels/peers, and no remote discovery RPC. `hub_capabilities` lists local observed manifests; callers obtain remote capability ids/schema information manually.
|
|
798
|
+
|
|
799
|
+
`hub_invoke` can name the exact `peer@label`. The source validates bounded flat scalar arguments; the target applies its actual schema, current share/digest/effective-read class and privacy/availability gates before building a native request. Source task scope remains local and is not interpreted on the target. Task/approval refs and write/external/destructive requests never cross.
|
|
800
|
+
|
|
801
|
+
A signed room admission receipt is not a capability result. Native execution outcome and result transport state are recorded separately. Result correlation binds room/member/epoch/request/peer/digest, and duplicate or mismatched frames cannot trigger another native invocation. Nested invocation turns refuse depth 2, including before native acceptance is reported. Unknown handoff, receipt, result deadline or restart publication is `needs_review` under the existing journal hold and never silently replays. Inspect the retained invocation and room queue before explicit person disposition.
|
|
802
|
+
|
|
803
|
+
Remote-origin host envelopes retain the existing remote-turn memory/capture/history/telemetry exclusions even though the local native request is hub-authored. Live native account and two-Mac X7 receipts remain separate from the two-daemon broker/crypto fixtures.
|
|
804
|
+
|
|
763
805
|
### Dashboard design tokens (#469 R1)
|
|
764
806
|
|
|
765
807
|
The page's style block starts with one token block that owns every colour, the 4px spacing scale (4, 8, 12, 16, 24, 32), the
|
|
@@ -1399,7 +1441,7 @@ ahub memory forget 1a2b3c4d # a person's; also promote, demote, sup
|
|
|
1399
1441
|
|
|
1400
1442
|
`"memory": { "native": false }` in `.agenthub/config.json` turns it off for a project.
|
|
1401
1443
|
|
|
1402
|
-
The exact-pinned `@staix/agent-mem` 0.
|
|
1444
|
+
The exact-pinned `@staix/agent-mem` 0.14.0 resolves `AMEM_HOME`, then `$AGENTHUB_HOME/memory`, then `~/.agenthub/memory`. An absent `M/profile.json` selects shared v1; an explicit generation selects `M/generations/G` with the package's readonly legacy bridge. All daemon memory consumers and the hub memory/goal CLI use that selected profile. Invalid metadata or a missing selected generation makes native memory unavailable while the bus and board continue; there is no fallback to legacy writes. Inspect the daemon's memory binding/availability before interpreting missing memory as an empty store.
|
|
1403
1445
|
|
|
1404
1446
|
The daemon checks its selector at hub operation entry and before candidate/counter writes. It does not hot-switch handles or provide an atomic fence against selection changes during an in-flight core call. Quiesce consumers and close their handles before migration or selection changes; reopen through the supported session-preserving lifecycle afterward. Candidate/counter storage remains `M/candidates.db`, independent of `G`; since agent-mem 0.7.0 `amem review` also lists and accepts candidates on a selected generation. Every running hub using this home needs an authenticated receipt: a matching ready binding, or a generation-capable unbound receipt (`disabled` when native memory is off, `unavailable` when opening failed) proving it holds no memory handle. An unbound daemon and zero running hubs are quiesced for memory; a stale bound handle, unreadable registry or unknown runtime is not ready. Existing JSON memory/goal results remain unchanged; the additive binding receipt belongs to daemon status, profile-check and the decay object. This source integration does not authorize installing a new runtime, migrating real memory, restarting sessions or claiming real-machine sync/capture receipts. Migration/deactivation and their limits belong to [agent-mem](https://github.com/STAIxBWLB/agent-mem); see [the hub profile contract](memory.md#selected-memory-profile).
|
|
1405
1447
|
|
|
@@ -1974,6 +2016,21 @@ A guarded file edit that the daemon keeps at its accepted value remains visible
|
|
|
1974
2016
|
| Routing | `recovery.control_timeout_s` (30-300 seconds, default 30) | `config.local.json` | next use; the next control request of the recovery coordinator to a hub reads it |
|
|
1975
2017
|
| Routing | `recovery.shutdown_wait_s` (30-300 seconds, default 30) | `config.local.json` | next use; the next wait for a committed source to stop reads it once, at its start |
|
|
1976
2018
|
| Routing | `recovery.terminal_idle_wait_s` (30-600 seconds, default 120) | `config.local.json` | next use; the next wait for a terminal to go idle before it is closed reads it |
|
|
2019
|
+
| Start | `adapter_timing.acp_tool_ceiling_min` (1-30 minutes, default 30) | `config.local.json` | next use; a new ACP launch captures it; effective ceiling is at least the watchdog |
|
|
2020
|
+
| Start | `adapter_timing.acp_activity_batch_ms` (10-50 ms, default 50) | `config.local.json` | next use; a new ACP launch captures its activity batch delay; pending flushes are unchanged |
|
|
2021
|
+
| Start | `adapter_timing.jcode_steer_timeout_s` (5-60 seconds, default 5) | `config.local.json` | next use; a new Jcode launch captures one total native steer deadline |
|
|
2022
|
+
| Routing | `model_relay.cooldown_failures` (1-100, default 3) | `config.local.json` | restart; consecutive transport/startup failures before cooling an alias |
|
|
2023
|
+
| Routing | `model_relay.cooldown_s` (1-300 seconds, default 30) | `config.local.json` | restart; initial cooldown and recent-failure window |
|
|
2024
|
+
| Routing | `model_relay.cooldown_cap_s` (300-3600 seconds, default 300) | `config.local.json` | restart; cooldown cap, never below the initial cooldown |
|
|
2025
|
+
| Routing | `model_relay.terminal_history_limit` (1-1000 records, default 1000) | `config.local.json` | restart; only tightens retained closed-request history |
|
|
2026
|
+
| Routing | `inference.backoff_min` (1-60 minutes, default 5) | `config.local.json` | restart; failure backoff for optional inference, interactive calls exempt |
|
|
2027
|
+
| Routing | `inference.digest_min_items` (1-100000 items, default 5) | `config.local.json` | restart; existing condensation threshold |
|
|
2028
|
+
| Routing | `inference.digest_min_chars` (1-10000000 characters, default 8000) | `config.local.json` | restart; existing condensation threshold |
|
|
2029
|
+
| Start | `console.poll_ms` (2000-60000 ms, default 4000) | `config.local.json` | next use; next console launch captures its refresh interval |
|
|
2030
|
+
| Start | `console.notice_ms` (10000-60000 ms, default 10000) | `config.local.json` | next use; notice visibility may only lengthen |
|
|
2031
|
+
| Start | `console.history_limit` (1-100 commands, default 100) | `config.local.json` | next use; only tightens transient command history |
|
|
2032
|
+
| Start | `console.pending_stream_limit` (1-1000 events, default 1000) | `config.local.json` | next use; only tightens offscreen stream buffering, with omitted counts |
|
|
2033
|
+
| Start | `console.attention_limit` (1-1000 messages, default 1000) | `config.local.json` | next use; only tightens transient message attention entries |
|
|
1977
2034
|
|
|
1978
2035
|
### Operational numeric settings (#489 H1)
|
|
1979
2036
|
|
|
@@ -2066,6 +2123,88 @@ not a setting: the source's preparation lease is a fixed ten minutes, so no othe
|
|
|
2066
2123
|
install timeout has no project to own it, and the 610 s Orca command timeout follows terminal recovery's own ten-minute
|
|
2067
2124
|
ceiling. See the [H4 ledger](verification/2026-10-30-recovery-wait-settings.md) for every item in the group.
|
|
2068
2125
|
|
|
2126
|
+
#### Adapters and handshakes (#489 H5)
|
|
2127
|
+
|
|
2128
|
+
Three `adapter_timing.*` rows use the same H1 metadata, closed schema, loader and
|
|
2129
|
+
settings writer. `next use` means a new native peer launch: the host captures
|
|
2130
|
+
primitive option values for Kimi/generic ACP and Jcode, including Jcode's inherited
|
|
2131
|
+
ACP options. An accepted edit leaves active tool ceilings, pending activity
|
|
2132
|
+
flushes and native steer deadlines unchanged until a new peer starts.
|
|
2133
|
+
|
|
2134
|
+
The open-tool ceiling defaults to the legacy 30 minutes, can only tighten, and
|
|
2135
|
+
remains `max(watchdogMs, configured ceiling)` with remaining-only rearming. The
|
|
2136
|
+
activity delay defaults to the legacy 50 ms and never exceeds that buffer window;
|
|
2137
|
+
its 4096-character threshold and privacy handling stay fixed. Jcode's default
|
|
2138
|
+
5-second steer deadline can only rise, and one absolute timer covers all native
|
|
2139
|
+
phases; pre-send refusals and post-send uncertainty retain their existing fences.
|
|
2140
|
+
No new conductor authority or native permission mapping is added.
|
|
2141
|
+
|
|
2142
|
+
The [H5 ledger](verification/2026-10-30-adapter-operational-settings.md) accounts for
|
|
2143
|
+
all 232 unique H0 identities: 3 converted and 229 excluded with reasons. ACP
|
|
2144
|
+
startup/mode/cancel and Jcode readiness retain their 30-second constants: raising
|
|
2145
|
+
startup deadlines does not fit the existing 65/70-second callers, and shortening
|
|
2146
|
+
would fail slow healthy launches. The shared MCP server's reconnect cap stays
|
|
2147
|
+
fixed at 30 seconds; no daemon-to-plugin projection, raw-config reader, RPC or
|
|
2148
|
+
environment knob is introduced. Cleanup/identity proofs, wire/storage/safety caps,
|
|
2149
|
+
existing settings and test seams remain at their current owners.
|
|
2150
|
+
|
|
2151
|
+
#### Model relay and inference (#489 H6)
|
|
2152
|
+
|
|
2153
|
+
The seven rows above use the same H1 descriptors, validation, settings writer and
|
|
2154
|
+
undo path. All require a daemon restart: an inference consumer captures its
|
|
2155
|
+
configuration at daemon start, and each relay captures its policy at creation.
|
|
2156
|
+
A relay remains cached across a native peer stop/start, so restarting that peer
|
|
2157
|
+
alone does not adopt these values. Pending edits never retime current cooldowns,
|
|
2158
|
+
backoff deadlines or requests, or trim the running relay's journal.
|
|
2159
|
+
|
|
2160
|
+
The initial cooldown and its recent-failure window share one setting. Cooldowns
|
|
2161
|
+
still start only after consecutive transport/startup failures, double by the
|
|
2162
|
+
same fixed factor and end on expiry or an HTTP answer. The cap's minimum equals
|
|
2163
|
+
the initial cooldown's maximum, so it can never fall below that delay. The
|
|
2164
|
+
history setting can only tighten the legacy 1000 closed records; it does not
|
|
2165
|
+
change active-request evidence or suppress terminal observers.
|
|
2166
|
+
|
|
2167
|
+
`inference.backoff_min` preserves fail-open delivery and interactive-call
|
|
2168
|
+
exemption. The two digest thresholds were already configurable; their H1 rows
|
|
2169
|
+
retain those keys, defaults and existing schema bounds. No new model selection,
|
|
2170
|
+
PII screening, conductor authority or cross-process configuration path is added.
|
|
2171
|
+
The 180-second request deadline, 15-second moved-attempt first-byte deadline and
|
|
2172
|
+
8-second inference timeout stay fixed under their current caller budgets.
|
|
2173
|
+
|
|
2174
|
+
The [H6 ledger](verification/2026-10-30-model-operational-settings.md) accounts for
|
|
2175
|
+
all 169 H0 identities: seven registered and 162 left out with individual reasons.
|
|
2176
|
+
PII policy, route scoring, protocol contracts, existing model/routing settings
|
|
2177
|
+
and test seams remain with their current owners.
|
|
2178
|
+
|
|
2179
|
+
#### UI and console (#489 H7)
|
|
2180
|
+
|
|
2181
|
+
Five `console.*` rows use H1 and apply at the next console launch. The console
|
|
2182
|
+
reads accepted `settings_get` row values once through its existing authenticated
|
|
2183
|
+
control connection, under the existing 3-second read bound. It uses fixed H1
|
|
2184
|
+
bounds, ignores pending/file values, and retains legacy defaults for an older,
|
|
2185
|
+
refusing or unreachable hub. A running console keeps its captured preferences.
|
|
2186
|
+
The existing `options.pollMs` test seam still wins, with the fixed 2000 ms floor;
|
|
2187
|
+
refresh serialization and its pending-refresh coalescing remain unchanged.
|
|
2188
|
+
|
|
2189
|
+
Notice visibility may only lengthen. The other retention settings only tighten
|
|
2190
|
+
local presentation memory: command history, offscreen stream events and message
|
|
2191
|
+
attention references. They never trim bus queues, delivery records or approvals.
|
|
2192
|
+
Close/signal and permission handlers remain active while the startup read waits.
|
|
2193
|
+
If pushes arrived before capture, only cold-start presentation buffers are
|
|
2194
|
+
trimmed, preserving accurate omitted counts and every approval/control message.
|
|
2195
|
+
|
|
2196
|
+
The 1-second redraw/approval countdown interval, `appendEvent` cap test seam,
|
|
2197
|
+
Usage history query bound, browser toast/poll/office timings, authentication
|
|
2198
|
+
lifetimes and action budgets remain fixed. The multi-project manager owns the
|
|
2199
|
+
browser page; this change adds no per-project configuration projection or RPC.
|
|
2200
|
+
|
|
2201
|
+
The [H7 ledger](verification/2026-10-30-ui-console-operational-settings.md) covers
|
|
2202
|
+
all 1007 H0 UI/console fallback identities, with 81 in these UI/console paths and
|
|
2203
|
+
926 outside them. The additional `task-progress.ts` initializer is classified
|
|
2204
|
+
under tasks/routing by the inventory's precedence and is recorded separately.
|
|
2205
|
+
Five rows cover six H0 identities (the two command-history paths share one key).
|
|
2206
|
+
The deferred support, CLI and memory items stay constant at their current owners and bounds ([closure record](verification/2026-10-30-operational-settings-closure.md), #989); #489 is complete.
|
|
2207
|
+
|
|
2069
2208
|
- Writes go only to `.agenthub/config.local.json` and `.agenthub/routing.local.toml`,
|
|
2070
2209
|
the machine-local files (`ahub init` ignores both in git). The shared
|
|
2071
2210
|
`config.json` and the hand-written `routing.toml` are never rewritten, and a
|
|
@@ -3476,10 +3615,12 @@ after them is not retried on MLX, and a failed one marks `dgx/fast` failing (a c
|
|
|
3476
3615
|
disconnects does not). Each backend is tried once per request, so with moves on, a moved attempt
|
|
3477
3616
|
cut at 15 s followed by an MLX slot that stays busy past 120 s fails the request (502), where
|
|
3478
3617
|
without moves the request would have waited for MLX and then had `dgx/fast` with its full
|
|
3479
|
-
deadline; this is part of what the opt-in accepts until #199 AC5 measures it. After
|
|
3480
|
-
consecutive transport or startup failures
|
|
3481
|
-
|
|
3482
|
-
|
|
3618
|
+
deadline; this is part of what the opt-in accepts until #199 AC5 measures it. After
|
|
3619
|
+
`model_relay.cooldown_failures` (default three) consecutive transport or startup failures
|
|
3620
|
+
outside a cooldown (a failure more than twice the cap, 10 min by default, after the last counted
|
|
3621
|
+
one starts the count over), a relay alias cools down for `model_relay.cooldown_s` (default
|
|
3622
|
+
30 s), doubling up to `model_relay.cooldown_cap_s` (default 5 min); all three apply at the next
|
|
3623
|
+
daemon restart. A busy MLX slot and timeouts cut short by an execution budget never count; any HTTP
|
|
3483
3624
|
answer, a success or an error status, proves the transport works and ends the streak and the
|
|
3484
3625
|
cooldown. `ahub status` shows a `cooling` detail with a relative time and failure count and `events.jsonl`
|
|
3485
3626
|
records `cooldown` events.
|
|
@@ -3959,7 +4100,7 @@ selected roster and existing capabilities; the TUI is always a person's start.
|
|
|
3959
4100
|
|
|
3960
4101
|
### Qwen's own TUI, driven through dual output (#286 Part 3)
|
|
3961
4102
|
|
|
3962
|
-
`ahub qwen --mode tui` runs `qwen --input-file <in> --json-file <out> --mcp-config <hub server> --approval-mode default` in the terminal you ran it from. The daemon prepares the two files (mode 0600, under `<state dir>/qwen/qwen-dual/`) and returns the launch; the hub then appends one `submit` line per delivery and tails the event file from a saved offset. It needs a Qwen the dual-output compat entry verifies (`ahub agents` shows `qwen-dual`, verified 0.26.1 only; the headless `qwen` entry
|
|
4103
|
+
`ahub qwen --mode tui` runs `qwen --input-file <in> --json-file <out> --mcp-config <hub server> --approval-mode default` in the terminal you ran it from. The daemon prepares the two files (mode 0600, under `<state dir>/qwen/qwen-dual/`) and returns the launch; the hub then appends one `submit` line per delivery and tails the event file from a saved offset. It needs a Qwen the dual-output compat entry verifies (`ahub agents` shows `qwen-dual`, verified 0.26.1 only; the headless `qwen` entry has its own ACP receipts) and refuses another version before it creates a file. The TUI is explicit only: `ahub qwen` without `--mode` is still headless, Qwen is not a start-mode peer, and the hub never opens this terminal by itself. The TUI start is refused for an extra Qwen instance, off roster, outside a terminal, and while a headless Qwen runs (`ahub stop qwen` first); a headless start is refused while the TUI holds its terminal.
|
|
3963
4104
|
|
|
3964
4105
|
- What the hub does: a delivery becomes a `submit`; the last assistant message of the turn (an `assistant` event whose `stop_reason` is not `tool_use`, then `message_stop`) is shared as the answer; busy and idle follow the events. A `can_use_tool` request goes to the console approval path (`ahub permit`, the dashboard), and the hub's own tools are approved only by their exact `mcp__agent-hub__<tool>` name. If you answer a prompt in the Qwen terminal first, the hub's pending approval is withdrawn.
|
|
3965
4106
|
- **Limit 1, a deny ends the turn without an answer.** Qwen emits nothing when the hub denies: it cancels the whole turn. The hub ends the turn itself, shares no answer, settles the delivery as `completed` (a deny is a known outcome, and a `needs_review` would hold every later message to Qwen until a person resolved it) and says so in a notice. A turn whose outcome is not known (Qwen exited, an Esc followed by a new prompt, an event file that became unreadable) is the one that settles `needs_review`. Qwen's own notice that a background shell finished (a `user` event that is wholly one or more `<task-notification>` envelopes) is not a new prompt: it ends no hub turn and leaves an idle peer idle (task 517).
|
|
@@ -3968,7 +4109,7 @@ selected roster and existing capabilities; the TUI is always a person's start.
|
|
|
3968
4109
|
- If the hub cannot read the event file the peer is `detached`, shown as offline with a notice, never idle: Qwen itself keeps working, and the hub reattaches when the file is readable again.
|
|
3969
4110
|
- Restart: if the hub restarts while your Qwen keeps running, it reads on from its saved offset (no event twice, no launch). If Qwen is gone, `ahub qwen --mode tui` starts `qwen -r <session id>` on the same two files; the earlier submits are not replayed and the recorded answers are not shared again. Recovery never restores this terminal as a headless worker, and `ahub stop qwen` refuses: end Qwen in its own terminal.
|
|
3970
4111
|
|
|
3971
|
-
Receipt-verified native ACP identities are qwen-code 0.25.0 and 0.26.0 ([dated receipt](verification/2026-10-22-kimi-qwen-compat.md))
|
|
4112
|
+
Receipt-verified native ACP identities are qwen-code 0.25.0 and 0.26.0 ([dated receipt](verification/2026-10-22-kimi-qwen-compat.md)), 0.26.1 ([0.26.1 receipt](verification/2026-10-26-qwen-0261-acp.md)) and 0.26.4 ([0.26.4 ACP receipt](verification/2026-10-31-qwen-0264-acp.md)). The separate [0.26.4 dual-output attempt](verification/2026-10-31-qwen-0264-dual-output.md) did not establish its queued-turn criterion, so `qwen-dual` stays at 0.26.1. A CLI version label alone
|
|
3972
4113
|
is insufficient: startup verifies its native identity/version and advertised mode
|
|
3973
4114
|
ids before any prompt. The earlier local installation reported CLI 0.25.0 while
|
|
3974
4115
|
its package/ACP source said 0.24.1; that was not a ready identity. The 0.26.0 receipt
|
|
@@ -4078,13 +4219,13 @@ A bundled adapter module is authorized by the person with `adapter_modules` (at
|
|
|
4078
4219
|
|
|
4079
4220
|
# Catalog session guidance (#295)
|
|
4080
4221
|
|
|
4081
|
-
- Read available guidance with `ahub role list [--group dev|research]` and `ahub role show <id>`. Each definition identifies its layer and content hash. Doctor reports invalid files/selections without printing their contents.
|
|
4222
|
+
- Read available guidance with `ahub role list [--group dev|research|writing|ops]` and `ahub role show <id>`. Each definition identifies its layer and content hash. Doctor reports invalid files/selections without printing their contents.
|
|
4082
4223
|
- Select up to two ids per peer in `catalog_roles` in the shared/local config, with at least one flow base already held by that peer. Empty local lists clear shared selections. This initial slice applies selections at a new daemon/MCP/adapter start; live assignment/settings and task roles follow separately.
|
|
4083
4224
|
- Defaults preserve the existing flow. Catalog guidance adds no authority or routing preference. Native ingestion/retention and measured benefit remain unverified. Schema, bounds and source notice: [roles](roles.md).
|
|
4084
4225
|
|
|
4085
4226
|
# Project profiles (#259)
|
|
4086
4227
|
|
|
4087
|
-
- Initialize new projects with `ahub init --profile research|writing|general`; default dev retains its existing templates and contracts. Profile aliases map to the existing base flow and are displayed beside it. `--profile <pack>` applies a domain pack (`.agenthub/roles/<name>.pack.json` or a shipped one) as review-checklist and example-check overrides; `--dry-run` previews ids only, and a pack is data, never a command ([domain packs](profiles.md#domain-packs-513a), #513a).
|
|
4228
|
+
- Initialize new projects with `ahub init --profile research|writing|general`; default dev retains its existing templates and contracts. Profile aliases map to the existing base flow and are displayed beside it. `--profile <pack>` applies a domain pack (`.agenthub/roles/<name>.pack.json` or a shipped one: `writing-pack`, `analysis`, `ops`) as review-checklist and example-check overrides; `--dry-run` previews ids only, and a pack is data, never a command ([domain packs](profiles.md#domain-packs-513a), #513a).
|
|
4088
4229
|
- Non-dev plans declare paths, section anchors, source ids and claims. Configure completion checks explicitly in machine-local config; selecting a profile never starts a check or study. Schema, examples and pending converter/policy slices: [profiles](profiles.md).
|
|
4089
4230
|
|
|
4090
4231
|
# Private and read-only data paths (#259)
|
|
@@ -4602,7 +4743,7 @@ Default-branch capture runs only at daemon startup when a Claude implementer is
|
|
|
4602
4743
|
|
|
4603
4744
|
Project Git retains the core policy that clears credential.helper and core.askpass; an HTTPS push needing those helpers may fail, rather than importing credentials or relaxing that policy. Task branch/config proof reads are bounded synchronous reads (1.5 seconds per reader); slow or unknown evidence asks. The existing sandbox profile discovery is also synchronous; moving it to an asynchronous inventory proof with a shared deadline remains a follow-up. The nonce is visible in a permission response, so it proves a bounded grant rather than native-dispatch provenance.
|
|
4604
4745
|
|
|
4605
|
-
The [native permission receipt](verification/2026-10-23-headless-claude-permissions.md) covers Claude Code 2.1.309, and the [2.1.310 receipt](verification/2026-10-26-claude-implementer-2.1.310.md) repeats it on 2.1.310 (with 2.1.309 as a control), each with a deterministic loopback fixture and no real account/model call. It is not sandbox proof or a runtime receipt gate. The real account/model/person interaction remains a separate live leg.
|
|
4746
|
+
The [native permission receipt](verification/2026-10-23-headless-claude-permissions.md) covers Claude Code 2.1.309, and the [2.1.310 receipt](verification/2026-10-26-claude-implementer-2.1.310.md) repeats it on 2.1.310 (with 2.1.309 as a control), and the [2.1.314 receipt](verification/2026-10-31-claude-implementer-2.1.314.md) repeats it and the host-wrapper procedure on 2.1.314 (with 2.1.310 as a control), each with a deterministic loopback fixture and no real account/model call. It is not sandbox proof or a runtime receipt gate. The real account/model/person interaction remains a separate live leg.
|
|
4606
4747
|
|
|
4607
4748
|
A headless Claude reviewer is run by the hub instead (task 223): declare
|
|
4608
4749
|
`"claude-review": { "kind": "claude", "roles": ["reviewer"], "start_mode": "headless" }`
|
|
@@ -4640,9 +4781,9 @@ A reviewer has a review scope (#852): `full` (reads the code, runs the affected
|
|
|
4640
4781
|
|
|
4641
4782
|
Start them with `ahub claude --as claude-review`, `ahub codex --as codex-review` and `ahub kimi --as kimi-review`; stop with `ahub stop <id>`. For the Claude and Codex reviewers the command asks the running hub to start the reviewer and returns: nothing runs in that terminal, it takes no launch arguments, and `--print-command` or `--dry-run` prints a hub-start note instead of a launch (#720). Fresh Claude and Codex variants share the project checkout; the Kimi variant runs in a clean temporary working directory and reviews only the screened brief and host facts. All fresh variants refuse a custom worktree or permission mode. Codex and Kimi require `review_mode: "fresh"`; without it, an existing headless instance with the reviewer role keeps its native app-server/ACP behavior, worktree and permission options and does not join this pool. Claude retains its task 223 automatic headless-reviewer declaration. The agy reviewer slot remains automatically unavailable (`agy is an implementer peer (#485)`). A failed or unsupported fresh start never downgrades to a native continuing session. Ordinary implementer instances retain their own adapters. The default logical pool order is Claude, Codex, Kimi. Only configured kinds with at least one declared reviewer count as slots, including declared ones that are offline, paused, quota-limited or failing, so availability never renumbers them; configured kinds with no declaration are listed after the walk as `no declared reviewer`. `review.rerotate` defaults to false. Pool selection starts at PR number modulo the number of declared kind slots, or task id when there is no PR, then skips the implementer's family and unavailable candidates. Delta review prefers the preceding eligible independent family in a fresh child. A preceding same-family fallback does not outrank a newly available independent family. `ahub route explain` shows the same pure walk.
|
|
4642
4783
|
|
|
4643
|
-
Codex requires a verified `codex-cli` (0.146.0, 0.163.0 or 0.164.0) or one the person trusts on this machine, and a passing bounded runtime receipt before its model child starts. 0.146.0 and 0.163.0 passed the [2026-10-22 model-free production admission](verification/2026-10-22-codex-reviewer-isolation.md) and 0.164.0 the [2026-10-24 one](verification/2026-10-24-codex-0164-reviewer-isolation.md). A refusal names the installed and verified versions. Either set `codex_bin` to a verified executable, or run `ahub agents trust codex-review <version>`: trust lifts only the version pin, and every proof below still runs before each child (#691). Each launch approves exactly `hub_review` and `hub_task_show` per tool (`approval_mode = "approve"`), never through a server-wide default: without it Codex 0.164.0 listed the tools but refused the call, so reviews ended with no verdict (#783). A model-free receipt proves the inventory, not that a verdict reaches the hub; a [model-backed 0.164.0 run](verification/2026-10-25-codex-0164-reviewer-tool-call.md) recorded one verdict through that path. The producer checks native binary/config stability, configured MCP inventory with only the two hub tools, effective Apps/plugins/hooks disablement, an explicit empty legacy `notify` command and a named filesystem profile. Apps needs separate feature readback because configured inventory omits runtime connectors. Web search is requested disabled; its effective turn mode is not read back and managed requirements can select Cached (see the verification ceiling). Each launch proves public-read, exact/glob/symlink private-denial, standard host credential denial and write-denial in temporary fixtures. Standard credential and configured tool-home/XDG subtrees are denied independently of project `local.deny`. Its sandboxed shell is filesystem read-only with private paths denied and network configured disabled; it is not Claude's Read/Grep/Glob-only tool whitelist. No credential home is copied.
|
|
4784
|
+
Codex requires a verified `codex-cli` (0.146.0, 0.163.0 or 0.164.0) or one the person trusts on this machine, and a passing bounded runtime receipt before its model child starts. 0.146.0 and 0.163.0 passed the [2026-10-22 model-free production admission](verification/2026-10-22-codex-reviewer-isolation.md) and 0.164.0 the [2026-10-24 one](verification/2026-10-24-codex-0164-reviewer-isolation.md); 0.166.0 passed the [2026-10-31 model-free procedure](verification/2026-10-31-codex-0166-reviewer-isolation.md), but its [one authorized model review](verification/2026-10-31-codex-0166-reviewer-tool-call.md) missed the required synthetic marker in the persisted verdict, so the version remains unverified and its proposed verified bump was reverted. A refusal names the installed and verified versions. Either set `codex_bin` to a verified executable, or run `ahub agents trust codex-review <version>`: trust lifts only the version pin, and every proof below still runs before each child (#691). Each launch approves exactly `hub_review` and `hub_task_show` per tool (`approval_mode = "approve"`), never through a server-wide default: without it Codex 0.164.0 listed the tools but refused the call, so reviews ended with no verdict (#783). A model-free receipt proves the inventory, not that a verdict reaches the hub; a [model-backed 0.164.0 run](verification/2026-10-25-codex-0164-reviewer-tool-call.md) recorded one verdict through that path. The producer checks native binary/config stability, configured MCP inventory with only the two hub tools, effective Apps/plugins/hooks disablement, an explicit empty legacy `notify` command and a named filesystem profile. Apps needs separate feature readback because configured inventory omits runtime connectors. Web search is requested disabled; its effective turn mode is not read back and managed requirements can select Cached (see the verification ceiling). Each launch proves public-read, exact/glob/symlink private-denial, standard host credential denial and write-denial in temporary fixtures. Standard credential and configured tool-home/XDG subtrees are denied independently of project `local.deny`. Its sandboxed shell is filesystem read-only with private paths denied and network configured disabled; it is not Claude's Read/Grep/Glob-only tool whitelist. No credential home is copied.
|
|
4644
4785
|
|
|
4645
|
-
Kimi requires a compat-admitted Kimi Code on the hub's PATH (receipt-verified 2.2.0, 2.3.0, 2.3.1 and 2.
|
|
4786
|
+
Kimi requires a compat-admitted Kimi Code on the hub's PATH (receipt-verified 2.2.0, 2.3.0, 2.3.1, 2.3.2 and 2.4.1), signed in through its managed OAuth provider, and a passing runtime preparation before each child; an unverified/untrusted version or a failed preparation is unavailable with its reason. The [2.3.0](verification/2026-10-22-kimi-qwen-compat.md), [2.3.1](verification/2026-10-25-kimi-231-reviewer-compat.md), [2.3.2](verification/2026-10-26-kimi-232-reviewer-compat.md) and [2.4.1](verification/2026-10-31-kimi-241-reviewer-compat.md) receipts are model-free and do not verify real login/provider readiness. Each review runs a new `kimi -p` child without Seatbelt, in a private `KIMI_CODE_HOME` with managed-provider-only config, a clean temporary working directory and an empty skills directory. Its agent may call only `hub_review` and `hub_task_show`, through a task-bound loopback MCP session that never carries the hub's control token, and has no native read, file or shell tool: it reviews the screened brief and host facts only. Its `credentials/`, `oauth/` and `device_id` link to the person's native Kimi files, so it shares the native login, refresh lock and the Kimi account's budget; no token is copied. A stop revokes its verdict authority first, then waits briefly while an OAuth refresh holds the lock; a forced stop mid-refresh may require a native re-login. The Kimi backend records no measured token counts here, so its usage stays unknown; the 2.3.0 receipt makes no usage claim. This is a native tool-authority boundary, not filesystem or network isolation.
|
|
4646
4787
|
|
|
4647
4788
|
A dangling or unreadable standard/configured credential root makes the Codex reviewer unavailable with only its path class in the reason (for example `~/.docker` or `$DOCKER_CONFIG`, never an expanded path or contents); repair the root and start the reviewer again.
|
|
4648
4789
|
|
|
@@ -4740,7 +4881,7 @@ Native launcher connections retry transport timeouts/unclean closes with bounded
|
|
|
4740
4881
|
|
|
4741
4882
|
## amem runtime (agent-mem#13 P4)
|
|
4742
4883
|
|
|
4743
|
-
The hub exact-pins @staix/agent-mem 0.
|
|
4884
|
+
The hub exact-pins @staix/agent-mem 0.14.0 for recall, curated evidence and local deterministic capture. No runtime call reaches a claude-mem worker; `memory.worker_url` is ignored. The person's `ahub memory import claude-mem` remains explicit and off by default. Existing profile, alias, PII and private-turn fences still apply. `ask --remember` and context checkpoints propose machine-local candidates, never automatic curated evidence. See [memory integration](memory.md#in-process-recall-and-capture-agent-mem13-p4) for changed behavior and bounded-view ceilings.
|
|
4744
4885
|
|
|
4745
4886
|
## Backlog issue identity and notes
|
|
4746
4887
|
|
package/docs/profiles.md
CHANGED
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
|
|
31
31
|
# Current scope and remaining slices
|
|
32
32
|
|
|
33
|
-
- Peer guidance and profile-specific task schemas use existing MCP/native/local paths. Research defaults use the #295 dev/research catalog, with bases already held. Writing
|
|
33
|
+
- Peer guidance and profile-specific task schemas use existing MCP/native/local paths. Research defaults use the #295 dev/research catalog, with bases already held. Writing and ops catalog groups are available through the domain packs below; profile defaults do not automatically assign them to peers.
|
|
34
34
|
- Board/public projections, route explain, terminal/dashboard cards and #247 events/records carry the profile alias and base. Native model ingestion and context retention remain unverified.
|
|
35
35
|
- Bounded rich-document facts/review/undo projections and declared pii.paths/data.readonly policy are supported. Opt-in shadow git supports non-git folders; project profile overrides are supported; bounded glob policy and deeper lifecycle integration are tracked in #259. Native ingestion and research measurement remain unverified.
|
|
36
36
|
|
|
@@ -61,14 +61,39 @@
|
|
|
61
61
|
|
|
62
62
|
# Domain packs (#513a)
|
|
63
63
|
|
|
64
|
-
A pack bundles a profile with the roles it expects, a review rubric and example checks in one file, so a project can adopt a way of working with one command. The
|
|
64
|
+
A pack bundles a profile with the roles it expects, a review rubric and example checks in one file, so a project can adopt a way of working with one command. The shipped writing, analysis and ops packs use this format (#513b).
|
|
65
65
|
|
|
66
|
-
- A pack comes from two layers: the shipped one (`templates/packs/`, bundled;
|
|
66
|
+
- A pack comes from two layers: the shipped one (`templates/packs/`, bundled; `example`, `writing-pack`, `analysis` and `ops`), and the project one, `.agenthub/roles/<name>.pack.json`, which replaces a shipped pack of the same name. The project layer is in the directory the role catalog already reads (the catalog takes only `.md`, so the two never collide). At most 16 packs of 8192 bytes each, plain regular files that are not links, in a directory that is not a link. A pack that is oversize, malformed or names a role that is not in the catalog (shipped or project) is refused with the reason, never loaded half usable.
|
|
67
67
|
- The file holds exactly `name` (equal to the file name, and never a profile's name), `profile` (one of dev, research, writing, general), `summary` (160 characters), `roles` (at most 8 distinct role ids), `rubric` (1 to 12 criteria, each `id`, `text` of at most 300 characters and `required`) and `checks` (at most 8, each `id` and a `note` of at most 500 characters). Any other field is refused. Text is plain ASCII punctuation with no control characters.
|
|
68
68
|
- Rubric ids are #507's criterion ids (a letter, then letters, digits or `-`, at most 48 characters), the same shape a task's recorded criteria use, so a reviewer names an unmet item with the id at the start of the line. The rubric only changes the wording of the review checklist; it never adds or removes a task's recorded criteria.
|
|
69
69
|
- A pack is data, never authority. A check is display text exactly like `example_checks`: nothing selects, runs or trusts a command from a pack, and there is no field that could. Roles are named, not created: the pack asks for catalog roles by id and the project assigns them with `catalog_roles` as before.
|
|
70
70
|
- `ahub init --profile <profile|pack>` takes a shipped profile or a project pack. `--dry-run` (add `--json` for rows) previews the config row with the pack's id, base profile, rubric criterion ids, check ids and expected role ids, and never their text. The apply writes the pack's rubric as `profile_overrides.review_checklist` (each line `<id>: <text>`, optional criteria marked, then the `{check}` line) and its checks as `profile_overrides.example_checks`, into a new `.agenthub/config.json` or the existing one, keeping every other key. Applying the same pack again changes nothing. A project whose `profile` differs from the pack's, or whose `profile_overrides` already hold different data (compared with sorted keys, so key order never matters), is refused with the reason rather than overwritten. Init does not copy role files and a check is a note, not a command: the spec amendment "domain packs (#513a)" records why.
|
|
71
71
|
|
|
72
|
+
# Shipped domain packs and checks (#513b)
|
|
73
|
+
|
|
74
|
+
| Pack | Base profile | Catalog roles | Deterministic check |
|
|
75
|
+
| --- | --- | --- | --- |
|
|
76
|
+
| `writing-pack` | writing | editor, translator, style-checker, terminology-checker | writing-metadata, writing-terms, writing-style |
|
|
77
|
+
| `analysis` | research | analyst | analysis-reproduce |
|
|
78
|
+
| `ops` | general | project-manager, meeting-actions-owner | ops-records |
|
|
79
|
+
|
|
80
|
+
Use `ahub init --profile writing-pack --dry-run` (or `analysis`/`ops`) to preview, then omit `--dry-run` to apply. `writing` remains the existing plain profile; the pack uses `writing-pack` because pack names cannot shadow profiles. Expected roles are already in the shipped catalog; assign them deliberately through the existing role flow. Neither init nor pack loading copies or executes a checker, adds trusted `checks` commands, or grants permissions.
|
|
81
|
+
|
|
82
|
+
From an agent-hub source checkout, these commands run the shipped synthetic examples:
|
|
83
|
+
|
|
84
|
+
```sh
|
|
85
|
+
bun templates/checks/writing.mjs templates/checks/fixtures/writing.json templates/checks/fixtures/writing-rules.json
|
|
86
|
+
bun templates/checks/analysis.mjs templates/checks/fixtures/analysis.mjs templates/checks/fixtures/analysis-input.json templates/checks/fixtures/analysis-expected.json
|
|
87
|
+
bun templates/checks/ops.mjs templates/checks/fixtures/ops.json
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
The npm package includes these files under `templates/checks/`; when installed, use their absolute installed-package paths or copy the reviewed examples into the project. Only an explicitly selected, trusted analysis program is executed. A project that wants an automatic task check must separately review and configure a command through the existing `checks` policy. A pack note remains advisory.
|
|
91
|
+
|
|
92
|
+
- Writing takes a JSON document with nonempty `title`, `language` and `text`, and JSON rules with literal `requiredTerms` and `bannedPhrases` arrays. Required terms must occur in text; banned phrases are absent from title/text. The mechanical style check refuses TODO/TBD placeholders and trailing whitespace. These checks do not assess translation fidelity, source support, readability or audience fit.
|
|
93
|
+
- Analysis takes a program, JSON input and an exact expected stdout file. It invokes the selected program twice with the input path as its first argument, without a shell, compares stdout bytes including the final newline, and refuses changes to any declared file after either run. Each run has a 5-second/1-MiB stdout bound; its stderr is withheld. Equality does not prove method suitability, input provenance or reproducibility of undeclared environment/dependencies.
|
|
94
|
+
- Ops takes an array of exact `{id, owner, due, status}` records. IDs must be unique and nonempty, owners nonempty, due dates real `YYYY-MM-DD` values, and status one of `open`, `in_progress`, `blocked`, `done`. It does not compare to the current clock. An empty array passes structure only; it does not establish that a meeting had no actions or that all actions were captured.
|
|
95
|
+
- All input files are bounded to 1 MiB. JSON inputs must be valid UTF-8/JSON; symbolic links and missing files refuse. Analysis expected output is compared as raw bytes rather than parsed JSON. Results contain stable criterion ids and a final `writing: OK`, `analysis: OK` or `ops: OK` (or FAIL), never artifact text. Exit codes are 0 for pass, 1 for refusal and 2 for incorrect arguments. Human rubric items remain separate from these mechanical results. Fixture success is not live workflow or publication approval.
|
|
96
|
+
|
|
72
97
|
# Profile scorecard (#799)
|
|
73
98
|
|
|
74
99
|
The existing ahub report --dogfood command adds a profile section when the selected profile is non-dev, or bound non-dev tasks have evidence in the requested window. Dev-only JSON and readable output retain their prior bytes. Existing flow, quality, cost and other fields keep their meanings; the profile section does not require merged PRs.
|
package/docs/quickstart.md
CHANGED
|
@@ -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.
|
|
20
|
+
bun add -g github:STAIxBWLB/agent-hub#v0.23.1
|
|
21
21
|
ahub setup
|
|
22
22
|
```
|
|
23
23
|
|
|
@@ -30,6 +30,14 @@ 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.23.1
|
|
34
|
+
|
|
35
|
+
The hub now exact-pins `@staix/agent-mem` 0.14.0, with no storage change for the hub. amem 0.14.0 adds an opt-in v2 public prune view (`amem migrate --public-prune-view`) that 0.13.0 and earlier readers refuse once it is activated: before a person activates it, upgrade every hub that shares the memory home to 0.23.1 or later (which bundles amem 0.14.0) and every global `amem` CLI to 0.14.0. Claude Code 2.1.314 joins the verified headless implementer versions (#996), Kimi Code 2.4.1 the fresh reviewer versions and Qwen Code 0.26.4 the ACP versions (#1002); the Qwen dual-output TUI stays on 0.26.1 and Codex 0.166.0 stays unverified (#997).
|
|
36
|
+
|
|
37
|
+
## Upgrading to 0.23.0
|
|
38
|
+
|
|
39
|
+
Nothing changes until you set something. Thirteen more operational numbers became settings in `config.local.json`, each keeping its old value when unset: three `adapter_timing.*` (at the next Kimi, ACP or Jcode launch, #982), four `model_relay.*` and `inference.backoff_min` (at the next daemon restart, #984) and five `console.*` (at the next console launch, #987). Capability sharing is new and opt-in. `ahub capabilities` (person only) lists the skills an attached Kimi 2.3.2, Qwen 0.26.1 or Codex 0.166.0 offers; nothing is offered to other agents until you pin an entry's digest under `invocation.share` in `.agenthub/config.local.json` (#985). Agents then call a shared `read` capability with `hub_invoke`. A `write` or `external` one needs an open task of the caller and your allow-once approval on every call, and one with no class is never invoked (#986, #991; see [invoking a shared capability](operations.md#invoking-a-shared-capability-445-c2c3)). Across a room, only a read capability both persons pinned crosses (#992). `ahub init --profile writing-pack|analysis|ops` applies the new shipped domain packs (#993). The `@staix/agent-mem` pin stays 0.13.0. Upgrade running hubs with `ahub upgrade` (an older hub refuses `hub_invoke`) and restart Claude sessions so they load the 0.23.0 plugin.
|
|
40
|
+
|
|
33
41
|
## Upgrading to 0.22.0
|
|
34
42
|
|
|
35
43
|
Nothing changes until you set something. Ten operational numbers became settings: five bus and delivery limits (`bus.*`, #975) and five upgrade-recovery waits (`recovery.*`, #979). Unset, each keeps the value it had. `ahub learn list|accept|reject` (#514b, person only) is your verdict on the recurring-unmet-criterion cards. The macOS PTY bridge no longer hangs under load or outlives its caller (#560). The `@staix/agent-mem` pin stays 0.13.0. Upgrade running hubs with `ahub upgrade` and restart Claude sessions so they load the 0.22.0 plugin.
|
|
@@ -128,7 +136,7 @@ The restarted Claude planner gets the release-train tools (`hub_release_new`, `h
|
|
|
128
136
|
|
|
129
137
|
Jcode must be 0.98.0 or later, below 1.0.0; 0.94.0 is no longer accepted, so `ahub doctor` fails its Jcode row for an older install and the peer does not start (#529). Every owned jcode process runs with `JCODE_NO_TELEMETRY=1`.
|
|
130
138
|
|
|
131
|
-
Since 0.12.37 the hub opens a split memory home through its shards (#534); the current exact `@staix/agent-mem` pin is 0.
|
|
139
|
+
Since 0.12.37 the hub opens a split memory home through its shards (#534); the current exact `@staix/agent-mem` pin is 0.14.0. Before anyone runs `amem migrate --shards` on a memory home, every hub using that home must run 0.12.37 or later: an older hub does not see the split and keeps writing the old layout. `ahub memory profile-check` is split-aware and compares each hub with its own project's shard (#536). See [memory profiles and rollout](memory.md#selected-memory-profile).
|
|
132
140
|
|
|
133
141
|
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:
|
|
134
142
|
|
|
@@ -196,7 +204,7 @@ More: `ahub help`. How it is built and why: [`docs/specs/2026-09-19-agent-hub-de
|
|
|
196
204
|
|
|
197
205
|
## Shared memory
|
|
198
206
|
|
|
199
|
-
The hub exact-pins the first-party `@staix/agent-mem` 0.
|
|
207
|
+
The hub exact-pins the first-party `@staix/agent-mem` 0.14.0 core, including deterministic capture; no claude-mem worker is needed. 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. Since agent-mem 0.7.0, `amem review` also accepts candidates on a selected generation.
|
|
200
208
|
|
|
201
209
|
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.
|
|
202
210
|
|
package/docs/security.md
CHANGED
|
@@ -7,7 +7,7 @@ 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, and a headless Claude implementer's proven shell commands through the host wrapper (#845, below); 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. A call the always-ask policy classes (below) is never approved this way, whatever its title. 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; a classed call from a hub-owned ACP peer, the local worker or Pi still waits for a person under it.
|
|
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` (still accepted, ignored since 0.16.0), `local.read_allow`, `local.bash_network`, `local.network_allow`, `local.doc_read`, `local.web_fetch`, `terminal.open`, `tasks.auto_pull`, `tasks.queue_backpressure`, `context.max_assignment_used`, `schedules`, `folder_triggers`, `corpus.paths`, `instances`, `action_policy`, `rooms`) apply only from a config file git confirms nobody committed (`instances`, `action_policy` and `rooms` 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, a folder the hub reads, work proposed on a timer, a host a model may fetch from, 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`).
|
|
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` (still accepted, ignored since 0.16.0), `local.read_allow`, `local.bash_network`, `local.network_allow`, `local.doc_read`, `local.web_fetch`, `terminal.open`, `tasks.auto_pull`, `tasks.queue_backpressure`, `context.max_assignment_used`, `schedules`, `folder_triggers`, `corpus.paths`, `invocation`, `instances`, `action_policy`, `rooms`) apply only from a config file git confirms nobody committed (`instances`, `action_policy` and `rooms` 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, a folder the hub reads, work proposed on a timer, a host a model may fetch from, which of your agents' skills other peers may call, 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
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. Kimi's token records (#285) are counted from its own session wire log under its home: only the four token integers, the model label and the row time are copied, and the record id is an opaque hash, never a path, session id or text. The log 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.
|
|
@@ -24,7 +24,7 @@ agent-hub connects agents that can each run commands. This page says what the hu
|
|
|
24
24
|
|
|
25
25
|
## Native memory authority and profile selection
|
|
26
26
|
|
|
27
|
-
The portable core is the exact-pinned first-party `@staix/agent-mem` 0.
|
|
27
|
+
The portable core is the exact-pinned first-party `@staix/agent-mem` 0.14.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.
|
|
28
28
|
|
|
29
29
|
`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.
|
|
30
30
|
|
|
@@ -543,7 +543,7 @@ withholds the scoped mode on these writes so an older hook grants nothing too.
|
|
|
543
543
|
|
|
544
544
|
Agent CLI versions (#691). One policy gates the Codex and Kimi fresh reviewers, the headless Claude implementer, Jcode, agy, Qwen (ACP and the dual-output TUI) and Pi (#843): 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.
|
|
545
545
|
|
|
546
|
-
Qwen Code's optional production ACP peer requires the qwen-code native identity, a compat-admitted version (receipt-verified 0.25.0, 0.26.0 and 0.26.
|
|
546
|
+
Qwen Code's optional production ACP peer requires the qwen-code native identity, a compat-admitted version (receipt-verified 0.25.0, 0.26.0, 0.26.1 and 0.26.4)
|
|
547
547
|
and advertised mode ids before setting a policy or starting work. Its own tools
|
|
548
548
|
have no hub sandbox; auto-edit approves file edits, yolo every tool. Configured
|
|
549
549
|
never-ask still requires the person's confirmation. qwen_cmd is machine-local and
|
|
@@ -675,7 +675,7 @@ A terminal Claude implementer or verifier instance (#525-5) owns write-class tas
|
|
|
675
675
|
|
|
676
676
|
A planner-only instance (#525-6: `planner` without `implementer` or `verifier`) plans on the project checkout, so a declared `worktree` is refused. A Codex planner's `thread/start` and `thread/resume` are requested read-only, and a thread is adopted only when Codex's reply positively confirms a read-only sandbox; an unconfirmed or malformed reply refuses that RPC and closes the link before the hub adopts the thread or delivers work, and the current roles are rechecked before each injection. This is an initial ceiling, not confinement of the person's attached TUI, which can change its native sandbox later. A declared planner-only Claude channel instance gets no automatic grant from its facts hook for any tool but Read, LS, Glob and Grep, whatever the permission mode.
|
|
677
677
|
|
|
678
|
-
The headless Claude implementer (#525-4) runs one owned `claude -p` child per turn in its declared worktree, with `--setting-sources ""`, `--strict-mcp-config`, native permission mode `default`, the hub's managed PreToolUse hook and `--permission-prompt-tool mcp__agent-hub__hub_permission_request`; never `--bare`, `acceptEdits`, `dontAsk` or `bypassPermissions`, and `never-ask` is refused. Each task keeps its own persisted session; unscoped chat reuses one only while exactly one task is in progress, and a `hub_inbox` read refuses a batch attributed to another task. The approval path fails closed: `hub_permission_request` is accepted only from the current child's launch and generation, goes to the console's existing approval queue and `approvals.timeout_s`, and offers allow-once and deny only, with no allow-always or session grant. A timeout, a prompt failure, a disconnect, a hub that is down or refusing, or a stale child binding answers deny; a failed hook decides nothing, so the call still waits for a person. The approval title is the tool name and a JSON rendering of the arguments passed through `sanitize()` and the public-text screen (any PII, secret or private-path match withholds the whole value as `[withheld]`, before any cut), never task text, cut at 600 characters with a `[cut, N chars]` marker; the original arguments stay with the child and are not logged. The MCP wire cannot tell a native permission dispatch from a call the model makes itself; a person allow still refers only to its original arguments. Automatic Bash authorization uses the separate single-use host wrapper contract below, never a caller-supplied tool id as native provenance. A stop holds an uncertain delivery until the owned process group is confirmed gone. The [native permission receipt](verification/2026-10-23-headless-claude-permissions.md) is model-free on Claude Code 2.1.309, [repeated on 2.1.310](verification/2026-10-26-claude-implementer-2.1.310.md); it is not sandbox proof, and a real account, model and person interaction remains a live leg.
|
|
678
|
+
The headless Claude implementer (#525-4) runs one owned `claude -p` child per turn in its declared worktree, with `--setting-sources ""`, `--strict-mcp-config`, native permission mode `default`, the hub's managed PreToolUse hook and `--permission-prompt-tool mcp__agent-hub__hub_permission_request`; never `--bare`, `acceptEdits`, `dontAsk` or `bypassPermissions`, and `never-ask` is refused. Each task keeps its own persisted session; unscoped chat reuses one only while exactly one task is in progress, and a `hub_inbox` read refuses a batch attributed to another task. The approval path fails closed: `hub_permission_request` is accepted only from the current child's launch and generation, goes to the console's existing approval queue and `approvals.timeout_s`, and offers allow-once and deny only, with no allow-always or session grant. A timeout, a prompt failure, a disconnect, a hub that is down or refusing, or a stale child binding answers deny; a failed hook decides nothing, so the call still waits for a person. The approval title is the tool name and a JSON rendering of the arguments passed through `sanitize()` and the public-text screen (any PII, secret or private-path match withholds the whole value as `[withheld]`, before any cut), never task text, cut at 600 characters with a `[cut, N chars]` marker; the original arguments stay with the child and are not logged. The MCP wire cannot tell a native permission dispatch from a call the model makes itself; a person allow still refers only to its original arguments. Automatic Bash authorization uses the separate single-use host wrapper contract below, never a caller-supplied tool id as native provenance. A stop holds an uncertain delivery until the owned process group is confirmed gone. The [native permission receipt](verification/2026-10-23-headless-claude-permissions.md) is model-free on Claude Code 2.1.309, [repeated on 2.1.310](verification/2026-10-26-claude-implementer-2.1.310.md) and [on 2.1.314](verification/2026-10-31-claude-implementer-2.1.314.md); it is not sandbox proof, and a real account, model and person interaction remains a live leg.
|
|
679
679
|
|
|
680
680
|
The #845 host shell wrapper is the only automatic Bash path for a headless implementer. In `ask-when-needed`, a complete public input must pass the existing #816 read-only proof or the closed task-command proof, with an active owned task and registered worktree. Enabled action-policy classes still ask, and `ask`, PII handling and the `never-ask` refusal are unchanged. The native request and authenticated host response both name wrapper protocol 1, so an older plugin cannot drop the rewrite and execute an original command automatically.
|
|
681
681
|
|
|
@@ -685,7 +685,7 @@ Task branch authority uses recorded `refs.branch`, otherwise a host-observed non
|
|
|
685
685
|
|
|
686
686
|
The fresh Codex reviewer (#519) needs `review_mode: "fresh"` and a compat-admitted Codex (receipt-verified 0.146.0, 0.163.0 and 0.164.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 (each approved by its own name, never by a server-wide default, #783), 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. A failed per-review start names one class from a closed list (`version`, `binary`, `mcp_inventory`, `plugins_hooks_apps`, `sandbox_proof`, `credential_root`, `timeout`, `unknown`) with a fixed sentence in the unavailable reason, `hub.log` and the `reviewer` line of `ahub status`; it never carries a path, native CLI text or a token (#722). 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; it binds nothing, so agy's own `hub_task_show` reads are not fenced as a review (#792).
|
|
687
687
|
|
|
688
|
-
The fresh Kimi reviewer (#533) needs `review_mode: "fresh"` and a compat-admitted Kimi Code (receipt-verified 2.2.0, 2.3.0, 2.3.1 and 2.
|
|
688
|
+
The fresh Kimi reviewer (#533) needs `review_mode: "fresh"` and a compat-admitted Kimi Code (receipt-verified 2.2.0, 2.3.0, 2.3.1, 2.3.2 and 2.4.1; `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](verification/2026-10-22-kimi-qwen-compat.md) and [2.3.1](verification/2026-10-25-kimi-231-reviewer-compat.md) receipts are model-free and do 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.
|
|
689
689
|
|
|
690
690
|
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. Issue identity linking at propose and accept (#723) parses the task's title and refs locally, reads nothing from GitHub and skips PII tasks. The CI workflow keeps `contents: read` and `actions: read`.
|
|
691
691
|
|
|
@@ -765,6 +765,12 @@ Room encryption authenticates a hub, not local role authority. Remote ids contai
|
|
|
765
765
|
`@` and cannot connect to local control, own tasks, review, plan, conduct, start a
|
|
766
766
|
native runtime or become a recovery session. Incoming chat is explicitly directed
|
|
767
767
|
to a current exposed local native peer, framed as untrusted, and capped at status.
|
|
768
|
+
Since X6 (#992), a remote peer may also ask an exposed local peer to run one
|
|
769
|
+
capability that both persons pinned as `read`: the target's own `invocation.share`
|
|
770
|
+
entry and the source's exact `peer@label` entry. Task and approval refs, and write,
|
|
771
|
+
external or destructive requests, never cross. The target re-applies its schema,
|
|
772
|
+
digest, class and privacy gates before any native request, and an unknown handoff or
|
|
773
|
+
result is `needs_review`, never replayed.
|
|
768
774
|
The same live sender-exposure check applies to restored inboxes. A removed
|
|
769
775
|
sender's registered pending application is quarantined before its plaintext is
|
|
770
776
|
retired, so it remains available for person disposition.
|