From f1e06b386510fbf5261cc3daebb164034918418f Mon Sep 17 00:00:00 2001 From: "Adam Perry @ OpenAI" Date: Wed, 19 Aug 2026 21:46:34 +0000 Subject: [PATCH] Stream executor capability and skill file reads (#39620) ## What changed - Use `ExecutorFileSystem::read_file_stream` for capability files and executor skill resources regardless of whether filesystem sandboxing is active. - Enforce per-file and bundle size limits incrementally as chunks arrive. - Preserve the existing error for Windows resources when the required filesystem sandbox is unavailable. GitOrigin-RevId: 1a40602a8f913588ec9a6ad7edd2d62eb82436e1 --- .../exec-server/src/capability_discovery.rs | 55 ++++++---------- codex-rs/ext/skills/src/provider/executor.rs | 62 +++++++------------ 2 files changed, 40 insertions(+), 77 deletions(-) diff --git a/codex-rs/exec-server/src/capability_discovery.rs b/codex-rs/exec-server/src/capability_discovery.rs index e935e69698..d2c9253eb2 100644 --- a/codex-rs/exec-server/src/capability_discovery.rs +++ b/codex-rs/exec-server/src/capability_discovery.rs @@ -354,49 +354,32 @@ async fn read_optional_text_file( )); return None; } - let contents = if sandbox.is_some_and(FileSystemSandboxContext::should_run_in_sandbox) { - match file_system.read_file(&path, sandbox).await { - Ok(contents) if contents.len() <= MAX_FILE_BYTES && budget.can_add(contents.len()) => { - contents - } - Ok(_) => { - warnings.push(format!("capability file {path} exceeded its read limit")); - return None; - } - Err(error) => { - warnings.push(format!("failed to read capability file {path}: {error}")); - return None; - } + let mut stream = match file_system.read_file_stream(&path, sandbox).await { + Ok(stream) => stream, + Err(error) => { + warnings.push(format!("failed to read capability file {path}: {error}")); + return None; } - } else { - let mut stream = match file_system.read_file_stream(&path, sandbox).await { - Ok(stream) => stream, + }; + let mut contents = Vec::with_capacity(size); + while let Some(chunk) = stream.next().await { + let chunk = match chunk { + Ok(chunk) => chunk, Err(error) => { warnings.push(format!("failed to read capability file {path}: {error}")); return None; } }; - let mut contents = Vec::with_capacity(size); - while let Some(chunk) = stream.next().await { - let chunk = match chunk { - Ok(chunk) => chunk, - Err(error) => { - warnings.push(format!("failed to read capability file {path}: {error}")); - return None; - } - }; - let Some(new_len) = contents.len().checked_add(chunk.len()) else { - warnings.push(format!("capability file {path} exceeded its read limit")); - return None; - }; - if new_len > MAX_FILE_BYTES || !budget.can_add(new_len) { - warnings.push(format!("capability file {path} exceeded its read limit")); - return None; - } - contents.extend_from_slice(&chunk); + let Some(new_len) = contents.len().checked_add(chunk.len()) else { + warnings.push(format!("capability file {path} exceeded its read limit")); + return None; + }; + if new_len > MAX_FILE_BYTES || !budget.can_add(new_len) { + warnings.push(format!("capability file {path} exceeded its read limit")); + return None; } - contents - }; + contents.extend_from_slice(&chunk); + } let contents = match String::from_utf8(contents) { Ok(contents) => contents, Err(error) => { diff --git a/codex-rs/ext/skills/src/provider/executor.rs b/codex-rs/ext/skills/src/provider/executor.rs index 0745d839a1..10be3c7327 100644 --- a/codex-rs/ext/skills/src/provider/executor.rs +++ b/codex-rs/ext/skills/src/provider/executor.rs @@ -302,51 +302,31 @@ async fn read_bounded_text( "failed to read executor skill resource {resource}: {err}" )) }; - let contents = if sandbox.is_some_and(FileSystemSandboxContext::should_run_in_sandbox) { - if path.infer_path_convention() == Some(PathConvention::Windows) - && sandbox.is_some_and(|context| { - context.windows_sandbox_level - == codex_protocol::config_types::WindowsSandboxLevel::Disabled - }) - { - return Err(SkillProviderError::new( - "executor skill resource requires an unavailable filesystem sandbox", - )); - } - let metadata = file_system - .get_metadata(path, sandbox) - .await - .map_err(&read_error)?; - if metadata.size > MAX_SKILL_RESOURCE_CONTENT_BYTES as u64 { + if sandbox.is_some_and(FileSystemSandboxContext::should_run_in_sandbox) + && path.infer_path_convention() == Some(PathConvention::Windows) + && sandbox.is_some_and(|context| { + context.windows_sandbox_level + == codex_protocol::config_types::WindowsSandboxLevel::Disabled + }) + { + return Err(SkillProviderError::new( + "executor skill resource requires an unavailable filesystem sandbox", + )); + } + + let mut stream = file_system + .read_file_stream(path, sandbox) + .await + .map_err(&read_error)?; + let mut contents = Vec::new(); + while let Some(chunk) = stream.next().await { + let chunk = chunk.map_err(&read_error)?; + if contents.len().saturating_add(chunk.len()) > MAX_SKILL_RESOURCE_CONTENT_BYTES { return Err(SkillProviderError::new(format!( "executor skill resource {resource} exceeds {MAX_SKILL_RESOURCE_CONTENT_BYTES} bytes" ))); } - file_system - .read_file(path, sandbox) - .await - .map_err(&read_error)? - } else { - let mut stream = file_system - .read_file_stream(path, sandbox) - .await - .map_err(&read_error)?; - let mut contents = Vec::new(); - while let Some(chunk) = stream.next().await { - let chunk = chunk.map_err(&read_error)?; - if contents.len().saturating_add(chunk.len()) > MAX_SKILL_RESOURCE_CONTENT_BYTES { - return Err(SkillProviderError::new(format!( - "executor skill resource {resource} exceeds {MAX_SKILL_RESOURCE_CONTENT_BYTES} bytes" - ))); - } - contents.extend_from_slice(&chunk); - } - contents - }; - if contents.len() > MAX_SKILL_RESOURCE_CONTENT_BYTES { - return Err(SkillProviderError::new(format!( - "executor skill resource {resource} exceeds {MAX_SKILL_RESOURCE_CONTENT_BYTES} bytes" - ))); + contents.extend_from_slice(&chunk); } String::from_utf8(contents).map_err(|_| { SkillProviderError::new(format!(