feat: add provenance-aware worker history

This commit is contained in:
2026-08-27 14:54:24 +09:00
parent 116d610ad0
commit e365189276
46 changed files with 2560 additions and 658 deletions
+45 -26
View File
@@ -32,16 +32,29 @@ fn history_from_sink(handle: &WorkerHandle) -> Vec<Item> {
let mut items = Vec::new();
for entry in entries {
match entry {
LogEntry::AnnotatedSegmentStart { history, .. } => {
items.extend(history.into_iter().map(|entry| Item::from(entry.item)));
}
LogEntry::SegmentStart { history, .. } => {
items.extend(history.into_iter().map(Item::from));
}
LogEntry::AnnotatedUserInput { history, .. } => {
items.extend(history.into_iter().map(|entry| Item::from(entry.item)));
}
LogEntry::UserInput { segments, .. } => {
let text = protocol::Segment::flatten_to_text(&segments);
items.push(Item::user_message(text));
}
LogEntry::AnnotatedAssistantItem { entry, .. }
| LogEntry::AnnotatedToolResult { entry, .. } => {
items.push(Item::from(entry.item));
}
LogEntry::AssistantItem { item, .. } | LogEntry::ToolResult { item, .. } => {
items.push(Item::from(item));
}
LogEntry::AnnotatedSystemItem { entry, .. } => {
items.push(entry.item.to_history_item());
}
LogEntry::SystemItem { item, .. } => {
items.push(item.to_history_item());
}
@@ -51,6 +64,14 @@ fn history_from_sink(handle: &WorkerHandle) -> Vec<Item> {
items
}
fn system_item(entry: &LogEntry) -> Option<&session_store::SystemItem> {
match entry {
LogEntry::AnnotatedSystemItem { entry, .. } => Some(&entry.item),
LogEntry::SystemItem { item, .. } => Some(item),
_ => None,
}
}
// ---------------------------------------------------------------------------
// Mock LLM Client
// ---------------------------------------------------------------------------
@@ -192,7 +213,8 @@ async fn make_worker_with_pwd_and_manifest(
let scope = manifest::Scope::writable(&pwd).unwrap();
std::mem::forget(pwd_tmp);
let worker = Engine::new(client);
let worker =
Engine::<_, agen::state::Mutable, worker::SessionHistoryMetadata>::new_annotated(client);
let authority = WorkerFilesystemAuthority::local(pwd.clone(), pwd.clone());
let worker = Worker::new(
manifest,
@@ -804,10 +826,12 @@ async fn snapshot_includes_user_input_for_in_flight_turn() {
// Walk the entries, find a `LogEntry::UserInput` and
// confirm its segments flatten to our submitted text.
let mut found = false;
for value in entries {
for value in &entries {
let entry: session_store::LogEntry =
serde_json::from_value(value).expect("LogEntry deserialise");
if let session_store::LogEntry::UserInput { segments, .. } = entry {
serde_json::from_value(value.clone()).expect("LogEntry deserialise");
if let session_store::LogEntry::UserInput { segments, .. }
| session_store::LogEntry::AnnotatedUserInput { segments, .. } = entry
{
let text = protocol::Segment::flatten_to_text(&segments);
if text == "hello in-flight" {
found = true;
@@ -815,7 +839,10 @@ async fn snapshot_includes_user_input_for_in_flight_turn() {
}
}
}
assert!(found, "snapshot must carry the in-flight UserInput entry");
assert!(
found,
"snapshot must carry the in-flight UserInput entry: {entries:?}"
);
return;
}
Event::Alert(_) => continue,
@@ -1086,7 +1113,7 @@ async fn run_with_paste_segment_inlines_content_and_emits_typed_user_message() {
_ => {}
},
entry = entry_rx.recv() => match entry {
Ok(session_store::LogEntry::UserInput { segments, .. }) => {
Ok(session_store::LogEntry::UserInput { segments, .. } | session_store::LogEntry::AnnotatedUserInput { segments, .. }) => {
user_input_segments = Some(segments);
if saw_turn_end {
break;
@@ -1317,11 +1344,8 @@ async fn notify_while_idle_auto_starts_turn_and_injects_system_message() {
let (entries, _) = handle.sink.subscribe_with_snapshot();
let saw_notify_in_mirror = entries.iter().any(|e| {
matches!(
e,
session_store::LogEntry::SystemItem {
item: session_store::SystemItem::Notification { message, .. },
..
} if message == "turn finished"
system_item(e),
Some(session_store::SystemItem::Notification { message, .. }) if message == "turn finished"
)
});
assert!(
@@ -1463,14 +1487,11 @@ async fn worker_event_turn_ended_while_idle_auto_starts_turn_and_injects_system_
let (entries, _) = handle.sink.subscribe_with_snapshot();
let saw_worker_event_in_mirror = entries.iter().any(|e| {
matches!(
e,
session_store::LogEntry::SystemItem {
item: session_store::SystemItem::WorkerEvent {
event: protocol::WorkerEvent::TurnEnded { worker_name },
..
},
system_item(e),
Some(session_store::SystemItem::WorkerEvent {
event: protocol::WorkerEvent::TurnEnded { worker_name },
..
} if worker_name == "child"
}) if worker_name == "child"
)
});
assert!(
@@ -1552,14 +1573,11 @@ async fn worker_event_scope_sub_delegated_while_idle_stays_control_plane_only()
let (entries, _) = handle.sink.subscribe_with_snapshot();
let saw_scope_event_in_mirror = entries.iter().any(|entry| {
matches!(
entry,
session_store::LogEntry::SystemItem {
item: session_store::SystemItem::WorkerEvent {
event: protocol::WorkerEvent::ScopeSubDelegated { .. },
..
},
system_item(entry),
Some(session_store::SystemItem::WorkerEvent {
event: protocol::WorkerEvent::ScopeSubDelegated { .. },
..
}
})
)
});
assert!(
@@ -2373,7 +2391,8 @@ async fn snapshot_contains_user_input(handle: &WorkerHandle, needle: &str) -> bo
let entry: session_store::LogEntry =
serde_json::from_value(value).expect("LogEntry deserialise");
match entry {
session_store::LogEntry::UserInput { segments, .. } => {
session_store::LogEntry::UserInput { segments, .. }
| session_store::LogEntry::AnnotatedUserInput { segments, .. } => {
protocol::Segment::flatten_to_text(&segments).contains(needle)
}
_ => false,