From ac5e84d45345ea746553dce38c4a39e940a37b4b Mon Sep 17 00:00:00 2001 From: tangtaizhong666 Date: Fri, 12 Jun 2026 09:08:48 +0800 Subject: [PATCH] fix(session): anchor save boundary to prompt prefix size (#4006) build_messages merges the current message into a same-role history tail, shrinking the prompt prefix to 1 + history_count. The save boundary assumed a standalone current message and skipped one message too many, cutting the first new-turn assistant message (with its tool_calls) from persistence while keeping its tool results - producing orphaned tool results in session history. --- nanobot/session/turn_continuation.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/nanobot/session/turn_continuation.py b/nanobot/session/turn_continuation.py index 17c8e237b..6ebfaaa7e 100644 --- a/nanobot/session/turn_continuation.py +++ b/nanobot/session/turn_continuation.py @@ -182,7 +182,15 @@ def _save_skip_for_turn( """Return the persisted-message append boundary for this turn.""" if internal_continuation_inbound(message_metadata): return initial_message_count - return 1 + history_count + (1 if user_persisted_early else 0) + # ``build_messages`` merges the current message into the last history + # entry when both share a role, so the prompt prefix is not always + # ``2 + history_count``. Runner-appended messages always start at + # ``initial_message_count``; only step back when the current message + # exists as a standalone entry that was not persisted early. + has_standalone_current = initial_message_count > 1 + history_count + if has_standalone_current and not user_persisted_early: + return initial_message_count - 1 + return initial_message_count def _goal_continuation_available(