fix(agents): guard promoteThinkingTagsToBlocks against malformed content entries (#35143)

Merged via squash.

Prepared head SHA: 3971122f5f
Co-authored-by: Sid-Qin <201593046+Sid-Qin@users.noreply.github.com>
Co-authored-by: shakkernerd <165377636+shakkernerd@users.noreply.github.com>
Reviewed-by: @shakkernerd
This commit is contained in:
Sid
2026-03-05 13:37:33 +08:00
committed by GitHub
parent ce0c13191f
commit d9b69a6145
3 changed files with 42 additions and 1 deletions

View File

@@ -3,6 +3,7 @@ import { describe, expect, it } from "vitest";
import {
extractAssistantText,
formatReasoningMessage,
promoteThinkingTagsToBlocks,
stripDowngradedToolCallText,
} from "./pi-embedded-utils.js";
@@ -549,6 +550,39 @@ describe("stripDowngradedToolCallText", () => {
});
});
describe("promoteThinkingTagsToBlocks", () => {
it("does not crash on malformed null content entries", () => {
const msg = makeAssistantMessage({
role: "assistant",
content: [null as never, { type: "text", text: "<thinking>hello</thinking>ok" }],
timestamp: Date.now(),
});
expect(() => promoteThinkingTagsToBlocks(msg)).not.toThrow();
const types = msg.content.map((b: { type?: string }) => b?.type);
expect(types).toContain("thinking");
expect(types).toContain("text");
});
it("does not crash on undefined content entries", () => {
const msg = makeAssistantMessage({
role: "assistant",
content: [undefined as never, { type: "text", text: "no tags here" }],
timestamp: Date.now(),
});
expect(() => promoteThinkingTagsToBlocks(msg)).not.toThrow();
});
it("passes through well-formed content unchanged when no thinking tags", () => {
const msg = makeAssistantMessage({
role: "assistant",
content: [{ type: "text", text: "hello world" }],
timestamp: Date.now(),
});
promoteThinkingTagsToBlocks(msg);
expect(msg.content).toEqual([{ type: "text", text: "hello world" }]);
});
});
describe("empty input handling", () => {
it("returns empty string", () => {
const helpers = [formatReasoningMessage, stripDowngradedToolCallText];

View File

@@ -333,7 +333,9 @@ export function promoteThinkingTagsToBlocks(message: AssistantMessage): void {
if (!Array.isArray(message.content)) {
return;
}
const hasThinkingBlock = message.content.some((block) => block.type === "thinking");
const hasThinkingBlock = message.content.some(
(block) => block && typeof block === "object" && block.type === "thinking",
);
if (hasThinkingBlock) {
return;
}
@@ -342,6 +344,10 @@ export function promoteThinkingTagsToBlocks(message: AssistantMessage): void {
let changed = false;
for (const block of message.content) {
if (!block || typeof block !== "object" || !("type" in block)) {
next.push(block);
continue;
}
if (block.type !== "text") {
next.push(block);
continue;