[codex] Use AgentAssertion downstream behind use_agent_identity (#17980)

## Summary

This is the AgentAssertion downstream slice for feature-gated agent
identity support, replacing the oversized AgentAssertion slice from PR
#17807.

It isolates task-scoped downstream AgentAssertion wiring on top of the
merged PR3.1 work without re-carrying the earlier agent registration,
task registration, or task-state history.

This PR includes the task-scoped bug-fix call sites from the review:
generic file upload auth, MCP OpenAI file upload auth, and ARC monitor
auth. Broader user/control-plane calls move to PR4.1 and PR4.2.

## Stack

- PR1: https://github.com/openai/codex/pull/17385 - add
`features.use_agent_identity`
- PR2: https://github.com/openai/codex/pull/17386 - register agent
identities when enabled
- PR3: https://github.com/openai/codex/pull/17387 - register agent tasks
when enabled
- PR3.1: https://github.com/openai/codex/pull/17978 - persist and
prewarm registered tasks per thread
- PR4: this PR - use task-scoped `AgentAssertion` downstream when
enabled
- PR4.1: https://github.com/openai/codex/pull/18094 - introduce
AuthManager-owned background/control-plane `AgentAssertion` auth
- PR4.2: https://github.com/openai/codex/pull/18260 - use background
task auth for additional backend/control-plane calls

## What Changed

- add AgentAssertion envelope generation in `codex-core`
- route downstream HTTP and websocket auth through AgentAssertion when
an agent task is present
- extend the model-provider auth provider so non-bearer authorization
schemes can be passed through cleanly
- make generic file uploads attach the full authorization header value
- make MCP OpenAI file uploads use the cached thread agent task
assertion when present
- make ARC monitor calls use the cached thread agent task assertion when
present

## Why

The original PR had drifted ancestry and showed a much larger diff than
the semantic change actually required. Restacking it onto PR3.1 keeps
the reviewable surface down to the downstream assertion slice.

## Validation

- `just fmt`
- `cargo check -p codex-core -p codex-login -p codex-analytics -p
codex-app-server -p codex-cloud-requirements -p codex-cloud-tasks -p
codex-models-manager -p codex-chatgpt -p codex-model-provider -p
codex-mcp -p codex-core-skills`
- `cargo test -p codex-model-provider bearer_auth_provider`
- `cargo test -p codex-core agent_assertion`
- `cargo test -p codex-app-server remote_control`
- `cargo test -p codex-cloud-requirements fetch_cloud_requirements`
- `cargo test -p codex-models-manager manager::tests`
- `cargo test -p codex-chatgpt`
- `cargo test -p codex-cloud-tasks`
- `cargo test -p codex-login agent_identity`
- `just fix -p codex-core -p codex-login -p codex-analytics -p
codex-app-server -p codex-cloud-requirements -p codex-cloud-tasks -p
codex-models-manager -p codex-chatgpt -p codex-model-provider -p
codex-mcp -p codex-core-skills`
- `just fix -p codex-app-server`
- `git diff --check`
This commit is contained in:
Adrian authored and GitHub committed 2026-04-19 23:16:43 -07:00
1 parent 3c75f9b4dd
commit b44d2851cf
19 files changed
+1206 -65

No files matched your search

+26 -6
View File
@@ -13,6 +13,7 @@ use codex_login::CodexAuth;
use codex_login::default_client::build_reqwest_client;
use codex_protocol::models::MessagePhase;
use codex_protocol::models::ResponseItem;
use reqwest::header::AUTHORIZATION;
const ARC_MONITOR_TIMEOUT: Duration = Duration::from_secs(30);
const CODEX_ARC_MONITOR_ENDPOINT_OVERRIDE: &str = "CODEX_ARC_MONITOR_ENDPOINT_OVERRIDE";
@@ -109,13 +110,31 @@ pub(crate) async fn monitor_action(
},
None => None,
};
let token = if let Some(token) = read_non_empty_env_var(CODEX_ARC_MONITOR_TOKEN) {
token
let (authorization_header_value, account_id) = if let Some(token) =
read_non_empty_env_var(CODEX_ARC_MONITOR_TOKEN)
{
(
format!("Bearer {token}"),
auth.as_ref().and_then(CodexAuth::get_account_id),
)
} else if let Some(authorization_header_value) =
match sess.authorization_header_for_current_agent_task().await {
Ok(authorization_header_value) => authorization_header_value,
Err(err) => {
warn!(
error = %err,
"skipping safety monitor because agent assertion authorization is unavailable"
);
return ArcMonitorOutcome::Ok;
}
}
{
(authorization_header_value, None)
} else {
let Some(auth) = auth.as_ref() else {
return ArcMonitorOutcome::Ok;
};
match auth.get_token() {
let token = match auth.get_token() {
Ok(token) => token,
Err(err) => {
warn!(
@@ -124,7 +143,8 @@ pub(crate) async fn monitor_action(
);
return ArcMonitorOutcome::Ok;
}
}
};
(format!("Bearer {token}"), auth.get_account_id())
};
let url = read_non_empty_env_var(CODEX_ARC_MONITOR_ENDPOINT_OVERRIDE).unwrap_or_else(|| {
@@ -147,8 +167,8 @@ pub(crate) async fn monitor_action(
.post(&url)
.timeout(ARC_MONITOR_TIMEOUT)
.json(&body)
.bearer_auth(token);
if let Some(account_id) = auth.as_ref().and_then(CodexAuth::get_account_id) {
.header(AUTHORIZATION, authorization_header_value);
if let Some(account_id) = account_id {
request = request.header("chatgpt-account-id", account_id);
}