@aeondave/pi-persona 1.17.3 → 1.17.4

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.
Files changed (84) hide show
  1. package/AGENTS.md +44 -36
  2. package/CHANGELOG.md +76 -1
  3. package/README.md +23 -13
  4. package/docs/ARCHITECTURE.md +97 -37
  5. package/docs/COLLABORATION.md +110 -0
  6. package/docs/EXPERIENCE.md +33 -8
  7. package/docs/README.md +12 -0
  8. package/docs/REFERENCE.md +65 -14
  9. package/package.json +12 -6
  10. package/src/agents/agent.ts +6 -0
  11. package/src/bridge.ts +25 -8
  12. package/src/bus/broker/client.ts +119 -21
  13. package/src/bus/broker/host.ts +123 -8
  14. package/src/bus/broker/messages.ts +31 -4
  15. package/src/bus/contact.ts +113 -38
  16. package/src/bus/peers.ts +15 -0
  17. package/src/bus/result-reader.ts +88 -0
  18. package/src/core/brief.ts +14 -4
  19. package/src/core/capabilities.ts +1 -1
  20. package/src/core/leg-thinking.ts +57 -0
  21. package/src/core/ownership.ts +1 -2
  22. package/src/core/pi-compat.ts +5 -12
  23. package/src/core/session-runs.ts +1 -1
  24. package/src/core/types.ts +1 -1
  25. package/src/engine/adapter.ts +83 -19
  26. package/src/engine/async.ts +49 -10
  27. package/src/engine/child.ts +10 -2
  28. package/src/engine/inproc.ts +87 -46
  29. package/src/engine/result-grant.ts +62 -0
  30. package/src/engine/session-tools.ts +6 -0
  31. package/src/engine/stream.ts +6 -0
  32. package/src/engine/worktree.ts +17 -7
  33. package/src/exocom/collaboration-delivery.ts +29 -0
  34. package/src/exocom/collaboration.ts +148 -0
  35. package/src/exocom/envelope.ts +43 -4
  36. package/src/exocom/gate.ts +7 -2
  37. package/src/exocom/install.ts +45 -17
  38. package/src/exocom/ledger.ts +58 -8
  39. package/src/exocom/plane.ts +32 -1
  40. package/src/exocom/profile.ts +40 -0
  41. package/src/exocom/registry.ts +5 -0
  42. package/src/exocom/status.ts +11 -1
  43. package/src/exocom/write-guard.ts +7 -0
  44. package/src/extension/broker-host.ts +61 -9
  45. package/src/extension/engine.ts +20 -21
  46. package/src/extension/hooks.ts +952 -938
  47. package/src/extension/identity.ts +41 -75
  48. package/src/extension/results.ts +136 -0
  49. package/src/extension/shared.ts +17 -0
  50. package/src/extension.ts +2963 -2873
  51. package/src/loader.ts +11 -3
  52. package/src/orchestration/flow-journal.ts +7 -1
  53. package/src/orchestration/flow-run.ts +122 -19
  54. package/src/orchestration/flow.ts +11 -0
  55. package/src/orchestration/roster.ts +71 -23
  56. package/src/orchestration/sdk.ts +44 -6
  57. package/src/orchestration/types.ts +10 -0
  58. package/src/persona/model-participants.ts +20 -7
  59. package/src/persona/orchestrate.ts +2 -0
  60. package/src/persona/persona.ts +1 -1
  61. package/src/results/archive.ts +281 -0
  62. package/src/results/record.ts +166 -0
  63. package/src/results/retrieval.ts +142 -0
  64. package/src/results/types.ts +128 -0
  65. package/src/telemetry/contract.ts +1 -1
  66. package/src/tools/council.ts +40 -23
  67. package/src/tools/delegate-tool.ts +50 -14
  68. package/src/tools/delegate.ts +13 -6
  69. package/src/tools/exocom-collaboration-input.ts +168 -0
  70. package/src/tools/exocom-collaboration.ts +315 -0
  71. package/src/tools/exocom-profile.ts +102 -0
  72. package/src/tools/exocom.ts +20 -3
  73. package/src/tools/flow.ts +24 -8
  74. package/src/tools/intercom-tool.ts +98 -14
  75. package/src/tools/models.ts +15 -0
  76. package/src/tools/monitor.ts +20 -1
  77. package/src/tools/read-result.ts +52 -0
  78. package/src/tools/timer.ts +23 -2
  79. package/src/ui/agent-overlay.ts +11 -3
  80. package/src/ui/agent-tree.ts +43 -1
  81. package/src/ui/literal-tool-result.ts +1 -1
  82. package/src/ui/model-picker.ts +1 -1
  83. package/src/ui/orchestration-launch.ts +49 -0
  84. package/src/ui/session-cards.ts +191 -14
package/AGENTS.md CHANGED
@@ -37,8 +37,13 @@ the shared behavioral prompt layer: [`docs/SPINE.md`](docs/SPINE.md).
37
37
  `@earendil-works/pi-*`; never bundle copies.
38
38
  - npm publishes as `@aeondave/pi-persona`. Pi supplies the four `@earendil-works/pi-*` packages
39
39
  and `typebox`: keep them as `peerDependencies: "*"` with development-only copies, never runtime
40
- dependencies. The supported host floor is Pi ≥ 1.0.0 (`MIN_PI_VERSION` and the README); Node.js
41
- must be ≥ 22.19 to match that host. Keep the npm
40
+ dependencies. Target the latest stable Pi only (currently 1.4.0, aligned with `MIN_PI_VERSION`,
41
+ the four development pins and README); do not maintain historical-host compatibility branches.
42
+ **No legacy or deferred cleanup:** remove dead code, obsolete options, superseded implementations
43
+ and compatibility-only aliases with their replacement. Update every caller, fixture and document
44
+ in the same change. A completed feature must not leave temporary scaffolding or cleanup debt.
45
+ Keep a path only when it serves the current host/runtime or a demonstrated current data contract.
46
+ Node.js must be ≥ 22.19 to match that host. Keep the npm
42
47
  `files` allowlist explicit for docs/artwork, inspect `npm pack --dry-run`, and never publish drafts.
43
48
  - Supply-chain controls: pin every GitHub Action to a verified full commit SHA; keep top-level
44
49
  token permissions read-only, grant write permissions only to the reporting job that needs them,
@@ -175,7 +180,7 @@ the shared behavioral prompt layer: [`docs/SPINE.md`](docs/SPINE.md).
175
180
  a live, capability-filtered roster (agents, teams and flows, with descriptions and gated flows tagged in
176
181
  headless sessions) + a hand-off default (with one decision-class → method line, checked against the
177
182
  registry) to the system-prompt TAIL every turn — discovery that survives context burn.
178
- - **Sibling peer comm (in-process)**: a strategy can opt a run into direct sibling messaging
183
+ - **Sibling peer comm**: a strategy can opt a run into direct sibling messaging
179
184
  (`AgentRunSpec.peers` — the `debate` strategy does). The child gets a `contact_peer` tool
180
185
  (`bus/peers.ts`): `list`/`send`, ONE-WAY only (blocking stays supervisor-only, so peers can
181
186
  never deadlock), peer list scoped per engine instance (never the whole bus), send budget 20,
@@ -183,7 +188,11 @@ the shared behavioral prompt layer: [`docs/SPINE.md`](docs/SPINE.md).
183
188
  Delivery: the in-process engine's bridge steers incoming bus messages into the child session,
184
189
  fenced with the sender attributed OUTSIDE the fence — the same bridge delivers the supervisor's
185
190
  `intercom send` (previously a dead letter). Gated by `EffectiveCapabilities.canUseBus` (OFF iff
186
- the persona explicitly denies `intercom`). The child engine ignores `peers`.
191
+ the persona explicitly denies `intercom`). Child-process legs use the broker's engine-scoped
192
+ peer roster; `contact_peer` is installed only when that run opts in and the bus is allowed.
193
+ Granted communication tools join a nonempty child tool allowlist; explicit empty lists and
194
+ tool exclusions still win. In-process supervisor contact additionally requires `coaching`;
195
+ broker-connected children expose it when the bus is allowed.
187
196
  Design: [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md#the-comm-plane-in-practice) + [`docs/STRATEGIES.md`](docs/STRATEGIES.md).
188
197
  On the peer plane: `debate` and `pair` always; `map`/`synthesize` opt-in via `params.peers`.
189
198
  `magi`/`judge`/`fanout` stay peer-less BY DESIGN — independence is their bias guard
@@ -191,38 +200,37 @@ the shared behavioral prompt layer: [`docs/SPINE.md`](docs/SPINE.md).
191
200
  this. `compete` runs its competitors with `isolation: worktree` (REQUIRES a clean git repo; missing
192
201
  repo/dirty checkout/worktree failure fails closed and never falls back to the real tree) and returns
193
202
  the winning diff for the SUPERVISOR to apply.
194
- - **exocom** (`src/exocom/*`, `src/tools/exocom*.ts`; opt-in `PI_PERSONA_EXOCOM=1` / `--exocom`,
195
- capability-gated): a plane apart from everything above — those are all INTERNAL to one supervisor's
196
- own run (hierarchical, session-keyed children); exocom is FLAT and EXTERNAL, between independent
197
- top-level pi instances sharing one selected scope, no parent/child relationship. Bare `--exocom`
198
- keeps the current workspace scope; exact `--exocom=Ab0T` joins that existing workspace's scope from
199
- another cwd through a persistent, collision-aware, case-sensitive four-character Base62 alias.
200
- The scope selects registry/transport/ledger/artifacts, while every peer advertises its actual home
201
- workspace id/code/safe label so external file sets are explicit. A foreign peer is advisory for
202
- writes: it can inspect its own files and use postcards/ask/answer/wait/progress/release, but
203
- `exocom_claim` is inactive and rejected because the ledger's paths are repository-relative to the
204
- scope workspace. The code is a same-host/same-agent-dir join reference, not authentication.
205
- Postcards remain one-way and non-blocking: `exocom_list`/`exocom_send`
206
- plus `exocom_name` (a reply is a send with `in_reply_to` set). The separate shared JSONL work
207
- ledger adds `claim`/`ask`/`answer`/`decline`/non-blocking `wait`/`progress`/`release`: overlapping
208
- open write sets NACK atomically, and a pending targeted ask gates that participant to answer/decline,
209
- read-only tools, and `exocom_name` identity metadata. Semantic frames are signed immediate-wake signals; the ledger is authoritative
210
- if delivery is deferred. This is cooperative runtime coordination, not OS authorization. All ten
211
- tools remain in `EXOCOM_TOOL_NAMES` (with `claim` withheld for a foreign member), targeted denies win independently, and joining requires
212
- `canUseBus` plus at least one callable closer (`answer` or `decline`) so a published peer cannot be
213
- wedged by an obligation it has no way to settle. If `exocom_name` is permitted, an unnamed top-level
214
- Pi is prompted to invent a task-derived call-sign on its first user or inbound peer turn
215
- (no catalog or extra model call). Naming is metadata permitted during pending asks, without
216
- settling them; a targeted deny suppresses the prompt. Session identity is shared with standalone
217
- `agent_name` and persisted across resume/persona changes. Until a handle is chosen, its actionable
218
- naming bootstrap is delivered through the `context` hook because custom Exocom wakes can bypass
219
- `before_agent_start`; after selection the hook stays silent. The session-hash suffix of a qualified target survives renames and telemetry resolves the
220
- same canonical session.
221
- Reuses the broker's wire framing
222
- (`bus/broker/framing.ts`) and the
223
- core fence (`fenceUntrusted`/`attributeInbound`) — no new trust-sensitive code path; inbound
224
- attribution is resolved from the registry, never the envelope's self-reported `from_name`.
225
- Design: [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md#exocom--the-external-plane).
203
+ - **Exocom** (`src/exocom/*`, `src/tools/exocom*.ts`; opt-in `PI_PERSONA_EXOCOM=1` / `--exocom`;
204
+ see [the collaboration guide](docs/COLLABORATION.md)): a flat external plane between independent,
205
+ top-level Pi sessions, separate from one supervisor's internal workers. Bare `--exocom` selects the
206
+ current workspace; exact `--exocom=Ab0T` joins that existing scope from another cwd through a
207
+ persistent, case-sensitive four-character Base62 alias. The scope selects registry, transport,
208
+ ledger and artifacts; each member still advertises its actual home workspace. The code is a
209
+ same-host/same-agent-dir join reference, not authentication.
210
+ `EXOCOM_TOOL_NAMES` contains 15 call-time-gated tools: `exocom_list`, `exocom_send`, `exocom_name`,
211
+ `exocom_claim`, `exocom_ask`, `exocom_answer`, `exocom_decline`, `exocom_wait`, `exocom_release`,
212
+ `exocom_progress`, `exocom_status`, `exocom_task`, `exocom_offer`, `exocom_publish`, and
213
+ `exocom_profile`. A joined foreign-workspace peer cannot use `exocom_claim`; write-set task create
214
+ and pickup also require local scope and callable claim permission. Ledger paths are advisory
215
+ coordination, never filesystem authorization.
216
+ `exocom_task create` declares a voluntary task but does not start work or reserve files. The
217
+ immutable `task_id` is the creation frame's `msg_id`; carry it with `work_key` for take, progress,
218
+ finish, offer and publish so a reused work key cannot select another generation. Write-set pickup
219
+ and its claim are one locked ledger transaction; read-only tasks may be taken from a foreign scope.
220
+ An optional `exocom_offer` expires in at most 24 hours (one hour by default) and creates no answer
221
+ obligation. Use `exocom_ask` for an explicit blocking question. `exocom_publish` shares a bounded,
222
+ explicitly written excerpt, not another session's terminal-result archive. Peer prose is fenced and
223
+ attributed using the registry identity.
224
+ Tasks, offers and evidence are each capped at 64 records and 128 KiB aggregate. Terminal or stale
225
+ task facts and expired offers are retained for up to one hour; evidence is retained for up to one
226
+ hour. `exocom_profile` reports self-described capabilities (up to 8 tags, 48 characters each) and
227
+ optional `available`/`busy`/`away` status; absent status is unknown, and profiles confer no access.
228
+ During a pending ask, only settlement, `exocom_name`, status, explicit collaboration reads and
229
+ read-only filesystem tools remain available: profile `get` is allowed, but `set` and `clear` are not.
230
+ Naming remains metadata and does not answer the ask. Joining requires `canUseBus` plus at least one
231
+ callable ask closer (`exocom_answer` or `exocom_decline`) and is rechecked on persona changes.
232
+ Design: [architecture](docs/ARCHITECTURE.md#exocom--the-external-plane) and
233
+ [collaboration guide](docs/COLLABORATION.md).
226
234
 
227
235
  ## Testing
228
236
 
package/CHANGELOG.md CHANGED
@@ -4,7 +4,82 @@ All notable changes to this project are documented here. The format follows
4
4
  [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); this project follows the version scheme
5
5
  used in its [GitHub releases](https://github.com/AeonDave/pi-persona/releases).
6
6
 
7
- ## [Unreleased]
7
+ ## [1.17.4](https://github.com/AeonDave/pi-persona/releases/tag/v1.17.4) - 2026-10-25
8
+
9
+ ### Added
10
+
11
+ - Voluntary Exocom collaboration: generation-bound shared tasks with atomic pickup/write claims,
12
+ expiring optional invitations, bounded attributed evidence publication and self-reported profiles.
13
+ Stale ownership remains visible without automatic takeover; task/evidence reads are paginated.
14
+ See [the collaboration guide](docs/COLLABORATION.md) for the authority and retention contracts.
15
+ - Uniform operator views across every pi-persona surface: tool cards and commands use compact
16
+ purpose lines with shared-literal expansion (monitor, timer, models, read_result, agent_name,
17
+ contact_supervisor, contact_peer); session cards share the delegate-family frame (`Event · datum`
18
+ titles, indented clipped lines, boilerplate-free compact with drift-guarded projections, full
19
+ payload on expansion); the agent tree and F9 overlay show each leg's effort sigla (`mn l m h xh mx`,
20
+ off renders nothing) with honest requested→observed divergence, and delegate launch cards show the
21
+ full effort level next to the model.
22
+ - Audited all operator notifications: bounded and sanitized toasts, single-surface settlement
23
+ announcements (done/stopped announce once via the follow-up card; failures keep their immediate
24
+ durable surface).
25
+ - Installed-tarball qualification harness (`scripts/qualify-package.mjs`): packs and inspects the
26
+ artifact, installs it into isolated temporary hosts, and exercises real delegation, stop and
27
+ shutdown on both engines with a faux provider, network tripwires and empty credential stores.
28
+ Current policy: latest stable Pi only (1.4.0); historical host versions are rejected.
29
+ - Per-leg thinking/effort: a typed `thinking` level on agents, delegate single/batch calls, roster
30
+ and council members, and auxiliary actors. Precedence is explicit leg/member > agent definition >
31
+ the existing configured/inherited child setting; malformed values fail before launch. Both engines
32
+ report requested and observed Pi effort separately (an observation is Pi's requested effort, never
33
+ a claim about provider cognition); the child engine passes a separate `--thinking` argument and the
34
+ model-suffix synthesis is gone.
35
+ - Bounded durable terminal results: settled delegate/council/flow outcomes are saved as versioned
36
+ session entries with immutable `result:<UUID>` references (count/byte budgets, honest truncation
37
+ and storage receipts). `intercom results` lists them and `intercom result { ref, offset, limit }`
38
+ reads pages after reload — read-only: no respawn, no rebilling, no extra wake. The restore notice
39
+ now distinguishes vanished runtime work from retained terminal reports.
40
+
41
+ ### Fixed
42
+
43
+ - Show every council/flow participant's launch model and requested effort, including auxiliary
44
+ judges and repeated roles with distinct inline selections. Persist the receipt for history;
45
+ pending model setup remains explicit. Role hints omit code and technical identifiers rather than
46
+ turning environment-variable names or Git refs into reviewer labels.
47
+ - Treat failed model-availability reads as unavailable rather than advertising the whole catalog.
48
+ Session identity failures remain explicit and cannot reuse a previous owner or synthesize a
49
+ process-based identity; branch recovery restores the persisted name.
50
+ - Admit granted bridge communication tools through Pi 1.4's plain tool allowlist, so a read-only
51
+ worker can still contact its supervisor. Empty grants, explicit exclusions and semantic bus denial
52
+ remain restrictive; sibling contact requires peer opt-in.
53
+ - Refuse incompatible reference-flow journal versions and non-successful archived phases without
54
+ respawning completed work, including unapproved saved checkpoints. Retain truncation provenance
55
+ in resumed previews and final reports.
56
+ - Label compact status usage as cumulative input/output tokens, distinct from context occupancy.
57
+ - Preserve observed model identity after recovery in terminal results and completion views, while
58
+ launch cards retain the original selection. Requested and observed values remain distinguishable.
59
+ - Announce a successful or stopped async run ONCE: the completion follow-up card is the single
60
+ settlement surface (the tree row already settles immediately); the duplicate info toast is gone.
61
+ Failures keep their immediate durable card/sanitized toast.
62
+ - Pin isolated worktree exports to the starting commit, preserving worker commits together with later
63
+ uncommitted changes and new files in the returned patch.
64
+ - Keep unanswered supervisor decisions/interviews unresolved instead of suggesting implicit approval.
65
+ Expose explicit outcomes and suppress bus activity for already-cancelled calls.
66
+
67
+ ### Removed
68
+
69
+ - Remove the `/persona none` alias; use `/persona off` to deactivate the current persona.
70
+ - Remove obsolete host API facades, the CJS-only Pi resolver fallback, unused session/notifier
71
+ options, unemitted identity-message aliases and redundant internal re-exports. Callers and test
72
+ fixtures now use the current Pi contracts directly.
73
+
74
+ ### Changed
75
+
76
+ - Clarify and regression-test `/persona restore`: overwrite all bundled defaults without pruning
77
+ additional custom personas, agents or resources. Settings, credentials and sessions are not reset.
78
+ - Target the latest stable Pi only (currently 1.4.0): development pins, `MIN_PI_VERSION`, docs and
79
+ the package-qualification gate move together, and historical host versions are rejected. A scoped
80
+ development override pins `undici` past known advisories inside the Pi 1.4.0 host dependency tree.
81
+ - Align source SDK smoke checks with the canonical settings store and the current interactive vs
82
+ headless execution contract; settings qualification also rejects historical host versions.
8
83
 
9
84
  ## [1.17.3](https://github.com/AeonDave/pi-persona/tree/v1.17.3) - 2026-10-23
10
85
 
package/README.md CHANGED
@@ -17,7 +17,8 @@ you can inspect workers, redirect them, or stop a run while it is in progress.
17
17
 
18
18
  ## Install
19
19
 
20
- Requires **Pi ≥ 1.0.0**, **Node.js ≥ 22.19**, and **Git**. Configure a model provider in Pi first.
20
+ Requires **the latest stable Pi (currently 1.4.0)**, **Node.js ≥ 22.19**, and **Git**.
21
+ Update Pi together with this plugin; historical Pi releases are not maintained. Configure a model provider in Pi first.
21
22
 
22
23
  Run in your terminal:
23
24
 
@@ -39,9 +40,9 @@ your customizations.
39
40
  | Install option | Command |
40
41
  |---|---|
41
42
  | This project only | `pi install -l npm:@aeondave/pi-persona` |
42
- | Pin this release | `pi install npm:@aeondave/pi-persona@1.17.3` |
43
+ | Pin this release | `pi install npm:@aeondave/pi-persona@1.17.4` |
43
44
  | Update an unpinned install | `pi update npm:@aeondave/pi-persona` |
44
- | Git tag 1.17.3 | `pi install git:github.com/AeonDave/pi-persona@v1.17.3` |
45
+ | Git tag 1.17.4 | `pi install git:github.com/AeonDave/pi-persona@v1.17.4` |
45
46
 
46
47
  Restart Pi after updating. For a pinned install, install the new version or tag explicitly.
47
48
  If switching from a Git install, remove that source first with
@@ -52,7 +53,8 @@ then install the npm package. Keep only one source configured to avoid loading t
52
53
 
53
54
  | Capability | What you get |
54
55
  |---|---|
55
- | **Async workers** | Specialists with their own roles, skills, and models; results return while the supervisor continues other work |
56
+ | **Async workers** | Specialists with their own roles, skills, models, and thinking levels; results return while the supervisor continues other work |
57
+ | **Retained results** | Bounded reports remain available after reload and can be read in pages without restarting workers |
56
58
  | **Live steering** | Inspect a worker, send guidance, or stop a run from the agent panel |
57
59
  | **Cross-session collaboration** | Exocom connects independent Pi sessions to exchange findings and coordinate work |
58
60
  | **Councils and flows** | Compare perspectives, run repair-and-check loops, or organize work into stages |
@@ -119,8 +121,9 @@ To enable Exocom, open two terminals in the same workspace and start each with:
119
121
  pi --exocom --persona dev
120
122
  ```
121
123
 
122
- Then ask one session to consult the other or agree who handles which files.
123
- Use `/exocom` to inspect the connection and workspace code.
124
+ Use `/exocom` to inspect the connection and workspace code. Sessions can exchange one-way messages,
125
+ record a voluntary task, or share a bounded evidence excerpt. Creating a task does not start work in
126
+ the other session; a peer chooses whether to take it.
124
127
 
125
128
  A session in another workspace can join using that exact, case-sensitive code:
126
129
 
@@ -129,10 +132,11 @@ pi --exocom=Ab0T --persona researcher
129
132
  ```
130
133
 
131
134
  Replace `Ab0T` with the code shown by `/exocom`. Both sessions must run on the same machine and
132
- share a Pi agent directory. A peer joining from another workspace can advise and exchange results;
133
- it cannot claim files in the joined workspace.
135
+ share a Pi agent directory. A peer joining from another workspace can advise, exchange results, and
136
+ take read-only tasks; it cannot claim or take tasks that declare write paths in the joined workspace.
134
137
 
135
- See the [collaboration guide](docs/EXPERIENCE.md#work-with-another-pi-through-exocom) for day-to-day use.
138
+ See the [cross-session collaboration guide](docs/COLLABORATION.md) for the task, invitation, ask and
139
+ evidence workflows.
136
140
 
137
141
  ## Choose a persona
138
142
 
@@ -170,8 +174,11 @@ See [the reference](docs/REFERENCE.md) for an inline council example and recover
170
174
  Edit installed personas and agents in `~/.pi/agent/persona/agents/`, or add project-specific
171
175
  overrides in `.pi/agents/`. Run `/persona reload` after editing.
172
176
 
173
- `/persona seed` preserves existing files. **`/persona restore` replaces bundled defaults**, so use it
174
- only when you intend to discard customizations to those files.
177
+ `/persona seed` preserves existing files. **`/persona restore` overwrites all bundled defaults**
178
+ (personas, agents, teams, flows, contracts, presets and shared prompts), discarding customizations
179
+ to those files. It does not delete additional custom files: a persona `pippo.md` or an agent
180
+ `pluto.md` remains untouched. A custom file using the same destination as a bundled default is
181
+ replaced. Settings, credentials, sessions and project-local overrides are not reset.
175
182
 
176
183
  For optional long-term memory, add [pi-persona-mind](https://github.com/AeonDave/pi-persona-mind).
177
184
 
@@ -186,6 +193,7 @@ refreshes persona definitions. Model changes apply to future runs. See [configur
186
193
  | Guide | What's inside |
187
194
  |---|---|
188
195
  | [Everyday use](docs/EXPERIENCE.md) | Choosing a persona, following work, and reading results |
196
+ | [Cross-session collaboration](docs/COLLABORATION.md) | Voluntary tasks, optional offers, asks, evidence and presence between Pi sessions |
189
197
  | [Customization reference](docs/REFERENCE.md) | Tool APIs, configuration, and copyable recipes |
190
198
  | [Strategies](docs/STRATEGIES.md) | Teams, councils, flows, and orchestration options |
191
199
  | [Timers and monitors](docs/MONITORS.md) | Reminders and programs that wake the supervisor |
@@ -205,8 +213,10 @@ npm test
205
213
  ```
206
214
 
207
215
  Pi loads the TypeScript extension directly; there is no build step.
208
- To qualify a separately installed Pi 1.3.0 host without cloud calls or your profile, run
209
- `node --import tsx scripts/qualify-host.mjs <absolute-path-to-pi-coding-agent-package>`.
216
+ Qualify the installed tarball on the current host without provider traffic or your profile with
217
+ `node scripts/qualify-package.mjs --host <absolute-path-to-pi-coding-agent-package>`.
218
+ The narrower source-SDK contract check is
219
+ `node --import tsx scripts/qualify-host.mjs <absolute-path-to-pi-coding-agent-package>`; both reject historical host versions.
210
220
  The [CI workflow](https://github.com/AeonDave/pi-persona/actions/workflows/test.yml) runs strict
211
221
  typechecking, dependency auditing and the test suite on Linux and Windows. The OpenSSF badge links to the project's
212
222
  scored supply-chain checks; it is not a certification that the code is free of vulnerabilities.
@@ -81,9 +81,10 @@ These are the guardrails a contributor must not violate. They are enforced in co
81
81
  by the SDK on every `agent()` call and by the engine per child. Concurrency is shared across all
82
82
  calls in one SDK instance; token admission uses completed usage and is rechecked after queueing, so
83
83
  active legs can overshoot it. Nesting depth is not one of these numeric knobs — it is structural,
84
- capped at 1: a child's whole pi-persona extension activation short-circuits under
85
- `PI_PERSONA_DISABLE=1` (the **fork-bomb guard**), so it registers NO tools at all —
86
- `delegate`/`council`/`orchestrate`/`flow` included. On the child engine this is a one-shot env var
84
+ capped at 1: full pi-persona activation short-circuits under `PI_PERSONA_DISABLE=1` (the
85
+ **fork-bomb guard**), so no supervisor orchestration tools (`delegate`/`council`/`flow`) are
86
+ registered. A broker-enabled child instead loads the minimal bridge, which registers only
87
+ granted communication and scoped result-reader tools. On the child engine this is a one-shot env var
87
88
  set on each spawned process; the in-process engine instead shares one Node process across
88
89
  concurrently-building sub-sessions, so it ref-counts the guard (`inproc.ts`) to keep it set until the
89
90
  LAST one finishes. The in-process engine also excludes `ORCHESTRATION_TOOLS` from the child session
@@ -151,6 +152,12 @@ These are the guardrails a contributor must not violate. They are enforced in co
151
152
 
152
153
  ## Module layout
153
154
 
155
+ The supported host is the latest stable Pi (currently 1.4.0). Runtime code uses its public APIs
156
+ directly; obsolete host facades, ignored SDK options and compatibility-only aliases are removed
157
+ with their callers. Genuine host failures remain explicit rather than fabricating session identity
158
+ or model availability. One-way user-data migrations and retained-history readers are separate data
159
+ contracts: they remain only while serving stored user data, not to support an older Pi runtime.
160
+
154
161
  Domain dependencies remain downward-only and acyclic: `core ← all`; `engine`/`bus → core`;
155
162
  `orchestration → engine + bus + core`; `persona → orchestration + core`; the Exocom transport and
156
163
  ledger → `core + bus`; `tools`/`ui → lower layers`. `src/exocom/install.ts` is deliberately the
@@ -282,9 +289,12 @@ up to 16 names. Same idea, two layers, two caps — see docs/REFERENCE.md's exam
282
289
  `isolation: worktree` is fail-closed. It requires a clean Git checkout so the detached `HEAD` view
283
290
  cannot silently omit staged/unstaged/untracked supervisor work; a non-repository, dirty checkout,
284
291
  worktree creation failure, missing diff artifact, or an artifact over the return limit fails the leg —
285
- the base engine is never invoked against the real tree as a fallback. The diff returns to the
286
- supervisor before the temporary tree is removed, and every git call is asynchronous so the extension
287
- host keeps rendering, ticking timers, and serving broker sockets while a large checkout runs.
292
+ the base engine is never invoked against the real tree as a fallback. The created worktree's initial
293
+ commit is pinned before the worker starts. Export compares the final file state against that immutable
294
+ commit, so worker commits, later unstaged changes and new files are included together. The diff is
295
+ captured before the temporary tree is removed and then returned to the supervisor; commit history is
296
+ not applied to the main checkout. Every git call is asynchronous so the extension host keeps rendering,
297
+ ticking timers, and serving broker sockets while a large checkout runs.
288
298
 
289
299
  Transient retries inside one agent session belong to the host **Pi runtime**'s `retry.*` settings, not
290
300
  a persona contract — pi-persona neither parses retry notation nor schedules its own backoff. Both
@@ -501,12 +511,17 @@ events and cannot route, reply, steer, or otherwise control agents.
501
511
  ## The comm plane in practice
502
512
 
503
513
  - **In-process bus** (`bus/inproc.ts`) — a handle-based mailbox: `send` (one-way), `ask` (blocks for a
504
- reply), `reply`, `onMessage`. `contact_supervisor` (child→supervisor, gated by `coaching: on`) and
514
+ reply), `reply`, `onMessage`. `contact_supervisor` (child→supervisor, gated by `canUseBus` and,
515
+ for in-process children, `coaching: on`) and
505
516
  `contact_peer` (sibling→sibling) are the child-side tools bound onto it. Each unread inbox holds at
506
517
  most 200 messages; overflow evicts an old one-way note first, and an inbox full of live asks rejects
507
518
  new delivery rather than stranding a sender. Ask settlement (reply, timeout, abort, departure, or
508
519
  failed delivery) releases its timer/listener and unread entry; a previously retained question stays
509
520
  readable with `expectsReply: false`, so old questions never advertise an active reply id.
521
+ Tool outcomes distinguish `answered` from unavailable, timed-out, cancelled and failed requests;
522
+ delivery evidence is never approval. Headless questions remain nonblocking and unresolved, allowing
523
+ only independent already-authorized work or a blocked/partial handoff. Pre-aborted calls perform no
524
+ bus action, and replies racing cancellation are not reported as answers.
510
525
  - **Sibling peer comm** — a strategy opts a run in via `AgentRunSpec.peers` (gated by `canUseBus`). The
511
526
  child gets `contact_peer` (`list`/`send`, ONE-WAY so peers can never deadlock; per-engine-instance
512
527
  scoping; a 20-send budget and an 8,000-character body limit, enforced before consuming a send). The
@@ -529,6 +544,10 @@ events and cannot route, reply, steer, or otherwise control agents.
529
544
  spawn without a bus endpoint, so they never burn connect backoff against a dead socket), and the next
530
545
  build retries; `/doctor` surfaces its state on one line. A dropped client connection flips the
531
546
  child-side bridge to `⇄ offline` and fails a fresh ask fast rather than hanging.
547
+ A connected child exposes `contact_supervisor` when `canUseBus` is allowed; `contact_peer` also
548
+ requires the run's peer opt-in. Granted custom tool names join a nonempty plain allowlist so Pi's
549
+ host filter does not hide them. Explicit `tools: []` and exclusions remain restrictive. A denied
550
+ semantic bus does not remove supervisor runtime steering or an independently scoped result reader.
532
551
 
533
552
  ### Presentation is a projection, not another comm plane
534
553
 
@@ -627,6 +646,21 @@ for the supervisor and for explicit retrieval; the default TUI projection is del
627
646
  unreadable versions. Session-file deletion owns log deletion; ordinary shutdown saves diagnostics,
628
647
  not deletes them. Operator-only expandable log entries do not create model context or extra wakes.
629
648
 
649
+ **Terminal result archive** (`src/results/`, `src/extension/results.ts`) applies the same
650
+ ownership model to settled agent work: every terminal delegate/council/flow outcome (and each final
651
+ SDK leg result, after any model recovery) is projected — once, idempotently by immutable origin —
652
+ into a versioned session entry with an opaque `result:<UUID>` reference, independent from runtime
653
+ run ids. Budgets: 128 entries, 8 MiB cumulative serialized payload per owning session file, 512 KiB
654
+ per record; bodies cap at 262,144 UTF-16 units with honest truncation metadata. Unknown, malformed
655
+ and duplicate history still consumes quota; unaccountable history disables new persistence instead
656
+ of resetting it. A fork/copy is a new owning file charged only for the entries it actually carries.
657
+ `storage: "session-entry"` means the host accepted the entry — never an fsync guarantee; memory-only
658
+ retention is labelled as such. Retrieval is read-only: `intercom results` lists, `intercom result
659
+ { ref, offset, limit }` pages (8,000 default / 16,000 max units, surrogate-safe), with no respawn,
660
+ rebilling, collection or extra wake; reading a page never returns the whole run hidden in metadata.
661
+ A saved result is not a live worker: the restore notice says runtime handles are gone while retained
662
+ reports remain readable.
663
+
630
664
  This is a UI invariant only: truncating a collapsed card must never be confused with truncating the
631
665
  underlying result or changing a strategy's contract.
632
666
 
@@ -640,17 +674,22 @@ and message peer-to-peer, with no parent/child relationship. (intercom = interna
640
674
  external comm.)
641
675
 
642
676
  Exocom supplies fenced one-way postcards and presence; chat `message` never mutates shared work
643
- state. Separately, a workspace work ledger (`ledger.ts`, tools in `tools/exocom-work.ts`) records
644
- claims/asks/answers/progress/releases. `exocom_ask` canonicalizes its target through
645
- `plane.resolvePeer` (raw session ids on the ledger, never the display name); registry display name is
646
- keyed by `from_session`, never the envelope's self-reported `from_name`. A pending ask constrains the
647
- turn (`gate.ts`: answer/decline plus read-only tools) behind a bounded `pendingAskPrompt`.
648
- `exocom_wait` is non-blocking. Clean shutdown releases this session's claims/asks; if that best-effort
649
- write fails, the vanished owner is pruned on the next ledger transaction (live registry sessions are
650
- the lease), and `expireWaiters` wakes any waiter left behind instead of leaving `exocom_wait` armed
651
- forever. This gate is cooperative coordination for participating local Pi processes — not filesystem
652
- authorization, not isolation from another same-user process, and not a delegate/council or task/run
653
- replacement.
677
+ state. `src/exocom/ledger.ts` stores claims, questions, answers and progress; `src/exocom/collaboration.ts`
678
+ adds voluntary tasks, optional offers and explicit evidence excerpts. Tool adapters live in
679
+ `src/tools/exocom.ts`, `src/tools/exocom-collaboration.ts`, and `src/tools/exocom-profile.ts`.
680
+ `exocom_ask` resolves its target through the live registry to a raw session id, never a display name.
681
+ Attribution is resolved by `from_session`, never the envelope's self-reported `from_name`.
682
+
683
+ A pending ask gates the participant to `exocom_answer`/`exocom_decline`, `exocom_name` identity
684
+ metadata, `exocom_status`, explicit collaboration reads (`exocom_task` list/get, `exocom_publish`
685
+ read, `exocom_profile` get), and read-only filesystem tools. Profile `set`/`clear` and all other
686
+ mutations remain unavailable until the ask is settled. Naming does not settle an ask. The bounded
687
+ `pendingAskPrompt` explains the outstanding obligation. `exocom_wait` is non-blocking. Clean shutdown
688
+ releases this session's claims/asks; if that best-effort write fails, the vanished owner is pruned on
689
+ the next ledger transaction (live registry sessions are the lease), and `expireWaiters` wakes any
690
+ waiter left behind instead of leaving `exocom_wait` armed forever. This gate is cooperative
691
+ coordination for participating local Pi processes — not filesystem authorization, not isolation from
692
+ another same-user process, and not a delegate/council or task/run replacement.
654
693
 
655
694
  - **Opt-in, OFF by default.** `PI_PERSONA_EXOCOM=1` or bare `--exocom` joins the current workspace;
656
695
  `--exocom=Ab0T` joins an existing scope by its exact, case-sensitive four-character Base62 alias
@@ -677,18 +716,31 @@ replacement.
677
716
  standing brief, and `/exocom` label a peer as same-workspace or external without exposing its
678
717
  absolute `cwd`. External peers are full Pi instances that can inspect files in their own workspace;
679
718
  paths are never implicitly shared.
680
- - **Postcards plus a durable, non-blocking join.** `exocom_send` returns a `msg_id` immediately; a
681
- reply is another `exocom_send` with `in_reply_to` set. `target: "*"` broadcasts postcards
682
- best-effort (one unreachable peer doesn't fail the rest). Work coordination is separate:
683
- `exocom_ask` commits an obligation to the ledger, `answer`/`decline` settles it, and `exocom_wait`
684
- arms a bounded idle wake rather than blocking. The signed frame is a wake signal; the ledger remains
685
- the source of truth if delivery is deferred.
719
+ - **Postcards, asks and offers have different obligations.** `exocom_send` returns a `msg_id`
720
+ immediately; a reply is another `exocom_send` with `in_reply_to` set. `target: "*"` broadcasts
721
+ postcards best-effort. `exocom_ask` commits a targeted question that must be answered or declined;
722
+ `exocom_wait` arms a bounded idle wake rather than blocking. `exocom_offer` is an optional,
723
+ expiring invitation to consider an open task, targeted to one peer or the pool; it creates no ask,
724
+ no answer obligation and no work authorization. Its default lifetime is one hour, capped at 24 hours.
725
+ Signed immediate-wake frames are signals; the ledger remains authoritative when delivery is deferred.
726
+ - **Task generations and atomic pickup.** `exocom_task create` records a voluntary objective and
727
+ declared write set; creation neither launches work nor claims paths. The task's immutable
728
+ `task_id` is its creation frame's `msg_id`; later take/progress/finish, offer and publish actions
729
+ carry both `work_key` and `task_id`, so reusing a key cannot attach a delayed action to a newer
730
+ task. A write-set task's pickup and overlapping write claim commit together under the ledger lock;
731
+ competing overlapping pickups are rejected. Read-only tasks can be taken by a peer in another
732
+ workspace scope. A task whose owning registry lease disappears becomes stale and cannot be taken
733
+ over automatically. `exocom_task list` is paginated; `get` returns the complete bounded task detail,
734
+ including the exact write set. `exocom_publish` attaches an explicitly authored bounded excerpt to
735
+ one task generation; discovery pages titles and message ids, and the full excerpt is read by id.
736
+ These excerpts do not grant access to a peer's terminal-result archive.
686
737
  - **Cross-workspace writes are advisory-only in v1.** A repository-relative claim path has meaning
687
738
  only in the workspace that owns the selected scope, so a member whose home workspace differs from
688
- the scope cannot `exocom_claim` (the tool and the signed-frame receiver both reject a foreign
689
- claim) — it can still send, inspect its own files, ask, answer/decline, wait, and release its own
690
- asks. Multi-repository write coordination would need a versioned resource namespace; paths from
691
- unrelated repositories are never compared as if they named the same files.
739
+ the scope cannot `exocom_claim` or create/pick up a write-set task (the tool and signed-frame
740
+ transaction both enforce the scope). It can still read shared tasks/evidence, take read-only tasks,
741
+ send postcards, ask, answer/decline, and release its own asks. Multi-repository write coordination
742
+ would need a versioned resource namespace; paths from unrelated repositories are never compared as
743
+ if they named the same files.
692
744
  - **Reply routing is session-stable.** `exocom_list` shows human display names (`name`/`name#2`);
693
745
  sends and inbound reply hints use `name@<96-bit session hash>`, so a retained qualified target
694
746
  stays valid after a rename. Ambiguous suffix matches fail closed rather than guess. The
@@ -727,15 +779,23 @@ replacement.
727
779
  double-trigger a turn. An exchange's LENGTH is instead bounded prompt-side: the per-turn peer brief
728
780
  (`core/brief.ts`) carries a relevance bound — send only what changes what someone does — deliberately
729
781
  not a round count, since a counter can't see whether a round still serves the work.
730
- - **Tools are lazy and fail closed.** `exocom_list({ offset?, limit? })` exposes bounded, paginated
731
- presence; `exocom_send({ target, message, in_reply_to? })` sends one-way (a session-qualified-looking
732
- target resolves only as one, never falling back to a display name, since names are self-chosen);
733
- `exocom_name({ name })` rebrands the display call-sign without moving any state. The ledger tools —
734
- `exocom_claim`, `exocom_ask`, `exocom_answer`, `exocom_decline`, `exocom_wait`, `exocom_progress`,
735
- `exocom_release` — are capability-gated with the postcard tools; the live accessor, capability gate,
736
- and active-tool set all deny them once the plane stops, since Pi has no dynamic tool-unregister API.
737
- Admission requires at least one obligation closer, because every published peer may receive a
738
- durable ask and must retain a way to settle it.
782
+ - **Fifteen tools are lazy and fail closed.** `EXOCOM_TOOL_NAMES` lists `exocom_list`, `exocom_send`,
783
+ `exocom_name`, `exocom_claim`, `exocom_ask`, `exocom_answer`, `exocom_decline`, `exocom_wait`,
784
+ `exocom_release`, `exocom_progress`, `exocom_status`, `exocom_task`, `exocom_offer`,
785
+ `exocom_publish`, and `exocom_profile`. Each tool rechecks effective persona capabilities at call
786
+ time; the live accessor and active-tool set also deny calls once the plane stops, since Pi has no
787
+ dynamic tool-unregister API. A joined foreign-scope peer has no `exocom_claim`. Admission requires
788
+ `canUseBus` and at least one callable ask closer, because every published peer may receive a durable
789
+ ask and must retain a way to settle it.
790
+ - **Collaboration and profile retention are bounded.** The ledger retains at most 64 tasks, 64 offers,
791
+ and 64 evidence excerpts with a 128 KiB aggregate collaboration-state budget. Task descriptions cap
792
+ at 2,048 characters; write sets at 64 paths of 256 characters each; progress/finish notes at 4,096.
793
+ Evidence titles cap at 160 characters and excerpts at 4,096 characters / 8,192 UTF-8 bytes. Finished
794
+ or stale tasks remain for up to one hour; expired offers remain for up to one hour after expiry, and
795
+ evidence for up to one hour after publication. Active tasks remain while their owner lease is live.
796
+ `exocom_profile` contains up to eight self-described capability labels (48 characters each) and
797
+ optional `available`, `busy` or `away` status. Missing status is unknown. Profiles exist only for
798
+ their live Exocom session and never prove skills, permission or availability.
739
799
  - **Inbound delivery is bounded without loss.** Each message is injected under the same byte cap
740
800
  whether plain text or an artifact descriptor. Bursts stay FIFO-queued; each rate-limited wake drains
741
801
  as many whole messages as fit, leaving the rest queued. A presentation cap on the collapsed card