mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-04-29 22:44:45 -07:00
temp states with session-learnings hook
This commit is contained in:
@@ -796,8 +796,6 @@ export async function loadCliConfig(
|
||||
toolOutputMasking: settings.experimental?.toolOutputMasking,
|
||||
noBrowser: !!process.env['NO_BROWSER'],
|
||||
summarizeToolOutput: settings.model?.summarizeToolOutput,
|
||||
sessionLearnings: settings.general?.sessionLearnings?.enabled,
|
||||
sessionLearningsOutputPath: settings.general?.sessionLearnings?.outputPath,
|
||||
ideMode,
|
||||
disableLoopDetection: settings.model?.disableLoopDetection,
|
||||
compressionThreshold: settings.model?.compressionThreshold,
|
||||
|
||||
@@ -322,37 +322,6 @@ const SETTINGS_SCHEMA = {
|
||||
},
|
||||
description: 'Settings for automatic session cleanup.',
|
||||
},
|
||||
sessionLearnings: {
|
||||
type: 'object',
|
||||
label: 'Session Learnings',
|
||||
category: 'General',
|
||||
requiresRestart: false,
|
||||
default: {},
|
||||
description: 'Settings for session learning summaries.',
|
||||
showInDialog: false,
|
||||
properties: {
|
||||
enabled: {
|
||||
type: 'boolean',
|
||||
label: 'Enable Session Learnings',
|
||||
category: 'General',
|
||||
requiresRestart: false,
|
||||
default: false,
|
||||
description:
|
||||
'Automatically generate a session-learnings.md file when the session ends.',
|
||||
showInDialog: true,
|
||||
},
|
||||
outputPath: {
|
||||
type: 'string',
|
||||
label: 'Output Path',
|
||||
category: 'General',
|
||||
requiresRestart: false,
|
||||
default: undefined as string | undefined,
|
||||
description:
|
||||
'Directory where session-learnings files should be saved. Defaults to project root.',
|
||||
showInDialog: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
output: {
|
||||
|
||||
@@ -477,8 +477,6 @@ export interface ConfigParameters {
|
||||
experimentalJitContext?: boolean;
|
||||
toolOutputMasking?: Partial<ToolOutputMaskingConfig>;
|
||||
disableLLMCorrection?: boolean;
|
||||
sessionLearnings?: boolean;
|
||||
sessionLearningsOutputPath?: string;
|
||||
plan?: boolean;
|
||||
onModelChange?: (model: string) => void;
|
||||
mcpEnabled?: boolean;
|
||||
@@ -664,8 +662,6 @@ export class Config {
|
||||
|
||||
private readonly experimentalJitContext: boolean;
|
||||
private readonly disableLLMCorrection: boolean;
|
||||
private readonly sessionLearnings: boolean;
|
||||
private readonly sessionLearningsOutputPath: string | undefined;
|
||||
private readonly planEnabled: boolean;
|
||||
private contextManager?: ContextManager;
|
||||
private terminalBackground: string | undefined = undefined;
|
||||
@@ -754,8 +750,6 @@ export class Config {
|
||||
this.enableAgents = params.enableAgents ?? false;
|
||||
this.agents = params.agents ?? {};
|
||||
this.disableLLMCorrection = params.disableLLMCorrection ?? true;
|
||||
this.sessionLearnings = params.sessionLearnings ?? false;
|
||||
this.sessionLearningsOutputPath = params.sessionLearningsOutputPath;
|
||||
this.planEnabled = params.plan ?? false;
|
||||
this.enableEventDrivenScheduler = params.enableEventDrivenScheduler ?? true;
|
||||
this.skillsSupport = params.skillsSupport ?? true;
|
||||
@@ -1955,14 +1949,6 @@ export class Config {
|
||||
return this.disableLLMCorrection;
|
||||
}
|
||||
|
||||
isSessionLearningsEnabled(): boolean {
|
||||
return this.sessionLearnings;
|
||||
}
|
||||
|
||||
getSessionLearningsOutputPath(): string | undefined {
|
||||
return this.sessionLearningsOutputPath;
|
||||
}
|
||||
|
||||
isPlanEnabled(): boolean {
|
||||
return this.planEnabled;
|
||||
}
|
||||
|
||||
@@ -74,7 +74,6 @@ describe('HookRegistry', () => {
|
||||
getDisabledHooks: vi.fn().mockReturnValue([]),
|
||||
isTrustedFolder: vi.fn().mockReturnValue(true),
|
||||
getProjectRoot: vi.fn().mockReturnValue('/project'),
|
||||
isSessionLearningsEnabled: vi.fn().mockReturnValue(false),
|
||||
} as unknown as Config;
|
||||
|
||||
hookRegistry = new HookRegistry(mockConfig);
|
||||
@@ -280,21 +279,6 @@ describe('HookRegistry', () => {
|
||||
hookRegistry.getHooksForEvent(HookEventName.BeforeTool),
|
||||
).toHaveLength(0);
|
||||
});
|
||||
|
||||
it('should register builtin session-learnings hook when enabled', async () => {
|
||||
vi.mocked(mockConfig.isSessionLearningsEnabled).mockReturnValue(true);
|
||||
|
||||
await hookRegistry.initialize();
|
||||
|
||||
const hooks = hookRegistry.getHooksForEvent(HookEventName.SessionEnd);
|
||||
expect(hooks).toHaveLength(1);
|
||||
expect(hooks[0].config.type).toBe(HookType.Builtin);
|
||||
|
||||
expect((hooks[0].config as BuiltinHookConfig).builtin_id).toBe(
|
||||
'session-learnings',
|
||||
);
|
||||
expect(hooks[0].source).toBe(ConfigSource.System);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getHooksForEvent', () => {
|
||||
|
||||
@@ -6,12 +6,7 @@
|
||||
|
||||
import type { Config } from '../config/config.js';
|
||||
import type { HookDefinition, HookConfig } from './types.js';
|
||||
import {
|
||||
HookEventName,
|
||||
ConfigSource,
|
||||
HOOKS_CONFIG_FIELDS,
|
||||
HookType,
|
||||
} from './types.js';
|
||||
import { HookEventName, ConfigSource, HOOKS_CONFIG_FIELDS } from './types.js';
|
||||
import { debugLogger } from '../utils/debugLogger.js';
|
||||
import { TrustedHooksManager } from './trustedHooks.js';
|
||||
import { coreEvents } from '../utils/events.js';
|
||||
@@ -142,8 +137,6 @@ please review the project settings (.gemini/settings.json) and remove them.`;
|
||||
this.checkProjectHooksTrust();
|
||||
}
|
||||
|
||||
this.registerBuiltinHooks();
|
||||
|
||||
// Get hooks from the main config (this comes from the merged settings)
|
||||
const configHooks = this.config.getHooks();
|
||||
if (configHooks) {
|
||||
@@ -168,27 +161,6 @@ please review the project settings (.gemini/settings.json) and remove them.`;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register system-level builtin hooks
|
||||
*/
|
||||
private registerBuiltinHooks(): void {
|
||||
if (this.config.isSessionLearningsEnabled()) {
|
||||
debugLogger.debug('Registering builtin session-learnings hook');
|
||||
this.entries.push({
|
||||
config: {
|
||||
type: HookType.Builtin,
|
||||
builtin_id: 'session-learnings',
|
||||
name: 'session-learnings',
|
||||
description: 'Automatically generate session learning summaries',
|
||||
source: ConfigSource.System,
|
||||
},
|
||||
source: ConfigSource.System,
|
||||
eventName: HookEventName.SessionEnd,
|
||||
enabled: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Process hooks configuration and add entries
|
||||
*/
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
import { spawn } from 'node:child_process';
|
||||
import type { HookConfig } from './types.js';
|
||||
import { HookEventName, ConfigSource, HookType } from './types.js';
|
||||
import { HookEventName, ConfigSource } from './types.js';
|
||||
import type { Config } from '../config/config.js';
|
||||
import type {
|
||||
HookInput,
|
||||
@@ -16,9 +16,7 @@ import type {
|
||||
BeforeModelInput,
|
||||
BeforeModelOutput,
|
||||
BeforeToolInput,
|
||||
SessionEndInput,
|
||||
} from './types.js';
|
||||
import { SessionEndReason } from './types.js';
|
||||
import type { LLMRequest } from './hookTranslator.js';
|
||||
import { debugLogger } from '../utils/debugLogger.js';
|
||||
import { sanitizeEnvironment } from '../services/environmentSanitization.js';
|
||||
@@ -27,7 +25,6 @@ import {
|
||||
getShellConfiguration,
|
||||
type ShellType,
|
||||
} from '../utils/shell-utils.js';
|
||||
import { SessionLearningsService } from '../services/sessionLearningsService.js';
|
||||
|
||||
/**
|
||||
* Default timeout for hook execution (60 seconds)
|
||||
@@ -46,11 +43,9 @@ const EXIT_CODE_NON_BLOCKING_ERROR = 1;
|
||||
*/
|
||||
export class HookRunner {
|
||||
private readonly config: Config;
|
||||
private readonly sessionLearningsService: SessionLearningsService;
|
||||
|
||||
constructor(config: Config) {
|
||||
this.config = config;
|
||||
this.sessionLearningsService = new SessionLearningsService(config);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -81,25 +76,12 @@ export class HookRunner {
|
||||
}
|
||||
|
||||
try {
|
||||
if (hookConfig.type === HookType.Command) {
|
||||
return await this.executeCommandHook(
|
||||
hookConfig,
|
||||
eventName,
|
||||
input,
|
||||
startTime,
|
||||
);
|
||||
} else if (hookConfig.type === HookType.Builtin) {
|
||||
return await this.executeBuiltinHook(
|
||||
hookConfig,
|
||||
eventName,
|
||||
input,
|
||||
startTime,
|
||||
);
|
||||
} else {
|
||||
throw new Error(
|
||||
`Unsupported hook type: ${(hookConfig as HookConfig).type}`,
|
||||
);
|
||||
}
|
||||
return await this.executeCommandHook(
|
||||
hookConfig,
|
||||
eventName,
|
||||
input,
|
||||
startTime,
|
||||
);
|
||||
} catch (error) {
|
||||
const duration = Date.now() - startTime;
|
||||
const hookId = hookConfig.name || hookConfig.command || 'unknown';
|
||||
@@ -249,52 +231,6 @@ export class HookRunner {
|
||||
return modifiedInput;
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a builtin hook
|
||||
*/
|
||||
private async executeBuiltinHook(
|
||||
hookConfig: HookConfig,
|
||||
eventName: HookEventName,
|
||||
input: HookInput,
|
||||
startTime: number,
|
||||
): Promise<HookExecutionResult> {
|
||||
if (hookConfig.type !== HookType.Builtin) {
|
||||
throw new Error('Expected builtin hook configuration');
|
||||
}
|
||||
|
||||
try {
|
||||
if (hookConfig.builtin_id === 'session-learnings') {
|
||||
if (eventName === HookEventName.SessionEnd) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
|
||||
const sessionEndInput = input as SessionEndInput;
|
||||
if (
|
||||
sessionEndInput.reason === SessionEndReason.Exit ||
|
||||
sessionEndInput.reason === SessionEndReason.Logout
|
||||
) {
|
||||
await this.sessionLearningsService.generateAndSaveLearnings();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
hookConfig,
|
||||
eventName,
|
||||
success: true,
|
||||
duration: Date.now() - startTime,
|
||||
exitCode: EXIT_CODE_SUCCESS,
|
||||
output: {},
|
||||
};
|
||||
} catch (error) {
|
||||
return {
|
||||
hookConfig,
|
||||
eventName,
|
||||
success: false,
|
||||
error: error instanceof Error ? error : new Error(String(error)),
|
||||
duration: Date.now() - startTime,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute a command hook
|
||||
*/
|
||||
|
||||
@@ -62,16 +62,7 @@ export interface CommandHookConfig {
|
||||
env?: Record<string, string>;
|
||||
}
|
||||
|
||||
export interface BuiltinHookConfig {
|
||||
type: HookType.Builtin;
|
||||
builtin_id: string;
|
||||
name?: string;
|
||||
description?: string;
|
||||
timeout?: number;
|
||||
source?: ConfigSource;
|
||||
}
|
||||
|
||||
export type HookConfig = CommandHookConfig | BuiltinHookConfig;
|
||||
export type HookConfig = CommandHookConfig;
|
||||
|
||||
/**
|
||||
* Hook definition with matcher
|
||||
@@ -87,7 +78,6 @@ export interface HookDefinition {
|
||||
*/
|
||||
export enum HookType {
|
||||
Command = 'command',
|
||||
Builtin = 'builtin',
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -95,9 +85,8 @@ export enum HookType {
|
||||
*/
|
||||
export function getHookKey(hook: HookConfig): string {
|
||||
const name = hook.name || '';
|
||||
const identifier =
|
||||
hook.type === HookType.Command ? hook.command : hook.builtin_id;
|
||||
return `${name}:${identifier}`;
|
||||
const command = hook.command || '';
|
||||
return `${name}:${command}`;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user