import type { ToolUIPart } from 'ai' import { describe, expect, test } from 'vitest' // End of third-party imports import { prepareMessagesForAPI } from '../message-utils' import { createAssistantMessageWithExecuteSqlTool, createAssistantMessageWithMultipleTools, createLongConversation, } from '../test-fixtures' import { NO_DATA_PERMISSIONS, sanitizeMessagePart } from './tool-sanitizer' describe('messages are sanitized based on opt-in level', () => { test('messages are sanitized at disabled level', () => { const messages = [ createAssistantMessageWithExecuteSqlTool('SELECT email FROM users', [ { email: 'test@example.com' }, ]), ] // Prepare messages as frontend would const preparedMessages = prepareMessagesForAPI(messages) // Sanitize messages as API endpoint would const processedMessages = preparedMessages.map((msg) => { if (msg.role === 'assistant' && msg.parts) { const processedParts = msg.parts.map((part) => { return sanitizeMessagePart(part, 'disabled') }) return { ...msg, parts: processedParts } } return msg }) const output = (processedMessages[0].parts[1] as ToolUIPart).output expect(output).toMatch(NO_DATA_PERMISSIONS) }) test('messages are sanitized at schema level', () => { const messages = [ createAssistantMessageWithExecuteSqlTool('SELECT email FROM users', [ { email: 'test@example.com' }, ]), ] // Prepare messages as frontend would const preparedMessages = prepareMessagesForAPI(messages) // Sanitize messages as API endpoint would const processedMessages = preparedMessages.map((msg) => { if (msg.role === 'assistant' && msg.parts) { const processedParts = msg.parts.map((part) => { return sanitizeMessagePart(part, 'schema') }) return { ...msg, parts: processedParts } } return msg }) const output = (processedMessages[0].parts[1] as ToolUIPart).output expect(output).toMatch(NO_DATA_PERMISSIONS) }) test('messages are sanitized at schema and log level', () => { const messages = [ createAssistantMessageWithExecuteSqlTool('SELECT email FROM users', [ { email: 'test@example.com' }, ]), ] // Prepare messages as frontend would const preparedMessages = prepareMessagesForAPI(messages) // Sanitize messages as API endpoint would const processedMessages = preparedMessages.map((msg) => { if (msg.role === 'assistant' && msg.parts) { const processedParts = msg.parts.map((part) => { return sanitizeMessagePart(part, 'schema_and_log') }) return { ...msg, parts: processedParts } } return msg }) const output = (processedMessages[0].parts[1] as ToolUIPart).output expect(output).toMatch(NO_DATA_PERMISSIONS) }) test('messages are not sanitized at data level', () => { const messages = [ createAssistantMessageWithExecuteSqlTool('SELECT email FROM users', [ { email: 'test@example.com' }, ]), ] // Prepare messages as frontend would const preparedMessages = prepareMessagesForAPI(messages) // Sanitize messages as API endpoint would const processedMessages = preparedMessages.map((msg) => { if (msg.role === 'assistant' && msg.parts) { const processedParts = msg.parts.map((part) => { return sanitizeMessagePart(part, 'schema_and_log_and_data') }) return { ...msg, parts: processedParts } } return msg }) const output = (processedMessages[0].parts[1] as ToolUIPart).output expect(output).toEqual([{ email: 'test@example.com' }]) }) test('multiple tool parts in message are sanitized', () => { const messages = [createAssistantMessageWithMultipleTools()] // Prepare messages as frontend would const preparedMessages = prepareMessagesForAPI(messages) // Sanitize messages as API endpoint would const processedMessages = preparedMessages.map((msg) => { if (msg.role === 'assistant' && msg.parts) { const processedParts = msg.parts.map((part) => { return sanitizeMessagePart(part, 'schema') }) return { ...msg, parts: processedParts } } return msg }) const parts = processedMessages[0].parts parts.forEach((part) => { if (part.type.startsWith('tool')) { const tool = part as ToolUIPart expect(tool.output).toMatch(NO_DATA_PERMISSIONS) } }) }) test('long message chain is sanitized', () => { const messages = createLongConversation() // Prepare messages as frontend would const preparedMessages = prepareMessagesForAPI(messages) // Sanitize messages as API endpoint would const processedMessages = preparedMessages.map((msg) => { if (msg.role === 'assistant' && msg.parts) { const processedParts = msg.parts.map((part) => { return sanitizeMessagePart(part, 'schema') }) return { ...msg, parts: processedParts } } return msg }) processedMessages.forEach((msg) => { if (msg.role === 'assistant' && msg.parts) { const parts = msg.parts parts.forEach((part) => { if (part.type.startsWith('tool')) { const tool = part as ToolUIPart expect(tool.output).toMatch(NO_DATA_PERMISSIONS) } }) } }) }) })