import { describe, expect, it, vi } from 'vitest'; import { ChatGateway } from './chat.gateway.js'; const CONVERSATION_ID = 'conversation-1'; const CANARY = 'token=sk_canary12345678'; type GatewayInternals = { clientSessions: Map; relayEvent(client: unknown, conversationId: string, event: unknown): void; }; function buildGateway() { const brain = { conversations: { addMessage: vi.fn().mockResolvedValue(undefined), }, }; const agentService = { getSession: vi.fn().mockReturnValue(undefined), }; const gateway = new ChatGateway( agentService as never, {} as never, brain as never, {} as never, {} as never, {} as never, ); return { gateway: gateway as unknown as GatewayInternals, brain }; } describe('ChatGateway redaction boundary', (): void => { it('redacts an assistant delta before egress and before its persistence buffer', (): void => { const { gateway } = buildGateway(); const client = { connected: true, id: 'client-1', data: { user: { id: 'user-1' } }, emit: vi.fn(), }; const session = { conversationId: CONVERSATION_ID, cleanup: vi.fn(), assistantText: '', toolCalls: [], pendingToolCalls: new Map(), scope: { userId: 'user-1', tenantId: 'tenant-1' }, }; gateway.clientSessions.set(client.id, session); gateway.relayEvent(client, CONVERSATION_ID, { type: 'message_update', assistantMessageEvent: { type: 'text_delta', delta: CANARY }, }); expect(client.emit).toHaveBeenCalledWith('agent:text', { conversationId: CONVERSATION_ID, text: '[REDACTED_SECRET]', }); expect(session.assistantText).toBe('[REDACTED_SECRET]'); expect(JSON.stringify(client.emit.mock.calls)).not.toContain(CANARY); }); it('persists only redacted assistant content with classifications', (): void => { const { gateway, brain } = buildGateway(); const client = { connected: true, id: 'client-1', data: { user: { id: 'user-1' } }, emit: vi.fn(), }; gateway.clientSessions.set(client.id, { conversationId: CONVERSATION_ID, cleanup: vi.fn(), assistantText: CANARY, toolCalls: [], pendingToolCalls: new Map(), scope: { userId: 'user-1', tenantId: 'tenant-1' }, }); gateway.relayEvent(client, CONVERSATION_ID, { type: 'agent_end' }); expect(brain.conversations.addMessage).toHaveBeenCalledWith( expect.objectContaining({ content: '[REDACTED_SECRET]', metadata: expect.objectContaining({ classifications: ['secret'] }), }), 'user-1', ); expect(JSON.stringify(brain.conversations.addMessage.mock.calls)).not.toContain(CANARY); }); });