
    .cj                      d Z ddlmZ ddlZddlZddlZddlZddlZddlZddl	Z	ddl
Z
ddlZddlZddlmZ ddlmZ ddlmZmZmZ ddlmZmZ ddlmZ  ej        e          Zd	Zd
e dZdZddZdZ dZ!dZ"dZ#dZ$dZ%dZ&dZ'ee (                    d          e!(                    dd          e"(                    dd           e#(                    d!d"d#$          e$(                    d%d&'          e%(                    d!d'          e&(                    dd!(          fZ)dd*Z*dd.Z+ G d/ d0          Z,dd2Z-dd4Z.dd7Z/dd8dd?Z0dd@Z1ddAZ2ddJZ3 G dK dL          Z4 G dM dN          Z5ddOZ6ddPZ7	 dddUZ8dVZ9ddXZ:dYZ;ddZZ<dd\Z=dd_Z>dd`Z?ddcZ@ddZAddgZBddhZCddjZDddkddldlddmdduZEddkdldvddwZFdxdydd}ZGg d~ZHdS )u  Context compression — extract the AIAgent methods that drive summarisation.

Three concerns live here:

* :func:`check_compression_model_feasibility` — startup probe of the
  configured auxiliary compression model.  Warns when the aux context
  window can't fit the main model's compression threshold; auto-lowers
  the session threshold when possible; hard-rejects auxes below
  ``MINIMUM_CONTEXT_LENGTH``.

* :func:`replay_compression_warning` — re-emit a stored warning through
  the gateway ``status_callback`` once it's wired up (the callback is
  set after :class:`AIAgent` construction).

* :func:`compress_context` — the actual compression call.  Runs the
  configured compressor, splits the SQLite session, rotates the
  session_id, notifies plugin context engines / memory providers, and
  returns the compressed message list and active system prompt.

* :func:`try_shrink_image_parts_in_messages` — image-too-large recovery
  helper that re-encodes ``data:image/...;base64,...`` parts at a smaller
  size so retries can fit under provider ceilings (Anthropic's 5 MB).

``run_agent`` keeps thin wrappers for each so existing call sites
(``self._compress_context(...)``) keep working.  Tests that exercise
these paths see no behavioural change.
    )annotationsN)datetime)Path)AnyOptionalTuple)#automatic_compaction_status_messagesanitize_memory_context)estimate_request_tokens_roughzCompacting contextu   🗜️ u:    — summarizing earlier conversation so I can continue...u6   ✓ Context compaction complete — continuing turn...agentr   returnNonec                    t          | dd          }|sdS 	  |dt                     dS # t          $ r  t                              dd           Y dS w xY w)z;Emit the structured terminal edge for a started compaction.status_callbackN	compactedz.status_callback error in compaction completionTexc_info)getattrCOMPACTION_DONE_STATUS	Exceptionloggerdebug)r   r   s     @/home/ice/.hermes/hermes-agent/agent/conversation_compression.py_emit_compaction_doner   B   s    e%6==O V%;<<<<< V V VEPTUUUUUUVs   * &AAur   📦 Pre-API compression: ~{tokens:,} tokens near the context/output limit. Compacting before the next model call.ub   📦 Preflight compression: ~{tokens:,} tokens >= {threshold:,} threshold. This may take a moment.u\   💤 Resumed after {idle_seconds}s idle — compacting ~{tokens:,} tokens before continuing.uS   🗜️ Context too large (~{tokens:,} tokens) — compressing ({attempt}/{cap})...u=   🗜️ Compressed {before} → {after} messages, retrying...uA   🗜️ Compressed ~{before:,} → ~{after:,} tokens, retrying...uL   🗜️ Context reduced to {new_ctx:,} tokens (was {old_ctx:,}), retrying...u   ⚠ Context is over the compression threshold (~{tokens:,} tokens >= {threshold:,}) but compression is currently blocked ({reason}). The model may stop responding. Run /new to start a fresh session or /compress to retry immediately.i@ )tokensi i )r   	thresholdi  )idle_secondsr   i       )r   attemptcap      )beforeafter)new_ctxold_ctxOptional[Tuple[str, str]]c                   t          | dd          }|dS 	 t          | dd          r|                    d          pdnd}t          | dd          r|                    d	          pdnd}n# t          $ r Y dS w xY w||fS )
a  Return the built-in memory text that can affect a system prompt.

    ``MemoryStore`` freezes this text until ``load_from_disk()``.  Rendering
    the frozen blocks after that reload lets compression retain the exact
    cached system prompt when it already embeds the current memory (see
    :func:`_cached_prompt_reflects_builtin_memory`).  An unreadable snapshot
    returns ``None`` so callers take the conservative rebuild path.
    _memory_storeN) r+   _memory_enabledFmemoryr+   _user_profile_enableduser)r   format_for_system_promptr   )r   storer-   r/   s       r   _builtin_memory_prompt_snapshotr2      s     E?D11E}v u/77E**844: 	 u5u==E**6228b 	
    tt4<s   AA, ,
A:9A:cached_promptstrboolc                    t          |           }|dS 	 ddlm} n# t          $ r Y dS w xY wt	          d|          D ]0\  }}|                                }|r||vr dS #||         |v r dS 1dS )u2  Whether the cached system prompt already embeds current built-in memory.

    The retention fast path must NOT compare the memory snapshot before vs
    after the disk reload: on fresh-agent surfaces (gateway, TUI) the cached
    prompt is restored from the session DB and can predate mid-session memory
    writes that the fresh ``MemoryStore`` already picked up at init — the
    snapshot is then identical on both sides of the reload while the prompt
    itself is stale, and retaining it would latch old memory for the life of
    the session (and re-persist it via ``update_system_prompt``).

    Instead, verify the CURRENT (post-reload) rendered blocks appear verbatim
    in the cached prompt, and that no leftover block header remains for a
    target whose entries have since been emptied or disabled.
    NFr   )MEMORY_BLOCK_HEADERS)r-   r/   T)r2   tools.memory_toolr7   r   zipstrip)r   r3   snapshotr7   targetblocks         r   &_cached_prompt_reflects_builtin_memoryr>      s     /u55Hu:::::::   uu/::   		 M))uu *!&)]:: 55 ; 4s    
**c                  :    e Zd ZdZddZddZddZdd	Zdd
ZdS )CompressionCommitFencea  Fence timeout cancellation against post-summary session mutation.

    Compression itself is synchronous and may be running in an executor thread.
    A caller can stop waiting for the summary, but it cannot kill that thread.
    This fence makes the commit boundary deterministic: cancellation either wins
    before session mutation starts, or waits until an already-started commit is
    fully complete before the caller proceeds.
    r   r   c                R    t          j                    | _        d| _        d| _        d S NF)	threadingLock_lock
_cancelled_commit_startedselfs    r   __init__zCompressionCommitFence.__init__   s&    ^%%
$    r5   c                    | j         5  | j        r	 ddd           dS d| _        	 ddd           dS # 1 swxY w Y   dS )a5  Cancel a pending commit, or wait for an active commit to finish.

        Returns ``True`` when cancellation won before the commit boundary.
        Returns ``False`` when the worker had already entered the boundary; in
        that case acquiring this lock waits until all session mutation finishes.
        NFT)rE   rG   rF   rH   s    r   cancel_before_commitz+CompressionCommitFence.cancel_before_commit   s     Z 	 	# 	 	 	 	 	 	 	 	 #DO		 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	s   	3377Optional[bool]c                   | j                             d          sdS 	 | j        r	 | j                                          dS d| _        	 | j                                          dS # | j                                          w xY w)zNon-blocking form of :meth:`cancel_before_commit`.

        Returns ``None`` while an active commit owns the fence, allowing an
        async caller to yield instead of blocking its event loop.
        F)blockingNT)rE   acquirerG   releaserF   rH   s    r   try_cancel_before_commitz/CompressionCommitFence.try_cancel_before_commit   s     z!!5!11 	4	!#  J      #DOJ     DJ    s   A% A% %B c                    | j                                          | j        r| j                                          dS d| _        dS )z:Enter the commit boundary unless cancellation already won.FT)rE   rQ   rF   rR   rG   rH   s    r   begin_commitz#CompressionCommitFence.begin_commit   sF    
? 	J   5#trK   c                8    | j                                          dS )z8Leave a commit boundary entered by :meth:`begin_commit`.N)rE   rR   rH   s    r   finish_commitz$CompressionCommitFence.finish_commit  s    
rK   Nr   r   r   r5   )r   rN   )	__name__
__module____qualname____doc__rJ   rM   rS   rU   rW    rK   r   r@   r@      s         % % % %
   ! ! ! !         rK   r@   lock_dbc                    	 ddl m} t                      }t          |           |u ot	          j        |d|          |u S # t          $ r Y dS w xY w)a  Whether the live in-memory SessionDB class structurally predates locks.

    In the supported hot-reload skew, this module is new while the already
    imported ``hermes_state.SessionDB`` class (and its live instances) is old.
    Only that exact class identity may fail open. Proxies, nominal lookalikes,
    non-callables, and descriptor failures must fail closed. Static lookup
    avoids invoking a present-but-broken descriptor.
    r   )	SessionDBtry_acquire_compression_lockF)hermes_statera   objecttypeinspectgetattr_staticr   )r_   ra   missings      r   !_lock_api_is_absent_on_session_dbri     s~    ******((MMY& &97 	
    uus   <? 
AA
compressorc                   dddifdi fdi ff}|D ]p\  }}t          t          |           |d          }t          |          s3	  || fi | ># t          $ r&}t                              d||           Y d}~id}~ww xY wdS )zFRefresh durable automatic-compression guards on a built-in compressor.'get_active_compression_failure_cooldownrefreshT!_load_fallback_compression_streak#_load_ineffective_compression_countNz)compression guard refresh failed (%s): %s)r   re   callabler   r   r   )rj   method_callsmethod_namekwargsmethodexcs         r   %_refresh_persisted_compression_guardsrv   #  s     
3Y4EF	,b1	.3L
  , X XVj));== 		XF:(((((( 	X 	X 	XLLDkSVWWWWWWWW	XX Xs   	A
A>A99A>
session_db
session_idc                    t          t          |           dd          }t          |          sdS  || |          }t          |o/|                    d          duo|                    d          dk              S )zDReturn whether another path already rotated this compression parent.get_sessionNFended_at
end_reasoncompression)r   re   rp   r5   get)rw   rx   gettersessions       r   #_session_was_rotated_by_compressionr   4  s    T*%%}d;;FF ufZ,,G 	7KK
##4/	7KK%%6  rK   )failure_class
started_atfloatcommit_statussplit_statusr   
str | Nonec                  	 t          | j        dd          }t          |t                    si }t          |          }|                    dd           |                    dt          | dd          pt          j                    j                   |                    dt          | dd          pd           t          t          j
                    |z
  d	z            |d
<   ||d<   ||d<   |r||d<   |                    dd           |                    dd           t          |                    d          p+t          | j        dd          pt          | j        dd                    |d<   t                              dt          j        |dd                     dS # t"          $ r&}t                              d|           Y d}~dS d}~ww xY w)z>Emit one content-free JSON log line for a compression attempt._last_compression_telemetryNeventcompression_attempt
attempt_id_compression_attempt_idr+   rx     total_duration_msr   r   r   chunkingFchunk_countr   fallback_used_last_summary_fallback_used_last_aux_model_failure_modelz)context compression attempt telemetry: %sT),:)	sort_keys
separatorsz0failed to emit compression attempt telemetry: %s)r   context_compressor
isinstancedict
setdefaultuuiduuid4hexinttime	monotonicr5   r~   r   infojsondumpsr   r   )r   r   r   r   r   	telemetrypayloadru   s           r   #_emit_compression_attempt_telemetryr   A  s   NE46SUYZZ	)T** 	Iy//7$9:::<8QSU)V)V)jZ^ZdZfZfZjkkk<b)I)I)ORPPP'*DN,<,<z,IT+Q'R'R#$#0 ". 	5'4GO$:u---=!,,,#'KK(( Xu/1NPUVVXu/1PRVWW$
 $
 
 	7Jw$:FFF	
 	
 	
 	
 	
  N N NGMMMMMMMMMNs   FF! !
G+GGc                V    t          | dd          }|du pt          |t                    S )u  Type-pinned read of the #69870 lock-skip signal.

    ``agent._compression_skipped_due_to_lock`` is set by ``compress_context``
    when a compression pass no-ops because another path holds the per-session
    compression lock (holder string when the holder was confirmed, ``True``
    otherwise) and cleared to ``None`` at the entry of every call.

    The read MUST be type-pinned (``is True or isinstance(x, str)``), never
    bare truthiness: MagicMock test-double agents auto-create truthy
    attributes, and a bare ``if getattr(agent, ...)`` would hijack every
    mocked agent in sibling suites into the lock-skip branch (the
    #69870 × #69840 type-ahead incident).
     _compression_skipped_due_to_lockNT)r   r   r4   )r   _sigs     r   compression_skipped_due_to_lockr   f  s/     5<dCCD4<0:dC000rK   c           
         ddl }dt          j                     d |j                     dt	          |           ddt          j                    j        dd          S )	a  Build a unique holder id for the lock: pid:tid:agent-instance:uuid.

    The pid+tid prefix lets ops tell crashed/abandoned holders apart from
    live ones (expiry-based recovery uses the timestamp, but ``holder``
    is what shows up in diagnostics + log lines). The agent instance id
    and a per-acquire uuid disambiguate two co-resident agents on the
    same thread (background_review forks run on a worker thread, but
    on machines where compression itself dispatches to a thread pool
    we want each acquire to be unique).
    r   Nzpid=z:tid=z:agent=xz:nonce=   )rC   osgetpid	get_identidr   r   r   )r   rC   s     r   _compression_lock_holderr   x  s{     	)ry{{ 	) 	)#	#%%	) 	)U))	) 	) *,,"2A2&	) 	)rK   compress_fncurrent_tokensOptional[int]focus_topicOptional[str]forcememory_contextr   c               (   |||d}|r||d<   	 t          j        |           j        n# t          t          f$ r d|icY S w xY wt          d                                 D                       }|r|S fd|                                D             S )aI  Return only compression kwargs accepted by an engine callable.

    Context-engine plugins can outlive additions to the optional host contract.
    Inspecting the callable before invoking it keeps those older signatures
    compatible without catching an internal ``TypeError`` and executing a
    stateful compressor twice.
    )r   r   r   r   r   c              3  F   K   | ]}|j         t          j        j        u V  d S N)kindrf   	ParameterVAR_KEYWORD).0	parameters     r   	<genexpr>z0_supported_compression_kwargs.<locals>.<genexpr>  sC         	'+77     rK   c                $    i | ]\  }}|v 	||S r^   r^   )r   namevalue
parameterss      r   
<dictcomp>z1_supported_compression_kwargs.<locals>.<dictcomp>  s)    TTTKD%ASASD%ASASASrK   )rf   	signaturer   	TypeError
ValueErroranyvaluesitems)r   r   r   r   r   
candidatesaccepts_kwargsr   s          @r   _supported_compression_kwargsr     s      )" J
  6'5
#$2&{33>

z" 2 2 2 !.1111	2   #**,,    N  TTTT:+;+;+=+=TTTTs   * AAc                  >    e Zd ZdZddd	ZddZdddZddZddZdS )_CompressionActivityHeartbeatzMRefresh the agent inactivity tracker while compression blocks in an aux call.Nr   r   interval_secondsfloat | Noner   r   c                Z   || _         |t          |dd          }	 t          |pd          }n# t          t          f$ r d}Y nw xY wt          j        |          sd}t          d|          | _        t          j
                    | _        t          j        | j        dd          | _        d S )N(_compression_activity_heartbeat_interval      N@皙?zcompression-activity-heartbeatTr<   r   daemon)_agentr   r   r   r   mathisfinitemax_interval_secondsrC   Event_stopThread_run_thread)rI   r   r   s      r   rJ   z&_CompressionActivityHeartbeat.__init__  s    #&u.XZ^__	$$%5%=>>:& 	$ 	$ 	$#	$}-.. 	$#!$S*:!;!;_&&
 '91
 
 
s   . AA'_CompressionActivityHeartbeat'c                b    |                      d           | j                                         | S )Nzcontext compression started)_touchr   startrH   s    r   r   z#_CompressionActivityHeartbeat.start  s.    1222rK   context compression completeddescr4   c                    | j                                          | j                                        r5t	          j                    | j        ur| j                            d           |                     |           d S N      ?)timeout)r   setr   is_aliverC   current_threadjoinr   )rI   r   s     r   stopz"_CompressionActivityHeartbeat.stop  sp    
<  "" 	+y'?'A'A'U'ULc***DrK   c                    	 t          | j        dd           }t          |          r ||           d S d S # t          $ r  t                              dd           Y d S w xY w)N_touch_activityz+compression activity heartbeat touch failedTr   )r   r   rp   r   r   r   )rI   r   touchs      r   r   z$_CompressionActivityHeartbeat._touch  s    	WDK):DAAE d  	W 	W 	WLLFQULVVVVVV	Ws   06 &A A c                    | j                             | j                  s6|                     d           | j                             | j                  4d S d S )Nzcontext compression in progress)r   waitr   r   rH   s    r   r   z"_CompressionActivityHeartbeat._run  s]    *//$"899 	;KK9::: *//$"899 	; 	; 	; 	; 	;rK   r   )r   r   r   r   r   r   )r   r   )r   )r   r4   r   r   rX   )	rZ   r[   r\   r]   rJ   r   r   r   r   r^   rK   r   r   r     s        WW
 
 
 
 
$   
    W W W W; ; ; ; ; ;rK   r   c                  2    e Zd Z	 dddZddZddZddZdS )_CompressionLockLeaseRefresherNdbr   rx   r4   holderttl_secondsr   refresh_interval_secondsr   r   r   c                   || _         || _        || _        || _        |!t	          dt          d|dz                      }t	          dt          |                    | _        t	          dt          | j        | j        z                      | _	        t          j                    | _        t          j        | j        dd          | _        d S )	Nr   r   g       @r   r   zcompression-lock-refreshTr   )_db_session_id_holder_ttl_secondsr   minr   _refresh_interval_secondsr   _max_consecutive_failuresrC   r   r   r   r   r   )rI   r   rx   r   r   r   s         r   rJ   z'_CompressionLockLeaseRefresher.__init__  s     %'#+'*3D+:K0L0L'M'M$),S%8P2Q2Q)R)R& *-s4$t'EEFF*
 *
& _&&
 '9+
 
 
rK    '_CompressionLockLeaseRefresher'c                8    | j                                          | S r   )r   r   rH   s    r   r   z$_CompressionLockLeaseRefresher.start  s    rK   c                    | j                                          | j                                        r7t	          j                    | j        ur| j                            d           d S d S d S r   )r   r   r   r   rC   r   r   rH   s    r   r   z#_CompressionLockLeaseRefresher.stop  sp    
 <  "" 	+y'?'A'A'U'ULc*****	+ 	+'U'UrK   c                   d}| j                             | j                  s	 | j                            | j        | j        | j                  }n4# t          $ r'}t          
                    d|           d}Y d }~nd }~ww xY w|rd}|dz  }|| j        k    r#t          
                    d|| j                   d S | j                             | j                  d S d S )Nr   r   z#compression lock refresh raised: %sFr   zZcompression lock refresh failed %d times in a row; stopping lease refresher for session %s)r   r   r  r  refresh_compression_lockr  r  r  r   r   r   r  )rI   consecutive_failures	refreshedru   s       r   r   z#_CompressionLockLeaseRefresher._run  s!     !*//$"@AA 	" H==$L $ 1 >  		
  " " "BCHHH!						"  '($ A% #t'EEE>($*:  
 ) *//$"@AA 	 	 	 	 	s   ,A 
BA<<Br   )r   r   rx   r4   r   r4   r   r   r   r   r   r   )r   r	  rX   )rZ   r[   r\   rJ   r   r   r   r^   rK   r   r   r     sn         26
 
 
 
 
<   + + + +     rK   r   c                0   | j         sdS 	 ddlm}m}m} ddlm}m} 	  |d          \  }}}}}n# t          $ r d}Y nw xY w |d| 	                                          \  }}	||	sU |d|          \  }
}}|
C|rA|
|}	}d|v r9|
                    d	          r$|                    dd
          d
         dd         }||	sI|r|dk    rd| d}nd}|| _        |                     |           t                              d           dS t!          t#          |dd                    }t#          |dd          }t%          |          rt'          |t                     sdnt!          |pd          } ||	||t#          | dd          |r|dk    r|nt#          | dd          | j                  }|r'||k     r!t+          d|	 d|dd|dd|dz   d	          | j        j        }||k     r|}|}|| j        _        t#          | j        dd          }t'          |t0          t2          f          rt1          ||z            | j        _        | j        j        }|r||z  | j        _        |rt1          ||z  dz            nd}dd lm} d}|rWt'          | j        |          rB|                    ||                     ||dz            t#          | j        d!d                    }|du p||k    }t#          | d"d          pd#}t#          | dd          pd}|r|dk    r|nd}|s.	 dd$l!m"}  ||          j#        p|}n# t          $ r |pd}Y nw xY w|r| d%| d	n|}|	 d%| d	} d&|  d'|dd(| d)|dd*|dd+}|r|d,|dd-|d.z  }n|d/|dd0| d1|dd2|dd3|dd4z  }|| _        |                     |           t                              d5|	|||           dS dS # t*          $ r  t          $ r&}!t          $                    d6|!           Y d}!~!dS d}!~!ww xY w)7au  Warn at session start if the auxiliary compression model's context
    window is smaller than the main model's compression threshold.

    When the auxiliary model cannot fit the content that needs summarising,
    compression will either fail outright (the LLM call errors) or produce
    a severely truncated summary.

    Called during ``AIAgent.__init__`` so CLI users see the warning
    immediately (via ``_vprint``).  The gateway sets ``status_callback``
    *after* construction, so :func:`replay_compression_warning` re-sends
    the stored warning through the callback on the first
    ``run_conversation()`` call.
    Nr   )_resolve_task_provider_model/_try_configured_fallback_for_unavailable_clientget_text_auxiliary_client)MINIMUM_CONTEXT_LENGTHget_model_context_lengthr}   r+   )main_runtime()r   autou/   ⚠ Configured auxiliary compression provider 'u   ' is unavailable — context compression will drop middle turns without a summary. Check auxiliary.compression in config.yaml and reauthenticate that provider.u   ⚠ No auxiliary LLM provider configured — context compression will drop middle turns without a summary. Run `hermes setup` or set OPENROUTER_API_KEY.uL   No auxiliary LLM provider for compression — summaries will be unavailable.base_urlapi_key&_aux_compression_context_length_configprovider)r  r  config_context_lengthr  custom_providerszAuxiliary compression model z has a context window of r   z$ tokens, which is below the minimum zE required by Hermes Agent.  Choose a compression model with at least r   zK context (set auxiliary.compression.model in config.yaml), or set auxiliary.compression.context_length to override the detected value if it is wrong.summary_target_ratiod   2   ContextCompressor
max_tokensmodel?)urlparsez (u   ⚠ Compression model z context is z tokens, but the main model z's compression threshold was z2 tokens. Auto-lowered this session's threshold to z  tokens so compression can run.
u     To make this permanent, edit config.yaml — either:
  1. Use a larger compression model:
       auxiliary:
         compression:
           model: <model-with-z[+-context>
  2. Lower the compression threshold:
       compression:
         threshold: 0.02dz  To make this permanent, use a larger compression model in config.yaml:
       auxiliary:
         compression:
           model: <model-with-uG   +-context>
  (Lowering compression.threshold cannot help here — with z's zb-token window, Hermes's small-context floor and output reservation would recompute the trigger to z- tokens, still above the compression model's z.)u   Auxiliary compression model %s has %d token context, below the main model's compression threshold of %d tokens — auto-lowered session threshold to %d to keep compression working.z4Compression feasibility check failed (non-fatal): %s)%compression_enabledagent.auxiliary_clientr  r  r  agent.model_metadatar  r  r   _current_main_runtimeendswithrsplit_compression_warning_emit_statusr   warningr4   r   rp   r   _custom_providersr   r   threshold_tokensr   r   tail_token_budgetcontext_lengththreshold_percentagent.context_compressorr&  _compute_threshold_tokens_effective_threshold_percenturllib.parser*  hostnamer   )"r   r  r  r  r  r  _aux_cfg_provider_client	aux_model	fb_clientfb_modelfb_labelmsgaux_base_url_raw_aux_keyaux_api_keyaux_contextr   old_thresholdnew_thresholdr"  main_ctxsafe_pct_CCrecomputed_thresholdthreshold_suggestion_viable_main_model_main_provider_aux_provider_labelr*  _main_label
_aux_labelru   s"                                     r   #check_compression_model_feasibilityrW  /  sy    $ i
	
 	
 	
 	
 	
 	
 	
 	
 	
 	

	
 	
 	
 	
 	
 	
 	
 	
	#,H,H,W,W)q!Q 	# 	# 	# "	#554466
 
 
	 >>,[,[!- -)Ix $$$-x	(??x'8'8'='=?(0Q(?(?(B3B3(G%>>  %6&%@%@4)4 4 4 D 
 *-E&s###NN1   F76:r::;; vy"55%l33wJ|UX<Y<Ywbb`cdpdvtv`w`w..!")%1Y[_"`"` ,=  AARV\A\A\''bijoq{}  cA  cA"4

 

 

$  
	;)???2y 2 2(H2 21I2 2 *T1	2 2 2	 	 	 ,=	"" &M'M8EE$5 $+(*@$$ $  .e== =@!$88> >(: />H !H, (: ?GNsK(2c9:::BH JIIIII#'  Ju'?EE '*'D'D44Xx#~NNE4lDII( ($ %,S0D0S ( "%"55<K$UJ;;ArN %):f)D)D "!  
 ' AA555555 ..7G< (' ! A A A*6*@&'''A "!;33.3333  
 &??)<???JF F F>F FF F !-F F
 !EF F F  + < 6CR	< < .6;< <	 
= 6CR	
= 
= (
= 
= -5G
= 
= ,H
= 
= ,7<
= 
= 
= *-E&s###NN, 	 	 	 	 	o #"B     	 
 
 
BC	
 	
 	
 	
 	
 	
 	
 	
 	

s_   O 0 O ?O ?C	O 
HO L7 6O 7MO MBO P/PPc                    t          | dd          }|r1| j        r,	 |                     d|           dS # t          $ r Y dS w xY wdS dS )u  Re-send the compression warning through ``status_callback``.

    During ``__init__`` the gateway's ``status_callback`` is not yet
    wired, so ``_emit_status`` only reaches ``_vprint`` (CLI).  This
    method is called once at the start of the first
    ``run_conversation()`` — by then the gateway has set the callback,
    so every platform (Telegram, Discord, Slack, etc.) receives the
    warning.
    r2  N	lifecycle)r   r   r   )r   rF  s     r   replay_compression_warningrZ  +  s     %/
6
6C
 u$ 	!!+s33333 	 	 	DD	   s   4 
AAmessageslistprevious_historyOptional[list]c                    t          t          | dd                    r,t          | dd          }|du rt          |          S |du rdS |S t          t          | dd                    rt          |          S dS )a  Return the correct flush baseline after a compression boundary.

    Legacy compression rotates to a fresh child session. That child has not
    seen the compacted transcript through the normal same-turn flush path yet,
    so callers must clear ``conversation_history`` to ``None`` and let the next
    persistence call write the whole compacted list.

    In-place compaction is different: ``archive_and_compact()`` has already
    soft-archived the previous active rows and inserted ``messages`` as the new
    active live transcript under the same session id. If the same agent turn
    continues with ``conversation_history=None``, the identity-based flush path
    treats those already-persisted compacted dicts as new and appends them a
    second time, doubling the active context and retriggering compression.

    A shallow copy is intentional: it captures the current compacted dict
    identities as history while allowing later same-turn appends to remain new.

    An aborted or no-op attempt after an earlier in-place compaction must retain
    the pre-attempt baseline.  Treating all current messages as persisted would
    drop any later, unflushed turns on restart; clearing the baseline would
    append the already-persisted compacted rows a second time.
    "_last_compression_attempt_recordedF"_last_compression_attempt_in_placeNT_last_compaction_in_place)r5   r   r\  )r   r[  r]  attempt_in_places       r   &conversation_history_after_compressionrd  =  s    6 GE?GGHH  "5*NPTUUt##>>!u$$4GE6>>?? H~~4rK   )z-[System: Your previous response was truncatedz*[System: The previous response was cut offz [System: Your previous tool callz@[Your active task list was preserved across context compression]z[IMPORTANT: Background process messagec                    t          | t                    r|                     d          nd }t          |t                    r|S t          |t                    rd                    d |D                       S dS )Ncontent
c              3     K   | ]R}t          |t                    t          |                    d           p|                    d          pd          V  SdS )textrg  r+   N)r   r   r4   r~   )r   parts     r   r   z _message_text.<locals>.<genexpr>r  sp       
 
$%%
  =DHHY$7$7=2>>
 
 
 
 
 
rK   r+   )r   r   r~   r4   r\  r   )re  rg  s     r   _message_textrl  m  s    (27D(A(AKgkk)$$$tG'3 '4   
yy 
 

 
 
 
 
 	

 2rK   )_todo_snapshot_synthetic_empty_recovery_synthetic_verification_stop_synthetic_pre_verify_syntheticc                b    t           t                    r                     d          dk    rdS t           fdt          D                       rdS t                                                     }|sdS |                    t                    rdS ddl	m
} |                                S )a  Distinguish human intent from user-role runtime scaffolding.

    A compaction summary pinned to ``role="user"`` (the compressor flips the
    summary role to preserve alternation when the tail starts with an
    assistant message) is scaffolding too: treating it as human intent would
    short-circuit anchor restoration with a message the model is explicitly
    told NOT to act on.
    roler/   Fc              3  B   K   | ]}                     |          V  d S r   )r~   )r   flagre  s     r   r   z(_is_real_user_message.<locals>.<genexpr>  s/      
?
?7;;t
?
?
?
?
?
?rK   r   r%  )r   r   r~   r   _SYNTHETIC_USER_FLAGSrl  r:   
startswith_SYNTHETIC_USER_PREFIXESr:  r&  #_is_synthetic_compression_user_turn)re  rj  r&  s   `  r   _is_real_user_messagery    s     gt$$ F(;(;v(E(Eu

?
?
?
?)>
?
?
??? u!!''))D u/00 u:::::: DDWMMMMrK   rg  c                    ddl m t          | t                    r9|                               }|dk    r| S | d|                                         S t          | t                    rfd| D             S | S )a  Remove a previously merged todo-snapshot block from message content.

    Snapshot merges (see the injection site in ``compress_context``) always
    append the block at the end of the trailing user turn, so a surviving
    header marks stale todo state from an earlier compaction boundary.
    Stripping before re-injection keeps repeated boundaries from
    accumulating outdated snapshots (#26981).
    r   )TODO_INJECTION_HEADERr  Nc                   g | ]{}t          |t                    rb|                    d           dk    rIt          |                    d          pd                                                                        y||S )re   rj  r+   )r   r   r~   r4   lstriprv  )r   rk  r{  s     r   
<listcomp>z._strip_stale_todo_snapshot.<locals>.<listcomp>  s     

 

 

4&&	


 HHV$$..((.B//122 /	  /..rK   )tools.todo_toolr{  r   r4   findrstripr\  )rg  idxr{  s     @r   _strip_stale_todo_snapshotr    s     655555'3 &ll011"99Ntt}##%%%'4   


 

 

 



 

 

 
	
 NrK   r<   anchorc                (   |                     d          }|                      d          }t          |t                    st          |t                    r{t          |t                    rt          |          ndt          |pd          dg}t          |t                    rt          |          ndt          |pd          dg}||z   | d<   n"|pd d|pd                                 }|| d<   t
          D ]}|                     |d           dS )af  Fold the human anchor into an existing user-role scaffolding turn.

    Used only when every insertion slot would create two consecutive
    user-role messages. The anchor text leads (it is the active task), the
    scaffolding content is preserved after it, and the synthetic flags are
    cleared because the merged turn now carries real human intent.
    rg  rj  r+   )re   rj  

N)r~   r   r\  r4   r:   ru  pop)r<   r  anchor_contenttarget_contentanchor_partstarget_partsmergedrt  s           r   _merge_anchor_into_user_messager    sD    ZZ	**NZZ	**N.$'' #:nd+K+K # .$//GD   !3~/C+D+DEEF 	 .$//GD   !3~/C+D+DEEF 	
 )<7y"(bDDn.BDDJJLL"y%  

4 rK   c                   dd}t          |           D ]P\  }} ||          dk    r|dk    r || |dz
                     nd	}|d
k    r|                     ||            d	S Q| r || d                   d
k    r|                     |           d	S ddlm} |                    t          | d                             r|                     |           d	S t          | d         |           d	S )z?Insert the latest human turn without breaking role alternation.rF  r   r   r   c                Z    t          | t                    r|                     d          nd S )Nrr  )r   r   r~   )rF  s    r   _rolez'_insert_real_user_anchor.<locals>._role  s%    ",S$"7"7AswwvTArK   	assistantr   r   Nr/   r  r%  )rF  r   r   r   )	enumerateinsertappendr:  r&  _is_context_summary_contentrl  r  )r[  r  r  indexre  previous_roler&  s          r   _insert_real_user_anchorr    sF   B B B B $H--  w5>>[((6;aiihuqy1222TF""OOE6***FF #
  uuXb\**f44 ;:::::44hrl##  
 	 $HRL&99999rK   original_messages
compressedc                    t          d |D                       rdS ddlm}m} t	          |           D ]-}t          |          rt          | ||                      dS .|                    d|d           dS )zDPreserve human intent, not merely a synthetic user-role placeholder.c              3  4   K   | ]}t          |          V  d S r   )ry  r   re  s     r   r   z3_ensure_compressed_has_user_turn.<locals>.<genexpr>   s+      
D
Dg ))
D
D
D
D
D
DrK   Nr   )%COMPRESSION_CONTINUATION_USER_CONTENT_fresh_compaction_message_copyr/   rr  rg  )r   r:  r  r  reversedry  r  r  )r  r  r  r  re  s        r    _ensure_compressed_has_user_turnr    s    

D
D
D
D
DDD        
 -..   )) 	$..w77   FF	 8      rK   0_pending_context_engine_compression_notificationnew_session_idold_session_idc                  t          | j        dd          }t          |          sdS 	  ||d|t          | dd          pdt          | dd                     n-# t          $ r  t                              d	d
           Y dS w xY wd
S )zDNotify the active context engine after a durable compression commit.on_session_startNFr}   platformcli_gateway_session_key)boundary_reasonr  r  conversation_idz4context engine on_session_start (compression) failedTr   )r   r   rp   r   r   r   )r   r  r  callbacks       r   +_notify_context_engine_compression_completer    s     u/1CTJJHH u))UJ55>#E+A4HH	
 	
 	
 	
 	
     	B 	 	
 	
 	
 uu 4s   0A &BBc                    t          t           t          d                    rt          d          d fd}t	           t          |           dS )zCStage exactly one existing hook call for an outer host transaction.N-a compression notification is already pendingr   r5   c                 (    t                     S )Nr  r  )r  )r   r  r  s   r   _notifyz?_queue_context_engine_compression_notification.<locals>._notify@  s#    :))
 
 
 	
rK   rY   )rp   r   $_PENDING_CONTEXT_ENGINE_NOTIFICATIONRuntimeErrorsetattr)r   r  r  r  s   ``` r   ._queue_context_engine_compression_notificationr  6  sw     CTJJKK LJKKK
 
 
 
 
 
 
 
 E7AAAAArK   	committedc                   t          | t          d          }t          | t          d           |rt          |          sdS t	           |                      S )zCEmit or discard a deferred notification; repeated calls are no-ops.NF)r   r  r  rp   r5   )r   r  pendings      r   0finalize_context_engine_compression_notificationr  J  sW     eA4HHGE7>>> HW-- u		??rK   defaultF)approx_tokenstask_idr   r   !defer_context_engine_notificationcommit_fencesystem_messager  r  r  r   Optional[CompressionCommitFence]Tuple[list, str]c               /   TUVWXYZ[ |r2t          t           t          d                    rt          d          d _        d _        d _        t          j                    }	t          j
                    j        }
|rdnd}	 |
 _        t           j        d|
 j        pd|d           n# t           $ r Y nw xY wt           d	d          d
k    rd}|B|                                }|s,t           dd          }|s                     |          }||fS 	 t'           |||||          |r|                                 S S # |r|                                 w w xY w|st+           j                   t          t-           j                  dd          }t          |          r< | j                  r,t           dd          }|s                     |          }||fS t           dd          st/                      d _        t3          |          }t5          t           dd                    }d}t6                              d j        pd||r|dnd j        |           t<          }|s t?           j        d||| j        |          }t5          |          U|r                      |           dTdTU fdVt           dd          W j        pd[dXd}d}d}d _        Wn	 tC          W          }n# t           $ r}|}Y d}~nd}~ww xY w|B|s@	 Wj"        }t          |          stG          d          }n# t           $ r}|}Y d}~nd}~ww xY w	 tI          t           dd          pd          }n# tF          tJ          f$ r d}Y nw xY wt           dd          }dYWL[rItM                     X|4dXt6          '                    d[t-          |          j(        |           d}n|<dXt           d d          [k    r"[ _)        t6          '                    d![           d}n	  |[X|"          }n# t           $ r}	 W*                    [X           n2# t           $ r%}t6          +                    d#|           Y d}~nd}~ww xY wdXt6          '                    d$[t-          |          j(        |           d}Y d}~nd}~ww xY w|s	 W,                    [          }n# t           $ r d}Y nw xY wt6          '                    d%[|           dX|pd _        t           d&d          [k    r.[ _-        	  .                    d'           n# t           $ r Y nw xY wt           dd          }|s                     |          }	 t_           j        d(          r j        0                    |)           n# t           $ r Y nw xY wtc           |	d*d*d+,            V             ||fS dZdVWXYZ[fd-}W[r	 te          W[          }n|# t           $ ro} t6          '                    d.[t-          |           j(        |             |             t           dd          }|s                     |          }||fcY d} ~ S d} ~ ww xY w|rQt6                              d/[            |             t           dd          }|s                     |          }||fS |s j        }!t+          |!           t          t-          |!          dd          }t          |          rA ||!          r6 |             t           dd          }|s                     |          }||fS d}"	 X'tg          W[X||          YY4                                 d}# j5        rP	  j5        6                    |          }$to          |$tp                    rts          |$          }#n# t           $ r Y nw xY w j        j:        }%tw          |%||||#0          }&|#<                                rhd1|&vrdt           j        d2t-           j                  j(                  }'t           d3d          |'k    r"|' _=        t6          '                    d4|'           t}          j?        |          }(t                     4                                }" |%|fi |&})n_# t          $ rR}*|"|"B                    d5           d}" |             tc           |	d*d*d6t-          |*          j(         ,            d}*~*ww xY w	 |"|"B                    d7           n# |"|"B                    d7           w w xY wd}+	 t5          t           j        d8d                    },t5          t           j        d9d                    }-t           j        d:d          r	 t           j        d;d          pd<}.t           d=d          |.k    r |. _C         .                    d>|. d?           t           dd          }|s                     |          }tc           |	d*d*t           j        d;d          od@,           ||f |             	  |             |+r|                                 S S # |+r|                                 w w xY w#  |             w xY w|)|(k    r||(k    rt}          j?        |(          |dd<   t6                              dA j        pd           t           dd          }|s                     |          }tc           |	d*d*dB,            |             ||f	  |             |+r|                                 S S # |+r|                                 w w xY w|)st6          D                    dC j        pd           	  .                    dD           n# t           $ r Y nw xY wt           dd          }|s                     |          } |             ||f	  |             |+r|                                 S S # |+r|                                 w w xY w||                                }+|+st6                              dE j        pd           d _E        t           dd          }|s                     |          }tc           |	d*d*dF,            |             ||f	  |             |+r|                                 S S # |+r|                                 w w xY wt           j        d;d          }/|/r6t           d=d          |/k    r |/ _C         .                    dG|/ dH           nlt           j        dId          }0t           j        dJd          }1|0r>|0|1f}2t           dKd          |2k    r%|2 _F         .                    dL|0 dM|1pd< dN            jG        H                                }3|3r2dOdPlImJ}4 d}5|)r#to          |)dQ         t                    r|)dQ         nd}6|6|6L                    dR          dSk    rt          |6L                    dT                    }7dU |6N                                D             }8|7|8dT<   t          |8          r0to          |7tp                    r|7rdV|3 n|3}9 |4|7|9          |6dT<   d}5nI|7|6L                    dT          k    r0t          dS|7dW          <                                s|3|6dT<   d|6dX<   d}5|5s|)Q                    dS|3ddY           t          ||)            jS        }: T                                 |:+t           dZd          t           |:          r
|:};|: _S        n                     |          };|; _S        d}<d[}= jV        rd\}=	  W                    |           |r9 jV        X                     j        |)           d]}=t                       _Z        d}nht           d^d          }>to          |>t                    r'dO|>cxk    rt3          |          k    rn n
|d|>         nd}?	  \                    ||?_           n# t           $ r Y nw xY w jV        ]                     j                  }@ jV        ^                     j        d`            j        }At          j`                    a                    da           dbt          j
                    j        ddc           _        	 dOddlbmc}B  |B j                   n$# t           $ r  j        t          je        de<   Y nw xY w	 dOdflfmg}C  |C j                   n# t           $ r Y nw xY wd _h        	  jV        i                     j         jj        pt          je        L                    dgdh           j         jk        |Ai           n# t           $ r}Dt6          '                    dj|D|A           |A _        	 dOddlbmc}B  |B j                   n$# t           $ r  j        t          je        de<   Y nw xY w	 dOdflfmg}C  |C j                   n# t           $ r Y nw xY w	  jV        l                    |A           n# t           $ r Y nw xY wd}Ad _h         d}D~Dww xY wd _h        dk}=	 dOdllmmn}E  |E|A j        d`m           n2# t           $ r%}Ft6          +                    dn|F           Y d}F~Fnd}F~Fww xY w|@ru	  jV        o                    |@          }G jV        p                     j        |G           n9# tJ          t           f$ r%}Ht6          +                    do|H           Y d}H~Hnd}H~Hww xY w jV        q                     j        |;           |rdO _r        nQ jV        s                     j        |)           t3          |)           _r         j         _t        dp |)D              _Z        d}<n# t           $ r}Ht                      L                    dq          |sd*ndr}=t                      L                    dq          &|s$t6          '                    ds j        pdt|H           nt6          '                    du|H           Y d}H~Hnd}H~Hww xY wt                      L                    dq          }It5          |I          p|}J|<ot5          |I          p|}K|Ip j        pd}L|Kr5|rt            j        pd|Lv           nt            j        pd|Lv           	 |Jr, j5        r% j5        x                     j        pd|Ldd`w           n2# t           $ r%}Mt6          +                    dx|M           Y d}M~Mnd}M~Mww xY w j        jy        }N|Ndyk    r) jz         dz|N d{}O|O _{                              |O           t           d|d          rh	  |                    d} jj        pd j        |Ipd| j        jy        d~           n2# t           $ r%}Ht6          +                    d|H           Y d}H~Hnd}H~Hww xY w| _        | _E        t          |)|;pd j~        pd          }P|P j        _        dQ j        _        dO j        _        d j        _        |,rQt          t-           j                  dd          }Qt          |Q          r |Q j        |-           nd j        _        	 dOdlm}R  |R|           n# t           $ r Y nw xY wt6                              d j        pd|t3          |)          |Pd           |=dv rdnd*}Stc           |	|S|=|=dv rdnd,           |)|;f	  |             |+r|                                 S S # |+r|                                 w w xY w# 	  |             |+r|                                 w w # |+r|                                 w w xY wxY w)ut  Compress conversation context and split the session in SQLite.

    Args:
        agent: The owning :class:`AIAgent`.
        messages: Current message history (will be summarised).
        system_message: Current system prompt; used when compression needs a
            rebuilt cached prompt.
        approx_tokens: Pre-compression token estimate, logged for ops.
        task_id: Tool task scope (used for clearing file-read dedup state).
        focus_topic: Optional focus string for guided compression — the
            summariser will prioritise preserving information related to
            this topic.  Inspired by Claude Code's ``/compact <focus>``.
        force: If True, bypass any active summary-failure cooldown.  Set
            by the manual ``/compress`` slash command so users can retry
            immediately after an auto-compress abort.  Auto-compress
            callers use the default ``False``.
        defer_context_engine_notification: Delay the existing context-engine
            hook until a manual host commits its outer history transaction.
        commit_fence: Optional cooperative fence for executor callers that
            may time out. It prevents a late worker from mutating session state
            after its caller has moved on.

    Returns:
        ``(compressed_messages, new_system_prompt)`` tuple.  When
        compression aborts (aux LLM failed to produce a usable summary),
        returns the original messages unchanged and the existing system
        prompt — the session is NOT rotated.  Callers should detect the
        no-op via ``len(returned) == len(input)`` and stop the retry loop.
    Nr  Tmanualr  _compression_telemetry_seedr+   )r   rx   trigger_sourceapi_modecodex_app_serverF_cached_system_promptr  r  r   _automatic_compression_blocked _compression_feasibility_checkedcompression_in_placezPcontext compression started: session=%s messages=%d tokens=~%s model=%s focus=%rnoner   unknowncompress)phasedefault_messager  message_countr(  r   r   r   c                 :     rd S d rt                     d S d S NTr   )_compaction_done_emitted_compaction_status_emittedr   s   r   _complete_compaction_lifecyclez8compress_context.<locals>._complete_compaction_lifecycle  s>    # 	F#'  & 	)!%(((((	) 	)rK   _session_dbz0compression lock API is present but not callable_compression_lock_ttl_secondsg     r@"_compression_lock_refresh_intervalug   compression lock lookup raised unexpectedly for session=%s (%s: %s) — skipping compression this cycle _last_compression_lock_error_sidu   compression lock subsystem unavailable for session=%s — proceeding without lock. This usually means a stale in-memory module after an update; restart the process (or `hermes update`) to resync.r  z8compression lock cleanup after failed acquire failed: %sul   compression lock acquisition raised unexpectedly for session=%s (%s: %s) — skipping compression this cycleu~   compression skipped: another path is compressing session=%s (holder=%s) — returning messages unchanged to avoid session fork"_last_compression_lock_warning_siduw   ⚠ Skipping concurrent compression — another path is already compressing this session. Will retry after it finishes._begin_compression_telemetry)r   abortedlock_contended)r   r   r   r   c                 h                 rdS dH	                                   n2# t          $ r%} t                              d|            Y d} ~ nd} ~ ww xY wPrPrP	                                dS # t          $ r&}t                              d|           Y d}~dS d}~ww xY wdS dS dS )z?Release the lock keyed on the OLD session_id (before rotation).NTz*compression lock refresher stop failed: %sz#compression lock release failed: %s)r   r   r   r   release_compression_lock)	_stop_err_rel_errr  _lock_db_lock_holder_lock_refresher_lock_released	_lock_sids     r   _release_lockz'compress_context.<locals>._release_lock  s    	'&((( 	F&V$$&&&& V V VI9UUUUUUUUVI,N11)\JJJJJ N N NBHMMMMMMMMMN  s,   * 
AAA#A; ;
B+B&&B+zecompression session ownership lookup failed for session=%s (%s: %s) - skipping compression this cyclezOcompression skipped: session=%s was already rotated by another compression path)r   r   r   r   r   r   '_last_memory_context_unsupported_enginezfcontext engine %s does not accept memory_context; continuing without provider-supplied summary contextcontext compression failedz
exception:r   _last_compression_made_progressr   _last_compress_aborted_last_summary_errorzunknown error!_last_compression_summary_warningu   ⚠ Compression aborted: uz   . No messages were dropped — conversation continues unchanged. Run /compress to retry, or /new to start a fresh session.summary_generation_aborteduH   Compression made no progress (session=%s) — skipping boundary rewrite.no_progresszocontext compression returned an empty transcript; refusing to rotate session=%s so the parent remains resumableuo   ⚠ Compression returned an empty transcript. No session split was performed; conversation continues unchanged.zBCompression commit cancelled before session mutation (session=%s).commit_fence_cancelledu    ⚠ Compression summary failed: z%. Inserted a fallback context marker.r   _last_aux_model_failure_error_last_aux_fallback_warning_keyu"   ℹ Configured compression model 'z
' failed (uS   ). Recovered using main model — check auxiliary.compression.model in config.yaml.r   )_append_text_to_contentr  rr  r/   rg  c                &    i | ]\  }}|d k    ||S )rg  r^   )r   keyr   s      r   r   z$compress_context.<locals>.<dictcomp>  s/       #-3#BRBRCBRBRBRrK   r  r  rm  )rr  rg  rm  _memory_managernot_applicabler  in_place_committed_persist_user_message_idx)conversation_historyr}   z%Y%m%d_%H%M%Sr@     )set_current_session_idHERMES_SESSION_ID)set_session_contextHERMES_SESSION_SOURCEr  )rx   sourcer(  model_configparent_session_iduf   Compression child session create failed (%s) — rolling back to parent session %s to avoid an orphan.rotated_committed)migrate_goal_to_session)reasonz)Could not migrate goal on compression: %sz,Could not propagate title on compression: %sc                V    h | ]&}t          |t                    t          |          'S r^   )r   r   r   r  s     r   	<setcomp>z#compress_context.<locals>.<setcomp>  s>     5 5 5#%gt44575 5 5rK   r  failed_not_indexedzKCompression rotation aborted and rolled back to the parent session (%s): %sr)  uK   Session DB compression split failed — new session will NOT be indexed: %sr  )r  resetr
  z2memory manager on_session_switch (compression): %s   u   ⚠️  Session compressed u>    times — accuracy may degrade. Consider /new to start fresh.event_callbackzsession:compress)r  rx   r  in_placecompression_countz,event_callback error on session:compress: %s)system_prompttoolsrecord_completed_compaction)used_fallbackreset_file_dedupz^context compression done: session=%s messages=%d->%d rough_tokens=~%s awaiting_real_usage=true>   r  r  r  r  >   r  r  session_split_failedrX   )rp   r   r  r  r`  ra  r   r   r   r   r   r   r   r  r   rx   r   rU   _build_system_prompt&_compress_context_via_codex_app_serverrW   rv   re   rW  r  lenr5   r   r   r(  COMPACTION_STATUSr	   r3  ri   rb   r   r   r   r   r4  rZ   r  r  r   get_compression_lock_holderr  _emit_warninghasattrr  r   r   r   r   r  on_pre_compressr   r4   r
   r  r   r:   r  copydeepcopyr   BaseExceptionr   r  errorrb  r  _todo_storeformat_for_injectionr:  r  r   r~   r  r   ry  rl  r  r  r  _invalidate_system_promptr>   r  commit_memory_sessionarchive_and_compactr   _flushed_db_message_idsr   _flush_messages_to_session_dbget_session_titleend_sessionr   nowstrftimegateway.session_contextr  r   environhermes_loggingr  _session_db_createdcreate_sessionr  _session_init_model_configreopen_sessionhermes_cli.goalsr	  get_next_title_in_lineageset_session_titleupdate_system_prompt_last_flushed_db_idxreplace_messages_flushed_db_message_session_idlocalsr  r  on_session_switchr  
log_prefixr2  r  r   r  last_compression_rough_tokenslast_prompt_tokenslast_completion_tokens%awaiting_real_usage_after_compression$_verify_compaction_cleared_thresholdtools.file_toolsr  )\r   r[  r  r  r  r   r   r  r  _attempt_started_at_attempt_id_trigger_source_codex_fence_enteredexisting_promptblocked_pre_msg_countr  compacted_in_place_compaction_status_try_acquire_lock_lock_lookup_error#_legacy_session_db_without_lock_apiru   	_lock_ttl_lock_refresh_interval_lock_acquired	_lock_err_release_errexisting_existing_spr  _parent_already_rotated_session_errrj   _activity_heartbeatr   
_maybe_ctxr   compress_kwargsengine_namemessages_before_compressionr  _compress_exc_commit_fence_entered_compression_made_progress_compression_used_fallback_errsummary_error_aux_fail_model_aux_fail_err_aux_keytodo_snapshotr  r  _tail	_stripped_probe_snapshot_textcached_system_promptnew_system_prompt_session_commit_succeededr   current_idxpersisted_history	old_titler  r  r  _cs_errr	  	_goal_err	new_titlee_old_sid_is_boundary"_context_engine_boundary_committed_boundary_parent_me_err_cc_cc_msg_compressed_estrecord_boundaryr  _commit_statusr  r  r  r  r  r  r  r  s\   `                                                                                   @@@@@@@@r   compress_contextr  W  sJ!   T 	*LWU$H$OOPPL JKKK 04E,/3E, .2E*.***,,"K"'3hhVO(3%(*G%*0b-J
 J
 	 	 	 	
     uj$''+===$##/#<#<#>#> ' 1")%1H$"O"O& Q&+&@&@&P&PO00	-9+   $ -**,,,,-# -**,,,,-  --e.FGGG)**,
 

 G 	-)A!B!B 	-%e-DdKKO" M"'"<"<^"L"L_,, 5<eDD 6 	,E22215.]]N GE#95AABBH 
KKZ"FN -<=9ek	   + 	
@$.'(+#
 
 
 "&&8!9!9 /-...$	) 	) 	) 	) 	) 	) 	) 	)B umT22H &BI"&L .2*/' .2E*	%2S3 3//  	% 	% 	%!$	%%.Q%)$,$I! 122 )2J* *&  ) ) )%("""""")'%)H%PPYTYZZ		z"   			$U,PRVWW@DO	/66)  LNN?4 233<>P  
 #NN&  Lu@$GG9TT9B66    "NN'!2!2|" " "  ' ' '55iNNNN    LLR$       
  $NtI7  
 "'+',  +	* #??	JJ       NNU8  
  L
 6>5EE2uBDIIYVV;D8''-   
 !   D"5*A4HHL J$99.II535STT h,IIYfIggg   /.'&.    +*,,,\))NN N N N N N N N N N N* 		*&I)' '##  	* 	* 	*NN=\""+   MOOO"5*A4HHL J$99.II\)))))))	* # 
	*KK+  
 MOOO"5*A4HHL J$99.II\))  --
-j999,
 

 G 	-!4!4 	-MOOO%e-DdKKO" M"'"<"<^"L"L_,,CGFF#<& O !!###   	"2BB8LL
j#.. I%<Z%H%HN    .77(#)
 
 
 !! 	&6o&M&M!(U-..7 K H$OO  AL=@   '+mH&=&=#;EBBHHJJ [==_==

    *$$%ABBB"&+*#"EtM':':'CEE	
 	
 	
 	
 	 	$ *$$%DEEE *$$%DEEEE + "t-
 &*E,.OQVWW&
 &
" &*E,.KUSS&
 &
" 5+-EuMM 	  u79NPTUUhYh5"EtLLPTTT>BE;''TD T T T  
  'u.EtLL# N#(#=#=n#M#ML32"+!* 8:OQUVV 98	 	 	 	  -L	-MOOO$ -**,,,,-$ -**,,,,-S  444666"m,GHHKKZ *F   #5*A4HHL J$99.II/.'&+    MOOO\)\	-MOOO$ -**,,,,-$ -**,,,,-_  	*LLD *F  
##X       "5*A4HHL J$99.IIMOOO\)v	-MOOO$ -**,,,,-$ -**,,,,-y #$0$=$=$?$?!( .$$.  
 38/&u.EtLL# N#(#=#=n#M#ML32"+!*":    -J	-MOOO$ -**,,,,-$ -**,,,,-M   8:OQUVV 	uA4HHMYY:G7##:} : : :   &e&>@_aeffO#E$<>]_cddM 	+];5"BDIIXUU;CE8''L_ L L)<_L L L   )>>@@ 0	 IHHHHHF ",Z^T"B"B
2 
  UYYv%6%6&%@%@6uyy7K7KLL	 16   %.y!(00 " &i55+:C+.}...* #
 (?'>!>( (E)$ "FF%))I"6"666}#	::@ @%'' 7
 (5E)$8<E45!F !!",04# #   
 	):>>>$:''))) !,0$77?6u>RSS @ !5*>E'' % : :> J J*;E'$)!' E	u$LCu
 ++H555 Y\& %99%:JJWWW#7L 58EEE1 *.&&" #*%1Ld"S"SK &k377"====H===== !+.. "	 &;;$1B <     %    !& 1 C CEDT U UI%11%2BMRRR%*%5N*2,..*A*A/*R*R'k'kUYU_UaUaUefhghfhUi'k'kE$KRRRRRR..u/?@@@@$ K K K:?:J
#6777KFFFFFF++E,<====$   05E--)88','7#(>#cRZ^^D[]b5c5c"'+).)I.< 9     % % % % T#^  
 ,:(OVVVVVV2253CDDDD( O O O>C>NBJ':;;;O!JJJJJJ//0@AAAA( ! ! ! D!!!-<<^LLLL( ! ! ! D!)- 591K%L 15E-#6L
]LLLLLL//@PYfggggg$ ] ] ]%PR[\\\\\\\\] ! \\(-(9(S(ST](^(^I!-??@PR[\\\\ *I6 \ \ \"LL)WYZ[[[[[[[[\ !66u7GIZ[[[ 12E..
 %66u7GTTT14ZE.;@;KE85 5'15 5 5E1
 -1)) u u u,2HHLL9I,J,J,R[c,Ryyi} 88<< 0119(9NN2383C3JsA   
 NN#prstttu& 88<< 011H~~1-F .
NN00 	+ $=u'7=2 . 	0 >#(#3#9r#3     <#(#3#9r#3   		X  5 %77$*&6(	 8     	X 	X 	XLLMwWWWWWWWW	X &8!88# G G G G G  *1E&w''' 5*D11 
	P	P$$%7 % 4""'"2&.n" ().)A)S: :      P P PKQOOOOOOOOP 4F0*<'
 8+1r+%
 
 

 BQ >68 3:; 7IM F
 & 	U%U-..- O
 (( U,"<    
 QU(M
	999999W%%%% 	 	 	D	 	l&J""	
 	
 	

 )58u(u(u  |E+*(%  #DDD '&
	
 
	
 
	
 
	
 ,,	-MOOO$ -**,,,,-$ -**,,,,-	-MOOO$ -**,,,,-$ -**,,,,-s  (B. .
B;:B;E E2L 
LLL %M 
MMM  N NN7Q 
SQ('S(
R2RSR6SSS4 4TT
U   
U-,U-0W
 

WWX 
Z)A$ZZZ72c *>_) (c )
_63c 5_66Cc e 
d0Ad++d00e e&,AA]? 	B"j4 +
A]? 6
jj14k  BA]? 
m66n$A]? 7o A]? 
oA]? o8A]? 
p55qB
A]? 
s>>tJ
A]? %BAO AAA AAO A
AA*A'AO A)AA*A*BAO DAD DAO DAD:D7AO D9AD:D:AO D>AE EAO E
AE"EAO E!AE"E"
AO E-AAG G AO G
AJG#AJG/AHHAJHAH'H$AJH&AH'H'AJH+AIIAJI
AIIAJIAIIAJIAI.I-AJI.
AI;I8AJI:AI;I;AJJAJJAO JAJ4 J3AO J4
AK#J>AKKAO KAK#K#AO K):AL$ L#AO L$AML5AMMAO MAMMB AO OA]? O
AQ8O%B	AQ3Q.A]? Q3AQ8Q8B
A]? T.AT2 T1A]? T2
AU!T<AUUA]? UAU!U!AA]? V14AW& W%A]? W&
AXW0AXXA]? XAXXB/A]? [A[ [A]? [
A[$[!A]? [#A[$[$AA]? ]
A]#]#A]<]?A^?^
A^#^A^?^#A^<^<A^?r  c          
     b    t          t           dd          pd                                          }|dvrd}|st|dk    rnt                              d|t           dd          pdt          |          |r|d	nd
           t           dd          }|s                     |          }||fS t           dd          }|mt                              dt           dd          pdt          |          |r|d	nd
           t           dd          }|s                     |          }||fS t                              dt           dd          pdt          |          |r|d	nd
           	                      t                     n# t          $ r Y nw xY wdd% fd}	d}
	 t                                                     }
|                                }n0# t          $ r# |
|
                    d            |	              w xY wt          |dd          st          |dd          r|
                    d           n|
                    d           t          |dd          r-	 |                                 n# t          $ r Y nw xY wd _        t          |dd          st          |dd          re	                      d|j                    n# t          $ r Y nw xY wt           dd          }|s                     |          } |	             ||fS 	 ddlm}m}  | ||d           t-           j        d          r | |           n,# t          $ r t                              dd           Y nw xY w	 dd lm}  ||           n# t          $ r Y nw xY wt                              d!t           dd          pdt          |d"d          pd#t          |d$d          pd#           t           dd          }|s                     |          } |	             ||fS )&a7  Route compaction to Codex app-server for Codex-owned threads.

    Hermes' normal compressor rewrites the local OpenAI-style transcript.
    That does not shrink the actual Codex app-server thread context. For this
    runtime, ask Codex to compact its own thread and keep Hermes' transcript
    unchanged.
     codex_app_server_auto_compactionnative>   offhermesr  r  z\codex app-server compaction skipped: mode=%s force=false (session=%s messages=%d tokens=~%s)rx   Nr  r   r  r  _codex_sessionz_codex app-server compaction skipped: no active codex thread (session=%s messages=%d tokens=~%s)zFcodex app-server compaction started: session=%s messages=%d tokens=~%sFr   r   c                 2     rd S d t                     d S r  r  )r  r   s   r   r  zN_compress_context_via_codex_app_server.<locals>._complete_compaction_lifecycle  s*    # 	F#' e$$$$$rK   r  interruptedr%  r   should_retireu(   ⚠ Codex app-server compaction failed: r   )#_record_codex_app_server_compaction_record_codex_app_server_usageT)r  r   update_from_responsez#codex compaction bookkeeping failedr   r  z>codex app-server compaction done: session=%s thread=%s turn=%s	thread_idr+   turn_idrX   )r4   r   lowerr   r   r  r  r3  r  r   r   r   compact_threadr$  r   closer  r  r%  agent.codex_runtimer  r  r   r   r   rG  r  )r   r[  r  r  r  r   	auto_moderL  codex_sessionr  r]  resultr  r  r  r  s   `              @r   r  r  w  s     98DDP egg  333	 )Y(**2E<..8&MM$1@}   y	
 	
 	
 "%)@$GG 	I#88HHO((E#3T::M2E<..8&MM$1@}   y	
 	
 	
 "%)@$GG 	I#88HHO((
KKP|T**4fH -<=9	  ,----     %% % % % % % % DH;EBBHHJJ--//   *$$%ABBB&&(((	 v}e,, B0N0N B  !=>>>>  !@AAAv.. $	!!!! 	 	 	D	#v}e,, )0N0N )	I6<II     	 	 	D	!%)@$GG 	I#88HHO&&(((((K	
 	
 	
 	
 	
 	
 	
 	

 	,+'		
 	
 	
 	
 5+-CDD 	:**5&999 K K K:TJJJJJK555555!!!!    KKH|T**4fT**0b	4((.B	   e%<dCCO E44^DD""$$$_$$sf   3F 
FF*5G   -H/J 
JJ>K 
K)(K)#8M &NN	N 
N('N(i@  )max_dimensionapi_messagesr  r   c               T   | sdS 	 ddl m n3# t          $ r&}t                              d|           Y d}~dS d}~ww xY wdd}d}d dd!fd}d"d}d#d}| D ]}}t          |t                    s|                    d          }	t          |	t                    sD|	D ]5}
t          |
t                    s|
                    d          }|dk    ra|
                    d          } ||          } ||pd          \  }}|r't          |t                    r |||           |dz  }n|r|dz  }|dvr|
                    d          }t          |t                    r9|                    dd          } ||          \  }}|r||d<   |dz  }|r|dz  }t          |t                    r# ||          \  }}|r||
d<   |dz  }.|r|dz  }7|rt          
                    d|dz             |r!t                              d|dz             dS |dk    S )$u  Re-encode all native image parts at a smaller size to recover from
    image-too-large errors (Anthropic 5 MB, unknown other providers).

    Mutates ``api_messages`` in place. Returns True if any image part was
    actually replaced, False if there were no image parts to shrink or
    Pillow couldn't help (caller should surface the original error).

    Strategy: look for ``image_url`` / ``input_image`` parts carrying a
    ``data:image/...;base64,...`` payload, plus Anthropic-native
    ``{"type": "image", "source": {"type": "base64", ...}}`` blocks.
    For each one whose encoded size exceeds 4 MB (a safe target that slides
    under Anthropic's 5 MB ceiling with header overhead) or whose longest side
    exceeds ``max_dimension``, write the base64 to a tempfile, call
    ``vision_tools._resize_image_for_vision`` to produce a smaller data
    URL, and substitute it in place.

    Non-data-URL images (http/https URLs) are not touched — the provider
    fetches those itself and the size limit is different.
    Fr   )_resize_image_for_visionu6   image-shrink recovery: vision_tools unavailable — %sNi  @ data_urlr4   r   Optional[tuple]c                ^   	 ddl }ddl}|                     d          \  }}}|r|                     d          sdS ddlm} |                    |                    |                    |                              5 }|j	        cddd           S # 1 swxY w Y   dS # t          $ r Y dS w xY w)zReturn ``(width, height)`` of a base64 data URL, or None on failure.

        Soft-depends on Pillow; returns None (caller falls back to a
        bytes-only check) if Pillow is missing or the payload is corrupt.
        r   Nr   data:)Image)base64io	partitionrv  PILr  openBytesIO	b64decodesizer   )r  _b64_dim_io_dimheader_dr@  data_d	_PILImage_imgs           r   _decode_pixelsz:try_shrink_image_parts_in_messages.<locals>._decode_pixels:	  s"   
	%%%%    "*"4"4S"9"9Ha !4!4W!=!= t......0B0B60J0J K KLL !PTy! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! 	 	 	44	s;   8B AB =BB BB BB 
B,+B,urltuplec                   t          | t                    r|                     d          sdS t          |           k    }|rdnd}|s( |           }|dS t	          |          k    rdS d}d}	 |                     d          \  }}}d}|                    d          rZ|t          d          d                             d	d
          d                                         }|                    d          r|}ddl}	|		                    |          }
ddddddd
                    |d          }t          j        d|d          }	 |                    |
           |                                  t          |j                  |          }	 t          |j                                      d           nN# t$          $ r Y nBw xY w# 	 t          |j                                      d           w # t$          $ r Y w w xY wxY w|sdS |dk    rHt          |          t          |           k    rdS  |          }|t	          |          k    rdS |dfS  |          }|t	          |          k    r|dfS dS t          |          t          |           k    rdS |dfS # t$          $ r+}t&                              d|           d|dufcY d}~S d}~ww xY w)u  Return ``(resized_url, unshrinkable)`` for a data URL.

        ``resized_url`` is a smaller/dimension-correct data URL, or None when
        no rewrite was applied.  ``unshrinkable`` is True only when the image
        exceeded a constraint (byte-size or dimensions) and the resize failed
        to satisfy *that same* constraint — so the caller knows retrying is
        pointless even if a different image in the request shrank.
        r  rB   bytesNT	dimensionr   
image/jpeg;r   r   image/z.pngz.gifz.webpz.jpgz.bmp)z	image/pngz	image/gifz
image/webpr  z	image/jpgz	image/bmphermes_shrink_F)prefixsuffixdelete)	mime_typemax_base64_bytesr  )
missing_okr  u.   image-shrink recovery: re-encode failed — %s)r   r4   rv  r  r   r  splitr:   r  r  r~   tempfileNamedTemporaryFilewriter  r   r   unlinkr   r   r4  )r  needs_shrinktriggered_bydimsheaderr@  datamime	mime_part_b64rawr  tmpresizednew_dimsru   r  r  r  target_bytess                   r   _shrink_data_urlz<try_shrink_image_parts_in_messages.<locals>._shrink_data_urlL	  s    #s## 	3>>'+B+B 	; 3xx,.".8wwD 	' ">#&&D|"{4yyM))"{L&LD	2!mmC00OFAtD  )) %"3w<<==177Q??BHHJJ	''11 %$D!!!!..&&C#&$6  c$  -'u  C		#		22NN"%1"/	  NN))T)::::    DNN))T)::::    D "!zw&&w<<3s88++%: *>'22'CMMM,I,I%:~% &~g..H#x==M11"E>)!z 7||s3xx''!zE>! 	2 	2 	2NNKSQQQT11111111	2s   2CJ* 
A
G (F> =J* >
GJ* 
GJ* H	(G98H	9
HH	HH		J* &J* 8 J* J* #J*  J* &J* *
K4 KKKr  r   r   c                h   t          | t                    r|                     d          dk    rd S |                     d          }t          |t                    r|sd S t          |                     d          pd                                          }|                    d          sd}d| d| S )	Nre   r  r  
media_typer  r  r  z;base64,)r   r   r~   r4   r:   rv  )r  r  r  s      r   _source_to_data_urlz?try_shrink_image_parts_in_messages.<locals>._source_to_data_url	  s    &$'' 	6::f+=+=+I+I4zz&!!$$$ 	D 	4L11A\BBHHJJ
$$X.. 	&%J1z114111rK   r   r   c                8   |                     d          \  }}}d}|                    d          rZ|t          d          d                              dd          d                                         }|                    d          r|}d| d	<   || d
<   || d<   d S )Nr   r  r  r  r   r   r  r  re   r  r  )r  rv  r  r  r:   )r  r  r  r@  r  r  	candidates          r   _write_data_url_to_sourcezEtry_shrink_image_parts_in_messages.<locals>._write_data_url_to_source	  s    ",,S114!
W%% 	's7||}}-33C;;A>DDFFI##H-- '&
!v)|vrK   rg  re   imager+   r   >   	image_urlinput_imager  zGimage-shrink recovery: re-encoded %d image part(s) to fit under %.0f MBi   u   image-shrink recovery: %d oversized image part(s) could not be shrunk under %.0f MB — not retrying (would re-send rejected payload))r  r4   r   r  )r  r4   r   r  )r  r   r   r   )r  r   r  r4   r   r   )tools.vision_toolsr  r   r   r4  r   r   r~   r\  r4   r   )r  r  ru   changed_countunshrinkable_oversizedr  r  r  rF  rg  rk  ptyper  r  r  unshrinkableimage_valuer  r  r  s    `               @@@r   "try_shrink_image_parts_in_messagesr  	  sm   0  u???????   OQTUUUuuuuu #L
 M    $g2 g2 g2 g2 g2 g2 g2 g2 g2R	2 	2 	2 	2	 	 	 	  '0 '0#t$$ 	'')$$'4(( 	 !	0 !	0DdD)) HHV$$E(++))&11(8(8(C(C% 0z&$77 0--fg>>>!Q&MM! 0*a/*888((;//K +t,, 0!ooeR00(8(8(=(=% 0)0K&!Q&MM! 0*a/*K-- 0(8(8(E(E% 0(/D%!Q&MM! 0*a/*C!	0F  
U<;7	
 	
 	
  

 	U"LK$@	
 	
 	

 u1s    
A<A)r  r   COMPACTION_STATUS_MARKERrW  rZ  r  r  )r   r   r   r   )r   r   r   r(   )r   r   r3   r4   r   r5   )r_   r   r   r5   )rj   r   r   r   )rw   r   rx   r4   r   r5   )r   r   r   r   r   r4   r   r4   r   r   r   r   )r   r   r   r5   )r   r   r   r4   )r   r   r   r   r   r   r   r5   r   r4   r   r   r   )r   r   r[  r\  r]  r^  r   r^  )re  r   r   r4   )re  r   r   r5   )rg  r   r   r   )r<   r   r  r   r   r   )r[  r\  r  r   r   r   )r  r\  r  r\  r   r   )r   r   r  r4   r  r4   r   r5   )r   r   r  r4   r  r4   r   r   )r   r   r  r5   r   r5   )r   r   r[  r\  r  r4   r  r   r  r4   r   r   r   r5   r  r5   r  r  r   r  )r   r   r[  r\  r  r   r  r   r  r4   r   r5   r   r  )r  r\  r  r   r   r5   )Ir]   
__future__r   r"  rf   r   loggingr   r   r  r   r   rC   r   pathlibr   typingr   r   r   agent.context_enginer	   r
   r.  r   	getLoggerrZ   r   r  r  r   r   #PRE_API_COMPRESSION_STATUS_TEMPLATE%PREFLIGHT_COMPRESSION_STATUS_TEMPLATEIDLE_COMPACTION_STATUS_TEMPLATE+COMPRESSION_RETRY_TOO_LARGE_STATUS_TEMPLATE*COMPRESSION_RETRY_MESSAGES_STATUS_TEMPLATE(COMPRESSION_RETRY_TOKENS_STATUS_TEMPLATE1COMPRESSION_RETRY_CONTEXT_REDUCED_STATUS_TEMPLATE)CONTEXT_OVERFLOW_BLOCKED_WARNING_TEMPLATEformat"ROUTINE_COMPRESSION_STATUS_SAMPLESr2   r>   r@   ri   rv   r   r   r   r   r   r   r   rW  rZ  rd  rw  rl  ru  ry  r  r  r  r  r  r  r  r  r  r  r  __all__r^   rK   r   <module>r     ss   8 # " " " " "      				                    ' ' ' ' ' ' ' ' ' '        ? > > > > >		8	$	$ 0 c'ccc  R V V V V,L $
: &
,  
 Z , D + H ) S 21 * '..f.==)00&0QQ#**V*LL/66faUV6WW.55Rr5JJ,3363PP5<< =  & "   8" " " "J7 7 7 7 7 7 7 7t   .X X X X"
 
 
 
& !%"N "N "N "N "N "NJ1 1 1 1$   ($U $U $U $UN*; *; *; *; *; *; *; *;ZL L L L L L L L^y
 y
 y
 y
x   * (,$ $ $ $ $N 
 
 
 
 N N N N.   >   :&: &: &: &:R   . 7 %
   :B B B B(
 
 
 
$ $(!%.359]- ]- ]- ]- ]- ]-J! $(N% N% N% N% N% N%h | | | | | |~  rK   