Move Epiphany view projection into bridge
This commit is contained in:
parent
431c56f525
commit
88f8ca2436
6 changed files with 350 additions and 280 deletions
|
|
@ -11,6 +11,7 @@ pub mod retrieve;
|
|||
pub mod runtime_results;
|
||||
pub mod scene;
|
||||
pub mod state;
|
||||
pub mod view;
|
||||
|
||||
pub use epiphany_openai_adapter as openai_adapter;
|
||||
pub use epiphany_openai_codex_spine as openai_codex_spine;
|
||||
|
|
|
|||
314
epiphany-codex-bridge/src/view.rs
Normal file
314
epiphany-codex-bridge/src/view.rs
Normal file
|
|
@ -0,0 +1,314 @@
|
|||
use std::path::Path;
|
||||
|
||||
use codex_app_server_protocol::*;
|
||||
use codex_protocol::protocol::EpiphanyRetrievalState;
|
||||
use codex_protocol::protocol::EpiphanyThreadState;
|
||||
use codex_protocol::protocol::TokenUsageInfo as CoreTokenUsageInfo;
|
||||
use epiphany_core::EpiphanySceneInput;
|
||||
use epiphany_core::derive_scene;
|
||||
|
||||
use crate::context::map_epiphany_planning;
|
||||
use crate::coordinator::derive_epiphany_coordinator_status;
|
||||
use crate::coordinator::epiphany_reorient_finding_already_accepted;
|
||||
use crate::coordinator::map_epiphany_coordinator_view;
|
||||
use crate::coordinator::map_epiphany_crrc_recommendation;
|
||||
use crate::coordinator::map_epiphany_roles;
|
||||
use crate::coordinator::render_epiphany_roles_note;
|
||||
use crate::jobs::map_epiphany_jobs;
|
||||
use crate::launch::EPIPHANY_REORIENT_LAUNCH_BINDING_ID;
|
||||
use crate::pressure::map_epiphany_pressure;
|
||||
use crate::reorient::EpiphanyFreshnessWatcherSnapshot;
|
||||
use crate::reorient::map_epiphany_freshness;
|
||||
use crate::reorient::map_epiphany_reorient;
|
||||
use crate::runtime_results::load_epiphany_reorient_result_snapshot;
|
||||
use crate::scene::map_core_epiphany_scene_action;
|
||||
use crate::scene::map_epiphany_scene;
|
||||
|
||||
pub fn default_epiphany_view_lenses() -> Vec<ThreadEpiphanyViewLens> {
|
||||
vec![
|
||||
ThreadEpiphanyViewLens::Scene,
|
||||
ThreadEpiphanyViewLens::Jobs,
|
||||
ThreadEpiphanyViewLens::Roles,
|
||||
ThreadEpiphanyViewLens::Planning,
|
||||
ThreadEpiphanyViewLens::Pressure,
|
||||
ThreadEpiphanyViewLens::Reorient,
|
||||
ThreadEpiphanyViewLens::Crrc,
|
||||
ThreadEpiphanyViewLens::Coordinator,
|
||||
]
|
||||
}
|
||||
|
||||
pub fn epiphany_view_needs_jobs(lenses: &[ThreadEpiphanyViewLens]) -> bool {
|
||||
lenses.contains(&ThreadEpiphanyViewLens::Jobs)
|
||||
|| lenses.contains(&ThreadEpiphanyViewLens::Roles)
|
||||
|| lenses.contains(&ThreadEpiphanyViewLens::Crrc)
|
||||
|| lenses.contains(&ThreadEpiphanyViewLens::Coordinator)
|
||||
}
|
||||
|
||||
pub fn epiphany_view_needs_reorientation_inputs(lenses: &[ThreadEpiphanyViewLens]) -> bool {
|
||||
lenses.contains(&ThreadEpiphanyViewLens::Roles)
|
||||
|| lenses.contains(&ThreadEpiphanyViewLens::Reorient)
|
||||
|| lenses.contains(&ThreadEpiphanyViewLens::Crrc)
|
||||
|| lenses.contains(&ThreadEpiphanyViewLens::Coordinator)
|
||||
}
|
||||
|
||||
pub fn epiphany_view_needs_pressure(lenses: &[ThreadEpiphanyViewLens]) -> bool {
|
||||
lenses.contains(&ThreadEpiphanyViewLens::Pressure)
|
||||
|| epiphany_view_needs_reorientation_inputs(lenses)
|
||||
}
|
||||
|
||||
pub fn epiphany_view_needs_runtime_store(lenses: &[ThreadEpiphanyViewLens]) -> bool {
|
||||
lenses.contains(&ThreadEpiphanyViewLens::Roles)
|
||||
|| lenses.contains(&ThreadEpiphanyViewLens::Crrc)
|
||||
|| lenses.contains(&ThreadEpiphanyViewLens::Coordinator)
|
||||
}
|
||||
|
||||
pub struct EpiphanyViewResponseInput<'a> {
|
||||
pub thread_id: String,
|
||||
pub lenses: Vec<ThreadEpiphanyViewLens>,
|
||||
pub loaded: bool,
|
||||
pub state: Option<&'a EpiphanyThreadState>,
|
||||
pub retrieval_override: Option<&'a EpiphanyRetrievalState>,
|
||||
pub watcher_snapshot: Option<EpiphanyFreshnessWatcherSnapshot<'a>>,
|
||||
pub token_usage_info: Option<&'a CoreTokenUsageInfo>,
|
||||
pub runtime_store_path: Option<&'a Path>,
|
||||
}
|
||||
|
||||
pub async fn map_epiphany_view_response(
|
||||
input: EpiphanyViewResponseInput<'_>,
|
||||
) -> ThreadEpiphanyViewResponse {
|
||||
let EpiphanyViewResponseInput {
|
||||
thread_id,
|
||||
lenses,
|
||||
loaded,
|
||||
state,
|
||||
retrieval_override,
|
||||
watcher_snapshot,
|
||||
token_usage_info,
|
||||
runtime_store_path,
|
||||
} = input;
|
||||
|
||||
let needs_jobs = epiphany_view_needs_jobs(&lenses);
|
||||
let needs_reorientation_inputs = epiphany_view_needs_reorientation_inputs(&lenses);
|
||||
let needs_pressure = epiphany_view_needs_pressure(&lenses);
|
||||
let pressure = needs_pressure.then(|| map_epiphany_pressure(token_usage_info));
|
||||
let freshness = needs_reorientation_inputs
|
||||
.then(|| map_epiphany_freshness(state, retrieval_override, watcher_snapshot));
|
||||
let (state_revision, reorient_state_status, reorient_decision) =
|
||||
if let (Some((state_revision, retrieval, graph, watcher)), Some(pressure)) =
|
||||
(freshness.as_ref(), pressure.as_ref())
|
||||
{
|
||||
let (state_status, decision) =
|
||||
map_epiphany_reorient(state, pressure, retrieval, graph, watcher);
|
||||
(*state_revision, state_status, Some(decision))
|
||||
} else {
|
||||
(None, ThreadEpiphanyReorientStateStatus::Missing, None)
|
||||
};
|
||||
let jobs = if needs_jobs {
|
||||
map_epiphany_jobs(state, retrieval_override)
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
let reorient_job = jobs
|
||||
.iter()
|
||||
.find(|job| job.id == EPIPHANY_REORIENT_LAUNCH_BINDING_ID)
|
||||
.cloned();
|
||||
let (reorient_result_status, reorient_finding, reorient_result_note) = if runtime_store_path
|
||||
.is_some()
|
||||
|| lenses.contains(&ThreadEpiphanyViewLens::Crrc)
|
||||
|| lenses.contains(&ThreadEpiphanyViewLens::Coordinator)
|
||||
{
|
||||
load_epiphany_reorient_result_snapshot(
|
||||
state,
|
||||
runtime_store_path,
|
||||
EPIPHANY_REORIENT_LAUNCH_BINDING_ID,
|
||||
)
|
||||
.await
|
||||
} else {
|
||||
(
|
||||
ThreadEpiphanyReorientResultStatus::MissingState,
|
||||
None,
|
||||
"Reorient result was not requested.".to_string(),
|
||||
)
|
||||
};
|
||||
let checkpoint_present = state
|
||||
.and_then(|state| state.investigation_checkpoint.as_ref())
|
||||
.is_some();
|
||||
let reorient_finding_accepted = reorient_finding.as_ref().is_some_and(|finding| {
|
||||
state.is_some_and(|state| epiphany_reorient_finding_already_accepted(state, finding))
|
||||
});
|
||||
let recommendation =
|
||||
if let (Some(pressure), Some(decision)) = (pressure.as_ref(), reorient_decision.as_ref()) {
|
||||
Some(map_epiphany_crrc_recommendation(
|
||||
loaded,
|
||||
reorient_state_status,
|
||||
pressure,
|
||||
decision,
|
||||
reorient_result_status,
|
||||
checkpoint_present,
|
||||
reorient_finding.is_some(),
|
||||
reorient_finding_accepted,
|
||||
))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let roles = if let (Some(pressure), Some(decision), Some(recommendation)) = (
|
||||
pressure.as_ref(),
|
||||
reorient_decision.as_ref(),
|
||||
recommendation.as_ref(),
|
||||
) {
|
||||
Some(map_epiphany_roles(
|
||||
state,
|
||||
&jobs,
|
||||
decision,
|
||||
pressure,
|
||||
recommendation,
|
||||
reorient_result_status,
|
||||
reorient_job.as_ref(),
|
||||
))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let coordinator_response = if lenses.contains(&ThreadEpiphanyViewLens::Coordinator) {
|
||||
if let (Some(pressure), Some(recommendation), Some(roles)) =
|
||||
(pressure.as_ref(), recommendation.as_ref(), roles.clone())
|
||||
{
|
||||
let status = derive_epiphany_coordinator_status(
|
||||
state,
|
||||
runtime_store_path,
|
||||
reorient_state_status,
|
||||
pressure,
|
||||
recommendation,
|
||||
roles,
|
||||
reorient_decision.as_ref(),
|
||||
reorient_result_status,
|
||||
reorient_finding.as_ref(),
|
||||
checkpoint_present,
|
||||
)
|
||||
.await;
|
||||
Some(map_epiphany_coordinator_view(
|
||||
thread_id.clone(),
|
||||
loaded,
|
||||
reorient_state_status,
|
||||
state_revision,
|
||||
status,
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
ThreadEpiphanyViewResponse {
|
||||
thread_id: thread_id.clone(),
|
||||
scene: lenses
|
||||
.contains(&ThreadEpiphanyViewLens::Scene)
|
||||
.then(|| map_epiphany_scene(state, loaded, EPIPHANY_REORIENT_LAUNCH_BINDING_ID)),
|
||||
jobs: if lenses.contains(&ThreadEpiphanyViewLens::Jobs) {
|
||||
jobs.clone()
|
||||
} else {
|
||||
Vec::new()
|
||||
},
|
||||
roles: lenses.contains(&ThreadEpiphanyViewLens::Roles).then(|| {
|
||||
let roles = roles.clone().unwrap_or_default();
|
||||
ThreadEpiphanyViewRoles {
|
||||
thread_id: thread_id.clone(),
|
||||
source: if loaded {
|
||||
ThreadEpiphanyRolesSource::Live
|
||||
} else {
|
||||
ThreadEpiphanyRolesSource::Stored
|
||||
},
|
||||
state_status: reorient_state_status,
|
||||
state_revision,
|
||||
note: render_epiphany_roles_note(
|
||||
&roles,
|
||||
reorient_state_status,
|
||||
recommendation
|
||||
.as_ref()
|
||||
.map(|recommendation| recommendation.action)
|
||||
.unwrap_or(ThreadEpiphanyCrrcAction::Continue),
|
||||
),
|
||||
roles,
|
||||
}
|
||||
}),
|
||||
planning: lenses.contains(&ThreadEpiphanyViewLens::Planning).then(|| {
|
||||
let (state_status, state_revision, planning, summary) = map_epiphany_planning(state);
|
||||
ThreadEpiphanyViewPlanning {
|
||||
thread_id: thread_id.clone(),
|
||||
source: if loaded {
|
||||
ThreadEpiphanyContextSource::Live
|
||||
} else {
|
||||
ThreadEpiphanyContextSource::Stored
|
||||
},
|
||||
state_status,
|
||||
state_revision,
|
||||
planning,
|
||||
summary,
|
||||
}
|
||||
}),
|
||||
pressure: lenses
|
||||
.contains(&ThreadEpiphanyViewLens::Pressure)
|
||||
.then(|| pressure.clone())
|
||||
.flatten(),
|
||||
reorient: lenses
|
||||
.contains(&ThreadEpiphanyViewLens::Reorient)
|
||||
.then(|| {
|
||||
reorient_decision
|
||||
.clone()
|
||||
.map(|decision| ThreadEpiphanyViewReorient {
|
||||
thread_id: thread_id.clone(),
|
||||
source: if loaded {
|
||||
ThreadEpiphanyReorientSource::Live
|
||||
} else {
|
||||
ThreadEpiphanyReorientSource::Stored
|
||||
},
|
||||
state_status: reorient_state_status,
|
||||
state_revision,
|
||||
decision,
|
||||
})
|
||||
})
|
||||
.flatten(),
|
||||
crrc: lenses
|
||||
.contains(&ThreadEpiphanyViewLens::Crrc)
|
||||
.then(|| {
|
||||
let pressure = pressure.clone()?;
|
||||
let decision = reorient_decision.clone()?;
|
||||
let recommendation = recommendation.clone()?;
|
||||
let available_actions = derive_scene(EpiphanySceneInput {
|
||||
state,
|
||||
loaded,
|
||||
reorient_binding_id: EPIPHANY_REORIENT_LAUNCH_BINDING_ID,
|
||||
})
|
||||
.available_actions
|
||||
.into_iter()
|
||||
.map(map_core_epiphany_scene_action)
|
||||
.collect();
|
||||
let note = format!(
|
||||
"{} Result status: {:?}. {}",
|
||||
recommendation.reason, reorient_result_status, reorient_result_note
|
||||
);
|
||||
Some(ThreadEpiphanyViewCrrc {
|
||||
thread_id: thread_id.clone(),
|
||||
source: if loaded {
|
||||
ThreadEpiphanyReorientSource::Live
|
||||
} else {
|
||||
ThreadEpiphanyReorientSource::Stored
|
||||
},
|
||||
state_status: reorient_state_status,
|
||||
state_revision,
|
||||
pressure,
|
||||
decision,
|
||||
recommendation,
|
||||
reorient_binding_id: EPIPHANY_REORIENT_LAUNCH_BINDING_ID.to_string(),
|
||||
reorient_result_status,
|
||||
reorient_job: reorient_job.clone(),
|
||||
reorient_finding: reorient_finding.clone(),
|
||||
available_actions,
|
||||
note,
|
||||
})
|
||||
})
|
||||
.flatten(),
|
||||
coordinator: coordinator_response,
|
||||
lenses,
|
||||
}
|
||||
}
|
||||
|
|
@ -698,6 +698,17 @@ longer owns that coordinator policy. The root `codex_message_processor.rs`
|
|||
imports for the evacuated Epiphany protocol/policy registry were cut as proof
|
||||
that those names no longer belong to the host file.
|
||||
|
||||
The full `thread/epiphany/view` projection has now followed. App-server still
|
||||
parses the thread id, loads live/stored thread state, registers watcher input,
|
||||
fetches token usage, and obtains the runtime-spine store path. The actual lens
|
||||
selection, freshness/pressure/reorient/CRRC/roles/coordinator composition,
|
||||
scene/planning/job projection, and `ThreadEpiphanyViewResponse` construction
|
||||
now live in `epiphany-codex-bridge::view`. `epiphany_read_routes.rs` is about
|
||||
722 lines after this cut. The remaining read-route bodies are mostly individual
|
||||
legacy read verbs (`roleResult`, `freshness`, `context`, `graphQuery`,
|
||||
`reorientResult`, `retrieve`, `distill`, `propose`) plus host loading and
|
||||
response/error shaping.
|
||||
|
||||
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.
|
||||
|
|
|
|||
Binary file not shown.
File diff suppressed because one or more lines are too long
|
|
@ -8,26 +8,20 @@ use codex_core::distill_observation;
|
|||
use codex_protocol::ThreadId;
|
||||
use epiphany_codex_bridge::context::map_epiphany_context;
|
||||
use epiphany_codex_bridge::context::map_epiphany_graph_query;
|
||||
use epiphany_codex_bridge::context::map_epiphany_planning;
|
||||
use epiphany_codex_bridge::coordinator::derive_epiphany_coordinator_status;
|
||||
use epiphany_codex_bridge::coordinator::epiphany_reorient_finding_already_accepted;
|
||||
use epiphany_codex_bridge::coordinator::map_epiphany_coordinator_view;
|
||||
use epiphany_codex_bridge::coordinator::map_epiphany_crrc_recommendation;
|
||||
use epiphany_codex_bridge::coordinator::map_epiphany_roles;
|
||||
use epiphany_codex_bridge::coordinator::render_epiphany_roles_note;
|
||||
use epiphany_codex_bridge::jobs::map_epiphany_jobs;
|
||||
use epiphany_codex_bridge::launch::EPIPHANY_REORIENT_LAUNCH_BINDING_ID;
|
||||
use epiphany_codex_bridge::launch::epiphany_role_binding_id;
|
||||
use epiphany_codex_bridge::pressure::map_epiphany_pressure;
|
||||
use epiphany_codex_bridge::reorient::map_epiphany_freshness;
|
||||
use epiphany_codex_bridge::reorient::map_epiphany_reorient;
|
||||
use epiphany_codex_bridge::retrieve::map_epiphany_retrieve_response;
|
||||
use epiphany_codex_bridge::runtime_results::load_epiphany_reorient_result_snapshot;
|
||||
use epiphany_codex_bridge::runtime_results::load_epiphany_role_result_snapshot;
|
||||
use epiphany_codex_bridge::scene::map_core_epiphany_scene_action;
|
||||
use epiphany_codex_bridge::scene::map_epiphany_scene;
|
||||
use epiphany_core::EpiphanySceneInput;
|
||||
use epiphany_core::derive_scene;
|
||||
use epiphany_codex_bridge::view::EpiphanyViewResponseInput;
|
||||
use epiphany_codex_bridge::view::default_epiphany_view_lenses;
|
||||
use epiphany_codex_bridge::view::epiphany_view_needs_jobs;
|
||||
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_view_response;
|
||||
|
||||
use super::CodexMessageProcessor;
|
||||
use super::ConnectionRequestId;
|
||||
|
|
@ -43,16 +37,7 @@ impl CodexMessageProcessor {
|
|||
) {
|
||||
let ThreadEpiphanyViewParams { thread_id, lenses } = params;
|
||||
let lenses = if lenses.is_empty() {
|
||||
vec![
|
||||
ThreadEpiphanyViewLens::Scene,
|
||||
ThreadEpiphanyViewLens::Jobs,
|
||||
ThreadEpiphanyViewLens::Roles,
|
||||
ThreadEpiphanyViewLens::Planning,
|
||||
ThreadEpiphanyViewLens::Pressure,
|
||||
ThreadEpiphanyViewLens::Reorient,
|
||||
ThreadEpiphanyViewLens::Crrc,
|
||||
ThreadEpiphanyViewLens::Coordinator,
|
||||
]
|
||||
default_epiphany_view_lenses()
|
||||
} else {
|
||||
lenses
|
||||
};
|
||||
|
|
@ -80,16 +65,9 @@ impl CodexMessageProcessor {
|
|||
}
|
||||
};
|
||||
|
||||
let needs_jobs = lenses.contains(&ThreadEpiphanyViewLens::Jobs)
|
||||
|| lenses.contains(&ThreadEpiphanyViewLens::Roles)
|
||||
|| lenses.contains(&ThreadEpiphanyViewLens::Crrc)
|
||||
|| lenses.contains(&ThreadEpiphanyViewLens::Coordinator);
|
||||
let needs_reorientation_inputs = lenses.contains(&ThreadEpiphanyViewLens::Roles)
|
||||
|| lenses.contains(&ThreadEpiphanyViewLens::Reorient)
|
||||
|| lenses.contains(&ThreadEpiphanyViewLens::Crrc)
|
||||
|| lenses.contains(&ThreadEpiphanyViewLens::Coordinator);
|
||||
let needs_pressure =
|
||||
lenses.contains(&ThreadEpiphanyViewLens::Pressure) || needs_reorientation_inputs;
|
||||
let needs_jobs = epiphany_view_needs_jobs(&lenses);
|
||||
let needs_reorientation_inputs = epiphany_view_needs_reorientation_inputs(&lenses);
|
||||
let needs_pressure = epiphany_view_needs_pressure(&lenses);
|
||||
let retrieval_override = if (needs_jobs || needs_reorientation_inputs)
|
||||
&& thread
|
||||
.epiphany_state
|
||||
|
|
@ -134,40 +112,7 @@ impl CodexMessageProcessor {
|
|||
} else {
|
||||
None
|
||||
};
|
||||
let pressure = needs_pressure.then(|| map_epiphany_pressure(token_usage_info.as_ref()));
|
||||
let freshness = needs_reorientation_inputs.then(|| {
|
||||
map_epiphany_freshness(
|
||||
thread.epiphany_state.as_ref(),
|
||||
retrieval_override.as_ref(),
|
||||
watcher_snapshot
|
||||
.as_ref()
|
||||
.map(epiphany_freshness_watcher_snapshot),
|
||||
)
|
||||
});
|
||||
let (state_revision, reorient_state_status, reorient_decision) =
|
||||
if let (Some((state_revision, retrieval, graph, watcher)), Some(pressure)) =
|
||||
(freshness.as_ref(), pressure.as_ref())
|
||||
{
|
||||
let (state_status, decision) = map_epiphany_reorient(
|
||||
thread.epiphany_state.as_ref(),
|
||||
pressure,
|
||||
retrieval,
|
||||
graph,
|
||||
watcher,
|
||||
);
|
||||
(*state_revision, state_status, Some(decision))
|
||||
} else {
|
||||
(None, ThreadEpiphanyReorientStateStatus::Missing, None)
|
||||
};
|
||||
let jobs = if needs_jobs {
|
||||
map_epiphany_jobs(thread.epiphany_state.as_ref(), retrieval_override.as_ref())
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
let runtime_store_path = if lenses.contains(&ThreadEpiphanyViewLens::Roles)
|
||||
|| lenses.contains(&ThreadEpiphanyViewLens::Crrc)
|
||||
|| lenses.contains(&ThreadEpiphanyViewLens::Coordinator)
|
||||
{
|
||||
let runtime_store_path = if epiphany_view_needs_runtime_store(&lenses) {
|
||||
if let Some(loaded_thread) = loaded_thread.as_ref() {
|
||||
Some(loaded_thread.epiphany_runtime_spine_store_path().await)
|
||||
} else {
|
||||
|
|
@ -176,220 +121,19 @@ impl CodexMessageProcessor {
|
|||
} else {
|
||||
None
|
||||
};
|
||||
let reorient_job = jobs
|
||||
.iter()
|
||||
.find(|job| job.id == EPIPHANY_REORIENT_LAUNCH_BINDING_ID)
|
||||
.cloned();
|
||||
let (reorient_result_status, reorient_finding, reorient_result_note) = if runtime_store_path
|
||||
.is_some()
|
||||
|| lenses.contains(&ThreadEpiphanyViewLens::Crrc)
|
||||
|| lenses.contains(&ThreadEpiphanyViewLens::Coordinator)
|
||||
{
|
||||
load_epiphany_reorient_result_snapshot(
|
||||
thread.epiphany_state.as_ref(),
|
||||
runtime_store_path.as_deref(),
|
||||
EPIPHANY_REORIENT_LAUNCH_BINDING_ID,
|
||||
)
|
||||
.await
|
||||
} else {
|
||||
(
|
||||
ThreadEpiphanyReorientResultStatus::MissingState,
|
||||
None,
|
||||
"Reorient result was not requested.".to_string(),
|
||||
)
|
||||
};
|
||||
let checkpoint_present = thread
|
||||
.epiphany_state
|
||||
.as_ref()
|
||||
.and_then(|state| state.investigation_checkpoint.as_ref())
|
||||
.is_some();
|
||||
let reorient_finding_accepted = reorient_finding.as_ref().is_some_and(|finding| {
|
||||
thread
|
||||
.epiphany_state
|
||||
.as_ref()
|
||||
.is_some_and(|state| epiphany_reorient_finding_already_accepted(state, finding))
|
||||
});
|
||||
let recommendation = if let (Some(pressure), Some(decision)) =
|
||||
(pressure.as_ref(), reorient_decision.as_ref())
|
||||
{
|
||||
Some(map_epiphany_crrc_recommendation(
|
||||
loaded,
|
||||
reorient_state_status,
|
||||
pressure,
|
||||
decision,
|
||||
reorient_result_status,
|
||||
checkpoint_present,
|
||||
reorient_finding.is_some(),
|
||||
reorient_finding_accepted,
|
||||
))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let roles = if let (Some(pressure), Some(decision), Some(recommendation)) = (
|
||||
pressure.as_ref(),
|
||||
reorient_decision.as_ref(),
|
||||
recommendation.as_ref(),
|
||||
) {
|
||||
Some(map_epiphany_roles(
|
||||
thread.epiphany_state.as_ref(),
|
||||
&jobs,
|
||||
decision,
|
||||
pressure,
|
||||
recommendation,
|
||||
reorient_result_status,
|
||||
reorient_job.as_ref(),
|
||||
))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let coordinator_response = if lenses.contains(&ThreadEpiphanyViewLens::Coordinator) {
|
||||
if let (Some(pressure), Some(recommendation), Some(roles)) =
|
||||
(pressure.as_ref(), recommendation.as_ref(), roles.clone())
|
||||
{
|
||||
let status = derive_epiphany_coordinator_status(
|
||||
thread.epiphany_state.as_ref(),
|
||||
runtime_store_path.as_deref(),
|
||||
reorient_state_status,
|
||||
pressure,
|
||||
recommendation,
|
||||
roles,
|
||||
reorient_decision.as_ref(),
|
||||
reorient_result_status,
|
||||
reorient_finding.as_ref(),
|
||||
checkpoint_present,
|
||||
)
|
||||
.await;
|
||||
Some(map_epiphany_coordinator_view(
|
||||
thread_id.clone(),
|
||||
loaded,
|
||||
reorient_state_status,
|
||||
state_revision,
|
||||
status,
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let response = ThreadEpiphanyViewResponse {
|
||||
let response = map_epiphany_view_response(EpiphanyViewResponseInput {
|
||||
thread_id: thread_id.clone(),
|
||||
scene: lenses.contains(&ThreadEpiphanyViewLens::Scene).then(|| {
|
||||
map_epiphany_scene(
|
||||
thread.epiphany_state.as_ref(),
|
||||
loaded,
|
||||
EPIPHANY_REORIENT_LAUNCH_BINDING_ID,
|
||||
)
|
||||
}),
|
||||
jobs: if lenses.contains(&ThreadEpiphanyViewLens::Jobs) {
|
||||
jobs.clone()
|
||||
} else {
|
||||
Vec::new()
|
||||
},
|
||||
roles: lenses.contains(&ThreadEpiphanyViewLens::Roles).then(|| {
|
||||
let roles = roles.clone().unwrap_or_default();
|
||||
ThreadEpiphanyViewRoles {
|
||||
thread_id: thread_id.clone(),
|
||||
source: if loaded {
|
||||
ThreadEpiphanyRolesSource::Live
|
||||
} else {
|
||||
ThreadEpiphanyRolesSource::Stored
|
||||
},
|
||||
state_status: reorient_state_status,
|
||||
state_revision,
|
||||
note: render_epiphany_roles_note(
|
||||
&roles,
|
||||
reorient_state_status,
|
||||
recommendation
|
||||
.as_ref()
|
||||
.map(|recommendation| recommendation.action)
|
||||
.unwrap_or(ThreadEpiphanyCrrcAction::Continue),
|
||||
),
|
||||
roles,
|
||||
}
|
||||
}),
|
||||
planning: lenses.contains(&ThreadEpiphanyViewLens::Planning).then(|| {
|
||||
let (state_status, state_revision, planning, summary) =
|
||||
map_epiphany_planning(thread.epiphany_state.as_ref());
|
||||
ThreadEpiphanyViewPlanning {
|
||||
thread_id: thread_id.clone(),
|
||||
source: if loaded {
|
||||
ThreadEpiphanyContextSource::Live
|
||||
} else {
|
||||
ThreadEpiphanyContextSource::Stored
|
||||
},
|
||||
state_status,
|
||||
state_revision,
|
||||
planning,
|
||||
summary,
|
||||
}
|
||||
}),
|
||||
pressure: lenses
|
||||
.contains(&ThreadEpiphanyViewLens::Pressure)
|
||||
.then(|| pressure.clone())
|
||||
.flatten(),
|
||||
reorient: lenses
|
||||
.contains(&ThreadEpiphanyViewLens::Reorient)
|
||||
.then(|| {
|
||||
reorient_decision
|
||||
.clone()
|
||||
.map(|decision| ThreadEpiphanyViewReorient {
|
||||
thread_id: thread_id.clone(),
|
||||
source: if loaded {
|
||||
ThreadEpiphanyReorientSource::Live
|
||||
} else {
|
||||
ThreadEpiphanyReorientSource::Stored
|
||||
},
|
||||
state_status: reorient_state_status,
|
||||
state_revision,
|
||||
decision,
|
||||
})
|
||||
})
|
||||
.flatten(),
|
||||
crrc: lenses
|
||||
.contains(&ThreadEpiphanyViewLens::Crrc)
|
||||
.then(|| {
|
||||
let pressure = pressure.clone()?;
|
||||
let decision = reorient_decision.clone()?;
|
||||
let recommendation = recommendation.clone()?;
|
||||
let available_actions = derive_scene(EpiphanySceneInput {
|
||||
state: thread.epiphany_state.as_ref(),
|
||||
loaded,
|
||||
reorient_binding_id: EPIPHANY_REORIENT_LAUNCH_BINDING_ID,
|
||||
})
|
||||
.available_actions
|
||||
.into_iter()
|
||||
.map(map_core_epiphany_scene_action)
|
||||
.collect();
|
||||
let note = format!(
|
||||
"{} Result status: {:?}. {}",
|
||||
recommendation.reason, reorient_result_status, reorient_result_note
|
||||
);
|
||||
Some(ThreadEpiphanyViewCrrc {
|
||||
thread_id: thread_id.clone(),
|
||||
source: if loaded {
|
||||
ThreadEpiphanyReorientSource::Live
|
||||
} else {
|
||||
ThreadEpiphanyReorientSource::Stored
|
||||
},
|
||||
state_status: reorient_state_status,
|
||||
state_revision,
|
||||
pressure,
|
||||
decision,
|
||||
recommendation,
|
||||
reorient_binding_id: EPIPHANY_REORIENT_LAUNCH_BINDING_ID.to_string(),
|
||||
reorient_result_status,
|
||||
reorient_job: reorient_job.clone(),
|
||||
reorient_finding: reorient_finding.clone(),
|
||||
available_actions,
|
||||
note,
|
||||
})
|
||||
})
|
||||
.flatten(),
|
||||
coordinator: coordinator_response,
|
||||
lenses,
|
||||
};
|
||||
loaded,
|
||||
state: thread.epiphany_state.as_ref(),
|
||||
retrieval_override: retrieval_override.as_ref(),
|
||||
watcher_snapshot: watcher_snapshot
|
||||
.as_ref()
|
||||
.map(epiphany_freshness_watcher_snapshot),
|
||||
token_usage_info: token_usage_info.as_ref(),
|
||||
runtime_store_path: runtime_store_path.as_deref(),
|
||||
})
|
||||
.await;
|
||||
self.outgoing.send_response(request_id, response).await;
|
||||
}
|
||||
|
||||
|
|
|
|||
Reference in a new issue