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
2026-04-15 10:08:27 -07:00
committed by GitHub
Unverified
parent 1dead46c90
commit 8e784bba2f
23 changed files with 1340 additions and 26 deletions
+57
View File
@@ -13,6 +13,7 @@ use crate::agent::Mailbox;
use crate::agent::MailboxReceiver;
use crate::agent::agent_status_from_event;
use crate::agent::status::is_final;
use crate::agent_identity::AgentIdentityManager;
use crate::apps::render_apps_section;
use crate::commit_attribution::commit_message_trailer_instruction;
use crate::compact;
@@ -1510,6 +1511,56 @@ impl Session {
});
}
fn start_agent_identity_registration(self: &Arc<Self>) {
if !self.services.agent_identity_manager.is_enabled() {
return;
}
let weak_sess = Arc::downgrade(self);
let mut auth_state_rx = self.services.auth_manager.subscribe_auth_state();
tokio::spawn(async move {
loop {
let Some(sess) = weak_sess.upgrade() else {
return;
};
match sess
.services
.agent_identity_manager
.ensure_registered_identity()
.await
{
Ok(Some(_)) => return,
Ok(None) => {
drop(sess);
if auth_state_rx.changed().await.is_err() {
return;
}
}
Err(error) => {
sess.fail_agent_identity_registration(error).await;
return;
}
}
}
});
}
async fn fail_agent_identity_registration(self: &Arc<Self>, error: anyhow::Error) {
warn!(error = %error, "agent identity registration failed");
let message = format!(
"Agent identity registration failed. Codex cannot continue while `features.use_agent_identity` is enabled: {error}"
);
self.send_event_raw(Event {
id: self.next_internal_sub_id(),
msg: EventMsg::Error(ErrorEvent {
message,
codex_error_info: Some(CodexErrorInfo::Other),
}),
})
.await;
handlers::shutdown(self, self.next_internal_sub_id()).await;
}
#[allow(clippy::too_many_arguments)]
fn make_turn_context(
conversation_id: ThreadId,
@@ -2055,6 +2106,11 @@ impl Session {
hooks,
rollout: Mutex::new(rollout_recorder),
user_shell: Arc::new(default_shell),
agent_identity_manager: Arc::new(AgentIdentityManager::new(
config.as_ref(),
Arc::clone(&auth_manager),
session_configuration.session_source.clone(),
)),
shell_snapshot_tx,
show_raw_agent_reasoning: config.show_raw_agent_reasoning,
exec_policy,
@@ -2152,6 +2208,7 @@ impl Session {
// Start the watcher after SessionConfigured so it cannot emit earlier events.
sess.start_skills_watcher_listener();
sess.start_agent_identity_registration();
// Construct sandbox_state before MCP startup so it can be sent to each
// MCP server immediately after it becomes ready (avoiding blocking).
let sandbox_state = SandboxState {