+
    lHàjÀN  ã                  óT  a € 0 t $ R t^ RIHt ^ RIHt ^RIHtHt ]! 4       t	R R lt
R R ltRsR	]R
&   R R ltR R ltR R ltR R ltR R ltR R lt/ tR]R&   R R ltR R ltR R ltR R  ltR! R" ltR# tR$ R% ltR& R' ltRSt/ tR(]R)&   R* R+ lt] ! 4       t!R, t"R- t#R. t$R/ t%R0t&Rs'R1]R2&   R3 R4 lt(^ s)R5 t*R6 t+R7R8]]3R9R:R; ],3R<R=]#],3R>R8R? ],3R@R8]$],3RAR8]%],3RBR8]+],3RCR:]*],3/t-RD]RE&   R<R8R>RF/t./ t/RG]RH&   / t0RI]RJ&   / t1RI]RK&   RTRL RM llt2RNs3RO RP lt4RQ RR lt5R# )UzùSkin + config-change watcher: on-disk signatures for skin/pet/cron/sessions/platforms/
pairing/bot-relay state and the broadcast loop that pushes *.changed events. Bodies are
rebound onto server.py's globals at install time (method_ctx.bind_module).)Úannotations)ÚPath)ÚHandlerRegistryÚbind_modulec               ó   € V ^8„  d   QhRR/# ©é   ÚreturnÚdict© )Úformats   "Ú?/home/ubuntu/.hermes/hermes-agent/tui_gateway/change_watcher.pyÚ__annotate__r      s   € ÷ %ñ %�dñ %ó    c                 ó(   € \        \        4       4      # ©N)Úskin_payloadÚ	_load_cfgr   r   r   Úresolve_skinr      s   € Üœ	›Ó$Ð$r   c               ó    € V ^8„  d   QhRRRR/# )r   Úconfigr
   r	   r   )r   s   "r   r   r      s   € ÷ ñ ˜ð  $ñ r   c                ó€  €  ^ RI HpHp V! V 4       V! 4       pRVP                  RVP                  RVP
                  RVP                  RVP                  RVP                  RVP                  R	VP                  R
VP                  ;'       g    / P                  R
R4      RVP                  /
#   \         d    / u # i ; i)z¿The ``skin.changed`` / ``gateway.ready`` skin for ``config``'s ``display.skin`` (``{}`` when the
skin engine fails). Pure of server state: the shared owner calls it under the profile's scope.)Úinit_skin_from_configÚget_active_skinÚnameÚcolorsÚlight_colorsÚdark_colorsÚbrandingÚbanner_logoÚbanner_heroÚtool_prefixÚhelp_headerÚ Ú	customCSS)Úhermes_cli.skin_enginer   r   r   r   r   r   r   r   r    r!   ÚgetÚ
custom_cssÚ	Exception)r   r   r   Úskins   &   r   r   r      s±   € ðßQÙ˜fÔ%ÙÓ ˆð �D—I‘I˜x¨¯©Ø˜D×-Ñ-¨}¸d×>NÑ>NØ˜Ÿ™ }°d×6FÑ6FØ˜4×+Ñ+¨]¸D×<LÑ<LØ˜DŸM™M×/Ð/¨R×4Ñ4°]ÀBÓGà˜Ÿ™ð*ð 	*øô ô ØŠ	ðús   ‚B
B- ÂB- Â-B=Â<B=Nztuple[str, float | None] | NoneÚ_last_skin_sigc               ó   € V ^8„  d   QhRR/# )r   r	   r   r   )r   s   "r   r   r   +   s   € ÷ Vñ V�tñ Vr   c                 ó|   € \        4       p \        \        V \        4      '       d   V '       d   V 4      # \        4      # )z>Active profile home for the change watcher's signature probes.)Úget_hermes_home_overrider   Ú
isinstanceÚstrÚ_hermes_home)Úoverrides    r   Ú_watcher_homer2   +   s+   € ä'Ó)€HÜœJ x´×5Ò5¿(�ÓUÐUÌÓUÐUr   c               ó   € V ^8„  d   QhRR/# )r   Úpathr   r   )r   s   "r   r   r   1   s   € ÷ ñ ˜Dñ r   c                ó\   €  V P                  4       P                  #   \         d     R# i ; i)z?``st_mtime_ns`` of ``path``, or None when it cannot be stat'ed.N)ÚstatÚst_mtime_nsÚOSError)r4   s   &r   Ú_watcher_mtime_nsr9   1   s+   € ðØ�y‰y‹{×&Ñ&Ð&øÜô Úðús   ‚ œ+ª+c               ó   € V ^8„  d   QhRR/# )r   Úpartsr/   r   )r   s   "r   r   r   9   s   € ÷ ?ñ ?˜3ñ ?r   c                 óD   € \        \        4       P                  ! V !  4      # r   )r9   r2   Újoinpath)r;   s   *r   Ú_home_mtime_nsr>   9   s   € Üœ]›_×5Ò5°uÑ=Ó>Ð>r   c               ó   € V ^8„  d   QhRR/# )r   r	   z
int | Noner   )r   s   "r   r   r   =   s   € ÷ Zñ Z˜zñ Zr   c                óF   € \        R \        \        V 4       4       RR7      # )zSMax ``st_mtime_ns`` across ``paths`` (unstat-able ignored); None when none stat'ed.c              3  ó0   "  € T F  qf   K  Vx € K  	  R # 5ir   r   )Ú.0Úms   & r   Ú	<genexpr>Ú#_newest_mtime_ns.<locals>.<genexpr>?   s   é € ÐJÑ8�a—’Ó8ùs   ‚Œ
N©Údefault)ÚmaxÚmapr9   )Úpathss   &r   Ú_newest_mtime_nsrK   =   s   € äÑJœ3Ô0°%Ô8ÓJÐTXÔYÐYr   c               ó   € V ^8„  d   QhRR/# ©r   r	   Útupler   )r   s   "r   r   r   B   s   € ÷ "ñ "˜ñ "r   c                ó>  € . pV  Fn  p\         P                  \        4      ;_uu_ 4        VP                  4       pVP	                  \        V4      VP                  VP                  34       RRR4       Kp  	  \        \        V4      4      #   + '       g   i     K—  ; i)zýSorted ``(path, mtime_ns, size)`` per stat-able file in ``paths``.

For multi-file signatures, unlike a newest-mtime max: a file added, removed or rewritten in
the same timestamp tick as the current newest one (coarse-mtime filesystems) still moves it.
N)
Ú
contextlibÚsuppressr8   r6   Úappendr/   r7   Úst_sizerN   Úsorted)rJ   Úentriesr4   Ústs   &   r   Ú
_files_sigrW   B   ss   € ð €GÛˆÜ× Ñ ¤×)Õ)Ø—‘“ˆBØ�N‰NœC ›I r§~¡~°r·z±zÐBÔC÷ *Ñ)ñ ô ”˜“Ó!Ð!÷ *×)Ð)ús   ©ABÂBc               ó   € V ^8„  d   QhRR/# )r   r	   ztuple[str, float | None]r   )r   s   "r   r   r   P   s   € ÷ ñ Ð+ñ r   c                 ó"  € \        \        4       P                  R4      ;'       g    / P                  R4      ;'       g    R4      p  V \        4       R,          V  R2,          P	                  4       P
                  3#   \         d    T R3u # i ; i)z—(active skin name, its user-file mtime). Built-ins have no file, so only
their name moves; a user skin's mtime lets an in-place color edit repaint too.Údisplayr)   rG   Úskinsz.yamlN)r/   r   r&   r2   r6   Úst_mtimer8   )r   s    r   Ú	_skin_sigr]   P   s~   € ô ”	“—‘ 	Ó*×0Ð0¨b×5Ñ5°fÓ=×JÐJÀÓK€DðØ”m“o¨Õ/°T°F¸%°.Õ@×FÑFÓH×QÑQÐQÐQøÜô Ø�TˆzÒðús   Á4A< Á<BÂBz#dict[str, tuple[str, float | None]]Ú_skin_sig_by_homec               ó   € V ^8„  d   QhRR/# )r   r	   z
str | Noner   )r   s   "r   r   r   `   s   € ÷ ñ  *ñ r   c                óŒ  € \         P                  \        4      ;_uu_ 4        \        \	        V 4      4      pVP                  4       '       d\   VP                  4       '       dF   VP                  4       \        \        4      P                  4       8w  d   \	        V4      uuRRR4       # RRR4       R#   + '       g   i     R# ; i)zXThe profile home a peer's skin follows, or None for the launch home (the ambient scope).N)	rP   rQ   r(   r   r/   Úis_absoluteÚis_dirÚresolver0   )Ú
profile_idÚhomes   & r   Ú_skin_home_forrf   `   sz   € ä	×	Ñ	œY×	'Õ	'Ü”C˜
“OÓ$ˆØ×Ñ×Ò $§+¡+§-¢-°D·L±L³NÄdÌ<ÓFX×F`ÑF`ÓFbÔ4bÜ�t“9÷ 
(×	'Ò	'ñ ÷	 
(Ö	'ñ ús   ¢A B2Á#:B2Â2C	c               ó   € V ^8„  d   QhRR/# )r   re   r/   r   )r   s   "r   r   r   i   s   € ÷ *ñ *�cñ *r   c                ó^   € \        V 4      p V! 4       \        V4       #   \        T4       i ; ir   )Úset_hermes_home_overrideÚreset_hermes_home_override)re   ÚfnÚtokens   && r   Ú_under_homerm   i   s(   € Ü$ TÓ*€Eð*Ù‹tä" 5Õ)øÔ" 5Õ)ús   � Ÿ,c               ó   € V ^8„  d   QhRR/# ©r   r	   ÚNoner   )r   s   "r   r   r   q   s   € ÷ )ñ )˜dñ )r   c                 óð   € \         P                  \        4      ;_uu_ 4        \        \	        4       4      ;p '       d   \        4       \        V &   M
\        4       sRRR4       R#   + '       g   i     R# ; i)zSSync the baseline after the /skin RPC emits so the watcher doesn't re-broadcast it.N)rP   rQ   r(   rf   r-   r]   r^   r*   )re   s    r   Ú_note_skin_broadcastrr   q   sL   € ô 
×	Ñ	œY×	'Õ	'Ü!Ô":Ó"<Ó=Ð=ˆ4Ö=Ü&/£kÔ˜dÒ#ä&›[ˆN÷	 
(×	'×	'Ò	'ús   ¢8A$Á$A5	c               ó   € V ^8„  d   QhRR/# )r   r	   zdict[str, list]r   )r   s   "r   r   r   {   s   € ÷ ñ ˜_ñ r   c                 óþ   € \         ;_uu_ 4        \        \        4      p R R R 4       / pX  F;  p\        VRR 4      ;p'       g   K  VP	                  V. 4      P                  V4       K=  	  V#   + '       g   i     LU; i)NÚ	skin_home)Ú_live_transports_lockÚlistÚ_live_transportsÚgetattrÚ
setdefaultrR   )ÚtargetsÚpeersÚ	transportre   s       r   Ú_skin_peers_by_homer~   {   si   € ß	Ö	ÜÔ'Ó(ˆ÷ 
à€EÛˆ	Ü˜9 k°4Ó8Ð8ˆ4×8Ð8Ø×Ñ˜T 2Ó&×-Ñ-¨iÖ8ñ ð €L÷ 
×	ús   �A,Á,A<	c               ó   € V ^8„  d   QhRR/# ro   r   )r   s   "r   r   r   …   s   € ÷ &ñ & Dñ &r   c                 ód  € \        4       p \        P                  \        4      ;_uu_ 4        \	        4       pV\
        8w  d@   Vs\        R\        4       V P                  4        UUu0 uF  q" F  q3kK  	  K  	  uppR7       RRR4       V P                  4        FÑ  w  rB\        P                  \        4      ;_uu_ 4        \        V\        4      p\        P                  V4      pV\        V&   Ve   W8X  d    RRR4       Kj  \        RR\        V\        4      4      pV F>  p\        P                  \        4      ;_uu_ 4        VP                  V4       RRR4       K@  	  RRR4       KÓ  	  R# u uppi   + '       g   i     Lý; i  + '       g   i     Kz  ; i  + '       g   i     EK  ; i)z Emit ``skin.changed`` when the active skin moved, via the SAME live path as
``/skin`` so every surface repaints. The check is a dict lookup + one stat per home.zskin.changed©ÚskipNr#   )r~   rP   rQ   r(   r]   r*   Ú_broadcast_global_eventr   ÚvaluesÚitemsrm   r^   r&   Ú_event_frameÚwrite)Úby_homeÚsigr|   Úpeerre   ÚseenÚframes          r   Ú_broadcast_skin_if_changedr�   …   s@  € ô "Ó#€GÜ	×	Ñ	œY×	'Õ	'Ü‹kˆØ”.Ô Ø ˆNÜ# N´L³NØ<C¿N¹NÔ<LÔ)_Ñ<L°5ÒY^ÐQUª$ÑY^©$Ñ<LÒ)_õa÷	 
(ð —}‘}–‰ˆÜ× Ñ ¤×+Õ+Ü˜d¤IÓ.ˆCÜ$×(Ñ(¨Ó.ˆDØ&)Ô˜dÑ#ØŠ|˜sœ{Ø÷ ,Ñ+ô ! °´[ÀÄ|Ó5TÓUˆEÛ�Ü×(Ñ(¬×3Õ3Ø—J‘J˜uÔ%÷ 4Ñ3ñ ÷ ,Ñ+ó 'ùó *`÷ 
(×	'ú÷ 4×3Ð3ú÷ ,×+Ñ+úsH   ¬:E6Á&E0Á8	E6Â?:FÄAFÅF	ÅFÅ0E6Å6F	Æ	FÆ	FÆF/c                 ób   € \        4       w  rpV '       d   Ve   VP                  '       d   W3# R# )zO(pet, scale) when an enabled pet with an existing sheet is selected, else None.N)Ú_pet_active_selectionÚexists)ÚenabledÚpetÚscales      r   Ú_active_petr”   �   s*   € ä/Ó1Ñ€G�%ß" s¢¸3¿:¿:¸:ˆCˆ<ÐOÈ4ÐOr   c               ó   € V ^8„  d   QhRR/# rM   r   )r   s   "r   r   r   £   s   € ÷ ñ �%ñ r   c                 ó¬  € \        4       P                  R4      ;'       g    / p \        V P                  R4      \        4      '       d   V P                  R4      M/ pV'       d#   \	        VP                  R4      RR7      '       g   R#  \        4       ;p'       d'   Vw  r4VP                  \        VP                  4      V3#  R#   \         d     R# i ; i)uM   (slug, spritesheet revision, scale) of the active pet â€” ("off",) when none.rZ   r’   r‘   FrF   )Úoff)
r   r&   r.   r
   Úis_truthy_valuer”   ÚslugÚ_pet_sheet_revisionÚspritesheetr(   )rZ   Úpet_cfgÚactiver’   r“   s        r   Ú_pet_sigrž   £   sµ   € ä‹k�o‰o˜iÓ(×.Ð.¨B€GÜ$.¨w¯{©{¸5Ó/AÄ4×$HÒ$Hˆg�k‰k˜%Ô Èb€Gßœ/¨'¯+©+°iÓ*@È%×PÓPØˆðÜ “]Ð"ˆ6Ö"Ø‰JˆCØ—H‘HÔ1°#·/±/ÓBÀEÐJÐJð #ð
 €Oøô ô ØØ€Oðús   Â	7C ÃCÃCc               ó   € V ^8„  d   QhRR/# r   r   )r   s   "r   r   r   ²   s   € ÷ 	ñ 	˜dñ 	r   c                 óÈ   €  \        4       ;p '       d8   V w  rRRRVP                  RVP                  RVR\        VP                  4      /#  RR/#   \
         d     RR/# i ; i)zW``pet.info.meta``-shaped payload so the renderer can decide whether to refetch sprites.r‘   Tr™   ÚdisplayNamer“   ÚspritesheetRevisionF)r”   r™   Údisplay_namerš   r›   r(   )r�   r’   r“   s      r   Ú_pet_changed_payloadr¤   ²   sƒ   € ðÜ “]Ð"ˆ6Ö"Ø‰JˆCØ˜t V¨S¯X©X°}Àc×FVÑFVØ˜UÐ$9Ô;NÈsÏÉÓ;_ðað að #ð �uÐÐøô ô ØØ�uÐÐðús   ‚AA ÁA!Á A!z&dict[str, tuple[object, tuple | None]]Ú_sessions_db_sig_cachec               ó   € V ^8„  d   QhRR/# )r   Údb_pathr   r   )r   s   "r   r   r   Í   s   € ÷ 7ñ 7 Tñ 7r   c                ó¾  a€ \        W P                  V P                   R24      34      p\        V 4      p\        P                  V4      pVe   V^ ,          V8X  d
   V^,          # V P                  4       '       g   VR3\        V&   R# Rp ^ RIp^ RIH	p ^ RI
Hp V! V! V 4      V RRR7      pVP                  R4       Uu0 uF  qˆ^,          kK  	  upo\        ;QJ d    . V3R	 l\         4       F  NK  	  5M! V3R	 l\         4       4      p	V	'       g   R
V3p
M¢RS9   d   RMRpVP                  RRP                  V	4       RV 24      pVP!                  ^R7      pV FG  pVP#                  \%        \        V4      4      P'                  RR4      4       VP#                  R4       KI  	  W�P)                  4       3p
 Ve   VP-                  4        \/        T4      T
3\        T&   T
# u upi   \*         dL    TeA   T^,          e6   T^,          ^ ,          R
8w  d!   T^,          u Te   TP-                  4        # # R
T3p
 L„i ; i  Te   TP-                  4        i i ; i)a‘  Digest list/transcript-relevant session rows, excluding unrelated tables.

``gateway_heartbeats`` shares state.db and writes every minute. Using the
database mtime directly therefore emits sessions.changed while no session
changed (#98005). Cache behind the DB/WAL mtime, then inspect only the
sessions columns that drive Desktop projections. Legacy stores safely use
the subset of columns they have.
z-walN)Ú_connect_tracked_db)Úread_only_db_uriTgš™™™™™©?)Útracking_pathÚuriÚtimeoutzPRAGMA table_info(sessions)c              3  ó8   <"  € T F  qS9   g   K  Vx € K  	  R # 5ir   r   )rB   ÚfieldÚ	availables   & €r   rD   Ú*_session_db_content_sig.<locals>.<genexpr>ê   s   øé € ÐZÑ*C ÐPYÑGY—u’uÓ*Cùs   ƒ�
zmtime-fallbackÚidz ORDER BY idr#   zSELECT z, z FROM sessions)Údigest_sizezutf-8Úbackslashreplaceó    )rK   Ú	with_namer   r/   r¥   r&   r�   ÚhashlibÚhermes_stater©   Úhermes_state_holdersrª   ÚexecuterN   Ú_SESSION_SIGNATURE_FIELDSÚjoinÚblake2bÚupdateÚreprÚencodeÚdigestr(   ÚcloseÚ_settled_mtime)r§   ÚmtimeÚ	cache_keyÚcachedÚconnr·   r©   rª   ÚrowÚfieldsÚ	signatureÚorderÚrowsrÁ   r°   s   &             @r   Ú_session_db_content_sigrÍ   Í   s-  ø€ ô ˜g×'8Ñ'8¸G¿L¹L¸>ÈÐ9NÓ'OÐPÓQ€EÜ�G“€IÜ#×'Ñ'¨	Ó2€FàÒ˜f Q�i¨5Ô0Ø�a�yÐØ�>‰>×ÒØ-2°D¨MÔ˜yÑ)áà€DðÛÝ4Ý9á"Ñ#3°GÓ#<ÈGØ'+°Tô;ˆà'+§|¡|Ð4QÔ'RÓSÑ'R ˜—V�VÑ'RÑSˆ	ß”ÔZÕ*CÓZ—‘ÔZÕ*CÓZÓZˆßð *¨5Ð1‰Ià&*¨iÔ&7‘N¸RˆEØ—<‘< '¨$¯)©)°FÓ*;Ð)<¸NÈ5È'Ð RÓSˆDØ—_‘_°�_Ó4ˆFÛ�Ø—‘œd¤5¨£:Ó.×5Ñ5°gÐ?QÓRÔSØ—‘˜eÖ$ñ ð  §¡£Ð1‰Ið ÒØ�J‰JŒLä)7¸Ó)>À	Ð(JÔ˜9Ñ%àÐùò7 Tøô ô .ð Ò &¨¥)Ò"7¸FÀ1½IÀa½LÐL\Ô<\Ø˜!•9Ñð ÒØ�J‰J�Lð ð & uÐ-Š	ð.ûð ÒØ�J‰J�Lð úsa   Â5G, Ã G'ÃG, Ã6G, ÄG, ÄI ÄB!G, Æ<I Ç'G, Ç,8IÈ$I È;IÈ?I ÉIÉI ÉIc                óB   € ^ RI Hp V e   V! V 4      '       d   \        # V # )zX``mtime`` when it is old enough to trust as a cache key, else a never-matching sentinel.)Úmtime_is_racy)ÚutilsrÏ   Ú_RACY_MTIME)rÄ   rÏ   s   & r   rÃ   rÃ     s   € å#Ø‚}™ e×,Ò,ÜÐØ€Lr   c                 ó–   € \         ;QJ d#    . R \        4       .\        O 4       F  NK  	  5# ! R \        4       .\        O 4       4      # )a[  Session-table content across the active and served profile stores.

Messaging-gateway turns and cron runs are written by other processes that
never touch this gateway's transports, so their session rows are the shared
change signal. Hashing only those rows avoids false Desktop refreshes from
unrelated state.db writes such as gateway heartbeats.
c              3  óF   "  € T F  p\        VR ,          4      x € K  	  R# 5i)zstate.dbN)rÍ   )rB   Úroots   & r   rD   Ú _sessions_sig.<locals>.<genexpr>  s$   é € ð á=ˆDô 	   zÕ 1×2Ð2Û=ùs   ‚!)rN   r2   Ú_served_profile_homesr   r   r   Ú_sessions_sigr×     sL   € ÷ Œ5ñ ä"“_Ð=Ô'<Ñ=ó�5ð ˆ5ñ ä"“_Ð=Ô'<Ñ=óó ð r   c                 óD   € \        R \        4       .\        O 4       4      # )u,  Per-file stat signature of projects.db (+ WAL) for the watcher home and every served
sibling profile. The CLI and other windows write projects.db directly â€” nothing in
their process touches this gateway's transports â€” so the file is the only shared
signal, exactly like state.db (#53046, #56757).c              3  óD   "  € T F  pR F  pW,          x € K  	  K  	  R# 5i)úprojects.dbN)rÚ   zprojects.db-walr   )rB   rÔ   r   s   &  r   rD   Ú _projects_sig.<locals>.<genexpr>)  s(   é € ð 8á=ˆDÛ6ˆDð 	�Šá6ñ 	Û=ùs   ‚ )rW   r2   rÖ   r   r   r   Ú_projects_sigrÜ   $  s)   € ô
 ñ 8ä"“_Ð=Ô'<Ñ=ó8ó 8ð 8r   c            
     óh  € . p \        \        4       4       Fv  p\        P                  \        4      ;_uu_ 4        T VP                  4        Uu. uF'  q"P                  P                  R4      '       g   K%  VNK)  	  up,          p RRR4       Kx  	  \        V 4      # u upi   + '       g   i     K›  ; i)z÷Per-file stat signature of every profile's pairing ledgers (legacy ``pairing/`` and
``platforms/pairing/``): the gateway process writes pending codes, so the files are the only
shared signal (a pairing request moves nothing in gateway_state.json).N)z-pending.jsonz-approved.json)	Ú_pairing_rootsr2   rP   rQ   r8   Úiterdirr   ÚendswithrW   )rU   rÔ   Úes      r   Ú_pairing_sigrâ   /  sŒ   € ð €GÜœ}›Ö/ˆÜ× Ñ ¤×)Õ)àØŸ<™<œ>ócÙ)�a¯V©V¯_©_Ð=`×-a—�™>ñcõ cˆG÷ *Ñ)ñ 0ô
 �gÓÐùòc÷ *×)Ð)ús#   ºB Á"BÁ5BÁ;	B ÂB Â B1g      >@z+tuple[Path, int | None, float, list] | NoneÚ_pairing_roots_cachec               ó    € V ^8„  d   QhRRRR/# )r   re   r   r	   rw   r   )r   s   "r   r   r   D  s   € ÷ ñ ˜ð  $ñ r   c                ó.  € V R ,          p\        V4      \        P                  4       r2\        pVe>   V^ ,          V 8X  d0   V^,          V8X  d"   W4^,          ,
          \        8  d
   V^,          # ^ RIHp V R,          V R,          R,          .p\        P                  \        4      ;_uu_ 4        VP                  4        F2  pV! V4      '       g   K  WgR,          VR,          R,          .,          pK4  	  RRR4       WW63sV#   + '       g   i     L; i)ÚprofilesN)Únamed_profile_is_liveÚpairingÚ	platforms)r9   ÚtimeÚ	monotonicrã   Ú_PAIRING_ROOTS_TTL_SÚhermes_constantsrç   rP   rQ   r8   rß   )re   Úprofiles_dirÚ	dir_mtimeÚnowrÆ   rç   ÚrootsÚprofile_dirs   &       r   rÞ   rÞ   D  sá   € à˜*Õ$€LÜ& |Ó4´d·n±nÓ6FˆsÜ!€FØÒ˜f Q�i¨4Ô/°F¸1µIÀÔ4JÈsÐ\]ÕU^ÍÔauÔOuØ�a�yÐÝ6Ø�IÕ˜t kÕ1°IÕ=Ð>€EÜ	×	Ñ	œW×	%Õ	%Ø'×/Ñ/Ö1ˆKÙ$ [×1Ô1Ø¨	Õ1°;ÀÕ3LÈyÕ3XÐYÕY’ñ 2÷ 
&ð !¨SÐ8ÐØ€L÷ 
&×	%ús   Â/DÃ#DÄD	c            	     óì  € \        4       p V P                  P                  R8X  d   V P                  P                  MT p\        P	                  \
        4      ;_uu_ 4        VR,          R,          P                  4        FI  pVP                  P                  R4      '       g   K&  \        \        \        V4      ;'       g    ^ 4      s	KK  	  RRR4       \        ;'       g    R#   + '       g   i     L ; i)uÄ  Newest mtime across pending bot-relay outbox envelopes (monotone). Written by the AGENT
process, so the files are the only shared signal; the Desktop reacts with a debounced drain.

Envelopes are written by the AGENT process (``message_agent`` â†’ ``tools.bot_relay.enqueue_envelope``) â€”
a different process that never touches this gateway's transports â€” so the files are the only shared
signal, exactly like the pairing store. See #92760, #93091.
ræ   Ú	bot_relayÚoutboxz.jsonN)r2   Úparentr   rP   rQ   r8   rß   rà   rH   Ú_bot_relay_outbox_seenr9   )re   rÔ   Úentrys      r   Ú_bot_relay_outbox_sigrù   Z  s¬   € ô ‹?€DØ!%§¡×!1Ñ!1°ZÔ!?ˆ4�;‰;×ÒÀT€DÜ	×	Ñ	œW×	%Õ	%Ø˜[Õ(¨8Õ3×<Ñ<Ö>ˆEØ�z‰z×"Ñ" 7×+Ô+Ü),Ô-CÔEVÐW\ÓE]×EbÐEbÐabÓ)cÒ&ñ ?÷ 
&ô "×)Ð) TÐ)÷	 
&×	%ús   ÁA C#Â$C#Ã C#Ã#C3	c                 ó   € ^ RI Hp  V ! 4       # )a¨  The skill roots' stat signature for every scope a session resolved commands in (each profile
home and session repo, not only the launch one: ``agent.skill_commands.published_scopes_signature``):
a skill installed or removed from another terminal (``hermes skills install``, a dropped SKILL.md,
nested under a category) moves it, and attached clients refetch ``commands.catalog`` from this
gateway instead of rescanning disk.©Úpublished_scopes_signature)Úagent.skill_commandsrü   rû   s    r   Ú_skills_sigrþ   l  s   € õ @Ù%Ó'Ð'r   zpet.changedg       @zcron.changedg      ð?c                 ó   € \        R R4      # )Úcronz	jobs.json©r>   r   r   r   Ú<lambda>r  |  s   € ¤.°¸Ô"Er   zsessions.changedç      à?zplatforms.changedc                 ó   € \        R 4      # )zgateway_state.jsonr  r   r   r   r  r  �  s
   € ¤~Ð6JÔ'Kr   zprojects.changedzpairing.changedzskills.changedzbot_relay.outbox.pendingz!dict[str, tuple[float, Any, Any]]Ú_CHANGE_WATCHESg      @zdict[str, Any]Ú_change_sigszdict[str, float]Ú_change_checked_atÚ_change_broadcast_atc               ó    € V ^8„  d   QhRRRR/# )r   rð   zfloat | Noner	   rp   r   )r   s   "r   r   r   ’  s   € ÷ 9ñ 9 Lð 9¸Dñ 9r   c                ó¦  € V f   \         P                  4       MT p \        P                  4        Fö  w  pw  r#pV \        P                  W) 4      ,
          V8  d   K-  V \        V&    V! 4       pT\        9  d   T\        T&   KT  \        P                  TR4      pT\        T,          8X  d   K~  T'       d%   T \        P                  Y) 4      ,
          T8  d   Kª  T\        T&   T \        T&   \        P                  \        4      ;_uu_ 4        \        Y! 4       4       RRR4       Kø  	  R#   \         d     EK  i ; i  + '       g   i     EK!  ; i)z˜One pass: recompute due signatures, broadcast events whose signature moved.
First sighting seeds silently so a gateway boot never fires a refresh storm.Ng        )rê   rë   r  r…   r  r&   r(   r  Ú_CHANGE_BROADCAST_FLOOR_Sr  rP   rQ   rƒ   )rð   ÚeventÚintervalÚsig_fnÚ
payload_fnr‰   Úfloors   &      r   Ú_broadcast_watched_changesr  ’  s  € ð "škŒ$�.‰.Ô
¨s€CÜ1@×1FÑ1FÖ1HÑ-ˆÑ-� *ØÔ#×'Ñ'¨¨yÓ9Õ9¸HÔDÙØ$'Ô˜5Ñ!ð	Ù“(ˆCð œÔ$Ø"%ŒL˜ÑÙÜ)×-Ñ-¨e°SÓ9ˆØ”,˜uÕ%Ô%Ùß�SÔ/×3Ñ3°E¸6ÓBÕBÀUÔJÙØ!Œ�UÑØ&)Ô˜UÑ#Ü× Ñ ¤×+Õ+Ü# E¨:«<Ô8÷ ,Ñ+ó% 2Iøô ô 	Ûð	ú÷ ,×+Ñ+ús   Á&D+ÄD>Ä+D;Ä:D;Ä>EFc               ó   € V ^8„  d   QhRR/# ro   r   )r   s   "r   r   r   ¯  s   € ÷ Vñ V˜dñ Vr   c                 ó’   € \         '       d   R# Rs \        4        R R lp \        P                  V RRR7      P	                  4        R# )uµ   Start the process's one change watcher (named for its original skin-only duty): cheap
on-disk signatures â†’ broadcast events, so changes go live without client polling. Idempotent.NTc               ó   € V ^8„  d   QhRR/# ro   r   )r   s   "r   r   Ú*_ensure_skin_watcher.<locals>.__annotate__¸  s   € ÷ )ñ )�4ñ )r   c                 óZ   €  \         P                  R4       \        4        \        4        K,  )Tr  )rê   Úsleepr�   r  r   r   r   Ú_loopÚ#_ensure_skin_watcher.<locals>._loop¸  s   € ØÜ�J‰J�sŒOÜ&Ô(Ü&Ö(r   zhermes-change-watcher)Útargetr   Údaemon)Ú_skin_watcher_startedrr   Ú	threadingÚThreadÚstart)r  s    r   Ú_ensure_skin_watcherr   ¯  s?   € ÷ ÓÙØ ÐÜÔõ)ô
 ×Ñ˜EÐ(?ÈÐÓM×SÑSÖUr   c               ó   € V ^8„  d   QhRR/# ro   r   )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.r�   N)Ú_)r   Úglobals)Úservers   &r   Úregisterr&  À  s   € ä”“	˜6¨×/r   )r²   ÚsourceÚsession_keyr£   ÚmodelÚparent_session_idÚ
started_atÚended_atÚ
end_reasonÚmessage_countÚtool_call_countÚcwdÚ
git_branchÚgit_repo_rootÚtitleÚtitle_sourceÚprofile_nameÚarchivedÚpinnedÚhiddenÚlast_read_atÚhandoff_stater   )6Ú__conditional_annotations__Ú__doc__Ú
__future__r   Úpathlibr   Ú
method_ctxr   r   Ú	_registryr   r   r*   Ú__annotations__r2   r9   r>   rK   rW   r]   r^   rf   rm   rr   r~   r�   r”   rž   r¤   r»   r¥   rÍ   ÚobjectrÑ   rÃ   r×   rÜ   râ   rì   rã   rÞ   r÷   rù   rþ   r
   r  r  r  r  r  r  r  r   r&  )r;  s   @r   Ú<module>rC     s¸  øðôNõ #å ç 4áÓ€	õ%õð, 37€Ð/Ó 6õVõõ?õZõ
"õð :<Ð Ð6Ó ;õõ*õ)õõ&ò0Põõ	ðÐ ð BDÐ Ð>Ó Cõ7ñ| ‹h€òòò8ò
ð" Ð ØDHÐ ÐAÓ Hõð& Ð ò*ò$(ð �C˜Ð#7Ð8Ø�SÑEÀtÐLØ˜˜m¨TÐ2ð ˜#ÑKÈTÐRØ˜˜m¨TÐ2Ø˜˜\¨4Ð0Ø�s˜K¨Ð.à Ð&;¸TÐ Bð6D€Ð2ó Dð" 0°Ð6IÈ3ÐOÐ à!€ˆnÓ !Ø')Ð Ð$Ó )Ø)+Ð Ð&Ó +÷9ð4 Ð õV÷"0r   