Skip to content

Commit cebe044

Browse files
authored
Merge pull request #1327 from QwenLM/feat/vscode-ide-companion-context-left
context left on vscode ide companion
2 parents 919560e + 9a27857 commit cebe044

File tree

17 files changed

+680
-15
lines changed

17 files changed

+680
-15
lines changed

packages/cli/src/acp-integration/acpAgent.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import {
1919
type Config,
2020
type ConversationRecord,
2121
type DeviceAuthorizationData,
22+
tokenLimit,
2223
} from '@qwen-code/qwen-code-core';
2324
import type { ApprovalModeValue } from './schema.js';
2425
import * as acp from './acp.js';
@@ -165,9 +166,30 @@ class GeminiAgent {
165166
this.setupFileSystem(config);
166167

167168
const session = await this.createAndStoreSession(config);
169+
const configuredModel = (
170+
config.getModel() ||
171+
this.config.getModel() ||
172+
''
173+
).trim();
174+
const modelId = configuredModel || 'default';
175+
const modelName = configuredModel || modelId;
168176

169177
return {
170178
sessionId: session.getId(),
179+
models: {
180+
currentModelId: modelId,
181+
availableModels: [
182+
{
183+
modelId,
184+
name: modelName,
185+
description: null,
186+
_meta: {
187+
contextLimit: tokenLimit(modelId),
188+
},
189+
},
190+
],
191+
_meta: null,
192+
},
171193
};
172194
}
173195

packages/cli/src/acp-integration/schema.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ export type ModeInfo = z.infer<typeof modeInfoSchema>;
9393
export type ModesData = z.infer<typeof modesDataSchema>;
9494

9595
export type AgentInfo = z.infer<typeof agentInfoSchema>;
96+
export type ModelInfo = z.infer<typeof modelInfoSchema>;
9697

9798
export type PromptCapabilities = z.infer<typeof promptCapabilitiesSchema>;
9899

@@ -254,8 +255,26 @@ export const authenticateUpdateSchema = z.object({
254255

255256
export type AuthenticateUpdate = z.infer<typeof authenticateUpdateSchema>;
256257

258+
export const acpMetaSchema = z.record(z.unknown()).nullable().optional();
259+
260+
export const modelIdSchema = z.string();
261+
262+
export const modelInfoSchema = z.object({
263+
_meta: acpMetaSchema,
264+
description: z.string().nullable().optional(),
265+
modelId: modelIdSchema,
266+
name: z.string(),
267+
});
268+
269+
export const sessionModelStateSchema = z.object({
270+
_meta: acpMetaSchema,
271+
availableModels: z.array(modelInfoSchema),
272+
currentModelId: modelIdSchema,
273+
});
274+
257275
export const newSessionResponseSchema = z.object({
258276
sessionId: z.string(),
277+
models: sessionModelStateSchema,
259278
});
260279

261280
export const loadSessionResponseSchema = z.null();
@@ -514,6 +533,13 @@ export const currentModeUpdateSchema = z.object({
514533

515534
export type CurrentModeUpdate = z.infer<typeof currentModeUpdateSchema>;
516535

536+
export const currentModelUpdateSchema = z.object({
537+
sessionUpdate: z.literal('current_model_update'),
538+
model: modelInfoSchema,
539+
});
540+
541+
export type CurrentModelUpdate = z.infer<typeof currentModelUpdateSchema>;
542+
517543
export const sessionUpdateSchema = z.union([
518544
z.object({
519545
content: contentBlockSchema,
@@ -555,6 +581,7 @@ export const sessionUpdateSchema = z.union([
555581
sessionUpdate: z.literal('plan'),
556582
}),
557583
currentModeUpdateSchema,
584+
currentModelUpdateSchema,
558585
availableCommandsUpdateSchema,
559586
]);
560587

packages/vscode-ide-companion/src/services/acpConnection.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,8 @@ export class AcpConnection {
146146
console.error(
147147
`[ACP qwen] Process exited with code: ${code}, signal: ${signal}`,
148148
);
149+
// Clear pending requests when process exits
150+
this.pendingRequests.clear();
149151
});
150152

151153
// Wait for process to start

packages/vscode-ide-companion/src/services/qwenAgentManager.ts

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import type {
88
AcpSessionUpdate,
99
AcpPermissionRequest,
1010
AuthenticateUpdateNotification,
11+
ModelInfo,
1112
} from '../types/acpTypes.js';
1213
import type { ApprovalModeValue } from '../types/approvalModeValueTypes.js';
1314
import { QwenSessionReader, type QwenSession } from './qwenSessionReader.js';
@@ -17,13 +18,15 @@ import type {
1718
PlanEntry,
1819
ToolCallUpdateData,
1920
QwenAgentCallbacks,
21+
UsageStatsPayload,
2022
} from '../types/chatTypes.js';
2123
import {
2224
QwenConnectionHandler,
2325
type QwenConnectionResult,
2426
} from '../services/qwenConnectionHandler.js';
2527
import { QwenSessionUpdateHandler } from './qwenSessionUpdateHandler.js';
2628
import { authMethod } from '../types/acpTypes.js';
29+
import { extractModelInfoFromNewSessionResult } from '../utils/acpModelInfo.js';
2730
import { isAuthenticationRequiredError } from '../utils/authErrors.js';
2831
import { handleAuthenticateUpdate } from '../utils/authNotificationHandler.js';
2932

@@ -195,12 +198,16 @@ export class QwenAgentManager {
195198
options?: AgentConnectOptions,
196199
): Promise<QwenConnectionResult> {
197200
this.currentWorkingDir = workingDir;
198-
return this.connectionHandler.connect(
201+
const res = await this.connectionHandler.connect(
199202
this.connection,
200203
workingDir,
201204
cliEntryPath,
202205
options,
203206
);
207+
if (res.modelInfo && this.callbacks.onModelInfo) {
208+
this.callbacks.onModelInfo(res.modelInfo);
209+
}
210+
return res;
204211
}
205212

206213
/**
@@ -1091,9 +1098,10 @@ export class QwenAgentManager {
10911098

10921099
this.sessionCreateInFlight = (async () => {
10931100
try {
1101+
let newSessionResult: unknown;
10941102
// Try to create a new ACP session. If Qwen asks for auth, let it handle authentication.
10951103
try {
1096-
await this.connection.newSession(workingDir);
1104+
newSessionResult = await this.connection.newSession(workingDir);
10971105
} catch (err) {
10981106
const requiresAuth = isAuthenticationRequiredError(err);
10991107

@@ -1115,7 +1123,7 @@ export class QwenAgentManager {
11151123
);
11161124
// Add a slight delay to ensure auth state is settled
11171125
await new Promise((resolve) => setTimeout(resolve, 300));
1118-
await this.connection.newSession(workingDir);
1126+
newSessionResult = await this.connection.newSession(workingDir);
11191127
} catch (reauthErr) {
11201128
console.error(
11211129
'[QwenAgentManager] Re-authentication failed:',
@@ -1127,6 +1135,13 @@ export class QwenAgentManager {
11271135
throw err;
11281136
}
11291137
}
1138+
1139+
const modelInfo =
1140+
extractModelInfoFromNewSessionResult(newSessionResult);
1141+
if (modelInfo && this.callbacks.onModelInfo) {
1142+
this.callbacks.onModelInfo(modelInfo);
1143+
}
1144+
11301145
const newSessionId = this.connection.currentSessionId;
11311146
console.log(
11321147
'[QwenAgentManager] New session created with ID:',
@@ -1257,6 +1272,22 @@ export class QwenAgentManager {
12571272
this.sessionUpdateHandler.updateCallbacks(this.callbacks);
12581273
}
12591274

1275+
/**
1276+
* Register callback for usage metadata updates
1277+
*/
1278+
onUsageUpdate(callback: (stats: UsageStatsPayload) => void): void {
1279+
this.callbacks.onUsageUpdate = callback;
1280+
this.sessionUpdateHandler.updateCallbacks(this.callbacks);
1281+
}
1282+
1283+
/**
1284+
* Register callback for model info updates
1285+
*/
1286+
onModelInfo(callback: (info: ModelInfo) => void): void {
1287+
this.callbacks.onModelInfo = callback;
1288+
this.sessionUpdateHandler.updateCallbacks(this.callbacks);
1289+
}
1290+
12601291
/**
12611292
* Disconnect
12621293
*/

packages/vscode-ide-companion/src/services/qwenConnectionHandler.ts

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,13 @@
1313
import type { AcpConnection } from './acpConnection.js';
1414
import { isAuthenticationRequiredError } from '../utils/authErrors.js';
1515
import { authMethod } from '../types/acpTypes.js';
16+
import { extractModelInfoFromNewSessionResult } from '../utils/acpModelInfo.js';
17+
import type { ModelInfo } from '../types/acpTypes.js';
1618

1719
export interface QwenConnectionResult {
1820
sessionCreated: boolean;
1921
requiresAuth: boolean;
22+
modelInfo?: ModelInfo;
2023
}
2124

2225
/**
@@ -44,6 +47,7 @@ export class QwenConnectionHandler {
4447
const autoAuthenticate = options?.autoAuthenticate ?? true;
4548
let sessionCreated = false;
4649
let requiresAuth = false;
50+
let modelInfo: ModelInfo | undefined;
4751

4852
// Build extra CLI arguments (only essential parameters)
4953
const extraArgs: string[] = [];
@@ -66,13 +70,15 @@ export class QwenConnectionHandler {
6670
console.log(
6771
'[QwenAgentManager] Creating new session (letting CLI handle authentication)...',
6872
);
69-
await this.newSessionWithRetry(
73+
const newSessionResult = await this.newSessionWithRetry(
7074
connection,
7175
workingDir,
7276
3,
7377
authMethod,
7478
autoAuthenticate,
7579
);
80+
modelInfo =
81+
extractModelInfoFromNewSessionResult(newSessionResult) || undefined;
7682
console.log('[QwenAgentManager] New session created successfully');
7783
sessionCreated = true;
7884
} catch (sessionError) {
@@ -99,7 +105,7 @@ export class QwenConnectionHandler {
99105
console.log(`\n========================================`);
100106
console.log(`[QwenAgentManager] ✅ CONNECT() COMPLETED SUCCESSFULLY`);
101107
console.log(`========================================\n`);
102-
return { sessionCreated, requiresAuth };
108+
return { sessionCreated, requiresAuth, modelInfo };
103109
}
104110

105111
/**
@@ -115,15 +121,15 @@ export class QwenConnectionHandler {
115121
maxRetries: number,
116122
authMethod: string,
117123
autoAuthenticate: boolean,
118-
): Promise<void> {
124+
): Promise<unknown> {
119125
for (let attempt = 1; attempt <= maxRetries; attempt++) {
120126
try {
121127
console.log(
122128
`[QwenAgentManager] Creating session (attempt ${attempt}/${maxRetries})...`,
123129
);
124-
await connection.newSession(workingDir);
130+
const res = await connection.newSession(workingDir);
125131
console.log('[QwenAgentManager] Session created successfully');
126-
return;
132+
return res;
127133
} catch (error) {
128134
const errorMessage =
129135
error instanceof Error ? error.message : String(error);
@@ -155,11 +161,11 @@ export class QwenConnectionHandler {
155161
'[QwenAgentManager] newSessionWithRetry Authentication successful',
156162
);
157163
// Retry immediately after successful auth
158-
await connection.newSession(workingDir);
164+
const res = await connection.newSession(workingDir);
159165
console.log(
160166
'[QwenAgentManager] Session created successfully after auth',
161167
);
162-
return;
168+
return res;
163169
} catch (authErr) {
164170
console.error(
165171
'[QwenAgentManager] Re-authentication failed:',
@@ -180,5 +186,7 @@ export class QwenConnectionHandler {
180186
await new Promise((resolve) => setTimeout(resolve, delay));
181187
}
182188
}
189+
190+
throw new Error('Session creation failed unexpectedly');
183191
}
184192
}

packages/vscode-ide-companion/src/services/qwenSessionUpdateHandler.ts

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,12 @@
1010
* Handles session updates from ACP and dispatches them to appropriate callbacks
1111
*/
1212

13-
import type { AcpSessionUpdate } from '../types/acpTypes.js';
13+
import type { AcpSessionUpdate, SessionUpdateMeta } from '../types/acpTypes.js';
1414
import type { ApprovalModeValue } from '../types/approvalModeValueTypes.js';
15-
import type { QwenAgentCallbacks } from '../types/chatTypes.js';
15+
import type {
16+
QwenAgentCallbacks,
17+
UsageStatsPayload,
18+
} from '../types/chatTypes.js';
1619

1720
/**
1821
* Qwen Session Update Handler class
@@ -57,6 +60,7 @@ export class QwenSessionUpdateHandler {
5760
if (update.content?.text && this.callbacks.onStreamChunk) {
5861
this.callbacks.onStreamChunk(update.content.text);
5962
}
63+
this.emitUsageMeta(update._meta);
6064
break;
6165

6266
case 'agent_thought_chunk':
@@ -71,6 +75,7 @@ export class QwenSessionUpdateHandler {
7175
this.callbacks.onStreamChunk(update.content.text);
7276
}
7377
}
78+
this.emitUsageMeta(update._meta);
7479
break;
7580

7681
case 'tool_call': {
@@ -160,4 +165,17 @@ export class QwenSessionUpdateHandler {
160165
break;
161166
}
162167
}
168+
169+
private emitUsageMeta(meta?: SessionUpdateMeta): void {
170+
if (!meta || !this.callbacks.onUsageUpdate) {
171+
return;
172+
}
173+
174+
const payload: UsageStatsPayload = {
175+
usage: meta.usage || undefined,
176+
durationMs: meta.durationMs ?? undefined,
177+
};
178+
179+
this.callbacks.onUsageUpdate(payload);
180+
}
163181
}

packages/vscode-ide-companion/src/types/acpTypes.ts

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,35 @@ export interface ContentBlock {
4848
uri?: string;
4949
}
5050

51+
export interface UsageMetadata {
52+
promptTokens?: number | null;
53+
completionTokens?: number | null;
54+
thoughtsTokens?: number | null;
55+
totalTokens?: number | null;
56+
cachedTokens?: number | null;
57+
}
58+
59+
export interface SessionUpdateMeta {
60+
usage?: UsageMetadata | null;
61+
durationMs?: number | null;
62+
}
63+
64+
export type AcpMeta = Record<string, unknown>;
65+
export type ModelId = string;
66+
67+
export interface ModelInfo {
68+
_meta?: AcpMeta | null;
69+
description?: string | null;
70+
modelId: ModelId;
71+
name: string;
72+
}
73+
74+
export interface SessionModelState {
75+
_meta?: AcpMeta | null;
76+
availableModels: ModelInfo[];
77+
currentModelId: ModelId;
78+
}
79+
5180
export interface UserMessageChunkUpdate extends BaseSessionUpdate {
5281
update: {
5382
sessionUpdate: 'user_message_chunk';
@@ -59,13 +88,15 @@ export interface AgentMessageChunkUpdate extends BaseSessionUpdate {
5988
update: {
6089
sessionUpdate: 'agent_message_chunk';
6190
content: ContentBlock;
91+
_meta?: SessionUpdateMeta;
6292
};
6393
}
6494

6595
export interface AgentThoughtChunkUpdate extends BaseSessionUpdate {
6696
update: {
6797
sessionUpdate: 'agent_thought_chunk';
6898
content: ContentBlock;
99+
_meta?: SessionUpdateMeta;
69100
};
70101
}
71102

0 commit comments

Comments
 (0)