mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-07-16 04:50:29 -07:00
feat(sandbox): dynamic macOS sandbox expansion and worktree support (#23301)
This commit is contained in:
@@ -77,7 +77,8 @@ export async function checkPolicy(
|
||||
// confirmation prompt if the policy engine's decision is 'ASK_USER'.
|
||||
if (
|
||||
decision === PolicyDecision.ASK_USER &&
|
||||
toolCall.request.isClientInitiated
|
||||
toolCall.request.isClientInitiated &&
|
||||
!toolCall.request.args?.['additional_permissions']
|
||||
) {
|
||||
return {
|
||||
decision: PolicyDecision.ALLOW,
|
||||
|
||||
@@ -792,6 +792,110 @@ export class Scheduler {
|
||||
return true;
|
||||
}
|
||||
|
||||
let isSandboxError = false;
|
||||
let sandboxDetailsStr = '';
|
||||
|
||||
if (
|
||||
result.status === CoreToolCallStatus.Error &&
|
||||
result.response.errorType === 'sandbox_expansion_required'
|
||||
) {
|
||||
isSandboxError = true;
|
||||
sandboxDetailsStr = result.response.error?.message || '';
|
||||
}
|
||||
|
||||
if (isSandboxError) {
|
||||
try {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
const parsedError = JSON.parse(sandboxDetailsStr) as {
|
||||
rootCommand: string;
|
||||
additionalPermissions: import('../services/sandboxManager.js').SandboxPermissions;
|
||||
};
|
||||
|
||||
const confirmationDetails: SerializableConfirmationDetails = {
|
||||
type: 'sandbox_expansion',
|
||||
title: 'Sandbox Expansion Request',
|
||||
command: String(
|
||||
activeCall.request.args['command'] ?? parsedError.rootCommand,
|
||||
),
|
||||
rootCommand: parsedError.rootCommand,
|
||||
additionalPermissions: parsedError.additionalPermissions,
|
||||
};
|
||||
|
||||
const correlationId = crypto.randomUUID();
|
||||
|
||||
// Mutate the active call so resolveConfirmation generates the correct Sandbox Expansion details
|
||||
activeCall.request.args['additional_permissions'] =
|
||||
parsedError.additionalPermissions;
|
||||
activeCall.invocation = activeCall.tool.build(activeCall.request.args);
|
||||
|
||||
// CRITICAL: We must push the new args and invocation into the state manager
|
||||
// before calling resolveConfirmation, because resolveConfirmation fetches
|
||||
// the tool call directly from the state manager!
|
||||
this.state.updateArgs(
|
||||
callId,
|
||||
activeCall.request.args,
|
||||
activeCall.invocation,
|
||||
);
|
||||
|
||||
this.state.updateStatus(callId, CoreToolCallStatus.AwaitingApproval, {
|
||||
confirmationDetails,
|
||||
correlationId,
|
||||
});
|
||||
|
||||
const validatingCall = {
|
||||
...activeCall,
|
||||
status: CoreToolCallStatus.Validating,
|
||||
} as ValidatingToolCall;
|
||||
|
||||
const confResult = await resolveConfirmation(validatingCall, signal, {
|
||||
config: this.config,
|
||||
messageBus: this.messageBus,
|
||||
state: this.state,
|
||||
modifier: this.modifier,
|
||||
getPreferredEditor: this.getPreferredEditor,
|
||||
schedulerId: this.schedulerId,
|
||||
onWaitingForConfirmation: this.onWaitingForConfirmation,
|
||||
});
|
||||
|
||||
if (confResult.outcome === ToolConfirmationOutcome.Cancel) {
|
||||
type LegacyHack = ToolCallResponseInfo & {
|
||||
llmContent?: string;
|
||||
returnDisplay?: string;
|
||||
};
|
||||
const errorResponse = { ...result.response } as LegacyHack;
|
||||
errorResponse.llmContent =
|
||||
'User cancelled sandbox expansion. The command failed with a sandbox denial. Shell output:\n' +
|
||||
String(errorResponse.returnDisplay);
|
||||
|
||||
this.state.updateStatus(
|
||||
callId,
|
||||
CoreToolCallStatus.Error,
|
||||
errorResponse,
|
||||
);
|
||||
return false;
|
||||
}
|
||||
|
||||
activeCall.request.args['additional_permissions'] =
|
||||
parsedError.additionalPermissions;
|
||||
|
||||
// Reset the output stream visual so it replaces the error text
|
||||
this.state.updateStatus(callId, CoreToolCallStatus.Executing, {
|
||||
liveOutput: undefined,
|
||||
});
|
||||
|
||||
// Call _execute synchronously and properly return its promise to loop internally!
|
||||
return await this._execute(
|
||||
{
|
||||
...activeCall,
|
||||
status: CoreToolCallStatus.Scheduled,
|
||||
} as ScheduledToolCall,
|
||||
signal,
|
||||
);
|
||||
} catch (_e) {
|
||||
// Fallback to normal error handling if parsing/looping fails
|
||||
}
|
||||
}
|
||||
|
||||
if (result.status === CoreToolCallStatus.Success) {
|
||||
this.state.updateStatus(
|
||||
callId,
|
||||
|
||||
Reference in New Issue
Block a user