pi-subagents 0.36.0 → 0.37.1

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 (54) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/README.md +111 -16
  3. package/agents/delegate.md +2 -0
  4. package/agents/planner.md +2 -1
  5. package/agents/worker.md +2 -0
  6. package/package.json +6 -4
  7. package/skills/pi-subagents/SKILL.md +18 -973
  8. package/skills/pi-subagents/references/constraints-and-recipes.md +256 -0
  9. package/skills/pi-subagents/references/execution-controls.md +411 -0
  10. package/skills/pi-subagents/references/management-authoring-rpc.md +140 -0
  11. package/skills/pi-subagents/references/prompting-and-roles.md +268 -0
  12. package/src/agents/agent-management.ts +7 -1
  13. package/src/agents/agents.ts +99 -12
  14. package/src/api/capability-ceiling.ts +17 -0
  15. package/src/api/delegation.ts +6 -1
  16. package/src/api/preflight.ts +399 -0
  17. package/src/extension/index.ts +16 -7
  18. package/src/extension/rpc.ts +29 -2
  19. package/src/extension/schemas.ts +10 -4
  20. package/src/extension/tool-description.ts +4 -2
  21. package/src/intercom/intercom-bridge.ts +5 -2
  22. package/src/runs/background/async-execution.ts +125 -14
  23. package/src/runs/background/async-job-tracker.ts +19 -12
  24. package/src/runs/background/async-resume.ts +27 -9
  25. package/src/runs/background/async-status.ts +18 -0
  26. package/src/runs/background/notify.ts +3 -0
  27. package/src/runs/background/process-terminal.ts +280 -0
  28. package/src/runs/background/result-watcher.ts +9 -3
  29. package/src/runs/background/run-status.ts +7 -1
  30. package/src/runs/background/scheduled-runs.ts +6 -1
  31. package/src/runs/background/stale-run-reconciler.ts +6 -0
  32. package/src/runs/background/subagent-runner.ts +182 -12
  33. package/src/runs/foreground/chain-execution.ts +5 -0
  34. package/src/runs/foreground/execution.ts +59 -13
  35. package/src/runs/foreground/subagent-executor.ts +142 -47
  36. package/src/runs/shared/acceptance.ts +41 -30
  37. package/src/runs/shared/capability-ceiling.ts +177 -0
  38. package/src/runs/shared/dynamic-fanout.ts +1 -1
  39. package/src/runs/shared/mcp-direct-tool-allowlist.ts +56 -17
  40. package/src/runs/shared/model-fallback.ts +8 -0
  41. package/src/runs/shared/nested-events.ts +8 -1
  42. package/src/runs/shared/parallel-utils.ts +5 -0
  43. package/src/runs/shared/pi-args.ts +141 -58
  44. package/src/runs/shared/session-lease.ts +25 -5
  45. package/src/runs/shared/subagent-prompt-runtime.ts +14 -1
  46. package/src/runs/shared/task-intent.ts +1 -1
  47. package/src/runs/shared/tool-availability.ts +18 -2
  48. package/src/shared/launch-contract.ts +123 -0
  49. package/src/shared/types.ts +118 -8
  50. package/src/shared/utils.ts +17 -42
  51. package/src/slash/delegation-adapters.ts +6 -1
  52. package/src/slash/slash-commands.ts +1 -1
  53. package/src/tui/fleet-status.ts +62 -16
  54. package/src/tui/fleet.ts +1 -1
package/CHANGELOG.md CHANGED
@@ -2,6 +2,44 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [0.37.1] - 2026-07-27
6
+
7
+ ### Added
8
+ - Added package-owned resume control to the extension RPC surface, including preserved revival metadata and native result-delivery controls. Thanks to @shaneconner for #656.
9
+
10
+ ### Changed
11
+ - Added a generous 30-minute foreground wall-clock timeout when neither the call nor selected agent provides `timeoutMs`/`maxRuntimeMs`. Explicit call values and agent timeout defaults remain authoritative.
12
+ - Split the bundled `pi-subagents` skill into a short router plus focused reference files to avoid truncation and unnecessary context loading. Thanks to @peedrr for #659.
13
+ - Added `fleetViewPlacement` so the persistent FleetView can be placed above or below the editor. Thanks to @rtbe for #660.
14
+ - Refreshed the bundled `pi-subagents` skill for 0.35–0.37 control and config surface: `/subagents`, `/subagents-stop`, `/subagents-watchdog`, `stop`/`append-step`, parallel `count`, watchdog overview, frontmatter `async`/`timeoutMs`/`turnBudget` defaults, `artifactDir`/`asyncWidget`, fresh/fork badges, and builtin worker/delegate ambient-tool boundaries.
15
+
16
+ ### Fixed
17
+ - Suppressed redundant local completion notifications after acknowledged grouped intercom delivery, while preserving fallback notifications when relay delivery is unavailable. Thanks to @Wiandono for #662.
18
+ - Stopped the persistent FleetView from refreshing through a stale extension context after session replacement or reload. Thanks to @kylegl for #657.
19
+ - Invalidated the live Fleet inspector before timer-driven refreshes so cached transcript frames do not repeat stale headers. Thanks to @shaneconner for #661.
20
+ - Removed repository write tools from the bundled planner and marked it read-only so planning-only runs cannot modify project files while producing `plan.md`. Thanks to @DrunkenDonkey80 for #664.
21
+ - Kept async child model inheritance stable after parent continuation so background launches keep using the authenticated parent provider/model. Thanks to @DrunkenDonkey80 for #663.
22
+ - Accepted persisted async recovery descriptors that include the launch contract digest written by async execution. Thanks to @boadij for #654 and #652.
23
+ - Classified verification-only tasks that prohibit product/source/config files as read-only. Thanks to @git-geeky for #648.
24
+ - Matched pi-mcp-adapter metadata cache identity so valid direct MCP tools are not rejected as stale when tool filters, socket transport, URL interpolation, or command-backed secrets are configured. Thanks to @mattrobenolt for #649.
25
+
26
+ ## [0.37.0] - 2026-07-25
27
+
28
+ ### Added
29
+ - Bound public launch preflight to versioned selected-agent definition digests, projected async lifecycle/status/result/process-terminal roots, and actual foreground/async execution digests in result and status metadata. Thanks to @shaggitza for #637.
30
+ - Added `subagents.defaultExtensions` for shared child extension allowlists and `agentOverrides.<name>.extensions` for per-agent settings. Thanks to chronoAP for #642.
31
+ - Added a public `pi-subagents/preflight` API that resolves an ordinary single-agent launch contract without creating child sessions, temp prompt files, structured-output runtimes, or run artifacts. Thanks to @shaggitza for #634.
32
+ - Added an out-of-band, session-scoped capability-ceiling API for monotonic child tool and extension restrictions, with inherited async/nested propagation and bounded audit metadata. Thanks to aoguai for #585.
33
+ - Added durable v3 process-terminal proof for detached async runners, with exact close observation, conservative unknown states after observer loss, and status/RPC projections. Thanks to shaggitza for #626.
34
+ - Added `subagents.defaultThinking` for project- or user-scoped default thinking levels on agents without explicit thinking settings. Thanks to corrius for #612.
35
+ - Documented that builtin worker and delegate agents use strict tool allowlists and do not inherit ambient parent extension tools; custom agents must explicitly name extension tools and load their providers. Thanks to buihongduc132 for #586.
36
+
37
+ ### Fixed
38
+ - Preferred direct empty terminal-response evidence over stale tool errors so fallback models can retry abandoned child turns, and stopped treating successful tool output as a hidden failure. Thanks to Dmitry S. (@nuzayets) for #645.
39
+ - Separated evidence acceptance from independent review: evidence levels now end at `verified`, risky runs carry an orthogonal review requirement, `review-required` reports pending review while preserving `evidenceStatus`, and `reviewed` is reserved for achieved independent review. Explicit `reviewed` remains schema-recognized solely for actionable preflight recovery. Thanks to Theodor Hillmann (@t0dorakis) for #440.
40
+ - Bound public preflight launch digests to resolved skill injection metadata, matching execution when skill descriptions change.
41
+ - Classified missing resolved MCP direct tools as a host/pi-mcp-adapter child-registration problem while preserving strict fail-closed diagnostics. Thanks to peedrr for #638.
42
+
5
43
  ## [0.36.0] - 2026-07-24
6
44
 
7
45
  ### Added
package/README.md CHANGED
@@ -6,7 +6,7 @@
6
6
 
7
7
  `pi-subagents` lets Pi delegate work to focused child agents. Use it for code review, scouting, implementation, parallel audits, saved workflows, background jobs, and anything else that benefits from a second or third set of model eyes.
8
8
 
9
- https://github.com/user-attachments/assets/702554ec-faaf-4635-80aa-fb5d6e292fd1
9
+ <https://github.com/user-attachments/assets/702554ec-faaf-4635-80aa-fb5d6e292fd1>
10
10
 
11
11
  ## Installation
12
12
 
@@ -157,7 +157,37 @@ For a persistent override, edit settings. This example pins the reviewer everywh
157
157
 
158
158
  Use `~/.pi/agent/settings.json` for a user override or the project config settings file (`.pi/settings.json` in standard Pi) for a project override. `subagents.defaultModel` applies to builtin, package, user, and project agents that do not set `model` in frontmatter. Per-run model overrides and `agentOverrides.<name>.model` still win, and explicit agent frontmatter still wins over the global default. The same `agentOverrides` block can change `tools`, `skills`, inherited context, prompt text, or disable a builtin. Matching user and project agents also receive override fields that their frontmatter leaves unset, so a shared project config agent can keep the persona while local settings choose the model.
159
159
 
160
- If your provider rejects model IDs with thinking suffixes, set `subagents.disableThinking: true` in user or project settings. That clears bundled builtin thinking defaults in one place; an explicit higher-precedence `agentOverrides.<name>.thinking` value can opt a role back in.
160
+ Set `subagents.defaultThinking` to give builtin, package, user, and project agents without a `thinking` value a shared thinking level, independent of the parent session's default. Project settings win over user settings. Explicit frontmatter, `agentOverrides.<name>.thinking`, and per-run thinking overrides still win; `thinking: false` remains an explicit opt-out:
161
+
162
+ ```json
163
+ {
164
+ "subagents": {
165
+ "defaultThinking": "medium",
166
+ "agentOverrides": {
167
+ "reviewer": { "thinking": "high" }
168
+ }
169
+ }
170
+ }
171
+ ```
172
+
173
+ If your provider rejects model IDs with thinking suffixes, set `subagents.disableThinking: true` in user or project settings. That clears bundled builtin thinking defaults in one place; an explicit higher-precedence `agentOverrides.<name>.thinking` value can opt a role back in. Existing custom-agent frontmatter remains authoritative.
174
+
175
+ Set `subagents.defaultExtensions` to give builtin, package, user, and project agents without an `extensions` field a shared extension allowlist. Absent preserves Pi's normal ambient extension discovery. Present as an empty array, the default sets `extensions: []` for agents that do not explicitly define it, disabling ambient extension loading. Present as a non-empty array, the default supplies that allowlist to agents that do not explicitly define one. Project settings win over user settings. Use `agentOverrides.<name>.extensions` for per-agent settings; explicit custom-agent frontmatter remains authoritative.
176
+
177
+ ```json
178
+ {
179
+ "subagents": {
180
+ "defaultExtensions": [],
181
+ "agentOverrides": {
182
+ "researcher": {
183
+ "extensions": ["./tools/research.ts"]
184
+ }
185
+ }
186
+ }
187
+ }
188
+ ```
189
+
190
+ A non-array value, an array containing a non-string entry, or an empty/whitespace-only string raises a settings error naming `defaultExtensions` and the offending settings file, matching the validation pattern used by `defaultModel` and `defaultThinking`.
161
191
 
162
192
  To inspect what `pi-subagents` has actually loaded right now, use:
163
193
 
@@ -242,9 +272,9 @@ To keep subagents inside a budget or compliance profile, enforce a model scope.
242
272
 
243
273
  ## Where running subagents show up
244
274
 
245
- Foreground runs stream progress in the conversation while they run.
275
+ Foreground runs stream progress in the conversation while they run. They default to a generous 30-minute wall-clock timeout when neither the call nor the selected agent provides a timeout; explicit `timeoutMs`/`maxRuntimeMs` and agent defaults win.
246
276
 
247
- Background runs keep working after control returns to you. Inspect active runs with `subagent({ action: "status" })`, or a specific run with `subagent({ action: "status", id: "..." })`. In the TUI, a persistent FleetView below the editor shows `main` plus active children with task, elapsed time, and token totals. When the focused editor is empty, use `↑`/`↓` or `j`/`k` to select a child and `Enter` to inspect it; normal editor input is never intercepted.
277
+ Background runs keep working after control returns to you. Inspect active runs with `subagent({ action: "status" })`, or a specific run with `subagent({ action: "status", id: "..." })`. In the TUI, a persistent FleetView below the editor by default shows `main` plus active children with task, elapsed time, and token totals. Set `fleetViewPlacement` to `"aboveEditor"` to move it above the editor. When the focused editor is empty, use `↑`/`↓` or `j`/`k` to select a child and `Enter` to inspect it; normal editor input is never intercepted.
248
278
 
249
279
  `/subagents-fleet` opens the live, inspection-only fleet inspector with current-session foreground work, recent async children, structured Markdown/tool transcripts, and completed output/session paths. Use `↑`/`↓` or `j`/`k` to select a child, `Shift+K`/`Shift+J` to scroll one line, `PgUp`/`PgDn` to scroll one page, `x`/`Ctrl+O` to toggle tool details, `r` to refresh, and `Esc` to close. `Ctrl+Alt+F` opens the same inspector even while a foreground turn is active and slash input is queued. Without a TUI, `/subagents-fleet` retains the textual `subagent({ action: "status", view: "fleet" })` fallback. Mutations stay in explicit commands: run `/subagents-stop` and pick from the selector, or use `/subagents-stop <run-id>` / `subagent({ action: "stop", id: "..." })` when you already know the id. To inspect one background child in text, use `subagent({ action: "status", id: "...", view: "transcript" })`; add `index` for a specific child in a parallel or chain run.
250
280
 
@@ -256,13 +286,15 @@ You can also ask naturally:
256
286
  Show me the current async runs.
257
287
  ```
258
288
 
289
+ Lifecycle artifact v3 adds `process-terminal-candidate.json` (private runner evidence) and `process-terminal.json` (the public proof projection). A proof is `observed` only after the live parent observes the exact detached runner's `close` event, every recorded child writer has a close record, and any tracked canonical-session lease is free. If the observer is unavailable, the proof is `unknown`; do not infer process exit from `endedAt`, result-file existence, PID disappearance, or lease-directory absence. The `subagent:process-terminal` event and RPC `ping.capabilities.processTerminalProof` expose this status. Process proof is point-in-time evidence and remains separate from execution success or stopped/non-resumable state.
290
+
259
291
  Async runs also write machine-readable lifecycle artifacts for observability and workflow gates. For a top-level async run, `details.asyncDir` points at a directory containing `status.json`, `events.jsonl`, `output-<index>.log`, and `subagent-log-<runId>.md`; the final summary is written to Pi's subagent results directory as `<runId>.json`. Nested async runs use the same shape under the nested async root and are discoverable through status projections that read the nested-run registry. These files are append/update artifacts only; interactive foreground behavior is unchanged.
260
292
 
261
293
  Foreground and async runners share bounded child-protocol handling. A child JSONL line above 4 MiB fails with structured `protocolError` code `protocol_output_limit`, stderr retains only its latest 128 KiB, split UTF-8 and final unterminated JSON events remain valid, and `agent_end.willRetry` defers completion until the child settles. Current Pi builds use `agent_settled` as the terminal watermark; older builds retain the bounded terminal-message fallback.
262
294
 
263
295
  The stable v1 status/result fields are `lifecycleArtifactVersion`, `runId`/`id`, `sessionId`, `mode`, `state`, `startedAt`, `lastUpdate`, `endedAt`, `durationMs`, `cwd`, `asyncDir`, `sessionFile`, `outputFile`, `workflowGraph`, `steps`, `results`, `totalTokens`, `totalCost`, `model`/`attemptedModels`/`modelAttempts`, `toolCount`, `turnCount`, and nested `children` when a child is allowed to launch subagents. `events.jsonl` records lifecycle transitions such as `subagent.run.started`, `subagent.step.started`, `subagent.step.completed`/`failed`/`paused`/`stopped`, control attention events, nested interrupt failures, and `subagent.run.completed`/`stopped`; run boundary events include the lifecycle artifact version. Consumers should read these JSON files instead of scraping terminal output; unknown fields and event types should be ignored for forward compatibility.
264
296
 
265
- Other Pi extensions can use the versioned in-process event-bus RPC instead of scraping slash output or calling internal modules. Listen for `subagents:rpc:v1:ready`, send requests on `subagents:rpc:v1:request`, and read replies from `subagents:rpc:v1:reply:<requestId>`. The `ping` capability metadata also advertises `events.asyncComplete` for exact process-local completion correlation after RPC `spawn`.
297
+ Other Pi extensions can use the versioned in-process event-bus RPC instead of scraping slash output or calling internal modules. Listen for `subagents:rpc:v1:ready`, send requests on `subagents:rpc:v1:request`, and read replies from `subagents:rpc:v1:reply:<requestId>`. The `ping` capability metadata also advertises `events.asyncComplete` for exact process-local completion correlation after RPC `spawn`. Delegation v1/v2 progress updates carry `runId` as soon as foreground execution allocates it, so a caller can retain the package-owned revival target even if its own tool turn is interrupted before the terminal response.
266
298
 
267
299
  ```typescript
268
300
  const requestId = crypto.randomUUID();
@@ -278,7 +310,7 @@ pi.events.emit("subagents:rpc:v1:request", {
278
310
  });
279
311
  ```
280
312
 
281
- The v1 methods are `ping`, `status`, `spawn`, `steer`, `interrupt`, and `stop`. `status`, `steer`, and `interrupt` reuse the normal control actions. `steer` requires an async run `id` (plus optional child `index`) and a non-empty `message`; its reply preserves the normal acknowledged-delivery result. RPC steering disables the direct tool's pause-and-revive recovery so an extension keeps authority over the exact child it spawned; `ping.capabilities.nonRecoveringSteer` advertises this guarantee. `spawn` is async-only: omit `async` or set `async: true`, omit `clarify` or set `clarify: false`, and do not pass management `action` values. It goes through the same executor as the `subagent` tool, so agent discovery, validation, session attribution, configured spawn caps, child-safety depth, artifacts, and async status all behave the same. `stop` targets current-session top-level async runs through the stop control channel and records a `stopped` lifecycle instead of reporting a timeout.
313
+ The v1 methods are `ping`, `status`, `spawn`, `steer`, `interrupt`, `stop`, and `resume`. `status`, `steer`, `interrupt`, and `resume` reuse the normal package-owned actions. `steer` requires an async run `id` (plus optional child `index`) and a non-empty `message`; its reply preserves the normal acknowledged-delivery result. RPC steering disables the direct tool's pause-and-revive recovery so an extension keeps authority over the exact child it spawned; `ping.capabilities.nonRecoveringSteer` advertises this guarantee. `resume` requires a run target and non-empty `message`; it delegates to the existing revival path, which validates current-session ownership, persisted session/recovery metadata, stopped/live state, capability ceilings, and the exclusive session lease before returning the new async run details. Callers may request a `file-only` output path for the revived result without overriding its model, tools, or budgets. `ping.capabilities.resume` advertises this seam. `spawn` is async-only: omit `async` or set `async: true`, omit `clarify` or set `clarify: false`, and do not pass management `action` values. It goes through the same executor as the `subagent` tool, so agent discovery, validation, session attribution, configured spawn caps, child-safety depth, artifacts, and async status all behave the same. `stop` targets current-session top-level async runs through the stop control channel and records a `stopped` lifecycle instead of reporting a timeout.
282
314
 
283
315
  `pi.events` is in-process only. It does not reach separate Pi processes or child subagents; use the file lifecycle artifacts or `pi-intercom` for cross-process coordination.
284
316
 
@@ -565,7 +597,7 @@ Append `[key=value,...]` to an agent name to override defaults. `/chain` applies
565
597
  | `cwd` | `cwd=packages/api` | Run the step in a subdirectory. |
566
598
  | `count` | `count=3` | Fan a group task into N copies (only inside a `( ... )` group). |
567
599
  | `outputSchema` | `outputSchema=schema.json` | Validate structured output against a JSON Schema file (path resolved against the session cwd, not an inline step `cwd`). |
568
- | `acceptance` | `acceptance=checked` | Inline acceptance level: `auto`, `attested`, or `checked`. Use the tool API or saved `.chain.json` for object contracts such as `none` or `verified`; `reviewed` is inferred-only. |
600
+ | `acceptance` | `acceptance=checked` | Inline evidence level: `auto`, `attested`, or `checked`. Use the tool API or saved `.chain.json` for object contracts such as `none`, `verified`, or an orthogonal review requirement. `reviewed` is an achieved status, not an input level. |
569
601
 
570
602
  Set `output=false`, `reads=false`, or `skills=false` to disable that behavior explicitly. Do not use `output=false` for file-only returns; use `outputMode=file-only` with an `output` path.
571
603
 
@@ -759,7 +791,7 @@ Important fields:
759
791
  | `defaultReads` | Files to read before running in chain/parallel behavior. |
760
792
  | `defaultProgress` | Maintain `progress.md`. |
761
793
  | `async` | Default a single-agent launch to background (`true`) or foreground (`false`) when the call omits `async`. Explicit call values and `forceTopLevelAsync` win. |
762
- | `timeoutMs` | Positive integer default runtime deadline in milliseconds for single-agent launches. An explicit `timeoutMs` or `maxRuntimeMs` wins. |
794
+ | `timeoutMs` | Positive integer default runtime deadline in milliseconds for single-agent launches. Foreground launches use 30 minutes when neither the call nor agent provides a timeout; explicit `timeoutMs`/`maxRuntimeMs` and agent defaults win. |
763
795
  | `turnBudget` | JSON object default such as `{"maxTurns":20,"graceTurns":2}` for single-agent launches. An explicit call value wins, followed by this agent default, then global `turnBudget` config. |
764
796
  | `acceptance` | Acceptance default for single-agent launches. Use a scalar level such as `checked` or an inline/block YAML map such as `{ level: "none", reason: "lightweight lookup" }`. Explicit call values win; chain and parallel acceptance remains task/step configuration. |
765
797
  | `acceptanceRole` | Optional `read-only` or `writer` role for automatic acceptance inference. Explicit task mutation or no-edit intent wins; otherwise the declared role replaces agent-name guessing. This does not grant or revoke tools. |
@@ -796,7 +828,7 @@ Examples:
796
828
  - `tools: subagent, read`: a child-safe `subagent` tool is available inside that child so it can run explicitly assigned nested fanout.
797
829
  - `tools: read, fixture_search` plus `subagentOnlyExtensions: ./tools/fixture-search.ts`: the provider loads only in this agent's child process, and the registered `fixture_search` name survives the strict allowlist.
798
830
 
799
- Direct MCP tools require [pi-mcp-adapter](https://github.com/nicobailon/pi-mcp-adapter). Subagents only receive direct MCP tools when `mcp:` entries are listed in their frontmatter; global `directTools: true` in `mcp.json` is not enough by itself. The generic `mcp` proxy tool can still be used for discovery when available. The adapter caches tool metadata at startup, so after connecting a new MCP server for the first time, restart Pi before relying on direct tools. An `mcp:` entry named `subagent` does not authorize nested fanout; only the builtin `subagent` tool name does.
831
+ Direct MCP tools require [pi-mcp-adapter](https://github.com/nicobailon/pi-mcp-adapter). Subagents only receive direct MCP tools when `mcp:` entries are listed in their frontmatter; global `directTools: true` in `mcp.json` is not enough by itself. The generic `mcp` proxy tool can still be used for discovery when available. The adapter caches tool metadata at startup, so after connecting a new MCP server for the first time, restart Pi before relying on direct tools. An `mcp:` entry named `subagent` does not authorize nested fanout; only the builtin `subagent` tool name does. If a resolved direct MCP name is missing from the child registry, pi-subagents keeps the launch failed under the strict allowlist and identifies the condition as a host/pi-mcp-adapter registration problem; verify that the adapter registers the selected tools before child startup.
800
832
 
801
833
  `extensions` controls child extension loading:
802
834
 
@@ -814,6 +846,8 @@ When `extensions` is present, normal discovered extensions are disabled; the lis
814
846
 
815
847
  Use `subagentOnlyExtensions` when a custom extension tool should exist only inside child sessions. It is scoped by agent config: every run of that agent receives those extension paths, while other agents do not unless they declare the same field. The current model does not have a separate named-subagent audience inside one agent definition.
816
848
 
849
+ To apply the same `extensions` allowlist to every agent that does not declare its own, set `subagents.defaultExtensions` in user or project settings. Omit it to preserve ambient extension discovery or set it to `[]` to disable ambient extensions by default; project settings win over user settings. Agents that explicitly define `extensions` keep their own value, including an empty `extensions:` field.
850
+
817
851
  Before the first model turn, the child runtime compares every explicit tool name with Pi's final filtered registry. A missing provider now fails the run with the unavailable names and concrete `subagentOnlyExtensions`/`extensions` guidance instead of letting a direct or chained child silently continue without its requested tools.
818
852
 
819
853
  ## Chain files
@@ -973,6 +1007,7 @@ Missing skills do not fail execution. The result summary shows a warning.
973
1007
  The package bundles a `pi-subagents` skill that is automatically available to the parent agent when the extension is installed. It is for the orchestrating parent only: child subagents never receive it, and their context is explicitly filtered to strip parent-only orchestration instructions.
974
1008
 
975
1009
  What the bundled skill covers:
1010
+
976
1011
  - **Delegation patterns**: when to launch which agent, whether to use single, parallel, chain, or async mode, and whether to use fresh or forked context
977
1012
  - **Prompt workflow recipes**: how to apply the packaged techniques directly with `subagent(...)` when the user describes the workflow in natural language instead of invoking a slash command. This includes parallel review, review-loop, parallel research, parallel context-build, parallel handoff-plan, gather-context-and-clarify, and parallel cleanup
978
1013
  - **Role-agent prompting guidance**: compact contract prompts instead of long scripts, what to include in role-specific meta prompts, and retrieval budgets for researchers
@@ -987,6 +1022,33 @@ If you are writing an agent that orchestrates subagents, the bundled skill helps
987
1022
  Pi extensions can request configured foreground agents through the public event
988
1023
  contract exported by `pi-subagents/delegation`.
989
1024
 
1025
+ ### Launch contract preflight
1026
+
1027
+ Use `pi-subagents/preflight` when an extension needs to inspect the resolved child launch contract before deciding whether to run anything:
1028
+
1029
+ ```ts
1030
+ import { resolveSubagentLaunchContract } from "pi-subagents/preflight";
1031
+
1032
+ const result = await resolveSubagentLaunchContract({
1033
+ agent: "reviewer",
1034
+ task: "Review the current diff.",
1035
+ context: "fresh",
1036
+ cwd: ctx.cwd,
1037
+ sessionRoot: "/tmp/my-extension-preflight-session-root",
1038
+ availableModels: ctx.modelRegistry.getAvailable(),
1039
+ });
1040
+
1041
+ if (!result.ok) {
1042
+ // missing_agent, ambiguous_agent, missing_skill, denied_required_tool,
1043
+ // invalid_artifact_dir, invalid_cwd, or unsupported_mode
1044
+ throw new Error(result.message);
1045
+ }
1046
+
1047
+ console.log(result.contract.digest, result.contract.tools.effectiveAllowlist);
1048
+ ```
1049
+
1050
+ Preflight covers ordinary single-agent launch resolution under public contract version 2: selected agent identity and shadowed candidates, a versioned parsed-definition digest (including system prompt and launch-affecting model, tool, skill, extension, output, and memory fields), fresh/fork context, effective model and thinking, skill and tool resolution, direct MCP selections, runtime/configured extensions, artifact/session paths, async lifecycle/status/result/event/process-terminal paths, package/lifecycle versions, capability-ceiling audit data, and stable digests. `launchContractDigest` is the canonical digest of the caller task, effective system prompt (including the resolved `turnBudget` prompt augmentation when supplied), model candidates, effective tools/extensions/MCP (including inherited capability ceilings), output binding, and structured-output schema that ordinary foreground and async execution report in results/status/events and metadata. Runtime acceptance prose and output-task annotations are intentionally excluded because side-effect-free preflight does not resolve those host/runtime augmentations; the contract version and task digest make that boundary explicit. Raw prompts are not exposed in public contract output. It is side-effect-free for launch state: it does not create child sessions, temp prompt files, structured-output runtimes, tool-diagnostic files, or run artifacts. Some host-owned facts, such as exact fork snapshots, nested async roots, and live model registries, can only be proven by the Pi host; those appear as `host_required` diagnostics instead of silently pretending to be exact.
1051
+
990
1052
  ### Delegation v1
991
1053
 
992
1054
  The compatibility v1 contract runs one configured foreground agent per request:
@@ -1104,6 +1166,26 @@ tools, skills, context, model policy, and workspace authority; it is not a
1104
1166
  sandbox or a durable task broker. `pi-subagents/delegation` is the canonical
1105
1167
  contract for extension integrations.
1106
1168
 
1169
+ ## Capability ceilings
1170
+
1171
+ Parent extensions can enforce an out-of-band, session-scoped capability ceiling without adding a model-visible field to `subagent`:
1172
+
1173
+ ```ts
1174
+ import { registerSubagentCapabilityCeiling } from "pi-subagents/capability-ceiling";
1175
+
1176
+ const restriction = registerSubagentCapabilityCeiling({
1177
+ sessionId: ctx.sessionManager.getSessionId(),
1178
+ source: "plan-mode",
1179
+ ceiling: { allowedTools: ["read", "grep", "find", "ls"], denyExtensions: true },
1180
+ });
1181
+ // restriction.update(...) replaces this provider's policy atomically.
1182
+ // restriction.dispose() removes only this provider's registration.
1183
+ ```
1184
+
1185
+ Active registrations intersect their `allowedTools` sets and OR `denyExtensions`; an explicit empty list means no caller-facing tools, while an omitted list does not restrict names. The resolved snapshot is propagated monotonically to nested and async children and is retained for recovery. `structured_output` may remain as a package-owned internal protocol tool when an output schema requires it; it is not a caller capability. A denied lazy-skill `read` requirement fails before spawn rather than widening the ceiling.
1186
+
1187
+ `denyExtensions` suppresses ambient, configured, and MCP provider extensions while retaining the package runtime needed for child protocol enforcement. This is a same-process policy boundary, not a sandbox against malicious code already running in the parent process. Schedules created while a ceiling is active are rejected until durable schedule persistence is available; unrestricted schedules remain subject to any policy active when they fire. Public status exposes bounded audit counts and sources, never full extension paths.
1188
+
1107
1189
  ## Background-work provider API
1108
1190
 
1109
1191
  Other Pi extensions can make their current-session jobs visible to `subagent_wait` through the versioned process-local provider contract:
@@ -1300,7 +1382,7 @@ Agent definitions are not loaded into context by default. Management actions let
1300
1382
  | `clarify` | boolean | false | Show TUI preview/edit flow. Explicit `clarify: true` keeps the run foreground for the clarify UI. |
1301
1383
  | `agentScope` | `user \| project \| both` | `both` | Agent discovery scope. Project wins on collisions. |
1302
1384
  | `async` | boolean | false | Background execution. For chains, `clarify: true` explicitly keeps the run foreground for the clarify UI. |
1303
- | `timeoutMs` / `maxRuntimeMs` | number | none | Optional run-level max runtime in milliseconds for foreground and async/background runs. |
1385
+ | `timeoutMs` / `maxRuntimeMs` | number | 30 min foreground; none async | Optional run-level max runtime in milliseconds. Foreground uses 30 minutes only when neither the call nor selected agent provides a timeout. |
1304
1386
  | `turnBudget` | object | none | Optional assistant-turn budget `{ maxTurns, graceTurns }`. At `maxTurns` the child is warned to wrap up. After the grace window (default 1), termination occurs at the next assistant boundary; a response that starts tool work records `termination-deferred` until a later boundary. Partial output is returned on abort. |
1305
1387
  | `toolBudget` | object | none | Optional child tool-call budget `{ soft?, hard, block? }`. At `soft` the child is nudged to finalize. After `hard`, configured tools are blocked; `block` defaults to `read`, `grep`, `find`, and `ls`, while `"*"` blocks every tool call. Final assistant text is never blocked. |
1306
1388
  | `cwd` | string | runtime cwd | Override working directory. |
@@ -1309,7 +1391,7 @@ Agent definitions are not loaded into context by default. Management actions let
1309
1391
  | `includeProgress` | boolean | false | Include full progress in result. |
1310
1392
  | `share` | boolean | false | Upload session export to GitHub Gist. |
1311
1393
  | `sessionDir` | string | derived | Override session log directory. |
1312
- | `acceptance` | string/object/false | inferred | Override inferred gates with `"auto"`, `"attested"`, `"checked"`, `"verified"`, or `{ level: "none", reason: "..." }`. `reviewed` is inferred-only; explicit requests fail preflight. `false` disables gates. With `agentContract: { version: 1 }`, omitted, `"auto"`, and `false` mean no acceptance request for that run; explicit acceptance is reported separately from execution. |
1394
+ | `acceptance` | string/object/false | inferred | Configure evidence gates with `"auto"`, `"attested"`, `"checked"`, `"verified"`, or `{ level: "none", reason: "..." }`. Independent review is orthogonal: use `review: { required: true, agent?: "reviewer", focus?: "..." }`. `review-required` means evidence passed but review is pending; `reviewed` is achieved only after a real independent result. Explicit `"reviewed"` remains schema-recognized solely for actionable preflight recovery. For reviewer/read-only calls, omit acceptance. `false` disables gates. With `agentContract: { version: 1 }`, omitted, `"auto"`, and `false` mean no acceptance request for that run; explicit acceptance is reported separately from execution. |
1313
1395
 
1314
1396
  `agentContract: { version: 1 }` keeps existing fields and artifacts but adds derived `execution`, `acceptance`, `review`, and `effects` projections. In v1, acceptance failures do not rewrite execution success, and an explicit completion guard reports `effects.fileMutation` instead of failing the run by itself. Chain steps default to advancing on execution under v1; set `gateOn: "acceptance"` on a v1 step or parallel task when rejected acceptance should stop the chain.
1315
1397
 
@@ -1413,7 +1495,15 @@ Makes top-level calls use background execution when the request does not explici
1413
1495
  { "fleetView": false }
1414
1496
  ```
1415
1497
 
1416
- Controls the persistent, navigable FleetView below the editor. The default is `true`. Set it to `false` to hide FleetView without disabling status tracking, completion notifications, `/subagents-fleet`, or lifecycle events.
1498
+ Controls the persistent, navigable FleetView. The default is `true`. Set it to `false` to hide FleetView without disabling status tracking, completion notifications, `/subagents-fleet`, or lifecycle events.
1499
+
1500
+ ### `fleetViewPlacement`
1501
+
1502
+ ```json
1503
+ { "fleetViewPlacement": "aboveEditor" }
1504
+ ```
1505
+
1506
+ Places the persistent FleetView either `"belowEditor"` or `"aboveEditor"`. The default is `"belowEditor"`; invalid values fall back to `"belowEditor"`.
1417
1507
 
1418
1508
  ### `asyncWidget`
1419
1509
 
@@ -1518,7 +1608,8 @@ Overrides the command used to launch child Pi processes. Package wrappers can se
1518
1608
  {
1519
1609
  "intercomBridge": {
1520
1610
  "mode": "always",
1521
- "instructionFile": "./intercom-bridge.md"
1611
+ "instructionFile": "./intercom-bridge.md",
1612
+ "resultDelivery": true
1522
1613
  }
1523
1614
  }
1524
1615
  ```
@@ -1529,6 +1620,7 @@ Fields:
1529
1620
 
1530
1621
  - `mode`: default `always`; use `fork-only` to inject only for forked runs, or `off` to disable the bridge.
1531
1622
  - `instructionFile`: optional Markdown template replacing the default bridge instructions. `{orchestratorTarget}` is interpolated. Relative paths resolve from `~/.pi/agent/extensions/subagent/`.
1623
+ - `resultDelivery`: default `true`; attempts acknowledged grouped completion delivery through an external `subagent:result-intercom` listener. Set `false` when native parent notifications own completion delivery. Supervisor asks/progress remain active, and genuine enabled-transport acknowledgement failures remain visible.
1532
1624
 
1533
1625
  Bridge activation requires a targetable current parent session id, which `pi-subagents` passes to children automatically. It no longer depends on an external `pi-intercom` installation or per-agent extension allowlists.
1534
1626
 
@@ -1644,15 +1736,18 @@ Every run resolves an effective acceptance policy. Callers may omit `acceptance`
1644
1736
  }
1645
1737
  ```
1646
1738
 
1647
- Acceptance policies use the levels `auto`, `none`, `attested`, `checked`, `verified`, and `reviewed`. `acceptance: "auto"` is the default. Callers may explicitly request levels through `verified`; `reviewed` is reserved for inferred policy because the current execution path cannot supply an independent reviewer result. Explicit `reviewed` fails preflight instead of spawning a child that is guaranteed to be rejected. Read-only tasks infer lightweight attestation, normal writer tasks infer checked evidence, and async/risky/dynamic writer contexts infer a reviewed gate. Agent frontmatter or `subagents.agentOverrides` may set `acceptanceRole: "read-only" | "writer"` for ambiguous tasks; explicit task mutation or no-edit intent wins over that role, while omitted metadata preserves the existing reviewer/scout/worker name heuristics. The role affects acceptance inference only and does not change tool access. The bare string `"none"` is rejected; use `{ level: "none", reason: "..." }` instead. `acceptance: false` is accepted only as a deprecated shorthand for disabling gates.
1739
+ Acceptance evidence levels are `auto`, `none`, `attested`, `checked`, and `verified`. `acceptance: "auto"` is the default. Review is a separate gate configured with `acceptance.review`; async, risky, and dynamic writer contexts infer checked evidence plus `review: { agent: "reviewer", required: true }`. Read-only tasks infer lightweight attestation, while normal writer tasks infer checked evidence without review. Agent frontmatter or `subagents.agentOverrides` may set `acceptanceRole: "read-only" | "writer"` for ambiguous tasks; explicit task mutation or no-edit intent wins over that role, while omitted metadata preserves the existing reviewer/scout/worker name heuristics. The role affects acceptance inference only and does not change tool access. The bare string `"none"` is rejected; use `{ level: "none", reason: "..." }` instead. `acceptance: false` is accepted only as a deprecated shorthand for disabling gates.
1740
+
1741
+ For reviewer/read-only calls, omit `acceptance`. The explicit value `"reviewed"` is not a policy level: it remains schema-recognized only so semantic preflight can explain the mistake without spawning a child. To require review of a writer result, use `acceptance: { level: "checked", review: { required: true, agent: "reviewer" } }` and orchestrate the reviewer separately.
1648
1742
 
1649
- Acceptance provenance is stored separately from child prose:
1743
+ Acceptance provenance is stored separately from child prose. `evidenceStatus` preserves evidence progress when the overall status is waiting on or has completed review:
1650
1744
 
1651
1745
  - `claimed`: child finished but did not provide structured evidence.
1652
1746
  - `attested`: child returned a structured acceptance report.
1653
1747
  - `checked`: runtime structural checks passed, such as required evidence and no staged files.
1654
1748
  - `verified`: configured runtime verification commands passed. Child-reported command success does not count.
1655
- - `reviewed`: an independent reviewer result is present.
1749
+ - `review-required`: required evidence passed, but no independent reviewer result has been supplied.
1750
+ - `reviewed`: an independent reviewer result is present and has no blockers.
1656
1751
  - `rejected`: attestation, structural checks, verification, or review failed.
1657
1752
 
1658
1753
  For `attested` or stricter levels, the child prompt includes a standardized acceptance section and asks for a fenced `acceptance-report` JSON block. The parser canonicalizes known enum synonyms, snake_case report keys and wrappers, underscore fence tags, unambiguous scalar arrays, string booleans, and criterion-id separators. Unknown or ambiguous keys and enum values fail with field-level diagnostics. Explicit empty `changedFiles` and `testsAddedOrUpdated` arrays are recorded as not applicable; missing fields and empty required command or validation evidence still fail.
@@ -9,4 +9,6 @@ inheritSkills: false
9
9
 
10
10
  You are a delegated agent. Execute the assigned task using the provided tools. Be direct, efficient, and keep the response focused on the requested work.
11
11
 
12
+ The builtin delegate uses a strict tool allowlist and does not inherit ambient extension tools from the parent session. To use an extension tool, configure a custom agent with the tool name explicitly listed in `tools` and load its provider through `extensions` or `subagentOnlyExtensions`.
13
+
12
14
  If runtime bridge instructions identify a safe supervisor target and you are blocked or need a decision, use `contact_supervisor` with `reason: "need_decision"` and stay alive for the reply. Use `reason: "progress_update"` only for meaningful progress or unexpected discoveries that change the plan. Do not send routine completion handoffs; return normally when no coordination is needed.
package/agents/planner.md CHANGED
@@ -1,8 +1,9 @@
1
1
  ---
2
2
  name: planner
3
3
  description: Creates implementation plans from context and requirements
4
- tools: read, grep, find, ls, write, intercom
4
+ tools: read, grep, find, ls, intercom
5
5
  thinking: high
6
+ acceptanceRole: read-only
6
7
  systemPromptMode: replace
7
8
  inheritProjectContext: true
8
9
  inheritSkills: false
package/agents/worker.md CHANGED
@@ -17,6 +17,8 @@ You are the single writer thread. Your job is to execute the assigned task or ap
17
17
 
18
18
  Use the provided tools directly. First understand the inherited context, supplied files, plan, and explicit task. Then implement carefully and minimally.
19
19
 
20
+ The builtin worker uses a strict tool allowlist. It does not inherit ambient extension tools from the parent session. To use an extension tool, configure a custom agent with the tool name explicitly listed in `tools` and load its provider through `extensions` or `subagentOnlyExtensions`.
21
+
20
22
  If the task is framed as an approved direction, oracle handoff, or execution plan, treat that direction as the contract. Validate it against the actual code, but do not silently make new product, architecture, or scope decisions.
21
23
 
22
24
  If the implementation reveals a decision that was not approved and is required to continue safely, pause and escalate through the live coordination channel. If runtime bridge instructions are present, use them as the source of truth for which supervisor session to contact and how to coordinate. Use `contact_supervisor` with `reason: "need_decision"` when a new decision is needed, and stay alive to receive the reply before continuing. Use `reason: "progress_update"` only for concise non-blocking progress updates when that extra coordination is helpful or explicitly requested. Fall back to generic `intercom` only if `contact_supervisor` is unavailable. Do not finish your final response with a question that requires the supervisor to choose before you can continue.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-subagents",
3
- "version": "0.36.0",
3
+ "version": "0.37.1",
4
4
  "description": "Pi extension for delegating tasks to subagents with chains, parallel execution, and TUI clarification",
5
5
  "author": "Nico Bailon",
6
6
  "license": "MIT",
@@ -8,7 +8,9 @@
8
8
  "exports": {
9
9
  ".": "./index.ts",
10
10
  "./background-work": "./src/api/background-work.ts",
11
- "./delegation": "./src/api/delegation.ts"
11
+ "./delegation": "./src/api/delegation.ts",
12
+ "./capability-ceiling": "./src/api/capability-ceiling.ts",
13
+ "./preflight": "./src/api/preflight.ts"
12
14
  },
13
15
  "repository": {
14
16
  "type": "git",
@@ -43,8 +45,8 @@
43
45
  "scripts": {
44
46
  "test": "npm run test:unit",
45
47
  "test:unit": "node --experimental-strip-types --test test/unit/*.test.ts",
46
- "test:integration": "node --experimental-transform-types --import ./test/support/register-loader.mjs --test test/integration/*.test.ts",
47
- "test:e2e": "node --experimental-transform-types --import ./test/support/register-loader.mjs --test test/e2e/*.test.ts",
48
+ "test:integration": "node --experimental-strip-types --import ./test/support/register-loader.mjs --test test/integration/*.test.ts",
49
+ "test:e2e": "node --experimental-strip-types --import ./test/support/register-loader.mjs --test test/e2e/*.test.ts",
48
50
  "test:all": "npm run test:unit && npm run test:integration && npm run test:e2e"
49
51
  },
50
52
  "pi": {