1use crate::FunctionCallError;
2use crate::ToolName;
3use crate::ToolPayload;
4use codex_extension_items::ExtensionItem;
5use codex_file_system::ExecutorFileSystem;
6use codex_file_system::FileSystemSandboxContext;
7use codex_protocol::models::ResponseItem;
8use codex_protocol::protocol::EventMsg;
9use codex_utils_absolute_path::AbsolutePathBuf;
10use codex_utils_output_truncation::TruncationPolicy;
11use std::future::Future;
12use std::pin::Pin;
13use std::sync::Arc;
14
15#[derive(Clone, Debug, Default)]
17pub struct ConversationHistory {
18 items: Arc<[ResponseItem]>,
19}
20
21impl ConversationHistory {
22 pub fn new(items: Vec<ResponseItem>) -> Self {
23 Self {
24 items: items.into(),
25 }
26 }
27
28 pub fn items(&self) -> &[ResponseItem] {
29 &self.items
30 }
31}
32
33pub type TurnItemEmissionFuture<'a> = Pin<Box<dyn Future<Output = ()> + Send + 'a>>;
35
36#[derive(Clone, Debug)]
38pub struct ExtensionTurnItem {
39 pub item: ExtensionItem,
47 pub legacy_events: Vec<EventMsg>,
48}
49
50pub trait TurnItemEmitter: Send + Sync {
55 fn emit_started<'a>(&'a self, item: ExtensionTurnItem) -> TurnItemEmissionFuture<'a>;
57
58 fn emit_completed<'a>(&'a self, item: ExtensionTurnItem) -> TurnItemEmissionFuture<'a>;
60}
61
62#[derive(Clone)]
64pub struct ToolEnvironment {
65 pub environment_id: String,
67 pub cwd: AbsolutePathBuf,
69 pub file_system: Arc<dyn ExecutorFileSystem>,
71 pub file_system_sandbox_context: FileSystemSandboxContext,
73}
74
75#[derive(Debug, Default, Clone, Copy)]
77pub struct NoopTurnItemEmitter;
78
79impl TurnItemEmitter for NoopTurnItemEmitter {
80 fn emit_started<'a>(&'a self, _item: ExtensionTurnItem) -> TurnItemEmissionFuture<'a> {
81 Box::pin(std::future::ready(()))
82 }
83
84 fn emit_completed<'a>(&'a self, _item: ExtensionTurnItem) -> TurnItemEmissionFuture<'a> {
85 Box::pin(std::future::ready(()))
86 }
87}
88
89#[derive(Clone)]
90pub struct ToolCall {
91 pub turn_id: String,
92 pub call_id: String,
93 pub tool_name: ToolName,
94 pub model: String,
95 pub codex_turn_metadata: Option<String>,
96 pub truncation_policy: TruncationPolicy,
97 pub conversation_history: ConversationHistory,
98 pub turn_item_emitter: Arc<dyn TurnItemEmitter>,
99 pub environments: Vec<ToolEnvironment>,
100 pub payload: ToolPayload,
101}
102
103impl std::fmt::Debug for ToolCall {
104 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
105 f.debug_struct("ToolCall")
106 .field("turn_id", &self.turn_id)
107 .field("call_id", &self.call_id)
108 .field("tool_name", &self.tool_name)
109 .field("model", &self.model)
110 .field(
111 "has_codex_turn_metadata",
112 &self.codex_turn_metadata.is_some(),
113 )
114 .field("truncation_policy", &self.truncation_policy)
115 .field("conversation_history", &self.conversation_history)
116 .field("turn_item_emitter", &"<host turn item emitter>")
117 .field("environment_count", &self.environments.len())
118 .field("payload", &self.payload)
119 .finish()
120 }
121}
122
123impl ToolCall {
124 pub fn function_arguments(&self) -> Result<&str, FunctionCallError> {
125 match &self.payload {
126 ToolPayload::Function { arguments } => Ok(arguments),
127 _ => Err(FunctionCallError::Fatal(format!(
128 "tool {} invoked with incompatible payload",
129 self.tool_name
130 ))),
131 }
132 }
133}