feat(cli): auto-restart agent on background task completion

When the agent is idle and a backgrounded execution completes, the
completion output is now automatically submitted as a new turn
instead of being silently dropped. Uses the same InjectionService
listener pattern as user steering hints.
This commit is contained in:
Adam Weidman
2026-03-16 15:55:46 -04:00
parent bb80f5fd67
commit 6880859fdb
+35 -7
View File
@@ -83,6 +83,7 @@ import {
ProjectIdRequiredError,
CoreToolCallStatus,
buildUserSteeringHintPrompt,
formatBackgroundCompletionForModel,
logBillingEvent,
ApiKeyUpdatedEvent,
type InjectionSource,
@@ -1078,6 +1079,8 @@ Logging in with Google... Restarting Gemini CLI to continue.
const pendingHintsRef = useRef<string[]>([]);
const [pendingHintCount, setPendingHintCount] = useState(0);
const pendingBgCompletionsRef = useRef<string[]>([]);
const [pendingBgCompletionCount, setPendingBgCompletionCount] = useState(0);
const consumePendingHints = useCallback(() => {
if (pendingHintsRef.current.length === 0) {
@@ -1090,16 +1093,18 @@ Logging in with Google... Restarting Gemini CLI to continue.
}, []);
useEffect(() => {
const hintListener = (text: string, source: InjectionSource) => {
if (source !== 'user_steering') {
return;
const injectionListener = (text: string, source: InjectionSource) => {
if (source === 'user_steering') {
pendingHintsRef.current.push(text);
setPendingHintCount((prev) => prev + 1);
} else if (source === 'background_completion') {
pendingBgCompletionsRef.current.push(text);
setPendingBgCompletionCount((prev) => prev + 1);
}
pendingHintsRef.current.push(text);
setPendingHintCount((prev) => prev + 1);
};
config.injectionService.onInjection(hintListener);
config.injectionService.onInjection(injectionListener);
return () => {
config.injectionService.offInjection(hintListener);
config.injectionService.offInjection(injectionListener);
};
}, [config]);
@@ -2135,6 +2140,29 @@ Logging in with Google... Restarting Gemini CLI to continue.
pendingHintCount,
]);
useEffect(() => {
if (
!isConfigInitialized ||
streamingState !== StreamingState.Idle ||
!isMcpReady ||
pendingBgCompletionsRef.current.length === 0
) {
return;
}
const bgText = pendingBgCompletionsRef.current.join('\n');
pendingBgCompletionsRef.current = [];
setPendingBgCompletionCount(0);
void submitQuery([{ text: formatBackgroundCompletionForModel(bgText) }]);
}, [
isConfigInitialized,
isMcpReady,
streamingState,
submitQuery,
pendingBgCompletionCount,
]);
const allToolCalls = useMemo(
() =>
pendingHistoryItems