@aeondave/pi-persona 1.15.2 → 1.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/AGENTS.md +30 -6
  2. package/CHANGELOG.md +82 -0
  3. package/README.md +27 -8
  4. package/SECURITY.md +42 -0
  5. package/docs/ARCHITECTURE.md +52 -7
  6. package/docs/EXPERIENCE.md +21 -5
  7. package/docs/README.md +2 -0
  8. package/docs/REFERENCE.md +84 -14
  9. package/docs/STRATEGIES.md +58 -31
  10. package/docs/TELEMETRY.md +8 -0
  11. package/package.json +11 -9
  12. package/src/bus/broker/paths.ts +3 -2
  13. package/src/core/brief.ts +1 -1
  14. package/src/core/nudge.ts +3 -4
  15. package/src/core/pi-compat.ts +3 -3
  16. package/src/engine/adapter.ts +11 -6
  17. package/src/engine/async.ts +12 -6
  18. package/src/engine/inproc.ts +14 -6
  19. package/src/extension/engine.ts +25 -4
  20. package/src/extension/hooks.ts +68 -21
  21. package/src/extension.ts +209 -49
  22. package/src/orchestration/model-retry.ts +32 -61
  23. package/src/orchestration/params.ts +27 -1
  24. package/src/orchestration/roster.ts +80 -3
  25. package/src/orchestration/sdk.ts +150 -36
  26. package/src/orchestration/strategies/compete.ts +14 -8
  27. package/src/orchestration/strategies/council-rounds.ts +8 -1
  28. package/src/orchestration/strategies/critic-loop.ts +25 -11
  29. package/src/orchestration/strategies/judge.ts +18 -9
  30. package/src/orchestration/strategies/magi.ts +42 -93
  31. package/src/orchestration/strategies/map.ts +41 -19
  32. package/src/orchestration/strategies/pipeline.ts +2 -2
  33. package/src/orchestration/strategies/synthesize.ts +18 -11
  34. package/src/orchestration/types.ts +15 -0
  35. package/src/persona/model-participants.ts +240 -0
  36. package/src/persona/orchestrate.ts +44 -2
  37. package/src/persona/persona.ts +86 -14
  38. package/src/telemetry/contract.ts +1 -1
  39. package/src/telemetry/producer.ts +241 -8
  40. package/src/tools/council.ts +36 -8
  41. package/src/tools/delegate-tool.ts +162 -24
  42. package/src/tools/intercom-tool.ts +119 -32
  43. package/src/ui/agent-overlay.ts +15 -6
  44. package/src/ui/agent-tree.ts +39 -2
package/AGENTS.md CHANGED
@@ -30,8 +30,18 @@ the shared behavioral prompt layer: [`docs/SPINE.md`](docs/SPINE.md).
30
30
  `@earendil-works/pi-*`; never bundle copies.
31
31
  - npm publishes as `@aeondave/pi-persona`. Pi supplies the four `@earendil-works/pi-*` packages
32
32
  and `typebox`: keep them as `peerDependencies: "*"` with development-only copies, never runtime
33
- dependencies. The supported host floor lives in `MIN_PI_VERSION` and the README. Keep the npm
33
+ dependencies. The supported host floor is Pi ≥ 1.0.0 (`MIN_PI_VERSION` and the README); Node.js
34
+ must be ≥ 22.19 to match that host. Keep the npm
34
35
  `files` allowlist explicit for docs/artwork, inspect `npm pack --dry-run`, and never publish drafts.
36
+ - Supply-chain controls: pin every GitHub Action to a verified full commit SHA; keep top-level
37
+ token permissions read-only, grant write permissions only to the reporting job that needs them,
38
+ and disable persisted checkout credentials. Dependabot proposes updates; it does not auto-merge.
39
+ Keep the four Pi development packages in one update group so their APIs stay aligned.
40
+ Match `@types/node` to the oldest supported Node major, including in CI; upgrade them together.
41
+ Keep security overrides scoped to the affected development dependency and version; remove them
42
+ when upgrading Pi if the new dependency graph no longer needs them.
43
+ Run the full dependency audit (including development dependencies), tests and typecheck after
44
+ lockfile updates. A development override does not patch a user's installed Pi host.
35
45
  - **Cross-OS**: use Pi's spawn/kill/path/temp helpers (`getPiInvocation`, `killProcessTree`), not raw
36
46
  `child_process`. Windows kill goes straight to `taskkill /F /T`; always attach an `error` listener.
37
47
  - Two engines behind the `StrategyEngine` seam: **InProcessEngine** (`engine/inproc`,
@@ -78,6 +88,11 @@ the shared behavioral prompt layer: [`docs/SPINE.md`](docs/SPINE.md).
78
88
  policy (OpenAI-family → OpenAI providers; Claude-family → the native Claude provider). A caller may
79
89
  explicitly opt a pinned run into cross-provider recovery. Only `failureKind === "provider"` reroutes;
80
90
  abort/timeout/contract/unknown/agent are terminal.
91
+ Strategy/council/flow runs disable that provider search and use SDK-level main-only recovery:
92
+ `provider`/`unknown-model` may retry ONCE on the current supervisor model, including a saved or
93
+ inline-pinned choice (explicit user-authorized exception). Never borrow a peer model, never restart
94
+ a stopped/timed-out/contract-failed leg, and charge each attempt to child/token limits and usage.
95
+ Ordinary delegate pins remain strict. Recovery must remain visible in metadata/UI and MAGI rulings.
81
96
  Engines classify the cause on the `AgentResult` (`failureKind` + resolved `modelUsed`); keep those
82
97
  set when you touch `inproc.ts`/`adapter.ts` or the fallback silently stops working.
83
98
  - **Fork-bomb guard**: children run with env `PI_PERSONA_DISABLE=1` so pi-persona self-disables inside
@@ -93,11 +108,13 @@ the shared behavioral prompt layer: [`docs/SPINE.md`](docs/SPINE.md).
93
108
  blind retry loops are stopped by the runtime `DelegationLedger` (an identical agent+model+task
94
109
  delegation that failed twice is vetoed before it spawns).
95
110
  - Dynamic sub-agents: `delegate` shapes an on-the-fly specialist with `role` (extra system prompt,
96
- appended to the agent's own) + `skills` — prompt-level only, capabilities stay the gate. Async runs
97
- are joined with intercom `wait` (bounded ≤ the bus-ask timeout; collected results are discarded from
98
- the pending completion follow-up so they are never double-reported). In interactive sessions
99
- `delegate` is background-by-default (`sync: true` opts a call out; headless `pi -p` defaults to
100
- sync so the single turn carries the result).
111
+ appended to the agent's own) + `skills` — prompt-level only, capabilities stay the gate. Intercom
112
+ `wait` snapshots already-settled reports by default in interactive/RPC sessions; `sync: true` opts
113
+ into a bounded join (≤ the bus-ask timeout). Headless waits default to joining; `sync: false` takes
114
+ a snapshot. Collected reports are discarded from the pending completion follow-up so they are
115
+ never double-reported; snapshots leave running children active. In interactive sessions `delegate`
116
+ is background-by-default (`sync: true` opts a call out; headless `pi -p` defaults to sync so the
117
+ single turn carries the result). An explicit `async` flag takes precedence over `sync` and the default.
101
118
  Message ids and run ids are separate: `intercom message { messageId }` retrieves retained bus
102
119
  text, `intercom result { to: runId }` retrieves a settled run. Both explicit and automatic inbox
103
120
  drains retain bounded history (256 messages / 256,000 body characters). Ask settlement clears
@@ -120,6 +137,10 @@ the shared behavioral prompt layer: [`docs/SPINE.md`](docs/SPINE.md).
120
137
  fills with every leg's body, failures included), while `renderCompletion` scans only `status: "done"`
121
138
  runs. A background leg that FAILED while emitting `[BLOCKED]` therefore gets the failure block but no
122
139
  persistence note — deliberate (see the `renderCompletion` doc comment), not a bug to "fix" silently.
140
+ Nested reports from codemode's `ctx.executeTool` retain bounded provenance through Pi's explicit
141
+ `parentToolCallId`; never infer nesting from the call-id string. Only an outer result that relays
142
+ the surrender marker gets the note/card. Nested programmatic data is never patched with a reminder,
143
+ and discarded reports never generate an invisible one. Relay state clears at turn/session settlement.
123
144
  `config.nudge` (`PI_PERSONA_NUDGE=off`) silences all of it: the `tool_result` hook (the by-hand
124
145
  reminder and the sync-result persistence one) and both `renderCompletion` call sites, which take
125
146
  their `scan` through the same gate — so a background/`intercom wait` report carries no note either.
@@ -190,6 +211,9 @@ the shared behavioral prompt layer: [`docs/SPINE.md`](docs/SPINE.md).
190
211
  persistence; `src/extension/identity.ts` registers `agent_name` and the pre-name context bootstrap.
191
212
  Exocom uses that same state. A delegate's optional `AgentRunSpec.name` is leader-assigned
192
213
  display identity, propagated to both engines without changing routing ids.
214
+ Async launch/control cards use that alias, with persisted tool-result snapshots for history.
215
+ Keep run IDs for routing/diagnostics, not primary display identity; disambiguate duplicate aliases.
216
+ Steering cards preview the accepted message and expose all of it with Pi's expand-key binding.
193
217
  Runtime metadata guidance belongs in the stable supervisor system prompt, not a new user-like
194
218
  acknowledgement message. Keep initial setup/actionable asks working, preserve user-visible history,
195
219
  and never re-inject a chosen identity. Progress `tokens` is cumulative input/output usage, not live
package/CHANGELOG.md CHANGED
@@ -6,6 +6,88 @@ used in its [GitHub releases](https://github.com/AeonDave/pi-persona/releases).
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [1.16.0](https://github.com/AeonDave/pi-persona/releases/tag/v1.16.0) - 2026-10-03
10
+
11
+ ### Compatibility
12
+
13
+ - Require Pi ≥ 1.0.0 and Node.js ≥ 22.19. Update the development cohort to Pi 1.0's four coordinated
14
+ host-package pins and qualify its SDK and tool lifecycle. Pre-1.0 Pi hosts are no longer supported;
15
+ the host packages and TypeBox remain wildcard peers supplied by Pi, not bundled runtime copies.
16
+ - Update the live control harness to use Pi 1.0's session `modelRuntime` through the public
17
+ `ModelRegistry` facade instead of the removed session registry property.
18
+ - An authored `council.members` field is now validated. Older malformed fields that were ignored
19
+ must be corrected or removed; council calls fail with the field diagnostic instead of selecting
20
+ an unrelated fallback panel.
21
+
22
+ ### Fixed
23
+
24
+ - Reserve mandatory downstream child slots before optional model recovery in `map`, `pipeline`,
25
+ `critic-loop`, and `council-rounds`. Provider outages no longer spend the slots needed by later
26
+ workers, verifiers, steps, or rounds and turn completed work into a run-fatal admission error.
27
+ - Pass live supervisor-ask state to the `intercom peek` tool, including untargeted peeks; a child
28
+ waiting for its supervisor is not reported as stuck. Collapsed steer receipts identify the run.
29
+ - Honor members-only council profiles for active and explicitly selected personas, including their
30
+ params when MAGI is selected explicitly as the same default strategy. Evaluate each configured
31
+ agent-model lookup once.
32
+ - Carry persistence checkpoints through nested codemode reports when their `[BLOCKED]`/`FLAG:
33
+ UNKNOWN` marker is relayed to the model. Suppressed nested output stays unchanged; only the outer
34
+ visible result receives the checkpoint and its expandable card.
35
+ - Surface telemetry cleanup/write diagnostics without requiring debug mode; diagnostics remain
36
+ non-fatal. Failed admission removes only the empty directories created by that attempt.
37
+ - Release a telemetry writer lease after its terminal writes finish without waiting for the shared
38
+ retention sweep over unrelated sessions; explicit maintenance joins remain available.
39
+ - Harden the exported council-preset expansion boundary with normalized strategy names and the
40
+ same strategy/roster/params allowlist as the JSON loader; validated authored members remain intact.
41
+ - Resolve live tree and telemetry model labels for auxiliary actors through the same role-specific
42
+ participants as the picker, including inline model pins and repeated SDK keys. Ambiguous short
43
+ role hints omit an uncertain model rather than attributing a different participant's choice.
44
+
45
+ ### Changed
46
+
47
+ - Make general council guidance proportionate while preserving MAGI and Judge's specialized focus;
48
+ all eleven strategies remain available through the same tool. Add ephemeral inline council members
49
+ and specialized external actors without creating persona/team files.
50
+ - Include role-specific members and external arbiters in model selection; respect configured models
51
+ and remember prompts per participant rather than suppressing later teams for the whole persona.
52
+ - Recover model/provider failures across strategies and flows once on the supervisor's current model,
53
+ including unavailable saved or pinned choices. Remove MAGI's peer-model substitution; report the
54
+ switch, account both attempts, and preserve child/token limits and cancellation.
55
+ - Use worker aliases in async launch cards and targeted Intercom call headers, matching the live tree.
56
+ Persist display identity in tool results so history remains readable after reload or tracker eviction;
57
+ routing still uses run IDs, with a secondary ID shown when aliases collide.
58
+ - Show the accepted steering message as a compact preview, with Pi's expand-key hint for the full
59
+ message and diagnostic run ID. Replace repetitive soft-request/cancellation boilerplate with a
60
+ concise queued receipt; steering remains a request, not proof of execution or cancellation.
61
+ - Make context-burn checkpoints aware of Pi's nested-tool metadata: raw inner results do not
62
+ consume the model-visible output budget or receive invisible reminders. Permission gates,
63
+ telemetry and genuine successful hand-off resets still apply inside codemode.
64
+ - Show an immediate async completion banner with the worker's display name, including successful
65
+ runs. The semantic report still arrives once through the idle-gated completion path, and a failed
66
+ UI notification cannot swallow it.
67
+ - Replace the child watchdog test's load-sensitive kill-latency assertion with an observed blocked →
68
+ released → idle-timeout barrier; preserve the two intentional Windows-only skips.
69
+ - Clarify that automatic background completion follow-ups and end-turn guidance are the intended
70
+ async default: continue independent work, and do not monitor or join all workers when they are the
71
+ only remaining work. `intercom wait` now snapshots without blocking by default in interactive/RPC
72
+ sessions; `sync: true` opts into its bounded join, while headless remains blocking by default and
73
+ `sync: false` opts out. Snapshots collect only already-settled reports, leaving running work active.
74
+ Queued steering does not preempt an explicitly blocking join. A real Pi 1.0 SDK test with a local faux provider characterizes steering becoming
75
+ dormant when delayed input hooks outlast the active run. No unsafe input-triggered join abort ships.
76
+ - A council call that switches strategy no longer inherits unrelated profile params; params continue
77
+ to merge when the selected strategy is unchanged, and unknown explicit keys still warn.
78
+ - Show live supervisor asks as `waiting for supervisor`, rather than stalled, in the tree, F9 and
79
+ peek; refresh council parent clocks on child progress. A waiting worker becomes eligible for
80
+ stall detection again when its ask settles. Steering an already-settled run points to its result.
81
+ - Add age-based telemetry cleanup with a 30-day default; see
82
+ [`docs/TELEMETRY.md`](docs/TELEMETRY.md) for the retention contract.
83
+ - Reuse one tree snapshot per F9 list redraw for rows, selection and waiting state; remove an
84
+ unreachable delegate-title branch and distinguish event liveness from proof of useful progress.
85
+ - Align the README and technical guides on async snapshots/joins, steering delivery, model-selection
86
+ precedence, members-only councils, and automatic `map` verifier headroom.
87
+
88
+ After updating, let active workers settle and restart Pi (or use `/reload`). `/persona reload`
89
+ refreshes definitions, not extension code. Existing custom personas and provider pins are preserved.
90
+
9
91
  ## [1.15.2](https://github.com/AeonDave/pi-persona/releases/tag/v1.15.2) - 2026-09-28
10
92
 
11
93
  ### Distribution
package/README.md CHANGED
@@ -4,9 +4,12 @@
4
4
  <img src="assets/banner.png" alt="pi-persona — supervised multi-agent orchestration for Pi">
5
5
  </p>
6
6
 
7
+ [![CI](https://github.com/AeonDave/pi-persona/actions/workflows/test.yml/badge.svg?branch=main)](https://github.com/AeonDave/pi-persona/actions/workflows/test.yml) [![OpenSSF Scorecard](https://api.scorecard.dev/projects/github.com/AeonDave/pi-persona/badge)](https://scorecard.dev/viewer/?uri=github.com/AeonDave/pi-persona) [![npm version](https://img.shields.io/npm/v/@aeondave/pi-persona?logo=npm)](https://www.npmjs.com/package/@aeondave/pi-persona) [![License](https://img.shields.io/github/license/AeonDave/pi-persona)](LICENSE)
8
+
7
9
  **Give Pi a supervised team.**
8
- `pi-persona` brings supervised multi-agent orchestration to [Pi](https://github.com/earendil-works/pi):
9
- async workers, live steering, cross-session collaboration, and switchable personas.
10
+ `pi-persona` adds supervised multi-agent orchestration to [Pi](https://github.com/earendil-works/pi):
11
+ async workers, live steering, cross-session collaboration, and switchable personas. It builds on
12
+ Pi's native SDK and tool hooks; the extension adds worker supervision and coordination.
10
13
  Describe the outcome. A supervisor delegates work, gathers results, and checks the outcome;
11
14
  you can inspect workers, redirect them, or stop a run while it is in progress.
12
15
 
@@ -14,7 +17,7 @@ you can inspect workers, redirect them, or stop a run while it is in progress.
14
17
 
15
18
  ## Install
16
19
 
17
- Requires **Pi ≥ 0.83**, **Node.js 22+**, and **Git**. Configure a model provider in Pi first.
20
+ Requires **Pi ≥ 1.0.0**, **Node.js ≥ 22.19**, and **Git**. Configure a model provider in Pi first.
18
21
 
19
22
  Run in your terminal:
20
23
 
@@ -36,9 +39,9 @@ your customizations.
36
39
  | Install option | Command |
37
40
  |---|---|
38
41
  | This project only | `pi install -l npm:@aeondave/pi-persona` |
39
- | Pin this release | `pi install npm:@aeondave/pi-persona@1.15.2` |
42
+ | Pin this release | `pi install npm:@aeondave/pi-persona@1.16.0` |
40
43
  | Update an unpinned install | `pi update npm:@aeondave/pi-persona` |
41
- | Git alternative | `pi install git:github.com/AeonDave/pi-persona@v1.15.2` |
44
+ | Git alternative | `pi install git:github.com/AeonDave/pi-persona@v1.16.0` |
42
45
 
43
46
  Restart Pi after updating. For a pinned install, install the new version or tag explicitly.
44
47
  If switching from a Git install, remove that source first with
@@ -69,8 +72,12 @@ For example, specialists can investigate code paths, tests, and the API contract
69
72
  Their findings return to the **same supervisor**, which integrates them, verifies the fix, and reports
70
73
  the outcome. This is one possible workflow, not a fixed pipeline; dependent steps run in order.
71
74
 
72
- Interactive delegation runs in the background by default. The supervisor gathers results and keeps
73
- you informed; you can inspect or redirect a worker at any point.
75
+ Interactive delegation runs in the background by default. Results return automatically when the
76
+ supervisor is free; you can inspect or redirect a worker at any point.
77
+
78
+ Launch and control cards show the worker's name. Steering receipts preview the message sent;
79
+ expand with `Ctrl+O` (the default binding) to read it in full. A queued message is not proof that
80
+ the worker has acted on it.
74
81
 
75
82
  | Key or command | Action |
76
83
  |---|---|
@@ -135,6 +142,13 @@ everyday coding, or choose one for the work ahead.
135
142
  These are editable starting points. Browse the bundled [personas](personas) and [worker agents](agents),
136
143
  or read the [practical guide](docs/EXPERIENCE.md) for help choosing a workflow.
137
144
 
145
+ Strategies are not restricted to the similarly named persona: `council` can select `judge`, `magi`,
146
+ `debate`, `critic-loop`, or another installed shape when it adds value. Temporary `members` allow
147
+ custom role-based groups without team files. Model selection includes external arbiters. In councils
148
+ and flows, a provider failure or unavailable model can recover once on the supervisor's model, with
149
+ the switch visible and both attempts accounted; ordinary delegates keep explicit provider pins strict.
150
+ See [the reference](docs/REFERENCE.md) for an inline council example and recovery limits.
151
+
138
152
  ## Make it yours
139
153
 
140
154
  Edit installed personas and agents in `~/.pi/agent/persona/agents/`, or add project-specific
@@ -169,4 +183,9 @@ npm test
169
183
  ```
170
184
 
171
185
  Pi loads the TypeScript extension directly; there is no build step.
172
- See [AGENTS.md](AGENTS.md) for contribution conventions. Licensed under [MIT](LICENSE).
186
+ The [CI workflow](https://github.com/AeonDave/pi-persona/actions/workflows/test.yml) runs strict
187
+ typechecking, dependency auditing and the test suite on Linux and Windows. The OpenSSF badge links to the project's
188
+ scored supply-chain checks; it is not a certification that the code is free of vulnerabilities.
189
+
190
+ See [AGENTS.md](AGENTS.md) for contribution conventions and the [security policy](SECURITY.md)
191
+ for private vulnerability reporting. Licensed under [MIT](LICENSE).
package/SECURITY.md ADDED
@@ -0,0 +1,42 @@
1
+ # Security Policy
2
+
3
+ ## Supported versions
4
+
5
+ Security fixes target the latest published version of
6
+ [`@aeondave/pi-persona`](https://www.npmjs.com/package/@aeondave/pi-persona).
7
+ Older versions do not have a guaranteed backport policy. Check the
8
+ [release notes](https://github.com/AeonDave/pi-persona/releases) for fixes and
9
+ upgrade instructions. Keep your Pi host and Node.js runtime updated as well.
10
+
11
+ ## Reporting a vulnerability
12
+
13
+ Use [GitHub private vulnerability reporting](https://github.com/AeonDave/pi-persona/security/advisories/new).
14
+ Do not open a public issue with exploit details, credentials, private session
15
+ logs, or other sensitive material. Ordinary bugs can use the public issue tracker.
16
+
17
+ Include the affected package version or commit, Pi and Node.js versions, operating
18
+ system, relevant configuration, expected security boundary, actual behavior, and
19
+ a minimal reproduction with synthetic data. Explain the impact and any safe
20
+ workaround you have verified. Do not test against other users' sessions or systems
21
+ without their permission.
22
+
23
+ Follow-up and disclosure coordination happen in the private advisory. This is a
24
+ maintainer-run project, with no guaranteed response or remediation deadline and
25
+ no paid bug bounty. Agree on public disclosure after triage so users can receive
26
+ a fix or mitigation before exploit details are published.
27
+
28
+ ## Security boundaries
29
+
30
+ Pi extensions and installed persona/agent definitions are trusted local code and
31
+ configuration, not an OS sandbox. Capability checks and Exocom's cooperative file
32
+ claims do not isolate a malicious local process. A workspace join code is a
33
+ same-host routing reference, not an authentication secret. See the
34
+ [architecture](docs/ARCHITECTURE.md) for the actual trust boundaries.
35
+
36
+ Reports of capability bypasses, broker/Exocom trust violations, unsafe handling of
37
+ untrusted worker output, or unintended disclosure of session data are welcome.
38
+ Use synthetic credentials and payloads in reproductions.
39
+
40
+ The npm package uses Pi-provided peer dependencies rather than shipping another
41
+ Pi runtime. Development lockfile fixes do not update an installed user's host;
42
+ host vulnerabilities must also be addressed by updating Pi itself.
@@ -54,6 +54,10 @@ rejected with the run id to wait on, so a later call cannot slip past the in-bat
54
54
  - **L3 — flow file.** `flows/<name>.flow.json` composes strategies into a resumable DAG.
55
55
 
56
56
  You climb only when you want determinism, structure, or reuse. Nothing forces the higher tiers.
57
+ The `council` tool is a gateway to all installed strategies, not a universal instruction to vote on
58
+ routine work. Choose a strategy when its topology adds value; MAGI and Judge retain their specialized
59
+ persona focus. A caller can supply ephemeral `members` with inline roles/models/skills instead of a
60
+ named team, without persisting a team or switching personas.
57
61
 
58
62
  ## Binding invariants (I1–I7)
59
63
 
@@ -193,7 +197,8 @@ engine, telemetry, UI, and tool surfaces; it is not part of the transport/domain
193
197
  constrained-turn allowlist), `install.ts` (the feature's composition adapter: session-scoped plane +
194
198
  ledger + wait/tool/hook wiring).
195
199
  - **`src/telemetry/`** — a generic, versioned observer/export contract for future plugins: projected
196
- lifecycle metadata only, never an agent-message router or control surface.
200
+ lifecycle metadata only, never an agent-message router or control surface. See
201
+ [`TELEMETRY.md`](./TELEMETRY.md) for privacy, retention, writer leases, and shutdown behavior.
197
202
  - **`src/persona/`** — identity: `persona.ts` (parse + `expandCouncilPreset` + `composeSystemPrompt`),
198
203
  `controller.ts`, `gating.ts`, `orchestrate.ts`, `config-store.ts`, `state.ts` (last-selected persona),
199
204
  `spine.ts` (the shared behavioral layer's SOURCE resolution — docs/SPINE.md; composition sits in
@@ -282,6 +287,19 @@ prevents an explicitly selected OpenAI or native Claude leg from silently moving
282
287
  provider. (`"verification"` is a further terminal value that never appears on an `AgentResult` — see
283
288
  `map`'s `verify` param in [`docs/STRATEGIES.md`](./STRATEGIES.md).)
284
289
 
290
+ Strategy/council/flow runs use a different, explicitly authorized recovery policy: the selected model
291
+ is tried first without arbitrary provider reroutes, then `makeSDK` may retry a `provider` or
292
+ `unknown-model` failure ONCE on the supervisor's current model. This main-only exception also applies
293
+ to saved and inline-pinned assignments; ordinary delegate provider pins remain strict. Missing main
294
+ models, repeated same-model attempts, stop/timeout/contract/agent/infrastructure failures, and exhausted
295
+ child/token budgets do not recover. Both attempts are accounted, and a warning plus result metadata
296
+ identify the switch. A per-role model assignment can supplement the legacy per-agent assignment;
297
+ preflight model selection includes external arbiters and other declared agent-valued parameters.
298
+ Live tree and telemetry labels use those participant identities for auxiliary actors too, with
299
+ inline model pins taking precedence. If distinct full roles share a short display hint, the display
300
+ keeps the hint (with `#N` suffixes for repeated node keys) but omits the uncertain model label rather
301
+ than borrowing another role's choice. Routing still resolves the full role key, not the display hint.
302
+
285
303
  ### MCP (and other `session_start`-scoped extensions) in sub-agents
286
304
 
287
305
  **A sub-agent does NOT share the supervisor's MCP session, and an in-process sub-agent gets NO MCP at
@@ -403,7 +421,9 @@ never simultaneously a telemetry event, a routed message, and a UI source of tru
403
421
  - **ProgressView** — a derived, read-only UI view computed *from* EngineEvents. Never authored, never
404
422
  a source of truth. The `f9` agent tree and `peek` digest are ProgressViews.
405
423
 
406
- Steering is always a Bus action; the peek digest is always a read-only ProgressView.
424
+ Steering is a supervisor-to-child control action, distinct from semantic Bus messages; requesting
425
+ steering does not establish that an active blocking join has been interrupted. The peek digest is
426
+ always a read-only ProgressView.
407
427
 
408
428
  External telemetry is not a fourth communication plane: it is a generic observer/export contract that
409
429
  future plugins may consume or produce, not an exclusive pi-persona protocol. It observes projected
@@ -459,6 +479,12 @@ for the supervisor and for explicit retrieval; the default TUI projection is del
459
479
  failed legs first, a failed council/flow card leads with the cause in its title, and an
460
480
  `intercom wait` card leads with the `N settled — X done, Y failed` tally (per-leg causes sit in the
461
481
  body, below the preview cut, so `wait`'s own success doesn't get a `failed` prefix);
482
+ - async launch cards and targeted Intercom call headers use the worker's canonical alias/model,
483
+ matching the tree rather than promoting its routing ID to display identity. Launch/control receipts
484
+ persist identity snapshots for history rendering; aliases never become routing keys. Colliding
485
+ aliases carry a secondary ID, and legacy receipts without a recoverable name retain their ID fallback.
486
+ Accepted steering receipts retain the exact sent message: collapsed cards preview it, expansion
487
+ shows all sanitized text and the diagnostic ID. A queued receipt does not prove the worker acted;
462
488
  - follow-up cards (`pi-persona`, exocom) retain their complete semantic content but render a bounded
463
489
  preview until expanded; terminal escape/control sequences are removed from the visible projection;
464
490
  - the sticky agent and exocom widgets have fixed row budgets — F9/`/agents` and paginated
@@ -606,15 +632,29 @@ a local process able to modify that directory is already inside the trust bounda
606
632
 
607
633
  - **sync** — the supervisor actively blocks on the delegate/strategy call (results still stream); no
608
634
  idle/peek/steer.
609
- - **async** — the supervisor returns control and goes **idle, spending no tokens**, until woken by an
610
- **event** (a child's `contact_supervisor`: a `decision`/`interview` blocks for a reply, `progress` is
611
- one-way) or the **peek watchdog**, which fires while async children run but stays SILENT unless there
612
- is something to act on — a healthy background run never interrupts. It surfaces on two independent
635
+ - **async** — interactive delegation is background-first; headless execution remains synchronous
636
+ by default. The prompt/runtime guidance is to continue independent work and, when only background
637
+ children remain, end the turn for automatic completion follow-ups rather than monitor or join the
638
+ whole batch. `intercom wait` collects a snapshot without blocking by default in interactive/RPC
639
+ sessions; settled results are reported and running children continue. Use `sync: true` for an
640
+ intentional bounded join. Headless sessions preserve the blocking default; `sync: false` requests a
641
+ snapshot. A snapshot does not wait, start timers/input hooks, or cancel children. Queued steering
642
+ does not release an explicitly active join. Input-triggered join interruption is
643
+ deliberately not implemented: a real Pi 1.0 SDK characterization shows steering can remain dormant
644
+ if delayed input hooks outlast the active run; no supported post-admission ordering guarantee
645
+ has been established. The supervisor otherwise returns control and
646
+ goes idle until a wake; the **peek watchdog** fires while async children run but stays SILENT unless
647
+ there is something to act on — a healthy background run should not interrupt. It surfaces on two independent
613
648
  signals: a **fast** wakeup (`PI_PERSONA_PEEK_MS`, ~30s, `0` disables) when a child NEWLY crosses the
614
649
  `STALL_FLAG_MS` (90s) stall window (a focused *possibly stuck* alert, framed patience-first — ask the
615
650
  leg, don't probe its environment) or messages the supervisor; and a **slow routine check-in**
616
651
  (`PI_PERSONA_CHECKIN_MS`, ~5 min, `0` disables) delivering the compact ProgressView digest — never
617
- full transcripts — so the supervisor can catch a leg going off-track early. Both let an idle
652
+ full transcripts — so the supervisor can catch a leg going off-track early. Live supervisor asks
653
+ are derived from the exact engine handle's optional `isWaitingForSupervisor` predicate: tree/F9/peek
654
+ show waiting, and the watchdog suppresses false stalls until the ask settles. Parent clocks advance
655
+ with child engine events (liveness, not proof of useful work); parents are waiting only when every
656
+ live descendant branch is waiting.
657
+ Both let an idle
618
658
  supervisor steer/stop a wedged or drifting child even with NO completion fired; the enforcing
619
659
  backstop is the engines' hard wall-clock cap (above). The full digest is also on demand via `/peek`.
620
660
  Progress tokens are labelled **cumulative input/output**, not context occupancy; cache-read/write
@@ -649,6 +689,11 @@ a persona directive lives at the TOP of the prompt and its pull decays as recent
649
689
  only `status === "done"` runs by design — a failed background/`wait` leg already gets a failure
650
690
  block, so a `[BLOCKED]` marker there doesn't also get the persistence note. Same marker, same leg,
651
691
  different counterweight depending on how it was collected.
692
+ Nested calls through codemode's `ctx.executeTool` leave their programmatic result untouched.
693
+ Pi's explicit `parentToolCallId` supplies the parent identity; call-id string shapes are never
694
+ used to infer nesting. A bounded set of parent call ids carries report provenance through wrappers
695
+ only while their output relays a surrender marker; the outer model-visible result alone gets the
696
+ note/card. Suppressed reports produce neither, and turn/session settlement clears abandoned relay state.
652
697
  - **The off switch covers every path.** `PI_PERSONA_NUDGE=off` silences DelegationNudge entirely and
653
698
  PersistenceNudge on every collection path (sync result, background notifier, `intercom wait`).
654
699
 
@@ -40,20 +40,36 @@ ask for a monitor. Pi runs a small event-producing program and brings the result
40
40
  supervisor is free. Keep the session open, and cancel a watch when its purpose ends. See
41
41
  [time and event wakes](MONITORS.md) for examples and limits.
42
42
 
43
- - **F9** opens the agent tree. Every running row shows its elapsed time and switches to `⚠ stalled
44
- <duration>` once it has gone 90 seconds without progress — the same threshold behind the
45
- supervisor's stall alert. Enter opens a selectable worker's output; `s` sends a course correction;
43
+ - **F9** opens the agent tree. Queued rows have not started their clocks. Live supervisor asks show
44
+ `waiting for supervisor`, not a stall; running rows otherwise show elapsed time and switch to
45
+ `⚠ stalled <duration>` after 90 seconds without progress — a suspicion, not proof of a deadlock.
46
+ Council parent clocks refresh on child engine events; that indicates liveness, not useful work.
47
+ A parent is waiting only when all its live descendant branches are waiting. Enter opens a selectable worker's output; `s` sends a course correction;
46
48
  `x` requests cancellation and appears only when the run can actually be stopped, marking the row
47
49
  `stopping…` at once; a stop that can't be applied says so instead of doing nothing. Escape goes
48
50
  back or closes the panel.
49
51
  - **`/peek`** gives a compact progress view. A quiet worker is a reason to inspect its last
50
52
  progress and current activity, not to launch a duplicate immediately.
51
53
  - **Background results arrive automatically.** A supervisor can continue independent work while
52
- they run. `intercom wait` joins results when the next step depends on them.
54
+ they run. When only background workers remain, end the turn and let completion follow-ups return
55
+ their results; do not monitor or poll healthy workers or automatically join the whole batch.
56
+ `intercom wait` snapshots immediately by default in interactive/RPC sessions; use `sync: true` for
57
+ an intentional bounded join when the next step depends on those results. Headless waits remain
58
+ blocking by default; pass `sync: false` for a snapshot.
53
59
  - **Answer a waiting worker before joining it.** A coaching worker may need a decision from its
54
60
  supervisor before it can finish. An unanswered question is actionable work.
55
61
  - **Steering is a request.** Check the subsequent output to see whether the worker incorporated
56
- it. Check the terminal result after requesting cancellation; changes already made are not undone.
62
+ it. In-process workers can receive guidance mid-run; child-engine workers (including MCP/worktree
63
+ runs) receive it as a queued follow-up. Neither promises to interrupt arbitrary tool work, and
64
+ steering does not preempt an explicitly blocking `intercom wait`. End the supervisor turn rather
65
+ than join merely to idle. Check the terminal result after requesting cancellation; changes already
66
+ made are not undone.
67
+
68
+ Launch cards and targeted Intercom calls show the same worker alias as the live tree. A steering
69
+ receipt previews the message that was actually queued; expand it (`Ctrl+O` by default, or your
70
+ configured key) to read the full message and diagnostic run ID. Aliases are display names, not
71
+ addresses: tools still route by run ID, and duplicate aliases show a secondary ID to distinguish them.
72
+ Saved launch/control receipts keep their display names even after the runtime tracker is gone.
57
73
 
58
74
  A truncated message includes a message id. The supervisor can retrieve its retained body with
59
75
  `intercom { action: "message", messageId: "m1" }`. A worker's final report has a run id instead:
package/docs/README.md CHANGED
@@ -1,6 +1,8 @@
1
1
  # pi-persona documentation
2
2
 
3
3
  This folder contains the long-form documentation that defines how the project is designed and run.
4
+ It follows the current checkout; consult the [changelog](../CHANGELOG.md) for the release that
5
+ introduced a behavior. An `Unreleased` entry is not yet part of a published release.
4
6
 
5
7
  ## Canonical docs
6
8
 
package/docs/REFERENCE.md CHANGED
@@ -16,7 +16,7 @@ plus a file that calls `parallel` and `reduce.vote`.
16
16
 
17
17
  | Primitive | Does |
18
18
  |---|---|
19
- | `agent(spec)` | run one sub-agent and return a structured `AgentResult`; `spec` may carry `name`, model, tools, skills, or `outputContract` |
19
+ | `agent(spec, { reserveChildren? }?)` | run one sub-agent and return a structured `AgentResult`; `spec` may carry `name`, model, tools, skills, or `outputContract`; optional reservations protect mandatory later legs from model recovery |
20
20
  | `parallel(thunks, {concurrency})` | run many at once, bounded by the run limits; this also provides the basis of `map` |
21
21
  | `reduce.aggregate(results)` | merge N results into one, used by fan-out |
22
22
  | `reduce.vote(candidates, opts)` | tally the candidates' own votes and preserve `winner`, `tie`, `no_consensus`, `invalid_outputs`, and dissent |
@@ -35,8 +35,8 @@ hard-fail.
35
35
  | Surface | Does |
36
36
  |---|---|
37
37
  | `delegate` tool | spawn one or more sub-agents; interactive calls are background-first, while `sync: true` blocks the turn |
38
- | `council` tool | run the selected strategy over a roster and return its result; voting strategies preserve tally and dissent |
39
- | `intercom` tool | observe and control internal runs with `peek`, `result`, `wait`, `steer`, and `stop`; `list`, `inbox`, `message`, `reply`, and `send` drive the coaching bus |
38
+ | `council` tool | select any installed strategy over a named roster or temporary inline `members`; voting strategies preserve tally and dissent. Use when the topology adds value, not for routine work |
39
+ | `intercom` tool | observe and control internal runs with `peek`, `result`, `wait`, `steer`, and `stop`; `list`, `inbox`, `message`, `reply`, and `send` drive the coaching bus. `wait` snapshots without blocking by default with UI; `sync: true` opts into a bounded blocking join (headless defaults to blocking; use `sync: false` for a snapshot); queued steering does not preempt an explicit join |
40
40
  | `timer` tool | read the fresh clock with `now`, or schedule an idle wake with `arm`, `cancel`, and `list`; alarms are one-shot and in-memory per session |
41
41
  | `monitor` tool | run a bounded job (`mode: exit`) or event-producing program (`mode: output`) and wake the supervisor; see [`MONITORS.md`](./MONITORS.md) |
42
42
  | `flow` tool and `/flow` | run a journaled DAG of strategies; `gate: true` creates an approval checkpoint |
@@ -49,30 +49,70 @@ hard-fail.
49
49
  | `contracts/*.json` | define a hot-editable structured return requested through `outputContract` and pinned per run |
50
50
 
51
51
  The delegate tool's optional `name` is carried into `AgentRunSpec.name` and reaches both engine
52
- backends, keeping a caller-provided run label available in the live tree and runtime result.
52
+ backends, keeping a caller-provided run label available in the live tree and runtime result. Every
53
+ settled async run also emits an immediate UI banner with that name and its terminal outcome. The
54
+ model-facing report remains independently idle-gated; a UI notification failure cannot discard it.
55
+ An explicit `delegate` `async` flag overrides both `sync` and the session default: `async: true`
56
+ forces background execution; `async: false` forces synchronous execution.
57
+
58
+ ### Pi 1.0 codemode
59
+
60
+ Pi's native `codemode` can call callable pi-persona tools through the normal nested-tool pipeline.
61
+ Argument validation and persona permission gates still apply to every inner call; scripts do not
62
+ turn a denied tool into an allowed one. Native `model-only` and `hidden` tools are not callable.
63
+
64
+ Use scripts for independent batches or for filtering large results before returning them to the
65
+ model, not as a wrapper that prints every result in full. `Promise.allSettled()` preserves successful
66
+ siblings when a call rejects; a fulfilled `bash` call still requires checking `exit_code`.
67
+ Only printed/returned script output reaches the model. Nested results therefore do not contribute
68
+ raw bytes or invisible reminders to pi-persona's context-burn nudges; an actual successful nested
69
+ `delegate`/`council` still resets the hand-off streak.
70
+
71
+ Pi identifies calls made through codemode's `ctx.executeTool` with `parentToolCallId`. If a nested
72
+ delegation report is relayed with `[BLOCKED]` or `FLAG: UNKNOWN`, only the outer model-visible result
73
+ gets the persistence checkpoint and its expandable card. Discarding the report creates no reminder
74
+ and never alters the programmatic inner result. See [the nudge contract](./ARCHITECTURE.md#supervision--the-waiting-model)
75
+ for the differences between synchronous and background reports.
76
+
77
+ The outer code/result card belongs to Pi, not pi-persona's delegate renderer. Keep bulky details
78
+ expandable (`Ctrl+O` by default; `/hotkeys` shows active bindings). `Ctrl+T` toggles thinking blocks
79
+ separately. A smaller script output also reduces model context; merely collapsing its card does not.
80
+ No replacement sandbox or renderer is required for this integration.
53
81
 
54
82
  `outputContract` takes a contract **name**, not an inline shape — `/doctor` lists what's installed
55
- (the only built-in is `default`). Naming one that isn't installed is rejected before any worker
83
+ (the built-in `default` and bundled `finding` are available without custom files). Naming one that
84
+ isn't installed is rejected before any worker
56
85
  spawns, checked across every task in a parallel batch: `delegate: unknown output contract(s) "x" —
57
86
  nothing was spawned. Installed contracts: default, finding` (the list is capped at 16 names; a
58
87
  `, …` marker is appended only when more than 16 are installed).
59
88
 
60
89
  ### Supervising running sub-agents — the `intercom` plane
61
90
 
62
- The supervisor's internal communication with children has three layers:
91
+ The supervisor's internal communication with children exposes these surfaces. Tree/F9/peek distinguish
92
+ queued, running, waiting for supervisor, suspected stalled and terminal runs. Waiting is derived from
93
+ a live pending ask on the exact child handle, never inferred from its last activity text:
63
94
 
64
- | Layer | Needs | What you get |
95
+ | Surface | Needs | What you get |
65
96
  |---|---|---|
66
- | **Observe and control** | any persona | `peek` watches async sub-agents; `wait` joins them; `steer` sends a soft correction; `stop` requests cancellation. The `f9` overlay provides the same controls (`s`, `x`). Cancelling a worker does not undo existing changes |
97
+ | **Observe and control** | any persona | `peek` inspects async sub-agents; `wait` is nonblocking by default with UI (`sync: true` joins; headless defaults to joining, with `sync: false` for snapshots); `steer` sends a soft correction; `stop` requests cancellation. The `f9` overlay provides the same controls (`s`, `x`). Queued steering does not preempt an active join. Cancelling a worker does not undo existing changes |
67
98
  | **Retrieve** | any persona | `result { to }` returns one retained run payload; `message { messageId }` returns one retained bus message. History is bounded to 256 messages and 256,000 body characters, and retrieval never starts a worker |
68
99
  | **Message bus** | `coaching: true` | children get `contact_supervisor`; progress appears in results and `intercom inbox`, while a blocking `decision` wakes the supervisor for `intercom reply` |
69
100
  | **Sibling peer comm** | strategy opt-in | `debate` and `pair` members always get one-way `contact_peer`; `map` and `synthesize` add it with `params: { peers: true }` |
70
101
 
71
- Delegated output is untrusted and is fenced before it reaches the supervisor. Async runs report
72
- failures to the supervisor, and a repeated identical failed delegation is stopped by the runtime
73
- ledger before another child is spawned. A run whose model's provider fails at call time (auth,
74
- outage, 5xx) may retry the same model id through a fallback provider; each reroute surfaces to the
75
- supervisor as a warning toast, `<agent>: <from> failed, retrying on <to>`.
102
+ With coaching enabled, an async child's `decision` or `interview` waits for a supervisor reply.
103
+ A synchronous child posts the question one-way and continues instead: the supervisor is holding
104
+ that turn and cannot answer a blocking ask. Progress messages are always one-way.
105
+
106
+ Delegated output is untrusted and is fenced before it reaches the supervisor. Interactive async
107
+ work is background-first: continue independent work, and when only children remain, end the turn for
108
+ automatic completion follow-ups rather than monitoring or joining all children. Use `wait` only when
109
+ the next step depends on results. Async runs report failures to the supervisor, and a repeated
110
+ identical failed delegation is stopped by the runtime ledger before another child is spawned. A run whose model's provider fails at call time (auth,
111
+ outage, 5xx) may retry the same model id through a compatible fallback provider when its selection
112
+ is unpinned; explicit delegate provider pins remain strict. Strategies and flows instead recover a
113
+ model/provider failure once on the current main model, including unavailable saved or pinned choices.
114
+ They never substitute a peer's model or search arbitrary providers. Each recovery is visible and both
115
+ attempts consume usage/child budget; stop, timeout, contract and infrastructure failures do not retry.
76
116
 
77
117
  Any leg that does NOT run in `isolation: worktree` shares the real checkout, so its result gets a
78
118
  "what changed" block appended: `--- FILES CHANGED DURING THIS LEG (shared checkout; parallel legs
@@ -235,6 +275,36 @@ Convene the council before sign-off, then apply its merged findings yourself.
235
275
  council({ question: "cache this or recompute?", strategy: "debate", roster: "review", params: { bestOf: 2 } })
236
276
  ```
237
277
 
278
+ A one-off group can be composed without editing a team file. The external arbiter is also an
279
+ agent specification, independent of the panel:
280
+
281
+ ```js
282
+ council({
283
+ question: "Choose between these designs; prioritize correctness and operational cost.",
284
+ strategy: "judge",
285
+ members: [
286
+ { agent: "reviewer", role: "Evaluate correctness and failure boundaries." },
287
+ { agent: "reviewer", role: "Evaluate operating cost and maintainability." }
288
+ ],
289
+ params: { judge: { agent: "reviewer", role: "Choose impartially from the anonymised options." } }
290
+ })
291
+ ```
292
+
293
+ `members` overrides the named roster for this invocation only. Roles/skills do not grant tools or
294
+ bypass capability gates. Model selection covers roster members and declared auxiliary actors such
295
+ as `judge` and `synthesizer`; supported agent-valued params accept inline member specs too. Model
296
+ precedence is inline `model`, saved role assignment, saved agent assignment, agent frontmatter,
297
+ then the session model (`src/persona/model-participants.ts`).
298
+
299
+ With an active persona, UI, and at least two configured models, unassigned participants can be
300
+ picked and saved for the active persona, even when borrowing another persona's council profile.
301
+ Dismissing a picker suppresses another prompt for that participant during the session, not for
302
+ later teams with different participants. Headless runs never prompt.
303
+ An unavailable choice is not overwritten: a provider or unknown-model failure may recover once
304
+ on the main model, with the switch reported and both attempts accounted. This does not relax
305
+ ordinary delegate provider pins. The MAGI persona retains its canonical core team; the Judge
306
+ persona retains impartial arbitration. Other personas choose whichever strategy fits the task.
307
+
238
308
  **A delegation policy on any persona** — fields are generic and are never keyed to a persona name.
239
309
  The runtime rejects incomplete briefs before a model call, supplies a missing contract, rejects
240
310
  overlapping or missing ownership for parallel writers, and refuses a declared verifier that would
@@ -360,7 +430,7 @@ build retries. A connected broker child's status shows `⇄ <handle>`; if that c
360
430
  status flips to `⇄ offline` and a fresh ask on it fails fast instead of hanging.
361
431
 
362
432
  `/doctor` also reports the installed host's version against this extension's floor: `pi: <version>
363
- (requires ≥ 0.83.0)`, with a trailing `— BELOW FLOOR` marker once an installed version fails the
433
+ (requires ≥ 1.0.0)`, with a trailing `— BELOW FLOOR` marker once an installed version fails the
364
434
  check (`src/core/pi-compat.ts`). `pi: unknown` means the host's own `package.json` could not be
365
435
  resolved (a non-standard install layout) — not a failed check.
366
436