@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/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,45 @@
|
|
|
2
2
|
|
|
3
3
|
Issue and pull request numbers in the entries for 0.7.7 and earlier refer to the previous repository, archived on 2026-09-30 when this repository's history was rewritten; the one exception is the open smoke-check issue, formerly #12, which moved here as #1. Numbers in newer entries refer to this repository.
|
|
4
4
|
|
|
5
|
+
## Unreleased
|
|
6
|
+
|
|
7
|
+
## 0.12.31
|
|
8
|
+
|
|
9
|
+
After upgrade, Jcode projects need the one-time MCP rewrite below. Cancellation requires a daemon advertising `task_cancel`; controlled downgrades refuse while cancelled rows exist (an old binary started by hand remains outside that protection). The repository now requires the `seeded guards` check. Installed managed-Pi #418 AC8 (#142), cancellation/sweep #438 AC7 and the native legs in #273/#1 remain unverified by this release preparation. Installation and the person's restart require separate receipts.
|
|
10
|
+
|
|
11
|
+
- Retry an addressed uncertain delivery once, retaining message-only lineage evidence and excluding shared hub presence; delivery-only local/ACP peers leave resolution to the person. Run `ahub jcode --mode headless` once per Jcode project after upgrade, before conductor or dashboard starts of Jcode, to update its owned MCP entry (#448, #434).
|
|
12
|
+
- Add cause-deduplicated stalls, durable unread pull-only offer attention, a guarded ready-unassigned sweep and revision-checked cancellation that preserves history without approving work or releasing dependents; refuse cancellation on older daemons and controlled downgrade with cancelled rows (#460, #438).
|
|
13
|
+
- `seeded guards` is now a required check (repository setting after #458). CI runs its four shards beside both OS checks, reads Bun from `.bun-version`, updates pinned actions and covers actions with Dependabot; the release gate refuses tags not on `origin/main` and separates OIDC npm publication from GitHub Release creation (#458, #457).
|
|
14
|
+
- Wait for a pending managed terminal's authenticated session with explicit pending/waiting/done/failed restoration receipts and exact terminal fences, preserving completed conversations and the bounded no-duplicate-launch rules (#435, #433).
|
|
15
|
+
- Record agent-mem co-development and the claude-mem capability freeze in project instructions; new portable memory functionality belongs in agent-mem and the hub keeps its exact pin (#436).
|
|
16
|
+
- Safely return unseen Claude pushes, settle only provably moot hub-only holds, preserve stop notices and shared presence, show every hold and identify inherited agent markers in refusals (#440, #434).
|
|
17
|
+
- Follow new Events rows while at the tail, preserve a row selected for reading and add Home/End or </> navigation to console panels (#442, #441).
|
|
18
|
+
- Add bounded authenticated task-spec reads and opt-in isolated exact-head container checks; non-PII local/Pi implement, test and bulk_edit owners need available checks, and issue-backed owners in every class need spec access. Designated reviewers are exempt from these owner gates; PII work remains local-only without the opt-in (#443, #418).
|
|
19
|
+
- Instruct pull-only Kimi sessions to check the hub between work steps and share blockers or changed contracts through the addressed hub tools (#444).
|
|
20
|
+
- Open the dashboard with console `w`, the pet window with `o`, or the allowed `:ui` and `:pet` commands (#453, #452).
|
|
21
|
+
- Keep waiting through late launcher records and id-less attachments, ignore offline session ids and use the operation's preserved-source coordinator when an older CLI mislabels tagged recovery receipts (#454, #433).
|
|
22
|
+
- Designate an exclusive planner with the live `planner` setting, choose one automatically when needed and display or change it through status, console `l`/`y` and the dashboard (#456, #455).
|
|
23
|
+
- Keep explicit GitHub issue references gated without a GitHub origin, clear warnings after receipt-directory removal and preserve unknown check payloads under the documented retention exception instead of unsafe deletion (#459, #418).
|
|
24
|
+
- Resume an already-committed source through transient connection loss or typed database contention within the existing 30-second shutdown bound, without duplicate prepare, terminal close or commit; retain replacement and pending downgrade fences (#461, #438).
|
|
25
|
+
|
|
26
|
+
## 0.12.30
|
|
27
|
+
|
|
28
|
+
Memory now uses the exact-pinned first-party `@staix/agent-mem` 0.3.0. Do not migrate a memory home until every hub using it runs 0.12.30; run `ahub memory profile-check`, then quiesce and close all consumers before changing profiles. Shared v1 stays the default, with no automatic data migration.
|
|
29
|
+
|
|
30
|
+
- Consume the extracted agent-mem library, retain hub privacy policy, and preserve the exact dependency beside recovery sources so upgrades remain self-contained (#412, PR #423).
|
|
31
|
+
- Bind the selected shared-v1 or generation memory profile, stop memory features on invalid or changed selection without stopping the bus, and add person-only profile-check readiness receipts (#412, PR #430).
|
|
32
|
+
- Drop automatic approval noise from recall, keep complete words and memory headers, and deduplicate restored presence blocks and note references per delivery (#412, PR #419).
|
|
33
|
+
- Let sandboxed peers search, show and graph screened project memory through bounded, privacy-aware hub tools (#412, PR #420).
|
|
34
|
+
- Add advisory fail-first plan briefs, structured fail notes, person-only decay review and file-level staleness hints without changing stored evidence (#412, PR #425).
|
|
35
|
+
- Record screened review failures and agent corrections as machine-local candidates, plus rendered/delivered/cited counters kept outside recall, briefs and graph evidence (#412, PR #426).
|
|
36
|
+
- Add revision-checked task assignment/unassignment and queue completion, retry and discard controls in the console, preserving person-unassigned work across automatic routing (#424, PR #427).
|
|
37
|
+
- Notify a former owner to stop after supersession, while requiring that peer's own acceptance since its latest assignment before treating an unread task as received (#424, PR #429).
|
|
38
|
+
- Wait for restored managed peers to attach before preserved-session verification; retain terminal receipts and bounded timeout diagnostics so resume creates no second terminal (#414, PR #415).
|
|
39
|
+
- Separate portable memory storage from hub privacy policy and platform integration as the seam for the agent-mem package (#412, PR #421).
|
|
40
|
+
- Freeze the read-only memory baseline, 60-case failure registry and outstanding live-leg ownership before the memory changes (#412, PR #417).
|
|
41
|
+
- Rewrite the README around current capabilities, direction, installation and the documentation map, with operational detail in the operations guide (#412, #418, PR #422).
|
|
42
|
+
- Move website source and deployment to the separate agent-hub-site repository while keeping the public site address (#411, PR #413).
|
|
43
|
+
|
|
5
44
|
## 0.12.29
|
|
6
45
|
|
|
7
46
|
- Let an explicitly authorized agent run live verification through `ahub --live-test`, with operational console authority scoped to the invocation and its ahub continuations, the initiating peer retained in a mandatory delegation audit, and ordinary agent permissions unchanged (#408).
|
package/README.md
CHANGED
|
@@ -1,237 +1,111 @@
|
|
|
1
1
|
# agent-hub
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
hub-owned local
|
|
5
|
-
task
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
3
|
+
A hub on one developer's machine where coding agents work as a team: Claude Code,
|
|
4
|
+
Codex, Kimi Code, Pi and a hub-owned local worker (Qwen Code and Jcode optional)
|
|
5
|
+
share a message bus and a task board per project and one memory on the machine,
|
|
6
|
+
each attached through its own native interface.
|
|
7
|
+
|
|
8
|
+
Status: 0.12.31, control protocol 16. Bun + TypeScript, one loopback daemon per
|
|
9
|
+
project. Site: [agent-hub.staix.net](https://agent-hub.staix.net).
|
|
10
|
+
|
|
11
|
+
## What it does today
|
|
12
|
+
|
|
13
|
+
- **Peers, not a pipeline.** Every agent can message the others, and the hub
|
|
14
|
+
delivers to each through its native surface (Claude channel plugin, Codex
|
|
15
|
+
app-server, ACP, Pi RPC or TUI). Messages are batched into digests, priorities
|
|
16
|
+
decide what interrupts a turn, and delivery is durable across restarts.
|
|
17
|
+
- **A shared task board.** Tasks are proposed, accepted, done and reviewed through
|
|
18
|
+
hub tools. `routing.toml` assigns owners by task class, roles split planning,
|
|
19
|
+
implementation and review, and a designated planner can be changed live.
|
|
20
|
+
Cause-deduplicated stalls and unread pull-only offers need attention; cancellation
|
|
21
|
+
closes work without approving it or releasing its dependents. Owners of
|
|
22
|
+
overlapping work are told what changed.
|
|
23
|
+
- **Shared memory.** Decisions, contracts, fails and findings go to the hub's own
|
|
24
|
+
store through the exact-pinned first-party `@staix/agent-mem` library (short-
|
|
25
|
+
and long-term tiers, a typed graph, git sync between machines),
|
|
26
|
+
next to optional claude-mem capture. Entries belong to their project unless
|
|
27
|
+
saved as shared, which makes them readable from every project on the machine.
|
|
28
|
+
Agents get memory as a recall preface, as briefs on a task, and through bounded
|
|
29
|
+
`hub_memory_search`, `hub_memory_show` and `hub_memory_graph` tools. Agents
|
|
30
|
+
can propose corrections; candidates stay outside evidence until a person acts.
|
|
31
|
+
- **Local and remote models.** Pi and the local worker run on DGX or local MLX
|
|
32
|
+
through an authenticated relay; Switchyard routes model calls by task in front
|
|
33
|
+
of a self-hosted OmniRoute gateway. Tasks flagged as PII stay on the on-prem
|
|
34
|
+
worker.
|
|
35
|
+
- **Guard rails.** The local worker and Pi run in a sandbox, and by default their
|
|
36
|
+
writes and shell commands wait for the person's approval; a permission mode
|
|
37
|
+
the person picks (`ask-when-needed`, `never-ask`) skips some or all prompts.
|
|
38
|
+
Budgets pause a peer before it runs out of quota, and recovery never replays
|
|
39
|
+
an effect whose outcome is unknown.
|
|
40
|
+
- **Operations.** A terminal console and a local dashboard, one hub per
|
|
41
|
+
repository or worktree with a machine-wide view, and `ahub upgrade` that carries
|
|
42
|
+
running sessions across versions. The console supports revision-checked task
|
|
43
|
+
assignment/unassignment and delivery-queue resolution. Peers with independent
|
|
44
|
+
tool turns can request one uncertain redelivery; the delivery-only local worker
|
|
45
|
+
and headless ACP peers leave held deliveries for the person to resolve.
|
|
46
|
+
|
|
47
|
+
## Where it is going
|
|
48
|
+
|
|
49
|
+
- **Curated memory** ([#412](https://github.com/STAIxBWLB/agent-hub/issues/412)).
|
|
50
|
+
The memory core now lives in [agent-mem](https://github.com/STAIxBWLB/agent-mem)
|
|
51
|
+
(`@staix/agent-mem`, with the `amem` CLI), which agent-hub uses as its one
|
|
52
|
+
exact-pinned first-party dependency. Next, memory becomes curated rather than
|
|
53
|
+
captured: compression, removal, correction
|
|
54
|
+
and failure-driven management, each shipped only after its contract and a
|
|
55
|
+
measurement.
|
|
56
|
+
- **Every peer able to do its share** ([#418](https://github.com/STAIxBWLB/agent-hub/issues/418)).
|
|
57
|
+
Bounded task-spec reads and opt-in isolated checks are implemented. Installed
|
|
58
|
+
managed-Pi verification (AC8) remains open; source and CI results do not certify
|
|
59
|
+
that live runtime.
|
|
60
|
+
- **Measured, not assumed.** Add-ons and memory are kept or dropped by controlled
|
|
61
|
+
comparisons ([#248](https://github.com/STAIxBWLB/agent-hub/issues/248),
|
|
62
|
+
[#258](https://github.com/STAIxBWLB/agent-hub/issues/258)).
|
|
63
|
+
|
|
64
|
+
## Install
|
|
65
|
+
|
|
66
|
+
Requires [Bun](https://bun.sh) 1.4.2 or later on PATH.
|
|
27
67
|
|
|
28
68
|
```bash
|
|
29
69
|
bun add -g @staix/agent-hub && ahub setup
|
|
30
70
|
cd <your project> && ahub init && ahub up
|
|
31
71
|
```
|
|
32
72
|
|
|
33
|
-
|
|
73
|
+
`ahub init --dry-run --json` shows what init would write first. While hubs run,
|
|
74
|
+
`ahub upgrade` moves them to the latest release.
|
|
34
75
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
76
|
+
After upgrading to 0.12.31, run `ahub jcode --mode headless` once in each project
|
|
77
|
+
that uses Jcode to refresh its owned MCP entry before conductor or dashboard starts of Jcode.
|
|
78
|
+
Cancellation needs a daemon advertising `task_cancel`; controlled downgrades
|
|
79
|
+
refuse while cancelled rows exist. For local/Pi implementation, opt in to an
|
|
80
|
+
available `checks.runner = "container"`; issue-backed owners also need authenticated
|
|
81
|
+
spec access. PII work and designated reviewers retain their separate exemptions.
|
|
82
|
+
See [release verification](docs/verification/2026-10-17-0.12.31.md) for the live legs
|
|
83
|
+
still open, including #418 AC8 and #438 AC7.
|
|
38
84
|
|
|
39
|
-
|
|
85
|
+
Before migrating a memory home with `amem`, upgrade every hub using that home to
|
|
86
|
+
0.12.30 or later, check `ahub memory profile-check`, then quiesce and close all memory
|
|
87
|
+
consumers. Shared v1 remains the default; installing this release does not migrate
|
|
88
|
+
data. See [memory profiles and rollout](docs/memory.md#selected-memory-profile).
|
|
40
89
|
|
|
41
|
-
|
|
42
|
-
the latest release, shows the plan for every running project, applies it on
|
|
43
|
-
one key and follows it: the operation starts the new hub, installs the Claude
|
|
44
|
-
plugin and the global CLI, and restores or restarts the agent sessions as the
|
|
45
|
-
plan says. The [operations guide](docs/operations.md#upgrade-and-crash-recovery)
|
|
46
|
-
has the details, and the one-time start from an older CLI. With no hub running
|
|
47
|
-
there is nothing to carry over: install the new version as above and run
|
|
48
|
-
`ahub setup`. Afterwards run `ahub init` in a project to refresh its managed
|
|
49
|
-
agent instructions.
|
|
90
|
+
## Documentation
|
|
50
91
|
|
|
51
|
-
- [Quickstart](docs/quickstart.md): install, first session,
|
|
52
|
-
- [Operations guide](docs/operations.md):
|
|
92
|
+
- [Quickstart](docs/quickstart.md): install, the first session, a working day
|
|
93
|
+
- [Operations guide](docs/operations.md): console, task flow, Pi and models, dashboard, projects, upgrade and recovery
|
|
94
|
+
- [Hub memory](docs/memory.md) and [agent-mem](https://github.com/STAIxBWLB/agent-mem)
|
|
53
95
|
- [Security notes](docs/security.md): what the hub defends and what it does not
|
|
54
|
-
- [Design spec](docs/specs/2026-09-19-agent-hub-design.md): how it is built and why
|
|
55
|
-
- [Smoke checklist](docs/smoke.md):
|
|
96
|
+
- [Design spec](docs/specs/2026-09-19-agent-hub-design.md): how it is built and why
|
|
97
|
+
- [Smoke checklist](docs/smoke.md): what has been verified against real agents
|
|
56
98
|
- [Changelog](CHANGELOG.md), [Contributing](CONTRIBUTING.md)
|
|
57
99
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
`ahub init --dry-run --json` lists missing project defaults, the managed AGENTS.md
|
|
61
|
-
block, legacy CLAUDE.md cleanup and gitignore updates before writing them.
|
|
62
|
-
The preview does not register the project.
|
|
63
|
-
|
|
64
|
-
`ahub claude|codex|kimi|pi --print-command` (also `--dry-run`) prints the native
|
|
65
|
-
argv, Claude session settings and environment variable names without launching.
|
|
66
|
-
It uses the normal command builders. Arbitrary user arguments, configured commands
|
|
67
|
-
and settings are redacted; runtime-assigned endpoints and session identities are
|
|
68
|
-
explicitly unresolved. It does not check executable or account readiness.
|
|
69
|
-
See [Security notes](docs/security.md).
|
|
100
|
+
`AGENTS.md` and [`docs/agent-notes/`](docs/agent-notes/) hold the rules coding
|
|
101
|
+
agents follow when they work on this repository.
|
|
70
102
|
|
|
71
|
-
##
|
|
103
|
+
## Origin
|
|
72
104
|
|
|
73
|
-
[
|
|
74
|
-
Claude
|
|
75
|
-
|
|
76
|
-
an N-peer message bus and attaches each agent through its native control surface.
|
|
105
|
+
The first idea came from [agent-bridge](https://github.com/raysonmeng/agent-bridge),
|
|
106
|
+
which lets Claude Code and Codex talk to each other. agent-hub has since grown
|
|
107
|
+
into a different system with its own goals and shares no code with it.
|
|
77
108
|
|
|
78
|
-
##
|
|
79
|
-
|
|
80
|
-
| Peer | Inbound | Outbound | Busy handling |
|
|
81
|
-
| --- | --- | --- | --- |
|
|
82
|
-
| Claude Code | channel plugin push (`notifications/claude/channel`) under `ahub claude`; otherwise tools-only, read with `hub_inbox` | MCP tools | queued by Claude Code, grouped on next turn; tools-only: queued at the hub |
|
|
83
|
-
| Codex | app-server `turn/start` (idle) / `turn/steer` (busy) | proxy intercepts `item/agentMessage` | steer or queue |
|
|
84
|
-
| Kimi Code | TUI: pull with `hub_inbox`; headless: ACP `session/prompt` | TUI: `hub_send`; headless: ACP `session/update` stream | TUI: hub queue with important-count hints; headless: queue (`turn.agent_busy`) |
|
|
85
|
-
| Pi | RPC or managed native TUI extension | hub-approved tools | queued until `agent_settled` |
|
|
86
|
-
| Local worker | hub-native agent loop | hub-native | hub-owned |
|
|
87
|
-
|
|
88
|
-
## Pi with DGX and MLX
|
|
89
|
-
|
|
90
|
-
Pi is optional. Install Pi separately and set `pi.enabled` to `true` in
|
|
91
|
-
`.agenthub/config.json`. Set `pi.auto_start` to `true` to start it with the hub.
|
|
92
|
-
Pi starts in its TUI by default; `ahub pi --headless`, or the start mode
|
|
93
|
-
setting (`ahub settings set peers.pi.start_mode headless`), asks for the
|
|
94
|
-
background peer instead. The project uses its existing OmniRoute connection for DGX;
|
|
95
|
-
`pi.dgx_coding` and `pi.dgx_fast` select the gateway's configured model IDs.
|
|
96
|
-
|
|
97
|
-
```bash
|
|
98
|
-
ahub models setup # prepare bounded Qwen3.5 4B MLX in an existing Ollama service
|
|
99
|
-
ahub models start # verify loopback Ollama/model readiness (no daemon spawn)
|
|
100
|
-
ahub models status
|
|
101
|
-
ahub pi --headless --backend auto
|
|
102
|
-
ahub say @pi "Read the project and summarize the next small implementation task"
|
|
103
|
-
ahub pi # its TUI in this terminal; an idle headless session moves into it
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
The default routing template assigns implementation, bulk edits and tests to
|
|
107
|
-
DGX, and summaries/triage to MLX. Pi and the existing local worker take priority
|
|
108
|
-
before cloud peers; planning and final review stay with Claude/Codex. Existing
|
|
109
|
-
projects keep their routing file, so add `pi` to the relevant `peers` lists and
|
|
110
|
-
set `pi_backend = "dgx"` or `"mlx"` for each class. PII tasks remain local-worker
|
|
111
|
-
only. `--backend dgx` or `--backend mlx` explicitly pins a Pi session's backend.
|
|
112
|
-
|
|
113
|
-
Pi connects to an authenticated relay with model aliases `dgx/coding`,
|
|
114
|
-
`dgx/fast`, and `mlx/fast`; upstream gateway credentials stay in the hub.
|
|
115
|
-
Its managed tools use the existing path guards, shell sandbox and terminal
|
|
116
|
-
approval flow. Keep `ahub console` open to approve write/edit/shell operations:
|
|
117
|
-
a request nobody answers expires, and two in a row end Pi's turn.
|
|
118
|
-
Tool-call receipts survive daemon restart; an interrupted operation with an
|
|
119
|
-
unknown outcome must be reconciled before repeating it. Cancelling a Pi turn
|
|
120
|
-
does not automatically hand its task to a cloud peer.
|
|
121
|
-
|
|
122
|
-
The `mlx/fast` alias now defaults to an externally managed Ollama MLX backend.
|
|
123
|
-
Install and start Ollama separately, with `OLLAMA_NO_CLOUD=1`,
|
|
124
|
-
`OLLAMA_NUM_PARALLEL=1`, `OLLAMA_MAX_LOADED_MODELS=1`, and a finite
|
|
125
|
-
`OLLAMA_KEEP_ALIVE` (five minutes is the tested setting). `models setup`
|
|
126
|
-
prepares `agenthub-fast-mlx:4b-8k` from `qwen3.5:4b-mlx`, with an 8K total
|
|
127
|
-
context and a 2K output cap. The relay budgets input separately at 6,000
|
|
128
|
-
estimated tokens and serializes local requests across project hubs.
|
|
129
|
-
|
|
130
|
-
`models status` distinguishes model availability from memory residency.
|
|
131
|
-
`models stop` refuses to unload an externally managed Ollama model: other
|
|
132
|
-
clients may be using it. Ollama owns idle eviction; stopping a hub never
|
|
133
|
-
kills Ollama. The OpenAI compatibility endpoint does not enforce a request
|
|
134
|
-
`keep_alive` field, so configure the server and verify its expiry readback.
|
|
135
|
-
The relay can fall back to DGX before streaming starts as before. See the
|
|
136
|
-
[Ollama migration procedure](docs/operations.md#ollama-mlx-migration-issue-51)
|
|
137
|
-
for existing Python runtime/configuration cleanup and rollback.
|
|
138
|
-
|
|
139
|
-
A project has one managed Pi session owner. Mode changes require the agent to
|
|
140
|
-
settle and preserve the session file. Before the first generation, a live source
|
|
141
|
-
verified to be empty can instead retain its exact session ID without a file.
|
|
142
|
-
Missing history from a nonempty session blocks handover. Protocol 9 adds Pi
|
|
143
|
-
recovery metadata. Protocol 10, released in 0.7.0, adds durable delivery
|
|
144
|
-
receipts and conservative queue recovery; it is verified live (see the
|
|
145
|
-
[smoke checklist](docs/smoke.md)).
|
|
146
|
-
Older protocol hubs still require their matching CLI and an attended bootstrap.
|
|
147
|
-
|
|
148
|
-
## Runtime
|
|
149
|
-
|
|
150
|
-
Bun + TypeScript. Single daemon per project directory, loopback only.
|
|
151
|
-
|
|
152
|
-
## Local dashboard
|
|
153
|
-
|
|
154
|
-
With a running daemon, `ahub ui` opens a local dashboard for messages, peer states
|
|
155
|
-
and queues, the task board, budget windows, pending approvals and this machine's
|
|
156
|
-
benchmark runs. Use `ahub ui --no-open` to print a single-use link when the
|
|
157
|
-
browser cannot be opened.
|
|
158
|
-
Open the link within 60 seconds; the browser session lasts one hour.
|
|
159
|
-
|
|
160
|
-
The dashboard can send console messages, propose and assign tasks, pause or resume
|
|
161
|
-
peers, answer approvals, start a peer in its start mode and show the settings.
|
|
162
|
-
An ordinary session changes the safe switches and lowers a mode to `ask`; the
|
|
163
|
-
rest needs `ahub ui --settings` or `ahub settings` in a terminal. See
|
|
164
|
-
[Settings](docs/operations.md#settings-from-the-dashboard-and-the-terminal) and
|
|
165
|
-
[Start modes](docs/operations.md#start-modes-tui-by-default-headless-as-opt-in).
|
|
166
|
-
Budget pauses still require the terminal override.
|
|
167
|
-
Private envelopes and PII tasks stay redacted. Local-worker and Pi tool details and
|
|
168
|
-
allow decisions stay in `ahub tail` / `ahub permit`; the page can deny them.
|
|
169
|
-
No dashboard port is opened until requested. See [the session security model](docs/security.md#local-dashboard-sessions-issue-6).
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
## Multiple projects and worktrees
|
|
173
|
-
|
|
174
|
-
Version 0.4.0 adds one independent hub per repository or Git worktree.
|
|
175
|
-
Subdirectory commands resolve to that project; symlink aliases resolve to the same
|
|
176
|
-
hub. Explicit selection works from any directory:
|
|
177
|
-
|
|
178
|
-
```bash
|
|
179
|
-
ahub --project /path/to/project-a init
|
|
180
|
-
ahub --project /path/to/project-b init
|
|
181
|
-
ahub --project /path/to/project-a up
|
|
182
|
-
ahub --project /path/to/project-b up
|
|
183
|
-
ahub projects
|
|
184
|
-
ahub status --all
|
|
185
|
-
ahub --project /path/to/project-a codex
|
|
186
|
-
ahub ui --all
|
|
187
|
-
```
|
|
109
|
+
## License
|
|
188
110
|
|
|
189
|
-
|
|
190
|
-
messages, approvals and budgets. It can start hubs and stop a selected hub after
|
|
191
|
-
confirmation. Its Start control opens a peer's TUI only through a terminal provider
|
|
192
|
-
(`terminal.open` or Orca); without one, launch Claude/Codex TUIs from a terminal. The manager stays available
|
|
193
|
-
when a project stops; `ahub ui --all --stop` stops only the manager. Existing
|
|
194
|
-
`ahub ui` still opens the current project's dashboard.
|
|
195
|
-
|
|
196
|
-
`ahub projects --json` returns machine-readable live status. Use a listed ID or a
|
|
197
|
-
path with `--project`; the selector must precede the command. `ahub projects remove
|
|
198
|
-
<id>` forgets a stopped registration without deleting project data. A moved root
|
|
199
|
-
gets a new registration. Missing roots remain visible until removed.
|
|
200
|
-
|
|
201
|
-
Runtime state stays in each project's `.agenthub/state/`. The machine registry is
|
|
202
|
-
`~/.agenthub/registry.db`; existing `ports.json` allocations are imported once.
|
|
203
|
-
`AGENTHUB_HOME` selects an isolated machine registry (also used by tests). An
|
|
204
|
-
inherited state directory from a different project cannot redirect a new CLI
|
|
205
|
-
invocation. Explicit custom state requires a matching project marker or the
|
|
206
|
-
initialization pair `AGENTHUB_PROJECT_DIR` / `AGENTHUB_STATE_DIR`.
|
|
207
|
-
|
|
208
|
-
Manager-started hubs use attended mode and project configuration, including
|
|
209
|
-
credential-file references. They do not inherit the launching project's gateway
|
|
210
|
-
URL/key overrides. Native memory remains shared according to claude-mem aliases;
|
|
211
|
-
worktrees recall their parent and composite identity. A shared-alias notice does
|
|
212
|
-
not mean that task boards or agent sessions are shared. Provider quota readings
|
|
213
|
-
may describe the same account and should not be added across projects.
|
|
214
|
-
|
|
215
|
-
Before upgrading an older hub, inspect the plan with the matching CLI.
|
|
216
|
-
Incompatible or unverified processes are shown explicitly and are never killed by
|
|
217
|
-
PID-name matching. See the [operations guide](docs/operations.md) and the
|
|
218
|
-
[multi-project specification](docs/specs/2026-09-19-multi-project-design.md).
|
|
219
|
-
|
|
220
|
-
## Agent instructions
|
|
221
|
-
|
|
222
|
-
`AGENTS.md` holds what every agent session loads: commands, conventions,
|
|
223
|
-
architecture and the rules that hold anywhere in the code. Rules that hold in one
|
|
224
|
-
area (benchmarks, adapters, the bus, the local worker, models, the task board,
|
|
225
|
-
budget, the daemon and recovery, tests) live in [`docs/agent-notes/`](docs/agent-notes/),
|
|
226
|
-
and `AGENTS.md` names the paths that call for each note, so a session loads only
|
|
227
|
-
the notes for what it edits (#158). Background behind some of those rules:
|
|
228
|
-
|
|
229
|
-
- Benchmark trust entry: the review rounds of #115 kept finding paths that broke
|
|
230
|
-
the runner's settlement of the Claude trust entry.
|
|
231
|
-
- Reply addressing: leaving `to` empty fanned a reply out to every peer, which is
|
|
232
|
-
what made one directed question cost every agent a turn.
|
|
233
|
-
- Bun test hang: the stacks of two local hangs show a test timeout starting the next
|
|
234
|
-
test inside `Bun.spawnSync`'s own event loop (#115,
|
|
235
|
-
`docs/verification/2026-10-03-0.12.6.md`); minimal reproductions did not hang, so
|
|
236
|
-
the full trigger is not pinned down. On Bun 1.4.2 the next test still starts there
|
|
237
|
-
(#121, checked directly); the spin was not reproduced on demand on either version.
|
|
111
|
+
MIT
|
|
@@ -3,3 +3,15 @@
|
|
|
3
3
|
Human explanation. Agent instructions: the area notes in this directory.
|
|
4
4
|
|
|
5
5
|
On 2026-10-13, two PR #368 reviews found that a missing delivery id or a missing visible row could select another Queue row. The console navigation rule in daemon.md records the exact-row contract. The missing-row and ID-less secondary-delivery regression tests demonstrate both cases.
|
|
6
|
+
|
|
7
|
+
Background behind some of the rules, moved from the top-level README on 2026-10-16:
|
|
8
|
+
|
|
9
|
+
- Benchmark trust entry: the review rounds of #115 kept finding paths that broke
|
|
10
|
+
the runner's settlement of the Claude trust entry.
|
|
11
|
+
- Reply addressing: leaving `to` empty fanned a reply out to every peer, which is
|
|
12
|
+
what made one directed question cost every agent a turn.
|
|
13
|
+
- Bun test hang: the stacks of two local hangs show a test timeout starting the next
|
|
14
|
+
test inside `Bun.spawnSync`'s own event loop (#115,
|
|
15
|
+
`docs/verification/2026-10-03-0.12.6.md`); minimal reproductions did not hang, so
|
|
16
|
+
the full trigger is not pinned down. On Bun 1.4.2 the next test still starts there
|
|
17
|
+
(#121, checked directly); the spin was not reproduced on demand on either version.
|
|
@@ -9,7 +9,8 @@ Scope: the Codex, ACP and Pi adapters and the Pi extension (turn `generation` an
|
|
|
9
9
|
- Codex 0.154.0 `agentMessage` items carry `text` and `phase` (not `content[]`); only the last non-`commentary` message of a turn is shared.
|
|
10
10
|
- Approval titles are agent-written text shown to a person: the daemon escapes control characters, and write/edit/bash show what will be written or run.
|
|
11
11
|
- An ACP permission request may carry no `rawInput` (Kimi 2.0.1): the arguments were on the earlier `tool_call` update, which `acp.ts` remembers by `toolCallId`. Kimi 2.1.1 sends no `rawInput` before the answer at all: the argument JSON streams as `content` text on `tool_call_update`, and only complete JSON counts. A payload that still cannot be resolved, or is too long to show whole (marked `[cut, N chars]`), withholds its `allow_always` option - never render a bare tool name as if it described the call.
|
|
12
|
-
- Tag a Pi hub-tool refusal only at a proven pre-effect guard with `PreEffectToolRefusal`; return its exact error and settle its receipt. Never classify a generic/post-effect/storage failure or a legacy pending receipt from error text or current board state.
|
|
12
|
+
- Tag a Pi hub-tool refusal only at a proven pre-effect guard with `PreEffectToolRefusal`; return its exact error and settle its receipt. Never classify a generic/post-effect/storage failure or a legacy pending receipt from error text or current board state. After durable `markStale`, cancellation or output consumption, retain ordinary failure classification; a later binding/privacy rejection cannot prove that no effect occurred. Recheck task revision, owner/session generation and privacy across awaited bounded spec/check operations; never turn those tools into arbitrary host execution.
|
|
13
|
+
- Hide `hub_delivery_retry` from delivery-only ACP/local tool lists and omit its retry hint. Treat `AGENTHUB_DELIVERY_ONLY` only as an exposure hint; the daemon must refuse by the actual attached adapter, and native children must not inherit the marker. A Claude push that cannot reach its model reports `failed_safe`; if the daemon refuses that optional receipt state, try `needs_review` and retain uncertainty when neither receipt is confirmed.
|
|
13
14
|
- Report each Pi owner exit once, with code/signal (unknown for an unobserved TUI exit), turn/tool activity and only the last tool name. Auto-restart only a current, started, unexpectedly idle headless owner on its verified persisted session, once per 60 s; preserve shutdown/requested-stop/recovery/startup/active-tool fences and never fall back fresh.
|
|
14
15
|
- Pi's `always` grant (#209) is keyed by `executeTool`'s tool name, never parsed from the approval title, and lives only in that Pi start's closure. Never persist it, share it with the local worker, or let the dashboard give it.
|
|
15
16
|
- Auto-approval covers the hub's own tools by exact `mcp__agent-hub__<name>` title and only ever picks `allow_once`. Identity comes from the permission request title or, when an agent titles the request with the argument JSON (Qwen), from the announced `tool_call` title bound to the call id and resolved against the session's configured MCP servers, never from payload text, a prefix or an unrelated display title. Never widen it to a prefix or replace it with an agent-selected session mode. Set Kimi `yolo`/`auto` only for its verified native identity and an operator mode; config never-ask waits for a console y, and other ACP agents stay unmanaged on fresh/resumed starts without setting/resetting modes or failing over their reported mode; refuse runtime changes until their map exists; await the advertised mode change after session new/load and before a prompt, and fail startup on refusal. Use the shared `KIMI_MODE_IDS` mapping in `src/hub/permission-mode.ts`.
|
|
@@ -24,7 +25,7 @@ Scope: the Codex, ACP and Pi adapters and the Pi extension (turn `generation` an
|
|
|
24
25
|
- A peer must set `busy` synchronously inside `deliver()`, otherwise the bus drains the next envelope into a running turn (Kimi answers `turn.agent_busy`). Outside a turn the adapter accepts an occupancy diagnostic (a `context-used` `usage_update` naming its current session) and nothing else: no token total, no chunk, nothing unnamed or for an offline peer (#285).
|
|
25
26
|
- A watchdog-cancelled turn still reports later. Anything a turn does on completion must check it is still the current turn (`turn` generation in `acp.ts`, `activeTurns` in `codex-appserver.ts`). A rejected prompt other than a recognised quota refusal, or an abnormal prompt end, reports through the adapter's `onTurnFailure` (same shape in `acp.ts` and `pi.ts`), even when ACP streamed partial text; a stale cancelled turn never reports it; a rejected watchdog-cancelled ACP prompt during its journal-less wait is discarded with one log line, not requeued as on the earlier adapter.
|
|
26
27
|
- Native owner teardown must handle a lost shutdown acknowledgement using verified process identity. Pending launches must be revocable, and active tools/streaming output must count as watchdog activity.
|
|
27
|
-
- Pi owner identity
|
|
28
|
+
- Keep Pi owner identity on the shared `processSignature`/`processLiveness` contract exported by the exact-pinned package's Node-safe `@staix/agent-mem/platform` entry, re-exported unchanged by `src/pi/process-signature.ts` for the startup claim, extension claim, TUI owner monitor, `recoveryReady` and verified teardown. Memory's `sync.lock` uses the same implementation. Preserve the Node-safe process-entry regression in `test/memory-parity.test.ts`, the existing Pi process behavior tests, the LC_ALL=C/TZ=UTC `ps` read and strict pid + start + command identity; never accept PID-only or a fuzzy command match.
|
|
28
29
|
- Return the shared `toolResultFailed` verdict as `/tool`'s `failed`; map only `failed === true` to Pi's `isError`. Preserve text and the older bridge's absent-flag behavior. The one exception is a person's `!command` whose process exit was observed: its `failed` is `exitCode !== 0` and the reply carries `exitCode`, from `onProcessExit` through `packPiShellResult`, never parsed from the output (#253).
|
|
29
30
|
- Pi's bridge executes `/tool` only for the verified owner (`verifiedOwner()`: the claimed pid with its `processSignature`; a headless owner is pinned from the spawned process once `get_state` proved its session) with the current `sessionId` and turn `generation`, and admits `/budget` only for that owner and generation, checked again after the awaited admission. Refuse anything else before execution. `tool_abort` aborts that pending call, or fences its id when it arrives before the call (64 ids a turn, then every further call of that turn). A person's `!command` (`purpose: "idle_user_bash"`) is taken only in TUI mode from an idle owner on its one-use reservation, and it alone skips `piPermit`: never widen that to a model's bash call (#253).
|
|
30
31
|
- Pi and local approvals (#253): `permit` answers a `ToolApproval` (contract: `local-worker.md`) and `requestPermission` (`src/hub/daemon.ts`) tells the adapter's `ApprovalObserver` what settled it. An expiry counts toward the stop only as `source: "expired"` with `eligibleExpiry` not false (no person answered a request of that peer while it was pending); `person` resets the count, `automatic` (a mode grant, Pi's `always`) never does, and a new turn starts it over. The second one ends the turn, never the peer: local throws `ApprovalWaitStop`, Pi sends `abort_budget` with `cause: "approval"`, and both settle the delivery `completed` with the reason; a Pi whose extension did not announce `approvalTurnAbort` goes offline with the delivery held. `requestPermission` deletes a request from `permissions` before it runs an observer, resolves or reports, and an aborted signal closes it as `cancelled`: a late answer must find nothing to grant, and a failed event, notice or console push never strands the caller.
|
|
@@ -10,7 +10,7 @@ Scope: quota and native context readings, checkpoints, pause, handoff and resume
|
|
|
10
10
|
- The status line tee must never fail or slow the render: no throw, original command run with the same stdin, 5 s cap.
|
|
11
11
|
|
|
12
12
|
- Keep native context occupancy in `src/hub/context-window.ts` separate from quota accounting; never use Codex's accumulated `total` as occupancy or estimate Pi counters.
|
|
13
|
-
- Context checkpoint completion requires its request id and the current attached peer/native session. Invalidate its transport generation synchronously at a new peer claim, before asynchronous recall or attachment. Never pause, reassign or replace a session on context pressure. Refuse private-turn, PII-pattern and all
|
|
13
|
+
- Context checkpoint completion requires its request id and the current attached peer/native session. Invalidate its transport generation synchronously at a new peer claim, before asynchronous recall or attachment. Never pause, reassign or replace a session on context pressure. Refuse private-turn, PII-pattern and all associated open PII-task summaries (owner or reviewer, including in_review, with the current routing policy; use `isClosedTaskState` to exclude approved and cancelled work) before local persistence or cloud memory; never broadcast checkpoint text.
|
|
14
14
|
- Invalid/stale context readings expose unknown and do not rearm a crossing. Latch a crossing only after its event is accepted; reconsider paused/recovery-held crossings after release with fresh current-session readings. Tee context fields contain finite numeric values or null only, bound to daemon instance, launcher and native session.
|
|
15
15
|
|
|
16
16
|
- A provider hard refusal ends a reading override and supersedes a soft evaluation still awaiting its checkpoint; never let the obsolete checkpoint overwrite that pause. An ACP provider quota refusal reports a hard budget reading and confirms the live pause before the adapter requeues work, so the pause fences retries and the existing relay handles assignments. Use the observed window length for an unknown reset; repeated quota refusals while paused do not consume poison-envelope retries.
|
package/docs/agent-notes/bus.md
CHANGED
|
@@ -14,8 +14,14 @@ Scope: delivery, digests and condensation, reply addressing, priority and limits
|
|
|
14
14
|
- A reply is addressed, never broadcast. An adapter that answers a delivery passes `to: replyAudience(envs)`; anything else with an `inReplyTo` inherits that envelope's sender. Leaving `to` empty fans the message out to every peer.
|
|
15
15
|
- `digest` is not a peer: addressing a reply at the envelopes the peer was handed sends it nowhere once a delivery was condensed. The bus resolves `digest` back through `lastDelivery.originals`; anything else that reads a reply's `to` has to do the same.
|
|
16
16
|
- A pull-only peer (a Claude session without channel pushes or Kimi in its own TUI) is never drained; `Bus.pull` hands it what one push would take. Drain and pull share `held()` (recovery, an uncertain delivery, any pause, the lifecycle `deliveryHeld` fence) and `remember()` (so `reply_to` resolves), and a drain rechecks `deliverable()` after condensing because such a session can take the peer meanwhile. A pull is journaled `completed` in the same transaction as the bus snapshot, never `accepted`; a failed one puts back only that peer's queue and preface and never reloads the snapshot.
|
|
17
|
+
- Record exact task-offer reads only through `DeliveryJournal.completeDelivered` on an active-fenced adapter completion, correlated reply or actual pull, atomically with completion. Capture original offers before terminal pruning and record their ids, never condensed ids; accepted receipts, generic transitions, operator/hub dispositions and restored completed rows are not read evidence.
|
|
17
18
|
- The `onImportantQueued` nudge for a pull-only peer (#398) is advisory: it never dequeues, steers, journals or settles, and a throw from it never stops an enqueue.
|
|
18
|
-
- `
|
|
19
|
+
- Merge recall and restored prefaces through `mergePrefaces`/`uniqueMemoryLines`; preserve distinct context and new notes, deduplicate memory identities, and keep colliding short prefixes distinct. Use `recallDisplayText`/`recallMemoryLines` before display caps: suppress only matching automatic approval outcomes, retain nonempty rejection details, preserve id headers and complete-word excerpts, and keep token trimming at line boundaries. Keep these changes display-only.
|
|
20
|
+
- Observe memory rendering from final `out`, not `originals`, and suppress telemetry when either contains private envelopes. A pull emits rendered/delivered observations only after its journal transaction commits; observer exceptions must never change delivery settlement.
|
|
21
|
+
- Classify automatic held-row completion only through `supersededDeliveryReason`: every original must be a hub presence or a provably moot hub task/review notice. Unknown/live/mixed rows stay held; keep `deliveryTaskContext` display-only. Tag only current-owner notices as owner audience; leave former-owner withdrawals untagged until the task closes. For cancelled tasks, settle earlier workflow notices but retain the stop notice whose `refs.taskCancelled` matches the cancellation revision. Preserve the requested-stop exclusion in `daemon.md`. Record the hub actor through `resolveDelivery`, restoring presence originals through `restorePreface` only for hub completion (person completion stays consumed); run on hold entry, restore, recovery release and board changes, never replay uncertain work. List every hold once in status, console notices and route explanations (#434).
|
|
22
|
+
- `resolveDelivery` mutates queues in place; a refused or failed resolution puts back only that peer's queue and preface and keeps every pause. Never reload a snapshot on a live bus. Keep the selected delivery id and journal revision fixed in console row actions; retry/discard/completed never change a task role or cancel a native turn.
|
|
23
|
+
- Do not offer recipient retry to delivery-only local/ACP transports or pass ordinary messages through their hold; their hints direct the person to resolve. Keep current-adapter refusal authoritative and the ephemeral MCP exposure marker advisory; pull-only Kimi TUI retains retry.
|
|
24
|
+
- Route peer retry only through `retryDelivery`: scope explicit-retry history by peer and immutable original ids across all grouped parents, commit monotone consumed-proof inheritance for every grouped non-context original with the checkpoint while keeping resolution audit rows unchanged; exclude hub `presence` originals from every retry proof, warning, inheritance and unknown-marking path, preserve unknown imported lineage fences without disabling fresh work, and admit busy-peer consent only to requeue its own `needs_review` row. Decorate rendered clones after condensation/pull/steer, never originals; reserve the shared journal body cap for the warning and an explicit Unicode-safe cut marker. Keep held hints bounded, body-free and advisory; metadata failure never changes a saved tool result or refusal class (#434).
|
|
19
25
|
- `restore` takes queued work from the journal, never from the committed snapshot once the journal has state (an empty journal, revision 0, is seeded from the commit): from the commit only an empty queue key is carried over, for an offline peer whose key no delivery ever persisted (#234). Never resurrect a non-empty queue from it.
|
|
20
26
|
- The journal's owner check (`rejectLiveOwner` in `delivery-journal.ts`) judges the manifest pid with `processLiveness` and its `pidSignature`, never a bare probe (rule: `daemon.md`).
|
|
21
27
|
- A hub-native peer (Pi, the local worker) does not get to call its own message `important`: `capPriority` caps it unless the delivery it answers held an `important` envelope addressed to it. Check the delivery, not `replyParent`, which ties on hop and takes the later item. Do not bypass it by setting `priority` in the adapter.
|
|
@@ -14,15 +14,20 @@ Scope: the operator console, control WS and its protocol, state files, the dashb
|
|
|
14
14
|
- `ahub setup` reads Claude Code's state from `--json` listings and takes one step at a time, re-reading after each; never match paths or names by substring.
|
|
15
15
|
- A copy of the git index keeps the original's mtime (`snapshot()`): git's racy-entry check compares entries with the index file's time in whole seconds, and a fresh copy makes a same-size edit look clean.
|
|
16
16
|
- State files that clients read (`status.json`, `control-token`) are written after the port is bound, and `status.json` via temp file + rename.
|
|
17
|
+
- Open native memory through the shared profile-aware helper with an explicit current selector binding; never fall back to shared v1 after invalid/missing generation metadata. Check that binding at hub call-site entry and before candidate/counter writes, including callbacks retained across awaits; do not intercept store methods with proxies/private hooks or claim atomic in-flight cutover. Quiesce and close consumers before migration/selection changes, then reopen through the supported lifecycle. Keep the sidecar at the independent memory root, not inside the selected generation. Report memory availability separately from bus health; never hot-rebind or migrate as a startup repair (`docs/memory.md` §Selected memory profile).
|
|
17
18
|
- The channel's reconnect loop stops on closes a retry cannot fix (`TERMINAL_CLOSES` in `claude-channel.ts`); a new daemon close code that means "do not come back" belongs there, or two clients fight over it forever.
|
|
18
19
|
- Close 4000 is the one close that ends on its own, so it is not in `TERMINAL_CLOSES`: the session stands by and reconnects only once `status.json` reports the peer offline and not `claiming`. One exception (#205, `peerHeld` in `claude-channel.ts`): a session with channel pushes also reconnects when the holder is reported `toolsOnly` and not `claiming`, and takes the peer; the daemon refuses the reverse with 4000. Reconnecting blind would evict whoever holds the id now, and the two would trade it forever. `claim()` happens at hello and `attach()` only after the preface, so the peer reads as offline in between: the daemon writes the status file at the claim and the flag covers that window.
|
|
19
20
|
- Adding a native peer requires testing the command emitted by the actual recovery driver, not only its inspection or launch helpers. Never let a generic non-Codex branch treat a new peer as Claude.
|
|
20
21
|
- Cross-version recovery must use the authenticated source protocol for prepare/commit/abort and the target protocol after startup. Prove the transition against a real prior release before claiming compatibility.
|
|
22
|
+
- Preserve `task_cancel` target-feature preflight before source holds, capture or commit. Reuse stored target features only after verifying the same retained root and digest; legacy receipts still capable of commit must obtain feature proof. Do not apply pre-commit compatibility checks to sources already past commit.
|
|
23
|
+
- Resume a prepared `commitSent` source that is unavailable or database-busy inside the existing bounded shutdown poll; never re-send commit, re-prepare or close terminals again on that path. Require confirmed `stopped` before target startup, refuse a replacement instance, and retain the prepared receipt on timeout.
|
|
24
|
+
- Keep restoration receipts explicitly tagged as pending, waiting with origin/binding, done with binding, or failed. Project waiting origins as `waiting:pending` and `waiting:failed`; preserve the chosen binding and origin across interruption instead of inferring completion from an object.
|
|
21
25
|
- A zero-turn Claude session has no transcript, so it can never be resumed: every identity gate that compares session ids (restore, coordinator verify, daemon readiness) must tolerate a fresh session while the transcript is absent and stay strict the moment one exists. The commit snapshot records `sessionPersisted` for the restored daemon, and snapshots too old to have it are re-derived from disk. Never treat "session id changed" as proof of a lost conversation without checking the transcript first.
|
|
22
26
|
- The terminal-recovery launcher signature is the shared pinned `processSignature` (`src/pi/process-signature.ts`): a record written by one CLI invocation must compare equal when a later invocation re-reads the live launcher under another TZ/LC_ALL (#177). In-process `process.env` mutation does not reach Bun.spawnSync's default child environment, so cross-environment regressions are tested with real subprocesses. A row written by 0.12.12 or older carries the unpinned signature, which `launcherState` also compares (#228). A launch-record row that fails validation is unknown for the peer it may belong to and is written back unchanged, never filtered out (#228).
|
|
23
27
|
- Judge every recorded owner pid (hub manifest, registry claim, recovery runner, manager owner, launcher record) with `processLiveness` in `src/pi/process-signature.ts`, never a bare `process.kill(pid, 0)`: after a reboot a recorded pid can belong to another process, even one we may not signal, and the bare probe wedged recovery and `ahub up` (#226). A writer records `processSignature(process.pid)` beside its pid; a registry claim stores it as `<instance id> <signature>` because an older daemon's claim rewrites the pid but not that column. Records without a signature keep the bare-pid answer; unknown is never gone.
|
|
24
28
|
- A restored daemon accepts a changed native session id (Kimi and local aside: they restart as fresh workers) in two ways only: the #64 rule for a Claude session that never persisted a transcript, and the operation-fenced `recovery-waivers.json`, which the coordinator writes wherever it accepts a session other than the planned one: a reconnect-only Claude (#206), an operator-chosen fresh session, the plan's `freshStart` of a Codex thread the hub saw start with no turn, or a zero-turn Claude it accepted (#215). Never waive on any other evidence. Re-preparation after an expired lease keeps every effect receipt, and `restored:<peer>` = `failed` never counts as restored. Judge a restoration launcher by its `terminal-recovery.json` record and process signature (and a `terminal_handle_stale` answer), never by the terminal's exit; an unreadable record is unknown, never "none".
|
|
25
|
-
- A target that stopped after it started is restarted only through `recordRestart` (`src/cli/upgrade.ts`), recorded before the new one starts and at most once per project per resume: each done `restored:<peer>` becomes `retired:<peer>`, whose terminal is closed (`closedRetired:<peer>`) or found absent before that peer is relaunched, and is never revalidated against the new instance. Launcher evidence is read for every instance in `restarts`, and a session attached without an id is unknown, never gone (#225).
|
|
29
|
+
- A target that stopped after it started is restarted only through `recordRestart` (`src/cli/upgrade.ts`), recorded before the new one starts and at most once per project per resume: each done or waiting `restored:<peer>` binding becomes `retired:<peer>`, whose terminal is closed (`closedRetired:<peer>`) or found absent before that peer is relaunched, and is never revalidated against the new instance. Launcher evidence is read for every instance in `restarts`, and a session attached without an id is unknown, never gone (#225).
|
|
30
|
+
- After a managed terminal is created, persist its binding before waiting up to `RECONNECT_WAIT_MS` for authenticated attachment to the expected target instance, operation and conversation (#414). Resume waits on the saved binding without creating another terminal. Count only attached, non-offline session ids; when the chosen terminal has no current launch record, a confirmed-ended row for another handle or an id-less attachment retains the bounded wait after older-launcher checks. A late current record must match the saved terminal; unknown or live replacement evidence still blocks. Timeout keeps the receipt blocked and records only terminal/hub metadata; a last native connect attempt that was never recorded is unknown, never inferred from terminal readiness or offline state. Final identity and preservation verification stays strict.
|
|
26
31
|
- An operation receipt stays `schema: 1`. Only `abandoned()` writes 2, on an operation with a recorded `disposition` whose coordinator predates #215: in the same write as the disposition, or, for a disposition 0.12.20 recorded at 1, by the next command that holds the runner claim. `validateReceipt` accepts 2 only with a disposition (#227). Never write 2 on an operation that can still be resumed.
|
|
27
32
|
- Three checks read another release's source text for an exact line: `coordinatorCurrent` (`export async function disposeRecovery` in `src/cli/upgrade.ts`), `targetReadsWaivers` (`export function readRecoveryWaivers` in `src/hub/restart.ts`) and `coordinatorRestarts` (`export function recordRestart` in `src/cli/upgrade.ts`). Renaming, moving or un-exporting one makes this release read as an older one: keep those lines until a capability list replaces the checks.
|
|
28
33
|
- The interactive screens (`src/cli/upgrade-interactive.ts`, #272) change an operation only by running an existing command (`recovery resume|abort|dispose`, `reset`) through `io.run`, and build their menu from `nextChoices`, whose text is `nextActions`: never call `abortRecovery`, `disposeRecovery` or a driver from a screen, and never offer a choice `next` does not have. The one addition is `cancellableWait` (a runner still at `prepare` with no effect): read the receipt again at the key press, stop only a runner `signedRunner` names (a claim without a matching process signature is never signalled), then run `recovery abort`, which decides again. Agents are ended (`endPlannedPeer`) only before an operation holds the lock, and only after the lock, the hub instance, the attached session and the terminal's handle and incarnation were read again: never on the plan as it was drawn. Ctrl+C leaves the screens and the terminal is not read after it; a prompt answers an interrupt with null, never text. What a hub says it waits for (`recovery.waiting`, the daemon's `blockers`) is shown, never decided on.
|
|
@@ -39,3 +44,11 @@ Scope: the operator console, control WS and its protocol, state files, the dashb
|
|
|
39
44
|
- Use `KNOWN_PEERS` for the native catalog and `rosterAllows` for participation. Change live membership only at the authorized roster settings write or undo, applying exactly that key. Keep external ids and projects without roster settings compatible with the existing admission and lazy status rules. Fence starts and attachment before detaching or stopping a removed runtime; retain its queue, uncertain deliveries and independent pauses. Refuse removal of the conductor holder and require explicit confirmation and `--now` for an active turn.
|
|
40
45
|
|
|
41
46
|
- A Kimi one-launch TUI admission ends on its owning detach, roster removal, matched launcher cleanup or the unclaimed 30 s deadline. An old socket or launcher never clears a newer admission. Pin --mode tui in the hub-opened terminal argv.
|
|
47
|
+
|
|
48
|
+
- When preserving a recovery source, copy only the exact-pinned `@staix/agent-mem` package through `preserveRuntimeDependencies` in `src/cli/recovery-package.ts`; include its identity and bytes in `packageDigest`. Never copy the whole node_modules tree or link a preserved source to a mutable installation. Verify preserved sources with `ownDependencies: true` so a missing or symlinked memory copy cannot resolve an ancestor installation. Verify staged releases with `dependencyRoot` bound to their retained install directory; never resolve beyond it. Keep no-dependency release digests compatible.
|
|
49
|
+
|
|
50
|
+
- Render staleness only through the call-scoped privacy preflight and bounded metadata probe (`memory-staleness.ts`); on a tree not modified during the call, private/denied/observed-symlink/unknown paths never produce a marker. Retain the explicit same-user concurrent-swap ceiling in docs/memory.md and the probe-loop ponytail. Keep hints out of stored rows and route native brief decoration after selection so one batch shares its probe budget.
|
|
51
|
+
|
|
52
|
+
- Observe memory counters only through the machine-local candidate sidecar. Bind delivered counters to a positive journal acknowledgement and the original render context; skip private envelopes and current PII tasks/turns. Leave missing telemetry unknown, and never pass candidates or counters to evidence consumers. Require memory_correct for correction proposals, refuse PII turns, and keep project/identity authority server-side.
|
|
53
|
+
|
|
54
|
+
- Keep full memory home, generation ID, selector revision and malformed-row totals in authenticated person status/CLI receipts, never disk status.json or agent CLI output; low-privilege receipts use a generation prefix and omit home, selector revision and malformed-row totals. Emit typed unbound disabled/unavailable receipts with no profile when no memory handle opened; profile-check may treat an authenticated same-home generation-capable unbound daemon as memory-quiesced, never a stale bound handle. Scope profile-check readiness to its named registry inventory; old, unknown or unreadable receipts refuse readiness, and other custom registries need separate checks.
|
|
@@ -5,9 +5,9 @@ Scope: the hub-native local worker, its tools, path guard, denylist and seatbelt
|
|
|
5
5
|
- Nothing the local worker executes may run outside `sandboxedExec`; a new tool that spawns a process goes through it, and a tool that touches a path goes through `guardPath`.
|
|
6
6
|
- The sandbox denies home reads by default (toolchain dirs, the project and its real git dir excepted) and all network, loopback included: claude-mem and the Codex app-server listen on loopback without auth. Tests that bind a local port therefore fail when the worker runs them, also with `local.bash_network: true` (the egress proxy keeps loopback closed, and the proxy variables send a test's plain-HTTP `fetch` to the proxy, which refuses it); that is the intended trade-off, and `"direct"` is the switch until 0.13.0 removes it.
|
|
7
7
|
- The seatbelt profile refuses the `.git` name itself at any depth (creation, rename, symlink, gitfile), `config` and `hooks` directly under it, `commondir` at any depth below it or below an external git dir, and `.agenthub` anywhere in the project, every name folded against the 16 code points HFS+ ignores (#270). A submodule's git dir under the common dir's `modules/` tree gets the same `config`/`hooks` refusal at the depth submodules nest, every term anchored below the discovered modules root so ancestor directories named `refs`/`config`/`hooks` neither disable nor trip the rule, and a `/refs/` or `/logs/` segment below it excludes the path so refs named `config` or `hooks` stay writable (#281); the discovered git dirs, the modules root and the components in between are also refused as directory-entry targets (rename, replace, symlink, recreate). Known residuals: a submodule whose own path holds a whole `refs` or `logs` segment (a substring, `catalogs` included, keeps the refusal — the exclusion matches whole components only), a nested repository in bare layout (the hub never runs git there; docs/security.md), folded spellings of the components between `.git` and a submodule's git dir on a volume that folds names (the 1024-byte SBPL literal cannot hold a folded path; external git dirs carry the same residual since #270), and the submodules of a repository nested inside the project (only the project's own common dir is discovered). An external git dir's `config`/`hooks` rules cover only its own children plus the hooks directory's contents: at any depth they would deny refs named `config` or `hooks` in a worktree or submodule project. The fold is a group alternation, never a character class (the seatbelt regex engine is byte-oriented and a quantifier after a multi-byte class member binds to its last byte), and every pattern keeps its `^` anchor (an unanchored starred alternation does not match). Folded literals never contain the project root: an SBPL string literal dies at 1024 bytes. The cost: `git init`, `git clone` and `git worktree add` do not run inside the sandbox, and neither do `git worktree remove`, `git worktree prune` of a stale record, `git clean -ffd` with a nested repository, `git submodule update --init` (its git dir's `config` write is refused), or `rm -rf` of a directory holding a `.git`.
|
|
8
|
-
-
|
|
8
|
+
- Build every git invocation outside the sandbox through the single implementation exported by exact-pinned `@staix/agent-mem/platform` (`memoryGitArgv`/`memoryGitEnv`/`memoryGitSync`), re-exported as `hubGitArgv`/`hubGitEnv`/`hubGitSync` by `src/hub/git.ts`. Keep the git export identity, behavior and direct-spawn tripwire checks in `test/hub-git.test.ts` passing; retain home compatibility and Node-safe process-entry checks in `test/memory-parity.test.ts`. The sandboxed git tool in `src/local/tools.ts` retains its own behavior. Use `{ credentials: true }` only for the memory repository the hub creates itself, never a project's repository. Use the core deny policy re-exported by `src/local/deny.ts` for guards, sandbox rules and capture; do not add another denylist.
|
|
9
9
|
- SBPL strings go through `sbplString` (the plain `"..."` form). In the raw `#"..."` form a backslash escapes nothing, so a `"` in a path ends the literal and the whole profile fails to parse.
|
|
10
|
-
- One denylist, `src/local/deny.ts`: the path guard, the seatbelt profile and the memory filter all read it. Seatbelt sees absolute paths, so `local.deny` entries are anchored under the project root (a bare `private/` once denied all of `/private/var`).
|
|
10
|
+
- One denylist, exported by `@staix/agent-mem` and re-exported by `src/local/deny.ts`: the path guard, the seatbelt profile and the memory filter all read it. Seatbelt sees absolute paths, so `local.deny` entries are anchored under the project root (a bare `private/` once denied all of `/private/var`).
|
|
11
11
|
- `guardPath` walks with `lstat`: `existsSync` follows symlinks, so a dangling link looked like a new file and the write landed at its target.
|
|
12
12
|
- git arguments never pass through `guardPath`; `gitArgsProblem` refuses absolute paths, `..`, `--no-index` and denylisted `rev:path` forms.
|
|
13
13
|
- A worker turn builds its messages in a local array and joins the history only as a whole. Never push to `history` mid-turn: one tool call without its result poisons every later request.
|
|
@@ -17,3 +17,5 @@ Scope: the hub-native local worker, its tools, path guard, denylist and seatbelt
|
|
|
17
17
|
- A tool that asks for approval passes its name to `ctx.permit`, and `write`/`edit` also the `guardPath` result, never the model's path string: also pass the original path separately through every permit wrapper; `ask-when-needed` grants only when both the original project-relative spelling and the real path avoid protected names through `grantablePath` (`src/hub/permission-mode.ts`). `bash` and mutating `git` pass no path. After the awaited approval `write`/`edit` run `guardPath` again and refuse a target that changed (#242).
|
|
18
18
|
|
|
19
19
|
- Honor the live `protect_instruction_files` policy in scoped grants for the three instruction names; keep reads, explicit approval and never-ask unchanged. Only the settings registry writes this machine-local policy (#323).
|
|
20
|
+
|
|
21
|
+
- For sandboxed task checks, use the opt-in hub-owned bounded container runner; never widen Seatbelt, expose credentials, mount host paths or fall back to a host command. Keep spec/check arguments and results out of capture.
|
|
@@ -3,8 +3,14 @@
|
|
|
3
3
|
Scope: the task flow, assignment, the state machine and the hub's MCP tools. Read before editing `src/hub/tasks.ts`, `src/hub/board.ts`, `src/hub/routing.ts`, `src/hub/task-sweep.ts`, `src/hub/conductor.ts`, `src/hub/supervision.ts`, `src/hub/tool-refusal.ts` or `src/hub/hub-tools.ts`.
|
|
4
4
|
|
|
5
5
|
- Tool callers are models: MCP `inputSchema` is not enforced on the way in. Normalize at the boundary (`cleanRefs`) before anything reaches the board, and never throw after a board write: a publish or notify that follows a `review()` or `done()` board write goes through the one `releaseNotice` guard, which turns a failed publish into a console notice, drops a failed notify and never retries (#231, #243). An assignment's delivery once its board write is saved (#276, #297): the move is fact, the failed publish is not retried in place (the journal's own recovery covers a storage error, and a retry may duplicate a publish that actually left), and a person looks for a queued or needs_review delivery with `ahub queue list --peer <owner>` (then `ahub queue show <id>`, or the accepted entry in `ahub task show <id>` if the journal is down) before re-sending with `ahub task assign <id> <owner>`. The distinction is made once, in `assignOwner`: a failure after the write prints the one console notice from `undeliveredNotice` right there, once, and throws `AssignmentUndeliveredError` carrying the saved task; callers (`review()`'s repeated rejection, `escalate` and its callers, the ready offer, both relays, the idle sweep, rescreen) decide control flow by the type and print nothing more, and `propose`, `assignTo` and `decline` additionally let the error reach the caller in its turn, which is a person or peer that can act. Two exceptions get no notice, by construction: a row superseded meanwhile, meaning a later history entry that moves or settles the task (another assignment, an unassign, the owner's accept, done, approval, close), while entries that do neither (the hub's quiet bookkeeping: answers, reviewer changes, idle sweeps, check results) leave the assignment undelivered and the notice stands, so the original error goes to the caller as before only on supersession; and a claim's own accepted write and "claimed by" line stay outside the delivery rule (a claim is its own hand-over; its overlap and routing announcements to earlier owners are deliveries and share the wrapper, so a failure there does read as one). A failure before the write stays a plain `Error` (#254), a failed write leaves the ready offer unconsumed (#231), and the notices before the write (waits, no-owner, the reserved-owner line) go through `releaseNotice` too, so those pre-write notices cannot fail their caller.
|
|
6
|
+
- Post-await assignment publication is fenced by current role authority: supersession returns the current task without a saved-delivery notice; reviewer-only changes refresh current metadata and do not suppress a valid owner delivery. A replacement notice goes only to a role that actually received the request, not to an unread-only queue.
|
|
7
|
+
- Console task row assignment/unassignment carries the exact role, task id and `flowRevision` (history length), checks it before effects and passes it to `Board.update`; a changed revision requires reload. Person owner removal clears owner/reservation and suppresses automatic offers until a later explicit person assignment/reservation; refuse owner removal under review or after approval, and preserve the required PII person reviewer.
|
|
8
|
+
- Role withdrawal matches the task id, role kind and owner-request header. Owner acceptance evidence must be that peer's accepted entry after the latest ownership event, never another owner's state or an earlier assignment span. Withdraw unread requests without inventing delivery evidence. Preserve the original former-owner stop notice after a superseding escalation, while suppressing stale success/decision tails.
|
|
6
9
|
- `assign()` never defaults to the task's current owner, or a decline can only come back to the decliner. A reservation (#207) is the same risk: the reserved owner goes first only through `blocked()`, and a decline or an escalation for reason `rejections` or `manual` records the owner it left in the history entry's `from`, which `excluded()` reads, so no later reroute hands the task back through its reservation.
|
|
7
10
|
- The state machine allows `in_progress -> approved` only for classes without a reviewer; `review()` checks `in_review` itself.
|
|
11
|
+
- Treat `cancelled` as closed through `isClosedTaskState`, but keep dependency release restricted to approval. Cancellation goes through `Tasks.cancel`, preserves the row and roles, clears its reservation/cohort, sends revision-bound stop notices and writes no outcome memory; never map it to approval or cancel a native turn or worktree. Planner cancellation stays limited to its own historically unaccepted proposed/backlog work; accepted work remains person-only.
|
|
12
|
+
- Capture `TaskOffer.pullOnly` and the exact offer envelope id at publication. Keep unread-offer attention independent of attachment and restart until acceptance, decline, supersession or `offerRead` evidence; never replace that evidence with a generic inbox timestamp or completion state.
|
|
13
|
+
- Use the daemon's settled role map for planner authority and notice recipients. An explicit planner designation is exclusive; automatic fallback remains runtime-only. Keep ready-unassigned sweep admission consistent with person-unassignment suppression, planner-pull reservation/WIP limits and ordinary routing guards; persist each ladder step before its notice.
|
|
8
14
|
- `.agenthub/routing.local.toml` (#269) may hold only `stay_switch` and per-class `peers`, `escalate_to`, `route` and `pi_backend` (`checkOverlay` in `src/hub/routing.ts`): never widen it to targets, routes, signals or constraints, which stay in the hand-written `routing.toml`. `parseRouting` is the one merge, and `assign()` reads `routing.sources` only for its trace line.
|
|
9
15
|
- Conductor authority requires the explicit unique role before capability checks. The only exceptions run before that check in `Conductor.execute` and stay task-scoped: a task's current owner and reviewer read it with `hub_task_show` (#208), and its proposer redirects it with `hub_task_assign` while nobody ever accepted it and no person moved it (#207). Removing them strands reviewers that missed a review request. Assignments preserve the acting peer. A peer releases only its own conductor hold; operator release and manual, quota and recovery holds remain separate paths.
|
|
10
16
|
- The PII screen (#198) runs in `propose` before anything else reads the task (no board row, envelope, triage or brief before its verdict), and `unknown` is PII. A call that screens free text (`done`, `review`, `remember`) screens only after its own checks pass, and `done` and `review` run them again after the awaited screen, because the task can change meanwhile. A history entry marked `withheld` stays withheld in public views, completed notices and review requests, whatever `nameable()` says; `ahub ask` shows it only on campus, like a note that matches a PII pattern.
|
|
@@ -19,3 +25,11 @@ Scope: the task flow, assignment, the state machine and the hub's MCP tools. Rea
|
|
|
19
25
|
- A planner patch that names `reserved` at all, the same value included, is refused while `hasPersonReservation` holds; planner pull and grooming both ask it (#378: a same-value write laundered a person's reservation).
|
|
20
26
|
- `done()` gates the SDLC transition once: pass `gated` to `complete()` when nothing was awaited since, and after any awaited gate readback refuse if another done already moved the task, in every mode (#382: advise recorded overlapping dones).
|
|
21
27
|
- A changed `reviewedHead`, or one supplied with a changed repo or PR, needs the reviewer or a person; a head dropped because the owner relinked the PR does not (#382: the owner could not relink, and could carry a head to a new PR).
|
|
28
|
+
|
|
29
|
+
- Memory read tools (`hub_memory_search`, `hub_memory_show`, `hub_memory_graph`, #412) take their project only from the daemon. Validate UTF-8 input caps before logs or storage, require `memory_read`, refuse PII turns and exclude arguments/results from capture. Screen complete rows and graph metadata before counting or truncating; keep whole-result byte caps, complete id/provenance/kind headers and one neutral unavailable-id refusal.
|
|
30
|
+
|
|
31
|
+
- Compose structured `hub_remember` fail fields through `composeRememberText` before screening or writes; reject over 4000 UTF-8 bytes without truncation. Treat plan-fail matches as screened advisory priority only, never a state-transition guard; skip all plan-memory work for PII tasks.
|
|
32
|
+
|
|
33
|
+
- Keep candidate production in Tasks: screen review/correction payloads and current task/turn privacy before CandidateStore writes; recheck after asynchronous screening. Never make candidate failures roll back a saved review, or let a peer accept a candidate. Keep candidate tool arguments/results out of capture.
|
|
34
|
+
|
|
35
|
+
- Feed the same current spec/test capability map into every `assign()` path, explicit nomination and reservation. Apply these capability refusals only to non-PII owner admission, preserving reviewer and on-prem PII routing exemptions; refuse a missing required capability before a manual owner assignment write. Bind spec/check authority and result delivery to the current task, owner, peer generation and privacy policy.
|