@staix/agent-hub 0.25.0 → 0.27.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 +17 -0
- package/README.md +12 -6
- package/docs/agent-notes/README.md +12 -0
- package/docs/agent-notes/adapters.md +1 -1
- package/docs/agent-notes/daemon.md +7 -5
- package/docs/agent-notes/tasks.md +7 -6
- package/docs/agent-notes/tests.md +1 -0
- package/docs/dogfood.md +7 -1
- package/docs/operations.md +160 -17
- package/docs/profiles.md +1 -0
- package/docs/quickstart.md +18 -4
- package/docs/sdlc.md +3 -3
- package/docs/security.md +52 -9
- package/docs/specs/2026-09-19-agent-hub-design.md +1 -1
- package/docs/specs/2026-11-01-research-campaigns-design.md +21 -7
- package/docs/verification/2026-11-01-0.26.0.md +54 -0
- package/docs/verification/2026-11-01-0.27.0.md +49 -0
- package/docs/verified.json +28 -16
- package/package.json +1 -1
- package/plugins/agent-hub/.claude-plugin/plugin.json +1 -1
- package/plugins/agent-hub/server.js +190 -101
- package/src/adapters/channel-peer.ts +4 -2
- package/src/adapters/definitions/builtin-metadata.ts +6 -6
- package/src/adapters/definitions/qwen.ts +2 -0
- package/src/adapters/registry-types.ts +2 -0
- package/src/adapters/registry.ts +1 -0
- package/src/cli/conductor-recovery.ts +3 -2
- package/src/cli/help.ts +9 -8
- package/src/cli/identity.ts +5 -5
- package/src/cli/main.ts +68 -24
- package/src/cli/recovery-package.ts +94 -4
- package/src/cli/reset.ts +14 -4
- package/src/cli/runtime-version-warning.ts +28 -0
- package/src/cli/start-agents-run.ts +125 -0
- package/src/cli/study-output.ts +56 -0
- package/src/cli/study.ts +26 -3
- package/src/cli/task-output.ts +5 -0
- package/src/cli/upgrade-interactive.ts +4 -4
- package/src/cli/upgrade-runtime.ts +105 -57
- package/src/cli/upgrade.ts +90 -28
- package/src/hub/action-policy.ts +5 -3
- package/src/hub/autonomy-defaults.ts +32 -7
- package/src/hub/board.ts +129 -13
- package/src/hub/budget.ts +3 -4
- package/src/hub/child-process.ts +4 -3
- package/src/hub/conductor.ts +3 -4
- package/src/hub/config-apply.ts +13 -2
- package/src/hub/config-edit-host.ts +1 -0
- package/src/hub/config-tier.ts +3 -0
- package/src/hub/config-trust.ts +10 -2
- package/src/hub/control-client.ts +30 -10
- package/src/hub/daemon.ts +103 -26
- package/src/hub/db-schema.ts +143 -0
- package/src/hub/delivery-journal.ts +3 -4
- package/src/hub/events.ts +2 -1
- package/src/hub/execution-budget.ts +3 -4
- package/src/hub/folder-trigger.ts +3 -4
- package/src/hub/hub-tools.ts +7 -5
- package/src/hub/invocations.ts +3 -4
- package/src/hub/lifecycle.ts +87 -29
- package/src/hub/manager.ts +49 -33
- package/src/hub/no-writes.ts +1 -1
- package/src/hub/orca-env.ts +19 -0
- package/src/hub/project-runtime-state.ts +164 -0
- package/src/hub/project-runtime.ts +62 -0
- package/src/hub/protocol-window.ts +19 -0
- package/src/hub/recovery-store.ts +102 -12
- package/src/hub/registry.ts +5 -0
- package/src/hub/research-experiments.ts +33 -4
- package/src/hub/research-runner.ts +172 -0
- package/src/hub/restart.ts +9 -0
- package/src/hub/runtime-capture-prune.ts +415 -0
- package/src/hub/runtime-prune-health.ts +112 -0
- package/src/hub/schedules.ts +3 -4
- package/src/hub/sdlc-merge.ts +7 -0
- package/src/hub/sdlc-queue.ts +13 -1
- package/src/hub/sdlc-release-authorization.ts +11 -5
- package/src/hub/session-identity.ts +1 -1
- package/src/hub/settings.ts +4 -0
- package/src/hub/snapshots.ts +3 -4
- package/src/hub/start-agents.ts +44 -0
- package/src/hub/task-approval.ts +23 -0
- package/src/hub/tasks.ts +133 -34
- package/src/hub/usage-rollup.ts +3 -4
- package/src/pi/tool-receipts.ts +3 -4
- package/templates/presets/confirmation-matrix.json +5 -5
- package/templates/release-guard.block.md +2 -1
package/docs/operations.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Operations guide
|
|
2
2
|
|
|
3
|
-
This guide describes ahub 0.
|
|
3
|
+
This guide describes ahub 0.27.0 and control protocols 16 through 17. Live verification
|
|
4
4
|
results and remaining prerequisites are recorded separately in [the smoke ledger](smoke.md).
|
|
5
5
|
|
|
6
6
|
## Agent-operated live verification
|
|
@@ -1168,14 +1168,18 @@ and start): a committed file must not choose commands the hub runs. Keys that
|
|
|
1168
1168
|
are not task classes are ignored with a log line. A hub stopped mid-check leaves
|
|
1169
1169
|
the task in progress (`check interrupted`); the owner marks it done again.
|
|
1170
1170
|
|
|
1171
|
-
The owner may call `hub_task_done` again while the task is in review, with a
|
|
1172
|
-
different full lowercase commit SHA in `refs.commit` (#1000, #1021). It replaces the
|
|
1171
|
+
The owner may call `hub_task_done` again while the task is in review, or approved with its PR not merged, with a
|
|
1172
|
+
different full lowercase commit SHA in `refs.commit` (#1000, #1021, #1047). It replaces the
|
|
1173
1173
|
delivered artifact, not a verdict: the reviewer gets a fresh request for the new
|
|
1174
1174
|
head, the old CI watch is replaced, stale observed, reviewed and merge evidence is
|
|
1175
1175
|
cleared, and open Important findings, spec acceptance and the rejection count stay.
|
|
1176
1176
|
A configured completion check first returns the task to in progress until it passes.
|
|
1177
1177
|
The same head, a malformed SHA, another owner or a running check refuse without
|
|
1178
|
-
effect, and a verdict started on the old request no longer applies.
|
|
1178
|
+
effect, and a verdict started on the old request no longer applies. Reopening an
|
|
1179
|
+
approval first reads the PR state with `gh`: a merged PR, an unreadable state or a
|
|
1180
|
+
private task with a linked PR refuses without effect, and an approved task with no
|
|
1181
|
+
reviewer goes to the person. The reviewer may instead withdraw an approval of an
|
|
1182
|
+
unmerged PR with `hub_review` `changes_requested`, which returns the task to its owner.
|
|
1179
1183
|
|
|
1180
1184
|
While the project root checkout is on a branch other than its default
|
|
1181
1185
|
(`origin/HEAD`, else `main`), finishing a task adds `the main checkout is on
|
|
@@ -1484,6 +1488,7 @@ ahub study approve rc-0123456789ab 2 # person only: create the unit task
|
|
|
1484
1488
|
ahub study show [rc-0123456789ab] # list campaigns (50 per page, --after continues) or one projection
|
|
1485
1489
|
ahub study close rc-0123456789ab 2 --reason "answered"
|
|
1486
1490
|
ahub study approve-protocol <64-hex hash> # person only: approve one exact experiment protocol
|
|
1491
|
+
ahub study experiments --campaign rc-0123456789ab [--after 50] # read the recorded attempts (also --attempt ra-... or --plan <hash> --baseline ra-... --variant ra-...)
|
|
1487
1492
|
```
|
|
1488
1493
|
|
|
1489
1494
|
- **Contract.** The JSON names the question, scope, exclusions, registered source ids, 1 to 32 units
|
|
@@ -1514,13 +1519,39 @@ ahub study approve-protocol <64-hex hash> # person only: approve one exact ex
|
|
|
1514
1519
|
- **Experiments.** Protocols, comparison plans and attempts are stored with their provenance (`declared`,
|
|
1515
1520
|
`host-observed` from a check receipt or a hub-hashed deliverable), and `ahub study approve-protocol` binds an
|
|
1516
1521
|
approval to one exact protocol hash and its evaluator; a changed protocol needs its own approval. The hub
|
|
1517
|
-
never ranks results.
|
|
1518
|
-
|
|
1522
|
+
never ranks results. The current in-progress owner of a bound unit task records inert data through
|
|
1523
|
+
`hub_experiment_record`: `kind` is `protocol`, `plan`, `attempt`, `event` or `receipt`, using the existing
|
|
1524
|
+
protocol/attempt/event/plan envelope or `{ kind: "receipt", receipt, signature }` for a signed runner receipt.
|
|
1525
|
+
The person may relay a signed receipt through the authenticated console connection; this does not grant
|
|
1526
|
+
agents person authority or allow a person to bypass the current task/source/campaign checks. Every PII turn
|
|
1527
|
+
refuses the tool. It returns only record metadata (kind, ids/hashes, sequence and receipt id as applicable),
|
|
1528
|
+
and neither starts an experiment nor signs or verifies a receipt in the relay. Existing host evidence and
|
|
1529
|
+
signature verification remain inside `Tasks`. Read records with `ahub study experiments` or
|
|
1530
|
+
`hub_experiment_show` (see Experiment readback below).
|
|
1531
|
+
- **Experiment readback (#602, #932 T3).** `ahub study experiments --campaign <id>` lists a campaign's attempts
|
|
1532
|
+
(50 rows a page, `--after <cursor>` continues), `--attempt <ra-id>` shows one, and `--plan <hash> --baseline
|
|
1533
|
+
<ra-id> --variant <ra-id>` shows one comparison under a recorded plan. `hub_experiment_show` takes the same three
|
|
1534
|
+
forms and is a read for every local peer. It runs no command and reads no file; it renders what
|
|
1535
|
+
`Tasks.experimentShow` already returns. Every value says where it came from: `declared` (the agent's own word),
|
|
1536
|
+
`host-observed` (a check receipt or a hub-hashed deliverable), `runner-attested` (a signature-verified runner
|
|
1537
|
+
receipt) or `person-attested`. A declared value is never shown as observed, a metric or output the hub could not
|
|
1538
|
+
read is `unknown`, sealing is `declared` and `isolation unverified` unless a runner receipt supplies the
|
|
1539
|
+
evidence, and failed, cancelled and unknown attempts stay in the counts at the top. A repeat is never an
|
|
1540
|
+
independent reproduction, and a comparison whose fixed inputs, evaluator, split or metric units differ, or that
|
|
1541
|
+
changes an undeclared factor, reads `no: <field> differs` and is shown, never ranked. A private campaign, task or
|
|
1542
|
+
source shows ids, counts and states only. **Optional rerun checklist** (written for a person, never run by the
|
|
1543
|
+
hub): a person who wants stronger evidence reruns the protocol themselves, then attaches it through the existing
|
|
1544
|
+
paths and no new one: a check on the task (`hub_check_start`, #513 and the remote runner #793) makes the end
|
|
1545
|
+
event's exit status `host-observed`; an output file named in an `output` event is hashed by the hub (#509) and
|
|
1546
|
+
stays `unknown` when it is missing or denied; a registered runner's signed receipt (#1050) is the only source of
|
|
1547
|
+
`runner-attested` sealing and isolation. Without one of these the experiment is reported as declared and
|
|
1548
|
+
unverified, which is not the same as wrong.
|
|
1519
1549
|
- **Privacy.** A campaign whose parent task is PII, or that names a private source, is private: every view keeps
|
|
1520
1550
|
ids, counts, states and provenance and stubs its text as `[pii]`. Study tool arguments and results are kept out
|
|
1521
1551
|
of hub-managed memory capture.
|
|
1522
1552
|
- **Upgrades.** The daemon advertises `research_records`, `research_dispatch`, `research_dropped_offers`,
|
|
1523
|
-
`research_experiments` and `
|
|
1553
|
+
`research_experiments`, `research_comparisons` and `research_runners`. While the matching rows exist (for
|
|
1554
|
+
`research_runners`, stored runner receipts or any `research.runners` block), a restart, upgrade or
|
|
1524
1555
|
recovery onto a release without the feature is refused.
|
|
1525
1556
|
|
|
1526
1557
|
## Hub memory
|
|
@@ -2146,6 +2177,7 @@ A guarded file edit that the daemon keeps at its accepted value remains visible
|
|
|
2146
2177
|
| Permissions | `permission_modes.<peer>` (the default mode) | `config.local.json` | next peer start |
|
|
2147
2178
|
| Start | `peers.pi.start_mode`, `peers.codex.start_mode`, `peers.kimi.start_mode`, `peers.jcode.start_mode` (`tui` or `headless`) | `config.local.json` | that peer's next start |
|
|
2148
2179
|
| Start | `pi.auto_start` | `config.local.json` | next startup/recovery auto-start decision; never starts a peer merely on reload |
|
|
2180
|
+
| Start | `start.agents` (peer list; `none` for an empty list) | `config.local.json` | next use; the next person's `ahub up` reads it |
|
|
2149
2181
|
| Routing | `routing.stay_switch`, `routing.classes.<class>.peers`, `.escalate_to`, `.route`, `.pi_backend` | `routing.local.toml` | at once |
|
|
2150
2182
|
| Switches | `research.enabled`, `approvals.notify`, `snapshots.enabled` | `config.local.json` | live, without disturbing an active turn |
|
|
2151
2183
|
| Switches | `coordination` | `config.local.json` | newly created tasks only |
|
|
@@ -3025,6 +3057,95 @@ Agent CLIs update themselves often, and a hub release cannot follow every one. O
|
|
|
3025
3057
|
|
|
3026
3058
|
## Upgrade and crash recovery
|
|
3027
3059
|
|
|
3060
|
+
### Side by side project versions (#1039)
|
|
3061
|
+
|
|
3062
|
+
`ahub --project <path|id> upgrade --to <version>` reviews and changes only that
|
|
3063
|
+
project. With no selector, the current project is selected. Its recovery hold is
|
|
3064
|
+
per project, so another project can keep working, start, or perform its own
|
|
3065
|
+
upgrade. The registry and manager still serialize their short ownership writes.
|
|
3066
|
+
Legacy unscoped recovery receipts retain their machine-wide hold and original
|
|
3067
|
+
shared installation steps; finish or dispose those before a new project upgrade.
|
|
3068
|
+
|
|
3069
|
+
From 0.26.0, `ahub up`, manager Start and dashboard Start honor the selected
|
|
3070
|
+
project's `<state dir>/runtime.json`. It binds the project identity to a verified
|
|
3071
|
+
runtime version, root and capture digest. A caller at that version or later uses
|
|
3072
|
+
the retained runtime; an older caller refuses with `this project runs X; use X or
|
|
3073
|
+
later / ahub upgrade`. A scoped upgrade changes only that project's selection and
|
|
3074
|
+
does not refresh the shared manager. The daemon records its claimed instance,
|
|
3075
|
+
and readiness confirms the same selection before returning.
|
|
3076
|
+
|
|
3077
|
+
With no runtime record, the first managed startup captures the invoking package
|
|
3078
|
+
and its exact first-party memory core under
|
|
3079
|
+
`~/.agenthub/releases/<VERSION>/source-<digest>/`. Existing records with invalid
|
|
3080
|
+
bindings or missing/changed captures refuse rather than selecting another
|
|
3081
|
+
package. Runtime selection is machine-local state: a Git-tracked runtime record
|
|
3082
|
+
or indeterminate Git tracking check refuses before launch. Each startup checks
|
|
3083
|
+
only the runtime file's containing paths in Git once, with a bounded deadline;
|
|
3084
|
+
unrelated index entries are not enumerated. Plain directories without a
|
|
3085
|
+
repository remain supported. Capture reuse does not depend on a mutable global installation. Scoped
|
|
3086
|
+
upgrade targets use the same version directory, with the verified registry
|
|
3087
|
+
install kept separately in `registry/`. Recovery uses its authority receipt's
|
|
3088
|
+
exact target path and records that selection through the updated startup path.
|
|
3089
|
+
|
|
3090
|
+
This selector/refusal guarantee starts with 0.26.0. Published CLIs older than
|
|
3091
|
+
0.26.0 ignore the runtime record. After a scoped upgrade, invoke `ahub up` with a
|
|
3092
|
+
0.26.0-or-later CLI that is at least the selected project's version; keeping an
|
|
3093
|
+
older global CLI does not retrofit this protection. No database schema or
|
|
3094
|
+
bootstrap floor is raised for runtime selection. Tests of the updated resolver
|
|
3095
|
+
are not receipts from those older published binaries.
|
|
3096
|
+
|
|
3097
|
+
After a confirmed new startup, a separate, rate-limited maintenance worker keeps
|
|
3098
|
+
the newest three source captures per version plus every registered runtime, live
|
|
3099
|
+
manager runtime and retained recovery reference. Metadata snapshots and atomic
|
|
3100
|
+
quarantine use short registry writer transactions. Only removal candidates are
|
|
3101
|
+
hashed; package verification and recursive deletion run outside that lock and
|
|
3102
|
+
outside the startup result. Active recovery, uncertain owners, exhausted
|
|
3103
|
+
inventory budgets or an unreadable/incomplete inventory defer deletion.
|
|
3104
|
+
Only verified versioned `source-<digest>` directories are eligible;
|
|
3105
|
+
registry installs, unversioned coordinator sources, unknown layouts and symlinks
|
|
3106
|
+
remain. Reserved `.runtime-prune-*` quarantine paths cannot launch a managed
|
|
3107
|
+
runtime. Changed or unknown quarantined contents remain undeleted for inspection.
|
|
3108
|
+
Admission is home-wide, at most once per hour with at most three removals per
|
|
3109
|
+
run. `~/.agenthub/runtime-prune.db` records admission and the bounded outcome.
|
|
3110
|
+
Admission stores the worker's PID and process signature. The next admission
|
|
3111
|
+
atomically replaces a worker proven gone; live or uncertain owners remain
|
|
3112
|
+
fenced. Worker completion uses the normal database wait bound, while startup
|
|
3113
|
+
admission keeps its 1 ms contention bound. `ahub doctor` names an uncertain or
|
|
3114
|
+
stuck ledger and leftover quarantine paths without changing either. Leftover
|
|
3115
|
+
containers remain available for inspection even after a dead worker is replaced.
|
|
3116
|
+
Startup still succeeds when maintenance defers. A damaged runtime
|
|
3117
|
+
record/capture requires inspection or restoration.
|
|
3118
|
+
Archiving the record deliberately resets first-use selection; archived copies
|
|
3119
|
+
are not active pruning references.
|
|
3120
|
+
|
|
3121
|
+
A scoped upgrade leaves the global CLI and shared Claude plugin unchanged.
|
|
3122
|
+
Update the CLI separately with `bun add --global @staix/agent-hub@<version>`, then
|
|
3123
|
+
run `ahub setup` for the plugin. `ahub doctor` reports either tool when its known
|
|
3124
|
+
version is older than the selected running daemon; it installs nothing.
|
|
3125
|
+
|
|
3126
|
+
| Runtime | Control protocol window | Legacy status field |
|
|
3127
|
+
| --- | --- | --- |
|
|
3128
|
+
| 0.12.20 through 0.25.0 | 16 only | `protocol: 16` |
|
|
3129
|
+
| 0.26.0 through 0.27.0 | 16 through 17 | `protocol: 16`, `protocolMin: 16`, `protocolMax: 17` |
|
|
3130
|
+
|
|
3131
|
+
New clients choose the highest common protocol. The runtime supports N-1, so the
|
|
3132
|
+
published protocol-16 client and channel plugin remain usable; unrelated feature
|
|
3133
|
+
capabilities still require their own labels. A disjoint window refuses with 4426.
|
|
3134
|
+
The compatibility-floor `protocol` field remains readable by old clients.
|
|
3135
|
+
|
|
3136
|
+
Guard-aware runtimes read `hub.db` format metadata before startup effects and
|
|
3137
|
+
before writable database opens. A newer incompatible format refuses with
|
|
3138
|
+
`this project was opened by agent-hub X; use X or later`. The format counter
|
|
3139
|
+
advances for incompatible storage changes, not every package release. Controlled
|
|
3140
|
+
recovery refuses a target without `schema_guard` before source replacement. A
|
|
3141
|
+
fully committed legacy receipt continues from retained target identity evidence;
|
|
3142
|
+
new staging gates are not applied retroactively to a target already started.
|
|
3143
|
+
Published binaries from before this guard cannot enforce it; use their matching CLI for bootstrap and never open
|
|
3144
|
+
a newer database directly with an unguarded old binary. New scoped receipts use
|
|
3145
|
+
schema 3 so an older recovery coordinator refuses them rather than applying its
|
|
3146
|
+
machine-wide upgrade steps.
|
|
3147
|
+
|
|
3148
|
+
|
|
3028
3149
|
On a terminal, one command reviews, applies and follows an upgrade:
|
|
3029
3150
|
|
|
3030
3151
|
```bash
|
|
@@ -3042,8 +3163,8 @@ section is what they run.
|
|
|
3042
3163
|
|
|
3043
3164
|
Upgrade running projects with the target release's own coordinator. It accepts
|
|
3044
3165
|
a running source on control protocol 9 (0.6.x), 10 (0.7.0 through 0.12.0),
|
|
3045
|
-
11 (0.12.1 and 0.12.2), 12 (0.12.3), 13 (0.12.4 through 0.12.15), 14 (0.12.16), 15 (0.12.17 through 0.12.19) or 16 (0.12.20 through 0.
|
|
3046
|
-
a target
|
|
3166
|
+
11 (0.12.1 and 0.12.2), 12 (0.12.3), 13 (0.12.4 through 0.12.15), 14 (0.12.16), 15 (0.12.17 through 0.12.19) or 16 (0.12.20 through 0.25.0), plus the current 17/16 window, and only
|
|
3167
|
+
a target with a common protocol and the required recovery capabilities, so the target's coordinator fits every supported
|
|
3047
3168
|
source and carries every recovery fix released up to it. Protocol 8 and older
|
|
3048
3169
|
(0.5.x and earlier) are refused as `manual-bootstrap-required`. Run from the
|
|
3049
3170
|
project directory, without replacing the global CLI first:
|
|
@@ -3056,7 +3177,7 @@ bunx --package @staix/agent-hub@0.12.43 ahub upgrade --to 0.12.43 --yes
|
|
|
3056
3177
|
| Running now | Coordinator to use |
|
|
3057
3178
|
| --- | --- |
|
|
3058
3179
|
| 0.6.x (protocol 9) | the target's, through `bunx` as above |
|
|
3059
|
-
| 0.7.0 through 0.12.0 (protocol 10), 0.12.1 and 0.12.2 (protocol 11), 0.12.3 (protocol 12), 0.12.4 through 0.12.15 (protocol 13), 0.12.16 (protocol 14), 0.12.17 through 0.12.19 (protocol 15), 0.12.20 through 0.
|
|
3180
|
+
| 0.7.0 through 0.12.0 (protocol 10), 0.12.1 and 0.12.2 (protocol 11), 0.12.3 (protocol 12), 0.12.4 through 0.12.15 (protocol 13), 0.12.16 (protocol 14), 0.12.17 through 0.12.19 (protocol 15), 0.12.20 through 0.25.0 (protocol 16) | the target's, through `bunx` as above |
|
|
3060
3181
|
| any supported source, with the installed CLI already at the target | `ahub upgrade` below, which is the same coordinator |
|
|
3061
3182
|
| 0.5.x or earlier (protocol 8 and older) | not supported: bootstrap by hand with the matching CLI |
|
|
3062
3183
|
|
|
@@ -3080,11 +3201,10 @@ one queued); and one with the 0.11.0 coordinator from a 0.10.0 hub with
|
|
|
3080
3201
|
`local.bash_network` on.
|
|
3081
3202
|
|
|
3082
3203
|
The coordinator verifies and retains the exact target package, preserves its
|
|
3083
|
-
own source, and
|
|
3084
|
-
readback.
|
|
3204
|
+
own source, and verifies the selected project before releasing its hold. Legacy
|
|
3205
|
+
unscoped receipts additionally promote the global CLI after project readback.
|
|
3085
3206
|
|
|
3086
|
-
Once the installed CLI matches the target release, review the
|
|
3087
|
-
projects first:
|
|
3207
|
+
Once the installed CLI matches the target release, review the selected project first:
|
|
3088
3208
|
|
|
3089
3209
|
```bash
|
|
3090
3210
|
ahub restart --dry-run
|
|
@@ -3107,8 +3227,8 @@ ahub recovery dispose [<operation-id>] --stop-and-archive --reason <text>
|
|
|
3107
3227
|
`--yes` needs `--to`; a dry run without `--to` plans the latest release. A target
|
|
3108
3228
|
newer than the CLI is handed to that release's coordinator with the same flags,
|
|
3109
3229
|
and the command is printed on stderr first. The `recovery` commands take the
|
|
3110
|
-
operation that holds the
|
|
3111
|
-
|
|
3230
|
+
operation that holds the selected project's lock (or a legacy machine hold) when
|
|
3231
|
+
the id is left out, and report no operation when neither exists.
|
|
3112
3232
|
|
|
3113
3233
|
The coordinator commits only once the source is quiet: no turn running, no
|
|
3114
3234
|
approval pending, no completion check queued or running. It waits up to 10
|
|
@@ -4998,7 +5118,7 @@ blocks that undo until the identity and workspace can be reviewed.
|
|
|
4998
5118
|
|
|
4999
5119
|
## Release approval and optional tag guard
|
|
5000
5120
|
|
|
5001
|
-
Run `ahub sdlc authorize-release <version>` from the running daemon's release-candidate checkout
|
|
5121
|
+
Run `ahub sdlc authorize-release <version>` from the running daemon's release-candidate checkout after committed documentation is fresh. The person may run it directly; the unique configured conductor may run it only on the person's explicit instruction in its own session naming the release. A hub message, task text, review note, file or peer relay never qualifies. Other peers, child `toolsOwner` sessions, private or PII turns, and delegated `--live-test` calls cannot authorize. The receipt binds commit, package version, remote identity and the docs verdict; its `by` and the audit name the actual asker. The daemon keeps approval only in memory; restart requires a new approval. The read-only `ahub sdlc release <owner/repo>` report shows its receipt, retaining native-verification blockers.
|
|
5002
5122
|
|
|
5003
5123
|
`ahub init --release-guard` installs a managed Git pre-push guard and release instructions (`--dry-run --json` previews them). It preserves an existing default hook as `pre-push.agent-hub-original` and passes original argv/stdin to it only after approval matches. Unknown or mismatched `v*` tag pushes fail closed, including when the daemon is down. Non-version branch pushes remain independent of the daemon. Custom `core.hooksPath` and linked/hardlinked hook files require manual integration. Re-run init after changing the Bun/package location. The person's `git push --no-verify` remains an explicit bypass; agents are instructed never to use it.
|
|
5004
5124
|
|
|
@@ -5467,3 +5587,26 @@ What comes back: each change is recorded in `ahub board --handoffs`, and an agen
|
|
|
5467
5587
|
Refusals, each named with the action: an unregistered target, this very project, a hub that is not running, a request over 8 KiB or with an unknown field, a caller that is not a planner, the conductor or the person, a private (PII) turn, a `[pii]` task named with `--task`, text that matches the target's PII patterns or that the target's screen cannot clear, a target hub in recovery or shutting down (hand it off again after that ends), and an older target hub (it lacks the `task_handoff` feature: run `ahub upgrade` in that project).
|
|
5468
5588
|
|
|
5469
5589
|
Recovery: `task_handoff` is its own recovery label. A project that holds a handoff row, in either direction, refuses a recovery target that lacks it.
|
|
5590
|
+
|
|
5591
|
+
## The agents `ahub up` starts (#608, agent-hub#1040)
|
|
5592
|
+
|
|
5593
|
+
`ahub up` starts the hub, then asks it to start this project's default agents: `claude` and `codex` unless you chose others. Each goes through the same hub start as the dashboard's Start button and `ahub <peer>` from the console, so the peer's own start mode applies (`peers.<id>.start_mode`), the terminal comes from `terminal.open` or Orca, and a peer that is already attached answers `already attached`. There is no headless fallback: a peer whose TUI cannot be opened is refused with the command to run by hand.
|
|
5594
|
+
|
|
5595
|
+
```
|
|
5596
|
+
ahub up # the hub, then one line per agent
|
|
5597
|
+
ahub up --no-agents # the hub alone, for this run
|
|
5598
|
+
ahub setup --agents kimi,codex # this machine's choice, once
|
|
5599
|
+
ahub settings set start.agents claude,codex,kimi # this project's own list; none for no agent; inherit removes it
|
|
5600
|
+
```
|
|
5601
|
+
|
|
5602
|
+
```
|
|
5603
|
+
claude: started tui (terminal.open), it attaches when its TUI is ready
|
|
5604
|
+
codex: already attached
|
|
5605
|
+
kimi: not started: kimi starts in its TUI and the hub could not open a terminal for it (...). Run ahub kimi in a terminal
|
|
5606
|
+
```
|
|
5607
|
+
|
|
5608
|
+
- **Where the list comes from, in order:** `start.agents` in the project's `.agenthub/config.local.json` (an empty list starts none), else the machine default `start.agents` in `~/.agenthub/defaults.json`, else `["claude", "codex"]`. `ahub setup` asks once ("Agents to start with ahub up"); `--agents <a,b>` answers it, `--yes` keeps the current choice or the default, and a run with no terminal and neither flag keeps the default and says so. `ahub init` of a new project copies the machine default into its `config.local.json`; a project that has its own value keeps it, and nothing is written into a file git tracks.
|
|
5609
|
+
- **Machine-local and trust tier:** `start.agents` is in `ALL_MACHINE_LOCAL`: a committed `.agenthub/config.json` that sets it is ignored with a notice, because a clone must not open terminals on someone else's machine. It is a settings row in the Start group that raises what the hub does on its own, so it needs a settings session (`ahub ui --settings`) or a person's terminal. `defaults.json` stays owner-only (mode 0600, outside every project root); a `start` entry that is not an object with an `agents` list of allowed peers makes the whole file ignored with the existing notice.
|
|
5610
|
+
- **Who and when:** only a person's own `ahub up` and the dashboard manager's "start" (which counts as one). An agent's `ahub up` is refused by the CLI before this runs. A daemon restart, a crash recovery, a config reload (`start` applies at the next `ahub up`) and the `ahub up` that an upgrade or recovery relaunches (it carries `AGENTHUB_RECOVERY_OPERATION`) start nothing from the list, and the running hub never reads it. `--live-test` starts none either.
|
|
5611
|
+
- **What can be listed:** `claude`, `codex`, `kimi`, `pi`, `local` and `jcode`. `qwen` is refused when saved ("the hub never opens its TUI"), as are `agy`, a declared instance, a Claude channel extra, an unknown id and a duplicate, each with a named reason; the loader and the settings writer say the same words. Pi in the list is the user-facing switch; `pi.auto_start` keeps working for configs that have it.
|
|
5612
|
+
- **Failure is local to one agent:** a refusal, a terminal provider that fails or a request that times out prints its own line and never stops the hub or the other agents. The lines come from the hub's own reply, so a provider's error text stays in `hub.log`.
|
package/docs/profiles.md
CHANGED
|
@@ -22,6 +22,7 @@
|
|
|
22
22
|
- Example deterministic check: `bun /path/to/agent-hub/scripts/check-citations.ts report.md references.bib`. It checks bracketed Markdown `[@key; @other]` keys against BibTeX entry keys, with 256 KiB/4 MiB input caps. It performs no fetch, validates no source quality and is not a full Markdown/BibTeX parser. A missing key exits 1. A resolved bibliography is one check, not proof that claims are supported.
|
|
23
23
|
- Keep a person's sign-off as the final review. The live three-source/two-agent example remains unverified and was not run during solo implementation.
|
|
24
24
|
- A multi-unit study (#930, #601) goes through `ahub study`: the person (or a configured planner with `hub_study_draft` / `hub_study_revise`) stores a contract as a draft, only the person approves it with `ahub study approve <id> <revision>`, which creates one task per unit, and anyone reads coverage with `ahub study show [<id>]` or `hub_study_show`. A unit owner records `hub_study_handoff` with evidence for every source the unit names, or `insufficientEvidence`; a done task is not a handoff, and an incomplete handoff does not close a discovery round. A private campaign, task or source shows ids, counts and states only.
|
|
25
|
+
- Experiment attempts recorded on a study unit are read with `ahub study experiments --campaign <id>` (or `--attempt`, or `--plan` with `--baseline` and `--variant`) and `hub_experiment_show`. Each value shows its source: `declared` is the agent's own word, and only `host-observed`, `runner-attested` or `person-attested` rows are evidence, none of which proves the science. A rerun by a person attaches through an existing check, a hashed output file or a registered runner's signed receipt, as the checklist in `docs/operations.md` ("Experiment readback") says; without one the experiment stays declared and its sealing unverified. The synthetic CPU-only fixture is `test/research-experiments-fixture.test.ts`; no account, GPU or native model has run an experiment here.
|
|
25
26
|
- The deterministic three-source, two-peer example (`test/study-daemon.test.ts`, "three-source, two-peer example"): one contract names sources S0, S1 and S2 and two discovery units, `alpha` (S0 and S1, owned by codex) and `beta` (S2, an independent check, owned by kimi). After `ahub study approve` both units read `none` and `0/2 covered`, `0/1 covered`. A handoff for S0 only moves `alpha` to `incomplete` with `1/2 covered`. A handoff for both S0 and S1, and one for S2, make both `complete` and the discovery round complete. Another peer's handoff for `alpha` is refused. This runs with fake peers: it shows the hub's rules, not that a native model follows the guidance.
|
|
26
27
|
|
|
27
28
|
# General project example
|
package/docs/quickstart.md
CHANGED
|
@@ -17,7 +17,7 @@ ahub setup # installs the channel plugin and a pr
|
|
|
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.27.0
|
|
21
21
|
ahub setup
|
|
22
22
|
```
|
|
23
23
|
|
|
@@ -27,8 +27,17 @@ From a clone instead: `git clone`, `bun install`, `bun link`.
|
|
|
27
27
|
|
|
28
28
|
For managed launchers, two-agent task/review work, approvals, graceful shutdown,
|
|
29
29
|
and upgrade or crash recovery, see the [operations guide](operations.md). To
|
|
30
|
-
move running
|
|
31
|
-
terminal: it reviews the plan with you, applies it and follows
|
|
30
|
+
move a running project to a newer release later, run `ahub upgrade` in a
|
|
31
|
+
terminal in that project: it reviews the plan with you, applies it and follows
|
|
32
|
+
it; other projects keep their version.
|
|
33
|
+
|
|
34
|
+
## Upgrading to 0.27.0
|
|
35
|
+
|
|
36
|
+
Nothing changes until you use something new. Release consent can now come from your explicit instruction to the conductor: say in its own session which release to ship (for example "release 0.27.0"), and the configured conductor runs `ahub sdlc authorize-release <version>` for you, under the same admission as `sdlc merge`; you can still run it yourself. The approval keeps every binding it had (exact commit, version, origin and fresh docs; a restart revokes it), its record names who ran it, and a hub message, task text, review note, file or another peer's relay never counts as the instruction (#1063, #1066). Research experiments gain a read and a write: `ahub study experiments` and `hub_experiment_show` read a campaign's attempts, one attempt or one comparison (#602), and an owner records protocols, plans, attempts, events and receipts with `hub_experiment_record`, refused during a private turn (#615). Concurrent starts of one project no longer fail on a `runtime.json` replaced while it was being read (#616). As for 0.26.0, update the global CLI and the plugin yourself afterwards: `bun add -g @staix/agent-hub@0.27.0`, then `ahub setup`.
|
|
37
|
+
|
|
38
|
+
## Upgrading to 0.26.0
|
|
39
|
+
|
|
40
|
+
`ahub upgrade` now upgrades one project: the one in the current directory, or the one `--project <path|id>` names; other projects keep their running version (#1039, #1045). From a 0.25.0 CLI, run it in the project directory, since 0.25.0 refuses `--project` for upgrade; it hands over to the 0.26.0 coordinator as before. It no longer updates the global CLI or the shared Claude plugin: run `bun add -g @staix/agent-hub@0.26.0` and then `ahub setup` yourself, and `ahub doctor` warns while either is older than a running daemon. The daemon speaks control protocol 17 and still accepts 16, so a 0.25.0 plugin keeps connecting. Each project keeps its verified runtime across stop and start; start it with a 0.26.0 or later CLI, because an older CLI ignores that record (#1052). A daemon refuses a `hub.db` in a newer format and names the version it needs. `ahub up` now also starts `claude` and `codex` (see [First session](#first-session)): `ahub setup` asks once which agents (`--agents <a,b>`, `none` for no agent), `ahub settings set start.agents <a,b>` changes one project, and `ahub up --no-agents` skips them for one run; a restart, recovery or upgrade relaunch starts none (#1046). An owner can re-file `hub_task_done` on an approved, unmerged task with a new head, which sends it back to review, and a reviewer can withdraw an approval with `changes_requested` (#1049). Signed external runner receipts for research attempts are opt-in: the person registers a runner under `research.runners` in untracked `.agenthub/config.local.json` (#1050). Restart Claude sessions so they load the 0.26.0 plugin.
|
|
32
41
|
|
|
33
42
|
## Upgrading to 0.25.0
|
|
34
43
|
|
|
@@ -167,9 +176,14 @@ In your project directory, one terminal each:
|
|
|
167
176
|
|
|
168
177
|
```bash
|
|
169
178
|
ahub init # .agenthub/config.json, .agenthub/routing.toml, the marker block in AGENTS.md
|
|
170
|
-
ahub up # starts the daemon and
|
|
179
|
+
ahub up # starts the daemon, opens claude and codex in terminals (see below), then the operator console
|
|
171
180
|
ahub console # use in another terminal if up used --no-console; Tab opens panels
|
|
172
181
|
ahub kimi # Kimi in its own pull-only TUI; --mode headless for the hub-driven peer
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
`ahub up` also starts a default set of agents, `claude` and `codex` unless you choose others, each in a terminal the hub opens (`terminal.open`, or Orca). It prints one line per agent, and an agent it cannot open is named with the command to run yourself (`ahub claude`, `ahub codex`). Choose the set once with `ahub setup --agents kimi,codex` (or answer the question in `ahub setup`), change one project with `ahub settings set start.agents <a,b>`, and skip it for a run with `ahub up --no-agents`. Without a terminal provider nothing is opened and the hub keeps running; start them by hand:
|
|
185
|
+
|
|
186
|
+
```bash
|
|
173
187
|
ahub codex # Codex TUI, attached through the hub
|
|
174
188
|
ahub claude # Claude Code with the hub channel
|
|
175
189
|
```
|
package/docs/sdlc.md
CHANGED
|
@@ -389,7 +389,7 @@ A private (PII) task reads no eval metadata and records none.
|
|
|
389
389
|
|
|
390
390
|
## Person release authorization (#481 S6)
|
|
391
391
|
|
|
392
|
-
From the running daemon's candidate checkout, the person runs `ahub sdlc authorize-release <version
|
|
392
|
+
From the running daemon's candidate checkout, the person or the one configured conductor runs `ahub sdlc authorize-release <version>` (#1063). The conductor acts only on the person's explicit instruction in its own session naming that release. A hub message, task text, review note, file or another peer's relay is never that instruction; the daemon cannot verify this cooperative rule. A conductor request uses the tools role and refuses a child `toolsOwner`, private turn or PII context, exactly like `sdlc merge`. Other peers and `--live-test` refuse. Consent binds the exact current commit, its committed package version, origin fetch/push identity and fresh committed documentation evidence, all reread after the docs check. Receipt `by` and the append-only metadata audit identify the actual asker alongside the approval id, commit, version and docs digest/count. The authoritative ledger is daemon-owned memory: restart revokes every approval; audit history cannot restore one.
|
|
393
393
|
|
|
394
394
|
`ahub sdlc release <owner/repo>` shows a matching or stale authorization receipt. Authorization removes only the person-decision blocker. Native acceptance remains unverified and `ready` remains false until separately supported. No tag, workflow or publication is changed by authorization or readback.
|
|
395
395
|
|
|
@@ -403,13 +403,13 @@ Tracked npm cleanliness compares the cached index to HEAD and raw tracked blob b
|
|
|
403
403
|
|
|
404
404
|
## Release preparation and the guarded tag (#787 P3b)
|
|
405
405
|
|
|
406
|
-
Release preparation is an agent task; the release decision
|
|
406
|
+
Release preparation is an agent task; the release decision remains the person's. Consent may be recorded by the person or by the configured conductor on the explicit version-naming instruction above; preparation alone grants no consent.
|
|
407
407
|
|
|
408
408
|
`bun scripts/release-prepare.ts [--version X.Y.Z] [--dry-run]` is the mechanical half of AGENTS.md "Releasing" steps 1 and 2. On a `release/<version>` branch (it refuses `main`, a detached HEAD, a dirty tree and any other branch name) it reads the commits since the last `v*` tag, applies the rule (any `feat` subject bumps the minor, otherwise the patch; a version below the rule or not newer than the last tag is refused, so is a missing or empty `## Unreleased`), and writes `package.json`, the README `Status:` line, the CHANGELOG section (the Unreleased entries move under the version, Unreleased stays empty), the bundle (`bun run build`) and a verification report skeleton `docs/verification/<date>-<version>.md`. The skeleton lists the pull requests the commits name and the native acceptance receipts, which it reads from `docs/verification/` files added since the tag, never from what an agent writes. It leaves `TODO(agent)` markers for what only a review can state: each PR's review and CI, the document review, the checks and the known gaps.
|
|
409
409
|
|
|
410
410
|
It restamps nothing by itself. A stamp in `docs/verified.json` records that someone read what changed under a document, so `bun scripts/release-prepare.ts restamp <document>... --reviewed` sets `verifiedAgainst` to HEAD for the named documents only, refuses without `--reviewed`, refuses an unnamed or unknown document and refuses with staged changes. The agent runs it per document after reading the source commits, commits `docs/verified.json`, opens the release PR and merges it with a merge commit (never squash).
|
|
411
411
|
|
|
412
|
-
`bun scripts/release-tag.ts <version> [--execute]` is the one guarded tag run. Every run repeats the whole preflight and reports every refusal at once: a valid release version; `package.json`, the README Status line and a CHANGELOG section at HEAD all naming it; HEAD on `main`, clean, and the merge commit of a `release/<version>` PR (a squash is refused); HEAD an ancestor of `origin/main` (fetched, not assumed); no `v<version>` tag locally or on the remote; `scripts/check-docs.mjs --require-fresh --committed` clean; and exactly one `docs/verification/<date>-<version>.md` with no `TODO(agent)` left. Without `--execute` it only reports, and whether the person's consent matches (exit 2 when it does not). With `--execute` it reads the consent through `ahub sdlc release-guard -- git push <remote> <commit>:refs/tags/v<version>` (read only; the ledger `ahub sdlc authorize-release` fills
|
|
412
|
+
`bun scripts/release-tag.ts <version> [--execute]` is the one guarded tag run. Every run repeats the whole preflight and reports every refusal at once: a valid release version; `package.json`, the README Status line and a CHANGELOG section at HEAD all naming it; HEAD on `main`, clean, and the merge commit of a `release/<version>` PR (a squash is refused); HEAD an ancestor of `origin/main` (fetched, not assumed); no `v<version>` tag locally or on the remote; `scripts/check-docs.mjs --require-fresh --committed` clean; and exactly one `docs/verification/<date>-<version>.md` with no `TODO(agent)` left. Without `--execute` it only reports, and whether the person's consent matches (exit 2 when it does not). With `--execute` it reads the consent through `ahub sdlc release-guard -- git push <remote> <commit>:refs/tags/v<version>` (read only; the ledger `ahub sdlc authorize-release` fills from the person or the explicitly instructed configured conductor, revoked by a hub restart), repeats the preflight, checks the commit did not move, reads the consent once more, and then makes the single push of that exact commit. A rejected push is reported and never retried, and the script passes neither `--force` nor `--no-verify`; the pre-push hook from `ahub init --release-guard` still runs on that push. The push creates a lightweight tag (`<commit>:refs/tags/v<version>`), the exact form `ahub sdlc release-guard` parses and the consent receipt binds; the repository's tags are mixed (v0.19.0 and v0.18.0 are annotated, v0.17.0 is not) and `.github/workflows/release.yml` accepts either (`v*`, then `--verify-tag`), so this is a convention difference, not a gap. If the project wants annotated release tags, create the tag object first and push the tag ref, which the guard also accepts (its source object is peeled to the commit); that is a later change to this script. `AHUB_RELEASE_ROOT` points both scripts at a checkout other than the one they live in (the tests use it so nothing outside a scratch repository is touched). Neither script calls `authorize-release`: the report only names the command; the person or the explicitly instructed configured conductor records consent separately. Publication (npm, the GitHub release) stays with the tag-triggered workflow, which has its own gate (`.github/workflows/release.yml`).
|
|
413
413
|
|
|
414
414
|
The scripts and their pure helpers live in `scripts/release-prepare.ts`, `scripts/release-tag.ts` and `scripts/lib/release-plan.ts`; they are maintainer tooling and are not part of the published package. The deterministic recovery barriers of #787 AC3 are not needed by either leg and are not part of this change.
|
|
415
415
|
|
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, a person's console `y` for a config default at hub start, or retained autonomy consent bound to that peer's exact never-ask value in the untracked `config.local.json` (#999, below). Without one of these 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` (the `every`, `cron` and `tz` forms alike), `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`).
|
|
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` (the `every`, `cron` and `tz` forms alike), `folder_triggers`, `corpus.paths`, `invocation`, `start.agents`, `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, which agents `ahub up` opens terminals for, 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.
|
|
@@ -819,7 +819,7 @@ The review brief (#520) withholds private changes whole (both sides of a move) a
|
|
|
819
819
|
|
|
820
820
|
Deliverable records (#509a). `hub_task_done` and `hub_sdlc_record` name files as `{path, format, version?}`; the hub hashes them itself and refuses a claimed `sha256`, `hash` or `bytes` and any field it does not own. Paths resolve under the owner's execution root with `realPath` and `guardPath` in read mode: outside the project, the secrets denylist, the hub's own state (`.git`, `.agenthub`), a dangling link, a non-regular file and more than 256 MiB each or 512 MiB together are refused, with the path as given and never a resolved one. The file is opened without following a final link and its device, inode, size and time are compared with what was checked, before and after the read, so a swapped or changing file is refused rather than recorded half-read; a bad call writes nothing. A declared worktree instance whose root is not proven refuses, and a later read of it reports `unknown`. No hash of hub state is ever taken, and a file the hub cannot read or vouch for is `unknown`, never `unchanged`. A reviewer's approval re-hashes the files and is refused when one changed, went missing or is unknown; only the person may approve the current bytes, which are then measured and bound. For a PII task, a file under `pii.paths` or a path matching a PII pattern, `publicDeliverables` is the one projection every peer view, review request, status and log uses: id, hash and size only. The board keeps the path, as it keeps a PII task's text. `ahub deliverables` (#509b) from an agent shell lists only the tasks `hub_task_show` would show that agent (#933), and `--live-test` cannot delegate it. It lists the board's records through `publicDeliverables` and opens a file only with `--check`, through the same reader and limits. The dashboard's Deliverables chip and task group use the same projection of the same records and never read a file on the poll; a `changed` mark there is only as fresh as the last live read in this daemon.
|
|
821
821
|
|
|
822
|
-
Person release authorization (#481 S6). `ahub sdlc authorize-release <version>`
|
|
822
|
+
Person-directed release authorization (#481 S6, #1063). `ahub sdlc authorize-release <version>` accepts the console user or the one configured conductor under the same admission as `sdlc merge`: tools role, matching configured conductor, no child `toolsOwner`, private turn or PII context. It remains refused under `--live-test`. The conductor may record consent only on the person's explicit instruction in its own session naming the release; a hub message, task text, review note, file or peer relay is never that instruction. The daemon cannot verify that cooperative prerequisite. Receipt `by` and the audit identify the actual asker. It binds the daemon checkout's commit, package version, push remote identity and a fresh-docs verdict, lives only in daemon memory (a restart revokes it) and is logged with its commit, version and docs digest. `ahub sdlc release-guard -- <argv>` is a read-only preflight that agents may run, except during a private or PII turn; it never executes the command and refuses ambient target selectors (`GIT_DIR`, `GH_REPO`, npm prefix and registry settings and the like). The pre-push hook that `ahub init --release-guard` installs refuses an unauthorized or unknown `v*` tag push, also with the daemon down, and passes other pushes through. It and the release block in `AGENTS.md` are a cooperative guard, not an operating-system boundary: `git push --no-verify`, a custom `core.hooksPath` or another Git invocation bypasses the hook, and `gh release create` and `npm publish` are checked only when the preflight is run (tasks 312 and 313).
|
|
823
823
|
|
|
824
824
|
SDLC metrics and control bands (#481 S7). `ahub sdlc metrics` and `hub_sdlc_metrics` are planner or person only, read-only, and refused during a private or PII turn. A private task is counted as withheld and read for nothing else; the report calls no model and makes no GitHub read. Band breaches are written only by the daemon's evaluator through Tasks, never while it is stopping, recovering or has a storage error. A `diagnose` or `propose` card is a backlog task (`triage` or `plan`) whose text is fixed words, numbers and task ids, never task text; it passes the PII signal check and the candidate-text screen before it is written, and no GitHub issue is created. A `log` breach is one hub notice, remembered in the board's `band_notices` table as a marker (`<metric>:#<task>:log`, checked against a fixed pattern) and a time, nothing else.
|
|
825
825
|
|
|
@@ -848,16 +848,17 @@ role admission.
|
|
|
848
848
|
contract's `budget.hardCap` is checked against that exact budget id at approval
|
|
849
849
|
and again at every dispatch and owner change; another budget covering the task
|
|
850
850
|
proves nothing. A hold never stops a running native turn.
|
|
851
|
-
- **Provenance
|
|
852
|
-
|
|
853
|
-
or
|
|
854
|
-
is never host evidence. `ahub study approve-protocol` binds approval to one exact
|
|
851
|
+
- **Provenance stays separate.** Experiment declarations remain `declared`. Separate
|
|
852
|
+
`host-observed` values come from a finished check receipt for the same task and
|
|
853
|
+
commit, or from hashing a deliverable through the #509 path policy; an
|
|
854
|
+
agent-supplied hash is never host evidence. `ahub study approve-protocol` binds approval to one exact
|
|
855
855
|
protocol hash and evaluator identity hash, and nothing copies it to a changed
|
|
856
856
|
protocol. Environment descriptors are refused when they contain assignment
|
|
857
857
|
forms, Bearer credentials or known secret shapes; that screen detects those forms
|
|
858
|
-
only and does not prove a secret is absent.
|
|
859
|
-
|
|
860
|
-
|
|
858
|
+
only and does not prove a secret is absent. Signed external receipts use the
|
|
859
|
+
separate `runner-attested` level described below; their fields never overwrite
|
|
860
|
+
declared or host-measured evidence. Public experiment recording tools and the
|
|
861
|
+
receipt CLI are the separate #602 slice.
|
|
861
862
|
- **Privacy.** A campaign whose parent task is PII, or that names a private source,
|
|
862
863
|
is private, and so is an attempt with a private task, input or output path.
|
|
863
864
|
Public views keep ids, counts, states and provenance and stub every question,
|
|
@@ -869,6 +870,48 @@ role admission.
|
|
|
869
870
|
`research_*` feature is refused while campaign, dispatch, re-offer, experiment or
|
|
870
871
|
comparison rows exist, or while `research.runners` is configured.
|
|
871
872
|
|
|
873
|
+
### Registered external runner receipts (#1018)
|
|
874
|
+
|
|
875
|
+
A runner registration is a trust root, not permission to execute a command. The
|
|
876
|
+
person registers an Ed25519 SPKI public key, allowed project ids and a revocation
|
|
877
|
+
flag under `research.runners` in untracked `.agenthub/config.local.json`. Shared,
|
|
878
|
+
tracked or untrusted sources refuse even an empty block. Whole-block structural
|
|
879
|
+
edits require the person's tier2 confirmation; the conductor cannot register a
|
|
880
|
+
key. The hub stores no private key, contacts no runner and schedules no compute.
|
|
881
|
+
|
|
882
|
+
Receipt admission verifies the domain-separated canonical body, signature,
|
|
883
|
+
current accepted registration and exact project/attempt/protocol binding. A
|
|
884
|
+
commit-based protocol also requires an explicitly supplied commit claim to match.
|
|
885
|
+
The current owner or the person may relay a receipt; signature authority never
|
|
886
|
+
bypasses current task, source, campaign, privacy or request authority. All checks
|
|
887
|
+
are repeated after asynchronous screening before any write. An unknown/revoked
|
|
888
|
+
key, foreign project, changed identity or tampered signature refuses before write.
|
|
889
|
+
|
|
890
|
+
Stored fields are `runner-attested`, including claimed timestamps, exit status,
|
|
891
|
+
image, network, resources and file hashes. Missing fields remain unknown. A signed
|
|
892
|
+
commit for a deliverable-based protocol stays an independent claim with unknown
|
|
893
|
+
code match. Sealing is attested only with distinct candidate/evaluator containers,
|
|
894
|
+
valid mount references and nonempty evaluator code/data mounts that are all
|
|
895
|
+
read-only and absent from candidates. Other receipts retain declared/unverified
|
|
896
|
+
sealing. This is a signed runner statement, not host observation or proof of
|
|
897
|
+
scientific correctness, isolation enforcement or evaluator secrecy.
|
|
898
|
+
|
|
899
|
+
An identical canonical receipt and signature with a known receipt id returns its
|
|
900
|
+
stored proof without re-verification or writes, including after revocation. A
|
|
901
|
+
changed body/signature at that id refuses; every new id needs a currently trusted
|
|
902
|
+
key. Readback retains as-of-signing provenance and separately shows whether the
|
|
903
|
+
signing key is active, revoked, replaced or unavailable now. Private records stub
|
|
904
|
+
the signed fields, mount/output names, signer identity and key details.
|
|
905
|
+
|
|
906
|
+
The first stored attestation, its event and the `hub.db` format-2 marker commit in
|
|
907
|
+
one transaction; unused databases keep the format-1 baseline. Recovery requires
|
|
908
|
+
`research_runners` for stored receipts or raw/accepted registration presence,
|
|
909
|
+
including empty blocks. Its release-equality integrity digest covers stored proofs only and is present
|
|
910
|
+
only when receipts exist. Accepted registrations remain configuration, governed by
|
|
911
|
+
the feature floor and the config-reload path; pending edits do not change the
|
|
912
|
+
board-state equality required for release. Earlier research guards remain intact. Fixtures
|
|
913
|
+
use test key pairs with no GPU, runner network request or live account.
|
|
914
|
+
|
|
872
915
|
## Cross-project task handoff (#604, #1030)
|
|
873
916
|
|
|
874
917
|
A handoff crosses a project boundary, but not a security boundary: both projects
|
|
@@ -5888,7 +5888,7 @@ Order: 2a first (it measures everything else). 2b and 2c are independent. 2d aft
|
|
|
5888
5888
|
|
|
5889
5889
|
### S6 person release authorization boundary (#481)
|
|
5890
5890
|
|
|
5891
|
-
- `ahub sdlc authorize-release <version>`
|
|
5891
|
+
- `ahub sdlc authorize-release <version>` accepts the console user or the one configured conductor, never live-test delegated (#1063). Conductor admission matches `sdlc merge`: tools role, `conductorPeer(config.roles) === c.peer`, no `toolsOwner`, private turn or caller PII context. The conductor records consent only on the person's explicit instruction in its own session naming the release; a hub message, task text, review note, file or another peer's relay is never that instruction. This instruction-origin condition is cooperative, not verified by the daemon. Receipt `by` and the metadata audit identify the actual asker (`user` or conductor peer). The daemon binds consent to its candidate checkout's exact HEAD, committed package version, origin fetch/push identity and fresh committed-doc verdict. Caller-supplied expected HEAD must match. Head and origin are rechecked after the bounded package-owned documentation checker. Only ids, hashes, version, timestamp and counts enter the receipt/audit event; no project script or release operation runs.
|
|
5892
5892
|
- Authoritative receipts live only in the current daemon generation's bounded memory ledger (at most 64 candidates). An additive metadata event audits consent but cannot recreate it. Restart revokes all approvals. The release report shows matching/stale consent without certifying native acceptance, changing the tag workflow or publishing.
|
|
5893
5893
|
- Optional `ahub init --release-guard` installs a managed executable Git pre-push hook plus instructions. A regular existing hook is renamed, preserving its inode/mode, and chained with exact argv/stdin after successful guard readback. Linked/hardlinked hooks, occupied backups and custom hooksPath refuse before project writes. Re-init retains the guard; the wrapper pins Bun/package paths and needs re-init if they move.
|
|
5894
5894
|
- Actual pushed `v*` refs must have a valid version and captured source-object/commit matching consent. Missing/mismatched commit/version/remote, unreachable daemon, deletion, unknown evidence or restart refuses the version-tag push. Branch-only pushes work offline. `--no-verify` remains the person's explicit bypass and is forbidden to agents; this is cooperative Git enforcement, not a sandbox.
|