mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-06-24 18:27:01 -07:00
fix(core): make read_background_output delay abort-aware
Pressing ESC during a read_background_output call with delay_ms left the scheduler blocked until the timer fired, since the sleep ignored the abort signal. Use the abortable timers/promises setTimeout so cancellation rejects immediately with an AbortError, which the tool executor already converts into a Cancelled result. Fixes #18007
This commit is contained in:
@@ -331,4 +331,29 @@ describe('Background Tools', () => {
|
||||
|
||||
fs.unlinkSync(logPath);
|
||||
});
|
||||
|
||||
it('read_background_output should abort the delay_ms wait when the signal is aborted', async () => {
|
||||
const invocation = readTool.build({ pid: 12345, delay_ms: 60_000 });
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(invocation as any).context = { config: { getSessionId: () => 'default' } };
|
||||
|
||||
const controller = new AbortController();
|
||||
const promise = invocation.execute({ abortSignal: controller.signal });
|
||||
controller.abort();
|
||||
|
||||
await expect(promise).rejects.toMatchObject({ name: 'AbortError' });
|
||||
});
|
||||
|
||||
it('read_background_output should reject immediately when the signal is already aborted', async () => {
|
||||
const invocation = readTool.build({ pid: 12345, delay_ms: 60_000 });
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
(invocation as any).context = { config: { getSessionId: () => 'default' } };
|
||||
|
||||
const controller = new AbortController();
|
||||
controller.abort();
|
||||
|
||||
await expect(
|
||||
invocation.execute({ abortSignal: controller.signal }),
|
||||
).rejects.toMatchObject({ name: 'AbortError' });
|
||||
});
|
||||
});
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
*/
|
||||
|
||||
import fs from 'node:fs';
|
||||
import { setTimeout as delay } from 'node:timers/promises';
|
||||
import { ShellExecutionService } from '../services/shellExecutionService.js';
|
||||
import {
|
||||
BaseDeclarativeTool,
|
||||
@@ -130,11 +131,15 @@ class ReadBackgroundOutputInvocation extends BaseToolInvocation<
|
||||
return `Reading output for background process ${this.params.pid}`;
|
||||
}
|
||||
|
||||
async execute({ abortSignal: _signal }: ExecuteOptions): Promise<ToolResult> {
|
||||
async execute({ abortSignal }: ExecuteOptions): Promise<ToolResult> {
|
||||
const pid = this.params.pid;
|
||||
|
||||
if (this.params.delay_ms && this.params.delay_ms > 0) {
|
||||
await new Promise((resolve) => setTimeout(resolve, this.params.delay_ms));
|
||||
// Abort-aware delay: rejects with an AbortError when the user cancels,
|
||||
// which the tool executor converts into a Cancelled result. Without
|
||||
// this, cancellation would leave the scheduler blocked until the
|
||||
// timer fires.
|
||||
await delay(this.params.delay_ms, undefined, { signal: abortSignal });
|
||||
}
|
||||
|
||||
// Verify process belongs to this session to prevent reading logs of processes from other sessions/users
|
||||
|
||||
Reference in New Issue
Block a user