feat(core): add configurable delay for shell commands in YOLO mode#25028
feat(core): add configurable delay for shell commands in YOLO mode#25028jardondiego wants to merge 5 commits intogoogle-gemini:mainfrom
Conversation
🛑 Action Required: Evaluation ApprovalSteering changes have been detected in this PR. To prevent regressions, a maintainer must approve the evaluation run before this PR can be merged. Maintainers:
Once approved, the evaluation results will be posted here automatically. |
Summary of ChangesHello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request enhances the security of the CLI's YOLO mode by introducing a configurable delay before shell commands are executed. This change provides a critical safety window for users to review and cancel potentially dangerous commands, significantly reducing the risk of accidental execution. Highlights
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here. Footnotes
|
There was a problem hiding this comment.
Code Review
This pull request introduces a new security.yoloShellDelayMs configuration setting, enabling a configurable delay before shell commands execute in YOLO mode. This feature includes an interactive countdown in the CLI, allowing users to cancel commands during the delay. The review feedback points out that the current setInterval implementation for the delay is imprecise, rounding up to the nearest second, and suggests an alternative recursive setTimeout approach for more accurate millisecond timing.
| if (isYoloMode && delayMs > 0 && !this.params.is_background) { | ||
| let countdownInterval: NodeJS.Timeout | undefined; | ||
| let countdownMs = delayMs; | ||
| const updateIntervalMs = 1000; | ||
|
|
||
| try { | ||
| await new Promise<void>((resolve, reject) => { | ||
| const onAbort = () => { | ||
| if (countdownInterval) clearInterval(countdownInterval); | ||
| reject(new Error('Command cancelled by user during YOLO delay.')); | ||
| }; | ||
| signal.addEventListener('abort', onAbort, { once: true }); | ||
|
|
||
| if (updateOutput) { | ||
| updateOutput( | ||
| `[YOLO] Executing in ${Math.ceil(countdownMs / 1000)}s... (Press Ctrl+C to cancel)\n`, | ||
| ); | ||
| } | ||
|
|
||
| countdownInterval = setInterval(() => { | ||
| countdownMs -= updateIntervalMs; | ||
| if (countdownMs <= 0) { | ||
| clearInterval(countdownInterval); | ||
| signal.removeEventListener('abort', onAbort); | ||
| resolve(); | ||
| } else if (updateOutput) { | ||
| updateOutput( | ||
| `[YOLO] Executing in ${Math.ceil(countdownMs / 1000)}s... (Press Ctrl+C to cancel)\n`, | ||
| ); | ||
| } | ||
| }, updateIntervalMs); | ||
| }); | ||
| } catch { | ||
| return { | ||
| llmContent: | ||
| 'Command was cancelled by user during the YOLO mode delay.', | ||
| returnDisplay: 'Command cancelled by user.', | ||
| }; | ||
| } | ||
| } |
There was a problem hiding this comment.
The current implementation of the YOLO mode delay using setInterval is not precise and will always round the delay up to the nearest second. For example, a configured delay of 2500ms will result in an actual delay of 3000ms. This can be confusing for users who expect millisecond precision as implied by the yoloShellDelayMs setting name.
To ensure the delay is accurate, I suggest using a recursive setTimeout approach that calculates the remaining time based on the current time. This will respect the configured millisecond value precisely.
if (isYoloMode && delayMs > 0 && !this.params.is_background) {
try {
await new Promise<void>((resolve, reject) => {
const endTime = Date.now() + delayMs;
let timeout: NodeJS.Timeout;
const onAbort = () => {
clearTimeout(timeout);
reject(new Error('Command cancelled by user during YOLO delay.'));
};
signal.addEventListener('abort', onAbort, { once: true });
const tick = () => {
const remainingMs = Math.max(0, endTime - Date.now());
if (updateOutput) {
updateOutput(
`[YOLO] Executing in ${Math.ceil(
remainingMs / 1000,
)}s... (Press Ctrl+C to cancel)\n`,
);
}
if (remainingMs <= 0) {
signal.removeEventListener('abort', onAbort);
resolve();
} else {
timeout = setTimeout(tick, Math.min(remainingMs, 1000));
}
};
tick();
});
} catch {
return {
llmContent:
'Command was cancelled by user during the YOLO mode delay.',
returnDisplay: 'Command cancelled by user.',
};
}
}References
- The suggested code correctly utilizes
AbortSignalfor cancellation of the asynchronous delay, aligning with the principle that asynchronous operations should rely onAbortSignalfor cancellation to maintain consistency with existing patterns.
Summary
Adds a configurable delay (
security.yoloShellDelayMs) before automaticallyexecuting shell commands when the CLI is in YOLO mode. The delay natively
supports an interactive countdown in the CLI, allowing users to safely cancel
(
Ctrl+C) dangerous commands without them executing. The default value is0(disabled) to maintain backwards compatibility.
Details
yoloShellDelayMstosettingsSchema.jsonandpassed it down to the
Configsingleton in@google/gemini-cli-core.ShellToolInvocation.executeto intercept YOLOmode shell commands (unless they are background commands) and display a
1-second interval countdown using the
updateOutputcallback.AbortSignalduring the countdown. If theuser hits
Ctrl+Cbefore the timer expires, the tool returns early and avoidsshelling out.
shell.test.tsutilizingvi.useFakeTimers()to ensure accurate timing and correct cancellationhandling.
Related Issues
How to Validate
settings.json, add"security": { "yoloShellDelayMs": 3000 }.--yoloflag.Run ls -la).[YOLO] Executing in 3s... (Press Ctrl+C to cancel).Pre-Merge Checklist