feat(coding-agent): introduced fine grained per agent advisor configuration

- Replaced blanket subagent advisor global settings with fine-grained per-agent configuration and frontmatter support.
- Added dashboard keybindings and inline override editors for managing agent advisor patterns.
- Implemented settings migration logic to convert legacy global options into per-agent settings.
- Updated session persistence and execution layers to restore and enforce per-agent advisor behaviors.
This commit is contained in:
can1357
2026-08-13 04:59:50 +02:00
parent 1132c3e31c
commit e8eed95130
27 changed files with 586 additions and 91 deletions
+1
View File
@@ -27,6 +27,7 @@ interface AgentFrontmatter {
thinkingLevel?: string;
blocking?: boolean;
prewalk?: boolean | string;
advisor?: boolean | string;
}
interface EmbeddedAgentDef {
@@ -15,6 +15,7 @@ import { ModelRegistry } from "../config/model-registry";
import {
formatModelSelectorValue,
formatModelStringWithRouting,
resolveAgentAdvisorSelection,
resolveAgentPrewalkPattern,
resolveConfiguredModelPatterns,
resolveExplicitModelRole,
@@ -894,6 +895,9 @@ export function createSubagentSettings(
// the parent task approval is the authorization boundary. Use yolo mode
// to preserve unattended subagent execution. User `tools.approval` policies still apply.
"tools.approvalMode": "yolo",
// Subagents run unadvised by default; runSubprocess opts a spawn back in
// per agent (frontmatter `advisor` / `task.agentAdvisor`) via overrides.
"advisor.enabled": false,
...overrides,
},
{ storage: baseSettings.getStorage() },
@@ -2690,12 +2694,26 @@ export async function runSubprocess(options: ExecutorOptions): Promise<SingleRes
}
const settings = options.settings ?? Settings.isolated();
// Per-agent advisor: the agent definition's `advisor` frontmatter or the
// `task.agentAdvisor` settings override (agent name → "on"/"off"/model
// pattern) pairs the spawned session with an advisor. Subagents default to
// no advisor (createSubagentSettings forces `advisor.enabled` off); an
// explicit model pattern lands on the child's `modelRoles.advisor` so role
// aliases and `:level` suffixes resolve inside the spawned session.
const advisorSelection = resolveAgentAdvisorSelection({
settingsOverride: settings.get("task.agentAdvisor")[agent.name],
agentAdvisor: agent.advisor,
});
const subagentSettings = createSubagentSettings(
settings,
{
...(agent.readSummarize === false ? { "read.summarize.enabled": false } : undefined),
// Isolated runs must not expose roots outside the worktree.
...(worktree !== undefined ? { "workspace.additionalDirectories": [] } : undefined),
...(advisorSelection ? { "advisor.enabled": true } : undefined),
...(advisorSelection?.model
? { modelRoles: { ...settings.getModelRoles(), advisor: advisorSelection.model } }
: undefined),
},
options.parentServiceTier,
);
@@ -3197,6 +3215,7 @@ export async function runSubprocess(options: ExecutorOptions): Promise<SingleRes
readOnly: isReadOnlyAgent(agent),
spawns: spawnsEnv,
readSummarize: agent.readSummarize,
advisor: advisorSelection ? (advisorSelection.model ?? "on") : undefined,
outputSchema,
outputSchemaMode: options.outputSchemaMode,
restrictToolNames: restrictToolNames || undefined,
@@ -79,10 +79,21 @@ export function createPersistedSubagentReviverFactory(
taskDepth++;
parentId = registry.get(parentId)?.parentId;
}
const subagentSettings = createSubagentSettings(
ctx.settings,
init.readSummarize === false ? { "read.summarize.enabled": false } : undefined,
);
// Rebuild the same advisor opt-in the original spawn resolved: `"on"` =
// advisor-role model, anything else = the explicit pattern stamped onto
// this session's `modelRoles.advisor`. Absent = unadvised (the
// createSubagentSettings default).
const subagentSettings = createSubagentSettings(ctx.settings, {
...(init.readSummarize === false ? { "read.summarize.enabled": false } : undefined),
...(init.advisor
? {
"advisor.enabled": true,
...(init.advisor !== "on"
? { modelRoles: { ...ctx.settings.getModelRoles(), advisor: init.advisor } }
: undefined),
}
: undefined),
});
const persistedModelPattern =
init.modelRole && init.modelRole !== "default"
? [formatModelRoleAlias(init.modelRole), ...(init.resolvedModel ? [init.resolvedModel] : [])]
+2
View File
@@ -371,6 +371,8 @@ export interface AgentDefinition {
readSummarize?: boolean;
/** Prewalk hand-off for the spawned session: `true` = switch to the default prewalk target at the first edit/write, string = custom target model pattern. */
prewalk?: boolean | string;
/** Advisor for spawned sessions of this agent: `true` = advise with the default advisor-role model, string = advise with that model pattern (optional `:level` suffix). Absent/`false` = no advisor. */
advisor?: boolean | string;
source: AgentSource;
filePath?: string;
}