From 54e79162b4d600437fef847ca76410ab79ab27d1 Mon Sep 17 00:00:00 2001 From: roboomp Date: Tue, 16 Jun 2026 20:50:51 +0000 Subject: [PATCH 1/2] fix(cli): skipped empty session persistence Prevented shutdown flushes from materializing sessions that never produced assistant output, and kept close from marking a non-existent session file current. Added regression coverage for opening omp and exiting before any prompt or assistant turn reaches history. Fixes #2800 --- packages/coding-agent/CHANGELOG.md | 4 ++++ .../src/session/session-manager.ts | 8 +++++--- .../session-manager-immediate-persist.test.ts | 20 +++++++++++++++++++ 3 files changed, 29 insertions(+), 3 deletions(-) diff --git a/packages/coding-agent/CHANGELOG.md b/packages/coding-agent/CHANGELOG.md index 80c6e5cf8..e566fe751 100644 --- a/packages/coding-agent/CHANGELOG.md +++ b/packages/coding-agent/CHANGELOG.md @@ -2,6 +2,10 @@ ## [Unreleased] +### Fixed + +- Fixed empty CLI sessions being retained after opening `omp` and exiting without a prompt ([#2800](https://github.com/can1357/oh-my-pi/issues/2800)). + ## [16.0.2] - 2026-06-16 ### Added diff --git a/packages/coding-agent/src/session/session-manager.ts b/packages/coding-agent/src/session/session-manager.ts index 3b13ba255..621ae4163 100644 --- a/packages/coding-agent/src/session/session-manager.ts +++ b/packages/coding-agent/src/session/session-manager.ts @@ -462,7 +462,7 @@ export class SessionManager { * bytes in the kernel page cache, so the file is software-crash durable. */ #rewriteSynchronously(): void { - if (!this.#persist || !this.#sessionFile) return; + if (!this.#persist || !this.#sessionFile || !this.#shouldHaveSessionFile()) return; try { const body = this.#fileBody(); @@ -483,7 +483,7 @@ export class SessionManager { * closed — so entries appended before the task runs are included. */ async #rewriteAtomically(): Promise { - if (!this.#persist || !this.#sessionFile) return; + if (!this.#persist || !this.#sessionFile || !this.#shouldHaveSessionFile()) return; const epoch = this.#diskEpoch; await this.#scheduleDiskWork( @@ -931,8 +931,10 @@ export class SessionManager { async close(): Promise { if (!this.#persist) return; await this.#scheduleDiskWork(async () => { + const hadWriter = this.#writer !== undefined; await this.#closeWriterHandle(); - this.#fileIsCurrent = true; + if (hadWriter || (this.#sessionFile && this.#storage.existsSync(this.#sessionFile))) + this.#fileIsCurrent = true; }); if (this.#diskFailure) throw this.#diskFailure; } diff --git a/packages/coding-agent/test/session-manager-immediate-persist.test.ts b/packages/coding-agent/test/session-manager-immediate-persist.test.ts index 81849911d..807ca9fa1 100644 --- a/packages/coding-agent/test/session-manager-immediate-persist.test.ts +++ b/packages/coding-agent/test/session-manager-immediate-persist.test.ts @@ -87,4 +87,24 @@ describe("SessionManager immediate JSONL persistence", () => { expect(messageRole(entries[3] ?? {})).toBe("user"); expect(messageContent(entries[3] ?? {})).toBe("written immediately"); }); + + it("keeps pre-assistant sessions out of history during shutdown", async () => { + const cwd = makeTempDir("@pi-empty-session-cwd-"); + const sessionDir = path.join(cwd, "sessions"); + const manager = SessionManager.create(cwd, sessionDir); + const sessionFile = manager.getSessionFile(); + if (!sessionFile) throw new Error("Expected a persisted session file path"); + + manager.flushSync(); + await manager.close(); + + expect(fs.existsSync(sessionFile)).toBe(false); + expect(await SessionManager.list(cwd, sessionDir)).toHaveLength(0); + + manager.appendMessage({ role: "user", content: "queued before assistant", timestamp: Date.now() }); + manager.flushSync(); + + expect(fs.existsSync(sessionFile)).toBe(false); + expect(await SessionManager.list(cwd, sessionDir)).toHaveLength(0); + }); }); From e3f48b44bd59b6ede46f5ed99c917b8728b62e5c Mon Sep 17 00:00:00 2001 From: roboomp Date: Tue, 16 Jun 2026 21:00:25 +0000 Subject: [PATCH 2/2] fix(cli): preserved explicit session rewrites Kept shutdown flushes lazy while allowing explicit atomic rewrites to materialize pre-assistant session entries. Added regression coverage for rewriteEntries before the first assistant message. Fixes #2800 --- .../coding-agent/src/session/session-manager.ts | 2 +- .../session-manager-immediate-persist.test.ts | 17 +++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/packages/coding-agent/src/session/session-manager.ts b/packages/coding-agent/src/session/session-manager.ts index 621ae4163..3196e456e 100644 --- a/packages/coding-agent/src/session/session-manager.ts +++ b/packages/coding-agent/src/session/session-manager.ts @@ -483,7 +483,7 @@ export class SessionManager { * closed — so entries appended before the task runs are included. */ async #rewriteAtomically(): Promise { - if (!this.#persist || !this.#sessionFile || !this.#shouldHaveSessionFile()) return; + if (!this.#persist || !this.#sessionFile) return; const epoch = this.#diskEpoch; await this.#scheduleDiskWork( diff --git a/packages/coding-agent/test/session-manager-immediate-persist.test.ts b/packages/coding-agent/test/session-manager-immediate-persist.test.ts index 807ca9fa1..a67c42aea 100644 --- a/packages/coding-agent/test/session-manager-immediate-persist.test.ts +++ b/packages/coding-agent/test/session-manager-immediate-persist.test.ts @@ -107,4 +107,21 @@ describe("SessionManager immediate JSONL persistence", () => { expect(fs.existsSync(sessionFile)).toBe(false); expect(await SessionManager.list(cwd, sessionDir)).toHaveLength(0); }); + + it("lets explicit rewrites materialize pre-assistant entries", async () => { + const cwd = makeTempDir("@pi-explicit-rewrite-cwd-"); + const sessionDir = path.join(cwd, "sessions"); + const manager = SessionManager.create(cwd, sessionDir); + const sessionFile = manager.getSessionFile(); + if (!sessionFile) throw new Error("Expected a persisted session file path"); + + manager.appendMessage({ role: "user", content: "persist me", timestamp: Date.now() }); + await manager.rewriteEntries(); + + expect(fs.existsSync(sessionFile)).toBe(true); + const entries = readJsonl(sessionFile); + expect(entries).toHaveLength(2); + expect(messageRole(entries[1] ?? {})).toBe("user"); + expect(messageContent(entries[1] ?? {})).toBe("persist me"); + }); });