diff --git a/sdk/packages/core/src/hooks/checkpoint-hooks.test.ts b/sdk/packages/core/src/hooks/checkpoint-hooks.test.ts index c7e5f2f4f2..33383a807a 100644 --- a/sdk/packages/core/src/hooks/checkpoint-hooks.test.ts +++ b/sdk/packages/core/src/hooks/checkpoint-hooks.test.ts @@ -341,6 +341,47 @@ describe("createCheckpointHooks", () => { } }); + it("starts checkpointing after the user runs git init mid-session", async () => { + const cwd = await mkdtemp(join(tmpdir(), "core-checkpoint-nogit-")); + let metadata: Record | undefined; + try { + const hooks = createCheckpointHooks({ + cwd, + sessionId: "sess_git_init_later", + readSessionMetadata: async () => metadata, + writeSessionMetadata: async (next) => { + metadata = next; + }, + }); + + // First turn: no git repo yet, so no checkpoint is written. + await writeFile(join(cwd, "note.txt"), "before-init\n", "utf8"); + await runCheckpointHooks(hooks); + expect(metadata?.checkpoint).toBeUndefined(); + + // The user initializes git mid-session. + await runGit(cwd, "init"); + await runGit(cwd, "config", "user.name", "Codex Test"); + await runGit(cwd, "config", "user.email", "codex@example.com"); + await runGit(cwd, "add", "note.txt"); + await runGit(cwd, "commit", "-m", "initial"); + + // Next turn on the same hook instance picks up the new repo and + // checkpoints from here forward. + await writeFile(join(cwd, "note.txt"), "after-init\n", "utf8"); + await runCheckpointHooks(hooks, { + messages: [userMessage("first request"), userMessage("second request")], + }); + + const checkpoint = metadata?.checkpoint as CheckpointMetadata; + expect(checkpoint.history).toHaveLength(1); + expect(checkpoint.latest.runCount).toBe(2); + expect(checkpoint.latest.kind).toBe("stash"); + } finally { + await rm(cwd, { recursive: true, force: true }); + } + }); + it("skips checkpoint creation for subagents", async () => { const cwd = await createGitRepo(); let writes = 0; diff --git a/sdk/packages/core/src/hooks/checkpoint-hooks.ts b/sdk/packages/core/src/hooks/checkpoint-hooks.ts index c855a4ad09..f45a7c39af 100644 --- a/sdk/packages/core/src/hooks/checkpoint-hooks.ts +++ b/sdk/packages/core/src/hooks/checkpoint-hooks.ts @@ -321,8 +321,12 @@ export function createCheckpointHooks( let rootRunMessageStart: number | undefined; const ensureGitRepository = async (): Promise => { - if (repoSupported !== undefined) { - return repoSupported; + // Only cache the positive answer: a cwd that is not a git repo can + // become one mid-session (the user runs `git init`), and this probe + // fires at most once per user turn, so re-checking is cheap. Once the + // repo is detected, checkpoints start applying from that turn onward. + if (repoSupported === true) { + return true; } try { const result = await runGit(options.cwd, [