mirror of
https://github.com/pchuan98/codex.git
synced 2026-07-01 00:31:56 +08:00
0df7e9a820
stacked on #17402. MCP tools returned by `tool_search` (deferred tools) get registered in our `ToolRegistry` with a different format than directly available tools. this leads to two different ways of accessing MCP tools from our tool catalog, only one of which works for each. fix this by registering all MCP tools with the namespace format, since this info is already available. also, direct MCP tools are registered to responsesapi without a namespace, while deferred MCP tools have a namespace. this means we can receive MCP `FunctionCall`s in both formats from namespaces. fix this by always registering MCP tools with namespace, regardless of deferral status. make code mode track `ToolName` provenance of tools so it can map the literal JS function name string to the correct `ToolName` for invocation, rather than supporting both in core. this lets us unify to a single canonical `ToolName` representation for each MCP tool and force everywhere to use that one, without supporting fallbacks.
145 lines
5.7 KiB
Rust
145 lines
5.7 KiB
Rust
use super::RuntimeState;
|
|
use super::callbacks::clear_timeout_callback;
|
|
use super::callbacks::exit_callback;
|
|
use super::callbacks::image_callback;
|
|
use super::callbacks::load_callback;
|
|
use super::callbacks::notify_callback;
|
|
use super::callbacks::set_timeout_callback;
|
|
use super::callbacks::store_callback;
|
|
use super::callbacks::text_callback;
|
|
use super::callbacks::tool_callback;
|
|
use super::callbacks::yield_control_callback;
|
|
|
|
pub(super) fn install_globals(scope: &mut v8::PinScope<'_, '_>) -> Result<(), String> {
|
|
let global = scope.get_current_context().global(scope);
|
|
let console = v8::String::new(scope, "console")
|
|
.ok_or_else(|| "failed to allocate global `console`".to_string())?;
|
|
if global.delete(scope, console.into()) != Some(true) {
|
|
return Err("failed to remove global `console`".to_string());
|
|
}
|
|
|
|
let tools = build_tools_object(scope)?;
|
|
let all_tools = build_all_tools_value(scope)?;
|
|
let clear_timeout = helper_function(scope, "clearTimeout", clear_timeout_callback)?;
|
|
let set_timeout = helper_function(scope, "setTimeout", set_timeout_callback)?;
|
|
let text = helper_function(scope, "text", text_callback)?;
|
|
let image = helper_function(scope, "image", image_callback)?;
|
|
let store = helper_function(scope, "store", store_callback)?;
|
|
let load = helper_function(scope, "load", load_callback)?;
|
|
let notify = helper_function(scope, "notify", notify_callback)?;
|
|
let yield_control = helper_function(scope, "yield_control", yield_control_callback)?;
|
|
let exit = helper_function(scope, "exit", exit_callback)?;
|
|
|
|
set_global(scope, global, "tools", tools.into())?;
|
|
set_global(scope, global, "ALL_TOOLS", all_tools)?;
|
|
set_global(scope, global, "clearTimeout", clear_timeout.into())?;
|
|
set_global(scope, global, "setTimeout", set_timeout.into())?;
|
|
set_global(scope, global, "text", text.into())?;
|
|
set_global(scope, global, "image", image.into())?;
|
|
set_global(scope, global, "store", store.into())?;
|
|
set_global(scope, global, "load", load.into())?;
|
|
set_global(scope, global, "notify", notify.into())?;
|
|
set_global(scope, global, "yield_control", yield_control.into())?;
|
|
set_global(scope, global, "exit", exit.into())?;
|
|
Ok(())
|
|
}
|
|
|
|
fn build_tools_object<'s>(
|
|
scope: &mut v8::PinScope<'s, '_>,
|
|
) -> Result<v8::Local<'s, v8::Object>, String> {
|
|
let tools = v8::Object::new(scope);
|
|
let enabled_tools = scope
|
|
.get_slot::<RuntimeState>()
|
|
.map(|state| state.enabled_tools.clone())
|
|
.unwrap_or_default();
|
|
|
|
for (tool_index, tool) in enabled_tools.iter().enumerate() {
|
|
let name = v8::String::new(scope, &tool.global_name)
|
|
.ok_or_else(|| "failed to allocate tool name".to_string())?;
|
|
let function = tool_function(scope, tool_index)?;
|
|
tools.set(scope, name.into(), function.into());
|
|
}
|
|
Ok(tools)
|
|
}
|
|
|
|
fn build_all_tools_value<'s>(
|
|
scope: &mut v8::PinScope<'s, '_>,
|
|
) -> Result<v8::Local<'s, v8::Value>, String> {
|
|
let enabled_tools = scope
|
|
.get_slot::<RuntimeState>()
|
|
.map(|state| state.enabled_tools.clone())
|
|
.unwrap_or_default();
|
|
let array = v8::Array::new(scope, enabled_tools.len() as i32);
|
|
let name_key = v8::String::new(scope, "name")
|
|
.ok_or_else(|| "failed to allocate ALL_TOOLS name key".to_string())?;
|
|
let description_key = v8::String::new(scope, "description")
|
|
.ok_or_else(|| "failed to allocate ALL_TOOLS description key".to_string())?;
|
|
|
|
for (index, tool) in enabled_tools.iter().enumerate() {
|
|
let item = v8::Object::new(scope);
|
|
let name = v8::String::new(scope, &tool.global_name)
|
|
.ok_or_else(|| "failed to allocate ALL_TOOLS name".to_string())?;
|
|
let description = v8::String::new(scope, &tool.description)
|
|
.ok_or_else(|| "failed to allocate ALL_TOOLS description".to_string())?;
|
|
|
|
if item.set(scope, name_key.into(), name.into()) != Some(true) {
|
|
return Err("failed to set ALL_TOOLS name".to_string());
|
|
}
|
|
if item.set(scope, description_key.into(), description.into()) != Some(true) {
|
|
return Err("failed to set ALL_TOOLS description".to_string());
|
|
}
|
|
if array.set_index(scope, index as u32, item.into()) != Some(true) {
|
|
return Err("failed to append ALL_TOOLS metadata".to_string());
|
|
}
|
|
}
|
|
|
|
Ok(array.into())
|
|
}
|
|
|
|
fn helper_function<'s, F>(
|
|
scope: &mut v8::PinScope<'s, '_>,
|
|
name: &str,
|
|
callback: F,
|
|
) -> Result<v8::Local<'s, v8::Function>, String>
|
|
where
|
|
F: v8::MapFnTo<v8::FunctionCallback>,
|
|
{
|
|
let name =
|
|
v8::String::new(scope, name).ok_or_else(|| "failed to allocate helper name".to_string())?;
|
|
let template = v8::FunctionTemplate::builder(callback)
|
|
.data(name.into())
|
|
.build(scope);
|
|
template
|
|
.get_function(scope)
|
|
.ok_or_else(|| "failed to create helper function".to_string())
|
|
}
|
|
|
|
fn tool_function<'s>(
|
|
scope: &mut v8::PinScope<'s, '_>,
|
|
tool_index: usize,
|
|
) -> Result<v8::Local<'s, v8::Function>, String> {
|
|
let data = v8::String::new(scope, &tool_index.to_string())
|
|
.ok_or_else(|| "failed to allocate tool callback data".to_string())?;
|
|
let template = v8::FunctionTemplate::builder(tool_callback)
|
|
.data(data.into())
|
|
.build(scope);
|
|
template
|
|
.get_function(scope)
|
|
.ok_or_else(|| "failed to create tool function".to_string())
|
|
}
|
|
|
|
fn set_global<'s>(
|
|
scope: &mut v8::PinScope<'s, '_>,
|
|
global: v8::Local<'s, v8::Object>,
|
|
name: &str,
|
|
value: v8::Local<'s, v8::Value>,
|
|
) -> Result<(), String> {
|
|
let key = v8::String::new(scope, name)
|
|
.ok_or_else(|| format!("failed to allocate global `{name}`"))?;
|
|
if global.set(scope, key.into(), value) == Some(true) {
|
|
Ok(())
|
|
} else {
|
|
Err(format!("failed to set global `{name}`"))
|
|
}
|
|
}
|