+
    iHàjK!  ã                  ó  € R t ^ RIHt ^ RIt^ RIt^ RIt^ RIHt ^ RIH	t	H
t
 ]P                  ! ]4      t^t]P                  ! R4      t]P                  ! R4      t0 RmtR R ltR	 R
 ltR R ltR R ltR R ltR R ltR R ltR# )a‰  Re-attach user images on turns whose history was rebuilt from the session DB.

The DB stores user rows as text (``session_persistence._durable_content``): an image part becomes a
``[screenshot]`` line at its position (``[screenshot detail=low]`` when the caller chose a detail).
A turn rebuilt from the DB (every API-server request, ``--resume``, a gateway restart) therefore
showed the model that placeholder instead of the photo the user asked about one message earlier.
The bytes are not gone: the session-authority attach paths (CLI, the shared-gateway TUI, messaging
gateway, API server) commit them under the profile's content-addressed ``native-inputs/<sha256>/<name>``
and name that path in the same row (``[Image attached at: <path>]``,
written by ``image_routing.build_native_content_parts`` / ``session_api_media.restore_api_images``).

On the WIRE COPY only, when this turn sends images natively, the ``vision.replay_recent_images``
newest images (default 3, 0 = off) go back as ``image_url`` parts; older ones drop in whole
batches of that size (``replay_frontier``), so the cached prefix is rewritten at most once per
batch rather than on every new photo. The data URL comes from the same
``_file_to_data_url`` the live turn used, so the rebuilt row is byte-identical to the request that
first sent it and the prompt-cache prefix holds. Only retained bytes under this profile's
``native-inputs`` whose digest still matches are replayed; remote URLs (they can expire and 400
every later turn), missing files (the gateway's settled-media sweep collects them after
``MEDIA_CACHE_MAX_AGE_HOURS``) and anything outside the window keep their ``[screenshot]`` text.
)ÚannotationsN)ÚPath)ÚAnyÚOptionalz0^\[Image attached(?P<local> at)?: (?P<ref>.+)\]$z6^\[screenshot(?: detail=(?P<detail>low|high|auto))?\]$c               ó    € V ^8„  d   QhRRRR/# )é   ÚcfgÚdictÚreturnÚint© )Úformats   "Ú7/home/ubuntu/.hermes/hermes-agent/agent/image_replay.pyÚ__annotate__r   (   s   € ÷ 
ñ 
 dð 
¨sñ 
ó    c                óL  € T ;'       g    / P                  R4      p\        V\        4      '       d   TM/ P                  R\        4      pVf   \        # \        V\        4      '       d    \        V\
        4      '       g
   V^ 8¼  d   V# \        P                  RV\        4       ^ # )z\``vision.replay_recent_images`` as an int >= 0; an unusable value warns and disables replay.ÚvisionÚreplay_recent_imageszpvision.replay_recent_images=%r is not a non-negative integer (0 = off, default %d); not replaying earlier images)ÚgetÚ
isinstancer	   ÚDEFAULT_REPLAY_RECENT_IMAGESr   ÚboolÚloggerÚwarning)r   r   Úraws   &  r   Úresolve_replay_recent_imagesr   (   s‚   € à�iˆi�R�_‰_˜XÓ&€FÜ ¬×-Ò-‰6°2×
:Ñ
:Ð;QÔSoÓ
p€CØ
‚{Ü+Ð+Ü�#”s×Ò¤J¨s´D×$9Ò$9¸cÀQ¼hØˆ
Ü
‡N�Nð 2Ø36Ô8TôVár   c               ó    € V ^8„  d   QhRRRR/# )r   ÚcontentÚstrr
   zOptional[list[tuple[str, Any]]]r   )r   s   "r   r   r   5   s   € ÷ ñ  ð Ð(Gñ r   c           	     ó
  € V P                  R4      p\        V4       UUu. uF6  w  r#\        P                  V4      ;p'       g   K$  W$P	                  R4      3NK8  	  pppV Uu. uF$  p\
        P                  V4      ;p'       g   K"  VNK&  	  ppV'       d   \        V4      \        V4      8w  d   R# . p^ p\        WV4       F  w  w  r)p
W(8”  d%   VP                  RRP                  WV 4      34       TP                  RV
P	                  R4      '       d   V
P	                  R4      MRW‘V,          334       V^,           pK�  	  V\        V4      8  d%   VP                  RRP                  WR 4      34       V# u uppi u upi )	u‚  A DB-rebuilt user row as its original parts, in order: ``("text", str)`` for each text run and
``("image", (ref, detail, marker_line))`` per ``[screenshot]`` line, the markers pairing 1:1 in order
with the row's ``[Image attachedâ€¦]`` hints wherever the image sat (API callers put it first;
turn-time memory/hook context follows it). ``None`` when the counts disagree (a typed marker).Ú
ÚdetailNÚtextÚimageÚlocalÚrefÚ )
ÚsplitÚ	enumerateÚ
_MARKER_REÚmatchÚgroupÚ_HINT_REÚlenÚzipÚappendÚjoin)r   ÚlinesÚiÚlineÚmÚmarkersÚhintsÚpartsÚstartr!   Úhints   &          r   Ú_split_rebuilt_rowr:   5   sN  € ð
 �M‰M˜$Ó€EÜ5>¸uÔ5EÔgÑ5E©'¨!Ìz×O_ÑO_Ð`dÓOeÐJeÈ!ÓJeÔ%�—7‘7˜8Ó$Ó%Ñ5E€GÑgÙ Ó@™5�4¬(¯.©.¸Ó*>Ð%> QÓ%>�QˆQ™5€EÐ@ß”c˜%“j¤C¨£LÔ0ÙØ#%€EØ€EÜ  Ö0Ñ‰ˆ�TØŒ9Ø�L‰L˜& $§)¡)¨E¸¨NÓ";Ð<Ô=Ø�‰�g°T·Z±ZÀ×5HÒ5H §
¡
¨5Ô 1ÈbÐRXÐ`aÕZbÐcÐdÔeØ�A•Šñ	 1ð
 Œs�5‹zÔØ�‰�f˜dŸi™i¨¨f¨Ó6Ð7Ô8Ø€Lùó hùÚ@s    !E:ÁE:Á#F ÂF c               ó    € V ^8„  d   QhRRRR/# )r   r%   r   r
   zOptional[str]r   )r   s   "r   r   r   K   s   € ÷ #ñ #˜Cð # Mñ #r   c                ó²  € V '       g   R# ^ RI Hp ^ RIHp \	        V 4      p VP
                  P
                  V! 4       8w  g-   VP                  4       '       g   VP                  4       '       g   R# \        P                  ! VP                  4       4      P                  4       VP
                  P                  8w  d   R#  T! T4      #   \         d     R# i ; i)zVData URL for a retained local image this profile still holds unchanged, else ``None``.N)Ú_file_to_data_url)Únative_inputs_root)Úagent.image_routingr=   Úgateway.session_ingress_mediar>   r   ÚparentÚ
is_symlinkÚis_fileÚhashlibÚsha256Ú
read_bytesÚ	hexdigestÚnameÚOSError)r%   r=   r>   Úpaths   &   r   Ú_retained_data_urlrK   K   s¥   € çÙÝ5Ý@ä�‹9€DðØ�;‰;×ÑÑ!3Ó!5Ô5¸¿¹×9JÒ9JÐRV×R^ÑR^×R`ÒR`ÙÜ�>Š>˜$Ÿ/™/Ó+Ó,×6Ñ6Ó8¸D¿K¹K×<LÑ<LÔLÙð Mñ ˜TÓ"Ð"øô ô Úðús   £5C ÁC Á1AC ÃCÃCc               ó$   € V ^8„  d   QhRRRRRR/# )r   Útotalr   Úkeepr
   r   )r   s   "r   r   r   ]   s&   € ÷ 	@ñ 	@˜3ð 	@ cð 	@¨cñ 	@r   c                ó>   € W8”  d   W,
          V,          V,          # ^ # )a9  Oldest image slot still replayed when the request holds ``total`` images.

The frontier moves forward only in whole batches of ``keep``: the newest ``keep`` to
``2 * keep - 1`` images replay, and an image that drops out stays dropped as the session
grows. A strict newest-``keep`` window would turn one early row back into ``[screenshot]``
text on every new photo, rewriting the cached prefix each time; this rewrites it at most once
per ``keep`` new images. Counted by slot position (missing or changed files hold their slot),
so a swept file never moves the frontier.r   )rM   rN   s   &&r   Úreplay_frontierrP   ]   s   € ð -2¬LˆE�L˜TÕ! DÕ(Ð?¸aÐ?r   c               ó$   € V ^8„  d   QhRRRRRR/# )r   Úapi_messagesúlist[dict[str, Any]]rN   r   r
   r   )r   s   "r   r   r   i   s"   € ÷ $ñ $Ð';ð $À3ð $È3ñ $r   c                ó@  € ^ p. pV  F²  pVP                  R4      R8w  d   K  VP                  R4      p\        V\        4      '       d   V\        R V 4       4      ,          pK]  \        V\        4      '       d   \        V4      MRpVf   K†  VP                  WFV34       V\        R V 4       4      ,          pK´  	  \        W!4      p^ pV FÎ  w  rFp	. p
^ pV F¨  w  rÍVR8X  d   V
P                  RRRV/4       K#  W—8¼  d   \        V^ ,          4      MRpV	^,          p	V'       d?   T
P                  RR	R	R
V/V^,          '       d   RV^,          /M/ C/4       V^,          pKŒ  V
P                  RRRV^,          /4       Kª  	  V'       g   KÂ  W¤R&   W‹,          pKÐ  	  V# )z¹Rebuild retained images at or after ``replay_frontier`` in place on ``api_messages`` (wire
copies); images still live in the conversation hold slots too. Returns the number re-attached.ÚroleÚuserr   c              3  óŽ   "  € T F;  p\        V\        4      '       g   K  VP                  R 4      \        9   g   K7  ^x € K=  	  R# 5i)ÚtypeN)r   r	   r   Ú_IMAGE_PART_TYPES)Ú.0Úps   & r   Ú	<genexpr>Ú'replay_recent_images.<locals>.<genexpr>s   s-   é € Ðj¡G˜q¬z¸!¼T×/BœÀqÇuÁuÈVÃ}ÔXiÑGiŸš£Gùs   ‚AŸA»
ANc              3  ó<   "  € T F  w  rVR 8X  g   K  ^x € K  	  R# 5i)r#   Nr   )rZ   ÚkindÚ_s   &  r   r\   r]   y   s   é € Ð?¡e™7˜4¨t°w©—Q’Q£eùs   ‚’
r"   rX   Ú	image_urlÚurlr!   )	r   r   ÚlistÚsumr   r:   r/   rP   rK   )rR   rN   ÚslotsÚrowsÚmsgr   r7   ÚfrontierÚattachedÚslotÚrebuiltÚurlsr_   Úvaluerb   s   &&             r   r   r   i   s–  € ð €EØDF€DÛˆØ�7‰7�6‹?˜fÔ$ÙØ—'‘'˜)Ó$ˆÜ�gœt×$Ò$Ø”SÑj¡GÓjÓjÕjˆEÙÜ/9¸'Ä3×/GÒ/GÔ" 7Ô+ÈTˆØŠ=ÙØ�‰�S Ð'Ô(Ø”Ñ?¡eÓ?Ó?Õ?Šñ ô ˜uÓ+€HØ€HÛ Ñˆ�DØ(*ˆØˆÛ ‰KˆDØ�vŒ~Ø—‘ ¨°¸Ð>Ô?ÙØ26Ô2BÔ$ U¨1¥XÔ.ÈˆCØ�A�IˆDßØ—‘ ¨°[À5È#ÐB|ÐjoÐpq×jrÔjrÐS[Ð]bÐcdÕ]eÑRfÐxzÐB|Ð}Ô~Ø˜•	’à—‘ ¨°¸¸a½ÐAÖBñ !÷ ‰4Ø$�	‰NØÕŠHñ! !ð" €Or   c               ó    € V ^8„  d   QhRRRR/# )r   Úagentr   r
   r   r   )r   s   "r   r   r   �   s   € ÷ ñ ˜sð  sñ r   c                ó‚  € \        V RR4      ;'       g    RP                  4       p\        V RR4      ;'       g    RP                  4       p\        V RR4      W3p\        V RR4      pVe   V^ ,          V8X  d
   V^,          # ^ RIHp ^ RIHp V! 4       p\        V4      pV^ 8”  d   V! WV4      R	8w  d   ^ pW83V n        V# )
z³Images to replay this turn: 0 unless the model takes images natively. Resolved once per turn
(keyed on the turn's frozen admission clock and the model), not per tool-loop request.Úproviderr&   ÚmodelÚ_current_turn_timestampNÚ_image_replay_window)Údecide_image_input_mode)Úload_config_readonlyÚnative)ÚgetattrÚstripr?   ru   Úhermes_cli.configrv   r   rt   )	ro   rq   rr   ÚkeyÚcachedru   rv   r   rN   s	   &        r   Ú_turn_replay_windowr}   �   sÂ   € ô ˜˜z¨2Ó.×4Ð4°"×;Ñ;Ó=€HÜ�U˜G RÓ(×.Ð.¨B×5Ñ5Ó7€EÜ�5Ð3°TÓ:¸HÐ
L€CÜ�UÐ2°DÓ9€FØÒ˜f Q�i¨3Ô.Ø�a�yÐÝ;Ý6á
Ó
 €CÜ'¨Ó,€DØˆa„xÑ+¨H¸SÓAÀXÔMØˆØ"% €EÔØ€Kr   c               ó$   € V ^8„  d   QhRRRRRR/# )r   ro   r   rR   rS   r
   ÚNoner   )r   s   "r   r   r   ¤   s'   € ÷ ^ñ ^˜Cð ^Ð/Cð ^Èñ ^r   c                óü   € \         ;QJ d    R V 4       F  '       g   K   RM	  RM! R V 4       4      '       g   R# \        V 4      ;p^ 8:  d   R# \        W4      ;p'       d   \        P	                  RV4       R# R# )zV``build_api_messages`` hook: replay only when this turn's model takes images natively.c              3  óÀ   "  € T FT  pVP                  R 4      R8H  ;'       d4    \        VP                  R4      \        4      ;'       d    RVR,          9   x € KV  	  R# 5i)rU   rV   r   z[screenshotN)r   r   r   )rZ   r4   s   & r   r\   Ú#replay_for_agent.<locals>.<genexpr>¦   s\   é € ð HÙ:F°Qð �u‰u�V‹} Ñ&÷ 1ð 1¬:°a·e±e¸IÓ6FÌÓ+L÷ 1ð 1Ø  A i¥LÑ0ô1Û:Fùs   ‚A¢&AÁ	ATFNz<image_replay: re-attached %d earlier image(s) to the request)Úanyr}   r   r   Úinfo)ro   rR   rN   ri   s   &&  r   Úreplay_for_agentr…   ¤   st   € ç‹3ñ HÙ:FóH�3�3Š3ñ HÙ:FóH÷ Hò HáÜ# EÓ*Ð*ˆ¨qÔ0ÙÜ'¨Ó;Ð;€xÖ;Ü�‰ÐRÐT\Ö]ñ <r   >   r#   ra   Úinput_image)Ú__doc__Ú
__future__r   rD   ÚloggingÚreÚpathlibr   Útypingr   r   Ú	getLoggerÚ__name__r   r   Úcompiler,   r)   rY   r   r:   rK   rP   r   r}   r…   r   r   r   Ú<module>r�      s~   ðñõ, #ã Û Û 	Ý ß  à	×	Ò	˜8Ó	$€à Ð Ø�:Š:ÐIÓJ€à�ZŠZÐQÓR€
Ú9Ð õ
õõ,#õ$	@õ$õN÷(^r   