use super::AdditionalPermissionProfile; use super::ExecPolicyAmendment; use super::McpToolCallError; use super::McpToolCallResult; use super::NetworkApprovalContext; use super::NetworkApprovalProtocol; use super::NetworkPolicyAmendment; use super::RequestPermissionProfile; use super::UserInput; use super::shared::v2_enum_from_core; use crate::JsonSchema; use crate::TS; use crate::protocol::item_builders::CommandExecutionPresentation; use crate::protocol::item_builders::convert_patch_changes; use crate::protocol::item_builders::review_output_text; use codex_experimental_api_macros::ExperimentalApi; use codex_extension_items::ExtensionItem; pub use codex_extension_items::image_generation::ImageGenerationFailure; pub use codex_extension_items::image_generation::ImageGenerationItem; pub use codex_extension_items::sleep::SleepItem; pub use codex_extension_items::web_search::WebSearchAction; pub use codex_extension_items::web_search::WebSearchItem; use codex_protocol::approvals::ExecApprovalKind as CoreExecApprovalKind; use codex_protocol::approvals::GuardianAssessmentAction as CoreGuardianAssessmentAction; use codex_protocol::approvals::GuardianAssessmentDecisionSource as CoreGuardianAssessmentDecisionSource; use codex_protocol::approvals::GuardianCommandSource as CoreGuardianCommandSource; use codex_protocol::items::AgentMessageContent as CoreAgentMessageContent; pub use codex_protocol::items::AgentMessageDelivery; pub use codex_protocol::items::AsyncUserInputQuestion; use codex_protocol::items::CollabAgentTool as CoreCollabAgentTool; use codex_protocol::items::CollabAgentToolCallStatus as CoreCollabAgentToolCallStatus; use codex_protocol::items::CommandExecutionStatus as CoreCommandExecutionStatus; use codex_protocol::items::DynamicToolCallStatus as CoreDynamicToolCallStatus; pub use codex_protocol::items::McpAppDisplayMode; pub use codex_protocol::items::McpAppUi; use codex_protocol::items::McpToolCallStatus as CoreMcpToolCallStatus; use codex_protocol::items::TurnItem as CoreTurnItem; use codex_protocol::memory_citation::MemoryCitation as CoreMemoryCitation; use codex_protocol::memory_citation::MemoryCitationEntry as CoreMemoryCitationEntry; use codex_protocol::models::FunctionCallOutputBody; use codex_protocol::models::MessagePhase; use codex_protocol::models::ResponseItem; use codex_protocol::openai_models::ReasoningEffort; use codex_protocol::parse_command::ParsedCommand as CoreParsedCommand; use codex_protocol::protocol::AgentStatus as CoreAgentStatus; use codex_protocol::protocol::ExecCommandSource as CoreExecCommandSource; use codex_protocol::protocol::ExecCommandStatus as CoreExecCommandStatus; use codex_protocol::protocol::GuardianRiskLevel as CoreGuardianRiskLevel; use codex_protocol::protocol::GuardianUserAuthorization as CoreGuardianUserAuthorization; use codex_protocol::protocol::PatchApplyStatus as CorePatchApplyStatus; use codex_protocol::protocol::ReviewDecision as CoreReviewDecision; use codex_protocol::protocol::SubAgentActivityKind as CoreSubAgentActivityKind; 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 serde_with::serde_as; use std::collections::HashMap; use std::io; use std::path::PathBuf; #[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 for CommandExecutionApprovalDecision { fn from(value: CoreReviewDecision) -> Self { match value { CoreReviewDecision::Approved => Self::Accept, // MCP approvals are handled through elicitations, so an MCP policy amendment should // never appear in a command execution approval. To be cautious here, we fail closed. CoreReviewDecision::ApprovedMcpPolicyAmendment => Self::Decline, 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, CoreReviewDecision::TimedOut => Self::Decline, } } } #[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, 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: LegacyAppPathString, }, ListFiles { command: String, path: Option, }, Search { command: String, query: Option, path: Option, }, Unknown { command: String, }, } #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)] #[serde(rename_all = "camelCase")] #[ts(export_to = "v2/")] pub struct MemoryCitation { pub entries: Vec, pub thread_ids: Vec, } impl From for MemoryCitation { fn from(value: CoreMemoryCitation) -> Self { Self { entries: value.entries.into_iter().map(Into::into).collect(), thread_ids: value.rollout_ids, } } } #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)] #[serde(rename_all = "camelCase")] #[ts(export_to = "v2/")] pub struct MemoryCitationEntry { pub path: String, pub line_start: u32, pub line_end: u32, pub note: String, } impl From for MemoryCitationEntry { fn from(value: CoreMemoryCitationEntry) -> Self { Self { path: value.path, line_start: value.line_start, line_end: value.line_end, note: value.note, } } } impl CommandAction { pub fn into_core(self) -> CoreParsedCommand { match self { CommandAction::Read { command: cmd, name, path, } => CoreParsedCommand::Read { cmd, name, path: PathBuf::from(path.into_string()), }, 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 }, } } pub fn from_core_with_cwd(value: CoreParsedCommand, cwd: &AbsolutePathBuf) -> Self { match value { CoreParsedCommand::Read { cmd, name, path } => CommandAction::Read { command: cmd, name, path: cwd.join(path).into(), }, 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 ThreadItem { #[serde(rename_all = "camelCase")] #[ts(rename_all = "camelCase")] UserMessage { id: String, client_id: Option, content: Vec, }, #[serde(rename_all = "camelCase")] #[ts(rename_all = "camelCase")] HookPrompt { id: String, fragments: Vec, }, #[serde(rename_all = "camelCase")] #[ts(rename_all = "camelCase")] AgentMessage { id: String, text: String, #[serde(default)] phase: Option, #[serde(default)] memory_citation: Option, #[serde(default)] delivery: Option, #[serde(default)] questions: Option>, }, #[serde(rename_all = "camelCase")] #[ts(rename_all = "camelCase")] FunctionCallOutput { id: String, name: String, namespace: Option, output: FunctionCallOutputBody, }, #[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, #[serde(default)] content: Vec, }, #[serde(rename_all = "camelCase")] #[ts(rename_all = "camelCase")] CommandExecution { #[serde(skip)] #[schemars(skip)] #[ts(skip)] model_context: Option, id: String, /// Trusted first-party plugin id when this command resolves to one plugin script. #[serde(default)] plugin_id: Option, /// Safe plugin-relative path when this command resolves to one plugin script. #[serde(default)] script_path: Option, /// The command to be executed. command: String, /// The command's working directory. cwd: LegacyAppPathString, /// Identifier for the underlying PTY process (when available). process_id: Option, #[serde(default)] source: CommandExecutionSource, 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, /// The command's output, aggregated from stdout and stderr. aggregated_output: Option, /// The command's exit code. exit_code: Option, /// The duration of the command execution in milliseconds. #[ts(type = "number | null")] duration_ms: Option, }, #[serde(rename_all = "camelCase")] #[ts(rename_all = "camelCase")] FileChange { id: String, changes: Vec, status: PatchApplyStatus, }, #[serde(rename_all = "camelCase")] #[ts(rename_all = "camelCase")] McpToolCall { id: String, server: String, tool: String, status: McpToolCallStatus, arguments: JsonValue, app_context: Option, #[serde(default, skip_serializing_if = "Option::is_none")] #[ts(optional)] /// Legacy compatibility field; prefer `mcpAppUi.resourceUri` when available. mcp_app_resource_uri: Option, /// Presentation captured from the invoked descriptor; absent in older history. mcp_app_ui: Option, plugin_id: Option, read_only_hint: Option, result: Option>, error: Option, /// The duration of the MCP tool call in milliseconds. #[ts(type = "number | null")] duration_ms: Option, }, #[serde(rename_all = "camelCase")] #[ts(rename_all = "camelCase")] DynamicToolCall { id: String, namespace: Option, tool: String, arguments: JsonValue, status: DynamicToolCallStatus, content_items: Option>, success: Option, /// The duration of the dynamic tool call in milliseconds. #[ts(type = "number | null")] duration_ms: Option, }, #[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, /// Prompt text sent as part of the collab tool call, when available. prompt: Option, /// Model requested for the spawned agent, when applicable. model: Option, /// Reasoning effort requested for the spawned agent, when applicable. reasoning_effort: Option, /// Last known status of the target agents, when available. agents_states: HashMap, }, #[serde(rename_all = "camelCase")] #[ts(rename_all = "camelCase")] SubAgentActivity { id: String, kind: SubAgentActivityKind, agent_thread_id: String, agent_path: String, }, WebSearch(WebSearchItem), #[serde(rename_all = "camelCase")] #[ts(rename_all = "camelCase")] ImageView { id: String, path: LegacyAppPathString, }, Sleep(SleepItem), ImageGeneration(ImageGenerationItem), #[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, }, } #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)] #[serde(rename_all = "camelCase")] #[ts(rename_all = "camelCase", export_to = "v2/")] pub struct McpToolCallAppContext { pub connector_id: String, pub link_id: Option, pub resource_uri: Option, pub app_name: Option, pub action_name: Option, } #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)] #[serde(rename_all = "camelCase")] #[ts(rename_all = "camelCase", export_to = "v2/")] pub struct HookPromptFragment { pub text: String, pub hook_run_id: String, } impl ThreadItem { pub fn id(&self) -> &str { match self { ThreadItem::UserMessage { id, .. } | ThreadItem::HookPrompt { id, .. } | ThreadItem::AgentMessage { id, .. } | ThreadItem::FunctionCallOutput { id, .. } | ThreadItem::Plan { id, .. } | ThreadItem::Reasoning { id, .. } | ThreadItem::CommandExecution { id, .. } | ThreadItem::FileChange { id, .. } | ThreadItem::McpToolCall { id, .. } | ThreadItem::DynamicToolCall { id, .. } | ThreadItem::CollabAgentToolCall { id, .. } | ThreadItem::SubAgentActivity { id, .. } | ThreadItem::ImageView { id, .. } | ThreadItem::EnteredReviewMode { id, .. } | ThreadItem::ExitedReviewMode { id, .. } | ThreadItem::ContextCompaction { id, .. } => id, ThreadItem::WebSearch(item) => &item.id, ThreadItem::Sleep(item) => &item.id, ThreadItem::ImageGeneration(item) => &item.id, } } } #[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, JsonSchema, TS)] #[serde(rename_all = "camelCase")] #[ts(export_to = "v2/")] /// [UNSTABLE] Lifecycle state for an approval auto-review. pub enum GuardianApprovalReviewStatus { InProgress, Approved, Denied, TimedOut, Aborted, } #[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, JsonSchema, TS)] #[serde(rename_all = "camelCase")] #[ts(export_to = "v2/")] /// [UNSTABLE] Source that produced a terminal approval auto-review decision. pub enum AutoReviewDecisionSource { Agent, } impl From for AutoReviewDecisionSource { fn from(value: CoreGuardianAssessmentDecisionSource) -> Self { match value { CoreGuardianAssessmentDecisionSource::Agent => Self::Agent, } } } #[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, JsonSchema, TS)] #[serde(rename_all = "lowercase")] #[ts(export_to = "v2/")] /// [UNSTABLE] Risk level assigned by approval auto-review. pub enum GuardianRiskLevel { Low, Medium, High, Critical, } impl From for GuardianRiskLevel { fn from(value: CoreGuardianRiskLevel) -> Self { match value { CoreGuardianRiskLevel::Low => Self::Low, CoreGuardianRiskLevel::Medium => Self::Medium, CoreGuardianRiskLevel::High => Self::High, CoreGuardianRiskLevel::Critical => Self::Critical, } } } #[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, JsonSchema, TS)] #[serde(rename_all = "lowercase")] #[ts(export_to = "v2/")] /// [UNSTABLE] Authorization level assigned by approval auto-review. pub enum GuardianUserAuthorization { Unknown, Low, Medium, High, } impl From for GuardianUserAuthorization { fn from(value: CoreGuardianUserAuthorization) -> Self { match value { CoreGuardianUserAuthorization::Unknown => Self::Unknown, CoreGuardianUserAuthorization::Low => Self::Low, CoreGuardianUserAuthorization::Medium => Self::Medium, CoreGuardianUserAuthorization::High => Self::High, } } } /// [UNSTABLE] Temporary approval auto-review payload used by /// `item/autoApprovalReview/*` notifications. This shape is expected to change /// soon. #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)] #[serde(rename_all = "camelCase")] #[ts(export_to = "v2/")] pub struct GuardianApprovalReview { pub status: GuardianApprovalReviewStatus, pub risk_level: Option, pub user_authorization: Option, pub rationale: Option, } #[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, JsonSchema, TS)] #[serde(rename_all = "camelCase")] #[ts(rename_all = "camelCase")] #[ts(export_to = "v2/")] pub enum GuardianCommandSource { Shell, UnifiedExec, } impl From for GuardianCommandSource { fn from(value: CoreGuardianCommandSource) -> Self { match value { CoreGuardianCommandSource::Shell => Self::Shell, CoreGuardianCommandSource::UnifiedExec => Self::UnifiedExec, } } } impl From for CoreGuardianCommandSource { fn from(value: GuardianCommandSource) -> Self { match value { GuardianCommandSource::Shell => Self::Shell, GuardianCommandSource::UnifiedExec => Self::UnifiedExec, } } } #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)] #[serde(rename_all = "camelCase")] #[ts(export_to = "v2/")] pub struct GuardianCommandReviewAction { pub source: GuardianCommandSource, pub command: String, pub cwd: LegacyAppPathString, } #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)] #[serde(rename_all = "camelCase")] #[ts(export_to = "v2/")] pub struct GuardianExecveReviewAction { pub source: GuardianCommandSource, pub program: String, pub argv: Vec, pub cwd: AbsolutePathBuf, } #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)] #[serde(rename_all = "camelCase")] #[ts(export_to = "v2/")] pub struct GuardianApplyPatchReviewAction { pub cwd: LegacyAppPathString, pub files: Vec, } #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)] #[serde(rename_all = "camelCase")] #[ts(export_to = "v2/")] pub struct GuardianNetworkAccessReviewAction { pub target: String, pub host: String, pub protocol: NetworkApprovalProtocol, pub port: u16, } #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)] #[serde(rename_all = "camelCase")] #[ts(export_to = "v2/")] pub struct GuardianMcpToolCallReviewAction { pub server: String, pub tool_name: String, pub connector_id: Option, pub connector_name: Option, pub tool_title: Option, } #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)] #[serde(rename_all = "camelCase")] #[ts(export_to = "v2/")] pub struct GuardianRequestPermissionsReviewAction { pub reason: Option, pub permissions: RequestPermissionProfile, } #[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 GuardianApprovalReviewAction { #[serde(rename_all = "camelCase")] #[ts(rename_all = "camelCase")] Command { source: GuardianCommandSource, command: String, cwd: LegacyAppPathString, }, #[serde(rename_all = "camelCase")] #[ts(rename_all = "camelCase")] Execve { source: GuardianCommandSource, program: String, argv: Vec, cwd: AbsolutePathBuf, }, /// A child approval for input to an existing command execution item. #[serde(rename_all = "camelCase")] #[ts(rename_all = "camelCase")] WriteStdin { approval_id: String, process_id: String, stdin: String, cwd: LegacyAppPathString, }, #[serde(rename_all = "camelCase")] #[ts(rename_all = "camelCase")] ApplyPatch { cwd: LegacyAppPathString, files: Vec, }, #[serde(rename_all = "camelCase")] #[ts(rename_all = "camelCase")] NetworkAccess { target: String, host: String, protocol: NetworkApprovalProtocol, port: u16, }, #[serde(rename_all = "camelCase")] #[ts(rename_all = "camelCase")] McpToolCall { server: String, tool_name: String, connector_id: Option, connector_name: Option, tool_title: Option, }, #[serde(rename_all = "camelCase")] #[ts(rename_all = "camelCase")] RequestPermissions { reason: Option, permissions: RequestPermissionProfile, }, } impl From for GuardianApprovalReviewAction { fn from(value: CoreGuardianAssessmentAction) -> Self { match value { CoreGuardianAssessmentAction::Command { source, command, cwd, } => Self::Command { source: source.into(), command, cwd, }, CoreGuardianAssessmentAction::Execve { source, program, argv, cwd, } => Self::Execve { source: source.into(), program, argv, cwd, }, CoreGuardianAssessmentAction::WriteStdin { approval_id, process_id, stdin, cwd, } => Self::WriteStdin { approval_id, process_id, stdin, cwd: cwd.into(), }, CoreGuardianAssessmentAction::ApplyPatch { cwd, files } => { Self::ApplyPatch { cwd, files } } CoreGuardianAssessmentAction::NetworkAccess { target, host, protocol, port, } => Self::NetworkAccess { target, host, protocol: protocol.into(), port, }, CoreGuardianAssessmentAction::McpToolCall { server, tool_name, connector_id, connector_name, tool_title, } => Self::McpToolCall { server, tool_name, connector_id, connector_name, tool_title, }, CoreGuardianAssessmentAction::RequestPermissions { reason, permissions, } => Self::RequestPermissions { reason, permissions: permissions.into(), }, } } } impl TryFrom for CoreGuardianAssessmentAction { type Error = io::Error; fn try_from(value: GuardianApprovalReviewAction) -> Result { Ok(match value { GuardianApprovalReviewAction::Command { source, command, cwd, } => Self::Command { source: source.into(), command, cwd, }, GuardianApprovalReviewAction::Execve { source, program, argv, cwd, } => Self::Execve { source: source.into(), program, argv, cwd, }, GuardianApprovalReviewAction::WriteStdin { approval_id, process_id, stdin, cwd, } => Self::WriteStdin { approval_id, process_id, stdin, cwd: cwd .try_into() .map_err(|err| io::Error::new(io::ErrorKind::InvalidInput, err))?, }, GuardianApprovalReviewAction::ApplyPatch { cwd, files } => { Self::ApplyPatch { cwd, files } } GuardianApprovalReviewAction::NetworkAccess { target, host, protocol, port, } => Self::NetworkAccess { target, host, protocol: protocol.to_core(), port, }, GuardianApprovalReviewAction::McpToolCall { server, tool_name, connector_id, connector_name, tool_title, } => Self::McpToolCall { server, tool_name, connector_id, connector_name, tool_title, }, GuardianApprovalReviewAction::RequestPermissions { reason, permissions, } => Self::RequestPermissions { reason, permissions: permissions.try_into()?, }, }) } } pub(crate) fn web_search_action_from_core( value: codex_protocol::models::WebSearchAction, ) -> WebSearchAction { 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 for ThreadItem { fn from(value: CoreTurnItem) -> Self { match value { CoreTurnItem::UserMessage(user) => ThreadItem::UserMessage { id: user.id, client_id: user.client_id, content: user.content.into_iter().map(UserInput::from).collect(), }, CoreTurnItem::HookPrompt(hook_prompt) => ThreadItem::HookPrompt { id: hook_prompt.id, fragments: hook_prompt .fragments .into_iter() .map(HookPromptFragment::from) .collect(), }, CoreTurnItem::AgentMessage(agent) => { let text = agent .content .into_iter() .map(|entry| match entry { CoreAgentMessageContent::Text { text } => text, }) .collect::(); ThreadItem::AgentMessage { id: agent.id, text, phase: agent.phase, memory_citation: agent.memory_citation.map(Into::into), delivery: agent.delivery, questions: agent.questions, } } CoreTurnItem::FunctionCallOutput(output) => ThreadItem::FunctionCallOutput { id: output.id, name: output.name, namespace: output.namespace, output: output.output, }, 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::CommandExecution(command) => { let presentation = CommandExecutionPresentation::from_raw( &command.command, &command.parsed_cmd, &command.cwd, ); ThreadItem::CommandExecution { id: command.id, model_context: command.model_context, plugin_id: command.plugin_id, script_path: command.script_path, command: presentation.command, cwd: command.cwd.clone().into(), process_id: command.process_id, source: command.source.into(), status: command.status.into(), command_actions: presentation.command_actions, aggregated_output: command .aggregated_output .filter(|output| !output.is_empty()), exit_code: command.exit_code, duration_ms: command .duration .and_then(|duration| i64::try_from(duration.as_millis()).ok()), } } CoreTurnItem::DynamicToolCall(call) => ThreadItem::DynamicToolCall { id: call.id, namespace: call.namespace, tool: call.tool, arguments: call.arguments, status: call.status.into(), content_items: call.content_items.map(|items| { items .into_iter() .map(DynamicToolCallOutputContentItem::from) .collect() }), success: call.success, duration_ms: call .duration .and_then(|duration| i64::try_from(duration.as_millis()).ok()), }, CoreTurnItem::CollabAgentToolCall(call) => ThreadItem::CollabAgentToolCall { id: call.id, tool: call.tool.into(), status: call.status.into(), sender_thread_id: call.sender_thread_id.to_string(), receiver_thread_ids: call .receiver_thread_ids .into_iter() .map(String::from) .collect(), prompt: call.prompt, model: call.model, reasoning_effort: call.reasoning_effort, agents_states: call .agents_states .into_iter() .map(|(thread_id, status)| (thread_id.to_string(), status.into())) .collect(), }, CoreTurnItem::SubAgentActivity(activity) => ThreadItem::SubAgentActivity { id: activity.id, kind: activity.kind.into(), agent_thread_id: activity.agent_thread_id.to_string(), agent_path: String::from(activity.agent_path), }, CoreTurnItem::WebSearch(search) => ThreadItem::WebSearch(WebSearchItem { id: search.id, query: search.query, action: Some(web_search_action_from_core(search.action)), results: search.results, }), CoreTurnItem::ImageView(image) => ThreadItem::ImageView { id: image.id, path: image.path.into(), }, CoreTurnItem::Extension(extension) => match extension { ExtensionItem::ImageGeneration(item) => ThreadItem::ImageGeneration(item), ExtensionItem::Sleep(item) => ThreadItem::Sleep(item), ExtensionItem::WebSearch(item) => ThreadItem::WebSearch(item), }, CoreTurnItem::ImageGeneration(image) => { ThreadItem::ImageGeneration(ImageGenerationItem { id: image.id, status: image.status, revised_prompt: image.revised_prompt, result: image.result, transparent_background: None, failure: None, saved_path: image.saved_path, imagegen_request_id: None, generation_id: None, }) } CoreTurnItem::EnteredReviewMode(review) => ThreadItem::EnteredReviewMode { id: review.id, review: review.user_facing_hint, }, CoreTurnItem::ExitedReviewMode(review) => ThreadItem::ExitedReviewMode { id: review.id, review: review_output_text(review.review_output.as_ref()), }, CoreTurnItem::FileChange(file_change) => ThreadItem::FileChange { id: file_change.id, changes: convert_patch_changes(&file_change.changes), status: file_change .status .as_ref() .map(PatchApplyStatus::from) .unwrap_or(PatchApplyStatus::InProgress), }, CoreTurnItem::McpToolCall(mcp) => { let duration_ms = mcp .duration .and_then(|duration| i64::try_from(duration.as_millis()).ok()); ThreadItem::McpToolCall { id: mcp.id, server: mcp.server, tool: mcp.tool, status: McpToolCallStatus::from(mcp.status), arguments: mcp.arguments, app_context: mcp.connector_id.map(|connector_id| McpToolCallAppContext { connector_id, link_id: mcp.link_id, resource_uri: mcp.mcp_app_resource_uri.clone(), app_name: mcp.app_name, action_name: mcp.action_name, }), mcp_app_resource_uri: mcp.mcp_app_resource_uri, mcp_app_ui: mcp.mcp_app_ui, plugin_id: mcp.plugin_id, read_only_hint: mcp.read_only_hint, result: mcp.result.map(McpToolCallResult::from).map(Box::new), error: mcp.error.map(McpToolCallError::from), duration_ms, } } CoreTurnItem::ContextCompaction(compaction) => { ThreadItem::ContextCompaction { id: compaction.id } } } } } impl From for HookPromptFragment { fn from(value: codex_protocol::items::HookPromptFragment) -> Self { Self { text: value.text, hook_run_id: value.hook_run_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 for CommandExecutionStatus { fn from(value: CoreExecCommandStatus) -> Self { Self::from(&value) } } impl From for CommandExecutionStatus { fn from(value: CoreCommandExecutionStatus) -> Self { match value { CoreCommandExecutionStatus::InProgress => Self::InProgress, CoreCommandExecutionStatus::Completed => Self::Completed, CoreCommandExecutionStatus::Failed => Self::Failed, CoreCommandExecutionStatus::Declined => Self::Declined, } } } impl From<&CoreExecCommandStatus> for CommandExecutionStatus { fn from(value: &CoreExecCommandStatus) -> Self { match value { CoreExecCommandStatus::Completed => CommandExecutionStatus::Completed, CoreExecCommandStatus::Failed => CommandExecutionStatus::Failed, CoreExecCommandStatus::Declined => CommandExecutionStatus::Declined, } } } v2_enum_from_core! { #[derive(Default)] pub enum CommandExecutionSource from CoreExecCommandSource { #[default] Agent, UserShell, UnifiedExecStartup, UnifiedExecInteraction, } } #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)] #[serde(rename_all = "camelCase")] #[ts(export_to = "v2/")] pub enum CollabAgentTool { SpawnAgent, SendInput, ResumeAgent, Wait, CloseAgent, SendMessage, FollowupTask, InterruptAgent, ListAgents, } #[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 }, } #[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 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, } } } impl From for McpToolCallStatus { fn from(value: CoreMcpToolCallStatus) -> Self { match value { CoreMcpToolCallStatus::InProgress => McpToolCallStatus::InProgress, CoreMcpToolCallStatus::Completed => McpToolCallStatus::Completed, CoreMcpToolCallStatus::Failed => McpToolCallStatus::Failed, } } } impl From for DynamicToolCallStatus { fn from(value: CoreDynamicToolCallStatus) -> Self { match value { CoreDynamicToolCallStatus::InProgress => Self::InProgress, CoreDynamicToolCallStatus::Completed => Self::Completed, CoreDynamicToolCallStatus::Failed => Self::Failed, } } } #[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, Interrupted, } impl From for CollabAgentTool { fn from(value: CoreCollabAgentTool) -> Self { match value { CoreCollabAgentTool::SpawnAgent => Self::SpawnAgent, CoreCollabAgentTool::SendInput => Self::SendInput, CoreCollabAgentTool::ResumeAgent => Self::ResumeAgent, CoreCollabAgentTool::Wait => Self::Wait, CoreCollabAgentTool::CloseAgent => Self::CloseAgent, CoreCollabAgentTool::SendMessage => Self::SendMessage, CoreCollabAgentTool::FollowupTask => Self::FollowupTask, CoreCollabAgentTool::InterruptAgent => Self::InterruptAgent, CoreCollabAgentTool::ListAgents => Self::ListAgents, } } } impl From for CollabAgentToolCallStatus { fn from(value: CoreCollabAgentToolCallStatus) -> Self { match value { CoreCollabAgentToolCallStatus::InProgress => Self::InProgress, CoreCollabAgentToolCallStatus::Completed => Self::Completed, CoreCollabAgentToolCallStatus::Failed => Self::Failed, CoreCollabAgentToolCallStatus::Interrupted => Self::Interrupted, } } } #[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, JsonSchema, TS)] #[serde(rename_all = "camelCase")] #[ts(export_to = "v2/")] pub enum SubAgentActivityKind { Started, Interacted, Interrupted, Completed, } impl From for SubAgentActivityKind { fn from(value: CoreSubAgentActivityKind) -> Self { match value { CoreSubAgentActivityKind::Started => SubAgentActivityKind::Started, CoreSubAgentActivityKind::Interacted => SubAgentActivityKind::Interacted, CoreSubAgentActivityKind::Interrupted => SubAgentActivityKind::Interrupted, CoreSubAgentActivityKind::Completed => SubAgentActivityKind::Completed, } } } #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq, JsonSchema, TS)] #[serde(rename_all = "camelCase")] #[ts(export_to = "v2/")] pub enum CollabAgentStatus { PendingInit, Running, Interrupted, 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, } impl From 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::Interrupted => Self { status: CollabAgentStatus::Interrupted, 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 ItemStartedNotification { pub item: ThreadItem, pub thread_id: String, pub turn_id: String, /// Unix timestamp (in milliseconds) when this item lifecycle started. #[ts(type = "number")] pub started_at_ms: i64, } #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)] #[serde(rename_all = "camelCase")] #[ts(export_to = "v2/")] /// [UNSTABLE] Temporary notification payload for approval auto-review. This /// shape is expected to change soon. pub struct ItemGuardianApprovalReviewStartedNotification { pub thread_id: String, pub turn_id: String, /// Unix timestamp (in milliseconds) when this review started. #[ts(type = "number")] pub started_at_ms: i64, /// Stable identifier for this review. pub review_id: String, /// Identifier for the reviewed item or tool call when one exists. /// /// In most cases, one review maps to one target item. The exceptions are /// - execve reviews, where a single command may contain multiple execve /// calls to review (only possible when using the shell_zsh_fork feature) /// - stdin reviews, which refer to the existing parent command item and /// have a separate approval ID in the action payload /// - network policy reviews, where there is no target item /// /// A network call is triggered by a CommandExecution item, so having a /// target_item_id set to the CommandExecution item would be misleading /// because the review is about the network call, not the command execution. /// Therefore, target_item_id is set to None for network policy reviews. pub target_item_id: Option, pub review: GuardianApprovalReview, pub action: GuardianApprovalReviewAction, } #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)] #[serde(rename_all = "camelCase")] #[ts(export_to = "v2/")] /// [UNSTABLE] Temporary notification payload for approval auto-review. This /// shape is expected to change soon. pub struct ItemGuardianApprovalReviewCompletedNotification { pub thread_id: String, pub turn_id: String, /// Unix timestamp (in milliseconds) when this review started. #[ts(type = "number")] pub started_at_ms: i64, /// Unix timestamp (in milliseconds) when this review completed. #[ts(type = "number")] pub completed_at_ms: i64, /// Stable identifier for this review. pub review_id: String, /// Identifier for the reviewed item or tool call when one exists. /// /// In most cases, one review maps to one target item. The exceptions are /// - execve reviews, where a single command may contain multiple execve /// calls to review (only possible when using the shell_zsh_fork feature) /// - stdin reviews, which refer to the existing parent command item and /// have a separate approval ID in the action payload /// - network policy reviews, where there is no target item /// /// A network call is triggered by a CommandExecution item, so having a /// target_item_id set to the CommandExecution item would be misleading /// because the review is about the network call, not the command execution. /// Therefore, target_item_id is set to None for network policy reviews. pub target_item_id: Option, pub decision_source: AutoReviewDecisionSource, pub review: GuardianApprovalReview, pub action: GuardianApprovalReviewAction, } #[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, /// Unix timestamp (in milliseconds) when this item lifecycle completed. #[ts(type = "number")] pub completed_at_ms: i64, } #[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, } #[serde_as] #[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, } /// Deprecated legacy notification for `apply_patch` textual output. /// /// The server no longer emits this notification. #[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 FileChangePatchUpdatedNotification { pub thread_id: String, pub turn_id: String, pub item_id: String, pub changes: Vec, } v2_enum_from_core! { /// Distinguishes a command approval from input sent to an existing terminal. #[derive(Default)] #[ts(rename_all = "camelCase")] pub enum CommandExecutionApprovalKind from CoreExecApprovalKind { #[default] Command, WriteStdin, } } #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS, ExperimentalApi)] #[serde(rename_all = "camelCase")] #[ts(export_to = "v2/")] pub struct CommandExecutionRequestApprovalParams { /// Kind of action under review. Defaults to `command` for older servers. #[serde(default)] pub kind: CommandExecutionApprovalKind, pub thread_id: String, pub turn_id: String, pub item_id: String, /// Unix timestamp (in milliseconds) when this approval request started. #[ts(type = "number")] pub started_at_ms: i64, /// 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. /// Stdin approvals also use a distinct callback id; inspect `kind` to distinguish them. #[serde(default, skip_serializing_if = "Option::is_none")] #[ts(optional = nullable)] pub approval_id: Option, /// Environment in which the command will run. #[serde(default)] pub environment_id: Option, /// Optional explanatory reason (e.g. request for network access). #[serde(default, skip_serializing_if = "Option::is_none")] #[ts(optional = nullable)] pub reason: Option, /// Optional context for a managed-network approval prompt. #[serde(default, skip_serializing_if = "Option::is_none")] #[ts(optional = nullable)] pub network_approval_context: Option, /// The command to be executed. #[serde(default, skip_serializing_if = "Option::is_none")] #[ts(optional = nullable)] pub command: Option, /// The command's working directory. #[serde(default, skip_serializing_if = "Option::is_none")] #[ts(optional = nullable)] pub cwd: Option, /// Best-effort parsed command actions for friendly display. #[serde(default, skip_serializing_if = "Option::is_none")] #[ts(optional = nullable)] pub command_actions: Option>, /// 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, /// 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, /// 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>, /// 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>, } 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, /// Unix timestamp (in milliseconds) when this approval request started. #[ts(type = "number")] pub started_at_ms: i64, /// Optional explanatory reason (e.g. request for extra write access). #[ts(optional = nullable)] pub reason: Option, /// [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, } #[derive(Serialize, Deserialize, Debug, Clone, PartialEq, JsonSchema, TS)] #[ts(export_to = "v2/")] pub struct FileChangeRequestApprovalResponse { pub decision: FileChangeApprovalDecision, } #[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 namespace: Option, 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, 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 }, #[serde(rename_all = "camelCase")] InputAudio { audio_url: String }, } impl From for DynamicToolCallOutputContentItem { fn from(item: codex_protocol::dynamic_tools::DynamicToolCallOutputContentItem) -> Self { match item { codex_protocol::dynamic_tools::DynamicToolCallOutputContentItem::InputText { text } => { Self::InputText { text } } codex_protocol::dynamic_tools::DynamicToolCallOutputContentItem::InputImage { image_url, } => Self::InputImage { image_url }, codex_protocol::dynamic_tools::DynamicToolCallOutputContentItem::InputAudio { audio_url, } => Self::InputAudio { audio_url }, } } } impl From 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 } } DynamicToolCallOutputContentItem::InputAudio { audio_url } => { Self::InputAudio { audio_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>, } #[derive(Serialize, 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, pub is_blocking: bool, /// @deprecated Use `isBlocking` to decide whether the request should block. #[serde(default)] #[ts(type = "number | null")] pub auto_resolution_ms: Option, } impl<'de> Deserialize<'de> for ToolRequestUserInputParams { fn deserialize(deserializer: D) -> Result where D: serde::Deserializer<'de>, { #[derive(Deserialize)] #[serde(rename_all = "camelCase")] struct WireToolRequestUserInputParams { thread_id: String, turn_id: String, item_id: String, questions: Vec, is_blocking: Option, auto_resolution_ms: Option, } let wire = WireToolRequestUserInputParams::deserialize(deserializer)?; Ok(Self { thread_id: wire.thread_id, turn_id: wire.turn_id, item_id: wire.item_id, questions: wire.questions, is_blocking: wire.is_blocking.unwrap_or(true), auto_resolution_ms: wire.auto_resolution_ms, }) } } #[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, } #[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, }