@aeondave/pi-persona 1.15.2

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 (165) hide show
  1. package/AGENTS.md +249 -0
  2. package/CHANGELOG.md +260 -0
  3. package/LICENSE +21 -0
  4. package/README.md +172 -0
  5. package/agents/balthasar.md +32 -0
  6. package/agents/casper.md +33 -0
  7. package/agents/evidence-verifier.md +23 -0
  8. package/agents/melchior.md +32 -0
  9. package/agents/operator.md +63 -0
  10. package/agents/research.md +58 -0
  11. package/agents/reviewer.md +49 -0
  12. package/agents/scout.md +23 -0
  13. package/agents/verifier.md +21 -0
  14. package/assets/banner.png +0 -0
  15. package/assets/demo1.gif +0 -0
  16. package/assets/demo1.mp4 +0 -0
  17. package/assets/workflow.png +0 -0
  18. package/contracts/finding.contract.json +9 -0
  19. package/docs/ARCHITECTURE.md +727 -0
  20. package/docs/EXPERIENCE.md +96 -0
  21. package/docs/MONITORS.md +84 -0
  22. package/docs/README.md +53 -0
  23. package/docs/REFERENCE.md +408 -0
  24. package/docs/SPINE.md +152 -0
  25. package/docs/STRATEGIES.md +215 -0
  26. package/docs/TELEMETRY.md +9 -0
  27. package/flows/deep-review.flow.json +8 -0
  28. package/flows/gated-build.flow.json +9 -0
  29. package/package.json +106 -0
  30. package/personas/audit.md +29 -0
  31. package/personas/dev.md +52 -0
  32. package/personas/elite.md +256 -0
  33. package/personas/judge.md +32 -0
  34. package/personas/magi.md +41 -0
  35. package/personas/planner.md +37 -0
  36. package/personas/researcher.md +37 -0
  37. package/personas/swarm.md +38 -0
  38. package/personas/verify.md +31 -0
  39. package/presets/magi-rounds.preset.json +5 -0
  40. package/prompts/spine.md +11 -0
  41. package/prompts/spine.worker.md +9 -0
  42. package/src/agents/agent.ts +78 -0
  43. package/src/bridge.ts +209 -0
  44. package/src/bus/broker/client.ts +379 -0
  45. package/src/bus/broker/framing.ts +43 -0
  46. package/src/bus/broker/host.ts +407 -0
  47. package/src/bus/broker/messages.ts +87 -0
  48. package/src/bus/broker/paths.ts +31 -0
  49. package/src/bus/contact.ts +145 -0
  50. package/src/bus/inproc.ts +279 -0
  51. package/src/bus/peers.ts +114 -0
  52. package/src/core/brief.ts +356 -0
  53. package/src/core/capabilities.ts +114 -0
  54. package/src/core/config.ts +228 -0
  55. package/src/core/contract.ts +391 -0
  56. package/src/core/data-root.ts +260 -0
  57. package/src/core/discovery.ts +33 -0
  58. package/src/core/display-label.ts +47 -0
  59. package/src/core/fence.ts +151 -0
  60. package/src/core/format.ts +13 -0
  61. package/src/core/frontmatter.ts +202 -0
  62. package/src/core/models.ts +192 -0
  63. package/src/core/monitor.ts +415 -0
  64. package/src/core/naming.ts +42 -0
  65. package/src/core/nudge.ts +238 -0
  66. package/src/core/ownership.ts +102 -0
  67. package/src/core/permissions.ts +56 -0
  68. package/src/core/pi-compat.ts +50 -0
  69. package/src/core/seed.ts +770 -0
  70. package/src/core/session-identity.ts +89 -0
  71. package/src/core/time.ts +202 -0
  72. package/src/core/timer.ts +221 -0
  73. package/src/core/types.ts +28 -0
  74. package/src/engine/adapter.ts +257 -0
  75. package/src/engine/async.ts +898 -0
  76. package/src/engine/change-report.ts +143 -0
  77. package/src/engine/child.ts +532 -0
  78. package/src/engine/errors.ts +30 -0
  79. package/src/engine/fallback.ts +57 -0
  80. package/src/engine/handles.ts +21 -0
  81. package/src/engine/inproc.ts +857 -0
  82. package/src/engine/signals.ts +11 -0
  83. package/src/engine/spec-preflight.ts +33 -0
  84. package/src/engine/stream.ts +369 -0
  85. package/src/engine/worktree.ts +144 -0
  86. package/src/exocom/activation.ts +31 -0
  87. package/src/exocom/codes.ts +261 -0
  88. package/src/exocom/envelope.ts +180 -0
  89. package/src/exocom/gate.ts +6 -0
  90. package/src/exocom/guards.ts +45 -0
  91. package/src/exocom/inbound.ts +141 -0
  92. package/src/exocom/install.ts +970 -0
  93. package/src/exocom/ledger.ts +603 -0
  94. package/src/exocom/limits.ts +20 -0
  95. package/src/exocom/paths.ts +35 -0
  96. package/src/exocom/plane.ts +1116 -0
  97. package/src/exocom/registry.ts +307 -0
  98. package/src/exocom/scope.ts +42 -0
  99. package/src/exocom/status.ts +86 -0
  100. package/src/exocom/untrusted.ts +40 -0
  101. package/src/exocom/wait.ts +23 -0
  102. package/src/exocom/write-guard.ts +89 -0
  103. package/src/extension/broker-host.ts +181 -0
  104. package/src/extension/clock.ts +14 -0
  105. package/src/extension/engine.ts +321 -0
  106. package/src/extension/hooks.ts +796 -0
  107. package/src/extension/identity.ts +244 -0
  108. package/src/extension/shared.ts +175 -0
  109. package/src/extension.ts +2221 -0
  110. package/src/loader.ts +218 -0
  111. package/src/monitor/process.ts +116 -0
  112. package/src/monitor/session.ts +61 -0
  113. package/src/orchestration/flow-journal.ts +86 -0
  114. package/src/orchestration/flow-run.ts +301 -0
  115. package/src/orchestration/flow.ts +125 -0
  116. package/src/orchestration/judge.ts +78 -0
  117. package/src/orchestration/model-retry.ts +85 -0
  118. package/src/orchestration/parallel.ts +107 -0
  119. package/src/orchestration/params.ts +23 -0
  120. package/src/orchestration/reducers.ts +123 -0
  121. package/src/orchestration/render.ts +127 -0
  122. package/src/orchestration/roster.ts +133 -0
  123. package/src/orchestration/sdk.ts +326 -0
  124. package/src/orchestration/strategies/compete.ts +182 -0
  125. package/src/orchestration/strategies/council-rounds.ts +124 -0
  126. package/src/orchestration/strategies/critic-loop.ts +168 -0
  127. package/src/orchestration/strategies/debate.ts +126 -0
  128. package/src/orchestration/strategies/fanout.ts +20 -0
  129. package/src/orchestration/strategies/judge.ts +117 -0
  130. package/src/orchestration/strategies/magi.ts +239 -0
  131. package/src/orchestration/strategies/map.ts +327 -0
  132. package/src/orchestration/strategies/pair.ts +72 -0
  133. package/src/orchestration/strategies/pipeline.ts +91 -0
  134. package/src/orchestration/strategies/synthesize.ts +99 -0
  135. package/src/orchestration/strategy.ts +49 -0
  136. package/src/orchestration/types.ts +31 -0
  137. package/src/orchestration/voting.ts +126 -0
  138. package/src/persona/config-store.ts +215 -0
  139. package/src/persona/controller.ts +173 -0
  140. package/src/persona/gating.ts +82 -0
  141. package/src/persona/orchestrate.ts +83 -0
  142. package/src/persona/persona.ts +294 -0
  143. package/src/persona/spine.ts +221 -0
  144. package/src/persona/state.ts +28 -0
  145. package/src/telemetry/contract.ts +244 -0
  146. package/src/telemetry/paths.ts +14 -0
  147. package/src/telemetry/producer.ts +390 -0
  148. package/src/tools/council.ts +200 -0
  149. package/src/tools/delegate-tool.ts +637 -0
  150. package/src/tools/delegate.ts +661 -0
  151. package/src/tools/exocom-work.ts +328 -0
  152. package/src/tools/exocom.ts +476 -0
  153. package/src/tools/flow.ts +117 -0
  154. package/src/tools/intercom-tool.ts +292 -0
  155. package/src/tools/intercom.ts +181 -0
  156. package/src/tools/models.ts +39 -0
  157. package/src/tools/monitor.ts +66 -0
  158. package/src/tools/timer.ts +105 -0
  159. package/src/ui/agent-overlay.ts +526 -0
  160. package/src/ui/agent-tree.ts +307 -0
  161. package/src/ui/live-clock.ts +42 -0
  162. package/src/ui/model-picker.ts +160 -0
  163. package/src/ui/presentation.ts +170 -0
  164. package/src/ui/usage.ts +133 -0
  165. package/teams.yaml +27 -0
@@ -0,0 +1,96 @@
1
+ # Working comfortably with Pi Persona
2
+
3
+ Choose the outcome you want, describe what counts as finished, and let the supervisor coordinate
4
+ the work. You can inspect or redirect individual workers without managing every hand-off.
5
+
6
+ ## Choose a starting persona
7
+
8
+ | Your request | Start with | What to expect |
9
+ |---|---|---|
10
+ | Implement or fix something | `dev` | A scoped change, checks, and a concise explanation of the result |
11
+ | Review a change | `audit` | Prioritized, evidence-backed findings; edits only when requested |
12
+ | Turn an idea into an implementation plan | `planner` | Decisions, dependencies, and verifiable steps without changing application code |
13
+ | Investigate a question | `researcher` | Sources, conclusions, uncertainty, and durable notes when the scope warrants them |
14
+ | Apply one operation across many independent items | `swarm` | Explicit coverage of completed, failed, and remaining items |
15
+ | Repair a failing build or verify an implementation | `verify` | Fresh checks on the actual tree; repairs only when requested, with unresolved failures stated clearly |
16
+
17
+ Switch with `/persona <name>` or F8 (the default shortcut). `/persona seed` installs missing defaults. Existing customized
18
+ persona files remain yours; `/persona restore` replaces installed defaults, so use it only when
19
+ you intend to discard those customizations.
20
+
21
+ Restart Pi after updating the extension. Known, untouched defaults upgrade automatically; customized
22
+ copies remain unchanged and can be compared with the bundled persona before adopting individual edits.
23
+
24
+ An effective request names the intended result and its constraints. For example:
25
+
26
+ > Fix the cancellation bug. Preserve the public API and my existing edits. Delegate independent
27
+ > investigations, implement the fix, and run the relevant checks. Tell me what changed and what
28
+ > remains unverified.
29
+
30
+ ## Follow the work without constant polling
31
+
32
+ Your persona describes a role; your session has its own personal handle. Pi chooses it on the first
33
+ task or inbound Exocom request when the naming tool is permitted. Until then it shows a distinct
34
+ session suffix. The name survives persona changes and resume. `agent_name` sets it in standalone
35
+ use; `exocom_name` sets the same identity when connected to peers. The leader can assign each
36
+ delegated worker a `name` before launch, which reaches both its display and its first prompt.
37
+
38
+ For a fixed-time reminder, ask for a timer. For a build finishing or a file, log, or system event,
39
+ ask for a monitor. Pi runs a small event-producing program and brings the result back when the
40
+ supervisor is free. Keep the session open, and cancel a watch when its purpose ends. See
41
+ [time and event wakes](MONITORS.md) for examples and limits.
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;
46
+ `x` requests cancellation and appears only when the run can actually be stopped, marking the row
47
+ `stopping…` at once; a stop that can't be applied says so instead of doing nothing. Escape goes
48
+ back or closes the panel.
49
+ - **`/peek`** gives a compact progress view. A quiet worker is a reason to inspect its last
50
+ progress and current activity, not to launch a duplicate immediately.
51
+ - **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.
53
+ - **Answer a waiting worker before joining it.** A coaching worker may need a decision from its
54
+ supervisor before it can finish. An unanswered question is actionable work.
55
+ - **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.
57
+
58
+ A truncated message includes a message id. The supervisor can retrieve its retained body with
59
+ `intercom { action: "message", messageId: "m1" }`. A worker's final report has a run id instead:
60
+ `intercom { action: "result", to: "run-1" }`. Retention is bounded and session-local, so save any
61
+ evidence needed beyond the current session as a project artifact.
62
+
63
+ For a long task, useful updates say what changed, what is blocked, and what happens next. Worker
64
+ names help distinguish concurrent work; the final answer should use file names and concrete
65
+ outcomes that make sense without remembering those names.
66
+
67
+ ## Work with another Pi through Exocom
68
+
69
+ Use Intercom for this supervisor's workers and Exocom for independent Pi sessions. Ask a peer for
70
+ a relevant second opinion or coordinate work already in progress. A bounded implementation task
71
+ belongs in a worker brief with explicit ownership and a success criterion.
72
+
73
+ When Exocom is enabled, use its listed targets to keep messages attached to the right session.
74
+ Exchange decisions and evidence when they affect the work; acknowledgement-only conversations
75
+ add noise. If a peer has joined from another workspace, agree which workspace owns each artifact.
76
+ That peer's membership does not grant it a write claim in the selected workspace.
77
+
78
+ `exocom_status` (or `/exocom`) shows who owns what right now: your open claims, a peer's open
79
+ claims, and any asks waiting on either side. Claims are advisory, not a lock — if you write into a
80
+ path a peer has claimed, that write is blocked once with a reminder to coordinate or claim it
81
+ yourself first; asking again on the same path goes through, so the warning never repeats for work
82
+ you've already been told about.
83
+
84
+ ## Read completion as an evidence statement
85
+
86
+ A useful completion tells you what changed, which checks ran against the final state, and what
87
+ still needs attention. A successful review, a passing mocked test, and a live integration check
88
+ answer different questions. Skipped checks and partial batch coverage remain visible.
89
+
90
+ A worker that did not run in its own isolated worktree has its report followed by a "files changed"
91
+ block: every path `git status` saw move since the worker started, flagging anything outside what it
92
+ was asked to touch. Treat it as a same-checkout approximation, not a private diff — a sibling
93
+ running at the same time can touch the same file.
94
+
95
+ The bundled persona prompts guide these habits. Tool capabilities, engine limits, and filesystem
96
+ isolation are enforced separately by the runtime; a persona's prose does not create a sandbox.
@@ -0,0 +1,84 @@
1
+ # Time and event wakes
2
+
3
+ The supervisor receives a UTC and local clock snapshot when a run starts, alongside the original
4
+ session start and elapsed duration. The precise clock arrives as hidden model-visible context,
5
+ preserving the cached system prefix. During a long run it can call `timer` with `{"action":"now"}`
6
+ to refresh the clock. This is sampled wall-clock time, not a continuously updated model sense.
7
+
8
+ Use `timer arm` for a known time. Use `monitor arm` when a program can report the event you care
9
+ about. Sub-agent completions and Exocom messages already wake the supervisor automatically.
10
+
11
+ ## Background programs
12
+
13
+ `monitor` starts an executable with literal arguments in the current workspace and returns an id
14
+ immediately. There is no implicit shell. For shell syntax, pass a script file to the appropriate
15
+ shell. Windows `.cmd` launchers likewise need an explicit shell; a native executable or script
16
+ interpreter avoids platform-specific quoting.
17
+
18
+ | Mode | When the model wakes | Typical source |
19
+ |---|---|---|
20
+ | `exit` | The launched program finishes, with its exit code and bounded output tail | Build or local job |
21
+ | `output` | A nonempty stdout line arrives | File watcher, filtered log tail, system-event subscription, service adapter |
22
+
23
+ For example, start an existing build script and get its completion:
24
+
25
+ ```json
26
+ {"action":"arm","command":"node","args":["scripts/build.mjs"],"label":"build finished","mode":"exit","timeoutSeconds":900}
27
+ ```
28
+
29
+ Or start an existing event adapter that prints only meaningful state changes:
30
+
31
+ ```json
32
+ {"action":"arm","command":"node","args":["scripts/watch-status.mjs"],"label":"status changed","mode":"output","timeoutSeconds":600,"maxEvents":3}
33
+ ```
34
+
35
+ Adapters can use native file/system subscriptions, a program's streaming API, or inexpensive
36
+ polling inside the program when no subscription exists. The model does not run a polling loop.
37
+ Print one short event per line to stdout; send diagnostic chatter to stderr. Flush output promptly
38
+ (for Python, use `-u` or `flush=True`). Repeated identical lines are ignored for that watch.
39
+ Keep complete logs in the program's normal log file if they are needed later.
40
+
41
+ The model can continue independent work while a monitor runs. Events wait while it is busy and
42
+ arrive together at an idle boundary. When only the event remains, it should end its turn. Natural
43
+ requests such as “let me know when this build finishes” are enough; tool parameters are for the
44
+ model or an integration, not something the user needs to manage.
45
+
46
+ ## Lifetime and limits
47
+
48
+ - `list` shows active watches; `cancel` with the returned `id` stops the owned process tree and
49
+ removes that watch's queued events. It never stops an unrelated job merely because its status
50
+ is being observed by an adapter.
51
+ - Defaults are one output event and a ten-minute deadline. Limits are eight active monitors,
52
+ twenty distinct output events per monitor, and twenty-four hours of lifetime.
53
+ - Exit, launch errors and deadline expiry produce terminal notices. Reaching the output event
54
+ count ends the watch immediately; the notice says the watch ended, without claiming the job succeeded.
55
+ - Lines and diagnostics are bounded. The session retains at most 64 pending events, delivers up
56
+ to four per batch, and reports omitted older events if a busy model cannot keep up.
57
+ - Keep Pi open. Watches are in memory and stop on shutdown, reload, or session replacement;
58
+ they are not restored on resume. A headless `pi -p` invocation that exits after its answer is
59
+ not a persistent monitoring service.
60
+
61
+ ## Permissions and architecture
62
+
63
+ Starting a command requires both `monitor` and `bash` to be active and allowed by the persona.
64
+ Changing to a persona that removes either permission stops its monitors and discards queued
65
+ events. Normal Exocom pending-question gates apply to monitor calls as well.
66
+
67
+ This is command execution with the host user's access, not an OS sandbox or a read-only guarantee.
68
+ An extension that enforces command-specific policy through `tool_call` must inspect `monitor`
69
+ as well as `bash`; Pi Persona does not synthesize another extension's Bash hooks. External event
70
+ text is fenced as untrusted data before delivery, with runtime id, time and outcome outside the fence.
71
+
72
+ The pure `src/core/monitor.ts` owns limits and lifecycle. `src/monitor/process.ts` adapts a process
73
+ using the existing process-tree cleanup helper. `src/monitor/session.ts` connects bounded events
74
+ to the existing `IdleCoalescingNotifier` and `sendPersonaFollowUp` path. No separate wake broker,
75
+ public listener, durable queue, or new orchestration engine is introduced.
76
+
77
+ The design follows the background-program model described in the official
78
+ [Claude Monitor documentation](https://code.claude.com/docs/en/tools-reference#monitor-tool).
79
+ Protocol-specific integrations can be ordinary adapter programs; direct WebSocket connections
80
+ and persistent services are outside this tool's current scope.
81
+
82
+ For live verification, `scripts/monitor-smoke.mjs` runs a persistent Pi RPC session and checks an
83
+ automatic model turn after a local event. It requires an explicit `LIVE_MODEL=provider/id` because
84
+ it spends provider tokens. Unit and integration tests use deterministic clocks and local programs.
package/docs/README.md ADDED
@@ -0,0 +1,53 @@
1
+ # pi-persona documentation
2
+
3
+ This folder contains the long-form documentation that defines how the project is designed and run.
4
+
5
+ ## Canonical docs
6
+
7
+ - [`ARCHITECTURE.md`](./ARCHITECTURE.md)
8
+ - Core architecture, invariants, module boundaries, and design contracts.
9
+ - [`STRATEGIES.md`](./STRATEGIES.md)
10
+ - Strategy SDK and built-in orchestration behavior.
11
+ - [`SPINE.md`](./SPINE.md)
12
+ - Shared behavioral spine prompt layer and rollout controls.
13
+ - [`TELEMETRY.md`](./TELEMETRY.md)
14
+ - Vendor-neutral observer contract, privacy boundary, persistence, and consumer conformance.
15
+ - [`EXPERIENCE.md`](./EXPERIENCE.md)
16
+ - Choosing a persona, supervising background work, and collaborating through Exocom.
17
+ - [`MONITORS.md`](./MONITORS.md)
18
+ - Clock snapshots, timers, and background event sources that wake the supervisor.
19
+ - [`REFERENCE.md`](./REFERENCE.md)
20
+ - Canonical Core API, configuration, environment variables, commands, and data-driven recipes.
21
+
22
+ ## Quick map
23
+
24
+ - **For users / operators:** start from [README](../README.md) and [EXPERIENCE.md](./EXPERIENCE.md); use `REFERENCE.md` for configuration and recipes.
25
+ - **For contributors:** begin at `ARCHITECTURE.md`, then `REFERENCE.md` for runtime surfaces and `STRATEGIES.md` for implementation details.
26
+ - **For behavior-layer changes:** consult `SPINE.md` and `src/core` / `src/persona/spine.ts` together.
27
+ - **For telemetry producers or consumers:** start with `TELEMETRY.md` and its conformance corpus.
28
+
29
+ ## Documentation maintenance notes
30
+
31
+ When touching this folder:
32
+
33
+ - Keep names stable (`ARCHITECTURE.md`, `STRATEGIES.md`, `SPINE.md`, `REFERENCE.md`) because many docs and comments link to them directly.
34
+ - Prefer links to this folder over copying duplicate prose into code comments.
35
+ - Update both this map and inbound links when adding new authoritative docs.
36
+ - A few factual claims are machine-checked by `test/unit/docs/doc-claims.test.ts`. Know its blind
37
+ spots before trusting it:
38
+ - **Env names.** It collects the whole `PI_*` identifiers `src/**/*.ts` actually reads and fails
39
+ when a documented name is not one of them, so a documented name that is merely a *prefix* of a
40
+ real one is caught. It still never reads `scripts/`, so a variable only that tree consumes
41
+ reads as undocumented-in-reverse: the check is one-directional (docs → source), and a source
42
+ variable nothing documents is not flagged at all.
43
+ - **Strategy table.** It fails when a registered strategy has anything other than exactly one row
44
+ in the canonical table in `STRATEGIES.md` (the one headed `| Strategy | … | Params …`), or when a
45
+ declared param is not backticked in a cell *after* the row's name. It still cannot tell whether
46
+ the *description* of a param or its default is correct.
47
+ - **Skip count.** It scans every `test/**/*.test.ts` for skip markers (an options-object `skip` key
48
+ or a `.skip` shorthand), and fails unless AGENTS.md's stated word, its `skipped N` on Windows and
49
+ its `skipped 0` elsewhere all match, every marker is gated on `process.platform === "win32"`, and
50
+ AGENTS.md names each file carrying one. It counts markers rather than running the runner, so a
51
+ marker on a suite of several skipped subtests would count once where the runner counts each.
52
+ - Everything else here is prose a reader has to check against the code — so state behavior precisely
53
+ enough to be falsifiable, and cite the module that settles it.
@@ -0,0 +1,408 @@
1
+ # pi-persona technical reference
2
+
3
+ This is the canonical technical guide for the runtime surfaces that users configure through
4
+ personas, agents, teams, flows, contracts, presets, and environment variables. The design contract
5
+ lives in [`ARCHITECTURE.md`](./ARCHITECTURE.md); strategy shapes and their complete parameter table
6
+ live in [`STRATEGIES.md`](./STRATEGIES.md). User-oriented orientation and operating examples start
7
+ in the root [`README.md`](../README.md) and [`EXPERIENCE.md`](./EXPERIENCE.md).
8
+
9
+ ## Core API
10
+
11
+ The runtime is composed from a small, fixed set of primitives. A strategy is a TypeScript file
12
+ composing the **Strategy SDK**; a persona picks whether and how those run. `magi` is a `.md` persona
13
+ plus a file that calls `parallel` and `reduce.vote`.
14
+
15
+ **Strategy SDK** (`src/orchestration/sdk.ts`) — the primitives available to a strategy:
16
+
17
+ | Primitive | Does |
18
+ |---|---|
19
+ | `agent(spec)` | run one sub-agent and return a structured `AgentResult`; `spec` may carry `name`, model, tools, skills, or `outputContract` |
20
+ | `parallel(thunks, {concurrency})` | run many at once, bounded by the run limits; this also provides the basis of `map` |
21
+ | `reduce.aggregate(results)` | merge N results into one, used by fan-out |
22
+ | `reduce.vote(candidates, opts)` | tally the candidates' own votes and preserve `winner`, `tie`, `no_consensus`, `invalid_outputs`, and dissent |
23
+ | `reduce.judge(candidates, order?)` | anonymise and label candidates for an impartial judge, then map the verdict back with `pick(label)` |
24
+ | `roster.team(name)` | resolve the agents of a named team |
25
+ | `signal` · `limits` · `log` | cooperative abort, admission/runtime limits, and progress reporting |
26
+ | series and loops | plain `await` and `for`; strategies are TypeScript, so pipelines and critic loops are ordinary code |
27
+
28
+ The complete built-in strategy catalog and each strategy's declared parameters are maintained in
29
+ [`STRATEGIES.md`](./STRATEGIES.md). Parameters are declared per strategy and looked up by
30
+ `knownParams()`; `/doctor` exposes the same schema live. Unknown parameter keys warn but do not
31
+ hard-fail.
32
+
33
+ **Supervisor surface** — the tools and persona fields that drive those primitives:
34
+
35
+ | Surface | Does |
36
+ |---|---|
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 |
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
+ | `monitor` tool | run a bounded job (`mode: exit`) or event-producing program (`mode: output`) and wake the supervisor; see [`MONITORS.md`](./MONITORS.md) |
42
+ | `flow` tool and `/flow` | run a journaled DAG of strategies; `gate: true` creates an approval checkpoint |
43
+ | `models` tool | list or search authenticated model ids in `provider/id` form; the session provider is marked |
44
+ | persona `mode:` | choose `solo`, `parallel`, `pipeline`, `strategy:<name>`, or `flow:<name>` |
45
+ | persona `coaching:` | give children `contact_supervisor` for progress and blocking decisions |
46
+ | persona `delegation:` | require briefs, set an output contract, schedule disjoint writes, and order fresh verification |
47
+ | `isolation: worktree` | run in a clean throwaway Git worktree and return an exported diff; non-Git, dirty, or oversized cases fail closed |
48
+ | `council: { preset }` | expand a reusable `presets/<name>.preset.json`; authored fields override the preset |
49
+ | `contracts/*.json` | define a hot-editable structured return requested through `outputContract` and pinned per run |
50
+
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.
53
+
54
+ `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
56
+ spawns, checked across every task in a parallel batch: `delegate: unknown output contract(s) "x" —
57
+ nothing was spawned. Installed contracts: default, finding` (the list is capped at 16 names; a
58
+ `, …` marker is appended only when more than 16 are installed).
59
+
60
+ ### Supervising running sub-agents — the `intercom` plane
61
+
62
+ The supervisor's internal communication with children has three layers:
63
+
64
+ | Layer | Needs | What you get |
65
+ |---|---|---|
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 |
67
+ | **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
+ | **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
+ | **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
+
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>`.
76
+
77
+ Any leg that does NOT run in `isolation: worktree` shares the real checkout, so its result gets a
78
+ "what changed" block appended: `--- FILES CHANGED DURING THIS LEG (shared checkout; parallel legs
79
+ may overlap) ---` followed by one `- <path> (<M|A|D|R|?>)` line per path (capped at 40, `+N more`
80
+ beyond that); a changed path outside the leg's own declared `writeSet` is flagged
81
+ `⚠ outside declared writeSet`. This is a `git status` before/after snapshot, not a private diff — a
82
+ parallel sibling touching the same file can appear here too — and it is skipped when there is
83
+ nothing to report, or when the leg IS worktree-isolated (that path already returns a real unified
84
+ diff instead).
85
+
86
+ The cross-process broker is on by default for child-engine, worktree, and MCP legs. It provides the
87
+ same supervisor communication surface over a session-scoped POSIX socket or Windows named pipe.
88
+ `PI_PERSONA_BROKER=off` restores the pre-broker child spawn behavior. See the comm-plane contract in
89
+ [`ARCHITECTURE.md`](./ARCHITECTURE.md#the-comm-plane-in-practice).
90
+
91
+ > **MCP in sub-agents.** A normal in-process leg has no MCP connection. Set `mcp: true` in an agent
92
+ > definition or delegation task to route it through the child engine, which initializes MCP from
93
+ > `~/.pi/agent/mcp.json`. A fresh UI-less child cannot complete an interactive OAuth flow, so
94
+ > authenticate the server in a normal session first. A backend that keys state by a session id must
95
+ > receive that id in the task. A local registration, stdio process, or loopback endpoint does not
96
+ > prove local target egress; verify source routing with the executor and canary required by the
97
+ > engagement. This is a behavioral boundary, not a network sandbox.
98
+
99
+ ### Identity and Exocom
100
+
101
+ An unnamed standalone session receives a provisional process/session-derived handle. On its first
102
+ turn where `agent_name` is callable, the runtime context asks the model to invent a
103
+ short handle and call `agent_name` before other work. The chosen handle is separate from the active
104
+ persona and role, is persisted in the session identity entry, and remains across later turns and
105
+ persona changes. Once chosen, it is not re-injected into every provider request; the runtime and
106
+ session entry remain authoritative. `agent_name` is unavailable while Exocom is active; its call-time capability check
107
+ also rejects empty, unsafe, generic, or reserved persona/agent names.
108
+
109
+ Naming needs one tool action, not a recurring prose confirmation. A stable system-level runtime rule
110
+ also distinguishes skill-prefetch and routine check-ins from new user requests; it neither hides
111
+ updates nor rewrites conversation history. After updating extension code, use Pi's `/reload` once
112
+ delegated work has settled (or restart/resume); `/persona reload` reloads definitions only.
113
+
114
+ With Exocom active, the corresponding tool is `exocom_name`. The first incoming peer wake can be the
115
+ first model turn in an RPC session; the context hook supplies the naming bootstrap for that custom
116
+ wake as well as for an ordinary user turn. A chosen Exocom handle updates the live registry while
117
+ its session-qualified routing target remains stable across renames. Naming metadata remains callable
118
+ when a pending ask permits `exocom_name`, but it does not settle that work ask.
119
+
120
+ Exocom is a flat, external plane between independent top-level Pi sessions. It provides presence,
121
+ fenced postcards, and a durable claim/ask ledger. `--exocom` joins the current workspace; an exact
122
+ `--exocom=Ab0T` joins an existing workspace scope from another cwd. The four-character code is a
123
+ same-host join reference, not authentication. Foreign members advertise the workspace they can
124
+ actually inspect and cannot claim repository-relative paths in another workspace's ledger.
125
+
126
+ `exocom_status` (a tool, taking no arguments) and `/exocom` (a command, which additionally lists
127
+ live peers) both render the same ownership view: your claims, peers' claims, asks waiting on you,
128
+ and your own open asks. A claim's `write_set` is advisory, not enforced: a supervisor's own
129
+ `write`/`edit` call whose path overlaps a PEER's open claim is blocked once with a reason to
130
+ coordinate (`exocom_ask`) or claim the path itself; calling the same tool again on the same path is
131
+ then allowed through (one warning per claim + normalized path, not a standing lock).
132
+
133
+ ## Configuration and data
134
+
135
+ Personas and agents are Markdown files with YAML frontmatter. Teams, flows, contracts, and presets
136
+ are data files. Discovery precedence is builtin < user `~/.pi/agent/persona` < project `.pi/`.
137
+ Personas and agents are loaded from the user and project agent folders; bundled persona and agent
138
+ files are seed sources. A fresh install has no installed personas until `/persona seed`,
139
+ `/persona restore`, or opt-in first-run seeding installs them.
140
+
141
+ | Definition | Location | Purpose |
142
+ |---|---|---|
143
+ | Persona | `personas/*.md` or installed `agents/*.md` with `persona: true` | supervisor prompt, capabilities, delegation, council, orchestration, and coaching |
144
+ | Agent | `agents/*.md` or project `.pi/agents/*.md` | worker prompt, optional tools/model/isolation/MCP |
145
+ | Team | `teams.yaml` | named roster of bare agent names or inline `{ agent, role, model, skills }` members |
146
+ | Flow | `flows/*.flow.json` | strategy phases connected by `needs`, optionally gated |
147
+ | Contract | `contracts/*.contract.json` | structured output fields requested by name and pinned per run |
148
+ | Preset | `presets/*.preset.json` | reusable council strategy, roster, and parameter bundle |
149
+
150
+ `tools` accepts an allowlist such as `[read, grep]`, an `allow`/`deny` object, or `[]` for no tools.
151
+ Omit it to inherit the session tool set. `model`, `isolation: worktree`, `mcp: true`, and `purpose`
152
+ are optional agent metadata. The body is the agent prompt. Inline roster members specialise one
153
+ agent with a role, model, or skills without requiring another agent file.
154
+
155
+ ### Multi-agent quick start
156
+
157
+ Follow the [installation instructions](../README.md#install), then start a coding supervisor:
158
+
159
+ ```bash
160
+ pi --persona dev
161
+ ```
162
+
163
+ Delegate a bounded task with the complete brief that Dev requires:
164
+
165
+ ```js
166
+ delegate({
167
+ agent: "scout", name: "Login-Trace", task: "Inspect the login flow and report the root cause.",
168
+ brief: {
169
+ objective: "Explain the login failure with code evidence",
170
+ scopeRoe: "Read this repository only; do not contact external systems",
171
+ position: "Start from the current checkout and its existing tests",
172
+ constraints: ["Do not modify files"],
173
+ requiredArtifacts: ["File and line references, with a suggested minimal fix"],
174
+ stopConditions: ["Stop after finding the cause or report the missing evidence"]
175
+ }
176
+ })
177
+ intercom({ action: "peek" })
178
+ ```
179
+
180
+ For independent Pi processes, open the same workspace in two terminals with `pi --exocom` and use
181
+ the qualified target returned by `exocom_list`:
182
+
183
+ ```js
184
+ exocom_name({ name: "<invented from the current task>" })
185
+ exocom_list({})
186
+ exocom_send({ target: "<qualified target from exocom_list>", message: "Review the API boundary and send back concrete risks." })
187
+
188
+ exocom_claim({ work_key: "api-review", write_set: ["src/api"], slice: "Review and fix the API boundary" })
189
+ exocom_ask({ target: "<qualified target>", work_key: "api-review", question: "Is the transport contract safe to change?" })
190
+ exocom_wait({ work_key: "api-review", ask_id: "<ask_id returned above>" })
191
+ exocom_answer({ work_key: "api-review", ask_id: "<received ask_id>", ok: true, evidence: "Tests and file references" })
192
+ exocom_release({ work_key: "api-review" })
193
+ ```
194
+
195
+ ### Recipes
196
+
197
+ Everything in these examples is data. Drop files into the discovery locations above and switch
198
+ personas with `f8`. A brand-new strategy shape is the only case that requires a source file.
199
+
200
+ **Opportunistic delegation** — a simple persona with no orchestration block:
201
+
202
+ ```markdown
203
+ <!-- .pi/agents/my-researcher.md -->
204
+ ---
205
+ name: my-researcher
206
+ persona: true
207
+ ---
208
+ You research thoroughly. For independent sub-questions, fan out `scout` sub-agents in ONE
209
+ `delegate` call (`tasks: [...]`), each with a disjoint scope, then synthesise their findings.
210
+ ```
211
+
212
+ **A review council and a per-call override** — `council:` is the persona default; the tool can
213
+ override it for one call without editing the file:
214
+
215
+ ```yaml
216
+ # teams.yaml — one reviewer agent, three lens roles
217
+ review:
218
+ - { agent: reviewer, role: "Focus ONLY on the SECURITY lens" }
219
+ - { agent: reviewer, role: "Focus ONLY on the PERFORMANCE lens" }
220
+ - { agent: reviewer, role: "Focus ONLY on the TESTS lens" }
221
+ ```
222
+
223
+ ```markdown
224
+ <!-- .pi/agents/myaudit.md -->
225
+ ---
226
+ name: myaudit
227
+ persona: true
228
+ council: { strategy: synthesize, roster: review, params: { synthesizer: reviewer, peers: true } }
229
+ ---
230
+ Convene the council before sign-off, then apply its merged findings yourself.
231
+ ```
232
+
233
+ ```js
234
+ // one-off: run this decision as a debate over the same roster — no file edit
235
+ council({ question: "cache this or recompute?", strategy: "debate", roster: "review", params: { bestOf: 2 } })
236
+ ```
237
+
238
+ **A delegation policy on any persona** — fields are generic and are never keyed to a persona name.
239
+ The runtime rejects incomplete briefs before a model call, supplies a missing contract, rejects
240
+ overlapping or missing ownership for parallel writers, and refuses a declared verifier that would
241
+ start before or beside a material writer in the same call or while a mutating background run is in
242
+ flight. Only `read`, `grep`, `find`, and `ls` count as read-only; shell-capable, MCP-enabled, and
243
+ unknown toolsets fail closed as writers. `writeSet` is a scheduling contract, not an OS sandbox.
244
+
245
+ ```markdown
246
+ ---
247
+ name: my-project-lead
248
+ persona: true
249
+ delegation:
250
+ requireBrief: true
251
+ outputContract: finding
252
+ requireDisjointWrites: true
253
+ requireFreshVerification: true
254
+ verificationAgents: [my-checker]
255
+ council: { strategy: critic-loop, roster: my-repair-team, params: { rounds: 3 } }
256
+ ---
257
+ Delegate bounded work, then accept completion only after fresh evidence.
258
+ ```
259
+
260
+ The six required `brief` fields are `objective`, `scopeRoe`, `position`, `constraints`,
261
+ `requiredArtifacts`, and `stopConditions`. `verificationAgents` names ordinary installed agents;
262
+ there is no built-in verifier identity.
263
+
264
+ When `requireBrief` is enabled, it also applies to read-only scouts. Single-worker calls put the
265
+ brief alongside `agent` and `task`; parallel calls put a complete brief inside **each** `tasks[]`
266
+ entry. A top-level brief is not inherited by the batch, and prose in `task` does not replace these
267
+ fields. A missing-brief error means the batch was rejected before any worker started: correct all
268
+ affected entries before retrying.
269
+
270
+ **A mandatory-orchestration persona** — `orchestration:` runs automatically every turn, while
271
+ `council:` runs only when requested. Only `ok: true` is presented as a ruling; failures, cancellation,
272
+ and unresolved output remain labelled evidence to repair and verify.
273
+
274
+ ```markdown
275
+ <!-- .pi/agents/myguard.md -->
276
+ ---
277
+ name: myguard
278
+ persona: true
279
+ orchestration: { mode: strategy, strategy: magi, roster: magi, params: { reflect: false } }
280
+ ---
281
+ Every turn is decided by the MAGI triarchy first (no reflection round); present and act on it.
282
+ ```
283
+
284
+ **Coaching** — add `coaching: true` to give children `contact_supervisor`, then read and answer
285
+ progress or decisions with `intercom inbox` and `intercom reply`.
286
+
287
+ **A custom agent** — the body is the worker prompt and the frontmatter sets its capabilities:
288
+
289
+ ```markdown
290
+ <!-- .pi/agents/hardener.md -->
291
+ ---
292
+ name: hardener
293
+ description: Locks down a change's security posture — authz, input validation, secrets handling.
294
+ tools: [read, grep, edit, bash]
295
+ model: opus
296
+ isolation: worktree
297
+ ---
298
+ You are the Hardener. Given a diff or area, find and FIX authz/access-control gaps, injection and
299
+ unsafe-sink risks, secrets/token mishandling, and missing input validation. Cite `file:line`.
300
+ ```
301
+
302
+ **A flow with a human checkpoint** — phases are journaled and dependents wait for a `gate: true`
303
+ approval. Save as `.pi/flows/gated-build.flow.json`:
304
+
305
+ ```json
306
+ {
307
+ "name": "gated-build",
308
+ "phases": [
309
+ { "id": "plan", "strategy": "magi", "roster": "magi", "gate": true },
310
+ { "id": "build", "strategy": "critic-loop", "roster": "repair", "needs": ["plan"] },
311
+ { "id": "verify", "strategy": "fanout", "roster": "review", "needs": ["build"] }
312
+ ]
313
+ }
314
+ ```
315
+
316
+ **A structured-return contract** — save as `.pi/contracts/ship-verdict.contract.json` so votes and
317
+ judges can tally fields mechanically:
318
+
319
+ ```json
320
+ { "name": "ship-verdict",
321
+ "fields": {
322
+ "vote": { "type": "string", "required": true },
323
+ "severity": { "type": "enum", "values": ["low", "medium", "high", "critical"] },
324
+ "confidence": { "type": "number", "min": 0, "max": 1 }
325
+ }
326
+ }
327
+ ```
328
+
329
+ **A preset keeps persona files to one line** — save as `.pi/presets/magi-rounds.preset.json`:
330
+
331
+ ```json
332
+ { "strategy": "council-rounds", "roster": "magi", "params": { "rounds": 3, "bestOf": 3 } }
333
+ ```
334
+
335
+ ```yaml
336
+ # any persona's frontmatter
337
+ council: { preset: magi-rounds }
338
+ ```
339
+
340
+ For one-line strategy calls and the canonical parameter defaults, use the table in
341
+ [`STRATEGIES.md`](./STRATEGIES.md). A new vote rule or custom loop adds
342
+ `src/orchestration/strategies/<name>.ts`, registers it in `strategy.ts`, and gets a stub-engine unit
343
+ test; the data-driven definitions above require no core change.
344
+
345
+ ## Commands, flags, and environment
346
+
347
+ **Keys** — `f8` cycles persona; `f9` or `/agents` opens the agent overlay: ↑↓ navigate, ⏎ open, `x`
348
+ stop, `s` steer, and Esc close.
349
+
350
+ **Commands** — `/persona [name|off|list|reload|seed|restore]` · `/models [query]` ·
351
+ `/orchestrate <task>` · `/flow <name> <task>` · `/peek [id]` · `/exocom` · `/doctor`.
352
+
353
+ `/doctor` reports the cross-process broker's lifecycle on one line: `broker: on — endpoint …` once
354
+ the host is listening, `broker: on — (starting…)` mid-attempt, or
355
+ `broker: on — (not started — no child-engine build yet)` before any child-engine build has run. A
356
+ bind failure reports on its own, without the `on —` prefix or a connected-children count (there is
357
+ neither while down): `broker: failed — <reason>` — children built while it is down spawn without a
358
+ bus endpoint, so they never burn connect backoff against a dead socket, and the next child-engine
359
+ build retries. A connected broker child's status shows `⇄ <handle>`; if that connection drops, the
360
+ status flips to `⇄ offline` and a fresh ask on it fails fast instead of hanging.
361
+
362
+ `/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
364
+ check (`src/core/pi-compat.ts`). `pi: unknown` means the host's own `package.json` could not be
365
+ resolved (a non-standard install layout) — not a failed check.
366
+
367
+ **CLI flags** — `--persona <name>` starts with that installed persona and errors if it is missing;
368
+ `--exocom` joins this workspace's Exocom plane; `--exocom=Ab0T` joins another workspace's scope
369
+ from a different cwd. Pi owns `--model <provider/id>`, `--thinking
370
+ <off|minimal|low|medium|high|xhigh|max>`, and `--list-models`.
371
+
372
+ | Variable | Default | Effect |
373
+ |---|---|---|
374
+ | `PI_PERSONA_ENGINE` | in-process | `child` spawns each sub-agent as an isolated `pi` process |
375
+ | `PI_PERSONA_CHILD_THINKING` | inherited | reasoning effort for child-engine sub-agents |
376
+ | `PI_PERSONA_SEED` | off | `on` auto-installs bundled personas on first run |
377
+ | `PI_PERSONA_BROKER` | on | `off` disables the cross-process comm plane for child, worktree, and MCP legs |
378
+ | `PI_PERSONA_PEEK_MS` | 30000 | fast async stall/message wakeup tick; `0` disables it |
379
+ | `PI_PERSONA_CHECKIN_MS` | 300000 | routine direction check-in while async children run; `0` disables it |
380
+ | `PI_PERSONA_AGENT_MAX_MS` | off | opt-in per-agent hard wall-clock cap; unset leaves healthy work running |
381
+ | `PI_PERSONA_AGENT_STARTUP_MS` | 300000 | startup deadline for a child that makes no progress; `0` disables it. Like the idle window, a leg waiting on a supervisor reply is not idle: the watchdog re-arms while its ask is pending, instead of killing on a blind ceiling |
382
+ | `PI_PERSONA_NUDGE` | on | `off` silences delegation and persistence nudges; synchronous checkpoints stay in model-visible tool results and are mirrored to durable, expandable TUI-only cards |
383
+ | `PI_PERSONA_EXOCOM` | off | `1` joins the current workspace Exocom scope; cross-workspace selection is CLI-only |
384
+ | `PI_PERSONA_SPINE` | off | `on` or a path enables the shared behavioral layer; see [`SPINE.md`](./SPINE.md) |
385
+ | `PI_PERSONA_SPINE_LEGS` | follows `PI_PERSONA_SPINE` | selector for delegated legs, allowing all four spine measurement arms |
386
+ | `PI_PERSONA_DISABLE` | unset | any non-empty value disables the whole extension for this process (also set internally as the fork-bomb guard on delegated legs) |
387
+ | `PI_PERSONA_DIRS` | none | extra discovery directories (`;`/`,`-separated), searched alongside the user and project agent folders |
388
+ | `PI_PERSONA_DEFAULT` | none | persona activated automatically when no persona is otherwise selected |
389
+ | `PI_PERSONA_KEY` | `f8` | keybinding that cycles installed personas |
390
+ | `PI_PERSONA_PERSIST` | on | `off` stops remembering the last-selected persona across sessions |
391
+ | `PI_PERSONA_DELEGATE_DEFAULT` | allow | `deny` (or an off-word) makes an unlisted delegate target default-deny instead of default-allow |
392
+ | `PI_PERSONA_STATE_FILE` | a global default path | overrides where the last-selected-persona state is stored |
393
+ | `PI_PERSONA_ASYNC_RETAIN` | 25 | how many settled-but-uncollected async runs are retained before FIFO-evicting the oldest |
394
+ | `PI_PERSONA_LEDGER_V2` | off | `on` widens the delegation-ledger retry-veto key to also include role/tools/isolation, not just agent+model+task |
395
+ | `PI_PERSONA_LEG_CHANGE_REPORT` | on | `off` disables the non-worktree files-changed report (two `git status` spawns per leg) |
396
+
397
+ ## Development
398
+
399
+ ```bash
400
+ npm install
401
+ npm run typecheck # strict tsc --noEmit
402
+ npm test # node --test
403
+ ```
404
+
405
+ The architecture contract is [`ARCHITECTURE.md`](./ARCHITECTURE.md), orchestration behavior is
406
+ [`STRATEGIES.md`](./STRATEGIES.md), the shared prompt layer is [`SPINE.md`](./SPINE.md), and the
407
+ observer/export contract is [`TELEMETRY.md`](./TELEMETRY.md). After updating Pi packages, run
408
+ `npm run typecheck`: it catches changes at the in-process session and `ThinkingLevel` seams.