Delete unconsumed Epiphany app-server routes
This commit is contained in:
parent
09761d41be
commit
5fd927481c
11 changed files with 116 additions and 1893 deletions
|
|
@ -22,20 +22,20 @@ These are retained only while the named compatibility binaries remain. They do
|
|||
not justify route-local policy, implicit triggers, Codex state ownership, or new
|
||||
clients.
|
||||
|
||||
## Unconsumed advertised routes
|
||||
## Removed unconsumed routes
|
||||
|
||||
| Route | Finding | Deletion owner |
|
||||
| Route | Finding | Result |
|
||||
|---|---|---|
|
||||
| `roleLaunch` | no executable caller | remove request registration, dispatch, handler, tests/docs |
|
||||
| `roleAccept` | no executable caller | remove request registration, dispatch, handler, tests/docs |
|
||||
| `reorientLaunch` | no executable caller | remove request registration, dispatch, handler, tests/docs |
|
||||
| `reorientAccept` | no executable caller | remove request registration, dispatch, handler, tests/docs |
|
||||
| `index` | no executable caller | remove request registration, dispatch, handler, tests/docs |
|
||||
| `distill` | no executable caller | remove request registration, dispatch, handler, tests/docs |
|
||||
| `propose` | no executable caller | remove request registration, dispatch, handler, tests/docs |
|
||||
| `promote` | no executable caller | remove request registration, dispatch, handler, tests/docs |
|
||||
| `jobLaunch` | no executable caller | remove request registration, dispatch, handler, tests/docs |
|
||||
| `retrieve` | no executable caller | remove request registration, dispatch, handler, tests/docs |
|
||||
| `roleLaunch` | no executable caller | request surface deleted |
|
||||
| `roleAccept` | no executable caller | request surface deleted |
|
||||
| `reorientLaunch` | no executable caller | request surface deleted |
|
||||
| `reorientAccept` | no executable caller | request surface deleted |
|
||||
| `index` | no executable caller | request surface deleted |
|
||||
| `distill` | no executable caller | request surface deleted |
|
||||
| `propose` | no executable caller | request surface deleted |
|
||||
| `promote` | no executable caller | request surface deleted |
|
||||
| `jobLaunch` | no executable caller | request surface deleted |
|
||||
| `retrieve` | no executable caller | request surface deleted |
|
||||
|
||||
Protocol DTOs may remain temporarily only when retained response projections or
|
||||
bridge conversion tests still compile against them. A DTO is not authority and
|
||||
|
|
|
|||
|
|
@ -1952,8 +1952,12 @@ compatibility API with no named living client. The client matrix now lives at
|
|||
`notes/epiphany-app-server-client-matrix.md`: eight routes have named phase-6
|
||||
smoke or `epiphany-mvp-status --codex` consumers; ten advertised routes have no
|
||||
executable caller beyond definitions/tests/docs and are marked for deletion.
|
||||
Cut those ten request registrations, dispatch arms, handlers, tests, and README
|
||||
claims next; do not let generated bindings count as clients.
|
||||
Those ten request registrations, dispatch arms, handlers, protocol tests, and
|
||||
README claims are now deleted. A source guard fixes registration to the eight
|
||||
named-client routes and explicitly rejects the removed names. Protocol compile,
|
||||
app-server compile, and four compatibility-architecture tests pass. Next audit
|
||||
zero-caller protocol DTOs and bridge conversion helpers left by this cut, then
|
||||
migrate or retire the phase-6 smoke and `epiphany-mvp-status --codex` clients.
|
||||
|
||||
## Immediate Re-entry Instruction
|
||||
|
||||
|
|
|
|||
|
|
@ -365,9 +365,8 @@ routes, notifications, and read projection only. A source guard rejects renewed
|
|||
automation authority in the event loop, processor module list, or thread state.
|
||||
|
||||
The remaining app-server surface is now mechanically classified and guarded.
|
||||
Mutation routes must call one of nine explicit bridge delegates for role launch,
|
||||
role acceptance, reorientation launch/acceptance, indexing, promotion, update,
|
||||
job launch, or interruption; forbidden local receipt/binding/runtime writes,
|
||||
Mutation routes may call only the explicit update or interruption bridge
|
||||
delegate; forbidden local receipt/binding/runtime writes,
|
||||
prompt steering, compaction submission, and automation selection fail the
|
||||
source test. Read routes reject every mutation, launch, interruption, indexing,
|
||||
prompt-steering, compaction, or scheduler symbol. Migration, read projection,
|
||||
|
|
@ -382,5 +381,6 @@ in-repo consumers, all explicit phase-6 smokes or
|
|||
unconsumed: role/reorientation launch and accept, index, distill, propose,
|
||||
promote, job launch, and retrieve. Definitions, generated bindings, README
|
||||
claims, and serialization tests do not count as clients. Those ten request
|
||||
registrations, dispatch arms, handlers, tests, and docs are the next deletion
|
||||
line.
|
||||
registrations, dispatch arms, handlers, tests, and README claims are deleted.
|
||||
The registration guard now fixes the compatibility surface at the eight named
|
||||
client routes and rejects restoration of the unconsumed methods.
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -659,4 +659,4 @@ current_status:
|
|||
- Service lifecycle receipt history is now event-only: `load_epiphany_cultmesh_daemon_service_lifecycle_receipts` filters out the `epiphany-local/daemon-service-lifecycle-receipt/latest` mirror, while `load_latest_epiphany_cultmesh_daemon_service_lifecycle_receipt` remains the latest-state reader. Focused unit test `service_lifecycle_receipt_history_excludes_latest_mirror` proves two per-receipt writes return exactly two history records and latest points at the second. Fresh smoke `verse-query-lifecycle-history-filter-smoke` and live overview/triage `local-20260618-145632-497-efac6f0d` / `local-20260618-145632-321-867947af` prove compact swarm sights still expose service lifecycle attention plus the present elevated runbook artifact without daemon pokes or service mutation. Do not use a broad unscoped cargo test filter for this check on Windows; it fans out across bin harnesses and hit a broken pipe during this pass.
|
||||
- `epiphany-verse-query seed-compact` is the compact seed receipt for wrappers and read-only rites. The older `seed` command still prints the full local Verse context for explicit debugging, but compact wrapper modes now avoid full `seed`/final `query` readback after the combined overview proof exposed Windows debug stack overflow from full-context serialization pressure.
|
||||
last_completed_repo_swarm_cut: Huginn live-fire has crossed the first real repo Body from Persona pressure to branch-local Hands commit and Soul/Mind closure, and now has repeatable closure and redacted-public-proof mouths. On `E:\Projects\Huginn` branch `epiphany/huginn/first-awakening`, `tools/epiphany_local_run.ps1 -Mode repo-persona-intake` accepted item `huginn-inspection-readback-livefire`, Weksa/Persona intake remained sealed, and `epiphany-work derive-plan` lowered it to model-authored safe family `repo.status_section` for `README.md` only. `tools/epiphany_local_run.ps1 -Mode repo-swarm-run` advanced four bounded pulses: run-from-plan, adopt-from-plan, execute-from-plan, and close-from-execute. Huginn commit `e9b07c38fceabbdc89ea35c2156326c80c0393a2` updated only `README.md`; closure receipt `.epiphany\work\work-close-huginn-inspection-readback-livefire.json` records status `closed`, Soul verdict `passed`, Mind state commit `repo-work-close-huginn-inspection-readback-livefire-mind-commit`, safe family `repo.status_section`, path scope `README.md`, Bifrost publication gate, and `privateStateExposed=false`. Verification also ran Huginn's TypeScript compiler through concrete Node path: `C:\Program Files\nodejs\node.exe E:\Projects\Huginn\node_modules\typescript\bin\tsc -p tsconfig.json` passed. Native verifier `epiphany-repo-livefire-closure-smoke --workspace E:\Projects\Huginn --item huginn-inspection-readback-livefire --expected-path README.md --expected-family repo.status_section --expected-gate awaiting-publication --expected-blocker bifrost-publication-missing` checks closure receipt, Mind map entry, overview gate/blocker, authority seals, and `git show` changed paths, returning `status=ok`, `soulVerdict=passed`, `publicationGate=Bifrost`, and `privateStateExposed=false`. Wrapper `tools/epiphany_local_run.ps1 -Mode repo-livefire-closure -Workspace E:\Projects\Huginn -RepoWorkItem huginn-inspection-readback-livefire -SkipBuild` exposes the same read-only proof through the local operator mouth; artifact `local-20260621-190156-803-2f85ba15` printed commit, path, gate, blocker, Soul verdict, Mind commit receipt, and `privateStateExposed=False` without push, merge, publication, or target mutation. New wrapper `tools/epiphany_local_run.ps1 -Mode repo-work-public-proof -Workspace E:\Projects\Huginn -RepoWorkItem huginn-inspection-readback-livefire -SkipBuild` exports the redacted proof bundle and repo-local Verse index without publishing; artifact `local-20260621-190645-345-4555b1dc` wrote `.epiphany\public\proof-bundles\repo-work-public-proof-huginn-inspection-readback-livefire.json`, proof id `repo-work-public-proof-huginn-inspection-readback-livefire`, SHA-256 `857545b199f3e37eb06c2e757e5cfc201958e46667bb1f8403fd333a22620f63`, nine artifact rows, zero publication rows, and `privateStateExposed=False`. Gjallar readback `local-20260621-190701-140-b9519ec6` sees the `REPO-WORK-PUBLIC-PROOF` row and action row from Huginn's repo-local Verse with the same SHA-256 while Bifrost publication remains missing. Launcher compact modes are now named compact operator-context modes instead of read-only modes, and `bifrost-public-proof` summaries print item, target public Verse, room, proof SHA-256, review receipts, and credit receipts so publication receipts are auditable without claiming upstream sync.
|
||||
next_action: Use `notes/epiphany-app-server-client-matrix.md` to delete the ten unconsumed advertised routes: roleLaunch, roleAccept, reorientLaunch, reorientAccept, index, distill, propose, promote, jobLaunch, and retrieve. Remove request registration, dispatch, handlers, route tests, and README claims; retain DTOs only where a living retained projection still needs them. Keep the eight named smoke/`--codex` fallback routes guarded as compatibility-only.
|
||||
next_action: Audit protocol DTOs and bridge conversion helpers left behind by the ten deleted app-server request methods; delete zero-caller shapes without disturbing the eight guarded compatibility routes. Then migrate or retire the phase-6 smoke and `epiphany-mvp-status --codex` consumers so the remaining compatibility surface can contract again.
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
8
vendor/codex/codex-rs/app-server/README.md
vendored
8
vendor/codex/codex-rs/app-server/README.md
vendored
|
|
@ -136,16 +136,12 @@ Example with notification opt-out:
|
|||
|
||||
Hydrated thread responses such as `thread/start`, `thread/resume`, `thread/fork`, `thread/read`, and `thread/unarchive` may include `thread.epiphanyState` when the thread has persisted Epiphany state. When the thread is currently loaded, app-server prefers the live in-memory value; otherwise it reconstructs the latest surviving snapshot from rollout items using the same rollback/compaction semantics as core session replay. For live threads with Epiphany state but no persisted retrieval summary yet, app-server backfills a lightweight query-time retrieval summary instead of pretending the retrieval field is absent.
|
||||
|
||||
Experimental Epiphany retrieval:
|
||||
Experimental Epiphany state access:
|
||||
|
||||
- `thread/epiphany/index` runs an explicit loaded-thread-only semantic indexing pass that writes a Qdrant-backed workspace index under Codex home metadata, using local Ollama embeddings by default. It is the write path for persistent semantic retrieval and keeps `thread/epiphany/retrieve` read-only.
|
||||
- `thread/epiphany/retrieve` runs a loaded-thread-only hybrid repo query that combines `codex_file_search` path hits with BM25 chunk retrieval and returns typed result metadata plus an Epiphany retrieval summary.
|
||||
- `thread/epiphany/distill` is a loaded-thread-only read surface that turns one explicit observation input into a deterministic observation/evidence patch plus the current `expectedRevision`. It does not mutate thread state; callers must pass the returned patch to `thread/epiphany/update` to promote it.
|
||||
- `thread/epiphany/context` is a targeted read-only state-shard surface. It selects graph nodes/edges, active frontier, checkpoint, observations, and evidence from authoritative typed state without mutating or notifying.
|
||||
- `thread/epiphany/graphQuery` is a read-only graph traversal surface over authoritative typed state. It can return explicit nodes, path/symbol matches, one-to-three-hop neighbors, and the current frontier neighborhood while preserving architecture/dataflow links; it does not mutate, index, schedule, or promote.
|
||||
- `thread/epiphany/promote` is an explicit loaded-thread-only promotion gate. It evaluates a patch plus verifier evidence, rejects with reasons without mutating state when policy fails, and applies the patch through the durable update path when accepted. Accepted responses include the landed `revision` and `changedFields`.
|
||||
- `thread/epiphany/update` is the explicit loaded-thread-only control-plane write path for Epiphany state. It can append observations/evidence and replace bounded typed state fields such as objective, subgoals, graph frontier/checkpoint, scratch, churn, and mode; it bumps the Epiphany revision, returns `revision` plus `changedFields`, and persists a rollout snapshot immediately.
|
||||
- `thread/epiphany/stateUpdated` is an experimental server notification emitted after successful `thread/epiphany/update` and accepted `thread/epiphany/promote`. It carries the updated typed `epiphanyState`, a `source` of `update` or `promote`, event-level `revision`, and typed `changedFields`; rejected promotions do not emit it.
|
||||
- `thread/epiphany/stateUpdated` is an experimental server notification emitted after successful `thread/epiphany/update`. It carries the updated typed `epiphanyState`, an `update` source, event-level `revision`, and typed `changedFields`.
|
||||
- `thread/epiphany/jobsUpdated` is a sealed legacy notification shape. Epiphany no longer emits it from Codex batch-job progress; heartbeat/runtime-spine owns live activation telemetry, while the `jobs` lens on `thread/epiphany/view` remains a read-only projection over current typed state.
|
||||
- `thread/epiphany/freshness` is a read-only reflection surface for retrieval and graph staleness. It reports revision/source identity, retrieval freshness, graph freshness, and exact dirty-path pressure, and for loaded threads now also reports watcher-backed invalidation inputs with watched root, changed paths, mapped graph-node hits, and active-frontier hits. It still does not mutate state, schedule refresh work, or perform automatic watcher-driven semantic invalidation.
|
||||
- `thread/epiphany/reorient` is a read-only CRRC policy surface. It consumes the durable investigation checkpoint plus freshness, watcher, and pressure signals to return a bounded `resume` versus `regather` verdict with explicit checkpoint-path/frontier reasons. It does not mutate state, compact, notify, schedule, or silently continue work on the caller's behalf.
|
||||
|
|
|
|||
|
|
@ -641,18 +641,10 @@ impl CodexMessageProcessor {
|
|||
self.thread_epiphany_view(to_connection_request_id(request_id), params)
|
||||
.await;
|
||||
}
|
||||
ClientRequest::ThreadEpiphanyRoleLaunch { request_id, params } => {
|
||||
self.thread_epiphany_role_launch(to_connection_request_id(request_id), params)
|
||||
.await;
|
||||
}
|
||||
ClientRequest::ThreadEpiphanyRoleResult { request_id, params } => {
|
||||
self.thread_epiphany_role_result(to_connection_request_id(request_id), params)
|
||||
.await;
|
||||
}
|
||||
ClientRequest::ThreadEpiphanyRoleAccept { request_id, params } => {
|
||||
self.thread_epiphany_role_accept(to_connection_request_id(request_id), params)
|
||||
.await;
|
||||
}
|
||||
ClientRequest::ThreadEpiphanyFreshness { request_id, params } => {
|
||||
self.thread_epiphany_freshness(to_connection_request_id(request_id), params)
|
||||
.await;
|
||||
|
|
@ -665,38 +657,10 @@ impl CodexMessageProcessor {
|
|||
self.thread_epiphany_graph_query(to_connection_request_id(request_id), params)
|
||||
.await;
|
||||
}
|
||||
ClientRequest::ThreadEpiphanyReorientLaunch { request_id, params } => {
|
||||
self.thread_epiphany_reorient_launch(to_connection_request_id(request_id), params)
|
||||
.await;
|
||||
}
|
||||
ClientRequest::ThreadEpiphanyReorientResult { request_id, params } => {
|
||||
self.thread_epiphany_reorient_result(to_connection_request_id(request_id), params)
|
||||
.await;
|
||||
}
|
||||
ClientRequest::ThreadEpiphanyReorientAccept { request_id, params } => {
|
||||
self.thread_epiphany_reorient_accept(to_connection_request_id(request_id), params)
|
||||
.await;
|
||||
}
|
||||
ClientRequest::ThreadEpiphanyIndex { request_id, params } => {
|
||||
self.thread_epiphany_index(to_connection_request_id(request_id), params)
|
||||
.await;
|
||||
}
|
||||
ClientRequest::ThreadEpiphanyDistill { request_id, params } => {
|
||||
self.thread_epiphany_distill(to_connection_request_id(request_id), params)
|
||||
.await;
|
||||
}
|
||||
ClientRequest::ThreadEpiphanyPropose { request_id, params } => {
|
||||
self.thread_epiphany_propose(to_connection_request_id(request_id), params)
|
||||
.await;
|
||||
}
|
||||
ClientRequest::ThreadEpiphanyPromote { request_id, params } => {
|
||||
self.thread_epiphany_promote(to_connection_request_id(request_id), params)
|
||||
.await;
|
||||
}
|
||||
ClientRequest::ThreadEpiphanyJobLaunch { request_id, params } => {
|
||||
self.thread_epiphany_job_launch(to_connection_request_id(request_id), params)
|
||||
.await;
|
||||
}
|
||||
ClientRequest::ThreadEpiphanyJobInterrupt { request_id, params } => {
|
||||
self.thread_epiphany_job_interrupt(to_connection_request_id(request_id), params)
|
||||
.await;
|
||||
|
|
@ -705,10 +669,6 @@ impl CodexMessageProcessor {
|
|||
self.thread_epiphany_update(to_connection_request_id(request_id), params)
|
||||
.await;
|
||||
}
|
||||
ClientRequest::ThreadEpiphanyRetrieve { request_id, params } => {
|
||||
self.thread_epiphany_retrieve(to_connection_request_id(request_id), params)
|
||||
.await;
|
||||
}
|
||||
ClientRequest::ThreadTurnsList { request_id, params } => {
|
||||
self.thread_turns_list(to_connection_request_id(request_id), params)
|
||||
.await;
|
||||
|
|
|
|||
|
|
@ -2,14 +2,7 @@
|
|||
fn mutation_routes_are_explicit_compatibility_delegates() {
|
||||
let source = include_str!("epiphany_mutation_routes.rs");
|
||||
for delegate in [
|
||||
"launch_thread_epiphany_role(",
|
||||
"apply_thread_epiphany_role_accept(",
|
||||
"launch_thread_epiphany_reorient(",
|
||||
"apply_thread_epiphany_reorient_accept(",
|
||||
"index_epiphany_retrieval_for_paths(",
|
||||
"apply_thread_epiphany_promote(",
|
||||
"apply_thread_epiphany_update(",
|
||||
"launch_thread_epiphany_job(",
|
||||
"interrupt_thread_epiphany_job(",
|
||||
] {
|
||||
assert!(source.contains(delegate), "missing compatibility delegate {delegate}");
|
||||
|
|
@ -30,6 +23,41 @@ fn mutation_routes_are_explicit_compatibility_delegates() {
|
|||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn only_named_client_routes_remain_registered() {
|
||||
let protocol = include_str!("../../../app-server-protocol/src/protocol/common.rs");
|
||||
let registrations = protocol
|
||||
.split("ThreadTurnsList =>")
|
||||
.next()
|
||||
.expect("client request registration section");
|
||||
for retained in [
|
||||
"thread/epiphany/view",
|
||||
"thread/epiphany/roleResult",
|
||||
"thread/epiphany/freshness",
|
||||
"thread/epiphany/context",
|
||||
"thread/epiphany/graphQuery",
|
||||
"thread/epiphany/reorientResult",
|
||||
"thread/epiphany/jobInterrupt",
|
||||
"thread/epiphany/update",
|
||||
] {
|
||||
assert!(registrations.contains(retained), "missing retained client route {retained}");
|
||||
}
|
||||
for removed in [
|
||||
"thread/epiphany/roleLaunch",
|
||||
"thread/epiphany/roleAccept",
|
||||
"thread/epiphany/reorientLaunch",
|
||||
"thread/epiphany/reorientAccept",
|
||||
"thread/epiphany/index",
|
||||
"thread/epiphany/distill",
|
||||
"thread/epiphany/propose",
|
||||
"thread/epiphany/promote",
|
||||
"thread/epiphany/jobLaunch",
|
||||
"thread/epiphany/retrieve",
|
||||
] {
|
||||
assert!(!registrations.contains(removed), "unconsumed route remains registered: {removed}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn read_routes_have_no_mutation_or_scheduler_authority() {
|
||||
let source = include_str!("epiphany_read_routes.rs");
|
||||
|
|
|
|||
|
|
@ -1,45 +1,20 @@
|
|||
use codex_app_server_protocol::*;
|
||||
use codex_core::CodexThread;
|
||||
use codex_protocol::ThreadId;
|
||||
use epiphany_state_model::EpiphanyRetrievalState;
|
||||
use epiphany_state_model::EpiphanyThreadState;
|
||||
use epiphany_codex_bridge::cultnet::EpiphanyStateUpdatedField;
|
||||
use epiphany_codex_bridge::error::EpiphanyBridgeError;
|
||||
use epiphany_codex_bridge::invalidation::epiphany_freshness_watcher_snapshot;
|
||||
use epiphany_codex_bridge::launch::EPIPHANY_REORIENT_LAUNCH_BINDING_ID;
|
||||
use epiphany_codex_bridge::launch::build_epiphany_job_launch_request;
|
||||
use epiphany_codex_bridge::launch::epiphany_role_binding_id;
|
||||
use epiphany_codex_bridge::mutation_service::EpiphanyThreadPromoteApplied;
|
||||
use epiphany_codex_bridge::mutation_service::apply_thread_epiphany_promote;
|
||||
use epiphany_codex_bridge::mutation_service::apply_thread_epiphany_reorient_accept;
|
||||
use epiphany_codex_bridge::mutation_service::apply_thread_epiphany_role_accept;
|
||||
use epiphany_codex_bridge::mutation_service::apply_thread_epiphany_update;
|
||||
use epiphany_codex_bridge::mutation_service::interrupt_thread_epiphany_job;
|
||||
use epiphany_codex_bridge::mutation_service::launch_thread_epiphany_job;
|
||||
use epiphany_codex_bridge::mutation_service::launch_thread_epiphany_reorient;
|
||||
use epiphany_codex_bridge::mutation_service::launch_thread_epiphany_role;
|
||||
use epiphany_codex_bridge::protocol_edge::protocol_job_from_surface;
|
||||
use epiphany_codex_bridge::protocol_edge::protocol_patch_from_core;
|
||||
use epiphany_codex_bridge::protocol_edge::protocol_reorient_decision;
|
||||
use epiphany_codex_bridge::protocol_edge::protocol_reorient_finding;
|
||||
use epiphany_codex_bridge::protocol_edge::protocol_reorient_source;
|
||||
use epiphany_codex_bridge::protocol_edge::protocol_reorient_state_status;
|
||||
use epiphany_codex_bridge::protocol_edge::protocol_role_finding;
|
||||
use epiphany_codex_bridge::protocol_edge::protocol_role_id_to_core;
|
||||
use epiphany_codex_bridge::protocol_edge::protocol_state_updated_fields;
|
||||
use epiphany_codex_bridge::protocol_edge::protocol_state_updated_notification;
|
||||
use epiphany_codex_bridge::protocol_edge::protocol_update_patch_to_core;
|
||||
use epiphany_codex_bridge::protocol_edge::protocol_worker_launch_document_to_core;
|
||||
use epiphany_codex_bridge::retrieve::epiphany_retrieval_state_for_paths;
|
||||
use epiphany_codex_bridge::retrieve::index_epiphany_retrieval_for_paths;
|
||||
use epiphany_codex_bridge::retrieve_protocol::protocol_index_response;
|
||||
use std::sync::Arc;
|
||||
|
||||
use super::CodexMessageProcessor;
|
||||
use super::ConnectionRequestId;
|
||||
use super::ThreadReadViewError;
|
||||
use super::epiphany_thread_host::EpiphanyCodexThreadHost;
|
||||
use super::epiphany_thread_host::epiphany_token_usage_snapshot;
|
||||
|
||||
impl CodexMessageProcessor {
|
||||
async fn load_epiphany_thread(
|
||||
|
|
@ -94,450 +69,6 @@ impl CodexMessageProcessor {
|
|||
.await;
|
||||
}
|
||||
|
||||
pub(super) async fn thread_epiphany_role_launch(
|
||||
&self,
|
||||
request_id: ConnectionRequestId,
|
||||
params: ThreadEpiphanyRoleLaunchParams,
|
||||
) {
|
||||
let ThreadEpiphanyRoleLaunchParams {
|
||||
thread_id,
|
||||
role_id,
|
||||
expected_revision,
|
||||
max_runtime_seconds,
|
||||
} = params;
|
||||
let core_role_id = protocol_role_id_to_core(role_id);
|
||||
|
||||
let (thread_uuid, loaded_thread) =
|
||||
match self.load_epiphany_thread(&request_id, &thread_id).await {
|
||||
Some(thread) => thread,
|
||||
None => return,
|
||||
};
|
||||
let host = EpiphanyCodexThreadHost::new(loaded_thread.as_ref());
|
||||
let applied = match launch_thread_epiphany_role(
|
||||
&host,
|
||||
&thread_id,
|
||||
core_role_id,
|
||||
expected_revision,
|
||||
max_runtime_seconds,
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(applied) => applied,
|
||||
Err(EpiphanyBridgeError::InvalidRequest(message)) => {
|
||||
self.send_invalid_request_error(request_id, message).await;
|
||||
return;
|
||||
}
|
||||
Err(err) => {
|
||||
self.send_internal_error(
|
||||
request_id,
|
||||
format!("failed to launch Epiphany role specialist for {thread_uuid}: {err}"),
|
||||
)
|
||||
.await;
|
||||
return;
|
||||
}
|
||||
};
|
||||
let changed_fields = applied.changed_fields;
|
||||
let protocol_changed_fields = protocol_state_updated_fields(changed_fields.clone());
|
||||
let epiphany_state = applied.epiphany_state;
|
||||
|
||||
self.outgoing
|
||||
.send_response(
|
||||
request_id,
|
||||
ThreadEpiphanyRoleLaunchResponse {
|
||||
thread_id: thread_uuid.to_string(),
|
||||
role_id,
|
||||
revision: applied.revision,
|
||||
changed_fields: protocol_changed_fields,
|
||||
epiphany_state: epiphany_state.clone(),
|
||||
job: protocol_job_from_surface(
|
||||
applied.job,
|
||||
Some(applied.launcher_job_id),
|
||||
Some(applied.backend_job_id),
|
||||
),
|
||||
},
|
||||
)
|
||||
.await;
|
||||
self.send_epiphany_state_updated(
|
||||
thread_uuid,
|
||||
ThreadEpiphanyStateUpdatedSource::JobLaunch,
|
||||
changed_fields,
|
||||
epiphany_state,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
pub(super) async fn thread_epiphany_role_accept(
|
||||
&self,
|
||||
request_id: ConnectionRequestId,
|
||||
params: ThreadEpiphanyRoleAcceptParams,
|
||||
) {
|
||||
let ThreadEpiphanyRoleAcceptParams {
|
||||
thread_id,
|
||||
role_id,
|
||||
expected_revision,
|
||||
binding_id,
|
||||
} = params;
|
||||
let core_role_id = protocol_role_id_to_core(role_id);
|
||||
|
||||
let default_binding_id = match epiphany_role_binding_id(core_role_id) {
|
||||
Ok(binding_id) => binding_id,
|
||||
Err(message) => {
|
||||
self.send_invalid_request_error(request_id, message).await;
|
||||
return;
|
||||
}
|
||||
};
|
||||
let binding_id = binding_id.unwrap_or_else(|| default_binding_id.into());
|
||||
|
||||
let (thread_uuid, loaded_thread) =
|
||||
match self.load_epiphany_thread(&request_id, &thread_id).await {
|
||||
Some(thread) => thread,
|
||||
None => return,
|
||||
};
|
||||
let host = EpiphanyCodexThreadHost::new(loaded_thread.as_ref());
|
||||
let applied = match apply_thread_epiphany_role_accept(
|
||||
&host,
|
||||
core_role_id,
|
||||
expected_revision,
|
||||
&binding_id,
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(applied) => applied,
|
||||
Err(EpiphanyBridgeError::InvalidRequest(message)) => {
|
||||
self.send_invalid_request_error(request_id, message).await;
|
||||
return;
|
||||
}
|
||||
Err(err) => {
|
||||
self.send_internal_error(
|
||||
request_id,
|
||||
format!("failed to apply Epiphany role finding: {err}"),
|
||||
)
|
||||
.await;
|
||||
return;
|
||||
}
|
||||
};
|
||||
let changed_fields = applied.changed_fields;
|
||||
let protocol_changed_fields = protocol_state_updated_fields(changed_fields.clone());
|
||||
let epiphany_state = applied.epiphany_state;
|
||||
|
||||
self.outgoing
|
||||
.send_response(
|
||||
request_id,
|
||||
ThreadEpiphanyRoleAcceptResponse {
|
||||
revision: applied.revision,
|
||||
changed_fields: protocol_changed_fields,
|
||||
epiphany_state: epiphany_state.clone(),
|
||||
role_id,
|
||||
binding_id: binding_id.clone(),
|
||||
accepted_receipt_id: applied.accepted_receipt_id,
|
||||
accepted_observation_id: applied.accepted_observation_id,
|
||||
accepted_evidence_id: applied.accepted_evidence_id,
|
||||
applied_patch: protocol_patch_from_core(applied.applied_patch),
|
||||
finding: protocol_role_finding(role_id, applied.finding),
|
||||
},
|
||||
)
|
||||
.await;
|
||||
self.send_epiphany_state_updated(
|
||||
thread_uuid,
|
||||
ThreadEpiphanyStateUpdatedSource::RoleAccept,
|
||||
changed_fields,
|
||||
epiphany_state,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
pub(super) async fn thread_epiphany_reorient_launch(
|
||||
&self,
|
||||
request_id: ConnectionRequestId,
|
||||
params: ThreadEpiphanyReorientLaunchParams,
|
||||
) {
|
||||
let ThreadEpiphanyReorientLaunchParams {
|
||||
thread_id,
|
||||
expected_revision,
|
||||
max_runtime_seconds,
|
||||
} = params;
|
||||
|
||||
let (thread_uuid, loaded_thread) =
|
||||
match self.load_epiphany_thread(&request_id, &thread_id).await {
|
||||
Some(thread) => thread,
|
||||
None => return,
|
||||
};
|
||||
let thread = match self.read_thread_view(thread_uuid, false).await {
|
||||
Ok(thread) => thread,
|
||||
Err(ThreadReadViewError::InvalidRequest(message)) => {
|
||||
self.send_invalid_request_error(request_id, message).await;
|
||||
return;
|
||||
}
|
||||
Err(ThreadReadViewError::Internal(message)) => {
|
||||
self.send_internal_error(request_id, message).await;
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let retrieval_override = thread_epiphany_retrieval_state(loaded_thread.as_ref()).await;
|
||||
let config_snapshot = loaded_thread.config_snapshot().await;
|
||||
self.epiphany_invalidation_manager
|
||||
.ensure_thread_watch(&thread_id, &config_snapshot.cwd)
|
||||
.await;
|
||||
let watcher_snapshot = self
|
||||
.epiphany_invalidation_manager
|
||||
.snapshot(&thread_id)
|
||||
.await;
|
||||
let token_usage_info = loaded_thread.token_usage_info().await;
|
||||
let host = EpiphanyCodexThreadHost::new(loaded_thread.as_ref());
|
||||
let token_usage_snapshot = epiphany_token_usage_snapshot(token_usage_info.as_ref());
|
||||
let applied = match launch_thread_epiphany_reorient(
|
||||
&host,
|
||||
&thread_id,
|
||||
expected_revision,
|
||||
max_runtime_seconds,
|
||||
thread.epiphany_state.as_ref(),
|
||||
Some(&retrieval_override),
|
||||
Some(epiphany_freshness_watcher_snapshot(&watcher_snapshot)),
|
||||
token_usage_snapshot.as_ref(),
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(applied) => applied,
|
||||
Err(EpiphanyBridgeError::InvalidRequest(message)) => {
|
||||
self.send_invalid_request_error(request_id, message).await;
|
||||
return;
|
||||
}
|
||||
Err(err) => {
|
||||
self.send_internal_error(
|
||||
request_id,
|
||||
format!(
|
||||
"failed to launch Epiphany reorientation worker for {thread_uuid}: {err}"
|
||||
),
|
||||
)
|
||||
.await;
|
||||
return;
|
||||
}
|
||||
};
|
||||
let changed_fields = applied.changed_fields;
|
||||
let protocol_changed_fields = protocol_state_updated_fields(changed_fields.clone());
|
||||
let epiphany_state = applied.epiphany_state;
|
||||
|
||||
self.outgoing
|
||||
.send_response(
|
||||
request_id,
|
||||
ThreadEpiphanyReorientLaunchResponse {
|
||||
thread_id: thread_uuid.to_string(),
|
||||
source: protocol_reorient_source(applied.source),
|
||||
state_status: protocol_reorient_state_status(applied.state_status),
|
||||
state_revision: applied.state_revision,
|
||||
decision: protocol_reorient_decision(applied.decision),
|
||||
revision: applied.revision,
|
||||
changed_fields: protocol_changed_fields,
|
||||
epiphany_state: epiphany_state.clone(),
|
||||
job: protocol_job_from_surface(
|
||||
applied.job,
|
||||
Some(applied.launcher_job_id),
|
||||
Some(applied.backend_job_id),
|
||||
),
|
||||
},
|
||||
)
|
||||
.await;
|
||||
self.send_epiphany_state_updated(
|
||||
thread_uuid,
|
||||
ThreadEpiphanyStateUpdatedSource::JobLaunch,
|
||||
changed_fields,
|
||||
epiphany_state,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
pub(super) async fn thread_epiphany_reorient_accept(
|
||||
&self,
|
||||
request_id: ConnectionRequestId,
|
||||
params: ThreadEpiphanyReorientAcceptParams,
|
||||
) {
|
||||
let ThreadEpiphanyReorientAcceptParams {
|
||||
thread_id,
|
||||
expected_revision,
|
||||
binding_id,
|
||||
update_scratch,
|
||||
update_investigation_checkpoint,
|
||||
} = params;
|
||||
let binding_id = binding_id.unwrap_or_else(|| EPIPHANY_REORIENT_LAUNCH_BINDING_ID.into());
|
||||
|
||||
let (thread_uuid, loaded_thread) =
|
||||
match self.load_epiphany_thread(&request_id, &thread_id).await {
|
||||
Some(thread) => thread,
|
||||
None => return,
|
||||
};
|
||||
let host = EpiphanyCodexThreadHost::new(loaded_thread.as_ref());
|
||||
let applied = match apply_thread_epiphany_reorient_accept(
|
||||
&host,
|
||||
expected_revision,
|
||||
binding_id.as_str(),
|
||||
update_scratch,
|
||||
update_investigation_checkpoint,
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(applied) => applied,
|
||||
Err(EpiphanyBridgeError::InvalidRequest(message)) => {
|
||||
self.send_invalid_request_error(request_id, message).await;
|
||||
return;
|
||||
}
|
||||
Err(err) => {
|
||||
self.send_internal_error(
|
||||
request_id,
|
||||
format!("failed to accept Epiphany reorientation finding: {err}"),
|
||||
)
|
||||
.await;
|
||||
return;
|
||||
}
|
||||
};
|
||||
let changed_fields = applied.changed_fields;
|
||||
let protocol_changed_fields = protocol_state_updated_fields(changed_fields.clone());
|
||||
let epiphany_state = applied.epiphany_state;
|
||||
|
||||
self.outgoing
|
||||
.send_response(
|
||||
request_id,
|
||||
ThreadEpiphanyReorientAcceptResponse {
|
||||
revision: applied.revision,
|
||||
changed_fields: protocol_changed_fields,
|
||||
epiphany_state: epiphany_state.clone(),
|
||||
binding_id: binding_id.clone(),
|
||||
accepted_receipt_id: applied.accepted_receipt_id,
|
||||
accepted_observation_id: applied.accepted_observation_id,
|
||||
accepted_evidence_id: applied.accepted_evidence_id,
|
||||
finding: protocol_reorient_finding(applied.finding),
|
||||
},
|
||||
)
|
||||
.await;
|
||||
self.send_epiphany_state_updated(
|
||||
thread_uuid,
|
||||
ThreadEpiphanyStateUpdatedSource::ReorientAccept,
|
||||
changed_fields,
|
||||
epiphany_state,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
pub(super) async fn thread_epiphany_index(
|
||||
&self,
|
||||
request_id: ConnectionRequestId,
|
||||
params: ThreadEpiphanyIndexParams,
|
||||
) {
|
||||
let ThreadEpiphanyIndexParams {
|
||||
thread_id,
|
||||
force_full_rebuild,
|
||||
} = params;
|
||||
|
||||
let (thread_uuid, thread) = match self.load_epiphany_thread(&request_id, &thread_id).await {
|
||||
Some(thread) => thread,
|
||||
None => return,
|
||||
};
|
||||
|
||||
let config = thread.config_snapshot().await;
|
||||
let codex_home = thread.codex_home().await;
|
||||
let response = match index_epiphany_retrieval_for_paths(
|
||||
config.cwd.to_path_buf(),
|
||||
codex_home,
|
||||
force_full_rebuild,
|
||||
)
|
||||
.await
|
||||
.and_then(protocol_index_response)
|
||||
{
|
||||
Ok(response) => response,
|
||||
Err(err) => {
|
||||
self.send_internal_error(
|
||||
request_id,
|
||||
format!("failed to index Epiphany retrieval state for {thread_uuid}: {err}"),
|
||||
)
|
||||
.await;
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
self.outgoing.send_response(request_id, response).await;
|
||||
}
|
||||
|
||||
pub(super) async fn thread_epiphany_promote(
|
||||
&self,
|
||||
request_id: ConnectionRequestId,
|
||||
params: ThreadEpiphanyPromoteParams,
|
||||
) {
|
||||
let ThreadEpiphanyPromoteParams {
|
||||
thread_id,
|
||||
expected_revision,
|
||||
patch,
|
||||
verifier_evidence,
|
||||
} = params;
|
||||
|
||||
let (thread_uuid, thread) = match self.load_epiphany_thread(&request_id, &thread_id).await {
|
||||
Some(thread) => thread,
|
||||
None => return,
|
||||
};
|
||||
|
||||
let host = EpiphanyCodexThreadHost::new(thread.as_ref());
|
||||
let core_patch = protocol_update_patch_to_core(&patch);
|
||||
let applied = match apply_thread_epiphany_promote(
|
||||
&host,
|
||||
expected_revision,
|
||||
core_patch,
|
||||
verifier_evidence,
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(applied) => applied,
|
||||
Err(EpiphanyBridgeError::InvalidRequest(message)) => {
|
||||
self.send_invalid_request_error(request_id, message).await;
|
||||
return;
|
||||
}
|
||||
Err(err) => {
|
||||
self.send_internal_error(
|
||||
request_id,
|
||||
format!("failed to promote Epiphany state update: {err}"),
|
||||
)
|
||||
.await;
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let applied = match applied {
|
||||
EpiphanyThreadPromoteApplied::Accepted(applied) => applied,
|
||||
EpiphanyThreadPromoteApplied::Rejected { reasons } => {
|
||||
self.outgoing
|
||||
.send_response(
|
||||
request_id,
|
||||
ThreadEpiphanyPromoteResponse {
|
||||
accepted: false,
|
||||
reasons,
|
||||
revision: None,
|
||||
changed_fields: Vec::new(),
|
||||
epiphany_state: None,
|
||||
},
|
||||
)
|
||||
.await;
|
||||
return;
|
||||
}
|
||||
};
|
||||
let changed_fields = applied.changed_fields;
|
||||
let protocol_changed_fields = protocol_state_updated_fields(changed_fields.clone());
|
||||
let epiphany_state = applied.epiphany_state;
|
||||
let response = ThreadEpiphanyPromoteResponse {
|
||||
accepted: true,
|
||||
reasons: Vec::new(),
|
||||
revision: Some(applied.revision),
|
||||
changed_fields: protocol_changed_fields,
|
||||
epiphany_state: Some(epiphany_state.clone()),
|
||||
};
|
||||
|
||||
self.outgoing.send_response(request_id, response).await;
|
||||
self.send_epiphany_state_updated(
|
||||
thread_uuid,
|
||||
ThreadEpiphanyStateUpdatedSource::Promote,
|
||||
changed_fields,
|
||||
epiphany_state,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
pub(super) async fn thread_epiphany_update(
|
||||
&self,
|
||||
request_id: ConnectionRequestId,
|
||||
|
|
@ -591,97 +122,6 @@ impl CodexMessageProcessor {
|
|||
.await;
|
||||
}
|
||||
|
||||
pub(super) async fn thread_epiphany_job_launch(
|
||||
&self,
|
||||
request_id: ConnectionRequestId,
|
||||
params: ThreadEpiphanyJobLaunchParams,
|
||||
) {
|
||||
let ThreadEpiphanyJobLaunchParams {
|
||||
thread_id,
|
||||
expected_revision,
|
||||
binding_id,
|
||||
kind,
|
||||
scope,
|
||||
owner_role,
|
||||
authority_scope,
|
||||
linked_subgoal_ids,
|
||||
linked_graph_node_ids,
|
||||
instruction,
|
||||
launch_document,
|
||||
output_contract_id,
|
||||
max_runtime_seconds,
|
||||
} = params;
|
||||
|
||||
let (thread_uuid, thread) = match self.load_epiphany_thread(&request_id, &thread_id).await {
|
||||
Some(thread) => thread,
|
||||
None => return,
|
||||
};
|
||||
|
||||
let host = EpiphanyCodexThreadHost::new(thread.as_ref());
|
||||
let core_launch_document = protocol_worker_launch_document_to_core(launch_document);
|
||||
let applied = match launch_thread_epiphany_job(
|
||||
&host,
|
||||
build_epiphany_job_launch_request(
|
||||
expected_revision,
|
||||
binding_id.clone(),
|
||||
kind,
|
||||
scope,
|
||||
owner_role,
|
||||
authority_scope,
|
||||
linked_subgoal_ids,
|
||||
linked_graph_node_ids,
|
||||
instruction,
|
||||
core_launch_document,
|
||||
output_contract_id,
|
||||
max_runtime_seconds,
|
||||
),
|
||||
kind,
|
||||
"missing launched job projection",
|
||||
)
|
||||
.await
|
||||
{
|
||||
Ok(applied) => applied,
|
||||
Err(EpiphanyBridgeError::InvalidRequest(message)) => {
|
||||
self.send_invalid_request_error(request_id, message).await;
|
||||
return;
|
||||
}
|
||||
Err(err) => {
|
||||
self.send_internal_error(
|
||||
request_id,
|
||||
format!("failed to launch Epiphany job for {thread_uuid}: {err}"),
|
||||
)
|
||||
.await;
|
||||
return;
|
||||
}
|
||||
};
|
||||
let changed_fields = applied.changed_fields;
|
||||
let protocol_changed_fields = protocol_state_updated_fields(changed_fields.clone());
|
||||
let epiphany_state = applied.epiphany_state;
|
||||
|
||||
self.outgoing
|
||||
.send_response(
|
||||
request_id,
|
||||
ThreadEpiphanyJobLaunchResponse {
|
||||
revision: applied.revision,
|
||||
changed_fields: protocol_changed_fields,
|
||||
epiphany_state: epiphany_state.clone(),
|
||||
job: protocol_job_from_surface(
|
||||
applied.job,
|
||||
Some(applied.launcher_job_id),
|
||||
Some(applied.backend_job_id),
|
||||
),
|
||||
},
|
||||
)
|
||||
.await;
|
||||
self.send_epiphany_state_updated(
|
||||
thread_uuid,
|
||||
ThreadEpiphanyStateUpdatedSource::JobLaunch,
|
||||
changed_fields,
|
||||
epiphany_state,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
||||
pub(super) async fn thread_epiphany_job_interrupt(
|
||||
&self,
|
||||
request_id: ConnectionRequestId,
|
||||
|
|
@ -748,9 +188,3 @@ impl CodexMessageProcessor {
|
|||
.await;
|
||||
}
|
||||
}
|
||||
|
||||
async fn thread_epiphany_retrieval_state(thread: &CodexThread) -> EpiphanyRetrievalState {
|
||||
let config = thread.config_snapshot().await;
|
||||
let codex_home = thread.codex_home().await;
|
||||
epiphany_retrieval_state_for_paths(config.cwd.to_path_buf(), codex_home).await
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ use codex_app_server_protocol::*;
|
|||
use codex_core::CodexThread;
|
||||
use codex_protocol::ThreadId;
|
||||
use epiphany_state_model::EpiphanyRetrievalState;
|
||||
use super::epiphany_thread_host::load_authoritative_epiphany_state;
|
||||
use super::epiphany_thread_host::runtime_spine_store_path;
|
||||
use epiphany_codex_bridge::cultnet::EpiphanySurfaceSource;
|
||||
use epiphany_codex_bridge::invalidation::epiphany_freshness_watcher_snapshot;
|
||||
|
|
@ -14,24 +13,18 @@ use epiphany_codex_bridge::protocol_edge::core_epiphany_view_needs_reorientation
|
|||
use epiphany_codex_bridge::protocol_edge::core_epiphany_view_needs_runtime_store;
|
||||
use epiphany_codex_bridge::protocol_edge::default_core_epiphany_view_lenses;
|
||||
use epiphany_codex_bridge::protocol_edge::protocol_context_params_to_core;
|
||||
use epiphany_codex_bridge::protocol_edge::protocol_distill_params_to_core;
|
||||
use epiphany_codex_bridge::protocol_edge::protocol_freshness_response_from_surface;
|
||||
use epiphany_codex_bridge::protocol_edge::protocol_graph_query_to_core;
|
||||
use epiphany_codex_bridge::protocol_edge::protocol_role_id_to_core;
|
||||
use epiphany_codex_bridge::protocol_edge::protocol_view_lenses_to_core;
|
||||
use epiphany_codex_bridge::retrieve::epiphany_retrieval_state_for_paths;
|
||||
use epiphany_codex_bridge::retrieve::normalize_thread_epiphany_retrieve_query;
|
||||
use epiphany_codex_bridge::retrieve::retrieve_epiphany_for_paths;
|
||||
use epiphany_codex_bridge::retrieve_protocol::protocol_retrieve_response;
|
||||
use epiphany_codex_bridge::view_protocol::EpiphanyFreshnessResponseInput;
|
||||
use epiphany_codex_bridge::view_protocol::EpiphanyReorientResultResponseInput;
|
||||
use epiphany_codex_bridge::view_protocol::EpiphanyRoleResultResponseInput;
|
||||
use epiphany_codex_bridge::view_protocol::EpiphanyViewResponseInput;
|
||||
use epiphany_codex_bridge::view_protocol::derive_epiphany_freshness_surface;
|
||||
use epiphany_codex_bridge::view_protocol::map_epiphany_context_response;
|
||||
use epiphany_codex_bridge::view_protocol::map_epiphany_distill_response;
|
||||
use epiphany_codex_bridge::view_protocol::map_epiphany_graph_query_response;
|
||||
use epiphany_codex_bridge::view_protocol::map_epiphany_propose_response;
|
||||
use epiphany_codex_bridge::view_protocol::map_epiphany_reorient_result_response;
|
||||
use epiphany_codex_bridge::view_protocol::map_epiphany_role_result_response;
|
||||
use epiphany_codex_bridge::view_protocol::map_epiphany_view_response;
|
||||
|
|
@ -415,171 +408,7 @@ impl CodexMessageProcessor {
|
|||
self.outgoing.send_response(request_id, response).await;
|
||||
}
|
||||
|
||||
pub(super) async fn thread_epiphany_retrieve(
|
||||
&self,
|
||||
request_id: ConnectionRequestId,
|
||||
params: ThreadEpiphanyRetrieveParams,
|
||||
) {
|
||||
let ThreadEpiphanyRetrieveParams {
|
||||
thread_id,
|
||||
query,
|
||||
limit,
|
||||
path_prefixes,
|
||||
} = params;
|
||||
|
||||
let thread_uuid = match ThreadId::from_string(&thread_id) {
|
||||
Ok(id) => id,
|
||||
Err(err) => {
|
||||
self.send_invalid_request_error(request_id, format!("invalid thread id: {err}"))
|
||||
.await;
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let query = match normalize_thread_epiphany_retrieve_query(query, limit, path_prefixes) {
|
||||
Ok(query) => query,
|
||||
Err(message) => {
|
||||
self.send_invalid_request_error(request_id, message.to_string())
|
||||
.await;
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let thread = match self.thread_manager.get_thread(thread_uuid).await {
|
||||
Ok(thread) => thread,
|
||||
Err(_) => {
|
||||
self.send_invalid_request_error(
|
||||
request_id,
|
||||
format!("thread not loaded: {thread_uuid}"),
|
||||
)
|
||||
.await;
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let config = thread.config_snapshot().await;
|
||||
let codex_home = thread.codex_home().await;
|
||||
let response =
|
||||
match retrieve_epiphany_for_paths(config.cwd.to_path_buf(), codex_home, query)
|
||||
.await
|
||||
.and_then(protocol_retrieve_response)
|
||||
{
|
||||
Ok(response) => response,
|
||||
Err(err) => {
|
||||
self.send_internal_error(
|
||||
request_id,
|
||||
format!("failed to retrieve Epiphany results for {thread_uuid}: {err}"),
|
||||
)
|
||||
.await;
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
self.outgoing.send_response(request_id, response).await;
|
||||
}
|
||||
|
||||
pub(super) async fn thread_epiphany_distill(
|
||||
&self,
|
||||
request_id: ConnectionRequestId,
|
||||
params: ThreadEpiphanyDistillParams,
|
||||
) {
|
||||
let thread_id = params.thread_id.clone();
|
||||
|
||||
let thread_uuid = match ThreadId::from_string(&thread_id) {
|
||||
Ok(id) => id,
|
||||
Err(err) => {
|
||||
self.send_invalid_request_error(request_id, format!("invalid thread id: {err}"))
|
||||
.await;
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let thread = match self.thread_manager.get_thread(thread_uuid).await {
|
||||
Ok(thread) => thread,
|
||||
Err(_) => {
|
||||
self.send_invalid_request_error(
|
||||
request_id,
|
||||
format!("thread not loaded: {thread_uuid}"),
|
||||
)
|
||||
.await;
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let expected_revision = load_authoritative_epiphany_state(&thread)
|
||||
.await
|
||||
.map(|state| state.revision)
|
||||
.unwrap_or(0);
|
||||
let input = protocol_distill_params_to_core(params);
|
||||
let response = match map_epiphany_distill_response(expected_revision, input) {
|
||||
Ok(response) => response,
|
||||
Err(err) => {
|
||||
self.send_invalid_request_error(
|
||||
request_id,
|
||||
format!("failed to distill Epiphany observation: {err}"),
|
||||
)
|
||||
.await;
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
self.outgoing.send_response(request_id, response).await;
|
||||
}
|
||||
|
||||
pub(super) async fn thread_epiphany_propose(
|
||||
&self,
|
||||
request_id: ConnectionRequestId,
|
||||
params: ThreadEpiphanyProposeParams,
|
||||
) {
|
||||
let ThreadEpiphanyProposeParams { thread_id, .. } = ¶ms;
|
||||
let thread_id = thread_id.clone();
|
||||
|
||||
let thread_uuid = match ThreadId::from_string(&thread_id) {
|
||||
Ok(id) => id,
|
||||
Err(err) => {
|
||||
self.send_invalid_request_error(request_id, format!("invalid thread id: {err}"))
|
||||
.await;
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let thread = match self.thread_manager.get_thread(thread_uuid).await {
|
||||
Ok(thread) => thread,
|
||||
Err(_) => {
|
||||
self.send_invalid_request_error(
|
||||
request_id,
|
||||
format!("thread not loaded: {thread_uuid}"),
|
||||
)
|
||||
.await;
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let state = match load_authoritative_epiphany_state(&thread).await {
|
||||
Some(state) => state,
|
||||
None => {
|
||||
self.send_invalid_request_error(
|
||||
request_id,
|
||||
format!("thread has no Epiphany state: {thread_uuid}"),
|
||||
)
|
||||
.await;
|
||||
return;
|
||||
}
|
||||
};
|
||||
let response = match map_epiphany_propose_response(state, params.observation_ids) {
|
||||
Ok(response) => response,
|
||||
Err(err) => {
|
||||
self.send_invalid_request_error(
|
||||
request_id,
|
||||
format!("failed to propose Epiphany map update: {err}"),
|
||||
)
|
||||
.await;
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
self.outgoing.send_response(request_id, response).await;
|
||||
}
|
||||
}
|
||||
|
||||
async fn thread_epiphany_retrieval_state(thread: &CodexThread) -> EpiphanyRetrievalState {
|
||||
|
|
|
|||
Reference in a new issue