From 261ed6e8557ded1e431ff80957a91d7f769dd266 Mon Sep 17 00:00:00 2001 From: Tuck Date: Sun, 9 Aug 2026 20:52:39 -0400 Subject: [PATCH] fix: stamp timestamps in model_metadata, turn_finalizer --- agent/model_metadata.py | 3 ++- agent/turn_finalizer.py | 10 +++++++++- 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/agent/model_metadata.py b/agent/model_metadata.py index 9a44b2c9bc..c3a0fc4b41 100644 --- a/agent/model_metadata.py +++ b/agent/model_metadata.py @@ -24,6 +24,7 @@ if TYPE_CHECKING: # pragma: no cover โ€” runtime import is lazy (see below) from utils import atomic_json_write, atomic_yaml_write, base_url_host_matches, base_url_hostname from hermes_constants import OPENROUTER_MODELS_URL +from agent.message_metadata import PERSISTENCE_ONLY_MESSAGE_FIELDS logger = logging.getLogger(__name__) @@ -3350,7 +3351,7 @@ def _wire_message_shadow(msg: Dict[str, Any]) -> Dict[str, Any]: ) shadow: Dict[str, Any] = {} for k, v in msg.items(): - if k in ("_anthropic_content_blocks", "reasoning_details"): + if k in ("_anthropic_content_blocks", "reasoning_details") or k in PERSISTENCE_ONLY_MESSAGE_FIELDS: continue if k == "api_content": # Always popped before the request is built; only counted when it diff --git a/agent/turn_finalizer.py b/agent/turn_finalizer.py index 7297c7edca..402e1b6a5d 100644 --- a/agent/turn_finalizer.py +++ b/agent/turn_finalizer.py @@ -26,6 +26,7 @@ import os from agent.codex_responses_adapter import _summarize_user_message_for_log from agent.message_content import flatten_message_text +from agent.message_metadata import append_message, stamp_message_timestamp from agent.message_sanitization import _sanitize_surrogates @@ -315,7 +316,10 @@ def finalize_turn( if _tail_role != "assistant": # Tail is not an assistant row โ€” append the final response # so the durable turn closes with the answer (#43849/#44100). - messages.append({"role": "assistant", "content": final_response}) + append_message( + messages, + {"role": "assistant", "content": final_response}, + ) elif isinstance(_tail, dict) and _tail.get("content") != final_response and _is_pure_tool_call_tail(_tail): # The tail IS an assistant row, but a *pure tool-call turn*: # tool_calls with no text of its own. The role check alone @@ -332,6 +336,10 @@ def finalize_turn( # candidate collapse โ€” the provisional answer was persisted and # reused as the terminal response, #65919 ยง7). _tail["content"] = final_response + # The normal assistant builder already stamps this row. Cover + # legacy/exceptional pure-tool tails before they become a + # delivered final response. + stamp_message_timestamp(_tail) # The row may have already been flushed to SQLite by the # incremental tool-call persist (conversation_loop.py:4990), # which stamps ``_DB_PERSISTED_MARKER`` so subsequent flushes