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
@@ -1409,7 +1409,7 @@ components:
AgentSession: AgentSession:
type: object type: object
required: [id, task_id, owner, vendor, model, vendor_session_id, state, role, created_by, created_at] required: [id, task_id, owner, vendor, model, vendor_session_id, state, role, created_by, created_at, subagents]
properties: properties:
id: { type: string } id: { type: string }
task_id: { type: string, description: Empty for a lead session. } task_id: { type: string, description: Empty for a lead session. }
@@ -1426,6 +1426,7 @@ components:
created_at: { type: string, format: date-time } created_at: { type: string, format: date-time }
forked_from: { type: string, description: "A fork's first start resumes this vendor session, cut at forked_at, under a new vendor_session_id; absent for other sessions." } forked_from: { type: string, description: "A fork's first start resumes this vendor session, cut at forked_at, under a new vendor_session_id; absent for other sessions." }
forked_at: { type: string, description: "With forked_from: the vendor's id of the last thing the fork keeps, a message for Claude Code, a turn for Codex." } forked_at: { type: string, description: "With forked_from: the vendor's id of the last thing the fork keeps, a message for Claude Code, a turn for Codex." }
subagents: { type: integer, description: "How many of the agent's subagents are running, background ones included after the turn that started them has ended." }
SessionCreate: SessionCreate:
type: object type: object
required: [prompt] required: [prompt]
+5 -3
View File
File diff suppressed because one or more lines are too long
+47 -2
View File
@@ -95,6 +95,7 @@ type live struct {
wantCompact bool wantCompact bool
rateLimited map[string]any rateLimited map[string]any
lastText string lastText string
subagents map[string]bool
contextModel string contextModel string
contextUsed int contextUsed int
@@ -321,6 +322,17 @@ func (m *Manager) Recover(ctx context.Context) error {
return err return err
} }
stale, err := m.store.SessionsWithSubagents(ctx)
if err != nil {
return err
}
for _, sess := range stale {
err = m.store.SetSubagents(ctx, sess.Id, 0, "byre")
if err != nil {
return err
}
}
sessions, err := m.store.SessionsInState(ctx, api.AgentSessionStateRunning) sessions, err := m.store.SessionsInState(ctx, api.AgentSessionStateRunning)
if err != nil { if err != nil {
return err return err
@@ -686,6 +698,7 @@ func (m *Manager) startLive(ctx context.Context, sess api.AgentSession, task api
taskID: task.Id, taskID: task.Id,
projectID: task.ProjectId, projectID: task.ProjectId,
owner: sess.Owner, owner: sess.Owner,
subagents: map[string]bool{},
} }
g, err := m.taskGuidance(ctx, task) g, err := m.taskGuidance(ctx, task)
@@ -764,9 +777,17 @@ func (m *Manager) pump(l *live) {
m.mu.Lock() m.mu.Lock()
delete(m.live, l.id) delete(m.live, l.id)
quiet := m.closing || l.stopping closing := m.closing
quiet := closing || l.stopping
m.mu.Unlock() m.mu.Unlock()
if len(l.subagents) > 0 && !closing {
err := m.store.SetSubagents(ctx, l.id, 0, "byre")
if err != nil {
slog.Error("clear exited session's subagents", "session", l.id, "err", err)
}
}
if quiet { if quiet {
m.logUndelivered(ctx, l.id, "agent process ended while closing or stopping") m.logUndelivered(ctx, l.id, "agent process ended while closing or stopping")
return return
@@ -934,7 +955,31 @@ func (m *Manager) handle(ctx context.Context, l *live, ev claude.Event) error {
if err != nil { if err != nil {
return err return err
} }
return m.emit(ctx, l, agentActor, "agent.vendor", raw) err = m.emit(ctx, l, agentActor, "agent.vendor", raw)
if err != nil {
return err
}
return m.trackSubagents(ctx, l, raw)
}
func (m *Manager) trackSubagents(ctx context.Context, l *live, raw map[string]any) error {
if raw["type"] != "system" {
return nil
}
id, _ := raw["task_id"].(string)
n := len(l.subagents)
switch raw["subtype"] {
case "task_started":
if raw["task_type"] == "local_agent" {
l.subagents[id] = true
}
case "task_notification":
delete(l.subagents, id)
}
if len(l.subagents) == n {
return nil
}
return m.store.SetSubagents(ctx, l.id, len(l.subagents), agentActor)
} }
type endResult struct { type endResult struct {
+211 -207
View File
@@ -575,6 +575,9 @@ type AgentSession struct {
Role AgentSessionRole `json:"role"` Role AgentSessionRole `json:"role"`
State AgentSessionState `json:"state"` State AgentSessionState `json:"state"`
// Subagents How many of the agent's subagents are running, background ones included after the turn that started them has ended.
Subagents int `json:"subagents"`
// TaskId Empty for a lead session. // TaskId Empty for a lead session.
TaskId string `json:"task_id"` TaskId string `json:"task_id"`
@@ -3496,213 +3499,214 @@ var swaggerSpec = []string{
"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",
"uSK4Ozxjb/vP3n1+r/YeHz/+vwAAAP//", "vO4zjAzI0/y3y19+DrYcKAttY/yS59DPvPj67OnTTvxSQlFBc0bK6kDSp7EMy3zTdKlljDY8ilC+XBHc",
"HZ6xt/1n7z6/V3uPjx//XwAAAP//",
} }
// decodeSpec returns the embedded OpenAPI spec as raw JSON bytes, // decodeSpec returns the embedded OpenAPI spec as raw JSON bytes,
+31 -2
View File
@@ -464,6 +464,8 @@ ALTER TABLE tasks ADD COLUMN merge_after_review_target TEXT NOT NULL DEFAULT '';
ALTER TABLE inbox_responses ADD COLUMN images TEXT NOT NULL DEFAULT '[]'; ALTER TABLE inbox_responses ADD COLUMN images TEXT NOT NULL DEFAULT '[]';
`}, {"add-lead-note-projects", ` `}, {"add-lead-note-projects", `
ALTER TABLE lead_notes ADD COLUMN project_id TEXT NOT NULL DEFAULT ''; ALTER TABLE lead_notes ADD COLUMN project_id TEXT NOT NULL DEFAULT '';
`}, {"add-session-subagents", `
ALTER TABLE agent_sessions ADD COLUMN subagents INTEGER NOT NULL DEFAULT 0;
`}} `}}
const ( const (
@@ -1604,6 +1606,32 @@ func (s *Store) transitionSession(ctx context.Context, id string, from *api.Agen
}) })
} }
func (s *Store) SessionsWithSubagents(ctx context.Context) ([]api.AgentSession, error) {
return s.querySessions(ctx, `SELECT `+sessionColumns+` FROM agent_sessions WHERE subagents > 0`)
}
func (s *Store) SetSubagents(ctx context.Context, id string, n int, actor string) error {
return s.write(ctx, func(tx *sql.Tx, emit emitFunc) error {
taskID := ""
projectID := ""
owner := ""
err := tx.QueryRowContext(ctx, `
SELECT coalesce(t.id, ''), coalesce(t.project_id, ''), s.owner FROM agent_sessions s LEFT JOIN tasks t ON t.id = s.task_id WHERE s.id = ?
`, id).Scan(&taskID, &projectID, &owner)
if errors.Is(err, sql.ErrNoRows) {
return ErrNotFound
}
if err != nil {
return err
}
_, err = tx.ExecContext(ctx, `UPDATE agent_sessions SET subagents = ? WHERE id = ?`, n, id)
if err != nil {
return err
}
return emit(sessionEvent("session.subagents", projectID, taskID, owner, id, actor, map[string]any{"subagents": n}))
})
}
func (s *Store) SessionStarted(ctx context.Context, sessionID string) (bool, error) { func (s *Store) SessionStarted(ctx context.Context, sessionID string) (bool, error) {
started := false started := false
err := s.db.QueryRowContext(ctx, `SELECT EXISTS (SELECT 1 FROM events WHERE agent_session_id = ? AND kind = 'user.message')`, sessionID).Scan(&started) err := s.db.QueryRowContext(ctx, `SELECT EXISTS (SELECT 1 FROM events WHERE agent_session_id = ? AND kind = 'user.message')`, sessionID).Scan(&started)
@@ -1791,6 +1819,7 @@ const taskStateExpr = `CASE
ELSE coalesce(( ELSE coalesce((
SELECT CASE SELECT CASE
WHEN s.state != 'idle' THEN s.state WHEN s.state != 'idle' THEN s.state
WHEN s.subagents > 0 THEN 'running'
WHEN EXISTS (SELECT 1 FROM reviews r WHERE r.task_id = tasks.id AND r.state = 'running') THEN 'reviewing' WHEN EXISTS (SELECT 1 FROM reviews r WHERE r.task_id = tasks.id AND r.state = 'running') THEN 'reviewing'
WHEN tasks.review_requests > 0 THEN 'review_pending' WHEN tasks.review_requests > 0 THEN 'review_pending'
ELSE 'idle' ELSE 'idle'
@@ -1856,7 +1885,7 @@ const projectColumns = `id, name, repo_path, default_branch, remote, created_by,
const taskColumns = `id, project_id, title, merge_title, branch, worktree_path, base_ref, port_base, color, status, created_by, created_at, closed_at, merge_commit, todo_item, forked_from, forked_at, merge_queued_at, workflow_name, workflow_run, workflow_stage, parent_task_id, parent_merge_by, merge_after_review_by != '', state, merging_by != '' OR finalizing != '', discard_state != '' AND status = 'active', (SELECT coalesce(max(seq), 0) FROM events WHERE events.task_id = tasks.id AND kind = 'turn.ended' AND json_extract(payload, '$.summary') = 1)` const taskColumns = `id, project_id, title, merge_title, branch, worktree_path, base_ref, port_base, color, status, created_by, created_at, closed_at, merge_commit, todo_item, forked_from, forked_at, merge_queued_at, workflow_name, workflow_run, workflow_stage, parent_task_id, parent_merge_by, merge_after_review_by != '', state, merging_by != '' OR finalizing != '', discard_state != '' AND status = 'active', (SELECT coalesce(max(seq), 0) FROM events WHERE events.task_id = tasks.id AND kind = 'turn.ended' AND json_extract(payload, '$.summary') = 1)`
const sessionColumns = `id, coalesce(task_id, ''), owner, vendor, model, vendor_session_id, state, role, created_by, created_at, forked_from, forked_at` const sessionColumns = `id, coalesce(task_id, ''), owner, vendor, model, vendor_session_id, state, role, created_by, created_at, forked_from, forked_at, subagents`
type scanner interface { type scanner interface {
Scan(dest ...any) error Scan(dest ...any) error
@@ -1946,7 +1975,7 @@ func scanSession(row scanner) (api.AgentSession, error) {
createdAt := "" createdAt := ""
forkedFrom := "" forkedFrom := ""
forkedAt := "" forkedAt := ""
err := row.Scan(&sess.Id, &sess.TaskId, &sess.Owner, &vendor, &sess.Model, &sess.VendorSessionId, &state, &role, &sess.CreatedBy, &createdAt, &forkedFrom, &forkedAt) err := row.Scan(&sess.Id, &sess.TaskId, &sess.Owner, &vendor, &sess.Model, &sess.VendorSessionId, &state, &role, &sess.CreatedBy, &createdAt, &forkedFrom, &forkedAt, &sess.Subagents)
if err != nil { if err != nil {
return sess, err return sess, err
} }
+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); return state.sessions.get(taskId)?.findLast((s) => s.state !== "completed") ?? state.sessions.get(taskId)?.at(-1);
} }
const workingTitles = { 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", review_pending: "A review of the turn's work is about to start",
reviewing: "A review is running", reviewing: "A review is running",
discarding: "Discarding", discarding: "Discarding",
@@ -438,6 +438,7 @@ export function handleEvent(ev) {
break; break;
case "session.created": case "session.created":
case "session.state": case "session.state":
case "session.subagents":
case "session.model": case "session.model":
if (ev.owner) { if (ev.owner) {
void loadLeadSessions(); 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); 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 release = () => {
const buffered = ctx.held; const buffered = ctx.held;
if (!buffered) { if (!buffered) {
@@ -425,6 +428,16 @@ export function mountChat(root, src, makeHead) {
claudeTaskStatus(p, ctx); claudeTaskStatus(p, ctx);
continue; 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")) { if (!sink && ((ev.kind === "agent.message" && !parent) || ev.kind === "turn.ended" || ev.kind === "user.message")) {
dropStream(); dropStream();
} }
@@ -817,9 +830,10 @@ function renderSubagent(p, ctx) {
const result = h("div", { class: "tool-result pending" }, "running…"); const result = h("div", { class: "tool-result pending" }, "running…");
const body = h("div", { class: "subagent-body" }); const body = h("div", { class: "subagent-body" });
const tag = subagentTag(parentOf(p), ctx); 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.tools.set(id, result);
ctx.subagents.set(id, { type, description, status, body, run: undefined }); ctx.subagents.set(id, { el, 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); return el;
} }
function claudeTaskStatus(p, ctx) { function claudeTaskStatus(p, ctx) {
if (p["type"] !== "system") { if (p["type"] !== "system") {
@@ -831,17 +845,26 @@ function claudeTaskStatus(p, ctx) {
} }
switch (p["subtype"]) { switch (p["subtype"]) {
case "task_started": case "task_started":
sub.el.classList.add("active");
replace(sub.status, p["is_backgrounded"] === true ? "running in the background" : "running"); replace(sub.status, p["is_backgrounded"] === true ? "running in the background" : "running");
return; return;
case "task_progress": case "task_progress":
replace(sub.status, String(p["description"] ?? "running")); replace(sub.status, String(p["description"] ?? "running"));
return; return;
case "task_notification": case "task_notification":
sub.status.className = `subagent-status ${p["status"] === "completed" ? "done" : "error"}`; endSubagent(sub, p["status"] === "completed" ? "done" : "error", String(p["status"]));
replace(sub.status, String(p["status"]));
return; 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) { function markReview(button, r) {
const n = findingCount(r); const n = findingCount(r);
button.classList.toggle("attention", n > 0); button.classList.toggle("attention", n > 0);
@@ -953,7 +976,7 @@ function renderEvent(ev, ctx) {
} }
case "turn.ended": case "turn.ended":
for (const result of ctx.tools.values()) { 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"; result.className = "tool-result unfinished";
replace(result, "no result"); replace(result, "no result");
} }
+1 -1
View File
File diff suppressed because one or more lines are too long
+2
View File
@@ -1334,6 +1334,8 @@ export interface components {
forked_from?: string; forked_from?: string;
/** @description With forked_from: the vendor's id of the last thing the fork keeps, a message for Claude Code, a turn for Codex. */ /** @description With forked_from: the vendor's id of the last thing the fork keeps, a message for Claude Code, a turn for Codex. */
forked_at?: string; forked_at?: string;
/** @description How many of the agent's subagents are running, background ones included after the turn that started them has ended. */
subagents: number;
}; };
SessionCreate: { SessionCreate: {
prompt: string; prompt: string;
+2 -1
View File
@@ -89,7 +89,7 @@ export function currentSession(taskId: string): AgentSession | undefined {
} }
const workingTitles: Partial<Record<Task["state"], string>> = { const workingTitles: Partial<Record<Task["state"], string>> = {
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", review_pending: "A review of the turn's work is about to start",
reviewing: "A review is running", reviewing: "A review is running",
discarding: "Discarding", discarding: "Discarding",
@@ -518,6 +518,7 @@ export function handleEvent(ev: ByreEvent): void {
break; break;
case "session.created": case "session.created":
case "session.state": case "session.state":
case "session.subagents":
case "session.model": case "session.model":
if (ev.owner) { if (ev.owner) {
void loadLeadSessions(); void loadLeadSessions();
+32 -7
View File
@@ -65,6 +65,7 @@ type Run = {
}; };
type Subagent = { type Subagent = {
el: HTMLElement;
type: string; type: string;
description: string; description: string;
status: HTMLElement; status: HTMLElement;
@@ -308,7 +309,10 @@ export function mountChat(root: HTMLElement, src: ChatSource, makeHead: (rerende
transcript.insertBefore(el, scanner); 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 release = () => {
const buffered = ctx.held; const buffered = ctx.held;
if (!buffered) { if (!buffered) {
@@ -515,6 +519,16 @@ export function mountChat(root: HTMLElement, src: ChatSource, makeHead: (rerende
claudeTaskStatus(p, ctx); claudeTaskStatus(p, ctx);
continue; 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")) { if (!sink && ((ev.kind === "agent.message" && !parent) || ev.kind === "turn.ended" || ev.kind === "user.message")) {
dropStream(); dropStream();
} }
@@ -947,9 +961,7 @@ function renderSubagent(p: Record<string, unknown>, ctx: Ctx): HTMLElement {
const result = h("div", { class: "tool-result pending" }, "running…"); const result = h("div", { class: "tool-result pending" }, "running…");
const body = h("div", { class: "subagent-body" }); const body = h("div", { class: "subagent-body" });
const tag = subagentTag(parentOf(p), ctx); const tag = subagentTag(parentOf(p), ctx);
ctx.tools.set(id, result); const el = h(
ctx.subagents.set(id, { type, description, status, body, run: undefined });
return h(
"details", "details",
{ class: "tool subagent" }, { class: "tool subagent" },
h("summary", {}, tag, h("span", { class: "tool-name" }, `${p["name"]} ${type}`), " ", h("span", { class: "tool-brief" }, description), " ", status), h("summary", {}, tag, h("span", { class: "tool-name" }, `${p["name"]} ${type}`), " ", h("span", { class: "tool-brief" }, description), " ", status),
@@ -957,6 +969,9 @@ function renderSubagent(p: Record<string, unknown>, ctx: Ctx): HTMLElement {
body, body,
result, result,
); );
ctx.tools.set(id, result);
ctx.subagents.set(id, { el, type, description, status, body, run: undefined });
return el;
} }
function claudeTaskStatus(p: Record<string, unknown>, ctx: Ctx): void { function claudeTaskStatus(p: Record<string, unknown>, ctx: Ctx): void {
@@ -969,18 +984,28 @@ function claudeTaskStatus(p: Record<string, unknown>, ctx: Ctx): void {
} }
switch (p["subtype"]) { switch (p["subtype"]) {
case "task_started": case "task_started":
sub.el.classList.add("active");
replace(sub.status, p["is_backgrounded"] === true ? "running in the background" : "running"); replace(sub.status, p["is_backgrounded"] === true ? "running in the background" : "running");
return; return;
case "task_progress": case "task_progress":
replace(sub.status, String(p["description"] ?? "running")); replace(sub.status, String(p["description"] ?? "running"));
return; return;
case "task_notification": case "task_notification":
sub.status.className = `subagent-status ${p["status"] === "completed" ? "done" : "error"}`; endSubagent(sub, p["status"] === "completed" ? "done" : "error", String(p["status"]));
replace(sub.status, String(p["status"]));
return; return;
} }
} }
function endSubagent(sub: Subagent, kind: string, status: string): void {
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 as HTMLElement, "no result");
}
}
function markReview(button: HTMLElement, r: Review): void { function markReview(button: HTMLElement, r: Review): void {
const n = findingCount(r); const n = findingCount(r);
button.classList.toggle("attention", n > 0); button.classList.toggle("attention", n > 0);
@@ -1111,7 +1136,7 @@ function renderEvent(ev: ByreEvent, ctx: Ctx): HTMLElement | null {
case "turn.ended": case "turn.ended":
for (const result of ctx.tools.values()) { 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"; result.className = "tool-result unfinished";
replace(result, "no result"); replace(result, "no result");
} }
+9 -3
View File
@@ -693,7 +693,9 @@ button.compact.danger {
} }
.spinner, .spinner,
.transcript.running :is(.tool, .tool-group):has(.tool-result.pending) > summary::after { .transcript.running :is(.tool, .tool-group):has(.tool-result.pending) > summary::after,
.transcript:is(.running, .subagents) .tool.subagent.active > summary::after,
.transcript.subagents .tool.subagent.active :is(.tool, .tool-group):has(.tool-result.pending) > summary::after {
--frames: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='50' height='10'%3E%3Cg%3E%3Ccircle cx='1' cy='1' r='1'/%3E%3Ccircle cx='1' cy='5' r='1'/%3E%3Ccircle cx='4' cy='1' r='1'/%3E%3Ccircle cx='6' cy='1' r='1'/%3E%3Ccircle cx='9' cy='1' r='1'/%3E%3Ccircle cx='9' cy='5' r='1'/%3E%3Ccircle cx='11' cy='1' r='1'/%3E%3Ccircle cx='14' cy='1' r='1'/%3E%3Ccircle cx='14' cy='5' r='1'/%3E%3Ccircle cx='14' cy='9' r='1'/%3E%3Ccircle cx='19' cy='1' r='1'/%3E%3Ccircle cx='19' cy='5' r='1'/%3E%3Ccircle cx='19' cy='9' r='1'/%3E%3Ccircle cx='21' cy='9' r='1'/%3E%3Ccircle cx='24' cy='1' r='1'/%3E%3Ccircle cx='24' cy='5' r='1'/%3E%3Ccircle cx='24' cy='9' r='1'/%3E%3Ccircle cx='26' cy='9' r='1'/%3E%3Ccircle cx='29' cy='5' r='1'/%3E%3Ccircle cx='29' cy='9' r='1'/%3E%3Ccircle cx='31' cy='5' r='1'/%3E%3Ccircle cx='31' cy='9' r='1'/%3E%3Ccircle cx='34' cy='9' r='1'/%3E%3Ccircle cx='36' cy='1' r='1'/%3E%3Ccircle cx='36' cy='5' r='1'/%3E%3Ccircle cx='36' cy='9' r='1'/%3E%3Ccircle cx='39' cy='9' r='1'/%3E%3Ccircle cx='41' cy='1' r='1'/%3E%3Ccircle cx='41' cy='5' r='1'/%3E%3Ccircle cx='41' cy='9' r='1'/%3E%3Ccircle cx='46' cy='1' r='1'/%3E%3Ccircle cx='46' cy='5' r='1'/%3E%3Ccircle cx='46' cy='9' r='1'/%3E%3Ccircle cx='49' cy='1' r='1'/%3E%3C/g%3E%3C/svg%3E"); --frames: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='50' height='10'%3E%3Cg%3E%3Ccircle cx='1' cy='1' r='1'/%3E%3Ccircle cx='1' cy='5' r='1'/%3E%3Ccircle cx='4' cy='1' r='1'/%3E%3Ccircle cx='6' cy='1' r='1'/%3E%3Ccircle cx='9' cy='1' r='1'/%3E%3Ccircle cx='9' cy='5' r='1'/%3E%3Ccircle cx='11' cy='1' r='1'/%3E%3Ccircle cx='14' cy='1' r='1'/%3E%3Ccircle cx='14' cy='5' r='1'/%3E%3Ccircle cx='14' cy='9' r='1'/%3E%3Ccircle cx='19' cy='1' r='1'/%3E%3Ccircle cx='19' cy='5' r='1'/%3E%3Ccircle cx='19' cy='9' r='1'/%3E%3Ccircle cx='21' cy='9' r='1'/%3E%3Ccircle cx='24' cy='1' r='1'/%3E%3Ccircle cx='24' cy='5' r='1'/%3E%3Ccircle cx='24' cy='9' r='1'/%3E%3Ccircle cx='26' cy='9' r='1'/%3E%3Ccircle cx='29' cy='5' r='1'/%3E%3Ccircle cx='29' cy='9' r='1'/%3E%3Ccircle cx='31' cy='5' r='1'/%3E%3Ccircle cx='31' cy='9' r='1'/%3E%3Ccircle cx='34' cy='9' r='1'/%3E%3Ccircle cx='36' cy='1' r='1'/%3E%3Ccircle cx='36' cy='5' r='1'/%3E%3Ccircle cx='36' cy='9' r='1'/%3E%3Ccircle cx='39' cy='9' r='1'/%3E%3Ccircle cx='41' cy='1' r='1'/%3E%3Ccircle cx='41' cy='5' r='1'/%3E%3Ccircle cx='41' cy='9' r='1'/%3E%3Ccircle cx='46' cy='1' r='1'/%3E%3Ccircle cx='46' cy='5' r='1'/%3E%3Ccircle cx='46' cy='9' r='1'/%3E%3Ccircle cx='49' cy='1' r='1'/%3E%3C/g%3E%3C/svg%3E");
flex-shrink: 0; flex-shrink: 0;
width: 5px; width: 5px;
@@ -1001,7 +1003,9 @@ button.compact.danger {
} }
} }
.transcript.running :is(.tool, .tool-group):has(.tool-result.pending) { .transcript.running :is(.tool, .tool-group):has(.tool-result.pending),
.transcript:is(.running, .subagents) .tool.subagent.active,
.transcript.subagents .tool.subagent.active :is(.tool, .tool-group):has(.tool-result.pending) {
border-color: var(--task); border-color: var(--task);
@starting-style { @starting-style {
@@ -1009,7 +1013,9 @@ button.compact.danger {
} }
} }
.transcript.running :is(.tool, .tool-group):has(.tool-result.pending) > summary::after { .transcript.running :is(.tool, .tool-group):has(.tool-result.pending) > summary::after,
.transcript:is(.running, .subagents) .tool.subagent.active > summary::after,
.transcript.subagents .tool.subagent.active :is(.tool, .tool-group):has(.tool-result.pending) > summary::after {
content: ""; content: "";
display: inline-block; display: inline-block;
vertical-align: middle; vertical-align: middle;