diff --git a/src/core/bot.ts b/src/core/bot.ts index b1b50ce..b51f831 100644 --- a/src/core/bot.ts +++ b/src/core/bot.ts @@ -2005,7 +2005,9 @@ export class LettaBot implements AgentSession { apiError: (msg as any).apiError, }; } - if (msg.type === 'assistant') { + if (msg.type === 'reasoning') { + // Skip reasoning -- internal thinking should not leak into delivery + } else if (msg.type === 'assistant') { response += msg.content || ''; } if (msg.type === 'result') { diff --git a/src/core/sdk-session-contract.test.ts b/src/core/sdk-session-contract.test.ts index 0f2abf4..557cd02 100644 --- a/src/core/sdk-session-contract.test.ts +++ b/src/core/sdk-session-contract.test.ts @@ -133,6 +133,36 @@ describe('SDK session contract', () => { expect(mockSession.stream).toHaveBeenCalledTimes(2); }); + it('does not include reasoning chunks in sendToAgent responses', async () => { + const mockSession = { + initialize: vi.fn(async () => undefined), + send: vi.fn(async (_message: unknown) => undefined), + stream: vi.fn(() => + (async function* () { + yield { type: 'reasoning', content: 'internal-thought-1' }; + yield { type: 'assistant', content: 'public-response' }; + yield { type: 'reasoning', content: 'internal-thought-2' }; + yield { type: 'assistant', content: ' continues' }; + yield { type: 'result', success: true }; + })() + ), + close: vi.fn(() => undefined), + agentId: 'agent-contract-test', + conversationId: 'conversation-contract-test', + }; + + vi.mocked(resumeSession).mockReturnValue(mockSession as never); + + const bot = new LettaBot({ + workingDir: join(dataDir, 'working'), + allowedTools: [], + }); + + const response = await bot.sendToAgent('test message'); + + expect(response).toBe('public-response continues'); + }); + it('accumulates tool_call arguments when continuation chunks omit toolCallId', async () => { const mockSession = { initialize: vi.fn(async () => undefined),