+
    iHàjq,  ã                  ól   € R t ^ RIHt ^ RIt^ RIHtHt ]P                  ! R4      tR t	 ! R R4      t
R# )a  The proactive tool-result prune: deterministic passes plus the in-place row commit (#124102).

Split out of :mod:`agent.context_compressor` (FILE_LINES ratchet): the prune is a self-contained
reclamation feature. The origin module keeps the ``ContextCompressor`` class; this sibling owns
``prune_tool_results_only`` and its in-place commit helper. Log name parity with the origin
module, and every origin-module symbol is read through ``_cc()`` at call time so tests that patch
``agent.context_compressor.X`` stay effective.
)ÚannotationsN)ÚAnyÚOptionalzagent.context_compressorc                 ó   € ^ RI Hp  V # )z£The origin module, resolved lazily: avoids the import cycle and keeps tests that patch
``agent.context_compressor.X`` effective (attributes are read at call time).©Úcontext_compressor)Úagentr   r   s    ÚC/home/ubuntu/.hermes/hermes-agent/agent/context_compressor_prune.pyÚ_ccr
      s   € õ )ØÐó    c                  ó6   € ] tR t^tRtRR R lltR R ltRtR# )	ÚProactivePruneMixinzX``ContextCompressor`` mixin: the deterministic, no-LLM tool-result prune and its commit.Nc               ó$   € V ^8„  d   QhRRRRRR/# )é   Úmessagesúlist[dict[str, Any]]Úcurrent_tokensz
int | NoneÚreturnz tuple[list[dict[str, Any]], int]© )Úformats   "r	   Ú__annotate__Ú ProactivePruneMixin.__annotate__   s)   € ÷ i)ñ i)Ø,ði)Ø>Hði)à	)ñi)r   c           
     ó  a€ V P                   ^ 8:  g   Ve   W P                   8  d   V^ 3# \        V4      V P                  V P                  V4      ,           ^,           8:  d   V P	                  RV4       V^ 3# \        4       P                  o\        V3R lV 4       4      pW0P                  8  d   V P                  V4      '       g   V^ 3# \        V RR4      p\        V RR4      pV'       d;   V'       d3   \        \        VRR4      4      '       g   V P	                  RV4       V^ 3# V P                  WP                  RV P                  R	7      w  rgV'       g   V P	                  R
V4       V^ 3# \        V3R lV 4       4      p\        ^ W8,
          4      p	W�P                  8  d   V P	                  RW#R7       V^ 3# \        W�P                   V P                  4      p
WŠ,           pV'       dì   V'       dä   \        4       p^ RIHp VP$                  V/pVP&                  pVP(                  pRp VP+                  WEV4      pV P-                  WEWV4      pVf*   ^ RIHp V! WEV4      w  ppVP3                  WVVVVVVR7       TP<                  pTf
   T! T4       M?T! \?        Y4       UUu. uF!  w  ppTPA                  T4      '       g   K  TNK#  	  upp4       W°n        RV n!        Wg3#   T d0    \4        P7                  R4       T P	                  RY#R7       T^ 3u # T d0    \4        P7                  R4       T P	                  RY#R7       T^ 3u # \8         d%   p\4        P;                  RT4       T^ 3u Rp?# Rp?ii ; iu uppi )ue  Deterministic, no-LLM tool-result prune gated on ``proactive_prune_tokens``.

Protects the tail by message COUNT only. A commit breaks the prompt cache, so it requires
``proactive_prune_min_reclaim_tokens`` and a full regrowth runway; otherwise returns the INPUT
object as ``(messages, 0)``. The rearm gate is measured on message bodies only, so it is
bypassed (never the reclaim gate) when a provider-billed ``current_tokens`` reading already
puts the request over ``threshold_tokens`` (#101889); every no-op taken while over threshold
is logged once per distinct reason.

``_prune_old_tool_results`` runs all deterministic passes: (1) dedup byte-identical tool results â€”
keeps the newest full copy and back-references older exact duplicates ANYWHERE in the list
(including the protected tail), so no unique content is ever lost; (2) summarize non-tail tool
results larger than ``min_prune_chars``; (3) truncate oversized tool_call arguments on non-tail
assistant messages; (3.5) retire image payloads on all but the newest ``_MAX_KEEP_TOOL_IMAGES``
image-bearing tool results, except a pending round that fits the hard share (#92699). Only pass (2)'s floor is
raised by ``proactive_prune_min_result_chars``; passes (1) and (3) keep their own fixed floors. The
recent-tail protection applies to passes (2) and (3); pass (1) is tail-agnostic by design because
dedup is lossless.
Nzprune:tail_onlyc              3  ó4   <"  € T F  pS! V4      x € K  	  R # 5i©Nr   ©Ú.0ÚmÚ_estimate_msg_budget_tokenss   & €r	   Ú	<genexpr>Ú>ProactivePruneMixin.prune_tool_results_only.<locals>.<genexpr>:   s   øé € ÐF¹X¸Ñ0°×3Ð3»Xùó   ƒÚ_session_dbÚ_session_idÚ Úarchive_and_compactzprune:store_cannot_persist)Úprotect_tail_countÚprotect_tail_tokensÚmin_prune_charszprune:nothing_eligiblec              3  ó4   <"  € T F  pS! V4      x € K  	  R # 5ir   r   r   s   & €r	   r   r    L   s   øé € ÐH¹K°qÑ/°×2Ð2»Kùr!   zprune:reclaim_below_minimum)Úbefore)ÚHeldRowRewrittenError)Úcoverage_for_commit)Úmodel_config_patchÚ	watermarkÚcovered_idsÚunresolved_heldÚheld_for_caszVProactive tool-result prune skipped: another compaction already committed this sessionzprune:stale_generationzXProactive tool-result prune skipped: a held row was rewritten in place before the commitzQProactive tool-result prune DB commit failed; keeping the original transcript: %s)"Úproactive_prune_tokensÚlenÚprotect_last_nÚ_protect_head_sizeÚ_warn_reclamation_no_opr
   r   ÚsumÚ_proactive_prune_rearm_tokensÚ_billed_basis_over_thresholdÚgetattrÚcallableÚ_prune_old_tool_resultsÚ proactive_prune_min_result_charsÚmaxÚ"proactive_prune_min_reclaim_tokensÚhermes_state_errorsr+   Ú&PROACTIVE_PRUNE_REARM_MODEL_CONFIG_KEYÚStaleHeldHistoryÚ_DB_PERSISTED_MARKERÚ_archive_watermark_forÚ_rewrite_pruned_rows_in_placeÚ&agent.conversation_compression_archiver,   r%   ÚloggerÚinfoÚ	ExceptionÚwarningÚstamp_db_persisted_markersÚzipÚgetÚ_last_reclaim_block_warn)Úselfr   r   r*   Ú
session_dbÚ
session_idÚpruned_msgsÚpruned_countÚafterÚ	reclaimedÚrunwayÚnext_rearm_tokensÚ_cc_r+   r-   rB   rC   Ú	rewrittenr.   r,   r/   r0   ÚexcrK   ÚoriginalÚreplacementr   s   &&&                       @r	   Úprune_tool_results_onlyÚ+ProactivePruneMixin.prune_tool_results_only   s~  ø€ ð, ×&Ñ&¨!Ô+ØÒ&¨>×<WÑ<WÔ+Wà˜Q�;ÐÜˆx‹=˜D×/Ñ/°$×2IÑ2IÈ(Ó2SÕSÐVWÕWÔWØ×(Ñ(Ð):¸NÔKØ˜Q�;ÐÜ&)£e×&GÑ&GÐ#ÜÔF¹XÓFÓFˆà×6Ñ6Ô6¸t×?`Ñ?`Ðao×?pÒ?pØ˜Q�;Ðä˜T =°$Ó7ˆ
Ü˜T =°"Ó5ˆ
ßŸ*¬X´g¸jÐJ_ÐaeÓ6f×-gÒ-gØ×(Ñ(Ð)EÀ~ÔVØ˜Q�;ÐØ$(×$@Ñ$@Ø×)<Ñ)<ÐRVÐhl÷  iNñ  iNð %Aó %
Ñ!ˆ÷ à×(Ñ(Ð)AÀ>ÔRØ˜Q�;ÐäÔH¹KÓHÓHˆÜ˜˜6�>Ó*ˆ	Ø×>Ñ>Ô>Ø×(Ñ(Ð)FÈÐ(ÔfØ˜Q�;Ðä�Y× ;Ñ ;¸T×=dÑ=dÓeˆØ!�NÐßŸ*Ü“5ˆDÝAØ"&×"MÑ"MÐO`Ð!aÐØ#×4Ñ4ÐØ#'×#<Ñ#<Ð ØˆIð#à ×7Ñ7¸
ÐPXÓY�	Ø ×>Ñ>Ø¨HÐCUóW�	àÒ$ÝZÙ3FÀzÐ_gÓ3hÑ0�K Ø×2Ñ2Ø"Ø+=Ø"+Ø$/ÀØ%-ð 3ô /ð0 *.×)HÑ)HÐ&ØÒ Ù*¨;Õ7á*Ü=@ÀÔ=Wô,;Ù=WÑ$9 H¨kØ—|‘|Ð$8×9÷  �KÑ=Wò,;ô <ð .?Ô*à(,ˆÔ%ØÐ(Ð(øð9 $ô #ô —‘ÐtÔuØ×,Ñ,Ð-EÀ~Ð,ÔeØ �{Ò"Ø(ô #ô —‘ÐvÔwØ×,Ñ,Ð-EÀ~Ð,ÔeØ �{Ò"Üô #Ü—‘ÐrÐtwÔxØ �{Õ"ûð#üó,;sC   È/AK) Ê*N
ËN
Ë)3N ÌN Ì#-N ÍN ÍN ÍM;Í5N Í;N c               ó0   € V ^8„  d   QhRRRRRRRRRR	R
R/# )r   rP   r   rQ   Ústrr   r   rR   r-   zdict[str, Any]r   zOptional[int]r   )r   s   "r	   r   r   ‡   s>   € ÷ -ñ -Øð-Ø+.ð-Ø:Nð-à)ð-à?Mð-ð 
ñ-r   c                óR  € \        VRR4      p\        V4      '       d   \        V4      \        V4      8w  d   R# ^ RIHpHp \        4       P                  p	V	R0p
. pRp\        W44       FÍ  w  rÞ\        V\        4      '       d   \        V\        4      '       g    R# VP                  V	4      '       g   RpKO  V'       d    R# VP                  4        UUu/ uF  w  ppWú9  g   K  VVbK  	  uppVP                  4        UUu/ uF  w  ppWú9  g   K  VVbK  	  upp8w  g   K»  VP                  WÞ34       KÏ  	   V! W+VR7      # u uppi u uppi   T d)   p\        4       P                  \        T4      4      ThRp?iT d"   p\         P#                  RT4        Rp?R# Rp?ii ; i)	aÓ  Commit a prune by rewriting only its changed rows (#124102); ``None`` means use the full rewrite.

A prune edits the content of a few existing messages and never adds, drops or reorders one, so the
durable rows it touched can be updated where they are. That needs a store with
``rewrite_pruned_rows`` and unwritten messages only at the end (the next flush appends those in
order; one sitting before durable rows would land out of order). A row the store cannot name
exactly also falls back: ``archive_and_compact`` handles every shape. A row it *can* name whose
stored body changed is not ambiguity but a lost race, and raises ``StaleHeldHistory`` so the
caller skips the pass rather than republishing the held generation.
Úrewrite_pruned_rowsN)ÚPruneRowStaleErrorÚPruneRowUnresolvedErrorÚ_row_idFT)r-   zFProactive prune: in-place rewrite refused (%s); using the full rewrite)r:   r;   r3   r@   rc   rd   r
   rC   rL   Ú
isinstanceÚdictrM   ÚitemsÚappendrB   r`   rG   rH   )rO   rP   rQ   r   rR   r-   Úrewriterc   rd   rC   ÚignoredÚchangesÚunwritten_seenr[   r\   ÚkÚvrZ   s   &&&&&&            r	   rE   Ú1ProactivePruneMixin._rewrite_pruned_rows_in_place‡   ss  € ô ˜*Ð&;¸TÓBˆÜ˜× Ò ¤C¨£M´S¸Ó5EÔ$EÙßSä"›u×9Ñ9ÐØ'¨Ð3ˆØ!ˆØˆÜ%(¨Ö%?Ñ!ˆHÜ˜h¬×-Ò-´ZÀÌT×5RÒ5RÚØ—<‘<Ð 4×5Ò5Ø!%�ÙßÚØ"*§.¡.Ô"2ÔGÑ"2™$˜!˜Q°aÑ6F”��A’Ñ"2ÒGØ)4×):Ñ):Ô)<ÔQÑ)<¡  AÀÑ@Pœ˜˜1šÑ)<ÒQöRà—‘ Ð6Ö7ñ &@ð	Ù˜:ÐCUÔVÐVùó	 HùÛQøð "ô 	<ô “%×(Ñ(¬¨S«Ó2¸Ð;ûØ&ô 	Ü�K‰KÐ`ÐbeÔfÝûð	úsB   ÃEÃ&EÄE
ÄE
Ä;	E ÅF&Å#E;Å;F&ÆF&ÆF!Æ!F&)rN   r8   r   )Ú__name__Ú
__module__Ú__qualname__Ú__firstlineno__Ú__doc__r]   rE   Ú__static_attributes__r   r   r	   r   r      s   † Ùb÷i)÷V-ñ -r   r   )ru   Ú
__future__r   ÚloggingÚtypingr   r   Ú	getLoggerrG   r
   r   r   r   r	   Ú<module>r{      s7   ðñõ #ã ß  à	×	Ò	Ð5Ó	6€ò÷[ó [r   