pi-subagents 0.61.0 → 0.63.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 +52 -1
- package/docs/agents.md +11 -6
- package/docs/configuration.md +31 -5
- package/docs/extension-api.md +2 -2
- package/docs/models.md +5 -5
- package/docs/observability.md +5 -2
- package/docs/tool-reference.md +3 -3
- package/install.mjs +0 -1
- package/package.json +1 -1
- package/skills/pi-subagents/references/execution-controls.md +3 -4
- package/skills/pi-subagents/references/management-authoring-rpc.md +1 -1
- package/skills/pi-subagents/references/prompting-and-roles.md +3 -3
- package/src/agents/agent-management.ts +41 -4
- package/src/agents/agent-serializer.ts +3 -0
- package/src/agents/agents.ts +120 -124
- package/src/agents/runtime-agent-registry.ts +5 -1
- package/src/api/preflight.ts +4 -0
- package/src/api/shared-types.ts +3 -0
- package/src/extension/config.ts +20 -0
- package/src/extension/public-execution.ts +1 -0
- package/src/extension/schemas.ts +6 -2
- package/src/extension/tool-description.ts +1 -1
- package/src/inspectors/herdr/inspector-runner.ts +19 -13
- package/src/runs/background/active-async-capacity.ts +26 -8
- package/src/runs/background/async-execution.ts +100 -23
- package/src/runs/background/async-resume.ts +6 -2
- package/src/runs/background/async-status.ts +18 -2
- package/src/runs/background/notify.ts +13 -1
- package/src/runs/background/process-terminal.ts +16 -0
- package/src/runs/background/run-status.ts +22 -2
- package/src/runs/background/scheduled-runs.ts +63 -6
- package/src/runs/background/steering.ts +4 -1
- package/src/runs/background/subagent-runner.ts +44 -9
- package/src/runs/background/wait-completions.ts +13 -0
- package/src/runs/background/wait-tool.ts +1 -7
- package/src/runs/foreground/execution.ts +14 -7
- package/src/runs/foreground/subagent-executor.ts +38 -5
- package/src/runs/shared/acceptance.ts +85 -18
- package/src/runs/shared/capability-ceiling.ts +1 -0
- package/src/runs/shared/dynamic-fanout.ts +1 -1
- package/src/runs/shared/lane-metadata.ts +24 -3
- package/src/runs/shared/parallel-handoff.ts +4 -0
- package/src/runs/shared/parallel-utils.ts +2 -6
- package/src/runs/shared/permissions.ts +1 -1
- package/src/runs/shared/pi-args.ts +32 -14
- package/src/runs/shared/pi-spawn.ts +69 -35
- package/src/runs/shared/structured-output.ts +33 -6
- package/src/runs/shared/subagent-prompt-runtime.ts +20 -3
- package/src/runs/shared/task-intent.ts +21 -7
- package/src/runs/shared/tool-timeout.ts +1 -1
- package/src/runs/shared/worktree.ts +467 -63
- package/src/shared/atomic-json.ts +3 -1
- package/src/shared/fork-context.ts +0 -12
- package/src/shared/fork-session-cwd.ts +27 -0
- package/src/shared/launch-contract.ts +3 -0
- package/src/shared/types.ts +32 -1
- package/src/shared/utils.ts +18 -7
- package/src/slash/slash-commands.ts +1 -1
- package/src/slash/subagents-admin.ts +26 -12
- package/src/tui/fleet-status.ts +61 -2
- package/src/tui/fleet.ts +12 -7
- package/src/tui/render.ts +222 -14
- package/src/workflows/workflow-checklist.ts +441 -0
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,57 @@
|
|
|
3
3
|
|
|
4
4
|
## [Unreleased]
|
|
5
5
|
|
|
6
|
+
## [0.63.0] - 2026-09-01
|
|
7
|
+
|
|
8
|
+
### Highlights
|
|
9
|
+
- Workflow progress is easier to scan in status, Fleet, and live widgets.
|
|
10
|
+
- Additional agent folders can now be configured without copying definitions into one directory.
|
|
11
|
+
- Worktrunk users get managed worktrees automatically, with native Git available as the fallback.
|
|
12
|
+
- Fleet can jump straight into the selected child run's Herdr inspector.
|
|
13
|
+
- Async runs clean up and report edge cases more reliably.
|
|
14
|
+
|
|
15
|
+
### Added
|
|
16
|
+
- Show workflow progress as stacked checklist summaries in status, Fleet, and live widget views (#1806).
|
|
17
|
+
- Add configurable extra agent scan directories with one-segment wildcard expansion. Thanks to [@mystery4f](https://github.com/mystery4f) for #1801.
|
|
18
|
+
- Make Worktrunk a first-class managed worktree provider, selected automatically when available with native Git as the fallback (#1800).
|
|
19
|
+
- Let Fleet open the selected async child in its child-specific Herdr inspector. Thanks to [@stekman08](https://github.com/stekman08) for #1790.
|
|
20
|
+
|
|
21
|
+
### Changed
|
|
22
|
+
- Show workflow checklist phases first in collapsed views, while keeping child details available when expanded (#1810).
|
|
23
|
+
|
|
24
|
+
### Fixed
|
|
25
|
+
- Keep isolated test runs from writing agent definitions into an inherited `PI_CODING_AGENT_DIR`. Thanks to [@mapleluvr](https://github.com/mapleluvr) for #1809.
|
|
26
|
+
- Prevent nested tool-availability diagnostics from failing an otherwise valid parent result. Thanks to [@robertvangor](https://github.com/robertvangor) for #1802.
|
|
27
|
+
- Free async capacity correctly after workflows finish, even when saved step status is stale. Thanks to [@boggylp](https://github.com/boggylp) for #1804.
|
|
28
|
+
- Make `subagents.agentOverrides.<name>` replace matching custom-agent frontmatter fields, consistently with builtin agents. Thanks to [@expoli](https://github.com/expoli) for #1796.
|
|
29
|
+
- Strip the trailing Pi turn-timing footer from child output. Thanks to [@fkhawajagh](https://github.com/fkhawajagh) for #1792.
|
|
30
|
+
- Keep inferred acceptance reports out of reviewer and read-only child prompts. Thanks to [@expoli](https://github.com/expoli) for #1797.
|
|
31
|
+
- Preserve coordinated read-only intent when direct async children resume, and show captured structured output in completion and status evidence. Thanks to [@fkhawajagh](https://github.com/fkhawajagh) for #1788.
|
|
32
|
+
- Keep macOS subagent tasks out of argv by delivering them through temporary files. Thanks to [@josephkallas](https://github.com/josephkallas) for #1793.
|
|
33
|
+
|
|
34
|
+
## [0.62.0] - 2026-08-31
|
|
35
|
+
|
|
36
|
+
### Highlights
|
|
37
|
+
- Child agents can report completion evidence more cleanly and stay away from tools they should not use.
|
|
38
|
+
- Session-only schedules keep personal scheduled work tied to the session that created it.
|
|
39
|
+
- Async forked runs now start and resume in the working directory you requested.
|
|
40
|
+
- Windows child launches are more reliable, with clearer errors when Pi cannot find a valid CLI.
|
|
41
|
+
- External CLI and read-only recovery paths are sturdier when workers disappear or prompts include unusual line separators.
|
|
42
|
+
|
|
43
|
+
### Added
|
|
44
|
+
- Let native children with `outputSchema` include required acceptance evidence in the same `structured_output` call with `acceptance.report: "on"`; `acceptance.report: "off"` keeps fenced acceptance reports. Thanks [@mapleluvr](https://github.com/mapleluvr) for #1770.
|
|
45
|
+
- Add per-agent `excludeTools` deny-lists that compose with Pi's ambient or explicit child tool selection. Thanks [@expoli](https://github.com/expoli) for #1776.
|
|
46
|
+
- Add session-only durable schedules that only run in the session that created them. Thanks [@yangfeng20](https://github.com/yangfeng20) for #1777.
|
|
47
|
+
|
|
48
|
+
### Fixed
|
|
49
|
+
- Keep async forked runs in the requested child `cwd` when they start or resume. Thanks [@stekman08](https://github.com/stekman08) for #1785.
|
|
50
|
+
- Accept JSON-encoded acceptance objects from model tool calls, while still failing clearly for malformed strings. Thanks [@mapleluvr](https://github.com/mapleluvr) for #1781.
|
|
51
|
+
- Keep steer and follow-up receipt statuses separate from their redacted message previews (#1773).
|
|
52
|
+
- Create async lifecycle sidecars before external CLI workers begin worktree changes, so disappeared runners are reported as failed runs. Thanks [@fkhawajagh](https://github.com/fkhawajagh) for #1764.
|
|
53
|
+
- Preserve explicit read-only intent when escaped line separators surround no-edit wording. Thanks [@fkhawajagh](https://github.com/fkhawajagh) for #1765.
|
|
54
|
+
- Launch child Pi processes through the resolved CLI JavaScript on Windows, and run JavaScript `PI_SUBAGENT_PI_BINARY` overrides with Node. Thanks [@caohuipeng](https://github.com/caohuipeng) for #1768.
|
|
55
|
+
- Resolve the installed Pi CLI on Windows wrapper hosts from the forwarded package root, and report a clear error when no verified CLI can be found. Thanks [@lux032](https://github.com/lux032) for #1780.
|
|
56
|
+
|
|
6
57
|
## [0.61.0] - 2026-08-31
|
|
7
58
|
|
|
8
59
|
### Highlights
|
|
@@ -15,7 +66,7 @@
|
|
|
15
66
|
### Added
|
|
16
67
|
- Add extension-owned named workflow resources so permission and policy extensions can distinguish trusted workflow resources from raw scripts. Thanks [@mathiasloh](https://github.com/mathiasloh) for #1751.
|
|
17
68
|
- Add workflow-only `globalConcurrencyLimit` and `maxSubagentSpawnsPerRun` overrides for top-level `workflowScript` calls. Thanks [@RapierCraft](https://github.com/RapierCraft) for #1760.
|
|
18
|
-
-
|
|
69
|
+
- Remove the deprecated compatibility wait alias; use `bg_wait` instead (#1729).
|
|
19
70
|
|
|
20
71
|
### Changed
|
|
21
72
|
- Show effective model mappings for discovered and runtime-registered subagents through management and `/subagents-models`. Thanks [@RapierCraft](https://github.com/RapierCraft) for #1732.
|
package/docs/agents.md
CHANGED
|
@@ -27,6 +27,7 @@ Discovery notes:
|
|
|
27
27
|
|
|
28
28
|
- Project discovery also reads legacy `.agents/**/*.md` files. If both `.agents/` and the project config agents directory define the same parsed runtime agent name, the project config directory wins.
|
|
29
29
|
- Nested subdirectories are discovered recursively. `.chain.md` files do not define agents.
|
|
30
|
+
- User and project settings can add extra recursive scan roots with `subagents.agentScanDirs`; fixed user/project agent directories keep higher priority than same-name agents from scan roots.
|
|
30
31
|
- Installed Pi packages can expose agent directories from either `{"pi-subagents":{"agents":["./agents"]}}` or `{"pi":{"subagents":{"agents":["./agents"]}}}` in their package manifest. Package agents load above builtins and below user/project agents.
|
|
31
32
|
- Use `agentScope: "user" | "project" | "both"` to control discovery. `both` is the default, and project definitions win runtime-name collisions.
|
|
32
33
|
|
|
@@ -189,9 +190,9 @@ The `researcher` builtin uses `web_search`, `fetch_content`, and `get_search_con
|
|
|
189
190
|
pi install npm:pi-web-access
|
|
190
191
|
```
|
|
191
192
|
|
|
192
|
-
## Overriding builtins
|
|
193
|
+
## Overriding builtins and custom agents
|
|
193
194
|
|
|
194
|
-
You can override selected
|
|
195
|
+
You can override selected agent fields without copying the whole agent. Overrides live in settings:
|
|
195
196
|
|
|
196
197
|
- User: `~/.pi/agent/settings.json`
|
|
197
198
|
- Project: project config settings file (`.pi/settings.json` in standard Pi)
|
|
@@ -213,9 +214,9 @@ Supported override fields: `description`, `output`, `outputMode`, `defaultReads`
|
|
|
213
214
|
|
|
214
215
|
- `description` replaces the discovered description for builtin and custom agents, which lets list output show deployment-specific routing or model metadata.
|
|
215
216
|
- Use `output: false`, `defaultReads: false`, `defaultContext: false`, or `acceptanceRole: false` to clear an inherited value.
|
|
216
|
-
- Use `tools: "inherit"`
|
|
217
|
+
- Use `tools: "inherit"` when that one role should omit its bundled or frontmatter tool allowlist and receive Pi's normal builtins and ambient extensions.
|
|
217
218
|
- Project overrides beat user overrides.
|
|
218
|
-
- Matching user and project agents also receive override fields
|
|
219
|
+
- Matching package, user, and project agents also receive override fields, which replace the same fields declared in their frontmatter. This lets a shared agent keep its persona while local settings choose the effective model, context, tools, or other supported options.
|
|
219
220
|
|
|
220
221
|
Disable and restore:
|
|
221
222
|
|
|
@@ -255,6 +256,7 @@ package: code-analysis
|
|
|
255
256
|
description: Fast codebase recon
|
|
256
257
|
aliases: explorer, code-scout
|
|
257
258
|
tools: read, grep, find, ls, bash, mcp:chrome-devtools
|
|
259
|
+
excludeTools: bash
|
|
258
260
|
extensions:
|
|
259
261
|
subagentOnlyExtensions: ./tools/child-only-search.ts
|
|
260
262
|
model: claude-haiku-4-5
|
|
@@ -283,7 +285,7 @@ allowNestedSubagents: true
|
|
|
283
285
|
Your system prompt goes here.
|
|
284
286
|
```
|
|
285
287
|
|
|
286
|
-
Simple-scalar list fields accept either a comma-separated form or a newline block list with one `- item` per line. This applies to `tools`, `defaultReads`, `skill`/`skills`, `skillPath`, `fallbackModels`, `extensions`, and `subagentOnlyExtensions`:
|
|
288
|
+
Simple-scalar list fields accept either a comma-separated form or a newline block list with one `- item` per line. This applies to `tools`, `excludeTools`, `defaultReads`, `skill`/`skills`, `skillPath`, `fallbackModels`, `extensions`, and `subagentOnlyExtensions`:
|
|
287
289
|
|
|
288
290
|
```yaml
|
|
289
291
|
tools:
|
|
@@ -301,6 +303,7 @@ Field notes:
|
|
|
301
303
|
| `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
304
|
| `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
305
|
| `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. |
|
|
306
|
+
| `excludeTools` | Optional child tool deny-list applied after normal tool resolution. With an explicit `tools` allowlist, matching names are removed; when `tools` is omitted, the names are forwarded to Pi as `--exclude-tools` so the ambient tool set is inherited minus those names. Unknown names are ignored by Pi without making the agent definition invalid. |
|
|
304
307
|
| `allowNestedSubagents` | Set `true` to authorize the child-safe nested `subagent` runtime without making omitted `tools` an allowlist. Inherited depth and capability ceilings remain authoritative. |
|
|
305
308
|
| `extensions` | Omitted means normal extensions; empty means no extensions; list values allowlist specific extensions. |
|
|
306
309
|
| `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. |
|
|
@@ -319,7 +322,7 @@ Field notes:
|
|
|
319
322
|
| `defaultProgress` | Maintain `progress.md`. |
|
|
320
323
|
| `async` | Default a single-agent launch to background (`true`) or foreground (`false`) when the call omits `async`. Explicit call values and `forceTopLevelAsync` win. |
|
|
321
324
|
| `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. |
|
|
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`,
|
|
325
|
+
| `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 `bg_wait` are exempt. |
|
|
323
326
|
| `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
327
|
| `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
328
|
| `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. |
|
|
@@ -385,6 +388,8 @@ How `tools` behaves:
|
|
|
385
388
|
- `tools:` empty: emits `--no-tools`.
|
|
386
389
|
- `allowNestedSubagents: true`: explicitly enables child-safe nested fanout without turning omitted `tools` into an allowlist. Depth and inherited capability ceilings still apply.
|
|
387
390
|
|
|
391
|
+
`excludeTools` is applied after this resolution. It can narrow an explicit `tools` allowlist or, when `tools` is omitted, compose with Pi's ambient builtin tools through `--exclude-tools`. Runtime-injected tools are excluded only when their exact names are listed. An empty `excludeTools` list has no effect.
|
|
392
|
+
|
|
388
393
|
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.
|
|
389
394
|
|
|
390
395
|
More rules:
|
package/docs/configuration.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
`pi-subagents` reads optional JSON config from `~/.pi/agent/extensions/subagent/config.json`. This page lists every key, plus the environment variables and the settings-file keys that affect config resolution.
|
|
4
4
|
|
|
5
|
-
Settings-level keys (`subagents.defaultModel`, `defaultProvider`, `defaultThinking`, `defaultExtensions`, `agentOverrides`, `modelScope`, `disableThinking`, `disableBuiltins`, watchdog settings) live in Pi settings files, not this config file. `modelScope.agents.<name>` adds per-agent restrictions, and `allow: ["inherit"]` permits the current parent model. See [models.md](models.md), [agents.md](agents.md), and [watchdog.md](watchdog.md).
|
|
5
|
+
Settings-level keys (`subagents.defaultModel`, `defaultProvider`, `defaultThinking`, `defaultExtensions`, `agentOverrides`, `agentScanDirs`, `modelScope`, `disableThinking`, `disableBuiltins`, watchdog settings) live in Pi settings files, not this config file. `modelScope.agents.<name>` adds per-agent restrictions, and `allow: ["inherit"]` permits the current parent model. See [models.md](models.md), [agents.md](agents.md), and [watchdog.md](watchdog.md).
|
|
6
6
|
|
|
7
7
|
## Project root resolution (settings)
|
|
8
8
|
|
|
@@ -18,6 +18,20 @@ By default, project settings resolve from the nearest parent directory that cont
|
|
|
18
18
|
|
|
19
19
|
`"git-root"` keeps package discovery, project agents, chains, and `agentOverrides` anchored to the git worktree root when that root also has Pi project config. A nested project can still opt back into nearest-root behavior by setting `"projectRootResolution": "nearest"` in its own `.pi/settings.json`.
|
|
20
20
|
|
|
21
|
+
## Extra agent scan directories (settings)
|
|
22
|
+
|
|
23
|
+
Add recursive user or project agent roots with `subagents.agentScanDirs` in Pi settings:
|
|
24
|
+
|
|
25
|
+
```json
|
|
26
|
+
{
|
|
27
|
+
"subagents": {
|
|
28
|
+
"agentScanDirs": ["~/.pi/flows/*/agents"]
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Entries support `~` expansion. A single `*` path segment expands one directory level, so package-like folders can each expose an `agents/` directory. Missing directories are ignored. Fixed user/project agent directories still win over same-name agents from scan roots.
|
|
34
|
+
|
|
21
35
|
## `modelExclusions`
|
|
22
36
|
|
|
23
37
|
```json
|
|
@@ -187,9 +201,9 @@ Controls the under-editor widget for active background runs. It defaults to `tru
|
|
|
187
201
|
{ "waitTool": { "enabled": true, "defaultTimeoutMs": 120000 } }
|
|
188
202
|
```
|
|
189
203
|
|
|
190
|
-
`defaultTimeoutMs` sets the blocking window used when a `bg_wait` call omits `timeoutMs`; explicit call values win, followed by this setting, then the 30-minute fallback.
|
|
204
|
+
`defaultTimeoutMs` sets the blocking window used when a `bg_wait` call omits `timeoutMs`; explicit call values win, followed by this setting, then the 30-minute fallback. `bg_wait` is the only registered wait tool. 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 make 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
205
|
|
|
192
|
-
Blocking `bg_wait({ id: "..." })` keeps the current tool call open until that run changes. By default it returns when a run needs attention. Use `bg_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, `bg_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. Use it for provider, detached, or other background work without a native completion notification; ordinary async subagent runs notify the parent natively and do not need a wait subscription. Armed subscriptions appear in ordinary `subagent({ action: "status" })` output and are not counted as active child work.
|
|
206
|
+
Blocking `bg_wait({ id: "..." })` keeps the current tool call open until that run changes. By default it returns when a run needs attention. Use `bg_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, `bg_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. Use it for provider, detached, or other background work without a native completion notification; ordinary async subagent runs notify the parent natively and do not need a wait subscription. Armed subscriptions appear in ordinary `subagent({ action: "status" })` output and are not counted as active child work.
|
|
193
207
|
|
|
194
208
|
This is different from `waitTool.enabled=false`, which returns immediately without registering any future wake. Provider items remain available only to blocking fleet-wide waits; non-blocking subscriptions require one async or remembered detached foreground run id.
|
|
195
209
|
|
|
@@ -233,7 +247,7 @@ Optional hard per-tool-call deadline in milliseconds. When configured, a child t
|
|
|
233
247
|
|
|
234
248
|
Without a configured value, Pi still applies a five-minute hard timeout to known-fast built-in tools: `read`, `grep`, `find`, `ls`, `edit`, `write`, and `structured_output`. Long-running tools such as `bash`, custom tools, and MCP tools do not get a hard default. They get the normal open-tool attention notice after `activeNoticeAfterMs` and remain bounded by the run-level deadline.
|
|
235
249
|
|
|
236
|
-
The tool timer tracks each active `toolCallId` separately and never extends the run-level deadline: when the remaining run budget is shorter, the ordinary run-level timeout wins. `contact_supervisor`, `intercom`,
|
|
250
|
+
The tool timer tracks each active `toolCallId` separately and never extends the run-level deadline: when the remaining run budget is shorter, the ordinary run-level timeout wins. `contact_supervisor`, `intercom`, and `bg_wait` are exempt because their legitimate purpose can be to wait for a human, supervisor, or background run. Use hard tool timeouts only for wedge protection; an elapsed timeout is not a mutation-safe boundary. Configured values must be positive integers no greater than `2147483647`; invalid or out-of-range values are rejected with a visible error rather than silently ignored.
|
|
237
251
|
|
|
238
252
|
## `globalConcurrencyLimit`
|
|
239
253
|
|
|
@@ -356,7 +370,7 @@ Overrides the command used to launch child Pi processes. Package wrappers can se
|
|
|
356
370
|
export PI_SUBAGENT_TASK_DELIVERY=file # auto | file (default: auto)
|
|
357
371
|
```
|
|
358
372
|
|
|
359
|
-
Controls how the task text reaches the child Pi process. `auto` (default) passes short tasks as an inline argv token and writes tasks longer than 8000 characters to a temp `task.md` referenced as `@<path>`. `file` always uses a temp file, keeping the task out of argv entirely.
|
|
373
|
+
Controls how the task text reaches the child Pi process. `auto` (default) passes short non-macOS tasks as an inline argv token, and writes macOS tasks plus tasks longer than 8000 characters to a temp `task.md` referenced as `@<path>`. `file` always uses a temp file, keeping the task out of argv entirely.
|
|
360
374
|
|
|
361
375
|
Use `file` on hosts where endpoint protection (EDR) pre-execution scanning denies child processes whose command line embeds a long natural-language task — that denial surfaces as an immediate zero-activity `SIGKILL`. Independently of this setting, startup retries automatically escalate to file delivery after an unexplained zero-activity `SIGKILL`. Empty, whitespace-only, or unrecognized values fall back to `auto`.
|
|
362
376
|
|
|
@@ -392,6 +406,18 @@ The default injected guidance tells children to use `contact_supervisor` with `r
|
|
|
392
406
|
|
|
393
407
|
Sets the base directory for `worktree: true` runs. Relative paths resolve from the repository root, `~/...` expands to your home directory, and `PI_SUBAGENTS_WORKTREE_DIR` is used when config is unset. The default remains the system temp directory.
|
|
394
408
|
|
|
409
|
+
## `worktreeProvider`
|
|
410
|
+
|
|
411
|
+
```json
|
|
412
|
+
{ "worktreeProvider": "auto", "worktreeBranchPrefix": "pi-subagents/" }
|
|
413
|
+
```
|
|
414
|
+
|
|
415
|
+
Selects the managed worktree allocator: `auto` (the default) uses Worktrunk when its machine-readable interface is available and otherwise falls back to Pi's native Git worktrees; `native` always uses Pi's Git implementation; and `worktrunk` fails closed when Worktrunk is unavailable or incompatible. A configured `worktreeBaseDir` (or `PI_SUBAGENTS_WORKTREE_DIR`) selects native allocation and cannot be combined with explicit `worktrunk`.
|
|
416
|
+
|
|
417
|
+
`worktreeBranchPrefix` is normalized as a Git ref namespace and defaults to `pi-subagents/`. Branch names include readable task/lane identity plus run and fan-out indexes. Pi continues to own setup hooks, launch, handoff/diff evidence, resume, and cleanup; Worktrunk is used only to allocate and report the worktree path.
|
|
418
|
+
|
|
419
|
+
Set `worktree` to `true` to make managed worktree isolation the default for launches that omit the per-call `worktree` flag. A per-call value still takes precedence.
|
|
420
|
+
|
|
395
421
|
## `worktreeSetupHook`
|
|
396
422
|
|
|
397
423
|
```json
|
package/docs/extension-api.md
CHANGED
|
@@ -309,7 +309,7 @@ Semantics:
|
|
|
309
309
|
- Providers share a registry through `Symbol.for("pi-subagents.background-work.v1")`, allowing independently loaded extension modules to meet in one Pi process.
|
|
310
310
|
- Registration is reload-safe: a new provider with the same name replaces the old callback, and the old disposer cannot remove the replacement. Call the disposer during extension shutdown when possible.
|
|
311
311
|
|
|
312
|
-
Child processes do not gain provider tools or extensions automatically. Add `bg_wait` to the child agent's `tools` allowlist
|
|
312
|
+
Child processes do not gain provider tools or extensions automatically. Add `bg_wait` to the child agent's `tools` allowlist and load each provider through `extensions` or `subagentOnlyExtensions`. The parent's effective `waitTool` setting is serialized through foreground, async, resume, chain, parallel, and fanout launch paths; `PI_SUBAGENT_WAIT_TOOL_ENABLED` keeps precedence.
|
|
313
313
|
|
|
314
314
|
## External job provider bridge
|
|
315
315
|
|
|
@@ -406,7 +406,7 @@ The API returns discriminated structured results with canonical project root, bi
|
|
|
406
406
|
|
|
407
407
|
A host that embeds this extension owns whether completion wakes can be delivered at all.
|
|
408
408
|
|
|
409
|
-
Ordinary async and foreground completion wakes use `registerSubagentNotify` and `sendCompletion`. They listen for completion events and deliver through `pi.sendMessage(..., { triggerTurn })`. Session shutdown stops the result watcher and disposes this completion notifier. `createWaitSubscriptionManager` is separate: it is the explicit non-blocking `bg_wait` subscription path for work without native notification, not the ordinary completion wake path.
|
|
409
|
+
Ordinary async and foreground completion wakes use `registerSubagentNotify` and `sendCompletion`. They listen for completion events and deliver through `pi.sendMessage(..., { triggerTurn })`. Session shutdown stops the result watcher and disposes this completion notifier. `createWaitSubscriptionManager` is separate: it is the explicit non-blocking `bg_wait` subscription path for work without native notification, not the ordinary completion wake path.
|
|
410
410
|
|
|
411
411
|
Detached children do not stop when the session does. They are the host process's children, not the session's, so the run keeps going, completes, and notifies nobody. What is lost is the notification, not the work.
|
|
412
412
|
|
package/docs/models.md
CHANGED
|
@@ -11,7 +11,7 @@ Builtin agents inherit your current Pi default model. This keeps new installs fr
|
|
|
11
11
|
- `subagents.agentOverridesByProvider.<provider>.<name>` — layer role fields for the active parent provider.
|
|
12
12
|
- Per-run overrides — for one launch only.
|
|
13
13
|
|
|
14
|
-
Precedence, strongest first: per-run override →
|
|
14
|
+
Precedence, strongest first: per-run override → provider-scoped role override → `agentOverrides.<name>.model` → agent frontmatter `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.
|
|
15
15
|
|
|
16
16
|
Use `model: "inherit"` in agent frontmatter or `agentOverrides.<name>.model` to select the current parent session model explicitly.
|
|
17
17
|
|
|
@@ -81,7 +81,7 @@ For a persistent role override with a backup model for provider failures:
|
|
|
81
81
|
}
|
|
82
82
|
```
|
|
83
83
|
|
|
84
|
-
`subagents.defaultModel` and `subagents.defaultProvider` apply to builtin, package, user, and project agents. `defaultModel` fills only agents that do not set `model` in frontmatter. `defaultProvider` is also applied to frontmatter and override models so bare ids resolve against the intended provider. Per-run model overrides and `agentOverrides.<name>.model`
|
|
84
|
+
`subagents.defaultModel` and `subagents.defaultProvider` apply to builtin, package, user, and project agents. `defaultModel` fills only agents that do not set `model` in frontmatter. `defaultProvider` is also applied to frontmatter and override models so bare ids resolve against the intended provider. Per-run model overrides and `agentOverrides.<name>.model` win over frontmatter and the global default. The same `agentOverrides` block can change `tools`, `skills`, inherited context, prompt text, or disable an agent (see [agents.md](agents.md)); matching custom-agent frontmatter is replaced for any field set by the override.
|
|
85
85
|
|
|
86
86
|
## Fast mode
|
|
87
87
|
|
|
@@ -116,7 +116,7 @@ One interaction worth knowing for tier 4: forked context over an Anthropic paren
|
|
|
116
116
|
|
|
117
117
|
## Thinking level defaults
|
|
118
118
|
|
|
119
|
-
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.
|
|
119
|
+
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. Matching `agentOverrides.<name>.thinking` and per-run thinking overrides replace frontmatter; otherwise explicit frontmatter remains in effect. `thinking: false` remains an explicit opt-out:
|
|
120
120
|
|
|
121
121
|
```json
|
|
122
122
|
{
|
|
@@ -129,7 +129,7 @@ Set `subagents.defaultThinking` to give builtin, package, user, and project agen
|
|
|
129
129
|
}
|
|
130
130
|
```
|
|
131
131
|
|
|
132
|
-
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
|
|
132
|
+
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 or replace custom-agent frontmatter thinking.
|
|
133
133
|
|
|
134
134
|
### Thinking ceiling
|
|
135
135
|
|
|
@@ -154,7 +154,7 @@ Set `subagents.defaultExtensions` to give builtin, package, user, and project ag
|
|
|
154
154
|
- Empty array: sets `extensions: []` for agents that do not explicitly define it, disabling ambient extension loading.
|
|
155
155
|
- Non-empty array: supplies that allowlist to agents that do not explicitly define one.
|
|
156
156
|
|
|
157
|
-
Project settings win over user settings. Use `agentOverrides.<name>.extensions` for per-agent settings;
|
|
157
|
+
Project settings win over user settings. Use `agentOverrides.<name>.extensions` for per-agent settings; a matching override replaces custom-agent frontmatter for that field.
|
|
158
158
|
|
|
159
159
|
```json
|
|
160
160
|
{
|
package/docs/observability.md
CHANGED
|
@@ -54,7 +54,7 @@ After you expand it:
|
|
|
54
54
|
reviewer · running 38s · ↓ 1.1k window · 1.4k spent
|
|
55
55
|
```
|
|
56
56
|
|
|
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
|
|
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 open the Fleet lobby; press `Enter` or `H` there to open its child-specific Herdr inspector. Printable navigation keys are never intercepted before activation.
|
|
58
58
|
|
|
59
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
|
|
|
@@ -70,6 +70,7 @@ Default keys:
|
|
|
70
70
|
- `x`/`Ctrl+O` — toggle tool details
|
|
71
71
|
- `r` — refresh
|
|
72
72
|
- `Esc` — close
|
|
73
|
+
- `Enter` — open the selected inspectable async child in its child-specific Herdr inspector
|
|
73
74
|
- `s` — compose an acknowledged message to a selected live async child; Tab cycles `steer`, `follow_up`, and `auto`
|
|
74
75
|
- `D` — stop a selected child's top-level async run after confirmation
|
|
75
76
|
- `H` — open the selected active async child in a Herdr inspector pane (Herdr 0.7.5+)
|
|
@@ -78,6 +79,8 @@ Set `fleetKeybindings` in the extension config to replace inspector-level keys w
|
|
|
78
79
|
|
|
79
80
|
`Ctrl+Alt+F` opens the same inspector even while a foreground turn is active and slash input is queued.
|
|
80
81
|
|
|
82
|
+
Enter and `H` use the existing Herdr pane path. In a child-specific Herdr inspector, type ordinary guidance and press Enter to send it through the acknowledged steer channel; `steer <message>`, `status`, and `stop` remain available as explicit controls.
|
|
83
|
+
|
|
81
84
|
Without a TUI, `/subagents-fleet` retains the textual `subagent({ action: "status", view: "fleet" })` fallback, and mutations use 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.
|
|
82
85
|
|
|
83
86
|
Use `/subagents-detach [run-id]` only for an active foreground single-subagent run you want to leave running without terminating; the eventual result remains available through status/wait.
|
|
@@ -157,7 +160,7 @@ For a top-level async run, `details.asyncDir` points at that directory; the fina
|
|
|
157
160
|
|
|
158
161
|
The result file is consumed and deleted once its completion notice is delivered. Before deletion, the watcher writes a versioned replay record under `<resultsDir>/completion-replay/<runId>.json` and a bounded output archive under `<resultsDir>/output-archives/<runId>.json`. Replay records expire with the completion deduplication window and are best-effort temporary state, not a permanent run ledger.
|
|
159
162
|
|
|
160
|
-
`bg_wait` surfaces a slim projection of each terminal payload it covered in its own tool-result `details.completions` — run identity, per-child agent/`runId`/success, artifact paths, and the bounded `archivePath`, without duplicating output text. It reads the replay when watcher delivery or a watcher restart has removed the one-shot result file and in-memory completion state is unavailable. Durable non-blocking wait subscriptions use the same replay in their delivered details.
|
|
163
|
+
`bg_wait` surfaces a slim projection of each terminal payload it covered in its own tool-result `details.completions` — run identity, per-child agent/`runId`/success, artifact paths, and the bounded `archivePath`, without duplicating output text. It reads the replay when watcher delivery or a watcher restart has removed the one-shot result file and in-memory completion state is unavailable. Durable non-blocking wait subscriptions use the same replay in their delivered details. Workflow result files record each child's `runId` explicitly, since a workflow child's `artifactPaths` entry points at its saved output rather than the artifact files keyed by the id. Extensions observing `tool_result` events can read run and artifact identity from there instead of parsing the text summary.
|
|
161
164
|
|
|
162
165
|
Output archives reference an existing child output artifact or session file when one is available. For children without either file, the archive stores a per-child `result-tail` entry with `resultIndex`, bounded to 64 KiB per child, and records whether it was truncated. Replay and archive JSON use `version: 1`; consumers must ignore unknown fields.
|
|
163
166
|
|
package/docs/tool-reference.md
CHANGED
|
@@ -109,7 +109,7 @@ The complete plain-JSON inventory is validated before the first launch (maximum
|
|
|
109
109
|
| `chatProgress` | `auto \| off \| live-card` | `auto` | WorkflowScript chat projection. `auto` renders a live in-chat card only for watched foreground workflows in the same Git repository, including managed worktrees; it is off otherwise. Explicit `live-card` requires `async:false` and the same Git repository. Async workflows have no inline live card, so omit `chatProgress` or use `auto`/`off`; use `async:false` only when the parent must block. |
|
|
110
110
|
| `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. |
|
|
111
111
|
| `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. Expiration of this run-level deadline is terminal and does not trigger `fallbackModels`. |
|
|
112
|
-
| `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`,
|
|
112
|
+
| `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 `bg_wait` are exempt. |
|
|
113
113
|
| `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. |
|
|
114
114
|
| `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. |
|
|
115
115
|
| `cwd` | string | runtime cwd | Override working directory. |
|
|
@@ -393,7 +393,7 @@ Acceptance evidence levels are `auto`, `none`, `attested`, `checked`, and `verif
|
|
|
393
393
|
Review is a separate gate configured with `acceptance.review`:
|
|
394
394
|
|
|
395
395
|
- Async, risky, and dynamic writer contexts infer checked evidence plus `review: { agent: "reviewer", required: true }`.
|
|
396
|
-
-
|
|
396
|
+
- Reviewer/read-only calls infer no acceptance by default; explicit acceptance requests still apply.
|
|
397
397
|
- Normal writer tasks infer checked evidence without review.
|
|
398
398
|
|
|
399
399
|
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.
|
|
@@ -420,7 +420,7 @@ Acceptance provenance is stored separately from child prose. `evidenceStatus` pr
|
|
|
420
420
|
|
|
421
421
|
### The acceptance report
|
|
422
422
|
|
|
423
|
-
For `attested` or stricter levels, the child prompt includes a standardized acceptance section and asks for a fenced `acceptance-report` JSON block.
|
|
423
|
+
For `attested` or stricter levels, the child prompt includes a standardized acceptance section and asks for a fenced `acceptance-report` JSON block. Reviewer/read-only inference resolves to `none`, so it does not add this section; explicit acceptance still does. With `outputSchema`, set `acceptance.report: "on"` to require the same report in the final `structured_output` call, or `"off"` to keep the fenced-report path. Omitting `report` preserves the default behavior. Runs without `outputSchema` never gain a standalone structured-output tool from this option.
|
|
424
424
|
|
|
425
425
|
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.
|
|
426
426
|
|
package/install.mjs
CHANGED
|
@@ -88,7 +88,6 @@ console.log(`
|
|
|
88
88
|
The extension is now available in pi. Tools added:
|
|
89
89
|
• subagent - Delegate tasks to agents and inspect run status
|
|
90
90
|
• bg_wait - Wait for background/provider/detached work without native completion notifications
|
|
91
|
-
(subagent_wait remains a deprecated compatibility alias)
|
|
92
91
|
|
|
93
92
|
Documentation: ${EXTENSION_DIR}/README.md
|
|
94
93
|
`);
|
package/package.json
CHANGED
|
@@ -168,13 +168,12 @@ from disabling `waitTool`, which returns immediately without arming a future
|
|
|
168
168
|
wake. If a foreground child detaches for supervisor coordination, reply first,
|
|
169
169
|
then wait on its id; do not resume or launch a replacement while it remains
|
|
170
170
|
detached. Headless sessions also auto-drain exact current-session work at
|
|
171
|
-
`agent_end` as a final safeguard.
|
|
172
|
-
deprecated compatibility alias for `bg_wait`.
|
|
171
|
+
`agent_end` as a final safeguard.
|
|
173
172
|
|
|
174
173
|
Providers are discovered through the `pi-subagents/background-work` registry and
|
|
175
174
|
must expose a stable item id and owning session id. Load a provider through the
|
|
176
|
-
child’s `extensions` or `subagentOnlyExtensions` and allow `bg_wait`
|
|
177
|
-
|
|
175
|
+
child’s `extensions` or `subagentOnlyExtensions` and allow `bg_wait` in its
|
|
176
|
+
tools. For
|
|
178
177
|
non-interactive fleets, launch N workers, wait for the next completion, react,
|
|
179
178
|
and replace as needed; use `all: true` only when intentionally draining the
|
|
180
179
|
fleet. If `PI_SUBAGENT_WAIT_TOOL_ENABLED` disables blocking, direct waits return
|
|
@@ -85,7 +85,7 @@ subagent({ action: "reset", agent: "reviewer" })
|
|
|
85
85
|
Use management actions when the system needs to create or edit subagents on
|
|
86
86
|
demand without dropping into raw file editing.
|
|
87
87
|
|
|
88
|
-
Management actions create or update user/project agent files. `config.name` is the local frontmatter name; optional `config.package` registers and looks up the runtime name as `{package}.{name}`. Use the dotted runtime name for `get`, `update`, `delete`, slash commands, and scripted workflow steps. For small
|
|
88
|
+
Management actions create or update user/project agent files. `config.name` is the local frontmatter name; optional `config.package` registers and looks up the runtime name as `{package}.{name}`. Use the dotted runtime name for `get`, `update`, `delete`, slash commands, and scripted workflow steps. For small agent changes such as a model swap, prefer `subagents.agentOverrides` in settings. Durable `.chain.md` definitions are legacy records, not a current authoring target; use `workflowScript` or `/prompt-workflow` for repeatable orchestration.
|
|
89
89
|
|
|
90
90
|
## Creating and Editing Agents by File
|
|
91
91
|
|
|
@@ -185,7 +185,7 @@ Builtin `worker` and `delegate` use strict tool allowlists and do not inherit am
|
|
|
185
185
|
|
|
186
186
|
Builtin agents inherit the current Pi default model unless a run, user setting, project setting, or `subagents.defaultModel` overrides `model`. The table records recommended tier routing, not shipped hard defaults; explicit run, user, or project settings still win. Keep the parent/orchestrator on the ordinary strong default model unless parent/user policy says otherwise. Override builtin defaults before copying full agent files when a small tweak is enough.
|
|
187
187
|
|
|
188
|
-
Set `subagents.defaultThinking` to apply a shared thinking level to builtin, package, user, and project agents whose frontmatter leaves `thinking` unset. Project settings win over user settings;
|
|
188
|
+
Set `subagents.defaultThinking` to apply a shared thinking level to builtin, package, user, and project agents whose frontmatter leaves `thinking` unset. Project settings win over user settings; matching `agentOverrides.<name>.thinking` and per-run overrides replace frontmatter, while an explicit frontmatter value remains in effect when no matching override is set. This setting affects child agents only and does not change the parent session's default thinking level.
|
|
189
189
|
|
|
190
190
|
```json
|
|
191
191
|
{
|
|
@@ -288,8 +288,8 @@ Use `fallbackModels` when a tier has provider quota or availability risk. Prefer
|
|
|
288
288
|
If a provider rejects model IDs with thinking suffixes, use
|
|
289
289
|
`subagents.disableThinking: true` in user or project settings to clear bundled
|
|
290
290
|
builtin thinking defaults globally. A higher-precedence per-agent `thinking`
|
|
291
|
-
override can opt one builtin back in
|
|
291
|
+
override can opt one builtin back in or replace custom-agent frontmatter thinking.
|
|
292
292
|
|
|
293
|
-
Set `subagents.defaultExtensions` to give agents without an `extensions` field a shared child extension allowlist. Omit it to preserve ambient extension discovery, set it to `[]` to disable ambient extensions by default, or use `agentOverrides.<name>.extensions` for one agent.
|
|
293
|
+
Set `subagents.defaultExtensions` to give agents without an `extensions` field a shared child extension allowlist. Omit it to preserve ambient extension discovery, set it to `[]` to disable ambient extensions by default, or use `agentOverrides.<name>.extensions` for one agent. A matching override replaces custom-agent frontmatter for that field.
|
|
294
294
|
|
|
295
295
|
Tool description modes live in `~/.pi/agent/extensions/subagent/config.json`, not `subagents` settings. The default uses split prompt metadata: a short tool description plus active `promptSnippet` and `promptGuidelines`. Set `toolDescriptionMode` to `full` or `compact` to force one description string, or `custom` to read `subagent-tool-description.md` from the project config dir or agent dir; invalid custom files fall back to full mode and the safety guidance is still appended.
|
|
@@ -250,13 +250,25 @@ function withDeclaredExtensionPaths(config: AgentConfig, filePath: string): Agen
|
|
|
250
250
|
export function editableAgentConfig(agent: AgentConfig): AgentConfig {
|
|
251
251
|
const { extensions: _extensions, ...withoutExtensions } = agent;
|
|
252
252
|
const base = agent.override?.base;
|
|
253
|
+
const description = base?.description ?? agent.description;
|
|
254
|
+
const frontmatterFields = agent.source === "builtin" || agent.source === "runtime" ? undefined : readAgentFrontmatterFields(agent.filePath);
|
|
255
|
+
const hasDeclaredField = (...fields: string[]) => frontmatterFields === undefined || fields.some((field) => frontmatterFields.has(field));
|
|
256
|
+
const withoutSettingsDefaults = (config: AgentConfig): AgentConfig => {
|
|
257
|
+
if (!frontmatterFields) return config;
|
|
258
|
+
const next = { ...config };
|
|
259
|
+
if (!hasDeclaredField("model")) delete next.model;
|
|
260
|
+
if (!hasDeclaredField("thinking")) delete next.thinking;
|
|
261
|
+
return next;
|
|
262
|
+
};
|
|
253
263
|
const {
|
|
254
264
|
override: _override,
|
|
265
|
+
description: _description,
|
|
255
266
|
output: _output,
|
|
256
267
|
outputMode: _outputMode,
|
|
257
268
|
defaultReads: _defaultReads,
|
|
258
269
|
model: _model,
|
|
259
270
|
fallbackModels: _fallbackModels,
|
|
271
|
+
fast: _fast,
|
|
260
272
|
thinking: _thinking,
|
|
261
273
|
systemPromptMode: _systemPromptMode,
|
|
262
274
|
inheritProjectContext: _inheritProjectContext,
|
|
@@ -269,27 +281,32 @@ export function editableAgentConfig(agent: AgentConfig): AgentConfig {
|
|
|
269
281
|
skills: _skills,
|
|
270
282
|
skillPath: _skillPath,
|
|
271
283
|
tools: _tools,
|
|
284
|
+
excludeTools: _excludeTools,
|
|
272
285
|
mcpDirectTools: _mcpDirectTools,
|
|
286
|
+
allowNestedSubagents: _allowNestedSubagents,
|
|
273
287
|
subagentOnlyExtensions: _subagentOnlyExtensions,
|
|
274
288
|
mutationTools: _mutationTools,
|
|
275
289
|
completionGuard: _completionGuard,
|
|
290
|
+
toolBudget: _toolBudget,
|
|
276
291
|
...editable
|
|
277
292
|
} = withoutExtensions;
|
|
278
293
|
if (!base) {
|
|
279
|
-
return withDeclaredExtensionPaths({
|
|
294
|
+
return withDeclaredExtensionPaths(withoutSettingsDefaults({
|
|
280
295
|
...withoutExtensions,
|
|
281
296
|
...(agent.extensionsFromDefault ? {} : agent.extensions !== undefined ? { extensions: [...agent.extensions] } : {}),
|
|
282
|
-
}, agent.filePath);
|
|
297
|
+
}), agent.filePath);
|
|
283
298
|
}
|
|
284
299
|
|
|
285
300
|
return withDeclaredExtensionPaths({
|
|
286
301
|
...editable,
|
|
302
|
+
description,
|
|
287
303
|
...(base.output !== undefined ? { output: base.output } : {}),
|
|
288
304
|
...(base.outputMode !== undefined ? { outputMode: base.outputMode } : {}),
|
|
289
305
|
...(base.defaultReads !== undefined ? { defaultReads: [...base.defaultReads] } : {}),
|
|
290
|
-
...(base.model !== undefined ? { model: base.model } : {}),
|
|
306
|
+
...(base.model !== undefined && hasDeclaredField("model") ? { model: base.model } : {}),
|
|
291
307
|
...(base.fallbackModels !== undefined ? { fallbackModels: [...base.fallbackModels] } : {}),
|
|
292
|
-
...(base.
|
|
308
|
+
...(base.fast !== undefined ? { fast: base.fast } : {}),
|
|
309
|
+
...(base.thinking !== undefined && hasDeclaredField("thinking") ? { thinking: base.thinking } : {}),
|
|
293
310
|
systemPromptMode: base.systemPromptMode,
|
|
294
311
|
inheritProjectContext: base.inheritProjectContext,
|
|
295
312
|
inheritGlobalContext: base.inheritGlobalContext,
|
|
@@ -301,11 +318,14 @@ export function editableAgentConfig(agent: AgentConfig): AgentConfig {
|
|
|
301
318
|
...(base.skills !== undefined ? { skills: [...base.skills] } : {}),
|
|
302
319
|
...(base.skillPath !== undefined ? { skillPath: [...base.skillPath] } : {}),
|
|
303
320
|
...(base.tools !== undefined ? { tools: [...base.tools] } : {}),
|
|
321
|
+
...(base.excludeTools !== undefined ? { excludeTools: [...base.excludeTools] } : {}),
|
|
304
322
|
...(base.mcpDirectTools !== undefined ? { mcpDirectTools: [...base.mcpDirectTools] } : {}),
|
|
323
|
+
...(base.allowNestedSubagents !== undefined ? { allowNestedSubagents: base.allowNestedSubagents } : {}),
|
|
305
324
|
...(base.extensions !== undefined ? { extensions: [...base.extensions] } : {}),
|
|
306
325
|
...(base.subagentOnlyExtensions !== undefined ? { subagentOnlyExtensions: [...base.subagentOnlyExtensions] } : {}),
|
|
307
326
|
...(base.mutationTools !== undefined ? { mutationTools: [...base.mutationTools] } : {}),
|
|
308
327
|
...(base.completionGuard !== undefined ? { completionGuard: base.completionGuard } : {}),
|
|
328
|
+
...(base.toolBudget !== undefined ? { toolBudget: base.toolBudget } : {}),
|
|
309
329
|
}, agent.filePath);
|
|
310
330
|
}
|
|
311
331
|
|
|
@@ -333,6 +353,7 @@ export function preservedAgentFrontmatterFields(agent: AgentConfig, cfg: Record<
|
|
|
333
353
|
if (hasKey(cfg, "model")) changed("model");
|
|
334
354
|
if (hasKey(cfg, "fallbackModels")) changed("fallbackModels");
|
|
335
355
|
if (hasKey(cfg, "tools")) changed("tools");
|
|
356
|
+
if (hasKey(cfg, "excludeTools")) changed("excludeTools");
|
|
336
357
|
if (hasKey(cfg, "skills")) changed("skill", "skills");
|
|
337
358
|
if (hasKey(cfg, "skillPath")) changed("skillPath");
|
|
338
359
|
if (hasKey(cfg, "extensions")) changed("extensions");
|
|
@@ -463,6 +484,18 @@ function applyAgentConfig(target: AgentConfig, cfg: Record<string, unknown>): st
|
|
|
463
484
|
else delete target.mcpDirectTools;
|
|
464
485
|
} else return "config.tools must be a comma-separated string or false when provided.";
|
|
465
486
|
}
|
|
487
|
+
if (hasKey(cfg, "excludeTools")) {
|
|
488
|
+
if (cfg.excludeTools === false || cfg.excludeTools === "") delete target.excludeTools;
|
|
489
|
+
else if (typeof cfg.excludeTools === "string") {
|
|
490
|
+
const excludeTools = parseCsv(cfg.excludeTools);
|
|
491
|
+
if (excludeTools.length) target.excludeTools = [...new Set(excludeTools)];
|
|
492
|
+
else delete target.excludeTools;
|
|
493
|
+
} else if (Array.isArray(cfg.excludeTools) && cfg.excludeTools.every((entry) => typeof entry === "string")) {
|
|
494
|
+
const excludeTools = [...new Set(cfg.excludeTools.map((entry) => entry.trim()).filter(Boolean))];
|
|
495
|
+
if (excludeTools.length) target.excludeTools = excludeTools;
|
|
496
|
+
else delete target.excludeTools;
|
|
497
|
+
} else return "config.excludeTools must be a comma-separated string, string array, or false when provided.";
|
|
498
|
+
}
|
|
466
499
|
if (hasKey(cfg, "skills")) {
|
|
467
500
|
if (cfg.skills === false || cfg.skills === "") delete target.skills;
|
|
468
501
|
else if (typeof cfg.skills === "string") {
|
|
@@ -595,6 +628,7 @@ function applyAgentConfig(target: AgentConfig, cfg: Record<string, unknown>): st
|
|
|
595
628
|
if (target.runner?.type === "external-cli" || target.runner?.type === "external-job") {
|
|
596
629
|
const unsupported = [
|
|
597
630
|
target.tools?.length || target.mcpDirectTools?.length ? "tools" : undefined,
|
|
631
|
+
target.excludeTools?.length ? "excludeTools" : undefined,
|
|
598
632
|
target.model ? "model" : undefined,
|
|
599
633
|
target.fallbackModels?.length ? "fallbackModels" : undefined,
|
|
600
634
|
target.thinking ? "thinking" : undefined,
|
|
@@ -701,6 +735,7 @@ function formatAgentCapabilitiesLine(agent: AgentConfig, providerNames: Set<stri
|
|
|
701
735
|
} else if (declaredTools.length > 0) {
|
|
702
736
|
tools = declaredTools.join(", ");
|
|
703
737
|
}
|
|
738
|
+
if (agent.excludeTools?.length) tools = `${tools}; excludes: ${agent.excludeTools.join(", ")}`;
|
|
704
739
|
let model = "inherits current session";
|
|
705
740
|
if (agent.model !== undefined) {
|
|
706
741
|
model = agent.model;
|
|
@@ -728,6 +763,7 @@ function agentCapabilityTools(agent: AgentConfig): AgentCapabilityRow["tools"] {
|
|
|
728
763
|
return {
|
|
729
764
|
ambient: agent.tools === undefined && agent.mcpDirectTools === undefined,
|
|
730
765
|
names: listOrEmpty(agent.tools),
|
|
766
|
+
...(agent.excludeTools !== undefined ? { excludeTools: [...agent.excludeTools] } : {}),
|
|
731
767
|
mcpDirectTools: listOrEmpty(agent.mcpDirectTools),
|
|
732
768
|
mutationTools: agent.mutationTools,
|
|
733
769
|
};
|
|
@@ -846,6 +882,7 @@ function formatAgentDetail(agent: AgentConfig): string {
|
|
|
846
882
|
if (agent.model) lines.push(`Model: ${agent.model}`);
|
|
847
883
|
if (agent.fallbackModels?.length) lines.push(`Fallback models: ${agent.fallbackModels.join(", ")}`);
|
|
848
884
|
if (tools.length) lines.push(`Tools: ${tools.join(", ")}`);
|
|
885
|
+
if (agent.excludeTools?.length) lines.push(`Excluded tools: ${agent.excludeTools.join(", ")}`);
|
|
849
886
|
if (agent.skills?.length) lines.push(`Skills: ${agent.skills.join(", ")}`);
|
|
850
887
|
if (agent.skillPath?.length) lines.push(`Skill paths: ${agent.skillPath.join(", ")}`);
|
|
851
888
|
lines.push(`System prompt mode: ${agent.systemPromptMode}`);
|
|
@@ -9,6 +9,7 @@ export const KNOWN_FIELDS = new Set([
|
|
|
9
9
|
"alias",
|
|
10
10
|
"aliases",
|
|
11
11
|
"tools",
|
|
12
|
+
"excludeTools",
|
|
12
13
|
"allowNestedSubagents",
|
|
13
14
|
"model",
|
|
14
15
|
"fallbackModels",
|
|
@@ -70,6 +71,8 @@ export function serializeAgent(config: AgentConfig, options: SerializeAgentOptio
|
|
|
70
71
|
];
|
|
71
72
|
const toolsValue = joinComma(tools);
|
|
72
73
|
if (toolsValue || preserve("tools")) lines.push(`tools: ${toolsValue ?? ""}`);
|
|
74
|
+
const excludeToolsValue = joinComma(config.excludeTools);
|
|
75
|
+
if (excludeToolsValue || preserve("excludeTools")) lines.push(`excludeTools: ${excludeToolsValue ?? ""}`);
|
|
73
76
|
if (config.allowNestedSubagents === true || preserve("allowNestedSubagents")) {
|
|
74
77
|
lines.push(`allowNestedSubagents: ${config.allowNestedSubagents === undefined ? "" : config.allowNestedSubagents ? "true" : "false"}`);
|
|
75
78
|
}
|