Skip to content

Commit a4df85e

Browse files
committed
fix(ui): render text-block tool results
1 parent 6f8b9bb commit a4df85e

4 files changed

Lines changed: 51 additions & 1 deletion

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ Docs: https://docs.openclaw.ai
4747

4848
- Control UI: point the Appearance tweakcn browse action and docs at the live tweakcn editor route instead of the removed `/themes` page. Fixes #77048.
4949
- Control UI: render Dream Diary prose through the sanitized markdown pipeline, so diary bold/italic/header markdown no longer appears as literal source text. Fixes #62413.
50+
- Control UI: render tool results whose output arrives as text-block arrays and give expanded tool output a scrollable block, so read/exec output remains visible in WebChat. Fixes #77054.
5051
- Diagnostics: keep webhook/message OTEL attributes and Prometheus delivery labels low-cardinality and omit raw chat/message IDs from spans, so progress-draft and message-tool modes do not leak high-cardinality messaging identifiers.
5152
- Google Meet: stop advertising legacy `mode: "realtime"` to agents and config UIs, while keeping it as a hidden compatibility alias for `mode: "agent"`, so new joins use the STT -> OpenClaw agent -> TTS path instead of selecting the direct realtime voice fallback.
5253
- Google Meet: add `chrome.audioBufferBytes` for generated command-pair SoX audio commands and lower the default buffer from SoX's 8192 bytes to 4096 bytes to reduce Chrome talk-back latency.

ui/src/styles/chat/tool-cards.css

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -361,7 +361,15 @@
361361

362362
.chat-tool-card__block-content {
363363
color: var(--text);
364-
overflow-x: auto;
364+
overflow: auto;
365+
max-height: min(520px, 60vh);
366+
}
367+
368+
.chat-tool-card__inline {
369+
margin-top: 10px;
370+
white-space: pre-wrap;
371+
overflow-wrap: anywhere;
372+
word-break: break-word;
365373
}
366374

367375
.chat-tool-card__block-preview {

ui/src/ui/chat/tool-cards.node.test.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,35 @@ describe("tool-card extraction", () => {
136136
});
137137
});
138138

139+
it("extracts tool result output from text block content arrays", () => {
140+
const cards = extractToolCards(
141+
{
142+
role: "assistant",
143+
content: [
144+
{
145+
type: "toolcall",
146+
id: "call-read",
147+
name: "read",
148+
input: { path: "README.md" },
149+
},
150+
{
151+
type: "tool_result",
152+
id: "call-read",
153+
name: "read",
154+
content: [
155+
{ type: "text", text: "# Heading" },
156+
{ type: "text", text: "file body" },
157+
],
158+
},
159+
],
160+
},
161+
"msg:read",
162+
);
163+
164+
expect(cards).toHaveLength(1);
165+
expect(cards[0]?.outputText).toBe("# Heading\nfile body");
166+
});
167+
139168
it("builds sidebar content with input and empty output status", () => {
140169
const [card] = extractToolCards(
141170
{

ui/src/ui/chat/tool-cards.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,18 @@ function extractToolText(item: Record<string, unknown>): string | undefined {
4848
if (typeof item.content === "string") {
4949
return item.content;
5050
}
51+
if (Array.isArray(item.content)) {
52+
const parts = item.content.flatMap((entry) => {
53+
if (!entry || typeof entry !== "object") {
54+
return [];
55+
}
56+
const text = (entry as { text?: unknown }).text;
57+
return typeof text === "string" ? [text] : [];
58+
});
59+
if (parts.length > 0) {
60+
return parts.join("\n");
61+
}
62+
}
5163
return undefined;
5264
}
5365

0 commit comments

Comments
 (0)