diff --git a/plugins/codex/scripts/lib/codex.mjs b/plugins/codex/scripts/lib/codex.mjs index fead00cc4..dd7efec1c 100644 --- a/plugins/codex/scripts/lib/codex.mjs +++ b/plugins/codex/scripts/lib/codex.mjs @@ -248,7 +248,10 @@ function describeStartedItem(state, item) { phase: looksLikeVerificationCommand(item.command) ? "verifying" : "running" }; case "fileChange": - return { message: `Applying ${item.changes.length} file change(s).`, phase: "editing" }; + return { + message: `Applying ${Array.isArray(item.changes) ? item.changes.length : 0} file change(s).`, + phase: "editing" + }; case "mcpToolCall": return { message: `Calling ${item.server}/${item.tool}.`, phase: "investigating" }; case "dynamicToolCall": diff --git a/tests/fake-codex-fixture.mjs b/tests/fake-codex-fixture.mjs index f83c96a0d..5743b9dce 100644 --- a/tests/fake-codex-fixture.mjs +++ b/tests/fake-codex-fixture.mjs @@ -568,6 +568,13 @@ rl.on("line", (line) => { } const items = [ + ...(BEHAVIOR === "file-change-started-without-changes" + ? [ + { + started: { type: "fileChange", id: "fc_" + turnId } + } + ] + : []), ...(BEHAVIOR === "with-reasoning" ? [ { diff --git a/tests/runtime.test.mjs b/tests/runtime.test.mjs index 8f276835b..6d0efbe78 100644 --- a/tests/runtime.test.mjs +++ b/tests/runtime.test.mjs @@ -175,6 +175,25 @@ test("task runs when the active provider does not require OpenAI login", () => { assert.match(result.stdout, /Handled the requested task/); }); +test("task survives fileChange started items that omit changes", () => { + const repo = makeTempDir(); + const binDir = makeTempDir(); + installFakeCodex(binDir, "file-change-started-without-changes"); + initGitRepo(repo); + fs.writeFileSync(path.join(repo, "README.md"), "hello\n"); + run("git", ["add", "README.md"], { cwd: repo }); + run("git", ["commit", "-m", "init"], { cwd: repo }); + + const result = run("node", [SCRIPT, "task", "apply the patch"], { + cwd: repo, + env: buildEnv(binDir) + }); + + assert.equal(result.status, 0, result.stderr); + assert.match(result.stdout, /Handled the requested task/); + assert.doesNotMatch(result.stderr, /Cannot read properties of undefined/); +}); + test("task runs without auth preflight so Codex can refresh an expired session", () => { const repo = makeTempDir(); const binDir = makeTempDir();