oira666_pi-subagent 0.2.21 → 0.2.22

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 CHANGED
@@ -1,456 +1,460 @@
1
- # Pi Subagent
2
-
3
- Delegate tasks to specialized subagents.
4
-
5
- ## Install
6
-
7
- ```bash
8
- pi install npm:oira666_pi-subagent
9
- ```
10
-
11
- Or via git:
12
-
13
- ```bash
14
- pi install git:github.com/gee666/pi-subagent.git
15
- ```
16
-
17
- ## Remove
18
-
19
- ```bash
20
- pi remove npm:oira666_pi-subagent
21
- ```
22
-
23
- ## How It Works
24
-
25
- Each subagent runs as a **separate `pi` process** — fully isolated memory, its own model/tool loop.
26
- Processes are spawned via the operating system and communicate through JSON-line stdout.
27
- Subagent sessions are persisted separately under a `sessions-subagents` directory (a sibling of Pi's normal `sessions` directory), so they can be resumed without mixing into the main session list.
28
-
29
- - Full OS-level isolation — a crashed subagent cannot affect the parent
30
- - True parallel execution across all CPU cores
31
- - Each subprocess boots a fresh Node.js runtime
32
- - Uses the same Pi CLI entrypoint as the parent process when available
33
-
34
- Each subagent receives only the task string. The main agent in turn receives
35
- only the **final text output** from subagents (no tool calls, no reasoning).
36
-
37
- ## Tool Call Shape
38
-
39
- ```json
40
- { "tasks": [{ "agent": "code-writer", "task": "Implement the API" }] }
41
- ```
42
-
43
- Multiple tasks run in parallel:
44
-
45
- ```json
46
- {
47
- "tasks": [
48
- { "agent": "code-writer", "task": "Draft the implementation" },
49
- { "agent": "code-reviwer", "task": "Review the plan" }
50
- ]
51
- }
52
- ```
53
-
54
- Each task supports `agent` and `task`.
55
-
56
- ## Bundled Agents
57
-
58
- Three fallback agents ship with the extension (used when no user/project agents are configured):
59
-
60
- - `code-writer` — implementation and refactoring
61
- - `code-reviwer` — code review and risk finding
62
- - `code-architect` — technical design and approach selection
63
-
64
- ## Defining Agents
65
-
66
- Create Markdown files with YAML frontmatter:
67
-
68
- - **User agents:** `~/.pi/agent/agents/*.md`
69
- - **Env agents:** `$PI_CODING_AGENT_DIR/agents/*.md` *(when `PI_CODING_AGENT_DIR` is set)*
70
- - **Project agents:** `.pi/agents/*.md` *(may prompt for confirmation — see `PI_SUBAGENT_CONFIRM_PROJECT_AGENTS`)*
71
-
72
- Agent discovery priority (highest wins on name collision): project > env > user.
73
- Built-in agents are only used as a fallback when **all three** locations are empty.
74
-
75
- ```markdown
76
- ---
77
- name: writer
78
- description: Expert technical writer
79
- model: anthropic/claude-3-5-sonnet
80
- thinking: low
81
- tools: read,write
82
- ---
83
-
84
- You are an expert technical writer focused on clarity and conciseness.
85
- ```
86
-
87
- ### Frontmatter Fields
88
-
89
- | Field | Required | Default | Description |
90
- | ------------- | -------- | -------------------- | -------------------------------------------------------- |
91
- | `name` | Yes | — | Agent identifier used in tool calls |
92
- | `description` | Yes | — | What the agent does (shown to the main agent) |
93
- | `model` | No | Pi default | Override model, e.g. `anthropic/claude-3-5-sonnet` |
94
- | `thinking` | No | Pi default | `off`, `minimal`, `low`, `medium`, `high`, `xhigh` |
95
- | `tools` | No | `read,bash,edit,write` | Comma-separated built-in tools |
96
-
97
- Available tools: `read`, `bash`, `edit`, `write`.
98
-
99
- The Markdown body becomes the agent's system prompt (appended to Pi's default, not replacing it).
100
-
101
- ## Delegation Guards
102
-
103
- Depth and cycle guards prevent runaway recursive delegation.
104
-
105
- | Config | Default | Description |
106
- | ------------------------------ | ------- | ------------------------------------------------ |
107
- | `--subagent-max-depth` / `PI_SUBAGENT_MAX_DEPTH` | `3` | Max delegation depth (0 disables delegation) |
108
- | `--subagent-prevent-cycles` / `PI_SUBAGENT_PREVENT_CYCLES` | `true` | Block same agent in delegation chain |
109
-
110
- ```bash
111
- pi --subagent-max-depth 2 # one nested level
112
- pi --subagent-max-depth 0 # disable delegation entirely
113
- pi --no-subagent-prevent-cycles # allow cycles (not recommended)
114
- ```
115
-
116
- ## Parallel Limits
117
-
118
- | Env Var | Default | Description |
119
- | -------------------------------- | ------- | ---------------------------------------- |
120
- | `PI_SUBAGENT_MAX_PARALLEL_TASKS` | `16` | Max tasks per single call |
121
- | `PI_SUBAGENT_MAX_CONCURRENCY` | `8` | Max subagents running simultaneously |
122
-
123
- ## Subagent Session Resume
124
-
125
- Subagent subprocesses save sessions in `sessions-subagents`. When a main Pi session is resumed and its latest branch contains an unfinished `subagent` tool call (aborted, errored, or closed by Pi's synthetic unfinished-tool error), the extension can resume that delegation from the saved subagent sessions.
126
-
127
- - TUI mode asks: **Resume subagents?**
128
- - Non-UI modes (`pi -p`, JSON/RPC) resume automatically.
129
- - Already-finished subagents are reused as completed; unfinished ones continue from their own saved sessions.
130
- - Nested subagents use the same mechanism recursively.
131
-
132
- | Env Var | Default | Description |
133
- | --- | --- | --- |
134
- | `PI_SUBAGENT_RESUME_PROMPT` | `true` | Set to `false` to suppress the TUI yes/no prompt and auto-resume. |
135
- | `PI_SUBAGENT_DISABLE_RESUME` | `false` | Set to `true` to disable automatic subagent resume detection entirely. |
136
-
137
- ## Agent Discovery
138
-
139
- | Env Var | Description |
140
- | ----------------------- | ------------------------------------------------------------ |
141
- | `PI_CODING_AGENT_DIR` | Base path for an additional agents directory (`$PI_CODING_AGENT_DIR/agents/*.md`). Agents here override user agents but are overridden by project agents. Built-in agents are only used when user, env, and project locations all yield zero agents. |
142
-
143
- ## CLI Argument Proxying
144
-
145
- Flags passed to the parent `pi` process are forwarded to subagent child
146
- processes, so they inherit the same provider, API key, model, and other runtime settings. Flags the
147
- extension manages itself are blocked from being forwarded.
148
-
149
- **Always forwarded verbatim:**
150
-
151
- | Flag(s) | Purpose |
152
- | --- | --- |
153
- | `--provider` | AI provider |
154
- | `--api-key` | API key |
155
- | `--system-prompt` | Base system prompt override |
156
- | `--session-dir` | Session storage directory |
157
- | `--models` | Model cycling list |
158
- | `--skill`, `--no-skills`/`-ns` | Skill loading |
159
- | `--prompt-template`, `--no-prompt-templates`/`-np` | Prompt templates |
160
- | `--theme`, `--no-themes` | Themes |
161
- | `--verbose` | Verbose startup output |
162
- | Unknown/custom flags | Forwarded with heuristic value detection |
163
-
164
- **Forwarded as fallback** (agent frontmatter overrides if set):
165
-
166
- | Flag | Overridden by |
167
- | --- | --- |
168
- | `--model` | `model:` in agent frontmatter |
169
- | `--thinking` | `thinking:` in agent frontmatter |
170
- | `--tools` / `--no-tools` | `tools:` in agent frontmatter |
171
-
172
- **Never forwarded** (managed by the extension itself):
173
- `--mode`, `-p`/`--print`, `--session`/`--no-session`, `--continue`, `--resume`,
174
- `--append-system-prompt`, `--offline`, `--extension`/`-e`, `--no-extensions`/`-ne`,
175
- `--subagent-max-depth`, `--subagent-prevent-cycles`, `--export`, `--list-models`,
176
- `--help`, `--version`.
177
-
178
- ---
179
-
180
- ## Programmatic Usage (JSON RPC)
181
-
182
- When running `pi` programmatically with `--mode rpc` (or `--mode json`), the stream contains
183
- `tool_result_end` events whenever the agent completes a `subagent` tool call. The `details` field
184
- of these events carries the full stats for that delegation — including recursive usage and tool
185
- call counts from all subagents in the tree.
186
-
187
- ### Stream event shape
188
-
189
- ```
190
- tool_result_end
191
- └── message
192
- ├── role: "toolResult"
193
- ├── toolName: "subagent"
194
- ├── toolCallId: string
195
- ├── isError: boolean
196
- ├── content: [{ type: "text", text: "<final output>" }]
197
- └── details: SubagentDetails
198
- ```
199
-
200
- ### `SubagentDetails` object
201
-
202
- ```ts
203
- interface SubagentDetails {
204
- // Execution metadata
205
- mode: "single" | "parallel"; // one task vs multiple parallel tasks
206
- delegationMode: "spawn"; // always "spawn" (kept for backward-compatible serialization)
207
- projectAgentsDir: string | null; // path to .pi/agents/ dir if used
208
-
209
- // Individual agent results (one per task)
210
- results: SingleResult[];
211
-
212
- // ── Stats summary (own + all descendants, recursively) ──────────────────
213
- aggregatedUsage: UsageStats; // token counts and cost, full tree
214
- aggregatedToolCalls: ToolCallCounts; // { toolName: callCount }, full tree
215
-
216
- // ── Per-agent breakdown ──────────────────────────────────────────────────
217
- usageTree: UsageTreeNode[]; // one root node per result
218
- }
219
-
220
- interface SingleResult {
221
- agent: string; // agent name
222
- agentSource: "user" | "project" | "builtin" | "unknown";
223
- task: string; // task string passed to this agent
224
- exitCode: number; // 0 = success, >0 = error, -1 = still running
225
- messages: Message[]; // full conversation history of the subagent
226
- stderr: string;
227
- usage: UsageStats; // this agent's OWN token usage only
228
- toolCalls: ToolCallCounts; // this agent's OWN tool calls only
229
- model?: string;
230
- stopReason?: string; // "end_turn" | "error" | "aborted" | ...
231
- errorMessage?: string;
232
- }
233
-
234
- interface UsageStats {
235
- input: number; // input tokens
236
- output: number; // output tokens
237
- cacheRead: number; // cache read tokens
238
- cacheWrite: number; // cache write tokens
239
- cost: number; // total cost in USD
240
- contextTokens: number; // snapshot: last context window size (not summed in aggregates)
241
- turns: number; // number of assistant turns
242
- }
243
-
244
- // toolName call count, e.g. { "bash": 5, "read": 3, "subagent": 1 }
245
- type ToolCallCounts = Record<string, number>;
246
-
247
- interface UsageTreeNode {
248
- agent: string;
249
- task: string;
250
- ownUsage: UsageStats; // only this agent's turns
251
- ownToolCalls: ToolCallCounts; // only this agent's tool calls
252
- aggregatedUsage: UsageStats; // ownUsage + all children recursively
253
- aggregatedToolCalls: ToolCallCounts; // ownToolCalls + all children recursively
254
- children: UsageTreeNode[]; // one node per nested subagent invocation
255
- }
256
- ```
257
-
258
- ### Important notes on stats
259
-
260
- - **`SingleResult.usage`** and **`SingleResult.toolCalls`** cover **only that one agent's own work** —
261
- not its children. Children run in separate processes; their tokens never appear in the parent's usage.
262
- - **`aggregatedUsage`** / **`aggregatedToolCalls`** on `SubagentDetails` (and on each `UsageTreeNode`)
263
- are the correct totals to use when you want the cost or tool call count for an entire delegation
264
- subtree.
265
- - **`contextTokens`** is a point-in-time snapshot of the context window size at the last turn of that
266
- agent. It is **not** summed in aggregated stats (it would be meaningless as a cross-process sum).
267
- - **`toolCalls`** includes **all** tool calls an agent made, including the `"subagent"` call itself.
268
- You can use the `"subagent"` count to see how many nested delegations an agent spawned.
269
-
270
- ### Annotated example JSON
271
-
272
- The scenario below: main agent delegates to `code-writer`, which does some file work and then
273
- delegates to `code-reviwer` before finishing.
274
-
275
- ```json
276
- {
277
- "type": "tool_result_end",
278
- "message": {
279
- "role": "toolResult",
280
- "toolName": "subagent",
281
- "toolCallId": "toolu_01XYZ",
282
- "isError": false,
283
- "content": [
284
- {
285
- "type": "text",
286
- "text": "Feature implemented and reviewed. Added validation logic in auth.ts and updated the test suite."
287
- }
288
- ],
289
- "details": {
290
- "mode": "single",
291
- "delegationMode": "spawn",
292
- "projectAgentsDir": null,
293
-
294
- "aggregatedUsage": {
295
- "input": 2180,
296
- "output": 615,
297
- "cacheRead": 940,
298
- "cacheWrite": 120,
299
- "cost": 0.0079,
300
- "contextTokens": 0,
301
- "turns": 3
302
- },
303
- "aggregatedToolCalls": {
304
- "read": 3,
305
- "bash": 2,
306
- "edit": 1,
307
- "subagent": 1
308
- },
309
-
310
- "usageTree": [
311
- {
312
- "agent": "code-writer",
313
- "task": "Implement the auth feature and have it reviewed",
314
- "ownUsage": {
315
- "input": 1380,
316
- "output": 365,
317
- "cacheRead": 540,
318
- "cacheWrite": 120,
319
- "cost": 0.0058,
320
- "contextTokens": 2840,
321
- "turns": 2
322
- },
323
- "ownToolCalls": {
324
- "read": 1,
325
- "bash": 1,
326
- "edit": 1,
327
- "subagent": 1
328
- },
329
- "aggregatedUsage": {
330
- "input": 2180,
331
- "output": 615,
332
- "cacheRead": 940,
333
- "cacheWrite": 120,
334
- "cost": 0.0079,
335
- "contextTokens": 0,
336
- "turns": 3
337
- },
338
- "aggregatedToolCalls": {
339
- "read": 3,
340
- "bash": 2,
341
- "edit": 1,
342
- "subagent": 1
343
- },
344
- "children": [
345
- {
346
- "agent": "code-reviwer",
347
- "task": "Review the auth implementation in auth.ts",
348
- "ownUsage": {
349
- "input": 800,
350
- "output": 250,
351
- "cacheRead": 400,
352
- "cacheWrite": 0,
353
- "cost": 0.0021,
354
- "contextTokens": 1450,
355
- "turns": 1
356
- },
357
- "ownToolCalls": {
358
- "read": 2,
359
- "bash": 1
360
- },
361
- "aggregatedUsage": {
362
- "input": 800,
363
- "output": 250,
364
- "cacheRead": 400,
365
- "cacheWrite": 0,
366
- "cost": 0.0021,
367
- "contextTokens": 0,
368
- "turns": 1
369
- },
370
- "aggregatedToolCalls": {
371
- "read": 2,
372
- "bash": 1
373
- },
374
- "children": []
375
- }
376
- ]
377
- }
378
- ],
379
-
380
- "results": [
381
- {
382
- "agent": "code-writer",
383
- "agentSource": "builtin",
384
- "task": "Implement the auth feature and have it reviewed",
385
- "exitCode": 0,
386
- "stopReason": "end_turn",
387
- "model": "claude-opus-4-5",
388
- "stderr": "",
389
- "usage": {
390
- "input": 1380,
391
- "output": 365,
392
- "cacheRead": 540,
393
- "cacheWrite": 120,
394
- "cost": 0.0058,
395
- "contextTokens": 2840,
396
- "turns": 2
397
- },
398
- "toolCalls": {
399
- "read": 1,
400
- "bash": 1,
401
- "edit": 1,
402
- "subagent": 1
403
- },
404
- "messages": [
405
- "... full conversation history of code-writer (includes the nested subagent tool_result) ..."
406
- ]
407
- }
408
- ]
409
- }
410
- }
411
- }
412
- ```
413
-
414
- ### Collecting stats across an entire session
415
-
416
- If you are consuming the JSON stream programmatically and want to track the total cost and tool
417
- usage across all subagent work in a session, listen for every `tool_result_end` event where
418
- `message.toolName === "subagent"` and sum `message.details.aggregatedUsage` across them.
419
-
420
- ```js
421
- let totalCost = 0;
422
- const totalToolCalls = {};
423
-
424
- for await (const line of jsonLines) {
425
- const event = JSON.parse(line);
426
- if (
427
- event.type === "tool_result_end" &&
428
- event.message?.toolName === "subagent" &&
429
- event.message?.details
430
- ) {
431
- const { aggregatedUsage, aggregatedToolCalls } = event.message.details;
432
- totalCost += aggregatedUsage.cost;
433
- for (const [tool, count] of Object.entries(aggregatedToolCalls)) {
434
- totalToolCalls[tool] = (totalToolCalls[tool] ?? 0) + count;
435
- }
436
- }
437
- }
438
- ```
439
-
440
- Note: if you also track the main agent's own usage from `message_end` events, make sure **not** to
441
- double-count the subagent costs there — the main agent's own token usage (from its own `message_end`
442
- events) does not include subagent work.
443
-
444
- ---
445
-
446
- ## create-subagent Skill
447
-
448
- If you want the agent to **create new subagent definition files** for itself, install the [`create-subagent` skill](https://github.com/gee666/pi-subagent/tree/main/create-subagent). Once installed, the agent will know how to scaffold new `.md` agent files in the right location with correct frontmatter.
449
-
450
- ## Attribution
451
-
452
- Inspired by [vaayne/agent-kit](https://github.com/vaayne/agent-kit) and [mariozechner/pi-mono](https://github.com/badlogic/pi-mono).
453
-
454
- ## License
455
-
456
- MIT
1
+ # Pi Subagent
2
+
3
+ Delegate tasks to specialized subagents.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ pi install npm:oira666_pi-subagent
9
+ ```
10
+
11
+ Or via git:
12
+
13
+ ```bash
14
+ pi install git:github.com/gee666/pi-subagent.git
15
+ ```
16
+
17
+ ## Remove
18
+
19
+ ```bash
20
+ pi remove npm:oira666_pi-subagent
21
+ ```
22
+
23
+ ## How It Works
24
+
25
+ Each subagent runs as a **separate `pi` process** — fully isolated memory, its own model/tool loop.
26
+ Processes are spawned via the operating system and communicate through JSON-line stdout.
27
+ Subagent sessions are persisted separately under a `sessions-subagents` directory (a sibling of Pi's normal `sessions` directory), so they can be resumed without mixing into the main session list.
28
+
29
+ - Full OS-level isolation — a crashed subagent cannot affect the parent
30
+ - True parallel execution across all CPU cores
31
+ - Each subprocess boots a fresh Node.js runtime
32
+ - Uses the same Pi CLI entrypoint as the parent process when available
33
+
34
+ Each subagent receives only the task string. The main agent in turn receives
35
+ only the **final text output** from subagents (no tool calls, no reasoning).
36
+
37
+ ## Tool Call Shape
38
+
39
+ ```json
40
+ { "tasks": [{ "agent": "code-writer", "task": "Implement the API" }] }
41
+ ```
42
+
43
+ Multiple tasks run in parallel:
44
+
45
+ ```json
46
+ {
47
+ "tasks": [
48
+ { "agent": "code-writer", "task": "Draft the implementation" },
49
+ { "agent": "code-reviwer", "task": "Review the plan" }
50
+ ]
51
+ }
52
+ ```
53
+
54
+ Each task supports `agent` and `task`.
55
+
56
+ ## Bundled Agents
57
+
58
+ Three fallback agents ship with the extension (used when no user/project agents are configured):
59
+
60
+ - `code-writer` — implementation and refactoring
61
+ - `code-reviwer` — code review and risk finding
62
+ - `code-architect` — technical design and approach selection
63
+
64
+ ## Defining Agents
65
+
66
+ Create Markdown files with YAML frontmatter:
67
+
68
+ - **User agents:** `~/.pi/agent/agents/*.md`
69
+ - **Env agents:** `$PI_CODING_AGENT_DIR/agents/*.md` *(when `PI_CODING_AGENT_DIR` is set)*
70
+ - **Project agents:** `.pi/agents/*.md` *(may prompt for confirmation — see `PI_SUBAGENT_CONFIRM_PROJECT_AGENTS`)*
71
+
72
+ Agent discovery priority (highest wins on name collision): project > env > user.
73
+ Built-in agents are only used as a fallback when **all three** locations are empty.
74
+
75
+ ```markdown
76
+ ---
77
+ name: writer
78
+ description: Expert technical writer
79
+ model: anthropic/claude-3-5-sonnet
80
+ thinking: low
81
+ tools: read,write
82
+ ---
83
+
84
+ You are an expert technical writer focused on clarity and conciseness.
85
+ ```
86
+
87
+ ### Frontmatter Fields
88
+
89
+ | Field | Required | Default | Description |
90
+ | ------------- | -------- | -------------------- | -------------------------------------------------------- |
91
+ | `name` | Yes | — | Agent identifier used in tool calls |
92
+ | `description` | Yes | — | What the agent does (shown to the main agent) |
93
+ | `model` | No | Pi default | Override model, e.g. `anthropic/claude-3-5-sonnet` |
94
+ | `thinking` | No | Pi default | `off`, `minimal`, `low`, `medium`, `high`, `xhigh` |
95
+ | `tools` | No | `read,bash,edit,write` | Comma-separated built-in tools |
96
+
97
+ Available tools: `read`, `bash`, `edit`, `write`.
98
+
99
+ The Markdown body becomes the agent's system prompt (appended to Pi's default, not replacing it).
100
+
101
+ ## Delegation Guards
102
+
103
+ Depth and cycle guards prevent runaway recursive delegation.
104
+
105
+ | Config | Default | Description |
106
+ | ------------------------------ | ------- | ------------------------------------------------ |
107
+ | `--subagent-max-depth` / `PI_SUBAGENT_MAX_DEPTH` | `3` | Max delegation depth (0 disables delegation) |
108
+ | `--subagent-prevent-cycles` / `PI_SUBAGENT_PREVENT_CYCLES` | `true` | Block same agent in delegation chain |
109
+
110
+ ```bash
111
+ pi --subagent-max-depth 2 # one nested level
112
+ pi --subagent-max-depth 0 # disable delegation entirely
113
+ pi --no-subagent-prevent-cycles # allow cycles (not recommended)
114
+ ```
115
+
116
+ ## Parallel Limits
117
+
118
+ | Env Var | Default | Description |
119
+ | -------------------------------- | ------- | ---------------------------------------- |
120
+ | `PI_SUBAGENT_MAX_PARALLEL_TASKS` | `16` | Max tasks per single call |
121
+ | `PI_SUBAGENT_MAX_CONCURRENCY` | `8` | Max subagents running simultaneously |
122
+
123
+ ## Steering Running Subagents
124
+
125
+ While a `subagent` tool call is running, mid-stream steering input can be broadcast to one or more child agents. The extension uses Pi's `InputEvent.streamingBehavior` metadata when available, so idle prompts and queued follow-ups continue to the parent normally; only true `steer` inputs open the broadcast routing prompt.
126
+
127
+ ## Subagent Session Resume
128
+
129
+ Subagent subprocesses save sessions in `sessions-subagents`. When a main Pi session is resumed and its latest branch contains an unfinished `subagent` tool call (aborted, errored, or closed by Pi's synthetic unfinished-tool error), the extension can resume that delegation from the saved subagent sessions.
130
+
131
+ - TUI mode asks: **Resume subagents?**
132
+ - Non-UI modes (`pi -p`, JSON/RPC) resume automatically.
133
+ - Already-finished subagents are reused as completed; unfinished ones continue from their own saved sessions.
134
+ - Nested subagents use the same mechanism recursively.
135
+
136
+ | Env Var | Default | Description |
137
+ | --- | --- | --- |
138
+ | `PI_SUBAGENT_RESUME_PROMPT` | `true` | Set to `false` to suppress the TUI yes/no prompt and auto-resume. |
139
+ | `PI_SUBAGENT_DISABLE_RESUME` | `false` | Set to `true` to disable automatic subagent resume detection entirely. |
140
+
141
+ ## Agent Discovery
142
+
143
+ | Env Var | Description |
144
+ | ----------------------- | ------------------------------------------------------------ |
145
+ | `PI_CODING_AGENT_DIR` | Base path for an additional agents directory (`$PI_CODING_AGENT_DIR/agents/*.md`). Agents here override user agents but are overridden by project agents. Built-in agents are only used when user, env, and project locations all yield zero agents. |
146
+
147
+ ## CLI Argument Proxying
148
+
149
+ Flags passed to the parent `pi` process are forwarded to subagent child
150
+ processes, so they inherit the same provider, API key, model, and other runtime settings. Flags the
151
+ extension manages itself are blocked from being forwarded.
152
+
153
+ **Always forwarded verbatim:**
154
+
155
+ | Flag(s) | Purpose |
156
+ | --- | --- |
157
+ | `--provider` | AI provider |
158
+ | `--api-key` | API key |
159
+ | `--system-prompt` | Base system prompt override |
160
+ | `--session-dir` | Session storage directory |
161
+ | `--models` | Model cycling list |
162
+ | `--skill`, `--no-skills`/`-ns` | Skill loading |
163
+ | `--prompt-template`, `--no-prompt-templates`/`-np` | Prompt templates |
164
+ | `--theme`, `--no-themes` | Themes |
165
+ | `--verbose` | Verbose startup output |
166
+ | Unknown/custom flags | Forwarded with heuristic value detection |
167
+
168
+ **Forwarded as fallback** (agent frontmatter overrides if set):
169
+
170
+ | Flag | Overridden by |
171
+ | --- | --- |
172
+ | `--model` | `model:` in agent frontmatter |
173
+ | `--thinking` | `thinking:` in agent frontmatter |
174
+ | `--tools` / `--no-tools` | `tools:` in agent frontmatter |
175
+
176
+ **Never forwarded** (managed by the extension itself):
177
+ `--mode`, `-p`/`--print`, `--session`/`--no-session`, `--continue`, `--resume`,
178
+ `--append-system-prompt`, `--offline`, `--extension`/`-e`, `--no-extensions`/`-ne`,
179
+ `--subagent-max-depth`, `--subagent-prevent-cycles`, `--export`, `--list-models`,
180
+ `--help`, `--version`.
181
+
182
+ ---
183
+
184
+ ## Programmatic Usage (JSON RPC)
185
+
186
+ When running `pi` programmatically with `--mode rpc` (or `--mode json`), the stream contains
187
+ `tool_result_end` events whenever the agent completes a `subagent` tool call. The `details` field
188
+ of these events carries the full stats for that delegation — including recursive usage and tool
189
+ call counts from all subagents in the tree.
190
+
191
+ ### Stream event shape
192
+
193
+ ```
194
+ tool_result_end
195
+ └── message
196
+ ├── role: "toolResult"
197
+ ├── toolName: "subagent"
198
+ ├── toolCallId: string
199
+ ├── isError: boolean
200
+ ├── content: [{ type: "text", text: "<final output>" }]
201
+ └── details: SubagentDetails
202
+ ```
203
+
204
+ ### `SubagentDetails` object
205
+
206
+ ```ts
207
+ interface SubagentDetails {
208
+ // Execution metadata
209
+ mode: "single" | "parallel"; // one task vs multiple parallel tasks
210
+ delegationMode: "spawn"; // always "spawn" (kept for backward-compatible serialization)
211
+ projectAgentsDir: string | null; // path to .pi/agents/ dir if used
212
+
213
+ // Individual agent results (one per task)
214
+ results: SingleResult[];
215
+
216
+ // ── Stats summary (own + all descendants, recursively) ──────────────────
217
+ aggregatedUsage: UsageStats; // token counts and cost, full tree
218
+ aggregatedToolCalls: ToolCallCounts; // { toolName: callCount }, full tree
219
+
220
+ // ── Per-agent breakdown ──────────────────────────────────────────────────
221
+ usageTree: UsageTreeNode[]; // one root node per result
222
+ }
223
+
224
+ interface SingleResult {
225
+ agent: string; // agent name
226
+ agentSource: "user" | "project" | "builtin" | "unknown";
227
+ task: string; // task string passed to this agent
228
+ exitCode: number; // 0 = success, >0 = error, -1 = still running
229
+ messages: Message[]; // full conversation history of the subagent
230
+ stderr: string;
231
+ usage: UsageStats; // this agent's OWN token usage only
232
+ toolCalls: ToolCallCounts; // this agent's OWN tool calls only
233
+ model?: string;
234
+ stopReason?: string; // "end_turn" | "error" | "aborted" | ...
235
+ errorMessage?: string;
236
+ }
237
+
238
+ interface UsageStats {
239
+ input: number; // input tokens
240
+ output: number; // output tokens
241
+ cacheRead: number; // cache read tokens
242
+ cacheWrite: number; // cache write tokens
243
+ cost: number; // total cost in USD
244
+ contextTokens: number; // snapshot: last context window size (not summed in aggregates)
245
+ turns: number; // number of assistant turns
246
+ }
247
+
248
+ // toolName → call count, e.g. { "bash": 5, "read": 3, "subagent": 1 }
249
+ type ToolCallCounts = Record<string, number>;
250
+
251
+ interface UsageTreeNode {
252
+ agent: string;
253
+ task: string;
254
+ ownUsage: UsageStats; // only this agent's turns
255
+ ownToolCalls: ToolCallCounts; // only this agent's tool calls
256
+ aggregatedUsage: UsageStats; // ownUsage + all children recursively
257
+ aggregatedToolCalls: ToolCallCounts; // ownToolCalls + all children recursively
258
+ children: UsageTreeNode[]; // one node per nested subagent invocation
259
+ }
260
+ ```
261
+
262
+ ### Important notes on stats
263
+
264
+ - **`SingleResult.usage`** and **`SingleResult.toolCalls`** cover **only that one agent's own work** —
265
+ not its children. Children run in separate processes; their tokens never appear in the parent's usage.
266
+ - **`aggregatedUsage`** / **`aggregatedToolCalls`** on `SubagentDetails` (and on each `UsageTreeNode`)
267
+ are the correct totals to use when you want the cost or tool call count for an entire delegation
268
+ subtree.
269
+ - **`contextTokens`** is a point-in-time snapshot of the context window size at the last turn of that
270
+ agent. It is **not** summed in aggregated stats (it would be meaningless as a cross-process sum).
271
+ - **`toolCalls`** includes **all** tool calls an agent made, including the `"subagent"` call itself.
272
+ You can use the `"subagent"` count to see how many nested delegations an agent spawned.
273
+
274
+ ### Annotated example JSON
275
+
276
+ The scenario below: main agent delegates to `code-writer`, which does some file work and then
277
+ delegates to `code-reviwer` before finishing.
278
+
279
+ ```json
280
+ {
281
+ "type": "tool_result_end",
282
+ "message": {
283
+ "role": "toolResult",
284
+ "toolName": "subagent",
285
+ "toolCallId": "toolu_01XYZ",
286
+ "isError": false,
287
+ "content": [
288
+ {
289
+ "type": "text",
290
+ "text": "Feature implemented and reviewed. Added validation logic in auth.ts and updated the test suite."
291
+ }
292
+ ],
293
+ "details": {
294
+ "mode": "single",
295
+ "delegationMode": "spawn",
296
+ "projectAgentsDir": null,
297
+
298
+ "aggregatedUsage": {
299
+ "input": 2180,
300
+ "output": 615,
301
+ "cacheRead": 940,
302
+ "cacheWrite": 120,
303
+ "cost": 0.0079,
304
+ "contextTokens": 0,
305
+ "turns": 3
306
+ },
307
+ "aggregatedToolCalls": {
308
+ "read": 3,
309
+ "bash": 2,
310
+ "edit": 1,
311
+ "subagent": 1
312
+ },
313
+
314
+ "usageTree": [
315
+ {
316
+ "agent": "code-writer",
317
+ "task": "Implement the auth feature and have it reviewed",
318
+ "ownUsage": {
319
+ "input": 1380,
320
+ "output": 365,
321
+ "cacheRead": 540,
322
+ "cacheWrite": 120,
323
+ "cost": 0.0058,
324
+ "contextTokens": 2840,
325
+ "turns": 2
326
+ },
327
+ "ownToolCalls": {
328
+ "read": 1,
329
+ "bash": 1,
330
+ "edit": 1,
331
+ "subagent": 1
332
+ },
333
+ "aggregatedUsage": {
334
+ "input": 2180,
335
+ "output": 615,
336
+ "cacheRead": 940,
337
+ "cacheWrite": 120,
338
+ "cost": 0.0079,
339
+ "contextTokens": 0,
340
+ "turns": 3
341
+ },
342
+ "aggregatedToolCalls": {
343
+ "read": 3,
344
+ "bash": 2,
345
+ "edit": 1,
346
+ "subagent": 1
347
+ },
348
+ "children": [
349
+ {
350
+ "agent": "code-reviwer",
351
+ "task": "Review the auth implementation in auth.ts",
352
+ "ownUsage": {
353
+ "input": 800,
354
+ "output": 250,
355
+ "cacheRead": 400,
356
+ "cacheWrite": 0,
357
+ "cost": 0.0021,
358
+ "contextTokens": 1450,
359
+ "turns": 1
360
+ },
361
+ "ownToolCalls": {
362
+ "read": 2,
363
+ "bash": 1
364
+ },
365
+ "aggregatedUsage": {
366
+ "input": 800,
367
+ "output": 250,
368
+ "cacheRead": 400,
369
+ "cacheWrite": 0,
370
+ "cost": 0.0021,
371
+ "contextTokens": 0,
372
+ "turns": 1
373
+ },
374
+ "aggregatedToolCalls": {
375
+ "read": 2,
376
+ "bash": 1
377
+ },
378
+ "children": []
379
+ }
380
+ ]
381
+ }
382
+ ],
383
+
384
+ "results": [
385
+ {
386
+ "agent": "code-writer",
387
+ "agentSource": "builtin",
388
+ "task": "Implement the auth feature and have it reviewed",
389
+ "exitCode": 0,
390
+ "stopReason": "end_turn",
391
+ "model": "claude-opus-4-5",
392
+ "stderr": "",
393
+ "usage": {
394
+ "input": 1380,
395
+ "output": 365,
396
+ "cacheRead": 540,
397
+ "cacheWrite": 120,
398
+ "cost": 0.0058,
399
+ "contextTokens": 2840,
400
+ "turns": 2
401
+ },
402
+ "toolCalls": {
403
+ "read": 1,
404
+ "bash": 1,
405
+ "edit": 1,
406
+ "subagent": 1
407
+ },
408
+ "messages": [
409
+ "... full conversation history of code-writer (includes the nested subagent tool_result) ..."
410
+ ]
411
+ }
412
+ ]
413
+ }
414
+ }
415
+ }
416
+ ```
417
+
418
+ ### Collecting stats across an entire session
419
+
420
+ If you are consuming the JSON stream programmatically and want to track the total cost and tool
421
+ usage across all subagent work in a session, listen for every `tool_result_end` event where
422
+ `message.toolName === "subagent"` and sum `message.details.aggregatedUsage` across them.
423
+
424
+ ```js
425
+ let totalCost = 0;
426
+ const totalToolCalls = {};
427
+
428
+ for await (const line of jsonLines) {
429
+ const event = JSON.parse(line);
430
+ if (
431
+ event.type === "tool_result_end" &&
432
+ event.message?.toolName === "subagent" &&
433
+ event.message?.details
434
+ ) {
435
+ const { aggregatedUsage, aggregatedToolCalls } = event.message.details;
436
+ totalCost += aggregatedUsage.cost;
437
+ for (const [tool, count] of Object.entries(aggregatedToolCalls)) {
438
+ totalToolCalls[tool] = (totalToolCalls[tool] ?? 0) + count;
439
+ }
440
+ }
441
+ }
442
+ ```
443
+
444
+ Note: if you also track the main agent's own usage from `message_end` events, make sure **not** to
445
+ double-count the subagent costs there — the main agent's own token usage (from its own `message_end`
446
+ events) does not include subagent work.
447
+
448
+ ---
449
+
450
+ ## create-subagent Skill
451
+
452
+ If you want the agent to **create new subagent definition files** for itself, install the [`create-subagent` skill](https://github.com/gee666/pi-subagent/tree/main/create-subagent). Once installed, the agent will know how to scaffold new `.md` agent files in the right location with correct frontmatter.
453
+
454
+ ## Attribution
455
+
456
+ Inspired by [vaayne/agent-kit](https://github.com/vaayne/agent-kit) and [mariozechner/pi-mono](https://github.com/badlogic/pi-mono).
457
+
458
+ ## License
459
+
460
+ MIT