Merge tag 'v0.3.0' into chore/sync-gemini-cli-v0.3.0

This commit is contained in:
mingholy.lmh 2025-09-10 21:01:40 +08:00
commit 14ea33063f
583 changed files with 30160 additions and 10770 deletions

View file

@ -5,36 +5,36 @@
*/
/* eslint-disable @typescript-eslint/no-explicit-any */
import { describe, it, expect, vi, beforeEach, afterEach, Mock } from 'vitest';
import type { Mock } from 'vitest';
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import { renderHook, act } from '@testing-library/react';
import {
useReactToolScheduler,
mapToDisplay,
} from './useReactToolScheduler.js';
import { PartUnion, FunctionResponse } from '@google/genai';
import {
import type { PartUnion, FunctionResponse } from '@google/genai';
import type {
Config,
ToolCallRequestInfo,
ToolRegistry,
ToolResult,
ToolCallConfirmationDetails,
ToolConfirmationOutcome,
ToolCallResponseInfo,
ToolCall, // Import from core
Status as ToolCallStatusType,
ApprovalMode,
Kind,
BaseDeclarativeTool,
BaseToolInvocation,
ToolInvocation,
AnyDeclarativeTool,
AnyToolInvocation,
} from '@qwen-code/qwen-code-core';
import {
HistoryItemWithoutId,
ToolCallStatus,
HistoryItemToolGroup,
} from '../types.js';
ToolConfirmationOutcome,
ApprovalMode,
Kind,
BaseDeclarativeTool,
BaseToolInvocation,
} from '@qwen-code/qwen-code-core';
import type { HistoryItemWithoutId, HistoryItemToolGroup } from '../types.js';
import { ToolCallStatus } from '../types.js';
// Mocks
vi.mock('@qwen-code/qwen-code-core', async () => {
@ -48,19 +48,21 @@ vi.mock('@qwen-code/qwen-code-core', async () => {
const mockToolRegistry = {
getTool: vi.fn(),
getAllToolNames: vi.fn(() => ['mockTool', 'anotherTool']),
};
const mockConfig = {
getToolRegistry: vi.fn(() => mockToolRegistry as unknown as ToolRegistry),
getApprovalMode: vi.fn(() => ApprovalMode.DEFAULT),
getSessionId: () => 'test-session-id',
getUsageStatisticsEnabled: () => true,
getDebugMode: () => false,
getSessionId: () => 'test-session-id',
getAllowedTools: vi.fn(() => []),
getContentGeneratorConfig: () => ({
model: 'test-model',
authType: 'oauth-personal',
}),
};
} as unknown as Config;
class MockToolInvocation extends BaseToolInvocation<object, ToolResult> {
constructor(
@ -194,7 +196,6 @@ describe('useReactToolScheduler in YOLO Mode', () => {
(mockToolRequiresConfirmation.execute as Mock).mockResolvedValue({
llmContent: expectedOutput,
returnDisplay: 'YOLO Formatted tool output',
summary: 'YOLO summary',
} as ToolResult);
const { result } = renderSchedulerInYoloMode();
@ -219,11 +220,6 @@ describe('useReactToolScheduler in YOLO Mode', () => {
await vi.runAllTimersAsync(); // Process execution
});
// Check that shouldConfirmExecute was NOT called
expect(
mockToolRequiresConfirmation.shouldConfirmExecute,
).not.toHaveBeenCalled();
// Check that execute WAS called
expect(mockToolRequiresConfirmation.execute).toHaveBeenCalledWith(
request.args,
@ -240,13 +236,15 @@ describe('useReactToolScheduler in YOLO Mode', () => {
request,
response: expect.objectContaining({
resultDisplay: 'YOLO Formatted tool output',
responseParts: {
functionResponse: {
id: 'yoloCall',
name: 'mockToolRequiresConfirmation',
response: { output: expectedOutput },
responseParts: [
{
functionResponse: {
id: 'yoloCall',
name: 'mockToolRequiresConfirmation',
response: { output: expectedOutput },
},
},
},
],
}),
}),
]);
@ -352,7 +350,6 @@ describe('useReactToolScheduler', () => {
(mockTool.execute as Mock).mockResolvedValue({
llmContent: 'Tool output',
returnDisplay: 'Formatted tool output',
summary: 'Formatted summary',
} as ToolResult);
(mockTool.shouldConfirmExecute as Mock).mockResolvedValue(null);
@ -390,13 +387,15 @@ describe('useReactToolScheduler', () => {
request,
response: expect.objectContaining({
resultDisplay: 'Formatted tool output',
responseParts: {
functionResponse: {
id: 'call1',
name: 'mockTool',
response: { output: 'Tool output' },
responseParts: [
{
functionResponse: {
id: 'call1',
name: 'mockTool',
response: { output: 'Tool output' },
},
},
},
],
}),
}),
]);
@ -429,11 +428,17 @@ describe('useReactToolScheduler', () => {
request,
response: expect.objectContaining({
error: expect.objectContaining({
message: 'Tool "nonexistentTool" not found in registry.',
message: expect.stringMatching(
/Tool "nonexistentTool" not found in registry/,
),
}),
}),
}),
]);
const errorMessage = onComplete.mock.calls[0][0][0].response.error.message;
expect(errorMessage).toContain('Did you mean one of:');
expect(errorMessage).toContain('"mockTool"');
expect(errorMessage).toContain('"anotherTool"');
expect(result.current[0]).toEqual([]);
});
@ -517,7 +522,6 @@ describe('useReactToolScheduler', () => {
(mockToolRequiresConfirmation.execute as Mock).mockResolvedValue({
llmContent: expectedOutput,
returnDisplay: 'Confirmed display',
summary: 'Confirmed summary',
} as ToolResult);
const { result } = renderScheduler();
@ -684,7 +688,6 @@ describe('useReactToolScheduler', () => {
resolveExecutePromise({
llmContent: 'Final output',
returnDisplay: 'Final display',
summary: 'Final summary',
} as ToolResult);
});
await act(async () => {
@ -718,7 +721,6 @@ describe('useReactToolScheduler', () => {
tool1.execute.mockResolvedValue({
llmContent: 'Output 1',
returnDisplay: 'Display 1',
summary: 'Summary 1',
} as ToolResult);
tool1.shouldConfirmExecute.mockResolvedValue(null);
@ -726,7 +728,6 @@ describe('useReactToolScheduler', () => {
tool2.execute.mockResolvedValue({
llmContent: 'Output 2',
returnDisplay: 'Display 2',
summary: 'Summary 2',
} as ToolResult);
tool2.shouldConfirmExecute.mockResolvedValue(null);
@ -775,13 +776,15 @@ describe('useReactToolScheduler', () => {
request: requests[0],
response: expect.objectContaining({
resultDisplay: 'Display 1',
responseParts: {
functionResponse: {
id: 'multi1',
name: 'tool1',
response: { output: 'Output 1' },
responseParts: [
{
functionResponse: {
id: 'multi1',
name: 'tool1',
response: { output: 'Output 1' },
},
},
},
],
}),
});
expect(call2Result).toMatchObject({
@ -789,13 +792,15 @@ describe('useReactToolScheduler', () => {
request: requests[1],
response: expect.objectContaining({
resultDisplay: 'Display 2',
responseParts: {
functionResponse: {
id: 'multi2',
name: 'tool2',
response: { output: 'Output 2' },
responseParts: [
{
functionResponse: {
id: 'multi2',
name: 'tool2',
response: { output: 'Output 2' },
},
},
},
],
}),
});
expect(result.current[0]).toEqual([]);
@ -809,7 +814,6 @@ describe('useReactToolScheduler', () => {
resolve({
llmContent: 'done',
returnDisplay: 'done display',
summary: 'done summary',
}),
50,
),