diff --git a/package.json b/package.json index 965dc4513..1f5b43771 100644 --- a/package.json +++ b/package.json @@ -153,7 +153,7 @@ "oxlint": "^1.28.0", "picocolors": "^1.1.1", "pinia": "^3.0.4", - "prettier": "^3.6.2", + "prettier": "^3.7.1", "reka-ui": "^2.5.1", "simple-git-hooks": "^2.13.1", "stream-monaco": "^0.0.7", diff --git a/src/main/presenter/configPresenter/acpConfHelper.ts b/src/main/presenter/configPresenter/acpConfHelper.ts index f1b24edaf..246bfb6c0 100644 --- a/src/main/presenter/configPresenter/acpConfHelper.ts +++ b/src/main/presenter/configPresenter/acpConfHelper.ts @@ -24,8 +24,8 @@ const BUILTIN_TEMPLATES: Record = { name: 'Kimi CLI', defaultProfile: () => ({ name: DEFAULT_PROFILE_NAME, - command: 'uv', - args: ['tool', 'run', '--from', 'kimi-cli', 'kimi', '--acp'], + command: 'kimi', + args: ['--acp'], env: {} }) }, diff --git a/src/main/presenter/configPresenter/acpInitHelper.ts b/src/main/presenter/configPresenter/acpInitHelper.ts index e29f26907..94b6d2b32 100644 --- a/src/main/presenter/configPresenter/acpInitHelper.ts +++ b/src/main/presenter/configPresenter/acpInitHelper.ts @@ -52,7 +52,7 @@ const EXTERNAL_DEPENDENCIES: ExternalDependency[] = [ const BUILTIN_INIT_COMMANDS: Record = { 'kimi-cli': { - commands: ['uv tool run --from kimi-cli kimi'], + commands: ['uv tool install --python 3.13 kimi-cli', 'kimi'], description: 'Initialize Kimi CLI' }, 'claude-code-acp': { diff --git a/src/main/presenter/llmProviderPresenter/agent/acpProcessManager.ts b/src/main/presenter/llmProviderPresenter/agent/acpProcessManager.ts index b9d6b07d6..d51195f99 100644 --- a/src/main/presenter/llmProviderPresenter/agent/acpProcessManager.ts +++ b/src/main/presenter/llmProviderPresenter/agent/acpProcessManager.ts @@ -174,11 +174,51 @@ export class AcpProcessManager implements AgentProcessManager client, stream) - await connection.initialize({ - protocolVersion: PROTOCOL_VERSION, - clientCapabilities: {}, - clientInfo: { name: 'DeepChat', version: app.getVersion() } - }) + // Add process health check before initialization + if (child.killed) { + throw new Error( + `[ACP] Agent process ${agent.id} exited before initialization (PID: ${child.pid})` + ) + } + + // Initialize connection with timeout and error handling + console.info(`[ACP] Starting connection initialization for agent ${agent.id}`) + const timeoutMs = 60 * 1000 * 5 // 5 minutes timeout for initialization + + try { + const initPromise = connection.initialize({ + protocolVersion: PROTOCOL_VERSION, + clientCapabilities: {}, + clientInfo: { name: 'DeepChat', version: app.getVersion() } + }) + + const timeoutPromise = new Promise((_, reject) => { + setTimeout(() => { + reject( + new Error( + `[ACP] Connection initialization timeout after ${timeoutMs}ms for agent ${agent.id}` + ) + ) + }, timeoutMs) + }) + + await Promise.race([initPromise, timeoutPromise]) + console.info(`[ACP] Connection initialization completed successfully for agent ${agent.id}`) + } catch (error) { + console.error(`[ACP] Connection initialization failed for agent ${agent.id}:`, error) + + // Clean up the child process if initialization failed + if (!child.killed) { + try { + child.kill() + console.info(`[ACP] Killed process for failed agent ${agent.id} (PID: ${child.pid})`) + } catch (killError) { + console.warn(`[ACP] Failed to kill process for agent ${agent.id}:`, killError) + } + } + + throw error + } const handle: AcpProcessHandle = { providerId: this.providerId, @@ -196,7 +236,7 @@ export class AcpProcessManager implements AgentProcessManager { console.warn( - `[ACP] Agent process for ${agent.id} exited (code=${code ?? 'null'}, signal=${signal ?? 'null'})` + `[ACP] Agent process for ${agent.id} exited (PID: ${child.pid}, code=${code ?? 'null'}, signal=${signal ?? 'null'})` ) if (this.handles.get(agent.id)?.child === child) { this.handles.delete(agent.id) @@ -204,10 +244,27 @@ export class AcpProcessManager implements AgentProcessManager { + const output = chunk.toString().trim() + if (output) { + console.info(`[ACP] ${agent.id} stdout: ${output}`) + } + }) + child.stderr?.on('data', (chunk: Buffer) => { - console.warn(`[ACP] ${agent.id} stderr: ${chunk.toString()}`) + const error = chunk.toString().trim() + if (error) { + console.error(`[ACP] ${agent.id} stderr: ${error}`) + } }) + // Add additional process monitoring + child.on('error', (error) => { + console.error(`[ACP] Agent process ${agent.id} encountered error:`, error) + }) + + console.info(`[ACP] Process monitoring set up for agent ${agent.id} (PID: ${child.pid})`) + return handle } diff --git a/src/main/presenter/llmProviderPresenter/providers/openAICompatibleProvider.ts b/src/main/presenter/llmProviderPresenter/providers/openAICompatibleProvider.ts index ca99a3f6e..4e0e6ecc3 100644 --- a/src/main/presenter/llmProviderPresenter/providers/openAICompatibleProvider.ts +++ b/src/main/presenter/llmProviderPresenter/providers/openAICompatibleProvider.ts @@ -1723,7 +1723,8 @@ export class OpenAICompatibleProvider extends BaseLLMProvider { .filter( ( call - ): call is { id: string; type: string; function: { name: string; arguments: string } } => // Type guard ensures correct structure + ): call is { id: string; type: string; function: { name: string; arguments: string } } => + // Type guard ensures correct structure call !== null && typeof call.id === 'string' && typeof call.function === 'object' && diff --git a/src/renderer/src/components/editor/mention/MentionList.vue b/src/renderer/src/components/editor/mention/MentionList.vue index 461a2cb9c..2811fb1d6 100644 --- a/src/renderer/src/components/editor/mention/MentionList.vue +++ b/src/renderer/src/components/editor/mention/MentionList.vue @@ -87,10 +87,10 @@ const hasFiles = (item: CategorizedData): boolean => { // 类型保护:检查是否是 PromptListEntry 并且有 files 字段 return Boolean( mcpEntry && - 'files' in mcpEntry && - mcpEntry.files && - Array.isArray(mcpEntry.files) && - mcpEntry.files.length > 0 + 'files' in mcpEntry && + mcpEntry.files && + Array.isArray(mcpEntry.files) && + mcpEntry.files.length > 0 ) } diff --git a/src/renderer/src/composables/useIpcQuery.ts b/src/renderer/src/composables/useIpcQuery.ts index cd0c933cc..cab6d93e0 100644 --- a/src/renderer/src/composables/useIpcQuery.ts +++ b/src/renderer/src/composables/useIpcQuery.ts @@ -19,9 +19,9 @@ export interface UseIpcQueryOptions< TName extends PresenterName, TMethod extends PresenterMethod > extends Pick< - UseQueryOptions>>>, - QueryOptionKeys - > { + UseQueryOptions>>>, + QueryOptionKeys +> { key: () => EntryKey presenter: TName method: TMethod diff --git a/test/main/presenter/FileValidationService.test.ts b/test/main/presenter/FileValidationService.test.ts index c31f00bd1..3413f46b5 100644 --- a/test/main/presenter/FileValidationService.test.ts +++ b/test/main/presenter/FileValidationService.test.ts @@ -346,9 +346,8 @@ describe('FileValidationService', () => { vi.clearAllMocks() // Import and use the real function directly - const { getMimeTypeAdapterMap: realGetMimeTypeAdapterMap } = await import( - '../../../src/main/presenter/filePresenter/mime' - ) + const { getMimeTypeAdapterMap: realGetMimeTypeAdapterMap } = + await import('../../../src/main/presenter/filePresenter/mime') // Mock with real implementation vi.mocked(getMimeTypeAdapterMap).mockImplementation(realGetMimeTypeAdapterMap)