use super::{SessionActor, SessionCommand, SessionError, SessionEventKind, SessionHandle};
use bcode_session_models::{
SessionMilestoneEvaluationCompletion, SessionMilestoneEvaluationRequest, SessionMilestonePage,
SessionMilestonePublicationRequest, SessionMilestoneQuery,
};
impl SessionHandle {
pub async fn milestone_context(
&self,
max_bytes: usize,
max_turns: usize,
) -> Result<bcode_session_models::SessionMilestoneContext, SessionError> {
self.send(|reply| SessionCommand::MilestoneContext {
max_bytes,
max_turns,
reply,
})
.await?
}
pub async fn milestone_evaluation_status(
&self,
evaluation_id: &str,
) -> Result<Option<bcode_session_models::SessionMilestoneEvaluationStatus>, SessionError> {
self.send(|reply| SessionCommand::MilestoneEvaluationStatus {
evaluation_id: evaluation_id.into(),
reply,
})
.await?
}
pub async fn milestone_evaluation_checkpoint(
&self,
) -> Result<Option<bcode_session_models::SessionMilestoneEvaluationStatus>, SessionError> {
self.send(|reply| SessionCommand::MilestoneEvaluationCheckpoint { reply })
.await?
}
pub async fn request_milestone_evaluation(
&self,
request: SessionMilestoneEvaluationRequest,
) -> Result<bool, SessionError> {
self.send(|reply| SessionCommand::RequestMilestoneEvaluation { request, reply })
.await?
}
pub async fn publish_milestone(
&self,
request: SessionMilestonePublicationRequest,
) -> Result<(), SessionError> {
self.send(|reply| SessionCommand::PublishMilestone { request, reply })
.await?
}
pub async fn complete_milestone_evaluation(
&self,
completion: SessionMilestoneEvaluationCompletion,
) -> Result<(), SessionError> {
self.send(|reply| SessionCommand::CompleteMilestoneEvaluation { completion, reply })
.await?
}
pub async fn milestones(
&self,
query: SessionMilestoneQuery,
) -> Result<SessionMilestonePage, SessionError> {
self.send(|reply| SessionCommand::Milestones { query, reply })
.await?
}
}
impl SessionActor {
pub(super) async fn milestone_context(
&mut self,
max_bytes: usize,
max_turns: usize,
) -> Result<bcode_session_models::SessionMilestoneContext, SessionError> {
let id = self.state.summary.id;
let db = self
.existing_session_db()
.await?
.ok_or(SessionError::NotFound(id))?;
db.validate_read_readiness().await?;
Ok(db.milestone_context(max_bytes, max_turns).await?)
}
pub(super) async fn milestone_evaluation_status(
&mut self,
evaluation_id: &str,
) -> Result<Option<bcode_session_models::SessionMilestoneEvaluationStatus>, SessionError> {
let id = self.state.summary.id;
let db = self
.existing_session_db()
.await?
.ok_or(SessionError::NotFound(id))?;
Ok(db.milestone_evaluation_status(evaluation_id).await?)
}
pub(super) async fn milestone_evaluation_checkpoint(
&mut self,
) -> Result<Option<bcode_session_models::SessionMilestoneEvaluationStatus>, SessionError> {
let id = self.state.summary.id;
let db = self
.existing_session_db()
.await?
.ok_or(SessionError::NotFound(id))?;
Ok(db.milestone_evaluation_checkpoint().await?)
}
pub(super) async fn request_milestone_evaluation(
&mut self,
request: SessionMilestoneEvaluationRequest,
) -> Result<bool, SessionError> {
self.ensure_ownership()?;
let db = self.session_db_for_write().await?;
if let Some((_, existing, _)) = db.milestone_evaluation(&request.evaluation_id).await? {
return if existing == request {
Ok(false)
} else {
Err(SessionError::EventSerialization(
"conflicting milestone evaluation identity".into(),
))
};
}
self.validate_append_generation(Some(request.expected_generation))?;
if self.state.summary.explicit_name != request.expected_explicit_name {
return Err(SessionError::EventSerialization(
"explicit name changed before milestone admission".into(),
));
}
self.append_event(
SessionEventKind::MilestoneEvaluationRequested { request },
None,
crate::current_unix_millis(),
)
.await?;
Ok(true)
}
pub(super) async fn publish_milestone(
&mut self,
request: SessionMilestonePublicationRequest,
) -> Result<(), SessionError> {
self.ensure_ownership()?;
let db = self.session_db_for_write().await?;
let id = &request.milestone.evaluation_id;
let (admitted, evaluation, completed) =
db.milestone_evaluation(id).await?.ok_or_else(|| {
SessionError::EventSerialization("unknown milestone evaluation".into())
})?;
if let Some(existing) = db.milestone_publication(id).await? {
if existing != request {
return Err(SessionError::EventSerialization(
"conflicting milestone publication including title".into(),
));
}
} else {
if completed {
return Err(SessionError::EventSerialization(
"evaluation already completed".into(),
));
}
self.append_event(
SessionEventKind::MilestonePublicationRequested {
request: request.clone(),
},
None,
crate::current_unix_millis(),
)
.await?;
}
let db = self.session_db_for_write().await?;
if let Some(existing) = db.milestone_by_evaluation(id).await? {
if existing != request.milestone {
return Err(SessionError::EventSerialization(
"conflicting milestone publication".into(),
));
}
if completed {
return Ok(());
}
} else {
if completed {
return Err(SessionError::EventSerialization(
"evaluation already completed without milestone".into(),
));
}
self.append_event(
SessionEventKind::MilestonePublished {
milestone: request.milestone.clone(),
},
None,
crate::current_unix_millis(),
)
.await?;
}
if let Some(title) = request.automatic_title
&& self.state.summary.explicit_name.is_none()
&& evaluation.expected_explicit_name.is_none()
&& !self
.session_db_for_write()
.await?
.milestone_title_already_published(admitted)
.await?
&& self
.session_db_for_write()
.await?
.milestone_title_fence(admitted)
.await?
{
self.append_event(
SessionEventKind::AutomaticTitleUpdated {
evaluation_id: id.clone(),
title,
},
None,
crate::current_unix_millis(),
)
.await?;
}
self.complete_milestone_evaluation(SessionMilestoneEvaluationCompletion {
evaluation_id: id.clone(),
})
.await
}
pub(super) async fn complete_milestone_evaluation(
&mut self,
completion: SessionMilestoneEvaluationCompletion,
) -> Result<(), SessionError> {
self.ensure_ownership()?;
let db = self.session_db_for_write().await?;
if let Some((_, _, true)) = db.milestone_evaluation(&completion.evaluation_id).await? {
return Ok(());
}
self.append_event(
SessionEventKind::MilestoneEvaluationCompleted { completion },
None,
crate::current_unix_millis(),
)
.await?;
Ok(())
}
pub(super) async fn milestones(
&mut self,
query: SessionMilestoneQuery,
) -> Result<SessionMilestonePage, SessionError> {
let session_id = self.state.summary.id;
let db = self
.existing_session_db()
.await?
.ok_or(SessionError::NotFound(session_id))?;
Ok(db.milestones(query).await?)
}
}