mirror of
https://github.com/openai/codex.git
synced 2026-10-31 19:28:49 +00:00
## What changed - Allow audio inputs to carry uploaded file references via `fileId` in the app-server API and `file_id` in model content, preserving existing inline audio shapes and Python class names. - Preserve mixed inline and file-backed audio ordering through history reconstruction and migration, and budget file-backed audio conservatively when duration is unknown. - Keep uploaded audio disabled at runtime: reject it before inference or raw history injection, preserve accompanying text, and prevent rejected attachments from reaching follow-up or resumed requests. ## Testing Add coverage for wire-format round trips, SDK compatibility, mixed audio history ordering, and rejection during turn start, steering, task completion, and raw injection. GitOrigin-RevId: 70895c47a2494c0c9936348ad1250eb1b7b892a6
672 lines
24 KiB
Rust
672 lines
24 KiB
Rust
use super::ApprovalsReviewer;
|
|
use super::AskForApproval;
|
|
use super::SandboxPolicy;
|
|
use super::Turn;
|
|
use crate::JsonSchema;
|
|
use crate::TS;
|
|
use codex_experimental_api_macros::ExperimentalApi;
|
|
use codex_protocol::config_types::CollaborationMode;
|
|
use codex_protocol::config_types::MultiAgentMode;
|
|
use codex_protocol::config_types::Personality;
|
|
use codex_protocol::config_types::ReasoningSummary;
|
|
use codex_protocol::models::AudioReference as CoreAudioReference;
|
|
use codex_protocol::models::FunctionCallOutputBody;
|
|
use codex_protocol::models::ImageDetail;
|
|
use codex_protocol::models::ImageReference as CoreImageReference;
|
|
use codex_protocol::openai_models::ReasoningEffort;
|
|
use codex_protocol::plan_tool::PlanItemArg as CorePlanItemArg;
|
|
use codex_protocol::plan_tool::StepStatus as CorePlanStepStatus;
|
|
use codex_protocol::turn_input::CyberAccessProgram as CoreCyberAccessProgram;
|
|
use codex_protocol::user_input::ByteRange as CoreByteRange;
|
|
use codex_protocol::user_input::TextElement as CoreTextElement;
|
|
use codex_protocol::user_input::UserInput as CoreUserInput;
|
|
use codex_utils_absolute_path::AbsolutePathBuf;
|
|
use codex_utils_path_uri::LegacyAppPathString;
|
|
use serde::Deserialize;
|
|
use serde::Serialize;
|
|
use serde_json::Value as JsonValue;
|
|
use std::collections::HashMap;
|
|
use std::path::PathBuf;
|
|
|
|
#[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
|
|
/// Experimental settings changes for one running turn, not future turns.
|
|
/// Unsupported fields are rejected rather than silently ignored.
|
|
/// Any live task kind may accept publication. Child sessions and consumers of
|
|
/// frozen initial settings are unchanged.
|
|
#[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase", deny_unknown_fields)]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct TurnSettingsUpdateParams {
|
|
pub thread_id: String,
|
|
pub turn_id: String,
|
|
/// Changes the active turn's reviewer without changing future thread settings.
|
|
/// Already captured steps and pending approvals retain their original reviewer.
|
|
#[ts(optional = nullable)]
|
|
pub approvals_reviewer: Option<ApprovalsReviewer>,
|
|
/// Omission or `null` leaves the model unchanged.
|
|
#[ts(optional = nullable)]
|
|
pub model: Option<String>,
|
|
/// Omission or `null` leaves the effort unchanged.
|
|
#[ts(optional = nullable)]
|
|
pub effort: Option<ReasoningEffort>,
|
|
/// Omission or `null` leaves the summary preference unchanged.
|
|
#[ts(optional = nullable)]
|
|
pub summary: Option<ReasoningSummary>,
|
|
/// `null` clears the requested tier; omission leaves it unchanged.
|
|
#[serde(
|
|
default,
|
|
deserialize_with = "crate::protocol::serde_helpers::deserialize_double_option",
|
|
serialize_with = "crate::protocol::serde_helpers::serialize_double_option",
|
|
skip_serializing_if = "Option::is_none"
|
|
)]
|
|
#[ts(optional = nullable)]
|
|
pub service_tier: Option<Option<String>>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct TurnSettingsUpdateResponse {
|
|
pub status: TurnSettingsUpdateStatus,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(rename_all = "camelCase", export_to = "v2/")]
|
|
pub enum TurnSettingsUpdateStatus {
|
|
/// Published for subsequent captures, even if the values were unchanged.
|
|
/// Already captured steps are unchanged; a later inference is not guaranteed.
|
|
Applied,
|
|
/// No matching live task remained available for publication.
|
|
TargetUnavailable,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS, ExperimentalApi)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct TurnEnvironmentParams {
|
|
pub environment_id: String,
|
|
pub cwd: LegacyAppPathString,
|
|
/// Environment-native runtime workspace roots. Omitted defaults to `cwd`.
|
|
#[ts(optional = nullable)]
|
|
pub runtime_workspace_roots: Option<Vec<LegacyAppPathString>>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, JsonSchema, TS)]
|
|
#[serde(rename_all = "lowercase")]
|
|
#[ts(rename_all = "lowercase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub enum AdditionalContextKind {
|
|
Untrusted,
|
|
Application,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct AdditionalContextEntry {
|
|
pub value: String,
|
|
pub kind: AdditionalContextKind,
|
|
/// Optional name for untrusted context, used to label its tool output.
|
|
#[ts(optional = nullable)]
|
|
pub name: Option<String>,
|
|
}
|
|
|
|
/// Requested cyber treatment for an OpenAI model turn.
|
|
/// Authorization and model-tier restrictions remain server-owned.
|
|
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub enum CyberAccessProgram {
|
|
Standard,
|
|
DaybreakBlue,
|
|
DaybreakRed,
|
|
}
|
|
|
|
impl From<CyberAccessProgram> for CoreCyberAccessProgram {
|
|
fn from(value: CyberAccessProgram) -> Self {
|
|
match value {
|
|
CyberAccessProgram::Standard => Self::Standard,
|
|
CyberAccessProgram::DaybreakBlue => Self::DaybreakBlue,
|
|
CyberAccessProgram::DaybreakRed => Self::DaybreakRed,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<CoreCyberAccessProgram> for CyberAccessProgram {
|
|
fn from(value: CoreCyberAccessProgram) -> Self {
|
|
match value {
|
|
CoreCyberAccessProgram::Standard => Self::Standard,
|
|
CoreCyberAccessProgram::DaybreakBlue => Self::DaybreakBlue,
|
|
CoreCyberAccessProgram::DaybreakRed => Self::DaybreakRed,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct TurnToolOutput {
|
|
pub name: String,
|
|
pub namespace: Option<String>,
|
|
pub output: FunctionCallOutputBody,
|
|
/// Requests retention of this output in the thread's model history.
|
|
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
|
|
pub retain: bool,
|
|
}
|
|
|
|
#[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,
|
|
/// Replace this thread's disabled plugin IDs.
|
|
/// Omitted/null preserves the list; [] clears it.
|
|
#[ts(optional = nullable)]
|
|
pub disabled_plugin_ids: Option<Vec<String>>,
|
|
#[ts(optional = nullable)]
|
|
pub client_user_message_id: Option<String>,
|
|
pub input: Vec<UserInput>,
|
|
/// Optional source classification for the caller that starts this turn.
|
|
/// Ignored when this request steers an already-active turn.
|
|
#[ts(optional = nullable)]
|
|
pub turn_trigger: Option<String>,
|
|
/// ID of the turn that caused this new turn to start.
|
|
///
|
|
/// Set this when starting work on behalf of another turn, such as delegated
|
|
/// work in a different thread. Leave unset for work started directly by the
|
|
/// user. Ignored when this request adds input to an active turn.
|
|
#[ts(optional = nullable)]
|
|
pub parent_turn_id: Option<String>,
|
|
/// ID of the first turn in the chain of work that led to this new turn.
|
|
///
|
|
/// When setting `parentTurnId`, set this to the parent turn's `rootTurnId`
|
|
/// when known. This keeps descendant work attributed to the original turn.
|
|
/// If omitted, the new turn becomes its own root. Ignored when this request
|
|
/// adds input to an active turn.
|
|
#[ts(optional = nullable)]
|
|
pub root_turn_id: Option<String>,
|
|
#[ts(optional = nullable)]
|
|
pub tool_output: Option<Box<TurnToolOutput>>,
|
|
/// Optional metadata to enrich Codex's ResponsesAPI turn metadata.
|
|
/// Replaces unprotected client-provided entries when supplied; an empty map clears them.
|
|
///
|
|
/// Entries are flattened into the JSON string sent as
|
|
/// `client_metadata["x-codex-turn-metadata"]` on ResponsesAPI HTTP and websocket requests.
|
|
///
|
|
/// They are not sent as top-level ResponsesAPI `client_metadata` keys, and reserved keys
|
|
/// such as `session_id`, `thread_id`, `turn_id`, and `window_id` cannot be overridden.
|
|
#[experimental("turn/start.responsesapiClientMetadata")]
|
|
#[ts(optional = nullable)]
|
|
pub responsesapi_client_metadata: Option<HashMap<String, String>>,
|
|
/// Merge entries into the ResponsesAPI turn metadata without removing existing entries.
|
|
/// Applied after `responsesapiClientMetadata` if both are provided; reserved keys remain protected.
|
|
#[experimental("turn/start.responsesapiClientMetadataPatch")]
|
|
#[ts(optional = nullable)]
|
|
pub responsesapi_client_metadata_patch: Option<HashMap<String, String>>,
|
|
/// Optional client-provided context fragments keyed by an opaque source identifier.
|
|
#[experimental("turn/start.additionalContext")]
|
|
#[ts(optional = nullable)]
|
|
pub additional_context: Option<HashMap<String, AdditionalContextEntry>>,
|
|
/// Optional environments for this turn and subsequent turns.
|
|
///
|
|
/// Omitted uses the thread sticky environments. Empty disables
|
|
/// environment access for this turn. Non-empty selects the first
|
|
/// environment as the current turn environment for this turn.
|
|
#[experimental("turn/start.environments")]
|
|
#[ts(optional = nullable)]
|
|
pub environments: Option<Vec<TurnEnvironmentParams>>,
|
|
/// Override the working directory for this turn and subsequent turns.
|
|
#[ts(optional = nullable)]
|
|
pub cwd: Option<PathBuf>,
|
|
/// Replace the thread's runtime workspace roots for this turn and
|
|
/// subsequent turns. Paths must be absolute.
|
|
#[experimental("turn/start.runtimeWorkspaceRoots")]
|
|
#[ts(optional = nullable)]
|
|
pub runtime_workspace_roots: Option<Vec<AbsolutePathBuf>>,
|
|
/// Override the approval policy for this turn and subsequent turns.
|
|
#[experimental(nested)]
|
|
#[ts(optional = nullable)]
|
|
pub approval_policy: Option<AskForApproval>,
|
|
/// Override where approval requests are routed for review on this turn and
|
|
/// subsequent turns.
|
|
#[ts(optional = nullable)]
|
|
pub approvals_reviewer: Option<ApprovalsReviewer>,
|
|
/// Override the sandbox policy for this turn and subsequent turns.
|
|
#[ts(optional = nullable)]
|
|
pub sandbox_policy: Option<SandboxPolicy>,
|
|
/// Select a named permissions profile id for this turn and subsequent
|
|
/// turns. Cannot be combined with `sandboxPolicy`.
|
|
#[experimental("turn/start.permissions")]
|
|
#[ts(optional = nullable)]
|
|
pub permissions: Option<String>,
|
|
/// Override the model for this turn and subsequent turns.
|
|
#[ts(optional = nullable)]
|
|
pub model: Option<String>,
|
|
/// Override the service tier for this turn and subsequent turns.
|
|
#[serde(
|
|
default,
|
|
deserialize_with = "crate::protocol::serde_helpers::deserialize_double_option",
|
|
serialize_with = "crate::protocol::serde_helpers::serialize_double_option",
|
|
skip_serializing_if = "Option::is_none"
|
|
)]
|
|
#[ts(optional = nullable)]
|
|
pub service_tier: Option<Option<String>>,
|
|
/// Override the service tier only when this request starts a new turn.
|
|
/// Use "default" for standard speed. Omitted or null inherits the thread's tier.
|
|
/// Does not change the thread's tier or a turn being steered.
|
|
#[ts(optional = nullable)]
|
|
pub service_tier_for_turn: Option<String>,
|
|
/// Override the reasoning effort for this turn and subsequent turns.
|
|
#[ts(optional = nullable)]
|
|
pub effort: Option<ReasoningEffort>,
|
|
/// Override the reasoning summary for this turn and subsequent turns.
|
|
#[ts(optional = nullable)]
|
|
pub summary: Option<ReasoningSummary>,
|
|
/// @deprecated `friendly` and `pragmatic` no longer select a style.
|
|
/// Changing this does not rewrite the thread's existing instructions.
|
|
#[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>,
|
|
|
|
/// @deprecated Ignored. Use `effort: "ultra"` for proactive multi-agent behavior.
|
|
#[experimental("turn/start.multiAgentMode")]
|
|
#[ts(optional = nullable)]
|
|
pub multi_agent_mode: Option<MultiAgentMode>,
|
|
|
|
/// EXPERIMENTAL - Request an authorized cyber program for this
|
|
/// turn. Omission preserves automatic behavior. This does not grant access.
|
|
#[experimental("turn/start.cyberAccessProgram")]
|
|
#[ts(optional = nullable)]
|
|
pub cyber_access_program: Option<CyberAccessProgram>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct TurnStartResponse {
|
|
pub turn: Turn,
|
|
}
|
|
|
|
#[derive(
|
|
Serialize, Deserialize, Debug, Default, Clone, PartialEq, JsonSchema, TS, ExperimentalApi,
|
|
)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct TurnSteerParams {
|
|
pub thread_id: String,
|
|
#[ts(optional = nullable)]
|
|
pub client_user_message_id: Option<String>,
|
|
pub input: Vec<UserInput>,
|
|
/// Optional metadata to enrich Codex's ResponsesAPI turn metadata.
|
|
/// Replaces unprotected client-provided entries when supplied; an empty map clears them.
|
|
///
|
|
/// Entries are flattened into the JSON string sent as
|
|
/// `client_metadata["x-codex-turn-metadata"]` on ResponsesAPI HTTP and websocket requests.
|
|
///
|
|
/// They are not sent as top-level ResponsesAPI `client_metadata` keys, and reserved keys
|
|
/// such as `session_id`, `thread_id`, `turn_id`, and `window_id` cannot be overridden.
|
|
#[experimental("turn/steer.responsesapiClientMetadata")]
|
|
#[ts(optional = nullable)]
|
|
pub responsesapi_client_metadata: Option<HashMap<String, String>>,
|
|
/// Merge entries into the ResponsesAPI turn metadata without removing existing entries.
|
|
/// Applied after `responsesapiClientMetadata` if both are provided; reserved keys remain protected.
|
|
#[experimental("turn/steer.responsesapiClientMetadataPatch")]
|
|
#[ts(optional = nullable)]
|
|
pub responsesapi_client_metadata_patch: Option<HashMap<String, String>>,
|
|
/// Optional client-provided context fragments keyed by an opaque source identifier.
|
|
#[experimental("turn/steer.additionalContext")]
|
|
#[ts(optional = nullable)]
|
|
pub additional_context: Option<HashMap<String, AdditionalContextEntry>>,
|
|
/// Required active turn id precondition. The request fails when it does not
|
|
/// match the currently active turn.
|
|
pub expected_turn_id: String,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct TurnSteerResponse {
|
|
pub turn_id: String,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct TurnInterruptParams {
|
|
pub thread_id: String,
|
|
pub turn_id: String,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct TurnInterruptResponse {}
|
|
|
|
// User input types
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct ByteRange {
|
|
pub start: usize,
|
|
pub end: usize,
|
|
}
|
|
|
|
impl From<CoreByteRange> for ByteRange {
|
|
fn from(value: CoreByteRange) -> Self {
|
|
Self {
|
|
start: value.start,
|
|
end: value.end,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<ByteRange> for CoreByteRange {
|
|
fn from(value: ByteRange) -> Self {
|
|
Self {
|
|
start: value.start,
|
|
end: value.end,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct TextElement {
|
|
/// Byte range in the parent `text` buffer that this element occupies.
|
|
pub byte_range: ByteRange,
|
|
/// Optional human-readable placeholder for the element, displayed in the UI.
|
|
placeholder: Option<String>,
|
|
}
|
|
|
|
impl TextElement {
|
|
pub fn new(byte_range: ByteRange, placeholder: Option<String>) -> Self {
|
|
Self {
|
|
byte_range,
|
|
placeholder,
|
|
}
|
|
}
|
|
|
|
pub fn set_placeholder(&mut self, placeholder: Option<String>) {
|
|
self.placeholder = placeholder;
|
|
}
|
|
|
|
pub fn placeholder(&self) -> Option<&str> {
|
|
self.placeholder.as_deref()
|
|
}
|
|
}
|
|
|
|
impl From<CoreTextElement> for TextElement {
|
|
fn from(value: CoreTextElement) -> Self {
|
|
Self::new(
|
|
value.byte_range.into(),
|
|
value._placeholder_for_conversion_only().map(str::to_string),
|
|
)
|
|
}
|
|
}
|
|
|
|
impl From<TextElement> for CoreTextElement {
|
|
fn from(value: TextElement) -> Self {
|
|
Self::new(value.byte_range.into(), value.placeholder)
|
|
}
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(untagged)]
|
|
#[ts(untagged)]
|
|
#[ts(export_to = "v2/")]
|
|
pub enum ImageReference {
|
|
Inline {
|
|
url: String,
|
|
},
|
|
File {
|
|
#[serde(rename = "fileId")]
|
|
#[ts(rename = "fileId")]
|
|
file_id: String,
|
|
},
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(untagged)]
|
|
#[ts(untagged)]
|
|
#[ts(export_to = "v2/")]
|
|
pub enum AudioReference {
|
|
Inline {
|
|
url: String,
|
|
},
|
|
File {
|
|
#[serde(rename = "fileId")]
|
|
#[ts(rename = "fileId")]
|
|
file_id: String,
|
|
},
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(tag = "type", rename_all = "camelCase")]
|
|
#[ts(tag = "type")]
|
|
#[ts(export_to = "v2/")]
|
|
pub enum UserInput {
|
|
Text {
|
|
text: String,
|
|
/// UI-defined spans within `text` used to render or persist special elements.
|
|
#[serde(default)]
|
|
text_elements: Vec<TextElement>,
|
|
},
|
|
Image {
|
|
#[serde(flatten)]
|
|
image: ImageReference,
|
|
#[serde(default)]
|
|
#[ts(optional)]
|
|
detail: Option<ImageDetail>,
|
|
},
|
|
LocalImage {
|
|
#[serde(default)]
|
|
#[ts(optional)]
|
|
detail: Option<ImageDetail>,
|
|
path: PathBuf,
|
|
},
|
|
Audio {
|
|
#[serde(flatten)]
|
|
audio: AudioReference,
|
|
},
|
|
LocalAudio {
|
|
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 { image, detail } => CoreUserInput::Image {
|
|
image: match image {
|
|
ImageReference::Inline { url } => CoreImageReference::Inline { image_url: url },
|
|
ImageReference::File { file_id } => CoreImageReference::File { file_id },
|
|
},
|
|
detail,
|
|
},
|
|
UserInput::LocalImage { path, detail } => CoreUserInput::LocalImage { path, detail },
|
|
UserInput::Audio { audio } => CoreUserInput::Audio {
|
|
audio: match audio {
|
|
AudioReference::Inline { url } => CoreAudioReference::Inline { audio_url: url },
|
|
AudioReference::File { file_id } => CoreAudioReference::File { file_id },
|
|
},
|
|
},
|
|
UserInput::LocalAudio { path } => CoreUserInput::LocalAudio { 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, detail } => UserInput::Image {
|
|
image: match image {
|
|
CoreImageReference::Inline { image_url } => {
|
|
ImageReference::Inline { url: image_url }
|
|
}
|
|
CoreImageReference::File { file_id } => ImageReference::File { file_id },
|
|
},
|
|
detail,
|
|
},
|
|
CoreUserInput::LocalImage { path, detail } => UserInput::LocalImage { path, detail },
|
|
CoreUserInput::Audio { audio } => UserInput::Audio {
|
|
audio: match audio {
|
|
CoreAudioReference::Inline { audio_url } => {
|
|
AudioReference::Inline { url: audio_url }
|
|
}
|
|
CoreAudioReference::File { file_id } => AudioReference::File { file_id },
|
|
},
|
|
},
|
|
CoreUserInput::LocalAudio { path } => UserInput::LocalAudio { 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::Audio { .. }
|
|
| UserInput::LocalAudio { .. }
|
|
| UserInput::Skill { .. }
|
|
| UserInput::Mention { .. } => 0,
|
|
}
|
|
}
|
|
}
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct TurnStartedNotification {
|
|
pub thread_id: String,
|
|
pub turn: Turn,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct Usage {
|
|
pub input_tokens: i32,
|
|
pub cached_input_tokens: i32,
|
|
pub output_tokens: i32,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct TurnCompletedNotification {
|
|
pub thread_id: String,
|
|
pub turn: Turn,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
/// Notification that the turn-level unified diff has changed.
|
|
/// Contains the latest aggregated diff across all file changes in the turn.
|
|
pub struct TurnDiffUpdatedNotification {
|
|
pub thread_id: String,
|
|
pub turn_id: String,
|
|
pub diff: String,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct TurnPlanUpdatedNotification {
|
|
pub thread_id: String,
|
|
pub turn_id: String,
|
|
pub explanation: Option<String>,
|
|
pub plan: Vec<TurnPlanStep>,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub struct TurnPlanStep {
|
|
pub step: String,
|
|
pub status: TurnPlanStepStatus,
|
|
}
|
|
|
|
#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, JsonSchema, TS)]
|
|
#[serde(rename_all = "camelCase")]
|
|
#[ts(export_to = "v2/")]
|
|
pub enum TurnPlanStepStatus {
|
|
Pending,
|
|
InProgress,
|
|
Completed,
|
|
}
|
|
|
|
impl From<CorePlanItemArg> for TurnPlanStep {
|
|
fn from(value: CorePlanItemArg) -> Self {
|
|
Self {
|
|
step: value.step,
|
|
status: value.status.into(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl From<CorePlanStepStatus> for TurnPlanStepStatus {
|
|
fn from(value: CorePlanStepStatus) -> Self {
|
|
match value {
|
|
CorePlanStepStatus::Pending => Self::Pending,
|
|
CorePlanStepStatus::InProgress => Self::InProgress,
|
|
CorePlanStepStatus::Completed => Self::Completed,
|
|
}
|
|
}
|
|
}
|