pi-subagents 0.36.0 → 0.37.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 +17 -0
- package/README.md +93 -8
- package/agents/delegate.md +2 -0
- package/agents/worker.md +2 -0
- package/package.json +4 -2
- package/skills/pi-subagents/SKILL.md +20 -4
- package/src/agents/agent-management.ts +7 -1
- package/src/agents/agents.ts +99 -12
- package/src/api/capability-ceiling.ts +17 -0
- package/src/api/delegation.ts +3 -1
- package/src/api/preflight.ts +399 -0
- package/src/extension/index.ts +2 -0
- package/src/extension/rpc.ts +4 -0
- package/src/extension/schemas.ts +8 -2
- package/src/extension/tool-description.ts +2 -0
- package/src/runs/background/async-execution.ts +125 -14
- package/src/runs/background/async-resume.ts +24 -7
- package/src/runs/background/async-status.ts +18 -0
- package/src/runs/background/process-terminal.ts +280 -0
- package/src/runs/background/run-status.ts +7 -1
- package/src/runs/background/scheduled-runs.ts +6 -1
- package/src/runs/background/stale-run-reconciler.ts +6 -0
- package/src/runs/background/subagent-runner.ts +182 -12
- package/src/runs/foreground/chain-execution.ts +5 -0
- package/src/runs/foreground/execution.ts +59 -13
- package/src/runs/foreground/subagent-executor.ts +32 -2
- package/src/runs/shared/acceptance.ts +41 -30
- package/src/runs/shared/capability-ceiling.ts +177 -0
- package/src/runs/shared/dynamic-fanout.ts +1 -1
- package/src/runs/shared/mcp-direct-tool-allowlist.ts +12 -6
- package/src/runs/shared/nested-events.ts +8 -1
- package/src/runs/shared/parallel-utils.ts +5 -0
- package/src/runs/shared/pi-args.ts +141 -58
- package/src/runs/shared/session-lease.ts +25 -5
- package/src/runs/shared/subagent-prompt-runtime.ts +14 -1
- package/src/runs/shared/tool-availability.ts +18 -2
- package/src/shared/launch-contract.ts +123 -0
- package/src/shared/types.ts +104 -7
- package/src/shared/utils.ts +17 -42
- package/src/slash/delegation-adapters.ts +1 -1
- package/src/slash/slash-commands.ts +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,23 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [0.37.0] - 2026-07-25
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
- Bound public launch preflight to versioned selected-agent definition digests, projected async lifecycle/status/result/process-terminal roots, and actual foreground/async execution digests in result and status metadata. Thanks to @shaggitza for #637.
|
|
9
|
+
- Added `subagents.defaultExtensions` for shared child extension allowlists and `agentOverrides.<name>.extensions` for per-agent settings. Thanks to chronoAP for #642.
|
|
10
|
+
- Added a public `pi-subagents/preflight` API that resolves an ordinary single-agent launch contract without creating child sessions, temp prompt files, structured-output runtimes, or run artifacts. Thanks to @shaggitza for #634.
|
|
11
|
+
- Added an out-of-band, session-scoped capability-ceiling API for monotonic child tool and extension restrictions, with inherited async/nested propagation and bounded audit metadata. Thanks to aoguai for #585.
|
|
12
|
+
- Added durable v3 process-terminal proof for detached async runners, with exact close observation, conservative unknown states after observer loss, and status/RPC projections. Thanks to shaggitza for #626.
|
|
13
|
+
- Added `subagents.defaultThinking` for project- or user-scoped default thinking levels on agents without explicit thinking settings. Thanks to corrius for #612.
|
|
14
|
+
- Documented that builtin worker and delegate agents use strict tool allowlists and do not inherit ambient parent extension tools; custom agents must explicitly name extension tools and load their providers. Thanks to buihongduc132 for #586.
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
- Preferred direct empty terminal-response evidence over stale tool errors so fallback models can retry abandoned child turns, and stopped treating successful tool output as a hidden failure. Thanks to Dmitry S. (@nuzayets) for #645.
|
|
18
|
+
- Separated evidence acceptance from independent review: evidence levels now end at `verified`, risky runs carry an orthogonal review requirement, `review-required` reports pending review while preserving `evidenceStatus`, and `reviewed` is reserved for achieved independent review. Explicit `reviewed` remains schema-recognized solely for actionable preflight recovery. Thanks to Theodor Hillmann (@t0dorakis) for #440.
|
|
19
|
+
- Bound public preflight launch digests to resolved skill injection metadata, matching execution when skill descriptions change.
|
|
20
|
+
- Classified missing resolved MCP direct tools as a host/pi-mcp-adapter child-registration problem while preserving strict fail-closed diagnostics. Thanks to peedrr for #638.
|
|
21
|
+
|
|
5
22
|
## [0.36.0] - 2026-07-24
|
|
6
23
|
|
|
7
24
|
### Added
|
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
`pi-subagents` lets Pi delegate work to focused child agents. Use it for code review, scouting, implementation, parallel audits, saved workflows, background jobs, and anything else that benefits from a second or third set of model eyes.
|
|
8
8
|
|
|
9
|
-
https://github.com/user-attachments/assets/702554ec-faaf-4635-80aa-fb5d6e292fd1
|
|
9
|
+
<https://github.com/user-attachments/assets/702554ec-faaf-4635-80aa-fb5d6e292fd1>
|
|
10
10
|
|
|
11
11
|
## Installation
|
|
12
12
|
|
|
@@ -157,7 +157,37 @@ For a persistent override, edit settings. This example pins the reviewer everywh
|
|
|
157
157
|
|
|
158
158
|
Use `~/.pi/agent/settings.json` for a user override or the project config settings file (`.pi/settings.json` in standard Pi) for a project override. `subagents.defaultModel` applies to builtin, package, user, and project agents that do not set `model` in frontmatter. Per-run model overrides and `agentOverrides.<name>.model` still win, and explicit agent frontmatter still wins over the global default. The same `agentOverrides` block can change `tools`, `skills`, inherited context, prompt text, or disable a builtin. Matching user and project agents also receive override fields that their frontmatter leaves unset, so a shared project config agent can keep the persona while local settings choose the model.
|
|
159
159
|
|
|
160
|
-
|
|
160
|
+
Set `subagents.defaultThinking` to give builtin, package, user, and project agents without a `thinking` value a shared thinking level, independent of the parent session's default. Project settings win over user settings. Explicit frontmatter, `agentOverrides.<name>.thinking`, and per-run thinking overrides still win; `thinking: false` remains an explicit opt-out:
|
|
161
|
+
|
|
162
|
+
```json
|
|
163
|
+
{
|
|
164
|
+
"subagents": {
|
|
165
|
+
"defaultThinking": "medium",
|
|
166
|
+
"agentOverrides": {
|
|
167
|
+
"reviewer": { "thinking": "high" }
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
If your provider rejects model IDs with thinking suffixes, set `subagents.disableThinking: true` in user or project settings. That clears bundled builtin thinking defaults in one place; an explicit higher-precedence `agentOverrides.<name>.thinking` value can opt a role back in. Existing custom-agent frontmatter remains authoritative.
|
|
174
|
+
|
|
175
|
+
Set `subagents.defaultExtensions` to give builtin, package, user, and project agents without an `extensions` field a shared extension allowlist. Absent preserves Pi's normal ambient extension discovery. Present as an empty array, the default sets `extensions: []` for agents that do not explicitly define it, disabling ambient extension loading. Present as a non-empty array, the default supplies that allowlist to agents that do not explicitly define one. Project settings win over user settings. Use `agentOverrides.<name>.extensions` for per-agent settings; explicit custom-agent frontmatter remains authoritative.
|
|
176
|
+
|
|
177
|
+
```json
|
|
178
|
+
{
|
|
179
|
+
"subagents": {
|
|
180
|
+
"defaultExtensions": [],
|
|
181
|
+
"agentOverrides": {
|
|
182
|
+
"researcher": {
|
|
183
|
+
"extensions": ["./tools/research.ts"]
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
A non-array value, an array containing a non-string entry, or an empty/whitespace-only string raises a settings error naming `defaultExtensions` and the offending settings file, matching the validation pattern used by `defaultModel` and `defaultThinking`.
|
|
161
191
|
|
|
162
192
|
To inspect what `pi-subagents` has actually loaded right now, use:
|
|
163
193
|
|
|
@@ -256,6 +286,8 @@ You can also ask naturally:
|
|
|
256
286
|
Show me the current async runs.
|
|
257
287
|
```
|
|
258
288
|
|
|
289
|
+
Lifecycle artifact v3 adds `process-terminal-candidate.json` (private runner evidence) and `process-terminal.json` (the public proof projection). A proof is `observed` only after the live parent observes the exact detached runner's `close` event, every recorded child writer has a close record, and any tracked canonical-session lease is free. If the observer is unavailable, the proof is `unknown`; do not infer process exit from `endedAt`, result-file existence, PID disappearance, or lease-directory absence. The `subagent:process-terminal` event and RPC `ping.capabilities.processTerminalProof` expose this status. Process proof is point-in-time evidence and remains separate from execution success or stopped/non-resumable state.
|
|
290
|
+
|
|
259
291
|
Async runs also write machine-readable lifecycle artifacts for observability and workflow gates. For a top-level async run, `details.asyncDir` points at a directory containing `status.json`, `events.jsonl`, `output-<index>.log`, and `subagent-log-<runId>.md`; the final summary is written to Pi's subagent results directory as `<runId>.json`. Nested async runs use the same shape under the nested async root and are discoverable through status projections that read the nested-run registry. These files are append/update artifacts only; interactive foreground behavior is unchanged.
|
|
260
292
|
|
|
261
293
|
Foreground and async runners share bounded child-protocol handling. A child JSONL line above 4 MiB fails with structured `protocolError` code `protocol_output_limit`, stderr retains only its latest 128 KiB, split UTF-8 and final unterminated JSON events remain valid, and `agent_end.willRetry` defers completion until the child settles. Current Pi builds use `agent_settled` as the terminal watermark; older builds retain the bounded terminal-message fallback.
|
|
@@ -565,7 +597,7 @@ Append `[key=value,...]` to an agent name to override defaults. `/chain` applies
|
|
|
565
597
|
| `cwd` | `cwd=packages/api` | Run the step in a subdirectory. |
|
|
566
598
|
| `count` | `count=3` | Fan a group task into N copies (only inside a `( ... )` group). |
|
|
567
599
|
| `outputSchema` | `outputSchema=schema.json` | Validate structured output against a JSON Schema file (path resolved against the session cwd, not an inline step `cwd`). |
|
|
568
|
-
| `acceptance` | `acceptance=checked` | Inline
|
|
600
|
+
| `acceptance` | `acceptance=checked` | Inline evidence level: `auto`, `attested`, or `checked`. Use the tool API or saved `.chain.json` for object contracts such as `none`, `verified`, or an orthogonal review requirement. `reviewed` is an achieved status, not an input level. |
|
|
569
601
|
|
|
570
602
|
Set `output=false`, `reads=false`, or `skills=false` to disable that behavior explicitly. Do not use `output=false` for file-only returns; use `outputMode=file-only` with an `output` path.
|
|
571
603
|
|
|
@@ -796,7 +828,7 @@ Examples:
|
|
|
796
828
|
- `tools: subagent, read`: a child-safe `subagent` tool is available inside that child so it can run explicitly assigned nested fanout.
|
|
797
829
|
- `tools: read, fixture_search` plus `subagentOnlyExtensions: ./tools/fixture-search.ts`: the provider loads only in this agent's child process, and the registered `fixture_search` name survives the strict allowlist.
|
|
798
830
|
|
|
799
|
-
Direct MCP tools require [pi-mcp-adapter](https://github.com/nicobailon/pi-mcp-adapter). Subagents only receive direct MCP tools when `mcp:` entries are listed in their frontmatter; global `directTools: true` in `mcp.json` is not enough by itself. The generic `mcp` proxy tool can still be used for discovery when available. The adapter caches tool metadata at startup, so after connecting a new MCP server for the first time, restart Pi before relying on direct tools. An `mcp:` entry named `subagent` does not authorize nested fanout; only the builtin `subagent` tool name does.
|
|
831
|
+
Direct MCP tools require [pi-mcp-adapter](https://github.com/nicobailon/pi-mcp-adapter). Subagents only receive direct MCP tools when `mcp:` entries are listed in their frontmatter; global `directTools: true` in `mcp.json` is not enough by itself. The generic `mcp` proxy tool can still be used for discovery when available. The adapter caches tool metadata at startup, so after connecting a new MCP server for the first time, restart Pi before relying on direct tools. An `mcp:` entry named `subagent` does not authorize nested fanout; only the builtin `subagent` tool name does. If a resolved direct MCP name is missing from the child registry, pi-subagents keeps the launch failed under the strict allowlist and identifies the condition as a host/pi-mcp-adapter registration problem; verify that the adapter registers the selected tools before child startup.
|
|
800
832
|
|
|
801
833
|
`extensions` controls child extension loading:
|
|
802
834
|
|
|
@@ -814,6 +846,8 @@ When `extensions` is present, normal discovered extensions are disabled; the lis
|
|
|
814
846
|
|
|
815
847
|
Use `subagentOnlyExtensions` when a custom extension tool should exist only inside child sessions. It is scoped by agent config: every run of that agent receives those extension paths, while other agents do not unless they declare the same field. The current model does not have a separate named-subagent audience inside one agent definition.
|
|
816
848
|
|
|
849
|
+
To apply the same `extensions` allowlist to every agent that does not declare its own, set `subagents.defaultExtensions` in user or project settings. Omit it to preserve ambient extension discovery or set it to `[]` to disable ambient extensions by default; project settings win over user settings. Agents that explicitly define `extensions` keep their own value, including an empty `extensions:` field.
|
|
850
|
+
|
|
817
851
|
Before the first model turn, the child runtime compares every explicit tool name with Pi's final filtered registry. A missing provider now fails the run with the unavailable names and concrete `subagentOnlyExtensions`/`extensions` guidance instead of letting a direct or chained child silently continue without its requested tools.
|
|
818
852
|
|
|
819
853
|
## Chain files
|
|
@@ -973,6 +1007,7 @@ Missing skills do not fail execution. The result summary shows a warning.
|
|
|
973
1007
|
The package bundles a `pi-subagents` skill that is automatically available to the parent agent when the extension is installed. It is for the orchestrating parent only: child subagents never receive it, and their context is explicitly filtered to strip parent-only orchestration instructions.
|
|
974
1008
|
|
|
975
1009
|
What the bundled skill covers:
|
|
1010
|
+
|
|
976
1011
|
- **Delegation patterns**: when to launch which agent, whether to use single, parallel, chain, or async mode, and whether to use fresh or forked context
|
|
977
1012
|
- **Prompt workflow recipes**: how to apply the packaged techniques directly with `subagent(...)` when the user describes the workflow in natural language instead of invoking a slash command. This includes parallel review, review-loop, parallel research, parallel context-build, parallel handoff-plan, gather-context-and-clarify, and parallel cleanup
|
|
978
1013
|
- **Role-agent prompting guidance**: compact contract prompts instead of long scripts, what to include in role-specific meta prompts, and retrieval budgets for researchers
|
|
@@ -987,6 +1022,33 @@ If you are writing an agent that orchestrates subagents, the bundled skill helps
|
|
|
987
1022
|
Pi extensions can request configured foreground agents through the public event
|
|
988
1023
|
contract exported by `pi-subagents/delegation`.
|
|
989
1024
|
|
|
1025
|
+
### Launch contract preflight
|
|
1026
|
+
|
|
1027
|
+
Use `pi-subagents/preflight` when an extension needs to inspect the resolved child launch contract before deciding whether to run anything:
|
|
1028
|
+
|
|
1029
|
+
```ts
|
|
1030
|
+
import { resolveSubagentLaunchContract } from "pi-subagents/preflight";
|
|
1031
|
+
|
|
1032
|
+
const result = await resolveSubagentLaunchContract({
|
|
1033
|
+
agent: "reviewer",
|
|
1034
|
+
task: "Review the current diff.",
|
|
1035
|
+
context: "fresh",
|
|
1036
|
+
cwd: ctx.cwd,
|
|
1037
|
+
sessionRoot: "/tmp/my-extension-preflight-session-root",
|
|
1038
|
+
availableModels: ctx.modelRegistry.getAvailable(),
|
|
1039
|
+
});
|
|
1040
|
+
|
|
1041
|
+
if (!result.ok) {
|
|
1042
|
+
// missing_agent, ambiguous_agent, missing_skill, denied_required_tool,
|
|
1043
|
+
// invalid_artifact_dir, invalid_cwd, or unsupported_mode
|
|
1044
|
+
throw new Error(result.message);
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
console.log(result.contract.digest, result.contract.tools.effectiveAllowlist);
|
|
1048
|
+
```
|
|
1049
|
+
|
|
1050
|
+
Preflight covers ordinary single-agent launch resolution under public contract version 2: selected agent identity and shadowed candidates, a versioned parsed-definition digest (including system prompt and launch-affecting model, tool, skill, extension, output, and memory fields), fresh/fork context, effective model and thinking, skill and tool resolution, direct MCP selections, runtime/configured extensions, artifact/session paths, async lifecycle/status/result/event/process-terminal paths, package/lifecycle versions, capability-ceiling audit data, and stable digests. `launchContractDigest` is the canonical digest of the caller task, effective system prompt (including the resolved `turnBudget` prompt augmentation when supplied), model candidates, effective tools/extensions/MCP (including inherited capability ceilings), output binding, and structured-output schema that ordinary foreground and async execution report in results/status/events and metadata. Runtime acceptance prose and output-task annotations are intentionally excluded because side-effect-free preflight does not resolve those host/runtime augmentations; the contract version and task digest make that boundary explicit. Raw prompts are not exposed in public contract output. It is side-effect-free for launch state: it does not create child sessions, temp prompt files, structured-output runtimes, tool-diagnostic files, or run artifacts. Some host-owned facts, such as exact fork snapshots, nested async roots, and live model registries, can only be proven by the Pi host; those appear as `host_required` diagnostics instead of silently pretending to be exact.
|
|
1051
|
+
|
|
990
1052
|
### Delegation v1
|
|
991
1053
|
|
|
992
1054
|
The compatibility v1 contract runs one configured foreground agent per request:
|
|
@@ -1104,6 +1166,26 @@ tools, skills, context, model policy, and workspace authority; it is not a
|
|
|
1104
1166
|
sandbox or a durable task broker. `pi-subagents/delegation` is the canonical
|
|
1105
1167
|
contract for extension integrations.
|
|
1106
1168
|
|
|
1169
|
+
## Capability ceilings
|
|
1170
|
+
|
|
1171
|
+
Parent extensions can enforce an out-of-band, session-scoped capability ceiling without adding a model-visible field to `subagent`:
|
|
1172
|
+
|
|
1173
|
+
```ts
|
|
1174
|
+
import { registerSubagentCapabilityCeiling } from "pi-subagents/capability-ceiling";
|
|
1175
|
+
|
|
1176
|
+
const restriction = registerSubagentCapabilityCeiling({
|
|
1177
|
+
sessionId: ctx.sessionManager.getSessionId(),
|
|
1178
|
+
source: "plan-mode",
|
|
1179
|
+
ceiling: { allowedTools: ["read", "grep", "find", "ls"], denyExtensions: true },
|
|
1180
|
+
});
|
|
1181
|
+
// restriction.update(...) replaces this provider's policy atomically.
|
|
1182
|
+
// restriction.dispose() removes only this provider's registration.
|
|
1183
|
+
```
|
|
1184
|
+
|
|
1185
|
+
Active registrations intersect their `allowedTools` sets and OR `denyExtensions`; an explicit empty list means no caller-facing tools, while an omitted list does not restrict names. The resolved snapshot is propagated monotonically to nested and async children and is retained for recovery. `structured_output` may remain as a package-owned internal protocol tool when an output schema requires it; it is not a caller capability. A denied lazy-skill `read` requirement fails before spawn rather than widening the ceiling.
|
|
1186
|
+
|
|
1187
|
+
`denyExtensions` suppresses ambient, configured, and MCP provider extensions while retaining the package runtime needed for child protocol enforcement. This is a same-process policy boundary, not a sandbox against malicious code already running in the parent process. Schedules created while a ceiling is active are rejected until durable schedule persistence is available; unrestricted schedules remain subject to any policy active when they fire. Public status exposes bounded audit counts and sources, never full extension paths.
|
|
1188
|
+
|
|
1107
1189
|
## Background-work provider API
|
|
1108
1190
|
|
|
1109
1191
|
Other Pi extensions can make their current-session jobs visible to `subagent_wait` through the versioned process-local provider contract:
|
|
@@ -1309,7 +1391,7 @@ Agent definitions are not loaded into context by default. Management actions let
|
|
|
1309
1391
|
| `includeProgress` | boolean | false | Include full progress in result. |
|
|
1310
1392
|
| `share` | boolean | false | Upload session export to GitHub Gist. |
|
|
1311
1393
|
| `sessionDir` | string | derived | Override session log directory. |
|
|
1312
|
-
| `acceptance` | string/object/false | inferred |
|
|
1394
|
+
| `acceptance` | string/object/false | inferred | Configure evidence gates with `"auto"`, `"attested"`, `"checked"`, `"verified"`, or `{ level: "none", reason: "..." }`. Independent review is orthogonal: use `review: { required: true, agent?: "reviewer", focus?: "..." }`. `review-required` means evidence passed but review is pending; `reviewed` is achieved only after a real independent result. Explicit `"reviewed"` remains schema-recognized solely for actionable preflight recovery. For reviewer/read-only calls, omit acceptance. `false` disables gates. With `agentContract: { version: 1 }`, omitted, `"auto"`, and `false` mean no acceptance request for that run; explicit acceptance is reported separately from execution. |
|
|
1313
1395
|
|
|
1314
1396
|
`agentContract: { version: 1 }` keeps existing fields and artifacts but adds derived `execution`, `acceptance`, `review`, and `effects` projections. In v1, acceptance failures do not rewrite execution success, and an explicit completion guard reports `effects.fileMutation` instead of failing the run by itself. Chain steps default to advancing on execution under v1; set `gateOn: "acceptance"` on a v1 step or parallel task when rejected acceptance should stop the chain.
|
|
1315
1397
|
|
|
@@ -1644,15 +1726,18 @@ Every run resolves an effective acceptance policy. Callers may omit `acceptance`
|
|
|
1644
1726
|
}
|
|
1645
1727
|
```
|
|
1646
1728
|
|
|
1647
|
-
Acceptance
|
|
1729
|
+
Acceptance evidence levels are `auto`, `none`, `attested`, `checked`, and `verified`. `acceptance: "auto"` is the default. Review is a separate gate configured with `acceptance.review`; async, risky, and dynamic writer contexts infer checked evidence plus `review: { agent: "reviewer", required: true }`. Read-only tasks infer lightweight attestation, while normal writer tasks infer checked evidence without review. Agent frontmatter or `subagents.agentOverrides` may set `acceptanceRole: "read-only" | "writer"` for ambiguous tasks; explicit task mutation or no-edit intent wins over that role, while omitted metadata preserves the existing reviewer/scout/worker name heuristics. The role affects acceptance inference only and does not change tool access. The bare string `"none"` is rejected; use `{ level: "none", reason: "..." }` instead. `acceptance: false` is accepted only as a deprecated shorthand for disabling gates.
|
|
1730
|
+
|
|
1731
|
+
For reviewer/read-only calls, omit `acceptance`. The explicit value `"reviewed"` is not a policy level: it remains schema-recognized only so semantic preflight can explain the mistake without spawning a child. To require review of a writer result, use `acceptance: { level: "checked", review: { required: true, agent: "reviewer" } }` and orchestrate the reviewer separately.
|
|
1648
1732
|
|
|
1649
|
-
Acceptance provenance is stored separately from child prose:
|
|
1733
|
+
Acceptance provenance is stored separately from child prose. `evidenceStatus` preserves evidence progress when the overall status is waiting on or has completed review:
|
|
1650
1734
|
|
|
1651
1735
|
- `claimed`: child finished but did not provide structured evidence.
|
|
1652
1736
|
- `attested`: child returned a structured acceptance report.
|
|
1653
1737
|
- `checked`: runtime structural checks passed, such as required evidence and no staged files.
|
|
1654
1738
|
- `verified`: configured runtime verification commands passed. Child-reported command success does not count.
|
|
1655
|
-
- `
|
|
1739
|
+
- `review-required`: required evidence passed, but no independent reviewer result has been supplied.
|
|
1740
|
+
- `reviewed`: an independent reviewer result is present and has no blockers.
|
|
1656
1741
|
- `rejected`: attestation, structural checks, verification, or review failed.
|
|
1657
1742
|
|
|
1658
1743
|
For `attested` or stricter levels, the child prompt includes a standardized acceptance section and asks for a fenced `acceptance-report` JSON block. The parser canonicalizes known enum synonyms, snake_case report keys and wrappers, underscore fence tags, unambiguous scalar arrays, string booleans, and criterion-id separators. Unknown or ambiguous keys and enum values fail with field-level diagnostics. Explicit empty `changedFiles` and `testsAddedOrUpdated` arrays are recorded as not applicable; missing fields and empty required command or validation evidence still fail.
|
package/agents/delegate.md
CHANGED
|
@@ -9,4 +9,6 @@ inheritSkills: false
|
|
|
9
9
|
|
|
10
10
|
You are a delegated agent. Execute the assigned task using the provided tools. Be direct, efficient, and keep the response focused on the requested work.
|
|
11
11
|
|
|
12
|
+
The builtin delegate uses a strict tool allowlist and does not inherit ambient extension tools from the parent session. To use an extension tool, configure a custom agent with the tool name explicitly listed in `tools` and load its provider through `extensions` or `subagentOnlyExtensions`.
|
|
13
|
+
|
|
12
14
|
If runtime bridge instructions identify a safe supervisor target and you are blocked or need a decision, use `contact_supervisor` with `reason: "need_decision"` and stay alive for the reply. Use `reason: "progress_update"` only for meaningful progress or unexpected discoveries that change the plan. Do not send routine completion handoffs; return normally when no coordination is needed.
|
package/agents/worker.md
CHANGED
|
@@ -17,6 +17,8 @@ You are the single writer thread. Your job is to execute the assigned task or ap
|
|
|
17
17
|
|
|
18
18
|
Use the provided tools directly. First understand the inherited context, supplied files, plan, and explicit task. Then implement carefully and minimally.
|
|
19
19
|
|
|
20
|
+
The builtin worker uses a strict tool allowlist. It does not inherit ambient extension tools from the parent session. To use an extension tool, configure a custom agent with the tool name explicitly listed in `tools` and load its provider through `extensions` or `subagentOnlyExtensions`.
|
|
21
|
+
|
|
20
22
|
If the task is framed as an approved direction, oracle handoff, or execution plan, treat that direction as the contract. Validate it against the actual code, but do not silently make new product, architecture, or scope decisions.
|
|
21
23
|
|
|
22
24
|
If the implementation reveals a decision that was not approved and is required to continue safely, pause and escalate through the live coordination channel. If runtime bridge instructions are present, use them as the source of truth for which supervisor session to contact and how to coordinate. Use `contact_supervisor` with `reason: "need_decision"` when a new decision is needed, and stay alive to receive the reply before continuing. Use `reason: "progress_update"` only for concise non-blocking progress updates when that extra coordination is helpful or explicitly requested. Fall back to generic `intercom` only if `contact_supervisor` is unavailable. Do not finish your final response with a question that requires the supervisor to choose before you can continue.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-subagents",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.37.0",
|
|
4
4
|
"description": "Pi extension for delegating tasks to subagents with chains, parallel execution, and TUI clarification",
|
|
5
5
|
"author": "Nico Bailon",
|
|
6
6
|
"license": "MIT",
|
|
@@ -8,7 +8,9 @@
|
|
|
8
8
|
"exports": {
|
|
9
9
|
".": "./index.ts",
|
|
10
10
|
"./background-work": "./src/api/background-work.ts",
|
|
11
|
-
"./delegation": "./src/api/delegation.ts"
|
|
11
|
+
"./delegation": "./src/api/delegation.ts",
|
|
12
|
+
"./capability-ceiling": "./src/api/capability-ceiling.ts",
|
|
13
|
+
"./preflight": "./src/api/preflight.ts"
|
|
12
14
|
},
|
|
13
15
|
"repository": {
|
|
14
16
|
"type": "git",
|
|
@@ -14,6 +14,10 @@ This skill is for the main parent orchestrator only. Do not inject or follow it
|
|
|
14
14
|
|
|
15
15
|
Use this skill when the parent orchestrator needs to launch a specialized subagent, compose multiple agents into a workflow, or create/edit agents and chains on demand.
|
|
16
16
|
|
|
17
|
+
## Capability ceilings
|
|
18
|
+
|
|
19
|
+
Parent extensions may register a session-scoped, out-of-band ceiling through `pi-subagents/capability-ceiling`. Child tools are intersected with every active registration and inherited snapshot; `denyExtensions` removes ambient/provider extension loading while retaining package protocol runtime. Do not add a model-visible ceiling field or rely on role selection for enforcement. Restricted schedules are rejected until their ceiling can be persisted safely.
|
|
20
|
+
|
|
17
21
|
## When to Use
|
|
18
22
|
|
|
19
23
|
- **Complex work orchestration**: use Fable mode as the default parent-agent loop for complex work. Complex means the task has multiple moving parts, unclear acceptance, cross-cutting code, meaningful user-visible impact, expensive or irreversible validation, broad review surface, or the user asks for orchestration. Lightweight one-off delegation can stay lightweight.
|
|
@@ -195,6 +199,16 @@ and user/project agents override builtins with the same name.
|
|
|
195
199
|
|
|
196
200
|
Builtin agents inherit the current Pi default model unless a run, user setting, project setting, or `subagents.defaultModel` overrides `model`. Set `subagents.defaultModel` when subagents should use a different default model than the parent session. Override builtin defaults before copying full agent files when a small tweak is enough.
|
|
197
201
|
|
|
202
|
+
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; explicit frontmatter (including `thinking: false`), `agentOverrides.<name>.thinking`, and per-run overrides remain more specific. This setting affects child agents only and does not change the parent session's default thinking level.
|
|
203
|
+
|
|
204
|
+
```json
|
|
205
|
+
{
|
|
206
|
+
"subagents": {
|
|
207
|
+
"defaultThinking": "medium"
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
```
|
|
211
|
+
|
|
198
212
|
For one run, use inline config:
|
|
199
213
|
|
|
200
214
|
```text
|
|
@@ -247,14 +261,16 @@ Direct settings example:
|
|
|
247
261
|
|
|
248
262
|
Useful override fields: `model`, `fallbackModels`, `thinking`,
|
|
249
263
|
`systemPromptMode`, `inheritProjectContext`, `inheritSkills`, `defaultContext`,
|
|
250
|
-
`acceptanceRole`, `disabled`, `skills`, `tools`, and `systemPrompt`.
|
|
251
|
-
`acceptanceRole: false` to clear an override. Create a user or project
|
|
264
|
+
`acceptanceRole`, `disabled`, `skills`, `tools`, `extensions`, and `systemPrompt`.
|
|
265
|
+
Use `acceptanceRole: false` to clear an override. Create a user or project
|
|
252
266
|
agent with the same name only when you want a substantially different agent.
|
|
253
267
|
|
|
254
268
|
If a provider rejects model IDs with thinking suffixes, use
|
|
255
269
|
`subagents.disableThinking: true` in user or project settings to clear bundled
|
|
256
270
|
builtin thinking defaults globally. A higher-precedence per-agent `thinking`
|
|
257
|
-
override can opt one builtin back in.
|
|
271
|
+
override can opt one builtin back in. Existing custom-agent frontmatter remains authoritative.
|
|
272
|
+
|
|
273
|
+
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. Explicit custom-agent frontmatter still wins.
|
|
258
274
|
|
|
259
275
|
Tool description modes live in `~/.pi/agent/extensions/subagent/config.json`, not `subagents` settings. Set `toolDescriptionMode` to `compact` to reduce tool-description prompt cost while keeping the execution, async/`subagent_wait`, child-safety, one-writer, management/action, and artifact/status guardrails. Set it to `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.
|
|
260
276
|
|
|
@@ -860,7 +876,7 @@ clarify → validation contract → planner → async worker → parallel async
|
|
|
860
876
|
|
|
861
877
|
The validation contract defines acceptance before code is written: expected behavior, acceptance checks, commands or user flows to exercise, and evidence the worker should return. Keep it lightweight for small tasks, but make it explicit enough that reviewers and validators are checking the intended outcome rather than the worker’s own assumptions.
|
|
862
878
|
|
|
863
|
-
Use the structured `acceptance` field when the run should carry an explicit acceptance contract. If omitted, subagents infer an effective
|
|
879
|
+
Use the structured `acceptance` field when the run should carry an explicit acceptance contract. If omitted, subagents infer an effective policy from role, mode, and risk. Evidence levels end at `verified`: use `level: "checked"` for ordinary writer evidence and `level: "verified"` when the runtime should run explicit validation commands. Independent review is orthogonal; use `review: { required: true, agent: "reviewer" }` and orchestrate the reviewer separately. `review-required` means evidence passed but review is pending, while `reviewed` means a real independent result found no blockers. For reviewer/read-only calls, omit `acceptance`. Never explicitly request `level: "reviewed"`; that value remains recognized only so preflight can return an actionable correction. To disable gates, use `{ level: "none", reason: "..." }`; the bare string `"none"` is rejected, and `false` is accepted only as a deprecated shorthand. Child-reported command success is evidence, not runtime verification.
|
|
864
880
|
|
|
865
881
|
The first `worker` implements the approved plan. The parent continues with independent inspection or validation prep while it runs, not parallel edits to the same worktree. When the async worker completes, treat its handoff as the transition into review, not as final completion, unless the user explicitly asked for worker-only work, review-only output, or to stop after implementation. Parallel reviewers inspect the resulting diff from fresh context. Validators check behavior with the best available evidence: commands, tests, browser/CLI interaction, screenshots, logs, or manual reproduction notes. The final `worker` applies synthesized review fixes in forked context, then the parent looks over the final diff before completing. The parent may launch these steps as an initial async chain when the workflow is already clear, or as follow-up subagent runs after each async completion. Initial chains should pass `async: true` so the main chat is unblocked; avoid `clarify: true` unless the user asked for foreground clarification. Do not stop after parallel review unless the user explicitly asked for review-only output or the review surfaced a decision that needs approval first.
|
|
866
882
|
|
|
@@ -203,7 +203,12 @@ function skillsWarning(cwd: string, agent: Pick<AgentConfig, "skills" | "skillPa
|
|
|
203
203
|
|
|
204
204
|
export function editableAgentConfig(agent: AgentConfig): AgentConfig {
|
|
205
205
|
const base = agent.override?.base;
|
|
206
|
-
if (!base)
|
|
206
|
+
if (!base) {
|
|
207
|
+
return {
|
|
208
|
+
...agent,
|
|
209
|
+
extensions: agent.extensionsFromDefault ? undefined : agent.extensions ? [...agent.extensions] : undefined,
|
|
210
|
+
};
|
|
211
|
+
}
|
|
207
212
|
|
|
208
213
|
return {
|
|
209
214
|
...agent,
|
|
@@ -221,6 +226,7 @@ export function editableAgentConfig(agent: AgentConfig): AgentConfig {
|
|
|
221
226
|
skillPath: base.skillPath ? [...base.skillPath] : undefined,
|
|
222
227
|
tools: base.tools ? [...base.tools] : undefined,
|
|
223
228
|
mcpDirectTools: base.mcpDirectTools ? [...base.mcpDirectTools] : undefined,
|
|
229
|
+
extensions: base.extensions ? [...base.extensions] : undefined,
|
|
224
230
|
subagentOnlyExtensions: base.subagentOnlyExtensions ? [...base.subagentOnlyExtensions] : undefined,
|
|
225
231
|
completionGuard: base.completionGuard,
|
|
226
232
|
override: undefined,
|
package/src/agents/agents.ts
CHANGED
|
@@ -73,6 +73,7 @@ export interface BuiltinAgentOverrideBase {
|
|
|
73
73
|
skillPath?: string[];
|
|
74
74
|
tools?: string[];
|
|
75
75
|
mcpDirectTools?: string[];
|
|
76
|
+
extensions?: string[];
|
|
76
77
|
subagentOnlyExtensions?: string[];
|
|
77
78
|
completionGuard?: boolean;
|
|
78
79
|
toolBudget?: ToolBudgetConfig;
|
|
@@ -91,6 +92,7 @@ interface BuiltinAgentOverrideConfig {
|
|
|
91
92
|
systemPrompt?: string;
|
|
92
93
|
skills?: string[] | false;
|
|
93
94
|
tools?: string[] | false;
|
|
95
|
+
extensions?: string[] | false;
|
|
94
96
|
subagentOnlyExtensions?: string[] | false;
|
|
95
97
|
completionGuard?: boolean;
|
|
96
98
|
toolBudget?: ToolBudgetConfig | false;
|
|
@@ -134,6 +136,7 @@ export interface AgentConfig {
|
|
|
134
136
|
skills?: string[];
|
|
135
137
|
skillPath?: string[];
|
|
136
138
|
extensions?: string[];
|
|
139
|
+
extensionsFromDefault?: boolean;
|
|
137
140
|
subagentOnlyExtensions?: string[];
|
|
138
141
|
output?: string;
|
|
139
142
|
defaultReads?: string[];
|
|
@@ -152,6 +155,8 @@ export interface AgentConfig {
|
|
|
152
155
|
interface SubagentSettings {
|
|
153
156
|
overrides: Record<string, BuiltinAgentOverrideConfig>;
|
|
154
157
|
defaultModel?: string;
|
|
158
|
+
defaultThinking?: string;
|
|
159
|
+
defaultExtensions?: string[];
|
|
155
160
|
disableBuiltins?: boolean;
|
|
156
161
|
disableThinking?: boolean;
|
|
157
162
|
modelScope?: ModelScopeConfig;
|
|
@@ -516,6 +521,7 @@ function cloneOverrideBase(agent: AgentConfig): BuiltinAgentOverrideBase {
|
|
|
516
521
|
skillPath: agent.skillPath ? [...agent.skillPath] : undefined,
|
|
517
522
|
tools: agent.tools ? [...agent.tools] : undefined,
|
|
518
523
|
mcpDirectTools: agent.mcpDirectTools ? [...agent.mcpDirectTools] : undefined,
|
|
524
|
+
extensions: agent.extensionsFromDefault ? undefined : agent.extensions ? [...agent.extensions] : undefined,
|
|
519
525
|
subagentOnlyExtensions: agent.subagentOnlyExtensions ? [...agent.subagentOnlyExtensions] : undefined,
|
|
520
526
|
completionGuard: agent.completionGuard,
|
|
521
527
|
toolBudget: agent.toolBudget,
|
|
@@ -538,6 +544,7 @@ function cloneOverrideValue(override: BuiltinAgentOverrideConfig): BuiltinAgentO
|
|
|
538
544
|
...(override.systemPrompt !== undefined ? { systemPrompt: override.systemPrompt } : {}),
|
|
539
545
|
...(override.skills !== undefined ? { skills: override.skills === false ? false : [...override.skills] } : {}),
|
|
540
546
|
...(override.tools !== undefined ? { tools: override.tools === false ? false : [...override.tools] } : {}),
|
|
547
|
+
...(override.extensions !== undefined ? { extensions: override.extensions === false ? false : [...override.extensions] } : {}),
|
|
541
548
|
...(override.subagentOnlyExtensions !== undefined ? { subagentOnlyExtensions: override.subagentOnlyExtensions === false ? false : [...override.subagentOnlyExtensions] } : {}),
|
|
542
549
|
...(override.completionGuard !== undefined ? { completionGuard: override.completionGuard } : {}),
|
|
543
550
|
...(override.toolBudget !== undefined ? { toolBudget: override.toolBudget === false ? false : { ...override.toolBudget, ...(Array.isArray(override.toolBudget.block) ? { block: [...override.toolBudget.block] } : {}) } } : {}),
|
|
@@ -717,6 +724,9 @@ function parseBuiltinOverrideEntry(
|
|
|
717
724
|
const tools = parseOverrideStringArrayOrFalse(input.tools, { filePath, name, field: "tools" });
|
|
718
725
|
if (tools !== undefined) override.tools = tools;
|
|
719
726
|
|
|
727
|
+
const extensions = parseOverrideStringArrayOrFalse(input.extensions, { filePath, name, field: "extensions" });
|
|
728
|
+
if (extensions !== undefined) override.extensions = extensions;
|
|
729
|
+
|
|
720
730
|
const subagentOnlyExtensions = parseOverrideStringArrayOrFalse(input.subagentOnlyExtensions, { filePath, name, field: "subagentOnlyExtensions" });
|
|
721
731
|
if (subagentOnlyExtensions !== undefined) override.subagentOnlyExtensions = subagentOnlyExtensions;
|
|
722
732
|
|
|
@@ -754,18 +764,34 @@ function readSubagentSettings(filePath: string | null): SubagentSettings {
|
|
|
754
764
|
throw new Error(`Subagent settings in '${filePath}' have invalid 'defaultModel'; expected a non-empty string.`);
|
|
755
765
|
}
|
|
756
766
|
}
|
|
767
|
+
let defaultThinking: string | undefined;
|
|
768
|
+
if ("defaultThinking" in subagentsObject) {
|
|
769
|
+
if (typeof subagentsObject.defaultThinking === "string" && subagentsObject.defaultThinking.trim()) {
|
|
770
|
+
defaultThinking = subagentsObject.defaultThinking.trim();
|
|
771
|
+
} else {
|
|
772
|
+
throw new Error(`Subagent settings in '${filePath}' have invalid 'defaultThinking'; expected a non-empty string.`);
|
|
773
|
+
}
|
|
774
|
+
}
|
|
775
|
+
let defaultExtensions: string[] | undefined;
|
|
776
|
+
if ("defaultExtensions" in subagentsObject) {
|
|
777
|
+
if (!Array.isArray(subagentsObject.defaultExtensions)
|
|
778
|
+
|| subagentsObject.defaultExtensions.some((item) => typeof item !== "string" || !item.trim())) {
|
|
779
|
+
throw new Error(`Subagent settings in '${filePath}' have invalid 'defaultExtensions'; expected an array of non-empty strings.`);
|
|
780
|
+
}
|
|
781
|
+
defaultExtensions = subagentsObject.defaultExtensions.map((item) => item.trim());
|
|
782
|
+
}
|
|
757
783
|
const modelScope = parseModelScopeConfig(subagentsObject.modelScope, { filePath });
|
|
758
784
|
|
|
759
785
|
const parsed: Record<string, BuiltinAgentOverrideConfig> = {};
|
|
760
786
|
const agentOverrides = subagentsObject.agentOverrides;
|
|
761
787
|
if (!agentOverrides || typeof agentOverrides !== "object" || Array.isArray(agentOverrides)) {
|
|
762
|
-
return { overrides: parsed, defaultModel, disableBuiltins, disableThinking, modelScope };
|
|
788
|
+
return { overrides: parsed, defaultModel, defaultThinking, defaultExtensions, disableBuiltins, disableThinking, modelScope };
|
|
763
789
|
}
|
|
764
790
|
for (const [name, value] of Object.entries(agentOverrides)) {
|
|
765
791
|
const override = parseBuiltinOverrideEntry(name, value, filePath);
|
|
766
792
|
if (override) parsed[name] = override;
|
|
767
793
|
}
|
|
768
|
-
return { overrides: parsed, defaultModel, disableBuiltins, disableThinking, modelScope };
|
|
794
|
+
return { overrides: parsed, defaultModel, defaultThinking, defaultExtensions, disableBuiltins, disableThinking, modelScope };
|
|
769
795
|
}
|
|
770
796
|
|
|
771
797
|
function resolveSubagentDefaultModel(
|
|
@@ -793,6 +819,58 @@ function applySubagentDefaultModel(agents: AgentConfig[], defaultModel: AgentMod
|
|
|
793
819
|
});
|
|
794
820
|
}
|
|
795
821
|
|
|
822
|
+
function resolveSubagentDefaultThinking(
|
|
823
|
+
userSettings: SubagentSettings,
|
|
824
|
+
projectSettings: SubagentSettings,
|
|
825
|
+
projectSettingsPath: string | null,
|
|
826
|
+
): string | undefined {
|
|
827
|
+
if (projectSettingsPath && projectSettings.defaultThinking !== undefined) return projectSettings.defaultThinking;
|
|
828
|
+
return userSettings.defaultThinking;
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
function applySubagentDefaultThinking(agents: AgentConfig[], defaultThinking: string | undefined): AgentConfig[] {
|
|
832
|
+
if (defaultThinking === undefined) return agents;
|
|
833
|
+
return agents.map((agent) => {
|
|
834
|
+
if (agent.thinking !== undefined) return agent;
|
|
835
|
+
const next = { ...agent, thinking: defaultThinking };
|
|
836
|
+
const frontmatterFields = agentFrontmatterFields.get(agent);
|
|
837
|
+
if (frontmatterFields) agentFrontmatterFields.set(next, frontmatterFields);
|
|
838
|
+
return next;
|
|
839
|
+
});
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
function resolveSubagentDefaultExtensions(
|
|
843
|
+
userSettings: SubagentSettings,
|
|
844
|
+
projectSettings: SubagentSettings,
|
|
845
|
+
projectSettingsPath: string | null,
|
|
846
|
+
): string[] | undefined {
|
|
847
|
+
if (projectSettingsPath && projectSettings.defaultExtensions !== undefined) return projectSettings.defaultExtensions;
|
|
848
|
+
return userSettings.defaultExtensions;
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
function applySubagentDefaultExtensions(agents: AgentConfig[], defaultExtensions: string[] | undefined): AgentConfig[] {
|
|
852
|
+
if (defaultExtensions === undefined) return agents;
|
|
853
|
+
return agents.map((agent) => {
|
|
854
|
+
if (agent.extensions !== undefined) return agent;
|
|
855
|
+
const next = { ...agent, extensions: [...defaultExtensions], extensionsFromDefault: true };
|
|
856
|
+
const frontmatterFields = agentFrontmatterFields.get(agent);
|
|
857
|
+
if (frontmatterFields) agentFrontmatterFields.set(next, frontmatterFields);
|
|
858
|
+
return next;
|
|
859
|
+
});
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
function applySubagentDefaults(
|
|
863
|
+
agents: AgentConfig[],
|
|
864
|
+
defaultModel: AgentModelSourceInfo | undefined,
|
|
865
|
+
defaultThinking: string | undefined,
|
|
866
|
+
defaultExtensions: string[] | undefined,
|
|
867
|
+
): AgentConfig[] {
|
|
868
|
+
return applySubagentDefaultExtensions(
|
|
869
|
+
applySubagentDefaultThinking(applySubagentDefaultModel(agents, defaultModel), defaultThinking),
|
|
870
|
+
defaultExtensions,
|
|
871
|
+
);
|
|
872
|
+
}
|
|
873
|
+
|
|
796
874
|
function applyBuiltinOverride(
|
|
797
875
|
agent: AgentConfig,
|
|
798
876
|
override: BuiltinAgentOverrideConfig,
|
|
@@ -821,6 +899,7 @@ function applyBuiltinOverride(
|
|
|
821
899
|
next.tools = tools;
|
|
822
900
|
next.mcpDirectTools = mcpDirectTools;
|
|
823
901
|
}
|
|
902
|
+
if (override.extensions !== undefined) next.extensions = override.extensions === false ? undefined : [...override.extensions];
|
|
824
903
|
if (override.subagentOnlyExtensions !== undefined) {
|
|
825
904
|
next.subagentOnlyExtensions = override.subagentOnlyExtensions === false ? undefined : [...override.subagentOnlyExtensions];
|
|
826
905
|
}
|
|
@@ -964,6 +1043,9 @@ function applyCustomAgentOverride(
|
|
|
964
1043
|
target.mcpDirectTools = mcpDirectTools;
|
|
965
1044
|
anyFilled = true;
|
|
966
1045
|
}
|
|
1046
|
+
if (override.extensions !== undefined) {
|
|
1047
|
+
fill("extensions", ["extensions"], override.extensions === false ? undefined : [...override.extensions]);
|
|
1048
|
+
}
|
|
967
1049
|
if (override.subagentOnlyExtensions !== undefined) {
|
|
968
1050
|
fill(
|
|
969
1051
|
"subagentOnlyExtensions",
|
|
@@ -1009,7 +1091,7 @@ function applyCustomAgentOverrides(
|
|
|
1009
1091
|
|
|
1010
1092
|
export function buildBuiltinOverrideConfig(
|
|
1011
1093
|
base: BuiltinAgentOverrideBase,
|
|
1012
|
-
draft: Pick<AgentConfig, "model" | "fallbackModels" | "thinking" | "systemPromptMode" | "inheritProjectContext" | "inheritSkills" | "defaultContext" | "acceptanceRole" | "disabled" | "systemPrompt" | "skills" | "tools" | "mcpDirectTools" | "subagentOnlyExtensions" | "completionGuard" | "toolBudget">,
|
|
1094
|
+
draft: Pick<AgentConfig, "model" | "fallbackModels" | "thinking" | "systemPromptMode" | "inheritProjectContext" | "inheritSkills" | "defaultContext" | "acceptanceRole" | "disabled" | "systemPrompt" | "skills" | "tools" | "mcpDirectTools" | "extensions" | "subagentOnlyExtensions" | "completionGuard" | "toolBudget">,
|
|
1013
1095
|
): BuiltinAgentOverrideConfig | undefined {
|
|
1014
1096
|
const override: BuiltinAgentOverrideConfig = {};
|
|
1015
1097
|
|
|
@@ -1028,6 +1110,7 @@ export function buildBuiltinOverrideConfig(
|
|
|
1028
1110
|
const baseTools = joinToolList(base);
|
|
1029
1111
|
const draftTools = joinToolList(draft);
|
|
1030
1112
|
if (!arraysEqual(draftTools, baseTools)) override.tools = draftTools ? [...draftTools] : false;
|
|
1113
|
+
if (!arraysEqual(draft.extensions, base.extensions)) override.extensions = draft.extensions ? [...draft.extensions] : false;
|
|
1031
1114
|
if (!arraysEqual(draft.subagentOnlyExtensions, base.subagentOnlyExtensions)) {
|
|
1032
1115
|
override.subagentOnlyExtensions = draft.subagentOnlyExtensions ? [...draft.subagentOnlyExtensions] : false;
|
|
1033
1116
|
}
|
|
@@ -1460,6 +1543,8 @@ export function discoverAgents(cwd: string, scope: AgentScope): AgentDiscoveryRe
|
|
|
1460
1543
|
const userSettings = scope === "project" ? EMPTY_SUBAGENT_SETTINGS : readSubagentSettings(userSettingsPath);
|
|
1461
1544
|
const projectSettings = scope === "user" ? EMPTY_SUBAGENT_SETTINGS : readSubagentSettings(projectSettingsPath);
|
|
1462
1545
|
const defaultModel = resolveSubagentDefaultModel(userSettings, projectSettings, userSettingsPath, projectSettingsPath);
|
|
1546
|
+
const defaultThinking = resolveSubagentDefaultThinking(userSettings, projectSettings, projectSettingsPath);
|
|
1547
|
+
const defaultExtensions = resolveSubagentDefaultExtensions(userSettings, projectSettings, projectSettingsPath);
|
|
1463
1548
|
const modelScope = projectSettings.modelScope ?? userSettings.modelScope;
|
|
1464
1549
|
const packageSubagentPaths = collectPackageSubagentPaths(cwd, {
|
|
1465
1550
|
includeUser: scope !== "project",
|
|
@@ -1467,7 +1552,7 @@ export function discoverAgents(cwd: string, scope: AgentScope): AgentDiscoveryRe
|
|
|
1467
1552
|
});
|
|
1468
1553
|
|
|
1469
1554
|
const builtinAgents = applyBuiltinOverrides(
|
|
1470
|
-
|
|
1555
|
+
applySubagentDefaults(loadAgentsFromDir(BUILTIN_AGENTS_DIR, "builtin"), defaultModel, defaultThinking, defaultExtensions),
|
|
1471
1556
|
userSettings,
|
|
1472
1557
|
projectSettings,
|
|
1473
1558
|
userSettingsPath,
|
|
@@ -1478,7 +1563,7 @@ export function discoverAgents(cwd: string, scope: AgentScope): AgentDiscoveryRe
|
|
|
1478
1563
|
const userAgentsOld = scope === "project" ? [] : loadAgentsFromDir(userDirOld, "user");
|
|
1479
1564
|
const userAgentsNew = scope === "project" ? [] : loadAgentsFromDir(userDirNew, "user");
|
|
1480
1565
|
const userAgents = applyCustomAgentOverrides(
|
|
1481
|
-
|
|
1566
|
+
applySubagentDefaults([...userAgentsExtra, ...userAgentsOld, ...userAgentsNew], defaultModel, defaultThinking, defaultExtensions),
|
|
1482
1567
|
userSettings,
|
|
1483
1568
|
projectSettings,
|
|
1484
1569
|
userSettingsPath,
|
|
@@ -1486,14 +1571,14 @@ export function discoverAgents(cwd: string, scope: AgentScope): AgentDiscoveryRe
|
|
|
1486
1571
|
);
|
|
1487
1572
|
|
|
1488
1573
|
const projectAgents = applyCustomAgentOverrides(
|
|
1489
|
-
|
|
1574
|
+
applySubagentDefaults(scope === "user" ? [] : projectAgentDirs.flatMap((dir) => loadAgentsFromDir(dir, "project")), defaultModel, defaultThinking, defaultExtensions),
|
|
1490
1575
|
userSettings,
|
|
1491
1576
|
projectSettings,
|
|
1492
1577
|
userSettingsPath,
|
|
1493
1578
|
projectSettingsPath,
|
|
1494
1579
|
);
|
|
1495
1580
|
const packageAgents = applyCustomAgentOverrides(
|
|
1496
|
-
|
|
1581
|
+
applySubagentDefaults(packageSubagentPaths.agents.flatMap((dir) => loadAgentsFromDir(dir, "package")), defaultModel, defaultThinking, defaultExtensions),
|
|
1497
1582
|
userSettings,
|
|
1498
1583
|
projectSettings,
|
|
1499
1584
|
userSettingsPath,
|
|
@@ -1529,21 +1614,23 @@ export function discoverAgentsAll(cwd: string): {
|
|
|
1529
1614
|
const userSettings = readSubagentSettings(userSettingsPath);
|
|
1530
1615
|
const projectSettings = readSubagentSettings(projectSettingsPath);
|
|
1531
1616
|
const defaultModel = resolveSubagentDefaultModel(userSettings, projectSettings, userSettingsPath, projectSettingsPath);
|
|
1617
|
+
const defaultThinking = resolveSubagentDefaultThinking(userSettings, projectSettings, projectSettingsPath);
|
|
1618
|
+
const defaultExtensions = resolveSubagentDefaultExtensions(userSettings, projectSettings, projectSettingsPath);
|
|
1532
1619
|
const packageSubagentPaths = collectPackageSubagentPaths(cwd);
|
|
1533
1620
|
|
|
1534
1621
|
const builtin = applyBuiltinOverrides(
|
|
1535
|
-
|
|
1622
|
+
applySubagentDefaults(loadAgentsFromDir(BUILTIN_AGENTS_DIR, "builtin"), defaultModel, defaultThinking, defaultExtensions),
|
|
1536
1623
|
userSettings,
|
|
1537
1624
|
projectSettings,
|
|
1538
1625
|
userSettingsPath,
|
|
1539
1626
|
projectSettingsPath,
|
|
1540
1627
|
);
|
|
1541
1628
|
const user = applyCustomAgentOverrides(
|
|
1542
|
-
|
|
1629
|
+
applySubagentDefaults([
|
|
1543
1630
|
...extraUserAgentDirs().flatMap((dir) => loadAgentsFromDir(dir, "user")),
|
|
1544
1631
|
...loadAgentsFromDir(userDirOld, "user"),
|
|
1545
1632
|
...loadAgentsFromDir(userDirNew, "user"),
|
|
1546
|
-
], defaultModel),
|
|
1633
|
+
], defaultModel, defaultThinking, defaultExtensions),
|
|
1547
1634
|
userSettings,
|
|
1548
1635
|
projectSettings,
|
|
1549
1636
|
userSettingsPath,
|
|
@@ -1556,7 +1643,7 @@ export function discoverAgentsAll(cwd: string): {
|
|
|
1556
1643
|
}
|
|
1557
1644
|
}
|
|
1558
1645
|
const packageAgents = applyCustomAgentOverrides(
|
|
1559
|
-
|
|
1646
|
+
applySubagentDefaults(Array.from(packageMap.values()), defaultModel, defaultThinking, defaultExtensions),
|
|
1560
1647
|
userSettings,
|
|
1561
1648
|
projectSettings,
|
|
1562
1649
|
userSettingsPath,
|
|
@@ -1569,7 +1656,7 @@ export function discoverAgentsAll(cwd: string): {
|
|
|
1569
1656
|
}
|
|
1570
1657
|
}
|
|
1571
1658
|
const project = applyCustomAgentOverrides(
|
|
1572
|
-
|
|
1659
|
+
applySubagentDefaults(Array.from(projectMap.values()), defaultModel, defaultThinking, defaultExtensions),
|
|
1573
1660
|
userSettings,
|
|
1574
1661
|
projectSettings,
|
|
1575
1662
|
userSettingsPath,
|