From f58dd0f9b6ae21e72e6ed2413a83f292b1a391bc Mon Sep 17 00:00:00 2001 From: FullerStackDev <263060202+fuller-stack-dev@users.noreply.github.com> Date: Tue, 21 Apr 2026 16:16:34 -0600 Subject: [PATCH] fix(agents): harden replay normalization guards --- src/agents/pi-embedded-runner/replay-history.ts | 7 +++++++ src/agents/pi-embedded-runner/run/attempt.ts | 10 ++++++---- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/agents/pi-embedded-runner/replay-history.ts b/src/agents/pi-embedded-runner/replay-history.ts index 25b156998dd..cb0985cd0c3 100644 --- a/src/agents/pi-embedded-runner/replay-history.ts +++ b/src/agents/pi-embedded-runner/replay-history.ts @@ -40,6 +40,7 @@ import { type AssistantUsageSnapshot, type UsageLike, } from "../usage.js"; +import { log } from "./logger.js"; import { dropThinkingBlocks } from "./thinking.js"; const INTER_SESSION_PREFIX_BASE = "[Inter-session message]"; @@ -235,6 +236,12 @@ export function normalizeAssistantReplayContent(messages: AgentMessage[]): Agent if (!message || message.role !== "assistant" || Array.isArray(message.content)) { continue; } + if (typeof message.content !== "string") { + log.warn( + `normalizeAssistantReplayContent: repairing malformed assistant content ` + + `(index=${i}, type=${typeof message.content})`, + ); + } out[i] = { ...(message as unknown as Record), content: diff --git a/src/agents/pi-embedded-runner/run/attempt.ts b/src/agents/pi-embedded-runner/run/attempt.ts index c462873b94c..01ab6984ba2 100644 --- a/src/agents/pi-embedded-runner/run/attempt.ts +++ b/src/agents/pi-embedded-runner/run/attempt.ts @@ -1144,9 +1144,11 @@ export async function runEmbeddedAttempt( throw new Error("Embedded agent session missing"); } const activeSession = session; - const baseConvertToLlm = activeSession.agent.convertToLlm.bind(activeSession.agent); - activeSession.agent.convertToLlm = async (messages) => - await baseConvertToLlm(normalizeAssistantReplayContent(messages)); + if (typeof activeSession.agent.convertToLlm === "function") { + const baseConvertToLlm = activeSession.agent.convertToLlm.bind(activeSession.agent); + activeSession.agent.convertToLlm = async (messages) => + await baseConvertToLlm(normalizeAssistantReplayContent(messages)); + } let prePromptMessageCount = activeSession.messages.length; abortSessionForYield = () => { yieldAbortSettled = Promise.resolve(activeSession.abort()); @@ -2193,7 +2195,7 @@ export async function runEmbeddedAttempt( activeSession.agent.state.messages = normalizedReplayMessages; } finalPromptText = effectivePrompt; - const btwSnapshotMessages = activeSession.messages.slice(-MAX_BTW_SNAPSHOT_MESSAGES); + const btwSnapshotMessages = normalizedReplayMessages.slice(-MAX_BTW_SNAPSHOT_MESSAGES); updateActiveEmbeddedRunSnapshot(params.sessionId, { transcriptLeafId, messages: btwSnapshotMessages,