pi-subagents 0.28.0 → 0.30.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 +31 -0
- package/README.md +26 -62
- package/package.json +1 -1
- package/skills/pi-subagents/SKILL.md +29 -35
- package/src/agents/agent-management.ts +29 -22
- package/src/agents/agent-selection.ts +2 -0
- package/src/agents/agent-serializer.ts +5 -10
- package/src/agents/agents.ts +339 -47
- package/src/agents/chain-serializer.ts +4 -9
- package/src/agents/proactive-skills.ts +191 -0
- package/src/extension/doctor.ts +4 -3
- package/src/extension/fanout-child.ts +1 -3
- package/src/extension/index.ts +6 -9
- package/src/extension/schemas.ts +63 -26
- package/src/intercom/intercom-bridge.ts +11 -1
- package/src/intercom/result-intercom.ts +0 -5
- package/src/runs/background/async-execution.ts +186 -74
- package/src/runs/background/async-resume.ts +53 -5
- package/src/runs/background/async-status.ts +4 -1
- package/src/runs/background/chain-append.ts +282 -0
- package/src/runs/background/chain-root-attachment.ts +161 -0
- package/src/runs/background/run-status.ts +2 -7
- package/src/runs/background/subagent-runner.ts +160 -219
- package/src/runs/foreground/chain-execution.ts +62 -58
- package/src/runs/foreground/execution.ts +39 -343
- package/src/runs/foreground/subagent-executor.ts +316 -111
- package/src/runs/shared/acceptance.ts +605 -22
- package/src/runs/shared/chain-outputs.ts +23 -8
- package/src/runs/shared/completion-guard.ts +3 -26
- package/src/runs/shared/dynamic-fanout.ts +1 -1
- package/src/runs/shared/model-fallback.ts +38 -0
- package/src/runs/shared/parallel-utils.ts +13 -10
- package/src/runs/shared/pi-args.ts +3 -2
- package/src/runs/shared/subagent-control.ts +8 -11
- package/src/runs/shared/subagent-prompt-runtime.ts +3 -2
- package/src/runs/shared/workflow-graph.ts +2 -6
- package/src/shared/atomic-json.ts +68 -11
- package/src/shared/settings.ts +1 -0
- package/src/shared/types.ts +20 -49
- package/src/shared/utils.ts +2 -8
- package/src/slash/slash-bridge.ts +3 -1
- package/src/slash/slash-commands.ts +1 -1
- package/src/tui/render.ts +14 -29
- package/src/runs/shared/acceptance-contract.ts +0 -318
- package/src/runs/shared/acceptance-evaluation.ts +0 -221
- package/src/runs/shared/acceptance-finalization.ts +0 -173
- package/src/runs/shared/acceptance-reports.ts +0 -127
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,37 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [0.30.0] - 2026-06-20
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
- Allow active async chains to accept an `append-step` request that adds one new tail step while the chain is still running.
|
|
9
|
+
- Allow async subagent results to be attached as the root step of a new follow-up chain.
|
|
10
|
+
- Added `subagentOnlyExtensions` so agents can pass selected tool extensions only to spawned subagents without exposing them to the parent agent.
|
|
11
|
+
- Added proactive skill-subagent suggestions to `subagent({ action: "list" })` based on repeatedly configured skill use, while keeping the behavior advisory and opt-out friendly.
|
|
12
|
+
- Added regression coverage for long worker/reviewer chains and parallel -> funnel -> fanout chain flows across foreground and async execution.
|
|
13
|
+
|
|
14
|
+
### Fixed
|
|
15
|
+
- Interrupt live async children before delivering `resume` follow-up messages so intercom nudges reach workers that are stuck mid-turn more reliably.
|
|
16
|
+
- Reject appended chain steps with duplicate reserved output names or unknown named-output references before they are queued.
|
|
17
|
+
- Ignore legacy `.agents/skills` files during agent discovery so skill definitions are not registered as subagents. Thanks to chyax98 (@chyax98) for #257.
|
|
18
|
+
- Launch detached async runners through Node when Pi itself is not the Node executable. Thanks to Tetsuya.dev (@tetsuya-dev-jp) for #273.
|
|
19
|
+
- Preserve the slash command requester context when bridge requests launch subagents. Thanks to Victor Sumner (@vsumner) for #268.
|
|
20
|
+
- Trim repeated nested `subagent` tool schema descriptions so provider payloads stay compact while retaining top-level parameter guidance. Thanks to Thomas Mustier (@tmustier) for #250.
|
|
21
|
+
|
|
22
|
+
## [0.29.0] - 2026-06-19
|
|
23
|
+
|
|
24
|
+
### Added
|
|
25
|
+
- Added package-provided agent and chain discovery from installed Pi packages and package settings, including read-only management behavior, package source counts in doctor output, nested-cwd project package discovery, and package definitions that remain below user/project overrides. Thanks to Fabian Jocks (@iamfj) for #278.
|
|
26
|
+
- Added `PI_SUBAGENT_EXTRA_AGENT_DIRS` and `PI_INTERCOM_EXTENSION_DIR` overrides so bundled agents and `pi-intercom` can be loaded from read-only package locations. Thanks to David Barroso (@dbarrosop) for #288.
|
|
27
|
+
|
|
28
|
+
### Fixed
|
|
29
|
+
- Show captured output from failed foreground subagents instead of returning only the failure summary. Thanks to Jürgen Schmied (@jschmied) for #277.
|
|
30
|
+
- Preserve nested fanout child subagent history when building child prompts. Thanks to James Wood (@jamesjwood) for the original #270 fix.
|
|
31
|
+
- Retry Windows atomic JSON renames on transient `EPERM`, `EBUSY`, and `EACCES` failures. Thanks to Wings Butterfly (@wings1848) for #269.
|
|
32
|
+
- Inherit the parent session model for subagents instead of falling back to global settings, including foreground, chain, async chain, async single, and resume/revive paths. Thanks to Rogerio Saulo (@rsaulo) for #266 and Nicolas Marchildon (@elecnix) for the original #283 fix.
|
|
33
|
+
- Avoid duplicate `subagent` tool registration in fanout-authorized child processes. Thanks to Aleksei Gurianov (@Guria) for #279.
|
|
34
|
+
- Hardened the parallel intercom integration test fixture after Windows CI exposed nondeterministic failure ordering.
|
|
35
|
+
|
|
5
36
|
## [0.28.0] - 2026-06-03
|
|
6
37
|
|
|
7
38
|
### Added
|
package/README.md
CHANGED
|
@@ -145,7 +145,7 @@ Use `~/.pi/agent/settings.json` for a user override or `.pi/settings.json` for a
|
|
|
145
145
|
|
|
146
146
|
## Where running subagents show up
|
|
147
147
|
|
|
148
|
-
Foreground runs stream progress in the conversation while they run.
|
|
148
|
+
Foreground runs stream progress in the conversation while they run.
|
|
149
149
|
|
|
150
150
|
Background runs keep working after control returns to you. Inspect active runs with `subagent({ action: "status" })`, or a specific run with `subagent({ action: "status", id: "..." })`.
|
|
151
151
|
|
|
@@ -358,10 +358,11 @@ Agent locations, lowest to highest priority:
|
|
|
358
358
|
| Scope | Path |
|
|
359
359
|
|-------|------|
|
|
360
360
|
| Builtin | `~/.pi/agent/extensions/subagent/agents/` |
|
|
361
|
+
| Installed package | `package.json` `pi-subagents.agents` or `pi.subagents.agents` |
|
|
361
362
|
| User | `~/.pi/agent/agents/**/*.md` |
|
|
362
363
|
| Project | `.pi/agents/**/*.md` |
|
|
363
364
|
|
|
364
|
-
Project discovery also reads legacy `.agents/**/*.md` files. Nested subdirectories are discovered recursively. `.chain.md` files do not define agents. If both `.agents/` and `.pi/agents/` define the same parsed runtime agent name, `.pi/agents/` wins. Use `agentScope: "user" | "project" | "both"` to control discovery; `both` is the default and project definitions win runtime-name collisions.
|
|
365
|
+
Project discovery also reads legacy `.agents/**/*.md` files. Nested subdirectories are discovered recursively. `.chain.md` files do not define agents. 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. If both `.agents/` and `.pi/agents/` define the same parsed runtime agent name, `.pi/agents/` wins. Use `agentScope: "user" | "project" | "both"` to control discovery; `both` is the default and project definitions win runtime-name collisions.
|
|
365
366
|
|
|
366
367
|
Builtin agents load at the lowest priority, so a user or project agent with the same name overrides them. They do not pin a provider model; they inherit your current Pi default model unless you set `subagents.agentOverrides.<name>.model`. `oracle` is an advisory reviewer that critiques direction and proposes an execution prompt without editing files. `worker` is the implementation agent for normal tasks and approved oracle handoffs.
|
|
367
368
|
|
|
@@ -423,6 +424,7 @@ package: code-analysis
|
|
|
423
424
|
description: Fast codebase recon
|
|
424
425
|
tools: read, grep, find, ls, bash, mcp:chrome-devtools
|
|
425
426
|
extensions:
|
|
427
|
+
subagentOnlyExtensions: ./tools/child-only-search.ts
|
|
426
428
|
model: claude-haiku-4-5
|
|
427
429
|
fallbackModels: openai/gpt-5-mini, anthropic/claude-sonnet-4
|
|
428
430
|
thinking: high
|
|
@@ -436,8 +438,6 @@ defaultProgress: true
|
|
|
436
438
|
completionGuard: false
|
|
437
439
|
interactive: true
|
|
438
440
|
maxSubagentDepth: 1
|
|
439
|
-
maxExecutionTimeMs: 600000
|
|
440
|
-
maxTokens: 50000
|
|
441
441
|
---
|
|
442
442
|
|
|
443
443
|
Your system prompt goes here.
|
|
@@ -450,6 +450,7 @@ Important fields:
|
|
|
450
450
|
| `package` | Optional package identifier. A file with `name: scout` and `package: code-analysis` registers as `code-analysis.scout`; serialization keeps `name` and `package` separate. |
|
|
451
451
|
| `tools` | Builtin tool allowlist. `mcp:` entries select direct MCP tools when `pi-mcp-adapter` is installed. |
|
|
452
452
|
| `extensions` | Omitted means normal extensions; empty means no extensions; comma-separated values allowlist specific extensions. |
|
|
453
|
+
| `subagentOnlyExtensions` | Comma-separated 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. |
|
|
453
454
|
| `model` | Default model. Bare ids prefer the current provider when possible, then unique registry matches. |
|
|
454
455
|
| `fallbackModels` | Ordered backup models for provider/model failures such as quota, auth, timeout, or unavailable model. Ordinary task failures do not trigger fallback. |
|
|
455
456
|
| `thinking` | Appended as a `:level` suffix at runtime unless a suffix is already present. |
|
|
@@ -464,8 +465,6 @@ Important fields:
|
|
|
464
465
|
| `completionGuard` | Set `false` only for non-implementation agents that may mention implementation words while using mutation-capable tools such as `bash`. |
|
|
465
466
|
| `interactive` | Parsed for compatibility but not enforced in v1. |
|
|
466
467
|
| `maxSubagentDepth` | Tightens nested delegation for this agent’s children. |
|
|
467
|
-
| `maxExecutionTimeMs` | Stops each foreground or async child run for this agent after the given number of milliseconds. |
|
|
468
|
-
| `maxTokens` | Stops each foreground or async child run for this agent when observed input plus output tokens reach the limit. Token enforcement is best-effort because usage is reported after model events arrive. |
|
|
469
468
|
|
|
470
469
|
### Tool and extension selection
|
|
471
470
|
|
|
@@ -494,16 +493,19 @@ extensions: /abs/path/to/ext-a.ts, /abs/path/to/ext-b.ts
|
|
|
494
493
|
|
|
495
494
|
When `extensions` is present, it takes precedence over extension paths implied by `tools` entries.
|
|
496
495
|
|
|
496
|
+
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.
|
|
497
|
+
|
|
497
498
|
## Chain files
|
|
498
499
|
|
|
499
500
|
Chains are reusable workflows stored separately from agent files. Use `.chain.md` for simple sequential saved chains. Use `.chain.json` when a chain needs dynamic fanout.
|
|
500
501
|
|
|
501
502
|
| Scope | Path |
|
|
502
503
|
|-------|------|
|
|
504
|
+
| Installed package | `package.json` `pi-subagents.chains` or `pi.subagents.chains` |
|
|
503
505
|
| User | `~/.pi/agent/chains/**/*.chain.md`, `~/.pi/agent/chains/**/*.chain.json` |
|
|
504
506
|
| Project | `.pi/chains/**/*.chain.md`, `.pi/chains/**/*.chain.json` |
|
|
505
507
|
|
|
506
|
-
Nested subdirectories are discovered recursively. If both `.chain.md` and `.chain.json` define the same parsed runtime chain name in the same scope, `.chain.json` wins. If user and project scopes define the same parsed runtime chain name, the project chain wins. Chains support the same optional `package` frontmatter as agents; `name: review-flow` plus `package: code-analysis` runs as `code-analysis.review-flow`.
|
|
508
|
+
Nested subdirectories are discovered recursively. Installed Pi packages can expose chain directories from either `{"pi-subagents":{"chains":["./chains"]}}` or `{"pi":{"subagents":{"chains":["./chains"]}}}` in their package manifest. Package chains load below user/project chains. If both `.chain.md` and `.chain.json` define the same parsed runtime chain name in the same scope, `.chain.json` wins. If user and project scopes define the same parsed runtime chain name, the project chain wins. Chains support the same optional `package` frontmatter as agents; `name: review-flow` plus `package: code-analysis` runs as `code-analysis.review-flow`.
|
|
507
509
|
|
|
508
510
|
Example:
|
|
509
511
|
|
|
@@ -788,7 +790,7 @@ Agent definitions are not loaded into context by default. Management actions let
|
|
|
788
790
|
|-------|------|---------|-------------|
|
|
789
791
|
| `agent` | string | - | Agent name for single mode, or target for management actions. |
|
|
790
792
|
| `task` | string | - | Task string for single mode. |
|
|
791
|
-
| `action` | string | - | `list`, `get`, `create`, `update`, `delete`, `status`, `interrupt`, `resume`, or `doctor`. |
|
|
793
|
+
| `action` | string | - | `list`, `get`, `create`, `update`, `delete`, `status`, `interrupt`, `resume`, `append-step`, or `doctor`. |
|
|
792
794
|
| `chainName` | string | - | Chain name for management actions. |
|
|
793
795
|
| `config` | object/string | - | Agent or chain config for create/update. |
|
|
794
796
|
| `output` | `string \| false` | agent default | Override single-agent output file. |
|
|
@@ -797,9 +799,8 @@ Agent definitions are not loaded into context by default. Management actions let
|
|
|
797
799
|
| `model` | string | agent default | Override model. |
|
|
798
800
|
| `tasks` | array | - | Top-level parallel tasks. Supports `agent`, `task`, `cwd`, `count`, `output`, `outputMode`, `reads`, `progress`, `skill`, `model`, and `acceptance`. |
|
|
799
801
|
| `concurrency` | number | config or `4` | Top-level parallel concurrency. |
|
|
800
|
-
| `timeoutMs` / `maxRuntimeMs` | number | - | Foreground wall-clock timeout for single, parallel, and chain runs. Timed-out children return `timedOut: true`; async/background runs reject it. |
|
|
801
802
|
| `worktree` | boolean | false | Create isolated git worktrees for parallel tasks. |
|
|
802
|
-
| `chain` | array | - | Sequential, static parallel, and dynamic fanout chain steps.
|
|
803
|
+
| `chain` | array | - | Sequential, static parallel, and dynamic fanout chain steps. Steps and chain parallel tasks support `phase`, `label`, `as`, `outputSchema`, and `acceptance` in addition to the usual execution fields. Dynamic fanout uses `expand`, one child `parallel` template, and `collect`. With `action: "append-step"`, pass exactly one step to append to a running async chain. |
|
|
803
804
|
| `context` | `fresh \| fork` | agent default or `fresh` | `fork` creates real branched sessions from the parent leaf. Packaged `planner`, `worker`, and `oracle` default to `fork`. |
|
|
804
805
|
| `chainDir` | string | temp chain dir | Persistent directory for chain artifacts. |
|
|
805
806
|
| `clarify` | boolean | true for chains | Show TUI preview/edit flow. |
|
|
@@ -811,7 +812,7 @@ Agent definitions are not loaded into context by default. Management actions let
|
|
|
811
812
|
| `includeProgress` | boolean | false | Include full progress in result. |
|
|
812
813
|
| `share` | boolean | false | Upload session export to GitHub Gist. |
|
|
813
814
|
| `sessionDir` | string | derived | Override session log directory. |
|
|
814
|
-
| `acceptance` | object |
|
|
815
|
+
| `acceptance` | string/object/false | inferred | Override the run's inferred acceptance gates. Use `"auto"`, `"attested"`, `"checked"`, `"verified"`, `"reviewed"`, or `{ level: "none", reason: "..." }`. |
|
|
815
816
|
|
|
816
817
|
`context: "fork"` fails fast when the parent session is not persisted, the current leaf is missing, or the branched child session cannot be created. It never silently downgrades to `fresh`. In multi-agent runs, if any requested agent has `defaultContext: fork` and the launch omits `context`, the whole invocation uses forked context; pass `context: "fresh"` when you intentionally want a fresh run.
|
|
817
818
|
|
|
@@ -830,6 +831,7 @@ subagent({ action: "interrupt", id: "<nested-run-id>" })
|
|
|
830
831
|
subagent({ action: "resume", id: "<run-id>", message: "follow-up question" })
|
|
831
832
|
subagent({ action: "resume", id: "<run-id>", index: 1, message: "follow-up for child 2" })
|
|
832
833
|
subagent({ action: "resume", id: "<nested-run-id>", message: "follow-up for a nested child" })
|
|
834
|
+
subagent({ action: "append-step", id: "<run-id>", chain: [{ agent: "worker", task: "Continue from {previous}" }] })
|
|
833
835
|
subagent({ action: "doctor" })
|
|
834
836
|
```
|
|
835
837
|
|
|
@@ -837,6 +839,8 @@ subagent({ action: "doctor" })
|
|
|
837
839
|
|
|
838
840
|
`resume` sends the follow-up directly when an async child is still reachable over intercom. After completion, it revives the child by starting a new async child from the stored child session file. Multi-child async runs and remembered foreground single, parallel, or chain runs can be revived by passing `index` to choose the child. Nested runs can be resumed by nested id when their live route or persisted session metadata is available. Revive starts a new child process from the old session context; it does not restart the same OS process, and it requires the chosen child to have a persisted `.jsonl` session file.
|
|
839
841
|
|
|
842
|
+
`append-step` accepts exactly one sequential, static parallel, or dynamic fanout chain step for a top-level async chain whose status is still `running`. The step is persisted in the run directory and becomes eligible only after the chain's already-queued steps finish; completed, failed, paused, foreground, single, and top-level parallel runs reject appends.
|
|
843
|
+
|
|
840
844
|
## Worktree isolation
|
|
841
845
|
|
|
842
846
|
Parallel agents can clobber each other if they edit the same checkout. `worktree: true` gives each parallel child its own git worktree branched from `HEAD`.
|
|
@@ -916,19 +920,6 @@ Session directory precedence is: `params.sessionDir`, then `config.defaultSessio
|
|
|
916
920
|
|
|
917
921
|
Controls nested delegation when no inherited `PI_SUBAGENT_MAX_DEPTH` is already in effect. Per-agent `maxSubagentDepth` can tighten the limit for that agent’s child runs, but cannot relax an inherited stricter limit. This applies even to children that explicitly declare `tools: subagent`; at the cap, execution fanout is blocked instead of silently hiding nested work.
|
|
918
922
|
|
|
919
|
-
### Agent resource limits
|
|
920
|
-
|
|
921
|
-
Set `maxExecutionTimeMs` and `maxTokens` in agent frontmatter or through `subagent({ action: "create" | "update", config })` to bound a specific agent across foreground and async runs.
|
|
922
|
-
|
|
923
|
-
```yaml
|
|
924
|
-
maxExecutionTimeMs: 600000
|
|
925
|
-
maxTokens: 50000
|
|
926
|
-
```
|
|
927
|
-
|
|
928
|
-
When a limit is reached, the child receives a soft interrupt, the run fails with a clear `Resource limit exceeded...` error, and the result includes `resourceLimitExceeded` with the limit kind, configured limit, and observed token count when available. Resource-limit failures do not trigger fallback model retries. `maxTokens` is best-effort because providers report usage after message events; a child may exceed the exact limit before the runtime can stop it.
|
|
929
|
-
|
|
930
|
-
Spawn-count and per-agent child-concurrency quotas are not part of this release; use `maxSubagentDepth` and parallel `concurrency` for those boundaries today.
|
|
931
|
-
|
|
932
923
|
### `intercomBridge`
|
|
933
924
|
|
|
934
925
|
```json
|
|
@@ -987,7 +978,7 @@ Debug artifacts live under `{sessionDir}/subagent-artifacts/` or a user-scoped t
|
|
|
987
978
|
- `{runId}_{agent}.jsonl`
|
|
988
979
|
- `{runId}_{agent}_meta.json`
|
|
989
980
|
|
|
990
|
-
Metadata records timing, usage, exit code, final model, attempted models, fallback attempt outcomes
|
|
981
|
+
Metadata records timing, usage, exit code, final model, attempted models, and fallback attempt outcomes.
|
|
991
982
|
|
|
992
983
|
Session files are stored under a per-run session directory. With `context: "fork"`, each child starts with `--session <branched-session-file>` produced from the parent’s current leaf. That is a real session fork, not an injected summary.
|
|
993
984
|
|
|
@@ -1007,60 +998,33 @@ Async runs write:
|
|
|
1007
998
|
|
|
1008
999
|
## Acceptance Gates
|
|
1009
1000
|
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
If you are coming from Codex Goals, `acceptance` is the subagent equivalent for one delegated run. When a user says `/goal`, “goal”, “active goal”, “continue until evidence says done”, or “verify against a goal”, translate that into an acceptance contract: `criteria` are the target, `evidence` and `verify` are proof, `stopRules` are constraints, and `maxFinalizationTurns` is the bounded loop budget.
|
|
1001
|
+
Every run resolves an effective acceptance policy. Callers may omit `acceptance` for the inferred default, or set it on single runs, top-level parallel task items, chain steps, static parallel tasks, and dynamic fanout templates.
|
|
1013
1002
|
|
|
1014
1003
|
```ts
|
|
1015
1004
|
{
|
|
1016
1005
|
agent: "worker",
|
|
1017
1006
|
task: "Implement the fix",
|
|
1018
1007
|
acceptance: {
|
|
1008
|
+
level: "verified",
|
|
1019
1009
|
criteria: ["Patch the bug without widening scope"],
|
|
1020
1010
|
evidence: ["changed-files", "tests-added", "commands-run", "residual-risks", "no-staged-files"],
|
|
1021
|
-
verify: [{ id: "focused", command: "npm test", timeoutMs: 120000 }]
|
|
1022
|
-
maxFinalizationTurns: 3
|
|
1011
|
+
verify: [{ id: "focused", command: "npm test", timeoutMs: 120000 }]
|
|
1023
1012
|
}
|
|
1024
1013
|
}
|
|
1025
1014
|
```
|
|
1026
1015
|
|
|
1027
|
-
|
|
1016
|
+
Accepted levels are `auto`, `none`, `attested`, `checked`, `verified`, and `reviewed`. `acceptance: "auto"` is the default. Read-only reviewer/scout tasks infer lightweight attestation, normal writer tasks infer checked evidence, and async/risky/dynamic writer contexts infer a reviewed gate. To disable gates, prefer `{ level: "none", reason: "..." }`.
|
|
1028
1017
|
|
|
1029
|
-
|
|
1018
|
+
Acceptance provenance is stored separately from child prose:
|
|
1030
1019
|
|
|
1031
|
-
- `
|
|
1032
|
-
- `
|
|
1020
|
+
- `claimed`: child finished but did not provide structured evidence.
|
|
1021
|
+
- `attested`: child returned a structured acceptance report.
|
|
1022
|
+
- `checked`: runtime structural checks passed, such as required evidence and no staged files.
|
|
1033
1023
|
- `verified`: configured runtime verification commands passed. Child-reported command success does not count.
|
|
1034
1024
|
- `reviewed`: an independent reviewer result is present.
|
|
1035
|
-
- `rejected`: attestation, structural checks, verification,
|
|
1036
|
-
|
|
1037
|
-
Self-review finalization never counts as `reviewed`, and it never counts as `verified` unless configured runtime verification commands actually pass. The visible child output remains the initial answer; finalization reports and residual risks are stored in the acceptance ledger and async/status details.
|
|
1025
|
+
- `rejected`: attestation, structural checks, verification, or review failed.
|
|
1038
1026
|
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
```ts
|
|
1042
|
-
subagent({
|
|
1043
|
-
agent: "worker",
|
|
1044
|
-
async: true,
|
|
1045
|
-
task: "Implement the plan at /Users/me/docs/mcp-alignment-plan.md. Use scout artifacts in ./handoff/ as context. Do not commit the scout artifacts.",
|
|
1046
|
-
acceptance: {
|
|
1047
|
-
criteria: [
|
|
1048
|
-
"Implementation follows /Users/me/docs/mcp-alignment-plan.md",
|
|
1049
|
-
"Plan acceptance checks are addressed",
|
|
1050
|
-
"Scout handoff artifacts are not committed",
|
|
1051
|
-
"Focused validation for changed behavior passes",
|
|
1052
|
-
"Residual risks or skipped checks are reported"
|
|
1053
|
-
],
|
|
1054
|
-
evidence: ["changed-files", "commands-run", "validation-output", "residual-risks"],
|
|
1055
|
-
verify: [{ id: "focused", command: "npm test -- --runInBand" }],
|
|
1056
|
-
stopRules: [
|
|
1057
|
-
"Do not edit unrelated files",
|
|
1058
|
-
"Stop and report if the plan requires an unapproved product decision"
|
|
1059
|
-
],
|
|
1060
|
-
maxFinalizationTurns: 3
|
|
1061
|
-
}
|
|
1062
|
-
})
|
|
1063
|
-
```
|
|
1027
|
+
For `attested` or stricter levels, the child prompt includes a standardized acceptance section and asks for a fenced `acceptance-report` JSON block. Explicit failed gates fail the run. Inferred gates are persisted for observability without breaking older calls that omit `acceptance`.
|
|
1064
1028
|
|
|
1065
1029
|
## Live progress
|
|
1066
1030
|
|
package/package.json
CHANGED
|
@@ -20,6 +20,7 @@ Use this skill when the parent orchestrator needs to launch a specialized subage
|
|
|
20
20
|
- **Implementation handoff**: have `oracle` advise, then `worker` implement only after an approved direction
|
|
21
21
|
- **Recon and planning**: use `scout` or `context-builder`, then `planner`
|
|
22
22
|
- **Parallel exploration**: run multiple non-conflicting tasks concurrently
|
|
23
|
+
- **Regular skill specialists**: when discovery shows proactive skill subagent suggestions and the current work is broad enough, launch a small fresh-context fanout that asks one subagent per relevant regularly used skill to apply that skill's perspective to the task
|
|
23
24
|
- **Long-running work**: launch async/background runs and inspect them later
|
|
24
25
|
- **Subagent control**: watch needs-attention signals and soft-interrupt only when a delegated run is genuinely blocked
|
|
25
26
|
- **Agent authoring**: create, update, or override agents and chains for a project
|
|
@@ -55,6 +56,30 @@ The prompt templates in `prompts/` encode workflows the parent agent can run on
|
|
|
55
56
|
|
|
56
57
|
Use this when the user wants adversarial review of a diff, plan, issue, file, or implemented work. Launch fresh-context `reviewer` agents with distinct angles generated from the actual target. Common angles are correctness/regressions, tests/validation, and simplicity/maintainability; adapt for TypeScript, UI, security, docs, or large structural changes. Reviewers should inspect files and diffs directly, return concise evidence-backed findings with file/line references, and avoid edits unless the user explicitly asks for a writer pass. The parent synthesizes fixes worth doing now, optional improvements, and feedback to ignore/defer before applying anything.
|
|
57
58
|
|
|
59
|
+
### Proactive skill-specialist technique
|
|
60
|
+
|
|
61
|
+
Use this when `{ action: "list" }` reports proactive skill subagent suggestions and the user's task would benefit from perspectives the parent regularly uses. These suggestions are conservative: a skill is recommended only when it is available and referenced repeatedly by configured agents or saved chains. Treat the list as an opt-in hint for the current task, not a command to always fan out.
|
|
62
|
+
|
|
63
|
+
Default guardrails:
|
|
64
|
+
- Keep the fanout small: usually one or two skill-specialist children, never more than the listed recommendations or configured cap.
|
|
65
|
+
- Prefer `context: "fresh"` and include only the files, diff, plan, URL, or request details each child needs. Use forked context only when private/session history is essential and appropriate to share.
|
|
66
|
+
- Use read-only agents for analysis/review unless implementation was explicitly requested; do not create several writers in the same worktree.
|
|
67
|
+
- Skip proactive skill subagents for tiny questions, direct commands, highly private requests, or when the user asks not to delegate.
|
|
68
|
+
- Make cost and concurrency visible by using an ordinary `subagent(...)` call rather than hidden/background automation.
|
|
69
|
+
|
|
70
|
+
Example shape:
|
|
71
|
+
|
|
72
|
+
```typescript
|
|
73
|
+
subagent({
|
|
74
|
+
tasks: [
|
|
75
|
+
{ agent: "reviewer", task: "Apply the available 'deslop' skill to review the current diff for concrete cleanup findings only. Do not modify files.", skill: "deslop" },
|
|
76
|
+
{ agent: "reviewer", task: "Apply the available 'accessibility' skill to review the UI changes for concrete issues only. Do not modify files.", skill: "accessibility" }
|
|
77
|
+
],
|
|
78
|
+
context: "fresh",
|
|
79
|
+
concurrency: 2
|
|
80
|
+
})
|
|
81
|
+
```
|
|
82
|
+
|
|
58
83
|
### Review-loop technique
|
|
59
84
|
|
|
60
85
|
Use this when the user wants implementation or current diff review to continue until reviewers stop finding fixes worth doing now. Keep the loop in the parent session: one async `worker` implements or fixes, fresh-context `reviewer` agents inspect the actual repo and diff, the parent synthesizes accepted fixes, and one async forked `worker` applies them. The parent can express the sequence up front as an async/background chain when the workflow is known, or continue with explicit follow-up subagent runs after each async completion. For an initial chain, pass `async: true` so the main chat is unblocked; do not set `clarify: true` unless the user explicitly wants the foreground clarify UI. Treat an async implementation worker handoff as an intermediate state, not final completion, unless the user explicitly asked for worker-only work, review-only output, or to stop after implementation. Stop when reviewers find no blockers or fixes worth doing now, remaining feedback is optional or deferred, an unapproved product/scope/architecture decision appears, or the max review-round cap is reached. Default to 3 review rounds unless the user sets a different cap. Do not loop for optional polish, and do not let children launch subagents or decide the loop outcome.
|
|
@@ -134,7 +159,7 @@ subagent({
|
|
|
134
159
|
{ agent: "reviewer", phase: "Planning", label: "Scheduler contract", as: "schedulerPlan", task: "Plan fixes for scheduler contract. Inspect the current diff. Do not modify project/source files; returning findings via the configured output artifact is allowed.", output: "plans/scheduler.md", outputMode: "file-only" },
|
|
135
160
|
{ agent: "reviewer", phase: "Planning", label: "Sandbox/security", as: "sandboxPlan", task: "Plan fixes for sandbox/security. Inspect the current diff. Do not modify project/source files; returning findings via the configured output artifact is allowed.", output: "plans/sandbox.md", outputMode: "file-only" }
|
|
136
161
|
], concurrency: 3 },
|
|
137
|
-
{ agent: "worker", phase: "Implementation", label: "Apply accepted fixes", as: "workerResult", task: "Apply only the accepted fixes from these planning summaries. You are the sole writer for the active worktree.\n\nDeploy plan:\n{outputs.deployPlan}\n\nScheduler plan:\n{outputs.schedulerPlan}\n\nSandbox plan:\n{outputs.sandboxPlan}",
|
|
162
|
+
{ agent: "worker", phase: "Implementation", label: "Apply accepted fixes", as: "workerResult", task: "Apply only the accepted fixes from these planning summaries. You are the sole writer for the active worktree. Run focused validation and report changed files, commands, failures, and remaining issues.\n\nDeploy plan:\n{outputs.deployPlan}\n\nScheduler plan:\n{outputs.schedulerPlan}\n\nSandbox plan:\n{outputs.sandboxPlan}", output: "worker/fixes.md", outputMode: "file-only", progress: true },
|
|
138
163
|
{ parallel: [
|
|
139
164
|
{ agent: "reviewer", phase: "Validation", label: "Deploy/scheduler validation", task: "Validate the post-worker diff for deploy and scheduler fixes. Start from the worker result: {outputs.workerResult}. Do not modify project/source files; returning findings via the configured output artifact is allowed.", output: "validation/deploy-scheduler.md", outputMode: "file-only" },
|
|
140
165
|
{ agent: "reviewer", phase: "Validation", label: "Sandbox validation", task: "Validate the post-worker diff for sandbox/security fixes. Start from the worker result: {outputs.workerResult}. Do not modify project/source files; returning findings via the configured output artifact is allowed.", output: "validation/sandbox.md", outputMode: "file-only" }
|
|
@@ -692,37 +717,7 @@ clarify → validation contract → planner → async worker → parallel async
|
|
|
692
717
|
|
|
693
718
|
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.
|
|
694
719
|
|
|
695
|
-
Use the structured `acceptance` field when the run should carry an explicit acceptance contract. If omitted,
|
|
696
|
-
|
|
697
|
-
Goal-style requests map to `acceptance`. If the user says `/goal`, “goal”, “active goal”, “continue until evidence says done”, or “verify against a goal” for a subagent run, create an explicit run-scoped acceptance contract: `criteria` for the target, `evidence` and `verify` for proof, `stopRules` for constraints, and `maxFinalizationTurns` for the bounded loop budget.
|
|
698
|
-
|
|
699
|
-
When launching a writer/worker from a plan, PRD, spec, issue, or broad fix, set structured `acceptance` proactively. Put implementation instructions, plan paths, and handoff artifacts in `task`; put the definition of done in `acceptance.criteria`, proof requirements in `acceptance.evidence` and `acceptance.verify`, constraints in `acceptance.stopRules`, and usually set `maxFinalizationTurns: 3`. Do not bury all validation requirements only in the task prompt.
|
|
700
|
-
|
|
701
|
-
Example writer handoff:
|
|
702
|
-
|
|
703
|
-
```typescript
|
|
704
|
-
subagent({
|
|
705
|
-
agent: "worker",
|
|
706
|
-
async: true,
|
|
707
|
-
task: "Implement the plan at /Users/me/docs/mcp-alignment-plan.md. Use scout artifacts in ./handoff/ as context. Do not commit the scout artifacts.",
|
|
708
|
-
acceptance: {
|
|
709
|
-
criteria: [
|
|
710
|
-
"Implementation follows /Users/me/docs/mcp-alignment-plan.md",
|
|
711
|
-
"Plan acceptance checks are addressed",
|
|
712
|
-
"Scout handoff artifacts are not committed",
|
|
713
|
-
"Focused validation for changed behavior passes",
|
|
714
|
-
"Residual risks or skipped checks are reported"
|
|
715
|
-
],
|
|
716
|
-
evidence: ["changed-files", "commands-run", "validation-output", "residual-risks"],
|
|
717
|
-
verify: [{ id: "focused", command: "npm test -- --runInBand" }],
|
|
718
|
-
stopRules: [
|
|
719
|
-
"Do not edit unrelated files",
|
|
720
|
-
"Stop and report if the plan requires an unapproved product decision"
|
|
721
|
-
],
|
|
722
|
-
maxFinalizationTurns: 3
|
|
723
|
-
}
|
|
724
|
-
})
|
|
725
|
-
```
|
|
720
|
+
Use the structured `acceptance` field when the run should carry an explicit acceptance contract. If omitted, subagents infer an effective acceptance policy from role, mode, and risk. Use `level: "checked"` for ordinary writer evidence gates, `level: "verified"` when the runtime should run explicit validation commands, and `level: "reviewed"` only when an independent reviewer result is expected. Do not call a run reviewed just because the worker says it is done; reviewed means a reviewer gate returned a result. Child-reported command success is evidence, not runtime verification.
|
|
726
721
|
|
|
727
722
|
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.
|
|
728
723
|
|
|
@@ -751,9 +746,8 @@ subagent({
|
|
|
751
746
|
agent: "worker",
|
|
752
747
|
task: "Implement the approved feature.\n\nClarified requirements:\n- ...\n\nPlan: see ~/Documents/docs/...-plan.md\n\nValidation contract:\n- ...\n\nReturn a handoff with changed files, what was implemented, what was left undone, commands run with exit codes, validation evidence, surprises/new risks, and decisions needing parent approval.",
|
|
753
748
|
acceptance: {
|
|
754
|
-
|
|
755
|
-
evidence: ["changed-files", "tests-added", "commands-run", "residual-risks", "no-staged-files"]
|
|
756
|
-
maxFinalizationTurns: 3
|
|
749
|
+
level: "checked",
|
|
750
|
+
evidence: ["changed-files", "tests-added", "commands-run", "residual-risks", "no-staged-files"]
|
|
757
751
|
},
|
|
758
752
|
async: true
|
|
759
753
|
})
|
|
@@ -19,11 +19,14 @@ import {
|
|
|
19
19
|
import { serializeAgent } from "./agent-serializer.ts";
|
|
20
20
|
import { serializeChain, serializeJsonChain } from "./chain-serializer.ts";
|
|
21
21
|
import { discoverAvailableSkills } from "./skills.ts";
|
|
22
|
-
import
|
|
22
|
+
import {
|
|
23
|
+
buildProactiveSkillSubagentRecommendationLines,
|
|
24
|
+
} from "./proactive-skills.ts";
|
|
25
|
+
import type { Details, ExtensionConfig } from "../shared/types.ts";
|
|
23
26
|
|
|
24
27
|
type ManagementAction = "list" | "get" | "create" | "update" | "delete";
|
|
25
28
|
type ManagementScope = "user" | "project";
|
|
26
|
-
type ManagementContext = Pick<ExtensionContext, "cwd" | "modelRegistry"
|
|
29
|
+
type ManagementContext = Pick<ExtensionContext, "cwd" | "modelRegistry"> & { config?: ExtensionConfig };
|
|
27
30
|
|
|
28
31
|
interface ManagementParams {
|
|
29
32
|
action?: string;
|
|
@@ -78,8 +81,8 @@ function parsePackageConfig(value: unknown): { packageName?: string; error?: str
|
|
|
78
81
|
return parsePackageName(value, "config.package");
|
|
79
82
|
}
|
|
80
83
|
|
|
81
|
-
function allAgents(d: { builtin: AgentConfig[]; user: AgentConfig[]; project: AgentConfig[] }): AgentConfig[] {
|
|
82
|
-
return [...d.builtin, ...d.user, ...d.project];
|
|
84
|
+
function allAgents(d: { builtin: AgentConfig[]; package: AgentConfig[]; user: AgentConfig[]; project: AgentConfig[] }): AgentConfig[] {
|
|
85
|
+
return [...d.builtin, ...d.package, ...d.user, ...d.project];
|
|
83
86
|
}
|
|
84
87
|
|
|
85
88
|
function availableNames(cwd: string, kind: "agent" | "chain"): string[] {
|
|
@@ -116,6 +119,10 @@ function nameExistsInScope(cwd: string, scope: ManagementScope, name: string, ex
|
|
|
116
119
|
return false;
|
|
117
120
|
}
|
|
118
121
|
|
|
122
|
+
function isMutableSource(source: AgentSource): source is ManagementScope {
|
|
123
|
+
return source === "user" || source === "project";
|
|
124
|
+
}
|
|
125
|
+
|
|
119
126
|
function unknownChainAgents(cwd: string, steps: ChainStepConfig[]): string[] {
|
|
120
127
|
const d = discoverAgentsAll(cwd);
|
|
121
128
|
const known = new Set(allAgents(d).map((a) => a.name));
|
|
@@ -269,6 +276,12 @@ function applyAgentConfig(target: AgentConfig, cfg: Record<string, unknown>): st
|
|
|
269
276
|
else if (typeof cfg.extensions === "string") target.extensions = parseCsv(cfg.extensions);
|
|
270
277
|
else return "config.extensions must be a comma-separated string, empty string, or false when provided.";
|
|
271
278
|
}
|
|
279
|
+
if (hasKey(cfg, "subagentOnlyExtensions")) {
|
|
280
|
+
if (cfg.subagentOnlyExtensions === false) target.subagentOnlyExtensions = undefined;
|
|
281
|
+
else if (cfg.subagentOnlyExtensions === "") target.subagentOnlyExtensions = [];
|
|
282
|
+
else if (typeof cfg.subagentOnlyExtensions === "string") target.subagentOnlyExtensions = parseCsv(cfg.subagentOnlyExtensions);
|
|
283
|
+
else return "config.subagentOnlyExtensions must be a comma-separated string, empty string, or false when provided.";
|
|
284
|
+
}
|
|
272
285
|
if (hasKey(cfg, "thinking")) {
|
|
273
286
|
if (cfg.thinking === false || cfg.thinking === "") target.thinking = undefined;
|
|
274
287
|
else if (typeof cfg.thinking === "string") target.thinking = cfg.thinking.trim() || undefined;
|
|
@@ -313,18 +326,6 @@ function applyAgentConfig(target: AgentConfig, cfg: Record<string, unknown>): st
|
|
|
313
326
|
target.maxSubagentDepth = cfg.maxSubagentDepth;
|
|
314
327
|
} else return "config.maxSubagentDepth must be an integer >= 0 or false when provided.";
|
|
315
328
|
}
|
|
316
|
-
if (hasKey(cfg, "maxExecutionTimeMs")) {
|
|
317
|
-
if (cfg.maxExecutionTimeMs === false || cfg.maxExecutionTimeMs === "") target.maxExecutionTimeMs = undefined;
|
|
318
|
-
else if (typeof cfg.maxExecutionTimeMs === "number" && Number.isInteger(cfg.maxExecutionTimeMs) && cfg.maxExecutionTimeMs >= 1) {
|
|
319
|
-
target.maxExecutionTimeMs = cfg.maxExecutionTimeMs;
|
|
320
|
-
} else return "config.maxExecutionTimeMs must be an integer >= 1 or false when provided.";
|
|
321
|
-
}
|
|
322
|
-
if (hasKey(cfg, "maxTokens")) {
|
|
323
|
-
if (cfg.maxTokens === false || cfg.maxTokens === "") target.maxTokens = undefined;
|
|
324
|
-
else if (typeof cfg.maxTokens === "number" && Number.isInteger(cfg.maxTokens) && cfg.maxTokens >= 1) {
|
|
325
|
-
target.maxTokens = cfg.maxTokens;
|
|
326
|
-
} else return "config.maxTokens must be an integer >= 1 or false when provided.";
|
|
327
|
-
}
|
|
328
329
|
if (hasKey(cfg, "completionGuard")) {
|
|
329
330
|
if (typeof cfg.completionGuard !== "boolean") return "config.completionGuard must be a boolean when provided.";
|
|
330
331
|
target.completionGuard = cfg.completionGuard;
|
|
@@ -339,10 +340,10 @@ function resolveTarget<T extends { source: AgentSource; filePath: string }>(
|
|
|
339
340
|
cwd: string,
|
|
340
341
|
scopeHint?: string,
|
|
341
342
|
): T | AgentToolResult<Details> {
|
|
342
|
-
const mutable = matches.filter((m) => m.source
|
|
343
|
+
const mutable = matches.filter((m): m is T & { source: ManagementScope } => isMutableSource(m.source));
|
|
343
344
|
if (mutable.length === 0) {
|
|
344
345
|
if (matches.length > 0) {
|
|
345
|
-
return result(`${kind === "agent" ? "Agent" : "Chain"} '${name}' is
|
|
346
|
+
return result(`${kind === "agent" ? "Agent" : "Chain"} '${name}' is read-only and cannot be modified. Create a same-named ${kind} in user or project scope to override it.`, true);
|
|
346
347
|
}
|
|
347
348
|
const available = availableNames(cwd, kind);
|
|
348
349
|
return result(`${kind === "agent" ? "Agent" : "Chain"} '${name}' not found. Available: ${available.join(", ") || "none"}.`, true);
|
|
@@ -393,13 +394,12 @@ function formatAgentDetail(agent: AgentConfig): string {
|
|
|
393
394
|
if (agent.defaultContext) lines.push(`Default context: ${agent.defaultContext}`);
|
|
394
395
|
if (agent.source === "builtin") lines.push(`Disabled: ${agent.disabled ? "true" : "false"}`);
|
|
395
396
|
if (agent.extensions !== undefined) lines.push(`Extensions: ${agent.extensions.length ? agent.extensions.join(", ") : "(none)"}`);
|
|
397
|
+
if (agent.subagentOnlyExtensions !== undefined) lines.push(`Subagent-only extensions: ${agent.subagentOnlyExtensions.length ? agent.subagentOnlyExtensions.join(", ") : "(none)"}`);
|
|
396
398
|
if (agent.thinking) lines.push(`Thinking: ${agent.thinking}`);
|
|
397
399
|
if (agent.output) lines.push(`Output: ${agent.output}`);
|
|
398
400
|
if (agent.defaultReads?.length) lines.push(`Reads: ${agent.defaultReads.join(", ")}`);
|
|
399
401
|
if (agent.defaultProgress) lines.push("Progress: true");
|
|
400
402
|
if (agent.maxSubagentDepth !== undefined) lines.push(`Max subagent depth: ${agent.maxSubagentDepth}`);
|
|
401
|
-
if (agent.maxExecutionTimeMs !== undefined) lines.push(`Max execution time: ${agent.maxExecutionTimeMs}ms`);
|
|
402
|
-
if (agent.maxTokens !== undefined) lines.push(`Max tokens: ${agent.maxTokens}`);
|
|
403
403
|
if (agent.completionGuard === false) lines.push("Completion guard: false");
|
|
404
404
|
if (agent.systemPrompt.trim()) lines.push("", "System Prompt:", agent.systemPrompt);
|
|
405
405
|
return lines.join("\n");
|
|
@@ -456,10 +456,16 @@ function formatChainDetail(chain: ChainConfig): string {
|
|
|
456
456
|
export function handleList(params: ManagementParams, ctx: ManagementContext): AgentToolResult<Details> {
|
|
457
457
|
const scope = normalizeListScope(params.agentScope) ?? "both";
|
|
458
458
|
const d = discoverAgentsAll(ctx.cwd);
|
|
459
|
-
const scopedAgents = allAgents(d).filter((a) => scope === "both" || a.source === "builtin" || a.source === scope).sort((a, b) => a.name.localeCompare(b.name));
|
|
459
|
+
const scopedAgents = allAgents(d).filter((a) => scope === "both" || a.source === "builtin" || a.source === "package" || a.source === scope).sort((a, b) => a.name.localeCompare(b.name));
|
|
460
460
|
const agents = scopedAgents.filter((a) => !a.disabled);
|
|
461
|
-
const chains = d.chains.filter((c) => scope === "both" || c.source === scope).sort((a, b) => a.name.localeCompare(b.name));
|
|
461
|
+
const chains = d.chains.filter((c) => scope === "both" || c.source === "package" || c.source === scope).sort((a, b) => a.name.localeCompare(b.name));
|
|
462
462
|
const diagnostics = d.chainDiagnostics.filter((entry) => scope === "both" || entry.source === scope);
|
|
463
|
+
const proactiveSuggestions = buildProactiveSkillSubagentRecommendationLines({
|
|
464
|
+
agents,
|
|
465
|
+
chains,
|
|
466
|
+
config: ctx.config?.proactiveSkillSubagents,
|
|
467
|
+
discoverAvailableSkills: () => discoverAvailableSkills(ctx.cwd),
|
|
468
|
+
});
|
|
463
469
|
const lines = [
|
|
464
470
|
"Executable agents:",
|
|
465
471
|
...(agents.length
|
|
@@ -468,6 +474,7 @@ export function handleList(params: ManagementParams, ctx: ManagementContext): Ag
|
|
|
468
474
|
"",
|
|
469
475
|
"Chains:",
|
|
470
476
|
...(chains.length ? chains.map((c) => `- ${c.name} (${c.source}): ${c.description}`) : ["- (none)"]),
|
|
477
|
+
...(proactiveSuggestions.length ? ["", ...proactiveSuggestions] : []),
|
|
471
478
|
...(diagnostics.length ? ["", "Chain diagnostics:", ...diagnostics.map((entry) => `- ${entry.filePath}: ${entry.error}`)] : []),
|
|
472
479
|
];
|
|
473
480
|
return result(lines.join("\n"));
|
|
@@ -5,10 +5,12 @@ export function mergeAgentsForScope(
|
|
|
5
5
|
userAgents: AgentConfig[],
|
|
6
6
|
projectAgents: AgentConfig[],
|
|
7
7
|
builtinAgents: AgentConfig[] = [],
|
|
8
|
+
packageAgents: AgentConfig[] = [],
|
|
8
9
|
): AgentConfig[] {
|
|
9
10
|
const agentMap = new Map<string, AgentConfig>();
|
|
10
11
|
|
|
11
12
|
for (const agent of builtinAgents) agentMap.set(agent.name, agent);
|
|
13
|
+
for (const agent of packageAgents) agentMap.set(agent.name, agent);
|
|
12
14
|
|
|
13
15
|
if (scope === "both") {
|
|
14
16
|
for (const agent of userAgents) agentMap.set(agent.name, agent);
|
|
@@ -16,13 +16,12 @@ export const KNOWN_FIELDS = new Set([
|
|
|
16
16
|
"skill",
|
|
17
17
|
"skills",
|
|
18
18
|
"extensions",
|
|
19
|
+
"subagentOnlyExtensions",
|
|
19
20
|
"output",
|
|
20
21
|
"defaultReads",
|
|
21
22
|
"defaultProgress",
|
|
22
23
|
"interactive",
|
|
23
24
|
"maxSubagentDepth",
|
|
24
|
-
"maxExecutionTimeMs",
|
|
25
|
-
"maxTokens",
|
|
26
25
|
"completionGuard",
|
|
27
26
|
]);
|
|
28
27
|
|
|
@@ -61,6 +60,10 @@ export function serializeAgent(config: AgentConfig): string {
|
|
|
61
60
|
const extensionsValue = joinComma(config.extensions);
|
|
62
61
|
lines.push(`extensions: ${extensionsValue ?? ""}`);
|
|
63
62
|
}
|
|
63
|
+
if (config.subagentOnlyExtensions !== undefined) {
|
|
64
|
+
const subagentOnlyExtensionsValue = joinComma(config.subagentOnlyExtensions);
|
|
65
|
+
lines.push(`subagentOnlyExtensions: ${subagentOnlyExtensionsValue ?? ""}`);
|
|
66
|
+
}
|
|
64
67
|
|
|
65
68
|
if (config.output) lines.push(`output: ${config.output}`);
|
|
66
69
|
|
|
@@ -73,14 +76,6 @@ export function serializeAgent(config: AgentConfig): string {
|
|
|
73
76
|
if (typeof maxSubagentDepth === "number" && Number.isInteger(maxSubagentDepth) && maxSubagentDepth >= 0) {
|
|
74
77
|
lines.push(`maxSubagentDepth: ${maxSubagentDepth}`);
|
|
75
78
|
}
|
|
76
|
-
const maxExecutionTimeMs = config.maxExecutionTimeMs;
|
|
77
|
-
if (typeof maxExecutionTimeMs === "number" && Number.isInteger(maxExecutionTimeMs) && maxExecutionTimeMs >= 1) {
|
|
78
|
-
lines.push(`maxExecutionTimeMs: ${maxExecutionTimeMs}`);
|
|
79
|
-
}
|
|
80
|
-
const maxTokens = config.maxTokens;
|
|
81
|
-
if (typeof maxTokens === "number" && Number.isInteger(maxTokens) && maxTokens >= 1) {
|
|
82
|
-
lines.push(`maxTokens: ${maxTokens}`);
|
|
83
|
-
}
|
|
84
79
|
if (config.completionGuard === false) lines.push("completionGuard: false");
|
|
85
80
|
|
|
86
81
|
if (config.extraFields) {
|