fix: TaskStore snapshot を compact 後 history の末尾に置いて retained 中の TaskCreate 重複を防ぐ
This commit is contained in:
parent
6f2aca84bf
commit
ceafff92b6
|
|
@ -1395,7 +1395,12 @@ impl<C: LlmClient, St: Store> Pod<C, St> {
|
|||
.filter(|i| i.is_user_message())
|
||||
.count();
|
||||
|
||||
// Build new history: [summary, ...auto-read, task snapshot, TaskList result, references, ...retained].
|
||||
// Build new history: [summary, ...auto-read, references, ...retained, task snapshot, TaskList synthetic call/result].
|
||||
// The TaskStore snapshot trails the retained items so that, on resume,
|
||||
// `replay_history` walks any pre-compact Task* calls preserved verbatim
|
||||
// in retained_items first and the trailing snapshot's `replace_with`
|
||||
// is the final word — pre-compact `TaskCreate` calls cannot leak as
|
||||
// duplicate entries.
|
||||
let mut new_history = Vec::with_capacity(
|
||||
1 + auto_read_messages.len()
|
||||
+ 3
|
||||
|
|
@ -1406,6 +1411,10 @@ impl<C: LlmClient, St: Store> Pod<C, St> {
|
|||
"[Compacted context summary]\n\n{summary_text}"
|
||||
)));
|
||||
new_history.extend(auto_read_messages);
|
||||
if let Some(msg) = reference_message {
|
||||
new_history.push(msg);
|
||||
}
|
||||
new_history.extend(retained_items);
|
||||
new_history.push(Item::system_message(format!(
|
||||
"[Session TaskStore snapshot]\n\n{task_snapshot_text}\n\n\
|
||||
This is the complete session task list preserved across compaction. \
|
||||
|
|
@ -1417,10 +1426,6 @@ impl<C: LlmClient, St: Store> Pod<C, St> {
|
|||
tools::task::snapshot_overview(&self.task_store.list()),
|
||||
task_snapshot_text.clone(),
|
||||
));
|
||||
if let Some(msg) = reference_message {
|
||||
new_history.push(msg);
|
||||
}
|
||||
new_history.extend(retained_items);
|
||||
|
||||
// Persist as a new compacted session.
|
||||
let old_session_id = self.session_id;
|
||||
|
|
|
|||
|
|
@ -609,4 +609,37 @@ mod tests {
|
|||
assert_eq!(tasks[1].taskid, 2);
|
||||
assert_eq!(tasks[1].subject, "new");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn trailing_snapshot_supersedes_pre_compact_taskcreates_in_retained() {
|
||||
// Mirrors the post-compact layout: pre-compact `TaskCreate` calls are
|
||||
// preserved verbatim in retained_items, and the snapshot trails them.
|
||||
// The trailing snapshot must reset the store to the captured state so
|
||||
// pre-compact `TaskCreate`s do not surface as duplicates.
|
||||
let snapshot = "[Session TaskStore snapshot]\n\nTaskStore: 2 task(s) (pending: 0, inprogress: 1, completed: 1, deleted: 0)\n\n- taskid: 1\n status: completed\n subject: A\n description: A-desc\n\n- taskid: 2\n status: inprogress\n subject: B\n description: B-desc\n";
|
||||
let history = vec![
|
||||
Item::tool_call("c1", "TaskCreate", r#"{"subject":"A","description":"A-desc"}"#),
|
||||
Item::tool_call("u1", "TaskUpdate", r#"{"taskid":1,"status":"completed"}"#),
|
||||
Item::tool_call("c2", "TaskCreate", r#"{"subject":"B","description":"B-desc"}"#),
|
||||
Item::tool_call("u2", "TaskUpdate", r#"{"taskid":2,"status":"inprogress"}"#),
|
||||
Item::system_message(snapshot),
|
||||
Item::tool_call("compact-tasklist", "TaskList", "{}"),
|
||||
Item::tool_call(
|
||||
"c3",
|
||||
"TaskCreate",
|
||||
r#"{"subject":"C","description":"after compact"}"#,
|
||||
),
|
||||
];
|
||||
let store = TaskStore::from_history(&history);
|
||||
let tasks = store.list();
|
||||
assert_eq!(tasks.len(), 3);
|
||||
assert_eq!(tasks[0].taskid, 1);
|
||||
assert_eq!(tasks[0].subject, "A");
|
||||
assert_eq!(tasks[0].status, TaskStatus::Completed);
|
||||
assert_eq!(tasks[1].taskid, 2);
|
||||
assert_eq!(tasks[1].subject, "B");
|
||||
assert_eq!(tasks[1].status, TaskStatus::Inprogress);
|
||||
assert_eq!(tasks[2].taskid, 3);
|
||||
assert_eq!(tasks[2].subject, "C");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user