From 07e532dcb927b08b0174bd8d0cdf7d0632eea14f Mon Sep 17 00:00:00 2001 From: pash-openai Date: Tue, 3 Mar 2026 02:35:09 -0800 Subject: [PATCH] app-server service tier plumbing (plus some cleanup) (#13334) followup to https://github.com/openai/codex/pull/13212 to expose fast tier controls to app server (majority of this PR is generated schema jsons - actual code is +69 / -35 and +24 tests ) - add service tier fields to the app-server protocol surfaces used by thread lifecycle, turn start, config, and session configured events - thread service tier through the app-server message processor and core thread config snapshots - allow runtime config overrides to carry service tier for app-server callers cleanup: - Removing useless "legacy" code supporting "standard" - we moved to None | "fast", so "standard" is not needed. --- codex-rs/Cargo.lock | 1 + codex-rs/app-server-protocol/Cargo.toml | 1 + .../schema/json/ClientRequest.json | 75 ++++++++++ .../schema/json/EventMsg.json | 26 ++++ .../codex_app_server_protocol.schemas.json | 135 ++++++++++++++++++ .../schema/json/v2/ConfigReadResponse.json | 26 ++++ .../schema/json/v2/ThreadForkParams.json | 23 +++ .../schema/json/v2/ThreadForkResponse.json | 16 +++ .../schema/json/v2/ThreadResumeParams.json | 23 +++ .../schema/json/v2/ThreadResumeResponse.json | 16 +++ .../schema/json/v2/ThreadStartParams.json | 23 +++ .../schema/json/v2/ThreadStartResponse.json | 16 +++ .../schema/json/v2/TurnStartParams.json | 24 ++++ .../schema/typescript/SendUserTurnParams.ts | 3 +- .../schema/typescript/ServiceTier.ts | 5 + .../typescript/SessionConfiguredEvent.ts | 3 +- .../SessionConfiguredNotification.ts | 3 +- .../schema/typescript/index.ts | 1 + .../schema/typescript/v2/Config.ts | 3 +- .../schema/typescript/v2/ProfileV2.ts | 3 +- .../schema/typescript/v2/ThreadForkParams.ts | 3 +- .../typescript/v2/ThreadForkResponse.ts | 3 +- .../typescript/v2/ThreadResumeParams.ts | 3 +- .../typescript/v2/ThreadResumeResponse.ts | 3 +- .../schema/typescript/v2/ThreadStartParams.ts | 3 +- .../typescript/v2/ThreadStartResponse.ts | 3 +- .../schema/typescript/v2/TurnStartParams.ts | 4 + .../app-server-protocol/src/protocol/mod.rs | 1 + .../src/protocol/serde_helpers.rs | 23 +++ .../app-server-protocol/src/protocol/v1.rs | 58 ++++++++ .../app-server-protocol/src/protocol/v2.rs | 91 ++++++++++++ .../app-server/src/codex_message_processor.rs | 21 ++- .../suite/codex_message_processor_flow.rs | 3 + .../app-server/tests/suite/output_schema.rs | 4 + .../app-server/tests/suite/v2/turn_start.rs | 2 + codex-rs/core/config.schema.json | 19 +-- codex-rs/core/src/codex.rs | 2 + codex-rs/core/src/codex_thread.rs | 2 + codex-rs/core/src/config/mod.rs | 39 ++--- codex-rs/exec/src/lib.rs | 1 + .../tests/event_processor_with_json_output.rs | 1 + codex-rs/mcp-server/src/outgoing_message.rs | 3 + codex-rs/protocol/src/config_types.rs | 2 - codex-rs/protocol/src/protocol.rs | 4 + codex-rs/tui/src/app.rs | 11 ++ codex-rs/tui/src/chatwidget/tests.rs | 14 ++ codex-rs/tui/src/history_cell.rs | 1 + 47 files changed, 689 insertions(+), 61 deletions(-) create mode 100644 codex-rs/app-server-protocol/schema/typescript/ServiceTier.ts create mode 100644 codex-rs/app-server-protocol/src/protocol/serde_helpers.rs diff --git a/codex-rs/Cargo.lock b/codex-rs/Cargo.lock index 6e13edc7b..ed6f377d0 100644 --- a/codex-rs/Cargo.lock +++ b/codex-rs/Cargo.lock @@ -1474,6 +1474,7 @@ dependencies = [ "schemars 0.8.22", "serde", "serde_json", + "serde_with", "shlex", "similar", "strum_macros 0.27.2", diff --git a/codex-rs/app-server-protocol/Cargo.toml b/codex-rs/app-server-protocol/Cargo.toml index f5df7af03..dc6823a4a 100644 --- a/codex-rs/app-server-protocol/Cargo.toml +++ b/codex-rs/app-server-protocol/Cargo.toml @@ -20,6 +20,7 @@ codex-utils-absolute-path = { workspace = true } schemars = { workspace = true } serde = { workspace = true, features = ["derive"] } serde_json = { workspace = true } +serde_with = { workspace = true } shlex = { workspace = true } strum_macros = { workspace = true } thiserror = { workspace = true } diff --git a/codex-rs/app-server-protocol/schema/json/ClientRequest.json b/codex-rs/app-server-protocol/schema/json/ClientRequest.json index 03e42bed4..01790fb98 100644 --- a/codex-rs/app-server-protocol/schema/json/ClientRequest.json +++ b/codex-rs/app-server-protocol/schema/json/ClientRequest.json @@ -1703,6 +1703,12 @@ } ] }, + "ServiceTier": { + "enum": [ + "fast" + ], + "type": "string" + }, "Settings": { "description": "Settings for a collaboration mode.", "properties": { @@ -1933,6 +1939,23 @@ } ] }, + "serviceTier": { + "anyOf": [ + { + "anyOf": [ + { + "$ref": "#/definitions/ServiceTier" + }, + { + "type": "null" + } + ] + }, + { + "type": "null" + } + ] + }, "threadId": { "type": "string" } @@ -2155,6 +2178,23 @@ } ] }, + "serviceTier": { + "anyOf": [ + { + "anyOf": [ + { + "$ref": "#/definitions/ServiceTier" + }, + { + "type": "null" + } + ] + }, + { + "type": "null" + } + ] + }, "threadId": { "type": "string" } @@ -2299,6 +2339,23 @@ "string", "null" ] + }, + "serviceTier": { + "anyOf": [ + { + "anyOf": [ + { + "$ref": "#/definitions/ServiceTier" + }, + { + "type": "null" + } + ] + }, + { + "type": "null" + } + ] } }, "type": "object" @@ -2409,6 +2466,24 @@ ], "description": "Override the sandbox policy for this turn and subsequent turns." }, + "serviceTier": { + "anyOf": [ + { + "anyOf": [ + { + "$ref": "#/definitions/ServiceTier" + }, + { + "type": "null" + } + ] + }, + { + "type": "null" + } + ], + "description": "Override the service tier for this turn and subsequent turns." + }, "summary": { "anyOf": [ { diff --git a/codex-rs/app-server-protocol/schema/json/EventMsg.json b/codex-rs/app-server-protocol/schema/json/EventMsg.json index 200135f25..4258bf7c2 100644 --- a/codex-rs/app-server-protocol/schema/json/EventMsg.json +++ b/codex-rs/app-server-protocol/schema/json/EventMsg.json @@ -1138,6 +1138,16 @@ ], "description": "How to sandbox commands executed in the system" }, + "service_tier": { + "anyOf": [ + { + "$ref": "#/definitions/ServiceTier" + }, + { + "type": "null" + } + ] + }, "session_id": { "$ref": "#/definitions/ThreadId" }, @@ -5410,6 +5420,12 @@ } ] }, + "ServiceTier": { + "enum": [ + "fast" + ], + "type": "string" + }, "SessionNetworkProxyRuntime": { "properties": { "admin_addr": { @@ -6754,6 +6770,16 @@ ], "description": "How to sandbox commands executed in the system" }, + "service_tier": { + "anyOf": [ + { + "$ref": "#/definitions/ServiceTier" + }, + { + "type": "null" + } + ] + }, "session_id": { "$ref": "#/definitions/ThreadId" }, diff --git a/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.schemas.json b/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.schemas.json index 694f8312f..4cf3933e7 100644 --- a/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.schemas.json +++ b/codex-rs/app-server-protocol/schema/json/codex_app_server_protocol.schemas.json @@ -2306,6 +2306,16 @@ ], "description": "How to sandbox commands executed in the system" }, + "service_tier": { + "anyOf": [ + { + "$ref": "#/definitions/v2/ServiceTier" + }, + { + "type": "null" + } + ] + }, "session_id": { "$ref": "#/definitions/v2/ThreadId" }, @@ -8595,6 +8605,16 @@ } ] }, + "service_tier": { + "anyOf": [ + { + "$ref": "#/definitions/v2/ServiceTier" + }, + { + "type": "null" + } + ] + }, "tools": { "anyOf": [ { @@ -10845,6 +10865,16 @@ } ] }, + "service_tier": { + "anyOf": [ + { + "$ref": "#/definitions/v2/ServiceTier" + }, + { + "type": "null" + } + ] + }, "web_search": { "anyOf": [ { @@ -11996,6 +12026,12 @@ "title": "ServerRequestResolvedNotification", "type": "object" }, + "ServiceTier": { + "enum": [ + "fast" + ], + "type": "string" + }, "SessionSource": { "oneOf": [ { @@ -12811,6 +12847,23 @@ } ] }, + "serviceTier": { + "anyOf": [ + { + "anyOf": [ + { + "$ref": "#/definitions/v2/ServiceTier" + }, + { + "type": "null" + } + ] + }, + { + "type": "null" + } + ] + }, "threadId": { "type": "string" } @@ -12849,6 +12902,16 @@ "sandbox": { "$ref": "#/definitions/v2/SandboxPolicy" }, + "serviceTier": { + "anyOf": [ + { + "$ref": "#/definitions/v2/ServiceTier" + }, + { + "type": "null" + } + ] + }, "thread": { "$ref": "#/definitions/v2/Thread" } @@ -13794,6 +13857,23 @@ } ] }, + "serviceTier": { + "anyOf": [ + { + "anyOf": [ + { + "$ref": "#/definitions/v2/ServiceTier" + }, + { + "type": "null" + } + ] + }, + { + "type": "null" + } + ] + }, "threadId": { "type": "string" } @@ -13832,6 +13912,16 @@ "sandbox": { "$ref": "#/definitions/v2/SandboxPolicy" }, + "serviceTier": { + "anyOf": [ + { + "$ref": "#/definitions/v2/ServiceTier" + }, + { + "type": "null" + } + ] + }, "thread": { "$ref": "#/definitions/v2/Thread" } @@ -14010,6 +14100,23 @@ "string", "null" ] + }, + "serviceTier": { + "anyOf": [ + { + "anyOf": [ + { + "$ref": "#/definitions/v2/ServiceTier" + }, + { + "type": "null" + } + ] + }, + { + "type": "null" + } + ] } }, "title": "ThreadStartParams", @@ -14043,6 +14150,16 @@ "sandbox": { "$ref": "#/definitions/v2/SandboxPolicy" }, + "serviceTier": { + "anyOf": [ + { + "$ref": "#/definitions/v2/ServiceTier" + }, + { + "type": "null" + } + ] + }, "thread": { "$ref": "#/definitions/v2/Thread" } @@ -14610,6 +14727,24 @@ ], "description": "Override the sandbox policy for this turn and subsequent turns." }, + "serviceTier": { + "anyOf": [ + { + "anyOf": [ + { + "$ref": "#/definitions/v2/ServiceTier" + }, + { + "type": "null" + } + ] + }, + { + "type": "null" + } + ], + "description": "Override the service tier for this turn and subsequent turns." + }, "summary": { "anyOf": [ { diff --git a/codex-rs/app-server-protocol/schema/json/v2/ConfigReadResponse.json b/codex-rs/app-server-protocol/schema/json/v2/ConfigReadResponse.json index 4511515d1..626db8dae 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ConfigReadResponse.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ConfigReadResponse.json @@ -323,6 +323,16 @@ } ] }, + "service_tier": { + "anyOf": [ + { + "$ref": "#/definitions/ServiceTier" + }, + { + "type": "null" + } + ] + }, "tools": { "anyOf": [ { @@ -608,6 +618,16 @@ } ] }, + "service_tier": { + "anyOf": [ + { + "$ref": "#/definitions/ServiceTier" + }, + { + "type": "null" + } + ] + }, "web_search": { "anyOf": [ { @@ -685,6 +705,12 @@ }, "type": "object" }, + "ServiceTier": { + "enum": [ + "fast" + ], + "type": "string" + }, "ToolsV2": { "properties": { "view_image": { diff --git a/codex-rs/app-server-protocol/schema/json/v2/ThreadForkParams.json b/codex-rs/app-server-protocol/schema/json/v2/ThreadForkParams.json index 2b832d5bf..ff0bf3f04 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ThreadForkParams.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ThreadForkParams.json @@ -50,6 +50,12 @@ "danger-full-access" ], "type": "string" + }, + "ServiceTier": { + "enum": [ + "fast" + ], + "type": "string" } }, "description": "There are two ways to fork a thread: 1. By thread_id: load the thread from disk by thread_id and fork it into a new thread. 2. By path: load the thread from disk by path and fork it into a new thread.\n\nIf using path, the thread_id param will be ignored.\n\nPrefer using thread_id whenever possible.", @@ -112,6 +118,23 @@ } ] }, + "serviceTier": { + "anyOf": [ + { + "anyOf": [ + { + "$ref": "#/definitions/ServiceTier" + }, + { + "type": "null" + } + ] + }, + { + "type": "null" + } + ] + }, "threadId": { "type": "string" } diff --git a/codex-rs/app-server-protocol/schema/json/v2/ThreadForkResponse.json b/codex-rs/app-server-protocol/schema/json/v2/ThreadForkResponse.json index fd3887650..3e4dca5b1 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ThreadForkResponse.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ThreadForkResponse.json @@ -738,6 +738,12 @@ } ] }, + "ServiceTier": { + "enum": [ + "fast" + ], + "type": "string" + }, "SessionSource": { "oneOf": [ { @@ -1906,6 +1912,16 @@ "sandbox": { "$ref": "#/definitions/SandboxPolicy" }, + "serviceTier": { + "anyOf": [ + { + "$ref": "#/definitions/ServiceTier" + }, + { + "type": "null" + } + ] + }, "thread": { "$ref": "#/definitions/Thread" } diff --git a/codex-rs/app-server-protocol/schema/json/v2/ThreadResumeParams.json b/codex-rs/app-server-protocol/schema/json/v2/ThreadResumeParams.json index ef7607d3c..353bda693 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ThreadResumeParams.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ThreadResumeParams.json @@ -738,6 +738,12 @@ ], "type": "string" }, + "ServiceTier": { + "enum": [ + "fast" + ], + "type": "string" + }, "WebSearchAction": { "oneOf": [ { @@ -910,6 +916,23 @@ } ] }, + "serviceTier": { + "anyOf": [ + { + "anyOf": [ + { + "$ref": "#/definitions/ServiceTier" + }, + { + "type": "null" + } + ] + }, + { + "type": "null" + } + ] + }, "threadId": { "type": "string" } diff --git a/codex-rs/app-server-protocol/schema/json/v2/ThreadResumeResponse.json b/codex-rs/app-server-protocol/schema/json/v2/ThreadResumeResponse.json index 56e268bb5..d9fbc0d02 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ThreadResumeResponse.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ThreadResumeResponse.json @@ -738,6 +738,12 @@ } ] }, + "ServiceTier": { + "enum": [ + "fast" + ], + "type": "string" + }, "SessionSource": { "oneOf": [ { @@ -1906,6 +1912,16 @@ "sandbox": { "$ref": "#/definitions/SandboxPolicy" }, + "serviceTier": { + "anyOf": [ + { + "$ref": "#/definitions/ServiceTier" + }, + { + "type": "null" + } + ] + }, "thread": { "$ref": "#/definitions/Thread" } diff --git a/codex-rs/app-server-protocol/schema/json/v2/ThreadStartParams.json b/codex-rs/app-server-protocol/schema/json/v2/ThreadStartParams.json index 5f2dda11a..311b7b40c 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ThreadStartParams.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ThreadStartParams.json @@ -75,6 +75,12 @@ "danger-full-access" ], "type": "string" + }, + "ServiceTier": { + "enum": [ + "fast" + ], + "type": "string" } }, "properties": { @@ -156,6 +162,23 @@ "string", "null" ] + }, + "serviceTier": { + "anyOf": [ + { + "anyOf": [ + { + "$ref": "#/definitions/ServiceTier" + }, + { + "type": "null" + } + ] + }, + { + "type": "null" + } + ] } }, "title": "ThreadStartParams", diff --git a/codex-rs/app-server-protocol/schema/json/v2/ThreadStartResponse.json b/codex-rs/app-server-protocol/schema/json/v2/ThreadStartResponse.json index 043354b68..ee8b38499 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/ThreadStartResponse.json +++ b/codex-rs/app-server-protocol/schema/json/v2/ThreadStartResponse.json @@ -738,6 +738,12 @@ } ] }, + "ServiceTier": { + "enum": [ + "fast" + ], + "type": "string" + }, "SessionSource": { "oneOf": [ { @@ -1906,6 +1912,16 @@ "sandbox": { "$ref": "#/definitions/SandboxPolicy" }, + "serviceTier": { + "anyOf": [ + { + "$ref": "#/definitions/ServiceTier" + }, + { + "type": "null" + } + ] + }, "thread": { "$ref": "#/definitions/Thread" } diff --git a/codex-rs/app-server-protocol/schema/json/v2/TurnStartParams.json b/codex-rs/app-server-protocol/schema/json/v2/TurnStartParams.json index bc15473cc..0a769c352 100644 --- a/codex-rs/app-server-protocol/schema/json/v2/TurnStartParams.json +++ b/codex-rs/app-server-protocol/schema/json/v2/TurnStartParams.json @@ -299,6 +299,12 @@ } ] }, + "ServiceTier": { + "enum": [ + "fast" + ], + "type": "string" + }, "Settings": { "description": "Settings for a collaboration mode.", "properties": { @@ -539,6 +545,24 @@ ], "description": "Override the sandbox policy for this turn and subsequent turns." }, + "serviceTier": { + "anyOf": [ + { + "anyOf": [ + { + "$ref": "#/definitions/ServiceTier" + }, + { + "type": "null" + } + ] + }, + { + "type": "null" + } + ], + "description": "Override the service tier for this turn and subsequent turns." + }, "summary": { "anyOf": [ { diff --git a/codex-rs/app-server-protocol/schema/typescript/SendUserTurnParams.ts b/codex-rs/app-server-protocol/schema/typescript/SendUserTurnParams.ts index dc4cfba8f..063191649 100644 --- a/codex-rs/app-server-protocol/schema/typescript/SendUserTurnParams.ts +++ b/codex-rs/app-server-protocol/schema/typescript/SendUserTurnParams.ts @@ -6,10 +6,11 @@ import type { InputItem } from "./InputItem"; import type { ReasoningEffort } from "./ReasoningEffort"; import type { ReasoningSummary } from "./ReasoningSummary"; import type { SandboxPolicy } from "./SandboxPolicy"; +import type { ServiceTier } from "./ServiceTier"; import type { ThreadId } from "./ThreadId"; import type { JsonValue } from "./serde_json/JsonValue"; -export type SendUserTurnParams = { conversationId: ThreadId, items: Array, cwd: string, approvalPolicy: AskForApproval, sandboxPolicy: SandboxPolicy, model: string, effort: ReasoningEffort | null, summary: ReasoningSummary, +export type SendUserTurnParams = { conversationId: ThreadId, items: Array, cwd: string, approvalPolicy: AskForApproval, sandboxPolicy: SandboxPolicy, model: string, serviceTier?: ServiceTier | null | null, effort: ReasoningEffort | null, summary: ReasoningSummary, /** * Optional JSON Schema used to constrain the final assistant message for this turn. */ diff --git a/codex-rs/app-server-protocol/schema/typescript/ServiceTier.ts b/codex-rs/app-server-protocol/schema/typescript/ServiceTier.ts new file mode 100644 index 000000000..bb2b5103e --- /dev/null +++ b/codex-rs/app-server-protocol/schema/typescript/ServiceTier.ts @@ -0,0 +1,5 @@ +// GENERATED CODE! DO NOT MODIFY BY HAND! + +// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. + +export type ServiceTier = "fast"; diff --git a/codex-rs/app-server-protocol/schema/typescript/SessionConfiguredEvent.ts b/codex-rs/app-server-protocol/schema/typescript/SessionConfiguredEvent.ts index d964f4445..b4696a0e4 100644 --- a/codex-rs/app-server-protocol/schema/typescript/SessionConfiguredEvent.ts +++ b/codex-rs/app-server-protocol/schema/typescript/SessionConfiguredEvent.ts @@ -5,6 +5,7 @@ import type { AskForApproval } from "./AskForApproval"; import type { EventMsg } from "./EventMsg"; import type { ReasoningEffort } from "./ReasoningEffort"; import type { SandboxPolicy } from "./SandboxPolicy"; +import type { ServiceTier } from "./ServiceTier"; import type { SessionNetworkProxyRuntime } from "./SessionNetworkProxyRuntime"; import type { ThreadId } from "./ThreadId"; @@ -16,7 +17,7 @@ thread_name?: string, /** * Tell the client what model is being queried. */ -model: string, model_provider_id: string, +model: string, model_provider_id: string, service_tier: ServiceTier | null, /** * When to escalate for approval for execution */ diff --git a/codex-rs/app-server-protocol/schema/typescript/SessionConfiguredNotification.ts b/codex-rs/app-server-protocol/schema/typescript/SessionConfiguredNotification.ts index 3dee74aa3..0fd5edcb3 100644 --- a/codex-rs/app-server-protocol/schema/typescript/SessionConfiguredNotification.ts +++ b/codex-rs/app-server-protocol/schema/typescript/SessionConfiguredNotification.ts @@ -3,6 +3,7 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. import type { EventMsg } from "./EventMsg"; import type { ReasoningEffort } from "./ReasoningEffort"; +import type { ServiceTier } from "./ServiceTier"; import type { ThreadId } from "./ThreadId"; -export type SessionConfiguredNotification = { sessionId: ThreadId, model: string, reasoningEffort: ReasoningEffort | null, historyLogId: bigint, historyEntryCount: number, initialMessages: Array | null, rolloutPath: string, }; +export type SessionConfiguredNotification = { sessionId: ThreadId, model: string, serviceTier: ServiceTier | null, reasoningEffort: ReasoningEffort | null, historyLogId: bigint, historyEntryCount: number, initialMessages: Array | null, rolloutPath: string, }; diff --git a/codex-rs/app-server-protocol/schema/typescript/index.ts b/codex-rs/app-server-protocol/schema/typescript/index.ts index c72f24999..81e08b159 100644 --- a/codex-rs/app-server-protocol/schema/typescript/index.ts +++ b/codex-rs/app-server-protocol/schema/typescript/index.ts @@ -200,6 +200,7 @@ export type { SendUserTurnParams } from "./SendUserTurnParams"; export type { SendUserTurnResponse } from "./SendUserTurnResponse"; export type { ServerNotification } from "./ServerNotification"; export type { ServerRequest } from "./ServerRequest"; +export type { ServiceTier } from "./ServiceTier"; export type { SessionConfiguredEvent } from "./SessionConfiguredEvent"; export type { SessionConfiguredNotification } from "./SessionConfiguredNotification"; export type { SessionNetworkProxyRuntime } from "./SessionNetworkProxyRuntime"; diff --git a/codex-rs/app-server-protocol/schema/typescript/v2/Config.ts b/codex-rs/app-server-protocol/schema/typescript/v2/Config.ts index aee0ac338..fb5d6ecbb 100644 --- a/codex-rs/app-server-protocol/schema/typescript/v2/Config.ts +++ b/codex-rs/app-server-protocol/schema/typescript/v2/Config.ts @@ -4,6 +4,7 @@ import type { ForcedLoginMethod } from "../ForcedLoginMethod"; import type { ReasoningEffort } from "../ReasoningEffort"; import type { ReasoningSummary } from "../ReasoningSummary"; +import type { ServiceTier } from "../ServiceTier"; import type { Verbosity } from "../Verbosity"; import type { WebSearchMode } from "../WebSearchMode"; import type { JsonValue } from "../serde_json/JsonValue"; @@ -14,4 +15,4 @@ import type { SandboxMode } from "./SandboxMode"; import type { SandboxWorkspaceWrite } from "./SandboxWorkspaceWrite"; import type { ToolsV2 } from "./ToolsV2"; -export type Config = {model: string | null, review_model: string | null, model_context_window: bigint | null, model_auto_compact_token_limit: bigint | null, model_provider: string | null, approval_policy: AskForApproval | null, sandbox_mode: SandboxMode | null, sandbox_workspace_write: SandboxWorkspaceWrite | null, forced_chatgpt_workspace_id: string | null, forced_login_method: ForcedLoginMethod | null, web_search: WebSearchMode | null, tools: ToolsV2 | null, profile: string | null, profiles: { [key in string]?: ProfileV2 }, instructions: string | null, developer_instructions: string | null, compact_prompt: string | null, model_reasoning_effort: ReasoningEffort | null, model_reasoning_summary: ReasoningSummary | null, model_verbosity: Verbosity | null, analytics: AnalyticsConfig | null} & ({ [key in string]?: number | string | boolean | Array | { [key in string]?: JsonValue } | null }); +export type Config = {model: string | null, review_model: string | null, model_context_window: bigint | null, model_auto_compact_token_limit: bigint | null, model_provider: string | null, approval_policy: AskForApproval | null, sandbox_mode: SandboxMode | null, sandbox_workspace_write: SandboxWorkspaceWrite | null, forced_chatgpt_workspace_id: string | null, forced_login_method: ForcedLoginMethod | null, web_search: WebSearchMode | null, tools: ToolsV2 | null, profile: string | null, profiles: { [key in string]?: ProfileV2 }, instructions: string | null, developer_instructions: string | null, compact_prompt: string | null, model_reasoning_effort: ReasoningEffort | null, model_reasoning_summary: ReasoningSummary | null, model_verbosity: Verbosity | null, service_tier: ServiceTier | null, analytics: AnalyticsConfig | null} & ({ [key in string]?: number | string | boolean | Array | { [key in string]?: JsonValue } | null }); diff --git a/codex-rs/app-server-protocol/schema/typescript/v2/ProfileV2.ts b/codex-rs/app-server-protocol/schema/typescript/v2/ProfileV2.ts index 56428ba7a..81d20993c 100644 --- a/codex-rs/app-server-protocol/schema/typescript/v2/ProfileV2.ts +++ b/codex-rs/app-server-protocol/schema/typescript/v2/ProfileV2.ts @@ -3,9 +3,10 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. import type { ReasoningEffort } from "../ReasoningEffort"; import type { ReasoningSummary } from "../ReasoningSummary"; +import type { ServiceTier } from "../ServiceTier"; import type { Verbosity } from "../Verbosity"; import type { WebSearchMode } from "../WebSearchMode"; import type { JsonValue } from "../serde_json/JsonValue"; import type { AskForApproval } from "./AskForApproval"; -export type ProfileV2 = { model: string | null, model_provider: string | null, approval_policy: AskForApproval | null, model_reasoning_effort: ReasoningEffort | null, model_reasoning_summary: ReasoningSummary | null, model_verbosity: Verbosity | null, web_search: WebSearchMode | null, chatgpt_base_url: string | null, } & ({ [key in string]?: number | string | boolean | Array | { [key in string]?: JsonValue } | null }); +export type ProfileV2 = { model: string | null, model_provider: string | null, approval_policy: AskForApproval | null, service_tier: ServiceTier | null, model_reasoning_effort: ReasoningEffort | null, model_reasoning_summary: ReasoningSummary | null, model_verbosity: Verbosity | null, web_search: WebSearchMode | null, chatgpt_base_url: string | null, } & ({ [key in string]?: number | string | boolean | Array | { [key in string]?: JsonValue } | null }); diff --git a/codex-rs/app-server-protocol/schema/typescript/v2/ThreadForkParams.ts b/codex-rs/app-server-protocol/schema/typescript/v2/ThreadForkParams.ts index 742e4d703..b071bc852 100644 --- a/codex-rs/app-server-protocol/schema/typescript/v2/ThreadForkParams.ts +++ b/codex-rs/app-server-protocol/schema/typescript/v2/ThreadForkParams.ts @@ -1,6 +1,7 @@ // GENERATED CODE! DO NOT MODIFY BY HAND! // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. +import type { ServiceTier } from "../ServiceTier"; import type { JsonValue } from "../serde_json/JsonValue"; import type { AskForApproval } from "./AskForApproval"; import type { SandboxMode } from "./SandboxMode"; @@ -21,7 +22,7 @@ export type ThreadForkParams = {threadId: string, /** path?: string | null, /** * Configuration overrides for the forked thread, if any. */ -model?: string | null, modelProvider?: string | null, cwd?: string | null, approvalPolicy?: AskForApproval | null, sandbox?: SandboxMode | null, config?: { [key in string]?: JsonValue } | null, baseInstructions?: string | null, developerInstructions?: string | null, /** +model?: string | null, modelProvider?: string | null, serviceTier?: ServiceTier | null | null, cwd?: string | null, approvalPolicy?: AskForApproval | null, sandbox?: SandboxMode | null, config?: { [key in string]?: JsonValue } | null, baseInstructions?: string | null, developerInstructions?: string | null, /** * If true, persist additional rollout EventMsg variants required to * reconstruct a richer thread history on subsequent resume/fork/read. */ diff --git a/codex-rs/app-server-protocol/schema/typescript/v2/ThreadForkResponse.ts b/codex-rs/app-server-protocol/schema/typescript/v2/ThreadForkResponse.ts index a46480cb7..6125448c0 100644 --- a/codex-rs/app-server-protocol/schema/typescript/v2/ThreadForkResponse.ts +++ b/codex-rs/app-server-protocol/schema/typescript/v2/ThreadForkResponse.ts @@ -2,8 +2,9 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. import type { ReasoningEffort } from "../ReasoningEffort"; +import type { ServiceTier } from "../ServiceTier"; import type { AskForApproval } from "./AskForApproval"; import type { SandboxPolicy } from "./SandboxPolicy"; import type { Thread } from "./Thread"; -export type ThreadForkResponse = { thread: Thread, model: string, modelProvider: string, cwd: string, approvalPolicy: AskForApproval, sandbox: SandboxPolicy, reasoningEffort: ReasoningEffort | null, }; +export type ThreadForkResponse = { thread: Thread, model: string, modelProvider: string, serviceTier: ServiceTier | null, cwd: string, approvalPolicy: AskForApproval, sandbox: SandboxPolicy, reasoningEffort: ReasoningEffort | null, }; diff --git a/codex-rs/app-server-protocol/schema/typescript/v2/ThreadResumeParams.ts b/codex-rs/app-server-protocol/schema/typescript/v2/ThreadResumeParams.ts index e79c748cc..cc12020bd 100644 --- a/codex-rs/app-server-protocol/schema/typescript/v2/ThreadResumeParams.ts +++ b/codex-rs/app-server-protocol/schema/typescript/v2/ThreadResumeParams.ts @@ -3,6 +3,7 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. import type { Personality } from "../Personality"; import type { ResponseItem } from "../ResponseItem"; +import type { ServiceTier } from "../ServiceTier"; import type { JsonValue } from "../serde_json/JsonValue"; import type { AskForApproval } from "./AskForApproval"; import type { SandboxMode } from "./SandboxMode"; @@ -30,7 +31,7 @@ history?: Array | null, /** path?: string | null, /** * Configuration overrides for the resumed thread, if any. */ -model?: string | null, modelProvider?: string | null, cwd?: string | null, approvalPolicy?: AskForApproval | null, sandbox?: SandboxMode | null, config?: { [key in string]?: JsonValue } | null, baseInstructions?: string | null, developerInstructions?: string | null, personality?: Personality | null, /** +model?: string | null, modelProvider?: string | null, serviceTier?: ServiceTier | null | null, cwd?: string | null, approvalPolicy?: AskForApproval | null, sandbox?: SandboxMode | null, config?: { [key in string]?: JsonValue } | null, baseInstructions?: string | null, developerInstructions?: string | null, personality?: Personality | null, /** * If true, persist additional rollout EventMsg variants required to * reconstruct a richer thread history on subsequent resume/fork/read. */ diff --git a/codex-rs/app-server-protocol/schema/typescript/v2/ThreadResumeResponse.ts b/codex-rs/app-server-protocol/schema/typescript/v2/ThreadResumeResponse.ts index 6d7a70a6a..91ca82419 100644 --- a/codex-rs/app-server-protocol/schema/typescript/v2/ThreadResumeResponse.ts +++ b/codex-rs/app-server-protocol/schema/typescript/v2/ThreadResumeResponse.ts @@ -2,8 +2,9 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. import type { ReasoningEffort } from "../ReasoningEffort"; +import type { ServiceTier } from "../ServiceTier"; import type { AskForApproval } from "./AskForApproval"; import type { SandboxPolicy } from "./SandboxPolicy"; import type { Thread } from "./Thread"; -export type ThreadResumeResponse = { thread: Thread, model: string, modelProvider: string, cwd: string, approvalPolicy: AskForApproval, sandbox: SandboxPolicy, reasoningEffort: ReasoningEffort | null, }; +export type ThreadResumeResponse = { thread: Thread, model: string, modelProvider: string, serviceTier: ServiceTier | null, cwd: string, approvalPolicy: AskForApproval, sandbox: SandboxPolicy, reasoningEffort: ReasoningEffort | null, }; diff --git a/codex-rs/app-server-protocol/schema/typescript/v2/ThreadStartParams.ts b/codex-rs/app-server-protocol/schema/typescript/v2/ThreadStartParams.ts index 5f34f6198..db73763e4 100644 --- a/codex-rs/app-server-protocol/schema/typescript/v2/ThreadStartParams.ts +++ b/codex-rs/app-server-protocol/schema/typescript/v2/ThreadStartParams.ts @@ -2,11 +2,12 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. import type { Personality } from "../Personality"; +import type { ServiceTier } from "../ServiceTier"; import type { JsonValue } from "../serde_json/JsonValue"; import type { AskForApproval } from "./AskForApproval"; import type { SandboxMode } from "./SandboxMode"; -export type ThreadStartParams = {model?: string | null, modelProvider?: string | null, cwd?: string | null, approvalPolicy?: AskForApproval | null, sandbox?: SandboxMode | null, config?: { [key in string]?: JsonValue } | null, serviceName?: string | null, baseInstructions?: string | null, developerInstructions?: string | null, personality?: Personality | null, ephemeral?: boolean | null, /** +export type ThreadStartParams = {model?: string | null, modelProvider?: string | null, serviceTier?: ServiceTier | null | null, cwd?: string | null, approvalPolicy?: AskForApproval | null, sandbox?: SandboxMode | null, config?: { [key in string]?: JsonValue } | null, serviceName?: string | null, baseInstructions?: string | null, developerInstructions?: string | null, personality?: Personality | null, ephemeral?: boolean | null, /** * If true, opt into emitting raw Responses API items on the event stream. * This is for internal use only (e.g. Codex Cloud). */ diff --git a/codex-rs/app-server-protocol/schema/typescript/v2/ThreadStartResponse.ts b/codex-rs/app-server-protocol/schema/typescript/v2/ThreadStartResponse.ts index 4a76f9af2..f4882ce6f 100644 --- a/codex-rs/app-server-protocol/schema/typescript/v2/ThreadStartResponse.ts +++ b/codex-rs/app-server-protocol/schema/typescript/v2/ThreadStartResponse.ts @@ -2,8 +2,9 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. import type { ReasoningEffort } from "../ReasoningEffort"; +import type { ServiceTier } from "../ServiceTier"; import type { AskForApproval } from "./AskForApproval"; import type { SandboxPolicy } from "./SandboxPolicy"; import type { Thread } from "./Thread"; -export type ThreadStartResponse = { thread: Thread, model: string, modelProvider: string, cwd: string, approvalPolicy: AskForApproval, sandbox: SandboxPolicy, reasoningEffort: ReasoningEffort | null, }; +export type ThreadStartResponse = { thread: Thread, model: string, modelProvider: string, serviceTier: ServiceTier | null, cwd: string, approvalPolicy: AskForApproval, sandbox: SandboxPolicy, reasoningEffort: ReasoningEffort | null, }; diff --git a/codex-rs/app-server-protocol/schema/typescript/v2/TurnStartParams.ts b/codex-rs/app-server-protocol/schema/typescript/v2/TurnStartParams.ts index d595035dd..940aaa700 100644 --- a/codex-rs/app-server-protocol/schema/typescript/v2/TurnStartParams.ts +++ b/codex-rs/app-server-protocol/schema/typescript/v2/TurnStartParams.ts @@ -5,6 +5,7 @@ import type { CollaborationMode } from "../CollaborationMode"; import type { Personality } from "../Personality"; import type { ReasoningEffort } from "../ReasoningEffort"; import type { ReasoningSummary } from "../ReasoningSummary"; +import type { ServiceTier } from "../ServiceTier"; import type { JsonValue } from "../serde_json/JsonValue"; import type { AskForApproval } from "./AskForApproval"; import type { SandboxPolicy } from "./SandboxPolicy"; @@ -23,6 +24,9 @@ sandboxPolicy?: SandboxPolicy | null, /** * Override the model for this turn and subsequent turns. */ model?: string | null, /** + * Override the service tier for this turn and subsequent turns. + */ +serviceTier?: ServiceTier | null | null, /** * Override the reasoning effort for this turn and subsequent turns. */ effort?: ReasoningEffort | null, /** diff --git a/codex-rs/app-server-protocol/src/protocol/mod.rs b/codex-rs/app-server-protocol/src/protocol/mod.rs index e26933243..1e0410f4b 100644 --- a/codex-rs/app-server-protocol/src/protocol/mod.rs +++ b/codex-rs/app-server-protocol/src/protocol/mod.rs @@ -3,6 +3,7 @@ pub mod common; mod mappers; +mod serde_helpers; pub mod thread_history; pub mod v1; pub mod v2; diff --git a/codex-rs/app-server-protocol/src/protocol/serde_helpers.rs b/codex-rs/app-server-protocol/src/protocol/serde_helpers.rs new file mode 100644 index 000000000..0e35ebdba --- /dev/null +++ b/codex-rs/app-server-protocol/src/protocol/serde_helpers.rs @@ -0,0 +1,23 @@ +use serde::Deserialize; +use serde::Deserializer; +use serde::Serialize; +use serde::Serializer; + +pub fn deserialize_double_option<'de, T, D>(deserializer: D) -> Result>, D::Error> +where + T: Deserialize<'de>, + D: Deserializer<'de>, +{ + serde_with::rust::double_option::deserialize(deserializer) +} + +pub fn serialize_double_option( + value: &Option>, + serializer: S, +) -> Result +where + T: Serialize, + S: Serializer, +{ + serde_with::rust::double_option::serialize(value, serializer) +} diff --git a/codex-rs/app-server-protocol/src/protocol/v1.rs b/codex-rs/app-server-protocol/src/protocol/v1.rs index 719268e9c..ed1ca8622 100644 --- a/codex-rs/app-server-protocol/src/protocol/v1.rs +++ b/codex-rs/app-server-protocol/src/protocol/v1.rs @@ -5,6 +5,7 @@ use codex_protocol::ThreadId; use codex_protocol::config_types::ForcedLoginMethod; use codex_protocol::config_types::ReasoningSummary; use codex_protocol::config_types::SandboxMode; +use codex_protocol::config_types::ServiceTier; use codex_protocol::config_types::Verbosity; use codex_protocol::models::ResponseItem; use codex_protocol::openai_models::ReasoningEffort; @@ -419,6 +420,13 @@ pub struct SendUserTurnParams { pub approval_policy: AskForApproval, pub sandbox_policy: SandboxPolicy, pub model: String, + #[serde( + default, + deserialize_with = "super::serde_helpers::deserialize_double_option", + serialize_with = "super::serde_helpers::serialize_double_option", + skip_serializing_if = "Option::is_none" + )] + pub service_tier: Option>, pub effort: Option, pub summary: ReasoningSummary, /// Optional JSON Schema used to constrain the final assistant message for this turn. @@ -429,6 +437,55 @@ pub struct SendUserTurnParams { #[serde(rename_all = "camelCase")] pub struct SendUserTurnResponse {} +#[cfg(test)] +mod tests { + use super::*; + use pretty_assertions::assert_eq; + use std::path::PathBuf; + + #[test] + fn send_user_turn_params_preserve_explicit_null_service_tier() { + let params = SendUserTurnParams { + conversation_id: ThreadId::new(), + items: vec![], + cwd: PathBuf::from("/tmp"), + approval_policy: AskForApproval::Never, + sandbox_policy: SandboxPolicy::DangerFullAccess, + model: "gpt-4.1".to_string(), + service_tier: Some(None), + effort: None, + summary: ReasoningSummary::Auto, + output_schema: None, + }; + + let serialized = serde_json::to_value(¶ms).expect("params should serialize"); + assert_eq!( + serialized.get("serviceTier"), + Some(&serde_json::Value::Null) + ); + + let roundtrip: SendUserTurnParams = + serde_json::from_value(serialized).expect("params should deserialize"); + assert_eq!(roundtrip.service_tier, Some(None)); + + let without_override = SendUserTurnParams { + conversation_id: ThreadId::new(), + items: vec![], + cwd: PathBuf::from("/tmp"), + approval_policy: AskForApproval::Never, + sandbox_policy: SandboxPolicy::DangerFullAccess, + model: "gpt-4.1".to_string(), + service_tier: None, + effort: None, + summary: ReasoningSummary::Auto, + output_schema: None, + }; + let serialized_without_override = + serde_json::to_value(&without_override).expect("params should serialize"); + assert_eq!(serialized_without_override.get("serviceTier"), None); + } +} + #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)] #[serde(rename_all = "camelCase")] pub struct InterruptConversationParams { @@ -555,6 +612,7 @@ pub struct LoginChatGptCompleteNotification { pub struct SessionConfiguredNotification { pub session_id: ThreadId, pub model: String, + pub service_tier: Option, pub reasoning_effort: Option, pub history_log_id: u64, #[ts(type = "number")] diff --git a/codex-rs/app-server-protocol/src/protocol/v2.rs b/codex-rs/app-server-protocol/src/protocol/v2.rs index bde0e17fa..8f0b37bee 100644 --- a/codex-rs/app-server-protocol/src/protocol/v2.rs +++ b/codex-rs/app-server-protocol/src/protocol/v2.rs @@ -17,6 +17,7 @@ use codex_protocol::config_types::ModeKind; use codex_protocol::config_types::Personality; use codex_protocol::config_types::ReasoningSummary; use codex_protocol::config_types::SandboxMode as CoreSandboxMode; +use codex_protocol::config_types::ServiceTier; use codex_protocol::config_types::Verbosity; use codex_protocol::config_types::WebSearchMode; use codex_protocol::items::AgentMessageContent as CoreAgentMessageContent; @@ -392,6 +393,7 @@ pub struct ProfileV2 { pub model: Option, pub model_provider: Option, pub approval_policy: Option, + pub service_tier: Option, pub model_reasoning_effort: Option, pub model_reasoning_summary: Option, pub model_verbosity: Option, @@ -503,6 +505,7 @@ pub struct Config { pub model_reasoning_effort: Option, pub model_reasoning_summary: Option, pub model_verbosity: Option, + pub service_tier: Option, pub analytics: Option, #[experimental("config/read.apps")] #[serde(default)] @@ -1788,6 +1791,14 @@ pub struct ThreadStartParams { pub model: Option, #[ts(optional = nullable)] pub model_provider: Option, + #[serde( + default, + deserialize_with = "super::serde_helpers::deserialize_double_option", + serialize_with = "super::serde_helpers::serialize_double_option", + skip_serializing_if = "Option::is_none" + )] + #[ts(optional = nullable)] + pub service_tier: Option>, #[ts(optional = nullable)] pub cwd: Option, #[ts(optional = nullable)] @@ -1850,6 +1861,7 @@ pub struct ThreadStartResponse { pub thread: Thread, pub model: String, pub model_provider: String, + pub service_tier: Option, pub cwd: PathBuf, pub approval_policy: AskForApproval, pub sandbox: SandboxPolicy, @@ -1891,6 +1903,14 @@ pub struct ThreadResumeParams { pub model: Option, #[ts(optional = nullable)] pub model_provider: Option, + #[serde( + default, + deserialize_with = "super::serde_helpers::deserialize_double_option", + serialize_with = "super::serde_helpers::serialize_double_option", + skip_serializing_if = "Option::is_none" + )] + #[ts(optional = nullable)] + pub service_tier: Option>, #[ts(optional = nullable)] pub cwd: Option, #[ts(optional = nullable)] @@ -1919,6 +1939,7 @@ pub struct ThreadResumeResponse { pub thread: Thread, pub model: String, pub model_provider: String, + pub service_tier: Option, pub cwd: PathBuf, pub approval_policy: AskForApproval, pub sandbox: SandboxPolicy, @@ -1951,6 +1972,14 @@ pub struct ThreadForkParams { pub model: Option, #[ts(optional = nullable)] pub model_provider: Option, + #[serde( + default, + deserialize_with = "super::serde_helpers::deserialize_double_option", + serialize_with = "super::serde_helpers::serialize_double_option", + skip_serializing_if = "Option::is_none" + )] + #[ts(optional = nullable)] + pub service_tier: Option>, #[ts(optional = nullable)] pub cwd: Option, #[ts(optional = nullable)] @@ -1977,6 +2006,7 @@ pub struct ThreadForkResponse { pub thread: Thread, pub model: String, pub model_provider: String, + pub service_tier: Option, pub cwd: PathBuf, pub approval_policy: AskForApproval, pub sandbox: SandboxPolicy, @@ -2837,6 +2867,15 @@ pub struct TurnStartParams { /// Override the model for this turn and subsequent turns. #[ts(optional = nullable)] pub model: Option, + /// Override the service tier for this turn and subsequent turns. + #[serde( + default, + deserialize_with = "super::serde_helpers::deserialize_double_option", + serialize_with = "super::serde_helpers::serialize_double_option", + skip_serializing_if = "Option::is_none" + )] + #[ts(optional = nullable)] + pub service_tier: Option>, /// Override the reasoning effort for this turn and subsequent turns. #[ts(optional = nullable)] pub effort: Option, @@ -4566,4 +4605,56 @@ mod tests { }) ); } + + #[test] + fn thread_start_params_preserve_explicit_null_service_tier() { + let params: ThreadStartParams = serde_json::from_value(json!({ "serviceTier": null })) + .expect("params should deserialize"); + assert_eq!(params.service_tier, Some(None)); + + let serialized = serde_json::to_value(¶ms).expect("params should serialize"); + assert_eq!( + serialized.get("serviceTier"), + Some(&serde_json::Value::Null) + ); + + let serialized_without_override = + serde_json::to_value(ThreadStartParams::default()).expect("params should serialize"); + assert_eq!(serialized_without_override.get("serviceTier"), None); + } + + #[test] + fn turn_start_params_preserve_explicit_null_service_tier() { + let params: TurnStartParams = serde_json::from_value(json!({ + "threadId": "thread_123", + "input": [], + "serviceTier": null + })) + .expect("params should deserialize"); + assert_eq!(params.service_tier, Some(None)); + + let serialized = serde_json::to_value(¶ms).expect("params should serialize"); + assert_eq!( + serialized.get("serviceTier"), + Some(&serde_json::Value::Null) + ); + + let without_override = TurnStartParams { + thread_id: "thread_123".to_string(), + input: vec![], + cwd: None, + approval_policy: None, + sandbox_policy: None, + model: None, + service_tier: None, + effort: None, + summary: None, + output_schema: None, + collaboration_mode: None, + personality: None, + }; + let serialized_without_override = + serde_json::to_value(&without_override).expect("params should serialize"); + assert_eq!(serialized_without_override.get("serviceTier"), None); + } } diff --git a/codex-rs/app-server/src/codex_message_processor.rs b/codex-rs/app-server/src/codex_message_processor.rs index 60db6b786..3c1c29eca 100644 --- a/codex-rs/app-server/src/codex_message_processor.rs +++ b/codex-rs/app-server/src/codex_message_processor.rs @@ -2042,6 +2042,7 @@ impl CodexMessageProcessor { let ThreadStartParams { model, model_provider, + service_tier, cwd, approval_policy, sandbox, @@ -2059,6 +2060,7 @@ impl CodexMessageProcessor { let mut typesafe_overrides = self.build_thread_config_overrides( model, model_provider, + service_tier, cwd, approval_policy, sandbox, @@ -2213,6 +2215,7 @@ impl CodexMessageProcessor { thread: thread.clone(), model: config_snapshot.model, model_provider: config_snapshot.model_provider_id, + service_tier: config_snapshot.service_tier, cwd: config_snapshot.cwd, approval_policy: config_snapshot.approval_policy.into(), sandbox: config_snapshot.sandbox_policy.into(), @@ -2249,6 +2252,7 @@ impl CodexMessageProcessor { &self, model: Option, model_provider: Option, + service_tier: Option>, cwd: Option, approval_policy: Option, sandbox: Option, @@ -2259,6 +2263,7 @@ impl CodexMessageProcessor { ConfigOverrides { model, model_provider, + service_tier, cwd: cwd.map(PathBuf::from), approval_policy: approval_policy .map(codex_app_server_protocol::AskForApproval::to_core), @@ -3060,6 +3065,7 @@ impl CodexMessageProcessor { path, model, model_provider, + service_tier, cwd, approval_policy, sandbox, @@ -3092,6 +3098,7 @@ impl CodexMessageProcessor { let typesafe_overrides = self.build_thread_config_overrides( model, model_provider, + service_tier, cwd, approval_policy, sandbox, @@ -3190,6 +3197,7 @@ impl CodexMessageProcessor { thread, model: session_configured.model, model_provider: session_configured.model_provider_id, + service_tier: session_configured.service_tier, cwd: session_configured.cwd, approval_policy: session_configured.approval_policy.into(), sandbox: session_configured.sandbox_policy.into(), @@ -3497,6 +3505,7 @@ impl CodexMessageProcessor { path, model, model_provider, + service_tier, cwd, approval_policy, sandbox, @@ -3577,6 +3586,7 @@ impl CodexMessageProcessor { let typesafe_overrides = self.build_thread_config_overrides( model, model_provider, + service_tier, cwd, approval_policy, sandbox, @@ -3718,6 +3728,7 @@ impl CodexMessageProcessor { thread: thread.clone(), model: session_configured.model, model_provider: session_configured.model_provider_id, + service_tier: session_configured.service_tier, cwd: session_configured.cwd, approval_policy: session_configured.approval_policy.into(), sandbox: session_configured.sandbox_policy.into(), @@ -4626,6 +4637,7 @@ impl CodexMessageProcessor { SessionConfiguredNotification { session_id: session_configured.session_id, model: session_configured.model.clone(), + service_tier: session_configured.service_tier, reasoning_effort: session_configured.reasoning_effort, history_log_id: session_configured.history_log_id, history_entry_count: session_configured.history_entry_count, @@ -4847,6 +4859,7 @@ impl CodexMessageProcessor { SessionConfiguredNotification { session_id: session_configured.session_id, model: session_configured.model.clone(), + service_tier: session_configured.service_tier, reasoning_effort: session_configured.reasoning_effort, history_log_id: session_configured.history_log_id, history_entry_count: session_configured.history_entry_count, @@ -5274,6 +5287,7 @@ impl CodexMessageProcessor { approval_policy, sandbox_policy, model, + service_tier, effort, summary, output_schema, @@ -5323,7 +5337,7 @@ impl CodexMessageProcessor { model, effort, summary: Some(summary), - service_tier: None, + service_tier, final_output_json_schema: output_schema, collaboration_mode: None, personality: None, @@ -5865,6 +5879,7 @@ impl CodexMessageProcessor { || params.approval_policy.is_some() || params.sandbox_policy.is_some() || params.model.is_some() + || params.service_tier.is_some() || params.effort.is_some() || params.summary.is_some() || collaboration_mode.is_some() @@ -5881,7 +5896,7 @@ impl CodexMessageProcessor { model: params.model, effort: params.effort.map(Some), summary: params.summary, - service_tier: None, + service_tier: params.service_tier, collaboration_mode, personality: params.personality, }) @@ -7153,6 +7168,7 @@ async fn handle_pending_thread_resume_request( let ThreadConfigSnapshot { model, model_provider_id, + service_tier, approval_policy, sandbox_policy, cwd, @@ -7163,6 +7179,7 @@ async fn handle_pending_thread_resume_request( thread, model, model_provider: model_provider_id, + service_tier, cwd, approval_policy: approval_policy.into(), sandbox: sandbox_policy.into(), diff --git a/codex-rs/app-server/tests/suite/codex_message_processor_flow.rs b/codex-rs/app-server/tests/suite/codex_message_processor_flow.rs index 9f653ab9c..b5176886b 100644 --- a/codex-rs/app-server/tests/suite/codex_message_processor_flow.rs +++ b/codex-rs/app-server/tests/suite/codex_message_processor_flow.rs @@ -337,6 +337,7 @@ async fn test_send_user_turn_changes_approval_policy_behavior() -> Result<()> { model: "mock-model".to_string(), effort: Some(ReasoningEffort::Medium), summary: ReasoningSummary::Auto, + service_tier: None, output_schema: None, }) .await?; @@ -453,6 +454,7 @@ async fn test_send_user_turn_updates_sandbox_and_cwd_between_turns() -> Result<( model: model.clone(), effort: Some(ReasoningEffort::Medium), summary: ReasoningSummary::Auto, + service_tier: None, output_schema: None, }) .await?; @@ -481,6 +483,7 @@ async fn test_send_user_turn_updates_sandbox_and_cwd_between_turns() -> Result<( model: model.clone(), effort: Some(ReasoningEffort::Medium), summary: ReasoningSummary::Auto, + service_tier: None, output_schema: None, }) .await?; diff --git a/codex-rs/app-server/tests/suite/output_schema.rs b/codex-rs/app-server/tests/suite/output_schema.rs index db006e328..b182e4262 100644 --- a/codex-rs/app-server/tests/suite/output_schema.rs +++ b/codex-rs/app-server/tests/suite/output_schema.rs @@ -92,6 +92,7 @@ async fn send_user_turn_accepts_output_schema_v1() -> Result<()> { model: "mock-model".to_string(), effort: Some(ReasoningEffort::Medium), summary: ReasoningSummary::Auto, + service_tier: None, output_schema: Some(output_schema.clone()), }) .await?; @@ -184,6 +185,7 @@ async fn send_user_turn_rejects_oversized_input_v1() -> Result<()> { model: "mock-model".to_string(), effort: Some(ReasoningEffort::Low), summary: ReasoningSummary::Auto, + service_tier: None, output_schema: None, }) .await?; @@ -273,6 +275,7 @@ async fn send_user_turn_output_schema_is_per_turn_v1() -> Result<()> { model: "mock-model".to_string(), effort: Some(ReasoningEffort::Medium), summary: ReasoningSummary::Auto, + service_tier: None, output_schema: Some(output_schema.clone()), }) .await?; @@ -321,6 +324,7 @@ async fn send_user_turn_output_schema_is_per_turn_v1() -> Result<()> { model: "mock-model".to_string(), effort: Some(ReasoningEffort::Medium), summary: ReasoningSummary::Auto, + service_tier: None, output_schema: None, }) .await?; diff --git a/codex-rs/app-server/tests/suite/v2/turn_start.rs b/codex-rs/app-server/tests/suite/v2/turn_start.rs index 472c1a96b..0888d0f23 100644 --- a/codex-rs/app-server/tests/suite/v2/turn_start.rs +++ b/codex-rs/app-server/tests/suite/v2/turn_start.rs @@ -1385,6 +1385,7 @@ async fn turn_start_updates_sandbox_and_cwd_between_turns_v2() -> Result<()> { model: Some("mock-model".to_string()), effort: Some(ReasoningEffort::Medium), summary: Some(ReasoningSummary::Auto), + service_tier: None, personality: None, output_schema: None, collaboration_mode: None, @@ -1416,6 +1417,7 @@ async fn turn_start_updates_sandbox_and_cwd_between_turns_v2() -> Result<()> { model: Some("mock-model".to_string()), effort: Some(ReasoningEffort::Medium), summary: Some(ReasoningSummary::Auto), + service_tier: None, personality: None, output_schema: None, collaboration_mode: None, diff --git a/codex-rs/core/config.schema.json b/codex-rs/core/config.schema.json index 77ac5f45e..af1253374 100644 --- a/codex-rs/core/config.schema.json +++ b/codex-rs/core/config.schema.json @@ -1328,21 +1328,10 @@ "type": "object" }, "ServiceTier": { - "oneOf": [ - { - "enum": [ - "fast" - ], - "type": "string" - }, - { - "description": "Legacy compatibility value for older local config files.", - "enum": [ - "standard" - ], - "type": "string" - } - ] + "enum": [ + "fast" + ], + "type": "string" }, "ShellEnvironmentPolicyInherit": { "oneOf": [ diff --git a/codex-rs/core/src/codex.rs b/codex-rs/core/src/codex.rs index 86c59e78d..6db1c995e 100644 --- a/codex-rs/core/src/codex.rs +++ b/codex-rs/core/src/codex.rs @@ -882,6 +882,7 @@ impl SessionConfiguration { ThreadConfigSnapshot { model: self.collaboration_mode.model().to_string(), model_provider_id: self.original_config_do_not_use.model_provider_id.clone(), + service_tier: self.service_tier, approval_policy: self.approval_policy.value(), sandbox_policy: self.sandbox_policy.get().clone(), cwd: self.cwd.clone(), @@ -1556,6 +1557,7 @@ impl Session { thread_name: session_configuration.thread_name.clone(), model: session_configuration.collaboration_mode.model().to_string(), model_provider_id: config.model_provider_id.clone(), + service_tier: session_configuration.service_tier, approval_policy: session_configuration.approval_policy.value(), sandbox_policy: session_configuration.sandbox_policy.get().clone(), cwd: session_configuration.cwd.clone(), diff --git a/codex-rs/core/src/codex_thread.rs b/codex-rs/core/src/codex_thread.rs index dc3c4ea75..c4fc1e087 100644 --- a/codex-rs/core/src/codex_thread.rs +++ b/codex-rs/core/src/codex_thread.rs @@ -9,6 +9,7 @@ use crate::protocol::Event; use crate::protocol::Op; use crate::protocol::Submission; use codex_protocol::config_types::Personality; +use codex_protocol::config_types::ServiceTier; use codex_protocol::models::ContentItem; use codex_protocol::models::ResponseInputItem; use codex_protocol::models::ResponseItem; @@ -27,6 +28,7 @@ use crate::state_db::StateDbHandle; pub struct ThreadConfigSnapshot { pub model: String, pub model_provider_id: String, + pub service_tier: Option, pub approval_policy: AskForApproval, pub sandbox_policy: SandboxPolicy, pub cwd: PathBuf, diff --git a/codex-rs/core/src/config/mod.rs b/codex-rs/core/src/config/mod.rs index cf7f6db17..bb313f25f 100644 --- a/codex-rs/core/src/config/mod.rs +++ b/codex-rs/core/src/config/mod.rs @@ -1565,6 +1565,7 @@ pub struct ConfigOverrides { pub approval_policy: Option, pub sandbox_mode: Option, pub model_provider: Option, + pub service_tier: Option>, pub config_profile: Option, pub codex_linux_sandbox_exe: Option, pub main_execve_wrapper_exe: Option, @@ -1695,6 +1696,7 @@ impl Config { approval_policy: approval_policy_override, sandbox_mode, model_provider, + service_tier: service_tier_override, config_profile: config_profile_key, codex_linux_sandbox_exe, main_execve_wrapper_exe, @@ -1956,10 +1958,12 @@ impl Config { let model = model.or(config_profile.model).or(cfg.model); let service_tier = if features.enabled(Feature::FastMode) { - config_profile - .service_tier - .or(cfg.service_tier) - .filter(|tier| matches!(tier, ServiceTier::Fast)) + service_tier_override.unwrap_or_else(|| { + config_profile + .service_tier + .or(cfg.service_tier) + .filter(|tier| matches!(tier, ServiceTier::Fast)) + }) } else { None }; @@ -5655,33 +5659,6 @@ trust_level = "untrusted" Ok(()) } - #[test] - fn legacy_standard_service_tier_loads_as_default_none() -> anyhow::Result<()> { - let codex_home = TempDir::new()?; - let cfg = toml::from_str::( - r#" -service_tier = "standard" - -[features] -fast_mode = true -"#, - ) - .expect("TOML deserialization should succeed"); - - let config = Config::load_from_base_config_with_overrides( - cfg, - ConfigOverrides { - cwd: Some(codex_home.path().to_path_buf()), - ..Default::default() - }, - codex_home.path().to_path_buf(), - )?; - - assert_eq!(config.service_tier, None); - - Ok(()) - } - #[test] fn derive_sandbox_policy_falls_back_to_constraint_value_for_implicit_defaults() -> anyhow::Result<()> { diff --git a/codex-rs/exec/src/lib.rs b/codex-rs/exec/src/lib.rs index 7c40c9be4..5f0ac4865 100644 --- a/codex-rs/exec/src/lib.rs +++ b/codex-rs/exec/src/lib.rs @@ -275,6 +275,7 @@ pub async fn run_main(cli: Cli, arg0_paths: Arg0DispatchPaths) -> anyhow::Result sandbox_mode, cwd: resolved_cwd, model_provider: model_provider.clone(), + service_tier: None, codex_linux_sandbox_exe: arg0_paths.codex_linux_sandbox_exe.clone(), main_execve_wrapper_exe: arg0_paths.main_execve_wrapper_exe.clone(), js_repl_node_path: None, diff --git a/codex-rs/exec/tests/event_processor_with_json_output.rs b/codex-rs/exec/tests/event_processor_with_json_output.rs index 6595e82ea..a051b5bb0 100644 --- a/codex-rs/exec/tests/event_processor_with_json_output.rs +++ b/codex-rs/exec/tests/event_processor_with_json_output.rs @@ -93,6 +93,7 @@ fn session_configured_produces_thread_started_event() { thread_name: None, model: "codex-mini-latest".to_string(), model_provider_id: "test-provider".to_string(), + service_tier: None, approval_policy: AskForApproval::Never, sandbox_policy: SandboxPolicy::new_read_only_policy(), cwd: PathBuf::from("/home/user/project"), diff --git a/codex-rs/mcp-server/src/outgoing_message.rs b/codex-rs/mcp-server/src/outgoing_message.rs index 7dcfa0c23..684cd1a70 100644 --- a/codex-rs/mcp-server/src/outgoing_message.rs +++ b/codex-rs/mcp-server/src/outgoing_message.rs @@ -300,6 +300,7 @@ mod tests { thread_name: None, model: "gpt-4o".to_string(), model_provider_id: "test-provider".to_string(), + service_tier: None, approval_policy: AskForApproval::Never, sandbox_policy: SandboxPolicy::new_read_only_policy(), cwd: PathBuf::from("/home/user/project"), @@ -342,6 +343,7 @@ mod tests { thread_name: None, model: "gpt-4o".to_string(), model_provider_id: "test-provider".to_string(), + service_tier: None, approval_policy: AskForApproval::Never, sandbox_policy: SandboxPolicy::new_read_only_policy(), cwd: PathBuf::from("/home/user/project"), @@ -408,6 +410,7 @@ mod tests { thread_name: None, model: "gpt-4o".to_string(), model_provider_id: "test-provider".to_string(), + service_tier: None, approval_policy: AskForApproval::Never, sandbox_policy: SandboxPolicy::new_read_only_policy(), cwd: PathBuf::from("/home/user/project"), diff --git a/codex-rs/protocol/src/config_types.rs b/codex-rs/protocol/src/config_types.rs index 29c173aa4..984dcf39f 100644 --- a/codex-rs/protocol/src/config_types.rs +++ b/codex-rs/protocol/src/config_types.rs @@ -117,8 +117,6 @@ pub enum WebSearchMode { #[serde(rename_all = "lowercase")] #[strum(serialize_all = "lowercase")] pub enum ServiceTier { - /// Legacy compatibility value for older local config files. - Standard, Fast, } diff --git a/codex-rs/protocol/src/protocol.rs b/codex-rs/protocol/src/protocol.rs index b1caa5779..3f1c39271 100644 --- a/codex-rs/protocol/src/protocol.rs +++ b/codex-rs/protocol/src/protocol.rs @@ -2783,6 +2783,9 @@ pub struct SessionConfiguredEvent { pub model_provider_id: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub service_tier: Option, + /// When to escalate for approval for execution pub approval_policy: AskForApproval, @@ -3464,6 +3467,7 @@ mod tests { thread_name: None, model: "codex-mini-latest".to_string(), model_provider_id: "openai".to_string(), + service_tier: None, approval_policy: AskForApproval::Never, sandbox_policy: SandboxPolicy::new_read_only_policy(), cwd: PathBuf::from("/home/user/project"), diff --git a/codex-rs/tui/src/app.rs b/codex-rs/tui/src/app.rs index 8052c8af6..1fe9d0d49 100644 --- a/codex-rs/tui/src/app.rs +++ b/codex-rs/tui/src/app.rs @@ -3353,6 +3353,7 @@ impl App { thread_name: None, model: config_snapshot.model, model_provider_id: config_snapshot.model_provider_id, + service_tier: config_snapshot.service_tier, approval_policy: config_snapshot.approval_policy, sandbox_policy: config_snapshot.sandbox_policy, cwd: config_snapshot.cwd, @@ -3814,6 +3815,7 @@ mod tests { thread_name: None, model: "gpt-test".to_string(), model_provider_id: "test-provider".to_string(), + service_tier: None, approval_policy: AskForApproval::Never, sandbox_policy: SandboxPolicy::new_read_only_policy(), cwd: PathBuf::from("/tmp/project"), @@ -4146,6 +4148,7 @@ mod tests { thread_name: None, model: "gpt-5".to_string(), model_provider_id: "test-provider".to_string(), + service_tier: None, approval_policy: AskForApproval::OnRequest, sandbox_policy: SandboxPolicy::new_workspace_write_policy(), cwd: PathBuf::from("/tmp/agent"), @@ -4366,6 +4369,7 @@ mod tests { thread_name: None, model: "gpt-test".to_string(), model_provider_id: "test-provider".to_string(), + service_tier: None, approval_policy: AskForApproval::Never, sandbox_policy: SandboxPolicy::new_read_only_policy(), cwd: PathBuf::from("/tmp/project"), @@ -5003,6 +5007,7 @@ mod tests { thread_name: None, model: "gpt-test".to_string(), model_provider_id: "test-provider".to_string(), + service_tier: None, approval_policy: AskForApproval::Never, sandbox_policy: SandboxPolicy::new_read_only_policy(), cwd: PathBuf::from("/home/user/project"), @@ -5060,6 +5065,7 @@ mod tests { thread_name: None, model: "gpt-test".to_string(), model_provider_id: "test-provider".to_string(), + service_tier: None, approval_policy: AskForApproval::Never, sandbox_policy: SandboxPolicy::new_read_only_policy(), cwd: PathBuf::from("/home/user/project"), @@ -5151,6 +5157,7 @@ mod tests { thread_name: None, model: "gpt-test".to_string(), model_provider_id: "test-provider".to_string(), + service_tier: None, approval_policy: AskForApproval::Never, sandbox_policy: SandboxPolicy::new_read_only_policy(), cwd: PathBuf::from("/home/user/project"), @@ -5215,6 +5222,7 @@ mod tests { thread_name: None, model: "gpt-test".to_string(), model_provider_id: "test-provider".to_string(), + service_tier: None, approval_policy: AskForApproval::Never, sandbox_policy: SandboxPolicy::new_read_only_policy(), cwd: PathBuf::from("/home/user/project"), @@ -5294,6 +5302,7 @@ mod tests { thread_name: None, model: "gpt-test".to_string(), model_provider_id: "test-provider".to_string(), + service_tier: None, approval_policy: AskForApproval::Never, sandbox_policy: SandboxPolicy::new_read_only_policy(), cwd: PathBuf::from("/home/user/project"), @@ -5420,6 +5429,7 @@ mod tests { thread_name: None, model: "gpt-test".to_string(), model_provider_id: "test-provider".to_string(), + service_tier: None, approval_policy: AskForApproval::Never, sandbox_policy: SandboxPolicy::new_read_only_policy(), cwd: PathBuf::from("/home/user/project"), @@ -5488,6 +5498,7 @@ mod tests { thread_name: Some("keep me".to_string()), model: "gpt-test".to_string(), model_provider_id: "test-provider".to_string(), + service_tier: None, approval_policy: AskForApproval::Never, sandbox_policy: SandboxPolicy::new_read_only_policy(), cwd: PathBuf::from("/tmp/project"), diff --git a/codex-rs/tui/src/chatwidget/tests.rs b/codex-rs/tui/src/chatwidget/tests.rs index 5184b608b..a720559b5 100644 --- a/codex-rs/tui/src/chatwidget/tests.rs +++ b/codex-rs/tui/src/chatwidget/tests.rs @@ -161,6 +161,7 @@ async fn resumed_initial_messages_render_history() { thread_name: None, model: "test-model".to_string(), model_provider_id: "test-provider".to_string(), + service_tier: None, approval_policy: AskForApproval::Never, sandbox_policy: SandboxPolicy::new_read_only_policy(), cwd: PathBuf::from("/home/user/project"), @@ -230,6 +231,7 @@ async fn replayed_user_message_preserves_text_elements_and_local_images() { thread_name: None, model: "test-model".to_string(), model_provider_id: "test-provider".to_string(), + service_tier: None, approval_policy: AskForApproval::Never, sandbox_policy: SandboxPolicy::new_read_only_policy(), cwd: PathBuf::from("/home/user/project"), @@ -289,6 +291,7 @@ async fn replayed_user_message_preserves_remote_image_urls() { thread_name: None, model: "test-model".to_string(), model_provider_id: "test-provider".to_string(), + service_tier: None, approval_policy: AskForApproval::Never, sandbox_policy: SandboxPolicy::new_read_only_policy(), cwd: PathBuf::from("/home/user/project"), @@ -355,6 +358,7 @@ async fn session_configured_syncs_widget_config_permissions_and_cwd() { thread_name: None, model: "test-model".to_string(), model_provider_id: "test-provider".to_string(), + service_tier: None, approval_policy: AskForApproval::Never, sandbox_policy: expected_sandbox.clone(), cwd: expected_cwd.clone(), @@ -396,6 +400,7 @@ async fn replayed_user_message_with_only_remote_images_renders_history_cell() { thread_name: None, model: "test-model".to_string(), model_provider_id: "test-provider".to_string(), + service_tier: None, approval_policy: AskForApproval::Never, sandbox_policy: SandboxPolicy::new_read_only_policy(), cwd: PathBuf::from("/home/user/project"), @@ -447,6 +452,7 @@ async fn replayed_user_message_with_only_local_images_does_not_render_history_ce thread_name: None, model: "test-model".to_string(), model_provider_id: "test-provider".to_string(), + service_tier: None, approval_policy: AskForApproval::Never, sandbox_policy: SandboxPolicy::new_read_only_policy(), cwd: PathBuf::from("/home/user/project"), @@ -557,6 +563,7 @@ async fn submission_preserves_text_elements_and_local_images() { thread_name: None, model: "test-model".to_string(), model_provider_id: "test-provider".to_string(), + service_tier: None, approval_policy: AskForApproval::Never, sandbox_policy: SandboxPolicy::new_read_only_policy(), cwd: PathBuf::from("/home/user/project"), @@ -639,6 +646,7 @@ async fn submission_with_remote_and_local_images_keeps_local_placeholder_numberi thread_name: None, model: "test-model".to_string(), model_provider_id: "test-provider".to_string(), + service_tier: None, approval_policy: AskForApproval::Never, sandbox_policy: SandboxPolicy::new_read_only_policy(), cwd: PathBuf::from("/home/user/project"), @@ -732,6 +740,7 @@ async fn enter_with_only_remote_images_submits_user_turn() { thread_name: None, model: "test-model".to_string(), model_provider_id: "test-provider".to_string(), + service_tier: None, approval_policy: AskForApproval::Never, sandbox_policy: SandboxPolicy::new_read_only_policy(), cwd: PathBuf::from("/home/user/project"), @@ -795,6 +804,7 @@ async fn shift_enter_with_only_remote_images_does_not_submit_user_turn() { thread_name: None, model: "test-model".to_string(), model_provider_id: "test-provider".to_string(), + service_tier: None, approval_policy: AskForApproval::Never, sandbox_policy: SandboxPolicy::new_read_only_policy(), cwd: PathBuf::from("/home/user/project"), @@ -833,6 +843,7 @@ async fn enter_with_only_remote_images_does_not_submit_when_modal_is_active() { thread_name: None, model: "test-model".to_string(), model_provider_id: "test-provider".to_string(), + service_tier: None, approval_policy: AskForApproval::Never, sandbox_policy: SandboxPolicy::new_read_only_policy(), cwd: PathBuf::from("/home/user/project"), @@ -871,6 +882,7 @@ async fn enter_with_only_remote_images_does_not_submit_when_input_disabled() { thread_name: None, model: "test-model".to_string(), model_provider_id: "test-provider".to_string(), + service_tier: None, approval_policy: AskForApproval::Never, sandbox_policy: SandboxPolicy::new_read_only_policy(), cwd: PathBuf::from("/home/user/project"), @@ -910,6 +922,7 @@ async fn submission_prefers_selected_duplicate_skill_path() { thread_name: None, model: "test-model".to_string(), model_provider_id: "test-provider".to_string(), + service_tier: None, approval_policy: AskForApproval::Never, sandbox_policy: SandboxPolicy::new_read_only_policy(), cwd: PathBuf::from("/home/user/project"), @@ -4425,6 +4438,7 @@ async fn plan_slash_command_with_args_submits_prompt_in_plan_mode() { thread_name: None, model: "test-model".to_string(), model_provider_id: "test-provider".to_string(), + service_tier: None, approval_policy: AskForApproval::Never, sandbox_policy: SandboxPolicy::new_read_only_policy(), cwd: PathBuf::from("/home/user/project"), diff --git a/codex-rs/tui/src/history_cell.rs b/codex-rs/tui/src/history_cell.rs index 14510c10a..76e92ca0e 100644 --- a/codex-rs/tui/src/history_cell.rs +++ b/codex-rs/tui/src/history_cell.rs @@ -2484,6 +2484,7 @@ mod tests { thread_name: None, model: model.to_string(), model_provider_id: "test-provider".to_string(), + service_tier: None, approval_policy: AskForApproval::Never, sandbox_policy: SandboxPolicy::new_read_only_policy(), cwd: PathBuf::from("/tmp/project"),