From 1dc06b6ffcc8e9d90511f721d23dd1f61619b06c Mon Sep 17 00:00:00 2001 From: cryptonerdcn Date: Thu, 5 Feb 2026 14:19:56 +0900 Subject: [PATCH] fix: ensure resume args precede image args (#10709) ## Summary Fixes argument ordering when `resumeThread()` is used with `local_image`. The SDK previously emitted CLI args with `--image` before `resume `, which caused the Codex CLI to treat `resume`/UUID as image paths and start a new session. This PR moves `resume ` before any `--image` flags and adds a regression test. ## Bug Report / Links - OpenAI issue: https://github.com/openai/codex/issues/10708 - Repro repo: https://github.com/cryptonerdcn/codex-resume-local-image-repro - Repro issue (repo): https://github.com/cryptonerdcn/codex-resume-local-image-repro/issues/1 ## Repro (pre-fix) 1. Build SDK from source 2. Run resume + local_image 3. Args order: `--image resume ` 4. Result: new session created (thread id changes) ## Fix Move `resume ` before `--image` in `CodexExec.run` and add a regression test to assert ordering. ## Tests - `cd sdk/typescript && npm test` - **Failed**: `codex-rs/target/debug/codex` missing (ENOENT) ## Notes - I can rerun tests in an environment with `codex-rs` built and report results. --- sdk/typescript/src/exec.ts | 8 ++++---- sdk/typescript/tests/exec.test.ts | 26 ++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/sdk/typescript/src/exec.ts b/sdk/typescript/src/exec.ts index d569106c8..6f8048e5e 100644 --- a/sdk/typescript/src/exec.ts +++ b/sdk/typescript/src/exec.ts @@ -115,16 +115,16 @@ export class CodexExec { commandArgs.push("--config", `approval_policy="${args.approvalPolicy}"`); } + if (args.threadId) { + commandArgs.push("resume", args.threadId); + } + if (args.images?.length) { for (const image of args.images) { commandArgs.push("--image", image); } } - if (args.threadId) { - commandArgs.push("resume", args.threadId); - } - const env: Record = {}; if (this.envOverride) { Object.assign(env, this.envOverride); diff --git a/sdk/typescript/tests/exec.test.ts b/sdk/typescript/tests/exec.test.ts index 9c4b6c253..7ef52d72e 100644 --- a/sdk/typescript/tests/exec.test.ts +++ b/sdk/typescript/tests/exec.test.ts @@ -67,4 +67,30 @@ describe("CodexExec", () => { expect(result.error.message).toMatch(/Codex Exec exited/); } }); + + it("places resume args before image args", async () => { + const { CodexExec } = await import("../src/exec"); + spawnMock.mockClear(); + const child = new FakeChildProcess(); + spawnMock.mockReturnValue(child as unknown as child_process.ChildProcess); + + setImmediate(() => { + child.stdout.end(); + child.stderr.end(); + child.emit("exit", 0, null); + }); + + const exec = new CodexExec("codex"); + for await (const _ of exec.run({ input: "hi", images: ["img.png"], threadId: "thread-id" })) { + // no-op + } + + const commandArgs = spawnMock.mock.calls[0]?.[1] as string[] | undefined; + expect(commandArgs).toBeDefined(); + const resumeIndex = commandArgs!.indexOf("resume"); + const imageIndex = commandArgs!.indexOf("--image"); + expect(resumeIndex).toBeGreaterThan(-1); + expect(imageIndex).toBeGreaterThan(-1); + expect(resumeIndex).toBeLessThan(imageIndex); + }); });