diff --git a/src/lib/ai/coach/conversation-summary.ts b/src/lib/ai/coach/conversation-summary.ts index 074fc7c9d..41d7306b5 100644 --- a/src/lib/ai/coach/conversation-summary.ts +++ b/src/lib/ai/coach/conversation-summary.ts @@ -182,6 +182,9 @@ export async function refreshConversationSummary( // fail-closed like every other decrypt in the Coach stack. return []; } + // #781 — skip empty bodies: a cancelled-turn marker (an empty + // assistant row closing an aborted turn) carries no content to fold. + if (!content.trim()) return []; return [{ role: m.role === "assistant" ? "assistant" : "user", content }]; }); diff --git a/src/lib/ai/coach/facts.ts b/src/lib/ai/coach/facts.ts index 265598d64..8d3921c19 100644 --- a/src/lib/ai/coach/facts.ts +++ b/src/lib/ai/coach/facts.ts @@ -255,6 +255,9 @@ async function loadRecentTurns( for (const m of [...row.messages].reverse()) { const content = decryptOrNull(m.encryptedContent); if (content === null) continue; + // #781 — skip empty bodies: a cancelled-turn marker (an empty assistant + // row closing an aborted turn) carries nothing to extract from. + if (!content.trim()) continue; turns.push({ role: m.role, content }); } return turns;