History

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

Julius Marminge committed 8 months ago

Commit 551aebc1 parent 9de85ea3 files · +60 −7Browse files

Unified · Split

3 files
  1. apps/server/src/codexAppServerManager.test.ts +31 −0
  2. apps/server/src/codexAppServerManager.ts +21 −5
  3. apps/web/src/store.test.ts +8 −2
apps/server/src/codexAppServerManager.test.ts+31−0
@@ -7,6 +7,7 @@ import { ApprovalRequestId, ThreadId } from "@t3tools/contracts"; · 6 unmodified lines
77
88 import {
99 buildCodexInitializeParams,
10+ CODEX_DEFAULT_MODE_DEVELOPER_INSTRUCTIONS,
1011 CODEX_PLAN_MODE_DEVELOPER_INSTRUCTIONS,
1112 CodexAppServerManager,
1213 classifyCodexStderrLine,
@@ -346,6 +347,36 @@ describe("sendTurn", () => { · 333 unmodified lines
346347 });
347348 });
348349
350+ it("passes Codex default mode as a collaboration preset on turn/start", async () => {
351+ const { manager, context, sendRequest } = createSendTurnHarness();
352+
353+ await manager.sendTurn({
354+ threadId: asThreadId("thread_1"),
355+ input: "PLEASE IMPLEMENT THIS PLAN:\n- step 1",
356+ interactionMode: "default",
357+ });
358+
359+ expect(sendRequest).toHaveBeenCalledWith(context, "turn/start", {
360+ threadId: "thread_1",
361+ input: [
362+ {
363+ type: "text",
364+ text: "PLEASE IMPLEMENT THIS PLAN:\n- step 1",
365+ text_elements: [],
366+ },
367+ ],
368+ model: "gpt-5.3-codex",
369+ collaborationMode: {
370+ mode: "default",
371+ settings: {
372+ model: "gpt-5.3-codex",
373+ reasoning_effort: "medium",
374+ developer_instructions: CODEX_DEFAULT_MODE_DEVELOPER_INSTRUCTIONS,
375+ },
376+ },
377+ });
378+ });
379+
349380 it("rejects empty turn input", async () => {
350381 const { manager } = createSendTurnHarness();
351382
apps/server/src/codexAppServerManager.ts+21−5
@@ -257,6 +257,19 @@ Do not ask "should I proceed?" in the final output. The user can easily switch o · 256 unmodified lines
257257 Only produce at most one \`<proposed_plan>\` block per turn, and only when you are presenting a complete spec.
258258 </collaboration_mode>`;
259259
260+export const CODEX_DEFAULT_MODE_DEVELOPER_INSTRUCTIONS = `<collaboration_mode># Collaboration Mode: Default
261+
262+You are now in Default mode. Any previous instructions for other modes (e.g. Plan mode) are no longer active.
263+
264+Your active mode changes only when new developer instructions with a different \`<collaboration_mode>...</collaboration_mode>\` change it; user requests or tool descriptions do not change mode by themselves. Known mode names are Default and Plan.
265+
266+## request_user_input availability
267+
268+The \`request_user_input\` tool is unavailable in Default mode. If you call it while in Default mode, it will return an error.
269+
270+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.
271+</collaboration_mode>`;
272+
260273 function mapCodexRuntimeMode(runtimeMode: RuntimeMode): {
261274 readonly approvalPolicy: "on-request" | "never";
262275 readonly sandbox: "workspace-write" | "danger-full-access";
@@ -326,7 +339,7 @@ function buildCodexCollaborationMode(input: { · 63 unmodified lines
326339 readonly effort?: string;
327340 }):
328341 | {
329− mode: "plan";
342+ mode: "default" | "plan";
330343 settings: {
331344 model: string;
332345 reasoning_effort: string;
@@ -334,16 +347,19 @@ function buildCodexCollaborationMode(input: { · 1 unmodified line
334347 };
335348 }
336349 | undefined {
337− if (input.interactionMode !== "plan") {
350+ if (input.interactionMode === undefined) {
338351 return undefined;
339352 }
340353 const model = normalizeCodexModelSlug(input.model) ?? "gpt-5.3-codex";
341354 return {
342− mode: "plan",
355+ mode: input.interactionMode,
343356 settings: {
344357 model,
345358 reasoning_effort: input.effort ?? "medium",
346− developer_instructions: CODEX_PLAN_MODE_DEVELOPER_INSTRUCTIONS,
359+ developer_instructions:
360+ input.interactionMode === "plan"
361+ ? CODEX_PLAN_MODE_DEVELOPER_INSTRUCTIONS
362+ : CODEX_DEFAULT_MODE_DEVELOPER_INSTRUCTIONS,
347363 },
348364 };
349365 }
@@ -644,7 +660,7 @@ export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEve · 294 unmodified lines
644660 effort?: string;
645661 serviceTier?: string;
646662 collaborationMode?: {
647− mode: "plan";
663+ mode: "default" | "plan";
648664 settings: {
649665 model: string;
650666 reasoning_effort: string;
apps/web/src/store.test.ts+8−2
@@ -1,4 +1,10 @@
1−import { ProjectId, ThreadId, TurnId, type OrchestrationReadModel } from "@t3tools/contracts";
1+import {
2+ DEFAULT_MODEL_BY_PROVIDER,
3+ ProjectId,
4+ ThreadId,
5+ TurnId,
6+ type OrchestrationReadModel,
7+} from "@t3tools/contracts";
28 import { describe, expect, it } from "vitest";
39
410 import { markThreadUnread, syncServerReadModel, type AppState } from "./store";
@@ -139,6 +145,6 @@ describe("store read model sync", () => { · 134 unmodified lines
139145
140146 const next = syncServerReadModel(initialState, readModel);
141147
142− expect(next.threads[0]?.model).toBe("gpt-5.3-codex");
148+ expect(next.threads[0]?.model).toBe(DEFAULT_MODEL_BY_PROVIDER.codex);
143149 });
144150 });