mirror of
https://github.com/QwenLM/qwen-code.git
synced 2025-12-19 09:33:53 +00:00
feat(vscode-ide-companion): implement session rehydration for loading past conversations
- Add rehydratingSessionId flag to track session loading state - Route message chunks as discrete messages during rehydration instead of streaming - Update session handlers to properly manage conversation switching - Improve session manager documentation
This commit is contained in:
@@ -37,6 +37,10 @@ export class QwenAgentManager {
|
||||
private connectionHandler: QwenConnectionHandler;
|
||||
private sessionUpdateHandler: QwenSessionUpdateHandler;
|
||||
private currentWorkingDir: string = process.cwd();
|
||||
// When loading a past session via ACP, the CLI replays history through
|
||||
// session/update notifications. We set this flag to route message chunks
|
||||
// (user/assistant) as discrete chat messages instead of live streaming.
|
||||
private rehydratingSessionId: string | null = null;
|
||||
// Cache the last used AuthStateManager so internal calls (e.g. fallback paths)
|
||||
// can reuse it and avoid forcing a fresh authentication unnecessarily.
|
||||
private defaultAuthStateManager?: AuthStateManager;
|
||||
@@ -53,6 +57,55 @@ export class QwenAgentManager {
|
||||
|
||||
// Set ACP connection callbacks
|
||||
this.connection.onSessionUpdate = (data: AcpSessionUpdate) => {
|
||||
// If we are rehydrating a loaded session, map message chunks into
|
||||
// full messages for the UI, instead of streaming behavior.
|
||||
try {
|
||||
const targetId = this.rehydratingSessionId;
|
||||
if (
|
||||
targetId &&
|
||||
typeof data === 'object' &&
|
||||
data &&
|
||||
'update' in data &&
|
||||
(data as { sessionId?: string }).sessionId === targetId
|
||||
) {
|
||||
const update = (
|
||||
data as unknown as {
|
||||
update: { sessionUpdate: string; content?: { text?: string } };
|
||||
}
|
||||
).update;
|
||||
const text = update?.content?.text || '';
|
||||
if (update?.sessionUpdate === 'user_message_chunk' && text) {
|
||||
console.log(
|
||||
'[QwenAgentManager] Rehydration: routing user message chunk',
|
||||
);
|
||||
this.callbacks.onMessage?.({
|
||||
role: 'user',
|
||||
content: text,
|
||||
timestamp: Date.now(),
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (update?.sessionUpdate === 'agent_message_chunk' && text) {
|
||||
console.log(
|
||||
'[QwenAgentManager] Rehydration: routing agent message chunk',
|
||||
);
|
||||
this.callbacks.onMessage?.({
|
||||
role: 'assistant',
|
||||
content: text,
|
||||
timestamp: Date.now(),
|
||||
});
|
||||
return;
|
||||
}
|
||||
// For other types during rehydration, fall through to normal handler
|
||||
console.log(
|
||||
'[QwenAgentManager] Rehydration: non-text update, forwarding to handler',
|
||||
);
|
||||
}
|
||||
} catch (err) {
|
||||
console.warn('[QwenAgentManager] Rehydration routing failed:', err);
|
||||
}
|
||||
|
||||
// Default handling path
|
||||
this.sessionUpdateHandler.handleSessionUpdate(data);
|
||||
};
|
||||
|
||||
@@ -986,6 +1039,12 @@ export class QwenAgentManager {
|
||||
}
|
||||
|
||||
try {
|
||||
// Route upcoming session/update messages as discrete messages for replay
|
||||
this.rehydratingSessionId = sessionId;
|
||||
console.log(
|
||||
'[QwenAgentManager] Rehydration start for session:',
|
||||
sessionId,
|
||||
);
|
||||
console.log(
|
||||
'[QwenAgentManager] Attempting session/load via ACP for session:',
|
||||
sessionId,
|
||||
@@ -1032,6 +1091,10 @@ export class QwenAgentManager {
|
||||
}
|
||||
|
||||
throw error;
|
||||
} finally {
|
||||
// End rehydration routing regardless of outcome
|
||||
console.log('[QwenAgentManager] Rehydration end for session:', sessionId);
|
||||
this.rehydratingSessionId = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,10 @@ import type { QwenSession, QwenMessage } from './qwenSessionReader.js';
|
||||
*
|
||||
* This service provides direct filesystem access to save and load sessions
|
||||
* without relying on the CLI's ACP session/save method.
|
||||
*
|
||||
* Note: This is primarily used as a fallback mechanism when ACP methods are
|
||||
* unavailable or fail. In normal operation, ACP session/list and session/load
|
||||
* should be preferred for consistency with the CLI.
|
||||
*/
|
||||
export class QwenSessionManager {
|
||||
private qwenDir: string;
|
||||
|
||||
@@ -598,24 +598,22 @@ export class SessionMessageHandler extends BaseMessageHandler {
|
||||
|
||||
// Try to load session via ACP (now we should be connected)
|
||||
try {
|
||||
// Set current id and clear UI first so replayed updates append afterwards
|
||||
this.currentConversationId = sessionId;
|
||||
this.sendToWebView({
|
||||
type: 'qwenSessionSwitched',
|
||||
data: { sessionId, messages: [], session: sessionDetails },
|
||||
});
|
||||
|
||||
const loadResponse = await this.agentManager.loadSessionViaAcp(
|
||||
sessionId,
|
||||
(sessionDetails?.cwd as string | undefined) || undefined,
|
||||
);
|
||||
console.log(
|
||||
'[SessionMessageHandler] session/load succeeded:',
|
||||
'[SessionMessageHandler] session/load succeeded (per ACP spec result is null; actual history comes via session/update):',
|
||||
loadResponse,
|
||||
);
|
||||
|
||||
this.currentConversationId = sessionId;
|
||||
|
||||
const messages = await this.agentManager.getSessionMessages(sessionId);
|
||||
|
||||
this.sendToWebView({
|
||||
type: 'qwenSessionSwitched',
|
||||
data: { sessionId, messages, session: sessionDetails },
|
||||
});
|
||||
|
||||
// Reset title flag when switching sessions
|
||||
this.isTitleSet = false;
|
||||
|
||||
@@ -1029,17 +1027,15 @@ export class SessionMessageHandler extends BaseMessageHandler {
|
||||
|
||||
// Try ACP load first
|
||||
try {
|
||||
await this.agentManager.loadSessionViaAcp(sessionId);
|
||||
|
||||
// Pre-clear UI so replayed updates append afterwards
|
||||
this.currentConversationId = sessionId;
|
||||
|
||||
const messages = await this.agentManager.getSessionMessages(sessionId);
|
||||
|
||||
this.sendToWebView({
|
||||
type: 'qwenSessionSwitched',
|
||||
data: { sessionId, messages },
|
||||
data: { sessionId, messages: [] },
|
||||
});
|
||||
|
||||
await this.agentManager.loadSessionViaAcp(sessionId);
|
||||
|
||||
// Reset title flag when resuming sessions
|
||||
this.isTitleSet = false;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user