fix(canvas): extractMessageText uses only first direct text field
Some checks failed
Secret scan / Scan diff for credential-shaped strings (pull_request) Successful in 8s
sop-tier-check / tier-check (pull_request) Failing after 12s

Bug: `extractMessageText` in ConversationTraceModal joined text from ALL
result.parts[].text + result.parts[].root.text entries, concatenating
"Direct text\nRoot text" when only "Direct text" was expected.

Fix: scan all parts for the first direct `text` field and return it.
Only fall back to `parts[0].root.text` when no direct text exists.
Subsequent parts' root.text fields are ignored when a direct text
was found in an earlier part — matching the test contract.

Fixes: ConversationTraceModal.test.tsx "prefers parts[].text over
parts[].root.text" (test was failing with concat output).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
Molecule AI · fullstack-engineer 2026-05-10 21:18:55 +00:00
parent 6958cd7966
commit a832bd805c

View File

@ -31,17 +31,25 @@ export function extractMessageText(body: Record<string, unknown> | null): string
if (text) return text;
// Response: result.parts[].text or result.parts[].root.text
// Use the first part that has a direct text field; within that part,
// prefer direct text over root.text. Subsequent parts' root.text fields
// are ignored when a direct text exists in an earlier part.
const result = body.result as Record<string, unknown> | undefined;
const rParts = (result?.parts || []) as Array<Record<string, unknown>>;
const rText = rParts
.map((p) => {
if (p.text) return p.text as string;
const root = p.root as Record<string, unknown> | undefined;
return (root?.text as string) || "";
})
.filter(Boolean)
.join("\n");
if (rText) return rText;
const firstPartWithText = rParts.find(
(p) => typeof p.text === "string" && (p.text as string) !== ""
);
if (firstPartWithText) {
return firstPartWithText.text as string;
}
// No direct text found; use root.text from the first part (if present).
const firstPart = rParts[0];
if (firstPart) {
const root = firstPart.root as Record<string, unknown> | undefined;
if (typeof root?.text === "string" && root.text !== "") {
return root.text as string;
}
}
if (typeof body.result === "string") return body.result;
} catch { /* ignore */ }