mirror of
https://github.com/openclaw/openclaw.git
synced 2026-03-12 07:20:45 +00:00
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:
@@ -21,6 +21,7 @@ Docs: https://docs.openclaw.ai
|
||||
- Agents/schema cleaning: detect Venice + Grok model IDs as xAI-proxied targets so unsupported JSON Schema keywords are stripped before requests, preventing Venice/Grok `Invalid arguments` failures. (openclaw#35355) thanks @Sid-Qin.
|
||||
- Skills/native command deduplication: centralize skill command dedupe by canonical `skillName` in `listSkillCommandsForAgents` so duplicate suffixed variants (for example `_2`) are no longer surfaced across interfaces outside Discord. (#27521) thanks @shivama205.
|
||||
- Agents/xAI tool-call argument decoding: decode HTML-entity encoded xAI/Grok tool-call argument values (`&`, `"`, `<`, `>`, numeric entities) before tool execution so commands with shell operators and quotes no longer fail with parse errors. (#35276) Thanks @Sid-Qin.
|
||||
- Agents/thinking-tag promotion hardening: guard `promoteThinkingTagsToBlocks` against malformed assistant content entries (`null`/`undefined`) before `block.type` reads so malformed provider payloads no longer crash session processing while preserving pass-through behavior. (#35143) thanks @Sid-Qin.
|
||||
- Feishu/streaming card delivery synthesis: unify snapshot and delta streaming merge semantics, apply overlap-aware final merge, suppress duplicate final text delivery (including text+media final packets), prefer topic-thread `message.reply` routing when a reply target exists, and tune card print cadence to avoid duplicate incremental rendering. (from #33245, #32896, #33840) Thanks @rexl2018, @kcinzgg, and @aerelune.
|
||||
- Security/dependency audit: patch transitive Hono vulnerabilities by pinning `hono` to `4.12.5` and `@hono/node-server` to `1.19.10` in production resolution paths. Thanks @shakkernerd.
|
||||
- Security/dependency audit: bump `tar` to `7.5.10` (from `7.5.9`) to address the high-severity hardlink path traversal advisory (`GHSA-qffp-2rhf-9h96`). Thanks @shakkernerd.
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user