From a7cb270999ecc25731b18648d4431c6687968041 Mon Sep 17 00:00:00 2001 From: Keith the Silly Goose Date: Mon, 12 Jan 2026 16:11:26 +1300 Subject: [PATCH] fix(agent): buffer streaming output until tag appears - Enforces strict buffering when enforceFinalTag is enabled. - Prevents 'thinking out loud' planning steps (e.g. '*Locating Manulife*') from leaking to WhatsApp. - Hardens tag stripping to remove nested/hallucinated tags. --- src/agents/pi-embedded-subscribe.ts | 22 ++++++++++++++-------- src/utils/provider-utils.ts | 5 +++++ 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/src/agents/pi-embedded-subscribe.ts b/src/agents/pi-embedded-subscribe.ts index a6dcabe06..209936806 100644 --- a/src/agents/pi-embedded-subscribe.ts +++ b/src/agents/pi-embedded-subscribe.ts @@ -369,8 +369,12 @@ export function subscribeEmbeddedPiSession(params: { state.thinking = inThinking; // 2. Handle blocks (stateful, strip content OUTSIDE) - // If enforcement is disabled, just return processed text as-is. - if (!params.enforceFinalTag) return processed; + // If enforcement is disabled, we still strip the tags themselves to prevent + // hallucinations (e.g. Minimax copying the style) from leaking, but we + // do not enforce buffering/extraction logic. + if (!params.enforceFinalTag) { + return processed.replace(FINAL_TAG_SCAN_RE, ""); + } // If enforcement is enabled, only return text that appeared inside a block. let result = ""; @@ -414,14 +418,16 @@ export function subscribeEmbeddedPiSession(params: { })); } - // Fallback: if we are at the end of the process and never saw a final tag, - // but we have processed text, use the processed text. - // NOTE: This fallback only triggers if we explicitly pass a state that we can check. - if (!everInFinal && processed.trim().length > 0) { - return processed; + // Strict Mode: If enforcing final tags, we MUST NOT return content unless + // we have seen a tag. Otherwise, we leak "thinking out loud" text + // (e.g. "**Locating Manulife**...") that the model emitted without tags. + if (!everInFinal) { + return ""; } - return result; + // Hardened Cleanup: Remove any remaining tags that might have been + // missed (e.g. nested tags or hallucinations) to prevent leakage. + return result.replace(FINAL_TAG_SCAN_RE, ""); }; const emitBlockChunk = (text: string) => { diff --git a/src/utils/provider-utils.ts b/src/utils/provider-utils.ts index e15fce2e6..841b60938 100644 --- a/src/utils/provider-utils.ts +++ b/src/utils/provider-utils.ts @@ -24,6 +24,11 @@ export function isReasoningTagProvider(provider: string | undefined | null): boo if (normalized.includes("google-antigravity")) { return true; } + + // Handle Minimax (M2.1 is chatty/reasoning-like) + if (normalized.includes("minimax")) { + return true; + } return false; }