use codex_extension_api::JsonToolOutput; use codex_extension_api::ToolCall; use codex_extension_api::ToolExecutor; use codex_extension_api::ToolName; use codex_extension_api::ToolSpec; use codex_otel::MetricsClient; use schemars::JsonSchema; use serde::Deserialize; use serde_json::json; use crate::DEFAULT_LIST_MAX_RESULTS; use crate::LIST_TOOL_NAME; use crate::MAX_LIST_RESULTS; use crate::backend::ListMemoriesRequest; use crate::backend::ListMemoriesResponse; use crate::backend::MemoriesBackend; use crate::metrics::record_tool_call; use crate::metrics::scope_from_optional_path; use crate::metrics::truncated_tag; use super::backend_error_to_function_call; use super::clamp_max_results; use super::memory_function_tool; use super::memory_tool_name; use super::parse_args; #[derive(Deserialize, JsonSchema)] #[serde(deny_unknown_fields)] struct ListArgs { path: Option, cursor: Option, #[schemars(range(min = 1))] max_results: Option, } #[derive(Clone)] pub(super) struct ListTool { pub(super) backend: B, pub(super) metrics_client: Option, } #[async_trait::async_trait] impl ToolExecutor for ListTool where B: MemoriesBackend, { fn tool_name(&self) -> ToolName { memory_tool_name(LIST_TOOL_NAME) } fn spec(&self) -> ToolSpec { memory_function_tool::( LIST_TOOL_NAME, "List immediate files and directories under a path in the Codex memories store.", ) } async fn handle( &self, call: ToolCall, ) -> Result, codex_extension_api::FunctionCallError> { let backend = self.backend.clone(); let args: ListArgs = parse_args(&call)?; let scope = scope_from_optional_path(args.path.as_deref(), "root"); let response = backend .list(ListMemoriesRequest { path: args.path, cursor: args.cursor, max_results: clamp_max_results( args.max_results, DEFAULT_LIST_MAX_RESULTS, MAX_LIST_RESULTS, ), }) .await; record_tool_call( self.metrics_client.as_ref(), LIST_TOOL_NAME, scope, response.is_ok(), truncated_tag(response.as_ref().ok().map(|response| response.truncated)), ); let response = response.map_err(backend_error_to_function_call)?; Ok(Box::new(JsonToolOutput::new(json!(response)))) } }