+
    iHàjb3  ã                   ó”   € R t ^ RIt^ RIt^ RIHtHt ^ RIHtHtH	t	H
t
 ^ RIHt ^ RIHt RtRtRtR	 R
 ltR R lt ! R R]4      tR# )a	  Abstract base class for pluggable context engines.

A context engine decides when/how conversation context is compacted near the token
limit, tracks usage, and may expose tools. ContextCompressor is the default;
``context.engine`` selects a plugin (``plugins/context_engine/<name>/``); one is active.
Lifecycle: on_session_start() -> per API response update_from_response() -> per turn
should_compress() / compress() -> on_session_end() at real session boundaries only
(CLI exit, /reset, gateway expiry), never per-turn.
N)ÚABCÚabstractmethod)ÚAnyÚDictÚListÚOptional)Úelide_middle)Úredact_sensitive_textip  i   iÜ  c                ó0   € V ^8„  d   QhR\         R\         /# )é   Úmemory_contextÚreturn©Ústr)Úformats   "Ú9/home/ubuntu/.hermes/hermes-agent/agent/context_engine.pyÚ__annotate__r      s   € ÷ [ñ [¬Cð [´Cñ [ó    c                ó�   € \        V P                  4       RRR7      p\        V4      \        8:  d   V# \	        V\
        \        4      # )zBPrepare provider context for a context-engine/LLM egress boundary.T)ÚforceÚredact_url_credentials)r	   ÚstripÚlenÚMEMORY_CONTEXT_MAX_CHARSr   Ú_MEMORY_CONTEXT_HEAD_CHARSÚ_MEMORY_CONTEXT_TAIL_CHARS)r   Ú	sanitizeds   & r   Úsanitize_memory_contextr      s>   € ä% n×&:Ñ&:Ó&<ÀDÐaeÔf€IÜ
ˆ9ƒ~Ô1Ô1ØÐÜ˜	Ô#=Ô?YÓZÐZr   c                ób   € V ^8„  d   QhR\         R\        R\        R\         R\        R,          /# )r   ÚengineÚphaseÚdefault_messageÚcontextr   N)r   r   )r   s   "r   r   r   !   s8   € ÷ (ñ (´ð (¼sð (ÔUXð (Ôehð (ÔmpÐswÕmwñ (r   c               óÖ   € \        V RR4      '       g   R# \        V RR4      p\        V4      '       d   V! RRVRV/VB MTpVf   R# \        V4      P                  4       ;'       g    R# )zÖHost-visible status for an automatic compaction event; ``None`` = emit nothing.

Engines suppress via ``emit_automatic_compaction_status = False`` or
customize via ``get_automatic_compaction_status_message(...)``.
Ú emit_automatic_compaction_statusTNÚ'get_automatic_compaction_status_messager    r!   © )ÚgetattrÚcallabler   r   )r   r    r!   r"   Ú	formatterÚmessages   &$$,  r   Ú#automatic_compaction_status_messager+   !   sn   € ô �6Ð=¸t×DÒDÙÜ˜Ð IÈ4ÓP€IÜT\Ð]f×TgÒTg‰iÑP˜eÐP°_ÐPÈÒPÐm|€GØ‚ÙÜˆw‹<×ÑÓ×'Ð' 4Ð'r   c                   ó  a € ] tR t^0t o Rt]]V 3R lR l4       4       t^ t^ t	^ t
^ t^ t^ tRt^t^tRt]V 3R lR l4       t]R2V 3R	 lR
 ll4       tR2V 3R lR llt]R3V 3R lR ll4       tR2V 3R lR lltRRRRR^ /V 3R lR lltR2V 3R lR lltV 3R lR ltV 3R lR ltV 3R lR ltV 3R lR ltV 3R  lR! ltV 3R" lR# ltV 3R$ lR% ltV 3R& lR' lt V 3R( lR) lt!V 3R* lR+ lt"V 3R, lR- lt#R4V 3R. lR/ llt$V 3R0 lt%R1t&V t'R# )5ÚContextEnginez.Base class all context engines must implement.c                ó    <€ V ^8„  d   QhRS[ /# ©r   r   r   )r   Ú__classdict__s   "€r   r   ÚContextEngine.__annotate__5   s   ø€ ÷ ;ñ ;‘cñ ;r   c                ó   € R# )z,Short identifier (e.g. 'compressor', 'lcm').Nr&   ©Úselfs   &r   ÚnameÚContextEngine.name3   ó   ‚ r   g      è?Tc                ó:   <€ V ^8„  d   QhRS[ S[S[3,          RR/# )r   Úusager   N©Údictr   r   )r   r0   s   "€r   r   r1   L   s#   ø€ ÷ ñ ©$©s±C¨x­.ð ¸Tñ r   c                ó   € R# )a  Update tracked token usage after every LLM call.

``prompt_tokens``/``completion_tokens``/``total_tokens`` are always present; the
canonical buckets (``input_tokens``, ``output_tokens``, ``cache_read_tokens``,
``cache_write_tokens``, ``reasoning_tokens``) are optional on older hosts.
Nr&   )r4   r9   s   &&r   Úupdate_from_responseÚ"ContextEngine.update_from_responseK   r7   r   Nc                ó4   <€ V ^8„  d   QhRS[ R,          RS[/# ©r   Úprompt_tokensNr   ©ÚintÚbool)r   r0   s   "€r   r   r1   U   s   ø€ ÷ ?ñ ?©S°4­Zð ?Á4ñ ?r   c                ó   € R# )z0Return True if compaction should fire this turn.Nr&   ©r4   rA   s   &&r   Úshould_compressÚContextEngine.should_compressT   r7   r   c                óX   <€ V ^8„  d   QhRS[ R,          RS[S[S[R,          3,          /# r@   )rC   ÚtuplerD   r   )r   r0   s   "€r   r   r1   X   s0   ø€ ÷ 9ñ 9±#¸µ*ð 9ÉÉdÑTWÐZ^ÕT^ÐN^ÕH_ñ 9r   c                ó(   € V P                  V4      R3# )zíReturn ``(should_compress, reason)``.

Engines with block reasons (summary-LLM cooldown, anti-thrashing guard) override
this so callers can warn instead of silently skipping; the default keeps plugin
engines from raising AttributeError.
N)rG   rF   s   &&r   Úshould_compress_infoÚ"ContextEngine.should_compress_infoX   s   € ð ×#Ñ# MÓ2°DÐ8Ð8r   c                óª   <€ V ^8„  d   QhRS[ S[S[S[3,          ,          RS[S[,          RS[S[,          RS[RS[RS[ S[S[S[3,          ,          /# )r   ÚmessagesÚcurrent_tokensÚfocus_topicr   r   r   )Úlistr;   r   r   r   rC   rD   )r   r0   s   "€r   r   r1   b   sa   ø€ ÷ 
ñ 
Ù™T¡#¡s (�^Õ,ð
Ù>FÁs½mð
á™c•]ð
á26ð
áPSð
ñ 
‰d‘3™�8�nÕ	ñ
r   c                ó   € R# )u_  Compact ``messages`` into a valid OpenAI-format list that fits the budget.

``focus_topic`` comes from manual ``/compress <focus>`` (prioritise that topic);
``force`` asks to bypass an engine-owned cooldown; ``memory_context`` is provider
text for the handoff prompt. Older engines may omit optional parameters â€” the
host filters them by signature.
Nr&   )r4   rO   rP   rQ   r   r   s   &&&&&&r   ÚcompressÚContextEngine.compressa   r7   r   c          
      óœ   <€ V ^8„  d   QhRS[ S[S[S[3,          ,          RS[R,          RS[S[ S[S[S[3,          ,          S[3,          /# )r   rO   rP   Nr   )rR   r;   r   r   rC   rJ   )r   r0   s   "€r   r   r1   n   sM   ø€ ÷ 	ñ 	Ù™T¡#¡s (�^Õ,ð	Ù>AÀD½jð	á	‰t‘D™™c˜•NÕ#¡SÐ(Õ	)ñ	r   c                ó
   € V^ 3# )a-  Deterministically trim old tool-result payloads without an LLM call.

Runs on a low, cost-oriented trigger independent of ``should_compress`` so
large-window engines reclaim re-sent tool output long before full compaction.
Returns ``(messages, n_pruned)``; the default no-op keeps older engines safe.
r&   )r4   rO   rP   s   &&&r   Úprune_tool_results_onlyÚ%ContextEngine.prune_tool_results_onlyn   s   € ð ˜ˆ{Ðr   Úconversation_messagesÚincoming_messageÚbudget_tokensc                óÜ   <€ V ^8„  d   QhRS[ S[S[S[3,          ,          RS[ S[S[S[3,          ,          R,          RS[S[S[3,          R,          RS[RS[ S[S[S[3,          ,          /# )r   Úrequest_messagesrZ   Nr[   r\   r   )rR   r;   r   r   rC   )r   r0   s   "€r   r   r1   y   sv   ø€ ÷ ñ Ù $¡T©#©s¨(¥^Õ 4ðÙPTÑUYÑZ]Ñ_bÐZbÕUcÕPdÐgkÕPkðá™s¡C˜x�.¨4Õ/ðáGJðñ 
‰d‘3™�8�nÕ	ñr   c               ó   € R# )u  Optionally *select* (replace) the context for THIS request, pre-generation.

Runs on every provider request (also retries), independent of
``should_compress()``: ``compress()`` shrinks over-long context, this swaps in a
different one (retrieval, topic routing, branch switching). Return ``None`` to
leave the request unchanged. The returned list is request-only â€” it MUST NOT be
treated as persisted transcript state (session DB history is untouched); unlike
``pre_llm_call`` it may replace the list. The host runs it before prompt
cache-control and every request sanitizer, so a malformed replacement never
reaches the provider and the default no-op keeps the request byte-identical;
an engine that replaces the list changes its own cache prefix (breakpoints are
re-derived on the selected list). ``request_messages`` is the assembled request
(system prompt + history + ephemeral prefill); ``conversation_messages`` is the
persisted history for reference only (do not mutate); ``budget_tokens`` is the
model's context length or 0 if unknown.
Nr&   )r4   r^   rZ   r[   r\   s   &&$$$r   Úselect_contextÚContextEngine.select_contexty   s   € ñ( r   c                óz   <€ V ^8„  d   QhRS[ S[S[S[3,          ,          RS[S[S[3,          R,          RS[RR/# )r   rO   r9   NÚkwargsr   ©rR   r;   r   r   )r   r0   s   "€r   r   r1   �   sG   ø€ ÷ ñ ©©d±3¹°8­nÕ)=ð ÁdÉ3ÑPSÈ8ÅnÐW[ÕF[ð Ñnqð Ðvzñ r   c                ó   € R# )ad  Observe a finished turn (complement of ``select_context()``) to index/update
routing state for the next request.

Best-effort, not guaranteed: fires from the normal finalization seam only; some
abnormal early returns (content-policy block, provider terminal failure) skip it.
``messages`` is a read-only shallow copy (return value ignored; never rely on
transcript mutation). ``usage`` has the ``update_from_response`` shape and is
``None`` when no provider response was reached (interrupt). ``kwargs`` may include
``turn_id``, ``task_id``, ``api_call_count``, ``interrupted``, ``failed``, ``turn_exit_reason``.
Nr&   )r4   rO   r9   rc   s   &&&,r   Úon_turn_completeÚContextEngine.on_turn_complete�   s   € ñ 	r   c                óL   <€ V ^8„  d   QhRS[ S[S[S[3,          ,          RS[/# ©r   rO   r   ©rR   r;   r   r   rD   )r   r0   s   "€r   r   r1   œ   s(   ø€ ÷ ñ ±$±t¹CÁ¸Hµ~Õ2Fð É4ñ r   c                ó   € R# )zOCheap rough check before the API call (no real token count yet); default skips.Fr&   ©r4   rO   s   &&r   Úshould_compress_preflightÚ'ContextEngine.should_compress_preflightœ   s   € ár   c                ó&   <€ V ^8„  d   QhRS[ RS[/# )r   Úrough_tokensr   rB   )r   r0   s   "€r   r   r1       s   ø€ ÷ ñ Áð Éñ r   c                ó   € R# )z�True when preflight should trust recent real usage over the noisy rough
estimate (avoids re-compacting after a compressed request already fit).Fr&   )r4   rp   s   &&r   Ú$should_defer_preflight_to_real_usageÚ2ContextEngine.should_defer_preflight_to_real_usage    s   € ñ r   c          	      ó@   <€ V ^8„  d   QhRS[ RS[ RS[RS[ R,          /# )r   r    r!   r"   r   N)r   r   )r   r0   s   "€r   r   r1   ¥   s7   ø€ ÷ 	Rñ 	RÙð	RÙ.1ð	RÙ>Að	Rá	ˆt�ñ	Rr   c               ó.   € V P                   '       d   V# R# )a"  User-visible status for automatic compaction, or ``None`` to suppress it.

``phase`` is the host call site (``"preflight"`` / ``"compress"``); ``context``
carries best-effort ``approx_tokens`` / ``threshold_tokens``. Warnings, errors
and manual ``/compress`` are not governed by this hook.
N)r$   )r4   r    r!   r"   s   &$$,r   r%   Ú5ContextEngine.get_automatic_compaction_status_message¥   s   € ð #'×"G×"GÐ"GˆÐQÈTÐQr   c                óL   <€ V ^8„  d   QhRS[ S[S[S[3,          ,          RS[/# ri   rj   )r   r0   s   "€r   r   r1   °   s(   ø€ ÷ ñ ±±T¹#¹s¸(µ^Õ0Dð Éñ r   c                ó   € R# )zŒPreflight guard for gateway ``/compress``: False reports "nothing to
compress yet" without an LLM call (e.g. transcript entirely protected).Tr&   rl   s   &&r   Úhas_content_to_compressÚ%ContextEngine.has_content_to_compress°   s   € ñ r   c                ó$   <€ V ^8„  d   QhRS[ RR/# )r   Ú
session_idr   Nr   )r   r0   s   "€r   r   r1   µ   s   ø€ ÷ eñ e©3ð e¸Tñ er   c                ó   € R# )zVSession begins: load persisted state. kwargs may include hermes_home, platform, model.Nr&   )r4   r|   rc   s   &&,r   Úon_session_startÚContextEngine.on_session_startµ   r7   r   c                óP   <€ V ^8„  d   QhRS[ RS[S[S[ S[3,          ,          RR/# )r   r|   rO   r   N)r   rR   r;   r   )r   r0   s   "€r   r   r1   ¸   s5   ø€ ÷ [ñ [©ð [¹¹TÁ#ÁsÀ(½^Õ8Lð [ÐQUñ [r   c                ó   € R# )uL   Real session boundary (CLI exit, /reset, gateway expiry) â€” never per-turn.Nr&   )r4   r|   rO   s   &&&r   Úon_session_endÚContextEngine.on_session_end¸   r7   r   c                ó   <€ V ^8„  d   QhRR/# )r   r   Nr&   )r   r0   s   "€r   r   r1   »   s   ø€ ÷ #ñ # $ñ #r   c                ó>   € ^ V n         ^ V n        ^ V n        ^ V n        R# )zO/new or /reset: reset per-session state (default: counters and token tracking).N)Úlast_prompt_tokensÚlast_completion_tokensÚlast_total_tokensÚcompression_countr3   s   &r   Úon_session_resetÚContextEngine.on_session_reset»   s$   € ð #$ˆÔØ&'ˆÔ#Ø!"ˆÔØ!"ˆÖr   c                óF   <€ V ^8„  d   QhRS[ S[S[S[3,          ,          /# r/   rd   )r   r0   s   "€r   r   r1   Ì   s!   ø€ ÷ ñ ¡$¡t©C±¨H¥~Õ"6ñ r   c                ó   € . # )z>Tool schemas this engine exposes to the agent (default: none).r&   r3   s   &r   Úget_tool_schemasÚContextEngine.get_tool_schemasÌ   s   € àˆ	r   c                óB   <€ V ^8„  d   QhRS[ RS[S[ S[3,          RS[ /# )r   r5   Úargsr   )r   r;   r   )r   r0   s   "€r   r   r1   Ð   s/   ø€ ÷ Mñ M¡Sð M±±S¹#°Xµð MÉSñ Mr   c                ó8   € \         P                  ! RRV 2/4      # )z~Handle a call to one of this engine's tools; must return a JSON string.
kwargs may include ``messages`` (live in-memory list).ÚerrorzUnknown context engine tool: )ÚjsonÚdumps)r4   r5   r‘   rc   s   &&&,r   Úhandle_tool_callÚContextEngine.handle_tool_callÐ   s    € ô �zŠz˜7Ð&CÀDÀ6Ð$JÐKÓLÐLr   c                ó6   <€ V ^8„  d   QhRS[ S[S[3,          /# r/   r:   )r   r0   s   "€r   r   r1   Õ   s   ø€ ÷ 
ñ 
™D¡¡c �Nñ 
r   c                óì   € \        V P                  ^ 4      pRTRV P                  RV P                  RV P                  '       d$   \	        ^dWP                  ,          ^d,          4      M^ RV P
                  /# )z:Status dict with the standard fields run_agent.py expects.r†   Úthreshold_tokensÚcontext_lengthÚusage_percentr‰   )Úmaxr†   rš   r›   Úminr‰   )r4   Úlast_prompts   & r   Ú
get_statusÚContextEngine.get_statusÕ   so   € ô ˜$×1Ñ1°1Ó5ˆà  +Ø × 5Ñ 5Ø˜d×1Ñ1ØÐRV×Re×ReÐReœS  k×4GÑ4GÕ&GÈ#Õ&MÔNÐklØ ×!7Ñ!7ð
ð 	
r   c                ó   <€ V ^8„  d   QhRR/# )r   r   r-   r&   )r   r0   s   "€r   r   r1   â   s   ø€ ÷ #ñ # ñ #r   c                ó.   € \         P                  ! V 4      # )aT  Per-agent instance of a plugin-registered engine (the plugin system holds ONE shared
instance; every AIAgent gets its own so a child's update_model() cannot mutate the parent's).
Override when the engine holds uncopyable state (locks, DB connections): return a fresh
engine sharing the durable backend and copying only mutable budget state.)ÚcopyÚdeepcopyr3   s   &r   Úclone_for_agentÚContextEngine.clone_for_agentâ   s   € ô
 �}Š}˜TÓ"Ð"r   c                óB   <€ V ^8„  d   QhRS[ RS[RS[ RS[ RS[ RS[ RR/# )	r   Úmodelr›   Úbase_urlÚapi_keyÚproviderÚapi_moder   N)r   rC   )r   r0   s   "€r   r   r1   é   sP   ø€ ÷ Mñ MÙðMÙ*-ðMÙ9<ðMÙLOðMáðMá&)ðMð 
ñMr   c                ó  € W n         ^ RIHp \        V R4      '       g   V P                  V n        V! V\        V R/ 4      V P
                  V4      V n        V P                  V n        \        W P                  ,          4      V n	        R# )u  Model switch / fallback: recompute threshold_tokens (override for more).

Per-model threshold override (longest substring match), else the raw config
percent â€” snapshotted ONCE so repeated switches fall back to the configured
value, not the previous model's override.
)Úresolve_model_thresholdÚ_config_threshold_percentÚmodel_thresholdsN)
r›   Úagent.context_compressorr¯   ÚhasattrÚthreshold_percentr°   r'   Ú_base_threshold_percentrC   rš   )r4   r©   r›   rª   r«   r¬   r­   r¯   s   &&&&&&& r   Úupdate_modelÚContextEngine.update_modelé   su   € ð -ÔÝDÜ�tÐ8×9Ò9Ø-1×-CÑ-CˆDÔ*Ù'>Ø”7˜4Ð!3°RÓ8¸$×:XÑ:XÐZbó(dˆÔ$à!%×!=Ñ!=ˆÔÜ # N×5KÑ5KÕ$KÓ LˆÖr   c                ó’   <€ V ^8„  d   Qh/ S[ ;R&   S[ ;R&   S[ ;R&   S[ ;R&   S[ ;R&   S[ ;R&   S[;R&   S[ ;R&   S[ ;R	&   S[;R
&   # )r   r†   r‡   rˆ   rš   r›   r‰   r´   Úprotect_first_nÚprotect_last_nr$   )rC   ÚfloatrD   )r   r0   s   "€r   r   r1   0   s‡   ø‡ ‚ ñ Ññ ñ  Ñ#ñ ñ Ññ ñ Ññ ñ Ññ ñ Ññ ñ( Ñ#ñ) ñ* Ññ+ ñ, Ññ- ñ2 '+Ñ1ò3 r   )	rµ   r°   r‰   r›   r‡   r†   rˆ   r´   rš   )N)NNFÚ )r¼   r¼   r¼   r¼   )(Ú__name__Ú
__module__Ú__qualname__Ú__firstlineno__Ú__doc__Úpropertyr   r5   r†   r‡   rˆ   rš   r›   r‰   r´   r¹   rº   r$   r=   rG   rL   rT   rX   r`   rf   rm   rr   r%   ry   r~   r‚   rŠ   rŽ   r–   r    r¦   r¶   Ú__annotate_func__Ú__static_attributes__Ú__classdictcell__)r0   s   @r   r-   r-   0   so  ø‡ € Ù8àØ÷;ó ó ð;ð  ÐØ"#ÐØÐØÐØ€NØÐð  $ÐØ€OØ€Nð .2Ð$à÷ó ðð ÷?ñ ?ó ð?÷9ò 9ð ÷
ñ 
ó ð
÷	ò 	ðØnrðà26ðàMN÷ò ÷,ò ÷ð ÷ð ÷
	Rð 	R÷ð ÷
eð e÷[ð [÷#ð #÷"ð ÷Mð M÷

ð 
÷#ð #÷Mò M÷s ƒ r   r-   )rÁ   r¤   r”   Úabcr   r   Útypingr   r   r   r   Úagent.compression_markerr   Úagent.redactr	   r   r   r   r   r+   r-   r&   r   r   Ú<module>rÊ      sP   ðñó Û ß #ß ,Ó ,å 1Ý .ð !Ð Ø"Ð Ø"Ð õ[õ(ôJM�Cö JMr   