refactor(core): delete obsolete coreToolScheduler (#23502)

This commit is contained in:
Abhi
2026-03-23 17:21:14 -04:00
committed by GitHub
parent db14cdf92b
commit 919e5da581
7 changed files with 30 additions and 3987 deletions

View File

@@ -24,14 +24,16 @@ import {
} from '@google/genai';
import * as codeAssist from './codeAssist.js';
import type { CodeAssistServer } from './server.js';
import type { CompletedToolCall } from '../core/coreToolScheduler.js';
import type {
CompletedToolCall,
ToolCallResponseInfo,
} from '../scheduler/types.js';
import {
ToolConfirmationOutcome,
type AnyDeclarativeTool,
type AnyToolInvocation,
} from '../tools/tools.js';
import type { Config } from '../config/config.js';
import type { ToolCallResponseInfo } from '../scheduler/types.js';
function createMockResponse(
candidates: GenerateContentResponse['candidates'] = [],

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,313 +0,0 @@
/**
* @license
* Copyright 2025 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import { describe, it, expect, vi } from 'vitest';
import { CoreToolScheduler } from './coreToolScheduler.js';
import type { ToolCall, ErroredToolCall } from '../scheduler/types.js';
import type { Config, ToolRegistry, AgentLoopContext } from '../index.js';
import {
ApprovalMode,
DEFAULT_TRUNCATE_TOOL_OUTPUT_THRESHOLD,
} from '../index.js';
import { createMockMessageBus } from '../test-utils/mock-message-bus.js';
import { MockTool } from '../test-utils/mock-tool.js';
import { DEFAULT_GEMINI_MODEL } from '../config/models.js';
import type { PolicyEngine } from '../policy/policy-engine.js';
import type { HookSystem } from '../hooks/hookSystem.js';
import { BeforeToolHookOutput } from '../hooks/types.js';
function createMockConfig(overrides: Partial<Config> = {}): Config {
const defaultToolRegistry = {
getTool: () => undefined,
getToolByName: () => undefined,
getFunctionDeclarations: () => [],
tools: new Map(),
discovery: {},
registerTool: () => {},
getToolByDisplayName: () => undefined,
getTools: () => [],
discoverTools: async () => {},
getAllTools: () => [],
getToolsByServer: () => [],
getExperiments: () => {},
} as unknown as ToolRegistry;
const baseConfig = {
getSessionId: () => 'test-session-id',
getUsageStatisticsEnabled: () => true,
getDebugMode: () => false,
isInteractive: () => true,
getApprovalMode: () => ApprovalMode.DEFAULT,
setApprovalMode: () => {},
getAllowedTools: () => [],
getContentGeneratorConfig: () => ({
model: 'test-model',
authType: 'oauth-personal',
}),
getShellExecutionConfig: () => ({
terminalWidth: 90,
terminalHeight: 30,
sanitizationConfig: {
enableEnvironmentVariableRedaction: true,
allowedEnvironmentVariables: [],
blockedEnvironmentVariables: [],
},
}),
storage: {
getProjectTempDir: () => '/tmp',
},
getTruncateToolOutputThreshold: () =>
DEFAULT_TRUNCATE_TOOL_OUTPUT_THRESHOLD,
getTruncateToolOutputLines: () => 1000,
getToolRegistry: () => defaultToolRegistry,
getActiveModel: () => DEFAULT_GEMINI_MODEL,
getGeminiClient: () => null,
getMessageBus: () => createMockMessageBus(),
getEnableHooks: () => true, // Enabled for these tests
getExperiments: () => {},
getPolicyEngine: () =>
({
check: async () => ({ decision: 'allow' }), // Default allow for hook tests
}) as unknown as PolicyEngine,
} as unknown as Config;
// eslint-disable-next-line @typescript-eslint/no-misused-spread
return { ...baseConfig, ...overrides } as Config;
}
describe('CoreToolScheduler Hooks', () => {
it('should stop execution if BeforeTool hook requests stop', async () => {
const executeFn = vi.fn().mockResolvedValue({
llmContent: 'Tool executed',
returnDisplay: 'Tool executed',
});
const mockTool = new MockTool({ name: 'mockTool', execute: executeFn });
const toolRegistry = {
getTool: () => mockTool,
getToolByName: () => mockTool,
getFunctionDeclarations: () => [],
tools: new Map(),
discovery: {},
registerTool: () => {},
getToolByDisplayName: () => mockTool,
getTools: () => [],
discoverTools: async () => {},
getAllTools: () => [],
getToolsByServer: () => [],
} as unknown as ToolRegistry;
const mockMessageBus = createMockMessageBus();
const mockHookSystem = {
fireBeforeToolEvent: vi.fn().mockResolvedValue({
shouldStopExecution: () => true,
getEffectiveReason: () => 'Hook stopped execution',
getBlockingError: () => ({ blocked: false }),
isAskDecision: () => false,
}),
} as unknown as HookSystem;
const mockConfig = createMockConfig({
getToolRegistry: () => toolRegistry,
getMessageBus: () => mockMessageBus,
getHookSystem: () => mockHookSystem,
getApprovalMode: () => ApprovalMode.YOLO,
});
const onAllToolCallsComplete = vi.fn();
const scheduler = new CoreToolScheduler({
context: {
config: mockConfig,
messageBus: mockMessageBus,
toolRegistry,
} as unknown as AgentLoopContext,
onAllToolCallsComplete,
getPreferredEditor: () => 'vscode',
});
const request = {
callId: '1',
name: 'mockTool',
args: {},
isClientInitiated: false,
prompt_id: 'prompt-1',
};
await scheduler.schedule([request], new AbortController().signal);
await vi.waitFor(() => {
expect(onAllToolCallsComplete).toHaveBeenCalled();
});
const completedCalls = onAllToolCallsComplete.mock
.calls[0][0] as ToolCall[];
expect(completedCalls[0].status).toBe('error');
const erroredCall = completedCalls[0] as ErroredToolCall;
// Check error type/message
expect(erroredCall.response.error?.message).toContain(
'Hook stopped execution',
);
expect(executeFn).not.toHaveBeenCalled();
});
it('should block tool execution if BeforeTool hook requests block', async () => {
const executeFn = vi.fn();
const mockTool = new MockTool({ name: 'mockTool', execute: executeFn });
const toolRegistry = {
getTool: () => mockTool,
getToolByName: () => mockTool,
getFunctionDeclarations: () => [],
tools: new Map(),
discovery: {},
registerTool: () => {},
getToolByDisplayName: () => mockTool,
getTools: () => [],
discoverTools: async () => {},
getAllTools: () => [],
getToolsByServer: () => [],
} as unknown as ToolRegistry;
const mockMessageBus = createMockMessageBus();
const mockHookSystem = {
fireBeforeToolEvent: vi.fn().mockResolvedValue({
shouldStopExecution: () => false,
getBlockingError: () => ({
blocked: true,
reason: 'Hook blocked execution',
}),
isAskDecision: () => false,
}),
} as unknown as HookSystem;
const mockConfig = createMockConfig({
getToolRegistry: () => toolRegistry,
getMessageBus: () => mockMessageBus,
getHookSystem: () => mockHookSystem,
getApprovalMode: () => ApprovalMode.YOLO,
});
const onAllToolCallsComplete = vi.fn();
const scheduler = new CoreToolScheduler({
context: {
config: mockConfig,
messageBus: mockMessageBus,
toolRegistry,
} as unknown as AgentLoopContext,
onAllToolCallsComplete,
getPreferredEditor: () => 'vscode',
});
const request = {
callId: '1',
name: 'mockTool',
args: {},
isClientInitiated: false,
prompt_id: 'prompt-1',
};
await scheduler.schedule([request], new AbortController().signal);
await vi.waitFor(() => {
expect(onAllToolCallsComplete).toHaveBeenCalled();
});
const completedCalls = onAllToolCallsComplete.mock
.calls[0][0] as ToolCall[];
expect(completedCalls[0].status).toBe('error');
const erroredCall = completedCalls[0] as ErroredToolCall;
expect(erroredCall.response.error?.message).toContain(
'Hook blocked execution',
);
expect(executeFn).not.toHaveBeenCalled();
});
it('should update tool input if BeforeTool hook provides modified input', async () => {
const executeFn = vi.fn().mockResolvedValue({
llmContent: 'Tool executed',
returnDisplay: 'Tool executed',
});
const mockTool = new MockTool({ name: 'mockTool', execute: executeFn });
const toolRegistry = {
getTool: () => mockTool,
getToolByName: () => mockTool,
getFunctionDeclarations: () => [],
tools: new Map(),
discovery: {},
registerTool: () => {},
getToolByDisplayName: () => mockTool,
getTools: () => [],
discoverTools: async () => {},
getAllTools: () => [],
getToolsByServer: () => [],
} as unknown as ToolRegistry;
const mockMessageBus = createMockMessageBus();
const mockBeforeOutput = new BeforeToolHookOutput({
continue: true,
hookSpecificOutput: {
hookEventName: 'BeforeTool',
tool_input: { newParam: 'modifiedValue' },
},
});
const mockHookSystem = {
fireBeforeToolEvent: vi.fn().mockResolvedValue(mockBeforeOutput),
fireAfterToolEvent: vi.fn(),
} as unknown as HookSystem;
const mockConfig = createMockConfig({
getToolRegistry: () => toolRegistry,
getMessageBus: () => mockMessageBus,
getHookSystem: () => mockHookSystem,
getApprovalMode: () => ApprovalMode.YOLO,
});
const onAllToolCallsComplete = vi.fn();
const scheduler = new CoreToolScheduler({
context: {
config: mockConfig,
messageBus: mockMessageBus,
toolRegistry,
} as unknown as AgentLoopContext,
onAllToolCallsComplete,
getPreferredEditor: () => 'vscode',
});
const request = {
callId: '1',
name: 'mockTool',
args: { originalParam: 'originalValue' },
isClientInitiated: false,
prompt_id: 'prompt-1',
};
await scheduler.schedule([request], new AbortController().signal);
await vi.waitFor(() => {
expect(onAllToolCallsComplete).toHaveBeenCalled();
});
const completedCalls = onAllToolCallsComplete.mock
.calls[0][0] as ToolCall[];
expect(completedCalls[0].status).toBe('success');
// Verify execute was called with modified args
expect(executeFn).toHaveBeenCalledWith(
{ newParam: 'modifiedValue' },
expect.anything(),
undefined,
expect.anything(),
);
// Verify call request args were updated in the completion report
expect(completedCalls[0].request.args).toEqual({
newParam: 'modifiedValue',
});
});
});

View File

@@ -34,11 +34,9 @@ import {
ROOT_SCHEDULER_ID,
type ValidatingToolCall,
type ToolCallRequestInfo,
type CompletedToolCall,
} from './types.js';
import type { PolicyEngine } from '../policy/policy-engine.js';
import { DiscoveredMCPTool } from '../tools/mcp-tool.js';
import { CoreToolScheduler } from '../core/coreToolScheduler.js';
import { Scheduler } from './scheduler.js';
import { ToolErrorType } from '../tools/tool-error.js';
import type { ToolRegistry } from '../tools/tool-registry.js';
@@ -840,61 +838,32 @@ describe('Plan Mode Denial Consistency', () => {
vi.clearAllMocks();
});
describe.each([
{ enableEventDrivenScheduler: false, name: 'Legacy CoreToolScheduler' },
{ enableEventDrivenScheduler: true, name: 'Event-Driven Scheduler' },
])('$name', ({ enableEventDrivenScheduler }) => {
it('should return the correct Plan Mode denial message when policy denies execution', async () => {
let resultMessage: string | undefined;
let resultErrorType: ToolErrorType | undefined;
it('should return the correct Plan Mode denial message when policy denies execution', async () => {
let resultMessage: string | undefined;
let resultErrorType: ToolErrorType | undefined;
const signal = new AbortController().signal;
const signal = new AbortController().signal;
if (enableEventDrivenScheduler) {
const scheduler = new Scheduler({
context: {
config: mockConfig,
messageBus: mockMessageBus,
toolRegistry: mockToolRegistry,
} as unknown as AgentLoopContext,
getPreferredEditor: () => undefined,
schedulerId: ROOT_SCHEDULER_ID,
});
const results = await scheduler.schedule(req, signal);
const result = results[0];
expect(result.status).toBe('error');
if (result.status === 'error') {
resultMessage = result.response.error?.message;
resultErrorType = result.response.errorType;
}
} else {
let capturedCalls: CompletedToolCall[] = [];
const scheduler = new CoreToolScheduler({
context: {
config: mockConfig,
messageBus: mockMessageBus,
toolRegistry: mockToolRegistry,
} as unknown as AgentLoopContext,
getPreferredEditor: () => undefined,
onAllToolCallsComplete: async (calls) => {
capturedCalls = calls;
},
});
await scheduler.schedule(req, signal);
expect(capturedCalls.length).toBeGreaterThan(0);
const call = capturedCalls[0];
if (call.status === 'error') {
resultMessage = call.response.error?.message;
resultErrorType = call.response.errorType;
}
}
expect(resultMessage).toBe('Tool execution denied by policy.');
expect(resultErrorType).toBe(ToolErrorType.POLICY_VIOLATION);
const scheduler = new Scheduler({
context: {
config: mockConfig,
messageBus: mockMessageBus,
toolRegistry: mockToolRegistry,
} as unknown as AgentLoopContext,
getPreferredEditor: () => undefined,
schedulerId: ROOT_SCHEDULER_ID,
});
const results = await scheduler.schedule(req, signal);
const result = results[0];
expect(result.status).toBe('error');
if (result.status === 'error') {
resultMessage = result.response.error?.message;
resultErrorType = result.response.errorType;
}
expect(resultMessage).toBe('Tool execution denied by policy.');
expect(resultErrorType).toBe(ToolErrorType.POLICY_VIOLATION);
});
});

View File

@@ -25,7 +25,7 @@ import {
AuthType,
type ContentGeneratorConfig,
} from '../../core/contentGenerator.js';
import type { SuccessfulToolCall } from '../../core/coreToolScheduler.js';
import type { SuccessfulToolCall } from '../../scheduler/types.js';
import type { ConfigParameters } from '../../config/config.js';
import { EventMetadataKey } from './event-metadata-key.js';
import { makeFakeConfig } from '../../test-utils/config.js';

View File

@@ -20,7 +20,7 @@ import type {
CompletedToolCall,
ErroredToolCall,
SuccessfulToolCall,
} from '../core/coreToolScheduler.js';
} from '../scheduler/types.js';
import { ToolErrorType } from '../tools/tool-error.js';
import { ToolConfirmationOutcome } from '../tools/tools.js';
import { MockTool } from '../test-utils/mock-tool.js';