Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Next Next commit
fix(cli): never honor memory agent budgets from workspace scope
memory.agentMaxTurns and memory.agentTimeoutMinutes cap the turn count
and wall-clock time of the auto-approved background memory agents that
hold write access to the cross-project memory directory, and 0 disables
each limit. A cloned repository could set them in .qwen/settings.json
and remove those budgets. Add both keys to WORKSPACE_RESTRICTED_SETTINGS
so workspace values are stripped with a warning like the other
restricted keys, and state the allowed scopes in the setting
descriptions.

Closes #13477
Refs #13462
  • Loading branch information
yiliang114 committed Oct 6, 2026
commit 4e94eff7b41fa2289d220a4a0bf53d9e612f4dd0
4 changes: 2 additions & 2 deletions docs/users/configuration/settings.md
Original file line number Diff line number Diff line change
Expand Up @@ -431,8 +431,8 @@ The bridge-availability and missing-bridge warning rules below describe direct t
| `memory.enableTeamMemory` | boolean | Enable a project memory tier shared with collaborators via the git-tracked `.qwen/team-memory/` directory. Writes to it are secret-scanned and reviewable in the git diff. | `false` |
| `memory.enableTeamMemorySync` | boolean | When team memory is enabled, automatically commit, fast-forward-pull, and push the `.qwen/team-memory/` directory at session start so collaborators stay in sync. Requires a configured git upstream. | `false` |
| `memory.enableStructuredRecall` | boolean | Switch memory recall from the flat `MEMORY.md` listing to the structured protocol (hierarchical memory tree, focused subtree, `search_memory` tool). While off, the background metadata migration is never scheduled, so the protocol costs no background model calls. Override with `QWEN_CODE_MEMORY_STRUCTURED_RECALL=0\|1`. Requires a restart. | `false` |
| `memory.agentTimeoutMinutes` | number | Max runtime in minutes for background memory agents (extraction, dream, remember, skill review). Unset uses each agent's built-in default (2–5 minutes); `0` disables the time limit. | unset |
| `memory.agentMaxTurns` | number | Max turns for background memory agents (extraction, dream, remember, skill review). Unset uses each agent's built-in default (5–8); `0` disables the turn limit. | unset |
| `memory.agentTimeoutMinutes` | number | Max runtime in minutes for background memory agents (extraction, dream, remember, skill review). Unset uses each agent's built-in default (2–5 minutes); `0` disables the time limit. Configure in user or system settings. | unset |
| `memory.agentMaxTurns` | number | Max turns for background memory agents (extraction, dream, remember, skill review). Unset uses each agent's built-in default (5–8); `0` disables the turn limit. Configure in user or system settings. | unset |

See [Memory](../features/memory) for details on how auto-memory works and how to use the `/memory`, `/remember`, and `/dream` commands.

Expand Down
58 changes: 57 additions & 1 deletion packages/cli/src/config/settings.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4335,7 +4335,9 @@ describe('Settings Loading and Merging', () => {
workspacePayload[section][key] =
key === 'allowedInsecureVoiceBaseUrls'
? ['http://voice.example/v1']
: true;
: key === 'agentMaxTurns' || key === 'agentTimeoutMinutes'
? 0 // numeric budgets; 0 disables the limit, the value being guarded against
: true;
Comment thread
yiliang114 marked this conversation as resolved.
}
(fs.readFileSync as Mock).mockImplementation(
(p: fs.PathOrFileDescriptor) => {
Expand Down Expand Up @@ -4415,6 +4417,60 @@ describe('Settings Loading and Merging', () => {
});
});

describe('memory agent budget scope handling', () => {
Comment thread
yiliang114 marked this conversation as resolved.
Outdated
it('is listed as workspace-restricted', () => {
expect(WORKSPACE_RESTRICTED_SETTING_KEYS).toContain(
'memory.agentMaxTurns',
);
expect(WORKSPACE_RESTRICTED_SETTING_KEYS).toContain(
'memory.agentTimeoutMinutes',
);
});

it('honors the budgets from user scope, including 0 (limit disabled)', () => {
(mockFsExistsSync as Mock).mockReturnValue(true);
(fs.readFileSync as Mock).mockImplementation(
(p: fs.PathOrFileDescriptor) => {
if (p === USER_SETTINGS_PATH)
return JSON.stringify({
memory: { agentMaxTurns: 0, agentTimeoutMinutes: 0 },
});
return '{}';
},
);

const settings = loadSettings(MOCK_WORKSPACE_DIR);
expect(settings.merged.memory?.agentMaxTurns).toBe(0);
expect(settings.merged.memory?.agentTimeoutMinutes).toBe(0);
});

it('strips a workspace 0 and warns, per key', () => {
// `0` disables the turn and wall-clock budgets of the auto-approved,
// cross-project memory agents, so a cloned repository must not set it.
(mockFsExistsSync as Mock).mockReturnValue(true);
(fs.readFileSync as Mock).mockImplementation(
(p: fs.PathOrFileDescriptor) => {
if (p === MOCK_WORKSPACE_SETTINGS_PATH)
return JSON.stringify({
memory: { agentMaxTurns: 0, agentTimeoutMinutes: 0 },
});
return '{}';
},
);

const settings = loadSettings(MOCK_WORKSPACE_DIR);
expect(settings.merged.memory?.agentMaxTurns).toBeUndefined();
expect(settings.merged.memory?.agentTimeoutMinutes).toBeUndefined();
const warnings = getSettingsWarnings(settings);
expect(warnings.some((w) => w.includes('memory.agentMaxTurns'))).toBe(
true,
);
expect(
warnings.some((w) => w.includes('memory.agentTimeoutMinutes')),
).toBe(true);
});
});

describe('named-workflows-only lock scope handling', () => {
it('honors a workspace that turns the lock on', () => {
(mockFsExistsSync as Mock).mockReturnValue(true);
Expand Down
4 changes: 2 additions & 2 deletions packages/cli/src/config/settingsSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2440,7 +2440,7 @@ const SETTINGS_SCHEMA = {
default: undefined as number | undefined,
minimum: 0,
description:
"Max runtime in minutes for background memory agents (extraction, dream, remember, skill review). Unset uses each agent's built-in default (2–5 minutes); 0 disables the time limit. Useful for slow local models that need longer than the defaults.",
"Max runtime in minutes for background memory agents (extraction, dream, remember, skill review). Unset uses each agent's built-in default (2–5 minutes); 0 disables the time limit. Useful for slow local models that need longer than the defaults. Configure in user or system settings.",
Comment thread
yiliang114 marked this conversation as resolved.
Outdated
showInDialog: false,
},
agentMaxTurns: {
Expand All @@ -2451,7 +2451,7 @@ const SETTINGS_SCHEMA = {
default: undefined as number | undefined,
minimum: 0,
description:
"Max turns for background memory agents (extraction, dream, remember, skill review). Unset uses each agent's built-in default (5–8); 0 disables the turn limit.",
"Max turns for background memory agents (extraction, dream, remember, skill review). Unset uses each agent's built-in default (5–8); 0 disables the turn limit. Configure in user or system settings.",
Comment thread
yiliang114 marked this conversation as resolved.
Outdated
showInDialog: false,
},
enableTeamMemory: {
Expand Down
2 changes: 2 additions & 0 deletions packages/cli/src/config/settingsUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -277,6 +277,8 @@ export function validateSettingValue(
*/
export const WORKSPACE_RESTRICTED_SETTINGS = [
{ section: 'memory', key: 'mem0' },
{ section: 'memory', key: 'agentMaxTurns' },
{ section: 'memory', key: 'agentTimeoutMinutes' },
{ section: 'tools', key: 'executionSandbox' },
{ section: 'tools', key: 'workflowsEnabled' },
{ section: 'security', key: 'allowPrivateNetworkHooks' },
Expand Down
4 changes: 2 additions & 2 deletions packages/vscode-ide-companion/schemas/settings.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -1177,12 +1177,12 @@
"default": true
},
"agentTimeoutMinutes": {
"description": "Max runtime in minutes for background memory agents (extraction, dream, remember, skill review). Unset uses each agent's built-in default (2–5 minutes); 0 disables the time limit. Useful for slow local models that need longer than the defaults.",
"description": "Max runtime in minutes for background memory agents (extraction, dream, remember, skill review). Unset uses each agent's built-in default (2–5 minutes); 0 disables the time limit. Useful for slow local models that need longer than the defaults. Configure in user or system settings.",
"type": "number",
"minimum": 0
},
"agentMaxTurns": {
"description": "Max turns for background memory agents (extraction, dream, remember, skill review). Unset uses each agent's built-in default (5–8); 0 disables the turn limit.",
"description": "Max turns for background memory agents (extraction, dream, remember, skill review). Unset uses each agent's built-in default (5–8); 0 disables the turn limit. Configure in user or system settings.",
"type": "number",
"minimum": 0
},
Expand Down
Loading