+
    iHàjª!  ã                  óT  € R t ^ RIHt ^ RIt^ RIt]P
                  ! ]4      t]P                  ! R]P                  4      t
]P                  ! R]P                  4      t]P                  ! R]P                  4      tR R lt ! R R	4      t]P                  ! R
4      tR R ltR R ltR# )aq  Memory-context fencing: how recalled memory is wrapped for the model and kept out of output.

``build_memory_context_block`` fences prefetched recall for the prompt; ``sanitize_context`` and
``StreamingContextScrubber`` strip those fences (and anything a provider pre-wrapped) from text that
leaves the agent. Pure text formatting: no provider calls, no profile state.
)ÚannotationsNz</?\s*memory-context\s*>z5<\s*memory-context\s*>[\s\S]*?</\s*memory-context\s*>z¨\[System note:\s*The following is recalled memory context,\s*NOT new user input\.\s*Treat as (?:informational background data|authoritative reference data[^\]]*)\.\]\s*c               ó    € V ^8„  d   QhRRRR/# ©é   ÚtextÚstrÚreturn© )Úformats   "Ú9/home/ubuntu/.hermes/hermes-agent/agent/memory_context.pyÚ__annotate__r      s   € ÷ ñ ˜3ð  3ñ ó    c                óZ   € \         \        \        3 F  pVP                  RV 4      p K  	  V # )zQStrip fence tags, injected context blocks, and system notes from provider output.Ú )Ú_INTERNAL_CONTEXT_REÚ_INTERNAL_NOTE_REÚ_FENCE_TAG_REÚsub)r   Úpatterns   & r   Úsanitize_contextr      s*   € ä(Ô*;¼]ÓKˆØ�{‰{˜2˜tÓ$Šñ Là€Kr   c                  óŒ   € ] tR t^tRtRtRtR R ltR R ltR R	 lt	R
 R lt
]R R l4       tR R ltR R ltR R ltRtR# )ÚStreamingContextScrubbera^  Stateful scrubber for streaming text whose memory-context spans may straddle deltas.

``sanitize_context`` needs both tags in one string, so a split span would leak to the UI;
this holds back partial-tag tails between ``feed()`` calls and drops span interiors.
One scrubber (or ``reset()``) per top-level response; call ``flush()`` at end of stream.
z<memory-context>z</memory-context>c               ó   € V ^8„  d   QhRR/# ©r   r   ÚNoner	   )r
   s   "r   r   Ú%StreamingContextScrubber.__annotate__)   s   € ÷ ñ ˜$ñ r   c                	ó&   € V P                  4        R # ©N)Úreset©Úselfs   &r   Ú__init__Ú!StreamingContextScrubber.__init__)   s   € Ø�
‰
Žr   c               ó   € V ^8„  d   QhRR/# r   r	   )r
   s   "r   r   r   ,   s   € ÷ -ñ -�tñ -r   c                	ó0   € R V n         RV n        RV n        R# )Fr   TN)Ú_in_spanÚ_bufÚ_at_block_boundaryr   s   &r   r   ÚStreamingContextScrubber.reset,   s   € Ø#ˆŒØˆŒ	Ø(,ˆÖr   c               ó    € V ^8„  d   QhRRRR/# r   r	   )r
   s   "r   r   r   1   s   € ÷ ñ ˜ð  ñ r   c                ón  € V'       g   R# V P                   V,           pRV n         . pV'       Edw   V P                  '       d>   V P                  pVP                  4       P	                  V4      pV P                  W$4      pM‡V P                  pV P                  V4      p\        V4      pVP                  4       P                  V4      '       d   V P                  VRV)  4      '       d   TM^ pT;'       g    V P                  W$4      pVR8X  dG   V P                  '       g    T P                  Y6'       d   VRV)  MT4       V'       d   W&) R MRV n         MSV P                  '       g   V P                  W2RV 4       W%\        V4      ,           R pV P                  '       * V n        EK  RP                  V4      # )z^Return the visible portion of ``text``; a possible partial tag tail is held for the next call.r   Néÿÿÿÿ)r&   r%   Ú
_CLOSE_TAGÚlowerÚfindÚ_max_partial_suffixÚ	_OPEN_TAGÚ_find_boundary_open_tagÚlenÚendswithÚ_ends_at_block_boundaryÚ_append_visibleÚjoin)	r    r   ÚbufÚoutÚtagÚidxÚheldÚnÚpendings	   &&       r   ÚfeedÚStreamingContextScrubber.feed1   s_  € çÙØ�i‰i˜$ÕˆØˆŒ	ØˆßˆcØ�}�}ˆ}Ø—o‘o�Ø—i‘i“k×&Ñ& sÓ+�Ø×/Ñ/°Ó9‘à—n‘n�Ø×2Ñ2°3Ó7�ä˜“H�Ø"Ÿy™y›{×3Ñ3°C×8Ò8¸T×=YÑ=YÐZ]Ð^aÐ`aÐ_aÐZb×=cÒ=c™!Ðij�Ø×DÐD $×":Ñ":¸3Ó"D�Ø�bŒyà—}—}�}Ø×(Ñ(¨¾T¨c°&°D°5©kÀsÔKß+/˜C  ™K°R�”	ØØ—=—=�=Ø×$Ñ$ S¨d¨s¨)Ô4ØœC ›H•n�oÐ&ˆCØ $§¡Ô-ˆD�MØ�w‰w�s‹|Ðr   c               ó   € V ^8„  d   QhRR/# )r   r   r   r	   )r
   s   "r   r   r   P   s   € ÷ ñ �sñ r   c                ób   € V P                   '       d   RMV P                  pRV n        RV n         V# )z˜Emit the held-back tail at end-of-stream; inside an unterminated span it is discarded
(leaking partial memory context is worse than a truncated answer).r   F)r%   r&   )r    Útails   & r   ÚflushÚStreamingContextScrubber.flushP   s*   € ð —]—]�]‰r¨¯	©	ˆØˆŒ	ØˆŒØˆr   c               ó$   € V ^8„  d   QhRRRRRR/# )r   r7   r   r9   r   Úintr	   )r
   s   "r   r   r   Y   s&   € ÷ Rñ R ð R¨3ð R°3ñ Rr   c                óÖ   aa€ VP                  4       V P                  4       uoo\        \        \        S4      \        S4      ^,
          4      ^ R4      p\	        VV3R lV 4       ^ 4      # )zXLength of the longest buf-suffix that is a (case-insensitive) prefix of ``tag``, else 0.c              3  ób   <"  € T F$  pSP                  SV) R  4      '       g   K   Vx € K&  	  R # 5ir   )Ú
startswith)Ú.0ÚiÚ	buf_lowerÚ	tag_lowers   & €€r   Ú	<genexpr>Ú?StreamingContextScrubber._max_partial_suffix.<locals>.<genexpr>]   s(   øé € ÐM¡˜1¨	×(<Ñ(<¸YÈÀrÀs¸^×(L—Q’Q£ùs   ƒ/¥
/r+   )r-   ÚrangeÚminr2   Únext)r7   r9   ÚspanrL   rM   s   && @@r   r/   Ú,StreamingContextScrubber._max_partial_suffixX   sR   ù€ ð  #Ÿy™y›{¨C¯I©I«KÐˆ	�9Ü”Sœ˜Y›¬¨Y«¸!Õ);Ó<¸aÀÓDˆÜÕM¡ÓMÈqÓQÐQr   c               ó    € V ^8„  d   QhRRRR/# )r   r7   r   r   rF   r	   )r
   s   "r   r   r   _   s   € ÷ 	ñ 	¨3ð 	°3ñ 	r   c                ó^  € VP                  4       \        V P                  4      r2VP                  V P                  4      pVR8w  dg   WC,           pV P	                  VRV 4      '       d    V\        V4      8  d   W,          R9   d   V# VP                  V P                  V^,           4      pKm  R# )zVFind an opening fence only when it starts a block-like span (own line, newline after).Nz
r+   )r-   r2   r0   r.   r4   )r    r7   rL   Útag_lenr:   Ú	after_idxs   &&    r   r1   Ú0StreamingContextScrubber._find_boundary_open_tag_   sˆ   € à ŸY™Y›[¬#¨d¯n©nÓ*=�7Ø�n‰n˜TŸ^™^Ó,ˆØ�RŒiØ�ˆIØ×+Ñ+¨C°°¨I×6Ò6¸9ÄsÈ3ÃxÔ;OÐTWÕTbÐflÔTlØ�
Ø—.‘. §¡°°qµÓ9ŠCØˆ	r   c               ó    € V ^8„  d   QhRRRR/# )r   r   r   r   Úboolr	   )r
   s   "r   r   r   j   s   € ÷ Mñ M¨Cð M°Dñ Mr   c                óž   € VP                  R4      w  r#pVP                  4       R8H  ;'       d     \        V4      ;'       g    V P                  # )z Whether emitting ``text`` leaves the stream at a line start (blank tail after the last newline;
no newline at all -> only whitespace and already at a boundary).Ú
r   )Ú
rpartitionÚstripr[   r'   )r    r   Ú_headÚseprB   s   &&   r   r4   Ú0StreamingContextScrubber._ends_at_block_boundaryj   sB   € ð  Ÿ?™?¨4Ó0Ñˆ�DØ�z‰z‹|˜rÑ!×LÐL¤t¨C£y×'KÐ'K°D×4KÑ4KÐLr   c               ó$   € V ^8„  d   QhRRRRRR/# )r   r8   z	list[str]r   r   r   r   r	   )r
   s   "r   r   r   p   s&   € ÷ Iñ I 9ð I°Cð I¸Dñ Ir   c                	óh   € V'       d*   VP                  V4       V P                  V4      V n        R # R # r   )Úappendr4   r'   )r    r8   r   s   &&&r   r5   Ú(StreamingContextScrubber._append_visiblep   s)   € ßØ�J‰J�tÔØ&*×&BÑ&BÀ4Ó&HˆDÖ#ñ r   )r'   r&   r%   N)Ú__name__Ú
__module__Ú__qualname__Ú__firstlineno__Ú__doc__r0   r,   r!   r   r>   rC   Ústaticmethodr/   r1   r4   r5   Ú__static_attributes__r	   r   r   r   r      sT   † ñð #€IØ$€Jõõ-õ
õ>ð ôRó ðRõ	õM÷Iñ Ir   r   z
[-*+]\s+\Sc               ó    € V ^8„  d   QhRRRR/# r   r	   )r
   s   "r   r   r   {   s   € ÷ +ñ + cð +¨cñ +r   c                óÌ  € V P                  R4      p\        4       p. p\        V4       EF'  w  rEVP                  4       pV'       d1   V^ ,          P	                  4       '       d   VP                  V4       KN  \        \        P                  V4      4      pV'       d   V'       g   VP                  4        V'       dƒ   V^,           \        V4      8  d   W^,           ,          MRp\        VP                  4       4      ;'       d    V^ ,          P	                  4       p	V	'       g   Wb9   d   EK  VP                  V4       VP                  V4       EK*  	  RP                  V4      # )uZ  Drop a recalled bullet that an EARLIER line of this same block already states.

Providers merge several stores (and this merges several providers), so one prefetch routinely
surfaces the same fact two or three times. A byte-identical repeat inside one block tells the
model nothing the block has not already said, and it is not free: the composed block is stamped
into the user row's ``api_content`` sidecar and replayed verbatim on every later request for as
long as that row is in context, so each duplicate is paid once per turn, forever.

The ``seen`` set is scoped per section â€” every non-bullet line at column 0 (a heading of any
style, a ``---`` rule, prose) starts a new one â€” so a repeat is only dropped when the SAME
section already states it.

Only a SELF-CONTAINED bullet is considered â€” a marker, whitespace, content, and no continuation
line indented beneath it. A bullet that carries continuation lines is never dropped and never
suppresses a later one, because two entries can share a headline and differ underneath it
(``- prefers draft PRs`` / ``  (logged 12 Jan, builtin)`` vs the same headline logged elsewhere):
dropping one would re-parent its provenance under the other and invent a record neither provider
reported. Headings â€” including ``**bold**`` ones â€” prose, blank lines, separators and numbered
items are left exactly as written.
r]   r   )ÚsplitÚsetÚ	enumerater_   Úisspacere   r[   Ú_RECALL_BULLET_REÚmatchÚclearr2   Úaddr6   )
r   ÚlinesÚseenÚkeptÚindexÚlineÚstrippedÚ	is_bulletÚ	followingÚcarries_continuations
   &         r   Ú_drop_repeated_recall_linesr�   {   sý   € ð* �J‰J�tÓ€EÜ“U€DØ€DÜ  ×'‰ˆØ—:‘:“<ˆ÷ ˜˜Q�Ÿ™×)Ò)Ø�K‰K˜ÔÙÜÔ*×0Ñ0°Ó:Ó;ˆ	çŸIØ�J‰JŒLßØ,1°A­I¼¸E»
Ô,B˜ a�iÖ(ÈˆIÜ#'¨	¯©Ó(9Ó#:×#UÐ#U¸yÈ½|×?SÑ?SÓ?UÐ ß'ØÔ#ÚØ—‘˜Ô"Ø�‰�D×ñ% (ð& �9‰9�T‹?Ðr   c               ó    € V ^8„  d   QhRRRR/# )r   Úraw_contextr   r   r	   )r
   s   "r   r   r   ©   s   € ÷ ñ ¨Cð °Cñ r   c                ó°   € V '       d   V P                  4       '       g   R# \        V 4      pW8w  d   \        P                  R4       \	        V4      pRV R2# )z:Wrap prefetched memory in a fenced block with system note.r   z6memory provider returned pre-wrapped context; strippeduÐ   <memory-context>
[System note: The following is recalled memory context, NOT new user input. Treat as authoritative reference data â€” this is the agent's persistent memory and should inform all responses.]

z
</memory-context>)r_   r   ÚloggerÚwarningr�   )rƒ   Ú	sanitizedÚcleans   &  r   Úbuild_memory_context_blockr‰   ©   sY   € ç˜k×/Ñ/×1Ò1ÙÜ  Ó-€IØÔä�‰ÐOÔPÜ'¨	Ó2€Eð	Vð ˆ'ð ð		ðr   )rk   Ú
__future__r   ÚloggingÚreÚ	getLoggerrg   r…   ÚcompileÚ
IGNORECASEr   r   r   r   r   rt   r�   r‰   r	   r   r   Ú<module>r�      sŸ   ðñõ #ã Û 	à	×	Ò	˜8Ó	$€à—
’
Ð6¸¿¹ÓF€Ø—z’zÐ"ZÐ\^×\iÑ\iÓjÐ Ø—J’Jð pØ‡M�MóÐ õ÷UIñ UIðt —J’J˜}Ó-Ð õ+÷\r   