Skip to content

Commit 4f57db4

Browse files
committed
fix: preserve tool-call history across thread hydration (#568)
Prevent model re-attempts and data inconsistencies when rebuilding conversation context from persisted tool-call records. - Remove raw tool parameters from persisted tool_calls JSON to prevent unredacted sensitive data from being stored in the database. The LLM context rebuild only needs call_id + name + result. - Make record_tool_error/record_tool_result mutually exclusive in all three execution paths (dispatcher, approval, deferred). Previously error cases called both methods, violating the TurnToolCall invariant and sending contradictory outcomes to the LLM. - Unify call_id format to turn{N}_{i} between live sessions and persisted hydration to eliminate ID mismatch in the LLM context. - Auto-close </tool_output> XML tags after truncate_preview truncation to prevent malformed tool output reaching the LLM. [skip-regression-check]
1 parent 652f30a commit 4f57db4

File tree

4 files changed

+666
-87
lines changed

4 files changed

+666
-87
lines changed

src/agent/dispatcher.rs

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -743,23 +743,6 @@ impl Agent {
743743
.await;
744744
}
745745

746-
// Record result in thread
747-
{
748-
let mut sess = session.lock().await;
749-
if let Some(thread) = sess.threads.get_mut(&thread_id)
750-
&& let Some(turn) = thread.last_turn_mut()
751-
{
752-
match &tool_result {
753-
Ok(output) => {
754-
turn.record_tool_result(serde_json::json!(output));
755-
}
756-
Err(e) => {
757-
turn.record_tool_error(e.to_string());
758-
}
759-
}
760-
}
761-
}
762-
763746
// Check for auth awaiting — defer the return
764747
// until all results are recorded.
765748
if deferred_auth.is_none()
@@ -799,6 +782,7 @@ impl Agent {
799782
}
800783

801784
// Sanitize and add tool result to context
785+
let is_tool_error = tool_result.is_err();
802786
let result_content = match tool_result {
803787
Ok(output) => {
804788
let sanitized =
@@ -812,6 +796,23 @@ impl Agent {
812796
Err(e) => format!("Tool '{}' failed: {}", tc.name, e),
813797
};
814798

799+
// Record sanitized result in thread so messages()
800+
// and persist_tool_calls() use cleaned content.
801+
{
802+
let mut sess = session.lock().await;
803+
if let Some(thread) = sess.threads.get_mut(&thread_id)
804+
&& let Some(turn) = thread.last_turn_mut()
805+
{
806+
if is_tool_error {
807+
turn.record_tool_error(result_content.clone());
808+
} else {
809+
turn.record_tool_result(serde_json::json!(
810+
result_content
811+
));
812+
}
813+
}
814+
}
815+
815816
context_messages.push(ChatMessage::tool_result(
816817
&tc.id,
817818
&tc.name,

0 commit comments

Comments
 (0)