fix: better message bubble handling

This commit is contained in:
Timothy
2026-03-18 17:49:55 -07:00
parent 9aebeb181e
commit fc643060ce
+25 -6
View File
@@ -630,6 +630,10 @@ export default function Workspace() {
// it was created in (avoids stale-closure when phase change and message
// events arrive in the same React batch).
const queenPhaseRef = useRef<Record<string, string>>({});
// Accumulated queen text across inner_turns within the same iteration.
// Key: `${agentType}:${execution_id}:${iteration}`, value: { [inner_turn]: snapshot }.
// This lets us merge all inner_turn text into one chat bubble per iteration.
const queenIterTextRef = useRef<Record<string, Record<number, string>>>({});
// Timestamp when designingDraft was set — used to enforce minimum spinner duration.
const designingDraftSinceRef = useRef<Record<string, number>>({});
const designingDraftTimerRef = useRef<Record<string, ReturnType<typeof setTimeout>>>({});
@@ -1707,14 +1711,29 @@ export default function Workspace() {
if (isQueen) console.log('[QUEEN] chatMsg:', chatMsg?.id, chatMsg?.content?.slice(0, 50), 'turn:', currentTurn);
if (chatMsg && !suppressQueenMessages) {
// Queen emits multiple client_output_delta / llm_text_delta snapshots
// across iterations and inner tool-loop turns. Build a stable ID that
// groups streaming deltas for the *same* output (same execution +
// iteration + inner_turn) into one bubble, while keeping distinct
// outputs as separate bubbles so earlier text isn't overwritten.
// across iterations and inner tool-loop turns. Merge all inner_turns
// within the same iteration into ONE bubble so the queen's multi-step
// tool loop (text → tool → text → tool → text) appears as one cohesive
// message rather than many small fragments.
if (isQueen && (event.type === "client_output_delta" || event.type === "llm_text_delta") && event.execution_id) {
const iter = event.data?.iteration ?? 0;
const inner = event.data?.inner_turn ?? 0;
chatMsg.id = `queen-stream-${event.execution_id}-${iter}-${inner}`;
const inner = (event.data?.inner_turn as number) ?? 0;
const iterKey = `${agentType}:${event.execution_id}:${iter}`;
// Store the latest snapshot for this inner_turn
if (!queenIterTextRef.current[iterKey]) {
queenIterTextRef.current[iterKey] = {};
}
const snapshot = (event.data?.snapshot as string) || (event.data?.content as string) || "";
queenIterTextRef.current[iterKey][inner] = snapshot;
// Concatenate all inner_turn snapshots in order
const parts = queenIterTextRef.current[iterKey];
const sortedInners = Object.keys(parts).map(Number).sort((a, b) => a - b);
chatMsg.content = sortedInners.map(k => parts[k]).join("\n");
// Single ID per iteration — no inner_turn in the ID
chatMsg.id = `queen-stream-${event.execution_id}-${iter}`;
}
if (isQueen) {
chatMsg.role = role;