pi-subagents 0.58.0 → 0.59.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +65 -0
- package/docs/agents.md +5 -3
- package/docs/configuration.md +4 -4
- package/docs/extension-api.md +1 -1
- package/docs/models.md +24 -1
- package/docs/observability.md +1 -1
- package/docs/tool-reference.md +89 -4
- package/docs/workflows.md +93 -2
- package/package.json +3 -1
- package/prompts/review-loop.md +2 -2
- package/skills/council-mode/SKILL.md +1 -1
- package/skills/pi-subagents/SKILL.md +1 -1
- package/skills/pi-subagents/references/execution-controls.md +5 -1
- package/skills/pi-subagents/references/management-authoring-rpc.md +1 -2
- package/skills/pi-subagents/references/prompting-and-roles.md +24 -3
- package/src/agents/agent-management.ts +5 -17
- package/src/agents/agent-serializer.ts +4 -2
- package/src/agents/agents.ts +67 -26
- package/src/agents/runtime-agent-registry.ts +9 -16
- package/src/api/background-work.ts +5 -1
- package/src/api/delegation.ts +0 -7
- package/src/api/preflight.ts +6 -9
- package/src/extension/fanout-child.ts +5 -3
- package/src/extension/index.ts +51 -23
- package/src/extension/public-execution.ts +15 -2
- package/src/extension/schemas.ts +35 -10
- package/src/extension/tool-description.ts +18 -3
- package/src/intercom/result-intercom.ts +2 -0
- package/src/profiles/profiles.ts +5 -6
- package/src/runs/background/active-async-capacity.ts +2 -2
- package/src/runs/background/async-execution.ts +50 -35
- package/src/runs/background/async-job-tracker.ts +14 -12
- package/src/runs/background/async-resume.ts +23 -25
- package/src/runs/background/async-status-snapshot.ts +23 -261
- package/src/runs/background/async-status.ts +66 -7
- package/src/runs/background/chain-append.ts +6 -3
- package/src/runs/background/chain-root-attachment.ts +60 -8
- package/src/runs/background/fleet-view.ts +21 -11
- package/src/runs/background/notify.ts +158 -6
- package/src/runs/background/result-files.ts +2 -1
- package/src/runs/background/result-watcher.ts +2 -0
- package/src/runs/background/resume-guidance.ts +1 -1
- package/src/runs/background/retained-children.ts +1 -1
- package/src/runs/background/run-status.ts +18 -8
- package/src/runs/background/scheduled-runs.ts +86 -7
- package/src/runs/background/stale-run-reconciler.ts +10 -4
- package/src/runs/background/steering.ts +4 -14
- package/src/runs/background/subagent-runner.ts +346 -357
- package/src/runs/background/subagent-wait.ts +58 -10
- package/src/runs/background/terminal-run-index.ts +1 -1
- package/src/runs/background/wait-completions.ts +22 -1
- package/src/runs/background/wait-config.ts +23 -9
- package/src/runs/background/wait-tool.ts +9 -2
- package/src/runs/foreground/async-steering-action.ts +2 -2
- package/src/runs/foreground/execution.ts +114 -120
- package/src/runs/foreground/foreground-control.ts +3 -0
- package/src/runs/foreground/foreground-history.ts +1 -0
- package/src/runs/foreground/subagent-executor.ts +478 -239
- package/src/runs/foreground/workflow-detach-reconcile.ts +99 -200
- package/src/runs/shared/abort-recovery.ts +119 -0
- package/src/runs/shared/async-status-projection.ts +463 -0
- package/src/runs/shared/child-identity.ts +19 -4
- package/src/runs/shared/child-launch-plan.ts +151 -0
- package/src/runs/shared/completion-evidence.ts +89 -0
- package/src/runs/shared/completion-guard.ts +1 -1
- package/src/runs/shared/dynamic-fanout.ts +2 -2
- package/src/runs/shared/host-step-status.ts +230 -0
- package/src/runs/shared/lane-metadata.ts +105 -0
- package/src/runs/shared/mcp-config-sources.ts +42 -6
- package/src/runs/shared/mcp-direct-tool-allowlist.ts +93 -143
- package/src/runs/shared/mcp-direct-tool-grant.ts +197 -0
- package/src/runs/shared/model-fallback.ts +9 -2
- package/src/runs/shared/nested-events.ts +6 -2
- package/src/runs/shared/nested-render.ts +7 -3
- package/src/runs/shared/parallel-handoff.ts +419 -7
- package/src/runs/shared/parallel-utils.ts +7 -0
- package/src/runs/shared/pi-args.ts +20 -2
- package/src/runs/shared/single-output.ts +27 -4
- package/src/runs/shared/subagent-prompt-runtime.ts +13 -4
- package/src/runs/shared/worktree-cleanup-plan.ts +847 -0
- package/src/runs/shared/worktree.ts +18 -0
- package/src/shared/child-session-name.ts +46 -0
- package/src/shared/extension-context.ts +24 -0
- package/src/shared/formatters.ts +5 -2
- package/src/shared/launch-contract.ts +1 -1
- package/src/shared/settings.ts +9 -103
- package/src/shared/types.ts +198 -29
- package/src/shared/utils.ts +35 -55
- package/src/slash/delegation-adapters.ts +1 -8
- package/src/slash/delegation-request.ts +0 -4
- package/src/slash/slash-bridge.ts +1 -2
- package/src/slash/slash-commands.ts +369 -90
- package/src/slash/slash-live-state.ts +22 -11
- package/src/tui/fleet-status.ts +125 -74
- package/src/tui/fleet.ts +11 -5
- package/src/tui/render.ts +316 -27
- package/src/watchdog/turn-delta.ts +1 -1
- package/src/workflows/chat-progress.ts +6 -3
- package/src/workflows/host-command.ts +230 -0
- package/src/workflows/scripted-workflow.ts +452 -38
- package/src/workflows/workflow-child-summary.ts +9 -5
- package/src/workflows/workflow-preflight.ts +270 -0
- package/src/workflows/workflow-receipt.ts +43 -4
- package/src/workflows/workflow-settlement.ts +246 -0
- package/src/runs/shared/turn-budget.ts +0 -98
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,71 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [0.59.0] - 2026-08-28
|
|
6
|
+
|
|
7
|
+
### Highlights
|
|
8
|
+
- Run host commands from workflow scripts with safer saved output and clearer command results.
|
|
9
|
+
- Build sequential parallel workflows with `runs.lanes(...)`, launch preflight labels, and better retained-resume behavior.
|
|
10
|
+
- See cleaner async status, child labels, and worktree handoff details without extra setup.
|
|
11
|
+
- Recover more reliably from stale contexts, provider aborts, missing outputs, malformed MCP metadata, and Windows file locks.
|
|
12
|
+
- Control live workflow children from more places, including non-TUI and RPC hosts.
|
|
13
|
+
|
|
14
|
+
### Added
|
|
15
|
+
- Add `runs.host(...)` command steps to `workflowScript`, with required timeouts, saved output, and status and receipt evidence (#1648).
|
|
16
|
+
- Add CI and gate monitor rows that record monitor kind, terminal verdict, freshness, and report pointers in workflow status and receipts.
|
|
17
|
+
- Persist workflow lane metadata in child status, receipts, and existing worktree handoff manifests, including display-only worktree paths and branches.
|
|
18
|
+
- Add `runs.lanes(...)` for parallel sequential workflow stages with per-lane results and retained-resume support (#1633).
|
|
19
|
+
- Add display-only `preflight` lane metadata for workflowScript launches, with coverage warnings and planned-lane rendering in launch, status, and live-card views.
|
|
20
|
+
- Add a plan-only `worktree.cleanup` management action that records a cleanup plan without removing worktrees or branches.
|
|
21
|
+
- Give every subagent child session a human-readable display name, and include it in result, progress, status, workflow, nested, and intercom payloads. Thanks to [@yanqianglu](https://github.com/yanqianglu) for #1615.
|
|
22
|
+
- Add `/subagents-steer <run-id> [--child <child-id>] <message>` so non-TUI sessions and RPC hosts can steer live async runs. Thanks to [@yanqianglu](https://github.com/yanqianglu) for #1608.
|
|
23
|
+
- Add explicit `allowNestedSubagents` agent authorization for nested fanout without replacing inherited tools or extensions. Thanks to [@tutu359](https://github.com/tutu359) for #1587.
|
|
24
|
+
- Accept a child id on `/subagents-stop <run-id> <child-id>` so one child of a multi-child async run can be stopped without stopping the whole run. Thanks to [@yanqianglu](https://github.com/yanqianglu) for #1603.
|
|
25
|
+
|
|
26
|
+
### Changed
|
|
27
|
+
- Reduce noisy launch, status, output, settings, and worktree cleanup messages without changing behavior.
|
|
28
|
+
- Include a direct resumable child id in missing workflow receipt guidance when retained status proves it is safe.
|
|
29
|
+
- Clarify `workflowScript` contracts for retained resume keys and durable child output paths.
|
|
30
|
+
- Record merge or supersession evidence in existing worktree handoff manifests and show cleanup eligibility without removing anything.
|
|
31
|
+
- Show optional lane and work-item context in async status rows, including phase, gate, next action, output, run reference, and stale or blocked state.
|
|
32
|
+
- Show workflow child labels and phases in async status progress while preserving stable workflow keys.
|
|
33
|
+
- Remove assistant turn budgets, including hard termination, wrap-up prompt injection, and launch configuration.
|
|
34
|
+
- Split internal launch, status, settlement, evidence, and direct-MCP planning code into smaller modules without changing public behavior.
|
|
35
|
+
|
|
36
|
+
### Fixed
|
|
37
|
+
- Match detached workflow completion by exact child identity, keep host gate rows in snapshots, and report missing host verdicts as inconclusive.
|
|
38
|
+
- Replace explicit `runs.host(...)` output files atomically inside their verified directory, and retry transient Windows destination locks.
|
|
39
|
+
- Reject malformed MCP direct-tool server and metadata-cache fields when JSON is loaded.
|
|
40
|
+
- Preserve live composite child tool-call ids for `cursor-native`, so Cursor MCP results keep matching pending execs. Thanks to [@moofone](https://github.com/moofone) for #1677 and #1678.
|
|
41
|
+
- Ignore stale cached UI contexts during background status refresh and session lifecycle cleanup (#1670).
|
|
42
|
+
- Compact workflow preflight status in default TUI and status views while keeping full details available when expanded (#1668).
|
|
43
|
+
- Give `runs.lanes(...)` stage-0 retained-resume validation actionable `runs.run(...)` guidance instead of a generic error (#1657).
|
|
44
|
+
- Remove the remaining `lane:` prefix from operator-facing async status rows in the TUI (#1658).
|
|
45
|
+
- Allow scheduled project roots shared through a registered Git worktree's `.pi` symlink while rejecting unrelated or unproven Git-layout escapes. Thanks to [@sususu98](https://github.com/sususu98) for #1656.
|
|
46
|
+
- Include delegated child usage in subagent tool results and `/subagent-cost`, including completed async workflow children and parent compaction usage with persisted workflow-receipt recovery when needed (#1662, #1666). Thanks to [@Geraldo-Morais](https://github.com/Geraldo-Morais) for #1662 and [@jf88888](https://github.com/jf88888) for #1666.
|
|
47
|
+
- Avoid false preflight mismatch warnings for generated `runs.lanes(...)` stage keys (#1649).
|
|
48
|
+
- Accept long host tool-call ids in workflow child summaries, matching the existing 4,096-byte session id bound. Thanks to [@SudoKillMe](https://github.com/SudoKillMe) for #1653.
|
|
49
|
+
- Keep an async `workflowScript` continuation live while an awaited child coordinates with its supervisor, so later sequential steps still run (#1634).
|
|
50
|
+
- Stop stale extension and slash-command contexts from escaping during reload or session replacement.
|
|
51
|
+
- Include saved workflow child output paths and inline previews in completion notices (#1629).
|
|
52
|
+
- Format million-scale context limits as `1M` instead of `1000k` in live status displays.
|
|
53
|
+
- Restore active workflow children under their workflow parent in Fleet Status after reload, while keeping unmatched shell rows visible.
|
|
54
|
+
- Prefer loaded workspace context over repeated internal workflow keys in async TUI lane rows (#1619).
|
|
55
|
+
- Accept path-like Pi session ids up to 4,096 characters when snapshotting background work. Thanks to [@dvishoot](https://github.com/dvishoot) for #1616.
|
|
56
|
+
- Accept bare leaf model ids reported by provider drivers when verifying provider-qualified launch candidates. Thanks to [@lallenlowe](https://github.com/lallenlowe) for #1609.
|
|
57
|
+
- Resume compaction-induced child aborts once when retained state is safe, and report the exact recovery blocker otherwise.
|
|
58
|
+
- Report aborted or signalled no-output child runs with their terminal stop, stderr, or process signal before missing-output handoff diagnostics.
|
|
59
|
+
- Apply `globalConcurrencyLimit` to `workflowScript` children launched through `runs.run` and `runs.all`, not only legacy multi-child runners. Thanks to [@mateominato](https://github.com/mateominato) for #1600.
|
|
60
|
+
- Ignore nested `.pi` and `sync-backups` directories during agent discovery so stale backup definitions cannot become executable agents. Thanks to [@arlishansenn](https://github.com/arlishansenn) for #1596.
|
|
61
|
+
- Let projects layer agent overrides by the active parent model provider without duplicating agent definitions. Thanks to [@arichiardi](https://github.com/arichiardi) for #1597.
|
|
62
|
+
- Let operators configure the default `subagent_wait` window and report window expiry as non-error active work while preserving strict headless draining. Thanks to [@Shujakuinkuraudo](https://github.com/Shujakuinkuraudo) for #1591.
|
|
63
|
+
- Degrade run status to the stored fan-out budget snapshot when persisted state is unavailable, instead of failing the entire run list. Thanks to [@qsgy-edge](https://github.com/qsgy-edge) for #1595.
|
|
64
|
+
- Enforce MCP server `includeTools` and `excludeTools` policies for child direct-tool grants, including adapter-compatible glob matching. Thanks to [@Shujakuinkuraudo](https://github.com/Shujakuinkuraudo) for #1590.
|
|
65
|
+
- Prevent Fleet prompt audit rendering from crashing on malformed non-string task payloads. Thanks to [@bengidev](https://github.com/bengidev) for #1586.
|
|
66
|
+
- Resume a retained child session once after a provider or transport abort follows useful progress, without restarting the task or involving the parent model.
|
|
67
|
+
- Retry unused fallback models after a provider reports a plain-text `500` or `internal server error`. Thanks to [@rafafortes](https://github.com/rafafortes) for #1642.
|
|
68
|
+
- Mark missing required child handoffs with useful mutation evidence as partial needs-attention results.
|
|
69
|
+
|
|
5
70
|
## [0.58.0] - 2026-08-27
|
|
6
71
|
|
|
7
72
|
### Highlights
|
package/docs/agents.md
CHANGED
|
@@ -272,12 +272,12 @@ defaultProgress: true
|
|
|
272
272
|
async: true
|
|
273
273
|
timeoutMs: 900000
|
|
274
274
|
toolTimeoutMs: 600000
|
|
275
|
-
turnBudget: {"maxTurns":20,"graceTurns":2}
|
|
276
275
|
acceptance: {"level":"none","reason":"lightweight lookup"}
|
|
277
276
|
acceptanceRole: read-only
|
|
278
277
|
completionGuard: false
|
|
279
278
|
interactive: true
|
|
280
279
|
maxSubagentDepth: 1
|
|
280
|
+
allowNestedSubagents: true
|
|
281
281
|
---
|
|
282
282
|
|
|
283
283
|
Your system prompt goes here.
|
|
@@ -301,6 +301,7 @@ Field notes:
|
|
|
301
301
|
| `package` | Optional package identifier. A file with `name: scout` and `package: code-analysis` registers as `code-analysis.scout`; serialization keeps `name` and `package` separate. |
|
|
302
302
|
| `aliases` | Optional comma-separated or block-list names that resolve to this agent for selection and explicit `agent` and task inputs. Runtime status, persistence, and config still use the canonical `name`. Exact canonical names take precedence over aliases, and alias collisions between distinct canonical agents fail as ambiguous. |
|
|
303
303
|
| `tools` | Strict child tool allowlist. Named extension tools must also have their provider loaded. `mcp:` entries select direct MCP tools when `pi-mcp-adapter` is installed. |
|
|
304
|
+
| `allowNestedSubagents` | Set `true` to authorize the child-safe nested `subagent` runtime without making omitted `tools` an allowlist. Inherited depth and capability ceilings remain authoritative. |
|
|
304
305
|
| `extensions` | Omitted means normal extensions; empty means no extensions; list values allowlist specific extensions. |
|
|
305
306
|
| `subagentOnlyExtensions` | Extension paths loaded only in spawned child sessions for this agent. Tools registered there are unavailable to the main agent unless also installed through normal Pi extension configuration. |
|
|
306
307
|
| `model` | Default model. Bare ids prefer the current provider when possible, then unique registry matches. |
|
|
@@ -319,7 +320,6 @@ Field notes:
|
|
|
319
320
|
| `async` | Default a single-agent launch to background (`true`) or foreground (`false`) when the call omits `async`. Explicit call values and `forceTopLevelAsync` win. |
|
|
320
321
|
| `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. |
|
|
321
322
|
| `toolTimeoutMs` | Optional positive integer hard per-tool-call deadline in milliseconds. An explicit call value wins, then this agent default, global `toolTimeoutMs`, and `PI_SUBAGENT_TOOL_TIMEOUT_MS`. When omitted, known-fast built-in tools get a five-minute default; long-running tools get attention notices but no hard default. It does not extend the run-level deadline; `contact_supervisor`, `intercom`, and `subagent_wait` are exempt. |
|
|
322
|
-
| `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. |
|
|
323
323
|
| `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. |
|
|
324
324
|
| `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. |
|
|
325
325
|
| `mutationTools` | Comma-separated extension tool names whose calls count as mutation attempts for the completion guard. This declares evidence only; list and load each tool through `tools` and its extension provider as usual. |
|
|
@@ -383,6 +383,7 @@ How `tools` behaves:
|
|
|
383
383
|
- `tools` omitted: `pi-subagents` does not pass `--tools`, so the child gets Pi's normal builtin tools.
|
|
384
384
|
- `tools` present: regular tool names become an explicit allowlist.
|
|
385
385
|
- `tools:` empty: emits `--no-tools`.
|
|
386
|
+
- `allowNestedSubagents: true`: explicitly enables child-safe nested fanout without turning omitted `tools` into an allowlist. Depth and inherited capability ceilings still apply.
|
|
386
387
|
|
|
387
388
|
An allowlisted name does not load the extension that registers it. Load that provider through normal Pi extension discovery, `extensions`, `subagentOnlyExtensions`, or a path-like `tools` entry.
|
|
388
389
|
|
|
@@ -400,9 +401,10 @@ Examples:
|
|
|
400
401
|
- `tools: mcp:chrome-devtools`: only the resolved direct Chrome DevTools MCP tools.
|
|
401
402
|
- `tools: read, bash, mcp:chrome-devtools`: only `read` and `bash` as builtins, plus direct Chrome DevTools MCP tools.
|
|
402
403
|
- `tools: subagent, read`: a child-safe `subagent` tool is available inside that child so it can run explicitly assigned nested fanout.
|
|
404
|
+
- `allowNestedSubagents: true` with `tools` omitted: normal builtin tools and ambient extensions remain inherited, and the child-safe nested `subagent` runtime is added.
|
|
403
405
|
- `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.
|
|
404
406
|
|
|
405
|
-
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;
|
|
407
|
+
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. Server `includeTools` and `excludeTools` policies are enforced while resolving cached metadata for children: both accept exact names and `*`/`?` glob patterns against raw, generated-resource, and server/short/none-prefixed names, with `excludeTools` taking precedence. An `mcp:` entry named `subagent` does not authorize nested fanout; declare the builtin `subagent` tool or set `allowNestedSubagents: true`. 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.
|
|
406
408
|
|
|
407
409
|
`extensions` controls child extension loading:
|
|
408
410
|
|
package/docs/configuration.md
CHANGED
|
@@ -184,10 +184,10 @@ Controls the under-editor widget for active background runs. It defaults to `tru
|
|
|
184
184
|
## `waitTool`
|
|
185
185
|
|
|
186
186
|
```json
|
|
187
|
-
{ "waitTool": { "enabled":
|
|
187
|
+
{ "waitTool": { "enabled": true, "defaultTimeoutMs": 120000 } }
|
|
188
188
|
```
|
|
189
189
|
|
|
190
|
-
|
|
190
|
+
`defaultTimeoutMs` sets the blocking window used when a `subagent_wait` call omits `timeoutMs`; explicit call values win, followed by this setting, then the 30-minute fallback. When the window elapses, the tool returns a non-error `window_elapsed` result with the still-active work identities, and that work keeps running. Set `enabled` to `false` to keep the tool registered while making direct calls return immediately instead of blocking. The default is enabled. You can also set `"waitTool": false`; set `PI_SUBAGENT_WAIT_TOOL_ENABLED=false` (or `0`, `off`, `disabled`) to override config for one process. The effective enabled and default-timeout values are passed explicitly to child runtimes. Headless `agent_end` auto-drain retains its own strict deadline and fails if required work remains unresolved. Invalid config or environment values fail instead of being coerced.
|
|
191
191
|
|
|
192
192
|
Blocking `subagent_wait({ id: "..." })` keeps the current tool call open until that run changes. By default it returns when a run needs attention. Use `subagent_wait({ stopOnAttention: false })` only for run-to-completion flows that should wait through idle or long-thinking attention; supervisor/contact requests still stop the wait. In a long-lived interactive parent session, `subagent_wait({ id: "...", nonBlocking: true })` instead resolves the prefix once, persists the exact run identity, returns a subscription token immediately, and wakes that session on completion, failure, attention, reconciliation failure, or timeout. Armed subscriptions appear in ordinary `subagent({ action: "status" })` output and are not counted as active child work.
|
|
193
193
|
|
|
@@ -241,7 +241,7 @@ The tool timer tracks each active `toolCallId` separately and never extends the
|
|
|
241
241
|
{ "globalConcurrencyLimit": 20 }
|
|
242
242
|
```
|
|
243
243
|
|
|
244
|
-
Caps simultaneously running children inside
|
|
244
|
+
Caps simultaneously running children inside one run, including durable legacy multi-child runs and `workflowScript` launches through `runs.run`/`runs.all`. Queued workflow children retain their stable keys and begin when a running sibling releases capacity. The default is `20`.
|
|
245
245
|
|
|
246
246
|
## `maxSubagentSpawnsPerSession`
|
|
247
247
|
|
|
@@ -336,7 +336,7 @@ Routes relative `output` paths for single-agent `/run` calls under this director
|
|
|
336
336
|
{ "maxSubagentDepth": 1 }
|
|
337
337
|
```
|
|
338
338
|
|
|
339
|
-
Controls nested delegation when no inherited `PI_SUBAGENT_MAX_DEPTH` is already in effect. Per-agent `maxSubagentDepth` can tighten the limit for that agent's child runs, but cannot relax an inherited stricter limit. This applies even to children that explicitly declare `tools: subagent`; at the cap, execution fanout is blocked instead of silently hiding nested work.
|
|
339
|
+
Controls nested delegation when no inherited `PI_SUBAGENT_MAX_DEPTH` is already in effect. Per-agent `maxSubagentDepth` can tighten the limit for that agent's child runs, but cannot relax an inherited stricter limit. This applies even to children that explicitly declare `tools: subagent` or `allowNestedSubagents: true`; at the cap, execution fanout is blocked instead of silently hiding nested work.
|
|
340
340
|
|
|
341
341
|
## `PI_SUBAGENT_PI_BINARY`
|
|
342
342
|
|
package/docs/extension-api.md
CHANGED
|
@@ -165,7 +165,7 @@ Preflight covers ordinary single-agent launch resolution:
|
|
|
165
165
|
- Fresh/fork context, effective model and thinking, skill and tool resolution, direct MCP selections, runtime/configured extensions.
|
|
166
166
|
- Artifact/session paths, async lifecycle/status/result/event/process-terminal paths, package/lifecycle versions, capability-ceiling audit data, and stable digests.
|
|
167
167
|
|
|
168
|
-
`launchContractDigest` is the canonical digest of the caller task, effective system prompt
|
|
168
|
+
`launchContractDigest` is the canonical digest of the caller task, effective system prompt, 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.
|
|
169
169
|
|
|
170
170
|
Boundaries:
|
|
171
171
|
|
package/docs/models.md
CHANGED
|
@@ -8,9 +8,10 @@ Builtin agents inherit your current Pi default model. This keeps new installs fr
|
|
|
8
8
|
- `subagents.defaultProvider` — a provider preference for bare model ids, such as `llama-3`, when multiple providers expose the same id.
|
|
9
9
|
- `subagents.agentOverrides.<name>.model` — pin one role.
|
|
10
10
|
- `subagents.agentOverrides.<name>.defaultProvider` — choose or clear the provider preference for one role.
|
|
11
|
+
- `subagents.agentOverridesByProvider.<provider>.<name>` — layer role fields for the active parent provider.
|
|
11
12
|
- Per-run overrides — for one launch only.
|
|
12
13
|
|
|
13
|
-
Precedence, strongest first: per-run override → agent frontmatter `model` → `agentOverrides.<name>.model` → `subagents.defaultModel` → the parent session model. A provider preference does not replace this order; it only resolves bare model ids when the active registry has more than one match. Fully qualified `provider/model` strings still win exactly.
|
|
14
|
+
Precedence, strongest first: per-run override → agent frontmatter `model` → provider-scoped role override → `agentOverrides.<name>.model` → `subagents.defaultModel` → the parent session model. A provider preference does not replace this order; it only resolves bare model ids when the active registry has more than one match. Fully qualified `provider/model` strings still win exactly.
|
|
14
15
|
|
|
15
16
|
Use `model: "inherit"` in agent frontmatter or `agentOverrides.<name>.model` to select the current parent session model explicitly.
|
|
16
17
|
|
|
@@ -36,6 +37,28 @@ In `~/.pi/agent/settings.json` (user) or the project config settings file (`.pi/
|
|
|
36
37
|
}
|
|
37
38
|
```
|
|
38
39
|
|
|
40
|
+
To keep one role definition but configure it differently for work and personal providers, add the unambiguous provider map beside `agentOverrides`:
|
|
41
|
+
|
|
42
|
+
```json
|
|
43
|
+
{
|
|
44
|
+
"subagents": {
|
|
45
|
+
"agentOverrides": {
|
|
46
|
+
"worker": { "thinking": "medium" }
|
|
47
|
+
},
|
|
48
|
+
"agentOverridesByProvider": {
|
|
49
|
+
"github-copilot": {
|
|
50
|
+
"worker": { "model": "github-copilot/gpt-5-mini" }
|
|
51
|
+
},
|
|
52
|
+
"openrouter": {
|
|
53
|
+
"worker": { "model": "openrouter/openai/gpt-5-mini" }
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
The provider key comes from the active parent session model (or an explicit host `preferredProvider`) before fallback selection. Provider-scoped fields layer over the ordinary override in the same settings file; project settings still win over user settings. A fallback attempt does not switch the selected provider configuration.
|
|
61
|
+
|
|
39
62
|
For one run, put the override in the command:
|
|
40
63
|
|
|
41
64
|
```text
|
package/docs/observability.md
CHANGED
|
@@ -56,7 +56,7 @@ After you expand it:
|
|
|
56
56
|
|
|
57
57
|
When the focused editor is empty, press `↓` or `←` to expand the summary into `main` plus active children with agent name, state, elapsed time, and token usage. When providers report usage, `window` is the latest assistant turn's input plus cache-read tokens, while `spent` keeps the cumulative input-plus-output total. Old run artifacts without window data keep the existing token-total label. The compact line counts active current-session work and Herdr project panes. Then use `↑`/`↓` or `j`/`k` to select a child and `Enter` to inspect it. Printable navigation keys are never intercepted before activation.
|
|
58
58
|
|
|
59
|
-
FleetView replaces the legacy above-editor async widget by default. Successful background completions stay quiet so inactive Pi tabs are not marked unread, while failed or paused completions still notify the originating session. Parallel runs show every active child independently. Chains with parallel groups keep their grouped shape in progress and results, so failed or paused agents stay visible next to completed ones. When a child is explicitly allowed to fan out with `tools: subagent`, its nested runs appear under that parent child in the main status tree instead of being hidden inside the child process.
|
|
59
|
+
FleetView replaces the legacy above-editor async widget by default. Successful background completions stay quiet so inactive Pi tabs are not marked unread, while failed or paused completions still notify the originating session. Parallel runs show every active child independently. Chains with parallel groups keep their grouped shape in progress and results, so failed or paused agents stay visible next to completed ones. When a child is explicitly allowed to fan out with `tools: subagent` or `allowNestedSubagents: true`, its nested runs appear under that parent child in the main status tree instead of being hidden inside the child process.
|
|
60
60
|
|
|
61
61
|
## The fleet inspector
|
|
62
62
|
|
package/docs/tool-reference.md
CHANGED
|
@@ -36,18 +36,61 @@ Use `workflowScriptPath` instead of `workflowScript` to load the same JavaScript
|
|
|
36
36
|
` }
|
|
37
37
|
```
|
|
38
38
|
|
|
39
|
+
### Parallel sequential lanes
|
|
40
|
+
|
|
41
|
+
Use `runs.lanes(lanes)` inside a `workflowScript` when several independent lanes each have ordered stages. This helper composes the existing workflow child runner; it does not add a top-level `lanes` parameter or a second persistence/cleanup system.
|
|
42
|
+
|
|
43
|
+
```js
|
|
44
|
+
{ workflowScript: `
|
|
45
|
+
const board = await runs.lanes([
|
|
46
|
+
{ key: "api", stages: [
|
|
47
|
+
{ key: "writer", agent: "worker", task: "Implement the API change" },
|
|
48
|
+
{ key: "challenge", resume: "previous", task: "Challenge the implementation" },
|
|
49
|
+
{ key: "review", agent: "reviewer", task: "Review the API lane" }
|
|
50
|
+
] },
|
|
51
|
+
{ key: "ui", stages: [
|
|
52
|
+
{ key: "writer", agent: "worker", task: "Implement the UI change" },
|
|
53
|
+
{ key: "review", agent: "reviewer", task: "Review the UI lane" }
|
|
54
|
+
] }
|
|
55
|
+
]);
|
|
56
|
+
return board.map((lane) => ({
|
|
57
|
+
key: lane.key,
|
|
58
|
+
state: lane.state,
|
|
59
|
+
failedStage: lane.failedStage,
|
|
60
|
+
stages: lane.stages.map((stage) => ({
|
|
61
|
+
key: stage.key,
|
|
62
|
+
state: stage.state,
|
|
63
|
+
ok: stage.ok,
|
|
64
|
+
runId: stage.runId,
|
|
65
|
+
outputReference: stage.outputReference,
|
|
66
|
+
verdict: stage.verdict
|
|
67
|
+
}))
|
|
68
|
+
}));
|
|
69
|
+
` }
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
The first stage of each lane is launched by one existing `runs.all(...)` batch. Later stages run in lane order. Set `resume: "previous"` on a later stage to continue the preceding retained child; the helper requires that child’s returned `runId` and delegates to the existing resume checks. Stage keys are local to the lane, and generated child keys are `<lane>.<stage>`.
|
|
73
|
+
|
|
74
|
+
The complete plain-JSON inventory is validated before the first launch (maximum 32 lanes, 16 stages per lane, 64 total stages, and 64 KiB canonical JSON). A failed, stopped, or detached stage blocks only its lane and marks later stages `skipped`; an explicit `structuredOutput.verdict === "blocked"` has the same effect. Reviewer prose is not parsed. The bounded board returns lane/stage keys, state, `ok`, run ids, explicit output references, bounded errors, and optional verdicts, not transcripts. Use raw `runs.run(...)`/`runs.all(...)` for conditional or rolling workflows.
|
|
75
|
+
|
|
39
76
|
## Parameter reference
|
|
40
77
|
|
|
41
78
|
| Param | Type | Default | Description |
|
|
42
79
|
|-------|------|---------|-------------|
|
|
43
80
|
| `agent` | string | - | Agent target for management actions. Workflow child agents are set inside `runs.run` or `runs.all`. |
|
|
44
|
-
| `action` | string | - | Offline workflow `validate`, agent management (including `guide`, `children.list`, and `refine`/`refine.show`/`refine.rollback`), mission (`mission.create/list/show/update/resolve-decision/attach-run/close`), Herdr inspector (`inspector.open/status/close`), Herdr project pane (`project.open/status/close`), status/control, schedule, watchdog, or doctor action. |
|
|
81
|
+
| `action` | string | - | Offline workflow `validate`, agent management (including `guide`, `children.list`, and `refine`/`refine.show`/`refine.rollback`), lane evidence (`lane.status`, `lane.recordMerge`, `lane.recordSupersession`), mission (`mission.create/list/show/update/resolve-decision/attach-run/close`), Herdr inspector (`inspector.open/status/close`), Herdr project pane (`project.open/status/close`), status/control, plan-only `worktree.cleanup`, schedule, watchdog, or doctor action. |
|
|
45
82
|
| `topic` | `overview \| workflows \| agents \| missions \| observability \| tool-reference \| configuration \| models \| watchdog \| extension-api` | `overview` | Packaged guide topic for `action: "guide"`. |
|
|
46
83
|
| `config` | object/string | - | Agent config for management create/update. |
|
|
47
84
|
| `context` | `fresh \| fork` | global or per-agent default, else `fresh` | Explicit `fresh` or `fork` overrides every workflow child. When omitted, [`defaultSubagentContext`](configuration.md#defaultsubagentcontext) wins over each agent's `defaultContext`; `"fork"` creates a real branched session when the parent session file and current leaf exist, otherwise it falls back to `fresh`. Packaged `worker`, `oracle`, and `advisor` default to `fork`. |
|
|
48
85
|
| `missionId` | string | - | Attach a workflow to an existing project mission instead of creating its default enclosing mission. |
|
|
49
86
|
| `mission` | object/false | auto-create | Override the default enclosing mission with `{ title \| summary, objective?, goal?, budget?, labels? }`. Set exactly one non-empty `title` or `summary`; `objective` and `labels` are optional. `goal` may only be `true`, requires `budget.tokens`, and enables continuation notices. Pass `false` for an intentionally ephemeral workflow with no mission for it or its children and no `state` global. Explicit mission persistence failures are strict. |
|
|
50
|
-
| `handoffPath` | string | - | Aggregate handoff manifest
|
|
87
|
+
| `handoffPath` | string | - | Aggregate handoff manifest for `action: "worktree.discard"` or lane evidence actions, or optional explicit metadata for `action: "worktree.cleanup"`. |
|
|
88
|
+
| `repo` | string | runtime cwd | Repository path for `action: "worktree.cleanup"`; plan mode only. The configured worktree base filters candidates but never discovers them. |
|
|
89
|
+
| `planId` | string | - | Reserved for a future `worktree.cleanup` apply action; rejected by the current plan-only action. |
|
|
90
|
+
| `mode` | `steer \| follow_up \| auto \| plan \| apply` | - | Delivery mode for `action: "steer"`; `worktree.cleanup` currently accepts `plan` only. Apply/removal is reserved for a later change. |
|
|
91
|
+
| `laneId` | string | - | Exact `runId` stored in the handoff manifest for `lane.status`, `lane.recordMerge`, or `lane.recordSupersession`. |
|
|
92
|
+
| `merge` | object | - | Attested merge evidence for `lane.recordMerge`; requires a positive PR number, full reviewed/merge SHAs, tree-equivalence and post-merge-check statuses, attestor, and timestamp. |
|
|
93
|
+
| `supersession` | object | - | Attested replacement-lane evidence for `lane.recordSupersession`; requires a different replacement lane id, attestor, and timestamp. |
|
|
51
94
|
| `focus` | boolean | false | Focus the newly split pane for `action: "inspector.open"` or `action: "project.open"`; not a standalone action. Panes open in the background unless you set `focus: true`. Existing saved project panes can be focused through the public project-pane API when Herdr reports a tab or workspace id. |
|
|
52
95
|
| `view` | `fleet \| transcript` | - | Optional `status` view for the active fleet surface or transcript tail inspection. |
|
|
53
96
|
| `lines` | number | `80` | Maximum transcript lines for `action: "status", view: "transcript"`; capped at 500. |
|
|
@@ -57,7 +100,6 @@ Use `workflowScriptPath` instead of `workflowScript` to load the same JavaScript
|
|
|
57
100
|
| `isolation` | `none \| worktree` | - | Workflow child isolation. `none` runs in the shared cwd and does not need Git. `worktree` requires a managed Git worktree. Do not combine it with a contradictory `worktree` value. |
|
|
58
101
|
| `timeoutMs` / `maxRuntimeMs` | number | config `timeoutMs`, else 30 min foreground / single-agent async | Optional run-level max runtime in milliseconds. When omitted, the global [`timeoutMs`](configuration.md#timeoutms) config provides the default; absent that, foreground and plain single-agent async runs fall back to 30 minutes, while composite async runs (chains, parallel tasks, workflows) stay unbounded at the top level. |
|
|
59
102
|
| `toolTimeoutMs` | number | fast-tool default | Optional positive hard per-tool-call deadline in milliseconds. Precedence: call value → agent frontmatter → config → `PI_SUBAGENT_TOOL_TIMEOUT_MS`. The timer starts on `tool_execution_start`, clears on the matching `tool_execution_end`, and terminates the run with `timedOut: true` if the tool remains open. When omitted, known-fast built-in tools get a five-minute default; long-running tools get attention notices but no hard default. It never extends the run deadline; `contact_supervisor`, `intercom`, and `subagent_wait` are exempt. |
|
|
60
|
-
| `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. |
|
|
61
103
|
| `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. |
|
|
62
104
|
| `usageBudget` | object | none | Optional root-only reported-usage budget `{ tokens?: { soft?, hard }, costUsd?: { soft?, hard } }`. Soft limits are status-only. Hard limits prevent later child launches after reported usage is reconciled; already-running children are not stopped and no reservations are made. |
|
|
63
105
|
| `cwd` | string | runtime cwd | Override working directory. |
|
|
@@ -71,7 +113,7 @@ Use `workflowScriptPath` instead of `workflowScript` to load the same JavaScript
|
|
|
71
113
|
|
|
72
114
|
### Budget guidance for writers
|
|
73
115
|
|
|
74
|
-
As a conservative orchestration policy, do not set
|
|
116
|
+
As a conservative orchestration policy, do not set a hard `toolBudget` or tight `usageBudget` on implementation workers, fix workers, reviewers with edit authority, or other mutation-capable children. A default tool budget blocks read/search tools rather than mutation tools, and reported usage has no reservation model, so neither tool-call counts nor token/cost totals measure whether a delivery slice is buildable or safe to hand off. Hard caps remain appropriate for explicitly read-only scouts, reviewers, and validators.
|
|
75
117
|
|
|
76
118
|
Bound writer work with a narrow task and an outer `timeoutMs` or `maxRuntimeMs` that leaves enough margin for the slice. An elapsed timeout is not a mutation-safe boundary and may still signal a child during tool work. Before the deadline, use `steer` or an attention notice to request a checkpoint after the current tool returns, including changed files, build/test state, remaining work, and commit or PR state.
|
|
77
119
|
|
|
@@ -112,6 +154,8 @@ Use `outputMode: "file-only"` when a saved output may be large and the parent on
|
|
|
112
154
|
|
|
113
155
|
In workflowScript, give each child an explicit output path when later script steps need a durable file reference. A child with only read-only tools does not need direct filesystem access for `output`: it returns the complete artifact in its final response and the runtime persists it. Children with mutation-capable tools retain the direct-write instruction.
|
|
114
156
|
|
|
157
|
+
The `output` field is the API binding; a filename mentioned in task text (for example, `Write your findings to exactly this path: report.md`) is only instruction and does not override runtime routing. When a later workflow step or parent needs a durable file, set `output` on `runs.run`/`runs.all` and return the child’s `outputReference`, `outputPathMapping`, or `artifactPaths`; arbitrary literal strings returned by workflow JavaScript are not rewritten. Omitted child output may use a managed aggregate-derived sibling path.
|
|
158
|
+
|
|
115
159
|
Workflows get `await state.get(key)` and `await state.set(key, value)` through their default or explicit mission. Use them to share durable JSON values across later workflows attached with the same `missionId`. Each `set` takes the state-file lock and merges its key with the latest on-disk state. Missing keys return `undefined`, and the complete state file has a strict 256 KiB limit. `mission:false` workflows have no `state` global.
|
|
116
160
|
|
|
117
161
|
### Retained children
|
|
@@ -129,6 +173,8 @@ Completed workflow children from the current parent session stay addressable as
|
|
|
129
173
|
` }
|
|
130
174
|
```
|
|
131
175
|
|
|
176
|
+
Each workflow key identifies one result lane. Use a new stable workflow key for every distinct retained resume pass; same-key calls are reused only when launch parameters are identical, and incompatible parameters are rejected.
|
|
177
|
+
|
|
132
178
|
Inside `workflowScript`, `await runs.run(key, { resume, task })` waits for the revived child to finish and returns its completed output and new `runId`. Each resume can return a new retained run id, so loops must continue from the latest returned `runId`. Top-level `{ action: "resume" }` remains detached and returns a background-run receipt.
|
|
133
179
|
|
|
134
180
|
For a simple implementation challenge outside a workflow script, send the challenge through `subagent({ action: "resume", id: "<retained-writer-run>", message: "Reconsider the implementation and make any better current-scope change." })` only when `children.list` reports that retained writer as `resumable`. If no retained writer is resumable, start a same-role fallback challenge and record why it is a fallback. Use workflow `runs.run({ resume })` only when the script must await the revived writer output before the next step. Do not use `steer` as the sole challenge action for a completed retained child; `steer` with `mode: "follow_up"` only queues text for the next `resume`.
|
|
@@ -201,6 +247,42 @@ Rules:
|
|
|
201
247
|
|
|
202
248
|
`refine`, `refine.show`, and `refine.rollback` manage project-local refinement overlays for one agent. `/subagents-refine <agent>` is the slash equivalent of `refine`. See [agents.md](agents.md#refinement-overlays) for behavior and storage.
|
|
203
249
|
|
|
250
|
+
## Lane merge evidence and cleanup eligibility
|
|
251
|
+
|
|
252
|
+
Lane evidence actions update an existing parallel handoff manifest at an explicit update boundary. They do not verify GitHub state, run Git commands, or remove worktrees. Pass the manifest path and its exact `runId` as `laneId`:
|
|
253
|
+
|
|
254
|
+
```ts
|
|
255
|
+
subagent({
|
|
256
|
+
action: "lane.recordMerge",
|
|
257
|
+
laneId: "<manifest-run-id>",
|
|
258
|
+
handoffPath: "/path/to/handoff.json",
|
|
259
|
+
merge: {
|
|
260
|
+
prNumber: 123,
|
|
261
|
+
reviewedHead: "<40-character-sha>",
|
|
262
|
+
mergeCommit: "<40-character-sha>",
|
|
263
|
+
treeEquivalent: true,
|
|
264
|
+
postMergeChecks: "recorded",
|
|
265
|
+
attestedBy: "operator",
|
|
266
|
+
attestedAt: "2026-08-27T16:23:00.000Z"
|
|
267
|
+
}
|
|
268
|
+
})
|
|
269
|
+
subagent({
|
|
270
|
+
action: "lane.recordSupersession",
|
|
271
|
+
laneId: "<manifest-run-id>",
|
|
272
|
+
handoffPath: "/path/to/handoff.json",
|
|
273
|
+
supersession: {
|
|
274
|
+
supersededBy: "<replacement-lane-id>",
|
|
275
|
+
attestedBy: "operator",
|
|
276
|
+
attestedAt: "2026-08-27T16:23:00.000Z"
|
|
277
|
+
}
|
|
278
|
+
})
|
|
279
|
+
subagent({ action: "lane.status", laneId: "<manifest-run-id>", handoffPath: "/path/to/handoff.json" })
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
The manifest stores one of these fail-closed eligibility states: `active` (an owning child is still running), `terminal-eligible` (complete merge evidence and recorded post-merge checks), `terminal-blocked` with a reason, `superseded-eligible` (an explicit replacement attestation), or `unknown` (missing or malformed evidence/manifest). Each attestation stores a digest of the manifest facts it covered; later group, worktree, or patch changes downgrade that evidence to `terminal-blocked` until it is recorded again. A terminal update recomputes a previously stored `active` state from the current child statuses and evidence. Conflicting reviewed heads and mismatched lane ids are rejected as stale. Existing workflow receipts remain immutable.
|
|
283
|
+
|
|
284
|
+
`lane.status` renders the stored state and a copy-pasteable `worktree.cleanup` plan invocation. It never runs that invocation. All cleanup planning/apply and apply-time Git/ownership revalidation belong to `worktree.cleanup` from #1622; remote branch deletion and extension-side GitHub verification remain out of scope.
|
|
285
|
+
|
|
204
286
|
## Status and control actions
|
|
205
287
|
|
|
206
288
|
```ts
|
|
@@ -250,6 +332,7 @@ subagent({ action: "doctor" })
|
|
|
250
332
|
- Direct id calls execute immediately.
|
|
251
333
|
- `/subagents-stop` without an id opens a selector with confirmation when a TUI is available. Use `↑`/`↓` or `j`/`k` to move through the selector.
|
|
252
334
|
- In non-TUI contexts the slash command prints exact `subagent({ action: "stop", id })` and `/subagents-stop <id>` commands.
|
|
335
|
+
- Pass a child id to stop one child of a multi-child async run or workflow while the rest continue: `/subagents-stop <run-id> <child-id>` (equivalent to `subagent({ action: "stop", id, childId })`). Child ids come from status output, the async status snapshot, or `/subagents-inspect-rpc` replies. Only pending or running children are stoppable; the request is rejected for anything else instead of widening to a run-level stop.
|
|
253
336
|
- Inactive schedules can appear in the selector, but they are labeled as schedules and route through `schedule.pause`, not `stop`.
|
|
254
337
|
|
|
255
338
|
### steer
|
|
@@ -262,6 +345,8 @@ Only a top-level single run may interrupt after the acknowledgment deadline and
|
|
|
262
345
|
|
|
263
346
|
The persisted `steering` ledger retains 20 requests and replaces the old `steerCount`/`lastSteerAt` fields.
|
|
264
347
|
|
|
348
|
+
The `/subagents-steer <run-id> [--child <child-id>] <message>` slash command is the host bridge for non-TUI sessions and RPC hosts. `--child` accepts the stable child identity shown in status output and inspect replies (workflow key, child run id, or `step:<index>`) and resolves it to the child index before steering; unknown or ambiguous child ids fail closed. Flags are parsed only between the run id and the message tail — once the message starts, `--` tokens are message text. The bridge always disables pause-and-revive recovery (`steeringRecovery: false`), matching the extension RPC `nonRecoveringSteer` guarantee so the caller keeps authority over the exact child it addressed.
|
|
349
|
+
|
|
265
350
|
## Acceptance gates
|
|
266
351
|
|
|
267
352
|
Every run resolves an effective acceptance policy. Callers may omit `acceptance` for the inferred default, or set it on single runs, top-level parallel task items, chain steps, static parallel tasks, and dynamic fanout templates.
|
package/docs/workflows.md
CHANGED
|
@@ -68,14 +68,13 @@ subagent({
|
|
|
68
68
|
return runs.run("review", { agent: "reviewer", task: "Review:\n" + scan.output });
|
|
69
69
|
`,
|
|
70
70
|
timeoutMs: 900000,
|
|
71
|
-
turnBudget: { maxTurns: 30, graceTurns: 2 },
|
|
72
71
|
toolBudget: { soft: 40, hard: 60 },
|
|
73
72
|
usageBudget: { tokens: { soft: 100000, hard: 150000 } }
|
|
74
73
|
});
|
|
75
74
|
```
|
|
76
75
|
|
|
77
76
|
- `timeoutMs` sets the workflow deadline and bounds child deadlines to the remaining time.
|
|
78
|
-
- `
|
|
77
|
+
- `toolBudget` becomes the default for each child unless that child supplies a narrower value.
|
|
79
78
|
- `usageBudget` accounts for reported usage across completed workflow children. Once exhausted, it rejects later child launches but does not stop children that are already running.
|
|
80
79
|
- Budget and timeout stops return a structured `terminalOutcome` with `state: "partial"` and reason `budget_exhausted` or `timeout`. Workflow receipts keep settled child evidence for recovery.
|
|
81
80
|
|
|
@@ -116,6 +115,71 @@ subagent({ workflowScript: `
|
|
|
116
115
|
` });
|
|
117
116
|
```
|
|
118
117
|
|
|
118
|
+
### Parallel sequential lanes
|
|
119
|
+
|
|
120
|
+
For a bounded set of independent chains, `runs.lanes(...)` removes the mechanical loop that would otherwise connect each lane's stages. It is a helper inside `workflowScript`, not a new top-level `subagent` execution mode:
|
|
121
|
+
|
|
122
|
+
```js
|
|
123
|
+
subagent({ workflowScript: `
|
|
124
|
+
const board = await runs.lanes([
|
|
125
|
+
{
|
|
126
|
+
key: "api",
|
|
127
|
+
stages: [
|
|
128
|
+
{ key: "writer", agent: "worker", task: "Implement the API change" },
|
|
129
|
+
{ key: "challenge", resume: "previous", task: "Challenge the API implementation" },
|
|
130
|
+
{ key: "review", agent: "reviewer", task: "Review the API lane" }
|
|
131
|
+
]
|
|
132
|
+
},
|
|
133
|
+
{
|
|
134
|
+
key: "ui",
|
|
135
|
+
stages: [
|
|
136
|
+
{ key: "writer", agent: "worker", task: "Implement the UI change" },
|
|
137
|
+
{ key: "review", agent: "reviewer", task: "Review the UI lane" }
|
|
138
|
+
]
|
|
139
|
+
}
|
|
140
|
+
]);
|
|
141
|
+
return board.map((lane) => ({
|
|
142
|
+
key: lane.key,
|
|
143
|
+
state: lane.state,
|
|
144
|
+
failedStage: lane.failedStage,
|
|
145
|
+
stages: lane.stages.map((stage) => ({
|
|
146
|
+
key: stage.key,
|
|
147
|
+
state: stage.state,
|
|
148
|
+
ok: stage.ok,
|
|
149
|
+
runId: stage.runId,
|
|
150
|
+
outputReference: stage.outputReference,
|
|
151
|
+
verdict: stage.verdict
|
|
152
|
+
}))
|
|
153
|
+
}));
|
|
154
|
+
` });
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
The first stage from every lane is launched in one existing `runs.all(...)` batch. Later stages in each lane start only after the preceding stage settles. A later stage with `resume: "previous"` requires the preceding child to return a retained `runId`; the helper then uses the existing retained-resume launch checks and does not accept an arbitrary run id. Generated child keys use `<lane>.<stage>`, while the returned board uses the local lane and stage keys.
|
|
158
|
+
|
|
159
|
+
The helper validates the complete plain-JSON lane inventory before launching anything. It bounds the inventory to 32 lanes, 16 stages per lane, 64 total stages, and 64 KiB of canonical JSON; task and path fields retain the existing 1 MiB and 32 KiB limits. Stage keys must be unique within a lane and generated keys must be unique and valid workflow keys. A child failure, stopped/detached result, or explicit `structuredOutput.verdict === "blocked"` blocks only that lane; later stages are marked `skipped` and sibling lanes continue. Reviewer prose is never parsed.
|
|
160
|
+
|
|
161
|
+
The board is bounded and contains only lane/stage keys, state, success, retained run ids, explicit output references, bounded errors, and an optional structured verdict. It does not return child transcripts or create a lane registry or cleanup authority. Use raw `runs.run(...)`/`runs.all(...)` when a workflow needs conditional or rolling orchestration beyond this helper.
|
|
162
|
+
|
|
163
|
+
### Host command steps
|
|
164
|
+
|
|
165
|
+
Use `runs.host(...)` when the operator wants one non-interactive command to be part of the workflow evidence instead of a child-agent run:
|
|
166
|
+
|
|
167
|
+
```js
|
|
168
|
+
subagent({ workflowScript: `
|
|
169
|
+
const tests = await runs.host("unit-tests", {
|
|
170
|
+
kind: "command",
|
|
171
|
+
command: "npm run test:unit",
|
|
172
|
+
timeoutMs: 120000,
|
|
173
|
+
output: "reports/unit-tests.log",
|
|
174
|
+
role: "ci",
|
|
175
|
+
provider: "local"
|
|
176
|
+
});
|
|
177
|
+
return { state: tests.state, exitCode: tests.exitCode, outputPath: tests.outputPath };
|
|
178
|
+
` });
|
|
179
|
+
```
|
|
180
|
+
|
|
181
|
+
The first version supports only `kind: "command"`. `command` and `timeoutMs` are required; `output` must be a relative path without traversal. `role` may be `ci` or `gate`, and `provider` is display metadata only. The command has no stdin, receives the workflow cwd, and must be awaited or returned. Stdout, stderr, and the saved log are bounded. A nonzero exit, timeout, abort, or output-write failure fails the workflow. Async status and terminal receipts store the bounded host-step state; renderers do not run commands or read command output.
|
|
182
|
+
|
|
119
183
|
### Steering a workflow child
|
|
120
184
|
|
|
121
185
|
Use `await runs.steer(key, message, options?)` after `runs.run` or `runs.all` has launched that stable key. Scripts do not target raw run ids. The optional fields are `mode: "steer" | "follow_up" | "auto"`, a non-negative child `index`, and a positive `ackTimeoutMs`.
|
|
@@ -306,6 +370,33 @@ A top-level `{ workflowScript, worktree: true }` makes isolation the default for
|
|
|
306
370
|
|
|
307
371
|
Configure the worktree base directory and setup hook in [configuration.md](configuration.md).
|
|
308
372
|
|
|
373
|
+
### Lane metadata lifecycle
|
|
374
|
+
|
|
375
|
+
Workflow children may declare a bounded `lane` object (`version`, `key`, optional
|
|
376
|
+
`mode`, opaque `sourceRef`, advisory `claims`, and advisory `outputPaths`). The
|
|
377
|
+
lane key must match the `runs.run`/`runs.all` workflow key. These fields are
|
|
378
|
+
display and triage hints only: they do not grant tools, authorization, or
|
|
379
|
+
cleanup permission, and `sourceRef` is never resolved over the network while
|
|
380
|
+
rendering status. Worktree paths and branches copied into status are also
|
|
381
|
+
display-only; the handoff manifest remains the deletion authority.
|
|
382
|
+
|
|
383
|
+
| Durable file | Owner | Pending / running / finalized / cleanup states | Release predicate | Rollback predicate | Stale-head behavior | Fail-closed cases |
|
|
384
|
+
| --- | --- | --- | --- | --- | --- | --- |
|
|
385
|
+
| `status.json` | Async runner and workflow status projector | Child step starts `pending`, becomes `running`, then terminal `complete`/`failed`/`paused`/`stopped`; worktree path and branch are copied at launch | Status is terminal and the existing active-run/process proof can release the run marker; lane metadata alone never releases a worktree | Setup or persistence failure keeps the lane unknown; only the existing verified setup rollback may remove a newly created worktree | Recorded status is retained; a base/head mismatch is not repaired or inferred from render-time Git calls | Missing, malformed, or key-mismatched lane data; only one of `worktreePath`/`branch`; unverified process state |
|
|
386
|
+
| `handoffs/<run-id>.json` | Existing parallel handoff writer and cleanup engine | Group is `partial` with preserved cleanup tasks while pending/running; finalized groups contain child identity, patch, and cleanup evidence; cleanup is `partial` or `complete` | Only the existing cleanup engine's fresh Git checks and recorded task evidence can release a worktree/branch; #1621 adds no deletion path | Missing diff, failed capture, or cleanup error preserves the task and records the reason | `baseCommit` is retained as evidence; stale or changed heads remain unknown/preserved until an explicit later reconciliation | Missing/invalid manifest, mismatched run/key/task identity, duplicate identity, dirty or uncaptured work |
|
|
387
|
+
| `workflow-receipt.json` | Workflow terminal settlement | No receipt while `pending`/`running`; terminal receipt is finalized with one optional lane block per keyed child | Receipt publication is complete only after every included child entry is serialized; it does not authorize cleanup | Receipt write failure leaves status/handoff evidence authoritative and the workflow reports the missing receipt | Existing receipt is not backfilled or rewritten from a newer head | Invalid version/state, mismatched entry key or lane key, stale continuation lineage |
|
|
388
|
+
| `.active-runs` marker | Existing active-run index | `pending`/`running` while the runner is live; terminal marker remains until observed process proof | Marker removal requires the existing exact-run process-terminal proof | Unknown proof keeps the marker and lane retained for inspection | Marker state is not inferred from Git head or timestamps alone | Missing/unknown process proof, active marker, or foreign run identity |
|
|
389
|
+
|
|
390
|
+
Older runs without lane metadata remain readable and retain their existing
|
|
391
|
+
handoff/cleanup behavior. Missing lane, receipt, or handoff metadata is
|
|
392
|
+
unknown—not eligible for destructive cleanup.
|
|
393
|
+
|
|
394
|
+
For managed worktree launches, the runner writes the pending handoff and the
|
|
395
|
+
display-only status path/branch from the deterministic setup plan before the
|
|
396
|
+
first `git worktree add`. If setup then fails or is interrupted, that pending
|
|
397
|
+
ownership record remains preserved evidence; cleanup still rechecks the actual
|
|
398
|
+
worktree state before any removal.
|
|
399
|
+
|
|
309
400
|
## Supervisor coordination (child asks parent)
|
|
310
401
|
|
|
311
402
|
Child agents can talk back to the parent Pi session without installing `pi-intercom`. `pi-subagents` provides the child-facing `contact_supervisor` tool and the parent-facing `subagent_supervisor({ action: "reply" })` path natively. Generic `intercom` remains available only when an explicitly loaded external provider supplies it.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-subagents",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.59.0",
|
|
4
4
|
"description": "Pi extension for single-agent delegation and scripted multi-agent workflows",
|
|
5
5
|
"author": "Nico Bailon",
|
|
6
6
|
"license": "MIT",
|
|
@@ -100,7 +100,9 @@
|
|
|
100
100
|
"@earendil-works/pi-agent-core": "0.81.0",
|
|
101
101
|
"@earendil-works/pi-ai": "0.81.0",
|
|
102
102
|
"@earendil-works/pi-tui": "0.81.0",
|
|
103
|
+
"@oxlint/plugins": "1.80.0",
|
|
103
104
|
"@types/node": "24.13.3",
|
|
105
|
+
"oxlint": "1.80.0",
|
|
104
106
|
"typescript": "5.9.3",
|
|
105
107
|
"@earendil-works/pi-coding-agent": "file:./test/fixtures/pi-coding-agent-shim"
|
|
106
108
|
}
|
package/prompts/review-loop.md
CHANGED
|
@@ -10,7 +10,7 @@ Default to a maximum of 3 review rounds unless I specify a different cap. Count
|
|
|
10
10
|
|
|
11
11
|
If the invocation includes an implementation request, first launch one async `worker` to implement the approved scope. If the current diff is already the target, start with review. The sequence can be launched up front with `workflowScript` when it is already clear, or continued as follow-up single-agent runs after each async completion. For an initial workflowScript, pass `async: true` so the main chat is unblocked; do not set `clarify: true` unless I explicitly want the foreground clarify UI. Use only one writer against the active worktree at a time unless I explicitly ask for isolated worktrees.
|
|
12
12
|
|
|
13
|
-
As a conservative orchestration policy, do not set
|
|
13
|
+
As a conservative orchestration policy, do not set a hard `toolBudget` or tight `usageBudget` on implementation or fix workers. A default tool budget blocks read/search tools rather than mutation tools, and reported usage has no reservation model, so count or usage limits still do not measure delivery safety. Give each writer a narrow delivery slice and an outer elapsed deadline with enough margin. Before that deadline, request a checkpoint after the current tool returns with changed files, build/test state, remaining work, and commit or PR state. An elapsed timeout is not a mutation-safe boundary and must not be the checkpoint trigger.
|
|
14
14
|
|
|
15
15
|
For each review round, launch fresh-context `reviewer` agents in parallel. Reviewers must inspect the repository, relevant instructions, and current diff directly from files and commands. They must not rely on the main conversation history and must not edit files.
|
|
16
16
|
|
|
@@ -28,7 +28,7 @@ Do not blindly apply every reviewer suggestion. If reviewers surface an unapprov
|
|
|
28
28
|
|
|
29
29
|
When an async implementation worker completes, treat its handoff as the transition into review, not as final completion, unless I explicitly asked for worker-only work, review-only output, or to stop after implementation.
|
|
30
30
|
|
|
31
|
-
When there are fixes worth doing now and the workflow is implementation-authorized, launch one async forked `worker` without hard
|
|
31
|
+
When there are fixes worth doing now and the workflow is implementation-authorized, launch one async forked `worker` without hard tool-call caps to apply only those synthesized fixes. Ask it to preserve the approved scope, run focused validation, and report changed files, commands run with exit codes, validation evidence, surprises, and anything left undone.
|
|
32
32
|
|
|
33
33
|
After a fix worker returns, run another review round only when it made material changes or addressed non-trivial findings. Do not keep looping for optional polish, speculative improvements, or findings already deferred by the parent.
|
|
34
34
|
|
|
@@ -109,7 +109,7 @@ If an advisor is not resumable, run the same profile in fresh context with its o
|
|
|
109
109
|
pass-1 report and the challenge packet. Label that response as a fresh-context
|
|
110
110
|
fallback, not a true cross-exam.
|
|
111
111
|
|
|
112
|
-
Do not set `clarify`, `worktree`, `gate`,
|
|
112
|
+
Do not set `clarify`, `worktree`, `gate`, tool budgets, or tight usage
|
|
113
113
|
budgets on advisors. Bound work through the roster, pass cap, and report length.
|
|
114
114
|
|
|
115
115
|
## Advisor contracts and pass receipts
|
|
@@ -48,4 +48,4 @@ External CLI agents such as `codex-exec`, `codex-exec-writer`, `claude-code`, an
|
|
|
48
48
|
- Preserve capability ceilings, including child tool restrictions and session-scoped allowed-agent restrictions.
|
|
49
49
|
- Escalate unresolved product, architecture, authority, release, merge, or safety decisions upward instead of letting a child decide silently.
|
|
50
50
|
- Treat receipts, CI, review bots, and external-run records as evidence, not authority to merge, close, comment, publish, or release.
|
|
51
|
-
- As a conservative orchestration policy, do not pass
|
|
51
|
+
- As a conservative orchestration policy, do not pass a hard `toolBudget` or tight `usageBudget` to mutation-capable workers. The default tool budget blocks read/search tools rather than mutation tools, and reported usage has no reservation model. If a worker is interrupted after a tool call starts, checkpoint after the current tool returns with changed files, build/test state, and commit or PR state.
|