mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-07-10 01:29:17 +00:00
add integration test and --experimental-hooks
This commit is contained in:
parent
981d1aed3e
commit
7b0929d00c
22 changed files with 1105 additions and 1614 deletions
File diff suppressed because it is too large
Load diff
|
|
@ -1 +1 @@
|
|||
{"method":"generateContentStream","response":[{"candidates":[{"content":{"parts":[{"text":"Parallel hooks executed with mixed results.","thought":true}],"role":"model"},"index":0}],"usageMetadata":{"promptTokenCount":100,"totalTokenCount":120}}]}
|
||||
{"method":"generateContentStream","response":[{"candidates":[{"content":{"parts":[{"text":"Parallel hooks executed with mixed results - one succeeded, one failed.","thought":true}],"role":"model"},"index":0}],"usageMetadata":{"promptTokenCount":100,"totalTokenCount":120}}]}
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
{"method":"generateContentStream","response":[{"candidates":[{"content":{"parts":[{"text":"Sequential hook 1 executed first.","thought":true}],"role":"model"},"index":0}],"usageMetadata":{"promptTokenCount":100,"totalTokenCount":120}}]}
|
||||
{"method":"generateContentStream","response":[{"candidates":[{"content":{"parts":[{"text":"The first hook blocked the request. Second hook was not executed.","thought":true}],"role":"model"},"index":0}],"usageMetadata":{"promptTokenCount":100,"totalTokenCount":120}}]}
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
{"method":"generateContentStream","response":[{"candidates":[{"content":{"parts":[{"text":"Sequential hooks executed with output passthrough.","thought":true}],"role":"model"},"index":0}],"usageMetadata":{"promptTokenCount":100,"totalTokenCount":120}}]}
|
||||
{"method":"generateContentStream","response":[{"candidates":[{"content":{"parts":[{"text":"Output from first hook was passed to second hook successfully.","thought":true}],"role":"model"},"index":0}],"usageMetadata":{"promptTokenCount":100,"totalTokenCount":120}}]}
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
{"method":"generateContentStream","response":[{"candidates":[{"content":{"parts":[{"text":"Final response with additional context from hook.","thought":true}],"role":"model"},"index":0}],"usageMetadata":{"promptTokenCount":100,"totalTokenCount":120}}]}
|
||||
{"method":"generateContentStream","response":[{"candidates":[{"content":{"parts":[{"text":"I have received the final context from the stop hook and included it in my response.","thought":true}],"role":"model"},"index":0}],"usageMetadata":{"promptTokenCount":100,"totalTokenCount":120}}]}
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
{"method":"generateContentStream","response":[{"candidates":[{"content":{"parts":[{"text":"Stopping with warning from hook.","thought":true}],"role":"model"},"index":0}],"usageMetadata":{"promptTokenCount":100,"totalTokenCount":120}}]}
|
||||
{"method":"generateContentStream","response":[{"candidates":[{"content":{"parts":[{"text":"The stop hook had an error but I completed my response.","thought":true}],"role":"model"},"index":0}],"usageMetadata":{"promptTokenCount":100,"totalTokenCount":120}}]}
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
{"method":"generateContentStream","response":[{"candidates":[{"content":{"parts":[{"text":"Stopping now. Reason: Hook specified stop reason.","thought":true}],"role":"model"},"index":0}],"usageMetadata":{"promptTokenCount":100,"totalTokenCount":120}}]}
|
||||
{"method":"generateContentStream","response":[{"candidates":[{"content":{"parts":[{"text":"Task completed with custom stop reason from hook.","thought":true}],"role":"model"},"index":0}],"usageMetadata":{"promptTokenCount":100,"totalTokenCount":120}}]}
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
{"method":"generateContentStream","response":[{"candidates":[{"content":{"parts":[{"text":"The stop hook timed out but stopping anyway.","thought":true}],"role":"model"},"index":0}],"usageMetadata":{"promptTokenCount":100,"totalTokenCount":120}}]}
|
||||
{"method":"generateContentStream","response":[{"candidates":[{"content":{"parts":[{"text":"The stop hook timed out but I completed my response.","thought":true}],"role":"model"},"index":0}],"usageMetadata":{"promptTokenCount":100,"totalTokenCount":120}}]}
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
{"method":"generateContentStream","response":[{"candidates":[{"content":{"parts":[{"text":"Received last assistant message in hook.","thought":true}],"role":"model"},"index":0}],"usageMetadata":{"promptTokenCount":100,"totalTokenCount":120}}]}
|
||||
{"method":"generateContentStream","response":[{"candidates":[{"content":{"parts":[{"text":"Final system message from stop hook has been processed.","thought":true}],"role":"model"},"index":0}],"usageMetadata":{"promptTokenCount":100,"totalTokenCount":120}}]}
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
{"method":"generateContentStream","response":[{"candidates":[{"content":{"parts":[{"text":"The hook has added additional context to your request.","thought":true}],"role":"model"},"index":0}],"usageMetadata":{"promptTokenCount":120,"totalTokenCount":150}}]}
|
||||
{"method":"generateContentStream","response":[{"candidates":[{"content":{"parts":[{"text":"I have received additional context from the hook and will use it in my response.","thought":true}],"role":"model"},"index":0}],"usageMetadata":{"promptTokenCount":100,"totalTokenCount":120}}]}
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
{"method":"generateContentStream","response":[{"candidates":[{"content":{"parts":[{"text":"Empty prompt received and handled.","thought":true}],"role":"model"},"index":0}],"usageMetadata":{"promptTokenCount":50,"totalTokenCount":70}}]}
|
||||
{"method":"generateContentStream","response":[{"candidates":[{"content":{"parts":[{"text":"I received an empty prompt. How can I help you?","thought":true}],"role":"model"},"index":0}],"usageMetadata":{"promptTokenCount":100,"totalTokenCount":120}}]}
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
{"method":"generateContentStream","response":[{"candidates":[{"content":{"parts":[{"text":"Request denied due to critical error.","thought":true}],"role":"model"},"index":0}],"usageMetadata":{"promptTokenCount":100,"totalTokenCount":120}}]}
|
||||
{"method":"generateContentStream","response":[{"candidates":[{"content":{"parts":[{"text":"The hook had a blocking error and your request was blocked.","thought":true}],"role":"model"},"index":0}],"usageMetadata":{"promptTokenCount":100,"totalTokenCount":120}}]}
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
{"method":"generateContentStream","response":[{"candidates":[{"content":{"parts":[{"text":"There was a warning but the request continues.","thought":true}],"role":"model"},"index":0}],"usageMetadata":{"promptTokenCount":100,"totalTokenCount":120}}]}
|
||||
{"method":"generateContentStream","response":[{"candidates":[{"content":{"parts":[{"text":"The hook had a non-blocking error but I continued processing your request.","thought":true}],"role":"model"},"index":0}],"usageMetadata":{"promptTokenCount":100,"totalTokenCount":120}}]}
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
{"method":"generateContentStream","response":[{"candidates":[{"content":{"parts":[{"text":"The command was not found but continuing.","thought":true}],"role":"model"},"index":0}],"usageMetadata":{"promptTokenCount":100,"totalTokenCount":120}}]}
|
||||
{"method":"generateContentStream","response":[{"candidates":[{"content":{"parts":[{"text":"The hook command was not found but I continued processing your request.","thought":true}],"role":"model"},"index":0}],"usageMetadata":{"promptTokenCount":100,"totalTokenCount":120}}]}
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
{"method":"generateContentStream","response":[{"candidates":[{"content":{"parts":[{"text":"I've received your modified request. I'll respond to the modified version.","thought":true}],"role":"model"},"index":0}],"usageMetadata":{"promptTokenCount":120,"totalTokenCount":150}}]}
|
||||
{"method":"generateContentStream","response":[{"candidates":[{"content":{"parts":[{"text":"I received your modified prompt and will process it accordingly.","thought":true}],"role":"model"},"index":0}],"usageMetadata":{"promptTokenCount":100,"totalTokenCount":120}}]}
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
{"method":"generateContentStream","response":[{"candidates":[{"content":{"parts":[{"text":"The request timed out but will continue.","thought":true}],"role":"model"},"index":0}],"usageMetadata":{"promptTokenCount":100,"totalTokenCount":120}}]}
|
||||
{"method":"generateContentStream","response":[{"candidates":[{"content":{"parts":[{"text":"The hook timed out but I continued processing your request.","thought":true}],"role":"model"},"index":0}],"usageMetadata":{"promptTokenCount":100,"totalTokenCount":120}}]}
|
||||
|
|
|
|||
|
|
@ -1,325 +0,0 @@
|
|||
/**
|
||||
* @license
|
||||
* Copyright 2026 Qwen Team
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { TestRig, printDebugInfo, validateModelOutput } from './test-helper.js';
|
||||
|
||||
describe('hooks', () => {
|
||||
it('should execute Stop hook when response finishes', async () => {
|
||||
const rig = new TestRig();
|
||||
await rig.setup('should execute Stop hook when response finishes', {
|
||||
settings: {
|
||||
hooks: {
|
||||
Stop: [
|
||||
{
|
||||
hooks: [
|
||||
{
|
||||
type: 'command',
|
||||
command: 'echo "STOP_HOOK_EXECUTED" > stop_hook_result.txt',
|
||||
name: 'test-stop-hook',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
trusted: true,
|
||||
},
|
||||
});
|
||||
|
||||
const prompt = `Say "hello" and that's it.`;
|
||||
|
||||
const result = await rig.run(prompt);
|
||||
|
||||
// Wait for telemetry to be ready (hook should have executed)
|
||||
await rig.waitForTelemetryReady();
|
||||
|
||||
// Check that the Stop hook executed by looking for the output file
|
||||
try {
|
||||
const hookOutput = rig.readFile('stop_hook_result.txt');
|
||||
expect(hookOutput).toContain('STOP_HOOK_EXECUTED');
|
||||
} catch {
|
||||
// Hook file might not exist - check telemetry for hook execution
|
||||
// Stop hook is a command hook, it may not appear in tool logs
|
||||
// but the test should at least complete without errors
|
||||
}
|
||||
|
||||
// Validate model output
|
||||
validateModelOutput(result, 'hello', 'Stop hook test');
|
||||
});
|
||||
|
||||
it('should execute UserPromptSubmit hook when user submits prompt', async () => {
|
||||
const rig = new TestRig();
|
||||
await rig.setup(
|
||||
'should execute UserPromptSubmit hook when user submits prompt',
|
||||
{
|
||||
settings: {
|
||||
hooks: {
|
||||
UserPromptSubmit: [
|
||||
{
|
||||
hooks: [
|
||||
{
|
||||
type: 'command',
|
||||
command:
|
||||
'echo "USER_PROMPT_SUBMITTED: $QWEN_HOOK_PROMPT" > prompt_hook_result.txt',
|
||||
name: 'test-prompt-submit-hook',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
trusted: true,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
const prompt = `Just say "received" and nothing else.`;
|
||||
|
||||
const result = await rig.run(prompt);
|
||||
|
||||
// Wait for telemetry
|
||||
await rig.waitForTelemetryReady();
|
||||
|
||||
// Check that the UserPromptSubmit hook executed
|
||||
try {
|
||||
const hookOutput = rig.readFile('prompt_hook_result.txt');
|
||||
expect(hookOutput).toContain('USER_PROMPT_SUBMITTED');
|
||||
} catch {
|
||||
// Hook file might not exist - that's okay, the test verifies the CLI runs
|
||||
}
|
||||
|
||||
// Validate model output
|
||||
validateModelOutput(result, 'received', 'UserPromptSubmit hook test');
|
||||
});
|
||||
|
||||
it('should execute both Stop and UserPromptSubmit hooks', async () => {
|
||||
const rig = new TestRig();
|
||||
await rig.setup('should execute both Stop and UserPromptSubmit hooks', {
|
||||
settings: {
|
||||
hooks: {
|
||||
Stop: [
|
||||
{
|
||||
hooks: [
|
||||
{
|
||||
type: 'command',
|
||||
command: 'echo "stop_executed" > both_stop.txt',
|
||||
name: 'stop-hook',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
UserPromptSubmit: [
|
||||
{
|
||||
hooks: [
|
||||
{
|
||||
type: 'command',
|
||||
command: 'echo "prompt_submitted" > both_prompt.txt',
|
||||
name: 'prompt-hook',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
trusted: true,
|
||||
},
|
||||
});
|
||||
|
||||
const prompt = `Say "testing both hooks".`;
|
||||
|
||||
const result = await rig.run(prompt);
|
||||
|
||||
// Wait for telemetry
|
||||
await rig.waitForTelemetryReady();
|
||||
|
||||
// Check both hooks executed
|
||||
try {
|
||||
const stopOutput = rig.readFile('both_stop.txt');
|
||||
expect(stopOutput).toContain('stop_executed');
|
||||
} catch {
|
||||
/* empty */
|
||||
}
|
||||
|
||||
try {
|
||||
const promptOutput = rig.readFile('both_prompt.txt');
|
||||
expect(promptOutput).toContain('prompt_submitted');
|
||||
} catch {
|
||||
/* empty */
|
||||
}
|
||||
|
||||
validateModelOutput(result, 'testing both hooks', 'Both hooks test');
|
||||
});
|
||||
|
||||
it('should support sequential hook execution for Stop event', async () => {
|
||||
const rig = new TestRig();
|
||||
await rig.setup('should support sequential hook execution for Stop event', {
|
||||
settings: {
|
||||
hooks: {
|
||||
Stop: [
|
||||
{
|
||||
sequential: true,
|
||||
hooks: [
|
||||
{
|
||||
type: 'command',
|
||||
command: 'echo "first" > seq1.txt',
|
||||
name: 'seq-hook-1',
|
||||
},
|
||||
{
|
||||
type: 'command',
|
||||
command: 'echo "second" > seq2.txt',
|
||||
name: 'seq-hook-2',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
trusted: true,
|
||||
},
|
||||
});
|
||||
|
||||
const prompt = `Say "sequential test".`;
|
||||
|
||||
const result = await rig.run(prompt);
|
||||
|
||||
// Wait for telemetry
|
||||
await rig.waitForTelemetryReady();
|
||||
|
||||
// Check that both sequential hooks executed
|
||||
try {
|
||||
const firstOutput = rig.readFile('seq1.txt');
|
||||
expect(firstOutput).toContain('first');
|
||||
} catch {
|
||||
/* empty */
|
||||
}
|
||||
|
||||
try {
|
||||
const secondOutput = rig.readFile('seq2.txt');
|
||||
expect(secondOutput).toContain('second');
|
||||
} catch {
|
||||
/* empty */
|
||||
}
|
||||
|
||||
validateModelOutput(
|
||||
result,
|
||||
'sequential test',
|
||||
'Sequential Stop hooks test',
|
||||
);
|
||||
});
|
||||
|
||||
it('should support matcher for Stop hook', async () => {
|
||||
const rig = new TestRig();
|
||||
await rig.setup('should support matcher for Stop hook', {
|
||||
settings: {
|
||||
hooks: {
|
||||
Stop: [
|
||||
{
|
||||
matcher: 'write_file',
|
||||
hooks: [
|
||||
{
|
||||
type: 'command',
|
||||
command: 'echo "matched_stop" > matcher_stop.txt',
|
||||
name: 'matcher-stop-hook',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
trusted: true,
|
||||
},
|
||||
});
|
||||
|
||||
const prompt = `Create a file "matcher_test.txt" with content "hello".`;
|
||||
|
||||
const result = await rig.run(prompt);
|
||||
|
||||
const foundToolCall = await rig.waitForToolCall('write_file');
|
||||
|
||||
if (!foundToolCall) {
|
||||
printDebugInfo(rig, result);
|
||||
}
|
||||
|
||||
expect(foundToolCall).toBeTruthy();
|
||||
validateModelOutput(result, 'matcher_test.txt', 'Matcher Stop hook test');
|
||||
|
||||
const fileContent = rig.readFile('matcher_test.txt');
|
||||
expect(fileContent).toContain('hello');
|
||||
});
|
||||
|
||||
it('should allow Stop hook to add additional context to response', async () => {
|
||||
const rig = new TestRig();
|
||||
await rig.setup(
|
||||
'should allow Stop hook to add additional context to response',
|
||||
{
|
||||
settings: {
|
||||
hooks: {
|
||||
Stop: [
|
||||
{
|
||||
hooks: [
|
||||
{
|
||||
type: 'command',
|
||||
command:
|
||||
'echo "{\\"continue\\": true, \\"hookSpecificOutput\\": {\\"additionalContext\\": \\"Custom context from hook\\"}}}"',
|
||||
name: 'context-hook',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
trusted: true,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
const prompt = `Say "test complete".`;
|
||||
|
||||
const result = await rig.run(prompt);
|
||||
|
||||
// Wait for telemetry
|
||||
await rig.waitForTelemetryReady();
|
||||
|
||||
// The hook can add context to the response
|
||||
// Check that the model produced output
|
||||
validateModelOutput(result, 'test complete', 'Stop hook with context test');
|
||||
});
|
||||
|
||||
it('should allow UserPromptSubmit hook to add system message', async () => {
|
||||
const rig = new TestRig();
|
||||
await rig.setup(
|
||||
'should allow UserPromptSubmit hook to add system message',
|
||||
{
|
||||
settings: {
|
||||
hooks: {
|
||||
UserPromptSubmit: [
|
||||
{
|
||||
hooks: [
|
||||
{
|
||||
type: 'command',
|
||||
command:
|
||||
'echo "{\\"continue\\": true, \\"systemMessage\\": \\"You are being tested.\\"}}}"',
|
||||
name: 'system-msg-hook',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
trusted: true,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
const prompt = `What is 2+2?`;
|
||||
|
||||
const result = await rig.run(prompt);
|
||||
|
||||
// Wait for telemetry
|
||||
await rig.waitForTelemetryReady();
|
||||
|
||||
// The hook can add a system message that influences the response
|
||||
validateModelOutput(
|
||||
result,
|
||||
'4',
|
||||
'UserPromptSubmit with system message test',
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
@ -145,6 +145,7 @@ export class TestRig {
|
|||
testName?: string;
|
||||
_lastRunStdout?: string;
|
||||
_interactiveOutput = '';
|
||||
_fakeResponsesPath?: string;
|
||||
|
||||
constructor() {
|
||||
this.bundlePath = join(__dirname, '..', 'dist/cli.js');
|
||||
|
|
@ -160,13 +161,21 @@ export class TestRig {
|
|||
|
||||
setup(
|
||||
testName: string,
|
||||
options: { settings?: Record<string, unknown> } = {},
|
||||
options: {
|
||||
settings?: Record<string, unknown>;
|
||||
fakeResponsesPath?: string;
|
||||
} = {},
|
||||
) {
|
||||
this.testName = testName;
|
||||
const sanitizedName = sanitizeTestName(testName);
|
||||
this.testDir = join(env['INTEGRATION_TEST_FILE_DIR']!, sanitizedName);
|
||||
mkdirSync(this.testDir, { recursive: true });
|
||||
|
||||
// Store fake responses path for use in run()
|
||||
if (options.fakeResponsesPath) {
|
||||
this._fakeResponsesPath = options.fakeResponsesPath;
|
||||
}
|
||||
|
||||
// Create a settings file to point the CLI to the local collector
|
||||
const qwenDir = join(this.testDir, '.qwen');
|
||||
mkdirSync(qwenDir, { recursive: true });
|
||||
|
|
@ -190,6 +199,16 @@ export class TestRig {
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a script file in the test directory and returns its path.
|
||||
* Useful for creating hook scripts that need to be executed.
|
||||
*/
|
||||
createScript(fileName: string, content: string): string {
|
||||
const filePath = join(this.testDir!, fileName);
|
||||
writeFileSync(filePath, content, { mode: 0o755 });
|
||||
return filePath;
|
||||
}
|
||||
|
||||
createFile(fileName: string, content: string) {
|
||||
const filePath = join(this.testDir!, fileName);
|
||||
writeFileSync(filePath, content);
|
||||
|
|
@ -256,10 +275,16 @@ export class TestRig {
|
|||
|
||||
commandArgs.push(...args);
|
||||
|
||||
// Set up environment with fake responses path if configured
|
||||
const childEnv = { ...process.env };
|
||||
if (this._fakeResponsesPath) {
|
||||
childEnv['QWEN_FAKE_RESPONSES_PATH'] = this._fakeResponsesPath;
|
||||
}
|
||||
|
||||
const child = spawn(command, commandArgs, {
|
||||
cwd: this.testDir!,
|
||||
stdio: 'pipe',
|
||||
env: process.env,
|
||||
env: childEnv,
|
||||
});
|
||||
|
||||
let stdout = '';
|
||||
|
|
|
|||
|
|
@ -125,6 +125,7 @@ export interface CliArgs {
|
|||
acp: boolean | undefined;
|
||||
experimentalAcp: boolean | undefined;
|
||||
experimentalLsp: boolean | undefined;
|
||||
experimentalHooks: boolean | undefined;
|
||||
extensions: string[] | undefined;
|
||||
listExtensions: boolean | undefined;
|
||||
openaiLogging: boolean | undefined;
|
||||
|
|
@ -338,6 +339,12 @@ export async function parseArguments(): Promise<CliArgs> {
|
|||
'Enable experimental LSP (Language Server Protocol) feature for code intelligence',
|
||||
default: false,
|
||||
})
|
||||
.option('experimental-hooks', {
|
||||
type: 'boolean',
|
||||
description:
|
||||
'Enable experimental hooks feature for lifecycle event customization',
|
||||
default: false,
|
||||
})
|
||||
.option('channel', {
|
||||
type: 'string',
|
||||
choices: ['VSCode', 'ACP', 'SDK', 'CI'],
|
||||
|
|
@ -1027,6 +1034,8 @@ export async function loadCliConfig(
|
|||
format: outputSettingsFormat,
|
||||
},
|
||||
hooks: settings.hooks,
|
||||
enableHooks:
|
||||
argv.experimentalHooks === true || settings.hooks?.enabled === true,
|
||||
channel: argv.channel,
|
||||
// Precedence: explicit CLI flag > settings file > default(true).
|
||||
// NOTE: do NOT set a yargs default for `chat-recording`, otherwise argv will
|
||||
|
|
|
|||
|
|
@ -1187,6 +1187,16 @@ const SETTINGS_SCHEMA = {
|
|||
'Hook configurations for extending CLI behavior at various lifecycle points.',
|
||||
showInDialog: false,
|
||||
properties: {
|
||||
enabled: {
|
||||
type: 'boolean',
|
||||
label: 'Enable Hooks',
|
||||
category: 'Advanced',
|
||||
requiresRestart: false,
|
||||
default: false,
|
||||
description:
|
||||
'Enable the hooks feature. When enabled, hooks defined in UserPromptSubmit and Stop will be executed.',
|
||||
showInDialog: false,
|
||||
},
|
||||
disabled: {
|
||||
type: 'array',
|
||||
label: 'Disabled Hooks',
|
||||
|
|
|
|||
|
|
@ -497,6 +497,7 @@ describe('gemini.tsx main function kitty protocol', () => {
|
|||
authType: undefined,
|
||||
maxSessionTurns: undefined,
|
||||
experimentalLsp: undefined,
|
||||
experimentalHooks: undefined,
|
||||
channel: undefined,
|
||||
chatRecording: undefined,
|
||||
sessionId: undefined,
|
||||
|
|
|
|||
|
|
@ -688,7 +688,7 @@ export class Config {
|
|||
enabledExtensionOverrides: this.overrideExtensions,
|
||||
isWorkspaceTrusted: this.isTrustedFolder(),
|
||||
});
|
||||
this.enableHooks = params.enableHooks ?? true;
|
||||
this.enableHooks = params.enableHooks ?? false;
|
||||
this.hooks = params.hooks;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue