@narumitw/pi-subagents 0.11.0 → 0.13.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/README.md +53 -24
- package/package.json +1 -1
- package/src/agents.ts +15 -0
- package/src/subagents.ts +83 -19
package/README.md
CHANGED
|
@@ -14,7 +14,7 @@ Use it to split research, planning, implementation, and review work across focus
|
|
|
14
14
|
- Loads custom user agents from `~/.pi/agent/agents/*.md`.
|
|
15
15
|
- Optionally loads project agents from `.pi/agents/*.md` with confirmation.
|
|
16
16
|
- Provides `/subagents:config` to persist per-agent tool allow-lists.
|
|
17
|
-
- Supports per-task `cwd`, hard subprocess `timeoutMs`, abort propagation, and streaming progress.
|
|
17
|
+
- Supports per-task `cwd`, hard subprocess `timeoutMs`, `thinkingLevel`, abort propagation, and streaming progress.
|
|
18
18
|
- Publishes transient runtime status through Pi's generic extension status API while subagents are running.
|
|
19
19
|
- Returns complete worker output in tool details and a concise result for the main agent.
|
|
20
20
|
|
|
@@ -49,29 +49,50 @@ Execution modes:
|
|
|
49
49
|
- **parallel + aggregator** — run parallel jobs, then pass all outputs into one fan-in agent.
|
|
50
50
|
- **chain** — run sequential steps, passing prior output with `{previous}`.
|
|
51
51
|
|
|
52
|
+
Common controls:
|
|
53
|
+
|
|
54
|
+
- `cwd` — run a job from a different working directory.
|
|
55
|
+
- `timeoutMs` — set a hard subprocess timeout.
|
|
56
|
+
- `thinkingLevel` — request `off`, `minimal`, `low`, `medium`, `high`, or `xhigh` thinking for the spawned Pi process.
|
|
57
|
+
|
|
52
58
|
## 🧭 Proactive use
|
|
53
59
|
|
|
54
|
-
The `subagent` tool
|
|
55
|
-
|
|
60
|
+
The `subagent` tool advertises concise prompt guidance so the main Pi agent can decide
|
|
61
|
+
whether to spawn 0, 1, or multiple subagents without an explicit user-specified count.
|
|
62
|
+
|
|
63
|
+
Count-selection guidance:
|
|
56
64
|
|
|
57
|
-
Use
|
|
65
|
+
- Use **no subagent** for simple answers, quick targeted edits, latency-sensitive one-step work, or
|
|
66
|
+
tasks that need frequent user back-and-forth.
|
|
67
|
+
- Use **one subagent** for isolated research, high-volume command output, planning, or independent
|
|
68
|
+
review/verification after implementation.
|
|
69
|
+
- Prefer **2–4 parallel read-only subagents** when a broad task naturally splits into independent
|
|
70
|
+
branches that can each return a concise summary.
|
|
71
|
+
- Exceed 4 tasks only when the branches are clearly distinct and worth the extra cost, while staying
|
|
72
|
+
within the existing hard max of 8 parallel tasks.
|
|
73
|
+
- Do not parallelize implementation that may edit the same files or shared state; serialize
|
|
74
|
+
write-heavy work instead.
|
|
75
|
+
- Do not use project-local agents unless the user explicitly opts into them with
|
|
76
|
+
`agentScope: "project"` or `"both"`; keep confirmation enabled for untrusted repositories.
|
|
58
77
|
|
|
59
|
-
|
|
60
|
-
that would clutter the main context.
|
|
61
|
-
- Parallel multi-domain investigation where each branch can return a concise summary.
|
|
62
|
-
- Independent review or verification after implementation, especially with the read-only
|
|
63
|
-
`reviewer` agent.
|
|
78
|
+
Examples where the main agent chooses the count:
|
|
64
79
|
|
|
65
|
-
|
|
80
|
+
No subagent for a known-file edit:
|
|
66
81
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
instead.
|
|
71
|
-
- Project-local agents unless the user explicitly opts into them with `agentScope: "project"` or
|
|
72
|
-
`"both"`; keep confirmation enabled for untrusted repositories.
|
|
82
|
+
```txt
|
|
83
|
+
Rename one symbol in src/foo.ts.
|
|
84
|
+
```
|
|
73
85
|
|
|
74
|
-
|
|
86
|
+
One subagent for an independent review:
|
|
87
|
+
|
|
88
|
+
```json
|
|
89
|
+
{
|
|
90
|
+
"agent": "reviewer",
|
|
91
|
+
"task": "Review the current changes for release blockers. Do not edit files. Report PASS/FAIL/PARTIAL with evidence."
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
Two to four parallel subagents for broad independent reconnaissance:
|
|
75
96
|
|
|
76
97
|
```json
|
|
77
98
|
{
|
|
@@ -83,6 +104,10 @@ Good delegation example:
|
|
|
83
104
|
{
|
|
84
105
|
"agent": "scout",
|
|
85
106
|
"task": "Research auth-related tests. Report coverage gaps. Do not edit files."
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
"agent": "scout",
|
|
110
|
+
"task": "Research API entry points that depend on auth. Report integration risks. Do not edit files."
|
|
86
111
|
}
|
|
87
112
|
],
|
|
88
113
|
"aggregator": {
|
|
@@ -92,9 +117,6 @@ Good delegation example:
|
|
|
92
117
|
}
|
|
93
118
|
```
|
|
94
119
|
|
|
95
|
-
Bad delegation example: do not spawn a worker just to rename one symbol in a known file; edit it
|
|
96
|
-
directly in the main conversation.
|
|
97
|
-
|
|
98
120
|
## 🚀 Examples
|
|
99
121
|
|
|
100
122
|
Run one read-only reconnaissance agent:
|
|
@@ -106,7 +128,7 @@ Run one read-only reconnaissance agent:
|
|
|
106
128
|
}
|
|
107
129
|
```
|
|
108
130
|
|
|
109
|
-
Run multiple agents in parallel:
|
|
131
|
+
Run multiple agents in parallel with a shared thinking level and one per-task override:
|
|
110
132
|
|
|
111
133
|
```json
|
|
112
134
|
{
|
|
@@ -114,14 +136,16 @@ Run multiple agents in parallel:
|
|
|
114
136
|
{
|
|
115
137
|
"agent": "scout",
|
|
116
138
|
"task": "Map package metadata files",
|
|
117
|
-
"timeoutMs": 30000
|
|
139
|
+
"timeoutMs": 30000,
|
|
140
|
+
"thinkingLevel": "low"
|
|
118
141
|
},
|
|
119
142
|
{
|
|
120
143
|
"agent": "reviewer",
|
|
121
144
|
"task": "Review TypeScript config consistency"
|
|
122
145
|
}
|
|
123
146
|
],
|
|
124
|
-
"timeoutMs": 120000
|
|
147
|
+
"timeoutMs": 120000,
|
|
148
|
+
"thinkingLevel": "medium"
|
|
125
149
|
}
|
|
126
150
|
```
|
|
127
151
|
|
|
@@ -196,6 +220,7 @@ name: api-reviewer
|
|
|
196
220
|
description: Review API changes for compatibility and tests
|
|
197
221
|
tools: read, grep, find, ls, bash
|
|
198
222
|
model: sonnet
|
|
223
|
+
thinkingLevel: high
|
|
199
224
|
---
|
|
200
225
|
|
|
201
226
|
You are an API review subagent. Do not edit files. Check compatibility,
|
|
@@ -204,7 +229,7 @@ test coverage, and migration risks. Report PASS/FAIL/PARTIAL with evidence.
|
|
|
204
229
|
|
|
205
230
|
By default, `subagent` loads user agents only. Set `agentScope` to `"project"` or `"both"` to load project-local agents. Interactive sessions ask for confirmation before using project agents unless `confirmProjectAgents` is disabled.
|
|
206
231
|
|
|
207
|
-
## ⏱️ Runtime limits
|
|
232
|
+
## ⏱️ Runtime limits and thinking levels
|
|
208
233
|
|
|
209
234
|
Each subprocess has a hard timeout to avoid runaway workers.
|
|
210
235
|
|
|
@@ -212,6 +237,10 @@ Each subprocess has a hard timeout to avoid runaway workers.
|
|
|
212
237
|
- Set `timeoutMs` on a task, chain step, or aggregator to override it locally.
|
|
213
238
|
- If omitted, the default is `PI_SUBAGENT_TIMEOUT_MS`, or `600000` milliseconds (10 minutes) when unset.
|
|
214
239
|
|
|
240
|
+
Set `thinkingLevel` to pass Pi's `--thinking <level>` to a subprocess. Supported values are `off`, `minimal`, `low`, `medium`, `high`, and `xhigh`.
|
|
241
|
+
|
|
242
|
+
Thinking-level precedence is: task/chain step/aggregator `thinkingLevel` → top-level `thinkingLevel` → agent default from config or frontmatter → Pi subprocess default. Omit `thinkingLevel` to preserve existing behavior. Pi still owns model capability clamping, so unsupported thinking levels are handled by the spawned Pi process.
|
|
243
|
+
|
|
215
244
|
On timeout, the extension sends `SIGTERM`, escalates to `SIGKILL` after a short grace period, and returns any partial messages or stderr collected so far.
|
|
216
245
|
|
|
217
246
|
## 📡 Runtime status
|
package/package.json
CHANGED
package/src/agents.ts
CHANGED
|
@@ -4,8 +4,17 @@
|
|
|
4
4
|
|
|
5
5
|
import * as fs from "node:fs";
|
|
6
6
|
import * as path from "node:path";
|
|
7
|
+
import type { ThinkingLevel } from "@earendil-works/pi-agent-core";
|
|
7
8
|
import { getAgentDir, parseFrontmatter } from "@earendil-works/pi-coding-agent";
|
|
8
9
|
|
|
10
|
+
export const THINKING_LEVELS = ["off", "minimal", "low", "medium", "high", "xhigh"] as const satisfies readonly ThinkingLevel[];
|
|
11
|
+
|
|
12
|
+
export type SubagentThinkingLevel = (typeof THINKING_LEVELS)[number];
|
|
13
|
+
|
|
14
|
+
export function isThinkingLevel(value: unknown): value is SubagentThinkingLevel {
|
|
15
|
+
return typeof value === "string" && THINKING_LEVELS.includes(value as SubagentThinkingLevel);
|
|
16
|
+
}
|
|
17
|
+
|
|
9
18
|
export type AgentScope = "user" | "project" | "both";
|
|
10
19
|
|
|
11
20
|
export type AgentSource = "built-in" | "user" | "project";
|
|
@@ -15,6 +24,7 @@ export interface AgentConfig {
|
|
|
15
24
|
description: string;
|
|
16
25
|
tools?: string[];
|
|
17
26
|
model?: string;
|
|
27
|
+
thinkingLevel?: SubagentThinkingLevel;
|
|
18
28
|
timeoutMs?: number;
|
|
19
29
|
systemPrompt: string;
|
|
20
30
|
source: AgentSource;
|
|
@@ -24,6 +34,7 @@ export interface AgentConfig {
|
|
|
24
34
|
export interface SubagentAgentConfig {
|
|
25
35
|
tools?: string[];
|
|
26
36
|
model?: string | null;
|
|
37
|
+
thinkingLevel?: SubagentThinkingLevel | null;
|
|
27
38
|
timeoutMs?: number | null;
|
|
28
39
|
}
|
|
29
40
|
|
|
@@ -146,6 +157,7 @@ function loadAgentsFromDir(dir: string, source: "user" | "project"): AgentConfig
|
|
|
146
157
|
description: frontmatter.description,
|
|
147
158
|
tools: tools && tools.length > 0 ? tools : undefined,
|
|
148
159
|
model: frontmatter.model,
|
|
160
|
+
thinkingLevel: isThinkingLevel(frontmatter.thinkingLevel) ? frontmatter.thinkingLevel : undefined,
|
|
149
161
|
systemPrompt: body,
|
|
150
162
|
source,
|
|
151
163
|
filePath,
|
|
@@ -217,6 +229,9 @@ export function discoverAgents(
|
|
|
217
229
|
if (hasOwn(override, "model")) {
|
|
218
230
|
nextAgent.model = override.model === null ? undefined : override.model;
|
|
219
231
|
}
|
|
232
|
+
if (hasOwn(override, "thinkingLevel")) {
|
|
233
|
+
nextAgent.thinkingLevel = override.thinkingLevel === null ? undefined : override.thinkingLevel;
|
|
234
|
+
}
|
|
220
235
|
if (hasOwn(override, "timeoutMs")) {
|
|
221
236
|
nextAgent.timeoutMs = override.timeoutMs === null ? undefined : override.timeoutMs;
|
|
222
237
|
}
|
package/src/subagents.ts
CHANGED
|
@@ -39,12 +39,15 @@ import {
|
|
|
39
39
|
} from "@earendil-works/pi-tui";
|
|
40
40
|
import { Type } from "typebox";
|
|
41
41
|
import {
|
|
42
|
+
THINKING_LEVELS,
|
|
42
43
|
type AgentConfig,
|
|
43
44
|
type AgentScope,
|
|
44
45
|
type AgentSource,
|
|
45
46
|
type SubagentAgentConfig,
|
|
46
47
|
type SubagentSettings,
|
|
48
|
+
type SubagentThinkingLevel,
|
|
47
49
|
discoverAgents,
|
|
50
|
+
isThinkingLevel,
|
|
48
51
|
} from "./agents.js";
|
|
49
52
|
|
|
50
53
|
const MAX_PARALLEL_TASKS = 8;
|
|
@@ -132,6 +135,7 @@ export function formatUsageStats(
|
|
|
132
135
|
turns?: number;
|
|
133
136
|
},
|
|
134
137
|
model?: string,
|
|
138
|
+
thinkingLevel?: SubagentThinkingLevel,
|
|
135
139
|
): string {
|
|
136
140
|
const parts: string[] = [];
|
|
137
141
|
if (usage.turns) parts.push(`${usage.turns} turn${usage.turns > 1 ? "s" : ""}`);
|
|
@@ -144,6 +148,7 @@ export function formatUsageStats(
|
|
|
144
148
|
parts.push(`ctx:${formatTokens(usage.contextTokens)}`);
|
|
145
149
|
}
|
|
146
150
|
if (model) parts.push(model);
|
|
151
|
+
if (thinkingLevel) parts.push(`thinking:${thinkingLevel}`);
|
|
147
152
|
return parts.join(" ");
|
|
148
153
|
}
|
|
149
154
|
|
|
@@ -234,6 +239,7 @@ interface SingleResult {
|
|
|
234
239
|
stderr: string;
|
|
235
240
|
usage: UsageStats;
|
|
236
241
|
model?: string;
|
|
242
|
+
thinkingLevel?: SubagentThinkingLevel;
|
|
237
243
|
stopReason?: string;
|
|
238
244
|
errorMessage?: string;
|
|
239
245
|
step?: number;
|
|
@@ -326,6 +332,25 @@ async function writePromptToTempFile(agentName: string, prompt: string): Promise
|
|
|
326
332
|
return { dir: tmpDir, filePath };
|
|
327
333
|
}
|
|
328
334
|
|
|
335
|
+
export function buildPiArgs(options: {
|
|
336
|
+
model?: string;
|
|
337
|
+
thinkingLevel?: SubagentThinkingLevel;
|
|
338
|
+
tools?: string[];
|
|
339
|
+
systemPromptPath?: string;
|
|
340
|
+
task: string;
|
|
341
|
+
}): string[] {
|
|
342
|
+
const args: string[] = ["--mode", "json", "-p", "--no-session"];
|
|
343
|
+
if (options.model) args.push("--model", options.model);
|
|
344
|
+
if (options.thinkingLevel) args.push("--thinking", options.thinkingLevel);
|
|
345
|
+
if (Array.isArray(options.tools)) {
|
|
346
|
+
if (options.tools.length > 0) args.push("--tools", options.tools.join(","));
|
|
347
|
+
else args.push("--no-tools");
|
|
348
|
+
}
|
|
349
|
+
if (options.systemPromptPath) args.push("--append-system-prompt", options.systemPromptPath);
|
|
350
|
+
args.push(`Task: ${options.task}`);
|
|
351
|
+
return args;
|
|
352
|
+
}
|
|
353
|
+
|
|
329
354
|
function getPiInvocation(args: string[]): { command: string; args: string[] } {
|
|
330
355
|
const currentScript = process.argv[1];
|
|
331
356
|
const isBunVirtualScript = currentScript?.startsWith("/$bunfs/root/");
|
|
@@ -378,6 +403,7 @@ async function runSingleAgent(
|
|
|
378
403
|
cwd: string | undefined,
|
|
379
404
|
step: number | undefined,
|
|
380
405
|
signal: AbortSignal | undefined,
|
|
406
|
+
thinkingLevel: SubagentThinkingLevel | undefined,
|
|
381
407
|
timeoutMs: number,
|
|
382
408
|
onUpdate: OnUpdateCallback | undefined,
|
|
383
409
|
makeDetails: (results: SingleResult[]) => SubagentDetails,
|
|
@@ -394,18 +420,12 @@ async function runSingleAgent(
|
|
|
394
420
|
messages: [],
|
|
395
421
|
stderr: `Unknown agent: "${agentName}". Available agents: ${available}.`,
|
|
396
422
|
usage: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: 0, contextTokens: 0, turns: 0 },
|
|
423
|
+
thinkingLevel,
|
|
397
424
|
step,
|
|
398
425
|
finalOutput: "",
|
|
399
426
|
};
|
|
400
427
|
}
|
|
401
428
|
|
|
402
|
-
const args: string[] = ["--mode", "json", "-p", "--no-session"];
|
|
403
|
-
if (agent.model) args.push("--model", agent.model);
|
|
404
|
-
if (Array.isArray(agent.tools)) {
|
|
405
|
-
if (agent.tools.length > 0) args.push("--tools", agent.tools.join(","));
|
|
406
|
-
else args.push("--no-tools");
|
|
407
|
-
}
|
|
408
|
-
|
|
409
429
|
let tmpPromptDir: string | null = null;
|
|
410
430
|
let tmpPromptPath: string | null = null;
|
|
411
431
|
|
|
@@ -418,6 +438,7 @@ async function runSingleAgent(
|
|
|
418
438
|
stderr: "",
|
|
419
439
|
usage: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: 0, contextTokens: 0, turns: 0 },
|
|
420
440
|
model: agent.model ?? undefined,
|
|
441
|
+
thinkingLevel,
|
|
421
442
|
step,
|
|
422
443
|
timeoutMs,
|
|
423
444
|
};
|
|
@@ -437,10 +458,15 @@ async function runSingleAgent(
|
|
|
437
458
|
const tmp = await writePromptToTempFile(agent.name, agent.systemPrompt);
|
|
438
459
|
tmpPromptDir = tmp.dir;
|
|
439
460
|
tmpPromptPath = tmp.filePath;
|
|
440
|
-
args.push("--append-system-prompt", tmpPromptPath);
|
|
441
461
|
}
|
|
442
462
|
|
|
443
|
-
args
|
|
463
|
+
const args = buildPiArgs({
|
|
464
|
+
model: agent.model,
|
|
465
|
+
thinkingLevel,
|
|
466
|
+
tools: agent.tools,
|
|
467
|
+
systemPromptPath: tmpPromptPath ?? undefined,
|
|
468
|
+
task,
|
|
469
|
+
});
|
|
444
470
|
let wasAborted = false;
|
|
445
471
|
let timedOut = false;
|
|
446
472
|
|
|
@@ -568,11 +594,16 @@ const TimeoutMs = Type.Number({
|
|
|
568
594
|
minimum: 1,
|
|
569
595
|
});
|
|
570
596
|
|
|
597
|
+
const ThinkingLevelSchema = StringEnum(THINKING_LEVELS, {
|
|
598
|
+
description: "Pi thinking level for the subagent process: off, minimal, low, medium, high, or xhigh.",
|
|
599
|
+
});
|
|
600
|
+
|
|
571
601
|
const TaskItem = Type.Object({
|
|
572
602
|
agent: Type.String({ description: "Name of the agent to invoke" }),
|
|
573
603
|
task: Type.String({ description: "Task to delegate to the agent" }),
|
|
574
604
|
cwd: Type.Optional(Type.String({ description: "Working directory for the agent process" })),
|
|
575
605
|
timeoutMs: Type.Optional(TimeoutMs),
|
|
606
|
+
thinkingLevel: Type.Optional(ThinkingLevelSchema),
|
|
576
607
|
});
|
|
577
608
|
|
|
578
609
|
const ChainItem = Type.Object({
|
|
@@ -580,6 +611,7 @@ const ChainItem = Type.Object({
|
|
|
580
611
|
task: Type.String({ description: "Task with optional {previous} placeholder for prior output" }),
|
|
581
612
|
cwd: Type.Optional(Type.String({ description: "Working directory for the agent process" })),
|
|
582
613
|
timeoutMs: Type.Optional(TimeoutMs),
|
|
614
|
+
thinkingLevel: Type.Optional(ThinkingLevelSchema),
|
|
583
615
|
});
|
|
584
616
|
|
|
585
617
|
const AggregatorItem = Type.Object({
|
|
@@ -587,6 +619,7 @@ const AggregatorItem = Type.Object({
|
|
|
587
619
|
task: Type.String({ description: "Fan-in task. Use {previous} to include all parallel outputs." }),
|
|
588
620
|
cwd: Type.Optional(Type.String({ description: "Working directory for the aggregator process" })),
|
|
589
621
|
timeoutMs: Type.Optional(TimeoutMs),
|
|
622
|
+
thinkingLevel: Type.Optional(ThinkingLevelSchema),
|
|
590
623
|
});
|
|
591
624
|
|
|
592
625
|
const AgentScopeSchema = StringEnum(["user", "project", "both"] as const, {
|
|
@@ -606,6 +639,7 @@ const SubagentParams = Type.Object({
|
|
|
606
639
|
),
|
|
607
640
|
cwd: Type.Optional(Type.String({ description: "Working directory for the agent process (single mode)" })),
|
|
608
641
|
timeoutMs: Type.Optional(TimeoutMs),
|
|
642
|
+
thinkingLevel: Type.Optional(ThinkingLevelSchema),
|
|
609
643
|
});
|
|
610
644
|
|
|
611
645
|
// ---- Settings helpers ----
|
|
@@ -644,6 +678,12 @@ export function normalizeAgentSettings(value: unknown): SubagentAgentConfig | un
|
|
|
644
678
|
hasKnownField = true;
|
|
645
679
|
}
|
|
646
680
|
|
|
681
|
+
if (hasOwn(value, "thinkingLevel")) {
|
|
682
|
+
if (value.thinkingLevel !== null && !isThinkingLevel(value.thinkingLevel)) return undefined;
|
|
683
|
+
config.thinkingLevel = value.thinkingLevel;
|
|
684
|
+
hasKnownField = true;
|
|
685
|
+
}
|
|
686
|
+
|
|
647
687
|
if (hasOwn(value, "timeoutMs")) {
|
|
648
688
|
if (value.timeoutMs !== null && !isPositiveNumber(value.timeoutMs)) return undefined;
|
|
649
689
|
config.timeoutMs = value.timeoutMs;
|
|
@@ -696,8 +736,22 @@ export function sameToolSet(left: string[], right: string[]): boolean {
|
|
|
696
736
|
return [...leftSet].every((tool) => rightSet.has(tool));
|
|
697
737
|
}
|
|
698
738
|
|
|
739
|
+
export function resolveSubagentThinkingLevel(
|
|
740
|
+
agents: readonly Pick<AgentConfig, "name" | "thinkingLevel">[],
|
|
741
|
+
agentName: string,
|
|
742
|
+
topLevelThinkingLevel?: SubagentThinkingLevel,
|
|
743
|
+
localThinkingLevel?: SubagentThinkingLevel,
|
|
744
|
+
): SubagentThinkingLevel | undefined {
|
|
745
|
+
return localThinkingLevel ?? topLevelThinkingLevel ?? agents.find((agent) => agent.name === agentName)?.thinkingLevel;
|
|
746
|
+
}
|
|
747
|
+
|
|
699
748
|
function hasAnyAgentOverride(config: SubagentAgentConfig): boolean {
|
|
700
|
-
return
|
|
749
|
+
return (
|
|
750
|
+
hasOwn(config, "tools") ||
|
|
751
|
+
hasOwn(config, "model") ||
|
|
752
|
+
hasOwn(config, "thinkingLevel") ||
|
|
753
|
+
hasOwn(config, "timeoutMs")
|
|
754
|
+
);
|
|
701
755
|
}
|
|
702
756
|
|
|
703
757
|
// ---- Tool toggle component ----
|
|
@@ -770,11 +824,14 @@ export default function (pi: ExtensionAPI) {
|
|
|
770
824
|
'To enable project-local agents in .pi/agents, set agentScope: "both" (or "project").',
|
|
771
825
|
].join(" "),
|
|
772
826
|
promptSnippet:
|
|
773
|
-
"
|
|
827
|
+
"Decide whether to spawn 0, 1, or multiple subagents for independent research, review, verification, or multi-step work in isolated Pi processes.",
|
|
774
828
|
promptGuidelines: [
|
|
775
|
-
"Use subagent
|
|
776
|
-
"Use subagent
|
|
777
|
-
"
|
|
829
|
+
"Use subagent only when delegation fits; the main agent should decide how many subagents to spawn from task shape instead of waiting for the user to specify a count.",
|
|
830
|
+
"Use no subagent for simple answers, quick targeted edits, latency-sensitive one-step work, or tasks requiring frequent user back-and-forth.",
|
|
831
|
+
"Use one subagent for isolated research, broad command output, planning, or independent review/verification that benefits from a separate context.",
|
|
832
|
+
"Use subagent parallel mode with 2-4 parallel read-only subagents when work has broad independent branches; prefer scout or reviewer for fan-out and add an aggregator when synthesis helps.",
|
|
833
|
+
"Use more than 4 subagent tasks only when clearly justified by distinct independent branches, and stay within the existing hard max 8 parallel tasks.",
|
|
834
|
+
"Do not use subagent parallel mode for write-heavy implementation touching the same files or shared state; serialize those changes in the main agent or one worker.",
|
|
778
835
|
'Do not use subagent with project-local agents unless the user explicitly wants project agents or sets agentScope to "project" or "both"; keep confirmation enabled for untrusted repositories.',
|
|
779
836
|
"When using subagent, write self-contained tasks with file paths, context, expected output, and whether the subagent may edit files.",
|
|
780
837
|
],
|
|
@@ -791,6 +848,8 @@ export default function (pi: ExtensionAPI) {
|
|
|
791
848
|
params.timeoutMs ??
|
|
792
849
|
agents.find((agent) => agent.name === agentName)?.timeoutMs ??
|
|
793
850
|
DEFAULT_TIMEOUT_MS;
|
|
851
|
+
const resolveThinkingLevel = (agentName: string, localThinkingLevel?: SubagentThinkingLevel) =>
|
|
852
|
+
resolveSubagentThinkingLevel(agents, agentName, params.thinkingLevel, localThinkingLevel);
|
|
794
853
|
|
|
795
854
|
const hasChain = (params.chain?.length ?? 0) > 0;
|
|
796
855
|
const hasTasks = (params.tasks?.length ?? 0) > 0;
|
|
@@ -884,6 +943,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
884
943
|
step.cwd,
|
|
885
944
|
i + 1,
|
|
886
945
|
signal,
|
|
946
|
+
resolveThinkingLevel(step.agent, step.thinkingLevel),
|
|
887
947
|
resolveTimeoutMs(step.agent, step.timeoutMs),
|
|
888
948
|
chainUpdate,
|
|
889
949
|
makeDetails("chain"),
|
|
@@ -939,6 +999,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
939
999
|
messages: [],
|
|
940
1000
|
stderr: "",
|
|
941
1001
|
usage: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0, cost: 0, contextTokens: 0, turns: 0 },
|
|
1002
|
+
thinkingLevel: resolveThinkingLevel(params.tasks[i].agent, params.tasks[i].thinkingLevel),
|
|
942
1003
|
finalOutput: "",
|
|
943
1004
|
};
|
|
944
1005
|
}
|
|
@@ -970,6 +1031,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
970
1031
|
t.cwd,
|
|
971
1032
|
undefined,
|
|
972
1033
|
signal,
|
|
1034
|
+
resolveThinkingLevel(t.agent, t.thinkingLevel),
|
|
973
1035
|
resolveTimeoutMs(t.agent, t.timeoutMs),
|
|
974
1036
|
// Per-task update callback
|
|
975
1037
|
(partial) => {
|
|
@@ -1003,6 +1065,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
1003
1065
|
aggregator.cwd,
|
|
1004
1066
|
undefined,
|
|
1005
1067
|
signal,
|
|
1068
|
+
resolveThinkingLevel(aggregator.agent, aggregator.thinkingLevel),
|
|
1006
1069
|
resolveTimeoutMs(aggregator.agent, aggregator.timeoutMs),
|
|
1007
1070
|
(partial) => {
|
|
1008
1071
|
status.update(fanInStatus(aggregator.agent));
|
|
@@ -1060,6 +1123,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
1060
1123
|
params.cwd,
|
|
1061
1124
|
undefined,
|
|
1062
1125
|
signal,
|
|
1126
|
+
resolveThinkingLevel(params.agent, params.thinkingLevel),
|
|
1063
1127
|
resolveTimeoutMs(params.agent, params.timeoutMs),
|
|
1064
1128
|
onUpdate,
|
|
1065
1129
|
makeDetails("single"),
|
|
@@ -1203,7 +1267,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
1203
1267
|
container.addChild(new Markdown(finalOutput.trim(), 0, 0, mdTheme));
|
|
1204
1268
|
}
|
|
1205
1269
|
}
|
|
1206
|
-
const usageStr = formatUsageStats(r.usage, r.model);
|
|
1270
|
+
const usageStr = formatUsageStats(r.usage, r.model, r.thinkingLevel);
|
|
1207
1271
|
if (usageStr) {
|
|
1208
1272
|
container.addChild(new Spacer(1));
|
|
1209
1273
|
container.addChild(new Text(theme.fg("dim", usageStr), 0, 0));
|
|
@@ -1219,7 +1283,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
1219
1283
|
text += `\n${renderDisplayItems(displayItems, COLLAPSED_ITEM_COUNT)}`;
|
|
1220
1284
|
if (displayItems.length > COLLAPSED_ITEM_COUNT) text += `\n${theme.fg("muted", "(Ctrl+O to expand)")}`;
|
|
1221
1285
|
}
|
|
1222
|
-
const usageStr = formatUsageStats(r.usage, r.model);
|
|
1286
|
+
const usageStr = formatUsageStats(r.usage, r.model, r.thinkingLevel);
|
|
1223
1287
|
if (usageStr) text += `\n${theme.fg("dim", usageStr)}`;
|
|
1224
1288
|
return new Text(text, 0, 0);
|
|
1225
1289
|
}
|
|
@@ -1288,7 +1352,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
1288
1352
|
container.addChild(new Markdown(finalOutput.trim(), 0, 0, mdTheme));
|
|
1289
1353
|
}
|
|
1290
1354
|
|
|
1291
|
-
const stepUsage = formatUsageStats(r.usage, r.model);
|
|
1355
|
+
const stepUsage = formatUsageStats(r.usage, r.model, r.thinkingLevel);
|
|
1292
1356
|
if (stepUsage) container.addChild(new Text(theme.fg("dim", stepUsage), 0, 0));
|
|
1293
1357
|
}
|
|
1294
1358
|
|
|
@@ -1380,7 +1444,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
1380
1444
|
container.addChild(new Markdown(finalOutput.trim(), 0, 0, mdTheme));
|
|
1381
1445
|
}
|
|
1382
1446
|
|
|
1383
|
-
const taskUsage = formatUsageStats(r.usage, r.model);
|
|
1447
|
+
const taskUsage = formatUsageStats(r.usage, r.model, r.thinkingLevel);
|
|
1384
1448
|
if (taskUsage) container.addChild(new Text(theme.fg("dim", taskUsage), 0, 0));
|
|
1385
1449
|
}
|
|
1386
1450
|
|
|
@@ -1413,7 +1477,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
1413
1477
|
container.addChild(new Spacer(1));
|
|
1414
1478
|
container.addChild(new Markdown(finalOutput.trim(), 0, 0, mdTheme));
|
|
1415
1479
|
}
|
|
1416
|
-
const fanInUsage = formatUsageStats(aggregator.usage, aggregator.model);
|
|
1480
|
+
const fanInUsage = formatUsageStats(aggregator.usage, aggregator.model, aggregator.thinkingLevel);
|
|
1417
1481
|
if (fanInUsage) container.addChild(new Text(theme.fg("dim", fanInUsage), 0, 0));
|
|
1418
1482
|
}
|
|
1419
1483
|
|