+
    lHàj,  ã                  óP  a € 0 t $ R t^ RIHt ^ RIt^ RIt^RIHtH	t	 ]! 4       t
R'tR(t^tR)tR]R&   ]P                   ! R4      tAR R ltR	 R
 ltR R ltR*R R l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 R ltR R ltR R  ltR! R" ltR# R$ lt R% R& lt!R# )+zâAttachment staging: image sniffing, size caps, per-session attachment dirs, path resolution.

Bodies are rebound onto server.py's globals at install time (see
method_ctx.bind_module), so they reference server.py globals bare.
)ÚannotationsN)ÚHandlerRegistryÚbind_moduleztuple[tuple[bytes, str], ...]Ú_IMAGE_MAGICz[\s()\[\]{}<>"'`]c               ó(   € V ^8„  d   QhRRRRRRRR/# )é   ÚrawÚstrÚdata_url_reÚflagsÚintÚreturnÚbytes© )Úformats   "ÚC/home/ubuntu/.hermes/hermes-agent/tui_gateway/prompt_attachments.pyÚ__annotate__r      s.   € ÷ Jñ J�cð J¨ð J°Cð J¸Eñ Jó    c                óè   € ^ RI p^ RIpT ;'       g    RP                  4       pVP                  ! WV4      ;p'       d   VP	                  ^4      pVP                  VP                  ! RRV4      RR7      # )zXStrip an optional ``data:...;base64,`` wrapper and all whitespace, then strictly decode.NÚ z\s+T)Úvalidate)Úbase64ÚreÚstripÚmatchÚgroupÚ	b64decodeÚsub)r   r
   r   Ú_base64Ú_reÚcleanedÚms   &&&    r   Ú_b64_payloadr"      sb   € ãÛØ�yˆy�b×ÑÓ!€GØ�IŠI�k¨EÓ2Ð2€qÖ2Ø—'‘'˜!“*ˆØ×Ñ˜SŸWšW V¨R°Ó9ÀDÐÓIÐIr   c               ó$   € V ^8„  d   QhRRRRRR/# )r   r   r	   Úmime_prefixr   zbytes | Noner   )r   s   "r   r   r   )   s!   € ÷ ñ ˜sð °Cð ¸Lñ r   c               ó†   € ^ RI p \        V RVP                  ! V4       R2VP                  4      #   \         d     R# i ; i)zPDecode a (``data:<mime_prefix>...;base64,``-wrapped) payload; None when invalid.Nz^data:z[a-zA-Z0-9.+-]*;base64,(.*)$)r   r"   ÚescapeÚDOTALLÚ	Exception)r   r$   r   s   &$ r   Ú_decode_attach_base64r)   )   sM   € ãðÜØ�F˜3Ÿ:š: kÓ2Ð3Ð3OÐPÐRU×R\ÑR\ó^ð 	^øäô Úðús   †*1 ±A ¿A c          
     ó,   € V ^8„  d   QhRRRRRRRRRR/# )r   Úraw_b64r	   r$   Ú	max_bytesr   ÚlabelÚ	empty_msgr   )r   s   "r   r   r   3   s4   € ÷ ñ ØðØ'*ðØ7:ðØCFðØSVñr   c               óê   € \        WR7      pVf   R\        V RR4      3# V'       g   R\        V RV4      3# \        V4      V8”  d+   VR	,          pR\        V RV R\        V4       RV R24      3# VR3# )
zX``(bytes, None)`` or ``(None, error)``: 4017 on bad/empty base64, 4018 over *max_bytes*.)r$   Ni±  zdata is not valid base64i²  z too large (z bytes; cap is z MB)i   )r)   Ú_errÚlen)Úridr+   r$   r,   r-   r.   ÚdataÚmbs   &&$$$$  r   Ú_decode_attach_payloadr5   3   s�   € ô ! ÔB€DØ‚|Ø”T˜#˜tÐ%?Ó@Ð@Ð@ßØ”T˜#˜t YÓ/Ð/Ð/Ü
ˆ4ƒy�9ÔØ˜;Õ'ˆØ”T˜#˜t¨ w¨l¼3¸t»9¸+À_ÐUWÐTXÐX\Ð%]Ó^Ð^Ð^Ø�ˆ:Ðr   c               ó$   € V ^8„  d   QhRRRRRR/# )r   Ú	img_bytesr   Úfilenamer	   r   r   )r   s   "r   r   r   A   s&   € ÷ Tñ T ð T°ð T¸cñ Tr   c                ó  a€ ^ RI Hp V'       d+   V! V4      P                  P                  4       ;p'       d   V# V R,          oSP	                  R4      '       d   SR,          R8X  d   R# \        V3R l\         4       R4      # )	zYExtension from the filename hint, else magic bytes (WebP: RIFF container), else ``.png``.©ÚPath:Né   Ns   RIFF:é   é   Ns   WEBPú.webpc              3  ó^   <"  € T F"  w  rSP                  V4      '       g   K  Vx € K$  	  R # 5i)N)Ú
startswith)Ú.0ÚsigÚextÚheads   &  €r   Ú	<genexpr>Ú#_sniff_image_ext.<locals>.<genexpr>I   s    øé € ÐJ¡\™˜°T·_±_ÀS×5I—’£\ùs   ƒ-£
-ú.png)Úpathlibr;   ÚsuffixÚlowerrA   Únextr   )r7   r8   r;   rJ   rE   s   &&  @r   Ú_sniff_image_extrM   A   sd   ø€ åß™t H›~×4Ñ4×:Ñ:Ó<Ð<�VÖ<ØˆØ�S�>€DØ‡��w×Ò D¨¥J°'Ô$9ÙÜÔJ¥\ÓJÈFÓSÐSr   c               ó   € V ^8„  d   QhRR/# )r   r   zfrozenset[str]r   )r   s   "r   r   r   L   s   € ÷ Mñ M >ñ Mr   c                 ób   €  ^ RI Hp  \        V 4      #   \         d    \        0 Rm4      u # i ; i)é    ©Ú_IMAGE_EXTENSIONS>   ú.bmpú.gifú.jpgrH   ú.jpegr?   )ÚclirR   Ú	frozensetr(   rQ   s    r   Ú_allowed_image_extensionsrY   L   s4   € ðMÝ)ÜÐ*Ó+Ð+øÜô MÜÒKÓLÒLðMús   ‚ “.­.c               ó$   € V ^8„  d   QhRRRRRR/# )r   ÚsessionÚdictÚnamer	   r   r;   r   )r   s   "r   r   r   T   s&   € ÷ Iñ I˜tð I¨3ð I°4ñ Ir   c                óæ   € V P                  R4      pVR8X  d4   \        V4      R8X  d$   \        V 4      ;p'       d   VR,          R,          # V'       d   \        V4      V,          # \        V,          # )u;  ``<session home>/<name>``, anchored on the session's stored ``profile_home``: attach
RPCs run BEFORE ``prompt.submit`` installs the profile HERMES_HOME override, while
the sandbox mounts and the vision host-read allowlist resolve the *session profile's*
dirs at run time â€” writing anywhere else means the agent can never see the file.

``attachments`` instead follows the session workspace when the profile's config opts
in via ``attachments.storage: workspace`` (#110662): staging then lands inside the
allowed ref root, so the ``@file:`` ref stays workspace-relative.Úprofile_homeÚattachmentsÚ	workspacez.hermes)ÚgetÚ_profile_attachments_storageÚ_session_attachments_workspacer;   Ú_hermes_home)r[   r]   r_   ra   s   &&  r   Ú_session_home_dirrf   T   sc   € ð —;‘;˜~Ó.€LØˆ}ÔÔ!=¸lÓ!KÈ{Ô!ZÜ6°wÓ?Ð?ˆ9Ö?Ø˜yÕ(¨=Õ8Ð8ß".ŒD�ÓÀDÕHÐH´LÀDÕHÐHr   c               ó   € V ^8„  d   QhRR/# )r   r   r	   r   )r   s   "r   r   r   d   s   € ÷ ñ °#ñ r   c                óì  € ^ RI pV '       d   \        V 4      M\        pVP                  \        4      ;_uu_ 4        ^ RIHp VR,          pVP                  4       '       dx   V! V4      P                  R4      p\        V\        4      '       dK   \        VP                  R4      ;'       g    R4      P                  4       P                  4       uuRRR4       # RRR4       R#   + '       g   i     R# ; i)u/  The session profile's ``attachments.storage`` ("" unless it opts into "workspace").

Read from THAT profile's config.yaml â€” ``file.attach`` runs before ``prompt.submit``
installs the profile scope, so the process config still belongs to the launch profile
(same reason as ``_profile_configured_cwd``).N)Úload_user_config_effectivezconfig.yamlr`   Ústorager   )Ú
contextlibr;   re   Úsuppressr(   Úhermes_cli.config_effectiveri   Úexistsrb   Ú
isinstancer\   r	   r   rK   )r_   Ú_contextlibÚhomeri   Úcfg_pathÚattachments_cfgs   &     r   rc   rc   d   s­   € ó %ß!-Œ4�Ô´<€DØ	×	Ñ	œi×	(Õ	(ÝJØ˜-Õ'ˆØ�?‰?×ÒÙ8¸ÓB×FÑFÀ}ÓUˆOÜ˜/¬4×0Ò0Ü˜?×.Ñ.¨yÓ9×?Ð?¸RÓ@×FÑFÓH×NÑNÓP÷ 
)×	(Ò	(ñ ÷ 
)Ö	(ñ ús   »A/C"Â+"C"Ã"C3	c               ó    € V ^8„  d   QhRRRR/# )r   r[   r\   r   zPath | Noner   )r   s   "r   r   r   v   s   € ÷ ñ ¨Dð °[ñ r   c                óP  € ^ RI p\        V P                  R4      4      '       d   R# VP                  \        4      ;_uu_ 4        \        \        V 4      4      P                  4       pVP                  4       '       d   VuuRRR4       #  RRR4       R#   + '       g   i     R# ; i)ax  The session workspace when ``attachments.storage: workspace`` can actually write to it.

Only a workspace on THIS host can hold gateway-staged files: an ssh-profile cwd lives
on the remote execution host and a cwd that doesn't exist locally can't be vouched
for, so both keep the bind-mounted ``<profile home>/attachments`` that container and
remote backends receive (#76577).Nr_   )	rk   Ú_cwd_is_remoterb   rl   r(   r;   Ú_session_cwdÚresolveÚis_dir)r[   rp   ra   s   &  r   rd   rd   v   s|   € ó %Ü�g—k‘k .Ó1×2Ò2ÙØ	×	Ñ	œi×	(Õ	(Üœ gÓ.Ó/×7Ñ7Ó9ˆ	Ø×Ñ×ÒØ÷ 
)Ò	(à÷ 
)ñ ÷	 
)Ö	(ñ ús   Á:BÂB%	c               ó    € V ^8„  d   QhRRRR/# )r   r[   r\   r   r;   r   )r   s   "r   r   r   ‡   s   € ÷ 0ñ 0 ð 0¨$ñ 0r   c                ó   € \        V R 4      # )Úimages)rf   )r[   s   &r   Ú_session_images_dirr}   ‡   s   € Ü˜W hÓ/Ð/r   c               ó$   € V ^8„  d   QhRRRRRR/# )r   r7   r   rD   r	   Úprefixr   )r   s   "r   r   r   ‹   s!   € ÷ ñ ¨Eð ¸ð Èñ r   c               óÚ  € ^ RI p^ RIHp ^ RIpV! V 4      p V P	                  RRRR7       VP                  WR,           VR7      w  rxV! V4      p	 VP                  ! VR4      ;_uu_ 4       p
V
P                  V4       V
P                  4        VP                  ! V
P                  4       4       RRR4       V	#   + '       g   i     T	# ; i  \         d    T	P                  RR	7       h i ; i)
zKPrivate, collision-free staging shared by local and canonical byte uploads.Nr:   iÀ  T)ÚmodeÚparentsÚexist_okÚ_)Údirr   rJ   Úwb©Ú
missing_ok)ÚosrI   r;   ÚtempfileÚmkdirÚmkstempÚfdopenÚwriteÚflushÚfsyncÚfilenoÚBaseExceptionÚunlink)Ú	directoryr7   rD   r   r‰   r;   rŠ   Úfdr]   ÚpathÚoutputs   &&&$       r   Ústage_image_bytesr˜   ‹   sÆ   € ãÝÛÙ�Y“€IØ‡O�O˜¨°t€OÔ<Ø×Ñ I¸sµlÈ3ÐÓO�H€BÙ�‹:€DðØ�YŠY�r˜4× Ô  FØ�L‰L˜Ô#Ø�L‰LŒNØ�HŠH�V—]‘]“_Ô%÷ !ð €K÷ !Ö ð €Kûô ô Ø�‰˜tˆÔ$Øðús+   ÁC Á+AB7Â-C Â7C	ÃC ÃC ÃC*c          
     ó,   € V ^8„  d   QhRRRRRRRRRR	/# )
r   r[   r\   r7   r   rD   r	   r   r   r;   r   )r   s   "r   r   r   Ÿ   s1   € ÷ ñ  4ð °Eð Àð ÐPSð ÐX\ñ r   c               ó  € V P                  R^ 4      ^,           V R&   \        V 4      p \        WAW#R7      pT P                  R. 4      P                  \        T4      4       T#   \         d     \	        ^ T R,          ^,
          4      T R&   h i ; i)zQWrite image bytes into the session images dir and queue them for the next submit.Úimage_counter)r   Úattached_images)rb   r}   r˜   r(   ÚmaxÚ
setdefaultÚappendr	   )r[   r7   rD   r   Úimg_dirÚimg_paths   &&&$  r   Ú_queue_attached_imager¢   Ÿ   s�   € à&Ÿ{™{¨?¸AÓ>ÀÕB€GˆOÑÜ! 'Ó*€Gðô % W¸ÔLˆð ×ÑÐ(¨"Ó-×4Ñ4´S¸³]ÔCØ€Oøô	 ô Ü#& q¨'°/Õ*BÀQÕ*FÓ#Gˆ�Ñ Øðús   ©A" Á"*Bc               ó    € V ^8„  d   QhRRRR/# )r   Úvaluer	   r   r   )r   s   "r   r   r   ®   s   € ÷ ñ ˜Sð  Sñ r   c                ó‚   € V '       d   \         P                  V 4      '       g   V # R F  pW9  g   K  V V  V 2u # 	  V # )zPQuote a value with whitespace/brackets/quotes so the ``@file:`` ref round-trips.)Ú`Ú"Ú')Ú _ATTACHMENT_REF_NEEDS_QUOTING_REÚsearch)r¤   Úqs   & r   Ú_format_ref_valuer¬   ®   sD   € çÔ8×?Ñ?À×FÒFØˆÛˆØŽ>Ø�S˜˜ ˜sÐ#Ò#ñ ð €Lr   c               ó$   € V ^8„  d   QhRRRRRR/# )r   r[   r\   Útargetr;   r   r	   r   )r   s   "r   r   r   ¸   s!   € ÷ %ñ % $ð %°ð %¸ñ %r   c                ó(  € \        \        V 4      4      P                  4       p \        VP                  4       P	                  V4      4      P                  \        P                  R4      #   \         d    \        TP                  4       4      u # i ; i)zKWorkspace-relative path for an attachment, or the absolute path if outside.Ú/)	r;   rw   rx   r	   Úrelative_toÚreplacer‰   ÚsepÚ
ValueError)r[   r®   ra   s   && r   Ú_attachment_ref_pathrµ   ¸   sn   € ä”\ 'Ó*Ó+×3Ñ3Ó5€Ið%Ü�6—>‘>Ó#×/Ñ/°	Ó:Ó;×CÑCÄBÇFÁFÈCÓPÐPøÜô %Ü�6—>‘>Ó#Ó$Ò$ð%ús   ¤AA* Á*$BÂBc               ó    € V ^8„  d   QhRRRR/# )r   r]   r	   r   r   )r   s   "r   r   r   Á   s   € ÷ 8ñ 8 Cð 8¨Cñ 8r   c           
     óì   € ^ RI pVP                  ! RR\        \        T ;'       g    R4      P	                  4       4      P
                  4      pVP	                  4       P	                  R4      ;'       g    R# )rP   Nz[\x00-\x1f]+r„   r   Ú.Ú
attachment)r   r   r;   r	   r   r]   )r]   r   Ú	candidates   &  r   Ú_sanitize_attachment_namer»   Á   sU   € ÛØ—’˜¨¬d´3°t·z°z¸r³?×3HÑ3HÓ3JÓ.K×.PÑ.PÓQ€IØ�?‰?Ó×"Ñ" 3Ó'×7Ð7¨<Ð7r   c          
     ó,   € V ^8„  d   QhRRRRRRRRRR/# )	r   r[   r\   Úraw_pathr	   Údata_urlr]   r   ztuple[Path, bool]r   )r   s   "r   r   r   Ç   s;   € ÷ B"ñ B"ØðB"Ø #ðB"Ø/2ðB"Ø:=ðB"ØBSñB"r   c               óÒ  € \        \        V 4      4      P                  4       pRpV'       dr    ^ RIHpHpHp Vec   V! V4      p	V	'       d"   \        V	R,          4      P                  4       pM1X! V4      w  r«X! V
4      pVe   \        V4      P                  4       MRpVe    VP                  V4       VR3# V'       g   \        R4      h^ RIp^ RIp \        VRVP                  VP                   ,          4      p\%        T;'       g)    \        \'        T;'       g    R4      4      P                  4      p\)        T R	4      pTP+                  R
R
R7       \%        T4      pTT,          p\        T4      P,                  ;'       g    Rp\        T4      P.                  p^p  \0        P2                  P5                  T4      '       d   \7        T4      hTP9                  R4      p  T;_uu_ 4        TP;                  T4       RRR4       TP                  4       R
3#   \         d    Rp ELói ; i  \         d!    TP                  4       pTP                  p EL"i ; i  \        TP"                  3 d   p\        R4      ThRp?ii ; i  \6         d    TT RT T 2,          pT^,          p EK  i ; i  + '       g   i     L»; i  \         d    TP=                  R
R7       h i ; i)aL  Make a desktop file attachment available to the gateway agent: ``(stored_path, uploaded)``.
Inside the workspace -> as-is; gateway-visible but outside -> copied into ``attachments/``
(bind-mounted into container backends so ``@file:`` resolves in the sandbox); not on the
gateway -> ``data_url`` bytes decoded into ``attachments/``.N)Ú_detect_file_dropÚ_resolve_attachment_pathÚ_split_path_inputr–   Fz2file not found on gateway and no data_url providedz-^data:[^;,]*(?:;[^;,=]+=[^;,]+)*;base64,(.*)$zinvalid data_url payloadr   r`   T)r‚   rƒ   r¹   ÚxbÚ-r‡   )r;   rw   rx   rW   rÀ   rÁ   rÂ   r(   r±   r´   Ú
read_bytesr]   Úbinasciir   r"   r'   Ú
IGNORECASEÚErrorr»   r	   rf   r‹   ÚstemrJ   r‰   r–   ÚlexistsÚFileExistsErrorÚopenrŽ   r“   )r[   r½   r¾   r]   ra   ÚresolvedrÀ   rÁ   rÂ   ÚdroppedÚ
path_tokenÚ
_remainderÚfoundÚpayloadr8   Ú	_binasciir   ÚexcÚrootr®   rÉ   rJ   ÚcounterÚuploads   &$$$                    r   Ú_stage_session_file_attachmentrØ   Ç   sž  € ô ”\ 'Ó*Ó+×3Ñ3Ó5€IØ€Hßð	%ßZÑZð Ò(Ù'¨Ó1ˆGßÜ ¨¥Ó0×8Ñ8Ó:‘á):¸8Ó)DÑ&�
Ù0°Ó<�Ø49Ò4Eœ4 ›;×.Ñ.Ô0È4�ØÒð	%Ø× Ñ  Ô+Ø˜U�?Ð"÷
 ÜÐQÓRÐRã$Ûð	BÜ"ØÐJÈCÏJÉJÐY\×YgÑYgÕLgóiˆGô -¨T×-SÐ-S´T¼#¸h¿n¸nÈ"Ó:MÓ5N×5SÑ5SÓTˆÜ˜W mÓ4€DØ‡J�J�t d€JÔ+Ü(¨Ó2€HØ�H�_€FÜ�‹>×Ñ×.Ð. ,€DÜ�(‹^×"Ñ"€FØ€Gà
ð
	ô �w‰w�‰˜v×&Ò&Ü% fÓ-Ð-Ø—[‘[ Ó&ˆFð
 ðßŠVØ�L‰L˜Ô!÷ ð
 �>‰>Ó˜TÐ!Ð!øôo ô 	%Ø $Óð	%ûô ô 	%Ø×)Ñ)Ó+ˆGØ—}‘}‹Hð	%ûô ˜IŸO™OÐ,ô 	BÜÐ7Ó8¸cÐAûð	Bûô$ ô 	Ø˜t˜f A g Y¨v¨hÐ7Õ7ˆFØ�q�LŒGð	ú÷ �Vûäô Ø�‰ ˆÔ&Øðús}   ®
H Â#H/ Ã(I Æ"$J ÇJ Ç%
K Ç/J4ÈK ÈH,È+H,È/'IÉIÉJÉ4J Ê JÊ$J1Ê0J1Ê4K	Ê?K ËK ËK&c               ó   € V ^8„  d   QhRR/# )r   r   ÚNoner   )r   s   "r   r   r     s   € ÷ 0ñ 0˜ñ 0r   c                ó2   € \        \        4       V RR7       R# )zQPublish this module's helpers + handlers onto ``server``, rebound to its globals.)ÚskipN)r„   )r   Úglobals)Úservers   &r   Úregisterrß     s   € ä”“	˜6¨×/r   i  �i   ))s   ‰PNG

rH   )s   ÿØÿrU   )s   GIF87arT   )s   GIF89arT   )s   BMrS   )r   )"Ú__conditional_annotations__Ú__doc__Ú
__future__r   r‰   r   r   Ú
method_ctxr   r   Ú	_registryÚ_ATTACH_BYTES_MAX_BYTESÚ_PDF_ATTACH_MAX_BYTESÚ_PDF_ATTACH_MAX_PAGESr   Ú__annotations__Úcompiler©   r"   r)   r5   rM   rY   rf   rc   rd   r}   r˜   r¢   r¬   rµ   r»   rØ   rß   )rà   s   @r   Ú<module>rê      s¯   øðôõ #ã 	Û ç 4áÓ€	ð +Ð Ø(Ð ØÐ ð/*€Ð+ó *ð
 $'§;¢;Ð/GÓ#HÐ  ØõJõõ÷TõMõIõ õ$õ"0õõ(õõ%õ8õB"÷J0r   