Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 13 additions & 12 deletions docs/users/configuration/settings.md
Original file line number Diff line number Diff line change
Expand Up @@ -228,18 +228,19 @@ The `extra_body` field allows you to add custom parameters to the request body s

#### context

| Setting | Type | Description | Default |
| -------------------------------------------------------- | -------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- |
| `context.fileName` | string or array of strings | The name of the context file(s). | `undefined` |
| `context.importFormat` | string | The format to use when importing memory. | `undefined` |
| `context.includeDirectories` | array | Additional directories to include in the workspace context. Specifies an array of additional absolute or relative paths to include in the workspace context. Missing directories will be skipped with a warning by default. Paths can use `~` to refer to the user's home directory. This setting can be combined with the `--include-directories` command-line flag. | `[]` |
| `context.loadFromIncludeDirectories` | boolean | Controls the behavior of the `/memory refresh` command. If set to `true`, `QWEN.md` files should be loaded from all directories that are added. If set to `false`, `QWEN.md` should only be loaded from the current directory. | `false` |
| `context.fileFiltering.respectGitIgnore` | boolean | Respect .gitignore files when searching. | `true` |
| `context.fileFiltering.respectQwenIgnore` | boolean | Respect .qwenignore files when searching. | `true` |
| `context.fileFiltering.enableRecursiveFileSearch` | boolean | Whether to enable searching recursively for filenames under the current tree when completing `@` prefixes in the prompt. | `true` |
| `context.fileFiltering.enableFuzzySearch` | boolean | When `true`, enables fuzzy search capabilities when searching for files. Set to `false` to improve performance on projects with a large number of files. | `true` |
| `context.clearContextOnIdle.toolResultsThresholdMinutes` | number | Minutes of inactivity before clearing old tool result content. Use `-1` to disable. | `60` |
| `context.clearContextOnIdle.toolResultsNumToKeep` | number | Number of most-recent compactable tool results to preserve when clearing. Floor at 1. | `5` |
| Setting | Type | Description | Default |
| ----------------------------------------------------------- | -------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- |
| `context.fileName` | string or array of strings | The name of the context file(s). | `undefined` |
| `context.importFormat` | string | The format to use when importing memory. | `undefined` |
| `context.includeDirectories` | array | Additional directories to include in the workspace context. Specifies an array of additional absolute or relative paths to include in the workspace context. Missing directories will be skipped with a warning by default. Paths can use `~` to refer to the user's home directory. This setting can be combined with the `--include-directories` command-line flag. | `[]` |
| `context.loadFromIncludeDirectories` | boolean | Controls the behavior of the `/memory refresh` command. If set to `true`, `QWEN.md` files should be loaded from all directories that are added. If set to `false`, `QWEN.md` should only be loaded from the current directory. | `false` |
| `context.fileFiltering.respectGitIgnore` | boolean | Respect .gitignore files when searching. | `true` |
| `context.fileFiltering.respectQwenIgnore` | boolean | Respect .qwenignore files when searching. | `true` |
| `context.fileFiltering.enableRecursiveFileSearch` | boolean | Whether to enable searching recursively for filenames under the current tree when completing `@` prefixes in the prompt. | `true` |
| `context.fileFiltering.enableFuzzySearch` | boolean | When `true`, enables fuzzy search capabilities when searching for files. Set to `false` to improve performance on projects with a large number of files. | `true` |
| `context.clearContextOnIdle.toolResultsThresholdMinutes` | number | Minutes of inactivity before clearing old tool result content. Use `-1` to disable the idle trigger. | `60` |
| `context.clearContextOnIdle.toolResultsNumToKeep` | number | Number of most-recent compactable tool results to preserve when clearing. Floor at 1. | `5` |
| `context.clearContextOnIdle.toolResultsTotalCharsThreshold` | number | Total compactable tool result output characters allowed in history before clearing oldest results. Use `-1` to disable the size trigger. This is a soft threshold: protected recent tool results may keep the total above it. | `500000` |

#### Troubleshooting File Search Performance

Expand Down
1 change: 1 addition & 0 deletions packages/cli/src/acp-integration/acpAgent.worktree.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ vi.mock('@qwen-code/qwen-code-core', () => ({
APPROVAL_MODE_INFO: {},
APPROVAL_MODES: [],
DEFAULT_STOP_HOOK_BLOCK_CAP: 8,
DEFAULT_TOOL_RESULTS_TOTAL_CHARS_THRESHOLD: 500_000,
DEFAULT_TRUNCATE_TOOL_OUTPUT_LINES: 1000,
DEFAULT_TRUNCATE_TOOL_OUTPUT_THRESHOLD: 25_000,
ApprovalMode: {
Expand Down
11 changes: 11 additions & 0 deletions packages/cli/src/config/settingsSchema.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,17 @@ describe('SettingsSchema', () => {
).toBeDefined();
});

it('should expose cumulative tool result threshold in clearContextOnIdle', () => {
const threshold =
getSettingsSchema().context.properties.clearContextOnIdle.properties
?.toolResultsTotalCharsThreshold;

expect(threshold).toBeDefined();
expect(threshold?.type).toBe('number');
expect(threshold?.default).toBe(500_000);
expect(threshold?.requiresRestart).toBe(false);
});

it('should have sandboxImage setting under tools', () => {
expect(getSettingsSchema().tools.properties.sandboxImage).toBeDefined();
expect(getSettingsSchema().tools.properties.sandboxImage.type).toBe(
Expand Down
13 changes: 12 additions & 1 deletion packages/cli/src/config/settingsSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
ApprovalMode,
DEFAULT_STOP_HOOK_BLOCK_CAP,
DEFAULT_TOOL_OUTPUT_BATCH_BUDGET,
DEFAULT_TOOL_RESULTS_TOTAL_CHARS_THRESHOLD,
DEFAULT_TRUNCATE_TOOL_OUTPUT_LINES,
DEFAULT_TRUNCATE_TOOL_OUTPUT_THRESHOLD,
} from '@qwen-code/qwen-code-core';
Expand Down Expand Up @@ -1370,7 +1371,7 @@ const SETTINGS_SCHEMA = {
requiresRestart: false,
default: {},
description:
'Settings for clearing stale context after idle periods. Use -1 to disable a threshold.',
'Settings for clearing stale or oversized tool result context. Use -1 to disable a threshold.',
showInDialog: false,
properties: {
toolResultsThresholdMinutes: {
Expand All @@ -1393,6 +1394,16 @@ const SETTINGS_SCHEMA = {
'Number of most-recent compactable tool results to preserve when clearing. Floor at 1.',
showInDialog: false,
},
toolResultsTotalCharsThreshold: {
type: 'number',
label: 'Tool Results Total Chars Threshold',
category: 'Context',
requiresRestart: false,
default: DEFAULT_TOOL_RESULTS_TOTAL_CHARS_THRESHOLD as number,
description:
'Total compactable tool result output characters allowed in history before clearing oldest results. Use -1 to disable. This is a soft threshold: protected recent tool results may keep the total above it.',
showInDialog: false,
},
},
},
fileFiltering: {
Expand Down
7 changes: 7 additions & 0 deletions packages/core/src/config/clearContextDefaults.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/**
* @license
* Copyright 2026 Qwen Team
* SPDX-License-Identifier: Apache-2.0
*/

export const DEFAULT_TOOL_RESULTS_TOTAL_CHARS_THRESHOLD = 500_000;
70 changes: 70 additions & 0 deletions packages/core/src/config/config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2865,6 +2865,76 @@ describe('Server Config (config.ts)', () => {
});
});

describe('getClearContextOnIdle', () => {
it('should default the cumulative tool result threshold to 500000 chars', () => {
const config = new Config(baseParams);

expect(config.getClearContextOnIdle()).toMatchObject({
toolResultsThresholdMinutes: 60,
toolResultsNumToKeep: 5,
toolResultsTotalCharsThreshold: 500_000,
});
});

it('should use a custom cumulative tool result threshold if provided', () => {
const config = new Config({
...baseParams,
clearContextOnIdle: {
toolResultsTotalCharsThreshold: 123_456,
},
});

expect(
config.getClearContextOnIdle().toolResultsTotalCharsThreshold,
).toBe(123_456);
});

it('should preserve an explicit disabled cumulative tool result threshold', () => {
const config = new Config({
...baseParams,
clearContextOnIdle: {
toolResultsTotalCharsThreshold: -1,
},
});

expect(config.getClearContextOnIdle()).toMatchObject({
toolResultsThresholdMinutes: 60,
toolResultsNumToKeep: 5,
toolResultsTotalCharsThreshold: -1,
});
});

it('should keep legacy disabled idle cleanup disabled for the size trigger too', () => {
const config = new Config({
...baseParams,
clearContextOnIdle: {
toolResultsThresholdMinutes: -1,
},
});

expect(config.getClearContextOnIdle()).toMatchObject({
toolResultsThresholdMinutes: -1,
toolResultsNumToKeep: 5,
toolResultsTotalCharsThreshold: -1,
});
});

it('should treat any negative legacy idle threshold as disabling the size trigger too', () => {
const config = new Config({
...baseParams,
clearContextOnIdle: {
toolResultsThresholdMinutes: -2,
},
});

expect(config.getClearContextOnIdle()).toMatchObject({
toolResultsThresholdMinutes: -2,
toolResultsNumToKeep: 5,
toolResultsTotalCharsThreshold: -1,
});
});
});

// PR 14b fix (codex round 4 — wenshao gpt-5.5 review): the
// `Config.setMcpBudgetEventCallback → pendingMcpBudgetCallback →
// createToolRegistry → registry.getMcpClientManager().setOnBudgetEvent`
Expand Down
25 changes: 20 additions & 5 deletions packages/core/src/config/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ import {
DEFAULT_FILE_FILTERING_OPTIONS,
DEFAULT_MEMORY_FILE_FILTERING_OPTIONS,
} from './constants.js';
import { DEFAULT_TOOL_RESULTS_TOTAL_CHARS_THRESHOLD } from './clearContextDefaults.js';
import { DEFAULT_QWEN_EMBEDDING_MODEL } from './models.js';
import { Storage } from './storage.js';
import { ChatRecordingService } from '../services/chatRecordingService.js';
Expand Down Expand Up @@ -357,15 +358,23 @@ export interface ChatCompressionSettings {
screenshotTriggerThreshold?: number;
}

export { DEFAULT_TOOL_RESULTS_TOTAL_CHARS_THRESHOLD } from './clearContextDefaults.js';

/**
* Settings for clearing stale context after idle periods.
* Settings for clearing stale or oversized tool-result context.
* Threshold values of -1 mean "never clear" (disabled).
*/

export interface ClearContextOnIdleSettings {
/** Minutes idle before clearing old tool results. Default 60. Use -1 to disable. */
toolResultsThresholdMinutes?: number;
/** Number of most-recent tool results to preserve. Default 5. */
toolResultsNumToKeep?: number;
/**
* Total compactable tool result output chars before clearing old results.
* Default 500000. Use -1 to disable.
*/
toolResultsTotalCharsThreshold?: number;
}

export interface TelemetrySettings {
Expand Down Expand Up @@ -1417,11 +1426,17 @@ export class Config {
this.maxSessionTurns = params.maxSessionTurns ?? -1;
this.maxWallTimeSeconds = params.maxWallTimeSeconds ?? -1;
this.maxToolCalls = params.maxToolCalls ?? -1;
const clearContextOnIdle = params.clearContextOnIdle;
const toolResultsThresholdMinutes =
clearContextOnIdle?.toolResultsThresholdMinutes ?? 60;
this.clearContextOnIdle = {
toolResultsThresholdMinutes:
params.clearContextOnIdle?.toolResultsThresholdMinutes ?? 60,
toolResultsNumToKeep:
params.clearContextOnIdle?.toolResultsNumToKeep ?? 5,
toolResultsThresholdMinutes,
toolResultsNumToKeep: clearContextOnIdle?.toolResultsNumToKeep ?? 5,
toolResultsTotalCharsThreshold:
clearContextOnIdle?.toolResultsTotalCharsThreshold ??
((clearContextOnIdle?.toolResultsThresholdMinutes ?? 0) < 0
? -1
: DEFAULT_TOOL_RESULTS_TOTAL_CHARS_THRESHOLD),
};
this.sessionTokenLimit = params.sessionTokenLimit ?? -1;
this.experimentalZedIntegration =
Expand Down
Loading
Loading