pi-subagents 0.59.0 → 0.60.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 +37 -0
- package/docs/tool-reference.md +4 -1
- package/docs/workflows.md +2 -2
- package/package.json +1 -1
- package/skills/council-mode/SKILL.md +48 -243
- package/skills/council-mode/references/pass-contracts.md +150 -0
- package/skills/pi-subagents/SKILL.md +87 -37
- package/skills/pi-subagents/references/constraints-and-recipes.md +29 -233
- package/skills/pi-subagents/references/execution-controls.md +47 -6
- package/skills/pi-subagents/references/management-authoring-rpc.md +2 -2
- package/skills/pi-subagents/references/multi-lane-orchestration.md +13 -1
- package/skills/pi-subagents/references/prompting-and-roles.md +34 -27
- package/skills/pi-subagents/references/review-and-validation.md +73 -0
- package/src/agents/agent-management.ts +154 -25
- package/src/api/shared-types.ts +2 -0
- package/src/extension/public-execution.ts +1 -0
- package/src/extension/schemas.ts +1 -0
- package/src/extension/tool-description.ts +8 -4
- package/src/runs/background/async-execution.ts +2 -2
- package/src/runs/background/async-job-tracker.ts +3 -0
- package/src/runs/background/async-status.ts +45 -2
- package/src/runs/background/control-channel.ts +3 -2
- package/src/runs/background/run-status.ts +13 -2
- package/src/runs/background/subagent-runner.ts +5 -1
- package/src/runs/background/subagent-wait.ts +10 -2
- package/src/runs/background/wait-completions.ts +3 -0
- package/src/runs/foreground/execution.ts +11 -2
- package/src/runs/foreground/subagent-executor.ts +98 -1
- package/src/runs/shared/async-status-projection.ts +138 -4
- package/src/runs/shared/background-process-options.ts +9 -0
- package/src/runs/shared/mcp-direct-tool-grant.ts +2 -5
- package/src/runs/shared/mutation-evidence.ts +52 -3
- package/src/runs/shared/pi-args.ts +47 -1
- package/src/runs/shared/single-output.ts +45 -18
- package/src/runs/shared/subagent-prompt-runtime.ts +20 -2
- package/src/runs/shared/workflow-graph.ts +15 -0
- package/src/shared/types.ts +34 -1
- package/src/tui/fleet-status.ts +11 -3
- package/src/tui/render-helpers.ts +31 -0
- package/src/tui/render.ts +597 -112
- package/src/watchdog/change-signature.ts +40 -1
- package/src/workflows/host-command.ts +6 -1
- package/src/workflows/scripted-workflow.ts +53 -2
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
Use this reference when several independent tasks need coordinated workers, worktrees, or repositories. It defines lane ownership; use the other pi-subagents references for run controls, prompts, and mission details. The parent remains the final decision-maker.
|
|
4
4
|
|
|
5
|
+
Create lanes only when delegation materially improves evidence, independent review, or isolated execution. Do not manufacture parallelism: keep dependent work serial, and only split work when each lane has a distinct decision and useful output.
|
|
6
|
+
|
|
5
7
|
## Lane board and authority
|
|
6
8
|
|
|
7
9
|
Before multiple mutation-capable lanes start, record this board in the parent context:
|
|
@@ -20,15 +22,25 @@ For Pi extension repositories, keep lane worktrees outside auto-discovered exten
|
|
|
20
22
|
|
|
21
23
|
Partition fanout by repository, source seam, decision, or review angle. Each run needs a stable key, lane-specific task, and a managed output path when a file is needed. Do not launch prompts that differ only by item name or broad file glob.
|
|
22
24
|
|
|
25
|
+
### Cold-start packets and bounded orchestration audits
|
|
26
|
+
|
|
27
|
+
Every child packet must stand alone: include the goal, exact repository/cwd/ref, authority and edit boundary, relevant context/evidence, success criteria, validation, expected output, and stop/escalation rules. Do not rely on parent history, an issue number, or a broad glob alone. An orchestration audit by a top-reasoning critic model is read-only and returns at most three cited omissions; use high thinking only as an explicit parent/user escalation, never as an autonomous root or a parallel placeholder.
|
|
28
|
+
|
|
23
29
|
Use one async `workflowScript` for a coordinated wave. Use `runs.all` for independent lanes and `runs.run` for dependent lane stages. Give cross-repository runs explicit `cwd` values and lane-qualified outputs. Use `outputMode: "file-only"` when a report must survive the run or feed a later stage. Keep scratch outputs relative so they live under subagent artifacts; use absolute paths only for durable memory, approved docs paths, or final handoff files.
|
|
24
30
|
|
|
25
31
|
## Keep independent work moving
|
|
26
32
|
|
|
27
33
|
While one lane waits, run safe independent preparation, validation, or fresh read-only review lanes. Do not block the parent just because a run is active. If no safe lane remains, record the blocker and the event that will reopen work.
|
|
28
34
|
|
|
35
|
+
In an ordinary interactive session, completion wakes the parent; after useful
|
|
36
|
+
async lanes are launched or triaged, yield rather than use
|
|
37
|
+
`subagent_wait({ all: true })` as a barrier. “Continue/orchestrate/work until
|
|
38
|
+
done” means keep the board moving while safe immediate work remains. If only
|
|
39
|
+
async lanes are running, record the revisit trigger and yield.
|
|
40
|
+
|
|
29
41
|
An ordinary coordinated workflow has one mission. Use its durable state, artifacts, run records, and receipts for recovery. Treat a receipt as evidence, not as authority or acceptance.
|
|
30
42
|
|
|
31
|
-
After a writer produces a candidate, run the required fresh-context, read-only reviewer. The reviewer inspects the exact worktree and returns evidence-backed findings. The parent decides which findings are in scope and whether the lane is ready. Send accepted fixes to that lane's sole writer, then rerun only the affected gate.
|
|
43
|
+
After a writer produces a candidate, run the required fresh-context, read-only reviewer. The reviewer inspects the exact worktree and returns evidence-backed findings. The parent decides which findings are in scope and whether the lane is ready. Use `review-and-validation.md` for finding disposition, validation, and gate-failure triage. Send accepted fixes to that lane's sole writer, then rerun only the affected gate.
|
|
32
44
|
|
|
33
45
|
## Handoff, cleanup, and recovery
|
|
34
46
|
|
|
@@ -8,7 +8,7 @@ Parent extensions may register a session-scoped, out-of-band ceiling through `pi
|
|
|
8
8
|
|
|
9
9
|
## When to Use
|
|
10
10
|
|
|
11
|
-
- **Complex work orchestration**: use
|
|
11
|
+
- **Complex work orchestration**: keep the parent on its ordinary strong default model. Delegate only when another child materially improves evidence, independent review, or isolated execution; omission failures are cheaper than unnecessary commissions. For hard orchestration or root-cause questions, use a top-reasoning model only as a bounded read-only critic/oracle escalation, never as an autonomous root. 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.
|
|
12
12
|
- **Advisory review**: use fresh-context `reviewer` agents for adversarial code review, or fork to `oracle` when inherited decisions and drift matter
|
|
13
13
|
- **Implementation handoff**: have `oracle` advise, then `worker` implement only after an approved direction
|
|
14
14
|
- **Recon and planning**: use `scout`, then write a plan when needed
|
|
@@ -20,7 +20,7 @@ Parent extensions may register a session-scoped, out-of-band ceiling through `pi
|
|
|
20
20
|
|
|
21
21
|
## Tool vs Slash Commands
|
|
22
22
|
|
|
23
|
-
Agents use the `subagent(...)` tool
|
|
23
|
+
Agents use the `subagent(...)` tool for execution, management, status, and control. Direct `{ agent, task }` execution is enough for one bounded child task; use `workflowScript` when the parent needs JavaScript control flow or data-dependent branching, keyed, parallel, sequential, retry, retained-resume, aggregate, or explicit staged-lane behavior (`runs.lanes`). Humans often use the slash-command layer instead:
|
|
24
24
|
|
|
25
25
|
- `/run` — launch a single agent
|
|
26
26
|
- `workflowScript` — the sole public surface for sequence, parallelism, branching, retries, and aggregation
|
|
@@ -51,11 +51,15 @@ Packaged prompt shortcuts are also available for repeatable workflows. Treat the
|
|
|
51
51
|
|
|
52
52
|
The prompt templates in `prompts/` encode workflows the parent agent can run on demand. If the user provides a URL, issue, PR, plan, local file, screenshot, or freeform target, treat that target as the primary scope: read or fetch it before launching children, then include it explicitly in every child task. For targets outside the parent cwd, include the exact repository, explicit `cwd`, authority boundary, and expected output path in each child task. Do not depend on the parent conversation history when the recipe calls for fresh context.
|
|
53
53
|
|
|
54
|
+
### Commission-risk and cold-start packets
|
|
55
|
+
|
|
56
|
+
Delegate only when the child materially improves evidence, independent review, or isolated execution; do not manufacture parallelism. Every child packet must be cold-start complete: state the goal, exact target/cwd/ref, authority and edit boundary, relevant context/evidence, success criteria, validation, output, and stop/escalation rules. For an orchestration audit by the critic tier, make the child read-only and request at most three omissions, each cited to a file, line, or decision; high thinking is an explicit escalation, not a default.
|
|
57
|
+
|
|
54
58
|
### Council Mode technique
|
|
55
59
|
|
|
56
|
-
Use Council Mode when the user asks to convene advisors, debate a material decision, cross-examine recommendations, or critique and improve a plan with several model perspectives. This includes requests such as “run a council on this architecture,” “have
|
|
60
|
+
Use Council Mode when the user asks to convene advisors, debate a material decision, cross-examine recommendations, or critique and improve a plan with several model perspectives. This includes requests such as “run a council on this architecture,” “have the configured advisors critique this plan,” or “get multiple oracles to debate the tradeoffs.” Read `../council-mode/SKILL.md` and follow its bounded parent-supervised protocol instead of launching ad hoc parallel oracle calls.
|
|
57
61
|
|
|
58
|
-
Council advisors are read-only. User or project `council-*` profiles
|
|
62
|
+
Council advisors are read-only. User or project `council-*` profiles choose allowed models and define any persistent stance in the profile body. A top-reasoning advisor remains bounded and read-only; it does not become the root. Package advisors such as Surf's `gpt-pro` can join the roster only when the `surf-cli` Pi extension is installed and its `surf-oracle` provider is registered; treat them as external runners, omit child `async` for attached results, and do not pass `outputSchema` to them. The council question and scope provide the decision frame; do not invent per-advisor role labels. The parent collects independent reports, optionally sends curated cross-exam packets, and writes the final memo. Do not treat the council as agent-to-agent chat, implementation authority, or a writer swarm.
|
|
59
63
|
|
|
60
64
|
### Parallel review technique
|
|
61
65
|
|
|
@@ -111,6 +115,12 @@ Use this after implementation when the user wants cleanup review or when a final
|
|
|
111
115
|
|
|
112
116
|
Use this when a broad diff has known reviewer findings across several items and the user wants the parent to “orchestrate subagents like a boss.” Keep the active worktree safe with a three-stage `workflowScript`:
|
|
113
117
|
|
|
118
|
+
When staged seams are available, a low-tier writer should not receive the
|
|
119
|
+
end-to-end issue. Use `runs.lanes` inside `workflowScript` to keep stages narrow:
|
|
120
|
+
a scout/red test, helper-only change, one render seam, validation, minimality
|
|
121
|
+
challenge, or fresh review. Give the writer only its assigned implementation
|
|
122
|
+
stage; keep sequencing and synthesis with the parent.
|
|
123
|
+
|
|
114
124
|
1. A parallel read-only planning fanout, one reviewer per issue cluster. Each child inspects the real diff and returns exact files, line refs, proposed fixes, and focused validation. They must not edit.
|
|
115
125
|
2. One writer worker. It receives the reviewer summaries as the awaited planning results (or their durable output paths) interpolated into its task, plus the parent’s accepted scope, stop rules, and verification contract. It is the only child allowed to edit the active worktree.
|
|
116
126
|
3. A parallel read-only validation fanout. Validators inspect the worker diff from fresh context with distinct angles, report pass/fail, remaining blockers, and missing verification.
|
|
@@ -161,19 +171,19 @@ subagent({
|
|
|
161
171
|
Builtin agents load at the lowest priority. Project agents override user agents,
|
|
162
172
|
and user/project agents override builtins with the same name.
|
|
163
173
|
|
|
164
|
-
| Agent | Purpose |
|
|
174
|
+
| Agent | Purpose | Recommended tier | Typical output / role |
|
|
165
175
|
|-------|---------|-------|------------------------|
|
|
166
|
-
| `scout` | Fast codebase recon |
|
|
167
|
-
| `worker` | Implementation and approved oracle handoffs |
|
|
168
|
-
| `reviewer` | Review specialist |
|
|
169
|
-
| `researcher` | Web research brief generator | inherits default | Writes `research.md` |
|
|
170
|
-
| `delegate` | Lightweight generic delegate | inherits default | No fixed output; generic delegated work |
|
|
171
|
-
| `oracle` | Decision-consistency advisory review |
|
|
172
|
-
| `advisor` |
|
|
176
|
+
| `scout` | Fast codebase recon | fast worker/scout tier | Writes `context.md` handoff material |
|
|
177
|
+
| `worker` | Implementation and approved oracle handoffs | capable worker tier | Single-writer implementation with decision escalation |
|
|
178
|
+
| `reviewer` | Review specialist | strong reviewer tier; high thinking for serious reviews | Default recipes are review-only; tools include edit/write when a fix pass is explicit |
|
|
179
|
+
| `researcher` | Web research brief generator | inherits configured default | Writes `research.md` |
|
|
180
|
+
| `delegate` | Lightweight generic delegate | inherits configured default | No fixed output; generic delegated work |
|
|
181
|
+
| `oracle` | Decision-consistency advisory review | top-reasoning critic tier, bounded read-only; high thinking escalation only | Advisory review, intercom coordination |
|
|
182
|
+
| `advisor` | Compatibility alias for `oracle` | top-reasoning critic tier, bounded read-only; high thinking escalation only | Same advisory role as `oracle` |
|
|
173
183
|
|
|
174
184
|
Builtin `worker` and `delegate` use strict tool allowlists and do not inherit ambient parent extension tools. To give a child an extension tool, name it in `tools` and load its provider via `extensions`, a path-like `tools` entry, or `subagentOnlyExtensions`. Custom agents without an `extensions` field follow `subagents.defaultExtensions` when set.
|
|
175
185
|
|
|
176
|
-
Builtin agents inherit the current Pi default model unless a run, user setting, project setting, or `subagents.defaultModel` overrides `model`.
|
|
186
|
+
Builtin agents inherit the current Pi default model unless a run, user setting, project setting, or `subagents.defaultModel` overrides `model`. The table records recommended tier routing, not shipped hard defaults; explicit run, user, or project settings still win. Keep the parent/orchestrator on the ordinary strong default model unless parent/user policy says otherwise. Override builtin defaults before copying full agent files when a small tweak is enough.
|
|
177
187
|
|
|
178
188
|
Set `subagents.defaultThinking` to apply a shared thinking level to builtin, package, user, and project agents whose frontmatter leaves `thinking` unset. Project settings win over user settings; 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.
|
|
179
189
|
|
|
@@ -188,7 +198,7 @@ Set `subagents.defaultThinking` to apply a shared thinking level to builtin, pac
|
|
|
188
198
|
For one run, use inline config:
|
|
189
199
|
|
|
190
200
|
```text
|
|
191
|
-
/run reviewer[model=
|
|
201
|
+
/run reviewer[model=provider/review-model] "Review this diff"
|
|
192
202
|
```
|
|
193
203
|
|
|
194
204
|
For persistent tweaks, edit `subagents.agentOverrides` in user or project settings. User overrides apply everywhere. Project overrides apply only in that repo and win over user overrides. Use `/subagents-models` or `subagent({ action: "models" })` to inspect the live mapping after settings and overrides load.
|
|
@@ -202,18 +212,18 @@ Provider-scoped entries can layer on top of the default override for the active
|
|
|
202
212
|
"worker": { "thinking": "medium" }
|
|
203
213
|
},
|
|
204
214
|
"agentOverridesByProvider": {
|
|
205
|
-
"
|
|
206
|
-
"worker": { "model": "
|
|
215
|
+
"provider-a": {
|
|
216
|
+
"worker": { "model": "provider-a/fast-worker-model" }
|
|
207
217
|
},
|
|
208
|
-
"
|
|
209
|
-
"worker": { "model": "
|
|
218
|
+
"provider-b": {
|
|
219
|
+
"worker": { "model": "provider-b/fast-worker-model" }
|
|
210
220
|
}
|
|
211
221
|
}
|
|
212
222
|
}
|
|
213
223
|
}
|
|
214
224
|
```
|
|
215
225
|
|
|
216
|
-
Model ids do not have to be exact. Separator variations (`
|
|
226
|
+
Model ids do not have to be exact. Separator variations (`fast.worker-v1` vs `fast-worker-v1`), case (`Strong-Review-Model`), and optional trailing date stamps all resolve to the same registry model. Exact `provider/id` wins; a qualified `provider/model` never switches providers. To constrain subagents to a budget or compliance profile, set `subagents.modelScope: { enforce: true, allow: ["approved-provider/*", "second-provider/approved-*"] }` in user or project settings. Out-of-scope models you pass explicitly error and abort; models inherited from frontmatter, `subagents.defaultModel`, agent frontmatter, or the parent session only warn.
|
|
217
227
|
|
|
218
228
|
For model fleets, use the profile commands instead of hand-editing repeated overrides: `/subagents-refresh-provider-models <provider>`, `/subagents-generate-profiles <provider>`, `/subagents-load-profile <name>`, and `/subagents-check-profile <name>`. Profiles live under `~/.pi/agent/profiles/pi-subagents/` and replace only `settings.subagents` when loaded.
|
|
219
229
|
|
|
@@ -249,9 +259,9 @@ Direct settings example:
|
|
|
249
259
|
"subagents": {
|
|
250
260
|
"agentOverrides": {
|
|
251
261
|
"reviewer": {
|
|
252
|
-
"model": "
|
|
262
|
+
"model": "provider/strong-review-model",
|
|
253
263
|
"thinking": "high",
|
|
254
|
-
"fallbackModels": ["
|
|
264
|
+
"fallbackModels": ["backup-provider/strong-review-model"],
|
|
255
265
|
"acceptanceRole": "read-only"
|
|
256
266
|
}
|
|
257
267
|
}
|
|
@@ -269,14 +279,11 @@ agent with the same name only when you want a substantially different agent.
|
|
|
269
279
|
|
|
270
280
|
### Recommended model tiering (optional)
|
|
271
281
|
|
|
272
|
-
|
|
282
|
+
Keep the parent/orchestrator on the ordinary strong default model because omission failures are cheaper than unnecessary commissions. Route workers and scouts to a fast, capable worker tier, and keep serious reviews on the strong tier at high thinking. Use a top-reasoning model only for bounded, read-only critic/oracle/root-cause audits; critic-tier high thinking is escalation-only and never an autonomous root. Explicit parent/user model policy wins over these recommendations.
|
|
273
283
|
|
|
274
|
-
|
|
275
|
-
2. **Standard well-scoped** — mid-tier model at medium thinking for most delegations: routine multi-file edits, focused reviews, straightforward implementation (for example on `worker`, `reviewer`, `delegate`).
|
|
276
|
-
3. **Deep but bounded** — top reasoning model at high thinking only for hard tasks that arrive with explicit goals and completion criteria; these models loop on vague goals (for example on oracle-style agents).
|
|
277
|
-
4. **Taste and intent** — a model that reads human intent well for ambiguous work: UX/design judgment, product tradeoffs, planning from vague requirements, writing quality.
|
|
284
|
+
Examples are illustrative, not requirements. Map these tiers to concrete models in user/project settings or a profile. A non-OpenAI setup should choose comparable available models by capability.
|
|
278
285
|
|
|
279
|
-
|
|
286
|
+
Use `fallbackModels` when a tier has provider quota or availability risk. Prefer fresh context for cross-provider children when inherited provider-specific reasoning blocks would force thinking off.
|
|
280
287
|
|
|
281
288
|
If a provider rejects model IDs with thinking suffixes, use
|
|
282
289
|
`subagents.disableThinking: true` in user or project settings to clear bundled
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# Pi Subagents: Review And Validation
|
|
2
|
+
|
|
3
|
+
Generic review and delivery guidance for delegated work. This file does not encode private backlog, merge, or release policy.
|
|
4
|
+
|
|
5
|
+
## Delivery loop
|
|
6
|
+
|
|
7
|
+
Use the smallest loop that proves the change:
|
|
8
|
+
|
|
9
|
+
1. Inspect the source, diff, issue, or plan directly.
|
|
10
|
+
2. Keep one writer for each cwd or worktree.
|
|
11
|
+
3. Run focused validation that can fail for the changed behavior.
|
|
12
|
+
4. Use fresh-context read-only review for substantial, risky, public, or hard-to-see changes.
|
|
13
|
+
5. Apply only accepted findings inside the same writer boundary.
|
|
14
|
+
6. Re-run affected validation and review only the changed blast radius.
|
|
15
|
+
7. Inspect the final diff and evidence before parent acceptance.
|
|
16
|
+
|
|
17
|
+
Skip review ceremony for trivial wording, renames, or local-only probes when direct parent inspection is enough.
|
|
18
|
+
|
|
19
|
+
## Review shape
|
|
20
|
+
|
|
21
|
+
| Situation | Shape |
|
|
22
|
+
| --- | --- |
|
|
23
|
+
| One coherent diff or one risk | one reviewer |
|
|
24
|
+
| Independent risks, such as correctness, tests, security, or UI | parallel reviewers with distinct contracts |
|
|
25
|
+
| Possible over-scope or needless complexity | same-writer challenge before fresh review |
|
|
26
|
+
| Material design tradeoff | council mode |
|
|
27
|
+
|
|
28
|
+
Reviewers are fresh-context by default. Forked reviewers are for parent-history, drift, or prior-decision evidence.
|
|
29
|
+
|
|
30
|
+
## Finding disposition
|
|
31
|
+
|
|
32
|
+
The parent classifies each finding against current HEAD:
|
|
33
|
+
|
|
34
|
+
- **Valid blocker:** concrete failure, repro, security issue, contract mismatch, or source-proven regression. Fix now.
|
|
35
|
+
- **Valid non-blocker:** real but outside the delivery slice. Record or defer.
|
|
36
|
+
- **Stale:** fixed or absent at the reviewed head. Cite current evidence.
|
|
37
|
+
- **Invalid:** contradicted by source, tests, docs, or user-approved scope. Cite the contradiction.
|
|
38
|
+
- **Out of policy/scope:** needs unapproved product, architecture, authority, release, or public-repo action. Escalate.
|
|
39
|
+
- **Speculative:** no contract, repro, or reachable failure. Do not block.
|
|
40
|
+
|
|
41
|
+
A clean reviewer result is evidence, not publication authority.
|
|
42
|
+
|
|
43
|
+
## Gate-failure triage
|
|
44
|
+
|
|
45
|
+
When validation fails:
|
|
46
|
+
|
|
47
|
+
1. Confirm the run belongs to the exact head/ref under judgment.
|
|
48
|
+
2. Read the focused failing logs first.
|
|
49
|
+
3. Name the failing test, assertion, contract, or thread.
|
|
50
|
+
4. Classify cause: current diff, stale test, environment/setup, or existing flake.
|
|
51
|
+
5. Reproduce locally when practical with the narrowest command.
|
|
52
|
+
6. Patch forward when the current diff caused it.
|
|
53
|
+
7. For stale/flaky failures, collect proof before one rerun or residual-risk note.
|
|
54
|
+
8. Re-run the affected command or exact-head gate after every fix.
|
|
55
|
+
|
|
56
|
+
For bot comments, classify each thread as valid, stale, invalid, or out of policy before assigning severity.
|
|
57
|
+
|
|
58
|
+
## Final checklist
|
|
59
|
+
|
|
60
|
+
Before reporting delegated work as done, verify the relevant subset:
|
|
61
|
+
|
|
62
|
+
- final diff contains only intended files
|
|
63
|
+
- focused validation covers changed behavior
|
|
64
|
+
- substantial or risky changes have fresh-review evidence
|
|
65
|
+
- accepted findings are fixed and revalidated
|
|
66
|
+
- publication authority exists before push, comment, close, merge, deploy, or release
|
|
67
|
+
- external checks are exact-head when used as evidence
|
|
68
|
+
- handoff is durable before cleanup
|
|
69
|
+
- residual risks, skipped validation, and blocked decisions are explicit
|
|
70
|
+
|
|
71
|
+
## Public/private boundary
|
|
72
|
+
|
|
73
|
+
For issue/PR backlogs, releases, merge queues, contributor credit, or repo-specific policy, load the matching user/project skill when available. Keep those rules out of this public package until intentionally released.
|
|
@@ -32,9 +32,10 @@ import { toModelInfo } from "../shared/model-info.ts";
|
|
|
32
32
|
import { resolveSubagentModelOverride, type ParentModel } from "../runs/shared/model-fallback.ts";
|
|
33
33
|
import { validateToolBudgetConfig } from "../runs/shared/tool-budget.ts";
|
|
34
34
|
import { validateAcceptanceInput } from "../runs/shared/acceptance.ts";
|
|
35
|
-
import { CODE_OWNED_EXTERNAL_CLI_ADAPTER_LABEL, isCodeOwnedExternalCliAdapterId, validateCodeOwnedProfileRunner } from "../runs/shared/external-cli-contract.ts";
|
|
36
|
-
import type { AcceptanceInput, Details, ExtensionConfig, ToolBudgetConfig } from "../shared/types.ts";
|
|
35
|
+
import { CODE_OWNED_EXTERNAL_CLI_ADAPTER_LABEL, isCodeOwnedExternalCliAdapterId, resolveExternalCliRunnerStatus, validateCodeOwnedProfileRunner } from "../runs/shared/external-cli-contract.ts";
|
|
36
|
+
import type { AcceptanceInput, AgentCapabilitiesSnapshot, AgentCapabilityRow, Details, ExtensionConfig, ToolBudgetConfig } from "../shared/types.ts";
|
|
37
37
|
import { getProjectConfigDir } from "../shared/utils.ts";
|
|
38
|
+
import { previewDisplayText } from "../shared/display-text.ts";
|
|
38
39
|
import { capabilityCeilingAgentRestrictionSources, isAgentAllowedByCapabilityCeiling, resolveCurrentSubagentCapabilityCeiling } from "../runs/shared/capability-ceiling.ts";
|
|
39
40
|
import { listRuntimeAgentConfigs, mergeRuntimeAgents, type RuntimeAgentOwner } from "./runtime-agent-registry.ts";
|
|
40
41
|
import { listExternalJobProviders } from "../api/external-job-provider.ts";
|
|
@@ -47,11 +48,21 @@ interface ManagementParams {
|
|
|
47
48
|
action?: string;
|
|
48
49
|
agent?: string;
|
|
49
50
|
agentScope?: unknown;
|
|
51
|
+
capabilities?: unknown;
|
|
50
52
|
config?: unknown;
|
|
51
53
|
}
|
|
52
54
|
|
|
53
|
-
function result(text: string, isError = false): AgentToolResult<Details> {
|
|
54
|
-
return { content: [{ type: "text", text }], isError, details: { mode: "management", results: [] } };
|
|
55
|
+
function result(text: string, isError = false, details?: Partial<Details>): AgentToolResult<Details> {
|
|
56
|
+
return { content: [{ type: "text", text }], isError, details: { mode: "management", results: [], ...details } };
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function jsonDetails<T>(value: T): T {
|
|
60
|
+
return JSON.parse(JSON.stringify(value)) as T;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function presentDetails<T extends Record<string, unknown>>(value: T): T | undefined {
|
|
64
|
+
const cleaned = jsonDetails(value);
|
|
65
|
+
return Object.keys(cleaned).length > 0 ? cleaned : undefined;
|
|
55
66
|
}
|
|
56
67
|
|
|
57
68
|
function parseCsv(value: string): string[] {
|
|
@@ -645,18 +656,135 @@ function runnerListBadge(agent: AgentConfig, providerNames: Set<string> | undefi
|
|
|
645
656
|
return undefined;
|
|
646
657
|
}
|
|
647
658
|
|
|
648
|
-
function
|
|
659
|
+
function agentListMetadata(agent: AgentConfig, providerNames: Set<string> | undefined): string {
|
|
649
660
|
const source = agent.source === "package" ? packageSourceLabel(agent) : agent.source;
|
|
650
|
-
|
|
661
|
+
return [
|
|
651
662
|
source,
|
|
652
663
|
runnerListBadge(agent, providerNames),
|
|
653
664
|
agent.defaultContext ? `context: ${agent.defaultContext}` : undefined,
|
|
654
665
|
agent.aliases?.length ? `aliases: ${agent.aliases.join(", ")}` : undefined,
|
|
655
|
-
].filter((part): part is string => Boolean(part));
|
|
656
|
-
|
|
666
|
+
].filter((part): part is string => Boolean(part)).join(", ");
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
function formatAgentListLine(agent: AgentConfig, providerNames: Set<string> | undefined): string {
|
|
670
|
+
return `- ${agent.name} (${agentListMetadata(agent, providerNames)}): ${agent.description}`;
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
function formatAgentCapabilitiesLine(agent: AgentConfig, providerNames: Set<string> | undefined): string {
|
|
674
|
+
const declaredTools = [
|
|
675
|
+
...(agent.tools ?? []),
|
|
676
|
+
...(agent.mcpDirectTools ?? []).map((tool) => `mcp:${tool}`),
|
|
677
|
+
];
|
|
678
|
+
let tools = "none";
|
|
679
|
+
if (agent.tools === undefined && agent.mcpDirectTools === undefined) {
|
|
680
|
+
tools = "default/ambient";
|
|
681
|
+
} else if (declaredTools.length > 0) {
|
|
682
|
+
tools = declaredTools.join(", ");
|
|
683
|
+
}
|
|
684
|
+
let model = "inherits current session";
|
|
685
|
+
if (agent.model !== undefined) {
|
|
686
|
+
model = agent.model;
|
|
687
|
+
if (agent.modelProvider && !agent.model.includes("/")) model = `${agent.modelProvider}/${agent.model}`;
|
|
688
|
+
}
|
|
689
|
+
const thinking = agent.thinking === false ? "off" : agent.thinking ?? "default";
|
|
690
|
+
return `- ${agent.name} (${agentListMetadata(agent, providerNames)}): Description: ${previewDisplayText(agent.description, 240)}; Tools: ${tools}; Model: ${model}; Thinking: ${thinking}`;
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
const EXTERNAL_JOB_CAPABILITIES = { stop: false, steer: false, resume: false, structuredOutput: false, toolEvents: false } as const;
|
|
694
|
+
const PI_AGENT_RUNNER = { type: "pi" } as const;
|
|
695
|
+
|
|
696
|
+
function listOrEmpty<T>(values: T[] | undefined): T[] {
|
|
697
|
+
return values ?? [];
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
function agentCapabilityRunner(agent: AgentConfig, providerNames: Set<string> | undefined): AgentCapabilityRow["runner"] {
|
|
701
|
+
const runner = agent.runner;
|
|
702
|
+
if (!runner || runner.type === "pi") return PI_AGENT_RUNNER;
|
|
703
|
+
if (runner.type === "external-cli") return { type: "external-cli", adapter: runner.adapter, capabilities: resolveExternalCliRunnerStatus(runner).capabilities };
|
|
704
|
+
return { type: "external-job", provider: runner.provider, available: providerNames?.has(runner.provider), capabilities: EXTERNAL_JOB_CAPABILITIES };
|
|
705
|
+
}
|
|
706
|
+
|
|
707
|
+
function agentCapabilityTools(agent: AgentConfig): AgentCapabilityRow["tools"] {
|
|
708
|
+
return {
|
|
709
|
+
ambient: agent.tools === undefined && agent.mcpDirectTools === undefined,
|
|
710
|
+
names: listOrEmpty(agent.tools),
|
|
711
|
+
mcpDirectTools: listOrEmpty(agent.mcpDirectTools),
|
|
712
|
+
mutationTools: agent.mutationTools,
|
|
713
|
+
};
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
function agentCapabilityRow(agent: AgentConfig, options: { executable: boolean; providerNames?: Set<string>; restrictionSources?: string[] }): AgentCapabilityRow {
|
|
717
|
+
return {
|
|
718
|
+
name: agent.name,
|
|
719
|
+
description: previewDisplayText(agent.description, 1000),
|
|
720
|
+
source: agent.source,
|
|
721
|
+
executable: options.executable,
|
|
722
|
+
restrictionSources: options.executable ? undefined : options.restrictionSources ?? [],
|
|
723
|
+
aliases: agent.aliases ? [...agent.aliases] : undefined,
|
|
724
|
+
runner: agentCapabilityRunner(agent, options.providerNames),
|
|
725
|
+
tools: agentCapabilityTools(agent),
|
|
726
|
+
model: presentDetails({ value: agent.model, fallbackModels: agent.fallbackModels, thinking: agent.thinking }),
|
|
727
|
+
execution: presentDetails({ defaultAsync: agent.defaultAsync, timeoutMs: agent.defaultTimeoutMs }),
|
|
728
|
+
output: presentDetails({ path: agent.output, mode: agent.outputMode }),
|
|
729
|
+
extensions: presentDetails({ names: agent.extensions, subagentOnly: agent.subagentOnlyExtensions, skills: agent.skills }),
|
|
730
|
+
};
|
|
657
731
|
}
|
|
658
732
|
|
|
659
|
-
function
|
|
733
|
+
function agentCapabilitiesSnapshot(input: { agents: AgentConfig[]; restrictedAgents: AgentConfig[]; providerNames?: Set<string>; restrictedSources?: string[] }): AgentCapabilitiesSnapshot {
|
|
734
|
+
return {
|
|
735
|
+
agents: [
|
|
736
|
+
...input.agents.map((agent) => agentCapabilityRow(agent, { executable: true, providerNames: input.providerNames })),
|
|
737
|
+
...input.restrictedAgents.map((agent) => agentCapabilityRow(agent, { executable: false, providerNames: input.providerNames, restrictionSources: input.restrictedSources })),
|
|
738
|
+
],
|
|
739
|
+
restrictedCount: input.restrictedAgents.length,
|
|
740
|
+
...(input.restrictedSources?.length ? { capabilityCeilingSources: [...input.restrictedSources] } : {}),
|
|
741
|
+
};
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
function providerNames(status: ExternalJobProviderStatus): Set<string> | undefined {
|
|
745
|
+
return status.ok ? status.names : undefined;
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
function appendRestrictedAgentLines(input: { lines: string[]; agents: AgentConfig[]; sources?: string[]; providerNames?: Set<string>; formatLine: (agent: AgentConfig, providerNames: Set<string> | undefined) => string }): void {
|
|
749
|
+
if (input.agents.length === 0) return;
|
|
750
|
+
input.lines.push(
|
|
751
|
+
"",
|
|
752
|
+
`Restricted agents (not executable in this session${input.sources?.length ? `; capability ceiling: ${input.sources.join(", ")}` : ""}):`,
|
|
753
|
+
...input.agents.map((agent) => input.formatLine(agent, input.providerNames)),
|
|
754
|
+
);
|
|
755
|
+
}
|
|
756
|
+
|
|
757
|
+
function appendExternalJobRegistryLine(lines: string[], agents: AgentConfig[], status: ExternalJobProviderStatus): void {
|
|
758
|
+
if (status.ok || !agents.some((agent) => agent.runner?.type === "external-job")) return;
|
|
759
|
+
lines.push("", `External-job provider registry unavailable: ${status.error}`);
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
function appendAgentDiagnosticLines(lines: string[], diagnostics: AgentDiscoveryDiagnostic[] | undefined): void {
|
|
763
|
+
if (!diagnostics?.length) return;
|
|
764
|
+
lines.push(
|
|
765
|
+
"",
|
|
766
|
+
"Invalid agent definitions:",
|
|
767
|
+
...diagnostics.map((diagnostic) => `- ${diagnostic.name ?? diagnostic.filePath} (${diagnostic.source}): ${diagnostic.error}`),
|
|
768
|
+
);
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
function agentCapabilityDetails(input: { capabilityMode: boolean; agents: AgentConfig[]; restrictedAgents: AgentConfig[]; providerNames?: Set<string>; restrictedSources?: string[] }): Partial<Details> | undefined {
|
|
772
|
+
if (!input.capabilityMode) return undefined;
|
|
773
|
+
return {
|
|
774
|
+
agentCapabilities: jsonDetails(agentCapabilitiesSnapshot({
|
|
775
|
+
agents: input.agents,
|
|
776
|
+
restrictedAgents: input.restrictedAgents,
|
|
777
|
+
providerNames: input.providerNames,
|
|
778
|
+
restrictedSources: input.restrictedSources,
|
|
779
|
+
})),
|
|
780
|
+
};
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
function formatAgentListSections(
|
|
784
|
+
agents: AgentConfig[],
|
|
785
|
+
providerNames: Set<string> | undefined,
|
|
786
|
+
formatLine: (agent: AgentConfig, providerNames: Set<string> | undefined) => string = formatAgentListLine,
|
|
787
|
+
): string[] {
|
|
660
788
|
if (agents.length === 0) return ["- (none)"];
|
|
661
789
|
const sections: Array<[AgentSource, string]> = [
|
|
662
790
|
["package", "Package agents"],
|
|
@@ -670,7 +798,7 @@ function formatAgentListSections(agents: AgentConfig[], providerNames: Set<strin
|
|
|
670
798
|
const matches = agents.filter((agent) => agent.source === source);
|
|
671
799
|
if (matches.length === 0) continue;
|
|
672
800
|
if (lines.length > 0) lines.push("");
|
|
673
|
-
lines.push(label, ...matches.map((agent) =>
|
|
801
|
+
lines.push(label, ...matches.map((agent) => formatLine(agent, providerNames)));
|
|
674
802
|
}
|
|
675
803
|
return lines;
|
|
676
804
|
}
|
|
@@ -758,23 +886,24 @@ export function handleList(params: ManagementParams, ctx: ManagementContext): Ag
|
|
|
758
886
|
discoverAvailableSkills: () => discoverAvailableSkills(ctx.cwd),
|
|
759
887
|
});
|
|
760
888
|
const providerStatus = registeredExternalJobProviderStatus();
|
|
889
|
+
const providerNameSet = providerNames(providerStatus);
|
|
890
|
+
const capabilityMode = params.capabilities === true;
|
|
891
|
+
const formatLine = capabilityMode ? formatAgentCapabilitiesLine : formatAgentListLine;
|
|
761
892
|
const lines = [
|
|
762
|
-
"Executable agents:",
|
|
763
|
-
...formatAgentListSections(agents,
|
|
764
|
-
...(restrictedAgents.length ? [
|
|
765
|
-
"",
|
|
766
|
-
`Restricted agents (not executable in this session${restrictedSources?.length ? `; capability ceiling: ${restrictedSources.join(", ")}` : ""}):`,
|
|
767
|
-
...restrictedAgents.map((a) => formatAgentListLine(a, providerStatus.ok ? providerStatus.names : undefined)),
|
|
768
|
-
] : []),
|
|
769
|
-
...(!providerStatus.ok && [...agents, ...restrictedAgents].some((agent) => agent.runner?.type === "external-job") ? ["", `External-job provider registry unavailable: ${providerStatus.error}`] : []),
|
|
770
|
-
...(d.agentDiagnostics?.length ? [
|
|
771
|
-
"",
|
|
772
|
-
"Invalid agent definitions:",
|
|
773
|
-
...d.agentDiagnostics.map((diagnostic) => `- ${diagnostic.name ?? diagnostic.filePath} (${diagnostic.source}): ${diagnostic.error}`),
|
|
774
|
-
] : []),
|
|
775
|
-
...(proactiveSuggestions.length ? ["", ...proactiveSuggestions] : []),
|
|
893
|
+
capabilityMode ? "Executable agents (capabilities):" : "Executable agents:",
|
|
894
|
+
...formatAgentListSections(agents, providerNameSet, formatLine),
|
|
776
895
|
];
|
|
777
|
-
|
|
896
|
+
appendRestrictedAgentLines({ lines, agents: restrictedAgents, sources: restrictedSources, providerNames: providerNameSet, formatLine });
|
|
897
|
+
appendExternalJobRegistryLine(lines, [...agents, ...restrictedAgents], providerStatus);
|
|
898
|
+
appendAgentDiagnosticLines(lines, d.agentDiagnostics);
|
|
899
|
+
if (proactiveSuggestions.length) lines.push("", ...proactiveSuggestions);
|
|
900
|
+
return result(lines.join("\n"), false, agentCapabilityDetails({
|
|
901
|
+
capabilityMode,
|
|
902
|
+
agents,
|
|
903
|
+
restrictedAgents,
|
|
904
|
+
providerNames: providerNameSet,
|
|
905
|
+
restrictedSources,
|
|
906
|
+
}));
|
|
778
907
|
}
|
|
779
908
|
|
|
780
909
|
function formatModelSource(agent: AgentConfig, currentModel: ParentModel | undefined): string {
|
package/src/api/shared-types.ts
CHANGED
package/src/extension/schemas.ts
CHANGED
|
@@ -282,6 +282,7 @@ const SubagentParamProperties = {
|
|
|
282
282
|
action: Type.Optional(Type.String({ minLength: 1,
|
|
283
283
|
description: "Optional management/control action. Use action='validate' with workflowScript or workflowScriptPath for offline checks. Omit this field for structured single-child or workflow execution; otherwise, use it only for management/control actions."
|
|
284
284
|
})),
|
|
285
|
+
capabilities: Type.Optional(Type.Boolean({ description: "For action='list', return compact capability rows and structured details without system prompts." })),
|
|
285
286
|
name: Type.Optional(Type.String({ description: "Human-readable name for action='schedule.create'." })),
|
|
286
287
|
id: Type.Optional(Type.String({
|
|
287
288
|
description: "Run id/prefix for status/debug.run, interrupt, steer, or mission.attach-run."
|
|
@@ -9,23 +9,26 @@ const EXTERNAL_CLI_RUNNER_GUIDANCE = "External CLI agents (codex-exec, codex-exe
|
|
|
9
9
|
const WORKFLOW_RESUME_KEY_GUIDANCE = "Each workflow key identifies one result lane: use a new stable workflow key for every distinct retained resume pass; same-key calls are reused only when launch parameters are identical, and incompatible parameters are rejected.";
|
|
10
10
|
const WORKFLOW_OUTPUT_BINDING_GUIDANCE = "For durable workflow child files, set output on runs.run/runs.all; task filename prose is not an output declaration, and return the child's outputReference, outputPathMapping, or artifactPaths instead of inventing a literal path.";
|
|
11
11
|
const WORKFLOW_LANES_GUIDANCE = "For bounded parallel sequential chains, use runs.lanes([{key,stages:[{key,agent,task},{key,resume:'previous',task},...]}]); first stages run together, later stages sequence per lane, and the bounded board reports lane-local failures. Only an explicit structuredOutput.verdict === 'blocked' blocks a successful stage; reviewer prose is not parsed.";
|
|
12
|
-
const
|
|
12
|
+
const WORKFLOW_SCRIPT_PORTABILITY_GUIDANCE = "workflowScript rejects nested async function, arrow, and method helpers; use top-level await, plain helper functions that return runs.run(...), or explicit Promise chains instead.";
|
|
13
|
+
const WORKFLOW_HOST_GUIDANCE = "For one non-interactive operator-owned command, await runs.host(key,{kind:'command',command,timeoutMs,output?,role?,provider?}). runs.host has no per-step cwd: commands and relative output paths use the workflow cwd; set cwd on the outer subagent request instead (for example, {cwd:'/path/to/worktree',workflowScript:'...'}), or put a trusted directory change in the command (for example, 'cd /path/to/worktree && npm test'). v1 supports only command steps; output is bounded and command failure fails the workflow.";
|
|
14
|
+
const AGENT_CAPABILITY_GUIDANCE = "For capability selection, use { action: \"list\", capabilities: true } for compact prompt-free rows.";
|
|
13
15
|
|
|
14
|
-
export const DEFAULT_SUBAGENT_TOOL_DESCRIPTION = `Delegate to configured subagents. For execution, omit action and use {agent, task?} for one child, workflowScript for inline orchestration, or workflowScriptPath to load a script from the request cwd. The script inputs are mutually exclusive. Use action:'validate' with either script input to check it without launching children. For multi-step or parallel work, make exactly one top-level subagent call with async:true; launch children only inside that workflow and do not make another top-level call for them. Use runs.run('key',{agent,task}) for one child, await runs.all([{key:'a',agent:'reviewer',task:'...'},{key:'b',agent:'reviewer',task:'...'}]) for ordinary parallel children, and read its ordered array result with indexes, destructuring, or .map(...), not by key property. ${WORKFLOW_LANES_GUIDANCE} ${WORKFLOW_HOST_GUIDANCE} ${EXTERNAL_CLI_RUNNER_GUIDANCE} Use action only for management/control. Use guide or the pi-subagents skill for advanced workflow details.`;
|
|
16
|
+
export const DEFAULT_SUBAGENT_TOOL_DESCRIPTION = `Delegate to configured subagents. For execution, omit action and use {agent, task?} for one child, workflowScript for inline orchestration, or workflowScriptPath to load a script from the request cwd. The script inputs are mutually exclusive. Use action:'validate' with either script input to check it without launching children. For multi-step or parallel work, make exactly one top-level subagent call with async:true; launch children only inside that workflow and do not make another top-level call for them. Use runs.run('key',{agent,task}) for one child, await runs.all([{key:'a',agent:'reviewer',task:'...'},{key:'b',agent:'reviewer',task:'...'}]) for ordinary parallel children, and read its ordered array result with indexes, destructuring, or .map(...), not by key property. ${WORKFLOW_SCRIPT_PORTABILITY_GUIDANCE} ${WORKFLOW_LANES_GUIDANCE} ${WORKFLOW_HOST_GUIDANCE} ${EXTERNAL_CLI_RUNNER_GUIDANCE} Use action only for management/control. Use guide or the pi-subagents skill for advanced workflow details.`;
|
|
15
17
|
|
|
16
18
|
export const SUBAGENT_TOOL_PROMPT_SNIPPET = "Delegate to subagents; orchestrate in one workflowScript call.";
|
|
17
19
|
|
|
18
20
|
export const SUBAGENT_TOOL_PROMPT_GUIDELINES = [
|
|
19
|
-
|
|
21
|
+
`Use subagent only when delegation is needed. Before executing, call { action: "list" } and run only executable, non-disabled agents. ${AGENT_CAPABILITY_GUIDANCE}`,
|
|
20
22
|
"Omit action for execution. Use { agent, task? } only for one child; use workflowScript for multi-step or parallel work.",
|
|
21
23
|
"workflowScript means exactly one top-level subagent tool call with async:true. Inside it, use runs.run/runs.all to launch children; do not make another top-level subagent call for those children.",
|
|
24
|
+
WORKFLOW_SCRIPT_PORTABILITY_GUIDANCE,
|
|
22
25
|
WORKFLOW_LANES_GUIDANCE,
|
|
23
26
|
WORKFLOW_HOST_GUIDANCE,
|
|
24
27
|
WORKFLOW_RESUME_KEY_GUIDANCE,
|
|
25
28
|
WORKFLOW_OUTPUT_BINDING_GUIDANCE,
|
|
26
29
|
"For ordinary parallel work, use await runs.all([{key,agent,task}, ...]); it resolves to an ordered array, not a key map, so use results[0], destructuring, or results.map(...), not results.<key>. Do not read .output from unawaited runs.run launches. Stored runs.run promises are only for advanced rolling fanout and each must later be observed with direct await, Promise.race, or Promise.all.",
|
|
27
30
|
"Keep one writer per cwd/worktree unless writers run in isolated worktrees.",
|
|
28
|
-
"To pass an explicit model to a child, first call { action: \"models\" } and copy an exact provider/id (e.g.
|
|
31
|
+
"To pass an explicit model to a child, first call { action: \"models\" } and copy an exact provider/id (e.g. provider/model-id); bare ids resolve only when unique in the registry, and agent names (gpt-pro, advisor) are not model ids. Set per-run thinking with a suffix on the model string (e.g. provider/model-id:high; off/minimal/low/medium/high/xhigh/max); the suffix wins over the agent's thinking default. The thinking field only applies to action='watchdog.configure' and is ignored on dispatch.",
|
|
29
32
|
EXTERNAL_CLI_RUNNER_GUIDANCE,
|
|
30
33
|
"Use guide or the pi-subagents skill for advanced scheduling, missions, steering, and retention.",
|
|
31
34
|
];
|
|
@@ -85,6 +88,7 @@ ASYNC / SAFETY:
|
|
|
85
88
|
• Omitted async follows asyncByDefault config; set async:true explicitly when async behavior matters. Continue independent work only until its next dependency barrier; consume the result before work that depends on it. Do not sleep or poll merely to wait; use subagent_wait only when this turn must receive results.
|
|
86
89
|
• ${WORKFLOW_RESUME_KEY_GUIDANCE}
|
|
87
90
|
• ${WORKFLOW_OUTPUT_BINDING_GUIDANCE}
|
|
91
|
+
• ${WORKFLOW_HOST_GUIDANCE}
|
|
88
92
|
• Ordinary children are not orchestrators. Keep one writer per cwd/worktree and use fresh read-only reviewers for independent checks.
|
|
89
93
|
• Oracle/advisor consultations use available supervisor dialogue for material unknowns; request one-shot when desired.
|
|
90
94
|
• Status and artifacts live under asyncId/asyncDir with status.json, events.jsonl, output logs, and {action:"status",id:"..."}.`;
|
|
@@ -22,6 +22,7 @@ import type { ContextMode } from "../shared/context-mode.ts";
|
|
|
22
22
|
import { resolvePiPackageRoot } from "../shared/pi-spawn.ts";
|
|
23
23
|
import { preflightLaunchCwd } from "../shared/launch-cwd.ts";
|
|
24
24
|
import { resolveNodeExecutable } from "../../shared/node-executable.ts";
|
|
25
|
+
import { backgroundProcessOptions } from "../shared/background-process-options.ts";
|
|
25
26
|
import { buildSkillInjection, normalizeSkillInput, resolveSkillsWithFallback } from "../../agents/skills.ts";
|
|
26
27
|
import { buildAgentMemoryInjection } from "../../agents/agent-memory.ts";
|
|
27
28
|
import { PI_CODING_AGENT_PACKAGE_ROOT_ENV, PROMPT_REDACTED, resolveChildCwd } from "../../shared/utils.ts";
|
|
@@ -562,9 +563,8 @@ function spawnRunner(cfg: object, suffix: string, cwd: string, initialStatus: Om
|
|
|
562
563
|
}
|
|
563
564
|
const proc = spawn(nodeCommand, [jitiCliPath, runner, cfgPath], {
|
|
564
565
|
cwd,
|
|
565
|
-
|
|
566
|
+
...backgroundProcessOptions(),
|
|
566
567
|
stdio: ["ignore", stdoutFd ?? "ignore", stderrFd ?? "ignore"],
|
|
567
|
-
windowsHide: true,
|
|
568
568
|
env: {
|
|
569
569
|
...omitExtensionBindingsEnv(process.env),
|
|
570
570
|
...(piPackageRoot ? { [PI_CODING_AGENT_PACKAGE_ROOT_ENV]: piPackageRoot } : {}),
|
|
@@ -146,6 +146,7 @@ export function createAsyncJobTracker(pi: Pick<ExtensionAPI, "events">, state: S
|
|
|
146
146
|
chainStepCount: run.chainStepCount,
|
|
147
147
|
parallelGroups: groups,
|
|
148
148
|
hostSteps: run.hostSteps,
|
|
149
|
+
...(run.mode === "workflow" && run.workflowGraph ? { workflowGraph: run.workflowGraph } : {}),
|
|
149
150
|
preflight: run.preflight,
|
|
150
151
|
steps: visibleSteps,
|
|
151
152
|
stepsTotal: visibleSteps.length,
|
|
@@ -401,6 +402,7 @@ export function createAsyncJobTracker(pi: Pick<ExtensionAPI, "events">, state: S
|
|
|
401
402
|
job.workflowKey = status.workflowKey ?? job.workflowKey;
|
|
402
403
|
job.lane = status.lane ?? job.lane;
|
|
403
404
|
job.workflow = status.workflow ?? job.workflow;
|
|
405
|
+
if (status.mode === "workflow") job.workflowGraph = status.workflowGraph ?? job.workflowGraph;
|
|
404
406
|
job.hostSteps = validHostStepNodes(status.workflowGraph);
|
|
405
407
|
const workflowChildren = parseWorkflowChildSummary(status.workflowChildren);
|
|
406
408
|
if (workflowChildren && workflowChildren.workflowRunId !== status.runId) throw new Error("workflowChildren.workflowRunId does not match async status runId.");
|
|
@@ -627,6 +629,7 @@ export function createAsyncJobTracker(pi: Pick<ExtensionAPI, "events">, state: S
|
|
|
627
629
|
turnBudget: info.turnBudget,
|
|
628
630
|
parentWorkflowRunId: info.parentWorkflowRunId,
|
|
629
631
|
workflowKey: info.workflowKey,
|
|
632
|
+
...(info.mode === "workflow" && info.workflowGraph ? { workflowGraph: info.workflowGraph } : {}),
|
|
630
633
|
controlEventCursor: 0,
|
|
631
634
|
});
|
|
632
635
|
const job = state.asyncJobs.get(info.id)!;
|