fix(tui): show background subagent notifications immediately when idle (#1242)

This commit is contained in:
Devansh Jain
2026-03-03 12:36:55 -08:00
committed by GitHub
parent 5bd09dd17d
commit 200390ca33
3 changed files with 232 additions and 17 deletions

View File

@@ -276,7 +276,10 @@ import {
flushEligibleLinesBeforeReentry,
shouldClearCompletedSubagentsOnTurnStart,
} from "./helpers/subagentTurnStart";
import { extractTaskNotificationsForDisplay } from "./helpers/taskNotifications";
import {
appendTaskNotificationEventsToBuffer,
extractTaskNotificationsForDisplay,
} from "./helpers/taskNotifications";
import {
getRandomPastTenseVerb,
getRandomThinkingVerb,
@@ -1894,23 +1897,19 @@ export default function App({
);
}, [isExecutingTool]);
// Ref indirection: refreshDerived is declared later in the component but
// appendTaskNotificationEvents needs to call it. Using a ref avoids a
// forward-declaration error while keeping the deps array empty.
const refreshDerivedRef = useRef<(() => void) | null>(null);
const appendTaskNotificationEvents = useCallback(
(summaries: string[]): boolean => {
if (summaries.length === 0) return false;
for (const summary of summaries) {
const eventId = uid("event");
buffersRef.current.byId.set(eventId, {
kind: "event",
id: eventId,
eventType: "task_notification",
eventData: {},
phase: "finished",
summary,
});
buffersRef.current.order.push(eventId);
}
return true;
},
(summaries: string[]): boolean =>
appendTaskNotificationEventsToBuffer(
summaries,
buffersRef.current,
() => uid("event"),
() => refreshDerivedRef.current?.(),
),
[],
);
@@ -2702,6 +2701,7 @@ export default function App({
setLines(newLines);
commitEligibleLines(b);
}, [commitEligibleLines]);
refreshDerivedRef.current = refreshDerived;
const recordCommandReminder = useCallback((event: CommandFinishedEvent) => {
const input = event.input.trim();

View File

@@ -106,6 +106,47 @@ export function extractTaskNotificationsForDisplay(message: string): {
return { notifications, cleanedText };
}
// ============================================================================
// Buffer Helpers
// ============================================================================
/**
* Append task-notification events to a transcript buffer and flush.
*
* This is the pure-function core of App.tsx's `appendTaskNotificationEvents`
* useCallback. Extracting it here makes the behavioral contract testable:
* buffer writes MUST be followed by a flush so that notifications from
* background subagent onComplete callbacks (which run outside React's render
* cycle) appear immediately instead of waiting for the next unrelated render.
*/
export type NotificationBuffer = Pick<
import("./accumulator").Buffers,
"byId" | "order"
>;
export function appendTaskNotificationEventsToBuffer(
summaries: string[],
buffer: NotificationBuffer,
generateId: () => string,
flush?: () => void,
): boolean {
if (summaries.length === 0) return false;
for (const summary of summaries) {
const eventId = generateId();
buffer.byId.set(eventId, {
kind: "event",
id: eventId,
eventType: "task_notification",
eventData: {},
phase: "finished",
summary,
});
buffer.order.push(eventId);
}
flush?.();
return true;
}
/**
* Format multiple notifications as XML string.
* @deprecated Use formatTaskNotification and queue individually