
    Yjo                        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       GC:\Users\server\AppData\Local\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Optional[Any]persist_user_timestampOptional[float]c                  A  |	             	 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}d|j        v r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lm}  | |           d _        d _        d _        d _        d _        d _         d _!        d _"        d _#        d _$        d _%         j&        '                                 d _(        t           j)        dd          }tU          |          r
 |             d _+         j,        dk    r;	  -                                r .                    d           n# t          $ r Y nw xY w j/        r 0                                 d _/        tc           j2                   _3         ||          }ti          |          dk    r|dd         dz   n|}|5                    dd          }t          6                    d  j        pd! j7         j8        pd" j9        pd"ti          pg           |           rtu                    ng At           d#d          }||n|}t          |tv                    r!|<                    d$          |k    r|}||d$<   n!d%|d&}t          |tv                    rd _=        r. j>        ?                                s @                               rV jA        dk    rKt          d' D                       }|dk    r,| _A         jC        dk    r jD        dk    r| jC        z   _D        AE                    |           ti          A          d(z
  }| _         xjA        d(z  c_A        d _F        t           d)d          }||G                                 t           d*d          }||G                                 ||n|} d}! jC        dk    r9d+ jH        v r0 j)        r) xjD        d(z  c_D         jD         jC        k    r	d}!d _D        t           d,d          }"|"0	 dd-lImJ}#  |#|           }$|$r |"|$           n# t          $ r Y nw xY w jK        sC ||          }% L                    d.|%dd/          ti          |%          d/k    rdnd d0            jM         | |            jM        }&t           d1d          }'dY Afd4}(	 |' |(             n$|'5   |(             ddd           n# 1 swxY w Y   n4# t          $ r' t          N                    d5 j        pd!d           Y nw xY wt          |tv                    r|<                    d6          rd _=        n6# t          |tv                    r|<                    d6          rd _=        w xY w jO        rt          A jQ        jR         jQ        jS         jQ        jT                  rt          A|&pd jV        pd7          }) jQ        }*t          |*d8d9           }+ |+|)          },t           dd          d:k    o3t          t           d;d<          pd<          W                                d=v }-|,s|*jX        }.|.dk    r|)|.k    r|)|*_X         t          |*d>d?                       }/|,r/t          6                    d@|)dA|*jT        dA|*jY        dA           n|/rFt          6                    dBt          |/<                    dCdD                     j        pd!           n_|-r,t          6                    dEt           d;d<                     n1|*[                    |)          rt          6                    dF|)dA|*jT        dA j7        |*j\        dA            .                    dG|)dAdH|*jT        dAdI           t          dJ          D ]}0ti          A          }1|)}2 ^                    A||)|K          \  A}&t          A|&pd jV        pd7          })t          |1ti          A          |2|)          s nKt           A          d _        d _         d _"        d _#        d _$        |*[                    |)          s nd}3	 ddLlamb}4  |4dM j        ||| tu          A          t                      j7        t           dNd          pdt           dOd          pdP
  
        }5g }6	 ddQldme}7mf}8  |7            }9n# t          $ r d}8d}9Y nw xY w|5D ]}:d};t          |:tv                    r+|:<                    dR          rt          |:dR                   };n-t          |:t                    r|:g                                r|:};nq|8H	  |8|; j        dS|9T          };n2# t          $ r%}<t          N                    dU|<           Y d}<~<nd}<~<ww xY w|6E                    |;           |6rdVh                    |6          }3n2# t          $ r%}=t          N                    dW|=           Y d}=~=nd}=~=ww xY wi  _i        t                       _k        d _l        d _m        t          jo                    jp         _q         |            r                    d jq                    js        r+ |            r                    d jq                   d _t        nd _u        d _t         jv        rK	 t          | t                    r| nd}> jv        w                     jA        |>           n# t          $ r Y nw xY wd}? jv        rG	 t          | t                    r| nd}@ jv        x                    |@          pd}?n# t          $ r Y nw xY wt          || A|&||||!|3|?X          S )Za  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)rA   rB   rC   _memory_write_originassistant_tool_skip_mcp_refreshFNztools.mcp_tool)has_registered_mcp_toolsrefresh_agent_mcp_toolsT)
quiet_modez&between-turns MCP tool refresh skipped)exc_infosession:   )note_turn_start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unknown_pending_cli_user_messagecontentuser)rolerW   c              3  L   K   | ]}|                     d           dk    dV   dS )rY   rX   r   N)get).0ms     r   	<genexpr>z%build_turn_context.<locals>.<genexpr>4  s?       
 
quuV}}/F/FA/F/F/F/F
 
r   r   _stream_context_scrubber_stream_think_scrubbermemoryreaction_callback)detect_reactionu   💬 Starting conversation: '<   '_session_persist_lockr   Nonec                 \                                                                       d S N)_ensure_db_session_persist_session)agentr(   r   s   r   _ensure_and_persistz/build_turn_context.<locals>._ensure_and_persist  s2      """x)=>>>>>r   z:Early turn-start session persistence failed for session=%s_db_persisted)system_prompttools$should_defer_preflight_to_real_usagec                    dS )NFr   )_tokenss    r   <lambda>z$build_turn_context.<locals>.<lambda>  s    E r   codex_app_server codex_app_server_auto_compactionnative>   offrw   'get_active_compression_failure_cooldownc                     d S ri   r   r   r   r   rt   z$build_turn_context.<locals>.<lambda>  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        zsSkipping Hermes preflight compression for codex app-server (mode=%s); Hermes will not start thread compaction here.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@   r   	sender_id)get_spill_configspill_if_oversizedcontextzplugin hook)r   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   )r   rg   )zagent.auxiliary_clientr>   getattr	Exceptionr   _restore_primary_runtimesysmodulestools.mcp_toolrG   rH   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agent.agent_runtime_helpersrN   _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_supportedrC   _cleanup_dead_connections_emit_status_compression_warning_replay_compression_warningr
   max_iterationsiteration_budgetr    replaceinfor@   r?   r   listdictr[   rV   _todo_store	has_items_hydrate_todo_store_user_turn_countsum_memory_nudge_interval_turns_since_memoryappend_is_user_initiated_turnresetvalid_tool_namesagent.reactionsrc   rI   _safe_print_cached_system_promptwarningcompression_enabledr!   context_compressorr   r   r   r   rp   lowerlast_prompt_tokenslast_real_prompt_tokensr   should_compresscontext_lengthrange_compress_contextr   r	   hermes_cli.pluginsr   r   tools.hook_output_spillr   r   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#   )Brl   r%   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>   _sysrG   rH   r+   r,   rN   _reset_consol_preview_text_msg_previewpending_cli_messageexpected_persist_contentuser_msgprior_user_turnsr-   scrubberthink_scrubberr&   r.   rb   rc   kind_print_previewr*   persist_lockrm   _preflight_tokens_compressor_defer_preflight_preflight_deferred_codex_native_auto_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   _queryr   sB   `  `                                                             @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 	D 4<//\\\\\\\\++-- D++EdCCCC 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! <;;;;;OE7### #$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 "%)DdKK 4 @l  	&--3##I..2JJJ& +"|<<)400 	3.2E+  8E$5$?$?$A$A 8!!"6777  \ 6! ; ; 
 
+
 
 
 
 
 a%5E"+a//E4MQR4R4R,<u?[,[)
 OOHMMA-&;E# 
a %)E! u8$??HU$<dCCN! 5I4T00Zf !$q((E222# 3!!Q&!!$(DDD#' ()E%
  ':DAA$	777777"?#899D (!!$''' 	 	 	D	  
77EE<N3B3,? <N++b00uub< < <	
 	
 	
 "*&&un>RSSS 6 5"94@@L? ? ? ? ? ? ? ?
3!!!! & &##%%%& & & & & & & & & & & & & & & 
 
 
H& 	 	
 	
 	
 	
 	

 -t44 	38K8O8OP_8`8` 	3.2E+ -t44 	38K8O8OP_8`8` 	3.2E+2222   l%C 0 / 1	& & l :.4"+%
 
 

 ."2!!
 

 /./@AA E:t,,0BB 	!6  
   egg ! 	 # 	C2Ezz/%771B.!
5L!
 !
 ! !  ?	KKI$((/336::    # 7	KK6)--.A3GGHH *F	      0	KKKA8LL   
 (():;; *	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+A; ;
BB
=D &D10D17)K! !
K.-K.4W 
W W "Z 1Z<Z ZZ ZZ [: .[[: [[: :3\-A$m9 'i: 9m9 :jm9 
jA8m9 lm9 
m#m>m9 m0m9 9
n(n##n(9r 
r"!r"/5s% %
s21s2)
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x x x x x x xr   