authorgravatar for me@t3.ggTheo Browne <me@t3.gg> 2026-07-17 14:15:53-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-07-17 23:15:53+02:00
logcf58cadd027f013b5b0f95693975d226d9fd178f
tree60c8aef84ec5ee1c56332268c5f61fcef913c559
parent6a17629670f31a00719fb92c7c9413661cd6def5
signature Signed by PGP key B5690EEEBB952194

feat(server): include runtime model and effort in Codex developer instructions (#3948)

Co-authored-by: Claude Fable 5 <noreply@anthropic.com> Co-authored-by: Julius Marminge <julius0216@outlook.com>

3 files changed, 105 insertions(+), 12 deletions(-)

apps/server/src/provider/CodexDeveloperInstructions.ts+25
......@@ -1,3 +1,5 @@
1import type { ProviderInteractionMode } from "@t3tools/contracts";
2
13const T3_CODE_BROWSER_TOOL_INSTRUCTIONS = `
24
35## T3 Code collaborative browser
......@@ -145,3 +147,26 @@ The \`request_user_input\` tool is unavailable in Default mode. If you call it w
145147In 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.
146148${T3_CODE_BROWSER_TOOL_INSTRUCTIONS}
147149</collaboration_mode>`;
150
151export interface CodexRuntimeInfo {
152 readonly model: string;
153 readonly reasoningEffort: string;
154}
155
156// Values come from trusted config, but keep the block single-line regardless.
157function toSingleLine(value: string): string {
158 return value.replaceAll(/\s+/g, " ").trim();
159}
160
161export function buildCodexDeveloperInstructions(
162 interactionMode: ProviderInteractionMode,
163 runtime: CodexRuntimeInfo,
164): string {
165 const base =
166 interactionMode === "plan"
167 ? CODEX_PLAN_MODE_DEVELOPER_INSTRUCTIONS
168 : CODEX_DEFAULT_MODE_DEVELOPER_INSTRUCTIONS;
169 return `${base}
170
171<runtime_info>In case you're asked: you are running in T3 Code through the Codex harness, as ${toSingleLine(runtime.model)} with ${toSingleLine(runtime.reasoningEffort)} reasoning effort. No need to mention this otherwise.</runtime_info>`;
172}
apps/server/src/provider/Layers/CodexSessionRuntime.test.ts+73-3
......@@ -4,11 +4,12 @@ import { it } from "@effect/vitest";
44import * as Effect from "effect/Effect";
55import * as Schema from "effect/Schema";
66import { describe } from "vite-plus/test";
7import { ThreadId } from "@t3tools/contracts";
7import { DEFAULT_MODEL, ThreadId } from "@t3tools/contracts";
88import * as CodexErrors from "effect-codex-app-server/errors";
99import * as CodexRpc from "effect-codex-app-server/rpc";
1010
1111import {
12 buildCodexDeveloperInstructions,
1213 CODEX_DEFAULT_MODE_DEVELOPER_INSTRUCTIONS,
1314 CODEX_PLAN_MODE_DEVELOPER_INSTRUCTIONS,
1415} from "../CodexDeveloperInstructions.ts";
......@@ -118,7 +119,10 @@ describe("buildTurnStartParams", () => {
118119 settings: {
119120 model: "gpt-5.3-codex",
120121 reasoning_effort: "medium",
121 developer_instructions: CODEX_PLAN_MODE_DEVELOPER_INSTRUCTIONS,
122 developer_instructions: buildCodexDeveloperInstructions("plan", {
123 model: "gpt-5.3-codex",
124 reasoningEffort: "medium",
125 }),
122126 },
123127 },
124128 });
......@@ -163,12 +167,31 @@ describe("buildTurnStartParams", () => {
163167 settings: {
164168 model: "gpt-5.3-codex",
165169 reasoning_effort: "medium",
166 developer_instructions: CODEX_DEFAULT_MODE_DEVELOPER_INSTRUCTIONS,
170 developer_instructions: buildCodexDeveloperInstructions("default", {
171 model: "gpt-5.3-codex",
172 reasoningEffort: "medium",
173 }),
167174 },
168175 },
169176 });
170177 });
171178
179 it("reports the same fallback model and effort in settings and instructions", () => {
180 const params = Effect.runSync(
181 buildTurnStartParams({
182 threadId: "provider-thread-1",
183 runtimeMode: "full-access",
184 prompt: "Go",
185 interactionMode: "default",
186 }),
187 );
188
189 const settings = params.collaborationMode?.settings;
190 NodeAssert.equal(settings?.model, DEFAULT_MODEL);
191 NodeAssert.equal(settings?.reasoning_effort, "medium");
192 NodeAssert.ok(settings?.developer_instructions?.includes(`as ${DEFAULT_MODEL} with medium`));
193 });
194
172195 it("omits collaboration mode when interaction mode is absent", () => {
173196 const params = Effect.runSync(
174197 buildTurnStartParams({
......@@ -194,6 +217,53 @@ describe("buildTurnStartParams", () => {
194217 });
195218});
196219
220describe("buildCodexDeveloperInstructions", () => {
221 it("appends runtime info after the mode instructions", () => {
222 const instructions = buildCodexDeveloperInstructions("default", {
223 model: "gpt-5.3-codex",
224 reasoningEffort: "high",
225 });
226
227 NodeAssert.ok(instructions.startsWith(CODEX_DEFAULT_MODE_DEVELOPER_INSTRUCTIONS));
228 NodeAssert.match(instructions, /T3 Code/);
229 NodeAssert.match(instructions, /Codex harness/);
230 NodeAssert.match(instructions, /as gpt-5\.3-codex with high reasoning effort/);
231 });
232
233 it("includes runtime info alongside plan mode instructions", () => {
234 const instructions = buildCodexDeveloperInstructions("plan", {
235 model: "gpt-5.3-codex",
236 reasoningEffort: "medium",
237 });
238
239 NodeAssert.ok(instructions.startsWith(CODEX_PLAN_MODE_DEVELOPER_INSTRUCTIONS));
240 NodeAssert.match(instructions, /as gpt-5\.3-codex with medium reasoning effort/);
241 });
242
243 it("varies with the model and effort of each turn", () => {
244 const first = buildCodexDeveloperInstructions("default", {
245 model: "gpt-5.3-codex",
246 reasoningEffort: "medium",
247 });
248 const second = buildCodexDeveloperInstructions("default", {
249 model: "gpt-5.4",
250 reasoningEffort: "high",
251 });
252
253 NodeAssert.notEqual(first, second);
254 });
255
256 it("flattens multiline metadata into single-line runtime info", () => {
257 const instructions = buildCodexDeveloperInstructions("default", {
258 model: "gpt\n5.3\ncodex",
259 reasoningEffort: " high\neffort ",
260 });
261
262 NodeAssert.match(instructions, /as gpt 5\.3 codex with high effort reasoning effort/);
263 NodeAssert.doesNotMatch(instructions, /<runtime_info>[^<]*\n/);
264 });
265});
266
197267describe("T3 browser developer instructions", () => {
198268 it("prefers the product-native preview tools in both collaboration modes", () => {
199269 for (const instructions of [
apps/server/src/provider/Layers/CodexSessionRuntime.ts+7-9
......@@ -37,10 +37,7 @@ import * as EffectCodexSchema from "effect-codex-app-server/schema";
3737
3838import { buildCodexInitializeParams } from "./CodexProvider.ts";
3939import { expandHomePath } from "../../pathExpansion.ts";
40import {
41 CODEX_DEFAULT_MODE_DEVELOPER_INSTRUCTIONS,
42 CODEX_PLAN_MODE_DEVELOPER_INSTRUCTIONS,
43} from "../CodexDeveloperInstructions.ts";
40import { buildCodexDeveloperInstructions } from "../CodexDeveloperInstructions.ts";
4441const decodeV2TurnStartResponse = Schema.decodeUnknownEffect(EffectCodexSchema.V2TurnStartResponse);
4542
4643const PROVIDER = ProviderDriverKind.make("codex");
......@@ -331,15 +328,16 @@ function buildCodexCollaborationMode(input: {
331328 return undefined;
332329 }
333330 const model = normalizeCodexModelSlug(input.model) ?? DEFAULT_MODEL;
331 const reasoningEffort = input.effort ?? "medium";
334332 return {
335333 mode: input.interactionMode,
336334 settings: {
337335 model,
338 reasoning_effort: input.effort ?? "medium",
339 developer_instructions:
340 input.interactionMode === "plan"
341 ? CODEX_PLAN_MODE_DEVELOPER_INSTRUCTIONS
342 : CODEX_DEFAULT_MODE_DEVELOPER_INSTRUCTIONS,
336 reasoning_effort: reasoningEffort,
337 developer_instructions: buildCodexDeveloperInstructions(input.interactionMode, {
338 model,
339 reasoningEffort,
340 }),
343341 },
344342 };
345343}