Skip to content
Open
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
1 change: 1 addition & 0 deletions apps/server/src/provider/Layers/CodexAdapter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1401,6 +1401,7 @@ export const makeCodexAdapter = Effect.fn("makeCodexAdapter")(function* (
cwd: input.cwd ?? process.cwd(),
binaryPath: codexConfig.binaryPath,
launchArgs: resolveCodexLaunchArgs(codexConfig.launchArgs, options?.environment),
injectDeveloperInstructions: codexConfig.injectDeveloperInstructions,
...(options?.environment ? { environment: options.environment } : {}),
...(codexConfig.homePath ? { homePath: codexConfig.homePath } : {}),
...(isCodexResumeCursorSchema(input.resumeCursor)
Expand Down
21 changes: 21 additions & 0 deletions apps/server/src/provider/Layers/CodexSessionRuntime.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,27 @@ describe("buildTurnStartParams", () => {
NodeAssert.ok(settings?.developer_instructions?.includes(`as ${DEFAULT_MODEL} with medium`));
});

it.effect("omits developer instructions when injection is disabled", () =>
Effect.gen(function* () {
const params = yield* buildTurnStartParams({
threadId: "provider-thread-1",
runtimeMode: "full-access",
prompt: "Go",
model: "gpt-5.3-codex",
interactionMode: "plan",
injectDeveloperInstructions: false,
});

NodeAssert.deepStrictEqual(params.collaborationMode, {
mode: "plan",
settings: {
model: "gpt-5.3-codex",
reasoning_effort: "medium",
},
});
}),
);

it.effect("routes approvals to the auto reviewer in auto mode", () =>
Effect.gen(function* () {
const params = yield* buildTurnStartParams({
Expand Down
21 changes: 17 additions & 4 deletions apps/server/src/provider/Layers/CodexSessionRuntime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,7 @@ export interface CodexSessionRuntimeOptions {
readonly serviceTier?: CodexServiceTier | undefined;
readonly resumeCursor?: CodexResumeCursor;
readonly appServerArgs?: ReadonlyArray<string>;
readonly injectDeveloperInstructions?: boolean;
}

export interface CodexSessionRuntimeSendTurnInput {
Expand Down Expand Up @@ -339,6 +340,7 @@ function buildCodexCollaborationMode(input: {
readonly interactionMode?: ProviderInteractionMode;
readonly model?: string;
readonly effort?: EffectCodexSchema.V2TurnStartParams__ReasoningEffort;
readonly injectDeveloperInstructions?: boolean;
}): EffectCodexSchema.V2TurnStartParams__CollaborationMode | undefined {
if (input.interactionMode === undefined) {
return undefined;
Expand All @@ -350,10 +352,14 @@ function buildCodexCollaborationMode(input: {
settings: {
model,
reasoning_effort: reasoningEffort,
developer_instructions: buildCodexDeveloperInstructions(input.interactionMode, {
model,
reasoningEffort,
}),
...(input.injectDeveloperInstructions !== false
? {
developer_instructions: buildCodexDeveloperInstructions(input.interactionMode, {
model,
reasoningEffort,
}),
}
: {}),
},
};
}
Expand All @@ -370,6 +376,7 @@ export function buildTurnStartParams(input: {
readonly serviceTier?: CodexServiceTier;
readonly effort?: EffectCodexSchema.V2TurnStartParams__ReasoningEffort;
readonly interactionMode?: ProviderInteractionMode;
readonly injectDeveloperInstructions?: boolean;
}): Effect.Effect<
CodexTurnStartParamsWithCollaborationMode,
CodexErrors.CodexAppServerProtocolParseError
Expand All @@ -390,6 +397,9 @@ export function buildTurnStartParams(input: {
...(input.interactionMode ? { interactionMode: input.interactionMode } : {}),
...(input.model ? { model: input.model } : {}),
...(input.effort ? { effort: input.effort } : {}),
...(input.injectDeveloperInstructions !== undefined
? { injectDeveloperInstructions: input.injectDeveloperInstructions }
: {}),
});

return decodeCodexTurnStartParamsWithCollaborationMode({
Expand Down Expand Up @@ -1301,6 +1311,9 @@ export const makeCodexSessionRuntime = (
...(input.serviceTier ? { serviceTier: input.serviceTier } : {}),
...(input.effort ? { effort: input.effort } : {}),
...(input.interactionMode ? { interactionMode: input.interactionMode } : {}),
...(options.injectDeveloperInstructions !== undefined
? { injectDeveloperInstructions: options.injectDeveloperInstructions }
: {}),
});
const rawResponse = yield* client.raw.request("turn/start", params);
const response = yield* decodeV2TurnStartResponse(rawResponse).pipe(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ const makeCodexConfig = (overrides: Partial<CodexSettings>): CodexSettings => ({
homePath: "",
shadowHomePath: "",
launchArgs: "",
injectDeveloperInstructions: true,
customModels: [],
...overrides,
});
Expand Down
2 changes: 2 additions & 0 deletions apps/server/src/serverSettings.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ it.layer(NodeServices.layer)("server settings", (it) => {
homePath: "/Users/julius/.codex",
shadowHomePath: "",
launchArgs: "",
injectDeveloperInstructions: true,
customModels: [],
});
assert.deepEqual(next.providers.claudeAgent, {
Expand Down Expand Up @@ -514,6 +515,7 @@ it.layer(NodeServices.layer)("server settings", (it) => {
homePath: "",
shadowHomePath: "",
launchArgs: "",
injectDeveloperInstructions: true,
customModels: [],
});
assert.deepEqual(next.providers.claudeAgent, {
Expand Down
9 changes: 9 additions & 0 deletions docs/user/providers-codex.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,15 @@ This is useful when a Codex-compatible setup needs account-specific variables. A
the provider instance that should receive them, and mark API keys or tokens as sensitive. Sensitive
values are stored as server secrets and are not sent back to the app after saving.

## I Want Codex Without T3 Code's Extra Instructions

By default, T3 Code adds its own developer instructions to every Codex turn. These carry Plan mode
rules, preview browser guidance, and a note about which model is running.

Turn off **Developer instructions** in the Codex provider's Settings to run Codex with its native
behavior. Plan mode then loses its strict rules, so only disable this if you prefer Codex's built-in
behavior.

## Can I Switch Accounts In An Existing Thread?

Yes, when both Codex providers share the same `CODEX_HOME path`.
Expand Down
18 changes: 17 additions & 1 deletion packages/contracts/src/settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -237,13 +237,28 @@ export const CodexSettings = makeProviderSettingsSchema(
description: "Additional CLI arguments passed to codex app-server on session start.",
}),
),
injectDeveloperInstructions: Schema.Boolean.pipe(
Schema.withDecodingDefault(Effect.succeed(true)),
Schema.annotateKey({
title: "Developer instructions",
description:
"Inject T3 Code's collaboration mode instructions into Codex turns. Turning this off also removes Plan mode rules and preview browser guidance.",
providerSettingsForm: { control: "switch" },
}),
),
customModels: Schema.Array(Schema.String).pipe(
Schema.withDecodingDefault(Effect.succeed([])),
Schema.annotateKey({ providerSettingsForm: { hidden: true } }),
),
},
{
order: ["binaryPath", "homePath", "shadowHomePath", "launchArgs"],
order: [
"binaryPath",
"homePath",
"shadowHomePath",
"launchArgs",
"injectDeveloperInstructions",
],
},
);
export type CodexSettings = typeof CodexSettings.Type;
Expand Down Expand Up @@ -591,6 +606,7 @@ const CodexSettingsPatch = Schema.Struct({
homePath: Schema.optionalKey(TrimmedString),
shadowHomePath: Schema.optionalKey(TrimmedString),
launchArgs: Schema.optionalKey(TrimmedString),
injectDeveloperInstructions: Schema.optionalKey(Schema.Boolean),
customModels: Schema.optionalKey(Schema.Array(Schema.String)),
});

Expand Down