Scope extension capabilities to invocation lifetimes (#41020)

## What changed

- Add callback lifetimes to extension `ToolCall`, `ToolEnvironment`, turn-input context, and skill-read request types.
- Require extension tool executors to handle calls for any invocation lifetime and tie their returned futures to that lifetime.
- Update built-in extensions, adapters, and tests to use the lifetime-scoped APIs without retaining host-owned environment capabilities.

GitOrigin-RevId: aca9ce3a1b6870df550bc84ec9308cc65533f161
This commit is contained in:
Adam Perry @ OpenAI authored and copyberry committed 2026-08-27 02:00:31 +00:00
1 parent eed1dee69e
commit 81e180044d
85 files changed
+477 -202

No files matched your search

+3 -1
View File
@@ -106,7 +106,9 @@ impl ToolContributor for MemoriesExtension {
&self,
_session_store: &ExtensionData,
thread_store: &ExtensionData,
) -> Vec<Arc<dyn codex_extension_api::ToolExecutor<codex_extension_api::ToolCall>>> {
) -> Vec<
Arc<dyn for<'call> codex_extension_api::ToolExecutor<codex_extension_api::ToolCall<'call>>>,
> {
let Some(config) = thread_store.get::<MemoriesExtensionConfig>() else {
return Vec::new();
};
+4 -1
View File
@@ -580,7 +580,10 @@ async fn search_tool_rejects_legacy_single_query() {
assert!(err.to_string().contains("query"));
}
fn memory_tool(memory_root: &Path, tool_name: &str) -> Arc<dyn ToolExecutor<ToolCall>> {
fn memory_tool(
memory_root: &Path,
tool_name: &str,
) -> Arc<dyn for<'call> ToolExecutor<ToolCall<'call>>> {
let expected_tool_name = memory_tool_name(tool_name);
crate::tools::memory_tools(
LocalMemoriesBackend::from_memory_root(memory_root),
@@ -41,7 +41,7 @@ pub(super) struct AddAdHocNoteTool<B> {
pub(super) metrics_client: Option<MetricsClient>,
}
impl<B> ToolExecutor<ToolCall> for AddAdHocNoteTool<B>
impl<'call, B> ToolExecutor<ToolCall<'call>> for AddAdHocNoteTool<B>
where
B: MemoriesBackend,
{
@@ -56,7 +56,10 @@ where
)
}
fn handle(&self, call: ToolCall) -> codex_extension_api::ToolExecutorFuture<'_> {
fn handle<'a>(&'a self, call: ToolCall<'call>) -> codex_extension_api::ToolExecutorFuture<'a>
where
'call: 'a,
{
Box::pin(self.handle_call(call))
}
}
@@ -67,7 +70,7 @@ where
{
async fn handle_call(
&self,
call: ToolCall,
call: ToolCall<'_>,
) -> Result<Box<dyn codex_extension_api::ToolOutput>, codex_extension_api::FunctionCallError>
{
let backend = self.backend.clone();
+6 -3
View File
@@ -39,7 +39,7 @@ pub(super) struct ListTool<B> {
pub(super) metrics_client: Option<MetricsClient>,
}
impl<B> ToolExecutor<ToolCall> for ListTool<B>
impl<'call, B> ToolExecutor<ToolCall<'call>> for ListTool<B>
where
B: MemoriesBackend,
{
@@ -54,7 +54,10 @@ where
)
}
fn handle(&self, call: ToolCall) -> codex_extension_api::ToolExecutorFuture<'_> {
fn handle<'a>(&'a self, call: ToolCall<'call>) -> codex_extension_api::ToolExecutorFuture<'a>
where
'call: 'a,
{
Box::pin(self.handle_call(call))
}
}
@@ -65,7 +68,7 @@ where
{
async fn handle_call(
&self,
call: ToolCall,
call: ToolCall<'_>,
) -> Result<Box<dyn codex_extension_api::ToolOutput>, codex_extension_api::FunctionCallError>
{
let backend = self.backend.clone();
+2 -2
View File
@@ -28,7 +28,7 @@ mod search;
pub(crate) fn memory_tools<B>(
backend: B,
metrics_client: Option<MetricsClient>,
) -> Vec<Arc<dyn ToolExecutor<ToolCall>>>
) -> Vec<Arc<dyn for<'call> ToolExecutor<ToolCall<'call>>>>
where
B: MemoriesBackend,
{
@@ -77,7 +77,7 @@ pub(super) fn memory_function_tool<I: JsonSchema, O: JsonSchema>(
})
}
fn parse_args<T: for<'de> Deserialize<'de>>(call: &ToolCall) -> Result<T, FunctionCallError> {
fn parse_args<T: for<'de> Deserialize<'de>>(call: &ToolCall<'_>) -> Result<T, FunctionCallError> {
let arguments = call.function_arguments()?;
let value = if arguments.trim().is_empty() {
Value::Object(serde_json::Map::new())
+6 -3
View File
@@ -38,7 +38,7 @@ pub(super) struct ReadTool<B> {
pub(super) metrics_client: Option<MetricsClient>,
}
impl<B> ToolExecutor<ToolCall> for ReadTool<B>
impl<'call, B> ToolExecutor<ToolCall<'call>> for ReadTool<B>
where
B: MemoriesBackend,
{
@@ -53,7 +53,10 @@ where
)
}
fn handle(&self, call: ToolCall) -> codex_extension_api::ToolExecutorFuture<'_> {
fn handle<'a>(&'a self, call: ToolCall<'call>) -> codex_extension_api::ToolExecutorFuture<'a>
where
'call: 'a,
{
Box::pin(self.handle_call(call))
}
}
@@ -64,7 +67,7 @@ where
{
async fn handle_call(
&self,
call: ToolCall,
call: ToolCall<'_>,
) -> Result<Box<dyn codex_extension_api::ToolOutput>, codex_extension_api::FunctionCallError>
{
let backend = self.backend.clone();
+6 -3
View File
@@ -47,7 +47,7 @@ pub(super) struct SearchTool<B> {
pub(super) metrics_client: Option<MetricsClient>,
}
impl<B> ToolExecutor<ToolCall> for SearchTool<B>
impl<'call, B> ToolExecutor<ToolCall<'call>> for SearchTool<B>
where
B: MemoriesBackend,
{
@@ -62,7 +62,10 @@ where
)
}
fn handle(&self, call: ToolCall) -> codex_extension_api::ToolExecutorFuture<'_> {
fn handle<'a>(&'a self, call: ToolCall<'call>) -> codex_extension_api::ToolExecutorFuture<'a>
where
'call: 'a,
{
Box::pin(self.handle_call(call))
}
}
@@ -73,7 +76,7 @@ where
{
async fn handle_call(
&self,
call: ToolCall,
call: ToolCall<'_>,
) -> Result<Box<dyn codex_extension_api::ToolOutput>, codex_extension_api::FunctionCallError>
{
let backend = self.backend.clone();