+
    lHàj�(  ã                  óP  € R t ^ RIHt ^ RIt^ RIt^ RIt^ RIHt ^ RIH	t	 ^ RI
Ht ^ RIHtHtHtHtHt ]]]]3,          tRtRtR R	 ltR
 R ltR R ltR R ltR R lt ! R R4      t]! 4       tR R ltR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$Ot&R# )%a   Cross-agent file state coordination.

Prevents mangled edits when concurrent subagents (same process, same
filesystem) touch the same file: B writes a file A already read, so A's next
write would clobber B's changes. Complements the single-agent path-overlap
check in ``agent.tool_dispatch_helpers._should_parallelize_tool_batch``. A process-wide
``FileStateRegistry`` tracks per-agent read stamps, the global last writer and
a per-path lock; every method is a no-op under ``HERMES_DISABLE_FILE_STATE_GUARD=1``.
)ÚannotationsN)Údefaultdict)Úcontextmanager)ÚPath)ÚDictÚIterableÚListÚOptionalÚTuplei   c               ó   € V ^8„  d   QhRR/# ©é   ÚreturnÚbool© )Úformats   "Ú5/home/ubuntu/.hermes/hermes-agent/tools/file_state.pyÚ__annotate__r      s   € ÷ Pñ P�4ñ Pó    c                 ód   € \         P                  P                  R R4      P                  4       R8H  # )ÚHERMES_DISABLE_FILE_STATE_GUARDÚ Ú1)ÚosÚenvironÚgetÚstripr   r   r   Ú	_disabledr      s&   € ä�:‰:�>‰>Ð;¸RÓ@×FÑFÓHÈCÑOÐOr   c               ó   € V ^8„  d   QhRR/# r   r   )r   s   "r   r   r   #   s   € ÷ ñ ˜ñ r   c                 ó   € \        4       # )z�True when the user switched the read-before-write guard off; the file
tools then warn instead of refusing stale/unread write_file overwrites.)r   r   r   r   Úguard_disabledr    #   s   € ô ‹;Ðr   c               ó    € V ^8„  d   QhRRRR/# )r   ÚresolvedÚstrr   úOptional[float]r   )r   s   "r   r   r   )   s   € ÷ ñ ˜Sð  _ñ r   c                óf   €  \         P                  P                  V 4      #   \         d     R # i ; i©N)r   ÚpathÚgetmtimeÚOSError)r"   s   &r   Ú_mtime_or_noner*   )   s.   € ðÜ�w‰w×Ñ Ó)Ð)øÜô Úðús   ‚! ¡0¯0c               ó    € V ^8„  d   QhRRRR/# )r   ÚtsÚfloatr   r#   r   )r   s   "r   r   r   0   s   € ÷ 9ñ 9�ð 9˜#ñ 9r   c                óX   € \         P                  ! R \         P                  ! V 4      4      # )z%H:%M:%S)ÚtimeÚstrftimeÚ	localtime)r,   s   &r   Ú_fmt_tsr2   0   s   € ä�=Š=˜¤T§^¢^°BÓ%7Ó8Ð8r   c               ó    € V ^8„  d   QhRRRR/# )r   ÚcapÚintr   ÚNoner   )r   s   "r   r   r   5   s   € ÷ 
ñ 
 #ð 
¨$ñ 
r   c                ó  € \        \        V 4      V,
          4       FO  p \        V \        4      '       d   V P	                  4        K,  V P	                  \        \        V 4      4      4       KQ  	  R#   \        \        3 d      R# i ; i)z£Pop entries until *container* is within *cap* (sets: arbitrary; dicts: oldest
by insertion order). An eviction only costs one redundant re-send or staleness check.N)	ÚrangeÚlenÚ
isinstanceÚsetÚpopÚnextÚiterÚStopIterationÚKeyError)Ú	containerr4   Ú_s   && r   Ú_evict_oldestrC   5   se   € ô ”3�y“> CÕ'Ö(ˆð	Ü˜)¤S×)Ò)Ø—‘–à—‘œd¤4¨	£?Ó3Ö4ó )øô œxÐ(ô 	Ûð	ús    &A1Á#A1Á1BÂBc                  ó°   € ] tR t^BtRtR R lt]R R l4       tR R ltRR	R
R/R R llt	R
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tR# )ÚFileStateRegistryz4Process-wide coordinator for cross-agent file edits.c               ó   € V ^8„  d   QhRR/# ©r   r   r6   r   )r   s   "r   r   ÚFileStateRegistry.__annotate__E   s   € ÷ ,ñ ,˜$ñ ,r   c                	óÀ   € \        \        4      V n        / V n        / V n        / V n        \        P                  ! 4       V n        \        P                  ! 4       V n	        R # r&   )
r   ÚdictÚ_readsÚ_last_writerÚ_path_locksÚ_path_lock_usersÚ	threadingÚLockÚ
_meta_lockÚ_state_lock©Úselfs   &r   Ú__init__ÚFileStateRegistry.__init__E   sC   € Ü7BÄ4Ó7HˆŒØ:<ˆÔØ68ˆÔØ02ˆÔÜ#Ÿ.š.Ó*ˆŒÜ$Ÿ>š>Ó+ˆÖr   c               ó   € V ^8„  d   QhRR/# )r   r"   r#   r   )r   s   "r   r   rH   N   s   € ÷ 9ñ 9 #ñ 9r   c           	   #  ó  "  € V P                   ;_uu_ 4        V P                  P                  V\        P                  ! 4       4      pV P
                  P                  V^ 4      ^,           V P
                  V&   RRR4       XP                  4         Rx € VP                  4        V P                   ;_uu_ 4        V P
                  V,          ^,
          pV'       d   W0P
                  V&   M8V P
                  P                  VR4       V P                  P                  VR4       RRR4       R#   + '       g   i     L½; i  + '       g   i     R# ; i  TP                  4        T P                   ;_uu_ 4        T P
                  T,          ^,
          pT'       d   Y0P
                  T&   M8T P
                  P                  TR4       T P                  P                  TR4       RRR4       i   + '       g   i     i ; i; i5i)zˆPer-path lock: threads on the same path serialize, different paths proceed.
The lock entry is dropped once the last holder/waiter exits.N)
rQ   rM   Ú
setdefaultrO   rP   rN   r   ÚacquireÚreleaser<   )rT   r"   ÚlockÚuserss   &&  r   Ú	lock_pathÚFileStateRegistry.lock_pathM   s_  é € ð �_�_‹_Ø×#Ñ#×.Ñ.¨x¼¿ºÓ9IÓJˆDØ.2×.CÑ.C×.GÑ.GÈÐRSÓ.TÐWXÕ.XˆD×!Ñ! (Ñ+÷ ð 	�‰Œð
	9Ûà�L‰LŒNØ——“Ø×-Ñ-¨hÕ7¸!Õ;�ßØ6;×)Ñ)¨(Ò3à×)Ñ)×-Ñ-¨h¸Ô=Ø×$Ñ$×(Ñ(¨°4Ô8÷ !‘÷ �_ú÷ !—�ûð �L‰LŒNØ——“Ø×-Ñ-¨hÕ7¸!Õ;�ßØ6;×)Ñ)¨(Ò3à×)Ñ)×-Ñ-¨h¸Ô=Ø×$Ñ$×(Ñ(¨°4Ô8÷ !——�ÿsg   ‚H—A D,Á7HÂE Â$HÂ8A*D?Ä"
HÄ,D<	Ä7HÄ?E	Å
	HÅ%G>Å8A*G+	Ç"	G>Ç+G;Ç6G>Ç>Hc               ó0   € V ^8„  d   QhRRRRRRRRRRR	R
/# )r   Útask_idr#   r"   Úmtimer-   ÚnowÚpartialr   r   r6   r   )r   s   "r   r   rH   a   s8   € ÷ 9ñ 9˜cð 9¨Sð 9¸ð 9ÀUð 9ÐUYð 9Ð^bñ 9r   c                ó~   € V P                   V,          p\        V4      V\        V4      3Wb&   \        V\        4       R# )zCaller holds ``_state_lock``.N)rK   r-   r   rC   Ú_MAX_PATHS_PER_AGENT)rT   ra   r"   rb   rc   rd   Úagent_readss   &&&&&& r   Ú_stampÚFileStateRegistry._stampa   s2   € à—k‘k 'Õ*ˆÜ!& u£¨s´D¸³MÐ BˆÑÜ�kÔ#7Ö8r   rd   Frb   Nc          
     ó,   € V ^8„  d   QhRRRRRRRRRR	/# )
r   ra   r#   r"   rd   r   rb   r$   r   r6   r   )r   s   "r   r   rH   g   s8   € ÷ Hñ H 3ð H°#ð HÀ4ð HØ*ðHØ6:ñHr   c          	     	ó  € \        4       '       d   R # Vf   \        V4      MTpVf   R # V P                  ;_uu_ 4        V P                  WV\        P                  ! 4       V4       R R R 4       R #   + '       g   i     R # ; ir&   )r   r*   rR   rh   r/   )rT   ra   r"   rd   rb   s   &&&$$r   Úrecord_readÚFileStateRegistry.record_readg   sW   € ä�;Š;ÙØ,1ªM”˜xÔ(¸uˆØŠ=ÙØ××ÓØ�K‰K˜¨5´$·)²)³+¸wÔG÷ ××Òús   ¾(A0Á0B	c               ó(   € V ^8„  d   QhRRRRRRRR/# )r   ra   r#   r"   rb   r$   r   r6   r   )r   s   "r   r   rH   q   s)   € ÷ >ñ > #ð >°ð >Àð >Ð[_ñ >r   c          	     ó^  € \        4       '       d   R# Vf   \        V4      MTpVf   R# \        P                  ! 4       pV P                  ;_uu_ 4        W3V P                  V&   \        V P                  \        4       V P                  WW4R4       RRR4       R#   + '       g   i     R# ; i)zƒRecord a successful write: global last-writer AND this agent's own
read stamp (a write is an implicit read of the current content).NF)r   r*   r/   rR   rL   rC   Ú_MAX_GLOBAL_WRITERSrh   )rT   ra   r"   rb   rc   s   &&&$ r   Ú
note_writeÚFileStateRegistry.note_writeq   s   € ô �;Š;ÙØ,1ªM”˜xÔ(¸uˆØŠ=ÙÜ�iŠi‹kˆØ××ÓØ+2¨.ˆD×Ñ˜hÑ'Ü˜$×+Ñ+Ô-@ÔAØ�K‰K˜¨5°uÔ=÷ ××Òús   Á>BÂB,	c               ó$   € V ^8„  d   QhRRRRRR/# )r   ra   r#   r"   r   úOptional[str]r   )r   s   "r   r   rH      s&   € ÷ 0Fñ 0F 3ð 0F°#ð 0F¸-ñ 0Fr   c           	     ó.  € \        4       '       d   R# V P                  ;_uu_ 4        V P                  P                  V/ 4      P                  V4      pV P                  P                  V4      pRRR4       Xf   Xf   R# \        V4      pVf   R# XeH   Vw  rgWa8w  d>   Vf
   V RV: R2# V^,          pWx8”  d"   V RV: R\        V4       R\        V4       R2# Ve    Vw  ršpWY8w  d   V R2# V'       d   V R2# R# V R	2#   + '       g   i     Lž; i)
z™Model-facing warning if this write would be stale, else ``None``. Severity
order: sibling wrote after our read > mtime drift / partial read > never read.Nz" was modified by sibling subagent zg but this agent never read it. Read the file before writing to avoid overwriting the sibling's changes.z at u%    â€” after this agent's last read at z". Re-read the file before writing.zs was modified since you last read it on disk (external edit or unrecorded writer). Re-read the file before writing.zz was last read with offset/limit pagination (partial view). Read the remaining pages, or use patch, before overwriting it.zS was not read by this agent. Read the file first so you can write an informed edit.)r   rR   rK   r   rL   r*   r2   )rT   ra   r"   ÚstampÚlast_writerÚcurrent_mtimeÚ
writer_tidÚ	writer_tsÚread_tsÚ
read_mtimeÚ_read_tsrd   s   &&&         r   Úcheck_staleÚFileStateRegistry.check_stale   sc  € ô �;Š;ÙØ××ÓØ—K‘K—O‘O G¨RÓ0×4Ñ4°XÓ>ˆEØ×+Ñ+×/Ñ/°Ó9ˆK÷ ð Š=˜[Ò0ÙÜ& xÓ0ˆØÒ ÙàÒ"Ø$/Ñ!ˆJØÔ$Ø’=à#˜*Ð$FØ%™.ð )1ð1ð2ð
   �(�ØÔ&à#˜*Ð$FØ%™.¨¬W°YÓ-?Ð,@ð A5Ü5<¸WÓ5EÐ4Fð G;ð;ð<ð ÒØ,1Ñ)ˆJ 'ØÔ*à�jð !7ð 7ð8÷ à�jð !-ð -ð.ñ ð ˆjð Eð Eð	F÷S ×ús   §ADÄD	c               ó(   € V ^8„  d   QhRRRRRRRR/# )	r   Úexclude_task_idr#   Úsince_tsr-   ÚpathszIterable[str]r   údict[str, list[str]]r   )r   s   "r   r   rH   ±   s*   € ÷ ñ ¨Cð ¸5ð Ø)ðØ.Bñr   c                óz  € \        4       '       d   / # \        V4      p\        \        4      pV P                  ;_uu_ 4        V P
                  P                  4        F7  w  pw  rxWq8w  g   K  W‚8¼  g   K  Wd9   g   K   WW,          P                  V4       K9  	  RRR4       \        V4      #   + '       g   i     L; i)z®``{writer_task_id: [paths]}`` for writes after ``since_ts`` by agents
other than ``exclude_task_id`` (delegate_task's "subagent modified files
you previously read" reminder).N)	r   r;   r   ÚlistrR   rL   ÚitemsÚappendrJ   )	rT   r�   r‚   rƒ   Ú	paths_setÚoutÚpry   r,   s	   &&&&     r   Úwrites_sinceÚFileStateRegistry.writes_since±   s‰   € ô
 �;Š;ØˆIÜ˜“Jˆ	Ü$/´Ó$5ˆØ××ÓØ'+×'8Ñ'8×'>Ñ'>Ö'@Ñ#�Ñ#�JØÖ0°R¶^ÈÎØ•O×*Ñ*¨1Ö-ñ (A÷ ô �C‹yÐ÷	 ×ús   Á&B*Á,B*Á4B*Á<B*Â*B:	c               ó    € V ^8„  d   QhRRRR/# ©r   ra   r#   r   z	list[str]r   )r   s   "r   r   rH   À   s   € ÷ =ñ = 3ð =¨9ñ =r   c                óò   € \        4       '       d   . # V P                  ;_uu_ 4        \        V P                  P	                  V/ 4      P                  4       4      uuRRR4       #   + '       g   i     R# ; i)z#Resolved paths this agent has read.N)r   rR   r†   rK   r   Úkeys)rT   ra   s   &&r   Úknown_readsÚFileStateRegistry.known_readsÀ   sI   € ä�;Š;ØˆIØ××ÓÜ˜Ÿ™Ÿ™¨°Ó4×9Ñ9Ó;Ó<÷ ××Óús   §3A%Á%A6	c               ó    € V ^8„  d   QhRRRR/# )r   ra   r#   r   r6   r   )r   s   "r   r   rH   Ç   s   € ÷ 	)ñ 	) 3ð 	)¨4ñ 	)r   c           	     óL  € V P                   ;_uu_ 4        V P                  P                  VR4       V P                  P	                  4        UUUu. uF  w  pw  r4W18X  g   K  VNK  	  uppp F  pV P                  V K  	  RRR4       R# u upppi   + '       g   i     R# ; i)aK  Release read stamps and writer claims owned by a task after its lifecycle ends.

A finished task is not a concurrent sibling: leaving its writer claims behind makes
the next run of the same job (a fresh ``cron:<job>:<uuid>`` id) refuse to write the
same scratch path as "modified by sibling subagent" hours after the writer exited.N)rR   rK   r<   rL   r‡   )rT   ra   r‹   ry   Ú_tss   &&   r   Úforget_taskÚFileStateRegistry.forget_taskÇ   s‚   € ð ××ÓØ�K‰K�O‰O˜G TÔ*Ø48×4EÑ4E×4KÑ4KÔ4MÕgÑ4MÑ0˜AÑ0 
ÐQ[ÑQf—a�aÑ4MÖg�Ø×%Ñ% aÒ(ñ h÷ Ñùäg÷ ×Ðús#   •;BÁBÁ"BÁ(BÂBÂB#	c               ó   € V ^8„  d   QhRR/# rG   r   )r   s   "r   r   rH   Ò   s   € ÷ *ñ *�tñ *r   c                ó˜  € V P                   ;_uu_ 4        V P                  P                  4        V P                  P                  4        RRR4       V P                  ;_uu_ 4        V P
                  P                  4        V P                  P                  4        RRR4       R#   + '       g   i     Lc; i  + '       g   i     R# ; i)z)Reset all state. Intended for tests only.N)rR   rK   ÚclearrL   rQ   rM   rN   rS   s   &r   r›   ÚFileStateRegistry.clearÒ   s~   € à××ÓØ�K‰K×ÑÔØ×Ñ×#Ñ#Ô%÷ ð �_�_‹_Ø×Ñ×"Ñ"Ô$Ø×!Ñ!×'Ñ'Ô)÷ ‰_÷ ×ú÷ �_ˆ_ús   •5B%Á&5B8Â%B5	Â8C		)rL   rQ   rN   rM   rK   rR   )Ú__name__Ú
__module__Ú__qualname__Ú__firstlineno__Ú__doc__rU   r   r^   rh   rl   rq   r~   rŒ   r’   r—   r›   Ú__static_attributes__r   r   r   rE   rE   B   sg   † Ù>õ,ð ô9ó ð9õ&9ðHÈ%ð HØ-1÷Hð>ÐRV÷ >õ0Fõdõ=õ	)÷*ñ *r   rE   c               ó   € V ^8„  d   QhRR/# )r   r   rE   r   )r   s   "r   r   r   ß   s   € ÷ ñ Ð'ñ r   c                 ó   € \         # r&   )Ú	_registryr   r   r   Úget_registryr¦   ß   s   € ÜÐr   rd   Fc               ó(   € V ^8„  d   QhRRRRRRRR/# )	r   ra   r#   Úresolved_or_pathú
str | Pathrd   r   r   r6   r   )r   s   "r   r   r   ä   s/   € ÷ Kñ K˜ð K°
ð KÈð KÐY]ñ Kr   c               óH   € \         P                  V \        V4      VR 7       R# ))rd   N)r¥   rl   r#   )ra   r¨   rd   s   &&$r   rl   rl   ä   s   € Ü×Ñ˜'¤3Ð'7Ó#8À'ÐÖJr   c               ó$   € V ^8„  d   QhRRRRRR/# )r   ra   r#   r¨   r©   r   r6   r   )r   s   "r   r   r   è   s!   € ÷ 9ñ 9˜ð 9¨zð 9¸dñ 9r   c                óD   € \         P                  V \        V4      4       R # r&   )r¥   rq   r#   ©ra   r¨   s   &&r   rq   rq   è   s   € Ü×Ñ˜¤#Ð&6Ó"7Ö8r   c               ó$   € V ^8„  d   QhRRRRRR/# )r   ra   r#   r¨   r©   r   rt   r   )r   s   "r   r   r   ì   s&   € ÷ Añ A˜ð A°
ð A¸}ñ Ar   c                ó@   € \         P                  V \        V4      4      # r&   )r¥   r~   r#   r­   s   &&r   r~   r~   ì   s   € Ü× Ñ  ¬#Ð.>Ó*?Ó@Ð@r   c               ó   € V ^8„  d   QhRR/# )r   r¨   r©   r   )r   s   "r   r   r   ð   s   € ÷ 6ñ 6 
ñ 6r   c                ó>   € \         P                  \        V 4      4      # r&   )r¥   r^   r#   )r¨   s   &r   r^   r^   ð   s   € Ü×ÑœsÐ#3Ó4Ó5Ð5r   c               ó(   € V ^8„  d   QhRRRRRRRR/# )	r   r�   r#   r‚   r-   rƒ   zIterable[str | Path]r   r„   r   )r   s   "r   r   r   ô   s0   € ÷ Vñ V #ð V°ð VÐ?Sð VÐXlñ Vr   c           
     ój   € \         P                  YV Uu. uF  p\        V4      NK  	  up4      # u upi r&   )r¥   rŒ   r#   )r�   r‚   rƒ   r‹   s   &&& r   rŒ   rŒ   ô   s+   € Ü×!Ñ! /ÉeÓ=TÉeÈ¼cÀ!¾fÉeÑ=TÓUÐUùÒ=Ts   •0c               ó    € V ^8„  d   QhRRRR/# r�   r   )r   s   "r   r   r   ø   s   € ÷ *ñ *˜ð * ñ *r   c                ó,   € \         P                  V 4      # r&   )r¥   r’   )ra   s   &r   r’   r’   ø   s   € Ü× Ñ  Ó)Ð)r   )rE   r~   r¦   r’   r^   rq   rl   rŒ   )'r¡   Ú
__future__r   r   rO   r/   Úcollectionsr   Ú
contextlibr   Úpathlibr   Útypingr   r   r   r	   r
   Útupler-   r   Ú	ReadStamprf   rp   r   r    r*   r2   rC   rE   r¥   r¦   rl   rq   r~   r^   rŒ   r’   Ú__all__r   r   r   Ú<module>r¾      s®   ðñõ #ã 	Û Û Ý #Ý %Ý ß 8Õ 8ð
 �%˜ Ð$Õ%€	ð Ð ØÐ õPõ
õõ9õ

÷W*ñ W*ñt Ó€	õð
KÈu÷ Kõ9õAõ6õVõ*ò	‚r   