import { describe, expect, it } from "bun:test"; import * as fs from "node:fs/promises"; import * as path from "node:path"; import { CURRENT_SESSION_VERSION, type SessionHeader } from "@oh-my-pi/pi-coding-agent/session/session-entries"; import { SessionManager } from "@oh-my-pi/pi-coding-agent/session/session-manager"; import { getTerminalId } from "@oh-my-pi/pi-tui"; import { getAgentDir, getTerminalSessionsDir, removeWithRetries, setAgentDir, TempDir } from "@oh-my-pi/pi-utils"; interface JsonlMessageEntry { type: "message"; id: string; parentId: string | null; timestamp: string; message: { role: "user"; content: string; timestamp: number; }; } describe("SessionManager.forkFrom", () => { it("suppresses terminal breadcrumbs while preserving source history under a new parented session", async () => { using tempDir = TempDir.createSync("@omp-session-fork-"); const previousAgentDir = getAgentDir(); const previousTermSessionId = process.env.TERM_SESSION_ID; setAgentDir(path.join(tempDir.path(), "agent")); process.env.TERM_SESSION_ID = "omp-fork-test"; try { const cwd = path.join(tempDir.path(), "project"); const sessionDir = path.join(tempDir.path(), "sessions"); await fs.mkdir(sessionDir, { recursive: true }); const sourceFile = path.join(sessionDir, "source.jsonl"); const timestamp = new Date().toISOString(); const sourceHeader: SessionHeader = { type: "session", version: CURRENT_SESSION_VERSION, id: "source-session", timestamp, cwd, }; const sourceMessage: JsonlMessageEntry = { type: "message", id: "message-1", parentId: null, timestamp, message: { role: "user", content: "hello", timestamp: Date.now() }, }; const sourceText = `${JSON.stringify(sourceHeader)}\n${JSON.stringify(sourceMessage)}\n`; await Bun.write(sourceFile, sourceText); const terminalId = getTerminalId(); expect(terminalId).toBeString(); const breadcrumbFile = path.join(getTerminalSessionsDir(), terminalId ?? "missing"); await removeWithRetries(breadcrumbFile); const forked = await SessionManager.forkFrom(sourceFile, cwd, sessionDir, undefined, { suppressBreadcrumb: true, }); await Bun.sleep(10); const cloneFile = forked.getSessionFile(); expect(cloneFile).toBeString(); if (!cloneFile) throw new Error("expected forked session file"); expect(await Bun.file(sourceFile).text()).toBe(sourceText); expect(await Bun.file(breadcrumbFile).exists()).toBe(false); expect(cloneFile).not.toBe(sourceFile); const lines = (await Bun.file(cloneFile).text()).trim().split("\n"); const cloneHeader = JSON.parse(lines[0] ?? "{}") as SessionHeader; const cloneMessage = JSON.parse(lines[1] ?? "{}") as JsonlMessageEntry; expect(cloneHeader.id).not.toBe(sourceHeader.id); expect(cloneHeader.parentSession).toBe(sourceHeader.id); expect(cloneHeader.cwd).toBe(cwd); expect(cloneMessage.message.content).toBe("hello"); } finally { if (previousTermSessionId === undefined) { delete process.env.TERM_SESSION_ID; } else { process.env.TERM_SESSION_ID = previousTermSessionId; } setAgentDir(previousAgentDir); } }); });