@staix/agent-hub 0.12.18 → 0.12.19
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 +5 -0
- package/README.md +2 -2
- package/docs/agent-notes/adapters.md +1 -0
- package/docs/operations.md +13 -7
- package/docs/quickstart.md +1 -1
- package/docs/security.md +1 -1
- package/docs/smoke.md +33 -0
- package/docs/specs/2026-09-19-agent-hub-design.md +13 -0
- package/docs/verified.json +9 -9
- package/package.json +1 -1
- package/plugins/agent-hub/.claude-plugin/plugin.json +1 -1
- package/plugins/agent-hub/server.js +1 -1
- package/src/hub/daemon.ts +14 -3
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,11 @@ Issue and pull request numbers in the entries for 0.7.7 and earlier refer to the
|
|
|
4
4
|
|
|
5
5
|
## Unreleased
|
|
6
6
|
|
|
7
|
+
## 0.12.19
|
|
8
|
+
|
|
9
|
+
- Pi's write, edit, bash and git write approvals offer `Always allow <tool> until Pi restarts`: later calls of that tool by the running Pi skip the prompt, still under the path guard and sandbox. The grant is in memory only, logged by tool name, and the dashboard can still only deny (#209).
|
|
10
|
+
- Control protocol stays 15 and events schema stays 1; a running 0.12.18 hub upgrades with the 0.12.19 coordinator.
|
|
11
|
+
|
|
7
12
|
## 0.12.18
|
|
8
13
|
|
|
9
14
|
- Attribute token increments, provider usage and completed turns to task ids by delivery or a single open task; add `ahub report --by task` with task/class totals, explicit unknown counters, unattributed shares and a separate historical bucket. Preserve PII id-only exports and derive no prices (#200).
|
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.12.
|
|
7
|
+
Status: 0.12.19, control protocol 15. 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
|
|
|
28
28
|
Or install the same version from GitHub:
|
|
29
29
|
|
|
30
30
|
```bash
|
|
31
|
-
bun add -g github:STAIxBWLB/agent-hub#v0.12.
|
|
31
|
+
bun add -g github:STAIxBWLB/agent-hub#v0.12.19 && ahub setup
|
|
32
32
|
```
|
|
33
33
|
|
|
34
34
|
The installed commands remain `ahub` and `agent-hub`.
|
|
@@ -6,6 +6,7 @@ Scope: the Codex, ACP and Pi adapters and the Pi extension (turn `generation` an
|
|
|
6
6
|
- Codex 0.154.0 `agentMessage` items carry `text` and `phase` (not `content[]`); only the last non-`commentary` message of a turn is shared.
|
|
7
7
|
- Approval titles are agent-written text shown to a person: the daemon escapes control characters, and write/edit/bash show what will be written or run.
|
|
8
8
|
- An ACP permission request may carry no `rawInput` (Kimi 2.0.1): the arguments were on the earlier `tool_call` update, which `acp.ts` remembers by `toolCallId`. Kimi 2.1.1 sends no `rawInput` before the answer at all: the argument JSON streams as `content` text on `tool_call_update`, and only complete JSON counts. A payload that still cannot be resolved, or is too long to show whole (marked `[cut, N chars]`), withholds its `allow_always` option - never render a bare tool name as if it described the call.
|
|
9
|
+
- Pi's `always` grant (#209) is keyed by `executeTool`'s tool name, never parsed from the approval title, and lives only in that Pi start's closure. Never persist it, share it with the local worker, or let the dashboard give it.
|
|
9
10
|
- Auto-approval covers the hub's own tools by exact `mcp__agent-hub__<name>` title and only ever picks `allow_once`. Identity comes from the permission request title or, when an agent titles the request with the argument JSON (Qwen), from the announced `tool_call` title bound to the call id and resolved against the session's configured MCP servers, never from payload text, a prefix or an unrelated display title. Never widen it to a prefix, and never set Kimi's session mode to `auto` or `yolo` instead: those approve everything.
|
|
10
11
|
- Every agent the hub spawns (the Codex app-server, ACP agents such as Kimi, Pi) runs in its own process group and is stopped as one (`stopOwnedProcess(proc, { group: true })`, `trackGroup` after spawn): `codex` is a node launcher, and mid-turn its native app-server does not exit on SIGTERM within the grace period, so a SIGKILL to the launcher alone left the app-server running under init and holding the daemon alive (#113, #115). A new adapter that spawns an agent does the same.
|
|
11
12
|
- To stop a process tree, freeze it (SIGSTOP) before reading what it contains, then SIGKILL, and call it done only when the table shows none of it (with no table at all, the stop can only ask the group itself, which cannot see groups below it). A process a read after the freeze shows for the first time is frozen and read again before anything is killed. A snapshot taken while the tree runs misses what it starts next: three review rounds of #113 found such a gap (groups of its own, an unreadable first read, a child started during the grace period).
|
package/docs/operations.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Operations guide
|
|
2
2
|
|
|
3
|
-
This guide describes ahub 0.12.
|
|
3
|
+
This guide describes ahub 0.12.19 and control protocol 15. Live verification
|
|
4
4
|
results and remaining prerequisites are recorded separately in [the smoke ledger](smoke.md).
|
|
5
5
|
|
|
6
6
|
## Operator console and panels
|
|
@@ -17,6 +17,12 @@ approval titles are terminal-only; expiry and answers from another console remov
|
|
|
17
17
|
the pending item. Approval audit records contain id, peer, option kind, response
|
|
18
18
|
time and answering surface, without the title.
|
|
19
19
|
|
|
20
|
+
Pi's write, edit, bash and git write requests also offer `Always allow <tool>
|
|
21
|
+
until Pi restarts`. It allows later calls of that tool by the running Pi without
|
|
22
|
+
asking, under the same path guard and sandbox; a Pi restart or replacement, or a
|
|
23
|
+
hub restart, drops it. The hub log records each granted call by tool name only.
|
|
24
|
+
The dashboard can only deny Pi's requests.
|
|
25
|
+
|
|
20
26
|
Tab toggles stream and panels; `ahub console --panels` starts in panels. Peers,
|
|
21
27
|
Approvals, Tasks, Queue and Events support arrow keys or j/k, Enter for detail,
|
|
22
28
|
Escape to return and `?` for help. `:` enters a command. Assignment, delivery
|
|
@@ -786,21 +792,21 @@ Rows without a live process are stale registrations; forget them with
|
|
|
786
792
|
|
|
787
793
|
Upgrade running projects with the target release's own coordinator. It accepts
|
|
788
794
|
a running source on control protocol 9 (0.6.x), 10 (0.7.0 through 0.12.0),
|
|
789
|
-
11 (0.12.1 and 0.12.2), 12 (0.12.3), 13 (0.12.4 through 0.12.15) 14 (0.12.16) or 15 (0.12.17
|
|
795
|
+
11 (0.12.1 and 0.12.2), 12 (0.12.3), 13 (0.12.4 through 0.12.15) 14 (0.12.16) or 15 (0.12.17 through 0.12.19), and only
|
|
790
796
|
a target on its own protocol, so the target's coordinator fits every supported
|
|
791
797
|
source and carries every recovery fix released up to it. Protocol 8 and older
|
|
792
798
|
(0.5.x and earlier) are refused as `manual-bootstrap-required`. Run from the
|
|
793
799
|
project directory, without replacing the global CLI first:
|
|
794
800
|
|
|
795
801
|
```bash
|
|
796
|
-
bunx --package @staix/agent-hub@0.12.
|
|
797
|
-
bunx --package @staix/agent-hub@0.12.
|
|
802
|
+
bunx --package @staix/agent-hub@0.12.19 ahub upgrade --to 0.12.19 --dry-run
|
|
803
|
+
bunx --package @staix/agent-hub@0.12.19 ahub upgrade --to 0.12.19 --yes
|
|
798
804
|
```
|
|
799
805
|
|
|
800
806
|
| Running now | Coordinator to use |
|
|
801
807
|
| --- | --- |
|
|
802
808
|
| 0.6.x (protocol 9) | the target's, through `bunx` as above |
|
|
803
|
-
| 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
|
|
809
|
+
| 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) | the target's, through `bunx` as above |
|
|
804
810
|
| any supported source, with the installed CLI already at the target | `ahub upgrade` below, which is the same coordinator |
|
|
805
811
|
| 0.5.x or earlier (protocol 8 and older) | not supported: bootstrap by hand with the matching CLI |
|
|
806
812
|
|
|
@@ -832,14 +838,14 @@ projects first:
|
|
|
832
838
|
|
|
833
839
|
```bash
|
|
834
840
|
ahub restart --dry-run
|
|
835
|
-
ahub upgrade --to 0.12.
|
|
841
|
+
ahub upgrade --to 0.12.19 --dry-run
|
|
836
842
|
```
|
|
837
843
|
|
|
838
844
|
Apply only after reviewing the plan:
|
|
839
845
|
|
|
840
846
|
```bash
|
|
841
847
|
ahub restart --yes
|
|
842
|
-
ahub upgrade --to 0.12.
|
|
848
|
+
ahub upgrade --to 0.12.19 --yes
|
|
843
849
|
ahub recovery status <operation-id>
|
|
844
850
|
ahub recovery resume <operation-id>
|
|
845
851
|
ahub recovery abort <operation-id>
|
package/docs/quickstart.md
CHANGED
package/docs/security.md
CHANGED
|
@@ -6,7 +6,7 @@ agent-hub connects agents that can each run commands. This page says what the hu
|
|
|
6
6
|
|
|
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, a fixed header line plus 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`. External clients cannot claim the console user's id or a hub-managed peer's id.
|
|
9
|
-
- **Permission prompts stay on.** `ahub claude` and `ahub codex` add nothing that weakens the agents' own prompts. Kimi's and `local`'s permission requests are relayed to the console and cancelled after `approvals.timeout_s` (default 120 s) of silence; the macOS notification for a waiting request carries the peer and the tool name only. The one exception is the hub's own tools (`hub_send` and the task tools, matched by exact name): Kimi's requests for them are approved once without a prompt and logged by name, the same trust Codex gets through `approval_mode` in the hub's config. They invoke hub-owned operations rather than arbitrary file or shell tools, and every call passes the hub's own checks. Identity comes from the exact permission title, or from the earlier tool-call title bound to the same call id and resolved against the configured MCP servers when the permission title contains argument JSON (Qwen). Payload text and unrelated display titles never establish identity.
|
|
9
|
+
- **Permission prompts stay on.** `ahub claude` and `ahub codex` add nothing that weakens the agents' own prompts. Kimi's and `local`'s permission requests are relayed to the console and cancelled after `approvals.timeout_s` (default 120 s) of silence; the macOS notification for a waiting request carries the peer and the tool name only. The one exception is the hub's own tools (`hub_send` and the task tools, matched by exact name): Kimi's requests for them are approved once without a prompt and logged by name, the same trust Codex gets through `approval_mode` in the hub's config. They invoke hub-owned operations rather than arbitrary file or shell tools, and every call passes the hub's own checks. Identity comes from the exact permission title, or from the earlier tool-call title bound to the same call id and resolved against the configured MCP servers when the permission title contains argument JSON (Qwen). Payload text and unrelated display titles never establish identity. 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. `--unattended` turns prompts off, says so loudly, and is never the default.
|
|
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`, `local.network_allow`) 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, and the task id (with a PII flag) that each usage and token record is attributed to. It never records a message body, a task title or detail, and marks private (PII) envelopes and tasks as such. It stays on the machine; `ahub export` only prints it.
|
|
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.
|
package/docs/smoke.md
CHANGED
|
@@ -1252,3 +1252,36 @@ the sentinel. Missing served-model evidence remains `identified: false`.
|
|
|
1252
1252
|
Before recording live acceptance, run a bounded unavailable-local/healthy-remote
|
|
1253
1253
|
leg and retain the sanitized JSON plus process cleanup confirmation. Fixture
|
|
1254
1254
|
verdicts do not certify provider availability or a real local generation.
|
|
1255
|
+
|
|
1256
|
+
## 0.12.18 live checks: task attribution and console colors (#200, #201, 2026-10-09)
|
|
1257
|
+
|
|
1258
|
+
The agent-hub project's own hub was upgraded from 0.12.17 to 0.12.18 by the
|
|
1259
|
+
user with the 0.12.18 coordinator, run from a plain terminal (an agent shell is
|
|
1260
|
+
refused by the identity gate). Claude (the session writing this) and Pi on
|
|
1261
|
+
`dgx/coding` were attached for the checks. Times are UTC.
|
|
1262
|
+
|
|
1263
|
+
- **The upgrade left Claude held.** Operation `c671d786` (source 0.12.17,
|
|
1264
|
+
protocol 15, running) was created at 05:26:55, the 0.12.18 daemon started
|
|
1265
|
+
the same second, and the operation completed with the project verified. Ten deliveries Claude had accepted but never settled (three board review
|
|
1266
|
+
requests, seven Codex chats, all handled before the upgrade) were moved to
|
|
1267
|
+
`needs_review` with "peer disconnected before delivery settlement", and
|
|
1268
|
+
Claude's queue was held behind them. `ahub queue resolve` is a console
|
|
1269
|
+
command, so only the user can release it; the unsettled acceptances are #205.
|
|
1270
|
+
- **#200 AC4, attribution on a live turn.** Board task #19 (class `test`,
|
|
1271
|
+
owner Pi) was proposed at 05:30:03, accepted at 05:30:07, done at 05:34:52
|
|
1272
|
+
and approved at 05:35:16. `ahub report --by task` at 05:35:06 attributed Pi's
|
|
1273
|
+
one turn and 602,255 tokens to #19 by its delivery. Unattributed: 2,159,396
|
|
1274
|
+
of 38,171,695 token increments (5.7%) and 21 of 116 usage records (18.1%),
|
|
1275
|
+
all Claude's own user-driven turns, which have no task-bearing delivery and
|
|
1276
|
+
no single open task. The `before attribution` bucket held 35,410,044 tokens
|
|
1277
|
+
and 95 records from before the upgrade. Both shares equal the JSON
|
|
1278
|
+
`unattributed` counts over `totals`, and the JSON carried task ids, class and
|
|
1279
|
+
outcome but no title or detail text.
|
|
1280
|
+
- **Pi could not run the check itself.** Its sandbox denies
|
|
1281
|
+
`.agenthub/state/` (the `ahub` binary under the bunx cache and `events.jsonl`
|
|
1282
|
+
both failed with EPERM). It reported every check as not run instead of
|
|
1283
|
+
passing it, and the reviewer ran them.
|
|
1284
|
+
- **#201 AC7, idle console CPU.** Sampled with `ps` cumulative CPU time while
|
|
1285
|
+
nothing happened on the hub: plain `ahub console`, 0.11 s over 60 s
|
|
1286
|
+
(about 0.18%); `ahub console --panels`, 0.46 s over 120 s from 05:52:30
|
|
1287
|
+
(about 0.38%, 0.21 to 0.25 s per minute, not growing).
|
|
@@ -1521,3 +1521,16 @@ to a task; both buckets appear even when empty. A zero denominator has unknown
|
|
|
1521
1521
|
share. Export carries the new fields without task text. No prices or savings
|
|
1522
1522
|
counterfactuals are derived. These additive fields retain events schema 1 and
|
|
1523
1523
|
the existing control protocol. Plain `ahub report` remains unchanged.
|
|
1524
|
+
|
|
1525
|
+
## Amendment: Pi "always" approvals (issue #209)
|
|
1526
|
+
|
|
1527
|
+
Pi's `write`, `edit`, `bash` and `git` write calls ask with three options:
|
|
1528
|
+
`allow` (`allow_once`), `always` (`allow_always`, named `Always allow <tool>
|
|
1529
|
+
until Pi restarts`) and `deny` (`reject_once`). `always` allows the call and
|
|
1530
|
+
every later call of the same hub tool by that Pi start without asking. The grant
|
|
1531
|
+
is held in memory by the Pi start: stopping or replacing Pi, or restarting the
|
|
1532
|
+
hub, drops it, and nothing is persisted. A granted call is logged by tool name
|
|
1533
|
+
only. The path guard, denylist and sandbox still apply to each call; the
|
|
1534
|
+
dashboard still offers Pi only deny; unattended mode still picks `allow_once`;
|
|
1535
|
+
the local worker keeps allow and deny. The console and `ahub permit` already
|
|
1536
|
+
handle more than one allow option, so the control protocol is unchanged.
|
package/docs/verified.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"schemaVersion": 1,
|
|
3
3
|
"documents": {
|
|
4
4
|
"README.md": {
|
|
5
|
-
"verifiedAgainst": "
|
|
5
|
+
"verifiedAgainst": "25d6ffe00c490a3690fc81782a45e62bfb383803",
|
|
6
6
|
"paths": [
|
|
7
7
|
"package.json",
|
|
8
8
|
"src/",
|
|
@@ -11,14 +11,14 @@
|
|
|
11
11
|
]
|
|
12
12
|
},
|
|
13
13
|
"docs/security.md": {
|
|
14
|
-
"verifiedAgainst": "
|
|
14
|
+
"verifiedAgainst": "25d6ffe00c490a3690fc81782a45e62bfb383803",
|
|
15
15
|
"paths": [
|
|
16
16
|
"src/",
|
|
17
17
|
"templates/"
|
|
18
18
|
]
|
|
19
19
|
},
|
|
20
20
|
"docs/operations.md": {
|
|
21
|
-
"verifiedAgainst": "
|
|
21
|
+
"verifiedAgainst": "25d6ffe00c490a3690fc81782a45e62bfb383803",
|
|
22
22
|
"paths": [
|
|
23
23
|
"package.json",
|
|
24
24
|
"src/",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
]
|
|
36
36
|
},
|
|
37
37
|
"docs/quickstart.md": {
|
|
38
|
-
"verifiedAgainst": "
|
|
38
|
+
"verifiedAgainst": "25d6ffe00c490a3690fc81782a45e62bfb383803",
|
|
39
39
|
"paths": [
|
|
40
40
|
"package.json",
|
|
41
41
|
"src/",
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
]
|
|
44
44
|
},
|
|
45
45
|
"docs/agent-notes/adapters.md": {
|
|
46
|
-
"verifiedAgainst": "
|
|
46
|
+
"verifiedAgainst": "25d6ffe00c490a3690fc81782a45e62bfb383803",
|
|
47
47
|
"paths": [
|
|
48
48
|
"src/adapters/",
|
|
49
49
|
"src/pi/",
|
|
@@ -68,7 +68,7 @@
|
|
|
68
68
|
]
|
|
69
69
|
},
|
|
70
70
|
"docs/agent-notes/budget.md": {
|
|
71
|
-
"verifiedAgainst": "
|
|
71
|
+
"verifiedAgainst": "25d6ffe00c490a3690fc81782a45e62bfb383803",
|
|
72
72
|
"paths": [
|
|
73
73
|
"src/hub/budget.ts",
|
|
74
74
|
"src/cli/statusline-tee.ts",
|
|
@@ -80,7 +80,7 @@
|
|
|
80
80
|
]
|
|
81
81
|
},
|
|
82
82
|
"docs/agent-notes/bus.md": {
|
|
83
|
-
"verifiedAgainst": "
|
|
83
|
+
"verifiedAgainst": "25d6ffe00c490a3690fc81782a45e62bfb383803",
|
|
84
84
|
"paths": [
|
|
85
85
|
"src/hub/bus.ts",
|
|
86
86
|
"src/hub/envelope.ts",
|
|
@@ -92,7 +92,7 @@
|
|
|
92
92
|
]
|
|
93
93
|
},
|
|
94
94
|
"docs/agent-notes/daemon.md": {
|
|
95
|
-
"verifiedAgainst": "
|
|
95
|
+
"verifiedAgainst": "25d6ffe00c490a3690fc81782a45e62bfb383803",
|
|
96
96
|
"paths": [
|
|
97
97
|
"src/hub/daemon.ts",
|
|
98
98
|
"src/hub/control-client.ts",
|
|
@@ -151,7 +151,7 @@
|
|
|
151
151
|
]
|
|
152
152
|
},
|
|
153
153
|
"docs/agent-notes/tests.md": {
|
|
154
|
-
"verifiedAgainst": "
|
|
154
|
+
"verifiedAgainst": "25d6ffe00c490a3690fc81782a45e62bfb383803",
|
|
155
155
|
"paths": [
|
|
156
156
|
"test/",
|
|
157
157
|
"scripts/check.sh",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-hub",
|
|
3
|
-
"version": "0.12.
|
|
3
|
+
"version": "0.12.19",
|
|
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",
|
|
@@ -15403,7 +15403,7 @@ class ControlClient {
|
|
|
15403
15403
|
// package.json
|
|
15404
15404
|
var package_default = {
|
|
15405
15405
|
name: "@staix/agent-hub",
|
|
15406
|
-
version: "0.12.
|
|
15406
|
+
version: "0.12.19",
|
|
15407
15407
|
description: "Native multi-agent hub: Claude Code, Codex, Kimi Code, Pi and local inference as peers in one project",
|
|
15408
15408
|
license: "MIT",
|
|
15409
15409
|
type: "module",
|
package/src/hub/daemon.ts
CHANGED
|
@@ -1979,11 +1979,22 @@ export async function startDaemon(opts: DaemonOptions) {
|
|
|
1979
1979
|
});
|
|
1980
1980
|
piReceipts ??= new PiToolReceipts(join(opts.stateDir, "hub.db"));
|
|
1981
1981
|
let piReply: Envelope | undefined;
|
|
1982
|
+
// Tools the person allowed "always" for this Pi start; a new start gets a new set (#209).
|
|
1983
|
+
const piAlways = new Set<string>();
|
|
1984
|
+
const piPermit = async (title: string, tool: string): Promise<boolean> => {
|
|
1985
|
+
if (piAlways.has(tool)) log(`permission auto-allowed for pi: ${tool} (granted until Pi restarts)`); // the name only, never the arguments
|
|
1986
|
+
else {
|
|
1987
|
+
const picked = await onPermission({ peer: "pi", title, options: [{ optionId: "allow", name: "Allow", kind: "allow_once" }, { optionId: "always", name: `Always allow ${tool} until Pi restarts`, kind: "allow_always" }, { optionId: "deny", name: "Deny", kind: "reject_once" }] });
|
|
1988
|
+
if (picked === "always") piAlways.add(tool);
|
|
1989
|
+
else if (picked !== "allow") return false;
|
|
1990
|
+
}
|
|
1991
|
+
return pi.acceptingTools && bus.peers.get("pi") === pi;
|
|
1992
|
+
};
|
|
1982
1993
|
const ctx: ToolContext = {
|
|
1983
1994
|
cwd: opts.cwd, deny: config.local.deny,
|
|
1984
1995
|
sandboxProfile: profile(opts.cwd, sandboxNetwork, config.local.read_allow, config.local.deny),
|
|
1985
1996
|
sandboxEnv: { ...proxyEnv(sandboxNetwork), AGENTHUB_PEER_ID: "pi" },
|
|
1986
|
-
permit: (
|
|
1997
|
+
permit: async () => false, // Pi's calls ask through piPermit in executeTool, which knows the tool's name
|
|
1987
1998
|
send: (text, to) => {
|
|
1988
1999
|
if (to?.some((id) => !bus.peers.has(id) && id !== USER)) return "error: unknown peer";
|
|
1989
2000
|
const refused = pi.onMessage?.(text, { inReplyTo: piReply, ...(to?.length ? { to } : {}) });
|
|
@@ -2005,12 +2016,12 @@ export async function startDaemon(opts: DaemonOptions) {
|
|
|
2005
2016
|
if (!raw || typeof raw !== "object" || Array.isArray(raw)) return "error: invalid tool arguments";
|
|
2006
2017
|
const nativeTurn = pi.observationTurn;
|
|
2007
2018
|
const output = TASK_TOOLS.some((t) => t.name === name) || CONDUCTOR_TOOL_NAMES.has(name) ? await taskOp("pi", name, raw as Record<string, unknown>, true) : await runTool(name, JSON.stringify(raw), { ...ctx, signal, permit: async title => {
|
|
2008
|
-
if (!signal) return
|
|
2019
|
+
if (!signal) return piPermit(title, name);
|
|
2009
2020
|
if (signal.aborted) return false;
|
|
2010
2021
|
return new Promise<boolean>((resolve, reject) => {
|
|
2011
2022
|
const finish = (allowed: boolean) => { signal.removeEventListener("abort", aborted); resolve(allowed && !signal.aborted); };
|
|
2012
2023
|
const aborted = () => finish(false); signal.addEventListener("abort", aborted, { once: true });
|
|
2013
|
-
|
|
2024
|
+
piPermit(title, name).then(finish, error => { signal.removeEventListener("abort", aborted); reject(error); });
|
|
2014
2025
|
});
|
|
2015
2026
|
} });
|
|
2016
2027
|
if (!signal?.aborted && bus.peers.get("pi") === pi) {
|