diff --git a/src/agents/transcript/session-manager.test.ts b/src/agents/transcript/session-manager.test.ts index 685a873bac1..efd78bbd83b 100644 --- a/src/agents/transcript/session-manager.test.ts +++ b/src/agents/transcript/session-manager.test.ts @@ -153,6 +153,40 @@ describe("TranscriptSessionManager", () => { }); }); + it("preserves non-main agent scope for virtual sqlite branches and forks", async () => { + await makeTempSessionFile(); + const sessionFile = createSqliteSessionTranscriptLocator({ + agentId: "qa", + sessionId: "qa-source-session", + }); + const sessionManager = openTranscriptSessionManager({ + sessionFile, + sessionId: "qa-source-session", + cwd: "/tmp/qa-workspace", + }); + const userId = sessionManager.appendMessage({ + role: "user", + content: "qa source", + timestamp: 4, + }); + + const branchFile = sessionManager.createBranchedSession(userId); + expect(branchFile).toMatch(/^sqlite-transcript:\/\/qa\//); + expect( + resolveSqliteSessionTranscriptScopeForPath({ transcriptPath: branchFile! }), + ).toMatchObject({ + agentId: "qa", + }); + + const forked = SessionManager.forkFrom(sessionFile, "/tmp/qa-fork"); + expect(forked.getSessionFile()).toMatch(/^sqlite-transcript:\/\/qa\//); + expect( + resolveSqliteSessionTranscriptScopeForPath({ transcriptPath: forked.getSessionFile()! }), + ).toMatchObject({ + agentId: "qa", + }); + }); + it("persists initial user messages synchronously before the first assistant message", async () => { const sessionFile = await makeTempSessionFile(); const sessionManager = openTranscriptSessionManager({ diff --git a/src/agents/transcript/session-manager.ts b/src/agents/transcript/session-manager.ts index ec507aaa2ef..36a88ff10fa 100644 --- a/src/agents/transcript/session-manager.ts +++ b/src/agents/transcript/session-manager.ts @@ -3,6 +3,7 @@ import path from "node:path"; import { createSqliteSessionTranscriptLocator, isSqliteSessionTranscriptLocator, + parseSqliteSessionTranscriptLocator, } from "../../config/sessions/paths.js"; import { appendSqliteSessionTranscriptEvent, @@ -70,19 +71,26 @@ function resolveSessionDirForIdentifier(sessionFile: string, sessionDir?: string return isSqliteSessionTranscriptLocator(sessionFile) ? "" : path.dirname(sessionFile); } -function createSessionFileIdentifier(header: SessionHeader, sessionDir?: string): string { +function createSessionFileIdentifier( + header: SessionHeader, + sessionDir?: string, + agentId = DEFAULT_AGENT_ID, +): string { const dir = sessionDir?.trim(); if (dir) { return path.join(path.resolve(dir), createSessionFileName(header)); } return createSqliteSessionTranscriptLocator({ - agentId: DEFAULT_AGENT_ID, + agentId, sessionId: header.id, }); } function resolveAgentIdFromSessionPath(sessionFile: string): string { - void sessionFile; + const locator = parseSqliteSessionTranscriptLocator(sessionFile); + if (locator) { + return locator.agentId; + } return DEFAULT_AGENT_ID; } @@ -443,10 +451,10 @@ export class TranscriptSessionManager implements SessionManager { cwd: targetCwd, parentSession: sourceFile, }); - const sessionFile = createSessionFileIdentifier(header, sessionDir); + const sessionFile = createSessionFileIdentifier(header, sessionDir, sourceScope.agentId); const state = new TranscriptState({ header, entries: sourceState.getEntries() }); const sqliteScope = { - agentId: resolveAgentIdFromSessionPath(sessionFile), + agentId: sourceScope.agentId, sessionId: header.id, transcriptPath: normalizeSessionFileIdentifier(sessionFile), }; @@ -507,7 +515,7 @@ export class TranscriptSessionManager implements SessionManager { this.sessionFile ?? (this.sessionDir ? path.join(this.sessionDir, createSessionFileName(header)) - : createSessionFileIdentifier(header)); + : createSessionFileIdentifier(header, undefined, this.sqliteScope?.agentId)); this.sqliteScope = { agentId: resolveAgentIdFromSessionPath(this.sessionFile), sessionId: header.id, @@ -672,7 +680,7 @@ export class TranscriptSessionManager implements SessionManager { const sessionFile = this.sessionDir ? path.join(this.sessionDir, `${timestamp}_${header.id}.jsonl`) : createSqliteSessionTranscriptLocator({ - agentId: DEFAULT_AGENT_ID, + agentId: this.sqliteScope?.agentId ?? DEFAULT_AGENT_ID, sessionId: header.id, }); if (!this.persist) {