From fe7ff5b148b26ba4044bc1a7d8dd5e9951767c41 Mon Sep 17 00:00:00 2001 From: LaZzyMan Date: Fri, 26 Dec 2025 17:09:16 +0800 Subject: [PATCH] feat: stable-acp-flag --- .gitignore | 1 + docs/users/configuration/settings.md | 2 +- docs/users/integration-zed.md | 2 +- integration-tests/acp-integration.test.ts | 105 +++++++++++++++++- packages/cli/src/config/config.ts | 26 ++++- packages/cli/src/gemini.test.tsx | 1 + .../src/services/acpConnection.ts | 2 +- 7 files changed, 130 insertions(+), 9 deletions(-) diff --git a/.gitignore b/.gitignore index fac00d412..705216c80 100644 --- a/.gitignore +++ b/.gitignore @@ -23,6 +23,7 @@ package-lock.json .idea *.iml .cursor +.qoder # OS metadata .DS_Store diff --git a/docs/users/configuration/settings.md b/docs/users/configuration/settings.md index 3e87985b8..9cf704dae 100644 --- a/docs/users/configuration/settings.md +++ b/docs/users/configuration/settings.md @@ -381,7 +381,7 @@ Arguments passed directly when running the CLI can override other configurations | `--telemetry-otlp-protocol` | | Sets the OTLP protocol for telemetry (`grpc` or `http`). | | Defaults to `grpc`. See [telemetry](../../developers/development/telemetry) for more information. | | `--telemetry-log-prompts` | | Enables logging of prompts for telemetry. | | See [telemetry](../../developers/development/telemetry) for more information. | | `--checkpointing` | | Enables [checkpointing](../features/checkpointing). | | | -| `--experimental-acp` | | Enables ACP mode (Agent Control Protocol). Useful for IDE/editor integrations like [Zed](../integration-zed). | | Experimental. | +| `--acp` | | Enables ACP mode (Agent Control Protocol). Useful for IDE/editor integrations like [Zed](../integration-zed). | | Stable. Replaces the deprecated `--experimental-acp` flag. | | `--experimental-skills` | | Enables experimental [Agent Skills](../features/skills) (registers the `skill` tool and loads Skills from `.qwen/skills/` and `~/.qwen/skills/`). | | Experimental. | | `--extensions` | `-e` | Specifies a list of extensions to use for the session. | Extension names | If not provided, all available extensions are used. Use the special term `qwen -e none` to disable all extensions. Example: `qwen -e my-extension -e my-other-extension` | | `--list-extensions` | `-l` | Lists all available extensions and exits. | | | diff --git a/docs/users/integration-zed.md b/docs/users/integration-zed.md index cd4cb2ae4..663e23e80 100644 --- a/docs/users/integration-zed.md +++ b/docs/users/integration-zed.md @@ -32,7 +32,7 @@ "Qwen Code": { "type": "custom", "command": "qwen", - "args": ["--experimental-acp"], + "args": ["--acp"], "env": {} } ``` diff --git a/integration-tests/acp-integration.test.ts b/integration-tests/acp-integration.test.ts index 31e32da76..b89292d87 100644 --- a/integration-tests/acp-integration.test.ts +++ b/integration-tests/acp-integration.test.ts @@ -80,10 +80,11 @@ type PermissionHandler = ( /** * Sets up an ACP test environment with all necessary utilities. + * @param useNewFlag - If true, uses --acp; if false, uses --experimental-acp (for backward compatibility testing) */ function setupAcpTest( rig: TestRig, - options?: { permissionHandler?: PermissionHandler }, + options?: { permissionHandler?: PermissionHandler; useNewFlag?: boolean }, ) { const pending = new Map(); let nextRequestId = 1; @@ -95,9 +96,13 @@ function setupAcpTest( const permissionHandler = options?.permissionHandler ?? (() => ({ optionId: 'proceed_once' })); + // Use --acp by default, but allow testing with --experimental-acp for backward compatibility + const acpFlag = + options?.useNewFlag !== false ? '--acp' : '--experimental-acp'; + const agent = spawn( 'node', - [rig.bundlePath, '--experimental-acp', '--no-chat-recording'], + [rig.bundlePath, acpFlag, '--no-chat-recording'], { cwd: rig.testDir!, stdio: ['pipe', 'pipe', 'pipe'], @@ -621,3 +626,99 @@ function setupAcpTest( } }); }); + +(IS_SANDBOX ? describe.skip : describe)( + 'acp flag backward compatibility', + () => { + it('should work with deprecated --experimental-acp flag and show warning', async () => { + const rig = new TestRig(); + rig.setup('acp backward compatibility'); + + const { sendRequest, cleanup, stderr } = setupAcpTest(rig, { + useNewFlag: false, + }); + + try { + const initResult = await sendRequest('initialize', { + protocolVersion: 1, + clientCapabilities: { + fs: { readTextFile: true, writeTextFile: true }, + }, + }); + expect(initResult).toBeDefined(); + + // Verify deprecation warning is shown + const stderrOutput = stderr.join(''); + expect(stderrOutput).toContain('--experimental-acp is deprecated'); + expect(stderrOutput).toContain('Please use --acp instead'); + + await sendRequest('authenticate', { methodId: 'openai' }); + + const newSession = (await sendRequest('session/new', { + cwd: rig.testDir!, + mcpServers: [], + })) as { sessionId: string }; + expect(newSession.sessionId).toBeTruthy(); + + // Verify functionality still works + const promptResult = await sendRequest('session/prompt', { + sessionId: newSession.sessionId, + prompt: [{ type: 'text', text: 'Say hello.' }], + }); + expect(promptResult).toBeDefined(); + } catch (e) { + if (stderr.length) { + console.error('Agent stderr:', stderr.join('')); + } + throw e; + } finally { + await cleanup(); + } + }); + + it('should work with new --acp flag without warnings', async () => { + const rig = new TestRig(); + rig.setup('acp new flag'); + + const { sendRequest, cleanup, stderr } = setupAcpTest(rig, { + useNewFlag: true, + }); + + try { + const initResult = await sendRequest('initialize', { + protocolVersion: 1, + clientCapabilities: { + fs: { readTextFile: true, writeTextFile: true }, + }, + }); + expect(initResult).toBeDefined(); + + // Verify no deprecation warning is shown + const stderrOutput = stderr.join(''); + expect(stderrOutput).not.toContain('--experimental-acp is deprecated'); + + await sendRequest('authenticate', { methodId: 'openai' }); + + const newSession = (await sendRequest('session/new', { + cwd: rig.testDir!, + mcpServers: [], + })) as { sessionId: string }; + expect(newSession.sessionId).toBeTruthy(); + + // Verify functionality works + const promptResult = await sendRequest('session/prompt', { + sessionId: newSession.sessionId, + prompt: [{ type: 'text', text: 'Say hello.' }], + }); + expect(promptResult).toBeDefined(); + } catch (e) { + if (stderr.length) { + console.error('Agent stderr:', stderr.join('')); + } + throw e; + } finally { + await cleanup(); + } + }); + }, +); diff --git a/packages/cli/src/config/config.ts b/packages/cli/src/config/config.ts index 7cd7d685a..e45ec2a3b 100755 --- a/packages/cli/src/config/config.ts +++ b/packages/cli/src/config/config.ts @@ -111,6 +111,7 @@ export interface CliArgs { telemetryOutfile: string | undefined; allowedMcpServerNames: string[] | undefined; allowedTools: string[] | undefined; + acp: boolean | undefined; experimentalAcp: boolean | undefined; experimentalSkills: boolean | undefined; extensions: string[] | undefined; @@ -304,10 +305,16 @@ export async function parseArguments(settings: Settings): Promise { description: 'Enables checkpointing of file edits', default: false, }) - .option('experimental-acp', { + .option('acp', { type: 'boolean', description: 'Starts the agent in ACP mode', }) + .option('experimental-acp', { + type: 'boolean', + description: + 'Starts the agent in ACP mode (deprecated, use --acp instead)', + hidden: true, + }) .option('experimental-skills', { type: 'boolean', description: 'Enable experimental Skills feature', @@ -589,8 +596,19 @@ export async function parseArguments(settings: Settings): Promise { // The import format is now only controlled by settings.memoryImportFormat // We no longer accept it as a CLI argument - // Apply ACP fallback: if experimental-acp is present but no explicit --channel, treat as ACP - if (result['experimentalAcp'] && !result['channel']) { + // Handle deprecated --experimental-acp flag + if (result['experimentalAcp']) { + console.warn( + '\x1b[33m⚠ Warning: --experimental-acp is deprecated and will be removed in a future release. Please use --acp instead.\x1b[0m', + ); + // Map experimental-acp to acp if acp is not explicitly set + if (!result['acp']) { + (result as Record)['acp'] = true; + } + } + + // Apply ACP fallback: if acp or experimental-acp is present but no explicit --channel, treat as ACP + if ((result['acp'] || result['experimentalAcp']) && !result['channel']) { (result as Record)['channel'] = 'ACP'; } @@ -981,7 +999,7 @@ export async function loadCliConfig( sessionTokenLimit: settings.model?.sessionTokenLimit ?? -1, maxSessionTurns: argv.maxSessionTurns ?? settings.model?.maxSessionTurns ?? -1, - experimentalZedIntegration: argv.experimentalAcp || false, + experimentalZedIntegration: argv.acp || argv.experimentalAcp || false, experimentalSkills: argv.experimentalSkills || false, listExtensions: argv.listExtensions || false, extensions: allExtensions, diff --git a/packages/cli/src/gemini.test.tsx b/packages/cli/src/gemini.test.tsx index 9fa0b8261..064b67fc5 100644 --- a/packages/cli/src/gemini.test.tsx +++ b/packages/cli/src/gemini.test.tsx @@ -460,6 +460,7 @@ describe('gemini.tsx main function kitty protocol', () => { telemetryOutfile: undefined, allowedMcpServerNames: undefined, allowedTools: undefined, + acp: undefined, experimentalAcp: undefined, experimentalSkills: undefined, extensions: undefined, diff --git a/packages/vscode-ide-companion/src/services/acpConnection.ts b/packages/vscode-ide-companion/src/services/acpConnection.ts index 4b2c4028b..219f7809f 100644 --- a/packages/vscode-ide-companion/src/services/acpConnection.ts +++ b/packages/vscode-ide-companion/src/services/acpConnection.ts @@ -95,7 +95,7 @@ export class AcpConnection { const spawnCommand: string = process.execPath; const spawnArgs: string[] = [ cliEntryPath, - '--experimental-acp', + '--acp', '--channel=VSCode', ...extraArgs, ];