Move job interrupt protocol shaping into bridge
This commit is contained in:
parent
946145a85e
commit
5e8bb3229a
6 changed files with 77 additions and 28 deletions
|
|
@ -25,6 +25,8 @@ use codex_app_server_protocol::ThreadEpiphanyGraphQueryKind;
|
|||
use codex_app_server_protocol::ThreadEpiphanyInvalidationInput;
|
||||
use codex_app_server_protocol::ThreadEpiphanyInvalidationStatus;
|
||||
use codex_app_server_protocol::ThreadEpiphanyJob;
|
||||
use codex_app_server_protocol::ThreadEpiphanyJobInterruptParams;
|
||||
use codex_app_server_protocol::ThreadEpiphanyJobInterruptResponse;
|
||||
use codex_app_server_protocol::ThreadEpiphanyJobKind;
|
||||
use codex_app_server_protocol::ThreadEpiphanyJobLaunchParams;
|
||||
use codex_app_server_protocol::ThreadEpiphanyJobStatus;
|
||||
|
|
@ -96,6 +98,7 @@ use epiphany_core::epiphany_view_needs_reorientation_inputs;
|
|||
use epiphany_core::epiphany_view_needs_runtime_store;
|
||||
use epiphany_state_model::EpiphanyThreadState;
|
||||
|
||||
use crate::mutation_service::EpiphanyJobInterruptApplied;
|
||||
use crate::mutation_service::EpiphanyThreadPromoteApplied;
|
||||
use crate::mutation_service::EpiphanyThreadUpdateApplied;
|
||||
|
||||
|
|
@ -567,6 +570,56 @@ pub fn thread_epiphany_promote_output(
|
|||
}
|
||||
}
|
||||
|
||||
pub struct ThreadEpiphanyJobInterruptPlan {
|
||||
pub thread_id: String,
|
||||
pub expected_revision: Option<u64>,
|
||||
pub binding_id: String,
|
||||
pub reason: Option<String>,
|
||||
}
|
||||
|
||||
pub fn plan_thread_epiphany_job_interrupt(
|
||||
params: ThreadEpiphanyJobInterruptParams,
|
||||
) -> ThreadEpiphanyJobInterruptPlan {
|
||||
let ThreadEpiphanyJobInterruptParams {
|
||||
thread_id,
|
||||
expected_revision,
|
||||
binding_id,
|
||||
reason,
|
||||
} = params;
|
||||
ThreadEpiphanyJobInterruptPlan {
|
||||
thread_id,
|
||||
expected_revision,
|
||||
binding_id,
|
||||
reason,
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ThreadEpiphanyJobInterruptRouteOutput {
|
||||
pub response: ThreadEpiphanyJobInterruptResponse,
|
||||
pub changed_fields: Vec<EpiphanyStateUpdatedField>,
|
||||
pub epiphany_state: EpiphanyThreadState,
|
||||
}
|
||||
|
||||
pub fn thread_epiphany_job_interrupt_output(
|
||||
applied: EpiphanyJobInterruptApplied,
|
||||
) -> ThreadEpiphanyJobInterruptRouteOutput {
|
||||
let changed_fields = applied.changed_fields;
|
||||
let epiphany_state = applied.epiphany_state;
|
||||
let response = ThreadEpiphanyJobInterruptResponse {
|
||||
cancel_requested: applied.cancel_requested,
|
||||
interrupted_thread_ids: applied.interrupted_thread_ids,
|
||||
revision: applied.revision,
|
||||
changed_fields: protocol_state_updated_fields(changed_fields.clone()),
|
||||
epiphany_state: epiphany_state.clone(),
|
||||
job: protocol_job_from_surface(applied.job, None, None),
|
||||
};
|
||||
ThreadEpiphanyJobInterruptRouteOutput {
|
||||
response,
|
||||
changed_fields,
|
||||
epiphany_state,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn protocol_state_updated_fields(
|
||||
fields: Vec<EpiphanyStateUpdatedField>,
|
||||
) -> Vec<ThreadEpiphanyStateUpdatedField> {
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ The spine exists in more than twenty-five live paths:
|
|||
- scene projection: the `scene` view lens reads the same live/stored Epiphany state view used by `thread/read` and adapts the core scene projection. `epiphany-core` derives the compact client scene with objective, active subgoal, invariant status counts, graph focus/counts, retrieval status, investigation-checkpoint summary counts, observation/evidence summaries, churn, live/stored source, and available control-plane actions without mutating or persisting state in [scene.rs](E:/Projects/EpiphanyAgent/epiphany-core/src/surfaces/scene.rs:1).
|
||||
- job/progress reflection: the `jobs` view lens reads the same thread view as scene, optionally fills retrieval state for loaded threads with no Epiphany state, and adapts core-derived non-authoritative progress slots for retrieval indexing, graph remap, invariant verification, and bound specialist work. Durable `job_bindings` now act as thin Epiphany-owned authority slots with binding id, kind, scope, owner role, authority scope, linked ids, and optional blocking reason only. Runtime job ids project from `runtime_links` through core job view policy; old `agent_jobs` bindings are sealed instead of live-crawled in [jobs.rs](E:/Projects/EpiphanyAgent/epiphany-core/src/surfaces/jobs.rs:1).
|
||||
- bound-job progress notifications: app-server still declares experimental `thread/epiphany/jobsUpdated` as sealed legacy Codex evacuation telemetry, but the handler no longer emits Epiphany updates from old `agent_job_progress:{json}` background events. Heartbeat-owned activation and typed runtime-spine receipts are now the live Epiphany path.
|
||||
- explicit job control: app-server declares experimental `thread/epiphany/jobLaunch` and `thread/epiphany/jobInterrupt`. `jobLaunch` route handling delegates protocol worker-launch document conversion and core `EpiphanyJobLaunchRequest` construction to `epiphany-codex-bridge::protocol_edge::plan_thread_epiphany_job_launch`; app-server then routes the planned request through core launch handlers that persist heartbeat-backed durable `job_bindings` revisions through the authoritative Epiphany state path. The write emits `thread/epiphany/stateUpdated` with source `jobLaunch` or `jobInterrupt` instead of creating SQLite `agent_jobs` rows or pretending jobs reflection is the scheduler.
|
||||
- explicit job control: app-server declares experimental `thread/epiphany/jobLaunch` and `thread/epiphany/jobInterrupt`. `jobLaunch` route handling delegates protocol worker-launch document conversion and core `EpiphanyJobLaunchRequest` construction to `epiphany-codex-bridge::protocol_edge::plan_thread_epiphany_job_launch`; `jobInterrupt` delegates protocol param planning and legacy response projection to `plan_thread_epiphany_job_interrupt` / `thread_epiphany_job_interrupt_output`. App-server then routes the planned request through core launch/interrupt handlers that persist heartbeat-backed durable `job_bindings` revisions through the authoritative Epiphany state path. The write emits `thread/epiphany/stateUpdated` with source `jobLaunch` or `jobInterrupt` instead of creating SQLite `agent_jobs` rows or pretending jobs reflection is the scheduler.
|
||||
- freshness reflection: app-server declares experimental read-only `thread/epiphany/freshness`, reads the same live/stored thread view used by the view lenses and context, fills live retrieval state for loaded threads, and gathers watcher snapshots because those are host lifecycle facts. `epiphany-codex-bridge::view_protocol::map_epiphany_freshness_response` owns freshness surface derivation and protocol response projection. `epiphany-core` derives the bounded retrieval/graph freshness lens plus watcher-backed invalidation inputs with watched root, changed paths, mapped graph-node hits, active-frontier hits, and revision/source identity; the read does not mutate, schedule, notify, or perform automatic semantic invalidation in [freshness.rs](E:/Projects/EpiphanyAgent/epiphany-core/src/surfaces/freshness.rs:1), [view_protocol.rs](E:/Projects/EpiphanyAgent/epiphany-codex-bridge/src/view_protocol.rs:91), [common.rs](E:/Projects/EpiphanyAgent/vendor/codex/codex-rs/app-server-protocol/src/protocol/common.rs:350), [v2.rs](E:/Projects/EpiphanyAgent/vendor/codex/codex-rs/app-server-protocol/src/protocol/v2.rs:4167), [v2.rs](E:/Projects/EpiphanyAgent/vendor/codex/codex-rs/app-server-protocol/src/protocol/v2.rs:4259), [epiphany_invalidation.rs](E:/Projects/EpiphanyAgent/vendor/codex/codex-rs/app-server/src/epiphany_invalidation.rs:19), and [epiphany_read_routes.rs](E:/Projects/EpiphanyAgent/vendor/codex/codex-rs/app-server/src/codex_message_processor/epiphany_read_routes.rs:221).
|
||||
- targeted context reflection: app-server declares experimental read-only `thread/epiphany/context`, reads the same live/stored Epiphany state view used by the view lenses, and derives a bounded state shard containing selected graph nodes/edges, links, active frontier, checkpoint, the full durable investigation-checkpoint packet, observations, direct evidence, linked evidence, and missing requested ids without retrieval, proposal, promotion, notification, mutation, or persistence in [common.rs](E:/Projects/EpiphanyAgent/vendor/codex/codex-rs/app-server-protocol/src/protocol/common.rs:355), [v2.rs](E:/Projects/EpiphanyAgent/vendor/codex/codex-rs/app-server-protocol/src/protocol/v2.rs:4253), [codex_message_processor.rs](E:/Projects/EpiphanyAgent/vendor/codex/codex-rs/app-server/src/codex_message_processor.rs:4261), and [codex_message_processor.rs](E:/Projects/EpiphanyAgent/vendor/codex/codex-rs/app-server/src/codex_message_processor.rs:11152).
|
||||
- graph traversal reflection: app-server declares experimental read-only `thread/epiphany/graphQuery`, reads the same live Epiphany state view used by context, and derives explicit node/edge lookup, path/symbol matches, bounded neighbors, edge-kind matches, and frontier neighborhoods with architecture/dataflow links plus frontier/checkpoint identity, matched selectors, and missing ids without retrieval, proposal, promotion, indexing, notification, mutation, or scheduling in [common.rs](E:/Projects/EpiphanyAgent/vendor/codex/codex-rs/app-server-protocol/src/protocol/common.rs:379), [v2.rs](E:/Projects/EpiphanyAgent/vendor/codex/codex-rs/app-server-protocol/src/protocol/v2.rs:4720), [codex_message_processor.rs](E:/Projects/EpiphanyAgent/vendor/codex/codex-rs/app-server/src/codex_message_processor.rs:5060), and [codex_message_processor.rs](E:/Projects/EpiphanyAgent/vendor/codex/codex-rs/app-server/src/codex_message_processor.rs:15201).
|
||||
|
|
|
|||
|
|
@ -925,6 +925,14 @@ App-server still loads the thread, calls the mutation service, sends the
|
|||
response, and emits `stateUpdated`. `epiphany_mutation_routes.rs` is about 686
|
||||
lines after this cut.
|
||||
|
||||
Job-interrupt write-route planning and response shaping moved to the bridge
|
||||
protocol edge. `plan_thread_epiphany_job_interrupt` owns protocol param
|
||||
unpacking and `thread_epiphany_job_interrupt_output` owns the legacy interrupt
|
||||
response DTO plus notification payload handoff. App-server still loads the
|
||||
thread, calls the mutation service, sends the response, and emits
|
||||
`stateUpdated`. `epiphany_mutation_routes.rs` is about 674 lines after this
|
||||
cut.
|
||||
|
||||
Context and graph-query route-level conversion followed. App-server still
|
||||
parses thread ids, loads live/stored thread views, and emits JSON-RPC
|
||||
responses, but `map_thread_epiphany_context_response` and
|
||||
|
|
|
|||
BIN
state/ledgers.cc
BIN
state/ledgers.cc
Binary file not shown.
|
|
@ -139,7 +139,7 @@ accepted_design:
|
|||
- `epiphany-core` and `epiphany-codex-bridge` must not depend directly on vendored `codex-core`. Host-specific Codex concepts belong in vendored app-server host adapters or the thin OpenAI subscription auth/model-transport spine, while native retrieval/state/runtime/mutation policy stays in Epiphany crates.
|
||||
- The prompt reads typed state through a bounded `<epiphany_state>` developer fragment.
|
||||
- Clients read typed state through `Thread.epiphanyState`, read-only `thread/epiphany/view` lenses for scene/jobs/roles/planning/pressure/reorient/CRRC/coordinator, freshness through `thread/epiphany/freshness`, targeted state shards through `thread/epiphany/context`, bounded graph traversal through `thread/epiphany/graphQuery`, and reviewable role output through `thread/epiphany/roleResult`. The scene, jobs, roles, planning, pressure, reorient, CRRC, and coordinator read projections now live only as view lenses, not standalone protocol verbs.
|
||||
- Durable typed state writes route through `thread/epiphany/update`, accepted `thread/epiphany/promote`, `thread/epiphany/reorientAccept`, `thread/epiphany/roleAccept`, or the bounded `thread/epiphany/jobLaunch`, `thread/epiphany/jobInterrupt`, `thread/epiphany/reorientLaunch`, and `thread/epiphany/roleLaunch` authority surfaces when they mutate state or `job_bindings`; update/promote patch conversion and response shaping, jobLaunch request construction, plus roleLaunch/roleAccept/reorientAccept protocol/default-binding planning now live in the bridge protocol edge rather than app-server mutation routes.
|
||||
- Durable typed state writes route through `thread/epiphany/update`, accepted `thread/epiphany/promote`, `thread/epiphany/reorientAccept`, `thread/epiphany/roleAccept`, or the bounded `thread/epiphany/jobLaunch`, `thread/epiphany/jobInterrupt`, `thread/epiphany/reorientLaunch`, and `thread/epiphany/roleLaunch` authority surfaces when they mutate state or `job_bindings`; update/promote and jobInterrupt response shaping, jobLaunch request construction, plus roleLaunch/roleAccept/reorientAccept protocol/default-binding planning now live in the bridge protocol edge rather than app-server mutation routes.
|
||||
- `thread/epiphany/retrieve`, `thread/epiphany/distill`, `thread/epiphany/propose`, `thread/epiphany/freshness`, `thread/epiphany/context`, `thread/epiphany/graphQuery`, and `thread/epiphany/view` stay read-only.
|
||||
- `thread/epiphany/index` writes the retrieval catalog, not durable Epiphany understanding; catalog is auspex output, not doctrine.
|
||||
- Qdrant is the preferred persistent semantic backend; BM25 remains the bootstrap/fallback/control path. Qdrant's HTTP API and acknowledgements remain JSON wire edges, but Epiphany-owned retrieval point payloads are typed at the adapter boundary before serialization/deserialization.
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ 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::plan_thread_epiphany_job_interrupt;
|
||||
use epiphany_codex_bridge::protocol_edge::plan_thread_epiphany_job_launch;
|
||||
use epiphany_codex_bridge::protocol_edge::plan_thread_epiphany_promote;
|
||||
use epiphany_codex_bridge::protocol_edge::plan_thread_epiphany_reorient_accept;
|
||||
|
|
@ -29,6 +30,7 @@ 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_state_updated_fields;
|
||||
use epiphany_codex_bridge::protocol_edge::protocol_state_updated_notification;
|
||||
use epiphany_codex_bridge::protocol_edge::thread_epiphany_job_interrupt_output;
|
||||
use epiphany_codex_bridge::protocol_edge::thread_epiphany_promote_output;
|
||||
use epiphany_codex_bridge::protocol_edge::thread_epiphany_update_output;
|
||||
use epiphany_codex_bridge::retrieve::epiphany_retrieval_state_for_paths;
|
||||
|
|
@ -617,14 +619,12 @@ impl CodexMessageProcessor {
|
|||
request_id: ConnectionRequestId,
|
||||
params: ThreadEpiphanyJobInterruptParams,
|
||||
) {
|
||||
let ThreadEpiphanyJobInterruptParams {
|
||||
thread_id,
|
||||
expected_revision,
|
||||
binding_id,
|
||||
reason,
|
||||
} = params;
|
||||
let interrupt_plan = plan_thread_epiphany_job_interrupt(params);
|
||||
|
||||
let (thread_uuid, thread) = match self.load_epiphany_thread(&request_id, &thread_id).await {
|
||||
let (thread_uuid, thread) = match self
|
||||
.load_epiphany_thread(&request_id, &interrupt_plan.thread_id)
|
||||
.await
|
||||
{
|
||||
Some(thread) => thread,
|
||||
None => return,
|
||||
};
|
||||
|
|
@ -632,9 +632,9 @@ impl CodexMessageProcessor {
|
|||
let host = EpiphanyCodexThreadHost::new(thread.as_ref());
|
||||
let applied = match interrupt_thread_epiphany_job(
|
||||
&host,
|
||||
expected_revision,
|
||||
&binding_id,
|
||||
reason,
|
||||
interrupt_plan.expected_revision,
|
||||
&interrupt_plan.binding_id,
|
||||
interrupt_plan.reason,
|
||||
)
|
||||
.await
|
||||
{
|
||||
|
|
@ -652,28 +652,16 @@ impl CodexMessageProcessor {
|
|||
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 output = thread_epiphany_job_interrupt_output(applied);
|
||||
|
||||
self.outgoing
|
||||
.send_response(
|
||||
request_id,
|
||||
ThreadEpiphanyJobInterruptResponse {
|
||||
cancel_requested: applied.cancel_requested,
|
||||
interrupted_thread_ids: applied.interrupted_thread_ids.clone(),
|
||||
revision: applied.revision,
|
||||
changed_fields: protocol_changed_fields,
|
||||
epiphany_state: epiphany_state.clone(),
|
||||
job: protocol_job_from_surface(applied.job, None, None),
|
||||
},
|
||||
)
|
||||
.send_response(request_id, output.response)
|
||||
.await;
|
||||
self.send_epiphany_state_updated(
|
||||
thread_uuid,
|
||||
ThreadEpiphanyStateUpdatedSource::JobInterrupt,
|
||||
changed_fields,
|
||||
epiphany_state,
|
||||
output.changed_fields,
|
||||
output.epiphany_state,
|
||||
)
|
||||
.await;
|
||||
}
|
||||
|
|
|
|||
Reference in a new issue