From 63817d30dc286c6476736b30e7567656a2a712b9 Mon Sep 17 00:00:00 2001 From: xiaomo Date: Fri, 16 Jan 2026 14:17:26 +0800 Subject: [PATCH 01/16] chore: typecheck with tsgo (#1278) --- .vscode/settings.json | 4 +++- package.json | 3 ++- tsconfig.node.json | 8 +++++--- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 77acfbec6..a476674e2 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -5,5 +5,7 @@ "i18n-ally.keystyle": "nested", "i18n-ally.sourceLanguage": "zh-CN", "i18n-ally.namespace": true, - "i18n-ally.pathMatcher": "{locale}/{namespaces}.json" + "i18n-ally.pathMatcher": "{locale}/{namespaces}.json", + "unocss.disable": true, + "typescript.experimental.useTsgo": true } diff --git a/package.json b/package.json index 7304a162e..c2f359b8e 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,7 @@ "format:check": "prettier --check .", "format": "prettier --cache --write .", "lint": "oxlint .", - "typecheck:node": "tsc --noEmit -p tsconfig.node.json --composite false", + "typecheck:node": "tsgo --noEmit -p tsconfig.node.json --composite false", "typecheck:web": "vue-tsc --noEmit -p tsconfig.app.json --composite false", "typecheck": "pnpm run typecheck:node && pnpm run typecheck:web", "start": "electron-vite preview", @@ -132,6 +132,7 @@ "@types/mime-types": "^3.0.1", "@types/node": "^22.19.3", "@types/xlsx": "^0.0.35", + "@typescript/native-preview": "7.0.0-dev.20260115.1", "@vee-validate/zod": "^4.15.1", "@vitejs/plugin-vue": "^6.0.3", "@vitest/ui": "^3.2.4", diff --git a/tsconfig.node.json b/tsconfig.node.json index dc6f521a1..eea19869f 100644 --- a/tsconfig.node.json +++ b/tsconfig.node.json @@ -11,13 +11,15 @@ "resolveJsonModule": false, "types": ["electron-vite/node"], "moduleResolution": "bundler", - "baseUrl": ".", "paths": { + "*": [ + "./*" + ], "@/*": [ - "src/main/*" + "./src/main/*" ], "@shared/*": [ - "src/shared/*" + "./src/shared/*" ] } } From c0de19ee5d85f41c3b9006fc069d34afd7b6e5e2 Mon Sep 17 00:00:00 2001 From: wanna Date: Sat, 17 Jan 2026 10:56:34 +0800 Subject: [PATCH 02/16] feat: image left align --- src/renderer/src/components/message/MessageBlockImage.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/renderer/src/components/message/MessageBlockImage.vue b/src/renderer/src/components/message/MessageBlockImage.vue index 1025993ca..0958cd25d 100644 --- a/src/renderer/src/components/message/MessageBlockImage.vue +++ b/src/renderer/src/components/message/MessageBlockImage.vue @@ -1,6 +1,6 @@ + { | 'permission-granted' | 'permission-denied' | 'continue' + | 'question-required' permission_request?: { toolName: string serverName: string @@ -927,6 +929,8 @@ export const useChatStore = defineStore('chat', () => { }> rememberable?: boolean } + question_request?: QuestionInfo + question_error?: string totalUsage?: { prompt_tokens: number completion_tokens: number @@ -1031,8 +1035,53 @@ export const useChatStore = defineStore('chat', () => { needContinue: true } }) + } else if (msg.question_error) { + const normalizedQuestionError = msg.question_error.trim() + const questionErrorContent = + normalizedQuestionError === 'Invalid question request' + ? 'common.error.invalidQuestionRequest' + : msg.question_error + + finalizeAssistantMessageBlocks(assistantMsg.content) + assistantMsg.content.push({ + type: 'error', + content: questionErrorContent, + status: 'error', + timestamp: Date.now() + }) } else if (msg.tool_call) { - if (msg.tool_call === 'permission-required') { + if (msg.tool_call === 'question-required') { + const payload = msg.question_request + if (payload) { + finalizeAssistantMessageBlocks(assistantMsg.content) + assistantMsg.content.push({ + type: 'action', + content: '', + status: 'pending', + timestamp: Date.now(), + action_type: 'question_request', + tool_call: { + id: msg.tool_call_id, + name: msg.tool_call_name, + params: msg.tool_call_params || '', + server_name: msg.tool_call_server_name, + server_icons: msg.tool_call_server_icons, + server_description: msg.tool_call_server_description + }, + extra: { + needsUserAction: true, + questionHeader: payload.header ?? '', + questionText: payload.question, + questionOptions: payload.options, + questionMultiple: Boolean(payload.multiple), + questionCustom: payload.custom !== false, + questionResolution: 'asked' + } + }) + } + } else if (msg.tool_call_name === 'question') { + // Ignore question tool call lifecycle events (handled by question-required) + } else if (msg.tool_call === 'permission-required') { finalizeAssistantMessageBlocks(assistantMsg.content) const permissionRequest = msg.permission_request const permissionExtra: Record = { diff --git a/src/shared/chat.d.ts b/src/shared/chat.d.ts index 8ba383299..c85310214 100644 --- a/src/shared/chat.d.ts +++ b/src/shared/chat.d.ts @@ -136,7 +136,11 @@ export type AssistantMessageBlock = { server_icons?: string server_description?: string } - action_type?: 'tool_call_permission' | 'maximum_tool_calls_reached' | 'rate_limit' + action_type?: + | 'tool_call_permission' + | 'maximum_tool_calls_reached' + | 'rate_limit' + | 'question_request' image_data?: { data: string mimeType: string @@ -170,6 +174,19 @@ export type AssistantMessageExtra = Record + | string + questionMultiple?: boolean + questionCustom?: boolean + questionResolution?: 'asked' | 'replied' | 'rejected' + answerText?: string + answerMessageId?: string } // Search-related message block types export type SearchBlock = { diff --git a/src/shared/chat/messageBlocks.ts b/src/shared/chat/messageBlocks.ts index b0a56ee59..30968285e 100644 --- a/src/shared/chat/messageBlocks.ts +++ b/src/shared/chat/messageBlocks.ts @@ -11,7 +11,11 @@ export function finalizeAssistantMessageBlocks(blocks: AssistantMessageBlock[] | return } - if (lastBlock.type === 'action' && lastBlock.action_type === 'tool_call_permission') { + if ( + lastBlock.type === 'action' && + (lastBlock.action_type === 'tool_call_permission' || + lastBlock.action_type === 'question_request') + ) { return } diff --git a/src/shared/types/core/agent-events.ts b/src/shared/types/core/agent-events.ts index 8f5499070..890c329fd 100644 --- a/src/shared/types/core/agent-events.ts +++ b/src/shared/types/core/agent-events.ts @@ -1,4 +1,5 @@ import type { PermissionRequestOption } from './llm-events' +import type { QuestionInfo } from './question' import type { UsageStats, RateLimitInfo } from './usage' export interface LLMAgentEventData { @@ -30,6 +31,7 @@ export interface LLMAgentEventData { | 'permission-granted' | 'permission-denied' | 'continue' + | 'question-required' permission_request?: { toolName: string serverName: string @@ -53,6 +55,8 @@ export interface LLMAgentEventData { options?: PermissionRequestOption[] rememberable?: boolean } + question_request?: QuestionInfo + question_error?: string totalUsage?: UsageStats image_data?: { data: string; mimeType: string } rate_limit?: RateLimitInfo diff --git a/src/shared/types/core/chat.ts b/src/shared/types/core/chat.ts index 7066100d0..a7cc5edac 100644 --- a/src/shared/types/core/chat.ts +++ b/src/shared/types/core/chat.ts @@ -99,7 +99,11 @@ export type AssistantMessageBlock = { server_icons?: string server_description?: string } - action_type?: 'tool_call_permission' | 'maximum_tool_calls_reached' | 'rate_limit' + action_type?: + | 'tool_call_permission' + | 'maximum_tool_calls_reached' + | 'rate_limit' + | 'question_request' image_data?: { data: string; mimeType: string } reasoning_time?: { start: number; end: number } } diff --git a/src/shared/types/core/question.ts b/src/shared/types/core/question.ts new file mode 100644 index 000000000..82fdd5686 --- /dev/null +++ b/src/shared/types/core/question.ts @@ -0,0 +1,19 @@ +export type QuestionOption = { + label: string + description?: string +} + +export type QuestionInfo = { + header?: string + question: string + options: QuestionOption[] + multiple?: boolean + custom?: boolean +} + +export type QuestionResolution = 'asked' | 'replied' | 'rejected' + +export type QuestionAnswer = { + answerText: string + answerMessageId?: string +} diff --git a/src/shared/types/presenters/agent.presenter.d.ts b/src/shared/types/presenters/agent.presenter.d.ts index a06ed7873..2615ac87c 100644 --- a/src/shared/types/presenters/agent.presenter.d.ts +++ b/src/shared/types/presenters/agent.presenter.d.ts @@ -31,5 +31,12 @@ export interface IAgentPresenter { permissionType: 'read' | 'write' | 'all' | 'command', remember?: boolean ): Promise + resolveQuestion( + messageId: string, + toolCallId: string, + answerText: string, + answerMessageId?: string + ): Promise + rejectQuestion(messageId: string, toolCallId: string): Promise getMessageRequestPreview(agentId: string, messageId?: string): Promise } diff --git a/src/shared/types/presenters/legacy.presenters.d.ts b/src/shared/types/presenters/legacy.presenters.d.ts index 5fe9c6a3a..0ee90004e 100644 --- a/src/shared/types/presenters/legacy.presenters.d.ts +++ b/src/shared/types/presenters/legacy.presenters.d.ts @@ -385,6 +385,7 @@ export interface ISQLitePresenter { ): Promise getMessageAttachments(messageId: string, type: string): Promise<{ content: string }[]> getLastUserMessage(conversationId: string): Promise + getLastAssistantMessage(conversationId: string): Promise getMainMessageByParentId(conversationId: string, parentId: string): Promise deleteAllMessagesInConversation(conversationId: string): Promise getAcpSession(conversationId: string, agentId: string): Promise diff --git a/src/shared/types/presenters/session.presenter.d.ts b/src/shared/types/presenters/session.presenter.d.ts index b0ee26944..e43376ea0 100644 --- a/src/shared/types/presenters/session.presenter.d.ts +++ b/src/shared/types/presenters/session.presenter.d.ts @@ -8,7 +8,13 @@ import type { AcpWorkdirInfo } from './thread.presenter' -export type SessionStatus = 'idle' | 'generating' | 'paused' | 'waiting_permission' | 'error' +export type SessionStatus = + | 'idle' + | 'generating' + | 'paused' + | 'waiting_permission' + | 'waiting_question' + | 'error' export type SessionConfig = { sessionId: string @@ -111,6 +117,7 @@ export interface ISessionPresenter extends IThreadPresenter { markMessageAsContextEdge(messageId: string, isEdge: boolean): Promise getContextMessages(sessionId: string): Promise getLastUserMessage(sessionId: string): Promise + getLastAssistantMessage(sessionId: string): Promise forkSession( targetSessionId: string, diff --git a/src/types/i18n.d.ts b/src/types/i18n.d.ts index 53d3e47c6..d0903e964 100644 --- a/src/types/i18n.d.ts +++ b/src/types/i18n.d.ts @@ -376,6 +376,15 @@ declare module 'vue-i18n' { critical: string } } + messageBlockQuestionRequest: { + title: string + send: string + reject: string + selected: string + rejected: string + customPlaceholder: string + answerLabel: string + } promptParamsDialog: { title: string description: string diff --git a/test/main/presenter/agentPresenter/loop/toolCallProcessor.test.ts b/test/main/presenter/agentPresenter/loop/toolCallProcessor.test.ts index ad4995e4b..556ef1d53 100644 --- a/test/main/presenter/agentPresenter/loop/toolCallProcessor.test.ts +++ b/test/main/presenter/agentPresenter/loop/toolCallProcessor.test.ts @@ -94,3 +94,129 @@ describe('ToolCallProcessor tool output offload', () => { expect(toolMessage?.content).toContain('[Tool output offloaded]') }) }) + +describe('ToolCallProcessor question tool', () => { + const questionToolDef = { + type: 'function', + function: { + name: 'question', + description: 'question tool', + parameters: { + type: 'object', + properties: {} + } + }, + server: { + name: 'agent-core', + icons: '❓', + description: 'Agent core tools' + } + } as MCPToolDefinition + + const executeCommandDef = { + type: 'function', + function: { + name: 'execute_command', + description: 'execute command', + parameters: { + type: 'object', + properties: {} + } + }, + server: { + name: 'mock', + icons: '', + description: '' + } + } as MCPToolDefinition + + const basicQuestionArgs = JSON.stringify({ + question: 'Choose one', + options: [{ label: 'A' }, { label: 'B' }] + }) + + it('emits question-required and pauses the loop', async () => { + const callTool = vi.fn() + const processor = new ToolCallProcessor({ + getAllToolDefinitions: async () => [questionToolDef], + callTool + }) + + const conversationMessages: ChatMessage[] = [{ role: 'assistant', content: 'hello' }] + const iterator = processor.process({ + eventId: 'event-question-1', + toolCalls: [{ id: 'tool-q1', name: 'question', arguments: basicQuestionArgs }], + enabledMcpTools: [], + conversationMessages, + modelConfig: { functionCall: true } as ModelConfig, + abortSignal: new AbortController().signal, + currentToolCallCount: 0, + maxToolCalls: 5, + conversationId: 'conv-question' + }) + + const events: any[] = [] + let result: any = null + while (true) { + const { value, done } = await iterator.next() + if (done) { + result = value + break + } + events.push(value) + } + + const questionEvent = events.find( + (event) => event.type === 'response' && event.data?.tool_call === 'question-required' + ) + expect(questionEvent).toBeDefined() + expect(questionEvent.data.question_request?.question).toBe('Choose one') + expect(result.needContinueConversation).toBe(false) + expect(callTool).not.toHaveBeenCalled() + }) + + it('rejects non-standalone question tool calls', async () => { + const callTool = vi.fn(async () => ({ + content: 'ok', + rawData: { content: 'ok' } as MCPToolResponse + })) + const processor = new ToolCallProcessor({ + getAllToolDefinitions: async () => [questionToolDef, executeCommandDef], + callTool + }) + + const conversationMessages: ChatMessage[] = [{ role: 'assistant', content: 'hello' }] + const iterator = processor.process({ + eventId: 'event-question-2', + toolCalls: [ + { id: 'tool-q1', name: 'question', arguments: basicQuestionArgs }, + { id: 'tool-2', name: 'execute_command', arguments: '{}' } + ], + enabledMcpTools: [], + conversationMessages, + modelConfig: { functionCall: true } as ModelConfig, + abortSignal: new AbortController().signal, + currentToolCallCount: 0, + maxToolCalls: 5, + conversationId: 'conv-question' + }) + + const events: any[] = [] + let result: any = null + while (true) { + const { value, done } = await iterator.next() + if (done) { + result = value + break + } + events.push(value) + } + + const errorEvent = events.find( + (event) => event.type === 'response' && event.data?.question_error + ) + expect(errorEvent).toBeDefined() + expect(result.needContinueConversation).toBe(true) + expect(callTool).toHaveBeenCalled() + }) +}) diff --git a/test/renderer/message/eventMappingTable.test.ts b/test/renderer/message/eventMappingTable.test.ts index 9be5a7bd3..0b2ba84b2 100644 --- a/test/renderer/message/eventMappingTable.test.ts +++ b/test/renderer/message/eventMappingTable.test.ts @@ -151,6 +151,34 @@ const mappingTestCases: MappingTestCase[] = [ notes: '待用户授权,后续置 granted/denied' }, + // 问题请求 + { + name: 'response.question-required → action block (question_request)', + event: { + type: 'response', + data: { + eventId: 'test-123', + tool_call: 'question-required', + tool_call_id: 'tool-999', + tool_call_name: 'question', + question_request: { + question: 'Pick one', + options: [{ label: 'A' }] + } + } + }, + expectedBlock: { + type: 'action', + action_type: 'question_request', + status: 'pending', + tool_call: { + id: 'tool-999', + name: 'question' + } + }, + notes: '等待用户选择或输入' + }, + // 速率限制 { name: 'response.rate_limit → action block (rate_limit)', @@ -245,6 +273,7 @@ const statusTransitionCases = [ { from: 'loading', to: 'error', valid: true }, { from: 'pending', to: 'granted', valid: true }, { from: 'pending', to: 'denied', valid: true }, + { from: 'pending', to: 'success', valid: true }, { from: 'pending', to: 'error', valid: true }, { from: 'success', to: 'loading', valid: false }, { from: 'error', to: 'success', valid: false }, @@ -328,7 +357,12 @@ describe('Event-to-UI Mapping Table Contract Tests', () => { }) it('should only allow defined action_type values', () => { - const validActionTypes = ['tool_call_permission', 'maximum_tool_calls_reached', 'rate_limit'] + const validActionTypes = [ + 'tool_call_permission', + 'maximum_tool_calls_reached', + 'rate_limit', + 'question_request' + ] mappingTestCases.forEach((testCase) => { if (testCase.expectedBlock?.action_type) { @@ -552,6 +586,19 @@ function mapEventToBlock(event: LLMAgentEvent): AssistantMessageBlock { } } } + + if (data.tool_call === 'question-required') { + return { + type: 'action', + action_type: 'question_request', + status: 'pending', + timestamp, + tool_call: { + id: data.tool_call_id, + name: data.tool_call_name + } + } + } } if (data.rate_limit) { @@ -583,7 +630,7 @@ function isValidStatusTransition( ): boolean { const validTransitions: Record = { loading: ['success', 'error'], - pending: ['granted', 'denied', 'error'], + pending: ['granted', 'denied', 'success', 'error'], success: [], error: [], granted: [], @@ -599,7 +646,10 @@ function processEndEvent( ): AssistantMessageBlock[] { return blocks.map((block) => { // Preserve permission blocks - if (block.type === 'action' && block.action_type === 'tool_call_permission') { + if ( + block.type === 'action' && + (block.action_type === 'tool_call_permission' || block.action_type === 'question_request') + ) { return block } diff --git a/test/renderer/message/messageBlockSnapshot.test.ts b/test/renderer/message/messageBlockSnapshot.test.ts index 45ee6e290..62838531d 100644 --- a/test/renderer/message/messageBlockSnapshot.test.ts +++ b/test/renderer/message/messageBlockSnapshot.test.ts @@ -288,7 +288,7 @@ function isValidStatusTransition( ): boolean { const validTransitions: Record = { loading: ['success', 'error'], - pending: ['granted', 'denied', 'error'], + pending: ['granted', 'denied', 'success', 'error'], success: [], error: [], granted: [], diff --git a/test/renderer/message/rendererContract.test.ts b/test/renderer/message/rendererContract.test.ts index 28151110d..10b72e7bc 100644 --- a/test/renderer/message/rendererContract.test.ts +++ b/test/renderer/message/rendererContract.test.ts @@ -133,6 +133,35 @@ describe('Renderer Contract Tests', () => { expect(mapEventToBlock(agentEvent)).toEqual(expectedBlock) }) + it('should map question request correctly', () => { + const agentEvent: LLMAgentEvent = { + type: 'response', + data: { + eventId: 'test-123', + tool_call: 'question-required', + tool_call_id: 'tool-999', + tool_call_name: 'question', + question_request: { + question: 'Pick one', + options: [{ label: 'A' }] + } + } + } + + const expectedBlock: AssistantMessageBlock = { + type: 'action', + action_type: 'question_request', + status: 'pending', + timestamp: expect.any(Number), + tool_call: { + id: 'tool-999', + name: 'question' + } + } + + expect(mapEventToBlock(agentEvent)).toEqual(expectedBlock) + }) + it('should map rate limit correctly', () => { const agentEvent: LLMAgentEvent = { type: 'response', @@ -234,7 +263,12 @@ describe('Renderer Contract Tests', () => { }) it('should only allow valid action_type values', () => { - const validActionTypes = ['tool_call_permission', 'maximum_tool_calls_reached', 'rate_limit'] + const validActionTypes = [ + 'tool_call_permission', + 'maximum_tool_calls_reached', + 'rate_limit', + 'question_request' + ] const block: AssistantMessageBlock = { type: 'action', @@ -503,6 +537,19 @@ function mapEventToBlock(event: LLMAgentEvent): AssistantMessageBlock { } } } + + if (data.tool_call === 'question-required') { + return { + type: 'action', + action_type: 'question_request', + status: 'pending', + timestamp, + tool_call: { + id: data.tool_call_id, + name: data.tool_call_name + } + } + } } // Rate limit @@ -562,7 +609,7 @@ function isValidStatusTransition( ): boolean { const validTransitions: Record = { loading: ['success', 'error'], - pending: ['granted', 'denied', 'error'], + pending: ['granted', 'denied', 'success', 'error'], success: [], error: [], granted: [], @@ -578,7 +625,10 @@ function processEndEvent( ): AssistantMessageBlock[] { return blocks.map((block) => { // Preserve permission blocks - if (block.type === 'action' && block.action_type === 'tool_call_permission') { + if ( + block.type === 'action' && + (block.action_type === 'tool_call_permission' || block.action_type === 'question_request') + ) { return block } From fe2ec87ccf5247d283da5160362b291d2def076a Mon Sep 17 00:00:00 2001 From: xiaomo Date: Thu, 5 Feb 2026 15:29:14 +0800 Subject: [PATCH 14/16] chore: integrated vue-tsgo (#1299) * chore: integrated vue-tsgo * chore: update recommendation exteion * chore: update --- .vscode/extensions.json | 2 +- package.json | 4 ++-- tsconfig.app.json | 16 +++++++------- tsconfig.app.tsgo.json | 46 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 58 insertions(+), 10 deletions(-) create mode 100644 tsconfig.app.tsgo.json diff --git a/.vscode/extensions.json b/.vscode/extensions.json index fe1ca403b..347bc4222 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,3 +1,3 @@ { - "recommendations": ["dbaeumer.vscode-eslint", "lokalise.i18n-ally", "esbenp.prettier-vscode"] + "recommendations": ["dbaeumer.vscode-eslint", "lokalise.i18n-ally", "esbenp.prettier-vscode", "TypeScriptTeam.native-preview"] } diff --git a/package.json b/package.json index 5923db0e2..93b77d225 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,7 @@ "format": "prettier --cache --write .", "lint": "oxlint .", "typecheck:node": "tsgo --noEmit -p tsconfig.node.json --composite false", - "typecheck:web": "vue-tsc --noEmit -p tsconfig.app.json --composite false", + "typecheck:web": "vue-tsgo --project tsconfig.app.tsgo.json", "typecheck": "pnpm run typecheck:node && pnpm run typecheck:web", "start": "electron-vite preview", "dev": "cross-env VITE_ENABLE_PLAYGROUND=true electron-vite dev --watch", @@ -180,7 +180,7 @@ "vue-i18n": "^11.2.7", "vue-router": "4", "vue-sonner": "^2.0.9", - "vue-tsc": "^2.2.12", + "vue-tsgo": "0.0.1-yggdrasill.11", "vue-virtual-scroller": "^2.0.0-beta.8", "vuedraggable": "^4.1.0", "yaml": "^2.8.2", diff --git a/tsconfig.app.json b/tsconfig.app.json index e855bf9fa..dd4a4c0d5 100644 --- a/tsconfig.app.json +++ b/tsconfig.app.json @@ -23,22 +23,24 @@ "src/renderer/settings/**/*.vue" ], "compilerOptions": { - "composite": true, - "baseUrl": ".", "module": "esnext", + "composite": false, + "declaration": false, + "emitDeclarationOnly": false, + "noEmit": true, "paths": { "@/*": [ - "src/renderer/src/*" + "./src/renderer/src/*" ], "@shell/*": [ - "src/renderer/shell/*" + "./src/renderer/shell/*" ], "@shared/*": [ - "src/shared/*" + "./src/shared/*" ], "@shadcn/*": [ - "src/shadcn/*" + "./src/shadcn/*" ] } } -} \ No newline at end of file +} diff --git a/tsconfig.app.tsgo.json b/tsconfig.app.tsgo.json new file mode 100644 index 000000000..dd4a4c0d5 --- /dev/null +++ b/tsconfig.app.tsgo.json @@ -0,0 +1,46 @@ +{ + "extends": "@electron-toolkit/tsconfig/tsconfig.web.json", + "include": [ + "src/renderer/src/env.d.ts", + "src/renderer/src/**/*", + "src/renderer/src/**/*.vue", + "src/renderer/src/assets/**/*", + "src/renderer/shell/env.d.ts", + "src/renderer/shell/**/*", + "src/renderer/shell/**/*.vue", + "src/renderer/shell/assets/**/*", + "src/renderer/floating/env.d.ts", + "src/renderer/floating/**/*", + "src/renderer/floating/**/*.vue", + "src/renderer/floating/assets/**/*", + "src/renderer/splash/**/*", + "src/renderer/splash/**/*.vue", + "src/renderer/splash/assets/**/*", + "src/preload/*.d.ts", + "src/shared/**/*", + "src/shadcn/**/*", + "src/renderer/settings/**/*", + "src/renderer/settings/**/*.vue" + ], + "compilerOptions": { + "module": "esnext", + "composite": false, + "declaration": false, + "emitDeclarationOnly": false, + "noEmit": true, + "paths": { + "@/*": [ + "./src/renderer/src/*" + ], + "@shell/*": [ + "./src/renderer/shell/*" + ], + "@shared/*": [ + "./src/shared/*" + ], + "@shadcn/*": [ + "./src/shadcn/*" + ] + } + } +} From d62289c01001ea0358b0482360237627f1ad4ac3 Mon Sep 17 00:00:00 2001 From: zerob13 Date: Thu, 5 Feb 2026 16:53:38 +0800 Subject: [PATCH 15/16] feat: add prompt for ask tool --- src/main/presenter/agentPresenter/tools/questionTool.ts | 2 +- src/main/presenter/toolPresenter/index.ts | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/main/presenter/agentPresenter/tools/questionTool.ts b/src/main/presenter/agentPresenter/tools/questionTool.ts index afd27b1c0..1d71f8060 100644 --- a/src/main/presenter/agentPresenter/tools/questionTool.ts +++ b/src/main/presenter/agentPresenter/tools/questionTool.ts @@ -2,7 +2,7 @@ import { z } from 'zod' import { jsonrepair } from 'jsonrepair' import type { QuestionInfo } from '@shared/types/core/question' -export const QUESTION_TOOL_NAME = 'question' +export const QUESTION_TOOL_NAME = 'deepchat_question' const questionOptionSchema = z.object({ label: z.string().trim().min(1).max(30), diff --git a/src/main/presenter/toolPresenter/index.ts b/src/main/presenter/toolPresenter/index.ts index 4d7975806..1a0c2b8af 100644 --- a/src/main/presenter/toolPresenter/index.ts +++ b/src/main/presenter/toolPresenter/index.ts @@ -7,6 +7,7 @@ import type { MCPToolResponse } from '@shared/presenter' import { resolveToolOffloadTemplatePath } from '../sessionPresenter/sessionPaths' +import { QUESTION_TOOL_NAME } from '../agentPresenter/tools/questionTool' import { ToolMapper } from './toolMapper' import { AgentToolManager, type AgentToolCallResult } from '../agentPresenter/acp' import { jsonrepair } from 'jsonrepair' @@ -172,7 +173,8 @@ export class ToolPresenter implements IToolPresenter { return [ 'Tool outputs may be offloaded when large.', `When you see an offload stub, read the full output from: ${offloadPath}`, - 'Use file tools to read that path. Access is limited to the current conversation session.' + 'Use file tools to read that path. Access is limited to the current conversation session.', + `If you need user confirmation or choices, ask with the ${QUESTION_TOOL_NAME} tool.` ].join('\n') } } From 85665d7434dec7e1e1c2ce3917b760929abf9b70 Mon Sep 17 00:00:00 2001 From: zerob13 Date: Thu, 5 Feb 2026 16:54:38 +0800 Subject: [PATCH 16/16] chore: version to 0.5.7 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 93b77d225..8a34cc198 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "DeepChat", - "version": "0.5.6-beta.5", + "version": "0.5.7", "description": "DeepChat,一个简单易用的 Agent 客户端", "main": "./out/main/index.js", "author": "ThinkInAIXYZ",