Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
4 changes: 2 additions & 2 deletions src/main/presenter/configPresenter/acpConfHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ const BUILTIN_TEMPLATES: Record<AcpBuiltinAgentId, BuiltinTemplate> = {
name: 'Kimi CLI',
defaultProfile: () => ({
name: DEFAULT_PROFILE_NAME,
command: 'uv',
args: ['tool', 'run', '--from', 'kimi-cli', 'kimi', '--acp'],
command: 'kimi',
args: ['--acp'],
env: {}
})
},
Expand Down
2 changes: 1 addition & 1 deletion src/main/presenter/configPresenter/acpInitHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ const EXTERNAL_DEPENDENCIES: ExternalDependency[] = [

const BUILTIN_INIT_COMMANDS: Record<AcpBuiltinAgentId, InitCommandConfig> = {
'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': {
Expand Down
71 changes: 64 additions & 7 deletions src/main/presenter/llmProviderPresenter/agent/acpProcessManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -174,11 +174,51 @@ export class AcpProcessManager implements AgentProcessManager<AcpProcessHandle,
const client = this.createClientProxy()
const connection = new ClientSideConnection(() => 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<never>((_, 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,
Expand All @@ -196,18 +236,35 @@ export class AcpProcessManager implements AgentProcessManager<AcpProcessHandle,

child.on('exit', (code, signal) => {
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)
}
this.clearSessionsForAgent(agent.id)
})

child.stdout?.on('data', (chunk: Buffer) => {
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
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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' &&
Expand Down
8 changes: 4 additions & 4 deletions src/renderer/src/components/editor/mention/MentionList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -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
)
}

Expand Down
6 changes: 3 additions & 3 deletions src/renderer/src/composables/useIpcQuery.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ export interface UseIpcQueryOptions<
TName extends PresenterName,
TMethod extends PresenterMethod<TName>
> extends Pick<
UseQueryOptions<Awaited<ReturnType<PresenterMethodFn<TName, TMethod>>>>,
QueryOptionKeys
> {
UseQueryOptions<Awaited<ReturnType<PresenterMethodFn<TName, TMethod>>>>,
QueryOptionKeys
> {
key: () => EntryKey
presenter: TName
method: TMethod
Expand Down
5 changes: 2 additions & 3 deletions test/main/presenter/FileValidationService.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down