mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-05-02 16:04:38 -07:00
refactor(core): introduce InjectionService with source-aware injection and backend-native background completions (#22544)
This commit is contained in:
@@ -63,7 +63,11 @@ import { getVersion } from '../utils/version.js';
|
||||
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 {
|
||||
formatUserHintsForModel,
|
||||
formatBackgroundCompletionForModel,
|
||||
} 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;
|
||||
@@ -513,18 +517,25 @@ 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.userHintService.getLatestHintIndex();
|
||||
this.config.userHintService.onUserHint(hintListener);
|
||||
const startIndex = this.config.injectionService.getLatestInjectionIndex();
|
||||
this.config.injectionService.onInjection(injectionListener);
|
||||
|
||||
try {
|
||||
const initialHints =
|
||||
this.config.userHintService.getUserHintsAfter(startIndex);
|
||||
const initialHints = this.config.injectionService.getInjectionsAfter(
|
||||
startIndex,
|
||||
'user_steering',
|
||||
);
|
||||
const formattedInitialHints = formatUserHintsForModel(initialHints);
|
||||
|
||||
let currentMessage: Content = formattedInitialHints
|
||||
@@ -572,20 +583,30 @@ 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
|
||||
// Prepend inter-turn injections. User hints are unshifted first so
|
||||
// that bg completions (unshifted second) appear before them in the
|
||||
// final message — the model sees context before the user's reaction.
|
||||
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 });
|
||||
}
|
||||
}
|
||||
|
||||
if (pendingBgCompletionsQueue.length > 0) {
|
||||
const bgText = pendingBgCompletionsQueue.join('\n');
|
||||
pendingBgCompletionsQueue.length = 0;
|
||||
currentMessage.parts ??= [];
|
||||
currentMessage.parts.unshift({
|
||||
text: formatBackgroundCompletionForModel(bgText),
|
||||
});
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
this.config.userHintService.offUserHint(hintListener);
|
||||
this.config.injectionService.offInjection(injectionListener);
|
||||
}
|
||||
|
||||
// === UNIFIED RECOVERY BLOCK ===
|
||||
|
||||
Reference in New Issue
Block a user