@henryqw/pi-subagent 10.1.1 → 11.0.1

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/CONTEXT.md CHANGED
@@ -9,8 +9,8 @@ Provide validated built-in and user Roles, shared task-model Pi launch policy, g
9
9
  - **Main**: Pi session that plans and orchestrates delegated work.
10
10
  - **Subagent**: isolated Pi child process handling one task.
11
11
  - **Role**: package-shipped built-in or user-owned Markdown definition of a reusable responsibility, with a name, description, system instructions, required `tools`, `extensions`, and `skills` arrays, and optional isolation.
12
- - **Model Class**: `fast`, `balanced`, `frontier`, or `fav`, resolved through shared task-model settings; direct model/thinking overrides remain explicit user choices.
13
- - **Route**: configured model and thinking-level pair selected from a shared Model Class profile; the primary route precedes its optional fallback.
12
+ - **Model Class**: `fast`, `balanced`, `frontier`, or `fav`, resolved through shared task-model settings.
13
+ - **Route**: configured model and exact thinking-level pair selected from a shared Model Class profile; the primary route precedes its optional fallback. A direct model can replace only its model.
14
14
  - **Delegated Task**: one bounded work request sent from Main to one Role.
15
15
  - **Workflow**: generic orchestration of one or more Delegated Tasks; `delegate_task` owns its selected mode, while library callers compose executor runs in JavaScript.
16
16
  - **Workflow Mode**: `delegate_task` tool policy selected per call for `single`, `parallel`, or `chain` execution; not a Role property or executor API.
@@ -27,8 +27,8 @@ Provide validated built-in and user Roles, shared task-model Pi launch policy, g
27
27
  - Up to five active ephemeral `delegate_task` children run per Main by default, configurable via `maxSubagents` in `~/.pi/agent/config/pi-subagent/config.json` or the `PI_SUBAGENT_MAX_SUBAGENTS` environment variable; excess calls wait FIFO. Queued calls do not start a child or consume child timeout.
28
28
  - Ambient child extensions and Skills stay disabled. Every Role requires `tools`, `extensions`, and `skills` YAML arrays, and every launch installs the Role tool policy. `tools: []` activates no base built-ins but does activate all tools from explicitly selected trusted extension bundles and explicit caller tool additions; `skills: []` selects no separately named Role Skills but trusted selected extension Skills still load; `extensions: []` selects no Role extension bundle. A Role/caller explicitly selected extension is a trusted atomic capability bundle: all tools it registers and all Skills supplied through its Pi package metadata or dynamic `resources_discover` load alongside separately named Role Skills. This intentionally includes the extension's executable lifecycle/prompt behavior; pi-subagent does not infer or externally narrow undocumented dependencies, and loading an extension is not sandboxing. Scope children by selecting fewer trusted extensions; finer granularity requires separate entry points/configuration or an upstream split. Explicit Role/caller tool names still verify against the final filtered registry, while parent-only recursive orchestration tools remain excluded.
29
29
  - Role Skill names resolve through Main's effective Pi Skill registry; unavailable names warn and skip without blocking delegation. Explicit Role/caller tool names verify against the final filtered child registry after explicit provider `session_start` handlers, and unavailable names fail before the first turn.
30
- - Main policy populates direct `model` and `thinking` only for explicit user overrides; otherwise it selects only `modelClass`. This has no provenance tracking or runtime enforcement. An omitted class uses pi-subagent's local `pi-subagent/delegateTask` Model Task declaration (default `fast`); library callers select a Role plus their own Model Task declaration.
31
- - The selected profile resolves primary then fallback only before launch when a route, model, or thinking level is unavailable. A missing local JSON config uses defaults quietly. Missing shared task-model config warns once per session because delegation needs a route. If neither route is usable, launch rejects with `Run /task-models`; a started child is never retried by this package.
30
+ - Main selects `modelClass`; an omitted class uses pi-subagent's local `pi-subagent/delegateTask` Model Task declaration (default `fast`). A direct model replaces only the selected route model and must honor its exact thinking level. Library callers select a Role plus their own Model Task declaration.
31
+ - The selected profile resolves primary then fallback only before launch when a route, model, or thinking level is unavailable. A direct model never changes the route level and fails before launch if it cannot honor it. A missing local JSON config uses defaults quietly. Missing shared task-model config warns once per session because delegation needs a route. If neither route is usable, launch rejects with `Run /task-models`; a started child is never retried by this package.
32
32
  - User Role Markdown files and Subagent JSON config (`config/pi-subagent/config.json`) live only in the user `config/pi-subagent` directory; model routes live in shared `config/pi-task-models/config.json`. Package-shipped built-in Roles (`implementer`, `reviewer`, `scout`) resolve from the package's own `examples/roles/` Markdown through the same parser; same-named user files override built-ins for `delegate_task`, while same-named `implementer` and `reviewer` files override Flow defaults. The `synthesizer` Markdown remains the only optional inert sample.
33
33
  - `delegate_flow` accepts 1–8 independent units with direct validation commands, optional `modelClass`, and optional non-empty `review` judgment text. Omitted classes use pi-subagent's local `pi-subagent/delegateTask` declaration (default `fast`); each unit's current class resolves through its shared profile route for its Implementer and, when `review` exists, Reviewer. At Flow start it always resolves/freezes the effective Implementer and resolves/freezes a Reviewer only when at least one requested unit has `review`. One active Flow creates every Unit Worktree before launching Implementers in parallel, then processes settled units in declared order. For each unit Flow rebases in place when earlier units advanced Main, inspects committed Git state, and runs declared validation. Validation is authoritative for objective verification: units without `review` fast-forward their exact validated tip; units with `review` send the exact Review Packet to the Reviewer in the same worktree and require exact `PASS` before the same guarded `git merge --ff-only` path.
34
34
  - Flow is memory-only. Only a post-rebase commit drop produces a no-op (`base === tip`); it validates, skips Reviewer and merge, then cleans up ordinarily. Initial zero-commit implementations block. Implementer, validation, or reviewer blocks allow one `delegate_flow_continue({ guidance, modelClass? })` repair in the same worktree; omission retains the Unit's current class and presence replaces it for that repair. A second block is terminal. Rebase and evidence/Reviewer/infrastructure failures retain worktrees. A reported fast-forward failure retains its worktree unless Main is clean at the exact integrated tip, which completes with the merge diagnostic as a warning. Cleanup uses non-forced worktree removal and branch deletion; cleanup refusal is a completion warning.
package/README.md CHANGED
@@ -44,27 +44,25 @@ Select exactly one shape:
44
44
 
45
45
  ```text
46
46
  // Single
47
- { role, name, task, model?, modelClass?, thinking?, background? }
47
+ { role, name, task, model?, modelClass?, background? }
48
48
 
49
49
  // Parallel: 1–8 independent delegations
50
- { tasks: [{ role, name, task, model?, modelClass?, thinking? }], background? }
50
+ { tasks: [{ role, name, task, model?, modelClass? }], background? }
51
51
 
52
52
  // Chain: 1–8 dependent delegations
53
- { chain: [{ role, name, task, model?, modelClass?, thinking? }], background? }
53
+ { chain: [{ role, name, task, model?, modelClass? }], background? }
54
54
  ```
55
55
 
56
56
  Main supplies every delegation's required `name`: a short description of about five words and fewer than 30 characters. Names must not contain C0/C1 control characters, including newlines and terminal escape characters.
57
57
 
58
- #### Model precedence
58
+ #### Model routing
59
59
 
60
- 1. An explicit `model` is `provider/modelId` and overrides `modelClass`.
61
- 2. Main sets direct `model` and `thinking` only for an explicit user override.
62
- 3. Otherwise, Main selects `modelClass` according to the [delegation policy](./docs/orchestration.md#delegation-fields).
63
- 4. `modelClass` is `fast`, `balanced`, `frontier`, or `fav`.
64
- When omitted, it uses pi-subagent's local `pi-subagent/delegateTask` Model Task declaration.
65
- That declaration defaults to `fast` and shared config can explicitly override it.
60
+ 1. `modelClass` selects a route. It is `fast`, `balanced`, `frontier`, or `fav`.
61
+ 2. An omitted class uses pi-subagent's `pi-subagent/delegateTask` declaration. Its default is `fast`.
62
+ 3. The route supplies the model and exact thinking level.
63
+ 4. An explicit `model` is `provider/modelId`. It replaces only the route model and must support its thinking level.
66
64
 
67
- This is Main policy only. The runtime records no provenance and does not enforce it. `background` applies to the whole selected mode and is never a per-delegation field.
65
+ `background` applies to the whole selected mode. It is never a per-delegation field.
68
66
 
69
67
  The transient status widget renders one line per child with: status glyph, bracketed uppercase role initial (`[I]` for `implementer`, `[R]` for `reviewer`, and likewise for custom Roles), Main-supplied short name, activity (thinking… or active tool with elapsed time and path basename), and metrics (completed turns, started tools, compact `model·thinking` pair, tokens, total duration). Rows are ordered active-first (working items first, stable insertion order for the rest). A hard six-physical-line maximum applies: when total items are six or fewer, all child rows render; above six, five child rows plus one status-aware overflow line render (`… N more · X working · Y complete · Z failed · W stopped`). Terminal rows clear on the next real user input; active rows persist until the child settles. It is separate from Pi's built-in tool-execution block.
70
68
 
@@ -92,28 +90,7 @@ delegate_flow({ units: [{ id, name, task, modelClass?, validation: [{ command, a
92
90
  delegate_flow_continue({ guidance, modelClass? })
93
91
  ```
94
92
 
95
- ```mermaid
96
- flowchart LR
97
- setup["Setup: resolve roles and create Unit Worktrees"] --> implement["Implementers in parallel"]
98
- implement -->|Implementer block| repair["One repair: delegate_flow_continue"]
99
- implement --> rebase["Rebase, inspect committed state, and validate"]
100
- rebase -->|Post-rebase no-op| cleanup["Cleanup"]
101
- rebase --> review{"Review requested?"}
102
- review -->|No| integrate["Integrate in declared order"]
103
- review -->|Yes| packet["Exact {base, tip, patchPath}; require PASS"]
104
- packet -->|PASS| integrate
105
- rebase -->|Validation block| repair
106
- packet -->|Review block| repair
107
- repair --> revalidate["Revalidate repaired work"]
108
- revalidate -->|No review requested| integrate
109
- revalidate -->|Review required| rereview["Re-review exact {base, tip, patchPath}; require PASS"]
110
- rereview -->|PASS| integrate
111
- revalidate -->|Second block| terminal["Terminal failure: retain worktree"]
112
- rereview -->|Second block| terminal
113
- rebase -->|Rebase or infrastructure failure| terminal
114
- repair -->|Infrastructure failure| terminal
115
- integrate --> cleanup
116
- ```
93
+ ![Delegate Flow lifecycle](./docs/delegate-flow.svg)
117
94
 
118
95
  Objective verification is authoritative. Flow always inspects committed Git state and runs declared validation.
119
96
 
package/dist/index.d.ts CHANGED
@@ -54,7 +54,7 @@ export declare function loadBuiltinRole(name: BuiltinRoleName): Role;
54
54
  * user files are an error. Missing user directory returns the built-ins.
55
55
  */
56
56
  export declare function loadRoles(agentDir?: string): Role[];
57
- export declare function resolveTaskRoute(ctx: ExtensionContext, profileName: ProfileName, agentDir?: string, thinking?: ThinkingLevel): ResolvedTaskRoute;
57
+ export declare function resolveTaskRoute(ctx: ExtensionContext, profileName: ProfileName, agentDir?: string): ResolvedTaskRoute;
58
58
  export declare function resolveRoleSkills(pi: Pick<ExtensionAPI, "getCommands">, role: Role): ResolvedRoleSkills;
59
59
  export declare function createRoleLaunch(pi: Pick<ExtensionAPI, "getCommands">, ctx: Pick<ExtensionContext, "isProjectTrusted">, input: CreateRoleLaunchInput): ResolvedRoleLaunch;
60
60
  export declare function resolveRoleLaunch(pi: Pick<ExtensionAPI, "getCommands">, ctx: ExtensionContext, input: ResolveRoleLaunchInput): ResolvedRoleLaunch;
package/dist/index.js CHANGED
@@ -117,7 +117,7 @@ export function loadRoles(agentDir = getAgentDir()) {
117
117
  }
118
118
  return [...byName.values()];
119
119
  }
120
- export function resolveTaskRoute(ctx, profileName, agentDir = getAgentDir(), thinking) {
120
+ export function resolveTaskRoute(ctx, profileName, agentDir = getAgentDir()) {
121
121
  let config;
122
122
  try {
123
123
  config = loadTaskModelsConfig(agentDir).value;
@@ -125,17 +125,17 @@ export function resolveTaskRoute(ctx, profileName, agentDir = getAgentDir(), thi
125
125
  catch {
126
126
  throw new Error("Couldn't read task model config. Run /task-models.");
127
127
  }
128
- return resolveConfiguredRoute(ctx, profileName, config.profiles[profileName], agentDir, thinking);
128
+ return resolveConfiguredRoute(ctx, profileName, config.profiles[profileName], agentDir);
129
129
  }
130
- function resolveConfiguredRoute(ctx, profileName, profile, agentDir = getAgentDir(), thinking) {
130
+ function resolveConfiguredRoute(ctx, profileName, profile, agentDir = getAgentDir()) {
131
131
  if (!profile)
132
132
  throw new Error(`No ${profileName} task model profile is configured. Run /task-models.`);
133
133
  for (const route of orderedProfileRoutes(profile)) {
134
- const resolved = resolveTaskModelRoute(ctx, route, agentDir, thinking);
134
+ const resolved = resolveTaskModelRoute(ctx, route, agentDir);
135
135
  if (resolved)
136
136
  return resolved;
137
137
  }
138
- throw new Error(`No usable ${profileName} task model route${thinking ? ` supporting thinking ${thinking}` : ""}. Run /task-models.`);
138
+ throw new Error(`No usable ${profileName} task model route. Run /task-models.`);
139
139
  }
140
140
  export function resolveRoleSkills(pi, role) {
141
141
  const skills = new Map(pi.getCommands()
@@ -14,7 +14,7 @@ Resource Policy is split at launch preparation:
14
14
 
15
15
  Built-in `implementer`, `reviewer`, and `scout` Roles ship as Markdown in `examples/roles/` and use the same parser as user Roles. For generic delegation, a same-named user Role explicitly overrides a built-in. The package does not install, copy, or write user configuration.
16
16
 
17
- Main populates direct `model` and `thinking` only for explicit user overrides; otherwise it chooses only `modelClass` according to delegation tool policy. This is not executor provenance tracking or runtime enforcement.
17
+ `modelClass` selects a task-model route. The route owns its exact thinking level. A direct `model` replaces only the route model and must honor that level.
18
18
 
19
19
  ## Scope boundary
20
20
 
@@ -0,0 +1,202 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Delegate Flow lifecycle</title>
7
+ <link href="https://fonts.googleapis.com/css2?family=Geist:wght@400;500;600;700&amp;family=Geist+Mono:wght@400;500;600&amp;display=swap" rel="stylesheet">
8
+ <style>
9
+ *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
10
+ body { min-height: 100vh; display: grid; place-items: center; padding: 32px; background: #f0eee9; color: #101828; }
11
+ .frame { width: min(1280px, 100%); }
12
+ .eyebrow { margin-bottom: 8px; color: #4c5665; font: 500 8px/1 'Geist Mono', monospace; letter-spacing: .18em; text-transform: uppercase; }
13
+ h1 { margin-bottom: 24px; font: 600 28px/1.15 'Geist', sans-serif; letter-spacing: -.03em; }
14
+ svg { display: block; width: 100%; min-width: 960px; }
15
+ </style>
16
+ </head>
17
+ <body>
18
+ <main class="frame">
19
+ <p class="eyebrow">Flowchart · @henryqw/pi-subagent</p>
20
+ <h1>Delegate Flow lifecycle</h1>
21
+ <svg viewBox="0 0 1280 720" xmlns="http://www.w3.org/2000/svg" role="img" aria-labelledby="delegate-flow-title delegate-flow-desc">
22
+ <title id="delegate-flow-title">Delegate Flow lifecycle</title>
23
+ <desc id="delegate-flow-desc">Flowchart showing setup, implementation, validation, optional exact review, one repair attempt, integration, cleanup, and retained-worktree failure outcomes.</desc>
24
+ <defs>
25
+ <style>
26
+ .zone { fill: #e6e4de; stroke: rgba(16,24,40,.141); stroke-width: 1; }
27
+ .zone-label { fill: #6a7282; font: 500 8px 'Geist Mono', monospace; letter-spacing: .18em; }
28
+ .node { fill: #fff; stroke: #101828; stroke-width: 1; }
29
+ .node-soft { fill: rgba(16,24,40,.03); stroke: rgba(16,24,40,.32); stroke-width: 1; }
30
+ .node-focal { fill: rgba(29,78,216,.08); stroke: #1d4ed8; stroke-width: 1.2; }
31
+ .node-failure { fill: rgba(16,24,40,.02); stroke: rgba(16,24,40,.42); stroke-width: 1; stroke-dasharray: 4 4; }
32
+ .name { fill: #101828; font: 600 12px 'Geist', sans-serif; text-anchor: middle; }
33
+ .sub { fill: #4c5665; font: 400 8px 'Geist Mono', monospace; text-anchor: middle; }
34
+ .edge { fill: none; stroke: #4c5665; stroke-width: 1.2; marker-end: url(#arrow); }
35
+ .edge-failure { fill: none; stroke: #6a7282; stroke-width: 1.2; stroke-dasharray: 5 4; marker-end: url(#arrow); }
36
+ .edge-success { fill: none; stroke: #1d4ed8; stroke-width: 1.4; marker-end: url(#arrow-accent); }
37
+ .label-mask { fill: #f0eee9; }
38
+ .label { fill: #4c5665; font: 500 8px 'Geist Mono', monospace; text-anchor: middle; letter-spacing: .04em; }
39
+ .label-success { fill: #1d4ed8; font: 500 8px 'Geist Mono', monospace; text-anchor: middle; letter-spacing: .04em; }
40
+ .legend { fill: #4c5665; font: 400 8px 'Geist', sans-serif; }
41
+ </style>
42
+ <marker id="arrow" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto"><polygon points="0 0,8 3,0 6" fill="#4c5665"/></marker>
43
+ <marker id="arrow-accent" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto"><polygon points="0 0,8 3,0 6" fill="#1d4ed8"/></marker>
44
+ <marker id="arrow-link" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto"><polygon points="0 0,8 3,0 6" fill="#1d4ed8"/></marker>
45
+ </defs>
46
+
47
+ <rect width="1280" height="720" fill="#f0eee9"/>
48
+
49
+ <!-- Zones -->
50
+ <rect class="zone" x="40" y="40" width="760" height="280" rx="8"/>
51
+ <text class="zone-label" x="56" y="64">INITIAL RUN</text>
52
+ <rect class="zone" x="40" y="340" width="760" height="260" rx="8"/>
53
+ <text class="zone-label" x="56" y="364">ONE REPAIR MAXIMUM</text>
54
+ <rect class="zone" x="840" y="40" width="400" height="560" rx="8"/>
55
+ <text class="zone-label" x="856" y="64">OUTCOMES</text>
56
+
57
+ <!-- Initial-run edges -->
58
+ <path class="edge" d="M248 128 H288"/>
59
+ <path class="edge" d="M464 128 H504"/>
60
+ <path class="edge" d="M600 128 H624"/>
61
+ <path class="edge" d="M784 128 H872"/>
62
+ <path class="edge" d="M704 176 V216"/>
63
+ <path class="edge-success" d="M784 248 H812 Q820 248 820 240 V152 Q820 144 828 144 H856 Q864 144 864 136 V128 H872"/>
64
+
65
+ <!-- Blocked initial-run paths to repair -->
66
+ <path class="edge" d="M376 160 V304 Q376 312 368 312 H160 Q152 312 152 320 V412"/>
67
+ <path class="edge" d="M552 160 V288 Q552 296 544 296 H136 Q128 296 128 304 V412"/>
68
+ <path class="edge" d="M664 280 V320 Q664 328 656 328 H184 Q176 328 176 336 V412"/>
69
+
70
+ <!-- Repair path -->
71
+ <path class="edge" d="M272 444 H320"/>
72
+ <path class="edge" d="M520 444 H592"/>
73
+ <path class="edge-success" d="M384 412 V384 Q384 376 392 376 H808 Q816 376 816 368 V120 Q816 112 824 112 H856 Q864 112 864 120 V128 H872"/>
74
+ <path class="edge-success" d="M768 444 H824 Q832 444 832 436 V168 Q832 160 840 160 H856 Q864 160 864 152 V128 H872"/>
75
+
76
+ <!-- Success outcome -->
77
+ <path class="edge-success" d="M880 128 H912"/>
78
+ <path class="edge-success" d="M1012 160 V216"/>
79
+ <path class="edge" d="M552 96 V80 Q552 72 560 72 H1144 Q1152 72 1152 80 V240 Q1152 248 1144 248 H1112"/>
80
+
81
+ <!-- Failure paths, fanned into one merge -->
82
+ <path class="edge-failure" d="M584 160 V304 Q584 312 592 312 H808 Q816 312 816 320 V488 Q816 496 824 496 H872"/>
83
+ <path class="edge-failure" d="M232 476 V568 Q232 576 240 576 H856 Q864 576 864 568 V512 Q864 504 872 504"/>
84
+ <path class="edge-failure" d="M472 476 V536 Q472 544 480 544 H840 Q848 544 848 536 V520 Q848 512 856 512 H872"/>
85
+ <path class="edge-failure" d="M768 460 H808 Q816 460 816 468 V512 Q816 520 824 520 H856 Q864 520 864 512 V504 H872"/>
86
+ <path class="edge-failure" d="M880 504 H912"/>
87
+
88
+ <!-- Completion-warning and integration-failure outcomes -->
89
+ <path class="edge-failure" d="M1012 280 V336"/>
90
+ <path class="edge-failure" d="M1112 136 H1184 Q1192 136 1192 144 V356 Q1192 364 1184 364 H1152"/>
91
+ <path class="edge-failure" d="M1112 112 H1208 Q1216 112 1216 120 V496 Q1216 504 1208 504 H1152"/>
92
+
93
+ <!-- Edge labels -->
94
+ <rect class="label-mask" x="272" y="252" width="128" height="12" rx="2"/>
95
+ <text class="label" x="336" y="261">IMPLEMENTER BLOCK</text>
96
+ <rect class="label-mask" x="416" y="276" width="120" height="12" rx="2"/>
97
+ <text class="label" x="476" y="285">VALIDATION BLOCK</text>
98
+ <rect class="label-mask" x="504" y="316" width="96" height="12" rx="2"/>
99
+ <text class="label" x="552" y="325">REVIEW BLOCK</text>
100
+ <rect class="label-mask" x="800" y="100" width="32" height="12" rx="2"/>
101
+ <text class="label" x="816" y="109">NO</text>
102
+ <rect class="label-mask" x="688" y="188" width="32" height="12" rx="2"/>
103
+ <text class="label" x="704" y="197">YES</text>
104
+ <rect class="label-mask" x="828" y="224" width="40" height="12" rx="2"/>
105
+ <text class="label-success" x="848" y="233">PASS</text>
106
+ <rect class="label-mask" x="520" y="364" width="144" height="12" rx="2"/>
107
+ <text class="label-success" x="592" y="373">NO REVIEW REQUESTED</text>
108
+ <rect class="label-mask" x="528" y="424" width="56" height="12" rx="2"/>
109
+ <text class="label" x="556" y="433">REVIEW</text>
110
+ <rect class="label-mask" x="800" y="404" width="40" height="12" rx="2"/>
111
+ <text class="label-success" x="820" y="413">PASS</text>
112
+ <rect class="label-mask" x="816" y="292" width="160" height="12" rx="2"/>
113
+ <text class="label" x="896" y="301">REBASE / INFRA FAILURE</text>
114
+ <rect class="label-mask" x="336" y="556" width="136" height="12" rx="2"/>
115
+ <text class="label" x="404" y="565">INFRASTRUCTURE FAILURE</text>
116
+ <rect class="label-mask" x="560" y="524" width="96" height="12" rx="2"/>
117
+ <text class="label" x="608" y="533">SECOND BLOCK</text>
118
+ <rect class="label-mask" x="748" y="476" width="96" height="12" rx="2"/>
119
+ <text class="label" x="796" y="485">SECOND BLOCK</text>
120
+ <rect class="label-mask" x="920" y="60" width="128" height="12" rx="2"/>
121
+ <text class="label" x="984" y="69">POST-REBASE NO-OP</text>
122
+ <rect class="label-mask" x="1024" y="296" width="120" height="12" rx="2"/>
123
+ <text class="label" x="1084" y="305">CLEANUP REFUSAL</text>
124
+ <rect class="label-mask" x="1024" y="312" width="152" height="12" rx="2"/>
125
+ <text class="label" x="1100" y="321">FF DIAGNOSTIC / CLEAN TIP</text>
126
+ <rect class="label-mask" x="1064" y="432" width="136" height="12" rx="2"/>
127
+ <text class="label" x="1132" y="441">OTHER FF FAILURE</text>
128
+
129
+ <!-- Nodes -->
130
+ <rect class="node-soft" x="72" y="96" width="176" height="64" rx="32"/>
131
+ <text class="name" x="160" y="124">Resolve roles</text>
132
+ <text class="sub" x="160" y="140">CREATE UNIT WORKTREES</text>
133
+
134
+ <rect class="node" x="288" y="96" width="176" height="64" rx="6"/>
135
+ <text class="name" x="376" y="124">Implement in parallel</text>
136
+ <text class="sub" x="376" y="140">EACH UNIT ISOLATED</text>
137
+
138
+ <rect class="node" x="504" y="96" width="96" height="64" rx="6"/>
139
+ <text class="name" x="552" y="120">Rebase</text>
140
+ <text class="sub" x="552" y="136">INSPECT</text>
141
+ <text class="sub" x="552" y="148">VALIDATE</text>
142
+
143
+ <polygon class="node" points="704,80 784,128 704,176 624,128"/>
144
+ <text class="name" x="704" y="124">Review</text>
145
+ <text class="sub" x="704" y="140">REQUESTED?</text>
146
+
147
+ <rect class="node" x="624" y="216" width="160" height="64" rx="6"/>
148
+ <text class="name" x="704" y="240">Exact review packet</text>
149
+ <text class="sub" x="704" y="256">BASE · TIP · PATCH</text>
150
+ <text class="sub" x="704" y="268">REQUIRE PASS</text>
151
+
152
+ <rect class="node-focal" x="912" y="96" width="200" height="64" rx="6"/>
153
+ <text class="name" x="1012" y="124">Integrate in order</text>
154
+ <text class="sub" x="1012" y="140">FAST-FORWARD EXACT TIP</text>
155
+
156
+ <rect class="node-soft" x="912" y="216" width="200" height="64" rx="32"/>
157
+ <text class="name" x="1012" y="244">Cleanup</text>
158
+ <text class="sub" x="1012" y="260">REMOVE WORKTREE + BRANCH</text>
159
+
160
+ <rect class="node-soft" x="912" y="336" width="240" height="72" rx="32"/>
161
+ <text class="name" x="1032" y="364">Completion warning</text>
162
+ <text class="sub" x="1032" y="380">CLEAN TIP · OR CLEANUP REFUSAL</text>
163
+ <text class="sub" x="1032" y="392">RETAIN REPORTED RESOURCES</text>
164
+
165
+ <rect class="node" x="72" y="412" width="200" height="64" rx="6"/>
166
+ <text class="name" x="172" y="440">One repair</text>
167
+ <text class="sub" x="172" y="456">DELEGATE_FLOW_CONTINUE</text>
168
+
169
+ <rect class="node" x="320" y="412" width="200" height="64" rx="6"/>
170
+ <text class="name" x="420" y="440">Revalidate repair</text>
171
+ <text class="sub" x="420" y="456">SAME UNIT WORKTREE</text>
172
+
173
+ <rect class="node" x="592" y="412" width="176" height="64" rx="6"/>
174
+ <text class="name" x="680" y="440">Re-review packet</text>
175
+ <text class="sub" x="680" y="456">REQUIRE PASS AGAIN</text>
176
+
177
+ <rect class="node-failure" x="912" y="472" width="240" height="64" rx="32"/>
178
+ <text class="name" x="1032" y="500">Terminal failure</text>
179
+ <text class="sub" x="1032" y="516">RETAIN UNIT WORKTREE</text>
180
+
181
+ <!-- Merge points -->
182
+ <circle cx="876" cy="128" r="4" fill="#1d4ed8"/>
183
+ <circle cx="876" cy="504" r="4" fill="#6a7282"/>
184
+
185
+ <!-- Legend -->
186
+ <line x1="40" y1="636" x2="1240" y2="636" stroke="rgba(16,24,40,.141)" stroke-width="0.8"/>
187
+ <text class="zone-label" x="40" y="656">LEGEND</text>
188
+ <rect class="node" x="120" y="648" width="24" height="12" rx="2"/>
189
+ <text class="legend" x="152" y="658">Step</text>
190
+ <polygon class="node" points="236,654 248,648 260,654 248,660"/>
191
+ <text class="legend" x="272" y="658">Decision</text>
192
+ <circle cx="380" cy="654" r="4" fill="#4c5665"/>
193
+ <text class="legend" x="392" y="658">Branch merge</text>
194
+ <line x1="512" y1="654" x2="548" y2="654" stroke="#1d4ed8" stroke-width="1.4" marker-end="url(#arrow-accent)"/>
195
+ <text class="legend" x="560" y="658">Validated path</text>
196
+ <line x1="696" y1="654" x2="732" y2="654" stroke="#6a7282" stroke-width="1.2" stroke-dasharray="5 4" marker-end="url(#arrow)"/>
197
+ <text class="legend" x="744" y="658">Terminal path</text>
198
+ <text class="sub" x="1120" y="658">ONE REPAIR · NO AUTOMATIC RETRY</text>
199
+ </svg>
200
+ </main>
201
+ </body>
202
+ </html>
@@ -0,0 +1,181 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <svg viewBox="0 0 1280 720" xmlns="http://www.w3.org/2000/svg" role="img" aria-labelledby="delegate-flow-title delegate-flow-desc">
3
+ <title id="delegate-flow-title">Delegate Flow lifecycle</title>
4
+ <desc id="delegate-flow-desc">Flowchart showing setup, implementation, validation, optional exact review, one repair attempt, integration, cleanup, and retained-worktree failure outcomes.</desc>
5
+ <defs>
6
+ <style>
7
+ @import url(&quot;https://fonts.googleapis.com/css2?family=Geist:wght@400;500;600;700&amp;family=Geist+Mono:wght@400;500;600&amp;display=swap&quot;);
8
+ .zone { fill: #e6e4de; stroke: rgba(16,24,40,.141); stroke-width: 1; }
9
+ .zone-label { fill: #6a7282; font: 500 8px 'Geist Mono', monospace; letter-spacing: .18em; }
10
+ .node { fill: #fff; stroke: #101828; stroke-width: 1; }
11
+ .node-soft { fill: rgba(16,24,40,.03); stroke: rgba(16,24,40,.32); stroke-width: 1; }
12
+ .node-focal { fill: rgba(29,78,216,.08); stroke: #1d4ed8; stroke-width: 1.2; }
13
+ .node-failure { fill: rgba(16,24,40,.02); stroke: rgba(16,24,40,.42); stroke-width: 1; stroke-dasharray: 4 4; }
14
+ .name { fill: #101828; font: 600 12px 'Geist', sans-serif; text-anchor: middle; }
15
+ .sub { fill: #4c5665; font: 400 8px 'Geist Mono', monospace; text-anchor: middle; }
16
+ .edge { fill: none; stroke: #4c5665; stroke-width: 1.2; marker-end: url(#arrow); }
17
+ .edge-failure { fill: none; stroke: #6a7282; stroke-width: 1.2; stroke-dasharray: 5 4; marker-end: url(#arrow); }
18
+ .edge-success { fill: none; stroke: #1d4ed8; stroke-width: 1.4; marker-end: url(#arrow-accent); }
19
+ .label-mask { fill: #f0eee9; }
20
+ .label { fill: #4c5665; font: 500 8px 'Geist Mono', monospace; text-anchor: middle; letter-spacing: .04em; }
21
+ .label-success { fill: #1d4ed8; font: 500 8px 'Geist Mono', monospace; text-anchor: middle; letter-spacing: .04em; }
22
+ .legend { fill: #4c5665; font: 400 8px 'Geist', sans-serif; }
23
+ </style>
24
+ <marker id="arrow" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto"><polygon points="0 0,8 3,0 6" fill="#4c5665"/></marker>
25
+ <marker id="arrow-accent" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto"><polygon points="0 0,8 3,0 6" fill="#1d4ed8"/></marker>
26
+ <marker id="arrow-link" markerWidth="8" markerHeight="6" refX="7" refY="3" orient="auto"><polygon points="0 0,8 3,0 6" fill="#1d4ed8"/></marker>
27
+ </defs>
28
+
29
+ <rect width="1280" height="720" fill="#f0eee9"/>
30
+
31
+ <!-- Zones -->
32
+ <rect class="zone" x="40" y="40" width="760" height="280" rx="8"/>
33
+ <text class="zone-label" x="56" y="64">INITIAL RUN</text>
34
+ <rect class="zone" x="40" y="340" width="760" height="260" rx="8"/>
35
+ <text class="zone-label" x="56" y="364">ONE REPAIR MAXIMUM</text>
36
+ <rect class="zone" x="840" y="40" width="400" height="560" rx="8"/>
37
+ <text class="zone-label" x="856" y="64">OUTCOMES</text>
38
+
39
+ <!-- Initial-run edges -->
40
+ <path class="edge" d="M248 128 H288"/>
41
+ <path class="edge" d="M464 128 H504"/>
42
+ <path class="edge" d="M600 128 H624"/>
43
+ <path class="edge" d="M784 128 H872"/>
44
+ <path class="edge" d="M704 176 V216"/>
45
+ <path class="edge-success" d="M784 248 H812 Q820 248 820 240 V152 Q820 144 828 144 H856 Q864 144 864 136 V128 H872"/>
46
+
47
+ <!-- Blocked initial-run paths to repair -->
48
+ <path class="edge" d="M376 160 V304 Q376 312 368 312 H160 Q152 312 152 320 V412"/>
49
+ <path class="edge" d="M552 160 V288 Q552 296 544 296 H136 Q128 296 128 304 V412"/>
50
+ <path class="edge" d="M664 280 V320 Q664 328 656 328 H184 Q176 328 176 336 V412"/>
51
+
52
+ <!-- Repair path -->
53
+ <path class="edge" d="M272 444 H320"/>
54
+ <path class="edge" d="M520 444 H592"/>
55
+ <path class="edge-success" d="M384 412 V384 Q384 376 392 376 H808 Q816 376 816 368 V120 Q816 112 824 112 H856 Q864 112 864 120 V128 H872"/>
56
+ <path class="edge-success" d="M768 444 H824 Q832 444 832 436 V168 Q832 160 840 160 H856 Q864 160 864 152 V128 H872"/>
57
+
58
+ <!-- Success outcome -->
59
+ <path class="edge-success" d="M880 128 H912"/>
60
+ <path class="edge-success" d="M1012 160 V216"/>
61
+ <path class="edge" d="M552 96 V80 Q552 72 560 72 H1144 Q1152 72 1152 80 V240 Q1152 248 1144 248 H1112"/>
62
+
63
+ <!-- Failure paths, fanned into one merge -->
64
+ <path class="edge-failure" d="M584 160 V304 Q584 312 592 312 H808 Q816 312 816 320 V488 Q816 496 824 496 H872"/>
65
+ <path class="edge-failure" d="M232 476 V568 Q232 576 240 576 H856 Q864 576 864 568 V512 Q864 504 872 504"/>
66
+ <path class="edge-failure" d="M472 476 V536 Q472 544 480 544 H840 Q848 544 848 536 V520 Q848 512 856 512 H872"/>
67
+ <path class="edge-failure" d="M768 460 H808 Q816 460 816 468 V512 Q816 520 824 520 H856 Q864 520 864 512 V504 H872"/>
68
+ <path class="edge-failure" d="M880 504 H912"/>
69
+
70
+ <!-- Completion-warning and integration-failure outcomes -->
71
+ <path class="edge-failure" d="M1012 280 V336"/>
72
+ <path class="edge-failure" d="M1112 136 H1184 Q1192 136 1192 144 V356 Q1192 364 1184 364 H1152"/>
73
+ <path class="edge-failure" d="M1112 112 H1208 Q1216 112 1216 120 V496 Q1216 504 1208 504 H1152"/>
74
+
75
+ <!-- Edge labels -->
76
+ <rect class="label-mask" x="272" y="252" width="128" height="12" rx="2"/>
77
+ <text class="label" x="336" y="261">IMPLEMENTER BLOCK</text>
78
+ <rect class="label-mask" x="416" y="276" width="120" height="12" rx="2"/>
79
+ <text class="label" x="476" y="285">VALIDATION BLOCK</text>
80
+ <rect class="label-mask" x="504" y="316" width="96" height="12" rx="2"/>
81
+ <text class="label" x="552" y="325">REVIEW BLOCK</text>
82
+ <rect class="label-mask" x="800" y="100" width="32" height="12" rx="2"/>
83
+ <text class="label" x="816" y="109">NO</text>
84
+ <rect class="label-mask" x="688" y="188" width="32" height="12" rx="2"/>
85
+ <text class="label" x="704" y="197">YES</text>
86
+ <rect class="label-mask" x="828" y="224" width="40" height="12" rx="2"/>
87
+ <text class="label-success" x="848" y="233">PASS</text>
88
+ <rect class="label-mask" x="520" y="364" width="144" height="12" rx="2"/>
89
+ <text class="label-success" x="592" y="373">NO REVIEW REQUESTED</text>
90
+ <rect class="label-mask" x="528" y="424" width="56" height="12" rx="2"/>
91
+ <text class="label" x="556" y="433">REVIEW</text>
92
+ <rect class="label-mask" x="800" y="404" width="40" height="12" rx="2"/>
93
+ <text class="label-success" x="820" y="413">PASS</text>
94
+ <rect class="label-mask" x="816" y="292" width="160" height="12" rx="2"/>
95
+ <text class="label" x="896" y="301">REBASE / INFRA FAILURE</text>
96
+ <rect class="label-mask" x="336" y="556" width="136" height="12" rx="2"/>
97
+ <text class="label" x="404" y="565">INFRASTRUCTURE FAILURE</text>
98
+ <rect class="label-mask" x="560" y="524" width="96" height="12" rx="2"/>
99
+ <text class="label" x="608" y="533">SECOND BLOCK</text>
100
+ <rect class="label-mask" x="748" y="476" width="96" height="12" rx="2"/>
101
+ <text class="label" x="796" y="485">SECOND BLOCK</text>
102
+ <rect class="label-mask" x="920" y="60" width="128" height="12" rx="2"/>
103
+ <text class="label" x="984" y="69">POST-REBASE NO-OP</text>
104
+ <rect class="label-mask" x="1024" y="296" width="120" height="12" rx="2"/>
105
+ <text class="label" x="1084" y="305">CLEANUP REFUSAL</text>
106
+ <rect class="label-mask" x="1024" y="312" width="152" height="12" rx="2"/>
107
+ <text class="label" x="1100" y="321">FF DIAGNOSTIC / CLEAN TIP</text>
108
+ <rect class="label-mask" x="1064" y="432" width="136" height="12" rx="2"/>
109
+ <text class="label" x="1132" y="441">OTHER FF FAILURE</text>
110
+
111
+ <!-- Nodes -->
112
+ <rect class="node-soft" x="72" y="96" width="176" height="64" rx="32"/>
113
+ <text class="name" x="160" y="124">Resolve roles</text>
114
+ <text class="sub" x="160" y="140">CREATE UNIT WORKTREES</text>
115
+
116
+ <rect class="node" x="288" y="96" width="176" height="64" rx="6"/>
117
+ <text class="name" x="376" y="124">Implement in parallel</text>
118
+ <text class="sub" x="376" y="140">EACH UNIT ISOLATED</text>
119
+
120
+ <rect class="node" x="504" y="96" width="96" height="64" rx="6"/>
121
+ <text class="name" x="552" y="120">Rebase</text>
122
+ <text class="sub" x="552" y="136">INSPECT</text>
123
+ <text class="sub" x="552" y="148">VALIDATE</text>
124
+
125
+ <polygon class="node" points="704,80 784,128 704,176 624,128"/>
126
+ <text class="name" x="704" y="124">Review</text>
127
+ <text class="sub" x="704" y="140">REQUESTED?</text>
128
+
129
+ <rect class="node" x="624" y="216" width="160" height="64" rx="6"/>
130
+ <text class="name" x="704" y="240">Exact review packet</text>
131
+ <text class="sub" x="704" y="256">BASE · TIP · PATCH</text>
132
+ <text class="sub" x="704" y="268">REQUIRE PASS</text>
133
+
134
+ <rect class="node-focal" x="912" y="96" width="200" height="64" rx="6"/>
135
+ <text class="name" x="1012" y="124">Integrate in order</text>
136
+ <text class="sub" x="1012" y="140">FAST-FORWARD EXACT TIP</text>
137
+
138
+ <rect class="node-soft" x="912" y="216" width="200" height="64" rx="32"/>
139
+ <text class="name" x="1012" y="244">Cleanup</text>
140
+ <text class="sub" x="1012" y="260">REMOVE WORKTREE + BRANCH</text>
141
+
142
+ <rect class="node-soft" x="912" y="336" width="240" height="72" rx="32"/>
143
+ <text class="name" x="1032" y="364">Completion warning</text>
144
+ <text class="sub" x="1032" y="380">CLEAN TIP · OR CLEANUP REFUSAL</text>
145
+ <text class="sub" x="1032" y="392">RETAIN REPORTED RESOURCES</text>
146
+
147
+ <rect class="node" x="72" y="412" width="200" height="64" rx="6"/>
148
+ <text class="name" x="172" y="440">One repair</text>
149
+ <text class="sub" x="172" y="456">DELEGATE_FLOW_CONTINUE</text>
150
+
151
+ <rect class="node" x="320" y="412" width="200" height="64" rx="6"/>
152
+ <text class="name" x="420" y="440">Revalidate repair</text>
153
+ <text class="sub" x="420" y="456">SAME UNIT WORKTREE</text>
154
+
155
+ <rect class="node" x="592" y="412" width="176" height="64" rx="6"/>
156
+ <text class="name" x="680" y="440">Re-review packet</text>
157
+ <text class="sub" x="680" y="456">REQUIRE PASS AGAIN</text>
158
+
159
+ <rect class="node-failure" x="912" y="472" width="240" height="64" rx="32"/>
160
+ <text class="name" x="1032" y="500">Terminal failure</text>
161
+ <text class="sub" x="1032" y="516">RETAIN UNIT WORKTREE</text>
162
+
163
+ <!-- Merge points -->
164
+ <circle cx="876" cy="128" r="4" fill="#1d4ed8"/>
165
+ <circle cx="876" cy="504" r="4" fill="#6a7282"/>
166
+
167
+ <!-- Legend -->
168
+ <line x1="40" y1="636" x2="1240" y2="636" stroke="rgba(16,24,40,.141)" stroke-width="0.8"/>
169
+ <text class="zone-label" x="40" y="656">LEGEND</text>
170
+ <rect class="node" x="120" y="648" width="24" height="12" rx="2"/>
171
+ <text class="legend" x="152" y="658">Step</text>
172
+ <polygon class="node" points="236,654 248,648 260,654 248,660"/>
173
+ <text class="legend" x="272" y="658">Decision</text>
174
+ <circle cx="380" cy="654" r="4" fill="#4c5665"/>
175
+ <text class="legend" x="392" y="658">Branch merge</text>
176
+ <line x1="512" y1="654" x2="548" y2="654" stroke="#1d4ed8" stroke-width="1.4" marker-end="url(#arrow-accent)"/>
177
+ <text class="legend" x="560" y="658">Validated path</text>
178
+ <line x1="696" y1="654" x2="732" y2="654" stroke="#6a7282" stroke-width="1.2" stroke-dasharray="5 4" marker-end="url(#arrow)"/>
179
+ <text class="legend" x="744" y="658">Terminal path</text>
180
+ <text class="sub" x="1120" y="658">ONE REPAIR · NO AUTOMATIC RETRY</text>
181
+ </svg>
@@ -26,7 +26,6 @@ A call selects exactly one of these shapes. Unknown properties and nested modes
26
26
  "name": "Inspect authentication flow",
27
27
  "task": "One bounded task packet",
28
28
  "modelClass": "balanced",
29
- "thinking": "high",
30
29
  "background": false
31
30
  }
32
31
  ```
@@ -69,11 +68,10 @@ Single mode puts one delegation's fields at the top level.
69
68
  | `role` | yes | Name of a Role in the user's effective `config/pi-subagent` directory or a package-shipped built-in (`implementer`, `reviewer`, `scout`); a same-named user file overrides the built-in. |
70
69
  | `name` | yes | Main-supplied short task name: about five words and fewer than 30 characters; C0/C1 control characters are rejected. |
71
70
  | `task` | yes | Non-empty bounded task packet. |
72
- | `model` | no | Designated `provider/modelId`; takes precedence over `modelClass`, and Main supplies it only for an explicit user override. |
73
- | `modelClass` | no | `fast`, `balanced`, `frontier`, or `fav`; Main prioritizes `fast` for straightforward work and `balanced` for complex work, reserves `frontier` for exceptionally complex or tricky work, and omission uses pi-subagent's local Model Task declaration. |
74
- | `thinking` | no | `off`, `minimal`, `low`, `medium`, `high`, `xhigh`, or `max`; Main supplies it only for an explicit user override. Route selection skips models that cannot honor it. |
71
+ | `model` | no | Designated `provider/modelId`; replaces only the selected route model and must support that route's thinking level. |
72
+ | `modelClass` | no | `fast`, `balanced`, `frontier`, or `fav`; selects the route. Main prioritizes `fast` for straightforward work and `balanced` for complex work. It reserves `frontier` for exceptionally complex or tricky work. Omission uses pi-subagent's local Model Task declaration. |
75
73
 
76
- Those six fields are the complete delegation object. The direct-model/thinking rule is Main-facing policy only: the runtime adds no provenance tracking or enforcement. `tasks`, `chain`, and `background` cannot be nested. Route fallback occurs only before launch; a started child is never retried by this package.
74
+ Those five fields are the complete delegation object. Routes set exact thinking levels. A direct model never changes that level and fails before launch if it cannot support it. `tasks`, `chain`, and `background` cannot be nested. Route fallback occurs only before launch; a started child is never retried by this package.
77
75
 
78
76
  ### Background, failures, and transport
79
77
 
@@ -5,7 +5,6 @@ import { type Component, type TUI, truncateToWidth } from "@earendil-works/pi-tu
5
5
  import {
6
6
  availableTaskModels,
7
7
  loadTaskModelsConfig,
8
- type ThinkingLevel,
9
8
  modelReference,
10
9
  registerModelTask,
11
10
  resolveAvailableModel,
@@ -183,23 +182,17 @@ function renderWidgetRows(
183
182
  return lines;
184
183
  }
185
184
 
186
- function resolveDesignatedRoute(ctx: ExtensionContext, reference: string, thinking?: ThinkingLevel): ResolvedTaskRoute {
185
+ function replaceRouteModel(ctx: ExtensionContext, reference: string, route: ResolvedTaskRoute): ResolvedTaskRoute {
187
186
  const models = availableTaskModels(ctx);
188
187
  const model = resolveAvailableModel(models, reference, ctx.model?.provider);
189
188
  if (!model) {
190
189
  throw new Error(`Unknown delegate_task model: ${reference}. Available models: ${models.map((candidate) => modelReference(candidate)).join(", ") || "none"}.`);
191
190
  }
192
191
  const levels = taskThinkingLevels(ctx, model);
193
- if (thinking !== undefined) {
194
- if (!levels.includes(thinking)) {
195
- throw new Error(`delegate_task thinking ${thinking} is not usable for ${modelReference(model)} in this session. Usable levels here: ${levels.join(", ") || "none"}.`);
196
- }
197
- return { model, thinkingLevel: thinking };
198
- }
199
- if (!levels.length) {
200
- throw new Error(`${modelReference(model)} has no usable thinking level in this session; pick another model or adjust the scoped thinking pin.`);
192
+ if (!levels.includes(route.thinkingLevel)) {
193
+ throw new Error(`delegate_task model ${modelReference(model)} cannot use route thinking ${route.thinkingLevel} in this session. Usable levels here: ${levels.join(", ") || "none"}.`);
201
194
  }
202
- return { model, thinkingLevel: levels.includes("medium") ? "medium" : levels.at(-1)! };
195
+ return { model, thinkingLevel: route.thinkingLevel };
203
196
  }
204
197
 
205
198
  const BACKGROUND_RESULT_TYPE = "subagent-background-result";
@@ -539,7 +532,7 @@ export default function subagentExtension(
539
532
  promptGuidelines: [
540
533
  "Call delegate_task with exactly one mode: role+name+task for one task, tasks for 1–8 independent parallel tasks, or chain for 1–8 dependent sequential tasks using {previous} for the immediately preceding assistant output; split independent, commuting outcomes into parallel entries, sequence dependent work in chain entries, and never divide one invariant across multiple entries.",
541
534
  `${TASK_NAME_CONTRACT.promptGuidance} Every delegate_task entry must own one concrete outcome with one focused validation story: state its objective, exact scope and exclusions, relevant context and constraints, expected deliverable, and validation; if the affected flow or scope is not yet known, perform bounded read-only discovery first; never pass the parent request unchanged.`,
542
- `For each delegate_task entry, populate model and thinking only for an explicit user override. Otherwise, ${MODEL_CLASS_GUIDANCE} This is Main policy, not runtime enforcement.`,
535
+ `For each delegate_task entry, ${MODEL_CLASS_GUIDANCE} A direct model replaces only the selected route's model; its thinking level stays unchanged.`,
543
536
  "Parallel delegate_task entries must own non-overlapping files. Keep integration and cross-cutting decisions in Main, and use the minimum number of Subagents needed.",
544
537
  "delegate_task background applies to the whole selected workflow and returns before results exist; use it only when the user explicitly asks for non-blocking work.",
545
538
  ],
@@ -578,14 +571,16 @@ export default function subagentExtension(
578
571
 
579
572
  // Resolve against the latest known session context after each FIFO permit.
580
573
  const launchCtx = () => latestCtx ?? ctx;
581
- const resolveLaunch = (role: Role, delegation: Delegation) => createRoleLaunch(pi, launchCtx(), {
582
- role,
583
- route: delegation.model !== undefined
584
- ? resolveDesignatedRoute(launchCtx(), delegation.model, delegation.thinking)
585
- : delegation.modelClass === undefined
586
- ? resolveConfiguredTaskRoute(launchCtx(), DELEGATE_TASK, undefined, delegation.thinking)
587
- : resolveTaskRoute(launchCtx(), delegation.modelClass, undefined, delegation.thinking),
588
- });
574
+ const resolveLaunch = (role: Role, delegation: Delegation) => {
575
+ const context = launchCtx();
576
+ const route = delegation.modelClass === undefined
577
+ ? resolveConfiguredTaskRoute(context, DELEGATE_TASK)
578
+ : resolveTaskRoute(context, delegation.modelClass);
579
+ return createRoleLaunch(pi, context, {
580
+ role,
581
+ route: delegation.model === undefined ? route : replaceRouteModel(context, delegation.model, route),
582
+ });
583
+ };
589
584
  const notifyMissingSkills = (role: Role, launch: ReturnType<typeof resolveLaunch>) => {
590
585
  if (launch.missingSkills.length) {
591
586
  ctx.ui.notify(
@@ -1,5 +1,5 @@
1
1
  import { StringEnum } from "@earendil-works/pi-ai";
2
- import { PROFILE_NAMES, THINKING_LEVELS } from "@henryqw/pi-task-models";
2
+ import { PROFILE_NAMES } from "@henryqw/pi-task-models";
3
3
  import { Type, type Static } from "typebox";
4
4
  import { Check } from "typebox/value";
5
5
  import { TaskNameSchema, normalizeTaskName } from "./task-name.ts";
@@ -8,9 +8,8 @@ export const MAX_WORKFLOW_ENTRIES = 8;
8
8
 
9
9
  const RoleSchema = Type.String({ minLength: 1, description: "Configured Subagent role name" });
10
10
  const TaskSchema = Type.String({ minLength: 1, description: "Bounded task packet" });
11
- const ModelSchema = Type.String({ minLength: 1, description: "Designated model as provider/modelId; overrides modelClass" });
11
+ const ModelSchema = Type.String({ minLength: 1, description: "Designated model as provider/modelId; replaces the selected route model" });
12
12
  const ModelClassSchema = StringEnum(PROFILE_NAMES, { description: "Task model profile" });
13
- const ThinkingSchema = StringEnum(THINKING_LEVELS, { description: "Task thinking-level override" });
14
13
 
15
14
  export const DelegationSchema = Type.Object({
16
15
  role: RoleSchema,
@@ -18,7 +17,6 @@ export const DelegationSchema = Type.Object({
18
17
  task: TaskSchema,
19
18
  model: Type.Optional(ModelSchema),
20
19
  modelClass: Type.Optional(ModelClassSchema),
21
- thinking: Type.Optional(ThinkingSchema),
22
20
  }, { additionalProperties: false });
23
21
 
24
22
  export const WorkflowSchema = Type.Object({
@@ -27,7 +25,6 @@ export const WorkflowSchema = Type.Object({
27
25
  task: Type.Optional(TaskSchema),
28
26
  model: Type.Optional(ModelSchema),
29
27
  modelClass: Type.Optional(ModelClassSchema),
30
- thinking: Type.Optional(ThinkingSchema),
31
28
  tasks: Type.Optional(Type.Array(DelegationSchema, {
32
29
  minItems: 1,
33
30
  maxItems: MAX_WORKFLOW_ENTRIES,
@@ -53,7 +50,7 @@ export type ParsedWorkflow =
53
50
 
54
51
  type WorkflowInput = Static<typeof WorkflowSchema>;
55
52
 
56
- const DELEGATION_KEYS = ["role", "name", "task", "model", "modelClass", "thinking"] as const;
53
+ const DELEGATION_KEYS = ["role", "name", "task", "model", "modelClass"] as const;
57
54
 
58
55
  function text(value: string, path: string): string {
59
56
  const normalized = value.trim();
@@ -68,7 +65,6 @@ function normalizeDelegation(value: Delegation, path: string): Delegation {
68
65
  task: text(value.task, `${path}.task`),
69
66
  ...(value.model === undefined ? {} : { model: text(value.model, `${path}.model`) }),
70
67
  ...(value.modelClass === undefined ? {} : { modelClass: value.modelClass }),
71
- ...(value.thinking === undefined ? {} : { thinking: value.thinking }),
72
68
  };
73
69
  }
74
70
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@henryqw/pi-subagent",
3
- "version": "10.1.1",
3
+ "version": "11.0.1",
4
4
  "description": "Delegate bounded single, parallel, or chained tasks to isolated Pi roles.",
5
5
  "keywords": [
6
6
  "pi-package",
@@ -31,4 +31,4 @@ Make the guidance specific to the reported implementation, validation, or review
31
31
 
32
32
  ## Ordinary delegation
33
33
 
34
- Use `delegate_task` for a single bounded task, independent parallel tasks, or dependent chain work that is not a Flow. Give each entry its objective, exact scope and exclusions, relevant context and constraints, expected deliverable, and focused validation. Populate direct `model` and `thinking` only when the user explicitly asks for those overrides; otherwise choose `modelClass` according to the delegation tool's guidance. Keep integration and cross-cutting decisions in Main, and use the minimum number of Subagents needed.
34
+ Use `delegate_task` for a single bounded task, independent parallel tasks, or dependent chain work that is not a Flow. Give each entry its objective, exact scope and exclusions, relevant context and constraints, expected deliverable, and focused validation. Choose `modelClass` according to the delegation tool's guidance. A direct `model` replaces only the selected route's model. The route keeps its thinking level. Keep integration and cross-cutting decisions in Main, and use the minimum number of Subagents needed.