@aeondave/pi-persona 1.17.1 → 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 (146) hide show
  1. package/AGENTS.md +103 -64
  2. package/CHANGELOG.md +310 -1
  3. package/README.md +37 -23
  4. package/agents/architect.md +21 -0
  5. package/agents/balthasar.md +17 -22
  6. package/agents/casper.md +17 -23
  7. package/agents/evidence-verifier.md +31 -16
  8. package/agents/melchior.md +17 -22
  9. package/agents/operator.md +15 -55
  10. package/agents/research.md +14 -48
  11. package/agents/reviewer.md +20 -39
  12. package/agents/scout.md +14 -15
  13. package/agents/verifier.md +15 -16
  14. package/docs/ARCHITECTURE.md +280 -92
  15. package/docs/COLLABORATION.md +110 -0
  16. package/docs/EXPERIENCE.md +52 -17
  17. package/docs/MONITORS.md +62 -3
  18. package/docs/README.md +12 -0
  19. package/docs/REFERENCE.md +139 -38
  20. package/docs/SPINE.md +2 -2
  21. package/docs/STRATEGIES.md +23 -8
  22. package/docs/TELEMETRY.md +1 -0
  23. package/flows/deep-review.flow.json +1 -1
  24. package/package.json +15 -8
  25. package/personas/audit.md +28 -17
  26. package/personas/dev.md +30 -32
  27. package/personas/elite.md +223 -221
  28. package/personas/judge.md +25 -19
  29. package/personas/magi.md +26 -29
  30. package/personas/planner.md +31 -24
  31. package/personas/researcher.md +25 -26
  32. package/personas/swarm.md +35 -25
  33. package/personas/verify.md +30 -19
  34. package/src/agents/agent.ts +26 -0
  35. package/src/bridge.ts +28 -10
  36. package/src/bus/broker/client.ts +119 -21
  37. package/src/bus/broker/host.ts +123 -8
  38. package/src/bus/broker/messages.ts +31 -4
  39. package/src/bus/contact.ts +113 -38
  40. package/src/bus/peers.ts +15 -0
  41. package/src/bus/result-reader.ts +88 -0
  42. package/src/core/brief.ts +436 -353
  43. package/src/core/capabilities.ts +1 -1
  44. package/src/core/config.ts +15 -10
  45. package/src/core/leg-thinking.ts +57 -0
  46. package/src/core/model-catalog.ts +171 -0
  47. package/src/core/model-tier.ts +89 -0
  48. package/src/core/models.ts +13 -4
  49. package/src/core/monitor-output.ts +85 -0
  50. package/src/core/monitor.ts +34 -2
  51. package/src/core/nudge.ts +176 -23
  52. package/src/core/ownership.ts +1 -2
  53. package/src/core/pi-compat.ts +5 -12
  54. package/src/core/seed.ts +99 -6
  55. package/src/core/session-runs.ts +87 -0
  56. package/src/core/settings.ts +1 -1
  57. package/src/core/timer.ts +34 -9
  58. package/src/core/types.ts +1 -1
  59. package/src/engine/adapter.ts +95 -27
  60. package/src/engine/async.ts +213 -155
  61. package/src/engine/child.ts +12 -4
  62. package/src/engine/host-module.ts +12 -0
  63. package/src/engine/inproc.ts +99 -53
  64. package/src/engine/result-grant.ts +62 -0
  65. package/src/engine/session-tools.ts +6 -0
  66. package/src/engine/stream.ts +10 -20
  67. package/src/engine/worktree.ts +17 -7
  68. package/src/exocom/collaboration-delivery.ts +29 -0
  69. package/src/exocom/collaboration.ts +148 -0
  70. package/src/exocom/envelope.ts +43 -4
  71. package/src/exocom/gate.ts +7 -2
  72. package/src/exocom/install.ts +47 -18
  73. package/src/exocom/ledger.ts +58 -8
  74. package/src/exocom/plane.ts +32 -1
  75. package/src/exocom/profile.ts +40 -0
  76. package/src/exocom/registry.ts +5 -0
  77. package/src/exocom/status.ts +11 -1
  78. package/src/exocom/write-guard.ts +7 -0
  79. package/src/extension/broker-host.ts +61 -9
  80. package/src/extension/engine.ts +54 -28
  81. package/src/extension/hooks.ts +952 -868
  82. package/src/extension/identity.ts +41 -75
  83. package/src/extension/persona-model-settings.ts +31 -13
  84. package/src/extension/results.ts +136 -0
  85. package/src/extension/shared.ts +32 -7
  86. package/src/extension.ts +2965 -2611
  87. package/src/loader.ts +11 -3
  88. package/src/monitor/archive.ts +235 -0
  89. package/src/monitor/session.ts +21 -1
  90. package/src/orchestration/flow-journal.ts +33 -6
  91. package/src/orchestration/flow-run.ts +160 -32
  92. package/src/orchestration/flow.ts +20 -2
  93. package/src/orchestration/reducers.ts +9 -5
  94. package/src/orchestration/render.ts +1 -1
  95. package/src/orchestration/roster.ts +71 -23
  96. package/src/orchestration/sdk.ts +61 -11
  97. package/src/orchestration/strategies/compete.ts +5 -1
  98. package/src/orchestration/strategies/council-rounds.ts +2 -0
  99. package/src/orchestration/strategies/critic-loop.ts +3 -0
  100. package/src/orchestration/strategies/debate.ts +2 -0
  101. package/src/orchestration/strategies/fanout.ts +3 -2
  102. package/src/orchestration/strategies/judge.ts +5 -1
  103. package/src/orchestration/strategies/magi.ts +40 -23
  104. package/src/orchestration/strategies/map.ts +13 -3
  105. package/src/orchestration/strategies/pair.ts +2 -0
  106. package/src/orchestration/strategies/pipeline.ts +2 -0
  107. package/src/orchestration/strategies/synthesize.ts +27 -7
  108. package/src/orchestration/strategy.ts +31 -0
  109. package/src/orchestration/types.ts +10 -0
  110. package/src/persona/model-participants.ts +20 -7
  111. package/src/persona/orchestrate.ts +2 -0
  112. package/src/persona/persona.ts +8 -4
  113. package/src/results/archive.ts +281 -0
  114. package/src/results/record.ts +166 -0
  115. package/src/results/retrieval.ts +142 -0
  116. package/src/results/types.ts +128 -0
  117. package/src/telemetry/contract.ts +1 -1
  118. package/src/tools/background-run.ts +151 -0
  119. package/src/tools/council.ts +108 -66
  120. package/src/tools/delegate-tool.ts +349 -283
  121. package/src/tools/delegate.ts +35 -216
  122. package/src/tools/exocom-collaboration-input.ts +168 -0
  123. package/src/tools/exocom-collaboration.ts +315 -0
  124. package/src/tools/exocom-profile.ts +102 -0
  125. package/src/tools/exocom-work.ts +18 -53
  126. package/src/tools/exocom.ts +35 -22
  127. package/src/tools/flow.ts +103 -30
  128. package/src/tools/intercom-tool.ts +202 -128
  129. package/src/tools/intercom.ts +1 -1
  130. package/src/tools/models.ts +34 -9
  131. package/src/tools/monitor.ts +58 -6
  132. package/src/tools/read-result.ts +52 -0
  133. package/src/tools/timer.ts +34 -15
  134. package/src/ui/agent-overlay.ts +65 -16
  135. package/src/ui/agent-tree.ts +127 -19
  136. package/src/ui/compact-widget.ts +25 -0
  137. package/src/ui/failure-summary.ts +55 -0
  138. package/src/ui/literal-tool-result.ts +24 -0
  139. package/src/ui/model-picker.ts +158 -81
  140. package/src/ui/orchestration-launch.ts +49 -0
  141. package/src/ui/persona-settings.ts +9 -3
  142. package/src/ui/presentation.ts +24 -0
  143. package/src/ui/report-body.ts +63 -0
  144. package/src/ui/session-cards.ts +366 -0
  145. package/src/ui/wake-rows.ts +262 -0
  146. package/teams.yaml +5 -1
@@ -33,12 +33,17 @@ Persona names are data, never dispatch keys. Optional `delegation:` fields apply
33
33
  builtin, user, or project persona: `requireBrief`, `outputContract`, `requireDisjointWrites`,
34
34
  `requireFreshVerification`, and `verificationAgents`. Council behavior remains independently
35
35
  declarative through `council: { strategy, roster, params }`; a custom persona can compose any installed
36
- strategy/team without a core change. The gates use effective agent/tool capabilities, conservatively
36
+ strategy/team without a core change. Ownership intent follows the agent, not the caller: an agent definition
37
+ can declare the area it alone writes (`writeSet`, e.g. `research` → `.research/`), and a write-capable delegate leg that
38
+ names no `writeSet` inherits it before the gates run, so the disjoint-ownership and overlap checks see a
39
+ declared owner; a leg's own set wins, and the field stays declarative (nothing enforces paths at runtime).
40
+ Legs restricted to read-only tools do not inherit ownership defaults, so parallel readers do not conflict.
41
+ The gates use effective agent/tool capabilities, conservatively
37
42
  treat shell-capable, MCP-enabled, or unknown tools as potential writers, and enforce the requested fan-out
38
- concurrency in both synchronous and background execution. A serialized verifier is accepted only
43
+ concurrency on the single launch path (interactive background or headless inline). A serialized verifier is accepted only
39
44
  after every material writer in that batch; otherwise the call fails before a child starts. The rule
40
45
  also holds ACROSS calls: a verifier launched while a material-writer background run is still live is
41
- rejected with the run id to wait on, so a later call cannot slip past the in-batch ordering.
46
+ rejected, naming the mutating run ids, so a later call cannot slip past the in-batch ordering.
42
47
 
43
48
  ### The effort ladder (make the simple case simple; complexity is opt-in)
44
49
 
@@ -56,7 +61,9 @@ rejected with the run id to wait on, so a later call cannot slip past the in-bat
56
61
  You climb only when you want determinism, structure, or reuse. Nothing forces the higher tiers.
57
62
  The `council` tool is a gateway to all installed strategies, not a universal instruction to vote on
58
63
  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
64
+ persona focus. The tool description lists every strategy with its `when`, positional roster and required
65
+ params, generated from the registry (`strategyCatalog`), and a call missing a required param is refused
66
+ before any run exists. A caller can supply ephemeral `members` with inline roles/models/skills instead of a
60
67
  named team, without persisting a team or switching personas.
61
68
 
62
69
  ## Binding invariants (I1–I7)
@@ -74,9 +81,10 @@ These are the guardrails a contributor must not violate. They are enforced in co
74
81
  by the SDK on every `agent()` call and by the engine per child. Concurrency is shared across all
75
82
  calls in one SDK instance; token admission uses completed usage and is rechecked after queueing, so
76
83
  active legs can overshoot it. Nesting depth is not one of these numeric knobs — it is structural,
77
- capped at 1: a child's whole pi-persona extension activation short-circuits under
78
- `PI_PERSONA_DISABLE=1` (the **fork-bomb guard**), so it registers NO tools at all —
79
- `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
80
88
  set on each spawned process; the in-process engine instead shares one Node process across
81
89
  concurrently-building sub-sessions, so it ref-counts the guard (`inproc.ts`) to keep it set until the
82
90
  LAST one finishes. The in-process engine also excludes `ORCHESTRATION_TOOLS` from the child session
@@ -100,7 +108,10 @@ These are the guardrails a contributor must not violate. They are enforced in co
100
108
  - **I5 — Flows are the top tier.** A declarative DAG (statically verified: acyclicity, references
101
109
  resolve, join reducers valid) above strategies. The flow engine stays thin — parse, verify,
102
110
  schedule, persist, resume — and a flow node may call any strategy. Journaled: finished nodes replay
103
- from disk with no re-spend.
111
+ from disk with no re-spend. A resume matches only the same flow, the same task and the same project
112
+ (`flowRunHash` = flow + task + cwd); another task starts from scratch. A `gate: true` phase shows its
113
+ output and its dependents to the operator; a rejection returns `stoppedAtGate` (not a failure, though
114
+ `ok` stays false so mandatory orchestration still fails closed).
104
115
  - **I6 — Spawning is opportunistic by default; mandatory only when the persona declares it.** Two
105
116
  first-class cases: *opportunistic* (`mode: solo` / no block — the supervisor MAY delegate by its own
106
117
  judgement; never forced) and *mandatory* (`mode: strategy|flow|parallel|pipeline` with a roster —
@@ -141,6 +152,12 @@ These are the guardrails a contributor must not violate. They are enforced in co
141
152
 
142
153
  ## Module layout
143
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
+
144
161
  Domain dependencies remain downward-only and acyclic: `core ← all`; `engine`/`bus → core`;
145
162
  `orchestration → engine + bus + core`; `persona → orchestration + core`; the Exocom transport and
146
163
  ledger → `core + bus`; `tools`/`ui → lower layers`. `src/exocom/install.ts` is deliberately the
@@ -161,8 +178,9 @@ engine, telemetry, UI, and tool surfaces; it is not part of the transport/domain
161
178
  `peerDependencies`, `/doctor`'s `pi:` line, and the README's stated floor trace back to this
162
179
  constant), `models`, `brief` (`buildDelegationBrief` — the per-turn delegation brief: live roster +
163
180
  standing hand-off default; and `buildExocomBrief` — the per-turn peer brief: live peers, the
164
- peer-vs-sub-agent split, and the relevance bound on an exchange), `nudge` (the two
165
- runtime-reinforcement state machines, `DelegationNudge` + `PersistenceNudge`), `display-label`
181
+ peer-vs-sub-agent split, and the relevance bound on an exchange), `nudge` (the
182
+ runtime-reinforcement state machines `DelegationNudge` + `PersistenceNudge`, and the call-time wake
183
+ guards `PollGuard` + `longWaitSeconds`), `display-label`
166
184
  (`sanitizeDisplayLabel` — an untrusted name reduced to bounded metadata before interpolation
167
185
  outside a fence), `timer` (`TimerScheduler` — the pure alarm engine behind the `timer` tool), `time`
168
186
  (`formatDuration` / `peerSentLabel` / `sessionElapsedLabel` / `buildSessionAnchor` — the elapsed-time
@@ -205,7 +223,7 @@ engine, telemetry, UI, and tool surfaces; it is not part of the transport/domain
205
223
  `spine.ts` (the shared behavioral layer's SOURCE resolution — docs/SPINE.md; composition sits in
206
224
  `persona.ts` for supervisor turns and in the engines for delegated legs).
207
225
  - **`src/tools/`** — `delegate.ts`, `intercom.ts`, `exocom.ts` (the `exocom_list`/`exocom_send`
208
- tools), `exocom-work.ts` (claim/ask/answer/decline/wait/release/progress). **`src/ui/`** — agent tree/overlay, model picker, `presentation.ts` (the shared
226
+ tools), `exocom-work.ts` (claim/ask/answer/decline/wait/release/progress). **`src/ui/`** — agent tree/overlay, model picker, `wake-rows.ts` (the monitor/timer → tree-row projection), `presentation.ts` (the shared
209
227
  card-compaction/sanitization helpers behind the projection rules below), `usage.ts`
210
228
  (token/usage formatting, `ChildUsageLedger` / `toToolUsage` — sub-agent spend is
211
229
  attached as Pi `toolResult.usage` so the footer and pi-theme-1337's frame count it;
@@ -271,9 +289,12 @@ up to 16 names. Same idea, two layers, two caps — see docs/REFERENCE.md's exam
271
289
  `isolation: worktree` is fail-closed. It requires a clean Git checkout so the detached `HEAD` view
272
290
  cannot silently omit staged/unstaged/untracked supervisor work; a non-repository, dirty checkout,
273
291
  worktree creation failure, missing diff artifact, or an artifact over the return limit fails the leg —
274
- the base engine is never invoked against the real tree as a fallback. The diff returns to the
275
- supervisor before the temporary tree is removed, and every git call is asynchronous so the extension
276
- 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.
277
298
 
278
299
  Transient retries inside one agent session belong to the host **Pi runtime**'s `retry.*` settings, not
279
300
  a persona contract — pi-persona neither parses retry notation nor schedules its own backoff. Both
@@ -314,6 +335,14 @@ Running engines retain their captured assignments. Missing credentials are class
314
335
  adapter boundary; only the SDK decides whether a main-model recovery is admissible. This separation
315
336
  avoids a global reset or automatic repinning that could silently change billing and panel diversity.
316
337
 
338
+ The extension owns the derived candidate cache, keyed by session and base provider/model. Ordinary
339
+ turns reuse it; explicit catalog refresh invalidates the initiating key even after partial provider
340
+ failure. The settings UI supplies the refresh deadline and reporting, while the extension's refresh
341
+ adapter captures the key before awaiting the host. Briefs and nudges share the same callable-`models`
342
+ gate. Automatic model setup for tools, commands and mandated orchestration shares the checkpoint
343
+ dialog queue; each run registers its stop handle before entering that queue. Aborting setup discards
344
+ its unsaved choices and removes only prompt markers created by that attempt, so it can be retried.
345
+
317
346
  **Unified settings.** `/persona-settings` is independent of Pi's `/settings`: full-page TUI, native
318
347
  RPC dialogs, and a read-only `show` report. `core/settings.ts` is the public preference registry and
319
348
  validator; the UI and environment precedence consume it rather than maintaining duplicate field lists.
@@ -435,8 +464,9 @@ decides how precise each may be is a **prompt-cache** rule, not a taste one:
435
464
  - **Tail surfaces are free.** A tool result or a delivered message is written once and never re-sent
436
465
  as the cached prefix, so precision costs nothing there. An inbound peer delivery dates itself
437
466
  (`[rune] — reply · sent 20m ago`), and every settled leg reports its wall time on every
438
- tracker-backed path — the done/failed/stopped report, the join, `intercom result`, and each
439
- settled line of a peek digest.
467
+ tracker-backed path — the done/failed/stopped report, `intercom result`, and each
468
+ settled line of a peek digest; a running line ends its turns/tokens parenthesis with
469
+ `· running for <duration>` (not shown while queued).
440
470
  - **The system prompt is not.** It is re-composed and re-sent every turn and IS the cached prefix,
441
471
  so a minute-granular value there rewrites it every minute and throws away the provider's cache of
442
472
  everything before it. The **session anchor** therefore reads on a deliberately coarse ladder —
@@ -471,7 +501,7 @@ never simultaneously a telemetry event, a routed message, and a UI source of tru
471
501
  a source of truth. The `f9` agent tree and `peek` digest are ProgressViews.
472
502
 
473
503
  Steering is a supervisor-to-child control action, distinct from semantic Bus messages; requesting
474
- steering does not establish that an active blocking join has been interrupted. The peek digest is
504
+ steering does not establish that a blocking headless delegate call has been interrupted. The peek digest is
475
505
  always a read-only ProgressView.
476
506
 
477
507
  External telemetry is not a fourth communication plane: it is a generic observer/export contract that
@@ -481,12 +511,17 @@ events and cannot route, reply, steer, or otherwise control agents.
481
511
  ## The comm plane in practice
482
512
 
483
513
  - **In-process bus** (`bus/inproc.ts`) — a handle-based mailbox: `send` (one-way), `ask` (blocks for a
484
- 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
485
516
  `contact_peer` (sibling→sibling) are the child-side tools bound onto it. Each unread inbox holds at
486
517
  most 200 messages; overflow evicts an old one-way note first, and an inbox full of live asks rejects
487
518
  new delivery rather than stranding a sender. Ask settlement (reply, timeout, abort, departure, or
488
519
  failed delivery) releases its timer/listener and unread entry; a previously retained question stays
489
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.
490
525
  - **Sibling peer comm** — a strategy opts a run in via `AgentRunSpec.peers` (gated by `canUseBus`). The
491
526
  child gets `contact_peer` (`list`/`send`, ONE-WAY so peers can never deadlock; per-engine-instance
492
527
  scoping; a 20-send budget and an 8,000-character body limit, enforced before consuming a send). The
@@ -509,6 +544,10 @@ events and cannot route, reply, steer, or otherwise control agents.
509
544
  spawn without a bus endpoint, so they never burn connect backoff against a dead socket), and the next
510
545
  build retries; `/doctor` surfaces its state on one line. A dropped client connection flips the
511
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.
512
551
 
513
552
  ### Presentation is a projection, not another comm plane
514
553
 
@@ -524,36 +563,103 @@ for the supervisor and for explicit retrieval; the default TUI projection is del
524
563
  Oversized or evicted messages return an unavailable diagnostic — retention is not durable evidence
525
564
  storage;
526
565
  - collapsed delegate/intercom/council/flow cards show state, identity, a short sanitized preview, and
527
- Pi's expand-key hint, each surfacing failure at the top in its own shape: the delegate card **sorts**
528
- failed legs first, a failed council/flow card leads with the cause in its title, and an
529
- `intercom wait` card leads with the `N settled — X done, Y failed` tally (per-leg causes sit in the
530
- body, below the preview cut, so `wait`'s own success doesn't get a `failed` prefix);
566
+ Pi's expand-key hint, each surfacing failure at the top in its own shape: the delegate card
567
+ names each failed leg on its own line, and a failed council/flow card leads with the cause in its title;
531
568
  - async launch cards and targeted Intercom call headers use the worker's canonical alias/model,
532
569
  matching the tree rather than promoting its routing ID to display identity. Launch/control receipts
533
570
  persist identity snapshots for history rendering; aliases never become routing keys. Colliding
534
571
  aliases carry a secondary ID, and legacy receipts without a recoverable name retain their ID fallback.
535
572
  Accepted steering receipts retain the exact sent message: collapsed cards preview it, expansion
536
573
  shows all sanitized text and the diagnostic ID. A queued receipt does not prove the worker acted;
574
+ - async failures append a `pi-persona-run-failure` entry in TUI mode, with alias, agent/model,
575
+ elapsed time and a concise cause. The entry stores the original diagnostic for expanded history
576
+ rendering; it is operator-only and adds no model context or wake. Other modes, or an append
577
+ failure, retain a bounded notification. The semantic completion follows its existing single-wake path;
537
578
  - follow-up cards (`pi-persona`, exocom) retain their complete semantic content but render a bounded
538
579
  preview until expanded; terminal escape/control sequences are removed from the visible projection;
539
- - the sticky agent and exocom widgets have fixed row budgets — F9/`/agents` and paginated
580
+ - custom follow-ups and operator-only entries share `ui/session-cards.ts`: glyph-free generated
581
+ headings, explicit textual state, one width-aware layout and semantic theme backgrounds. Neutral
582
+ information/unknown state uses `customMessageBg`, verified completion `toolSuccessBg`, attention
583
+ (nudge/stall/stopped) `toolPendingBg`, and verified failure `toolErrorBg`. Only runtime-owned status
584
+ evidence selects chrome, never peer/child prose. Colors are resolved from the active theme at render
585
+ time (including pi-theme-1337); normal tools keep Pi's own shell rather than gaining a duplicate box.
586
+ Expanded prose reports use the native Markdown projection in `ui/report-body.ts` (including visible
587
+ trust blockquotes); receipts, live output, monitor/timer notices and raw diagnostics stay literal.
588
+ Source payloads and fences remain intact. The native Markdown component is retained per report and
589
+ invalidated for theme/layout changes, without a global transcript cache. Rendering never rewrites
590
+ stored or model-facing payloads, nor creates a wake;
591
+ - literal control receipts and fallback errors share `ui/literal-tool-result.ts`: bounded collapsed
592
+ text, an expansion hint only when detail is hidden, and full sanitized expansion. Tool errors use
593
+ the host's `ToolRenderContext.isError`, not a presumed result field; semantic negative peer answers
594
+ remain successful retrievals. Retained `exocom_wait` answers are prose reports and render Markdown
595
+ when expanded. Native tool shells remain host-owned;
596
+ - the sticky agent and exocom widgets have fixed physical-row budgets: the shared width-aware
597
+ `ui/compact-widget.ts` clips each already-budgeted row instead of letting native Text wrap it.
598
+ F9/`/agents` and paginated
540
599
  `exocom_list({ offset, limit })` are the explicit detail surfaces, so a wide fan-out cannot push the
541
600
  editor off screen or dump an entire peer registry into model context. Agent-tree deltas share one
542
601
  16 ms widget composition per terminal frame; identical lines/status are not republished, hierarchy
543
602
  rendering uses one adjacency index, and the F9 overlay rebuilds lazily when Pi paints rather than
544
603
  once per streamed token;
545
- - `/flow` and `/orchestrate` append durable, TUI-only expandable result entries rather than dumping a
546
- large notification or a second copy into the model context;
604
+ - `/flow` and `/orchestrate` append a durable, TUI-only expandable result card rather than a large
605
+ notification; the supervisor gets the same text (fenced, 12k-bounded) once, as a passive next-turn message;
606
+ - an interactive `tasks` fan-out of two or more legs gets one container row, `delegate · N legs`
607
+ (`k/N settled`), that never counts as an in-flight agent and leaves with its last leg;
608
+ - `X` is not a directed key: it acts on the whole stoppable set of leaf rows after a confirmation that
609
+ states how many agents it covers, and a changed set is re-confirmed. The set is every agent leaf (wake
610
+ rows excepted) plus any run root with a stop handle (council/flow/mandated runs), and `x` on such a
611
+ root stops the whole run.
612
+ `intercom stop` / `stop { to: "all" }` also cover tracker runs, `council` and `flow` included;
547
613
  - the F9 overlay's *directed* keys (`x` stop, `s` steer) act only on the agent the ▸ marker still
548
614
  shows; if that agent settles and is pruned mid-keystroke, the selection re-anchors visibly and the
549
615
  keypress is spent re-aiming instead of aborting an agent the user never chose. ↑↓, ⏎, and the scroll
550
616
  keys never refuse;
551
617
  - every agent-tree node carries `startedAt`/`lastAdvanceAt`; `LiveClock` (`src/ui/live-clock.ts`)
552
618
  repaints the sticky widget and the F9 overlay once a second while any node is running and stops
553
- itself once nothing is. A running row shows elapsed time until it goes `STALL_FLAG_MS` (90s) quiet,
619
+ itself once nothing is. Running/completed usage shares `formatUsage`: `↑` input and `↓` output,
620
+ using copied existing cumulative usage snapshots, never context occupancy. A legacy progress source
621
+ providing only an aggregate shows a neutral compact count without `tok` or a fabricated direction. A running row shows elapsed time until it goes `STALL_FLAG_MS` (90s) quiet,
554
622
  then the same `⚠ stalled <duration>` badge the async peek watchdog uses. `x` marks the target
555
623
  `stopping…` at once and is offered only when a live stop handle exists; a refusal (no handle,
556
- already settled) surfaces as a one-line notice instead of silently no-op'ing.
624
+ already settled) surfaces as a one-line notice instead of silently no-op'ing;
625
+ - armed timers and active monitors are **wake rows** in the same tree (`src/ui/wake-rows.ts`, ids
626
+ `monitor:<id>` / `timer:<id>`): rows, not agents. The countdown replaces the stall clock: a timer
627
+ shows `fires in <d>` only, a monitor keeps its elapsed time (`<elapsed> · deadline in <d>`), so
628
+ neither carries the `⚠ stalled` badge or `waiting for supervisor`. `x` cancels
629
+ one through the same stop registry as an agent (the `monitor cancel` / `timer cancel` effect); `s` steer
630
+ is never offered. They are excluded from the `X` stop-all count, confirmation and bulk stop, from the
631
+ `N agents` in-flight status, and from telemetry (`agent.*` events are agents only). They settle and
632
+ leave like an async row: the final ✓/✗/■ goes to the tree change stream (telemetry excluded), not to
633
+ the screen, because the update and the removal are synchronous and the widget frame is coalesced. They
634
+ live only in the arming session, never restored. Raw monitor stdout/stderr has a separate passive
635
+ observer: raw chunks feed bounded sanitized tails immediately, but full row/preview projection is
636
+ coalesced per dirty monitor into the existing 16 ms widget frame, before its tree snapshot. Terminal
637
+ events and cancellation publish the pending final tail synchronously; teardown clears pending state.
638
+ This updates the widget and F9 without semantic events, model wakes, event-budget consumption or
639
+ stall-clock credit. The same observer feeds a bounded session-native log
640
+ archive (`src/monitor/archive.ts`): one plain custom entry per terminal/cancelled run, no sidecars or
641
+ per-chunk writes. `monitor result` retrieves retained captures across reload/resume from the whole
642
+ session tree, including sibling conversation branches: this archive is session-wide, deliberately
643
+ not a projection of the active branch. Branch navigation changes neither visibility nor the shared
644
+ lifetime budget. Restored logs never restore processes. Activation-unique monitor ids avoid historical result collisions. A finite
645
+ lifetime serialized-byte/count budget includes all previous entries of the archive type, even
646
+ unreadable versions. Session-file deletion owns log deletion; ordinary shutdown saves diagnostics,
647
+ not deletes them. Operator-only expandable log entries do not create model context or extra wakes.
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.
557
663
 
558
664
  This is a UI invariant only: truncating a collapsed card must never be confused with truncating the
559
665
  underlying result or changing a strategy's contract.
@@ -568,17 +674,22 @@ and message peer-to-peer, with no parent/child relationship. (intercom = interna
568
674
  external comm.)
569
675
 
570
676
  Exocom supplies fenced one-way postcards and presence; chat `message` never mutates shared work
571
- state. Separately, a workspace work ledger (`ledger.ts`, tools in `tools/exocom-work.ts`) records
572
- claims/asks/answers/progress/releases. `exocom_ask` canonicalizes its target through
573
- `plane.resolvePeer` (raw session ids on the ledger, never the display name); registry display name is
574
- keyed by `from_session`, never the envelope's self-reported `from_name`. A pending ask constrains the
575
- turn (`gate.ts`: answer/decline plus read-only tools) behind a bounded `pendingAskPrompt`.
576
- `exocom_wait` is non-blocking. Clean shutdown releases this session's claims/asks; if that best-effort
577
- write fails, the vanished owner is pruned on the next ledger transaction (live registry sessions are
578
- the lease), and `expireWaiters` wakes any waiter left behind instead of leaving `exocom_wait` armed
579
- forever. This gate is cooperative coordination for participating local Pi processes — not filesystem
580
- authorization, not isolation from another same-user process, and not a delegate/council or task/run
581
- 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.
582
693
 
583
694
  - **Opt-in, OFF by default.** `PI_PERSONA_EXOCOM=1` or bare `--exocom` joins the current workspace;
584
695
  `--exocom=Ab0T` joins an existing scope by its exact, case-sensitive four-character Base62 alias
@@ -605,18 +716,31 @@ replacement.
605
716
  standing brief, and `/exocom` label a peer as same-workspace or external without exposing its
606
717
  absolute `cwd`. External peers are full Pi instances that can inspect files in their own workspace;
607
718
  paths are never implicitly shared.
608
- - **Postcards plus a durable, non-blocking join.** `exocom_send` returns a `msg_id` immediately; a
609
- reply is another `exocom_send` with `in_reply_to` set. `target: "*"` broadcasts postcards
610
- best-effort (one unreachable peer doesn't fail the rest). Work coordination is separate:
611
- `exocom_ask` commits an obligation to the ledger, `answer`/`decline` settles it, and `exocom_wait`
612
- arms a bounded idle wake rather than blocking. The signed frame is a wake signal; the ledger remains
613
- 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.
614
737
  - **Cross-workspace writes are advisory-only in v1.** A repository-relative claim path has meaning
615
738
  only in the workspace that owns the selected scope, so a member whose home workspace differs from
616
- the scope cannot `exocom_claim` (the tool and the signed-frame receiver both reject a foreign
617
- claim) — it can still send, inspect its own files, ask, answer/decline, wait, and release its own
618
- asks. Multi-repository write coordination would need a versioned resource namespace; paths from
619
- 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.
620
744
  - **Reply routing is session-stable.** `exocom_list` shows human display names (`name`/`name#2`);
621
745
  sends and inbound reply hints use `name@<96-bit session hash>`, so a retained qualified target
622
746
  stays valid after a rename. Ambiguous suffix matches fail closed rather than guess. The
@@ -655,15 +779,23 @@ replacement.
655
779
  double-trigger a turn. An exchange's LENGTH is instead bounded prompt-side: the per-turn peer brief
656
780
  (`core/brief.ts`) carries a relevance bound — send only what changes what someone does — deliberately
657
781
  not a round count, since a counter can't see whether a round still serves the work.
658
- - **Tools are lazy and fail closed.** `exocom_list({ offset?, limit? })` exposes bounded, paginated
659
- presence; `exocom_send({ target, message, in_reply_to? })` sends one-way (a session-qualified-looking
660
- target resolves only as one, never falling back to a display name, since names are self-chosen);
661
- `exocom_name({ name })` rebrands the display call-sign without moving any state. The ledger tools —
662
- `exocom_claim`, `exocom_ask`, `exocom_answer`, `exocom_decline`, `exocom_wait`, `exocom_progress`,
663
- `exocom_release` — are capability-gated with the postcard tools; the live accessor, capability gate,
664
- and active-tool set all deny them once the plane stops, since Pi has no dynamic tool-unregister API.
665
- Admission requires at least one obligation closer, because every published peer may receive a
666
- 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.
667
799
  - **Inbound delivery is bounded without loss.** Each message is injected under the same byte cap
668
800
  whether plain text or an artifact descriptor. Bursts stay FIFO-queued; each rate-limited wake drains
669
801
  as many whole messages as fit, leaving the rest queued. A presentation cap on the collapsed card
@@ -679,38 +811,72 @@ a local process able to modify that directory is already inside the trust bounda
679
811
 
680
812
  ## Supervision & the waiting model
681
813
 
682
- - **sync** — the supervisor actively blocks on the delegate/strategy call (results still stream); no
683
- idle/peek/steer.
684
- - **async** — interactive delegation is background-first; headless execution remains synchronous
685
- by default. The prompt/runtime guidance is to continue independent work and, when only background
686
- children remain, end the turn for automatic completion follow-ups rather than monitor or join the
687
- whole batch. `intercom wait` collects a snapshot without blocking by default in interactive/RPC
688
- sessions; settled results are reported and running children continue. Use `sync: true` for an
689
- intentional bounded join. Headless sessions preserve the blocking default; `sync: false` requests a
690
- snapshot. A snapshot does not wait, start timers/input hooks, or cancel children. Queued steering
691
- does not release an explicitly active join. Input-triggered join interruption is
692
- deliberately not implemented: a real Pi 1.0 SDK characterization shows steering can remain dormant
693
- if delayed input hooks outlast the active run; no supported post-admission ordering guarantee
694
- has been established. The supervisor otherwise returns control and
695
- goes idle until a wake; the **peek watchdog** fires while async children run but stays SILENT unless
696
- there is something to act on — a healthy background run should not interrupt. Every newly detected stall
697
- episode (`STALL_FLAG_MS`, 90s) appends a durable, expandable operator-only warning entry, including while
698
- the supervisor is busy. Interactive stalls never wake the model. In headless sessions, the first stall
699
- episode may produce one focused patience-first model wake per run lifetime (ask the leg, don't probe its
700
- environment); later stall episodes for that run do not re-arm the model-wake budget. Unread messages to the
701
- supervisor retain their existing wake path. The **slow routine status card** (`PI_PERSONA_CHECKIN_MS`,
814
+ There is no `sync`/`async` switch on `delegate` and no `intercom wait`; the mode follows the session.
815
+
816
+ - **Headless (`ctx.hasUI !== true`, e.g. `pi -p`)** — the supervisor blocks on the delegate, council or
817
+ flow call and gets results inline (returned once, unclipped; progress is only a `k/N done` counter), because nothing drains a follow-up after the
818
+ single turn exits. A runtime constraint, not a model-facing choice. The legs are still tracked, but
819
+ the status monitor stays off: the model is parked in the call and nobody reads a card.
820
+ - **Interactive (TUI and RPC)** — delegation is ALWAYS background: the call returns run ids at once
821
+ and each result returns as an idle-gated completion follow-up (`intercom result { to: runId }`
822
+ retrieves a settled run; `peek`/`steer`/`stop` stay available, which a blocking call could never
823
+ offer). With only background children left, the supervisor ends its turn and goes idle until a wake;
824
+ it never sleeps, polls, or arms a `timer` to wait for a child or a monitor. The **peek watchdog**
825
+ fires while async children run but stays SILENT unless there is something to act on. A newly
826
+ detected stall (`STALL_FLAG_MS`, 90s) appends a durable, expandable operator-only card, even while
827
+ the supervisor is busy, and never wakes the model; unread messages to the supervisor keep their
828
+ wake path. The **slow routine status card** (`PI_PERSONA_CHECKIN_MS`,
702
829
  ~5 min, `0` disables) is a durable, expandable operator-only session entry with the compact ProgressView
703
830
  digest — never full transcripts — and does not start a model turn. Live supervisor asks
704
831
  are derived from the exact engine handle's optional `isWaitingForSupervisor` predicate: tree/F9/peek
705
832
  show waiting, and the watchdog suppresses false stalls until the ask settles. Parent clocks advance
706
833
  with child engine events (liveness, not proof of useful work); parents are waiting only when every
707
- live descendant branch is waiting.
708
- Stall alerts and live messages let an idle supervisor steer/stop a wedged child even with NO completion
709
- fired; the enforcing backstop is the engines' hard wall-clock cap (above). The full digest is also on demand
710
- via `/peek`.
834
+ live descendant branch is waiting. The peek watchdog's clock is stricter (`madeHeadway` in
835
+ `engine/async.ts`): a run advances only when assistant output, tokens or turns grew or a tool call
836
+ completed, and raw tool-output chunks earn nothing, so a leg whose tool streams forever (`npm run dev`,
837
+ `tail -f`) raises the operator-only stall card. A false advisory on a legitimately noisy long tool is
838
+ accepted; nothing is killed and the model is not woken. A council or flow run is credited per leg. The
839
+ tree/F9 clocks stay event-based.
840
+ The operator (or a manual `/peek`) can steer/stop a silent child. The supervisor's net for one is the
841
+ engine idle window (`RUN_LIMITS.timeoutMs`, 180s; startup deadline 300s), which kills it and returns a
842
+ failed completion follow-up whose text says to raise the leg's `timeoutMs`; the opt-in hard wall-clock cap (`PI_PERSONA_AGENT_MAX_MS`, off by
843
+ default) catches a busy non-converging leg.
844
+ Two call-time guards (under `config.nudge`, `PI_PERSONA_NUDGE=off` disables both) stop the in-turn
845
+ waits that would block those wakes: a repeated `intercom peek`/`result` whose answer has not changed
846
+ (per scope; `steer`/`stop` reset it) is refused, then asks Pi to end the turn (`terminate` is a hint
847
+ Pi honours when the peek is the batch's only call); a leading long `sleep`/`Start-Sleep` (>= 20s) is
848
+ stopped while background children run or are being launched (interactive only; a sleep prepared
849
+ before a same-batch `delegate` is an accepted false negative). A `delegate` fan-out (`tasks`) is
850
+ held and delivered as ONE completion follow-up when its last member settles, with every failed leg
851
+ named and clipped on its own line; single legs and separate calls are unchanged. Held reports are
852
+ dropped at session shutdown or reload, like the running legs, and the `PI_PERSONA_ASYNC_RETAIN`
853
+ default (64, the `tasks` cap) covers one whole call so early finishers stay fetchable. In headless `pi -p` the
854
+ `timer`/`monitor` arm text says the process exits when the turn settles, so such a wake lands only
855
+ if it fires before then.
856
+ **`council` and `flow` are the same kind of run** (`tools/background-run.ts`, `createBackgroundRuns`).
857
+ Interactive, each is ONE tracker run rooted at `async:<runId>`: a receipt now, ONE completion follow-up
858
+ (`intercom result { to: runId }` for the full payload), and the same `peek`/`stop`/`stop all`/f9 reach
859
+ as a delegate leg. Headless, the same run is launched and joined inside the tool call. Esc no longer aborts
860
+ one; stop it by key. Ownership of the limits: a run holds ONE slot of the delegate `maxConcurrency` launch
861
+ pool for its whole life (`queued` until granted; a flow keeps it while parked at a checkpoint); the strategy
862
+ SDK's per-run semaphore gates the legs INSIDE it, so members never compete with delegate legs for pool
863
+ slots; token budgets, the child limit, the delegation ledger and the fork-bomb guard stay in the SDK and
864
+ engine. A flow parked at a checkpoint is blocked on the OPERATOR: the run carries `awaiting`, peek reads
865
+ `waiting for operator at checkpoint <id>`, the stall watchdog skips it and nothing wakes the model; a
866
+ rejected checkpoint settles the run `stopped` and the plan reaches the model as its partial output. A
867
+ persona's `council:` defaults resolve unchanged when the call omits strategy, roster and params. The
868
+ mandated `orchestration:` path stays PRE-turn and inline (its result is the supervisor's first answer),
869
+ as do the `/orchestrate` and `/flow` slash commands; those runs register their own abort under their root
870
+ row, so shutdown ends the whole run. Operator-run results also reach the supervisor's next turn as a
871
+ passive, fenced, 12k-bounded message.
711
872
  Progress tokens are labelled **cumulative input/output**, not context occupancy; cache-read/write
712
873
  accounting is separate. Do not infer a full context window from that counter. File age or line-count
713
874
  changes alone likewise do not establish a stall, a passing test or completed work.
875
+ **Restore honesty:** `session_shutdown` records lost run ids, timers, monitors and the run-id counter
876
+ in a `pi-persona:runs` session entry (`core/session-runs.ts`). The next `session_start` (reload,
877
+ in-session resume, `pi -c`; not fork or `/new`) tells the supervisor once through a passive `nextTurn`
878
+ message, acknowledges it, and continues run ids so they never restart at `run-1` within a session.
879
+ Only ids matching a strict shape and capped counts are ever read back.
714
880
  Async failures are ALWAYS reported; the runtime `DelegationLedger` vetoes a blind retry loop (an
715
881
  identical agent+model+task delegation that failed twice is stopped before it spawns). Coaching is
716
882
  gated by `coaching: on` AND `canUseBus`.
@@ -718,26 +884,42 @@ a local process able to modify that directory is already inside the trust bounda
718
884
  **Runtime reinforcement of the hand-off default** comes as a standing part and a reactive part, because
719
885
  a persona directive lives at the TOP of the prompt and its pull decays as recent tool output balloons:
720
886
 
721
- - The **delegation brief** (`core/brief.ts`) is the STANDING half: live roster (agents + teams + flows)
722
- and the hand-off default, appended to the system-prompt TAIL every turn, where recency wins the
887
+ - The **delegation brief** (`core/brief.ts`) is the STANDING half: live roster (agents + teams + flows, with descriptions;
888
+ gated flows are tagged in headless sessions) and the hand-off default (the supervisor's role: it holds the
889
+ objective, the plan and the judgement, normally handing bounded independent work to supervised legs;
890
+ quick, trivial or tightly coupled work can stay inline when hand-off overhead would outweigh the benefit; one decision-class →
891
+ method line that a test checks against the strategy registry; model-sizing guidance and a cached provider
892
+ candidate list), appended to the system-prompt TAIL every turn, where recency wins the
723
893
  tug-of-war a top-of-prompt line loses. It regenerates from the live registry (so it can't desync) and
724
- filters to the active persona's `delegate` allowlist. It never dictates how MANY sub-agents or which
725
- shape — that is each persona's own method.
894
+ filters to the active persona's `delegate` allowlist. It never dictates how MANY sub-agents; beyond the one class line,
895
+ the shape is each persona's own method.
896
+ The model snapshot is acquired once per session/base-model reference, refreshed on a base-model change
897
+ or explicit `models` call, and reused across turns. Recommendations first stay near the current
898
+ model's provider, textual name root and numbered major generation, then order distinct variants by
899
+ relative cost band and numeric version. They do not fill spare slots with older or unrelated models.
900
+ This affects discovery only; exact assignment and search retain the full eligible catalog.
901
+ Relative cost hints use host prices against an uncapped recent-provider reference, never a
902
+ model-name-to-tier table, and leave missing or zero-price metadata unknown. The LLM chooses task fit.
726
903
  - The **nudges** (`core/nudge.ts`, on by default) are the REACTIVE half, landing in RECENT context on
727
904
  the event that warrants them:
728
905
  - **DelegationNudge** — a `tool_result` hook watches the supervisor's own tool stream and, when a
729
- delegating persona grinds heavy work by hand (output burn since the last `delegate`/`council`
730
- crosses a threshold), appends a compact labelled checkpoint (`Trigger` / `Scope` / `Action`) to
906
+ delegating persona grinds heavy work by hand (output burn since the last `delegate`/`council`/`flow`
907
+ crosses a threshold: 3 substantive calls and ~1k tokens), appends a compact labelled checkpoint
908
+ (`Trigger` / `Scope` / `Action`, with four delegate targets named in the action, those the persona's own prompt mentions first, and a
909
+ `Model candidates` line for the session provider) to
731
910
  that command's result for the model and mirrors it into a durable, expandable
732
911
  `pi-persona-nudge` custom entry for the operator. The entry is TUI-only (no duplicate model
733
912
  context or turn) and guarantees visibility even when a built-in/custom tool renderer suppresses
734
913
  result `content`. A successful hand-off resets the streak; a failed one keeps it and returns the
735
914
  same visible re-dispatch checkpoint.
915
+ - `flow` is a hand-off beside `delegate`/`council`: a successful flow resets the by-hand streak and a
916
+ failed one gets no re-dispatch checkpoint (its failure text is self-describing).
736
917
  - **PersistenceNudge** — the counterweight to premature surrender: a delegated leg's report carrying
737
- `[BLOCKED]`/`FLAG: UNKNOWN` gets a "don't bank it yet" reminder. All three delivery paths carry it
738
- (sync `delegate`/`council`, background completion, `intercom wait`), but coverage isn't identical:
918
+ `[BLOCKED]`/`FLAG: UNKNOWN` gets a "don't bank it yet" reminder. Three delivery paths carry it
919
+ (the inline `delegate`/`council`/`flow` result — headless only — background
920
+ completion, and an explicit `intercom result` of a done run), but coverage isn't identical:
739
921
  the sync path scans the WHOLE tool result including failed legs, while `renderCompletion` scans
740
- only `status === "done"` runs by design — a failed background/`wait` leg already gets a failure
922
+ only `status === "done"` runs by design — a failed background leg already gets a failure
741
923
  block, so a `[BLOCKED]` marker there doesn't also get the persistence note. Same marker, same leg,
742
924
  different counterweight depending on how it was collected.
743
925
  Nested calls through codemode's `ctx.executeTool` leave their programmatic result untouched.
@@ -745,8 +927,9 @@ a persona directive lives at the TOP of the prompt and its pull decays as recent
745
927
  used to infer nesting. A bounded set of parent call ids carries report provenance through wrappers
746
928
  only while their output relays a surrender marker; the outer model-visible result alone gets the
747
929
  note/card. Suppressed reports produce neither, and turn/session settlement clears abandoned relay state.
748
- - **The off switch covers every path.** `PI_PERSONA_NUDGE=off` silences DelegationNudge entirely and
749
- PersistenceNudge on every collection path (sync result, background notifier, `intercom wait`).
930
+ - **The off switch covers every path.** `PI_PERSONA_NUDGE=off` silences DelegationNudge entirely,
931
+ PersistenceNudge on all three delivery paths (inline result, background completion, `intercom result`),
932
+ and both call-time wake guards (`PollGuard`, the long-sleep block).
750
933
 
751
934
  ## Discovery & seeding
752
935
 
@@ -763,6 +946,11 @@ a persona directive lives at the TOP of the prompt and its pull decays as recent
763
946
  pristine prior copy and preserves edited, linked, or unknown files. A migration can declare a bundled
764
947
  agent dependency; that missing file is installed first with no-clobber atomic creation, or the parent
765
948
  remains on its old bytes. Upgrades, dependency installs, and warnings are exposed in `/doctor`.
949
+ The legacy digest table (`LEGACY_SEEDED_DEFAULTS`) holds the LF-checkout bytes of every shipped
950
+ elite/swarm/MAGI-core version from v1.11.0, plus the v1.17.1 copies of every bundled persona. Release prep must add the
951
+ previous tag's digest for each changed persona/agent; the guard test in `seed.test.ts` prints the
952
+ row (and skips in CI's depth-1 checkout). With zero personas installed, F8 and `/doctor` point to
953
+ `/persona seed`, like `/persona list`.
766
954
  Contracts/presets/teams keep a builtin layer (they aren't personas).
767
955
 
768
956
  ## Cross-OS constructs