Register agent identities behind use_agent_identity (#17386)

## Summary

Stack PR 2 of 4 for feature-gated agent identity support.

This PR adds agent identity registration behind
`features.use_agent_identity`. It keeps the app-server protocol
unchanged and starts registration after ChatGPT auth exists rather than
requiring a client restart.

## Stack

- PR1: https://github.com/openai/codex/pull/17385 - add
`features.use_agent_identity`
- PR2: https://github.com/openai/codex/pull/17386 - this PR
- PR3: https://github.com/openai/codex/pull/17387 - register agent tasks
when enabled
- PR4: https://github.com/openai/codex/pull/17388 - use `AgentAssertion`
downstream when enabled

## Validation

Covered as part of the local stack validation pass:

- `just fmt`
- `cargo test -p codex-core --lib agent_identity`
- `cargo test -p codex-core --lib agent_assertion`
- `cargo test -p codex-core --lib websocket_agent_task`
- `cargo test -p codex-api api_bridge`
- `cargo build -p codex-cli --bin codex`

## Notes

The full local app-server E2E path is still being debugged after PR
creation. The current branch stack is directionally ready for review
while that follow-up continues.
This commit is contained in:
Adrian authored and GitHub committed 2026-04-15 10:08:27 -07:00
1 parent 1dead46c90
commit 8e784bba2f
23 files changed
+1340 -26

No files matched your search

@@ -54,6 +54,7 @@ async fn refresh_token_succeeds_updates_storage() -> Result<()> {
openai_api_key: None,
tokens: Some(initial_tokens.clone()),
last_refresh: Some(initial_last_refresh),
agent_identity: None,
};
ctx.write_auth(&initial_auth)?;
@@ -117,6 +118,7 @@ async fn refresh_token_refreshes_when_auth_is_unchanged() -> Result<()> {
openai_api_key: None,
tokens: Some(initial_tokens.clone()),
last_refresh: Some(initial_last_refresh),
agent_identity: None,
};
ctx.write_auth(&initial_auth)?;
@@ -171,6 +173,7 @@ async fn refresh_token_skips_refresh_when_auth_changed() -> Result<()> {
openai_api_key: None,
tokens: Some(initial_tokens),
last_refresh: Some(initial_last_refresh),
agent_identity: None,
};
ctx.write_auth(&initial_auth)?;
@@ -180,6 +183,7 @@ async fn refresh_token_skips_refresh_when_auth_changed() -> Result<()> {
openai_api_key: None,
tokens: Some(disk_tokens.clone()),
last_refresh: Some(initial_last_refresh),
agent_identity: None,
};
save_auth(
ctx.codex_home.path(),
@@ -234,6 +238,7 @@ async fn refresh_token_errors_on_account_mismatch() -> Result<()> {
openai_api_key: None,
tokens: Some(initial_tokens.clone()),
last_refresh: Some(initial_last_refresh),
agent_identity: None,
};
ctx.write_auth(&initial_auth)?;
@@ -244,6 +249,7 @@ async fn refresh_token_errors_on_account_mismatch() -> Result<()> {
openai_api_key: None,
tokens: Some(disk_tokens),
last_refresh: Some(initial_last_refresh),
agent_identity: None,
};
save_auth(
ctx.codex_home.path(),
@@ -302,6 +308,7 @@ async fn returns_fresh_tokens_as_is() -> Result<()> {
openai_api_key: None,
tokens: Some(initial_tokens.clone()),
last_refresh: Some(stale_refresh),
agent_identity: None,
};
ctx.write_auth(&initial_auth)?;
@@ -349,6 +356,7 @@ async fn refreshes_token_when_access_token_is_expired() -> Result<()> {
openai_api_key: None,
tokens: Some(initial_tokens.clone()),
last_refresh: Some(fresh_refresh),
agent_identity: None,
};
ctx.write_auth(&initial_auth)?;
@@ -398,6 +406,7 @@ async fn auth_reloads_disk_auth_when_cached_auth_is_stale() -> Result<()> {
openai_api_key: None,
tokens: Some(initial_tokens),
last_refresh: Some(stale_refresh),
agent_identity: None,
};
ctx.write_auth(&initial_auth)?;
@@ -408,6 +417,7 @@ async fn auth_reloads_disk_auth_when_cached_auth_is_stale() -> Result<()> {
openai_api_key: None,
tokens: Some(disk_tokens.clone()),
last_refresh: Some(fresh_refresh),
agent_identity: None,
};
save_auth(
ctx.codex_home.path(),
@@ -459,6 +469,7 @@ async fn auth_reloads_disk_auth_without_calling_expired_refresh_token() -> Resul
openai_api_key: None,
tokens: Some(initial_tokens),
last_refresh: Some(stale_refresh),
agent_identity: None,
};
ctx.write_auth(&initial_auth)?;
@@ -469,6 +480,7 @@ async fn auth_reloads_disk_auth_without_calling_expired_refresh_token() -> Resul
openai_api_key: None,
tokens: Some(disk_tokens.clone()),
last_refresh: Some(fresh_refresh),
agent_identity: None,
};
save_auth(
ctx.codex_home.path(),
@@ -518,6 +530,7 @@ async fn refresh_token_returns_permanent_error_for_expired_refresh_token() -> Re
openai_api_key: None,
tokens: Some(initial_tokens.clone()),
last_refresh: Some(initial_last_refresh),
agent_identity: None,
};
ctx.write_auth(&initial_auth)?;
@@ -570,6 +583,7 @@ async fn refresh_token_does_not_retry_after_permanent_failure() -> Result<()> {
openai_api_key: None,
tokens: Some(initial_tokens.clone()),
last_refresh: Some(initial_last_refresh),
agent_identity: None,
};
ctx.write_auth(&initial_auth)?;
@@ -636,6 +650,7 @@ async fn refresh_token_reloads_changed_auth_after_permanent_failure() -> Result<
openai_api_key: None,
tokens: Some(initial_tokens.clone()),
last_refresh: Some(initial_last_refresh),
agent_identity: None,
};
ctx.write_auth(&initial_auth)?;
@@ -657,6 +672,7 @@ async fn refresh_token_reloads_changed_auth_after_permanent_failure() -> Result<
openai_api_key: None,
tokens: Some(disk_tokens.clone()),
last_refresh: Some(fresh_refresh),
agent_identity: None,
};
save_auth(
ctx.codex_home.path(),
@@ -715,6 +731,7 @@ async fn refresh_token_returns_transient_error_on_server_failure() -> Result<()>
openai_api_key: None,
tokens: Some(initial_tokens.clone()),
last_refresh: Some(initial_last_refresh),
agent_identity: None,
};
ctx.write_auth(&initial_auth)?;
@@ -767,6 +784,7 @@ async fn unauthorized_recovery_reloads_then_refreshes_tokens() -> Result<()> {
openai_api_key: None,
tokens: Some(initial_tokens.clone()),
last_refresh: Some(initial_last_refresh),
agent_identity: None,
};
ctx.write_auth(&initial_auth)?;
@@ -776,6 +794,7 @@ async fn unauthorized_recovery_reloads_then_refreshes_tokens() -> Result<()> {
openai_api_key: None,
tokens: Some(disk_tokens.clone()),
last_refresh: Some(initial_last_refresh),
agent_identity: None,
};
save_auth(
ctx.codex_home.path(),
@@ -859,6 +878,7 @@ async fn unauthorized_recovery_errors_on_account_mismatch() -> Result<()> {
openai_api_key: None,
tokens: Some(initial_tokens.clone()),
last_refresh: Some(initial_last_refresh),
agent_identity: None,
};
ctx.write_auth(&initial_auth)?;
@@ -869,6 +889,7 @@ async fn unauthorized_recovery_errors_on_account_mismatch() -> Result<()> {
openai_api_key: None,
tokens: Some(disk_tokens),
last_refresh: Some(initial_last_refresh),
agent_identity: None,
};
save_auth(
ctx.codex_home.path(),
@@ -926,6 +947,7 @@ async fn unauthorized_recovery_requires_chatgpt_auth() -> Result<()> {
openai_api_key: Some("sk-test".to_string()),
tokens: None,
last_refresh: None,
agent_identity: None,
};
ctx.write_auth(&auth)?;