@agimon-ai/doompi-team 0.0.1-alpha.7 → 0.0.1-alpha.9
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/dist/agents/discovery.cjs +1 -1
- package/dist/agents/discovery.cjs.map +1 -1
- package/dist/agents/discovery.mjs +1 -1
- package/dist/agents/discovery.mjs.map +1 -1
- package/dist/agents/settings.cjs +1 -1
- package/dist/agents/settings.cjs.map +1 -1
- package/dist/agents/settings.mjs +1 -1
- package/dist/agents/settings.mjs.map +1 -1
- package/dist/extensions/spawnPlan.cjs +2 -2
- package/dist/extensions/spawnPlan.cjs.map +1 -1
- package/dist/extensions/spawnPlan.mjs +2 -2
- package/dist/extensions/spawnPlan.mjs.map +1 -1
- package/package.json +7 -7
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require(`../_virtual/_rolldown/runtime.cjs`),t=require(`../shared/lruCache.cjs`),n=require(`./projectRoot.cjs`),r=require(`./settings.cjs`),i=require(`./loader.cjs`),a=require(`../_virtual/_@oxc-project_runtime@0.127.0/helpers/decorate.cjs`);let o=require(`node:fs`);o=e.__toESM(o,1);let s=require(`node:path`);s=e.__toESM(s,1);let c=require(`@agimon-ai/doompi-config`),l=require(`inversify`);const u=s.join(`.doom`,`modes.yaml`),d=/:(?:off|minimal|low|medium|high|xhigh|max)$/;function f(e,t,n,r=[]){let i=new Map;for(let e of r)i.set(e.name,e);if(e!==`project`)for(let e of t)i.set(e.name,e);if(e!==`user`)for(let e of n)i.set(e.name,e);return[...i.values()]}function p(e){try{let t=o.statSync(e);return`${e}:${t.mtimeMs}:${t.size}`}catch{return`${e}:-`}}function m(){let e=(0,c.getHarnessState)();if(!e.root||e.layers.length===0)return;let t=(0,c.resolveLayerCustomization)((0,c.loadMajorModesConfig)(e.root),e.layers).subagents;return t?{config:t,path:s.join(e.root,u)}:void 0}function h(e){if(e?.models)return e.models.map(({model:e,thinking:t})=>t?`${e.replace(d,``)}:${t}`:e)}let
|
|
1
|
+
const e=require(`../_virtual/_rolldown/runtime.cjs`),t=require(`../shared/lruCache.cjs`),n=require(`./projectRoot.cjs`),r=require(`./settings.cjs`),i=require(`./loader.cjs`),a=require(`../_virtual/_@oxc-project_runtime@0.127.0/helpers/decorate.cjs`);let o=require(`node:fs`);o=e.__toESM(o,1);let s=require(`node:path`);s=e.__toESM(s,1);let c=require(`@agimon-ai/doompi-config`),l=require(`inversify`);const u=s.join(`.doom`,`modes.yaml`),d=/:(?:off|minimal|low|medium|high|xhigh|max)$/;function f(e,t,n,r=[]){let i=new Map;for(let e of r)i.set(e.name,e);if(e!==`project`)for(let e of t)i.set(e.name,e);if(e!==`user`)for(let e of n)i.set(e.name,e);return[...i.values()]}function p(e){try{let t=o.statSync(e);return`${e}:${t.mtimeMs}:${t.size}`}catch{return`${e}:-`}}function m(){let e=(0,c.getHarnessState)();if(!e.root||e.layers.length===0)return;let t=(0,c.resolveLayerCustomization)((0,c.loadMajorModesConfig)(e.root),e.layers).subagents;return t?{config:t,path:s.join(e.root,u)}:void 0}function h(e){if(e?.models)return e.models.map(({model:e,thinking:t})=>t?`${e.replace(d,``)}:${t}`:e)}function g(){return h(m()?.config)}let _=class{cache=new t.LruCache(64);ttlMs=5e3;now(){return Date.now()}discover(e,t){let n=`${t}|${e}`,r=this.cache.get(n),i=this.now();if(r&&r.expiresAt>i&&r.fingerprint===this.fingerprint(e))return v(r.result);let a=this.load(e,t);return this.cache.set(n,{result:a,expiresAt:i+this.ttlMs,fingerprint:this.fingerprint(e)}),v(a)}find(e,t,n){return this.discover(e,t).agents.find(e=>e.name===n)}invalidate(){this.cache.clear()}fingerprint(e){let t=(0,c.getHarnessState)(),r=[n.getUserAgentSettingsPath(),n.getProjectAgentSettingsPath(e)??`-`,...n.userAgentDirs(),...n.resolveNearestProjectAgentDirs(e).readDirs,...i.pluginAgentDirs()];return t.root&&r.push(s.join(t.root,u)),[...r.map(p),`active-layers:${t.layers.join(`,`)}`].join(`|`)}load(e,t){let a=n.getUserAgentSettingsPath(),o=n.getProjectAgentSettingsPath(e),s=t===`project`?r.EMPTY_SUBAGENT_SETTINGS:r.readSubagentSettings(a),c=t===`user`?r.EMPTY_SUBAGENT_SETTINGS:r.readSubagentSettings(o),l=r.resolveSubagentDefaultModel(s,c,a,o),u=m(),d=h(u?.config)?.[0],p=d&&u?{type:`layers.subagents.defaultModel`,scope:`layer`,path:u.path,model:d}:l,g=r.resolveSubagentDefaultThinking(s,c,o),_=r.resolveSubagentDefaultExtensions(s,c,o),v=e=>r.applyCustomAgentOverrides(r.applySubagentDefaults(e,p,g,_),s,c,a,o),y=v(i.pluginAgentDirs().flatMap(e=>i.loadAgentsFromDir(e,`plugin`)));return{agents:f(t,t===`project`?[]:v(n.userAgentDirs().flatMap(e=>i.loadAgentsFromDir(e,`user`))),t===`user`?[]:v(n.resolveNearestProjectAgentDirs(e).readDirs.flatMap(e=>i.loadAgentsFromDir(e,`project`))),y).filter(e=>e.disabled!==!0),projectAgentsDir:n.resolveNearestProjectAgentDirs(e).preferredDir,modelScope:c.modelScope??s.modelScope}}};_=a.__decorate([(0,l.injectable)()],_);function v(e){return{...e,agents:[...e.agents]}}Object.defineProperty(exports,`AgentDiscoveryService`,{enumerable:!0,get:function(){return _}}),exports.resolveActiveLayerModelSpecs=g,exports.resolveActiveLayerSubagentConfig=m;
|
|
2
2
|
//# sourceMappingURL=discovery.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"discovery.cjs","names":["path","fs","LruCache","getUserAgentSettingsPath","getProjectAgentSettingsPath","userAgentDirs","resolveNearestProjectAgentDirs","pluginAgentDirs","EMPTY_SUBAGENT_SETTINGS","readSubagentSettings","resolveSubagentDefaultModel","resolveSubagentDefaultThinking","resolveSubagentDefaultExtensions","applyCustomAgentOverrides","applySubagentDefaults","loadAgentsFromDir"],"sources":["../../src/agents/discovery.ts"],"sourcesContent":["/**\n * Cached discovery of the agents visible from a working directory.\n *\n * WHY THIS IS A SERVICE AND NOT A FUNCTION:\n * Discovery reads two settings files and walks every agent directory in scope,\n * parsing the frontmatter of every file it finds. The slash-command completion\n * path asks for the agent list on each keystroke, and preflight asks again for\n * each launch, so the predecessor's plain function re-walked the tree many times\n * per second. Caching needs somewhere to live, and invalidation needs an owner.\n *\n * DESIGN PATTERNS:\n * - Two-layer validity. A short TTL bounds how stale an answer can be, and a\n * cheap source fingerprint catches the common edits sooner than the TTL would\n * - Bounded by an LRU keyed on cwd and scope, so a long session that visits many\n * directories cannot grow the cache without limit\n * - Mutations invalidate explicitly rather than relying on the TTL, so an agent\n * the user just created is visible immediately\n *\n * WHY THE FINGERPRINT IS NOT ENOUGH ON ITS OWN:\n * It stats the settings files and the agent directories, not every agent file.\n * A directory's mtime moves when a file is added or removed but not when an\n * existing file's contents change, so an in-place edit is caught by the TTL\n * rather than by the fingerprint. Stat'ing every agent file on every keystroke\n * would reintroduce the cost this cache exists to remove.\n *\n * AVOID:\n * - Handing out the cached array without copying it; a caller that sorts or\n * splices in place would corrupt every later reader\n * - Raising the TTL to hide a missing invalidate call\n */\n\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport {\n getHarnessState,\n loadMajorModesConfig,\n resolveLayerCustomization,\n type SubagentLayerConfig,\n} from '@agimon-ai/doompi-config';\nimport { injectable } from 'inversify';\n\nimport { LruCache } from '../shared/lruCache.ts';\nimport { loadAgentsFromDir, pluginAgentDirs } from './loader.ts';\nimport {\n getProjectAgentSettingsPath,\n getUserAgentSettingsPath,\n resolveNearestProjectAgentDirs,\n userAgentDirs,\n} from './projectRoot.ts';\nimport {\n applyCustomAgentOverrides,\n applySubagentDefaults,\n EMPTY_SUBAGENT_SETTINGS,\n readSubagentSettings,\n resolveSubagentDefaultExtensions,\n resolveSubagentDefaultModel,\n resolveSubagentDefaultThinking,\n} from './settings.ts';\nimport type {\n AgentConfig,\n AgentDiscoveryResult,\n AgentScope,\n IAgentDiscoveryService,\n SubagentSettings,\n} from './types.ts';\n\n/** How long a discovery result may be reused without revalidation. */\nconst DISCOVERY_TTL_MS = 5_000;\n\n/**\n * How many (cwd, scope) combinations stay cached.\n *\n * A session works in a handful of directories and there are three scopes, so\n * this is far above real usage; it exists to bound a pathological case.\n */\nconst MAX_CACHED_DISCOVERIES = 64;\n\nconst FINGERPRINT_FIELD_SEPARATOR = ':';\nconst FINGERPRINT_ENTRY_SEPARATOR = '|';\nconst MISSING_SOURCE_MARKER = '-';\nconst MAJOR_MODES_CONFIG_RELATIVE_PATH = path.join('.doom', 'modes.yaml');\nconst THINKING_SUFFIX = /:(?:off|minimal|low|medium|high|xhigh|max)$/;\n\ninterface CacheEntry {\n result: AgentDiscoveryResult;\n expiresAt: number;\n fingerprint: string;\n}\n\n/**\n * Merge the sources into one list, later sources shadowing earlier ones.\n *\n * Precedence is plugin, then user, then project: the definition closest to the\n * work being done wins, which is what lets a repository pin an agent that the\n * user has also defined globally.\n */\nexport function mergeAgentsForScope(\n scope: AgentScope,\n userAgents: AgentConfig[],\n projectAgents: AgentConfig[],\n pluginAgents: AgentConfig[] = [],\n): AgentConfig[] {\n const byName = new Map<string, AgentConfig>();\n for (const agent of pluginAgents) byName.set(agent.name, agent);\n if (scope !== 'project') {\n for (const agent of userAgents) byName.set(agent.name, agent);\n }\n if (scope !== 'user') {\n for (const agent of projectAgents) byName.set(agent.name, agent);\n }\n return [...byName.values()];\n}\n\n/** Size and mtime of one path, or a marker when it does not exist. */\nfunction stampOf(target: string): string {\n try {\n const stat = fs.statSync(target);\n return `${target}${FINGERPRINT_FIELD_SEPARATOR}${stat.mtimeMs}${FINGERPRINT_FIELD_SEPARATOR}${stat.size}`;\n } catch {\n // A missing settings file or agent directory is a normal state, and it has\n // to be distinguishable from a present one so that creating it invalidates.\n return `${target}${FINGERPRINT_FIELD_SEPARATOR}${MISSING_SOURCE_MARKER}`;\n }\n}\n\nexport interface ActiveLayerSubagentConfig {\n config: {\n models?: SubagentLayerConfig['models'];\n excludeTools?: string[];\n };\n path: string;\n}\n\n/** Resolve active-layer subagent policy from the current parent harness state. */\nexport function resolveActiveLayerSubagentConfig(): ActiveLayerSubagentConfig | undefined {\n const harness = getHarnessState();\n if (!harness.root || harness.layers.length === 0) return undefined;\n const layers = loadMajorModesConfig(harness.root);\n const config = resolveLayerCustomization(layers, harness.layers).subagents;\n return config ? { config, path: path.join(harness.root, MAJOR_MODES_CONFIG_RELATIVE_PATH) } : undefined;\n}\n\nfunction layerModelSpecs(config: ActiveLayerSubagentConfig['config'] | undefined): string[] | undefined {\n if (!config?.models) return undefined;\n return config.models.map(({ model, thinking }) =>\n thinking ? `${model.replace(THINKING_SUFFIX, '')}:${thinking}` : model,\n );\n}\n\n@injectable()\nexport class AgentDiscoveryService implements IAgentDiscoveryService {\n private readonly cache = new LruCache<string, CacheEntry>(MAX_CACHED_DISCOVERIES);\n\n /**\n * Seams for tests, as protected members rather than constructor arguments.\n *\n * The container resolves this service by type, and inversify cannot supply a\n * primitive like a TTL, so configuration cannot arrive through the\n * constructor. The repo convention is also not to abstract the clock, so a\n * test subclass overrides these instead.\n */\n protected readonly ttlMs: number = DISCOVERY_TTL_MS;\n\n protected now(): number {\n return Date.now();\n }\n\n discover(cwd: string, scope: AgentScope): AgentDiscoveryResult {\n const key = `${scope}${FINGERPRINT_ENTRY_SEPARATOR}${cwd}`;\n const cached = this.cache.get(key);\n const timestamp = this.now();\n\n if (cached && cached.expiresAt > timestamp && cached.fingerprint === this.fingerprint(cwd)) {\n return cloneResult(cached.result);\n }\n\n const result = this.load(cwd, scope);\n this.cache.set(key, {\n result,\n expiresAt: timestamp + this.ttlMs,\n fingerprint: this.fingerprint(cwd),\n });\n return cloneResult(result);\n }\n\n find(cwd: string, scope: AgentScope, name: string): AgentConfig | undefined {\n return this.discover(cwd, scope).agents.find((agent) => agent.name === name);\n }\n\n invalidate(): void {\n this.cache.clear();\n }\n\n /**\n * A cheap stamp of everything discovery reads at the directory level.\n *\n * Deliberately independent of scope: a user-scoped and a project-scoped lookup\n * from the same directory read overlapping sources, and computing one stamp\n * keeps the stat count fixed regardless of how many scopes are queried.\n */\n private fingerprint(cwd: string): string {\n const harness = getHarnessState();\n const sources = [\n getUserAgentSettingsPath(),\n getProjectAgentSettingsPath(cwd) ?? MISSING_SOURCE_MARKER,\n ...userAgentDirs(),\n ...resolveNearestProjectAgentDirs(cwd).readDirs,\n ...pluginAgentDirs(),\n ];\n if (harness.root) sources.push(path.join(harness.root, MAJOR_MODES_CONFIG_RELATIVE_PATH));\n return [...sources.map(stampOf), `active-layers${FINGERPRINT_FIELD_SEPARATOR}${harness.layers.join(',')}`].join(\n FINGERPRINT_ENTRY_SEPARATOR,\n );\n }\n\n /**\n * The uncached read. Protected so a test can count how often it actually runs,\n * which is the only direct way to assert that the cache is doing its job.\n */\n protected load(cwd: string, scope: AgentScope): AgentDiscoveryResult {\n const userSettingsPath = getUserAgentSettingsPath();\n const projectSettingsPath = getProjectAgentSettingsPath(cwd);\n const userSettings: SubagentSettings =\n scope === 'project' ? EMPTY_SUBAGENT_SETTINGS : readSubagentSettings(userSettingsPath);\n const projectSettings: SubagentSettings =\n scope === 'user' ? EMPTY_SUBAGENT_SETTINGS : readSubagentSettings(projectSettingsPath);\n\n const legacyDefaultModel = resolveSubagentDefaultModel(\n userSettings,\n projectSettings,\n userSettingsPath,\n projectSettingsPath,\n );\n const layerSubagents = resolveActiveLayerSubagentConfig();\n const layerModels = layerModelSpecs(layerSubagents?.config);\n const layerPrimaryModel = layerModels?.[0];\n const defaultModel =\n layerPrimaryModel && layerSubagents\n ? {\n type: 'layers.subagents.defaultModel' as const,\n scope: 'layer' as const,\n path: layerSubagents.path,\n model: layerPrimaryModel,\n }\n : legacyDefaultModel;\n const defaultThinking = resolveSubagentDefaultThinking(userSettings, projectSettings, projectSettingsPath);\n const defaultExtensions = resolveSubagentDefaultExtensions(userSettings, projectSettings, projectSettingsPath);\n\n const resolve = (agents: AgentConfig[]): AgentConfig[] =>\n applyCustomAgentOverrides(\n applySubagentDefaults(agents, defaultModel, defaultThinking, defaultExtensions, layerModels?.slice(1)),\n userSettings,\n projectSettings,\n userSettingsPath,\n projectSettingsPath,\n );\n\n const plugin = resolve(pluginAgentDirs().flatMap((directory) => loadAgentsFromDir(directory, 'plugin')));\n const user =\n scope === 'project' ? [] : resolve(userAgentDirs().flatMap((directory) => loadAgentsFromDir(directory, 'user')));\n const project =\n scope === 'user'\n ? []\n : resolve(\n resolveNearestProjectAgentDirs(cwd).readDirs.flatMap((directory) =>\n loadAgentsFromDir(directory, 'project'),\n ),\n );\n\n return {\n // A disabled agent is dropped last, so that an override which re-enables\n // one has already been applied by the time this runs.\n agents: mergeAgentsForScope(scope, user, project, plugin).filter((agent) => agent.disabled !== true),\n projectAgentsDir: resolveNearestProjectAgentDirs(cwd).preferredDir,\n modelScope: projectSettings.modelScope ?? userSettings.modelScope,\n };\n }\n}\n\n/**\n * Copy the parts a caller could mutate in place.\n *\n * The agent objects themselves are treated as immutable by every consumer, so\n * only the array is copied; deep-cloning every config on each keystroke would\n * cost more than the walk this cache replaced.\n */\nfunction cloneResult(result: AgentDiscoveryResult): AgentDiscoveryResult {\n return { ...result, agents: [...result.agents] };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"discovery.cjs","names":["path","fs","LruCache","getUserAgentSettingsPath","getProjectAgentSettingsPath","userAgentDirs","resolveNearestProjectAgentDirs","pluginAgentDirs","EMPTY_SUBAGENT_SETTINGS","readSubagentSettings","resolveSubagentDefaultModel","resolveSubagentDefaultThinking","resolveSubagentDefaultExtensions","applyCustomAgentOverrides","applySubagentDefaults","loadAgentsFromDir"],"sources":["../../src/agents/discovery.ts"],"sourcesContent":["/**\n * Cached discovery of the agents visible from a working directory.\n *\n * WHY THIS IS A SERVICE AND NOT A FUNCTION:\n * Discovery reads two settings files and walks every agent directory in scope,\n * parsing the frontmatter of every file it finds. The slash-command completion\n * path asks for the agent list on each keystroke, and preflight asks again for\n * each launch, so the predecessor's plain function re-walked the tree many times\n * per second. Caching needs somewhere to live, and invalidation needs an owner.\n *\n * DESIGN PATTERNS:\n * - Two-layer validity. A short TTL bounds how stale an answer can be, and a\n * cheap source fingerprint catches the common edits sooner than the TTL would\n * - Bounded by an LRU keyed on cwd and scope, so a long session that visits many\n * directories cannot grow the cache without limit\n * - Mutations invalidate explicitly rather than relying on the TTL, so an agent\n * the user just created is visible immediately\n *\n * WHY THE FINGERPRINT IS NOT ENOUGH ON ITS OWN:\n * It stats the settings files and the agent directories, not every agent file.\n * A directory's mtime moves when a file is added or removed but not when an\n * existing file's contents change, so an in-place edit is caught by the TTL\n * rather than by the fingerprint. Stat'ing every agent file on every keystroke\n * would reintroduce the cost this cache exists to remove.\n *\n * AVOID:\n * - Handing out the cached array without copying it; a caller that sorts or\n * splices in place would corrupt every later reader\n * - Raising the TTL to hide a missing invalidate call\n */\n\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport {\n getHarnessState,\n loadMajorModesConfig,\n resolveLayerCustomization,\n type SubagentLayerConfig,\n} from '@agimon-ai/doompi-config';\nimport { injectable } from 'inversify';\n\nimport { LruCache } from '../shared/lruCache.ts';\nimport { loadAgentsFromDir, pluginAgentDirs } from './loader.ts';\nimport {\n getProjectAgentSettingsPath,\n getUserAgentSettingsPath,\n resolveNearestProjectAgentDirs,\n userAgentDirs,\n} from './projectRoot.ts';\nimport {\n applyCustomAgentOverrides,\n applySubagentDefaults,\n EMPTY_SUBAGENT_SETTINGS,\n readSubagentSettings,\n resolveSubagentDefaultExtensions,\n resolveSubagentDefaultModel,\n resolveSubagentDefaultThinking,\n} from './settings.ts';\nimport type {\n AgentConfig,\n AgentDiscoveryResult,\n AgentScope,\n IAgentDiscoveryService,\n SubagentSettings,\n} from './types.ts';\n\n/** How long a discovery result may be reused without revalidation. */\nconst DISCOVERY_TTL_MS = 5_000;\n\n/**\n * How many (cwd, scope) combinations stay cached.\n *\n * A session works in a handful of directories and there are three scopes, so\n * this is far above real usage; it exists to bound a pathological case.\n */\nconst MAX_CACHED_DISCOVERIES = 64;\n\nconst FINGERPRINT_FIELD_SEPARATOR = ':';\nconst FINGERPRINT_ENTRY_SEPARATOR = '|';\nconst MISSING_SOURCE_MARKER = '-';\nconst MAJOR_MODES_CONFIG_RELATIVE_PATH = path.join('.doom', 'modes.yaml');\nconst THINKING_SUFFIX = /:(?:off|minimal|low|medium|high|xhigh|max)$/;\n\ninterface CacheEntry {\n result: AgentDiscoveryResult;\n expiresAt: number;\n fingerprint: string;\n}\n\n/**\n * Merge the sources into one list, later sources shadowing earlier ones.\n *\n * Precedence is plugin, then user, then project: the definition closest to the\n * work being done wins, which is what lets a repository pin an agent that the\n * user has also defined globally.\n */\nexport function mergeAgentsForScope(\n scope: AgentScope,\n userAgents: AgentConfig[],\n projectAgents: AgentConfig[],\n pluginAgents: AgentConfig[] = [],\n): AgentConfig[] {\n const byName = new Map<string, AgentConfig>();\n for (const agent of pluginAgents) byName.set(agent.name, agent);\n if (scope !== 'project') {\n for (const agent of userAgents) byName.set(agent.name, agent);\n }\n if (scope !== 'user') {\n for (const agent of projectAgents) byName.set(agent.name, agent);\n }\n return [...byName.values()];\n}\n\n/** Size and mtime of one path, or a marker when it does not exist. */\nfunction stampOf(target: string): string {\n try {\n const stat = fs.statSync(target);\n return `${target}${FINGERPRINT_FIELD_SEPARATOR}${stat.mtimeMs}${FINGERPRINT_FIELD_SEPARATOR}${stat.size}`;\n } catch {\n // A missing settings file or agent directory is a normal state, and it has\n // to be distinguishable from a present one so that creating it invalidates.\n return `${target}${FINGERPRINT_FIELD_SEPARATOR}${MISSING_SOURCE_MARKER}`;\n }\n}\n\nexport interface ActiveLayerSubagentConfig {\n config: {\n models?: SubagentLayerConfig['models'];\n excludeTools?: string[];\n };\n path: string;\n}\n\n/** Resolve active-layer subagent policy from the current parent harness state. */\nexport function resolveActiveLayerSubagentConfig(): ActiveLayerSubagentConfig | undefined {\n const harness = getHarnessState();\n if (!harness.root || harness.layers.length === 0) return undefined;\n const layers = loadMajorModesConfig(harness.root);\n const config = resolveLayerCustomization(layers, harness.layers).subagents;\n return config ? { config, path: path.join(harness.root, MAJOR_MODES_CONFIG_RELATIVE_PATH) } : undefined;\n}\n\nfunction layerModelSpecs(config: ActiveLayerSubagentConfig['config'] | undefined): string[] | undefined {\n if (!config?.models) return undefined;\n return config.models.map(({ model, thinking }) =>\n thinking ? `${model.replace(THINKING_SUFFIX, '')}:${thinking}` : model,\n );\n}\n\n/** Resolve the active layer's ordered model policy without merging it into an agent definition. */\nexport function resolveActiveLayerModelSpecs(): string[] | undefined {\n return layerModelSpecs(resolveActiveLayerSubagentConfig()?.config);\n}\n\n@injectable()\nexport class AgentDiscoveryService implements IAgentDiscoveryService {\n private readonly cache = new LruCache<string, CacheEntry>(MAX_CACHED_DISCOVERIES);\n\n /**\n * Seams for tests, as protected members rather than constructor arguments.\n *\n * The container resolves this service by type, and inversify cannot supply a\n * primitive like a TTL, so configuration cannot arrive through the\n * constructor. The repo convention is also not to abstract the clock, so a\n * test subclass overrides these instead.\n */\n protected readonly ttlMs: number = DISCOVERY_TTL_MS;\n\n protected now(): number {\n return Date.now();\n }\n\n discover(cwd: string, scope: AgentScope): AgentDiscoveryResult {\n const key = `${scope}${FINGERPRINT_ENTRY_SEPARATOR}${cwd}`;\n const cached = this.cache.get(key);\n const timestamp = this.now();\n\n if (cached && cached.expiresAt > timestamp && cached.fingerprint === this.fingerprint(cwd)) {\n return cloneResult(cached.result);\n }\n\n const result = this.load(cwd, scope);\n this.cache.set(key, {\n result,\n expiresAt: timestamp + this.ttlMs,\n fingerprint: this.fingerprint(cwd),\n });\n return cloneResult(result);\n }\n\n find(cwd: string, scope: AgentScope, name: string): AgentConfig | undefined {\n return this.discover(cwd, scope).agents.find((agent) => agent.name === name);\n }\n\n invalidate(): void {\n this.cache.clear();\n }\n\n /**\n * A cheap stamp of everything discovery reads at the directory level.\n *\n * Deliberately independent of scope: a user-scoped and a project-scoped lookup\n * from the same directory read overlapping sources, and computing one stamp\n * keeps the stat count fixed regardless of how many scopes are queried.\n */\n private fingerprint(cwd: string): string {\n const harness = getHarnessState();\n const sources = [\n getUserAgentSettingsPath(),\n getProjectAgentSettingsPath(cwd) ?? MISSING_SOURCE_MARKER,\n ...userAgentDirs(),\n ...resolveNearestProjectAgentDirs(cwd).readDirs,\n ...pluginAgentDirs(),\n ];\n if (harness.root) sources.push(path.join(harness.root, MAJOR_MODES_CONFIG_RELATIVE_PATH));\n return [...sources.map(stampOf), `active-layers${FINGERPRINT_FIELD_SEPARATOR}${harness.layers.join(',')}`].join(\n FINGERPRINT_ENTRY_SEPARATOR,\n );\n }\n\n /**\n * The uncached read. Protected so a test can count how often it actually runs,\n * which is the only direct way to assert that the cache is doing its job.\n */\n protected load(cwd: string, scope: AgentScope): AgentDiscoveryResult {\n const userSettingsPath = getUserAgentSettingsPath();\n const projectSettingsPath = getProjectAgentSettingsPath(cwd);\n const userSettings: SubagentSettings =\n scope === 'project' ? EMPTY_SUBAGENT_SETTINGS : readSubagentSettings(userSettingsPath);\n const projectSettings: SubagentSettings =\n scope === 'user' ? EMPTY_SUBAGENT_SETTINGS : readSubagentSettings(projectSettingsPath);\n\n const legacyDefaultModel = resolveSubagentDefaultModel(\n userSettings,\n projectSettings,\n userSettingsPath,\n projectSettingsPath,\n );\n const layerSubagents = resolveActiveLayerSubagentConfig();\n const layerModels = layerModelSpecs(layerSubagents?.config);\n const layerPrimaryModel = layerModels?.[0];\n const defaultModel =\n layerPrimaryModel && layerSubagents\n ? {\n type: 'layers.subagents.defaultModel' as const,\n scope: 'layer' as const,\n path: layerSubagents.path,\n model: layerPrimaryModel,\n }\n : legacyDefaultModel;\n const defaultThinking = resolveSubagentDefaultThinking(userSettings, projectSettings, projectSettingsPath);\n const defaultExtensions = resolveSubagentDefaultExtensions(userSettings, projectSettings, projectSettingsPath);\n\n const resolve = (agents: AgentConfig[]): AgentConfig[] =>\n applyCustomAgentOverrides(\n applySubagentDefaults(agents, defaultModel, defaultThinking, defaultExtensions),\n userSettings,\n projectSettings,\n userSettingsPath,\n projectSettingsPath,\n );\n\n const plugin = resolve(pluginAgentDirs().flatMap((directory) => loadAgentsFromDir(directory, 'plugin')));\n const user =\n scope === 'project' ? [] : resolve(userAgentDirs().flatMap((directory) => loadAgentsFromDir(directory, 'user')));\n const project =\n scope === 'user'\n ? []\n : resolve(\n resolveNearestProjectAgentDirs(cwd).readDirs.flatMap((directory) =>\n loadAgentsFromDir(directory, 'project'),\n ),\n );\n\n return {\n // A disabled agent is dropped last, so that an override which re-enables\n // one has already been applied by the time this runs.\n agents: mergeAgentsForScope(scope, user, project, plugin).filter((agent) => agent.disabled !== true),\n projectAgentsDir: resolveNearestProjectAgentDirs(cwd).preferredDir,\n modelScope: projectSettings.modelScope ?? userSettings.modelScope,\n };\n }\n}\n\n/**\n * Copy the parts a caller could mutate in place.\n *\n * The agent objects themselves are treated as immutable by every consumer, so\n * only the array is copied; deep-cloning every config on each keystroke would\n * cost more than the walk this cache replaced.\n */\nfunction cloneResult(result: AgentDiscoveryResult): AgentDiscoveryResult {\n return { ...result, agents: [...result.agents] };\n}\n"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{__esmMin as e}from"../_virtual/_rolldown/runtime.mjs";import{LruCache as t,init_lruCache as n}from"../shared/lruCache.mjs";import{getProjectAgentSettingsPath as r,getUserAgentSettingsPath as i,init_projectRoot as a,resolveNearestProjectAgentDirs as o,userAgentDirs as s}from"./projectRoot.mjs";import{EMPTY_SUBAGENT_SETTINGS as c,applyCustomAgentOverrides as l,applySubagentDefaults as u,init_settings as d,readSubagentSettings as f,resolveSubagentDefaultExtensions as p,resolveSubagentDefaultModel as m,resolveSubagentDefaultThinking as h}from"./settings.mjs";import{init_loader as g,loadAgentsFromDir as _,pluginAgentDirs as v}from"./loader.mjs";import{__decorate as y,init_decorate as b}from"../_virtual/_@oxc-project_runtime@0.127.0/helpers/decorate.mjs";import*as x from"node:fs";import*as S from"node:path";import{getHarnessState as C,loadMajorModesConfig as w,resolveLayerCustomization as T}from"@agimon-ai/doompi-config";import{injectable as E}from"inversify";function D(e,t,n,r=[]){let i=new Map;for(let e of r)i.set(e.name,e);if(e!==`project`)for(let e of t)i.set(e.name,e);if(e!==`user`)for(let e of n)i.set(e.name,e);return[...i.values()]}function O(e){try{let t=x.statSync(e);return`${e}${
|
|
1
|
+
import{__esmMin as e}from"../_virtual/_rolldown/runtime.mjs";import{LruCache as t,init_lruCache as n}from"../shared/lruCache.mjs";import{getProjectAgentSettingsPath as r,getUserAgentSettingsPath as i,init_projectRoot as a,resolveNearestProjectAgentDirs as o,userAgentDirs as s}from"./projectRoot.mjs";import{EMPTY_SUBAGENT_SETTINGS as c,applyCustomAgentOverrides as l,applySubagentDefaults as u,init_settings as d,readSubagentSettings as f,resolveSubagentDefaultExtensions as p,resolveSubagentDefaultModel as m,resolveSubagentDefaultThinking as h}from"./settings.mjs";import{init_loader as g,loadAgentsFromDir as _,pluginAgentDirs as v}from"./loader.mjs";import{__decorate as y,init_decorate as b}from"../_virtual/_@oxc-project_runtime@0.127.0/helpers/decorate.mjs";import*as x from"node:fs";import*as S from"node:path";import{getHarnessState as C,loadMajorModesConfig as w,resolveLayerCustomization as T}from"@agimon-ai/doompi-config";import{injectable as E}from"inversify";function D(e,t,n,r=[]){let i=new Map;for(let e of r)i.set(e.name,e);if(e!==`project`)for(let e of t)i.set(e.name,e);if(e!==`user`)for(let e of n)i.set(e.name,e);return[...i.values()]}function O(e){try{let t=x.statSync(e);return`${e}${F}${t.mtimeMs}${F}${t.size}`}catch{return`${e}${F}${L}`}}function k(){let e=C();if(!e.root||e.layers.length===0)return;let t=T(w(e.root),e.layers).subagents;return t?{config:t,path:S.join(e.root,R)}:void 0}function A(e){if(e?.models)return e.models.map(({model:e,thinking:t})=>t?`${e.replace(z,``)}:${t}`:e)}function j(){return A(k()?.config)}function M(e){return{...e,agents:[...e.agents]}}var N,P,F,I,L,R,z,B,V=e((()=>{n(),g(),a(),d(),b(),N=5e3,P=64,F=`:`,I=`|`,L=`-`,R=S.join(`.doom`,`modes.yaml`),z=/:(?:off|minimal|low|medium|high|xhigh|max)$/,B=class{cache=new t(P);ttlMs=N;now(){return Date.now()}discover(e,t){let n=`${t}${I}${e}`,r=this.cache.get(n),i=this.now();if(r&&r.expiresAt>i&&r.fingerprint===this.fingerprint(e))return M(r.result);let a=this.load(e,t);return this.cache.set(n,{result:a,expiresAt:i+this.ttlMs,fingerprint:this.fingerprint(e)}),M(a)}find(e,t,n){return this.discover(e,t).agents.find(e=>e.name===n)}invalidate(){this.cache.clear()}fingerprint(e){let t=C(),n=[i(),r(e)??L,...s(),...o(e).readDirs,...v()];return t.root&&n.push(S.join(t.root,R)),[...n.map(O),`active-layers${F}${t.layers.join(`,`)}`].join(I)}load(e,t){let n=i(),a=r(e),d=t===`project`?c:f(n),g=t===`user`?c:f(a),y=m(d,g,n,a),b=k(),x=A(b?.config)?.[0],S=x&&b?{type:`layers.subagents.defaultModel`,scope:`layer`,path:b.path,model:x}:y,C=h(d,g,a),w=p(d,g,a),T=e=>l(u(e,S,C,w),d,g,n,a),E=T(v().flatMap(e=>_(e,`plugin`)));return{agents:D(t,t===`project`?[]:T(s().flatMap(e=>_(e,`user`))),t===`user`?[]:T(o(e).readDirs.flatMap(e=>_(e,`project`))),E).filter(e=>e.disabled!==!0),projectAgentsDir:o(e).preferredDir,modelScope:g.modelScope??d.modelScope}}},B=y([E()],B)}));V();export{B as AgentDiscoveryService,V as init_discovery,j as resolveActiveLayerModelSpecs,k as resolveActiveLayerSubagentConfig};
|
|
2
2
|
//# sourceMappingURL=discovery.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"discovery.mjs","names":[],"sources":["../../src/agents/discovery.ts"],"sourcesContent":["/**\n * Cached discovery of the agents visible from a working directory.\n *\n * WHY THIS IS A SERVICE AND NOT A FUNCTION:\n * Discovery reads two settings files and walks every agent directory in scope,\n * parsing the frontmatter of every file it finds. The slash-command completion\n * path asks for the agent list on each keystroke, and preflight asks again for\n * each launch, so the predecessor's plain function re-walked the tree many times\n * per second. Caching needs somewhere to live, and invalidation needs an owner.\n *\n * DESIGN PATTERNS:\n * - Two-layer validity. A short TTL bounds how stale an answer can be, and a\n * cheap source fingerprint catches the common edits sooner than the TTL would\n * - Bounded by an LRU keyed on cwd and scope, so a long session that visits many\n * directories cannot grow the cache without limit\n * - Mutations invalidate explicitly rather than relying on the TTL, so an agent\n * the user just created is visible immediately\n *\n * WHY THE FINGERPRINT IS NOT ENOUGH ON ITS OWN:\n * It stats the settings files and the agent directories, not every agent file.\n * A directory's mtime moves when a file is added or removed but not when an\n * existing file's contents change, so an in-place edit is caught by the TTL\n * rather than by the fingerprint. Stat'ing every agent file on every keystroke\n * would reintroduce the cost this cache exists to remove.\n *\n * AVOID:\n * - Handing out the cached array without copying it; a caller that sorts or\n * splices in place would corrupt every later reader\n * - Raising the TTL to hide a missing invalidate call\n */\n\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport {\n getHarnessState,\n loadMajorModesConfig,\n resolveLayerCustomization,\n type SubagentLayerConfig,\n} from '@agimon-ai/doompi-config';\nimport { injectable } from 'inversify';\n\nimport { LruCache } from '../shared/lruCache.ts';\nimport { loadAgentsFromDir, pluginAgentDirs } from './loader.ts';\nimport {\n getProjectAgentSettingsPath,\n getUserAgentSettingsPath,\n resolveNearestProjectAgentDirs,\n userAgentDirs,\n} from './projectRoot.ts';\nimport {\n applyCustomAgentOverrides,\n applySubagentDefaults,\n EMPTY_SUBAGENT_SETTINGS,\n readSubagentSettings,\n resolveSubagentDefaultExtensions,\n resolveSubagentDefaultModel,\n resolveSubagentDefaultThinking,\n} from './settings.ts';\nimport type {\n AgentConfig,\n AgentDiscoveryResult,\n AgentScope,\n IAgentDiscoveryService,\n SubagentSettings,\n} from './types.ts';\n\n/** How long a discovery result may be reused without revalidation. */\nconst DISCOVERY_TTL_MS = 5_000;\n\n/**\n * How many (cwd, scope) combinations stay cached.\n *\n * A session works in a handful of directories and there are three scopes, so\n * this is far above real usage; it exists to bound a pathological case.\n */\nconst MAX_CACHED_DISCOVERIES = 64;\n\nconst FINGERPRINT_FIELD_SEPARATOR = ':';\nconst FINGERPRINT_ENTRY_SEPARATOR = '|';\nconst MISSING_SOURCE_MARKER = '-';\nconst MAJOR_MODES_CONFIG_RELATIVE_PATH = path.join('.doom', 'modes.yaml');\nconst THINKING_SUFFIX = /:(?:off|minimal|low|medium|high|xhigh|max)$/;\n\ninterface CacheEntry {\n result: AgentDiscoveryResult;\n expiresAt: number;\n fingerprint: string;\n}\n\n/**\n * Merge the sources into one list, later sources shadowing earlier ones.\n *\n * Precedence is plugin, then user, then project: the definition closest to the\n * work being done wins, which is what lets a repository pin an agent that the\n * user has also defined globally.\n */\nexport function mergeAgentsForScope(\n scope: AgentScope,\n userAgents: AgentConfig[],\n projectAgents: AgentConfig[],\n pluginAgents: AgentConfig[] = [],\n): AgentConfig[] {\n const byName = new Map<string, AgentConfig>();\n for (const agent of pluginAgents) byName.set(agent.name, agent);\n if (scope !== 'project') {\n for (const agent of userAgents) byName.set(agent.name, agent);\n }\n if (scope !== 'user') {\n for (const agent of projectAgents) byName.set(agent.name, agent);\n }\n return [...byName.values()];\n}\n\n/** Size and mtime of one path, or a marker when it does not exist. */\nfunction stampOf(target: string): string {\n try {\n const stat = fs.statSync(target);\n return `${target}${FINGERPRINT_FIELD_SEPARATOR}${stat.mtimeMs}${FINGERPRINT_FIELD_SEPARATOR}${stat.size}`;\n } catch {\n // A missing settings file or agent directory is a normal state, and it has\n // to be distinguishable from a present one so that creating it invalidates.\n return `${target}${FINGERPRINT_FIELD_SEPARATOR}${MISSING_SOURCE_MARKER}`;\n }\n}\n\nexport interface ActiveLayerSubagentConfig {\n config: {\n models?: SubagentLayerConfig['models'];\n excludeTools?: string[];\n };\n path: string;\n}\n\n/** Resolve active-layer subagent policy from the current parent harness state. */\nexport function resolveActiveLayerSubagentConfig(): ActiveLayerSubagentConfig | undefined {\n const harness = getHarnessState();\n if (!harness.root || harness.layers.length === 0) return undefined;\n const layers = loadMajorModesConfig(harness.root);\n const config = resolveLayerCustomization(layers, harness.layers).subagents;\n return config ? { config, path: path.join(harness.root, MAJOR_MODES_CONFIG_RELATIVE_PATH) } : undefined;\n}\n\nfunction layerModelSpecs(config: ActiveLayerSubagentConfig['config'] | undefined): string[] | undefined {\n if (!config?.models) return undefined;\n return config.models.map(({ model, thinking }) =>\n thinking ? `${model.replace(THINKING_SUFFIX, '')}:${thinking}` : model,\n );\n}\n\n@injectable()\nexport class AgentDiscoveryService implements IAgentDiscoveryService {\n private readonly cache = new LruCache<string, CacheEntry>(MAX_CACHED_DISCOVERIES);\n\n /**\n * Seams for tests, as protected members rather than constructor arguments.\n *\n * The container resolves this service by type, and inversify cannot supply a\n * primitive like a TTL, so configuration cannot arrive through the\n * constructor. The repo convention is also not to abstract the clock, so a\n * test subclass overrides these instead.\n */\n protected readonly ttlMs: number = DISCOVERY_TTL_MS;\n\n protected now(): number {\n return Date.now();\n }\n\n discover(cwd: string, scope: AgentScope): AgentDiscoveryResult {\n const key = `${scope}${FINGERPRINT_ENTRY_SEPARATOR}${cwd}`;\n const cached = this.cache.get(key);\n const timestamp = this.now();\n\n if (cached && cached.expiresAt > timestamp && cached.fingerprint === this.fingerprint(cwd)) {\n return cloneResult(cached.result);\n }\n\n const result = this.load(cwd, scope);\n this.cache.set(key, {\n result,\n expiresAt: timestamp + this.ttlMs,\n fingerprint: this.fingerprint(cwd),\n });\n return cloneResult(result);\n }\n\n find(cwd: string, scope: AgentScope, name: string): AgentConfig | undefined {\n return this.discover(cwd, scope).agents.find((agent) => agent.name === name);\n }\n\n invalidate(): void {\n this.cache.clear();\n }\n\n /**\n * A cheap stamp of everything discovery reads at the directory level.\n *\n * Deliberately independent of scope: a user-scoped and a project-scoped lookup\n * from the same directory read overlapping sources, and computing one stamp\n * keeps the stat count fixed regardless of how many scopes are queried.\n */\n private fingerprint(cwd: string): string {\n const harness = getHarnessState();\n const sources = [\n getUserAgentSettingsPath(),\n getProjectAgentSettingsPath(cwd) ?? MISSING_SOURCE_MARKER,\n ...userAgentDirs(),\n ...resolveNearestProjectAgentDirs(cwd).readDirs,\n ...pluginAgentDirs(),\n ];\n if (harness.root) sources.push(path.join(harness.root, MAJOR_MODES_CONFIG_RELATIVE_PATH));\n return [...sources.map(stampOf), `active-layers${FINGERPRINT_FIELD_SEPARATOR}${harness.layers.join(',')}`].join(\n FINGERPRINT_ENTRY_SEPARATOR,\n );\n }\n\n /**\n * The uncached read. Protected so a test can count how often it actually runs,\n * which is the only direct way to assert that the cache is doing its job.\n */\n protected load(cwd: string, scope: AgentScope): AgentDiscoveryResult {\n const userSettingsPath = getUserAgentSettingsPath();\n const projectSettingsPath = getProjectAgentSettingsPath(cwd);\n const userSettings: SubagentSettings =\n scope === 'project' ? EMPTY_SUBAGENT_SETTINGS : readSubagentSettings(userSettingsPath);\n const projectSettings: SubagentSettings =\n scope === 'user' ? EMPTY_SUBAGENT_SETTINGS : readSubagentSettings(projectSettingsPath);\n\n const legacyDefaultModel = resolveSubagentDefaultModel(\n userSettings,\n projectSettings,\n userSettingsPath,\n projectSettingsPath,\n );\n const layerSubagents = resolveActiveLayerSubagentConfig();\n const layerModels = layerModelSpecs(layerSubagents?.config);\n const layerPrimaryModel = layerModels?.[0];\n const defaultModel =\n layerPrimaryModel && layerSubagents\n ? {\n type: 'layers.subagents.defaultModel' as const,\n scope: 'layer' as const,\n path: layerSubagents.path,\n model: layerPrimaryModel,\n }\n : legacyDefaultModel;\n const defaultThinking = resolveSubagentDefaultThinking(userSettings, projectSettings, projectSettingsPath);\n const defaultExtensions = resolveSubagentDefaultExtensions(userSettings, projectSettings, projectSettingsPath);\n\n const resolve = (agents: AgentConfig[]): AgentConfig[] =>\n applyCustomAgentOverrides(\n applySubagentDefaults(agents, defaultModel, defaultThinking, defaultExtensions, layerModels?.slice(1)),\n userSettings,\n projectSettings,\n userSettingsPath,\n projectSettingsPath,\n );\n\n const plugin = resolve(pluginAgentDirs().flatMap((directory) => loadAgentsFromDir(directory, 'plugin')));\n const user =\n scope === 'project' ? [] : resolve(userAgentDirs().flatMap((directory) => loadAgentsFromDir(directory, 'user')));\n const project =\n scope === 'user'\n ? []\n : resolve(\n resolveNearestProjectAgentDirs(cwd).readDirs.flatMap((directory) =>\n loadAgentsFromDir(directory, 'project'),\n ),\n );\n\n return {\n // A disabled agent is dropped last, so that an override which re-enables\n // one has already been applied by the time this runs.\n agents: mergeAgentsForScope(scope, user, project, plugin).filter((agent) => agent.disabled !== true),\n projectAgentsDir: resolveNearestProjectAgentDirs(cwd).preferredDir,\n modelScope: projectSettings.modelScope ?? userSettings.modelScope,\n };\n }\n}\n\n/**\n * Copy the parts a caller could mutate in place.\n *\n * The agent objects themselves are treated as immutable by every consumer, so\n * only the array is copied; deep-cloning every config on each keystroke would\n * cost more than the walk this cache replaced.\n */\nfunction cloneResult(result: AgentDiscoveryResult): AgentDiscoveryResult {\n return { ...result, agents: [...result.agents] };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"discovery.mjs","names":[],"sources":["../../src/agents/discovery.ts"],"sourcesContent":["/**\n * Cached discovery of the agents visible from a working directory.\n *\n * WHY THIS IS A SERVICE AND NOT A FUNCTION:\n * Discovery reads two settings files and walks every agent directory in scope,\n * parsing the frontmatter of every file it finds. The slash-command completion\n * path asks for the agent list on each keystroke, and preflight asks again for\n * each launch, so the predecessor's plain function re-walked the tree many times\n * per second. Caching needs somewhere to live, and invalidation needs an owner.\n *\n * DESIGN PATTERNS:\n * - Two-layer validity. A short TTL bounds how stale an answer can be, and a\n * cheap source fingerprint catches the common edits sooner than the TTL would\n * - Bounded by an LRU keyed on cwd and scope, so a long session that visits many\n * directories cannot grow the cache without limit\n * - Mutations invalidate explicitly rather than relying on the TTL, so an agent\n * the user just created is visible immediately\n *\n * WHY THE FINGERPRINT IS NOT ENOUGH ON ITS OWN:\n * It stats the settings files and the agent directories, not every agent file.\n * A directory's mtime moves when a file is added or removed but not when an\n * existing file's contents change, so an in-place edit is caught by the TTL\n * rather than by the fingerprint. Stat'ing every agent file on every keystroke\n * would reintroduce the cost this cache exists to remove.\n *\n * AVOID:\n * - Handing out the cached array without copying it; a caller that sorts or\n * splices in place would corrupt every later reader\n * - Raising the TTL to hide a missing invalidate call\n */\n\nimport * as fs from 'node:fs';\nimport * as path from 'node:path';\nimport {\n getHarnessState,\n loadMajorModesConfig,\n resolveLayerCustomization,\n type SubagentLayerConfig,\n} from '@agimon-ai/doompi-config';\nimport { injectable } from 'inversify';\n\nimport { LruCache } from '../shared/lruCache.ts';\nimport { loadAgentsFromDir, pluginAgentDirs } from './loader.ts';\nimport {\n getProjectAgentSettingsPath,\n getUserAgentSettingsPath,\n resolveNearestProjectAgentDirs,\n userAgentDirs,\n} from './projectRoot.ts';\nimport {\n applyCustomAgentOverrides,\n applySubagentDefaults,\n EMPTY_SUBAGENT_SETTINGS,\n readSubagentSettings,\n resolveSubagentDefaultExtensions,\n resolveSubagentDefaultModel,\n resolveSubagentDefaultThinking,\n} from './settings.ts';\nimport type {\n AgentConfig,\n AgentDiscoveryResult,\n AgentScope,\n IAgentDiscoveryService,\n SubagentSettings,\n} from './types.ts';\n\n/** How long a discovery result may be reused without revalidation. */\nconst DISCOVERY_TTL_MS = 5_000;\n\n/**\n * How many (cwd, scope) combinations stay cached.\n *\n * A session works in a handful of directories and there are three scopes, so\n * this is far above real usage; it exists to bound a pathological case.\n */\nconst MAX_CACHED_DISCOVERIES = 64;\n\nconst FINGERPRINT_FIELD_SEPARATOR = ':';\nconst FINGERPRINT_ENTRY_SEPARATOR = '|';\nconst MISSING_SOURCE_MARKER = '-';\nconst MAJOR_MODES_CONFIG_RELATIVE_PATH = path.join('.doom', 'modes.yaml');\nconst THINKING_SUFFIX = /:(?:off|minimal|low|medium|high|xhigh|max)$/;\n\ninterface CacheEntry {\n result: AgentDiscoveryResult;\n expiresAt: number;\n fingerprint: string;\n}\n\n/**\n * Merge the sources into one list, later sources shadowing earlier ones.\n *\n * Precedence is plugin, then user, then project: the definition closest to the\n * work being done wins, which is what lets a repository pin an agent that the\n * user has also defined globally.\n */\nexport function mergeAgentsForScope(\n scope: AgentScope,\n userAgents: AgentConfig[],\n projectAgents: AgentConfig[],\n pluginAgents: AgentConfig[] = [],\n): AgentConfig[] {\n const byName = new Map<string, AgentConfig>();\n for (const agent of pluginAgents) byName.set(agent.name, agent);\n if (scope !== 'project') {\n for (const agent of userAgents) byName.set(agent.name, agent);\n }\n if (scope !== 'user') {\n for (const agent of projectAgents) byName.set(agent.name, agent);\n }\n return [...byName.values()];\n}\n\n/** Size and mtime of one path, or a marker when it does not exist. */\nfunction stampOf(target: string): string {\n try {\n const stat = fs.statSync(target);\n return `${target}${FINGERPRINT_FIELD_SEPARATOR}${stat.mtimeMs}${FINGERPRINT_FIELD_SEPARATOR}${stat.size}`;\n } catch {\n // A missing settings file or agent directory is a normal state, and it has\n // to be distinguishable from a present one so that creating it invalidates.\n return `${target}${FINGERPRINT_FIELD_SEPARATOR}${MISSING_SOURCE_MARKER}`;\n }\n}\n\nexport interface ActiveLayerSubagentConfig {\n config: {\n models?: SubagentLayerConfig['models'];\n excludeTools?: string[];\n };\n path: string;\n}\n\n/** Resolve active-layer subagent policy from the current parent harness state. */\nexport function resolveActiveLayerSubagentConfig(): ActiveLayerSubagentConfig | undefined {\n const harness = getHarnessState();\n if (!harness.root || harness.layers.length === 0) return undefined;\n const layers = loadMajorModesConfig(harness.root);\n const config = resolveLayerCustomization(layers, harness.layers).subagents;\n return config ? { config, path: path.join(harness.root, MAJOR_MODES_CONFIG_RELATIVE_PATH) } : undefined;\n}\n\nfunction layerModelSpecs(config: ActiveLayerSubagentConfig['config'] | undefined): string[] | undefined {\n if (!config?.models) return undefined;\n return config.models.map(({ model, thinking }) =>\n thinking ? `${model.replace(THINKING_SUFFIX, '')}:${thinking}` : model,\n );\n}\n\n/** Resolve the active layer's ordered model policy without merging it into an agent definition. */\nexport function resolveActiveLayerModelSpecs(): string[] | undefined {\n return layerModelSpecs(resolveActiveLayerSubagentConfig()?.config);\n}\n\n@injectable()\nexport class AgentDiscoveryService implements IAgentDiscoveryService {\n private readonly cache = new LruCache<string, CacheEntry>(MAX_CACHED_DISCOVERIES);\n\n /**\n * Seams for tests, as protected members rather than constructor arguments.\n *\n * The container resolves this service by type, and inversify cannot supply a\n * primitive like a TTL, so configuration cannot arrive through the\n * constructor. The repo convention is also not to abstract the clock, so a\n * test subclass overrides these instead.\n */\n protected readonly ttlMs: number = DISCOVERY_TTL_MS;\n\n protected now(): number {\n return Date.now();\n }\n\n discover(cwd: string, scope: AgentScope): AgentDiscoveryResult {\n const key = `${scope}${FINGERPRINT_ENTRY_SEPARATOR}${cwd}`;\n const cached = this.cache.get(key);\n const timestamp = this.now();\n\n if (cached && cached.expiresAt > timestamp && cached.fingerprint === this.fingerprint(cwd)) {\n return cloneResult(cached.result);\n }\n\n const result = this.load(cwd, scope);\n this.cache.set(key, {\n result,\n expiresAt: timestamp + this.ttlMs,\n fingerprint: this.fingerprint(cwd),\n });\n return cloneResult(result);\n }\n\n find(cwd: string, scope: AgentScope, name: string): AgentConfig | undefined {\n return this.discover(cwd, scope).agents.find((agent) => agent.name === name);\n }\n\n invalidate(): void {\n this.cache.clear();\n }\n\n /**\n * A cheap stamp of everything discovery reads at the directory level.\n *\n * Deliberately independent of scope: a user-scoped and a project-scoped lookup\n * from the same directory read overlapping sources, and computing one stamp\n * keeps the stat count fixed regardless of how many scopes are queried.\n */\n private fingerprint(cwd: string): string {\n const harness = getHarnessState();\n const sources = [\n getUserAgentSettingsPath(),\n getProjectAgentSettingsPath(cwd) ?? MISSING_SOURCE_MARKER,\n ...userAgentDirs(),\n ...resolveNearestProjectAgentDirs(cwd).readDirs,\n ...pluginAgentDirs(),\n ];\n if (harness.root) sources.push(path.join(harness.root, MAJOR_MODES_CONFIG_RELATIVE_PATH));\n return [...sources.map(stampOf), `active-layers${FINGERPRINT_FIELD_SEPARATOR}${harness.layers.join(',')}`].join(\n FINGERPRINT_ENTRY_SEPARATOR,\n );\n }\n\n /**\n * The uncached read. Protected so a test can count how often it actually runs,\n * which is the only direct way to assert that the cache is doing its job.\n */\n protected load(cwd: string, scope: AgentScope): AgentDiscoveryResult {\n const userSettingsPath = getUserAgentSettingsPath();\n const projectSettingsPath = getProjectAgentSettingsPath(cwd);\n const userSettings: SubagentSettings =\n scope === 'project' ? EMPTY_SUBAGENT_SETTINGS : readSubagentSettings(userSettingsPath);\n const projectSettings: SubagentSettings =\n scope === 'user' ? EMPTY_SUBAGENT_SETTINGS : readSubagentSettings(projectSettingsPath);\n\n const legacyDefaultModel = resolveSubagentDefaultModel(\n userSettings,\n projectSettings,\n userSettingsPath,\n projectSettingsPath,\n );\n const layerSubagents = resolveActiveLayerSubagentConfig();\n const layerModels = layerModelSpecs(layerSubagents?.config);\n const layerPrimaryModel = layerModels?.[0];\n const defaultModel =\n layerPrimaryModel && layerSubagents\n ? {\n type: 'layers.subagents.defaultModel' as const,\n scope: 'layer' as const,\n path: layerSubagents.path,\n model: layerPrimaryModel,\n }\n : legacyDefaultModel;\n const defaultThinking = resolveSubagentDefaultThinking(userSettings, projectSettings, projectSettingsPath);\n const defaultExtensions = resolveSubagentDefaultExtensions(userSettings, projectSettings, projectSettingsPath);\n\n const resolve = (agents: AgentConfig[]): AgentConfig[] =>\n applyCustomAgentOverrides(\n applySubagentDefaults(agents, defaultModel, defaultThinking, defaultExtensions),\n userSettings,\n projectSettings,\n userSettingsPath,\n projectSettingsPath,\n );\n\n const plugin = resolve(pluginAgentDirs().flatMap((directory) => loadAgentsFromDir(directory, 'plugin')));\n const user =\n scope === 'project' ? [] : resolve(userAgentDirs().flatMap((directory) => loadAgentsFromDir(directory, 'user')));\n const project =\n scope === 'user'\n ? []\n : resolve(\n resolveNearestProjectAgentDirs(cwd).readDirs.flatMap((directory) =>\n loadAgentsFromDir(directory, 'project'),\n ),\n );\n\n return {\n // A disabled agent is dropped last, so that an override which re-enables\n // one has already been applied by the time this runs.\n agents: mergeAgentsForScope(scope, user, project, plugin).filter((agent) => agent.disabled !== true),\n projectAgentsDir: resolveNearestProjectAgentDirs(cwd).preferredDir,\n modelScope: projectSettings.modelScope ?? userSettings.modelScope,\n };\n }\n}\n\n/**\n * Copy the parts a caller could mutate in place.\n *\n * The agent objects themselves are treated as immutable by every consumer, so\n * only the array is copied; deep-cloning every config on each keystroke would\n * cost more than the walk this cache replaced.\n */\nfunction cloneResult(result: AgentDiscoveryResult): AgentDiscoveryResult {\n return { ...result, agents: [...result.agents] };\n}\n"],"mappings":"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"}
|
package/dist/agents/settings.cjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const e=require(`../_virtual/_rolldown/runtime.cjs`),t=require(`../shared/configDir.cjs`);require(`../shared/atomicJson.cjs`);let n=require(`node:fs`);n=e.__toESM(n,1);const r={overrides:{}},i=new WeakMap;function a(e,t){i.set(e,t)}function o(e,...t){let n=i.get(e);return n?t.some(e=>n.has(e)):!1}function s(e,t){let n=i.get(e);return n&&i.set(t,n),t}function c(e){let t=[],n=[];for(let r of e??[])r.startsWith(`mcp:`)?t.push(r.slice(4)):n.push(r);return{...e===void 0?{}:{tools:n},...t.length>0?{mcpDirectTools:t}:{}}}function l(e,t){let n=e[t];if(!(!n||typeof n!=`object`||Array.isArray(n)))return n}function u(e,t){if(e===void 0)return;if(!e||typeof e!=`object`||Array.isArray(e))throw Error(`Subagent settings in '${t}' have invalid 'modelScope'; expected an object.`);let n=e,r={};if(`enforce`in n){if(typeof n.enforce!=`boolean`)throw Error(`Subagent settings in '${t}' have invalid 'modelScope.enforce'; expected a boolean.`);r.enforce=n.enforce}if(`allow`in n){if(!Array.isArray(n.allow))throw Error(`Subagent settings in '${t}' have invalid 'modelScope.allow'; expected an array of strings.`);let e=[];for(let r of n.allow){if(typeof r!=`string`)throw Error(`Subagent settings in '${t}' have invalid 'modelScope.allow'; expected an array of strings.`);let n=r.trim();n&&e.push(n)}if(e.length===0)throw Error(`Subagent settings in '${t}' have invalid 'modelScope.allow'; expected a non-empty array of patterns.`);r.allow=e}if(r.enforce===!0&&(!r.allow||r.allow.length===0))throw Error(`Subagent settings in '${t}' set modelScope.enforce without a non-empty 'allow' list; supply allowed model patterns or disable enforcement.`);return Object.keys(r).length>0?r:void 0}function d(e,t){if(e===void 0)return;if(e===!1)return!1;if(!Array.isArray(e))throw Error(`Builtin override '${t.name}' in '${t.filePath}' has invalid '${t.field}'; expected an array of strings or false.`);let n=[];for(let r of e){if(typeof r!=`string`)throw Error(`Builtin override '${t.name}' in '${t.filePath}' has invalid '${t.field}'; expected an array of strings or false.`);let e=r.trim();e&&n.push(e)}return n}function f(e,t,n){if(!t||typeof t!=`object`||Array.isArray(t))throw Error(`Builtin override '${e}' in '${n}' must be an object.`);let r=t,i={};if(`model`in r)if(typeof r.model==`string`||r.model===!1)i.model=r.model;else throw Error(`Builtin override '${e}' in '${n}' has invalid 'model'; expected a string or false.`);if(`thinking`in r)if(typeof r.thinking==`string`||r.thinking===!1)i.thinking=r.thinking;else throw Error(`Builtin override '${e}' in '${n}' has invalid 'thinking'; expected a string or false.`);if(`systemPromptMode`in r)if(r.systemPromptMode===`append`||r.systemPromptMode===`replace`)i.systemPromptMode=r.systemPromptMode;else throw Error(`Builtin override '${e}' in '${n}' has invalid 'systemPromptMode'; expected 'append' or 'replace'.`);if(`inheritProjectContext`in r)if(typeof r.inheritProjectContext==`boolean`)i.inheritProjectContext=r.inheritProjectContext;else throw Error(`Builtin override '${e}' in '${n}' has invalid 'inheritProjectContext'; expected a boolean.`);if(`inheritSkills`in r)if(typeof r.inheritSkills==`boolean`)i.inheritSkills=r.inheritSkills;else throw Error(`Builtin override '${e}' in '${n}' has invalid 'inheritSkills'; expected a boolean.`);if(`defaultContext`in r)if(r.defaultContext===`fresh`||r.defaultContext===`fork`||r.defaultContext===!1)i.defaultContext=r.defaultContext;else throw Error(`Builtin override '${e}' in '${n}' has invalid 'defaultContext'; expected 'fresh', 'fork', or false.`);if(`disabled`in r)if(typeof r.disabled==`boolean`)i.disabled=r.disabled;else throw Error(`Builtin override '${e}' in '${n}' has invalid 'disabled'; expected a boolean.`);if(`completionGuard`in r)if(typeof r.completionGuard==`boolean`)i.completionGuard=r.completionGuard;else throw Error(`Builtin override '${e}' in '${n}' has invalid 'completionGuard'; expected a boolean.`);if(`toolBudget`in r){let t=p(r.toolBudget,e,n);t!==void 0&&(i.toolBudget=t)}if(`systemPrompt`in r)if(typeof r.systemPrompt==`string`)i.systemPrompt=r.systemPrompt;else throw Error(`Builtin override '${e}' in '${n}' has invalid 'systemPrompt'; expected a string.`);let a=t=>d(r[t],{filePath:n,name:e,field:t}),o=a(`fallbackModels`);o!==void 0&&(i.fallbackModels=o);let s=a(`skills`);s!==void 0&&(i.skills=s);let c=a(`tools`);c!==void 0&&(i.tools=c);let l=a(`extensions`);l!==void 0&&(i.extensions=l);let u=a(`subagentOnlyExtensions`);return u!==void 0&&(i.subagentOnlyExtensions=u),Object.keys(i).length>0?i:void 0}function p(e,t,n){if(e===!1)return!1;if(!e||typeof e!=`object`||Array.isArray(e))throw Error(`Builtin override '${t}' in '${n}' has invalid 'toolBudget'; expected an object or false.`);let r=e;if(typeof r.hard!=`number`)throw Error(`Builtin override '${t}' in '${n}' has invalid 'toolBudget.hard'; expected a number.`);let i={hard:r.hard};if(`soft`in r){if(typeof r.soft!=`number`)throw Error(`Builtin override '${t}' in '${n}' has invalid 'toolBudget.soft'; expected a number.`);i.soft=r.soft}if(`block`in r)if(r.block===`*`)i.block=`*`;else if(Array.isArray(r.block)&&r.block.every(e=>typeof e==`string`))i.block=[...r.block];else throw Error(`Builtin override '${t}' in '${n}' has invalid 'toolBudget.block'; expected an array of strings or '*'.`);return i}function m(e){if(!e)return r;let n=l(t.readSettingsFileStrict(e),`subagents`);if(!n)return r;let i;if(`disableBuiltins`in n)if(typeof n.disableBuiltins==`boolean`)i=n.disableBuiltins;else throw Error(`Subagent settings in '${e}' have invalid 'disableBuiltins'; expected a boolean.`);let a;if(`disableThinking`in n)if(typeof n.disableThinking==`boolean`)a=n.disableThinking;else throw Error(`Subagent settings in '${e}' have invalid 'disableThinking'; expected a boolean.`);let o;if(`defaultModel`in n)if(typeof n.defaultModel==`string`&&n.defaultModel.trim())o=n.defaultModel.trim();else throw Error(`Subagent settings in '${e}' have invalid 'defaultModel'; expected a non-empty string.`);let s;if(`defaultThinking`in n)if(typeof n.defaultThinking==`string`&&n.defaultThinking.trim())s=n.defaultThinking.trim();else throw Error(`Subagent settings in '${e}' have invalid 'defaultThinking'; expected a non-empty string.`);let c;if(`defaultExtensions`in n){let t=n.defaultExtensions;if(!Array.isArray(t)||t.some(e=>typeof e!=`string`||!e.trim()))throw Error(`Subagent settings in '${e}' have invalid 'defaultExtensions'; expected an array of non-empty strings.`);c=t.map(e=>String(e).trim())}let d=u(n.modelScope,e),p={},m=l(n,`agentOverrides`);if(m)for(let[t,n]of Object.entries(m)){let r=f(t,n,e);r&&(p[t]=r)}return{overrides:p,defaultModel:o,defaultThinking:s,defaultExtensions:c,disableBuiltins:i,disableThinking:a,modelScope:d}}function h(e,t,n,r){return r&&t.defaultModel!==void 0?{type:`subagents.defaultModel`,scope:`project`,path:r,model:t.defaultModel}:e.defaultModel===void 0?void 0:{type:`subagents.defaultModel`,scope:`user`,path:n,model:e.defaultModel}}function g(e,t){return t?e.map(e=>e.model===void 0?s(e,{...e,model:t.model,modelSource:t}):e):e}function _(e,t){return t===void 0?e:e.map(e=>e.fallbackModels===void 0?s(e,{...e,fallbackModels:[...t]}):e)}function v(e,t,n){return n&&t.defaultThinking!==void 0?t.defaultThinking:e.defaultThinking}function y(e,t){return t===void 0?e:e.map(e=>e.thinking===void 0?s(e,{...e,thinking:t}):e)}function b(e,t,n){return n&&t.defaultExtensions!==void 0?t.defaultExtensions:e.defaultExtensions}function x(e,t){return t===void 0?e:e.map(e=>e.extensions===void 0?s(e,{...e,extensions:[...t],extensionsFromDefault:!0}):e)}function S(e,t,n,r,i){return x(y(_(g(e,t),i),n),r)}function C(e){return{model:e.model,fallbackModels:e.fallbackModels?[...e.fallbackModels]:void 0,thinking:e.thinking,systemPromptMode:e.systemPromptMode,inheritProjectContext:e.inheritProjectContext,inheritSkills:e.inheritSkills,defaultContext:e.defaultContext,disabled:e.disabled,systemPrompt:e.systemPrompt,skills:e.skills?[...e.skills]:void 0,skillPath:e.skillPath?[...e.skillPath]:void 0,tools:e.tools?[...e.tools]:void 0,mcpDirectTools:e.mcpDirectTools?[...e.mcpDirectTools]:void 0,extensions:e.extensionsFromDefault?void 0:e.extensions?[...e.extensions]:void 0,subagentOnlyExtensions:e.subagentOnlyExtensions?[...e.subagentOnlyExtensions]:void 0,completionGuard:e.completionGuard,toolBudget:e.toolBudget}}function w(e,t,n){let r,i=!1,a=()=>(r??={...e},r),l=(t,n,r)=>{o(e,...n)||(a()[t]=r,i=!0)};if(t.model!==void 0&&l(`model`,[`model`],t.model===!1?void 0:t.model),t.fallbackModels!==void 0&&l(`fallbackModels`,[`fallbackModels`],t.fallbackModels===!1?void 0:[...t.fallbackModels]),t.thinking!==void 0&&l(`thinking`,[`thinking`],t.thinking===!1?void 0:t.thinking),t.systemPromptMode!==void 0&&l(`systemPromptMode`,[`systemPromptMode`],t.systemPromptMode),t.inheritProjectContext!==void 0&&l(`inheritProjectContext`,[`inheritProjectContext`],t.inheritProjectContext),t.inheritSkills!==void 0&&l(`inheritSkills`,[`inheritSkills`],t.inheritSkills),t.defaultContext!==void 0&&l(`defaultContext`,[`defaultContext`],t.defaultContext===!1?void 0:t.defaultContext),t.disabled!==void 0&&e.disabled===void 0&&(a().disabled=t.disabled,i=!0),t.skills!==void 0&&l(`skills`,[`skill`,`skills`],t.skills===!1?void 0:[...t.skills]),t.tools!==void 0&&!o(e,`tools`)){let{tools:e,mcpDirectTools:n}=c(t.tools===!1?[]:t.tools),r=a();r.tools=e,r.mcpDirectTools=n,i=!0}return t.extensions!==void 0&&l(`extensions`,[`extensions`],t.extensions===!1?void 0:[...t.extensions]),t.subagentOnlyExtensions!==void 0&&l(`subagentOnlyExtensions`,[`subagentOnlyExtensions`],t.subagentOnlyExtensions===!1?void 0:[...t.subagentOnlyExtensions]),t.completionGuard!==void 0&&l(`completionGuard`,[`completionGuard`],t.completionGuard),t.toolBudget!==void 0&&l(`toolBudget`,[`toolBudget`],t.toolBudget===!1?void 0:t.toolBudget),!i||!r?e:(r.override={...n,base:C(e)},s(e,r))}function T(e,t,n,r,i){return e.map(e=>{let a=n.overrides[e.name];if(a&&i)return w(e,a,{scope:`project`,path:i});let o=t.overrides[e.name];return o?w(e,o,{scope:`user`,path:r}):e})}exports.EMPTY_SUBAGENT_SETTINGS=r,exports.applyCustomAgentOverrides=T,exports.applySubagentDefaults=S,exports.readSubagentSettings=m,exports.resolveSubagentDefaultExtensions=b,exports.resolveSubagentDefaultModel=h,exports.resolveSubagentDefaultThinking=v,exports.setAgentFrontmatterFields=a;
|
|
1
|
+
const e=require(`../_virtual/_rolldown/runtime.cjs`),t=require(`../shared/configDir.cjs`);require(`../shared/atomicJson.cjs`);let n=require(`node:fs`);n=e.__toESM(n,1);const r={overrides:{}},i=new WeakMap;function a(e,t){i.set(e,t)}function o(e,...t){let n=i.get(e);return n?t.some(e=>n.has(e)):!1}function s(e,t){let n=i.get(e);return n&&i.set(t,n),t}function c(e){let t=[],n=[];for(let r of e??[])r.startsWith(`mcp:`)?t.push(r.slice(4)):n.push(r);return{...e===void 0?{}:{tools:n},...t.length>0?{mcpDirectTools:t}:{}}}function l(e,t){let n=e[t];if(!(!n||typeof n!=`object`||Array.isArray(n)))return n}function u(e,t){if(e===void 0)return;if(!e||typeof e!=`object`||Array.isArray(e))throw Error(`Subagent settings in '${t}' have invalid 'modelScope'; expected an object.`);let n=e,r={};if(`enforce`in n){if(typeof n.enforce!=`boolean`)throw Error(`Subagent settings in '${t}' have invalid 'modelScope.enforce'; expected a boolean.`);r.enforce=n.enforce}if(`allow`in n){if(!Array.isArray(n.allow))throw Error(`Subagent settings in '${t}' have invalid 'modelScope.allow'; expected an array of strings.`);let e=[];for(let r of n.allow){if(typeof r!=`string`)throw Error(`Subagent settings in '${t}' have invalid 'modelScope.allow'; expected an array of strings.`);let n=r.trim();n&&e.push(n)}if(e.length===0)throw Error(`Subagent settings in '${t}' have invalid 'modelScope.allow'; expected a non-empty array of patterns.`);r.allow=e}if(r.enforce===!0&&(!r.allow||r.allow.length===0))throw Error(`Subagent settings in '${t}' set modelScope.enforce without a non-empty 'allow' list; supply allowed model patterns or disable enforcement.`);return Object.keys(r).length>0?r:void 0}function d(e,t){if(e===void 0)return;if(e===!1)return!1;if(!Array.isArray(e))throw Error(`Builtin override '${t.name}' in '${t.filePath}' has invalid '${t.field}'; expected an array of strings or false.`);let n=[];for(let r of e){if(typeof r!=`string`)throw Error(`Builtin override '${t.name}' in '${t.filePath}' has invalid '${t.field}'; expected an array of strings or false.`);let e=r.trim();e&&n.push(e)}return n}function f(e,t,n){if(!t||typeof t!=`object`||Array.isArray(t))throw Error(`Builtin override '${e}' in '${n}' must be an object.`);let r=t,i={};if(`model`in r)if(typeof r.model==`string`||r.model===!1)i.model=r.model;else throw Error(`Builtin override '${e}' in '${n}' has invalid 'model'; expected a string or false.`);if(`thinking`in r)if(typeof r.thinking==`string`||r.thinking===!1)i.thinking=r.thinking;else throw Error(`Builtin override '${e}' in '${n}' has invalid 'thinking'; expected a string or false.`);if(`systemPromptMode`in r)if(r.systemPromptMode===`append`||r.systemPromptMode===`replace`)i.systemPromptMode=r.systemPromptMode;else throw Error(`Builtin override '${e}' in '${n}' has invalid 'systemPromptMode'; expected 'append' or 'replace'.`);if(`inheritProjectContext`in r)if(typeof r.inheritProjectContext==`boolean`)i.inheritProjectContext=r.inheritProjectContext;else throw Error(`Builtin override '${e}' in '${n}' has invalid 'inheritProjectContext'; expected a boolean.`);if(`inheritSkills`in r)if(typeof r.inheritSkills==`boolean`)i.inheritSkills=r.inheritSkills;else throw Error(`Builtin override '${e}' in '${n}' has invalid 'inheritSkills'; expected a boolean.`);if(`defaultContext`in r)if(r.defaultContext===`fresh`||r.defaultContext===`fork`||r.defaultContext===!1)i.defaultContext=r.defaultContext;else throw Error(`Builtin override '${e}' in '${n}' has invalid 'defaultContext'; expected 'fresh', 'fork', or false.`);if(`disabled`in r)if(typeof r.disabled==`boolean`)i.disabled=r.disabled;else throw Error(`Builtin override '${e}' in '${n}' has invalid 'disabled'; expected a boolean.`);if(`completionGuard`in r)if(typeof r.completionGuard==`boolean`)i.completionGuard=r.completionGuard;else throw Error(`Builtin override '${e}' in '${n}' has invalid 'completionGuard'; expected a boolean.`);if(`toolBudget`in r){let t=p(r.toolBudget,e,n);t!==void 0&&(i.toolBudget=t)}if(`systemPrompt`in r)if(typeof r.systemPrompt==`string`)i.systemPrompt=r.systemPrompt;else throw Error(`Builtin override '${e}' in '${n}' has invalid 'systemPrompt'; expected a string.`);let a=t=>d(r[t],{filePath:n,name:e,field:t}),o=a(`fallbackModels`);o!==void 0&&(i.fallbackModels=o);let s=a(`skills`);s!==void 0&&(i.skills=s);let c=a(`tools`);c!==void 0&&(i.tools=c);let l=a(`extensions`);l!==void 0&&(i.extensions=l);let u=a(`subagentOnlyExtensions`);return u!==void 0&&(i.subagentOnlyExtensions=u),Object.keys(i).length>0?i:void 0}function p(e,t,n){if(e===!1)return!1;if(!e||typeof e!=`object`||Array.isArray(e))throw Error(`Builtin override '${t}' in '${n}' has invalid 'toolBudget'; expected an object or false.`);let r=e;if(typeof r.hard!=`number`)throw Error(`Builtin override '${t}' in '${n}' has invalid 'toolBudget.hard'; expected a number.`);let i={hard:r.hard};if(`soft`in r){if(typeof r.soft!=`number`)throw Error(`Builtin override '${t}' in '${n}' has invalid 'toolBudget.soft'; expected a number.`);i.soft=r.soft}if(`block`in r)if(r.block===`*`)i.block=`*`;else if(Array.isArray(r.block)&&r.block.every(e=>typeof e==`string`))i.block=[...r.block];else throw Error(`Builtin override '${t}' in '${n}' has invalid 'toolBudget.block'; expected an array of strings or '*'.`);return i}function m(e){if(!e)return r;let n=l(t.readSettingsFileStrict(e),`subagents`);if(!n)return r;let i;if(`disableBuiltins`in n)if(typeof n.disableBuiltins==`boolean`)i=n.disableBuiltins;else throw Error(`Subagent settings in '${e}' have invalid 'disableBuiltins'; expected a boolean.`);let a;if(`disableThinking`in n)if(typeof n.disableThinking==`boolean`)a=n.disableThinking;else throw Error(`Subagent settings in '${e}' have invalid 'disableThinking'; expected a boolean.`);let o;if(`defaultModel`in n)if(typeof n.defaultModel==`string`&&n.defaultModel.trim())o=n.defaultModel.trim();else throw Error(`Subagent settings in '${e}' have invalid 'defaultModel'; expected a non-empty string.`);let s;if(`defaultThinking`in n)if(typeof n.defaultThinking==`string`&&n.defaultThinking.trim())s=n.defaultThinking.trim();else throw Error(`Subagent settings in '${e}' have invalid 'defaultThinking'; expected a non-empty string.`);let c;if(`defaultExtensions`in n){let t=n.defaultExtensions;if(!Array.isArray(t)||t.some(e=>typeof e!=`string`||!e.trim()))throw Error(`Subagent settings in '${e}' have invalid 'defaultExtensions'; expected an array of non-empty strings.`);c=t.map(e=>String(e).trim())}let d=u(n.modelScope,e),p={},m=l(n,`agentOverrides`);if(m)for(let[t,n]of Object.entries(m)){let r=f(t,n,e);r&&(p[t]=r)}return{overrides:p,defaultModel:o,defaultThinking:s,defaultExtensions:c,disableBuiltins:i,disableThinking:a,modelScope:d}}function h(e,t,n,r){return r&&t.defaultModel!==void 0?{type:`subagents.defaultModel`,scope:`project`,path:r,model:t.defaultModel}:e.defaultModel===void 0?void 0:{type:`subagents.defaultModel`,scope:`user`,path:n,model:e.defaultModel}}function g(e,t){return t?e.map(e=>e.model===void 0?s(e,{...e,model:t.model,modelSource:t}):e):e}function _(e,t){return t===void 0?e:e.map(e=>e.fallbackModels===void 0?s(e,{...e,fallbackModels:[...t]}):e)}function v(e,t,n){return n&&t.defaultThinking!==void 0?t.defaultThinking:e.defaultThinking}function y(e,t){return t===void 0?e:e.map(e=>e.thinking===void 0?s(e,{...e,thinking:t}):e)}function b(e,t,n){return n&&t.defaultExtensions!==void 0?t.defaultExtensions:e.defaultExtensions}function x(e,t){return t===void 0?e:e.map(e=>e.extensions===void 0?s(e,{...e,extensions:[...t],extensionsFromDefault:!0}):e)}function S(e,t,n,r,i){return x(y(_(g(e,t),i),n),r)}function C(e){return{model:e.model,fallbackModels:e.fallbackModels?[...e.fallbackModels]:void 0,thinking:e.thinking,systemPromptMode:e.systemPromptMode,inheritProjectContext:e.inheritProjectContext,inheritSkills:e.inheritSkills,defaultContext:e.defaultContext,disabled:e.disabled,systemPrompt:e.systemPrompt,skills:e.skills?[...e.skills]:void 0,skillPath:e.skillPath?[...e.skillPath]:void 0,tools:e.tools?[...e.tools]:void 0,mcpDirectTools:e.mcpDirectTools?[...e.mcpDirectTools]:void 0,extensions:e.extensionsFromDefault?void 0:e.extensions?[...e.extensions]:void 0,subagentOnlyExtensions:e.subagentOnlyExtensions?[...e.subagentOnlyExtensions]:void 0,completionGuard:e.completionGuard,toolBudget:e.toolBudget}}function w(e,t,n){let r,i=!1,a=()=>(r??={...e},r),l=(t,n,r)=>{o(e,...n)||(a()[t]=r,i=!0)};if(t.model!==void 0&&!o(e,`model`)){let e=a();e.model=t.model===!1?void 0:t.model,e.modelSource=void 0,i=!0}if(t.fallbackModels!==void 0&&l(`fallbackModels`,[`fallbackModels`],t.fallbackModels===!1?void 0:[...t.fallbackModels]),t.thinking!==void 0&&l(`thinking`,[`thinking`],t.thinking===!1?void 0:t.thinking),t.systemPromptMode!==void 0&&l(`systemPromptMode`,[`systemPromptMode`],t.systemPromptMode),t.inheritProjectContext!==void 0&&l(`inheritProjectContext`,[`inheritProjectContext`],t.inheritProjectContext),t.inheritSkills!==void 0&&l(`inheritSkills`,[`inheritSkills`],t.inheritSkills),t.defaultContext!==void 0&&l(`defaultContext`,[`defaultContext`],t.defaultContext===!1?void 0:t.defaultContext),t.disabled!==void 0&&e.disabled===void 0&&(a().disabled=t.disabled,i=!0),t.skills!==void 0&&l(`skills`,[`skill`,`skills`],t.skills===!1?void 0:[...t.skills]),t.tools!==void 0&&!o(e,`tools`)){let{tools:e,mcpDirectTools:n}=c(t.tools===!1?[]:t.tools),r=a();r.tools=e,r.mcpDirectTools=n,i=!0}return t.extensions!==void 0&&l(`extensions`,[`extensions`],t.extensions===!1?void 0:[...t.extensions]),t.subagentOnlyExtensions!==void 0&&l(`subagentOnlyExtensions`,[`subagentOnlyExtensions`],t.subagentOnlyExtensions===!1?void 0:[...t.subagentOnlyExtensions]),t.completionGuard!==void 0&&l(`completionGuard`,[`completionGuard`],t.completionGuard),t.toolBudget!==void 0&&l(`toolBudget`,[`toolBudget`],t.toolBudget===!1?void 0:t.toolBudget),!i||!r?e:(r.override={...n,base:C(e)},s(e,r))}function T(e,t,n,r,i){return e.map(e=>{let a=n.overrides[e.name];if(a&&i)return w(e,a,{scope:`project`,path:i});let o=t.overrides[e.name];return o?w(e,o,{scope:`user`,path:r}):e})}exports.EMPTY_SUBAGENT_SETTINGS=r,exports.applyCustomAgentOverrides=T,exports.applySubagentDefaults=S,exports.readSubagentSettings=m,exports.resolveSubagentDefaultExtensions=b,exports.resolveSubagentDefaultModel=h,exports.resolveSubagentDefaultThinking=v,exports.setAgentFrontmatterFields=a;
|
|
2
2
|
//# sourceMappingURL=settings.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"settings.cjs","names":["readSettingsFileStrict"],"sources":["../../src/agents/settings.ts"],"sourcesContent":["/**\n * Agent settings: reading them, and applying what they say to discovered agents.\n *\n * Two things live here because they are the same contract seen from both ends.\n * Reading turns untyped JSON on disk into settings; applying folds those\n * settings over the agents discovery found. The precedence is fixed: project\n * settings beat user settings, an explicit per-agent override beats a bulk\n * switch, and for a custom agent the agent's own frontmatter beats both.\n *\n * That last rule is why builtins and custom agents take different paths.\n * Overriding a builtin replaces values outright, because a builtin has no file\n * the user could have edited instead. Overriding a custom agent only fills\n * fields its frontmatter left unset, so a settings default never silently\n * contradicts what the author wrote in the file.\n *\n * DESIGN PATTERNS:\n * - Reading is strict and throws with the offending path and field. A settings\n * file that is quietly half-ignored is worse than one that refuses to load,\n * because the user sees agents behaving in ways their config does not explain\n * - Applying is pure and copy-on-write. Every `apply*` returns new configs and\n * never mutates its input, so a cached discovery result cannot be corrupted\n * by a later pass over it\n * - Overriding a builtin snapshots the values it replaced into `override.base`,\n * which is what makes removing the override restore the builtin exactly\n *\n * AVOID:\n * - Trusting any shape read off disk without a real guard\n * - Mutating an `AgentConfig` in place\n * - Writing a settings file non-atomically; it holds the user's overrides\n */\n\nimport * as fs from 'node:fs';\nimport { writeAtomicJson } from '../shared/atomicJson.ts';\nimport { readSettingsFileStrict } from '../shared/configDir.ts';\nimport type { ModelScopeConfig, ToolBudgetConfig } from '../types.ts';\nimport { getProjectAgentSettingsPath, getUserAgentSettingsPath } from './projectRoot.ts';\nexport { readSettingsFileStrict } from '../shared/configDir.ts';\nimport type {\n AgentConfig,\n AgentModelSourceInfo,\n BuiltinAgentOverrideBase,\n BuiltinAgentOverrideConfig,\n SubagentSettings,\n} from './types.ts';\n\nconst SUBAGENTS_SETTINGS_KEY = 'subagents';\nconst AGENT_OVERRIDES_KEY = 'agentOverrides';\n/** Prefix marking a tool the child calls on an MCP server directly. */\nconst MCP_DIRECT_TOOL_PREFIX = 'mcp:';\n\n/** Which settings file an override came from, without the value snapshot. */\ntype OverrideScopeMeta = { scope: 'user' | 'project'; path: string };\n\n/**\n * The snapshot of a builtin's pre-override values.\n *\n * Widens the contract with `mcpDirectTools` because a builtin's tool list is\n * stored split in `AgentConfig` and must round-trip through the snapshot to be\n * restorable. See the report note on `BuiltinAgentOverrideBase`.\n */\nexport interface BuiltinOverrideSnapshot extends BuiltinAgentOverrideBase {\n mcpDirectTools?: string[];\n}\n\n/**\n * Returned whenever there is nothing to read.\n *\n * Shared rather than freshly built per call because it is returned on the hot\n * discovery path for every session with no project settings file.\n */\nexport const EMPTY_SUBAGENT_SETTINGS: SubagentSettings = { overrides: {} };\n\n/**\n * Which fields of a config came from its file's frontmatter.\n *\n * Keyed by the config object rather than by agent name because configs are\n * copied on write: each `apply*` pass produces new objects, and the new one\n * inherits the entry so later passes still know what the author declared.\n * A WeakMap means a discarded config's entry is collectable with it.\n */\nconst agentFrontmatterFields = new WeakMap<AgentConfig, Set<string>>();\n\n/** Record which fields a freshly parsed config took from frontmatter. */\nexport function setAgentFrontmatterFields(agent: AgentConfig, fields: Set<string>): void {\n agentFrontmatterFields.set(agent, fields);\n}\n\n/** True when the agent's file declared any of `fields` in its frontmatter. */\nexport function agentHasFrontmatterField(agent: AgentConfig, ...fields: string[]): boolean {\n const frontmatterFields = agentFrontmatterFields.get(agent);\n return frontmatterFields ? fields.some((field) => frontmatterFields.has(field)) : false;\n}\n\n/** Carry the frontmatter record onto a copy produced by an `apply*` pass. */\nfunction inheritFrontmatterFields(source: AgentConfig, next: AgentConfig): AgentConfig {\n const frontmatterFields = agentFrontmatterFields.get(source);\n if (frontmatterFields) agentFrontmatterFields.set(next, frontmatterFields);\n return next;\n}\n\n/** Split a flat tool list into host tools and direct MCP tools. */\nfunction splitToolList(rawTools: string[] | undefined): { tools?: string[]; mcpDirectTools?: string[] } {\n const mcpDirectTools: string[] = [];\n const tools: string[] = [];\n for (const tool of rawTools ?? []) {\n if (tool.startsWith(MCP_DIRECT_TOOL_PREFIX)) mcpDirectTools.push(tool.slice(MCP_DIRECT_TOOL_PREFIX.length));\n else tools.push(tool);\n }\n return {\n ...(rawTools !== undefined ? { tools } : {}),\n ...(mcpDirectTools.length > 0 ? { mcpDirectTools } : {}),\n };\n}\n\n/** Inverse of `splitToolList`, for writing a tool list back to settings. */\nfunction joinToolList(config: { tools?: string[]; mcpDirectTools?: string[] }): string[] | undefined {\n const joined = [\n ...(config.tools ?? []),\n ...(config.mcpDirectTools ?? []).map((tool) => `${MCP_DIRECT_TOOL_PREFIX}${tool}`),\n ];\n return joined.length > 0 ? joined : undefined;\n}\n\n/** Order-sensitive equality; a reordered tool list is a real change. */\nfunction arraysEqual(a: string[] | undefined, b: string[] | undefined): boolean {\n if (!a && !b) return true;\n if (!a || !b) return false;\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) {\n if (a[i] !== b[i]) return false;\n }\n return true;\n}\n\n// ============================================================================\n// Reading\n// ============================================================================\n\n/**\n * Write a settings file atomically.\n *\n * This file holds every override the user configured. A partial write from a\n * crash mid-save would be read back as malformed on the next start and, because\n * reading is strict, would refuse to load at all.\n */\nexport function writeSettingsFile(filePath: string, settings: Record<string, unknown>): void {\n writeAtomicJson(filePath, settings);\n}\n\n/** Read a settings object at `key`, or undefined when it is absent or not an object. */\nfunction readObjectField(source: Record<string, unknown>, key: string): Record<string, unknown> | undefined {\n const value = source[key];\n if (!value || typeof value !== 'object' || Array.isArray(value)) return undefined;\n return value as Record<string, unknown>;\n}\n\n/** Copy a settings object at `key`, or start a fresh one when it is absent. */\nfunction cloneObjectField(source: Record<string, unknown>, key: string): Record<string, unknown> {\n // Spreading undefined is already a no-op, so no empty-object fallback is needed.\n return { ...readObjectField(source, key) };\n}\n\n/**\n * Parse the model-scope block.\n *\n * Lives here rather than beside the scope enforcement logic because parsing is\n * a settings concern and the agents domain may not reach into the runs domain.\n */\nfunction parseModelScopeConfig(value: unknown, filePath: string): ModelScopeConfig | undefined {\n if (value === undefined) return undefined;\n if (!value || typeof value !== 'object' || Array.isArray(value)) {\n throw new Error(`Subagent settings in '${filePath}' have invalid 'modelScope'; expected an object.`);\n }\n\n const input = value as Record<string, unknown>;\n const config: ModelScopeConfig = {};\n\n if ('enforce' in input) {\n if (typeof input.enforce !== 'boolean') {\n throw new Error(`Subagent settings in '${filePath}' have invalid 'modelScope.enforce'; expected a boolean.`);\n }\n config.enforce = input.enforce;\n }\n\n if ('allow' in input) {\n if (!Array.isArray(input.allow)) {\n throw new Error(\n `Subagent settings in '${filePath}' have invalid 'modelScope.allow'; expected an array of strings.`,\n );\n }\n const allow: string[] = [];\n for (const entry of input.allow) {\n if (typeof entry !== 'string') {\n throw new Error(\n `Subagent settings in '${filePath}' have invalid 'modelScope.allow'; expected an array of strings.`,\n );\n }\n const trimmed = entry.trim();\n if (trimmed) allow.push(trimmed);\n }\n if (allow.length === 0) {\n throw new Error(\n `Subagent settings in '${filePath}' have invalid 'modelScope.allow'; expected a non-empty array of patterns.`,\n );\n }\n config.allow = allow;\n }\n\n // Enforcing against an empty allow list would reject every model, so it is\n // rejected here rather than at the point it would start failing runs.\n if (config.enforce === true && (!config.allow || config.allow.length === 0)) {\n throw new Error(\n `Subagent settings in '${filePath}' set modelScope.enforce without a non-empty 'allow' list; supply allowed model patterns or disable enforcement.`,\n );\n }\n\n return Object.keys(config).length > 0 ? config : undefined;\n}\n\n/**\n * Parse a string-array override field, which may also be `false` to clear it.\n *\n * Empty entries are dropped rather than rejected: trailing commas in a hand\n * edited list are common and unambiguous.\n */\nexport function parseOverrideStringArrayOrFalse(\n value: unknown,\n meta: { filePath: string; name: string; field: string },\n): string[] | false | undefined {\n if (value === undefined) return undefined;\n if (value === false) return false;\n if (!Array.isArray(value)) {\n throw new Error(\n `Builtin override '${meta.name}' in '${meta.filePath}' has invalid '${meta.field}'; expected an array of strings or false.`,\n );\n }\n\n const items: string[] = [];\n for (const item of value) {\n if (typeof item !== 'string') {\n throw new Error(\n `Builtin override '${meta.name}' in '${meta.filePath}' has invalid '${meta.field}'; expected an array of strings or false.`,\n );\n }\n const trimmed = item.trim();\n if (trimmed) items.push(trimmed);\n }\n return items;\n}\n\n/**\n * Parse one entry of `subagents.agentOverrides`.\n *\n * Returns undefined for an entry that declared nothing, so an empty object in\n * the file does not register as an override and mark the agent as overridden.\n */\nexport function parseBuiltinOverrideEntry(\n name: string,\n value: unknown,\n filePath: string,\n): BuiltinAgentOverrideConfig | undefined {\n if (!value || typeof value !== 'object' || Array.isArray(value)) {\n throw new Error(`Builtin override '${name}' in '${filePath}' must be an object.`);\n }\n\n const input = value as Record<string, unknown>;\n const override: BuiltinAgentOverrideConfig = {};\n\n if ('model' in input) {\n if (typeof input.model === 'string' || input.model === false) override.model = input.model;\n else\n throw new Error(`Builtin override '${name}' in '${filePath}' has invalid 'model'; expected a string or false.`);\n }\n\n if ('thinking' in input) {\n if (typeof input.thinking === 'string' || input.thinking === false) override.thinking = input.thinking;\n else\n throw new Error(\n `Builtin override '${name}' in '${filePath}' has invalid 'thinking'; expected a string or false.`,\n );\n }\n\n if ('systemPromptMode' in input) {\n if (input.systemPromptMode === 'append' || input.systemPromptMode === 'replace') {\n override.systemPromptMode = input.systemPromptMode;\n } else {\n throw new Error(\n `Builtin override '${name}' in '${filePath}' has invalid 'systemPromptMode'; expected 'append' or 'replace'.`,\n );\n }\n }\n\n if ('inheritProjectContext' in input) {\n if (typeof input.inheritProjectContext === 'boolean') {\n override.inheritProjectContext = input.inheritProjectContext;\n } else {\n throw new Error(\n `Builtin override '${name}' in '${filePath}' has invalid 'inheritProjectContext'; expected a boolean.`,\n );\n }\n }\n\n if ('inheritSkills' in input) {\n if (typeof input.inheritSkills === 'boolean') {\n override.inheritSkills = input.inheritSkills;\n } else {\n throw new Error(`Builtin override '${name}' in '${filePath}' has invalid 'inheritSkills'; expected a boolean.`);\n }\n }\n\n if ('defaultContext' in input) {\n if (input.defaultContext === 'fresh' || input.defaultContext === 'fork' || input.defaultContext === false) {\n override.defaultContext = input.defaultContext;\n } else {\n throw new Error(\n `Builtin override '${name}' in '${filePath}' has invalid 'defaultContext'; expected 'fresh', 'fork', or false.`,\n );\n }\n }\n\n if ('disabled' in input) {\n if (typeof input.disabled === 'boolean') {\n override.disabled = input.disabled;\n } else {\n throw new Error(`Builtin override '${name}' in '${filePath}' has invalid 'disabled'; expected a boolean.`);\n }\n }\n\n if ('completionGuard' in input) {\n if (typeof input.completionGuard === 'boolean') {\n override.completionGuard = input.completionGuard;\n } else {\n throw new Error(`Builtin override '${name}' in '${filePath}' has invalid 'completionGuard'; expected a boolean.`);\n }\n }\n\n if ('toolBudget' in input) {\n const toolBudget = parseToolBudgetOverride(input.toolBudget, name, filePath);\n if (toolBudget !== undefined) override.toolBudget = toolBudget;\n }\n\n if ('systemPrompt' in input) {\n if (typeof input.systemPrompt === 'string') override.systemPrompt = input.systemPrompt;\n else throw new Error(`Builtin override '${name}' in '${filePath}' has invalid 'systemPrompt'; expected a string.`);\n }\n\n const parseArrayField = (field: string): string[] | false | undefined =>\n parseOverrideStringArrayOrFalse(input[field], { filePath, name, field });\n\n const fallbackModels = parseArrayField('fallbackModels');\n if (fallbackModels !== undefined) override.fallbackModels = fallbackModels;\n const skills = parseArrayField('skills');\n if (skills !== undefined) override.skills = skills;\n const tools = parseArrayField('tools');\n if (tools !== undefined) override.tools = tools;\n const extensions = parseArrayField('extensions');\n if (extensions !== undefined) override.extensions = extensions;\n const subagentOnlyExtensions = parseArrayField('subagentOnlyExtensions');\n if (subagentOnlyExtensions !== undefined) override.subagentOnlyExtensions = subagentOnlyExtensions;\n\n return Object.keys(override).length > 0 ? override : undefined;\n}\n\n/**\n * Parse a `toolBudget` override.\n *\n * `hard` is required by the contract, so an object without a usable one is\n * rejected here instead of producing a budget that never blocks.\n */\nfunction parseToolBudgetOverride(value: unknown, name: string, filePath: string): ToolBudgetConfig | false | undefined {\n if (value === false) return false;\n if (!value || typeof value !== 'object' || Array.isArray(value)) {\n throw new Error(\n `Builtin override '${name}' in '${filePath}' has invalid 'toolBudget'; expected an object or false.`,\n );\n }\n\n const input = value as Record<string, unknown>;\n if (typeof input.hard !== 'number') {\n throw new Error(`Builtin override '${name}' in '${filePath}' has invalid 'toolBudget.hard'; expected a number.`);\n }\n\n const budget: ToolBudgetConfig = { hard: input.hard };\n if ('soft' in input) {\n if (typeof input.soft !== 'number') {\n throw new Error(`Builtin override '${name}' in '${filePath}' has invalid 'toolBudget.soft'; expected a number.`);\n }\n budget.soft = input.soft;\n }\n if ('block' in input) {\n if (input.block === '*') {\n budget.block = '*';\n } else if (Array.isArray(input.block) && input.block.every((entry) => typeof entry === 'string')) {\n budget.block = [...input.block];\n } else {\n throw new Error(\n `Builtin override '${name}' in '${filePath}' has invalid 'toolBudget.block'; expected an array of strings or '*'.`,\n );\n }\n }\n return budget;\n}\n\n/** Read and validate one settings file's `subagents` block. */\nexport function readSubagentSettings(filePath: string | null): SubagentSettings {\n if (!filePath) return EMPTY_SUBAGENT_SETTINGS;\n const settings = readSettingsFileStrict(filePath);\n const subagentsObject = readObjectField(settings, SUBAGENTS_SETTINGS_KEY);\n if (!subagentsObject) return EMPTY_SUBAGENT_SETTINGS;\n\n let disableBuiltins: boolean | undefined;\n if ('disableBuiltins' in subagentsObject) {\n if (typeof subagentsObject.disableBuiltins === 'boolean') {\n disableBuiltins = subagentsObject.disableBuiltins;\n } else {\n throw new Error(`Subagent settings in '${filePath}' have invalid 'disableBuiltins'; expected a boolean.`);\n }\n }\n\n let disableThinking: boolean | undefined;\n if ('disableThinking' in subagentsObject) {\n if (typeof subagentsObject.disableThinking === 'boolean') {\n disableThinking = subagentsObject.disableThinking;\n } else {\n throw new Error(`Subagent settings in '${filePath}' have invalid 'disableThinking'; expected a boolean.`);\n }\n }\n\n let defaultModel: string | undefined;\n if ('defaultModel' in subagentsObject) {\n if (typeof subagentsObject.defaultModel === 'string' && subagentsObject.defaultModel.trim()) {\n defaultModel = subagentsObject.defaultModel.trim();\n } else {\n throw new Error(`Subagent settings in '${filePath}' have invalid 'defaultModel'; expected a non-empty string.`);\n }\n }\n\n let defaultThinking: string | undefined;\n if ('defaultThinking' in subagentsObject) {\n if (typeof subagentsObject.defaultThinking === 'string' && subagentsObject.defaultThinking.trim()) {\n defaultThinking = subagentsObject.defaultThinking.trim();\n } else {\n throw new Error(\n `Subagent settings in '${filePath}' have invalid 'defaultThinking'; expected a non-empty string.`,\n );\n }\n }\n\n let defaultExtensions: string[] | undefined;\n if ('defaultExtensions' in subagentsObject) {\n const raw = subagentsObject.defaultExtensions;\n if (!Array.isArray(raw) || raw.some((item) => typeof item !== 'string' || !item.trim())) {\n throw new Error(\n `Subagent settings in '${filePath}' have invalid 'defaultExtensions'; expected an array of non-empty strings.`,\n );\n }\n defaultExtensions = raw.map((item) => String(item).trim());\n }\n\n const modelScope = parseModelScopeConfig(subagentsObject.modelScope, filePath);\n\n const overrides: Record<string, BuiltinAgentOverrideConfig> = {};\n const agentOverrides = readObjectField(subagentsObject, AGENT_OVERRIDES_KEY);\n if (agentOverrides) {\n for (const [name, value] of Object.entries(agentOverrides)) {\n const override = parseBuiltinOverrideEntry(name, value, filePath);\n if (override) overrides[name] = override;\n }\n }\n\n return { overrides, defaultModel, defaultThinking, defaultExtensions, disableBuiltins, disableThinking, modelScope };\n}\n\n// ============================================================================\n// Settings-level defaults\n// ============================================================================\n\n/** Which settings file supplies `defaultModel`, project winning over user. */\nexport function resolveSubagentDefaultModel(\n userSettings: SubagentSettings,\n projectSettings: SubagentSettings,\n userSettingsPath: string,\n projectSettingsPath: string | null,\n): AgentModelSourceInfo | undefined {\n if (projectSettingsPath && projectSettings.defaultModel !== undefined) {\n return {\n type: 'subagents.defaultModel',\n scope: 'project',\n path: projectSettingsPath,\n model: projectSettings.defaultModel,\n };\n }\n return userSettings.defaultModel !== undefined\n ? { type: 'subagents.defaultModel', scope: 'user', path: userSettingsPath, model: userSettings.defaultModel }\n : undefined;\n}\n\n/**\n * Fill in the default model for agents that declared none.\n *\n * `modelSource` is recorded alongside so callers can tell an inherited model\n * from a requested one, which decides how hard model-scope enforcement bites.\n */\nexport function applySubagentDefaultModel(\n agents: AgentConfig[],\n defaultModel: AgentModelSourceInfo | undefined,\n): AgentConfig[] {\n if (!defaultModel) return agents;\n return agents.map((agent) => {\n if (agent.model !== undefined) return agent;\n return inheritFrontmatterFields(agent, { ...agent, model: defaultModel.model, modelSource: defaultModel });\n });\n}\n\n/** Fill in fallback models only when the agent declared no fallback policy. */\nexport function applySubagentDefaultFallbackModels(\n agents: AgentConfig[],\n fallbackModels: string[] | undefined,\n): AgentConfig[] {\n if (fallbackModels === undefined) return agents;\n return agents.map((agent) => {\n if (agent.fallbackModels !== undefined) return agent;\n return inheritFrontmatterFields(agent, { ...agent, fallbackModels: [...fallbackModels] });\n });\n}\n\nexport function resolveSubagentDefaultThinking(\n userSettings: SubagentSettings,\n projectSettings: SubagentSettings,\n projectSettingsPath: string | null,\n): string | undefined {\n if (projectSettingsPath && projectSettings.defaultThinking !== undefined) return projectSettings.defaultThinking;\n return userSettings.defaultThinking;\n}\n\nexport function applySubagentDefaultThinking(\n agents: AgentConfig[],\n defaultThinking: string | undefined,\n): AgentConfig[] {\n if (defaultThinking === undefined) return agents;\n return agents.map((agent) => {\n if (agent.thinking !== undefined) return agent;\n return inheritFrontmatterFields(agent, { ...agent, thinking: defaultThinking });\n });\n}\n\nexport function resolveSubagentDefaultExtensions(\n userSettings: SubagentSettings,\n projectSettings: SubagentSettings,\n projectSettingsPath: string | null,\n): string[] | undefined {\n if (projectSettingsPath && projectSettings.defaultExtensions !== undefined) return projectSettings.defaultExtensions;\n return userSettings.defaultExtensions;\n}\n\n/**\n * Fill in default extensions for agents that declared none.\n *\n * The `extensionsFromDefault` flag matters when the agent is later overridden:\n * a snapshot must not record a settings default as if the builtin had declared\n * it, or removing the override would bake the default in permanently.\n */\nexport function applySubagentDefaultExtensions(\n agents: AgentConfig[],\n defaultExtensions: string[] | undefined,\n): AgentConfig[] {\n if (defaultExtensions === undefined) return agents;\n return agents.map((agent) => {\n if (agent.extensions !== undefined) return agent;\n return inheritFrontmatterFields(agent, {\n ...agent,\n extensions: [...defaultExtensions],\n extensionsFromDefault: true,\n });\n });\n}\n\nexport function applySubagentDefaults(\n agents: AgentConfig[],\n defaultModel: AgentModelSourceInfo | undefined,\n defaultThinking: string | undefined,\n defaultExtensions: string[] | undefined,\n defaultFallbackModels?: string[],\n): AgentConfig[] {\n return applySubagentDefaultExtensions(\n applySubagentDefaultThinking(\n applySubagentDefaultFallbackModels(applySubagentDefaultModel(agents, defaultModel), defaultFallbackModels),\n defaultThinking,\n ),\n defaultExtensions,\n );\n}\n\n// ============================================================================\n// Override snapshots\n// ============================================================================\n\n/**\n * Snapshot the fields an override may replace, so it can be undone.\n *\n * `extensions` is omitted when it came from a settings default rather than the\n * agent itself; restoring it would turn a default into a permanent value.\n */\nexport function cloneOverrideBase(agent: AgentConfig): BuiltinOverrideSnapshot {\n return {\n model: agent.model,\n fallbackModels: agent.fallbackModels ? [...agent.fallbackModels] : undefined,\n thinking: agent.thinking,\n systemPromptMode: agent.systemPromptMode,\n inheritProjectContext: agent.inheritProjectContext,\n inheritSkills: agent.inheritSkills,\n defaultContext: agent.defaultContext,\n disabled: agent.disabled,\n systemPrompt: agent.systemPrompt,\n skills: agent.skills ? [...agent.skills] : undefined,\n skillPath: agent.skillPath ? [...agent.skillPath] : undefined,\n tools: agent.tools ? [...agent.tools] : undefined,\n mcpDirectTools: agent.mcpDirectTools ? [...agent.mcpDirectTools] : undefined,\n extensions: agent.extensionsFromDefault ? undefined : agent.extensions ? [...agent.extensions] : undefined,\n subagentOnlyExtensions: agent.subagentOnlyExtensions ? [...agent.subagentOnlyExtensions] : undefined,\n completionGuard: agent.completionGuard,\n toolBudget: agent.toolBudget,\n };\n}\n\n/**\n * Deep-copy an override before it is written to a settings file.\n *\n * Only keys the caller actually set are copied, because an explicit `undefined`\n * would serialise the key away and read back as \"not overridden\".\n */\nexport function cloneOverrideValue(override: BuiltinAgentOverrideConfig): BuiltinAgentOverrideConfig {\n return {\n ...(override.model !== undefined ? { model: override.model } : {}),\n ...(override.fallbackModels !== undefined\n ? { fallbackModels: override.fallbackModels === false ? false : [...override.fallbackModels] }\n : {}),\n ...(override.thinking !== undefined ? { thinking: override.thinking } : {}),\n ...(override.systemPromptMode !== undefined ? { systemPromptMode: override.systemPromptMode } : {}),\n ...(override.inheritProjectContext !== undefined ? { inheritProjectContext: override.inheritProjectContext } : {}),\n ...(override.inheritSkills !== undefined ? { inheritSkills: override.inheritSkills } : {}),\n ...(override.defaultContext !== undefined ? { defaultContext: override.defaultContext } : {}),\n ...(override.disabled !== undefined ? { disabled: override.disabled } : {}),\n ...(override.systemPrompt !== undefined ? { systemPrompt: override.systemPrompt } : {}),\n ...(override.skills !== undefined ? { skills: override.skills === false ? false : [...override.skills] } : {}),\n ...(override.tools !== undefined ? { tools: override.tools === false ? false : [...override.tools] } : {}),\n ...(override.extensions !== undefined\n ? { extensions: override.extensions === false ? false : [...override.extensions] }\n : {}),\n ...(override.subagentOnlyExtensions !== undefined\n ? {\n subagentOnlyExtensions:\n override.subagentOnlyExtensions === false ? false : [...override.subagentOnlyExtensions],\n }\n : {}),\n ...(override.completionGuard !== undefined ? { completionGuard: override.completionGuard } : {}),\n ...(override.toolBudget !== undefined\n ? {\n toolBudget:\n override.toolBudget === false\n ? false\n : {\n ...override.toolBudget,\n ...(Array.isArray(override.toolBudget.block) ? { block: [...override.toolBudget.block] } : {}),\n },\n }\n : {}),\n };\n}\n\n// ============================================================================\n// Applying overrides\n// ============================================================================\n\n/**\n * Replace a builtin's values with an override's.\n *\n * `false` clears a field rather than setting it to false; that distinction is\n * the whole reason the stored shape differs from the snapshot shape.\n */\nexport function applyBuiltinOverride(\n agent: AgentConfig,\n override: BuiltinAgentOverrideConfig,\n meta: OverrideScopeMeta,\n): AgentConfig {\n const next: AgentConfig = { ...agent, override: { ...meta, base: cloneOverrideBase(agent) } };\n\n if (override.model !== undefined) next.model = override.model === false ? undefined : override.model;\n if (override.fallbackModels !== undefined) {\n next.fallbackModels = override.fallbackModels === false ? undefined : [...override.fallbackModels];\n }\n if (override.thinking !== undefined) next.thinking = override.thinking === false ? undefined : override.thinking;\n if (override.systemPromptMode !== undefined) next.systemPromptMode = override.systemPromptMode;\n if (override.inheritProjectContext !== undefined) next.inheritProjectContext = override.inheritProjectContext;\n if (override.inheritSkills !== undefined) next.inheritSkills = override.inheritSkills;\n if (override.defaultContext !== undefined) {\n next.defaultContext = override.defaultContext === false ? undefined : override.defaultContext;\n }\n if (override.disabled !== undefined) next.disabled = override.disabled;\n if (override.systemPrompt !== undefined) next.systemPrompt = override.systemPrompt;\n if (override.skills !== undefined) next.skills = override.skills === false ? undefined : [...override.skills];\n if (override.tools !== undefined) {\n const { tools, mcpDirectTools } = splitToolList(override.tools === false ? [] : override.tools);\n next.tools = tools;\n next.mcpDirectTools = mcpDirectTools;\n }\n if (override.extensions !== undefined) {\n next.extensions = override.extensions === false ? undefined : [...override.extensions];\n }\n if (override.subagentOnlyExtensions !== undefined) {\n next.subagentOnlyExtensions =\n override.subagentOnlyExtensions === false ? undefined : [...override.subagentOnlyExtensions];\n }\n if (override.completionGuard !== undefined) next.completionGuard = override.completionGuard;\n if (override.toolBudget !== undefined) {\n next.toolBudget = override.toolBudget === false ? undefined : override.toolBudget;\n }\n\n return next;\n}\n\n/**\n * Strip an agent's thinking level for the bulk `disableThinking` switch.\n *\n * An existing `override` is kept rather than replaced, so a per-agent override\n * remains the recorded reason the agent differs from its builtin definition.\n */\nexport function clearBuiltinThinking(agent: AgentConfig, meta: OverrideScopeMeta): AgentConfig {\n if (agent.thinking === undefined) return agent;\n return { ...agent, thinking: undefined, override: agent.override ?? { ...meta, base: cloneOverrideBase(agent) } };\n}\n\n/**\n * Apply user and project settings to the builtin agents.\n *\n * Precedence per agent: a project override, then the project bulk disable, then\n * a user override, then the user bulk disable. A project file that sets\n * `disableBuiltins` at all (including to false) shadows the user's switch, so a\n * project can re-enable builtins its user settings turned off.\n */\nexport function applyBuiltinOverrides(\n builtinAgents: AgentConfig[],\n userSettings: SubagentSettings,\n projectSettings: SubagentSettings,\n userSettingsPath: string,\n projectSettingsPath: string | null,\n): AgentConfig[] {\n const projectBulkDisabled = projectSettings.disableBuiltins === true && projectSettingsPath !== null;\n const userBulkDisabled = projectSettings.disableBuiltins === undefined && userSettings.disableBuiltins === true;\n const projectThinkingConfigured = projectSettings.disableThinking !== undefined && projectSettingsPath !== null;\n const disableThinking = projectThinkingConfigured\n ? projectSettings.disableThinking === true\n : userSettings.disableThinking === true;\n const disableThinkingMeta: OverrideScopeMeta =\n projectThinkingConfigured && projectSettingsPath\n ? { scope: 'project', path: projectSettingsPath }\n : { scope: 'user', path: userSettingsPath };\n\n // A per-agent thinking override is more specific than the bulk switch, so it\n // survives it; everything else is cleared.\n const applyGlobalThinking = (agent: AgentConfig, hasExplicitThinkingOverride: boolean): AgentConfig => {\n if (!disableThinking || hasExplicitThinkingOverride) return agent;\n return clearBuiltinThinking(agent, disableThinkingMeta);\n };\n\n return builtinAgents.map((agent) => {\n const projectOverride = projectSettings.overrides[agent.name];\n if (projectOverride && projectSettingsPath) {\n return applyGlobalThinking(\n applyBuiltinOverride(agent, projectOverride, { scope: 'project', path: projectSettingsPath }),\n projectOverride.thinking !== undefined,\n );\n }\n\n if (projectBulkDisabled && projectSettingsPath) {\n return applyGlobalThinking(\n applyBuiltinOverride(agent, { disabled: true }, { scope: 'project', path: projectSettingsPath }),\n false,\n );\n }\n\n const userOverride = userSettings.overrides[agent.name];\n if (userOverride) {\n return applyGlobalThinking(\n applyBuiltinOverride(agent, userOverride, { scope: 'user', path: userSettingsPath }),\n !projectThinkingConfigured && userOverride.thinking !== undefined,\n );\n }\n\n if (userBulkDisabled) {\n return applyGlobalThinking(\n applyBuiltinOverride(agent, { disabled: true }, { scope: 'user', path: userSettingsPath }),\n false,\n );\n }\n\n return applyGlobalThinking(agent, false);\n });\n}\n\n/**\n * Apply an override to a custom agent, filling only unset fields.\n *\n * A custom agent has a file the user could have edited, so a settings value\n * must never contradict its frontmatter. Nothing is copied and no `override`\n * is recorded unless at least one field was actually filled, which keeps agents\n * that declared everything from being reported as overridden.\n */\nexport function applyCustomAgentOverride(\n agent: AgentConfig,\n override: BuiltinAgentOverrideConfig,\n meta: OverrideScopeMeta,\n): AgentConfig {\n let next: AgentConfig | undefined;\n let anyFilled = false;\n\n const mutable = (): AgentConfig => {\n next ??= { ...agent };\n return next;\n };\n\n const fill = <K extends keyof AgentConfig>(field: K, frontmatterFields: string[], value: AgentConfig[K]): void => {\n if (agentHasFrontmatterField(agent, ...frontmatterFields)) return;\n mutable()[field] = value;\n anyFilled = true;\n };\n\n if (override.model !== undefined) {\n fill('model', ['model'], override.model === false ? undefined : override.model);\n }\n if (override.fallbackModels !== undefined) {\n fill(\n 'fallbackModels',\n ['fallbackModels'],\n override.fallbackModels === false ? undefined : [...override.fallbackModels],\n );\n }\n if (override.thinking !== undefined) {\n fill('thinking', ['thinking'], override.thinking === false ? undefined : override.thinking);\n }\n if (override.systemPromptMode !== undefined) {\n fill('systemPromptMode', ['systemPromptMode'], override.systemPromptMode);\n }\n if (override.inheritProjectContext !== undefined) {\n fill('inheritProjectContext', ['inheritProjectContext'], override.inheritProjectContext);\n }\n if (override.inheritSkills !== undefined) {\n fill('inheritSkills', ['inheritSkills'], override.inheritSkills);\n }\n if (override.defaultContext !== undefined) {\n fill('defaultContext', ['defaultContext'], override.defaultContext === false ? undefined : override.defaultContext);\n }\n if (override.disabled !== undefined && agent.disabled === undefined) {\n mutable().disabled = override.disabled;\n anyFilled = true;\n }\n if (override.skills !== undefined) {\n fill('skills', ['skill', 'skills'], override.skills === false ? undefined : [...override.skills]);\n }\n if (override.tools !== undefined && !agentHasFrontmatterField(agent, 'tools')) {\n const { tools, mcpDirectTools } = splitToolList(override.tools === false ? [] : override.tools);\n const target = mutable();\n target.tools = tools;\n target.mcpDirectTools = mcpDirectTools;\n anyFilled = true;\n }\n if (override.extensions !== undefined) {\n fill('extensions', ['extensions'], override.extensions === false ? undefined : [...override.extensions]);\n }\n if (override.subagentOnlyExtensions !== undefined) {\n fill(\n 'subagentOnlyExtensions',\n ['subagentOnlyExtensions'],\n override.subagentOnlyExtensions === false ? undefined : [...override.subagentOnlyExtensions],\n );\n }\n if (override.completionGuard !== undefined) {\n fill('completionGuard', ['completionGuard'], override.completionGuard);\n }\n if (override.toolBudget !== undefined) {\n fill('toolBudget', ['toolBudget'], override.toolBudget === false ? undefined : override.toolBudget);\n }\n\n if (!anyFilled || !next) return agent;\n next.override = { ...meta, base: cloneOverrideBase(agent) };\n return inheritFrontmatterFields(agent, next);\n}\n\nexport function applyCustomAgentOverrides(\n agents: AgentConfig[],\n userSettings: SubagentSettings,\n projectSettings: SubagentSettings,\n userSettingsPath: string,\n projectSettingsPath: string | null,\n): AgentConfig[] {\n return agents.map((agent) => {\n const projectOverride = projectSettings.overrides[agent.name];\n if (projectOverride && projectSettingsPath) {\n return applyCustomAgentOverride(agent, projectOverride, { scope: 'project', path: projectSettingsPath });\n }\n\n const userOverride = userSettings.overrides[agent.name];\n if (userOverride) {\n return applyCustomAgentOverride(agent, userOverride, { scope: 'user', path: userSettingsPath });\n }\n\n return agent;\n });\n}\n\n/**\n * Diff an edited agent against its pre-override snapshot.\n *\n * Only changed fields are written, so a settings file records the user's edits\n * rather than a full copy of the builtin that would drift as the builtin does.\n * Returns undefined when nothing changed, which callers treat as \"remove the\n * override\" rather than \"write an empty one\".\n */\nexport function buildBuiltinOverrideConfig(\n base: BuiltinOverrideSnapshot,\n draft: Pick<\n AgentConfig,\n | 'model'\n | 'fallbackModels'\n | 'thinking'\n | 'systemPromptMode'\n | 'inheritProjectContext'\n | 'inheritSkills'\n | 'defaultContext'\n | 'disabled'\n | 'systemPrompt'\n | 'skills'\n | 'tools'\n | 'mcpDirectTools'\n | 'extensions'\n | 'subagentOnlyExtensions'\n | 'completionGuard'\n | 'toolBudget'\n >,\n): BuiltinAgentOverrideConfig | undefined {\n const override: BuiltinAgentOverrideConfig = {};\n\n if (draft.model !== base.model) override.model = draft.model ?? false;\n if (!arraysEqual(draft.fallbackModels, base.fallbackModels)) {\n override.fallbackModels = draft.fallbackModels ? [...draft.fallbackModels] : false;\n }\n if (draft.thinking !== base.thinking) override.thinking = draft.thinking ?? false;\n if (draft.systemPromptMode !== base.systemPromptMode) override.systemPromptMode = draft.systemPromptMode;\n if (draft.inheritProjectContext !== base.inheritProjectContext) {\n override.inheritProjectContext = draft.inheritProjectContext;\n }\n if (draft.inheritSkills !== base.inheritSkills) override.inheritSkills = draft.inheritSkills;\n if (draft.defaultContext !== base.defaultContext) override.defaultContext = draft.defaultContext ?? false;\n if (draft.disabled !== base.disabled) override.disabled = draft.disabled ?? false;\n if (draft.systemPrompt !== base.systemPrompt) override.systemPrompt = draft.systemPrompt;\n if (!arraysEqual(draft.skills, base.skills)) override.skills = draft.skills ? [...draft.skills] : false;\n\n const baseTools = joinToolList(base);\n const draftTools = joinToolList(draft);\n if (!arraysEqual(draftTools, baseTools)) override.tools = draftTools ? [...draftTools] : false;\n if (!arraysEqual(draft.extensions, base.extensions)) {\n override.extensions = draft.extensions ? [...draft.extensions] : false;\n }\n if (!arraysEqual(draft.subagentOnlyExtensions, base.subagentOnlyExtensions)) {\n override.subagentOnlyExtensions = draft.subagentOnlyExtensions ? [...draft.subagentOnlyExtensions] : false;\n }\n // Compared as booleans because an unset guard and an enabled one behave the\n // same, so only a switch to or from explicitly disabled is a real change.\n if ((draft.completionGuard !== false) !== (base.completionGuard !== false)) {\n override.completionGuard = draft.completionGuard !== false;\n }\n if (JSON.stringify(draft.toolBudget) !== JSON.stringify(base.toolBudget)) {\n override.toolBudget = draft.toolBudget ?? false;\n }\n\n return Object.keys(override).length > 0 ? override : undefined;\n}\n\n// ============================================================================\n// Persisting overrides\n// ============================================================================\n\nconst NO_PROJECT_ROOT_MESSAGE = 'Project override is not available here. No project config root was found.';\n\n/** Resolve the settings file for a scope, or throw when there is no project. */\nfunction requireSettingsPath(cwd: string, scope: 'user' | 'project'): string {\n const filePath = scope === 'project' ? getProjectAgentSettingsPath(cwd) : getUserAgentSettingsPath();\n if (!filePath) throw new Error(NO_PROJECT_ROOT_MESSAGE);\n return filePath;\n}\n\n/** Write an override, replacing any existing entry for the agent wholesale. */\nexport function saveBuiltinAgentOverride(\n cwd: string,\n name: string,\n scope: 'user' | 'project',\n override: BuiltinAgentOverrideConfig,\n): string {\n const filePath = requireSettingsPath(cwd, scope);\n\n const settings = readSettingsFileStrict(filePath);\n const subagents = cloneObjectField(settings, SUBAGENTS_SETTINGS_KEY);\n const agentOverrides = cloneObjectField(subagents, AGENT_OVERRIDES_KEY);\n\n agentOverrides[name] = cloneOverrideValue(override);\n subagents[AGENT_OVERRIDES_KEY] = agentOverrides;\n settings[SUBAGENTS_SETTINGS_KEY] = subagents;\n writeSettingsFile(filePath, settings);\n return filePath;\n}\n\n/**\n * Drop an agent's whole override entry.\n *\n * Emptied containers are deleted rather than left behind, so removing the last\n * override leaves the settings file as it was before any were added.\n */\nexport function removeBuiltinAgentOverride(\n cwd: string,\n name: string,\n scope: 'user' | 'project',\n): { path: string; removed: boolean } {\n const filePath = requireSettingsPath(cwd, scope);\n if (!fs.existsSync(filePath)) return { path: filePath, removed: false };\n\n const settings = readSettingsFileStrict(filePath);\n const subagents = readObjectField(settings, SUBAGENTS_SETTINGS_KEY);\n if (!subagents) return { path: filePath, removed: false };\n const agentOverrides = readObjectField(subagents, AGENT_OVERRIDES_KEY);\n if (!agentOverrides) return { path: filePath, removed: false };\n if (!Object.hasOwn(agentOverrides, name)) return { path: filePath, removed: false };\n\n const nextSubagents = { ...subagents };\n const nextOverrides = { ...agentOverrides };\n delete nextOverrides[name];\n if (Object.keys(nextOverrides).length > 0) nextSubagents[AGENT_OVERRIDES_KEY] = nextOverrides;\n else delete nextSubagents[AGENT_OVERRIDES_KEY];\n\n if (Object.keys(nextSubagents).length > 0) settings[SUBAGENTS_SETTINGS_KEY] = nextSubagents;\n else delete settings[SUBAGENTS_SETTINGS_KEY];\n\n writeSettingsFile(filePath, settings);\n return { path: filePath, removed: true };\n}\n\n/** Merge fields into an agent's existing override, leaving the rest in place. */\nexport function mergeBuiltinAgentOverride(\n cwd: string,\n name: string,\n scope: 'user' | 'project',\n fields: BuiltinAgentOverrideConfig,\n): string {\n const filePath = requireSettingsPath(cwd, scope);\n\n const settings = readSettingsFileStrict(filePath);\n const subagents = cloneObjectField(settings, SUBAGENTS_SETTINGS_KEY);\n const agentOverrides = cloneObjectField(subagents, AGENT_OVERRIDES_KEY);\n\n const existing = readObjectField(agentOverrides, name) ?? {};\n agentOverrides[name] = { ...existing, ...cloneOverrideValue(fields) };\n subagents[AGENT_OVERRIDES_KEY] = agentOverrides;\n settings[SUBAGENTS_SETTINGS_KEY] = subagents;\n writeSettingsFile(filePath, settings);\n return filePath;\n}\n\n/** Remove named fields from an agent's override, dropping it once empty. */\nexport function removeBuiltinAgentOverrideFields(\n cwd: string,\n name: string,\n scope: 'user' | 'project',\n fields: string[],\n): { path: string; removed: boolean } {\n const filePath = requireSettingsPath(cwd, scope);\n if (!fs.existsSync(filePath)) return { path: filePath, removed: false };\n\n const settings = readSettingsFileStrict(filePath);\n const subagents = readObjectField(settings, SUBAGENTS_SETTINGS_KEY);\n if (!subagents) return { path: filePath, removed: false };\n const agentOverrides = readObjectField(subagents, AGENT_OVERRIDES_KEY);\n if (!agentOverrides) return { path: filePath, removed: false };\n const entry = readObjectField(agentOverrides, name);\n if (!entry) return { path: filePath, removed: false };\n\n const nextEntry: Record<string, unknown> = { ...entry };\n let removed = false;\n for (const field of fields) {\n if (Object.hasOwn(nextEntry, field)) {\n delete nextEntry[field];\n removed = true;\n }\n }\n if (!removed) return { path: filePath, removed: false };\n\n const nextSubagents = { ...subagents };\n const nextOverrides = { ...agentOverrides };\n if (Object.keys(nextEntry).length > 0) {\n nextOverrides[name] = nextEntry;\n nextSubagents[AGENT_OVERRIDES_KEY] = nextOverrides;\n } else {\n delete nextOverrides[name];\n if (Object.keys(nextOverrides).length > 0) nextSubagents[AGENT_OVERRIDES_KEY] = nextOverrides;\n else delete nextSubagents[AGENT_OVERRIDES_KEY];\n }\n if (Object.keys(nextSubagents).length > 0) settings[SUBAGENTS_SETTINGS_KEY] = nextSubagents;\n else delete settings[SUBAGENTS_SETTINGS_KEY];\n\n writeSettingsFile(filePath, settings);\n return { path: filePath, removed: true };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"settings.cjs","names":["readSettingsFileStrict"],"sources":["../../src/agents/settings.ts"],"sourcesContent":["/**\n * Agent settings: reading them, and applying what they say to discovered agents.\n *\n * Two things live here because they are the same contract seen from both ends.\n * Reading turns untyped JSON on disk into settings; applying folds those\n * settings over the agents discovery found. The precedence is fixed: project\n * settings beat user settings, an explicit per-agent override beats a bulk\n * switch, and for a custom agent the agent's own frontmatter beats both.\n *\n * That last rule is why builtins and custom agents take different paths.\n * Overriding a builtin replaces values outright, because a builtin has no file\n * the user could have edited instead. Overriding a custom agent only fills\n * fields its frontmatter left unset, so a settings default never silently\n * contradicts what the author wrote in the file.\n *\n * DESIGN PATTERNS:\n * - Reading is strict and throws with the offending path and field. A settings\n * file that is quietly half-ignored is worse than one that refuses to load,\n * because the user sees agents behaving in ways their config does not explain\n * - Applying is pure and copy-on-write. Every `apply*` returns new configs and\n * never mutates its input, so a cached discovery result cannot be corrupted\n * by a later pass over it\n * - Overriding a builtin snapshots the values it replaced into `override.base`,\n * which is what makes removing the override restore the builtin exactly\n *\n * AVOID:\n * - Trusting any shape read off disk without a real guard\n * - Mutating an `AgentConfig` in place\n * - Writing a settings file non-atomically; it holds the user's overrides\n */\n\nimport * as fs from 'node:fs';\nimport { writeAtomicJson } from '../shared/atomicJson.ts';\nimport { readSettingsFileStrict } from '../shared/configDir.ts';\nimport type { ModelScopeConfig, ToolBudgetConfig } from '../types.ts';\nimport { getProjectAgentSettingsPath, getUserAgentSettingsPath } from './projectRoot.ts';\nexport { readSettingsFileStrict } from '../shared/configDir.ts';\nimport type {\n AgentConfig,\n AgentModelSourceInfo,\n BuiltinAgentOverrideBase,\n BuiltinAgentOverrideConfig,\n SubagentSettings,\n} from './types.ts';\n\nconst SUBAGENTS_SETTINGS_KEY = 'subagents';\nconst AGENT_OVERRIDES_KEY = 'agentOverrides';\n/** Prefix marking a tool the child calls on an MCP server directly. */\nconst MCP_DIRECT_TOOL_PREFIX = 'mcp:';\n\n/** Which settings file an override came from, without the value snapshot. */\ntype OverrideScopeMeta = { scope: 'user' | 'project'; path: string };\n\n/**\n * The snapshot of a builtin's pre-override values.\n *\n * Widens the contract with `mcpDirectTools` because a builtin's tool list is\n * stored split in `AgentConfig` and must round-trip through the snapshot to be\n * restorable. See the report note on `BuiltinAgentOverrideBase`.\n */\nexport interface BuiltinOverrideSnapshot extends BuiltinAgentOverrideBase {\n mcpDirectTools?: string[];\n}\n\n/**\n * Returned whenever there is nothing to read.\n *\n * Shared rather than freshly built per call because it is returned on the hot\n * discovery path for every session with no project settings file.\n */\nexport const EMPTY_SUBAGENT_SETTINGS: SubagentSettings = { overrides: {} };\n\n/**\n * Which fields of a config came from its file's frontmatter.\n *\n * Keyed by the config object rather than by agent name because configs are\n * copied on write: each `apply*` pass produces new objects, and the new one\n * inherits the entry so later passes still know what the author declared.\n * A WeakMap means a discarded config's entry is collectable with it.\n */\nconst agentFrontmatterFields = new WeakMap<AgentConfig, Set<string>>();\n\n/** Record which fields a freshly parsed config took from frontmatter. */\nexport function setAgentFrontmatterFields(agent: AgentConfig, fields: Set<string>): void {\n agentFrontmatterFields.set(agent, fields);\n}\n\n/** True when the agent's file declared any of `fields` in its frontmatter. */\nexport function agentHasFrontmatterField(agent: AgentConfig, ...fields: string[]): boolean {\n const frontmatterFields = agentFrontmatterFields.get(agent);\n return frontmatterFields ? fields.some((field) => frontmatterFields.has(field)) : false;\n}\n\n/** Carry the frontmatter record onto a copy produced by an `apply*` pass. */\nfunction inheritFrontmatterFields(source: AgentConfig, next: AgentConfig): AgentConfig {\n const frontmatterFields = agentFrontmatterFields.get(source);\n if (frontmatterFields) agentFrontmatterFields.set(next, frontmatterFields);\n return next;\n}\n\n/** Split a flat tool list into host tools and direct MCP tools. */\nfunction splitToolList(rawTools: string[] | undefined): { tools?: string[]; mcpDirectTools?: string[] } {\n const mcpDirectTools: string[] = [];\n const tools: string[] = [];\n for (const tool of rawTools ?? []) {\n if (tool.startsWith(MCP_DIRECT_TOOL_PREFIX)) mcpDirectTools.push(tool.slice(MCP_DIRECT_TOOL_PREFIX.length));\n else tools.push(tool);\n }\n return {\n ...(rawTools !== undefined ? { tools } : {}),\n ...(mcpDirectTools.length > 0 ? { mcpDirectTools } : {}),\n };\n}\n\n/** Inverse of `splitToolList`, for writing a tool list back to settings. */\nfunction joinToolList(config: { tools?: string[]; mcpDirectTools?: string[] }): string[] | undefined {\n const joined = [\n ...(config.tools ?? []),\n ...(config.mcpDirectTools ?? []).map((tool) => `${MCP_DIRECT_TOOL_PREFIX}${tool}`),\n ];\n return joined.length > 0 ? joined : undefined;\n}\n\n/** Order-sensitive equality; a reordered tool list is a real change. */\nfunction arraysEqual(a: string[] | undefined, b: string[] | undefined): boolean {\n if (!a && !b) return true;\n if (!a || !b) return false;\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) {\n if (a[i] !== b[i]) return false;\n }\n return true;\n}\n\n// ============================================================================\n// Reading\n// ============================================================================\n\n/**\n * Write a settings file atomically.\n *\n * This file holds every override the user configured. A partial write from a\n * crash mid-save would be read back as malformed on the next start and, because\n * reading is strict, would refuse to load at all.\n */\nexport function writeSettingsFile(filePath: string, settings: Record<string, unknown>): void {\n writeAtomicJson(filePath, settings);\n}\n\n/** Read a settings object at `key`, or undefined when it is absent or not an object. */\nfunction readObjectField(source: Record<string, unknown>, key: string): Record<string, unknown> | undefined {\n const value = source[key];\n if (!value || typeof value !== 'object' || Array.isArray(value)) return undefined;\n return value as Record<string, unknown>;\n}\n\n/** Copy a settings object at `key`, or start a fresh one when it is absent. */\nfunction cloneObjectField(source: Record<string, unknown>, key: string): Record<string, unknown> {\n // Spreading undefined is already a no-op, so no empty-object fallback is needed.\n return { ...readObjectField(source, key) };\n}\n\n/**\n * Parse the model-scope block.\n *\n * Lives here rather than beside the scope enforcement logic because parsing is\n * a settings concern and the agents domain may not reach into the runs domain.\n */\nfunction parseModelScopeConfig(value: unknown, filePath: string): ModelScopeConfig | undefined {\n if (value === undefined) return undefined;\n if (!value || typeof value !== 'object' || Array.isArray(value)) {\n throw new Error(`Subagent settings in '${filePath}' have invalid 'modelScope'; expected an object.`);\n }\n\n const input = value as Record<string, unknown>;\n const config: ModelScopeConfig = {};\n\n if ('enforce' in input) {\n if (typeof input.enforce !== 'boolean') {\n throw new Error(`Subagent settings in '${filePath}' have invalid 'modelScope.enforce'; expected a boolean.`);\n }\n config.enforce = input.enforce;\n }\n\n if ('allow' in input) {\n if (!Array.isArray(input.allow)) {\n throw new Error(\n `Subagent settings in '${filePath}' have invalid 'modelScope.allow'; expected an array of strings.`,\n );\n }\n const allow: string[] = [];\n for (const entry of input.allow) {\n if (typeof entry !== 'string') {\n throw new Error(\n `Subagent settings in '${filePath}' have invalid 'modelScope.allow'; expected an array of strings.`,\n );\n }\n const trimmed = entry.trim();\n if (trimmed) allow.push(trimmed);\n }\n if (allow.length === 0) {\n throw new Error(\n `Subagent settings in '${filePath}' have invalid 'modelScope.allow'; expected a non-empty array of patterns.`,\n );\n }\n config.allow = allow;\n }\n\n // Enforcing against an empty allow list would reject every model, so it is\n // rejected here rather than at the point it would start failing runs.\n if (config.enforce === true && (!config.allow || config.allow.length === 0)) {\n throw new Error(\n `Subagent settings in '${filePath}' set modelScope.enforce without a non-empty 'allow' list; supply allowed model patterns or disable enforcement.`,\n );\n }\n\n return Object.keys(config).length > 0 ? config : undefined;\n}\n\n/**\n * Parse a string-array override field, which may also be `false` to clear it.\n *\n * Empty entries are dropped rather than rejected: trailing commas in a hand\n * edited list are common and unambiguous.\n */\nexport function parseOverrideStringArrayOrFalse(\n value: unknown,\n meta: { filePath: string; name: string; field: string },\n): string[] | false | undefined {\n if (value === undefined) return undefined;\n if (value === false) return false;\n if (!Array.isArray(value)) {\n throw new Error(\n `Builtin override '${meta.name}' in '${meta.filePath}' has invalid '${meta.field}'; expected an array of strings or false.`,\n );\n }\n\n const items: string[] = [];\n for (const item of value) {\n if (typeof item !== 'string') {\n throw new Error(\n `Builtin override '${meta.name}' in '${meta.filePath}' has invalid '${meta.field}'; expected an array of strings or false.`,\n );\n }\n const trimmed = item.trim();\n if (trimmed) items.push(trimmed);\n }\n return items;\n}\n\n/**\n * Parse one entry of `subagents.agentOverrides`.\n *\n * Returns undefined for an entry that declared nothing, so an empty object in\n * the file does not register as an override and mark the agent as overridden.\n */\nexport function parseBuiltinOverrideEntry(\n name: string,\n value: unknown,\n filePath: string,\n): BuiltinAgentOverrideConfig | undefined {\n if (!value || typeof value !== 'object' || Array.isArray(value)) {\n throw new Error(`Builtin override '${name}' in '${filePath}' must be an object.`);\n }\n\n const input = value as Record<string, unknown>;\n const override: BuiltinAgentOverrideConfig = {};\n\n if ('model' in input) {\n if (typeof input.model === 'string' || input.model === false) override.model = input.model;\n else\n throw new Error(`Builtin override '${name}' in '${filePath}' has invalid 'model'; expected a string or false.`);\n }\n\n if ('thinking' in input) {\n if (typeof input.thinking === 'string' || input.thinking === false) override.thinking = input.thinking;\n else\n throw new Error(\n `Builtin override '${name}' in '${filePath}' has invalid 'thinking'; expected a string or false.`,\n );\n }\n\n if ('systemPromptMode' in input) {\n if (input.systemPromptMode === 'append' || input.systemPromptMode === 'replace') {\n override.systemPromptMode = input.systemPromptMode;\n } else {\n throw new Error(\n `Builtin override '${name}' in '${filePath}' has invalid 'systemPromptMode'; expected 'append' or 'replace'.`,\n );\n }\n }\n\n if ('inheritProjectContext' in input) {\n if (typeof input.inheritProjectContext === 'boolean') {\n override.inheritProjectContext = input.inheritProjectContext;\n } else {\n throw new Error(\n `Builtin override '${name}' in '${filePath}' has invalid 'inheritProjectContext'; expected a boolean.`,\n );\n }\n }\n\n if ('inheritSkills' in input) {\n if (typeof input.inheritSkills === 'boolean') {\n override.inheritSkills = input.inheritSkills;\n } else {\n throw new Error(`Builtin override '${name}' in '${filePath}' has invalid 'inheritSkills'; expected a boolean.`);\n }\n }\n\n if ('defaultContext' in input) {\n if (input.defaultContext === 'fresh' || input.defaultContext === 'fork' || input.defaultContext === false) {\n override.defaultContext = input.defaultContext;\n } else {\n throw new Error(\n `Builtin override '${name}' in '${filePath}' has invalid 'defaultContext'; expected 'fresh', 'fork', or false.`,\n );\n }\n }\n\n if ('disabled' in input) {\n if (typeof input.disabled === 'boolean') {\n override.disabled = input.disabled;\n } else {\n throw new Error(`Builtin override '${name}' in '${filePath}' has invalid 'disabled'; expected a boolean.`);\n }\n }\n\n if ('completionGuard' in input) {\n if (typeof input.completionGuard === 'boolean') {\n override.completionGuard = input.completionGuard;\n } else {\n throw new Error(`Builtin override '${name}' in '${filePath}' has invalid 'completionGuard'; expected a boolean.`);\n }\n }\n\n if ('toolBudget' in input) {\n const toolBudget = parseToolBudgetOverride(input.toolBudget, name, filePath);\n if (toolBudget !== undefined) override.toolBudget = toolBudget;\n }\n\n if ('systemPrompt' in input) {\n if (typeof input.systemPrompt === 'string') override.systemPrompt = input.systemPrompt;\n else throw new Error(`Builtin override '${name}' in '${filePath}' has invalid 'systemPrompt'; expected a string.`);\n }\n\n const parseArrayField = (field: string): string[] | false | undefined =>\n parseOverrideStringArrayOrFalse(input[field], { filePath, name, field });\n\n const fallbackModels = parseArrayField('fallbackModels');\n if (fallbackModels !== undefined) override.fallbackModels = fallbackModels;\n const skills = parseArrayField('skills');\n if (skills !== undefined) override.skills = skills;\n const tools = parseArrayField('tools');\n if (tools !== undefined) override.tools = tools;\n const extensions = parseArrayField('extensions');\n if (extensions !== undefined) override.extensions = extensions;\n const subagentOnlyExtensions = parseArrayField('subagentOnlyExtensions');\n if (subagentOnlyExtensions !== undefined) override.subagentOnlyExtensions = subagentOnlyExtensions;\n\n return Object.keys(override).length > 0 ? override : undefined;\n}\n\n/**\n * Parse a `toolBudget` override.\n *\n * `hard` is required by the contract, so an object without a usable one is\n * rejected here instead of producing a budget that never blocks.\n */\nfunction parseToolBudgetOverride(value: unknown, name: string, filePath: string): ToolBudgetConfig | false | undefined {\n if (value === false) return false;\n if (!value || typeof value !== 'object' || Array.isArray(value)) {\n throw new Error(\n `Builtin override '${name}' in '${filePath}' has invalid 'toolBudget'; expected an object or false.`,\n );\n }\n\n const input = value as Record<string, unknown>;\n if (typeof input.hard !== 'number') {\n throw new Error(`Builtin override '${name}' in '${filePath}' has invalid 'toolBudget.hard'; expected a number.`);\n }\n\n const budget: ToolBudgetConfig = { hard: input.hard };\n if ('soft' in input) {\n if (typeof input.soft !== 'number') {\n throw new Error(`Builtin override '${name}' in '${filePath}' has invalid 'toolBudget.soft'; expected a number.`);\n }\n budget.soft = input.soft;\n }\n if ('block' in input) {\n if (input.block === '*') {\n budget.block = '*';\n } else if (Array.isArray(input.block) && input.block.every((entry) => typeof entry === 'string')) {\n budget.block = [...input.block];\n } else {\n throw new Error(\n `Builtin override '${name}' in '${filePath}' has invalid 'toolBudget.block'; expected an array of strings or '*'.`,\n );\n }\n }\n return budget;\n}\n\n/** Read and validate one settings file's `subagents` block. */\nexport function readSubagentSettings(filePath: string | null): SubagentSettings {\n if (!filePath) return EMPTY_SUBAGENT_SETTINGS;\n const settings = readSettingsFileStrict(filePath);\n const subagentsObject = readObjectField(settings, SUBAGENTS_SETTINGS_KEY);\n if (!subagentsObject) return EMPTY_SUBAGENT_SETTINGS;\n\n let disableBuiltins: boolean | undefined;\n if ('disableBuiltins' in subagentsObject) {\n if (typeof subagentsObject.disableBuiltins === 'boolean') {\n disableBuiltins = subagentsObject.disableBuiltins;\n } else {\n throw new Error(`Subagent settings in '${filePath}' have invalid 'disableBuiltins'; expected a boolean.`);\n }\n }\n\n let disableThinking: boolean | undefined;\n if ('disableThinking' in subagentsObject) {\n if (typeof subagentsObject.disableThinking === 'boolean') {\n disableThinking = subagentsObject.disableThinking;\n } else {\n throw new Error(`Subagent settings in '${filePath}' have invalid 'disableThinking'; expected a boolean.`);\n }\n }\n\n let defaultModel: string | undefined;\n if ('defaultModel' in subagentsObject) {\n if (typeof subagentsObject.defaultModel === 'string' && subagentsObject.defaultModel.trim()) {\n defaultModel = subagentsObject.defaultModel.trim();\n } else {\n throw new Error(`Subagent settings in '${filePath}' have invalid 'defaultModel'; expected a non-empty string.`);\n }\n }\n\n let defaultThinking: string | undefined;\n if ('defaultThinking' in subagentsObject) {\n if (typeof subagentsObject.defaultThinking === 'string' && subagentsObject.defaultThinking.trim()) {\n defaultThinking = subagentsObject.defaultThinking.trim();\n } else {\n throw new Error(\n `Subagent settings in '${filePath}' have invalid 'defaultThinking'; expected a non-empty string.`,\n );\n }\n }\n\n let defaultExtensions: string[] | undefined;\n if ('defaultExtensions' in subagentsObject) {\n const raw = subagentsObject.defaultExtensions;\n if (!Array.isArray(raw) || raw.some((item) => typeof item !== 'string' || !item.trim())) {\n throw new Error(\n `Subagent settings in '${filePath}' have invalid 'defaultExtensions'; expected an array of non-empty strings.`,\n );\n }\n defaultExtensions = raw.map((item) => String(item).trim());\n }\n\n const modelScope = parseModelScopeConfig(subagentsObject.modelScope, filePath);\n\n const overrides: Record<string, BuiltinAgentOverrideConfig> = {};\n const agentOverrides = readObjectField(subagentsObject, AGENT_OVERRIDES_KEY);\n if (agentOverrides) {\n for (const [name, value] of Object.entries(agentOverrides)) {\n const override = parseBuiltinOverrideEntry(name, value, filePath);\n if (override) overrides[name] = override;\n }\n }\n\n return { overrides, defaultModel, defaultThinking, defaultExtensions, disableBuiltins, disableThinking, modelScope };\n}\n\n// ============================================================================\n// Settings-level defaults\n// ============================================================================\n\n/** Which settings file supplies `defaultModel`, project winning over user. */\nexport function resolveSubagentDefaultModel(\n userSettings: SubagentSettings,\n projectSettings: SubagentSettings,\n userSettingsPath: string,\n projectSettingsPath: string | null,\n): AgentModelSourceInfo | undefined {\n if (projectSettingsPath && projectSettings.defaultModel !== undefined) {\n return {\n type: 'subagents.defaultModel',\n scope: 'project',\n path: projectSettingsPath,\n model: projectSettings.defaultModel,\n };\n }\n return userSettings.defaultModel !== undefined\n ? { type: 'subagents.defaultModel', scope: 'user', path: userSettingsPath, model: userSettings.defaultModel }\n : undefined;\n}\n\n/**\n * Fill in the default model for agents that declared none.\n *\n * `modelSource` is recorded alongside so callers can tell an inherited model\n * from a requested one, which decides how hard model-scope enforcement bites.\n */\nexport function applySubagentDefaultModel(\n agents: AgentConfig[],\n defaultModel: AgentModelSourceInfo | undefined,\n): AgentConfig[] {\n if (!defaultModel) return agents;\n return agents.map((agent) => {\n if (agent.model !== undefined) return agent;\n return inheritFrontmatterFields(agent, { ...agent, model: defaultModel.model, modelSource: defaultModel });\n });\n}\n\n/** Fill in fallback models only when the agent declared no fallback policy. */\nexport function applySubagentDefaultFallbackModels(\n agents: AgentConfig[],\n fallbackModels: string[] | undefined,\n): AgentConfig[] {\n if (fallbackModels === undefined) return agents;\n return agents.map((agent) => {\n if (agent.fallbackModels !== undefined) return agent;\n return inheritFrontmatterFields(agent, { ...agent, fallbackModels: [...fallbackModels] });\n });\n}\n\nexport function resolveSubagentDefaultThinking(\n userSettings: SubagentSettings,\n projectSettings: SubagentSettings,\n projectSettingsPath: string | null,\n): string | undefined {\n if (projectSettingsPath && projectSettings.defaultThinking !== undefined) return projectSettings.defaultThinking;\n return userSettings.defaultThinking;\n}\n\nexport function applySubagentDefaultThinking(\n agents: AgentConfig[],\n defaultThinking: string | undefined,\n): AgentConfig[] {\n if (defaultThinking === undefined) return agents;\n return agents.map((agent) => {\n if (agent.thinking !== undefined) return agent;\n return inheritFrontmatterFields(agent, { ...agent, thinking: defaultThinking });\n });\n}\n\nexport function resolveSubagentDefaultExtensions(\n userSettings: SubagentSettings,\n projectSettings: SubagentSettings,\n projectSettingsPath: string | null,\n): string[] | undefined {\n if (projectSettingsPath && projectSettings.defaultExtensions !== undefined) return projectSettings.defaultExtensions;\n return userSettings.defaultExtensions;\n}\n\n/**\n * Fill in default extensions for agents that declared none.\n *\n * The `extensionsFromDefault` flag matters when the agent is later overridden:\n * a snapshot must not record a settings default as if the builtin had declared\n * it, or removing the override would bake the default in permanently.\n */\nexport function applySubagentDefaultExtensions(\n agents: AgentConfig[],\n defaultExtensions: string[] | undefined,\n): AgentConfig[] {\n if (defaultExtensions === undefined) return agents;\n return agents.map((agent) => {\n if (agent.extensions !== undefined) return agent;\n return inheritFrontmatterFields(agent, {\n ...agent,\n extensions: [...defaultExtensions],\n extensionsFromDefault: true,\n });\n });\n}\n\nexport function applySubagentDefaults(\n agents: AgentConfig[],\n defaultModel: AgentModelSourceInfo | undefined,\n defaultThinking: string | undefined,\n defaultExtensions: string[] | undefined,\n defaultFallbackModels?: string[],\n): AgentConfig[] {\n return applySubagentDefaultExtensions(\n applySubagentDefaultThinking(\n applySubagentDefaultFallbackModels(applySubagentDefaultModel(agents, defaultModel), defaultFallbackModels),\n defaultThinking,\n ),\n defaultExtensions,\n );\n}\n\n// ============================================================================\n// Override snapshots\n// ============================================================================\n\n/**\n * Snapshot the fields an override may replace, so it can be undone.\n *\n * `extensions` is omitted when it came from a settings default rather than the\n * agent itself; restoring it would turn a default into a permanent value.\n */\nexport function cloneOverrideBase(agent: AgentConfig): BuiltinOverrideSnapshot {\n return {\n model: agent.model,\n fallbackModels: agent.fallbackModels ? [...agent.fallbackModels] : undefined,\n thinking: agent.thinking,\n systemPromptMode: agent.systemPromptMode,\n inheritProjectContext: agent.inheritProjectContext,\n inheritSkills: agent.inheritSkills,\n defaultContext: agent.defaultContext,\n disabled: agent.disabled,\n systemPrompt: agent.systemPrompt,\n skills: agent.skills ? [...agent.skills] : undefined,\n skillPath: agent.skillPath ? [...agent.skillPath] : undefined,\n tools: agent.tools ? [...agent.tools] : undefined,\n mcpDirectTools: agent.mcpDirectTools ? [...agent.mcpDirectTools] : undefined,\n extensions: agent.extensionsFromDefault ? undefined : agent.extensions ? [...agent.extensions] : undefined,\n subagentOnlyExtensions: agent.subagentOnlyExtensions ? [...agent.subagentOnlyExtensions] : undefined,\n completionGuard: agent.completionGuard,\n toolBudget: agent.toolBudget,\n };\n}\n\n/**\n * Deep-copy an override before it is written to a settings file.\n *\n * Only keys the caller actually set are copied, because an explicit `undefined`\n * would serialise the key away and read back as \"not overridden\".\n */\nexport function cloneOverrideValue(override: BuiltinAgentOverrideConfig): BuiltinAgentOverrideConfig {\n return {\n ...(override.model !== undefined ? { model: override.model } : {}),\n ...(override.fallbackModels !== undefined\n ? { fallbackModels: override.fallbackModels === false ? false : [...override.fallbackModels] }\n : {}),\n ...(override.thinking !== undefined ? { thinking: override.thinking } : {}),\n ...(override.systemPromptMode !== undefined ? { systemPromptMode: override.systemPromptMode } : {}),\n ...(override.inheritProjectContext !== undefined ? { inheritProjectContext: override.inheritProjectContext } : {}),\n ...(override.inheritSkills !== undefined ? { inheritSkills: override.inheritSkills } : {}),\n ...(override.defaultContext !== undefined ? { defaultContext: override.defaultContext } : {}),\n ...(override.disabled !== undefined ? { disabled: override.disabled } : {}),\n ...(override.systemPrompt !== undefined ? { systemPrompt: override.systemPrompt } : {}),\n ...(override.skills !== undefined ? { skills: override.skills === false ? false : [...override.skills] } : {}),\n ...(override.tools !== undefined ? { tools: override.tools === false ? false : [...override.tools] } : {}),\n ...(override.extensions !== undefined\n ? { extensions: override.extensions === false ? false : [...override.extensions] }\n : {}),\n ...(override.subagentOnlyExtensions !== undefined\n ? {\n subagentOnlyExtensions:\n override.subagentOnlyExtensions === false ? false : [...override.subagentOnlyExtensions],\n }\n : {}),\n ...(override.completionGuard !== undefined ? { completionGuard: override.completionGuard } : {}),\n ...(override.toolBudget !== undefined\n ? {\n toolBudget:\n override.toolBudget === false\n ? false\n : {\n ...override.toolBudget,\n ...(Array.isArray(override.toolBudget.block) ? { block: [...override.toolBudget.block] } : {}),\n },\n }\n : {}),\n };\n}\n\n// ============================================================================\n// Applying overrides\n// ============================================================================\n\n/**\n * Replace a builtin's values with an override's.\n *\n * `false` clears a field rather than setting it to false; that distinction is\n * the whole reason the stored shape differs from the snapshot shape.\n */\nexport function applyBuiltinOverride(\n agent: AgentConfig,\n override: BuiltinAgentOverrideConfig,\n meta: OverrideScopeMeta,\n): AgentConfig {\n const next: AgentConfig = { ...agent, override: { ...meta, base: cloneOverrideBase(agent) } };\n\n if (override.model !== undefined) {\n next.model = override.model === false ? undefined : override.model;\n next.modelSource = undefined;\n }\n if (override.fallbackModels !== undefined) {\n next.fallbackModels = override.fallbackModels === false ? undefined : [...override.fallbackModels];\n }\n if (override.thinking !== undefined) next.thinking = override.thinking === false ? undefined : override.thinking;\n if (override.systemPromptMode !== undefined) next.systemPromptMode = override.systemPromptMode;\n if (override.inheritProjectContext !== undefined) next.inheritProjectContext = override.inheritProjectContext;\n if (override.inheritSkills !== undefined) next.inheritSkills = override.inheritSkills;\n if (override.defaultContext !== undefined) {\n next.defaultContext = override.defaultContext === false ? undefined : override.defaultContext;\n }\n if (override.disabled !== undefined) next.disabled = override.disabled;\n if (override.systemPrompt !== undefined) next.systemPrompt = override.systemPrompt;\n if (override.skills !== undefined) next.skills = override.skills === false ? undefined : [...override.skills];\n if (override.tools !== undefined) {\n const { tools, mcpDirectTools } = splitToolList(override.tools === false ? [] : override.tools);\n next.tools = tools;\n next.mcpDirectTools = mcpDirectTools;\n }\n if (override.extensions !== undefined) {\n next.extensions = override.extensions === false ? undefined : [...override.extensions];\n }\n if (override.subagentOnlyExtensions !== undefined) {\n next.subagentOnlyExtensions =\n override.subagentOnlyExtensions === false ? undefined : [...override.subagentOnlyExtensions];\n }\n if (override.completionGuard !== undefined) next.completionGuard = override.completionGuard;\n if (override.toolBudget !== undefined) {\n next.toolBudget = override.toolBudget === false ? undefined : override.toolBudget;\n }\n\n return next;\n}\n\n/**\n * Strip an agent's thinking level for the bulk `disableThinking` switch.\n *\n * An existing `override` is kept rather than replaced, so a per-agent override\n * remains the recorded reason the agent differs from its builtin definition.\n */\nexport function clearBuiltinThinking(agent: AgentConfig, meta: OverrideScopeMeta): AgentConfig {\n if (agent.thinking === undefined) return agent;\n return { ...agent, thinking: undefined, override: agent.override ?? { ...meta, base: cloneOverrideBase(agent) } };\n}\n\n/**\n * Apply user and project settings to the builtin agents.\n *\n * Precedence per agent: a project override, then the project bulk disable, then\n * a user override, then the user bulk disable. A project file that sets\n * `disableBuiltins` at all (including to false) shadows the user's switch, so a\n * project can re-enable builtins its user settings turned off.\n */\nexport function applyBuiltinOverrides(\n builtinAgents: AgentConfig[],\n userSettings: SubagentSettings,\n projectSettings: SubagentSettings,\n userSettingsPath: string,\n projectSettingsPath: string | null,\n): AgentConfig[] {\n const projectBulkDisabled = projectSettings.disableBuiltins === true && projectSettingsPath !== null;\n const userBulkDisabled = projectSettings.disableBuiltins === undefined && userSettings.disableBuiltins === true;\n const projectThinkingConfigured = projectSettings.disableThinking !== undefined && projectSettingsPath !== null;\n const disableThinking = projectThinkingConfigured\n ? projectSettings.disableThinking === true\n : userSettings.disableThinking === true;\n const disableThinkingMeta: OverrideScopeMeta =\n projectThinkingConfigured && projectSettingsPath\n ? { scope: 'project', path: projectSettingsPath }\n : { scope: 'user', path: userSettingsPath };\n\n // A per-agent thinking override is more specific than the bulk switch, so it\n // survives it; everything else is cleared.\n const applyGlobalThinking = (agent: AgentConfig, hasExplicitThinkingOverride: boolean): AgentConfig => {\n if (!disableThinking || hasExplicitThinkingOverride) return agent;\n return clearBuiltinThinking(agent, disableThinkingMeta);\n };\n\n return builtinAgents.map((agent) => {\n const projectOverride = projectSettings.overrides[agent.name];\n if (projectOverride && projectSettingsPath) {\n return applyGlobalThinking(\n applyBuiltinOverride(agent, projectOverride, { scope: 'project', path: projectSettingsPath }),\n projectOverride.thinking !== undefined,\n );\n }\n\n if (projectBulkDisabled && projectSettingsPath) {\n return applyGlobalThinking(\n applyBuiltinOverride(agent, { disabled: true }, { scope: 'project', path: projectSettingsPath }),\n false,\n );\n }\n\n const userOverride = userSettings.overrides[agent.name];\n if (userOverride) {\n return applyGlobalThinking(\n applyBuiltinOverride(agent, userOverride, { scope: 'user', path: userSettingsPath }),\n !projectThinkingConfigured && userOverride.thinking !== undefined,\n );\n }\n\n if (userBulkDisabled) {\n return applyGlobalThinking(\n applyBuiltinOverride(agent, { disabled: true }, { scope: 'user', path: userSettingsPath }),\n false,\n );\n }\n\n return applyGlobalThinking(agent, false);\n });\n}\n\n/**\n * Apply an override to a custom agent, filling only unset fields.\n *\n * A custom agent has a file the user could have edited, so a settings value\n * must never contradict its frontmatter. Nothing is copied and no `override`\n * is recorded unless at least one field was actually filled, which keeps agents\n * that declared everything from being reported as overridden.\n */\nexport function applyCustomAgentOverride(\n agent: AgentConfig,\n override: BuiltinAgentOverrideConfig,\n meta: OverrideScopeMeta,\n): AgentConfig {\n let next: AgentConfig | undefined;\n let anyFilled = false;\n\n const mutable = (): AgentConfig => {\n next ??= { ...agent };\n return next;\n };\n\n const fill = <K extends keyof AgentConfig>(field: K, frontmatterFields: string[], value: AgentConfig[K]): void => {\n if (agentHasFrontmatterField(agent, ...frontmatterFields)) return;\n mutable()[field] = value;\n anyFilled = true;\n };\n\n if (override.model !== undefined && !agentHasFrontmatterField(agent, 'model')) {\n const target = mutable();\n target.model = override.model === false ? undefined : override.model;\n target.modelSource = undefined;\n anyFilled = true;\n }\n if (override.fallbackModels !== undefined) {\n fill(\n 'fallbackModels',\n ['fallbackModels'],\n override.fallbackModels === false ? undefined : [...override.fallbackModels],\n );\n }\n if (override.thinking !== undefined) {\n fill('thinking', ['thinking'], override.thinking === false ? undefined : override.thinking);\n }\n if (override.systemPromptMode !== undefined) {\n fill('systemPromptMode', ['systemPromptMode'], override.systemPromptMode);\n }\n if (override.inheritProjectContext !== undefined) {\n fill('inheritProjectContext', ['inheritProjectContext'], override.inheritProjectContext);\n }\n if (override.inheritSkills !== undefined) {\n fill('inheritSkills', ['inheritSkills'], override.inheritSkills);\n }\n if (override.defaultContext !== undefined) {\n fill('defaultContext', ['defaultContext'], override.defaultContext === false ? undefined : override.defaultContext);\n }\n if (override.disabled !== undefined && agent.disabled === undefined) {\n mutable().disabled = override.disabled;\n anyFilled = true;\n }\n if (override.skills !== undefined) {\n fill('skills', ['skill', 'skills'], override.skills === false ? undefined : [...override.skills]);\n }\n if (override.tools !== undefined && !agentHasFrontmatterField(agent, 'tools')) {\n const { tools, mcpDirectTools } = splitToolList(override.tools === false ? [] : override.tools);\n const target = mutable();\n target.tools = tools;\n target.mcpDirectTools = mcpDirectTools;\n anyFilled = true;\n }\n if (override.extensions !== undefined) {\n fill('extensions', ['extensions'], override.extensions === false ? undefined : [...override.extensions]);\n }\n if (override.subagentOnlyExtensions !== undefined) {\n fill(\n 'subagentOnlyExtensions',\n ['subagentOnlyExtensions'],\n override.subagentOnlyExtensions === false ? undefined : [...override.subagentOnlyExtensions],\n );\n }\n if (override.completionGuard !== undefined) {\n fill('completionGuard', ['completionGuard'], override.completionGuard);\n }\n if (override.toolBudget !== undefined) {\n fill('toolBudget', ['toolBudget'], override.toolBudget === false ? undefined : override.toolBudget);\n }\n\n if (!anyFilled || !next) return agent;\n next.override = { ...meta, base: cloneOverrideBase(agent) };\n return inheritFrontmatterFields(agent, next);\n}\n\nexport function applyCustomAgentOverrides(\n agents: AgentConfig[],\n userSettings: SubagentSettings,\n projectSettings: SubagentSettings,\n userSettingsPath: string,\n projectSettingsPath: string | null,\n): AgentConfig[] {\n return agents.map((agent) => {\n const projectOverride = projectSettings.overrides[agent.name];\n if (projectOverride && projectSettingsPath) {\n return applyCustomAgentOverride(agent, projectOverride, { scope: 'project', path: projectSettingsPath });\n }\n\n const userOverride = userSettings.overrides[agent.name];\n if (userOverride) {\n return applyCustomAgentOverride(agent, userOverride, { scope: 'user', path: userSettingsPath });\n }\n\n return agent;\n });\n}\n\n/**\n * Diff an edited agent against its pre-override snapshot.\n *\n * Only changed fields are written, so a settings file records the user's edits\n * rather than a full copy of the builtin that would drift as the builtin does.\n * Returns undefined when nothing changed, which callers treat as \"remove the\n * override\" rather than \"write an empty one\".\n */\nexport function buildBuiltinOverrideConfig(\n base: BuiltinOverrideSnapshot,\n draft: Pick<\n AgentConfig,\n | 'model'\n | 'fallbackModels'\n | 'thinking'\n | 'systemPromptMode'\n | 'inheritProjectContext'\n | 'inheritSkills'\n | 'defaultContext'\n | 'disabled'\n | 'systemPrompt'\n | 'skills'\n | 'tools'\n | 'mcpDirectTools'\n | 'extensions'\n | 'subagentOnlyExtensions'\n | 'completionGuard'\n | 'toolBudget'\n >,\n): BuiltinAgentOverrideConfig | undefined {\n const override: BuiltinAgentOverrideConfig = {};\n\n if (draft.model !== base.model) override.model = draft.model ?? false;\n if (!arraysEqual(draft.fallbackModels, base.fallbackModels)) {\n override.fallbackModels = draft.fallbackModels ? [...draft.fallbackModels] : false;\n }\n if (draft.thinking !== base.thinking) override.thinking = draft.thinking ?? false;\n if (draft.systemPromptMode !== base.systemPromptMode) override.systemPromptMode = draft.systemPromptMode;\n if (draft.inheritProjectContext !== base.inheritProjectContext) {\n override.inheritProjectContext = draft.inheritProjectContext;\n }\n if (draft.inheritSkills !== base.inheritSkills) override.inheritSkills = draft.inheritSkills;\n if (draft.defaultContext !== base.defaultContext) override.defaultContext = draft.defaultContext ?? false;\n if (draft.disabled !== base.disabled) override.disabled = draft.disabled ?? false;\n if (draft.systemPrompt !== base.systemPrompt) override.systemPrompt = draft.systemPrompt;\n if (!arraysEqual(draft.skills, base.skills)) override.skills = draft.skills ? [...draft.skills] : false;\n\n const baseTools = joinToolList(base);\n const draftTools = joinToolList(draft);\n if (!arraysEqual(draftTools, baseTools)) override.tools = draftTools ? [...draftTools] : false;\n if (!arraysEqual(draft.extensions, base.extensions)) {\n override.extensions = draft.extensions ? [...draft.extensions] : false;\n }\n if (!arraysEqual(draft.subagentOnlyExtensions, base.subagentOnlyExtensions)) {\n override.subagentOnlyExtensions = draft.subagentOnlyExtensions ? [...draft.subagentOnlyExtensions] : false;\n }\n // Compared as booleans because an unset guard and an enabled one behave the\n // same, so only a switch to or from explicitly disabled is a real change.\n if ((draft.completionGuard !== false) !== (base.completionGuard !== false)) {\n override.completionGuard = draft.completionGuard !== false;\n }\n if (JSON.stringify(draft.toolBudget) !== JSON.stringify(base.toolBudget)) {\n override.toolBudget = draft.toolBudget ?? false;\n }\n\n return Object.keys(override).length > 0 ? override : undefined;\n}\n\n// ============================================================================\n// Persisting overrides\n// ============================================================================\n\nconst NO_PROJECT_ROOT_MESSAGE = 'Project override is not available here. No project config root was found.';\n\n/** Resolve the settings file for a scope, or throw when there is no project. */\nfunction requireSettingsPath(cwd: string, scope: 'user' | 'project'): string {\n const filePath = scope === 'project' ? getProjectAgentSettingsPath(cwd) : getUserAgentSettingsPath();\n if (!filePath) throw new Error(NO_PROJECT_ROOT_MESSAGE);\n return filePath;\n}\n\n/** Write an override, replacing any existing entry for the agent wholesale. */\nexport function saveBuiltinAgentOverride(\n cwd: string,\n name: string,\n scope: 'user' | 'project',\n override: BuiltinAgentOverrideConfig,\n): string {\n const filePath = requireSettingsPath(cwd, scope);\n\n const settings = readSettingsFileStrict(filePath);\n const subagents = cloneObjectField(settings, SUBAGENTS_SETTINGS_KEY);\n const agentOverrides = cloneObjectField(subagents, AGENT_OVERRIDES_KEY);\n\n agentOverrides[name] = cloneOverrideValue(override);\n subagents[AGENT_OVERRIDES_KEY] = agentOverrides;\n settings[SUBAGENTS_SETTINGS_KEY] = subagents;\n writeSettingsFile(filePath, settings);\n return filePath;\n}\n\n/**\n * Drop an agent's whole override entry.\n *\n * Emptied containers are deleted rather than left behind, so removing the last\n * override leaves the settings file as it was before any were added.\n */\nexport function removeBuiltinAgentOverride(\n cwd: string,\n name: string,\n scope: 'user' | 'project',\n): { path: string; removed: boolean } {\n const filePath = requireSettingsPath(cwd, scope);\n if (!fs.existsSync(filePath)) return { path: filePath, removed: false };\n\n const settings = readSettingsFileStrict(filePath);\n const subagents = readObjectField(settings, SUBAGENTS_SETTINGS_KEY);\n if (!subagents) return { path: filePath, removed: false };\n const agentOverrides = readObjectField(subagents, AGENT_OVERRIDES_KEY);\n if (!agentOverrides) return { path: filePath, removed: false };\n if (!Object.hasOwn(agentOverrides, name)) return { path: filePath, removed: false };\n\n const nextSubagents = { ...subagents };\n const nextOverrides = { ...agentOverrides };\n delete nextOverrides[name];\n if (Object.keys(nextOverrides).length > 0) nextSubagents[AGENT_OVERRIDES_KEY] = nextOverrides;\n else delete nextSubagents[AGENT_OVERRIDES_KEY];\n\n if (Object.keys(nextSubagents).length > 0) settings[SUBAGENTS_SETTINGS_KEY] = nextSubagents;\n else delete settings[SUBAGENTS_SETTINGS_KEY];\n\n writeSettingsFile(filePath, settings);\n return { path: filePath, removed: true };\n}\n\n/** Merge fields into an agent's existing override, leaving the rest in place. */\nexport function mergeBuiltinAgentOverride(\n cwd: string,\n name: string,\n scope: 'user' | 'project',\n fields: BuiltinAgentOverrideConfig,\n): string {\n const filePath = requireSettingsPath(cwd, scope);\n\n const settings = readSettingsFileStrict(filePath);\n const subagents = cloneObjectField(settings, SUBAGENTS_SETTINGS_KEY);\n const agentOverrides = cloneObjectField(subagents, AGENT_OVERRIDES_KEY);\n\n const existing = readObjectField(agentOverrides, name) ?? {};\n agentOverrides[name] = { ...existing, ...cloneOverrideValue(fields) };\n subagents[AGENT_OVERRIDES_KEY] = agentOverrides;\n settings[SUBAGENTS_SETTINGS_KEY] = subagents;\n writeSettingsFile(filePath, settings);\n return filePath;\n}\n\n/** Remove named fields from an agent's override, dropping it once empty. */\nexport function removeBuiltinAgentOverrideFields(\n cwd: string,\n name: string,\n scope: 'user' | 'project',\n fields: string[],\n): { path: string; removed: boolean } {\n const filePath = requireSettingsPath(cwd, scope);\n if (!fs.existsSync(filePath)) return { path: filePath, removed: false };\n\n const settings = readSettingsFileStrict(filePath);\n const subagents = readObjectField(settings, SUBAGENTS_SETTINGS_KEY);\n if (!subagents) return { path: filePath, removed: false };\n const agentOverrides = readObjectField(subagents, AGENT_OVERRIDES_KEY);\n if (!agentOverrides) return { path: filePath, removed: false };\n const entry = readObjectField(agentOverrides, name);\n if (!entry) return { path: filePath, removed: false };\n\n const nextEntry: Record<string, unknown> = { ...entry };\n let removed = false;\n for (const field of fields) {\n if (Object.hasOwn(nextEntry, field)) {\n delete nextEntry[field];\n removed = true;\n }\n }\n if (!removed) return { path: filePath, removed: false };\n\n const nextSubagents = { ...subagents };\n const nextOverrides = { ...agentOverrides };\n if (Object.keys(nextEntry).length > 0) {\n nextOverrides[name] = nextEntry;\n nextSubagents[AGENT_OVERRIDES_KEY] = nextOverrides;\n } else {\n delete nextOverrides[name];\n if (Object.keys(nextOverrides).length > 0) nextSubagents[AGENT_OVERRIDES_KEY] = nextOverrides;\n else delete nextSubagents[AGENT_OVERRIDES_KEY];\n }\n if (Object.keys(nextSubagents).length > 0) settings[SUBAGENTS_SETTINGS_KEY] = nextSubagents;\n else delete settings[SUBAGENTS_SETTINGS_KEY];\n\n writeSettingsFile(filePath, settings);\n return { path: filePath, removed: true };\n}\n"],"mappings":"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"}
|
package/dist/agents/settings.mjs
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{__esmMin as e}from"../_virtual/_rolldown/runtime.mjs";import{init_configDir as t,readSettingsFileStrict as n}from"../shared/configDir.mjs";import{init_atomicJson as r}from"../shared/atomicJson.mjs";function i(e,t){k.set(e,t)}function a(e,...t){let n=k.get(e);return n?t.some(e=>n.has(e)):!1}function o(e,t){let n=k.get(e);return n&&k.set(t,n),t}function s(e){let t=[],n=[];for(let r of e??[])r.startsWith(D)?t.push(r.slice(4)):n.push(r);return{...e===void 0?{}:{tools:n},...t.length>0?{mcpDirectTools:t}:{}}}function c(e,t){let n=e[t];if(!(!n||typeof n!=`object`||Array.isArray(n)))return n}function l(e,t){if(e===void 0)return;if(!e||typeof e!=`object`||Array.isArray(e))throw Error(`Subagent settings in '${t}' have invalid 'modelScope'; expected an object.`);let n=e,r={};if(`enforce`in n){if(typeof n.enforce!=`boolean`)throw Error(`Subagent settings in '${t}' have invalid 'modelScope.enforce'; expected a boolean.`);r.enforce=n.enforce}if(`allow`in n){if(!Array.isArray(n.allow))throw Error(`Subagent settings in '${t}' have invalid 'modelScope.allow'; expected an array of strings.`);let e=[];for(let r of n.allow){if(typeof r!=`string`)throw Error(`Subagent settings in '${t}' have invalid 'modelScope.allow'; expected an array of strings.`);let n=r.trim();n&&e.push(n)}if(e.length===0)throw Error(`Subagent settings in '${t}' have invalid 'modelScope.allow'; expected a non-empty array of patterns.`);r.allow=e}if(r.enforce===!0&&(!r.allow||r.allow.length===0))throw Error(`Subagent settings in '${t}' set modelScope.enforce without a non-empty 'allow' list; supply allowed model patterns or disable enforcement.`);return Object.keys(r).length>0?r:void 0}function u(e,t){if(e===void 0)return;if(e===!1)return!1;if(!Array.isArray(e))throw Error(`Builtin override '${t.name}' in '${t.filePath}' has invalid '${t.field}'; expected an array of strings or false.`);let n=[];for(let r of e){if(typeof r!=`string`)throw Error(`Builtin override '${t.name}' in '${t.filePath}' has invalid '${t.field}'; expected an array of strings or false.`);let e=r.trim();e&&n.push(e)}return n}function d(e,t,n){if(!t||typeof t!=`object`||Array.isArray(t))throw Error(`Builtin override '${e}' in '${n}' must be an object.`);let r=t,i={};if(`model`in r)if(typeof r.model==`string`||r.model===!1)i.model=r.model;else throw Error(`Builtin override '${e}' in '${n}' has invalid 'model'; expected a string or false.`);if(`thinking`in r)if(typeof r.thinking==`string`||r.thinking===!1)i.thinking=r.thinking;else throw Error(`Builtin override '${e}' in '${n}' has invalid 'thinking'; expected a string or false.`);if(`systemPromptMode`in r)if(r.systemPromptMode===`append`||r.systemPromptMode===`replace`)i.systemPromptMode=r.systemPromptMode;else throw Error(`Builtin override '${e}' in '${n}' has invalid 'systemPromptMode'; expected 'append' or 'replace'.`);if(`inheritProjectContext`in r)if(typeof r.inheritProjectContext==`boolean`)i.inheritProjectContext=r.inheritProjectContext;else throw Error(`Builtin override '${e}' in '${n}' has invalid 'inheritProjectContext'; expected a boolean.`);if(`inheritSkills`in r)if(typeof r.inheritSkills==`boolean`)i.inheritSkills=r.inheritSkills;else throw Error(`Builtin override '${e}' in '${n}' has invalid 'inheritSkills'; expected a boolean.`);if(`defaultContext`in r)if(r.defaultContext===`fresh`||r.defaultContext===`fork`||r.defaultContext===!1)i.defaultContext=r.defaultContext;else throw Error(`Builtin override '${e}' in '${n}' has invalid 'defaultContext'; expected 'fresh', 'fork', or false.`);if(`disabled`in r)if(typeof r.disabled==`boolean`)i.disabled=r.disabled;else throw Error(`Builtin override '${e}' in '${n}' has invalid 'disabled'; expected a boolean.`);if(`completionGuard`in r)if(typeof r.completionGuard==`boolean`)i.completionGuard=r.completionGuard;else throw Error(`Builtin override '${e}' in '${n}' has invalid 'completionGuard'; expected a boolean.`);if(`toolBudget`in r){let t=f(r.toolBudget,e,n);t!==void 0&&(i.toolBudget=t)}if(`systemPrompt`in r)if(typeof r.systemPrompt==`string`)i.systemPrompt=r.systemPrompt;else throw Error(`Builtin override '${e}' in '${n}' has invalid 'systemPrompt'; expected a string.`);let a=t=>u(r[t],{filePath:n,name:e,field:t}),o=a(`fallbackModels`);o!==void 0&&(i.fallbackModels=o);let s=a(`skills`);s!==void 0&&(i.skills=s);let c=a(`tools`);c!==void 0&&(i.tools=c);let l=a(`extensions`);l!==void 0&&(i.extensions=l);let d=a(`subagentOnlyExtensions`);return d!==void 0&&(i.subagentOnlyExtensions=d),Object.keys(i).length>0?i:void 0}function f(e,t,n){if(e===!1)return!1;if(!e||typeof e!=`object`||Array.isArray(e))throw Error(`Builtin override '${t}' in '${n}' has invalid 'toolBudget'; expected an object or false.`);let r=e;if(typeof r.hard!=`number`)throw Error(`Builtin override '${t}' in '${n}' has invalid 'toolBudget.hard'; expected a number.`);let i={hard:r.hard};if(`soft`in r){if(typeof r.soft!=`number`)throw Error(`Builtin override '${t}' in '${n}' has invalid 'toolBudget.soft'; expected a number.`);i.soft=r.soft}if(`block`in r)if(r.block===`*`)i.block=`*`;else if(Array.isArray(r.block)&&r.block.every(e=>typeof e==`string`))i.block=[...r.block];else throw Error(`Builtin override '${t}' in '${n}' has invalid 'toolBudget.block'; expected an array of strings or '*'.`);return i}function p(e){if(!e)return O;let t=c(n(e),T);if(!t)return O;let r;if(`disableBuiltins`in t)if(typeof t.disableBuiltins==`boolean`)r=t.disableBuiltins;else throw Error(`Subagent settings in '${e}' have invalid 'disableBuiltins'; expected a boolean.`);let i;if(`disableThinking`in t)if(typeof t.disableThinking==`boolean`)i=t.disableThinking;else throw Error(`Subagent settings in '${e}' have invalid 'disableThinking'; expected a boolean.`);let a;if(`defaultModel`in t)if(typeof t.defaultModel==`string`&&t.defaultModel.trim())a=t.defaultModel.trim();else throw Error(`Subagent settings in '${e}' have invalid 'defaultModel'; expected a non-empty string.`);let o;if(`defaultThinking`in t)if(typeof t.defaultThinking==`string`&&t.defaultThinking.trim())o=t.defaultThinking.trim();else throw Error(`Subagent settings in '${e}' have invalid 'defaultThinking'; expected a non-empty string.`);let s;if(`defaultExtensions`in t){let n=t.defaultExtensions;if(!Array.isArray(n)||n.some(e=>typeof e!=`string`||!e.trim()))throw Error(`Subagent settings in '${e}' have invalid 'defaultExtensions'; expected an array of non-empty strings.`);s=n.map(e=>String(e).trim())}let u=l(t.modelScope,e),f={},p=c(t,E);if(p)for(let[t,n]of Object.entries(p)){let r=d(t,n,e);r&&(f[t]=r)}return{overrides:f,defaultModel:a,defaultThinking:o,defaultExtensions:s,disableBuiltins:r,disableThinking:i,modelScope:u}}function m(e,t,n,r){return r&&t.defaultModel!==void 0?{type:`subagents.defaultModel`,scope:`project`,path:r,model:t.defaultModel}:e.defaultModel===void 0?void 0:{type:`subagents.defaultModel`,scope:`user`,path:n,model:e.defaultModel}}function h(e,t){return t?e.map(e=>e.model===void 0?o(e,{...e,model:t.model,modelSource:t}):e):e}function g(e,t){return t===void 0?e:e.map(e=>e.fallbackModels===void 0?o(e,{...e,fallbackModels:[...t]}):e)}function _(e,t,n){return n&&t.defaultThinking!==void 0?t.defaultThinking:e.defaultThinking}function v(e,t){return t===void 0?e:e.map(e=>e.thinking===void 0?o(e,{...e,thinking:t}):e)}function y(e,t,n){return n&&t.defaultExtensions!==void 0?t.defaultExtensions:e.defaultExtensions}function b(e,t){return t===void 0?e:e.map(e=>e.extensions===void 0?o(e,{...e,extensions:[...t],extensionsFromDefault:!0}):e)}function x(e,t,n,r,i){return b(v(g(h(e,t),i),n),r)}function S(e){return{model:e.model,fallbackModels:e.fallbackModels?[...e.fallbackModels]:void 0,thinking:e.thinking,systemPromptMode:e.systemPromptMode,inheritProjectContext:e.inheritProjectContext,inheritSkills:e.inheritSkills,defaultContext:e.defaultContext,disabled:e.disabled,systemPrompt:e.systemPrompt,skills:e.skills?[...e.skills]:void 0,skillPath:e.skillPath?[...e.skillPath]:void 0,tools:e.tools?[...e.tools]:void 0,mcpDirectTools:e.mcpDirectTools?[...e.mcpDirectTools]:void 0,extensions:e.extensionsFromDefault?void 0:e.extensions?[...e.extensions]:void 0,subagentOnlyExtensions:e.subagentOnlyExtensions?[...e.subagentOnlyExtensions]:void 0,completionGuard:e.completionGuard,toolBudget:e.toolBudget}}function C(e,t,n){let r,i=!1,c=()=>(r??={...e},r),l=(t,n,r)=>{a(e,...n)||(c()[t]=r,i=!0)};if(t.model!==void 0&&l(`model`,[`model`],t.model===!1?void 0:t.model),t.fallbackModels!==void 0&&l(`fallbackModels`,[`fallbackModels`],t.fallbackModels===!1?void 0:[...t.fallbackModels]),t.thinking!==void 0&&l(`thinking`,[`thinking`],t.thinking===!1?void 0:t.thinking),t.systemPromptMode!==void 0&&l(`systemPromptMode`,[`systemPromptMode`],t.systemPromptMode),t.inheritProjectContext!==void 0&&l(`inheritProjectContext`,[`inheritProjectContext`],t.inheritProjectContext),t.inheritSkills!==void 0&&l(`inheritSkills`,[`inheritSkills`],t.inheritSkills),t.defaultContext!==void 0&&l(`defaultContext`,[`defaultContext`],t.defaultContext===!1?void 0:t.defaultContext),t.disabled!==void 0&&e.disabled===void 0&&(c().disabled=t.disabled,i=!0),t.skills!==void 0&&l(`skills`,[`skill`,`skills`],t.skills===!1?void 0:[...t.skills]),t.tools!==void 0&&!a(e,`tools`)){let{tools:e,mcpDirectTools:n}=s(t.tools===!1?[]:t.tools),r=c();r.tools=e,r.mcpDirectTools=n,i=!0}return t.extensions!==void 0&&l(`extensions`,[`extensions`],t.extensions===!1?void 0:[...t.extensions]),t.subagentOnlyExtensions!==void 0&&l(`subagentOnlyExtensions`,[`subagentOnlyExtensions`],t.subagentOnlyExtensions===!1?void 0:[...t.subagentOnlyExtensions]),t.completionGuard!==void 0&&l(`completionGuard`,[`completionGuard`],t.completionGuard),t.toolBudget!==void 0&&l(`toolBudget`,[`toolBudget`],t.toolBudget===!1?void 0:t.toolBudget),!i||!r?e:(r.override={...n,base:S(e)},o(e,r))}function w(e,t,n,r,i){return e.map(e=>{let a=n.overrides[e.name];if(a&&i)return C(e,a,{scope:`project`,path:i});let o=t.overrides[e.name];return o?C(e,o,{scope:`user`,path:r}):e})}var T,E,D,O,k,A=e((()=>{r(),t(),T=`subagents`,E=`agentOverrides`,D=`mcp:`,O={overrides:{}},k=new WeakMap}));A();export{O as EMPTY_SUBAGENT_SETTINGS,w as applyCustomAgentOverrides,x as applySubagentDefaults,A as init_settings,p as readSubagentSettings,y as resolveSubagentDefaultExtensions,m as resolveSubagentDefaultModel,_ as resolveSubagentDefaultThinking,i as setAgentFrontmatterFields};
|
|
1
|
+
import{__esmMin as e}from"../_virtual/_rolldown/runtime.mjs";import{init_configDir as t,readSettingsFileStrict as n}from"../shared/configDir.mjs";import{init_atomicJson as r}from"../shared/atomicJson.mjs";function i(e,t){k.set(e,t)}function a(e,...t){let n=k.get(e);return n?t.some(e=>n.has(e)):!1}function o(e,t){let n=k.get(e);return n&&k.set(t,n),t}function s(e){let t=[],n=[];for(let r of e??[])r.startsWith(D)?t.push(r.slice(4)):n.push(r);return{...e===void 0?{}:{tools:n},...t.length>0?{mcpDirectTools:t}:{}}}function c(e,t){let n=e[t];if(!(!n||typeof n!=`object`||Array.isArray(n)))return n}function l(e,t){if(e===void 0)return;if(!e||typeof e!=`object`||Array.isArray(e))throw Error(`Subagent settings in '${t}' have invalid 'modelScope'; expected an object.`);let n=e,r={};if(`enforce`in n){if(typeof n.enforce!=`boolean`)throw Error(`Subagent settings in '${t}' have invalid 'modelScope.enforce'; expected a boolean.`);r.enforce=n.enforce}if(`allow`in n){if(!Array.isArray(n.allow))throw Error(`Subagent settings in '${t}' have invalid 'modelScope.allow'; expected an array of strings.`);let e=[];for(let r of n.allow){if(typeof r!=`string`)throw Error(`Subagent settings in '${t}' have invalid 'modelScope.allow'; expected an array of strings.`);let n=r.trim();n&&e.push(n)}if(e.length===0)throw Error(`Subagent settings in '${t}' have invalid 'modelScope.allow'; expected a non-empty array of patterns.`);r.allow=e}if(r.enforce===!0&&(!r.allow||r.allow.length===0))throw Error(`Subagent settings in '${t}' set modelScope.enforce without a non-empty 'allow' list; supply allowed model patterns or disable enforcement.`);return Object.keys(r).length>0?r:void 0}function u(e,t){if(e===void 0)return;if(e===!1)return!1;if(!Array.isArray(e))throw Error(`Builtin override '${t.name}' in '${t.filePath}' has invalid '${t.field}'; expected an array of strings or false.`);let n=[];for(let r of e){if(typeof r!=`string`)throw Error(`Builtin override '${t.name}' in '${t.filePath}' has invalid '${t.field}'; expected an array of strings or false.`);let e=r.trim();e&&n.push(e)}return n}function d(e,t,n){if(!t||typeof t!=`object`||Array.isArray(t))throw Error(`Builtin override '${e}' in '${n}' must be an object.`);let r=t,i={};if(`model`in r)if(typeof r.model==`string`||r.model===!1)i.model=r.model;else throw Error(`Builtin override '${e}' in '${n}' has invalid 'model'; expected a string or false.`);if(`thinking`in r)if(typeof r.thinking==`string`||r.thinking===!1)i.thinking=r.thinking;else throw Error(`Builtin override '${e}' in '${n}' has invalid 'thinking'; expected a string or false.`);if(`systemPromptMode`in r)if(r.systemPromptMode===`append`||r.systemPromptMode===`replace`)i.systemPromptMode=r.systemPromptMode;else throw Error(`Builtin override '${e}' in '${n}' has invalid 'systemPromptMode'; expected 'append' or 'replace'.`);if(`inheritProjectContext`in r)if(typeof r.inheritProjectContext==`boolean`)i.inheritProjectContext=r.inheritProjectContext;else throw Error(`Builtin override '${e}' in '${n}' has invalid 'inheritProjectContext'; expected a boolean.`);if(`inheritSkills`in r)if(typeof r.inheritSkills==`boolean`)i.inheritSkills=r.inheritSkills;else throw Error(`Builtin override '${e}' in '${n}' has invalid 'inheritSkills'; expected a boolean.`);if(`defaultContext`in r)if(r.defaultContext===`fresh`||r.defaultContext===`fork`||r.defaultContext===!1)i.defaultContext=r.defaultContext;else throw Error(`Builtin override '${e}' in '${n}' has invalid 'defaultContext'; expected 'fresh', 'fork', or false.`);if(`disabled`in r)if(typeof r.disabled==`boolean`)i.disabled=r.disabled;else throw Error(`Builtin override '${e}' in '${n}' has invalid 'disabled'; expected a boolean.`);if(`completionGuard`in r)if(typeof r.completionGuard==`boolean`)i.completionGuard=r.completionGuard;else throw Error(`Builtin override '${e}' in '${n}' has invalid 'completionGuard'; expected a boolean.`);if(`toolBudget`in r){let t=f(r.toolBudget,e,n);t!==void 0&&(i.toolBudget=t)}if(`systemPrompt`in r)if(typeof r.systemPrompt==`string`)i.systemPrompt=r.systemPrompt;else throw Error(`Builtin override '${e}' in '${n}' has invalid 'systemPrompt'; expected a string.`);let a=t=>u(r[t],{filePath:n,name:e,field:t}),o=a(`fallbackModels`);o!==void 0&&(i.fallbackModels=o);let s=a(`skills`);s!==void 0&&(i.skills=s);let c=a(`tools`);c!==void 0&&(i.tools=c);let l=a(`extensions`);l!==void 0&&(i.extensions=l);let d=a(`subagentOnlyExtensions`);return d!==void 0&&(i.subagentOnlyExtensions=d),Object.keys(i).length>0?i:void 0}function f(e,t,n){if(e===!1)return!1;if(!e||typeof e!=`object`||Array.isArray(e))throw Error(`Builtin override '${t}' in '${n}' has invalid 'toolBudget'; expected an object or false.`);let r=e;if(typeof r.hard!=`number`)throw Error(`Builtin override '${t}' in '${n}' has invalid 'toolBudget.hard'; expected a number.`);let i={hard:r.hard};if(`soft`in r){if(typeof r.soft!=`number`)throw Error(`Builtin override '${t}' in '${n}' has invalid 'toolBudget.soft'; expected a number.`);i.soft=r.soft}if(`block`in r)if(r.block===`*`)i.block=`*`;else if(Array.isArray(r.block)&&r.block.every(e=>typeof e==`string`))i.block=[...r.block];else throw Error(`Builtin override '${t}' in '${n}' has invalid 'toolBudget.block'; expected an array of strings or '*'.`);return i}function p(e){if(!e)return O;let t=c(n(e),T);if(!t)return O;let r;if(`disableBuiltins`in t)if(typeof t.disableBuiltins==`boolean`)r=t.disableBuiltins;else throw Error(`Subagent settings in '${e}' have invalid 'disableBuiltins'; expected a boolean.`);let i;if(`disableThinking`in t)if(typeof t.disableThinking==`boolean`)i=t.disableThinking;else throw Error(`Subagent settings in '${e}' have invalid 'disableThinking'; expected a boolean.`);let a;if(`defaultModel`in t)if(typeof t.defaultModel==`string`&&t.defaultModel.trim())a=t.defaultModel.trim();else throw Error(`Subagent settings in '${e}' have invalid 'defaultModel'; expected a non-empty string.`);let o;if(`defaultThinking`in t)if(typeof t.defaultThinking==`string`&&t.defaultThinking.trim())o=t.defaultThinking.trim();else throw Error(`Subagent settings in '${e}' have invalid 'defaultThinking'; expected a non-empty string.`);let s;if(`defaultExtensions`in t){let n=t.defaultExtensions;if(!Array.isArray(n)||n.some(e=>typeof e!=`string`||!e.trim()))throw Error(`Subagent settings in '${e}' have invalid 'defaultExtensions'; expected an array of non-empty strings.`);s=n.map(e=>String(e).trim())}let u=l(t.modelScope,e),f={},p=c(t,E);if(p)for(let[t,n]of Object.entries(p)){let r=d(t,n,e);r&&(f[t]=r)}return{overrides:f,defaultModel:a,defaultThinking:o,defaultExtensions:s,disableBuiltins:r,disableThinking:i,modelScope:u}}function m(e,t,n,r){return r&&t.defaultModel!==void 0?{type:`subagents.defaultModel`,scope:`project`,path:r,model:t.defaultModel}:e.defaultModel===void 0?void 0:{type:`subagents.defaultModel`,scope:`user`,path:n,model:e.defaultModel}}function h(e,t){return t?e.map(e=>e.model===void 0?o(e,{...e,model:t.model,modelSource:t}):e):e}function g(e,t){return t===void 0?e:e.map(e=>e.fallbackModels===void 0?o(e,{...e,fallbackModels:[...t]}):e)}function _(e,t,n){return n&&t.defaultThinking!==void 0?t.defaultThinking:e.defaultThinking}function v(e,t){return t===void 0?e:e.map(e=>e.thinking===void 0?o(e,{...e,thinking:t}):e)}function y(e,t,n){return n&&t.defaultExtensions!==void 0?t.defaultExtensions:e.defaultExtensions}function b(e,t){return t===void 0?e:e.map(e=>e.extensions===void 0?o(e,{...e,extensions:[...t],extensionsFromDefault:!0}):e)}function x(e,t,n,r,i){return b(v(g(h(e,t),i),n),r)}function S(e){return{model:e.model,fallbackModels:e.fallbackModels?[...e.fallbackModels]:void 0,thinking:e.thinking,systemPromptMode:e.systemPromptMode,inheritProjectContext:e.inheritProjectContext,inheritSkills:e.inheritSkills,defaultContext:e.defaultContext,disabled:e.disabled,systemPrompt:e.systemPrompt,skills:e.skills?[...e.skills]:void 0,skillPath:e.skillPath?[...e.skillPath]:void 0,tools:e.tools?[...e.tools]:void 0,mcpDirectTools:e.mcpDirectTools?[...e.mcpDirectTools]:void 0,extensions:e.extensionsFromDefault?void 0:e.extensions?[...e.extensions]:void 0,subagentOnlyExtensions:e.subagentOnlyExtensions?[...e.subagentOnlyExtensions]:void 0,completionGuard:e.completionGuard,toolBudget:e.toolBudget}}function C(e,t,n){let r,i=!1,c=()=>(r??={...e},r),l=(t,n,r)=>{a(e,...n)||(c()[t]=r,i=!0)};if(t.model!==void 0&&!a(e,`model`)){let e=c();e.model=t.model===!1?void 0:t.model,e.modelSource=void 0,i=!0}if(t.fallbackModels!==void 0&&l(`fallbackModels`,[`fallbackModels`],t.fallbackModels===!1?void 0:[...t.fallbackModels]),t.thinking!==void 0&&l(`thinking`,[`thinking`],t.thinking===!1?void 0:t.thinking),t.systemPromptMode!==void 0&&l(`systemPromptMode`,[`systemPromptMode`],t.systemPromptMode),t.inheritProjectContext!==void 0&&l(`inheritProjectContext`,[`inheritProjectContext`],t.inheritProjectContext),t.inheritSkills!==void 0&&l(`inheritSkills`,[`inheritSkills`],t.inheritSkills),t.defaultContext!==void 0&&l(`defaultContext`,[`defaultContext`],t.defaultContext===!1?void 0:t.defaultContext),t.disabled!==void 0&&e.disabled===void 0&&(c().disabled=t.disabled,i=!0),t.skills!==void 0&&l(`skills`,[`skill`,`skills`],t.skills===!1?void 0:[...t.skills]),t.tools!==void 0&&!a(e,`tools`)){let{tools:e,mcpDirectTools:n}=s(t.tools===!1?[]:t.tools),r=c();r.tools=e,r.mcpDirectTools=n,i=!0}return t.extensions!==void 0&&l(`extensions`,[`extensions`],t.extensions===!1?void 0:[...t.extensions]),t.subagentOnlyExtensions!==void 0&&l(`subagentOnlyExtensions`,[`subagentOnlyExtensions`],t.subagentOnlyExtensions===!1?void 0:[...t.subagentOnlyExtensions]),t.completionGuard!==void 0&&l(`completionGuard`,[`completionGuard`],t.completionGuard),t.toolBudget!==void 0&&l(`toolBudget`,[`toolBudget`],t.toolBudget===!1?void 0:t.toolBudget),!i||!r?e:(r.override={...n,base:S(e)},o(e,r))}function w(e,t,n,r,i){return e.map(e=>{let a=n.overrides[e.name];if(a&&i)return C(e,a,{scope:`project`,path:i});let o=t.overrides[e.name];return o?C(e,o,{scope:`user`,path:r}):e})}var T,E,D,O,k,A=e((()=>{r(),t(),T=`subagents`,E=`agentOverrides`,D=`mcp:`,O={overrides:{}},k=new WeakMap}));A();export{O as EMPTY_SUBAGENT_SETTINGS,w as applyCustomAgentOverrides,x as applySubagentDefaults,A as init_settings,p as readSubagentSettings,y as resolveSubagentDefaultExtensions,m as resolveSubagentDefaultModel,_ as resolveSubagentDefaultThinking,i as setAgentFrontmatterFields};
|
|
2
2
|
//# sourceMappingURL=settings.mjs.map
|