pi-subagents 0.56.0 → 0.58.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.
- package/CHANGELOG.md +92 -0
- package/agents/claude-code-writer.md +15 -0
- package/agents/claude-code.md +15 -0
- package/agents/codex-exec-writer.md +15 -0
- package/agents/codex-exec.md +15 -0
- package/agents/cursor-agent-writer.md +14 -0
- package/agents/cursor-agent.md +14 -0
- package/docs/agents.md +124 -21
- package/docs/configuration.md +37 -0
- package/docs/extension-api.md +41 -2
- package/docs/models.md +3 -3
- package/docs/observability.md +8 -7
- package/docs/tool-reference.md +14 -3
- package/docs/workflows.md +44 -0
- package/package.json +1 -1
- package/skills/pi-subagents/SKILL.md +2 -0
- package/skills/pi-subagents/references/execution-controls.md +2 -2
- package/skills/pi-subagents/references/management-authoring-rpc.md +1 -0
- package/skills/pi-subagents/references/prompting-and-roles.md +2 -2
- package/src/agents/agent-management.ts +36 -5
- package/src/agents/agent-refinements.ts +4 -4
- package/src/agents/agent-serializer.ts +5 -0
- package/src/agents/agents.ts +257 -51
- package/src/agents/builtin-names.ts +6 -0
- package/src/agents/runtime-agent-events.ts +70 -0
- package/src/agents/runtime-agent-registry.ts +18 -4
- package/src/api/agents.ts +10 -5
- package/src/api/preflight.ts +28 -3
- package/src/extension/config.ts +70 -0
- package/src/extension/doctor.ts +3 -3
- package/src/extension/index.ts +33 -8
- package/src/extension/public-execution.ts +29 -13
- package/src/extension/rpc.ts +55 -19
- package/src/extension/schemas.ts +6 -5
- package/src/extension/tool-description.ts +18 -12
- package/src/inspectors/herdr/actions.ts +2 -1
- package/src/inspectors/herdr/inspector-runner.ts +2 -10
- package/src/inspectors/herdr/session-roots-codec.ts +42 -0
- package/src/integrations/herdr-status.ts +51 -3
- package/src/runs/background/active-async-capacity.ts +77 -10
- package/src/runs/background/async-execution.ts +127 -19
- package/src/runs/background/async-job-tracker.ts +5 -0
- package/src/runs/background/async-resume.ts +6 -2
- package/src/runs/background/async-retention.ts +20 -3
- package/src/runs/background/async-status.ts +7 -0
- package/src/runs/background/chain-append.ts +2 -0
- package/src/runs/background/chain-root-attachment.ts +15 -1
- package/src/runs/background/fleet-view.ts +16 -10
- package/src/runs/background/inspect-rpc.ts +8 -8
- package/src/runs/background/notify.ts +26 -3
- package/src/runs/background/result-delivery-ownership.ts +45 -0
- package/src/runs/background/result-files.ts +27 -14
- package/src/runs/background/result-watcher.ts +36 -15
- package/src/runs/background/run-status.ts +33 -6
- package/src/runs/background/scheduled-runs.ts +7 -1
- package/src/runs/background/subagent-runner.ts +239 -56
- package/src/runs/background/wait-completions.ts +4 -0
- package/src/runs/foreground/execution.ts +92 -11
- package/src/runs/foreground/foreground-control.ts +6 -0
- package/src/runs/foreground/foreground-history.ts +22 -1
- package/src/runs/foreground/subagent-executor.ts +322 -102
- package/src/runs/foreground/workflow-detach-reconcile.ts +144 -18
- package/src/runs/shared/child-protocol.ts +21 -7
- package/src/runs/shared/claude-code-adapter.ts +129 -0
- package/src/runs/shared/codex-exec-adapter.ts +129 -0
- package/src/runs/shared/completion-guard.ts +4 -3
- package/src/runs/shared/cursor-agent-adapter.ts +114 -0
- package/src/runs/shared/dynamic-fanout.ts +3 -3
- package/src/runs/shared/external-cli-contract.ts +167 -0
- package/src/runs/shared/external-cli-preflight.ts +122 -0
- package/src/runs/shared/external-cli-runner.ts +348 -55
- package/src/runs/shared/fast-mode-extension.ts +5 -5
- package/src/runs/shared/launch-cwd.ts +16 -0
- package/src/runs/shared/long-running-guard.ts +2 -1
- package/src/runs/shared/mcp-config-sources.ts +386 -0
- package/src/runs/shared/mcp-direct-tool-allowlist.ts +155 -42
- package/src/runs/shared/model-exclusions.ts +69 -7
- package/src/runs/shared/model-fallback.ts +39 -5
- package/src/runs/shared/mutation-evidence.ts +7 -2
- package/src/runs/shared/nested-events.ts +3 -1
- package/src/runs/shared/nested-render.ts +2 -2
- package/src/runs/shared/parallel-utils.ts +8 -1
- package/src/runs/shared/pi-args.ts +61 -6
- package/src/runs/shared/process-signal.ts +13 -0
- package/src/runs/shared/run-history.ts +21 -1
- package/src/runs/shared/single-output.ts +17 -0
- package/src/runs/shared/subagent-prompt-runtime.ts +85 -9
- package/src/shared/fork-context.ts +21 -0
- package/src/shared/formatters.ts +13 -1
- package/src/shared/launch-contract.ts +4 -0
- package/src/shared/pruned-fork.ts +450 -0
- package/src/shared/session-file-trust.ts +19 -0
- package/src/shared/session-tokens.ts +14 -3
- package/src/shared/settings.ts +10 -2
- package/src/shared/shortcuts.ts +17 -0
- package/src/shared/types.ts +160 -10
- package/src/shared/utils.ts +6 -29
- package/src/shared/workflow-child-permit.ts +116 -0
- package/src/slash/delegation-adapters.ts +0 -1
- package/src/slash/slash-commands.ts +8 -6
- package/src/slash/subagents-admin.ts +3 -0
- package/src/tui/fleet-status.ts +27 -10
- package/src/tui/fleet-transcript.ts +11 -5
- package/src/tui/fleet.ts +28 -13
- package/src/tui/render.ts +55 -21
- package/src/workflows/scripted-workflow.ts +299 -31
- package/src/workflows/workflow-child-summary.ts +117 -0
- package/src/workflows/workflow-receipt.ts +155 -5
package/src/agents/agents.ts
CHANGED
|
@@ -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";
|
|
@@ -61,6 +62,7 @@ export interface BuiltinAgentOverrideBase {
|
|
|
61
62
|
thinking?: string | false;
|
|
62
63
|
systemPromptMode: SystemPromptMode;
|
|
63
64
|
inheritProjectContext: boolean;
|
|
65
|
+
inheritGlobalContext: boolean;
|
|
64
66
|
inheritSkills: boolean;
|
|
65
67
|
defaultContext?: AgentDefaultContext;
|
|
66
68
|
acceptanceRole?: AcceptanceRole;
|
|
@@ -72,6 +74,7 @@ export interface BuiltinAgentOverrideBase {
|
|
|
72
74
|
mcpDirectTools?: string[];
|
|
73
75
|
extensions?: string[];
|
|
74
76
|
subagentOnlyExtensions?: string[];
|
|
77
|
+
mutationTools?: string[];
|
|
75
78
|
completionGuard?: boolean;
|
|
76
79
|
toolBudget?: ToolBudgetConfig;
|
|
77
80
|
}
|
|
@@ -88,6 +91,7 @@ interface BuiltinAgentOverrideConfig {
|
|
|
88
91
|
thinking?: string | false;
|
|
89
92
|
systemPromptMode?: SystemPromptMode;
|
|
90
93
|
inheritProjectContext?: boolean;
|
|
94
|
+
inheritGlobalContext?: boolean;
|
|
91
95
|
inheritSkills?: boolean;
|
|
92
96
|
defaultContext?: AgentDefaultContext | false;
|
|
93
97
|
acceptanceRole?: AcceptanceRole | false;
|
|
@@ -97,6 +101,7 @@ interface BuiltinAgentOverrideConfig {
|
|
|
97
101
|
tools?: string[] | false | "inherit";
|
|
98
102
|
extensions?: string[] | false;
|
|
99
103
|
subagentOnlyExtensions?: string[] | false;
|
|
104
|
+
mutationTools?: string[] | false;
|
|
100
105
|
completionGuard?: boolean;
|
|
101
106
|
toolBudget?: ToolBudgetConfig | false;
|
|
102
107
|
}
|
|
@@ -134,6 +139,7 @@ export interface AgentConfig {
|
|
|
134
139
|
thinking?: string | false;
|
|
135
140
|
systemPromptMode: SystemPromptMode;
|
|
136
141
|
inheritProjectContext: boolean;
|
|
142
|
+
inheritGlobalContext: boolean;
|
|
137
143
|
inheritSkills: boolean;
|
|
138
144
|
defaultContext?: AgentDefaultContext;
|
|
139
145
|
defaultAsync?: boolean;
|
|
@@ -151,6 +157,7 @@ export interface AgentConfig {
|
|
|
151
157
|
extensions?: string[];
|
|
152
158
|
extensionsFromDefault?: boolean;
|
|
153
159
|
subagentOnlyExtensions?: string[];
|
|
160
|
+
mutationTools?: string[];
|
|
154
161
|
output?: string;
|
|
155
162
|
outputMode?: OutputMode;
|
|
156
163
|
defaultReads?: string[];
|
|
@@ -262,14 +269,72 @@ export function findBlockingAgentDiagnostic(name: string, agent: AgentConfig | r
|
|
|
262
269
|
return !agents.length || (match && agentDefinitionPriority(match) > highestPriority) ? match : undefined;
|
|
263
270
|
}
|
|
264
271
|
|
|
265
|
-
|
|
272
|
+
export type AgentDefinitionDirectoryState = "absent" | "empty" | "candidates" | "unreadable" | "not-directory";
|
|
273
|
+
|
|
274
|
+
/** A definition directory actually inspected during one agent-discovery operation. */
|
|
275
|
+
export interface AgentDefinitionDirectoryReport {
|
|
276
|
+
source: AgentSource;
|
|
277
|
+
path: string;
|
|
278
|
+
state: AgentDefinitionDirectoryState;
|
|
279
|
+
candidateCount?: number;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Filesystem provenance for a failed agent resolution. Context is created from
|
|
284
|
+
* the discovery result that supplied the effective agents; callers must not
|
|
285
|
+
* pair arbitrary agent arrays with these directory reports.
|
|
286
|
+
*/
|
|
287
|
+
export interface UnknownAgentDiagnosticContext {
|
|
288
|
+
cwd: string;
|
|
289
|
+
scope: AgentScope;
|
|
290
|
+
directories: readonly AgentDefinitionDirectoryReport[];
|
|
291
|
+
agents: readonly AgentConfig[];
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
export interface AgentDiscoveryResult {
|
|
266
295
|
agents: AgentConfig[];
|
|
267
296
|
agentDiagnostics?: AgentDiscoveryDiagnostic[];
|
|
268
297
|
projectAgentsDir: string | null;
|
|
298
|
+
cwd: string;
|
|
299
|
+
scope: AgentScope;
|
|
300
|
+
directories: readonly AgentDefinitionDirectoryReport[];
|
|
269
301
|
modelScope?: ModelScopeConfig;
|
|
270
302
|
maxThinking?: ThinkingLevel;
|
|
271
303
|
}
|
|
272
304
|
|
|
305
|
+
/** Create formatter input from the exact discovery operation used for resolution. */
|
|
306
|
+
export function unknownAgentDiagnosticContext(discovered: Pick<AgentDiscoveryResult, "cwd" | "scope" | "directories" | "agents">): UnknownAgentDiagnosticContext {
|
|
307
|
+
return {
|
|
308
|
+
cwd: discovered.cwd,
|
|
309
|
+
scope: discovered.scope,
|
|
310
|
+
directories: discovered.directories,
|
|
311
|
+
agents: discovered.agents,
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/** Render local discovery evidence without exposing filesystem error details. */
|
|
316
|
+
export function formatUnknownAgentError(name: string, context: UnknownAgentDiagnosticContext, prefix = "Unknown agent"): string {
|
|
317
|
+
const directories = context.directories.map((directory) => {
|
|
318
|
+
const state = directory.state === "candidates"
|
|
319
|
+
? `${directory.candidateCount ?? 0} candidate${directory.candidateCount === 1 ? "" : "s"}`
|
|
320
|
+
: directory.state === "empty" ? "present but empty"
|
|
321
|
+
: directory.state === "not-directory" ? "not a directory"
|
|
322
|
+
: directory.state;
|
|
323
|
+
return `- ${directory.source}: ${directory.path} (${state})`;
|
|
324
|
+
});
|
|
325
|
+
const agents = [...context.agents]
|
|
326
|
+
.sort((left, right) => left.name.localeCompare(right.name) || left.source.localeCompare(right.source))
|
|
327
|
+
.map((agent) => `- ${agent.name} (${agent.source})`);
|
|
328
|
+
return [
|
|
329
|
+
`${prefix}: ${name}`,
|
|
330
|
+
`Effective cwd: ${path.resolve(context.cwd)}`,
|
|
331
|
+
"Consulted agent-definition directories:",
|
|
332
|
+
...(directories.length ? directories : ["- (none)"]),
|
|
333
|
+
"Discovered agents:",
|
|
334
|
+
...(agents.length ? agents : ["- (none)"]),
|
|
335
|
+
].join("\n");
|
|
336
|
+
}
|
|
337
|
+
|
|
273
338
|
function getUserChainDir(): string {
|
|
274
339
|
return path.join(getAgentDir(), "chains");
|
|
275
340
|
}
|
|
@@ -381,6 +446,10 @@ function resolveSettingsPackageRoot(source: string, baseDir: string): string | u
|
|
|
381
446
|
if (normalized === "." || normalized === ".." || normalized.startsWith("./") || normalized.startsWith("../")) {
|
|
382
447
|
return path.resolve(baseDir, normalized);
|
|
383
448
|
}
|
|
449
|
+
if (/^https?:\/\//i.test(trimmed)) {
|
|
450
|
+
const parsed = parseGitPackagePath(`git:${trimmed}`);
|
|
451
|
+
return parsed ? path.join(baseDir, "git", parsed.host, parsed.repoPath) : undefined;
|
|
452
|
+
}
|
|
384
453
|
return undefined;
|
|
385
454
|
}
|
|
386
455
|
|
|
@@ -647,6 +716,7 @@ function cloneOverrideBase(agent: AgentConfig): BuiltinAgentOverrideBase {
|
|
|
647
716
|
...(agent.thinking !== undefined ? { thinking: agent.thinking } : {}),
|
|
648
717
|
systemPromptMode: agent.systemPromptMode,
|
|
649
718
|
inheritProjectContext: agent.inheritProjectContext,
|
|
719
|
+
inheritGlobalContext: agent.inheritGlobalContext,
|
|
650
720
|
inheritSkills: agent.inheritSkills,
|
|
651
721
|
...(agent.defaultContext !== undefined ? { defaultContext: agent.defaultContext } : {}),
|
|
652
722
|
...(agent.acceptanceRole !== undefined ? { acceptanceRole: agent.acceptanceRole } : {}),
|
|
@@ -658,6 +728,7 @@ function cloneOverrideBase(agent: AgentConfig): BuiltinAgentOverrideBase {
|
|
|
658
728
|
...(agent.mcpDirectTools ? { mcpDirectTools: [...agent.mcpDirectTools] } : {}),
|
|
659
729
|
...(!agent.extensionsFromDefault && agent.extensions ? { extensions: [...agent.extensions] } : {}),
|
|
660
730
|
...(agent.subagentOnlyExtensions ? { subagentOnlyExtensions: [...agent.subagentOnlyExtensions] } : {}),
|
|
731
|
+
...(agent.mutationTools ? { mutationTools: [...agent.mutationTools] } : {}),
|
|
661
732
|
...(agent.completionGuard !== undefined ? { completionGuard: agent.completionGuard } : {}),
|
|
662
733
|
...(agent.toolBudget !== undefined ? { toolBudget: agent.toolBudget } : {}),
|
|
663
734
|
};
|
|
@@ -678,6 +749,7 @@ function cloneOverrideValue(override: BuiltinAgentOverrideConfig): BuiltinAgentO
|
|
|
678
749
|
...(override.thinking !== undefined ? { thinking: override.thinking } : {}),
|
|
679
750
|
...(override.systemPromptMode !== undefined ? { systemPromptMode: override.systemPromptMode } : {}),
|
|
680
751
|
...(override.inheritProjectContext !== undefined ? { inheritProjectContext: override.inheritProjectContext } : {}),
|
|
752
|
+
...(override.inheritGlobalContext !== undefined ? { inheritGlobalContext: override.inheritGlobalContext } : {}),
|
|
681
753
|
...(override.inheritSkills !== undefined ? { inheritSkills: override.inheritSkills } : {}),
|
|
682
754
|
...(override.defaultContext !== undefined ? { defaultContext: override.defaultContext } : {}),
|
|
683
755
|
...(override.acceptanceRole !== undefined ? { acceptanceRole: override.acceptanceRole } : {}),
|
|
@@ -687,6 +759,7 @@ function cloneOverrideValue(override: BuiltinAgentOverrideConfig): BuiltinAgentO
|
|
|
687
759
|
...(override.tools !== undefined ? { tools: Array.isArray(override.tools) ? [...override.tools] : override.tools } : {}),
|
|
688
760
|
...(override.extensions !== undefined ? { extensions: override.extensions === false ? false : [...override.extensions] } : {}),
|
|
689
761
|
...(override.subagentOnlyExtensions !== undefined ? { subagentOnlyExtensions: override.subagentOnlyExtensions === false ? false : [...override.subagentOnlyExtensions] } : {}),
|
|
762
|
+
...(override.mutationTools !== undefined ? { mutationTools: override.mutationTools === false ? false : [...override.mutationTools] } : {}),
|
|
690
763
|
...(override.completionGuard !== undefined ? { completionGuard: override.completionGuard } : {}),
|
|
691
764
|
...(override.toolBudget !== undefined ? { toolBudget: override.toolBudget === false ? false : { ...override.toolBudget, ...(Array.isArray(override.toolBudget.block) ? { block: [...override.toolBudget.block] } : {}) } } : {}),
|
|
692
765
|
};
|
|
@@ -736,7 +809,7 @@ export function findNearestProjectRoot(cwd: string): string | null {
|
|
|
736
809
|
return findProjectRootCandidates(cwd)[0] ?? null;
|
|
737
810
|
}
|
|
738
811
|
|
|
739
|
-
function findConfiguredProjectRoot(cwd: string): string | null {
|
|
812
|
+
export function findConfiguredProjectRoot(cwd: string): string | null {
|
|
740
813
|
const candidates = findProjectRootCandidates(cwd);
|
|
741
814
|
const nearestRoot = candidates[0];
|
|
742
815
|
if (!nearestRoot) return null;
|
|
@@ -895,6 +968,14 @@ function parseBuiltinOverrideEntry(
|
|
|
895
968
|
}
|
|
896
969
|
}
|
|
897
970
|
|
|
971
|
+
if ("inheritGlobalContext" in input) {
|
|
972
|
+
if (typeof input.inheritGlobalContext === "boolean") {
|
|
973
|
+
override.inheritGlobalContext = input.inheritGlobalContext;
|
|
974
|
+
} else {
|
|
975
|
+
throw new Error(`Builtin override '${name}' in '${filePath}' has invalid 'inheritGlobalContext'; expected a boolean.`);
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
|
|
898
979
|
if ("inheritSkills" in input) {
|
|
899
980
|
if (typeof input.inheritSkills === "boolean") {
|
|
900
981
|
override.inheritSkills = input.inheritSkills;
|
|
@@ -974,6 +1055,9 @@ function parseBuiltinOverrideEntry(
|
|
|
974
1055
|
const subagentOnlyExtensions = parseOverrideStringArrayOrFalse(input.subagentOnlyExtensions, { filePath, name, field: "subagentOnlyExtensions" });
|
|
975
1056
|
if (subagentOnlyExtensions !== undefined) override.subagentOnlyExtensions = subagentOnlyExtensions;
|
|
976
1057
|
|
|
1058
|
+
const mutationTools = parseOverrideStringArrayOrFalse(input.mutationTools, { filePath, name, field: "mutationTools" });
|
|
1059
|
+
if (mutationTools !== undefined) override.mutationTools = mutationTools;
|
|
1060
|
+
|
|
977
1061
|
return Object.keys(override).length > 0 ? override : undefined;
|
|
978
1062
|
}
|
|
979
1063
|
|
|
@@ -1209,6 +1293,7 @@ function applyBuiltinOverride(
|
|
|
1209
1293
|
if (override.thinking !== undefined) { if (override.thinking === false) delete next.thinking; else next.thinking = override.thinking; }
|
|
1210
1294
|
if (override.systemPromptMode !== undefined) next.systemPromptMode = override.systemPromptMode;
|
|
1211
1295
|
if (override.inheritProjectContext !== undefined) next.inheritProjectContext = override.inheritProjectContext;
|
|
1296
|
+
if (override.inheritGlobalContext !== undefined) next.inheritGlobalContext = override.inheritGlobalContext;
|
|
1212
1297
|
if (override.inheritSkills !== undefined) next.inheritSkills = override.inheritSkills;
|
|
1213
1298
|
if (override.defaultContext !== undefined) { if (override.defaultContext === false) delete next.defaultContext; else next.defaultContext = override.defaultContext; }
|
|
1214
1299
|
if (override.acceptanceRole !== undefined) { if (override.acceptanceRole === false) delete next.acceptanceRole; else next.acceptanceRole = override.acceptanceRole; }
|
|
@@ -1218,6 +1303,7 @@ function applyBuiltinOverride(
|
|
|
1218
1303
|
if (override.tools !== undefined) applyToolsOverride(next, override.tools);
|
|
1219
1304
|
if (override.extensions !== undefined) { if (override.extensions === false) delete next.extensions; else next.extensions = [...override.extensions]; }
|
|
1220
1305
|
if (override.subagentOnlyExtensions !== undefined) { if (override.subagentOnlyExtensions === false) delete next.subagentOnlyExtensions; else next.subagentOnlyExtensions = [...override.subagentOnlyExtensions]; }
|
|
1306
|
+
if (override.mutationTools !== undefined) { if (override.mutationTools === false) delete next.mutationTools; else next.mutationTools = [...override.mutationTools]; }
|
|
1221
1307
|
if (override.completionGuard !== undefined) next.completionGuard = override.completionGuard;
|
|
1222
1308
|
if (override.toolBudget !== undefined) { if (override.toolBudget === false) delete next.toolBudget; else next.toolBudget = override.toolBudget; }
|
|
1223
1309
|
|
|
@@ -1355,6 +1441,9 @@ function applyCustomAgentOverride(
|
|
|
1355
1441
|
if (override.inheritProjectContext !== undefined) {
|
|
1356
1442
|
fill("inheritProjectContext", ["inheritProjectContext"], override.inheritProjectContext);
|
|
1357
1443
|
}
|
|
1444
|
+
if (override.inheritGlobalContext !== undefined) {
|
|
1445
|
+
fill("inheritGlobalContext", ["inheritGlobalContext"], override.inheritGlobalContext);
|
|
1446
|
+
}
|
|
1358
1447
|
if (override.inheritSkills !== undefined) {
|
|
1359
1448
|
fill("inheritSkills", ["inheritSkills"], override.inheritSkills);
|
|
1360
1449
|
}
|
|
@@ -1386,6 +1475,9 @@ function applyCustomAgentOverride(
|
|
|
1386
1475
|
override.subagentOnlyExtensions === false ? undefined : [...override.subagentOnlyExtensions],
|
|
1387
1476
|
);
|
|
1388
1477
|
}
|
|
1478
|
+
if (override.mutationTools !== undefined) {
|
|
1479
|
+
fill("mutationTools", ["mutationTools"], override.mutationTools === false ? undefined : [...override.mutationTools]);
|
|
1480
|
+
}
|
|
1389
1481
|
if (override.completionGuard !== undefined) {
|
|
1390
1482
|
fill("completionGuard", ["completionGuard"], override.completionGuard);
|
|
1391
1483
|
}
|
|
@@ -1425,7 +1517,7 @@ function applyCustomAgentOverrides(
|
|
|
1425
1517
|
|
|
1426
1518
|
export function buildBuiltinOverrideConfig(
|
|
1427
1519
|
base: BuiltinAgentOverrideBase,
|
|
1428
|
-
draft: Pick<AgentConfig, "model" | "modelProvider" | "fallbackModels" | "fast" | "thinking" | "systemPromptMode" | "inheritProjectContext" | "inheritSkills" | "defaultContext" | "acceptanceRole" | "disabled" | "systemPrompt" | "skills" | "tools" | "mcpDirectTools" | "extensions" | "subagentOnlyExtensions" | "completionGuard" | "toolBudget"> & Partial<Pick<AgentConfig, "description" | "output" | "outputMode" | "defaultReads">>,
|
|
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">>,
|
|
1429
1521
|
): BuiltinAgentOverrideConfig | undefined {
|
|
1430
1522
|
const override: BuiltinAgentOverrideConfig = {};
|
|
1431
1523
|
|
|
@@ -1443,6 +1535,7 @@ export function buildBuiltinOverrideConfig(
|
|
|
1443
1535
|
if (draft.thinking !== base.thinking) override.thinking = draft.thinking ?? false;
|
|
1444
1536
|
if (draft.systemPromptMode !== base.systemPromptMode) override.systemPromptMode = draft.systemPromptMode;
|
|
1445
1537
|
if (draft.inheritProjectContext !== base.inheritProjectContext) override.inheritProjectContext = draft.inheritProjectContext;
|
|
1538
|
+
if (draft.inheritGlobalContext !== base.inheritGlobalContext) override.inheritGlobalContext = draft.inheritGlobalContext;
|
|
1446
1539
|
if (draft.inheritSkills !== base.inheritSkills) override.inheritSkills = draft.inheritSkills;
|
|
1447
1540
|
if (draft.defaultContext !== base.defaultContext) override.defaultContext = draft.defaultContext ?? false;
|
|
1448
1541
|
if (draft.acceptanceRole !== base.acceptanceRole) override.acceptanceRole = draft.acceptanceRole ?? false;
|
|
@@ -1457,6 +1550,7 @@ export function buildBuiltinOverrideConfig(
|
|
|
1457
1550
|
if (!arraysEqual(draft.subagentOnlyExtensions, base.subagentOnlyExtensions)) {
|
|
1458
1551
|
override.subagentOnlyExtensions = draft.subagentOnlyExtensions ? [...draft.subagentOnlyExtensions] : false;
|
|
1459
1552
|
}
|
|
1553
|
+
if (!arraysEqual(draft.mutationTools, base.mutationTools)) override.mutationTools = draft.mutationTools ? [...draft.mutationTools] : false;
|
|
1460
1554
|
if ((draft.completionGuard !== false) !== (base.completionGuard !== false)) {
|
|
1461
1555
|
override.completionGuard = draft.completionGuard !== false;
|
|
1462
1556
|
}
|
|
@@ -1598,9 +1692,22 @@ function shouldPruneDiscoveryDir(rootDir: string, dir: string, dirName: string):
|
|
|
1598
1692
|
return path.resolve(dir) !== path.resolve(rootDir) && isDiscoveryNestedProjectRoot(dir);
|
|
1599
1693
|
}
|
|
1600
1694
|
|
|
1601
|
-
function listFilesRecursive(
|
|
1695
|
+
function listFilesRecursive(
|
|
1696
|
+
dir: string,
|
|
1697
|
+
predicate: (fileName: string) => boolean,
|
|
1698
|
+
rootDir = dir,
|
|
1699
|
+
visitedDirectories = new Set<string>(),
|
|
1700
|
+
): string[] {
|
|
1602
1701
|
const files: string[] = [];
|
|
1603
1702
|
if (!fs.existsSync(dir)) return files;
|
|
1703
|
+
let realDir: string;
|
|
1704
|
+
try {
|
|
1705
|
+
realDir = fs.realpathSync(dir);
|
|
1706
|
+
} catch {
|
|
1707
|
+
return files;
|
|
1708
|
+
}
|
|
1709
|
+
if (visitedDirectories.has(realDir)) return files;
|
|
1710
|
+
visitedDirectories.add(realDir);
|
|
1604
1711
|
|
|
1605
1712
|
let entries: fs.Dirent[];
|
|
1606
1713
|
try {
|
|
@@ -1611,9 +1718,17 @@ function listFilesRecursive(dir: string, predicate: (fileName: string) => boolea
|
|
|
1611
1718
|
|
|
1612
1719
|
for (const entry of entries) {
|
|
1613
1720
|
const filePath = path.join(dir, entry.name);
|
|
1614
|
-
|
|
1721
|
+
let isDirectory = entry.isDirectory();
|
|
1722
|
+
if (entry.isSymbolicLink()) {
|
|
1723
|
+
try {
|
|
1724
|
+
isDirectory = fs.statSync(filePath).isDirectory();
|
|
1725
|
+
} catch {
|
|
1726
|
+
isDirectory = false;
|
|
1727
|
+
}
|
|
1728
|
+
}
|
|
1729
|
+
if (isDirectory) {
|
|
1615
1730
|
if (!shouldPruneDiscoveryDir(rootDir, filePath, entry.name)) {
|
|
1616
|
-
files.push(...listFilesRecursive(filePath, predicate, rootDir));
|
|
1731
|
+
files.push(...listFilesRecursive(filePath, predicate, rootDir, visitedDirectories));
|
|
1617
1732
|
}
|
|
1618
1733
|
continue;
|
|
1619
1734
|
}
|
|
@@ -1624,6 +1739,81 @@ function listFilesRecursive(dir: string, predicate: (fileName: string) => boolea
|
|
|
1624
1739
|
return files;
|
|
1625
1740
|
}
|
|
1626
1741
|
|
|
1742
|
+
export interface AgentDefinitionInspection {
|
|
1743
|
+
files: string[];
|
|
1744
|
+
state: AgentDefinitionDirectoryState;
|
|
1745
|
+
}
|
|
1746
|
+
|
|
1747
|
+
/** Narrow filesystem seam so unavailable paths can be tested without permissions. */
|
|
1748
|
+
export interface AgentDefinitionInspectionFs {
|
|
1749
|
+
existsSync(filePath: string): boolean;
|
|
1750
|
+
realpathSync?(filePath: string): string;
|
|
1751
|
+
statSync(filePath: string): { isDirectory(): boolean };
|
|
1752
|
+
readdirSync(dir: string): fs.Dirent[];
|
|
1753
|
+
}
|
|
1754
|
+
|
|
1755
|
+
const DEFAULT_AGENT_DEFINITION_INSPECTION_FS: AgentDefinitionInspectionFs = {
|
|
1756
|
+
existsSync: fs.existsSync,
|
|
1757
|
+
realpathSync: fs.realpathSync,
|
|
1758
|
+
statSync: fs.statSync,
|
|
1759
|
+
readdirSync: (dir) => fs.readdirSync(dir, { withFileTypes: true }),
|
|
1760
|
+
};
|
|
1761
|
+
|
|
1762
|
+
/**
|
|
1763
|
+
* Inspect one agent-definition directory while retaining traversal failure
|
|
1764
|
+
* state. A nested unreadable directory makes the whole inspection unavailable,
|
|
1765
|
+
* preventing a partial traversal from being reported as empty or complete.
|
|
1766
|
+
*/
|
|
1767
|
+
export function inspectAgentDefinitionDirectory(dir: string, operations: AgentDefinitionInspectionFs = DEFAULT_AGENT_DEFINITION_INSPECTION_FS): AgentDefinitionInspection {
|
|
1768
|
+
const root = path.resolve(dir);
|
|
1769
|
+
try {
|
|
1770
|
+
if (!operations.existsSync(root)) return { files: [], state: "absent" };
|
|
1771
|
+
if (!operations.statSync(root).isDirectory()) return { files: [], state: "not-directory" };
|
|
1772
|
+
} catch {
|
|
1773
|
+
return { files: [], state: "unreadable" };
|
|
1774
|
+
}
|
|
1775
|
+
const files: string[] = [];
|
|
1776
|
+
let unreadable = false;
|
|
1777
|
+
const visitedDirectories = new Set<string>();
|
|
1778
|
+
const visit = (current: string): void => {
|
|
1779
|
+
let realDir: string;
|
|
1780
|
+
try {
|
|
1781
|
+
realDir = operations.realpathSync?.(current) ?? path.resolve(current);
|
|
1782
|
+
} catch {
|
|
1783
|
+
unreadable = true;
|
|
1784
|
+
return;
|
|
1785
|
+
}
|
|
1786
|
+
if (visitedDirectories.has(realDir)) return;
|
|
1787
|
+
visitedDirectories.add(realDir);
|
|
1788
|
+
|
|
1789
|
+
let entries: fs.Dirent[];
|
|
1790
|
+
try {
|
|
1791
|
+
entries = operations.readdirSync(current).sort((a, b) => a.name.localeCompare(b.name));
|
|
1792
|
+
} catch {
|
|
1793
|
+
unreadable = true;
|
|
1794
|
+
return;
|
|
1795
|
+
}
|
|
1796
|
+
for (const entry of entries) {
|
|
1797
|
+
const filePath = path.join(current, entry.name);
|
|
1798
|
+
let isDirectory = entry.isDirectory();
|
|
1799
|
+
if (entry.isSymbolicLink()) {
|
|
1800
|
+
try {
|
|
1801
|
+
isDirectory = operations.statSync(filePath).isDirectory();
|
|
1802
|
+
} catch {
|
|
1803
|
+
isDirectory = false;
|
|
1804
|
+
}
|
|
1805
|
+
}
|
|
1806
|
+
if (isDirectory) {
|
|
1807
|
+
if (!shouldPruneDiscoveryDir(root, filePath, entry.name)) visit(filePath);
|
|
1808
|
+
continue;
|
|
1809
|
+
}
|
|
1810
|
+
if ((entry.isFile() || entry.isSymbolicLink()) && entry.name.endsWith(".md") && !entry.name.endsWith(".chain.md") && !isLegacyAgentSkillPath(root, filePath)) files.push(filePath);
|
|
1811
|
+
}
|
|
1812
|
+
};
|
|
1813
|
+
visit(root);
|
|
1814
|
+
return { files, state: unreadable ? "unreadable" : files.length ? "candidates" : "empty" };
|
|
1815
|
+
}
|
|
1816
|
+
|
|
1627
1817
|
function isLegacyAgentSkillPath(rootDir: string, filePath: string): boolean {
|
|
1628
1818
|
const relative = path.relative(rootDir, filePath);
|
|
1629
1819
|
const parts = relative.split(path.sep).map((part) => part.toLowerCase());
|
|
@@ -1682,24 +1872,29 @@ function parseAgentRunnerFrontmatter(raw: string | undefined, agentName: string)
|
|
|
1682
1872
|
if (runner.args !== undefined && (!Array.isArray(runner.args) || runner.args.some((arg) => typeof arg !== "string"))) {
|
|
1683
1873
|
throw new Error(`Agent '${agentName}' external-cli runner args must be an array of strings.`);
|
|
1684
1874
|
}
|
|
1875
|
+
if (runner.adapter !== undefined && !isCodeOwnedExternalCliAdapterId(runner.adapter)) throw new Error(`Agent '${agentName}' external-cli runner adapter must be ${CODE_OWNED_EXTERNAL_CLI_ADAPTER_LABEL}.`);
|
|
1876
|
+
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.`);
|
|
1685
1877
|
if (runner.promptDelivery !== undefined && runner.promptDelivery !== "stdin") {
|
|
1686
1878
|
throw new Error(`Agent '${agentName}' external-cli runner promptDelivery must be 'stdin'.`);
|
|
1687
1879
|
}
|
|
1688
|
-
const
|
|
1880
|
+
const capabilities = parseExternalCliCapabilityNarrowing(runner.capabilities, `Agent '${agentName}' external-cli runner capabilities`);
|
|
1881
|
+
const supported = new Set(["type", "adapter", "command", "args", "promptDelivery", "capabilities"]);
|
|
1689
1882
|
const unknown = Object.keys(runner).filter((key) => !supported.has(key));
|
|
1690
1883
|
if (unknown.length > 0) throw new Error(`Agent '${agentName}' external-cli runner has unsupported fields: ${unknown.join(", ")}.`);
|
|
1691
1884
|
const runnerArgs = Array.isArray(runner.args) ? runner.args.filter((arg): arg is string => typeof arg === "string") : undefined;
|
|
1692
1885
|
return {
|
|
1693
1886
|
type: "external-cli",
|
|
1887
|
+
...(isCodeOwnedExternalCliAdapterId(runner.adapter) ? { adapter: runner.adapter } : {}),
|
|
1694
1888
|
command: runner.command.trim(),
|
|
1695
1889
|
...(runnerArgs?.length ? { args: runnerArgs } : {}),
|
|
1696
1890
|
...(runner.promptDelivery ? { promptDelivery: "stdin" as const } : {}),
|
|
1891
|
+
...(capabilities ? { capabilities } : {}),
|
|
1697
1892
|
};
|
|
1698
1893
|
}
|
|
1699
1894
|
|
|
1700
1895
|
function validateExternalRunnerProfile(frontmatter: Record<string, string>, agentName: string, runner: AgentRunnerConfig | undefined): void {
|
|
1701
1896
|
if (runner?.type !== "external-cli" && runner?.type !== "external-job") return;
|
|
1702
|
-
const unsupported = ["tools", "model", "fallbackModels", "thinking", "extensions", "subagentOnlyExtensions", "maxSubagentDepth", "completionGuard", "skills", "skill", "skillPath", "toolBudget", "permission", "permissions"]
|
|
1897
|
+
const unsupported = ["tools", "model", "fallbackModels", "thinking", "extensions", "subagentOnlyExtensions", "mutationTools", "maxSubagentDepth", "completionGuard", "skills", "skill", "skillPath", "toolBudget", "permission", "permissions"]
|
|
1703
1898
|
.filter((field) => frontmatter[field] !== undefined);
|
|
1704
1899
|
if (unsupported.length > 0) {
|
|
1705
1900
|
throw new Error(`Agent '${agentName}' uses runner.type='${runner.type}' and declares unsupported Pi-only fields: ${unsupported.join(", ")}.`);
|
|
@@ -1724,9 +1919,9 @@ interface AgentDefinitionFile {
|
|
|
1724
1919
|
content: string;
|
|
1725
1920
|
}
|
|
1726
1921
|
|
|
1727
|
-
function readAgentDefinitionFiles(dir: string): AgentDefinitionFile[] {
|
|
1922
|
+
function readAgentDefinitionFiles(dir: string, inspection = inspectAgentDefinitionDirectory(dir)): AgentDefinitionFile[] {
|
|
1728
1923
|
const files: AgentDefinitionFile[] = [];
|
|
1729
|
-
for (const filePath of
|
|
1924
|
+
for (const filePath of inspection.files) {
|
|
1730
1925
|
if (isLegacyAgentSkillPath(dir, filePath)) {
|
|
1731
1926
|
continue;
|
|
1732
1927
|
}
|
|
@@ -1783,6 +1978,8 @@ function loadAgentsFromDefinitionFiles(files: AgentDefinitionFile[], source: Age
|
|
|
1783
1978
|
const mcpDirectTools = parsedTools.mcpDirectTools ?? [];
|
|
1784
1979
|
const defaultReads = parseFrontmatterList(frontmatter.defaultReads);
|
|
1785
1980
|
const aliases = normalizeAgentAliases(parseFrontmatterList(frontmatter.aliases ?? frontmatter.alias), runtimeName);
|
|
1981
|
+
const profileError = validateCodeOwnedProfileRunner({ name: runtimeName, localName, aliases, runner });
|
|
1982
|
+
if (profileError) throw new Error(profileError);
|
|
1786
1983
|
const skillStr = frontmatter.skill || frontmatter.skills;
|
|
1787
1984
|
const skills = parseFrontmatterList(skillStr);
|
|
1788
1985
|
const skillPath = parseFrontmatterList(frontmatter.skillPath);
|
|
@@ -1797,6 +1994,7 @@ function loadAgentsFromDefinitionFiles(files: AgentDefinitionFile[], source: Age
|
|
|
1797
1994
|
: frontmatter.inheritProjectContext === "false"
|
|
1798
1995
|
? false
|
|
1799
1996
|
: defaultInheritProjectContext(localName);
|
|
1997
|
+
const inheritGlobalContext = frontmatter.inheritGlobalContext === "true";
|
|
1800
1998
|
const inheritSkills = frontmatter.inheritSkills === "true"
|
|
1801
1999
|
? true
|
|
1802
2000
|
: frontmatter.inheritSkills === "false"
|
|
@@ -1850,6 +2048,7 @@ function loadAgentsFromDefinitionFiles(files: AgentDefinitionFile[], source: Age
|
|
|
1850
2048
|
|
|
1851
2049
|
const extensions = resolveAgentRelativeExtensionPaths(parseFrontmatterList(frontmatter.extensions), filePath);
|
|
1852
2050
|
const subagentOnlyExtensions = resolveAgentRelativeExtensionPaths(parseFrontmatterList(frontmatter.subagentOnlyExtensions), filePath);
|
|
2051
|
+
const mutationTools = parseFrontmatterList(frontmatter.mutationTools);
|
|
1853
2052
|
let fast: boolean | undefined;
|
|
1854
2053
|
if (frontmatter.fast !== undefined) {
|
|
1855
2054
|
if (frontmatter.fast === "true") fast = true;
|
|
@@ -1906,6 +2105,7 @@ function loadAgentsFromDefinitionFiles(files: AgentDefinitionFile[], source: Age
|
|
|
1906
2105
|
...(frontmatter.thinking !== undefined ? { thinking: frontmatter.thinking === "false" ? false : frontmatter.thinking } : {}),
|
|
1907
2106
|
systemPromptMode,
|
|
1908
2107
|
inheritProjectContext,
|
|
2108
|
+
inheritGlobalContext,
|
|
1909
2109
|
inheritSkills,
|
|
1910
2110
|
...(defaultContext !== undefined ? { defaultContext } : {}),
|
|
1911
2111
|
...(defaultAsync !== undefined ? { defaultAsync } : {}),
|
|
@@ -1922,6 +2122,7 @@ function loadAgentsFromDefinitionFiles(files: AgentDefinitionFile[], source: Age
|
|
|
1922
2122
|
...(skillPath?.length ? { skillPath } : {}),
|
|
1923
2123
|
...(extensions !== undefined ? { extensions } : {}),
|
|
1924
2124
|
...(subagentOnlyExtensions !== undefined ? { subagentOnlyExtensions } : {}),
|
|
2125
|
+
...(mutationTools?.length ? { mutationTools } : {}),
|
|
1925
2126
|
...(frontmatter.output !== undefined ? { output: frontmatter.output } : {}),
|
|
1926
2127
|
...(outputMode !== undefined ? { outputMode } : {}),
|
|
1927
2128
|
...(defaultReads?.length ? { defaultReads } : {}),
|
|
@@ -1944,8 +2145,17 @@ function loadAgentsFromDefinitionFiles(files: AgentDefinitionFile[], source: Age
|
|
|
1944
2145
|
return { agents, diagnostics };
|
|
1945
2146
|
}
|
|
1946
2147
|
|
|
1947
|
-
function loadAgentsFromDir(dir: string, source: AgentSource, discoveryPriority?: number, packageSource?: Omit<PackageSubagentPath, "dir"
|
|
1948
|
-
return loadAgentsFromDefinitionFiles(readAgentDefinitionFiles(dir), source, discoveryPriority, packageSource);
|
|
2148
|
+
function loadAgentsFromDir(dir: string, source: AgentSource, discoveryPriority?: number, packageSource?: Omit<PackageSubagentPath, "dir">, inspection = inspectAgentDefinitionDirectory(dir)): { agents: AgentConfig[]; diagnostics: AgentDiscoveryDiagnostic[] } {
|
|
2149
|
+
return loadAgentsFromDefinitionFiles(readAgentDefinitionFiles(dir, inspection), source, discoveryPriority, packageSource);
|
|
2150
|
+
}
|
|
2151
|
+
|
|
2152
|
+
function reportAgentDefinitionDirectory(source: AgentSource, dir: string, inspection: AgentDefinitionInspection): AgentDefinitionDirectoryReport {
|
|
2153
|
+
return {
|
|
2154
|
+
source,
|
|
2155
|
+
path: path.resolve(dir),
|
|
2156
|
+
state: inspection.state,
|
|
2157
|
+
...(inspection.state === "candidates" ? { candidateCount: inspection.files.length } : {}),
|
|
2158
|
+
};
|
|
1949
2159
|
}
|
|
1950
2160
|
|
|
1951
2161
|
function loadChainsFromDir(dir: string, source: AgentSource): { chains: ChainConfig[]; diagnostics: ChainDiscoveryDiagnostic[] } {
|
|
@@ -1982,20 +2192,18 @@ function isDirectory(p: string): boolean {
|
|
|
1982
2192
|
}
|
|
1983
2193
|
}
|
|
1984
2194
|
|
|
1985
|
-
function resolveNearestProjectAgentDirs(cwd: string): { readDirs: string[]; preferredDir: string | null } {
|
|
2195
|
+
function resolveNearestProjectAgentDirs(cwd: string): { readDirs: string[]; candidateDirs: string[]; preferredDir: string | null } {
|
|
1986
2196
|
const projectRoot = findConfiguredProjectRoot(cwd);
|
|
1987
|
-
if (!projectRoot) return { readDirs: [], preferredDir: null };
|
|
2197
|
+
if (!projectRoot) return { readDirs: [], candidateDirs: [], preferredDir: null };
|
|
1988
2198
|
|
|
1989
2199
|
const legacyDir = path.join(projectRoot, ".agents");
|
|
1990
2200
|
const preferredDir = path.join(getProjectConfigDir(projectRoot), "agents");
|
|
2201
|
+
const candidateDirs = [legacyDir, preferredDir];
|
|
1991
2202
|
const readDirs: string[] = [];
|
|
1992
2203
|
if (isDirectory(legacyDir)) readDirs.push(legacyDir);
|
|
1993
2204
|
if (isDirectory(preferredDir)) readDirs.push(preferredDir);
|
|
1994
2205
|
|
|
1995
|
-
return {
|
|
1996
|
-
readDirs,
|
|
1997
|
-
preferredDir,
|
|
1998
|
-
};
|
|
2206
|
+
return { readDirs, candidateDirs, preferredDir };
|
|
1999
2207
|
}
|
|
2000
2208
|
|
|
2001
2209
|
function resolveNearestProjectChainDirs(cwd: string): { readDirs: string[]; preferredDir: string | null } {
|
|
@@ -2009,7 +2217,9 @@ function resolveNearestProjectChainDirs(cwd: string): { readDirs: string[]; pref
|
|
|
2009
2217
|
};
|
|
2010
2218
|
}
|
|
2011
2219
|
const BUILTIN_AGENTS_DIR = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..", "..", "agents");
|
|
2012
|
-
|
|
2220
|
+
// Candidate files and inspection state must describe the same cached builtin scan.
|
|
2221
|
+
const BUILTIN_AGENT_DEFINITION_INSPECTION = inspectAgentDefinitionDirectory(BUILTIN_AGENTS_DIR);
|
|
2222
|
+
const BUILTIN_AGENT_DEFINITION_FILES = readAgentDefinitionFiles(BUILTIN_AGENTS_DIR, BUILTIN_AGENT_DEFINITION_INSPECTION);
|
|
2013
2223
|
|
|
2014
2224
|
export const EXTRA_AGENT_DIRS_ENV = "PI_SUBAGENT_EXTRA_AGENT_DIRS";
|
|
2015
2225
|
|
|
@@ -2028,11 +2238,12 @@ function extraUserAgentDirs(): string[] {
|
|
|
2028
2238
|
}
|
|
2029
2239
|
|
|
2030
2240
|
export function discoverAgents(cwd: string, scope: AgentScope): AgentDiscoveryResult {
|
|
2241
|
+
const effectiveCwd = path.resolve(cwd);
|
|
2031
2242
|
const userDirOld = path.join(getAgentDir(), "agents");
|
|
2032
2243
|
const userDirNew = path.join(os.homedir(), ".agents");
|
|
2033
|
-
const { readDirs: projectAgentDirs, preferredDir: projectAgentsDir } = resolveNearestProjectAgentDirs(
|
|
2244
|
+
const { readDirs: projectAgentDirs, candidateDirs: projectCandidateDirs, preferredDir: projectAgentsDir } = resolveNearestProjectAgentDirs(effectiveCwd);
|
|
2034
2245
|
const userSettingsPath = getUserAgentSettingsPath();
|
|
2035
|
-
const projectSettingsPath = getProjectAgentSettingsPath(
|
|
2246
|
+
const projectSettingsPath = getProjectAgentSettingsPath(effectiveCwd);
|
|
2036
2247
|
const userSettings = scope === "project" ? EMPTY_SUBAGENT_SETTINGS : readSubagentSettings(userSettingsPath);
|
|
2037
2248
|
const projectSettings = scope === "user" ? EMPTY_SUBAGENT_SETTINGS : readSubagentSettings(projectSettingsPath);
|
|
2038
2249
|
const defaultProvider = resolveSubagentDefaultProvider(userSettings, projectSettings, projectSettingsPath);
|
|
@@ -2041,65 +2252,60 @@ export function discoverAgents(cwd: string, scope: AgentScope): AgentDiscoveryRe
|
|
|
2041
2252
|
const maxThinking = resolveSubagentMaxThinking(userSettings, projectSettings, projectSettingsPath);
|
|
2042
2253
|
const defaultExtensions = resolveSubagentDefaultExtensions(userSettings, projectSettings, projectSettingsPath);
|
|
2043
2254
|
const modelScope = projectSettings.modelScope ?? userSettings.modelScope;
|
|
2044
|
-
const packageSubagentPaths = collectPackageSubagentPaths(
|
|
2255
|
+
const packageSubagentPaths = collectPackageSubagentPaths(effectiveCwd, {
|
|
2045
2256
|
includeUser: scope !== "project",
|
|
2046
2257
|
includeProject: scope !== "user",
|
|
2047
2258
|
});
|
|
2259
|
+
const directories: AgentDefinitionDirectoryReport[] = [reportAgentDefinitionDirectory("builtin", BUILTIN_AGENTS_DIR, BUILTIN_AGENT_DEFINITION_INSPECTION)];
|
|
2048
2260
|
|
|
2049
2261
|
const builtinLoaded = loadAgentsFromDefinitionFiles(BUILTIN_AGENT_DEFINITION_FILES, "builtin");
|
|
2050
2262
|
const builtinAgents = applyBuiltinOverrides(
|
|
2051
2263
|
applySubagentDefaults(builtinLoaded.agents, defaultModel, defaultProvider, defaultThinking, defaultExtensions),
|
|
2052
|
-
userSettings,
|
|
2053
|
-
projectSettings,
|
|
2054
|
-
userSettingsPath,
|
|
2055
|
-
projectSettingsPath,
|
|
2264
|
+
userSettings, projectSettings, userSettingsPath, projectSettingsPath,
|
|
2056
2265
|
);
|
|
2057
2266
|
|
|
2058
|
-
const userLoaded = scope === "project" ? [] : [...extraUserAgentDirs(), userDirOld, userDirNew]
|
|
2059
|
-
|
|
2267
|
+
const userLoaded = scope === "project" ? [] : [...extraUserAgentDirs(), userDirOld, userDirNew].map((dir, discoveryPriority) => {
|
|
2268
|
+
const inspection = inspectAgentDefinitionDirectory(dir);
|
|
2269
|
+
directories.push(reportAgentDefinitionDirectory("user", dir, inspection));
|
|
2270
|
+
return loadAgentsFromDir(dir, "user", discoveryPriority, undefined, inspection);
|
|
2271
|
+
});
|
|
2060
2272
|
const userAgents = applyCustomAgentOverrides(
|
|
2061
2273
|
applySubagentDefaults(userLoaded.flatMap((loaded) => loaded.agents), defaultModel, defaultProvider, defaultThinking, defaultExtensions),
|
|
2062
|
-
userSettings,
|
|
2063
|
-
projectSettings,
|
|
2064
|
-
userSettingsPath,
|
|
2065
|
-
projectSettingsPath,
|
|
2274
|
+
userSettings, projectSettings, userSettingsPath, projectSettingsPath,
|
|
2066
2275
|
);
|
|
2067
2276
|
|
|
2068
|
-
|
|
2277
|
+
// Report both legacy and preferred project definition paths only after the
|
|
2278
|
+
// existing configured-root resolver found a root; never synthesize cwd paths.
|
|
2279
|
+
const projectInspections = scope === "user" ? new Map<string, AgentDefinitionInspection>() : new Map(projectCandidateDirs.map((dir) => [dir, inspectAgentDefinitionDirectory(dir)]));
|
|
2280
|
+
if (scope !== "user") for (const dir of projectCandidateDirs) directories.push(reportAgentDefinitionDirectory("project", dir, projectInspections.get(dir)!));
|
|
2281
|
+
const projectLoaded = scope === "user" ? [] : projectAgentDirs.map((dir) => loadAgentsFromDir(dir, "project", dir === projectAgentsDir ? 1 : 0, undefined, projectInspections.get(dir)!));
|
|
2069
2282
|
const projectAgents = applyCustomAgentOverrides(
|
|
2070
2283
|
applySubagentDefaults(projectLoaded.flatMap((loaded) => loaded.agents), defaultModel, defaultProvider, defaultThinking, defaultExtensions),
|
|
2071
|
-
userSettings,
|
|
2072
|
-
projectSettings,
|
|
2073
|
-
userSettingsPath,
|
|
2074
|
-
projectSettingsPath,
|
|
2284
|
+
userSettings, projectSettings, userSettingsPath, projectSettingsPath,
|
|
2075
2285
|
);
|
|
2076
|
-
|
|
2286
|
+
|
|
2287
|
+
const packageLoaded = packageSubagentPaths.agents.map((entry, index) => {
|
|
2288
|
+
const inspection = inspectAgentDefinitionDirectory(entry.dir);
|
|
2289
|
+
directories.push(reportAgentDefinitionDirectory("package", entry.dir, inspection));
|
|
2290
|
+
return loadAgentsFromDir(entry.dir, "package", packageSubagentPaths.agents.length - index, entry, inspection);
|
|
2291
|
+
});
|
|
2077
2292
|
const packageMap = new Map<string, AgentConfig>();
|
|
2078
|
-
for (const loaded of packageLoaded)
|
|
2079
|
-
for (const agent of loaded.agents) {
|
|
2080
|
-
if (!packageMap.has(agent.name)) packageMap.set(agent.name, agent);
|
|
2081
|
-
}
|
|
2082
|
-
}
|
|
2293
|
+
for (const loaded of packageLoaded) for (const agent of loaded.agents) if (!packageMap.has(agent.name)) packageMap.set(agent.name, agent);
|
|
2083
2294
|
const packageAgents = applyCustomAgentOverrides(
|
|
2084
2295
|
applySubagentDefaults(Array.from(packageMap.values()), defaultModel, defaultProvider, defaultThinking, defaultExtensions),
|
|
2085
|
-
userSettings,
|
|
2086
|
-
projectSettings,
|
|
2087
|
-
userSettingsPath,
|
|
2088
|
-
projectSettingsPath,
|
|
2296
|
+
userSettings, projectSettings, userSettingsPath, projectSettingsPath,
|
|
2089
2297
|
);
|
|
2090
2298
|
const agents = applySubagentMaxThinking(
|
|
2091
|
-
mergeAgentsForScope(scope, userAgents, projectAgents, builtinAgents, packageAgents)
|
|
2092
|
-
.filter((agent) => agent.disabled !== true),
|
|
2299
|
+
mergeAgentsForScope(scope, userAgents, projectAgents, builtinAgents, packageAgents).filter((agent) => agent.disabled !== true),
|
|
2093
2300
|
maxThinking,
|
|
2094
2301
|
);
|
|
2095
|
-
|
|
2096
2302
|
const agentDiagnostics = [
|
|
2097
2303
|
...builtinLoaded.diagnostics,
|
|
2098
2304
|
...userLoaded.flatMap((loaded) => loaded.diagnostics),
|
|
2099
2305
|
...projectLoaded.flatMap((loaded) => loaded.diagnostics),
|
|
2100
2306
|
...packageLoaded.flatMap((loaded) => loaded.diagnostics),
|
|
2101
2307
|
];
|
|
2102
|
-
return { agents, agentDiagnostics, projectAgentsDir, ...(modelScope !== undefined ? { modelScope } : {}), ...(maxThinking !== undefined ? { maxThinking } : {}) };
|
|
2308
|
+
return { agents, agentDiagnostics, projectAgentsDir, cwd: effectiveCwd, scope, directories, ...(modelScope !== undefined ? { modelScope } : {}), ...(maxThinking !== undefined ? { maxThinking } : {}) };
|
|
2103
2309
|
}
|
|
2104
2310
|
|
|
2105
2311
|
export function discoverAgentsAll(cwd: string): {
|