fix(subagents): announce delivery with descendant gating, frozen result refresh, and cron retry (#35080)

Thanks @tyler6204
This commit is contained in:
Tyler Yust
2026-03-05 19:20:24 -08:00
committed by GitHub
parent fa3fafdde5
commit 81b93b9ce0
32 changed files with 3440 additions and 805 deletions

View File

@@ -1,3 +1,4 @@
import { countPendingDescendantRuns } from "../../../agents/subagent-registry.js";
import { loadSessionStore, resolveStorePath } from "../../../config/sessions.js";
import { formatDurationCompact } from "../../../shared/subagents-format.js";
import type { CommandHandlerResult } from "../commands-types.js";
@@ -38,7 +39,7 @@ export function handleSubagentsInfoAction(ctx: SubagentsCommandContext): Command
const lines = [
" Subagent info",
`Status: ${resolveDisplayStatus(run)}`,
`Status: ${resolveDisplayStatus(run, { pendingDescendants: countPendingDescendantRuns(run.childSessionKey) })}`,
`Label: ${formatRunLabel(run)}`,
`Task: ${run.task}`,
`Run: ${run.runId}`,

View File

@@ -1,3 +1,4 @@
import { countPendingDescendantRuns } from "../../../agents/subagent-registry.js";
import { loadSessionStore, resolveStorePath } from "../../../config/sessions.js";
import type { CommandHandlerResult } from "../commands-types.js";
import { sortSubagentRuns } from "../subagents-utils.js";
@@ -16,6 +17,18 @@ export function handleSubagentsListAction(ctx: SubagentsCommandContext): Command
const now = Date.now();
const recentCutoff = now - RECENT_WINDOW_MINUTES * 60_000;
const storeCache: SessionStoreCache = new Map();
const pendingDescendantCache = new Map<string, number>();
const pendingDescendantCount = (sessionKey: string) => {
if (pendingDescendantCache.has(sessionKey)) {
return pendingDescendantCache.get(sessionKey) ?? 0;
}
const pending = Math.max(0, countPendingDescendantRuns(sessionKey));
pendingDescendantCache.set(sessionKey, pending);
return pending;
};
const isActiveRun = (entry: (typeof runs)[number]) =>
!entry.endedAt || pendingDescendantCount(entry.childSessionKey) > 0;
let index = 1;
const mapRuns = (entries: typeof runs, runtimeMs: (entry: (typeof runs)[number]) => number) =>
@@ -34,15 +47,16 @@ export function handleSubagentsListAction(ctx: SubagentsCommandContext): Command
index,
runtimeMs: runtimeMs(entry),
sessionEntry,
pendingDescendants: pendingDescendantCount(entry.childSessionKey),
});
index += 1;
return line;
});
const activeEntries = sorted.filter((entry) => !entry.endedAt);
const activeEntries = sorted.filter((entry) => isActiveRun(entry));
const activeLines = mapRuns(activeEntries, (entry) => now - (entry.startedAt ?? entry.createdAt));
const recentEntries = sorted.filter(
(entry) => !!entry.endedAt && (entry.endedAt ?? 0) >= recentCutoff,
(entry) => !isActiveRun(entry) && !!entry.endedAt && (entry.endedAt ?? 0) >= recentCutoff,
);
const recentLines = mapRuns(
recentEntries,

View File

@@ -1,4 +1,7 @@
import type { SubagentRunRecord } from "../../../agents/subagent-registry.js";
import {
countPendingDescendantRuns,
type SubagentRunRecord,
} from "../../../agents/subagent-registry.js";
import {
extractAssistantText,
resolveInternalSessionKey,
@@ -118,7 +121,15 @@ function resolveModelDisplay(
return combined;
}
export function resolveDisplayStatus(entry: SubagentRunRecord) {
export function resolveDisplayStatus(
entry: SubagentRunRecord,
options?: { pendingDescendants?: number },
) {
const pendingDescendants = Math.max(0, options?.pendingDescendants ?? 0);
if (pendingDescendants > 0) {
const childLabel = pendingDescendants === 1 ? "child" : "children";
return `active (waiting on ${pendingDescendants} ${childLabel})`;
}
const status = formatRunStatus(entry);
return status === "error" ? "failed" : status;
}
@@ -128,12 +139,15 @@ export function formatSubagentListLine(params: {
index: number;
runtimeMs: number;
sessionEntry?: SessionEntry;
pendingDescendants?: number;
}) {
const usageText = formatTokenUsageDisplay(params.sessionEntry);
const label = truncateLine(formatRunLabel(params.entry, { maxLength: 48 }), 48);
const task = formatTaskPreview(params.entry.task);
const runtime = formatDurationCompact(params.runtimeMs);
const status = resolveDisplayStatus(params.entry);
const status = resolveDisplayStatus(params.entry, {
pendingDescendants: params.pendingDescendants,
});
return `${params.index}. ${label} (${resolveModelDisplay(params.sessionEntry, params.entry.model)}, ${runtime}${usageText ? `, ${usageText}` : ""}) ${status}${task.toLowerCase() !== label.toLowerCase() ? ` - ${task}` : ""}`;
}
@@ -191,6 +205,8 @@ export function resolveSubagentTarget(
token,
recentWindowMinutes: RECENT_WINDOW_MINUTES,
label: (entry) => formatRunLabel(entry),
isActive: (entry) =>
!entry.endedAt || Math.max(0, countPendingDescendantRuns(entry.childSessionKey)) > 0,
errors: {
missingTarget: "Missing subagent id.",
invalidIndex: (value) => `Invalid subagent index: ${value}`,
@@ -220,7 +236,9 @@ export function resolveRequesterSessionKey(
): string | undefined {
const commandTarget = params.ctx.CommandTargetSessionKey?.trim();
const commandSession = params.sessionKey?.trim();
const raw = opts?.preferCommandTarget
const shouldPreferCommandTarget =
opts?.preferCommandTarget ?? params.ctx.CommandSource === "native";
const raw = shouldPreferCommandTarget
? commandTarget || commandSession
: commandSession || commandTarget;
if (!raw) {