@staix/agent-hub 0.19.0 → 0.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +11 -0
- package/README.md +2 -2
- package/docs/agent-notes/adapters.md +3 -2
- package/docs/agent-notes/bus.md +1 -1
- package/docs/agent-notes/daemon.md +6 -2
- package/docs/agent-notes/local-worker.md +1 -0
- package/docs/agent-notes/tasks.md +4 -2
- package/docs/agent-notes/tests.md +4 -3
- package/docs/dogfood.md +4 -1
- package/docs/memory.md +1 -1
- package/docs/operations.md +40 -8
- package/docs/quickstart.md +7 -3
- package/docs/sdlc.md +12 -0
- package/docs/security.md +4 -4
- package/docs/specs/2026-09-19-agent-hub-design.md +85 -3
- package/docs/verification/2026-10-27-0.20.0.md +75 -0
- package/docs/verified.json +24 -16
- package/package.json +2 -2
- package/plugins/agent-hub/.claude-plugin/plugin.json +1 -1
- package/plugins/agent-hub/server.js +103 -7
- package/src/adapters/acp.ts +8 -0
- package/src/adapters/channel-peer.ts +7 -5
- package/src/adapters/claude-implementer.ts +1 -0
- package/src/adapters/codex-appserver.ts +11 -1
- package/src/adapters/conformance/enrollment.ts +4 -4
- package/src/adapters/conformance/factories.ts +2 -1
- package/src/adapters/conformance/kit.ts +10 -0
- package/src/adapters/local-worker.ts +2 -0
- package/src/adapters/pi.ts +7 -0
- package/src/adapters/reviewer-print.ts +1 -0
- package/src/hub/ci-watch.ts +140 -0
- package/src/hub/claude-launch-proof.ts +67 -2
- package/src/hub/config-apply.ts +2 -2
- package/src/hub/config-trust.ts +1 -0
- package/src/hub/daemon.ts +86 -79
- package/src/hub/peers.ts +34 -0
- package/src/hub/settings.ts +1 -0
- package/src/hub/task-shell-default.ts +3 -2
- package/src/hub/tasks.ts +139 -7
- package/src/local/deny.ts +21 -0
- package/src/local/sandbox.ts +3 -1
package/docs/security.md
CHANGED
|
@@ -7,13 +7,13 @@ 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`, `terminal.open`, `instances`, `action_policy`) apply only from a config file git confirms nobody committed (`instances` and `action_policy` are read only from an untracked `.agenthub/config.local.json`; in `config.json` they are ignored even when untracked): `.agenthub/config.json` or `.agenthub/config.local.json`, matched by file identity so no other spelling the file system accepts slips past, and `.agenthub` itself not a committed symlink or submodule. Without a repository, or when git fails, they keep their defaults; an empty value always means the default. "Untracked" is answered by the repository that contains the project: a checkout copied or extracted into an unrelated repository, or into an ignored directory of one, is trusted like your own files. So a cloned repository cannot choose a launch command, a completion check, a gateway to send a key file to, a memory endpoint, a permission mode, the command that opens a terminal, or a wider sandbox. A command in `checks` runs as you, outside the local worker's sandbox, like a git hook. So do the `setup` and `verify` commands of a benchmark suite (issue #251): `ahub bench run <suite.json>` is a person's command, refused from an agent session, and it resets the work tree for each attempt, so it runs only with `bench.enabled`, at the root of a clean repository that tracks nothing under `.agenthub/`. Nothing in `routing.toml` or in task text is ever run. `routing.toml` and the other shared fields still come from the checkout, and they matter: `routing.toml` picks the models the local worker and the hub's inference use at your gateway and can turn the PII constraint off, and roles and budget shape who does what. Review them in a repository you do not trust. The hub's own git calls outside the sandbox (snapshots, facts, config trust, project discovery, memory recall, the bench runner) all use the single hardened implementation in the exact-pinned first-party `@staix/agent-mem/platform` package, re-exported by `src/hub/git.ts`, which disables the programs a repository's configuration can name — the file-system monitor, hooks, external diff, the pager, and the credential and askpass helpers, with prompts failing closed — so a config that ever does get written cannot run code with the hub's rights. Of those, the monitor, hooks and external diff are reachable by commands the hub issues (the planted-program test exercises all three; a `diff --no-index` reads the repo's config, so that call also passes `--no-ext-diff` and `--no-textconv`); the pager is not (every call pipes stdout). Credential and askpass helpers are disabled for project repositories; memory sync alone may use credentials for its own memory repository, without prompts. The cost: `core.fsmonitor=false` also turns off a person's enabled built-in monitor for the hub's own calls, so the per-turn snapshot's `add -A` scans the whole tree (the snapshot Git calls of one burst share a 250 ms budget, each call at most 10 s; a boundary that runs out is recorded as unknown, never as a stale baseline) (#576). git has no global switch for filter drivers, and of the hub's commands only the snapshot's `add`/`restore` would run one (its driver command lives in the protected `.git/config`).
|
|
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`, `terminal.open`, `tasks.auto_pull`, `instances`, `action_policy`) apply only from a config file git confirms nobody committed (`instances` and `action_policy` are read only from an untracked `.agenthub/config.local.json`; in `config.json` they are ignored even when untracked): `.agenthub/config.json` or `.agenthub/config.local.json`, matched by file identity so no other spelling the file system accepts slips past, and `.agenthub` itself not a committed symlink or submodule. Without a repository, or when git fails, they keep their defaults; an empty value always means the default. "Untracked" is answered by the repository that contains the project: a checkout copied or extracted into an unrelated repository, or into an ignored directory of one, is trusted like your own files. So a cloned repository cannot choose a launch command, a completion check, a gateway to send a key file to, a memory endpoint, a permission mode, the command that opens a terminal, or a wider sandbox. A command in `checks` runs as you, outside the local worker's sandbox, like a git hook. So do the `setup` and `verify` commands of a benchmark suite (issue #251): `ahub bench run <suite.json>` is a person's command, refused from an agent session, and it resets the work tree for each attempt, so it runs only with `bench.enabled`, at the root of a clean repository that tracks nothing under `.agenthub/`. Nothing in `routing.toml` or in task text is ever run. `routing.toml` and the other shared fields still come from the checkout, and they matter: `routing.toml` picks the models the local worker and the hub's inference use at your gateway and can turn the PII constraint off, and roles and budget shape who does what. Review them in a repository you do not trust. The hub's own git calls outside the sandbox (snapshots, facts, config trust, project discovery, memory recall, the bench runner) all use the single hardened implementation in the exact-pinned first-party `@staix/agent-mem/platform` package, re-exported by `src/hub/git.ts`, which disables the programs a repository's configuration can name — the file-system monitor, hooks, external diff, the pager, and the credential and askpass helpers, with prompts failing closed — so a config that ever does get written cannot run code with the hub's rights. Of those, the monitor, hooks and external diff are reachable by commands the hub issues (the planted-program test exercises all three; a `diff --no-index` reads the repo's config, so that call also passes `--no-ext-diff` and `--no-textconv`); the pager is not (every call pipes stdout). Credential and askpass helpers are disabled for project repositories; memory sync alone may use credentials for its own memory repository, without prompts. The cost: `core.fsmonitor=false` also turns off a person's enabled built-in monitor for the hub's own calls, so the per-turn snapshot's `add -A` scans the whole tree (the snapshot Git calls of one burst share a 250 ms budget, each call at most 10 s; a boundary that runs out is recorded as unknown, never as a stale baseline) (#576). git has no global switch for filter drivers, and of the hub's commands only the snapshot's `add`/`restore` would run one (its driver command lives in the protected `.git/config`).
|
|
11
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.
|
|
14
14
|
- **The session record holds identities only.** `.agenthub/state/sessions.json` (issue #37, mode 600) keeps each attached peer's recovery metadata: launch options, session and thread ids, Pi's session file path. No message or task text; loss notices name deliveries by id, sender and public task title. The Pi continuity bookmark that recovery keeps across stop and archive (`.agenthub/state/recovery-continuity.json`, mode 600, at most 16 KiB) holds identities only too: operation and project ids, the project root, and Pi's session id, session file, backend and model name; a bookmark that is not a private regular file, is larger than that or names another project or operation reads as invalid, which is kept distinct from no bookmark.
|
|
15
15
|
- **Context checkpoints have a separate text record.** Native occupancy readings carry numeric metadata only. An optional context checkpoint persists its summary in `.agenthub/state/context-checkpoint-<peer>.json` with mode 0600 and, when memory is enabled, proposes it as a machine-local memory candidate that only a person can accept. Completion requires the outstanding request id, current peer/native session and transport generation; new connection claims invalidate old requests. Private turns, every open PII task associated with the peer as owner or reviewer (including pending review), and PII-pattern summaries are refused before persistence or memory. Its text is never broadcast, and context pressure does not pause, reassign or replace the session.
|
|
16
|
-
- **The local worker is boxed in.** Paths are resolved through symlinks and must stay inside the project; a secrets denylist (`.env*`, keys, credential files, the hub's own state and the state archives `ahub reset --all` leaves) applies to its file tools, its git arguments and its memory capture alike; `.git` and `.agenthub` are not writable. Writes, edits, shell commands and mutating git wait for approval unless the operator's permission mode grants them (writes and edits inside the grant filter in `ask-when-needed`, all four in `never-ask`; see below), and the approver sees what will be written or run, with control characters escaped. Everything it executes runs under the macOS sandbox, attended or not. Since 0.10 the profile starts from deny default (issue #39): commands run and read only the system, toolchain and project directories (and the project's git dir and the selected developer dir; with network on, the public CA bundles); no writes outside the project, its git dir and a temp dir of its own (`TMPDIR`, made for each command and removed when it ends; issue #63); no `.git` name at any depth, closing the creation, rename, symlink and gitfile routes to a planted repository, no `config`, `config.worktree` or `hooks` directly under one, nor in any submodule git directory (every modules tree the project can reach is covered: the common dir's, each linked worktree's own under `<common>/worktrees/<wt>/modules/`, matched by pattern so a tree or worktree that does not exist yet is covered too, and each discovered submodule git directory's own `modules/`, nested submodules and a submodule's own linked worktrees included; under a modules tree every write that is not inside a discovered submodule git directory is refused, whatever its name, so a fake git directory cannot be planted for a later unsandboxed `git submodule update --init` to reuse; inside each discovered one its own `config`, `config.worktree` and `hooks` and its linked worktrees' `config.worktree` are refused, while refs, logs, objects, the index and `HEAD` stay writable, so commit, branch, tag, gc and rebase work; a linked worktree's git directory under `<common>/worktrees/` gets the same `config`, `config.worktree` and `hooks` refusal; every term is anchored at the discovered path, `hooks` by depth, so a directory named `config` or `hooks` above the project neither disables the rule nor trips it; the discovered git directories, the modules roots, the worktrees container, each worktree git directory and every component in between are also refused as targets, closing the rename, replace and symlink routes to the same files), no `commondir` at any depth below one, with every such name matched folded against the code points HFS+ ignores (the fold covers the security-relevant names; on a volume that folds names, folded spellings of the components between `.git` and a submodule's git directory are not matched — the 1024-byte SBPL literal cannot hold a folded path — the same residual the external git dir rules carry since #270), and no `.agenthub` anywhere in the project (in an external git dir the `config`/`config.worktree`/`hooks` rules cover only its own children and the hooks directory's contents, so refs named `config` or `hooks` still work); a nested repository in bare layout stays open — the directory is the git dir, with no `.git` name to anchor a rule on, and the hub never runs git there, whatever the project root is: its calls are all `-C <project root>` or `--no-index` on temp files, and none descends into a nested directory's git layout, so a planted hook fires only if a person or another tool runs git in that directory themselves; the rules are probed on HFS+, which folds the 16 code points the patterns tolerate, and on a case-sensitive APFS image, which folds nothing (a folded spelling is an inert new file there and the real names stay refused); the cost is that `git init`, `git clone` and `git worktree add` do not run inside the sandbox (add, commit, stash
|
|
16
|
+
- **The local worker is boxed in.** Paths are resolved through symlinks and must stay inside the project; a secrets denylist (`.env*`, keys, credential files, the hub's own state and the state archives `ahub reset --all` leaves) applies to its file tools, its git arguments and its memory capture alike; `.git` and `.agenthub` are not writable. Writes, edits, shell commands and mutating git wait for approval unless the operator's permission mode grants them (writes and edits inside the grant filter in `ask-when-needed`, all four in `never-ask`; see below), and the approver sees what will be written or run, with control characters escaped. Everything it executes runs under the macOS sandbox, attended or not. Since 0.10 the profile starts from deny default (issue #39): commands run and read only the system, toolchain and project directories (and the project's git dir and the selected developer dir; with network on, the public CA bundles); no writes outside the project, its git dir and a temp dir of its own (`TMPDIR`, made for each command and removed when it ends; issue #63); no `.git` name at any depth, closing the creation, rename, symlink and gitfile routes to a planted repository, no `config`, `config.worktree` or `hooks` directly under one, nor in any submodule git directory (every modules tree the project can reach is covered: the common dir's, each linked worktree's own under `<common>/worktrees/<wt>/modules/`, matched by pattern so a tree or worktree that does not exist yet is covered too, and each discovered submodule git directory's own `modules/`, nested submodules and a submodule's own linked worktrees included; under a modules tree every write that is not inside a discovered submodule git directory is refused, whatever its name, so a fake git directory cannot be planted for a later unsandboxed `git submodule update --init` to reuse; inside each discovered one its own `config`, `config.worktree` and `hooks` and its linked worktrees' `config.worktree` are refused, while refs, logs, objects, the index and `HEAD` stay writable, so commit, branch, tag, gc and rebase work; a linked worktree's git directory under `<common>/worktrees/` gets the same `config`, `config.worktree` and `hooks` refusal; every term is anchored at the discovered path, `hooks` by depth, so a directory named `config` or `hooks` above the project neither disables the rule nor trips it; the discovered git directories, the modules roots, the worktrees container, each worktree git directory and every component in between are also refused as targets, closing the rename, replace and symlink routes to the same files), no `commondir` at any depth below one, with every such name matched folded against the code points HFS+ ignores (the fold covers the security-relevant names; on a volume that folds names, folded spellings of the components between `.git` and a submodule's git directory are not matched — the 1024-byte SBPL literal cannot hold a folded path — the same residual the external git dir rules carry since #270), and no `.agenthub` anywhere in the project (in an external git dir the `config`/`config.worktree`/`hooks` rules cover only its own children and the hooks directory's contents, so refs named `config` or `hooks` still work); a nested repository in bare layout stays open — the directory is the git dir, with no `.git` name to anchor a rule on, and the hub never runs git there, whatever the project root is: its calls are all `-C <project root>` or `--no-index` on temp files, and none descends into a nested directory's git layout, so a planted hook fires only if a person or another tool runs git in that directory themselves; the rules are probed on HFS+, which folds the 16 code points the patterns tolerate, and on a case-sensitive APFS image, which folds nothing (a folded spelling is an inert new file there and the real names stay refused); the shared object store takes only new loose objects (#864): `objects/info` (alternates, http-alternates, commit-graph) and `objects/pack` are refused, and so are the `objects`, `objects/info` and `objects/pack` entries, moving a fan-out directory, and changing, removing or replacing an existing loose object, because git reads packs first and never rehashes what it reads, so a planted pack or alternate would change what every checkout and the implementer's host add, commit and push (#845) see; a hardlink to an existing object is refused as well; an object id that does not exist yet can still be planted before git writes it (its exact content must be predicted), which a local checkout then shows, while a push of it is rejected by the receiving side's rehash; the cost is that `git init`, `git clone` and `git worktree add` do not run inside the sandbox (add, commit, stash and checkout still do; `git gc`, `repack`, `fetch` and `maintenance` run outside it, #864), `git submodule update --init` does not either (its git directory's `config` write is refused), and neither do `git worktree remove`, `git worktree prune` of a stale record, `git clean -ffd` in a project with a nested repository, or `rm -rf` of a directory that holds a `.git` (refusing the unlink is what stops the rename-out route); no network, loopback included, unless `local.bash_network`; with it, only through the hub's egress proxy to the hosts in `local.network_allow` (issue #65), which refuses names that resolve to internal addresses (behind NAT64 with the well-known prefix, by the IPv4 address the answer carries; a network-specific prefix is not recognised), so claude-mem and the Codex app-server stay out of reach (`"direct"` opens everything, until 0.13.0). One channel stays open: `trustd`, which TLS clients need, can fetch a certificate's AIA or OCSP URL on a command's behalf, outside the proxy. The allow-default profile of 0.9 and earlier was removed in 0.12.0 (issue #83); a `local.sandbox` setting is ignored with a note. The shared temp dirs (the user's and `/private/tmp`) are closed. Without the sandbox there is no `bash` tool.
|
|
17
17
|
- **Capabilities are enforced, not suggested.** `capabilities` (issue #39) is checked by the daemon where task operations and messages arrive, so a peer cannot get round it by phrasing. A peer can never answer a permission request: the control link takes `permit` from the console role only, and a message that quotes a permit command is just text. Both bind the hub's own tool paths: a vendor agent with its own shell in the project (Codex, Claude, Kimi) can read `.agenthub/state/control-token` and connect as the console, which only the local worker's and Pi's sandbox prevents.
|
|
18
18
|
- **PII has an enforced path.** A task matching `signals.pii_patterns` goes to `local` or to nobody; its text is absent from other peers' envelopes, the console stream, the log and the board listing; the console user reviews it; `local` answers such a turn to the console only, keeps it out of its history, refuses it when the only gateway is off campus, and may not save notes or spin off tasks during it. Nothing about it enters hub-managed memory; local storage does not relax this privacy fence.
|
|
19
19
|
- **A model screen can add to the patterns (issue #198).** With `signals.pii_screen = "local"` in `routing.toml` (default `off`; it applies while `constraints.pii = "local_only"`), a newly proposed task is read by a model before the hub does anything else with it: until the verdict it is on no board, in no envelope, before no triage model, and the hub makes no brief or note call for it. The local worker's amem capture leaves its hub tool calls out (issue #230), so a proposal it makes in an ordinary turn cannot reach shared memory ahead of the verdict that way. The screen runs on the model on this machine (MLX or Ollama, loopback only) or, without one, on the gateway only while `onCampus()` holds, and the gateway call is refused once more right before transport if the gateway is behind Access; task text never goes off campus to be screened. When the on-device generation slot is taken (Pi generating, another screen), the screen goes to the campus gateway instead of waiting, and on campus a device that fails or is still loading after 60% of the deadline hands over to the gateway too; off campus it waits for the slot and the device within the deadline. With the legacy MLX provider the screen may start an `mlx_lm.server`, as Pi's relay does, and it stays running until `ahub models stop`. Its answer is `pii` or `clear` plus a category from a closed list (name, student id, phone, address, grade, health, other). Anything else, a timeout (8 s, one call and one generation slot per item), a missing or off-campus model, or text longer than 6000 UTF-8 bytes (about 2000 Hangul syllables) is `unknown`, and an unknown task is a PII task: this is the hub's one model call that fails closed, so with the screener down every new task waits for `local`. An unknown verdict has the costs of any PII task, even when a second look clears it later: opening it lifts every silent turn-free cohort for good and turns facts off while it is open, and off campus a proposal without a class is refused (triage is not asked for a PII task there), with an error that says so. Off campus with the on-device slot taken, a task stays `unknown` until it is screened again: while it is still proposed and no peer ever owned it, the hub's release timer (every 60 s, one task at a time, the least tried first, at most 10 times per task in a hub run; never text too long to read) asks again, and `clear` lifts its `pii` signal and routes it through the class peers (a named owner is not remembered; a reserved one is offered first, as always). A task `local` took keeps its PII path, also once it is declined back to nobody. A pattern match is PII without a call. The task's history records the source of its `pii` signal (`regex`, `screen` with the category, or `unknown` with the reason), an unknown verdict is a console line naming the task id only, and `events.jsonl` gets a `pii_screen` record with the item kind, label, source, category, reason and latency, never the text. A routing explanation of a draft does not call the screen and says so.
|
|
@@ -23,7 +23,7 @@ agent-hub connects agents that can each run commands. This page says what the hu
|
|
|
23
23
|
|
|
24
24
|
## Native memory authority and profile selection
|
|
25
25
|
|
|
26
|
-
The portable core is the exact-pinned first-party `@staix/agent-mem` 0.
|
|
26
|
+
The portable core is the exact-pinned first-party `@staix/agent-mem` 0.11.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.
|
|
27
27
|
|
|
28
28
|
`hub_memory_search`, `hub_memory_show` and `hub_memory_graph` take the project from the daemon, require `memory_read`, refuse private turns and validate UTF-8 input before logs or storage work. Complete records and graph metadata are screened before counts and truncation. Search is at most 20 lines/4 KiB (default 8); show accepts at most 8 ids and returns at most 8 KiB; graph is at most 120 nodes/240 edges/6 KiB (default 60 nodes), with no edge lacking an endpoint. Unknown, ambiguous and withheld ids share one neutral refusal. Arguments and results are excluded from hub-managed capture; this does not govern a vendor's independent transcript or memory hooks.
|
|
29
29
|
|
|
@@ -278,7 +278,7 @@ Please report vulnerabilities privately through GitHub's "Report a vulnerability
|
|
|
278
278
|
|
|
279
279
|
- Settings are a closed registry (`src/hub/settings.ts`). The dashboard and `ahub settings` can write only its keys, each checked against its own closed values, and only to `.agenthub/config.local.json` and `.agenthub/routing.local.toml`. A file git tracks is never written, so the shared `config.json` and `routing.toml` stay byte-identical; in a repository where git cannot answer, nothing is written either. Commands and binaries (`kimi_cmd`, `codex_bin`, `pi.cmd`, `checks`, `mlx`), credential files and gateway URLs (`omniroute`, `memory.worker_url`), the local worker's reach (`local.read_allow`, `local.bash_network`, `local.network_allow`), `bench.enabled`, `terminal` and `instances` are refused by name from every registry surface: they choose what the hub runs or where secrets and task text go. The structural editor below (#689) is the only other writer, and it keeps the executable and secret-locating keys console only.
|
|
280
280
|
- `routing.local.toml` holds `stay_switch` and, per class, `peers`, `escalate_to`, `route` (an id `routing.toml` defines) and `pi_backend`, and nothing else: targets, routes, signals and the PII constraint cannot be set through it. A write is read by the hub's own loader before it replaces the file (temp file and rename), and one previous version is kept for undo.
|
|
281
|
-
- An ordinary dashboard session (`ahub ui`) reads settings, lowers a mode to `ask`, turns `pi.auto_start`, `task_sweep.enabled` or `
|
|
281
|
+
- An ordinary dashboard session (`ahub ui`) reads settings, lowers a mode to `ask`, turns `pi.auto_start`, `task_sweep.enabled`, `task_sweep.auto_reassign` or `tasks.auto_pull` off, sets a start mode back to `tui` or a WIP limit to 0, and changes the safe switches (`research.enabled`, `approvals.notify`, `snapshots.enabled`, `coordination`). Whatever can widen what agents do without asking needs a settings session: a non-ask mode now or at hub start, routing, starting Pi with the hub, turning on automatic backlog pulls, removing a machine-local value, and undo. `ahub ui --settings` opens one: its ticket marks the session for 15 minutes, after which it is an ordinary session. `never-ask` also needs the peer's id typed as the confirmation, and a stored never-ask default still waits for the console's `y` at each hub start. An undo never puts never-ask back, and removing a machine-local value that uncovers a never-ask in the shared file needs the same confirmation. "Lowering to `ask`" means handing the decision back to the agent's own rules: for Codex that is the approval policy the person configured natively, which the hub does not judge.
|
|
282
282
|
- A settings session is as strong as the console for these settings and no stronger elsewhere. A mode change is the console's own request with the same refusals; tool approvals stay deny-only for Pi and local; there is still no shell, file, task-detail or launch-command API. `ahub ui --settings` and `ahub settings` pass the same identity gate as `ahub permission`, which is an operating policy, not authentication. The hostile-agent boundary above applies unchanged: an agent with an unrestricted shell as the same user can impersonate the console, and so can open a settings session.
|
|
283
283
|
- Every change writes a `settings` event (the key, the value before and after, the source `dashboard` or `terminal`) and a line in `hub.log` and open consoles; opening a settings session is logged too. The unified dashboard (`ahub ui --all`) forwards settings actions as an ordinary session and opens no settings session.
|
|
284
284
|
- Configuration reload (issue #439). The daemon watches `config.json`, `config.local.json`, `routing.toml` and `routing.local.toml` and re-reads the complete candidate after a change, as the dashboard's Reload configuration and `ahub settings reload` do; an invalid candidate applies nothing. Each changed key is then authorized on its own against its file's actual git provenance. A privacy tightening (local-only PII, the model screen on, added PII patterns, narrower roles or capability lists, unless it moves the planner or conductor) and a safe registered setting apply from any file. Every other change, machine-local fields included, keeps its running value when its file is tracked or git cannot answer. From an untracked file such a guarded change is adopted only by `ahub settings reload` in an interactive terminal, after it lists each guarded key with its full before and after values and the person types `y` within 30 seconds; the confirmation is bound to the file contents and accepted state it was shown for. File watches and dashboard reloads, a settings session's included, never confirm, and no reload changes the roster: membership moves only through the roster setting. Keys whose consumers are bound at start (memory, the gateway and MLX, checks, the privacy and sandbox policy of existing sessions, the terminal provider and others) stay pending until a restart.
|
|
@@ -4639,7 +4639,7 @@ resumeFromRoster?(): Promise<void>;
|
|
|
4639
4639
|
/** Fixed builtins only: a person's frontend attaches to the native session through this hub proxy. */
|
|
4640
4640
|
readonly frontendProxy?: FrontendProxyPort;
|
|
4641
4641
|
/** Fixed builtins only: a fixed operator notice; never native ids or provider text. */
|
|
4642
|
-
readonly statusNotice?: { code: "unidentified-cancellation"; message: string };
|
|
4642
|
+
readonly statusNotice?: { code: "unidentified-cancellation" | "qwen-detached" | "qwen-stalled"; message: string };
|
|
4643
4643
|
/** Fixed builtins only, and only with session.nativeRevert: roll the native conversation back to before this native turn. */
|
|
4644
4644
|
revertTurn?(nativeTurn: string): Promise<void>;
|
|
4645
4645
|
```
|
|
@@ -4671,7 +4671,7 @@ Who sets what (parity by construction; each value is today's member, renamed at
|
|
|
4671
4671
|
| `detachForRoster` | `WsPeer` (ignores the mode, `channel-peer.ts:91`), `CodexPeer` (`codex-appserver.ts:473`), `PiPeer` (`pi.ts:471`) | `daemon.ts:4758-4760` |
|
|
4672
4672
|
| `resumeFromRoster` | `PiPeer` (`pi.ts:496`) | `daemon.ts:4773` |
|
|
4673
4673
|
| `frontendProxy` | `CodexPeer`: `url` = `proxyUrl` (`:137`), `attached` = `proxyAttached` (`:488`), the two methods as at `:494`, `:500` | `daemon.ts:3701`, `:5198-5280` |
|
|
4674
|
-
| `statusNotice` | `CodexPeer` (`
|
|
4674
|
+
| `statusNotice` | `CodexPeer` (`unidentified-cancellation`) and the existing `QwenDualPeer` (`qwen-detached`, `qwen-stalled`), unchanged | fixed-builtin-gated status projection |
|
|
4675
4675
|
| `revertTurn` | `CodexPeer.revert` (`codex-appserver.ts:610`) | `daemon.ts:2446`, `:2459` |
|
|
4676
4676
|
|
|
4677
4677
|
Per-port absent and undefined semantics:
|
|
@@ -4924,7 +4924,7 @@ Common acceptance criteria:
|
|
|
4924
4924
|
- `frontendProxy` undefined refuses permission changes and `already`.
|
|
4925
4925
|
- The `unidentified-cancellation` notice shows only from a fixed builtin.
|
|
4926
4926
|
- The Codex sticky-override, restoring-turn, detached-clear and attached-refusal tests stay unchanged.
|
|
4927
|
-
- `isCodexPeer` rows leave the table.
|
|
4927
|
+
- The proxy/startup/revert `isCodexPeer` rows leave the table. The later `actionEnforcement` branch remains intentional: it reads Codex `actionGated` to choose the existing enforceability refusal. It is outside 2d and waits for a separately specified action-policy port; no `actionGated` member is added to `FrontendProxyPort`.
|
|
4928
4928
|
5. **2e: Claude launch proof extraction.** Area note: `daemon.md` (Claude launch and hook rules). Size M.
|
|
4929
4929
|
- Paths: new `src/hub/claude-launch-proof.ts`, `src/hub/daemon.ts` (hello, `permissionDisplay`, `changePermission`, facts), tests.
|
|
4930
4930
|
- No change to `src/cli/launch.ts`, `src/cli/facts-hook.ts`, `src/adapters/claude-channel.ts` or the plugin bundle.
|
|
@@ -5174,3 +5174,85 @@ Ceiling: a scan lists the board once per status revision (at most one per flush)
|
|
|
5174
5174
|
- `hub_task_show` also admits the configured planner (a peer holding the `planner` role, `Tasks.isConfiguredPlanner`, `ConductorHooks.isPlanner`) for any task outside the backlog (a staged backlog row stays hidden from agent task tools, #263, and is read through the planner tools; a peer that is also the conductor takes the audited conductor path). It gets exactly the conductor's view (`publicConductorTask` over `Tasks.publicView(task, true)`), so a PII task is still a stub with no history. The read writes no conduct audit event and grants nothing else: the planner cannot assign, escalate, hold or start through the conductor tools, and every other peer is refused with the conductor-role error as before.
|
|
5175
5175
|
- `hub_task_cancel` by the planner keeps every check it had (its own proposal, `backlog` or `proposed` state, planner authority, no private turn on ordinary work, the flow revision, recovery and shutdown exclusion) and replaces only the acceptance test with the evidence test `hub_backlog_return_untouched` uses (`taskHasWorkEvidence`): a branch, commit, PR, recorded local check, done, verdict or reopen refuses with the unchanged text `planner may cancel only its own historically unaccepted backlog or proposed work`. An accepted-then-declined task with none of them (the observed case, #454) is cancellable; the history keeps the acceptance and the decline.
|
|
5176
5176
|
- Reason: the planner could not read a task it had proposed once an owner took it, and could not clear the board of a task its owner declined untouched. The person's cancellation is unchanged.
|
|
5177
|
+
|
|
5178
|
+
## Accepted contract: external module-kind instances (#754)
|
|
5179
|
+
|
|
5180
|
+
**Status: accepted on 2026-10-27.** The [person's decision on #754](https://github.com/STAIxBWLB/agent-hub/issues/754#issuecomment-6441781951) selects O1 explicit module opt-in, O2 implementer/verifier/ordinary continuing reviewer, O3 live startup catalog only and O4 retained live-module recovery refusal. This is board task #287's contract-only deliverable against main `7a599197`. Implementation is a separate board task after this amendment merges; its plan must freeze context and retained-record field names before source enables this boundary. The existing A4 refusal in `validateInstances` and the external-definition validator stays authoritative. This amendment neither advertises a daemon feature nor proves a module or native account can run instances.
|
|
5181
|
+
|
|
5182
|
+
### Existing boundary and accepted scope
|
|
5183
|
+
|
|
5184
|
+
- Keep person-owned, untracked machine-local bundle declarations, content pins, daemon-startup-only imports and immutable project catalogs. CLI reads, previews, reloads and watchers never evaluate a module. A tracked, shared, cloned or unknown-provenance declaration grants nothing.
|
|
5185
|
+
- Keep the A4 executable subset: headless ACP, the public `AcpPeer` transport, bound peer id, `deliveryOnly === true`, `hubOwned === true`, `form === "headless"`, verified owned-group admission and load-session echo. A module pin authorizes daemon-privileged code; it is not a sandbox or a proof that an approved module cannot be malicious. An import timeout is not cancellation or rollback of privileged initialization.
|
|
5186
|
+
- Keep builtin namespace reservations and every fixed-builtin capability exclusion. No native engine, native/fresh-reviewer variant, host proof port, native home/model/provider selector, native hook, budget callback or arbitrary configuration callback becomes an external-instance capability. `instances` policies reserved for builtin definitions do not become module-supplied callbacks.
|
|
5187
|
+
- The accepted first slice requires explicit module opt-in and permits implementer, verifier and ordinary continuing reviewer instances within an explicit module-role subset and the kind ceiling. Every instance has its own managed worktree. Existing reviewer independence and MCP restrictions apply; planner, conductor and fresh/isolated reviewer capabilities remain excluded, with no claim of fresh-reviewer isolation. Semantic validation uses the live startup catalog, and live external controlled recovery remains refused.
|
|
5188
|
+
|
|
5189
|
+
### Stable module identity and a current startup binding
|
|
5190
|
+
|
|
5191
|
+
- A kind name, display label, instance suffix, roster row or adapter-reported generation is not module identity. Startup retains the provenance of each validated definition alongside its captured bundle: canonical project root, normalized project-relative declaration path, full lowercase SHA-256, exported kind, `ADAPTER_API` and a digest of the validated data-only capability/permission ceiling. Duplicate kinds, builtin/configured namespace collisions, wrong hash/API/kind, unsupported fields and unknown provenance refuse independently before any affected native start or bus admission.
|
|
5192
|
+
- Resolve `instances.<id>.kind` only against that daemon's verified startup catalog. Its module binding is the exact retained tuple, not a later path lookup or an entry from the process-global builtin registry. No kind is inferred from an id prefix to repair a missing declaration, and no missing external kind falls back to a builtin, configured kind or another module with the same label.
|
|
5193
|
+
- Keep stable identity separate from liveness: a new daemon has a new catalog epoch and new owner generations. Recovery may compare an exact stable tuple, but it never reuses an old catalog epoch as current-owner proof. Pin, declaration-path, exported-policy or instance-policy changes invalidate continuity; no automatic repin, path relocation, native-session rebinding or fresh-session fallback is implied.
|
|
5194
|
+
- Module and instance declarations remain restart-bound. A reload can report pending changes and revoke affected authority when its evidence no longer matches, but cannot import, repin, create a new catalog or silently publish the desired replacement policy as running policy.
|
|
5195
|
+
|
|
5196
|
+
### Instance declaration and opt-in policy
|
|
5197
|
+
|
|
5198
|
+
- Instance ids retain the existing `<declared-kind>-<name>` grammar, nonreserved ASCII id bound of 32 characters and project-wide maximum of 64 instances; modules retain their 16-entry bound. A kind too long to admit a valid instance id refuses; ids are never truncated or rebound. Existing builtin/configured declarations keep their current rules.
|
|
5199
|
+
- Under accepted decision O1, a newly pinned module explicitly declares a bounded own-data headless-instance opt-in. Its proposed logical fields are headless form and an allowed role subset. This is a new reviewed external data subset, not permission to declare builtin `instances`, reviewer backends or executable policy callbacks. Field/API compatibility names must be frozen in the separate implementation task plan; older hosts must clearly refuse the new subset rather than treat it as singleton support.
|
|
5200
|
+
- An instance declares only its kind, role subset, headless form, managed worktree and the existing permission/read-only selection. Command, argv, provider, model, native data home, environment overrides, launch callbacks and fresh review mode are outside the first slice. The host must newly derive a private per-instance state namespace and pass it with the exact instance id, execution root and MCP binding. Today the ACP context carries the shared daemon `stateDir`; it does not already provide that isolation. Keep canonical daemon control/MCP state separate from private instance state, and freeze the context field carrying the new namespace before implementation; config cannot choose a callback or alternate SDK.
|
|
5201
|
+
- Roles are intersected with the captured kind ceiling, the accepted instance-policy ceiling and host role rules at config validation, effective-role derivation, routing and tool admission. A later id-keyed role list cannot restore a refused role. Conductor and builtin-only planner eligibility stay excluded. Accepted O2 includes ordinary continuing reviewer instances alongside implementers and verifiers, only under the explicit module-role subset and existing reviewer-pool independence and MCP restrictions. An external reviewer instance never reviews a task it owns or implemented; the current reviewer-pool family and same-instance rules apply unchanged, as for builtin reviewers. Fresh mode and isolated builtin reviewer capabilities remain refused; every reviewer instance has its own worktree, without any claim of fresh-reviewer isolation.
|
|
5202
|
+
|
|
5203
|
+
### Execution root and non-transferable current-owner proof
|
|
5204
|
+
|
|
5205
|
+
- Every external instance, including a verifier, names its own existing `.worktrees/<id>` directory. This is intentionally stricter than today's generic verifier fallback. The host checks real directory identity, exact registered worktree root, same Git common directory and non-prunable inventory; no creation, repair, another checkout or canonical-root fallback occurs. A potentially writing instance requires an attached branch. Detached read-only execution needs an explicitly accepted read-only policy and cannot be used as write-owner evidence.
|
|
5206
|
+
- Before native spawn, freeze the module/instance binding and the validated cwd. An affected instance joins the bus or gains `ownerIsolation` only after the host has current evidence tying that exact runtime object and owned-child launch generation to that cwd, module tuple, instance policy and daemon/catalog epoch. A native session or MCP environment string alone cannot establish it.
|
|
5207
|
+
- The implementation must obtain that receipt from the actual managed ACP spawn/owned-process path, through a host-retained non-virtual binding, not a module's `recoveryMetadata().launch.cwd`, arbitrary getter or returned "proven" flag. Bypassing the managed spawn, returning the wrong transport/id/options or failing owned-group verification leaves no admissible receipt and refuses instance admission. A receipt describes the managed runtime; it does not confine daemon-privileged module code or all native tools to a filesystem sandbox.
|
|
5208
|
+
- Recheck current bus owner, child generation, startup binding, policy and the registered worktree before write/task-owner admission, after intervening awaits and immediately before effects. Replacing a directory, moving a branch required for writing, changing the module/policy binding, losing the owned process or receiving unreadable evidence revokes authority. An unavailable check is unknown, never isolation; retain a failed-start owner with unconfirmed processes for explicit teardown rather than assigning its id to a replacement.
|
|
5209
|
+
- Give every instance its own host records, queue/task identity, native session binding and MCP identity. Child environments replace inherited agent markers with the exact instance id. MCP access requires a host-issued current launch binding, rechecked after awaits; an old child, a base-kind child or a client that echoes another instance's env cannot act for it. Native configuration/credential-home isolation is not implied by worktree isolation, and no automatic native-home copying or credential migration is introduced.
|
|
5210
|
+
|
|
5211
|
+
### Permission ceilings and lifecycle
|
|
5212
|
+
|
|
5213
|
+
- A per-instance choice is machine-local and no wider than the captured module kind's native mapping and accepted host ceiling. An explicit managed mode must be advertised and acknowledged by that native session before bus admission. Missing mapping, failed acknowledgement or unknown state refuses that selection; it never becomes an implicit allow or a falsely claimed native `ask`. An omitted choice may retain the kind's existing unmanaged behavior, visibly unmanaged.
|
|
5214
|
+
- `readOnly: true` requires the kind's supported native read-only mapping and observed acknowledgement. It also applies the existing host ACP read/search/think restriction and read-only MCP/tool ceiling; neither confirmation, an allow-always choice nor session budgets raise it. Read-only and an independent `permission_mode` selection stay mutually exclusive. Neither read-only acknowledgement nor a pin claims kernel isolation.
|
|
5215
|
+
- Roster removal first fences dispatch, current tool authority and approvals, then stops the instance through the verified headless owned-process path. It does not detach a person's TUI or widen an external kind to a terminal form. Offline is insufficient if an owned process is pending or its absence is unknown: attempt only verified owned teardown, retain the owner/queue/in-flight uncertainty on failure, and perform no permission reset, task handoff, automatic restart or replacement until teardown is confirmed.
|
|
5216
|
+
- A successful removal preserves queued work, independent pauses and recorded history. Return to the roster does not itself start or resume a module instance. A later explicit start obtains a new owner/launch proof under the same accepted identity rules; stale tools and replies never revive a retired generation.
|
|
5217
|
+
|
|
5218
|
+
### Bounded CLI metadata without code evaluation
|
|
5219
|
+
|
|
5220
|
+
- The current `{kind, displayName}` metadata is insufficient for external-instance semantic validation. A supporting daemon must project a separate data-only view of its verified catalog binding and instance policy. Do not manufacture `start.create`, `instances` callbacks or executable `AdapterDefinition` values from display metadata to make a validator pass.
|
|
5221
|
+
- Proposed public view fields are bounded instance id, kind, display name, role names, headless/read-only and permission-state enums, availability/refusal code and an observation epoch/fingerprint. Keep at most 64 instance rows, kind/id at 32 characters, display names at 256 UTF-8 bytes and fixed refusal codes; truncate display only with an explicit incomplete indication, never silently validate a truncated inventory. Full pins/declaration provenance belong in authenticated person diagnostics. Module bodies, callback source, argv, native ids, provider errors, secrets and arbitrary native text are excluded.
|
|
5222
|
+
- `status`, `help`, `route explain`, previews and upgrade planning consume declarations plus that bounded host projection without importing modules or invoking module callbacks. Metadata explains observed state; it cannot register code, grant permissions, prove current ownership or authorize a start. A mutating/start request revalidates the current authenticated daemon instance, feature set, startup tuple and declaration revision, not metadata echoed by its caller.
|
|
5223
|
+
- Accepted O3 requires live-catalog semantic validation and keeps module declarations as `{path, sha256}`. Without a matching daemon, raw declarations may be listed as declared/unknown but cannot be resolved to an executable instance, assigned a guessed ceiling or used to prepare recovery. A cached projection never becomes runtime authority merely because its hash or label matches.
|
|
5224
|
+
|
|
5225
|
+
### Recovery compatibility and refusal before effects
|
|
5226
|
+
|
|
5227
|
+
- Reserve proposed daemon feature `external_adapter_instances`, in addition to the existing `external_adapter_modules` and `peer_instances`. It is not advertised by this amendment. Retained receipts and restart snapshots must carry a versioned stable binding containing canonical project root, instance id/kind, normalized module declaration path, full pin, API version, capability and instance-policy digests, and validated execution root. Integrity validation covers every field; any recorded native session is bound to that same tuple. Catalog epoch, runtime object identity and launch generation remain live host evidence, never portable ownership proof. Freeze the serialized schema in the separate implementation task plan and add the feature to every source/target incompatibility preflight. Old metadata cannot be silently read as that new proof.
|
|
5228
|
+
- A supporting coordinator/source must refuse a target missing any required feature before recovery holds, session capture, native stop, terminal close, commit, target startup or module evaluation for that operation. This applies even when the affected instances are offline: a target that cannot interpret their declarations/bindings is not compatible. Apply the same rule to reused staged targets, lease renewal and the last pre-commit read; once committed, retain the verified target/package binding rather than rediscovering identity from mutable config.
|
|
5229
|
+
- A source lacking the combined feature, an unreadable source feature/inventory response, or a declaration with no verified kind-to-pin binding also refuses before lifecycle effects. An older source's unknown/all-offline rows cannot waive the new external-instance floor. The CLI may conservatively report a possible unresolved module instance to refuse; that is not permission to invent its identity. Existing builtin/configured offline exceptions remain unchanged for those declarations.
|
|
5230
|
+
- The staged target's host features/SDK and captured declaration pins are checked without importing project modules. A supporting target later validates the same captured module tuple and policy before admitting any affected instance; changed/missing pin, incompatible API/policy, wrong project/root or incomplete retained binding refuses without reusing a session under another identity. Module evaluation already authorized at daemon startup is not rolled back; a post-import exported-kind mismatch must still prevent native spawn, bus admission and owner proof.
|
|
5231
|
+
- Accepted O4 retains A4's live-module controlled-recovery refusal. Stop affected module instances explicitly before the supported host restart/upgrade path; keep their stopped records and restore builtin peers normally. External instances remain stopped until a person's explicit start with the exact verified module/instance binding. A supplied recorded native session id is bounded and must receive a matching load-session echo; absent proof refuses and never falls back fresh. Automatic external-instance resume would require separately accepted source/target native-session, process-group and restoration receipts, not inference from `resumeById`.
|
|
5232
|
+
- Feature-aware binaries (and any explicitly designated compatibility backport) must reject unsupported module-instance declarations before project-module evaluation or native start. Already released A4 binaries cannot provide this guarantee: at baseline `7a599197`, daemon startup imports pinned modules before validating instance declarations, so direct execution can evaluate a module and then refuse the instance. A supporting coordinator must refuse that target before starting it; direct legacy execution remains an explicit migration limitation, not a promised safe path. No client attaches an instance id without the combined feature. Removing a declaration to cross an initial unsupported-source upgrade is an explicit person action with stopped/history evidence, never an automatic workaround; a backport/version floor requires separate acceptance.
|
|
5233
|
+
|
|
5234
|
+
### Acceptance matrix for the later implementation
|
|
5235
|
+
|
|
5236
|
+
These are required future proofs, not tests run or support enabled by this docs-only PR. Use fake bundled modules, fake ACP agents, isolated homes and host-side effect counters first; real native concurrency/session restoration needs its own declared receipt.
|
|
5237
|
+
|
|
5238
|
+
| Claim | Required positive and negative proof |
|
|
5239
|
+
|---|---|
|
|
5240
|
+
| A1 mixed catalog/identity | A builtin extra instance and implementer, verifier and ordinary continuing reviewer instances of an opted-in external kind coexist with distinct roots, native sessions, records, queues and MCP identities. The reviewer is admitted only within the explicit module-role subset and existing reviewer independence/MCP restrictions, with its own worktree and fresh mode refused. Missing opt-in, duplicate/colliding kinds, kind/pin/API/policy changes, ambiguous namespace and malformed/over-limit declarations refuse without rebinding. |
|
|
5241
|
+
| A2 current execution owner | Registered exact worktrees succeed. Missing, symlink-replaced, prunable, foreign-repository, canonical-fallback and detached-writer roots refuse. Swap the root/pin/policy or owner across each await; no stale write/owner admission occurs. A factory that lies in metadata or bypasses managed spawn cannot mint owner proof. |
|
|
5242
|
+
| A3 policy ceiling | Implementer, verifier and ordinary continuing reviewer roles and id-keyed overrides cannot exceed the explicit module-role subset or accepted kind/instance ceiling. Continuing reviewers cannot review tasks they own or implemented; existing reviewer-pool family, same-instance and MCP restrictions apply unchanged, and fresh mode is refused and planner/conductor remain excluded. Missing or unacknowledged native modes, read-only escalation and mutating MCP operations refuse before effects. Builtin-only fields, fresh reviewers, TUI/native engines, native proof ports and config callbacks remain refused, including explicit undefined declarations where presence is forbidden. |
|
|
5243
|
+
| A4 no-import CLI | With import/callback tripwires, exercise online and offline `status`, `help`, `route explain`, preview and upgrade planning. No module evaluation occurs; unknown/stale/foreign-epoch/incomplete metadata cannot enable a factory, guess a ceiling or grant ownership. Outputs respect all row/byte bounds and contain no argv/provider/source data. |
|
|
5244
|
+
| A5 roster and process lifetime | Fence an active delivery and approval before stop. Repeat with offline-pending and unknown owned groups: no handoff/reset/replacement until confirmed gone; queued and uncertain work is retained. Return and stale MCP/reply attempts cannot revive an old launch. |
|
|
5245
|
+
| A6 source/target compatibility | Test missing features on source and target, unknown/all-offline old rows, retained/reused target mismatch, changed pins/API/root/policy and feature-aware/backported binaries reading unsupported config. Counter assertions show zero holds/captures/stops/closes/commits/new imports/native starts at a supporting preflight refusal. Separately cover the unpatched A4 startup order with a harmless import tripwire: it may evaluate the pin before refusing the instance, and must not be represented as satisfying pre-import refusal. Legacy builtin/configured recovery remains byte/behavior compatible. |
|
|
5246
|
+
| A7 supported recovery ceiling | Under accepted O4, live external peers refuse controlled recovery; explicitly stopped instances stay stopped while compatible builtin recovery completes. Manual same-binding session load requires exact echo; missing/foreign echo and stale native ids refuse with no fresh fallback. Automatic resume is outside the accepted first slice; any later extension needs separate acceptance and its additional receipt matrix. |
|
|
5247
|
+
| A8 delivery gate | Amend the relevant source/target feature checks and docs in the implementation PR; focused mixed regressions, conformance, affected seeded guards and the full `scripts/check.sh` pass at its head with `check: OK`, as required by issue #754 AC5. This docs-only amendment may use `scripts/check.sh --changed`; that does not satisfy the later implementation gate. Report native/account checks and unknown coverage separately from fake/local/hosted evidence. |
|
|
5248
|
+
|
|
5249
|
+
### Decisions
|
|
5250
|
+
|
|
5251
|
+
The [person accepted these choices on 2026-10-27](https://github.com/STAIxBWLB/agent-hub/issues/754#issuecomment-6441781951). Contract acceptance does not enable implementation: A4's refusal remains until a separate implementation task delivers the required proofs.
|
|
5252
|
+
|
|
5253
|
+
| Decision | Accepted first-slice choice |
|
|
5254
|
+
|---|---|
|
|
5255
|
+
| O1 opt-in authority | Require an explicit bounded headless-instance policy in a newly pinned module; an existing singleton module does not acquire multiple ids by config alone. |
|
|
5256
|
+
| O2 initial roles | Implementer, verifier and ordinary continuing reviewer within an explicit module-role subset and the kind ceiling, with existing independence and MCP restrictions. Every instance has its own worktree. Planner, conductor and fresh/isolated reviewer capabilities stay excluded; no fresh-reviewer isolation is claimed. |
|
|
5257
|
+
| O3 offline declaration binding | Keep module declarations as `{path, sha256}` and require the matching authenticated live startup catalog for semantic instance resolution; offline rows remain declared/unknown. |
|
|
5258
|
+
| O4 controlled recovery scope | Preserve the live-module refusal. Stop the instance explicitly, then a person starts it again with the same binding; there is no automatic resume. |
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# 0.20.0 release verification
|
|
2
|
+
|
|
3
|
+
This record covers release preparation and source review. It claims no installation, no running-hub upgrade to 0.20.0 and no live TUI launch.
|
|
4
|
+
|
|
5
|
+
## Candidate
|
|
6
|
+
|
|
7
|
+
Version 0.20.0, prepared from main 8403b84d plus the agent-mem pin commit b72a4c24 (14 commits since v0.19.0, 3 of them `feat`; bump rule: minor). The release commits carry the `@staix/agent-mem` 0.11.0 pin, the version, the CHANGELOG section, the bundle (plugin 0.20.0, built with the pinned Bun 1.4.2 after a frozen install), the document corrections and the documentation stamps. `scripts/release-prepare.ts` (#893) wrote the version files and this report's skeleton.
|
|
8
|
+
|
|
9
|
+
What changed (see `CHANGELOG.md` for the full list):
|
|
10
|
+
- A bounded CI watcher per exact PR head with one `ci_completed` notice to the owner and the reviewer, and a held premature review re-offered on green (#785: #886).
|
|
11
|
+
- Opt-in, machine-local `tasks.auto_pull`: a freed planner slot pulls the top backlog card explicitly reserved for that peer (#785: #889).
|
|
12
|
+
- Release preparation as an agent task and a guarded one-shot tag that needs the person's consent (#787: #893).
|
|
13
|
+
- The local-worker and Pi sandbox adds only new loose objects to the shared git object store (#864: #881).
|
|
14
|
+
- Peer ports replace native type checks in the daemon (#625 slices 2b to 2e: #882, #887, #890, #891); behavior unchanged.
|
|
15
|
+
- `@staix/agent-mem` 0.11.0; the mawk progress filter (#883: #884); the quiet branch binding (#880: #885); the default-branch probe isolation (#863: #875); hang-watch test margins (#877); the accepted external module-instance contract (#754: #892, spec only).
|
|
16
|
+
|
|
17
|
+
## Reviews
|
|
18
|
+
|
|
19
|
+
Every PR below merged with exact-head CI green (ubuntu, macOS and seeded guards), no unresolved review thread and no pending change request; the conductor read each before merging. (#880 and #883 in the commit subjects are issues, fixed by #885 and #884.)
|
|
20
|
+
|
|
21
|
+
| PR | Task | Merge | Review |
|
|
22
|
+
| --- | --- | --- | --- |
|
|
23
|
+
| #875 (#863) | | 8122373b | merged earlier on 2026-10-27, exact-head CI green |
|
|
24
|
+
| #877 | | 8a48175a | merged earlier on 2026-10-27, exact-head CI green |
|
|
25
|
+
| #881 (#864) | 522 | d089ebaa | the person approved PR #881 at 0f70acb1 (codex read-only review addressed); the board approval was recorded by the person afterwards |
|
|
26
|
+
| #882 (#625 2b) | 348 | 8b2579e5 | claude, approved at the merged head |
|
|
27
|
+
| #884 (#883) | | 0afa6bdd | the person approved; codex read-only review (OCR delegation), no findings |
|
|
28
|
+
| #885 (#880) | | db50fddd | the person approved; codex read-only review (OCR delegation), no findings; backlog card 528 closed as superseded by it |
|
|
29
|
+
| #886 (#785 P1b) | 458 | 0682dd4c | claude approved at the board-bound 5b525c2e; the PR head a2d1f73d added only a real-daemon test and a host-only seam, reviewed, and the merge is pinned to it |
|
|
30
|
+
| #887 (#625 2c) | 349 | 201cc80a | claude, approved at the merged head |
|
|
31
|
+
| #889 (#785 P1c) | 459 | c2a8b121 | claude, after two change requests (admission recheck before the board write; seed rot on an anchored line), approved at the merged head |
|
|
32
|
+
| #890 (#625 2e) | 350 | fdc3f469 | claude, approved at the merged head |
|
|
33
|
+
| #891 (#625 2d) | 351 | 7a599197 | claude, approved at the merged head |
|
|
34
|
+
| #892 (#754 contract) | 287 | 9205ce15 | claude approved at the board-bound f181f51e; the PR head 6970d9a3 only clarified reviewer independence, reviewed, and the merge is pinned to it; the person decided O1 to O4 on #754 |
|
|
35
|
+
| #893 (#787 P3b) | 463 | 8403b84d | claude, after one change request (the CLI test reached the real origin), approved at the merged head |
|
|
36
|
+
|
|
37
|
+
## Native acceptance receipts
|
|
38
|
+
|
|
39
|
+
Receipts added under `docs/verification/` since v0.19.0: none.
|
|
40
|
+
|
|
41
|
+
This list is read from the repository, never written by hand. Native acceptance and the person's release consent are separate records: consent is `ahub sdlc authorize-release 0.20.0`, person only, and is not recorded in this file. The tag run refuses without it.
|
|
42
|
+
|
|
43
|
+
## Document review
|
|
44
|
+
|
|
45
|
+
Thirteen documents were stale against the 0.19.0 stamps (bc8a7f25). Each was read against the commits under its `docs/verified.json` paths and restamped at a9e2970d with `bun scripts/release-prepare.ts restamp ... --reviewed`.
|
|
46
|
+
|
|
47
|
+
| Document | Result | What changed |
|
|
48
|
+
| --- | --- | --- |
|
|
49
|
+
| `README.md` | corrected | release report link to 0.20.0; Status line written by release-prepare |
|
|
50
|
+
| `docs/quickstart.md` | corrected | install pin `v0.20.0`; new "Upgrading to 0.20.0" section (amem 0.11.0 for every shared home, CI watcher, `tasks.auto_pull`, object-store sandbox, quiet branch binding) |
|
|
51
|
+
| `docs/operations.md` | corrected | version and protocol-16 range to 0.20.0; `tasks.auto_pull` machine-local wording and settings row; CI watcher scope (not private, no restore after restart, 12-character head, console line for a person, no re-offer after a failure) |
|
|
52
|
+
| `docs/security.md` | corrected | `tasks.auto_pull` in the machine-local list and the ordinary dashboard-session rules |
|
|
53
|
+
| `docs/agent-notes/adapters.md` | corrected | new rule for the delivery, lifecycle and session ports (#625 2b-2c) |
|
|
54
|
+
| `docs/agent-notes/daemon.md` | corrected | dispatch through ports, predicates only at ratchet rows; claude-launch-proof.ts counted by the ratchet; `peer_stop` admission by `hubOwned` and `form` |
|
|
55
|
+
| `docs/agent-notes/bus.md` | corrected | delivery-only retry refusal keyed on `PeerAdapter.deliveryOnly` |
|
|
56
|
+
| `docs/agent-notes/tasks.md` | corrected | quiet branch-binding rule (#880); `ci-watch.ts` in scope; a background clause removed |
|
|
57
|
+
| `docs/agent-notes/tests.md` | corrected | release-script tests run against a scratch root (#893); the release scripts in scope |
|
|
58
|
+
| `docs/agent-notes/README.md`, `benchmarks.md`, `budget.md`, `local-worker.md` | accurate | checked against #881, #882, #886, #887, #889, #890 and #891 |
|
|
59
|
+
|
|
60
|
+
Also corrected, outside the stamped set: the `@staix/agent-mem` pin text in `docs/memory.md`; `AGENTS.md` area lists (`src/hub/ci-watch.ts` and the release scripts); `docs/verified.json` source paths for adapters, tasks and tests; the CHANGELOG `tasks.auto_pull` wording. `ahub help` matches `src/cli/main.ts` and `src/cli/launch.ts`: `src/cli/` and the adapter definitions are unchanged since v0.19.0, and no changed file adds a command or flag. Note for the next CooperBench collection: #881 changed `profile()` in `src/local/sandbox.ts`, a study condition (`docs/agent-notes/benchmarks.md`).
|
|
61
|
+
|
|
62
|
+
## Checks
|
|
63
|
+
|
|
64
|
+
- `node scripts/check-docs.mjs --require-fresh`: `docs: OK (0 stale documents)`.
|
|
65
|
+
- `bun x tsc --noEmit`: clean. `bun scripts/build.mjs --check`: fresh. `node scripts/check-package.mjs`: `package: OK (575 files, @staix/agent-hub@0.20.0)`.
|
|
66
|
+
- Full `scripts/check.sh` on Linux (remote_dev verify, mawk container): see the release PR.
|
|
67
|
+
- The release PR's exact-head CI run: see the release PR.
|
|
68
|
+
|
|
69
|
+
## Known gaps, not blocking
|
|
70
|
+
|
|
71
|
+
- No running hub was upgraded to 0.20.0 and no live TUI was launched; the installed hub on this machine is still 0.18.0.
|
|
72
|
+
- No new native acceptance receipt was recorded for this release.
|
|
73
|
+
- The global `amem` CLI on this machine is not yet 0.11.0; upgrade it with the hub (every amem sharing a memory home must be 0.11.0 or later).
|
|
74
|
+
- `test/memory-code-head-684.test.ts` (#367 cached HEAD proof) still flakes under local load (#519, assigned); CI is the gate.
|
|
75
|
+
- External module-kind instances (#754 implementation, PR #894) are not in this release.
|
package/docs/verified.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"schemaVersion": 1,
|
|
3
3
|
"documents": {
|
|
4
4
|
"README.md": {
|
|
5
|
-
"verifiedAgainst": "
|
|
5
|
+
"verifiedAgainst": "a9e2970da94d65cf5473bd9d3ddbe6193b9c3454",
|
|
6
6
|
"paths": [
|
|
7
7
|
"package.json",
|
|
8
8
|
"src/",
|
|
@@ -11,14 +11,14 @@
|
|
|
11
11
|
]
|
|
12
12
|
},
|
|
13
13
|
"docs/security.md": {
|
|
14
|
-
"verifiedAgainst": "
|
|
14
|
+
"verifiedAgainst": "a9e2970da94d65cf5473bd9d3ddbe6193b9c3454",
|
|
15
15
|
"paths": [
|
|
16
16
|
"src/",
|
|
17
17
|
"templates/"
|
|
18
18
|
]
|
|
19
19
|
},
|
|
20
20
|
"docs/operations.md": {
|
|
21
|
-
"verifiedAgainst": "
|
|
21
|
+
"verifiedAgainst": "a9e2970da94d65cf5473bd9d3ddbe6193b9c3454",
|
|
22
22
|
"paths": [
|
|
23
23
|
"package.json",
|
|
24
24
|
"src/",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
]
|
|
36
36
|
},
|
|
37
37
|
"docs/quickstart.md": {
|
|
38
|
-
"verifiedAgainst": "
|
|
38
|
+
"verifiedAgainst": "a9e2970da94d65cf5473bd9d3ddbe6193b9c3454",
|
|
39
39
|
"paths": [
|
|
40
40
|
"package.json",
|
|
41
41
|
"src/",
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
]
|
|
44
44
|
},
|
|
45
45
|
"docs/agent-notes/adapters.md": {
|
|
46
|
-
"verifiedAgainst": "
|
|
46
|
+
"verifiedAgainst": "a9e2970da94d65cf5473bd9d3ddbe6193b9c3454",
|
|
47
47
|
"paths": [
|
|
48
48
|
"src/adapters/",
|
|
49
49
|
"src/pi/",
|
|
@@ -74,11 +74,14 @@
|
|
|
74
74
|
"src/hub/permission-payload.ts",
|
|
75
75
|
"src/hub/read-only-shell.ts",
|
|
76
76
|
"src/hub/read-only-shell-proof.ts",
|
|
77
|
-
"src/hub/operational-authority.ts"
|
|
77
|
+
"src/hub/operational-authority.ts",
|
|
78
|
+
"test/daemon-native-branches.json",
|
|
79
|
+
"test/daemon-native-branches.test.ts",
|
|
80
|
+
"src/hub/claude-launch-proof.ts"
|
|
78
81
|
]
|
|
79
82
|
},
|
|
80
83
|
"docs/agent-notes/benchmarks.md": {
|
|
81
|
-
"verifiedAgainst": "
|
|
84
|
+
"verifiedAgainst": "a9e2970da94d65cf5473bd9d3ddbe6193b9c3454",
|
|
82
85
|
"paths": [
|
|
83
86
|
"scripts/benchmarks/",
|
|
84
87
|
"test/benchmarks/",
|
|
@@ -90,7 +93,7 @@
|
|
|
90
93
|
]
|
|
91
94
|
},
|
|
92
95
|
"docs/agent-notes/budget.md": {
|
|
93
|
-
"verifiedAgainst": "
|
|
96
|
+
"verifiedAgainst": "a9e2970da94d65cf5473bd9d3ddbe6193b9c3454",
|
|
94
97
|
"paths": [
|
|
95
98
|
"src/hub/budget.ts",
|
|
96
99
|
"src/cli/statusline-tee.ts",
|
|
@@ -114,7 +117,7 @@
|
|
|
114
117
|
]
|
|
115
118
|
},
|
|
116
119
|
"docs/agent-notes/bus.md": {
|
|
117
|
-
"verifiedAgainst": "
|
|
120
|
+
"verifiedAgainst": "a9e2970da94d65cf5473bd9d3ddbe6193b9c3454",
|
|
118
121
|
"paths": [
|
|
119
122
|
"src/hub/bus.ts",
|
|
120
123
|
"src/hub/envelope.ts",
|
|
@@ -129,7 +132,7 @@
|
|
|
129
132
|
]
|
|
130
133
|
},
|
|
131
134
|
"docs/agent-notes/daemon.md": {
|
|
132
|
-
"verifiedAgainst": "
|
|
135
|
+
"verifiedAgainst": "a9e2970da94d65cf5473bd9d3ddbe6193b9c3454",
|
|
133
136
|
"paths": [
|
|
134
137
|
"src/hub/daemon.ts",
|
|
135
138
|
"src/hub/control-client.ts",
|
|
@@ -202,7 +205,7 @@
|
|
|
202
205
|
]
|
|
203
206
|
},
|
|
204
207
|
"docs/agent-notes/local-worker.md": {
|
|
205
|
-
"verifiedAgainst": "
|
|
208
|
+
"verifiedAgainst": "a9e2970da94d65cf5473bd9d3ddbe6193b9c3454",
|
|
206
209
|
"paths": [
|
|
207
210
|
"src/adapters/local-worker.ts",
|
|
208
211
|
"src/local/",
|
|
@@ -222,7 +225,7 @@
|
|
|
222
225
|
]
|
|
223
226
|
},
|
|
224
227
|
"docs/agent-notes/tasks.md": {
|
|
225
|
-
"verifiedAgainst": "
|
|
228
|
+
"verifiedAgainst": "a9e2970da94d65cf5473bd9d3ddbe6193b9c3454",
|
|
226
229
|
"paths": [
|
|
227
230
|
"src/hub/tasks.ts",
|
|
228
231
|
"src/hub/board.ts",
|
|
@@ -256,11 +259,12 @@
|
|
|
256
259
|
"src/hub/deliverables.ts",
|
|
257
260
|
"src/hub/deliverable-files.ts",
|
|
258
261
|
"src/hub/sdlc-merge.ts",
|
|
259
|
-
"src/hub/sdlc-mirror.ts"
|
|
262
|
+
"src/hub/sdlc-mirror.ts",
|
|
263
|
+
"src/hub/ci-watch.ts"
|
|
260
264
|
]
|
|
261
265
|
},
|
|
262
266
|
"docs/agent-notes/tests.md": {
|
|
263
|
-
"verifiedAgainst": "
|
|
267
|
+
"verifiedAgainst": "a9e2970da94d65cf5473bd9d3ddbe6193b9c3454",
|
|
264
268
|
"paths": [
|
|
265
269
|
"test/",
|
|
266
270
|
"scripts/check.sh",
|
|
@@ -280,11 +284,15 @@
|
|
|
280
284
|
"scripts/check-closing-refs.mjs",
|
|
281
285
|
".github/workflows/closing-refs.yml",
|
|
282
286
|
"scripts/lib/ts-ast.ts",
|
|
283
|
-
"test/cli-json-guard.test.ts"
|
|
287
|
+
"test/cli-json-guard.test.ts",
|
|
288
|
+
"scripts/progress-tee.sh",
|
|
289
|
+
"scripts/release-prepare.ts",
|
|
290
|
+
"scripts/release-tag.ts",
|
|
291
|
+
"scripts/lib/release-plan.ts"
|
|
284
292
|
]
|
|
285
293
|
},
|
|
286
294
|
"docs/agent-notes/README.md": {
|
|
287
|
-
"verifiedAgainst": "
|
|
295
|
+
"verifiedAgainst": "a9e2970da94d65cf5473bd9d3ddbe6193b9c3454",
|
|
288
296
|
"paths": [
|
|
289
297
|
"src/cli/console-state.ts",
|
|
290
298
|
"src/cli/attention.ts",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@staix/agent-hub",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.20.0",
|
|
4
4
|
"description": "Native multi-agent hub: Claude Code, Codex, Kimi Code, Pi and local inference as peers in one project",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"bun": ">=1.4.2"
|
|
18
18
|
},
|
|
19
19
|
"dependencies": {
|
|
20
|
-
"@staix/agent-mem": "0.
|
|
20
|
+
"@staix/agent-mem": "0.11.0"
|
|
21
21
|
},
|
|
22
22
|
"devDependencies": {
|
|
23
23
|
"@modelcontextprotocol/sdk": "^1.27.1",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-hub",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.20.0",
|
|
4
4
|
"description": "Channel between Claude Code and the agent-hub daemon: peer messages from Codex, Kimi and the local worker arrive as channel events; hub_send replies.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Young Joon Lee",
|