From 59f0782400ea2083d5e8dfc9722eaeb3489746bd Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Wed, 13 May 2026 10:36:29 +0100 Subject: [PATCH] Hide Codex login behind OpenAI spine --- .../src/bin/epiphany-openai-spine.rs | 24 ++---------------- epiphany-openai-codex-spine/src/lib.rs | 20 +++++++++++++++ epiphany-openai-runtime/Cargo.toml | 1 - epiphany-openai-runtime/src/lib.rs | 24 ++---------------- notes/fresh-workspace-handoff.md | 6 +++++ state/ledgers.msgpack | Bin 91177 -> 91481 bytes state/map.yaml | 2 +- 7 files changed, 31 insertions(+), 46 deletions(-) diff --git a/epiphany-openai-codex-spine/src/bin/epiphany-openai-spine.rs b/epiphany-openai-codex-spine/src/bin/epiphany-openai-spine.rs index 3776bb18..0de81568 100644 --- a/epiphany-openai-codex-spine/src/bin/epiphany-openai-spine.rs +++ b/epiphany-openai-codex-spine/src/bin/epiphany-openai-spine.rs @@ -1,17 +1,16 @@ use std::env; use std::fs; use std::path::PathBuf; -use std::sync::Arc; use anyhow::Context; use anyhow::Result; use anyhow::anyhow; -use codex_login::AuthCredentialsStoreMode; -use codex_login::AuthManager; use codex_login::CodexAuth; use epiphany_openai_adapter::EpiphanyOpenAiInputItem; use epiphany_openai_adapter::EpiphanyOpenAiModelRequest; use epiphany_openai_codex_spine::EpiphanyCodexOpenAiTransport; +use epiphany_openai_codex_spine::auth_manager; +use epiphany_openai_codex_spine::default_codex_home; use epiphany_openai_codex_spine::responses_request_from_epiphany; use epiphany_openai_codex_spine::status_from_auth_manager; use epiphany_openai_codex_spine::status_from_codex_auth; @@ -129,25 +128,6 @@ fn next_value(iter: &mut impl Iterator, name: &str) -> Result Result { - if let Ok(path) = env::var("CODEX_HOME") { - return Ok(PathBuf::from(path)); - } - let home = env::var("USERPROFILE") - .or_else(|_| env::var("HOME")) - .context("CODEX_HOME is unset and no home directory environment variable exists")?; - Ok(PathBuf::from(home).join(".codex")) -} - -fn auth_manager(codex_home: PathBuf) -> Arc { - AuthManager::shared( - codex_home, - /*enable_codex_api_key_env*/ true, - AuthCredentialsStoreMode::File, - /*chatgpt_base_url*/ None, - ) -} - fn print_json(value: &T) -> Result<()> { println!("{}", serde_json::to_string_pretty(value)?); Ok(()) diff --git a/epiphany-openai-codex-spine/src/lib.rs b/epiphany-openai-codex-spine/src/lib.rs index a6006410..e0298552 100644 --- a/epiphany-openai-codex-spine/src/lib.rs +++ b/epiphany-openai-codex-spine/src/lib.rs @@ -10,6 +10,7 @@ use codex_api::ResponseEvent; use codex_api::ResponsesApiRequest; use codex_api::ResponsesClient; use codex_api::ResponsesOptions; +use codex_login::AuthCredentialsStoreMode; use codex_login::AuthManager; use codex_login::AuthMode; use codex_login::CodexAuth; @@ -34,6 +35,25 @@ use futures::StreamExt; pub const CODEX_SPINE_ADAPTER_ID: &str = "codex-openai-subscription-spine"; +pub fn default_codex_home() -> Result { + if let Ok(path) = std::env::var("CODEX_HOME") { + return Ok(std::path::PathBuf::from(path)); + } + let home = std::env::var("USERPROFILE") + .or_else(|_| std::env::var("HOME")) + .context("CODEX_HOME is unset and no home directory environment variable exists")?; + Ok(std::path::PathBuf::from(home).join(".codex")) +} + +pub fn auth_manager(codex_home: std::path::PathBuf) -> Arc { + AuthManager::shared( + codex_home, + /*enable_codex_api_key_env*/ true, + AuthCredentialsStoreMode::File, + /*chatgpt_base_url*/ None, + ) +} + pub async fn status_from_auth_manager( auth_manager: &AuthManager, default_model: Option, diff --git a/epiphany-openai-runtime/Cargo.toml b/epiphany-openai-runtime/Cargo.toml index 27c4a1bd..11b0db83 100644 --- a/epiphany-openai-runtime/Cargo.toml +++ b/epiphany-openai-runtime/Cargo.toml @@ -12,7 +12,6 @@ path = "src/lib.rs" [dependencies] anyhow = "1" chrono = "0.4.44" -codex-login = { path = "../vendor/codex/codex-rs/login" } epiphany-core = { path = "../epiphany-core" } epiphany-openai-adapter = { path = "../epiphany-openai-adapter" } epiphany-openai-codex-spine = { path = "../epiphany-openai-codex-spine" } diff --git a/epiphany-openai-runtime/src/lib.rs b/epiphany-openai-runtime/src/lib.rs index 32891c6e..3af2627f 100644 --- a/epiphany-openai-runtime/src/lib.rs +++ b/epiphany-openai-runtime/src/lib.rs @@ -1,13 +1,10 @@ use std::path::Path; use std::path::PathBuf; -use std::sync::Arc; use anyhow::Context; use anyhow::Result; use anyhow::anyhow; use chrono::SecondsFormat; -use codex_login::AuthCredentialsStoreMode; -use codex_login::AuthManager; use epiphany_core::EpiphanyRuntimeReorientWorkerResult; use epiphany_core::EpiphanyRuntimeRoleWorkerResult; use epiphany_core::EpiphanyRuntimeWorkerLaunchRequest; @@ -32,6 +29,8 @@ use epiphany_openai_adapter::EpiphanyOpenAiModelRequest; use epiphany_openai_adapter::EpiphanyOpenAiStreamEvent; use epiphany_openai_adapter::EpiphanyOpenAiStreamPayload; use epiphany_openai_codex_spine::EpiphanyCodexOpenAiTransport; +use epiphany_openai_codex_spine::auth_manager; +pub use epiphany_openai_codex_spine::default_codex_home; use epiphany_openai_codex_spine::status_from_auth_manager; use serde_json::Value; @@ -458,16 +457,6 @@ pub fn assistant_text_from_openai_events( Ok(text) } -pub fn default_codex_home() -> Result { - if let Ok(path) = std::env::var("CODEX_HOME") { - return Ok(PathBuf::from(path)); - } - let home = std::env::var("USERPROFILE") - .or_else(|_| std::env::var("HOME")) - .context("CODEX_HOME is unset and no home directory environment variable exists")?; - Ok(PathBuf::from(home).join(".codex")) -} - pub fn default_options( store_path: PathBuf, codex_home: PathBuf, @@ -485,15 +474,6 @@ pub fn default_options( } } -pub fn auth_manager(codex_home: PathBuf) -> Arc { - AuthManager::shared( - codex_home, - /*enable_codex_api_key_env*/ true, - AuthCredentialsStoreMode::File, - /*chatgpt_base_url*/ None, - ) -} - pub fn openai_event_key(request_id: &str, sequence: u64) -> String { format!("{request_id}:{sequence:08}") } diff --git a/notes/fresh-workspace-handoff.md b/notes/fresh-workspace-handoff.md index 8fef4a9d..5857ef06 100644 --- a/notes/fresh-workspace-handoff.md +++ b/notes/fresh-workspace-handoff.md @@ -579,6 +579,12 @@ native runtime/CultNet model calls through this spine and then shrinking the surviving Codex dependency surface to auth, provider/model routing, and the smallest OpenAI Responses call needed for subscription compatibility. +The native runtime no longer imports `codex-login` directly. The Codex spine now +exports the `default_codex_home` and `auth_manager` helpers, so +`epiphany-openai-runtime` reaches Codex credentials only through +`epiphany-openai-codex-spine`. Keep that boundary: if native runtime code needs +credential details, the spine owns the adapter surface. + That native route has now started. `epiphany-openai-adapter` documents are CultCache `DatabaseEntry` types, `epiphany-core::runtime_spine_cache` registers OpenAI adapter status/request/stream-event/receipt records, and the diff --git a/state/ledgers.msgpack b/state/ledgers.msgpack index b360e0a8fcd6c6294af26a53680345a7dbc6cac5..c8e73679d5615e7698aa1c5a544ab63396ccc395 100644 GIT binary patch delta 215 zcmZ2^g7xMp)(Of?sq!0Dr!g|VYu>}SeGemJc+&J&iHyQb#-`H?q8N2FZ@%>`&d*6K zNlj63&QD3LP{_$o&&*Ru&d)0@DJo4a$;{7F$jmFwOqu>LkWrS=disT6M#<^6fs86) z5S58}DGEiYiOCtM#ZVKHi&9fk^GY%kbBY!6^KvQ`N-~P_OVcxebgF`XL290(r$TW- zW?rhELRe~1W?ClDZZKZ~WY+YD!HmKZ#d-}}j0}v-bPY^(4UIz#EUk=9txQd#0FnJm A;{X5v delta 49 zcmcb4igo1))(Of?$?rF+PGe+z+q{Qy`yNKd@FcN@Ek*`LX1WHZx`xIf29{Pvrd9^# FQ2^n15Ptvw diff --git a/state/map.yaml b/state/map.yaml index 1c3c57a5..1274bd22 100644 --- a/state/map.yaml +++ b/state/map.yaml @@ -279,5 +279,5 @@ rejected_paths: reason: making Epiphany cleaner inside Codex still preserves the wrong organ; Codex must be starved down to OpenAI subscription auth/model transport, while Epiphany data and subsystem communication move to typed CultCache/CultNet. current_status: - summary: The Codex starvation pass has five typing receipts, twenty-three processor cuts, a real physical extraction in `epiphany-codex-bridge`, a source-grounded Codex auth spine inventory, the native `epiphany-openai-adapter` document boundary, a buildable Codex auth/model wrapper in `epiphany-openai-codex-spine`, the first native runtime caller in `epiphany-openai-runtime`, native heartbeat launch planning/job opening in `epiphany-core`, native state-update contract/validation/application in `epiphany-core`, and the route-facing mutation/view/coordinator projection services in `epiphany-codex-bridge`. The bridge owns Codex-facing Epiphany adapters for scene, jobs, context/planning/graph query, retrieval/indexing projection, result projection, runtime-spine result adaptation, launch/prompt doctrine, mutation document mechanics, mutation service update/promote/role-accept/reorient-accept/role-launch/reorient-launch/job-launch/job-interrupt application, live-state projection, pressure/pre-compaction rendering, freshness/reorientation projection, CRRC/coordinator/role-board projection, full `thread/epiphany/view` response construction, freshness/context/graph-query response construction, role/reorient result response construction, distill/propose patch-response construction, coordinator status/view construction, and acceptance/evidence signal helpers. Vendored app-server now retains only `epiphany_invalidation.rs` as a root Epiphany module because watcher lifecycle is still app-server machinery; `codex_message_processor.rs` still contains route dispatch, JSON-RPC response shaping, watcher registration/snapshotting, and notification emission, but its root no longer imports the evacuated Epiphany policy/protocol registry. `epiphany_read_routes.rs` is about 579 lines after the full view and read-response extractions; `epiphany_mutation_routes.rs` is about 691 lines after the route-load/notification helper cut and index projection move; `epiphany_state_helpers.rs` is down to a 19-line rollout-state loader; and `codex_thread.rs` is about 1,425 lines after state-update law moved native. `epiphany-openai-adapter` defines CultCache `DatabaseEntry` request/event/receipt/auth-status documents with no Codex app-server or generic JSON dependency. `epiphany-openai-codex-spine` projects `AuthManager` / `CodexAuth` into typed `EpiphanyOpenAiAdapterStatus`, maps typed `EpiphanyOpenAiModelRequest` into Codex API `ResponsesApiRequest`, resolves auth/provider through keeper Codex crates, opens HTTP Responses streams through `codex-api`, maps deltas / completion into typed stream events and receipts, and no longer directly depends on `codex-app-server-protocol` just to name auth mode. `epiphany-openai-runtime` writes OpenAI status/request/stream-event/receipt records plus runtime sessions/jobs/results/events into `epiphany-core::runtime_spine_cache`, can emit the assistant text artifact for legacy result consumers, is now the default executor for startup-only repo birth specialists, and now has `run-worker` to load a durable `EpiphanyRuntimeWorkerLaunchRequest`, call the typed OpenAI runtime, persist typed `EpiphanyRuntimeRoleWorkerResult` / `EpiphanyRuntimeReorientWorkerResult` documents, and complete the original heartbeat/specialist runtime job without Codex worker execution; `codex exec` survives only as explicit fallback in repo-birth startup. `epiphany-mvp-coordinator` now invokes `epiphany-openai-runtime run-worker` for launched runtime job ids and no longer creates a second coordinator-owned shadow runtime job/result as a compensator. `epiphany-core::plan_runtime_spine_heartbeat_launch` validates launch contracts/conflicts and projects durable job bindings/runtime links; `epiphany-core::open_runtime_spine_heartbeat_job` owns the initialize-runtime/ensure-session/create-job sequence and now persists `EpiphanyRuntimeWorkerLaunchRequest` CultCache records keyed by runtime job id, with indexed kind/contract fields plus MessagePack-encoded typed launch documents instead of discarding work intent after queue receipt creation; `roleResult` and `reorientResult` now prefer typed worker result documents and fall back only to generic job summaries for older receipts; `epiphany-core::EpiphanyStateUpdate`, `epiphany_state_update_validation_errors`, and `apply_epiphany_state_update` own state-update document shape and mutation law while `codex-core` calls them for compatibility. Vendored `CodexThread::epiphany_launch_job` is now mostly revision check, persistence validation, launch request handoff, and rollout/session writeback for the compatibility route. Vendored `codex-core` no longer has `epiphany_distillation`, `epiphany_promotion`, `epiphany_proposal`, `epiphany_retrieval`, or `epiphany_rollout` husk modules; it re-exports native surfaces from `epiphany-core` directly, and the only rollout adapter left is the real host seam that supplies Codex's turn-boundary predicate. `epiphany-runtime-spine` advertises OpenAI adapter status/model-request/stream-event/receipt CultNet schemas and mutation contracts, so the native caller now has a real storage route instead of only JSON-RPC. MCP itself is correctly JSON at its protocol edge; the target is typed Epiphany intent/result/receipt wrappers around MCP, not replacing MCP JSON. The current OpenAI spine still pulls `codex-api` and a large transport dependency stack, so its next cut is dependency starvation after richer live callers move. + summary: The Codex starvation pass has five typing receipts, twenty-three processor cuts, a real physical extraction in `epiphany-codex-bridge`, a source-grounded Codex auth spine inventory, the native `epiphany-openai-adapter` document boundary, a buildable Codex auth/model wrapper in `epiphany-openai-codex-spine`, the first native runtime caller in `epiphany-openai-runtime`, native heartbeat launch planning/job opening in `epiphany-core`, native state-update contract/validation/application in `epiphany-core`, and the route-facing mutation/view/coordinator projection services in `epiphany-codex-bridge`. The bridge owns Codex-facing Epiphany adapters for scene, jobs, context/planning/graph query, retrieval/indexing projection, result projection, runtime-spine result adaptation, launch/prompt doctrine, mutation document mechanics, mutation service update/promote/role-accept/reorient-accept/role-launch/reorient-launch/job-launch/job-interrupt application, live-state projection, pressure/pre-compaction rendering, freshness/reorientation projection, CRRC/coordinator/role-board projection, full `thread/epiphany/view` response construction, freshness/context/graph-query response construction, role/reorient result response construction, distill/propose patch-response construction, coordinator status/view construction, and acceptance/evidence signal helpers. Vendored app-server now retains only `epiphany_invalidation.rs` as a root Epiphany module because watcher lifecycle is still app-server machinery; `codex_message_processor.rs` still contains route dispatch, JSON-RPC response shaping, watcher registration/snapshotting, and notification emission, but its root no longer imports the evacuated Epiphany policy/protocol registry. `epiphany_read_routes.rs` is about 579 lines after the full view and read-response extractions; `epiphany_mutation_routes.rs` is about 691 lines after the route-load/notification helper cut and index projection move; `epiphany_state_helpers.rs` is down to a 19-line rollout-state loader; and `codex_thread.rs` is about 1,425 lines after state-update law moved native. `epiphany-openai-adapter` defines CultCache `DatabaseEntry` request/event/receipt/auth-status documents with no Codex app-server or generic JSON dependency. `epiphany-openai-codex-spine` owns all direct Codex auth/model-transport imports for the native OpenAI path: it exposes `default_codex_home` and `auth_manager`, projects `AuthManager` / `CodexAuth` into typed `EpiphanyOpenAiAdapterStatus`, maps typed `EpiphanyOpenAiModelRequest` into Codex API `ResponsesApiRequest`, resolves auth/provider through keeper Codex crates, opens HTTP Responses streams through `codex-api`, maps deltas / completion into typed stream events and receipts, and no longer directly depends on `codex-app-server-protocol` just to name auth mode. `epiphany-openai-runtime` no longer depends directly on `codex-login`; it writes OpenAI status/request/stream-event/receipt records plus runtime sessions/jobs/results/events into `epiphany-core::runtime_spine_cache`, can emit the assistant text artifact for legacy result consumers, is now the default executor for startup-only repo birth specialists, and now has `run-worker` to load a durable `EpiphanyRuntimeWorkerLaunchRequest`, call the typed OpenAI runtime through the spine, persist typed `EpiphanyRuntimeRoleWorkerResult` / `EpiphanyRuntimeReorientWorkerResult` documents, and complete the original heartbeat/specialist runtime job without Codex worker execution; `codex exec` survives only as explicit fallback in repo-birth startup. `epiphany-mvp-coordinator` now invokes `epiphany-openai-runtime run-worker` for launched runtime job ids and no longer creates a second coordinator-owned shadow runtime job/result as a compensator. `epiphany-core::plan_runtime_spine_heartbeat_launch` validates launch contracts/conflicts and projects durable job bindings/runtime links; `epiphany-core::open_runtime_spine_heartbeat_job` owns the initialize-runtime/ensure-session/create-job sequence and now persists `EpiphanyRuntimeWorkerLaunchRequest` CultCache records keyed by runtime job id, with indexed kind/contract fields plus MessagePack-encoded typed launch documents instead of discarding work intent after queue receipt creation; `roleResult` and `reorientResult` now prefer typed worker result documents and fall back only to generic job summaries for older receipts; `epiphany-core::EpiphanyStateUpdate`, `epiphany_state_update_validation_errors`, and `apply_epiphany_state_update` own state-update document shape and mutation law while `codex-core` calls them for compatibility. Vendored `CodexThread::epiphany_launch_job` is now mostly revision check, persistence validation, launch request handoff, and rollout/session writeback for the compatibility route. Vendored `codex-core` no longer has `epiphany_distillation`, `epiphany_promotion`, `epiphany_proposal`, `epiphany_retrieval`, or `epiphany_rollout` husk modules; it re-exports native surfaces from `epiphany-core` directly, and the only rollout adapter left is the real host seam that supplies Codex's turn-boundary predicate. `epiphany-runtime-spine` advertises OpenAI adapter status/model-request/stream-event/receipt CultNet schemas and mutation contracts, so the native caller now has a real storage route instead of only JSON-RPC. MCP itself is correctly JSON at its protocol edge; the target is typed Epiphany intent/result/receipt wrappers around MCP, not replacing MCP JSON. The current OpenAI spine still pulls `codex-api` and a large transport dependency stack, so its next cut is dependency starvation after richer live callers move. next_action: Move remaining coordinator/runtime execution and operator surfaces off app-server JSON-RPC where possible, then shrink the surviving Codex dependency surface to credential refresh, provider/model routing, and the smallest OpenAI Responses call required for Codex subscription compatibility.