pi-subagents 0.59.0 → 0.61.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 +69 -0
- package/docs/agents.md +2 -2
- package/docs/configuration.md +9 -5
- package/docs/extension-api.md +14 -7
- package/docs/models.md +1 -1
- package/docs/observability.md +1 -1
- package/docs/tool-reference.md +15 -3
- package/docs/workflows.md +15 -14
- package/install.mjs +2 -1
- package/package.json +1 -1
- package/skills/council-mode/SKILL.md +48 -243
- package/skills/council-mode/references/pass-contracts.md +150 -0
- package/skills/pi-subagents/SKILL.md +89 -37
- package/skills/pi-subagents/references/constraints-and-recipes.md +30 -234
- package/skills/pi-subagents/references/execution-controls.md +79 -7
- package/skills/pi-subagents/references/management-authoring-rpc.md +2 -2
- package/skills/pi-subagents/references/multi-lane-orchestration.md +13 -1
- package/skills/pi-subagents/references/prompting-and-roles.md +35 -28
- package/skills/pi-subagents/references/review-and-validation.md +73 -0
- package/src/agents/agent-management.ts +256 -88
- package/src/agents/agents.ts +527 -221
- package/src/api/background-work.ts +7 -2
- package/src/api/external-runs.ts +67 -4
- package/src/api/preflight.ts +13 -8
- package/src/api/shared-types.ts +3 -0
- package/src/extension/index.ts +7 -4
- package/src/extension/public-execution.ts +48 -4
- package/src/extension/rpc.ts +62 -4
- package/src/extension/schemas.ts +12 -7
- package/src/extension/tool-description.ts +16 -16
- package/src/runs/background/async-execution.ts +53 -37
- package/src/runs/background/async-job-tracker.ts +65 -3
- package/src/runs/background/async-resume.ts +3 -1
- package/src/runs/background/async-status.ts +104 -11
- package/src/runs/background/auto-drain.ts +1 -1
- package/src/runs/background/control-channel.ts +3 -2
- package/src/runs/background/fleet-view.ts +1 -1
- package/src/runs/background/result-watcher.ts +1 -1
- package/src/runs/background/resume-guidance.ts +1 -1
- package/src/runs/background/run-status.ts +15 -4
- package/src/runs/background/subagent-runner.ts +6 -3
- package/src/runs/background/subagent-wait.ts +30 -23
- package/src/runs/background/wait-completions.ts +4 -1
- package/src/runs/background/wait-tool.ts +24 -18
- package/src/runs/foreground/execution.ts +72 -4
- package/src/runs/foreground/subagent-executor.ts +276 -59
- package/src/runs/shared/acceptance.ts +43 -18
- package/src/runs/shared/async-status-projection.ts +138 -4
- package/src/runs/shared/background-process-options.ts +9 -0
- package/src/runs/shared/host-step-status.ts +1 -0
- package/src/runs/shared/mcp-direct-tool-grant.ts +2 -5
- package/src/runs/shared/model-fallback.ts +61 -17
- package/src/runs/shared/mutation-evidence.ts +52 -3
- package/src/runs/shared/permissions.ts +1 -1
- package/src/runs/shared/pi-args.ts +47 -1
- package/src/runs/shared/single-output.ts +45 -18
- package/src/runs/shared/subagent-prompt-runtime.ts +20 -2
- package/src/runs/shared/tool-timeout.ts +1 -1
- package/src/runs/shared/workflow-graph.ts +16 -0
- package/src/shared/types.ts +74 -4
- package/src/shared/workflow-child-permit.ts +91 -0
- package/src/slash/prompt-template-bridge.ts +37 -1
- package/src/slash/slash-commands.ts +18 -26
- package/src/tui/fleet-status.ts +11 -3
- package/src/tui/render-helpers.ts +31 -0
- package/src/tui/render.ts +673 -145
- package/src/watchdog/change-signature.ts +40 -1
- package/src/workflows/host-command.ts +6 -1
- package/src/workflows/scripted-workflow.ts +206 -6
- package/src/workflows/workflow-child-summary.ts +1 -1
- package/src/workflows/workflow-receipt.ts +41 -4
- package/src/workflows/workflow-resources.ts +150 -0
package/src/agents/agents.ts
CHANGED
|
@@ -341,16 +341,30 @@ function getUserChainDir(): string {
|
|
|
341
341
|
return path.join(getAgentDir(), "chains");
|
|
342
342
|
}
|
|
343
343
|
|
|
344
|
+
type PackageScope = "root" | "user" | "project";
|
|
345
|
+
type PackageSettingsScope = Exclude<PackageScope, "root">;
|
|
346
|
+
|
|
344
347
|
interface PackageSubagentPath {
|
|
345
348
|
dir: string;
|
|
349
|
+
// A package can be reachable from more than one settings scope. Keep the
|
|
350
|
+
// actual memberships instead of promoting a mixed-scope path to root.
|
|
351
|
+
scope: Set<PackageScope>;
|
|
346
352
|
packageName?: string;
|
|
347
353
|
packageVersion?: string;
|
|
348
354
|
packageRoot: string;
|
|
349
355
|
}
|
|
350
356
|
|
|
357
|
+
interface PackageChainPath {
|
|
358
|
+
dir: string;
|
|
359
|
+
scope: Set<PackageScope>;
|
|
360
|
+
packageRoot: string;
|
|
361
|
+
}
|
|
362
|
+
|
|
351
363
|
interface PackageSubagentPaths {
|
|
352
364
|
agents: PackageSubagentPath[];
|
|
353
|
-
chains:
|
|
365
|
+
chains: PackageChainPath[];
|
|
366
|
+
watchPaths: string[];
|
|
367
|
+
settingsErrors: Partial<Record<PackageSettingsScope, Error>>;
|
|
354
368
|
}
|
|
355
369
|
|
|
356
370
|
let cachedGlobalNpmRoot: string | null = null;
|
|
@@ -365,18 +379,6 @@ function readJsonFileBestEffort(filePath: string): unknown {
|
|
|
365
379
|
}
|
|
366
380
|
}
|
|
367
381
|
|
|
368
|
-
function readOptionalJsonFile(filePath: string): unknown {
|
|
369
|
-
try {
|
|
370
|
-
return JSON.parse(fs.readFileSync(filePath, "utf-8"));
|
|
371
|
-
} catch (error) {
|
|
372
|
-
const code = typeof error === "object" && error !== null && "code" in error
|
|
373
|
-
? (error as { code?: unknown }).code
|
|
374
|
-
: undefined;
|
|
375
|
-
if (code === "ENOENT") return null;
|
|
376
|
-
throw error;
|
|
377
|
-
}
|
|
378
|
-
}
|
|
379
|
-
|
|
380
382
|
function isSafePackagePath(value: string): boolean {
|
|
381
383
|
return value.length > 0
|
|
382
384
|
&& !path.isAbsolute(value)
|
|
@@ -488,7 +490,7 @@ function stringArray(value: unknown): string[] {
|
|
|
488
490
|
return value.filter((entry): entry is string => typeof entry === "string" && entry.trim().length > 0);
|
|
489
491
|
}
|
|
490
492
|
|
|
491
|
-
function packageMetadata(pkg: Record<string, unknown>, packageRoot: string): Omit<PackageSubagentPath, "dir"> {
|
|
493
|
+
function packageMetadata(pkg: Record<string, unknown>, packageRoot: string): Omit<PackageSubagentPath, "dir" | "scope"> {
|
|
492
494
|
const name = typeof pkg.name === "string" && pkg.name.trim() ? pkg.name.trim() : undefined;
|
|
493
495
|
const version = typeof pkg.version === "string" && pkg.version.trim() ? pkg.version.trim() : undefined;
|
|
494
496
|
return {
|
|
@@ -498,10 +500,10 @@ function packageMetadata(pkg: Record<string, unknown>, packageRoot: string): Omi
|
|
|
498
500
|
};
|
|
499
501
|
}
|
|
500
502
|
|
|
501
|
-
function extractSubagentPathsFromPackageRoot(packageRoot: string): PackageSubagentPaths {
|
|
503
|
+
function extractSubagentPathsFromPackageRoot(packageRoot: string, scope: Set<PackageScope>): PackageSubagentPaths {
|
|
502
504
|
const packageJsonPath = path.join(packageRoot, "package.json");
|
|
503
505
|
const pkg = readJsonFileBestEffort(packageJsonPath);
|
|
504
|
-
if (!pkg || typeof pkg !== "object" || Array.isArray(pkg)) return { agents: [], chains: [] };
|
|
506
|
+
if (!pkg || typeof pkg !== "object" || Array.isArray(pkg)) return { agents: [], chains: [], watchPaths: [packageJsonPath], settingsErrors: {} };
|
|
505
507
|
const pkgRecord = pkg as Record<string, unknown>;
|
|
506
508
|
const metadata = packageMetadata(pkgRecord, packageRoot);
|
|
507
509
|
|
|
@@ -520,16 +522,17 @@ function extractSubagentPathsFromPackageRoot(packageRoot: string): PackageSubage
|
|
|
520
522
|
}
|
|
521
523
|
|
|
522
524
|
const agents: PackageSubagentPath[] = [];
|
|
523
|
-
const chains:
|
|
525
|
+
const chains: PackageChainPath[] = [];
|
|
524
526
|
for (const root of roots) {
|
|
525
|
-
for (const entry of stringArray(root.agents)) agents.push({ dir: path.resolve(packageRoot, entry), ...metadata });
|
|
526
|
-
for (const entry of stringArray(root.chains)) chains.push(path.resolve(packageRoot, entry));
|
|
527
|
+
for (const entry of stringArray(root.agents)) agents.push({ dir: path.resolve(packageRoot, entry), scope, ...metadata });
|
|
528
|
+
for (const entry of stringArray(root.chains)) chains.push({ dir: path.resolve(packageRoot, entry), scope, packageRoot });
|
|
527
529
|
}
|
|
528
|
-
return { agents, chains };
|
|
530
|
+
return { agents, chains, watchPaths: [packageJsonPath], settingsErrors: {} };
|
|
529
531
|
}
|
|
530
532
|
|
|
531
|
-
function collectPackageRootsFromNodeModules(nodeModulesDir: string): string[] {
|
|
533
|
+
function collectPackageRootsFromNodeModules(nodeModulesDir: string, watchPaths?: string[]): string[] {
|
|
532
534
|
const roots: string[] = [];
|
|
535
|
+
watchPaths?.push(nodeModulesDir);
|
|
533
536
|
if (!fs.existsSync(nodeModulesDir)) return roots;
|
|
534
537
|
|
|
535
538
|
let entries: fs.Dirent[];
|
|
@@ -545,6 +548,7 @@ function collectPackageRootsFromNodeModules(nodeModulesDir: string): string[] {
|
|
|
545
548
|
|
|
546
549
|
if (entry.name.startsWith("@")) {
|
|
547
550
|
const scopeDir = path.join(nodeModulesDir, entry.name);
|
|
551
|
+
watchPaths?.push(scopeDir);
|
|
548
552
|
let scopeEntries: fs.Dirent[];
|
|
549
553
|
try {
|
|
550
554
|
scopeEntries = fs.readdirSync(scopeDir, { withFileTypes: true });
|
|
@@ -565,8 +569,7 @@ function collectPackageRootsFromNodeModules(nodeModulesDir: string): string[] {
|
|
|
565
569
|
}
|
|
566
570
|
|
|
567
571
|
function collectSettingsPackageRoots(settingsFile: string, baseDir: string): string[] {
|
|
568
|
-
const settings =
|
|
569
|
-
if (!settings || typeof settings !== "object" || Array.isArray(settings)) return [];
|
|
572
|
+
const settings = readSettingsFileStrict(settingsFile);
|
|
570
573
|
const packages = (settings as { packages?: unknown }).packages;
|
|
571
574
|
if (!Array.isArray(packages)) return [];
|
|
572
575
|
|
|
@@ -587,52 +590,82 @@ function collectSettingsPackageRoots(settingsFile: string, baseDir: string): str
|
|
|
587
590
|
function collectPackageSubagentPaths(cwd: string, options: { includeUser: boolean; includeProject: boolean } = { includeUser: true, includeProject: true }): PackageSubagentPaths {
|
|
588
591
|
const agentDir = getAgentDir();
|
|
589
592
|
const projectRoot = findConfiguredProjectRoot(cwd) ?? cwd;
|
|
590
|
-
const packageRoots = [
|
|
591
|
-
projectRoot,
|
|
593
|
+
const packageRoots: Array<{ root: string; scope: PackageScope }> = [
|
|
594
|
+
{ root: projectRoot, scope: "root" },
|
|
592
595
|
];
|
|
593
|
-
|
|
596
|
+
const watchPaths: string[] = [path.join(projectRoot, "package.json")];
|
|
597
|
+
const settingsErrors: Partial<Record<PackageSettingsScope, Error>> = {};
|
|
598
|
+
const collectScopedSettingsRoots = (scope: PackageSettingsScope, settingsFile: string, baseDir: string): string[] => {
|
|
599
|
+
try {
|
|
600
|
+
return collectSettingsPackageRoots(settingsFile, baseDir);
|
|
601
|
+
} catch (error) {
|
|
602
|
+
// The raw source snapshot includes both settings scopes. Defer a
|
|
603
|
+
// strict failure until a projection actually selects this scope so an
|
|
604
|
+
// unrelated malformed settings file stays out of scoped discovery.
|
|
605
|
+
settingsErrors[scope] = error instanceof Error
|
|
606
|
+
? error
|
|
607
|
+
: new Error(`Failed to read settings file '${settingsFile}': ${String(error)}`, { cause: error });
|
|
608
|
+
return [];
|
|
609
|
+
}
|
|
610
|
+
};
|
|
594
611
|
if (options.includeProject) {
|
|
595
612
|
const projectConfigDir = getProjectConfigDir(projectRoot);
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
);
|
|
613
|
+
const nodeModulesDir = path.join(projectConfigDir, "npm", "node_modules");
|
|
614
|
+
packageRoots.push(...collectPackageRootsFromNodeModules(nodeModulesDir, watchPaths).map((root) => ({ root, scope: "project" as const })));
|
|
615
|
+
packageRoots.push(...collectScopedSettingsRoots("project", path.join(projectConfigDir, "settings.json"), projectConfigDir).map((root) => ({ root, scope: "project" as const })));
|
|
600
616
|
}
|
|
601
617
|
|
|
602
618
|
if (options.includeUser) {
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
);
|
|
619
|
+
const nodeModulesDir = path.join(agentDir, "npm", "node_modules");
|
|
620
|
+
packageRoots.push(...collectPackageRootsFromNodeModules(nodeModulesDir, watchPaths).map((root) => ({ root, scope: "user" as const })));
|
|
621
|
+
packageRoots.push(...collectScopedSettingsRoots("user", path.join(agentDir, "settings.json"), agentDir).map((root) => ({ root, scope: "user" as const })));
|
|
607
622
|
}
|
|
608
623
|
|
|
609
624
|
if (options.includeUser) {
|
|
610
625
|
const globalRoot = getGlobalNpmRoot();
|
|
611
|
-
if (globalRoot)
|
|
626
|
+
if (globalRoot) {
|
|
627
|
+
packageRoots.push(...collectPackageRootsFromNodeModules(globalRoot, watchPaths).map((root) => ({ root, scope: "user" as const })));
|
|
628
|
+
}
|
|
612
629
|
}
|
|
613
630
|
|
|
614
|
-
const seenRoots = new Set<
|
|
615
|
-
const seenAgents = new
|
|
616
|
-
const seenChains = new
|
|
631
|
+
const seenRoots = new Map<string, Set<PackageScope>>();
|
|
632
|
+
const seenAgents = new Map<string, PackageSubagentPath>();
|
|
633
|
+
const seenChains = new Map<string, PackageChainPath>();
|
|
617
634
|
const agents: PackageSubagentPath[] = [];
|
|
618
|
-
const chains:
|
|
619
|
-
for (const packageRoot of packageRoots) {
|
|
635
|
+
const chains: PackageChainPath[] = [];
|
|
636
|
+
for (const { root: packageRoot, scope } of packageRoots) {
|
|
620
637
|
const resolvedRoot = path.resolve(packageRoot);
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
638
|
+
const scopes = seenRoots.get(resolvedRoot);
|
|
639
|
+
if (scopes !== undefined) {
|
|
640
|
+
scopes.add(scope);
|
|
641
|
+
continue;
|
|
642
|
+
}
|
|
643
|
+
const packageScopes = new Set<PackageScope>([scope]);
|
|
644
|
+
seenRoots.set(resolvedRoot, packageScopes);
|
|
645
|
+
const paths = extractSubagentPathsFromPackageRoot(resolvedRoot, packageScopes);
|
|
646
|
+
watchPaths.push(...paths.watchPaths);
|
|
624
647
|
for (const agentPath of paths.agents) {
|
|
625
|
-
|
|
626
|
-
seenAgents.
|
|
648
|
+
const agentKey = `${agentPath.dir}\u0000${agentPath.packageRoot}`;
|
|
649
|
+
const existing = seenAgents.get(agentKey);
|
|
650
|
+
if (existing) {
|
|
651
|
+
for (const packageScope of agentPath.scope) existing.scope.add(packageScope);
|
|
652
|
+
continue;
|
|
653
|
+
}
|
|
654
|
+
seenAgents.set(agentKey, agentPath);
|
|
627
655
|
agents.push(agentPath);
|
|
628
656
|
}
|
|
629
657
|
for (const chainDir of paths.chains) {
|
|
630
|
-
|
|
631
|
-
seenChains.
|
|
658
|
+
const chainKey = `${chainDir.dir}\u0000${chainDir.packageRoot}`;
|
|
659
|
+
const existing = seenChains.get(chainKey);
|
|
660
|
+
if (existing) {
|
|
661
|
+
for (const packageScope of chainDir.scope) existing.scope.add(packageScope);
|
|
662
|
+
continue;
|
|
663
|
+
}
|
|
664
|
+
seenChains.set(chainKey, chainDir);
|
|
632
665
|
chains.push(chainDir);
|
|
633
666
|
}
|
|
634
667
|
}
|
|
635
|
-
return { agents, chains };
|
|
668
|
+
return { agents, chains, watchPaths, settingsErrors };
|
|
636
669
|
}
|
|
637
670
|
|
|
638
671
|
function normalizeAgentAliases(rawAliases: string[] | undefined, agentName: string): string[] | undefined {
|
|
@@ -1739,9 +1772,11 @@ function listFilesRecursive(
|
|
|
1739
1772
|
predicate: (fileName: string) => boolean,
|
|
1740
1773
|
rootDir = dir,
|
|
1741
1774
|
visitedDirectories = new Set<string>(),
|
|
1775
|
+
inspectedDirectories = new Set<string>(),
|
|
1742
1776
|
): string[] {
|
|
1743
1777
|
const files: string[] = [];
|
|
1744
1778
|
if (!fs.existsSync(dir)) return files;
|
|
1779
|
+
inspectedDirectories.add(path.resolve(dir));
|
|
1745
1780
|
let realDir: string;
|
|
1746
1781
|
try {
|
|
1747
1782
|
realDir = fs.realpathSync(dir);
|
|
@@ -1770,7 +1805,7 @@ function listFilesRecursive(
|
|
|
1770
1805
|
}
|
|
1771
1806
|
if (isDirectory) {
|
|
1772
1807
|
if (!shouldPruneDiscoveryDir(rootDir, filePath, entry.name)) {
|
|
1773
|
-
files.push(...listFilesRecursive(filePath, predicate, rootDir, visitedDirectories));
|
|
1808
|
+
files.push(...listFilesRecursive(filePath, predicate, rootDir, visitedDirectories, inspectedDirectories));
|
|
1774
1809
|
}
|
|
1775
1810
|
continue;
|
|
1776
1811
|
}
|
|
@@ -1786,6 +1821,17 @@ export interface AgentDefinitionInspection {
|
|
|
1786
1821
|
state: AgentDefinitionDirectoryState;
|
|
1787
1822
|
}
|
|
1788
1823
|
|
|
1824
|
+
const agentDefinitionInspectionDirectories = new WeakMap<AgentDefinitionInspection, string[]>();
|
|
1825
|
+
|
|
1826
|
+
function rememberAgentDefinitionInspection(inspection: AgentDefinitionInspection, directories: Iterable<string>): AgentDefinitionInspection {
|
|
1827
|
+
agentDefinitionInspectionDirectories.set(inspection, [...new Set([...directories].map((directory) => path.resolve(directory)))]);
|
|
1828
|
+
return inspection;
|
|
1829
|
+
}
|
|
1830
|
+
|
|
1831
|
+
function inspectedAgentDefinitionDirectories(inspection: AgentDefinitionInspection, root: string): string[] {
|
|
1832
|
+
return agentDefinitionInspectionDirectories.get(inspection) ?? [path.resolve(root)];
|
|
1833
|
+
}
|
|
1834
|
+
|
|
1789
1835
|
/** Narrow filesystem seam so unavailable paths can be tested without permissions. */
|
|
1790
1836
|
export interface AgentDefinitionInspectionFs {
|
|
1791
1837
|
existsSync(filePath: string): boolean;
|
|
@@ -1809,15 +1855,17 @@ const DEFAULT_AGENT_DEFINITION_INSPECTION_FS: AgentDefinitionInspectionFs = {
|
|
|
1809
1855
|
export function inspectAgentDefinitionDirectory(dir: string, operations: AgentDefinitionInspectionFs = DEFAULT_AGENT_DEFINITION_INSPECTION_FS): AgentDefinitionInspection {
|
|
1810
1856
|
const root = path.resolve(dir);
|
|
1811
1857
|
try {
|
|
1812
|
-
if (!operations.existsSync(root)) return { files: [], state: "absent" };
|
|
1813
|
-
if (!operations.statSync(root).isDirectory()) return { files: [], state: "not-directory" };
|
|
1858
|
+
if (!operations.existsSync(root)) return rememberAgentDefinitionInspection({ files: [], state: "absent" }, [root]);
|
|
1859
|
+
if (!operations.statSync(root).isDirectory()) return rememberAgentDefinitionInspection({ files: [], state: "not-directory" }, [root]);
|
|
1814
1860
|
} catch {
|
|
1815
|
-
return { files: [], state: "unreadable" };
|
|
1861
|
+
return rememberAgentDefinitionInspection({ files: [], state: "unreadable" }, [root]);
|
|
1816
1862
|
}
|
|
1817
1863
|
const files: string[] = [];
|
|
1818
1864
|
let unreadable = false;
|
|
1819
1865
|
const visitedDirectories = new Set<string>();
|
|
1866
|
+
const inspectedDirectories = new Set<string>();
|
|
1820
1867
|
const visit = (current: string): void => {
|
|
1868
|
+
inspectedDirectories.add(path.resolve(current));
|
|
1821
1869
|
let realDir: string;
|
|
1822
1870
|
try {
|
|
1823
1871
|
realDir = operations.realpathSync?.(current) ?? path.resolve(current);
|
|
@@ -1853,7 +1901,7 @@ export function inspectAgentDefinitionDirectory(dir: string, operations: AgentDe
|
|
|
1853
1901
|
}
|
|
1854
1902
|
};
|
|
1855
1903
|
visit(root);
|
|
1856
|
-
return { files, state: unreadable ? "unreadable" : files.length ? "candidates" : "empty" };
|
|
1904
|
+
return rememberAgentDefinitionInspection({ files, state: unreadable ? "unreadable" : files.length ? "candidates" : "empty" }, inspectedDirectories);
|
|
1857
1905
|
}
|
|
1858
1906
|
|
|
1859
1907
|
function isLegacyAgentSkillPath(rootDir: string, filePath: string): boolean {
|
|
@@ -1989,7 +2037,7 @@ function resolveAgentRelativeExtensionPaths(paths: string[] | undefined, agentFi
|
|
|
1989
2037
|
});
|
|
1990
2038
|
}
|
|
1991
2039
|
|
|
1992
|
-
function loadAgentsFromDefinitionFiles(files: AgentDefinitionFile[], source: AgentSource, discoveryPriority?: number, packageSource?: Omit<PackageSubagentPath, "dir">): { agents: AgentConfig[]; diagnostics: AgentDiscoveryDiagnostic[] } {
|
|
2040
|
+
function loadAgentsFromDefinitionFiles(files: AgentDefinitionFile[], source: AgentSource, discoveryPriority?: number, packageSource?: Omit<PackageSubagentPath, "dir" | "scope">): { agents: AgentConfig[]; diagnostics: AgentDiscoveryDiagnostic[] } {
|
|
1993
2041
|
const agents: AgentConfig[] = [];
|
|
1994
2042
|
const diagnostics: AgentDiscoveryDiagnostic[] = [];
|
|
1995
2043
|
|
|
@@ -2186,7 +2234,7 @@ function loadAgentsFromDefinitionFiles(files: AgentDefinitionFile[], source: Age
|
|
|
2186
2234
|
return { agents, diagnostics };
|
|
2187
2235
|
}
|
|
2188
2236
|
|
|
2189
|
-
function loadAgentsFromDir(dir: string, source: AgentSource, discoveryPriority?: number, packageSource?: Omit<PackageSubagentPath, "dir">, inspection = inspectAgentDefinitionDirectory(dir)): { agents: AgentConfig[]; diagnostics: AgentDiscoveryDiagnostic[] } {
|
|
2237
|
+
function loadAgentsFromDir(dir: string, source: AgentSource, discoveryPriority?: number, packageSource?: Omit<PackageSubagentPath, "dir" | "scope">, inspection = inspectAgentDefinitionDirectory(dir)): { agents: AgentConfig[]; diagnostics: AgentDiscoveryDiagnostic[] } {
|
|
2190
2238
|
return loadAgentsFromDefinitionFiles(readAgentDefinitionFiles(dir, inspection), source, discoveryPriority, packageSource);
|
|
2191
2239
|
}
|
|
2192
2240
|
|
|
@@ -2199,11 +2247,13 @@ function reportAgentDefinitionDirectory(source: AgentSource, dir: string, inspec
|
|
|
2199
2247
|
};
|
|
2200
2248
|
}
|
|
2201
2249
|
|
|
2202
|
-
function loadChainsFromDir(dir: string, source: AgentSource): { chains: ChainConfig[]; diagnostics: ChainDiscoveryDiagnostic[] } {
|
|
2250
|
+
function loadChainsFromDir(dir: string, source: AgentSource): { chains: ChainConfig[]; diagnostics: ChainDiscoveryDiagnostic[]; files: string[]; directories: string[] } {
|
|
2203
2251
|
const chains = new Map<string, ChainConfig>();
|
|
2204
2252
|
const diagnostics: ChainDiscoveryDiagnostic[] = [];
|
|
2253
|
+
const directories = new Set<string>();
|
|
2254
|
+
const files = listFilesRecursive(dir, (fileName) => fileName.endsWith(".chain.md") || fileName.endsWith(".chain.json"), dir, new Set<string>(), directories);
|
|
2205
2255
|
|
|
2206
|
-
for (const filePath of
|
|
2256
|
+
for (const filePath of files) {
|
|
2207
2257
|
let content: string;
|
|
2208
2258
|
try {
|
|
2209
2259
|
content = fs.readFileSync(filePath, "utf-8");
|
|
@@ -2222,7 +2272,7 @@ function loadChainsFromDir(dir: string, source: AgentSource): { chains: ChainCon
|
|
|
2222
2272
|
}
|
|
2223
2273
|
}
|
|
2224
2274
|
|
|
2225
|
-
return { chains: Array.from(chains.values()), diagnostics };
|
|
2275
|
+
return { chains: Array.from(chains.values()), diagnostics, files, directories: [...directories] };
|
|
2226
2276
|
}
|
|
2227
2277
|
|
|
2228
2278
|
function isDirectory(p: string): boolean {
|
|
@@ -2278,78 +2328,7 @@ function extraUserAgentDirs(): string[] {
|
|
|
2278
2328
|
.filter((dir) => dir.length > 0);
|
|
2279
2329
|
}
|
|
2280
2330
|
|
|
2281
|
-
export
|
|
2282
|
-
const effectiveCwd = path.resolve(cwd);
|
|
2283
|
-
const userDirOld = path.join(getAgentDir(), "agents");
|
|
2284
|
-
const userDirNew = path.join(os.homedir(), ".agents");
|
|
2285
|
-
const { readDirs: projectAgentDirs, candidateDirs: projectCandidateDirs, preferredDir: projectAgentsDir } = resolveNearestProjectAgentDirs(effectiveCwd);
|
|
2286
|
-
const userSettingsPath = getUserAgentSettingsPath();
|
|
2287
|
-
const projectSettingsPath = getProjectAgentSettingsPath(effectiveCwd);
|
|
2288
|
-
const userSettings = selectProviderOverrides(scope === "project" ? EMPTY_SUBAGENT_SETTINGS : readSubagentSettings(userSettingsPath), preferredModelProvider);
|
|
2289
|
-
const projectSettings = selectProviderOverrides(scope === "user" ? EMPTY_SUBAGENT_SETTINGS : readSubagentSettings(projectSettingsPath), preferredModelProvider);
|
|
2290
|
-
const defaultProvider = resolveSubagentDefaultProvider(userSettings, projectSettings, projectSettingsPath);
|
|
2291
|
-
const defaultModel = resolveSubagentDefaultModel(userSettings, projectSettings, userSettingsPath, projectSettingsPath, defaultProvider);
|
|
2292
|
-
const defaultThinking = resolveSubagentDefaultThinking(userSettings, projectSettings, projectSettingsPath);
|
|
2293
|
-
const maxThinking = resolveSubagentMaxThinking(userSettings, projectSettings, projectSettingsPath);
|
|
2294
|
-
const defaultExtensions = resolveSubagentDefaultExtensions(userSettings, projectSettings, projectSettingsPath);
|
|
2295
|
-
const modelScope = projectSettings.modelScope ?? userSettings.modelScope;
|
|
2296
|
-
const packageSubagentPaths = collectPackageSubagentPaths(effectiveCwd, {
|
|
2297
|
-
includeUser: scope !== "project",
|
|
2298
|
-
includeProject: scope !== "user",
|
|
2299
|
-
});
|
|
2300
|
-
const directories: AgentDefinitionDirectoryReport[] = [reportAgentDefinitionDirectory("builtin", BUILTIN_AGENTS_DIR, BUILTIN_AGENT_DEFINITION_INSPECTION)];
|
|
2301
|
-
|
|
2302
|
-
const builtinLoaded = loadAgentsFromDefinitionFiles(BUILTIN_AGENT_DEFINITION_FILES, "builtin");
|
|
2303
|
-
const builtinAgents = applyBuiltinOverrides(
|
|
2304
|
-
applySubagentDefaults(builtinLoaded.agents, defaultModel, defaultProvider, defaultThinking, defaultExtensions),
|
|
2305
|
-
userSettings, projectSettings, userSettingsPath, projectSettingsPath,
|
|
2306
|
-
);
|
|
2307
|
-
|
|
2308
|
-
const userLoaded = scope === "project" ? [] : [...extraUserAgentDirs(), userDirOld, userDirNew].map((dir, discoveryPriority) => {
|
|
2309
|
-
const inspection = inspectAgentDefinitionDirectory(dir);
|
|
2310
|
-
directories.push(reportAgentDefinitionDirectory("user", dir, inspection));
|
|
2311
|
-
return loadAgentsFromDir(dir, "user", discoveryPriority, undefined, inspection);
|
|
2312
|
-
});
|
|
2313
|
-
const userAgents = applyCustomAgentOverrides(
|
|
2314
|
-
applySubagentDefaults(userLoaded.flatMap((loaded) => loaded.agents), defaultModel, defaultProvider, defaultThinking, defaultExtensions),
|
|
2315
|
-
userSettings, projectSettings, userSettingsPath, projectSettingsPath,
|
|
2316
|
-
);
|
|
2317
|
-
|
|
2318
|
-
// Report both legacy and preferred project definition paths only after the
|
|
2319
|
-
// existing configured-root resolver found a root; never synthesize cwd paths.
|
|
2320
|
-
const projectInspections = scope === "user" ? new Map<string, AgentDefinitionInspection>() : new Map(projectCandidateDirs.map((dir) => [dir, inspectAgentDefinitionDirectory(dir)]));
|
|
2321
|
-
if (scope !== "user") for (const dir of projectCandidateDirs) directories.push(reportAgentDefinitionDirectory("project", dir, projectInspections.get(dir)!));
|
|
2322
|
-
const projectLoaded = scope === "user" ? [] : projectAgentDirs.map((dir) => loadAgentsFromDir(dir, "project", dir === projectAgentsDir ? 1 : 0, undefined, projectInspections.get(dir)!));
|
|
2323
|
-
const projectAgents = applyCustomAgentOverrides(
|
|
2324
|
-
applySubagentDefaults(projectLoaded.flatMap((loaded) => loaded.agents), defaultModel, defaultProvider, defaultThinking, defaultExtensions),
|
|
2325
|
-
userSettings, projectSettings, userSettingsPath, projectSettingsPath,
|
|
2326
|
-
);
|
|
2327
|
-
|
|
2328
|
-
const packageLoaded = packageSubagentPaths.agents.map((entry, index) => {
|
|
2329
|
-
const inspection = inspectAgentDefinitionDirectory(entry.dir);
|
|
2330
|
-
directories.push(reportAgentDefinitionDirectory("package", entry.dir, inspection));
|
|
2331
|
-
return loadAgentsFromDir(entry.dir, "package", packageSubagentPaths.agents.length - index, entry, inspection);
|
|
2332
|
-
});
|
|
2333
|
-
const packageMap = new Map<string, AgentConfig>();
|
|
2334
|
-
for (const loaded of packageLoaded) for (const agent of loaded.agents) if (!packageMap.has(agent.name)) packageMap.set(agent.name, agent);
|
|
2335
|
-
const packageAgents = applyCustomAgentOverrides(
|
|
2336
|
-
applySubagentDefaults(Array.from(packageMap.values()), defaultModel, defaultProvider, defaultThinking, defaultExtensions),
|
|
2337
|
-
userSettings, projectSettings, userSettingsPath, projectSettingsPath,
|
|
2338
|
-
);
|
|
2339
|
-
const agents = applySubagentMaxThinking(
|
|
2340
|
-
mergeAgentsForScope(scope, userAgents, projectAgents, builtinAgents, packageAgents).filter((agent) => agent.disabled !== true),
|
|
2341
|
-
maxThinking,
|
|
2342
|
-
);
|
|
2343
|
-
const agentDiagnostics = [
|
|
2344
|
-
...builtinLoaded.diagnostics,
|
|
2345
|
-
...userLoaded.flatMap((loaded) => loaded.diagnostics),
|
|
2346
|
-
...projectLoaded.flatMap((loaded) => loaded.diagnostics),
|
|
2347
|
-
...packageLoaded.flatMap((loaded) => loaded.diagnostics),
|
|
2348
|
-
];
|
|
2349
|
-
return { agents, agentDiagnostics, projectAgentsDir, cwd: effectiveCwd, scope, directories, ...(modelScope !== undefined ? { modelScope } : {}), ...(maxThinking !== undefined ? { maxThinking } : {}) };
|
|
2350
|
-
}
|
|
2351
|
-
|
|
2352
|
-
export function discoverAgentsAll(cwd: string, preferredModelProvider?: string): {
|
|
2331
|
+
export interface AgentDiscoveryAllResult {
|
|
2353
2332
|
builtin: AgentConfig[];
|
|
2354
2333
|
package: AgentConfig[];
|
|
2355
2334
|
user: AgentConfig[];
|
|
@@ -2364,125 +2343,452 @@ export function discoverAgentsAll(cwd: string, preferredModelProvider?: string):
|
|
|
2364
2343
|
userSettingsPath: string;
|
|
2365
2344
|
projectSettingsPath: string | null;
|
|
2366
2345
|
maxThinking?: ThinkingLevel;
|
|
2367
|
-
}
|
|
2346
|
+
}
|
|
2347
|
+
|
|
2348
|
+
/**
|
|
2349
|
+
* A single fresh source scan with both projections retained. The effective
|
|
2350
|
+
* projection intentionally remains separate from the all-source projection:
|
|
2351
|
+
* disabled and shadowed definitions are needed for diagnostics and runtime
|
|
2352
|
+
* collision checks even when they are not launchable.
|
|
2353
|
+
*/
|
|
2354
|
+
export interface AgentDiscoverySnapshot {
|
|
2355
|
+
effective: AgentDiscoveryResult;
|
|
2356
|
+
all: AgentDiscoveryAllResult;
|
|
2357
|
+
}
|
|
2358
|
+
|
|
2359
|
+
interface LoadedAgentDirectory {
|
|
2360
|
+
dir: string;
|
|
2361
|
+
inspection: AgentDefinitionInspection;
|
|
2362
|
+
loaded: ReturnType<typeof loadAgentsFromDir>;
|
|
2363
|
+
packageEntry?: PackageSubagentPath;
|
|
2364
|
+
}
|
|
2365
|
+
|
|
2366
|
+
interface AgentDiscoverySources {
|
|
2367
|
+
cwd: string;
|
|
2368
|
+
preferredModelProvider?: string;
|
|
2369
|
+
userDir: string;
|
|
2370
|
+
userDirOld: string;
|
|
2371
|
+
userDirNew: string;
|
|
2372
|
+
userChainDir: string;
|
|
2373
|
+
projectAgentDirs: string[];
|
|
2374
|
+
projectCandidateDirs: string[];
|
|
2375
|
+
projectAgentsDir: string | null;
|
|
2376
|
+
projectChainDirs: string[];
|
|
2377
|
+
projectChainDir: string | null;
|
|
2378
|
+
userSettingsPath: string;
|
|
2379
|
+
projectSettingsPath: string | null;
|
|
2380
|
+
userSettings?: SubagentSettings;
|
|
2381
|
+
projectSettings?: SubagentSettings;
|
|
2382
|
+
packageSubagentPaths: PackageSubagentPaths;
|
|
2383
|
+
builtinLoaded: ReturnType<typeof loadAgentsFromDefinitionFiles>;
|
|
2384
|
+
userLoaded: LoadedAgentDirectory[];
|
|
2385
|
+
projectLoaded: LoadedAgentDirectory[];
|
|
2386
|
+
projectInspections: Map<string, AgentDefinitionInspection>;
|
|
2387
|
+
packageLoaded: LoadedAgentDirectory[];
|
|
2388
|
+
userChains?: ReturnType<typeof loadChainsFromDir>;
|
|
2389
|
+
projectChainLoaded?: Array<{ dir: string; loaded: ReturnType<typeof loadChainsFromDir> }>;
|
|
2390
|
+
packageChainLoaded?: Array<{ entry: PackageChainPath; loaded: ReturnType<typeof loadChainsFromDir> }>;
|
|
2391
|
+
watchPaths: string[];
|
|
2392
|
+
}
|
|
2393
|
+
|
|
2394
|
+
interface AgentDiscoveryCacheEntry {
|
|
2395
|
+
sources: AgentDiscoverySources;
|
|
2396
|
+
fingerprint: string;
|
|
2397
|
+
}
|
|
2398
|
+
|
|
2399
|
+
const agentDiscoveryCache = new Map<string, AgentDiscoveryCacheEntry>();
|
|
2400
|
+
|
|
2401
|
+
function isPathWithin(root: string, candidate: string): boolean {
|
|
2402
|
+
const relative = path.relative(path.resolve(root), path.resolve(candidate));
|
|
2403
|
+
return relative === "" || (!relative.startsWith("..") && !path.isAbsolute(relative));
|
|
2404
|
+
}
|
|
2405
|
+
|
|
2406
|
+
function addDirectoryWatchPaths(paths: Set<string>, root: string, files: readonly string[], directories: readonly string[] = []): void {
|
|
2407
|
+
const resolvedRoot = path.resolve(root);
|
|
2408
|
+
paths.add(resolvedRoot);
|
|
2409
|
+
// The initial recursive inspection is already bounded to this discovery
|
|
2410
|
+
// root. Keep every visited directory in the fingerprint so a file added to
|
|
2411
|
+
// an existing empty nested directory invalidates the snapshot too.
|
|
2412
|
+
for (const directory of directories) {
|
|
2413
|
+
if (isPathWithin(resolvedRoot, directory)) paths.add(path.resolve(directory));
|
|
2414
|
+
}
|
|
2415
|
+
for (const file of files) {
|
|
2416
|
+
paths.add(path.resolve(file));
|
|
2417
|
+
let current = path.resolve(path.dirname(file));
|
|
2418
|
+
while (isPathWithin(resolvedRoot, current)) {
|
|
2419
|
+
paths.add(current);
|
|
2420
|
+
if (current === resolvedRoot) break;
|
|
2421
|
+
current = path.dirname(current);
|
|
2422
|
+
}
|
|
2423
|
+
}
|
|
2424
|
+
}
|
|
2425
|
+
|
|
2426
|
+
function watchPathSignature(filePath: string): string {
|
|
2427
|
+
try {
|
|
2428
|
+
const stat = fs.statSync(filePath);
|
|
2429
|
+
if (stat.isDirectory()) {
|
|
2430
|
+
const entries = fs.readdirSync(filePath, { withFileTypes: true })
|
|
2431
|
+
.sort((left, right) => left.name.localeCompare(right.name))
|
|
2432
|
+
.map((entry) => `${entry.name}:${entry.isDirectory() ? "d" : entry.isSymbolicLink() ? "l" : "f"}`)
|
|
2433
|
+
.join("|");
|
|
2434
|
+
return `directory:${entries}`;
|
|
2435
|
+
}
|
|
2436
|
+
return `file:${stat.size}:${stat.mtimeMs}`;
|
|
2437
|
+
} catch {
|
|
2438
|
+
return "missing";
|
|
2439
|
+
}
|
|
2440
|
+
}
|
|
2441
|
+
|
|
2442
|
+
function discoveryFingerprint(paths: readonly string[]): string {
|
|
2443
|
+
return [...new Set(paths)]
|
|
2444
|
+
.sort((left, right) => left.localeCompare(right))
|
|
2445
|
+
.map((filePath) => `${filePath}:${watchPathSignature(filePath)}`)
|
|
2446
|
+
.join("\n");
|
|
2447
|
+
}
|
|
2448
|
+
|
|
2449
|
+
function discoveryCacheKey(cwd: string, preferredModelProvider: string | undefined): string {
|
|
2450
|
+
return JSON.stringify([
|
|
2451
|
+
path.resolve(cwd),
|
|
2452
|
+
preferredModelProvider ?? null,
|
|
2453
|
+
getProjectConfigDir(path.resolve(cwd)),
|
|
2454
|
+
getAgentDir(),
|
|
2455
|
+
os.homedir(),
|
|
2456
|
+
process.env.HOME ?? "",
|
|
2457
|
+
process.env.USERPROFILE ?? "",
|
|
2458
|
+
process.env[EXTRA_AGENT_DIRS_ENV] ?? "",
|
|
2459
|
+
process.env.PI_OFFLINE ?? "",
|
|
2460
|
+
process.env.APPDATA ?? "",
|
|
2461
|
+
]);
|
|
2462
|
+
}
|
|
2463
|
+
|
|
2464
|
+
function projectDiscoveryWatchPaths(cwd: string): string[] {
|
|
2465
|
+
const paths: string[] = [];
|
|
2466
|
+
let current = path.resolve(cwd);
|
|
2467
|
+
while (true) {
|
|
2468
|
+
paths.push(getProjectConfigDir(current), path.join(current, ".agents"), path.join(current, ".git"));
|
|
2469
|
+
const parent = path.dirname(current);
|
|
2470
|
+
if (parent === current) break;
|
|
2471
|
+
current = parent;
|
|
2472
|
+
}
|
|
2473
|
+
return paths;
|
|
2474
|
+
}
|
|
2475
|
+
|
|
2476
|
+
function packageEntryIncluded(scope: AgentScope, packageScopes: PackageSubagentPath["scope"]): boolean {
|
|
2477
|
+
if (scope === "both" || packageScopes.has("root")) return true;
|
|
2478
|
+
return packageScopes.has(scope);
|
|
2479
|
+
}
|
|
2480
|
+
|
|
2481
|
+
function buildAgentDiscoverySources(cwd: string, preferredModelProvider?: string): AgentDiscoverySources {
|
|
2482
|
+
const effectiveCwd = path.resolve(cwd);
|
|
2368
2483
|
const userDirOld = path.join(getAgentDir(), "agents");
|
|
2369
2484
|
const userDirNew = path.join(os.homedir(), ".agents");
|
|
2370
2485
|
const userChainDir = getUserChainDir();
|
|
2371
|
-
const { readDirs:
|
|
2372
|
-
const { readDirs: projectChainDirs, preferredDir: projectChainDir } = resolveNearestProjectChainDirs(
|
|
2486
|
+
const { readDirs: projectAgentDirs, candidateDirs: projectCandidateDirs, preferredDir: projectAgentsDir } = resolveNearestProjectAgentDirs(effectiveCwd);
|
|
2487
|
+
const { readDirs: projectChainDirs, preferredDir: projectChainDir } = resolveNearestProjectChainDirs(effectiveCwd);
|
|
2373
2488
|
const userSettingsPath = getUserAgentSettingsPath();
|
|
2374
|
-
const projectSettingsPath = getProjectAgentSettingsPath(
|
|
2375
|
-
const
|
|
2376
|
-
const projectSettings = selectProviderOverrides(readSubagentSettings(projectSettingsPath), preferredModelProvider);
|
|
2377
|
-
const defaultProvider = resolveSubagentDefaultProvider(userSettings, projectSettings, projectSettingsPath);
|
|
2378
|
-
const defaultModel = resolveSubagentDefaultModel(userSettings, projectSettings, userSettingsPath, projectSettingsPath, defaultProvider);
|
|
2379
|
-
const defaultThinking = resolveSubagentDefaultThinking(userSettings, projectSettings, projectSettingsPath);
|
|
2380
|
-
const maxThinking = resolveSubagentMaxThinking(userSettings, projectSettings, projectSettingsPath);
|
|
2381
|
-
const defaultExtensions = resolveSubagentDefaultExtensions(userSettings, projectSettings, projectSettingsPath);
|
|
2382
|
-
const packageSubagentPaths = collectPackageSubagentPaths(cwd);
|
|
2489
|
+
const projectSettingsPath = getProjectAgentSettingsPath(effectiveCwd);
|
|
2490
|
+
const packageSubagentPaths = collectPackageSubagentPaths(effectiveCwd);
|
|
2383
2491
|
|
|
2384
2492
|
const builtinLoaded = loadAgentsFromDefinitionFiles(BUILTIN_AGENT_DEFINITION_FILES, "builtin");
|
|
2385
|
-
const
|
|
2386
|
-
|
|
2387
|
-
|
|
2388
|
-
|
|
2493
|
+
const userLoaded = [...extraUserAgentDirs(), userDirOld, userDirNew].map((dir, discoveryPriority): LoadedAgentDirectory => {
|
|
2494
|
+
const inspection = inspectAgentDefinitionDirectory(dir);
|
|
2495
|
+
return { dir, inspection, loaded: loadAgentsFromDir(dir, "user", discoveryPriority, undefined, inspection) };
|
|
2496
|
+
});
|
|
2497
|
+
const projectInspections = new Map(projectCandidateDirs.map((dir) => [dir, inspectAgentDefinitionDirectory(dir)]));
|
|
2498
|
+
const projectLoaded = projectAgentDirs.map((dir): LoadedAgentDirectory => {
|
|
2499
|
+
const inspection = projectInspections.get(dir)!;
|
|
2500
|
+
return { dir, inspection, loaded: loadAgentsFromDir(dir, "project", dir === projectAgentsDir ? 1 : 0, undefined, inspection) };
|
|
2501
|
+
});
|
|
2502
|
+
const packageLoaded = packageSubagentPaths.agents.map((entry, index): LoadedAgentDirectory => {
|
|
2503
|
+
const inspection = inspectAgentDefinitionDirectory(entry.dir);
|
|
2504
|
+
return { dir: entry.dir, inspection, packageEntry: entry, loaded: loadAgentsFromDir(entry.dir, "package", packageSubagentPaths.agents.length - index, entry, inspection) };
|
|
2505
|
+
});
|
|
2506
|
+
|
|
2507
|
+
const watchPaths = new Set<string>([
|
|
2389
2508
|
userSettingsPath,
|
|
2390
|
-
projectSettingsPath,
|
|
2391
|
-
|
|
2392
|
-
|
|
2393
|
-
.
|
|
2394
|
-
|
|
2395
|
-
|
|
2396
|
-
|
|
2397
|
-
|
|
2509
|
+
...(projectSettingsPath ? [projectSettingsPath] : []),
|
|
2510
|
+
...projectDiscoveryWatchPaths(effectiveCwd),
|
|
2511
|
+
...findProjectRootCandidates(effectiveCwd).map((root) => path.join(getProjectConfigDir(root), "settings.json")),
|
|
2512
|
+
...packageSubagentPaths.watchPaths,
|
|
2513
|
+
]);
|
|
2514
|
+
for (const directory of userLoaded) addDirectoryWatchPaths(watchPaths, directory.dir, directory.inspection.files, inspectedAgentDefinitionDirectories(directory.inspection, directory.dir));
|
|
2515
|
+
for (const dir of projectCandidateDirs) {
|
|
2516
|
+
const inspection = projectInspections.get(dir);
|
|
2517
|
+
addDirectoryWatchPaths(watchPaths, dir, inspection?.files ?? [], inspection ? inspectedAgentDefinitionDirectories(inspection, dir) : []);
|
|
2518
|
+
}
|
|
2519
|
+
for (const directory of packageLoaded) addDirectoryWatchPaths(watchPaths, directory.dir, directory.inspection.files, inspectedAgentDefinitionDirectories(directory.inspection, directory.dir));
|
|
2520
|
+
|
|
2521
|
+
const userDir = process.env.PI_CODING_AGENT_DIR ? userDirOld : fs.existsSync(userDirNew) ? userDirNew : userDirOld;
|
|
2522
|
+
return {
|
|
2523
|
+
cwd: effectiveCwd,
|
|
2524
|
+
...(preferredModelProvider !== undefined ? { preferredModelProvider } : {}),
|
|
2525
|
+
userDir,
|
|
2526
|
+
userDirOld,
|
|
2527
|
+
userDirNew,
|
|
2528
|
+
userChainDir,
|
|
2529
|
+
projectAgentDirs,
|
|
2530
|
+
projectCandidateDirs,
|
|
2531
|
+
projectAgentsDir,
|
|
2532
|
+
projectChainDirs,
|
|
2533
|
+
projectChainDir,
|
|
2398
2534
|
userSettingsPath,
|
|
2399
2535
|
projectSettingsPath,
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
|
|
2407
|
-
|
|
2536
|
+
packageSubagentPaths,
|
|
2537
|
+
builtinLoaded,
|
|
2538
|
+
userLoaded,
|
|
2539
|
+
projectLoaded,
|
|
2540
|
+
projectInspections,
|
|
2541
|
+
packageLoaded,
|
|
2542
|
+
watchPaths: [...watchPaths],
|
|
2543
|
+
};
|
|
2544
|
+
}
|
|
2545
|
+
|
|
2546
|
+
function ensureDiscoveryChains(sources: AgentDiscoverySources): void {
|
|
2547
|
+
if (sources.userChains) return;
|
|
2548
|
+
sources.packageChainLoaded = sources.packageSubagentPaths.chains.map((entry) => ({ entry, loaded: loadChainsFromDir(entry.dir, "package") }));
|
|
2549
|
+
sources.userChains = loadChainsFromDir(sources.userChainDir, "user");
|
|
2550
|
+
sources.projectChainLoaded = sources.projectChainDirs.map((dir) => ({ dir, loaded: loadChainsFromDir(dir, "project") }));
|
|
2551
|
+
const watchPaths = new Set(sources.watchPaths);
|
|
2552
|
+
addDirectoryWatchPaths(watchPaths, sources.userChainDir, sources.userChains.files, sources.userChains.directories);
|
|
2553
|
+
for (const chain of sources.projectChainLoaded) addDirectoryWatchPaths(watchPaths, chain.dir, chain.loaded.files, chain.loaded.directories);
|
|
2554
|
+
for (const chain of sources.packageChainLoaded) addDirectoryWatchPaths(watchPaths, chain.entry.dir, chain.loaded.files, chain.loaded.directories);
|
|
2555
|
+
sources.watchPaths = [...watchPaths];
|
|
2556
|
+
}
|
|
2557
|
+
|
|
2558
|
+
function getAgentDiscoverySources(cwd: string, preferredModelProvider?: string, includeChains = false): AgentDiscoverySources {
|
|
2559
|
+
const key = discoveryCacheKey(cwd, preferredModelProvider);
|
|
2560
|
+
const cached = agentDiscoveryCache.get(key);
|
|
2561
|
+
if (cached && cached.fingerprint === discoveryFingerprint(cached.sources.watchPaths)) {
|
|
2562
|
+
if (includeChains) {
|
|
2563
|
+
ensureDiscoveryChains(cached.sources);
|
|
2564
|
+
cached.fingerprint = discoveryFingerprint(cached.sources.watchPaths);
|
|
2408
2565
|
}
|
|
2566
|
+
return cached.sources;
|
|
2567
|
+
}
|
|
2568
|
+
const sources = buildAgentDiscoverySources(cwd, preferredModelProvider);
|
|
2569
|
+
const entry = { sources, fingerprint: discoveryFingerprint(sources.watchPaths) };
|
|
2570
|
+
agentDiscoveryCache.set(key, entry);
|
|
2571
|
+
if (includeChains) {
|
|
2572
|
+
ensureDiscoveryChains(sources);
|
|
2573
|
+
entry.fingerprint = discoveryFingerprint(sources.watchPaths);
|
|
2409
2574
|
}
|
|
2410
|
-
|
|
2411
|
-
|
|
2575
|
+
return sources;
|
|
2576
|
+
}
|
|
2577
|
+
|
|
2578
|
+
/** Clear process-local discovery snapshots, primarily for hosts that reload settings in place. */
|
|
2579
|
+
export function clearAgentDiscoveryCache(): void {
|
|
2580
|
+
agentDiscoveryCache.clear();
|
|
2581
|
+
}
|
|
2582
|
+
|
|
2583
|
+
function ensureSettingsForScope(sources: AgentDiscoverySources, scope: AgentScope, preferredModelProvider?: string): void {
|
|
2584
|
+
if (scope !== "project" && sources.userSettings === undefined) {
|
|
2585
|
+
if (sources.packageSubagentPaths.settingsErrors.user) throw sources.packageSubagentPaths.settingsErrors.user;
|
|
2586
|
+
sources.userSettings = selectProviderOverrides(readSubagentSettings(sources.userSettingsPath), preferredModelProvider);
|
|
2587
|
+
}
|
|
2588
|
+
if (scope !== "user" && sources.projectSettings === undefined) {
|
|
2589
|
+
if (sources.packageSubagentPaths.settingsErrors.project) throw sources.packageSubagentPaths.settingsErrors.project;
|
|
2590
|
+
sources.projectSettings = selectProviderOverrides(readSubagentSettings(sources.projectSettingsPath), preferredModelProvider);
|
|
2591
|
+
}
|
|
2592
|
+
}
|
|
2593
|
+
|
|
2594
|
+
function settingsForScope(sources: AgentDiscoverySources, scope: AgentScope): { user: SubagentSettings; project: SubagentSettings } {
|
|
2595
|
+
// Parse only settings that participate in this projection. The source cache
|
|
2596
|
+
// still retains both source trees, while malformed out-of-scope settings do
|
|
2597
|
+
// not make an otherwise valid scoped projection fail.
|
|
2598
|
+
ensureSettingsForScope(sources, scope, sources.preferredModelProvider);
|
|
2599
|
+
return {
|
|
2600
|
+
user: scope === "project" ? EMPTY_SUBAGENT_SETTINGS : sources.userSettings ?? EMPTY_SUBAGENT_SETTINGS,
|
|
2601
|
+
project: scope === "user" ? EMPTY_SUBAGENT_SETTINGS : sources.projectSettings ?? EMPTY_SUBAGENT_SETTINGS,
|
|
2602
|
+
};
|
|
2603
|
+
}
|
|
2604
|
+
|
|
2605
|
+
function configuredAgentsForScope(sources: AgentDiscoverySources, scope: AgentScope, settingsScope = scope): {
|
|
2606
|
+
builtin: AgentConfig[];
|
|
2607
|
+
package: AgentConfig[];
|
|
2608
|
+
user: AgentConfig[];
|
|
2609
|
+
project: AgentConfig[];
|
|
2610
|
+
userSettings: SubagentSettings;
|
|
2611
|
+
projectSettings: SubagentSettings;
|
|
2612
|
+
maxThinking?: ThinkingLevel;
|
|
2613
|
+
modelScope?: ModelScopeConfig;
|
|
2614
|
+
} {
|
|
2615
|
+
const { user: userSettings, project: projectSettings } = settingsForScope(sources, settingsScope);
|
|
2616
|
+
const defaultProvider = resolveSubagentDefaultProvider(userSettings, projectSettings, sources.projectSettingsPath);
|
|
2617
|
+
const defaultModel = resolveSubagentDefaultModel(userSettings, projectSettings, sources.userSettingsPath, sources.projectSettingsPath, defaultProvider);
|
|
2618
|
+
const defaultThinking = resolveSubagentDefaultThinking(userSettings, projectSettings, sources.projectSettingsPath);
|
|
2619
|
+
const maxThinking = resolveSubagentMaxThinking(userSettings, projectSettings, sources.projectSettingsPath);
|
|
2620
|
+
const defaultExtensions = resolveSubagentDefaultExtensions(userSettings, projectSettings, sources.projectSettingsPath);
|
|
2621
|
+
const applyDefaults = (agents: AgentConfig[]): AgentConfig[] => applySubagentDefaults(agents, defaultModel, defaultProvider, defaultThinking, defaultExtensions);
|
|
2622
|
+
const builtin = applyBuiltinOverrides(applyDefaults(sources.builtinLoaded.agents), userSettings, projectSettings, sources.userSettingsPath, sources.projectSettingsPath);
|
|
2623
|
+
const user = applyCustomAgentOverrides(
|
|
2624
|
+
applyDefaults(scope === "project" ? [] : sources.userLoaded.flatMap((loaded) => loaded.loaded.agents)),
|
|
2412
2625
|
userSettings,
|
|
2413
2626
|
projectSettings,
|
|
2414
|
-
userSettingsPath,
|
|
2415
|
-
projectSettingsPath,
|
|
2627
|
+
sources.userSettingsPath,
|
|
2628
|
+
sources.projectSettingsPath,
|
|
2416
2629
|
);
|
|
2417
2630
|
const projectMap = new Map<string, AgentConfig>();
|
|
2418
|
-
const
|
|
2419
|
-
|
|
2420
|
-
|
|
2421
|
-
|
|
2422
|
-
|
|
2423
|
-
|
|
2424
|
-
}
|
|
2631
|
+
if (scope !== "user") for (const loaded of sources.projectLoaded) for (const agent of loaded.loaded.agents) projectMap.set(agent.name, agent);
|
|
2632
|
+
const project = applyCustomAgentOverrides(applyDefaults(Array.from(projectMap.values())), userSettings, projectSettings, sources.userSettingsPath, sources.projectSettingsPath);
|
|
2633
|
+
const packageMap = new Map<string, AgentConfig>();
|
|
2634
|
+
for (const loaded of sources.packageLoaded) {
|
|
2635
|
+
if (!loaded.packageEntry || !packageEntryIncluded(scope, loaded.packageEntry.scope)) continue;
|
|
2636
|
+
for (const agent of loaded.loaded.agents) if (!packageMap.has(agent.name)) packageMap.set(agent.name, agent);
|
|
2425
2637
|
}
|
|
2426
|
-
const
|
|
2427
|
-
|
|
2638
|
+
const packageAgents = applyCustomAgentOverrides(applyDefaults(Array.from(packageMap.values())), userSettings, projectSettings, sources.userSettingsPath, sources.projectSettingsPath);
|
|
2639
|
+
return {
|
|
2640
|
+
builtin,
|
|
2641
|
+
package: packageAgents,
|
|
2642
|
+
user,
|
|
2643
|
+
project,
|
|
2428
2644
|
userSettings,
|
|
2429
2645
|
projectSettings,
|
|
2430
|
-
|
|
2431
|
-
|
|
2646
|
+
maxThinking,
|
|
2647
|
+
modelScope: projectSettings.modelScope ?? userSettings.modelScope,
|
|
2648
|
+
};
|
|
2649
|
+
}
|
|
2650
|
+
|
|
2651
|
+
function discoveryDirectories(sources: AgentDiscoverySources, scope: AgentScope): AgentDefinitionDirectoryReport[] {
|
|
2652
|
+
const directories: AgentDefinitionDirectoryReport[] = [reportAgentDefinitionDirectory("builtin", BUILTIN_AGENTS_DIR, BUILTIN_AGENT_DEFINITION_INSPECTION)];
|
|
2653
|
+
if (scope !== "project") for (const directory of sources.userLoaded) directories.push(reportAgentDefinitionDirectory("user", directory.dir, directory.inspection));
|
|
2654
|
+
if (scope !== "user") for (const dir of sources.projectCandidateDirs) directories.push(reportAgentDefinitionDirectory("project", dir, sources.projectInspections.get(dir)!));
|
|
2655
|
+
for (const directory of sources.packageLoaded) {
|
|
2656
|
+
if (directory.packageEntry && packageEntryIncluded(scope, directory.packageEntry.scope)) directories.push(reportAgentDefinitionDirectory("package", directory.dir, directory.inspection));
|
|
2657
|
+
}
|
|
2658
|
+
return directories;
|
|
2659
|
+
}
|
|
2660
|
+
|
|
2661
|
+
function discoveryDiagnostics(sources: AgentDiscoverySources, scope: AgentScope): AgentDiscoveryDiagnostic[] {
|
|
2662
|
+
return [
|
|
2663
|
+
...sources.builtinLoaded.diagnostics,
|
|
2664
|
+
...(scope === "project" ? [] : sources.userLoaded.flatMap((loaded) => loaded.loaded.diagnostics)),
|
|
2665
|
+
...(scope === "user" ? [] : sources.projectLoaded.flatMap((loaded) => loaded.loaded.diagnostics)),
|
|
2666
|
+
...sources.packageLoaded
|
|
2667
|
+
.filter((loaded) => loaded.packageEntry && packageEntryIncluded(scope, loaded.packageEntry.scope))
|
|
2668
|
+
.flatMap((loaded) => loaded.loaded.diagnostics),
|
|
2669
|
+
];
|
|
2670
|
+
}
|
|
2671
|
+
|
|
2672
|
+
function buildEffectiveDiscovery(sources: AgentDiscoverySources, scope: AgentScope): AgentDiscoveryResult {
|
|
2673
|
+
const configured = configuredAgentsForScope(sources, scope);
|
|
2674
|
+
const agents = applySubagentMaxThinking(
|
|
2675
|
+
mergeAgentsForScope(scope, configured.user, configured.project, configured.builtin, configured.package).filter((agent) => agent.disabled !== true),
|
|
2676
|
+
configured.maxThinking,
|
|
2432
2677
|
);
|
|
2678
|
+
return {
|
|
2679
|
+
agents,
|
|
2680
|
+
agentDiagnostics: discoveryDiagnostics(sources, scope),
|
|
2681
|
+
projectAgentsDir: sources.projectAgentsDir,
|
|
2682
|
+
cwd: sources.cwd,
|
|
2683
|
+
scope,
|
|
2684
|
+
directories: discoveryDirectories(sources, scope),
|
|
2685
|
+
...(configured.modelScope !== undefined ? { modelScope: configured.modelScope } : {}),
|
|
2686
|
+
...(configured.maxThinking !== undefined ? { maxThinking: configured.maxThinking } : {}),
|
|
2687
|
+
};
|
|
2688
|
+
}
|
|
2433
2689
|
|
|
2434
|
-
|
|
2435
|
-
|
|
2690
|
+
function buildAllDiscovery(sources: AgentDiscoverySources, includeChains: boolean, settingsScope: AgentScope): AgentDiscoveryAllResult {
|
|
2691
|
+
if (includeChains) ensureDiscoveryChains(sources);
|
|
2692
|
+
const configured = configuredAgentsForScope(sources, "both", settingsScope);
|
|
2436
2693
|
const packageChainMap = new Map<string, ChainConfig>();
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
packageChainDiagnostics.push(...loaded.diagnostics);
|
|
2440
|
-
for (const
|
|
2441
|
-
if (!packageChainMap.has(chain.name)) packageChainMap.set(chain.name, chain);
|
|
2442
|
-
}
|
|
2694
|
+
const packageChainDiagnostics: ChainDiscoveryDiagnostic[] = [];
|
|
2695
|
+
for (const chain of sources.packageChainLoaded ?? []) {
|
|
2696
|
+
packageChainDiagnostics.push(...chain.loaded.diagnostics);
|
|
2697
|
+
for (const definition of chain.loaded.chains) if (!packageChainMap.has(definition.name)) packageChainMap.set(definition.name, definition);
|
|
2443
2698
|
}
|
|
2699
|
+
const projectChainMap = new Map<string, ChainConfig>();
|
|
2444
2700
|
const projectChainDiagnostics: ChainDiscoveryDiagnostic[] = [];
|
|
2445
|
-
for (const
|
|
2446
|
-
|
|
2447
|
-
|
|
2448
|
-
for (const chain of loaded.chains) {
|
|
2449
|
-
chainMap.set(chain.name, chain);
|
|
2450
|
-
}
|
|
2701
|
+
for (const chain of sources.projectChainLoaded ?? []) {
|
|
2702
|
+
projectChainDiagnostics.push(...chain.loaded.diagnostics);
|
|
2703
|
+
for (const definition of chain.loaded.chains) projectChainMap.set(definition.name, definition);
|
|
2451
2704
|
}
|
|
2452
|
-
const userChains = loadChainsFromDir(userChainDir, "user");
|
|
2453
2705
|
const chains = [
|
|
2454
2706
|
...Array.from(packageChainMap.values()),
|
|
2455
|
-
...userChains
|
|
2456
|
-
...Array.from(
|
|
2457
|
-
];
|
|
2458
|
-
const chainDiagnostics = [
|
|
2459
|
-
...packageChainDiagnostics,
|
|
2460
|
-
...userChains.diagnostics,
|
|
2461
|
-
...projectChainDiagnostics,
|
|
2707
|
+
...(sources.userChains?.chains ?? []),
|
|
2708
|
+
...Array.from(projectChainMap.values()),
|
|
2462
2709
|
];
|
|
2463
|
-
const agentDiagnostics = [
|
|
2464
|
-
...builtinLoaded.diagnostics,
|
|
2465
|
-
...userLoaded.flatMap((loaded) => loaded.diagnostics),
|
|
2466
|
-
...packageAgentDiagnostics,
|
|
2467
|
-
...projectAgentDiagnostics,
|
|
2468
|
-
];
|
|
2469
|
-
|
|
2470
|
-
const userDir = process.env.PI_CODING_AGENT_DIR ? userDirOld : fs.existsSync(userDirNew) ? userDirNew : userDirOld;
|
|
2471
|
-
|
|
2472
2710
|
return {
|
|
2473
|
-
builtin: applySubagentMaxThinking(builtin, maxThinking),
|
|
2474
|
-
package: applySubagentMaxThinking(
|
|
2475
|
-
user: applySubagentMaxThinking(user, maxThinking),
|
|
2476
|
-
project: applySubagentMaxThinking(project, maxThinking),
|
|
2477
|
-
agentDiagnostics
|
|
2711
|
+
builtin: applySubagentMaxThinking(configured.builtin, configured.maxThinking),
|
|
2712
|
+
package: applySubagentMaxThinking(configured.package, configured.maxThinking),
|
|
2713
|
+
user: applySubagentMaxThinking(configured.user, configured.maxThinking),
|
|
2714
|
+
project: applySubagentMaxThinking(configured.project, configured.maxThinking),
|
|
2715
|
+
agentDiagnostics: [
|
|
2716
|
+
...sources.builtinLoaded.diagnostics,
|
|
2717
|
+
...sources.userLoaded.flatMap((loaded) => loaded.loaded.diagnostics),
|
|
2718
|
+
...sources.packageLoaded.flatMap((loaded) => loaded.loaded.diagnostics),
|
|
2719
|
+
...sources.projectLoaded.flatMap((loaded) => loaded.loaded.diagnostics),
|
|
2720
|
+
],
|
|
2478
2721
|
chains,
|
|
2479
|
-
chainDiagnostics,
|
|
2480
|
-
userDir,
|
|
2481
|
-
projectDir,
|
|
2482
|
-
userChainDir,
|
|
2483
|
-
projectChainDir,
|
|
2484
|
-
userSettingsPath,
|
|
2485
|
-
projectSettingsPath,
|
|
2486
|
-
...(maxThinking !== undefined ? { maxThinking } : {}),
|
|
2722
|
+
chainDiagnostics: [...packageChainDiagnostics, ...(sources.userChains?.diagnostics ?? []), ...projectChainDiagnostics],
|
|
2723
|
+
userDir: sources.userDir,
|
|
2724
|
+
projectDir: sources.projectAgentsDir,
|
|
2725
|
+
userChainDir: sources.userChainDir,
|
|
2726
|
+
projectChainDir: sources.projectChainDir,
|
|
2727
|
+
userSettingsPath: sources.userSettingsPath,
|
|
2728
|
+
projectSettingsPath: sources.projectSettingsPath,
|
|
2729
|
+
...(configured.maxThinking !== undefined ? { maxThinking: configured.maxThinking } : {}),
|
|
2487
2730
|
};
|
|
2488
2731
|
}
|
|
2732
|
+
|
|
2733
|
+
export function discoverAgentSnapshot(
|
|
2734
|
+
cwd: string,
|
|
2735
|
+
scope: AgentScope,
|
|
2736
|
+
preferredModelProvider?: string,
|
|
2737
|
+
options: { includeChains?: boolean } = {},
|
|
2738
|
+
): AgentDiscoverySnapshot {
|
|
2739
|
+
const includeChains = options.includeChains !== false;
|
|
2740
|
+
const sources = getAgentDiscoverySources(cwd, preferredModelProvider, includeChains);
|
|
2741
|
+
return { effective: buildEffectiveDiscovery(sources, scope), all: buildAllDiscovery(sources, includeChains, scope) };
|
|
2742
|
+
}
|
|
2743
|
+
|
|
2744
|
+
function discoverAgentsUncached(cwd: string, scope: AgentScope, preferredModelProvider?: string): AgentDiscoveryResult {
|
|
2745
|
+
const effectiveCwd = path.resolve(cwd);
|
|
2746
|
+
const userDirOld = path.join(getAgentDir(), "agents");
|
|
2747
|
+
const userDirNew = path.join(os.homedir(), ".agents");
|
|
2748
|
+
const { readDirs: projectAgentDirs, candidateDirs: projectCandidateDirs, preferredDir: projectAgentsDir } = resolveNearestProjectAgentDirs(effectiveCwd);
|
|
2749
|
+
const userSettingsPath = getUserAgentSettingsPath();
|
|
2750
|
+
const projectSettingsPath = getProjectAgentSettingsPath(effectiveCwd);
|
|
2751
|
+
const userSettings = selectProviderOverrides(scope === "project" ? EMPTY_SUBAGENT_SETTINGS : readSubagentSettings(userSettingsPath), preferredModelProvider);
|
|
2752
|
+
const projectSettings = selectProviderOverrides(scope === "user" ? EMPTY_SUBAGENT_SETTINGS : readSubagentSettings(projectSettingsPath), preferredModelProvider);
|
|
2753
|
+
const defaultProvider = resolveSubagentDefaultProvider(userSettings, projectSettings, projectSettingsPath);
|
|
2754
|
+
const defaultModel = resolveSubagentDefaultModel(userSettings, projectSettings, userSettingsPath, projectSettingsPath, defaultProvider);
|
|
2755
|
+
const defaultThinking = resolveSubagentDefaultThinking(userSettings, projectSettings, projectSettingsPath);
|
|
2756
|
+
const maxThinking = resolveSubagentMaxThinking(userSettings, projectSettings, projectSettingsPath);
|
|
2757
|
+
const defaultExtensions = resolveSubagentDefaultExtensions(userSettings, projectSettings, projectSettingsPath);
|
|
2758
|
+
const modelScope = projectSettings.modelScope ?? userSettings.modelScope;
|
|
2759
|
+
const packageSubagentPaths = collectPackageSubagentPaths(effectiveCwd, { includeUser: scope !== "project", includeProject: scope !== "user" });
|
|
2760
|
+
const directories: AgentDefinitionDirectoryReport[] = [reportAgentDefinitionDirectory("builtin", BUILTIN_AGENTS_DIR, BUILTIN_AGENT_DEFINITION_INSPECTION)];
|
|
2761
|
+
const builtinLoaded = loadAgentsFromDefinitionFiles(BUILTIN_AGENT_DEFINITION_FILES, "builtin");
|
|
2762
|
+
const builtinAgents = applyBuiltinOverrides(applySubagentDefaults(builtinLoaded.agents, defaultModel, defaultProvider, defaultThinking, defaultExtensions), userSettings, projectSettings, userSettingsPath, projectSettingsPath);
|
|
2763
|
+
const userLoaded = scope === "project" ? [] : [...extraUserAgentDirs(), userDirOld, userDirNew].map((dir, discoveryPriority) => {
|
|
2764
|
+
const inspection = inspectAgentDefinitionDirectory(dir);
|
|
2765
|
+
directories.push(reportAgentDefinitionDirectory("user", dir, inspection));
|
|
2766
|
+
return loadAgentsFromDir(dir, "user", discoveryPriority, undefined, inspection);
|
|
2767
|
+
});
|
|
2768
|
+
const userAgents = applyCustomAgentOverrides(applySubagentDefaults(userLoaded.flatMap((loaded) => loaded.agents), defaultModel, defaultProvider, defaultThinking, defaultExtensions), userSettings, projectSettings, userSettingsPath, projectSettingsPath);
|
|
2769
|
+
const projectInspections = scope === "user" ? new Map<string, AgentDefinitionInspection>() : new Map(projectCandidateDirs.map((dir) => [dir, inspectAgentDefinitionDirectory(dir)]));
|
|
2770
|
+
if (scope !== "user") for (const dir of projectCandidateDirs) directories.push(reportAgentDefinitionDirectory("project", dir, projectInspections.get(dir)!));
|
|
2771
|
+
const projectLoaded = scope === "user" ? [] : projectAgentDirs.map((dir) => loadAgentsFromDir(dir, "project", dir === projectAgentsDir ? 1 : 0, undefined, projectInspections.get(dir)!));
|
|
2772
|
+
const projectAgents = applyCustomAgentOverrides(applySubagentDefaults(projectLoaded.flatMap((loaded) => loaded.agents), defaultModel, defaultProvider, defaultThinking, defaultExtensions), userSettings, projectSettings, userSettingsPath, projectSettingsPath);
|
|
2773
|
+
const packageLoaded = packageSubagentPaths.agents.map((entry, index) => {
|
|
2774
|
+
const inspection = inspectAgentDefinitionDirectory(entry.dir);
|
|
2775
|
+
directories.push(reportAgentDefinitionDirectory("package", entry.dir, inspection));
|
|
2776
|
+
return loadAgentsFromDir(entry.dir, "package", packageSubagentPaths.agents.length - index, entry, inspection);
|
|
2777
|
+
});
|
|
2778
|
+
const packageMap = new Map<string, AgentConfig>();
|
|
2779
|
+
for (const loaded of packageLoaded) for (const agent of loaded.agents) if (!packageMap.has(agent.name)) packageMap.set(agent.name, agent);
|
|
2780
|
+
const packageAgents = applyCustomAgentOverrides(applySubagentDefaults(Array.from(packageMap.values()), defaultModel, defaultProvider, defaultThinking, defaultExtensions), userSettings, projectSettings, userSettingsPath, projectSettingsPath);
|
|
2781
|
+
const agents = applySubagentMaxThinking(mergeAgentsForScope(scope, userAgents, projectAgents, builtinAgents, packageAgents).filter((agent) => agent.disabled !== true), maxThinking);
|
|
2782
|
+
const agentDiagnostics = [...builtinLoaded.diagnostics, ...userLoaded.flatMap((loaded) => loaded.diagnostics), ...projectLoaded.flatMap((loaded) => loaded.diagnostics), ...packageLoaded.flatMap((loaded) => loaded.diagnostics)];
|
|
2783
|
+
return { agents, agentDiagnostics, projectAgentsDir, cwd: effectiveCwd, scope, directories, ...(modelScope !== undefined ? { modelScope } : {}), ...(maxThinking !== undefined ? { maxThinking } : {}) };
|
|
2784
|
+
}
|
|
2785
|
+
|
|
2786
|
+
export function discoverAgents(cwd: string, scope: AgentScope, preferredModelProvider?: string): AgentDiscoveryResult {
|
|
2787
|
+
if (scope !== "both") return discoverAgentsUncached(cwd, scope, preferredModelProvider);
|
|
2788
|
+
const sources = getAgentDiscoverySources(cwd, preferredModelProvider);
|
|
2789
|
+
return buildEffectiveDiscovery(sources, scope);
|
|
2790
|
+
}
|
|
2791
|
+
|
|
2792
|
+
export function discoverAgentsAll(cwd: string, preferredModelProvider?: string): AgentDiscoveryAllResult {
|
|
2793
|
+
return discoverAgentSnapshot(cwd, "both", preferredModelProvider).all;
|
|
2794
|
+
}
|