
    epj                      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ZddlmZ ddlmZ ddlmZmZmZmZmZmZ ddlmZ ddlmZmZ dd	lmZ dd
l m!Z!m"Z"  ej#        e$          Z% e&e!j'        e!j(        h          Z)dZ*de* dZ+dZ,ddZ-dZ.dZ/dZ0dZ1dZ2dZ3dZ4dZ5e+e.6                    d          e/6                    dd           e06                    d!d"          e16                    d#d$d%&          e26                    d'd()          e36                    d#d)          e46                    dd#*          fZ7dd,Z8dd0Z9d1Z:d2Z;dd5Z<ddd6dd<Z=dd?Z> G d@ dA          Z?dBZ@dCZAdaB ejC                    ZDdDZEdEZF ejC                    ZGdaH G dF dGeI          ZJddHZKdddIZLdJ ZM	 dddNZNdddddOdd`ZOddbZPdcddddfZQddiZRddjddpZSddqZTddtZUdduZVddvZWddZX G d d          ZY G d d          ZZddZ[ddZ\	 dddZ]dZ^ddZ_dZ`ddZaddZbddZcddZdddZedZfddZgddZhddZidddddddddZjddddddZkddddZlg dZmdS )u6
  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.

Thread-safety contract for extension points (#76354 review)
------------------------------------------------------------

When the host-level progress-aware timeout is enabled (the default:
``compression.context_timeout_seconds > 0``), the WHOLE compression pass —
including plugin/legacy **context engines** (``compress()`` /
``on_session_start`` / boundary callbacks) and **memory providers**
(``on_pre_compress`` / ``on_session_switch``) — runs on a pooled daemon
thread, not the conversation thread. Extension authors must assume:

* Calls may arrive on an arbitrary pooled thread; do not rely on
  thread-affinity or ``threading.local`` state shared with the caller.
* The input message list is a private deep snapshot owned by the worker;
  engines MAY mutate it in place (legacy contract preserved), and that
  mutation is invisible to the live conversation unless the pass commits.
* Publication to caller-visible / durable state happens ONLY on an admitted
  commit (:class:`CompressionCommitFence`); after a host timeout the still-
  running engine's work is discarded.
* Two compression passes never run concurrently for one session (durable
  per-session lock), but passes for DIFFERENT sessions may run concurrently
  on pool siblings — engine/provider instances shared across sessions must
  be thread-safe or internally locked.
    )annotationsN)datetime)Path)AnyCallableDictListOptionalTuple)AuxiliaryExplicitCancellation)#automatic_compaction_status_messagesanitize_memory_context)estimate_request_tokens_rough)ActivityProvenancenormalize_activity_provenancez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     D/home/thesage/.hermes/hermes-agent/agent/conversation_compression.py_emit_compaction_doner    h   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) r1   _memory_enabledFmemoryr1   _user_profile_enableduser)r   format_for_system_promptr   )r   storer3   r5   s       r   _builtin_memory_prompt_snapshotr8      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)r3   r5   T)r8   tools.memory_toolr=   r   zipstrip)r   r9   snapshotr=   targetblocks         r   &_cached_prompt_reflects_builtin_memoryrD      s     /u55Hu:::::::   uu/::   		 M))uu *!&)]:: 55 ; 4s    
**)_previous_summary_summary_has_user_turncompression_count_last_compression_savings_pct_ineffective_compression_count_anti_thrash_recovery_deadline_fallback_compression_streak$_verify_compaction_cleared_threshold_last_compression_made_progress_summary_failure_cooldown_until_cooldown_persist_failed_last_summary_error_consecutive_timeout_failures_last_summary_dropped_count_last_summary_fallback_used_last_compress_aborted_last_summary_auth_failure_last_summary_network_failure_last_aux_model_failure_error_last_aux_model_failure_model_summary_model_fallen_backsummary_model_last_compression_telemetry_active_compression_telemetry_compression_telemetry_seed)rN   rP   rO   
compressordict[str, Any]c                    	 t          |           n# t          $ r i cY S w xY wfdt          D             }t          j        |          S )a'  Copy only mutable bookkeeping owned by one compression attempt.

    The explicit allow-list avoids copying provider clients, SessionDB handles,
    locks, and plugin resources. Missing fields are intentionally ignored so
    legacy and third-party compressors keep their existing contract.
    c                *    i | ]}|v ||         S  rb   ).0namevaluess     r   
<dictcomp>z6_snapshot_compressor_attempt_state.<locals>.<dictcomp>&  s0       6>> 	fTl>>    )vars	TypeError _COMPRESSOR_ATTEMPT_STATE_FIELDScopydeepcopy)r^   selectedre   s     @r   "_snapshot_compressor_attempt_statern     su    j!!   			   4  H ="""s    ""durable_cooldown_authoritativedurable_cooldown_staterA   rp   Optional[bool]rq   Optional[dict[str, Any]]c               `   d|v r|dur|du s$t          |                    dd                    st          |                               d          }t          |                               d          }|u|rr|du r_t          t	          |          dd          }t          |          r|t          d	           |||t          j        |                     n	 t          |d         pd
          }t          d
|t          j                    z
            }t          j                    |z   }	|                    d          }
|dk    r<t          t	          |          dd          }t          |          r ||||	|
           n9t          t	          |          dd          }t          |          r |||           n,# t          $ r t                              dd           Y nw xY wt          j        |          }|                                D ]\  }}t#          | ||           dS )zERestore the safe per-attempt snapshot after a pre-commit hard cancel.rN   FTrO   _session_db_session_idN(restore_compression_failure_cooldown_rowz6exact compression cooldown rollback API is unavailable        rP   r   #record_compression_failure_cooldown"clear_compression_failure_cooldownz0compression cooldown persistence rollback failedr   )r;   getrh   r   typecallableRuntimeErrorrk   rl   floatmaxtime	monotonicr   r   r   itemssetattr)r^   rA   rp   rq   
session_db
session_idrestorerdeadline	remainingdurable_deadlinedurable_errorrecorderclearerrestoredrd   values                   r   !_restore_compressor_attempt_stater   0  s    	*X55*%77*d22%?GGHH 3 *%%))-88
*%%))-88
!j!-55"$$> 
  )) -C-K&P  
 M"899   "$ !BCJs   H !$CDN4D4D)D E EI'+y{{Y'>$$,LL1F$G$GM 1}}#* ,,A $ $
 $H-- $H * * 0 -	   #* ,,@ # #
 $G,, <#GJ
;;;     LLJ!% !      }X&&H~~'' ) )e
D%(((() )s   !C!G &G,+G,attempt_snapshot/tuple[Optional[bool], Optional[dict[str, Any]]]c                   	 ddl m} t          | |          sdS t          |           }|                    d          }|                    d          }|t          t          |          dd          nd}||sdS t          |          sdS  |||          }t          |t                    st          d	          |
                    | d
           n3# t          $ r&}t                              d|           Y d}~dS d}~ww xY wt          | dd          }	|	d
ur|	dfS t          |           }t          D ]#}
|
|v rt          j        ||
                   ||
<   $d
t          j        |          fS )a  Refresh and snapshot built-in durable cooldown state under the lease.

    Third-party compressors are deliberately not invoked here: arbitrary plugin
    callbacks must not run while the session lease is held. A durable read
    failure returns ``False`` so rollback cannot mistake unknown durable state
    for an authoritative empty row and clear it; an unavailable legacy API
    returns ``None`` and preserves the compatibility path.
    r   ContextCompressorNNru   rv   N$get_compression_failure_cooldown_row)FNz3raw compression cooldown snapshot must be a mappingT)refreshz5authoritative compression cooldown capture failed: %s(_last_cooldown_refresh_was_authoritative)agent.context_compressorr   
isinstancerh   r{   r   r|   r}   dictri   'get_active_compression_failure_cooldownr   r   r   !_COMPRESSOR_COOLDOWN_STATE_FIELDSrk   rl   )r^   r   r   re   r   r   
raw_readerdurable_stateexcauthoritativerd   s              r   +_capture_authoritative_cooldown_under_leaser     s    >>>>>>*&788 	:j!!ZZ..
ZZ..

 % Z  "H$    	 Z:
## 	; #
:z::-.. 	SQRRRAA 	B 	
 	
 	
 	
    LcRRR{{{{{ > M D  d""*F1 A A6>>%)]6$<%@%@T"}----s*   C AC ;C AC 
DC??Dc                      e Zd ZdZddZddZddZdddZddZdddZ	ddZ
ed d            Zed d            Zd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                   t          j                    | _        d| _        d| _        t          j                    | _        d| _        t          j                    | _        d | _	        d| _
        t          j                    | _        d S NF)	threadingLock_lock
_cancelled_commit_startedEvent_commit_phase_admission_revoked_lock_release_guard_cancelled_lock_release!_cancelled_lock_release_requestedr   r   _last_progressselfs    r   __init__zCompressionCommitFence.__init__  sv    ^%%
$ '_.. #( $->#3#3 EI$16. #n..rg   c                6    t          j                    | _        dS )a  Record forward progress (e.g. a streamed summary token arriving).

        Called from the compression worker thread; read by async waiters via
        :meth:`seconds_since_progress`. A bare float store is atomic in
        CPython, so no lock is needed.
        N)r   r   r   r   s    r   touch_progressz%CompressionCommitFence.touch_progress  s     #n..rg   r   c                T    t          dt          j                    | j        z
            S )z8Seconds since the worker last reported forward progress.rx   )r   r   r   r   r   s    r   seconds_since_progressz-CompressionCommitFence.seconds_since_progress  s"    3((4+>>???rg   Ncancel_eventr   r;   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)r   r   setr   r   r   s     r   cancel_before_commitz+CompressionCommitFence.cancel_before_commit  s     Z 	 	# + $$&&&		 	 	 	 	 	 	 	
 #DO'  """	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	s   AAA#&A#rr   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)r   acquirer   releaser   r   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                T   | j                                          | j        s*| j        s#|^t	          |                                          r=d| _        | j                                          | j        r|                                  dS d| _        | j	        
                                 dS )z?Atomically admit commit unless a hard cancellation already won.NTF)r   r   r   r   r;   is_setr   "release_cancelled_compression_lockr   r   r   r   s     r   begin_commitz#CompressionCommitFence.begin_commit  s    
O	&	 (T,2E2E2G2G-H-H("DOJ   & :
 779995# 	   trg   c                    | j                                          | j                                         | j        r|                                  dS dS )z8Leave a commit boundary entered by :meth:`begin_commit`.N)r   clearr   r   r   r   r   s    r   finish_commitz$CompressionCommitFence.finish_commit/  sZ      """
" 	6 3355555	6 	6rg   c                4    | j                                         S )a  Lock-free read: an admitted commit has begun and not yet finished.

        Safe to call from the host while the worker holds the fence lock for
        the whole commit (a hung SessionDB write). Hosts use this to reach
        their overrun-warning loop WHILE the commit is blocked instead of
        spinning on ``try_cancel_before_commit`` (which needs the lock the
        worker retains until ``finish_commit``).
        )r   r   r   s    r   commit_in_flightz'CompressionCommitFence.commit_in_flight=  s     !((***rg   c                    | j         p| j        S )z7True after cancellation won before the commit boundary.)r   r   r   s    r   is_cancelledz#CompressionCommitFence.is_cancelledI  s     9$"99rg   c                    d| _         | j                            d          rN	 |                                  | j                                         dS # | j                                         w xY wdS )u!  Revoke FUTURE commit admission without blocking on the fence lock.

        #76354 review F2: every host unwind path (KeyboardInterrupt, task
        cancellation, unexpected exception while waiting) must guarantee a
        detached worker cannot later enter the commit boundary and mutate
        durable/session state. The flag store is lock-free: a commit that is
        ALREADY in flight cannot be safely abandoned (the invariant "commit
        never abandoned mid-mutation" holds), but no NEW commit will be
        admitted after this call — ``begin_commit`` re-checks the flag under
        the fence lock.

        Round-2 #1 (durable-lease timing): the worker's holder-qualified
        lease release (F4) must NOT run while an admitted commit is still
        mutating SessionDB — a second compressor could otherwise acquire the
        durable lock mid-commit and interleave with the first commit's
        writes. The release decision is therefore made under the fence lock:

        - non-blocking acquire succeeds → no commit is in flight (an
          admitted commit RETAINS the lock until ``finish_commit``), so the
          lease is released immediately, while still holding the lock so a
          concurrent ``begin_commit`` cannot slip in between the check and
          the release (it would be refused anyway — the flag is already set).
        - acquire fails → the lock holder is either an in-flight commit or a
          transient boundary (lock-setup / cancel admission). Defer: the
          release then runs in ``finish_commit`` (after the mutation fully
          completes) or on the ``begin_commit``-refusal path, whichever the
          worker reaches first. Both are idempotent with the worker's own
          outer cleanup because the DB release is holder-qualified.
        TFr   N)r   r   r   r   r   r   s    r   revoke_commit_admissionz.CompressionCommitFence.revoke_commit_admissionN  s|    < #':u-- 	%%77999
""$$$$$
""$$$$		% 	%s   A A.c                    | j                                          | j        s| j        r| j                                          dS dS )al  Fence durable-lock acquisition and release-hook publication.

        The caller keeps the fence until it has either published the exact
        holder-qualified release hook or established that no lock was
        acquired. A timeout cannot therefore win in the gap between acquiring
        the durable lock and making its cancellation cleanup callable.
        FT)r   r   r   r   r   r   s    r   begin_lock_setupz'CompressionCommitFence.begin_lock_setup~  sJ     	
? 	d5 	J   5trg   c                8    | j                                          dS )z@Leave a lock setup boundary entered by :meth:`begin_lock_setup`.N)r   r   r   s    r   finish_lock_setupz(CompressionCommitFence.finish_lock_setup  s    
rg   r   Callable[[], None]c                x    | j         5  || _        | j        }ddd           n# 1 swxY w Y   |r
 |             |S )zPublish the timed-out worker's holder-qualified lock release.

        Returns whether cancellation cleanup was requested before publication.
        In that race, the release runs synchronously before this method returns.
        N)r   r   r   )r   r   	requesteds      r   register_cancelled_lock_releasez6CompressionCommitFence.register_cancelled_lock_release  s     % 	? 	?+2D(>I	? 	? 	? 	? 	? 	? 	? 	? 	? 	? 	? 	? 	? 	? 	?  	GIII   #''c                f    | j         5  | j        |u rd| _        ddd           dS # 1 swxY w Y   dS )z>Forget ``release`` after the worker's normal cleanup finishes.N)r   r   r   r   s     r   clear_cancelled_lock_releasez3CompressionCommitFence.clear_cancelled_lock_release  s    % 	4 	4+w66/3,	4 	4 	4 	4 	4 	4 	4 	4 	4 	4 	4 	4 	4 	4 	4 	4 	4 	4s   &**c                |    | j         5  d| _        | j        }ddd           n# 1 swxY w Y   | |             dS dS )aE  Release the cancelled worker's lock without finalizing its clients.

        Callers invoke this only after cancellation won (fence cancelled or
        admission revoked). A request that races ahead of lock-hook
        publication is retained and fulfilled synchronously when the worker
        publishes the hook.
        TN)r   r   r   r   s     r   r   z9CompressionCommitFence.release_cancelled_compression_lock  s     % 	3 	359D22G	3 	3 	3 	3 	3 	3 	3 	3 	3 	3 	3 	3 	3 	3 	3 GIIIII r   r   r   )r   r   N)r   r   r   r;   )r   rr   r   r;   )r   r   r   r;   )r   r   r   r   )__name__
__module____qualname____doc__r   r   r   r   r   r   r   propertyr   r   r   r   r   r   r   r   rb   rg   r   r   r     sr        "/ "/ "/ "/H/ / / /@ @ @ @    "! ! ! !     .6 6 6 6 	+ 	+ 	+ X	+ : : : X:#% #% #% #%`         4 4 4 4     rg   r   g      ^@g     @g      >@   c                      e Zd ZdZdS )!CompressionExecutorSaturatedErrorz@All compression pool slots are occupied; submission was refused.N)r   r   r   r   rb   rg   r   r   r     s        JJJJrg   r   c                     t           5  t          t          k    r	 ddd           dS t          dz  a	 ddd           dS # 1 swxY w Y   dS )z9Reserve one bounded compression-pool admission slot (F6).NFr$   T)_compress_admission_lock_compress_admitted_count_COMPRESS_EXECUTOR_MAX_WORKERSrb   rg   r   _try_admit_compression_jobr     s     
"  #'EEE        	!A% 	                 s   ??AAc                p    t           5  t          dk    r
t          dz  addd           dS # 1 swxY w Y   dS )z?Free an admission slot (future done-callback or failed submit).r   r$   N)r   r   )_futures    r   _release_compression_admissionr     s     
" * *#a''$)$* * * * * * * * * * * * * * * * * *s   +//c                     t           } | | S ddlm} t          5  t            |t          d          a t           cddd           S # 1 swxY w Y   dS )zBReturn the process-wide compress-timeout DaemonThreadPoolExecutor.Nr   )DaemonThreadPoolExecutorzcompress-ctx-timeout)max_workersthread_name_prefix)_compress_timeout_executortools.daemon_poolr   _compress_timeout_executor_lockr   )executorr   s     r   _get_compress_timeout_executorr     s     *H::::::	( 	* 	*%- *B)A:#9* * *& *	* 	* 	* 	* 	* 	* 	* 	* 	* 	* 	* 	* 	* 	* 	* 	* 	* 	*s    AA
A
compression_cfgOptional[dict]Tuple[float, float]c                h   t           }t          }| }|j	 ddlm}  |            }t	          |t
                    r|                    di           ni }t	          |t
                    r|ni }n# t          $ r i }Y nw xY wt	          |t
                    r|                    d          }|*	 t          |          }|}n# t          t          f$ r Y nw xY w|                    d          }	|	0	 t          |	          }|dk    r|}n# t          t          f$ r Y nw xY w|dk    rt          ||          }||fS )a  Return ``(idle_timeout_seconds, total_ceiling_seconds)``.

    ``idle_timeout_seconds <= 0`` disables the owned progress-aware wrapper.
    The ceiling is clamped to at least one idle window when the idle budget
    is positive, matching gateway hygiene semantics.
    Nr   )load_configcompressioncontext_timeout_secondscontext_total_ceiling_seconds)DEFAULT_CONTEXT_TIMEOUT_SECONDS%DEFAULT_CONTEXT_TOTAL_CEILING_SECONDShermes_cli.configr   r   r   r{   r   r   ri   
ValueErrorr   )
r   idleceilingcfgr   rawmayberaw_idleparsedraw_ceilings
             r   $resolve_context_compression_timeoutsr    s    +D3G
C
{	555555+--C2<S$2G2GOCGGM2...RE%eT22:%%CC 	 	 	CCC	#t 77455xz*   gg=>>"{++A::$Gz*   axxgt$$=s6   AA+ +A:9A:*B< <CC+D DD)
on_timeouton_commit_overrunfencetelemetry_agentworker4Callable[[CompressionCommitFence], Tuple[list, str]]messageslistsystem_prompt_fallbackidle_timeout_secondsr   total_ceiling_secondsr  /Optional[Callable[[float, float, float], None]]r  (Optional[Callable[[float, float], None]]r   Optional[CompressionCommitFence]r  Tuple[list, str]c        	        	    |dk    rt          d          dfd}	||nt                      }t          t          |          t          |                    }
t          |          }ddlm} t                      }t                      sSt          	                    dt                     |%t          |t          j                    d	d	d
            |	            fS d fd}	 |                     ||          |          }n# t          $ r t!                        w xY w|                    t                      t          j                    }d}	 	 t          j                    |z
  }|
|z
  }|dk    rn|                                }t'          t          ||z
  d          |          }	 |                    |          }d}||s|                                 S S # t,          j        j        $ rX t          j                    |z
  }|                                }||k     r%||
k     rt                              d|||
           Y Y nw xY w|                                 d}|6|j        rd}n,|                                }|t          j        d           |6|s
d}d}	 t          j                    |z
  }|
|z
  }|dk    rt'          t<          t          |
d                    }|dz  }|dk    rt          j	        nt          j        } |d||
z
  ||
           |s>|<d}	  |||
           n,# t@          $ r t          !                    dd           Y nw xY w	 |                    |          }d}||s|                                 S S # t,          j        j        $ r Y w xY wd}|"                                 t          j                    |z
  }|                                }|;	  ||||           nI# t@          $ r t          !                    dd           Y n!w xY wt          	                    d|||
            |	            f|s|                                 S S # |s|                                 w w xY w)u  Run ``worker(fence)`` under a sync progress-aware timeout.

    The idle budget is inactivity-based (same idea as gateway session hygiene):
    streamed summary progress via :meth:`CompressionCommitFence.touch_progress`
    extends the wait. A hard ceiling still bounds a degenerate trickle stream.

    When cancellation wins before the commit boundary, returns
    ``(messages, system_prompt_fallback)`` immediately and leaves the worker
    thread detached — the fence prevents a late commit from mutating session
    state. When the worker already entered the commit boundary, waits for that
    commit to finish and returns its result.

    Timeout budgets (``idle_timeout_seconds`` / ``total_ceiling_seconds``) cover
    the **pre-commit** wait only — the summary / stream phase before
    :meth:`CompressionCommitFence.begin_commit`. Once the worker holds the
    commit fence, SessionDB mutation is already in flight and cannot be safely
    abandoned without risking transcript divergence; the commit is therefore
    always allowed to complete. The commit-phase wait is still *bounded in
    increments* against the remaining total ceiling: if the commit runs past
    ``total_ceiling_seconds``, the overrun is logged loudly (escalating from
    WARNING to ERROR on repeat) and surfaced once via ``on_commit_overrun``,
    while the host keeps waiting in bounded slices until the commit finishes.
    The documented guarantee is: **summary phase bounded by the ceiling;
    commit phase logged + surfaced if it exceeds it** (never silently hung,
    never abandoned mid-commit).

    ``system_prompt_fallback`` may be a string or a zero-arg callable resolved
    only on the timeout path, so successful compression never pays for (or
    fails on) an eager prompt rebuild.
    r   zwrun_compress_context_with_progress_timeout requires idle_timeout_seconds > 0; call compress_context directly to disabler   r:   c                 :    t                     r
              S  S r   )r}   )r  s   r   _resolve_fallback_promptzLrun_compress_context_with_progress_timeout.<locals>._resolve_fallback_promptb  s)    *++ 	,))+++%%rg   N)propagate_context_to_threadu   Context compression pool saturated (%d workers busy) — refusing new compression this cycle and continuing without compression. Wedged workers are fence-cancelled and free their slot when they return; if this persists, check the summary provider health.abortedpool_saturated
started_atcommit_statussplit_statusfailure_classworker_fencer   c                d    | j         rt                              d           dfS  |           S )Nz<Skipping stale compression job: fence cancelled before startr1   )r   r   info)r+  r  r  s    r   _fence_gated_workerzGrun_compress_context_with_progress_timeout.<locals>._fence_gated_worker  sE    
 $ 	 KKN   R<vl###rg   FTg{Gzt?timeoutul   Context compression still streaming after %.0fs (last progress %.1fs ago) — extending wait (ceiling %.0fs)g?皙?r$      u   Context compression SessionDB commit still running %.1fs past the total ceiling (waited %.1fs, ceiling %.1fs); commit cannot be abandoned mid-flight — continuing to wait (check SessionDB health if this persists)z/compress_context commit-overrun callback failedr   z(compress_context timeout callback failedzpContext compression made no progress for %.1fs (total wait %.1fs, ceiling %.1fs); continuing without compression)r   r:   )r+  r   )#r  r   r   r   tools.thread_contextr#  r   r   r   warningr   #_emit_compression_attempt_telemetryr   r   submitBaseExceptionr   add_done_callbackr   minresultr   
concurrentfuturesTimeoutErrorr-  cancelr   r   sleep"_COMMIT_OVERRUN_WAIT_SLICE_SECONDSerrorr   r   r   )r  r  r  r  r  r  r  r  r  r"  r	  r  r#  r   r.  futurewait_startedhandled_exitwaitedremaining_ceilingsince_progress
wait_slicer:  	cancelledoverrun_surfacedoverrun_reportsr   logs   ```                         r   *run_compress_context_with_progress_timeoutrM  2  s<   T q  R
 
 	

& & & & & &
 &EE,B,D,DE%-..6J0K0KLLG%&&D A@@@@@-//H
 &'' 4
 +	
 	
 	
 &/>++'&.    113333
$ 
$ 
$ 
$ 
$ 
$ 
$''(;<<e
 
    &((( ;<<<>##L LU,	^%%4F '& 0 A%%
 #99;;ND>)5113D Jz::#~  	,
 ))++++	,} %2   ))L8!&!=!=!?!?!D((Vg-=-=KK* &   H" 	$(	 % !	6688I  
5!!!! "  9	  %O+))L8#f,	>> !$:GT**! !I $q(O*9Q*>*>FL  C$
 (	 	 	 , 	0A0M+/(--fg>>>>(   "LL!))- )     #]]9]==F#'L!J  	,
 ))++++	,I ")6    H	 00222!!L05577!
48888   >!       NN    11333 	,
 ))++++	,| 	,
 ))++++	,s   'D D!AQ% -G A'I
Q% 	I

CQ% "L/ .Q% /&MQ% MQ% N N%!Q% $N%%AQ% +O9 8Q% 9&P"Q% !P""+Q% %Q>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_staterP  objectr|   inspectgetattr_staticr   )rN  rP  missings      r   !_lock_api_is_absent_on_session_dbrW  B  s~    ******((MMY& &97 	
    uus   <? 
AATinclude_cooldownrY  c               4   di fdi fg}|r|                     ddddi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.!_load_fallback_compression_streak#_load_ineffective_compression_countr   r   r   TNz)compression guard refresh failed (%s): %s)insertr   r|   r}   r   r   r   )r^   rY  method_callsmethod_namekwargsmethodr   s          r   %_refresh_persisted_compression_guardsrb  Y  s    
-b1	.3L  
6D8IJ	
 	
 	
  , X XVj));== 		XF:(((((( 	X 	X 	XLLDkSVWWWWWWWW	XX Xs   	A%%
B/BBr   r   c                    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_reasonr  )r   r|   r}   r;   r{   )r   r   gettersessions       r   #_session_was_rotated_by_compressionri  r  s    T*%%}d;;FF ufZ,,G 	7KK
##4/	7KK%%6  rg   )r*  r'  r(  r)  r*  
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.r[   Neventcompression_attempt
attempt_id_compression_attempt_idr1   r     total_duration_msr(  r)  r*  chunkingFchunk_countr   fallback_usedrS   rX   z)context compression attempt telemetry: %sT),:)	sort_keys
separatorsz0failed to emit compression attempt telemetry: %s)r   context_compressorr   r   
setdefaultuuiduuid4hexintr   r   r;   r{   r   r-  jsondumpsr   r   )r   r'  r(  r)  r*  	telemetrypayloadr   s           r   r5  r5    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   r:   )r   _sigs     r   compression_skipped_due_to_lockr    s/     5<dCCD4<0:dC000rg   parent_session_idOptional[List[Dict[str, Any]]]c                   t          t          |          dd          }t          t          |          dd          }t          |          rt          |          sdS  |||          }|r|                    d          sdS t	          |d                   } |||          }t          |t                    r|sdS  |||          }|r(t	          |                    d          pd          |k    rdS || _        	 ddlm	}	  |	|           n# t          $ r |t          j        d<   Y nw xY w	 dd	lm}
  |
|           n# t          $ r Y nw xY wd
| _        |                    d          r|d         | _        t#          |          | _        || _        d |D             | _        t          | j        dd          }t          |          re	  ||d||t          | dd          pdt          | dd                     nv# t          $ r%}t,                              d|           Y d}~nLd}~ww xY wt          | j        dd          }t          |          r	  |||           n# t          $ r Y nw xY w	 | j        r| j                            ||dd           n2# t          $ r%}t,                              d|           Y d}~nd}~ww xY w|S )zMove a stale compression contender onto the unique durable child.

    Resolve and load first, then mutate the live agent. This ordering keeps the
    stale contender fail-closed when lineage is ambiguous or the compacted
    handoff cannot be read.
    find_live_compression_childNget_messages_as_conversationidr1   r   set_current_session_idHERMES_SESSION_IDset_session_contextTsystem_promptc                V    h | ]&}t          |t                    t          |          'S rb   r   r   r  rc   messages     r   	<setcomp>z0_adopt_live_compression_child.<locals>.<setcomp>  s=     % % %
7D0I0I%
7% % %rg   on_session_startr  platformcli_gateway_session_key)boundary_reasonold_session_idr   r  conversation_idz4context engine compression-child adoption failed: %sbind_session_state)r   r   Fr  resetreasonz4memory manager compression-child adoption failed: %s)r   r|   r}   r{   r:   r   r  r   gateway.session_contextr  r   osenvironhermes_loggingr  _session_db_created_cached_system_promptlen_last_flushed_db_idx_flushed_db_message_session_id_flushed_db_message_idsry  r   r   _memory_manageron_session_switch)r   r   r  finderloaderchildchild_session_id	recovered	confirmedr  r  r  r   
bind_states                 r   _adopt_live_compression_childr    s    T*%%'DdKKFT*%%'EtLLFF 8F#3#3 tF:011E 		$ t5;''z#344Ii&& i t z#455I IMM$//5266:JJJt'E;BBBBBB/0000 ; ; ;*:
&''';666666,----    !%Eyy!! =&+O&<#!$YE+;E(% %#,% % %E! u79KTRR !! 
	V  -0% 
D99BU '/Et L L      	V 	V 	VLLOQTUUUUUUUU	V U57KTRR
J 	
j=MNNNNN   	R  	!33 "3$	 4     R R RKSQQQQQQQQR sf   <D D*)D*.E   
EE1G= =
H,H''H,I# #
I0/I04%J 
K	$KK	c                   t          | dd          }t          | dd          pd}||sdS 	 t          ||          sdS t          |dd          }t          d          D ]T}t          | ||          }||c S t	          |          r ||          nd}|r|dk    r dS t          j        d           UdS # t          $ r:}t          	                    d	|t          |          j        |           Y d}~dS d}~ww xY w)
zFRecover a stale live agent before a new turn writes to its old parent.ru   Nr   r1   get_compression_lock_holder      r1  z;compression session recovery failed for session=%s (%s: %s))r   ri  ranger  r}   r   r?  r   r   r4  r|   r   )r   r   r   holder_getterr&   r  holderr   s           r   #recover_rotated_compression_sessionr    sL    t44Jd339rJt2:zJJ 	4  
,I4PPRyy 	 	G5eZTTI$    2:=2I2IS]]:...tF W]]ttJtt   III		
 	
 	
 ttttts(   B5 8B5 7%B5 B5 5
C9?/C44C9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=   )r   r  getpid	get_identr  r{  r|  r}  )r   r   s     r   _compression_lock_holderr  -  s{     	)ry{{ 	) 	)#	#%%	) 	)U))	) 	) *,,"2A2&	) 	)rg   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 r   )kindrT  	ParameterVAR_KEYWORD)rc   	parameters     r   	<genexpr>z0_supported_compression_kwargs.<locals>.<genexpr>_  sC         	'+77     rg   c                $    i | ]\  }}|v 	||S rb   rb   )rc   rd   r   
parameterss      r   rf   z1_supported_compression_kwargs.<locals>.<dictcomp>e  s)    TTTKD%ASASD%ASASASrg   )rT  	signaturer  ri   r  anyre   r   )r  r  r  r  r  
candidatesaccepts_kwargsr  s          @r   _supported_compression_kwargsr  A  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                  Z    e Zd ZdZ	 	 dddZddZdddZd dZd dZddd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 | Nonecommit_fencer  r   r   c                v   || _         || _        d| _        |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 )NF(_compression_activity_heartbeat_interval      N@皙?zcompression-activity-heartbeatTrB   rd   daemon)_agent_commit_fence_suppressedr   r   ri   r  mathisfiniter   _interval_secondsr   r   _stopThread_run_thread)r   r   r  r  s       r   r   z&_CompressionActivityHeartbeat.__init__k  s     ) !#&u.XZ^__	$$%5%=>>:& 	$ 	$ 	$#	$}-.. 	$#!$S*:!;!;_&&
 '91
 
 
s   < AA'_CompressionActivityHeartbeat'c                t    d| _         |                     dd           | j                                         | S )NFzcontext compression startedT)allow_terminal_overwrite)r  _touchr  startr   s    r   r  z#_CompressionActivityHeartbeat.start  s<     !1DQQQrg   context compression completeddescr:   c                .   | j                                          | j                                        r5t	          j                    | j        ur| j                            d           |                                 rd S |                     |d           d S )N      ?r/  T)force_persist)	r  r   r  is_aliver   current_threadjoin_should_suppressr  )r   r  s     r   stopz"_CompressionActivityHeartbeat.stop  s    
<  "" 	+y'?'A'A'U'ULc***   "" 	F
 	D-----rg   r;   c                &    | j         }|d uo|j        S r   )r  r   )r   r  s     r   _fence_cancelledz._CompressionActivityHeartbeat._fence_cancelled  s    "D 7U%77rg   c                R    | j         rdS |                                 r	d| _         dS dS )NTF)r  r  r   s    r   r  z._CompressionActivityHeartbeat._should_suppress  s8     	4  "" 	#D4urg   F)r  r  r  r  c                  	 |sK|                                  rd S t          t          | j        dd                     }|t          v r	d| _        d S t          | j        dd           }t          |          r2|s|                                  rd S  ||t          j        |           d S d S # t          $ r  t                              dd           Y d S w xY w)N_last_activity_provenanceT_touch_activity)
provenancer  z+compression activity heartbeat touch failedr   )r  r   r   r  !_TERMINAL_COMPRESSION_PROVENANCESr  r}   r   AGENT_COMPRESSIONr   r   r   )r   r  r  r  currenttouchs         r   r  z$_CompressionActivityHeartbeat._touch  s$   	W+ ((** F7DK)DdKK  ???'+D$FDK):DAAE 	 0 D4I4I4K4K F1C"/     	 	  	W 	W 	WLLFQULVVVVVV	Ws"   B( 3B( ;B( B( (&CCc                    | j                             | j                  sL|                                 rd S |                     d           | j                             | j                  Jd S d S )Nzcontext compression in progress)r  waitr  r  r  r   s    r   r  z"_CompressionActivityHeartbeat._run  sv    *//$"899 	;$$&& KK9::: *//$"899 	; 	; 	; 	; 	;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   r   r   )r   r   r   r   r   r  r  r  r  r  r  rb   rg   r   r  r  h  s        WW
 *.9=	
 
 
 
 
6   . . . . .8 8 8 8    */#W W W W W W>; ; ; ; ; ;rg   r  c                  2    e Zd Z	 dddZddZddZddZdS )_CompressionLockLeaseRefresherNdbr   r   r:   r  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  )_dbrv   _holder_ttl_secondsr   r9  r   _refresh_interval_secondsr~  _max_consecutive_failuresr   r   r  r  r  r  )r   r  r   r  r  r  s         r   r   z'_CompressionLockLeaseRefresher.__init__  s     %'#+'*3D+:K0L0L'M'M$),S%8P2Q2Q)R)R& *-s4$t'EEFF*
 *
& _&&
 '9+
 
 
rg    '_CompressionLockLeaseRefresher'c                8    | j                                          | S r   )r  r  r   s    r   r  z$_CompressionLockLeaseRefresher.start  s    rg   c                    | j                                          | j                                        r7t	          j                    | j        ur| j                            d           d S d S d S )Nr  r/  )r  r   r  r  r   r  r  r   s    r   r  z#_CompressionLockLeaseRefresher.stop  sp    
 <  "" 	+y'?'A'A'U'ULc*****	+ 	+'U'Urg   c                   d}d}|s| j                             | j                  s|rd}| j                                         rd 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   TFr  z#compression lock refresh raised: %sr$   zZcompression lock refresh failed %d times in a row; stopping lease refresher for session %s)r  r
  r  r   r  refresh_compression_lockrv   r  r  r   r   r   r  )r   consecutive_failuresfirst	refreshedr   s        r   r  z#_CompressionLockLeaseRefresher._run  s^     !  	4:??4+IJJ 	 :$$&& E" H==$L $ 1 >  		
  " " "BCHHH!						"  '($ A% #t'EEE>($*:  
 1  	4:??4+IJJ 	 	 	 	 	s   ,A3 3
B$=BB$r   )r  r   r   r:   r  r:   r  r   r  r  r   r   )r   r  r   )r   r   r   r   r  r  r  rb   rg   r   r  r    sn         26
 
 
 
 
<   + + + +) ) ) ) ) )rg   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  r1   )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 ru  z$ tokens, which is below the minimum zE required by Hermes Agent.  Choose a compression model with at least rp  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   r   
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   r4  r:   r   r}   r   _custom_providersr  ry  threshold_tokensr~  r   tail_token_budgetcontext_lengththreshold_percentr   r   _compute_threshold_tokens_effective_threshold_percenturllib.parser6  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_thresholdr0  main_ctxsafe_pct_CCrecomputed_thresholdthreshold_suggestion_viable_main_model_main_provider_aux_provider_labelr6  _main_label
_aux_labelr   s"                                     r   #check_compression_model_feasibilityra  '  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.
    r>  N	lifecycle)r   r   r   )r   rP  s     r   replay_compression_warningrd  #  s     %/
6
6C
 u$ 	!!+s33333 	 	 	DD	   s   4 
AA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)r;   r   r  )r   r  re  attempt_in_places       r   &conversation_history_after_compressionrl  5  s    6 GE?GGHH  "5*NPTUUt##>>!u$$4GE6>>?? H~~4rg   )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 r  c                    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 )textrn  r1   N)r   r   r:   r{   )rc   parts     r   r  z _message_text.<locals>.<genexpr>j  sp       
 
$%%
  =DHHY$7$7=2>>
 
 
 
 
 
rg   r1   )r   r   r{   r:   r  r  )r  rn  s     r   _message_textrs  e  s    (27D(A(AKgkk)$$$tG'3 '4   
yy 
 

 
 
 
 
 	

 2rg   )_todo_snapshot_synthetic_empty_recovery_synthetic_verification_stop_synthetic_pre_verify_synthetic_dropped_toolcall_nudgec                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.
    roler5   Fc              3  B   K   | ]}                     |          V  d S r   )r{   )rc   flagr  s     r   r  z(_is_real_user_message.<locals>.<genexpr>  s/      
?
?7;;t
?
?
?
?
?
?rg   r   r   )r   r   r{   r  _SYNTHETIC_USER_FLAGSrs  r@   
startswith_SYNTHETIC_USER_PREFIXESr   r   #_is_synthetic_compression_user_turn)r  rq  r   s   `  r   _is_real_user_messager  {  s     gt$$ F(;(;v(E(Eu

?
?
?
?)>
?
?
??? u!!''))D u/00 u:::::: DDWMMMMrg   rn  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 )r|   rq  r1   )r   r   r{   r:   lstripr~  )rc   rr  r  s     r   
<listcomp>z._strip_stale_todo_snapshot.<locals>.<listcomp>  s     

 

 

4&&	


 HHV$$..((.B//122 /	  /..rg   )tools.todo_toolr  r   r:   findrstripr  )rn  idxr  s     @r   _strip_stale_todo_snapshotr    s     655555'3 &ll011"99Ntt}##%%%'4   


 

 

 



 

 

 
	
 Nrg   rB   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.
    rn  rq  r1   )r|   rq  

N)r{   r   r  r:   r@   r}  pop)rB   r  anchor_contenttarget_contentanchor_partstarget_partsmergedr|  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 rg   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.rP  r   r   r  c                Z    t          | t                    r|                     d          nd S )Nrz  )r   r   r{   )rP  s    r   _rolez'_insert_real_user_anchor.<locals>._role  s%    ",S$"7"7AswwvTArg   	assistantr   r$   Nr5   r(  r   )rP  r   r   r  )	enumerater]  appendr   r   _is_context_summary_contentrs  r  )r  r  r  indexr  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9rg   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   )r  r  s     r   r  z3_ensure_compressed_has_user_turn.<locals>.<genexpr>  s+      
D
Dg ))
D
D
D
D
D
Drg   Nr   )%COMPRESSION_CONTINUATION_USER_CONTENT_fresh_compaction_message_copyr5   rz  rn  )r  r   r  r  reversedr  r  r  )r  r  r  r  r  s        r    _ensure_compressed_has_user_turnr    s    

D
D
D
D
DDD        
 -..   )) 	$..w77   FF	 8      rg   0_pending_context_engine_compression_notificationnew_session_idr  c                  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.r  NFr  r  r  r  )r  r  r  r  z4context engine on_session_start (compression) failedTr   )r   ry  r}   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   r;   c                 (    t                     S )Nr  r  )r  )r   r  r  s   r   _notifyz?_queue_context_engine_compression_notification.<locals>._notify9  s#    :))
 
 
 	
rg   r   )r}   r   $_PENDING_CONTEXT_ENGINE_NOTIFICATIONr~   r   )r   r  r  r  s   ``` r   ._queue_context_engine_compression_notificationr  /  sw     CTJJKK LJKKK
 
 
 
 
 
 
 
 E7AAAAArg   	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   r}   r;   )r   r  pendings      r   0finalize_context_engine_compression_notificationr  C  sW     eA4HHGE7>>> HW-- u		??rg   defaultF)approx_tokenstask_idr  r  !defer_context_engine_notificationr  system_messager  r  r  r  c               ;   ghijklmnopqr t           j                  }	d}
d}|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}g                    t           dd                    }|sAt'           j        |	           t           dd          }|s                     |          }||fS 	 t+           |||||          |r                                 S S # |r                                 w w xY w|st/           j                   t          t1           j                  dd          }t          |          r< | j                  r,t           dd          }|s                     |          }||fS t           dd          st3                      d _        t7          |          }t9          t           dd                    }d}t:                              d j        pd||r|dnd j        |           t@          }|s tC           j        d||| j        |          }t9          |          h|r "                    |           dgdgh fdit           dd          k j        pdqdld}d}d}d _        kn	 tG          k          }n# t"          $ r}|}Y d}~nd}~ww xY w|B|s@	 kj$        }t          |          stK          d          }n# t"          $ r}|}Y d}~nd}~ww xY w	 tM          t           dd          pd          }n# tJ          tN          f$ r d}Y nw xY wt           dd          }dmdpdpfd jkqrtQ                     l|5dlt:          )                    d!qt1          |          j*        |           d}nn|=dlt           d"d          qk    r"q _+        t:          )                    d#q           d}n/,                                ppsst:                              d$ j        pd           d _-        t           dd          }|s                     |          }t]           |d%d%d&'            i             ||fS 	  |ql|(          }n# t"          $ r}	 k/                    ql           n2# t"          $ r%}t:          0                    d)|           Y d}~nd}~ww xY wdlt:          )                    d*qt1          |          j*        |           d}Y d}~nd}~ww xY w|s) j             	 k1                    q          } n# t"          $ r d} Y nw xY wt:          )                    d+q|            dl| pd _        t           d,d          qk    r.q _2        	  3                    d-           n# t"          $ r Y nw xY wt           dd          }|s                     |          }	 ti           j        d.          r j        5                    |/           n# t"          $ r Y nw xY wt]           |d%d%d0'            i             ||fS dotm          j7                    ndklmnoq fd1rdijrfd2}!ll _8        9                    r          rst:                              d3 j        pd           d _-        t           dd          }|s                     |          }t]           |d%d%d&'            |!             ||fS  j             kqr	 tu          kq          }"n|# t"          $ ro}#t:          )                    d4qt1          |#          j*        |#            |!             t           dd          }|s                     |          }||fcY d}#~#S d}#~#ww xY w|"rtw           kq          }$ |!             t           dd          }|s                     |          }|$%t:          )                    d5q j                   |$|fS t:          )                    d6q           ||fS ty           j        |	          \  }
}|
du r6 |!             t           dd          }|s                     |          }||fS |s j        }%t/          |%d7           t          t1          |%          dd          }t          |          rA ||%          r6 |!             t           dd          }|s                     |          }||fS d}&d}'	 lEt{          kql||          }(n5  os|(mm>                                 ddd           n# 1 swxY w Y   |skqrt          t1          k          d8d          })t          |)          r |)kq          }*t          |*t                    rjt7          |*          t7          |          k    rJt:                              d9qt7          |          t7          |*                     |*}t7          |          }d:}d}+ jA        rP	  jA        B                    |          },t          |,t                    rt          |,          }+n# t"          $ r Y nw xY w j        jE        }-t          |-||||+;          }.|+G                                rhd<|.vrdt           j        d=t1           j                  j*                  }/t           d>d          |/k    r"|/ _H        t:          )                    d?|/           t          jJ        |          }'t           @          >                                }&d:dAlLmM}0mN}1 jO        ndB }2!	 fdC j        _P        n# t"          $ r Y nw xY wt           dd          }3	 ,jQ        r%t:                              dD j        pd           |}4nt |1|2          5   |0|3E          5   |-|fi |.}4|3"|3R                                rt                      ddd           n# 1 swxY w Y   ddd           n# 1 swxY w Y   	 d j        _P        n6# t"          $ r Y n*w xY wn%# 	 d j        _P        w # t"          $ r Y w w xY ww xY wn# t          $ r9 	 t'           j        |	|
|F           n# t          $ rs}5|'||'k    rt          jJ        |'          |dd<   |&|&U                    dG           d}& |!             t]           |d%d%dHt1          |5          j*         '            d}5~5ww xY w|'||'k    rt          jJ        |'          |dd<   |&|&U                    dI           d}& |!             t]           |d%d%dJ'           t           dd          }|s                     |          }||fcY |&|&U                    dK           S S t          $ rR}6|&|&U                    dL           d}& |!             t]           |d%d%dMt1          |6          j*         '            d}6~6ww xY w|&|&U                    dK           n# |&|&U                    dK           w w xY wd}7	 t9          t           j        dNd                    }8t9          t           j        dOd                    }9t9          t           j        dPd                    }:t           j        dQd          r	 t           j        dRd          pdS};t           dTd          |;k    r |; _V         3                    dU|; dV           t           dd          }|s                     |          }t]           |d%d%t           j        dRd          odW'           ||f |!             	  |!             |7r                                 S S # |7r                                 w w xY w#  |!             w xY w|4|'k    r||'k    rt          jJ        |'          |dd<   t:                              dX j        pd           t           dd          }|s                     |          }t]           |d%d%dY'            |!             ||f	  |!             |7r                                 S S # |7r                                 w w xY w|4st:          W                    dZ j        pd           	  3                    d[           n# t"          $ r Y nw xY wt           dd          }|s                     |          } |!             ||f	  |!             |7r                                 S S # |7r                                 w w xY w                    |3          }7|7st'           j        |	|
|F           |'||'k    rt          jJ        |'          |dd<   t:                              d\ j        pd           d _-        t           dd          }|s                     |          }t]           |d%d%d&'            |!             ||f	  |!             |7r                                 S S # |7r                                 w w xY wt           j        dRd          }<|<r6t           dTd          |<k    r |< _V         3                    d]|< d^           nlt           j        d_d          }=t           j        d`d          }>|=r>|=|>f}?t           dad          |?k    r%|? _X         3                    db|= dc|>pdS dd            jY        Z                                }@|@r2d:del[m\}A d}B|4r#t          |4df         t                    r|4df         nd}C|C|C^                    dg          dhk    rt          |C^                    di                    }Ddj |C`                                D             }E|D|Edi<   t          |E          r0t          |Dt                    r|Drdk|@ n|@}F |A|D|F          |Cdi<   d}BnI|D|C^                    di          k    r0t          dh|Ddl          G                                s|@|Cdi<   d|Cdm<   d}B|Bs|4c                    dh|@ddn           t          ||4            je        }G f                                 |G?t           dod          .t           |G          r|G}H|G _e        d:dplhmi}I  |I |dqr           n                     |          }H|H _e        d}Jds}K jj        rQdt}K	  k                    |           |r9 jj        l                     j        |4           du}Kt                       _n        d}nt           dvd          }Lt          |Lt                    r'd:|Lcxk    rt7          |          k    rn n
|d|L         nd}M	  p                    ||Mw           n# t"          $ r Y nw xY w	 d:dxlqmr}N  |N            }O|Odyk    rd}On# t"          $ r d}OY nw xY w jj        s                     j                  }P j        }Qt          ju                    v                    dz           d{t          j                    j        dd|          }R jj        w                    |Q|R jx        pt          jz        ^                    d}d~           j         j{        |H|4t           dd          |Olldu           |R _        	 d:dl|m}}S  |S j                   n$# t"          $ r  j        t          jz        d<   Y nw xY w	 d:dl~m}T  |T j                   n# t"          $ r Y nw xY wd _        d}K	 d:dlm}U  |U|Q j        d           n2# t"          $ r%}Vt:          0                    d|V           Y d}V~Vnd}V~Vww xY w|Pru	  jj                            |P          }W jj                             j        |W           n9# tN          t"          f$ r%}Xt:          0                    d|X           Y d}X~Xnd}X~Xww xY w|r( jj                             j        |H           d: _        n1t7          |4           _         j         _        d |4D              _n        d}Jn# t"          $ r}X|sLt                      ^                    d          r* j        |Qk    rd}Qt          jJ        |'          |dd<   |}4d}8t                      ^                    d          |sd%nd}Kt                      ^                    d          &|s$t:          )                    d j        pd|X           nt:          )                    d|X           Y d}X~Xnd}X~Xww xY wt                      ^                    d          }Yt9          |Y          p|}Z|Jot9          |Y          p|}[|Yp j        pd}\|Yr~|Jr|	 t           dd          }]t          |]t1          |]          nddd          }^t          |^          r |^|]|Y           n,# t"          $ r t:          0                    dd           Y nw xY w|[r7|rt            j        pd|\           nt            j        pd|\           	 |Zr, jA        r% jA                             j        pd|\dd           n2# t"          $ r%}_t:          0                    d|_           Y d}_~_nd}_~_ww xY w j        j        }`|`dk    r) j         d|` d}a|a _         "                    |a           t           dd          rh	                      d jx        pd j        |Ypd| j        j        d           n2# t"          $ r%}Xt:          0                    d|X           Y d}X~Xnd}X~Xww xY w| _        | _-        t!          |4|Hpd j        pd          }b|b j        _        df j        _        d: j        _        d j        _        |8rRt          t1           j                  dd          }ct          |c          r |c j        |9|:           nd j        _        	 d:dlm}d  |d|           n# t"          $ r Y nw xY w	 d:dlm}e  |e|           n# t"          $ r Y nw xY wt:                              d j        pd|t7          |4          |bd           |Kdv rdnd%}ft]           ||f|K|Kdv rdnd'           |4|Hf	  |!             |7r                                 S S # |7r                                 w w xY w# 	  |!             |7r                                 w w # |7r                                 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)  r]   r1   )rn  r   trigger_sourceapi_modecodex_app_serverF_hard_interrupt_requestedr  r  r  r  _automatic_compression_blocked _compression_feasibility_checkedcompression_in_placezPcontext compression started: session=%s messages=%d tokens=~%s model=%s focus=%rnoneru  unknowncompress)phasedefault_messager  message_countr4  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#'  & 	)!%(((((	) 	)rg   ru   z0compression lock API is present but not callable_compression_lock_ttl_secondsg     r@"_compression_lock_refresh_intervalc                 @     rd S d                                   d S r   )r   )_lock_setup_enteredr  s   r   _finish_lock_setupz,compress_context.<locals>._finish_lock_setupX	  s3    " 	l&:F#&&(((((rg   ug   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.zBCompression commit cancelled before lock acquisition (session=%s).r$  commit_fence_cancelledr&  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  lock_contendedc                    5  r	 ddd           dS dt          dd          k    rd_        H	                                  n2# t          $ r%} t                              d|            Y d} ~ nd} ~ ww xY wNrLrJ	                                n2# t          $ r%}t                              d|           Y d}~nd}~ww xY wddd           dS # 1 swxY w Y   dS )uf  Stop this holder's refresher and release only its durable lock.

        Holder-qualified and idempotent (#76354 F4, from PR #71569): safe for
        the HOST to invoke after a timeout without an ABA race — the DB
        release is scoped to this worker's holder token, so a NEW holder's
        lease can never be deleted by this stale release.
        NT_active_compression_lock_holderz*compression lock refresher stop failed: %sz#compression lock release failed: %s)r   r  r  r   r   r   release_compression_lock)		_stop_err_rel_err_lock_db_lock_holder_lock_refresherr   _lock_released	_lock_sidr   s	     r   _release_lock_holder_onlyz3compress_context.<locals>._release_lock_holder_only	  s    ! 	R 	R 	R 	R 	R 	R 	R 	R 	R 	R "Nu?FF,VV8<5*Z#((****  Z Z ZLL!MyYYYYYYYYZ#	#l#R55iNNNN  R R RLL!FQQQQQQQQR	R 	R 	R 	R 	R 	R 	R 	R 	R 	R 	R 	R 	R 	R 	R 	R 	R 	Rsi   C CA
C
A:A50C5A::	CBC
C
%C CC

CC!Cc                    	                	               	                                               dS #               w xY w# 	                                               w #               w xY wxY w# 	               	                                               w #               w xY w# 	                                               w #               w xY wxY wxY w)z?Finish lifecycle cleanup and release the OLD session lock once.N)r   )r  r  r  r  s   r   _release_lockz'compress_context.<locals>._release_lock	  s   	)**,,,	)))+++)#/$AA5   '&(((((&&(((()#/$AA5   '&((((&&((((	)))+++)#/$AA5   '&((((&&(((()#/$AA5   '&((((&&((((sy   
B 
A < AA>A/$A>/A;;A>C5
B?B0%C50B<<C5?C2C#C2#C//C22C5zBCompression commit cancelled before summary dispatch (session=%s).zecompression session ownership lookup failed for session=%s (%s: %s) - skipping compression this cyclez<compression recovery: stale session=%s adopted live child=%szzcompression skipped: session=%s was already rotated by another compression path, but no unique live child could be adoptedrX  r  uU   compression: session=%s grew before lease (%d → %d msgs); adopting durable snapshotr   )r  r  r  r  r  rd   '_last_memory_context_unsupported_enginezfcontext engine %s does not accept memory_context; continuing without provider-supplied summary contextr  )aux_interrupt_protectionaux_progress_hookc                     d S r   rb   rb   rg   r   <lambda>z"compress_context.<locals>.<lambda>
  s    $ rg   c                      j         S r   )r   r  s   r   r  z"compress_context.<locals>.<lambda>  s
    L5 rg   uU   Compression cancelled before summary dispatch (session=%s) — skipping summary work.)r   ro   z#context compression rollback failedz	rollback:zcontext compression cancelledexplicit_interruptr  context compression failedz
exception:rM   rS   _last_feasibility_skiprT   rP   z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).u    ⚠ Compression summary failed: z%. Inserted a fallback context marker.rX   rW   _last_aux_fallback_warning_keyu"   ℹ Configured compression model 'z
' failed (uS   ). Recovered using main model — check auxiliary.compression.model in config.yaml.)_append_text_to_contentr(  rz  r5   rn  c                &    i | ]\  }}|d k    ||S )rn  rb   )rc   keyr   s      r   rf   z$compress_context.<locals>.<dictcomp>  s/       #-3#BRBRCBRBRBRrg   r  r  rt  )rz  rn  rt  r  )reconstruct_static_prefixzcompression keep-prompt)r  	log_labelnot_applicabler  in_place_committed_persist_user_message_idx)conversation_history)get_active_profile_namer  z%Y%m%d_%H%M%SrJ     HERMES_SESSION_SOURCEr  working_directory)r  r  sourcer4  model_configr  r  cwdprofile_namecompression_lock_holderrequire_compression_leaser  r  r  rotated_committed)migrate_goal_to_sessionr  )r  z)Could not migrate goal on compression: %sz,Could not propagate title on compression: %sc                V    h | ]&}t          |t                    t          |          'S rb   r  r  s     r   r  z#compress_context.<locals>.<setcomp>  s>     5 5 5#%gt44575 5 5rg   r  failed_not_indexedzKCompression rotation aborted and rolled back to the parent session (%s): %sr5  uK   Session DB compression split failed — new session will NOT be indexed: %sclear_session_activity_labelszGfailed to clear archived compression parent's activity labels (ignored)r   r  r  z2memory manager on_session_switch (compression): %sr2  u   ⚠️  Session compressed u>    times — accuracy may degrade. Consider /new to start fresh.event_callbackzsession:compress)r  r   r  in_placerG   z,event_callback error on session:compress: %s)r  toolsrecord_completed_compaction)used_fallbackfeasibility_skipreset_file_dedup)reset_skill_view_dedupz^context compression done: session=%s messages=%d->%d rough_tokens=~%s awaiting_real_usage=true>   r  r  r  r  >   r$  r  session_split_failedr   )rn   ry  r}   r   r  r~   rh  ri  r  r   r   r{  r|  r}  ro  r   r   r   r   r   _build_system_prompt&_compress_context_via_codex_app_serverr   rb  r|   ra  r  r  r;   r   r-  r4  COMPACTION_STATUSr   r?  rW  rQ  ri   r   r  r  r4  r   r  r   rj  r5  r  r   r  r  _emit_warninghasattrr  r   r   r  r   ri  r  r   r  r  r   r  r  on_pre_compressr:   r   r  r  r@   r  rk   rl   r  r9  r  r  r   _compression_cancelled_checkr   r   r   r7  r  r  rA  r  _todo_storeformat_for_injectionr   r  r   r{   r  r   r  rs  r  r  r  _invalidate_system_promptrD   agent.system_promptr  ru   commit_memory_sessionarchive_and_compactr   r  r~  _flush_messages_to_session_dbhermes_cli.profilesr  get_session_titler   nowstrftimepublish_compression_childr  r  r  _session_init_model_configr  r  r  r  r  hermes_cli.goalsr  get_next_title_in_lineageset_session_titleupdate_system_promptr  r  localsr  r  r  rG   
log_prefixr>  r  r   r  last_compression_rough_tokenslast_prompt_tokenslast_completion_tokens%awaiting_real_usage_after_compressionrL   tools.file_toolsr  tools.skills_toolr  )sr   r  r  r  r  r  r  r  r  _compressor_attempt_snapshot_durable_cooldown_authoritative_durable_cooldown_state_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_apir   	_lock_ttl_lock_refresh_interval_lock_acquired_existing_sp	_lock_err_release_errexistingr  _parent_already_rotated_session_errrecovered_messagesr^   _activity_heartbeatmessages_before_compression_candidate_refresherdurable_loaderdurable_parentr  
_maybe_ctxr  compress_kwargsengine_namer  r  _progress_hook_hard_cancel_eventr  _rollback_exc_compress_exc_commit_fence_entered_compression_made_progress_compression_used_fallback_compression_feasibility_skip_errsummary_error_aux_fail_model_aux_fail_err_aux_keytodo_snapshotr  r  _tail	_stripped_probe_snapshot_textcached_system_promptnew_system_promptr  _session_commit_succeededr)  current_idxpersisted_historyr  _profile_for_child	old_titler  r  r  r  r  	_goal_err	new_titlee_old_sid_is_boundary"_context_engine_boundary_committed_boundary_parent
_labels_db_clear_labels_me_err_cc_cc_msg_compressed_estrecord_boundaryr  r  _commit_statusr  r  r  r  r  r  r  r   r  r  r  r  ss   `       `                                                                                              @@@@@@@@@@@@r   compress_contextr  P  s<+   R $F $ $  7;#8<)LWU$H$OOPPL JKKK 04E,/3E, .2E*.***,,"K"'3hhVO(3%(*G%*0b-J
 J
 	 	 	 	
     uj$''+===$##/#<#<:DAA$ $  ( 11,.J   #*%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#94@@AAH 
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&C&C&E&E#* 2KK((2F  
 7<E3#*52I4#P#PL' R','A'A.'Q'Q7#6&/%.&>    32444#\11'!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#.**R R R R R R R R R R R R4) ) ) ) ) ) ) ) )  0<-$<<)  % KK  *F  
 /4E+"5*A4HHL J$99.II/.'&6    MOOO\)) 
 		*&I)' '##  	* 	* 	*NN=\""+   MOOO"5*A4HHL J$99.II\)))))))	* # 	*!>x" " MOOO"5*A4HHL J$99.II!-R$  
 *<77NNV  
 \)) 	4$(	
 	
 =#%< '%//
 	!%)@$GG 	I#88HHO((  --
-"	
 	
 	
 	
 ,
 

 G 	-!4!4 	-MOOO%e-DdKKO" M"'"<"<^"L"L_,,CG"&oF##A&$ $  % , ,% ,&:O#))+++, , , , , , , , , , , , , , ,2  	&H0Y0$X > N '' &!/)!D!Dnd33 &N8K8KcRZmm8[8[KKF!HN++    .H%(]]N %&M   	"2BB8LL
j#.. I%<Z%H%HN    .77(#)
 
 
 !! 	&6o&M&M!(U-..7 K H$OO  AL=@   '+mH&=&=#;
 
 

%'' 	,	
 	
 	
 	
 	
 	
 	
 	

 ,8+CL'', 	 #5555 (EE    
 %U,GNN	 'L,E'>$.  
 &

&&~66 > >8P8P!39 9 9 > > "-X!I!I!I!IJ
 +6.5577 7 <===> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > 'LPE,II    D (|'LPE,II    D (
 ) /& /& /&	-(,/N'>	      	 	 	 ,7 ;;;"m,GHH".#(()NOOO&*#MOOO/.'&H$}*=*=*FHH    '	* (3777-(CDDHQQQK*$$%DEEE"&+*#".	
 	
 	
 	
 u&=tDD 	F 55nEEL%%%$ *$$%DEEEE +#     *$$%ABBB"&+*#"EtM':':'CEE	
 	
 	
 	
 	" *$$%DEEE *$$%DEEEE + "a	-
 &*E,.OQVWW&
 &
" &*E,.KUSS&
 &
" )-E,.FNN)
 )
% 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	 	 	 	  -`	-MOOO$ -**,,,,-$ -**,,,,-g  444666"m,GHHKKZ *F   #5*A4HHL J$99.II/.'&+    MOOO\)p	-MOOO$ -**,,,,-$ -**,,,,-s  	*LLD *F  
##X       "5*A4HHL J$99.IIMOOO\)J	-MOOO$ -**,,,,-$ -**,,,,-M #$0$=$=>P$Q$Q!( .1,03R+B	    0; $???"&-0K"L"LHQQQK$$.  
 38/&u.EtLL# N#(#=#=n#M#ML32"+!*":    -H	-MOOO$ -**,,,,-$ -**,,,,-K   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' FEEEEE%%-3     !& : :> J J*;E'$)!' u	u$Lsu
 ++H555 ~\& %99%:JJWWW#7L 58EEE1 *.&&" #*%1Ld"S"SK &k377"====H===== !+.. "	 &;;$1B <     %   2OOOOOO-D-D-F-F*-::15.$ 2 2 2-1***2 % 1 C CEDT U UI%*%5N#<>>22?CC 2 2:<<+BQB/2 2 # %??*8)7$~  J:>>*A5II#k%*%E&7!+#E+>EE%70<2>d2J @    (6E$KRRRRRR..u/?@@@@$ K K K:?:J
#6777KFFFFFF++E,<====$   04E-#6L
]LLLLLL//@PYfggggg$ ] ] ]%PR[\\\\\\\\] ! \\(-(9(S(ST](^(^I!-??@PR[\\\\ *I6 \ \ \"LL)WYZ[[[[[[[[\
  %::(*;   23E..14ZE.;@;KE85 5'15 5 5E1
 -1)) u u u 
7%566
7 (N:: &*N"&-0K"L"LHQQQK!)J16. xx||$455=h= I-  88<< 0119(9NN2383C3JsA   
 NN#prsttt9uD 88<< 011H~~1-F .
NN00 	+ $=u'7=2  	1 	$UM4@@
 '(2(>D$$$D3! !
 M** 8!M*h777   '!       . 	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,"<%B     QU(M
	999999W%%%% 	 	 	D		@@@@@@""7++++ 	 	 	D	 	l&J""	
 	
 	

 )58u(u(u  |E+*(%  #DDD '&
	
 
	
 
	
 
	
 ,,	-MOOO$ -**,,,,-$ -**,,,,-	-MOOO$ -**,,,,-$ -**,,,,-s  "(C 
CCF F!4M 
MMM"%N 
NNN" O OOT 
V/)U ?V* 
U/
U*%V**U//6V**V/ W W%$W%,Y 
YY;0Z, ,
Z98Z9,^= =
`6A$`1+`61`6#t :gt g##t &g#'C
t 2>k1 0t 1
k>;t =k>>C,t +o; :t ;
pt pt 9s  r4#.rr4r!	!r4$r!	%r4(s  4r88s  ;r8<s  ?t s t 
st st  t$s10t1
s>;t=s>>tt {  z%t,+z%,
v)6A.v$$v))Bz%0{  
	z%Az  z%%{   { A?Av  B"AA @
Av @
A@/@/AAAAAABAv C+
ADDAD&D)$Av EAE$ E#Av E$
AE1E.Av E0AE1E18Av F*
AGGAG%G(CAv J.
AKKAK)K,JAv VBAc) X+AY YAc) Y
AYYAc) YAYYAc) YAY- Y,Ac) Y-AY<Y9Ac) Y;AY<Y<CAc) ]A]2 ]1Ac) ]2A^^Ac) ^A^^Ac) ^A^. ^-Ac) ^.
A^;^8Ac) ^:A^;^;Ac) _A_" _!Ac) _"
A`_,A``Ac) `A``Ac) `:Aa aAc) aAba#Aba>Ac) bAbbA Ac) c(Av c)
Agc3CAggAv gAggAAv h/AAi> i=Av i>&Aj'j$Av j&Aj'j'<Av k$.Al lAv l
AmlAl=l8Av l=AmmAAv n4Ao oAv o
Ao6oAo1o,Av o1Ao6o6B1Av r(Ar: r9Av r:
AssAv sAssAv sAs sAv s
As*s'Av s)As*s*AAv u
Au)u)AvvAwv
Av)vAwv)AwwAw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)r   Nr  ru  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$$$$$rg   r  interruptedrA  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_idr1   turn_idr   )r:   r   lowerr   r-  r  r  r?  r  r   r  r  compact_threadr7  r  closer  r  rA  agent.codex_runtimer  r  r  ry  r   r9  r  )r   r  r  r  r  r  	auto_moderB  codex_sessionr  rT  r:  r  r  r  r  s   `              @r   r  r    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               &   | 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Dd}
t          |	          D ]\  }}t          |t                    s|                    d          }|dk    r{|                    d          } ||          } ||pd          \  }}|rAt          |t                    r,|
t          |	          }
i |d |||          i|
|<   |dz  }n|r|dz  }|dvr|                    d          }t          |t                    rX|                    dd          } ||          \  }}|r)|
t          |	          }
i |di |d|ii|
|<   |dz  }0|r|dz  }9t          |t                    r: ||          \  }}|r#|
t          |	          }
i |d|i|
|<   |dz  }|r|dz  }|
|
|d<   |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_urlr:   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   Nru  data:)Image)base64io	partitionr~  PILr  openBytesIO	b64decodesizer   )r  _b64_dim_io_dimheader_drJ  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  r   bytesNT	dimensionru  
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   r:   r~  r  r   r  splitr@   r  r  r{   tempfileNamedTemporaryFilewriter  r   rd   unlinkr   r   r4  )r  needs_shrinktriggered_bydimsheaderrJ  datamime	mime_part_b64r  r  tmpresizednew_dimsr   r  r  r  target_bytess                   r   _shrink_data_urlz<try_shrink_image_parts_in_messages.<locals>._shrink_data_url  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 )	Nr|   r  r  
media_typer  r  r  z;base64,)r   r   r{   r:   r@   r~  )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1rg   r   c                *   |                     d          \  }}}d}|                    d          rZ|t          d          d                             dd          d                                         }|                    d          r|}i | d	||d
S )u  Return a NEW source dict carrying the re-encoded payload.

        Copy-on-write: content parts on the per-call ``api_messages`` list may
        be shared references into the persistent conversation history (the
        per-message copy is shallow, and cache decoration only deep-copies the
        marked messages). Mutating the existing dict would rewrite the stored
        transcript with the degraded image — so the caller replaces the part,
        never edits it in place.
        ru  r  r  Nr  r$   r   r  r  )r|   r  r  )r  r~  r  r  r@   )r  r  r  rJ  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-- '&


$	
 
 
 	
rg   rn  r|   imager1   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  r:   r   r  )r  r:   r   r  )r  r   r   r  )r  r   r  r:   r   r   )tools.vision_toolsr  r   r   r4  r   r   r{   r  r  r:   r-  )r  r  r   changed_countunshrinkable_oversizedr  r  r  rP  rn  new_contentpart_idxrr  ptyper  r  r  unshrinkableimage_valuer  r  r  s    `                 @@@r   "try_shrink_image_parts_in_messagesr  d  s   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
 
 
 
.  ;) ;)#t$$ 	'')$$'4(( 	 $('00 -	0 -	0NHddD)) HHV$$E(++))&11(8(8(C(C% 	0z&$77 	0"*&*7mm-- ";";FG"L"L- -K) "Q&MM! 0*a/*888((;//K +t,, 0!ooeR00(8(8(=(=% 	0"*&*7mm--#%D%DUG%D%D- -K) "Q&MM! 0*a/*K-- 0(8(8(E(E% 0"*&*7mm,Jt,J[',J,JK)!Q&MM! 0*a/*"(C	N 
U<;7	
 	
 	
  

 	U"LK$@	
 	
 	

 u1s    
A<A)r  r   COMPACTION_STATUS_MARKERra  rd  r  r  )r   r   r   r   )r   r   r   r.   )r   r   r9   r:   r   r;   )r^   r   r   r_   )
r^   r   rA   r_   rp   rr   rq   rs   r   r   )r^   r   r   r_   r   r   r   r   r   )r   r   r   r   )r  r  r  r  r  r   r  r   r  r   r  r  r  r  r  r  r  r   r   r  )rN  r   r   r;   )r^   r   rY  r;   r   r   )r   r   r   r:   r   r;   )r   r   r'  r   r(  r:   r)  r:   r*  rj  r   r   )r   r   r   r;   )r   r   r   r   r  r:   r   r  )r   r   r   r  )r   r   r   r:   )r  r   r  r  r  r  r  r;   r  r:   r   r   )r   r   r  r  re  rf  r   rf  )r  r   r   r:   )r  r   r   r;   )rn  r   r   r   )rB   r   r  r   r   r   )r  r  r  r   r   r   )r  r  r  r  r   r   )r   r   r  r:   r  r:   r   r;   )r   r   r  r:   r  r:   r   r   )r   r   r  r;   r   r;   )r   r   r  r  r  r:   r  r  r  r:   r  r  r  r;   r  r;   r  r  r   r  )r   r   r  r  r  r  r  r  r  r:   r  r;   r   r  )r  r  r  r~  r   r;   )nr   
__future__r   concurrent.futuresr;  rk   rT  r  loggingr  r  r  r   r{  r   r   pathlibr   typingr   r   r   r	   r
   r   r9  r   agent.context_enginer   r   r:  r   agent.session_activityr   r   	getLoggerr   r   	frozensetAGENT_COMPRESSION_TIMEOUTAGENT_COMPRESSION_COOLDOWNr  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_SAMPLESr8   rD   rj   r   rn   r   r   r   r  r  r   r   r   r@  r   r   r   r~   r   r   r   r   r  rM  rW  rb  ri  r5  r  r  r  r  r  r  r  ra  rd  rl  r  rs  r}  r  r  r  r  r  r  r  r  r  r  r  r  __all__rb   rg   r   <module>r     s  1 1f # " " " " "          				                    = = = = = = = = = = = = = = = = @ @ @ @ @ @        ? > > > > > T T T T T T T T		8	$	$ %.I45% % ! 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$  8% !# # # #2 6:7;O) O) O) O) O) O)d7. 7. 7. 7.tu u u u u u u ut #( (- % " "0)."2"2  &* "" "# )9>++  K K K K K K K K   * * * * ** * ** '+( ( ( ( (d CGBF.2M, M, M, M, M, M,`   4 "X X X X X X2
 
 
 
& !%"N "N "N "N "N "NJ1 1 1 1$R R R Rj   D   ($U $U $U $UNc; c; c; c; c; c; c; c;LV V V V V V V Vry
 y
 y
 y
x   * (,$ $ $ $ $N 
 
 
 
 N N N N.   >   :&: &: &: &:R   . 7 %
   :B B B B(
 
 
 
$ $(!%.359@- @- @- @- @- @-P, $(N% N% N% N% N% N%h \ \ \ \ \ \~  rg   