pi-subagents 0.60.0 → 0.62.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 +55 -0
  2. package/docs/agents.md +7 -3
  3. package/docs/configuration.md +9 -5
  4. package/docs/extension-api.md +14 -7
  5. package/docs/models.md +1 -1
  6. package/docs/observability.md +1 -1
  7. package/docs/tool-reference.md +13 -4
  8. package/docs/workflows.md +14 -13
  9. package/install.mjs +1 -1
  10. package/package.json +1 -1
  11. package/skills/pi-subagents/SKILL.md +6 -4
  12. package/skills/pi-subagents/references/constraints-and-recipes.md +1 -1
  13. package/skills/pi-subagents/references/execution-controls.md +41 -11
  14. package/skills/pi-subagents/references/multi-lane-orchestration.md +1 -1
  15. package/skills/pi-subagents/references/prompting-and-roles.md +4 -4
  16. package/skills/pi-subagents/references/review-and-validation.md +1 -1
  17. package/src/agents/agent-management.ts +121 -63
  18. package/src/agents/agent-serializer.ts +3 -0
  19. package/src/agents/agents.ts +543 -223
  20. package/src/agents/runtime-agent-registry.ts +5 -1
  21. package/src/api/background-work.ts +7 -2
  22. package/src/api/external-runs.ts +67 -4
  23. package/src/api/preflight.ts +17 -8
  24. package/src/api/shared-types.ts +1 -0
  25. package/src/extension/index.ts +7 -4
  26. package/src/extension/public-execution.ts +48 -4
  27. package/src/extension/rpc.ts +62 -4
  28. package/src/extension/schemas.ts +17 -9
  29. package/src/extension/tool-description.ts +15 -19
  30. package/src/runs/background/active-async-capacity.ts +26 -7
  31. package/src/runs/background/async-execution.ts +98 -51
  32. package/src/runs/background/async-job-tracker.ts +62 -3
  33. package/src/runs/background/async-resume.ts +6 -3
  34. package/src/runs/background/async-status.ts +59 -9
  35. package/src/runs/background/auto-drain.ts +1 -1
  36. package/src/runs/background/fleet-view.ts +1 -1
  37. package/src/runs/background/process-terminal.ts +16 -0
  38. package/src/runs/background/result-watcher.ts +1 -1
  39. package/src/runs/background/resume-guidance.ts +1 -1
  40. package/src/runs/background/run-status.ts +2 -2
  41. package/src/runs/background/scheduled-runs.ts +63 -6
  42. package/src/runs/background/steering.ts +4 -1
  43. package/src/runs/background/subagent-runner.ts +15 -9
  44. package/src/runs/background/subagent-wait.ts +20 -21
  45. package/src/runs/background/wait-completions.ts +1 -1
  46. package/src/runs/background/wait-tool.ts +18 -18
  47. package/src/runs/foreground/execution.ts +75 -9
  48. package/src/runs/foreground/subagent-executor.ts +181 -60
  49. package/src/runs/shared/acceptance.ts +113 -27
  50. package/src/runs/shared/capability-ceiling.ts +1 -0
  51. package/src/runs/shared/dynamic-fanout.ts +1 -1
  52. package/src/runs/shared/host-step-status.ts +1 -0
  53. package/src/runs/shared/model-fallback.ts +61 -17
  54. package/src/runs/shared/parallel-utils.ts +2 -6
  55. package/src/runs/shared/permissions.ts +1 -1
  56. package/src/runs/shared/pi-args.ts +24 -12
  57. package/src/runs/shared/pi-spawn.ts +69 -35
  58. package/src/runs/shared/structured-output.ts +33 -6
  59. package/src/runs/shared/subagent-prompt-runtime.ts +20 -3
  60. package/src/runs/shared/task-intent.ts +17 -6
  61. package/src/runs/shared/tool-timeout.ts +1 -1
  62. package/src/runs/shared/workflow-graph.ts +3 -2
  63. package/src/shared/atomic-json.ts +3 -1
  64. package/src/shared/fork-context.ts +0 -12
  65. package/src/shared/fork-session-cwd.ts +27 -0
  66. package/src/shared/launch-contract.ts +3 -0
  67. package/src/shared/types.ts +43 -4
  68. package/src/shared/workflow-child-permit.ts +91 -0
  69. package/src/slash/prompt-template-bridge.ts +37 -1
  70. package/src/slash/slash-commands.ts +18 -26
  71. package/src/slash/subagents-admin.ts +2 -0
  72. package/src/tui/render.ts +95 -52
  73. package/src/workflows/scripted-workflow.ts +153 -4
  74. package/src/workflows/workflow-child-summary.ts +1 -1
  75. package/src/workflows/workflow-receipt.ts +41 -4
  76. package/src/workflows/workflow-resources.ts +150 -0
@@ -7,7 +7,6 @@ import {
7
7
  type AgentDiscoveryDiagnostic,
8
8
  type AgentScope,
9
9
  type AgentSource,
10
- BUILTIN_AGENT_NAMES,
11
10
  defaultInheritProjectContext,
12
11
  defaultInheritSkills,
13
12
  defaultSystemPromptMode,
@@ -28,7 +27,7 @@ import {
28
27
  buildProactiveSkillSubagentRecommendationLines,
29
28
  } from "./proactive-skills.ts";
30
29
  import { parseFrontmatter, parseFrontmatterList } from "./frontmatter.ts";
31
- import { toModelInfo } from "../shared/model-info.ts";
30
+ import { resolveEffectiveThinking, toModelInfo } from "../shared/model-info.ts";
32
31
  import { resolveSubagentModelOverride, type ParentModel } from "../runs/shared/model-fallback.ts";
33
32
  import { validateToolBudgetConfig } from "../runs/shared/tool-budget.ts";
34
33
  import { validateAcceptanceInput } from "../runs/shared/acceptance.ts";
@@ -37,7 +36,7 @@ import type { AcceptanceInput, AgentCapabilitiesSnapshot, AgentCapabilityRow, De
37
36
  import { getProjectConfigDir } from "../shared/utils.ts";
38
37
  import { previewDisplayText } from "../shared/display-text.ts";
39
38
  import { capabilityCeilingAgentRestrictionSources, isAgentAllowedByCapabilityCeiling, resolveCurrentSubagentCapabilityCeiling } from "../runs/shared/capability-ceiling.ts";
40
- import { listRuntimeAgentConfigs, mergeRuntimeAgents, type RuntimeAgentOwner } from "./runtime-agent-registry.ts";
39
+ import { mergeRuntimeAgents, type RuntimeAgentOwner } from "./runtime-agent-registry.ts";
41
40
  import { listExternalJobProviders } from "../api/external-job-provider.ts";
42
41
 
43
42
  type ManagementAction = "list" | "get" | "models" | "create" | "update" | "delete" | "eject" | "disable" | "enable" | "reset";
@@ -129,18 +128,39 @@ function allAgents(d: { builtin: AgentConfig[]; package: AgentConfig[]; user: Ag
129
128
  return [...d.builtin, ...d.package, ...d.user, ...d.project];
130
129
  }
131
130
 
132
- function availableAgentNamesFromDiscovery(d: { builtin: AgentConfig[]; package: AgentConfig[]; user: AgentConfig[]; project: AgentConfig[] }): string[] {
133
- return [...new Set(allAgents(d).map((agent) => agent.name))].sort((a, b) => a.localeCompare(b));
131
+ function effectiveAgentsForScope(
132
+ scope: AgentScope,
133
+ d: { builtin: AgentConfig[]; package: AgentConfig[]; user: AgentConfig[]; project: AgentConfig[] },
134
+ runtimeAgentOwner?: RuntimeAgentOwner,
135
+ ): AgentConfig[] {
136
+ let agents = mergeAgentsForScope(scope, d.user, d.project, d.builtin, d.package);
137
+ if (runtimeAgentOwner) {
138
+ agents = mergeRuntimeAgents(runtimeAgentOwner, { agents }, allAgents(d)).agents;
139
+ }
140
+ return agents;
141
+ }
142
+
143
+ function availableAgentNamesFromDiscovery(
144
+ d: { builtin: AgentConfig[]; package: AgentConfig[]; user: AgentConfig[]; project: AgentConfig[] },
145
+ runtimeAgentOwner?: RuntimeAgentOwner,
146
+ ): string[] {
147
+ const agents = runtimeAgentOwner ? effectiveAgentsForScope("both", d, runtimeAgentOwner) : allAgents(d);
148
+ return [...new Set(agents.map((agent) => agent.name))].sort((a, b) => a.localeCompare(b));
134
149
  }
135
150
 
136
151
  function availableAgentNames(cwd: string): string[] {
137
152
  return availableAgentNamesFromDiscovery(discoverAgentsAll(cwd));
138
153
  }
139
154
 
140
- function findAgentsInDiscovery(name: string, d: { builtin: AgentConfig[]; package: AgentConfig[]; user: AgentConfig[]; project: AgentConfig[] }, scope: AgentScope = "both"): AgentConfig[] {
155
+ function findAgentsInDiscovery(
156
+ name: string,
157
+ d: { builtin: AgentConfig[]; package: AgentConfig[]; user: AgentConfig[]; project: AgentConfig[] },
158
+ scope: AgentScope = "both",
159
+ runtimeAgentOwner?: RuntimeAgentOwner,
160
+ ): AgentConfig[] {
141
161
  const raw = name.trim();
142
162
  const sanitized = sanitizeName(raw);
143
- const scoped = mergeAgentsForScope(scope, d.user, d.project, d.builtin, d.package);
163
+ const scoped = effectiveAgentsForScope(scope, d, runtimeAgentOwner);
144
164
  let resolved = resolveAgentName(raw, scoped);
145
165
  if (!resolved.agent && !resolved.error && sanitized !== raw) resolved = resolveAgentName(sanitized, scoped);
146
166
  if (resolved.agent) return scoped.filter((agent) => agent.name === resolved.agent!.name).sort((a, b) => a.source.localeCompare(b.source));
@@ -249,6 +269,7 @@ export function editableAgentConfig(agent: AgentConfig): AgentConfig {
249
269
  skills: _skills,
250
270
  skillPath: _skillPath,
251
271
  tools: _tools,
272
+ excludeTools: _excludeTools,
252
273
  mcpDirectTools: _mcpDirectTools,
253
274
  subagentOnlyExtensions: _subagentOnlyExtensions,
254
275
  mutationTools: _mutationTools,
@@ -281,6 +302,7 @@ export function editableAgentConfig(agent: AgentConfig): AgentConfig {
281
302
  ...(base.skills !== undefined ? { skills: [...base.skills] } : {}),
282
303
  ...(base.skillPath !== undefined ? { skillPath: [...base.skillPath] } : {}),
283
304
  ...(base.tools !== undefined ? { tools: [...base.tools] } : {}),
305
+ ...(base.excludeTools !== undefined ? { excludeTools: [...base.excludeTools] } : {}),
284
306
  ...(base.mcpDirectTools !== undefined ? { mcpDirectTools: [...base.mcpDirectTools] } : {}),
285
307
  ...(base.extensions !== undefined ? { extensions: [...base.extensions] } : {}),
286
308
  ...(base.subagentOnlyExtensions !== undefined ? { subagentOnlyExtensions: [...base.subagentOnlyExtensions] } : {}),
@@ -313,6 +335,7 @@ export function preservedAgentFrontmatterFields(agent: AgentConfig, cfg: Record<
313
335
  if (hasKey(cfg, "model")) changed("model");
314
336
  if (hasKey(cfg, "fallbackModels")) changed("fallbackModels");
315
337
  if (hasKey(cfg, "tools")) changed("tools");
338
+ if (hasKey(cfg, "excludeTools")) changed("excludeTools");
316
339
  if (hasKey(cfg, "skills")) changed("skill", "skills");
317
340
  if (hasKey(cfg, "skillPath")) changed("skillPath");
318
341
  if (hasKey(cfg, "extensions")) changed("extensions");
@@ -443,6 +466,18 @@ function applyAgentConfig(target: AgentConfig, cfg: Record<string, unknown>): st
443
466
  else delete target.mcpDirectTools;
444
467
  } else return "config.tools must be a comma-separated string or false when provided.";
445
468
  }
469
+ if (hasKey(cfg, "excludeTools")) {
470
+ if (cfg.excludeTools === false || cfg.excludeTools === "") delete target.excludeTools;
471
+ else if (typeof cfg.excludeTools === "string") {
472
+ const excludeTools = parseCsv(cfg.excludeTools);
473
+ if (excludeTools.length) target.excludeTools = [...new Set(excludeTools)];
474
+ else delete target.excludeTools;
475
+ } else if (Array.isArray(cfg.excludeTools) && cfg.excludeTools.every((entry) => typeof entry === "string")) {
476
+ const excludeTools = [...new Set(cfg.excludeTools.map((entry) => entry.trim()).filter(Boolean))];
477
+ if (excludeTools.length) target.excludeTools = excludeTools;
478
+ else delete target.excludeTools;
479
+ } else return "config.excludeTools must be a comma-separated string, string array, or false when provided.";
480
+ }
446
481
  if (hasKey(cfg, "skills")) {
447
482
  if (cfg.skills === false || cfg.skills === "") delete target.skills;
448
483
  else if (typeof cfg.skills === "string") {
@@ -575,6 +610,7 @@ function applyAgentConfig(target: AgentConfig, cfg: Record<string, unknown>): st
575
610
  if (target.runner?.type === "external-cli" || target.runner?.type === "external-job") {
576
611
  const unsupported = [
577
612
  target.tools?.length || target.mcpDirectTools?.length ? "tools" : undefined,
613
+ target.excludeTools?.length ? "excludeTools" : undefined,
578
614
  target.model ? "model" : undefined,
579
615
  target.fallbackModels?.length ? "fallbackModels" : undefined,
580
616
  target.thinking ? "thinking" : undefined,
@@ -681,6 +717,7 @@ function formatAgentCapabilitiesLine(agent: AgentConfig, providerNames: Set<stri
681
717
  } else if (declaredTools.length > 0) {
682
718
  tools = declaredTools.join(", ");
683
719
  }
720
+ if (agent.excludeTools?.length) tools = `${tools}; excludes: ${agent.excludeTools.join(", ")}`;
684
721
  let model = "inherits current session";
685
722
  if (agent.model !== undefined) {
686
723
  model = agent.model;
@@ -708,6 +745,7 @@ function agentCapabilityTools(agent: AgentConfig): AgentCapabilityRow["tools"] {
708
745
  return {
709
746
  ambient: agent.tools === undefined && agent.mcpDirectTools === undefined,
710
747
  names: listOrEmpty(agent.tools),
748
+ ...(agent.excludeTools !== undefined ? { excludeTools: [...agent.excludeTools] } : {}),
711
749
  mcpDirectTools: listOrEmpty(agent.mcpDirectTools),
712
750
  mutationTools: agent.mutationTools,
713
751
  };
@@ -826,6 +864,7 @@ function formatAgentDetail(agent: AgentConfig): string {
826
864
  if (agent.model) lines.push(`Model: ${agent.model}`);
827
865
  if (agent.fallbackModels?.length) lines.push(`Fallback models: ${agent.fallbackModels.join(", ")}`);
828
866
  if (tools.length) lines.push(`Tools: ${tools.join(", ")}`);
867
+ if (agent.excludeTools?.length) lines.push(`Excluded tools: ${agent.excludeTools.join(", ")}`);
829
868
  if (agent.skills?.length) lines.push(`Skills: ${agent.skills.join(", ")}`);
830
869
  if (agent.skillPath?.length) lines.push(`Skill paths: ${agent.skillPath.join(", ")}`);
831
870
  lines.push(`System prompt mode: ${agent.systemPromptMode}`);
@@ -863,16 +902,7 @@ function formatAgentDetail(agent: AgentConfig): string {
863
902
  export function handleList(params: ManagementParams, ctx: ManagementContext): AgentToolResult<Details> {
864
903
  const scope = normalizeListScope(params.agentScope) ?? "both";
865
904
  const d = discoverAgentsAll(ctx.cwd, ctx.model?.provider);
866
- let scopedAgents = mergeAgentsForScope(scope, d.user, d.project, d.builtin, d.package);
867
- if (ctx.runtimeAgentOwner && listRuntimeAgentConfigs(ctx.runtimeAgentOwner).length > 0) {
868
- const configuredAgents: AgentConfig[] = [
869
- ...d.builtin,
870
- ...d.package,
871
- ...d.user,
872
- ...d.project,
873
- ];
874
- scopedAgents = mergeRuntimeAgents(ctx.runtimeAgentOwner, { agents: scopedAgents }, configuredAgents).agents;
875
- }
905
+ let scopedAgents = effectiveAgentsForScope(scope, d, ctx.runtimeAgentOwner);
876
906
  scopedAgents = scopedAgents
877
907
  .sort((a, b) => a.name.localeCompare(b.name));
878
908
  const capabilityCeiling = resolveCurrentSubagentCapabilityCeiling(ctx.currentSessionId);
@@ -913,80 +943,108 @@ function formatModelSource(agent: AgentConfig, currentModel: ParentModel | undef
913
943
  if (agent.modelSource?.type === "subagents.defaultModel" && agent.model === agent.modelSource.model) {
914
944
  return `${agent.modelSource.scope} defaultModel`;
915
945
  }
916
- if (agent.model) return "builtin agent config";
946
+ if (agent.model) return `${agent.source} agent config`;
917
947
  if (currentModel) return "inherits current session model";
918
948
  return "inherit requested, but no current session model is available";
919
949
  }
920
950
 
921
951
  function handleModels(params: ManagementParams, ctx: ManagementContext): AgentToolResult<Details> {
922
952
  const requestedAgent = params.agent?.trim();
923
- if (requestedAgent && !(BUILTIN_AGENT_NAMES as readonly string[]).includes(requestedAgent)) {
924
- return result(`Builtin agent '${requestedAgent}' not found. Available: ${BUILTIN_AGENT_NAMES.join(", ")}.`, true);
925
- }
953
+ const scope = normalizeListScope(params.agentScope);
954
+ if (!scope) return result("agentScope must be 'user', 'project', or 'both' for models.", true);
926
955
 
927
956
  const discovered = discoverAgentsAll(ctx.cwd, ctx.model?.provider);
928
- const builtinByName = new Map(discovered.builtin.map((agent) => [agent.name, agent]));
929
- const resolveBuiltinModelAgent = (name: string): AgentConfig | undefined => builtinByName.get(name) ?? resolveAgentName(name, discovered.builtin).agent;
957
+ const effectiveAgents = effectiveAgentsForScope(scope, discovered, ctx.runtimeAgentOwner)
958
+ .sort((a, b) => a.name.localeCompare(b.name));
930
959
  const availableModels = ctx.modelRegistry.getAvailable().map(toModelInfo);
931
960
  const currentModel = ctx.model ? { provider: ctx.model.provider, id: ctx.model.id } : undefined;
932
961
  const preferredProvider = ctx.model?.provider;
933
- const names = requestedAgent ? [requestedAgent] : [...BUILTIN_AGENT_NAMES];
962
+ const capabilityCeiling = resolveCurrentSubagentCapabilityCeiling(ctx.currentSessionId);
934
963
 
964
+ let selectedAgents = effectiveAgents;
935
965
  if (requestedAgent) {
936
- const agent = resolveBuiltinModelAgent(requestedAgent);
937
- if (!agent) return result(`Builtin agent '${requestedAgent}' not found.`, true);
938
- const resolvedModel = resolveSubagentModelOverride(agent.model, currentModel, availableModels, preferredProvider);
939
- const lines = [
940
- "Builtin subagent model",
941
- "",
942
- `Agent: ${requestedAgent}`,
943
- "Effective model:",
944
- ` ${resolvedModel ?? "(unresolved)"}`,
945
- `Source: ${formatModelSource(agent, currentModel)}`,
946
- ];
947
- if (agent.override) {
948
- lines.push("Override file:");
949
- lines.push(` ${agent.override.path}`);
950
- }
951
- if (agent.model && resolvedModel && agent.model !== resolvedModel) {
952
- lines.push("Requested model setting:");
953
- lines.push(` ${agent.model}`);
966
+ const matches = findAgentsInDiscovery(requestedAgent, discovered, scope, ctx.runtimeAgentOwner);
967
+ const diagnostics = diagnosticsForScope(discovered.agentDiagnostics, scope);
968
+ const normalizedName = sanitizeName(requestedAgent);
969
+ const diagnostic = findBlockingAgentDiagnostic(requestedAgent, matches, diagnostics)
970
+ ?? (normalizedName !== requestedAgent ? findBlockingAgentDiagnostic(normalizedName, matches, diagnostics) : undefined);
971
+ if (diagnostic) return result(`Agent '${params.agent}' has invalid configuration: ${diagnostic.error}`, true);
972
+ const distinctNames = [...new Set(matches.map((agent) => agent.name))];
973
+ if (distinctNames.length > 1) return result(`Ambiguous agent alias or name '${params.agent}': ${distinctNames.sort((a, b) => a.localeCompare(b)).join(", ")}`, true);
974
+ if (!matches.length) {
975
+ return result(`Agent '${params.agent}' not found. Available: ${availableAgentNamesFromDiscovery(discovered, ctx.runtimeAgentOwner).join(", ") || "none"}.`, true);
954
976
  }
955
- if (agent.disabled) lines.push("Disabled: true");
956
- lines.push("Current session model:");
957
- lines.push(` ${currentModel ? `${currentModel.provider}/${currentModel.id}` : "(unavailable)"}`);
958
- return result(lines.join("\n"));
977
+ selectedAgents = [matches[0]!];
959
978
  }
960
979
 
961
980
  const lines = [
962
- "Builtin subagent models",
963
- "",
964
- "Current session model:",
965
- ` ${currentModel ? `${currentModel.provider}/${currentModel.id}` : "(unavailable)"}`,
981
+ requestedAgent ? "Subagent model" : "Subagent models",
966
982
  "",
983
+ ...(requestedAgent ? [] : [
984
+ "Current session model:",
985
+ ` ${currentModel ? `${currentModel.provider}/${currentModel.id}` : "(unavailable)"}`,
986
+ "",
987
+ ]),
967
988
  ];
968
989
 
969
- for (const name of names) {
970
- const agent = resolveBuiltinModelAgent(name);
971
- if (!agent) {
972
- lines.push(name);
973
- lines.push(" model:");
974
- lines.push(" (builtin definition not found)");
975
- lines.push(" source: missing");
976
- lines.push("");
977
- continue;
990
+ const modelEntries = selectedAgents.flatMap((agent) => requestedAgent
991
+ ? [{ agent, name: requestedAgent }]
992
+ : [{ agent, name: agent.name }, ...(agent.aliases ?? []).map((name) => ({ agent, name }))]);
993
+ for (const { agent, name } of modelEntries) {
994
+ const resolvedModel = resolveSubagentModelOverride(agent.model, currentModel, availableModels, agent.modelProvider ?? preferredProvider);
995
+ const effectiveThinking = resolveEffectiveThinking(resolvedModel, agent.thinking)
996
+ ?? (agent.thinking === false ? "off" : undefined);
997
+ const source = `${formatModelSource(agent, currentModel)}${agent.disabled ? "; disabled" : ""}${isAgentAllowedByCapabilityCeiling(agent.name, capabilityCeiling) ? "" : "; restricted"}`;
998
+ if (requestedAgent) {
999
+ lines.push(`Agent: ${requestedAgent}`);
1000
+ lines.push("Effective model:");
1001
+ lines.push(` ${resolvedModel ?? "(unresolved)"}`);
1002
+ lines.push(`Source: ${source}`);
1003
+ lines.push(`Thinking: ${effectiveThinking ?? "default"}`);
1004
+ if (agent.fallbackModels?.length) {
1005
+ lines.push("Fallback models:");
1006
+ for (const fallback of agent.fallbackModels) {
1007
+ lines.push(` ${resolveSubagentModelOverride(fallback, currentModel, availableModels, agent.modelProvider ?? preferredProvider) ?? fallback}`);
1008
+ }
1009
+ }
1010
+ if (agent.override) {
1011
+ lines.push("Override file:");
1012
+ lines.push(` ${agent.override.path}`);
1013
+ }
1014
+ if (agent.model && resolvedModel && agent.model !== resolvedModel) {
1015
+ lines.push("Requested model setting:");
1016
+ lines.push(` ${agent.model}`);
1017
+ }
1018
+ if (agent.disabled) lines.push("Disabled: true");
1019
+ if (!isAgentAllowedByCapabilityCeiling(agent.name, capabilityCeiling)) lines.push("Restricted: true");
1020
+ lines.push("Current session model:");
1021
+ lines.push(` ${currentModel ? `${currentModel.provider}/${currentModel.id}` : "(unavailable)"}`);
1022
+ break;
978
1023
  }
979
- const resolvedModel = resolveSubagentModelOverride(agent.model, currentModel, availableModels, preferredProvider);
980
- const source = `${formatModelSource(agent, currentModel)}${agent.disabled ? "; disabled" : ""}`;
981
1024
  lines.push(name);
982
1025
  lines.push(" model:");
983
1026
  lines.push(` ${resolvedModel ?? "(unresolved)"}`);
984
1027
  lines.push(` source: ${source}`);
1028
+ lines.push(` thinking: ${effectiveThinking ?? "default"}`);
1029
+ if (agent.fallbackModels?.length) {
1030
+ lines.push(" fallback models:");
1031
+ for (const fallback of agent.fallbackModels) {
1032
+ lines.push(` ${resolveSubagentModelOverride(fallback, currentModel, availableModels, agent.modelProvider ?? preferredProvider) ?? fallback}`);
1033
+ }
1034
+ }
1035
+ if (agent.override) {
1036
+ lines.push(" override file:");
1037
+ lines.push(` ${agent.override.path}`);
1038
+ }
1039
+ if (agent.model && resolvedModel && agent.model !== resolvedModel) {
1040
+ lines.push(" requested model setting:");
1041
+ lines.push(` ${agent.model}`);
1042
+ }
985
1043
  lines.push("");
986
1044
  }
987
1045
 
988
1046
  const availableFullIds = availableModels.map((m) => m.fullId).sort();
989
- if (availableFullIds.length > 0) {
1047
+ if (!requestedAgent && availableFullIds.length > 0) {
990
1048
  lines.push("Available models in this session's registry (copy an exact provider/id when passing model):");
991
1049
  lines.push("");
992
1050
  const shown = availableFullIds.slice(0, 80);
@@ -995,7 +1053,7 @@ function handleModels(params: ManagementParams, ctx: ManagementContext): AgentTo
995
1053
  lines.push("");
996
1054
  lines.push("Use an exact provider/id from this list when you pass model; bare ids resolve only when unique in the registry.");
997
1055
  }
998
-
1056
+ if (!requestedAgent) appendAgentDiagnosticLines(lines, diagnosticsForScope(discovered.agentDiagnostics, scope));
999
1057
  return result(lines.join("\n"));
1000
1058
  }
1001
1059
 
@@ -9,6 +9,7 @@ export const KNOWN_FIELDS = new Set([
9
9
  "alias",
10
10
  "aliases",
11
11
  "tools",
12
+ "excludeTools",
12
13
  "allowNestedSubagents",
13
14
  "model",
14
15
  "fallbackModels",
@@ -70,6 +71,8 @@ export function serializeAgent(config: AgentConfig, options: SerializeAgentOptio
70
71
  ];
71
72
  const toolsValue = joinComma(tools);
72
73
  if (toolsValue || preserve("tools")) lines.push(`tools: ${toolsValue ?? ""}`);
74
+ const excludeToolsValue = joinComma(config.excludeTools);
75
+ if (excludeToolsValue || preserve("excludeTools")) lines.push(`excludeTools: ${excludeToolsValue ?? ""}`);
73
76
  if (config.allowNestedSubagents === true || preserve("allowNestedSubagents")) {
74
77
  lines.push(`allowNestedSubagents: ${config.allowNestedSubagents === undefined ? "" : config.allowNestedSubagents ? "true" : "false"}`);
75
78
  }