@bacnh85/pi-subagent 0.15.2 → 0.16.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 CHANGED
@@ -1,5 +1,36 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.16.0 (2026-08-23)
4
+
5
+ ### Features
6
+
7
+ - **Role-based model routing** — select models by *function* instead of fixed
8
+ per-agent chains, inspired by oh-my-pi's `modelRoles`. Roles (`@fast`,
9
+ `@coder`, `@smart`, or custom) map functions to ordered fallback chains via
10
+ `subagent.roles` in `~/.pi/agent/settings.json`; bundled agents now reference
11
+ roles instead of hardcoding chains. Without settings, defaults reproduce the
12
+ previous chains exactly.
13
+ - `subagent.agentModels` per-agent overrides — remap a bundled agent's models
14
+ (e.g. `{ "reviewer": "@smart:high" }`) without editing its file. Repo
15
+ `.pi/settings.json` overlays the mapping for trusted projects (read-only).
16
+ - `:thinking` suffix support on any role/model entry (`"@smart:high"`);
17
+ openrouter `:free` ids are preserved.
18
+ - `/subagent roles` — interactive role editor (TUI panel via the shared
19
+ `@bacnh85/pi-config-panel` kernel, saves to global settings.json) or plain
20
+ text mapping in headless mode.
21
+ - Bare `/subagent` now opens the roles view directly (the agent list moved to
22
+ `/subagent list`); `/subagent <role>` / `/subagent @role` shows a role's
23
+ chain, default, and the agents using it instead of erroring.
24
+ - `/subagent <name>` and the system-prompt catalog now show each agent's
25
+ *resolved* chain (role → models → parent fallback) plus unresolved-role
26
+ warnings.
27
+
28
+ ## 0.15.3 (2026-08-20)
29
+ ### Improvements
30
+ - Timeouts have been extracted as Environment Variables enabling overriding.
31
+ - `PI_SUBAGENT_INACTIVITY_TIMEOUT_MINS` default : 3 Mins
32
+ - `PI_SUBAGENT_HARD_TIMEOUT_MINS` default: 20 Mins
33
+
3
34
  ## 0.15.2 (2026-08-18)
4
35
 
5
36
  ### Improvements
package/README.md CHANGED
@@ -2,6 +2,42 @@
2
2
 
3
3
  Isolated in-process subagents for Pi. The `subagent` tool supports single, parallel (8 tasks, 4 concurrent), and chained execution; `/agent` opens inspectable child threads. A live progress widget shows running tasks above the editor; `background: true` runs detached with follow-up-turn completion.
4
4
 
5
+ ## Role-based model routing
6
+
7
+ Models are selected **by function**, not fixed per agent. A *role* maps a
8
+ function (`fast`, `coder`, `smart`, or your own) to an ordered fallback chain
9
+ in `~/.pi/agent/settings.json` under `subagent.roles`:
10
+
11
+ ```json
12
+ {
13
+ "subagent": {
14
+ "roles": {
15
+ "fast": ["zai-coding-cn/glm-5-turbo", "nvidia/openai/gpt-oss-20b", "opencode-go/deepseek-v4-flash"],
16
+ "coder": "zai-coding-cn/glm-5.1, opencode-go/deepseek-v4-flash",
17
+ "smart": "*"
18
+ },
19
+ "agentModels": { "reviewer": "@smart:high" }
20
+ }
21
+ }
22
+ ```
23
+
24
+ - Bundled agents reference roles in frontmatter (`model: "@fast"`) — remap a
25
+ function once and every agent using it follows.
26
+ - Role values: string (comma chain) or array. `*` / `@default` = parent model.
27
+ - A trailing `:level` (`off|minimal|low|medium|high|xhigh|max`) on any entry
28
+ overrides the agent's thinking for that match (`"@smart:high"`); openrouter
29
+ `:free` ids are left intact.
30
+ - `subagent.agentModels` overrides a single agent's models without editing its
31
+ file. Repo `.pi/settings.json` overlays the global mapping when the project
32
+ is trusted (read-only; saves go to the global file).
33
+ - Without any settings, bundled defaults reproduce today's chains exactly.
34
+
35
+ `/subagent` opens the interactive role editor (TUI panel via the shared
36
+ `@bacnh85/pi-config-panel` kernel; prints the effective mapping headless).
37
+ `/subagent list` lists agents, `/subagent <name>` shows an agent's resolved
38
+ chain, and `/subagent @role` (or `/subagent fast`) shows a role's chain and
39
+ the agents using it.
40
+
5
41
  ## Live progress widget
6
42
 
7
43
  When subagents run, a persistent widget appears above the editor showing each
@@ -45,14 +81,16 @@ Requires Node.js >= 20.18.
45
81
 
46
82
  ## Bundled roles
47
83
 
48
- | Role | Ordered model preferences | Thinking | Tools |
84
+ | Role | Model role | Thinking | Tools |
49
85
  | --- | --- | --- | --- |
50
- | `scout` | `zai-coding-cn/glm-5-turbo` → `nvidia/openai/gpt-oss-20b` → `opencode-go/deepseek-v4-flash` | off | read, grep, find, ls |
51
- | `tester` | `zai-coding-cn/glm-5-turbo` → `nvidia/openai/gpt-oss-20b` → `opencode-go/deepseek-v4-flash` | off | read, bash, grep, find, ls |
52
- | `worker` | `zai-coding-cn/glm-5.1` → `nvidia/mistralai/mistral-small-4-119b-2603` → `openrouter/nvidia/nemotron-3-super-120b-a12b:free` → `opencode-go/deepseek-v4-flash` | medium | **inherits all parent tools** |
53
- | `general-purpose` | `zai-coding-cn/glm-5.1` → `nvidia/mistralai/mistral-small-4-119b-2603` → `openrouter/nvidia/nemotron-3-super-120b-a12b:free` → `opencode-go/deepseek-v4-flash` | medium | **inherits all parent tools** |
54
- | `planner` | `zai-coding-cn/glm-5.2` → `openrouter/nvidia/nemotron-3-ultra-550b-a55b:free` → `opencode-go/deepseek-v4-pro` | high | read, grep, find, ls |
55
- | `reviewer` | `zai-coding-cn/glm-5.2` → `openrouter/nvidia/nemotron-3-ultra-550b-a55b:free` → `opencode-go/deepseek-v4-pro` | high | read, grep, find, ls |
86
+ | `scout` | `@fast` | off | read, grep, find, ls |
87
+ | `tester` | `@fast` | off | read, bash, grep, find, ls |
88
+ | `worker` | `@coder` | medium | **inherits all parent tools** |
89
+ | `general-purpose` | `@coder` | medium | **inherits all parent tools** |
90
+ | `planner` | `@smart` | high | read, grep, find, ls |
91
+ | `reviewer` | `@smart` | high | read, grep, find, ls |
92
+
93
+ Default role chains (overridable via `subagent.roles` in settings.json — see above):
56
94
 
57
95
  Each role uses the first authenticated preference available through Pi's model registry, then falls back to the authenticated parent model. Chains are **free-first** to conserve the metered opencode-go budget: **zai-coding-cn** (free GLM, primary) → free **nvidia** NIM and **openrouter** `:free` models → **opencode-go** (paid DeepSeek, last resort — one per role: `deepseek-v4-flash` for fast/strong-coding, `deepseek-v4-pro` for deep reasoning). opencode-go's GLM models cost ~$1.40/$4.40 per M versus zai-coding-cn's free GLM, so GLM stays on zai-coding-cn. Fallback models were live-verified on 2026-07-24; `nvidia/moonshotai/kimi-k2.6` and `nvidia/z-ai/glm-5.2` return 404/timeout on the user's account and were removed — non-rate-limit failures kill the subagent instead of advancing the chain. User/project agent files remain stronger overrides and may set legacy `model`, ordered `models`, and `thinking`.
58
96
 
@@ -140,8 +178,8 @@ each writes into its own checkout.
140
178
 
141
179
  Every child execution receives a timeout:
142
180
 
143
- - **Default inactivity window:** 3 minutes (`DEFAULT_TIMEOUT_MS`); real SDK lifecycle activity resets it.
144
- - **Absolute cap:** 20 minutes for every child, even when active.
181
+ - **Default inactivity window:** 3 minutes (`DEFAULT_TIMEOUT_MS`); real SDK lifecycle activity resets it. Overridable with Environment Variable `PI_SUBAGENT_INACTIVITY_TIMEOUT_MINS`
182
+ - **Absolute cap:** Default 20 minutes for every child, even when active. Overridable with Environment variable `PI_SUBAGENT_HARD_TIMEOUT_MINS`
145
183
  - **Maximum requested inactivity window:** 60 minutes (`MAX_TIMEOUT_MS`); values must be positive integers.
146
184
  - Timeout diagnostics distinguish `Idle timeout` from `Hard timeout` and parent cancellation.
147
185
  - 30-second progress heartbeats only keep the parent transport alive; they never reset inactivity.
@@ -211,7 +249,8 @@ The raw `stopReason` from the Pi SDK is preserved in the result.
211
249
 
212
250
  ## Timeout and cancellation
213
251
 
214
- - **Default timeout:** 10 minutes per child.
252
+ - **Default inactivity window:** 3 minutes per child (`PI_SUBAGENT_INACTIVITY_TIMEOUT_MINS`, range 1–60).
253
+ - **Absolute cap:** 20 minutes per child, even when active (`PI_SUBAGENT_HARD_TIMEOUT_MINS`, range 1–60). See *Timeouts* above.
215
254
  - **Per-task/step override:** Use `timeout` in task/step params.
216
255
  - **Parent cancellation:** Aborting the parent tool call cancels all children.
217
256
  - **Sibling cancellation:** In parallel mode with `abortOnFailure: true`, the first failed task cancels running siblings.
@@ -229,10 +268,10 @@ See [CHANGELOG.md](CHANGELOG.md) for release history.
229
268
 
230
269
  ## Compatibility
231
270
 
232
- - Requires `@earendil-works/pi-coding-agent >=0.80.0 <0.83.0`
233
- - Requires `@earendil-works/pi-ai >=0.80.0 <0.83.0`
234
- - Requires `@earendil-works/pi-agent-core >=0.80.0 <0.83.0`
235
- - Requires `@earendil-works/pi-tui >=0.80.0 <0.83.0`
271
+ - Requires `@earendil-works/pi-coding-agent >=0.80.0 <0.85.0`
272
+ - Requires `@earendil-works/pi-ai >=0.80.0 <0.85.0`
273
+ - Requires `@earendil-works/pi-agent-core >=0.80.0 <0.85.0`
274
+ - Requires `@earendil-works/pi-tui >=0.80.0 <0.85.0`
236
275
  - Requires `typebox >=1.3.0 <2.0.0`
237
276
  - Requires Node.js >= 20.18
238
277
 
package/agent-format.md CHANGED
@@ -19,9 +19,9 @@ Project agents override user agents with the same name when `agentScope: "both"`
19
19
  name: my-agent # Required. Unique identifier (kebab-case).
20
20
  description: ... # Required. When to use this agent.
21
21
  tools: read, grep, ... # Optional. Comma-separated tool names. Defaults to all.
22
- model: provider/model # Optional explicit first choice (legacy compatible).
22
+ model: "@fast" # Optional. Role alias (quoted — @ is YAML-reserved) or provider/model.
23
23
  models: # Optional ordered fallbacks; comma form also accepted.
24
- - provider/fast-model
24
+ - "@fast"
25
25
  - provider/backup-model
26
26
  thinking: low # Optional: off|minimal|low|medium|high|xhigh|max.
27
27
  sandbox: read-only # Optional: read-only | workspace-write | worktree. Auto-derives tool restrictions.
@@ -29,9 +29,18 @@ color: cyan # Optional: red|blue|green|yellow|purple|orange|pink|c
29
29
  ---
30
30
  ```
31
31
 
32
+ ### Model roles
33
+
34
+ `model`/`models` entries may reference *roles* instead of concrete models:
35
+ `"@fast"`, `"@coder"`, `"@smart"`, or any role defined under
36
+ `subagent.roles` in `~/.pi/agent/settings.json`. A role expands to an ordered
37
+ fallback chain (string with commas, or array). `"*"` / `"@default"` mean
38
+ "use the parent model". A trailing `:level` (`"@smart:high"`) overrides the
39
+ agent's `thinking` for that match. See README → *Role-based model routing*.
40
+
32
41
  ### `sandbox`
33
42
 
34
- - `read-only`: Restricts tools to `read`, `grep`, `find`, `ls`. Overrides any `tools` field.
43
+ - `read-only`: Restricts tools to the read-only allowlist (`read`, `grep`, `find`, `ls` plus read-only extension tools when inherited — `web_*`, `serena_*`, `munin_*`, `fff*`, …). Overrides any `tools` field.
35
44
  - `workspace-write` (default): Uses the agent's `tools` list or defaults to all tools.
36
45
  - `worktree`: Runs the agent in an isolated git worktree (`.pi-worktrees/<id>` under the repo root). All file mutations land in the worktree; the main checkout is untouched. On completion, a unified diff of the changes is returned in the result (visible in the thread viewer as a `🌿 worktree` badge) — the parent merges explicitly via `apply_patch`/cherry-pick; nothing is applied automatically. Falls back to in-process execution when the cwd is not a git repo (with a warning). Requires git.
37
46
 
@@ -60,9 +69,16 @@ The `subagent` tool is always rejected to prevent recursive delegation.
60
69
  Unknown or misspelled tool names produce a clear error.
61
70
  Duplicate names are deduplicated automatically.
62
71
 
63
- Custom/extension tools are NOT available to sub-agents by default (each runs in an isolated in-memory session with no extensions).
72
+ **Tool inheritance:** an agent WITHOUT an explicit `tools:` line inherits every
73
+ tool the parent session has (minus `subagent`), including extension tools like
74
+ `web_search`, `serena_*`, `munin_*`, `obsidian`, and `notebooklm`, and runs with
75
+ the parent's extensions loaded. An agent WITH an explicit `tools:` list is
76
+ validated against built-ins ∪ the inherited set and, when the list contains
77
+ only built-ins, runs in a lean loader with no extensions/skills loaded. To
78
+ force an inheriting agent lean, set `tools: read, bash, edit, write, grep, find, ls`.
64
79
 
65
- Read-only service execution (used by `pi-review`) restricts tools to the read-only category.
80
+ Read-only service execution (used by `pi-review`) restricts tools to the
81
+ read-only allowlist (including read-only extension tools when inherited).
66
82
 
67
83
  ## Model Resolution
68
84
 
@@ -86,7 +102,7 @@ Children do not automatically load repository instructions. Callers may pass an
86
102
  Each sub-agent runs with:
87
103
  - **System prompt**: agent body only (~200-1K tokens typical)
88
104
  - **No AGENTS.md**: saves 500-5K tokens
89
- - **No extensions/skills loaded**: saves 200-1K tokens
105
+ - **No extensions/skills loaded**: saves 200-1K tokens — but only for agents whose effective tool set is built-in only (explicit `tools:` line). Agents that inherit parent tools load the parent's extensions into the child (higher token cost, full toolset).
90
106
  - **Thinking per role**: defaults off; bundled scout/reviewer/worker choose low/high/medium
91
107
  - **No compaction**: avoids compaction token cost
92
108
 
@@ -1,11 +1,7 @@
1
1
  ---
2
2
  name: general-purpose
3
3
  description: General-purpose sub-agent for any delegated task. Use when no specialized agent fits. Good for complex research, multi-step operations, and code modifications.
4
- models:
5
- - zai-coding-cn/glm-5.1
6
- - nvidia/mistralai/mistral-small-4-119b-2603
7
- - openrouter/nvidia/nemotron-3-super-120b-a12b:free
8
- - opencode-go/deepseek-v4-flash
4
+ model: "@coder"
9
5
  thinking: medium
10
6
  color: yellow
11
7
  ---
package/agents/planner.md CHANGED
@@ -2,10 +2,7 @@
2
2
  name: planner
3
3
  description: Read-only planning and architecture specialist. Use for consequential design, tradeoff analysis, and implementation plans.
4
4
  tools: read, grep, find, ls
5
- models:
6
- - zai-coding-cn/glm-5.3
7
- - openrouter/nvidia/nemotron-3-ultra-550b-a55b:free
8
- - opencode-go/deepseek-v4-pro
5
+ model: "@smart"
9
6
  thinking: high
10
7
  color: blue
11
8
  sandbox: read-only
@@ -2,10 +2,7 @@
2
2
  name: reviewer
3
3
  description: Code review specialist. Use for correctness, security, regression, and meaningful test-gap review.
4
4
  tools: read, grep, find, ls
5
- models:
6
- - zai-coding-cn/glm-5.3
7
- - openrouter/nvidia/nemotron-3-ultra-550b-a55b:free
8
- - opencode-go/deepseek-v4-pro
5
+ model: "@smart"
9
6
  thinking: high
10
7
  color: purple
11
8
  sandbox: read-only
package/agents/scout.md CHANGED
@@ -2,10 +2,7 @@
2
2
  name: scout
3
3
  description: Fast codebase recon that returns compressed context for handoff. Use for finding files, understanding structure, locating symbols.
4
4
  tools: read, grep, find, ls
5
- models:
6
- - zai-coding-cn/glm-5-turbo
7
- - nvidia/openai/gpt-oss-20b
8
- - opencode-go/deepseek-v4-flash
5
+ model: "@fast"
9
6
  thinking: off
10
7
  color: cyan
11
8
  sandbox: read-only
package/agents/tester.md CHANGED
@@ -2,10 +2,7 @@
2
2
  name: tester
3
3
  description: Focused verification agent. Use for cheap routine test, typecheck, lint, build, and regression checks without editing files.
4
4
  tools: read, bash, grep, find, ls
5
- models:
6
- - zai-coding-cn/glm-5-turbo
7
- - nvidia/openai/gpt-oss-20b
8
- - opencode-go/deepseek-v4-flash
5
+ model: "@fast"
9
6
  thinking: off
10
7
  color: orange
11
8
  ---
package/agents/worker.md CHANGED
@@ -1,11 +1,7 @@
1
1
  ---
2
2
  name: worker
3
3
  description: General-purpose coding agent with full tool access. Use only when explicitly requested for isolated implementation.
4
- models:
5
- - zai-coding-cn/glm-5.1
6
- - nvidia/mistralai/mistral-small-4-119b-2603
7
- - openrouter/nvidia/nemotron-3-super-120b-a12b:free
8
- - opencode-go/deepseek-v4-flash
4
+ model: "@coder"
9
5
  thinking: medium
10
6
  color: green
11
7
  ---
@@ -184,6 +184,8 @@ function loadAgentsFromDir(
184
184
  }
185
185
  for (const modelName of getModelCandidates({ model, models })) {
186
186
  if (modelName.includes("/")) continue;
187
+ // @role aliases and `*` (parent) are resolved at dispatch — not model ids.
188
+ if (modelName.startsWith("@") || modelName === "*") continue;
187
189
  diagnostics.push({
188
190
  filePath,
189
191
  issue: `Model "${modelName}" does not include a provider prefix (e.g., "anthropic/claude-sonnet-4-20250514"). Resolution may fail.`,
@@ -166,6 +166,19 @@ export function startBackgroundTask(input: StartBackgroundInput): StartBackgroun
166
166
  };
167
167
  backgroundTasks.set(taskId, bgTask);
168
168
 
169
+ // Record a running entry so a crash mid-run shows as "interrupted" after
170
+ // restart (recordHistory at completion upserts by id and replaces it).
171
+ try {
172
+ appendHistory(join(deps.ctx.cwd, CONFIG_DIR_NAME), {
173
+ id: taskId,
174
+ agent,
175
+ task,
176
+ status: "running",
177
+ startedAt,
178
+ background: true,
179
+ });
180
+ } catch { /* history file not writable — non-fatal */ }
181
+
169
182
  // Run detached — the parent does NOT await this.
170
183
  void deps
171
184
  .runOne(
@@ -89,12 +89,15 @@ export function findHistory(piDir: string, id: string): HistoryEntry | undefined
89
89
  * On session start, mark any `running` entries from a prior session as
90
90
  * `interrupted`. We cannot resume them (in-process SDK sessions don't survive
91
91
  * a restart). Honest about the architectural ceiling.
92
+ *
93
+ * `excludeIds` skips entries still live in this process (e.g. background tasks
94
+ * that keep running across a session reload — only shutdown aborts them).
92
95
  */
93
- export function markInterruptedOnRestart(piDir: string): number {
96
+ export function markInterruptedOnRestart(piDir: string, excludeIds: ReadonlySet<string> = new Set()): number {
94
97
  const entries = readHistory(piDir);
95
98
  let changed = 0;
96
99
  for (const e of entries) {
97
- if (e.status === "running") {
100
+ if (e.status === "running" && !excludeIds.has(e.id)) {
98
101
  e.status = "interrupted";
99
102
  changed++;
100
103
  }