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
7 7
8import {8import {
9 buildCodexInitializeParams,9 buildCodexInitializeParams,
10 CODEX_DEFAULT_MODE_DEVELOPER_INSTRUCTIONS,
10 CODEX_PLAN_MODE_DEVELOPER_INSTRUCTIONS,11 CODEX_PLAN_MODE_DEVELOPER_INSTRUCTIONS,
11 CodexAppServerManager,12 CodexAppServerManager,
12 classifyCodexStderrLine,13 classifyCodexStderrLine,
@@ -346,6 +347,36 @@ describe("sendTurn", () => { · 333 unmodified lines
346 });347 });
347 });348 });
348 349
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
349 it("rejects empty turn input", async () => {380 it("rejects empty turn input", async () => {
350 const { manager } = createSendTurnHarness();381 const { manager } = createSendTurnHarness();
351 382
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
257Only produce at most one \`<proposed_plan>\` block per turn, and only when you are presenting a complete spec.257Only produce at most one \`<proposed_plan>\` block per turn, and only when you are presenting a complete spec.
258</collaboration_mode>`;258</collaboration_mode>`;
259 259
260export const CODEX_DEFAULT_MODE_DEVELOPER_INSTRUCTIONS = `<collaboration_mode># Collaboration Mode: Default
261
262You are now in Default mode. Any previous instructions for other modes (e.g. Plan mode) are no longer active.
263
264Your 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
268The \`request_user_input\` tool is unavailable in Default mode. If you call it while in Default mode, it will return an error.
269
270In 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
260function mapCodexRuntimeMode(runtimeMode: RuntimeMode): {273function mapCodexRuntimeMode(runtimeMode: RuntimeMode): {
261 readonly approvalPolicy: "on-request" | "never";274 readonly approvalPolicy: "on-request" | "never";
262 readonly sandbox: "workspace-write" | "danger-full-access";275 readonly sandbox: "workspace-write" | "danger-full-access";
@@ -326,7 +339,7 @@ function buildCodexCollaborationMode(input: { · 63 unmodified lines
326 readonly effort?: string;339 readonly effort?: string;
327}):340}):
328 | {341 | {
329 mode: "plan";342 mode: "default" | "plan";
330 settings: {343 settings: {
331 model: string;344 model: string;
332 reasoning_effort: string;345 reasoning_effort: string;
@@ -334,16 +347,19 @@ function buildCodexCollaborationMode(input: { · 1 unmodified line
334 };347 };
335 }348 }
336 | undefined {349 | undefined {
337 if (input.interactionMode !== "plan") {350 if (input.interactionMode === undefined) {
338 return undefined;351 return undefined;
339 }352 }
340 const model = normalizeCodexModelSlug(input.model) ?? "gpt-5.3-codex";353 const model = normalizeCodexModelSlug(input.model) ?? "gpt-5.3-codex";
341 return {354 return {
342 mode: "plan",355 mode: input.interactionMode,
343 settings: {356 settings: {
344 model,357 model,
345 reasoning_effort: input.effort ?? "medium",358 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,
347 },363 },
348 };364 };
349}365}
@@ -644,7 +660,7 @@ export class CodexAppServerManager extends EventEmitter<CodexAppServerManagerEve · 294 unmodified lines
644 effort?: string;660 effort?: string;
645 serviceTier?: string;661 serviceTier?: string;
646 collaborationMode?: {662 collaborationMode?: {
647 mode: "plan";663 mode: "default" | "plan";
648 settings: {664 settings: {
649 model: string;665 model: string;
650 reasoning_effort: string;666 reasoning_effort: string;
apps/web/src/store.test.ts+8−2
@@ -1,4 +1,10 @@
1import { ProjectId, ThreadId, TurnId, type OrchestrationReadModel } from "@t3tools/contracts";1import {
2 DEFAULT_MODEL_BY_PROVIDER,
3 ProjectId,
4 ThreadId,
5 TurnId,
6 type OrchestrationReadModel,
7} from "@t3tools/contracts";
2import { describe, expect, it } from "vitest";8import { describe, expect, it } from "vitest";
3 9
4import { markThreadUnread, syncServerReadModel, type AppState } from "./store";10import { markThreadUnread, syncServerReadModel, type AppState } from "./store";
@@ -139,6 +145,6 @@ describe("store read model sync", () => { · 134 unmodified lines
139 145
140 const next = syncServerReadModel(initialState, readModel);146 const next = syncServerReadModel(initialState, readModel);
141 147
142 expect(next.threads[0]?.model).toBe("gpt-5.3-codex");148 expect(next.threads[0]?.model).toBe(DEFAULT_MODEL_BY_PROVIDER.codex);
143 });149 });
144});150});