| use super::CodexErrorInfo; |
| use super::ThreadEnvironment; |
| use super::ThreadItem; |
| use super::ThreadStatus; |
| use super::TurnStatus; |
| use crate::JsonSchema; |
| use crate::TS; |
| use codex_experimental_api_macros::ExperimentalApi; |
| use codex_protocol::openai_models::ReasoningEffort; |
| use codex_protocol::protocol::MisalignmentErrorDetails as CoreMisalignmentErrorDetails; |
| use codex_protocol::protocol::MisalignmentSteer as CoreMisalignmentSteer; |
| use codex_protocol::protocol::SessionSource as CoreSessionSource; |
| use codex_protocol::protocol::SubAgentSource as CoreSubAgentSource; |
| use codex_protocol::protocol::ThreadHistoryMode as CoreThreadHistoryMode; |
| use codex_protocol::protocol::ThreadSource as CoreThreadSource; |
| use codex_utils_absolute_path::AbsolutePathBuf; |
| #[cfg(test)] |
| use schemars::r#gen::SchemaGenerator; |
| #[cfg(test)] |
| use schemars::schema::Schema; |
| use serde::Deserialize; |
| use serde::Serialize; |
| use std::fmt; |
| use std::path::PathBuf; |
| use thiserror::Error; |
|
|
| #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)] |
| #[serde(rename_all = "camelCase")] |
| #[ts(rename_all = "camelCase", export_to = "v2/")] |
| #[derive(Default)] |
| pub enum SessionSource { |
| Cli, |
| #[serde(rename = "vscode")] |
| #[ts(rename = "vscode")] |
| #[default] |
| VsCode, |
| Exec, |
| AppServer, |
| Custom(String), |
| SubAgent(CoreSubAgentSource), |
| #[serde(other)] |
| Unknown, |
| } |
|
|
| impl From<CoreSessionSource> for SessionSource { |
| fn from(value: CoreSessionSource) -> Self { |
| match value { |
| CoreSessionSource::Cli => SessionSource::Cli, |
| CoreSessionSource::VSCode => SessionSource::VsCode, |
| CoreSessionSource::Exec => SessionSource::Exec, |
| CoreSessionSource::Mcp => SessionSource::AppServer, |
| CoreSessionSource::Custom(source) => SessionSource::Custom(source), |
| |
| CoreSessionSource::Internal(_) => SessionSource::Unknown, |
| CoreSessionSource::SubAgent(sub) => SessionSource::SubAgent(sub), |
| CoreSessionSource::Unknown => SessionSource::Unknown, |
| } |
| } |
| } |
|
|
| impl From<SessionSource> for CoreSessionSource { |
| fn from(value: SessionSource) -> Self { |
| match value { |
| SessionSource::Cli => CoreSessionSource::Cli, |
| SessionSource::VsCode => CoreSessionSource::VSCode, |
| SessionSource::Exec => CoreSessionSource::Exec, |
| SessionSource::AppServer => CoreSessionSource::Mcp, |
| SessionSource::Custom(source) => CoreSessionSource::Custom(source), |
| SessionSource::SubAgent(sub) => CoreSessionSource::SubAgent(sub), |
| SessionSource::Unknown => CoreSessionSource::Unknown, |
| } |
| } |
| } |
|
|
| #[derive(Default, Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, JsonSchema, TS)] |
| #[serde(rename_all = "lowercase")] |
| #[ts(rename_all = "lowercase", export_to = "v2/")] |
| pub enum ThreadHistoryMode { |
| #[default] |
| Legacy, |
| Paginated, |
| } |
|
|
| impl From<CoreThreadHistoryMode> for ThreadHistoryMode { |
| fn from(value: CoreThreadHistoryMode) -> Self { |
| match value { |
| CoreThreadHistoryMode::Legacy => Self::Legacy, |
| CoreThreadHistoryMode::Paginated => Self::Paginated, |
| } |
| } |
| } |
|
|
| impl From<ThreadHistoryMode> for CoreThreadHistoryMode { |
| fn from(value: ThreadHistoryMode) -> Self { |
| match value { |
| ThreadHistoryMode::Legacy => Self::Legacy, |
| ThreadHistoryMode::Paginated => Self::Paginated, |
| } |
| } |
| } |
|
|
| #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, TS)] |
| #[serde(try_from = "String", into = "String")] |
| #[ts(type = "string")] |
| #[ts(export_to = "v2/")] |
| pub enum ThreadSource { |
| User, |
| Subagent, |
| GuardianReview, |
| Feature(String), |
| MemoryConsolidation, |
| } |
|
|
| #[cfg(test)] |
| impl JsonSchema for ThreadSource { |
| fn schema_name() -> String { |
| "ThreadSource".to_string() |
| } |
|
|
| fn json_schema(generator: &mut SchemaGenerator) -> Schema { |
| String::json_schema(generator) |
| } |
| } |
|
|
| impl TryFrom<String> for ThreadSource { |
| type Error = String; |
|
|
| fn try_from(value: String) -> Result<Self, Self::Error> { |
| value.parse::<CoreThreadSource>().map(Into::into) |
| } |
| } |
|
|
| impl From<ThreadSource> for String { |
| fn from(value: ThreadSource) -> Self { |
| CoreThreadSource::from(value).into() |
| } |
| } |
|
|
| impl From<CoreThreadSource> for ThreadSource { |
| fn from(value: CoreThreadSource) -> Self { |
| match value { |
| CoreThreadSource::User => ThreadSource::User, |
| CoreThreadSource::Subagent => ThreadSource::Subagent, |
| CoreThreadSource::GuardianReview => ThreadSource::GuardianReview, |
| CoreThreadSource::Feature(feature) => ThreadSource::Feature(feature), |
| CoreThreadSource::MemoryConsolidation => ThreadSource::MemoryConsolidation, |
| } |
| } |
| } |
|
|
| impl From<ThreadSource> for CoreThreadSource { |
| fn from(value: ThreadSource) -> Self { |
| match value { |
| ThreadSource::User => CoreThreadSource::User, |
| ThreadSource::Subagent => CoreThreadSource::Subagent, |
| ThreadSource::GuardianReview => CoreThreadSource::GuardianReview, |
| ThreadSource::Feature(feature) => CoreThreadSource::Feature(feature), |
| ThreadSource::MemoryConsolidation => CoreThreadSource::MemoryConsolidation, |
| } |
| } |
| } |
|
|
| |
| #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)] |
| #[serde(rename_all = "camelCase")] |
| #[ts(rename_all = "camelCase", export_to = "v2/")] |
| pub struct ThreadExtra {} |
|
|
| #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)] |
| #[serde(rename_all = "camelCase")] |
| #[ts(export_to = "v2/")] |
| pub struct GitInfo { |
| pub sha: Option<String>, |
| pub branch: Option<String>, |
| pub origin_url: Option<String>, |
| } |
|
|
| |
| #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)] |
| #[serde(rename_all = "camelCase")] |
| #[ts(export_to = "v2/")] |
| pub struct ThreadSection { |
| |
| pub id: String, |
| |
| pub name: String, |
| |
| #[serde(default)] |
| pub appearance: Option<ThreadSectionAppearance>, |
| } |
|
|
| |
| #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)] |
| #[serde(rename_all = "camelCase")] |
| #[ts(export_to = "v2/")] |
| pub struct ThreadSectionAppearance { |
| pub icon: Option<String>, |
| pub color: Option<String>, |
| } |
|
|
| #[derive(Serialize, Debug, Clone, PartialEq, JsonSchema, TS, ExperimentalApi)] |
| #[serde(rename_all = "camelCase")] |
| #[ts(export_to = "v2/")] |
| pub struct Thread { |
| |
| pub id: String, |
| |
| |
| |
| #[experimental("thread.environments")] |
| #[serde(default)] |
| pub environments: Option<Vec<ThreadEnvironment>>, |
| |
| #[experimental("thread.extra")] |
| pub extra: Option<ThreadExtra>, |
| |
| pub session_id: String, |
| |
| pub forked_from_id: Option<String>, |
| |
| pub parent_thread_id: Option<String>, |
| |
| pub preview: String, |
| |
| pub ephemeral: bool, |
| |
| #[serde(default)] |
| pub section: Option<ThreadSection>, |
| |
| #[serde(default)] |
| #[ts(type = "number | null")] |
| pub section_entered_at: Option<i64>, |
| |
| #[schemars( |
| required, |
| schema_with = "crate::protocol::serde_helpers::nullable_string_schema" |
| )] |
| pub project_id: Option<String>, |
| |
| #[serde(default)] |
| pub history_mode: ThreadHistoryMode, |
| |
| pub model_provider: String, |
| |
| |
| pub model: Option<String>, |
| |
| |
| pub reasoning_effort: Option<ReasoningEffort>, |
| |
| #[ts(type = "number")] |
| pub created_at: i64, |
| |
| #[ts(type = "number")] |
| pub updated_at: i64, |
| |
| #[ts(type = "number | null")] |
| pub recency_at: Option<i64>, |
| |
| pub status: ThreadStatus, |
| |
| pub path: Option<PathBuf>, |
| |
| pub cwd: AbsolutePathBuf, |
| |
| pub cli_version: String, |
| |
| |
| pub originator: Option<String>, |
| |
| pub source: SessionSource, |
| |
| |
| #[experimental("thread.canAcceptDirectInput")] |
| pub can_accept_direct_input: Option<bool>, |
| |
| pub thread_source: Option<ThreadSource>, |
| |
| pub agent_nickname: Option<String>, |
| |
| pub agent_role: Option<String>, |
| |
| pub git_info: Option<GitInfo>, |
| |
| pub name: Option<String>, |
| |
| #[experimental("thread.daybreakEnabled")] |
| pub daybreak_enabled: Option<bool>, |
| |
| |
| |
| |
| pub turns: Vec<Turn>, |
| } |
|
|
| |
| |
| #[derive(Deserialize)] |
| #[serde(rename_all = "camelCase")] |
| struct ThreadCompatibility { |
| id: String, |
| #[serde(default)] |
| environments: Option<Vec<ThreadEnvironment>>, |
| extra: Option<ThreadExtra>, |
| session_id: String, |
| forked_from_id: Option<String>, |
| parent_thread_id: Option<String>, |
| preview: String, |
| ephemeral: bool, |
| #[serde(default)] |
| section: Option<ThreadSection>, |
| #[serde(default)] |
| section_entered_at: Option<i64>, |
| #[serde(default)] |
| project_id: Option<String>, |
| #[serde(default)] |
| history_mode: ThreadHistoryMode, |
| model_provider: String, |
| model: Option<String>, |
| reasoning_effort: Option<ReasoningEffort>, |
| created_at: i64, |
| updated_at: i64, |
| recency_at: Option<i64>, |
| status: ThreadStatus, |
| path: Option<PathBuf>, |
| cwd: AbsolutePathBuf, |
| cli_version: String, |
| originator: Option<String>, |
| source: SessionSource, |
| can_accept_direct_input: Option<bool>, |
| thread_source: Option<ThreadSource>, |
| agent_nickname: Option<String>, |
| agent_role: Option<String>, |
| git_info: Option<GitInfo>, |
| name: Option<String>, |
| daybreak_enabled: Option<bool>, |
| turns: Vec<Turn>, |
| } |
|
|
| impl<'de> Deserialize<'de> for Thread { |
| fn deserialize<D>(deserializer: D) -> Result<Self, D::Error> |
| where |
| D: serde::Deserializer<'de>, |
| { |
| let thread = ThreadCompatibility::deserialize(deserializer)?; |
| Ok(Self { |
| id: thread.id, |
| environments: thread.environments, |
| extra: thread.extra, |
| session_id: thread.session_id, |
| forked_from_id: thread.forked_from_id, |
| parent_thread_id: thread.parent_thread_id, |
| preview: thread.preview, |
| ephemeral: thread.ephemeral, |
| section: thread.section, |
| section_entered_at: thread.section_entered_at, |
| project_id: thread.project_id, |
| history_mode: thread.history_mode, |
| model_provider: thread.model_provider, |
| model: thread.model, |
| reasoning_effort: thread.reasoning_effort, |
| created_at: thread.created_at, |
| updated_at: thread.updated_at, |
| recency_at: thread.recency_at, |
| status: thread.status, |
| path: thread.path, |
| cwd: thread.cwd, |
| cli_version: thread.cli_version, |
| originator: thread.originator, |
| source: thread.source, |
| can_accept_direct_input: thread.can_accept_direct_input, |
| thread_source: thread.thread_source, |
| agent_nickname: thread.agent_nickname, |
| agent_role: thread.agent_role, |
| git_info: thread.git_info, |
| name: thread.name, |
| daybreak_enabled: thread.daybreak_enabled, |
| turns: thread.turns, |
| }) |
| } |
| } |
|
|
| #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)] |
| #[serde(rename_all = "camelCase")] |
| #[ts(export_to = "v2/")] |
| pub struct Turn { |
| |
| pub id: String, |
| |
| pub items: Vec<ThreadItem>, |
| |
| #[serde(default)] |
| pub items_view: TurnItemsView, |
| pub status: TurnStatus, |
| |
| pub error: Option<TurnError>, |
| |
| #[ts(type = "number | null")] |
| pub started_at: Option<i64>, |
| |
| #[ts(type = "number | null")] |
| pub completed_at: Option<i64>, |
| |
| #[ts(type = "number | null")] |
| pub duration_ms: Option<i64>, |
| } |
|
|
| #[derive(Default, Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, JsonSchema, TS)] |
| #[serde(rename_all = "camelCase")] |
| #[ts(export_to = "v2/")] |
| pub enum TurnItemsView { |
| |
| NotLoaded, |
| |
| Summary, |
| |
| #[default] |
| Full, |
| } |
|
|
| #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS, Error)] |
| #[serde(rename_all = "camelCase")] |
| #[ts(export_to = "v2/")] |
| #[error("{message}")] |
| pub struct TurnError { |
| pub message: String, |
| pub codex_error_info: Option<CodexErrorInfo>, |
| #[serde(default)] |
| pub additional_details: Option<String>, |
| |
| #[serde(default)] |
| pub misalignment: Option<MisalignmentErrorDetails>, |
| } |
|
|
| #[derive(Serialize, Deserialize, Clone, PartialEq, Eq, JsonSchema, TS)] |
| #[serde(rename_all = "camelCase")] |
| #[ts(export_to = "v2/")] |
| pub struct MisalignmentErrorDetails { |
| |
| pub error_type: Option<String>, |
| |
| pub detailed_explanation: Option<String>, |
| |
| pub steer: Option<MisalignmentSteer>, |
| } |
|
|
| impl fmt::Debug for MisalignmentErrorDetails { |
| fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { |
| formatter |
| .debug_struct("MisalignmentErrorDetails") |
| .field("error_type", &self.error_type) |
| .field( |
| "has_detailed_explanation", |
| &self.detailed_explanation.is_some(), |
| ) |
| .field("has_steer", &self.steer.is_some()) |
| .finish() |
| } |
| } |
|
|
| impl From<CoreMisalignmentErrorDetails> for MisalignmentErrorDetails { |
| fn from(value: CoreMisalignmentErrorDetails) -> Self { |
| Self { |
| error_type: value.error_type, |
| detailed_explanation: value.detailed_explanation, |
| steer: value.steer.map(Into::into), |
| } |
| } |
| } |
|
|
| #[derive(Serialize, Deserialize, Clone, PartialEq, Eq, JsonSchema, TS)] |
| #[serde(rename_all = "camelCase")] |
| #[ts(export_to = "v2/")] |
| pub struct MisalignmentSteer { |
| pub message: String, |
| } |
|
|
| impl fmt::Debug for MisalignmentSteer { |
| fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { |
| formatter |
| .debug_struct("MisalignmentSteer") |
| .field("message", &"[REDACTED]") |
| .finish() |
| } |
| } |
|
|
| impl From<CoreMisalignmentSteer> for MisalignmentSteer { |
| fn from(value: CoreMisalignmentSteer) -> Self { |
| Self { |
| message: value.message, |
| } |
| } |
| } |
|
|