Merge remote-tracking branch 'origin/main' into fix/pr-automation-permissions

This commit is contained in:
Bryan Morgan
2026-03-15 14:17:53 -04:00
46 changed files with 1653 additions and 161 deletions
+1
View File
@@ -152,6 +152,7 @@ they appear in the UI.
| Plan | `experimental.plan` | Enable Plan Mode. | `true` |
| Model Steering | `experimental.modelSteering` | Enable model steering (user hints) to guide the model during tool execution. | `false` |
| Direct Web Fetch | `experimental.directWebFetch` | Enable web fetch behavior that bypasses LLM summarization. | `false` |
| Topic & Update Narration | `experimental.topicUpdateNarration` | Enable the experimental Topic & Update communication model for reduced chattiness and structured progress reporting. | `false` |
### Skills
+146
View File
@@ -677,6 +677,141 @@ their corresponding top-level category object in your `settings.json` file.
used.
- **Default:** `[]`
- **`modelConfigs.modelDefinitions`** (object):
- **Description:** Registry of model metadata, including tier, family, and
features.
- **Default:**
```json
{
"gemini-3.1-pro-preview": {
"tier": "pro",
"family": "gemini-3",
"isPreview": true,
"dialogLocation": "manual",
"features": {
"thinking": true,
"multimodalToolUse": true
}
},
"gemini-3.1-pro-preview-customtools": {
"tier": "pro",
"family": "gemini-3",
"isPreview": true,
"features": {
"thinking": true,
"multimodalToolUse": true
}
},
"gemini-3-pro-preview": {
"tier": "pro",
"family": "gemini-3",
"isPreview": true,
"dialogLocation": "manual",
"features": {
"thinking": true,
"multimodalToolUse": true
}
},
"gemini-3-flash-preview": {
"tier": "flash",
"family": "gemini-3",
"isPreview": true,
"dialogLocation": "manual",
"features": {
"thinking": false,
"multimodalToolUse": true
}
},
"gemini-2.5-pro": {
"tier": "pro",
"family": "gemini-2.5",
"isPreview": false,
"dialogLocation": "manual",
"features": {
"thinking": false,
"multimodalToolUse": false
}
},
"gemini-2.5-flash": {
"tier": "flash",
"family": "gemini-2.5",
"isPreview": false,
"dialogLocation": "manual",
"features": {
"thinking": false,
"multimodalToolUse": false
}
},
"gemini-2.5-flash-lite": {
"tier": "flash-lite",
"family": "gemini-2.5",
"isPreview": false,
"dialogLocation": "manual",
"features": {
"thinking": false,
"multimodalToolUse": false
}
},
"auto": {
"tier": "auto",
"isPreview": true,
"features": {
"thinking": true,
"multimodalToolUse": false
}
},
"pro": {
"tier": "pro",
"isPreview": false,
"features": {
"thinking": true,
"multimodalToolUse": false
}
},
"flash": {
"tier": "flash",
"isPreview": false,
"features": {
"thinking": false,
"multimodalToolUse": false
}
},
"flash-lite": {
"tier": "flash-lite",
"isPreview": false,
"features": {
"thinking": false,
"multimodalToolUse": false
}
},
"auto-gemini-3": {
"displayName": "Auto (Gemini 3)",
"tier": "auto",
"isPreview": true,
"dialogLocation": "main",
"dialogDescription": "Let Gemini CLI decide the best model for the task: gemini-3.1-pro, gemini-3-flash",
"features": {
"thinking": true,
"multimodalToolUse": false
}
},
"auto-gemini-2.5": {
"displayName": "Auto (Gemini 2.5)",
"tier": "auto",
"isPreview": false,
"dialogLocation": "main",
"dialogDescription": "Let Gemini CLI decide the best model for the task: gemini-2.5-pro, gemini-2.5-flash",
"features": {
"thinking": false,
"multimodalToolUse": false
}
}
}
```
- **Requires restart:** Yes
#### `agents`
- **`agents.overrides`** (object):
@@ -1091,6 +1226,12 @@ their corresponding top-level category object in your `settings.json` file.
- **Default:** `false`
- **Requires restart:** Yes
- **`experimental.dynamicModelConfiguration`** (boolean):
- **Description:** Enable dynamic model configuration (definitions,
resolutions, and chains) via settings.
- **Default:** `false`
- **Requires restart:** Yes
- **`experimental.gemmaModelRouter.enabled`** (boolean):
- **Description:** Enable the Gemma Model Router (experimental). Requires a
local endpoint serving Gemma via the Gemini API using LiteRT-LM shim.
@@ -1108,6 +1249,11 @@ their corresponding top-level category object in your `settings.json` file.
- **Default:** `"gemma3-1b-gpu-custom"`
- **Requires restart:** Yes
- **`experimental.topicUpdateNarration`** (boolean):
- **Description:** Enable the experimental Topic & Update communication model
for reduced chattiness and structured progress reporting.
- **Default:** `false`
#### `skills`
- **`skills.enabled`** (boolean):
+37
View File
@@ -729,6 +729,43 @@ tools. The model will automatically:
The MCP integration tracks several states:
#### Overriding extension configurations
If an MCP server is provided by an extension (for example, the
`google-workspace` extension), you can still override its settings in your local
`settings.json`. Gemini CLI merges your local configuration with the extension's
defaults:
- **Tool lists:** Tool lists are merged securely to ensure the most restrictive
policy wins:
- **Exclusions (`excludeTools`):** Arrays are combined (unioned). If either
source blocks a tool, it remains disabled.
- **Inclusions (`includeTools`):** Arrays are intersected. If both sources
provide an allowlist, only tools present in **both** lists are enabled. If
only one source provides an allowlist, that list is respected.
- **Precedence:** `excludeTools` always takes precedence over `includeTools`.
This ensures you always have veto power over tools provided by an extension
and that an extension cannot re-enable tools you have omitted from your
personal allowlist.
- **Environment variables:** The `env` objects are merged. If the same variable
is defined in both places, your local value takes precedence.
- **Scalar properties:** Properties like `command`, `url`, and `timeout` are
replaced by your local values if provided.
**Example override:**
```json
{
"mcpServers": {
"google-workspace": {
"excludeTools": ["gmail.send"]
}
}
}
```
#### Server status (`MCPServerStatus`)
- **`DISCONNECTED`:** Server is not connected or has errors
+2
View File
@@ -1004,6 +1004,7 @@ export class Session {
callId,
toolResult.llmContent,
this.config.getActiveModel(),
this.config,
),
resultDisplay: toolResult.returnDisplay,
error: undefined,
@@ -1017,6 +1018,7 @@ export class Session {
callId,
toolResult.llmContent,
this.config.getActiveModel(),
this.config,
);
} catch (e) {
const error = e instanceof Error ? e : new Error(String(e));
+6 -3
View File
@@ -496,9 +496,10 @@ export async function loadCliConfig(
const experimentalJitContext = settings.experimental?.jitContext ?? false;
let extensionRegistryURI: string | undefined = trustedFolder
? settings.experimental?.extensionRegistryURI
: undefined;
let extensionRegistryURI =
process.env['GEMINI_CLI_EXTENSION_REGISTRY_URI'] ??
(trustedFolder ? settings.experimental?.extensionRegistryURI : undefined);
if (extensionRegistryURI && !extensionRegistryURI.startsWith('http')) {
extensionRegistryURI = resolveToRealPath(
path.resolve(cwd, resolvePath(extensionRegistryURI)),
@@ -813,6 +814,7 @@ export async function loadCliConfig(
disabledSkills: settings.skills?.disabled,
experimentalJitContext: settings.experimental?.jitContext,
modelSteering: settings.experimental?.modelSteering,
topicUpdateNarration: settings.experimental?.topicUpdateNarration,
toolOutputMasking: settings.experimental?.toolOutputMasking,
noBrowser: !!process.env['NO_BROWSER'],
summarizeToolOutput: settings.model?.summarizeToolOutput,
@@ -847,6 +849,7 @@ export async function loadCliConfig(
disableLLMCorrection: settings.tools?.disableLLMCorrection,
rawOutput: argv.rawOutput,
acceptRawOutputRisk: argv.acceptRawOutputRisk,
dynamicModelConfiguration: settings.experimental?.dynamicModelConfiguration,
modelConfigServiceConfig: settings.modelConfigs,
// TODO: loading of hooks based on workspace trust
enableHooks: settings.hooksConfig.enabled,
+8 -6
View File
@@ -898,9 +898,10 @@ Would you like to attempt to install via "git clone" instead?`,
let skills = await loadSkillsFromDir(
path.join(effectiveExtensionPath, 'skills'),
);
skills = skills.map((skill) =>
recursivelyHydrateStrings(skill, hydrationContext),
);
skills = skills.map((skill) => ({
...recursivelyHydrateStrings(skill, hydrationContext),
extensionName: config.name,
}));
let rules: PolicyRule[] | undefined;
let checkers: SafetyCheckerRule[] | undefined;
@@ -923,9 +924,10 @@ Would you like to attempt to install via "git clone" instead?`,
const agentLoadResult = await loadAgentsFromDirectory(
path.join(effectiveExtensionPath, 'agents'),
);
agentLoadResult.agents = agentLoadResult.agents.map((agent) =>
recursivelyHydrateStrings(agent, hydrationContext),
);
agentLoadResult.agents = agentLoadResult.agents.map((agent) => ({
...recursivelyHydrateStrings(agent, hydrationContext),
extensionName: config.name,
}));
// Log errors but don't fail the entire extension load
for (const error of agentLoadResult.errors) {
+53 -1
View File
@@ -1039,6 +1039,20 @@ const SETTINGS_SCHEMA = {
'Apply specific configuration overrides based on matches, with a primary key of model (or alias). The most specific match will be used.',
showInDialog: false,
},
modelDefinitions: {
type: 'object',
label: 'Model Definitions',
category: 'Model',
requiresRestart: true,
default: DEFAULT_MODEL_CONFIGS.modelDefinitions,
description:
'Registry of model metadata, including tier, family, and features.',
showInDialog: false,
additionalProperties: {
type: 'object',
ref: 'ModelDefinition',
},
},
},
},
@@ -1943,6 +1957,16 @@ const SETTINGS_SCHEMA = {
'Enable web fetch behavior that bypasses LLM summarization.',
showInDialog: true,
},
dynamicModelConfiguration: {
type: 'boolean',
label: 'Dynamic Model Configuration',
category: 'Experimental',
requiresRestart: true,
default: false,
description:
'Enable dynamic model configuration (definitions, resolutions, and chains) via settings.',
showInDialog: false,
},
gemmaModelRouter: {
type: 'object',
label: 'Gemma Model Router',
@@ -1994,9 +2018,18 @@ const SETTINGS_SCHEMA = {
},
},
},
topicUpdateNarration: {
type: 'boolean',
label: 'Topic & Update Narration',
category: 'Experimental',
requiresRestart: false,
default: false,
description:
'Enable the experimental Topic & Update communication model for reduced chattiness and structured progress reporting.',
showInDialog: true,
},
},
},
extensions: {
type: 'object',
label: 'Extensions',
@@ -2760,6 +2793,25 @@ export const SETTINGS_SCHEMA_DEFINITIONS: Record<
},
},
},
ModelDefinition: {
type: 'object',
description: 'Model metadata registry entry.',
properties: {
displayName: { type: 'string' },
tier: { enum: ['pro', 'flash', 'flash-lite', 'custom', 'auto'] },
family: { type: 'string' },
isPreview: { type: 'boolean' },
dialogLocation: { enum: ['main', 'manual'] },
dialogDescription: { type: 'string' },
features: {
type: 'object',
properties: {
thinking: { type: 'boolean' },
multimodalToolUse: { type: 'boolean' },
},
},
},
},
};
export function getSettingsSchema(): SettingsSchemaType {
@@ -122,4 +122,16 @@ describe('SkillCommandLoader', () => {
const actionResult = (await commands[0].action!({} as any, '')) as any;
expect(actionResult.toolArgs).toEqual({ name: 'my awesome skill' });
});
it('should propagate extensionName to the generated slash command', async () => {
const mockSkills = [
{ name: 'skill1', description: 'desc', extensionName: 'ext1' },
];
mockSkillManager.getDisplayableSkills.mockReturnValue(mockSkills);
const loader = new SkillCommandLoader(mockConfig);
const commands = await loader.loadCommands(new AbortController().signal);
expect(commands[0].extensionName).toBe('ext1');
});
});
@@ -41,6 +41,7 @@ export class SkillCommandLoader implements ICommandLoader {
description: skill.description || `Activate the ${skill.name} skill`,
kind: CommandKind.SKILL,
autoExecute: true,
extensionName: skill.extensionName,
action: async (_context, args) => ({
type: 'tool',
toolName: ACTIVATE_SKILL_TOOL_NAME,
@@ -172,4 +172,23 @@ describe('SlashCommandConflictHandler', () => {
vi.advanceTimersByTime(600);
expect(coreEvents.emitFeedback).not.toHaveBeenCalled();
});
it('should display a descriptive message for a skill conflict', () => {
simulateEvent([
{
name: 'chat',
renamedTo: 'google-workspace.chat',
loserExtensionName: 'google-workspace',
loserKind: CommandKind.SKILL,
winnerKind: CommandKind.BUILT_IN,
},
]);
vi.advanceTimersByTime(600);
expect(coreEvents.emitFeedback).toHaveBeenCalledWith(
'info',
"Extension 'google-workspace' skill '/chat' was renamed to '/google-workspace.chat' because it conflicts with built-in command.",
);
});
});
@@ -154,6 +154,10 @@ export class SlashCommandConflictHandler {
return extensionName
? `extension '${extensionName}' command`
: 'extension command';
case CommandKind.SKILL:
return extensionName
? `extension '${extensionName}' skill`
: 'skill command';
case CommandKind.MCP_PROMPT:
return mcpServerName
? `MCP server '${mcpServerName}' command`
@@ -173,5 +173,30 @@ describe('SlashCommandResolver', () => {
expect(finalCommands.find((c) => c.name === 'gcp.deploy1')).toBeDefined();
});
it('should prefix skills with extension name when they conflict with built-in', () => {
const builtin = createMockCommand('chat', CommandKind.BUILT_IN);
const skill = {
...createMockCommand('chat', CommandKind.SKILL),
extensionName: 'google-workspace',
};
const { finalCommands } = SlashCommandResolver.resolve([builtin, skill]);
const names = finalCommands.map((c) => c.name);
expect(names).toContain('chat');
expect(names).toContain('google-workspace.chat');
});
it('should NOT prefix skills with "skill" when extension name is missing', () => {
const builtin = createMockCommand('chat', CommandKind.BUILT_IN);
const skill = createMockCommand('chat', CommandKind.SKILL);
const { finalCommands } = SlashCommandResolver.resolve([builtin, skill]);
const names = finalCommands.map((c) => c.name);
expect(names).toContain('chat');
expect(names).toContain('chat1');
});
});
});
@@ -174,6 +174,7 @@ export class SlashCommandResolver {
private static getPrefix(cmd: SlashCommand): string | undefined {
switch (cmd.kind) {
case CommandKind.EXTENSION_FILE:
case CommandKind.SKILL:
return cmd.extensionName;
case CommandKind.MCP_PROMPT:
return cmd.mcpServerName;
@@ -185,7 +186,6 @@ export class SlashCommandResolver {
return undefined;
}
}
/**
* Logs a conflict event.
*/
@@ -27,6 +27,7 @@ import {
} from '../utils/displayUtils.js';
import { computeSessionStats } from '../utils/computeStats.js';
import {
type Config,
type RetrieveUserQuotaResponse,
isActiveModel,
getDisplayString,
@@ -88,13 +89,16 @@ const Section: React.FC<SectionProps> = ({ title, children }) => (
// Logic for building the unified list of table rows
const buildModelRows = (
models: Record<string, ModelMetrics>,
config: Config,
quotas?: RetrieveUserQuotaResponse,
useGemini3_1 = false,
useCustomToolModel = false,
) => {
const getBaseModelName = (name: string) => name.replace('-001', '');
const usedModelNames = new Set(
Object.keys(models).map(getBaseModelName).map(getDisplayString),
Object.keys(models)
.map(getBaseModelName)
.map((name) => getDisplayString(name, config)),
);
// 1. Models with active usage
@@ -104,7 +108,7 @@ const buildModelRows = (
const inputTokens = metrics.tokens.input;
return {
key: name,
modelName: getDisplayString(modelName),
modelName: getDisplayString(modelName, config),
requests: metrics.api.totalRequests,
cachedTokens: cachedTokens.toLocaleString(),
inputTokens: inputTokens.toLocaleString(),
@@ -121,11 +125,11 @@ const buildModelRows = (
(b) =>
b.modelId &&
isActiveModel(b.modelId, useGemini3_1, useCustomToolModel) &&
!usedModelNames.has(getDisplayString(b.modelId)),
!usedModelNames.has(getDisplayString(b.modelId, config)),
)
.map((bucket) => ({
key: bucket.modelId!,
modelName: getDisplayString(bucket.modelId!),
modelName: getDisplayString(bucket.modelId!, config),
requests: '-',
cachedTokens: '-',
inputTokens: '-',
@@ -139,6 +143,7 @@ const buildModelRows = (
const ModelUsageTable: React.FC<{
models: Record<string, ModelMetrics>;
config: Config;
quotas?: RetrieveUserQuotaResponse;
cacheEfficiency: number;
totalCachedTokens: number;
@@ -150,6 +155,7 @@ const ModelUsageTable: React.FC<{
useCustomToolModel?: boolean;
}> = ({
models,
config,
quotas,
cacheEfficiency,
totalCachedTokens,
@@ -162,7 +168,13 @@ const ModelUsageTable: React.FC<{
}) => {
const { stdout } = useStdout();
const terminalWidth = stdout?.columns ?? 84;
const rows = buildModelRows(models, quotas, useGemini3_1, useCustomToolModel);
const rows = buildModelRows(
models,
config,
quotas,
useGemini3_1,
useCustomToolModel,
);
if (rows.length === 0) {
return null;
@@ -676,6 +688,7 @@ export const StatsDisplay: React.FC<StatsDisplayProps> = ({
</Section>
<ModelUsageTable
models={models}
config={config}
quotas={quotas}
cacheEfficiency={computed.cacheEfficiency}
totalCachedTokens={computed.totalCachedTokens}
@@ -325,9 +325,9 @@ export const useSlashCommandProcessor = (
(async () => {
const commandService = await CommandService.create(
[
new BuiltinCommandLoader(config),
new SkillCommandLoader(config),
new McpPromptLoader(config),
new BuiltinCommandLoader(config),
new FileCommandLoader(config),
],
controller.signal,
@@ -120,4 +120,25 @@ describe('agent-scheduler', () => {
expect(schedulerConfig.toolRegistry).toBe(agentRegistry);
expect(schedulerConfig.toolRegistry).not.toBe(mainRegistry);
});
it('should create an AgentLoopContext that has a defined .config property', async () => {
const mockConfig = {
messageBus: mockMessageBus,
toolRegistry: mockToolRegistry,
promptId: 'test-prompt',
} as unknown as Mocked<Config>;
const options = {
schedulerId: 'subagent-1',
toolRegistry: mockToolRegistry as unknown as ToolRegistry,
signal: new AbortController().signal,
};
await scheduleAgentTools(mockConfig as unknown as Config, [], options);
const schedulerContext = vi.mocked(Scheduler).mock.calls[0][0].context;
expect(schedulerContext.config).toBeDefined();
expect(schedulerContext.config.promptId).toBe('test-prompt');
expect(schedulerContext.toolRegistry).toBe(mockToolRegistry);
});
});
+10 -1
View File
@@ -67,8 +67,17 @@ export async function scheduleAgentTools(
configurable: true,
});
const schedulerContext = {
config: agentConfig,
promptId: config.promptId,
toolRegistry,
messageBus: toolRegistry.messageBus,
geminiClient: config.geminiClient,
sandboxManager: config.sandboxManager,
};
const scheduler = new Scheduler({
context: agentConfig,
context: schedulerContext,
messageBus: toolRegistry.messageBus,
getPreferredEditor: getPreferredEditor ?? (() => undefined),
schedulerId,
@@ -122,7 +122,7 @@ export const BrowserAgentDefinition = (
): LocalAgentDefinition<typeof BrowserTaskResultSchema> => {
// Use Preview Flash model if the main model is any of the preview models.
// If the main model is not a preview model, use the default flash model.
const model = isPreviewModel(config.getModel())
const model = isPreviewModel(config.getModel(), config)
? PREVIEW_GEMINI_FLASH_MODEL
: DEFAULT_GEMINI_FLASH_MODEL;
@@ -54,19 +54,21 @@ export function resolvePolicyChain(
useCustomToolModel,
hasAccessToPreview,
);
const isAutoPreferred = preferredModel ? isAutoModel(preferredModel) : false;
const isAutoConfigured = isAutoModel(configuredModel);
const isAutoPreferred = preferredModel
? isAutoModel(preferredModel, config)
: false;
const isAutoConfigured = isAutoModel(configuredModel, config);
if (resolvedModel === DEFAULT_GEMINI_FLASH_LITE_MODEL) {
chain = getFlashLitePolicyChain();
} else if (
isGemini3Model(resolvedModel) ||
isGemini3Model(resolvedModel, config) ||
isAutoPreferred ||
isAutoConfigured
) {
if (hasAccessToPreview) {
const previewEnabled =
isGemini3Model(resolvedModel) ||
isGemini3Model(resolvedModel, config) ||
preferredModel === PREVIEW_GEMINI_MODEL_AUTO ||
configuredModel === PREVIEW_GEMINI_MODEL_AUTO;
chain = getModelPolicyChain({
+2 -1
View File
@@ -480,6 +480,7 @@ describe('oauth2', () => {
expect(fs.existsSync(googleAccountPath)).toBe(true);
if (fs.existsSync(googleAccountPath)) {
const cachedGoogleAccount = fs.readFileSync(googleAccountPath, 'utf-8');
expect(JSON.parse(cachedGoogleAccount)).toEqual({
active: 'test-user-code-account@gmail.com',
old: [],
@@ -1349,7 +1350,7 @@ describe('oauth2', () => {
let dataHandler: ((data: Buffer) => void) | undefined;
await vi.waitFor(() => {
const dataCall = stdinOnSpy.mock.calls.find(
(call: [string, ...unknown[]]) => call[0] === 'data',
(call: [string | symbol, ...unknown[]]) => call[0] === 'data',
);
dataHandler = dataCall?.[1] as ((data: Buffer) => void) | undefined;
if (!dataHandler) throw new Error('stdin handler not registered yet');
+56 -32
View File
@@ -609,6 +609,7 @@ export interface ConfigParameters {
disableAlwaysAllow?: boolean;
rawOutput?: boolean;
acceptRawOutputRisk?: boolean;
dynamicModelConfiguration?: boolean;
modelConfigServiceConfig?: ModelConfigServiceConfig;
enableHooks?: boolean;
enableHooksUI?: boolean;
@@ -622,6 +623,7 @@ export interface ConfigParameters {
disabledSkills?: string[];
adminSkillsEnabled?: boolean;
experimentalJitContext?: boolean;
topicUpdateNarration?: boolean;
toolOutputMasking?: Partial<ToolOutputMaskingConfig>;
disableLLMCorrection?: boolean;
plan?: boolean;
@@ -809,6 +811,7 @@ export class Config implements McpContext, AgentLoopContext {
private readonly disableAlwaysAllow: boolean;
private readonly rawOutput: boolean;
private readonly acceptRawOutputRisk: boolean;
private readonly dynamicModelConfiguration: boolean;
private pendingIncludeDirectories: string[];
private readonly enableHooks: boolean;
private readonly enableHooksUI: boolean;
@@ -842,6 +845,7 @@ export class Config implements McpContext, AgentLoopContext {
private readonly adminSkillsEnabled: boolean;
private readonly experimentalJitContext: boolean;
private readonly topicUpdateNarration: boolean;
private readonly disableLLMCorrection: boolean;
private readonly planEnabled: boolean;
private readonly trackerEnabled: boolean;
@@ -955,7 +959,42 @@ export class Config implements McpContext, AgentLoopContext {
this.disabledSkills = params.disabledSkills ?? [];
this.adminSkillsEnabled = params.adminSkillsEnabled ?? true;
this.modelAvailabilityService = new ModelAvailabilityService();
this.dynamicModelConfiguration = params.dynamicModelConfiguration ?? false;
// HACK: The settings loading logic doesn't currently merge the default
// generation config with the user's settings. This means if a user provides
// any `generation` settings (e.g., just `overrides`), the default `aliases`
// are lost. This hack manually merges the default aliases back in if they
// are missing from the user's config.
// TODO(12593): Fix the settings loading logic to properly merge defaults and
// remove this hack.
let modelConfigServiceConfig = params.modelConfigServiceConfig;
if (modelConfigServiceConfig) {
// Ensure user-defined model definitions augment, not replace, the defaults.
const mergedModelDefinitions = {
...DEFAULT_MODEL_CONFIGS.modelDefinitions,
...modelConfigServiceConfig.modelDefinitions,
};
modelConfigServiceConfig = {
// Preserve other user settings like customAliases
...modelConfigServiceConfig,
// Apply defaults for aliases and overrides if they are not provided
aliases:
modelConfigServiceConfig.aliases ?? DEFAULT_MODEL_CONFIGS.aliases,
overrides:
modelConfigServiceConfig.overrides ?? DEFAULT_MODEL_CONFIGS.overrides,
// Use the merged model definitions
modelDefinitions: mergedModelDefinitions,
};
}
this.modelConfigService = new ModelConfigService(
modelConfigServiceConfig ?? DEFAULT_MODEL_CONFIGS,
);
this.experimentalJitContext = params.experimentalJitContext ?? false;
this.topicUpdateNarration = params.topicUpdateNarration ?? false;
this.modelSteering = params.modelSteering ?? false;
this.userHintService = new UserHintService(() =>
this.isModelSteeringEnabled(),
@@ -1010,7 +1049,7 @@ export class Config implements McpContext, AgentLoopContext {
this.truncateToolOutputThreshold =
params.truncateToolOutputThreshold ??
DEFAULT_TRUNCATE_TOOL_OUTPUT_THRESHOLD;
this.useWriteTodos = isPreviewModel(this.model)
this.useWriteTodos = isPreviewModel(this.model, this)
? false
: (params.useWriteTodos ?? true);
this.workspacePoliciesDir = params.workspacePoliciesDir;
@@ -1128,33 +1167,6 @@ export class Config implements McpContext, AgentLoopContext {
this._sandboxManager = createSandboxManager(params.toolSandboxing ?? false);
this.shellExecutionConfig.sandboxManager = this._sandboxManager;
this.modelRouterService = new ModelRouterService(this);
// HACK: The settings loading logic doesn't currently merge the default
// generation config with the user's settings. This means if a user provides
// any `generation` settings (e.g., just `overrides`), the default `aliases`
// are lost. This hack manually merges the default aliases back in if they
// are missing from the user's config.
// TODO(12593): Fix the settings loading logic to properly merge defaults and
// remove this hack.
let modelConfigServiceConfig = params.modelConfigServiceConfig;
if (modelConfigServiceConfig) {
if (!modelConfigServiceConfig.aliases) {
modelConfigServiceConfig = {
...modelConfigServiceConfig,
aliases: DEFAULT_MODEL_CONFIGS.aliases,
};
}
if (!modelConfigServiceConfig.overrides) {
modelConfigServiceConfig = {
...modelConfigServiceConfig,
overrides: DEFAULT_MODEL_CONFIGS.overrides,
};
}
}
this.modelConfigService = new ModelConfigService(
modelConfigServiceConfig ?? DEFAULT_MODEL_CONFIGS,
);
}
get config(): Config {
@@ -1352,7 +1364,10 @@ export class Config implements McpContext, AgentLoopContext {
// Only reset when we have explicit "no access" (hasAccessToPreviewModel === false).
// When null (quota not fetched) or true, we preserve the saved model.
if (isPreviewModel(this.model) && this.hasAccessToPreviewModel === false) {
if (
isPreviewModel(this.model, this) &&
this.hasAccessToPreviewModel === false
) {
this.setModel(DEFAULT_GEMINI_MODEL_AUTO);
}
@@ -1624,7 +1639,7 @@ export class Config implements McpContext, AgentLoopContext {
const isPreview =
model === PREVIEW_GEMINI_MODEL_AUTO ||
isPreviewModel(this.getActiveModel());
isPreviewModel(this.getActiveModel(), this);
const proModel = isPreview ? PREVIEW_GEMINI_MODEL : DEFAULT_GEMINI_MODEL;
const flashModel = isPreview
? PREVIEW_GEMINI_FLASH_MODEL
@@ -1822,8 +1837,9 @@ export class Config implements McpContext, AgentLoopContext {
}
const hasAccess =
quota.buckets?.some((b) => b.modelId && isPreviewModel(b.modelId)) ??
false;
quota.buckets?.some(
(b) => b.modelId && isPreviewModel(b.modelId, this),
) ?? false;
this.setHasAccessToPreviewModel(hasAccess);
return quota;
} catch (e) {
@@ -2045,6 +2061,10 @@ export class Config implements McpContext, AgentLoopContext {
return this.experimentalJitContext;
}
isTopicUpdateNarrationEnabled(): boolean {
return this.topicUpdateNarration;
}
isModelSteeringEnabled(): boolean {
return this.modelSteering;
}
@@ -2219,6 +2239,10 @@ export class Config implements McpContext, AgentLoopContext {
return this.acceptRawOutputRisk;
}
getExperimentalDynamicModelConfiguration(): boolean {
return this.dynamicModelConfiguration;
}
getPendingIncludeDirectories(): string[] {
return this.pendingIncludeDirectories;
}
@@ -249,4 +249,94 @@ export const DEFAULT_MODEL_CONFIGS: ModelConfigServiceConfig = {
},
},
],
modelDefinitions: {
// Concrete Models
'gemini-3.1-pro-preview': {
tier: 'pro',
family: 'gemini-3',
isPreview: true,
dialogLocation: 'manual',
features: { thinking: true, multimodalToolUse: true },
},
'gemini-3.1-pro-preview-customtools': {
tier: 'pro',
family: 'gemini-3',
isPreview: true,
features: { thinking: true, multimodalToolUse: true },
},
'gemini-3-pro-preview': {
tier: 'pro',
family: 'gemini-3',
isPreview: true,
dialogLocation: 'manual',
features: { thinking: true, multimodalToolUse: true },
},
'gemini-3-flash-preview': {
tier: 'flash',
family: 'gemini-3',
isPreview: true,
dialogLocation: 'manual',
features: { thinking: false, multimodalToolUse: true },
},
'gemini-2.5-pro': {
tier: 'pro',
family: 'gemini-2.5',
isPreview: false,
dialogLocation: 'manual',
features: { thinking: false, multimodalToolUse: false },
},
'gemini-2.5-flash': {
tier: 'flash',
family: 'gemini-2.5',
isPreview: false,
dialogLocation: 'manual',
features: { thinking: false, multimodalToolUse: false },
},
'gemini-2.5-flash-lite': {
tier: 'flash-lite',
family: 'gemini-2.5',
isPreview: false,
dialogLocation: 'manual',
features: { thinking: false, multimodalToolUse: false },
},
// Aliases
auto: {
tier: 'auto',
isPreview: true,
features: { thinking: true, multimodalToolUse: false },
},
pro: {
tier: 'pro',
isPreview: false,
features: { thinking: true, multimodalToolUse: false },
},
flash: {
tier: 'flash',
isPreview: false,
features: { thinking: false, multimodalToolUse: false },
},
'flash-lite': {
tier: 'flash-lite',
isPreview: false,
features: { thinking: false, multimodalToolUse: false },
},
'auto-gemini-3': {
displayName: 'Auto (Gemini 3)',
tier: 'auto',
isPreview: true,
dialogLocation: 'main',
dialogDescription:
'Let Gemini CLI decide the best model for the task: gemini-3.1-pro, gemini-3-flash',
features: { thinking: true, multimodalToolUse: false },
},
'auto-gemini-2.5': {
displayName: 'Auto (Gemini 2.5)',
tier: 'auto',
isPreview: false,
dialogLocation: 'main',
dialogDescription:
'Let Gemini CLI decide the best model for the task: gemini-2.5-pro, gemini-2.5-flash',
features: { thinking: false, multimodalToolUse: false },
},
},
};
+90
View File
@@ -31,6 +31,96 @@ import {
isPreviewModel,
isProModel,
} from './models.js';
import type { Config } from './config.js';
import { ModelConfigService } from '../services/modelConfigService.js';
import { DEFAULT_MODEL_CONFIGS } from './defaultModelConfigs.js';
const modelConfigService = new ModelConfigService(DEFAULT_MODEL_CONFIGS);
const dynamicConfig = {
getExperimentalDynamicModelConfiguration: () => true,
modelConfigService,
} as unknown as Config;
const legacyConfig = {
getExperimentalDynamicModelConfiguration: () => false,
modelConfigService,
} as unknown as Config;
describe('Dynamic Configuration Parity', () => {
const modelsToTest = [
GEMINI_MODEL_ALIAS_AUTO,
GEMINI_MODEL_ALIAS_PRO,
GEMINI_MODEL_ALIAS_FLASH,
PREVIEW_GEMINI_MODEL_AUTO,
DEFAULT_GEMINI_MODEL_AUTO,
PREVIEW_GEMINI_MODEL,
DEFAULT_GEMINI_MODEL,
'custom-model',
];
it('getDisplayString should match legacy behavior', () => {
for (const model of modelsToTest) {
const legacy = getDisplayString(model, legacyConfig);
const dynamic = getDisplayString(model, dynamicConfig);
expect(dynamic).toBe(legacy);
}
});
it('isPreviewModel should match legacy behavior', () => {
const allModels = [
...modelsToTest,
PREVIEW_GEMINI_3_1_MODEL,
PREVIEW_GEMINI_3_1_CUSTOM_TOOLS_MODEL,
PREVIEW_GEMINI_FLASH_MODEL,
];
for (const model of allModels) {
const legacy = isPreviewModel(model, legacyConfig);
const dynamic = isPreviewModel(model, dynamicConfig);
expect(dynamic).toBe(legacy);
}
});
it('isProModel should match legacy behavior', () => {
for (const model of modelsToTest) {
const legacy = isProModel(model, legacyConfig);
const dynamic = isProModel(model, dynamicConfig);
expect(dynamic).toBe(legacy);
}
});
it('isGemini3Model should match legacy behavior', () => {
for (const model of modelsToTest) {
const legacy = isGemini3Model(model, legacyConfig);
const dynamic = isGemini3Model(model, dynamicConfig);
expect(dynamic).toBe(legacy);
}
});
it('isCustomModel should match legacy behavior', () => {
for (const model of modelsToTest) {
const legacy = isCustomModel(model, legacyConfig);
const dynamic = isCustomModel(model, dynamicConfig);
expect(dynamic).toBe(legacy);
}
});
it('supportsModernFeatures should match legacy behavior', () => {
for (const model of modelsToTest) {
const legacy = supportsModernFeatures(model);
const dynamic = supportsModernFeatures(model);
expect(dynamic).toBe(legacy);
}
});
it('supportsMultimodalFunctionResponse should match legacy behavior', () => {
for (const model of modelsToTest) {
const legacy = supportsMultimodalFunctionResponse(model, legacyConfig);
const dynamic = supportsMultimodalFunctionResponse(model, dynamicConfig);
expect(dynamic).toBe(legacy);
}
});
});
describe('isPreviewModel', () => {
it('should return true for preview models', () => {
+103 -7
View File
@@ -4,6 +4,33 @@
* SPDX-License-Identifier: Apache-2.0
*/
/**
* Interface for the ModelConfigService to break circular dependencies.
*/
export interface IModelConfigService {
getModelDefinition(modelId: string):
| {
tier?: string;
family?: string;
isPreview?: boolean;
displayName?: string;
features?: {
thinking?: boolean;
multimodalToolUse?: boolean;
};
}
| undefined;
}
/**
* Interface defining the minimal configuration required for model capability checks.
* This helps break circular dependencies between Config and models.ts.
*/
export interface ModelCapabilityContext {
readonly modelConfigService: IModelConfigService;
getExperimentalDynamicModelConfiguration(): boolean;
}
export const PREVIEW_GEMINI_MODEL = 'gemini-3-pro-preview';
export const PREVIEW_GEMINI_3_1_MODEL = 'gemini-3.1-pro-preview';
export const PREVIEW_GEMINI_3_1_CUSTOM_TOOLS_MODEL =
@@ -139,7 +166,17 @@ export function resolveClassifierModel(
}
return resolveModel(requestedModel, useGemini3_1, useCustomToolModel);
}
export function getDisplayString(model: string) {
export function getDisplayString(
model: string,
config?: ModelCapabilityContext,
) {
if (config?.getExperimentalDynamicModelConfiguration?.() === true) {
const definition = config.modelConfigService.getModelDefinition(model);
if (definition?.displayName) {
return definition.displayName;
}
}
switch (model) {
case PREVIEW_GEMINI_MODEL_AUTO:
return 'Auto (Gemini 3)';
@@ -160,9 +197,19 @@ export function getDisplayString(model: string) {
* Checks if the model is a preview model.
*
* @param model The model name to check.
* @param config Optional config object for dynamic model configuration.
* @returns True if the model is a preview model.
*/
export function isPreviewModel(model: string): boolean {
export function isPreviewModel(
model: string,
config?: ModelCapabilityContext,
): boolean {
if (config?.getExperimentalDynamicModelConfiguration?.() === true) {
return (
config.modelConfigService.getModelDefinition(model)?.isPreview === true
);
}
return (
model === PREVIEW_GEMINI_MODEL ||
model === PREVIEW_GEMINI_3_1_MODEL ||
@@ -177,9 +224,16 @@ export function isPreviewModel(model: string): boolean {
* Checks if the model is a Pro model.
*
* @param model The model name to check.
* @param config Optional config object for dynamic model configuration.
* @returns True if the model is a Pro model.
*/
export function isProModel(model: string): boolean {
export function isProModel(
model: string,
config?: ModelCapabilityContext,
): boolean {
if (config?.getExperimentalDynamicModelConfiguration?.() === true) {
return config.modelConfigService.getModelDefinition(model)?.tier === 'pro';
}
return model.toLowerCase().includes('pro');
}
@@ -187,9 +241,22 @@ export function isProModel(model: string): boolean {
* Checks if the model is a Gemini 3 model.
*
* @param model The model name to check.
* @param config Optional config object for dynamic model configuration.
* @returns True if the model is a Gemini 3 model.
*/
export function isGemini3Model(model: string): boolean {
export function isGemini3Model(
model: string,
config?: ModelCapabilityContext,
): boolean {
if (config?.getExperimentalDynamicModelConfiguration?.() === true) {
// Legacy behavior resolves the model first.
const resolved = resolveModel(model);
return (
config.modelConfigService.getModelDefinition(resolved)?.family ===
'gemini-3'
);
}
const resolved = resolveModel(model);
return /^gemini-3(\.|-|$)/.test(resolved);
}
@@ -201,6 +268,8 @@ export function isGemini3Model(model: string): boolean {
* @returns True if the model is a Gemini-2.x model.
*/
export function isGemini2Model(model: string): boolean {
// This is legacy behavior, will remove this when gemini 2 models are no
// longer needed.
return /^gemini-2(\.|$)/.test(model);
}
@@ -208,9 +277,20 @@ export function isGemini2Model(model: string): boolean {
* Checks if the model is a "custom" model (not Gemini branded).
*
* @param model The model name to check.
* @param config Optional config object for dynamic model configuration.
* @returns True if the model is not a Gemini branded model.
*/
export function isCustomModel(model: string): boolean {
export function isCustomModel(
model: string,
config?: ModelCapabilityContext,
): boolean {
if (config?.getExperimentalDynamicModelConfiguration?.() === true) {
const resolved = resolveModel(model);
return (
config.modelConfigService.getModelDefinition(resolved)?.tier ===
'custom' || !resolved.startsWith('gemini-')
);
}
const resolved = resolveModel(model);
return !resolved.startsWith('gemini-');
}
@@ -231,9 +311,16 @@ export function supportsModernFeatures(model: string): boolean {
* Checks if the model is an auto model.
*
* @param model The model name to check.
* @param config Optional config object for dynamic model configuration.
* @returns True if the model is an auto model.
*/
export function isAutoModel(model: string): boolean {
export function isAutoModel(
model: string,
config?: ModelCapabilityContext,
): boolean {
if (config?.getExperimentalDynamicModelConfiguration?.() === true) {
return config.modelConfigService.getModelDefinition(model)?.tier === 'auto';
}
return (
model === GEMINI_MODEL_ALIAS_AUTO ||
model === PREVIEW_GEMINI_MODEL_AUTO ||
@@ -248,7 +335,16 @@ export function isAutoModel(model: string): boolean {
* @param model The model name to check.
* @returns True if the model supports multimodal function responses.
*/
export function supportsMultimodalFunctionResponse(model: string): boolean {
export function supportsMultimodalFunctionResponse(
model: string,
config?: ModelCapabilityContext,
): boolean {
if (config?.getExperimentalDynamicModelConfiguration?.() === true) {
return (
config.modelConfigService.getModelDefinition(model)?.features
?.multimodalToolUse === true
);
}
return model.startsWith('gemini-3-');
}
@@ -49,9 +49,9 @@ Use the following guidelines to optimize your search and read patterns.
- **Testing:** ALWAYS search for and update related tests after making a code change. You must add a new test case to the existing test file (if one exists) or create a new test file to verify your changes.
- **User Hints:** During execution, the user may provide real-time hints (marked as "User hint:" or "User hints:"). Treat these as high-priority but scope-preserving course corrections: apply the minimal plan change needed, keep unaffected user tasks active, and never cancel/skip tasks unless cancellation is explicit for those tasks. Hints may add new tasks, modify one or more tasks, cancel specific tasks, or provide extra context only. If scope is ambiguous, ask for clarification before dropping work.
- **Confirm Ambiguity/Expansion:** Do not take significant actions beyond the clear scope of the request without confirming with the user. If the user implies a change (e.g., reports a bug) without explicitly asking for a fix, **ask for confirmation first**. If asked *how* to do something, explain first, don't just do it.
- **Explain Before Acting:** Never call tools in silence. You MUST provide a concise, one-sentence explanation of your intent or strategy immediately before executing tool calls. This is essential for transparency, especially when confirming a request or answering a question. Silence is only acceptable for repetitive, low-level discovery operations (e.g., sequential file reads) where narration would be noisy.
- **Explaining Changes:** After completing a code modification or file operation *do not* provide summaries unless asked.
- **Do Not revert changes:** Do not revert changes to the codebase unless asked to do so by the user. Only revert changes made by you if they have resulted in an error or if the user has explicitly asked you to revert the changes.
- **Explain Before Acting:** Never call tools in silence. You MUST provide a concise, one-sentence explanation of your intent or strategy immediately before executing tool calls. This is essential for transparency, especially when confirming a request or answering a question. Silence is only acceptable for repetitive, low-level discovery operations (e.g., sequential file reads) where narration would be noisy.
# Available Sub-Agents
@@ -147,7 +147,7 @@ Use the \`exit_plan_mode\` tool to present the plan and formally request approva
- **High-Signal Output:** Focus exclusively on **intent** and **technical rationale**. Avoid conversational filler, apologies, and mechanical tool-use narration (e.g., "I will now call...").
- **Concise & Direct:** Adopt a professional, direct, and concise tone suitable for a CLI environment.
- **Minimal Output:** Aim for fewer than 3 lines of text output (excluding tool use/code generation) per response whenever practical.
- **No Chitchat:** Avoid conversational filler, preambles ("Okay, I will now..."), or postambles ("I have finished the changes...") unless they serve to explain intent as required by the 'Explain Before Acting' mandate.
- **No Chitchat:** Avoid conversational filler, preambles ("Okay, I will now..."), or postambles ("I have finished the changes...") unless they are part of the 'Explain Before Acting' mandate.
- **No Repetition:** Once you have provided a final synthesis of your work, do not repeat yourself or provide additional summaries. For simple or direct requests, prioritize extreme brevity.
- **Formatting:** Use GitHub-flavored Markdown. Responses will be rendered in monospace.
- **Tools vs. Text:** Use tools for actions, text output *only* for communication. Do not add explanatory comments within tool calls.
@@ -220,9 +220,9 @@ Use the following guidelines to optimize your search and read patterns.
- **Testing:** ALWAYS search for and update related tests after making a code change. You must add a new test case to the existing test file (if one exists) or create a new test file to verify your changes.
- **User Hints:** During execution, the user may provide real-time hints (marked as "User hint:" or "User hints:"). Treat these as high-priority but scope-preserving course corrections: apply the minimal plan change needed, keep unaffected user tasks active, and never cancel/skip tasks unless cancellation is explicit for those tasks. Hints may add new tasks, modify one or more tasks, cancel specific tasks, or provide extra context only. If scope is ambiguous, ask for clarification before dropping work.
- **Confirm Ambiguity/Expansion:** Do not take significant actions beyond the clear scope of the request without confirming with the user. If the user implies a change (e.g., reports a bug) without explicitly asking for a fix, **ask for confirmation first**. If asked *how* to do something, explain first, don't just do it.
- **Explain Before Acting:** Never call tools in silence. You MUST provide a concise, one-sentence explanation of your intent or strategy immediately before executing tool calls. This is essential for transparency, especially when confirming a request or answering a question. Silence is only acceptable for repetitive, low-level discovery operations (e.g., sequential file reads) where narration would be noisy.
- **Explaining Changes:** After completing a code modification or file operation *do not* provide summaries unless asked.
- **Do Not revert changes:** Do not revert changes to the codebase unless asked to do so by the user. Only revert changes made by you if they have resulted in an error or if the user has explicitly asked you to revert the changes.
- **Explain Before Acting:** Never call tools in silence. You MUST provide a concise, one-sentence explanation of your intent or strategy immediately before executing tool calls. This is essential for transparency, especially when confirming a request or answering a question. Silence is only acceptable for repetitive, low-level discovery operations (e.g., sequential file reads) where narration would be noisy.
# Available Sub-Agents
@@ -324,7 +324,7 @@ An approved plan is available for this task at \`/tmp/plans/feature-x.md\`.
- **High-Signal Output:** Focus exclusively on **intent** and **technical rationale**. Avoid conversational filler, apologies, and mechanical tool-use narration (e.g., "I will now call...").
- **Concise & Direct:** Adopt a professional, direct, and concise tone suitable for a CLI environment.
- **Minimal Output:** Aim for fewer than 3 lines of text output (excluding tool use/code generation) per response whenever practical.
- **No Chitchat:** Avoid conversational filler, preambles ("Okay, I will now..."), or postambles ("I have finished the changes...") unless they serve to explain intent as required by the 'Explain Before Acting' mandate.
- **No Chitchat:** Avoid conversational filler, preambles ("Okay, I will now..."), or postambles ("I have finished the changes...") unless they are part of the 'Explain Before Acting' mandate.
- **No Repetition:** Once you have provided a final synthesis of your work, do not repeat yourself or provide additional summaries. For simple or direct requests, prioritize extreme brevity.
- **Formatting:** Use GitHub-flavored Markdown. Responses will be rendered in monospace.
- **Tools vs. Text:** Use tools for actions, text output *only* for communication. Do not add explanatory comments within tool calls.
@@ -510,9 +510,9 @@ Use the following guidelines to optimize your search and read patterns.
- **Testing:** ALWAYS search for and update related tests after making a code change. You must add a new test case to the existing test file (if one exists) or create a new test file to verify your changes.
- **User Hints:** During execution, the user may provide real-time hints (marked as "User hint:" or "User hints:"). Treat these as high-priority but scope-preserving course corrections: apply the minimal plan change needed, keep unaffected user tasks active, and never cancel/skip tasks unless cancellation is explicit for those tasks. Hints may add new tasks, modify one or more tasks, cancel specific tasks, or provide extra context only. If scope is ambiguous, ask for clarification before dropping work.
- **Confirm Ambiguity/Expansion:** Do not take significant actions beyond the clear scope of the request without confirming with the user. If the user implies a change (e.g., reports a bug) without explicitly asking for a fix, **ask for confirmation first**. If asked *how* to do something, explain first, don't just do it.
- **Explain Before Acting:** Never call tools in silence. You MUST provide a concise, one-sentence explanation of your intent or strategy immediately before executing tool calls. This is essential for transparency, especially when confirming a request or answering a question. Silence is only acceptable for repetitive, low-level discovery operations (e.g., sequential file reads) where narration would be noisy.
- **Explaining Changes:** After completing a code modification or file operation *do not* provide summaries unless asked.
- **Do Not revert changes:** Do not revert changes to the codebase unless asked to do so by the user. Only revert changes made by you if they have resulted in an error or if the user has explicitly asked you to revert the changes.
- **Explain Before Acting:** Never call tools in silence. You MUST provide a concise, one-sentence explanation of your intent or strategy immediately before executing tool calls. This is essential for transparency, especially when confirming a request or answering a question. Silence is only acceptable for repetitive, low-level discovery operations (e.g., sequential file reads) where narration would be noisy.
# Available Sub-Agents
@@ -608,7 +608,7 @@ Use the \`exit_plan_mode\` tool to present the plan and formally request approva
- **High-Signal Output:** Focus exclusively on **intent** and **technical rationale**. Avoid conversational filler, apologies, and mechanical tool-use narration (e.g., "I will now call...").
- **Concise & Direct:** Adopt a professional, direct, and concise tone suitable for a CLI environment.
- **Minimal Output:** Aim for fewer than 3 lines of text output (excluding tool use/code generation) per response whenever practical.
- **No Chitchat:** Avoid conversational filler, preambles ("Okay, I will now..."), or postambles ("I have finished the changes...") unless they serve to explain intent as required by the 'Explain Before Acting' mandate.
- **No Chitchat:** Avoid conversational filler, preambles ("Okay, I will now..."), or postambles ("I have finished the changes...") unless they are part of the 'Explain Before Acting' mandate.
- **No Repetition:** Once you have provided a final synthesis of your work, do not repeat yourself or provide additional summaries. For simple or direct requests, prioritize extreme brevity.
- **Formatting:** Use GitHub-flavored Markdown. Responses will be rendered in monospace.
- **Tools vs. Text:** Use tools for actions, text output *only* for communication. Do not add explanatory comments within tool calls.
@@ -681,9 +681,9 @@ Use the following guidelines to optimize your search and read patterns.
- **Testing:** ALWAYS search for and update related tests after making a code change. You must add a new test case to the existing test file (if one exists) or create a new test file to verify your changes.
- **User Hints:** During execution, the user may provide real-time hints (marked as "User hint:" or "User hints:"). Treat these as high-priority but scope-preserving course corrections: apply the minimal plan change needed, keep unaffected user tasks active, and never cancel/skip tasks unless cancellation is explicit for those tasks. Hints may add new tasks, modify one or more tasks, cancel specific tasks, or provide extra context only. If scope is ambiguous, ask for clarification before dropping work.
- **Confirm Ambiguity/Expansion:** Do not take significant actions beyond the clear scope of the request without confirming with the user. If the user implies a change (e.g., reports a bug) without explicitly asking for a fix, **ask for confirmation first**. If asked *how* to do something, explain first, don't just do it.
- **Explain Before Acting:** Never call tools in silence. You MUST provide a concise, one-sentence explanation of your intent or strategy immediately before executing tool calls. This is essential for transparency, especially when confirming a request or answering a question. Silence is only acceptable for repetitive, low-level discovery operations (e.g., sequential file reads) where narration would be noisy.
- **Explaining Changes:** After completing a code modification or file operation *do not* provide summaries unless asked.
- **Do Not revert changes:** Do not revert changes to the codebase unless asked to do so by the user. Only revert changes made by you if they have resulted in an error or if the user has explicitly asked you to revert the changes.
- **Explain Before Acting:** Never call tools in silence. You MUST provide a concise, one-sentence explanation of your intent or strategy immediately before executing tool calls. This is essential for transparency, especially when confirming a request or answering a question. Silence is only acceptable for repetitive, low-level discovery operations (e.g., sequential file reads) where narration would be noisy.
# Available Sub-Agents
@@ -762,7 +762,7 @@ Operate using a **Research -> Strategy -> Execution** lifecycle. For the Executi
- **High-Signal Output:** Focus exclusively on **intent** and **technical rationale**. Avoid conversational filler, apologies, and mechanical tool-use narration (e.g., "I will now call...").
- **Concise & Direct:** Adopt a professional, direct, and concise tone suitable for a CLI environment.
- **Minimal Output:** Aim for fewer than 3 lines of text output (excluding tool use/code generation) per response whenever practical.
- **No Chitchat:** Avoid conversational filler, preambles ("Okay, I will now..."), or postambles ("I have finished the changes...") unless they serve to explain intent as required by the 'Explain Before Acting' mandate.
- **No Chitchat:** Avoid conversational filler, preambles ("Okay, I will now..."), or postambles ("I have finished the changes...") unless they are part of the 'Explain Before Acting' mandate.
- **No Repetition:** Once you have provided a final synthesis of your work, do not repeat yourself or provide additional summaries. For simple or direct requests, prioritize extreme brevity.
- **Formatting:** Use GitHub-flavored Markdown. Responses will be rendered in monospace.
- **Tools vs. Text:** Use tools for actions, text output *only* for communication. Do not add explanatory comments within tool calls.
@@ -852,9 +852,9 @@ Use the following guidelines to optimize your search and read patterns.
- **Testing:** ALWAYS search for and update related tests after making a code change. You must add a new test case to the existing test file (if one exists) or create a new test file to verify your changes.
- **User Hints:** During execution, the user may provide real-time hints (marked as "User hint:" or "User hints:"). Treat these as high-priority but scope-preserving course corrections: apply the minimal plan change needed, keep unaffected user tasks active, and never cancel/skip tasks unless cancellation is explicit for those tasks. Hints may add new tasks, modify one or more tasks, cancel specific tasks, or provide extra context only. If scope is ambiguous, ask for clarification before dropping work.
- **Handle Ambiguity/Expansion:** Do not take significant actions beyond the clear scope of the request. If the user implies a change (e.g., reports a bug) without explicitly asking for a fix, do not perform it automatically.
- **Explain Before Acting:** Never call tools in silence. You MUST provide a concise, one-sentence explanation of your intent or strategy immediately before executing tool calls. This is essential for transparency, especially when confirming a request or answering a question. Silence is only acceptable for repetitive, low-level discovery operations (e.g., sequential file reads) where narration would be noisy.
- **Explaining Changes:** After completing a code modification or file operation *do not* provide summaries unless asked.
- **Do Not revert changes:** Do not revert changes to the codebase unless asked to do so by the user. Only revert changes made by you if they have resulted in an error or if the user has explicitly asked you to revert the changes.
- **Explain Before Acting:** Never call tools in silence. You MUST provide a concise, one-sentence explanation of your intent or strategy immediately before executing tool calls. This is essential for transparency, especially when confirming a request or answering a question. Silence is only acceptable for repetitive, low-level discovery operations (e.g., sequential file reads) where narration would be noisy.
- **Non-Interactive Environment:** You are running in a headless/CI environment and cannot interact with the user. Do not ask the user questions or request additional information, as the session will terminate. Use your best judgment to complete the task. If a tool fails because it requires user interaction, do not retry it indefinitely; instead, explain the limitation and suggest how the user can provide the required data (e.g., via environment variables).
# Hook Context
@@ -902,7 +902,7 @@ Operate using a **Research -> Strategy -> Execution** lifecycle. For the Executi
- **High-Signal Output:** Focus exclusively on **intent** and **technical rationale**. Avoid conversational filler, apologies, and mechanical tool-use narration (e.g., "I will now call...").
- **Concise & Direct:** Adopt a professional, direct, and concise tone suitable for a CLI environment.
- **Minimal Output:** Aim for fewer than 3 lines of text output (excluding tool use/code generation) per response whenever practical.
- **No Chitchat:** Avoid conversational filler, preambles ("Okay, I will now..."), or postambles ("I have finished the changes...") unless they serve to explain intent as required by the 'Explain Before Acting' mandate.
- **No Chitchat:** Avoid conversational filler, preambles ("Okay, I will now..."), or postambles ("I have finished the changes...") unless they are part of the 'Explain Before Acting' mandate.
- **No Repetition:** Once you have provided a final synthesis of your work, do not repeat yourself or provide additional summaries. For simple or direct requests, prioritize extreme brevity.
- **Formatting:** Use GitHub-flavored Markdown. Responses will be rendered in monospace.
- **Tools vs. Text:** Use tools for actions, text output *only* for communication. Do not add explanatory comments within tool calls.
@@ -975,9 +975,9 @@ Use the following guidelines to optimize your search and read patterns.
- **Testing:** ALWAYS search for and update related tests after making a code change. You must add a new test case to the existing test file (if one exists) or create a new test file to verify your changes.
- **User Hints:** During execution, the user may provide real-time hints (marked as "User hint:" or "User hints:"). Treat these as high-priority but scope-preserving course corrections: apply the minimal plan change needed, keep unaffected user tasks active, and never cancel/skip tasks unless cancellation is explicit for those tasks. Hints may add new tasks, modify one or more tasks, cancel specific tasks, or provide extra context only. If scope is ambiguous, ask for clarification before dropping work.
- **Handle Ambiguity/Expansion:** Do not take significant actions beyond the clear scope of the request. If the user implies a change (e.g., reports a bug) without explicitly asking for a fix, do not perform it automatically.
- **Explain Before Acting:** Never call tools in silence. You MUST provide a concise, one-sentence explanation of your intent or strategy immediately before executing tool calls. This is essential for transparency, especially when confirming a request or answering a question. Silence is only acceptable for repetitive, low-level discovery operations (e.g., sequential file reads) where narration would be noisy.
- **Explaining Changes:** After completing a code modification or file operation *do not* provide summaries unless asked.
- **Do Not revert changes:** Do not revert changes to the codebase unless asked to do so by the user. Only revert changes made by you if they have resulted in an error or if the user has explicitly asked you to revert the changes.
- **Explain Before Acting:** Never call tools in silence. You MUST provide a concise, one-sentence explanation of your intent or strategy immediately before executing tool calls. This is essential for transparency, especially when confirming a request or answering a question. Silence is only acceptable for repetitive, low-level discovery operations (e.g., sequential file reads) where narration would be noisy.
- **Non-Interactive Environment:** You are running in a headless/CI environment and cannot interact with the user. Do not ask the user questions or request additional information, as the session will terminate. Use your best judgment to complete the task. If a tool fails because it requires user interaction, do not retry it indefinitely; instead, explain the limitation and suggest how the user can provide the required data (e.g., via environment variables).
# Hook Context
@@ -1025,7 +1025,7 @@ Operate using a **Research -> Strategy -> Execution** lifecycle. For the Executi
- **High-Signal Output:** Focus exclusively on **intent** and **technical rationale**. Avoid conversational filler, apologies, and mechanical tool-use narration (e.g., "I will now call...").
- **Concise & Direct:** Adopt a professional, direct, and concise tone suitable for a CLI environment.
- **Minimal Output:** Aim for fewer than 3 lines of text output (excluding tool use/code generation) per response whenever practical.
- **No Chitchat:** Avoid conversational filler, preambles ("Okay, I will now..."), or postambles ("I have finished the changes...") unless they serve to explain intent as required by the 'Explain Before Acting' mandate.
- **No Chitchat:** Avoid conversational filler, preambles ("Okay, I will now..."), or postambles ("I have finished the changes...") unless they are part of the 'Explain Before Acting' mandate.
- **No Repetition:** Once you have provided a final synthesis of your work, do not repeat yourself or provide additional summaries. For simple or direct requests, prioritize extreme brevity.
- **Formatting:** Use GitHub-flavored Markdown. Responses will be rendered in monospace.
- **Tools vs. Text:** Use tools for actions, text output *only* for communication. Do not add explanatory comments within tool calls.
@@ -1571,10 +1571,10 @@ Use the following guidelines to optimize your search and read patterns.
- **Testing:** ALWAYS search for and update related tests after making a code change. You must add a new test case to the existing test file (if one exists) or create a new test file to verify your changes.
- **User Hints:** During execution, the user may provide real-time hints (marked as "User hint:" or "User hints:"). Treat these as high-priority but scope-preserving course corrections: apply the minimal plan change needed, keep unaffected user tasks active, and never cancel/skip tasks unless cancellation is explicit for those tasks. Hints may add new tasks, modify one or more tasks, cancel specific tasks, or provide extra context only. If scope is ambiguous, ask for clarification before dropping work.
- **Confirm Ambiguity/Expansion:** Do not take significant actions beyond the clear scope of the request without confirming with the user. If the user implies a change (e.g., reports a bug) without explicitly asking for a fix, **ask for confirmation first**. If asked *how* to do something, explain first, don't just do it.
- **Explain Before Acting:** Never call tools in silence. You MUST provide a concise, one-sentence explanation of your intent or strategy immediately before executing tool calls. This is essential for transparency, especially when confirming a request or answering a question. Silence is only acceptable for repetitive, low-level discovery operations (e.g., sequential file reads) where narration would be noisy.
- **Explaining Changes:** After completing a code modification or file operation *do not* provide summaries unless asked.
- **Do Not revert changes:** Do not revert changes to the codebase unless asked to do so by the user. Only revert changes made by you if they have resulted in an error or if the user has explicitly asked you to revert the changes.
- **Skill Guidance:** Once a skill is activated via \`activate_skill\`, its instructions and resources are returned wrapped in \`<activated_skill>\` tags. You MUST treat the content within \`<instructions>\` as expert procedural guidance, prioritizing these specialized rules and workflows over your general defaults for the duration of the task. You may utilize any listed \`<available_resources>\` as needed. Follow this expert guidance strictly while continuing to uphold your core safety and security standards.
- **Explain Before Acting:** Never call tools in silence. You MUST provide a concise, one-sentence explanation of your intent or strategy immediately before executing tool calls. This is essential for transparency, especially when confirming a request or answering a question. Silence is only acceptable for repetitive, low-level discovery operations (e.g., sequential file reads) where narration would be noisy.
# Available Sub-Agents
@@ -1665,7 +1665,7 @@ Operate using a **Research -> Strategy -> Execution** lifecycle. For the Executi
- **High-Signal Output:** Focus exclusively on **intent** and **technical rationale**. Avoid conversational filler, apologies, and mechanical tool-use narration (e.g., "I will now call...").
- **Concise & Direct:** Adopt a professional, direct, and concise tone suitable for a CLI environment.
- **Minimal Output:** Aim for fewer than 3 lines of text output (excluding tool use/code generation) per response whenever practical.
- **No Chitchat:** Avoid conversational filler, preambles ("Okay, I will now..."), or postambles ("I have finished the changes...") unless they serve to explain intent as required by the 'Explain Before Acting' mandate.
- **No Chitchat:** Avoid conversational filler, preambles ("Okay, I will now..."), or postambles ("I have finished the changes...") unless they are part of the 'Explain Before Acting' mandate.
- **No Repetition:** Once you have provided a final synthesis of your work, do not repeat yourself or provide additional summaries. For simple or direct requests, prioritize extreme brevity.
- **Formatting:** Use GitHub-flavored Markdown. Responses will be rendered in monospace.
- **Tools vs. Text:** Use tools for actions, text output *only* for communication. Do not add explanatory comments within tool calls.
@@ -1738,9 +1738,9 @@ Use the following guidelines to optimize your search and read patterns.
- **Testing:** ALWAYS search for and update related tests after making a code change. You must add a new test case to the existing test file (if one exists) or create a new test file to verify your changes.
- **User Hints:** During execution, the user may provide real-time hints (marked as "User hint:" or "User hints:"). Treat these as high-priority but scope-preserving course corrections: apply the minimal plan change needed, keep unaffected user tasks active, and never cancel/skip tasks unless cancellation is explicit for those tasks. Hints may add new tasks, modify one or more tasks, cancel specific tasks, or provide extra context only. If scope is ambiguous, ask for clarification before dropping work.
- **Confirm Ambiguity/Expansion:** Do not take significant actions beyond the clear scope of the request without confirming with the user. If the user implies a change (e.g., reports a bug) without explicitly asking for a fix, **ask for confirmation first**. If asked *how* to do something, explain first, don't just do it.
- **Explain Before Acting:** Never call tools in silence. You MUST provide a concise, one-sentence explanation of your intent or strategy immediately before executing tool calls. This is essential for transparency, especially when confirming a request or answering a question. Silence is only acceptable for repetitive, low-level discovery operations (e.g., sequential file reads) where narration would be noisy.
- **Explaining Changes:** After completing a code modification or file operation *do not* provide summaries unless asked.
- **Do Not revert changes:** Do not revert changes to the codebase unless asked to do so by the user. Only revert changes made by you if they have resulted in an error or if the user has explicitly asked you to revert the changes.
- **Explain Before Acting:** Never call tools in silence. You MUST provide a concise, one-sentence explanation of your intent or strategy immediately before executing tool calls. This is essential for transparency, especially when confirming a request or answering a question. Silence is only acceptable for repetitive, low-level discovery operations (e.g., sequential file reads) where narration would be noisy.
# Available Sub-Agents
@@ -1819,7 +1819,7 @@ Operate using a **Research -> Strategy -> Execution** lifecycle. For the Executi
- **High-Signal Output:** Focus exclusively on **intent** and **technical rationale**. Avoid conversational filler, apologies, and mechanical tool-use narration (e.g., "I will now call...").
- **Concise & Direct:** Adopt a professional, direct, and concise tone suitable for a CLI environment.
- **Minimal Output:** Aim for fewer than 3 lines of text output (excluding tool use/code generation) per response whenever practical.
- **No Chitchat:** Avoid conversational filler, preambles ("Okay, I will now..."), or postambles ("I have finished the changes...") unless they serve to explain intent as required by the 'Explain Before Acting' mandate.
- **No Chitchat:** Avoid conversational filler, preambles ("Okay, I will now..."), or postambles ("I have finished the changes...") unless they are part of the 'Explain Before Acting' mandate.
- **No Repetition:** Once you have provided a final synthesis of your work, do not repeat yourself or provide additional summaries. For simple or direct requests, prioritize extreme brevity.
- **Formatting:** Use GitHub-flavored Markdown. Responses will be rendered in monospace.
- **Tools vs. Text:** Use tools for actions, text output *only* for communication. Do not add explanatory comments within tool calls.
@@ -1896,9 +1896,9 @@ Use the following guidelines to optimize your search and read patterns.
- **Testing:** ALWAYS search for and update related tests after making a code change. You must add a new test case to the existing test file (if one exists) or create a new test file to verify your changes.
- **User Hints:** During execution, the user may provide real-time hints (marked as "User hint:" or "User hints:"). Treat these as high-priority but scope-preserving course corrections: apply the minimal plan change needed, keep unaffected user tasks active, and never cancel/skip tasks unless cancellation is explicit for those tasks. Hints may add new tasks, modify one or more tasks, cancel specific tasks, or provide extra context only. If scope is ambiguous, ask for clarification before dropping work.
- **Confirm Ambiguity/Expansion:** Do not take significant actions beyond the clear scope of the request without confirming with the user. If the user implies a change (e.g., reports a bug) without explicitly asking for a fix, **ask for confirmation first**. If asked *how* to do something, explain first, don't just do it.
- **Explain Before Acting:** Never call tools in silence. You MUST provide a concise, one-sentence explanation of your intent or strategy immediately before executing tool calls. This is essential for transparency, especially when confirming a request or answering a question. Silence is only acceptable for repetitive, low-level discovery operations (e.g., sequential file reads) where narration would be noisy.
- **Explaining Changes:** After completing a code modification or file operation *do not* provide summaries unless asked.
- **Do Not revert changes:** Do not revert changes to the codebase unless asked to do so by the user. Only revert changes made by you if they have resulted in an error or if the user has explicitly asked you to revert the changes.
- **Explain Before Acting:** Never call tools in silence. You MUST provide a concise, one-sentence explanation of your intent or strategy immediately before executing tool calls. This is essential for transparency, especially when confirming a request or answering a question. Silence is only acceptable for repetitive, low-level discovery operations (e.g., sequential file reads) where narration would be noisy.
# Available Sub-Agents
@@ -1977,7 +1977,7 @@ Operate using a **Research -> Strategy -> Execution** lifecycle. For the Executi
- **High-Signal Output:** Focus exclusively on **intent** and **technical rationale**. Avoid conversational filler, apologies, and mechanical tool-use narration (e.g., "I will now call...").
- **Concise & Direct:** Adopt a professional, direct, and concise tone suitable for a CLI environment.
- **Minimal Output:** Aim for fewer than 3 lines of text output (excluding tool use/code generation) per response whenever practical.
- **No Chitchat:** Avoid conversational filler, preambles ("Okay, I will now..."), or postambles ("I have finished the changes...") unless they serve to explain intent as required by the 'Explain Before Acting' mandate.
- **No Chitchat:** Avoid conversational filler, preambles ("Okay, I will now..."), or postambles ("I have finished the changes...") unless they are part of the 'Explain Before Acting' mandate.
- **No Repetition:** Once you have provided a final synthesis of your work, do not repeat yourself or provide additional summaries. For simple or direct requests, prioritize extreme brevity.
- **Formatting:** Use GitHub-flavored Markdown. Responses will be rendered in monospace.
- **Tools vs. Text:** Use tools for actions, text output *only* for communication. Do not add explanatory comments within tool calls.
@@ -2054,9 +2054,9 @@ Use the following guidelines to optimize your search and read patterns.
- **Testing:** ALWAYS search for and update related tests after making a code change. You must add a new test case to the existing test file (if one exists) or create a new test file to verify your changes.
- **User Hints:** During execution, the user may provide real-time hints (marked as "User hint:" or "User hints:"). Treat these as high-priority but scope-preserving course corrections: apply the minimal plan change needed, keep unaffected user tasks active, and never cancel/skip tasks unless cancellation is explicit for those tasks. Hints may add new tasks, modify one or more tasks, cancel specific tasks, or provide extra context only. If scope is ambiguous, ask for clarification before dropping work.
- **Confirm Ambiguity/Expansion:** Do not take significant actions beyond the clear scope of the request without confirming with the user. If the user implies a change (e.g., reports a bug) without explicitly asking for a fix, **ask for confirmation first**. If asked *how* to do something, explain first, don't just do it.
- **Explain Before Acting:** Never call tools in silence. You MUST provide a concise, one-sentence explanation of your intent or strategy immediately before executing tool calls. This is essential for transparency, especially when confirming a request or answering a question. Silence is only acceptable for repetitive, low-level discovery operations (e.g., sequential file reads) where narration would be noisy.
- **Explaining Changes:** After completing a code modification or file operation *do not* provide summaries unless asked.
- **Do Not revert changes:** Do not revert changes to the codebase unless asked to do so by the user. Only revert changes made by you if they have resulted in an error or if the user has explicitly asked you to revert the changes.
- **Explain Before Acting:** Never call tools in silence. You MUST provide a concise, one-sentence explanation of your intent or strategy immediately before executing tool calls. This is essential for transparency, especially when confirming a request or answering a question. Silence is only acceptable for repetitive, low-level discovery operations (e.g., sequential file reads) where narration would be noisy.
# Available Sub-Agents
@@ -2135,7 +2135,7 @@ Operate using a **Research -> Strategy -> Execution** lifecycle. For the Executi
- **High-Signal Output:** Focus exclusively on **intent** and **technical rationale**. Avoid conversational filler, apologies, and mechanical tool-use narration (e.g., "I will now call...").
- **Concise & Direct:** Adopt a professional, direct, and concise tone suitable for a CLI environment.
- **Minimal Output:** Aim for fewer than 3 lines of text output (excluding tool use/code generation) per response whenever practical.
- **No Chitchat:** Avoid conversational filler, preambles ("Okay, I will now..."), or postambles ("I have finished the changes...") unless they serve to explain intent as required by the 'Explain Before Acting' mandate.
- **No Chitchat:** Avoid conversational filler, preambles ("Okay, I will now..."), or postambles ("I have finished the changes...") unless they are part of the 'Explain Before Acting' mandate.
- **No Repetition:** Once you have provided a final synthesis of your work, do not repeat yourself or provide additional summaries. For simple or direct requests, prioritize extreme brevity.
- **Formatting:** Use GitHub-flavored Markdown. Responses will be rendered in monospace.
- **Tools vs. Text:** Use tools for actions, text output *only* for communication. Do not add explanatory comments within tool calls.
@@ -2208,9 +2208,9 @@ Use the following guidelines to optimize your search and read patterns.
- **Testing:** ALWAYS search for and update related tests after making a code change. You must add a new test case to the existing test file (if one exists) or create a new test file to verify your changes.
- **User Hints:** During execution, the user may provide real-time hints (marked as "User hint:" or "User hints:"). Treat these as high-priority but scope-preserving course corrections: apply the minimal plan change needed, keep unaffected user tasks active, and never cancel/skip tasks unless cancellation is explicit for those tasks. Hints may add new tasks, modify one or more tasks, cancel specific tasks, or provide extra context only. If scope is ambiguous, ask for clarification before dropping work.
- **Confirm Ambiguity/Expansion:** Do not take significant actions beyond the clear scope of the request without confirming with the user. If the user implies a change (e.g., reports a bug) without explicitly asking for a fix, **ask for confirmation first**. If asked *how* to do something, explain first, don't just do it.
- **Explain Before Acting:** Never call tools in silence. You MUST provide a concise, one-sentence explanation of your intent or strategy immediately before executing tool calls. This is essential for transparency, especially when confirming a request or answering a question. Silence is only acceptable for repetitive, low-level discovery operations (e.g., sequential file reads) where narration would be noisy.
- **Explaining Changes:** After completing a code modification or file operation *do not* provide summaries unless asked.
- **Do Not revert changes:** Do not revert changes to the codebase unless asked to do so by the user. Only revert changes made by you if they have resulted in an error or if the user has explicitly asked you to revert the changes.
- **Explain Before Acting:** Never call tools in silence. You MUST provide a concise, one-sentence explanation of your intent or strategy immediately before executing tool calls. This is essential for transparency, especially when confirming a request or answering a question. Silence is only acceptable for repetitive, low-level discovery operations (e.g., sequential file reads) where narration would be noisy.
# Available Sub-Agents
@@ -2289,7 +2289,7 @@ Operate using a **Research -> Strategy -> Execution** lifecycle. For the Executi
- **High-Signal Output:** Focus exclusively on **intent** and **technical rationale**. Avoid conversational filler, apologies, and mechanical tool-use narration (e.g., "I will now call...").
- **Concise & Direct:** Adopt a professional, direct, and concise tone suitable for a CLI environment.
- **Minimal Output:** Aim for fewer than 3 lines of text output (excluding tool use/code generation) per response whenever practical.
- **No Chitchat:** Avoid conversational filler, preambles ("Okay, I will now..."), or postambles ("I have finished the changes...") unless they serve to explain intent as required by the 'Explain Before Acting' mandate.
- **No Chitchat:** Avoid conversational filler, preambles ("Okay, I will now..."), or postambles ("I have finished the changes...") unless they are part of the 'Explain Before Acting' mandate.
- **No Repetition:** Once you have provided a final synthesis of your work, do not repeat yourself or provide additional summaries. For simple or direct requests, prioritize extreme brevity.
- **Formatting:** Use GitHub-flavored Markdown. Responses will be rendered in monospace.
- **Tools vs. Text:** Use tools for actions, text output *only* for communication. Do not add explanatory comments within tool calls.
@@ -2362,9 +2362,9 @@ Use the following guidelines to optimize your search and read patterns.
- **Testing:** ALWAYS search for and update related tests after making a code change. You must add a new test case to the existing test file (if one exists) or create a new test file to verify your changes.
- **User Hints:** During execution, the user may provide real-time hints (marked as "User hint:" or "User hints:"). Treat these as high-priority but scope-preserving course corrections: apply the minimal plan change needed, keep unaffected user tasks active, and never cancel/skip tasks unless cancellation is explicit for those tasks. Hints may add new tasks, modify one or more tasks, cancel specific tasks, or provide extra context only. If scope is ambiguous, ask for clarification before dropping work.
- **Confirm Ambiguity/Expansion:** Do not take significant actions beyond the clear scope of the request without confirming with the user. If the user implies a change (e.g., reports a bug) without explicitly asking for a fix, **ask for confirmation first**. If asked *how* to do something, explain first, don't just do it.
- **Explain Before Acting:** Never call tools in silence. You MUST provide a concise, one-sentence explanation of your intent or strategy immediately before executing tool calls. This is essential for transparency, especially when confirming a request or answering a question. Silence is only acceptable for repetitive, low-level discovery operations (e.g., sequential file reads) where narration would be noisy.
- **Explaining Changes:** After completing a code modification or file operation *do not* provide summaries unless asked.
- **Do Not revert changes:** Do not revert changes to the codebase unless asked to do so by the user. Only revert changes made by you if they have resulted in an error or if the user has explicitly asked you to revert the changes.
- **Explain Before Acting:** Never call tools in silence. You MUST provide a concise, one-sentence explanation of your intent or strategy immediately before executing tool calls. This is essential for transparency, especially when confirming a request or answering a question. Silence is only acceptable for repetitive, low-level discovery operations (e.g., sequential file reads) where narration would be noisy.
# Available Sub-Agents
@@ -2435,7 +2435,7 @@ Operate using a **Research -> Strategy -> Execution** lifecycle. For the Executi
- **High-Signal Output:** Focus exclusively on **intent** and **technical rationale**. Avoid conversational filler, apologies, and mechanical tool-use narration (e.g., "I will now call...").
- **Concise & Direct:** Adopt a professional, direct, and concise tone suitable for a CLI environment.
- **Minimal Output:** Aim for fewer than 3 lines of text output (excluding tool use/code generation) per response whenever practical.
- **No Chitchat:** Avoid conversational filler, preambles ("Okay, I will now..."), or postambles ("I have finished the changes...") unless they serve to explain intent as required by the 'Explain Before Acting' mandate.
- **No Chitchat:** Avoid conversational filler, preambles ("Okay, I will now..."), or postambles ("I have finished the changes...") unless they are part of the 'Explain Before Acting' mandate.
- **No Repetition:** Once you have provided a final synthesis of your work, do not repeat yourself or provide additional summaries. For simple or direct requests, prioritize extreme brevity.
- **Formatting:** Use GitHub-flavored Markdown. Responses will be rendered in monospace.
- **Tools vs. Text:** Use tools for actions, text output *only* for communication. Do not add explanatory comments within tool calls.
@@ -2508,9 +2508,9 @@ Use the following guidelines to optimize your search and read patterns.
- **Testing:** ALWAYS search for and update related tests after making a code change. You must add a new test case to the existing test file (if one exists) or create a new test file to verify your changes.
- **User Hints:** During execution, the user may provide real-time hints (marked as "User hint:" or "User hints:"). Treat these as high-priority but scope-preserving course corrections: apply the minimal plan change needed, keep unaffected user tasks active, and never cancel/skip tasks unless cancellation is explicit for those tasks. Hints may add new tasks, modify one or more tasks, cancel specific tasks, or provide extra context only. If scope is ambiguous, ask for clarification before dropping work.
- **Confirm Ambiguity/Expansion:** Do not take significant actions beyond the clear scope of the request without confirming with the user. If the user implies a change (e.g., reports a bug) without explicitly asking for a fix, **ask for confirmation first**. If asked *how* to do something, explain first, don't just do it.
- **Explain Before Acting:** Never call tools in silence. You MUST provide a concise, one-sentence explanation of your intent or strategy immediately before executing tool calls. This is essential for transparency, especially when confirming a request or answering a question. Silence is only acceptable for repetitive, low-level discovery operations (e.g., sequential file reads) where narration would be noisy.
- **Explaining Changes:** After completing a code modification or file operation *do not* provide summaries unless asked.
- **Do Not revert changes:** Do not revert changes to the codebase unless asked to do so by the user. Only revert changes made by you if they have resulted in an error or if the user has explicitly asked you to revert the changes.
- **Explain Before Acting:** Never call tools in silence. You MUST provide a concise, one-sentence explanation of your intent or strategy immediately before executing tool calls. This is essential for transparency, especially when confirming a request or answering a question. Silence is only acceptable for repetitive, low-level discovery operations (e.g., sequential file reads) where narration would be noisy.
# Available Sub-Agents
@@ -2588,7 +2588,7 @@ Operate using a **Research -> Strategy -> Execution** lifecycle. For the Executi
- **High-Signal Output:** Focus exclusively on **intent** and **technical rationale**. Avoid conversational filler, apologies, and mechanical tool-use narration (e.g., "I will now call...").
- **Concise & Direct:** Adopt a professional, direct, and concise tone suitable for a CLI environment.
- **Minimal Output:** Aim for fewer than 3 lines of text output (excluding tool use/code generation) per response whenever practical.
- **No Chitchat:** Avoid conversational filler, preambles ("Okay, I will now..."), or postambles ("I have finished the changes...") unless they serve to explain intent as required by the 'Explain Before Acting' mandate.
- **No Chitchat:** Avoid conversational filler, preambles ("Okay, I will now..."), or postambles ("I have finished the changes...") unless they are part of the 'Explain Before Acting' mandate.
- **No Repetition:** Once you have provided a final synthesis of your work, do not repeat yourself or provide additional summaries. For simple or direct requests, prioritize extreme brevity.
- **Formatting:** Use GitHub-flavored Markdown. Responses will be rendered in monospace.
- **Tools vs. Text:** Use tools for actions, text output *only* for communication. Do not add explanatory comments within tool calls.
@@ -2661,9 +2661,9 @@ Use the following guidelines to optimize your search and read patterns.
- **Testing:** ALWAYS search for and update related tests after making a code change. You must add a new test case to the existing test file (if one exists) or create a new test file to verify your changes.
- **User Hints:** During execution, the user may provide real-time hints (marked as "User hint:" or "User hints:"). Treat these as high-priority but scope-preserving course corrections: apply the minimal plan change needed, keep unaffected user tasks active, and never cancel/skip tasks unless cancellation is explicit for those tasks. Hints may add new tasks, modify one or more tasks, cancel specific tasks, or provide extra context only. If scope is ambiguous, ask for clarification before dropping work.
- **Confirm Ambiguity/Expansion:** Do not take significant actions beyond the clear scope of the request without confirming with the user. If the user implies a change (e.g., reports a bug) without explicitly asking for a fix, **ask for confirmation first**. If asked *how* to do something, explain first, don't just do it.
- **Explain Before Acting:** Never call tools in silence. You MUST provide a concise, one-sentence explanation of your intent or strategy immediately before executing tool calls. This is essential for transparency, especially when confirming a request or answering a question. Silence is only acceptable for repetitive, low-level discovery operations (e.g., sequential file reads) where narration would be noisy.
- **Explaining Changes:** After completing a code modification or file operation *do not* provide summaries unless asked.
- **Do Not revert changes:** Do not revert changes to the codebase unless asked to do so by the user. Only revert changes made by you if they have resulted in an error or if the user has explicitly asked you to revert the changes.
- **Explain Before Acting:** Never call tools in silence. You MUST provide a concise, one-sentence explanation of your intent or strategy immediately before executing tool calls. This is essential for transparency, especially when confirming a request or answering a question. Silence is only acceptable for repetitive, low-level discovery operations (e.g., sequential file reads) where narration would be noisy.
# Available Sub-Agents
@@ -2742,7 +2742,7 @@ Operate using a **Research -> Strategy -> Execution** lifecycle. For the Executi
- **High-Signal Output:** Focus exclusively on **intent** and **technical rationale**. Avoid conversational filler, apologies, and mechanical tool-use narration (e.g., "I will now call...").
- **Concise & Direct:** Adopt a professional, direct, and concise tone suitable for a CLI environment.
- **Minimal Output:** Aim for fewer than 3 lines of text output (excluding tool use/code generation) per response whenever practical.
- **No Chitchat:** Avoid conversational filler, preambles ("Okay, I will now..."), or postambles ("I have finished the changes...") unless they serve to explain intent as required by the 'Explain Before Acting' mandate.
- **No Chitchat:** Avoid conversational filler, preambles ("Okay, I will now..."), or postambles ("I have finished the changes...") unless they are part of the 'Explain Before Acting' mandate.
- **No Repetition:** Once you have provided a final synthesis of your work, do not repeat yourself or provide additional summaries. For simple or direct requests, prioritize extreme brevity.
- **Formatting:** Use GitHub-flavored Markdown. Responses will be rendered in monospace.
- **Tools vs. Text:** Use tools for actions, text output *only* for communication. Do not add explanatory comments within tool calls.
@@ -2815,9 +2815,9 @@ Use the following guidelines to optimize your search and read patterns.
- **Testing:** ALWAYS search for and update related tests after making a code change. You must add a new test case to the existing test file (if one exists) or create a new test file to verify your changes.
- **User Hints:** During execution, the user may provide real-time hints (marked as "User hint:" or "User hints:"). Treat these as high-priority but scope-preserving course corrections: apply the minimal plan change needed, keep unaffected user tasks active, and never cancel/skip tasks unless cancellation is explicit for those tasks. Hints may add new tasks, modify one or more tasks, cancel specific tasks, or provide extra context only. If scope is ambiguous, ask for clarification before dropping work.
- **Confirm Ambiguity/Expansion:** Do not take significant actions beyond the clear scope of the request without confirming with the user. If the user implies a change (e.g., reports a bug) without explicitly asking for a fix, **ask for confirmation first**. If asked *how* to do something, explain first, don't just do it.
- **Explain Before Acting:** Never call tools in silence. You MUST provide a concise, one-sentence explanation of your intent or strategy immediately before executing tool calls. This is essential for transparency, especially when confirming a request or answering a question. Silence is only acceptable for repetitive, low-level discovery operations (e.g., sequential file reads) where narration would be noisy.
- **Explaining Changes:** After completing a code modification or file operation *do not* provide summaries unless asked.
- **Do Not revert changes:** Do not revert changes to the codebase unless asked to do so by the user. Only revert changes made by you if they have resulted in an error or if the user has explicitly asked you to revert the changes.
- **Explain Before Acting:** Never call tools in silence. You MUST provide a concise, one-sentence explanation of your intent or strategy immediately before executing tool calls. This is essential for transparency, especially when confirming a request or answering a question. Silence is only acceptable for repetitive, low-level discovery operations (e.g., sequential file reads) where narration would be noisy.
# Available Sub-Agents
@@ -2907,7 +2907,7 @@ You are operating with a persistent file-based task tracking system located at \
- **High-Signal Output:** Focus exclusively on **intent** and **technical rationale**. Avoid conversational filler, apologies, and mechanical tool-use narration (e.g., "I will now call...").
- **Concise & Direct:** Adopt a professional, direct, and concise tone suitable for a CLI environment.
- **Minimal Output:** Aim for fewer than 3 lines of text output (excluding tool use/code generation) per response whenever practical.
- **No Chitchat:** Avoid conversational filler, preambles ("Okay, I will now..."), or postambles ("I have finished the changes...") unless they serve to explain intent as required by the 'Explain Before Acting' mandate.
- **No Chitchat:** Avoid conversational filler, preambles ("Okay, I will now..."), or postambles ("I have finished the changes...") unless they are part of the 'Explain Before Acting' mandate.
- **No Repetition:** Once you have provided a final synthesis of your work, do not repeat yourself or provide additional summaries. For simple or direct requests, prioritize extreme brevity.
- **Formatting:** Use GitHub-flavored Markdown. Responses will be rendered in monospace.
- **Tools vs. Text:** Use tools for actions, text output *only* for communication. Do not add explanatory comments within tool calls.
@@ -3221,9 +3221,9 @@ Use the following guidelines to optimize your search and read patterns.
- **Testing:** ALWAYS search for and update related tests after making a code change. You must add a new test case to the existing test file (if one exists) or create a new test file to verify your changes.
- **User Hints:** During execution, the user may provide real-time hints (marked as "User hint:" or "User hints:"). Treat these as high-priority but scope-preserving course corrections: apply the minimal plan change needed, keep unaffected user tasks active, and never cancel/skip tasks unless cancellation is explicit for those tasks. Hints may add new tasks, modify one or more tasks, cancel specific tasks, or provide extra context only. If scope is ambiguous, ask for clarification before dropping work.
- **Confirm Ambiguity/Expansion:** Do not take significant actions beyond the clear scope of the request without confirming with the user. If the user implies a change (e.g., reports a bug) without explicitly asking for a fix, **ask for confirmation first**. If asked *how* to do something, explain first, don't just do it.
- **Explain Before Acting:** Never call tools in silence. You MUST provide a concise, one-sentence explanation of your intent or strategy immediately before executing tool calls. This is essential for transparency, especially when confirming a request or answering a question. Silence is only acceptable for repetitive, low-level discovery operations (e.g., sequential file reads) where narration would be noisy.
- **Explaining Changes:** After completing a code modification or file operation *do not* provide summaries unless asked.
- **Do Not revert changes:** Do not revert changes to the codebase unless asked to do so by the user. Only revert changes made by you if they have resulted in an error or if the user has explicitly asked you to revert the changes.
- **Explain Before Acting:** Never call tools in silence. You MUST provide a concise, one-sentence explanation of your intent or strategy immediately before executing tool calls. This is essential for transparency, especially when confirming a request or answering a question. Silence is only acceptable for repetitive, low-level discovery operations (e.g., sequential file reads) where narration would be noisy.
# Available Sub-Agents
@@ -3302,7 +3302,7 @@ Operate using a **Research -> Strategy -> Execution** lifecycle. For the Executi
- **High-Signal Output:** Focus exclusively on **intent** and **technical rationale**. Avoid conversational filler, apologies, and mechanical tool-use narration (e.g., "I will now call...").
- **Concise & Direct:** Adopt a professional, direct, and concise tone suitable for a CLI environment.
- **Minimal Output:** Aim for fewer than 3 lines of text output (excluding tool use/code generation) per response whenever practical.
- **No Chitchat:** Avoid conversational filler, preambles ("Okay, I will now..."), or postambles ("I have finished the changes...") unless they serve to explain intent as required by the 'Explain Before Acting' mandate.
- **No Chitchat:** Avoid conversational filler, preambles ("Okay, I will now..."), or postambles ("I have finished the changes...") unless they are part of the 'Explain Before Acting' mandate.
- **No Repetition:** Once you have provided a final synthesis of your work, do not repeat yourself or provide additional summaries. For simple or direct requests, prioritize extreme brevity.
- **Formatting:** Use GitHub-flavored Markdown. Responses will be rendered in monospace.
- **Tools vs. Text:** Use tools for actions, text output *only* for communication. Do not add explanatory comments within tool calls.
@@ -3375,9 +3375,9 @@ Use the following guidelines to optimize your search and read patterns.
- **Testing:** ALWAYS search for and update related tests after making a code change. You must add a new test case to the existing test file (if one exists) or create a new test file to verify your changes.
- **User Hints:** During execution, the user may provide real-time hints (marked as "User hint:" or "User hints:"). Treat these as high-priority but scope-preserving course corrections: apply the minimal plan change needed, keep unaffected user tasks active, and never cancel/skip tasks unless cancellation is explicit for those tasks. Hints may add new tasks, modify one or more tasks, cancel specific tasks, or provide extra context only. If scope is ambiguous, ask for clarification before dropping work.
- **Confirm Ambiguity/Expansion:** Do not take significant actions beyond the clear scope of the request without confirming with the user. If the user implies a change (e.g., reports a bug) without explicitly asking for a fix, **ask for confirmation first**. If asked *how* to do something, explain first, don't just do it.
- **Explain Before Acting:** Never call tools in silence. You MUST provide a concise, one-sentence explanation of your intent or strategy immediately before executing tool calls. This is essential for transparency, especially when confirming a request or answering a question. Silence is only acceptable for repetitive, low-level discovery operations (e.g., sequential file reads) where narration would be noisy.
- **Explaining Changes:** After completing a code modification or file operation *do not* provide summaries unless asked.
- **Do Not revert changes:** Do not revert changes to the codebase unless asked to do so by the user. Only revert changes made by you if they have resulted in an error or if the user has explicitly asked you to revert the changes.
- **Explain Before Acting:** Never call tools in silence. You MUST provide a concise, one-sentence explanation of your intent or strategy immediately before executing tool calls. This is essential for transparency, especially when confirming a request or answering a question. Silence is only acceptable for repetitive, low-level discovery operations (e.g., sequential file reads) where narration would be noisy.
# Available Sub-Agents
@@ -3456,7 +3456,7 @@ Operate using a **Research -> Strategy -> Execution** lifecycle. For the Executi
- **High-Signal Output:** Focus exclusively on **intent** and **technical rationale**. Avoid conversational filler, apologies, and mechanical tool-use narration (e.g., "I will now call...").
- **Concise & Direct:** Adopt a professional, direct, and concise tone suitable for a CLI environment.
- **Minimal Output:** Aim for fewer than 3 lines of text output (excluding tool use/code generation) per response whenever practical.
- **No Chitchat:** Avoid conversational filler, preambles ("Okay, I will now..."), or postambles ("I have finished the changes...") unless they serve to explain intent as required by the 'Explain Before Acting' mandate.
- **No Chitchat:** Avoid conversational filler, preambles ("Okay, I will now..."), or postambles ("I have finished the changes...") unless they are part of the 'Explain Before Acting' mandate.
- **No Repetition:** Once you have provided a final synthesis of your work, do not repeat yourself or provide additional summaries. For simple or direct requests, prioritize extreme brevity.
- **Formatting:** Use GitHub-flavored Markdown. Responses will be rendered in monospace.
- **Tools vs. Text:** Use tools for actions, text output *only* for communication. Do not add explanatory comments within tool calls.
@@ -3641,9 +3641,9 @@ Use the following guidelines to optimize your search and read patterns.
- **Testing:** ALWAYS search for and update related tests after making a code change. You must add a new test case to the existing test file (if one exists) or create a new test file to verify your changes.
- **User Hints:** During execution, the user may provide real-time hints (marked as "User hint:" or "User hints:"). Treat these as high-priority but scope-preserving course corrections: apply the minimal plan change needed, keep unaffected user tasks active, and never cancel/skip tasks unless cancellation is explicit for those tasks. Hints may add new tasks, modify one or more tasks, cancel specific tasks, or provide extra context only. If scope is ambiguous, ask for clarification before dropping work.
- **Confirm Ambiguity/Expansion:** Do not take significant actions beyond the clear scope of the request without confirming with the user. If the user implies a change (e.g., reports a bug) without explicitly asking for a fix, **ask for confirmation first**. If asked *how* to do something, explain first, don't just do it.
- **Explain Before Acting:** Never call tools in silence. You MUST provide a concise, one-sentence explanation of your intent or strategy immediately before executing tool calls. This is essential for transparency, especially when confirming a request or answering a question. Silence is only acceptable for repetitive, low-level discovery operations (e.g., sequential file reads) where narration would be noisy.
- **Explaining Changes:** After completing a code modification or file operation *do not* provide summaries unless asked.
- **Do Not revert changes:** Do not revert changes to the codebase unless asked to do so by the user. Only revert changes made by you if they have resulted in an error or if the user has explicitly asked you to revert the changes.
- **Explain Before Acting:** Never call tools in silence. You MUST provide a concise, one-sentence explanation of your intent or strategy immediately before executing tool calls. This is essential for transparency, especially when confirming a request or answering a question. Silence is only acceptable for repetitive, low-level discovery operations (e.g., sequential file reads) where narration would be noisy.
# Available Sub-Agents
@@ -3722,7 +3722,7 @@ Operate using a **Research -> Strategy -> Execution** lifecycle. For the Executi
- **High-Signal Output:** Focus exclusively on **intent** and **technical rationale**. Avoid conversational filler, apologies, and mechanical tool-use narration (e.g., "I will now call...").
- **Concise & Direct:** Adopt a professional, direct, and concise tone suitable for a CLI environment.
- **Minimal Output:** Aim for fewer than 3 lines of text output (excluding tool use/code generation) per response whenever practical.
- **No Chitchat:** Avoid conversational filler, preambles ("Okay, I will now..."), or postambles ("I have finished the changes...") unless they serve to explain intent as required by the 'Explain Before Acting' mandate.
- **No Chitchat:** Avoid conversational filler, preambles ("Okay, I will now..."), or postambles ("I have finished the changes...") unless they are part of the 'Explain Before Acting' mandate.
- **No Repetition:** Once you have provided a final synthesis of your work, do not repeat yourself or provide additional summaries. For simple or direct requests, prioritize extreme brevity.
- **Formatting:** Use GitHub-flavored Markdown. Responses will be rendered in monospace.
- **Tools vs. Text:** Use tools for actions, text output *only* for communication. Do not add explanatory comments within tool calls.
@@ -3795,9 +3795,9 @@ Use the following guidelines to optimize your search and read patterns.
- **Testing:** ALWAYS search for and update related tests after making a code change. You must add a new test case to the existing test file (if one exists) or create a new test file to verify your changes.
- **User Hints:** During execution, the user may provide real-time hints (marked as "User hint:" or "User hints:"). Treat these as high-priority but scope-preserving course corrections: apply the minimal plan change needed, keep unaffected user tasks active, and never cancel/skip tasks unless cancellation is explicit for those tasks. Hints may add new tasks, modify one or more tasks, cancel specific tasks, or provide extra context only. If scope is ambiguous, ask for clarification before dropping work.
- **Confirm Ambiguity/Expansion:** Do not take significant actions beyond the clear scope of the request without confirming with the user. If the user implies a change (e.g., reports a bug) without explicitly asking for a fix, **ask for confirmation first**. If asked *how* to do something, explain first, don't just do it.
- **Explain Before Acting:** Never call tools in silence. You MUST provide a concise, one-sentence explanation of your intent or strategy immediately before executing tool calls. This is essential for transparency, especially when confirming a request or answering a question. Silence is only acceptable for repetitive, low-level discovery operations (e.g., sequential file reads) where narration would be noisy.
- **Explaining Changes:** After completing a code modification or file operation *do not* provide summaries unless asked.
- **Do Not revert changes:** Do not revert changes to the codebase unless asked to do so by the user. Only revert changes made by you if they have resulted in an error or if the user has explicitly asked you to revert the changes.
- **Explain Before Acting:** Never call tools in silence. You MUST provide a concise, one-sentence explanation of your intent or strategy immediately before executing tool calls. This is essential for transparency, especially when confirming a request or answering a question. Silence is only acceptable for repetitive, low-level discovery operations (e.g., sequential file reads) where narration would be noisy.
# Available Sub-Agents
@@ -3876,7 +3876,7 @@ Operate using a **Research -> Strategy -> Execution** lifecycle. For the Executi
- **High-Signal Output:** Focus exclusively on **intent** and **technical rationale**. Avoid conversational filler, apologies, and mechanical tool-use narration (e.g., "I will now call...").
- **Concise & Direct:** Adopt a professional, direct, and concise tone suitable for a CLI environment.
- **Minimal Output:** Aim for fewer than 3 lines of text output (excluding tool use/code generation) per response whenever practical.
- **No Chitchat:** Avoid conversational filler, preambles ("Okay, I will now..."), or postambles ("I have finished the changes...") unless they serve to explain intent as required by the 'Explain Before Acting' mandate.
- **No Chitchat:** Avoid conversational filler, preambles ("Okay, I will now..."), or postambles ("I have finished the changes...") unless they are part of the 'Explain Before Acting' mandate.
- **No Repetition:** Once you have provided a final synthesis of your work, do not repeat yourself or provide additional summaries. For simple or direct requests, prioritize extreme brevity.
- **Formatting:** Use GitHub-flavored Markdown. Responses will be rendered in monospace.
- **Tools vs. Text:** Use tools for actions, text output *only* for communication. Do not add explanatory comments within tool calls.
+1 -1
View File
@@ -133,7 +133,7 @@ export class CoreToolScheduler {
this.onAllToolCallsComplete = options.onAllToolCallsComplete;
this.onToolCallsUpdate = options.onToolCallsUpdate;
this.getPreferredEditor = options.getPreferredEditor;
this.toolExecutor = new ToolExecutor(this.context.config);
this.toolExecutor = new ToolExecutor(this.context);
this.toolModifier = new ToolModificationHandler();
// Subscribe to message bus for ASK_USER policy decisions
+2
View File
@@ -95,6 +95,7 @@ describe('Core System Prompt (prompts.ts)', () => {
},
isInteractive: vi.fn().mockReturnValue(true),
isInteractiveShellEnabled: vi.fn().mockReturnValue(true),
isTopicUpdateNarrationEnabled: vi.fn().mockReturnValue(false),
isAgentsEnabled: vi.fn().mockReturnValue(false),
getPreviewFeatures: vi.fn().mockReturnValue(true),
getModel: vi.fn().mockReturnValue(DEFAULT_GEMINI_MODEL_AUTO),
@@ -408,6 +409,7 @@ describe('Core System Prompt (prompts.ts)', () => {
},
isInteractive: vi.fn().mockReturnValue(false),
isInteractiveShellEnabled: vi.fn().mockReturnValue(false),
isTopicUpdateNarrationEnabled: vi.fn().mockReturnValue(false),
isAgentsEnabled: vi.fn().mockReturnValue(false),
getModel: vi.fn().mockReturnValue('auto'),
getActiveModel: vi.fn().mockReturnValue(PREVIEW_GEMINI_MODEL),
@@ -60,6 +60,7 @@ describe('PromptProvider', () => {
},
isInteractive: vi.fn().mockReturnValue(true),
isInteractiveShellEnabled: vi.fn().mockReturnValue(true),
isTopicUpdateNarrationEnabled: vi.fn().mockReturnValue(false),
getSkillManager: vi.fn().mockReturnValue({
getSkills: vi.fn().mockReturnValue([]),
}),
@@ -124,6 +124,7 @@ export class PromptProvider {
hasSkills: skills.length > 0,
hasHierarchicalMemory,
contextFilenames,
topicUpdateNarration: context.config.isTopicUpdateNarrationEnabled(),
})),
subAgents: this.withSection('agentContexts', () =>
context.config
@@ -162,6 +163,8 @@ export class PromptProvider {
? { path: approvedPlanPath }
: undefined,
taskTracker: context.config.isTrackerEnabled(),
topicUpdateNarration:
context.config.isTopicUpdateNarrationEnabled(),
}),
!isPlanMode,
),
@@ -183,6 +186,8 @@ export class PromptProvider {
enableShellEfficiency:
context.config.getEnableShellOutputEfficiency(),
interactiveShellEnabled: context.config.isInteractiveShellEnabled(),
topicUpdateNarration:
context.config.isTopicUpdateNarrationEnabled(),
}),
),
sandbox: this.withSection('sandbox', () => getSandboxMode()),
+69 -6
View File
@@ -60,6 +60,7 @@ export interface CoreMandatesOptions {
hasSkills: boolean;
hasHierarchicalMemory: boolean;
contextFilenames?: string[];
topicUpdateNarration: boolean;
}
export interface PrimaryWorkflowsOptions {
@@ -71,11 +72,13 @@ export interface PrimaryWorkflowsOptions {
enableGlob: boolean;
approvedPlan?: { path: string };
taskTracker?: boolean;
topicUpdateNarration: boolean;
}
export interface OperationalGuidelinesOptions {
interactive: boolean;
interactiveShellEnabled: boolean;
topicUpdateNarration: boolean;
}
export type SandboxMode = 'macos-seatbelt' | 'generic' | 'outside';
@@ -223,10 +226,12 @@ Use the following guidelines to optimize your search and read patterns.
- **Proactiveness:** When executing a Directive, persist through errors and obstacles by diagnosing failures in the execution phase and, if necessary, backtracking to the research or strategy phases to adjust your approach until a successful, verified outcome is achieved. Fulfill the user's request thoroughly, including adding tests when adding features or fixing bugs. Take reasonable liberties to fulfill broad goals while staying within the requested scope; however, prioritize simplicity and the removal of redundant logic over providing "just-in-case" alternatives that diverge from the established path.
- **Testing:** ALWAYS search for and update related tests after making a code change. You must add a new test case to the existing test file (if one exists) or create a new test file to verify your changes.${mandateConflictResolution(options.hasHierarchicalMemory)}
- **User Hints:** During execution, the user may provide real-time hints (marked as "User hint:" or "User hints:"). Treat these as high-priority but scope-preserving course corrections: apply the minimal plan change needed, keep unaffected user tasks active, and never cancel/skip tasks unless cancellation is explicit for those tasks. Hints may add new tasks, modify one or more tasks, cancel specific tasks, or provide extra context only. If scope is ambiguous, ask for clarification before dropping work.
- ${mandateConfirm(options.interactive)}
- **Explaining Changes:** After completing a code modification or file operation *do not* provide summaries unless asked.
- **Do Not revert changes:** Do not revert changes to the codebase unless asked to do so by the user. Only revert changes made by you if they have resulted in an error or if the user has explicitly asked you to revert the changes.${mandateSkillGuidance(options.hasSkills)}
- **Explain Before Acting:** Never call tools in silence. You MUST provide a concise, one-sentence explanation of your intent or strategy immediately before executing tool calls. This is essential for transparency, especially when confirming a request or answering a question. Silence is only acceptable for repetitive, low-level discovery operations (e.g., sequential file reads) where narration would be noisy.${mandateContinueWork(options.interactive)}
- ${mandateConfirm(options.interactive)}${
options.topicUpdateNarration
? mandateTopicUpdateModel()
: mandateExplainBeforeActing()
}
- **Do Not revert changes:** Do not revert changes to the codebase unless asked to do so by the user. Only revert changes made by you if they have resulted in an error or if the user has explicitly asked you to revert the changes.${mandateSkillGuidance(options.hasSkills)}${mandateContinueWork(options.interactive)}
`.trim();
}
@@ -341,10 +346,18 @@ export function renderOperationalGuidelines(
## Tone and Style
- **Role:** A senior software engineer and collaborative peer programmer.
- **High-Signal Output:** Focus exclusively on **intent** and **technical rationale**. Avoid conversational filler, apologies, and mechanical tool-use narration (e.g., "I will now call...").
- **High-Signal Output:** Focus exclusively on **intent** and **technical rationale**. Avoid conversational filler, apologies, and ${
options.topicUpdateNarration
? 'per-tool explanations.'
: 'mechanical tool-use narration (e.g., "I will now call...").'
}
- **Concise & Direct:** Adopt a professional, direct, and concise tone suitable for a CLI environment.
- **Minimal Output:** Aim for fewer than 3 lines of text output (excluding tool use/code generation) per response whenever practical.
- **No Chitchat:** Avoid conversational filler, preambles ("Okay, I will now..."), or postambles ("I have finished the changes...") unless they serve to explain intent as required by the 'Explain Before Acting' mandate.
- **No Chitchat:** Avoid conversational filler, preambles ("Okay, I will now..."), or postambles ("I have finished the changes...") unless they are ${
options.topicUpdateNarration
? 'part of the **Topic Model**.'
: "part of the 'Explain Before Acting' mandate."
}
- **No Repetition:** Once you have provided a final synthesis of your work, do not repeat yourself or provide additional summaries. For simple or direct requests, prioritize extreme brevity.
- **Formatting:** Use GitHub-flavored Markdown. Responses will be rendered in monospace.
- **Tools vs. Text:** Use tools for actions, text output *only* for communication. Do not add explanatory comments within tool calls.
@@ -560,6 +573,56 @@ function mandateConfirm(interactive: boolean): string {
: '**Handle Ambiguity/Expansion:** Do not take significant actions beyond the clear scope of the request. If the user implies a change (e.g., reports a bug) without explicitly asking for a fix, do not perform it automatically.';
}
function mandateTopicUpdateModel(): string {
return `
- **Protocol: Topic Model**
You are an agentic system. You must maintain a visible state log that tracks broad logical phases using a specific header format.
- **1. Topic Initialization & Persistence:**
- **The Trigger:** You MUST issue a \`Topic: <Phase> : <Brief Summary>\` header ONLY when beginning a task or when the broad logical nature of the task changes (e.g., transitioning from research to implementation).
- **The Format:** Use exactly \`Topic: <Phase> : <Brief Summary>\` (e.g., \`Topic: <Research> : Researching Agent Skills in the repo\`).
- **Persistence:** Once a Topic is declared, do NOT repeat it for subsequent tool calls or in subsequent messages within that same phase.
- **Start of Task:** Your very first tool execution must be preceded by a Topic header.
- **2. Tool Execution Protocol (Zero-Noise):**
- **No Per-Tool Headers:** It is a violation of protocol to print "Topic:" before every tool call.
- **Silent Mode:** No conversational filler, no "I will now...", and no summaries between tools.
- Only the Topic header at the start of a broad phase is permitted to break the silence. Everything in between must be silent.
- **3. Thinking Protocol:**
- Use internal thought blocks to keep track of what tools you have called, plan your next steps, and reason about the task.
- Without reasoning and tracking in thought blocks, you may lose context.
- Always use the required syntax for thought blocks to ensure they remain hidden from the user interface.
- **4. Completion:**
- Only when the entire task is finalized do you provide a **Final Summary**.
**IMPORTANT: Topic Headers vs. Thoughts**
The \`Topic: <Phase> : <Brief Summary>\` header must **NOT** be placed inside a thought block. It must be standard text output so that it is properly rendered and displayed in the UI.
**Correct State Log Example:**
\`\`\`
Topic: <Research> : Researching Agent Skills in the repo
<tool_call 1>
<tool_call 2>
<tool_call 3>
Topic: <Implementation> : Implementing the skill-creator logic
<tool_call 1>
<tool_call 2>
The task is complete. [Final Summary]
\`\`\`
- **Constraint Enforcement:** If you repeat a "Topic:" line without a fundamental shift in work, or if you provide a Topic for every tool call, you have failed the system integrity protocol.`;
}
function mandateExplainBeforeActing(): string {
return `
- **Explain Before Acting:** Never call tools in silence. You MUST provide a concise, one-sentence explanation of your intent or strategy immediately before executing tool calls. This is essential for transparency, especially when confirming a request or answering a question. Silence is only acceptable for repetitive, low-level discovery operations (e.g., sequential file reads) where narration would be noisy.
- **Explaining Changes:** After completing a code modification or file operation *do not* provide summaries unless asked.`;
}
function mandateSkillGuidance(hasSkills: boolean): string {
if (!hasSkills) return '';
return `
@@ -36,7 +36,7 @@ export class ApprovalModeStrategy implements RoutingStrategy {
const model = context.requestedModel ?? config.getModel();
// This strategy only applies to "auto" models.
if (!isAutoModel(model)) {
if (!isAutoModel(model, config)) {
return null;
}
@@ -139,7 +139,7 @@ export class ClassifierStrategy implements RoutingStrategy {
const model = context.requestedModel ?? config.getModel();
if (
(await config.getNumericalRoutingEnabled()) &&
isGemini3Model(model)
isGemini3Model(model, config)
) {
return null;
}
@@ -109,7 +109,7 @@ export class NumericalClassifierStrategy implements RoutingStrategy {
return null;
}
if (!isGemini3Model(model)) {
if (!isGemini3Model(model, config)) {
return null;
}
@@ -29,7 +29,7 @@ export class OverrideStrategy implements RoutingStrategy {
const overrideModel = context.requestedModel ?? config.getModel();
// If the model is 'auto' we should pass to the next strategy.
if (isAutoModel(overrideModel)) {
if (isAutoModel(overrideModel, config)) {
return null;
}
@@ -296,6 +296,7 @@ export class ToolExecutor {
call.request.callId,
output,
this.config.getActiveModel(),
this.config,
);
// Inject the cancellation error into the response object
@@ -352,6 +353,7 @@ export class ToolExecutor {
callId,
content,
this.config.getActiveModel(),
this.config,
);
const successResponse: ToolCallResponseInfo = {
@@ -439,6 +439,7 @@ describe('ChatRecordingService', () => {
describe('deleteSession', () => {
it('should delete the session file, tool outputs, session directory, and logs if they exist', () => {
const sessionId = 'test-session-id';
const shortId = '12345678';
const chatsDir = path.join(testTempDir, 'chats');
const logsDir = path.join(testTempDir, 'logs');
const toolOutputsDir = path.join(testTempDir, 'tool-outputs');
@@ -449,8 +450,12 @@ describe('ChatRecordingService', () => {
fs.mkdirSync(toolOutputsDir, { recursive: true });
fs.mkdirSync(sessionDir, { recursive: true });
const sessionFile = path.join(chatsDir, `${sessionId}.json`);
fs.writeFileSync(sessionFile, '{}');
// Create main session file with timestamp
const sessionFile = path.join(
chatsDir,
`session-2023-01-01T00-00-${shortId}.json`,
);
fs.writeFileSync(sessionFile, JSON.stringify({ sessionId }));
const logFile = path.join(logsDir, `session-${sessionId}.jsonl`);
fs.writeFileSync(logFile, '{}');
@@ -458,7 +463,8 @@ describe('ChatRecordingService', () => {
const toolOutputDir = path.join(toolOutputsDir, `session-${sessionId}`);
fs.mkdirSync(toolOutputDir, { recursive: true });
chatRecordingService.deleteSession(sessionId);
// Call with shortId
chatRecordingService.deleteSession(shortId);
expect(fs.existsSync(sessionFile)).toBe(false);
expect(fs.existsSync(logFile)).toBe(false);
@@ -466,6 +472,93 @@ describe('ChatRecordingService', () => {
expect(fs.existsSync(sessionDir)).toBe(false);
});
it('should delete subagent files and their logs when parent is deleted', () => {
const parentSessionId = '12345678-session-id';
const shortId = '12345678';
const subagentSessionId = 'subagent-session-id';
const chatsDir = path.join(testTempDir, 'chats');
const logsDir = path.join(testTempDir, 'logs');
const toolOutputsDir = path.join(testTempDir, 'tool-outputs');
fs.mkdirSync(chatsDir, { recursive: true });
fs.mkdirSync(logsDir, { recursive: true });
fs.mkdirSync(toolOutputsDir, { recursive: true });
// Create parent session file
const parentFile = path.join(
chatsDir,
`session-2023-01-01T00-00-${shortId}.json`,
);
fs.writeFileSync(
parentFile,
JSON.stringify({ sessionId: parentSessionId }),
);
// Create subagent session file
const subagentFile = path.join(
chatsDir,
`session-2023-01-01T00-01-${shortId}.json`,
);
fs.writeFileSync(
subagentFile,
JSON.stringify({ sessionId: subagentSessionId, kind: 'subagent' }),
);
// Create logs for both
const parentLog = path.join(logsDir, `session-${parentSessionId}.jsonl`);
fs.writeFileSync(parentLog, '{}');
const subagentLog = path.join(
logsDir,
`session-${subagentSessionId}.jsonl`,
);
fs.writeFileSync(subagentLog, '{}');
// Create tool outputs for both
const parentToolOutputDir = path.join(
toolOutputsDir,
`session-${parentSessionId}`,
);
fs.mkdirSync(parentToolOutputDir, { recursive: true });
const subagentToolOutputDir = path.join(
toolOutputsDir,
`session-${subagentSessionId}`,
);
fs.mkdirSync(subagentToolOutputDir, { recursive: true });
// Call with parent sessionId
chatRecordingService.deleteSession(parentSessionId);
expect(fs.existsSync(parentFile)).toBe(false);
expect(fs.existsSync(subagentFile)).toBe(false);
expect(fs.existsSync(parentLog)).toBe(false);
expect(fs.existsSync(subagentLog)).toBe(false);
expect(fs.existsSync(parentToolOutputDir)).toBe(false);
expect(fs.existsSync(subagentToolOutputDir)).toBe(false);
});
it('should delete by basename', () => {
const sessionId = 'test-session-id';
const shortId = '12345678';
const chatsDir = path.join(testTempDir, 'chats');
const logsDir = path.join(testTempDir, 'logs');
fs.mkdirSync(chatsDir, { recursive: true });
fs.mkdirSync(logsDir, { recursive: true });
const basename = `session-2023-01-01T00-00-${shortId}`;
const sessionFile = path.join(chatsDir, `${basename}.json`);
fs.writeFileSync(sessionFile, JSON.stringify({ sessionId }));
const logFile = path.join(logsDir, `session-${sessionId}.jsonl`);
fs.writeFileSync(logFile, '{}');
// Call with basename
chatRecordingService.deleteSession(basename);
expect(fs.existsSync(sessionFile)).toBe(false);
expect(fs.existsSync(logFile)).toBe(false);
});
it('should not throw if session file does not exist', () => {
expect(() =>
chatRecordingService.deleteSession('non-existent'),
@@ -590,46 +590,27 @@ export class ChatRecordingService {
}
/**
* Deletes a session file by session ID.
* Deletes a session file by sessionId, filename, or basename.
* Derives an 8-character shortId to find and delete all associated files
* (parent and subagents).
*
* @throws {Error} If shortId validation fails.
*/
deleteSession(sessionId: string): void {
deleteSession(sessionIdOrBasename: string): void {
try {
const tempDir = this.context.config.storage.getProjectTempDir();
const chatsDir = path.join(tempDir, 'chats');
const sessionPath = path.join(chatsDir, `${sessionId}.json`);
if (fs.existsSync(sessionPath)) {
fs.unlinkSync(sessionPath);
const shortId = this.deriveShortId(sessionIdOrBasename);
if (!fs.existsSync(chatsDir)) {
return; // Nothing to delete
}
// Cleanup Activity logs in the project logs directory
const logsDir = path.join(tempDir, 'logs');
const logPath = path.join(logsDir, `session-${sessionId}.jsonl`);
if (fs.existsSync(logPath)) {
fs.unlinkSync(logPath);
}
const matchingFiles = this.getMatchingSessionFiles(chatsDir, shortId);
// Cleanup tool outputs for this session
const safeSessionId = sanitizeFilenamePart(sessionId);
const toolOutputDir = path.join(
tempDir,
'tool-outputs',
`session-${safeSessionId}`,
);
// Robustness: Ensure the path is strictly within the tool-outputs base
const toolOutputsBase = path.join(tempDir, 'tool-outputs');
if (
fs.existsSync(toolOutputDir) &&
toolOutputDir.startsWith(toolOutputsBase)
) {
fs.rmSync(toolOutputDir, { recursive: true, force: true });
}
// ALSO cleanup the session-specific directory (contains plans, tasks, etc.)
const sessionDir = path.join(tempDir, safeSessionId);
// Robustness: Ensure the path is strictly within the temp root
if (fs.existsSync(sessionDir) && sessionDir.startsWith(tempDir)) {
fs.rmSync(sessionDir, { recursive: true, force: true });
for (const file of matchingFiles) {
this.deleteSessionAndArtifacts(chatsDir, file, tempDir);
}
} catch (error) {
debugLogger.error('Error deleting session file.', error);
@@ -637,6 +618,115 @@ export class ChatRecordingService {
}
}
/**
* Derives an 8-character shortId from a sessionId, filename, or basename.
*/
private deriveShortId(sessionIdOrBasename: string): string {
let shortId = sessionIdOrBasename;
if (sessionIdOrBasename.startsWith(SESSION_FILE_PREFIX)) {
const withoutExt = sessionIdOrBasename.replace('.json', '');
const parts = withoutExt.split('-');
shortId = parts[parts.length - 1];
} else if (sessionIdOrBasename.length >= 8) {
shortId = sessionIdOrBasename.slice(0, 8);
} else {
throw new Error('Invalid sessionId or basename provided for deletion');
}
if (shortId.length !== 8) {
throw new Error('Derived shortId must be exactly 8 characters');
}
return shortId;
}
/**
* Finds all session files matching the pattern session-*-<shortId>.json
*/
private getMatchingSessionFiles(chatsDir: string, shortId: string): string[] {
const files = fs.readdirSync(chatsDir);
return files.filter(
(f) =>
f.startsWith(SESSION_FILE_PREFIX) && f.endsWith(`-${shortId}.json`),
);
}
/**
* Deletes a single session file and its associated logs, tool-outputs, and directory.
*/
private deleteSessionAndArtifacts(
chatsDir: string,
file: string,
tempDir: string,
): void {
const filePath = path.join(chatsDir, file);
try {
const fileContent = fs.readFileSync(filePath, 'utf8');
const content = JSON.parse(fileContent) as unknown;
let fullSessionId: string | undefined;
if (content && typeof content === 'object' && 'sessionId' in content) {
const id = (content as Record<string, unknown>)['sessionId'];
if (typeof id === 'string') {
fullSessionId = id;
}
}
// Delete the session file
fs.unlinkSync(filePath);
if (fullSessionId) {
this.deleteSessionLogs(fullSessionId, tempDir);
this.deleteSessionToolOutputs(fullSessionId, tempDir);
this.deleteSessionDirectory(fullSessionId, tempDir);
}
} catch (error) {
debugLogger.error(`Error deleting associated file ${file}:`, error);
}
}
/**
* Cleans up activity logs for a session.
*/
private deleteSessionLogs(sessionId: string, tempDir: string): void {
const logsDir = path.join(tempDir, 'logs');
const safeSessionId = sanitizeFilenamePart(sessionId);
const logPath = path.join(logsDir, `session-${safeSessionId}.jsonl`);
if (fs.existsSync(logPath) && logPath.startsWith(logsDir)) {
fs.unlinkSync(logPath);
}
}
/**
* Cleans up tool outputs for a session.
*/
private deleteSessionToolOutputs(sessionId: string, tempDir: string): void {
const safeSessionId = sanitizeFilenamePart(sessionId);
const toolOutputDir = path.join(
tempDir,
'tool-outputs',
`session-${safeSessionId}`,
);
const toolOutputsBase = path.join(tempDir, 'tool-outputs');
if (
fs.existsSync(toolOutputDir) &&
toolOutputDir.startsWith(toolOutputsBase)
) {
fs.rmSync(toolOutputDir, { recursive: true, force: true });
}
}
/**
* Cleans up the session-specific directory.
*/
private deleteSessionDirectory(sessionId: string, tempDir: string): void {
const safeSessionId = sanitizeFilenamePart(sessionId);
const sessionDir = path.join(tempDir, safeSessionId);
if (fs.existsSync(sessionDir) && sessionDir.startsWith(tempDir)) {
fs.rmSync(sessionDir, { recursive: true, force: true });
}
}
/**
* Rewinds the conversation to the state just before the specified message ID.
* All messages from (and including) the specified ID onwards are removed.
@@ -51,11 +51,34 @@ export interface ModelConfigAlias {
modelConfig: ModelConfig;
}
// A model definition is a mapping from a model name to a list of features
// that the model supports. Model names can be either direct model IDs
// (gemini-2.5-pro) or aliases (auto).
export interface ModelDefinition {
displayName?: string;
tier?: string; // 'pro' | 'flash' | 'flash-lite' | 'custom' | 'auto'
family?: string; // The gemini family, e.g. 'gemini-3' | 'gemini-2'
isPreview?: boolean;
// Specifies which view the model should appear in. If unset, the model will
// not appear in the dialog.
dialogLocation?: 'main' | 'manual';
/** A short description of the model for the dialog. */
dialogDescription?: string;
features?: {
// Whether the model supports thinking.
thinking?: boolean;
// Whether the model supports mutlimodal function responses. This is
// supported in Gemini 3.
multimodalToolUse?: boolean;
};
}
export interface ModelConfigServiceConfig {
aliases?: Record<string, ModelConfigAlias>;
customAliases?: Record<string, ModelConfigAlias>;
overrides?: ModelConfigOverride[];
customOverrides?: ModelConfigOverride[];
modelDefinitions?: Record<string, ModelDefinition>;
}
const MAX_ALIAS_CHAIN_DEPTH = 100;
@@ -76,6 +99,28 @@ export class ModelConfigService {
// TODO(12597): Process config to build a typed alias hierarchy.
constructor(private readonly config: ModelConfigServiceConfig) {}
getModelDefinition(modelId: string): ModelDefinition | undefined {
const definition = this.config.modelDefinitions?.[modelId];
if (definition) {
return definition;
}
// For unknown models, return an implicit custom definition to match legacy behavior.
if (!modelId.startsWith('gemini-')) {
return {
tier: 'custom',
family: 'custom',
features: {},
};
}
return undefined;
}
getModelDefinitions(): Record<string, ModelDefinition> {
return this.config.modelDefinitions ?? {};
}
registerRuntimeModelConfig(aliasName: string, alias: ModelConfigAlias): void {
this.runtimeAliases[aliasName] = alias;
}
+2
View File
@@ -27,6 +27,8 @@ export interface SkillDefinition {
disabled?: boolean;
/** Whether the skill is a built-in skill. */
isBuiltin?: boolean;
/** The name of the extension that provided this skill, if any. */
extensionName?: string;
}
export const FRONTMATTER_REGEX =
@@ -89,6 +89,7 @@ const mockHeapStatistics = {
total_global_handles_size: 8192,
used_global_handles_size: 4096,
external_memory: 2097152,
total_allocated_bytes: 31457280,
};
const mockHeapSpaceStatistics = [
@@ -296,7 +296,7 @@ describe('McpClientManager', () => {
// A NEW McpClient should have been constructed with the updated config
expect(constructorCalls).toHaveLength(2);
expect(constructorCalls[1][1]).toBe(updatedConfig);
expect(constructorCalls[1][1]).toMatchObject(updatedConfig);
});
});
@@ -415,7 +415,7 @@ describe('McpClientManager', () => {
expect(manager.getMcpServers()).not.toHaveProperty('test-server');
});
it('should ignore an extension attempting to register a server with an existing name', async () => {
it('should merge extension configuration with an existing user-configured server', async () => {
const manager = new McpClientManager('0.0.1', toolRegistry, mockConfig);
const userConfig = { command: 'node', args: ['user-server.js'] };
@@ -441,8 +441,144 @@ describe('McpClientManager', () => {
await manager.startExtension(extension);
expect(mockedMcpClient.disconnect).not.toHaveBeenCalled();
expect(mockedMcpClient.connect).toHaveBeenCalledTimes(1);
// It should disconnect the user-only version and reconnect with the merged version
expect(mockedMcpClient.disconnect).toHaveBeenCalledTimes(1);
expect(mockedMcpClient.connect).toHaveBeenCalledTimes(2);
// Verify user settings (command/args) still win in the merged config
const lastCall = vi.mocked(McpClient).mock.calls[1];
expect(lastCall[1].command).toBe('node');
expect(lastCall[1].args).toEqual(['user-server.js']);
expect(lastCall[1].extension).toEqual(extension);
});
it('should securely merge tool lists and env variables regardless of load order', async () => {
const manager = new McpClientManager('0.0.1', toolRegistry, mockConfig);
const userConfig = {
excludeTools: ['user-tool'],
includeTools: ['shared-inc', 'user-only-inc'],
env: { USER_VAR: 'user-val', OVERRIDE_VAR: 'user-override' },
};
const extension: GeminiCLIExtension = {
name: 'test-extension',
mcpServers: {
'test-server': {
command: 'node',
args: ['ext.js'],
excludeTools: ['ext-tool'],
includeTools: ['shared-inc', 'ext-only-inc'],
env: { EXT_VAR: 'ext-val', OVERRIDE_VAR: 'ext-override' },
},
},
isActive: true,
version: '1.0.0',
path: '/some-path',
contextFiles: [],
id: '123',
};
// Case 1: Extension loads first, then User config (e.g. from startConfiguredMcpServers)
await manager.startExtension(extension);
mockedMcpClient.getServerConfig.mockReturnValue({
...extension.mcpServers!['test-server'],
extension,
});
await manager.maybeDiscoverMcpServer('test-server', userConfig);
let lastCall = vi.mocked(McpClient).mock.calls[1]; // Second call due to re-discovery
let mergedConfig = lastCall[1];
// Exclude list should be unioned (most restrictive)
expect(mergedConfig.excludeTools).toContain('ext-tool');
expect(mergedConfig.excludeTools).toContain('user-tool');
// Include list should be intersected (most restrictive)
expect(mergedConfig.includeTools).toContain('shared-inc');
expect(mergedConfig.includeTools).not.toContain('user-only-inc');
expect(mergedConfig.includeTools).not.toContain('ext-only-inc');
expect(mergedConfig.env!['EXT_VAR']).toBe('ext-val');
expect(mergedConfig.env!['USER_VAR']).toBe('user-val');
expect(mergedConfig.env!['OVERRIDE_VAR']).toBe('user-override');
expect(mergedConfig.extension).toBe(extension); // Extension ID preserved!
// Reset for Case 2
vi.mocked(McpClient).mockClear();
const manager2 = new McpClientManager('0.0.1', toolRegistry, mockConfig);
// Case 2: User config loads first, then Extension loads
await manager2.maybeDiscoverMcpServer('test-server', userConfig);
mockedMcpClient.getServerConfig.mockReturnValue(userConfig);
await manager2.startExtension(extension);
lastCall = vi.mocked(McpClient).mock.calls[1];
mergedConfig = lastCall[1];
expect(mergedConfig.excludeTools).toContain('ext-tool');
expect(mergedConfig.excludeTools).toContain('user-tool');
expect(mergedConfig.includeTools).toContain('shared-inc');
expect(mergedConfig.includeTools).not.toContain('user-only-inc');
expect(mergedConfig.includeTools).not.toContain('ext-only-inc');
expect(mergedConfig.env!['EXT_VAR']).toBe('ext-val');
expect(mergedConfig.env!['USER_VAR']).toBe('user-val');
expect(mergedConfig.env!['OVERRIDE_VAR']).toBe('user-override');
expect(mergedConfig.extension).toBe(extension); // Extension ID preserved!
});
it('should result in empty includeTools if intersection is empty', async () => {
const manager = new McpClientManager('0.0.1', toolRegistry, mockConfig);
const userConfig = { includeTools: ['user-tool'] };
const extConfig = {
command: 'node',
args: ['ext.js'],
includeTools: ['ext-tool'],
};
await manager.maybeDiscoverMcpServer('test-server', userConfig);
mockedMcpClient.getServerConfig.mockReturnValue(userConfig);
await manager.maybeDiscoverMcpServer('test-server', extConfig);
const lastCall = vi.mocked(McpClient).mock.calls[1];
expect(lastCall[1].includeTools).toEqual([]); // Empty array = no tools allowed
});
it('should respect a single allowlist if only one is provided', async () => {
const manager = new McpClientManager('0.0.1', toolRegistry, mockConfig);
const userConfig = { includeTools: ['user-tool'] };
const extConfig = { command: 'node', args: ['ext.js'] };
await manager.maybeDiscoverMcpServer('test-server', userConfig);
mockedMcpClient.getServerConfig.mockReturnValue(userConfig);
await manager.maybeDiscoverMcpServer('test-server', extConfig);
const lastCall = vi.mocked(McpClient).mock.calls[1];
expect(lastCall[1].includeTools).toEqual(['user-tool']);
});
it('should allow partial overrides of connection properties', async () => {
const manager = new McpClientManager('0.0.1', toolRegistry, mockConfig);
const extConfig = { command: 'node', args: ['ext.js'], timeout: 1000 };
const userOverride = { args: ['overridden.js'] };
// Load extension first
await manager.maybeDiscoverMcpServer('test-server', extConfig);
mockedMcpClient.getServerConfig.mockReturnValue(extConfig);
// Apply partial user override
await manager.maybeDiscoverMcpServer('test-server', userOverride);
const lastCall = vi.mocked(McpClient).mock.calls[1];
const finalConfig = lastCall[1];
expect(finalConfig.command).toBe('node'); // Preserved from base
expect(finalConfig.args).toEqual(['overridden.js']); // Overridden
expect(finalConfig.timeout).toBe(1000); // Preserved from base
});
it('should remove servers from blockedMcpServers when stopExtension is called', async () => {
+66 -5
View File
@@ -257,14 +257,62 @@ export class McpClientManager {
}
}
/**
* Merges two MCP configurations. The second configuration (override)
* takes precedence for scalar properties, but array properties are
* merged securely (exclude = union, include = intersection) and
* environment objects are merged.
*/
private mergeMcpConfigs(
base: MCPServerConfig,
override: MCPServerConfig,
): MCPServerConfig {
// For allowlists (includeTools), use intersection to ensure the most
// restrictive policy wins. A tool must be allowed by BOTH parties.
let includeTools: string[] | undefined;
if (base.includeTools && override.includeTools) {
includeTools = base.includeTools.filter((t) =>
override.includeTools!.includes(t),
);
// If the intersection is empty, we must keep an empty array to indicate
// that NO tools are allowed (undefined would allow everything).
} else {
// If only one provides an allowlist, use that.
includeTools = override.includeTools ?? base.includeTools;
}
// For blocklists (excludeTools), use union so if ANY party blocks it,
// it stays blocked.
const excludeTools = [
...new Set([
...(base.excludeTools ?? []),
...(override.excludeTools ?? []),
]),
];
const env = { ...(base.env ?? {}), ...(override.env ?? {}) };
return {
...base,
...override,
includeTools,
excludeTools: excludeTools.length > 0 ? excludeTools : undefined,
env: Object.keys(env).length > 0 ? env : undefined,
extension: override.extension ?? base.extension,
};
}
async maybeDiscoverMcpServer(
name: string,
config: MCPServerConfig,
): Promise<void> {
const existing = this.clients.get(name);
const existingConfig = existing?.getServerConfig();
if (
existing &&
existing.getServerConfig().extension?.id !== config.extension?.id
existingConfig?.extension?.id &&
config.extension?.id &&
existingConfig.extension.id !== config.extension.id
) {
const extensionText = config.extension
? ` from extension "${config.extension.name}"`
@@ -275,15 +323,28 @@ export class McpClientManager {
return;
}
let finalConfig = config;
if (existing && existingConfig) {
// If we're merging an extension config into a user config,
// the user config should be the override.
if (config.extension && !existingConfig.extension) {
finalConfig = this.mergeMcpConfigs(config, existingConfig);
} else {
// Otherwise (User over Extension, or User over User),
// the incoming config is the override.
finalConfig = this.mergeMcpConfigs(existingConfig, config);
}
}
// Always track server config for UI display
this.allServerConfigs.set(name, config);
this.allServerConfigs.set(name, finalConfig);
// Check if blocked by admin settings (allowlist/excludelist)
if (this.isBlockedBySettings(name)) {
if (!this.blockedMcpServers.find((s) => s.name === name)) {
this.blockedMcpServers?.push({
name,
extensionName: config.extension?.name ?? '',
extensionName: finalConfig.extension?.name ?? '',
});
}
return;
@@ -298,7 +359,7 @@ export class McpClientManager {
if (!this.cliConfig.isTrustedFolder()) {
return;
}
if (config.extension && !config.extension.isActive) {
if (finalConfig.extension && !finalConfig.extension.isActive) {
return;
}
@@ -312,7 +373,7 @@ export class McpClientManager {
const client = new McpClient(
name,
config,
finalConfig,
this.toolRegistry,
this.cliConfig.getPromptRegistry(),
this.cliConfig.getResourceRegistry(),
@@ -165,6 +165,29 @@ describe('getEnvironmentContext', () => {
expect(getFolderStructure).not.toHaveBeenCalled();
});
it('should exclude environment memory when JIT context is enabled', async () => {
(mockConfig as Record<string, unknown>)['isJitContextEnabled'] = vi
.fn()
.mockReturnValue(true);
const parts = await getEnvironmentContext(mockConfig as Config);
const context = parts[0].text;
expect(context).not.toContain('Mock Environment Memory');
expect(mockConfig.getEnvironmentMemory).not.toHaveBeenCalled();
});
it('should include environment memory when JIT context is disabled', async () => {
(mockConfig as Record<string, unknown>)['isJitContextEnabled'] = vi
.fn()
.mockReturnValue(false);
const parts = await getEnvironmentContext(mockConfig as Config);
const context = parts[0].text;
expect(context).toContain('Mock Environment Memory');
});
it('should handle read_many_files returning no content', async () => {
const mockReadManyFilesTool = {
build: vi.fn().mockReturnValue({
@@ -57,7 +57,12 @@ export async function getEnvironmentContext(config: Config): Promise<Part[]> {
? await getDirectoryContextString(config)
: '';
const tempDir = config.storage.getProjectTempDir();
const environmentMemory = config.getEnvironmentMemory();
// When JIT context is enabled, project memory is already included in the
// system instruction via renderUserMemory(). Skip it here to avoid sending
// the same GEMINI.md content twice.
const environmentMemory = config.isJitContextEnabled?.()
? ''
: config.getEnvironmentMemory();
const context = `
<session_context>
@@ -13,6 +13,7 @@ import type {
import { getResponseText } from './partUtils.js';
import { supportsMultimodalFunctionResponse } from '../config/models.js';
import { debugLogger } from './debugLogger.js';
import type { Config } from '../config/config.js';
/**
* Formats tool output for a Gemini FunctionResponse.
@@ -48,6 +49,7 @@ export function convertToFunctionResponse(
callId: string,
llmContent: PartListUnion,
model: string,
config?: Config,
): Part[] {
if (typeof llmContent === 'string') {
return [createFunctionResponsePart(callId, toolName, llmContent)];
@@ -96,7 +98,10 @@ export function convertToFunctionResponse(
},
};
const isMultimodalFRSupported = supportsMultimodalFunctionResponse(model);
const isMultimodalFRSupported = supportsMultimodalFunctionResponse(
model,
config,
);
const siblingParts: Part[] = [...fileDataParts];
if (inlineDataParts.length > 0) {
File diff suppressed because one or more lines are too long