+
    lHàjõŒ  ã                  ó  € R t ^ RIHt ^ RIt^ RIt^ RIt^ RIt^ RIt^ RIt^ RI	t	^ RI
t
^ RIt^ RIt^ RIHt ^ RIHtHtHtHt ^ RIHtHtHt ^ RIHt ]P4                  ! ]4      tRtRtR	tR
t Rt!Rt"^xt#Rt$Rt%^t&^´t']#]%]&,          ,           ]',           t(](^<,           t)])](,           ^<,           t*Rbt+Rt, ! R R]-4      t.]P^                  ! R4      t0R R lt1R R lt2RR/R R llt3R R lt4R R lt5R R lt6R  R! lt7R" R# lt8R$ R% lt9R& R' lt:R( R) lt;]<! 4       3R* R+ llt=]P^                  ! R,]P|                  4      t?]<! 4       3R- R. llt@R/ R0 ltAR1 R2 ltBR3 R4 ltCR5 R6 ltDR7 R8 ltER9 R: ltFR; R< ltGR= R> ltHR?R@RAR@RBR@/RC RD lltIRE RF ltJRGR/RH RI lltKRcRJ RK lltLRL RM ltMRN RO ltN ! RP RQ4      tORcRR RS lltPRT RU ltQRV RW ltRRcRX RY lltS ! RZ R[]-4      tTR\ R] ltUR^ R_ ltV]P®                  RcR` Ra ll4       tXR# )du  Bot Mode cross-connection relay â€” connections ARE the peer set.

Gateway-side half of the relay letting agents on ANY Desktop-connected gateway
message agents on ANY other. Plain file plumbing under ``<root>/bot_relay/`` â€”
no network; the Desktop owns every socket: ``roster.json`` (union roster of
agents on OTHER connections, pushed via ``bot_relay.roster.sync``), ``outbox/``
(envelopes queued by ``message_agent``, drained via ``bot_relay.outbox.drain``),
``replies/`` (one JSON per envelope via ``bot_relay.reply``; a waiter spawned at
send time watches it so the reply wakes the sender like a local DM).
Public helpers never raise, except ``enqueue_envelope`` â†’ ``EnvelopeRefusedError``
when the target is definitively offline (fail fast instead of queueing a DM nobody will drain).
)ÚannotationsN)ÚPath)ÚAnyÚIteratorÚMappingÚOptional)Ú_default_homeÚ_hermes_rootÚalias_forms©Úatomic_json_writeÚ	bot_relayzroster.jsonÚoutboxÚclaimedÚrepliesÚlocksi„  iX  c                  ó2   a € ] tR t^EtRtR V 3R lltRtV ;t# )ÚEnvelopeRefusedErrora  ``enqueue_envelope`` refused to queue (nothing written); ``reason`` is a stable machine code.

``reason`` is a stable machine code; ``str(exc)`` is the human text. 'runtime_offline' matches the
#93091 item-1 failure-reason enum (plain literal here so the branches merge cleanly).
c               ó    € V ^8„  d   QhRRRR/# )é   ÚreasonÚstrÚmessage© )Úformats   "Ú4/home/ubuntu/.hermes/hermes-agent/tools/bot_relay.pyÚ__annotate__Ú!EnvelopeRefusedError.__annotate__L   s   € ÷ ñ ˜sð ¨Sñ ó    c                	ó2   <€ \         SV `  V4       Wn        R # ©N)ÚsuperÚ__init__r   )Úselfr   r   Ú	__class__s   &&&€r   r"   ÚEnvelopeRefusedError.__init__L   s   ø€ Ü‰Ñ˜Ô!ØŽr   )r   )Ú__name__Ú
__module__Ú__qualname__Ú__firstlineno__Ú__doc__r"   Ú__static_attributes__Ú__classcell__©r$   s   @r   r   r   E   s   ø† ñ÷ö r   r   z ^[a-zA-Z0-9][a-zA-Z0-9_-]{0,63}$c               ó    € V ^8„  d   QhRRRR/# ©r   Úrootú
Path | strÚreturnr   r   )r   s   "r   r   r   V   s   € ÷ 'ñ '�Zð ' Dñ 'r   c                ó.   € \        V 4      \        ,          # r    )r   ÚRELAY_DIR_NAME)r0   s   &r   Ú
relay_rootr5   V   s   € Ü�‹:œÕ&Ð&r   c               ó    € V ^8„  d   QhRRRR/# r/   r   )r   s   "r   r   r   Z   s   € ÷ ñ �zð  dñ r   c                ót   € \        V 4      p\        \        \        3 F  p^ RIHp V! W,          4       K  	  V# )é    )Úmkdir_under_hermes_home)r5   Ú
OUTBOX_DIRÚCLAIMED_DIRÚREPLIES_DIRÚhermes_constantsr9   )r0   ÚbaseÚsubr9   s   &   r   Ú_ensure_dirsr@   Z   s1   € Ü�dÓ€DÜœK¬Ó5ˆÝ<Ù ¥
Ö+ñ 6ð €Kr   Ú	sort_keysFc               ó(   € V ^8„  d   QhRRRRRRRR/# )	r   Útargetr   Úpayloadr   rA   Úboolr2   ÚNoner   )r   s   "r   r   r   b   s/   € ÷ Uñ U˜tð U¨cð UÀð UÐRVñ Ur   c               ó$   € \        WR VRR7       R # )Né€  )ÚindentrA   Úmoder   )rC   rD   rA   s   &&$r   Ú_atomic_write_jsonrK   b   s   € Ü�f¨d¸iÈe×Tr   c               ó$   € V ^8„  d   QhRRRRRR/# )r   Úkeyr   Úloaderr2   r   r   )r   s   "r   r   r   f   s!   € ÷ 
ñ 
�sð 
 sð 
¨sñ 
r   c               óì   €  ^ RI Hp \        W!4      ! 4       ;'       g    / pVP                  R4      ;'       g    / P                  V 4      #   \         d    \
        P                  RT RR7        R# i ; i)z�``bot_mode.<key>`` from config, read lazily (tools/ must not import CLI
config at import time); None when absent or the config is unreadable.NÚbot_modezbot_mode.%s config read failedT©Úexc_info)Úhermes_cli.configÚconfigÚgetattrÚgetÚ	ExceptionÚloggerÚdebug)rM   rN   ÚcfgmodÚcfgs   &$  r   Ú_bot_mode_cfgr\   f   sf   € ðÝ*ä�fÔ%Ó'×-Ð-¨2ˆØ—‘˜
Ó#×)Ð) r×.Ñ.¨sÓ3Ð3øÜô Ü�‰Ð5°sÀTˆÔJÚðús   ‚A ŸA ºA Á#A3Á2A3c               ó    € V ^8„  d   QhRRRR/# )r   Úrowr   r2   úOptional[dict]r   )r   s   "r   r   r   s   s   € ÷ ñ ˜sð  ~ñ r   c                ó,  € \        V \        4      '       g   R# \        V P                  R4      ;'       g    R4      P	                  4       p\        V P                  R4      ;'       g    R4      P	                  4       P                  R4      ;'       g    VR8X  d   RMTp\        V P                  R4      ;'       g    R4      P	                  4       pV'       dG   V'       d?   \        ;QJ d    R	 W!V3 4       F  '       d   K   R
M	  RM! R	 W!V3 4       4      '       g   R# RTRTRTR\        V P                  R4      ;'       g    R4      P	                  4       R,          R\        V P                  R4      ;'       g    R4      P	                  4       R,          RRP                  \        V P                  R4      ;'       g    R4      P                  4       4      R,          /p\        V P                  R4      \        4      '       d   V R,          VR&   V# )uh   Validated, minimal roster row or None. Rows come from the Desktop over
RPC â€” treat as untrusted input.NÚprofileÚ ÚhandleÚ@ÚdefaultÚhermesÚconnection_idc              3  óL   "  € T F  p\         P                  V4      x € K  	  R # 5ir    )Ú
_HANDLE_REÚmatch)Ú.0Úvs   & r   Ú	<genexpr>Ú(_normalize_roster_row.<locals>.<genexpr>{   s    é € Ð2qÑPpÈ1´:×3CÑ3CÀA×3FÐ3FÓPpùs   ‚"$FTÚconnection_label:NéP   NÚtitle:Néx   NÚdescriptionÚ :Né    NÚonline)
Ú
isinstanceÚdictr   rV   ÚstripÚlstripÚallÚjoinÚsplitrE   )r^   ra   rc   rg   Úouts   &    r   Ú_normalize_roster_rowr   s   s—  € ô �cœ4× Ò ÙÜ�#—'‘'˜)Ó$×*Ð*¨Ó+×1Ñ1Ó3€GÜ�—‘˜Ó"×(Ð( bÓ)×/Ñ/Ó1×8Ñ8¸Ó=×pÐpÈgÐYbÔNbÁ(Ðho€FÜ˜Ÿ™ Ó0×6Ð6°BÓ7×=Ñ=Ó?€MßŸ-¯s«sÑ2qÐQWÐboÑPpÓ2q¯s¯sªsÑ2qÐQWÐboÑPpÓ2q×/qÒ/qÙà�7˜H f¨o¸}ØœC §¡Ð(:Ó ;× AÐ A¸rÓB×HÑHÓJÈ3ÕOØ”�S—W‘W˜WÓ%×+Ð+¨Ó,×2Ñ2Ó4°TÕ:Ø�s—x‘x¤ C§G¡G¨MÓ$:×$@Ð$@¸bÓ A× GÑ GÓ IÓJÈ4ÕPð	€Cô �#—'‘'˜(Ó#¤T×*Ò*Ø˜H�ˆˆH‰Ø€Jr   c               ó$   € V ^8„  d   QhRRRRRR/# )r   r0   r1   Úrowsr   r2   Úintr   )r   s   "r   r   r   ‰   s!   € ÷ ñ ˜jð °ð ¸ñ r   c           
     ó¤  € \        V 4      p/ p\        R\        \        \	        V\
        4      '       d   TM. 4      4       F%  pVP                  VR,          VR,          3V4       K'  	  \        V4       Uu. uF  qSV,          NK  	  pp\        V\        ,          R\        \        P                  ! 4       4      RV/RR7       \        V4      # u upi )zCAtomically persist the Desktop-pushed remote roster. Returns count.Nrg   ra   Ú
updated_atÚagentsT)rA   )r@   ÚfilterÚmapr   rw   ÚlistÚ
setdefaultÚsortedrK   ÚROSTER_FILEr‚   ÚtimeÚlen)r0   r�   r>   Úby_keyÚnormÚkÚcleaneds   &&     r   Úwrite_remote_rosterr’   ‰   s«   € ä˜Ó€DØ*,€FÜ�tœSÔ!6Ä
È4ÔQU×@VÒ@V¹Ð\^Ó_Ö`ˆØ×Ñ˜4 Õ0°$°yµ/ÐBÀDÖIñ aä"(¨¤.Ó1¡.˜Q�a�yˆy¡.€GÐ1Ü�tœkÕ)¨L¼#¼d¿iºi»kÓ:JÈHÐV]Ð+^ÐjnÕoÜˆw‹<Ðùò 2s   Á6Cc               ó    € V ^8„  d   QhRRRR/# ©r   r0   r1   r2   ú
list[dict]r   )r   s   "r   r   r   ”   s   € ÷ ñ ˜Zð ¨Jñ r   c                óÊ  €  \        V 4      \        ,          P                  RR7      p\        P                  ! V4      p\        V\        4      '       d   VP                  R4      MRp\        V\        4      '       d*   \        \        V4       Uu. uF  qD'       g   K  VNK  	  up# . # u upi   \         d    . u # \         d    \        P                  RRR7       . u # i ; i)z9The current remote roster (possibly empty). Never raises.ú	utf-8-sig©Úencodingr…   Nzbot_relay roster read failedTrQ   )r5   r‹   Ú	read_textÚjsonÚloadsrw   rx   rV   rˆ   r‡   r   ÚFileNotFoundErrorrW   rX   rY   )r0   ÚrawÚdatar…   Úrs   &    r   Úread_remote_rosterr¡   ”   s¹   € ð	Ü˜$Ó¤+Õ-×8Ñ8À+Ð8ÓNˆÜ�zŠz˜#‹ˆÜ'1°$¼×'=Ò'=�—‘˜(Ô#À4ˆÜGQÐRXÔZ^×G_ÒG_œ3Ô4°fÔ=ÓCÑ=�aÃ—�Ñ=ÑCÐgÐegÐgùÒCøÜô ØŠ	Üô Ü�‰Ð3¸dˆÔCØŠ	ðús<   ‚BB- Â	B(ÂB(Â#B- Â&B- Â(B- Â-C"Â<C"ÃC"Ã!C"c               ó    € V ^8„  d   QhRRRR/# ©r   r^   rx   r2   zset[str]r   )r   s   "r   r   r   ¢   s   € ÷ ;ñ ;�Tð ;˜hñ ;r   c                ó^   € V R,          P                  4       V R,          P                  4       0# )zKLower-cased routing ids of a roster row: its @handle and profile folder id.rc   ra   ©Úlower©r^   s   &r   Ú_target_idsr¨   ¢   s'   € à��M×ÑÓ! 3 y¥>×#7Ñ#7Ó#9Ð:Ð:r   c               ó    € V ^8„  d   QhRRRR/# r£   r   )r   s   "r   r   r   §   s   € ÷ Bñ B˜ð B (ñ Br   c                ój   € \        V 4      \        V P                  R4      ;'       g    R4      ,          # )u%  Every lower-cased bare form that addresses ``row``: routing ids plus the Bot Mode title's
mention slugs (``"CoS Bot"`` â†’ ``cos-bot``/``cosbot``, what the Desktop picker inserts). A remote
``default`` is ``@hermes`` on every gateway, so its title is the only bare form that can single it out.rq   rb   )r¨   r
   rV   r§   s   &r   Ú_target_aliasesr«   §   s*   € ô �sÓœk¨#¯'©'°'Ó*:×*@Ð*@¸bÓAÕAÐAr   c               ó$   € V ^8„  d   QhRRRRRR/# )r   Ú
raw_targetr   Úrosterr•   r2   r   r   )r   s   "r   r   r   ®   s!   € ÷ <ñ < cð <°:ð <À#ñ <r   c                óx  € R \        T ;'       g    R4      P                  4       P                  R4      P                  R4       4       w  r#pV'       d   V'       d   V'       g   R# VP	                  4       pV Uu. uF8  qT'       d-   VR,          P	                  4       VP	                  4       8X  g   K6  VNK:  	  ppV Uu. uF  qR\        V4      9   g   K  VNK  	  up;'       g$    V Uu. uF  qR\        V4      9   g   K  VNK  	  uppV'       g   R# \        V4      ^8X  d
   V^ ,          # R# u upi u upi u upi )a:  Matched row for a bare handle/profile/title slug (unique across connections) or
``<handle|profile|title-slug>@<connection-id>``; ``"ambiguous"`` for a bare form on several
connections; None otherwise. An exact handle/profile match beats a title slug, so a title
colliding with another row's handle never steals it.c              3  ó@   "  € T F  qP                  4       x € K  	  R # 5ir    )ry   )rk   Úps   & r   rm   Ú(resolve_remote_target.<locals>.<genexpr>³   s   é € ÐbÑ)a A—g‘g—i�iÓ)aùs   ‚rb   rd   Nrg   Ú	ambiguous)r   ry   rz   Ú	partitionr¦   r¨   r«   r�   )r­   r®   ÚwantÚatÚconnr^   r�   Úmatchess   &&      r   Úresolve_remote_targetr¹   ®   s   € ñ
 c¬¨Z×-=Ð-=¸2Ó)>×)DÑ)DÓ)F×)MÑ)MÈcÓ)R×)\Ñ)\Ð]`Ô)aÓb�N€Dˆdß—BŸtÙØ�:‰:‹<€DÙ!Ó^™6�C®°°_Õ1E×1KÑ1KÓ1MÐQU×Q[ÑQ[ÓQ]Ñ1]�CˆC™6€DÐ^Ù"Ó?™d�s¬k¸#Ó.>Ñ&>�sˆs™dÑ?×xÐxÑSWÓCxÑSWÈCÔcrÐsvÓcwÑ[wÇCÀCÑSWÑCx€GßÙÜ˜W›¨Ô*ˆ7�1�:Ð;°Ð;ùò	 _ùÚ?ùÒCxs$   Á:3D-Â2D-Â>D2ÃD2Ã)D7Ã?D7c               ó    € V ^8„  d   QhRRRR/# ©r   r^   rx   r2   r   r   )r   s   "r   r   r   ¾   s   € ÷ 6ñ 6�Tð 6˜cñ 6r   c                óø   € \        V P                  R4      ;'       g    R4      p\        P                  ! RRVP	                  4       P                  4       4      P	                  R4      pV\        V4      9   d   V# R# )u¬   The Bot Mode title's slug form (``"CoS Bot"`` â†’ ``cos-bot``, what the picker inserts); "" when the
title is empty, reserved (a bot titled "Hermes") or not a valid handle.rq   rb   z[^a-z0-9_-]+Ú-)r   rV   Úrer?   ry   r¦   r
   )r^   rq   Úslugs   &  r   Ú_title_slugrÀ   ¾   sb   € ô �—‘˜Ó ×&Ð& BÓ'€EÜ�6Š6�/ 3¨¯©«×(;Ñ(;Ó(=Ó>×DÑDÀSÓI€DØœ; uÓ-Ô-ˆ4Ð5°2Ð5r   c               ó$   € V ^8„  d   QhRRRRRR/# )r   r®   r•   Úlocal_takenúset[str] | frozenset[str]r2   z	list[str]r   )r   s   "r   r   r   Æ   s#   € ÷ *ñ * 
ð *Ð9Rð *Ðenñ *r   c                óp  aaa€ V Uu0 uF  q"P                  4       kK  	  upo/ o/ oV  F_  p\        V4       F  pSP                  V^ 4      ^,           SV&   K!  	  \        V4       F  pSP                  V^ 4      ^,           SV&   K!  	  Ka  	  R VVV3R llpV  Uu. uF
  q4! V4      NK  	  up# u upi u upi )a¢  One unambiguous target string per row, shortest first: the bare handle when no other remote
row and no LOCAL profile (``local_taken``: this gateway's handles and friendly-name slugs) answers
to it; else the title slug under the same test (a remote ``default`` titled "CoS Bot" is
``@cos-bot``, since bare ``@hermes`` is always this gateway's own default); else
``handle@connection``. Mirrors ``resolve_remote_target``.c               ó    € V ^8„  d   QhRRRR/# r»   r   )r   s   "r   r   Ú)remote_target_forms.<locals>.__annotate__Õ   s   € ÷ 9ñ 9�4ð 9˜Cñ 9r   c                ó  <€ V R ,          S3\        V 4      S33 FO  w  rV'       g   K  VP                  4       S9  g   K&  VP                  VP                  4       ^ 4      ^8X  g   KM  Vu # 	  V R ,           RV R,           2# )rc   rd   rg   )rÀ   r¦   rV   )r^   Ú	candidateÚclaimsÚalias_claimsÚ	id_claimsÚtakens   &  €€€r   Ú_formÚ"remote_target_forms.<locals>._formÕ   s}   ø€ ð $' x¥=°)Ð"<¼{È3Ó?OÐQ]Ð>^Ó!_ÑˆIß‰y˜YŸ_™_Ó.°eÖ;ÀÇ
Á
È9Ï?É?ÓK\Ð^_Ó@`ÐdeÖ@eØ Ò ñ "`ð �h•-�  # oÕ"6Ð!7Ð8Ð8r   )r¦   r¨   rV   r«   )r®   rÂ   Úformr^   rÍ   rÊ   rË   rÌ   s   &&   @@@r   Úremote_target_formsrÐ   Æ   s³   ú€ ñ '2Ó2¡k˜d�Z‰ZŽ\¡kÑ2€EØ "€IØ#%€LÛˆÜ Ö$ˆDØ'Ÿm™m¨D°!Ó4°qÕ8ˆI�d‹Oñ %ä# CÖ(ˆDØ!-×!1Ñ!1°$¸Ó!:¸QÕ!>ˆL˜Óó )ñ ÷9ò 9ñ #)Ó)¡&˜3ˆE�#ŽJ¡&Ñ)Ð)ùò# 3ùò" *s   ˆB.ÂB3u2   ^(Message from ðŸ¤– .+? \(@)([A-Za-z0-9_-]+)(\): )c               ó0   € V ^8„  d   QhRRRRRRRRRR	R
R/# )r   r   r   Úfrom_handler   Úfrom_connectionr®   r•   rÂ   rÃ   r2   r   )r   s   "r   r   r   ã   sA   € ÷ Lñ L #ð L°Cð LÈ#ð LÐWað LØ&?ðLØRUñLr   c           	     óÀ  € \        T;'       g    R4      P                  4       P                  R4      \        T;'       g    R4      P                  4       re\        P	                  \        T ;'       g    R4      4      pV'       d?   V'       d7   \
        P	                  V4      '       d   \
        P	                  V4      '       g   V # \        \        R V 4       \        W44      4      4      pVP                  VP                  4       VP                  4       34      ;'       g    V RV 2p	VP                  ^4       V	 VP                  ^4       WP                  4       R  2# )uc  Rewrite a relayed DM's ``Message from ðŸ¤– <name> (@<handle>):`` stamp so the handle is the
form THIS gateway can reply to: the sender's row in the local relay roster as
``remote_target_forms`` renders it, else ``handle@connection``. A relayed ``@hermes`` is another
machine's default â€” left bare, a reply lands on the recipient's own default (#103731).rb   rd   c              3  ó|   "  € T F2  qR ,          P                  4       VR,          P                  4       3x € K4  	  R# 5i)rg   rc   Nr¥   )rk   r    s   & r   rm   Ú'qualify_sender_stamp.<locals>.<genexpr>í   s0   é € ÐXÑQWÈA˜Õ)×/Ñ/Ó1°1°Xµ;×3DÑ3DÓ3FÕGÓQWùs   ‚:<N)r   ry   rz   Ú_SENDER_STAMP_RErj   ri   rx   ÚziprÐ   rV   r¦   ÚgroupÚend)
r   rÒ   rÓ   r®   rÂ   rc   r·   rj   ÚformsrÏ   s
   &&&&&     r   Úqualify_sender_stamprÜ   ã   s  € ô �{×(Ð( bÓ)×/Ñ/Ó1×8Ñ8¸Ó=¼sÀ?×CXÐCXÐVXÓ?Y×?_Ñ?_Ó?aˆDÜ×"Ñ"¤3 w§} }°"Ó#5Ó6€EßŸ¤J×$4Ñ$4°V×$<Ò$<ÄJ×DTÑDTÐUY×DZÒDZØˆÜ”ÑXÑQWÓXÔZmÐntó  [Có  Dó  E€EØ�9‰9�d—j‘j“l F§L¡L£NÐ3Ó4×JÐJ¸6¸(À!ÀDÀ6Ð8J€DØ�k‰k˜!‹nÐ˜d˜V E§K¡K°£NÐ#3°G¿I¹I»K¸LÐ4IÐ3JÐKÐKr   c               ó   € V ^8„  d   QhRR/# )r   r2   r‚   r   )r   s   "r   r   r   ò   s   € ÷ 
,ñ 
,˜sñ 
,r   c                 ó¸   € \        RRR7      p V f   \        #  \        V 4      #   \        \        \
        3 d     \        P                  RT 4       \        u # i ; i)znConfigured drain TTL (``bot_mode.envelope_ttl_seconds``), read per-drain.
``0`` (or negative) disables expiry.Úenvelope_ttl_secondsÚload_config_readonly©rN   z8Invalid bot_mode.envelope_ttl_seconds %r; using fallback)r\   ÚDEFAULT_ENVELOPE_TTL_SECONDSr‚   Ú	TypeErrorÚ
ValueErrorÚOverflowErrorrX   rY   ©Úvals    r   Ú_envelope_ttl_secondsrè   ò   sW   € ô Ð.Ð7MÔ
N€CØ
‚{Ü+Ð+ð,Ü�3‹xˆøÜ”z¤=Ð1ô ,Ü�‰ÐOÐQTÔUÜ+Ò+ð,ús   ™
$ ¤2AÁAc               ó$   € V ^8„  d   QhRRRRRR/# )r   r0   r1   rC   rx   r2   zOptional[bool]r   )r   s   "r   r   r   ÿ   s!   € ÷ ñ ˜:ð ¨tð ¸ñ r   c                óz  a€   \         P                   ! 4       \        V 4      \        ,          P                  4       P                  ,
          pT\        8:  d   \        T 4      M. pT'       g   R# \        TP                  R4      ;'       g    R4      \        TP                  R4      ;'       g    R4      3o\        T3R lT 4       R4      pTf   R# \        TP                  R4      \        4      '       d
   TR,          # R#   \
         d     R# i ; i  \         d    \        P                  RR	R
7        R# i ; i)uâ   Tri-state liveness: True / False / None (unknown â†’ callers fail open). Offline =
explicit ``online: false`` or ABSENT from a *fresh* roster; a missing, unreadable,
empty or stale roster proves nothing â†’ None. Never raises.Nrg   rb   ra   c              3  óX   <"  € T F  qR ,          VR,          3S8X  g   K  Vx € K!  	  R# 5i)rg   ra   Nr   )rk   r    rM   s   & €r   rm   Ú#_target_liveness.<locals>.<genexpr>  s&   øé € ÐS™v˜!¨OÕ*<¸aÀ	½lÐ)KÈsÑ)R—A’A›vùs   ƒ* 
*Frv   zbot_relay liveness check failedTrQ   )rŒ   r5   r‹   ÚstatÚst_mtimeÚOSErrorÚROSTER_FRESH_SECONDSr¡   r   rV   Únextrw   rE   rW   rX   rY   )r0   rC   Úager®   r^   rM   s   &&   @r   Ú_target_livenessró   ÿ   s  ø€ ðð	Ü—)’)“+¤¨DÓ!1´KÕ!?× EÑ EÓ G× PÑ PÕPˆCð .1Ô4HÔ-HÔ# DÔ)ÈbˆßÙÜ�6—:‘:˜oÓ.×4Ð4°"Ó5´s¸6¿:¹:ÀiÓ;P×;VÐ;VÐTVÓ7WÐXˆÜÔS™vÓSÐUYÓZˆØŠ;ÙÜ *¨3¯7©7°8Ó+<¼d× CÒ Cˆs�8�}ÐMÈÐMøô ô 	Úð	ûô ô Ü�‰Ð6ÀˆÔFÚðúsN   „AD ÁD Á.D Â"D Â."D Ã-D Ä D ÄDÄD ÄDÄD Ä"D:Ä9D:c               ó0   € V ^8„  d   QhRRRRRRRRRRR	R/# )
r   r0   r1   rC   rx   r   r   Úsender_profileÚsender_handler2   r   )r   s   "r   r   r     s9   € ÷ ñ ˜:ð °$ð Àð ÐVYð Ðjmð Ðrvñ r   c               óD  € \        W4      RJ d|   RVP                  R4      ;'       g    VP                  R4      ;'       g    R RVP                  R4      ;'       g    VP                  R4      ;'       g    R 2p\        R	V R
24      h\        V 4      pR\        P
                  ! 4       P                  R\        \        P                  ! 4       4      RRRVRVRVR,          RVR,          RVR,          RV/	p\        V\        ,          VR,           R2,          V4       V# )zÜQueue a cross-connection DM for the Desktop relay; returns the envelope. Raises
``EnvelopeRefusedError`` ('runtime_offline') without writing when the target is
definitively offline; unknown liveness enqueues (fail-open).Frd   rc   ra   Ú?ú on ro   rg   Úruntime_offlineul    is offline right now â€” the message was NOT queued. Try again once that machine reconnects to the Desktop.ÚidÚ
created_atÚcanonical_delivery_v1TÚfrom_profilerÒ   Útarget_connectionÚtarget_profileÚtarget_handler   ú.json)ró   rV   r   r@   ÚuuidÚuuid4Úhexr‚   rŒ   rK   r:   )r0   rC   r   rõ   rö   Úlabelr>   Úenvelopes   &$$$$   r   Úenqueue_enveloper    s  € ô ˜Ó%¨Ó.Ø�V—Z‘Z Ó)×IÐI¨V¯Z©Z¸	Ó-B×IÐIÀcÐJÈ$Ø—J‘JÐ1Ó2×XÐX°f·j±jÀÓ6Q×XÐXÐUXÐYð[ˆä"Ð#4¸¸ð @\ð 7\ó ]ð 	]ä˜Ó€DàŒd�jŠj‹l×Ñ ¬c´$·)²)³+Ó.>Ð@WÐY]Ø˜¨°}Ø˜V OÕ4Ð6FÈÈyÕHYØ˜ Õ)¨9°gð	€Hô �tœjÕ(¨h°t­nÐ-=¸UÐ+CÕCÀXÔNØ€Or   c          
     ó,   € V ^8„  d   QhRRRRRRRRRR	/# )
r   r0   r1   Úpathr   ÚttlÚfloatÚnowr2   rE   r   )r   s   "r   r   r   )  s/   € ÷ ñ ˜:ð ¨Tð ¸ð ÀEð Èdñ r   c                óê  €  \         P                  ! VP                  RR7      4      p\        V\        4      '       g"   \        R\        V4      P                   24      h\        VP                  R4      ;'       g    VP                  4       P                  4      pY5,
          T8:  d   R# \        P                  ! \        \
        4      ;_uu_ 4        \        T \!        TP                  R4      ;'       g    R4      RR	TP                  R
4      ;'       g    R RTP                  R4      ;'       g    R RT R2R7       RRR4       R#   \        \
        3 d     R# i ; i  + '       g   i     R# ; i)z²True when the outbox envelope is older than ``ttl``; writes the 'queued_expired'
reply so the sender's waiter resolves (best effort). Unreadable envelopes are left for the claim.r—   r˜   úexpected a JSON object, got rü   Frû   rb   Úqueued_expiredzqueued message to @r  rø   rù   rÿ   z expired after uc   s waiting for the Desktop to drain it â€” it was NOT delivered. Resend once the Desktop reconnects.©r   ÚerrorNT)r›   rœ   rš   rw   rx   rä   Útyper&   r  rV   rí   rî   rï   Ú
contextlibÚsuppressÚwrite_replyr   )r0   r
  r  r  ÚenvÚcreateds   &&&&  r   Ú_expire_if_staler  )  s1  € ðÜ�jŠj˜Ÿ™°˜Ó=Ó>ˆÜ˜#œt×$Ò$ÜÐ;¼DÀ»I×<NÑ<NÐ;OÐPÓQÐQÜ˜Ÿ™ Ó-×EÐE°·±³×1EÑ1EÓFˆð …}˜ÔÙÜ	×	Ò	œW¤j×	1Õ	1Ü�Dœ#˜cŸg™g d›m×1Ð1¨rÓ2Ð;KØ! #§'¡'¨/Ó":×"AÐ"A¸cÐ!BÀ$ÀsÇwÁwÐObÓGc×GjÐGjÐgjÐFkð lØ ˜Eð "2ð2õ	4÷ 
2ñ
 øô ”ZÐ ô Úðú÷ 
2Ö	1ñ
 ús1   ‚A9E	 Á<E	 ÃAE!ÄE!Ä1E!Å	EÅEÅ!E2	c               ó    € V ^8„  d   QhRRRR/# )r   r
  r   r2   ztuple[float, str]r   )r   s   "r   r   r   =  s   € ÷ ñ �Tð Ð/ñ r   c                óê   € \         P                  ! \        4      ;_uu_ 4        V P                  4       P                  V P
                  3uuRRR4       #   + '       g   i     M; iRT P
                  3# )zñClaim order for one outbox entry: oldest first. ``mtime`` is what ``_sweep_stale`` already
treats as an envelope's age, and unlike the whole-second ``created_at`` field it separates two
DMs sent in the same second. The name only breaks ties.Nç        )r  r  rï   rí   rî   Úname)r
  s   &r   Ú
_queued_atr  =  sK   € ô 
×	Ò	œW×	%Õ	%Ø—	‘	“×$Ñ$ d§i¡iÐ0÷ 
&×	%×	%Ò	%úà�—‘ÐÐs   £&AÁA$	c               ó    € V ^8„  d   QhRRRR/# r”   r   )r   s   "r   r   r   F  s   € ÷ !Tñ !T *ð !T°ñ !Tr   c           	     ó\  € \        V 4      p\        V4       \        4       p\        P                  ! 4       p. p\	        V\
        ,          P                  R4      \        R7       EF8  pV^ 8”  dP   \        WW#4      '       d>   \        P                  ! \        4      ;_uu_ 4        VP                  4        RRR4       KZ  V\        ,          VP                  ,          p\        V4      p\        P                  ! \        \        4      ;_uu_ 4        \         P"                  ! WV4       \$        P&                  ! VP)                  RR7      4      p\+        V\,        4      '       g"   \        R\/        V4      P0                   24      hVP3                  Wx34       RRR4       EK;  	  V U	U
u0 uF  w  ršV
R,          kK  	  pp	p
VP5                  \7        WW³4      4       \	        VR R7       U	Uu. uF  w  r˜VNK	  	  upp	#   + '       g   i     ELO; i  + '       g   i     EK¼  ; iu up
p	i u upp	i )	un  Drain the outbox (rename â†’ claimed/ so a second drain can't double-deliver).
TTL-expired envelopes get a 'queued_expired' reply and are removed instead.

Envelopes older than ``bot_mode.envelope_ttl_seconds`` are NOT delivered: each gets an error reply
(reason ``'queued_expired'``) so the sender's waiter resolves, and its outbox file is removed (#93091
item 2).
ú*.json©rM   Nr—   r˜   r  rû   c                ó   € V ^ ,          # )r8   r   )Úitems   &r   Ú<lambda>Ú)claim_pending_envelopes.<locals>.<lambda>g  s   € È$ÈqÎ'r   )r@   Ú_sweep_stalerè   rŒ   rŠ   r:   Úglobr  r  r  r  rï   Úunlinkr;   r  rä   ÚosÚreplacer›   rœ   rš   rw   rx   r  r&   ÚappendÚextendÚ_replay_unanswered)r0   r>   r  r  r~   r
  r   Úorderr  Ú_orderr^   Úseens   &           r   Úclaim_pending_envelopesr2  F  s�  € ô ˜Ó€DÜ�ÔÜ
Ó
!€CÜ
�)Š)‹+€CØ02€Cô ˜œzÕ)×/Ñ/°Ó9¼z×JÐJˆØ�Œ7Ô'¨°C×=Ò=Ü×$Ò$¤W×-Õ-Ø—‘”÷ .áØœÕ$ t§y¡yÕ0ˆÜ˜4Ó ˆÜ× Ò ¤¬*×5Õ5Ü�JŠJ�tÔ%Ü—z’z '×"3Ñ"3¸[Ð"3Ó"IÓJˆHÜ˜h¬×-Ò-Ü Ð#?ÄÀXÃ×@WÑ@WÐ?XÐ!YÓZÐZØ�J‰J˜Ð(Ô)÷ 6Ò5ñ Kñ *-Ô-©™+˜&ˆC��IˆI©€DÑ-ð ‡J�JÔ! $¨dÓ8Ô9Ü-3°CÑ=QÕ-RÔSÑ-RÑ)˜‹HÑ-RÒSÐS÷ .×-Ð-ú÷
 6×5Ñ5üó .ùó Ts%   Â"G9ÄBHÆ%H"Ç(H(Ç9H
ÈHc          
     ó,   € V ^8„  d   QhRRRRRRRRR	R
/# )r   r0   r1   r>   r   r1  Úsetr  r  r2   z$list[tuple[tuple[float, str], dict]]r   )r   s   "r   r   r   j  s0   € ÷ #ñ #˜Zð #¨tð #¸3ð #ÀUð #ÐOsñ #r   c                ó”  € . p\        V\        ,          P                  R4      \        R7       EF   pV\        ,          VP
                  ,          P                  4       '       d   K8  \        P                  ! \        \        4      ;_uu_ 4        \        P                  ! VP                  RR7      4      p\        V\        4      '       g"   \        R\!        V4      P"                   24      hVP%                  R4      V9   d    RRR4       KÞ  \'        VP%                  R4      ;'       g    R4      pR	VP%                  R
4      ;'       g    R RVP%                  R4      ;'       g    R 2pVP%                  R4      RJd   \)        WWxV4        RRR4       EKj  \+        VP%                  R4      ;'       g    VP-                  4       P.                  4      p	W9,
          \0        8”  d&   \3        WRRV R\0         R2R7        RRR4       EKÜ  VP5                  \        V4      V34       RRR4       EK  	  V#   + '       g   i     EK  ; i)u€  ``claimed/`` canonical envelopes whose reply has not landed yet, replayed on EVERY drain.

A drained envelope is not "delivered": its stable id is handed out again until the target's
terminal reply is written under that id, so a Desktop that disconnects between ``outbox.drain``
and ``bot_relay.deliver`` (#111021, #111207) or loses the drain result cannot drop the DM. The
replay is safe because ``bot_relay.deliver`` forwards the SAME id to the target profile's
authority, which admits one turn per id and answers a retry with the existing record â€” never a
second turn. Once ``created_at + REPLY_WAIT_SECONDS`` passes with no reply the waiter is gone
(or about to be): a ``delivery_timeout`` reply is written so it learns, and the envelope is
never handed out again. Each envelope comes back with its ``_queued_at`` enqueue key so the
drain can merge it with fresh outbox rows in send order.
r!  r"  r—   r˜   r  rû   Nrb   rd   r  rø   rù   rÿ   rý   Trü   Údelivery_timeoutzno reply from z within u   s of sending â€” the Desktop picked the message up but never reported a delivery. It will not be retried; resend if it matters.r  )rŠ   r;   r(  r  r<   r  Úexistsr  r  rï   rä   r›   rœ   rš   rw   rx   r  r&   rV   r   Ú_settle_pre_upgrade_claimr  rí   rî   ÚREPLY_WAIT_SECONDSr  r,  )
r0   r>   r1  r  r~   r
  r  Úenv_idr  r  s
   &&&&      r   r.  r.  j  sÝ  € ð 13€CÜ˜œ{Õ*×0Ñ0°Ó:Ä
×KÐKˆØ”;Õ §¡Õ*×2Ñ2×4Ò4ÙÜ× Ò ¤¬*×5Õ5Ü—z’z $§.¡.¸+ .Ó"FÓGˆHÜ˜h¬×-Ò-Ü Ð#?ÄÀXÃ×@WÑ@WÐ?XÐ!YÓZÐZØ�|‰|˜DÓ! TÔ)Ø÷ 6Ñ5ô ˜Ÿ™ dÓ+×1Ð1¨rÓ2ˆFØ˜Ÿ™ _Ó5×<Ð<¸Ð=¸TÀ(Ç,Á,ÐObÓBc×BjÐBjÐgjÐAkÐlˆEØ�|‰|Ð3Ó4¸DÓ@Ü)¨$°fÀSÔIØ÷ 6Ò5ô ˜HŸL™L¨Ó6×NÐN¸$¿)¹)»+×:NÑ:NÓOˆGØ�}Ô1Ô1Ü˜DÐ1CØ$ U G¨8Ô4FÐ3Gð Hrð rõtð ÷! 6Ò5ð" �J‰Jœ
 4Ó(¨(Ð3Ô4÷# 6Ò5ñ Lð* €J÷% 6×5Ñ5ús8   ÂA5H5ÄH5Ä)H5Å	H5Å%(H5ÆH5Æ5A
H5È
H5È5Ic               ó0   € V ^8„  d   QhRRRRRRRRRR	R
R/# )r   r0   r1   r
  r   r:  r   r  r  r  r2   rF   r   )r   s   "r   r   r   �  s9   € ÷ ,ñ , Jð ,°dð ,ÀCð ,ÐPSð ,ÐZ_ð ,Ðdhñ ,r   c                óŒ   € ^ RI Hp WAP                  4       P                  ,
          \        8  d   R# \        WVRV R2R7       R# )uO  A ``claimed/`` envelope from before canonical delivery (no ``canonical_delivery_v1``) is never
replayed: its old lane ran a CLI turn the authority has no record of, so re-admitting the id could
run the DM twice. Left alone, though, its sender's waiter watched a reply that never came. Once the
old drain's own re-offer point passes (``REOFFER_AFTER_SECONDS`` after its claim â€” the claim mtime
â€” by which time a live delivery has posted the Desktop's timeout reply) the outcome is settled as
``unknown``: the waiter resolves, the envelope is never offered again, and nothing auto-resends.)ÚUNKNOWNNzthe message to uÁ    was picked up before this gateway was upgraded and its delivery was never reported â€” it may or may not have been delivered. It will not be retried; check with the recipient before resending.r  )Útools.bot_failure_reasonsr=  rí   rî   ÚREOFFER_AFTER_SECONDSr  )r0   r
  r:  r  r  r=  s   &&&&& r   r8  r8  �  s?   € õ 2à
�Y‰Y‹[×!Ñ!Õ!Ô$9Ô9ÙÜ� WØ
˜%˜ð !*ð 	*÷,r   Úreplyrb   r  r   c               ó0   € V ^8„  d   QhRRRRRRRRRRRR	/# )
r   r0   r1   Úenvelope_idr   r@  r  r   r2   r   r   )r   s   "r   r   r   ¡  s8   € ÷ ñ �jð ¨sð ¸cð Èsð Ðadð Ðnrñ r   c               ó0  € \        V 4      p\        T;'       g    R4      P                  4       p\        P                  ! RV4      '       g   \        RV: 24      hV\        ,          V R2,          pVP                  4       '       d   V# \        T;'       g    R4      \        T;'       g    R4      r˜V	'       g   V'       d   ^ RIH	p
 V
! V4      p	\        TRTR\        \        P                  ! 4       4      R\        T;'       g    R4      R	VR
V	/4       V# )uË  Persist the relayed reply (or delivery error) for the waiter. ``reason`` (typed
code, ``tools.bot_failure_reasons``) is classified from ``error`` when omitted.

Idempotent by envelope id â€” the first settled reply stands, error or not. That is safe because
a replayed envelope (``_replay_unanswered``) carries the SAME id into the target authority,
which admits one turn per id, so a later write is a duplicate or a bookkeeping timeout, never
a truer answer.rb   z^[0-9a-f]{32}$zinvalid envelope id: r  )Úclassify_agent_errorrû   r¶   r@  r  r   )r@   r   ry   r¾   rj   rä   r<   r7  r>  rD  rK   r‚   rŒ   )r0   rB  r@  r  r   r>   Úsafer
  ÚerrÚcoderD  s   &&$$$      r   r  r  ¡  sã   € ô ˜Ó€DÜˆ{× Ð ˜bÓ!×'Ñ'Ó)€DÜ�8Š8Ð% t×,Ò,ÜÐ0°±Ð@ÓAÐAØ”+Õ 4 &¨ Õ.€DØ‡{�{‡}‚}ð ˆÜ�E—K�K˜RÓ ¤# f§l l°Ó"3ˆß—CÝBá# CÓ(ˆÜ�t˜d D¨$´´D·I²I³KÓ0@À'Ì3ÈuÏ{È{ÐXZÓK[Ð]dÐfiÐksÐuyÐzÔ{Ø€Kr   c               ó(   € V ^8„  d   QhRRRRRRRR/# )	r   Ú	directoryr   Úpatternr   Úcutoffr  r2   r‚   r   )r   s   "r   r   r   »  s(   € ÷ 	ñ 	 tð 	°cð 	À5ð 	ÈSñ 	r   c           
     óÒ  € ^ p\         P                  ! \        4      ;_uu_ 4        V P                  V4       F|  p\         P                  ! \        4      ;_uu_ 4        VP	                  4       '       d9   VP                  4       P                  V8  d   VP                  4        V^,          pRRR4       K~  	  RRR4       V#   + '       g   i     K›  ; i  + '       g   i     T# ; i)zdUnlink regular files matching ``pattern`` with mtime before ``cutoff``; returns count. Never raises.N)r  r  rï   r(  Úis_filerí   rî   r)  )rI  rJ  rK  Úremovedr
  s   &&&  r   Úunlink_files_older_thanrO  »  sœ   € à€GÜ	×	Ò	œW×	%Õ	%Ø—N‘N 7Ö+ˆDÜ×$Ò$¤W×-Õ-Ø—<‘<—>’> d§i¡i£k×&:Ñ&:¸VÔ&CØ—K‘K”MØ˜q•L�G÷ .Ñ-ñ ,÷ 
&ð €N÷	 .×-Ð-ú÷ 
&Ö	%ð €Nús$   ¥7CÁACÂ+CÃCÃ	CÃC&	r  c               ó$   € V ^8„  d   QhRRRRRR/# )r   r>   r   r  úfloat | Noner2   r‚   r   )r   s   "r   r   r   Ç  s&   € ÷ xñ x�tð x \ð x¸Sñ xr   c               óœ   a a€ Vf   \         P                   ! 4       MT\        ,
          o\        V V3R l\        \        \
        3 4       4      # )Nc              3  óL   <"  € T F  p\        SV,          R S4      x € K  	  R# 5i)r!  N)rO  )rk   r?   r>   rK  s   & €€r   rm   Ú_sweep_stale.<locals>.<genexpr>É  s$   øé € ÐwÑPvÈÔ& t¨c¥z°8¸V×DÐDÓPvùs   ƒ!$)rŒ   ÚSTALE_AFTER_SECONDSÚsumr;   r<   r:   )r>   r  rK  s   f$@r   r'  r'  Ç  s5   ù€ Ø š[Œd�iŠiŒk¨cÔ5HÕH€FÜÕwÔQ\Ô^iÔkuÑPvÓwÓwÐwr   c               ó    € V ^8„  d   QhRRRR/# )r   Úmax_age_hoursrQ  r2   r‚   r   )r   s   "r   r   r   Ì  s   € ÷ 	ñ 	¨|ð 	Àsñ 	r   c                óè   € ?  \        \        \        \        4       4      4      4      pVP	                  4       '       d   \        V4      # ^ #   \         d    \        P                  RRR7        ^ # i ; i)z±Hourly sweep of stale relay artifacts (DM plaintext; ``_sweep_stale`` otherwise runs
only on Desktop drains). ``max_age_hours`` is for ``cleanup_*_cache`` signature parity only.zbot_relay artifact sweep failedTrQ   )	r5   r	   r   r   Úis_dirr'  rW   rX   rY   )rX  r>   s   & r   Úcleanup_bot_relay_artifactsr[  Ì  s_   € ð 	ðÜœ,¤t¬M«OÓ'<Ó=Ó>ˆØ%)§[¡[§]¢]Œ|˜DÓ!Ð9¸Ð9øÜô Ü�‰Ð6ÀˆÔFÚðús   ƒAA Á	A Á"A1Á0A1c               ó$   € V ^8„  d   QhRRRRRR/# )r   r0   r1   r  rx   r2   r   r   )r   s   "r   r   r   Ø  s!   € ÷ ñ ˜ð ¨tð ¸ñ r   c                ó  € \        \        V 4      \        ,          VR,           R2,          4      pRVP                  RR4       RVP                  RR4       2p\        \	        \
        4      P                  4       P                  R4      4      p\        P                  ;'       g    R	VR
W#\        \        4      .p\        P                  R8X  d!   V Uu. uF  qfP                  RR4      NK  	  pp\        P                  ! V4      # u upi )uý  Shell command that blocks until the reply file appears, then prints it; spawned
via ``terminal_tool(background=True, notify_on_complete=True)`` so its stdout arrives
as the same completion notification local DMs use.

A ``tools/bot_mode_dm.py --wait-reply`` entrypoint, like the local delivery runner â€” not
``python -c``. The approval gate flags inline interpreter code ("script execution via -e/-c
flag"), and ``approvals.single_query_mode`` defaults to ``deny`` for the one-shot ``-Q`` turn a
bot replies from, so the reply waiter was refused exactly when a bot answered a teammate: the
message was delivered, the reply never woke the sender. Roster fields ride as argv (``shlex``
quoted), never as source text, so a hostile handle or connection id stays data.
rû   r  rd   r  rb   rù   rÿ   zbot_mode_dm.pyÚpython3z--wait-replyÚwin32Ú\Ú/)r   r5   r<   rV   r   Ú__file__ÚresolveÚ	with_nameÚsysÚ
executabler9  Úplatformr+  Úshlexr|   )r0   r  Ú
reply_pathr  ÚrunnerÚargvÚparts   &&     r   Úwaiter_commandrm  Ø  sÚ   € ô ”Z Ó%¬Õ3¸À$½Ð8HÈÐ6NÕNÓO€JØ�—‘˜_¨bÓ1Ð2°$°x·|±|ÐDWÐY[Ó7\Ð6]Ð^€EÜ””h“×'Ñ'Ó)×3Ñ3Ð4DÓEÓF€FÜ�N‰N×'Ð'˜i¨°ÀÔTWÔXjÓTkÐl€DÜ
‡|�|�wÔñ 59Ó9±D¨D—‘˜T 3Ö'±DˆÐ9Ü�:Š:�dÓÐùò :s   ÃDc               ó   € V ^8„  d   QhRR/# ©r   r2   r   r   )r   s   "r   r   r   ï  s   € ÷ Uñ U�Sñ Ur   c                 óæ  € \         P                  R8X  d   RMRp \        \        4      P	                  4       P
                  ^,          R,          R,          V ,          pVP                  4       '       d   \        V4      # \        \         P                  ;'       g    R4      P                  V ,          pVP                  4       '       d   \        V4      # \        P                  ! R4      ;'       g    R# )av  Prefer this install's published launcher, then interpreter/PATH fallbacks.

A long-lived caller can still run in an older dependency generation. Its
sibling console script pins that generation, whereas the published launcher
selects current dependencies at child start. Keep the historical fallbacks
for external/developer installs that have no published launcher (#93590).
r_  z
hermes.exerf   z.hermesÚbinrb   )re  rg  r   rb  rc  ÚparentsrM  r   rf  ÚparentÚshutilÚwhich)r  Ú	publishedÚsiblings      r   Ú_hermes_clirx  ï  s¨   € ô Ÿ<™<¨7Ô2‰<¸€DÜ”X“×&Ñ&Ó(×0Ñ0°Õ3°iÕ?À%ÕGÈ$ÕN€IØ×Ñ×ÒÜ�9‹~ÐÜ”3—>‘>×'Ð' RÓ(×/Ñ/°$Õ6€GØ"Ÿ?™?×,Ò,Œ3ˆw‹<ÐT´&·,²,¸xÓ2H×2TÐ2TÈHÐTr   c                  óB   € ] tR tRtRtR	tR R ltR R ltR R ltR	t	R
# )ÚDeliveryAuthori  a!  A relayed turn's author as an in-process object. ``bot_relay.deliver`` builds it from the sender fields
an admitted gateway client relays for another connection; nothing verifies the sender itself. A JSON
client cannot build one, so ``prompt.submit`` accepts the object and refuses a dict.c               ó    € V ^8„  d   QhRRRR/# )r   Úauthorrx   r2   rF   r   )r   s   "r   r   ÚDeliveryAuthor.__annotate__  s   € ÷ #ñ #˜tð #¨ñ #r   c                	ó&   € \        V4      V n        R # r    )rx   r|  )r#   r|  s   &&r   r"   ÚDeliveryAuthor.__init__  s   € Ü˜6“lˆŽr   c               ó    € V ^8„  d   QhRRRR/# )r   ÚotherÚobjectr2   rE   r   )r   s   "r   r   r}    s   € ÷ Qñ Q˜Fð Q tñ Qr   c                	ód   € \        V\        4      ;'       d    VP                  V P                  8H  # r    )rw   rz  r|  )r#   r�  s   &&r   Ú__eq__ÚDeliveryAuthor.__eq__  s$   € Ü˜%¤Ó0×PÐP°U·\±\ÀTÇ[Á[Ñ5PÐPr   c               ó   € V ^8„  d   QhRR/# ro  r   )r   s   "r   r   r}    s   € ÷ 2ñ 2˜#ñ 2r   c                	ó$   € R V P                   : R2# )zDeliveryAuthor(Ú)©r|  )r#   s   &r   Ú__repr__ÚDeliveryAuthor.__repr__  s   € Ø  §¡¡¨qÐ1Ð1r   r‰  N)
r&   r'   r(   r)   r*   Ú	__slots__r"   r„  rŠ  r+   r   r   r   rz  rz    s$   † ñ\ð €Iõ#õQ÷2ñ 2r   rz  c               ó(   € V ^8„  d   QhRRRRRRRR/# )r   rþ   r   rÒ   rÓ   r2   r_   r   )r   s   "r   r   r     s)   € ÷ ñ  sð ¸ð Èsð Ð^lñ r   c                ó  € ^ RI Hp \        T ;'       g    R4      P                  4       pV'       g   R# RT! T\        T;'       g    R4      4      R\        T;'       g    R4      P                  4       ;'       g    TRR/# )a\  The author of a relayed DM's recipient turn, built from the sender fields as the relaying client reports
them. A relayed DM always comes from another gateway, so the id carries the Desktop's id for the sender's
connection (``local`` included) and only the recipient's own profiles are bare ``bot:<profile>``. None when
the envelope names no sender.©Úbot_author_idrb   Nrû   r  Úis_botT©Úagent.turn_authorr�  r   ry   )rþ   rÒ   rÓ   r�  ra   s   &&&  r   Údelivery_turn_authorr”    su   € õ
 0ä�,×$Ð$ "Ó%×+Ñ+Ó-€GßÙØ‘- ¬¨_×-BÐ-BÀÓ)CÓDÀfÌcÐR]×RcÐRcÐacÓNd×NjÑNjÓNl×NwÐNwÐpwØ�dðð r   c               ó   € V ^8„  d   QhRR/# )r   r2   ztuple[str, ...]r   )r   s   "r   r   r      s   € ÷ ñ ¨?ñ r   c                 ó$   € ^ RI Hp  \        V 4      # )a  Session-bound env names to strip from a delivery child, from ``gateway.session_context``.

Synced with the session binding surface as vars are added; deliberately NOT a
``HERMES_SESSION_*`` prefix match, which would also strip non-identity knobs
(e.g. ``HERMES_SESSION_STALL_TIMEOUT``).©Ú_VAR_MAP)Úgateway.session_contextr˜  Útupler—  s    r   Ú!_delivery_child_session_env_namesr›     s   € õ 1ä�‹?Ðr   c               ó    € V ^8„  d   QhRRRR/# )r   Ú	principalr   r2   rx   r   )r   s   "r   r   r   +  s   € ÷ 
tñ 
t¨ð 
t°ñ 
tr   c                ón   € ^ RI Hp RT! R\        T ;'       g    R4      P                  4       4      RRRR/# )	u8  The author of a relayed DM whose sender fields cannot be trusted: a logged-in client named them.

Server-derived and unspoofable â€” the id is built from the caller's minted identity digest, never from
anything the client sent â€” and still a BOT author, because the recipient's memory routes on that:
Honcho writes a bot-authored turn into the bot's own a2a session and refuses conclusion / profile /
mirror writes for it, while an unattributed turn is treated as the human's (#107598 review). The
human-facing signature stays in the message text the sender composed.r�  rû   Úrelayrb   r  zrelayed teammater‘  Tr’  )r�  r�  s   & r   Úrelaying_principal_authorr   +  s8   € õ 0à‘- ¬¨Y¯_¨_¸"Ó)=×)CÑ)CÓ)EÓFÈÐPbÐdlÐnrÐsÐsr   c               ó$   € V ^8„  d   QhRRRRRR/# )r   r|  r_   Úprofile_homezstr | Path | Noner2   zdict[str, str]r   )r   s   "r   r   r   8  s#   € ÷ ñ ˜ð Ð7Hð ÐTbñ r   c                ó„  € ^ RI HpHp ^ RIHpHp ^ RIHpHp ^ RI	H
p Tp	V	'       g+   V! 4       '       d   V! 4       '       g   V! 4       f   V! 4       p	V! \        P                  V	RR7      p
V
P                  VR4       \        4        F  pV
P                  VR4       K  	  V '       d   V
P                  V! V 4      4       V
# )aƒ  Environment for one delivery turn's ``hermes -p <profile>`` child. The dispatcher's own
HERMES_TURN_AUTHOR is dropped first so a delivery without an author never inherits the author of the turn
that sent it. Dispatcher session identity (the canonical ``gateway.session_context`` session env names) is
dropped too: a nested recipient that ``message_agent``s onward must not stamp that grandchild
notify with the grandparent's key, or the live recipient never resumes. The child runs the target
profile's Bot Chat turn, so it starts from THAT profile's env (``served_profile_child_env``: launch
profile ``.env`` / TERMINAL_* residue dropped, target secrets overlaid), never the multiplexer's raw
``os.environ``; ``-p`` alone only pinned HERMES_HOME. ``profile_home`` is the target's home when the
caller knows it (relay RPC, roster); otherwise the active override, and under multiplex the launch
home.)Úcurrent_secret_scopeÚis_multiplex_active)ÚTURN_AUTHOR_ENVÚturn_author_env)Úget_hermes_home_overrideÚget_routing_process_hermes_home)Úserved_profile_child_envNT)r>   Útarget_homeÚinherit_credentials)Úagent.secret_scoper¤  r¥  r“  r¦  r§  r=   r¨  r©  Útools.environments.localrª  r*  ÚenvironÚpopr›  Úupdate)r|  r¢  r¤  r¥  r¦  r§  r¨  r©  rª  r«  r  r  s   &&          r   Údelivery_envr²  8  s“   € ÷ MßBßZÝAð €KßÑ/×1Ò1Ñ:R×:TÒ:TÙ$Ó&Ò.Ù5Ó7ˆÙ
"¬¯
©
ÀÐaeÔ
f€CØ‡G�GˆO˜TÔ"Ü1Ö3ˆØ�‰��dÖñ 4çØ�
‰
‘? 6Ó*Ô+Ø€Jr   c                  ó6   a € ] tR tRtRtRtR V 3R lltRtV ;t# )ÚTurnBusyErrorih  u  A delivery turn is already running for the target profile (``waited_seconds`` â‰ˆ time queued).

``reason`` is 'target_busy' â€” extends the #93091 item-1 structured refusal enum. ``waited_seconds`` is
roughly how long the caller queued behind the current turn before giving up.
Útarget_busyc               ó    € V ^8„  d   QhRRRR/# )r   ra   r   Úwaited_secondsr  r   )r   s   "r   r   ÚTurnBusyError.__annotate__q  s   € ÷ Jñ J ð J°Uñ Jr   c                	ób   <€ WuV n         V n        \        SV `  R V R\	        V4       R24       R# )zCtarget_busy: another delivery turn is already running for profile 'u   ' â€” queued behind it for ~zEs without it finishing. The message was NOT delivered; retry shortly.N)ra   r·  r!   r"   Úround)r#   ra   r·  r$   s   &&&€r   r"   ÚTurnBusyError.__init__q  sG   ø€ Ø,3Ð)ˆŒ�dÔ)Ü‰ÑÐ^Ð_fÐ^gð h2Ü27¸Ó2GÐ1Hð IIðIö 	Jr   )ra   r·  )	r&   r'   r(   r)   r*   r   r"   r+   r,   r-   s   @r   r´  r´  h  s   ø† ñð €F÷Jö Jr   r´  c               ó   € V ^8„  d   QhRR/# )r   r2   r  r   )r   s   "r   r   r   x  s   € ÷ Vñ V˜5ñ Vr   c                 ól   € \        RRR7      p V f   \        \        4      # \        R\        V 4      4      # )z=Wait budget for a queued delivery turn (config, lazily read).Úturn_wait_secondsÚload_configrá   r  )r\   r  ÚTURN_WAIT_SECONDS_FALLBACKÚmaxræ   s    r   r¾  r¾  x  s0   € ä
Ð+°MÔ
B€CØ03²Œ5Ô+Ó,ÐUÄÀSÌ%ÐPSË*ÓAUÐUr   c               ó$   € V ^8„  d   QhRRRRRR/# )r   r0   r1   ra   r   r2   r   r   )r   s   "r   r   r   ~  s!   € ÷ 9ñ 9˜ð 9¨cð 9°dñ 9r   c                óº   € \         P                  ! RR\        T;'       g    R4      4      R,          ;'       g    Rp\        V 4      \        ,          V R2,          # )u?   Per-profile lockfile path (short â€” safe on macOS temp roots).z[^a-zA-Z0-9_-]Ú_rb   :Né@   Nz.lock)r¾   r?   r   r5   Ú	LOCKS_DIR)r0   ra   rE  s   && r   Úturn_lock_pathrÇ  ~  sH   € ä�6Š6Ð# S¬#¨g¯m¨m¸Ó*<Ó=¸cÕB×IÐIÀc€DÜ�dÓœiÕ'¨T¨F°%¨.Õ8Ð8r   c               ó(   € V ^8„  d   QhRRRRRRRR/# )	r   r0   r1   ra   r   Útimeout_secondsrQ  r2   zIterator[Path]r   )r   s   "r   r   r   …  s)   € ÷ #ñ #˜Jð #°ð #À|ð #Ð_mñ #r   c              #  óà  "  €  ^ RI pTf   \        4       M\        R\        T4      4      p\	        Y4      pTP                  P                  RRR7       \        P                  ! T\        P                  \        P                  ,          R4      p \        P                  ! 4       pYt,           p  TP!                  YcP"                  TP$                  ,          4         Tx € \.        P0                  ! \&        4      ;_uu_ 4        TP!                  YcP2                  4       RRR4       \        P4                  ! T4       R#   \         d'    \        P                  R4       \	        Y4      x €  R# i ; i  \&         dc    \        P                  ! 4       p	Y˜8¼  d   \)        YT,
          4      h\        P*                  ! \-        R\        RY‰,
          4      4      4        EK2  i ; i  + '       g   i     LÌ; i  \.        P0                  ! \&        4      ;_uu_ 4        TP!                  YcP2                  4       RRR4       i   + '       g   i     i ; i; i  \        P4                  ! T4       i ; i5i)	u  Hold ``profile``'s cross-process turn lock for the ``with`` body: non-blocking
flock probe + short-sleep retry up to the budget (``bot_mode.turn_wait_seconds``
unless ``timeout_seconds``); raises :class:`TurnBusyError` when exhausted. No
ordering among waiters, but every waiter is bounded. Without ``fcntl`` (Windows)
the lock is a no-op â€” those installs never had this race path.Nz:bot turn lock disabled: fcntl unavailable on this platformr  T)rr  Úexist_okrH   gš™™™™™¹?g{®Gázt?)ÚfcntlÚImportErrorrX   rY   rÇ  r¾  rÁ  r  rs  Úmkdirr*  ÚopenÚO_RDWRÚO_CREATrŒ   Ú	monotonicÚflockÚLOCK_EXÚLOCK_NBrï   r´  ÚsleepÚminr  r  ÚLOCK_UNÚclose)
r0   ra   rÉ  rÌ  Úbudgetr
  ÚfdÚstartÚdeadliner  s
   &&&       r   Úacquire_turn_lockrÞ  „  s¨  é € ðÛð %4Ò$;ÔÔ ÄÀSÌ%ÐP_ÓJ`ÓAa€FÜ˜$Ó(€DØ‡K�K×Ñ˜d¨TÐÔ2Ü	�Š�”r—y‘y¤2§:¡:Õ-¨uÓ	5€BðÜ—’Ó ˆØ•>ˆØðAØ—‘˜B§¡°·±Õ =Ô>Øð	/ØŠJä×$Ò$¤W×-Õ-Ø—‘˜B§¡Ô.÷ .ô 	�Š�Žøô7 ô Ü�‰ÐQÔRÜ˜TÓ+Ò+Úðûô  ô AÜ—n’nÓ&�Ø”?Ü'¨°uµÓ=Ð=Ü—
’
œ3˜s¤C¨¨x­~Ó$>Ó?×@Ð@ð	Aú÷ .×-û”×$Ò$¤W×-Õ-Ø—‘˜B§¡Ô.÷ .×-×-Ð-þô 	�Š��üs­   ‚I.„D> ˆBI.ÂI Â.,E2 ÃI ÃG5 Ã "I ÄG"ÄI Ä&I.Ä>-E/Å+I.Å.E/Å/I.Å2A(GÇI ÇGÇI Ç"G2	Ç-I Ç5#IÈH=	È4	IÈ=IÉIÉI ÉI+É+I.i`T  r    )Yr*   Ú
__future__r   r  r›   Úloggingr*  r¾   rh  rt  re  rŒ   r  Úpathlibr   Útypingr   r   r   r   Útools.bot_mode_prober   r	   r
   Úutilsr   Ú	getLoggerr&   rX   r4   r‹   r:   r;   r<   rÆ  rÀ  râ   ÚTURN_ATTEMPT_TIMEOUT_SECONDSÚTURN_MAX_ATTEMPTSÚ)DESKTOP_DELIVER_SETTLEMENT_MARGIN_SECONDSÚDESKTOP_DELIVER_TIMEOUT_SECONDSr?  r9  rU  rð   ÚRuntimeErrorr   Úcompileri   r5   r@   rK   r\   r   r’   r¡   r¨   r«   r¹   rÀ   Ú	frozensetrÐ   ÚDOTALLr×   rÜ   rè   ró   r  r  r  r2  r.  r8  r  rO  r'  r[  rm  rx  rz  r”  r›  r   r²  r´  r¾  rÇ  ÚcontextmanagerrÞ  r   r   r   Ú<module>rï     s+  ðñõ #ã Û Û Û 	Û 	Û Û Û 
Û Û Ý ß 3Ó 3ç IÑ IÝ #à	×	Ò	˜8Ó	$€à€Ø€Ø€
Ø€Ø€Ø€	ð !Ð Ø"Ð ð  #Ð ØÐ à,/Ð )àÐ!=Ð@QÕ!QÕQÐT}Õ}ð  ð 8¸"Õ<Ð ð +Ð-LÕLÈrÕQÐ àÐ ð Ð ô	˜<ô 	ð �ZŠZÐ;Ó<€
õ'õðUÈ÷ Uõ
õõ,õõ;õ
Bõ<õ 6ñ V_ÓU`÷ *ð4 —:’:ÐSÐUW×U^ÑU^Ó_Ð ñ CLÃ+÷Lõ
,õõ,õ(õ(õ!TõH#õL,ð"ÀBð ÐUWð Ðgi÷ õ4	ðx°D÷ x÷
	õõ.U÷$2ñ 2÷"õõ
t÷ô`J�Lô Jõ Võ9ð ×Ñö#ó ò#r   