fix: recover partial session log writes

This commit is contained in:
2026-08-05 18:15:49 +09:00
parent 36df79e561
commit fd391ef705
10 changed files with 488 additions and 104 deletions
+27 -15
View File
@@ -112,13 +112,14 @@ impl WorkerInterceptor {
/// `Item::system_message`s reach the worker via
/// `ContinueWith` / `pending_history_appends`, so on-disk order
/// matches worker-history order.
fn commit_system_items(&self, items: &[SystemItem]) {
fn commit_system_items(&self, items: &[SystemItem]) -> Result<(), session_store::StoreError> {
let Some(writer) = self.log_writer.as_ref() else {
return;
return Ok(());
};
for item in items {
writer.commit_system_item(item.clone());
writer.commit_system_item(item.clone())?;
}
Ok(())
}
fn current_turn_index(&self) -> usize {
@@ -194,15 +195,17 @@ impl Interceptor for WorkerInterceptor {
// `Item::system_message`s, so on-disk order matches
// worker-history order.
let items: Vec<Item> = extras.iter().map(SystemItem::to_history_item).collect();
self.commit_system_items(&extras);
PromptAction::ContinueWith(items)
match self.commit_system_items(&extras) {
Ok(()) => PromptAction::ContinueWith(items),
Err(error) => PromptAction::Cancel(format!("session persistence failed: {error}")),
}
}
}
async fn pending_history_appends(&self) -> Vec<Item> {
async fn pending_history_appends(&self) -> Result<Vec<Item>, String> {
let drained = self.pending_notifies.drain();
if drained.is_empty() {
return Vec::new();
return Ok(Vec::new());
}
let mut system_items: Vec<SystemItem> = Vec::with_capacity(drained.len());
@@ -231,8 +234,9 @@ impl Interceptor for WorkerInterceptor {
}
}
}
self.commit_system_items(&system_items);
items
self.commit_system_items(&system_items)
.map_err(|error| format!("session persistence failed: {error}"))?;
Ok(items)
}
async fn pre_llm_request(&self, context: &mut Vec<Item>) -> PreRequestAction {
@@ -278,7 +282,9 @@ impl Interceptor for WorkerInterceptor {
let current_tokens = self.estimated_tokens(effective_context.as_ref());
if self.request_threshold_exceeded(current_tokens, effective_context.as_ref()) {
self.commit_system_items(&system_items);
if let Err(error) = self.commit_system_items(&system_items) {
return PreRequestAction::Cancel(format!("session persistence failed: {error}"));
}
return if appended_items.is_empty() {
PreRequestAction::Yield
} else {
@@ -292,8 +298,10 @@ impl Interceptor for WorkerInterceptor {
if system_items.is_empty() {
return PreRequestAction::Continue;
}
self.commit_system_items(&system_items);
PreRequestAction::ContinueWith(appended_items)
match self.commit_system_items(&system_items) {
Ok(()) => PreRequestAction::ContinueWith(appended_items),
Err(error) => PreRequestAction::Cancel(format!("session persistence failed: {error}")),
}
}
async fn pre_tool_call(&self, info: &mut ToolCallInfo) -> PreToolAction {
@@ -451,13 +459,17 @@ mod tests {
}
impl SystemItemCommitter for RecordingSystemItemCommitter {
fn commit_log_entry(&self, entry: session_store::LogEntry) {
fn commit_log_entry(
&self,
entry: session_store::LogEntry,
) -> Result<(), session_store::StoreError> {
if let session_store::LogEntry::SystemItem { item, .. } = entry {
self.committed
.lock()
.expect("committed system-item list poisoned")
.push(item);
}
Ok(())
}
}
@@ -1034,7 +1046,7 @@ mod tests {
None,
);
let items = interceptor.pending_history_appends().await;
let items = interceptor.pending_history_appends().await.unwrap();
assert_eq!(items.len(), 2);
let first = items[0].as_text().unwrap_or_default();
let second = items[1].as_text().unwrap_or_default();
@@ -1048,7 +1060,7 @@ mod tests {
);
// Empty buffer → empty Vec (no synthesised items).
let again = interceptor.pending_history_appends().await;
let again = interceptor.pending_history_appends().await.unwrap();
assert!(again.is_empty());
}