[codex] Remove async_trait from first-party code (#27475)

## Why

First-party async traits should expose their `Send` contracts explicitly
without requiring `async_trait`. This completes the migration pattern
established in #27303 and #27304.

## What changed

- Replaced the remaining first-party `async_trait` traits with native
return-position `impl Future + Send` where statically dispatched and
explicit boxed `Send` futures where object safety is required.
- Kept implementations behavior-preserving, outlining existing async
bodies into inherent methods where that keeps the diff reviewable.
- Removed all direct first-party `async-trait` dependencies and the
workspace dependency declaration.
- Added a cargo-deny policy that permits `async-trait` only through the
remaining transitive wrapper crates.
- Updated `rand` from 0.8.5 to 0.8.6 to resolve RUSTSEC-2026-0097 and
keep the full cargo-deny check passing.

## Validation

- `just test -p codex-exec-server`: 216 passed, 2 skipped.
- `just test -p codex-model-provider`: 39 passed.
- `just test -p codex-core` and `just test`: changed tests passed;
remaining failures are environment-sensitive suites unrelated to this
migration.
- `cargo deny check`
- `just fix`
- `just fmt`
- `cargo shear`
- `just bazel-lock-check`
This commit is contained in:
Adam Perry @ OpenAI
2026-06-11 18:16:39 -07:00
committed by GitHub
parent 1829ed1122
commit 5a56caf18c
98 changed files with 2010 additions and 1050 deletions
+3
View File
@@ -34,6 +34,7 @@ pub use mitm_hook::MitmHookMatchConfig;
pub use network_policy::NetworkDecision;
pub use network_policy::NetworkDecisionSource;
pub use network_policy::NetworkPolicyDecider;
pub use network_policy::NetworkPolicyDeciderFuture;
pub use network_policy::NetworkPolicyDecision;
pub use network_policy::NetworkPolicyRequest;
pub use network_policy::NetworkPolicyRequestArgs;
@@ -59,7 +60,9 @@ pub use proxy::proxy_url_env_value;
pub use runtime::BlockedRequest;
pub use runtime::BlockedRequestArgs;
pub use runtime::BlockedRequestObserver;
pub use runtime::BlockedRequestObserverFuture;
pub use runtime::ConfigReloader;
pub use runtime::ConfigReloaderFuture;
pub use runtime::ConfigState;
pub use runtime::NetworkProxyState;
pub use state::NetworkProxyAuditMetadata;
+15 -15
View File
@@ -3,10 +3,10 @@ use crate::runtime::HostBlockDecision;
use crate::runtime::HostBlockReason;
use crate::state::NetworkProxyState;
use anyhow::Result;
use async_trait::async_trait;
use chrono::SecondsFormat;
use chrono::Utc;
use std::future::Future;
use std::pin::Pin;
use std::sync::Arc;
const AUDIT_TARGET: &str = "codex_otel.network_proxy";
@@ -263,26 +263,26 @@ fn audit_timestamp() -> String {
/// If `command` or `exec_policy_hint` is provided, callers can map exec-policy
/// approvals to network access (e.g., allow all requests for commands matching
/// approved prefixes like `curl *`).
#[async_trait]
pub trait NetworkPolicyDecider: Send + Sync + 'static {
async fn decide(&self, req: NetworkPolicyRequest) -> NetworkDecision;
fn decide(&self, req: NetworkPolicyRequest) -> NetworkPolicyDeciderFuture<'_>;
}
#[async_trait]
pub type NetworkPolicyDeciderFuture<'a> =
Pin<Box<dyn Future<Output = NetworkDecision> + Send + 'a>>;
impl<D: NetworkPolicyDecider + ?Sized> NetworkPolicyDecider for Arc<D> {
async fn decide(&self, req: NetworkPolicyRequest) -> NetworkDecision {
(**self).decide(req).await
fn decide(&self, req: NetworkPolicyRequest) -> NetworkPolicyDeciderFuture<'_> {
Box::pin(async move { (**self).decide(req).await })
}
}
#[async_trait]
impl<F, Fut> NetworkPolicyDecider for F
where
F: Fn(NetworkPolicyRequest) -> Fut + Send + Sync + 'static,
Fut: Future<Output = NetworkDecision> + Send,
Fut: Future<Output = NetworkDecision> + Send + 'static,
{
async fn decide(&self, req: NetworkPolicyRequest) -> NetworkDecision {
(self)(req).await
fn decide(&self, req: NetworkPolicyRequest) -> NetworkPolicyDeciderFuture<'_> {
Box::pin((self)(req))
}
}
@@ -541,6 +541,7 @@ mod tests {
use crate::reasons::REASON_NOT_ALLOWED;
use crate::reasons::REASON_NOT_ALLOWED_LOCAL;
use crate::runtime::ConfigReloader;
use crate::runtime::ConfigReloaderFuture;
use crate::runtime::ConfigState;
use crate::runtime::NetworkProxyAuditMetadata;
use crate::state::NetworkProxyConstraints;
@@ -560,14 +561,13 @@ mod tests {
state: ConfigState,
}
#[async_trait]
impl ConfigReloader for StaticReloader {
async fn maybe_reload(&self) -> anyhow::Result<Option<ConfigState>> {
Ok(None)
fn maybe_reload(&self) -> ConfigReloaderFuture<'_, Option<ConfigState>> {
Box::pin(async { Ok(None) })
}
async fn reload_now(&self) -> anyhow::Result<ConfigState> {
Ok(self.state.clone())
fn reload_now(&self) -> ConfigReloaderFuture<'_, ConfigState> {
Box::pin(async { Ok(self.state.clone()) })
}
fn source_label(&self) -> String {
+17 -18
View File
@@ -20,7 +20,6 @@ use crate::state::build_config_state;
use crate::state::validate_policy_against_constraints;
use anyhow::Context;
use anyhow::Result;
use async_trait::async_trait;
use codex_utils_absolute_path::AbsolutePathBuf;
use globset::GlobSet;
use serde::Serialize;
@@ -30,6 +29,7 @@ use std::future::Future;
use std::net::IpAddr;
use std::net::SocketAddr;
use std::path::Path;
use std::pin::Pin;
use std::sync::Arc;
use std::time::Duration;
use time::OffsetDateTime;
@@ -168,38 +168,38 @@ pub struct ConfigState {
pub blocked_total: u64,
}
#[async_trait]
pub trait ConfigReloader: Send + Sync {
/// Human-readable description of where config is loaded from, for logs.
fn source_label(&self) -> String;
/// Return a freshly loaded state if a reload is needed; otherwise, return `None`.
async fn maybe_reload(&self) -> Result<Option<ConfigState>>;
fn maybe_reload(&self) -> ConfigReloaderFuture<'_, Option<ConfigState>>;
/// Force a reload, regardless of whether a change was detected.
async fn reload_now(&self) -> Result<ConfigState>;
fn reload_now(&self) -> ConfigReloaderFuture<'_, ConfigState>;
}
#[async_trait]
pub type ConfigReloaderFuture<'a, T> = Pin<Box<dyn Future<Output = Result<T>> + Send + 'a>>;
pub trait BlockedRequestObserver: Send + Sync + 'static {
async fn on_blocked_request(&self, request: BlockedRequest);
fn on_blocked_request(&self, request: BlockedRequest) -> BlockedRequestObserverFuture<'_>;
}
#[async_trait]
pub type BlockedRequestObserverFuture<'a> = Pin<Box<dyn Future<Output = ()> + Send + 'a>>;
impl<O: BlockedRequestObserver + ?Sized> BlockedRequestObserver for Arc<O> {
async fn on_blocked_request(&self, request: BlockedRequest) {
(**self).on_blocked_request(request).await
fn on_blocked_request(&self, request: BlockedRequest) -> BlockedRequestObserverFuture<'_> {
Box::pin(async move { (**self).on_blocked_request(request).await })
}
}
#[async_trait]
impl<F, Fut> BlockedRequestObserver for F
where
F: Fn(BlockedRequest) -> Fut + Send + Sync + 'static,
Fut: Future<Output = ()> + Send,
Fut: Future<Output = ()> + Send + 'static,
{
async fn on_blocked_request(&self, request: BlockedRequest) {
(self)(request).await
fn on_blocked_request(&self, request: BlockedRequest) -> BlockedRequestObserverFuture<'_> {
Box::pin((self)(request))
}
}
@@ -891,18 +891,17 @@ pub(crate) fn network_proxy_state_for_policy(
struct NoopReloader;
#[cfg(test)]
#[async_trait]
impl ConfigReloader for NoopReloader {
fn source_label(&self) -> String {
"test config state".to_string()
}
async fn maybe_reload(&self) -> Result<Option<ConfigState>> {
Ok(None)
fn maybe_reload(&self) -> ConfigReloaderFuture<'_, Option<ConfigState>> {
Box::pin(async { Ok(None) })
}
async fn reload_now(&self) -> Result<ConfigState> {
Err(anyhow::anyhow!("force reload is not supported in tests"))
fn reload_now(&self) -> ConfigReloaderFuture<'_, ConfigState> {
Box::pin(async { Err(anyhow::anyhow!("force reload is not supported in tests")) })
}
}
+5 -6
View File
@@ -714,10 +714,10 @@ mod tests {
use crate::network_policy::test_support::capture_events;
use crate::network_policy::test_support::find_event_by_name;
use crate::runtime::ConfigReloader;
use crate::runtime::ConfigReloaderFuture;
use crate::runtime::ConfigState;
use crate::state::NetworkProxyConstraints;
use crate::state::build_config_state;
use async_trait::async_trait;
use pretty_assertions::assert_eq;
use rama_core::extensions::Extensions;
use rama_core::extensions::ExtensionsMut;
@@ -738,14 +738,13 @@ mod tests {
state: ConfigState,
}
#[async_trait]
impl ConfigReloader for StaticReloader {
async fn maybe_reload(&self) -> anyhow::Result<Option<ConfigState>> {
Ok(None)
fn maybe_reload(&self) -> ConfigReloaderFuture<'_, Option<ConfigState>> {
Box::pin(async { Ok(None) })
}
async fn reload_now(&self) -> anyhow::Result<ConfigState> {
Ok(self.state.clone())
fn reload_now(&self) -> ConfigReloaderFuture<'_, ConfigState> {
Box::pin(async { Ok(self.state.clone()) })
}
fn source_label(&self) -> String {