mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-05-13 05:12:55 -07:00
fix(core): prevent duplicate tool approval entries in auto-saved.toml (#19487)
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
This commit is contained in:
@@ -117,8 +117,8 @@ export class RemoteAgentInvocation extends BaseToolInvocation<
|
|||||||
type: 'info',
|
type: 'info',
|
||||||
title: `Call Remote Agent: ${this.definition.displayName ?? this.definition.name}`,
|
title: `Call Remote Agent: ${this.definition.displayName ?? this.definition.name}`,
|
||||||
prompt: `Calling remote agent: "${this.params.query}"`,
|
prompt: `Calling remote agent: "${this.params.query}"`,
|
||||||
onConfirm: async (outcome: ToolConfirmationOutcome) => {
|
onConfirm: async (_outcome: ToolConfirmationOutcome) => {
|
||||||
await this.publishPolicyUpdate(outcome);
|
// Policy updates are now handled centrally by the scheduler
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -136,17 +136,17 @@ describe('Tool Confirmation Policy Updates', () => {
|
|||||||
it.each([
|
it.each([
|
||||||
{
|
{
|
||||||
outcome: ToolConfirmationOutcome.ProceedAlways,
|
outcome: ToolConfirmationOutcome.ProceedAlways,
|
||||||
shouldPublish: false,
|
_shouldPublish: false,
|
||||||
expectedApprovalMode: ApprovalMode.AUTO_EDIT,
|
expectedApprovalMode: ApprovalMode.AUTO_EDIT,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
outcome: ToolConfirmationOutcome.ProceedAlwaysAndSave,
|
outcome: ToolConfirmationOutcome.ProceedAlwaysAndSave,
|
||||||
shouldPublish: true,
|
_shouldPublish: true,
|
||||||
persist: true,
|
_persist: true,
|
||||||
},
|
},
|
||||||
])(
|
])(
|
||||||
'should handle $outcome correctly',
|
'should handle $outcome correctly',
|
||||||
async ({ outcome, shouldPublish, persist, expectedApprovalMode }) => {
|
async ({ outcome, expectedApprovalMode }) => {
|
||||||
const tool = create(mockConfig, mockMessageBus);
|
const tool = create(mockConfig, mockMessageBus);
|
||||||
|
|
||||||
// For file-based tools, ensure the file exists if needed
|
// For file-based tools, ensure the file exists if needed
|
||||||
@@ -172,26 +172,19 @@ describe('Tool Confirmation Policy Updates', () => {
|
|||||||
if (confirmation) {
|
if (confirmation) {
|
||||||
await confirmation.onConfirm(outcome);
|
await confirmation.onConfirm(outcome);
|
||||||
|
|
||||||
if (shouldPublish) {
|
// Policy updates are no longer published by onConfirm; they are
|
||||||
expect(mockMessageBus.publish).toHaveBeenCalledWith(
|
// handled centrally by the schedulers.
|
||||||
expect.objectContaining({
|
const publishCalls = (mockMessageBus.publish as any).mock.calls;
|
||||||
type: MessageBusType.UPDATE_POLICY,
|
const hasUpdatePolicy = publishCalls.some(
|
||||||
persist,
|
(call: any) => call[0].type === MessageBusType.UPDATE_POLICY,
|
||||||
}),
|
);
|
||||||
);
|
expect(hasUpdatePolicy).toBe(false);
|
||||||
} else {
|
|
||||||
// Should not publish UPDATE_POLICY message for ProceedAlways
|
|
||||||
const publishCalls = (mockMessageBus.publish as any).mock.calls;
|
|
||||||
const hasUpdatePolicy = publishCalls.some(
|
|
||||||
(call: any) => call[0].type === MessageBusType.UPDATE_POLICY,
|
|
||||||
);
|
|
||||||
expect(hasUpdatePolicy).toBe(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (expectedApprovalMode !== undefined) {
|
if (expectedApprovalMode !== undefined) {
|
||||||
expect(mockConfig.setApprovalMode).toHaveBeenCalledWith(
|
// expectedApprovalMode in this test (AUTO_EDIT) is now handled
|
||||||
expectedApprovalMode,
|
// by updatePolicy in the scheduler, so it should not be called
|
||||||
);
|
// here either.
|
||||||
|
expect(mockConfig.setApprovalMode).not.toHaveBeenCalled();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import {
|
|||||||
BaseToolInvocation,
|
BaseToolInvocation,
|
||||||
Kind,
|
Kind,
|
||||||
type ToolCallConfirmationDetails,
|
type ToolCallConfirmationDetails,
|
||||||
ToolConfirmationOutcome,
|
type ToolConfirmationOutcome,
|
||||||
type ToolEditConfirmationDetails,
|
type ToolEditConfirmationDetails,
|
||||||
type ToolInvocation,
|
type ToolInvocation,
|
||||||
type ToolLocation,
|
type ToolLocation,
|
||||||
@@ -725,14 +725,9 @@ class EditToolInvocation
|
|||||||
fileDiff,
|
fileDiff,
|
||||||
originalContent: editData.currentContent,
|
originalContent: editData.currentContent,
|
||||||
newContent: editData.newContent,
|
newContent: editData.newContent,
|
||||||
onConfirm: async (outcome: ToolConfirmationOutcome) => {
|
onConfirm: async (_outcome: ToolConfirmationOutcome) => {
|
||||||
if (outcome === ToolConfirmationOutcome.ProceedAlways) {
|
// Mode transitions (e.g. AUTO_EDIT) and policy updates are now
|
||||||
// No need to publish a policy update as the default policy for
|
// handled centrally by the scheduler.
|
||||||
// AUTO_EDIT already reflects always approving edit.
|
|
||||||
this.config.setApprovalMode(ApprovalMode.AUTO_EDIT);
|
|
||||||
} else {
|
|
||||||
await this.publishPolicyUpdate(outcome);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ideConfirmation) {
|
if (ideConfirmation) {
|
||||||
const result = await ideConfirmation;
|
const result = await ideConfirmation;
|
||||||
|
|||||||
@@ -105,7 +105,7 @@ export class EnterPlanModeInvocation extends BaseToolInvocation<
|
|||||||
'This will restrict the agent to read-only tools to allow for safe planning.',
|
'This will restrict the agent to read-only tools to allow for safe planning.',
|
||||||
onConfirm: async (outcome: ToolConfirmationOutcome) => {
|
onConfirm: async (outcome: ToolConfirmationOutcome) => {
|
||||||
this.confirmationOutcome = outcome;
|
this.confirmationOutcome = outcome;
|
||||||
await this.publishPolicyUpdate(outcome);
|
// Policy updates are now handled centrally by the scheduler
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -130,7 +130,7 @@ export class DiscoveredMCPToolInvocation extends BaseToolInvocation<
|
|||||||
DiscoveredMCPToolInvocation.allowlist.add(toolAllowListKey);
|
DiscoveredMCPToolInvocation.allowlist.add(toolAllowListKey);
|
||||||
} else if (outcome === ToolConfirmationOutcome.ProceedAlwaysAndSave) {
|
} else if (outcome === ToolConfirmationOutcome.ProceedAlwaysAndSave) {
|
||||||
DiscoveredMCPToolInvocation.allowlist.add(toolAllowListKey);
|
DiscoveredMCPToolInvocation.allowlist.add(toolAllowListKey);
|
||||||
await this.publishPolicyUpdate(outcome);
|
// Persistent policy updates are now handled centrally by the scheduler
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -204,7 +204,7 @@ class MemoryToolInvocation extends BaseToolInvocation<
|
|||||||
if (outcome === ToolConfirmationOutcome.ProceedAlways) {
|
if (outcome === ToolConfirmationOutcome.ProceedAlways) {
|
||||||
MemoryToolInvocation.allowlist.add(allowlistKey);
|
MemoryToolInvocation.allowlist.add(allowlistKey);
|
||||||
}
|
}
|
||||||
await this.publishPolicyUpdate(outcome);
|
// Policy updates are now handled centrally by the scheduler
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
return confirmationDetails;
|
return confirmationDetails;
|
||||||
|
|||||||
@@ -140,8 +140,8 @@ export class ShellToolInvocation extends BaseToolInvocation<
|
|||||||
command: this.params.command,
|
command: this.params.command,
|
||||||
rootCommand: rootCommandDisplay,
|
rootCommand: rootCommandDisplay,
|
||||||
rootCommands,
|
rootCommands,
|
||||||
onConfirm: async (outcome: ToolConfirmationOutcome) => {
|
onConfirm: async (_outcome: ToolConfirmationOutcome) => {
|
||||||
await this.publishPolicyUpdate(outcome);
|
// Policy updates are now handled centrally by the scheduler
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
return confirmationDetails;
|
return confirmationDetails;
|
||||||
|
|||||||
@@ -173,8 +173,8 @@ export abstract class BaseToolInvocation<
|
|||||||
type: 'info',
|
type: 'info',
|
||||||
title: `Confirm: ${this._toolDisplayName || this._toolName}`,
|
title: `Confirm: ${this._toolDisplayName || this._toolName}`,
|
||||||
prompt: this.getDescription(),
|
prompt: this.getDescription(),
|
||||||
onConfirm: async (outcome: ToolConfirmationOutcome) => {
|
onConfirm: async (_outcome: ToolConfirmationOutcome) => {
|
||||||
await this.publishPolicyUpdate(outcome);
|
// Policy updates are now handled centrally by the scheduler
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
return confirmationDetails;
|
return confirmationDetails;
|
||||||
|
|||||||
@@ -390,7 +390,7 @@ describe('WebFetchTool', () => {
|
|||||||
expect(confirmationDetails).toBe(false);
|
expect(confirmationDetails).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should call setApprovalMode when onConfirm is called with ProceedAlways', async () => {
|
it('should NOT call setApprovalMode when onConfirm is called with ProceedAlways (now handled by scheduler)', async () => {
|
||||||
const tool = new WebFetchTool(mockConfig, bus);
|
const tool = new WebFetchTool(mockConfig, bus);
|
||||||
const params = { prompt: 'fetch https://example.com' };
|
const params = { prompt: 'fetch https://example.com' };
|
||||||
const invocation = tool.build(params);
|
const invocation = tool.build(params);
|
||||||
@@ -408,9 +408,8 @@ describe('WebFetchTool', () => {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
expect(mockConfig.setApprovalMode).toHaveBeenCalledWith(
|
// Schedulers are now responsible for mode transitions via updatePolicy
|
||||||
ApprovalMode.AUTO_EDIT,
|
expect(mockConfig.setApprovalMode).not.toHaveBeenCalled();
|
||||||
);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import {
|
|||||||
BaseDeclarativeTool,
|
BaseDeclarativeTool,
|
||||||
BaseToolInvocation,
|
BaseToolInvocation,
|
||||||
Kind,
|
Kind,
|
||||||
ToolConfirmationOutcome,
|
type ToolConfirmationOutcome,
|
||||||
} from './tools.js';
|
} from './tools.js';
|
||||||
import type { MessageBus } from '../confirmation-bus/message-bus.js';
|
import type { MessageBus } from '../confirmation-bus/message-bus.js';
|
||||||
import { ToolErrorType } from './tool-error.js';
|
import { ToolErrorType } from './tool-error.js';
|
||||||
@@ -246,14 +246,9 @@ ${textContent}
|
|||||||
title: `Confirm Web Fetch`,
|
title: `Confirm Web Fetch`,
|
||||||
prompt: this.params.prompt,
|
prompt: this.params.prompt,
|
||||||
urls,
|
urls,
|
||||||
onConfirm: async (outcome: ToolConfirmationOutcome) => {
|
onConfirm: async (_outcome: ToolConfirmationOutcome) => {
|
||||||
if (outcome === ToolConfirmationOutcome.ProceedAlways) {
|
// Mode transitions (e.g. AUTO_EDIT) and policy updates are now
|
||||||
// No need to publish a policy update as the default policy for
|
// handled centrally by the scheduler.
|
||||||
// AUTO_EDIT already reflects always approving web-fetch.
|
|
||||||
this.config.setApprovalMode(ApprovalMode.AUTO_EDIT);
|
|
||||||
} else {
|
|
||||||
await this.publishPolicyUpdate(outcome);
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
return confirmationDetails;
|
return confirmationDetails;
|
||||||
|
|||||||
@@ -25,7 +25,7 @@ import {
|
|||||||
BaseDeclarativeTool,
|
BaseDeclarativeTool,
|
||||||
BaseToolInvocation,
|
BaseToolInvocation,
|
||||||
Kind,
|
Kind,
|
||||||
ToolConfirmationOutcome,
|
type ToolConfirmationOutcome,
|
||||||
} from './tools.js';
|
} from './tools.js';
|
||||||
import { ToolErrorType } from './tool-error.js';
|
import { ToolErrorType } from './tool-error.js';
|
||||||
import { makeRelative, shortenPath } from '../utils/paths.js';
|
import { makeRelative, shortenPath } from '../utils/paths.js';
|
||||||
@@ -228,14 +228,9 @@ class WriteFileToolInvocation extends BaseToolInvocation<
|
|||||||
fileDiff,
|
fileDiff,
|
||||||
originalContent,
|
originalContent,
|
||||||
newContent: correctedContent,
|
newContent: correctedContent,
|
||||||
onConfirm: async (outcome: ToolConfirmationOutcome) => {
|
onConfirm: async (_outcome: ToolConfirmationOutcome) => {
|
||||||
if (outcome === ToolConfirmationOutcome.ProceedAlways) {
|
// Mode transitions (e.g. AUTO_EDIT) and policy updates are now
|
||||||
// No need to publish a policy update as the default policy for
|
// handled centrally by the scheduler.
|
||||||
// AUTO_EDIT already reflects always approving write-file.
|
|
||||||
this.config.setApprovalMode(ApprovalMode.AUTO_EDIT);
|
|
||||||
} else {
|
|
||||||
await this.publishPolicyUpdate(outcome);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ideConfirmation) {
|
if (ideConfirmation) {
|
||||||
const result = await ideConfirmation;
|
const result = await ideConfirmation;
|
||||||
|
|||||||
Reference in New Issue
Block a user