Compare commits

..

1 Commits

Author SHA1 Message Date
github-actions[bot]
1041becda3 chore(release): v0.7.2-preview.1 2026-01-20 02:22:49 +00:00
17 changed files with 72 additions and 211 deletions

12
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "@qwen-code/qwen-code",
"version": "0.7.1",
"version": "0.7.2-preview.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@qwen-code/qwen-code",
"version": "0.7.1",
"version": "0.7.2-preview.1",
"workspaces": [
"packages/*"
],
@@ -17310,7 +17310,7 @@
},
"packages/cli": {
"name": "@qwen-code/qwen-code",
"version": "0.7.1",
"version": "0.7.2-preview.1",
"dependencies": {
"@google/genai": "1.30.0",
"@iarna/toml": "^2.2.5",
@@ -17947,7 +17947,7 @@
},
"packages/core": {
"name": "@qwen-code/qwen-code-core",
"version": "0.7.1",
"version": "0.7.2-preview.1",
"hasInstallScript": true,
"dependencies": {
"@anthropic-ai/sdk": "^0.36.1",
@@ -21408,7 +21408,7 @@
},
"packages/test-utils": {
"name": "@qwen-code/qwen-code-test-utils",
"version": "0.7.1",
"version": "0.7.2-preview.1",
"dev": true,
"license": "Apache-2.0",
"devDependencies": {
@@ -21420,7 +21420,7 @@
},
"packages/vscode-ide-companion": {
"name": "qwen-code-vscode-ide-companion",
"version": "0.7.1",
"version": "0.7.2-preview.1",
"license": "LICENSE",
"dependencies": {
"@modelcontextprotocol/sdk": "^1.25.1",

View File

@@ -1,6 +1,6 @@
{
"name": "@qwen-code/qwen-code",
"version": "0.7.1",
"version": "0.7.2-preview.1",
"engines": {
"node": ">=20.0.0"
},
@@ -13,7 +13,7 @@
"url": "git+https://github.com/QwenLM/qwen-code.git"
},
"config": {
"sandboxImageUri": "ghcr.io/qwenlm/qwen-code:0.7.1"
"sandboxImageUri": "ghcr.io/qwenlm/qwen-code:0.7.2-preview.1"
},
"scripts": {
"start": "cross-env node scripts/start.js",

View File

@@ -1,6 +1,6 @@
{
"name": "@qwen-code/qwen-code",
"version": "0.7.1",
"version": "0.7.2-preview.1",
"description": "Qwen Code",
"repository": {
"type": "git",
@@ -33,7 +33,7 @@
"dist"
],
"config": {
"sandboxImageUri": "ghcr.io/qwenlm/qwen-code:0.7.1"
"sandboxImageUri": "ghcr.io/qwenlm/qwen-code:0.7.2-preview.1"
},
"dependencies": {
"@google/genai": "1.30.0",

View File

@@ -8,7 +8,6 @@
import { z } from 'zod';
import * as schema from './schema.js';
import { ACP_ERROR_CODES } from './errorCodes.js';
export * from './schema.js';
import type { WritableStream, ReadableStream } from 'node:stream/web';
@@ -350,51 +349,27 @@ export class RequestError extends Error {
}
static parseError(details?: string): RequestError {
return new RequestError(
ACP_ERROR_CODES.PARSE_ERROR,
'Parse error',
details,
);
return new RequestError(-32700, 'Parse error', details);
}
static invalidRequest(details?: string): RequestError {
return new RequestError(
ACP_ERROR_CODES.INVALID_REQUEST,
'Invalid request',
details,
);
return new RequestError(-32600, 'Invalid request', details);
}
static methodNotFound(details?: string): RequestError {
return new RequestError(
ACP_ERROR_CODES.METHOD_NOT_FOUND,
'Method not found',
details,
);
return new RequestError(-32601, 'Method not found', details);
}
static invalidParams(details?: string): RequestError {
return new RequestError(
ACP_ERROR_CODES.INVALID_PARAMS,
'Invalid params',
details,
);
return new RequestError(-32602, 'Invalid params', details);
}
static internalError(details?: string): RequestError {
return new RequestError(
ACP_ERROR_CODES.INTERNAL_ERROR,
'Internal error',
details,
);
return new RequestError(-32603, 'Internal error', details);
}
static authRequired(details?: string): RequestError {
return new RequestError(
ACP_ERROR_CODES.AUTH_REQUIRED,
'Authentication required',
details,
);
return new RequestError(-32000, 'Authentication required', details);
}
toResult<T>(): Result<T> {

View File

@@ -1,25 +0,0 @@
/**
* @license
* Copyright 2025 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
export const ACP_ERROR_CODES = {
// Parse error: invalid JSON received by server.
PARSE_ERROR: -32700,
// Invalid request: JSON is not a valid Request object.
INVALID_REQUEST: -32600,
// Method not found: method does not exist or is unavailable.
METHOD_NOT_FOUND: -32601,
// Invalid params: invalid method parameter(s).
INVALID_PARAMS: -32602,
// Internal error: implementation-defined server error.
INTERNAL_ERROR: -32603,
// Authentication required: must authenticate before operation.
AUTH_REQUIRED: -32000,
// Resource not found: e.g. missing file.
RESOURCE_NOT_FOUND: -32002,
} as const;
export type AcpErrorCode =
(typeof ACP_ERROR_CODES)[keyof typeof ACP_ERROR_CODES];

View File

@@ -7,7 +7,6 @@
import { describe, expect, it, vi } from 'vitest';
import type { FileSystemService } from '@qwen-code/qwen-code-core';
import { AcpFileSystemService } from './filesystem.js';
import { ACP_ERROR_CODES } from '../errorCodes.js';
const createFallback = (): FileSystemService => ({
readTextFile: vi.fn(),
@@ -17,13 +16,11 @@ const createFallback = (): FileSystemService => ({
describe('AcpFileSystemService', () => {
describe('readTextFile ENOENT handling', () => {
it('converts RESOURCE_NOT_FOUND error to ENOENT', async () => {
const resourceNotFoundError = {
code: ACP_ERROR_CODES.RESOURCE_NOT_FOUND,
message: 'File not found',
};
it('parses path from ACP ENOENT message (quoted)', async () => {
const client = {
readTextFile: vi.fn().mockRejectedValue(resourceNotFoundError),
readTextFile: vi
.fn()
.mockResolvedValue({ content: 'ERROR: ENOENT: "/remote/file.txt"' }),
} as unknown as import('../acp.js').Client;
const svc = new AcpFileSystemService(
@@ -33,20 +30,15 @@ describe('AcpFileSystemService', () => {
createFallback(),
);
await expect(svc.readTextFile('/some/file.txt')).rejects.toMatchObject({
await expect(svc.readTextFile('/local/file.txt')).rejects.toMatchObject({
code: 'ENOENT',
errno: -2,
path: '/some/file.txt',
path: '/remote/file.txt',
});
});
it('re-throws other errors unchanged', async () => {
const otherError = {
code: ACP_ERROR_CODES.INTERNAL_ERROR,
message: 'Internal error',
};
it('falls back to requested path when none provided', async () => {
const client = {
readTextFile: vi.fn().mockRejectedValue(otherError),
readTextFile: vi.fn().mockResolvedValue({ content: 'ERROR: ENOENT:' }),
} as unknown as import('../acp.js').Client;
const svc = new AcpFileSystemService(
@@ -56,34 +48,12 @@ describe('AcpFileSystemService', () => {
createFallback(),
);
await expect(svc.readTextFile('/some/file.txt')).rejects.toMatchObject({
code: ACP_ERROR_CODES.INTERNAL_ERROR,
message: 'Internal error',
await expect(
svc.readTextFile('/fallback/path.txt'),
).rejects.toMatchObject({
code: 'ENOENT',
path: '/fallback/path.txt',
});
});
it('uses fallback when readTextFile capability is disabled', async () => {
const client = {
readTextFile: vi.fn(),
} as unknown as import('../acp.js').Client;
const fallback = createFallback();
(fallback.readTextFile as ReturnType<typeof vi.fn>).mockResolvedValue(
'fallback content',
);
const svc = new AcpFileSystemService(
client,
'session-3',
{ readTextFile: false, writeTextFile: true },
fallback,
);
const result = await svc.readTextFile('/some/file.txt');
expect(result).toBe('fallback content');
expect(fallback.readTextFile).toHaveBeenCalledWith('/some/file.txt');
expect(client.readTextFile).not.toHaveBeenCalled();
});
});
});

View File

@@ -6,7 +6,6 @@
import type { FileSystemService } from '@qwen-code/qwen-code-core';
import type * as acp from '../acp.js';
import { ACP_ERROR_CODES } from '../errorCodes.js';
/**
* ACP client-based implementation of FileSystemService
@@ -24,31 +23,25 @@ export class AcpFileSystemService implements FileSystemService {
return this.fallback.readTextFile(filePath);
}
let response: { content: string };
try {
response = await this.client.readTextFile({
path: filePath,
sessionId: this.sessionId,
line: null,
limit: null,
});
} catch (error) {
const errorCode =
typeof error === 'object' && error !== null && 'code' in error
? (error as { code?: unknown }).code
: undefined;
const response = await this.client.readTextFile({
path: filePath,
sessionId: this.sessionId,
line: null,
limit: null,
});
if (errorCode === ACP_ERROR_CODES.RESOURCE_NOT_FOUND) {
const err = new Error(
`File not found: ${filePath}`,
) as NodeJS.ErrnoException;
err.code = 'ENOENT';
err.errno = -2;
err.path = filePath;
throw err;
}
throw error;
if (response.content.startsWith('ERROR: ENOENT:')) {
// Treat ACP error strings as structured ENOENT errors without
// assuming a specific platform format.
const match = /^ERROR:\s*ENOENT:\s*(?<path>.*)$/i.exec(response.content);
const err = new Error(response.content) as NodeJS.ErrnoException;
err.code = 'ENOENT';
err.errno = -2;
const rawPath = match?.groups?.['path']?.trim();
err['path'] = rawPath
? rawPath.replace(/^['"]|['"]$/g, '') || filePath
: filePath;
throw err;
}
return response.content;

View File

@@ -1,6 +1,6 @@
{
"name": "@qwen-code/qwen-code-core",
"version": "0.7.1",
"version": "0.7.2-preview.1",
"description": "Qwen Code Core",
"repository": {
"type": "git",

View File

@@ -102,14 +102,16 @@ export const QWEN_OAUTH_ALLOWED_MODELS = [
export const QWEN_OAUTH_MODELS: ModelConfig[] = [
{
id: 'coder-model',
name: 'coder-model',
description: 'The latest Qwen Coder model from Alibaba Cloud ModelStudio',
name: 'Qwen Coder',
description:
'The latest Qwen Coder model from Alibaba Cloud ModelStudio (version: qwen3-coder-plus-2025-09-23)',
capabilities: { vision: false },
},
{
id: 'vision-model',
name: 'vision-model',
description: 'The latest Qwen Vision model from Alibaba Cloud ModelStudio',
name: 'Qwen Vision',
description:
'The latest Qwen Vision model from Alibaba Cloud ModelStudio (version: qwen3-vl-plus-2025-09-23)',
capabilities: { vision: true },
},
];

View File

@@ -1,6 +1,6 @@
{
"name": "@qwen-code/qwen-code-test-utils",
"version": "0.7.1",
"version": "0.7.2-preview.1",
"private": true,
"main": "src/index.ts",
"license": "Apache-2.0",

View File

@@ -2,7 +2,7 @@
"name": "qwen-code-vscode-ide-companion",
"displayName": "Qwen Code Companion",
"description": "Enable Qwen Code with direct access to your VS Code workspace.",
"version": "0.7.1",
"version": "0.7.2-preview.1",
"publisher": "qwenlm",
"icon": "assets/icon.png",
"repository": {

View File

@@ -23,23 +23,3 @@ export const CLIENT_METHODS = {
session_request_permission: 'session/request_permission',
session_update: 'session/update',
} as const;
export const ACP_ERROR_CODES = {
// Parse error: invalid JSON received by server.
PARSE_ERROR: -32700,
// Invalid request: JSON is not a valid Request object.
INVALID_REQUEST: -32600,
// Method not found: method does not exist or is unavailable.
METHOD_NOT_FOUND: -32601,
// Invalid params: invalid method parameter(s).
INVALID_PARAMS: -32602,
// Internal error: implementation-defined server error.
INTERNAL_ERROR: -32603,
// Authentication required: must authenticate before operation.
AUTH_REQUIRED: -32000,
// Resource not found: e.g. missing file.
RESOURCE_NOT_FOUND: -32002,
} as const;
export type AcpErrorCode =
(typeof ACP_ERROR_CODES)[keyof typeof ACP_ERROR_CODES];

View File

@@ -28,7 +28,6 @@ import * as os from 'node:os';
import type { z } from 'zod';
import type { DiffManager } from './diff-manager.js';
import { OpenFilesManager } from './open-files-manager.js';
import { ACP_ERROR_CODES } from './constants/acpSchema.js';
class CORSError extends Error {
constructor(message: string) {
@@ -265,7 +264,7 @@ export class IDEServer {
res.status(400).json({
jsonrpc: '2.0',
error: {
code: ACP_ERROR_CODES.AUTH_REQUIRED,
code: -32000,
message:
'Bad Request: No valid session ID provided for non-initialize request.',
},
@@ -284,7 +283,7 @@ export class IDEServer {
res.status(500).json({
jsonrpc: '2.0' as const,
error: {
code: ACP_ERROR_CODES.INTERNAL_ERROR,
code: -32603,
message: 'Internal server error',
},
id: null,

View File

@@ -5,7 +5,6 @@
*/
import { JSONRPC_VERSION } from '../types/acpTypes.js';
import { ACP_ERROR_CODES } from '../constants/acpSchema.js';
import type {
AcpMessage,
AcpPermissionRequest,
@@ -233,34 +232,12 @@ export class AcpConnection {
})
.catch((error) => {
if ('id' in message && typeof message.id === 'number') {
const errorMessage =
error instanceof Error
? error.message
: typeof error === 'object' &&
error !== null &&
'message' in error &&
typeof (error as { message: unknown }).message === 'string'
? (error as { message: string }).message
: String(error);
let errorCode: number = ACP_ERROR_CODES.INTERNAL_ERROR;
const errorCodeValue =
typeof error === 'object' && error !== null && 'code' in error
? (error as { code?: unknown }).code
: undefined;
if (typeof errorCodeValue === 'number') {
errorCode = errorCodeValue;
} else if (errorCodeValue === 'ENOENT') {
errorCode = ACP_ERROR_CODES.RESOURCE_NOT_FOUND;
}
this.messageHandler.sendResponseMessage(this.child, {
jsonrpc: JSONRPC_VERSION,
id: message.id,
error: {
code: errorCode,
message: errorMessage,
code: -32603,
message: error instanceof Error ? error.message : String(error),
},
});
}

View File

@@ -66,11 +66,6 @@ export class AcpFileHandler {
const errorMsg = error instanceof Error ? error.message : String(error);
console.error(`[ACP] Failed to read file ${params.path}:`, errorMsg);
const nodeError = error as NodeJS.ErrnoException;
if (nodeError?.code === 'ENOENT') {
throw error;
}
throw new Error(`Failed to read file '${params.path}': ${errorMsg}`);
}
}

View File

@@ -4,11 +4,9 @@
* SPDX-License-Identifier: Apache-2.0
*/
import { ACP_ERROR_CODES } from '../constants/acpSchema.js';
const AUTH_ERROR_PATTERNS = [
'Authentication required', // Standard authentication request message
`(code: ${ACP_ERROR_CODES.AUTH_REQUIRED})`, // RPC error code indicates auth failure
'(code: -32000)', // RPC error code -32000 indicates authentication failure
'Unauthorized', // HTTP unauthorized error
'Invalid token', // Invalid token
'Session expired', // Session expired

View File

@@ -8,9 +8,6 @@ import * as vscode from 'vscode';
import { BaseMessageHandler } from './BaseMessageHandler.js';
import type { ChatMessage } from '../../services/qwenAgentManager.js';
import type { ApprovalModeValue } from '../../types/approvalModeValueTypes.js';
import { ACP_ERROR_CODES } from '../../constants/acpSchema.js';
const AUTH_REQUIRED_CODE_PATTERN = `(code: ${ACP_ERROR_CODES.AUTH_REQUIRED})`;
/**
* Session message handler
@@ -358,7 +355,7 @@ export class SessionMessageHandler extends BaseMessageHandler {
createErr instanceof Error ? createErr.message : String(createErr);
if (
errorMsg.includes('Authentication required') ||
errorMsg.includes(AUTH_REQUIRED_CODE_PATTERN)
errorMsg.includes('(code: -32000)')
) {
await this.promptLogin(
'Your login session has expired or is invalid. Please login again to continue using Qwen Code.',
@@ -424,7 +421,7 @@ export class SessionMessageHandler extends BaseMessageHandler {
errorMsg.includes('Session not found') ||
errorMsg.includes('No active ACP session') ||
errorMsg.includes('Authentication required') ||
errorMsg.includes(AUTH_REQUIRED_CODE_PATTERN) ||
errorMsg.includes('(code: -32000)') ||
errorMsg.includes('Unauthorized') ||
errorMsg.includes('Invalid token')
) {
@@ -515,7 +512,7 @@ export class SessionMessageHandler extends BaseMessageHandler {
// Check for authentication/session expiration errors
if (
errorMsg.includes('Authentication required') ||
errorMsg.includes(AUTH_REQUIRED_CODE_PATTERN) ||
errorMsg.includes('(code: -32000)') ||
errorMsg.includes('Unauthorized') ||
errorMsg.includes('Invalid token') ||
errorMsg.includes('No active ACP session')
@@ -625,7 +622,7 @@ export class SessionMessageHandler extends BaseMessageHandler {
// Check for authentication/session expiration errors
if (
errorMsg.includes('Authentication required') ||
errorMsg.includes(AUTH_REQUIRED_CODE_PATTERN) ||
errorMsg.includes('(code: -32000)') ||
errorMsg.includes('Unauthorized') ||
errorMsg.includes('Invalid token') ||
errorMsg.includes('No active ACP session')
@@ -685,7 +682,7 @@ export class SessionMessageHandler extends BaseMessageHandler {
// Check for authentication/session expiration errors in session creation
if (
createErrorMsg.includes('Authentication required') ||
createErrorMsg.includes(AUTH_REQUIRED_CODE_PATTERN) ||
createErrorMsg.includes('(code: -32000)') ||
createErrorMsg.includes('Unauthorized') ||
createErrorMsg.includes('Invalid token') ||
createErrorMsg.includes('No active ACP session')
@@ -725,7 +722,7 @@ export class SessionMessageHandler extends BaseMessageHandler {
// Check for authentication/session expiration errors
if (
errorMsg.includes('Authentication required') ||
errorMsg.includes(AUTH_REQUIRED_CODE_PATTERN) ||
errorMsg.includes('(code: -32000)') ||
errorMsg.includes('Unauthorized') ||
errorMsg.includes('Invalid token') ||
errorMsg.includes('No active ACP session')
@@ -780,7 +777,7 @@ export class SessionMessageHandler extends BaseMessageHandler {
// Check for authentication/session expiration errors
if (
errorMsg.includes('Authentication required') ||
errorMsg.includes(AUTH_REQUIRED_CODE_PATTERN) ||
errorMsg.includes('(code: -32000)') ||
errorMsg.includes('Unauthorized') ||
errorMsg.includes('Invalid token') ||
errorMsg.includes('No active ACP session')
@@ -830,7 +827,7 @@ export class SessionMessageHandler extends BaseMessageHandler {
// Check for authentication/session expiration errors
if (
errorMsg.includes('Authentication required') ||
errorMsg.includes(AUTH_REQUIRED_CODE_PATTERN) ||
errorMsg.includes('(code: -32000)') ||
errorMsg.includes('Unauthorized') ||
errorMsg.includes('Invalid token') ||
errorMsg.includes('No active ACP session')
@@ -858,7 +855,7 @@ export class SessionMessageHandler extends BaseMessageHandler {
// Check for authentication/session expiration errors
if (
errorMsg.includes('Authentication required') ||
errorMsg.includes(AUTH_REQUIRED_CODE_PATTERN) ||
errorMsg.includes('(code: -32000)') ||
errorMsg.includes('Unauthorized') ||
errorMsg.includes('Invalid token') ||
errorMsg.includes('No active ACP session')
@@ -964,7 +961,7 @@ export class SessionMessageHandler extends BaseMessageHandler {
// Check for authentication/session expiration errors
if (
errorMsg.includes('Authentication required') ||
errorMsg.includes(AUTH_REQUIRED_CODE_PATTERN) ||
errorMsg.includes('(code: -32000)') ||
errorMsg.includes('Unauthorized') ||
errorMsg.includes('Invalid token') ||
errorMsg.includes('No active ACP session')
@@ -992,7 +989,7 @@ export class SessionMessageHandler extends BaseMessageHandler {
// Check for authentication/session expiration errors
if (
errorMsg.includes('Authentication required') ||
errorMsg.includes(AUTH_REQUIRED_CODE_PATTERN) ||
errorMsg.includes('(code: -32000)') ||
errorMsg.includes('Unauthorized') ||
errorMsg.includes('Invalid token') ||
errorMsg.includes('No active ACP session')