diff --git a/src/app/api/chat/sessions/[id]/route.ts b/src/app/api/chat/sessions/[id]/route.ts index 91d760b6..b44ee0dc 100644 --- a/src/app/api/chat/sessions/[id]/route.ts +++ b/src/app/api/chat/sessions/[id]/route.ts @@ -1,5 +1,5 @@ import { NextRequest } from 'next/server'; -import { deleteSession, getSession, updateSessionWorkingDirectory, updateSessionTitle, updateSessionMode, updateSessionModel, updateSessionProviderId, clearSessionMessages } from '@/lib/db'; +import { deleteSession, getSession, updateSessionWorkingDirectory, updateSessionTitle, updateSessionMode, updateSessionModel, updateSessionProviderId, clearSessionMessages, updateSdkSessionId } from '@/lib/db'; export async function GET( _request: NextRequest, @@ -46,6 +46,9 @@ export async function PATCH( if (body.provider_id !== undefined) { updateSessionProviderId(id, body.provider_id); } + if (body.sdk_session_id !== undefined) { + updateSdkSessionId(id, body.sdk_session_id); + } if (body.clear_messages) { clearSessionMessages(id); } diff --git a/src/lib/claude-client.ts b/src/lib/claude-client.ts index 61a813f3..afde9aae 100644 --- a/src/lib/claude-client.ts +++ b/src/lib/claude-client.ts @@ -235,9 +235,13 @@ function buildPromptWithHistory( ): string { if (!history || history.length === 0) return prompt; - const lines: string[] = ['']; + const lines: string[] = [ + '', + '(This is a summary of earlier conversation turns for context. Tool calls shown here were already executed — do not repeat them or output their markers as text.)', + ]; for (const msg of history) { - // For assistant messages with tool blocks (JSON arrays), summarize + // For assistant messages with tool blocks (JSON arrays), extract only the text portions. + // Tool-use and tool-result blocks are omitted to avoid Claude parroting them as plain text. let content = msg.content; if (msg.role === 'assistant' && content.startsWith('[')) { try { @@ -245,14 +249,9 @@ function buildPromptWithHistory( const parts: string[] = []; for (const b of blocks) { if (b.type === 'text' && b.text) parts.push(b.text); - else if (b.type === 'tool_use') parts.push(`[Used tool: ${b.name}]`); - else if (b.type === 'tool_result') { - const resultStr = typeof b.content === 'string' ? b.content : JSON.stringify(b.content); - // Truncate long tool results - parts.push(`[Tool result: ${resultStr.slice(0, 500)}${resultStr.length > 500 ? '...' : ''}]`); - } + // Skip tool_use and tool_result — they were already executed } - content = parts.join('\n'); + content = parts.length > 0 ? parts.join('\n') : '(assistant used tools)'; } catch { // Not JSON, use as-is } diff --git a/src/lib/stream-session-manager.ts b/src/lib/stream-session-manager.ts index e0548aa7..a041c89c 100644 --- a/src/lib/stream-session-manager.ts +++ b/src/lib/stream-session-manager.ts @@ -392,6 +392,12 @@ async function runStream(stream: ActiveStream, params: StartStreamParams): Promi stream.toolResultsArray = []; stream.toolOutputAccumulated = ''; emit(stream, 'completed'); + // Clear stale SDK session so next message starts fresh + fetch(`/api/chat/sessions/${encodeURIComponent(stream.sessionId)}`, { + method: 'PATCH', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ sdk_session_id: '' }), + }).catch(() => {}); scheduleGC(stream); } else if (stream.toolTimeoutInfo) { // Tool timeout — auto-retry