pi-subagents 0.53.0 → 0.55.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 (76) hide show
  1. package/CHANGELOG.md +66 -5
  2. package/README.md +1 -1
  3. package/agents/reviewer.md +12 -2
  4. package/docs/agents.md +4 -4
  5. package/docs/configuration.md +6 -6
  6. package/docs/extension-api.md +14 -4
  7. package/docs/models.md +41 -6
  8. package/docs/observability.md +14 -3
  9. package/docs/tool-reference.md +21 -6
  10. package/docs/workflows.md +7 -1
  11. package/index.ts +10 -1
  12. package/package.json +1 -1
  13. package/prompts/council.md +19 -7
  14. package/prompts/parallel-review.md +5 -1
  15. package/prompts/review-loop.md +9 -5
  16. package/skills/council-mode/SKILL.md +50 -26
  17. package/skills/pi-subagents/SKILL.md +5 -1
  18. package/skills/pi-subagents/references/constraints-and-recipes.md +16 -1
  19. package/skills/pi-subagents/references/execution-controls.md +13 -4
  20. package/skills/pi-subagents/references/multi-lane-orchestration.md +3 -3
  21. package/skills/pi-subagents/references/prompting-and-roles.md +14 -7
  22. package/src/agents/agent-management.ts +115 -14
  23. package/src/agents/agent-serializer.ts +2 -2
  24. package/src/agents/agents.ts +195 -47
  25. package/src/api/external-job-provider.ts +10 -1
  26. package/src/api/preflight.ts +33 -19
  27. package/src/api/project-panes.ts +2 -0
  28. package/src/extension/doctor.ts +10 -0
  29. package/src/extension/fanout-child.ts +3 -2
  30. package/src/extension/index.ts +24 -4
  31. package/src/extension/public-execution.ts +12 -9
  32. package/src/extension/rpc.ts +77 -3
  33. package/src/extension/schemas.ts +2 -1
  34. package/src/extension/tool-description.ts +9 -6
  35. package/src/extension/tool-result.ts +19 -0
  36. package/src/inspectors/herdr/client.ts +3 -3
  37. package/src/inspectors/herdr/focus.ts +55 -0
  38. package/src/inspectors/herdr/project-panes.ts +228 -44
  39. package/src/integrations/herdr-status.ts +26 -4
  40. package/src/runs/background/async-execution.ts +112 -13
  41. package/src/runs/background/async-job-tracker.ts +33 -14
  42. package/src/runs/background/async-resume.ts +20 -2
  43. package/src/runs/background/async-retention.ts +1 -1
  44. package/src/runs/background/async-status.ts +10 -0
  45. package/src/runs/background/chain-root-attachment.ts +5 -0
  46. package/src/runs/background/control-channel.ts +98 -10
  47. package/src/runs/background/notify.ts +62 -1
  48. package/src/runs/background/result-watcher.ts +4 -3
  49. package/src/runs/background/run-status.ts +15 -1
  50. package/src/runs/background/stale-run-reconciler.ts +3 -0
  51. package/src/runs/background/subagent-runner.ts +251 -38
  52. package/src/runs/background/wait-completions.ts +2 -0
  53. package/src/runs/background/wait-tool.ts +4 -3
  54. package/src/runs/foreground/async-stop-action.ts +23 -2
  55. package/src/runs/foreground/execution.ts +110 -8
  56. package/src/runs/foreground/subagent-executor.ts +361 -45
  57. package/src/runs/foreground/workflow-detach-reconcile.ts +3 -0
  58. package/src/runs/shared/acceptance.ts +1 -0
  59. package/src/runs/shared/child-identity.ts +36 -0
  60. package/src/runs/shared/completion-guard.ts +50 -1
  61. package/src/runs/shared/external-job-bridge.ts +53 -37
  62. package/src/runs/shared/external-job-runner.ts +126 -23
  63. package/src/runs/shared/model-fallback.ts +46 -15
  64. package/src/runs/shared/model-scope.ts +106 -39
  65. package/src/runs/shared/orca-progress-tabs.ts +71 -9
  66. package/src/runs/shared/parallel-utils.ts +12 -0
  67. package/src/runs/shared/pi-args.ts +31 -1
  68. package/src/runs/shared/subagent-prompt-runtime.ts +39 -10
  69. package/src/runs/shared/tool-availability.ts +1 -3
  70. package/src/shared/launch-contract.ts +6 -5
  71. package/src/shared/thinking-ceiling.ts +52 -0
  72. package/src/shared/types.ts +72 -5
  73. package/src/tui/fleet-status.ts +66 -13
  74. package/src/tui/render.ts +5 -4
  75. package/src/watchdog/permission-arbiter.ts +59 -51
  76. package/src/workflows/scripted-workflow.ts +107 -10
@@ -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,8 +51,11 @@ 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[];
56
60
  thinking?: string | false;
57
61
  systemPromptMode: SystemPromptMode;
@@ -73,8 +77,11 @@ export interface BuiltinAgentOverrideBase {
73
77
 
74
78
  interface BuiltinAgentOverrideConfig {
75
79
  description?: string;
80
+ output?: string | false;
76
81
  outputMode?: OutputMode;
82
+ defaultReads?: string[] | false;
77
83
  model?: string | false;
84
+ defaultProvider?: string | false;
78
85
  fallbackModels?: string[] | false;
79
86
  thinking?: string | false;
80
87
  systemPromptMode?: SystemPromptMode;
@@ -103,6 +110,7 @@ export interface AgentModelSourceInfo {
103
110
  scope: "user" | "project";
104
111
  path: string;
105
112
  model: string;
113
+ defaultProvider?: string;
106
114
  }
107
115
 
108
116
  export interface AgentConfig {
@@ -110,11 +118,15 @@ export interface AgentConfig {
110
118
  runner?: AgentRunnerConfig;
111
119
  localName?: string;
112
120
  packageName?: string;
121
+ packageSourceName?: string;
122
+ packageSourceVersion?: string;
123
+ packageSourceRoot?: string;
113
124
  description: string;
114
125
  aliases?: string[];
115
126
  tools?: string[];
116
127
  mcpDirectTools?: string[];
117
128
  model?: string;
129
+ modelProvider?: string;
118
130
  fallbackModels?: string[];
119
131
  thinking?: string | false;
120
132
  systemPromptMode: SystemPromptMode;
@@ -150,6 +162,7 @@ export interface AgentConfig {
150
162
  extraFields?: Record<string, string>;
151
163
  override?: BuiltinAgentOverrideInfo;
152
164
  modelSource?: AgentModelSourceInfo;
165
+ maxThinking?: ThinkingLevel;
153
166
  }
154
167
 
155
168
  type ProjectRootResolution = "nearest" | "git-root";
@@ -157,7 +170,9 @@ type ProjectRootResolution = "nearest" | "git-root";
157
170
  interface SubagentSettings {
158
171
  overrides: Record<string, BuiltinAgentOverrideConfig>;
159
172
  defaultModel?: string;
173
+ defaultProvider?: string;
160
174
  defaultThinking?: string;
175
+ maxThinking?: ThinkingLevel;
161
176
  defaultExtensions?: string[];
162
177
  disableBuiltins?: boolean;
163
178
  disableThinking?: boolean;
@@ -249,14 +264,22 @@ interface AgentDiscoveryResult {
249
264
  agentDiagnostics?: AgentDiscoveryDiagnostic[];
250
265
  projectAgentsDir: string | null;
251
266
  modelScope?: ModelScopeConfig;
267
+ maxThinking?: ThinkingLevel;
252
268
  }
253
269
 
254
270
  function getUserChainDir(): string {
255
271
  return path.join(getAgentDir(), "chains");
256
272
  }
257
273
 
274
+ interface PackageSubagentPath {
275
+ dir: string;
276
+ packageName?: string;
277
+ packageVersion?: string;
278
+ packageRoot: string;
279
+ }
280
+
258
281
  interface PackageSubagentPaths {
259
- agents: string[];
282
+ agents: PackageSubagentPath[];
260
283
  chains: string[];
261
284
  }
262
285
 
@@ -391,18 +414,30 @@ function stringArray(value: unknown): string[] {
391
414
  return value.filter((entry): entry is string => typeof entry === "string" && entry.trim().length > 0);
392
415
  }
393
416
 
417
+ function packageMetadata(pkg: Record<string, unknown>, packageRoot: string): Omit<PackageSubagentPath, "dir"> {
418
+ const name = typeof pkg.name === "string" && pkg.name.trim() ? pkg.name.trim() : undefined;
419
+ const version = typeof pkg.version === "string" && pkg.version.trim() ? pkg.version.trim() : undefined;
420
+ return {
421
+ packageRoot,
422
+ ...(name ? { packageName: name } : {}),
423
+ ...(version ? { packageVersion: version } : {}),
424
+ };
425
+ }
426
+
394
427
  function extractSubagentPathsFromPackageRoot(packageRoot: string): PackageSubagentPaths {
395
428
  const packageJsonPath = path.join(packageRoot, "package.json");
396
429
  const pkg = readJsonFileBestEffort(packageJsonPath);
397
430
  if (!pkg || typeof pkg !== "object" || Array.isArray(pkg)) return { agents: [], chains: [] };
431
+ const pkgRecord = pkg as Record<string, unknown>;
432
+ const metadata = packageMetadata(pkgRecord, packageRoot);
398
433
 
399
434
  const roots: Record<string, unknown>[] = [];
400
- const piSubagents = (pkg as { "pi-subagents"?: unknown })["pi-subagents"];
435
+ const piSubagents = pkgRecord["pi-subagents"];
401
436
  if (piSubagents && typeof piSubagents === "object" && !Array.isArray(piSubagents)) {
402
437
  roots.push(piSubagents as Record<string, unknown>);
403
438
  }
404
439
 
405
- const pi = (pkg as { pi?: unknown }).pi;
440
+ const pi = pkgRecord.pi;
406
441
  if (pi && typeof pi === "object" && !Array.isArray(pi)) {
407
442
  const subagents = (pi as { subagents?: unknown }).subagents;
408
443
  if (subagents && typeof subagents === "object" && !Array.isArray(subagents)) {
@@ -410,10 +445,10 @@ function extractSubagentPathsFromPackageRoot(packageRoot: string): PackageSubage
410
445
  }
411
446
  }
412
447
 
413
- const agents: string[] = [];
448
+ const agents: PackageSubagentPath[] = [];
414
449
  const chains: string[] = [];
415
450
  for (const root of roots) {
416
- for (const entry of stringArray(root.agents)) agents.push(path.resolve(packageRoot, entry));
451
+ for (const entry of stringArray(root.agents)) agents.push({ dir: path.resolve(packageRoot, entry), ...metadata });
417
452
  for (const entry of stringArray(root.chains)) chains.push(path.resolve(packageRoot, entry));
418
453
  }
419
454
  return { agents, chains };
@@ -505,17 +540,17 @@ function collectPackageSubagentPaths(cwd: string, options: { includeUser: boolea
505
540
  const seenRoots = new Set<string>();
506
541
  const seenAgents = new Set<string>();
507
542
  const seenChains = new Set<string>();
508
- const agents: string[] = [];
543
+ const agents: PackageSubagentPath[] = [];
509
544
  const chains: string[] = [];
510
545
  for (const packageRoot of packageRoots) {
511
546
  const resolvedRoot = path.resolve(packageRoot);
512
547
  if (seenRoots.has(resolvedRoot)) continue;
513
548
  seenRoots.add(resolvedRoot);
514
549
  const paths = extractSubagentPathsFromPackageRoot(resolvedRoot);
515
- for (const agentDir of paths.agents) {
516
- if (seenAgents.has(agentDir)) continue;
517
- seenAgents.add(agentDir);
518
- agents.push(agentDir);
550
+ for (const agentPath of paths.agents) {
551
+ if (seenAgents.has(agentPath.dir)) continue;
552
+ seenAgents.add(agentPath.dir);
553
+ agents.push(agentPath);
519
554
  }
520
555
  for (const chainDir of paths.chains) {
521
556
  if (seenChains.has(chainDir)) continue;
@@ -599,8 +634,11 @@ function arraysEqual(a: string[] | undefined, b: string[] | undefined): boolean
599
634
  function cloneOverrideBase(agent: AgentConfig): BuiltinAgentOverrideBase {
600
635
  return {
601
636
  description: agent.description,
637
+ ...(agent.output !== undefined ? { output: agent.output } : {}),
602
638
  ...(agent.outputMode !== undefined ? { outputMode: agent.outputMode } : {}),
639
+ ...(agent.defaultReads !== undefined ? { defaultReads: [...agent.defaultReads] } : {}),
603
640
  ...(agent.model !== undefined ? { model: agent.model } : {}),
641
+ ...(agent.modelProvider !== undefined ? { modelProvider: agent.modelProvider } : {}),
604
642
  ...(agent.fallbackModels ? { fallbackModels: [...agent.fallbackModels] } : {}),
605
643
  ...(agent.thinking !== undefined ? { thinking: agent.thinking } : {}),
606
644
  systemPromptMode: agent.systemPromptMode,
@@ -624,8 +662,11 @@ function cloneOverrideBase(agent: AgentConfig): BuiltinAgentOverrideBase {
624
662
  function cloneOverrideValue(override: BuiltinAgentOverrideConfig): BuiltinAgentOverrideConfig {
625
663
  return {
626
664
  ...(override.description !== undefined ? { description: override.description } : {}),
665
+ ...(override.output !== undefined ? { output: override.output } : {}),
627
666
  ...(override.outputMode !== undefined ? { outputMode: override.outputMode } : {}),
667
+ ...(override.defaultReads !== undefined ? { defaultReads: override.defaultReads === false ? false : [...override.defaultReads] } : {}),
628
668
  ...(override.model !== undefined ? { model: override.model } : {}),
669
+ ...(override.defaultProvider !== undefined ? { defaultProvider: override.defaultProvider } : {}),
629
670
  ...(override.fallbackModels !== undefined
630
671
  ? { fallbackModels: override.fallbackModels === false ? false : [...override.fallbackModels] }
631
672
  : {}),
@@ -805,6 +846,11 @@ function parseBuiltinOverrideEntry(
805
846
  }
806
847
  }
807
848
 
849
+ if ("output" in input) {
850
+ if ((typeof input.output === "string" && input.output.trim()) || input.output === false) override.output = input.output;
851
+ else throw new Error(`Builtin override '${name}' in '${filePath}' has invalid 'output'; expected a non-empty string or false.`);
852
+ }
853
+
808
854
  if ("outputMode" in input) {
809
855
  if (input.outputMode === "inline" || input.outputMode === "file-only") {
810
856
  override.outputMode = input.outputMode;
@@ -894,9 +940,18 @@ function parseBuiltinOverrideEntry(
894
940
  else throw new Error(`Builtin override '${name}' in '${filePath}' has invalid 'systemPrompt'; expected a string.`);
895
941
  }
896
942
 
943
+ const defaultReads = parseOverrideStringArrayOrFalse(input.defaultReads, { filePath, name, field: "defaultReads" });
944
+ if (defaultReads !== undefined) override.defaultReads = defaultReads;
945
+
897
946
  const fallbackModels = parseOverrideStringArrayOrFalse(input.fallbackModels, { filePath, name, field: "fallbackModels" });
898
947
  if (fallbackModels !== undefined) override.fallbackModels = fallbackModels;
899
948
 
949
+ if ("defaultProvider" in input) {
950
+ if (input.defaultProvider === false) override.defaultProvider = false;
951
+ else if (typeof input.defaultProvider === "string" && input.defaultProvider.trim()) override.defaultProvider = input.defaultProvider.trim();
952
+ else throw new Error(`Builtin override '${name}' in '${filePath}' has invalid 'defaultProvider'; expected a non-empty string or false.`);
953
+ }
954
+
900
955
  const skills = parseOverrideStringArrayOrFalse(input.skills, { filePath, name, field: "skills" });
901
956
  if (skills !== undefined) override.skills = skills;
902
957
 
@@ -943,6 +998,14 @@ function readSubagentSettings(filePath: string | null): SubagentSettings {
943
998
  throw new Error(`Subagent settings in '${filePath}' have invalid 'defaultModel'; expected a non-empty string.`);
944
999
  }
945
1000
  }
1001
+ let defaultProvider: string | undefined;
1002
+ if ("defaultProvider" in subagentsObject) {
1003
+ if (typeof subagentsObject.defaultProvider === "string" && subagentsObject.defaultProvider.trim()) {
1004
+ defaultProvider = subagentsObject.defaultProvider.trim();
1005
+ } else {
1006
+ throw new Error(`Subagent settings in '${filePath}' have invalid 'defaultProvider'; expected a non-empty string.`);
1007
+ }
1008
+ }
946
1009
  let defaultThinking: string | undefined;
947
1010
  if ("defaultThinking" in subagentsObject) {
948
1011
  if (typeof subagentsObject.defaultThinking === "string" && subagentsObject.defaultThinking.trim()) {
@@ -951,6 +1014,14 @@ function readSubagentSettings(filePath: string | null): SubagentSettings {
951
1014
  throw new Error(`Subagent settings in '${filePath}' have invalid 'defaultThinking'; expected a non-empty string.`);
952
1015
  }
953
1016
  }
1017
+ let maxThinking: ThinkingLevel | undefined;
1018
+ if ("maxThinking" in subagentsObject) {
1019
+ try {
1020
+ maxThinking = parseThinkingLevel(subagentsObject.maxThinking, `'${filePath}' subagents.maxThinking`);
1021
+ } catch (error) {
1022
+ 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 });
1023
+ }
1024
+ }
954
1025
  let defaultExtensions: string[] | undefined;
955
1026
  if ("defaultExtensions" in subagentsObject) {
956
1027
  if (!Array.isArray(subagentsObject.defaultExtensions)
@@ -966,7 +1037,9 @@ function readSubagentSettings(filePath: string | null): SubagentSettings {
966
1037
  const parsedSettings: SubagentSettings = {
967
1038
  overrides: parsed,
968
1039
  ...(defaultModel !== undefined ? { defaultModel } : {}),
1040
+ ...(defaultProvider !== undefined ? { defaultProvider } : {}),
969
1041
  ...(defaultThinking !== undefined ? { defaultThinking } : {}),
1042
+ ...(maxThinking !== undefined ? { maxThinking } : {}),
970
1043
  ...(defaultExtensions !== undefined ? { defaultExtensions } : {}),
971
1044
  ...(disableBuiltins !== undefined ? { disableBuiltins } : {}),
972
1045
  ...(disableThinking !== undefined ? { disableThinking } : {}),
@@ -982,25 +1055,39 @@ function readSubagentSettings(filePath: string | null): SubagentSettings {
982
1055
  return parsedSettings;
983
1056
  }
984
1057
 
1058
+ function resolveSubagentDefaultProvider(
1059
+ userSettings: SubagentSettings,
1060
+ projectSettings: SubagentSettings,
1061
+ projectSettingsPath: string | null,
1062
+ ): string | undefined {
1063
+ if (projectSettingsPath && projectSettings.defaultProvider !== undefined) return projectSettings.defaultProvider;
1064
+ return userSettings.defaultProvider;
1065
+ }
1066
+
985
1067
  function resolveSubagentDefaultModel(
986
1068
  userSettings: SubagentSettings,
987
1069
  projectSettings: SubagentSettings,
988
1070
  userSettingsPath: string,
989
1071
  projectSettingsPath: string | null,
1072
+ defaultProvider: string | undefined,
990
1073
  ): AgentModelSourceInfo | undefined {
991
1074
  if (projectSettingsPath && projectSettings.defaultModel !== undefined) {
992
- return { type: "subagents.defaultModel", scope: "project", path: projectSettingsPath, model: projectSettings.defaultModel };
1075
+ return { type: "subagents.defaultModel", scope: "project", path: projectSettingsPath, model: projectSettings.defaultModel, ...(defaultProvider ? { defaultProvider } : {}) };
993
1076
  }
994
1077
  return userSettings.defaultModel !== undefined
995
- ? { type: "subagents.defaultModel", scope: "user", path: userSettingsPath, model: userSettings.defaultModel }
1078
+ ? { type: "subagents.defaultModel", scope: "user", path: userSettingsPath, model: userSettings.defaultModel, ...(defaultProvider ? { defaultProvider } : {}) }
996
1079
  : undefined;
997
1080
  }
998
1081
 
999
- function applySubagentDefaultModel(agents: AgentConfig[], defaultModel: AgentModelSourceInfo | undefined): AgentConfig[] {
1000
- if (!defaultModel) return agents;
1082
+ function applySubagentDefaultModel(agents: AgentConfig[], defaultModel: AgentModelSourceInfo | undefined, defaultProvider: string | undefined): AgentConfig[] {
1083
+ if (!defaultModel && !defaultProvider) return agents;
1001
1084
  return agents.map((agent) => {
1002
- if (agent.model !== undefined) return agent;
1003
- const next = { ...agent, model: defaultModel.model, modelSource: defaultModel };
1085
+ if (agent.model !== undefined && (agent.modelProvider !== undefined || !defaultProvider)) return agent;
1086
+ const next = {
1087
+ ...agent,
1088
+ ...(agent.model === undefined && defaultModel ? { model: defaultModel.model, modelSource: defaultModel } : {}),
1089
+ ...(defaultProvider ? { modelProvider: defaultProvider } : {}),
1090
+ };
1004
1091
  const frontmatterFields = agentFrontmatterFields.get(agent);
1005
1092
  if (frontmatterFields) agentFrontmatterFields.set(next, frontmatterFields);
1006
1093
  return next;
@@ -1027,6 +1114,20 @@ function applySubagentDefaultThinking(agents: AgentConfig[], defaultThinking: st
1027
1114
  });
1028
1115
  }
1029
1116
 
1117
+ function resolveSubagentMaxThinking(
1118
+ userSettings: SubagentSettings,
1119
+ projectSettings: SubagentSettings,
1120
+ projectSettingsPath: string | null,
1121
+ ): ThinkingLevel | undefined {
1122
+ if (projectSettingsPath && projectSettings.maxThinking !== undefined) return projectSettings.maxThinking;
1123
+ return userSettings.maxThinking;
1124
+ }
1125
+
1126
+ function applySubagentMaxThinking(agents: AgentConfig[], maxThinking: ThinkingLevel | undefined): AgentConfig[] {
1127
+ if (maxThinking === undefined) return agents;
1128
+ return agents.map((agent) => agent.maxThinking === maxThinking ? agent : { ...agent, maxThinking });
1129
+ }
1130
+
1030
1131
  function resolveSubagentDefaultExtensions(
1031
1132
  userSettings: SubagentSettings,
1032
1133
  projectSettings: SubagentSettings,
@@ -1050,11 +1151,12 @@ function applySubagentDefaultExtensions(agents: AgentConfig[], defaultExtensions
1050
1151
  function applySubagentDefaults(
1051
1152
  agents: AgentConfig[],
1052
1153
  defaultModel: AgentModelSourceInfo | undefined,
1154
+ defaultProvider: string | undefined,
1053
1155
  defaultThinking: string | undefined,
1054
1156
  defaultExtensions: string[] | undefined,
1055
1157
  ): AgentConfig[] {
1056
1158
  return applySubagentDefaultExtensions(
1057
- applySubagentDefaultThinking(applySubagentDefaultModel(agents, defaultModel), defaultThinking),
1159
+ applySubagentDefaultThinking(applySubagentDefaultModel(agents, defaultModel, defaultProvider), defaultThinking),
1058
1160
  defaultExtensions,
1059
1161
  );
1060
1162
  }
@@ -1081,11 +1183,17 @@ function applyBuiltinOverride(
1081
1183
  };
1082
1184
 
1083
1185
  if (override.description !== undefined) next.description = override.description;
1186
+ if (override.output !== undefined) { if (override.output === false) delete next.output; else next.output = override.output; }
1084
1187
  if (override.outputMode !== undefined) next.outputMode = override.outputMode;
1188
+ if (override.defaultReads !== undefined) { if (override.defaultReads === false) delete next.defaultReads; else next.defaultReads = [...override.defaultReads]; }
1085
1189
  if (override.model !== undefined) {
1086
1190
  if (override.model === false) delete next.model; else next.model = override.model;
1087
1191
  delete next.modelSource;
1088
1192
  }
1193
+ if (override.defaultProvider !== undefined) {
1194
+ if (override.defaultProvider === false) delete next.modelProvider;
1195
+ else next.modelProvider = override.defaultProvider;
1196
+ }
1089
1197
  if (override.fallbackModels !== undefined) { if (override.fallbackModels === false) delete next.fallbackModels; else next.fallbackModels = [...override.fallbackModels]; }
1090
1198
  if (override.thinking !== undefined) { if (override.thinking === false) delete next.thinking; else next.thinking = override.thinking; }
1091
1199
  if (override.systemPromptMode !== undefined) next.systemPromptMode = override.systemPromptMode;
@@ -1199,15 +1307,24 @@ function applyCustomAgentOverride(
1199
1307
  mutable().description = override.description;
1200
1308
  anyFilled = true;
1201
1309
  }
1310
+ if (override.output !== undefined) {
1311
+ fill("output", ["output"], override.output === false ? undefined : override.output);
1312
+ }
1202
1313
  if (override.outputMode !== undefined) {
1203
1314
  fill("outputMode", ["outputMode"], override.outputMode);
1204
1315
  }
1316
+ if (override.defaultReads !== undefined) {
1317
+ fill("defaultReads", ["defaultReads"], override.defaultReads === false ? undefined : [...override.defaultReads]);
1318
+ }
1205
1319
  if (override.model !== undefined && !agentHasFrontmatterField(agent, "model")) {
1206
1320
  const target = mutable();
1207
1321
  if (override.model === false) delete target.model; else target.model = override.model;
1208
1322
  delete target.modelSource;
1209
1323
  anyFilled = true;
1210
1324
  }
1325
+ if (override.defaultProvider !== undefined) {
1326
+ fill("modelProvider", ["modelProvider", "defaultProvider"], override.defaultProvider === false ? undefined : override.defaultProvider);
1327
+ }
1211
1328
  if (override.fallbackModels !== undefined) {
1212
1329
  fill(
1213
1330
  "fallbackModels",
@@ -1233,7 +1350,8 @@ function applyCustomAgentOverride(
1233
1350
  if (override.acceptanceRole !== undefined) {
1234
1351
  fill("acceptanceRole", ["acceptanceRole"], override.acceptanceRole === false ? undefined : override.acceptanceRole);
1235
1352
  }
1236
- if (override.disabled !== undefined && agent.disabled === undefined) {
1353
+ if (override.disabled !== undefined) {
1354
+ // Custom agent files cannot set `disabled`, so project overrides replace user overrides.
1237
1355
  mutable().disabled = override.disabled;
1238
1356
  anyFilled = true;
1239
1357
  }
@@ -1262,7 +1380,7 @@ function applyCustomAgentOverride(
1262
1380
  }
1263
1381
 
1264
1382
  if (!anyFilled || !next) return agent;
1265
- next.override = { ...meta, base: cloneOverrideBase(agent) };
1383
+ next.override = { ...meta, base: agent.override?.base ?? cloneOverrideBase(agent) };
1266
1384
  const frontmatterFields = agentFrontmatterFields.get(agent);
1267
1385
  if (frontmatterFields) agentFrontmatterFields.set(next, frontmatterFields);
1268
1386
  return next;
@@ -1275,24 +1393,25 @@ function applyCustomAgentOverrides(
1275
1393
  userSettingsPath: string,
1276
1394
  projectSettingsPath: string | null,
1277
1395
  ): AgentConfig[] {
1396
+ // Apply user then project so project fields win without dropping user-only fields.
1278
1397
  return agents.map((agent) => {
1398
+ const userOverride = userSettings.overrides[agent.name];
1399
+ const withUserOverride = userOverride
1400
+ ? applyCustomAgentOverride(agent, userOverride, { scope: "user", path: userSettingsPath })
1401
+ : agent;
1402
+
1279
1403
  const projectOverride = projectSettings.overrides[agent.name];
1280
1404
  if (projectOverride && projectSettingsPath) {
1281
- return applyCustomAgentOverride(agent, projectOverride, { scope: "project", path: projectSettingsPath });
1282
- }
1283
-
1284
- const userOverride = userSettings.overrides[agent.name];
1285
- if (userOverride) {
1286
- return applyCustomAgentOverride(agent, userOverride, { scope: "user", path: userSettingsPath });
1405
+ return applyCustomAgentOverride(withUserOverride, projectOverride, { scope: "project", path: projectSettingsPath });
1287
1406
  }
1288
1407
 
1289
- return agent;
1408
+ return withUserOverride;
1290
1409
  });
1291
1410
  }
1292
1411
 
1293
1412
  export function buildBuiltinOverrideConfig(
1294
1413
  base: BuiltinAgentOverrideBase,
1295
- 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">>,
1414
+ draft: Pick<AgentConfig, "model" | "modelProvider" | "fallbackModels" | "thinking" | "systemPromptMode" | "inheritProjectContext" | "inheritSkills" | "defaultContext" | "acceptanceRole" | "disabled" | "systemPrompt" | "skills" | "tools" | "mcpDirectTools" | "extensions" | "subagentOnlyExtensions" | "completionGuard" | "toolBudget"> & Partial<Pick<AgentConfig, "description" | "output" | "outputMode" | "defaultReads">>,
1296
1415
  ): BuiltinAgentOverrideConfig | undefined {
1297
1416
  const override: BuiltinAgentOverrideConfig = {};
1298
1417
 
@@ -1300,8 +1419,11 @@ export function buildBuiltinOverrideConfig(
1300
1419
  const description = draft.description.trim();
1301
1420
  if (description && description !== base.description) override.description = description;
1302
1421
  }
1422
+ if (draft.output !== base.output) override.output = draft.output ?? false;
1303
1423
  if (draft.outputMode !== undefined && draft.outputMode !== base.outputMode) override.outputMode = draft.outputMode;
1424
+ if (!arraysEqual(draft.defaultReads, base.defaultReads)) override.defaultReads = draft.defaultReads ? [...draft.defaultReads] : false;
1304
1425
  if (draft.model !== base.model) override.model = draft.model ?? false;
1426
+ if (draft.modelProvider !== base.modelProvider) override.defaultProvider = draft.modelProvider ?? false;
1305
1427
  if (!arraysEqual(draft.fallbackModels, base.fallbackModels)) override.fallbackModels = draft.fallbackModels ? [...draft.fallbackModels] : false;
1306
1428
  if (draft.thinking !== base.thinking) override.thinking = draft.thinking ?? false;
1307
1429
  if (draft.systemPromptMode !== base.systemPromptMode) override.systemPromptMode = draft.systemPromptMode;
@@ -1615,7 +1737,7 @@ function resolveAgentRelativeExtensionPaths(paths: string[] | undefined, agentFi
1615
1737
  });
1616
1738
  }
1617
1739
 
1618
- function loadAgentsFromDefinitionFiles(files: AgentDefinitionFile[], source: AgentSource, discoveryPriority?: number): { agents: AgentConfig[]; diagnostics: AgentDiscoveryDiagnostic[] } {
1740
+ function loadAgentsFromDefinitionFiles(files: AgentDefinitionFile[], source: AgentSource, discoveryPriority?: number, packageSource?: Omit<PackageSubagentPath, "dir">): { agents: AgentConfig[]; diagnostics: AgentDiscoveryDiagnostic[] } {
1619
1741
  const agents: AgentConfig[] = [];
1620
1742
  const diagnostics: AgentDiscoveryDiagnostic[] = [];
1621
1743
 
@@ -1750,6 +1872,9 @@ function loadAgentsFromDefinitionFiles(files: AgentDefinitionFile[], source: Age
1750
1872
  ...(runner !== undefined ? { runner } : {}),
1751
1873
  localName,
1752
1874
  ...(packageName !== undefined ? { packageName } : {}),
1875
+ ...(packageSource?.packageName ? { packageSourceName: packageSource.packageName } : {}),
1876
+ ...(packageSource?.packageVersion ? { packageSourceVersion: packageSource.packageVersion } : {}),
1877
+ ...(packageSource?.packageRoot ? { packageSourceRoot: packageSource.packageRoot } : {}),
1753
1878
  description: frontmatter.description,
1754
1879
  ...(aliases !== undefined ? { aliases } : {}),
1755
1880
  ...(rawTools !== undefined ? { tools } : {}),
@@ -1797,8 +1922,8 @@ function loadAgentsFromDefinitionFiles(files: AgentDefinitionFile[], source: Age
1797
1922
  return { agents, diagnostics };
1798
1923
  }
1799
1924
 
1800
- function loadAgentsFromDir(dir: string, source: AgentSource, discoveryPriority?: number): { agents: AgentConfig[]; diagnostics: AgentDiscoveryDiagnostic[] } {
1801
- return loadAgentsFromDefinitionFiles(readAgentDefinitionFiles(dir), source, discoveryPriority);
1925
+ function loadAgentsFromDir(dir: string, source: AgentSource, discoveryPriority?: number, packageSource?: Omit<PackageSubagentPath, "dir">): { agents: AgentConfig[]; diagnostics: AgentDiscoveryDiagnostic[] } {
1926
+ return loadAgentsFromDefinitionFiles(readAgentDefinitionFiles(dir), source, discoveryPriority, packageSource);
1802
1927
  }
1803
1928
 
1804
1929
  function loadChainsFromDir(dir: string, source: AgentSource): { chains: ChainConfig[]; diagnostics: ChainDiscoveryDiagnostic[] } {
@@ -1888,8 +2013,10 @@ export function discoverAgents(cwd: string, scope: AgentScope): AgentDiscoveryRe
1888
2013
  const projectSettingsPath = getProjectAgentSettingsPath(cwd);
1889
2014
  const userSettings = scope === "project" ? EMPTY_SUBAGENT_SETTINGS : readSubagentSettings(userSettingsPath);
1890
2015
  const projectSettings = scope === "user" ? EMPTY_SUBAGENT_SETTINGS : readSubagentSettings(projectSettingsPath);
1891
- const defaultModel = resolveSubagentDefaultModel(userSettings, projectSettings, userSettingsPath, projectSettingsPath);
2016
+ const defaultProvider = resolveSubagentDefaultProvider(userSettings, projectSettings, projectSettingsPath);
2017
+ const defaultModel = resolveSubagentDefaultModel(userSettings, projectSettings, userSettingsPath, projectSettingsPath, defaultProvider);
1892
2018
  const defaultThinking = resolveSubagentDefaultThinking(userSettings, projectSettings, projectSettingsPath);
2019
+ const maxThinking = resolveSubagentMaxThinking(userSettings, projectSettings, projectSettingsPath);
1893
2020
  const defaultExtensions = resolveSubagentDefaultExtensions(userSettings, projectSettings, projectSettingsPath);
1894
2021
  const modelScope = projectSettings.modelScope ?? userSettings.modelScope;
1895
2022
  const packageSubagentPaths = collectPackageSubagentPaths(cwd, {
@@ -1899,7 +2026,7 @@ export function discoverAgents(cwd: string, scope: AgentScope): AgentDiscoveryRe
1899
2026
 
1900
2027
  const builtinLoaded = loadAgentsFromDefinitionFiles(BUILTIN_AGENT_DEFINITION_FILES, "builtin");
1901
2028
  const builtinAgents = applyBuiltinOverrides(
1902
- applySubagentDefaults(builtinLoaded.agents, defaultModel, defaultThinking, defaultExtensions),
2029
+ applySubagentDefaults(builtinLoaded.agents, defaultModel, defaultProvider, defaultThinking, defaultExtensions),
1903
2030
  userSettings,
1904
2031
  projectSettings,
1905
2032
  userSettingsPath,
@@ -1909,7 +2036,7 @@ export function discoverAgents(cwd: string, scope: AgentScope): AgentDiscoveryRe
1909
2036
  const userLoaded = scope === "project" ? [] : [...extraUserAgentDirs(), userDirOld, userDirNew]
1910
2037
  .map((dir, discoveryPriority) => loadAgentsFromDir(dir, "user", discoveryPriority));
1911
2038
  const userAgents = applyCustomAgentOverrides(
1912
- applySubagentDefaults(userLoaded.flatMap((loaded) => loaded.agents), defaultModel, defaultThinking, defaultExtensions),
2039
+ applySubagentDefaults(userLoaded.flatMap((loaded) => loaded.agents), defaultModel, defaultProvider, defaultThinking, defaultExtensions),
1913
2040
  userSettings,
1914
2041
  projectSettings,
1915
2042
  userSettingsPath,
@@ -1918,13 +2045,13 @@ export function discoverAgents(cwd: string, scope: AgentScope): AgentDiscoveryRe
1918
2045
 
1919
2046
  const projectLoaded = scope === "user" ? [] : projectAgentDirs.map((dir) => loadAgentsFromDir(dir, "project", dir === projectAgentsDir ? 1 : 0));
1920
2047
  const projectAgents = applyCustomAgentOverrides(
1921
- applySubagentDefaults(projectLoaded.flatMap((loaded) => loaded.agents), defaultModel, defaultThinking, defaultExtensions),
2048
+ applySubagentDefaults(projectLoaded.flatMap((loaded) => loaded.agents), defaultModel, defaultProvider, defaultThinking, defaultExtensions),
1922
2049
  userSettings,
1923
2050
  projectSettings,
1924
2051
  userSettingsPath,
1925
2052
  projectSettingsPath,
1926
2053
  );
1927
- const packageLoaded = packageSubagentPaths.agents.map((dir, index) => loadAgentsFromDir(dir, "package", packageSubagentPaths.agents.length - index));
2054
+ const packageLoaded = packageSubagentPaths.agents.map((entry, index) => loadAgentsFromDir(entry.dir, "package", packageSubagentPaths.agents.length - index, entry));
1928
2055
  const packageMap = new Map<string, AgentConfig>();
1929
2056
  for (const loaded of packageLoaded) {
1930
2057
  for (const agent of loaded.agents) {
@@ -1932,14 +2059,17 @@ export function discoverAgents(cwd: string, scope: AgentScope): AgentDiscoveryRe
1932
2059
  }
1933
2060
  }
1934
2061
  const packageAgents = applyCustomAgentOverrides(
1935
- applySubagentDefaults(Array.from(packageMap.values()), defaultModel, defaultThinking, defaultExtensions),
2062
+ applySubagentDefaults(Array.from(packageMap.values()), defaultModel, defaultProvider, defaultThinking, defaultExtensions),
1936
2063
  userSettings,
1937
2064
  projectSettings,
1938
2065
  userSettingsPath,
1939
2066
  projectSettingsPath,
1940
2067
  );
1941
- const agents = mergeAgentsForScope(scope, userAgents, projectAgents, builtinAgents, packageAgents)
1942
- .filter((agent) => agent.disabled !== true);
2068
+ const agents = applySubagentMaxThinking(
2069
+ mergeAgentsForScope(scope, userAgents, projectAgents, builtinAgents, packageAgents)
2070
+ .filter((agent) => agent.disabled !== true),
2071
+ maxThinking,
2072
+ );
1943
2073
 
1944
2074
  const agentDiagnostics = [
1945
2075
  ...builtinLoaded.diagnostics,
@@ -1947,7 +2077,7 @@ export function discoverAgents(cwd: string, scope: AgentScope): AgentDiscoveryRe
1947
2077
  ...projectLoaded.flatMap((loaded) => loaded.diagnostics),
1948
2078
  ...packageLoaded.flatMap((loaded) => loaded.diagnostics),
1949
2079
  ];
1950
- return { agents, agentDiagnostics, projectAgentsDir, ...(modelScope !== undefined ? { modelScope } : {}) };
2080
+ return { agents, agentDiagnostics, projectAgentsDir, ...(modelScope !== undefined ? { modelScope } : {}), ...(maxThinking !== undefined ? { maxThinking } : {}) };
1951
2081
  }
1952
2082
 
1953
2083
  export function discoverAgentsAll(cwd: string): {
@@ -1964,6 +2094,7 @@ export function discoverAgentsAll(cwd: string): {
1964
2094
  projectChainDir: string | null;
1965
2095
  userSettingsPath: string;
1966
2096
  projectSettingsPath: string | null;
2097
+ maxThinking?: ThinkingLevel;
1967
2098
  } {
1968
2099
  const userDirOld = path.join(getAgentDir(), "agents");
1969
2100
  const userDirNew = path.join(os.homedir(), ".agents");
@@ -1974,14 +2105,16 @@ export function discoverAgentsAll(cwd: string): {
1974
2105
  const projectSettingsPath = getProjectAgentSettingsPath(cwd);
1975
2106
  const userSettings = readSubagentSettings(userSettingsPath);
1976
2107
  const projectSettings = readSubagentSettings(projectSettingsPath);
1977
- const defaultModel = resolveSubagentDefaultModel(userSettings, projectSettings, userSettingsPath, projectSettingsPath);
2108
+ const defaultProvider = resolveSubagentDefaultProvider(userSettings, projectSettings, projectSettingsPath);
2109
+ const defaultModel = resolveSubagentDefaultModel(userSettings, projectSettings, userSettingsPath, projectSettingsPath, defaultProvider);
1978
2110
  const defaultThinking = resolveSubagentDefaultThinking(userSettings, projectSettings, projectSettingsPath);
2111
+ const maxThinking = resolveSubagentMaxThinking(userSettings, projectSettings, projectSettingsPath);
1979
2112
  const defaultExtensions = resolveSubagentDefaultExtensions(userSettings, projectSettings, projectSettingsPath);
1980
2113
  const packageSubagentPaths = collectPackageSubagentPaths(cwd);
1981
2114
 
1982
2115
  const builtinLoaded = loadAgentsFromDefinitionFiles(BUILTIN_AGENT_DEFINITION_FILES, "builtin");
1983
2116
  const builtin = applyBuiltinOverrides(
1984
- applySubagentDefaults(builtinLoaded.agents, defaultModel, defaultThinking, defaultExtensions),
2117
+ applySubagentDefaults(builtinLoaded.agents, defaultModel, defaultProvider, defaultThinking, defaultExtensions),
1985
2118
  userSettings,
1986
2119
  projectSettings,
1987
2120
  userSettingsPath,
@@ -1990,7 +2123,7 @@ export function discoverAgentsAll(cwd: string): {
1990
2123
  const userLoaded = [...extraUserAgentDirs(), userDirOld, userDirNew]
1991
2124
  .map((dir, discoveryPriority) => loadAgentsFromDir(dir, "user", discoveryPriority));
1992
2125
  const user = applyCustomAgentOverrides(
1993
- applySubagentDefaults(userLoaded.flatMap((loaded) => loaded.agents), defaultModel, defaultThinking, defaultExtensions),
2126
+ applySubagentDefaults(userLoaded.flatMap((loaded) => loaded.agents), defaultModel, defaultProvider, defaultThinking, defaultExtensions),
1994
2127
  userSettings,
1995
2128
  projectSettings,
1996
2129
  userSettingsPath,
@@ -1998,15 +2131,15 @@ export function discoverAgentsAll(cwd: string): {
1998
2131
  );
1999
2132
  const packageMap = new Map<string, AgentConfig>();
2000
2133
  const packageAgentDiagnostics: AgentDiscoveryDiagnostic[] = [];
2001
- for (const [index, dir] of packageSubagentPaths.agents.entries()) {
2002
- const loaded = loadAgentsFromDir(dir, "package", packageSubagentPaths.agents.length - index);
2134
+ for (const [index, entry] of packageSubagentPaths.agents.entries()) {
2135
+ const loaded = loadAgentsFromDir(entry.dir, "package", packageSubagentPaths.agents.length - index, entry);
2003
2136
  packageAgentDiagnostics.push(...loaded.diagnostics);
2004
2137
  for (const agent of loaded.agents) {
2005
2138
  if (!packageMap.has(agent.name)) packageMap.set(agent.name, agent);
2006
2139
  }
2007
2140
  }
2008
2141
  const packageAgents = applyCustomAgentOverrides(
2009
- applySubagentDefaults(Array.from(packageMap.values()), defaultModel, defaultThinking, defaultExtensions),
2142
+ applySubagentDefaults(Array.from(packageMap.values()), defaultModel, defaultProvider, defaultThinking, defaultExtensions),
2010
2143
  userSettings,
2011
2144
  projectSettings,
2012
2145
  userSettingsPath,
@@ -2022,7 +2155,7 @@ export function discoverAgentsAll(cwd: string): {
2022
2155
  }
2023
2156
  }
2024
2157
  const project = applyCustomAgentOverrides(
2025
- applySubagentDefaults(Array.from(projectMap.values()), defaultModel, defaultThinking, defaultExtensions),
2158
+ applySubagentDefaults(Array.from(projectMap.values()), defaultModel, defaultProvider, defaultThinking, defaultExtensions),
2026
2159
  userSettings,
2027
2160
  projectSettings,
2028
2161
  userSettingsPath,
@@ -2067,5 +2200,20 @@ export function discoverAgentsAll(cwd: string): {
2067
2200
 
2068
2201
  const userDir = process.env.PI_CODING_AGENT_DIR ? userDirOld : fs.existsSync(userDirNew) ? userDirNew : userDirOld;
2069
2202
 
2070
- return { builtin, package: packageAgents, user, project, agentDiagnostics, chains, chainDiagnostics, userDir, projectDir, userChainDir, projectChainDir, userSettingsPath, projectSettingsPath };
2203
+ return {
2204
+ builtin: applySubagentMaxThinking(builtin, maxThinking),
2205
+ package: applySubagentMaxThinking(packageAgents, maxThinking),
2206
+ user: applySubagentMaxThinking(user, maxThinking),
2207
+ project: applySubagentMaxThinking(project, maxThinking),
2208
+ agentDiagnostics,
2209
+ chains,
2210
+ chainDiagnostics,
2211
+ userDir,
2212
+ projectDir,
2213
+ userChainDir,
2214
+ projectChainDir,
2215
+ userSettingsPath,
2216
+ projectSettingsPath,
2217
+ ...(maxThinking !== undefined ? { maxThinking } : {}),
2218
+ };
2071
2219
  }
@@ -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;