diff --git a/packages/coding-agent/src/core/agent-session-runtime.ts b/packages/coding-agent/src/core/agent-session-runtime.ts index 7f29275a..93b7ee2d 100644 --- a/packages/coding-agent/src/core/agent-session-runtime.ts +++ b/packages/coding-agent/src/core/agent-session-runtime.ts @@ -306,6 +306,11 @@ export class AgentSessionRuntime { return { cancelled: false, selectedText }; } + if (!existsSync(currentSessionFile)) { + throw new Error( + "This session has not been saved yet. Wait for the first assistant response before cloning or forking it.", + ); + } const sessionManager = SessionManager.open(currentSessionFile, sessionDir); const forkedSessionPath = sessionManager.createBranchedSession(targetLeafId); if (!forkedSessionPath) { diff --git a/packages/coding-agent/test/suite/agent-session-runtime.test.ts b/packages/coding-agent/test/suite/agent-session-runtime.test.ts index 9f6c1388..79855e9c 100644 --- a/packages/coding-agent/test/suite/agent-session-runtime.test.ts +++ b/packages/coding-agent/test/suite/agent-session-runtime.test.ts @@ -290,6 +290,19 @@ describe("AgentSessionRuntime characterization", () => { expect(events).toEqual([{ type: "session_before_fork", entryId: "missing-entry", position: "at" }]); }); + it("reports why an unflushed session cannot be forked", async () => { + const { runtime } = await createRuntimeForTest(() => {}); + const sessionFile = runtime.session.sessionFile; + const leafId = runtime.session.sessionManager.getLeafId(); + expect(sessionFile).toBeDefined(); + expect(existsSync(sessionFile!)).toBe(false); + expect(leafId).toBeTruthy(); + + await expect(runtime.fork(leafId!, { position: "at" })).rejects.toThrow( + "This session has not been saved yet. Wait for the first assistant response before cloning or forking it.", + ); + }); + it("duplicates the current active branch when forking at the current position", async () => { const { runtime } = await createRuntimeForTest(() => {}); await runtime.session.prompt("hello");