diff --git a/codex-rs/core/src/tools/handlers/unified_exec.rs b/codex-rs/core/src/tools/handlers/unified_exec.rs index 4c943c628..f2500a413 100644 --- a/codex-rs/core/src/tools/handlers/unified_exec.rs +++ b/codex-rs/core/src/tools/handlers/unified_exec.rs @@ -146,6 +146,7 @@ impl ToolHandler for UnifiedExecHandler { codex_protocol::protocol::AskForApproval::OnRequest ) { + manager.release_process_id(&process_id).await; return Err(FunctionCallError::RespondToModel(format!( "approval policy is {policy:?}; reject command — you cannot ask for escalated permissions if the approval policy is {policy:?}", policy = context.turn.approval_policy @@ -169,6 +170,7 @@ impl ToolHandler for UnifiedExecHandler { ) .await? { + manager.release_process_id(&process_id).await; return Ok(output); } diff --git a/codex-rs/core/src/unified_exec/session_manager.rs b/codex-rs/core/src/unified_exec/session_manager.rs index da9cb338d..88d65ca14 100644 --- a/codex-rs/core/src/unified_exec/session_manager.rs +++ b/codex-rs/core/src/unified_exec/session_manager.rs @@ -111,6 +111,11 @@ impl UnifiedExecSessionManager { } } + pub(crate) async fn release_process_id(&self, process_id: &str) { + let mut store = self.session_store.lock().await; + store.remove(process_id); + } + pub(crate) async fn exec_command( &self, request: ExecCommandRequest, @@ -129,7 +134,15 @@ impl UnifiedExecSessionManager { request.justification, context, ) - .await?; + .await; + + let session = match session { + Ok(session) => session, + Err(err) => { + self.release_process_id(&request.process_id).await; + return Err(err); + } + }; let max_tokens = resolve_max_tokens(request.max_output_tokens); let yield_time_ms = clamp_yield_time(request.yield_time_ms); @@ -157,6 +170,7 @@ impl UnifiedExecSessionManager { let chunk_id = generate_chunk_id(); let process_id = request.process_id.clone(); if has_exited { + self.release_process_id(&request.process_id).await; let exit = exit_code.unwrap_or(-1); Self::emit_exec_end_from_context( context,