use std::collections::BTreeMap;
use std::sync::Arc;
use async_trait::async_trait;
use super::types::{
AgentBuildContext, AgentBuildDraft, AgentIdentity, CheckpointVersion, ContinuityGeneration,
ContinuityRecord, ContinuityResolveState, ContinuityStoreError, CustomizerError,
DurableAgentSpec, FencingToken, LeaseAcquireResult, LeaseError, LeaseGrant, LeaseRenewResult,
ManagedPeerEdge, RosterContext, RosterError, SessionSnapshot, TopologyContext, TopologyError,
};
use crate::mob_handle_runtime::SessionCreatedContext;
#[derive(Debug, Clone)]
pub struct SessionSnapshotMatchCandidate {
pub identity: AgentIdentity,
pub session_id: meerkat_core::types::SessionId,
pub generation: ContinuityGeneration,
pub checkpoint_version: CheckpointVersion,
pub fencing_token: FencingToken,
pub snapshot: Arc<SessionSnapshot>,
}
#[async_trait]
pub trait ContinuityStore: Send + Sync {
async fn resolve_many(
&self,
identities: &[AgentIdentity],
) -> Result<BTreeMap<AgentIdentity, ContinuityResolveState>, ContinuityStoreError>;
async fn load_session_snapshot(
&self,
session_id: &meerkat_core::types::SessionId,
) -> Result<Option<SessionSnapshot>, ContinuityStoreError>;
async fn session_snapshot_matches_current(
&self,
_candidate: SessionSnapshotMatchCandidate,
) -> Result<bool, ContinuityStoreError> {
Ok(false)
}
async fn delete_session_snapshot_if_current_revision(
&self,
_session_id: &meerkat_core::types::SessionId,
_expected_current_revision: &str,
) -> Result<bool, ContinuityStoreError> {
Ok(false)
}
async fn save_session_snapshot(
&self,
identity: &AgentIdentity,
session_id: &meerkat_core::types::SessionId,
generation: ContinuityGeneration,
version: CheckpointVersion,
fencing_token: FencingToken,
snapshot: &SessionSnapshot,
) -> Result<(), ContinuityStoreError>;
async fn save_session_snapshot_owned(
&self,
identity: AgentIdentity,
session_id: meerkat_core::types::SessionId,
generation: ContinuityGeneration,
version: CheckpointVersion,
fencing_token: FencingToken,
snapshot: SessionSnapshot,
) -> Result<(), ContinuityStoreError> {
self.save_session_snapshot(
&identity,
&session_id,
generation,
version,
fencing_token,
&snapshot,
)
.await
}
async fn upsert_continuity_record(
&self,
record: &ContinuityRecord,
fencing_token: FencingToken,
) -> Result<(), ContinuityStoreError>;
async fn rollback_continuity_record(
&self,
expected_attempt: &ContinuityRecord,
previous: Option<&ContinuityRecord>,
fencing_token: FencingToken,
) -> Result<(), ContinuityStoreError> {
let resolved = self
.resolve_many(std::slice::from_ref(&expected_attempt.identity))
.await?;
let Some(ContinuityResolveState::Ready { record: current }) =
resolved.get(&expected_attempt.identity)
else {
return Err(ContinuityStoreError::NotFound {
identity: expected_attempt.identity.clone(),
});
};
if current.agent_runtime_id != expected_attempt.agent_runtime_id
|| current.session_id != expected_attempt.session_id
|| current.generation != expected_attempt.generation
{
return Err(ContinuityStoreError::StaleContinuityGeneration {
identity: expected_attempt.identity.clone(),
presented: expected_attempt.generation,
current: current.generation,
});
}
self.delete_continuity_record(&expected_attempt.identity, fencing_token)
.await?;
match previous {
Some(previous) => self.upsert_continuity_record(previous, fencing_token).await,
None => Ok(()),
}
}
async fn delete_continuity_record(
&self,
identity: &AgentIdentity,
fencing_token: FencingToken,
) -> Result<(), ContinuityStoreError>;
fn as_incremental_sessions(&self) -> Option<Arc<dyn ContinuityIncrementalSessions>> {
None
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ContinuityWriteCursor {
pub identity: AgentIdentity,
pub generation: ContinuityGeneration,
pub checkpoint_version: CheckpointVersion,
pub fencing_token: FencingToken,
}
#[async_trait]
pub trait ContinuityIncrementalSessions: Send + Sync {
async fn append_messages(
&self,
cursor: &ContinuityWriteCursor,
id: &meerkat_core::types::SessionId,
strand: &meerkat_core::session_store::TranscriptStrandId,
base_seq: u64,
messages: &[meerkat_core::types::Message],
) -> Result<(), meerkat_core::SessionStoreError>;
async fn commit_rewrite(
&self,
cursor: &ContinuityWriteCursor,
id: &meerkat_core::types::SessionId,
record: &meerkat_core::TranscriptRewriteRecord,
expected: meerkat_core::session_store::SessionHeadCas,
) -> Result<meerkat_core::session_store::SessionHead, meerkat_core::SessionStoreError>;
async fn save_head(
&self,
cursor: &ContinuityWriteCursor,
head: &meerkat_core::session_store::SessionHead,
expected: meerkat_core::session_store::SessionHeadCas,
) -> Result<(), meerkat_core::SessionStoreError>;
async fn load_head(
&self,
id: &meerkat_core::types::SessionId,
) -> Result<Option<meerkat_core::session_store::SessionHead>, meerkat_core::SessionStoreError>;
async fn load_canonical_head(
&self,
id: &meerkat_core::types::SessionId,
) -> Result<Option<meerkat_core::session_store::SessionHead>, meerkat_core::SessionStoreError>;
async fn load_canonical_session(
&self,
id: &meerkat_core::types::SessionId,
) -> Result<Option<meerkat_core::Session>, meerkat_core::SessionStoreError>;
async fn load_canonical_previous(
&self,
id: &meerkat_core::types::SessionId,
) -> Result<
Option<(
meerkat_core::Session,
Vec<meerkat_core::TranscriptRewriteCommit>,
)>,
meerkat_core::SessionStoreError,
>;
async fn load_messages(
&self,
id: &meerkat_core::types::SessionId,
strand: &meerkat_core::session_store::TranscriptStrandId,
range: std::ops::Range<u64>,
) -> Result<Vec<meerkat_core::types::Message>, meerkat_core::SessionStoreError>;
async fn load_rewrites(
&self,
id: &meerkat_core::types::SessionId,
) -> Result<Vec<meerkat_core::TranscriptRewriteRecord>, meerkat_core::SessionStoreError>;
}
#[async_trait]
pub trait LeaseProvider: Send + Sync {
async fn acquire_leases(
&self,
identities: &[AgentIdentity],
runtime_instance: &str,
) -> Result<BTreeMap<AgentIdentity, LeaseAcquireResult>, LeaseError>;
async fn renew_leases(
&self,
grants: &[LeaseGrant],
) -> Result<BTreeMap<AgentIdentity, LeaseRenewResult>, LeaseError>;
async fn release_leases(&self, grants: &[LeaseGrant]) -> Result<(), LeaseError>;
}
#[async_trait]
pub trait RosterProvider: Send + Sync {
async fn roster(&self, context: &RosterContext) -> Result<Vec<DurableAgentSpec>, RosterError>;
}
#[async_trait]
pub trait AgentCustomizer: Send + Sync {
async fn customize_build(
&self,
context: &AgentBuildContext,
spec: &DurableAgentSpec,
draft: &mut AgentBuildDraft,
) -> Result<(), CustomizerError>;
async fn after_create(
&self,
identity: &AgentIdentity,
session_id: &meerkat_core::types::SessionId,
context: &SessionCreatedContext,
) -> Result<(), CustomizerError> {
let _ = (identity, session_id, context);
Ok(())
}
}
#[async_trait]
pub trait TopologyProvider: Send + Sync {
async fn compute_edges(
&self,
target_identities: &[AgentIdentity],
context: &TopologyContext,
) -> Result<Vec<ManagedPeerEdge>, TopologyError>;
}