pi-subagents 0.55.0 → 0.57.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 (97) hide show
  1. package/CHANGELOG.md +81 -3
  2. package/agents/claude-code-writer.md +15 -0
  3. package/agents/claude-code.md +15 -0
  4. package/agents/codex-exec-writer.md +15 -0
  5. package/agents/codex-exec.md +15 -0
  6. package/agents/cursor-agent-writer.md +14 -0
  7. package/agents/cursor-agent.md +14 -0
  8. package/docs/agents.md +111 -15
  9. package/docs/configuration.md +37 -0
  10. package/docs/extension-api.md +2 -2
  11. package/docs/models.md +6 -0
  12. package/docs/observability.md +8 -7
  13. package/docs/tool-reference.md +11 -1
  14. package/docs/workflows.md +20 -0
  15. package/package.json +1 -1
  16. package/skills/pi-subagents/references/execution-controls.md +1 -1
  17. package/src/agents/agent-management.ts +13 -5
  18. package/src/agents/agent-refinements.ts +4 -4
  19. package/src/agents/agent-serializer.ts +2 -0
  20. package/src/agents/agents.ts +242 -49
  21. package/src/agents/builtin-names.ts +6 -0
  22. package/src/agents/runtime-agent-registry.ts +8 -2
  23. package/src/api/preflight.ts +22 -3
  24. package/src/extension/config.ts +70 -0
  25. package/src/extension/doctor.ts +3 -3
  26. package/src/extension/index.ts +18 -4
  27. package/src/extension/public-execution.ts +29 -13
  28. package/src/extension/rpc.ts +20 -5
  29. package/src/extension/schemas.ts +9 -3
  30. package/src/extension/tool-description.ts +11 -9
  31. package/src/inspectors/herdr/actions.ts +2 -1
  32. package/src/inspectors/herdr/inspector-runner.ts +2 -10
  33. package/src/inspectors/herdr/session-roots-codec.ts +42 -0
  34. package/src/integrations/herdr-status.ts +46 -3
  35. package/src/runs/background/active-async-capacity.ts +77 -10
  36. package/src/runs/background/async-execution.ts +148 -27
  37. package/src/runs/background/async-job-tracker.ts +5 -0
  38. package/src/runs/background/async-resume.ts +3 -0
  39. package/src/runs/background/async-retention.ts +20 -3
  40. package/src/runs/background/async-status.ts +5 -0
  41. package/src/runs/background/chain-root-attachment.ts +15 -1
  42. package/src/runs/background/fleet-view.ts +16 -10
  43. package/src/runs/background/inspect-rpc.ts +8 -8
  44. package/src/runs/background/result-files.ts +27 -14
  45. package/src/runs/background/result-watcher.ts +9 -5
  46. package/src/runs/background/run-status.ts +33 -6
  47. package/src/runs/background/scheduled-runs.ts +7 -1
  48. package/src/runs/background/subagent-runner.ts +254 -49
  49. package/src/runs/background/wait-completions.ts +4 -0
  50. package/src/runs/foreground/execution.ts +107 -16
  51. package/src/runs/foreground/foreground-control.ts +6 -0
  52. package/src/runs/foreground/foreground-history.ts +25 -3
  53. package/src/runs/foreground/subagent-executor.ts +324 -84
  54. package/src/runs/foreground/workflow-detach-reconcile.ts +31 -7
  55. package/src/runs/shared/acceptance.ts +10 -5
  56. package/src/runs/shared/agent-contract.ts +1 -1
  57. package/src/runs/shared/child-protocol.ts +21 -7
  58. package/src/runs/shared/claude-code-adapter.ts +129 -0
  59. package/src/runs/shared/codex-exec-adapter.ts +129 -0
  60. package/src/runs/shared/completion-guard.ts +3 -2
  61. package/src/runs/shared/cursor-agent-adapter.ts +114 -0
  62. package/src/runs/shared/dynamic-fanout.ts +1 -1
  63. package/src/runs/shared/extension-bindings.ts +78 -0
  64. package/src/runs/shared/external-cli-contract.ts +167 -0
  65. package/src/runs/shared/external-cli-preflight.ts +122 -0
  66. package/src/runs/shared/external-cli-runner.ts +349 -54
  67. package/src/runs/shared/fast-mode-extension.ts +10 -0
  68. package/src/runs/shared/model-exclusions.ts +59 -8
  69. package/src/runs/shared/model-fallback.ts +25 -8
  70. package/src/runs/shared/mutation-evidence.ts +150 -0
  71. package/src/runs/shared/nested-events.ts +3 -1
  72. package/src/runs/shared/nested-render.ts +2 -2
  73. package/src/runs/shared/parallel-utils.ts +3 -0
  74. package/src/runs/shared/pi-args.ts +47 -0
  75. package/src/runs/shared/process-signal.ts +13 -0
  76. package/src/runs/shared/run-history.ts +21 -1
  77. package/src/runs/shared/structured-output.ts +18 -4
  78. package/src/runs/shared/subagent-prompt-runtime.ts +9 -5
  79. package/src/shared/fork-context.ts +21 -0
  80. package/src/shared/formatters.ts +7 -1
  81. package/src/shared/launch-contract.ts +6 -0
  82. package/src/shared/pruned-fork.ts +450 -0
  83. package/src/shared/session-file-trust.ts +19 -0
  84. package/src/shared/session-tokens.ts +14 -3
  85. package/src/shared/settings.ts +16 -3
  86. package/src/shared/shortcuts.ts +17 -0
  87. package/src/shared/types.ts +166 -9
  88. package/src/shared/workflow-child-permit.ts +116 -0
  89. package/src/slash/delegation-adapters.ts +0 -1
  90. package/src/slash/slash-commands.ts +8 -6
  91. package/src/tui/fleet-status.ts +27 -10
  92. package/src/tui/fleet-transcript.ts +11 -5
  93. package/src/tui/fleet.ts +28 -13
  94. package/src/tui/render.ts +47 -21
  95. package/src/workflows/scripted-workflow.ts +298 -30
  96. package/src/workflows/workflow-child-summary.ts +117 -0
  97. package/src/workflows/workflow-receipt.ts +119 -4
@@ -33,6 +33,7 @@ import { resolveSubagentModelOverride, type ParentModel } from "../runs/shared/m
33
33
  import { validateToolBudgetConfig } from "../runs/shared/tool-budget.ts";
34
34
  import { resolveTurnBudgetConfig } from "../runs/shared/turn-budget.ts";
35
35
  import { validateAcceptanceInput } from "../runs/shared/acceptance.ts";
36
+ import { CODE_OWNED_EXTERNAL_CLI_ADAPTER_LABEL, isCodeOwnedExternalCliAdapterId, validateCodeOwnedProfileRunner } from "../runs/shared/external-cli-contract.ts";
36
37
  import type { AcceptanceInput, Details, ExtensionConfig, ToolBudgetConfig } from "../shared/types.ts";
37
38
  import { getProjectConfigDir } from "../shared/utils.ts";
38
39
  import { capabilityCeilingAgentRestrictionSources, isAgentAllowedByCapabilityCeiling, resolveCurrentSubagentCapabilityCeiling } from "../runs/shared/capability-ceiling.ts";
@@ -378,15 +379,17 @@ function applyAgentConfig(target: AgentConfig, cfg: Record<string, unknown>): st
378
379
  if (runner.type === "pi" && Object.keys(runner).every((key) => key === "type")) target.runner = { type: "pi" };
379
380
  else if (runner.type === "external-cli" && typeof runner.command === "string" && runner.command.trim()
380
381
  && (runner.args === undefined || (Array.isArray(runner.args) && runner.args.every((arg) => typeof arg === "string")))
382
+ && (runner.adapter === undefined || isCodeOwnedExternalCliAdapterId(runner.adapter))
383
+ && (runner.adapter === undefined || runner.args === undefined || runner.args.length === 0)
381
384
  && (runner.promptDelivery === undefined || runner.promptDelivery === "stdin")
382
- && Object.keys(runner).every((key) => ["type", "command", "args", "promptDelivery"].includes(key))) {
385
+ && Object.keys(runner).every((key) => ["type", "adapter", "command", "args", "promptDelivery"].includes(key))) {
383
386
  const runnerArgs = Array.isArray(runner.args) ? runner.args.filter((arg): arg is string => typeof arg === "string") : undefined;
384
- target.runner = { type: "external-cli", command: runner.command.trim(), ...(runnerArgs?.length ? { args: runnerArgs } : {}), ...(runner.promptDelivery ? { promptDelivery: "stdin" } : {}) };
387
+ target.runner = { type: "external-cli", ...(isCodeOwnedExternalCliAdapterId(runner.adapter) ? { adapter: runner.adapter } : {}), command: runner.command.trim(), ...(runnerArgs?.length ? { args: runnerArgs } : {}), ...(runner.promptDelivery ? { promptDelivery: "stdin" } : {}) };
385
388
  } else if (runner.type === "external-job" && typeof runner.provider === "string" && runner.provider.trim() === runner.provider && runner.provider
386
389
  && (runner.options === undefined || (runner.options && typeof runner.options === "object" && !Array.isArray(runner.options) && isJsonSerializable(runner.options)))
387
390
  && Object.keys(runner).every((key) => ["type", "provider", "options"].includes(key))) {
388
391
  target.runner = { type: "external-job", provider: runner.provider, ...(runner.options ? { options: runner.options as Record<string, unknown> } : {}) };
389
- } else return "config.runner must be { type: 'pi' }, { type: 'external-cli', command: string, args?: string[], promptDelivery?: 'stdin' }, or { type: 'external-job', provider: string, options?: object }.";
392
+ } else return `config.runner must be { type: 'pi' }, { type: 'external-cli', adapter?: ${CODE_OWNED_EXTERNAL_CLI_ADAPTER_LABEL}, command: string, args?: string[], promptDelivery?: 'stdin' }, or { type: 'external-job', provider: string, options?: object }.`;
390
393
  } else return "config.runner must be an object, false, or empty string when provided.";
391
394
  }
392
395
  if (hasKey(cfg, "model")) {
@@ -784,13 +787,14 @@ function handleModels(params: ManagementParams, ctx: ManagementContext): AgentTo
784
787
 
785
788
  const discovered = discoverAgentsAll(ctx.cwd);
786
789
  const builtinByName = new Map(discovered.builtin.map((agent) => [agent.name, agent]));
790
+ const resolveBuiltinModelAgent = (name: string): AgentConfig | undefined => builtinByName.get(name) ?? resolveAgentName(name, discovered.builtin).agent;
787
791
  const availableModels = ctx.modelRegistry.getAvailable().map(toModelInfo);
788
792
  const currentModel = ctx.model ? { provider: ctx.model.provider, id: ctx.model.id } : undefined;
789
793
  const preferredProvider = ctx.model?.provider;
790
794
  const names = requestedAgent ? [requestedAgent] : [...BUILTIN_AGENT_NAMES];
791
795
 
792
796
  if (requestedAgent) {
793
- const agent = builtinByName.get(requestedAgent);
797
+ const agent = resolveBuiltinModelAgent(requestedAgent);
794
798
  if (!agent) return result(`Builtin agent '${requestedAgent}' not found.`, true);
795
799
  const resolvedModel = resolveSubagentModelOverride(agent.model, currentModel, availableModels, preferredProvider);
796
800
  const lines = [
@@ -824,7 +828,7 @@ function handleModels(params: ManagementParams, ctx: ManagementContext): AgentTo
824
828
  ];
825
829
 
826
830
  for (const name of names) {
827
- const agent = builtinByName.get(name);
831
+ const agent = resolveBuiltinModelAgent(name);
828
832
  if (!agent) {
829
833
  lines.push(name);
830
834
  lines.push(" model:");
@@ -915,6 +919,8 @@ export function handleCreate(params: ManagementParams, ctx: ManagementContext):
915
919
  };
916
920
  const applyError = applyAgentConfig(agent, cfg);
917
921
  if (applyError) return result(applyError, true);
922
+ const profileError = validateCodeOwnedProfileRunner(agent);
923
+ if (profileError) return result(profileError, true);
918
924
  const mw = modelWarning(ctx, agent.model);
919
925
  if (mw) warnings.push(mw);
920
926
  const fmw = fallbackModelsWarning(ctx, agent.fallbackModels);
@@ -966,6 +972,8 @@ export function handleUpdate(params: ManagementParams, ctx: ManagementContext):
966
972
  if (newPackageName !== undefined) updated.packageName = newPackageName;
967
973
  else delete updated.packageName;
968
974
  updated.name = buildRuntimeName(newLocalName, newPackageName);
975
+ const profileError = validateCodeOwnedProfileRunner(updated);
976
+ if (profileError) return result(profileError, true);
969
977
  if (hasKey(cfg, "description")) updated.description = (cfg.description as string).trim();
970
978
  if (hasKey(cfg, "model")) {
971
979
  const mw = modelWarning(ctx, updated.model);
@@ -2,7 +2,7 @@ import { createHash, randomUUID } from "node:crypto";
2
2
  import * as fs from "node:fs";
3
3
  import * as path from "node:path";
4
4
  import type { AgentToolResult } from "@earendil-works/pi-agent-core";
5
- import { discoverAgents, resolveAgentName, type AgentConfig } from "./agents.ts";
5
+ import { discoverAgents, formatUnknownAgentError, resolveAgentName, unknownAgentDiagnosticContext, type AgentConfig } from "./agents.ts";
6
6
  import { getProjectSubagentsDir } from "../shared/artifacts.ts";
7
7
  import type { Details, JsonSchemaObject, SingleResult, SubagentState } from "../shared/types.ts";
8
8
 
@@ -536,10 +536,10 @@ function guidanceFromProposal(proposal: RefinementProposal): string {
536
536
  }
537
537
 
538
538
  function resolveOneAgent(cwd: string, agentName: string): { ok: true; agent: AgentConfig } | { ok: false; error: string } {
539
- const agents = discoverAgents(cwd, "both").agents;
540
- const resolved = resolveAgentName(agentName, agents);
539
+ const discovered = discoverAgents(cwd, "both");
540
+ const resolved = resolveAgentName(agentName, discovered.agents);
541
541
  if (resolved.error) return { ok: false, error: resolved.error };
542
- if (!resolved.agent) return { ok: false, error: `Unknown agent: ${agentName}` };
542
+ if (!resolved.agent) return { ok: false, error: formatUnknownAgentError(agentName, unknownAgentDiagnosticContext(discovered)) };
543
543
  return { ok: true, agent: resolved.agent };
544
544
  }
545
545
 
@@ -11,6 +11,7 @@ export const KNOWN_FIELDS = new Set([
11
11
  "tools",
12
12
  "model",
13
13
  "fallbackModels",
14
+ "fast",
14
15
  "thinking",
15
16
  "systemPromptMode",
16
17
  "inheritProjectContext",
@@ -71,6 +72,7 @@ export function serializeAgent(config: AgentConfig, options: SerializeAgentOptio
71
72
  if (config.model || preserve("model")) lines.push(`model: ${config.model ?? ""}`);
72
73
  const fallbackModelsValue = joinComma(config.fallbackModels);
73
74
  if (fallbackModelsValue || preserve("fallbackModels")) lines.push(`fallbackModels: ${fallbackModelsValue ?? ""}`);
75
+ if (config.fast === true || preserve("fast")) lines.push(`fast: ${config.fast === undefined ? "" : config.fast ? "true" : "false"}`);
74
76
  if ((config.thinking && (config.thinking !== "off" || preserve("thinking"))) || (!config.thinking && preserve("thinking"))) {
75
77
  lines.push(`thinking: ${config.thinking ?? ""}`);
76
78
  }
@@ -9,6 +9,7 @@ import * as os from "node:os";
9
9
  import * as path from "node:path";
10
10
  import { fileURLToPath } from "node:url";
11
11
  import type { AcceptanceInput, AcceptanceRole, AgentRunnerConfig, OutputMode, ToolBudgetConfig, TurnBudgetConfig } from "../shared/types.ts";
12
+ import { CODE_OWNED_EXTERNAL_CLI_ADAPTER_LABEL, isCodeOwnedExternalCliAdapterId, parseExternalCliCapabilityNarrowing, validateCodeOwnedProfileRunner } from "../runs/shared/external-cli-contract.ts";
12
13
  import { getAgentDir, getProjectConfigDir } from "../shared/utils.ts";
13
14
  import { KNOWN_FIELDS } from "./agent-serializer.ts";
14
15
  import { parseChain, parseJsonChain } from "./chain-serializer.ts";
@@ -57,6 +58,7 @@ export interface BuiltinAgentOverrideBase {
57
58
  model?: string;
58
59
  modelProvider?: string;
59
60
  fallbackModels?: string[];
61
+ fast?: boolean;
60
62
  thinking?: string | false;
61
63
  systemPromptMode: SystemPromptMode;
62
64
  inheritProjectContext: boolean;
@@ -83,6 +85,7 @@ interface BuiltinAgentOverrideConfig {
83
85
  model?: string | false;
84
86
  defaultProvider?: string | false;
85
87
  fallbackModels?: string[] | false;
88
+ fast?: boolean;
86
89
  thinking?: string | false;
87
90
  systemPromptMode?: SystemPromptMode;
88
91
  inheritProjectContext?: boolean;
@@ -128,6 +131,7 @@ export interface AgentConfig {
128
131
  model?: string;
129
132
  modelProvider?: string;
130
133
  fallbackModels?: string[];
134
+ fast?: boolean;
131
135
  thinking?: string | false;
132
136
  systemPromptMode: SystemPromptMode;
133
137
  inheritProjectContext: boolean;
@@ -259,14 +263,72 @@ export function findBlockingAgentDiagnostic(name: string, agent: AgentConfig | r
259
263
  return !agents.length || (match && agentDefinitionPriority(match) > highestPriority) ? match : undefined;
260
264
  }
261
265
 
262
- interface AgentDiscoveryResult {
266
+ export type AgentDefinitionDirectoryState = "absent" | "empty" | "candidates" | "unreadable" | "not-directory";
267
+
268
+ /** A definition directory actually inspected during one agent-discovery operation. */
269
+ export interface AgentDefinitionDirectoryReport {
270
+ source: AgentSource;
271
+ path: string;
272
+ state: AgentDefinitionDirectoryState;
273
+ candidateCount?: number;
274
+ }
275
+
276
+ /**
277
+ * Filesystem provenance for a failed agent resolution. Context is created from
278
+ * the discovery result that supplied the effective agents; callers must not
279
+ * pair arbitrary agent arrays with these directory reports.
280
+ */
281
+ export interface UnknownAgentDiagnosticContext {
282
+ cwd: string;
283
+ scope: AgentScope;
284
+ directories: readonly AgentDefinitionDirectoryReport[];
285
+ agents: readonly AgentConfig[];
286
+ }
287
+
288
+ export interface AgentDiscoveryResult {
263
289
  agents: AgentConfig[];
264
290
  agentDiagnostics?: AgentDiscoveryDiagnostic[];
265
291
  projectAgentsDir: string | null;
292
+ cwd: string;
293
+ scope: AgentScope;
294
+ directories: readonly AgentDefinitionDirectoryReport[];
266
295
  modelScope?: ModelScopeConfig;
267
296
  maxThinking?: ThinkingLevel;
268
297
  }
269
298
 
299
+ /** Create formatter input from the exact discovery operation used for resolution. */
300
+ export function unknownAgentDiagnosticContext(discovered: Pick<AgentDiscoveryResult, "cwd" | "scope" | "directories" | "agents">): UnknownAgentDiagnosticContext {
301
+ return {
302
+ cwd: discovered.cwd,
303
+ scope: discovered.scope,
304
+ directories: discovered.directories,
305
+ agents: discovered.agents,
306
+ };
307
+ }
308
+
309
+ /** Render local discovery evidence without exposing filesystem error details. */
310
+ export function formatUnknownAgentError(name: string, context: UnknownAgentDiagnosticContext, prefix = "Unknown agent"): string {
311
+ const directories = context.directories.map((directory) => {
312
+ const state = directory.state === "candidates"
313
+ ? `${directory.candidateCount ?? 0} candidate${directory.candidateCount === 1 ? "" : "s"}`
314
+ : directory.state === "empty" ? "present but empty"
315
+ : directory.state === "not-directory" ? "not a directory"
316
+ : directory.state;
317
+ return `- ${directory.source}: ${directory.path} (${state})`;
318
+ });
319
+ const agents = [...context.agents]
320
+ .sort((left, right) => left.name.localeCompare(right.name) || left.source.localeCompare(right.source))
321
+ .map((agent) => `- ${agent.name} (${agent.source})`);
322
+ return [
323
+ `${prefix}: ${name}`,
324
+ `Effective cwd: ${path.resolve(context.cwd)}`,
325
+ "Consulted agent-definition directories:",
326
+ ...(directories.length ? directories : ["- (none)"]),
327
+ "Discovered agents:",
328
+ ...(agents.length ? agents : ["- (none)"]),
329
+ ].join("\n");
330
+ }
331
+
270
332
  function getUserChainDir(): string {
271
333
  return path.join(getAgentDir(), "chains");
272
334
  }
@@ -378,6 +440,10 @@ function resolveSettingsPackageRoot(source: string, baseDir: string): string | u
378
440
  if (normalized === "." || normalized === ".." || normalized.startsWith("./") || normalized.startsWith("../")) {
379
441
  return path.resolve(baseDir, normalized);
380
442
  }
443
+ if (/^https?:\/\//i.test(trimmed)) {
444
+ const parsed = parseGitPackagePath(`git:${trimmed}`);
445
+ return parsed ? path.join(baseDir, "git", parsed.host, parsed.repoPath) : undefined;
446
+ }
381
447
  return undefined;
382
448
  }
383
449
 
@@ -640,6 +706,7 @@ function cloneOverrideBase(agent: AgentConfig): BuiltinAgentOverrideBase {
640
706
  ...(agent.model !== undefined ? { model: agent.model } : {}),
641
707
  ...(agent.modelProvider !== undefined ? { modelProvider: agent.modelProvider } : {}),
642
708
  ...(agent.fallbackModels ? { fallbackModels: [...agent.fallbackModels] } : {}),
709
+ ...(agent.fast !== undefined ? { fast: agent.fast } : {}),
643
710
  ...(agent.thinking !== undefined ? { thinking: agent.thinking } : {}),
644
711
  systemPromptMode: agent.systemPromptMode,
645
712
  inheritProjectContext: agent.inheritProjectContext,
@@ -670,6 +737,7 @@ function cloneOverrideValue(override: BuiltinAgentOverrideConfig): BuiltinAgentO
670
737
  ...(override.fallbackModels !== undefined
671
738
  ? { fallbackModels: override.fallbackModels === false ? false : [...override.fallbackModels] }
672
739
  : {}),
740
+ ...(override.fast !== undefined ? { fast: override.fast } : {}),
673
741
  ...(override.thinking !== undefined ? { thinking: override.thinking } : {}),
674
742
  ...(override.systemPromptMode !== undefined ? { systemPromptMode: override.systemPromptMode } : {}),
675
743
  ...(override.inheritProjectContext !== undefined ? { inheritProjectContext: override.inheritProjectContext } : {}),
@@ -864,6 +932,11 @@ function parseBuiltinOverrideEntry(
864
932
  else throw new Error(`Builtin override '${name}' in '${filePath}' has invalid 'model'; expected a string or false.`);
865
933
  }
866
934
 
935
+ if ("fast" in input) {
936
+ if (typeof input.fast === "boolean") override.fast = input.fast;
937
+ else throw new Error(`Builtin override '${name}' in '${filePath}' has invalid 'fast'; expected a boolean.`);
938
+ }
939
+
867
940
  if ("thinking" in input) {
868
941
  if (typeof input.thinking === "string" || input.thinking === false) override.thinking = input.thinking;
869
942
  else throw new Error(`Builtin override '${name}' in '${filePath}' has invalid 'thinking'; expected a string or false.`);
@@ -1195,6 +1268,7 @@ function applyBuiltinOverride(
1195
1268
  else next.modelProvider = override.defaultProvider;
1196
1269
  }
1197
1270
  if (override.fallbackModels !== undefined) { if (override.fallbackModels === false) delete next.fallbackModels; else next.fallbackModels = [...override.fallbackModels]; }
1271
+ if (override.fast !== undefined) next.fast = override.fast;
1198
1272
  if (override.thinking !== undefined) { if (override.thinking === false) delete next.thinking; else next.thinking = override.thinking; }
1199
1273
  if (override.systemPromptMode !== undefined) next.systemPromptMode = override.systemPromptMode;
1200
1274
  if (override.inheritProjectContext !== undefined) next.inheritProjectContext = override.inheritProjectContext;
@@ -1332,6 +1406,9 @@ function applyCustomAgentOverride(
1332
1406
  override.fallbackModels === false ? undefined : [...override.fallbackModels],
1333
1407
  );
1334
1408
  }
1409
+ if (override.fast !== undefined) {
1410
+ fill("fast", ["fast"], override.fast);
1411
+ }
1335
1412
  if (override.thinking !== undefined) {
1336
1413
  fill("thinking", ["thinking"], override.thinking === false ? undefined : override.thinking);
1337
1414
  }
@@ -1411,7 +1488,7 @@ function applyCustomAgentOverrides(
1411
1488
 
1412
1489
  export function buildBuiltinOverrideConfig(
1413
1490
  base: BuiltinAgentOverrideBase,
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">>,
1491
+ draft: Pick<AgentConfig, "model" | "modelProvider" | "fallbackModels" | "fast" | "thinking" | "systemPromptMode" | "inheritProjectContext" | "inheritSkills" | "defaultContext" | "acceptanceRole" | "disabled" | "systemPrompt" | "skills" | "tools" | "mcpDirectTools" | "extensions" | "subagentOnlyExtensions" | "completionGuard" | "toolBudget"> & Partial<Pick<AgentConfig, "description" | "output" | "outputMode" | "defaultReads">>,
1415
1492
  ): BuiltinAgentOverrideConfig | undefined {
1416
1493
  const override: BuiltinAgentOverrideConfig = {};
1417
1494
 
@@ -1425,6 +1502,7 @@ export function buildBuiltinOverrideConfig(
1425
1502
  if (draft.model !== base.model) override.model = draft.model ?? false;
1426
1503
  if (draft.modelProvider !== base.modelProvider) override.defaultProvider = draft.modelProvider ?? false;
1427
1504
  if (!arraysEqual(draft.fallbackModels, base.fallbackModels)) override.fallbackModels = draft.fallbackModels ? [...draft.fallbackModels] : false;
1505
+ if (draft.fast !== base.fast) override.fast = draft.fast === true;
1428
1506
  if (draft.thinking !== base.thinking) override.thinking = draft.thinking ?? false;
1429
1507
  if (draft.systemPromptMode !== base.systemPromptMode) override.systemPromptMode = draft.systemPromptMode;
1430
1508
  if (draft.inheritProjectContext !== base.inheritProjectContext) override.inheritProjectContext = draft.inheritProjectContext;
@@ -1583,9 +1661,22 @@ function shouldPruneDiscoveryDir(rootDir: string, dir: string, dirName: string):
1583
1661
  return path.resolve(dir) !== path.resolve(rootDir) && isDiscoveryNestedProjectRoot(dir);
1584
1662
  }
1585
1663
 
1586
- function listFilesRecursive(dir: string, predicate: (fileName: string) => boolean, rootDir = dir): string[] {
1664
+ function listFilesRecursive(
1665
+ dir: string,
1666
+ predicate: (fileName: string) => boolean,
1667
+ rootDir = dir,
1668
+ visitedDirectories = new Set<string>(),
1669
+ ): string[] {
1587
1670
  const files: string[] = [];
1588
1671
  if (!fs.existsSync(dir)) return files;
1672
+ let realDir: string;
1673
+ try {
1674
+ realDir = fs.realpathSync(dir);
1675
+ } catch {
1676
+ return files;
1677
+ }
1678
+ if (visitedDirectories.has(realDir)) return files;
1679
+ visitedDirectories.add(realDir);
1589
1680
 
1590
1681
  let entries: fs.Dirent[];
1591
1682
  try {
@@ -1596,9 +1687,17 @@ function listFilesRecursive(dir: string, predicate: (fileName: string) => boolea
1596
1687
 
1597
1688
  for (const entry of entries) {
1598
1689
  const filePath = path.join(dir, entry.name);
1599
- if (entry.isDirectory()) {
1690
+ let isDirectory = entry.isDirectory();
1691
+ if (entry.isSymbolicLink()) {
1692
+ try {
1693
+ isDirectory = fs.statSync(filePath).isDirectory();
1694
+ } catch {
1695
+ isDirectory = false;
1696
+ }
1697
+ }
1698
+ if (isDirectory) {
1600
1699
  if (!shouldPruneDiscoveryDir(rootDir, filePath, entry.name)) {
1601
- files.push(...listFilesRecursive(filePath, predicate, rootDir));
1700
+ files.push(...listFilesRecursive(filePath, predicate, rootDir, visitedDirectories));
1602
1701
  }
1603
1702
  continue;
1604
1703
  }
@@ -1609,6 +1708,81 @@ function listFilesRecursive(dir: string, predicate: (fileName: string) => boolea
1609
1708
  return files;
1610
1709
  }
1611
1710
 
1711
+ export interface AgentDefinitionInspection {
1712
+ files: string[];
1713
+ state: AgentDefinitionDirectoryState;
1714
+ }
1715
+
1716
+ /** Narrow filesystem seam so unavailable paths can be tested without permissions. */
1717
+ export interface AgentDefinitionInspectionFs {
1718
+ existsSync(filePath: string): boolean;
1719
+ realpathSync?(filePath: string): string;
1720
+ statSync(filePath: string): { isDirectory(): boolean };
1721
+ readdirSync(dir: string): fs.Dirent[];
1722
+ }
1723
+
1724
+ const DEFAULT_AGENT_DEFINITION_INSPECTION_FS: AgentDefinitionInspectionFs = {
1725
+ existsSync: fs.existsSync,
1726
+ realpathSync: fs.realpathSync,
1727
+ statSync: fs.statSync,
1728
+ readdirSync: (dir) => fs.readdirSync(dir, { withFileTypes: true }),
1729
+ };
1730
+
1731
+ /**
1732
+ * Inspect one agent-definition directory while retaining traversal failure
1733
+ * state. A nested unreadable directory makes the whole inspection unavailable,
1734
+ * preventing a partial traversal from being reported as empty or complete.
1735
+ */
1736
+ export function inspectAgentDefinitionDirectory(dir: string, operations: AgentDefinitionInspectionFs = DEFAULT_AGENT_DEFINITION_INSPECTION_FS): AgentDefinitionInspection {
1737
+ const root = path.resolve(dir);
1738
+ try {
1739
+ if (!operations.existsSync(root)) return { files: [], state: "absent" };
1740
+ if (!operations.statSync(root).isDirectory()) return { files: [], state: "not-directory" };
1741
+ } catch {
1742
+ return { files: [], state: "unreadable" };
1743
+ }
1744
+ const files: string[] = [];
1745
+ let unreadable = false;
1746
+ const visitedDirectories = new Set<string>();
1747
+ const visit = (current: string): void => {
1748
+ let realDir: string;
1749
+ try {
1750
+ realDir = operations.realpathSync?.(current) ?? path.resolve(current);
1751
+ } catch {
1752
+ unreadable = true;
1753
+ return;
1754
+ }
1755
+ if (visitedDirectories.has(realDir)) return;
1756
+ visitedDirectories.add(realDir);
1757
+
1758
+ let entries: fs.Dirent[];
1759
+ try {
1760
+ entries = operations.readdirSync(current).sort((a, b) => a.name.localeCompare(b.name));
1761
+ } catch {
1762
+ unreadable = true;
1763
+ return;
1764
+ }
1765
+ for (const entry of entries) {
1766
+ const filePath = path.join(current, entry.name);
1767
+ let isDirectory = entry.isDirectory();
1768
+ if (entry.isSymbolicLink()) {
1769
+ try {
1770
+ isDirectory = operations.statSync(filePath).isDirectory();
1771
+ } catch {
1772
+ isDirectory = false;
1773
+ }
1774
+ }
1775
+ if (isDirectory) {
1776
+ if (!shouldPruneDiscoveryDir(root, filePath, entry.name)) visit(filePath);
1777
+ continue;
1778
+ }
1779
+ if ((entry.isFile() || entry.isSymbolicLink()) && entry.name.endsWith(".md") && !entry.name.endsWith(".chain.md") && !isLegacyAgentSkillPath(root, filePath)) files.push(filePath);
1780
+ }
1781
+ };
1782
+ visit(root);
1783
+ return { files, state: unreadable ? "unreadable" : files.length ? "candidates" : "empty" };
1784
+ }
1785
+
1612
1786
  function isLegacyAgentSkillPath(rootDir: string, filePath: string): boolean {
1613
1787
  const relative = path.relative(rootDir, filePath);
1614
1788
  const parts = relative.split(path.sep).map((part) => part.toLowerCase());
@@ -1667,18 +1841,23 @@ function parseAgentRunnerFrontmatter(raw: string | undefined, agentName: string)
1667
1841
  if (runner.args !== undefined && (!Array.isArray(runner.args) || runner.args.some((arg) => typeof arg !== "string"))) {
1668
1842
  throw new Error(`Agent '${agentName}' external-cli runner args must be an array of strings.`);
1669
1843
  }
1844
+ if (runner.adapter !== undefined && !isCodeOwnedExternalCliAdapterId(runner.adapter)) throw new Error(`Agent '${agentName}' external-cli runner adapter must be ${CODE_OWNED_EXTERNAL_CLI_ADAPTER_LABEL}.`);
1845
+ if (runner.adapter !== undefined && Array.isArray(runner.args) && runner.args.length > 0) throw new Error(`Agent '${agentName}' ${runner.adapter} adapter owns its argv; runner args are not supported.`);
1670
1846
  if (runner.promptDelivery !== undefined && runner.promptDelivery !== "stdin") {
1671
1847
  throw new Error(`Agent '${agentName}' external-cli runner promptDelivery must be 'stdin'.`);
1672
1848
  }
1673
- const supported = new Set(["type", "command", "args", "promptDelivery"]);
1849
+ const capabilities = parseExternalCliCapabilityNarrowing(runner.capabilities, `Agent '${agentName}' external-cli runner capabilities`);
1850
+ const supported = new Set(["type", "adapter", "command", "args", "promptDelivery", "capabilities"]);
1674
1851
  const unknown = Object.keys(runner).filter((key) => !supported.has(key));
1675
1852
  if (unknown.length > 0) throw new Error(`Agent '${agentName}' external-cli runner has unsupported fields: ${unknown.join(", ")}.`);
1676
1853
  const runnerArgs = Array.isArray(runner.args) ? runner.args.filter((arg): arg is string => typeof arg === "string") : undefined;
1677
1854
  return {
1678
1855
  type: "external-cli",
1856
+ ...(isCodeOwnedExternalCliAdapterId(runner.adapter) ? { adapter: runner.adapter } : {}),
1679
1857
  command: runner.command.trim(),
1680
1858
  ...(runnerArgs?.length ? { args: runnerArgs } : {}),
1681
1859
  ...(runner.promptDelivery ? { promptDelivery: "stdin" as const } : {}),
1860
+ ...(capabilities ? { capabilities } : {}),
1682
1861
  };
1683
1862
  }
1684
1863
 
@@ -1709,9 +1888,9 @@ interface AgentDefinitionFile {
1709
1888
  content: string;
1710
1889
  }
1711
1890
 
1712
- function readAgentDefinitionFiles(dir: string): AgentDefinitionFile[] {
1891
+ function readAgentDefinitionFiles(dir: string, inspection = inspectAgentDefinitionDirectory(dir)): AgentDefinitionFile[] {
1713
1892
  const files: AgentDefinitionFile[] = [];
1714
- for (const filePath of listFilesRecursive(dir, (fileName) => fileName.endsWith(".md") && !fileName.endsWith(".chain.md"))) {
1893
+ for (const filePath of inspection.files) {
1715
1894
  if (isLegacyAgentSkillPath(dir, filePath)) {
1716
1895
  continue;
1717
1896
  }
@@ -1768,6 +1947,8 @@ function loadAgentsFromDefinitionFiles(files: AgentDefinitionFile[], source: Age
1768
1947
  const mcpDirectTools = parsedTools.mcpDirectTools ?? [];
1769
1948
  const defaultReads = parseFrontmatterList(frontmatter.defaultReads);
1770
1949
  const aliases = normalizeAgentAliases(parseFrontmatterList(frontmatter.aliases ?? frontmatter.alias), runtimeName);
1950
+ const profileError = validateCodeOwnedProfileRunner({ name: runtimeName, localName, aliases, runner });
1951
+ if (profileError) throw new Error(profileError);
1771
1952
  const skillStr = frontmatter.skill || frontmatter.skills;
1772
1953
  const skills = parseFrontmatterList(skillStr);
1773
1954
  const skillPath = parseFrontmatterList(frontmatter.skillPath);
@@ -1835,6 +2016,12 @@ function loadAgentsFromDefinitionFiles(files: AgentDefinitionFile[], source: Age
1835
2016
 
1836
2017
  const extensions = resolveAgentRelativeExtensionPaths(parseFrontmatterList(frontmatter.extensions), filePath);
1837
2018
  const subagentOnlyExtensions = resolveAgentRelativeExtensionPaths(parseFrontmatterList(frontmatter.subagentOnlyExtensions), filePath);
2019
+ let fast: boolean | undefined;
2020
+ if (frontmatter.fast !== undefined) {
2021
+ if (frontmatter.fast === "true") fast = true;
2022
+ else if (frontmatter.fast === "false") fast = false;
2023
+ else throw new Error(`Agent '${localName}' has invalid fast frontmatter; expected true or false.`);
2024
+ }
1838
2025
 
1839
2026
  const extraFields: Record<string, string> = {};
1840
2027
  for (const [key, value] of Object.entries(frontmatter)) {
@@ -1881,6 +2068,7 @@ function loadAgentsFromDefinitionFiles(files: AgentDefinitionFile[], source: Age
1881
2068
  ...(mcpDirectTools.length > 0 ? { mcpDirectTools } : {}),
1882
2069
  ...(frontmatter.model !== undefined ? { model: frontmatter.model } : {}),
1883
2070
  ...(fallbackModels?.length ? { fallbackModels } : {}),
2071
+ ...(fast !== undefined ? { fast } : {}),
1884
2072
  ...(frontmatter.thinking !== undefined ? { thinking: frontmatter.thinking === "false" ? false : frontmatter.thinking } : {}),
1885
2073
  systemPromptMode,
1886
2074
  inheritProjectContext,
@@ -1922,8 +2110,17 @@ function loadAgentsFromDefinitionFiles(files: AgentDefinitionFile[], source: Age
1922
2110
  return { agents, diagnostics };
1923
2111
  }
1924
2112
 
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);
2113
+ function loadAgentsFromDir(dir: string, source: AgentSource, discoveryPriority?: number, packageSource?: Omit<PackageSubagentPath, "dir">, inspection = inspectAgentDefinitionDirectory(dir)): { agents: AgentConfig[]; diagnostics: AgentDiscoveryDiagnostic[] } {
2114
+ return loadAgentsFromDefinitionFiles(readAgentDefinitionFiles(dir, inspection), source, discoveryPriority, packageSource);
2115
+ }
2116
+
2117
+ function reportAgentDefinitionDirectory(source: AgentSource, dir: string, inspection: AgentDefinitionInspection): AgentDefinitionDirectoryReport {
2118
+ return {
2119
+ source,
2120
+ path: path.resolve(dir),
2121
+ state: inspection.state,
2122
+ ...(inspection.state === "candidates" ? { candidateCount: inspection.files.length } : {}),
2123
+ };
1927
2124
  }
1928
2125
 
1929
2126
  function loadChainsFromDir(dir: string, source: AgentSource): { chains: ChainConfig[]; diagnostics: ChainDiscoveryDiagnostic[] } {
@@ -1960,20 +2157,18 @@ function isDirectory(p: string): boolean {
1960
2157
  }
1961
2158
  }
1962
2159
 
1963
- function resolveNearestProjectAgentDirs(cwd: string): { readDirs: string[]; preferredDir: string | null } {
2160
+ function resolveNearestProjectAgentDirs(cwd: string): { readDirs: string[]; candidateDirs: string[]; preferredDir: string | null } {
1964
2161
  const projectRoot = findConfiguredProjectRoot(cwd);
1965
- if (!projectRoot) return { readDirs: [], preferredDir: null };
2162
+ if (!projectRoot) return { readDirs: [], candidateDirs: [], preferredDir: null };
1966
2163
 
1967
2164
  const legacyDir = path.join(projectRoot, ".agents");
1968
2165
  const preferredDir = path.join(getProjectConfigDir(projectRoot), "agents");
2166
+ const candidateDirs = [legacyDir, preferredDir];
1969
2167
  const readDirs: string[] = [];
1970
2168
  if (isDirectory(legacyDir)) readDirs.push(legacyDir);
1971
2169
  if (isDirectory(preferredDir)) readDirs.push(preferredDir);
1972
2170
 
1973
- return {
1974
- readDirs,
1975
- preferredDir,
1976
- };
2171
+ return { readDirs, candidateDirs, preferredDir };
1977
2172
  }
1978
2173
 
1979
2174
  function resolveNearestProjectChainDirs(cwd: string): { readDirs: string[]; preferredDir: string | null } {
@@ -1987,7 +2182,9 @@ function resolveNearestProjectChainDirs(cwd: string): { readDirs: string[]; pref
1987
2182
  };
1988
2183
  }
1989
2184
  const BUILTIN_AGENTS_DIR = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..", "..", "agents");
1990
- const BUILTIN_AGENT_DEFINITION_FILES = readAgentDefinitionFiles(BUILTIN_AGENTS_DIR);
2185
+ // Candidate files and inspection state must describe the same cached builtin scan.
2186
+ const BUILTIN_AGENT_DEFINITION_INSPECTION = inspectAgentDefinitionDirectory(BUILTIN_AGENTS_DIR);
2187
+ const BUILTIN_AGENT_DEFINITION_FILES = readAgentDefinitionFiles(BUILTIN_AGENTS_DIR, BUILTIN_AGENT_DEFINITION_INSPECTION);
1991
2188
 
1992
2189
  export const EXTRA_AGENT_DIRS_ENV = "PI_SUBAGENT_EXTRA_AGENT_DIRS";
1993
2190
 
@@ -2006,11 +2203,12 @@ function extraUserAgentDirs(): string[] {
2006
2203
  }
2007
2204
 
2008
2205
  export function discoverAgents(cwd: string, scope: AgentScope): AgentDiscoveryResult {
2206
+ const effectiveCwd = path.resolve(cwd);
2009
2207
  const userDirOld = path.join(getAgentDir(), "agents");
2010
2208
  const userDirNew = path.join(os.homedir(), ".agents");
2011
- const { readDirs: projectAgentDirs, preferredDir: projectAgentsDir } = resolveNearestProjectAgentDirs(cwd);
2209
+ const { readDirs: projectAgentDirs, candidateDirs: projectCandidateDirs, preferredDir: projectAgentsDir } = resolveNearestProjectAgentDirs(effectiveCwd);
2012
2210
  const userSettingsPath = getUserAgentSettingsPath();
2013
- const projectSettingsPath = getProjectAgentSettingsPath(cwd);
2211
+ const projectSettingsPath = getProjectAgentSettingsPath(effectiveCwd);
2014
2212
  const userSettings = scope === "project" ? EMPTY_SUBAGENT_SETTINGS : readSubagentSettings(userSettingsPath);
2015
2213
  const projectSettings = scope === "user" ? EMPTY_SUBAGENT_SETTINGS : readSubagentSettings(projectSettingsPath);
2016
2214
  const defaultProvider = resolveSubagentDefaultProvider(userSettings, projectSettings, projectSettingsPath);
@@ -2019,65 +2217,60 @@ export function discoverAgents(cwd: string, scope: AgentScope): AgentDiscoveryRe
2019
2217
  const maxThinking = resolveSubagentMaxThinking(userSettings, projectSettings, projectSettingsPath);
2020
2218
  const defaultExtensions = resolveSubagentDefaultExtensions(userSettings, projectSettings, projectSettingsPath);
2021
2219
  const modelScope = projectSettings.modelScope ?? userSettings.modelScope;
2022
- const packageSubagentPaths = collectPackageSubagentPaths(cwd, {
2220
+ const packageSubagentPaths = collectPackageSubagentPaths(effectiveCwd, {
2023
2221
  includeUser: scope !== "project",
2024
2222
  includeProject: scope !== "user",
2025
2223
  });
2224
+ const directories: AgentDefinitionDirectoryReport[] = [reportAgentDefinitionDirectory("builtin", BUILTIN_AGENTS_DIR, BUILTIN_AGENT_DEFINITION_INSPECTION)];
2026
2225
 
2027
2226
  const builtinLoaded = loadAgentsFromDefinitionFiles(BUILTIN_AGENT_DEFINITION_FILES, "builtin");
2028
2227
  const builtinAgents = applyBuiltinOverrides(
2029
2228
  applySubagentDefaults(builtinLoaded.agents, defaultModel, defaultProvider, defaultThinking, defaultExtensions),
2030
- userSettings,
2031
- projectSettings,
2032
- userSettingsPath,
2033
- projectSettingsPath,
2229
+ userSettings, projectSettings, userSettingsPath, projectSettingsPath,
2034
2230
  );
2035
2231
 
2036
- const userLoaded = scope === "project" ? [] : [...extraUserAgentDirs(), userDirOld, userDirNew]
2037
- .map((dir, discoveryPriority) => loadAgentsFromDir(dir, "user", discoveryPriority));
2232
+ const userLoaded = scope === "project" ? [] : [...extraUserAgentDirs(), userDirOld, userDirNew].map((dir, discoveryPriority) => {
2233
+ const inspection = inspectAgentDefinitionDirectory(dir);
2234
+ directories.push(reportAgentDefinitionDirectory("user", dir, inspection));
2235
+ return loadAgentsFromDir(dir, "user", discoveryPriority, undefined, inspection);
2236
+ });
2038
2237
  const userAgents = applyCustomAgentOverrides(
2039
2238
  applySubagentDefaults(userLoaded.flatMap((loaded) => loaded.agents), defaultModel, defaultProvider, defaultThinking, defaultExtensions),
2040
- userSettings,
2041
- projectSettings,
2042
- userSettingsPath,
2043
- projectSettingsPath,
2239
+ userSettings, projectSettings, userSettingsPath, projectSettingsPath,
2044
2240
  );
2045
2241
 
2046
- const projectLoaded = scope === "user" ? [] : projectAgentDirs.map((dir) => loadAgentsFromDir(dir, "project", dir === projectAgentsDir ? 1 : 0));
2242
+ // Report both legacy and preferred project definition paths only after the
2243
+ // existing configured-root resolver found a root; never synthesize cwd paths.
2244
+ const projectInspections = scope === "user" ? new Map<string, AgentDefinitionInspection>() : new Map(projectCandidateDirs.map((dir) => [dir, inspectAgentDefinitionDirectory(dir)]));
2245
+ if (scope !== "user") for (const dir of projectCandidateDirs) directories.push(reportAgentDefinitionDirectory("project", dir, projectInspections.get(dir)!));
2246
+ const projectLoaded = scope === "user" ? [] : projectAgentDirs.map((dir) => loadAgentsFromDir(dir, "project", dir === projectAgentsDir ? 1 : 0, undefined, projectInspections.get(dir)!));
2047
2247
  const projectAgents = applyCustomAgentOverrides(
2048
2248
  applySubagentDefaults(projectLoaded.flatMap((loaded) => loaded.agents), defaultModel, defaultProvider, defaultThinking, defaultExtensions),
2049
- userSettings,
2050
- projectSettings,
2051
- userSettingsPath,
2052
- projectSettingsPath,
2249
+ userSettings, projectSettings, userSettingsPath, projectSettingsPath,
2053
2250
  );
2054
- const packageLoaded = packageSubagentPaths.agents.map((entry, index) => loadAgentsFromDir(entry.dir, "package", packageSubagentPaths.agents.length - index, entry));
2251
+
2252
+ const packageLoaded = packageSubagentPaths.agents.map((entry, index) => {
2253
+ const inspection = inspectAgentDefinitionDirectory(entry.dir);
2254
+ directories.push(reportAgentDefinitionDirectory("package", entry.dir, inspection));
2255
+ return loadAgentsFromDir(entry.dir, "package", packageSubagentPaths.agents.length - index, entry, inspection);
2256
+ });
2055
2257
  const packageMap = new Map<string, AgentConfig>();
2056
- for (const loaded of packageLoaded) {
2057
- for (const agent of loaded.agents) {
2058
- if (!packageMap.has(agent.name)) packageMap.set(agent.name, agent);
2059
- }
2060
- }
2258
+ for (const loaded of packageLoaded) for (const agent of loaded.agents) if (!packageMap.has(agent.name)) packageMap.set(agent.name, agent);
2061
2259
  const packageAgents = applyCustomAgentOverrides(
2062
2260
  applySubagentDefaults(Array.from(packageMap.values()), defaultModel, defaultProvider, defaultThinking, defaultExtensions),
2063
- userSettings,
2064
- projectSettings,
2065
- userSettingsPath,
2066
- projectSettingsPath,
2261
+ userSettings, projectSettings, userSettingsPath, projectSettingsPath,
2067
2262
  );
2068
2263
  const agents = applySubagentMaxThinking(
2069
- mergeAgentsForScope(scope, userAgents, projectAgents, builtinAgents, packageAgents)
2070
- .filter((agent) => agent.disabled !== true),
2264
+ mergeAgentsForScope(scope, userAgents, projectAgents, builtinAgents, packageAgents).filter((agent) => agent.disabled !== true),
2071
2265
  maxThinking,
2072
2266
  );
2073
-
2074
2267
  const agentDiagnostics = [
2075
2268
  ...builtinLoaded.diagnostics,
2076
2269
  ...userLoaded.flatMap((loaded) => loaded.diagnostics),
2077
2270
  ...projectLoaded.flatMap((loaded) => loaded.diagnostics),
2078
2271
  ...packageLoaded.flatMap((loaded) => loaded.diagnostics),
2079
2272
  ];
2080
- return { agents, agentDiagnostics, projectAgentsDir, ...(modelScope !== undefined ? { modelScope } : {}), ...(maxThinking !== undefined ? { maxThinking } : {}) };
2273
+ return { agents, agentDiagnostics, projectAgentsDir, cwd: effectiveCwd, scope, directories, ...(modelScope !== undefined ? { modelScope } : {}), ...(maxThinking !== undefined ? { maxThinking } : {}) };
2081
2274
  }
2082
2275
 
2083
2276
  export function discoverAgentsAll(cwd: string): {
@@ -1,5 +1,11 @@
1
1
  export const BUILTIN_AGENT_NAMES = [
2
2
  "advisor",
3
+ "claude-code",
4
+ "claude-code-writer",
5
+ "codex-exec",
6
+ "codex-exec-writer",
7
+ "cursor-agent",
8
+ "cursor-agent-writer",
3
9
  "delegate",
4
10
  "oracle",
5
11
  "researcher",