
    Jja\                        d Z ddlmZ ddlZddlZddlZddlmZ ddlm	Z	m
Z
mZmZ ddlmZ ddlmZ ddlmZmZ  ej        e          Zd$dZd%dZe G d d                      Z	 d&d'd#ZdS )(u  Per-turn setup for ``run_conversation`` (the turn prologue).

``run_conversation`` opened with ~470 lines of straight-line setup before the
tool-calling loop ever started: stdio guarding, runtime-main wiring, retry-counter
resets, user-message sanitization, todo/nudge-counter hydration, system-prompt
restore-or-build, crash-resilience persistence, preflight context compression, the
``pre_llm_call`` plugin hook, and external-memory prefetch.

All of that is *prologue* — it runs once per turn, has no back-references into the
loop, and produces a fixed set of values the loop then consumes. ``TurnContext``
captures those produced values; ``build_turn_context`` performs the setup work and
returns one. ``run_conversation`` is left to unpack the context and run the loop,
shrinking the orchestrator by the full prologue.

The builder still mutates ``agent`` heavily (counters, thread id, cached prompt,
session DB) exactly as the inline code did — those side effects are the point. The
``TurnContext`` it returns carries only the *locals* the loop reads back.

Behavior is identical to the original inline prologue; this is a pure
move-and-name refactor with no semantic change.
    )annotationsN)	dataclass)AnyDictListOptional)&conversation_history_after_compression)IterationBudget)estimate_messages_tokens_roughestimate_request_tokens_roughorig_lenintnew_lenorig_tokens
new_tokensreturnboolc                0    || k     rdS |dk    o||dz  k     S )uF  Return ``True`` if a compression pass materially reduced the request.

    Compression can succeed by summarising message contents — reducing the
    estimated request token count — without reducing the message row
    count.  Treating row count as the sole progress signal false-positives
    on size-only wins and surfaces a misleading "Cannot compress further"
    failure even when post-compression tokens are well below the model
    context window.  See issue #39548 for an observed case: 220 → 220
    messages, ~288k → ~183k tokens on a 1M-context model still triggered
    auto-reset.

    The token reduction must be *material* (>5%) to count as progress — the
    same floor the overflow-handler retry path uses (conversation_loop.py,
    #39550) — so a sub-5% wobble doesn't keep the multi-pass loop spinning.
    Tr   gffffff? )r   r   r   r   s       8/home/rurouni/.hermes/hermes-agent/agent/turn_context.py_compression_made_progressr   )   s-    $ t?>zK$,>>>    messagesList[Dict[str, Any]]protect_first_nprotect_last_nthreshold_tokensc                ^    t          |           ||z   dz   k    rdS t          |           |k    S )u  Cheap gate for the (expensive) full preflight token estimate.

    Returns ``True`` when either:
      (a) message count exceeds the protected ranges (the historical gate), or
      (b) a cheap char-based estimate already crosses the configured threshold
          — the few-but-huge case from issue #27405 that the count-only gate
          would silently skip (a handful of very large messages never trips
          the count condition, so compression was never attempted and the
          turn hit a hard context-overflow error).

    Branch (b) uses ``estimate_messages_tokens_rough`` (the shared char-based
    estimator) so a single large base64 image isn't mistaken for ~250K tokens.
    It intentionally undercounts vs. the full request estimate — it omits the
    system prompt and tool schemas — because it is only a *hint* deciding
    whether to pay for the authoritative ``estimate_request_tokens_rough``,
    which (together with ``should_compress``) makes the real decision.
       T)lenr   )r   r   r   r   s       r   _should_run_preflight_estimater!   @   s8    . 8}}7!;;;t)(337GGGr   c                      e Zd ZU dZded<   ded<   ded<   ded	<   d
ed<   ded<   ded<   ded<   dZded<   dZded<   dZded<   dS )TurnContextzCValues produced by the turn prologue and consumed by the turn loop.struser_messager   original_user_messager   r   Optional[List[Dict[str, Any]]]conversation_historyOptional[str]active_system_prompteffective_task_idturn_idr   current_turn_user_idxFr   should_review_memory plugin_user_contextext_prefetch_cacheN)__name__
__module____qualname____doc____annotations__r.   r0   r1   r   r   r   r#   r#   \   s         MM """"8888''''LLL!&&&&&!!!!!       r   r#   r%   r$   system_messager)   r(   r'   task_idpersist_user_messagepersist_user_timestampOptional[float]c                   |	             	 ddl m}  |t          | dd          pdt          | dd          pdt          | dd          pdt          | dd          pdt          | dd          pd	           n# t          $ r Y nw xY w || j                    |t          | d
d                     |                                  	 t          | dd          sddlm}m}  |            r || d           n,# t          $ r t          
                    dd           Y nw xY wt          |t                    r |
|          }t          |t                    r |
|          }|| _        d| _        || _        || _        |pt          t#          j                              }|| _        | j        pd d| dt#          j                    j        dd          }|| _        d| _        d| _        d| _        d| _        d| _        d| _        d| _        d| _        d| _        d| _        d| _         d| _!        | j"        #                                 d| _$        t          | j%        dd          }tM          |          r
 |             d| _'        | j(        dk    r;	 | )                                r| *                    d           n# t          $ r Y nw xY w| j+        r| ,                                 d| _+        t[          | j.                  | _/         ||          }ta          |          dk    r|dd         dz   n|}|1                    dd          }t          2                    d| j        pd| j3        | j4        pd | j5        pd ta          |pg           |           |rtm          |          ng }|r.| j7        8                                s| 9                    |           |rV| j:        dk    rKtw          d! |D                       }|dk    r,|| _:        | j<        dk    r| j=        dk    r|| j<        z  | _=        | xj:        d"z  c_:        d| _>        t          | d#d          }||?                                 t          | d$d          }||?                                 ||n|}d}| j<        dk    r9d%| j@        v r0| j%        r)| xj=        d"z  c_=        | j=        | j<        k    r	d}d| _=        d&|d'}|A                    |           ta          |          d"z
  }|| _        | jB        sC ||          }| C                    d(|dd)          ta          |          d)k    rdnd d*           | jD         || ||           | jD        } | E                                 	 | F                    ||           n4# t          $ r' t          G                    d+| j        pdd           Y nw xY w| jH        rPt          || jJ        jK        | jJ        jL        | jJ        jM                  rt          || pd| jO        pd,          }!| jJ        }"t          |"d-d.           }# |#|!          }$|$s|"jP        }%|%dk    r|!|%k    r|!|"_P         t          |"d/d0                       }&|$r/t          2                    d1|!d2|"jM        d2|"jQ        d2           ny|&rFt          2                    d3t          |&S                    d4d5                    | j        pd           n1|"T                    |!          rt          2                    d6|!d2|"jM        d2| j3        |"jU        d2           | *                    d7|!d2d8|"jM        d2d9           t          d:          D ]}'ta          |          }(|!})| W                    |||!|;          \  }} t          || pd| jO        pd,          }!t          |(ta          |          |)|!          s nKt          | |          }d| _        d| _        d| _        d| _        d| _         |"T                    |!          s nd}*	 dd<lZm[}+  |+d=| j        |||tm          |          t          |           | j3        t          | d>d          pdt          | d?d          pd@
  
        },g }-	 ddAl]m^}.m_}/  |.            }0n# t          $ r d}/d}0Y nw xY w|,D ]}1d}2t          |1t                    r+|1S                    dB          rt          |1dB                   }2n-t          |1t                    r|1a                                r|1}2nq|/H	  |/|2| j        dC|0D          }2n2# t          $ r%}3t          G                    dE|3           Y d}3~3nd}3~3ww xY w|-A                    |2           |-rdFb                    |-          }*n2# t          $ r%}4t          G                    dG|4           Y d}4~4nd}4~4ww xY wi | _c        t                      | _e        d| _f        d| _g        t          ji                    jj        | _k         |            l                    d| jk                   | jm        r+ |            l                    d| jk                   d| _n        nd| _o        d| _n        | jp        rK	 t          |t                    r|nd}5| jp        q                    | j:        |5           n# t          $ r Y nw xY wd}6| jp        rG	 t          |t                    r|nd}7| jp        r                    |7          pd}6n# t          $ r Y nw xY wt          ||||| |||||*|6H          S )Ia  Run the once-per-turn setup and return the loop's input context.

    The callables/helpers the original prologue referenced from the
    ``conversation_loop`` module are passed in explicitly to keep this module
    free of an import cycle with ``agent.conversation_loop``.
    r   )set_runtime_mainproviderr/   modelbase_urlapi_keyapi_mode)r@   rA   rB   _memory_write_originassistant_tool_skip_mcp_refreshF)has_registered_mcp_toolsrefresh_agent_mcp_toolsT)
quiet_modez&between-turns MCP tool refresh skipped)exc_infoNsession:   reset_consolidation_failuresanthropic_messagesu~   🔌 Detected stale connections from a previous provider issue — cleaned up automatically. Proceeding with fresh connection.P   z...
 zPconversation turn: session=%s model=%s provider=%s platform=%s history=%d msg=%rnoneunknownc              3  L   K   | ]}|                     d           dk    dV   dS )roleuserr   N)get).0ms     r   	<genexpr>z%build_turn_context.<locals>.<genexpr>  s?       
 
quuV}}/F/FA/F/F/F/F
 
r   r   _stream_context_scrubber_stream_think_scrubbermemoryrV   )rU   contentu   💬 Starting conversation: '<   'z:Early turn-start session persistence failed for session=%s)system_prompttools$should_defer_preflight_to_real_usagec                    dS )NFr   )_tokenss    r   <lambda>z$build_turn_context.<locals>.<lambda>g  s    E r   'get_active_compression_failure_cooldownc                     d S Nr   r   r   r   rf   z$build_turn_context.<locals>.<lambda>t  s    D r   zpSkipping preflight compression: rough estimate ~%s >= %s, but last real provider prompt was %s after compression,z`Skipping preflight compression: same-session cooldown active (~%s seconds remaining, session %s)remaining_secondsg        zDPreflight compression: ~%s tokens >= %s threshold (model %s, ctx %s)u   📦 Preflight compression: ~z tokens >= z# threshold. This may take a moment.   )approx_tokensr8   )invoke_hookpre_llm_callplatform_user_id)	
session_idr8   r,   r%   r(   is_first_turnr?   rp   	sender_id)get_spill_configspill_if_oversizedcontextzplugin hook)rr   sourceconfigzhook context spill failed: %sz

zpre_llm_call hook failed: %s)r%   r&   r   r(   r*   r+   r,   r-   r.   r0   r1   )tagent.auxiliary_clientr=   getattr	Exceptionrr   _restore_primary_runtimetools.mcp_toolrF   rG   loggerdebug
isinstancer$   _stream_callback_persist_user_message_idx_persist_user_message_override_persist_user_message_timestampuuiduuid4_current_task_idhex_current_turn_id_current_api_request_id_invalid_tool_retries_invalid_json_retries_empty_content_retries_incomplete_scratchpad_retries_codex_incomplete_retries_thinking_prefill_retries_post_tool_empty_retried_last_content_with_tools$_last_content_tools_all_housekeeping_mute_post_response_unicode_sanitization_passes_tool_guardrailsreset_for_turn_tool_guardrail_halt_decision_memory_storecallable_vision_supportedrB   _cleanup_dead_connections_emit_status_compression_warning_replay_compression_warningr
   max_iterationsiteration_budgetr    replaceinfor?   r>   rp   list_todo_store	has_items_hydrate_todo_store_user_turn_countsum_memory_nudge_interval_turns_since_memory_is_user_initiated_turnresetvalid_tool_namesappendrH   _safe_print_cached_system_prompt_ensure_db_session_persist_sessionwarningcompression_enabledr!   context_compressorr   r   r   r   rb   last_prompt_tokenslast_real_prompt_tokensr   rW   should_compresscontext_lengthrange_compress_contextr   r	   hermes_cli.pluginsrn   r   tools.hook_output_spillru   rv   dictstripjoin_turn_failed_file_mutationsset_turn_file_mutation_paths_verification_stop_nudges_pre_verify_nudges	threadingcurrent_threadident_execution_thread_id_set_interrupt_interrupt_requested _interrupt_thread_signal_pending_interrupt_message_memory_manageron_turn_startprefetch_allr#   )8agentr%   r7   r(   r8   stream_callbackr9   r:   restore_or_build_system_promptinstall_safe_stdiosanitize_surrogatessummarize_user_message_for_logset_session_contextset_current_write_originrar=   rF   rG   r+   r,   _reset_consol_preview_text_msg_previewr   prior_user_turnsscrubberthink_scrubberr&   r.   user_msgr-   _print_previewr*   _preflight_tokens_compressor_defer_preflight_preflight_deferred_last_compression_cooldown_pass	_orig_len_orig_tokensr0   _invoke_hook_pre_results
_ctx_parts
_spill_cfg_spill_if_oversized_spill_config_cachedr_piece
_spill_excexc	_turn_msgr1   _querys8                                                           r   build_turn_contextr   w   s    2 
;;;;;;E:r**0bE7B''-2UJ339rE9b117RUJ339r	
 	
 	
 	
 	
     ())) WU,BDTUUVVV 
""$$$Nu1599 	@XXXXXXXX'')) @''$???? N N N=MMMMMN ,$$ 9**<88&,, I223GHH -E&*E#+?E(,BE)43tz||#4#4.E!.Y[[1B[[TZ\\EUVXWXVXEY[[G$E$&E! #$E"#E#$E +,E(&'E#&'E#%*E"%)E"16E. %E)*E&	))+++*.E'E/1OQUVVM "E ~---	..00 """  
  	 	 	D	 ! *))+++%)" -U-ABBE 32<@@M36}3E3E3J3JM#2#&..P]L''c22L
KKZ"FEK1L9#)S)=)C%D%D	   .BIt()))rH  8E$5$?$?$A$A 8!!"6777  \ 6! ; ; 
 
+
 
 
 
 
 a%5E"+a//E4MQR4R4R,<u?[,[) 
a %)E! u8$??HU$<dCCN! 5I4T00Zf !$q((E222# 3!!Q&!!$(DDD#' ()E% <88HOOHMMA-&;E# 
77EE<N3B3,? <N++b00uub< < <	
 	
 	
 "*&&un>RSSS 6
 

x)=>>>> 
 
 
H& 	 	
 	
 	
 	
 	

   X%C 0 / 1	& & X :.4"+%
 
 

 ."2!!
 

 /./@AA" 	C2Ezz/%771B.!
5L!
 !
 ! !  9	KKI$((/336::    # 1	KK6)--.A3GGHH *F	    (():;; *	KKV$((/33-11   *0AM * *!2B* * *  
 q  MM	0161H1Hn<M- 2I 2 2.. %B"6"<"+-% % %!
 2s8}}l<M   E'M8( ($ 01,23/15.=B:,1)"223DEE E 1<BBBBBB#|'%.!%h#$8999+UJ55;eZ66<"
 
 
 !#
	(        $.:<<   	( 	( 	("&#'   	(  	& 	&AF!T"" quuY'7'7 Qy\**As## 		 ".P00#(#3,3	  FF ! P P PNN#BJOOOOOOOOPf%%%% 	:"(++j"9"9 < < <5s;;;;;;;;< )+E%&)eeE#&'E# E "+!9!;!;!AE BDDu9:::! 7
D%"<===16..#' 16.  	1;<QSV1W1W_--]_I!//0F	RRRR 	 	 	D	  	.89NPS.T.T\**Z\F!&!6!C!CF!K!K!Qr 	 	 	D	 !311+31/-   s   A+A8 8
BB0C8 8&D! D!)J? ?
KKV& &.WW5A$f, b- ,f, -b>;f, =b>>A8f, 7ef, 
e;e61f, 6e;;0f, ,
g6gg9k 
kk"5l 
l%$l%)
r   r   r   r   r   r   r   r   r   r   )
r   r   r   r   r   r   r   r   r   r   ri   )r%   r$   r7   r)   r(   r'   r8   r)   r9   r)   r:   r;   r   r#   )r5   
__future__r   loggingr   r   dataclassesr   typingr   r   r   r   agent.conversation_compressionr	   agent.iteration_budgetr
   agent.model_metadatar   r   	getLoggerr2   r   r   r!   r#   r   r   r   r   <module>r     sg   , # " " " " "       ! ! ! ! ! ! , , , , , , , , , , , , Q Q Q Q Q Q 2 2 2 2 2 2       
 
	8	$	$? ? ? ?.H H H H8 ! ! ! ! ! ! ! !D /3` ` ` ` ` ` `r   