use super::ApprovalsReviewer;use super::AskForApproval;use super::SandboxPolicy;use super::Turn;use codex_experimental_api_macros::ExperimentalApi;use codex_protocol::config_types::CollaborationMode;use codex_protocol::config_types::Personality;use codex_protocol::config_types::ReasoningSummary;use codex_protocol::models::ImageDetail;use codex_protocol::openai_models::ReasoningEffort;use codex_protocol::plan_tool::PlanItemArg as CorePlanItemArg;use codex_protocol::plan_tool::StepStatus as CorePlanStepStatus;use codex_protocol::user_input::ByteRange as CoreByteRange;use codex_protocol::user_input::TextElement as CoreTextElement;use codex_protocol::user_input::UserInput as CoreUserInput;use codex_utils_absolute_path::AbsolutePathBuf;use codex_utils_path_uri::LegacyAppPathString;use schemars::JsonSchema;use serde::Deserialize;use serde::Serialize;use serde_json::Value as JsonValue;use std::collections::HashMap;use std::path::PathBuf;use ts_rs::TS;#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]#[serde(rename_all = "camelCase")]#[ts(export_to = "v2/")]pub enum TurnStatus { Completed, Interrupted, Failed, InProgress,}// Turn APIs#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS, ExperimentalApi)]#[serde(rename_all = "camelCase")]#[ts(export_to = "v2/")]pub struct TurnEnvironmentParams { pub environment_id: String, pub cwd: LegacyAppPathString,}#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, JsonSchema, TS)]#[serde(rename_all = "lowercase")]#[ts(rename_all = "lowercase")]#[ts(export_to = "v2/")]pub enum AdditionalContextKind { Untrusted, Application,}#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]#[serde(rename_all = "camelCase")]#[ts(export_to = "v2/")]pub struct AdditionalContextEntry { pub value: String, pub kind: AdditionalContextKind,}#[derive( Serialize, Deserialize, Debug, Default, Clone, PartialEq, JsonSchema, TS, ExperimentalApi,)]#[serde(rename_all = "camelCase")]#[ts(export_to = "v2/")]pub struct TurnStartParams { pub thread_id: String, #[ts(optional = nullable)] pub client_user_message_id: Option<String>, pub input: Vec<UserInput>, /// Optional metadata to enrich Codex's ResponsesAPI turn metadata. /// /// Entries are flattened into the JSON string sent as /// `client_metadata["x-codex-turn-metadata"]` on ResponsesAPI HTTP and websocket requests. /// /// They are not sent as top-level ResponsesAPI `client_metadata` keys, and reserved keys /// such as `session_id`, `thread_id`, `turn_id`, and `window_id` cannot be overridden. #[experimental("turn/start.responsesapiClientMetadata")] #[ts(optional = nullable)] pub responsesapi_client_metadata: Option<HashMap<String, String>>, /// Optional client-provided context fragments keyed by an opaque source identifier. #[experimental("turn/start.additionalContext")] #[ts(optional = nullable)] pub additional_context: Option<HashMap<String, AdditionalContextEntry>>, /// Optional environments for this turn and subsequent turns. /// /// Omitted uses the thread sticky environments. Empty disables /// environment access for this turn. Non-empty selects the first /// environment as the current turn environment for this turn. #[experimental("turn/start.environments")] #[ts(optional = nullable)] pub environments: Option<Vec<TurnEnvironmentParams>>, /// Override the working directory for this turn and subsequent turns. #[ts(optional = nullable)] pub cwd: Option<PathBuf>, /// Replace the thread's runtime workspace roots for this turn and /// subsequent turns. Paths must be absolute. #[experimental("turn/start.runtimeWorkspaceRoots")] #[ts(optional = nullable)] pub runtime_workspace_roots: Option<Vec<AbsolutePathBuf>>, /// Override the approval policy for this turn and subsequent turns. #[experimental(nested)] #[ts(optional = nullable)] pub approval_policy: Option<AskForApproval>, /// Override where approval requests are routed for review on this turn and /// subsequent turns. #[ts(optional = nullable)] pub approvals_reviewer: Option<ApprovalsReviewer>, /// Override the sandbox policy for this turn and subsequent turns. #[ts(optional = nullable)] pub sandbox_policy: Option<SandboxPolicy>, /// Select a named permissions profile id for this turn and subsequent /// turns. Cannot be combined with `sandboxPolicy`. #[experimental("turn/start.permissions")] #[ts(optional = nullable)] pub permissions: Option<String>, /// Override the model for this turn and subsequent turns. #[ts(optional = nullable)] pub model: Option<String>, /// Override the service tier for this turn and subsequent turns. #[serde( default, deserialize_with = "crate::protocol::serde_helpers::deserialize_double_option", serialize_with = "crate::protocol::serde_helpers::serialize_double_option", skip_serializing_if = "Option::is_none" )] #[ts(optional = nullable)] pub service_tier: Option<Option<String>>, /// Override the reasoning effort for this turn and subsequent turns. #[ts(optional = nullable)] pub effort: Option<ReasoningEffort>, /// Override the reasoning summary for this turn and subsequent turns. #[ts(optional = nullable)] pub summary: Option<ReasoningSummary>, /// Override the personality for this turn and subsequent turns. #[ts(optional = nullable)] pub personality: Option<Personality>, /// Optional JSON Schema used to constrain the final assistant message for /// this turn. #[ts(optional = nullable)] pub output_schema: Option<JsonValue>, /// EXPERIMENTAL - Set a pre-set collaboration mode. /// Takes precedence over model, reasoning_effort, and developer instructions if set. /// /// For `collaboration_mode.settings.developer_instructions`, `null` means /// "use the built-in instructions for the selected mode". #[experimental("turn/start.collaborationMode")] #[ts(optional = nullable)] pub collaboration_mode: Option<CollaborationMode>,}#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]#[serde(rename_all = "camelCase")]#[ts(export_to = "v2/")]pub struct TurnStartResponse { pub turn: Turn,}#[derive( Serialize, Deserialize, Debug, Default, Clone, PartialEq, JsonSchema, TS, ExperimentalApi,)]#[serde(rename_all = "camelCase")]#[ts(export_to = "v2/")]pub struct TurnSteerParams { pub thread_id: String, #[ts(optional = nullable)] pub client_user_message_id: Option<String>, pub input: Vec<UserInput>, /// Optional metadata to enrich Codex's ResponsesAPI turn metadata. /// /// Entries are flattened into the JSON string sent as /// `client_metadata["x-codex-turn-metadata"]` on ResponsesAPI HTTP and websocket requests. /// /// They are not sent as top-level ResponsesAPI `client_metadata` keys, and reserved keys /// such as `session_id`, `thread_id`, `turn_id`, and `window_id` cannot be overridden. #[experimental("turn/steer.responsesapiClientMetadata")] #[ts(optional = nullable)] pub responsesapi_client_metadata: Option<HashMap<String, String>>, /// Optional client-provided context fragments keyed by an opaque source identifier. #[experimental("turn/steer.additionalContext")] #[ts(optional = nullable)] pub additional_context: Option<HashMap<String, AdditionalContextEntry>>, /// Required active turn id precondition. The request fails when it does not /// match the currently active turn. pub expected_turn_id: String,}#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]#[serde(rename_all = "camelCase")]#[ts(export_to = "v2/")]pub struct TurnSteerResponse { pub turn_id: String,}#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]#[serde(rename_all = "camelCase")]#[ts(export_to = "v2/")]pub struct TurnInterruptParams { pub thread_id: String, pub turn_id: String,}#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]#[serde(rename_all = "camelCase")]#[ts(export_to = "v2/")]pub struct TurnInterruptResponse {}// User input types#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]#[serde(rename_all = "camelCase")]#[ts(export_to = "v2/")]pub struct ByteRange { pub start: usize, pub end: usize,}impl From<CoreByteRange> for ByteRange { fn from(value: CoreByteRange) -> Self { Self { start: value.start, end: value.end, } }}impl From<ByteRange> for CoreByteRange { fn from(value: ByteRange) -> Self { Self { start: value.start, end: value.end, } }}#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]#[serde(rename_all = "camelCase")]#[ts(export_to = "v2/")]pub struct TextElement { /// Byte range in the parent `text` buffer that this element occupies. pub byte_range: ByteRange, /// Optional human-readable placeholder for the element, displayed in the UI. placeholder: Option<String>,}impl TextElement { pub fn new(byte_range: ByteRange, placeholder: Option<String>) -> Self { Self { byte_range, placeholder, } } pub fn set_placeholder(&mut self, placeholder: Option<String>) { self.placeholder = placeholder; } pub fn placeholder(&self) -> Option<&str> { self.placeholder.as_deref() }}impl From<CoreTextElement> for TextElement { fn from(value: CoreTextElement) -> Self { Self::new( value.byte_range.into(), value._placeholder_for_conversion_only().map(str::to_string), ) }}impl From<TextElement> for CoreTextElement { fn from(value: TextElement) -> Self { Self::new(value.byte_range.into(), value.placeholder) }}#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]#[serde(tag = "type", rename_all = "camelCase")]#[ts(tag = "type")]#[ts(export_to = "v2/")]pub enum UserInput { Text { text: String, /// UI-defined spans within `text` used to render or persist special elements. #[serde(default)] text_elements: Vec<TextElement>, }, Image { #[serde(default)] #[ts(optional)] detail: Option<ImageDetail>, url: String, }, LocalImage { #[serde(default)] #[ts(optional)] detail: Option<ImageDetail>, path: PathBuf, }, Skill { name: String, path: PathBuf, }, Mention { name: String, path: String, },}impl UserInput { pub fn into_core(self) -> CoreUserInput { match self { UserInput::Text { text, text_elements, } => CoreUserInput::Text { text, text_elements: text_elements.into_iter().map(Into::into).collect(), }, UserInput::Image { url, detail } => CoreUserInput::Image { image_url: url, detail, }, UserInput::LocalImage { path, detail } => CoreUserInput::LocalImage { path, detail }, UserInput::Skill { name, path } => CoreUserInput::Skill { name, path }, UserInput::Mention { name, path } => CoreUserInput::Mention { name, path }, } }}impl From<CoreUserInput> for UserInput { fn from(value: CoreUserInput) -> Self { match value { CoreUserInput::Text { text, text_elements, } => UserInput::Text { text, text_elements: text_elements.into_iter().map(Into::into).collect(), }, CoreUserInput::Image { image_url, detail } => UserInput::Image { url: image_url, detail, }, CoreUserInput::LocalImage { path, detail } => UserInput::LocalImage { path, detail }, CoreUserInput::Skill { name, path } => UserInput::Skill { name, path }, CoreUserInput::Mention { name, path } => UserInput::Mention { name, path }, _ => unreachable!("unsupported user input variant"), } }}impl UserInput { pub fn text_char_count(&self) -> usize { match self { UserInput::Text { text, .. } => text.chars().count(), UserInput::Image { .. } | UserInput::LocalImage { .. } | UserInput::Skill { .. } | UserInput::Mention { .. } => 0, } }}#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]#[serde(rename_all = "camelCase")]#[ts(export_to = "v2/")]pub struct TurnStartedNotification { pub thread_id: String, pub turn: Turn,}#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]#[serde(rename_all = "camelCase")]#[ts(export_to = "v2/")]pub struct Usage { pub input_tokens: i32, pub cached_input_tokens: i32, pub output_tokens: i32,}#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]#[serde(rename_all = "camelCase")]#[ts(export_to = "v2/")]pub struct TurnCompletedNotification { pub thread_id: String, pub turn: Turn,}#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]#[serde(rename_all = "camelCase")]#[ts(export_to = "v2/")]/// Notification that the turn-level unified diff has changed./// Contains the latest aggregated diff across all file changes in the turn.pub struct TurnDiffUpdatedNotification { pub thread_id: String, pub turn_id: String, pub diff: String,}#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]#[serde(rename_all = "camelCase")]#[ts(export_to = "v2/")]pub struct TurnPlanUpdatedNotification { pub thread_id: String, pub turn_id: String, pub explanation: Option<String>, pub plan: Vec<TurnPlanStep>,}#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]#[serde(rename_all = "camelCase")]#[ts(export_to = "v2/")]pub struct TurnPlanStep { pub step: String, pub status: TurnPlanStepStatus,}#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, JsonSchema, TS)]#[serde(rename_all = "camelCase")]#[ts(export_to = "v2/")]pub enum TurnPlanStepStatus { Pending, InProgress, Completed,}impl From<CorePlanItemArg> for TurnPlanStep { fn from(value: CorePlanItemArg) -> Self { Self { step: value.step, status: value.status.into(), } }}impl From<CorePlanStepStatus> for TurnPlanStepStatus { fn from(value: CorePlanStepStatus) -> Self { match value { CorePlanStepStatus::Pending => Self::Pending, CorePlanStepStatus::InProgress => Self::InProgress, CorePlanStepStatus::Completed => Self::Completed, } }}