Collapse handoff turns in the transcript instead of streaming them

This commit is contained in:
Ian Gulliver
2026-10-15 10:30:24 -07:00
parent c777409dc1
commit e0d292fd75
7 changed files with 85 additions and 31 deletions
+36 -14
View File
@@ -159,6 +159,7 @@ export function mountChat(root, src, makeHead) {
lastMessage: undefined,
turnStart: undefined,
quiet: false,
handoff: undefined,
root: src.root(),
taskId: src.taskId(),
watchReport: (el, itemId) => {
@@ -192,7 +193,13 @@ export function mountChat(root, src, makeHead) {
transcript.append(el);
}
};
const put = (el) => putBefore(el, pending[0]?.el);
const put = (el) => {
if (ctx.handoff) {
ctx.handoff.body.append(el);
return;
}
putBefore(el, pending[0]?.el);
};
const renderHead = () => {
setRunning(src.session()?.state === "running");
if (!src.key()) {
@@ -322,6 +329,9 @@ export function mountChat(root, src, makeHead) {
stream = undefined;
};
const appendDelta = (ev) => {
if (ctx.handoff) {
return;
}
if (!stream) {
stream = { el: h("div", { class: "msg agent" }), text: "", timer: 0 };
ctx.run = undefined;
@@ -344,18 +354,25 @@ export function mountChat(root, src, makeHead) {
}
}, 100);
};
const foldTurn = (start, end) => {
const summary = h("summary");
summary.append(...end.childNodes);
const openHandoff = () => {
const summary = h("summary", {}, "writing a handoff…");
const body = h("div", { class: "folded-turn" });
start.before(h("details", { class: "note" }, summary, body));
let node = start;
while (node && node !== spinner) {
const next = node.nextSibling;
body.append(node);
node = next;
put(h("details", { class: "note" }, summary, body));
ctx.handoff = { summary, body };
};
const closeHandoff = (end) => {
const handoff = ctx.handoff;
if (!handoff) {
return undefined;
}
return summary;
ctx.handoff = undefined;
if (end) {
replace(handoff.summary, ...end.childNodes);
}
else {
replace(handoff.summary, "handoff not written");
}
return handoff.summary;
};
const appendEvents = (events) => {
const pinned = transcript.scrollHeight - transcript.scrollTop - transcript.clientHeight < 80;
@@ -395,11 +412,16 @@ export function mountChat(root, src, makeHead) {
continue;
}
ctx.run = undefined;
if (ev.kind === "turn.ended" && p["handoff"] === true && ctx.turnStart?.parentNode === transcript) {
addAnchor(ev.seq, foldTurn(ctx.turnStart, el), true);
ctx.turnStart = undefined;
if (ev.kind === "turn.ended" && p["handoff"] === true && ctx.handoff) {
addAnchor(ev.seq, closeHandoff(el), true);
continue;
}
if (ev.kind === "turn.ended" || (ev.kind === "user.message" && p["mid_turn"] !== true)) {
closeHandoff(undefined);
}
if (ev.kind === "user.message" && p["handoff"] === true) {
openHandoff();
}
if (ev.kind === "turn.ended" && ctx.quiet && ctx.turnStart?.parentNode === transcript) {
let node = ctx.turnStart;
while (node && node !== spinner) {