Codex Handbook
tools/src/tool_call.rs 121 lines
use crate::FunctionCallError;use crate::ToolName;use crate::ToolPayload;use codex_file_system::ExecutorFileSystem;use codex_file_system::FileSystemSandboxContext;use codex_protocol::items::ImageGenerationItem;use codex_protocol::items::WebSearchItem;use codex_protocol::models::ResponseItem;use codex_utils_absolute_path::AbsolutePathBuf;use codex_utils_output_truncation::TruncationPolicy;use std::future::Future;use std::pin::Pin;use std::sync::Arc;/// Raw response history snapshot available when an extension tool is invoked.#[derive(Clone, Debug, Default)]pub struct ConversationHistory {    items: Arc<[ResponseItem]>,}impl ConversationHistory {    pub fn new(items: Vec<ResponseItem>) -> Self {        Self {            items: items.into(),        }    }    pub fn items(&self) -> &[ResponseItem] {        &self.items    }}/// Future returned when an extension tool emits a visible turn-item lifecycle event.pub type TurnItemEmissionFuture<'a> = Pin<Box<dyn Future<Output = ()> + Send + 'a>>;/// Visible turn items that an extension may publish into the host lifecycle.#[derive(Clone, Debug, PartialEq)]pub enum ExtensionTurnItem {    WebSearch(WebSearchItem),    ImageGeneration(ImageGenerationItem),}/// Host-provided capability for extension tools to emit visible turn items.////// Implementations route lifecycle events through the host's normal item event/// pipeline, including any persistence and client delivery owned by the host.pub trait TurnItemEmitter: Send + Sync {    /// Emits the beginning of one visible turn item.    fn emit_started<'a>(&'a self, item: ExtensionTurnItem) -> TurnItemEmissionFuture<'a>;    /// Emits one visible turn item after host-owned finalization.    fn emit_completed<'a>(&'a self, item: ExtensionTurnItem) -> TurnItemEmissionFuture<'a>;}/// Host-owned turn environment summary visible to extension tools.#[derive(Clone)]pub struct ToolEnvironment {    /// Stable host environment id used to route executor-scoped capabilities.    pub environment_id: String,    /// Effective working directory for this turn in the environment.    pub cwd: AbsolutePathBuf,    /// Filesystem implementation for this environment.    pub file_system: Arc<dyn ExecutorFileSystem>,    /// Sandbox context to use for filesystem operations.    pub file_system_sandbox_context: FileSystemSandboxContext,}/// Turn-item emitter used when a caller does not expose visible item emission.#[derive(Debug, Default, Clone, Copy)]pub struct NoopTurnItemEmitter;impl TurnItemEmitter for NoopTurnItemEmitter {    fn emit_started<'a>(&'a self, _item: ExtensionTurnItem) -> TurnItemEmissionFuture<'a> {        Box::pin(std::future::ready(()))    }    fn emit_completed<'a>(&'a self, _item: ExtensionTurnItem) -> TurnItemEmissionFuture<'a> {        Box::pin(std::future::ready(()))    }}#[derive(Clone)]pub struct ToolCall {    pub turn_id: String,    pub call_id: String,    pub tool_name: ToolName,    pub model: String,    pub truncation_policy: TruncationPolicy,    pub conversation_history: ConversationHistory,    pub turn_item_emitter: Arc<dyn TurnItemEmitter>,    pub environments: Vec<ToolEnvironment>,    pub payload: ToolPayload,}impl std::fmt::Debug for ToolCall {    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {        f.debug_struct("ToolCall")            .field("turn_id", &self.turn_id)            .field("call_id", &self.call_id)            .field("tool_name", &self.tool_name)            .field("model", &self.model)            .field("truncation_policy", &self.truncation_policy)            .field("conversation_history", &self.conversation_history)            .field("turn_item_emitter", &"<host turn item emitter>")            .field("environment_count", &self.environments.len())            .field("payload", &self.payload)            .finish()    }}impl ToolCall {    pub fn function_arguments(&self) -> Result<&str, FunctionCallError> {        match &self.payload {            ToolPayload::Function { arguments } => Ok(arguments),            _ => Err(FunctionCallError::Fatal(format!(                "tool {} invoked with incompatible payload",                self.tool_name            ))),        }    }}