Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions codex-rs/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

66 changes: 66 additions & 0 deletions codex-rs/core/src/tools/handlers/extension_tools.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,8 +88,10 @@ impl CoreToolRuntime for ExtensionToolAdapter {

fn to_extension_call(invocation: &ToolInvocation) -> ExtensionToolCall {
ExtensionToolCall {
turn_id: invocation.turn.sub_id.clone(),
call_id: invocation.call_id.clone(),
tool_name: invocation.tool_name.clone(),
truncation_policy: invocation.turn.truncation_policy,
payload: invocation.payload.clone(),
}
}
Expand All @@ -108,6 +110,7 @@ mod tests {

use pretty_assertions::assert_eq;
use serde_json::json;
use tokio::sync::Mutex;

use super::ExtensionToolAdapter;
use crate::tools::context::ToolCallSource;
Expand Down Expand Up @@ -158,6 +161,27 @@ mod tests {
}
}

struct CapturingExtensionExecutor {
captured_call: Arc<Mutex<Option<codex_tools::ToolCall>>>,
}

#[async_trait::async_trait]
impl codex_extension_api::ToolExecutor<codex_tools::ToolCall> for CapturingExtensionExecutor {
fn tool_name(&self) -> codex_tools::ToolName {
codex_tools::ToolName::plain("extension_echo")
}

async fn handle(
&self,
call: codex_tools::ToolCall,
) -> Result<Box<dyn codex_tools::ToolOutput>, codex_tools::FunctionCallError> {
*self.captured_call.lock().await = Some(call);
Ok(Box::new(codex_tools::JsonToolOutput::new(
json!({ "ok": true }),
)))
}
}

#[tokio::test]
async fn exposes_generic_hook_payloads() {
let handler = ExtensionToolAdapter::new(Arc::new(StubExtensionExecutor));
Expand Down Expand Up @@ -193,4 +217,46 @@ mod tests {
})
);
}

#[tokio::test]
async fn passes_turn_fields_to_extension_call() {
let captured_call = Arc::new(Mutex::new(None));
let handler = ExtensionToolAdapter::new(Arc::new(CapturingExtensionExecutor {
captured_call: Arc::clone(&captured_call),
}));
let (session, turn) = crate::session::tests::make_session_and_context().await;
let turn_id = turn.sub_id.clone();
let truncation_policy = turn.truncation_policy;
let invocation = ToolInvocation {
session: session.into(),
turn: turn.into(),
cancellation_token: tokio_util::sync::CancellationToken::new(),
tracker: Arc::new(tokio::sync::Mutex::new(TurnDiffTracker::new())),
call_id: "call-extension".to_string(),
tool_name: codex_tools::ToolName::plain("extension_echo"),
source: ToolCallSource::Direct,
payload: ToolPayload::Function {
arguments: json!({ "message": "hello" }).to_string(),
},
};

crate::tools::registry::ToolExecutor::handle(&handler, invocation)
.await
.expect("extension call should succeed");

let captured_call = captured_call.lock().await.clone().expect("captured call");
assert_eq!(captured_call.turn_id, turn_id);
assert_eq!(captured_call.call_id, "call-extension");
assert_eq!(
captured_call.tool_name,
codex_tools::ToolName::plain("extension_echo")
);
assert_eq!(captured_call.truncation_policy, truncation_policy);
match captured_call.payload {
ToolPayload::Function { arguments } => {
assert_eq!(arguments, json!({ "message": "hello" }).to_string());
}
payload => panic!("expected function payload, got {payload:?}"),
}
}
}
9 changes: 9 additions & 0 deletions codex-rs/ext/memories/src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ use codex_tools::ToolOutput;
use codex_utils_absolute_path::test_support::PathBufExt;
use codex_utils_absolute_path::test_support::PathExt;
use codex_utils_absolute_path::test_support::test_path_buf;
use codex_utils_output_truncation::TruncationPolicy;
use pretty_assertions::assert_eq;
use serde_json::json;

Expand Down Expand Up @@ -134,8 +135,10 @@ async fn read_tool_reads_memory_file() {

let output = tool
.handle(ToolCall {
turn_id: "turn-1".to_string(),
call_id: "call-1".to_string(),
tool_name: memory_tool_name(crate::READ_TOOL_NAME),
truncation_policy: TruncationPolicy::Bytes(1024),
payload: payload.clone(),
})
.await
Expand Down Expand Up @@ -176,8 +179,10 @@ async fn search_tool_accepts_multiple_queries() {

let output = tool
.handle(ToolCall {
turn_id: "turn-1".to_string(),
call_id: "call-1".to_string(),
tool_name: memory_tool_name(crate::SEARCH_TOOL_NAME),
truncation_policy: TruncationPolicy::Bytes(1024),
payload: payload.clone(),
})
.await
Expand Down Expand Up @@ -244,8 +249,10 @@ async fn search_tool_accepts_windowed_all_match_mode() {

let output = tool
.handle(ToolCall {
turn_id: "turn-1".to_string(),
call_id: "call-1".to_string(),
tool_name: memory_tool_name(crate::SEARCH_TOOL_NAME),
truncation_policy: TruncationPolicy::Bytes(1024),
payload: payload.clone(),
})
.await
Expand Down Expand Up @@ -292,8 +299,10 @@ async fn search_tool_rejects_legacy_single_query() {

let result = tool
.handle(ToolCall {
turn_id: "turn-1".to_string(),
call_id: "call-1".to_string(),
tool_name: memory_tool_name(crate::SEARCH_TOOL_NAME),
truncation_policy: TruncationPolicy::Bytes(1024),
payload,
})
.await;
Expand Down
1 change: 1 addition & 0 deletions codex-rs/tools/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ codex-code-mode = { workspace = true }
codex-features = { workspace = true }
codex-protocol = { workspace = true }
codex-utils-absolute-path = { workspace = true }
codex-utils-output-truncation = { workspace = true }
Comment thread
jif-oai marked this conversation as resolved.
codex-utils-pty = { workspace = true }
codex-utils-string = { workspace = true }
rmcp = { workspace = true, default-features = false, features = [
Expand Down
3 changes: 3 additions & 0 deletions codex-rs/tools/src/tool_call.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
use crate::FunctionCallError;
use crate::ToolName;
use crate::ToolPayload;
use codex_utils_output_truncation::TruncationPolicy;

// TODO: this is temporary and will disappear in the next PR (as we make codex-extension-api generic on Invocation.
#[derive(Clone, Debug)]
pub struct ToolCall {
pub turn_id: String,
pub call_id: String,
pub tool_name: ToolName,
pub truncation_policy: TruncationPolicy,
Comment thread
jif-oai marked this conversation as resolved.
pub payload: ToolPayload,
}

Expand Down
Loading