From a9f94022b02fde606cccceb30eeeb32accfe2d37 Mon Sep 17 00:00:00 2001 From: Erosika Date: Thu, 16 Jul 2026 12:38:48 -0400 Subject: [PATCH] fix(agent): bind finalize_turn at import time MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The function-scoped import at the end of run_conversation loads agent.turn_finalizer fresh from disk on the first turn that reaches it. On a source/editable install whose checkout changed mid-session, that pairs an old caller with a new callee at the exact seam where every turn's work is persisted — the turn crashes on a signature mismatch after the work is done. The lazy import was never cycle-forced: turn_finalizer defers its own conversation_loop import. --- agent/conversation_loop.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/agent/conversation_loop.py b/agent/conversation_loop.py index 663e130f08c4f..57b5f13268db7 100644 --- a/agent/conversation_loop.py +++ b/agent/conversation_loop.py @@ -86,6 +86,11 @@ from agent.retry_utils import ( zai_coding_overload_retry_ceiling, ) from agent.trajectory import has_incomplete_scratchpad +# Bound at import time deliberately: a lazy end-of-turn import reads the +# module fresh from disk, so a mid-session git switch/pull in a source +# checkout loads a version-skewed finalizer and kills the finished turn. +# Safe to hoist: turn_finalizer defers its own conversation_loop import. +from agent.turn_finalizer import finalize_turn from agent.usage_pricing import estimate_usage_cost, normalize_usage from hermes_constants import PARTIAL_STREAM_STUB_ID from hermes_logging import set_session_context @@ -7513,7 +7518,6 @@ def run_conversation( # Post-loop turn finalization extracted to agent/turn_finalizer.finalize_turn # (god-file decomposition Phase 1 step 4). Behavior-neutral: the assembled # result dict is returned exactly as before. - from agent.turn_finalizer import finalize_turn return finalize_turn( agent, final_response=final_response,