From c3bb20602d77b0c8e62f84e9956e8086f439dfeb Mon Sep 17 00:00:00 2001 From: cooper-oai Date: Mon, 19 Oct 2026 22:12:51 +0000 Subject: [PATCH] Add wire types for runtime agent registration (#55398) ## What changed Add `codex-runtime-agent-instance` with serializable registration requests and deserializable responses. Requests support OAuth, personal access tokens, and platform API keys, along with an installation ID, public key, harness metadata, and runtime role. Responses include runtime and host IDs plus ChatGPT or platform actor context. Use `RedactedString` for credentials to keep secrets out of debug output. ## Testing Add tests covering request serialization for all three credential types, debug redaction, response decoding for both actor types, and rejection of responses missing required fields. GitOrigin-RevId: f33bae734fdd1402144dee407e5b868f5eef1d37 --- codex-rs/Cargo.lock | 11 +++ codex-rs/Cargo.toml | 1 + codex-rs/runtime-agent-instance/BUILD.bazel | 6 ++ codex-rs/runtime-agent-instance/Cargo.toml | 20 ++++ .../runtime-agent-instance/src/bootstrap.rs | 71 ++++++++++++++ .../src/bootstrap_tests.rs | 97 +++++++++++++++++++ codex-rs/runtime-agent-instance/src/lib.rs | 11 +++ 7 files changed, 217 insertions(+) create mode 100644 codex-rs/runtime-agent-instance/BUILD.bazel create mode 100644 codex-rs/runtime-agent-instance/Cargo.toml create mode 100644 codex-rs/runtime-agent-instance/src/bootstrap.rs create mode 100644 codex-rs/runtime-agent-instance/src/bootstrap_tests.rs create mode 100644 codex-rs/runtime-agent-instance/src/lib.rs diff --git a/codex-rs/Cargo.lock b/codex-rs/Cargo.lock index 221bab821c..30d567b26b 100644 --- a/codex-rs/Cargo.lock +++ b/codex-rs/Cargo.lock @@ -4574,6 +4574,17 @@ dependencies = [ "uuid", ] +[[package]] +name = "codex-runtime-agent-instance" +version = "0.0.0" +dependencies = [ + "codex-utils-redacted-string", + "pretty_assertions", + "serde", + "serde_json", + "uuid", +] + [[package]] name = "codex-sandboxing" version = "0.0.0" diff --git a/codex-rs/Cargo.toml b/codex-rs/Cargo.toml index e588cd6e59..5e1b303315 100644 --- a/codex-rs/Cargo.toml +++ b/codex-rs/Cargo.toml @@ -102,6 +102,7 @@ members = [ "rollout-trace", "rmcp-client", "responses-api-proxy", + "runtime-agent-instance", "response-debug-context", "sandboxing", "stdio-to-uds", diff --git a/codex-rs/runtime-agent-instance/BUILD.bazel b/codex-rs/runtime-agent-instance/BUILD.bazel new file mode 100644 index 0000000000..c1f2245ebe --- /dev/null +++ b/codex-rs/runtime-agent-instance/BUILD.bazel @@ -0,0 +1,6 @@ +load("//:defs.bzl", "codex_rust_crate") + +codex_rust_crate( + name = "runtime-agent-instance", + crate_name = "codex_runtime_agent_instance", +) diff --git a/codex-rs/runtime-agent-instance/Cargo.toml b/codex-rs/runtime-agent-instance/Cargo.toml new file mode 100644 index 0000000000..1398925d35 --- /dev/null +++ b/codex-rs/runtime-agent-instance/Cargo.toml @@ -0,0 +1,20 @@ +[package] +name = "codex-runtime-agent-instance" +version.workspace = true +edition.workspace = true +license.workspace = true + +[lib] +doctest = false + +[lints] +workspace = true + +[dependencies] +codex-utils-redacted-string = { workspace = true } +serde = { workspace = true, features = ["derive"] } +uuid = { workspace = true, features = ["serde"] } + +[dev-dependencies] +pretty_assertions = { workspace = true } +serde_json = { workspace = true } diff --git a/codex-rs/runtime-agent-instance/src/bootstrap.rs b/codex-rs/runtime-agent-instance/src/bootstrap.rs new file mode 100644 index 0000000000..1e2804e262 --- /dev/null +++ b/codex-rs/runtime-agent-instance/src/bootstrap.rs @@ -0,0 +1,71 @@ +//! Wire types for one-call runtime registration. Credentials are inputs, not identity claims. + +use codex_utils_redacted_string::RedactedString; +use serde::Deserialize; +use serde::Serialize; +use uuid::Uuid; + +#[derive(Clone, Debug, Serialize)] +#[serde(tag = "type", rename_all = "snake_case")] +pub enum AuthenticatedActorAuthorization { + Oauth { credential: RedactedString }, + PersonalAccessToken { credential: RedactedString }, + PlatformApiKey { credential: RedactedString }, +} + +#[derive(Clone, Debug, Serialize)] +#[serde(tag = "type", rename_all = "snake_case")] +pub enum AgentHostAuthorization { + // An installation ID is tracking metadata, not proof of device ownership. + InstallationId { installation_id: Uuid }, +} + +#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize)] +#[serde(rename_all = "snake_case")] +pub enum RuntimeRole { + Orchestrator, + Executor, + OrchestratorAndExecutor, +} + +#[derive(Clone, Debug, Serialize)] +pub struct RuntimeRegistrationMetadata { + #[serde(skip_serializing_if = "Option::is_none")] + pub harness: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub harness_version: Option, + pub role: RuntimeRole, +} + +#[derive(Clone, Debug, Serialize)] +pub struct RuntimeRegistrationRequest { + pub actor_auth: AuthenticatedActorAuthorization, + pub agent_host_auth: AgentHostAuthorization, + pub public_key: String, + pub metadata: RuntimeRegistrationMetadata, +} + +#[derive(Clone, Debug, PartialEq, Eq, Deserialize)] +pub struct RuntimeRegistrationResponse { + pub runtime_agent_instance_id: String, + pub agent_host_id: String, + pub actor: ActorInfo, +} + +#[derive(Clone, Debug, PartialEq, Eq, Deserialize)] +#[serde(tag = "kind", rename_all = "snake_case")] +pub enum ActorInfo { + Chatgpt { + user_id: String, + account_id: String, + }, + Platform { + user_id: String, + organization_id: String, + project_id: Option, + }, +} + +#[cfg(test)] +#[path = "bootstrap_tests.rs"] +mod tests; diff --git a/codex-rs/runtime-agent-instance/src/bootstrap_tests.rs b/codex-rs/runtime-agent-instance/src/bootstrap_tests.rs new file mode 100644 index 0000000000..8a4635a7fc --- /dev/null +++ b/codex-rs/runtime-agent-instance/src/bootstrap_tests.rs @@ -0,0 +1,97 @@ +//! Registration wire compatibility for each supported actor entry point. + +use super::*; +use pretty_assertions::assert_eq; +use serde_json::json; + +#[test] +fn registration_serializes_the_supported_actor_authorizations() { + let installation_id = "00000000-0000-4000-8000-000000000001"; + for (authorization, expected) in [ + ( + AuthenticatedActorAuthorization::Oauth { + credential: "secret".into(), + }, + json!({"type": "oauth", "credential": "secret"}), + ), + ( + AuthenticatedActorAuthorization::PersonalAccessToken { + credential: "secret".into(), + }, + json!({"type": "personal_access_token", "credential": "secret"}), + ), + ( + AuthenticatedActorAuthorization::PlatformApiKey { + credential: "secret".into(), + }, + json!({"type": "platform_api_key", "credential": "secret"}), + ), + ] { + let request = RuntimeRegistrationRequest { + actor_auth: authorization, + agent_host_auth: AgentHostAuthorization::InstallationId { + installation_id: installation_id.parse().unwrap(), + }, + public_key: "ssh-ed25519 public-key".into(), + metadata: RuntimeRegistrationMetadata { + harness: Some("codex".into()), + harness_version: Some("0.0.0".into()), + role: RuntimeRole::OrchestratorAndExecutor, + }, + }; + assert!(!format!("{request:?}").contains("secret")); + assert_eq!( + serde_json::to_value(request).unwrap(), + json!({ + "actor_auth": expected, + "agent_host_auth": {"type": "installation_id", "installation_id": installation_id}, + "public_key": "ssh-ed25519 public-key", + "metadata": {"harness": "codex", "harness_version": "0.0.0", "role": "orchestrator_and_executor"}, + }) + ); + } +} + +#[test] +fn registration_decodes_composite_actor_contexts() { + for (actor, expected) in [ + ( + json!({"kind": "chatgpt", "user_id": "user", "account_id": "account"}), + ActorInfo::Chatgpt { + user_id: "user".into(), + account_id: "account".into(), + }, + ), + ( + json!({"kind": "platform", "user_id": "user", "organization_id": "org", "project_id": "project"}), + ActorInfo::Platform { + user_id: "user".into(), + organization_id: "org".into(), + project_id: Some("project".into()), + }, + ), + ] { + let response: RuntimeRegistrationResponse = serde_json::from_value(json!({ + "runtime_agent_instance_id": "runtime", "agent_host_id": "host", "actor": actor, + })) + .unwrap(); + assert_eq!( + response, + RuntimeRegistrationResponse { + runtime_agent_instance_id: "runtime".into(), + agent_host_id: "host".into(), + actor: expected, + } + ); + } + assert!( + serde_json::from_value::( + json!({"runtime_agent_instance_id": "runtime"}) + ) + .is_err() + ); + assert!( + serde_json::from_value::(json!({"kind": "platform", "user_id": "user"})) + .is_err() + ); +} diff --git a/codex-rs/runtime-agent-instance/src/lib.rs b/codex-rs/runtime-agent-instance/src/lib.rs new file mode 100644 index 0000000000..3fceaf5c10 --- /dev/null +++ b/codex-rs/runtime-agent-instance/src/lib.rs @@ -0,0 +1,11 @@ +//! OpenAI runtime registration types, independent of credential storage and model providers. + +mod bootstrap; + +pub use bootstrap::ActorInfo; +pub use bootstrap::AgentHostAuthorization; +pub use bootstrap::AuthenticatedActorAuthorization; +pub use bootstrap::RuntimeRegistrationMetadata; +pub use bootstrap::RuntimeRegistrationRequest; +pub use bootstrap::RuntimeRegistrationResponse; +pub use bootstrap::RuntimeRole;