mirror of
https://github.com/hansjone/oclaw.git
synced 2026-10-11 09:20:49 +08:00
fix(chat): keep tool-call process notes out of visible body
Preserve tool_call event_type when hydrating WS final messages, never render main-channel process notes as assistant body text, and prefer structured message content over stream buffer on finalize. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
4610989d72
commit
39fd1905b6
1 changed files with 28 additions and 13 deletions
|
|
@ -522,10 +522,13 @@ async function _buildAggregatedAssistantBubble(tsIso, items) {
|
||||||
}
|
}
|
||||||
if (kind === "assistant_text") {
|
if (kind === "assistant_text") {
|
||||||
const aet = String((it && it.assistantEventType) || "assistant_text").toLowerCase();
|
const aet = String((it && it.assistantEventType) || "assistant_text").toLowerCase();
|
||||||
if (aet === "tool_call" && adminChatShowToolOutput) {
|
if (aet === "tool_call") {
|
||||||
const t0 = String(text || "").trim();
|
// Main-channel process notes belong in the reasoning fold only — never in visible body text.
|
||||||
if (t0 && !_foldProcessTextRedundant(t0, collapsedItems)) {
|
if (adminChatShowToolOutput) {
|
||||||
collapsedItems.push({ title: t("reasoning.processNotes"), text: t0 });
|
const t0 = String(text || "").trim();
|
||||||
|
if (t0 && !_foldProcessTextRedundant(t0, collapsedItems)) {
|
||||||
|
collapsedItems.push({ title: t("reasoning.processNotes"), text: t0 });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
const attsAssistant = await renderAttachmentsEl(it && it.attachments);
|
const attsAssistant = await renderAttachmentsEl(it && it.attachments);
|
||||||
if (attsAssistant) inner.appendChild(attsAssistant);
|
if (attsAssistant) inner.appendChild(attsAssistant);
|
||||||
|
|
@ -4263,6 +4266,8 @@ async function renderChatUi() {
|
||||||
if (!message || typeof message !== "object") return [];
|
if (!message || typeof message !== "object") return [];
|
||||||
const mesAttParsed = parseAttachments(message.attachments);
|
const mesAttParsed = parseAttachments(message.attachments);
|
||||||
const mesAtt = mesAttParsed.length ? mesAttParsed : null;
|
const mesAtt = mesAttParsed.length ? mesAttParsed : null;
|
||||||
|
const srcEventType = _normalizeEventType(message.event_type || message.eventType);
|
||||||
|
const fallbackEventType = srcEventType === "tool_call" ? "tool_call" : "assistant_text";
|
||||||
const base = {
|
const base = {
|
||||||
role: "assistant",
|
role: "assistant",
|
||||||
id: message.id,
|
id: message.id,
|
||||||
|
|
@ -4301,25 +4306,32 @@ async function renderChatUi() {
|
||||||
const textFallback = decodeEscapedNewlines(
|
const textFallback = decodeEscapedNewlines(
|
||||||
typeof message.content === "string" ? message.content : typeof message.text === "string" ? message.text : "",
|
typeof message.content === "string" ? message.content : typeof message.text === "string" ? message.text : "",
|
||||||
).trim();
|
).trim();
|
||||||
const hasAssistantText = out.some((row) => String(row.event_type || "") === "assistant_text");
|
const hasBodyText = out.some((row) => {
|
||||||
if (textFallback && !hasAssistantText) {
|
const et = _normalizeEventType(row.event_type);
|
||||||
out.push({ ...base, content: textFallback, event_type: "assistant_text" });
|
return et === "assistant_text" || et === "tool_call";
|
||||||
|
});
|
||||||
|
if (textFallback && !hasBodyText) {
|
||||||
|
out.push({ ...base, content: textFallback, event_type: fallbackEventType });
|
||||||
}
|
}
|
||||||
if (!out.length && (textFallback || (mesAtt && mesAtt.length))) {
|
if (!out.length && (textFallback || (mesAtt && mesAtt.length))) {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
...base,
|
...base,
|
||||||
content: textFallback,
|
content: textFallback,
|
||||||
event_type: "assistant_text",
|
event_type: fallbackEventType,
|
||||||
tool_calls: message.tool_calls != null ? message.tool_calls : message.toolCalls,
|
tool_calls: message.tool_calls != null ? message.tool_calls : message.toolCalls,
|
||||||
...(mesAtt && mesAtt.length ? { attachments: mesAtt } : {}),
|
...(mesAtt && mesAtt.length ? { attachments: mesAtt } : {}),
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
if (out.length) {
|
if (out.length) {
|
||||||
|
const _isBodyRow = (row) => {
|
||||||
|
const et = _normalizeEventType(row && row.event_type);
|
||||||
|
return et === "assistant_text" || et === "tool_call";
|
||||||
|
};
|
||||||
let attachIdx = out.length - 1;
|
let attachIdx = out.length - 1;
|
||||||
for (let i = out.length - 1; i >= 0; i -= 1) {
|
for (let i = out.length - 1; i >= 0; i -= 1) {
|
||||||
if (String(out[i].event_type || "") === "assistant_text") {
|
if (_isBodyRow(out[i])) {
|
||||||
attachIdx = i;
|
attachIdx = i;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -4328,10 +4340,10 @@ async function renderChatUi() {
|
||||||
...out[attachIdx],
|
...out[attachIdx],
|
||||||
tool_calls: message.tool_calls != null ? message.tool_calls : message.toolCalls,
|
tool_calls: message.tool_calls != null ? message.tool_calls : message.toolCalls,
|
||||||
};
|
};
|
||||||
// Avoid duplicating blobs: attach WS/API root attachments to the latest assistant_text row only.
|
// Avoid duplicating blobs: attach WS/API root attachments to the latest body row only.
|
||||||
if (mesAtt && mesAtt.length) {
|
if (mesAtt && mesAtt.length) {
|
||||||
for (let i = out.length - 1; i >= 0; i -= 1) {
|
for (let i = out.length - 1; i >= 0; i -= 1) {
|
||||||
if (String(out[i].event_type || "") === "assistant_text") {
|
if (_isBodyRow(out[i])) {
|
||||||
out[i] = { ...out[i], attachments: mesAtt };
|
out[i] = { ...out[i], attachments: mesAtt };
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -4934,7 +4946,7 @@ ${autoLimit ? `<div style="margin-top:8px;"><span class="muted">auto-added claus
|
||||||
hasRealStreamText ||
|
hasRealStreamText ||
|
||||||
(Array.isArray(chatStreamSegments) && chatStreamSegments.length > 0) ||
|
(Array.isArray(chatStreamSegments) && chatStreamSegments.length > 0) ||
|
||||||
!!String(chatStream || "").trim();
|
!!String(chatStream || "").trim();
|
||||||
const fbLine = chatStream || extractWsAssistantText(payload.message || {});
|
const fbLine = extractWsAssistantText(payload.message || {}) || chatStream;
|
||||||
const fbTrim = String(fbLine || "").trim();
|
const fbTrim = String(fbLine || "").trim();
|
||||||
const fbOk = !!fbTrim && !_isSilentReplyStream(fbTrim);
|
const fbOk = !!fbTrim && !_isSilentReplyStream(fbTrim);
|
||||||
const n = await loadMessagesForActive({ keepDomIfNoHistoryRows: hadStream });
|
const n = await loadMessagesForActive({ keepDomIfNoHistoryRows: hadStream });
|
||||||
|
|
@ -4959,7 +4971,10 @@ ${autoLimit ? `<div style="margin-top:8px;"><span class="muted">auto-added claus
|
||||||
}
|
}
|
||||||
scrollMessagesToBottom(true);
|
scrollMessagesToBottom(true);
|
||||||
} else {
|
} else {
|
||||||
ok = await appendFinalAssistant(payload.message, chatStream || extractWsAssistantText(payload.message || {}));
|
ok = await appendFinalAssistant(
|
||||||
|
payload.message,
|
||||||
|
extractWsAssistantText(payload.message || {}) || chatStream,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
if (!ok) _markStreamTerminal("end", t("chat.status.end"));
|
if (!ok) _markStreamTerminal("end", t("chat.status.end"));
|
||||||
chatStream = "";
|
chatStream = "";
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue