Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions apps/server/src/provider/CodexDeveloperInstructions.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import type { ProviderInteractionMode } from "@t3tools/contracts";

const T3_CODE_BROWSER_TOOL_INSTRUCTIONS = `

## T3 Code collaborative browser
Expand Down Expand Up @@ -145,3 +147,26 @@ The \`request_user_input\` tool is unavailable in Default mode. If you call it w
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.
${T3_CODE_BROWSER_TOOL_INSTRUCTIONS}
</collaboration_mode>`;

export interface CodexRuntimeInfo {
readonly model: string;
readonly reasoningEffort: string;
}

// Values come from trusted config, but keep the block single-line regardless.
function toSingleLine(value: string): string {
return value.replaceAll(/\s+/g, " ").trim();
}

export function buildCodexDeveloperInstructions(
interactionMode: ProviderInteractionMode,
runtime: CodexRuntimeInfo,
): string {
const base =
interactionMode === "plan"
? CODEX_PLAN_MODE_DEVELOPER_INSTRUCTIONS
: CODEX_DEFAULT_MODE_DEVELOPER_INSTRUCTIONS;
return `${base}

<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>`;
}
76 changes: 73 additions & 3 deletions apps/server/src/provider/Layers/CodexSessionRuntime.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@ import { it } from "@effect/vitest";
import * as Effect from "effect/Effect";
import * as Schema from "effect/Schema";
import { describe } from "vite-plus/test";
import { ThreadId } from "@t3tools/contracts";
import { DEFAULT_MODEL, ThreadId } from "@t3tools/contracts";
import * as CodexErrors from "effect-codex-app-server/errors";
import * as CodexRpc from "effect-codex-app-server/rpc";

import {
buildCodexDeveloperInstructions,
CODEX_DEFAULT_MODE_DEVELOPER_INSTRUCTIONS,
CODEX_PLAN_MODE_DEVELOPER_INSTRUCTIONS,
} from "../CodexDeveloperInstructions.ts";
Expand Down Expand Up @@ -118,7 +119,10 @@ describe("buildTurnStartParams", () => {
settings: {
model: "gpt-5.3-codex",
reasoning_effort: "medium",
developer_instructions: CODEX_PLAN_MODE_DEVELOPER_INSTRUCTIONS,
developer_instructions: buildCodexDeveloperInstructions("plan", {
model: "gpt-5.3-codex",
reasoningEffort: "medium",
}),
},
},
});
Expand Down Expand Up @@ -163,12 +167,31 @@ describe("buildTurnStartParams", () => {
settings: {
model: "gpt-5.3-codex",
reasoning_effort: "medium",
developer_instructions: CODEX_DEFAULT_MODE_DEVELOPER_INSTRUCTIONS,
developer_instructions: buildCodexDeveloperInstructions("default", {
model: "gpt-5.3-codex",
reasoningEffort: "medium",
}),
},
},
});
});

it("reports the same fallback model and effort in settings and instructions", () => {
const params = Effect.runSync(
buildTurnStartParams({
threadId: "provider-thread-1",
runtimeMode: "full-access",
prompt: "Go",
interactionMode: "default",
}),
);

const settings = params.collaborationMode?.settings;
NodeAssert.equal(settings?.model, DEFAULT_MODEL);
NodeAssert.equal(settings?.reasoning_effort, "medium");
NodeAssert.ok(settings?.developer_instructions?.includes(`as ${DEFAULT_MODEL} with medium`));
});

it("omits collaboration mode when interaction mode is absent", () => {
const params = Effect.runSync(
buildTurnStartParams({
Expand All @@ -194,6 +217,53 @@ describe("buildTurnStartParams", () => {
});
});

describe("buildCodexDeveloperInstructions", () => {
it("appends runtime info after the mode instructions", () => {
const instructions = buildCodexDeveloperInstructions("default", {
model: "gpt-5.3-codex",
reasoningEffort: "high",
});

NodeAssert.ok(instructions.startsWith(CODEX_DEFAULT_MODE_DEVELOPER_INSTRUCTIONS));
NodeAssert.match(instructions, /T3 Code/);
NodeAssert.match(instructions, /Codex harness/);
NodeAssert.match(instructions, /as gpt-5\.3-codex with high reasoning effort/);
});

it("includes runtime info alongside plan mode instructions", () => {
const instructions = buildCodexDeveloperInstructions("plan", {
model: "gpt-5.3-codex",
reasoningEffort: "medium",
});

NodeAssert.ok(instructions.startsWith(CODEX_PLAN_MODE_DEVELOPER_INSTRUCTIONS));
NodeAssert.match(instructions, /as gpt-5\.3-codex with medium reasoning effort/);
});

it("varies with the model and effort of each turn", () => {
const first = buildCodexDeveloperInstructions("default", {
model: "gpt-5.3-codex",
reasoningEffort: "medium",
});
const second = buildCodexDeveloperInstructions("default", {
model: "gpt-5.4",
reasoningEffort: "high",
});

NodeAssert.notEqual(first, second);
});

it("flattens multiline metadata into single-line runtime info", () => {
const instructions = buildCodexDeveloperInstructions("default", {
model: "gpt\n5.3\ncodex",
reasoningEffort: " high\neffort ",
});

NodeAssert.match(instructions, /as gpt 5\.3 codex with high effort reasoning effort/);
NodeAssert.doesNotMatch(instructions, /<runtime_info>[^<]*\n/);
});
});

describe("T3 browser developer instructions", () => {
it("prefers the product-native preview tools in both collaboration modes", () => {
for (const instructions of [
Expand Down
16 changes: 7 additions & 9 deletions apps/server/src/provider/Layers/CodexSessionRuntime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,7 @@ import * as EffectCodexSchema from "effect-codex-app-server/schema";

import { buildCodexInitializeParams } from "./CodexProvider.ts";
import { expandHomePath } from "../../pathExpansion.ts";
import {
CODEX_DEFAULT_MODE_DEVELOPER_INSTRUCTIONS,
CODEX_PLAN_MODE_DEVELOPER_INSTRUCTIONS,
} from "../CodexDeveloperInstructions.ts";
import { buildCodexDeveloperInstructions } from "../CodexDeveloperInstructions.ts";
const decodeV2TurnStartResponse = Schema.decodeUnknownEffect(EffectCodexSchema.V2TurnStartResponse);

const PROVIDER = ProviderDriverKind.make("codex");
Expand Down Expand Up @@ -331,15 +328,16 @@ function buildCodexCollaborationMode(input: {
return undefined;
}
const model = normalizeCodexModelSlug(input.model) ?? DEFAULT_MODEL;
const reasoningEffort = input.effort ?? "medium";
return {
mode: input.interactionMode,
settings: {
model,
reasoning_effort: input.effort ?? "medium",
developer_instructions:
input.interactionMode === "plan"
? CODEX_PLAN_MODE_DEVELOPER_INSTRUCTIONS
: CODEX_DEFAULT_MODE_DEVELOPER_INSTRUCTIONS,
reasoning_effort: reasoningEffort,
developer_instructions: buildCodexDeveloperInstructions(input.interactionMode, {
model,
reasoningEffort,
}),
},
};
}
Expand Down
Loading