highlight active subagents

This commit is contained in:
Ian Gulliver
2026-10-17 08:21:11 -07:00
parent 53699cc579
commit b43cffab6d
13 changed files with 374 additions and 235 deletions
+2 -1
View File
@@ -57,7 +57,7 @@ export function currentSession(taskId) {
return state.sessions.get(taskId)?.findLast((s) => s.state !== "completed") ?? state.sessions.get(taskId)?.at(-1);
}
const workingTitles = {
running: "The agent is mid-turn",
running: "The agent is mid-turn or its subagents are running",
review_pending: "A review of the turn's work is about to start",
reviewing: "A review is running",
discarding: "Discarding",
@@ -438,6 +438,7 @@ export function handleEvent(ev) {
break;
case "session.created":
case "session.state":
case "session.subagents":
case "session.model":
if (ev.owner) {
void loadLeadSessions();
+1 -1
View File
File diff suppressed because one or more lines are too long
+29 -6
View File
@@ -231,7 +231,10 @@ export function mountChat(root, src, makeHead) {
transcript.insertBefore(el, scanner);
}
};
const syncRunning = () => setRunning(src.session()?.state === "running" && ctx.open);
const syncRunning = () => {
setRunning(src.session()?.state === "running" && ctx.open);
transcript.classList.toggle("subagents", (src.session()?.subagents ?? 0) > 0);
};
const release = () => {
const buffered = ctx.held;
if (!buffered) {
@@ -425,6 +428,16 @@ export function mountChat(root, src, makeHead) {
claudeTaskStatus(p, ctx);
continue;
}
if (ev.kind === "session.subagents") {
if (p["subagents"] === 0) {
for (const sub of ctx.subagents.values()) {
if (sub.el.classList.contains("active")) {
endSubagent(sub, "error", "ended with its process");
}
}
}
continue;
}
if (!sink && ((ev.kind === "agent.message" && !parent) || ev.kind === "turn.ended" || ev.kind === "user.message")) {
dropStream();
}
@@ -817,9 +830,10 @@ function renderSubagent(p, ctx) {
const result = h("div", { class: "tool-result pending" }, "running…");
const body = h("div", { class: "subagent-body" });
const tag = subagentTag(parentOf(p), ctx);
const el = h("details", { class: "tool subagent" }, h("summary", {}, tag, h("span", { class: "tool-name" }, `${p["name"]} ${type}`), " ", h("span", { class: "tool-brief" }, description), " ", status), markdown(String(input["prompt"] ?? ""), "text md subagent-prompt", { taskId: ctx.taskId }), body, result);
ctx.tools.set(id, result);
ctx.subagents.set(id, { type, description, status, body, run: undefined });
return h("details", { class: "tool subagent" }, h("summary", {}, tag, h("span", { class: "tool-name" }, `${p["name"]} ${type}`), " ", h("span", { class: "tool-brief" }, description), " ", status), markdown(String(input["prompt"] ?? ""), "text md subagent-prompt", { taskId: ctx.taskId }), body, result);
ctx.subagents.set(id, { el, type, description, status, body, run: undefined });
return el;
}
function claudeTaskStatus(p, ctx) {
if (p["type"] !== "system") {
@@ -831,17 +845,26 @@ function claudeTaskStatus(p, ctx) {
}
switch (p["subtype"]) {
case "task_started":
sub.el.classList.add("active");
replace(sub.status, p["is_backgrounded"] === true ? "running in the background" : "running");
return;
case "task_progress":
replace(sub.status, String(p["description"] ?? "running"));
return;
case "task_notification":
sub.status.className = `subagent-status ${p["status"] === "completed" ? "done" : "error"}`;
replace(sub.status, String(p["status"]));
endSubagent(sub, p["status"] === "completed" ? "done" : "error", String(p["status"]));
return;
}
}
function endSubagent(sub, kind, status) {
sub.el.classList.remove("active");
sub.status.className = `subagent-status ${kind}`;
replace(sub.status, status);
for (const result of sub.body.querySelectorAll(".tool-result.pending")) {
result.className = "tool-result unfinished";
replace(result, "no result");
}
}
function markReview(button, r) {
const n = findingCount(r);
button.classList.toggle("attention", n > 0);
@@ -953,7 +976,7 @@ function renderEvent(ev, ctx) {
}
case "turn.ended":
for (const result of ctx.tools.values()) {
if (result.classList.contains("pending")) {
if (result.classList.contains("pending") && !result.closest(".tool.subagent.active")) {
result.className = "tool-result unfinished";
replace(result, "no result");
}
+1 -1
View File
File diff suppressed because one or more lines are too long