pi-subagents 0.58.0 → 0.60.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 (116) hide show
  1. package/CHANGELOG.md +102 -0
  2. package/docs/agents.md +5 -3
  3. package/docs/configuration.md +4 -4
  4. package/docs/extension-api.md +1 -1
  5. package/docs/models.md +24 -1
  6. package/docs/observability.md +1 -1
  7. package/docs/tool-reference.md +93 -5
  8. package/docs/workflows.md +94 -3
  9. package/package.json +3 -1
  10. package/prompts/review-loop.md +2 -2
  11. package/skills/council-mode/SKILL.md +48 -243
  12. package/skills/council-mode/references/pass-contracts.md +150 -0
  13. package/skills/pi-subagents/SKILL.md +87 -37
  14. package/skills/pi-subagents/references/constraints-and-recipes.md +29 -233
  15. package/skills/pi-subagents/references/execution-controls.md +52 -7
  16. package/skills/pi-subagents/references/management-authoring-rpc.md +3 -4
  17. package/skills/pi-subagents/references/multi-lane-orchestration.md +13 -1
  18. package/skills/pi-subagents/references/prompting-and-roles.md +54 -26
  19. package/skills/pi-subagents/references/review-and-validation.md +73 -0
  20. package/src/agents/agent-management.ts +159 -42
  21. package/src/agents/agent-serializer.ts +4 -2
  22. package/src/agents/agents.ts +67 -26
  23. package/src/agents/runtime-agent-registry.ts +9 -16
  24. package/src/api/background-work.ts +5 -1
  25. package/src/api/delegation.ts +0 -7
  26. package/src/api/preflight.ts +6 -9
  27. package/src/api/shared-types.ts +2 -0
  28. package/src/extension/fanout-child.ts +5 -3
  29. package/src/extension/index.ts +51 -23
  30. package/src/extension/public-execution.ts +16 -2
  31. package/src/extension/schemas.ts +36 -10
  32. package/src/extension/tool-description.ts +24 -5
  33. package/src/intercom/result-intercom.ts +2 -0
  34. package/src/profiles/profiles.ts +5 -6
  35. package/src/runs/background/active-async-capacity.ts +2 -2
  36. package/src/runs/background/async-execution.ts +52 -37
  37. package/src/runs/background/async-job-tracker.ts +17 -12
  38. package/src/runs/background/async-resume.ts +23 -25
  39. package/src/runs/background/async-status-snapshot.ts +23 -261
  40. package/src/runs/background/async-status.ts +110 -8
  41. package/src/runs/background/chain-append.ts +6 -3
  42. package/src/runs/background/chain-root-attachment.ts +60 -8
  43. package/src/runs/background/control-channel.ts +3 -2
  44. package/src/runs/background/fleet-view.ts +21 -11
  45. package/src/runs/background/notify.ts +158 -6
  46. package/src/runs/background/result-files.ts +2 -1
  47. package/src/runs/background/result-watcher.ts +2 -0
  48. package/src/runs/background/resume-guidance.ts +1 -1
  49. package/src/runs/background/retained-children.ts +1 -1
  50. package/src/runs/background/run-status.ts +30 -9
  51. package/src/runs/background/scheduled-runs.ts +86 -7
  52. package/src/runs/background/stale-run-reconciler.ts +10 -4
  53. package/src/runs/background/steering.ts +4 -14
  54. package/src/runs/background/subagent-runner.ts +351 -358
  55. package/src/runs/background/subagent-wait.ts +68 -12
  56. package/src/runs/background/terminal-run-index.ts +1 -1
  57. package/src/runs/background/wait-completions.ts +25 -1
  58. package/src/runs/background/wait-config.ts +23 -9
  59. package/src/runs/background/wait-tool.ts +9 -2
  60. package/src/runs/foreground/async-steering-action.ts +2 -2
  61. package/src/runs/foreground/execution.ts +125 -122
  62. package/src/runs/foreground/foreground-control.ts +3 -0
  63. package/src/runs/foreground/foreground-history.ts +1 -0
  64. package/src/runs/foreground/subagent-executor.ts +576 -240
  65. package/src/runs/foreground/workflow-detach-reconcile.ts +99 -200
  66. package/src/runs/shared/abort-recovery.ts +119 -0
  67. package/src/runs/shared/async-status-projection.ts +597 -0
  68. package/src/runs/shared/background-process-options.ts +9 -0
  69. package/src/runs/shared/child-identity.ts +19 -4
  70. package/src/runs/shared/child-launch-plan.ts +151 -0
  71. package/src/runs/shared/completion-evidence.ts +89 -0
  72. package/src/runs/shared/completion-guard.ts +1 -1
  73. package/src/runs/shared/dynamic-fanout.ts +2 -2
  74. package/src/runs/shared/host-step-status.ts +230 -0
  75. package/src/runs/shared/lane-metadata.ts +105 -0
  76. package/src/runs/shared/mcp-config-sources.ts +42 -6
  77. package/src/runs/shared/mcp-direct-tool-allowlist.ts +93 -143
  78. package/src/runs/shared/mcp-direct-tool-grant.ts +194 -0
  79. package/src/runs/shared/model-fallback.ts +9 -2
  80. package/src/runs/shared/mutation-evidence.ts +52 -3
  81. package/src/runs/shared/nested-events.ts +6 -2
  82. package/src/runs/shared/nested-render.ts +7 -3
  83. package/src/runs/shared/parallel-handoff.ts +419 -7
  84. package/src/runs/shared/parallel-utils.ts +7 -0
  85. package/src/runs/shared/pi-args.ts +67 -3
  86. package/src/runs/shared/single-output.ts +72 -22
  87. package/src/runs/shared/subagent-prompt-runtime.ts +33 -6
  88. package/src/runs/shared/workflow-graph.ts +15 -0
  89. package/src/runs/shared/worktree-cleanup-plan.ts +847 -0
  90. package/src/runs/shared/worktree.ts +18 -0
  91. package/src/shared/child-session-name.ts +46 -0
  92. package/src/shared/extension-context.ts +24 -0
  93. package/src/shared/formatters.ts +5 -2
  94. package/src/shared/launch-contract.ts +1 -1
  95. package/src/shared/settings.ts +9 -103
  96. package/src/shared/types.ts +232 -30
  97. package/src/shared/utils.ts +35 -55
  98. package/src/slash/delegation-adapters.ts +1 -8
  99. package/src/slash/delegation-request.ts +0 -4
  100. package/src/slash/slash-bridge.ts +1 -2
  101. package/src/slash/slash-commands.ts +369 -90
  102. package/src/slash/slash-live-state.ts +22 -11
  103. package/src/tui/fleet-status.ts +134 -75
  104. package/src/tui/fleet.ts +11 -5
  105. package/src/tui/render-helpers.ts +31 -0
  106. package/src/tui/render.ts +895 -121
  107. package/src/watchdog/change-signature.ts +40 -1
  108. package/src/watchdog/turn-delta.ts +1 -1
  109. package/src/workflows/chat-progress.ts +6 -3
  110. package/src/workflows/host-command.ts +235 -0
  111. package/src/workflows/scripted-workflow.ts +503 -38
  112. package/src/workflows/workflow-child-summary.ts +9 -5
  113. package/src/workflows/workflow-preflight.ts +270 -0
  114. package/src/workflows/workflow-receipt.ts +43 -4
  115. package/src/workflows/workflow-settlement.ts +246 -0
  116. package/src/runs/shared/turn-budget.ts +0 -98
@@ -31,11 +31,11 @@ import { parseFrontmatter, parseFrontmatterList } from "./frontmatter.ts";
31
31
  import { toModelInfo } from "../shared/model-info.ts";
32
32
  import { resolveSubagentModelOverride, type ParentModel } from "../runs/shared/model-fallback.ts";
33
33
  import { validateToolBudgetConfig } from "../runs/shared/tool-budget.ts";
34
- import { resolveTurnBudgetConfig } from "../runs/shared/turn-budget.ts";
35
34
  import { validateAcceptanceInput } from "../runs/shared/acceptance.ts";
36
- import { CODE_OWNED_EXTERNAL_CLI_ADAPTER_LABEL, isCodeOwnedExternalCliAdapterId, validateCodeOwnedProfileRunner } from "../runs/shared/external-cli-contract.ts";
37
- import type { AcceptanceInput, Details, ExtensionConfig, ToolBudgetConfig } from "../shared/types.ts";
35
+ import { CODE_OWNED_EXTERNAL_CLI_ADAPTER_LABEL, isCodeOwnedExternalCliAdapterId, resolveExternalCliRunnerStatus, validateCodeOwnedProfileRunner } from "../runs/shared/external-cli-contract.ts";
36
+ import type { AcceptanceInput, AgentCapabilitiesSnapshot, AgentCapabilityRow, Details, ExtensionConfig, ToolBudgetConfig } from "../shared/types.ts";
38
37
  import { getProjectConfigDir } from "../shared/utils.ts";
38
+ import { previewDisplayText } from "../shared/display-text.ts";
39
39
  import { capabilityCeilingAgentRestrictionSources, isAgentAllowedByCapabilityCeiling, resolveCurrentSubagentCapabilityCeiling } from "../runs/shared/capability-ceiling.ts";
40
40
  import { listRuntimeAgentConfigs, mergeRuntimeAgents, type RuntimeAgentOwner } from "./runtime-agent-registry.ts";
41
41
  import { listExternalJobProviders } from "../api/external-job-provider.ts";
@@ -48,11 +48,21 @@ interface ManagementParams {
48
48
  action?: string;
49
49
  agent?: string;
50
50
  agentScope?: unknown;
51
+ capabilities?: unknown;
51
52
  config?: unknown;
52
53
  }
53
54
 
54
- function result(text: string, isError = false): AgentToolResult<Details> {
55
- return { content: [{ type: "text", text }], isError, details: { mode: "management", results: [] } };
55
+ function result(text: string, isError = false, details?: Partial<Details>): AgentToolResult<Details> {
56
+ return { content: [{ type: "text", text }], isError, details: { mode: "management", results: [], ...details } };
57
+ }
58
+
59
+ function jsonDetails<T>(value: T): T {
60
+ return JSON.parse(JSON.stringify(value)) as T;
61
+ }
62
+
63
+ function presentDetails<T extends Record<string, unknown>>(value: T): T | undefined {
64
+ const cleaned = jsonDetails(value);
65
+ return Object.keys(cleaned).length > 0 ? cleaned : undefined;
56
66
  }
57
67
 
58
68
  function parseCsv(value: string): string[] {
@@ -331,7 +341,6 @@ export function preservedAgentFrontmatterFields(agent: AgentConfig, cfg: Record<
331
341
  if (hasKey(cfg, "defaultContext")) changed("defaultContext");
332
342
  if (hasKey(cfg, "async")) changed("async");
333
343
  if (hasKey(cfg, "timeoutMs")) changed("timeoutMs");
334
- if (hasKey(cfg, "turnBudget")) changed("turnBudget");
335
344
  if (hasKey(cfg, "acceptance")) changed("acceptance");
336
345
  if (hasKey(cfg, "acceptanceRole")) changed("acceptanceRole");
337
346
  if (hasKey(cfg, "output")) changed("output");
@@ -511,15 +520,6 @@ function applyAgentConfig(target: AgentConfig, cfg: Record<string, unknown>): st
511
520
  else if (typeof cfg.timeoutMs === "number" && Number.isInteger(cfg.timeoutMs) && cfg.timeoutMs > 0) target.defaultTimeoutMs = cfg.timeoutMs;
512
521
  else return "config.timeoutMs must be a positive integer or false when provided.";
513
522
  }
514
- if (hasKey(cfg, "turnBudget")) {
515
- if (cfg.turnBudget === false || cfg.turnBudget === "") delete target.defaultTurnBudget;
516
- else {
517
- const resolved = resolveTurnBudgetConfig(cfg.turnBudget, "config.turnBudget");
518
- if (resolved.error) return resolved.error;
519
- if (resolved.turnBudget !== undefined) target.defaultTurnBudget = resolved.turnBudget;
520
- else delete target.defaultTurnBudget;
521
- }
522
- }
523
523
  if (hasKey(cfg, "acceptance")) {
524
524
  if (cfg.acceptance === "") delete target.defaultAcceptance;
525
525
  else {
@@ -656,18 +656,135 @@ function runnerListBadge(agent: AgentConfig, providerNames: Set<string> | undefi
656
656
  return undefined;
657
657
  }
658
658
 
659
- function formatAgentListLine(agent: AgentConfig, providerNames: Set<string> | undefined): string {
659
+ function agentListMetadata(agent: AgentConfig, providerNames: Set<string> | undefined): string {
660
660
  const source = agent.source === "package" ? packageSourceLabel(agent) : agent.source;
661
- const parts = [
661
+ return [
662
662
  source,
663
663
  runnerListBadge(agent, providerNames),
664
664
  agent.defaultContext ? `context: ${agent.defaultContext}` : undefined,
665
665
  agent.aliases?.length ? `aliases: ${agent.aliases.join(", ")}` : undefined,
666
- ].filter((part): part is string => Boolean(part));
667
- return `- ${agent.name} (${parts.join(", ")}): ${agent.description}`;
666
+ ].filter((part): part is string => Boolean(part)).join(", ");
667
+ }
668
+
669
+ function formatAgentListLine(agent: AgentConfig, providerNames: Set<string> | undefined): string {
670
+ return `- ${agent.name} (${agentListMetadata(agent, providerNames)}): ${agent.description}`;
671
+ }
672
+
673
+ function formatAgentCapabilitiesLine(agent: AgentConfig, providerNames: Set<string> | undefined): string {
674
+ const declaredTools = [
675
+ ...(agent.tools ?? []),
676
+ ...(agent.mcpDirectTools ?? []).map((tool) => `mcp:${tool}`),
677
+ ];
678
+ let tools = "none";
679
+ if (agent.tools === undefined && agent.mcpDirectTools === undefined) {
680
+ tools = "default/ambient";
681
+ } else if (declaredTools.length > 0) {
682
+ tools = declaredTools.join(", ");
683
+ }
684
+ let model = "inherits current session";
685
+ if (agent.model !== undefined) {
686
+ model = agent.model;
687
+ if (agent.modelProvider && !agent.model.includes("/")) model = `${agent.modelProvider}/${agent.model}`;
688
+ }
689
+ const thinking = agent.thinking === false ? "off" : agent.thinking ?? "default";
690
+ return `- ${agent.name} (${agentListMetadata(agent, providerNames)}): Description: ${previewDisplayText(agent.description, 240)}; Tools: ${tools}; Model: ${model}; Thinking: ${thinking}`;
691
+ }
692
+
693
+ const EXTERNAL_JOB_CAPABILITIES = { stop: false, steer: false, resume: false, structuredOutput: false, toolEvents: false } as const;
694
+ const PI_AGENT_RUNNER = { type: "pi" } as const;
695
+
696
+ function listOrEmpty<T>(values: T[] | undefined): T[] {
697
+ return values ?? [];
698
+ }
699
+
700
+ function agentCapabilityRunner(agent: AgentConfig, providerNames: Set<string> | undefined): AgentCapabilityRow["runner"] {
701
+ const runner = agent.runner;
702
+ if (!runner || runner.type === "pi") return PI_AGENT_RUNNER;
703
+ if (runner.type === "external-cli") return { type: "external-cli", adapter: runner.adapter, capabilities: resolveExternalCliRunnerStatus(runner).capabilities };
704
+ return { type: "external-job", provider: runner.provider, available: providerNames?.has(runner.provider), capabilities: EXTERNAL_JOB_CAPABILITIES };
705
+ }
706
+
707
+ function agentCapabilityTools(agent: AgentConfig): AgentCapabilityRow["tools"] {
708
+ return {
709
+ ambient: agent.tools === undefined && agent.mcpDirectTools === undefined,
710
+ names: listOrEmpty(agent.tools),
711
+ mcpDirectTools: listOrEmpty(agent.mcpDirectTools),
712
+ mutationTools: agent.mutationTools,
713
+ };
714
+ }
715
+
716
+ function agentCapabilityRow(agent: AgentConfig, options: { executable: boolean; providerNames?: Set<string>; restrictionSources?: string[] }): AgentCapabilityRow {
717
+ return {
718
+ name: agent.name,
719
+ description: previewDisplayText(agent.description, 1000),
720
+ source: agent.source,
721
+ executable: options.executable,
722
+ restrictionSources: options.executable ? undefined : options.restrictionSources ?? [],
723
+ aliases: agent.aliases ? [...agent.aliases] : undefined,
724
+ runner: agentCapabilityRunner(agent, options.providerNames),
725
+ tools: agentCapabilityTools(agent),
726
+ model: presentDetails({ value: agent.model, fallbackModels: agent.fallbackModels, thinking: agent.thinking }),
727
+ execution: presentDetails({ defaultAsync: agent.defaultAsync, timeoutMs: agent.defaultTimeoutMs }),
728
+ output: presentDetails({ path: agent.output, mode: agent.outputMode }),
729
+ extensions: presentDetails({ names: agent.extensions, subagentOnly: agent.subagentOnlyExtensions, skills: agent.skills }),
730
+ };
731
+ }
732
+
733
+ function agentCapabilitiesSnapshot(input: { agents: AgentConfig[]; restrictedAgents: AgentConfig[]; providerNames?: Set<string>; restrictedSources?: string[] }): AgentCapabilitiesSnapshot {
734
+ return {
735
+ agents: [
736
+ ...input.agents.map((agent) => agentCapabilityRow(agent, { executable: true, providerNames: input.providerNames })),
737
+ ...input.restrictedAgents.map((agent) => agentCapabilityRow(agent, { executable: false, providerNames: input.providerNames, restrictionSources: input.restrictedSources })),
738
+ ],
739
+ restrictedCount: input.restrictedAgents.length,
740
+ ...(input.restrictedSources?.length ? { capabilityCeilingSources: [...input.restrictedSources] } : {}),
741
+ };
742
+ }
743
+
744
+ function providerNames(status: ExternalJobProviderStatus): Set<string> | undefined {
745
+ return status.ok ? status.names : undefined;
746
+ }
747
+
748
+ function appendRestrictedAgentLines(input: { lines: string[]; agents: AgentConfig[]; sources?: string[]; providerNames?: Set<string>; formatLine: (agent: AgentConfig, providerNames: Set<string> | undefined) => string }): void {
749
+ if (input.agents.length === 0) return;
750
+ input.lines.push(
751
+ "",
752
+ `Restricted agents (not executable in this session${input.sources?.length ? `; capability ceiling: ${input.sources.join(", ")}` : ""}):`,
753
+ ...input.agents.map((agent) => input.formatLine(agent, input.providerNames)),
754
+ );
755
+ }
756
+
757
+ function appendExternalJobRegistryLine(lines: string[], agents: AgentConfig[], status: ExternalJobProviderStatus): void {
758
+ if (status.ok || !agents.some((agent) => agent.runner?.type === "external-job")) return;
759
+ lines.push("", `External-job provider registry unavailable: ${status.error}`);
668
760
  }
669
761
 
670
- function formatAgentListSections(agents: AgentConfig[], providerNames: Set<string> | undefined): string[] {
762
+ function appendAgentDiagnosticLines(lines: string[], diagnostics: AgentDiscoveryDiagnostic[] | undefined): void {
763
+ if (!diagnostics?.length) return;
764
+ lines.push(
765
+ "",
766
+ "Invalid agent definitions:",
767
+ ...diagnostics.map((diagnostic) => `- ${diagnostic.name ?? diagnostic.filePath} (${diagnostic.source}): ${diagnostic.error}`),
768
+ );
769
+ }
770
+
771
+ function agentCapabilityDetails(input: { capabilityMode: boolean; agents: AgentConfig[]; restrictedAgents: AgentConfig[]; providerNames?: Set<string>; restrictedSources?: string[] }): Partial<Details> | undefined {
772
+ if (!input.capabilityMode) return undefined;
773
+ return {
774
+ agentCapabilities: jsonDetails(agentCapabilitiesSnapshot({
775
+ agents: input.agents,
776
+ restrictedAgents: input.restrictedAgents,
777
+ providerNames: input.providerNames,
778
+ restrictedSources: input.restrictedSources,
779
+ })),
780
+ };
781
+ }
782
+
783
+ function formatAgentListSections(
784
+ agents: AgentConfig[],
785
+ providerNames: Set<string> | undefined,
786
+ formatLine: (agent: AgentConfig, providerNames: Set<string> | undefined) => string = formatAgentListLine,
787
+ ): string[] {
671
788
  if (agents.length === 0) return ["- (none)"];
672
789
  const sections: Array<[AgentSource, string]> = [
673
790
  ["package", "Package agents"],
@@ -681,7 +798,7 @@ function formatAgentListSections(agents: AgentConfig[], providerNames: Set<strin
681
798
  const matches = agents.filter((agent) => agent.source === source);
682
799
  if (matches.length === 0) continue;
683
800
  if (lines.length > 0) lines.push("");
684
- lines.push(label, ...matches.map((agent) => formatAgentListLine(agent, providerNames)));
801
+ lines.push(label, ...matches.map((agent) => formatLine(agent, providerNames)));
685
802
  }
686
803
  return lines;
687
804
  }
@@ -724,7 +841,6 @@ function formatAgentDetail(agent: AgentConfig): string {
724
841
  if (agent.defaultContext) lines.push(`Default context: ${agent.defaultContext}`);
725
842
  if (agent.defaultAsync !== undefined) lines.push(`Async: ${agent.defaultAsync ? "true" : "false"}`);
726
843
  if (agent.defaultTimeoutMs !== undefined) lines.push(`Timeout: ${agent.defaultTimeoutMs}ms`);
727
- if (agent.defaultTurnBudget) lines.push(`Turn budget: ${JSON.stringify(agent.defaultTurnBudget)}`);
728
844
  if (agent.defaultAcceptance !== undefined) lines.push(`Acceptance: ${typeof agent.defaultAcceptance === "object" ? JSON.stringify(agent.defaultAcceptance) : String(agent.defaultAcceptance)}`);
729
845
  if (agent.acceptanceRole) lines.push(`Acceptance role: ${agent.acceptanceRole}`);
730
846
  if (agent.source === "builtin") lines.push(`Disabled: ${agent.disabled ? "true" : "false"}`);
@@ -746,7 +862,7 @@ function formatAgentDetail(agent: AgentConfig): string {
746
862
 
747
863
  export function handleList(params: ManagementParams, ctx: ManagementContext): AgentToolResult<Details> {
748
864
  const scope = normalizeListScope(params.agentScope) ?? "both";
749
- const d = discoverAgentsAll(ctx.cwd);
865
+ const d = discoverAgentsAll(ctx.cwd, ctx.model?.provider);
750
866
  let scopedAgents = mergeAgentsForScope(scope, d.user, d.project, d.builtin, d.package);
751
867
  if (ctx.runtimeAgentOwner && listRuntimeAgentConfigs(ctx.runtimeAgentOwner).length > 0) {
752
868
  const configuredAgents: AgentConfig[] = [
@@ -770,23 +886,24 @@ export function handleList(params: ManagementParams, ctx: ManagementContext): Ag
770
886
  discoverAvailableSkills: () => discoverAvailableSkills(ctx.cwd),
771
887
  });
772
888
  const providerStatus = registeredExternalJobProviderStatus();
889
+ const providerNameSet = providerNames(providerStatus);
890
+ const capabilityMode = params.capabilities === true;
891
+ const formatLine = capabilityMode ? formatAgentCapabilitiesLine : formatAgentListLine;
773
892
  const lines = [
774
- "Executable agents:",
775
- ...formatAgentListSections(agents, providerStatus.ok ? providerStatus.names : undefined),
776
- ...(restrictedAgents.length ? [
777
- "",
778
- `Restricted agents (not executable in this session${restrictedSources?.length ? `; capability ceiling: ${restrictedSources.join(", ")}` : ""}):`,
779
- ...restrictedAgents.map((a) => formatAgentListLine(a, providerStatus.ok ? providerStatus.names : undefined)),
780
- ] : []),
781
- ...(!providerStatus.ok && [...agents, ...restrictedAgents].some((agent) => agent.runner?.type === "external-job") ? ["", `External-job provider registry unavailable: ${providerStatus.error}`] : []),
782
- ...(d.agentDiagnostics?.length ? [
783
- "",
784
- "Invalid agent definitions:",
785
- ...d.agentDiagnostics.map((diagnostic) => `- ${diagnostic.name ?? diagnostic.filePath} (${diagnostic.source}): ${diagnostic.error}`),
786
- ] : []),
787
- ...(proactiveSuggestions.length ? ["", ...proactiveSuggestions] : []),
893
+ capabilityMode ? "Executable agents (capabilities):" : "Executable agents:",
894
+ ...formatAgentListSections(agents, providerNameSet, formatLine),
788
895
  ];
789
- return result(lines.join("\n"));
896
+ appendRestrictedAgentLines({ lines, agents: restrictedAgents, sources: restrictedSources, providerNames: providerNameSet, formatLine });
897
+ appendExternalJobRegistryLine(lines, [...agents, ...restrictedAgents], providerStatus);
898
+ appendAgentDiagnosticLines(lines, d.agentDiagnostics);
899
+ if (proactiveSuggestions.length) lines.push("", ...proactiveSuggestions);
900
+ return result(lines.join("\n"), false, agentCapabilityDetails({
901
+ capabilityMode,
902
+ agents,
903
+ restrictedAgents,
904
+ providerNames: providerNameSet,
905
+ restrictedSources,
906
+ }));
790
907
  }
791
908
 
792
909
  function formatModelSource(agent: AgentConfig, currentModel: ParentModel | undefined): string {
@@ -807,7 +924,7 @@ function handleModels(params: ManagementParams, ctx: ManagementContext): AgentTo
807
924
  return result(`Builtin agent '${requestedAgent}' not found. Available: ${BUILTIN_AGENT_NAMES.join(", ")}.`, true);
808
925
  }
809
926
 
810
- const discovered = discoverAgentsAll(ctx.cwd);
927
+ const discovered = discoverAgentsAll(ctx.cwd, ctx.model?.provider);
811
928
  const builtinByName = new Map(discovered.builtin.map((agent) => [agent.name, agent]));
812
929
  const resolveBuiltinModelAgent = (name: string): AgentConfig | undefined => builtinByName.get(name) ?? resolveAgentName(name, discovered.builtin).agent;
813
930
  const availableModels = ctx.modelRegistry.getAvailable().map(toModelInfo);
@@ -886,7 +1003,7 @@ function handleGet(params: ManagementParams, ctx: ManagementContext): AgentToolR
886
1003
  if (!params.agent) return result("Specify 'agent' for get.", true);
887
1004
  const scope = normalizeListScope(params.agentScope);
888
1005
  if (!scope) return result("agentScope must be 'user', 'project', or 'both' for get.", true);
889
- const discovered = discoverAgentsAll(ctx.cwd);
1006
+ const discovered = discoverAgentsAll(ctx.cwd, ctx.model?.provider);
890
1007
  const matches = findAgentsInDiscovery(params.agent, discovered, scope);
891
1008
  const diagnostics = diagnosticsForScope(discovered.agentDiagnostics, scope);
892
1009
  const rawName = params.agent.trim();
@@ -916,7 +1033,7 @@ export function handleCreate(params: ManagementParams, ctx: ManagementContext):
916
1033
  const runtimeName = buildRuntimeName(name, parsedPackage.packageName);
917
1034
  const scopeRaw = cfg.scope ?? "user";
918
1035
  if (scopeRaw !== "user" && scopeRaw !== "project") return result("config.scope must be 'user' or 'project'.", true);
919
- const scope = scopeRaw as ManagementScope;
1036
+ const scope = scopeRaw;
920
1037
  if (hasKey(cfg, "steps")) return result("Durable chain definitions were removed; use workflowScript or /prompt-workflow for repeatable workflows.", true);
921
1038
  const d = discoverAgentsAll(ctx.cwd);
922
1039
  const projectConfigDir = getProjectConfigDir(ctx.cwd);
@@ -1164,7 +1281,7 @@ function handleReset(params: ManagementParams, ctx: ManagementContext): AgentToo
1164
1281
  }
1165
1282
 
1166
1283
  export function handleManagementAction(action: string, params: ManagementParams, ctx: ManagementContext): AgentToolResult<Details> {
1167
- switch (action as ManagementAction) {
1284
+ switch (action) {
1168
1285
  case "list": return handleList(params, ctx);
1169
1286
  case "get": return handleGet(params, ctx);
1170
1287
  case "models": return handleModels(params, ctx);
@@ -9,6 +9,7 @@ export const KNOWN_FIELDS = new Set([
9
9
  "alias",
10
10
  "aliases",
11
11
  "tools",
12
+ "allowNestedSubagents",
12
13
  "model",
13
14
  "fallbackModels",
14
15
  "fast",
@@ -21,7 +22,6 @@ export const KNOWN_FIELDS = new Set([
21
22
  "async",
22
23
  "timeoutMs",
23
24
  "toolTimeoutMs",
24
- "turnBudget",
25
25
  "acceptance",
26
26
  "acceptanceRole",
27
27
  "skill",
@@ -70,6 +70,9 @@ export function serializeAgent(config: AgentConfig, options: SerializeAgentOptio
70
70
  ];
71
71
  const toolsValue = joinComma(tools);
72
72
  if (toolsValue || preserve("tools")) lines.push(`tools: ${toolsValue ?? ""}`);
73
+ if (config.allowNestedSubagents === true || preserve("allowNestedSubagents")) {
74
+ lines.push(`allowNestedSubagents: ${config.allowNestedSubagents === undefined ? "" : config.allowNestedSubagents ? "true" : "false"}`);
75
+ }
73
76
 
74
77
  if (config.model || preserve("model")) lines.push(`model: ${config.model ?? ""}`);
75
78
  const fallbackModelsValue = joinComma(config.fallbackModels);
@@ -94,7 +97,6 @@ export function serializeAgent(config: AgentConfig, options: SerializeAgentOptio
94
97
  if (config.defaultAsync !== undefined || preserve("async")) lines.push(`async: ${config.defaultAsync === undefined ? "" : config.defaultAsync ? "true" : "false"}`);
95
98
  if (config.defaultTimeoutMs !== undefined || preserve("timeoutMs")) lines.push(`timeoutMs: ${config.defaultTimeoutMs ?? ""}`);
96
99
  if (config.defaultToolTimeoutMs !== undefined || preserve("toolTimeoutMs")) lines.push(`toolTimeoutMs: ${config.defaultToolTimeoutMs ?? ""}`);
97
- if (config.defaultTurnBudget || preserve("turnBudget")) lines.push(`turnBudget: ${config.defaultTurnBudget ? JSON.stringify(config.defaultTurnBudget) : ""}`);
98
100
  if (config.defaultAcceptance !== undefined || preserve("acceptance")) {
99
101
  lines.push(`acceptance: ${config.defaultAcceptance === undefined
100
102
  ? ""
@@ -8,7 +8,7 @@ import { parse as parseYaml } from "yaml";
8
8
  import * as os from "node:os";
9
9
  import * as path from "node:path";
10
10
  import { fileURLToPath } from "node:url";
11
- import type { AcceptanceInput, AcceptanceRole, AgentRunnerConfig, OutputMode, ToolBudgetConfig, TurnBudgetConfig } from "../shared/types.ts";
11
+ import type { AcceptanceInput, AcceptanceRole, AgentRunnerConfig, OutputMode, ToolBudgetConfig } from "../shared/types.ts";
12
12
  import { CODE_OWNED_EXTERNAL_CLI_ADAPTER_LABEL, isCodeOwnedExternalCliAdapterId, parseExternalCliCapabilityNarrowing, validateCodeOwnedProfileRunner } from "../runs/shared/external-cli-contract.ts";
13
13
  import { getAgentDir, getProjectConfigDir } from "../shared/utils.ts";
14
14
  import { KNOWN_FIELDS } from "./agent-serializer.ts";
@@ -20,7 +20,6 @@ import { parseModelScopeConfig, type ModelScopeConfig } from "../runs/shared/mod
20
20
  export { BUILTIN_AGENT_NAMES } from "./builtin-names.ts";
21
21
  export { buildRuntimeName, frontmatterNameForConfig, parsePackageName } from "./identity.ts";
22
22
  import { parseMemoryFrontmatter } from "./agent-memory.ts";
23
- import { resolveTurnBudgetConfig } from "../runs/shared/turn-budget.ts";
24
23
  import { validateAcceptanceInput } from "../runs/shared/acceptance.ts";
25
24
  import { validatePermissionRules, type PermissionRules } from "../runs/shared/permissions.ts";
26
25
  import { parseThinkingLevel, type ThinkingLevel } from "../shared/thinking-ceiling.ts";
@@ -71,6 +70,7 @@ export interface BuiltinAgentOverrideBase {
71
70
  skills?: string[];
72
71
  skillPath?: string[];
73
72
  tools?: string[];
73
+ allowNestedSubagents?: boolean;
74
74
  mcpDirectTools?: string[];
75
75
  extensions?: string[];
76
76
  subagentOnlyExtensions?: string[];
@@ -99,6 +99,7 @@ interface BuiltinAgentOverrideConfig {
99
99
  systemPrompt?: string;
100
100
  skills?: string[] | false;
101
101
  tools?: string[] | false | "inherit";
102
+ allowNestedSubagents?: boolean;
102
103
  extensions?: string[] | false;
103
104
  subagentOnlyExtensions?: string[] | false;
104
105
  mutationTools?: string[] | false;
@@ -131,6 +132,7 @@ export interface AgentConfig {
131
132
  description: string;
132
133
  aliases?: string[];
133
134
  tools?: string[];
135
+ allowNestedSubagents?: boolean;
134
136
  mcpDirectTools?: string[];
135
137
  model?: string;
136
138
  modelProvider?: string;
@@ -145,7 +147,6 @@ export interface AgentConfig {
145
147
  defaultAsync?: boolean;
146
148
  defaultTimeoutMs?: number;
147
149
  defaultToolTimeoutMs?: number;
148
- defaultTurnBudget?: TurnBudgetConfig;
149
150
  defaultAcceptance?: AcceptanceInput;
150
151
  acceptanceRole?: AcceptanceRole;
151
152
  systemPrompt: string;
@@ -179,6 +180,7 @@ type ProjectRootResolution = "nearest" | "git-root";
179
180
 
180
181
  interface SubagentSettings {
181
182
  overrides: Record<string, BuiltinAgentOverrideConfig>;
183
+ providerOverrides: Record<string, Record<string, BuiltinAgentOverrideConfig>>;
182
184
  defaultModel?: string;
183
185
  defaultProvider?: string;
184
186
  defaultThinking?: string;
@@ -189,7 +191,7 @@ interface SubagentSettings {
189
191
  modelScope?: ModelScopeConfig;
190
192
  }
191
193
 
192
- const EMPTY_SUBAGENT_SETTINGS: SubagentSettings = { overrides: {} };
194
+ const EMPTY_SUBAGENT_SETTINGS: SubagentSettings = { overrides: {}, providerOverrides: {} };
193
195
  const agentFrontmatterFields = new WeakMap<AgentConfig, Set<string>>();
194
196
 
195
197
  export interface ChainStepConfig {
@@ -725,6 +727,7 @@ function cloneOverrideBase(agent: AgentConfig): BuiltinAgentOverrideBase {
725
727
  ...(agent.skills ? { skills: [...agent.skills] } : {}),
726
728
  ...(agent.skillPath ? { skillPath: [...agent.skillPath] } : {}),
727
729
  ...(agent.tools ? { tools: [...agent.tools] } : {}),
730
+ ...(agent.allowNestedSubagents !== undefined ? { allowNestedSubagents: agent.allowNestedSubagents } : {}),
728
731
  ...(agent.mcpDirectTools ? { mcpDirectTools: [...agent.mcpDirectTools] } : {}),
729
732
  ...(!agent.extensionsFromDefault && agent.extensions ? { extensions: [...agent.extensions] } : {}),
730
733
  ...(agent.subagentOnlyExtensions ? { subagentOnlyExtensions: [...agent.subagentOnlyExtensions] } : {}),
@@ -757,6 +760,7 @@ function cloneOverrideValue(override: BuiltinAgentOverrideConfig): BuiltinAgentO
757
760
  ...(override.systemPrompt !== undefined ? { systemPrompt: override.systemPrompt } : {}),
758
761
  ...(override.skills !== undefined ? { skills: override.skills === false ? false : [...override.skills] } : {}),
759
762
  ...(override.tools !== undefined ? { tools: Array.isArray(override.tools) ? [...override.tools] : override.tools } : {}),
763
+ ...(override.allowNestedSubagents !== undefined ? { allowNestedSubagents: override.allowNestedSubagents } : {}),
760
764
  ...(override.extensions !== undefined ? { extensions: override.extensions === false ? false : [...override.extensions] } : {}),
761
765
  ...(override.subagentOnlyExtensions !== undefined ? { subagentOnlyExtensions: override.subagentOnlyExtensions === false ? false : [...override.subagentOnlyExtensions] } : {}),
762
766
  ...(override.mutationTools !== undefined ? { mutationTools: override.mutationTools === false ? false : [...override.mutationTools] } : {}),
@@ -1048,6 +1052,10 @@ function parseBuiltinOverrideEntry(
1048
1052
 
1049
1053
  const tools = parseToolsOverride(input.tools, { filePath, name });
1050
1054
  if (tools !== undefined) override.tools = tools;
1055
+ if ("allowNestedSubagents" in input) {
1056
+ if (typeof input.allowNestedSubagents === "boolean") override.allowNestedSubagents = input.allowNestedSubagents;
1057
+ else throw new Error(`Builtin override '${name}' in '${filePath}' has invalid 'allowNestedSubagents'; expected a boolean.`);
1058
+ }
1051
1059
 
1052
1060
  const extensions = parseOverrideStringArrayOrFalse(input.extensions, { filePath, name, field: "extensions" });
1053
1061
  if (extensions !== undefined) override.extensions = extensions;
@@ -1127,9 +1135,12 @@ function readSubagentSettings(filePath: string | null): SubagentSettings {
1127
1135
  const modelScope = parseModelScopeConfig(subagentsObject.modelScope, { filePath });
1128
1136
 
1129
1137
  const parsed: Record<string, BuiltinAgentOverrideConfig> = {};
1138
+ const providerOverrides: Record<string, Record<string, BuiltinAgentOverrideConfig>> = {};
1130
1139
  const agentOverrides = subagentsObject.agentOverrides;
1140
+ const agentOverridesByProvider = subagentsObject.agentOverridesByProvider;
1131
1141
  const parsedSettings: SubagentSettings = {
1132
1142
  overrides: parsed,
1143
+ providerOverrides,
1133
1144
  ...(defaultModel !== undefined ? { defaultModel } : {}),
1134
1145
  ...(defaultProvider !== undefined ? { defaultProvider } : {}),
1135
1146
  ...(defaultThinking !== undefined ? { defaultThinking } : {}),
@@ -1139,16 +1150,42 @@ function readSubagentSettings(filePath: string | null): SubagentSettings {
1139
1150
  ...(disableThinking !== undefined ? { disableThinking } : {}),
1140
1151
  ...(modelScope !== undefined ? { modelScope } : {}),
1141
1152
  };
1142
- if (!agentOverrides || typeof agentOverrides !== "object" || Array.isArray(agentOverrides)) {
1143
- return parsedSettings;
1153
+ if (agentOverrides && typeof agentOverrides === "object" && !Array.isArray(agentOverrides)) {
1154
+ for (const [name, value] of Object.entries(agentOverrides)) {
1155
+ const override = parseBuiltinOverrideEntry(name, value, filePath);
1156
+ if (override) parsed[name] = override;
1157
+ }
1144
1158
  }
1145
- for (const [name, value] of Object.entries(agentOverrides)) {
1146
- const override = parseBuiltinOverrideEntry(name, value, filePath);
1147
- if (override) parsed[name] = override;
1159
+ if (agentOverridesByProvider !== undefined) {
1160
+ if (!agentOverridesByProvider || typeof agentOverridesByProvider !== "object" || Array.isArray(agentOverridesByProvider)) {
1161
+ throw new Error(`Subagent settings in '${filePath}' have invalid 'agentOverridesByProvider'; expected an object keyed by provider.`);
1162
+ }
1163
+ for (const [provider, value] of Object.entries(agentOverridesByProvider)) {
1164
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
1165
+ throw new Error(`Subagent settings in '${filePath}' have invalid 'agentOverridesByProvider.${provider}'; expected an object keyed by agent.`);
1166
+ }
1167
+ const entries: Record<string, BuiltinAgentOverrideConfig> = {};
1168
+ for (const [agentName, agentValue] of Object.entries(value)) {
1169
+ const override = parseBuiltinOverrideEntry(`agentOverridesByProvider.${provider}.${agentName}`, agentValue, filePath);
1170
+ if (override) entries[agentName] = override;
1171
+ }
1172
+ providerOverrides[provider] = entries;
1173
+ }
1148
1174
  }
1149
1175
  return parsedSettings;
1150
1176
  }
1151
1177
 
1178
+ function selectProviderOverrides(settings: SubagentSettings, provider: string | undefined): SubagentSettings {
1179
+ if (!provider) return settings;
1180
+ const selected = settings.providerOverrides[provider];
1181
+ if (!selected) return settings;
1182
+ const overrides = { ...settings.overrides };
1183
+ for (const [name, override] of Object.entries(selected)) {
1184
+ overrides[name] = { ...overrides[name], ...override };
1185
+ }
1186
+ return { ...settings, overrides };
1187
+ }
1188
+
1152
1189
  function resolveSubagentDefaultProvider(
1153
1190
  userSettings: SubagentSettings,
1154
1191
  projectSettings: SubagentSettings,
@@ -1301,6 +1338,7 @@ function applyBuiltinOverride(
1301
1338
  if (override.systemPrompt !== undefined) next.systemPrompt = override.systemPrompt;
1302
1339
  if (override.skills !== undefined) { if (override.skills === false) delete next.skills; else next.skills = [...override.skills]; }
1303
1340
  if (override.tools !== undefined) applyToolsOverride(next, override.tools);
1341
+ if (override.allowNestedSubagents !== undefined) next.allowNestedSubagents = override.allowNestedSubagents;
1304
1342
  if (override.extensions !== undefined) { if (override.extensions === false) delete next.extensions; else next.extensions = [...override.extensions]; }
1305
1343
  if (override.subagentOnlyExtensions !== undefined) { if (override.subagentOnlyExtensions === false) delete next.subagentOnlyExtensions; else next.subagentOnlyExtensions = [...override.subagentOnlyExtensions]; }
1306
1344
  if (override.mutationTools !== undefined) { if (override.mutationTools === false) delete next.mutationTools; else next.mutationTools = [...override.mutationTools]; }
@@ -1465,6 +1503,9 @@ function applyCustomAgentOverride(
1465
1503
  applyToolsOverride(mutable(), override.tools);
1466
1504
  anyFilled = true;
1467
1505
  }
1506
+ if (override.allowNestedSubagents !== undefined) {
1507
+ fill("allowNestedSubagents", ["allowNestedSubagents"], override.allowNestedSubagents);
1508
+ }
1468
1509
  if (override.extensions !== undefined) {
1469
1510
  fill("extensions", ["extensions"], override.extensions === false ? undefined : [...override.extensions]);
1470
1511
  }
@@ -1517,7 +1558,7 @@ function applyCustomAgentOverrides(
1517
1558
 
1518
1559
  export function buildBuiltinOverrideConfig(
1519
1560
  base: BuiltinAgentOverrideBase,
1520
- draft: Pick<AgentConfig, "model" | "modelProvider" | "fallbackModels" | "fast" | "thinking" | "systemPromptMode" | "inheritProjectContext" | "inheritGlobalContext" | "inheritSkills" | "defaultContext" | "acceptanceRole" | "disabled" | "systemPrompt" | "skills" | "tools" | "mcpDirectTools" | "extensions" | "subagentOnlyExtensions" | "mutationTools" | "completionGuard" | "toolBudget"> & Partial<Pick<AgentConfig, "description" | "output" | "outputMode" | "defaultReads">>,
1561
+ draft: Pick<AgentConfig, "model" | "modelProvider" | "fallbackModels" | "fast" | "thinking" | "systemPromptMode" | "inheritProjectContext" | "inheritGlobalContext" | "inheritSkills" | "defaultContext" | "acceptanceRole" | "disabled" | "systemPrompt" | "skills" | "tools" | "allowNestedSubagents" | "mcpDirectTools" | "extensions" | "subagentOnlyExtensions" | "mutationTools" | "completionGuard" | "toolBudget"> & Partial<Pick<AgentConfig, "description" | "output" | "outputMode" | "defaultReads">>,
1521
1562
  ): BuiltinAgentOverrideConfig | undefined {
1522
1563
  const override: BuiltinAgentOverrideConfig = {};
1523
1564
 
@@ -1546,6 +1587,7 @@ export function buildBuiltinOverrideConfig(
1546
1587
  const baseTools = joinToolList(base);
1547
1588
  const draftTools = joinToolList(draft);
1548
1589
  if (!arraysEqual(draftTools, baseTools)) override.tools = draftTools ? [...draftTools] : false;
1590
+ if (draft.allowNestedSubagents !== base.allowNestedSubagents) override.allowNestedSubagents = draft.allowNestedSubagents === true;
1549
1591
  if (!arraysEqual(draft.extensions, base.extensions)) override.extensions = draft.extensions ? [...draft.extensions] : false;
1550
1592
  if (!arraysEqual(draft.subagentOnlyExtensions, base.subagentOnlyExtensions)) {
1551
1593
  override.subagentOnlyExtensions = draft.subagentOnlyExtensions ? [...draft.subagentOnlyExtensions] : false;
@@ -1680,7 +1722,7 @@ export function removeBuiltinAgentOverrideFields(
1680
1722
  return { path: filePath, removed: true };
1681
1723
  }
1682
1724
 
1683
- const DISCOVERY_PRUNED_DIR_NAMES = new Set([".git", "node_modules"]);
1725
+ const DISCOVERY_PRUNED_DIR_NAMES = new Set([".git", "node_modules", ".pi", "sync-backups"]);
1684
1726
 
1685
1727
  function isDiscoveryNestedProjectRoot(dir: string): boolean {
1686
1728
  return isDirectory(getProjectConfigDir(dir)) || isDirectory(path.join(dir, ".agents"));
@@ -1894,7 +1936,7 @@ function parseAgentRunnerFrontmatter(raw: string | undefined, agentName: string)
1894
1936
 
1895
1937
  function validateExternalRunnerProfile(frontmatter: Record<string, string>, agentName: string, runner: AgentRunnerConfig | undefined): void {
1896
1938
  if (runner?.type !== "external-cli" && runner?.type !== "external-job") return;
1897
- const unsupported = ["tools", "model", "fallbackModels", "thinking", "extensions", "subagentOnlyExtensions", "mutationTools", "maxSubagentDepth", "completionGuard", "skills", "skill", "skillPath", "toolBudget", "permission", "permissions"]
1939
+ const unsupported = ["tools", "allowNestedSubagents", "model", "fallbackModels", "thinking", "extensions", "subagentOnlyExtensions", "mutationTools", "maxSubagentDepth", "completionGuard", "skills", "skill", "skillPath", "toolBudget", "permission", "permissions"]
1898
1940
  .filter((field) => frontmatter[field] !== undefined);
1899
1941
  if (unsupported.length > 0) {
1900
1942
  throw new Error(`Agent '${agentName}' uses runner.type='${runner.type}' and declares unsupported Pi-only fields: ${unsupported.join(", ")}.`);
@@ -2027,13 +2069,6 @@ function loadAgentsFromDefinitionFiles(files: AgentDefinitionFile[], source: Age
2027
2069
  }
2028
2070
  defaultToolTimeoutMs = parsed;
2029
2071
  }
2030
- let defaultTurnBudget: TurnBudgetConfig | undefined;
2031
- if (frontmatter.turnBudget !== undefined && frontmatter.turnBudget.trim()) {
2032
- const parsed = JSON.parse(frontmatter.turnBudget) as unknown;
2033
- const resolved = resolveTurnBudgetConfig(parsed, `Agent '${localName}' turnBudget frontmatter`);
2034
- if (resolved.error) throw new Error(resolved.error);
2035
- defaultTurnBudget = resolved.turnBudget;
2036
- }
2037
2072
  const defaultAcceptance = parseAgentAcceptanceFrontmatter(frontmatter.acceptance, localName);
2038
2073
  let outputMode: OutputMode | undefined;
2039
2074
  if (frontmatter.outputMode !== undefined) {
@@ -2055,6 +2090,12 @@ function loadAgentsFromDefinitionFiles(files: AgentDefinitionFile[], source: Age
2055
2090
  else if (frontmatter.fast === "false") fast = false;
2056
2091
  else throw new Error(`Agent '${localName}' has invalid fast frontmatter; expected true or false.`);
2057
2092
  }
2093
+ let allowNestedSubagents: boolean | undefined;
2094
+ if (frontmatter.allowNestedSubagents !== undefined) {
2095
+ if (frontmatter.allowNestedSubagents === "true") allowNestedSubagents = true;
2096
+ else if (frontmatter.allowNestedSubagents === "false") allowNestedSubagents = false;
2097
+ else throw new Error(`Agent '${localName}' has invalid allowNestedSubagents frontmatter; expected true or false.`);
2098
+ }
2058
2099
 
2059
2100
  const extraFields: Record<string, string> = {};
2060
2101
  for (const [key, value] of Object.entries(frontmatter)) {
@@ -2098,6 +2139,7 @@ function loadAgentsFromDefinitionFiles(files: AgentDefinitionFile[], source: Age
2098
2139
  description: frontmatter.description,
2099
2140
  ...(aliases !== undefined ? { aliases } : {}),
2100
2141
  ...(rawTools !== undefined ? { tools } : {}),
2142
+ ...(allowNestedSubagents !== undefined ? { allowNestedSubagents } : {}),
2101
2143
  ...(mcpDirectTools.length > 0 ? { mcpDirectTools } : {}),
2102
2144
  ...(frontmatter.model !== undefined ? { model: frontmatter.model } : {}),
2103
2145
  ...(fallbackModels?.length ? { fallbackModels } : {}),
@@ -2111,7 +2153,6 @@ function loadAgentsFromDefinitionFiles(files: AgentDefinitionFile[], source: Age
2111
2153
  ...(defaultAsync !== undefined ? { defaultAsync } : {}),
2112
2154
  ...(defaultTimeoutMs !== undefined ? { defaultTimeoutMs } : {}),
2113
2155
  ...(defaultToolTimeoutMs !== undefined ? { defaultToolTimeoutMs } : {}),
2114
- ...(defaultTurnBudget !== undefined ? { defaultTurnBudget } : {}),
2115
2156
  ...(defaultAcceptance !== undefined ? { defaultAcceptance } : {}),
2116
2157
  ...(acceptanceRole !== undefined ? { acceptanceRole } : {}),
2117
2158
  systemPrompt: body,
@@ -2237,15 +2278,15 @@ function extraUserAgentDirs(): string[] {
2237
2278
  .filter((dir) => dir.length > 0);
2238
2279
  }
2239
2280
 
2240
- export function discoverAgents(cwd: string, scope: AgentScope): AgentDiscoveryResult {
2281
+ export function discoverAgents(cwd: string, scope: AgentScope, preferredModelProvider?: string): AgentDiscoveryResult {
2241
2282
  const effectiveCwd = path.resolve(cwd);
2242
2283
  const userDirOld = path.join(getAgentDir(), "agents");
2243
2284
  const userDirNew = path.join(os.homedir(), ".agents");
2244
2285
  const { readDirs: projectAgentDirs, candidateDirs: projectCandidateDirs, preferredDir: projectAgentsDir } = resolveNearestProjectAgentDirs(effectiveCwd);
2245
2286
  const userSettingsPath = getUserAgentSettingsPath();
2246
2287
  const projectSettingsPath = getProjectAgentSettingsPath(effectiveCwd);
2247
- const userSettings = scope === "project" ? EMPTY_SUBAGENT_SETTINGS : readSubagentSettings(userSettingsPath);
2248
- const projectSettings = scope === "user" ? EMPTY_SUBAGENT_SETTINGS : readSubagentSettings(projectSettingsPath);
2288
+ const userSettings = selectProviderOverrides(scope === "project" ? EMPTY_SUBAGENT_SETTINGS : readSubagentSettings(userSettingsPath), preferredModelProvider);
2289
+ const projectSettings = selectProviderOverrides(scope === "user" ? EMPTY_SUBAGENT_SETTINGS : readSubagentSettings(projectSettingsPath), preferredModelProvider);
2249
2290
  const defaultProvider = resolveSubagentDefaultProvider(userSettings, projectSettings, projectSettingsPath);
2250
2291
  const defaultModel = resolveSubagentDefaultModel(userSettings, projectSettings, userSettingsPath, projectSettingsPath, defaultProvider);
2251
2292
  const defaultThinking = resolveSubagentDefaultThinking(userSettings, projectSettings, projectSettingsPath);
@@ -2308,7 +2349,7 @@ export function discoverAgents(cwd: string, scope: AgentScope): AgentDiscoveryRe
2308
2349
  return { agents, agentDiagnostics, projectAgentsDir, cwd: effectiveCwd, scope, directories, ...(modelScope !== undefined ? { modelScope } : {}), ...(maxThinking !== undefined ? { maxThinking } : {}) };
2309
2350
  }
2310
2351
 
2311
- export function discoverAgentsAll(cwd: string): {
2352
+ export function discoverAgentsAll(cwd: string, preferredModelProvider?: string): {
2312
2353
  builtin: AgentConfig[];
2313
2354
  package: AgentConfig[];
2314
2355
  user: AgentConfig[];
@@ -2331,8 +2372,8 @@ export function discoverAgentsAll(cwd: string): {
2331
2372
  const { readDirs: projectChainDirs, preferredDir: projectChainDir } = resolveNearestProjectChainDirs(cwd);
2332
2373
  const userSettingsPath = getUserAgentSettingsPath();
2333
2374
  const projectSettingsPath = getProjectAgentSettingsPath(cwd);
2334
- const userSettings = readSubagentSettings(userSettingsPath);
2335
- const projectSettings = readSubagentSettings(projectSettingsPath);
2375
+ const userSettings = selectProviderOverrides(readSubagentSettings(userSettingsPath), preferredModelProvider);
2376
+ const projectSettings = selectProviderOverrides(readSubagentSettings(projectSettingsPath), preferredModelProvider);
2336
2377
  const defaultProvider = resolveSubagentDefaultProvider(userSettings, projectSettings, projectSettingsPath);
2337
2378
  const defaultModel = resolveSubagentDefaultModel(userSettings, projectSettings, userSettingsPath, projectSettingsPath, defaultProvider);
2338
2379
  const defaultThinking = resolveSubagentDefaultThinking(userSettings, projectSettings, projectSettingsPath);