diff --git a/epiphany-codex-bridge/src/view.rs b/epiphany-codex-bridge/src/view.rs index b8c174dc..cebff3a8 100644 --- a/epiphany-codex-bridge/src/view.rs +++ b/epiphany-codex-bridge/src/view.rs @@ -1,6 +1,10 @@ use std::path::Path; use codex_app_server_protocol::*; +use codex_core::EpiphanyDistillInput; +use codex_core::EpiphanyMapProposalInput; +use codex_core::distill_observation; +use codex_core::propose_map_update; use codex_protocol::protocol::EpiphanyRetrievalState; use codex_protocol::protocol::EpiphanyThreadState; use codex_protocol::protocol::TokenUsageInfo as CoreTokenUsageInfo; @@ -497,3 +501,60 @@ pub async fn map_epiphany_reorient_result_response( note, } } + +pub fn map_epiphany_distill_response( + expected_revision: u64, + params: ThreadEpiphanyDistillParams, +) -> std::result::Result { + let ThreadEpiphanyDistillParams { + source_kind, + status, + text, + subject, + evidence_kind, + code_refs, + .. + } = params; + let proposal = distill_observation(EpiphanyDistillInput { + source_kind, + status, + text, + subject, + evidence_kind, + code_refs, + }) + .map_err(|err| err.to_string())?; + + Ok(ThreadEpiphanyDistillResponse { + expected_revision, + patch: ThreadEpiphanyUpdatePatch { + observations: vec![proposal.observation], + evidence: vec![proposal.evidence], + ..Default::default() + }, + }) +} + +pub fn map_epiphany_propose_response( + state: EpiphanyThreadState, + observation_ids: Vec, +) -> std::result::Result { + let expected_revision = state.revision; + let proposal = propose_map_update(EpiphanyMapProposalInput { + state, + observation_ids, + }) + .map_err(|err| err.to_string())?; + + Ok(ThreadEpiphanyProposeResponse { + expected_revision, + patch: ThreadEpiphanyUpdatePatch { + observations: vec![proposal.observation], + evidence: vec![proposal.evidence], + graphs: Some(proposal.graphs), + graph_frontier: Some(proposal.graph_frontier), + churn: Some(proposal.churn), + ..Default::default() + }, + }) +} diff --git a/notes/fresh-workspace-handoff.md b/notes/fresh-workspace-handoff.md index b2186361..8fef4a9d 100644 --- a/notes/fresh-workspace-handoff.md +++ b/notes/fresh-workspace-handoff.md @@ -720,7 +720,11 @@ Freshness/context/graph-query response construction is also bridge-owned now. `ThreadEpiphanyContextResponse`, and `ThreadEpiphanyGraphQueryResponse` from typed state plus host-supplied watcher/retrieval inputs. App-server still owns loading and watcher registration because those are host lifecycle seams. -`epiphany_read_routes.rs` is about 620 lines after this cut. +`epiphany_read_routes.rs` is about 620 lines after this cut. The distill and +propose patch-response builders have followed into the same bridge view module; +app-server still checks that the target thread is loaded and maps errors onto +JSON-RPC responses, but it no longer shapes those patches itself. +`epiphany_read_routes.rs` is about 579 lines after this cut. 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 diff --git a/state/ledgers.msgpack b/state/ledgers.msgpack index 6f3c3e6e..b360e0a8 100644 Binary files a/state/ledgers.msgpack and b/state/ledgers.msgpack differ diff --git a/state/map.yaml b/state/map.yaml index 0052443a..1c3c57a5 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-two 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, 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 620 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` 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. 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. diff --git a/vendor/codex/codex-rs/app-server/src/codex_message_processor/epiphany_read_routes.rs b/vendor/codex/codex-rs/app-server/src/codex_message_processor/epiphany_read_routes.rs index 21ae10c4..b72c2af2 100644 --- a/vendor/codex/codex-rs/app-server/src/codex_message_processor/epiphany_read_routes.rs +++ b/vendor/codex/codex-rs/app-server/src/codex_message_processor/epiphany_read_routes.rs @@ -1,10 +1,7 @@ use codex_app_server_protocol::*; use codex_core::EPIPHANY_RETRIEVAL_DEFAULT_LIMIT; use codex_core::EPIPHANY_RETRIEVAL_MAX_LIMIT; -use codex_core::EpiphanyDistillInput; -use codex_core::EpiphanyMapProposalInput; use codex_core::EpiphanyRetrieveQuery; -use codex_core::distill_observation; use codex_protocol::ThreadId; use epiphany_codex_bridge::launch::EPIPHANY_REORIENT_LAUNCH_BINDING_ID; use epiphany_codex_bridge::launch::epiphany_role_binding_id; @@ -19,8 +16,10 @@ use epiphany_codex_bridge::view::epiphany_view_needs_pressure; use epiphany_codex_bridge::view::epiphany_view_needs_reorientation_inputs; use epiphany_codex_bridge::view::epiphany_view_needs_runtime_store; use epiphany_codex_bridge::view::map_epiphany_context_response; +use epiphany_codex_bridge::view::map_epiphany_distill_response; use epiphany_codex_bridge::view::map_epiphany_freshness_response; use epiphany_codex_bridge::view::map_epiphany_graph_query_response; +use epiphany_codex_bridge::view::map_epiphany_propose_response; use epiphany_codex_bridge::view::map_epiphany_reorient_result_response; use epiphany_codex_bridge::view::map_epiphany_role_result_response; use epiphany_codex_bridge::view::map_epiphany_view_response; @@ -480,15 +479,7 @@ impl CodexMessageProcessor { request_id: ConnectionRequestId, params: ThreadEpiphanyDistillParams, ) { - let ThreadEpiphanyDistillParams { - thread_id, - source_kind, - status, - text, - subject, - evidence_kind, - code_refs, - } = params; + let thread_id = params.thread_id.clone(); let thread_uuid = match ThreadId::from_string(&thread_id) { Ok(id) => id, @@ -516,15 +507,8 @@ impl CodexMessageProcessor { .await .map(|state| state.revision) .unwrap_or(0); - let proposal = match distill_observation(EpiphanyDistillInput { - source_kind, - status, - text, - subject, - evidence_kind, - code_refs, - }) { - Ok(proposal) => proposal, + let response = match map_epiphany_distill_response(expected_revision, params) { + Ok(response) => response, Err(err) => { self.send_invalid_request_error( request_id, @@ -534,14 +518,6 @@ impl CodexMessageProcessor { return; } }; - let response = ThreadEpiphanyDistillResponse { - expected_revision, - patch: ThreadEpiphanyUpdatePatch { - observations: vec![proposal.observation], - evidence: vec![proposal.evidence], - ..Default::default() - }, - }; self.outgoing.send_response(request_id, response).await; } @@ -551,10 +527,8 @@ impl CodexMessageProcessor { request_id: ConnectionRequestId, params: ThreadEpiphanyProposeParams, ) { - let ThreadEpiphanyProposeParams { - thread_id, - observation_ids, - } = params; + let ThreadEpiphanyProposeParams { thread_id, .. } = ¶ms; + let thread_id = thread_id.clone(); let thread_uuid = match ThreadId::from_string(&thread_id) { Ok(id) => id, @@ -588,12 +562,8 @@ impl CodexMessageProcessor { return; } }; - let expected_revision = state.revision; - let proposal = match codex_core::propose_map_update(EpiphanyMapProposalInput { - state, - observation_ids, - }) { - Ok(proposal) => proposal, + let response = match map_epiphany_propose_response(state, params.observation_ids) { + Ok(response) => response, Err(err) => { self.send_invalid_request_error( request_id, @@ -603,17 +573,6 @@ impl CodexMessageProcessor { return; } }; - let response = ThreadEpiphanyProposeResponse { - expected_revision, - patch: ThreadEpiphanyUpdatePatch { - observations: vec![proposal.observation], - evidence: vec![proposal.evidence], - graphs: Some(proposal.graphs), - graph_frontier: Some(proposal.graph_frontier), - churn: Some(proposal.churn), - ..Default::default() - }, - }; self.outgoing.send_response(request_id, response).await; }