mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-03-26 14:01:14 -07:00
feat(core): enhance hook system with PreCompress replacement and Idle event
PreCompress hooks can now return newHistory to replace built-in compression. Add Idle hook event that fires after configurable inactivity period. - PreCompress: accept history in hook input, return newHistory to skip built-in summarization (CompressionStatus.HOOK_REPLACED) - Idle: new HookEventName with fireIdleEvent, auto-activates when extensions register Idle hooks (default 300s, configurable via hooksConfig.idleTimeout) - Hook can return hookSpecificOutput.prompt to auto-submit a message
This commit is contained in:
@@ -2021,6 +2021,16 @@ const SETTINGS_SCHEMA = {
|
||||
description: 'Show visual indicators when hooks are executing.',
|
||||
showInDialog: true,
|
||||
},
|
||||
idleTimeout: {
|
||||
type: 'number',
|
||||
label: 'Idle Timeout',
|
||||
category: 'Advanced',
|
||||
requiresRestart: false,
|
||||
default: 0,
|
||||
description:
|
||||
'Time in seconds before the Idle hook fires when there is no user input. Set to 0 to disable.',
|
||||
showInDialog: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -2165,6 +2175,18 @@ const SETTINGS_SCHEMA = {
|
||||
ref: 'HookDefinitionArray',
|
||||
mergeStrategy: MergeStrategy.CONCAT,
|
||||
},
|
||||
Idle: {
|
||||
type: 'array',
|
||||
label: 'Idle Hooks',
|
||||
category: 'Advanced',
|
||||
requiresRestart: false,
|
||||
default: [],
|
||||
description:
|
||||
'Hooks that execute after a period of inactivity. Can trigger maintenance tasks like memory consolidation.',
|
||||
showInDialog: false,
|
||||
ref: 'HookDefinitionArray',
|
||||
mergeStrategy: MergeStrategy.CONCAT,
|
||||
},
|
||||
},
|
||||
additionalProperties: {
|
||||
type: 'array',
|
||||
|
||||
@@ -1874,6 +1874,64 @@ export const useGeminiStream = (
|
||||
storage,
|
||||
]);
|
||||
|
||||
// Idle hook timer: fires after idleTimeout seconds of no activity.
|
||||
// If idleTimeout is explicitly set, use it. Otherwise, if any Idle hooks
|
||||
// are registered (e.g. by an extension), use a default of 300 seconds.
|
||||
const DEFAULT_IDLE_TIMEOUT = 300;
|
||||
const idleTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
|
||||
const configuredIdleTimeout = settings.merged.hooksConfig?.idleTimeout ?? 0;
|
||||
useEffect(() => {
|
||||
// Clear any existing timer
|
||||
if (idleTimerRef.current) {
|
||||
clearTimeout(idleTimerRef.current);
|
||||
idleTimerRef.current = null;
|
||||
}
|
||||
|
||||
if (streamingState !== StreamingState.Idle || !config.getEnableHooks()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Compute effective timeout: use configured value, or default if
|
||||
// Idle hooks are registered (e.g. by an extension).
|
||||
let idleTimeoutSeconds = configuredIdleTimeout;
|
||||
if (idleTimeoutSeconds <= 0) {
|
||||
const hookSystem = config.getHookSystem();
|
||||
const hasIdleHooks = hookSystem
|
||||
?.getAllHooks()
|
||||
.some((h) => h.eventName === 'Idle' && h.enabled);
|
||||
idleTimeoutSeconds = hasIdleHooks ? DEFAULT_IDLE_TIMEOUT : 0;
|
||||
}
|
||||
|
||||
if (idleTimeoutSeconds <= 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
const startTime = Date.now();
|
||||
idleTimerRef.current = setTimeout(async () => {
|
||||
const hookSystem = config.getHookSystem();
|
||||
if (!hookSystem) return;
|
||||
|
||||
const elapsed = Math.round((Date.now() - startTime) / 1000);
|
||||
try {
|
||||
const result = await hookSystem.fireIdleEvent(elapsed);
|
||||
const prompt = result?.finalOutput?.hookSpecificOutput?.['prompt'];
|
||||
if (typeof prompt === 'string' && prompt.trim()) {
|
||||
// Auto-submit the prompt returned by the hook
|
||||
void submitQuery(prompt);
|
||||
}
|
||||
} catch {
|
||||
// Idle hook failures are non-fatal
|
||||
}
|
||||
}, idleTimeoutSeconds * 1000);
|
||||
|
||||
return () => {
|
||||
if (idleTimerRef.current) {
|
||||
clearTimeout(idleTimerRef.current);
|
||||
idleTimerRef.current = null;
|
||||
}
|
||||
};
|
||||
}, [streamingState, configuredIdleTimeout, config, submitQuery]);
|
||||
|
||||
const lastOutputTime = Math.max(
|
||||
lastToolOutputTime,
|
||||
lastShellOutputTime,
|
||||
|
||||
Reference in New Issue
Block a user