diff --git a/src/agents/pi-embedded-subscribe.e2e-harness.ts b/src/agents/pi-embedded-subscribe.e2e-harness.ts index 53fc38233f4..12138fefbcd 100644 --- a/src/agents/pi-embedded-subscribe.e2e-harness.ts +++ b/src/agents/pi-embedded-subscribe.e2e-harness.ts @@ -13,6 +13,7 @@ export const THINKING_TAG_CASES = [ { tag: "thinking", open: "", close: "" }, { tag: "thought", open: "", close: "" }, { tag: "antthinking", open: "", close: "" }, + { tag: "antml:thinking", open: "", close: "" }, ] as const; export function createStubSessionHarness(): { diff --git a/src/agents/pi-embedded-subscribe.ts b/src/agents/pi-embedded-subscribe.ts index e257abf58b5..24b4bdb4718 100644 --- a/src/agents/pi-embedded-subscribe.ts +++ b/src/agents/pi-embedded-subscribe.ts @@ -36,7 +36,8 @@ import type { SubscribeEmbeddedPiSessionParams } from "./pi-embedded-subscribe.t import { formatReasoningMessage, stripDowngradedToolCallText } from "./pi-embedded-utils.js"; import { hasNonzeroUsage, normalizeUsage, type UsageLike } from "./usage.js"; -const THINKING_TAG_SCAN_RE = /<\s*(\/?)\s*(?:think(?:ing)?|thought|antthinking)\s*>/gi; +const THINKING_TAG_SCAN_RE = + /<\s*(\/?)\s*(?:(?:antml:)?(?:think(?:ing)?|thought)|antthinking)\s*>/gi; const FINAL_TAG_SCAN_RE = /<\s*(\/?)\s*final\s*>/gi; const log = createSubsystemLogger("agent/embedded"); diff --git a/src/agents/pi-embedded-utils.test.ts b/src/agents/pi-embedded-utils.test.ts index 2c69c9614bd..01cf81e386f 100644 --- a/src/agents/pi-embedded-utils.test.ts +++ b/src/agents/pi-embedded-utils.test.ts @@ -806,6 +806,20 @@ describe("promoteThinkingTagsToBlocks", () => { expect(types).toContain("text"); }); + it("splits antml namespaced thinking tags into thinking blocks", () => { + const msg = makeAssistantMessage({ + role: "assistant", + content: [{ type: "text", text: "hiddenVisible" }], + timestamp: Date.now(), + }); + + promoteThinkingTagsToBlocks(msg); + expect(msg.content).toEqual([ + { type: "thinking", thinking: "hidden" }, + { type: "text", text: "Visible" }, + ]); + }); + it("does not crash on undefined content entries", () => { const msg = makeAssistantMessage({ role: "assistant", diff --git a/src/agents/pi-embedded-utils.ts b/src/agents/pi-embedded-utils.ts index 666716ed20b..67b4b6adb47 100644 --- a/src/agents/pi-embedded-utils.ts +++ b/src/agents/pi-embedded-utils.ts @@ -189,8 +189,8 @@ export function splitThinkingTaggedText(text: string): ThinkTaggedSplitBlock[] | if (!trimmedStart.startsWith("<")) { return null; } - const openRe = /<\s*(?:think(?:ing)?|thought|antthinking)\s*>/i; - const closeRe = /<\s*\/\s*(?:think(?:ing)?|thought|antthinking)\s*>/i; + const openRe = /<\s*(?:(?:antml:)?(?:think(?:ing)?|thought)|antthinking)\s*>/i; + const closeRe = /<\s*\/\s*(?:(?:antml:)?(?:think(?:ing)?|thought)|antthinking)\s*>/i; if (!openRe.test(trimmedStart)) { return null; } @@ -198,7 +198,7 @@ export function splitThinkingTaggedText(text: string): ThinkTaggedSplitBlock[] | return null; } - const scanRe = /<\s*(\/?)\s*(?:think(?:ing)?|thought|antthinking)\s*>/gi; + const scanRe = /<\s*(\/?)\s*(?:(?:antml:)?(?:think(?:ing)?|thought)|antthinking)\s*>/gi; let inThinking = false; let cursor = 0; let thinkingStart = 0; @@ -299,7 +299,7 @@ export function extractThinkingFromTaggedText(text: string): string { if (!text) { return ""; } - const scanRe = /<\s*(\/?)\s*(?:think(?:ing)?|thought|antthinking)\s*>/gi; + const scanRe = /<\s*(\/?)\s*(?:(?:antml:)?(?:think(?:ing)?|thought)|antthinking)\s*>/gi; let result = ""; let lastIndex = 0; let inThinking = false; @@ -324,8 +324,8 @@ export function extractThinkingFromTaggedStream(text: string): string { return closed; } - const openRe = /<\s*(?:think(?:ing)?|thought|antthinking)\s*>/gi; - const closeRe = /<\s*\/\s*(?:think(?:ing)?|thought|antthinking)\s*>/gi; + const openRe = /<\s*(?:(?:antml:)?(?:think(?:ing)?|thought)|antthinking)\s*>/gi; + const closeRe = /<\s*\/\s*(?:(?:antml:)?(?:think(?:ing)?|thought)|antthinking)\s*>/gi; const openMatches = [...text.matchAll(openRe)]; if (openMatches.length === 0) { return "";