pi-subagents 0.54.0 → 0.56.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.
Files changed (71) hide show
  1. package/CHANGELOG.md +59 -0
  2. package/agents/reviewer.md +12 -2
  3. package/docs/agents.md +4 -4
  4. package/docs/configuration.md +4 -4
  5. package/docs/extension-api.md +14 -4
  6. package/docs/models.md +30 -3
  7. package/docs/observability.md +14 -3
  8. package/docs/tool-reference.md +21 -6
  9. package/docs/workflows.md +7 -1
  10. package/package.json +1 -1
  11. package/prompts/council.md +9 -0
  12. package/prompts/parallel-review.md +5 -1
  13. package/prompts/review-loop.md +9 -5
  14. package/skills/council-mode/SKILL.md +33 -10
  15. package/skills/pi-subagents/references/constraints-and-recipes.md +16 -1
  16. package/skills/pi-subagents/references/execution-controls.md +11 -4
  17. package/skills/pi-subagents/references/multi-lane-orchestration.md +3 -3
  18. package/skills/pi-subagents/references/prompting-and-roles.md +8 -8
  19. package/src/agents/agent-management.ts +30 -9
  20. package/src/agents/agent-serializer.ts +4 -2
  21. package/src/agents/agents.ts +168 -41
  22. package/src/api/external-job-provider.ts +10 -1
  23. package/src/api/preflight.ts +45 -17
  24. package/src/api/project-panes.ts +2 -0
  25. package/src/extension/index.ts +20 -1
  26. package/src/extension/public-execution.ts +12 -9
  27. package/src/extension/rpc.ts +77 -3
  28. package/src/extension/schemas.ts +7 -1
  29. package/src/extension/tool-description.ts +9 -6
  30. package/src/inspectors/herdr/focus.ts +55 -0
  31. package/src/inspectors/herdr/project-panes.ts +228 -44
  32. package/src/integrations/herdr-status.ts +26 -4
  33. package/src/runs/background/async-execution.ts +137 -13
  34. package/src/runs/background/async-job-tracker.ts +33 -14
  35. package/src/runs/background/async-resume.ts +23 -2
  36. package/src/runs/background/async-status.ts +10 -0
  37. package/src/runs/background/control-channel.ts +98 -10
  38. package/src/runs/background/notify.ts +62 -1
  39. package/src/runs/background/run-status.ts +15 -1
  40. package/src/runs/background/subagent-runner.ts +308 -47
  41. package/src/runs/foreground/async-stop-action.ts +23 -2
  42. package/src/runs/foreground/execution.ts +115 -12
  43. package/src/runs/foreground/foreground-history.ts +3 -2
  44. package/src/runs/foreground/subagent-executor.ts +404 -45
  45. package/src/runs/foreground/workflow-detach-reconcile.ts +18 -6
  46. package/src/runs/shared/acceptance.ts +11 -5
  47. package/src/runs/shared/agent-contract.ts +1 -1
  48. package/src/runs/shared/child-identity.ts +36 -0
  49. package/src/runs/shared/completion-guard.ts +52 -2
  50. package/src/runs/shared/dynamic-fanout.ts +1 -1
  51. package/src/runs/shared/extension-bindings.ts +78 -0
  52. package/src/runs/shared/external-cli-runner.ts +2 -0
  53. package/src/runs/shared/external-job-bridge.ts +53 -37
  54. package/src/runs/shared/external-job-runner.ts +126 -23
  55. package/src/runs/shared/fast-mode-extension.ts +10 -0
  56. package/src/runs/shared/model-exclusions.ts +2 -1
  57. package/src/runs/shared/model-fallback.ts +12 -7
  58. package/src/runs/shared/mutation-evidence.ts +145 -0
  59. package/src/runs/shared/orca-progress-tabs.ts +71 -9
  60. package/src/runs/shared/parallel-utils.ts +15 -0
  61. package/src/runs/shared/pi-args.ts +56 -12
  62. package/src/runs/shared/structured-output.ts +18 -4
  63. package/src/runs/shared/subagent-prompt-runtime.ts +12 -6
  64. package/src/runs/shared/tool-availability.ts +1 -3
  65. package/src/shared/launch-contract.ts +12 -5
  66. package/src/shared/settings.ts +6 -1
  67. package/src/shared/thinking-ceiling.ts +52 -0
  68. package/src/shared/types.ts +107 -2
  69. package/src/tui/fleet-status.ts +66 -13
  70. package/src/tui/render.ts +5 -4
  71. package/src/workflows/scripted-workflow.ts +156 -12
@@ -22,6 +22,7 @@ import { parseMemoryFrontmatter } from "./agent-memory.ts";
22
22
  import { resolveTurnBudgetConfig } from "../runs/shared/turn-budget.ts";
23
23
  import { validateAcceptanceInput } from "../runs/shared/acceptance.ts";
24
24
  import { validatePermissionRules, type PermissionRules } from "../runs/shared/permissions.ts";
25
+ import { parseThinkingLevel, type ThinkingLevel } from "../shared/thinking-ceiling.ts";
25
26
 
26
27
  export type AgentScope = "user" | "project" | "both";
27
28
 
@@ -50,9 +51,13 @@ export function defaultInheritSkills(): boolean {
50
51
 
51
52
  export interface BuiltinAgentOverrideBase {
52
53
  description?: string;
54
+ output?: string;
53
55
  outputMode?: OutputMode;
56
+ defaultReads?: string[];
54
57
  model?: string;
58
+ modelProvider?: string;
55
59
  fallbackModels?: string[];
60
+ fast?: boolean;
56
61
  thinking?: string | false;
57
62
  systemPromptMode: SystemPromptMode;
58
63
  inheritProjectContext: boolean;
@@ -73,9 +78,13 @@ export interface BuiltinAgentOverrideBase {
73
78
 
74
79
  interface BuiltinAgentOverrideConfig {
75
80
  description?: string;
81
+ output?: string | false;
76
82
  outputMode?: OutputMode;
83
+ defaultReads?: string[] | false;
77
84
  model?: string | false;
85
+ defaultProvider?: string | false;
78
86
  fallbackModels?: string[] | false;
87
+ fast?: boolean;
79
88
  thinking?: string | false;
80
89
  systemPromptMode?: SystemPromptMode;
81
90
  inheritProjectContext?: boolean;
@@ -103,6 +112,7 @@ export interface AgentModelSourceInfo {
103
112
  scope: "user" | "project";
104
113
  path: string;
105
114
  model: string;
115
+ defaultProvider?: string;
106
116
  }
107
117
 
108
118
  export interface AgentConfig {
@@ -118,7 +128,9 @@ export interface AgentConfig {
118
128
  tools?: string[];
119
129
  mcpDirectTools?: string[];
120
130
  model?: string;
131
+ modelProvider?: string;
121
132
  fallbackModels?: string[];
133
+ fast?: boolean;
122
134
  thinking?: string | false;
123
135
  systemPromptMode: SystemPromptMode;
124
136
  inheritProjectContext: boolean;
@@ -153,6 +165,7 @@ export interface AgentConfig {
153
165
  extraFields?: Record<string, string>;
154
166
  override?: BuiltinAgentOverrideInfo;
155
167
  modelSource?: AgentModelSourceInfo;
168
+ maxThinking?: ThinkingLevel;
156
169
  }
157
170
 
158
171
  type ProjectRootResolution = "nearest" | "git-root";
@@ -160,7 +173,9 @@ type ProjectRootResolution = "nearest" | "git-root";
160
173
  interface SubagentSettings {
161
174
  overrides: Record<string, BuiltinAgentOverrideConfig>;
162
175
  defaultModel?: string;
176
+ defaultProvider?: string;
163
177
  defaultThinking?: string;
178
+ maxThinking?: ThinkingLevel;
164
179
  defaultExtensions?: string[];
165
180
  disableBuiltins?: boolean;
166
181
  disableThinking?: boolean;
@@ -252,6 +267,7 @@ interface AgentDiscoveryResult {
252
267
  agentDiagnostics?: AgentDiscoveryDiagnostic[];
253
268
  projectAgentsDir: string | null;
254
269
  modelScope?: ModelScopeConfig;
270
+ maxThinking?: ThinkingLevel;
255
271
  }
256
272
 
257
273
  function getUserChainDir(): string {
@@ -621,9 +637,13 @@ function arraysEqual(a: string[] | undefined, b: string[] | undefined): boolean
621
637
  function cloneOverrideBase(agent: AgentConfig): BuiltinAgentOverrideBase {
622
638
  return {
623
639
  description: agent.description,
640
+ ...(agent.output !== undefined ? { output: agent.output } : {}),
624
641
  ...(agent.outputMode !== undefined ? { outputMode: agent.outputMode } : {}),
642
+ ...(agent.defaultReads !== undefined ? { defaultReads: [...agent.defaultReads] } : {}),
625
643
  ...(agent.model !== undefined ? { model: agent.model } : {}),
644
+ ...(agent.modelProvider !== undefined ? { modelProvider: agent.modelProvider } : {}),
626
645
  ...(agent.fallbackModels ? { fallbackModels: [...agent.fallbackModels] } : {}),
646
+ ...(agent.fast !== undefined ? { fast: agent.fast } : {}),
627
647
  ...(agent.thinking !== undefined ? { thinking: agent.thinking } : {}),
628
648
  systemPromptMode: agent.systemPromptMode,
629
649
  inheritProjectContext: agent.inheritProjectContext,
@@ -646,11 +666,15 @@ function cloneOverrideBase(agent: AgentConfig): BuiltinAgentOverrideBase {
646
666
  function cloneOverrideValue(override: BuiltinAgentOverrideConfig): BuiltinAgentOverrideConfig {
647
667
  return {
648
668
  ...(override.description !== undefined ? { description: override.description } : {}),
669
+ ...(override.output !== undefined ? { output: override.output } : {}),
649
670
  ...(override.outputMode !== undefined ? { outputMode: override.outputMode } : {}),
671
+ ...(override.defaultReads !== undefined ? { defaultReads: override.defaultReads === false ? false : [...override.defaultReads] } : {}),
650
672
  ...(override.model !== undefined ? { model: override.model } : {}),
673
+ ...(override.defaultProvider !== undefined ? { defaultProvider: override.defaultProvider } : {}),
651
674
  ...(override.fallbackModels !== undefined
652
675
  ? { fallbackModels: override.fallbackModels === false ? false : [...override.fallbackModels] }
653
676
  : {}),
677
+ ...(override.fast !== undefined ? { fast: override.fast } : {}),
654
678
  ...(override.thinking !== undefined ? { thinking: override.thinking } : {}),
655
679
  ...(override.systemPromptMode !== undefined ? { systemPromptMode: override.systemPromptMode } : {}),
656
680
  ...(override.inheritProjectContext !== undefined ? { inheritProjectContext: override.inheritProjectContext } : {}),
@@ -827,6 +851,11 @@ function parseBuiltinOverrideEntry(
827
851
  }
828
852
  }
829
853
 
854
+ if ("output" in input) {
855
+ if ((typeof input.output === "string" && input.output.trim()) || input.output === false) override.output = input.output;
856
+ else throw new Error(`Builtin override '${name}' in '${filePath}' has invalid 'output'; expected a non-empty string or false.`);
857
+ }
858
+
830
859
  if ("outputMode" in input) {
831
860
  if (input.outputMode === "inline" || input.outputMode === "file-only") {
832
861
  override.outputMode = input.outputMode;
@@ -840,6 +869,11 @@ function parseBuiltinOverrideEntry(
840
869
  else throw new Error(`Builtin override '${name}' in '${filePath}' has invalid 'model'; expected a string or false.`);
841
870
  }
842
871
 
872
+ if ("fast" in input) {
873
+ if (typeof input.fast === "boolean") override.fast = input.fast;
874
+ else throw new Error(`Builtin override '${name}' in '${filePath}' has invalid 'fast'; expected a boolean.`);
875
+ }
876
+
843
877
  if ("thinking" in input) {
844
878
  if (typeof input.thinking === "string" || input.thinking === false) override.thinking = input.thinking;
845
879
  else throw new Error(`Builtin override '${name}' in '${filePath}' has invalid 'thinking'; expected a string or false.`);
@@ -916,9 +950,18 @@ function parseBuiltinOverrideEntry(
916
950
  else throw new Error(`Builtin override '${name}' in '${filePath}' has invalid 'systemPrompt'; expected a string.`);
917
951
  }
918
952
 
953
+ const defaultReads = parseOverrideStringArrayOrFalse(input.defaultReads, { filePath, name, field: "defaultReads" });
954
+ if (defaultReads !== undefined) override.defaultReads = defaultReads;
955
+
919
956
  const fallbackModels = parseOverrideStringArrayOrFalse(input.fallbackModels, { filePath, name, field: "fallbackModels" });
920
957
  if (fallbackModels !== undefined) override.fallbackModels = fallbackModels;
921
958
 
959
+ if ("defaultProvider" in input) {
960
+ if (input.defaultProvider === false) override.defaultProvider = false;
961
+ else if (typeof input.defaultProvider === "string" && input.defaultProvider.trim()) override.defaultProvider = input.defaultProvider.trim();
962
+ else throw new Error(`Builtin override '${name}' in '${filePath}' has invalid 'defaultProvider'; expected a non-empty string or false.`);
963
+ }
964
+
922
965
  const skills = parseOverrideStringArrayOrFalse(input.skills, { filePath, name, field: "skills" });
923
966
  if (skills !== undefined) override.skills = skills;
924
967
 
@@ -965,6 +1008,14 @@ function readSubagentSettings(filePath: string | null): SubagentSettings {
965
1008
  throw new Error(`Subagent settings in '${filePath}' have invalid 'defaultModel'; expected a non-empty string.`);
966
1009
  }
967
1010
  }
1011
+ let defaultProvider: string | undefined;
1012
+ if ("defaultProvider" in subagentsObject) {
1013
+ if (typeof subagentsObject.defaultProvider === "string" && subagentsObject.defaultProvider.trim()) {
1014
+ defaultProvider = subagentsObject.defaultProvider.trim();
1015
+ } else {
1016
+ throw new Error(`Subagent settings in '${filePath}' have invalid 'defaultProvider'; expected a non-empty string.`);
1017
+ }
1018
+ }
968
1019
  let defaultThinking: string | undefined;
969
1020
  if ("defaultThinking" in subagentsObject) {
970
1021
  if (typeof subagentsObject.defaultThinking === "string" && subagentsObject.defaultThinking.trim()) {
@@ -973,6 +1024,14 @@ function readSubagentSettings(filePath: string | null): SubagentSettings {
973
1024
  throw new Error(`Subagent settings in '${filePath}' have invalid 'defaultThinking'; expected a non-empty string.`);
974
1025
  }
975
1026
  }
1027
+ let maxThinking: ThinkingLevel | undefined;
1028
+ if ("maxThinking" in subagentsObject) {
1029
+ try {
1030
+ maxThinking = parseThinkingLevel(subagentsObject.maxThinking, `'${filePath}' subagents.maxThinking`);
1031
+ } catch (error) {
1032
+ throw new Error(`Subagent settings in '${filePath}' have invalid 'maxThinking'; expected one of off, minimal, low, medium, high, xhigh, or max.`, { cause: error instanceof Error ? error : undefined });
1033
+ }
1034
+ }
976
1035
  let defaultExtensions: string[] | undefined;
977
1036
  if ("defaultExtensions" in subagentsObject) {
978
1037
  if (!Array.isArray(subagentsObject.defaultExtensions)
@@ -988,7 +1047,9 @@ function readSubagentSettings(filePath: string | null): SubagentSettings {
988
1047
  const parsedSettings: SubagentSettings = {
989
1048
  overrides: parsed,
990
1049
  ...(defaultModel !== undefined ? { defaultModel } : {}),
1050
+ ...(defaultProvider !== undefined ? { defaultProvider } : {}),
991
1051
  ...(defaultThinking !== undefined ? { defaultThinking } : {}),
1052
+ ...(maxThinking !== undefined ? { maxThinking } : {}),
992
1053
  ...(defaultExtensions !== undefined ? { defaultExtensions } : {}),
993
1054
  ...(disableBuiltins !== undefined ? { disableBuiltins } : {}),
994
1055
  ...(disableThinking !== undefined ? { disableThinking } : {}),
@@ -1004,25 +1065,39 @@ function readSubagentSettings(filePath: string | null): SubagentSettings {
1004
1065
  return parsedSettings;
1005
1066
  }
1006
1067
 
1068
+ function resolveSubagentDefaultProvider(
1069
+ userSettings: SubagentSettings,
1070
+ projectSettings: SubagentSettings,
1071
+ projectSettingsPath: string | null,
1072
+ ): string | undefined {
1073
+ if (projectSettingsPath && projectSettings.defaultProvider !== undefined) return projectSettings.defaultProvider;
1074
+ return userSettings.defaultProvider;
1075
+ }
1076
+
1007
1077
  function resolveSubagentDefaultModel(
1008
1078
  userSettings: SubagentSettings,
1009
1079
  projectSettings: SubagentSettings,
1010
1080
  userSettingsPath: string,
1011
1081
  projectSettingsPath: string | null,
1082
+ defaultProvider: string | undefined,
1012
1083
  ): AgentModelSourceInfo | undefined {
1013
1084
  if (projectSettingsPath && projectSettings.defaultModel !== undefined) {
1014
- return { type: "subagents.defaultModel", scope: "project", path: projectSettingsPath, model: projectSettings.defaultModel };
1085
+ return { type: "subagents.defaultModel", scope: "project", path: projectSettingsPath, model: projectSettings.defaultModel, ...(defaultProvider ? { defaultProvider } : {}) };
1015
1086
  }
1016
1087
  return userSettings.defaultModel !== undefined
1017
- ? { type: "subagents.defaultModel", scope: "user", path: userSettingsPath, model: userSettings.defaultModel }
1088
+ ? { type: "subagents.defaultModel", scope: "user", path: userSettingsPath, model: userSettings.defaultModel, ...(defaultProvider ? { defaultProvider } : {}) }
1018
1089
  : undefined;
1019
1090
  }
1020
1091
 
1021
- function applySubagentDefaultModel(agents: AgentConfig[], defaultModel: AgentModelSourceInfo | undefined): AgentConfig[] {
1022
- if (!defaultModel) return agents;
1092
+ function applySubagentDefaultModel(agents: AgentConfig[], defaultModel: AgentModelSourceInfo | undefined, defaultProvider: string | undefined): AgentConfig[] {
1093
+ if (!defaultModel && !defaultProvider) return agents;
1023
1094
  return agents.map((agent) => {
1024
- if (agent.model !== undefined) return agent;
1025
- const next = { ...agent, model: defaultModel.model, modelSource: defaultModel };
1095
+ if (agent.model !== undefined && (agent.modelProvider !== undefined || !defaultProvider)) return agent;
1096
+ const next = {
1097
+ ...agent,
1098
+ ...(agent.model === undefined && defaultModel ? { model: defaultModel.model, modelSource: defaultModel } : {}),
1099
+ ...(defaultProvider ? { modelProvider: defaultProvider } : {}),
1100
+ };
1026
1101
  const frontmatterFields = agentFrontmatterFields.get(agent);
1027
1102
  if (frontmatterFields) agentFrontmatterFields.set(next, frontmatterFields);
1028
1103
  return next;
@@ -1049,6 +1124,20 @@ function applySubagentDefaultThinking(agents: AgentConfig[], defaultThinking: st
1049
1124
  });
1050
1125
  }
1051
1126
 
1127
+ function resolveSubagentMaxThinking(
1128
+ userSettings: SubagentSettings,
1129
+ projectSettings: SubagentSettings,
1130
+ projectSettingsPath: string | null,
1131
+ ): ThinkingLevel | undefined {
1132
+ if (projectSettingsPath && projectSettings.maxThinking !== undefined) return projectSettings.maxThinking;
1133
+ return userSettings.maxThinking;
1134
+ }
1135
+
1136
+ function applySubagentMaxThinking(agents: AgentConfig[], maxThinking: ThinkingLevel | undefined): AgentConfig[] {
1137
+ if (maxThinking === undefined) return agents;
1138
+ return agents.map((agent) => agent.maxThinking === maxThinking ? agent : { ...agent, maxThinking });
1139
+ }
1140
+
1052
1141
  function resolveSubagentDefaultExtensions(
1053
1142
  userSettings: SubagentSettings,
1054
1143
  projectSettings: SubagentSettings,
@@ -1072,11 +1161,12 @@ function applySubagentDefaultExtensions(agents: AgentConfig[], defaultExtensions
1072
1161
  function applySubagentDefaults(
1073
1162
  agents: AgentConfig[],
1074
1163
  defaultModel: AgentModelSourceInfo | undefined,
1164
+ defaultProvider: string | undefined,
1075
1165
  defaultThinking: string | undefined,
1076
1166
  defaultExtensions: string[] | undefined,
1077
1167
  ): AgentConfig[] {
1078
1168
  return applySubagentDefaultExtensions(
1079
- applySubagentDefaultThinking(applySubagentDefaultModel(agents, defaultModel), defaultThinking),
1169
+ applySubagentDefaultThinking(applySubagentDefaultModel(agents, defaultModel, defaultProvider), defaultThinking),
1080
1170
  defaultExtensions,
1081
1171
  );
1082
1172
  }
@@ -1103,12 +1193,19 @@ function applyBuiltinOverride(
1103
1193
  };
1104
1194
 
1105
1195
  if (override.description !== undefined) next.description = override.description;
1196
+ if (override.output !== undefined) { if (override.output === false) delete next.output; else next.output = override.output; }
1106
1197
  if (override.outputMode !== undefined) next.outputMode = override.outputMode;
1198
+ if (override.defaultReads !== undefined) { if (override.defaultReads === false) delete next.defaultReads; else next.defaultReads = [...override.defaultReads]; }
1107
1199
  if (override.model !== undefined) {
1108
1200
  if (override.model === false) delete next.model; else next.model = override.model;
1109
1201
  delete next.modelSource;
1110
1202
  }
1203
+ if (override.defaultProvider !== undefined) {
1204
+ if (override.defaultProvider === false) delete next.modelProvider;
1205
+ else next.modelProvider = override.defaultProvider;
1206
+ }
1111
1207
  if (override.fallbackModels !== undefined) { if (override.fallbackModels === false) delete next.fallbackModels; else next.fallbackModels = [...override.fallbackModels]; }
1208
+ if (override.fast !== undefined) next.fast = override.fast;
1112
1209
  if (override.thinking !== undefined) { if (override.thinking === false) delete next.thinking; else next.thinking = override.thinking; }
1113
1210
  if (override.systemPromptMode !== undefined) next.systemPromptMode = override.systemPromptMode;
1114
1211
  if (override.inheritProjectContext !== undefined) next.inheritProjectContext = override.inheritProjectContext;
@@ -1221,15 +1318,24 @@ function applyCustomAgentOverride(
1221
1318
  mutable().description = override.description;
1222
1319
  anyFilled = true;
1223
1320
  }
1321
+ if (override.output !== undefined) {
1322
+ fill("output", ["output"], override.output === false ? undefined : override.output);
1323
+ }
1224
1324
  if (override.outputMode !== undefined) {
1225
1325
  fill("outputMode", ["outputMode"], override.outputMode);
1226
1326
  }
1327
+ if (override.defaultReads !== undefined) {
1328
+ fill("defaultReads", ["defaultReads"], override.defaultReads === false ? undefined : [...override.defaultReads]);
1329
+ }
1227
1330
  if (override.model !== undefined && !agentHasFrontmatterField(agent, "model")) {
1228
1331
  const target = mutable();
1229
1332
  if (override.model === false) delete target.model; else target.model = override.model;
1230
1333
  delete target.modelSource;
1231
1334
  anyFilled = true;
1232
1335
  }
1336
+ if (override.defaultProvider !== undefined) {
1337
+ fill("modelProvider", ["modelProvider", "defaultProvider"], override.defaultProvider === false ? undefined : override.defaultProvider);
1338
+ }
1233
1339
  if (override.fallbackModels !== undefined) {
1234
1340
  fill(
1235
1341
  "fallbackModels",
@@ -1237,6 +1343,9 @@ function applyCustomAgentOverride(
1237
1343
  override.fallbackModels === false ? undefined : [...override.fallbackModels],
1238
1344
  );
1239
1345
  }
1346
+ if (override.fast !== undefined) {
1347
+ fill("fast", ["fast"], override.fast);
1348
+ }
1240
1349
  if (override.thinking !== undefined) {
1241
1350
  fill("thinking", ["thinking"], override.thinking === false ? undefined : override.thinking);
1242
1351
  }
@@ -1256,17 +1365,7 @@ function applyCustomAgentOverride(
1256
1365
  fill("acceptanceRole", ["acceptanceRole"], override.acceptanceRole === false ? undefined : override.acceptanceRole);
1257
1366
  }
1258
1367
  if (override.disabled !== undefined) {
1259
- // Unconditional, matching applyBuiltinOverride: custom agents have no
1260
- // frontmatter concept of `disabled` to protect (unlike model/thinking/
1261
- // etc., which use `fill()` + agentHasFrontmatterField to defer to the
1262
- // agent's own file). A guard here previously read `agent.disabled ===
1263
- // undefined`, which was a no-op before user+project layering existed
1264
- // (this function only ever ran once, against the pristine base agent,
1265
- // whose `.disabled` is always undefined for custom agents) but became a
1266
- // real bug once a user-scope override could run first: the guard then
1267
- // silently blocked a later project-scope override from ever changing
1268
- // `disabled`, breaking this PR's own "project wins" precedence for this
1269
- // one field.
1368
+ // Custom agent files cannot set `disabled`, so project overrides replace user overrides.
1270
1369
  mutable().disabled = override.disabled;
1271
1370
  anyFilled = true;
1272
1371
  }
@@ -1295,7 +1394,7 @@ function applyCustomAgentOverride(
1295
1394
  }
1296
1395
 
1297
1396
  if (!anyFilled || !next) return agent;
1298
- next.override = { ...meta, base: cloneOverrideBase(agent) };
1397
+ next.override = { ...meta, base: agent.override?.base ?? cloneOverrideBase(agent) };
1299
1398
  const frontmatterFields = agentFrontmatterFields.get(agent);
1300
1399
  if (frontmatterFields) agentFrontmatterFields.set(next, frontmatterFields);
1301
1400
  return next;
@@ -1308,13 +1407,7 @@ function applyCustomAgentOverrides(
1308
1407
  userSettingsPath: string,
1309
1408
  projectSettingsPath: string | null,
1310
1409
  ): AgentConfig[] {
1311
- // Both scopes are applied, user first then project, so a project override that
1312
- // only sets a subset of fields (e.g. just `extensions`) layers on top of the
1313
- // user's override instead of silently discarding it (#1341 / issue writeup:
1314
- // "agentOverrides project override drops user-only fields for custom agents").
1315
- // Per-field precedence still favors project over user: applyCustomAgentOverride
1316
- // only fills fields present in the override it's given, and project is applied
1317
- // last, so any field set at both scopes ends up with the project's value.
1410
+ // Apply user then project so project fields win without dropping user-only fields.
1318
1411
  return agents.map((agent) => {
1319
1412
  const userOverride = userSettings.overrides[agent.name];
1320
1413
  const withUserOverride = userOverride
@@ -1332,7 +1425,7 @@ function applyCustomAgentOverrides(
1332
1425
 
1333
1426
  export function buildBuiltinOverrideConfig(
1334
1427
  base: BuiltinAgentOverrideBase,
1335
- draft: Pick<AgentConfig, "model" | "fallbackModels" | "thinking" | "systemPromptMode" | "inheritProjectContext" | "inheritSkills" | "defaultContext" | "acceptanceRole" | "disabled" | "systemPrompt" | "skills" | "tools" | "mcpDirectTools" | "extensions" | "subagentOnlyExtensions" | "completionGuard" | "toolBudget"> & Partial<Pick<AgentConfig, "description" | "outputMode">>,
1428
+ draft: Pick<AgentConfig, "model" | "modelProvider" | "fallbackModels" | "fast" | "thinking" | "systemPromptMode" | "inheritProjectContext" | "inheritSkills" | "defaultContext" | "acceptanceRole" | "disabled" | "systemPrompt" | "skills" | "tools" | "mcpDirectTools" | "extensions" | "subagentOnlyExtensions" | "completionGuard" | "toolBudget"> & Partial<Pick<AgentConfig, "description" | "output" | "outputMode" | "defaultReads">>,
1336
1429
  ): BuiltinAgentOverrideConfig | undefined {
1337
1430
  const override: BuiltinAgentOverrideConfig = {};
1338
1431
 
@@ -1340,9 +1433,13 @@ export function buildBuiltinOverrideConfig(
1340
1433
  const description = draft.description.trim();
1341
1434
  if (description && description !== base.description) override.description = description;
1342
1435
  }
1436
+ if (draft.output !== base.output) override.output = draft.output ?? false;
1343
1437
  if (draft.outputMode !== undefined && draft.outputMode !== base.outputMode) override.outputMode = draft.outputMode;
1438
+ if (!arraysEqual(draft.defaultReads, base.defaultReads)) override.defaultReads = draft.defaultReads ? [...draft.defaultReads] : false;
1344
1439
  if (draft.model !== base.model) override.model = draft.model ?? false;
1440
+ if (draft.modelProvider !== base.modelProvider) override.defaultProvider = draft.modelProvider ?? false;
1345
1441
  if (!arraysEqual(draft.fallbackModels, base.fallbackModels)) override.fallbackModels = draft.fallbackModels ? [...draft.fallbackModels] : false;
1442
+ if (draft.fast !== base.fast) override.fast = draft.fast === true;
1346
1443
  if (draft.thinking !== base.thinking) override.thinking = draft.thinking ?? false;
1347
1444
  if (draft.systemPromptMode !== base.systemPromptMode) override.systemPromptMode = draft.systemPromptMode;
1348
1445
  if (draft.inheritProjectContext !== base.inheritProjectContext) override.inheritProjectContext = draft.inheritProjectContext;
@@ -1753,6 +1850,12 @@ function loadAgentsFromDefinitionFiles(files: AgentDefinitionFile[], source: Age
1753
1850
 
1754
1851
  const extensions = resolveAgentRelativeExtensionPaths(parseFrontmatterList(frontmatter.extensions), filePath);
1755
1852
  const subagentOnlyExtensions = resolveAgentRelativeExtensionPaths(parseFrontmatterList(frontmatter.subagentOnlyExtensions), filePath);
1853
+ let fast: boolean | undefined;
1854
+ if (frontmatter.fast !== undefined) {
1855
+ if (frontmatter.fast === "true") fast = true;
1856
+ else if (frontmatter.fast === "false") fast = false;
1857
+ else throw new Error(`Agent '${localName}' has invalid fast frontmatter; expected true or false.`);
1858
+ }
1756
1859
 
1757
1860
  const extraFields: Record<string, string> = {};
1758
1861
  for (const [key, value] of Object.entries(frontmatter)) {
@@ -1799,6 +1902,7 @@ function loadAgentsFromDefinitionFiles(files: AgentDefinitionFile[], source: Age
1799
1902
  ...(mcpDirectTools.length > 0 ? { mcpDirectTools } : {}),
1800
1903
  ...(frontmatter.model !== undefined ? { model: frontmatter.model } : {}),
1801
1904
  ...(fallbackModels?.length ? { fallbackModels } : {}),
1905
+ ...(fast !== undefined ? { fast } : {}),
1802
1906
  ...(frontmatter.thinking !== undefined ? { thinking: frontmatter.thinking === "false" ? false : frontmatter.thinking } : {}),
1803
1907
  systemPromptMode,
1804
1908
  inheritProjectContext,
@@ -1931,8 +2035,10 @@ export function discoverAgents(cwd: string, scope: AgentScope): AgentDiscoveryRe
1931
2035
  const projectSettingsPath = getProjectAgentSettingsPath(cwd);
1932
2036
  const userSettings = scope === "project" ? EMPTY_SUBAGENT_SETTINGS : readSubagentSettings(userSettingsPath);
1933
2037
  const projectSettings = scope === "user" ? EMPTY_SUBAGENT_SETTINGS : readSubagentSettings(projectSettingsPath);
1934
- const defaultModel = resolveSubagentDefaultModel(userSettings, projectSettings, userSettingsPath, projectSettingsPath);
2038
+ const defaultProvider = resolveSubagentDefaultProvider(userSettings, projectSettings, projectSettingsPath);
2039
+ const defaultModel = resolveSubagentDefaultModel(userSettings, projectSettings, userSettingsPath, projectSettingsPath, defaultProvider);
1935
2040
  const defaultThinking = resolveSubagentDefaultThinking(userSettings, projectSettings, projectSettingsPath);
2041
+ const maxThinking = resolveSubagentMaxThinking(userSettings, projectSettings, projectSettingsPath);
1936
2042
  const defaultExtensions = resolveSubagentDefaultExtensions(userSettings, projectSettings, projectSettingsPath);
1937
2043
  const modelScope = projectSettings.modelScope ?? userSettings.modelScope;
1938
2044
  const packageSubagentPaths = collectPackageSubagentPaths(cwd, {
@@ -1942,7 +2048,7 @@ export function discoverAgents(cwd: string, scope: AgentScope): AgentDiscoveryRe
1942
2048
 
1943
2049
  const builtinLoaded = loadAgentsFromDefinitionFiles(BUILTIN_AGENT_DEFINITION_FILES, "builtin");
1944
2050
  const builtinAgents = applyBuiltinOverrides(
1945
- applySubagentDefaults(builtinLoaded.agents, defaultModel, defaultThinking, defaultExtensions),
2051
+ applySubagentDefaults(builtinLoaded.agents, defaultModel, defaultProvider, defaultThinking, defaultExtensions),
1946
2052
  userSettings,
1947
2053
  projectSettings,
1948
2054
  userSettingsPath,
@@ -1952,7 +2058,7 @@ export function discoverAgents(cwd: string, scope: AgentScope): AgentDiscoveryRe
1952
2058
  const userLoaded = scope === "project" ? [] : [...extraUserAgentDirs(), userDirOld, userDirNew]
1953
2059
  .map((dir, discoveryPriority) => loadAgentsFromDir(dir, "user", discoveryPriority));
1954
2060
  const userAgents = applyCustomAgentOverrides(
1955
- applySubagentDefaults(userLoaded.flatMap((loaded) => loaded.agents), defaultModel, defaultThinking, defaultExtensions),
2061
+ applySubagentDefaults(userLoaded.flatMap((loaded) => loaded.agents), defaultModel, defaultProvider, defaultThinking, defaultExtensions),
1956
2062
  userSettings,
1957
2063
  projectSettings,
1958
2064
  userSettingsPath,
@@ -1961,7 +2067,7 @@ export function discoverAgents(cwd: string, scope: AgentScope): AgentDiscoveryRe
1961
2067
 
1962
2068
  const projectLoaded = scope === "user" ? [] : projectAgentDirs.map((dir) => loadAgentsFromDir(dir, "project", dir === projectAgentsDir ? 1 : 0));
1963
2069
  const projectAgents = applyCustomAgentOverrides(
1964
- applySubagentDefaults(projectLoaded.flatMap((loaded) => loaded.agents), defaultModel, defaultThinking, defaultExtensions),
2070
+ applySubagentDefaults(projectLoaded.flatMap((loaded) => loaded.agents), defaultModel, defaultProvider, defaultThinking, defaultExtensions),
1965
2071
  userSettings,
1966
2072
  projectSettings,
1967
2073
  userSettingsPath,
@@ -1975,14 +2081,17 @@ export function discoverAgents(cwd: string, scope: AgentScope): AgentDiscoveryRe
1975
2081
  }
1976
2082
  }
1977
2083
  const packageAgents = applyCustomAgentOverrides(
1978
- applySubagentDefaults(Array.from(packageMap.values()), defaultModel, defaultThinking, defaultExtensions),
2084
+ applySubagentDefaults(Array.from(packageMap.values()), defaultModel, defaultProvider, defaultThinking, defaultExtensions),
1979
2085
  userSettings,
1980
2086
  projectSettings,
1981
2087
  userSettingsPath,
1982
2088
  projectSettingsPath,
1983
2089
  );
1984
- const agents = mergeAgentsForScope(scope, userAgents, projectAgents, builtinAgents, packageAgents)
1985
- .filter((agent) => agent.disabled !== true);
2090
+ const agents = applySubagentMaxThinking(
2091
+ mergeAgentsForScope(scope, userAgents, projectAgents, builtinAgents, packageAgents)
2092
+ .filter((agent) => agent.disabled !== true),
2093
+ maxThinking,
2094
+ );
1986
2095
 
1987
2096
  const agentDiagnostics = [
1988
2097
  ...builtinLoaded.diagnostics,
@@ -1990,7 +2099,7 @@ export function discoverAgents(cwd: string, scope: AgentScope): AgentDiscoveryRe
1990
2099
  ...projectLoaded.flatMap((loaded) => loaded.diagnostics),
1991
2100
  ...packageLoaded.flatMap((loaded) => loaded.diagnostics),
1992
2101
  ];
1993
- return { agents, agentDiagnostics, projectAgentsDir, ...(modelScope !== undefined ? { modelScope } : {}) };
2102
+ return { agents, agentDiagnostics, projectAgentsDir, ...(modelScope !== undefined ? { modelScope } : {}), ...(maxThinking !== undefined ? { maxThinking } : {}) };
1994
2103
  }
1995
2104
 
1996
2105
  export function discoverAgentsAll(cwd: string): {
@@ -2007,6 +2116,7 @@ export function discoverAgentsAll(cwd: string): {
2007
2116
  projectChainDir: string | null;
2008
2117
  userSettingsPath: string;
2009
2118
  projectSettingsPath: string | null;
2119
+ maxThinking?: ThinkingLevel;
2010
2120
  } {
2011
2121
  const userDirOld = path.join(getAgentDir(), "agents");
2012
2122
  const userDirNew = path.join(os.homedir(), ".agents");
@@ -2017,14 +2127,16 @@ export function discoverAgentsAll(cwd: string): {
2017
2127
  const projectSettingsPath = getProjectAgentSettingsPath(cwd);
2018
2128
  const userSettings = readSubagentSettings(userSettingsPath);
2019
2129
  const projectSettings = readSubagentSettings(projectSettingsPath);
2020
- const defaultModel = resolveSubagentDefaultModel(userSettings, projectSettings, userSettingsPath, projectSettingsPath);
2130
+ const defaultProvider = resolveSubagentDefaultProvider(userSettings, projectSettings, projectSettingsPath);
2131
+ const defaultModel = resolveSubagentDefaultModel(userSettings, projectSettings, userSettingsPath, projectSettingsPath, defaultProvider);
2021
2132
  const defaultThinking = resolveSubagentDefaultThinking(userSettings, projectSettings, projectSettingsPath);
2133
+ const maxThinking = resolveSubagentMaxThinking(userSettings, projectSettings, projectSettingsPath);
2022
2134
  const defaultExtensions = resolveSubagentDefaultExtensions(userSettings, projectSettings, projectSettingsPath);
2023
2135
  const packageSubagentPaths = collectPackageSubagentPaths(cwd);
2024
2136
 
2025
2137
  const builtinLoaded = loadAgentsFromDefinitionFiles(BUILTIN_AGENT_DEFINITION_FILES, "builtin");
2026
2138
  const builtin = applyBuiltinOverrides(
2027
- applySubagentDefaults(builtinLoaded.agents, defaultModel, defaultThinking, defaultExtensions),
2139
+ applySubagentDefaults(builtinLoaded.agents, defaultModel, defaultProvider, defaultThinking, defaultExtensions),
2028
2140
  userSettings,
2029
2141
  projectSettings,
2030
2142
  userSettingsPath,
@@ -2033,7 +2145,7 @@ export function discoverAgentsAll(cwd: string): {
2033
2145
  const userLoaded = [...extraUserAgentDirs(), userDirOld, userDirNew]
2034
2146
  .map((dir, discoveryPriority) => loadAgentsFromDir(dir, "user", discoveryPriority));
2035
2147
  const user = applyCustomAgentOverrides(
2036
- applySubagentDefaults(userLoaded.flatMap((loaded) => loaded.agents), defaultModel, defaultThinking, defaultExtensions),
2148
+ applySubagentDefaults(userLoaded.flatMap((loaded) => loaded.agents), defaultModel, defaultProvider, defaultThinking, defaultExtensions),
2037
2149
  userSettings,
2038
2150
  projectSettings,
2039
2151
  userSettingsPath,
@@ -2049,7 +2161,7 @@ export function discoverAgentsAll(cwd: string): {
2049
2161
  }
2050
2162
  }
2051
2163
  const packageAgents = applyCustomAgentOverrides(
2052
- applySubagentDefaults(Array.from(packageMap.values()), defaultModel, defaultThinking, defaultExtensions),
2164
+ applySubagentDefaults(Array.from(packageMap.values()), defaultModel, defaultProvider, defaultThinking, defaultExtensions),
2053
2165
  userSettings,
2054
2166
  projectSettings,
2055
2167
  userSettingsPath,
@@ -2065,7 +2177,7 @@ export function discoverAgentsAll(cwd: string): {
2065
2177
  }
2066
2178
  }
2067
2179
  const project = applyCustomAgentOverrides(
2068
- applySubagentDefaults(Array.from(projectMap.values()), defaultModel, defaultThinking, defaultExtensions),
2180
+ applySubagentDefaults(Array.from(projectMap.values()), defaultModel, defaultProvider, defaultThinking, defaultExtensions),
2069
2181
  userSettings,
2070
2182
  projectSettings,
2071
2183
  userSettingsPath,
@@ -2110,5 +2222,20 @@ export function discoverAgentsAll(cwd: string): {
2110
2222
 
2111
2223
  const userDir = process.env.PI_CODING_AGENT_DIR ? userDirOld : fs.existsSync(userDirNew) ? userDirNew : userDirOld;
2112
2224
 
2113
- return { builtin, package: packageAgents, user, project, agentDiagnostics, chains, chainDiagnostics, userDir, projectDir, userChainDir, projectChainDir, userSettingsPath, projectSettingsPath };
2225
+ return {
2226
+ builtin: applySubagentMaxThinking(builtin, maxThinking),
2227
+ package: applySubagentMaxThinking(packageAgents, maxThinking),
2228
+ user: applySubagentMaxThinking(user, maxThinking),
2229
+ project: applySubagentMaxThinking(project, maxThinking),
2230
+ agentDiagnostics,
2231
+ chains,
2232
+ chainDiagnostics,
2233
+ userDir,
2234
+ projectDir,
2235
+ userChainDir,
2236
+ projectChainDir,
2237
+ userSettingsPath,
2238
+ projectSettingsPath,
2239
+ ...(maxThinking !== undefined ? { maxThinking } : {}),
2240
+ };
2114
2241
  }
@@ -9,7 +9,7 @@ const MAX_FAILURE_MESSAGE_LENGTH = 4_096;
9
9
  const MAX_URL_LENGTH = 4_096;
10
10
 
11
11
  export type ExternalJobState = "queued" | "running" | "completed" | "failed" | "stopped" | "blocked";
12
- export type ExternalJobOperation = "start" | "status" | "result" | "reattach";
12
+ export type ExternalJobOperation = "start" | "follow-up" | "status" | "result" | "reattach";
13
13
 
14
14
  export type ExternalJobOptions = Record<string, unknown>;
15
15
 
@@ -24,6 +24,14 @@ export interface ExternalJobStartInput {
24
24
  sessionId?: string;
25
25
  }
26
26
 
27
+ export interface ExternalJobFollowUpInput extends ExternalJobStartInput {
28
+ sourceRunId: string;
29
+ sourceStepIndex: number;
30
+ parentProviderJobId: string;
31
+ requestId: string;
32
+ requestDigest: string;
33
+ }
34
+
27
35
  export interface ExternalJobHandle {
28
36
  providerJobId: string;
29
37
  state: ExternalJobState;
@@ -42,6 +50,7 @@ export interface ExternalJobResult extends ExternalJobHandle {
42
50
  export interface ExternalJobProvider {
43
51
  name: string;
44
52
  start(input: ExternalJobStartInput): Promise<ExternalJobHandle> | ExternalJobHandle;
53
+ followUp?(input: ExternalJobFollowUpInput): Promise<ExternalJobHandle> | ExternalJobHandle;
45
54
  status(providerJobId: string): Promise<ExternalJobHandle> | ExternalJobHandle;
46
55
  result(providerJobId: string): Promise<ExternalJobResult> | ExternalJobResult;
47
56
  reattach(providerJobId: string): Promise<ExternalJobHandle> | ExternalJobHandle;