@staix/agent-hub 0.29.0 → 0.31.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 +51 -0
- package/README.md +15 -7
- package/docs/agent-notes/README.md +13 -0
- package/docs/agent-notes/adapters.md +7 -3
- package/docs/agent-notes/budget.md +1 -0
- package/docs/agent-notes/bus.md +10 -4
- package/docs/agent-notes/daemon.md +16 -8
- package/docs/agent-notes/local-worker.md +1 -0
- package/docs/agent-notes/tasks.md +10 -4
- package/docs/agent-notes/tests.md +8 -4
- package/docs/design/adapters.md +108 -0
- package/docs/design/agent-hub-architecture.md +58 -0
- package/docs/design/bus.md +147 -1
- package/docs/design/task-board.md +100 -0
- package/docs/dogfood.md +29 -8
- package/docs/events.md +3 -0
- package/docs/memory.md +17 -1
- package/docs/operations.md +410 -86
- package/docs/profiles.md +35 -0
- package/docs/quickstart.md +25 -5
- package/docs/sdlc.md +1 -1
- package/docs/security.md +182 -62
- package/docs/smoke.md +13 -2
- package/docs/specs/2026-09-19-agent-hub-design.md +84 -14
- package/docs/specs/2026-09-19-multi-project-design.md +7 -2
- package/docs/specs/2026-10-12-office-view-design.md +86 -3
- package/docs/verification/2026-11-03-0.30.0.md +54 -0
- package/docs/verification/2026-11-03-agy-1.4.12-launcher-nonce.md +42 -0
- package/docs/verification/2026-11-03-agy-1.4.12.md +90 -0
- package/docs/verification/2026-11-03-agy-tui-close.md +49 -0
- package/docs/verification/2026-11-03-grok-1.0.62-t0.md +150 -0
- package/docs/verification/2026-11-03-pi-150-compat.md +40 -0
- package/docs/verification/2026-11-03-pi-title-api.md +26 -0
- package/docs/verification/2026-11-03-qwen-0265-acp.md +55 -0
- package/docs/verification/2026-11-03-qwen-0265-dual-output.md +40 -0
- package/docs/verification/2026-11-04-1171-channel-client-evidence.md +31 -0
- package/docs/verification/2026-11-05-0.31.0.md +182 -0
- package/docs/verification/2026-11-05-claude-implementer-2.1.318.md +70 -0
- package/docs/verification/2026-11-05-hardcoded-values-classification-rules.md +257 -0
- package/docs/verification/2026-11-05-hardcoded-values-ratchet.md +55 -0
- package/docs/verification/2026-11-06-rooms-presence-cost.md +152 -0
- package/docs/verification/claude-shell-wrapper-native.py +1 -1
- package/docs/verification/dogfood/2026-11-03.md +3095 -0
- package/docs/verification/pi-150-model-free.ts +57 -0
- package/docs/verification/pi-150-native.mjs +21 -0
- package/docs/verification/pi-150-title-api.mjs +31 -0
- package/docs/verified.json +14 -14
- package/package.json +3 -4
- package/plugins/agent-hub/.claude-plugin/plugin.json +1 -1
- package/plugins/agent-hub/server.js +4565 -3617
- package/src/adapters/acp.ts +5 -4
- package/src/adapters/channel-peer.ts +72 -3
- package/src/adapters/claude-channel-evidence.ts +52 -0
- package/src/adapters/claude-channel.ts +61 -18
- package/src/adapters/claude-implementer.ts +29 -7
- package/src/adapters/claude-print-child.ts +12 -2
- package/src/adapters/codex-appserver.ts +13 -12
- package/src/adapters/compat.ts +13 -13
- package/src/adapters/definitions/builtin-metadata.ts +13 -13
- package/src/adapters/definitions/qwen.ts +1 -0
- package/src/adapters/local-worker.ts +82 -23
- package/src/adapters/pi.ts +140 -16
- package/src/adapters/registry-types.ts +16 -0
- package/src/adapters/registry.ts +4 -0
- package/src/adapters/reviewer-print.ts +50 -11
- package/src/broker/server.ts +44 -13
- package/src/broker/store.ts +38 -8
- package/src/cli/answer-args.ts +50 -0
- package/src/cli/attention.ts +17 -2
- package/src/cli/autonomy.ts +66 -1
- package/src/cli/backlog-return-args.ts +37 -0
- package/src/cli/console-question.ts +110 -0
- package/src/cli/console-state.ts +82 -17
- package/src/cli/console.ts +54 -1
- package/src/cli/handoff-args.ts +15 -5
- package/src/cli/handoff-output.ts +15 -2
- package/src/cli/help.ts +10 -5
- package/src/cli/identity.ts +21 -4
- package/src/cli/main.ts +167 -16
- package/src/cli/memory.ts +107 -3
- package/src/cli/output.ts +29 -6
- package/src/cli/pet.ts +4 -4
- package/src/cli/results-args.ts +30 -0
- package/src/cli/runtime-version-warning.ts +50 -0
- package/src/cli/status-lines.ts +41 -1
- package/src/cli/tail-render.ts +1 -0
- package/src/cli/upgrade-interactive.ts +14 -2
- package/src/cli/worktree-tidy.ts +158 -0
- package/src/cli/worktrees-output.ts +43 -0
- package/src/hub/action-class.ts +9 -1
- package/src/hub/board.ts +396 -20
- package/src/hub/bus.ts +497 -57
- package/src/hub/completion-detail.ts +50 -0
- package/src/hub/completions-store.ts +168 -0
- package/src/hub/completions.ts +120 -0
- package/src/hub/conductor.ts +4 -3
- package/src/hub/config-apply.ts +8 -5
- package/src/hub/config-reload.ts +10 -1
- package/src/hub/control-client.ts +40 -4
- package/src/hub/daemon.ts +1795 -142
- package/src/hub/delivery-failure.ts +14 -0
- package/src/hub/delivery-hints.ts +14 -4
- package/src/hub/delivery-journal.ts +751 -42
- package/src/hub/delivery-superseded.ts +4 -1
- package/src/hub/dogfood-read.ts +62 -9
- package/src/hub/envelope.ts +3 -3
- package/src/hub/events.ts +6 -0
- package/src/hub/handoff.ts +145 -6
- package/src/hub/hub-tools.ts +66 -11
- package/src/hub/manager.ts +67 -3
- package/src/hub/memory-policy.ts +44 -1
- package/src/hub/memory-sync-policy.ts +109 -0
- package/src/hub/memory-sync-state.ts +145 -0
- package/src/hub/no-writes.ts +1 -1
- package/src/hub/operational-settings.ts +11 -2
- package/src/hub/operational-view.ts +12 -1
- package/src/hub/peer-doing.ts +33 -3
- package/src/hub/peers.ts +32 -2
- package/src/hub/planning.ts +6 -1
- package/src/hub/question-desk.ts +201 -0
- package/src/hub/question-detach.ts +6 -0
- package/src/hub/questions.ts +260 -0
- package/src/hub/research-evidence.ts +106 -0
- package/src/hub/restart.ts +14 -0
- package/src/hub/review-anchor.ts +181 -0
- package/src/hub/review-brief.ts +13 -0
- package/src/hub/review-comments.ts +115 -0
- package/src/hub/review-findings.ts +7 -2
- package/src/hub/review-material.ts +76 -0
- package/src/hub/routing.ts +3 -1
- package/src/hub/settings.ts +3 -0
- package/src/hub/task-sweep.ts +2 -0
- package/src/hub/tasks.ts +607 -71
- package/src/hub/ui.ts +2 -0
- package/src/hub/worktree-prs.ts +100 -0
- package/src/hub/worktree-shelf.ts +246 -0
- package/src/memory/capture.ts +5 -2
- package/src/pet/config.ts +25 -13
- package/src/pi/extension.ts +43 -3
- package/src/rooms/link.ts +209 -21
- package/src/rooms/remote-peer.ts +186 -19
- package/src/rooms/service.ts +338 -22
- package/src/rooms/store.ts +437 -65
- package/src/rooms/wire.ts +24 -6
- package/src/ui/index.html +271 -17
- package/src/ui/needs.ts +10 -4
- package/src/ui/office-model.ts +64 -8
- package/src/ui/office-space.ts +1 -0
- package/src/ui/plugin-questions.ts +56 -0
- package/src/ui/plugin-results.ts +81 -0
- package/src/ui/plugin.ts +219 -28
- package/src/pet/window.html +0 -114
- package/src/pet/window.jxa.js +0 -122
- package/src/pet/window.ts +0 -154
package/docs/security.md
CHANGED
|
@@ -26,7 +26,7 @@ agent-hub connects agents that can each run commands. This page says what the hu
|
|
|
26
26
|
|
|
27
27
|
## Native memory authority and profile selection
|
|
28
28
|
|
|
29
|
-
The portable core is the exact-pinned first-party `@staix/agent-mem` 0.
|
|
29
|
+
The portable core is the exact-pinned first-party `@staix/agent-mem` 0.21.0 package; it does not replace the hub's caller identity, PII policy or task-write authority. Deny, hardened git, real paths and process identity are single-sourced in its Node-safe platform entry. Current project privacy is applied by the hub before promotion and again before model-facing reads. Standalone person reads are unscreened, and the standalone `--policy-free` acknowledgement grants no hub or peer authority.
|
|
30
30
|
|
|
31
31
|
`hub_memory_search`, `hub_memory_show` and `hub_memory_graph` take the project from the daemon, require `memory_read`, refuse private turns and validate UTF-8 input before logs or storage work. Complete records and graph metadata are screened before counts and truncation. Search is at most 20 lines/4 KiB (default 8); show accepts at most 8 ids and returns at most 8 KiB; graph is at most 120 nodes/240 edges/6 KiB (default 60 nodes), with no edge lacking an endpoint. Unknown, ambiguous and withheld ids share one neutral refusal. Arguments and results are excluded from hub-managed capture; this does not govern a vendor's independent transcript or memory hooks.
|
|
32
32
|
|
|
@@ -165,6 +165,20 @@ enrollment and X4 privacy/framing/role boundaries are in their own sections belo
|
|
|
165
165
|
traffic metadata, availability, malicious owners and same-user compromise remain
|
|
166
166
|
outside the encryption boundary.
|
|
167
167
|
|
|
168
|
+
Room presence uses a signed `ephemeral: true` header (#1204 T1). This reveals
|
|
169
|
+
presence traffic class to the blind broker, not peer state or body text. Only the
|
|
170
|
+
latest sender observation is retained for connected, ready recipients; offline
|
|
171
|
+
recipients and broker restart do not retain presence. Durable queue limits and
|
|
172
|
+
receiver replay/admission checks are unchanged. A `queue_full` storage refusal
|
|
173
|
+
names the verified frame digest; local bounded backoff retries that same sealed
|
|
174
|
+
frame and grants no completion authority.
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
The ephemeral advertisement is a transport capability, not authentication or
|
|
178
|
+
completion authority. Old brokers receive the legacy frame shape. The broker withholds ephemeral frames from older member
|
|
179
|
+
hubs until they upgrade; chat
|
|
180
|
+
frames remain compatible. See [room wire compatibility](design/bus.md#room-wire-compatibility-1204-t1).
|
|
181
|
+
|
|
168
182
|
## What is not defended
|
|
169
183
|
|
|
170
184
|
- **A malicious local process running as you.** It can read the control token, `hub.db`, the project and your home directory without the hub's help. The token keeps web pages and other users out, not your own processes.
|
|
@@ -362,7 +376,7 @@ as that peer in tools mode. Messages and task changes retain that actor;
|
|
|
362
376
|
capability. Conflicting or malformed markers fail closed. Refusals name only the marker variable names actually present, never their values. There is no `--as-user`.
|
|
363
377
|
By default, console operations are refused before connecting, including permission answers
|
|
364
378
|
and permission modes, agent version trust, budget overrides, role assignment, `--as-user`, recovery
|
|
365
|
-
operations, the interactive terminals (`watch`,
|
|
379
|
+
operations, the interactive terminals (`watch`, a launcher's person-only flags), and `ask`
|
|
366
380
|
(#933 keeps the person's class to trust, money, attribution and interactive terminals).
|
|
367
381
|
For live verification explicitly authorized by the user, an agent may invoke
|
|
368
382
|
`ahub --live-test [--project <path|id>] <command> [args...]`. The flag delegates
|
|
@@ -389,8 +403,8 @@ messages, proposals and reviews. Peer starts, pause and resume may delegate
|
|
|
389
403
|
operational console authority; task assign/escalate under the flag refuse. `--as-user`, person task labels,
|
|
390
404
|
person-authored planning/release/memory/goal writes, SDLC record, spec and plan-departure acceptance, release authorization, design-spec stamps (`ahub specs stamp`), agent version trust, benchmark run,
|
|
391
405
|
`ahub recovery reattach`, a `--fresh-session` dispose that would replace a repaired
|
|
392
|
-
session's conversation (or whose scope cannot be read),
|
|
393
|
-
|
|
406
|
+
session's conversation (or whose scope cannot be read), `ahub answer` with an
|
|
407
|
+
operand and the `ahub results` receipt forms are refused; presentation flags such as `--color` or `--verbose` before
|
|
394
408
|
the recovery action do not hide it from this check. No argument text is copied into this audit.
|
|
395
409
|
Delegated dashboard restrictions are bound to its server-issued ticket/session:
|
|
396
410
|
operational settings/start/stop controls remain, but message, proposal and
|
|
@@ -465,9 +479,10 @@ start (Jcode). Memory changes and the other `goal` changes stay the person's
|
|
|
465
479
|
while amem records their actor as the user. `task unassign` is a conductor form;
|
|
466
480
|
it keeps the conductor as the board actor. `task cancel` remains separately
|
|
467
481
|
available through `hub_task_cancel` only on the caller's own eligible task
|
|
468
|
-
(#933),
|
|
469
|
-
|
|
470
|
-
`
|
|
482
|
+
(#933), and for the conductor only on an unaccepted task another project handed
|
|
483
|
+
off (#1175), not as a general conductor operation; `task role`, `task label` and
|
|
484
|
+
`ahub deliverables` retain their own-task checks. `models start|stop` remains
|
|
485
|
+
Console. `restart --yes`,
|
|
471
486
|
`upgrade --yes --to <exact-version>` and `recovery resume|abort` have the
|
|
472
487
|
separate continuity proof described below. Each conductor operation is admitted
|
|
473
488
|
after a recheck of role and project membership; effectful operations and history
|
|
@@ -557,7 +572,7 @@ Please report vulnerabilities privately through GitHub's "Report a vulnerability
|
|
|
557
572
|
- The exchange creates a separate random session, valid for one hour without renewal, in an `HttpOnly; SameSite=Strict; Path=/` cookie named for this listener's port. Sessions and tickets live only in memory and die with the daemon. The page itself keeps one more cookie, `agent-hub-theme` (issue #246): script-set, `SameSite=Strict`, kept for a year for the whole `127.0.0.1` host, and read back only as one of `system`, `light` or `dark`. A second script-set cookie, `agent-hub-office` (issue #264), keeps the Office view choice under the same attributes and is read back only as `off`, `flat` or `isometric`. The #391 view split adds two more script-set cookies under the same attributes: `agent-hub-view`, read back only as one of the seven view names, and `agent-hub-column-focus`, read back only as one of the five board columns or empty. HTTP is loopback-only; the cookie is not a substitute for the origin checks.
|
|
558
573
|
- Every request must have the exact listener Host. Every data or action request, including the ticket exchange and snapshot polling, must be POST with the exact listener Origin and JSON content type. Foreign and missing origins, missing or expired sessions, oversized bodies and unknown actions are rejected. There is no CORS support. The only unauthenticated GET is the fixed, data-free HTML shell. No files or paths are served dynamically.
|
|
559
574
|
- Responses are non-cacheable, cannot be framed, suppress referrers, and use a content security policy restricting scripts/styles to the shipped inline content and connections to this origin. Browser text is rendered with textContent, never interpreted as HTML.
|
|
560
|
-
- The browser receives a bounded redacted event stream, peer states and queue counts, public task views (with a task's screened changed-symbol observation, #684: ids, paths, names and kinds, never source or line spans, and its Code group, #684 phase 2c: those symbols marked against the plan's names, each name re-screened by the public metadata screen; a private task has no group and its plan names never enter the snapshot), budget windows, each peer's DOING line (#878: a native session title or current plan item, at most 120 characters, kept in daemon memory only and screened when observed and again when shown; a failed screen, a private turn or the peer's open PII task shows `[private]`), pending approvals, the settings rows (closed values and ids, the file each comes from) and each startable peer's start plan (issue #269), benchmark run summaries (issue #251): the newest runs in `~/.agenthub/bench/`, whichever project on this machine ran them, by suite name, arm label, state, the task id in progress, counts, tokens and times, never suite text, the Usage view (#683 U4), computed only while the Agents view is open: peer ids, UTC days and token counts with their source, budget pause, resume and reset events without their reason text, and per-task rows of the task id, a PII flag and token counts, never a title, and the office view's optional fields (issue #264): each peer's open turn (start time, attributed task id, attribution kind), a `checking` flag on tasks whose check is running, and warning `links` of overlapping, conflicting or cohort tasks (task and peer ids only, never paths, symbols or reasons; a PII task never joins a link, and a private or PII-attributed turn exposes no task id). Private envelope bodies and PII task text never cross this endpoint. Local-worker and Pi approval titles can contain PII, so their details remain terminal-only; the dashboard identifies the request and directs the operator to `ahub tail` before allowing it. Budget checkpoint summaries are also omitted.
|
|
575
|
+
- The browser receives a bounded redacted event stream, peer states and queue counts, public task views (with a task's screened changed-symbol observation, #684: ids, paths, names and kinds, never source or line spans, and its Code group, #684 phase 2c: those symbols marked against the plan's names, each name re-screened by the public metadata screen; a private task has no group and its plan names never enter the snapshot), budget windows, each peer's DOING line (#878: a native session title or current plan item, at most 120 characters, kept in daemon memory only and screened when observed and again when shown; a failed screen, a private turn or the peer's open PII task shows `[private]`), pending approvals, the settings rows (closed values and ids, the file each comes from) and each startable peer's start plan (issue #269), benchmark run summaries (issue #251): the newest runs in `~/.agenthub/bench/`, whichever project on this machine ran them, by suite name, arm label, state, the task id in progress, counts, tokens and times, never suite text, the Usage view (#683 U4), computed only while the Agents view is open: peer ids, UTC days and token counts with their source, budget pause, resume and reset events without their reason text, and per-task rows of the task id, a PII flag and token counts, never a title, and the office view's optional fields (issue #264): each peer's open turn (start time, attributed task id, attribution kind), a `checking` flag on tasks whose check is running, and warning `links` of overlapping, conflicting or cohort tasks (task and peer ids only, never paths, symbols or reasons; a PII task never joins a link, and a private or PII-attributed turn exposes no task id). Private envelope bodies and PII task text never cross this endpoint. Local-worker and Pi approval titles can contain PII, so their details remain terminal-only; the dashboard identifies the request and directs the operator to `ahub tail` before allowing it. Budget checkpoint summaries are also omitted. An ordinary (not live-test) session's snapshot also carries the open questions with their full text, a private one included, for the person's answer form (#1053), and unread results with the detail built when shown (#1054). An explicit review refresh of one task adds its person review comment threads and placement, with PII and private-path comments as counts only (#1056).
|
|
561
576
|
- The closed action list includes permission response, peer pause/resume, console message, task proposal/assignment and the settings actions below (set, undo, preview, reload, and the structural edit preview and apply of a settings session). Project-board actions add revision-fenced planning, Backlog pull/return/split/undo and release planning/state changes through Tasks; they do not tag, publish or write GitHub state. These use the existing daemon/task paths and budget pause rules. `start_peer` (issue #269) starts a peer from a closed list in the mode a person set, with a command the hub assembles: the request carries a peer id and nothing else. There is no generic control proxy, task-detail read, shell, file access, free-form configuration edit, budget override, launch of a caller-chosen command or daemon shutdown API.
|
|
562
577
|
- The listener stops with the daemon. Expired sessions must be reopened with `ahub ui`; the page does not silently obtain new credentials.
|
|
563
578
|
|
|
@@ -598,30 +613,38 @@ its existing basename/worktree aliases and provider accounts can remain shared.
|
|
|
598
613
|
|
|
599
614
|
`@staix/agent-hub/ui-plugin` runs inside the agent-console (`acon`) process as the
|
|
600
615
|
same user. It holds no credential of its own: it reaches project hubs only through
|
|
601
|
-
the multi-project manager
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
616
|
+
the multi-project manager, which connects as the console with each project's
|
|
617
|
+
control token. Beyond what `ahub ui --all` has, it uses dedicated manager ports the
|
|
618
|
+
dashboard page cannot reach: `answer` (#1053), `receipt` (#1054) and the review port
|
|
619
|
+
(`review_comment_put|delete|send`, `review_comments`, `review_blob`, #1056). Every
|
|
620
|
+
write through them requires acon's verified `ActionMeta`, as described in those
|
|
621
|
+
sections. Every other action must be on the manager's closed list and every view one
|
|
622
|
+
of the seven view names. There is no generic control proxy, task-detail read, shell
|
|
623
|
+
or caller-chosen command, and the only file read is the bounded review material of a
|
|
624
|
+
public task (`review_blob`).
|
|
606
625
|
|
|
607
626
|
- **Registration.** `ahub setup` writes `console/plugins/ahub.json` under the hub
|
|
608
627
|
home (directory 0700, file 0600, atomic replace) with the module path and its
|
|
609
628
|
sha256; acon refuses to load a module whose hash no longer matches.
|
|
610
629
|
- **Confirmation.** Outward or destructive actions (`stop`, `permit`, `send`,
|
|
611
630
|
`setting`, `setting_undo`, `setting_reload`, `config_edit_apply`, `start_peer`,
|
|
612
|
-
`backlog_split`
|
|
631
|
+
`backlog_split`, `worktree_override_set`, `worktrees_refresh_prs`,
|
|
632
|
+
`review_comment_send`, `review_comment_delete` and the inbox's Allow once and Deny) first answer `needsConfirm`
|
|
613
633
|
with a preview and a fingerprint, a sha256 of the request's own fields; the call
|
|
614
634
|
runs only when it comes back with `confirmed` and that fingerprint. The
|
|
615
635
|
fingerprint binds a confirmation to one exact request, so a changed request needs
|
|
616
636
|
a new confirmation; it is not a secret, and the confirm step is acon's host UI
|
|
617
637
|
contract, not authentication. A process that can load the module already has the
|
|
618
638
|
console's reach, and the hostile-agent boundary above applies unchanged.
|
|
619
|
-
- **Inbox.** Items are the pending approvals
|
|
620
|
-
is the person,
|
|
639
|
+
- **Inbox.** Items are the pending approvals, the in-review tasks whose reviewer
|
|
640
|
+
is the person, open questions (#1053) and, for a console that gives receipts
|
|
641
|
+
person evidence, unread results (#1054), read from the same dashboard snapshot: a local-worker or
|
|
621
642
|
Pi approval shows only the fixed private stub and offers Deny, and Allow once for
|
|
622
643
|
it is refused with "answer this approval with ahub permit". Allow once and Deny
|
|
623
644
|
resolve to the hub's own `permit` with an option the pending request offers,
|
|
624
|
-
looked up again at the time of the call; nothing else is reachable from an
|
|
645
|
+
looked up again at the time of the call; nothing else is reachable from an approval
|
|
646
|
+
item. A question's Answer and a result's mark-read go only through the person-evidence
|
|
647
|
+
paths in "Questions a peer asks the person" and "Results a person's runs leave behind".
|
|
625
648
|
Task titles are the public view's, so a PII task shows the `[pii]` stub.
|
|
626
649
|
- **Space.** The Space scene is built only from the office model the dashboard
|
|
627
650
|
already allowlists (#264): peer ids, conditions and counts, task ids, classes,
|
|
@@ -644,6 +667,15 @@ file edits, and a configuration reload refuses roster changes. External ids outs
|
|
|
644
667
|
admission rules. A roster key left by an undeclared instance loads as inert and grants
|
|
645
668
|
nothing; the only change it accepts is its removal from `config.local.json`.
|
|
646
669
|
|
|
670
|
+
Handing a peer to a standby session (#1174) is the person's: `peer_takeover` is
|
|
671
|
+
console-role only, and `ahub peer takeover` is refused to agent shells and
|
|
672
|
+
`--live-test`. A standby connection runs no peer operation until it owns the peer,
|
|
673
|
+
and the selected standby is rechecked for membership, launch and isolation,
|
|
674
|
+
lifecycle and claim generation after its preface. Standby ids and reported PIDs are
|
|
675
|
+
display metadata, never native-session or permission evidence. Takeover detaches
|
|
676
|
+
the old hub connection, not its native process; that session's later hub calls are
|
|
677
|
+
refused, and takeover never marks a delivery completed.
|
|
678
|
+
|
|
647
679
|
Kimi's pull-only TUI owns its tool approvals and session. The person's launcher
|
|
648
680
|
merges/removes only the hub MCP entry; the daemon never edits that file and
|
|
649
681
|
refuses headless starts while a stale entry exists. An attached Kimi TUI blocks
|
|
@@ -673,7 +705,7 @@ withholds the scoped mode on these writes so an older hook grants nothing too.
|
|
|
673
705
|
|
|
674
706
|
Agent CLI versions (#691). One policy gates the Codex and Kimi fresh reviewers, the headless Claude implementer, Jcode, agy, Qwen (ACP and the dual-output TUI) and Pi (#843): a receipt-verified version passes, Jcode and agy also start an untested version inside their compatible line with a note, and any other version is refused unless the person trusts that exact version on this machine with `ahub agents trust <id> <version>`. The record is `agent-compat.json` in the hub home (`~/.agenthub`), written atomically with mode 600 and at most 64 entries; an unreadable, linked, oversized or invalid record trusts nothing. Agent shells and `--live-test` may read `ahub agents` but cannot trust or revoke. Trust lifts only the version pin: the fresh reviewers' runtime proofs still run before every child, and an untested version's native contract stays unverified.
|
|
675
707
|
|
|
676
|
-
Qwen Code's optional production ACP peer requires the qwen-code native identity, a compat-admitted version (receipt-verified 0.25.0, 0.26.0, 0.26.1 and 0.26.
|
|
708
|
+
Qwen Code's optional production ACP peer requires the qwen-code native identity, a compat-admitted version (receipt-verified 0.25.0, 0.26.0, 0.26.1, 0.26.4 and 0.26.5)
|
|
677
709
|
and advertised mode ids before setting a policy or starting work. Its own tools
|
|
678
710
|
have no hub sandbox; auto-edit approves file edits, yolo every tool. Configured
|
|
679
711
|
never-ask still requires the person's confirmation. qwen_cmd is machine-local and
|
|
@@ -682,7 +714,7 @@ this production table. Startup diagnostics withhold native host/payload details.
|
|
|
682
714
|
|
|
683
715
|
Jcode is optional, source/version-fenced and runs native tools without a hub sandbox or hub permission-mode mapping. .jcode joins the scoped-grant exclusion, including lexical/resolved aliases. Only a person writes its unique project MCP entry; foreign/override/symlink entries refuse. The hub owns a private daemon/socket and ACP group, never the global daemon, and copies no credentials. It accepts a plain jcode release from 0.98.0 up to, not including, 1.0.0 (#529; only 0.98.1, 0.99.0 and 0.99.1 are verified, and another version in that line starts with a note), or an exact version the person trusts (#691), and runs every owned jcode process (probe, serve, ACP and TUI) with `JCODE_NO_TELEMETRY=1`, because jcode's telemetry can report the provider account's email. Native diagnostics withhold payloads/hosts. Hub tools are bound to an owned launch (task 368): the shared project entry names no peer, each owned Jcode process carries its own peer id and launch id, the daemon admits a hub-tools connection for a Jcode peer only with the id of that peer's current owned launch, and a tools-mode server started without a peer id refuses every tool. Extra Jcode instances (#685, at most 8) are declared only through `instances` in an untracked `.agenthub/config.local.json`, start headless, and each needs its own `jcode_home`, which may not be the base home or another instance's, nor nested in or around one, however it is spelled. Unproved conversation loads refuse; no fresh replacement is called a restore. A finished task's advisory about the project root checkout names the branch only after the hub's whole public-text screen (`Tasks.publicMetadataText`: a PII pattern, a secret shape and a project-private path) passes it and it is a plain ref name; otherwise a generic sentence carries no part of it (#572). Native TUI launch exposes its private socket/session tuple only to the authenticated console, bound to the same live owned socket and session after read-only native persistence proof. Tools/status do not receive it. The TUI has no single-writer guard: person input may interleave with ACP. Native policy remains unmanaged and closing the frontend does not stop ACP. An `important` message reaches a busy Jcode as a soft interrupt written to the hub's own private server socket for the owned session (#572); the hub never cancels a turn to deliver, and a steer that was sent but not acknowledged, or whose turn ended first, is held as `needs_review`, never replayed. Jcode has no switch to choose its MCP servers, so the owned serve and ACP processes load every server named in `<JCODE_HOME>/mcp.json` and the project's `.jcode/mcp.json`, `.mcp.json` and `.claude/mcp.json`, besides the hub's own; `ahub doctor` lists their names only. Point `jcode_home` at a separate home, or remove a server from those files, to keep it out.
|
|
684
716
|
|
|
685
|
-
Antigravity (`agy`, #485) is optional, cloud and off roster by default. The launcher accepts the Antigravity CLI 1.4.x line; only 1.4.2 and 1.4.
|
|
717
|
+
Antigravity (`agy`, #485) is optional, cloud and off roster by default. The launcher accepts the Antigravity CLI 1.4.x line; only 1.4.2, 1.4.7 and 1.4.12 have a native receipt ([1.4.7](verification/2026-10-26-agy-1.4.7.md), [1.4.12](verification/2026-11-03-agy-1.4.12-launcher-nonce.md)), so another 1.4.x patch launches with a warning that it is unverified, and every other version is refused before a hub connection or plugin write (#589) unless the person trusts that exact version (#691). It runs native tools under its own unmanaged permissions, without a hub sandbox or hub permission-mode mapping; headless, resume, unattended and native permission flags are refused. Its roles are capped at implementer and verifier, so it never plans, reviews or receives PII work. Only the person's `ahub agy` launcher writes its three plugin files in `.agents/plugins/agent-hub/` (`plugin.json`, `mcp_config.json`, `hooks.json`): linked or foreign entries refuse before any write, cleanup removes only that launch's matching files, and it never writes project-level `.agents/hooks.json` or the person's `~/.gemini` settings. Conductor starts are refused, and a declared agy extra instance is refused at config load (#625). Hook authority (`native_hook_launch`) comes only from that launcher, belongs to the console socket that began it and ends when that socket closes; hook input over 1 MiB is refused, and observations are bound to the daemon, launch, native session and project.
|
|
686
718
|
|
|
687
719
|
Configured ACP adapters (#451 A3) are declared only by the person, in a Git-confirmed untracked `.agenthub/config.local.json`. The whole declaration (command, argv, identity, roles and mode maps) is machine-local authority: a shared or tracked declaration is ignored with a notice, settings never edit it, and changes need a hub restart. The hub runs only the declared argv, never a module or callback. Argv that selects a leader, attach, connect or remote mode is refused, and a Grok declaration needs `--no-leader`. Each start requires the pinned native identity, an exact `session/load` echo on resume, and positive ownership of the spawned process and its original process group; unknown ownership refuses, and stop signals only that verified group. An agent that hands its work to a process outside that group is outside this boundary. Without a mode map native policy is unmanaged, and there is no hub sandbox either way. Configured kinds are cloud peers (never PII), headless only and off roster by default, and the hub makes no quota, context or controlled-recovery claims for them. A read-only instance needs the native read-only mode acknowledged before a prompt and is limited to read and review hub tools, both in the published MCP list and at the host; its native non-read tool requests are rejected, and no setting or runtime change can raise it. These agents' stderr and error text are withheld from logs, and previews redact the executable, argv and session id.
|
|
688
720
|
|
|
@@ -759,45 +791,47 @@ historical claude-mem database used only by the explicit read-only import.
|
|
|
759
791
|
Sync starts only after a person configures a remote. Public GitHub remotes are
|
|
760
792
|
refused; unknown visibility proceeds with a warning for the configured remote.
|
|
761
793
|
Short-term segments, machine identity and the derived index are excluded from
|
|
762
|
-
sync.
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
794
|
+
sync. Sync and direct long-term writes apply the configured agent-mem sync policy
|
|
795
|
+
narrowed by the hub's deny paths, PII patterns and project-private paths; a promotion
|
|
796
|
+
that fails it leaves the note short-term (#1173). Status exposes safe sync state and
|
|
797
|
+
counts, including verified withheld/exported counts from a successful SDK placement
|
|
798
|
+
result. Failure logs retain only the closed cause and known declined count.
|
|
799
|
+
`ahub memory declined` is person-only (refused to agent shells and `--live-test`)
|
|
800
|
+
and uses bounded physical-row inspection to show IDs and rule categories, never
|
|
801
|
+
note text or literal patterns; incomplete inspection is not a zero count.
|
|
802
|
+
Default-host v3 sync remains unsupported because the hub supplies no fresh
|
|
803
|
+
placement attestation. No activation proof is fabricated; the supported activation
|
|
804
|
+
and attestation path remains agent-mem#281. Suggested graph edges are off by default
|
|
805
|
+
and are not evidence until a person confirms them. Operational details and limits: [hub memory](memory.md).
|
|
806
|
+
|
|
807
|
+
## Floating pet retirement (#1012)
|
|
808
|
+
|
|
809
|
+
The standalone JXA host, WKWebView, window bridge and its packaged assets are
|
|
810
|
+
removed. `ahub pet --window [--close]` only prints the `acon ui` Mini pointer and
|
|
811
|
+
exits successfully; it has no launcher, process-control or network effect and
|
|
812
|
+
is available to agent shells and `--live-test`. Legacy `pet.window` is ignored
|
|
813
|
+
as compatibility input and grants no configuration or conductor authority.
|
|
814
|
+
|
|
815
|
+
Terminal pet reads retain their existing behavior. The retired `pet --palette`
|
|
816
|
+
command validates its argument and prints the same Mini pointer without writing
|
|
817
|
+
`.agenthub/config.local.json`. Existing `pet.palette` values remain validated
|
|
818
|
+
compatibility input and have no rendering effect.
|
|
819
|
+
|
|
820
|
+
The **Console message** view already exists in the agent-hub acon plugin
|
|
821
|
+
(#935). Its existing `send` action retains message audience, PII and actor
|
|
822
|
+
checks; the retired window adds no alternative composer path.
|
|
789
823
|
|
|
790
824
|
## Planner, cancellation and delivery authority
|
|
791
825
|
|
|
792
826
|
The `planner` setting is person-controlled: one peer id or a list of up to eight (#525-2). A valid pin determines the planner holders without adding them to the roster; otherwise configured rostered planner roles apply, then the first rostered peer in the closed planner catalog. The automatic choice is not written to disk. Each new non-PII task and each planner-created release records one effective planner from the same pure walk `ahub route explain` shows; only that planner or the person changes its planning. A bound planner keeps that authority while busy, paused or offline; one removed from the holders or the roster falls back to the current holders, and older rows with no binding, PII tasks included, keep holder-wide authority. Only the person moves a task's planner (`ahub task assign <id> <peer> --role planner`). Another holder may record a plan review without gaining the task's other planning powers; an agent cannot review its own plan while another rostered holder is not offline, and otherwise the receipt records `selfReview: true` with `planAuthor`. Planner guidance does not grant conductor authority or bypass roster, PII, WIP or owner capability checks.
|
|
793
827
|
|
|
794
|
-
`cancelled` is closed, never approval. The person can cancel non-approved work with a reason and optional supersession reference; a planner can cancel only its own proposed/backlog work with no historical acceptance, and only as that task's effective planner. Current task revision, privacy and recovery/shutdown holds are checked before writing and again after awaited screening. Cancellation preserves history, releases reservations and overlap membership, writes no outcome memory, and does not stop native turns or delete branches/worktrees. Owner/reviewer stop notices remain relevant; earlier task requests become moot. Dependents remain blocked and receive ids-only advice, including backlog dependents.
|
|
828
|
+
`cancelled` is closed, never approval. The person can cancel non-approved work with a reason and optional supersession reference; a planner can cancel only its own proposed/backlog work with no historical acceptance, and only as that task's effective planner. The configured, rostered conductor can cancel only a task another project handed this one (#1175), while it is proposed or backlog, nobody has accepted it, it has no execution evidence and it is not PII. Current task revision, privacy and recovery/shutdown holds are checked before writing and again after awaited screening. Cancellation preserves history, releases reservations and overlap membership, writes no outcome memory, and does not stop native turns or delete branches/worktrees. Owner/reviewer stop notices remain relevant; earlier task requests become moot. Dependents remain blocked and receive ids-only advice, including backlog dependents.
|
|
795
829
|
|
|
796
830
|
Current daemons advertise `task_cancel` in hello and status metadata. A current CLI refuses cancellation against a daemon without that feature. Controlled upgrade/restart refuses an unsupported target while cancelled task ids exist; source prepare and commit recheck compatibility, and the rows are never rewritten as approved. In the same way it refuses a target without `task_planners` once a planner list or an instance planner is configured or any task or release planner binding or plan-review authorship is stored, even after that configuration is removed. A manually started older binary is outside this compatibility guarantee: it can display cancelled rows as open or fail to move them.
|
|
797
831
|
|
|
798
832
|
Stall causes are persisted and deduplicated by cause and replanning revision. Planner advice joins its next delivery as bounded ids-only context; it does not manufacture an ordinary task turn. Pull-only offer attention clears only after acceptance, decline, supersession or durable positive evidence for that exact offer and recipient. Operator, hub or owner completion of an uncertain delivery is not evidence that the offer was read.
|
|
799
833
|
|
|
800
|
-
`hub_delivery_retry` grants an eligible recipient one retry of its own `needs_review` work, never pause removal. `hub_delivery_resolve` (#1112) lets a local agent mark only its own `needs_review` row `completed` or `discard`, at the revision `hub_inbox` showed, with a public, screened reason recorded under its actor. It never replays the originals or changes task state. An unknown id, another peer's row and a row
|
|
834
|
+
`hub_delivery_retry` grants an eligible recipient one retry of its own `needs_review` work, never pause removal. `hub_delivery_resolve` (#1112) lets a local agent mark only its own `needs_review` row `completed` or `discard`, at the revision `hub_inbox` showed, with a public, screened reason recorded under its actor. An attached Claude push-channel session may also settle its own `accepted` row that way (#1172); any other peer's `accepted` row is refused. It never replays the originals or changes task state. An unknown id, another peer's row and a row in neither state share one refusal, so the answer never confirms another peer's row. A channel `accepted` row that gets neither native completion nor that disposition within `bus.accepted_settle_ms` (default ten minutes) becomes `needs_review` with no replay. With `conductor.feed` on, the configured conductor gets one ids-only notice for an important, task or review timeout. The person, the hub, a remote peer, a paused or held peer, and recovery, shutdown or storage holds are refused. A row that carries an invocation stays person-only, because its outcome is uncertain. An explicit `hub_inbox` read may return newly queued messages behind such a hold, never the held originals, and only when no recovery, pause, lifecycle or storage fence applies. Durable per-original evidence survives journal pruning; unknown retry history or a used retry requires the person. Hub presence prefaces are excluded from attempted-work identity, so a shared context preface does not consume an unrelated message's retry. Delivery-only local and ACP peers (including Jcode) cannot invoke recipient retry; tool exposure hints do not replace the daemon's actual-adapter guard. Their held work requires person resolution. Before transport handoff, storage failure preserves the queue and preface; after handoff an uncertain effect is never automatically replayed.
|
|
801
835
|
|
|
802
836
|
A declared instance whose only role is reviewer gets review requests, task messages addressed to it and the person's messages; agent chat, digests and presence are discarded and journaled, and it gets no memory recall (task 223). The headless Claude reviewer runs one new `claude -p` per review with `--setting-sources ""`, `--strict-mcp-config`, `--no-session-persistence`, tools limited to Read, Grep, Glob, `hub_review` and `hub_task_show`, and `dontAsk`. Its prompt holds hub facts and a bounded diff, never the builder's summary, and its `hub_task_show` drops the builder's notes. Changes touching `local.deny` or `pii.paths` are withheld and passed as `Read` deny rules, which Claude enforces, not the hub. Private tasks are never given to it. Its `hub_review` counts only while the child started for that task still runs; a stop or exit revokes it first, and surviving processes hold the delivery as `needs_review` and keep the reviewer busy.
|
|
803
837
|
|
|
@@ -805,7 +839,7 @@ A terminal Claude implementer or verifier instance (#525-5) owns write-class tas
|
|
|
805
839
|
|
|
806
840
|
A planner-only instance (#525-6: `planner` without `implementer` or `verifier`) plans on the project checkout, so a declared `worktree` is refused. A Codex planner's `thread/start` and `thread/resume` are requested read-only, and a thread is adopted only when Codex's reply positively confirms a read-only sandbox; an unconfirmed or malformed reply refuses that RPC and closes the link before the hub adopts the thread or delivers work, and the current roles are rechecked before each injection. This is an initial ceiling, not confinement of the person's attached TUI, which can change its native sandbox later. A declared planner-only Claude channel instance gets no automatic grant from its facts hook for any tool but Read, LS, Glob and Grep, whatever the permission mode.
|
|
807
841
|
|
|
808
|
-
The headless Claude implementer (#525-4) runs one owned `claude -p` child per turn in its declared worktree, with `--setting-sources ""`, `--strict-mcp-config`, native permission mode `default`, the hub's managed PreToolUse hook and `--permission-prompt-tool mcp__agent-hub__hub_permission_request`; never `--bare`, `acceptEdits`, `dontAsk` or `bypassPermissions`, and `never-ask` is refused. Each task keeps its own persisted session; unscoped chat reuses one only while exactly one task is in progress, and a `hub_inbox` read refuses a batch attributed to another task. The approval path fails closed: `hub_permission_request` is accepted only from the current child's launch and generation, goes to the console's existing approval queue and `approvals.timeout_s`, and offers allow-once and deny only, with no allow-always or session grant. A timeout, a prompt failure, a disconnect, a hub that is down or refusing, or a stale child binding answers deny; a failed hook decides nothing, so the call still waits for a person. The approval title is the tool name and a JSON rendering of the arguments passed through `sanitize()` and the public-text screen (any PII, secret or private-path match withholds the whole value as `[withheld]`, before any cut), never task text, cut at 600 characters with a `[cut, N chars]` marker; the original arguments stay with the child and are not logged. The MCP wire cannot tell a native permission dispatch from a call the model makes itself; a person allow still refers only to its original arguments. Automatic Bash authorization uses the separate single-use host wrapper contract below, never a caller-supplied tool id as native provenance. A stop holds an uncertain delivery until the owned process group is confirmed gone. The [native permission receipt](verification/2026-10-23-headless-claude-permissions.md) is model-free on Claude Code 2.1.309, [repeated on 2.1.310](verification/2026-10-26-claude-implementer-2.1.310.md), [on 2.1.314](verification/2026-10-31-claude-implementer-2.1.314.md)
|
|
842
|
+
The headless Claude implementer (#525-4) runs one owned `claude -p` child per turn in its declared worktree, with `--setting-sources ""`, `--strict-mcp-config`, native permission mode `default`, the hub's managed PreToolUse hook and `--permission-prompt-tool mcp__agent-hub__hub_permission_request`; never `--bare`, `acceptEdits`, `dontAsk` or `bypassPermissions`, and `never-ask` is refused. Each task keeps its own persisted session; unscoped chat reuses one only while exactly one task is in progress, and a `hub_inbox` read refuses a batch attributed to another task. The approval path fails closed: `hub_permission_request` is accepted only from the current child's launch and generation, goes to the console's existing approval queue and `approvals.timeout_s`, and offers allow-once and deny only, with no allow-always or session grant. A timeout, a prompt failure, a disconnect, a hub that is down or refusing, or a stale child binding answers deny; a failed hook decides nothing, so the call still waits for a person. The approval title is the tool name and a JSON rendering of the arguments passed through `sanitize()` and the public-text screen (any PII, secret or private-path match withholds the whole value as `[withheld]`, before any cut), never task text, cut at 600 characters with a `[cut, N chars]` marker; the original arguments stay with the child and are not logged. The MCP wire cannot tell a native permission dispatch from a call the model makes itself; a person allow still refers only to its original arguments. Automatic Bash authorization uses the separate single-use host wrapper contract below, never a caller-supplied tool id as native provenance. A stop holds an uncertain delivery until the owned process group is confirmed gone. The [native permission receipt](verification/2026-10-23-headless-claude-permissions.md) is model-free on Claude Code 2.1.309, [repeated on 2.1.310](verification/2026-10-26-claude-implementer-2.1.310.md), [on 2.1.314](verification/2026-10-31-claude-implementer-2.1.314.md), [on 2.1.316](verification/2026-10-31-claude-implementer-2.1.316.md) and [on 2.1.318](verification/2026-11-05-claude-implementer-2.1.318.md); it is not sandbox proof, and a real account, model and person interaction remains a live leg.
|
|
809
843
|
|
|
810
844
|
The #845 host shell wrapper is the only automatic Bash path for a headless implementer. In `ask-when-needed`, a complete public input must pass the existing #816 read-only proof or the closed task-command proof, with an active owned task and registered worktree. Enabled action-policy classes still ask, and `ask`, PII handling and the `never-ask` refusal are unchanged. The native request and authenticated host response both name wrapper protocol 1, so an older plugin cannot drop the rewrite and execute an original command automatically.
|
|
811
845
|
|
|
@@ -835,6 +869,8 @@ What the sandbox can and cannot write is stated here so nobody relies on more. A
|
|
|
835
869
|
|
|
836
870
|
On decline or cancel the hub removes the worktree and deletes the branch while it is still the empty base. A worktree with uncommitted changes, or that cannot be checked, is never force-removed: it is kept with its branch and reported as a console notice. A branch the hub committed to is kept, because its commit may be pushed and under review. A peer that makes its own worktree (Claude, Codex, Kimi) is untouched, and a PII task never gets a hub worktree. Residual: a same-user process outside the sandbox can still plant a ref under `.git/refs/heads`; the sandbox no longer lets a sandboxed command do it (#1123), and the hub refuses to commit or push on top of a moved ref either way.
|
|
837
871
|
|
|
872
|
+
Worktree shelf (#1057). Apart from the empty hub-made task worktree above, the daemon never removes a worktree or deletes a branch; the shelf only reads and records the person's Park or Pin. `ahub worktrees` and the `ahub worktrees tidy` preview are reads an agent may run outside a private turn; Park, Pin, "Refresh PR states" and `tidy --confirm` are the person's (the console, or the dashboard session the manager forwards for it; a live-test dashboard is refused, and `tidy --confirm` is person-only for an agent identity and refused under `--live-test`). The only removal path is `ahub worktrees tidy --confirm <fingerprint>` in the person's own CLI process (`src/cli/worktree-tidy.ts`, which the daemon does not import). It re-reads every row and refuses the whole run when the fingerprint changed, operates only on a path git itself lists, and runs exactly `git worktree remove <path>` and `git branch -d <branch>`, never `--force` or `-D`. A row is refused when it is the main checkout, an instance worktree, outside the project, dirty, locked, without a branch, unpushed, without an upstream, not merged into the default branch, bound to a task that is not approved or cancelled, or on a branch that changed since the preview, and whenever git cannot tell; a parked row passes the same checks. No read makes a GitHub call; only the explicit refresh does, for at most 8 PRs. A worktree bound to a PII task keeps that binding on the host, so it stays live and tidy refuses it, but its row carries no task id, PR or task state.
|
|
873
|
+
|
|
838
874
|
Refs, HEAD and worktree admin writes (#1123). A hub-sandboxed peer (Pi, the local worker) cannot move what a branch, `HEAD` or a linked worktree points at. The profile (`gitRefRules` in `src/local/deny.ts`, built into `hubWriteRegexes`) refuses every write to `refs/**` (branches, tags, `refs/stash`, remote refs), `packed-refs` (and its lock and new files), `HEAD` (and `HEAD.lock`) and `worktrees/**` (a linked worktree's admin dir: its `HEAD`, `index`, `logs`, anything new, and a new admin dir). The rules sit directly under the git dir, so a project below a directory that happens to be named `refs`, `worktrees`, `HEAD` or `packed-refs` keeps working. They cover a plain `.git`, and each external git dir (the common dir and a linked worktree's own, whether the worktree is inside the project or beside it, and a bare repository's common dir that is not named `.git`). Every name is folded for the code points HFS+ ignores like the other git names, and each regex literal stays under seatbelt's 1024-byte limit at any project depth (`test/sandbox-git-refs.test.ts`, run with the real sandbox-exec in the main checkout, in a hub-made worktree, in a worktree outside its project and in a bare repository's worktree). The hub's own git is hardened and runs outside the sandbox, so #1115's hub commit and push are unaffected.
|
|
839
875
|
|
|
840
876
|
What a sandboxed command can still write under `.git`, each with its reason, probed with the real profile: the main checkout's `index` and `index.lock` (`git add` and `git restore` stage there), `logs/HEAD`, `ORIG_HEAD`, `FETCH_HEAD`, `MERGE_HEAD`, `COMMIT_EDITMSG`, `info/exclude` and `description` (none can run a program or move a branch), and NEW loose objects under `objects/` (a staged blob, #864; existing objects, `objects/info` and `objects/pack` stay refused). Still refused as before: a gitfile, `config`, `config.worktree`, `hooks`, `commondir` (#270). Inside a linked worktree the sandbox cannot write the worktree's own admin dir, so `git add` there fails on its `index.lock`; the working tree itself is writable and `git status`, `diff`, `log`, `show`, `blame`, `ls-files`, `rev-parse`, `for-each-ref` and `cat-file` all still work.
|
|
@@ -879,6 +915,14 @@ role admission.
|
|
|
879
915
|
separate `runner-attested` level described below; their fields never overwrite
|
|
880
916
|
declared or host-measured evidence. Public experiment recording tools and the
|
|
881
917
|
receipt CLI are the separate #602 slice.
|
|
918
|
+
- **Claim evidence links (#931 T1).** `hub_evidence_record` is published only to
|
|
919
|
+
research-profile peers. It admits only the current in-progress owner of a stored
|
|
920
|
+
research task, outside a private turn, on a non-PII task and a registered public
|
|
921
|
+
source whose raw hash matches. The checks repeat after screening and inside the
|
|
922
|
+
append transaction. Its relation, projection hash and locator are declared
|
|
923
|
+
provenance, never an entailment judgment or host verification; person verdicts
|
|
924
|
+
stay the only judgment record. Receipts carry the link id, revision and task id
|
|
925
|
+
only, and arguments and results are excluded from capture.
|
|
882
926
|
- **Privacy.** A campaign whose parent task is PII, or that names a private source,
|
|
883
927
|
is private, and so is an attempt with a private task, input or output path.
|
|
884
928
|
Public views keep ids, counts, states and provenance and stub every question,
|
|
@@ -938,7 +982,7 @@ A handoff crosses a project boundary, but not a security boundary: both projects
|
|
|
938
982
|
belong to the same machine user. The origin daemon reads the target's control
|
|
939
983
|
token file and connects to the target's daemon as the `console` role through
|
|
940
984
|
`ControlClient`, like every control message; the hello checks the target's project
|
|
941
|
-
root and instance. It sends that connection only the
|
|
985
|
+
root and instance. It sends that connection only the four handoff messages, and
|
|
942
986
|
it has no reach a same-user process lacks (see "What is not defended").
|
|
943
987
|
|
|
944
988
|
- **Who may hand off.** `ahub task handoff` is Console in `classifyPeerCommand`;
|
|
@@ -948,21 +992,28 @@ it has no reach a same-user process lacks (see "What is not defended").
|
|
|
948
992
|
the target's own `status.json` must advertise `task_handoff` and name no other project. A target
|
|
949
993
|
that is not running is refused with the action; nothing is queued.
|
|
950
994
|
- **No authority in the target.** The target answers `task_handoff`,
|
|
951
|
-
`task_handoff_event` and `task_handoff_states` to a console
|
|
995
|
+
`task_handoff_event`, `task_handoff_by_key` and `task_handoff_states` to a console
|
|
996
|
+
connection only, and
|
|
952
997
|
refuses a new handoff while it recovers or shuts down. The task is an ownerless proposal
|
|
953
998
|
its own hub makes and routes. The origin project and peer are a record in
|
|
954
999
|
`task_handoff_origin` and a line at the end of the detail, never an actor, a
|
|
955
1000
|
roster peer or a role: the origin cannot accept, review, approve, assign or
|
|
956
1001
|
cancel there.
|
|
957
1002
|
- **What crosses.** To the target: a title (300 characters), a class, a detail
|
|
958
|
-
(4,000), up to 16 repository-relative paths, a spec issue number,
|
|
959
|
-
|
|
1003
|
+
(4,000), up to 16 repository-relative paths, a spec issue number, an optional
|
|
1004
|
+
`duplicate_ok` reason (300, kept in the target's detail), a handoff key (a UUID,
|
|
1005
|
+
sent only to a target that advertises `task_handoff_key`), and the origin project
|
|
1006
|
+
id, peer id and task id, at most 8 KiB in all. The request is parsed
|
|
960
1007
|
closed on both sides: an unknown field, an accessor or a control character is
|
|
961
1008
|
refused, never dropped, and the target sanitizes before it stores. No memory,
|
|
962
1009
|
evidence, turn content or task history crosses. Back to the origin: an event
|
|
963
|
-
name, a PR number and a full 40-hex head, never the target's
|
|
964
|
-
other text;
|
|
965
|
-
|
|
1010
|
+
name, a sequence number, a PR number and a full 40-hex head, never the target's
|
|
1011
|
+
title, notes or other text; a duplicate-issue refusal names only the target's own
|
|
1012
|
+
open non-PII task ids. The catch-up (at start, every five minutes and when a
|
|
1013
|
+
target's descriptor changes) and the key lookup answer only for tasks whose
|
|
1014
|
+
recorded origin is the asking project, with the same fields. A key the target
|
|
1015
|
+
already holds answers with the task it made and never files a second one; the
|
|
1016
|
+
same key with a different request is refused. The origin peer gets that as a
|
|
966
1017
|
hub-written `kind: task` message.
|
|
967
1018
|
- **Privacy.** The origin refuses during a private turn and for a `[pii]` task
|
|
968
1019
|
named as the source, before anything leaves. The text itself is checked by the
|
|
@@ -1076,9 +1127,20 @@ targets before any local effect; implicit broadcast never crosses the boundary.
|
|
|
1076
1127
|
|
|
1077
1128
|
Signed completion proves remote durable queue admission only and is displayed as
|
|
1078
1129
|
completed (admitted). Crypto inbox, application claim and Bus publication use
|
|
1079
|
-
separate durable stages; a cross-DB failure is uncertainty, never an
|
|
1080
|
-
or native-execution claim.
|
|
1081
|
-
|
|
1130
|
+
separate durable stages; a receiver cross-DB failure is uncertainty, never an
|
|
1131
|
+
exactly-once or native-execution claim. A late authenticated ordinary-chat receipt
|
|
1132
|
+
may settle its matching unresolved sender row under current room/member identity,
|
|
1133
|
+
without overriding a person disposition. Invocation uncertainty remains person-held
|
|
1134
|
+
and is never inferred from transport proof. Person accept/discard is local disposed
|
|
1135
|
+
metadata and never a signed application receipt.
|
|
1136
|
+
|
|
1137
|
+
Sender retries retain the application id, canonical content and original queue TTL.
|
|
1138
|
+
A distinct root delivery requires the same retained content hash and recipient,
|
|
1139
|
+
with no disposition or permanent hold; it inherits the original creation/expiry.
|
|
1140
|
+
Legacy unknown content hashes refuse cross-root reuse. Receipt ciphertext commits
|
|
1141
|
+
to the local durable outbox before chat transport ACK and replays until broker
|
|
1142
|
+
storage confirmation. Deferred admission may ACK its durable crypto inbox entry
|
|
1143
|
+
and commit its receipt when admission resumes. Neither path claims native execution.
|
|
1082
1144
|
|
|
1083
1145
|
Schema3 reserves a separate durable lane for at most eight canonical receipt
|
|
1084
1146
|
payloads per room, at most 1,024 bytes each and 512 KiB extra globally. A receipt
|
|
@@ -1098,12 +1160,15 @@ Capacity recovery requests fresh presence once per transition; runtime and
|
|
|
1098
1160
|
other storage errors stay unACKed.
|
|
1099
1161
|
Fresh, fully authenticated chat at the ordinary cap is likewise durably refused
|
|
1100
1162
|
and transport-ACKed without admission, floor advance or signed receipt. Its
|
|
1101
|
-
sender
|
|
1102
|
-
|
|
1163
|
+
sender retries at the ACK deadline under the same immutable application id, without
|
|
1164
|
+
renewing its queue TTL. Queue expiry or a permanent refusal ends automatic sending.
|
|
1165
|
+
The refused chat cannot hold matching receipt traffic behind it in the broker FIFO.
|
|
1103
1166
|
|
|
1104
1167
|
Permanent signed sequence floors survive all application retention/eviction.
|
|
1105
1168
|
Application-id and trace guards retain 30 days/10,000 rows per room, settled first;
|
|
1106
|
-
outside that retention they make no historical claim.
|
|
1169
|
+
outside that retention they make no historical claim. Automatic resend can reach
|
|
1170
|
+
this settled-eviction ceiling and repeat admission under a fresh ciphertext sequence:
|
|
1171
|
+
exactly once within dedupe ceiling. MAX_HOP 3 remains absolute.
|
|
1107
1172
|
All-unsettled capacity refuses with ids/counts, and person disposition releases
|
|
1108
1173
|
settled capacity. This state protects against other users, not hostile code
|
|
1109
1174
|
running as the same user with access to the private room keys. Native/account,
|
|
@@ -1176,3 +1241,58 @@ path and no polling when no real cleanup occurred.
|
|
|
1176
1241
|
Done/discard acknowledgement precedes the background room retry. A network retry
|
|
1177
1242
|
failure or socket loss during that later work cannot report a committed local
|
|
1178
1243
|
disposition as failed.
|
|
1244
|
+
|
|
1245
|
+
## Hub-owned session titles
|
|
1246
|
+
|
|
1247
|
+
`hub_set_title` (#1055) is offered only to fixed Pi/local definitions with a
|
|
1248
|
+
hub-owned title slot. Publication grants no authority: the daemon checks the
|
|
1249
|
+
current adapter/session, private turn, open PII ownership or review and public
|
|
1250
|
+
metadata policy. An agent may write only while the native/current slot remains
|
|
1251
|
+
unnamed. A person's console rename may replace it, but cannot bypass privacy.
|
|
1252
|
+
The title and any unnamed display label are screened again when shown; unknown
|
|
1253
|
+
screening is `[private]`. Title tool inputs/results are excluded from capture
|
|
1254
|
+
and progress observations. No title crosses rooms, changes task/Git/instance
|
|
1255
|
+
records, or causes an extra model call.
|
|
1256
|
+
|
|
1257
|
+
A fallback label is never persisted. Its original message has the existing
|
|
1258
|
+
journal retention; the separate `session-title-inputs.json` stores only peer,
|
|
1259
|
+
title-session, delivery and envelope IDs written by positive delivery
|
|
1260
|
+
acknowledgement, never by a title tool. Restart resolves a still-retained
|
|
1261
|
+
original only for the same title-session ID. A missing or different session,
|
|
1262
|
+
invalid binding or pruned original yields no inherited label. Live status may
|
|
1263
|
+
show it only to the console or configured conductor; other readers see
|
|
1264
|
+
`unnamed`. Metadata screening does not grant audience membership. The label's
|
|
1265
|
+
source note identifies incoming-message text and never attributes it as the
|
|
1266
|
+
recipient peer's own speech. `status.json`, events, logs and memory never
|
|
1267
|
+
receive a derived copy. Removed-instance and Local bindings are not retained.
|
|
1268
|
+
|
|
1269
|
+
## Questions a peer asks the person (#1053)
|
|
1270
|
+
|
|
1271
|
+
`hub_ask` lets any peer put a structured question (one to four questions, two to eight options each) to the person. A question is not a permission: it is its own record in `hub.db` (`questions`), has its own control messages, and never expires as a denial.
|
|
1272
|
+
|
|
1273
|
+
- **Who answers.** Only the console role. The control message `answer { questionSet, answers, surface? }` is refused, with a reply and no change to the row, for every `tools` or `peer` connection, the asking peer's own included. Nothing a peer writes counts as an answer: a `hub_send` body, a tool result, a file or an envelope that quotes one (the exact JSON `hub_ask` returns included) is text, and `test/questions-daemon.test.ts` pins that none of them changes a row. The answer envelope the peer receives is built by the hub (`kind: "answer"`, `from: "user"`), so its header cannot be forged: every body goes through `sanitize()`, and `kind`, never the sender, is what code keys on.
|
|
1274
|
+
- **One terminal transition.** `open` becomes `answered`, `superseded`, `cancelled` or `expired` by one compare-and-set (`UPDATE ... WHERE state = 'open'`). Of any number of concurrent answers, stops and expiries exactly one wins, and every other caller is told the row's real state. A second answer is refused and publishes nothing.
|
|
1275
|
+
- **Validation.** An answer is checked against the stored row before anything is written: every question answered once, only offered option ids, one option unless `multi`, free text only where `allowOther` and at most 500 characters. The refusal names the question by position and never echoes the offered or submitted text. Input is closed and bounded the same way (`parseAsk`).
|
|
1276
|
+
- **Answering from acon (#1053 T5).** The plugin accepts an `answer` action only when the console advertises `action.meta` and passes its own `meta` (`via: "console"`, a verified `identity` and `node`), which the page cannot set. Without it, or with page-supplied `identity`, `surface`, `questionSet`, `projectId` or `instanceId` fields, the action is refused before any request reaches the hub (`test/questions-plugin.test.ts`). The project and set come from the inbox item id the plugin built, never from the page. The plugin reaches the hub through a dedicated manager port (`projectConsole(...).answer`), not through the dashboard action whitelist, so the dashboard page of `ahub ui --all` and the manager's `ui_action` whitelist have no `answer` action, and a page can never answer for the person. The hub re-validates every answer against the stored row. The verified `identity` the plugin read from the console's own `meta` rides with the answer (`answer { ..., surface: "acon", person }`); the hub records it, one bounded line, on the answered row and on the `question` event, and reads `person` only for the `acon` surface, so a terminal or console answer cannot claim another person. It is never question or answer text. The acon types of the form and of the submitted answer (`ActionMeta`, `InboxVerb`, `VerbForm`, `InboxAnswers`) are type-only imports from the exact-pinned `@staix/agent-console/contract` entry, never hand-copied. `ahub answer` with any operand is refused in `liveTestRefusal` (a delegated `--live-test` run is promoted to console identity, so without it an agent shell could answer its own `hub_ask` as the person); only the bare listing is allowed there.
|
|
1277
|
+
- **Residual.** The same as `permit`: a vendor agent that can read the control token can act as the console (see "What is not defended"). This feature does not narrow that and does not widen it.
|
|
1278
|
+
- **Privacy.** Question and answer text live in `hub.db` and in the envelope addressed to the asking peer, nowhere else: never `hub.log` (an `answer` or question-notice envelope logs a fixed stub), never `events.jsonl` (the `question` event carries the set id, peer, event name, counts, latency, surface and a closed reason), never the console tail or the dashboard feed (`redactBusEvent` stubs the body), never memory capture (`QUESTION_TOOLS`) or the progress observer. A question asked in a PII turn, or about a PII task, is stored `private` and its late envelope is private too. The inbox title of a private question is a stub (`<peer> asks a question`); its text is only in the answer form, the person's own answer surface, and the status attention model, the stream line and the CLI audit carry ids only. `test/questions-console-daemon.test.ts` pins these with a PII task, the status reply, the dashboard snapshot, `hub.log`, `events.jsonl` and memory capture.
|
|
1279
|
+
- **Not defended.** A peer that is cancelled, superseded or restarted loses a pending question's wait, never its row, until the person or `questions.ttl_s` settles it. An answer a person gives is shown to the asking peer in full; do not ask the person for a secret.
|
|
1280
|
+
|
|
1281
|
+
## Results a person's runs leave behind (#1054)
|
|
1282
|
+
|
|
1283
|
+
A result is the record that a run the person started has settled. Reading one and marking it read are separate acts, and only the person makes the second.
|
|
1284
|
+
|
|
1285
|
+
- **A receipt is the person's, on every surface.** `result_seen`, `results` (`read-all`, `unread-last`) and the dashboard's `result_seen`, `results_read_all` and `results_unread_last` write or delete a receipt, and each is refused unless the caller is the console role or a dashboard session the console opened: a peer's connection, a `hub_send` body, a tool result or an agent shell (`ahub results` is the person's command, and its receipt forms `--read`, `--read-all` and `--unread-last` are in `liveTestRefusal`, so a delegated `--live-test` run of them is refused too; the bare listing stays allowed there) is text and never reaches the store. A snapshot, a listing and the Results section's refresh never write one; a poll is not a person looking (`test/results-dashboard-1054.test.ts`).
|
|
1286
|
+
- **From acon.** The plugin accepts `result_seen`, `results_read_all` and `results_unread_last` only when the console advertises `action.meta` and passes its own `meta` (`via: "console"`, a verified `identity` and `node`), which the page cannot set. Without it, or with page-supplied `identity`, `surface`, `person` or a completion key, the action is refused before any request reaches the hub (`test/results-plugin-1054.test.ts`). The project and the result come from the inbox item id the plugin built or, from the panel, from the project it is showing; acon's `seenAt` and `explicit` are accepted and dropped, because the hub stamps its own time. The write goes through a dedicated port (`projectConsole(...).receipt`) as a console-role control message on the `acon` surface, never through the dashboard's forwarding list, and a plugin that is not given the person's meta cannot widen that.
|
|
1287
|
+
- **A live-test dashboard** gets no results and is refused every receipt, in two places that each hold alone: the page server's live-test allowlist and the daemon's own guard (`test/results-dashboard-1054.test.ts`).
|
|
1288
|
+
- **Residual.** The same as `permit`: a vendor agent that can read the control token can act as the console (see "What is not defended"), and so can write a receipt. This feature does not narrow that and does not widen it. A receipt is not an authority: it clears an unread need and nothing else.
|
|
1289
|
+
- **Privacy.** No prompt or reply text is stored: a completion row keeps envelope ids only. The one-line detail shown beside a result (the head of the starting prompt) is built when the person looks, from the envelope the bus still holds, after `sanitize` (so it cannot forge a hub header) and the hub's secret, PII and private-path screens; it is thrown away after it is shown and is in no `hub.db` row, `hub.log` line, `events.jsonl` event, status field or memory capture (canary tests: `test/results-dashboard-1054.test.ts`, `test/completions-daemon.test.ts`). A private run shows `a private request` without its body being read, a head the screens refuse shows a withheld stub, and a request the bus no longer holds shows nothing. The title is built from the peer and the outcome, never from request text, and the `completion_seen` event carries the id and the surface.
|
|
1290
|
+
- **Not defended.** The detail is the person's own request shown back to them on their own surfaces, so a request that holds something the screens do not recognise is shown to the person who typed it. Marking a result read does not mean the person read it: the hub takes the console's word, and acon's seeing rule (agent-console#27) is what makes that word mean the result was in view.
|
|
1291
|
+
|
|
1292
|
+
## Anchored review comments (#1056)
|
|
1293
|
+
|
|
1294
|
+
Person comment writes (`review_comment_put`, `review_comment_delete`, `review_comment_send`) are console-role control messages, and their actor is always the person: the conductor, a planner and every peer are refused. From acon they require the console's verified `ActionMeta` (`via: "console"`, `identity`, `node`). Page-supplied `author`, `meta`, `personMeta`, `at`, `state` or `sentAt` fields are refused before any request, and delete and send also need acon's confirmation fingerprint. The standalone `ahub ui` page is read-only for comments.
|
|
1295
|
+
|
|
1296
|
+
- **Material.** Placement material comes only from guarded immutable Git snapshots at the task's recorded head (declared refs, plan paths, or same-head verified PR paths), bounded per operation to eight files and eight seconds; a sent package is at most 12,000 bytes. `review_blob` refuses a PII task, and periodic snapshots make no comment-material Git or GitHub read. PII tasks and private paths keep comments on the board and count-only in every public view.
|
|
1297
|
+
- **Delivery.** A sent package carries the host-only `refs.reviewPackage` marker: the intended owner gets the body, while logs, stream projections, incoming labels, memory capture and observers get an ids-only stub; review markers a peer supplies are discarded. Open sent comments, anchored findings and unsettled review deliveries keep the original recipient's responses out of shared memory and observers until resolution, dismissal, task close or explicit disposition (#1192, #1205). The marker grants no task, console or PII authority.
|
|
1298
|
+
- **Retry and recovery.** A send is never retried automatically, and a queued receipt proves publication, not that the owner read it. A restart, upgrade or recovery onto a daemon lacking `review_comments` is refused while anchored comments are stored.
|
package/docs/smoke.md
CHANGED
|
@@ -38,7 +38,7 @@ Autonomy T4 (#999 AC8): [2026-11-02 live inventory](verification/2026-11-02-auto
|
|
|
38
38
|
| Character glyphs in real terminals and tmux (#280) | Console attention; Dashboard character | agent-evidence / person-verdict | glyph rendering in physical terminals |
|
|
39
39
|
| Console Settings panel in a native terminal (#439) | Settings reload and console fixture | agent-evidence / person-verdict | the panel as a person sees it |
|
|
40
40
|
| Reviewer pool native legs (#519) | Reviewer pool native legs | agent-executable | after #783 ships |
|
|
41
|
-
| Antigravity TUI close (#485) | Antigravity P1 | agent-executable | |
|
|
41
|
+
| Antigravity TUI close (#485) | Antigravity P1 | agent-executable | launcher close leg passed on 2026-11-03 ([receipt](verification/2026-11-03-agy-tui-close.md)); still open: `hub_send` and a board read or proposal through the TUI, a real account |
|
|
42
42
|
| Managed Pi live receipts (#418 AC8) | none yet (issue #418) | person-only, then agent-executable | the person opts in to a check runner and answers the approvals |
|
|
43
43
|
| Kimi credential probe (#285 AC9) | Kimi usage counting | person-only | the owner's approval of the probe |
|
|
44
44
|
| Six role instances (#525-6, hub card #138 I4) | Six role instances (#525-6) | person-only | native terminal/account starts, the person's sandbox and approval choices, and the live six-instance verdict |
|
|
@@ -2025,7 +2025,8 @@ Kimi usage ceiling: 2.2.0 print stream-json exposes no token counts. Keep unobse
|
|
|
2025
2025
|
|
|
2026
2026
|
## Antigravity P1 (#485)
|
|
2027
2027
|
|
|
2028
|
-
- Run agy on the 1.4.x line (1.4.2 and 1.4.
|
|
2028
|
+
- Run agy on the 1.4.x line (1.4.2, 1.4.7 and 1.4.12 verified; other patches warn, #589); verify its executable/version and keep the actual TUI/IDE leg separate from the account-free T0 transport receipts ([1.4.2](verification/2026-10-19-agy-p1-t0.md), [1.4.7](verification/2026-10-26-agy-1.4.7.md), [1.4.12](verification/2026-11-03-agy-1.4.12.md) and its [launcher nonce leg](verification/2026-11-03-agy-1.4.12-launcher-nonce.md)).
|
|
2029
|
+
- The hub launcher's own close leg ran in a scratch project with the real `ahub agy` and a fixture provider ([2026-11-03 receipt](verification/2026-11-03-agy-tui-close.md)): the launch slot is held while agy starts and runs, a normal `/exit` releases it so the next launch is admitted, only the managed three-file plugin is removed, and the real Gemini config hashes are unchanged. Set `AGY_CLI_DISABLE_AUTO_UPDATE=1` for any further live agy run: without it agy replaced `~/.local/bin/agy` during that run.
|
|
2029
2030
|
- With a reviewed compatible daemon running and agy explicitly on roster, the person runs `ahub agy` in a terminal. Confirm peer `agy` is pull-only, native permissions unmanaged and no hub sandbox/budget source is claimed.
|
|
2030
2031
|
- In the native TUI call `hub_inbox`, `hub_send`, and a permitted board read/proposal within a non-PII task. Confirm the tool audience and actor remain `agy`, the native model consumes the tool results, and no unsolicited push or PII task text reaches it.
|
|
2031
2032
|
- End the TUI normally. Confirm its group/socket detaches and only its matching managed three-file plugin is cleaned up; unrelated project/global settings remain unchanged.
|
|
@@ -2163,3 +2164,13 @@ account fixture needs the person's account setup. Ask the planner before any
|
|
|
2163
2164
|
quota-heavy run. Never publish prompts, tokens, credentials, endpoints, native
|
|
2164
2165
|
transcripts or session ids as evidence. This slice implements no task `exec`,
|
|
2165
2166
|
routing policy or matrix runner.
|
|
2167
|
+
|
|
2168
|
+
## Pi session title restart receipt (#1055, #1143)
|
|
2169
|
+
|
|
2170
|
+
- [ ] Unverified: in a real managed Pi session, call `hub_set_title`, verify its
|
|
2171
|
+
native name and DOING, restart into that same saved session and verify the
|
|
2172
|
+
name survives. A second agent call must return `already_named`; a person
|
|
2173
|
+
rename must replace the name and remain final against the agent.
|
|
2174
|
+
- The fake-free Pi 1.5.0 setter/readback/reopen API probe is recorded separately
|
|
2175
|
+
in `docs/verification/2026-11-03-pi-title-api.md`; it does not complete this
|
|
2176
|
+
model-backed managed-session restart receipt. Track the live leg in #1143.
|