diff --git a/packages/core/src/context/contextManager.ts b/packages/core/src/context/contextManager.ts index b26aff77a9..26256980a0 100644 --- a/packages/core/src/context/contextManager.ts +++ b/packages/core/src/context/contextManager.ts @@ -411,12 +411,23 @@ export class ContextManager { sentinels: this.sidecar.sentinels, }); + const firstPendingId = pendingHistory[0]?.id; + let splitIndex = renderedHistory.length; + if (firstPendingId) { + const foundIndex = hardenedAllHistory.findIndex( + (h) => h.id === firstPendingId, + ); + if (foundIndex !== -1) { + splitIndex = foundIndex; + } + } + const apiHistory = hardenedAllHistory - .slice(0, renderedHistory.length) + .slice(0, splitIndex) .map((h) => h.content); const pendingApiHistory = hardenedAllHistory - .slice(renderedHistory.length) + .slice(splitIndex) .map((h) => h.content); if (header) { diff --git a/packages/core/src/core/client.ts b/packages/core/src/core/client.ts index a495cdf25f..22a6680803 100644 --- a/packages/core/src/core/client.ts +++ b/packages/core/src/core/client.ts @@ -644,11 +644,7 @@ export class GeminiClient { if (this.contextManager) { const rawPendingRequest = createUserContent(request); const pendingRequest = { - id: - this.getChatRecordingService()?.recordSyntheticMessage( - 'user', - rawPendingRequest.parts || [], - ) || randomUUID(), + id: randomUUID(), content: rawPendingRequest, }; const { @@ -676,8 +672,9 @@ export class GeminiClient { this.getChat().setHistory(newHistory, { silent: true }); - // Update the request for turn.run so that the final history record - // matches the processed/cleaned content sent to the API. + // Use the original request for display/recording, + // but the processed one for the API and durable history. + displayContent = rawPendingRequest.parts || []; request = finalPendingContent.parts || []; } else { const newHistory = await this.agentHistoryProvider.manageHistory( diff --git a/packages/core/src/utils/sessionUtils.ts b/packages/core/src/utils/sessionUtils.ts index 5c50f13ed4..cef35650c9 100644 --- a/packages/core/src/utils/sessionUtils.ts +++ b/packages/core/src/utils/sessionUtils.ts @@ -126,34 +126,42 @@ export function convertSessionToClientHistory( } else if (msg.type === 'gemini') { const modelParts: Part[] = []; - // 1. Add thoughts from metadata if present - if (msg.thoughts && msg.thoughts.length > 0) { - for (const thought of msg.thoughts) { - const thoughtText = thought.subject - ? `**${thought.subject}** ${thought.description}` - : thought.description; - modelParts.push({ - text: thoughtText, - thought: true, - } as Part); + const contentParts = msg.content ? ensurePartArray(msg.content) : []; + const hasCallsInContent = contentParts.some((p) => !!p.functionCall); + const hasThoughtsInContent = contentParts.some((p) => p.thought); + + if (hasCallsInContent || hasThoughtsInContent) { + // Modern session: content is the source of truth for all parts + modelParts.push(...contentParts); + } else { + // Legacy session: rebuild from components + // 1. Add thoughts from metadata if present + if (msg.thoughts && msg.thoughts.length > 0) { + for (const thought of msg.thoughts) { + const thoughtText = thought.subject + ? `**${thought.subject}** ${thought.description}` + : thought.description; + modelParts.push({ + text: thoughtText, + thought: true, + } as Part); + } } - } - // 2. Add original parts from content (preserving multimodal integrity and synthetic thoughts) - if (msg.content) { - modelParts.push(...ensurePartArray(msg.content)); - } + // 2. Add content (usually just text in legacy) + modelParts.push(...contentParts); - // 3. Add tool calls if present - if (msg.toolCalls && msg.toolCalls.length > 0) { - for (const toolCall of msg.toolCalls) { - modelParts.push({ - functionCall: { - id: toolCall.id, - name: toolCall.name, - args: toolCall.args, - }, - }); + // 3. Add tool calls from metadata + if (msg.toolCalls && msg.toolCalls.length > 0) { + for (const toolCall of msg.toolCalls) { + modelParts.push({ + functionCall: { + id: toolCall.id, + name: toolCall.name, + args: toolCall.args, + }, + }); + } } }