mirror of
https://github.com/QwenLM/qwen-code.git
synced 2025-12-19 09:33:53 +00:00
Merge branch 'main' into feat/gemini-3-integration
This commit is contained in:
@@ -35,7 +35,7 @@ function npmBin() {
|
|||||||
function run(cmd, args, opts = {}) {
|
function run(cmd, args, opts = {}) {
|
||||||
const res = spawnSync(cmd, args, {
|
const res = spawnSync(cmd, args, {
|
||||||
stdio: 'inherit',
|
stdio: 'inherit',
|
||||||
shell: process.platform === 'win32' ? true : false,
|
shell: process.platform === 'win32',
|
||||||
...opts,
|
...opts,
|
||||||
});
|
});
|
||||||
if (res.error) {
|
if (res.error) {
|
||||||
|
|||||||
@@ -54,27 +54,31 @@ export class AcpSessionManager {
|
|||||||
};
|
};
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
// different timeout durations based on methods
|
// No timeout for session_prompt as LLM tasks can take 5-10 minutes or longer
|
||||||
let timeoutDuration = 60000; // default 60 seconds
|
// The request should always terminate with a stop_reason
|
||||||
if (
|
let timeoutId: NodeJS.Timeout | undefined;
|
||||||
method === AGENT_METHODS.session_prompt ||
|
let timeoutDuration: number | undefined;
|
||||||
method === AGENT_METHODS.initialize
|
|
||||||
) {
|
|
||||||
timeoutDuration = 120000; // 2min for session_prompt and initialize
|
|
||||||
}
|
|
||||||
|
|
||||||
const timeoutId = setTimeout(() => {
|
if (method !== AGENT_METHODS.session_prompt) {
|
||||||
|
// Set timeout for other methods
|
||||||
|
timeoutDuration = method === AGENT_METHODS.initialize ? 120000 : 60000;
|
||||||
|
timeoutId = setTimeout(() => {
|
||||||
pendingRequests.delete(id);
|
pendingRequests.delete(id);
|
||||||
reject(new Error(`Request ${method} timed out`));
|
reject(new Error(`Request ${method} timed out`));
|
||||||
}, timeoutDuration);
|
}, timeoutDuration);
|
||||||
|
}
|
||||||
|
|
||||||
const pendingRequest: PendingRequest<T> = {
|
const pendingRequest: PendingRequest<T> = {
|
||||||
resolve: (value: T) => {
|
resolve: (value: T) => {
|
||||||
|
if (timeoutId) {
|
||||||
clearTimeout(timeoutId);
|
clearTimeout(timeoutId);
|
||||||
|
}
|
||||||
resolve(value);
|
resolve(value);
|
||||||
},
|
},
|
||||||
reject: (error: Error) => {
|
reject: (error: Error) => {
|
||||||
|
if (timeoutId) {
|
||||||
clearTimeout(timeoutId);
|
clearTimeout(timeoutId);
|
||||||
|
}
|
||||||
reject(error);
|
reject(error);
|
||||||
},
|
},
|
||||||
timeoutId,
|
timeoutId,
|
||||||
|
|||||||
@@ -144,10 +144,7 @@ export const InputForm: React.FC<InputFormProps> = ({
|
|||||||
: '';
|
: '';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div className="p-1 px-4 pb-4 absolute bottom-0 left-0 right-0 bg-gradient-to-b from-transparent to-[var(--app-primary-background)]">
|
||||||
className="p-1 px-4 pb-4 absolute bottom-0 left-0 right-0"
|
|
||||||
style={{ backgroundColor: 'var(--app-primary-background)' }}
|
|
||||||
>
|
|
||||||
<div className="block">
|
<div className="block">
|
||||||
<form className="composer-form" onSubmit={onSubmit}>
|
<form className="composer-form" onSubmit={onSubmit}>
|
||||||
{/* Inner background layer */}
|
{/* Inner background layer */}
|
||||||
|
|||||||
@@ -152,6 +152,24 @@ export class SessionMessageHandler extends BaseMessageHandler {
|
|||||||
this.currentStreamContent = '';
|
this.currentStreamContent = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notify the webview that streaming has finished.
|
||||||
|
*/
|
||||||
|
private sendStreamEnd(reason?: string): void {
|
||||||
|
const data: { timestamp: number; reason?: string } = {
|
||||||
|
timestamp: Date.now(),
|
||||||
|
};
|
||||||
|
|
||||||
|
if (reason) {
|
||||||
|
data.reason = reason;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.sendToWebView({
|
||||||
|
type: 'streamEnd',
|
||||||
|
data,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prompt user to login and invoke the registered login handler/command.
|
* Prompt user to login and invoke the registered login handler/command.
|
||||||
* Returns true if a login was initiated.
|
* Returns true if a login was initiated.
|
||||||
@@ -373,10 +391,7 @@ export class SessionMessageHandler extends BaseMessageHandler {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.sendToWebView({
|
this.sendStreamEnd();
|
||||||
type: 'streamEnd',
|
|
||||||
data: { timestamp: Date.now() },
|
|
||||||
});
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('[SessionMessageHandler] Error sending message:', error);
|
console.error('[SessionMessageHandler] Error sending message:', error);
|
||||||
|
|
||||||
@@ -398,10 +413,7 @@ export class SessionMessageHandler extends BaseMessageHandler {
|
|||||||
if (isAbortLike) {
|
if (isAbortLike) {
|
||||||
// Do not show VS Code error popup for intentional cancellations.
|
// Do not show VS Code error popup for intentional cancellations.
|
||||||
// Ensure the webview knows the stream ended due to user action.
|
// Ensure the webview knows the stream ended due to user action.
|
||||||
this.sendToWebView({
|
this.sendStreamEnd('user_cancelled');
|
||||||
type: 'streamEnd',
|
|
||||||
data: { timestamp: Date.now(), reason: 'user_cancelled' },
|
|
||||||
});
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Check for session not found error and handle it appropriately
|
// Check for session not found error and handle it appropriately
|
||||||
@@ -423,12 +435,39 @@ export class SessionMessageHandler extends BaseMessageHandler {
|
|||||||
type: 'sessionExpired',
|
type: 'sessionExpired',
|
||||||
data: { message: 'Session expired. Please login again.' },
|
data: { message: 'Session expired. Please login again.' },
|
||||||
});
|
});
|
||||||
|
this.sendStreamEnd('session_expired');
|
||||||
} else {
|
} else {
|
||||||
|
const isTimeoutError =
|
||||||
|
lower.includes('timeout') || lower.includes('timed out');
|
||||||
|
if (isTimeoutError) {
|
||||||
|
// Note: session_prompt no longer has a timeout, so this should rarely occur
|
||||||
|
// This path may still be hit for other methods (initialize, etc.) or network-level timeouts
|
||||||
|
console.warn(
|
||||||
|
'[SessionMessageHandler] Request timed out; suppressing popup',
|
||||||
|
);
|
||||||
|
|
||||||
|
const timeoutMessage: ChatMessage = {
|
||||||
|
role: 'assistant',
|
||||||
|
content:
|
||||||
|
'Request timed out. This may be due to a network issue. Please try again.',
|
||||||
|
timestamp: Date.now(),
|
||||||
|
};
|
||||||
|
|
||||||
|
// Send a timeout message to the WebView
|
||||||
|
this.sendToWebView({
|
||||||
|
type: 'message',
|
||||||
|
data: timeoutMessage,
|
||||||
|
});
|
||||||
|
this.sendStreamEnd('timeout');
|
||||||
|
} else {
|
||||||
|
// Handling of Non-Timeout Errors
|
||||||
vscode.window.showErrorMessage(`Error sending message: ${error}`);
|
vscode.window.showErrorMessage(`Error sending message: ${error}`);
|
||||||
this.sendToWebView({
|
this.sendToWebView({
|
||||||
type: 'error',
|
type: 'error',
|
||||||
data: { message: errorMsg },
|
data: { message: errorMsg },
|
||||||
});
|
});
|
||||||
|
this.sendStreamEnd('error');
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,14 @@ import type { ToolCallUpdate } from '../../types/chatTypes.js';
|
|||||||
import type { ApprovalModeValue } from '../../types/approvalModeValueTypes.js';
|
import type { ApprovalModeValue } from '../../types/approvalModeValueTypes.js';
|
||||||
import type { PlanEntry } from '../../types/chatTypes.js';
|
import type { PlanEntry } from '../../types/chatTypes.js';
|
||||||
|
|
||||||
|
const FORCE_CLEAR_STREAM_END_REASONS = new Set([
|
||||||
|
'user_cancelled',
|
||||||
|
'cancelled',
|
||||||
|
'timeout',
|
||||||
|
'error',
|
||||||
|
'session_expired',
|
||||||
|
]);
|
||||||
|
|
||||||
interface UseWebViewMessagesProps {
|
interface UseWebViewMessagesProps {
|
||||||
// Session management
|
// Session management
|
||||||
sessionManagement: {
|
sessionManagement: {
|
||||||
@@ -364,12 +372,12 @@ export const useWebViewMessages = ({
|
|||||||
).toLowerCase();
|
).toLowerCase();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handle different types of stream end reasons:
|
* Handle different types of stream end reasons that require a full reset:
|
||||||
* - 'user_cancelled': User explicitly cancelled operation
|
* - 'user_cancelled' / 'cancelled': user explicitly cancelled
|
||||||
* - 'cancelled': General cancellation
|
* - 'timeout' / 'error' / 'session_expired': request failed unexpectedly
|
||||||
* For these cases, immediately clear all active states
|
* For these cases, immediately clear all active states.
|
||||||
*/
|
*/
|
||||||
if (reason === 'user_cancelled' || reason === 'cancelled') {
|
if (FORCE_CLEAR_STREAM_END_REASONS.has(reason)) {
|
||||||
// Clear active execution tool call tracking, reset state
|
// Clear active execution tool call tracking, reset state
|
||||||
activeExecToolCallsRef.current.clear();
|
activeExecToolCallsRef.current.clear();
|
||||||
// Clear waiting response state to ensure UI returns to normal
|
// Clear waiting response state to ensure UI returns to normal
|
||||||
@@ -393,6 +401,9 @@ export const useWebViewMessages = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
case 'error':
|
case 'error':
|
||||||
|
handlers.messageHandling.endStreaming();
|
||||||
|
handlers.messageHandling.clearThinking();
|
||||||
|
activeExecToolCallsRef.current.clear();
|
||||||
handlers.messageHandling.clearWaitingForResponse();
|
handlers.messageHandling.clearWaitingForResponse();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|||||||
@@ -43,7 +43,7 @@
|
|||||||
|
|
||||||
/* Composer: form wrapper */
|
/* Composer: form wrapper */
|
||||||
.composer-form {
|
.composer-form {
|
||||||
@apply relative flex flex-col max-w-[680px] mx-auto rounded-large border shadow-sm transition-colors duration-200;
|
@apply relative flex flex-col max-w-[680px] mx-auto rounded-large border shadow-sm transition-colors duration-200 z-[1];
|
||||||
background: var(--app-input-secondary-background);
|
background: var(--app-input-secondary-background);
|
||||||
border-color: var(--app-input-border);
|
border-color: var(--app-input-border);
|
||||||
color: var(--app-input-foreground);
|
color: var(--app-input-foreground);
|
||||||
|
|||||||
Reference in New Issue
Block a user