Add Codex OpenAI auth spine wrapper
This commit is contained in:
parent
743f8bcc6c
commit
7bc3a5cb63
11 changed files with 697 additions and 99 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -4,6 +4,8 @@ epiphany-core/target/
|
|||
epiphany-core/Cargo.lock
|
||||
epiphany-openai-adapter/target/
|
||||
epiphany-openai-adapter/Cargo.lock
|
||||
epiphany-openai-codex-spine/target/
|
||||
epiphany-openai-codex-spine/Cargo.lock
|
||||
apps/epiphany-gui/node_modules/
|
||||
apps/epiphany-gui/dist/
|
||||
apps/epiphany-gui/src-tauri/gen/
|
||||
|
|
|
|||
|
|
@ -16,6 +16,8 @@ codex-core = { path = "../vendor/codex/codex-rs/core" }
|
|||
codex-protocol = { path = "../vendor/codex/codex-rs/protocol" }
|
||||
codex-utils-absolute-path = { path = "../vendor/codex/codex-rs/utils/absolute-path" }
|
||||
epiphany-core = { path = "../epiphany-core" }
|
||||
epiphany-openai-adapter = { path = "../epiphany-openai-adapter" }
|
||||
epiphany-openai-codex-spine = { path = "../epiphany-openai-codex-spine" }
|
||||
serde_json = "1"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
toml = "0.9"
|
||||
|
|
|
|||
|
|
@ -5,8 +5,11 @@ pub mod launch;
|
|||
pub mod mutation;
|
||||
pub mod pressure;
|
||||
pub mod reorient;
|
||||
pub mod retrieve;
|
||||
pub mod results;
|
||||
pub mod retrieve;
|
||||
pub mod runtime_results;
|
||||
pub mod scene;
|
||||
pub mod state;
|
||||
|
||||
pub use epiphany_openai_adapter as openai_adapter;
|
||||
pub use epiphany_openai_codex_spine as openai_codex_spine;
|
||||
|
|
|
|||
|
|
@ -77,11 +77,23 @@ pub struct EpiphanyOpenAiStreamEvent {
|
|||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub enum EpiphanyOpenAiStreamPayload {
|
||||
TextDelta { text: String },
|
||||
ReasoningDelta { text: String },
|
||||
ToolCall { call_id: String, name: String, arguments: String },
|
||||
Completed { receipt: EpiphanyOpenAiModelReceipt },
|
||||
Failed { message: String },
|
||||
TextDelta {
|
||||
text: String,
|
||||
},
|
||||
ReasoningDelta {
|
||||
text: String,
|
||||
},
|
||||
ToolCall {
|
||||
call_id: String,
|
||||
name: String,
|
||||
arguments: String,
|
||||
},
|
||||
Completed {
|
||||
receipt: EpiphanyOpenAiModelReceipt,
|
||||
},
|
||||
Failed {
|
||||
message: String,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
|
||||
|
|
|
|||
15
epiphany-openai-codex-spine/Cargo.toml
Normal file
15
epiphany-openai-codex-spine/Cargo.toml
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
[package]
|
||||
name = "epiphany-openai-codex-spine"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
publish = false
|
||||
license-file = "../LICENSE"
|
||||
|
||||
[lib]
|
||||
name = "epiphany_openai_codex_spine"
|
||||
path = "src/lib.rs"
|
||||
|
||||
[dependencies]
|
||||
codex-app-server-protocol = { path = "../vendor/codex/codex-rs/app-server-protocol" }
|
||||
codex-login = { path = "../vendor/codex/codex-rs/login" }
|
||||
epiphany-openai-adapter = { path = "../epiphany-openai-adapter" }
|
||||
102
epiphany-openai-codex-spine/src/lib.rs
Normal file
102
epiphany-openai-codex-spine/src/lib.rs
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
use codex_app_server_protocol::AuthMode;
|
||||
use codex_login::AuthManager;
|
||||
use codex_login::CodexAuth;
|
||||
use epiphany_openai_adapter::EpiphanyOpenAiAdapterStatus;
|
||||
use epiphany_openai_adapter::EpiphanyOpenAiAuthMode;
|
||||
|
||||
pub const CODEX_SPINE_ADAPTER_ID: &str = "codex-openai-subscription-spine";
|
||||
|
||||
pub async fn status_from_auth_manager(
|
||||
auth_manager: &AuthManager,
|
||||
default_model: Option<String>,
|
||||
supports_websockets: bool,
|
||||
) -> EpiphanyOpenAiAdapterStatus {
|
||||
let auth = auth_manager.auth().await;
|
||||
let auth_mode = auth_mode_from_manager(auth_manager, auth.as_ref());
|
||||
let account_id = auth.as_ref().and_then(CodexAuth::get_account_id);
|
||||
let plan_type = auth
|
||||
.as_ref()
|
||||
.and_then(CodexAuth::account_plan_type)
|
||||
.map(|plan| format!("{plan:?}"));
|
||||
|
||||
EpiphanyOpenAiAdapterStatus {
|
||||
adapter_id: CODEX_SPINE_ADAPTER_ID.to_string(),
|
||||
auth_mode,
|
||||
account_id,
|
||||
plan_type,
|
||||
default_model,
|
||||
supports_websockets,
|
||||
codex_transport_attached: true,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn status_from_codex_auth(
|
||||
auth: Option<&CodexAuth>,
|
||||
default_model: Option<String>,
|
||||
supports_websockets: bool,
|
||||
) -> EpiphanyOpenAiAdapterStatus {
|
||||
EpiphanyOpenAiAdapterStatus {
|
||||
adapter_id: CODEX_SPINE_ADAPTER_ID.to_string(),
|
||||
auth_mode: auth_mode_from_codex_auth(auth),
|
||||
account_id: auth.and_then(CodexAuth::get_account_id),
|
||||
plan_type: auth
|
||||
.and_then(CodexAuth::account_plan_type)
|
||||
.map(|plan| format!("{plan:?}")),
|
||||
default_model,
|
||||
supports_websockets,
|
||||
codex_transport_attached: true,
|
||||
}
|
||||
}
|
||||
|
||||
fn auth_mode_from_manager(
|
||||
auth_manager: &AuthManager,
|
||||
auth: Option<&CodexAuth>,
|
||||
) -> EpiphanyOpenAiAuthMode {
|
||||
match auth_manager.auth_mode() {
|
||||
Some(AuthMode::ApiKey) => EpiphanyOpenAiAuthMode::ApiKey,
|
||||
Some(AuthMode::Chatgpt) | Some(AuthMode::ChatgptAuthTokens) => {
|
||||
EpiphanyOpenAiAuthMode::ChatGptSubscription
|
||||
}
|
||||
Some(AuthMode::AgentIdentity) => EpiphanyOpenAiAuthMode::ExternalBearer,
|
||||
None => auth_mode_from_codex_auth(auth),
|
||||
}
|
||||
}
|
||||
|
||||
fn auth_mode_from_codex_auth(auth: Option<&CodexAuth>) -> EpiphanyOpenAiAuthMode {
|
||||
let Some(auth) = auth else {
|
||||
return EpiphanyOpenAiAuthMode::Unknown;
|
||||
};
|
||||
if auth.is_api_key_auth() {
|
||||
EpiphanyOpenAiAuthMode::ApiKey
|
||||
} else if auth.is_chatgpt_auth() {
|
||||
EpiphanyOpenAiAuthMode::ChatGptSubscription
|
||||
} else {
|
||||
EpiphanyOpenAiAuthMode::ExternalBearer
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn api_key_auth_maps_to_typed_adapter_status() {
|
||||
let auth = CodexAuth::from_api_key("test-key");
|
||||
let status = status_from_codex_auth(Some(&auth), Some("gpt-5.4".to_string()), true);
|
||||
|
||||
assert_eq!(status.adapter_id, CODEX_SPINE_ADAPTER_ID);
|
||||
assert_eq!(status.auth_mode, EpiphanyOpenAiAuthMode::ApiKey);
|
||||
assert_eq!(status.default_model.as_deref(), Some("gpt-5.4"));
|
||||
assert!(status.supports_websockets);
|
||||
assert!(status.codex_transport_attached);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn missing_auth_maps_to_unknown_status() {
|
||||
let status = status_from_codex_auth(None, None, false);
|
||||
|
||||
assert_eq!(status.auth_mode, EpiphanyOpenAiAuthMode::Unknown);
|
||||
assert_eq!(status.account_id, None);
|
||||
assert!(!status.supports_websockets);
|
||||
}
|
||||
}
|
||||
|
|
@ -115,10 +115,17 @@ surface:
|
|||
|
||||
The first attempt to wire `codex-core` directly as a standalone path dependency
|
||||
escaped the vendored workspace lock and hit transitive ICU/temporal dependency
|
||||
skew. Do not paper over that with random version pins. The next implementation
|
||||
slice should add a Codex-transport feature or workspace-verified wrapper that
|
||||
builds under the same dependency regime as vendored Codex, while preserving the
|
||||
typed request/event/receipt surface as the Epiphany-owned contract.
|
||||
skew. Do not paper over that with random version pins.
|
||||
|
||||
The first workspace-verified wrapper now exists as
|
||||
`epiphany-openai-codex-spine`. It depends on the pure typed adapter plus the
|
||||
keeper Codex auth types and projects `AuthManager` / `CodexAuth` into a typed
|
||||
`EpiphanyOpenAiAdapterStatus`. `epiphany-codex-bridge` re-exports this spine so
|
||||
the current app-server compatibility shell can compile it without making the
|
||||
pure document crate depend on Codex. The remaining extraction is model
|
||||
transport: typed Epiphany model requests must call the keeper Responses API
|
||||
transport through this boundary rather than route through Epiphany JSON-RPC or
|
||||
the Codex host brain.
|
||||
|
||||
The point is ownership: Epiphany calls a model adapter; it does not live inside
|
||||
the Codex host brain.
|
||||
|
|
|
|||
|
|
@ -550,20 +550,22 @@ compiles as a native typed boundary: auth status, model request, stream event,
|
|||
and terminal receipt documents. It intentionally has no Codex app-server
|
||||
dependency and no generic JSON payload. A direct standalone `codex-core` path
|
||||
dependency attempt hit transitive dependency skew outside the vendored Codex
|
||||
workspace lock; do not "fix" that with random version pins. The next slice is a
|
||||
workspace-verified Codex transport feature/wrapper behind the typed adapter
|
||||
surface.
|
||||
workspace lock; do not "fix" that with random version pins. The first
|
||||
workspace-verified wrapper now exists as `epiphany-openai-codex-spine`: it
|
||||
depends on the pure typed adapter plus the keeper Codex auth types and projects
|
||||
`AuthManager` / `CodexAuth` into a typed `EpiphanyOpenAiAdapterStatus`.
|
||||
`epiphany-codex-bridge` re-exports that spine so the current app-server shell
|
||||
can compile the attachment without contaminating the pure document crate.
|
||||
|
||||
Also: MCP itself is allowed to be JSON. The target is not "replace MCP JSON";
|
||||
the target is an Epiphany-owned boundary that speaks typed Epiphany
|
||||
intent/result/receipt documents internally and normal MCP JSON-RPC externally.
|
||||
|
||||
Continue with the actual whale-carcass cut: create the first
|
||||
`epiphany-openai-adapter` Codex transport integration behind the native typed
|
||||
request/event/receipt surface, using the keeper auth/model transport spine
|
||||
identified in `notes/codex-auth-spine-inventory.md`. Success is Epiphany
|
||||
calling a model adapter rather than living inside the Codex host brain. Do not
|
||||
resume Rider, Unity, Aquarium, Face, dogfood, planning, app, skill,
|
||||
Continue with the actual whale-carcass cut: add model transport behind the
|
||||
native typed request/event/receipt surface, using the keeper Responses API
|
||||
transport identified in `notes/codex-auth-spine-inventory.md`. Success is
|
||||
Epiphany calling a model adapter rather than living inside the Codex host brain.
|
||||
Do not resume Rider, Unity, Aquarium, Face, dogfood, planning, app, skill,
|
||||
marketplace, or bridge expansion until this organ is being cut cleanly.
|
||||
|
||||
The Phase 6 freshness slice is landed. It exposes read-only
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -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, seventeen processor cuts, a real physical extraction in `epiphany-codex-bridge`, a source-grounded Codex auth spine inventory, and now the first native `epiphany-openai-adapter` crate boundary. The bridge owns Codex-facing Epiphany adapters for scene, jobs, context/planning/graph query, retrieval, result projection, runtime-spine result adaptation, launch/prompt doctrine, mutation document mechanics, live-state projection, pressure/pre-compaction rendering, freshness/reorientation projection, CRRC/coordinator/role-board projection, 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 and mutation orchestration. The processor is about 10,339 lines, `epiphany_mutation_routes.rs` is about 1,071 lines, and `epiphany_state_helpers.rs` is down to a 19-line rollout-state loader. `epiphany-openai-adapter` currently defines typed request/event/receipt/auth-status documents with no Codex app-server or generic JSON dependency; a direct standalone `codex-core` wrapper attempt hit dependency skew outside the vendored workspace lock and is recorded as a constraint, not papered over. MCP itself is correctly JSON at its protocol edge; the target is typed Epiphany intent/result/receipt wrappers around MCP, not replacing MCP JSON.
|
||||
next_action: Continue Codex starvation by wiring `epiphany-openai-adapter` to the keeper Codex auth/model transport under a workspace-verified dependency regime, without adding Codex app-server, apps, skills, marketplace, or Epiphany JSON-RPC dependencies. Do not resume app/skill/marketplace expansion.
|
||||
summary: The Codex starvation pass has five typing receipts, seventeen processor cuts, a real physical extraction in `epiphany-codex-bridge`, a source-grounded Codex auth spine inventory, the pure native `epiphany-openai-adapter` document boundary, and the first workspace-verified Codex auth wrapper in `epiphany-openai-codex-spine`. The bridge owns Codex-facing Epiphany adapters for scene, jobs, context/planning/graph query, retrieval, result projection, runtime-spine result adaptation, launch/prompt doctrine, mutation document mechanics, live-state projection, pressure/pre-compaction rendering, freshness/reorientation projection, CRRC/coordinator/role-board projection, 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 and mutation orchestration. The processor is about 10,339 lines, `epiphany_mutation_routes.rs` is about 1,071 lines, and `epiphany_state_helpers.rs` is down to a 19-line rollout-state loader. `epiphany-openai-adapter` defines typed request/event/receipt/auth-status documents with no Codex app-server or generic JSON dependency. `epiphany-openai-codex-spine` depends on the keeper Codex auth types and projects `AuthManager` / `CodexAuth` into typed `EpiphanyOpenAiAdapterStatus`; it compiles through the vendored Codex app-server graph via `epiphany-codex-bridge` without contaminating the pure adapter crate. MCP itself is correctly JSON at its protocol edge; the target is typed Epiphany intent/result/receipt wrappers around MCP, not replacing MCP JSON.
|
||||
next_action: Continue Codex starvation by adding model transport behind the typed `epiphany-openai-adapter` request/event/receipt surface, using the keeper Codex Responses API transport under the workspace-verified spine boundary, without adding Codex app-server, apps, skills, marketplace, or Epiphany JSON-RPC dependencies. Do not resume app/skill/marketplace expansion.
|
||||
|
|
|
|||
609
vendor/codex/codex-rs/Cargo.lock
generated
vendored
609
vendor/codex/codex-rs/Cargo.lock
generated
vendored
File diff suppressed because it is too large
Load diff
Reference in a new issue