Persist worker launch documents in runtime spine
This commit is contained in:
parent
1813915b00
commit
6c18bdaa6e
8 changed files with 190 additions and 13 deletions
|
|
@ -21,6 +21,7 @@ epiphany-openai-adapter = { path = "../epiphany-openai-adapter" }
|
|||
ignore = "0.4.23"
|
||||
postgres = "0.19"
|
||||
reqwest = { version = "0.12", features = ["blocking", "json"] }
|
||||
rmp-serde = "1"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
sha1 = "0.10.6"
|
||||
|
|
|
|||
|
|
@ -99,6 +99,7 @@ pub use runtime_spine::EpiphanyRuntimeJobStatus;
|
|||
pub use runtime_spine::EpiphanyRuntimeSession;
|
||||
pub use runtime_spine::EpiphanyRuntimeSessionStatus;
|
||||
pub use runtime_spine::EpiphanyRuntimeSpineStatus;
|
||||
pub use runtime_spine::EpiphanyRuntimeWorkerLaunchRequest;
|
||||
pub use runtime_spine::RUNTIME_EVENT_TYPE;
|
||||
pub use runtime_spine::RUNTIME_IDENTITY_KEY;
|
||||
pub use runtime_spine::RUNTIME_IDENTITY_TYPE;
|
||||
|
|
@ -106,10 +107,12 @@ pub use runtime_spine::RUNTIME_JOB_RESULT_TYPE;
|
|||
pub use runtime_spine::RUNTIME_JOB_TYPE;
|
||||
pub use runtime_spine::RUNTIME_SESSION_TYPE;
|
||||
pub use runtime_spine::RUNTIME_SPINE_SCHEMA_VERSION;
|
||||
pub use runtime_spine::RUNTIME_WORKER_LAUNCH_REQUEST_SCHEMA_VERSION;
|
||||
pub use runtime_spine::RUNTIME_WORKER_LAUNCH_REQUEST_TYPE;
|
||||
pub use runtime_spine::RuntimeSpineEventOptions;
|
||||
pub use runtime_spine::RuntimeSpineHeartbeatJobOptions;
|
||||
pub use runtime_spine::RuntimeSpineHeartbeatLaunchPlan;
|
||||
pub use runtime_spine::RuntimeSpineHeartbeatLaunchPlanOptions;
|
||||
pub use runtime_spine::RuntimeSpineHeartbeatJobOptions;
|
||||
pub use runtime_spine::RuntimeSpineInitOptions;
|
||||
pub use runtime_spine::RuntimeSpineJobOptions;
|
||||
pub use runtime_spine::RuntimeSpineJobResultOptions;
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
use anyhow::Context;
|
||||
use anyhow::Result;
|
||||
use anyhow::anyhow;
|
||||
use crate::EpiphanyWorkerLaunchDocument;
|
||||
use crate::agent_memory::AGENT_MEMORY_TYPE;
|
||||
use crate::heartbeat_state::HEARTBEAT_STATE_SCHEMA_VERSION;
|
||||
use crate::heartbeat_state::HEARTBEAT_STATE_TYPE;
|
||||
use crate::state_ledger::STATE_LEDGER_SCHEMA_VERSION;
|
||||
use crate::state_ledger::STATE_LEDGER_STORE_TYPE;
|
||||
use anyhow::Context;
|
||||
use anyhow::Result;
|
||||
use anyhow::anyhow;
|
||||
use codex_protocol::protocol::EpiphanyJobBinding;
|
||||
use codex_protocol::protocol::EpiphanyJobKind;
|
||||
use codex_protocol::protocol::EpiphanyRuntimeLink;
|
||||
|
|
@ -28,7 +29,6 @@ use epiphany_openai_adapter::EpiphanyOpenAiAdapterStatus;
|
|||
use epiphany_openai_adapter::EpiphanyOpenAiModelReceipt;
|
||||
use epiphany_openai_adapter::EpiphanyOpenAiModelRequest;
|
||||
use epiphany_openai_adapter::EpiphanyOpenAiStreamEvent;
|
||||
use crate::EpiphanyWorkerLaunchDocument;
|
||||
use serde::Deserialize;
|
||||
use serde::Serialize;
|
||||
use std::collections::BTreeMap;
|
||||
|
|
@ -39,6 +39,7 @@ use std::path::PathBuf;
|
|||
pub const RUNTIME_IDENTITY_TYPE: &str = "epiphany.runtime.identity";
|
||||
pub const RUNTIME_SESSION_TYPE: &str = "epiphany.runtime.session";
|
||||
pub const RUNTIME_JOB_TYPE: &str = "epiphany.runtime.job";
|
||||
pub const RUNTIME_WORKER_LAUNCH_REQUEST_TYPE: &str = "epiphany.runtime.worker_launch_request";
|
||||
pub const RUNTIME_JOB_RESULT_TYPE: &str = "epiphany.runtime.job_result";
|
||||
pub const RUNTIME_EVENT_TYPE: &str = "epiphany.runtime.event";
|
||||
pub const OPENAI_ADAPTER_STATUS_TYPE: &str = "epiphany.openai_adapter_status.v0";
|
||||
|
|
@ -66,6 +67,8 @@ pub const SURFACE_RIDER_BRIDGE_TYPE: &str = "epiphany.surface.rider_bridge";
|
|||
pub const SURFACE_UNITY_BRIDGE_TYPE: &str = "epiphany.surface.unity_bridge";
|
||||
pub const RUNTIME_IDENTITY_KEY: &str = "self";
|
||||
pub const RUNTIME_SPINE_SCHEMA_VERSION: &str = "epiphany.runtime_spine.v0";
|
||||
pub const RUNTIME_WORKER_LAUNCH_REQUEST_SCHEMA_VERSION: &str =
|
||||
"epiphany.runtime.worker_launch_request.v0";
|
||||
pub const OPENAI_ADAPTER_STATUS_SCHEMA_VERSION: &str = "epiphany.openai_adapter_status.v0";
|
||||
pub const OPENAI_MODEL_REQUEST_SCHEMA_VERSION: &str = "epiphany.openai_model_request.v0";
|
||||
pub const OPENAI_MODEL_STREAM_EVENT_SCHEMA_VERSION: &str = "epiphany.openai_model_stream_event.v0";
|
||||
|
|
@ -87,8 +90,7 @@ pub const FACE_SURFACE_SCHEMA_VERSION: &str = "epiphany.face_surface.v0";
|
|||
pub const VOID_MEMORY_SURFACE_SCHEMA_VERSION: &str = "epiphany.void_memory_surface.v0";
|
||||
pub const REPO_INITIALIZATION_SURFACE_SCHEMA_VERSION: &str =
|
||||
"epiphany.repo_initialization_surface.v0";
|
||||
pub const REPO_BIRTH_RUNNER_SURFACE_SCHEMA_VERSION: &str =
|
||||
"epiphany.repo_birth_runner_surface.v0";
|
||||
pub const REPO_BIRTH_RUNNER_SURFACE_SCHEMA_VERSION: &str = "epiphany.repo_birth_runner_surface.v0";
|
||||
pub const RIDER_BRIDGE_SURFACE_SCHEMA_VERSION: &str = "epiphany.rider_bridge_surface.v0";
|
||||
pub const UNITY_BRIDGE_SURFACE_SCHEMA_VERSION: &str = "epiphany.unity_bridge_surface.v0";
|
||||
pub const AGENT_MEMORY_PAYLOAD_SCHEMA_VERSION: &str = "epiphany.agent_memory.v0";
|
||||
|
|
@ -183,6 +185,51 @@ pub struct EpiphanyRuntimeJob {
|
|||
pub metadata: BTreeMap<String, String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, DatabaseEntry)]
|
||||
#[cultcache(
|
||||
type = "epiphany.runtime.worker_launch_request",
|
||||
schema = "EpiphanyRuntimeWorkerLaunchRequest"
|
||||
)]
|
||||
pub struct EpiphanyRuntimeWorkerLaunchRequest {
|
||||
#[cultcache(key = 0)]
|
||||
pub schema_version: String,
|
||||
#[cultcache(key = 1)]
|
||||
pub job_id: String,
|
||||
#[cultcache(key = 2)]
|
||||
pub binding_id: String,
|
||||
#[cultcache(key = 3)]
|
||||
pub role: String,
|
||||
#[cultcache(key = 4)]
|
||||
pub authority_scope: String,
|
||||
#[cultcache(key = 5)]
|
||||
pub instruction: String,
|
||||
#[cultcache(key = 6)]
|
||||
pub output_contract_id: String,
|
||||
#[cultcache(key = 7)]
|
||||
pub document_kind: String,
|
||||
#[cultcache(key = 8)]
|
||||
pub launch_document_msgpack: Vec<u8>,
|
||||
#[cultcache(key = 9, default)]
|
||||
pub metadata: BTreeMap<String, String>,
|
||||
}
|
||||
|
||||
impl EpiphanyRuntimeWorkerLaunchRequest {
|
||||
pub fn launch_document(&self) -> Result<EpiphanyWorkerLaunchDocument> {
|
||||
let document: EpiphanyWorkerLaunchDocument =
|
||||
rmp_serde::from_slice(&self.launch_document_msgpack)
|
||||
.context("failed to decode worker launch document MessagePack")?;
|
||||
let actual_kind = worker_launch_document_kind(&document);
|
||||
if actual_kind != self.document_kind {
|
||||
return Err(anyhow!(
|
||||
"worker launch document kind mismatch: indexed {:?}, payload {:?}",
|
||||
self.document_kind,
|
||||
actual_kind
|
||||
));
|
||||
}
|
||||
Ok(document)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, DatabaseEntry)]
|
||||
#[cultcache(
|
||||
type = "epiphany.runtime.job_result",
|
||||
|
|
@ -325,7 +372,7 @@ pub struct RuntimeSpineJobResultOptions {
|
|||
pub artifact_refs: Vec<String>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub struct RuntimeSpineHeartbeatJobOptions {
|
||||
pub runtime_id: String,
|
||||
pub display_name: String,
|
||||
|
|
@ -336,6 +383,9 @@ pub struct RuntimeSpineHeartbeatJobOptions {
|
|||
pub role: String,
|
||||
pub binding_id: String,
|
||||
pub authority_scope: String,
|
||||
pub instruction: String,
|
||||
pub launch_document: EpiphanyWorkerLaunchDocument,
|
||||
pub output_contract_id: String,
|
||||
pub created_at: String,
|
||||
}
|
||||
|
||||
|
|
@ -373,6 +423,7 @@ pub fn runtime_spine_cache(store_path: impl AsRef<Path>) -> Result<CultCache> {
|
|||
cache.register_entry_type::<EpiphanyRuntimeIdentity>()?;
|
||||
cache.register_entry_type::<EpiphanyRuntimeSession>()?;
|
||||
cache.register_entry_type::<EpiphanyRuntimeJob>()?;
|
||||
cache.register_entry_type::<EpiphanyRuntimeWorkerLaunchRequest>()?;
|
||||
cache.register_entry_type::<EpiphanyRuntimeJobResult>()?;
|
||||
cache.register_entry_type::<EpiphanyRuntimeEvent>()?;
|
||||
cache.register_entry_type::<EpiphanyOpenAiAdapterStatus>()?;
|
||||
|
|
@ -554,7 +605,10 @@ pub fn plan_runtime_spine_heartbeat_launch(
|
|||
blocking_reason: None,
|
||||
},
|
||||
runtime_link: EpiphanyRuntimeLink {
|
||||
id: format!("runtime-link-{}-{}", options.binding_id, options.runtime_job_id),
|
||||
id: format!(
|
||||
"runtime-link-{}-{}",
|
||||
options.binding_id, options.runtime_job_id
|
||||
),
|
||||
binding_id: options.binding_id,
|
||||
surface: "jobLaunch".to_string(),
|
||||
role_id: options.owner_role,
|
||||
|
|
@ -579,8 +633,26 @@ pub fn open_runtime_spine_heartbeat_job(
|
|||
validate_non_empty(&options.role, "role")?;
|
||||
validate_non_empty(&options.binding_id, "binding id")?;
|
||||
validate_non_empty(&options.authority_scope, "authority scope")?;
|
||||
validate_non_empty(&options.instruction, "instruction")?;
|
||||
validate_non_empty(
|
||||
options.launch_document.thread_id(),
|
||||
"worker launch document thread id",
|
||||
)?;
|
||||
validate_non_empty(&options.output_contract_id, "output contract id")?;
|
||||
if options.output_contract_id != options.launch_document.output_contract_id() {
|
||||
return Err(anyhow!(
|
||||
"worker launch output_contract_id must match the typed launch document"
|
||||
));
|
||||
}
|
||||
validate_non_empty(&options.created_at, "created at")?;
|
||||
let store_path = store_path.as_ref();
|
||||
let job_id = options.job_id.clone();
|
||||
let binding_id = options.binding_id.clone();
|
||||
let role = options.role.clone();
|
||||
let authority_scope = options.authority_scope.clone();
|
||||
let instruction = options.instruction.clone();
|
||||
let output_contract_id = options.output_contract_id.clone();
|
||||
let launch_document = options.launch_document.clone();
|
||||
initialize_runtime_spine(
|
||||
store_path,
|
||||
RuntimeSpineInitOptions {
|
||||
|
|
@ -598,7 +670,7 @@ pub fn open_runtime_spine_heartbeat_job(
|
|||
coordinator_note: options.coordinator_note,
|
||||
},
|
||||
)?;
|
||||
create_runtime_job(
|
||||
let job = create_runtime_job(
|
||||
store_path,
|
||||
RuntimeSpineJobOptions {
|
||||
job_id: options.job_id,
|
||||
|
|
@ -611,7 +683,32 @@ pub fn open_runtime_spine_heartbeat_job(
|
|||
),
|
||||
artifact_refs: Vec::new(),
|
||||
},
|
||||
)
|
||||
)?;
|
||||
let mut cache = runtime_spine_cache(store_path)?;
|
||||
cache.pull_all_backing_stores()?;
|
||||
if cache
|
||||
.get::<EpiphanyRuntimeWorkerLaunchRequest>(&job_id)?
|
||||
.is_some()
|
||||
{
|
||||
return Err(anyhow!(
|
||||
"runtime worker launch request {:?} already exists",
|
||||
job_id
|
||||
));
|
||||
}
|
||||
let request = EpiphanyRuntimeWorkerLaunchRequest {
|
||||
schema_version: RUNTIME_WORKER_LAUNCH_REQUEST_SCHEMA_VERSION.to_string(),
|
||||
job_id: job_id.clone(),
|
||||
binding_id,
|
||||
role,
|
||||
authority_scope,
|
||||
instruction,
|
||||
output_contract_id,
|
||||
document_kind: worker_launch_document_kind(&launch_document).to_string(),
|
||||
launch_document_msgpack: encode_worker_launch_document(&launch_document)?,
|
||||
metadata: BTreeMap::new(),
|
||||
};
|
||||
cache.put(&job_id, &request)?;
|
||||
Ok(job)
|
||||
}
|
||||
|
||||
pub fn runtime_job_snapshot(
|
||||
|
|
@ -1056,6 +1153,21 @@ fn epiphany_mutation_contracts() -> Vec<CultNetDocumentMutationContract> {
|
|||
"Heartbeat activation owns agent work; external callers submit intents and watch receipts.",
|
||||
],
|
||||
),
|
||||
mutation_contract(
|
||||
RUNTIME_WORKER_LAUNCH_REQUEST_TYPE,
|
||||
RUNTIME_WORKER_LAUNCH_REQUEST_SCHEMA_VERSION,
|
||||
vec![
|
||||
CultNetDocumentOperation::Snapshot,
|
||||
CultNetDocumentOperation::IntentSubmit,
|
||||
CultNetDocumentOperation::ReceiptWatch,
|
||||
],
|
||||
CultNetMutationAuthority::Coordinator,
|
||||
vec![RUNTIME_WORKER_LAUNCH_REQUEST_TYPE],
|
||||
vec![RUNTIME_JOB_TYPE],
|
||||
vec![
|
||||
"Worker launch requests are typed task-intent documents; runtime jobs are lifecycle receipts, not the source of work intent.",
|
||||
],
|
||||
),
|
||||
mutation_contract(
|
||||
RUNTIME_JOB_RESULT_TYPE,
|
||||
RUNTIME_SPINE_SCHEMA_VERSION,
|
||||
|
|
@ -1385,6 +1497,17 @@ fn validate_non_empty(value: &str, field: &str) -> Result<()> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn worker_launch_document_kind(document: &EpiphanyWorkerLaunchDocument) -> &'static str {
|
||||
match document {
|
||||
EpiphanyWorkerLaunchDocument::Role(_) => "role",
|
||||
EpiphanyWorkerLaunchDocument::Reorient(_) => "reorient",
|
||||
}
|
||||
}
|
||||
|
||||
fn encode_worker_launch_document(document: &EpiphanyWorkerLaunchDocument) -> Result<Vec<u8>> {
|
||||
rmp_serde::to_vec_named(document).context("failed to encode worker launch document MessagePack")
|
||||
}
|
||||
|
||||
fn validate_heartbeat_launch_options(
|
||||
state: &EpiphanyThreadState,
|
||||
options: &RuntimeSpineHeartbeatLaunchPlanOptions,
|
||||
|
|
@ -1602,6 +1725,29 @@ mod tests {
|
|||
role: "modeling".to_string(),
|
||||
binding_id: "modeling-checkpoint-worker".to_string(),
|
||||
authority_scope: "modeling".to_string(),
|
||||
instruction: "Inspect the checkpoint and return typed role findings.".to_string(),
|
||||
launch_document: EpiphanyWorkerLaunchDocument::Role(
|
||||
crate::EpiphanyRoleWorkerLaunchDocument {
|
||||
thread_id: "thread-1".to_string(),
|
||||
role_id: "modeling".to_string(),
|
||||
state_revision: 7,
|
||||
objective: Some("Run heartbeat worker.".to_string()),
|
||||
active_subgoal_id: None,
|
||||
active_subgoals: Vec::new(),
|
||||
active_graph_node_ids: vec!["node-model".to_string()],
|
||||
investigation_checkpoint: None,
|
||||
scratch: None,
|
||||
invariants: Vec::new(),
|
||||
graphs: None,
|
||||
recent_evidence: Vec::new(),
|
||||
recent_observations: Vec::new(),
|
||||
graph_frontier: None,
|
||||
graph_checkpoint: None,
|
||||
planning: None,
|
||||
churn: None,
|
||||
},
|
||||
),
|
||||
output_contract_id: crate::ROLE_WORKER_OUTPUT_CONTRACT_ID.to_string(),
|
||||
created_at: "2026-05-06T00:02:00Z".to_string(),
|
||||
},
|
||||
)?;
|
||||
|
|
@ -1614,6 +1760,21 @@ mod tests {
|
|||
assert_eq!(status.jobs, 1);
|
||||
assert_eq!(status.open_jobs, 1);
|
||||
assert_eq!(status.events, 1);
|
||||
let mut cache = runtime_spine_cache(&store)?;
|
||||
cache.pull_all_backing_stores()?;
|
||||
let launch_request = cache
|
||||
.get::<EpiphanyRuntimeWorkerLaunchRequest>("heartbeat-job-1")?
|
||||
.expect("typed worker launch request should be durable");
|
||||
assert_eq!(launch_request.job_id, "heartbeat-job-1");
|
||||
assert_eq!(launch_request.binding_id, "modeling-checkpoint-worker");
|
||||
assert_eq!(launch_request.role, "modeling");
|
||||
assert_eq!(
|
||||
launch_request.output_contract_id,
|
||||
crate::ROLE_WORKER_OUTPUT_CONTRACT_ID
|
||||
);
|
||||
assert_eq!(launch_request.document_kind, "role");
|
||||
assert!(!launch_request.launch_document_msgpack.is_empty());
|
||||
assert_eq!(launch_request.launch_document()?.thread_id(), "thread-1");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -598,6 +598,14 @@ initialize-session-open-job sequence. That is a small cut, but a real ownership
|
|||
move: native runtime lifecycle belongs to the runtime spine, not the Codex
|
||||
thread wrapper.
|
||||
|
||||
That job-opening path now preserves the work order instead of shaving it into a
|
||||
generic job id. `epiphany-core::EpiphanyRuntimeWorkerLaunchRequest` is a
|
||||
CultCache document keyed by runtime job id, with indexed binding/role/authority,
|
||||
instruction, output contract, document kind, and a MessagePack-encoded typed
|
||||
worker launch document. `EpiphanyRuntimeJob` owns lifecycle; the launch request
|
||||
owns task intent. The MessagePack field is a compatibility wound around
|
||||
ordinary Serde nested documents, not permission to reintroduce JSON cargo.
|
||||
|
||||
The job-launch plan has now followed it. `epiphany-core` owns
|
||||
`plan_runtime_spine_heartbeat_launch`, which validates heartbeat launch
|
||||
requests, reserved binding ids, output contract/document consistency, active
|
||||
|
|
|
|||
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, eighteen 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 service 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, 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. The processor is about 10,339 lines, `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, and is now the default executor for startup-only repo birth specialists; `codex exec` survives there only as explicit fallback. `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; `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, and rollout/session writeback for the compatibility route. `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 more live callers move.
|
||||
summary: The Codex starvation pass has five typing receipts, eighteen 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 service 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, 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. The processor is about 10,339 lines, `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, and is now the default executor for startup-only repo birth specialists; `codex exec` survives there only as explicit fallback. `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; `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. `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 more live callers move.
|
||||
next_action: Move the remaining live model-turn callers, especially heartbeat/specialist execution, onto `epiphany-openai-runtime` or the same native CultNet contract, then shrink the surviving Codex dependency surface to credential refresh, provider/model routing, and the smallest OpenAI Responses call required for Codex subscription compatibility.
|
||||
|
|
|
|||
1
vendor/codex/codex-rs/Cargo.lock
generated
vendored
1
vendor/codex/codex-rs/Cargo.lock
generated
vendored
|
|
@ -5251,6 +5251,7 @@ dependencies = [
|
|||
"ignore",
|
||||
"postgres",
|
||||
"reqwest",
|
||||
"rmp-serde",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha1",
|
||||
|
|
|
|||
|
|
@ -58,8 +58,8 @@ use codex_protocol::user_input::UserInput;
|
|||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use epiphany_core::EpiphanyStateUpdate;
|
||||
use epiphany_core::EpiphanyWorkerLaunchDocument;
|
||||
use epiphany_core::RuntimeSpineHeartbeatLaunchPlanOptions;
|
||||
use epiphany_core::RuntimeSpineHeartbeatJobOptions;
|
||||
use epiphany_core::RuntimeSpineHeartbeatLaunchPlanOptions;
|
||||
use epiphany_core::apply_epiphany_state_update;
|
||||
use epiphany_core::epiphany_state_update_validation_errors;
|
||||
use epiphany_core::open_runtime_spine_heartbeat_job;
|
||||
|
|
@ -744,6 +744,9 @@ fn open_epiphany_runtime_spine_job(
|
|||
role: request.owner_role.clone(),
|
||||
binding_id: request.binding_id.clone(),
|
||||
authority_scope: request.authority_scope.clone(),
|
||||
instruction: request.instruction.clone(),
|
||||
launch_document: request.launch_document.clone(),
|
||||
output_contract_id: request.output_contract_id.clone(),
|
||||
created_at: now,
|
||||
},
|
||||
)
|
||||
|
|
|
|||
Reference in a new issue