mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-05-19 08:14:35 -07:00
refactor(core): move background completion consumption from UI to agent loop
The agent loop in local-executor now listens via onInjection (all sources) instead of onUserHint (steering only), picking up background completions between turns. This removes the separate bg completion useEffect, refs, state, and callback from AppContainer entirely.
This commit is contained in:
@@ -85,7 +85,6 @@ import {
|
||||
buildUserSteeringHintPrompt,
|
||||
logBillingEvent,
|
||||
ApiKeyUpdatedEvent,
|
||||
type InjectionSource,
|
||||
} from '@google/gemini-cli-core';
|
||||
import { validateAuthMethod } from '../config/auth.js';
|
||||
import process from 'node:process';
|
||||
@@ -1078,8 +1077,6 @@ Logging in with Google... Restarting Gemini CLI to continue.
|
||||
|
||||
const pendingHintsRef = useRef<string[]>([]);
|
||||
const [pendingHintCount, setPendingHintCount] = useState(0);
|
||||
const pendingBackgroundCompletionsRef = useRef<string[]>([]);
|
||||
const [pendingBgCompletionCount, setPendingBgCompletionCount] = useState(0);
|
||||
|
||||
const consumePendingHints = useCallback(() => {
|
||||
if (pendingHintsRef.current.length === 0) {
|
||||
@@ -1091,29 +1088,14 @@ Logging in with Google... Restarting Gemini CLI to continue.
|
||||
return hint;
|
||||
}, []);
|
||||
|
||||
const consumePendingBackgroundCompletions = useCallback(() => {
|
||||
if (pendingBackgroundCompletionsRef.current.length === 0) {
|
||||
return null;
|
||||
}
|
||||
const output = pendingBackgroundCompletionsRef.current.join('\n');
|
||||
pendingBackgroundCompletionsRef.current = [];
|
||||
setPendingBgCompletionCount(0);
|
||||
return output;
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const injectionListener = (text: string, source: InjectionSource) => {
|
||||
if (source === 'user_steering') {
|
||||
pendingHintsRef.current.push(text);
|
||||
setPendingHintCount((prev) => prev + 1);
|
||||
} else if (source === 'background_completion') {
|
||||
pendingBackgroundCompletionsRef.current.push(text);
|
||||
setPendingBgCompletionCount((prev) => prev + 1);
|
||||
}
|
||||
const hintListener = (hint: string) => {
|
||||
pendingHintsRef.current.push(hint);
|
||||
setPendingHintCount((prev) => prev + 1);
|
||||
};
|
||||
config.injectionService.onInjection(injectionListener);
|
||||
config.injectionService.onUserHint(hintListener);
|
||||
return () => {
|
||||
config.injectionService.offInjection(injectionListener);
|
||||
config.injectionService.offUserHint(hintListener);
|
||||
};
|
||||
}, [config]);
|
||||
|
||||
@@ -2148,38 +2130,6 @@ Logging in with Google... Restarting Gemini CLI to continue.
|
||||
pendingHintCount,
|
||||
]);
|
||||
|
||||
// Reinject completed background execution output into the model conversation.
|
||||
// Unlike user steering hints, this is NOT gated on model steering being enabled.
|
||||
useEffect(() => {
|
||||
if (
|
||||
!isConfigInitialized ||
|
||||
streamingState !== StreamingState.Idle ||
|
||||
!isMcpReady ||
|
||||
isToolAwaitingConfirmation(pendingHistoryItems)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
const bgOutput = consumePendingBackgroundCompletions();
|
||||
if (!bgOutput) {
|
||||
return;
|
||||
}
|
||||
|
||||
void submitQuery([
|
||||
{
|
||||
text: `Background execution update:\n${bgOutput}\n\nThe above background execution has completed. Review the output and continue your work accordingly.`,
|
||||
},
|
||||
]);
|
||||
}, [
|
||||
isConfigInitialized,
|
||||
isMcpReady,
|
||||
streamingState,
|
||||
submitQuery,
|
||||
consumePendingBackgroundCompletions,
|
||||
pendingHistoryItems,
|
||||
pendingBgCompletionCount,
|
||||
]);
|
||||
|
||||
const allToolCalls = useMemo(
|
||||
() =>
|
||||
pendingHistoryItems
|
||||
|
||||
@@ -65,6 +65,7 @@ import { getToolCallContext } from '../utils/toolCallContext.js';
|
||||
import { scheduleAgentTools } from './agent-scheduler.js';
|
||||
import { DeadlineTimer } from '../utils/deadlineTimer.js';
|
||||
import { formatUserHintsForModel } from '../utils/fastAckHelper.js';
|
||||
import type { InjectionSource } from '../config/injectionService.js';
|
||||
|
||||
/** A callback function to report on agent activity. */
|
||||
export type ActivityCallback = (activity: SubagentActivityEvent) => void;
|
||||
@@ -526,14 +527,19 @@ export class LocalAgentExecutor<TOutput extends z.ZodTypeAny> {
|
||||
: DEFAULT_QUERY_STRING;
|
||||
|
||||
const pendingHintsQueue: string[] = [];
|
||||
const hintListener = (hint: string) => {
|
||||
pendingHintsQueue.push(hint);
|
||||
const pendingBgCompletionsQueue: string[] = [];
|
||||
const injectionListener = (text: string, source: InjectionSource) => {
|
||||
if (source === 'user_steering') {
|
||||
pendingHintsQueue.push(text);
|
||||
} else if (source === 'background_completion') {
|
||||
pendingBgCompletionsQueue.push(text);
|
||||
}
|
||||
};
|
||||
// Capture the index of the last hint before starting to avoid re-injecting old hints.
|
||||
// NOTE: Hints added AFTER this point will be broadcast to all currently running
|
||||
// local agents via the listener below.
|
||||
const startIndex = this.config.injectionService.getLatestHintIndex();
|
||||
this.config.injectionService.onUserHint(hintListener);
|
||||
this.config.injectionService.onInjection(injectionListener);
|
||||
|
||||
try {
|
||||
const initialHints =
|
||||
@@ -585,20 +591,29 @@ export class LocalAgentExecutor<TOutput extends z.ZodTypeAny> {
|
||||
// If status is 'continue', update message for the next loop
|
||||
currentMessage = turnResult.nextMessage;
|
||||
|
||||
// Check for new user steering hints collected via subscription
|
||||
// Inject background completion output into the next turn.
|
||||
if (pendingBgCompletionsQueue.length > 0) {
|
||||
const bgText = pendingBgCompletionsQueue.join('\n');
|
||||
pendingBgCompletionsQueue.length = 0;
|
||||
currentMessage.parts ??= [];
|
||||
currentMessage.parts.unshift({
|
||||
text: `Background execution update:\n${bgText}\n\nThe above background execution has completed. Review the output and continue your work accordingly.`,
|
||||
});
|
||||
}
|
||||
|
||||
// Check for new user steering hints collected via subscription.
|
||||
if (pendingHintsQueue.length > 0) {
|
||||
const hintsToProcess = [...pendingHintsQueue];
|
||||
pendingHintsQueue.length = 0;
|
||||
const formattedHints = formatUserHintsForModel(hintsToProcess);
|
||||
if (formattedHints) {
|
||||
// Append hints to the current message (next turn)
|
||||
currentMessage.parts ??= [];
|
||||
currentMessage.parts.unshift({ text: formattedHints });
|
||||
}
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
this.config.injectionService.offUserHint(hintListener);
|
||||
this.config.injectionService.offInjection(injectionListener);
|
||||
}
|
||||
|
||||
// === UNIFIED RECOVERY BLOCK ===
|
||||
|
||||
Reference in New Issue
Block a user