#### What on `plugin/install`, check if installed apps are already authed on chatgpt, and return list of all apps that are not. clients can use this list to trigger auth workflows as needed. checks are best effort based on `codex_apps` loading, much like `app/list`. #### Tests Added integration tests, tested locally.
5083 lines
169 KiB
Rust
5083 lines
169 KiB
Rust
use std::collections::BTreeMap;
|
|
use std::collections::HashMap;
|
|
use std::path::PathBuf;
|
|
|
|
use crate::RequestId;
|
|
use crate::protocol::common::AuthMode;
|
|
use codex_experimental_api_macros::ExperimentalApi;
|
|
use codex_protocol::account::PlanType;
|
|
use codex_protocol::approvals::ElicitationRequest as CoreElicitationRequest;
|
|
use codex_protocol::approvals::ExecPolicyAmendment as CoreExecPolicyAmendment;
|
|
use codex_protocol::approvals::NetworkApprovalContext as CoreNetworkApprovalContext;
|
|
use codex_protocol::approvals::NetworkApprovalProtocol as CoreNetworkApprovalProtocol;
|
|
use codex_protocol::approvals::NetworkPolicyAmendment as CoreNetworkPolicyAmendment;
|
|
use codex_protocol::approvals::NetworkPolicyRuleAction as CoreNetworkPolicyRuleAction;
|
|
use codex_protocol::config_types::CollaborationMode;
|
|
use codex_protocol::config_types::CollaborationModeMask as CoreCollaborationModeMask;
|
|
use codex_protocol::config_types::ForcedLoginMethod;
|
|
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;
|
|
use codex_protocol::items::TurnItem as CoreTurnItem;
|
|
use codex_protocol::mcp::Resource as McpResource;
|
|
use codex_protocol::mcp::ResourceTemplate as McpResourceTemplate;
|
|
use codex_protocol::mcp::Tool as McpTool;
|
|
use codex_protocol::models::FileSystemPermissions as CoreFileSystemPermissions;
|
|
use codex_protocol::models::MacOsAutomationPermission as CoreMacOsAutomationPermission;
|
|
use codex_protocol::models::MacOsPreferencesPermission as CoreMacOsPreferencesPermission;
|
|
use codex_protocol::models::MacOsSeatbeltProfileExtensions as CoreMacOsSeatbeltProfileExtensions;
|
|
use codex_protocol::models::MessagePhase;
|
|
use codex_protocol::models::NetworkPermissions as CoreNetworkPermissions;
|
|
use codex_protocol::models::PermissionProfile as CorePermissionProfile;
|
|
use codex_protocol::models::ResponseItem;
|
|
use codex_protocol::openai_models::InputModality;
|
|
use codex_protocol::openai_models::ModelAvailabilityNux as CoreModelAvailabilityNux;
|
|
use codex_protocol::openai_models::ReasoningEffort;
|
|
use codex_protocol::openai_models::default_input_modalities;
|
|
use codex_protocol::parse_command::ParsedCommand as CoreParsedCommand;
|
|
use codex_protocol::plan_tool::PlanItemArg as CorePlanItemArg;
|
|
use codex_protocol::plan_tool::StepStatus as CorePlanStepStatus;
|
|
use codex_protocol::protocol::AgentStatus as CoreAgentStatus;
|
|
use codex_protocol::protocol::AskForApproval as CoreAskForApproval;
|
|
use codex_protocol::protocol::CodexErrorInfo as CoreCodexErrorInfo;
|
|
use codex_protocol::protocol::CreditsSnapshot as CoreCreditsSnapshot;
|
|
use codex_protocol::protocol::ExecCommandStatus as CoreExecCommandStatus;
|
|
use codex_protocol::protocol::ModelRerouteReason as CoreModelRerouteReason;
|
|
use codex_protocol::protocol::NetworkAccess as CoreNetworkAccess;
|
|
use codex_protocol::protocol::PatchApplyStatus as CorePatchApplyStatus;
|
|
use codex_protocol::protocol::RateLimitSnapshot as CoreRateLimitSnapshot;
|
|
use codex_protocol::protocol::RateLimitWindow as CoreRateLimitWindow;
|
|
use codex_protocol::protocol::ReadOnlyAccess as CoreReadOnlyAccess;
|
|
use codex_protocol::protocol::RealtimeAudioFrame as CoreRealtimeAudioFrame;
|
|
use codex_protocol::protocol::RejectConfig as CoreRejectConfig;
|
|
use codex_protocol::protocol::ReviewDecision as CoreReviewDecision;
|
|
use codex_protocol::protocol::SessionSource as CoreSessionSource;
|
|
use codex_protocol::protocol::SkillDependencies as CoreSkillDependencies;
|
|
use codex_protocol::protocol::SkillErrorInfo as CoreSkillErrorInfo;
|
|
use codex_protocol::protocol::SkillInterface as CoreSkillInterface;
|
|
use codex_protocol::protocol::SkillMetadata as CoreSkillMetadata;
|
|
use codex_protocol::protocol::SkillScope as CoreSkillScope;
|
|
use codex_protocol::protocol::SkillToolDependency as CoreSkillToolDependency;
|
|
use codex_protocol::protocol::SubAgentSource as CoreSubAgentSource;
|
|
use codex_protocol::protocol::TokenUsage as CoreTokenUsage;
|
|
use codex_protocol::protocol::TokenUsageInfo as CoreTokenUsageInfo;
|
|
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 schemars::JsonSchema;
|
|
use serde::Deserialize;
|
|
use serde::Serialize;
|
|
use serde_json::Value as JsonValue;
|
|
use thiserror::Error;
|
|
use ts_rs::TS;
|
|
|
|
// Macro to declare a camelCased API v2 enum mirroring a core enum which
|
|
// tends to use either snake_case or kebab-case.
|
|
macro_rules! v2_enum_from_core {
|
|
(
|
|
pub enum $Name:ident from $Src:path { $( $Variant:ident ),+ $(,)? }
|
|
) => {
|
|
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub enum $Name { $( $Variant ),+ }
|
|
|
|
impl $Name {
|
|
pub fn to_core(self) -> $Src {
|
|
match self { $( $Name::$Variant => <$Src>::$Variant ),+ }
|
|
}
|
|
}
|
|
|
|
impl From<$Src> for $Name {
|
|
fn from(value: $Src) -> Self {
|
|
match value { $( <$Src>::$Variant => $Name::$Variant ),+ }
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
/// This translation layer make sure that we expose codex error code in camel case.
|
|
///
|
|
/// When an upstream HTTP status is available (for example, from the Responses API or a provider),
|
|
/// it is forwarded in `httpStatusCode` on the relevant `codexErrorInfo` variant.
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub enum CodexErrorInfo {
|
|
ContextWindowExceeded,
|
|
UsageLimitExceeded,
|
|
ServerOverloaded,
|
|
HttpConnectionFailed {
|
|
#[serde(rename = "httpStatusCode")]
|
|
#[ts(rename = "httpStatusCode")]
|
|
http_status_code: Option<u16>,
|
|
},
|
|
/// Failed to connect to the response SSE stream.
|
|
ResponseStreamConnectionFailed {
|
|
#[serde(rename = "httpStatusCode")]
|
|
#[ts(rename = "httpStatusCode")]
|
|
http_status_code: Option<u16>,
|
|
},
|
|
InternalServerError,
|
|
Unauthorized,
|
|
BadRequest,
|
|
ThreadRollbackFailed,
|
|
SandboxError,
|
|
/// The response SSE stream disconnected in the middle of a turn before completion.
|
|
ResponseStreamDisconnected {
|
|
#[serde(rename = "httpStatusCode")]
|
|
#[ts(rename = "httpStatusCode")]
|
|
http_status_code: Option<u16>,
|
|
},
|
|
/// Reached the retry limit for responses.
|
|
ResponseTooManyFailedAttempts {
|
|
#[serde(rename = "httpStatusCode")]
|
|
#[ts(rename = "httpStatusCode")]
|
|
http_status_code: Option<u16>,
|
|
},
|
|
Other,
|
|
}
|
|
|
|
impl From<CoreCodexErrorInfo> for CodexErrorInfo {
|
|
fn from(value: CoreCodexErrorInfo) -> Self {
|
|
match value {
|
|
CoreCodexErrorInfo::ContextWindowExceeded => CodexErrorInfo::ContextWindowExceeded,
|
|
CoreCodexErrorInfo::UsageLimitExceeded => CodexErrorInfo::UsageLimitExceeded,
|
|
CoreCodexErrorInfo::ServerOverloaded => CodexErrorInfo::ServerOverloaded,
|
|
CoreCodexErrorInfo::HttpConnectionFailed { http_status_code } => {
|
|
CodexErrorInfo::HttpConnectionFailed { http_status_code }
|
|
}
|
|
CoreCodexErrorInfo::ResponseStreamConnectionFailed { http_status_code } => {
|
|
CodexErrorInfo::ResponseStreamConnectionFailed { http_status_code }
|
|
}
|
|
CoreCodexErrorInfo::InternalServerError => CodexErrorInfo::InternalServerError,
|
|
CoreCodexErrorInfo::Unauthorized => CodexErrorInfo::Unauthorized,
|
|
CoreCodexErrorInfo::BadRequest => CodexErrorInfo::BadRequest,
|
|
CoreCodexErrorInfo::ThreadRollbackFailed => CodexErrorInfo::ThreadRollbackFailed,
|
|
CoreCodexErrorInfo::SandboxError => CodexErrorInfo::SandboxError,
|
|
CoreCodexErrorInfo::ResponseStreamDisconnected { http_status_code } => {
|
|
CodexErrorInfo::ResponseStreamDisconnected { http_status_code }
|
|
}
|
|
CoreCodexErrorInfo::ResponseTooManyFailedAttempts { http_status_code } => {
|
|
CodexErrorInfo::ResponseTooManyFailedAttempts { http_status_code }
|
|
}
|
|
CoreCodexErrorInfo::Other => CodexErrorInfo::Other,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, JsonSchema, TS)]
|
|
#[serde(rename_all = "kebab-case")]
|
|
#[ts(rename_all = "kebab-case", export_to = "v2/")]
|
|
pub enum AskForApproval {
|
|
#[serde(rename = "untrusted")]
|
|
#[ts(rename = "untrusted")]
|
|
UnlessTrusted,
|
|
OnFailure,
|
|
OnRequest,
|
|
Reject {
|
|
sandbox_approval: bool,
|
|
rules: bool,
|
|
mcp_elicitations: bool,
|
|
},
|
|
Never,
|
|
}
|
|
|
|
impl AskForApproval {
|
|
pub fn to_core(self) -> CoreAskForApproval {
|
|
match self {
|
|
AskForApproval::UnlessTrusted => CoreAskForApproval::UnlessTrusted,
|
|
AskForApproval::OnFailure => CoreAskForApproval::OnFailure,
|
|
AskForApproval::OnRequest => CoreAskForApproval::OnRequest,
|
|
AskForApproval::Reject {
|
|
sandbox_approval,
|
|
rules,
|
|
mcp_elicitations,
|
|
} => CoreAskForApproval::Reject(CoreRejectConfig {
|
|
sandbox_approval,
|
|
rules,
|
|
mcp_elicitations,
|
|
}),
|
|
AskForApproval::Never => CoreAskForApproval::Never,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<CoreAskForApproval> for AskForApproval {
|
|
fn from(value: CoreAskForApproval) -> Self {
|
|
match value {
|
|
CoreAskForApproval::UnlessTrusted => AskForApproval::UnlessTrusted,
|
|
CoreAskForApproval::OnFailure => AskForApproval::OnFailure,
|
|
CoreAskForApproval::OnRequest => AskForApproval::OnRequest,
|
|
CoreAskForApproval::Reject(reject_config) => AskForApproval::Reject {
|
|
sandbox_approval: reject_config.sandbox_approval,
|
|
rules: reject_config.rules,
|
|
mcp_elicitations: reject_config.mcp_elicitations,
|
|
},
|
|
CoreAskForApproval::Never => AskForApproval::Never,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, JsonSchema, TS)]
|
|
#[serde(rename_all = "kebab-case")]
|
|
#[ts(rename_all = "kebab-case", export_to = "v2/")]
|
|
pub enum SandboxMode {
|
|
ReadOnly,
|
|
WorkspaceWrite,
|
|
DangerFullAccess,
|
|
}
|
|
|
|
impl SandboxMode {
|
|
pub fn to_core(self) -> CoreSandboxMode {
|
|
match self {
|
|
SandboxMode::ReadOnly => CoreSandboxMode::ReadOnly,
|
|
SandboxMode::WorkspaceWrite => CoreSandboxMode::WorkspaceWrite,
|
|
SandboxMode::DangerFullAccess => CoreSandboxMode::DangerFullAccess,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<CoreSandboxMode> for SandboxMode {
|
|
fn from(value: CoreSandboxMode) -> Self {
|
|
match value {
|
|
CoreSandboxMode::ReadOnly => SandboxMode::ReadOnly,
|
|
CoreSandboxMode::WorkspaceWrite => SandboxMode::WorkspaceWrite,
|
|
CoreSandboxMode::DangerFullAccess => SandboxMode::DangerFullAccess,
|
|
}
|
|
}
|
|
}
|
|
|
|
v2_enum_from_core!(
|
|
pub enum ReviewDelivery from codex_protocol::protocol::ReviewDelivery {
|
|
Inline, Detached
|
|
}
|
|
);
|
|
|
|
v2_enum_from_core!(
|
|
pub enum McpAuthStatus from codex_protocol::protocol::McpAuthStatus {
|
|
Unsupported,
|
|
NotLoggedIn,
|
|
BearerToken,
|
|
OAuth
|
|
}
|
|
);
|
|
|
|
v2_enum_from_core!(
|
|
pub enum ModelRerouteReason from CoreModelRerouteReason {
|
|
HighRiskCyberActivity
|
|
}
|
|
);
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
|
|
#[serde(tag = "type", rename_all = "camelCase")]
|
|
#[ts(tag = "type")]
|
|
#[ts(export_to = "v2/")]
|
|
pub enum ConfigLayerSource {
|
|
/// Managed preferences layer delivered by MDM (macOS only).
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(rename_all = "camelCase")]
|
|
Mdm {
|
|
domain: String,
|
|
key: String,
|
|
},
|
|
|
|
/// Managed config layer from a file (usually `managed_config.toml`).
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(rename_all = "camelCase")]
|
|
System {
|
|
/// This is the path to the system config.toml file, though it is not
|
|
/// guaranteed to exist.
|
|
file: AbsolutePathBuf,
|
|
},
|
|
|
|
/// User config layer from $CODEX_HOME/config.toml. This layer is special
|
|
/// in that it is expected to be:
|
|
/// - writable by the user
|
|
/// - generally outside the workspace directory
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(rename_all = "camelCase")]
|
|
User {
|
|
/// This is the path to the user's config.toml file, though it is not
|
|
/// guaranteed to exist.
|
|
file: AbsolutePathBuf,
|
|
},
|
|
|
|
/// Path to a .codex/ folder within a project. There could be multiple of
|
|
/// these between `cwd` and the project/repo root.
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(rename_all = "camelCase")]
|
|
Project {
|
|
dot_codex_folder: AbsolutePathBuf,
|
|
},
|
|
|
|
/// Session-layer overrides supplied via `-c`/`--config`.
|
|
SessionFlags,
|
|
|
|
/// `managed_config.toml` was designed to be a config that was loaded
|
|
/// as the last layer on top of everything else. This scheme did not quite
|
|
/// work out as intended, but we keep this variant as a "best effort" while
|
|
/// we phase out `managed_config.toml` in favor of `requirements.toml`.
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(rename_all = "camelCase")]
|
|
LegacyManagedConfigTomlFromFile {
|
|
file: AbsolutePathBuf,
|
|
},
|
|
|
|
LegacyManagedConfigTomlFromMdm,
|
|
}
|
|
|
|
impl ConfigLayerSource {
|
|
/// A settings from a layer with a higher precedence will override a setting
|
|
/// from a layer with a lower precedence.
|
|
pub fn precedence(&self) -> i16 {
|
|
match self {
|
|
ConfigLayerSource::Mdm { .. } => 0,
|
|
ConfigLayerSource::System { .. } => 10,
|
|
ConfigLayerSource::User { .. } => 20,
|
|
ConfigLayerSource::Project { .. } => 25,
|
|
ConfigLayerSource::SessionFlags => 30,
|
|
ConfigLayerSource::LegacyManagedConfigTomlFromFile { .. } => 40,
|
|
ConfigLayerSource::LegacyManagedConfigTomlFromMdm => 50,
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Compares [ConfigLayerSource] by precedence, so `A < B` means settings from
|
|
/// layer `A` will be overridden by settings from layer `B`.
|
|
impl PartialOrd for ConfigLayerSource {
|
|
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
|
Some(self.precedence().cmp(&other.precedence()))
|
|
}
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default, JsonSchema, TS)]
|
|
#[serde(rename_all = "snake_case")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct SandboxWorkspaceWrite {
|
|
#[serde(default)]
|
|
pub writable_roots: Vec<PathBuf>,
|
|
#[serde(default)]
|
|
pub network_access: bool,
|
|
#[serde(default)]
|
|
pub exclude_tmpdir_env_var: bool,
|
|
#[serde(default)]
|
|
pub exclude_slash_tmp: bool,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "snake_case")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ToolsV2 {
|
|
#[serde(alias = "web_search_request")]
|
|
pub web_search: Option<bool>,
|
|
pub view_image: Option<bool>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct DynamicToolSpec {
|
|
pub name: String,
|
|
pub description: String,
|
|
pub input_schema: JsonValue,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "snake_case")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ProfileV2 {
|
|
pub model: Option<String>,
|
|
pub model_provider: Option<String>,
|
|
pub approval_policy: Option<AskForApproval>,
|
|
pub service_tier: Option<ServiceTier>,
|
|
pub model_reasoning_effort: Option<ReasoningEffort>,
|
|
pub model_reasoning_summary: Option<ReasoningSummary>,
|
|
pub model_verbosity: Option<Verbosity>,
|
|
pub web_search: Option<WebSearchMode>,
|
|
pub chatgpt_base_url: Option<String>,
|
|
#[serde(default, flatten)]
|
|
pub additional: HashMap<String, JsonValue>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "snake_case")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct AnalyticsConfig {
|
|
pub enabled: Option<bool>,
|
|
#[serde(default, flatten)]
|
|
pub additional: HashMap<String, JsonValue>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, JsonSchema, TS)]
|
|
#[serde(rename_all = "snake_case")]
|
|
#[ts(export_to = "v2/")]
|
|
pub enum AppToolApproval {
|
|
Auto,
|
|
Prompt,
|
|
Approve,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "snake_case")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct AppsDefaultConfig {
|
|
#[serde(default = "default_enabled")]
|
|
pub enabled: bool,
|
|
#[serde(default = "default_enabled")]
|
|
pub destructive_enabled: bool,
|
|
#[serde(default = "default_enabled")]
|
|
pub open_world_enabled: bool,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "snake_case")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct AppToolConfig {
|
|
pub enabled: Option<bool>,
|
|
pub approval_mode: Option<AppToolApproval>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "snake_case")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct AppToolsConfig {
|
|
#[serde(default, flatten)]
|
|
pub tools: HashMap<String, AppToolConfig>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "snake_case")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct AppConfig {
|
|
#[serde(default = "default_enabled")]
|
|
pub enabled: bool,
|
|
pub destructive_enabled: Option<bool>,
|
|
pub open_world_enabled: Option<bool>,
|
|
pub default_tools_approval_mode: Option<AppToolApproval>,
|
|
pub default_tools_enabled: Option<bool>,
|
|
pub tools: Option<AppToolsConfig>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "snake_case")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct AppsConfig {
|
|
#[serde(default, rename = "_default")]
|
|
pub default: Option<AppsDefaultConfig>,
|
|
#[serde(default, flatten)]
|
|
pub apps: HashMap<String, AppConfig>,
|
|
}
|
|
|
|
const fn default_enabled() -> bool {
|
|
true
|
|
}
|
|
|
|
const fn default_include_platform_defaults() -> bool {
|
|
true
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS, ExperimentalApi)]
|
|
#[serde(rename_all = "snake_case")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct Config {
|
|
pub model: Option<String>,
|
|
pub review_model: Option<String>,
|
|
pub model_context_window: Option<i64>,
|
|
pub model_auto_compact_token_limit: Option<i64>,
|
|
pub model_provider: Option<String>,
|
|
pub approval_policy: Option<AskForApproval>,
|
|
pub sandbox_mode: Option<SandboxMode>,
|
|
pub sandbox_workspace_write: Option<SandboxWorkspaceWrite>,
|
|
pub forced_chatgpt_workspace_id: Option<String>,
|
|
pub forced_login_method: Option<ForcedLoginMethod>,
|
|
pub web_search: Option<WebSearchMode>,
|
|
pub tools: Option<ToolsV2>,
|
|
pub profile: Option<String>,
|
|
#[serde(default)]
|
|
pub profiles: HashMap<String, ProfileV2>,
|
|
pub instructions: Option<String>,
|
|
pub developer_instructions: Option<String>,
|
|
pub compact_prompt: Option<String>,
|
|
pub model_reasoning_effort: Option<ReasoningEffort>,
|
|
pub model_reasoning_summary: Option<ReasoningSummary>,
|
|
pub model_verbosity: Option<Verbosity>,
|
|
pub service_tier: Option<ServiceTier>,
|
|
pub analytics: Option<AnalyticsConfig>,
|
|
#[experimental("config/read.apps")]
|
|
#[serde(default)]
|
|
pub apps: Option<AppsConfig>,
|
|
#[serde(default, flatten)]
|
|
pub additional: HashMap<String, JsonValue>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ConfigLayerMetadata {
|
|
pub name: ConfigLayerSource,
|
|
pub version: String,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ConfigLayer {
|
|
pub name: ConfigLayerSource,
|
|
pub version: String,
|
|
pub config: JsonValue,
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub disabled_reason: Option<String>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub enum MergeStrategy {
|
|
Replace,
|
|
Upsert,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub enum WriteStatus {
|
|
Ok,
|
|
OkOverridden,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct OverriddenMetadata {
|
|
pub message: String,
|
|
pub overriding_layer: ConfigLayerMetadata,
|
|
pub effective_value: JsonValue,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ConfigWriteResponse {
|
|
pub status: WriteStatus,
|
|
pub version: String,
|
|
/// Canonical path to the config file that was written.
|
|
pub file_path: AbsolutePathBuf,
|
|
pub overridden_metadata: Option<OverriddenMetadata>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub enum ConfigWriteErrorCode {
|
|
ConfigLayerReadonly,
|
|
ConfigVersionConflict,
|
|
ConfigValidationError,
|
|
ConfigPathNotFound,
|
|
ConfigSchemaUnknownKey,
|
|
UserLayerNotFound,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ConfigReadParams {
|
|
#[serde(default)]
|
|
pub include_layers: bool,
|
|
/// Optional working directory to resolve project config layers. If specified,
|
|
/// return the effective config as seen from that directory (i.e., including any
|
|
/// project layers between `cwd` and the project/repo root).
|
|
#[ts(optional = nullable)]
|
|
pub cwd: Option<String>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ConfigReadResponse {
|
|
pub config: Config,
|
|
pub origins: HashMap<String, ConfigLayerMetadata>,
|
|
#[serde(skip_serializing_if = "Option::is_none")]
|
|
pub layers: Option<Vec<ConfigLayer>>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS, ExperimentalApi)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ConfigRequirements {
|
|
pub allowed_approval_policies: Option<Vec<AskForApproval>>,
|
|
pub allowed_sandbox_modes: Option<Vec<SandboxMode>>,
|
|
pub allowed_web_search_modes: Option<Vec<WebSearchMode>>,
|
|
pub feature_requirements: Option<BTreeMap<String, bool>>,
|
|
pub enforce_residency: Option<ResidencyRequirement>,
|
|
#[experimental("configRequirements/read.network")]
|
|
pub network: Option<NetworkRequirements>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct NetworkRequirements {
|
|
pub enabled: Option<bool>,
|
|
pub http_port: Option<u16>,
|
|
pub socks_port: Option<u16>,
|
|
pub allow_upstream_proxy: Option<bool>,
|
|
pub dangerously_allow_non_loopback_proxy: Option<bool>,
|
|
pub dangerously_allow_all_unix_sockets: Option<bool>,
|
|
pub allowed_domains: Option<Vec<String>>,
|
|
pub denied_domains: Option<Vec<String>>,
|
|
pub allow_unix_sockets: Option<Vec<String>>,
|
|
pub allow_local_binding: Option<bool>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub enum ResidencyRequirement {
|
|
Us,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ConfigRequirementsReadResponse {
|
|
/// Null if no requirements are configured (e.g. no requirements.toml/MDM entries).
|
|
pub requirements: Option<ConfigRequirements>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, Hash, JsonSchema, TS)]
|
|
#[ts(export_to = "v2/")]
|
|
pub enum ExternalAgentConfigMigrationItemType {
|
|
#[serde(rename = "AGENTS_MD")]
|
|
#[ts(rename = "AGENTS_MD")]
|
|
AgentsMd,
|
|
#[serde(rename = "CONFIG")]
|
|
#[ts(rename = "CONFIG")]
|
|
Config,
|
|
#[serde(rename = "SKILLS")]
|
|
#[ts(rename = "SKILLS")]
|
|
Skills,
|
|
#[serde(rename = "MCP_SERVER_CONFIG")]
|
|
#[ts(rename = "MCP_SERVER_CONFIG")]
|
|
McpServerConfig,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ExternalAgentConfigMigrationItem {
|
|
pub item_type: ExternalAgentConfigMigrationItemType,
|
|
pub description: String,
|
|
/// Null or empty means home-scoped migration; non-empty means repo-scoped migration.
|
|
pub cwd: Option<PathBuf>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ExternalAgentConfigDetectResponse {
|
|
pub items: Vec<ExternalAgentConfigMigrationItem>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ExternalAgentConfigDetectParams {
|
|
/// If true, include detection under the user's home (~/.claude, ~/.codex, etc.).
|
|
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
|
|
pub include_home: bool,
|
|
/// Zero or more working directories to include for repo-scoped detection.
|
|
#[ts(optional = nullable)]
|
|
pub cwds: Option<Vec<PathBuf>>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ExternalAgentConfigImportParams {
|
|
pub migration_items: Vec<ExternalAgentConfigMigrationItem>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ExternalAgentConfigImportResponse {}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ConfigValueWriteParams {
|
|
pub key_path: String,
|
|
pub value: JsonValue,
|
|
pub merge_strategy: MergeStrategy,
|
|
/// Path to the config file to write; defaults to the user's `config.toml` when omitted.
|
|
#[ts(optional = nullable)]
|
|
pub file_path: Option<String>,
|
|
#[ts(optional = nullable)]
|
|
pub expected_version: Option<String>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ConfigBatchWriteParams {
|
|
pub edits: Vec<ConfigEdit>,
|
|
/// Path to the config file to write; defaults to the user's `config.toml` when omitted.
|
|
#[ts(optional = nullable)]
|
|
pub file_path: Option<String>,
|
|
#[ts(optional = nullable)]
|
|
pub expected_version: Option<String>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ConfigEdit {
|
|
pub key_path: String,
|
|
pub value: JsonValue,
|
|
pub merge_strategy: MergeStrategy,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub enum CommandExecutionApprovalDecision {
|
|
/// User approved the command.
|
|
Accept,
|
|
/// User approved the command and future prompts in the same session-scoped
|
|
/// approval cache should run without prompting.
|
|
AcceptForSession,
|
|
/// User approved the command, and wants to apply the proposed execpolicy amendment so future
|
|
/// matching commands can run without prompting.
|
|
AcceptWithExecpolicyAmendment {
|
|
execpolicy_amendment: ExecPolicyAmendment,
|
|
},
|
|
/// User chose a persistent network policy rule (allow/deny) for this host.
|
|
ApplyNetworkPolicyAmendment {
|
|
network_policy_amendment: NetworkPolicyAmendment,
|
|
},
|
|
/// User denied the command. The agent will continue the turn.
|
|
Decline,
|
|
/// User denied the command. The turn will also be immediately interrupted.
|
|
Cancel,
|
|
}
|
|
|
|
impl From<CoreReviewDecision> for CommandExecutionApprovalDecision {
|
|
fn from(value: CoreReviewDecision) -> Self {
|
|
match value {
|
|
CoreReviewDecision::Approved => Self::Accept,
|
|
CoreReviewDecision::ApprovedExecpolicyAmendment {
|
|
proposed_execpolicy_amendment,
|
|
} => Self::AcceptWithExecpolicyAmendment {
|
|
execpolicy_amendment: proposed_execpolicy_amendment.into(),
|
|
},
|
|
CoreReviewDecision::ApprovedForSession => Self::AcceptForSession,
|
|
CoreReviewDecision::NetworkPolicyAmendment {
|
|
network_policy_amendment,
|
|
} => Self::ApplyNetworkPolicyAmendment {
|
|
network_policy_amendment: network_policy_amendment.into(),
|
|
},
|
|
CoreReviewDecision::Abort => Self::Cancel,
|
|
CoreReviewDecision::Denied => Self::Decline,
|
|
}
|
|
}
|
|
}
|
|
|
|
v2_enum_from_core! {
|
|
pub enum NetworkApprovalProtocol from CoreNetworkApprovalProtocol {
|
|
Http,
|
|
Https,
|
|
Socks5Tcp,
|
|
Socks5Udp,
|
|
}
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct NetworkApprovalContext {
|
|
pub host: String,
|
|
pub protocol: NetworkApprovalProtocol,
|
|
}
|
|
|
|
impl From<CoreNetworkApprovalContext> for NetworkApprovalContext {
|
|
fn from(value: CoreNetworkApprovalContext) -> Self {
|
|
Self {
|
|
host: value.host,
|
|
protocol: value.protocol.into(),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct AdditionalFileSystemPermissions {
|
|
pub read: Option<Vec<AbsolutePathBuf>>,
|
|
pub write: Option<Vec<AbsolutePathBuf>>,
|
|
}
|
|
|
|
impl From<CoreFileSystemPermissions> for AdditionalFileSystemPermissions {
|
|
fn from(value: CoreFileSystemPermissions) -> Self {
|
|
Self {
|
|
read: value.read,
|
|
write: value.write,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct AdditionalMacOsPermissions {
|
|
pub preferences: CoreMacOsPreferencesPermission,
|
|
pub automations: CoreMacOsAutomationPermission,
|
|
pub accessibility: bool,
|
|
pub calendar: bool,
|
|
}
|
|
|
|
impl From<CoreMacOsSeatbeltProfileExtensions> for AdditionalMacOsPermissions {
|
|
fn from(value: CoreMacOsSeatbeltProfileExtensions) -> Self {
|
|
Self {
|
|
preferences: value.macos_preferences,
|
|
automations: value.macos_automation,
|
|
accessibility: value.macos_accessibility,
|
|
calendar: value.macos_calendar,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct AdditionalNetworkPermissions {
|
|
pub enabled: Option<bool>,
|
|
}
|
|
|
|
impl From<CoreNetworkPermissions> for AdditionalNetworkPermissions {
|
|
fn from(value: CoreNetworkPermissions) -> Self {
|
|
Self {
|
|
enabled: value.enabled,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct AdditionalPermissionProfile {
|
|
pub network: Option<AdditionalNetworkPermissions>,
|
|
pub file_system: Option<AdditionalFileSystemPermissions>,
|
|
pub macos: Option<AdditionalMacOsPermissions>,
|
|
}
|
|
|
|
impl From<CorePermissionProfile> for AdditionalPermissionProfile {
|
|
fn from(value: CorePermissionProfile) -> Self {
|
|
Self {
|
|
network: value.network.map(AdditionalNetworkPermissions::from),
|
|
file_system: value.file_system.map(AdditionalFileSystemPermissions::from),
|
|
macos: value.macos.map(AdditionalMacOsPermissions::from),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub enum FileChangeApprovalDecision {
|
|
/// User approved the file changes.
|
|
Accept,
|
|
/// User approved the file changes and future changes to the same files should run without prompting.
|
|
AcceptForSession,
|
|
/// User denied the file changes. The agent will continue the turn.
|
|
Decline,
|
|
/// User denied the file changes. The turn will also be immediately interrupted.
|
|
Cancel,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq, Eq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub enum NetworkAccess {
|
|
#[default]
|
|
Restricted,
|
|
Enabled,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq, Eq, JsonSchema, TS)]
|
|
#[serde(tag = "type", rename_all = "camelCase")]
|
|
#[ts(tag = "type")]
|
|
#[ts(export_to = "v2/")]
|
|
pub enum ReadOnlyAccess {
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(rename_all = "camelCase")]
|
|
Restricted {
|
|
#[serde(default = "default_include_platform_defaults")]
|
|
include_platform_defaults: bool,
|
|
#[serde(default)]
|
|
readable_roots: Vec<AbsolutePathBuf>,
|
|
},
|
|
#[default]
|
|
FullAccess,
|
|
}
|
|
|
|
impl ReadOnlyAccess {
|
|
pub fn to_core(&self) -> CoreReadOnlyAccess {
|
|
match self {
|
|
ReadOnlyAccess::Restricted {
|
|
include_platform_defaults,
|
|
readable_roots,
|
|
} => CoreReadOnlyAccess::Restricted {
|
|
include_platform_defaults: *include_platform_defaults,
|
|
readable_roots: readable_roots.clone(),
|
|
},
|
|
ReadOnlyAccess::FullAccess => CoreReadOnlyAccess::FullAccess,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<CoreReadOnlyAccess> for ReadOnlyAccess {
|
|
fn from(value: CoreReadOnlyAccess) -> Self {
|
|
match value {
|
|
CoreReadOnlyAccess::Restricted {
|
|
include_platform_defaults,
|
|
readable_roots,
|
|
} => ReadOnlyAccess::Restricted {
|
|
include_platform_defaults,
|
|
readable_roots,
|
|
},
|
|
CoreReadOnlyAccess::FullAccess => ReadOnlyAccess::FullAccess,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
|
|
#[serde(tag = "type", rename_all = "camelCase")]
|
|
#[ts(tag = "type")]
|
|
#[ts(export_to = "v2/")]
|
|
pub enum SandboxPolicy {
|
|
DangerFullAccess,
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(rename_all = "camelCase")]
|
|
ReadOnly {
|
|
#[serde(default)]
|
|
access: ReadOnlyAccess,
|
|
#[serde(default)]
|
|
network_access: bool,
|
|
},
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(rename_all = "camelCase")]
|
|
ExternalSandbox {
|
|
#[serde(default)]
|
|
network_access: NetworkAccess,
|
|
},
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(rename_all = "camelCase")]
|
|
WorkspaceWrite {
|
|
#[serde(default)]
|
|
writable_roots: Vec<AbsolutePathBuf>,
|
|
#[serde(default)]
|
|
read_only_access: ReadOnlyAccess,
|
|
#[serde(default)]
|
|
network_access: bool,
|
|
#[serde(default)]
|
|
exclude_tmpdir_env_var: bool,
|
|
#[serde(default)]
|
|
exclude_slash_tmp: bool,
|
|
},
|
|
}
|
|
|
|
impl SandboxPolicy {
|
|
pub fn to_core(&self) -> codex_protocol::protocol::SandboxPolicy {
|
|
match self {
|
|
SandboxPolicy::DangerFullAccess => {
|
|
codex_protocol::protocol::SandboxPolicy::DangerFullAccess
|
|
}
|
|
SandboxPolicy::ReadOnly {
|
|
access,
|
|
network_access,
|
|
} => codex_protocol::protocol::SandboxPolicy::ReadOnly {
|
|
access: access.to_core(),
|
|
network_access: *network_access,
|
|
},
|
|
SandboxPolicy::ExternalSandbox { network_access } => {
|
|
codex_protocol::protocol::SandboxPolicy::ExternalSandbox {
|
|
network_access: match network_access {
|
|
NetworkAccess::Restricted => CoreNetworkAccess::Restricted,
|
|
NetworkAccess::Enabled => CoreNetworkAccess::Enabled,
|
|
},
|
|
}
|
|
}
|
|
SandboxPolicy::WorkspaceWrite {
|
|
writable_roots,
|
|
read_only_access,
|
|
network_access,
|
|
exclude_tmpdir_env_var,
|
|
exclude_slash_tmp,
|
|
} => codex_protocol::protocol::SandboxPolicy::WorkspaceWrite {
|
|
writable_roots: writable_roots.clone(),
|
|
read_only_access: read_only_access.to_core(),
|
|
network_access: *network_access,
|
|
exclude_tmpdir_env_var: *exclude_tmpdir_env_var,
|
|
exclude_slash_tmp: *exclude_slash_tmp,
|
|
},
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<codex_protocol::protocol::SandboxPolicy> for SandboxPolicy {
|
|
fn from(value: codex_protocol::protocol::SandboxPolicy) -> Self {
|
|
match value {
|
|
codex_protocol::protocol::SandboxPolicy::DangerFullAccess => {
|
|
SandboxPolicy::DangerFullAccess
|
|
}
|
|
codex_protocol::protocol::SandboxPolicy::ReadOnly {
|
|
access,
|
|
network_access,
|
|
} => SandboxPolicy::ReadOnly {
|
|
access: ReadOnlyAccess::from(access),
|
|
network_access,
|
|
},
|
|
codex_protocol::protocol::SandboxPolicy::ExternalSandbox { network_access } => {
|
|
SandboxPolicy::ExternalSandbox {
|
|
network_access: match network_access {
|
|
CoreNetworkAccess::Restricted => NetworkAccess::Restricted,
|
|
CoreNetworkAccess::Enabled => NetworkAccess::Enabled,
|
|
},
|
|
}
|
|
}
|
|
codex_protocol::protocol::SandboxPolicy::WorkspaceWrite {
|
|
writable_roots,
|
|
read_only_access,
|
|
network_access,
|
|
exclude_tmpdir_env_var,
|
|
exclude_slash_tmp,
|
|
} => SandboxPolicy::WorkspaceWrite {
|
|
writable_roots,
|
|
read_only_access: ReadOnlyAccess::from(read_only_access),
|
|
network_access,
|
|
exclude_tmpdir_env_var,
|
|
exclude_slash_tmp,
|
|
},
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
|
|
#[serde(transparent)]
|
|
#[ts(type = "Array<string>", export_to = "v2/")]
|
|
pub struct ExecPolicyAmendment {
|
|
pub command: Vec<String>,
|
|
}
|
|
|
|
impl ExecPolicyAmendment {
|
|
pub fn into_core(self) -> CoreExecPolicyAmendment {
|
|
CoreExecPolicyAmendment::new(self.command)
|
|
}
|
|
}
|
|
|
|
impl From<CoreExecPolicyAmendment> for ExecPolicyAmendment {
|
|
fn from(value: CoreExecPolicyAmendment) -> Self {
|
|
Self {
|
|
command: value.command().to_vec(),
|
|
}
|
|
}
|
|
}
|
|
|
|
v2_enum_from_core!(
|
|
pub enum NetworkPolicyRuleAction from CoreNetworkPolicyRuleAction {
|
|
Allow, Deny
|
|
}
|
|
);
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct NetworkPolicyAmendment {
|
|
pub host: String,
|
|
pub action: NetworkPolicyRuleAction,
|
|
}
|
|
|
|
impl NetworkPolicyAmendment {
|
|
pub fn into_core(self) -> CoreNetworkPolicyAmendment {
|
|
CoreNetworkPolicyAmendment {
|
|
host: self.host,
|
|
action: self.action.to_core(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<CoreNetworkPolicyAmendment> for NetworkPolicyAmendment {
|
|
fn from(value: CoreNetworkPolicyAmendment) -> Self {
|
|
Self {
|
|
host: value.host,
|
|
action: NetworkPolicyRuleAction::from(value.action),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(tag = "type", rename_all = "camelCase")]
|
|
#[ts(tag = "type")]
|
|
#[ts(export_to = "v2/")]
|
|
pub enum CommandAction {
|
|
Read {
|
|
command: String,
|
|
name: String,
|
|
path: PathBuf,
|
|
},
|
|
ListFiles {
|
|
command: String,
|
|
path: Option<String>,
|
|
},
|
|
Search {
|
|
command: String,
|
|
query: Option<String>,
|
|
path: Option<String>,
|
|
},
|
|
Unknown {
|
|
command: String,
|
|
},
|
|
}
|
|
|
|
#[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,
|
|
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::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::SubAgent(sub) => CoreSessionSource::SubAgent(sub),
|
|
SessionSource::Unknown => CoreSessionSource::Unknown,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[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>,
|
|
}
|
|
|
|
impl CommandAction {
|
|
pub fn into_core(self) -> CoreParsedCommand {
|
|
match self {
|
|
CommandAction::Read {
|
|
command: cmd,
|
|
name,
|
|
path,
|
|
} => CoreParsedCommand::Read { cmd, name, path },
|
|
CommandAction::ListFiles { command: cmd, path } => {
|
|
CoreParsedCommand::ListFiles { cmd, path }
|
|
}
|
|
CommandAction::Search {
|
|
command: cmd,
|
|
query,
|
|
path,
|
|
} => CoreParsedCommand::Search { cmd, query, path },
|
|
CommandAction::Unknown { command: cmd } => CoreParsedCommand::Unknown { cmd },
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<CoreParsedCommand> for CommandAction {
|
|
fn from(value: CoreParsedCommand) -> Self {
|
|
match value {
|
|
CoreParsedCommand::Read { cmd, name, path } => CommandAction::Read {
|
|
command: cmd,
|
|
name,
|
|
path,
|
|
},
|
|
CoreParsedCommand::ListFiles { cmd, path } => {
|
|
CommandAction::ListFiles { command: cmd, path }
|
|
}
|
|
CoreParsedCommand::Search { cmd, query, path } => CommandAction::Search {
|
|
command: cmd,
|
|
query,
|
|
path,
|
|
},
|
|
CoreParsedCommand::Unknown { cmd } => CommandAction::Unknown { command: cmd },
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(tag = "type", rename_all = "camelCase")]
|
|
#[ts(tag = "type")]
|
|
#[ts(export_to = "v2/")]
|
|
pub enum Account {
|
|
#[serde(rename = "apiKey", rename_all = "camelCase")]
|
|
#[ts(rename = "apiKey", rename_all = "camelCase")]
|
|
ApiKey {},
|
|
|
|
#[serde(rename = "chatgpt", rename_all = "camelCase")]
|
|
#[ts(rename = "chatgpt", rename_all = "camelCase")]
|
|
Chatgpt { email: String, plan_type: PlanType },
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS, ExperimentalApi)]
|
|
#[serde(tag = "type")]
|
|
#[ts(tag = "type")]
|
|
#[ts(export_to = "v2/")]
|
|
pub enum LoginAccountParams {
|
|
#[serde(rename = "apiKey", rename_all = "camelCase")]
|
|
#[ts(rename = "apiKey", rename_all = "camelCase")]
|
|
ApiKey {
|
|
#[serde(rename = "apiKey")]
|
|
#[ts(rename = "apiKey")]
|
|
api_key: String,
|
|
},
|
|
#[serde(rename = "chatgpt")]
|
|
#[ts(rename = "chatgpt")]
|
|
Chatgpt,
|
|
/// [UNSTABLE] FOR OPENAI INTERNAL USE ONLY - DO NOT USE.
|
|
/// The access token must contain the same scopes that Codex-managed ChatGPT auth tokens have.
|
|
#[experimental("account/login/start.chatgptAuthTokens")]
|
|
#[serde(rename = "chatgptAuthTokens", rename_all = "camelCase")]
|
|
#[ts(rename = "chatgptAuthTokens", rename_all = "camelCase")]
|
|
ChatgptAuthTokens {
|
|
/// Access token (JWT) supplied by the client.
|
|
/// This token is used for backend API requests and email extraction.
|
|
access_token: String,
|
|
/// Workspace/account identifier supplied by the client.
|
|
chatgpt_account_id: String,
|
|
/// Optional plan type supplied by the client.
|
|
///
|
|
/// When `null`, Codex attempts to derive the plan type from access-token
|
|
/// claims. If unavailable, the plan defaults to `unknown`.
|
|
#[ts(optional = nullable)]
|
|
chatgpt_plan_type: Option<String>,
|
|
},
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(tag = "type", rename_all = "camelCase")]
|
|
#[ts(tag = "type")]
|
|
#[ts(export_to = "v2/")]
|
|
pub enum LoginAccountResponse {
|
|
#[serde(rename = "apiKey", rename_all = "camelCase")]
|
|
#[ts(rename = "apiKey", rename_all = "camelCase")]
|
|
ApiKey {},
|
|
#[serde(rename = "chatgpt", rename_all = "camelCase")]
|
|
#[ts(rename = "chatgpt", rename_all = "camelCase")]
|
|
Chatgpt {
|
|
// Use plain String for identifiers to avoid TS/JSON Schema quirks around uuid-specific types.
|
|
// Convert to/from UUIDs at the application layer as needed.
|
|
login_id: String,
|
|
/// URL the client should open in a browser to initiate the OAuth flow.
|
|
auth_url: String,
|
|
},
|
|
#[serde(rename = "chatgptAuthTokens", rename_all = "camelCase")]
|
|
#[ts(rename = "chatgptAuthTokens", rename_all = "camelCase")]
|
|
ChatgptAuthTokens {},
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct CancelLoginAccountParams {
|
|
pub login_id: String,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub enum CancelLoginAccountStatus {
|
|
Canceled,
|
|
NotFound,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct CancelLoginAccountResponse {
|
|
pub status: CancelLoginAccountStatus,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct LogoutAccountResponse {}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub enum ChatgptAuthTokensRefreshReason {
|
|
/// Codex attempted a backend request and received `401 Unauthorized`.
|
|
Unauthorized,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ChatgptAuthTokensRefreshParams {
|
|
pub reason: ChatgptAuthTokensRefreshReason,
|
|
/// Workspace/account identifier that Codex was previously using.
|
|
///
|
|
/// Clients that manage multiple accounts/workspaces can use this as a hint
|
|
/// to refresh the token for the correct workspace.
|
|
///
|
|
/// This may be `null` when the prior auth state did not include a workspace
|
|
/// identifier (`chatgpt_account_id`).
|
|
#[ts(optional = nullable)]
|
|
pub previous_account_id: Option<String>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ChatgptAuthTokensRefreshResponse {
|
|
pub access_token: String,
|
|
pub chatgpt_account_id: String,
|
|
pub chatgpt_plan_type: Option<String>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct GetAccountRateLimitsResponse {
|
|
/// Backward-compatible single-bucket view; mirrors the historical payload.
|
|
pub rate_limits: RateLimitSnapshot,
|
|
/// Multi-bucket view keyed by metered `limit_id` (for example, `codex`).
|
|
pub rate_limits_by_limit_id: Option<HashMap<String, RateLimitSnapshot>>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct GetAccountParams {
|
|
/// When `true`, requests a proactive token refresh before returning.
|
|
///
|
|
/// In managed auth mode this triggers the normal refresh-token flow. In
|
|
/// external auth mode this flag is ignored. Clients should refresh tokens
|
|
/// themselves and call `account/login/start` with `chatgptAuthTokens`.
|
|
#[serde(default)]
|
|
pub refresh_token: bool,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct GetAccountResponse {
|
|
pub account: Option<Account>,
|
|
pub requires_openai_auth: bool,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ModelListParams {
|
|
/// Opaque pagination cursor returned by a previous call.
|
|
#[ts(optional = nullable)]
|
|
pub cursor: Option<String>,
|
|
/// Optional page size; defaults to a reasonable server-side value.
|
|
#[ts(optional = nullable)]
|
|
pub limit: Option<u32>,
|
|
/// When true, include models that are hidden from the default picker list.
|
|
#[ts(optional = nullable)]
|
|
pub include_hidden: Option<bool>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ModelAvailabilityNux {
|
|
pub message: String,
|
|
}
|
|
|
|
impl From<CoreModelAvailabilityNux> for ModelAvailabilityNux {
|
|
fn from(value: CoreModelAvailabilityNux) -> Self {
|
|
Self {
|
|
message: value.message,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct Model {
|
|
pub id: String,
|
|
pub model: String,
|
|
pub upgrade: Option<String>,
|
|
pub upgrade_info: Option<ModelUpgradeInfo>,
|
|
pub availability_nux: Option<ModelAvailabilityNux>,
|
|
pub display_name: String,
|
|
pub description: String,
|
|
pub hidden: bool,
|
|
pub supported_reasoning_efforts: Vec<ReasoningEffortOption>,
|
|
pub default_reasoning_effort: ReasoningEffort,
|
|
#[serde(default = "default_input_modalities")]
|
|
pub input_modalities: Vec<InputModality>,
|
|
#[serde(default)]
|
|
pub supports_personality: bool,
|
|
// Only one model should be marked as default.
|
|
pub is_default: bool,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ModelUpgradeInfo {
|
|
pub model: String,
|
|
pub upgrade_copy: Option<String>,
|
|
pub model_link: Option<String>,
|
|
pub migration_markdown: Option<String>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ReasoningEffortOption {
|
|
pub reasoning_effort: ReasoningEffort,
|
|
pub description: String,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ModelListResponse {
|
|
pub data: Vec<Model>,
|
|
/// Opaque cursor to pass to the next call to continue after the last item.
|
|
/// If None, there are no more items to return.
|
|
pub next_cursor: Option<String>,
|
|
}
|
|
|
|
/// EXPERIMENTAL - list collaboration mode presets.
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct CollaborationModeListParams {}
|
|
|
|
/// EXPERIMENTAL - collaboration mode preset metadata for clients.
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct CollaborationModeMask {
|
|
pub name: String,
|
|
pub mode: Option<ModeKind>,
|
|
pub model: Option<String>,
|
|
#[serde(rename = "reasoning_effort")]
|
|
#[ts(rename = "reasoning_effort")]
|
|
pub reasoning_effort: Option<Option<ReasoningEffort>>,
|
|
}
|
|
|
|
impl From<CoreCollaborationModeMask> for CollaborationModeMask {
|
|
fn from(value: CoreCollaborationModeMask) -> Self {
|
|
Self {
|
|
name: value.name,
|
|
mode: value.mode,
|
|
model: value.model,
|
|
reasoning_effort: value.reasoning_effort,
|
|
}
|
|
}
|
|
}
|
|
|
|
/// EXPERIMENTAL - collaboration mode presets response.
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct CollaborationModeListResponse {
|
|
pub data: Vec<CollaborationModeMask>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ExperimentalFeatureListParams {
|
|
/// Opaque pagination cursor returned by a previous call.
|
|
#[ts(optional = nullable)]
|
|
pub cursor: Option<String>,
|
|
/// Optional page size; defaults to a reasonable server-side value.
|
|
#[ts(optional = nullable)]
|
|
pub limit: Option<u32>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub enum ExperimentalFeatureStage {
|
|
/// Feature is available for user testing and feedback.
|
|
Beta,
|
|
/// Feature is still being built and not ready for broad use.
|
|
UnderDevelopment,
|
|
/// Feature is production-ready.
|
|
Stable,
|
|
/// Feature is deprecated and should be avoided.
|
|
Deprecated,
|
|
/// Feature flag is retained only for backwards compatibility.
|
|
Removed,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ExperimentalFeature {
|
|
/// Stable key used in config.toml and CLI flag toggles.
|
|
pub name: String,
|
|
/// Lifecycle stage of this feature flag.
|
|
pub stage: ExperimentalFeatureStage,
|
|
/// User-facing display name shown in the experimental features UI.
|
|
/// Null when this feature is not in beta.
|
|
pub display_name: Option<String>,
|
|
/// Short summary describing what the feature does.
|
|
/// Null when this feature is not in beta.
|
|
pub description: Option<String>,
|
|
/// Announcement copy shown to users when the feature is introduced.
|
|
/// Null when this feature is not in beta.
|
|
pub announcement: Option<String>,
|
|
/// Whether this feature is currently enabled in the loaded config.
|
|
pub enabled: bool,
|
|
/// Whether this feature is enabled by default.
|
|
pub default_enabled: bool,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ExperimentalFeatureListResponse {
|
|
pub data: Vec<ExperimentalFeature>,
|
|
/// Opaque cursor to pass to the next call to continue after the last item.
|
|
/// If None, there are no more items to return.
|
|
pub next_cursor: Option<String>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ListMcpServerStatusParams {
|
|
/// Opaque pagination cursor returned by a previous call.
|
|
#[ts(optional = nullable)]
|
|
pub cursor: Option<String>,
|
|
/// Optional page size; defaults to a server-defined value.
|
|
#[ts(optional = nullable)]
|
|
pub limit: Option<u32>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct McpServerStatus {
|
|
pub name: String,
|
|
pub tools: std::collections::HashMap<String, McpTool>,
|
|
pub resources: Vec<McpResource>,
|
|
pub resource_templates: Vec<McpResourceTemplate>,
|
|
pub auth_status: McpAuthStatus,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ListMcpServerStatusResponse {
|
|
pub data: Vec<McpServerStatus>,
|
|
/// Opaque cursor to pass to the next call to continue after the last item.
|
|
/// If None, there are no more items to return.
|
|
pub next_cursor: Option<String>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
/// EXPERIMENTAL - list available apps/connectors.
|
|
pub struct AppsListParams {
|
|
/// Opaque pagination cursor returned by a previous call.
|
|
#[ts(optional = nullable)]
|
|
pub cursor: Option<String>,
|
|
/// Optional page size; defaults to a reasonable server-side value.
|
|
#[ts(optional = nullable)]
|
|
pub limit: Option<u32>,
|
|
/// Optional thread id used to evaluate app feature gating from that thread's config.
|
|
#[ts(optional = nullable)]
|
|
pub thread_id: Option<String>,
|
|
/// When true, bypass app caches and fetch the latest data from sources.
|
|
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
|
|
pub force_refetch: bool,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
/// EXPERIMENTAL - app metadata returned by app-list APIs.
|
|
pub struct AppBranding {
|
|
pub category: Option<String>,
|
|
pub developer: Option<String>,
|
|
pub website: Option<String>,
|
|
pub privacy_policy: Option<String>,
|
|
pub terms_of_service: Option<String>,
|
|
pub is_discoverable_app: bool,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct AppReview {
|
|
pub status: String,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct AppScreenshot {
|
|
pub url: Option<String>,
|
|
#[serde(alias = "file_id")]
|
|
pub file_id: Option<String>,
|
|
#[serde(alias = "user_prompt")]
|
|
pub user_prompt: String,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct AppMetadata {
|
|
pub review: Option<AppReview>,
|
|
pub categories: Option<Vec<String>>,
|
|
pub sub_categories: Option<Vec<String>>,
|
|
pub seo_description: Option<String>,
|
|
pub screenshots: Option<Vec<AppScreenshot>>,
|
|
pub developer: Option<String>,
|
|
pub version: Option<String>,
|
|
pub version_id: Option<String>,
|
|
pub version_notes: Option<String>,
|
|
pub first_party_type: Option<String>,
|
|
pub first_party_requires_install: Option<bool>,
|
|
pub show_in_composer_when_unlinked: Option<bool>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
/// EXPERIMENTAL - app metadata returned by app-list APIs.
|
|
pub struct AppInfo {
|
|
pub id: String,
|
|
pub name: String,
|
|
pub description: Option<String>,
|
|
pub logo_url: Option<String>,
|
|
pub logo_url_dark: Option<String>,
|
|
pub distribution_channel: Option<String>,
|
|
pub branding: Option<AppBranding>,
|
|
pub app_metadata: Option<AppMetadata>,
|
|
pub labels: Option<HashMap<String, String>>,
|
|
pub install_url: Option<String>,
|
|
#[serde(default)]
|
|
pub is_accessible: bool,
|
|
/// Whether this app is enabled in config.toml.
|
|
/// Example:
|
|
/// ```toml
|
|
/// [apps.bad_app]
|
|
/// enabled = false
|
|
/// ```
|
|
#[serde(default = "default_enabled")]
|
|
pub is_enabled: bool,
|
|
#[serde(default)]
|
|
pub plugin_display_names: Vec<String>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
/// EXPERIMENTAL - app metadata summary for plugin-install responses.
|
|
pub struct AppSummary {
|
|
pub id: String,
|
|
pub name: String,
|
|
pub description: Option<String>,
|
|
pub install_url: Option<String>,
|
|
}
|
|
|
|
impl From<AppInfo> for AppSummary {
|
|
fn from(value: AppInfo) -> Self {
|
|
Self {
|
|
id: value.id,
|
|
name: value.name,
|
|
description: value.description,
|
|
install_url: value.install_url,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
/// EXPERIMENTAL - app list response.
|
|
pub struct AppsListResponse {
|
|
pub data: Vec<AppInfo>,
|
|
/// Opaque cursor to pass to the next call to continue after the last item.
|
|
/// If None, there are no more items to return.
|
|
pub next_cursor: Option<String>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
/// EXPERIMENTAL - notification emitted when the app list changes.
|
|
pub struct AppListUpdatedNotification {
|
|
pub data: Vec<AppInfo>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct McpServerRefreshParams {}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct McpServerRefreshResponse {}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct McpServerOauthLoginParams {
|
|
pub name: String,
|
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
#[ts(optional = nullable)]
|
|
pub scopes: Option<Vec<String>>,
|
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
#[ts(optional = nullable)]
|
|
pub timeout_secs: Option<i64>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct McpServerOauthLoginResponse {
|
|
pub authorization_url: String,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct FeedbackUploadParams {
|
|
pub classification: String,
|
|
#[ts(optional = nullable)]
|
|
pub reason: Option<String>,
|
|
#[ts(optional = nullable)]
|
|
pub thread_id: Option<String>,
|
|
pub include_logs: bool,
|
|
#[ts(optional = nullable)]
|
|
pub extra_log_files: Option<Vec<PathBuf>>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct FeedbackUploadResponse {
|
|
pub thread_id: String,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct CommandExecParams {
|
|
pub command: Vec<String>,
|
|
#[ts(type = "number | null")]
|
|
#[ts(optional = nullable)]
|
|
pub timeout_ms: Option<i64>,
|
|
#[ts(optional = nullable)]
|
|
pub cwd: Option<PathBuf>,
|
|
#[ts(optional = nullable)]
|
|
pub sandbox_policy: Option<SandboxPolicy>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct CommandExecResponse {
|
|
pub exit_code: i32,
|
|
pub stdout: String,
|
|
pub stderr: String,
|
|
}
|
|
|
|
// === Threads, Turns, and Items ===
|
|
// Thread APIs
|
|
#[derive(
|
|
Serialize, Deserialize, Debug, Clone, PartialEq, Default, JsonSchema, TS, ExperimentalApi,
|
|
)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadStartParams {
|
|
#[ts(optional = nullable)]
|
|
pub model: Option<String>,
|
|
#[ts(optional = nullable)]
|
|
pub model_provider: Option<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"
|
|
)]
|
|
#[ts(optional = nullable)]
|
|
pub service_tier: Option<Option<ServiceTier>>,
|
|
#[ts(optional = nullable)]
|
|
pub cwd: Option<String>,
|
|
#[ts(optional = nullable)]
|
|
pub approval_policy: Option<AskForApproval>,
|
|
#[ts(optional = nullable)]
|
|
pub sandbox: Option<SandboxMode>,
|
|
#[ts(optional = nullable)]
|
|
pub config: Option<HashMap<String, JsonValue>>,
|
|
#[ts(optional = nullable)]
|
|
pub service_name: Option<String>,
|
|
#[ts(optional = nullable)]
|
|
pub base_instructions: Option<String>,
|
|
#[ts(optional = nullable)]
|
|
pub developer_instructions: Option<String>,
|
|
#[ts(optional = nullable)]
|
|
pub personality: Option<Personality>,
|
|
#[ts(optional = nullable)]
|
|
pub ephemeral: Option<bool>,
|
|
#[experimental("thread/start.dynamicTools")]
|
|
#[ts(optional = nullable)]
|
|
pub dynamic_tools: Option<Vec<DynamicToolSpec>>,
|
|
/// Test-only experimental field used to validate experimental gating and
|
|
/// schema filtering behavior in a stable way.
|
|
#[experimental("thread/start.mockExperimentalField")]
|
|
#[ts(optional = nullable)]
|
|
pub mock_experimental_field: Option<String>,
|
|
/// If true, opt into emitting raw Responses API items on the event stream.
|
|
/// This is for internal use only (e.g. Codex Cloud).
|
|
#[experimental("thread/start.experimentalRawEvents")]
|
|
#[serde(default)]
|
|
pub experimental_raw_events: bool,
|
|
/// If true, persist additional rollout EventMsg variants required to
|
|
/// reconstruct a richer thread history on resume/fork/read.
|
|
#[experimental("thread/start.persistFullHistory")]
|
|
#[serde(default)]
|
|
pub persist_extended_history: bool,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct MockExperimentalMethodParams {
|
|
/// Test-only payload field.
|
|
#[ts(optional = nullable)]
|
|
pub value: Option<String>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct MockExperimentalMethodResponse {
|
|
/// Echoes the input `value`.
|
|
pub echoed: Option<String>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadStartResponse {
|
|
pub thread: Thread,
|
|
pub model: String,
|
|
pub model_provider: String,
|
|
pub service_tier: Option<ServiceTier>,
|
|
pub cwd: PathBuf,
|
|
pub approval_policy: AskForApproval,
|
|
pub sandbox: SandboxPolicy,
|
|
pub reasoning_effort: Option<ReasoningEffort>,
|
|
}
|
|
|
|
#[derive(
|
|
Serialize, Deserialize, Debug, Default, Clone, PartialEq, JsonSchema, TS, ExperimentalApi,
|
|
)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
/// There are three ways to resume a thread:
|
|
/// 1. By thread_id: load the thread from disk by thread_id and resume it.
|
|
/// 2. By history: instantiate the thread from memory and resume it.
|
|
/// 3. By path: load the thread from disk by path and resume it.
|
|
///
|
|
/// The precedence is: history > path > thread_id.
|
|
/// If using history or path, the thread_id param will be ignored.
|
|
///
|
|
/// Prefer using thread_id whenever possible.
|
|
pub struct ThreadResumeParams {
|
|
pub thread_id: String,
|
|
|
|
/// [UNSTABLE] FOR CODEX CLOUD - DO NOT USE.
|
|
/// If specified, the thread will be resumed with the provided history
|
|
/// instead of loaded from disk.
|
|
#[experimental("thread/resume.history")]
|
|
#[ts(optional = nullable)]
|
|
pub history: Option<Vec<ResponseItem>>,
|
|
|
|
/// [UNSTABLE] Specify the rollout path to resume from.
|
|
/// If specified, the thread_id param will be ignored.
|
|
#[experimental("thread/resume.path")]
|
|
#[ts(optional = nullable)]
|
|
pub path: Option<PathBuf>,
|
|
|
|
/// Configuration overrides for the resumed thread, if any.
|
|
#[ts(optional = nullable)]
|
|
pub model: Option<String>,
|
|
#[ts(optional = nullable)]
|
|
pub model_provider: Option<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"
|
|
)]
|
|
#[ts(optional = nullable)]
|
|
pub service_tier: Option<Option<ServiceTier>>,
|
|
#[ts(optional = nullable)]
|
|
pub cwd: Option<String>,
|
|
#[ts(optional = nullable)]
|
|
pub approval_policy: Option<AskForApproval>,
|
|
#[ts(optional = nullable)]
|
|
pub sandbox: Option<SandboxMode>,
|
|
#[ts(optional = nullable)]
|
|
pub config: Option<HashMap<String, serde_json::Value>>,
|
|
#[ts(optional = nullable)]
|
|
pub base_instructions: Option<String>,
|
|
#[ts(optional = nullable)]
|
|
pub developer_instructions: Option<String>,
|
|
#[ts(optional = nullable)]
|
|
pub personality: Option<Personality>,
|
|
/// If true, persist additional rollout EventMsg variants required to
|
|
/// reconstruct a richer thread history on subsequent resume/fork/read.
|
|
#[experimental("thread/resume.persistFullHistory")]
|
|
#[serde(default)]
|
|
pub persist_extended_history: bool,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadResumeResponse {
|
|
pub thread: Thread,
|
|
pub model: String,
|
|
pub model_provider: String,
|
|
pub service_tier: Option<ServiceTier>,
|
|
pub cwd: PathBuf,
|
|
pub approval_policy: AskForApproval,
|
|
pub sandbox: SandboxPolicy,
|
|
pub reasoning_effort: Option<ReasoningEffort>,
|
|
}
|
|
|
|
#[derive(
|
|
Serialize, Deserialize, Debug, Default, Clone, PartialEq, JsonSchema, TS, ExperimentalApi,
|
|
)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
/// 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.
|
|
///
|
|
/// If using path, the thread_id param will be ignored.
|
|
///
|
|
/// Prefer using thread_id whenever possible.
|
|
pub struct ThreadForkParams {
|
|
pub thread_id: String,
|
|
|
|
/// [UNSTABLE] Specify the rollout path to fork from.
|
|
/// If specified, the thread_id param will be ignored.
|
|
#[experimental("thread/fork.path")]
|
|
#[ts(optional = nullable)]
|
|
pub path: Option<PathBuf>,
|
|
|
|
/// Configuration overrides for the forked thread, if any.
|
|
#[ts(optional = nullable)]
|
|
pub model: Option<String>,
|
|
#[ts(optional = nullable)]
|
|
pub model_provider: Option<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"
|
|
)]
|
|
#[ts(optional = nullable)]
|
|
pub service_tier: Option<Option<ServiceTier>>,
|
|
#[ts(optional = nullable)]
|
|
pub cwd: Option<String>,
|
|
#[ts(optional = nullable)]
|
|
pub approval_policy: Option<AskForApproval>,
|
|
#[ts(optional = nullable)]
|
|
pub sandbox: Option<SandboxMode>,
|
|
#[ts(optional = nullable)]
|
|
pub config: Option<HashMap<String, serde_json::Value>>,
|
|
#[ts(optional = nullable)]
|
|
pub base_instructions: Option<String>,
|
|
#[ts(optional = nullable)]
|
|
pub developer_instructions: Option<String>,
|
|
/// If true, persist additional rollout EventMsg variants required to
|
|
/// reconstruct a richer thread history on subsequent resume/fork/read.
|
|
#[experimental("thread/fork.persistFullHistory")]
|
|
#[serde(default)]
|
|
pub persist_extended_history: bool,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadForkResponse {
|
|
pub thread: Thread,
|
|
pub model: String,
|
|
pub model_provider: String,
|
|
pub service_tier: Option<ServiceTier>,
|
|
pub cwd: PathBuf,
|
|
pub approval_policy: AskForApproval,
|
|
pub sandbox: SandboxPolicy,
|
|
pub reasoning_effort: Option<ReasoningEffort>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadArchiveParams {
|
|
pub thread_id: String,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadArchiveResponse {}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadUnsubscribeParams {
|
|
pub thread_id: String,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadUnsubscribeResponse {
|
|
pub status: ThreadUnsubscribeStatus,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub enum ThreadUnsubscribeStatus {
|
|
NotLoaded,
|
|
NotSubscribed,
|
|
Unsubscribed,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadSetNameParams {
|
|
pub thread_id: String,
|
|
pub name: String,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadUnarchiveParams {
|
|
pub thread_id: String,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadSetNameResponse {}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadMetadataUpdateParams {
|
|
pub thread_id: String,
|
|
/// Patch the stored Git metadata for this thread.
|
|
/// Omit a field to leave it unchanged, set it to `null` to clear it, or
|
|
/// provide a string to replace the stored value.
|
|
#[ts(optional = nullable)]
|
|
pub git_info: Option<ThreadMetadataGitInfoUpdateParams>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadMetadataGitInfoUpdateParams {
|
|
/// Omit to leave the stored commit unchanged, set to `null` to clear it,
|
|
/// or provide a non-empty string to replace it.
|
|
#[serde(
|
|
default,
|
|
skip_serializing_if = "Option::is_none",
|
|
serialize_with = "super::serde_helpers::serialize_double_option",
|
|
deserialize_with = "super::serde_helpers::deserialize_double_option"
|
|
)]
|
|
#[ts(optional = nullable, type = "string | null")]
|
|
pub sha: Option<Option<String>>,
|
|
/// Omit to leave the stored branch unchanged, set to `null` to clear it,
|
|
/// or provide a non-empty string to replace it.
|
|
#[serde(
|
|
default,
|
|
skip_serializing_if = "Option::is_none",
|
|
serialize_with = "super::serde_helpers::serialize_double_option",
|
|
deserialize_with = "super::serde_helpers::deserialize_double_option"
|
|
)]
|
|
#[ts(optional = nullable, type = "string | null")]
|
|
pub branch: Option<Option<String>>,
|
|
/// Omit to leave the stored origin URL unchanged, set to `null` to clear it,
|
|
/// or provide a non-empty string to replace it.
|
|
#[serde(
|
|
default,
|
|
skip_serializing_if = "Option::is_none",
|
|
serialize_with = "super::serde_helpers::serialize_double_option",
|
|
deserialize_with = "super::serde_helpers::deserialize_double_option"
|
|
)]
|
|
#[ts(optional = nullable, type = "string | null")]
|
|
pub origin_url: Option<Option<String>>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadMetadataUpdateResponse {
|
|
pub thread: Thread,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadUnarchiveResponse {
|
|
pub thread: Thread,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadCompactStartParams {
|
|
pub thread_id: String,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadCompactStartResponse {}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadBackgroundTerminalsCleanParams {
|
|
pub thread_id: String,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadBackgroundTerminalsCleanResponse {}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadRollbackParams {
|
|
pub thread_id: String,
|
|
/// The number of turns to drop from the end of the thread. Must be >= 1.
|
|
///
|
|
/// This only modifies the thread's history and does not revert local file changes
|
|
/// that have been made by the agent. Clients are responsible for reverting these changes.
|
|
pub num_turns: u32,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadRollbackResponse {
|
|
/// The updated thread after applying the rollback, with `turns` populated.
|
|
///
|
|
/// The ThreadItems stored in each Turn are lossy since we explicitly do not
|
|
/// persist all agent interactions, such as command executions. This is the same
|
|
/// behavior as `thread/resume`.
|
|
pub thread: Thread,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadListParams {
|
|
/// Opaque pagination cursor returned by a previous call.
|
|
#[ts(optional = nullable)]
|
|
pub cursor: Option<String>,
|
|
/// Optional page size; defaults to a reasonable server-side value.
|
|
#[ts(optional = nullable)]
|
|
pub limit: Option<u32>,
|
|
/// Optional sort key; defaults to created_at.
|
|
#[ts(optional = nullable)]
|
|
pub sort_key: Option<ThreadSortKey>,
|
|
/// Optional provider filter; when set, only sessions recorded under these
|
|
/// providers are returned. When present but empty, includes all providers.
|
|
#[ts(optional = nullable)]
|
|
pub model_providers: Option<Vec<String>>,
|
|
/// Optional source filter; when set, only sessions from these source kinds
|
|
/// are returned. When omitted or empty, defaults to interactive sources.
|
|
#[ts(optional = nullable)]
|
|
pub source_kinds: Option<Vec<ThreadSourceKind>>,
|
|
/// Optional archived filter; when set to true, only archived threads are returned.
|
|
/// If false or null, only non-archived threads are returned.
|
|
#[ts(optional = nullable)]
|
|
pub archived: Option<bool>,
|
|
/// Optional cwd filter; when set, only threads whose session cwd exactly
|
|
/// matches this path are returned.
|
|
#[ts(optional = nullable)]
|
|
pub cwd: Option<String>,
|
|
/// Optional substring filter for the extracted thread title.
|
|
#[ts(optional = nullable)]
|
|
pub search_term: Option<String>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(rename_all = "camelCase", export_to = "v2/")]
|
|
pub enum ThreadSourceKind {
|
|
Cli,
|
|
#[serde(rename = "vscode")]
|
|
#[ts(rename = "vscode")]
|
|
VsCode,
|
|
Exec,
|
|
AppServer,
|
|
SubAgent,
|
|
SubAgentReview,
|
|
SubAgentCompact,
|
|
SubAgentThreadSpawn,
|
|
SubAgentOther,
|
|
Unknown,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "snake_case")]
|
|
#[ts(export_to = "v2/")]
|
|
pub enum ThreadSortKey {
|
|
CreatedAt,
|
|
UpdatedAt,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadListResponse {
|
|
pub data: Vec<Thread>,
|
|
/// Opaque cursor to pass to the next call to continue after the last item.
|
|
/// if None, there are no more items to return.
|
|
pub next_cursor: Option<String>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Default, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadLoadedListParams {
|
|
/// Opaque pagination cursor returned by a previous call.
|
|
#[ts(optional = nullable)]
|
|
pub cursor: Option<String>,
|
|
/// Optional page size; defaults to no limit.
|
|
#[ts(optional = nullable)]
|
|
pub limit: Option<u32>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadLoadedListResponse {
|
|
/// Thread ids for sessions currently loaded in memory.
|
|
pub data: Vec<String>,
|
|
/// Opaque cursor to pass to the next call to continue after the last item.
|
|
/// if None, there are no more items to return.
|
|
pub next_cursor: Option<String>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(tag = "type", rename_all = "camelCase")]
|
|
#[ts(tag = "type")]
|
|
#[ts(export_to = "v2/")]
|
|
pub enum ThreadStatus {
|
|
NotLoaded,
|
|
Idle,
|
|
SystemError,
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(rename_all = "camelCase")]
|
|
Active {
|
|
active_flags: Vec<ThreadActiveFlag>,
|
|
},
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub enum ThreadActiveFlag {
|
|
WaitingOnApproval,
|
|
WaitingOnUserInput,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadReadParams {
|
|
pub thread_id: String,
|
|
/// When true, include turns and their items from rollout history.
|
|
#[serde(default)]
|
|
pub include_turns: bool,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadReadResponse {
|
|
pub thread: Thread,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct SkillsListParams {
|
|
/// When empty, defaults to the current session working directory.
|
|
#[serde(default, skip_serializing_if = "Vec::is_empty")]
|
|
pub cwds: Vec<PathBuf>,
|
|
|
|
/// When true, bypass the skills cache and re-scan skills from disk.
|
|
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
|
|
pub force_reload: bool,
|
|
|
|
/// Optional per-cwd extra roots to scan as user-scoped skills.
|
|
#[serde(default)]
|
|
#[ts(optional = nullable)]
|
|
pub per_cwd_extra_user_roots: Option<Vec<SkillsListExtraRootsForCwd>>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct SkillsListExtraRootsForCwd {
|
|
pub cwd: PathBuf,
|
|
pub extra_user_roots: Vec<PathBuf>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct SkillsListResponse {
|
|
pub data: Vec<SkillsListEntry>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct PluginListParams {
|
|
/// Optional working directories used to discover repo marketplaces. When omitted,
|
|
/// only home-scoped marketplaces are considered.
|
|
#[ts(optional = nullable)]
|
|
pub cwds: Option<Vec<AbsolutePathBuf>>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct PluginListResponse {
|
|
pub marketplaces: Vec<PluginMarketplaceEntry>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct SkillsRemoteReadParams {
|
|
#[serde(default)]
|
|
pub hazelnut_scope: HazelnutScope,
|
|
#[serde(default)]
|
|
pub product_surface: ProductSurface,
|
|
#[serde(default)]
|
|
pub enabled: bool,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, JsonSchema, TS, Default)]
|
|
#[serde(rename_all = "kebab-case")]
|
|
#[ts(rename_all = "kebab-case")]
|
|
#[ts(export_to = "v2/")]
|
|
pub enum HazelnutScope {
|
|
#[default]
|
|
Example,
|
|
WorkspaceShared,
|
|
AllShared,
|
|
Personal,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, JsonSchema, TS, Default)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub enum ProductSurface {
|
|
Chatgpt,
|
|
#[default]
|
|
Codex,
|
|
Api,
|
|
Atlas,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct RemoteSkillSummary {
|
|
pub id: String,
|
|
pub name: String,
|
|
pub description: String,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct SkillsRemoteReadResponse {
|
|
pub data: Vec<RemoteSkillSummary>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct SkillsRemoteWriteParams {
|
|
pub hazelnut_id: String,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct SkillsRemoteWriteResponse {
|
|
pub id: String,
|
|
pub path: PathBuf,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, JsonSchema, TS)]
|
|
#[serde(rename_all = "snake_case")]
|
|
#[ts(rename_all = "snake_case")]
|
|
#[ts(export_to = "v2/")]
|
|
pub enum SkillScope {
|
|
User,
|
|
Repo,
|
|
System,
|
|
Admin,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct SkillMetadata {
|
|
pub name: String,
|
|
pub description: String,
|
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
#[ts(optional)]
|
|
/// Legacy short_description from SKILL.md. Prefer SKILL.json interface.short_description.
|
|
pub short_description: Option<String>,
|
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
#[ts(optional)]
|
|
pub interface: Option<SkillInterface>,
|
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
#[ts(optional)]
|
|
pub dependencies: Option<SkillDependencies>,
|
|
pub path: PathBuf,
|
|
pub scope: SkillScope,
|
|
pub enabled: bool,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct SkillInterface {
|
|
#[ts(optional)]
|
|
pub display_name: Option<String>,
|
|
#[ts(optional)]
|
|
pub short_description: Option<String>,
|
|
#[ts(optional)]
|
|
pub icon_small: Option<PathBuf>,
|
|
#[ts(optional)]
|
|
pub icon_large: Option<PathBuf>,
|
|
#[ts(optional)]
|
|
pub brand_color: Option<String>,
|
|
#[ts(optional)]
|
|
pub default_prompt: Option<String>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct SkillDependencies {
|
|
pub tools: Vec<SkillToolDependency>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct SkillToolDependency {
|
|
#[serde(rename = "type")]
|
|
#[ts(rename = "type")]
|
|
pub r#type: String,
|
|
pub value: String,
|
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
#[ts(optional)]
|
|
pub description: Option<String>,
|
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
#[ts(optional)]
|
|
pub transport: Option<String>,
|
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
#[ts(optional)]
|
|
pub command: Option<String>,
|
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
#[ts(optional)]
|
|
pub url: Option<String>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct SkillErrorInfo {
|
|
pub path: PathBuf,
|
|
pub message: String,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct SkillsListEntry {
|
|
pub cwd: PathBuf,
|
|
pub skills: Vec<SkillMetadata>,
|
|
pub errors: Vec<SkillErrorInfo>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct PluginMarketplaceEntry {
|
|
pub name: String,
|
|
pub path: PathBuf,
|
|
pub plugins: Vec<PluginSummary>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct PluginSummary {
|
|
pub name: String,
|
|
pub source: PluginSource,
|
|
pub enabled: bool,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(tag = "type", rename_all = "camelCase")]
|
|
#[ts(tag = "type")]
|
|
#[ts(export_to = "v2/")]
|
|
pub enum PluginSource {
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(rename_all = "camelCase")]
|
|
Local { path: PathBuf },
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct SkillsConfigWriteParams {
|
|
pub path: PathBuf,
|
|
pub enabled: bool,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct SkillsConfigWriteResponse {
|
|
pub effective_enabled: bool,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct PluginInstallParams {
|
|
pub marketplace_path: AbsolutePathBuf,
|
|
pub plugin_name: String,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct PluginInstallResponse {
|
|
pub apps_needing_auth: Vec<AppSummary>,
|
|
}
|
|
|
|
impl From<CoreSkillMetadata> for SkillMetadata {
|
|
fn from(value: CoreSkillMetadata) -> Self {
|
|
Self {
|
|
name: value.name,
|
|
description: value.description,
|
|
short_description: value.short_description,
|
|
interface: value.interface.map(SkillInterface::from),
|
|
dependencies: value.dependencies.map(SkillDependencies::from),
|
|
path: value.path,
|
|
scope: value.scope.into(),
|
|
enabled: true,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<CoreSkillInterface> for SkillInterface {
|
|
fn from(value: CoreSkillInterface) -> Self {
|
|
Self {
|
|
display_name: value.display_name,
|
|
short_description: value.short_description,
|
|
brand_color: value.brand_color,
|
|
default_prompt: value.default_prompt,
|
|
icon_small: value.icon_small,
|
|
icon_large: value.icon_large,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<CoreSkillDependencies> for SkillDependencies {
|
|
fn from(value: CoreSkillDependencies) -> Self {
|
|
Self {
|
|
tools: value
|
|
.tools
|
|
.into_iter()
|
|
.map(SkillToolDependency::from)
|
|
.collect(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<CoreSkillToolDependency> for SkillToolDependency {
|
|
fn from(value: CoreSkillToolDependency) -> Self {
|
|
Self {
|
|
r#type: value.r#type,
|
|
value: value.value,
|
|
description: value.description,
|
|
transport: value.transport,
|
|
command: value.command,
|
|
url: value.url,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<CoreSkillScope> for SkillScope {
|
|
fn from(value: CoreSkillScope) -> Self {
|
|
match value {
|
|
CoreSkillScope::User => Self::User,
|
|
CoreSkillScope::Repo => Self::Repo,
|
|
CoreSkillScope::System => Self::System,
|
|
CoreSkillScope::Admin => Self::Admin,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<CoreSkillErrorInfo> for SkillErrorInfo {
|
|
fn from(value: CoreSkillErrorInfo) -> Self {
|
|
Self {
|
|
path: value.path,
|
|
message: value.message,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct Thread {
|
|
pub id: String,
|
|
/// Usually the first user message in the thread, if available.
|
|
pub preview: String,
|
|
/// Whether the thread is ephemeral and should not be materialized on disk.
|
|
pub ephemeral: bool,
|
|
/// Model provider used for this thread (for example, 'openai').
|
|
pub model_provider: String,
|
|
/// Unix timestamp (in seconds) when the thread was created.
|
|
#[ts(type = "number")]
|
|
pub created_at: i64,
|
|
/// Unix timestamp (in seconds) when the thread was last updated.
|
|
#[ts(type = "number")]
|
|
pub updated_at: i64,
|
|
/// Current runtime status for the thread.
|
|
pub status: ThreadStatus,
|
|
/// [UNSTABLE] Path to the thread on disk.
|
|
pub path: Option<PathBuf>,
|
|
/// Working directory captured for the thread.
|
|
pub cwd: PathBuf,
|
|
/// Version of the CLI that created the thread.
|
|
pub cli_version: String,
|
|
/// Origin of the thread (CLI, VSCode, codex exec, codex app-server, etc.).
|
|
pub source: SessionSource,
|
|
/// Optional random unique nickname assigned to an AgentControl-spawned sub-agent.
|
|
pub agent_nickname: Option<String>,
|
|
/// Optional role (agent_role) assigned to an AgentControl-spawned sub-agent.
|
|
pub agent_role: Option<String>,
|
|
/// Optional Git metadata captured when the thread was created.
|
|
pub git_info: Option<GitInfo>,
|
|
/// Optional user-facing thread title.
|
|
pub name: Option<String>,
|
|
/// Only populated on `thread/resume`, `thread/rollback`, `thread/fork`, and `thread/read`
|
|
/// (when `includeTurns` is true) responses.
|
|
/// For all other responses and notifications returning a Thread,
|
|
/// the turns field will be an empty list.
|
|
pub turns: Vec<Turn>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct AccountUpdatedNotification {
|
|
pub auth_mode: Option<AuthMode>,
|
|
pub plan_type: Option<PlanType>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadTokenUsageUpdatedNotification {
|
|
pub thread_id: String,
|
|
pub turn_id: String,
|
|
pub token_usage: ThreadTokenUsage,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadTokenUsage {
|
|
pub total: TokenUsageBreakdown,
|
|
pub last: TokenUsageBreakdown,
|
|
// TODO(aibrahim): make this not optional
|
|
#[ts(type = "number | null")]
|
|
pub model_context_window: Option<i64>,
|
|
}
|
|
|
|
impl From<CoreTokenUsageInfo> for ThreadTokenUsage {
|
|
fn from(value: CoreTokenUsageInfo) -> Self {
|
|
Self {
|
|
total: value.total_token_usage.into(),
|
|
last: value.last_token_usage.into(),
|
|
model_context_window: value.model_context_window,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct TokenUsageBreakdown {
|
|
#[ts(type = "number")]
|
|
pub total_tokens: i64,
|
|
#[ts(type = "number")]
|
|
pub input_tokens: i64,
|
|
#[ts(type = "number")]
|
|
pub cached_input_tokens: i64,
|
|
#[ts(type = "number")]
|
|
pub output_tokens: i64,
|
|
#[ts(type = "number")]
|
|
pub reasoning_output_tokens: i64,
|
|
}
|
|
|
|
impl From<CoreTokenUsage> for TokenUsageBreakdown {
|
|
fn from(value: CoreTokenUsage) -> Self {
|
|
Self {
|
|
total_tokens: value.total_tokens,
|
|
input_tokens: value.input_tokens,
|
|
cached_input_tokens: value.cached_input_tokens,
|
|
output_tokens: value.output_tokens,
|
|
reasoning_output_tokens: value.reasoning_output_tokens,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct Turn {
|
|
pub id: String,
|
|
/// Only populated on a `thread/resume` or `thread/fork` response.
|
|
/// For all other responses and notifications returning a Turn,
|
|
/// the items field will be an empty list.
|
|
pub items: Vec<ThreadItem>,
|
|
pub status: TurnStatus,
|
|
/// Only populated when the Turn's status is failed.
|
|
pub error: Option<TurnError>,
|
|
}
|
|
|
|
#[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>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ErrorNotification {
|
|
pub error: TurnError,
|
|
// Set to true if the error is transient and the app-server process will automatically retry.
|
|
// If true, this will not interrupt a turn.
|
|
pub will_retry: bool,
|
|
pub thread_id: String,
|
|
pub turn_id: String,
|
|
}
|
|
|
|
/// EXPERIMENTAL - thread realtime audio chunk.
|
|
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadRealtimeAudioChunk {
|
|
pub data: String,
|
|
pub sample_rate: u32,
|
|
pub num_channels: u16,
|
|
pub samples_per_channel: Option<u32>,
|
|
}
|
|
|
|
impl From<CoreRealtimeAudioFrame> for ThreadRealtimeAudioChunk {
|
|
fn from(value: CoreRealtimeAudioFrame) -> Self {
|
|
let CoreRealtimeAudioFrame {
|
|
data,
|
|
sample_rate,
|
|
num_channels,
|
|
samples_per_channel,
|
|
} = value;
|
|
Self {
|
|
data,
|
|
sample_rate,
|
|
num_channels,
|
|
samples_per_channel,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<ThreadRealtimeAudioChunk> for CoreRealtimeAudioFrame {
|
|
fn from(value: ThreadRealtimeAudioChunk) -> Self {
|
|
let ThreadRealtimeAudioChunk {
|
|
data,
|
|
sample_rate,
|
|
num_channels,
|
|
samples_per_channel,
|
|
} = value;
|
|
Self {
|
|
data,
|
|
sample_rate,
|
|
num_channels,
|
|
samples_per_channel,
|
|
}
|
|
}
|
|
}
|
|
|
|
/// EXPERIMENTAL - start a thread-scoped realtime session.
|
|
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadRealtimeStartParams {
|
|
pub thread_id: String,
|
|
pub prompt: String,
|
|
#[ts(optional = nullable)]
|
|
pub session_id: Option<String>,
|
|
}
|
|
|
|
/// EXPERIMENTAL - response for starting thread realtime.
|
|
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadRealtimeStartResponse {}
|
|
|
|
/// EXPERIMENTAL - append audio input to thread realtime.
|
|
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadRealtimeAppendAudioParams {
|
|
pub thread_id: String,
|
|
pub audio: ThreadRealtimeAudioChunk,
|
|
}
|
|
|
|
/// EXPERIMENTAL - response for appending realtime audio input.
|
|
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadRealtimeAppendAudioResponse {}
|
|
|
|
/// EXPERIMENTAL - append text input to thread realtime.
|
|
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadRealtimeAppendTextParams {
|
|
pub thread_id: String,
|
|
pub text: String,
|
|
}
|
|
|
|
/// EXPERIMENTAL - response for appending realtime text input.
|
|
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadRealtimeAppendTextResponse {}
|
|
|
|
/// EXPERIMENTAL - stop thread realtime.
|
|
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadRealtimeStopParams {
|
|
pub thread_id: String,
|
|
}
|
|
|
|
/// EXPERIMENTAL - response for stopping thread realtime.
|
|
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadRealtimeStopResponse {}
|
|
|
|
/// EXPERIMENTAL - emitted when thread realtime startup is accepted.
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadRealtimeStartedNotification {
|
|
pub thread_id: String,
|
|
pub session_id: Option<String>,
|
|
}
|
|
|
|
/// EXPERIMENTAL - raw non-audio thread realtime item emitted by the backend.
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadRealtimeItemAddedNotification {
|
|
pub thread_id: String,
|
|
pub item: JsonValue,
|
|
}
|
|
|
|
/// EXPERIMENTAL - streamed output audio emitted by thread realtime.
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadRealtimeOutputAudioDeltaNotification {
|
|
pub thread_id: String,
|
|
pub audio: ThreadRealtimeAudioChunk,
|
|
}
|
|
|
|
/// EXPERIMENTAL - emitted when thread realtime encounters an error.
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadRealtimeErrorNotification {
|
|
pub thread_id: String,
|
|
pub message: String,
|
|
}
|
|
|
|
/// EXPERIMENTAL - emitted when thread realtime transport closes.
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadRealtimeClosedNotification {
|
|
pub thread_id: String,
|
|
pub reason: Option<String>,
|
|
}
|
|
|
|
#[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, Default, Clone, PartialEq, JsonSchema, TS, ExperimentalApi,
|
|
)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct TurnStartParams {
|
|
pub thread_id: String,
|
|
pub input: Vec<UserInput>,
|
|
/// Override the working directory for this turn and subsequent turns.
|
|
#[ts(optional = nullable)]
|
|
pub cwd: Option<PathBuf>,
|
|
/// Override the approval policy for this turn and subsequent turns.
|
|
#[ts(optional = nullable)]
|
|
pub approval_policy: Option<AskForApproval>,
|
|
/// Override the sandbox policy for this turn and subsequent turns.
|
|
#[ts(optional = nullable)]
|
|
pub sandbox_policy: Option<SandboxPolicy>,
|
|
/// 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 = "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<Option<ServiceTier>>,
|
|
/// 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 ReviewStartParams {
|
|
pub thread_id: String,
|
|
pub target: ReviewTarget,
|
|
|
|
/// Where to run the review: inline (default) on the current thread or
|
|
/// detached on a new thread (returned in `reviewThreadId`).
|
|
#[serde(default)]
|
|
#[ts(optional = nullable)]
|
|
pub delivery: Option<ReviewDelivery>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ReviewStartResponse {
|
|
pub turn: Turn,
|
|
/// Identifies the thread where the review runs.
|
|
///
|
|
/// For inline reviews, this is the original thread id.
|
|
/// For detached reviews, this is the id of the new review thread.
|
|
pub review_thread_id: String,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(tag = "type", rename_all = "camelCase")]
|
|
#[ts(tag = "type", export_to = "v2/")]
|
|
pub enum ReviewTarget {
|
|
/// Review the working tree: staged, unstaged, and untracked files.
|
|
UncommittedChanges,
|
|
|
|
/// Review changes between the current branch and the given base branch.
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(rename_all = "camelCase")]
|
|
BaseBranch { branch: String },
|
|
|
|
/// Review the changes introduced by a specific commit.
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(rename_all = "camelCase")]
|
|
Commit {
|
|
sha: String,
|
|
/// Optional human-readable label (e.g., commit subject) for UIs.
|
|
title: Option<String>,
|
|
},
|
|
|
|
/// Arbitrary instructions, equivalent to the old free-form prompt.
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(rename_all = "camelCase")]
|
|
Custom { instructions: String },
|
|
}
|
|
|
|
#[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)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct TurnSteerParams {
|
|
pub thread_id: String,
|
|
pub input: Vec<UserInput>,
|
|
/// 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 {
|
|
url: String,
|
|
},
|
|
LocalImage {
|
|
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 } => CoreUserInput::Image { image_url: url },
|
|
UserInput::LocalImage { path } => CoreUserInput::LocalImage { path },
|
|
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 } => UserInput::Image { url: image_url },
|
|
CoreUserInput::LocalImage { path } => UserInput::LocalImage { path },
|
|
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(tag = "type", rename_all = "camelCase")]
|
|
#[ts(tag = "type")]
|
|
#[ts(export_to = "v2/")]
|
|
pub enum ThreadItem {
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(rename_all = "camelCase")]
|
|
UserMessage { id: String, content: Vec<UserInput> },
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(rename_all = "camelCase")]
|
|
AgentMessage {
|
|
id: String,
|
|
text: String,
|
|
#[serde(default)]
|
|
phase: Option<MessagePhase>,
|
|
},
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(rename_all = "camelCase")]
|
|
/// EXPERIMENTAL - proposed plan item content. The completed plan item is
|
|
/// authoritative and may not match the concatenation of `PlanDelta` text.
|
|
Plan { id: String, text: String },
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(rename_all = "camelCase")]
|
|
Reasoning {
|
|
id: String,
|
|
#[serde(default)]
|
|
summary: Vec<String>,
|
|
#[serde(default)]
|
|
content: Vec<String>,
|
|
},
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(rename_all = "camelCase")]
|
|
CommandExecution {
|
|
id: String,
|
|
/// The command to be executed.
|
|
command: String,
|
|
/// The command's working directory.
|
|
cwd: PathBuf,
|
|
/// Identifier for the underlying PTY process (when available).
|
|
process_id: Option<String>,
|
|
status: CommandExecutionStatus,
|
|
/// A best-effort parsing of the command to understand the action(s) it will perform.
|
|
/// This returns a list of CommandAction objects because a single shell command may
|
|
/// be composed of many commands piped together.
|
|
command_actions: Vec<CommandAction>,
|
|
/// The command's output, aggregated from stdout and stderr.
|
|
aggregated_output: Option<String>,
|
|
/// The command's exit code.
|
|
exit_code: Option<i32>,
|
|
/// The duration of the command execution in milliseconds.
|
|
#[ts(type = "number | null")]
|
|
duration_ms: Option<i64>,
|
|
},
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(rename_all = "camelCase")]
|
|
FileChange {
|
|
id: String,
|
|
changes: Vec<FileUpdateChange>,
|
|
status: PatchApplyStatus,
|
|
},
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(rename_all = "camelCase")]
|
|
McpToolCall {
|
|
id: String,
|
|
server: String,
|
|
tool: String,
|
|
status: McpToolCallStatus,
|
|
arguments: JsonValue,
|
|
result: Option<McpToolCallResult>,
|
|
error: Option<McpToolCallError>,
|
|
/// The duration of the MCP tool call in milliseconds.
|
|
#[ts(type = "number | null")]
|
|
duration_ms: Option<i64>,
|
|
},
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(rename_all = "camelCase")]
|
|
DynamicToolCall {
|
|
id: String,
|
|
tool: String,
|
|
arguments: JsonValue,
|
|
status: DynamicToolCallStatus,
|
|
content_items: Option<Vec<DynamicToolCallOutputContentItem>>,
|
|
success: Option<bool>,
|
|
/// The duration of the dynamic tool call in milliseconds.
|
|
#[ts(type = "number | null")]
|
|
duration_ms: Option<i64>,
|
|
},
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(rename_all = "camelCase")]
|
|
CollabAgentToolCall {
|
|
/// Unique identifier for this collab tool call.
|
|
id: String,
|
|
/// Name of the collab tool that was invoked.
|
|
tool: CollabAgentTool,
|
|
/// Current status of the collab tool call.
|
|
status: CollabAgentToolCallStatus,
|
|
/// Thread ID of the agent issuing the collab request.
|
|
sender_thread_id: String,
|
|
/// Thread ID of the receiving agent, when applicable. In case of spawn operation,
|
|
/// this corresponds to the newly spawned agent.
|
|
receiver_thread_ids: Vec<String>,
|
|
/// Prompt text sent as part of the collab tool call, when available.
|
|
prompt: Option<String>,
|
|
/// Last known status of the target agents, when available.
|
|
agents_states: HashMap<String, CollabAgentState>,
|
|
},
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(rename_all = "camelCase")]
|
|
WebSearch {
|
|
id: String,
|
|
query: String,
|
|
action: Option<WebSearchAction>,
|
|
},
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(rename_all = "camelCase")]
|
|
ImageView { id: String, path: String },
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(rename_all = "camelCase")]
|
|
ImageGeneration {
|
|
id: String,
|
|
status: String,
|
|
revised_prompt: Option<String>,
|
|
result: String,
|
|
},
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(rename_all = "camelCase")]
|
|
EnteredReviewMode { id: String, review: String },
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(rename_all = "camelCase")]
|
|
ExitedReviewMode { id: String, review: String },
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(rename_all = "camelCase")]
|
|
ContextCompaction { id: String },
|
|
}
|
|
|
|
impl ThreadItem {
|
|
pub fn id(&self) -> &str {
|
|
match self {
|
|
ThreadItem::UserMessage { id, .. }
|
|
| ThreadItem::AgentMessage { id, .. }
|
|
| ThreadItem::Plan { id, .. }
|
|
| ThreadItem::Reasoning { id, .. }
|
|
| ThreadItem::CommandExecution { id, .. }
|
|
| ThreadItem::FileChange { id, .. }
|
|
| ThreadItem::McpToolCall { id, .. }
|
|
| ThreadItem::DynamicToolCall { id, .. }
|
|
| ThreadItem::CollabAgentToolCall { id, .. }
|
|
| ThreadItem::WebSearch { id, .. }
|
|
| ThreadItem::ImageView { id, .. }
|
|
| ThreadItem::ImageGeneration { id, .. }
|
|
| ThreadItem::EnteredReviewMode { id, .. }
|
|
| ThreadItem::ExitedReviewMode { id, .. }
|
|
| ThreadItem::ContextCompaction { id, .. } => id,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(tag = "type", rename_all = "camelCase")]
|
|
#[ts(tag = "type", rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub enum WebSearchAction {
|
|
Search {
|
|
query: Option<String>,
|
|
queries: Option<Vec<String>>,
|
|
},
|
|
OpenPage {
|
|
url: Option<String>,
|
|
},
|
|
FindInPage {
|
|
url: Option<String>,
|
|
pattern: Option<String>,
|
|
},
|
|
#[serde(other)]
|
|
Other,
|
|
}
|
|
|
|
impl From<codex_protocol::models::WebSearchAction> for WebSearchAction {
|
|
fn from(value: codex_protocol::models::WebSearchAction) -> Self {
|
|
match value {
|
|
codex_protocol::models::WebSearchAction::Search { query, queries } => {
|
|
WebSearchAction::Search { query, queries }
|
|
}
|
|
codex_protocol::models::WebSearchAction::OpenPage { url } => {
|
|
WebSearchAction::OpenPage { url }
|
|
}
|
|
codex_protocol::models::WebSearchAction::FindInPage { url, pattern } => {
|
|
WebSearchAction::FindInPage { url, pattern }
|
|
}
|
|
codex_protocol::models::WebSearchAction::Other => WebSearchAction::Other,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<CoreTurnItem> for ThreadItem {
|
|
fn from(value: CoreTurnItem) -> Self {
|
|
match value {
|
|
CoreTurnItem::UserMessage(user) => ThreadItem::UserMessage {
|
|
id: user.id,
|
|
content: user.content.into_iter().map(UserInput::from).collect(),
|
|
},
|
|
CoreTurnItem::AgentMessage(agent) => {
|
|
let text = agent
|
|
.content
|
|
.into_iter()
|
|
.map(|entry| match entry {
|
|
CoreAgentMessageContent::Text { text } => text,
|
|
})
|
|
.collect::<String>();
|
|
ThreadItem::AgentMessage {
|
|
id: agent.id,
|
|
text,
|
|
phase: agent.phase,
|
|
}
|
|
}
|
|
CoreTurnItem::Plan(plan) => ThreadItem::Plan {
|
|
id: plan.id,
|
|
text: plan.text,
|
|
},
|
|
CoreTurnItem::Reasoning(reasoning) => ThreadItem::Reasoning {
|
|
id: reasoning.id,
|
|
summary: reasoning.summary_text,
|
|
content: reasoning.raw_content,
|
|
},
|
|
CoreTurnItem::WebSearch(search) => ThreadItem::WebSearch {
|
|
id: search.id,
|
|
query: search.query,
|
|
action: Some(WebSearchAction::from(search.action)),
|
|
},
|
|
CoreTurnItem::ImageGeneration(image) => ThreadItem::ImageGeneration {
|
|
id: image.id,
|
|
status: image.status,
|
|
revised_prompt: image.revised_prompt,
|
|
result: image.result,
|
|
},
|
|
CoreTurnItem::ContextCompaction(compaction) => {
|
|
ThreadItem::ContextCompaction { id: compaction.id }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub enum CommandExecutionStatus {
|
|
InProgress,
|
|
Completed,
|
|
Failed,
|
|
Declined,
|
|
}
|
|
|
|
impl From<CoreExecCommandStatus> for CommandExecutionStatus {
|
|
fn from(value: CoreExecCommandStatus) -> Self {
|
|
Self::from(&value)
|
|
}
|
|
}
|
|
|
|
impl From<&CoreExecCommandStatus> for CommandExecutionStatus {
|
|
fn from(value: &CoreExecCommandStatus) -> Self {
|
|
match value {
|
|
CoreExecCommandStatus::Completed => CommandExecutionStatus::Completed,
|
|
CoreExecCommandStatus::Failed => CommandExecutionStatus::Failed,
|
|
CoreExecCommandStatus::Declined => CommandExecutionStatus::Declined,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub enum CollabAgentTool {
|
|
SpawnAgent,
|
|
SendInput,
|
|
ResumeAgent,
|
|
Wait,
|
|
CloseAgent,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct FileUpdateChange {
|
|
pub path: String,
|
|
pub kind: PatchChangeKind,
|
|
pub diff: String,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(tag = "type", rename_all = "camelCase")]
|
|
#[ts(tag = "type")]
|
|
#[ts(export_to = "v2/")]
|
|
pub enum PatchChangeKind {
|
|
Add,
|
|
Delete,
|
|
Update { move_path: Option<PathBuf> },
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub enum PatchApplyStatus {
|
|
InProgress,
|
|
Completed,
|
|
Failed,
|
|
Declined,
|
|
}
|
|
|
|
impl From<CorePatchApplyStatus> for PatchApplyStatus {
|
|
fn from(value: CorePatchApplyStatus) -> Self {
|
|
Self::from(&value)
|
|
}
|
|
}
|
|
|
|
impl From<&CorePatchApplyStatus> for PatchApplyStatus {
|
|
fn from(value: &CorePatchApplyStatus) -> Self {
|
|
match value {
|
|
CorePatchApplyStatus::Completed => PatchApplyStatus::Completed,
|
|
CorePatchApplyStatus::Failed => PatchApplyStatus::Failed,
|
|
CorePatchApplyStatus::Declined => PatchApplyStatus::Declined,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub enum McpToolCallStatus {
|
|
InProgress,
|
|
Completed,
|
|
Failed,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub enum DynamicToolCallStatus {
|
|
InProgress,
|
|
Completed,
|
|
Failed,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub enum CollabAgentToolCallStatus {
|
|
InProgress,
|
|
Completed,
|
|
Failed,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub enum CollabAgentStatus {
|
|
PendingInit,
|
|
Running,
|
|
Completed,
|
|
Errored,
|
|
Shutdown,
|
|
NotFound,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct CollabAgentState {
|
|
pub status: CollabAgentStatus,
|
|
pub message: Option<String>,
|
|
}
|
|
|
|
impl From<CoreAgentStatus> for CollabAgentState {
|
|
fn from(value: CoreAgentStatus) -> Self {
|
|
match value {
|
|
CoreAgentStatus::PendingInit => Self {
|
|
status: CollabAgentStatus::PendingInit,
|
|
message: None,
|
|
},
|
|
CoreAgentStatus::Running => Self {
|
|
status: CollabAgentStatus::Running,
|
|
message: None,
|
|
},
|
|
CoreAgentStatus::Completed(message) => Self {
|
|
status: CollabAgentStatus::Completed,
|
|
message,
|
|
},
|
|
CoreAgentStatus::Errored(message) => Self {
|
|
status: CollabAgentStatus::Errored,
|
|
message: Some(message),
|
|
},
|
|
CoreAgentStatus::Shutdown => Self {
|
|
status: CollabAgentStatus::Shutdown,
|
|
message: None,
|
|
},
|
|
CoreAgentStatus::NotFound => Self {
|
|
status: CollabAgentStatus::NotFound,
|
|
message: None,
|
|
},
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct McpToolCallResult {
|
|
// NOTE: `rmcp::model::Content` (and its `RawContent` variants) would be a more precise Rust
|
|
// representation of MCP content blocks. We intentionally use `serde_json::Value` here because
|
|
// this crate exports JSON schema + TS types (`schemars`/`ts-rs`), and the rmcp model types
|
|
// aren't set up to be schema/TS friendly (and would introduce heavier coupling to rmcp's Rust
|
|
// representations). Using `JsonValue` keeps the payload wire-shaped and easy to export.
|
|
pub content: Vec<JsonValue>,
|
|
pub structured_content: Option<JsonValue>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct McpToolCallError {
|
|
pub message: String,
|
|
}
|
|
|
|
// === Server Notifications ===
|
|
// Thread/Turn lifecycle notifications and item progress events
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadStartedNotification {
|
|
pub thread: Thread,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadStatusChangedNotification {
|
|
pub thread_id: String,
|
|
pub status: ThreadStatus,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadArchivedNotification {
|
|
pub thread_id: String,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadUnarchivedNotification {
|
|
pub thread_id: String,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadClosedNotification {
|
|
pub thread_id: String,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
/// Notification emitted when watched local skill files change.
|
|
///
|
|
/// Treat this as an invalidation signal and re-run `skills/list` with the
|
|
/// client's current parameters when refreshed skill metadata is needed.
|
|
pub struct SkillsChangedNotification {}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ThreadNameUpdatedNotification {
|
|
pub thread_id: String,
|
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
#[ts(optional)]
|
|
pub thread_name: Option<String>,
|
|
}
|
|
|
|
#[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,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ItemStartedNotification {
|
|
pub item: ThreadItem,
|
|
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 ItemCompletedNotification {
|
|
pub item: ThreadItem,
|
|
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 RawResponseItemCompletedNotification {
|
|
pub thread_id: String,
|
|
pub turn_id: String,
|
|
pub item: ResponseItem,
|
|
}
|
|
|
|
// Item-specific progress notifications
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct AgentMessageDeltaNotification {
|
|
pub thread_id: String,
|
|
pub turn_id: String,
|
|
pub item_id: String,
|
|
pub delta: String,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
/// EXPERIMENTAL - proposed plan streaming deltas for plan items. Clients should
|
|
/// not assume concatenated deltas match the completed plan item content.
|
|
pub struct PlanDeltaNotification {
|
|
pub thread_id: String,
|
|
pub turn_id: String,
|
|
pub item_id: String,
|
|
pub delta: String,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ReasoningSummaryTextDeltaNotification {
|
|
pub thread_id: String,
|
|
pub turn_id: String,
|
|
pub item_id: String,
|
|
pub delta: String,
|
|
#[ts(type = "number")]
|
|
pub summary_index: i64,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ReasoningSummaryPartAddedNotification {
|
|
pub thread_id: String,
|
|
pub turn_id: String,
|
|
pub item_id: String,
|
|
#[ts(type = "number")]
|
|
pub summary_index: i64,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ReasoningTextDeltaNotification {
|
|
pub thread_id: String,
|
|
pub turn_id: String,
|
|
pub item_id: String,
|
|
pub delta: String,
|
|
#[ts(type = "number")]
|
|
pub content_index: i64,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct TerminalInteractionNotification {
|
|
pub thread_id: String,
|
|
pub turn_id: String,
|
|
pub item_id: String,
|
|
pub process_id: String,
|
|
pub stdin: String,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct CommandExecutionOutputDeltaNotification {
|
|
pub thread_id: String,
|
|
pub turn_id: String,
|
|
pub item_id: String,
|
|
pub delta: String,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct FileChangeOutputDeltaNotification {
|
|
pub thread_id: String,
|
|
pub turn_id: String,
|
|
pub item_id: String,
|
|
pub delta: String,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ServerRequestResolvedNotification {
|
|
pub thread_id: String,
|
|
pub request_id: RequestId,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct McpToolCallProgressNotification {
|
|
pub thread_id: String,
|
|
pub turn_id: String,
|
|
pub item_id: String,
|
|
pub message: String,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct McpServerOauthLoginCompletedNotification {
|
|
pub name: String,
|
|
pub success: bool,
|
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
#[ts(optional)]
|
|
pub error: Option<String>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct WindowsWorldWritableWarningNotification {
|
|
pub sample_paths: Vec<String>,
|
|
pub extra_count: usize,
|
|
pub failed_scan: bool,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub enum WindowsSandboxSetupMode {
|
|
Elevated,
|
|
Unelevated,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct WindowsSandboxSetupStartParams {
|
|
pub mode: WindowsSandboxSetupMode,
|
|
#[ts(optional = nullable)]
|
|
pub cwd: Option<PathBuf>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct WindowsSandboxSetupStartResponse {
|
|
pub started: bool,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct WindowsSandboxSetupCompletedNotification {
|
|
pub mode: WindowsSandboxSetupMode,
|
|
pub success: bool,
|
|
pub error: Option<String>,
|
|
}
|
|
|
|
/// Deprecated: Use `ContextCompaction` item type instead.
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ContextCompactedNotification {
|
|
pub thread_id: String,
|
|
pub turn_id: String,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS, ExperimentalApi)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct CommandExecutionRequestApprovalParams {
|
|
pub thread_id: String,
|
|
pub turn_id: String,
|
|
pub item_id: String,
|
|
/// Unique identifier for this specific approval callback.
|
|
///
|
|
/// For regular shell/unified_exec approvals, this is null.
|
|
///
|
|
/// For zsh-exec-bridge subcommand approvals, multiple callbacks can belong to
|
|
/// one parent `itemId`, so `approvalId` is a distinct opaque callback id
|
|
/// (a UUID) used to disambiguate routing.
|
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
#[ts(optional = nullable)]
|
|
pub approval_id: Option<String>,
|
|
/// Optional explanatory reason (e.g. request for network access).
|
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
#[ts(optional = nullable)]
|
|
pub reason: Option<String>,
|
|
/// Optional context for a managed-network approval prompt.
|
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
#[ts(optional = nullable)]
|
|
pub network_approval_context: Option<NetworkApprovalContext>,
|
|
/// The command to be executed.
|
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
#[ts(optional = nullable)]
|
|
pub command: Option<String>,
|
|
/// The command's working directory.
|
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
#[ts(optional = nullable)]
|
|
pub cwd: Option<PathBuf>,
|
|
/// Best-effort parsed command actions for friendly display.
|
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
#[ts(optional = nullable)]
|
|
pub command_actions: Option<Vec<CommandAction>>,
|
|
/// Optional additional permissions requested for this command.
|
|
#[experimental("item/commandExecution/requestApproval.additionalPermissions")]
|
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
#[ts(optional = nullable)]
|
|
pub additional_permissions: Option<AdditionalPermissionProfile>,
|
|
/// Optional proposed execpolicy amendment to allow similar commands without prompting.
|
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
#[ts(optional = nullable)]
|
|
pub proposed_execpolicy_amendment: Option<ExecPolicyAmendment>,
|
|
/// Optional proposed network policy amendments (allow/deny host) for future requests.
|
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
#[ts(optional = nullable)]
|
|
pub proposed_network_policy_amendments: Option<Vec<NetworkPolicyAmendment>>,
|
|
/// Ordered list of decisions the client may present for this prompt.
|
|
#[experimental("item/commandExecution/requestApproval.availableDecisions")]
|
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
#[ts(optional = nullable)]
|
|
pub available_decisions: Option<Vec<CommandExecutionApprovalDecision>>,
|
|
}
|
|
|
|
impl CommandExecutionRequestApprovalParams {
|
|
pub fn strip_experimental_fields(&mut self) {
|
|
// TODO: Avoid hardcoding individual experimental fields here.
|
|
// We need a generic outbound compatibility design for stripping or
|
|
// otherwise handling experimental server->client payloads.
|
|
self.additional_permissions = None;
|
|
}
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct CommandExecutionRequestApprovalResponse {
|
|
pub decision: CommandExecutionApprovalDecision,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct FileChangeRequestApprovalParams {
|
|
pub thread_id: String,
|
|
pub turn_id: String,
|
|
pub item_id: String,
|
|
/// Optional explanatory reason (e.g. request for extra write access).
|
|
#[ts(optional = nullable)]
|
|
pub reason: Option<String>,
|
|
/// [UNSTABLE] When set, the agent is asking the user to allow writes under this root
|
|
/// for the remainder of the session (unclear if this is honored today).
|
|
#[ts(optional = nullable)]
|
|
pub grant_root: Option<PathBuf>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct FileChangeRequestApprovalResponse {
|
|
pub decision: FileChangeApprovalDecision,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub enum McpServerElicitationAction {
|
|
Accept,
|
|
Decline,
|
|
Cancel,
|
|
}
|
|
|
|
impl McpServerElicitationAction {
|
|
pub fn to_core(self) -> codex_protocol::approvals::ElicitationAction {
|
|
match self {
|
|
Self::Accept => codex_protocol::approvals::ElicitationAction::Accept,
|
|
Self::Decline => codex_protocol::approvals::ElicitationAction::Decline,
|
|
Self::Cancel => codex_protocol::approvals::ElicitationAction::Cancel,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<McpServerElicitationAction> for rmcp::model::ElicitationAction {
|
|
fn from(value: McpServerElicitationAction) -> Self {
|
|
match value {
|
|
McpServerElicitationAction::Accept => Self::Accept,
|
|
McpServerElicitationAction::Decline => Self::Decline,
|
|
McpServerElicitationAction::Cancel => Self::Cancel,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<rmcp::model::ElicitationAction> for McpServerElicitationAction {
|
|
fn from(value: rmcp::model::ElicitationAction) -> Self {
|
|
match value {
|
|
rmcp::model::ElicitationAction::Accept => Self::Accept,
|
|
rmcp::model::ElicitationAction::Decline => Self::Decline,
|
|
rmcp::model::ElicitationAction::Cancel => Self::Cancel,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct McpServerElicitationRequestParams {
|
|
pub thread_id: String,
|
|
/// Active Codex turn when this elicitation was observed, if app-server could correlate one.
|
|
///
|
|
/// This is nullable because MCP models elicitation as a standalone server-to-client request
|
|
/// identified by the MCP server request id. It may be triggered during a turn, but turn
|
|
/// context is app-server correlation rather than part of the protocol identity of the
|
|
/// elicitation itself.
|
|
pub turn_id: Option<String>,
|
|
pub server_name: String,
|
|
#[serde(flatten)]
|
|
pub request: McpServerElicitationRequest,
|
|
// TODO: When core can correlate an elicitation with an MCP tool call, expose the associated
|
|
// McpToolCall item id here as an optional field. The current core event does not carry that
|
|
// association.
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(tag = "mode", rename_all = "camelCase")]
|
|
#[ts(tag = "mode")]
|
|
#[ts(export_to = "v2/")]
|
|
pub enum McpServerElicitationRequest {
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(rename_all = "camelCase")]
|
|
Form {
|
|
message: String,
|
|
requested_schema: JsonValue,
|
|
},
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(rename_all = "camelCase")]
|
|
Url {
|
|
message: String,
|
|
url: String,
|
|
elicitation_id: String,
|
|
},
|
|
}
|
|
|
|
impl From<CoreElicitationRequest> for McpServerElicitationRequest {
|
|
fn from(value: CoreElicitationRequest) -> Self {
|
|
match value {
|
|
CoreElicitationRequest::Form {
|
|
message,
|
|
requested_schema,
|
|
} => Self::Form {
|
|
message,
|
|
requested_schema,
|
|
},
|
|
CoreElicitationRequest::Url {
|
|
message,
|
|
url,
|
|
elicitation_id,
|
|
} => Self::Url {
|
|
message,
|
|
url,
|
|
elicitation_id,
|
|
},
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct McpServerElicitationRequestResponse {
|
|
pub action: McpServerElicitationAction,
|
|
/// Structured user input for accepted elicitations, mirroring RMCP `CreateElicitationResult`.
|
|
///
|
|
/// This is nullable because decline/cancel responses have no content.
|
|
pub content: Option<JsonValue>,
|
|
}
|
|
|
|
impl From<McpServerElicitationRequestResponse> for rmcp::model::CreateElicitationResult {
|
|
fn from(value: McpServerElicitationRequestResponse) -> Self {
|
|
Self {
|
|
action: value.action.into(),
|
|
content: value.content,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<rmcp::model::CreateElicitationResult> for McpServerElicitationRequestResponse {
|
|
fn from(value: rmcp::model::CreateElicitationResult) -> Self {
|
|
Self {
|
|
action: value.action.into(),
|
|
content: value.content,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct DynamicToolCallParams {
|
|
pub thread_id: String,
|
|
pub turn_id: String,
|
|
pub call_id: String,
|
|
pub tool: String,
|
|
pub arguments: JsonValue,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct DynamicToolCallResponse {
|
|
pub content_items: Vec<DynamicToolCallOutputContentItem>,
|
|
pub success: bool,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(tag = "type", rename_all = "camelCase")]
|
|
#[ts(tag = "type")]
|
|
#[ts(export_to = "v2/")]
|
|
pub enum DynamicToolCallOutputContentItem {
|
|
#[serde(rename_all = "camelCase")]
|
|
InputText { text: String },
|
|
#[serde(rename_all = "camelCase")]
|
|
InputImage { image_url: String },
|
|
}
|
|
|
|
impl From<DynamicToolCallOutputContentItem>
|
|
for codex_protocol::dynamic_tools::DynamicToolCallOutputContentItem
|
|
{
|
|
fn from(item: DynamicToolCallOutputContentItem) -> Self {
|
|
match item {
|
|
DynamicToolCallOutputContentItem::InputText { text } => Self::InputText { text },
|
|
DynamicToolCallOutputContentItem::InputImage { image_url } => {
|
|
Self::InputImage { image_url }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
/// EXPERIMENTAL. Defines a single selectable option for request_user_input.
|
|
pub struct ToolRequestUserInputOption {
|
|
pub label: String,
|
|
pub description: String,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
/// EXPERIMENTAL. Represents one request_user_input question and its required options.
|
|
pub struct ToolRequestUserInputQuestion {
|
|
pub id: String,
|
|
pub header: String,
|
|
pub question: String,
|
|
#[serde(default)]
|
|
pub is_other: bool,
|
|
#[serde(default)]
|
|
pub is_secret: bool,
|
|
pub options: Option<Vec<ToolRequestUserInputOption>>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
/// EXPERIMENTAL. Params sent with a request_user_input event.
|
|
pub struct ToolRequestUserInputParams {
|
|
pub thread_id: String,
|
|
pub turn_id: String,
|
|
pub item_id: String,
|
|
pub questions: Vec<ToolRequestUserInputQuestion>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
/// EXPERIMENTAL. Captures a user's answer to a request_user_input question.
|
|
pub struct ToolRequestUserInputAnswer {
|
|
pub answers: Vec<String>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
/// EXPERIMENTAL. Response payload mapping question ids to answers.
|
|
pub struct ToolRequestUserInputResponse {
|
|
pub answers: HashMap<String, ToolRequestUserInputAnswer>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct AccountRateLimitsUpdatedNotification {
|
|
pub rate_limits: RateLimitSnapshot,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct RateLimitSnapshot {
|
|
pub limit_id: Option<String>,
|
|
pub limit_name: Option<String>,
|
|
pub primary: Option<RateLimitWindow>,
|
|
pub secondary: Option<RateLimitWindow>,
|
|
pub credits: Option<CreditsSnapshot>,
|
|
pub plan_type: Option<PlanType>,
|
|
}
|
|
|
|
impl From<CoreRateLimitSnapshot> for RateLimitSnapshot {
|
|
fn from(value: CoreRateLimitSnapshot) -> Self {
|
|
Self {
|
|
limit_id: value.limit_id,
|
|
limit_name: value.limit_name,
|
|
primary: value.primary.map(RateLimitWindow::from),
|
|
secondary: value.secondary.map(RateLimitWindow::from),
|
|
credits: value.credits.map(CreditsSnapshot::from),
|
|
plan_type: value.plan_type,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct RateLimitWindow {
|
|
pub used_percent: i32,
|
|
#[ts(type = "number | null")]
|
|
pub window_duration_mins: Option<i64>,
|
|
#[ts(type = "number | null")]
|
|
pub resets_at: Option<i64>,
|
|
}
|
|
|
|
impl From<CoreRateLimitWindow> for RateLimitWindow {
|
|
fn from(value: CoreRateLimitWindow) -> Self {
|
|
Self {
|
|
used_percent: value.used_percent.round() as i32,
|
|
window_duration_mins: value.window_minutes,
|
|
resets_at: value.resets_at,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct CreditsSnapshot {
|
|
pub has_credits: bool,
|
|
pub unlimited: bool,
|
|
pub balance: Option<String>,
|
|
}
|
|
|
|
impl From<CoreCreditsSnapshot> for CreditsSnapshot {
|
|
fn from(value: CoreCreditsSnapshot) -> Self {
|
|
Self {
|
|
has_credits: value.has_credits,
|
|
unlimited: value.unlimited,
|
|
balance: value.balance,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct AccountLoginCompletedNotification {
|
|
// Use plain String for identifiers to avoid TS/JSON Schema quirks around uuid-specific types.
|
|
// Convert to/from UUIDs at the application layer as needed.
|
|
pub login_id: Option<String>,
|
|
pub success: bool,
|
|
pub error: Option<String>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ModelReroutedNotification {
|
|
pub thread_id: String,
|
|
pub turn_id: String,
|
|
pub from_model: String,
|
|
pub to_model: String,
|
|
pub reason: ModelRerouteReason,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct DeprecationNoticeNotification {
|
|
/// Concise summary of what is deprecated.
|
|
pub summary: String,
|
|
/// Optional extra guidance, such as migration steps or rationale.
|
|
pub details: Option<String>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct TextPosition {
|
|
/// 1-based line number.
|
|
pub line: usize,
|
|
/// 1-based column number (in Unicode scalar values).
|
|
pub column: usize,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct TextRange {
|
|
pub start: TextPosition,
|
|
pub end: TextPosition,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ConfigWarningNotification {
|
|
/// Concise summary of the warning.
|
|
pub summary: String,
|
|
/// Optional extra guidance or error details.
|
|
pub details: Option<String>,
|
|
/// Optional path to the config file that triggered the warning.
|
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
#[ts(optional)]
|
|
pub path: Option<String>,
|
|
/// Optional range for the error location inside the config file.
|
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
|
#[ts(optional)]
|
|
pub range: Option<TextRange>,
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use codex_protocol::items::AgentMessageContent;
|
|
use codex_protocol::items::AgentMessageItem;
|
|
use codex_protocol::items::ReasoningItem;
|
|
use codex_protocol::items::TurnItem;
|
|
use codex_protocol::items::UserMessageItem;
|
|
use codex_protocol::items::WebSearchItem;
|
|
use codex_protocol::models::WebSearchAction as CoreWebSearchAction;
|
|
use codex_protocol::protocol::NetworkAccess as CoreNetworkAccess;
|
|
use codex_protocol::protocol::ReadOnlyAccess as CoreReadOnlyAccess;
|
|
use codex_protocol::user_input::UserInput as CoreUserInput;
|
|
use pretty_assertions::assert_eq;
|
|
use serde_json::json;
|
|
use std::path::PathBuf;
|
|
|
|
fn test_absolute_path() -> AbsolutePathBuf {
|
|
let path = if cfg!(windows) {
|
|
r"C:\readable"
|
|
} else {
|
|
"/readable"
|
|
};
|
|
AbsolutePathBuf::from_absolute_path(path).expect("path must be absolute")
|
|
}
|
|
|
|
#[test]
|
|
fn command_execution_request_approval_rejects_relative_additional_permission_paths() {
|
|
let err = serde_json::from_value::<CommandExecutionRequestApprovalParams>(json!({
|
|
"threadId": "thr_123",
|
|
"turnId": "turn_123",
|
|
"itemId": "call_123",
|
|
"command": "cat file",
|
|
"cwd": "/tmp",
|
|
"commandActions": null,
|
|
"reason": null,
|
|
"networkApprovalContext": null,
|
|
"additionalPermissions": {
|
|
"network": null,
|
|
"fileSystem": {
|
|
"read": ["relative/path"],
|
|
"write": null
|
|
},
|
|
"macos": null
|
|
},
|
|
"proposedExecpolicyAmendment": null,
|
|
"proposedNetworkPolicyAmendments": null,
|
|
"availableDecisions": null
|
|
}))
|
|
.expect_err("relative additional permission paths should fail");
|
|
assert!(
|
|
err.to_string()
|
|
.contains("AbsolutePathBuf deserialized without a base path"),
|
|
"unexpected error: {err}"
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn command_execution_request_approval_accepts_macos_automation_bundle_ids_object() {
|
|
let params = serde_json::from_value::<CommandExecutionRequestApprovalParams>(json!({
|
|
"threadId": "thr_123",
|
|
"turnId": "turn_123",
|
|
"itemId": "call_123",
|
|
"command": "cat file",
|
|
"cwd": "/tmp",
|
|
"commandActions": null,
|
|
"reason": null,
|
|
"networkApprovalContext": null,
|
|
"additionalPermissions": {
|
|
"network": null,
|
|
"fileSystem": null,
|
|
"macos": {
|
|
"preferences": "read_only",
|
|
"automations": {
|
|
"bundle_ids": ["com.apple.Notes"]
|
|
},
|
|
"accessibility": false,
|
|
"calendar": false
|
|
}
|
|
},
|
|
"proposedExecpolicyAmendment": null,
|
|
"proposedNetworkPolicyAmendments": null,
|
|
"availableDecisions": null
|
|
}))
|
|
.expect("bundle_ids object should deserialize");
|
|
|
|
assert_eq!(
|
|
params
|
|
.additional_permissions
|
|
.and_then(|permissions| permissions.macos)
|
|
.map(|macos| macos.automations),
|
|
Some(CoreMacOsAutomationPermission::BundleIds(vec![
|
|
"com.apple.Notes".to_string(),
|
|
]))
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn sandbox_policy_round_trips_external_sandbox_network_access() {
|
|
let v2_policy = SandboxPolicy::ExternalSandbox {
|
|
network_access: NetworkAccess::Enabled,
|
|
};
|
|
|
|
let core_policy = v2_policy.to_core();
|
|
assert_eq!(
|
|
core_policy,
|
|
codex_protocol::protocol::SandboxPolicy::ExternalSandbox {
|
|
network_access: CoreNetworkAccess::Enabled,
|
|
}
|
|
);
|
|
|
|
let back_to_v2 = SandboxPolicy::from(core_policy);
|
|
assert_eq!(back_to_v2, v2_policy);
|
|
}
|
|
|
|
#[test]
|
|
fn sandbox_policy_round_trips_read_only_access() {
|
|
let readable_root = test_absolute_path();
|
|
let v2_policy = SandboxPolicy::ReadOnly {
|
|
access: ReadOnlyAccess::Restricted {
|
|
include_platform_defaults: false,
|
|
readable_roots: vec![readable_root.clone()],
|
|
},
|
|
network_access: true,
|
|
};
|
|
|
|
let core_policy = v2_policy.to_core();
|
|
assert_eq!(
|
|
core_policy,
|
|
codex_protocol::protocol::SandboxPolicy::ReadOnly {
|
|
access: CoreReadOnlyAccess::Restricted {
|
|
include_platform_defaults: false,
|
|
readable_roots: vec![readable_root],
|
|
},
|
|
network_access: true,
|
|
}
|
|
);
|
|
|
|
let back_to_v2 = SandboxPolicy::from(core_policy);
|
|
assert_eq!(back_to_v2, v2_policy);
|
|
}
|
|
|
|
#[test]
|
|
fn mcp_server_elicitation_response_round_trips_rmcp_result() {
|
|
let rmcp_result = rmcp::model::CreateElicitationResult {
|
|
action: rmcp::model::ElicitationAction::Accept,
|
|
content: Some(json!({
|
|
"confirmed": true,
|
|
})),
|
|
};
|
|
|
|
let v2_response = McpServerElicitationRequestResponse::from(rmcp_result.clone());
|
|
assert_eq!(
|
|
v2_response,
|
|
McpServerElicitationRequestResponse {
|
|
action: McpServerElicitationAction::Accept,
|
|
content: Some(json!({
|
|
"confirmed": true,
|
|
})),
|
|
}
|
|
);
|
|
assert_eq!(
|
|
rmcp::model::CreateElicitationResult::from(v2_response),
|
|
rmcp_result
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn mcp_server_elicitation_request_from_core_url_request() {
|
|
let request = McpServerElicitationRequest::from(CoreElicitationRequest::Url {
|
|
message: "Finish sign-in".to_string(),
|
|
url: "https://example.com/complete".to_string(),
|
|
elicitation_id: "elicitation-123".to_string(),
|
|
});
|
|
|
|
assert_eq!(
|
|
request,
|
|
McpServerElicitationRequest::Url {
|
|
message: "Finish sign-in".to_string(),
|
|
url: "https://example.com/complete".to_string(),
|
|
elicitation_id: "elicitation-123".to_string(),
|
|
}
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn mcp_server_elicitation_response_serializes_nullable_content() {
|
|
let response = McpServerElicitationRequestResponse {
|
|
action: McpServerElicitationAction::Decline,
|
|
content: None,
|
|
};
|
|
|
|
assert_eq!(
|
|
serde_json::to_value(response).expect("response should serialize"),
|
|
json!({
|
|
"action": "decline",
|
|
"content": null,
|
|
})
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn sandbox_policy_round_trips_workspace_write_read_only_access() {
|
|
let readable_root = test_absolute_path();
|
|
let v2_policy = SandboxPolicy::WorkspaceWrite {
|
|
writable_roots: vec![],
|
|
read_only_access: ReadOnlyAccess::Restricted {
|
|
include_platform_defaults: false,
|
|
readable_roots: vec![readable_root.clone()],
|
|
},
|
|
network_access: true,
|
|
exclude_tmpdir_env_var: false,
|
|
exclude_slash_tmp: false,
|
|
};
|
|
|
|
let core_policy = v2_policy.to_core();
|
|
assert_eq!(
|
|
core_policy,
|
|
codex_protocol::protocol::SandboxPolicy::WorkspaceWrite {
|
|
writable_roots: vec![],
|
|
read_only_access: CoreReadOnlyAccess::Restricted {
|
|
include_platform_defaults: false,
|
|
readable_roots: vec![readable_root],
|
|
},
|
|
network_access: true,
|
|
exclude_tmpdir_env_var: false,
|
|
exclude_slash_tmp: false,
|
|
}
|
|
);
|
|
|
|
let back_to_v2 = SandboxPolicy::from(core_policy);
|
|
assert_eq!(back_to_v2, v2_policy);
|
|
}
|
|
|
|
#[test]
|
|
fn sandbox_policy_deserializes_legacy_read_only_without_access_field() {
|
|
let policy: SandboxPolicy = serde_json::from_value(json!({
|
|
"type": "readOnly"
|
|
}))
|
|
.expect("read-only policy should deserialize");
|
|
assert_eq!(
|
|
policy,
|
|
SandboxPolicy::ReadOnly {
|
|
access: ReadOnlyAccess::FullAccess,
|
|
network_access: false,
|
|
}
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn sandbox_policy_deserializes_legacy_workspace_write_without_read_only_access_field() {
|
|
let policy: SandboxPolicy = serde_json::from_value(json!({
|
|
"type": "workspaceWrite",
|
|
"writableRoots": [],
|
|
"networkAccess": false,
|
|
"excludeTmpdirEnvVar": false,
|
|
"excludeSlashTmp": false
|
|
}))
|
|
.expect("workspace-write policy should deserialize");
|
|
assert_eq!(
|
|
policy,
|
|
SandboxPolicy::WorkspaceWrite {
|
|
writable_roots: vec![],
|
|
read_only_access: ReadOnlyAccess::FullAccess,
|
|
network_access: false,
|
|
exclude_tmpdir_env_var: false,
|
|
exclude_slash_tmp: false,
|
|
}
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn core_turn_item_into_thread_item_converts_supported_variants() {
|
|
let user_item = TurnItem::UserMessage(UserMessageItem {
|
|
id: "user-1".to_string(),
|
|
content: vec![
|
|
CoreUserInput::Text {
|
|
text: "hello".to_string(),
|
|
text_elements: Vec::new(),
|
|
},
|
|
CoreUserInput::Image {
|
|
image_url: "https://example.com/image.png".to_string(),
|
|
},
|
|
CoreUserInput::LocalImage {
|
|
path: PathBuf::from("local/image.png"),
|
|
},
|
|
CoreUserInput::Skill {
|
|
name: "skill-creator".to_string(),
|
|
path: PathBuf::from("/repo/.codex/skills/skill-creator/SKILL.md"),
|
|
},
|
|
CoreUserInput::Mention {
|
|
name: "Demo App".to_string(),
|
|
path: "app://demo-app".to_string(),
|
|
},
|
|
],
|
|
});
|
|
|
|
assert_eq!(
|
|
ThreadItem::from(user_item),
|
|
ThreadItem::UserMessage {
|
|
id: "user-1".to_string(),
|
|
content: vec![
|
|
UserInput::Text {
|
|
text: "hello".to_string(),
|
|
text_elements: Vec::new(),
|
|
},
|
|
UserInput::Image {
|
|
url: "https://example.com/image.png".to_string(),
|
|
},
|
|
UserInput::LocalImage {
|
|
path: PathBuf::from("local/image.png"),
|
|
},
|
|
UserInput::Skill {
|
|
name: "skill-creator".to_string(),
|
|
path: PathBuf::from("/repo/.codex/skills/skill-creator/SKILL.md"),
|
|
},
|
|
UserInput::Mention {
|
|
name: "Demo App".to_string(),
|
|
path: "app://demo-app".to_string(),
|
|
},
|
|
],
|
|
}
|
|
);
|
|
|
|
let agent_item = TurnItem::AgentMessage(AgentMessageItem {
|
|
id: "agent-1".to_string(),
|
|
content: vec![
|
|
AgentMessageContent::Text {
|
|
text: "Hello ".to_string(),
|
|
},
|
|
AgentMessageContent::Text {
|
|
text: "world".to_string(),
|
|
},
|
|
],
|
|
phase: None,
|
|
});
|
|
|
|
assert_eq!(
|
|
ThreadItem::from(agent_item),
|
|
ThreadItem::AgentMessage {
|
|
id: "agent-1".to_string(),
|
|
text: "Hello world".to_string(),
|
|
phase: None,
|
|
}
|
|
);
|
|
|
|
let agent_item_with_phase = TurnItem::AgentMessage(AgentMessageItem {
|
|
id: "agent-2".to_string(),
|
|
content: vec![AgentMessageContent::Text {
|
|
text: "final".to_string(),
|
|
}],
|
|
phase: Some(MessagePhase::FinalAnswer),
|
|
});
|
|
|
|
assert_eq!(
|
|
ThreadItem::from(agent_item_with_phase),
|
|
ThreadItem::AgentMessage {
|
|
id: "agent-2".to_string(),
|
|
text: "final".to_string(),
|
|
phase: Some(MessagePhase::FinalAnswer),
|
|
}
|
|
);
|
|
|
|
let reasoning_item = TurnItem::Reasoning(ReasoningItem {
|
|
id: "reasoning-1".to_string(),
|
|
summary_text: vec!["line one".to_string(), "line two".to_string()],
|
|
raw_content: vec![],
|
|
});
|
|
|
|
assert_eq!(
|
|
ThreadItem::from(reasoning_item),
|
|
ThreadItem::Reasoning {
|
|
id: "reasoning-1".to_string(),
|
|
summary: vec!["line one".to_string(), "line two".to_string()],
|
|
content: vec![],
|
|
}
|
|
);
|
|
|
|
let search_item = TurnItem::WebSearch(WebSearchItem {
|
|
id: "search-1".to_string(),
|
|
query: "docs".to_string(),
|
|
action: CoreWebSearchAction::Search {
|
|
query: Some("docs".to_string()),
|
|
queries: None,
|
|
},
|
|
});
|
|
|
|
assert_eq!(
|
|
ThreadItem::from(search_item),
|
|
ThreadItem::WebSearch {
|
|
id: "search-1".to_string(),
|
|
query: "docs".to_string(),
|
|
action: Some(WebSearchAction::Search {
|
|
query: Some("docs".to_string()),
|
|
queries: None,
|
|
}),
|
|
}
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn skills_list_params_serialization_uses_force_reload() {
|
|
assert_eq!(
|
|
serde_json::to_value(SkillsListParams {
|
|
cwds: Vec::new(),
|
|
force_reload: false,
|
|
per_cwd_extra_user_roots: None,
|
|
})
|
|
.unwrap(),
|
|
json!({
|
|
"perCwdExtraUserRoots": null,
|
|
}),
|
|
);
|
|
|
|
assert_eq!(
|
|
serde_json::to_value(SkillsListParams {
|
|
cwds: vec![PathBuf::from("/repo")],
|
|
force_reload: true,
|
|
per_cwd_extra_user_roots: Some(vec![SkillsListExtraRootsForCwd {
|
|
cwd: PathBuf::from("/repo"),
|
|
extra_user_roots: vec![
|
|
PathBuf::from("/shared/skills"),
|
|
PathBuf::from("/tmp/x")
|
|
],
|
|
}]),
|
|
})
|
|
.unwrap(),
|
|
json!({
|
|
"cwds": ["/repo"],
|
|
"forceReload": true,
|
|
"perCwdExtraUserRoots": [
|
|
{
|
|
"cwd": "/repo",
|
|
"extraUserRoots": ["/shared/skills", "/tmp/x"],
|
|
}
|
|
],
|
|
}),
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn codex_error_info_serializes_http_status_code_in_camel_case() {
|
|
let value = CodexErrorInfo::ResponseTooManyFailedAttempts {
|
|
http_status_code: Some(401),
|
|
};
|
|
|
|
assert_eq!(
|
|
serde_json::to_value(value).unwrap(),
|
|
json!({
|
|
"responseTooManyFailedAttempts": {
|
|
"httpStatusCode": 401
|
|
}
|
|
})
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn dynamic_tool_response_serializes_content_items() {
|
|
let value = serde_json::to_value(DynamicToolCallResponse {
|
|
content_items: vec![DynamicToolCallOutputContentItem::InputText {
|
|
text: "dynamic-ok".to_string(),
|
|
}],
|
|
success: true,
|
|
})
|
|
.unwrap();
|
|
|
|
assert_eq!(
|
|
value,
|
|
json!({
|
|
"contentItems": [
|
|
{
|
|
"type": "inputText",
|
|
"text": "dynamic-ok"
|
|
}
|
|
],
|
|
"success": true,
|
|
})
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn dynamic_tool_response_serializes_text_and_image_content_items() {
|
|
let value = serde_json::to_value(DynamicToolCallResponse {
|
|
content_items: vec![
|
|
DynamicToolCallOutputContentItem::InputText {
|
|
text: "dynamic-ok".to_string(),
|
|
},
|
|
DynamicToolCallOutputContentItem::InputImage {
|
|
image_url: "data:image/png;base64,AAA".to_string(),
|
|
},
|
|
],
|
|
success: true,
|
|
})
|
|
.unwrap();
|
|
|
|
assert_eq!(
|
|
value,
|
|
json!({
|
|
"contentItems": [
|
|
{
|
|
"type": "inputText",
|
|
"text": "dynamic-ok"
|
|
},
|
|
{
|
|
"type": "inputImage",
|
|
"imageUrl": "data:image/png;base64,AAA"
|
|
}
|
|
],
|
|
"success": true,
|
|
})
|
|
);
|
|
}
|
|
|
|
#[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);
|
|
}
|
|
}
|