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
@@ -1436,7 +1436,6 @@ export class AgentSession {
onPayload: this.#onPayload,
onResponse: this.#onResponse,
onSseEvent: this.#onSseEvent,
agentKind: () => this.#agentKind,
isDisposed: () => this.#isDisposed,
abortInProgress: () => this.#abortInProgress,
allowAgentInitiatedTurns: () => this.#allowAcpAgentInitiatedTurns,
@@ -9188,9 +9187,10 @@ export class AgentSession {
/**
* Whether a live advisor agent is attached to this session. True only when
* `advisor.enabled` is set AND a model resolved for the `advisor` role AND
* the advisor applies to this agent kind — i.e. the actual runtime exists,
* not merely the setting. Drives the status-line badge and `/dump advisor`.
* `advisor.enabled` is set for this session (subagents opt in per agent via
* frontmatter `advisor` / `task.agentAdvisor`) AND a model resolved for the
* `advisor` role — i.e. the actual runtime exists, not merely the setting.
* Drives the status-line badge and `/dump advisor`.
*/
isAdvisorActive(): boolean {
return this.#advisors.isAdvisorActive();
@@ -242,7 +242,6 @@ export interface SessionAdvisorsHost {
onPayload: SimpleStreamOptions["onPayload"] | undefined;
onResponse: SimpleStreamOptions["onResponse"] | undefined;
onSseEvent: SimpleStreamOptions["onSseEvent"] | undefined;
agentKind(): "main" | "sub";
isDisposed(): boolean;
abortInProgress(): boolean;
allowAgentInitiatedTurns(): boolean;
@@ -657,7 +656,6 @@ export class SessionAdvisors {
if (this.#host.isDisposed()) return false;
if (this.#advisors.length > 0) return true;
if (!this.#advisorEnabled) return false;
if (this.#host.agentKind() !== "main" && !this.#host.settings.get("advisor.subagents")) return false;
// Rebuild the status map from scratch so removed/renamed advisors don't
// leave stale entries. #resolveAdvisorRuntimeDescriptors populates every
@@ -1626,9 +1624,10 @@ export class SessionAdvisors {
/**
* Whether a live advisor agent is attached to this session. True only when
* `advisor.enabled` is set AND a model resolved for the `advisor` role AND
* the advisor applies to this agent kind — i.e. the actual runtime exists,
* not merely the setting. Drives the status-line badge and `/dump advisor`.
* `advisor.enabled` is set for this session (subagents opt in per agent via
* frontmatter `advisor` / `task.agentAdvisor`) AND a model resolved for the
* `advisor` role — i.e. the actual runtime exists, not merely the setting.
* Drives the status-line badge and `/dump advisor`.
*/
isAdvisorActive(): boolean {
return this.#advisors.length > 0;
@@ -227,6 +227,8 @@ export interface SessionInitEntry extends SessionEntryBase {
spawns?: string;
/** The agent's `readSummarize` setting (`false` = read summarization disabled); absent uses the session default. */
readSummarize?: boolean;
/** Effective advisor for this subagent: `"on"` = advisor-role model, else an explicit model pattern; absent = unadvised. */
advisor?: string;
}
/** Mode change entry - tracks agent mode transitions (e.g. plan mode). */
@@ -2142,6 +2142,7 @@ export class SessionManager {
restrictToolNames?: boolean;
spawns?: string;
readSummarize?: boolean;
advisor?: string;
}): string {
const entry: SessionInitEntry = { type: "session_init", ...this.#freshEntryFields(), ...init };
this.#recordEntry(entry);
@@ -2634,6 +2635,7 @@ export class SessionManager {
restrictToolNames?: boolean;
spawns?: string;
readSummarize?: boolean;
advisor?: string;
} | null;
} | null> {
let loaded: FileEntry[];
@@ -2657,6 +2659,7 @@ export class SessionManager {
restrictToolNames?: boolean;
spawns?: string;
readSummarize?: boolean;
advisor?: string;
} | null = null;
for (let index = loaded.length - 1; index >= 0; index--) {
const entry = loaded[index];
@@ -2673,6 +2676,7 @@ export class SessionManager {
restrictToolNames: entry.restrictToolNames,
readSummarize: entry.readSummarize,
spawns: entry.spawns,
advisor: entry.advisor,
};
break;
}