mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-05-12 12:54:07 -07:00
fix(policy): address review feedback for robust path correction, priority consistency, and narrowing enforcement
This commit is contained in:
@@ -34,7 +34,7 @@ import { type MessageBus } from '../confirmation-bus/message-bus.js';
|
|||||||
import { coreEvents } from '../utils/events.js';
|
import { coreEvents } from '../utils/events.js';
|
||||||
import { debugLogger } from '../utils/debugLogger.js';
|
import { debugLogger } from '../utils/debugLogger.js';
|
||||||
import { SHELL_TOOL_NAMES } from '../utils/shell-utils.js';
|
import { SHELL_TOOL_NAMES } from '../utils/shell-utils.js';
|
||||||
import { SHELL_TOOL_NAME } from '../tools/tool-names.js';
|
import { SHELL_TOOL_NAME, SENSITIVE_TOOLS } from '../tools/tool-names.js';
|
||||||
import { isNodeError } from '../utils/errors.js';
|
import { isNodeError } from '../utils/errors.js';
|
||||||
import { MCP_TOOL_PREFIX } from '../tools/mcp-tool.js';
|
import { MCP_TOOL_PREFIX } from '../tools/mcp-tool.js';
|
||||||
|
|
||||||
@@ -491,6 +491,19 @@ export function createPolicyUpdater(
|
|||||||
if (message.commandPrefix) {
|
if (message.commandPrefix) {
|
||||||
// Convert commandPrefix(es) to argsPatterns for in-memory rules
|
// Convert commandPrefix(es) to argsPatterns for in-memory rules
|
||||||
const patterns = buildArgsPatterns(undefined, message.commandPrefix);
|
const patterns = buildArgsPatterns(undefined, message.commandPrefix);
|
||||||
|
const tier =
|
||||||
|
message.persistScope === 'user'
|
||||||
|
? USER_POLICY_TIER
|
||||||
|
: WORKSPACE_POLICY_TIER;
|
||||||
|
const priority = tier + (ALWAYS_ALLOW_PRIORITY % 1);
|
||||||
|
|
||||||
|
if (SENSITIVE_TOOLS.has(toolName) && !message.commandPrefix) {
|
||||||
|
debugLogger.warn(
|
||||||
|
`Attempted to update policy for sensitive tool '${toolName}' without a commandPrefix. Skipping.`,
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
for (const pattern of patterns) {
|
for (const pattern of patterns) {
|
||||||
if (pattern) {
|
if (pattern) {
|
||||||
// Note: patterns from buildArgsPatterns are derived from escapeRegex,
|
// Note: patterns from buildArgsPatterns are derived from escapeRegex,
|
||||||
@@ -498,7 +511,7 @@ export function createPolicyUpdater(
|
|||||||
policyEngine.addRule({
|
policyEngine.addRule({
|
||||||
toolName,
|
toolName,
|
||||||
decision: PolicyDecision.ALLOW,
|
decision: PolicyDecision.ALLOW,
|
||||||
priority: ALWAYS_ALLOW_PRIORITY,
|
priority,
|
||||||
argsPattern: new RegExp(pattern),
|
argsPattern: new RegExp(pattern),
|
||||||
source: 'Dynamic (Confirmed)',
|
source: 'Dynamic (Confirmed)',
|
||||||
});
|
});
|
||||||
@@ -517,10 +530,23 @@ export function createPolicyUpdater(
|
|||||||
? new RegExp(message.argsPattern)
|
? new RegExp(message.argsPattern)
|
||||||
: undefined;
|
: undefined;
|
||||||
|
|
||||||
|
const tier =
|
||||||
|
message.persistScope === 'user'
|
||||||
|
? USER_POLICY_TIER
|
||||||
|
: WORKSPACE_POLICY_TIER;
|
||||||
|
const priority = tier + (ALWAYS_ALLOW_PRIORITY % 1);
|
||||||
|
|
||||||
|
if (SENSITIVE_TOOLS.has(toolName) && !message.argsPattern) {
|
||||||
|
debugLogger.warn(
|
||||||
|
`Attempted to update policy for sensitive tool '${toolName}' without an argsPattern. Skipping.`,
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
policyEngine.addRule({
|
policyEngine.addRule({
|
||||||
toolName,
|
toolName,
|
||||||
decision: PolicyDecision.ALLOW,
|
decision: PolicyDecision.ALLOW,
|
||||||
priority: ALWAYS_ALLOW_PRIORITY,
|
priority,
|
||||||
argsPattern,
|
argsPattern,
|
||||||
source: 'Dynamic (Confirmed)',
|
source: 'Dynamic (Confirmed)',
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
||||||
import * as path from 'node:path';
|
import * as path from 'node:path';
|
||||||
import { createPolicyUpdater } from './config.js';
|
import { createPolicyUpdater } from './config.js';
|
||||||
import { ALWAYS_ALLOW_PRIORITY } from './utils.js';
|
import { getAlwaysAllowPriorityFraction } from './utils.js';
|
||||||
import { PolicyEngine } from './policy-engine.js';
|
import { PolicyEngine } from './policy-engine.js';
|
||||||
import { MessageBus } from '../confirmation-bus/message-bus.js';
|
import { MessageBus } from '../confirmation-bus/message-bus.js';
|
||||||
import { MessageBusType } from '../confirmation-bus/types.js';
|
import { MessageBusType } from '../confirmation-bus/types.js';
|
||||||
@@ -62,9 +62,7 @@ describe('createPolicyUpdater', () => {
|
|||||||
const content = memfs.readFileSync(policyFile, 'utf-8') as string;
|
const content = memfs.readFileSync(policyFile, 'utf-8') as string;
|
||||||
expect(content).toContain('toolName = "test_tool"');
|
expect(content).toContain('toolName = "test_tool"');
|
||||||
expect(content).toContain('decision = "allow"');
|
expect(content).toContain('decision = "allow"');
|
||||||
const expectedPriority = Math.round(
|
const expectedPriority = getAlwaysAllowPriorityFraction();
|
||||||
(ALWAYS_ALLOW_PRIORITY - Math.floor(ALWAYS_ALLOW_PRIORITY)) * 1000,
|
|
||||||
);
|
|
||||||
expect(content).toContain(`priority = ${expectedPriority}`);
|
expect(content).toContain(`priority = ${expectedPriority}`);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -10,17 +10,6 @@
|
|||||||
*/
|
*/
|
||||||
export const ALWAYS_ALLOW_PRIORITY = 3.95;
|
export const ALWAYS_ALLOW_PRIORITY = 3.95;
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculates a unique priority within the ALWAYS_ALLOW_PRIORITY tier.
|
|
||||||
* It uses the fractional part as a base and adds a small offset.
|
|
||||||
*/
|
|
||||||
export function getAlwaysAllowPriority(offset: number): number {
|
|
||||||
const base = Math.floor(ALWAYS_ALLOW_PRIORITY);
|
|
||||||
const fraction = ALWAYS_ALLOW_PRIORITY - base;
|
|
||||||
// Use a precision of 3 decimal places for the offset
|
|
||||||
return base + fraction + offset / 1000;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the fractional priority of ALWAYS_ALLOW_PRIORITY scaled to 1000.
|
* Returns the fractional priority of ALWAYS_ALLOW_PRIORITY scaled to 1000.
|
||||||
*/
|
*/
|
||||||
@@ -120,3 +109,15 @@ export function buildFilePathArgsPattern(filePath: string): string {
|
|||||||
const jsonPath = JSON.stringify(filePath).slice(1, -1);
|
const jsonPath = JSON.stringify(filePath).slice(1, -1);
|
||||||
return `"file_path":"${escapeRegex(jsonPath)}"`;
|
return `"file_path":"${escapeRegex(jsonPath)}"`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds a regex pattern to match a specific "pattern" in tool arguments.
|
||||||
|
* This is used to narrow tool approvals for search tools like glob/grep to specific patterns.
|
||||||
|
*
|
||||||
|
* @param pattern The pattern to match.
|
||||||
|
* @returns A regex string that matches "pattern":"<pattern>" in a JSON string.
|
||||||
|
*/
|
||||||
|
export function buildPatternArgsPattern(pattern: string): string {
|
||||||
|
const jsonPattern = JSON.stringify(pattern).slice(1, -1);
|
||||||
|
return `"pattern":"${escapeRegex(jsonPattern)}"`;
|
||||||
|
}
|
||||||
|
|||||||
@@ -553,6 +553,7 @@ describe('policy.ts', () => {
|
|||||||
const mockConfig = {
|
const mockConfig = {
|
||||||
isTrustedFolder: vi.fn().mockReturnValue(false),
|
isTrustedFolder: vi.fn().mockReturnValue(false),
|
||||||
getWorkspacePoliciesDir: vi.fn().mockReturnValue(undefined),
|
getWorkspacePoliciesDir: vi.fn().mockReturnValue(undefined),
|
||||||
|
getTargetDir: vi.fn().mockReturnValue('/mock/dir'),
|
||||||
setApprovalMode: vi.fn(),
|
setApprovalMode: vi.fn(),
|
||||||
} as unknown as Mocked<Config>;
|
} as unknown as Mocked<Config>;
|
||||||
const mockMessageBus = {
|
const mockMessageBus = {
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import {
|
|||||||
type PolicyUpdateOptions,
|
type PolicyUpdateOptions,
|
||||||
} from '../tools/tools.js';
|
} from '../tools/tools.js';
|
||||||
import { buildFilePathArgsPattern } from '../policy/utils.js';
|
import { buildFilePathArgsPattern } from '../policy/utils.js';
|
||||||
|
import { makeRelative } from '../utils/paths.js';
|
||||||
import { DiscoveredMCPTool } from '../tools/mcp-tool.js';
|
import { DiscoveredMCPTool } from '../tools/mcp-tool.js';
|
||||||
import { EDIT_TOOL_NAMES } from '../tools/tool-names.js';
|
import { EDIT_TOOL_NAMES } from '../tools/tool-names.js';
|
||||||
import type { ValidatingToolCall } from './types.js';
|
import type { ValidatingToolCall } from './types.js';
|
||||||
@@ -114,7 +115,7 @@ export async function updatePolicy(
|
|||||||
// If folder is trusted and workspace policies are enabled, we prefer workspace scope.
|
// If folder is trusted and workspace policies are enabled, we prefer workspace scope.
|
||||||
if (
|
if (
|
||||||
deps.config.isTrustedFolder() &&
|
deps.config.isTrustedFolder() &&
|
||||||
deps.config.getWorkspacePoliciesDir()
|
deps.config.getWorkspacePoliciesDir() !== undefined
|
||||||
) {
|
) {
|
||||||
persistScope = 'workspace';
|
persistScope = 'workspace';
|
||||||
} else {
|
} else {
|
||||||
@@ -142,6 +143,7 @@ export async function updatePolicy(
|
|||||||
deps.messageBus,
|
deps.messageBus,
|
||||||
persistScope,
|
persistScope,
|
||||||
deps.toolInvocation,
|
deps.toolInvocation,
|
||||||
|
deps.config,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -173,6 +175,7 @@ async function handleStandardPolicyUpdate(
|
|||||||
messageBus: MessageBus,
|
messageBus: MessageBus,
|
||||||
persistScope?: 'workspace' | 'user',
|
persistScope?: 'workspace' | 'user',
|
||||||
toolInvocation?: AnyToolInvocation,
|
toolInvocation?: AnyToolInvocation,
|
||||||
|
config?: Config,
|
||||||
): Promise<void> {
|
): Promise<void> {
|
||||||
if (
|
if (
|
||||||
outcome === ToolConfirmationOutcome.ProceedAlways ||
|
outcome === ToolConfirmationOutcome.ProceedAlways ||
|
||||||
@@ -184,9 +187,10 @@ async function handleStandardPolicyUpdate(
|
|||||||
if (!options.commandPrefix && confirmationDetails?.type === 'exec') {
|
if (!options.commandPrefix && confirmationDetails?.type === 'exec') {
|
||||||
options.commandPrefix = confirmationDetails.rootCommands;
|
options.commandPrefix = confirmationDetails.rootCommands;
|
||||||
} else if (!options.argsPattern && confirmationDetails?.type === 'edit') {
|
} else if (!options.argsPattern && confirmationDetails?.type === 'edit') {
|
||||||
options.argsPattern = buildFilePathArgsPattern(
|
const filePath = config
|
||||||
confirmationDetails.filePath,
|
? makeRelative(confirmationDetails.filePath, config.getTargetDir())
|
||||||
);
|
: confirmationDetails.filePath;
|
||||||
|
options.argsPattern = buildFilePathArgsPattern(filePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
await messageBus.publish({
|
await messageBus.publish({
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import type { MessageBus } from '../confirmation-bus/message-bus.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';
|
||||||
import { isNodeError } from '../utils/errors.js';
|
import { isNodeError } from '../utils/errors.js';
|
||||||
|
import { correctPath } from '../utils/pathCorrector.js';
|
||||||
import type { Config } from '../config/config.js';
|
import type { Config } from '../config/config.js';
|
||||||
import { ApprovalMode } from '../policy/types.js';
|
import { ApprovalMode } from '../policy/types.js';
|
||||||
import { CoreToolCallStatus } from '../scheduler/types.js';
|
import { CoreToolCallStatus } from '../scheduler/types.js';
|
||||||
@@ -453,10 +454,19 @@ class EditToolInvocation
|
|||||||
displayName?: string,
|
displayName?: string,
|
||||||
) {
|
) {
|
||||||
super(params, messageBus, toolName, displayName);
|
super(params, messageBus, toolName, displayName);
|
||||||
this.resolvedPath = path.resolve(
|
if (!path.isAbsolute(this.params.file_path)) {
|
||||||
this.config.getTargetDir(),
|
const result = correctPath(this.params.file_path, this.config);
|
||||||
this.params.file_path,
|
if (result.success) {
|
||||||
);
|
this.resolvedPath = result.correctedPath;
|
||||||
|
} else {
|
||||||
|
this.resolvedPath = path.resolve(
|
||||||
|
this.config.getTargetDir(),
|
||||||
|
this.params.file_path,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.resolvedPath = this.params.file_path;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override toolLocations(): ToolLocation[] {
|
override toolLocations(): ToolLocation[] {
|
||||||
@@ -996,10 +1006,20 @@ export class EditTool
|
|||||||
return "The 'file_path' parameter must be non-empty.";
|
return "The 'file_path' parameter must be non-empty.";
|
||||||
}
|
}
|
||||||
|
|
||||||
const resolvedPath = path.resolve(
|
let resolvedPath: string;
|
||||||
this.config.getTargetDir(),
|
if (!path.isAbsolute(params.file_path)) {
|
||||||
params.file_path,
|
const result = correctPath(params.file_path, this.config);
|
||||||
);
|
if (result.success) {
|
||||||
|
resolvedPath = result.correctedPath;
|
||||||
|
} else {
|
||||||
|
resolvedPath = path.resolve(
|
||||||
|
this.config.getTargetDir(),
|
||||||
|
params.file_path,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
resolvedPath = params.file_path;
|
||||||
|
}
|
||||||
|
|
||||||
const newPlaceholders = detectOmissionPlaceholders(params.new_string);
|
const newPlaceholders = detectOmissionPlaceholders(params.new_string);
|
||||||
if (newPlaceholders.length > 0) {
|
if (newPlaceholders.length > 0) {
|
||||||
|
|||||||
@@ -14,12 +14,15 @@ import {
|
|||||||
Kind,
|
Kind,
|
||||||
type ToolInvocation,
|
type ToolInvocation,
|
||||||
type ToolResult,
|
type ToolResult,
|
||||||
|
type PolicyUpdateOptions,
|
||||||
|
type ToolConfirmationOutcome,
|
||||||
} from './tools.js';
|
} from './tools.js';
|
||||||
import { shortenPath, makeRelative } from '../utils/paths.js';
|
import { shortenPath, makeRelative } from '../utils/paths.js';
|
||||||
import { type Config } from '../config/config.js';
|
import { type Config } from '../config/config.js';
|
||||||
import { DEFAULT_FILE_FILTERING_OPTIONS } from '../config/constants.js';
|
import { DEFAULT_FILE_FILTERING_OPTIONS } from '../config/constants.js';
|
||||||
import { ToolErrorType } from './tool-error.js';
|
import { ToolErrorType } from './tool-error.js';
|
||||||
import { GLOB_TOOL_NAME, GLOB_DISPLAY_NAME } from './tool-names.js';
|
import { GLOB_TOOL_NAME, GLOB_DISPLAY_NAME } from './tool-names.js';
|
||||||
|
import { buildPatternArgsPattern } from '../policy/utils.js';
|
||||||
import { getErrorMessage } from '../utils/errors.js';
|
import { getErrorMessage } from '../utils/errors.js';
|
||||||
import { debugLogger } from '../utils/debugLogger.js';
|
import { debugLogger } from '../utils/debugLogger.js';
|
||||||
import { GLOB_DEFINITION } from './definitions/coreTools.js';
|
import { GLOB_DEFINITION } from './definitions/coreTools.js';
|
||||||
@@ -118,6 +121,14 @@ class GlobToolInvocation extends BaseToolInvocation<
|
|||||||
return description;
|
return description;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override getPolicyUpdateOptions(
|
||||||
|
_outcome: ToolConfirmationOutcome,
|
||||||
|
): PolicyUpdateOptions | undefined {
|
||||||
|
return {
|
||||||
|
argsPattern: buildPatternArgsPattern(this.params.pattern),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
async execute(signal: AbortSignal): Promise<ToolResult> {
|
async execute(signal: AbortSignal): Promise<ToolResult> {
|
||||||
try {
|
try {
|
||||||
const workspaceContext = this.config.getWorkspaceContext();
|
const workspaceContext = this.config.getWorkspaceContext();
|
||||||
|
|||||||
@@ -21,6 +21,8 @@ import {
|
|||||||
Kind,
|
Kind,
|
||||||
type ToolInvocation,
|
type ToolInvocation,
|
||||||
type ToolResult,
|
type ToolResult,
|
||||||
|
type PolicyUpdateOptions,
|
||||||
|
type ToolConfirmationOutcome,
|
||||||
} from './tools.js';
|
} from './tools.js';
|
||||||
import { makeRelative, shortenPath } from '../utils/paths.js';
|
import { makeRelative, shortenPath } from '../utils/paths.js';
|
||||||
import { getErrorMessage, isNodeError } from '../utils/errors.js';
|
import { getErrorMessage, isNodeError } from '../utils/errors.js';
|
||||||
@@ -29,6 +31,7 @@ import type { Config } from '../config/config.js';
|
|||||||
import type { FileExclusions } from '../utils/ignorePatterns.js';
|
import type { FileExclusions } from '../utils/ignorePatterns.js';
|
||||||
import { ToolErrorType } from './tool-error.js';
|
import { ToolErrorType } from './tool-error.js';
|
||||||
import { GREP_TOOL_NAME } from './tool-names.js';
|
import { GREP_TOOL_NAME } from './tool-names.js';
|
||||||
|
import { buildPatternArgsPattern } from '../policy/utils.js';
|
||||||
import { debugLogger } from '../utils/debugLogger.js';
|
import { debugLogger } from '../utils/debugLogger.js';
|
||||||
import { GREP_DEFINITION } from './definitions/coreTools.js';
|
import { GREP_DEFINITION } from './definitions/coreTools.js';
|
||||||
import { resolveToolDeclaration } from './definitions/resolver.js';
|
import { resolveToolDeclaration } from './definitions/resolver.js';
|
||||||
@@ -285,6 +288,14 @@ class GrepToolInvocation extends BaseToolInvocation<
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override getPolicyUpdateOptions(
|
||||||
|
_outcome: ToolConfirmationOutcome,
|
||||||
|
): PolicyUpdateOptions | undefined {
|
||||||
|
return {
|
||||||
|
argsPattern: buildPatternArgsPattern(this.params.pattern),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if a command is available in the system's PATH.
|
* Checks if a command is available in the system's PATH.
|
||||||
* @param {string} command The command name (e.g., 'git', 'grep').
|
* @param {string} command The command name (e.g., 'git', 'grep').
|
||||||
|
|||||||
@@ -13,12 +13,15 @@ import {
|
|||||||
Kind,
|
Kind,
|
||||||
type ToolInvocation,
|
type ToolInvocation,
|
||||||
type ToolResult,
|
type ToolResult,
|
||||||
|
type PolicyUpdateOptions,
|
||||||
|
type ToolConfirmationOutcome,
|
||||||
} from './tools.js';
|
} from './tools.js';
|
||||||
import { makeRelative, shortenPath } from '../utils/paths.js';
|
import { makeRelative, shortenPath } from '../utils/paths.js';
|
||||||
import type { Config } from '../config/config.js';
|
import type { Config } from '../config/config.js';
|
||||||
import { DEFAULT_FILE_FILTERING_OPTIONS } from '../config/constants.js';
|
import { DEFAULT_FILE_FILTERING_OPTIONS } from '../config/constants.js';
|
||||||
import { ToolErrorType } from './tool-error.js';
|
import { ToolErrorType } from './tool-error.js';
|
||||||
import { LS_TOOL_NAME } from './tool-names.js';
|
import { LS_TOOL_NAME } from './tool-names.js';
|
||||||
|
import { buildFilePathArgsPattern } from '../policy/utils.js';
|
||||||
import { debugLogger } from '../utils/debugLogger.js';
|
import { debugLogger } from '../utils/debugLogger.js';
|
||||||
import { LS_DEFINITION } from './definitions/coreTools.js';
|
import { LS_DEFINITION } from './definitions/coreTools.js';
|
||||||
import { resolveToolDeclaration } from './definitions/resolver.js';
|
import { resolveToolDeclaration } from './definitions/resolver.js';
|
||||||
@@ -123,6 +126,14 @@ class LSToolInvocation extends BaseToolInvocation<LSToolParams, ToolResult> {
|
|||||||
return shortenPath(relativePath);
|
return shortenPath(relativePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override getPolicyUpdateOptions(
|
||||||
|
_outcome: ToolConfirmationOutcome,
|
||||||
|
): PolicyUpdateOptions | undefined {
|
||||||
|
return {
|
||||||
|
argsPattern: buildFilePathArgsPattern(this.params.dir_path),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// Helper for consistent error formatting
|
// Helper for consistent error formatting
|
||||||
private errorResult(
|
private errorResult(
|
||||||
llmContent: string,
|
llmContent: string,
|
||||||
|
|||||||
@@ -14,8 +14,11 @@ import {
|
|||||||
type ToolInvocation,
|
type ToolInvocation,
|
||||||
type ToolLocation,
|
type ToolLocation,
|
||||||
type ToolResult,
|
type ToolResult,
|
||||||
|
type PolicyUpdateOptions,
|
||||||
|
type ToolConfirmationOutcome,
|
||||||
} from './tools.js';
|
} from './tools.js';
|
||||||
import { ToolErrorType } from './tool-error.js';
|
import { ToolErrorType } from './tool-error.js';
|
||||||
|
import { buildFilePathArgsPattern } from '../policy/utils.js';
|
||||||
|
|
||||||
import type { PartUnion } from '@google/genai';
|
import type { PartUnion } from '@google/genai';
|
||||||
import {
|
import {
|
||||||
@@ -88,6 +91,14 @@ class ReadFileToolInvocation extends BaseToolInvocation<
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override getPolicyUpdateOptions(
|
||||||
|
_outcome: ToolConfirmationOutcome,
|
||||||
|
): PolicyUpdateOptions | undefined {
|
||||||
|
return {
|
||||||
|
argsPattern: buildFilePathArgsPattern(this.params.file_path),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
async execute(): Promise<ToolResult> {
|
async execute(): Promise<ToolResult> {
|
||||||
const validationError = this.config.validatePathAccess(
|
const validationError = this.config.validatePathAccess(
|
||||||
this.resolvedPath,
|
this.resolvedPath,
|
||||||
|
|||||||
@@ -11,11 +11,14 @@ import {
|
|||||||
Kind,
|
Kind,
|
||||||
type ToolInvocation,
|
type ToolInvocation,
|
||||||
type ToolResult,
|
type ToolResult,
|
||||||
|
type PolicyUpdateOptions,
|
||||||
|
type ToolConfirmationOutcome,
|
||||||
} from './tools.js';
|
} from './tools.js';
|
||||||
import { getErrorMessage } from '../utils/errors.js';
|
import { getErrorMessage } from '../utils/errors.js';
|
||||||
import * as fsPromises from 'node:fs/promises';
|
import * as fsPromises from 'node:fs/promises';
|
||||||
import * as path from 'node:path';
|
import * as path from 'node:path';
|
||||||
import { glob, escape } from 'glob';
|
import { glob, escape } from 'glob';
|
||||||
|
import { buildPatternArgsPattern } from '../policy/utils.js';
|
||||||
import {
|
import {
|
||||||
detectFileType,
|
detectFileType,
|
||||||
processSingleFileContent,
|
processSingleFileContent,
|
||||||
@@ -155,6 +158,16 @@ ${finalExclusionPatternsForDescription
|
|||||||
)}".`;
|
)}".`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override getPolicyUpdateOptions(
|
||||||
|
_outcome: ToolConfirmationOutcome,
|
||||||
|
): PolicyUpdateOptions | undefined {
|
||||||
|
// We join the include patterns to match the JSON stringified arguments.
|
||||||
|
// buildPatternArgsPattern handles JSON stringification.
|
||||||
|
return {
|
||||||
|
argsPattern: buildPatternArgsPattern(JSON.stringify(this.params.include)),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
async execute(signal: AbortSignal): Promise<ToolResult> {
|
async execute(signal: AbortSignal): Promise<ToolResult> {
|
||||||
const { include, exclude = [], useDefaultExcludes = true } = this.params;
|
const { include, exclude = [], useDefaultExcludes = true } = this.params;
|
||||||
|
|
||||||
|
|||||||
@@ -154,12 +154,22 @@ export const LS_TOOL_NAME_LEGACY = 'list_directory'; // Just to be safe if anyth
|
|||||||
|
|
||||||
export const EDIT_TOOL_NAMES = new Set([EDIT_TOOL_NAME, WRITE_FILE_TOOL_NAME]);
|
export const EDIT_TOOL_NAMES = new Set([EDIT_TOOL_NAME, WRITE_FILE_TOOL_NAME]);
|
||||||
|
|
||||||
// Tool Display Names
|
/**
|
||||||
export const WRITE_FILE_DISPLAY_NAME = 'WriteFile';
|
* Tools that can access local files or remote resources and should be
|
||||||
export const EDIT_DISPLAY_NAME = 'Edit';
|
* treated with extra caution when updating policies.
|
||||||
export const ASK_USER_DISPLAY_NAME = 'Ask User';
|
*/
|
||||||
export const READ_FILE_DISPLAY_NAME = 'ReadFile';
|
export const SENSITIVE_TOOLS = new Set([
|
||||||
export const GLOB_DISPLAY_NAME = 'FindFiles';
|
GLOB_TOOL_NAME,
|
||||||
|
GREP_TOOL_NAME,
|
||||||
|
READ_MANY_FILES_TOOL_NAME,
|
||||||
|
WEB_FETCH_TOOL_NAME,
|
||||||
|
READ_FILE_TOOL_NAME,
|
||||||
|
LS_TOOL_NAME,
|
||||||
|
WRITE_FILE_TOOL_NAME,
|
||||||
|
EDIT_TOOL_NAME,
|
||||||
|
SHELL_TOOL_NAME,
|
||||||
|
]);
|
||||||
|
|
||||||
export const TRACKER_CREATE_TASK_TOOL_NAME = 'tracker_create_task';
|
export const TRACKER_CREATE_TASK_TOOL_NAME = 'tracker_create_task';
|
||||||
export const TRACKER_UPDATE_TASK_TOOL_NAME = 'tracker_update_task';
|
export const TRACKER_UPDATE_TASK_TOOL_NAME = 'tracker_update_task';
|
||||||
export const TRACKER_GET_TASK_TOOL_NAME = 'tracker_get_task';
|
export const TRACKER_GET_TASK_TOOL_NAME = 'tracker_get_task';
|
||||||
@@ -167,6 +177,13 @@ export const TRACKER_LIST_TASKS_TOOL_NAME = 'tracker_list_tasks';
|
|||||||
export const TRACKER_ADD_DEPENDENCY_TOOL_NAME = 'tracker_add_dependency';
|
export const TRACKER_ADD_DEPENDENCY_TOOL_NAME = 'tracker_add_dependency';
|
||||||
export const TRACKER_VISUALIZE_TOOL_NAME = 'tracker_visualize';
|
export const TRACKER_VISUALIZE_TOOL_NAME = 'tracker_visualize';
|
||||||
|
|
||||||
|
// Tool Display Names
|
||||||
|
export const WRITE_FILE_DISPLAY_NAME = 'WriteFile';
|
||||||
|
export const EDIT_DISPLAY_NAME = 'Edit';
|
||||||
|
export const ASK_USER_DISPLAY_NAME = 'Ask User';
|
||||||
|
export const READ_FILE_DISPLAY_NAME = 'ReadFile';
|
||||||
|
export const GLOB_DISPLAY_NAME = 'FindFiles';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mapping of legacy tool names to their current names.
|
* Mapping of legacy tool names to their current names.
|
||||||
* This ensures backward compatibility for user-defined policies, skills, and hooks.
|
* This ensures backward compatibility for user-defined policies, skills, and hooks.
|
||||||
|
|||||||
@@ -12,7 +12,9 @@ import {
|
|||||||
type ToolInvocation,
|
type ToolInvocation,
|
||||||
type ToolResult,
|
type ToolResult,
|
||||||
type ToolConfirmationOutcome,
|
type ToolConfirmationOutcome,
|
||||||
|
type PolicyUpdateOptions,
|
||||||
} from './tools.js';
|
} from './tools.js';
|
||||||
|
import { buildPatternArgsPattern } from '../policy/utils.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';
|
||||||
import { getErrorMessage } from '../utils/errors.js';
|
import { getErrorMessage } from '../utils/errors.js';
|
||||||
@@ -291,6 +293,22 @@ ${textContent}
|
|||||||
return `Processing URLs and instructions from prompt: "${displayPrompt}"`;
|
return `Processing URLs and instructions from prompt: "${displayPrompt}"`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override getPolicyUpdateOptions(
|
||||||
|
_outcome: ToolConfirmationOutcome,
|
||||||
|
): PolicyUpdateOptions | undefined {
|
||||||
|
if (this.params.url) {
|
||||||
|
return {
|
||||||
|
argsPattern: buildPatternArgsPattern(this.params.url),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (this.params.prompt) {
|
||||||
|
return {
|
||||||
|
argsPattern: buildPatternArgsPattern(this.params.prompt),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
protected override async getConfirmationDetails(
|
protected override async getConfirmationDetails(
|
||||||
_abortSignal: AbortSignal,
|
_abortSignal: AbortSignal,
|
||||||
): Promise<ToolCallConfirmationDetails | false> {
|
): Promise<ToolCallConfirmationDetails | false> {
|
||||||
|
|||||||
Reference in New Issue
Block a user