mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-05-05 23:42:03 +00:00
Merge branch 'main' into feat/support-permission
This commit is contained in:
commit
b59864f554
57 changed files with 14025 additions and 619 deletions
|
|
@ -36,6 +36,8 @@ import { RipGrepTool } from '../tools/ripGrep.js';
|
|||
import { logRipgrepFallback } from '../telemetry/loggers.js';
|
||||
import { RipgrepFallbackEvent } from '../telemetry/types.js';
|
||||
import { ToolRegistry } from '../tools/tool-registry.js';
|
||||
import { fireNotificationHook } from '../core/toolHookTriggers.js';
|
||||
import type { MessageBus } from '../confirmation-bus/message-bus.js';
|
||||
|
||||
function createToolMock(toolName: string) {
|
||||
const ToolMock = vi.fn();
|
||||
|
|
@ -195,6 +197,10 @@ vi.mock('../ide/ide-client.js', () => ({
|
|||
import { BaseLlmClient } from '../core/baseLlmClient.js';
|
||||
|
||||
vi.mock('../core/baseLlmClient.js');
|
||||
// Mock fireNotificationHook from toolHookTriggers
|
||||
vi.mock('../core/toolHookTriggers.js', () => ({
|
||||
fireNotificationHook: vi.fn().mockResolvedValue({}),
|
||||
}));
|
||||
|
||||
describe('Server Config (config.ts)', () => {
|
||||
const MODEL = 'qwen3-coder-plus';
|
||||
|
|
@ -337,6 +343,64 @@ describe('Server Config (config.ts)', () => {
|
|||
expect(GeminiClient).toHaveBeenCalledWith(config);
|
||||
});
|
||||
|
||||
it('should fire auth_success notification hook when hooks are enabled', async () => {
|
||||
const mockMessageBus = { request: vi.fn() };
|
||||
const config = new Config({
|
||||
...baseParams,
|
||||
enableHooks: true,
|
||||
});
|
||||
// Set messageBus using the setter
|
||||
config.setMessageBus(mockMessageBus as unknown as MessageBus);
|
||||
|
||||
const authType = AuthType.USE_GEMINI;
|
||||
const mockContentConfig = {
|
||||
apiKey: 'test-key',
|
||||
model: 'qwen3-coder-plus',
|
||||
authType,
|
||||
};
|
||||
|
||||
vi.mocked(resolveContentGeneratorConfigWithSources).mockReturnValue({
|
||||
config: mockContentConfig as ContentGeneratorConfig,
|
||||
sources: {},
|
||||
});
|
||||
|
||||
await config.refreshAuth(authType);
|
||||
|
||||
// Verify that fireNotificationHook was called with correct parameters
|
||||
expect(fireNotificationHook).toHaveBeenCalledWith(
|
||||
mockMessageBus,
|
||||
`Successfully authenticated with ${authType}`,
|
||||
'auth_success',
|
||||
'Authentication successful',
|
||||
);
|
||||
});
|
||||
|
||||
it('should not fire notification hook when hooks are disabled', async () => {
|
||||
const config = new Config({
|
||||
...baseParams,
|
||||
enableHooks: false,
|
||||
});
|
||||
const authType = AuthType.USE_GEMINI;
|
||||
const mockContentConfig = {
|
||||
apiKey: 'test-key',
|
||||
model: 'qwen3-coder-plus',
|
||||
authType,
|
||||
};
|
||||
|
||||
vi.mocked(resolveContentGeneratorConfigWithSources).mockReturnValue({
|
||||
config: mockContentConfig as ContentGeneratorConfig,
|
||||
sources: {},
|
||||
});
|
||||
|
||||
// Clear any previous calls
|
||||
vi.mocked(fireNotificationHook).mockClear();
|
||||
|
||||
await config.refreshAuth(authType);
|
||||
|
||||
// Verify that fireNotificationHook was not called
|
||||
expect(fireNotificationHook).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should not strip thoughts when switching from Vertex to GenAI', async () => {
|
||||
const config = new Config(baseParams);
|
||||
|
||||
|
|
|
|||
|
|
@ -94,6 +94,12 @@ import {
|
|||
type HookExecutionRequest,
|
||||
type HookExecutionResponse,
|
||||
} from '../confirmation-bus/types.js';
|
||||
import {
|
||||
PermissionMode,
|
||||
NotificationType,
|
||||
type PermissionSuggestion,
|
||||
} from '../hooks/types.js';
|
||||
import { fireNotificationHook } from '../core/toolHookTriggers.js';
|
||||
|
||||
// Utils
|
||||
import { shouldAttemptBrowserLaunch } from '../utils/browser.js';
|
||||
|
|
@ -819,6 +825,73 @@ export class Config {
|
|||
(input['last_assistant_message'] as string) || '',
|
||||
);
|
||||
break;
|
||||
case 'PreToolUse': {
|
||||
result = await hookSystem.firePreToolUseEvent(
|
||||
(input['tool_name'] as string) || '',
|
||||
(input['tool_input'] as Record<string, unknown>) || {},
|
||||
(input['tool_use_id'] as string) || '',
|
||||
(input['permission_mode'] as PermissionMode | undefined) ??
|
||||
PermissionMode.Default,
|
||||
);
|
||||
break;
|
||||
}
|
||||
case 'PostToolUse':
|
||||
result = await hookSystem.firePostToolUseEvent(
|
||||
(input['tool_name'] as string) || '',
|
||||
(input['tool_input'] as Record<string, unknown>) || {},
|
||||
(input['tool_response'] as Record<string, unknown>) || {},
|
||||
(input['tool_use_id'] as string) || '',
|
||||
(input['permission_mode'] as PermissionMode) || 'default',
|
||||
);
|
||||
break;
|
||||
case 'PostToolUseFailure':
|
||||
result = await hookSystem.firePostToolUseFailureEvent(
|
||||
(input['tool_use_id'] as string) || '',
|
||||
(input['tool_name'] as string) || '',
|
||||
(input['tool_input'] as Record<string, unknown>) || {},
|
||||
(input['error'] as string) || '',
|
||||
input['is_interrupt'] as boolean | undefined,
|
||||
(input['permission_mode'] as PermissionMode) || 'default',
|
||||
);
|
||||
break;
|
||||
case 'Notification':
|
||||
result = await hookSystem.fireNotificationEvent(
|
||||
(input['message'] as string) || '',
|
||||
(input['notification_type'] as NotificationType) ||
|
||||
'permission_prompt',
|
||||
(input['title'] as string) || undefined,
|
||||
);
|
||||
break;
|
||||
case 'PermissionRequest':
|
||||
result = await hookSystem.firePermissionRequestEvent(
|
||||
(input['tool_name'] as string) || '',
|
||||
(input['tool_input'] as Record<string, unknown>) || {},
|
||||
(input['permission_mode'] as PermissionMode) ||
|
||||
PermissionMode.Default,
|
||||
(input['permission_suggestions'] as
|
||||
| PermissionSuggestion[]
|
||||
| undefined) || undefined,
|
||||
);
|
||||
break;
|
||||
case 'SubagentStart':
|
||||
result = await hookSystem.fireSubagentStartEvent(
|
||||
(input['agent_id'] as string) || '',
|
||||
(input['agent_type'] as string) || '',
|
||||
(input['permission_mode'] as PermissionMode) ||
|
||||
PermissionMode.Default,
|
||||
);
|
||||
break;
|
||||
case 'SubagentStop':
|
||||
result = await hookSystem.fireSubagentStopEvent(
|
||||
(input['agent_id'] as string) || '',
|
||||
(input['agent_type'] as string) || '',
|
||||
(input['agent_transcript_path'] as string) || '',
|
||||
(input['last_assistant_message'] as string) || '',
|
||||
(input['stop_hook_active'] as boolean) || false,
|
||||
(input['permission_mode'] as PermissionMode) ||
|
||||
PermissionMode.Default,
|
||||
);
|
||||
break;
|
||||
default:
|
||||
this.debugLogger.warn(
|
||||
`Unknown hook event: ${request.eventName}`,
|
||||
|
|
@ -846,6 +919,8 @@ export class Config {
|
|||
);
|
||||
|
||||
this.debugLogger.debug('MessageBus initialized with hook subscription');
|
||||
} else {
|
||||
this.debugLogger.debug('Hook system disabled, skipping initialization');
|
||||
}
|
||||
|
||||
this.subagentManager = new SubagentManager(this);
|
||||
|
|
@ -973,6 +1048,21 @@ export class Config {
|
|||
|
||||
// Initialize BaseLlmClient now that the ContentGenerator is available
|
||||
this.baseLlmClient = new BaseLlmClient(this.contentGenerator, this);
|
||||
|
||||
// Fire auth_success notification hook (supports both interactive & non-interactive)
|
||||
const messageBus = this.getMessageBus();
|
||||
const hooksEnabled = this.getEnableHooks();
|
||||
if (hooksEnabled && messageBus) {
|
||||
fireNotificationHook(
|
||||
messageBus,
|
||||
`Successfully authenticated with ${authMethod}`,
|
||||
NotificationType.AuthSuccess,
|
||||
'Authentication successful',
|
||||
).catch(() => {
|
||||
// Silently ignore errors - fireNotificationHook has internal error handling
|
||||
// and notification hooks should not block the auth flow
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue