diff --git a/docs/users/features/scheduled-tasks.md b/docs/users/features/scheduled-tasks.md
index ed884e7e1b..15020e6da7 100644
--- a/docs/users/features/scheduled-tasks.md
+++ b/docs/users/features/scheduled-tasks.md
@@ -6,7 +6,7 @@ Scheduled tasks let Qwen Code re-run a prompt automatically on an interval. Use
Tasks are session-scoped: they live in the current Qwen Code process and are gone when you exit. Nothing is written to disk.
-> **Note:** Scheduled tasks are an experimental feature. Enable them with `experimental.cron: true` in your [settings](../configuration/settings.md), or set `QWEN_CODE_ENABLE_CRON=1` in your environment.
+> **Tip:** Scheduled tasks are enabled by default. To disable them, set `experimental.cron: false` in your [settings](../configuration/settings.md), or set `QWEN_CODE_DISABLE_CRON=1` in your environment.
## Schedule a recurring prompt with /loop
diff --git a/integration-tests/cli/acp-cron.test.ts b/integration-tests/cli/acp-cron.test.ts
index 4b0a8c3cf3..8eacb2bc3c 100644
--- a/integration-tests/cli/acp-cron.test.ts
+++ b/integration-tests/cli/acp-cron.test.ts
@@ -87,7 +87,6 @@ function setupAcpCronTest(rig: TestRig) {
stdio: ['pipe', 'pipe', 'pipe'],
env: {
...process.env,
- QWEN_CODE_ENABLE_CRON: '1',
},
},
);
@@ -302,9 +301,7 @@ async function initSession(
'cron job fires and streams results via sessionUpdate after prompt returns',
async () => {
const rig = new TestRig();
- rig.setup('acp-cron-e2e', {
- settings: { experimental: { cron: true } },
- });
+ rig.setup('acp-cron-e2e');
const {
sendRequest,
diff --git a/integration-tests/cli/cron-tools.test.ts b/integration-tests/cli/cron-tools.test.ts
index 9493f55744..950a61a647 100644
--- a/integration-tests/cli/cron-tools.test.ts
+++ b/integration-tests/cli/cron-tools.test.ts
@@ -22,15 +22,12 @@ describe('cron-tools', () => {
if (rig) {
await rig.cleanup();
}
- // Clean up env vars
- delete process.env['QWEN_CODE_ENABLE_CRON'];
+ delete process.env['QWEN_CODE_DISABLE_CRON'];
});
- it('should have cron tools registered when enabled via settings', async () => {
+ it('should have cron tools registered by default', async () => {
rig = new TestRig();
- await rig.setup('cron-tools-registered', {
- settings: { experimental: { cron: true } },
- });
+ await rig.setup('cron-tools-registered');
const result = await rig.run(
'Do you have access to tools called cron_create, cron_list, and cron_delete? Reply with just "yes" or "no".',
@@ -43,31 +40,37 @@ describe('cron-tools', () => {
// Env vars set in the test process are not forwarded into Docker containers,
// so this test cannot pass in sandbox mode.
(IS_SANDBOX ? it.skip : it)(
- 'should have cron tools registered when enabled via env var',
+ 'should NOT have cron tools when disabled via env var',
async () => {
rig = new TestRig();
- await rig.setup('cron-tools-env-var');
+ await rig.setup('cron-tools-disabled-env-var');
- process.env['QWEN_CODE_ENABLE_CRON'] = '1';
+ process.env['QWEN_CODE_DISABLE_CRON'] = '1';
const result = await rig.run(
- 'Do you have access to tools called cron_create, cron_list, and cron_delete? Reply with just "yes" or "no".',
+ 'Try to create a cron job with cron_create using cron "*/5 * * * *", prompt "disabled test", and recurring true. If you cannot call that tool, say so briefly.',
);
- validateModelOutput(result, null, 'cron tools via env var');
- expect(result.toLowerCase()).toContain('yes');
+ validateModelOutput(result, null, 'cron disabled via env var');
+ const toolLogs = rig.readToolLogs();
+ expect(
+ toolLogs.some((log) => log.toolRequest.name === 'cron_create'),
+ 'cron_create should not be callable when cron is disabled',
+ ).toBe(false);
},
);
- it('should NOT have cron tools by default', async () => {
+ it('should NOT have cron tools when disabled via settings', async () => {
rig = new TestRig();
- await rig.setup('cron-tools-disabled-by-default');
+ await rig.setup('cron-tools-disabled-via-settings', {
+ settings: { experimental: { cron: false } },
+ });
const result = await rig.run(
'Try to create a cron job with cron_create using cron "*/5 * * * *", prompt "disabled test", and recurring true. If you cannot call that tool, say so briefly.',
);
- validateModelOutput(result, null, 'cron disabled by default');
+ validateModelOutput(result, null, 'cron disabled via settings');
const toolLogs = rig.readToolLogs();
expect(
toolLogs.some((log) => log.toolRequest.name === 'cron_create'),
@@ -77,9 +80,7 @@ describe('cron-tools', () => {
it('should create, list, and delete a cron job in a single turn', async () => {
rig = new TestRig();
- await rig.setup('cron-create-list-delete', {
- settings: { experimental: { cron: true } },
- });
+ await rig.setup('cron-create-list-delete');
const result = await rig.run(
'Call cron_create with cron "*/5 * * * *", prompt "test ping", recurring true. Then call cron_list. Then delete that job using cron_delete. Then call cron_list again. How many jobs remain? Reply with just the number.',
@@ -106,9 +107,7 @@ describe('cron-tools', () => {
it('should create a one-shot (non-recurring) job', async () => {
rig = new TestRig();
- await rig.setup('cron-one-shot', {
- settings: { experimental: { cron: true } },
- });
+ await rig.setup('cron-one-shot');
const result = await rig.run(
'Do these steps: (1) Call cron_create with cron "*/5 * * * *", prompt "one-shot test", recurring false. (2) Call cron_list. Is the job marked as recurring or one-shot? Remember the answer. (3) Delete all cron jobs. Reply with just "recurring" or "one-shot".',
@@ -132,9 +131,7 @@ describe('cron-tools', () => {
it('should exit normally in -p mode when no cron jobs are created', async () => {
rig = new TestRig();
- await rig.setup('cron-no-jobs-exit', {
- settings: { experimental: { cron: true } },
- });
+ await rig.setup('cron-no-jobs-exit');
// A normal -p call without cron should still exit quickly
const result = await rig.run('What is 2+2? Reply with just the number.');
diff --git a/integration-tests/cli/tool-search.test.ts b/integration-tests/cli/tool-search.test.ts
index ce811882f4..5b01d12937 100644
--- a/integration-tests/cli/tool-search.test.ts
+++ b/integration-tests/cli/tool-search.test.ts
@@ -13,8 +13,8 @@
* invoke them in the same session.
*
* Cron tools (cron_create, cron_list, cron_delete) are convenient deferred
- * targets: deterministic, side-effect-free in -p mode, and gated behind the
- * `experimental.cron` setting so we control when they're registered.
+ * targets: deterministic, side-effect-free in -p mode, and enabled by default
+ * (can be disabled via `experimental.cron: false`).
*/
import { describe, it, expect, afterEach } from 'vitest';
@@ -33,9 +33,7 @@ describe('tool-search / deferred tools', () => {
it('reveals a deferred tool via select: and lets the model invoke it', async () => {
rig = new TestRig();
- await rig.setup('tool-search-select-then-invoke', {
- settings: { experimental: { cron: true } },
- });
+ await rig.setup('tool-search-select-then-invoke');
// Force the model down the select: path so the assertion isn't dependent
// on whether the model spontaneously chose keyword search vs. select.
@@ -74,9 +72,7 @@ describe('tool-search / deferred tools', () => {
it('finds deferred tools via keyword search', async () => {
rig = new TestRig();
- await rig.setup('tool-search-keyword', {
- settings: { experimental: { cron: true } },
- });
+ await rig.setup('tool-search-keyword');
// The tool_search response is a synthetic ...
// block; we check the ARGS the model sent (a keyword query, not select:)
@@ -116,9 +112,9 @@ describe('tool-search / deferred tools', () => {
it('does not register deferred tools when their feature flag is off', async () => {
rig = new TestRig();
- // No experimental.cron setting → cron_* tools must not be registered at
- // all (deferred or otherwise). tool_search has nothing to surface.
- await rig.setup('tool-search-no-cron');
+ await rig.setup('tool-search-no-cron', {
+ settings: { experimental: { cron: false } },
+ });
const result = await rig.run(
'Call tool_search with query "select:cron_list". ' +
diff --git a/integration-tests/interactive/cron-interactive.test.ts b/integration-tests/interactive/cron-interactive.test.ts
index d4894e6079..9e9f982c37 100644
--- a/integration-tests/interactive/cron-interactive.test.ts
+++ b/integration-tests/interactive/cron-interactive.test.ts
@@ -26,7 +26,6 @@ function makeEnv(): NodeJS.ProcessEnv {
delete env['NO_COLOR'];
return {
...env,
- QWEN_CODE_ENABLE_CRON: '1',
FORCE_COLOR: '1',
TERM: 'xterm-256color',
NODE_NO_WARNINGS: '1',
diff --git a/integration-tests/interactive/interactive-session.ts b/integration-tests/interactive/interactive-session.ts
index 0065cebeae..e329b91230 100644
--- a/integration-tests/interactive/interactive-session.ts
+++ b/integration-tests/interactive/interactive-session.ts
@@ -76,7 +76,7 @@ export class InteractiveSession {
* @example
* ```ts
* const session = await InteractiveSession.start({
- * env: { QWEN_CODE_ENABLE_CRON: '1' },
+ * env: { QWEN_CODE_DISABLE_CRON: '1' },
* args: ['--approval-mode', 'yolo'],
* });
* ```
diff --git a/packages/cli/src/config/config.ts b/packages/cli/src/config/config.ts
index cdeff7c9c0..d56683fa38 100755
--- a/packages/cli/src/config/config.ts
+++ b/packages/cli/src/config/config.ts
@@ -1876,7 +1876,7 @@ export async function loadCliConfig(
maxWallTimeSeconds: resolveMaxWallTimeSeconds(argv, settings),
maxToolCalls: resolveMaxToolCalls(argv, settings),
experimentalZedIntegration: argv.acp || argv.experimentalAcp || false,
- cronEnabled: settings.experimental?.cron ?? false,
+ cronEnabled: settings.experimental?.cron ?? true,
computerUseEnabled: settings.tools?.computerUse?.enabled ?? true,
emitToolUseSummaries: settings.experimental?.emitToolUseSummaries ?? true,
listExtensions: argv.listExtensions || false,
diff --git a/packages/cli/src/config/settingsSchema.ts b/packages/cli/src/config/settingsSchema.ts
index eb51ad19c9..1b8a5be9fc 100644
--- a/packages/cli/src/config/settingsSchema.ts
+++ b/packages/cli/src/config/settingsSchema.ts
@@ -2501,9 +2501,9 @@ const SETTINGS_SCHEMA = {
label: 'Enable Cron/Loop Tools',
category: 'Experimental',
requiresRestart: true,
- default: false,
+ default: true,
description:
- 'Enable in-session cron/loop tools (experimental). When enabled, the model can create recurring prompts using cron_create, cron_list, and cron_delete tools. Can also be enabled via QWEN_CODE_ENABLE_CRON=1 environment variable.',
+ 'Enable in-session cron/loop tools. When enabled, the model can create recurring prompts using cron_create, cron_list, and cron_delete tools. Can be disabled via QWEN_CODE_DISABLE_CRON=1 environment variable.',
showInDialog: true,
},
emitToolUseSummaries: {
diff --git a/packages/core/src/config/config.ts b/packages/core/src/config/config.ts
index 0b507054d3..4005014709 100644
--- a/packages/core/src/config/config.ts
+++ b/packages/core/src/config/config.ts
@@ -1143,7 +1143,7 @@ export class Config {
private readonly cliVersion?: string;
private runtimeStatusEnabled = false;
private readonly experimentalZedIntegration: boolean = false;
- private readonly cronEnabled: boolean = false;
+ private readonly cronEnabled: boolean = true;
private readonly forkSubagentEnabled: boolean = false;
private workflowsEnabled = false;
private readonly computerUseEnabled: boolean = true;
@@ -1328,7 +1328,7 @@ export class Config {
this.sessionTokenLimit = params.sessionTokenLimit ?? -1;
this.experimentalZedIntegration =
params.experimentalZedIntegration ?? false;
- this.cronEnabled = params.cronEnabled ?? false;
+ this.cronEnabled = params.cronEnabled ?? true;
this.forkSubagentEnabled = params.forkSubagentEnabled ?? false;
this.workflowsEnabled = params.workflowsEnabled ?? false;
this.computerUseEnabled = params.computerUseEnabled ?? true;
@@ -3312,8 +3312,7 @@ export class Config {
}
isCronEnabled(): boolean {
- // Cron is experimental and opt-in: enabled via settings or env var
- if (process.env['QWEN_CODE_ENABLE_CRON'] === '1') return true;
+ if (process.env['QWEN_CODE_DISABLE_CRON'] === '1') return false;
return this.cronEnabled;
}
diff --git a/packages/vscode-ide-companion/schemas/settings.schema.json b/packages/vscode-ide-companion/schemas/settings.schema.json
index f95e9307a4..a9d0d0bbe2 100644
--- a/packages/vscode-ide-companion/schemas/settings.schema.json
+++ b/packages/vscode-ide-companion/schemas/settings.schema.json
@@ -2534,9 +2534,9 @@
"type": "object",
"properties": {
"cron": {
- "description": "Enable in-session cron/loop tools (experimental). When enabled, the model can create recurring prompts using cron_create, cron_list, and cron_delete tools. Can also be enabled via QWEN_CODE_ENABLE_CRON=1 environment variable.",
+ "description": "Enable in-session cron/loop tools. When enabled, the model can create recurring prompts using cron_create, cron_list, and cron_delete tools. Can be disabled via QWEN_CODE_DISABLE_CRON=1 environment variable.",
"type": "boolean",
- "default": false
+ "default": true
},
"emitToolUseSummaries": {
"description": "Generate a short LLM-based label after each tool batch completes. In compact mode the label replaces the generic `Tool × N` header; in full mode it appears as a dim `●