Enable & disable agents (#16225)

This commit is contained in:
Sehoon Shon
2026-01-14 19:30:17 -05:00
committed by GitHub
parent 42c26d1e1b
commit 4b2e9f7954
9 changed files with 800 additions and 13 deletions

View File

@@ -708,7 +708,6 @@ export async function loadCliConfig(
enableAgents: settings.experimental?.enableAgents,
skillsSupport: settings.experimental?.skills,
disabledSkills: settings.skills?.disabled,
experimentalJitContext: settings.experimental?.jitContext,
noBrowser: !!process.env['NO_BROWSER'],
summarizeToolOutput: settings.model?.summarizeToolOutput,
@@ -751,8 +750,7 @@ export async function loadCliConfig(
const refreshedSettings = loadSettings(cwd);
return {
disabledSkills: refreshedSettings.merged.skills?.disabled,
adminSkillsEnabled:
refreshedSettings.merged.admin?.skills?.enabled ?? adminSkillsEnabled,
agents: refreshedSettings.merged.agents,
};
},
});

View File

@@ -7,8 +7,20 @@
import { vi, describe, it, expect, beforeEach } from 'vitest';
import { agentsCommand } from './agentsCommand.js';
import { createMockCommandContext } from '../../test-utils/mockCommandContext.js';
import type { Config } from '@google/gemini-cli-core';
import type { Config, AgentOverride } from '@google/gemini-cli-core';
import type { LoadedSettings } from '../../config/settings.js';
import { MessageType } from '../types.js';
import { enableAgent, disableAgent } from '../../utils/agentSettings.js';
import { renderAgentActionFeedback } from '../../utils/agentUtils.js';
vi.mock('../../utils/agentSettings.js', () => ({
enableAgent: vi.fn(),
disableAgent: vi.fn(),
}));
vi.mock('../../utils/agentUtils.js', () => ({
renderAgentActionFeedback: vi.fn(),
}));
describe('agentsCommand', () => {
let mockContext: ReturnType<typeof createMockCommandContext>;
@@ -22,12 +34,18 @@ describe('agentsCommand', () => {
mockConfig = {
getAgentRegistry: vi.fn().mockReturnValue({
getAllDefinitions: vi.fn().mockReturnValue([]),
getAllAgentNames: vi.fn().mockReturnValue([]),
reload: vi.fn(),
}),
};
mockContext = createMockCommandContext({
services: {
config: mockConfig as unknown as Config,
settings: {
workspace: { path: '/mock/path' },
merged: { agents: { overrides: {} } },
} as unknown as LoadedSettings,
},
});
});
@@ -68,7 +86,12 @@ describe('agentsCommand', () => {
description: 'desc1',
kind: 'local',
},
{ name: 'agent2', description: 'desc2', kind: 'remote' },
{
name: 'agent2',
displayName: undefined,
description: 'desc2',
kind: 'remote',
},
];
mockConfig.getAgentRegistry().getAllDefinitions.mockReturnValue(mockAgents);
@@ -117,4 +140,178 @@ describe('agentsCommand', () => {
content: 'Agent registry not found.',
});
});
it('should enable an agent successfully', async () => {
const reloadSpy = vi.fn().mockResolvedValue(undefined);
mockConfig.getAgentRegistry = vi.fn().mockReturnValue({
getAllAgentNames: vi.fn().mockReturnValue([]),
reload: reloadSpy,
});
// Add agent to disabled overrides so validation passes
(
mockContext.services.settings.merged.agents!.overrides as Record<
string,
AgentOverride
>
)['test-agent'] = { disabled: true };
vi.mocked(enableAgent).mockReturnValue({
status: 'success',
agentName: 'test-agent',
action: 'enable',
modifiedScopes: [],
alreadyInStateScopes: [],
});
vi.mocked(renderAgentActionFeedback).mockReturnValue('Enabled test-agent.');
const enableCommand = agentsCommand.subCommands?.find(
(cmd) => cmd.name === 'enable',
);
expect(enableCommand).toBeDefined();
const result = await enableCommand!.action!(mockContext, 'test-agent');
expect(enableAgent).toHaveBeenCalledWith(
mockContext.services.settings,
'test-agent',
);
expect(reloadSpy).toHaveBeenCalled();
expect(mockContext.ui.addItem).toHaveBeenCalledWith(
expect.objectContaining({
type: MessageType.INFO,
text: 'Enabling test-agent...',
}),
);
expect(result).toEqual({
type: 'message',
messageType: 'info',
content: 'Enabled test-agent.',
});
});
it('should handle no-op when enabling an agent', async () => {
mockConfig
.getAgentRegistry()
.getAllAgentNames.mockReturnValue(['test-agent']);
const enableCommand = agentsCommand.subCommands?.find(
(cmd) => cmd.name === 'enable',
);
const result = await enableCommand!.action!(mockContext, 'test-agent');
expect(enableAgent).not.toHaveBeenCalled();
expect(mockContext.ui.addItem).not.toHaveBeenCalled();
expect(result).toEqual({
type: 'message',
messageType: 'info',
content: "Agent 'test-agent' is already enabled.",
});
});
it('should show usage error if no agent name provided for enable', async () => {
const enableCommand = agentsCommand.subCommands?.find(
(cmd) => cmd.name === 'enable',
);
const result = await enableCommand!.action!(mockContext, ' ');
expect(result).toEqual({
type: 'message',
messageType: 'error',
content: 'Usage: /agents enable <agent-name>',
});
});
it('should disable an agent successfully', async () => {
const reloadSpy = vi.fn().mockResolvedValue(undefined);
mockConfig.getAgentRegistry = vi.fn().mockReturnValue({
getAllAgentNames: vi.fn().mockReturnValue(['test-agent']),
reload: reloadSpy,
});
vi.mocked(disableAgent).mockReturnValue({
status: 'success',
agentName: 'test-agent',
action: 'disable',
modifiedScopes: [],
alreadyInStateScopes: [],
});
vi.mocked(renderAgentActionFeedback).mockReturnValue(
'Disabled test-agent.',
);
const disableCommand = agentsCommand.subCommands?.find(
(cmd) => cmd.name === 'disable',
);
expect(disableCommand).toBeDefined();
const result = await disableCommand!.action!(mockContext, 'test-agent');
expect(disableAgent).toHaveBeenCalledWith(
mockContext.services.settings,
'test-agent',
expect.anything(), // Scope is derived in the command
);
expect(reloadSpy).toHaveBeenCalled();
expect(mockContext.ui.addItem).toHaveBeenCalledWith(
expect.objectContaining({
type: MessageType.INFO,
text: 'Disabling test-agent...',
}),
);
expect(result).toEqual({
type: 'message',
messageType: 'info',
content: 'Disabled test-agent.',
});
});
it('should show info message if agent is already disabled', async () => {
mockConfig.getAgentRegistry().getAllAgentNames.mockReturnValue([]);
(
mockContext.services.settings.merged.agents!.overrides as Record<
string,
AgentOverride
>
)['test-agent'] = { disabled: true };
const disableCommand = agentsCommand.subCommands?.find(
(cmd) => cmd.name === 'disable',
);
const result = await disableCommand!.action!(mockContext, 'test-agent');
expect(disableAgent).not.toHaveBeenCalled();
expect(result).toEqual({
type: 'message',
messageType: 'info',
content: "Agent 'test-agent' is already disabled.",
});
});
it('should show error if agent is not found when disabling', async () => {
mockConfig.getAgentRegistry().getAllAgentNames.mockReturnValue([]);
const disableCommand = agentsCommand.subCommands?.find(
(cmd) => cmd.name === 'disable',
);
const result = await disableCommand!.action!(mockContext, 'test-agent');
expect(disableAgent).not.toHaveBeenCalled();
expect(result).toEqual({
type: 'message',
messageType: 'error',
content: "Agent 'test-agent' not found.",
});
});
it('should show usage error if no agent name provided for disable', async () => {
const disableCommand = agentsCommand.subCommands?.find(
(cmd) => cmd.name === 'disable',
);
const result = await disableCommand!.action!(mockContext, '');
expect(result).toEqual({
type: 'message',
messageType: 'error',
content: 'Usage: /agents disable <agent-name>',
});
});
});

View File

@@ -4,9 +4,17 @@
* SPDX-License-Identifier: Apache-2.0
*/
import type { SlashCommand, CommandContext } from './types.js';
import type {
SlashCommand,
CommandContext,
SlashCommandActionReturn,
} from './types.js';
import { CommandKind } from './types.js';
import { MessageType, type HistoryItemAgentsList } from '../types.js';
import { SettingScope } from '../../config/settings.js';
import type { AgentOverride } from '@google/gemini-cli-core';
import { disableAgent, enableAgent } from '../../utils/agentSettings.js';
import { renderAgentActionFeedback } from '../../utils/agentUtils.js';
const agentsListCommand: SlashCommand = {
name: 'list',
@@ -50,6 +58,197 @@ const agentsListCommand: SlashCommand = {
},
};
async function enableAction(
context: CommandContext,
args: string,
): Promise<SlashCommandActionReturn | void> {
const { config, settings } = context.services;
if (!config) return;
const agentName = args.trim();
if (!agentName) {
return {
type: 'message',
messageType: 'error',
content: 'Usage: /agents enable <agent-name>',
};
}
const agentRegistry = config.getAgentRegistry();
if (!agentRegistry) {
return {
type: 'message',
messageType: 'error',
content: 'Agent registry not found.',
};
}
const allAgents = agentRegistry.getAllAgentNames();
const overrides = (settings.merged.agents?.overrides ?? {}) as Record<
string,
AgentOverride
>;
const disabledAgents = Object.keys(overrides).filter(
(name) => overrides[name]?.disabled === true,
);
if (allAgents.includes(agentName)) {
return {
type: 'message',
messageType: 'info',
content: `Agent '${agentName}' is already enabled.`,
};
}
if (!disabledAgents.includes(agentName)) {
return {
type: 'message',
messageType: 'error',
content: `Agent '${agentName}' not found.`,
};
}
const result = enableAgent(settings, agentName);
if (result.status === 'no-op') {
return {
type: 'message',
messageType: 'info',
content: renderAgentActionFeedback(result, (l, p) => `${l} (${p})`),
};
}
context.ui.addItem({
type: MessageType.INFO,
text: `Enabling ${agentName}...`,
});
await agentRegistry.reload();
return {
type: 'message',
messageType: 'info',
content: renderAgentActionFeedback(result, (l, p) => `${l} (${p})`),
};
}
async function disableAction(
context: CommandContext,
args: string,
): Promise<SlashCommandActionReturn | void> {
const { config, settings } = context.services;
if (!config) return;
const agentName = args.trim();
if (!agentName) {
return {
type: 'message',
messageType: 'error',
content: 'Usage: /agents disable <agent-name>',
};
}
const agentRegistry = config.getAgentRegistry();
if (!agentRegistry) {
return {
type: 'message',
messageType: 'error',
content: 'Agent registry not found.',
};
}
const allAgents = agentRegistry.getAllAgentNames();
const overrides = (settings.merged.agents?.overrides ?? {}) as Record<
string,
AgentOverride
>;
const disabledAgents = Object.keys(overrides).filter(
(name) => overrides[name]?.disabled === true,
);
if (disabledAgents.includes(agentName)) {
return {
type: 'message',
messageType: 'info',
content: `Agent '${agentName}' is already disabled.`,
};
}
if (!allAgents.includes(agentName)) {
return {
type: 'message',
messageType: 'error',
content: `Agent '${agentName}' not found.`,
};
}
const scope = context.services.settings.workspace.path
? SettingScope.Workspace
: SettingScope.User;
const result = disableAgent(settings, agentName, scope);
if (result.status === 'no-op') {
return {
type: 'message',
messageType: 'info',
content: renderAgentActionFeedback(result, (l, p) => `${l} (${p})`),
};
}
context.ui.addItem({
type: MessageType.INFO,
text: `Disabling ${agentName}...`,
});
await agentRegistry.reload();
return {
type: 'message',
messageType: 'info',
content: renderAgentActionFeedback(result, (l, p) => `${l} (${p})`),
};
}
function completeAgentsToEnable(context: CommandContext, partialArg: string) {
const { config, settings } = context.services;
if (!config) return [];
const overrides = (settings.merged.agents?.overrides ?? {}) as Record<
string,
AgentOverride
>;
const disabledAgents = Object.entries(overrides)
.filter(([_, override]) => override?.disabled === true)
.map(([name]) => name);
return disabledAgents.filter((name) => name.startsWith(partialArg));
}
function completeAgentsToDisable(context: CommandContext, partialArg: string) {
const { config } = context.services;
if (!config) return [];
const agentRegistry = config.getAgentRegistry();
const allAgents = agentRegistry ? agentRegistry.getAllAgentNames() : [];
return allAgents.filter((name: string) => name.startsWith(partialArg));
}
const enableCommand: SlashCommand = {
name: 'enable',
description: 'Enable a disabled agent',
kind: CommandKind.BUILT_IN,
autoExecute: false,
action: enableAction,
completion: completeAgentsToEnable,
};
const disableCommand: SlashCommand = {
name: 'disable',
description: 'Disable an enabled agent',
kind: CommandKind.BUILT_IN,
autoExecute: false,
action: disableAction,
completion: completeAgentsToDisable,
};
const agentsRefreshCommand: SlashCommand = {
name: 'refresh',
description: 'Reload the agent registry',
@@ -84,7 +283,12 @@ export const agentsCommand: SlashCommand = {
name: 'agents',
description: 'Manage agents',
kind: CommandKind.BUILT_IN,
subCommands: [agentsListCommand, agentsRefreshCommand],
subCommands: [
agentsListCommand,
agentsRefreshCommand,
enableCommand,
disableCommand,
],
action: async (context: CommandContext, args) =>
// Default to list if no subcommand is provided
agentsListCommand.action!(context, args),

View File

@@ -0,0 +1,150 @@
/**
* @license
* Copyright 2026 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import {
SettingScope,
isLoadableSettingScope,
type LoadedSettings,
} from '../config/settings.js';
import type { ModifiedScope } from './skillSettings.js';
import type { AgentOverride } from '@google/gemini-cli-core';
export type AgentActionStatus = 'success' | 'no-op' | 'error';
/**
* Metadata representing the result of an agent settings operation.
*/
export interface AgentActionResult {
status: AgentActionStatus;
agentName: string;
action: 'enable' | 'disable';
/** Scopes where the agent's state was actually changed. */
modifiedScopes: ModifiedScope[];
/** Scopes where the agent was already in the desired state. */
alreadyInStateScopes: ModifiedScope[];
/** Error message if status is 'error'. */
error?: string;
}
/**
* Enables an agent by ensuring it is not disabled in any writable scope (User and Workspace).
* It sets `agents.overrides.<agentName>.disabled` to `false` if it was found to be `true`.
*/
export function enableAgent(
settings: LoadedSettings,
agentName: string,
): AgentActionResult {
const writableScopes = [SettingScope.Workspace, SettingScope.User];
const foundInDisabledScopes: ModifiedScope[] = [];
const alreadyEnabledScopes: ModifiedScope[] = [];
for (const scope of writableScopes) {
if (isLoadableSettingScope(scope)) {
const scopePath = settings.forScope(scope).path;
const agentOverrides = settings.forScope(scope).settings.agents
?.overrides as Record<string, AgentOverride> | undefined;
const isDisabled = agentOverrides?.[agentName]?.disabled === true;
if (isDisabled) {
foundInDisabledScopes.push({ scope, path: scopePath });
} else {
alreadyEnabledScopes.push({ scope, path: scopePath });
}
}
}
if (foundInDisabledScopes.length === 0) {
return {
status: 'no-op',
agentName,
action: 'enable',
modifiedScopes: [],
alreadyInStateScopes: alreadyEnabledScopes,
};
}
const modifiedScopes: ModifiedScope[] = [];
for (const { scope, path } of foundInDisabledScopes) {
if (isLoadableSettingScope(scope)) {
// Explicitly enable it to override any lower-precedence disables, or just clear the disable.
// Setting to false ensures it is enabled.
settings.setValue(scope, `agents.overrides.${agentName}.disabled`, false);
modifiedScopes.push({ scope, path });
}
}
return {
status: 'success',
agentName,
action: 'enable',
modifiedScopes,
alreadyInStateScopes: alreadyEnabledScopes,
};
}
/**
* Disables an agent by setting `agents.overrides.<agentName>.disabled` to `true` in the specified scope.
*/
export function disableAgent(
settings: LoadedSettings,
agentName: string,
scope: SettingScope,
): AgentActionResult {
if (!isLoadableSettingScope(scope)) {
return {
status: 'error',
agentName,
action: 'disable',
modifiedScopes: [],
alreadyInStateScopes: [],
error: `Invalid settings scope: ${scope}`,
};
}
const scopePath = settings.forScope(scope).path;
const agentOverrides = settings.forScope(scope).settings.agents?.overrides as
| Record<string, AgentOverride>
| undefined;
const isDisabled = agentOverrides?.[agentName]?.disabled === true;
if (isDisabled) {
return {
status: 'no-op',
agentName,
action: 'disable',
modifiedScopes: [],
alreadyInStateScopes: [{ scope, path: scopePath }],
};
}
// Check if it's already disabled in the other writable scope
const otherScope =
scope === SettingScope.Workspace
? SettingScope.User
: SettingScope.Workspace;
const alreadyDisabledInOther: ModifiedScope[] = [];
if (isLoadableSettingScope(otherScope)) {
const otherOverrides = settings.forScope(otherScope).settings.agents
?.overrides as Record<string, AgentOverride> | undefined;
if (otherOverrides?.[agentName]?.disabled === true) {
alreadyDisabledInOther.push({
scope: otherScope,
path: settings.forScope(otherScope).path,
});
}
}
settings.setValue(scope, `agents.overrides.${agentName}.disabled`, true);
return {
status: 'success',
agentName,
action: 'disable',
modifiedScopes: [{ scope, path: scopePath }],
alreadyInStateScopes: alreadyDisabledInOther,
};
}

View File

@@ -0,0 +1,150 @@
/**
* @license
* Copyright 2026 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import { describe, it, expect, vi } from 'vitest';
vi.mock('../config/settings.js', () => ({
SettingScope: {
User: 'User',
Workspace: 'Workspace',
System: 'System',
SystemDefaults: 'SystemDefaults',
},
}));
import { renderAgentActionFeedback } from './agentUtils.js';
import { SettingScope } from '../config/settings.js';
import type { AgentActionResult } from './agentSettings.js';
describe('agentUtils', () => {
describe('renderAgentActionFeedback', () => {
const mockFormatScope = (label: string, path: string) =>
`[${label}:${path}]`;
it('should return error message if status is error', () => {
const result: AgentActionResult = {
status: 'error',
agentName: 'my-agent',
action: 'enable',
modifiedScopes: [],
alreadyInStateScopes: [],
error: 'Something went wrong',
};
expect(renderAgentActionFeedback(result, mockFormatScope)).toBe(
'Something went wrong',
);
});
it('should return default error message if status is error and no error message provided', () => {
const result: AgentActionResult = {
status: 'error',
agentName: 'my-agent',
action: 'enable',
modifiedScopes: [],
alreadyInStateScopes: [],
};
expect(renderAgentActionFeedback(result, mockFormatScope)).toBe(
'An error occurred while attempting to enable agent "my-agent".',
);
});
it('should return no-op message for enable', () => {
const result: AgentActionResult = {
status: 'no-op',
agentName: 'my-agent',
action: 'enable',
modifiedScopes: [],
alreadyInStateScopes: [],
};
expect(renderAgentActionFeedback(result, mockFormatScope)).toBe(
'Agent "my-agent" is already enabled.',
);
});
it('should return no-op message for disable', () => {
const result: AgentActionResult = {
status: 'no-op',
agentName: 'my-agent',
action: 'disable',
modifiedScopes: [],
alreadyInStateScopes: [],
};
expect(renderAgentActionFeedback(result, mockFormatScope)).toBe(
'Agent "my-agent" is already disabled.',
);
});
it('should return success message for enable (single scope)', () => {
const result: AgentActionResult = {
status: 'success',
agentName: 'my-agent',
action: 'enable',
modifiedScopes: [
{ scope: SettingScope.User, path: '/path/to/user/settings' },
],
alreadyInStateScopes: [],
};
expect(renderAgentActionFeedback(result, mockFormatScope)).toBe(
'Agent "my-agent" enabled by setting it to enabled in [user:/path/to/user/settings] settings.',
);
});
it('should return success message for enable (two scopes)', () => {
const result: AgentActionResult = {
status: 'success',
agentName: 'my-agent',
action: 'enable',
modifiedScopes: [
{ scope: SettingScope.User, path: '/path/to/user/settings' },
],
alreadyInStateScopes: [
{
scope: SettingScope.Workspace,
path: '/path/to/workspace/settings',
},
],
};
expect(renderAgentActionFeedback(result, mockFormatScope)).toBe(
'Agent "my-agent" enabled by setting it to enabled in [user:/path/to/user/settings] and [project:/path/to/workspace/settings] settings.',
);
});
it('should return success message for disable (single scope)', () => {
const result: AgentActionResult = {
status: 'success',
agentName: 'my-agent',
action: 'disable',
modifiedScopes: [
{ scope: SettingScope.User, path: '/path/to/user/settings' },
],
alreadyInStateScopes: [],
};
expect(renderAgentActionFeedback(result, mockFormatScope)).toBe(
'Agent "my-agent" disabled by setting it to disabled in [user:/path/to/user/settings] settings.',
);
});
it('should return success message for disable (two scopes)', () => {
const result: AgentActionResult = {
status: 'success',
agentName: 'my-agent',
action: 'disable',
modifiedScopes: [
{ scope: SettingScope.User, path: '/path/to/user/settings' },
],
alreadyInStateScopes: [
{
scope: SettingScope.Workspace,
path: '/path/to/workspace/settings',
},
],
};
expect(renderAgentActionFeedback(result, mockFormatScope)).toBe(
'Agent "my-agent" is now disabled in both [user:/path/to/user/settings] and [project:/path/to/workspace/settings] settings.',
);
});
});
});

View File

@@ -0,0 +1,65 @@
/**
* @license
* Copyright 2026 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import { SettingScope } from '../config/settings.js';
import type { AgentActionResult } from './agentSettings.js';
/**
* Shared logic for building the core agent action message while allowing the
* caller to control how each scope and its path are rendered (e.g., bolding or
* dimming).
*
* This function ONLY returns the description of what happened. It is up to the
* caller to append any interface-specific guidance.
*/
export function renderAgentActionFeedback(
result: AgentActionResult,
formatScope: (label: string, path: string) => string,
): string {
const { agentName, action, status, error } = result;
if (status === 'error') {
return (
error ||
`An error occurred while attempting to ${action} agent "${agentName}".`
);
}
if (status === 'no-op') {
return `Agent "${agentName}" is already ${action === 'enable' ? 'enabled' : 'disabled'}.`;
}
const isEnable = action === 'enable';
const actionVerb = isEnable ? 'enabled' : 'disabled';
const preposition = isEnable
? 'by setting it to enabled in'
: 'by setting it to disabled in';
const formatScopeItem = (s: { scope: SettingScope; path: string }) => {
const label =
s.scope === SettingScope.Workspace ? 'project' : s.scope.toLowerCase();
return formatScope(label, s.path);
};
const totalAffectedScopes = [
...result.modifiedScopes,
...result.alreadyInStateScopes,
];
if (totalAffectedScopes.length === 2) {
const s1 = formatScopeItem(totalAffectedScopes[0]);
const s2 = formatScopeItem(totalAffectedScopes[1]);
if (isEnable) {
return `Agent "${agentName}" ${actionVerb} ${preposition} ${s1} and ${s2} settings.`;
} else {
return `Agent "${agentName}" is now disabled in both ${s1} and ${s2} settings.`;
}
}
const s = formatScopeItem(totalAffectedScopes[0]);
return `Agent "${agentName}" ${actionVerb} ${preposition} ${s} settings.`;
}

View File

@@ -64,7 +64,7 @@ export class A2AClientManager {
agentCardUrl: string,
authHandler?: AuthenticationHandler,
): Promise<AgentCard> {
if (this.clients.has(name)) {
if (this.clients.has(name) && this.agentCards.has(name)) {
throw new Error(`Agent with name '${name}' is already loaded.`);
}

View File

@@ -69,6 +69,7 @@ export class AgentRegistry {
*/
async reload(): Promise<void> {
A2AClientManager.getInstance().clearCache();
await this.config.reloadAgents();
this.agents.clear();
await this.loadAgents();
coreEvents.emitAgentsRefreshed();
@@ -143,9 +144,14 @@ export class AgentRegistry {
private loadBuiltInAgents(): void {
const investigatorSettings = this.config.getCodebaseInvestigatorSettings();
const cliHelpSettings = this.config.getCliHelpAgentSettings();
const agentsSettings = this.config.getAgentsSettings();
const agentsOverrides = agentsSettings.overrides ?? {};
// Only register the agent if it's enabled in the settings.
if (investigatorSettings?.enabled) {
// Only register the agent if it's enabled in the settings and not explicitly disabled via overrides.
if (
investigatorSettings?.enabled &&
!agentsOverrides[CodebaseInvestigatorAgent.name]?.disabled
) {
let model;
const settingsModel = investigatorSettings.model;
// Check if the user explicitly set a model in the settings.
@@ -189,8 +195,11 @@ export class AgentRegistry {
this.registerLocalAgent(agentDef);
}
// Register the CLI help agent if it's explicitly enabled.
if (cliHelpSettings.enabled) {
// Register the CLI help agent if it's explicitly enabled and not explicitly disabled via overrides.
if (
cliHelpSettings.enabled &&
!agentsOverrides[CliHelpAgent.name]?.disabled
) {
this.registerLocalAgent(CliHelpAgent(this.config));
}
}

View File

@@ -387,6 +387,7 @@ export interface ConfigParameters {
onReload?: () => Promise<{
disabledSkills?: string[];
adminSkillsEnabled?: boolean;
agents?: AgentSettings;
}>;
}
@@ -518,11 +519,12 @@ export class Config {
| (() => Promise<{
disabledSkills?: string[];
adminSkillsEnabled?: boolean;
agents?: AgentSettings;
}>)
| undefined;
private readonly enableAgents: boolean;
private readonly agents: AgentSettings;
private agents: AgentSettings;
private readonly skillsSupport: boolean;
private disabledSkills: string[];
private readonly adminSkillsEnabled: boolean;
@@ -1634,6 +1636,18 @@ export class Config {
await this.updateSystemInstructionIfInitialized();
}
/**
* Reloads agent settings.
*/
async reloadAgents(): Promise<void> {
if (this.onReload) {
const refreshed = await this.onReload();
if (refreshed.agents) {
this.agents = refreshed.agents;
}
}
}
isInteractive(): boolean {
return this.interactive;
}