@staix/agent-hub 0.12.29 → 0.12.31
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 +39 -0
- package/README.md +92 -218
- package/docs/agent-notes/README.md +12 -0
- package/docs/agent-notes/adapters.md +3 -2
- package/docs/agent-notes/budget.md +1 -1
- package/docs/agent-notes/bus.md +7 -1
- package/docs/agent-notes/daemon.md +14 -1
- package/docs/agent-notes/local-worker.md +4 -2
- package/docs/agent-notes/tasks.md +14 -0
- package/docs/agent-notes/tests.md +6 -3
- package/docs/memory.md +70 -152
- package/docs/operations.md +317 -25
- package/docs/plans/2026-10-16-amem-failure-registry.md +101 -0
- package/docs/plans/2026-10-16-amem-p0-baseline.md +110 -0
- package/docs/plans/2026-10-16-amem-p2-read-tools.md +28 -0
- package/docs/plans/amem-cases.json +482 -0
- package/docs/quickstart.md +24 -2
- package/docs/security.md +42 -2
- package/docs/smoke.md +11 -0
- package/docs/specs/2026-09-19-agent-hub-design.md +142 -7
- package/docs/specs/2026-09-20-upgrade-recovery-design.md +84 -10
- package/docs/verification/2026-10-17-0.12.30.md +70 -0
- package/docs/verification/2026-10-17-0.12.31.md +71 -0
- package/docs/verification/2026-10-17-418-probes.md +21 -0
- package/docs/verified.json +13 -13
- package/package.json +5 -2
- package/plugins/agent-hub/.claude-plugin/plugin.json +1 -1
- package/plugins/agent-hub/server.js +129 -62
- package/src/adapters/claude-channel.ts +29 -11
- package/src/adapters/local-worker.ts +2 -2
- package/src/adapters/pi.ts +5 -0
- package/src/cli/attention.ts +5 -1
- package/src/cli/console-state.ts +145 -29
- package/src/cli/console.ts +40 -10
- package/src/cli/help.ts +3 -2
- package/src/cli/identity.ts +4 -3
- package/src/cli/jcode-mcp.ts +1 -1
- package/src/cli/main.js +2 -2
- package/src/cli/main.ts +43 -14
- package/src/cli/memory.ts +87 -187
- package/src/cli/output.ts +19 -2
- package/src/cli/recovery-package.ts +59 -10
- package/src/cli/upgrade-interactive.ts +2 -1
- package/src/cli/upgrade-restore-state.ts +133 -0
- package/src/cli/upgrade-runtime.ts +204 -63
- package/src/cli/upgrade.ts +57 -24
- package/src/hub/board.ts +83 -24
- package/src/hub/bus.ts +108 -38
- package/src/hub/check-runner.ts +514 -0
- package/src/hub/child-process.ts +1 -0
- package/src/hub/control-client.ts +5 -1
- package/src/hub/daemon.ts +529 -89
- package/src/hub/delivery-hints.ts +30 -0
- package/src/hub/delivery-journal.ts +185 -12
- package/src/hub/delivery-superseded.ts +52 -0
- package/src/hub/envelope.ts +31 -1
- package/src/hub/git.ts +2 -38
- package/src/hub/host-executable.ts +11 -0
- package/src/hub/hub-tools.ts +14 -2
- package/src/hub/manager.ts +1 -1
- package/src/hub/memory-candidates.ts +103 -0
- package/src/hub/memory-fail.ts +25 -0
- package/src/hub/memory-observations.ts +47 -0
- package/src/hub/memory-plan-guard.ts +33 -0
- package/src/hub/memory-policy.ts +42 -0
- package/src/hub/memory-profile-check.ts +170 -0
- package/src/hub/memory-profile.ts +192 -0
- package/src/hub/memory-staleness.ts +55 -0
- package/src/hub/path-policy.ts +8 -0
- package/src/hub/peer-catalog.ts +3 -0
- package/src/hub/project.ts +4 -37
- package/src/hub/read-tools.ts +127 -0
- package/src/hub/routing.ts +35 -16
- package/src/hub/settings.ts +2 -1
- package/src/hub/spec-read.ts +120 -0
- package/src/hub/supervision.ts +21 -6
- package/src/hub/task-sweep.ts +7 -6
- package/src/hub/tasks.ts +370 -71
- package/src/local/deny.ts +4 -17
- package/src/memory/brief.ts +8 -5
- package/src/memory/capture.ts +3 -1
- package/src/memory/recall.ts +50 -110
- package/src/pi/process-signature.ts +2 -42
- package/src/ui/index.html +12 -9
- package/src/ui/needs.ts +7 -5
- package/src/ui/office-model.ts +4 -1
- package/src/ui/task-board.ts +1 -1
- package/src/ui/task-progress.ts +6 -2
- package/templates/AGENTS.block.md +1 -1
- package/templates/profiles/general.json +1 -1
- package/templates/profiles/research.json +1 -1
- package/templates/profiles/writing.json +1 -1
- package/src/memory/import.ts +0 -90
- package/src/memory/store.ts +0 -805
- package/src/memory/sync.ts +0 -145
package/docs/security.md
CHANGED
|
@@ -7,7 +7,7 @@ agent-hub connects agents that can each run commands. This page says what the hu
|
|
|
7
7
|
- **Other agents' text is untrusted.** Every message that crosses from one peer to another is framed as untrusted input: a channel tag with `meta.source` for Claude started with `ahub claude`; otherwise a fixed header line (`frame()`, `[agent-hub message from ...]`) with an instruction that says it is untrusted, the plugin's server instructions for a tools-only Claude reading `hub_inbox` and a standing instruction for the others. A message body cannot forge the hub's own headers: such lines are quoted (`sanitize`). Replies inherit a hop count capped at 3, so agents cannot ping-pong forever; neither a digest nor a steer can reset it.
|
|
8
8
|
- **The control link is loopback plus a secret.** The daemon and the Codex proxy bind 127.0.0.1 only. The control WebSocket requires a per-run token (`.agenthub/state/control-token`, mode 600), and both servers refuse any request that carries an `Origin` header: any web page can open a WebSocket to localhost, and browsers always send `Origin`. External clients cannot claim the console user's id or a hub-managed peer's id.
|
|
9
9
|
- **Permission prompts stay on unless an operator selects a permission mode.** `ask` leaves native decisions unchanged. `ask-when-needed` grants scoped Claude file tools or Pi/local writes and edits; Kimi and Codex use their own native policies. `never-ask` requires explicit runtime confirmation or a person's console `y` for a config default at hub start. Until that config confirmation the peer runs in `ask`. Kimi's and `local`'s permission requests are relayed to the console and cancelled after `approvals.timeout_s` (default 120 s) of silence; the macOS notification for a waiting request carries the peer and the tool name only. The one exception is the hub's own tools (`hub_send` and the task tools, matched by exact name): Kimi's requests for them are approved once without a prompt and logged by name, the same trust Codex gets through `approval_mode` in the hub's config. They invoke hub-owned operations rather than arbitrary file or shell tools, and every call passes the hub's own checks. 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. A Pi or `local` request whose call was cancelled or whose turn ended is withdrawn at once: a later answer is refused and grants nothing, an `Always allow` included. One shell command runs without a hub approval: the `!command` a person types in the managed Pi TUI. It stays inside the same sandbox and denylist, and the hub takes it only from the verified owner process of a Pi in TUI mode, while Pi is idle, on a reservation that is used once; a model's own bash call never takes that path (issue #253). `--unattended` turns prompts off, says so loudly, and is never the default.
|
|
10
|
-
- **A committed config cannot choose launch commands, credential files, data endpoints, permission modes or a wider sandbox.** The machine-local fields (`kimi_cmd`, `permission_modes`, `codex_bin`, `pi.cmd`, `checks`, `mlx.bin`, `mlx.runtimeDir`, `mlx.modelPath`, `omniroute.urls`, `omniroute.access_hosts`, the `omniroute` key files, `memory.worker_url`, `local.read_allow`, `local.bash_network`, `local.network_allow`, `terminal.open`) 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, a permission mode, the command that opens a terminal, or a wider sandbox. A command in `checks` runs as you, outside the local worker's sandbox, like a git hook. So do the `setup` and `verify` commands of a benchmark suite (issue #251): `ahub bench run <suite.json>` is a person's command, refused from an agent session, and it resets the work tree for each attempt, so it runs only with `bench.enabled`, at the root of a clean repository that tracks nothing under `.agenthub/`. Nothing in `routing.toml` or in task text is ever run. `routing.toml` and the other shared fields still come from the checkout, and they matter: `routing.toml` picks the models the local worker and the hub's inference use at your gateway and can turn the PII constraint off, and roles and budget shape who does what. Review them in a repository you do not trust. The hub's own git calls outside the sandbox (snapshots, facts, config trust, project discovery, memory recall, the bench runner) all
|
|
10
|
+
- **A committed config cannot choose launch commands, credential files, data endpoints, permission modes or a wider sandbox.** The machine-local fields (`kimi_cmd`, `permission_modes`, `codex_bin`, `pi.cmd`, `checks`, `mlx.bin`, `mlx.runtimeDir`, `mlx.modelPath`, `omniroute.urls`, `omniroute.access_hosts`, the `omniroute` key files, `memory.worker_url`, `local.read_allow`, `local.bash_network`, `local.network_allow`, `terminal.open`) 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, a permission mode, the command that opens a terminal, or a wider sandbox. A command in `checks` runs as you, outside the local worker's sandbox, like a git hook. So do the `setup` and `verify` commands of a benchmark suite (issue #251): `ahub bench run <suite.json>` is a person's command, refused from an agent session, and it resets the work tree for each attempt, so it runs only with `bench.enabled`, at the root of a clean repository that tracks nothing under `.agenthub/`. Nothing in `routing.toml` or in task text is ever run. `routing.toml` and the other shared fields still come from the checkout, and they matter: `routing.toml` picks the models the local worker and the hub's inference use at your gateway and can turn the PII constraint off, and roles and budget shape who does what. Review them in a repository you do not trust. The hub's own git calls outside the sandbox (snapshots, facts, config trust, project discovery, memory recall, the bench runner) all use the single hardened implementation in the exact-pinned first-party `@staix/agent-mem/platform` package, re-exported by `src/hub/git.ts`, which disables the programs a repository's configuration can name — the file-system monitor, hooks, external diff, the pager, and the credential and askpass helpers, with prompts failing closed — so a config that ever does get written cannot run code with the hub's rights. Of those, the monitor, hooks and external diff are reachable by commands the hub issues (the planted-program test exercises all three; a `diff --no-index` reads the repo's config, so that call also passes `--no-ext-diff` and `--no-textconv`); the pager is not (every call pipes stdout). Credential and askpass helpers are disabled for project repositories; memory sync alone may use credentials for its own memory repository, without prompts. The cost: `core.fsmonitor=false` also turns off a person's enabled built-in monitor for the hub's own calls, so the per-turn snapshot's `add -A` scans the whole tree (the snapshot has a 10 s cap). git has no global switch for filter drivers, and of the hub's commands only the snapshot's `add`/`restore` would run one (its driver command lives in the protected `.git/config`).
|
|
11
11
|
- **Telemetry holds no bodies.** `.agenthub/state/events.jsonl` (issue #40) records envelope ids, routing and sizes, task ids and states, overlapping paths and token counts, and the task id (with a PII flag) that each usage and token record is attributed to. It never records a message body, a task title or detail, and marks private (PII) envelopes and tasks as such. It stays on the machine; `ahub export` only prints it. Two opt-in stores copy numbers from it into your home directory, shared by every project on the machine (directories mode 700, files 600): research records (issue #247, `research.enabled`) in `~/.agenthub/research/` hold per approved task the ids, states, counts, tokens, times and PII flag the events hold, a person's label from a closed list, and a hash in place of the project id; benchmark runs (issue #251) in `~/.agenthub/bench/` hold suite name and hash, arm label, the attached peers' ids, models and permission modes, task ids, outcomes, exit codes, counts, tokens and times, never suite text or a command's output.
|
|
12
12
|
- **Snapshots stay in your repository.** Per-turn snapshots (issue #33) are git objects in the project's own object store, written through a temporary index; nothing is referenced, pushed or copied elsewhere, and `git gc` prunes them. They hold what the work tree held, including untracked files that are not ignored, so keep secrets in ignored files. They carry the repository's own permissions, and nothing caps their disk use but `git gc`. A turn of a peer holding an open PII task is not snapshotted; a PII file left in the project is snapshotted by later turns like any other file. `ahub undo` restores only files whose current content is exactly what the turn left.
|
|
13
13
|
- **The edit hook reads, never decides.** `ahub check-path --hook` (issue #32) reads hub.db and returns context for Claude and a line for you; it sets no permission decision, so your permission rules stay in charge. It names other owners' task ids, titles and states, which then reach Claude's model; PII tasks are left out.
|
|
@@ -21,6 +21,18 @@ agent-hub connects agents that can each run commands. This page says what the hu
|
|
|
21
21
|
- **Secrets stay where they are read.** The gateway key and Cloudflare Access values are read inside the gateway client and go only into request headers: never into logs, errors, envelopes, tool output, memory, or the generated Switchyard config (the key travels by environment variable name). Subscription logins of Claude, Codex and Kimi are never proxied or pooled.
|
|
22
22
|
- **The hub's own model calls are fenced.** Digest condensation, task triage and the PII screen read agent-written text as data; their output is capped text framed as untrusted, or a value checked against a closed list. It is never used as a route, a peer id, a tool call or an instruction.
|
|
23
23
|
|
|
24
|
+
## Native memory authority and profile selection
|
|
25
|
+
|
|
26
|
+
The portable core is the exact-pinned first-party `@staix/agent-mem` 0.3.0 package; it does not replace the hub's caller identity, PII policy or task-write authority. Deny, hardened git, real paths and process identity are single-sourced in its Node-safe platform entry. Current project privacy is applied by the hub before promotion and again before model-facing reads. Standalone person reads are unscreened, and the standalone `--policy-free` acknowledgement grants no hub or peer authority.
|
|
27
|
+
|
|
28
|
+
`hub_memory_search`, `hub_memory_show` and `hub_memory_graph` take the project from the daemon, require `memory_read`, refuse private turns and validate UTF-8 input before logs or storage work. Complete records and graph metadata are screened before counts and truncation. Search is at most 20 lines/4 KiB (default 8); show accepts at most 8 ids and returns at most 8 KiB; graph is at most 120 nodes/240 edges/6 KiB (default 60 nodes), with no edge lacking an endpoint. Unknown, ambiguous and withheld ids share one neutral refusal. Arguments and results are excluded from hub-managed capture; this does not govern a vendor's independent transcript or memory hooks.
|
|
29
|
+
|
|
30
|
+
`hub_memory_correct` requires `memory_correct` and proposes a machine-local candidate after the same project/privacy checks, including revalidation after asynchronous screening. It cannot accept or alter a note. Public review failures with unmet items may also propose candidates through Tasks. Candidates and usage counters stay at `M/candidates.db`, are not synced/imported, and never enter recall, briefs, ask or graph evidence. A person accepts shared-v1 candidates through the package CLI's TTY/policy checks; the accepted result is an ordinary v1 note, while the sidecar remains local. Generation `amem review` remains unsupported and refuses. Citation counters are observation proxies, not evidence that an agent understood or used a note.
|
|
31
|
+
|
|
32
|
+
The hub binds the selected root/profile at open. Invalid/missing generation metadata refuses native memory without a shared-v1 fallback; the bus and board continue. Explicit checks at hub operation entry and before sidecar writes stop later stale calls, but are not atomic against a selector change during an in-flight core operation. Migration therefore requires quiescing consumers and closing handles before changing selection, then reopening through the supported lifecycle. A public core pre-write hook is the upgrade path for atomic in-flight cutover.
|
|
33
|
+
|
|
34
|
+
Before migrating any real home, every hub sharing it must first be on 0.12.30 or later with positive authenticated binding or unbound/quiesced proof from `ahub memory profile-check` in each relevant registry. An unknown or unreadable runtime is not proof of absence, and a matching ready binding still holds a handle that must be closed. Profile-check reports only its named registry inventory. Root paths, selector receipts and malformed-legacy counts are person-local diagnostics, excluded from ordinary peer status and allowed agent CLI reads. Source/fixture checks do not establish native capture, real migration or account/device-sync success.
|
|
35
|
+
|
|
24
36
|
## What is not defended
|
|
25
37
|
|
|
26
38
|
- **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.
|
|
@@ -70,7 +82,7 @@ Inside an agent session, `ahub` identifies the caller from `AGENTHUB_PEER_ID`,
|
|
|
70
82
|
or the native Claude/Codex shell marker when the hub marker is absent. It connects
|
|
71
83
|
as that peer in tools mode. Messages and task changes retain that actor;
|
|
72
84
|
`say` defaults to status priority and important messages still require the peer's
|
|
73
|
-
capability. Conflicting or malformed markers fail closed. There is no `--as-user`.
|
|
85
|
+
capability. Conflicting or malformed markers fail closed. Refusals name only the marker variable names actually present, never their values. There is no `--as-user`.
|
|
74
86
|
By default, console operations are refused before connecting, including permission answers
|
|
75
87
|
and permission modes, queue resolution, budget overrides, benchmark runs, research
|
|
76
88
|
backfill and task labels, stopping a peer (`ahub stop`), lifecycle and recovery
|
|
@@ -304,3 +316,31 @@ plain terminal pet reads and `pet --palette` remain available to agent shells;
|
|
|
304
316
|
the palette write merges only the closed name into `.agenthub/config.local.json`,
|
|
305
317
|
validated and replaced by temp file and rename. Signed pidfiles
|
|
306
318
|
fence instance ownership and close; unknown owners are never signalled.
|
|
319
|
+
|
|
320
|
+
## Planner, cancellation and delivery authority
|
|
321
|
+
|
|
322
|
+
The `planner` setting is person-controlled. A valid pin determines the planner without adding it 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. Planner guidance does not grant conductor authority or bypass roster, PII, WIP or owner capability checks.
|
|
323
|
+
|
|
324
|
+
`cancelled` is closed, never approval. The person can cancel non-approved work with a reason and optional supersession reference; the configured planner can cancel only its own proposed/backlog work with no historical acceptance. 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.
|
|
325
|
+
|
|
326
|
+
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. A manually started older binary is outside this compatibility guarantee: it can display cancelled rows as open or fail to move them.
|
|
327
|
+
|
|
328
|
+
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 completion of an uncertain delivery is not evidence that the offer was read.
|
|
329
|
+
|
|
330
|
+
`hub_delivery_retry` grants an eligible recipient one retry of its own `needs_review` work, never completion/discard authority or pause removal. 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.
|
|
331
|
+
|
|
332
|
+
## Bounded task specs and isolated checks (#418)
|
|
333
|
+
|
|
334
|
+
`hub_task_spec` reads only the GitHub issues named by a current owner's or reviewer's task in this project's origin repository, including the validated canonical GitHub intake source when its title/body has no issue token. Bare `#N` references count only in the title; detail references require `owner/repo#N` or `https://github.com/owner/repo/issues/N`. Bare detail numbers denote hub tasks, and arbitrary file paths are not parsed as issue references. A qualified reference cannot begin after a slash or a name character, so `src/hub/tasks.ts#12` is a file path. Routing uses this same parser. Without a GitHub project identity, bare title numbers do not require spec access, but explicit qualified/URL references, `refs.repo` and imported GitHub identity still fail closed. The daemon resolves authenticated and container executables outside the project, pins their absolute paths and uses fixed arguments; repository-writable PATH shims are refused. Runner Git resolves its host executable through a restricted PATH. For specs, the model receives no credential or arbitrary authenticated URL capability. Foreign/transferred issues, more than three references, bodies over 64 KiB and aggregate results over 80 KiB fail explicitly. Spec fetches share a 25-second budget inside the control client's 30-second cap; each gh call remains bounded to 15 seconds. Current PII, secret and private-path policy withholds the complete spec rather than returning a partial contract. PII turns and PII tasks refuse these tools. The independent `spec_read` and `check_run` capabilities apply when a peer has a narrowed capability list. Arguments and results are excluded from capture. Proven deterministic pre-effect guards return PreEffectToolRefusal so Pi settles the exact refusal receipt; generic transport/storage failures and failures after durable receipt/output changes remain uncertain, never reclassified from error text.
|
|
335
|
+
|
|
336
|
+
`hub_check_start` accepts only a current owner's in-progress task, its recorded branch/commit and either `typecheck` or one to 32 existing `test/*.test.ts` paths. `checks.runner = "container"` is a person opt-in in an untracked config file; a tracked config cannot grant it. Missing service/image, an unverified internal network, stale task/owner/native generation, unavailable dependencies or an invalid path fails closed. There is no host-shell, network or Seatbelt fallback.
|
|
337
|
+
|
|
338
|
+
The runner uses Apple `container`, pinned Bun 1.4.2 and a verified `hostOnly` internal network with DNS disabled, two CPUs and 4 GiB. It starts a named, labeled VM with a bounded idle process, copies a hub-owned snapshot into the running VM, then invokes a fixed `container exec` check. This run/copy/exec sequence is required because Apple `container copy` rejects a stopped container. No host directory, home, credential directory, hub state or socket is mounted. Source blobs are read from the exact Git commit; symlinks and gitlinks anywhere in the source refuse the run before exclusions. Directory prefixes and filenames are checked for NFC/Unicode case-fold collisions before staging writes, including denied entries; folding must reach a fixed point within a bounded number of passes or the run refuses. This is a conservative collision screen, not a claim of exact parity with every host filesystem. Denied and PII paths are excluded and counted. Dependency files require an equal lockfile, bounded regular files/directories and internal relative symlinks. No dependency installation or Internet access occurs in the VM.
|
|
339
|
+
|
|
340
|
+
Dependency copies detect observed inode, size and timestamp changes and refuse final-component symlinks using `O_NOFOLLOW`. A same-user writer can still exchange and restore a parent directory between observations; this is not an atomic immutable-dependency guarantee. Source commits remain immutable. Do not treat a dependency copy as protection against a hostile process running as the same host user.
|
|
341
|
+
|
|
342
|
+
Checks serialize through the repository's existing `ahub-heavy.lock`, with one active and at most two queued runs, a ten-minute execution ceiling after heavy-slot admission and a bounded heavy-slot wait. Container startup has a 120-second cap for cold VM boot, and copy has a 180-second cap for the bounded 512 MiB source plus 1 GiB dependency corpus. Each stage is clamped to the remaining ten-minute budget and uses the same cancellation signal; exec receives only the remaining budget. Network/probe commands retain a 15-second cap, also clamped and cancellable during an admitted run. Named-container cleanup has its separate bounded teardown commands and must still be confirmed before releasing the heavy slot. Queued cancellation settles its receipt and releases queue capacity immediately; active cancellation, timeout and shutdown stop only the named check container. Cleanup is confirmed through inventory before releasing the heavy slot; unknown cleanup keeps the slot and reports an error. Startup removes only this project's labeled containers whose signed recorded hub owner is confirmed gone. Unknown owners are reported for the person, never killed.
|
|
343
|
+
|
|
344
|
+
Only an unchanged task/owner/native-generation binding receives an agent result. Stale receipts are person-only. Public receipts include the exact source/tree/lockfile, runner/platform, command, exit and bounded output; they omit internal host paths, owner process identity and generation binding. The output tail is screened again before any tool response or hub envelope. For inactive valid receipts, local full output is bounded to 1 MiB and removed when the owner reads its result (person inspection does not consume it), after 24 hours or beyond the newest 20 outputs; at most 200 inactive valid receipts remain, with active runs excluded from retention. Unknown receipts are an explicit retention exception: malformed/unreadable receipt bytes, full output and source staging remain until ownership/inactivity can be established or the receipt is removed. Their ID-only warnings remain visible. Unknown payload maintenance performs no path-based deletion: a same-user process can swap a parent directory after an entry check, and no atomic descriptor-relative cleanup is available at this boundary. Maintenance never removes an unknown container. Removing the whole receipt directory counts as an empty record set, clears its ID warnings and allows ordinary expired unindexed-staging cleanup. Linux VM evidence is separate from macOS Seatbelt and managed-native acceptance.
|
|
345
|
+
|
|
346
|
+
Routing receives capability availability and its reason as pure inputs. Capability admission applies to task owners only: non-PII implement/test/bulk-edit owners require tests, and non-PII issue-backed owners in every class (including review-class owners) require spec access. Designated reviewers are not gated by these owner-only capabilities; roster, privacy, role and other eligibility rules still apply. PII tasks are exempt from these two capability requirements and retain the existing local-only owner and person reviewer rules; spec/check tools still refuse them. Explicit nominations and reservations cannot bypass these checks. Inactive sandboxed peers establish their GitHub probe at first spec use, and every explicit local/Pi start path refreshes it (including a prior authentication failure) with concurrent probes shared; host-provided embeddings can inject bounded probe implementations without implicitly contacting real accounts in fixtures. Neither path grants a model control over the probes.
|
package/docs/smoke.md
CHANGED
|
@@ -56,6 +56,17 @@ operationId, console hello and initiating-peer audit (225 related tests passed).
|
|
|
56
56
|
This is fixture/CLI proof, not an actual native Pi restoration; that live leg
|
|
57
57
|
remains owed in #273.
|
|
58
58
|
|
|
59
|
+
## Console task and queue operations (#424)
|
|
60
|
+
|
|
61
|
+
2026-10-16, macOS, source `691f00ac`: a disposable Git project and fake ACP
|
|
62
|
+
peer ran the real console in `tmux` at 100x30. Through actual
|
|
63
|
+
`ahub console --panels` key input, `a` plus a peer, Enter and `y` assigned with
|
|
64
|
+
the displayed `flowRevision`; `u`, Enter and `y` removed the owner/reservation;
|
|
65
|
+
and `r` plus a reason and `y` discarded a queued delivery. Readback showed
|
|
66
|
+
`state=proposed`, `owner=null`, `reserved=null`, `flowRevision=3`, and queue
|
|
67
|
+
states `completed`, `discarded`, `queued`. This was real terminal input and
|
|
68
|
+
daemon readback, not a fake-terminal assertion or a model turn.
|
|
69
|
+
|
|
59
70
|
## Hub memory (#258)
|
|
60
71
|
|
|
61
72
|
Not run yet (2026-10-12). Source and fakes cover tiers, the graph, retrieval, sync between two simulated homes and a bare remote, leases, import and suggestions (`test/memory-*.test.ts`).
|
|
@@ -159,8 +159,8 @@ hub CLI / console ── control WS 127.0.0.1:<ctl> ─────────
|
|
|
159
159
|
30 s, then goes offline with queued work retained if no report arrives. `--model` maps to `kimi --model <alias> acp`
|
|
160
160
|
(verified flag). Reusable for `opencode acp`.
|
|
161
161
|
- Local worker (amended in M3): a hub-native tool-calling loop over non-streaming
|
|
162
|
-
`POST /chat/completions` with plain `fetch`, no SDK (the repo keeps zero runtime
|
|
163
|
-
dependencies). Tools `read`, `write`, `edit`, `bash`, `git`, `hub_send` (`hub_task_*` with
|
|
162
|
+
`POST /chat/completions` with plain `fetch`, no SDK (the repo keeps zero third-party runtime
|
|
163
|
+
dependencies; the first-party memory core is an exact-pinned library). Tools `read`, `write`, `edit`, `bash`, `git`, `hub_send` (`hub_task_*` with
|
|
164
164
|
M4). Paths are realpath-scoped to cwd with a secrets denylist; `.git` and `.agenthub` are
|
|
165
165
|
not writable. `write`, `edit`, `bash` and mutating `git` go through the console permission
|
|
166
166
|
relay unless the hub runs `--unattended`. Everything that executes runs under seatbelt
|
|
@@ -554,7 +554,7 @@ New repo layout:
|
|
|
554
554
|
src/hub/ daemon, bus, peer-registry, state-machines, board, budget, router
|
|
555
555
|
src/adapters/ claude-channel.ts, codex-appserver.ts, acp.ts, local-worker.ts
|
|
556
556
|
src/switchyard/ config generator, sidecar lifecycle, health
|
|
557
|
-
src/memory/ claude-mem
|
|
557
|
+
src/memory/ hub memory integration: claude-mem client, capture, briefs and recall; portable core is @staix/agent-mem
|
|
558
558
|
src/cli/ up, claude, codex, kimi, local, say, tail, board, route, budget, status, logs, kill, init
|
|
559
559
|
plugins/agent-hub/ .claude-plugin/plugin.json, .mcp.json, server bundle, hooks (SessionStart health, Stop announce)
|
|
560
560
|
.claude-plugin/marketplace.json
|
|
@@ -1255,7 +1255,8 @@ session modes (`default`, `plan`, `auto`, `yolo`) but nothing per server or tool
|
|
|
1255
1255
|
ignored with a note in `hub.log` and `ahub doctor`, and is no longer
|
|
1256
1256
|
machine-local because nothing reads it.
|
|
1257
1257
|
- Capabilities: `propose`, `assign` (a proposal naming another peer as owner),
|
|
1258
|
-
`remember`, `
|
|
1258
|
+
`remember`, `memory_correct` (screened correction candidates only, never acceptance), `memory_read` (the `hub_memory_search`, `hub_memory_show` and
|
|
1259
|
+
`hub_memory_graph` read tools, #412 P2), `important`. A peer not listed in `capabilities` has all of them,
|
|
1259
1260
|
which keeps today's behaviour, and a listed peer whose value is not a list has
|
|
1260
1261
|
none; the console user and the hub are never limited. `remember` gates the
|
|
1261
1262
|
`hub_remember` tool only, not the notes the hub keeps of done summaries and
|
|
@@ -1286,7 +1287,9 @@ session modes (`default`, `plan`, `auto`, `yolo`) but nothing per server or tool
|
|
|
1286
1287
|
## Reliability follow-ups, 0.12.1 (#89-#95)
|
|
1287
1288
|
|
|
1288
1289
|
- Delivery retries exhausted for an owned open task trigger escalation through `Tasks`, with a redacted reason and a console notice. Delivery health skips peers after three consecutive exhausted deliveries until a completed delivery clears the streak.
|
|
1289
|
-
- `needs_review`
|
|
1290
|
+
- `needs_review` holds uncertain work that still matters. The hub settles a row `completed` only when every original is a hub presence item or a hub task/review notice provably moot for its recipient: an approved or cancelled task (except its cancellation stop notice), a review no longer in review for that reviewer, or a notice whose authoritative `refs.taskRecipient` audience (`owner` or `reviewer`) is no longer current. Hub producers tag current-owner notices, reviewer reminders and planner coordination separately; former-owner stop notices and former-owner/former-reviewer withdrawal notices use untagged kind `task` and stay held until task approval or cancellation, because their purpose is to tell the former role to stop. Task-kind reviewer reminders remain live while the recipient is that in-review reviewer. Planner notices and untagged legacy task-kind notices stay held until approval or cancellation: without an audience marker, sender/kind/task id cannot prove that an owner assignment moved. This conservative legacy ceiling preserves still-live coordination and guidance. Unknown tasks, live recipient notices and mixed peer/user rows remain held. Rows whose recorded reason starts with `requested stop:` are never completed automatically, even when their tasks become approved. `supersededDeliveryReason` alone authorizes completion; a separate pure `deliveryTaskContext` retains ids-only approved/moved task context for mixed rows without authorizing settlement. Status, console notices and console/peer/tool route explanations list every hold once, including sender, kind and age; public explanations screen appended hold lines through the current privacy policy and keep task context ids-only. Classify on entry, after restore, after the daemon releases its recovery hold and on board changes; use the existing resolve path with a recorded hub actor, preserving pauses and Tasks state. Hub completion restores presence originals through the same preface merge and transaction rollback path as retry, preserving shared notes for the next queued delivery; person completion remains consumed (#434).
|
|
1291
|
+
- A Claude session without channel support returns `failed_safe` before the push reaches the model, preserving originals for pull. If an older daemon rejects that receipt, the plugin falls back once to its prior generation-bound `needs_review` receipt instead of leaving the row dispatching. A thrown `server.notification` remains `needs_review`: the MCP SDK delegates to a transport whose stdout write can already have emitted bytes before an exception is observed, so the exception alone cannot prove no frame left. Existing proven pre-acceptance guards in Codex, ACP, Pi and the local worker remain unchanged. Console-only command refusals name only the inherited identity marker keys actually present (never their values), explain `!` commands, and direct the person to a separate terminal or `ahub console` (#434).
|
|
1292
|
+
- `hub_delivery_retry { delivery_id }` lets an authenticated recipient retry only its own current `needs_review` row. This option is offered only to transports with an independent tool turn (Claude channel/pull, Codex and Pi). The delivery-only local worker and headless ACP peers cannot start a tool turn through their own hold, so they do not advertise this tool and the daemon refuses a manual call before mutation. Their held hints ask the person to resolve the row; ordinary messages never bypass the hold. Kimi TUI remains pull-only and keeps the recipient option. An ephemeral delivery-only MCP marker controls exposure and hint copy only; the current adapter class is the authority. There is no action or reason input: `completed` and `discard` remain person-only. Any explicit retry in that recipient's lineage, including a person's retry, consumes this allowance; a redelivery held again stays for the person. The existing resolution transaction records `peer:<id>` and a fixed reason, keeps original envelopes unchanged and requeues them through the existing put-back path. A busy recipient may consent to this queue-only operation, but delivery waits for idle and all pause/recovery and active-delivery fences remain. The rendered clone reserves the existing journal body cap for the warning, clipping only the clone at a Unicode character boundary with an explicit cut marker when needed; originals remain unchanged. Jcode project MCP entries carry the same delivery-only exposure marker as other headless ACP peers, preserving their own configuration and peer identity rules. Final rendered output, after condensation and on pull or steer, starts with the fixed sanitized header `Hub redelivery warning: An earlier attempt of this delivery is uncertain; check for its effects before acting.` Explicit retry history is scoped by recipient and original envelope ids. A separate monotone consumed-proof projection records one proof id and warning provenance per original, leaving resolution audit rows unchanged. Explicit resolution and coalesced handoff inherit that proof atomically for every grouped non-context original; a rejected checkpoint consumes no fresh allowance. Hub presence originals carry mutable recall/shared context whose id survives merging, so their ids never participate in retry proof lookup, inheritance, warnings or unknown marking. A preface shared with earlier work cannot consume an unrelated new message's retry allowance; actual task/chat originals retain their consumed lineage. Safe-failure requeue, withdrawal of an earlier original, terminal-state changes, pruning, restart or older-writer audit replacement cannot renew the allowance. Older attempt-bearing rows backfill this projection on return. Recovery snapshots without history and unmappable pruned pre-feature retry records mark only their affected recipient/original ids unknown: those lineages require a person, while unrelated new messages keep their allowance. Tool results expose held metadata only (total count, ids, senders, kinds and age), at most 32 rows/4 KiB with explicit truncation; no body or task text enters the hint. These hints work independently of channel mode and do not wait behind the held queue (#434).
|
|
1290
1293
|
- Local attach and idle model/route replacement require a reachable authenticated gateway, served models and successful minimal availability calls. Busy workers refuse replacement; validation failure leaves the current worker intact. Controlled recovery can reconstruct a manually paused local worker without gateway availability, but manual resume validates its restored choice before lifting the pause. Doctor checks the fixed model against the inventory.
|
|
1291
1294
|
- Reviewer routing appends attached peers holding `roles.reviewer` after the review class's candidates. Without a reviewer, task results and console notices explain direct approval and the skipped candidates.
|
|
1292
1295
|
- Conflict detection includes `in_review` work. Completed overlapping snapshots that share files generate one concurrent-edit notice per task pair/file, containing both turn ids and attributing the changes to neither peer. Private tasks and sensitive file names remain redacted.
|
|
@@ -1320,7 +1323,8 @@ to that socket, and a live accepted journal row. This acknowledgement is not pro
|
|
|
1320
1323
|
of a native turn boundary. Failed notifications, disconnects, replaced sessions
|
|
1321
1324
|
and interrupted daemon instances retain `needs_review`, with no automatic replay.
|
|
1322
1325
|
A person inspects and resolves uncertain work through the existing revision-fenced
|
|
1323
|
-
`ahub queue` commands
|
|
1326
|
+
`ahub queue` commands; #434 also permits an addressed peer with an independent tool turn one explicit
|
|
1327
|
+
`hub_delivery_retry`, with the transport, warning and lineage limits above. Older source protocols 9, 10 and 11 remain authenticated
|
|
1324
1328
|
upgrade sources; ordinary clients must use protocol 12 (13 from 0.12.4, with 12 a source).
|
|
1325
1329
|
|
|
1326
1330
|
## Amendment: stale overlap notices (issue #106)
|
|
@@ -1494,7 +1498,13 @@ Unicode cell widths, fall back below 80x24, and restore terminal state on exit.
|
|
|
1494
1498
|
Tabs change with the digits 1 to 5 or with Left and Right, wrapping at both
|
|
1495
1499
|
ends, with the digit's guards and resets (an arrow under a confirmation dismisses
|
|
1496
1500
|
it like any key but y, without moving a tab); a read holding nothing but arrow
|
|
1497
|
-
sequences is that many key presses, never a paste.
|
|
1501
|
+
or Home/End sequences is that many key presses, never a paste. Home or `<` selects
|
|
1502
|
+
the first row of a panel and End or `>` the last (Home tops an open view); the
|
|
1503
|
+
Events panel follows new events while its selection is on the last row, keeps a
|
|
1504
|
+
row the person moved to, and opening it or changing its filter selects the newest
|
|
1505
|
+
row (#441). In panels `w` opens the project dashboard (`ahub ui`) and `o` the pet
|
|
1506
|
+
window (`ahub pet --window`) as a person's console command; the `:` line also
|
|
1507
|
+
accepts `ui` and `pet` (#452). An Events row's header is
|
|
1498
1508
|
tokenized by the stream's own tokenizer on the same tab-flattened text, with the
|
|
1499
1509
|
event's tone and kind; body lines stay plain, and selection changes only the
|
|
1500
1510
|
`>` marker. The tail prints a peer's context reading once and repeats it only
|
|
@@ -1947,6 +1957,63 @@ task list polling does not. `hub_status` stays conductor-only.
|
|
|
1947
1957
|
proposer as the actor with reason `manual` (on the `reassigned` or `reserved`
|
|
1948
1958
|
entry), and writes no conduct audit event; during a PII turn it is refused
|
|
1949
1959
|
like the conductor's.
|
|
1960
|
+
|
|
1961
|
+
## Amendment: revision-safe console task and queue operations (issue #424)
|
|
1962
|
+
|
|
1963
|
+
- Task list/show responses add `flowRevision`, the monotonic history length,
|
|
1964
|
+
separate from `planningRevision` and the delivery journal revision. This is
|
|
1965
|
+
additive public metadata and does not bump the control protocol. `Board.update`
|
|
1966
|
+
can check an expected value in its synchronous read-check-write section.
|
|
1967
|
+
- A revision-bound `ahub task assign` uses the new console-only
|
|
1968
|
+
`task_assign_checked`; unchecked `task_assign` remains compatible. The new
|
|
1969
|
+
console-only `task_unassign` and checked operation are unknown to an older hub,
|
|
1970
|
+
which therefore refuses rather than ignoring the revision. A console whose
|
|
1971
|
+
rows lack `flowRevision` refuses task row actions and asks for an upgrade.
|
|
1972
|
+
`ahub queue resolve` accepts an optional displayed revision and otherwise keeps
|
|
1973
|
+
its existing fresh-show behavior. New operations and optional fields preserve
|
|
1974
|
+
the protocol version.
|
|
1975
|
+
- Tasks and Queue selections follow ids across refreshes, never indices into
|
|
1976
|
+
replacement rows. A row-started action fixes its id and revision while peer,
|
|
1977
|
+
reason and final role/action remain editable; confirmation uses the validated
|
|
1978
|
+
final command and its consequence. Refresh follows every completed console
|
|
1979
|
+
command, including failure and saved-but-unconfirmed assignment, without an
|
|
1980
|
+
automatic resend. Existing `AssignmentUndeliveredError` semantics remain.
|
|
1981
|
+
- Tasks `a` assigns owner in `proposed`, `in_progress` and `changes_requested`,
|
|
1982
|
+
and reviewer in `in_review`; waiting owner assignment is a reservation. `u`
|
|
1983
|
+
unassigns, and `r` reviews only in `in_review`. Direct owner reassignment or
|
|
1984
|
+
unassignment in `in_review`, physical task deletion/cancellation and roster
|
|
1985
|
+
removal stay outside this feature.
|
|
1986
|
+
- `Tasks.unassign` is person-only. It refuses `approved` and `backlog`, an
|
|
1987
|
+
already absent role and owner removal in `in_review`. Owner removal clears the
|
|
1988
|
+
owner and reservation and returns active work to `proposed`; reviewer removal
|
|
1989
|
+
keeps the current state. The human reviewer of PII work cannot be removed.
|
|
1990
|
+
Owner removal writes `unassigned` and leaves turn-free cohorts; reviewer
|
|
1991
|
+
removal writes `reviewer changed`. Neither operation routes a replacement,
|
|
1992
|
+
adds a decline exclusion, stops a native process or rolls back effects.
|
|
1993
|
+
- A person `unassigned` event suppresses later automatic owner offers across
|
|
1994
|
+
dependency release, planner pulls, release timers and restart recovery until
|
|
1995
|
+
an explicit assignment or reservation. The marker is derived from history, so
|
|
1996
|
+
no schema migration is needed, and it also fences planner undo and re-pull.
|
|
1997
|
+
Former queued task/review requests are withdrawn, but accepted, dispatching
|
|
1998
|
+
and needs_review deliveries are never implicitly resolved. Withdrawal notices
|
|
1999
|
+
use delivery evidence or accepted/claimed board evidence, never an unread
|
|
2000
|
+
queue alone. Former agents fail current-authority checks while the person's
|
|
2001
|
+
explicit review override remains.
|
|
2002
|
+
- Assignment/review publication has a role-specific fence after every await and
|
|
2003
|
+
immediately before publication. Existing supersession events, role generation
|
|
2004
|
+
and task state suppress obsolete envelopes; accept, done and approval count as
|
|
2005
|
+
supersession even when the owner id is unchanged. Supersession returns the
|
|
2006
|
+
current task rather than aborting the remaining operation. Current metadata is
|
|
2007
|
+
re-read before constructing a still-valid envelope, so reviewer-only changes
|
|
2008
|
+
refresh reviewer content and quiet bookkeeping does not suppress valid owner
|
|
2009
|
+
delivery.
|
|
2010
|
+
- Queue `r` prefills `discard` for `queued` and `retry` for `needs_review`, never
|
|
2011
|
+
`completed` by default. Every resolve needs reason and confirmation. Queued
|
|
2012
|
+
discard removes queued originals; needs-review discard settles its receipt and
|
|
2013
|
+
hold. Retry creates one linked attempt. None cancels a task or claims native
|
|
2014
|
+
or external rollback. Task and queue projections retain their distinct PII and
|
|
2015
|
+
envelope-privacy redaction.
|
|
2016
|
+
|
|
1950
2017
|
## Amendment: delegated agent live verification (#408)
|
|
1951
2018
|
|
|
1952
2019
|
- Decision, 2026-10-16: an agent explicitly authorized by the user to perform a live test may use the global `--live-test` flag, before the command and in either order with `--project`. Duplicate global options refuse. Default command classification and malformed/conflicting identity refusals stay unchanged.
|
|
@@ -2921,3 +2988,71 @@ and repo-heavy work was coordinated over chat it read too late.
|
|
|
2921
2988
|
holder whose pid is gone is reaped. The slot works with no hub running. It is
|
|
2922
2989
|
not bus-visible; moving it into the hub is the upgrade if pid reuse or reap
|
|
2923
2990
|
races bite.
|
|
2991
|
+
|
|
2992
|
+
|
|
2993
|
+
## Amendment: consume the first-party agent-mem package (#412, P5)
|
|
2994
|
+
|
|
2995
|
+
The portable core is the first-party `@staix/agent-mem` library, initially exact-pinned at `0.1.1`; agent-hub keeps zero third-party runtime dependencies. Its store, sync, claude-mem import, project/home helpers and raw memory/goal commands are imported from the package root, with no vendored copies. Platform helpers use the public Node-safe `@staix/agent-mem/platform` subpath: `0.1.0` exposes only a root that loads `bun:sqlite`, which cannot load in Node-hosted Pi extensions. `0.1.1` supplies the required compatibility export without changing the platform algorithms. The package is the single implementation of deny, hardened git, realPath and process identity, re-exported through the existing local/hub/Pi modules so their callers retain one contract. Package storage semantics and the compatibility schema/hash/path invariants are unchanged.
|
|
2996
|
+
|
|
2997
|
+
`src/memory/` retains only hub integration: briefs, recall, the optional claude-mem worker client and capture. The hub owns `src/hub/memory-policy.ts` (`withoutPii`, `promotionScreen`, `mentionsPrivatePath`), `src/hub/read-tools.ts`, task lifecycle writes, lease renewal, model-suggested edges, ask and dashboard consumers. Current project policy is injected through `StoreOptions.screen` and the raw command callbacks. Tasks remains the write authority; PII-turn refusals, current-policy screening before counting/truncation, capture exclusion and the peer read-tool byte budgets remain unchanged.
|
|
2998
|
+
|
|
2999
|
+
`ahub memory` and `ahub goal` keep the hub's argument and identity contract, screened agent-readable views, project configuration and TTL. The standalone person-only `amem` CLI is the package's interface; its `--policy-free` acknowledgement is not an ahub option or a reason to bypass host privacy policy. Memory availability and sync remain fail-open for hub turns, while privacy checks keep their existing fail-closed behavior. Data directory precedence is supplied by the package's `memoryHome()` without migration: `AMEM_HOME`, then `$AGENTHUB_HOME/memory`, then `~/.agenthub/memory`; explicit `StoreOptions.home` retains its parent-directory meaning.
|
|
3000
|
+
|
|
3001
|
+
`docs/memory.md` documents hub integration; [agent-mem](https://github.com/STAIxBWLB/agent-mem) owns the portable data model and CLI documentation. Portable-core import boundary checks belong to the agent-mem package CI. The former hub-local core boundary checker and its check.sh step are removed with the core; the hub keeps its exact first-party dependency pin. Consuming the package does not complete the owed live legs: copied-home old/new parity, source-unchanged checks, rollback and unrun account/device/sandbox receipts are reported separately. No release, runtime replacement or live proof is implied by the source change.
|
|
3002
|
+
|
|
3003
|
+
The consumed package requires Bun >=1.4.2; the hub manifest, plain-JavaScript startup guard and installation guide share that minimum. Node still receives the actionable refusal before any TypeScript or Bun-only import.
|
|
3004
|
+
|
|
3005
|
+
Recovery source preservation retains the exact first-party memory package under the preserved runtime, without copying any other node_modules content or linking to the mutable global install. Package identity/version and all retained dependency bytes participate in the recovery digest; cached or preserved dependency drift is refused. Preserved-source verification requires the package inside its own node_modules, refusing a missing or symlinked copy rather than falling back to a mutable ancestor installation. Staged release verification similarly binds dependency lookup to its retained install directory, including cached releases and a fresh coordinator. Releases without runtime dependencies keep their historical digest.
|
|
3006
|
+
|
|
3007
|
+
|
|
3008
|
+
## Amendment: P7 Stage A read-only memory presentation (#412 P7-1)
|
|
3009
|
+
|
|
3010
|
+
The exact first-party pin is `@staix/agent-mem` 0.2.0. Agent-hub adds advisory plan-matching live fails to owner briefs and reviewer checklists, optional labeled fail text validated before the 4000-byte cap, person-only decay review, and privacy-filtered bounded read-time staleness markers. The task/PII boundaries remain in Tasks and the daemon. Details and ceilings are in `docs/memory.md`; no new v1 op, stored marker, candidate writer or live-leg claim is introduced.
|
|
3011
|
+
|
|
3012
|
+
The P7-1 hint C3 probe gate assumes the tree is not modified during a call: no probe reaches a declared private/denied path in that scope. Concurrent ancestor swaps by a process running as the person are outside the security threat model and may expose one existence bit; this ceiling has a deterministic witness test.
|
|
3013
|
+
|
|
3014
|
+
|
|
3015
|
+
## P7 candidate producers and local observations (#412)
|
|
3016
|
+
|
|
3017
|
+
Tasks produces a fail candidate from a public changes-requested review with unmet items, after the current Tasks privacy screen clears the complete candidate text. The review evidence identifies the task, review event and reviewed head (explicitly unknown when absent). Missing cause or retry conditions remain explicitly unknown. Candidate failure never changes the saved review outcome.
|
|
3018
|
+
|
|
3019
|
+
`hub_memory_correct {id, text, reason}` requires its own `memory_correct` capability and refuses a PII turn. Input caps are checked before logging or storage; exact or unique visible-prefix resolution uses the current project and full-record screening. Tasks checks the proposed text like `hub_remember`, then rechecks context and target after asynchronous screening. A successful call records the full target id and revision in CandidateStore and returns only its candidate id and person-review guidance. MCP/Pi/local share the tool definition; capture excludes both arguments and results.
|
|
3020
|
+
|
|
3021
|
+
Journal handoff observes rendered native memory lines in the final preface/brief payload. First positive journal acknowledgement observes delivered for that delivery and its original render context, not a later turn; pull's completed read is the existing journal acknowledgement contract. Peer messages and cleared task completion summaries observe visible `mem:<id8+>` citations. Counter keys use full entry id, project and known task/turn/session plus journal delivery id for rendered/delivered. Unattributed citations use message:<envelope-id> or summary:<task>:<history-index> observation keys with rendered/delivered unknown; these are citation counts, not proof of a causal per-delivery ratio. Absent observations and identities remain unknown. A bounded in-memory render-context map does not reconstruct acknowledgements after restart, so those delivered values remain unknown. Private envelopes, PII tasks and PII turns produce no rows. There is no usage sync or note mutation.
|
|
3022
|
+
|
|
3023
|
+
The machine-local candidate/counter sidecar remains outside native import, recall, briefs, ask and graph evidence. A person accepts or rejects through `amem review` with its existing TTY/policy authority. Dashboard acceptance is deferred. No new v1 operation, note kind or scope is introduced.
|
|
3024
|
+
|
|
3025
|
+
## Amendment: selected memory profiles in the hub (#412 P9)
|
|
3026
|
+
|
|
3027
|
+
The exact first-party dependency pin is `@staix/agent-mem` 0.3.0. The memory root `M` follows the package's existing home precedence independently of project state. An absent `M/profile.json` binds shared v1; an explicit generation binds `M/generations/G` through the package profile API. The hub supplies current promotion/privacy policy and TTL at that open boundary. Generation reads include the package's readonly legacy bridge; writes and generation sync stay in `G`, outside legacy logs and legacy-main history. No hub tool gains migration or prune authority, and shared-v1 operation semantics remain unchanged.
|
|
3028
|
+
|
|
3029
|
+
One explicit profile binding is used by daemon Tasks, read tools, briefs/recall/ask/dashboard, lease renewal, suggestions and sync. Hub memory and goal CLI opens use the same profile resolution. Invalid selector or missing/corrupt active generation makes native memory unavailable while bus/task service continues, with no automatic shared-v1 fallback. Daemon status distinguishes the memory package/profile binding and availability from hub health. Existing handles remain bound: periodic checks and explicit checks at hub operation entry fence later stale calls rather than rebinding the daemon. No proxy/private core ABI is used. These checks are not atomic with an in-flight core operation, so in-flight cutover is excluded: consumers must be quiesced and closed before migration/selector changes. A public core pre-write hook is the named upgrade path for an atomic fence.
|
|
3030
|
+
|
|
3031
|
+
Candidates/counters remain at the independent root `M/candidates.db` using the package's explicit sidecar home API. They are neither migrated into `G` nor synced. Selector changes fence Tasks candidate producers and retained telemetry callbacks as well as native note operations; asynchronous privacy work rechecks binding before side effects. Standalone generation `amem review` remains unsupported and refuses; this change adds neither candidate acceptance nor dashboard acceptance for generations.
|
|
3032
|
+
|
|
3033
|
+
PII authority, complete-row host screening, capture exclusion, byte budgets and fail-open task delivery remain unchanged. Memory/goal domain JSON remains unchanged; `memoryProfile` is additive only in daemon status, profile-check and the decay object. Synthetic integration must show bridged legacy reads, cross-writes through the package CLI and daemon tools, failure without fallback for invalid/missing generation state, and no note/candidate/counter writes for calls begun after a selector change while bus service remains available. Source/fixture evidence does not authorize real installation, migration or session restart. Real rollout requires separately approved deployed versions and matching live root/profile/selector bindings, synthetic canaries and quiesced/closed/reopened handles. Every running hub using this home needs an authenticated receipt: a matching ready binding, or a generation-capable unbound receipt (`disabled` when native memory is off, `unavailable` when opening failed) proving it holds no memory handle. An unbound daemon and zero running hubs are quiesced for memory; a stale bound handle, unreadable registry or unknown runtime is not ready. Native capture, account/device sync and real memory receipts remain open until observed.
|
|
3034
|
+
|
|
3035
|
+
### Bounded sandboxed-peer capabilities (#418)
|
|
3036
|
+
|
|
3037
|
+
The frozen issue #418 contract supplies task-bound `hub_task_spec` and the finite `hub_check_start/result/cancel` tools; memory reads reuse #412 P2. `checks.runner = "container"` remains an untracked, person-owned opt-in. Specs include validated canonical GitHub intake identity as well as bounded textual references and remain complete-or-refused; checks run immutable Git source in a credential-free, internal-network Linux VM with fixed commands and binding-scoped receipts. Default sandbox permissions remain unchanged.
|
|
3038
|
+
|
|
3039
|
+
Apple container 1.5.0 requires a running VM for `copy`. The implementation therefore uses `run --detach --rm` with the fixed bounded idle process, copies the validated source/dependencies, executes only the selected finite check argv and confirms cleanup. A create/copy/start probe failed on the stopped-container copy and left no container; the run/copy/exec probe passed. `--internal` is verified as network configuration `mode: hostOnly`, not an inferred network name. Inactive sandboxed peers establish the cached spec authentication probe at first start/use rather than making unrelated host-embedding fixtures contact real accounts. Source and dependency staging ceilings are 100,000 entries each, 512 MiB source and 1 GiB dependencies; exceeding one refuses the check.
|
|
3040
|
+
|
|
3041
|
+
Without a GitHub project identity, bare title issue numbers require no spec capability; qualified GitHub references, issue URLs, explicit repository refs and imported external issues still require it and fail closed at the repository boundary. Malformed receipts remain visible as unknown ids and never prevent startup. Unknown receipt bytes, full output and source staging are retained until ownership/inactivity can be established or the receipt is removed, an explicit exception to normal retention. Unknown containers require confirmed ownership/inactivity before cleanup; removing a receipt never authorizes their removal. Unknown payloads are never deleted through path-based entry checks, which cannot prevent a same-user parent-directory swap at this boundary. Removing the receipts directory is an empty record set, clearing obsolete unknown warnings and allowing normal expired staging cleanup.
|
|
3042
|
+
|
|
3043
|
+
Evidence: `docs/verification/2026-10-17-418-probes.md`. The feasibility probe is Linux/arm64 Apple-container evidence, not a managed-Pi or macOS security acceptance receipt. Issue #418 stays open until its managed-Pi live and independent review gates pass.
|
|
3044
|
+
|
|
3045
|
+
## Amendment: designated planner (#455)
|
|
3046
|
+
|
|
3047
|
+
`config.roles` is derived from the policy roles (defaults, then each file's `roles` block) by `settlePlanner` in the daemon, in place, so Tasks reads the same object. A `planner` key (`config.json` or `config.local.json`, local wins; one of `PLANNER_PEERS`, which excludes the hub-sandboxed pi and local) makes that peer the only planner. With no key the `roles` planners stand; when none of them is on the roster, the first rostered `PLANNER_PEERS` entry plans (`automatic`), recomputed at start, on each policy refresh and after a roster change, never written to disk. The `planner` setting (group Roles, live, risk raises) is the one write path; status carries `planner: { peers, source }` and `roleInfo[peer].planner`. Planning authority and backlog notices follow at once; role text in a peer's instructions follows at its next start. A pinned planner that leaves the roster leaves the project without a planner until a person changes the setting. Planning operations that await a PII screen (pull, plan, split, new release) recheck planner authority after the await, before their board write.
|
|
3048
|
+
|
|
3049
|
+
|
|
3050
|
+
## Amendment: task stalls, exact offer attention and cancellation (#438)
|
|
3051
|
+
|
|
3052
|
+
- Ready ownerless tasks retain the ordinary routing decision and reservation guards. A persisted cause/replanning signature admits one ids-only console and supervision notice for an ineligible reserved peer, no eligible peer or a cancelled dependency. Repeated release passes with the same cause add neither history nor notices; a changed cause or explicit replanning admits one new notice. The designated planner (the settled configured planner roles) receives bounded ids-only advice in its next preface, while the configured supervision stream receives the structured item under its existing scope. Advice does not manufacture an ordinary task turn beside a stale backlog batch. Without a planner, the console attention list remains the recipient. Existing owner/reviewer ladders remain unchanged.
|
|
3053
|
+
- A current proposed offer to a pull-only peer remains console attention until acceptance, decline, supersession or positive delivery evidence for that exact offer envelope and recipient. Tasks records the original envelope id, assignment revision and offer-time pull-only classification before publication. This classification survives detach and restart; current attachment is not evidence that an unread offer disappeared. Actual adapter completion, a correlated reply or a completed pull records positive evidence atomically with journal completion, surviving terminal pruning and reopening. Accepted-only receipts, unrelated inbox activity and operator/hub completion are not read evidence. Advisory queries never dequeue, steer or settle work. Decoded offer/stall candidates are cached by board revision and live eligibility inputs; positive journal read evidence is checked afresh. The derived revision advances atomically with task and stall writes, independently of unrelated delivery-journal writes.
|
|
3054
|
+
- The disabled-by-default sweep adds `ready-unassigned` with `ready_min: 60`. A ready proposed ownerless task follows the existing deterministic, restart-safe ladder, without treating quiet stall/offer bookkeeping as activity. A person-unassigned task stays suppressed until explicit person assignment or reservation. Planner-pulled work retains reserved-only/WIP admission, and a saved-but-undelivered reassignment does not stop the remaining sweep. Cancellation does not satisfy a dependency. This change does not enable any project's live sweep configuration.
|
|
3055
|
+
- `cancelled` is terminal and retained. The person may cancel backlog, proposed, in-progress, in-review or changes-requested work, never approved work. A configured planner tool may cancel only its own backlog/proposed task with no historical acceptance. `Tasks.cancel` validates the optional flow revision, screens the reason/reference and rechecks authority, revision and recovery/shutdown exclusion after asynchronous screening before the sole board mutation. History retains actor, reason and optional supersession reference. After the cancellation write commits, advisory cleanup and notices cannot change its successful result. No outcome memory, native cancellation, branch deletion or dependent release occurs.
|
|
3056
|
+
- Cancellation releases overlap/reservation participation and sends each distinct former owner/reviewer one stop notice. Its trusted cancellation revision marker keeps that stop notice relevant; earlier notices for the cancelled task become moot under #434. The classifier never uses timestamps alone to identify the stop notice. Dependents keep waiting and receive the cancelled-dependency cause. List, show, explain, dashboard and release projections treat cancellation as closed, retaining the current privacy screen and distinguishing cancellation from successful approval.
|
|
3057
|
+
- The daemon advertises `task_cancel` in hello and status features. Current clients refuse cancellation without that feature. Upgrade and restart persist the verified target's exported features with its immutable root/digest before prepare, including cached targets, and reuse that receipt on resume after verifying the same bytes. A legacy staged receipt without feature evidence is probed only while a project is not yet past commit; fully committed projects do not require another probe. This preserves #215: a transient probe failure cannot refuse a target that was already staged with verified evidence. Preflight checks only uncommitted sources and refuses a target lacking `task_cancel` while cancelled rows exist. Source prepare/commit recheck before holds/snapshots and around asynchronous work; cancellation cannot write through recovery preparation. Refusals name task ids and never rewrite rows. An older binary started manually can misclassify cancelled rows as open or fail to move them; this is the explicit unsupported compatibility ceiling. Never map cancellation to approval.
|
|
3058
|
+
- The post-release live step remains separate: do not cancel the historical task or enable the project's sweep from implementation tests. AC7 needs installed-runtime receipts.
|
|
@@ -249,11 +249,36 @@ itself is not distinguished from a slow one, and whether Orca reports the bare
|
|
|
249
249
|
login shell as TUI-idle is not verified: if it does, the create stops at once as
|
|
250
250
|
not mappable to the session; if not, after the 10-minute readiness bound. Either
|
|
251
251
|
way it blocks as needing manual verification with the receipt `pending`, and the
|
|
252
|
-
next resume, finding no live launcher and no attached session, records `failed`.
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
252
|
+
next resume, finding no live launcher and no attached session, records `failed`.
|
|
253
|
+
After a new terminal becomes ready, or a `pending` or `failed` receipt finds a
|
|
254
|
+
verified live launcher of the current target, the coordinator saves that exact
|
|
255
|
+
handle, incarnation and worktree before waiting up to 600 seconds for the
|
|
256
|
+
planned (or accepted) session to attach. The restore receipt is an explicit
|
|
257
|
+
tagged union: `pending`, `waiting` with `origin` (`pending` for a new launch, or
|
|
258
|
+
`failed`) and `binding`, `done` with `binding`, or `failed`. A missing receipt
|
|
259
|
+
means no launch has been attempted. Only the compatibility decoder reads released older
|
|
260
|
+
string/bare-binding receipts; new writes use the tagged states. The never-released
|
|
261
|
+
development attachmentReceipt marker is refused rather than inferred as a state. Interruption and
|
|
262
|
+
timeout retain the waiting receipt, and resume waits on that binding without
|
|
263
|
+
adopting a replacement.
|
|
264
|
+
An idle terminal alone is not attachment proof. Each poll checks the selected
|
|
265
|
+
launcher's signed identity. Only that exact launcher being gone and the target
|
|
266
|
+
reporting no attached session or other live launcher exits the wait: a
|
|
267
|
+
pending-origin wait records `failed` and exposes the failed-restoration choices;
|
|
268
|
+
a failed-origin wait returns to `failed` and retries the launch. A launcher
|
|
269
|
+
record not written yet keeps polling within the same bound. A signed ended row
|
|
270
|
+
for a different handle is not a current record for the chosen terminal. An
|
|
271
|
+
attached session with no id keeps that same wait after all older-launcher
|
|
272
|
+
checks; an offline saved id does not count as attachment or bypass those
|
|
273
|
+
checks. A current record arriving between observations continues polling only
|
|
274
|
+
when its handle, incarnation and worktree match the saved binding. Unknown
|
|
275
|
+
records and live replacements still block. An unreadable launcher identity, changed
|
|
276
|
+
terminal binding or unreadable target blocks without recording failure.
|
|
277
|
+
Successful attachment records `done` after exact terminal/session revalidation.
|
|
278
|
+
A `done` receipt only revalidates its saved binding; an exited launcher cannot
|
|
279
|
+
turn it into `failed` or offer a fresh session. No terminal is
|
|
280
|
+
created while a recorded launcher for the peer may run on the target or its
|
|
281
|
+
session is attached.
|
|
257
282
|
A launch that failed, or a thread found not resumable before the launch, is
|
|
258
283
|
receipted `restored:<peer>` = `failed` and never counts as restored. A Codex
|
|
259
284
|
store that cannot be read (other than missing) is unknown and blocks the plan,
|
|
@@ -268,7 +293,7 @@ Status. `ahub recovery status <id>` reports the receipt (phase, step, update
|
|
|
268
293
|
time, error); whether a runner process holds the operation now (`runner.state`
|
|
269
294
|
`running` with its pid, or `none`); `stale` when the receipt says `running` but
|
|
270
295
|
no runner holds it; each project's phase and effect receipts (`closed:<peer>`,
|
|
271
|
-
`restored:<peer>` as `done`, `pending` or `failed`); whether the shared plugin
|
|
296
|
+
`restored:<peer>` as `done`, `pending`, `waiting:pending`, `waiting:failed` or `failed`); whether the shared plugin
|
|
272
297
|
and the global CLI were installed; any lost continuity and disposition; and
|
|
273
298
|
`next`, the commands that apply now. It never contains task or message text.
|
|
274
299
|
`resume` does not start a second runner while one is alive.
|
|
@@ -439,9 +464,9 @@ An attached session is the target's report of that peer online with a thread
|
|
|
439
464
|
| project `prepared`, no `commitSent`, operation of a #215 coordinator | source stopped (crashed before any commit request) | blocks; the phase stays `prepared` (nothing was committed, so there is nothing to start from); not offered | abort if no effects, dispose | `<c> recovery abort <id>` without effects, else stop-and-archive (the lock refuses starting that hub by hand) |
|
|
440
465
|
| project `prepared` | source unavailable, starting, stopping or not inspected | as above when it reads again | none: abort (its hold may still stand) and stop-and-archive are refused until it reads | `<c> recovery status <id>` once it answers |
|
|
441
466
|
| project `prepared`, operation of an older coordinator (no `commitSent` written) | source not running, or not inspected | as its own runner does | dispose once it reads running, stopped or missing; abort neither offered nor accepted (it may have committed) | `<c> recovery status <id>` |
|
|
442
|
-
| project `started` or `peers-restored` | target stopped (a crash or reboot after it started), this operation's snapshot kept | records the restart (dead instance, time), retires the dead instance's done `restored:<peer>`
|
|
467
|
+
| project `started` or `peers-restored` | target stopped (a crash or reboot after it started), this operation's snapshot kept | records the restart (dead instance, time), retires the dead instance's done and waiting `restored:<peer>` bindings, moves the project back to `stopped` and starts the target from the snapshot; once per resume | dispose | `the target stopped after it started; resume restarts it from this operation's snapshot` (`stopped again after restart <n> in this resume` when it stops again) |
|
|
443
468
|
| project `started` or `peers-restored` | target stopped, snapshot gone | blocks; not offered | dispose | `this operation's restart snapshot is gone, so nothing can start it` |
|
|
444
|
-
| `retired:<peer>` (a done restoration of a dead target instance) | Orca lists its terminal, or cannot be read | closed under the close receipt rules (`closedRetired:<peer>` `pending` until Orca no longer lists it); an unreadable inventory changes nothing | dispose | `close Orca terminal <handle> by hand` or `check it in Orca`, `then <c> recovery resume <id>` |
|
|
469
|
+
| `retired:<peer>` (a done or waiting restoration of a dead target instance) | Orca lists its terminal, or cannot be read | closed under the close receipt rules (`closedRetired:<peer>` `pending` until Orca no longer lists it); an unreadable inventory changes nothing | dispose | `close Orca terminal <handle> by hand` or `check it in Orca`, `then <c> recovery resume <id>` |
|
|
445
470
|
| project `stopped` | target stopped without this operation's snapshot (never committed) | blocks; not offered | dispose | `the target is stopped and this operation's restart snapshot is gone` |
|
|
446
471
|
| project `stopped`, `started` or `peers-restored` | target running unfenced, as another instance or another version | blocks; not offered | dispose | `daemon is not owned by this recovery operation` (or `daemon instance changed`, `target daemon version mismatch`) |
|
|
447
472
|
| any, at staging | preserved source or staged target changed, or (first staging of these bytes only) target protocol or recovery waivers refused | blocks; recorded as `final`, so resume is never offered again | abort if no effects, dispose | the refusal |
|
|
@@ -451,7 +476,14 @@ An attached session is the target's report of that peer online with a thread
|
|
|
451
476
|
| `closed:<peer>` done | peer attached again | blocks | dispose | `end that <peer> session, then <c> recovery resume <id>` |
|
|
452
477
|
| `restored:<peer>` `pending` or `failed` | session attached with the planned id (or an accepted new one: fresh choice, planned fresh start, zero-turn Claude) | recorded as restored | none: `--fresh-session` is neither offered nor accepted while the inspection shows the planned session attached; when it cannot tell (not inspected, target unreadable), the `failed` receipt alone decides, and a chosen fresh session whose original then attaches is cleared on resume while its audit entry stays | none |
|
|
453
478
|
| `restored:<peer>` `pending` or `failed` | another session attached | blocks | dispose | `end that <peer> session and close its terminal, then <c> recovery resume <id>` |
|
|
454
|
-
| `restored:<peer>` `pending` or `failed` | no session,
|
|
479
|
+
| `restored:<peer>` `pending` or `failed` | no session, verified live launcher of the current target | records `waiting {origin, binding}` with the exact binding, waits up to 600 s for authenticated attachment; timeout retains both for resume | dispose | `attachment not verified within 600 s; terminal <handle> retained; resume after it attaches` |
|
|
480
|
+
| `restored:<peer>` `pending` or `failed` | no session, a live launcher belongs to an older target instance | blocks, retains the original receipt; no terminal created | dispose | end that launcher and close its terminal, then `<c> recovery resume <id>` |
|
|
481
|
+
| `waiting {origin, binding}` | the planned or accepted session attaches | runs the ordinary settlement rules, writes a waiver for an accepted replacement or clears `fresh` when the planned session returns, exactly revalidates the selected terminal and clears the waiting origin | none while the planned session is attached | none |
|
|
482
|
+
| `waiting {origin, binding}` | selected launcher gone, but an older target instance's launcher remains live or unknown | blocks and retains the selected binding and waiting origin; no failure or retry is inferred | dispose | end or verify the older launcher, then `<c> recovery resume <id>` |
|
|
483
|
+
| `waiting {origin, binding}` | selected exact signed launcher gone, target confirms no session or other live launcher | pending origin (including a new launch): records `failed` and blocks; failed origin: records `failed` and retries the launch | fresh session (Codex, Claude), dispose | the failed-restoration choices below |
|
|
484
|
+
| `waiting {origin, binding}` | selected identity unreadable, replacement terminal, or target unreadable | blocks, retains exact binding and waiting origin; never adopts a replacement or treats unknown as gone | dispose | verify the saved launch or target, then `<c> recovery resume <id>` |
|
|
485
|
+
| `waiting {origin, binding}` | no current launcher record (including a confirmed-ended other-handle row), with no session or an id-less attachment and no older live/unknown launcher | polls within the same 600 s bound; timeout retains waiting | fresh-session only for waiting:failed (subject to ordinary choices), dispose | `waiting:<origin>`; resume uses the same exact binding |
|
|
486
|
+
| `done {binding}` | launcher gone or no session attached | blocks exact restoration verification, retains done and its resumable conversation; never launches again or records failed | dispose; no fresh-session | resume the recorded conversation; `done` |
|
|
455
487
|
| `restored:<peer>` `pending` | no session, launcher gone or never recorded | receipted `failed`, blocks | fresh session (Codex, Claude), dispose | the failed-restoration choices below |
|
|
456
488
|
| `restored:<peer>` `failed` | no session, launcher gone or never recorded | launches again (fresh if chosen) | fresh session (Codex, Claude), dispose | the failed-restoration choices below |
|
|
457
489
|
| `restored:codex` absent | rollout missing | receipted `failed`, blocks, no terminal created | fresh session, dispose | the failed-restoration choices below |
|
|
@@ -463,6 +495,48 @@ An attached session is the target's report of that peer online with a thread
|
|
|
463
495
|
| disposition recorded, not finished | any | refused | stop-and-archive only | `rerun <c> recovery dispose <id> --stop-and-archive --reason <text>` where `disposeRefusal` allows it, else `wait until <project>'s hub settles, then <c> recovery status <id>` |
|
|
464
496
|
| `completed` or `cancelled` | any | nothing to do | none | none |
|
|
465
497
|
|
|
498
|
+
The restore transition function is `restoreTransition` in
|
|
499
|
+
`src/cli/upgrade-restore-state.ts`. Its generated table below is checked byte for
|
|
500
|
+
byte by `test/upgrade-restore-state.test.ts`; the Cartesian test separately
|
|
501
|
+
checks all 540 receipt/evidence/choice cells. `settle` and `revalidate` propose a
|
|
502
|
+
state only: the runtime commits it after authenticated session and exact
|
|
503
|
+
terminal validation. Fresh-session eligibility is shared by status, interactive
|
|
504
|
+
operation lines and refusal messages: only `failed` and `waiting:failed` expose
|
|
505
|
+
it, further narrowed by Pi, an existing choice, a planned fresh start, target
|
|
506
|
+
waiver support and the planned session already being attached.
|
|
507
|
+
|
|
508
|
+
Restore transition table (generated).
|
|
509
|
+
|
|
510
|
+
Cells show action -> next receipt status. Fresh-session eligibility is marked fresh; peer/operation-specific restrictions still apply. Exact terminal revalidation and settlement must succeed before persisting a proposed done receipt.
|
|
511
|
+
|
|
512
|
+
| Priority condition | Decision |
|
|
513
|
+
|---|---|
|
|
514
|
+
| Target stopped or changed | block -> unchanged |
|
|
515
|
+
| Running target, other session not allowed | block -> unchanged |
|
|
516
|
+
| Absent receipt | unstarted; initial launch is handled by the runtime outside this receipt table |
|
|
517
|
+
|
|
518
|
+
Running target, no attached session (either allowOther value):
|
|
519
|
+
|
|
520
|
+
| Receipt | live | gone | unknown | no-record-yet | replaced | older-instance |
|
|
521
|
+
|---|---|---|---|---|---|---|
|
|
522
|
+
| pending | poll -> waiting:pending | fail -> failed; fresh | block -> pending | fail -> failed; fresh | block -> pending | block -> pending |
|
|
523
|
+
| waiting:pending | poll -> waiting:pending | fail -> failed; fresh | block -> waiting:pending | poll -> waiting:pending | block -> waiting:pending | block -> waiting:pending |
|
|
524
|
+
| waiting:failed | poll -> waiting:failed; fresh | create -> failed; fresh | block -> waiting:failed; fresh | poll -> waiting:failed; fresh | block -> waiting:failed; fresh | block -> waiting:failed; fresh |
|
|
525
|
+
| done | block -> done | block -> done | block -> done | block -> done | block -> done | block -> done |
|
|
526
|
+
| failed | poll -> waiting:failed; fresh | create -> failed; fresh | block -> failed; fresh | create -> failed; fresh | block -> failed; fresh | block -> failed; fresh |
|
|
527
|
+
|
|
528
|
+
Running target, planned session or explicitly allowed other session (different decisions are labeled):
|
|
529
|
+
|
|
530
|
+
| Receipt | live | gone | unknown | no-record-yet | replaced | older-instance |
|
|
531
|
+
|---|---|---|---|---|---|---|
|
|
532
|
+
| pending | settle -> done | settle -> done | settle -> done | settle -> done | settle -> done | settle -> done |
|
|
533
|
+
| waiting:pending | settle -> done | settle -> done | block -> waiting:pending | settle -> done | block -> waiting:pending | block -> waiting:pending |
|
|
534
|
+
| waiting:failed | settle -> done | settle -> done | block -> waiting:failed; fresh | settle -> done | block -> waiting:failed; fresh | block -> waiting:failed; fresh |
|
|
535
|
+
| done | planned: revalidate -> done; other: block -> done | planned: revalidate -> done; other: block -> done | planned: revalidate -> done; other: block -> done | planned: revalidate -> done; other: block -> done | planned: revalidate -> done; other: block -> done | planned: revalidate -> done; other: block -> done |
|
|
536
|
+
| failed | settle -> done | settle -> done | settle -> done | settle -> done | settle -> done | settle -> done |
|
|
537
|
+
|
|
538
|
+
End of generated restore transition table.
|
|
539
|
+
|
|
466
540
|
The failed-restoration choices are `<c> recovery dispose <id> --fresh-session
|
|
467
541
|
<peer> --reason <text>` (Codex or Claude; records the lost session and resumes;
|
|
468
542
|
if the original session attaches after all, the recorded loss is cleared) and
|
|
@@ -490,7 +564,7 @@ replaces the record of a Codex already running.
|
|
|
490
564
|
#225: a target that stopped after it started is restarted only while its state
|
|
491
565
|
directory holds this operation's unreleased snapshot. The runner records the restart
|
|
492
566
|
in the project's receipt (`restarts`: the dead instance and the time) before acting,
|
|
493
|
-
moves each done `restored:<peer>` binding to `retired:<peer>`, clears the instance
|
|
567
|
+
moves each done or waiting `restored:<peer>` binding to `retired:<peer>`, clears the instance
|
|
494
568
|
and moves the project back to `stopped`, whose path starts the target from the
|
|
495
569
|
snapshot; the journal imports the snapshot only at revision 0, so the second start
|
|
496
570
|
continues from what the first wrote. `closed:<peer>`, `commitSent`, `fresh`, waivers
|