From 551aebc6046f23eabcf0ea6dddab73619b83b2da Mon Sep 17 00:00:00 2001 From: Julius Marminge Date: Thu, 5 Mar 2026 22:43:16 -0800 Subject: [PATCH] Add default collaboration mode preset for Codex turn starts - Send `collaborationMode` for both `default` and `plan` interactions - Add default-mode developer instructions and preserve plan-mode behavior - Update server/web tests for default collaboration mode and model constant usage --- apps/server/src/codexAppServerManager.test.ts | 31 +++++++++++++++++++ apps/server/src/codexAppServerManager.ts | 26 +++++++++++++--- apps/web/src/store.test.ts | 10 ++++-- 3 files changed, 60 insertions(+), 7 deletions(-) diff --git a/apps/server/src/codexAppServerManager.test.ts b/apps/server/src/codexAppServerManager.test.ts index 58c34020294c8552e62d609679b19e1e3b7847ea..f2e11ca292e39b1a00e3917620d29d7666c2d66b 100644 --- a/apps/server/src/codexAppServerManager.test.ts +++ b/apps/server/src/codexAppServerManager.test.ts @@ -7,6 +7,7 @@ import { ApprovalRequestId, ThreadId } from "@t3tools/contracts"; import { buildCodexInitializeParams, + CODEX_DEFAULT_MODE_DEVELOPER_INSTRUCTIONS, CODEX_PLAN_MODE_DEVELOPER_INSTRUCTIONS, CodexAppServerManager, classifyCodexStderrLine, @@ -346,6 +347,36 @@ describe("sendTurn", () => { }); }); + it("passes Codex default mode as a collaboration preset on turn/start", async () => { + const { manager, context, sendRequest } = createSendTurnHarness(); + + await manager.sendTurn({ + threadId: asThreadId("thread_1"), + input: "PLEASE IMPLEMENT THIS PLAN:\n- step 1", + interactionMode: "default", + }); + + expect(sendRequest).toHaveBeenCalledWith(context, "turn/start", { + threadId: "thread_1", + input: [ + { + type: "text", + text: "PLEASE IMPLEMENT THIS PLAN:\n- step 1", + text_elements: [], + }, + ], + model: "gpt-5.3-codex", + collaborationMode: { + mode: "default", + settings: { + model: "gpt-5.3-codex", + reasoning_effort: "medium", + developer_instructions: CODEX_DEFAULT_MODE_DEVELOPER_INSTRUCTIONS, + }, + }, + }); + }); + it("rejects empty turn input", async () => { const { manager } = createSendTurnHarness(); diff --git a/apps/server/src/codexAppServerManager.ts b/apps/server/src/codexAppServerManager.ts index 185851ae16cb1c6ffc0e053459f5047383269963..6e83d211ad91e9bbcb13c2b76346bbc47b1d08a8 100644 --- a/apps/server/src/codexAppServerManager.ts +++ b/apps/server/src/codexAppServerManager.ts @@ -257,6 +257,19 @@ Do not ask "should I proceed?" in the final output. The user can easily switch o Only produce at most one \`\` block per turn, and only when you are presenting a complete spec. `; +export const CODEX_DEFAULT_MODE_DEVELOPER_INSTRUCTIONS = `# Collaboration Mode: Default + +You are now in Default mode. Any previous instructions for other modes (e.g. Plan mode) are no longer active. + +Your active mode changes only when new developer instructions with a different \`...\` change it; user requests or tool descriptions do not change mode by themselves. Known mode names are Default and Plan. + +## request_user_input availability + +The \`request_user_input\` tool is unavailable in Default mode. If you call it while in Default mode, it will return an error. + +In Default mode, strongly prefer making reasonable assumptions and executing the user's request rather than stopping to ask questions. If you absolutely must ask a question because the answer cannot be discovered from local context and a reasonable assumption would be risky, ask the user directly with a concise plain-text question. Never write a multiple choice question as a textual assistant message. +`; + function mapCodexRuntimeMode(runtimeMode: RuntimeMode): { readonly approvalPolicy: "on-request" | "never"; readonly sandbox: "workspace-write" | "danger-full-access"; @@ -326,7 +339,7 @@ function buildCodexCollaborationMode(input: { readonly effort?: string; }): | { - mode: "plan"; + mode: "default" | "plan"; settings: { model: string; reasoning_effort: string; @@ -334,16 +347,19 @@ function buildCodexCollaborationMode(input: { }; } | undefined { - if (input.interactionMode !== "plan") { + if (input.interactionMode === undefined) { return undefined; } const model = normalizeCodexModelSlug(input.model) ?? "gpt-5.3-codex"; return { - mode: "plan", + mode: input.interactionMode, settings: { model, reasoning_effort: input.effort ?? "medium", - developer_instructions: CODEX_PLAN_MODE_DEVELOPER_INSTRUCTIONS, + developer_instructions: + input.interactionMode === "plan" + ? CODEX_PLAN_MODE_DEVELOPER_INSTRUCTIONS + : CODEX_DEFAULT_MODE_DEVELOPER_INSTRUCTIONS, }, }; } @@ -644,7 +660,7 @@ export class CodexAppServerManager extends EventEmitter { const next = syncServerReadModel(initialState, readModel); - expect(next.threads[0]?.model).toBe("gpt-5.3-codex"); + expect(next.threads[0]?.model).toBe(DEFAULT_MODEL_BY_PROVIDER.codex); }); }); -- 2.54.0