@staix/agent-hub 0.8.1 → 0.10.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 +16 -0
- package/README.md +2 -2
- package/docs/events.md +2 -1
- package/docs/operations.md +185 -11
- package/docs/quickstart.md +1 -1
- package/docs/security.md +4 -1
- package/docs/smoke.md +54 -0
- package/docs/specs/2026-09-19-agent-hub-design.md +171 -0
- package/package.json +1 -1
- package/plugins/agent-hub/.claude-plugin/plugin.json +1 -1
- package/plugins/agent-hub/server.js +5 -5
- package/src/adapters/acp.ts +10 -2
- package/src/adapters/local-worker.ts +4 -3
- package/src/cli/main.ts +49 -12
- package/src/cli/upgrade.ts +1 -1
- package/src/hub/board.ts +50 -7
- package/src/hub/budget.ts +57 -6
- package/src/hub/bus.ts +24 -6
- package/src/hub/config-trust.ts +1 -0
- package/src/hub/conflicts.ts +63 -0
- package/src/hub/crash.ts +104 -0
- package/src/hub/daemon.ts +227 -30
- package/src/hub/events.ts +1 -0
- package/src/hub/hub-tools.ts +4 -4
- package/src/hub/limits.ts +71 -0
- package/src/hub/peers.ts +3 -2
- package/src/hub/report.ts +7 -2
- package/src/hub/routing.ts +59 -4
- package/src/hub/snapshots.ts +29 -2
- package/src/hub/tasks.ts +204 -14
- package/src/local/sandbox.ts +60 -9
- package/templates/claude-hooks.json +10 -0
- package/templates/config.json +7 -3
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,22 @@
|
|
|
2
2
|
|
|
3
3
|
Issue and pull request numbers in the entries for 0.7.7 and earlier refer to the previous repository, archived on 2026-09-30 when this repository's history was rewritten; the one exception is the open smoke-check issue, formerly #12, which moved here as #1. Numbers in newer entries refer to this repository.
|
|
4
4
|
|
|
5
|
+
## 0.10.0
|
|
6
|
+
|
|
7
|
+
- Review checklists and review outcomes: review requests ask the reviewer to map signatures and call sites to the plan, read the check result and list unmet items (`hub_review {unmet}`); the hub records approved, caught, contradicted and escalated reviews per implementer, reviewer and class, shown by `ahub task show` and `ahub route explain`, and `review.adaptive` (off by default) orders reviewers by that record, after idle before busy and ahead of quota. A record counts tasks, a catch belongs to the owner whose work was caught, and only a failure on the same file or symbol contradicts an approval (#35).
|
|
8
|
+
- Recovery after an unplanned stop: the hub keeps each attached peer's session identity while it runs; a hub started after a crash reports what happened to each peer in `ahub status`, resumes Kimi (ACP `session/load`), a headless Pi and the local worker (on its recorded route) when `recovery.auto_resume_after_crash` is on, and gives each peer a notice of its deliveries left in `needs_review` with its next delivery. A stop someone asked for, even one past the shutdown deadline, is not taken for a crash (#37).
|
|
9
|
+
- The local worker's sandbox starts from deny default: commands may run and read the system, toolchain and project directories (and the selected Xcode or Command Line Tools dir) and write the project and temp, nothing else; `/Applications`, `/nix`, `/Volumes` and `/Users/Shared` now need `local.read_allow`; `local.sandbox: "allow-default"` (machine-local) keeps the profile of 0.9 and earlier for one release; with network on, the public CA bundles stay readable for TLS. Per-peer `capabilities` (`propose`, `assign`, `remember`, `important`) are enforced by the daemon, a malformed entry grants nothing and every refusal is logged, under deny-default the worker's commands can no longer reach the LaunchServices, CoreServices or SecurityServer brokers, and a test pins that a peer can never answer a permission request (#39).
|
|
10
|
+
|
|
11
|
+
## 0.9.0
|
|
12
|
+
|
|
13
|
+
- Early conflict detection: in a git work tree, a turn that changes a file another owner's open task changed earlier warns both owners (and the console, and `events.jsonl`), once per file and task, marked concurrent when another peer worked meanwhile. `ahub check-path` and the PreToolUse hook template `templates/claude-hooks.json` give Claude the same warning before an edit, without blocking it (#32).
|
|
14
|
+
- Task dependencies: `hub_task_propose` takes `after: [ids]`; a task waits, offered to nobody and not claimable, until those are approved, then goes through assignment (a task the hub stopped before offering is offered once it runs again). `hub_task_list {ready: true}` and `ahub board --ready` show the ready queue, and existing boards gain the column on open (#34).
|
|
15
|
+
- Quota-aware routing: candidates with quota readings are ordered by headroom per hour to their reset, so the window that resets first is drained first; a paused peer whose window resets within `budget.wait_max_min` (30 with a project config) keeps its work unless a task is `urgent`; and a peer whose recent failures in a class outweigh its approvals there is demoted for that class, with a one-day half-life. `ahub route explain` shows both, and `ahub task propose ... --urgent` marks a task urgent (#36).
|
|
16
|
+
- Per-sender limits on what agents send: token buckets per sender, per recipient and for `[IMPORTANT]`, plus dropping the same text to the same recipients, answering the same message, within a window. A refused `hub_send` answers with the reason and the seconds to wait; a turn answer over the important budget goes out as status, and one over a rate limit is dropped, the agent hearing why with its next delivery. `[FYI]` and the console user are never limited (#38).
|
|
17
|
+
- An upgrade or controlled restart from a 0.9.0 or later source waits for completion checks and console task operations still in flight; one that finished after the commit used to leave the operation blocked at verification, unable to resume or abort. A 0.8.x or older source cannot report them: wait for its checks by hand before upgrading (see the operations guide) (#50).
|
|
18
|
+
- Upgrading a running 0.7.x or 0.8.x hub with tasks on its board verifies the board although 0.9.0 adds the `deps` column, like 0.8.1 did for `plan` (#50).
|
|
19
|
+
- Turn snapshots no longer miss an edit that keeps a file's size and lands in the second git last wrote the index: the snapshot's copy of the index now keeps the original's time, which git's racy-entry check compares against in whole seconds. A missed edit could leave a file out of `ahub undo`, let undo restore over another agent's later change, or skip an early conflict warning (#60).
|
|
20
|
+
|
|
5
21
|
## 0.8.1
|
|
6
22
|
|
|
7
23
|
- Upgrading a running 0.7.x hub that has a task on its board no longer stops at verification ("queue, manual pause, task board or budget preservation was not verified"): the 0.8.0 target digested its tasks with the new `plan` field, which the 0.7.x source never had. The target now also accepts the digest in the source's shape while every plan is empty; any real change to the board still fails the check. Use the 0.8.1 coordinator, not 0.8.0's, for such an upgrade (#57).
|
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@ Native multi-agent hub for one developer's machine: Claude Code, Codex, Kimi Cod
|
|
|
4
4
|
hub-owned local-LLM worker collaborate as peers in independent project directories, with
|
|
5
5
|
task-aware model routing (Switchyard) in front of a self-hosted gateway (OmniRoute).
|
|
6
6
|
|
|
7
|
-
Status: 0.
|
|
7
|
+
Status: 0.10.0, control protocol 10. Durable delivery records distinguish queued
|
|
8
8
|
work from uncertain execution. The [smoke checklist](docs/smoke.md) records
|
|
9
9
|
verified paths and remaining prerequisites.
|
|
10
10
|
|
|
@@ -28,7 +28,7 @@ cd <your project> && ahub init && ahub up && ahub tail
|
|
|
28
28
|
Or install the same version from GitHub:
|
|
29
29
|
|
|
30
30
|
```bash
|
|
31
|
-
bun add -g github:STAIxBWLB/agent-hub#v0.
|
|
31
|
+
bun add -g github:STAIxBWLB/agent-hub#v0.10.0 && ahub setup
|
|
32
32
|
```
|
|
33
33
|
|
|
34
34
|
The installed commands remain `ahub` and `agent-hub`.
|
package/docs/events.md
CHANGED
|
@@ -18,9 +18,10 @@ marked `private: true`, and PII tasks `pii: true`.
|
|
|
18
18
|
| `turn_start` | `peer`, `turn` (`<peer>#<hub run>.<n>`, unique across restarts). A turn follows the adapter: pausing a busy peer does not end it |
|
|
19
19
|
| `turn_end` | `peer`, `turn`, `ms`, `tokens` (when the adapter reported any during the turn), `files` and `snapshotMs` (when snapshots are on: how many files the turn changed, and the time both snapshots took) |
|
|
20
20
|
| `tokens` | `peer`, `n` (tokens added since the previous report) |
|
|
21
|
-
| `task` | `id`, `event` (the board history event, e.g. `proposed`, `assigned`, `done`, `check failed`), `by`, `state`, `owner`, `reviewer`, `class`, `pii` |
|
|
21
|
+
| `task` | `id`, `event` (the board history event, e.g. `proposed`, `assigned`, `done`, `check failed`, `blocked`, `ready`), `by`, `state`, `owner`, `reviewer`, `class`, `pii` |
|
|
22
22
|
| `overlap` | `task`, `owner`, `others` (`task`, `owner`, `paths`, and `symbols` when plans name the same symbol; a name that matches a PII pattern is left out, so either list can be empty), the structured twin of the console notice |
|
|
23
23
|
| `quota` | `peer`, `windows` (`id`, `used`, `resetsAt`), `hard`, `measuredAt` (when the reading was taken, if not when it arrived: Claude's numbers come through a file) |
|
|
24
|
+
| `conflict` | `peer`, `task` (its task in progress, when it had one), `other` (the other owner's open task), `owner`, `paths` (names that match a PII pattern left out, so it can be empty), `concurrent` (another peer worked during the turn) |
|
|
24
25
|
|
|
25
26
|
Token usage by adapter:
|
|
26
27
|
|
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.10.0 and control protocol 10. Live verification
|
|
4
4
|
results and remaining prerequisites are recorded separately in [the smoke ledger](smoke.md).
|
|
5
5
|
|
|
6
6
|
## Install and start
|
|
@@ -27,11 +27,38 @@ what the hub runs, which files it sends as credentials, where task text goes,
|
|
|
27
27
|
or how far the local worker's sandbox reaches (`kimi_cmd`, `codex_bin`,
|
|
28
28
|
`pi.cmd`, `checks`, `mlx.bin`, `mlx.runtimeDir`, `mlx.modelPath`, `omniroute.urls`,
|
|
29
29
|
`omniroute.access_hosts`, the `omniroute` key files, `memory.worker_url`,
|
|
30
|
-
`local.read_allow`, `local.bash_network`) are machine-local:
|
|
31
|
-
from a file git confirms nobody committed. Put them in
|
|
30
|
+
`local.read_allow`, `local.bash_network`, `local.sandbox`) are machine-local:
|
|
31
|
+
they apply only from a file git confirms nobody committed. Put them in
|
|
32
32
|
`.agenthub/config.local.json` (`ahub init` adds it to `.gitignore`), which is
|
|
33
33
|
read after `config.json`; outside a git repository they keep their defaults, and
|
|
34
34
|
an empty value always means the default.
|
|
35
|
+
|
|
36
|
+
The local worker's commands run under a sandbox that starts from deny default
|
|
37
|
+
(0.10): they may run and read the system, toolchain and project directories and
|
|
38
|
+
the selected Xcode or Command Line Tools dir (`xcode-select -p`), write the
|
|
39
|
+
project and temp, and nothing else. With `local.bash_network` on they may also
|
|
40
|
+
read the public CA bundles, which the `*.pem` key deny would otherwise hide.
|
|
41
|
+
`"local": { "sandbox": "allow-default" }` in `config.local.json` brings back the
|
|
42
|
+
profile of 0.9 and earlier for one release, should a toolchain need a path the
|
|
43
|
+
new one lacks; please report it. Newly closed outside home: `/Applications`
|
|
44
|
+
(an app's bundled CLI), `/nix`, `/Volumes` and `/Users/Shared`. A toolchain
|
|
45
|
+
there, or elsewhere in your home (a CI tool cache, a version manager the profile
|
|
46
|
+
does not list), needs its directory in `local.read_allow`, for example
|
|
47
|
+
`"/nix"` or `"~/.pixi"`.
|
|
48
|
+
|
|
49
|
+
`capabilities` in `.agenthub/config.json` narrows what a peer may do with the
|
|
50
|
+
hub's tools: list a peer and it keeps only the capabilities named, from
|
|
51
|
+
`propose` (`hub_task_propose`), `assign` (proposing with another peer as owner),
|
|
52
|
+
`remember` (the `hub_remember` tool; the notes the hub itself keeps of done
|
|
53
|
+
summaries and review verdicts are not gated) and `important` (`[IMPORTANT]`
|
|
54
|
+
messages). For example `"capabilities": { "local": ["propose", "remember"] }`.
|
|
55
|
+
A peer that is not listed keeps all of them, a listed peer whose value is not a
|
|
56
|
+
list gets none, and an unknown capability name grants nothing; `hub.log` says
|
|
57
|
+
so for each, and logs every refusal (`capabilities:`). A refused tool call says
|
|
58
|
+
which capability is missing; an `[IMPORTANT]` turn answer without the
|
|
59
|
+
capability goes out as status, with a note to the sender.
|
|
60
|
+
Approvals are never a capability: only the console (and the dashboard) answers a
|
|
61
|
+
permission request.
|
|
35
62
|
A committed value is ignored with a line in `hub.log`, a note from `ahub
|
|
36
63
|
codex` and `ahub models`, and a row in `ahub doctor`.
|
|
37
64
|
|
|
@@ -87,6 +114,21 @@ external side effect happened unless the task's refs and a live readback show
|
|
|
87
114
|
that effect. The hub can reassign after repeated review changes according to
|
|
88
115
|
the routing configuration.
|
|
89
116
|
|
|
117
|
+
A review request carries a checklist: map the changed signatures and call sites
|
|
118
|
+
to the task's plan (without one, to its detail, which the request then
|
|
119
|
+
includes), read the check result, and list what is
|
|
120
|
+
unmet (`hub_review` takes `unmet`, `ahub review ... --unmet <item>`). The hub
|
|
121
|
+
records how each review turned out, per implementer, reviewer and class:
|
|
122
|
+
approved; caught (changes were requested and the owner's redo was approved);
|
|
123
|
+
contradicted (within a week, work on the same file or symbol failed its check
|
|
124
|
+
or review); escalated (after the reviewer asked for changes). A record counts
|
|
125
|
+
tasks, not verdicts. `ahub task show <id>` lists a task's outcomes and `ahub
|
|
126
|
+
route explain` shows each reviewer's record with the implementer. With
|
|
127
|
+
`"review": { "adaptive": true }` in `.agenthub/config.json`, reviewers with at
|
|
128
|
+
least `min_reviews` (5) reviews of that implementer in the class are ordered by
|
|
129
|
+
how their reviews held up, ahead of quota but after idle before busy; off by
|
|
130
|
+
default, which leaves assignment as it was.
|
|
131
|
+
|
|
90
132
|
An agent can claim work nobody assigned it by proposing a task with itself as
|
|
91
133
|
owner; without a class, and with no model to name one, the claim is filed as
|
|
92
134
|
`implement`. A claim or an accept can carry a plan: the files, symbols and
|
|
@@ -98,7 +140,20 @@ task is done (after its check passes, when one is configured), the owners of
|
|
|
98
140
|
open tasks on the same paths or symbols get a message with the changed files,
|
|
99
141
|
the plan's signatures and the first line of the summary, each left out when it
|
|
100
142
|
matches a PII pattern; nobody else does. PII
|
|
101
|
-
tasks are left out on both sides.
|
|
143
|
+
tasks are left out on both sides.
|
|
144
|
+
|
|
145
|
+
A task can wait for others: `hub_task_propose` takes `after: [ids]` (`ahub task
|
|
146
|
+
propose ... --after <id>`). Until every one of them is approved, the task is
|
|
147
|
+
offered to nobody, cannot be claimed, accepted or marked done, and `ahub route
|
|
148
|
+
explain <id>` says what it waits for. When the last one is approved, the task
|
|
149
|
+
goes through assignment like a new one; if the hub stopped before it got that
|
|
150
|
+
far, the task is offered within a minute of a peer that can take it attaching
|
|
151
|
+
to the next run.
|
|
152
|
+
`ahub board --ready` and
|
|
153
|
+
`hub_task_list {ready: true}` list the proposed tasks with nothing left to wait
|
|
154
|
+
for. Dependencies are fixed when a task is proposed and can only name tasks that
|
|
155
|
+
already exist, so they cannot form a cycle. A waiting task cannot name an owner;
|
|
156
|
+
use `ahub task assign` once it is ready. An owner offline longer than `tasks.release_after_min`
|
|
102
157
|
(default 30, `0` turns it off) in `.agenthub/config.json` loses its open tasks
|
|
103
158
|
to a peer routing can give them to; with nobody to take them they stay, and a
|
|
104
159
|
paused peer or a hub in a recovery operation is left alone.
|
|
@@ -111,6 +166,27 @@ ahub say @claude "[STATUS] the test run is complete"
|
|
|
111
166
|
ahub say @kimi "[FYI] the result is recorded"
|
|
112
167
|
```
|
|
113
168
|
|
|
169
|
+
What agents send is limited per sender (`limits` in `.agenthub/config.json`; a
|
|
170
|
+
project config gets the values below unless it sets others, `0` turns one off):
|
|
171
|
+
|
|
172
|
+
- `sender_per_min` (12) messages a minute from one agent, `pair_per_min` (6) to
|
|
173
|
+
one recipient (a broadcast counts as one), and `important_per_hour` (6)
|
|
174
|
+
`[IMPORTANT]` messages, each of which can interrupt a running turn. Limits
|
|
175
|
+
count what is sent: a reply goes to the agent it answers, a reply to a
|
|
176
|
+
condensed digest counts against the agents behind it, and an `[IMPORTANT]` the
|
|
177
|
+
hub lowers to status is not important. `[FYI]` costs nobody a turn and is
|
|
178
|
+
never limited.
|
|
179
|
+
- `repeat_window_s` (120): the same text to the same recipients, answering the
|
|
180
|
+
same message, again within the window is dropped. "Yes." to two different
|
|
181
|
+
questions is two messages.
|
|
182
|
+
- A refused `hub_send` answers `not sent: <why>`, with the seconds to wait for a
|
|
183
|
+
rate limit, so the agent learns at once. A turn answer has nobody to refuse
|
|
184
|
+
to: one over the important budget goes out as status, and one over a rate
|
|
185
|
+
limit or repeated is not published; either way the agent gets the reason with
|
|
186
|
+
its next delivery, and has to send a dropped answer again. hub.log records
|
|
187
|
+
each refusal (`limits:`), and a value that is not a number falls back to the
|
|
188
|
+
default above. The console user and the hub itself are never limited.
|
|
189
|
+
|
|
114
190
|
`@peer` addresses one known peer. With no recipient, `ahub say` broadcasts
|
|
115
191
|
to attached peers. `[IMPORTANT]` can bypass batching where the peer supports
|
|
116
192
|
it; `[STATUS]` may batch, and `[FYI]` is recorded without follow-on
|
|
@@ -185,6 +261,34 @@ ahub undo <turn> --yes --context # Codex's latest turn: also drop it from Codex
|
|
|
185
261
|
older turn says so.
|
|
186
262
|
- Each `turn_end` event carries `files` and `snapshotMs` (`ahub export`).
|
|
187
263
|
|
|
264
|
+
## Edit conflicts
|
|
265
|
+
|
|
266
|
+
With snapshots on, the hub also compares each turn's files with what other
|
|
267
|
+
owners' open tasks changed before it. A turn's files count for every task its
|
|
268
|
+
peer has in progress, except files that another peer's overlapping turn changed;
|
|
269
|
+
while such a turn's changes are unknown (it still runs), the turn's files count
|
|
270
|
+
for none. When a peer changes a file that another owner's open task changed
|
|
271
|
+
earlier, both get a message naming the file and the other task, the console
|
|
272
|
+
shows a `conflict:` line, and `events.jsonl` records a `conflict` event (`ahub
|
|
273
|
+
report` counts them). Each peer, task and file is reported once per hub run.
|
|
274
|
+
When another peer worked during the same turn, the messages say so: the change
|
|
275
|
+
may be theirs. Edits by Claude or by you during a peer's turn count as that
|
|
276
|
+
peer's, without that note: the hub sees no turn of yours. Nothing is blocked. A
|
|
277
|
+
file only one agent touched, and anything to do with a PII task, warns nobody.
|
|
278
|
+
A file whose name matches a PII pattern is not named, as in overlap notices: the
|
|
279
|
+
messages and the console line only count such files, and the event leaves them out.
|
|
280
|
+
|
|
281
|
+
Claude's edits do not pass through turn snapshots, so Claude can ask before each
|
|
282
|
+
edit instead. `templates/claude-hooks.json` is a PreToolUse hook for Edit, Write,
|
|
283
|
+
MultiEdit and NotebookEdit that runs `ahub check-path --hook`. Merge it into
|
|
284
|
+
`.claude/settings.local.json` (or your user settings) yourself. When another
|
|
285
|
+
owner's open task claims the file (refs or plan paths) or changed it, Claude gets
|
|
286
|
+
the list with the tool result and you see one line. The hook never decides a
|
|
287
|
+
permission, so your permission rules apply as before. Task titles in the list
|
|
288
|
+
are quoted and marked as other agents' text. The hook runs `ahub`, so it has to
|
|
289
|
+
be on the PATH Claude Code's hooks see; otherwise every edit shows a hook error
|
|
290
|
+
(it never blocks). `ahub check-path <file>` prints the same list in a terminal.
|
|
291
|
+
|
|
188
292
|
## Approvals and pauses
|
|
189
293
|
|
|
190
294
|
Inspect permission requests in the terminal:
|
|
@@ -215,6 +319,25 @@ A budget pause remains authoritative until the budget command explicitly
|
|
|
215
319
|
overrides it or the window resets. Check `ahub status` and `ahub board` after
|
|
216
320
|
a pause or handoff.
|
|
217
321
|
|
|
322
|
+
Quota also shapes routing and handoffs:
|
|
323
|
+
|
|
324
|
+
- Among peers a task could go to, those with quota readings are ordered by
|
|
325
|
+
headroom per hour left until the reset of the window that bounds it (the most
|
|
326
|
+
used one, so a week window near its cap is not mistaken for a 5 h window about
|
|
327
|
+
to reset), so the window that resets first is used first. Peers without readings, such as `local` and `pi`, keep
|
|
328
|
+
their place in `routing.toml`. `ahub route explain` shows the reordering.
|
|
329
|
+
- When a paused peer's window resets within `budget.wait_max_min` (30 once the
|
|
330
|
+
project has `.agenthub/config.json` or `config.local.json`, 0 without one; `0`
|
|
331
|
+
always hands over), it keeps its work and only tasks proposed with `urgent`
|
|
332
|
+
(`ahub task propose ... --urgent`) move. If the reset moves past the limit
|
|
333
|
+
while it waits (a week window crosses the gate), its work is handed over after
|
|
334
|
+
all. `ahub budget` shows each decision and why in the pause reason.
|
|
335
|
+
- A peer whose recent failures in a class (failed checks, changes requested,
|
|
336
|
+
escalations by hand) reach 1.5 after decay, and outweigh its recent
|
|
337
|
+
approvals there (a task without a reviewer counts when done), goes behind the
|
|
338
|
+
other candidates in the same state for that class, `local` and `pi` included.
|
|
339
|
+
A failure counts half after a day. `ahub route explain` names demoted peers.
|
|
340
|
+
|
|
218
341
|
## Durable delivery and queue resolution
|
|
219
342
|
|
|
220
343
|
Protocol 10 records each recipient delivery in the private project journal.
|
|
@@ -302,8 +425,8 @@ source and carries every recovery fix released up to it. Protocol 8 and older
|
|
|
302
425
|
project directory, without replacing the global CLI first:
|
|
303
426
|
|
|
304
427
|
```bash
|
|
305
|
-
bunx --package @staix/agent-hub@0.
|
|
306
|
-
bunx --package @staix/agent-hub@0.
|
|
428
|
+
bunx --package @staix/agent-hub@0.10.0 ahub upgrade --to 0.10.0 --dry-run
|
|
429
|
+
bunx --package @staix/agent-hub@0.10.0 ahub upgrade --to 0.10.0 --yes
|
|
307
430
|
```
|
|
308
431
|
|
|
309
432
|
| Running now | Coordinator to use |
|
|
@@ -318,36 +441,64 @@ its verification never matches the board and the operation stays blocked (fixed
|
|
|
318
441
|
in 0.8.1). Such a blocked operation can neither resume nor abort, and its lock
|
|
319
442
|
refuses `up` and `kill` for every project; the [smoke ledger](smoke.md) (0.7.11
|
|
320
443
|
to 0.8.0) records the manual cleanup. Do not use an older installed CLI as the
|
|
321
|
-
coordinator.
|
|
444
|
+
coordinator. After an upgrade, do not start an older hub on the same project: it
|
|
445
|
+
does not know the newer task columns, and its board readback breaks a later
|
|
446
|
+
upgrade. A 0.6.x CLI cannot target
|
|
322
447
|
protocol 10: its plan does not check the target's protocol, so the dry-run shows
|
|
323
448
|
no blocker, and `--yes` stops at staging ("target protocol requires a newer
|
|
324
449
|
coordinator") with an operation left to clear by `ahub recovery abort <id>`. An
|
|
325
450
|
older 0.7.x CLI may lack recovery fixes released after it. The
|
|
326
451
|
[smoke ledger](smoke.md) records dry-runs from real 0.6.4 and 0.7.5 hubs (issue
|
|
327
|
-
#75)
|
|
452
|
+
#75), an applied upgrade with the 0.7.0 coordinator, and one from a running
|
|
453
|
+
0.8.1 hub with tasks and a budget pause with the 0.9.0 coordinator.
|
|
328
454
|
|
|
329
455
|
The coordinator verifies and retains the exact target package, preserves its
|
|
330
456
|
own source, and promotes the global CLI only after restored projects pass
|
|
331
457
|
readback.
|
|
332
458
|
|
|
333
|
-
Once the installed CLI is 0.
|
|
459
|
+
Once the installed CLI is 0.10.0, review the current project or all registered
|
|
334
460
|
projects first:
|
|
335
461
|
|
|
336
462
|
```bash
|
|
337
463
|
ahub restart --dry-run
|
|
338
|
-
ahub upgrade --to 0.
|
|
464
|
+
ahub upgrade --to 0.10.0 --dry-run
|
|
339
465
|
```
|
|
340
466
|
|
|
341
467
|
Apply only after reviewing the plan:
|
|
342
468
|
|
|
343
469
|
```bash
|
|
344
470
|
ahub restart --yes
|
|
345
|
-
ahub upgrade --to 0.
|
|
471
|
+
ahub upgrade --to 0.10.0 --yes
|
|
346
472
|
ahub recovery status <operation-id>
|
|
347
473
|
ahub recovery resume <operation-id>
|
|
348
474
|
ahub recovery abort <operation-id>
|
|
349
475
|
```
|
|
350
476
|
|
|
477
|
+
The coordinator commits only once the source is quiet: no turn running, no
|
|
478
|
+
approval pending, no completion check queued or running. It waits up to 10
|
|
479
|
+
minutes and then aborts, leaving the source running; upgrade between long
|
|
480
|
+
checks. A 0.8.x or older source does not report completion checks or console
|
|
481
|
+
task commands in flight, so the coordinator cannot wait for them. Before
|
|
482
|
+
`--yes`, for each task whose check `hub.log` reported as "queued or running",
|
|
483
|
+
wait until `ahub task show <id>` has `check passed`, `check failed` or `check
|
|
484
|
+
finished late` after `done (checking)` (a pass with a peer reviewer writes no
|
|
485
|
+
line of its own to `hub.log`), and until no task command or dashboard action is
|
|
486
|
+
still running. A check the commit's stop kills writes to the board after the
|
|
487
|
+
commit has recorded it, and the operation stays blocked.
|
|
488
|
+
|
|
489
|
+
0.9.0 turns on for every project with a config file, whether or not it has the
|
|
490
|
+
block: `limits` (12 messages a minute per sender, 6 per recipient, 6
|
|
491
|
+
`[IMPORTANT]` an hour, 120 s repeats) and `budget.wait_max_min` (30). Set them
|
|
492
|
+
to 0 to opt out.
|
|
493
|
+
|
|
494
|
+
0.10.0 changes every project's local worker: its commands run under the
|
|
495
|
+
deny-default sandbox described above, and a toolchain outside the listed
|
|
496
|
+
directories needs `local.read_allow`; `"local": { "sandbox": "allow-default" }`
|
|
497
|
+
in `config.local.json` restores the old profile for one release. Off unless set:
|
|
498
|
+
`review.adaptive`, `recovery.auto_resume_after_crash` and `capabilities`. A hub
|
|
499
|
+
before 0.10.0 keeps no session record, so a crash of one is not reported as such
|
|
500
|
+
by the next start.
|
|
501
|
+
|
|
351
502
|
The 0.7.0 transition stages the verified package and runs a retained
|
|
352
503
|
coordinator from the source tree. It accepts a verified protocol-9 source and
|
|
353
504
|
moves to a protocol-10 target. The source journal, queued envelopes, tasks,
|
|
@@ -369,6 +520,29 @@ Do not run an upgrade with an incompatible active protocol, an unverified
|
|
|
369
520
|
terminal binding, or an unresolved operation lock. Dry-run performs no package,
|
|
370
521
|
plugin, daemon, or terminal mutation.
|
|
371
522
|
|
|
523
|
+
### After an unplanned stop
|
|
524
|
+
|
|
525
|
+
While it runs, the hub keeps each attached peer's session identity in
|
|
526
|
+
`.agenthub/state/sessions.json` (ids and launch options, no message text); a stop
|
|
527
|
+
removes it as it begins. When a hub starts and finds the file, the previous run
|
|
528
|
+
died (`kill -9`, a crash, a lost machine), and `ahub status` and the console say
|
|
529
|
+
what happened to each peer. Hubs before 0.10.0 kept no such record, so a
|
|
530
|
+
crash of one is not reported this way.
|
|
531
|
+
|
|
532
|
+
- Deliveries that were in flight are in `needs_review` (`ahub queue list`), as
|
|
533
|
+
before. When a peer next attaches, its next delivery starts with a notice that
|
|
534
|
+
lists them by id, sender and task (`[pii]` for a PII task), never their text.
|
|
535
|
+
- Kimi, Pi and the local worker run inside the hub, so they died with it. With
|
|
536
|
+
`"recovery": { "auto_resume_after_crash": true }` in `.agenthub/config.json` the
|
|
537
|
+
hub starts them again: Kimi loads its recorded session (ACP `session/load`), Pi
|
|
538
|
+
resumes its session file, and the local worker starts without its history, on
|
|
539
|
+
its recorded route (or pinned model). Off by default: the report then says
|
|
540
|
+
what to start. A Pi that ran in a terminal (`--mode tui`) is never started by
|
|
541
|
+
the hub; the report gives the command. With `pi.auto_start` and no resume,
|
|
542
|
+
Pi starts on a fresh session as usual.
|
|
543
|
+
- Codex's app-server died with the hub; run `ahub codex` again. Claude Code's
|
|
544
|
+
plugin reconnects by itself while that session is open.
|
|
545
|
+
|
|
372
546
|
## Evidence and limits
|
|
373
547
|
|
|
374
548
|
The 0.6.4 release has real measurements in [the smoke checklist](smoke.md):
|
package/docs/quickstart.md
CHANGED
package/docs/security.md
CHANGED
|
@@ -10,7 +10,10 @@ agent-hub connects agents that can each run commands. This page says what the hu
|
|
|
10
10
|
- **A committed config cannot choose launch commands, credential files, data endpoints or a wider sandbox.** The machine-local fields (`kimi_cmd`, `codex_bin`, `pi.cmd`, `checks`, `mlx.bin`, `mlx.runtimeDir`, `mlx.modelPath`, `omniroute.urls`, `omniroute.access_hosts`, the `omniroute` key files, `memory.worker_url`, `local.read_allow`, `local.bash_network`) apply only from a config file git confirms nobody committed: `.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, or a wider sandbox. A command in `checks` runs as you, outside the local worker's sandbox, like a git hook. 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.
|
|
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. It never records a message body, a task title or detail, and marks private (PII) envelopes and tasks as such. It stays on the machine; `ahub export` only prints it.
|
|
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
|
-
- **The
|
|
13
|
+
- **The edit hook reads, never decides.** `ahub check-path --hook` (issue #32) reads hub.db and returns context for Claude and a line for you; it sets no permission decision, so your permission rules stay in charge. It names other owners' task ids, titles and states, which then reach Claude's model; PII tasks are left out.
|
|
14
|
+
- **The session record holds identities only.** `.agenthub/state/sessions.json` (issue #37, mode 600) keeps each attached peer's recovery metadata: launch options, session and thread ids, Pi's session file path. No message or task text; loss notices name deliveries by id, sender and public task title.
|
|
15
|
+
- **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) 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, 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, the selected developer dir and temp; with network on, the public CA bundles); no writes outside the project, its git dir and temp; no `.git/hooks` or `.git/config` writes; no network, loopback included, unless `local.bash_network`. `local.sandbox: "allow-default"`, machine-local, brings back the profile of 0.9 and earlier for one release. The user's temp dir is still readable in both: a command can read another tool's leftovers there. Without the sandbox there is no `bash` tool.
|
|
16
|
+
- **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.
|
|
14
17
|
- **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 is sent to claude-mem, whose observer is a cloud model.
|
|
15
18
|
- **Secrets stay where they are read.** The gateway key and Cloudflare Access values are read inside the gateway client and go only into request headers: never into logs, errors, envelopes, tool output, memory, or the generated Switchyard config (the key travels by environment variable name). Subscription logins of Claude, Codex and Kimi are never proxied or pooled.
|
|
16
19
|
- **The hub's own model calls are fenced.** Digest condensation and task triage read agent-written text as data; their output is capped text framed as untrusted, or a value checked against a closed list. It is never used as a route, a peer id, a tool call or an instruction.
|
package/docs/smoke.md
CHANGED
|
@@ -883,3 +883,57 @@ peers attached.
|
|
|
883
883
|
- The release gate for v0.8.0 (Actions run 36807880478) failed once on
|
|
884
884
|
"removing a running project is refused"; the rerun passed and published. Cause: `ahub kill` returned before
|
|
885
885
|
the hub released its registry claim. Fixed in 0.8.1 (#58).
|
|
886
|
+
|
|
887
|
+
## Edit conflict detection (issue #32, 2026-10-01)
|
|
888
|
+
|
|
889
|
+
- AC3, on a clone of this repository (156 tracked files) with 10 open tasks of
|
|
890
|
+
other owners holding 200 touches: the turn-end path (end snapshot, diff,
|
|
891
|
+
touch query, match) takes 57.6 ms median, 59.6 ms max; the detection part
|
|
892
|
+
alone (diff, query, match) 7.1 ms median. The machine was under load from
|
|
893
|
+
other work; the snapshot alone measured 17 ms median earlier the same day.
|
|
894
|
+
- AC4, Claude Code 2.1.286 headless (`claude -p --settings <hook settings>
|
|
895
|
+
--permission-mode acceptEdits`, model Haiku 4.5) in a scratch repository whose
|
|
896
|
+
hub.db had kimi's open task claiming `notes.txt`, asked to edit `notes.txt` and
|
|
897
|
+
quote any agent-hub note:
|
|
898
|
+
- Runs 2 and 3: the hook fired (logged input and output in run 2), Claude
|
|
899
|
+
quoted the warning verbatim, and the edit went through under the session's
|
|
900
|
+
permission mode.
|
|
901
|
+
- Run 1, before the hook was logged: Claude answered NONE. Whether the hook
|
|
902
|
+
did not fire or the model left the reminder out was not determined; that run
|
|
903
|
+
also had no stdin redirect (`< /dev/null`), which runs 2 and 3 had.
|
|
904
|
+
|
|
905
|
+
## Recovery after an unplanned stop (issue #37, 2026-10-01)
|
|
906
|
+
|
|
907
|
+
- AC1 baseline, released 0.7.11 in a scratch project, the fake ACP agent standing
|
|
908
|
+
in for the Kimi binary (`kimi_cmd`), `kill -9` of the daemon:
|
|
909
|
+
- the ACP child exited with the daemon;
|
|
910
|
+
- `ahub status` failed to reach the stale manifest's port;
|
|
911
|
+
- `ahub up` started a hub with no peers attached; nothing recorded Kimi's
|
|
912
|
+
session, so it could only start a new one.
|
|
913
|
+
- The same run with this branch and `auto_resume_after_crash` on: `ahub up`
|
|
914
|
+
reported the crash and `kimi resumed: ... session s1 ... (ACP session/load)`,
|
|
915
|
+
and Kimi was idle on its recorded session id.
|
|
916
|
+
- Pending, needs real accounts: Kimi 2.x (does it offer `loadSession`, and does
|
|
917
|
+
the resumed session keep its context), Pi with a real session file, and Codex
|
|
918
|
+
and Claude reattachment after `kill -9`.
|
|
919
|
+
|
|
920
|
+
## 0.8.1 to 0.9.0 attended upgrade (2026-10-01)
|
|
921
|
+
|
|
922
|
+
A scratch project (a git work tree whose `.agenthub/config.json` was written by
|
|
923
|
+
the 0.8.1 `ahub init`) ran a 0.8.1 hub with two proposed tasks, one with a path
|
|
924
|
+
and a detail, and an open budget pause: a peer had attached once and gone
|
|
925
|
+
offline, and `ahub budget set` fed it a 95% reading. No completion check was
|
|
926
|
+
configured or running, and no peer was attached at the upgrade.
|
|
927
|
+
|
|
928
|
+
- `bunx --package @staix/agent-hub@0.9.0 ahub upgrade --to 0.9.0 --dry-run`
|
|
929
|
+
exited 0: one project, source 0.8.1, protocol 10, no blockers.
|
|
930
|
+
- `--yes` scheduled the operation, which completed in about 10 s with the
|
|
931
|
+
project `verified`.
|
|
932
|
+
- After release the hub ran 0.9.0 under a new instance id. Both tasks were on
|
|
933
|
+
the board unchanged, now with `deps: []` (the column the target adds on open),
|
|
934
|
+
the budget pause kept its reset time, and the `outcomes` table was created.
|
|
935
|
+
- The coordinator promoted the global CLI to 0.9.0, and `ahub setup --yes`
|
|
936
|
+
installed the 0.9.0 plugin.
|
|
937
|
+
- For about two minutes after the publish step logged `+ @staix/agent-hub@0.9.0`,
|
|
938
|
+
`npm view` still showed `latest: 0.8.1`; the registry caught up without any
|
|
939
|
+
action.
|
|
@@ -921,3 +921,174 @@ session modes (`default`, `plan`, `auto`, `yolo`) but nothing per server or tool
|
|
|
921
921
|
empty plan on accept keeps the plan there is. The completed-change notice
|
|
922
922
|
leaves out any file, signature or summary line that matches a PII pattern.
|
|
923
923
|
|
|
924
|
+
## Amendment: early conflict detection (issue #32)
|
|
925
|
+
|
|
926
|
+
- Shared tree only. The issue's worktree mode (pairwise `git merge-tree` between
|
|
927
|
+
per-agent worktrees, AC2) moves to #41, which provides those worktrees.
|
|
928
|
+
- Attribution reuses the #33 turn snapshots: the files a turn changed are
|
|
929
|
+
recorded as `touches` (task, peer, path) in hub.db for each task the peer has
|
|
930
|
+
in progress. A later turn by another peer that changes one of those files, while
|
|
931
|
+
the task is open, is a conflict. A file touched by one agent only is not, even
|
|
932
|
+
when its task changed hands. PII tasks are left out on both sides, and a turn of
|
|
933
|
+
a peer with a PII task in progress is not checked.
|
|
934
|
+
- Notices are normal-priority `task` envelopes to both owners, once per (peer,
|
|
935
|
+
task, file) per hub run, plus a console line and a `conflict` event.
|
|
936
|
+
"Concurrent" means another peer had a turn open during this one.
|
|
937
|
+
- The PreToolUse hook template runs `ahub check-path --hook`, which reads hub.db
|
|
938
|
+
read-only and answers with `hookSpecificOutput.additionalContext` and a
|
|
939
|
+
`systemMessage`, never a `permissionDecision`. Claude Code adds that context
|
|
940
|
+
with the tool result, so it warns after the edit is allowed, not before.
|
|
941
|
+
- After review: a turn's touches leave out files an overlapping turn of another
|
|
942
|
+
peer changed (#33's `Turns.overlapping`), and none are recorded while such a
|
|
943
|
+
turn's changes are unknown; detection runs after the `turn_end` event; the
|
|
944
|
+
hook quotes task titles as JSON strings and says they are agents' text; a
|
|
945
|
+
file name that matches a PII pattern is never named, through the same check as
|
|
946
|
+
overlap mentions (#31): notices and the console line count such files, the
|
|
947
|
+
event leaves them out.
|
|
948
|
+
|
|
949
|
+
## Amendment: task dependencies (issue #34)
|
|
950
|
+
|
|
951
|
+
- `deps` is a JSON column of task ids, set from `after` when a task is proposed
|
|
952
|
+
and never changed. It may name only tasks that exist, so the new task closes no
|
|
953
|
+
cycle (nothing can depend on it yet); the issue's cycle check at propose holds
|
|
954
|
+
by construction, and unknown ids are refused.
|
|
955
|
+
- A task waits while any dependency is not approved. Waiting is an input to
|
|
956
|
+
`assign()` (`waitsFor`), which then returns no owner and says why, so `ahub
|
|
957
|
+
route explain` and assignment agree. Accept and done refuse a waiting task,
|
|
958
|
+
for the console user too.
|
|
959
|
+
- A proposal that names an owner (a claim, or an owner for someone else) and
|
|
960
|
+
would wait is refused: the owner of waiting work is chosen when it is ready,
|
|
961
|
+
by routing or by `ahub task assign`.
|
|
962
|
+
- Approval, by review or by a class without a reviewer, releases the
|
|
963
|
+
dependents that wait for nothing else; each is recorded as `ready` and assigned.
|
|
964
|
+
A proposal re-reads what it waits for after its insert, since triage may have
|
|
965
|
+
awaited an approval. An approval is saved before its dependents are assigned,
|
|
966
|
+
so the daemon's 60 s release timer sweeps for ownerless tasks whose last event
|
|
967
|
+
is `blocked` or `ready` and that wait for nothing: each is offered once per hub
|
|
968
|
+
run, once an attached peer can take it, outside recovery operations.
|
|
969
|
+
- A recovery commit also waits for task operations in flight and for completion
|
|
970
|
+
checks queued or running: both can write the board after its integrity digest
|
|
971
|
+
(a check the commit's stop kills records `check interrupted`).
|
|
972
|
+
|
|
973
|
+
## Amendment: quota-aware routing, wait or hand off, demotion (issue #36)
|
|
974
|
+
|
|
975
|
+
- `assign()` stays pure: quota (per peer, the headroom of the most used fresh
|
|
976
|
+
window and that window's reset, from `Budget.headroom()`), the clock and the
|
|
977
|
+
demotion weights are inputs. Demoted peers go behind the others (owner role
|
|
978
|
+
only; local and Pi lose their place ahead of the cloud peers too); within each
|
|
979
|
+
group, peers with readings swap places by headroom per hour to reset (at least
|
|
980
|
+
15 min), and peers without readings keep their configured place. Idle still
|
|
981
|
+
comes before busy, so demotion orders peers in the same state.
|
|
982
|
+
- Wait or hand off is decided at handoff time from the pause record's reset:
|
|
983
|
+
within `budget.wait_max_min` the handoff moves only tasks whose signals include
|
|
984
|
+
`urgent` (set by `hub_task_propose {urgent: true}`), and the pause reason says
|
|
985
|
+
so; beyond it the reason says the work was handed over and why. A wait is
|
|
986
|
+
undone when a later reading moves the reset past the limit: the record is
|
|
987
|
+
unmarked and the next tick hands over the rest, keeping both lists of moved
|
|
988
|
+
tasks. `DEFAULT_BUDGET` has it off, like approvals.notify; any project config turns it
|
|
989
|
+
on at 30 unless it sets another value.
|
|
990
|
+
- Outcomes (`peer`, `class`, ok, time) are recorded in hub.db and kept a week:
|
|
991
|
+
approved counts for the owner, by review or done without a reviewer; changes
|
|
992
|
+
requested, a failed check and an escalation by hand count against it (the
|
|
993
|
+
hub's own escalations are not counted: after repeated changes requested each
|
|
994
|
+
one already was, and after a Pi inference failure the backend failed). Demotion: decayed failures reach 1.5 and outweigh decayed
|
|
995
|
+
successes, half-life one day, fixed constants for now.
|
|
996
|
+
|
|
997
|
+
## Amendment: per-sender limits (issue #38)
|
|
998
|
+
|
|
999
|
+
- One `Limiter` (`src/hub/limits.ts`) serves both ways an agent sends: the
|
|
1000
|
+
control WS `send` (hub_send from Claude, and from Codex and Kimi through the
|
|
1001
|
+
tools server) refuses with the reason in `error`, which the channel already
|
|
1002
|
+
shows, so the message shape is unchanged; the bus's `admit` option guards
|
|
1003
|
+
adapter messages (turn answers, the local worker's hub_send), drops a refused
|
|
1004
|
+
one and tells the sender in a ride-along line.
|
|
1005
|
+
- Both admit the envelope as it will be sent: a reply without `to` goes to its
|
|
1006
|
+
parent's sender, `digest` is resolved to the senders it replaced, and the
|
|
1007
|
+
priority is capped. The repeat key includes the id of what the message
|
|
1008
|
+
answers. `fyi` is never limited: it reaches no peer.
|
|
1009
|
+
- An `important` hub_send through the control WS (Claude, and Codex and Kimi
|
|
1010
|
+
through the tools server) over its budget is refused, not downgraded, and the
|
|
1011
|
+
reason says to send it without `[IMPORTANT]`. On the bus path (turn answers,
|
|
1012
|
+
and the hub_send of Pi and the local worker) there is no caller to refuse:
|
|
1013
|
+
over its important budget a message is lowered to status and delivered, and
|
|
1014
|
+
the sender is told.
|
|
1015
|
+
- A refusal consumes no token and does not count as a send for repeat
|
|
1016
|
+
suppression. Limits are off in `DEFAULT_CONFIG` and on with any project config
|
|
1017
|
+
(12/min per sender, 6/min per recipient, 6 important an hour, 120 s repeats).
|
|
1018
|
+
|
|
1019
|
+
## Amendment: review checklists and outcomes (issue #35)
|
|
1020
|
+
|
|
1021
|
+
- A review request carries a checklist: map the changed signatures and call
|
|
1022
|
+
sites to the task's plan, or without a plan to the task detail, which the
|
|
1023
|
+
request then includes; the result of the class's check, or that none ran; and
|
|
1024
|
+
`hub_review`'s `unmet`, one item each, which is appended to the verdict note.
|
|
1025
|
+
- Review outcomes live in a `reviews` table in hub.db: implementer, reviewer,
|
|
1026
|
+
class, kind, task, time. Kinds: `approved`; `caught` (each reviewer who asked
|
|
1027
|
+
for changes on the current owner's work, which was then approved);
|
|
1028
|
+
`contradicted` (an approval, within seven days, of a task on the same file or
|
|
1029
|
+
symbol as one whose check failed or whose review asked for changes, a
|
|
1030
|
+
directory or `.` not counting; once per approval; console approvals and PII
|
|
1031
|
+
tasks are not judged); `escalated` (the reviewer had asked for changes on the
|
|
1032
|
+
work that was escalated, so not an escalation of unreviewed work).
|
|
1033
|
+
- A reviewer's record with an implementer in a class counts tasks: n = the tasks
|
|
1034
|
+
it reviewed (approved, caught or escalated), held = (n - contradicted tasks) /
|
|
1035
|
+
n. `assign()` takes the records as input and always shows them in its trace;
|
|
1036
|
+
with `review.adaptive` it orders reviewer candidates that have at least
|
|
1037
|
+
`min_reviews` by held, others keep their place, and the implementer is never
|
|
1038
|
+
a candidate. The record is applied after quota, so the order is idle before
|
|
1039
|
+
busy, then the record, then quota.
|
|
1040
|
+
|
|
1041
|
+
## Amendment: recovery after an unplanned stop (issue #37)
|
|
1042
|
+
|
|
1043
|
+
- The continuous record is `sessions.json` (instance id, time, and each attached
|
|
1044
|
+
peer's `recoveryMetadata()`), rewritten when a peer's state changes and
|
|
1045
|
+
removed when a stop of the run that wrote it begins (a stop that then runs past
|
|
1046
|
+
the shutdown deadline is still not a crash). Found at start, with no
|
|
1047
|
+
controlled-restart state in play, it means the previous run crashed; the new
|
|
1048
|
+
run takes the record over at once, so its own clean stop removes it even when
|
|
1049
|
+
no peer attaches. A controlled restart's target removes any record it finds.
|
|
1050
|
+
- Limits: a second crash before the peers attach loses their loss notices (the
|
|
1051
|
+
journal rows stay in `needs_review`, shown by `ahub queue list`), and the first
|
|
1052
|
+
attach rewrites the record with only the attached peers. A Pi in TUI mode is
|
|
1053
|
+
reported with its command, not resumed: the CLI runs its terminal.
|
|
1054
|
+
- Resume goes through the same start path as `ahub kimi` / `ahub pi` / `ahub
|
|
1055
|
+
local`: Kimi with `sessionId` (ACP `session/load`, refused when the agent does
|
|
1056
|
+
not offer `loadSession`), Pi with its session file, the local worker afresh.
|
|
1057
|
+
Codex and Claude are reported, not resumed: the TUI and the Claude session live
|
|
1058
|
+
outside the hub. The issue's Codex `thread/resume` needs the TUI, so the report
|
|
1059
|
+
names the thread instead.
|
|
1060
|
+
- Loss notices use the bus preface, not an envelope: a peer's `needs_review`
|
|
1061
|
+
rows block its later deliveries, so a notice queued behind them would arrive
|
|
1062
|
+
only after they are resolved anyway; the preface leads that next delivery. Only
|
|
1063
|
+
rows still in `needs_review` when the peer attaches are listed.
|
|
1064
|
+
- `recovery.auto_resume_after_crash` is off by default, also with a project
|
|
1065
|
+
config: an automatic start spends quota the user did not ask for.
|
|
1066
|
+
|
|
1067
|
+
## Amendment: deny-default sandbox and capabilities (issue #39)
|
|
1068
|
+
|
|
1069
|
+
- The deny-default profile allows exec and reads of the system directories
|
|
1070
|
+
(`/usr`, `/bin`, `/sbin`, `/System`, `/Library`, `/opt`, `/private/etc`, the
|
|
1071
|
+
dyld and timezone databases), the toolchain directories in home, `read_allow`,
|
|
1072
|
+
the project, its external git dirs, the selected developer dir (`xcode-select
|
|
1073
|
+
-p`) and temp; writes as before; a short list of mach services (directory
|
|
1074
|
+
lookups, logging, notifications), plus name resolution and TLS trust when
|
|
1075
|
+
network is on, the trust being the public CA bundles allowed by exact path
|
|
1076
|
+
after the denies (the `*.pem` key deny matches them); no brokers that act
|
|
1077
|
+
outside the sandbox (LaunchServices, SecurityServer). The denies at the end (credential
|
|
1078
|
+
stores, the denylist, `.agenthub`, git hooks and config) are shared by both
|
|
1079
|
+
bases.
|
|
1080
|
+
- The issue's allowlist proxy for `local.bash_network` is not built here: with
|
|
1081
|
+
the flag on, network is allowed as in 0.9 and earlier. It is left for a
|
|
1082
|
+
follow-up issue.
|
|
1083
|
+
- `local.sandbox` ("deny-default" | "allow-default") is machine-local, since
|
|
1084
|
+
"allow-default" widens the sandbox.
|
|
1085
|
+
- Capabilities: `propose`, `assign` (a proposal naming another peer as owner),
|
|
1086
|
+
`remember`, `important`. A peer not listed in `capabilities` has all of them,
|
|
1087
|
+
which keeps today's behaviour, and a listed peer whose value is not a list has
|
|
1088
|
+
none; the console user and the hub are never limited. `remember` gates the
|
|
1089
|
+
`hub_remember` tool only, not the notes the hub keeps of done summaries and
|
|
1090
|
+
review verdicts. Every refusal, a malformed entry and an unknown capability
|
|
1091
|
+
name are logged.
|
|
1092
|
+
`important` is checked where messages are admitted, the others in the task
|
|
1093
|
+
operations, so in-process tools (the local worker, Pi) are covered too.
|
|
1094
|
+
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-hub",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.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",
|