infinity-harness 2.8.9 → 2.8.11

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.
Files changed (192) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/dist/extensions/infinity-harness/index.js +3614 -0
  3. package/dist/extensions/infinity-harness/index.js.map +1 -0
  4. package/dist/src/approval.js +154 -0
  5. package/dist/src/approval.js.map +1 -0
  6. package/dist/src/core/brief.js +309 -0
  7. package/dist/src/core/brief.js.map +1 -0
  8. package/dist/src/core/config.js +539 -0
  9. package/dist/src/core/config.js.map +1 -0
  10. package/dist/src/core/exec.js +106 -0
  11. package/dist/src/core/exec.js.map +1 -0
  12. package/dist/src/core/featureList.js +365 -0
  13. package/dist/src/core/featureList.js.map +1 -0
  14. package/dist/src/core/fsx.js +123 -0
  15. package/dist/src/core/fsx.js.map +1 -0
  16. package/dist/src/core/gates.js +490 -0
  17. package/dist/src/core/gates.js.map +1 -0
  18. package/dist/src/core/init.js +480 -0
  19. package/dist/src/core/init.js.map +1 -0
  20. package/dist/src/core/lock.js +183 -0
  21. package/dist/src/core/lock.js.map +1 -0
  22. package/dist/src/core/modelRouter.js +185 -0
  23. package/dist/src/core/modelRouter.js.map +1 -0
  24. package/dist/src/core/paths.js +129 -0
  25. package/dist/src/core/paths.js.map +1 -0
  26. package/dist/src/core/phases.js +254 -0
  27. package/dist/src/core/phases.js.map +1 -0
  28. package/dist/src/core/plan.js +15 -0
  29. package/dist/src/core/plan.js.map +1 -0
  30. package/dist/src/core/runState.js +126 -0
  31. package/dist/src/core/runState.js.map +1 -0
  32. package/dist/src/core/settings.js +877 -0
  33. package/dist/src/core/settings.js.map +1 -0
  34. package/dist/src/core/skills.js +319 -0
  35. package/dist/src/core/skills.js.map +1 -0
  36. package/dist/src/core/skillsAudit.js +194 -0
  37. package/dist/src/core/skillsAudit.js.map +1 -0
  38. package/dist/src/core/types.js +77 -0
  39. package/dist/src/core/types.js.map +1 -0
  40. package/dist/src/daemon/budget.js +85 -0
  41. package/dist/src/daemon/budget.js.map +1 -0
  42. package/dist/src/daemon/guard.js +122 -0
  43. package/dist/src/daemon/guard.js.map +1 -0
  44. package/dist/src/daemon/index.js +461 -0
  45. package/dist/src/daemon/index.js.map +1 -0
  46. package/dist/src/daemon/isolation.js +84 -0
  47. package/dist/src/daemon/isolation.js.map +1 -0
  48. package/dist/src/daemon/preflight.js +131 -0
  49. package/dist/src/daemon/preflight.js.map +1 -0
  50. package/dist/src/daemon/server.js +174 -0
  51. package/dist/src/daemon/server.js.map +1 -0
  52. package/dist/src/daemon/supervisorState.js +42 -0
  53. package/dist/src/daemon/supervisorState.js.map +1 -0
  54. package/dist/src/daemon/worker.js +239 -0
  55. package/dist/src/daemon/worker.js.map +1 -0
  56. package/dist/src/daemon/worktree.js +102 -0
  57. package/dist/src/daemon/worktree.js.map +1 -0
  58. package/dist/src/escalate.js +312 -0
  59. package/dist/src/escalate.js.map +1 -0
  60. package/dist/src/exec/piWorker.js +630 -0
  61. package/dist/src/exec/piWorker.js.map +1 -0
  62. package/dist/src/goal.js +317 -0
  63. package/dist/src/goal.js.map +1 -0
  64. package/dist/src/goalLoop.js +374 -0
  65. package/dist/src/goalLoop.js.map +1 -0
  66. package/dist/src/goalSpec.js +397 -0
  67. package/dist/src/goalSpec.js.map +1 -0
  68. package/dist/src/goalState.js +255 -0
  69. package/dist/src/goalState.js.map +1 -0
  70. package/dist/src/handoff.js +205 -0
  71. package/dist/src/handoff.js.map +1 -0
  72. package/dist/src/intake.js +203 -0
  73. package/dist/src/intake.js.map +1 -0
  74. package/dist/src/loop.js +592 -0
  75. package/dist/src/loop.js.map +1 -0
  76. package/dist/src/modelRouter.js +216 -0
  77. package/dist/src/modelRouter.js.map +1 -0
  78. package/dist/src/remote.js +246 -0
  79. package/dist/src/remote.js.map +1 -0
  80. package/dist/src/replan.js +262 -0
  81. package/dist/src/replan.js.map +1 -0
  82. package/dist/src/review.js +54 -0
  83. package/dist/src/review.js.map +1 -0
  84. package/dist/src/rework.js +241 -0
  85. package/dist/src/rework.js.map +1 -0
  86. package/dist/src/runState.js +4 -0
  87. package/dist/src/runState.js.map +1 -0
  88. package/dist/src/scheduler.js +270 -0
  89. package/dist/src/scheduler.js.map +1 -0
  90. package/dist/src/supervisor.js +787 -0
  91. package/dist/src/supervisor.js.map +1 -0
  92. package/dist/src/taskList.js +430 -0
  93. package/dist/src/taskList.js.map +1 -0
  94. package/dist/src/ui/config.js +268 -0
  95. package/dist/src/ui/config.js.map +1 -0
  96. package/dist/src/ui/dashboard.js +1288 -0
  97. package/dist/src/ui/dashboard.js.map +1 -0
  98. package/dist/src/ui/display.js +229 -0
  99. package/dist/src/ui/display.js.map +1 -0
  100. package/dist/src/ui/planTree.js +219 -0
  101. package/dist/src/ui/planTree.js.map +1 -0
  102. package/dist/src/ui/theme.js +298 -0
  103. package/dist/src/ui/theme.js.map +1 -0
  104. package/dist/src/ui/viewState.js +65 -0
  105. package/dist/src/ui/viewState.js.map +1 -0
  106. package/dist/src/ui/widget.js +721 -0
  107. package/dist/src/ui/widget.js.map +1 -0
  108. package/dist/src/ui/wizard.js +540 -0
  109. package/dist/src/ui/wizard.js.map +1 -0
  110. package/dist/src/unstuck.js +210 -0
  111. package/dist/src/unstuck.js.map +1 -0
  112. package/dist/src/worker.js +267 -0
  113. package/dist/src/worker.js.map +1 -0
  114. package/dist/src/workflow.js +250 -0
  115. package/dist/src/workflow.js.map +1 -0
  116. package/dist/tests/approval.test.js +144 -0
  117. package/dist/tests/approval.test.js.map +1 -0
  118. package/dist/tests/brief.test.js +271 -0
  119. package/dist/tests/brief.test.js.map +1 -0
  120. package/dist/tests/config.test.js +320 -0
  121. package/dist/tests/config.test.js.map +1 -0
  122. package/dist/tests/dashboard.test.js +277 -0
  123. package/dist/tests/dashboard.test.js.map +1 -0
  124. package/dist/tests/display.test.js +229 -0
  125. package/dist/tests/display.test.js.map +1 -0
  126. package/dist/tests/escalate.test.js +272 -0
  127. package/dist/tests/escalate.test.js.map +1 -0
  128. package/dist/tests/featureList.test.js +429 -0
  129. package/dist/tests/featureList.test.js.map +1 -0
  130. package/dist/tests/gates.test.js +333 -0
  131. package/dist/tests/gates.test.js.map +1 -0
  132. package/dist/tests/goal.test.js +229 -0
  133. package/dist/tests/goal.test.js.map +1 -0
  134. package/dist/tests/goalLoop.test.js +170 -0
  135. package/dist/tests/goalLoop.test.js.map +1 -0
  136. package/dist/tests/goalSpec.test.js +146 -0
  137. package/dist/tests/goalSpec.test.js.map +1 -0
  138. package/dist/tests/goalState.test.js +202 -0
  139. package/dist/tests/goalState.test.js.map +1 -0
  140. package/dist/tests/handoff.test.js +127 -0
  141. package/dist/tests/handoff.test.js.map +1 -0
  142. package/dist/tests/init.test.js +264 -0
  143. package/dist/tests/init.test.js.map +1 -0
  144. package/dist/tests/intake.test.js +288 -0
  145. package/dist/tests/intake.test.js.map +1 -0
  146. package/dist/tests/loop.test.js +453 -0
  147. package/dist/tests/loop.test.js.map +1 -0
  148. package/dist/tests/modelRouter.test.js +131 -0
  149. package/dist/tests/modelRouter.test.js.map +1 -0
  150. package/dist/tests/phases.test.js +244 -0
  151. package/dist/tests/phases.test.js.map +1 -0
  152. package/dist/tests/planTree.test.js +190 -0
  153. package/dist/tests/planTree.test.js.map +1 -0
  154. package/dist/tests/remote.test.js +380 -0
  155. package/dist/tests/remote.test.js.map +1 -0
  156. package/dist/tests/replan.test.js +113 -0
  157. package/dist/tests/replan.test.js.map +1 -0
  158. package/dist/tests/review.test.js +77 -0
  159. package/dist/tests/review.test.js.map +1 -0
  160. package/dist/tests/rework.test.js +109 -0
  161. package/dist/tests/rework.test.js.map +1 -0
  162. package/dist/tests/routing-live.test.js +121 -0
  163. package/dist/tests/routing-live.test.js.map +1 -0
  164. package/dist/tests/runState.test.js +73 -0
  165. package/dist/tests/runState.test.js.map +1 -0
  166. package/dist/tests/settings.test.js +311 -0
  167. package/dist/tests/settings.test.js.map +1 -0
  168. package/dist/tests/skills.test.js +387 -0
  169. package/dist/tests/skills.test.js.map +1 -0
  170. package/dist/tests/supervisor.test.js +437 -0
  171. package/dist/tests/supervisor.test.js.map +1 -0
  172. package/dist/tests/surface.test.js +203 -0
  173. package/dist/tests/surface.test.js.map +1 -0
  174. package/dist/tests/taskList.test.js +376 -0
  175. package/dist/tests/taskList.test.js.map +1 -0
  176. package/dist/tests/theme.test.js +149 -0
  177. package/dist/tests/theme.test.js.map +1 -0
  178. package/dist/tests/unstuck.test.js +268 -0
  179. package/dist/tests/unstuck.test.js.map +1 -0
  180. package/dist/tests/widget.test.js +424 -0
  181. package/dist/tests/widget.test.js.map +1 -0
  182. package/dist/tests/worker.test.js +306 -0
  183. package/dist/tests/worker.test.js.map +1 -0
  184. package/dist/tests/workflow.test.js +187 -0
  185. package/dist/tests/workflow.test.js.map +1 -0
  186. package/extensions/infinity-harness/index.ts +39 -12
  187. package/package.json +5 -2
  188. package/src/core/modelRouter.ts +23 -1
  189. package/src/daemon/index.ts +3 -3
  190. package/src/daemon/worker.ts +28 -5
  191. package/src/ui/config.ts +25 -1
  192. package/src/ui/wizard.ts +10 -5
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "infinity-harness",
3
- "version": "2.8.9",
3
+ "version": "2.8.11",
4
4
  "description": "A pi agent extension that runs a gated build pipeline unattended \u2014 enforces phases, validates with deterministic gates, and keeps working for hours or days without losing the plan.",
5
5
  "type": "module",
6
6
  "keywords": [
@@ -19,6 +19,7 @@
19
19
  },
20
20
  "pi": {
21
21
  "extensions": [
22
+ "./dist/extensions",
22
23
  "./extensions"
23
24
  ],
24
25
  "skills": [
@@ -27,6 +28,7 @@
27
28
  },
28
29
  "files": [
29
30
  "extensions/",
31
+ "dist/",
30
32
  "src/",
31
33
  "types/",
32
34
  "harness/skills/",
@@ -51,7 +53,8 @@
51
53
  "scripts": {
52
54
  "test": "node scripts/run-tests.mjs",
53
55
  "check": "tsc --noEmit",
54
- "build": "tsc --noEmit",
56
+ "build": "tsc -p tsconfig.build.json",
57
+ "prepare": "npm run build",
55
58
  "e2e": "node scripts/e2e.mjs"
56
59
  },
57
60
  "homepage": "https://github.com/bakr-bagaber/infinity-harness#readme",
@@ -13,8 +13,30 @@ import type { TierMap, TierSpec, HarnessConfig, FeatureList, Phase, Difficulty }
13
13
  export type ThinkingLevel = import("./types.ts").ThinkingLevel;
14
14
  export type TierId = import("./types.ts").TierId;
15
15
 
16
- export function clampThinkingForProvider(provider: string, level: string | "" | undefined): string | "" {
16
+ export function clampThinkingForProvider(provider: string, level: string | "" | undefined, modelForCheck?: unknown): string | "" {
17
17
  if (!level) return (level ?? "") as string | "";
18
+ // If we have the actual model, use pi-default truth (getSupportedThinkingLevels semantics)
19
+ try {
20
+ const m = modelForCheck as { reasoning?: boolean; thinkingLevelMap?: Record<string, string | null> } | null | undefined;
21
+ if (m && typeof m.reasoning === "boolean") {
22
+ if (m.reasoning === false) return level === "off" || level === "" ? (level as string | "") : "off";
23
+ const map = m.thinkingLevelMap ?? {};
24
+ const all = ["off","minimal","low","medium","high","xhigh","max"] as const;
25
+ const supported = all.filter(l => {
26
+ const v = (map as Record<string, string | null | undefined>)[l];
27
+ if (v === null) return false;
28
+ if ((l === "xhigh" || l === "max") && v === undefined) return false;
29
+ return true;
30
+ });
31
+ if (supported.includes(level as typeof all[number])) return level as string | "";
32
+ // clamp to nearest supported (prefer lower)
33
+ const idx = (all as readonly string[]).indexOf(level);
34
+ for (let i = idx; i >= 0; i--) if (supported.includes(all[i] as typeof all[number])) return all[i] as string | "";
35
+ for (let i = idx+1; i < all.length; i++) if (supported.includes(all[i] as typeof all[number])) return all[i] as string | "";
36
+ return supported[0] ?? "off";
37
+ }
38
+ } catch {}
39
+ // Fallback: old provider-string heuristic for when model not available (e.g. in config migration)
18
40
  const pp = String(provider || "").toLowerCase();
19
41
  const isLocal = pp === "lm-studio" || pp.startsWith("lm-studio/") || pp === "lmstudio";
20
42
  if (!isLocal) return level as string | "";
@@ -275,7 +275,7 @@ async function runLoop(targetDir: string): Promise<void> {
275
275
  model: askedModel,
276
276
  askedModel,
277
277
  servedModel: null,
278
- thinking: clampThinkingForProvider(routed.provider, ((tierSpec as { thinkingLevel?: string } | null)?.thinkingLevel as unknown as string) ?? "medium") || "medium",
278
+ thinking: clampThinkingForProvider(routed.provider, ((tierSpec as { thinkingLevel?: string } | null)?.thinkingLevel as unknown as string) ?? "") || undefined,
279
279
  state: "starting",
280
280
  doing: "starting",
281
281
  startedAt: new Date().toISOString(),
@@ -298,7 +298,7 @@ async function runLoop(targetDir: string): Promise<void> {
298
298
  const workerFactory = await import("./worker.ts");
299
299
  workerHandle = await workerFactory.createWorker({
300
300
  cwd: targetDir,
301
- modelSpec: { provider: routed.provider, id: routed.id, thinkingLevel: clampThinkingForProvider(routed.provider, ((tierSpec as { thinkingLevel?: string } | null)?.thinkingLevel as unknown as string) ?? "medium") || "medium" },
301
+ modelSpec: { provider: routed.provider, id: routed.id, thinkingLevel: clampThinkingForProvider(routed.provider, ((tierSpec as { thinkingLevel?: string } | null)?.thinkingLevel as unknown as string) ?? "") || undefined },
302
302
  askedModel,
303
303
  sessionManagerDir: `harness/sessions/${workerLabel.replace(/[^a-z0-9._-]/gi, "-")}`,
304
304
  customTools: (await import("./isolation.ts")).harnessToolsForWorker() as unknown[],
@@ -317,7 +317,7 @@ async function runLoop(targetDir: string): Promise<void> {
317
317
  if (!workerHandle) {
318
318
  workerHandle = await workerFactory.createWorker({
319
319
  cwd: targetDir,
320
- modelSpec: { provider: routed.provider, id: routed.id, thinkingLevel: clampThinkingForProvider(routed.provider, ((tierSpec as { thinkingLevel?: string } | null)?.thinkingLevel as unknown as string) ?? "medium") || "medium" },
320
+ modelSpec: { provider: routed.provider, id: routed.id, thinkingLevel: clampThinkingForProvider(routed.provider, ((tierSpec as { thinkingLevel?: string } | null)?.thinkingLevel as unknown as string) ?? "") || undefined },
321
321
  askedModel,
322
322
  sessionManagerDir: `harness/sessions/${workerLabel.replace(/[^a-z0-9._-]/gi, "-")}`,
323
323
  customTools: (await import("./isolation.ts")).harnessToolsForWorker() as unknown[],
@@ -110,12 +110,35 @@ export async function createWorker(opts: CreateWorkerOpts): Promise<{
110
110
  ? SessionManager.create(opts.cwd, opts.sessionManagerDir)
111
111
  : SessionManager.inMemory(opts.cwd);
112
112
 
113
- let thinkingLevel = (opts.thinkingLevel ?? opts.modelSpec.thinkingLevel ?? "medium") as unknown as string;
114
- // A: clamp for lm-studio (xhigh/max not supported, caused max_completion_tokens:1)
113
+ // inherit means inherit don't force medium. Only clamp when explicitly set.
114
+ let thinkingLevel: string | undefined = (opts.thinkingLevel ?? opts.modelSpec.thinkingLevel ?? undefined) as unknown as string | undefined;
115
+ // capability-aware clamp: use pi-default supported levels for the actual model, not provider string
115
116
  try {
116
- const p = String(opts.modelSpec.provider || "").toLowerCase();
117
- const isLocal = p === "lm-studio" || p.startsWith("lm-studio/") || p === "lmstudio";
118
- if (isLocal && (thinkingLevel === "xhigh" || thinkingLevel === "max" || thinkingLevel === "high")) thinkingLevel = "medium";
117
+ const mod = model as unknown as { reasoning?: boolean; thinkingLevelMap?: Record<string, string | null> } | null;
118
+ if (thinkingLevel) {
119
+ // inline getSupportedThinkingLevels semantics
120
+ if (mod && typeof mod.reasoning === "boolean" && mod.reasoning === false) {
121
+ if (thinkingLevel !== "off") thinkingLevel = "off";
122
+ } else if (mod && mod.reasoning === true) {
123
+ const map = mod.thinkingLevelMap ?? {};
124
+ const all = ["off","minimal","low","medium","high","xhigh","max"] as const;
125
+ const supported = all.filter(l => {
126
+ const v = (map as Record<string, string | null | undefined>)[l];
127
+ if (v === null) return false;
128
+ if ((l === "xhigh" || l === "max") && v === undefined) return false;
129
+ return true;
130
+ });
131
+ if (!supported.includes(thinkingLevel as typeof all[number])) {
132
+ const idx = (all as readonly string[]).indexOf(thinkingLevel);
133
+ let found: string | null = null;
134
+ for (let i = idx; i >= 0; i--) if (supported.includes(all[i] as typeof all[number])) { found = all[i] as string; break; }
135
+ if (!found) for (let i = idx+1; i < all.length; i++) if (supported.includes(all[i] as typeof all[number])) { found = all[i] as string; break; }
136
+ thinkingLevel = found ?? supported[0] ?? "off";
137
+ }
138
+ }
139
+ } else if (thinkingLevel === undefined) {
140
+ // inherit (undefined) — leave undefined so createAgentSession uses pi-default for that model
141
+ }
119
142
  } catch {}
120
143
  // B: debug trace of routed model/thinking (readable in harness/daemon.log)
121
144
  try { console.log("[worker] model=" + opts.modelSpec.provider + "/" + opts.modelSpec.id + " thinking=" + thinkingLevel + " askedModel=" + opts.askedModel); } catch {}
package/src/ui/config.ts CHANGED
@@ -39,6 +39,8 @@ export type ModelChoice = {
39
39
  ref: string;
40
40
  /** What the user sees. */
41
41
  label: string;
42
+ /** Pi-default supported thinking levels for this model, or null if unknown. */
43
+ supportedThinkingLevels?: readonly string[];
42
44
  };
43
45
 
44
46
  export type ConfigMenuOptions = {
@@ -167,7 +169,29 @@ async function editSetting(setting: Setting, options: ConfigMenuOptions): Promis
167
169
  break;
168
170
  }
169
171
  case "thinking": {
170
- const choices = ["(inherit)", "off", "minimal", "low", "medium", "high", "xhigh", "max"];
172
+ // capability-aware: if setting is tiers.X.thinkingLevel etc, filter to what the model actually supports
173
+ let choices: string[] = ["(inherit)", "off", "minimal", "low", "medium", "high", "xhigh", "max"];
174
+ try {
175
+ const m = setting.path.match(/^tiers.([A-Z]).(provider|id|thinkingLevel)/);
176
+ // For tiers.A/B/C/D/X thinkingLevel, look up that tier's model and its supported levels
177
+ const tierMatch = setting.path.match(/^tiers.([A-Z]).thinkingLevel/);
178
+ if (tierMatch) {
179
+ const tier = tierMatch[1]!;
180
+ const all = readAll(targetDir);
181
+ const tierSpec = (all.config as unknown as { tiers?: Record<string, { provider?: string; id?: string }> }).tiers?.[tier];
182
+ if (tierSpec?.provider && tierSpec?.id) {
183
+ const ref = tierSpec.provider + "/" + tierSpec.id;
184
+ const models = await options.models();
185
+ const found = models.find(x => x.ref === ref) as unknown as { supportedThinkingLevels?: readonly string[] } | undefined;
186
+ if (found?.supportedThinkingLevels?.length) {
187
+ choices = ["(inherit)", ...found.supportedThinkingLevels.filter(l => l !== "off")];
188
+ // ensure off is last fallback if model supports only off
189
+ if (!found.supportedThinkingLevels.includes("off")) choices = ["(inherit)", ...found.supportedThinkingLevels];
190
+ else if (choices.indexOf("off") === -1) choices.splice(1,0,"off");
191
+ }
192
+ }
193
+ }
194
+ } catch {}
171
195
  const picked = await prompt.select(`${setting.label} — ${setting.help}`, [...choices, BACK]);
172
196
  if (picked === undefined || picked === BACK) return false;
173
197
  raw = picked;
package/src/ui/wizard.ts CHANGED
@@ -153,8 +153,9 @@ async function pickModelChoice(prompt: Prompter, title: string, models: ModelCho
153
153
  return model?.ref;
154
154
  }
155
155
 
156
- async function pickThinkingLevel(prompt: Prompter, title: string): Promise<ThinkingLevel | "" | undefined> {
157
- const picked = await prompt.select(title, [THINK_INHERIT, ...THINKING_LEVELS]);
156
+ async function pickThinkingLevel(prompt: Prompter, title: string, supported?: readonly string[] | null): Promise<ThinkingLevel | "" | undefined> {
157
+ const levels = Array.isArray(supported) && supported.length ? supported as ThinkingLevel[] : THINKING_LEVELS;
158
+ const picked = await prompt.select(title, [THINK_INHERIT, ...levels]);
158
159
  if (picked === undefined) return undefined;
159
160
  if (picked === THINK_INHERIT) return "";
160
161
  return picked as ThinkingLevel;
@@ -214,21 +215,25 @@ async function pickModelsStep(
214
215
  const tiers = ["easy", "moderate", "difficult"] as const;
215
216
  const byDifficulty: Record<string, string> = {};
216
217
  const thinkingByDifficulty: Partial<Record<string, ThinkingLevel | "">> = {};
218
+ const supportedFor = (ref: string): readonly string[] | null => {
219
+ const m = models.find(x => x.ref === ref) as unknown as { supportedThinkingLevels?: readonly string[] } | undefined;
220
+ return (m as { supportedThinkingLevels?: readonly string[] } | undefined)?.supportedThinkingLevels ?? null;
221
+ };
217
222
  for (const tier of tiers) {
218
223
  const model = await pickModelChoice(prompt, `${tier.toUpperCase()} tier — model`, models, "");
219
224
  if (model === undefined) return undefined;
220
225
  byDifficulty[tier] = model;
221
- const thinking = await pickThinkingLevel(prompt, `${tier.toUpperCase()} tier — thinking level`);
226
+ const thinking = await pickThinkingLevel(prompt, `${tier.toUpperCase()} tier — thinking level`, model ? supportedFor(model) : null);
222
227
  if (thinking === undefined) return undefined;
223
228
  thinkingByDifficulty[tier] = thinking;
224
229
  }
225
230
  const masterModel = await pickModelChoice(prompt, "Consulting master — model (used only when the ladder is exhausted)", models, "");
226
231
  if (masterModel === undefined) return undefined;
227
- const masterThinking = await pickThinkingLevel(prompt, "Consulting master — thinking level");
232
+ const masterThinking = await pickThinkingLevel(prompt, "Consulting master — thinking level", masterModel ? supportedFor(masterModel) : null);
228
233
  if (masterThinking === undefined) return undefined;
229
234
  const defaultModel = await pickModelChoice(prompt, "Default (tier A) — general work + undifficult tasks — idle under task handoff when every task has difficulty (" + tierScopeNote(handoff) + ")", models, "");
230
235
  if (defaultModel === undefined) return undefined;
231
- const defaultThinking = await pickThinkingLevel(prompt, "Default — thinking level fallback");
236
+ const defaultThinking = await pickThinkingLevel(prompt, "Default — thinking level fallback", defaultModel ? supportedFor(defaultModel) : null);
232
237
  if (defaultThinking === undefined) return undefined;
233
238
  return {
234
239
  router: {