diff --git a/src/integrations/harness/providers/codex/codexProtocol.test.ts b/src/integrations/harness/providers/codex/codexProtocol.test.ts index 4602559cc..6aacb3e3f 100644 --- a/src/integrations/harness/providers/codex/codexProtocol.test.ts +++ b/src/integrations/harness/providers/codex/codexProtocol.test.ts @@ -157,6 +157,28 @@ describe("buildThreadStartParams / buildTurnStartParams", () => { }); }); + it("leaves the model to Codex before one has been selected", () => { + const params = buildTurnStartParams({ + threadId: "t", + runtimeMode: "auto", + model: "", + }); + expect(params).not.toHaveProperty("model"); + expect(params).not.toHaveProperty("collaborationMode"); + }); + + it("does not send a null collaboration model for a plan turn either", () => { + const params = buildTurnStartParams({ + threadId: "t", + runtimeMode: "auto", + model: "", + intent: "plan", + }); + expect(params).not.toHaveProperty("collaborationMode"); + expect(params.sandboxPolicy).toEqual({ type: "readOnly" }); + expect(params.approvalPolicy).toBe("never"); + }); + it("uses native plan mode with a non-escalating read-only sandbox", () => { const turn = buildTurnStartParams({ threadId: "thr_1", diff --git a/src/integrations/harness/providers/codex/codexProtocol.ts b/src/integrations/harness/providers/codex/codexProtocol.ts index b46f69c9c..691e178c2 100644 --- a/src/integrations/harness/providers/codex/codexProtocol.ts +++ b/src/integrations/harness/providers/codex/codexProtocol.ts @@ -158,21 +158,29 @@ export function buildTurnStartParams(input: { input.controlsAgents, ) : runtimeConfig; + // settings.model is a required string. Null is rejected + // ("invalid type: null, expected a string") and omitting it is + // "missing field `model`". Skip the override until a model is known + // so Codex keeps the one it chose when the thread started. + const model = input.model?.trim() ?? ""; + const collaborationMode = model + ? { + mode: input.intent === "plan" ? "plan" : "default", + settings: { + model, + reasoning_effort: input.effort ?? null, + developer_instructions: null, + }, + } + : undefined; return { threadId: input.threadId, input: codexInput(input.prompt, input.attachments), approvalPolicy: config.approvalPolicy, approvalsReviewer: config.approvalsReviewer, sandboxPolicy: config.sandboxPolicy, - collaborationMode: { - mode: input.intent === "plan" ? "plan" : "default", - settings: { - model: input.model ?? null, - reasoning_effort: input.effort ?? null, - developer_instructions: null, - }, - }, - ...(input.model ? { model: input.model } : {}), + ...(collaborationMode ? { collaborationMode } : {}), + ...(model ? { model } : {}), ...(input.effort ? { effort: input.effort } : {}), ...(input.serviceTier && input.serviceTier !== "default" ? { serviceTier: input.serviceTier }