Fix/pending tools and trust overrides (#27854)

This commit is contained in:
jvargassanchez-dot
2026-06-15 22:24:50 +00:00
committed by GitHub
parent bca5667fc6
commit 0f8a157e5e
8 changed files with 249 additions and 38 deletions
+8 -2
View File
@@ -26,7 +26,10 @@ import {
type ScheduledToolCall,
} from './types.js';
import { ToolErrorType } from '../tools/tool-error.js';
import { UPDATE_TOPIC_TOOL_NAME } from '../tools/tool-names.js';
import {
UPDATE_TOPIC_TOOL_NAME,
EDIT_TOOL_NAMES,
} from '../tools/tool-names.js';
import { PolicyDecision, type ApprovalMode } from '../policy/types.js';
import {
ToolConfirmationOutcome,
@@ -548,7 +551,10 @@ export class Scheduler {
private _isParallelizable(request: ToolCallRequestInfo): boolean {
// update_topic tool is forced as sequential call
if (request.name === UPDATE_TOPIC_TOOL_NAME) {
if (
request.name === UPDATE_TOPIC_TOOL_NAME ||
EDIT_TOOL_NAMES.has(request.name)
) {
return false;
}
if (request.args) {
@@ -79,7 +79,12 @@ import {
type Status,
type ToolCall,
} from './types.js';
import { UPDATE_TOPIC_TOOL_NAME } from '../tools/tool-names.js';
import {
UPDATE_TOPIC_TOOL_NAME,
WRITE_FILE_TOOL_NAME,
EDIT_TOOL_NAME,
EDIT_TOOL_NAMES,
} from '../tools/tool-names.js';
import { GeminiCliOperation } from '../telemetry/constants.js';
import type { EditorType } from '../utils/editor.js';
@@ -161,6 +166,12 @@ describe('Scheduler Parallel Execution', () => {
isReadOnly: false,
build: vi.fn(),
} as unknown as AnyDeclarativeTool;
const editTool = {
name: EDIT_TOOL_NAME,
kind: Kind.Execute,
isReadOnly: false,
build: vi.fn(),
} as unknown as AnyDeclarativeTool;
const agentTool1 = {
name: 'agent-tool-1',
kind: Kind.Agent,
@@ -203,6 +214,8 @@ describe('Scheduler Parallel Execution', () => {
if (name === 'agent-tool-1') return agentTool1;
if (name === 'agent-tool-2') return agentTool2;
if (name === UPDATE_TOPIC_TOOL_NAME) return topicTool;
if (name === WRITE_FILE_TOOL_NAME) return writeTool;
if (name === EDIT_TOOL_NAME) return editTool;
return undefined;
}),
getAllToolNames: vi
@@ -214,6 +227,8 @@ describe('Scheduler Parallel Execution', () => {
'agent-tool-1',
'agent-tool-2',
UPDATE_TOPIC_TOOL_NAME,
WRITE_FILE_TOOL_NAME,
EDIT_TOOL_NAME,
]),
} as unknown as Mocked<ToolRegistry>;
@@ -336,6 +351,9 @@ describe('Scheduler Parallel Execution', () => {
vi.mocked(writeTool.build).mockReturnValue(
mockInvocation as unknown as AnyToolInvocation,
);
vi.mocked(editTool.build).mockReturnValue(
mockInvocation as unknown as AnyToolInvocation,
);
vi.mocked(agentTool1.build).mockReturnValue(
mockInvocation as unknown as AnyToolInvocation,
);
@@ -597,4 +615,44 @@ describe('Scheduler Parallel Execution', () => {
expect(executionLog.slice(2, 4)).toContain('start-call-1');
expect(executionLog.slice(2, 4)).toContain('start-call-2');
});
it.each(Array.from(EDIT_TOOL_NAMES))(
'should execute %s sequentially even without wait_for_previous',
async (toolName) => {
const executionLog: string[] = [];
mockExecutor.execute.mockImplementation(async ({ call }) => {
const id = call.request.callId;
executionLog.push(`start-${id}`);
await new Promise<void>((resolve) => setTimeout(resolve, 10));
executionLog.push(`end-${id}`);
return {
status: 'success',
response: { callId: id, responseParts: [] },
} as unknown as SuccessfulToolCall;
});
const e1: ToolCallRequestInfo = {
callId: 'e1',
name: toolName,
args: { path: 'a.txt', wait_for_previous: false },
isClientInitiated: false,
prompt_id: 'p1',
schedulerId: ROOT_SCHEDULER_ID,
};
const e2: ToolCallRequestInfo = {
...e1,
callId: 'e2',
};
await scheduler.schedule([e1, e2], signal);
// Even though wait_for_previous is false, EDIT_TOOL_NAMES enforces sequential execution
expect(executionLog).toEqual([
'start-e1',
'end-e1',
'start-e2',
'end-e2',
]);
},
);
});