fix: preserve role-neutral input policy across compaction and replay
This commit is contained in:
@@ -212,7 +212,7 @@ fn message(id: &str, text: &str) -> session_store::LoggedHistoryEntry {
|
||||
content: vec![session_store::LoggedContentPart::Text { text: text.into() }],
|
||||
},
|
||||
metadata: session_store::LoggedSessionHistoryMetadata {
|
||||
system_input: None,
|
||||
input_metadata: None,
|
||||
entry_id: session_store::LoggedSessionHistoryEntryId(id.into()),
|
||||
origin: session_store::LoggedSessionHistoryOrigin::LocalHumanInput,
|
||||
derivation: None,
|
||||
|
||||
@@ -215,6 +215,7 @@ async fn public_mid_turn_compactions_preserve_originals_and_drain_after_cancel_a
|
||||
}
|
||||
protocol::Event::TextDelta { text } if text.contains(PARTIAL) => break,
|
||||
protocol::Event::RunEnd { .. } => panic!("dialog ended before cancellation"),
|
||||
protocol::Event::SessionEntryCommitted { entry } if matches!(entry.data, protocol::SessionSnapshotEntryData::RunError { .. }) => panic!("dialog failed before cancellation: {entry:?}"),
|
||||
protocol::Event::Error { code, message } => panic!("{code:?}: {message}"),
|
||||
_ => {}
|
||||
}
|
||||
@@ -294,6 +295,32 @@ async fn public_mid_turn_compactions_preserve_originals_and_drain_after_cancel_a
|
||||
.iter()
|
||||
.flat_map(|segment| source_store.read_all(session, *segment).unwrap())
|
||||
.collect();
|
||||
for history in raw.iter().filter_map(|record| match record {
|
||||
session_store::LogEntry::AnnotatedSegmentStart { history, compacted_from: Some(_), .. } => Some(history),
|
||||
_ => None,
|
||||
}) {
|
||||
let mut calls = std::collections::HashSet::new();
|
||||
let mut results = 0;
|
||||
for entry in history {
|
||||
let is_tool = matches!(entry.item, session_store::LoggedItem::ToolCall { .. } | session_store::LoggedItem::ToolResult { .. });
|
||||
let Some(metadata) = entry.metadata.input_metadata.as_ref().filter(|_| is_tool) else { continue; };
|
||||
assert_eq!(metadata.usage().recording_material, session_store::InputEvidenceUse::Exclude);
|
||||
assert_eq!(metadata.usage().memory_evidence, session_store::InputEvidenceUse::Exclude);
|
||||
assert_eq!(metadata.usage().observation, session_store::InputObservationUse::Hidden);
|
||||
assert!(metadata.public_item().is_none());
|
||||
assert!(entry.metadata.derivation.is_some());
|
||||
match &entry.item {
|
||||
session_store::LoggedItem::ToolCall { call_id, .. } => { calls.insert(call_id.clone()); }
|
||||
session_store::LoggedItem::ToolResult { call_id, .. } => {
|
||||
assert!(calls.contains(call_id), "derived Tool result must follow its real logical call");
|
||||
results += 1;
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
assert!(!calls.is_empty());
|
||||
assert!(results >= calls.len());
|
||||
}
|
||||
assert_eq!(
|
||||
raw.iter()
|
||||
.filter(|entry| matches!(
|
||||
|
||||
@@ -11,7 +11,7 @@ use agen::llm_client::{
|
||||
types::Item,
|
||||
};
|
||||
use futures::Stream;
|
||||
use session_store::{CombinedStore, FsWorkerStore, LogEntry, LoggedSessionHistoryOrigin};
|
||||
use session_store::{CombinedStore, FsWorkerStore, LogEntry};
|
||||
use std::pin::Pin;
|
||||
|
||||
const BEFORE: &str = "FROZEN_POSITION: B partially implemented; tests not run; result unconfirmed";
|
||||
@@ -139,14 +139,14 @@ async fn local_host_compaction_freezes_pending_journal_through_request_and_durab
|
||||
let worker = prepared.worker();
|
||||
assert_ne!(worker.segment_id(), source_segment);
|
||||
let seed = worker.store().read_all(worker.session_id(), worker.segment_id()).unwrap().into_iter().find_map(|entry| match entry { LogEntry::AnnotatedSegmentStart { history, .. } => Some(history), _ => None }).expect("durable compacted seed");
|
||||
let journal_items: Vec<_> = seed.iter().filter(|entry| entry.metadata.origin == LoggedSessionHistoryOrigin::JournalContext).collect();
|
||||
let journal_items: Vec<_> = seed.iter().filter(|entry| entry.metadata.input_metadata.as_ref().is_some_and(|input| input.usage().compaction == session_store::InputCompactionUse::ReferenceInput)).collect();
|
||||
assert_eq!(journal_items.len(), 1);
|
||||
let journal_item = Item::from(journal_items[0].item.clone());
|
||||
let body = journal_item.as_text().unwrap();
|
||||
assert!(body.contains(&expected_json), "durable seed contains the full selected JSON and processing checkpoint");
|
||||
assert!(body.contains(BEFORE));
|
||||
assert!(!body.contains(AFTER));
|
||||
assert!(seed.iter().any(|entry| entry.metadata.origin == LoggedSessionHistoryOrigin::CompactionSummary && Item::from(entry.item.clone()).as_text().is_some_and(|text| text.contains(COMPLEMENT))));
|
||||
assert!(seed.iter().any(|entry| entry.metadata.input_metadata.as_ref().is_some_and(|input| input.usage().compaction == session_store::InputCompactionUse::OpeningSummary) && Item::from(entry.item.clone()).as_text().is_some_and(|text| text.contains(COMPLEMENT))));
|
||||
let requests = model.requests.lock().unwrap();
|
||||
let input = requests[0].items.iter().find_map(|item| match item {
|
||||
Item::Message { role: agen::llm_client::types::Role::User, .. } => item.as_text().filter(|text| text.contains("## Conversation overview/index")),
|
||||
|
||||
@@ -79,7 +79,7 @@ async fn recorded_idle_body_handoff_preserves_open_after_source_deletion_and_see
|
||||
let sessions = session_store::FsStore::new_with_retention(workers.sessions_dir(second.worker_id()), crate::store::session_retention(&state)).unwrap();
|
||||
let pointer = session_store::FsWorkerStore::new(workers.worker_metadata_dir(second.worker_id())).unwrap().read_by_name(&second.record().storage_key).unwrap().unwrap().active.unwrap();
|
||||
let entries = sessions.read_all(session, pointer.segment_id.unwrap()).unwrap();
|
||||
let journal_items = entries.iter().filter(|entry| matches!(entry, session_store::LogEntry::AnnotatedSystemItem { entry, .. } if entry.metadata.system_input.as_ref().and_then(|input| input.feature_payload()).is_some_and(|payload| payload.schema() == "journal:startup"))).count();
|
||||
let journal_items = entries.iter().filter(|entry| matches!(entry, session_store::LogEntry::AnnotatedSystemItem { entry, .. } if entry.metadata.input_metadata.as_ref().and_then(|input| input.feature_payload()).is_some_and(|payload| payload.schema() == "journal:startup"))).count();
|
||||
assert_eq!(journal_items, 1);
|
||||
assert_eq!(catalog.store.subject(&subject.id).unwrap().unwrap().behavior_md, "BEHAVIOR_ORIGINAL_UNCHANGED");
|
||||
second.shutdown().await.unwrap();
|
||||
|
||||
@@ -1621,7 +1621,7 @@ allow = []
|
||||
let history = |id: &str, item: LoggedItem| LoggedHistoryEntry {
|
||||
item,
|
||||
metadata: LoggedSessionHistoryMetadata {
|
||||
system_input: None,
|
||||
input_metadata: None,
|
||||
entry_id: LoggedSessionHistoryEntryId(id.into()),
|
||||
origin: LoggedSessionHistoryOrigin::BackendInstruction { operation_id: None },
|
||||
derivation: None,
|
||||
|
||||
Reference in New Issue
Block a user