Skip to content

Commit 5e944a0

Browse files
sestinjContinue
andcommitted
Fix CLI context pollution between sessions
Resolves context pollution issue where CLI sessions would retain state from previous invocations, causing responses to reference conversations that should have been isolated. Changes: - Added SessionManager.reset() static method for proper cleanup - Updated initializeChatHistory() to create new session by default - Clear undo/redo stacks in ChatHistoryService on initialization - Added comprehensive session isolation tests Fixes CON-4530 Co-authored-by: Username <nate@continue.dev> Generated with [Continue](https://continue.dev) Co-Authored-By: Continue <noreply@continue.dev>
1 parent 2088660 commit 5e944a0

File tree

5 files changed

+118
-1
lines changed

5 files changed

+118
-1
lines changed

extensions/cli/package-lock.json

Lines changed: 2 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

extensions/cli/src/commands/chat.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,11 @@ export async function initializeChatHistory(
118118
}
119119
}
120120

121+
// Starting a new session - ensure clean state
122+
// This prevents context pollution from previous CLI sessions
123+
const { startNewSession } = await import("../session.js");
124+
startNewSession([]); // Creates a new session with empty history
125+
121126
return [];
122127
}
123128

extensions/cli/src/services/ChatHistoryService.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,11 @@ export class ChatHistoryService extends BaseService<ChatHistoryState> {
8181
session?: any,
8282
isRemoteMode = false,
8383
): Promise<ChatHistoryState> {
84+
// Clear undo/redo stacks when initializing to prevent pollution
85+
this.past = [];
86+
this.future = [];
87+
this._memoSnapshot = { stateRef: null, historyRef: null, snapshot: null };
88+
8489
const activeSession = session || createSession([]);
8590

8691
logger.debug("Initializing ChatHistoryService", {

extensions/cli/src/session.test.ts

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {
1616
updateSessionHistory,
1717
updateSessionTitle,
1818
} from "./session.js";
19+
import type SessionManager from "./session.js";
1920

2021
// Mock dependencies first, before any imports
2122
vi.mock("os", () => ({
@@ -401,4 +402,100 @@ describe("SessionManager", () => {
401402
expect(currentSession).not.toBe(originalSession);
402403
});
403404
});
405+
406+
describe("session isolation", () => {
407+
it("should not pollute new sessions with previous session history", () => {
408+
// Simulate first CLI session
409+
vi.mocked(uuidv4).mockReturnValue("session-1" as any);
410+
const session1 = createSession();
411+
const history1: ChatHistoryItem[] = [
412+
{
413+
message: {
414+
role: "user",
415+
content: "Tell me about dogs",
416+
},
417+
contextItems: [],
418+
},
419+
{
420+
message: {
421+
role: "assistant",
422+
content: "Dogs are loyal companions...",
423+
},
424+
contextItems: [],
425+
},
426+
];
427+
updateSessionHistory(history1);
428+
429+
// Simulate starting a new CLI session (without --resume)
430+
vi.mocked(uuidv4).mockReturnValue("session-2" as any);
431+
const session2 = startNewSession([]);
432+
433+
// New session should have clean state
434+
expect(session2.sessionId).toBe("session-2");
435+
expect(session2.sessionId).not.toBe(session1.sessionId);
436+
expect(session2.history).toEqual([]);
437+
expect(session2.history.length).toBe(0);
438+
});
439+
440+
it("should create independent sessions for concurrent operations", () => {
441+
// Create first session with some data
442+
vi.mocked(uuidv4).mockReturnValue("concurrent-1" as any);
443+
const session1 = createSession();
444+
updateSessionTitle("Session 1");
445+
updateSessionHistory([
446+
{
447+
message: {
448+
role: "user",
449+
content: "First session message",
450+
},
451+
contextItems: [],
452+
},
453+
]);
454+
455+
// Start a new session
456+
vi.mocked(uuidv4).mockReturnValue("concurrent-2" as any);
457+
const session2 = startNewSession([]);
458+
459+
// Verify session2 is clean
460+
expect(session2.title).toBe("Untitled Session");
461+
expect(session2.history).toEqual([]);
462+
expect(session2.sessionId).not.toBe(session1.sessionId);
463+
});
464+
465+
it("should properly clear session state when transitioning between sessions", () => {
466+
// First session with complex history
467+
vi.mocked(uuidv4).mockReturnValue("complex-session-1" as any);
468+
const session1 = createSession();
469+
updateSessionTitle("Complex Session");
470+
const complexHistory: ChatHistoryItem[] = [
471+
{
472+
message: {
473+
role: "user",
474+
content: "What were we discussing?",
475+
},
476+
contextItems: [],
477+
},
478+
{
479+
message: {
480+
role: "assistant",
481+
content: "We were discussing dogs earlier.",
482+
},
483+
contextItems: [],
484+
},
485+
];
486+
updateSessionHistory(complexHistory);
487+
488+
// Verify first session has data
489+
expect(getCurrentSession().history.length).toBe(2);
490+
491+
// Start fresh session
492+
vi.mocked(uuidv4).mockReturnValue("fresh-session-2" as any);
493+
const session2 = startNewSession([]);
494+
495+
// Verify clean state
496+
expect(session2.history.length).toBe(0);
497+
expect(session2.title).toBe("Untitled Session");
498+
expect(getCurrentSession().sessionId).toBe("fresh-session-2");
499+
});
500+
});
404501
});

extensions/cli/src/session.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,15 @@ class SessionManager {
122122
this.currentSession = null;
123123
}
124124

125+
/**
126+
* Reset singleton instance for testing or to ensure clean state
127+
*/
128+
static reset(): void {
129+
if (SessionManager.instance) {
130+
SessionManager.instance.currentSession = null;
131+
}
132+
}
133+
125134
hasSession(): boolean {
126135
return this.currentSession !== null;
127136
}

0 commit comments

Comments
 (0)