Move checkpoint latch to bridge
This commit is contained in:
parent
1a9678bb01
commit
0dd4e79349
6 changed files with 86 additions and 60 deletions
61
epiphany-codex-bridge/src/checkpoint.rs
Normal file
61
epiphany-codex-bridge/src/checkpoint.rs
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
#[derive(Default)]
|
||||
pub struct EpiphanyCheckpointInterventionState {
|
||||
last_intervention_turn_id: Option<String>,
|
||||
pending_compaction_turn_id: Option<String>,
|
||||
}
|
||||
|
||||
impl EpiphanyCheckpointInterventionState {
|
||||
pub fn record_intervention(&mut self, turn_id: &str) -> bool {
|
||||
if self.last_intervention_turn_id.as_deref() == Some(turn_id) {
|
||||
return false;
|
||||
}
|
||||
self.last_intervention_turn_id = Some(turn_id.to_string());
|
||||
true
|
||||
}
|
||||
|
||||
pub fn mark_pending_compaction(&mut self, turn_id: &str) {
|
||||
self.pending_compaction_turn_id = Some(turn_id.to_string());
|
||||
}
|
||||
|
||||
pub fn take_pending_compaction(&mut self, turn_id: &str) -> bool {
|
||||
if self.pending_compaction_turn_id.as_deref() != Some(turn_id) {
|
||||
return false;
|
||||
}
|
||||
self.pending_compaction_turn_id = None;
|
||||
true
|
||||
}
|
||||
|
||||
pub fn clear_pending_compaction(&mut self) {
|
||||
self.pending_compaction_turn_id = None;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::EpiphanyCheckpointInterventionState;
|
||||
|
||||
#[test]
|
||||
fn pending_compaction_is_turn_scoped() {
|
||||
let mut state = EpiphanyCheckpointInterventionState::default();
|
||||
|
||||
assert!(state.record_intervention("turn-a"));
|
||||
assert!(!state.record_intervention("turn-a"));
|
||||
assert!(!state.take_pending_compaction("turn-a"));
|
||||
|
||||
state.mark_pending_compaction("turn-a");
|
||||
|
||||
assert!(!state.take_pending_compaction("turn-b"));
|
||||
assert!(state.take_pending_compaction("turn-a"));
|
||||
assert!(!state.take_pending_compaction("turn-a"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pending_compaction_can_be_cleared() {
|
||||
let mut state = EpiphanyCheckpointInterventionState::default();
|
||||
|
||||
state.mark_pending_compaction("turn-a");
|
||||
state.clear_pending_compaction();
|
||||
|
||||
assert!(!state.take_pending_compaction("turn-a"));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,3 +1,4 @@
|
|||
pub mod checkpoint;
|
||||
pub mod context;
|
||||
pub mod coordinator;
|
||||
pub mod invalidation;
|
||||
|
|
|
|||
|
|
@ -797,6 +797,12 @@ request. `epiphany_automation.rs` still gathers live host facts and executes
|
|||
the host side effects: submit `Op::Compact`, call `epiphany_launch_job`, and
|
||||
emit JSON-RPC state-update notifications.
|
||||
|
||||
The pre-compaction checkpoint intervention latch has also left app-server
|
||||
ownership. `epiphany-codex-bridge::checkpoint::EpiphanyCheckpointInterventionState`
|
||||
owns the one-steer-per-turn and same-turn pending-compaction invariant; vendored
|
||||
`ThreadState` only stores and delegates the latch while event handling remains
|
||||
host lifecycle glue.
|
||||
|
||||
The native OpenAI auth spine overcut has been corrected. It no longer owns a
|
||||
clone of Codex keyring/file/env auth or ChatGPT token refresh; it re-exports
|
||||
vendored `codex-login` and carries the Codex workspace tungstenite patches
|
||||
|
|
|
|||
Binary file not shown.
File diff suppressed because one or more lines are too long
|
|
@ -9,6 +9,7 @@ use codex_core::ThreadConfigSnapshot;
|
|||
use codex_protocol::ThreadId;
|
||||
use codex_protocol::protocol::EventMsg;
|
||||
use codex_utils_absolute_path::AbsolutePathBuf;
|
||||
use epiphany_codex_bridge::checkpoint::EpiphanyCheckpointInterventionState;
|
||||
use std::collections::HashMap;
|
||||
use std::collections::HashSet;
|
||||
use std::path::PathBuf;
|
||||
|
|
@ -65,8 +66,7 @@ pub(crate) struct ThreadState {
|
|||
pub(crate) listener_generation: u64,
|
||||
listener_command_tx: Option<mpsc::UnboundedSender<ThreadListenerCommand>>,
|
||||
current_turn_history: ThreadHistoryBuilder,
|
||||
last_epiphany_checkpoint_intervention_turn_id: Option<String>,
|
||||
pending_epiphany_checkpoint_compaction_turn_id: Option<String>,
|
||||
epiphany_checkpoint_intervention: EpiphanyCheckpointInterventionState,
|
||||
listener_thread: Option<Weak<CodexThread>>,
|
||||
}
|
||||
|
||||
|
|
@ -99,7 +99,8 @@ impl ThreadState {
|
|||
}
|
||||
self.listener_command_tx = None;
|
||||
self.current_turn_history.reset();
|
||||
self.pending_epiphany_checkpoint_compaction_turn_id = None;
|
||||
self.epiphany_checkpoint_intervention
|
||||
.clear_pending_compaction();
|
||||
self.listener_thread = None;
|
||||
}
|
||||
|
||||
|
|
@ -130,71 +131,29 @@ impl ThreadState {
|
|||
}
|
||||
|
||||
pub(crate) fn record_epiphany_checkpoint_intervention(&mut self, turn_id: &str) -> bool {
|
||||
if self
|
||||
.last_epiphany_checkpoint_intervention_turn_id
|
||||
.as_deref()
|
||||
== Some(turn_id)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
self.last_epiphany_checkpoint_intervention_turn_id = Some(turn_id.to_string());
|
||||
true
|
||||
self.epiphany_checkpoint_intervention
|
||||
.record_intervention(turn_id)
|
||||
}
|
||||
|
||||
pub(crate) fn mark_epiphany_checkpoint_intervention_pending_compaction(
|
||||
&mut self,
|
||||
turn_id: &str,
|
||||
) {
|
||||
self.pending_epiphany_checkpoint_compaction_turn_id = Some(turn_id.to_string());
|
||||
self.epiphany_checkpoint_intervention
|
||||
.mark_pending_compaction(turn_id);
|
||||
}
|
||||
|
||||
pub(crate) fn take_epiphany_checkpoint_intervention_pending_compaction(
|
||||
&mut self,
|
||||
turn_id: &str,
|
||||
) -> bool {
|
||||
if self
|
||||
.pending_epiphany_checkpoint_compaction_turn_id
|
||||
.as_deref()
|
||||
!= Some(turn_id)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
self.pending_epiphany_checkpoint_compaction_turn_id = None;
|
||||
true
|
||||
self.epiphany_checkpoint_intervention
|
||||
.take_pending_compaction(turn_id)
|
||||
}
|
||||
|
||||
pub(crate) fn clear_epiphany_checkpoint_intervention_pending_compaction(&mut self) {
|
||||
self.pending_epiphany_checkpoint_compaction_turn_id = None;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::ThreadState;
|
||||
|
||||
#[test]
|
||||
fn epiphany_checkpoint_intervention_pending_compaction_is_turn_scoped() {
|
||||
let mut state = ThreadState::default();
|
||||
|
||||
assert!(state.record_epiphany_checkpoint_intervention("turn-a"));
|
||||
assert!(!state.record_epiphany_checkpoint_intervention("turn-a"));
|
||||
assert!(!state.take_epiphany_checkpoint_intervention_pending_compaction("turn-a"));
|
||||
|
||||
state.mark_epiphany_checkpoint_intervention_pending_compaction("turn-a");
|
||||
|
||||
assert!(!state.take_epiphany_checkpoint_intervention_pending_compaction("turn-b"));
|
||||
assert!(state.take_epiphany_checkpoint_intervention_pending_compaction("turn-a"));
|
||||
assert!(!state.take_epiphany_checkpoint_intervention_pending_compaction("turn-a"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn epiphany_checkpoint_intervention_pending_compaction_can_be_cleared() {
|
||||
let mut state = ThreadState::default();
|
||||
|
||||
state.mark_epiphany_checkpoint_intervention_pending_compaction("turn-a");
|
||||
state.clear_epiphany_checkpoint_intervention_pending_compaction();
|
||||
|
||||
assert!(!state.take_epiphany_checkpoint_intervention_pending_compaction("turn-a"));
|
||||
self.epiphany_checkpoint_intervention
|
||||
.clear_pending_compaction();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
Reference in a new issue