diff --git a/package.json b/package.json index afb9e1b..3714dc1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "levante", - "version": "1.8.1", + "version": "1.8.2", "description": "A friendly, private desktop chat app with AI and MCP integration", "main": ".vite/build/main.js", "homepage": "https://github.com/minte-community/levante", diff --git a/src/main/services/ai/__tests__/toolMessageSanitizer.test.ts b/src/main/services/ai/__tests__/toolMessageSanitizer.test.ts index 5d68548..04399b6 100644 --- a/src/main/services/ai/__tests__/toolMessageSanitizer.test.ts +++ b/src/main/services/ai/__tests__/toolMessageSanitizer.test.ts @@ -378,4 +378,62 @@ describe('sanitizeMessagesForModel', () => { expect(part.output.content).toEqual([{ type: 'text', text: 'hi' }]); expect(part.output.uiResources).toBeUndefined(); }); + + it('preserves coding tool plain-object outputs (bash) untouched', () => { + const output = { + status: 'success', + exitCode: 0, + output: 'hello\nworld', + truncated: false, + }; + + const messages: UIMessage[] = [ + makeAssistantMessage([ + makeToolPart({ state: 'output-available', output }), + ]), + ]; + + const result = sanitizeMessagesForModel(messages); + const part = result[0].parts[0] as any; + + expect(part.output).toEqual(output); + }); + + it('preserves read-tool plain object with content as string', () => { + const output = { + success: true, + content: 'string contents', + totalLines: 50, + }; + + const messages: UIMessage[] = [ + makeAssistantMessage([ + makeToolPart({ state: 'output-available', output }), + ]), + ]; + + const result = sanitizeMessagesForModel(messages); + const part = result[0].parts[0] as any; + + expect(part.output).toEqual(output); + }); + + it('preserves grep tool empty-match output', () => { + const output = { + success: true, + matches: 0, + message: 'No matches found', + }; + + const messages: UIMessage[] = [ + makeAssistantMessage([ + makeToolPart({ state: 'output-available', output }), + ]), + ]; + + const result = sanitizeMessagesForModel(messages); + const part = result[0].parts[0] as any; + + expect(part.output).toEqual(output); + }); }); diff --git a/src/main/services/ai/toolMessageSanitizer.ts b/src/main/services/ai/toolMessageSanitizer.ts index b8aefe2..53d1d34 100644 --- a/src/main/services/ai/toolMessageSanitizer.ts +++ b/src/main/services/ai/toolMessageSanitizer.ts @@ -137,7 +137,15 @@ export function sanitizeMessagesForModel(messages: UIMessage[]): UIMessage[] { return part; } - if (output && typeof output === 'object') { + const isLegacyMcpUiOutput = + output && typeof output === 'object' && ( + 'uiResources' in output || + 'images' in output || + (Array.isArray((output as any).content) && + (output as any).content.some((c: any) => c?.type === 'image')) + ); + + if (isLegacyMcpUiOutput) { const cleanOutput: Record = {}; if ((output as any).structuredContent) {