+
    lHàjk.  ã                   ó‚  a € R* t#0 t R t^ RIt^ RIt^ RIt^ RIHt ^ RIH	t	 ^ RI
HtHtHt ]P                  ! R4      tR R ltR R	 ltR
 R lt ! R R4      t]P(                  ! RRR7      t] ^ k R R ltR R ltR R ltR R ltR R ltR R ltR R ltR R ltR  R! ltR" R# ltR$ R% lt R& R' lt!R( R) lt"R# )+a  Write/delete guards for ``skill_manage``. Every guard returns ``None`` when the
operation may proceed, else a refusal (error dict or message). Origin-owned state
(``_find_skill``, ``_skills_dir``) is reached lazily via ``tools.skill_manager_tool``
so test patches keep working.N)Úsuppress©ÚPath)ÚAnyÚDictÚOptionalztools.skill_manager_toolc                ó^   € V ^8„  d   QhR\         R\        R\        \         \        3,          /# )é   ÚmessageÚextraÚreturn)Ústrr   Údict)Úformats   "Ú?/home/ubuntu/.hermes/hermes-agent/tools/skill_manager_guards.pyÚ__annotate__r      s)   € ÷ 9ñ 9”cð 9¤Cð 9¬D´´c°­Nñ 9ó    c                 ó   € R RRV /VC# )ÚsuccessFÚerror© )r
   r   s   &,r   Ú_refusalr      s   € Ø�u˜g wÐ8°%Ð8Ð8r   c                ó$   € V ^8„  d   QhR\         /# )r	   r   )Úbool)r   s   "r   r   r      s   € ÷ ñ œtñ r   c                 óT   €  ^ RI Hp  \        V ! 4       4      #   \         d     R# i ; i)zLTrue inside the autonomous curator review fork; False on any lookup failure.©Úis_background_reviewF)Útools.skill_provenancer   r   Ú	Exceptionr   s    r   Ú_is_background_reviewr      s+   € ðÝ?ÜÑ(Ó*Ó+Ð+øÜô Úðús   ‚ ˜'¦'c                ó0   € V ^8„  d   QhR\         R\        /# ©r	   Úpathr   )r   r   )r   s   "r   r   r      s   € ÷ ñ œð ¤ñ r   c                 ó´   € \        \        4      ;_uu_ 4        \        V P                  4       4      uuR R R 4       #   + '       g   i     M; i\        T 4      # ©N)r   r   r   Úresolve©r"   s   &r   Ú_resolved_strr'      s4   € Ü	”)×	Õ	Ü�4—<‘<“>Ó"÷ 
×	×	Ò	úäˆt‹9Ðs	   ˜<¼A	c                   óZ   a € ] tR t^#t o RtV 3R lR ltV 3R lR ltV 3R lR ltRtV t	R	# )
Ú_BackgroundReviewReadMarksz@Read marks shared by copied tool contexts within one review run.c                ó   <€ V ^8„  d   QhRR/# ©r	   r   Nr   )r   Ú__classdict__s   "€r   r   Ú'_BackgroundReviewReadMarks.__annotate__&   s   ø€ ÷ &ñ &˜$ñ &r   c                óX   € \         P                  ! 4       V n        \        4       V n        R # r$   )Ú	threadingÚLockÚ_lockÚsetÚ_paths)Úselfs   &r   Ú__init__Ú#_BackgroundReviewReadMarks.__init__&   s   € Ü—^’^Ó%ˆŒ
Ü #£ˆŽr   c                ó$   <€ V ^8„  d   QhRS[ RR/# ©r	   r"   r   N)r   )r   r,   s   "€r   r   r-   *   s   ø€ ÷ "ñ "™ð " ñ "r   c                óž   € V P                   ;_uu_ 4        V P                  P                  V4       R R R 4       R #   + '       g   i     R # ; ir$   )r1   r3   Úadd©r4   r"   s   &&r   r:   Ú_BackgroundReviewReadMarks.add*   s'   € Ø�Z�Z‹ZØ�K‰K�O‰O˜DÔ!÷ �Z�ZŠZús	   •;»A	c                ó&   <€ V ^8„  d   QhRS[ RS[/# r!   )r   r   )r   r,   s   "€r   r   r-   .   s   ø€ ÷ 'ñ '™Sð '¡Tñ 'r   c                ó„   € V P                   ;_uu_ 4        WP                  9   uuR R R 4       #   + '       g   i     R # ; ir$   ©r1   r3   r;   s   &&r   ÚcontainsÚ#_BackgroundReviewReadMarks.contains.   s!   € Ø�Z�Z‹ZØŸ;™;Ñ&÷ �Z�Z‹Zús   •.®?	r?   N)
Ú__name__Ú
__module__Ú__qualname__Ú__firstlineno__Ú__doc__r5   r:   r@   Ú__static_attributes__Ú__classdictcell__)r,   s   @r   r)   r)   #   s&   ø‡ € ÙJ÷&ð &÷"ð "÷'ö 'r   r)   Úbackground_review_read_paths)Údefaultc                ó(   € V ^8„  d   QhR\         RR/# r8   r   )r   s   "r   r   r   7   s   € ÷ #ñ #¬Dð #°Tñ #r   c                óÌ   € \        4       '       g   R# \        P                  4       ;pf    \        P                  \	        4       ;p4       VP                  \        V 4      4       R# )zÑRecord that the active background-review fork has read a skill file. The fork must not
patch content it only inferred from the transcript: skill_view/read_file call this, and
the write guards require the mark.N)r   Ú_background_review_read_pathsÚgetr2   r)   r:   r'   ©r"   Úmarkss   & r   Ú!mark_background_review_skill_readrQ   7   sL   € ô !×"Ò"ÙÜ.×2Ñ2Ó4Ð4ˆÒ=Ü%×)Ñ)Ô3MÓ3OÐ*O¨%ÔPØ	‡I�IŒm˜DÓ!Ö"r   c                ó0   € V ^8„  d   QhR\         R\        /# r!   ©r   r   )r   s   "r   r   r   B   s   € ÷ Eñ E¤dð E¬tñ Er   c                 óv   € \         P                  4       pVR J;'       d    VP                  \        V 4      4      # r$   )rM   rN   r@   r'   rO   s   & r   Ú_background_review_has_readrU   B   s1   € Ü)×-Ñ-Ó/€EØ˜Ð×DÐD §¡´¸dÓ0CÓ!DÐDr   c                ó   € V ^8„  d   QhRR/# r+   r   )r   s   "r   r   r   G   s   € ÷ Dñ D¨Tñ Dr   c                 ó@   € \         P                  \        4       4       R# )z@Start a fresh, isolated read set for the current review context.N)rM   r2   r)   r   r   r   Ú#_reset_background_review_read_marksrX   G   s   € ä!×%Ñ%Ô&@Ó&BÖCr   c                ó$   € V ^8„  d   QhR\         /# )r	   Ú
skill_pathr   )r   s   "r   r   r   L   s   € ÷ ñ ¤ñ r   c                ó$  € ^ RI Hp  V P                  4       p. pV! 4        FC  p\	        \        4      ;_uu_ 4        VP                  WDP                  4       34       RRR4       KE  	  W#3#   \         d    T p Lbi ; i  + '       g   i     Kn  ; i)zZ``(resolved skill_path, [(root, resolved_root), ...])`` over every resolvable skills root.)Úget_all_skills_dirsN)Úagent.skill_utilsr\   r%   ÚOSErrorr   Úappend)rZ   r\   ÚresolvedÚrootsÚroots   &    r   Ú_resolved_rootsrc   L   s€   € å5ðØ×%Ñ%Ó'ˆð €EÙ#Ö%ˆÜ”g×ÕØ�L‰L˜$§¡£Ð/Ô0÷ Ññ &ð ˆ?Ðøô ô ØŠðú÷ ×Ðús   ˆA+ »!A>Á+A;Á:A;Á>Bc                ó0   € V ^8„  d   QhR\         R\         /# )r	   rZ   r   r   )r   s   "r   r   r   Z   s   € ÷ _ñ _¬ð _´ñ _r   c                ót   a€ ^ RI Hp \        V 4      w  op\        V3R lV 4       VP	                  4       4      # )zXSkills root (local or external_dirs) containing ``skill_path``; local dir if none match.©Úskill_manager_toolc              3   ó^   <"  € T F"  w  rSP                  V4      '       g   K  Vx € K$  	  R # 5ir$   )Úis_relative_to)Ú.0rb   Úrr`   s   &  €r   Ú	<genexpr>Ú*_containing_skills_root.<locals>.<genexpr>^   s"   øé € ÐI¡U™'˜$¨h×.EÑ.EÀa×.H—’£Uùs   ƒ-£
-)Útoolsrg   rc   ÚnextÚ_skills_dir)rZ   Ú_smtra   r`   s   &  @r   Ú_containing_skills_rootrr   Z   s0   ø€ å0Ü% jÓ1�O€HˆeÜÔI¡UÓIÈ4×K[ÑK[ÓK]Ó^Ð^r   c                ó0   € V ^8„  d   QhR\         R\        /# r!   rS   )r   s   "r   r   r   a   s   € ÷ ñ œDð ¤Tñ r   c                ó    €  V P                  4       ;'       g%    \        V R4      ;'       d    V P                  4       #   \         d     R# i ; i)u\   Symlink or (Windows 3.12+) junction â€” either lets a poisoned tree redirect rmtree outside.Úis_junctionF)Ú
is_symlinkÚhasattrru   r^   r&   s   &r   Ú_is_path_redirectrx   a   sF   € ðØ�‰Ó ×YÐY¤W¨T°=Ó%A×%XÐ%XÀd×FVÑFVÓFXÐYøÜô Úðús   ‚> ™> ­> ¾AÁAc                óF   € V ^8„  d   QhR\         R\        \        ,          /# )r	   Ú	skill_dirr   )r   r   r   )r   s   "r   r   r   i   s"   € ÷ dñ d¤tð d´¼µñ dr   c                ó   € \        V 4      '       d   RV  R2#  V P                  4        \        T 4      w  r#T F-  w  rEY%8X  d	   RT  R2u # TP	                  T4      '       g   K,   R# 	  RT  R2#   \         d   pRT  RT R2u Rp?# Rp?ii ; i)aÍ  Last-line guard before rmtree: even a poisoned tree must never delete (1) a path outside
every known skills root, (2) a skills root itself, (3) a symlink/junction (rmtree follows it).

``_find_skill`` already restricts ``skill_dir`` to a real ``SKILL.md`` parent discovered by walking the
skills roots, so the agent cannot inject an arbitrary path the way Kilo Code's HTTP endpoint could
(their issue 11227: a built-in-skill sentinel resolved to the server cwd and a recursive delete wiped
the user's entire working directory). This is the matching defense-in-depth for our agent-facing
``skill_manage`` delete path: even if discovery or a poisoned tree hands us a bad directory, never
recursively delete See #11227.
zRefusing to delete 'zZ': the skill directory is a symlink/junction. Remove the link target manually if intended.z': could not resolve path (z).NzP': resolves to the skills root itself, which would remove every installed skill.z6': path does not resolve inside any known skills root.)rx   r%   r^   rc   ri   )rz   Úexcr`   ra   Ú_rootrb   s   &     r   Ú_validate_delete_targetr~   i   sÄ   € ô ˜×#Ò#Ø& y kð 2Qð Rð 	SðTØ×ÑÔô & iÓ0�O€HÛ‰ˆØÔØ*¨9¨+ð 6Hð Iò Jà×"Ñ" 4×(Ô(Úñ ð " ) Ð,bÐcÐcøô ô TØ% i [Ð0KÈCÈ5ÐPRÐSÕSûðTús   ™A/ Á/BÁ:BÂBÂBc                óR   € V ^8„  d   QhR\         R\         R\        \        ,          /# )r	   ÚnameÚwhatr   )r   r   r   )r   s   "r   r   r   …   s%   € ÷ ñ ”Sð ¤ð ¬´­ñ r   c                ó¶   €  ^ RI Hp \        VP                  V 4      P	                  R4      4      #   \
         d    \        P                  RYRR7        R# i ; i)zNskill_usage pinned flag; None (logged at debug) when the record is unreadable.©Úskill_usageÚpinnedz%s lookup failed for %sT©Úexc_infoN)rn   r„   r   Ú
get_recordrN   r   ÚloggerÚdebug)r€   r�   r„   s   && r   Ú
_is_pinnedr‹   …   sP   € ðÝ%Ü�K×*Ñ*¨4Ó0×4Ñ4°XÓ>Ó?Ð?øÜô Ü�‰Ð.°ÀTˆÔJÚðús   ‚.1 ±#AÁAc                óF   € V ^8„  d   QhR\         R\        \         ,          /# )r	   r€   r   )r   r   )r   s   "r   r   r   �   s   € ÷ ñ œð ¤¬¥ñ r   c                ó¶   €  ^ RI Hp W9   d   RV  R2#  \        V R4      '       d
   RV  RV  R	2# R
#   \         d    \        P	                  RT RR7        LBi ; i)zûRefusal message if *name* is pinned or essential, else None. Pin only guards DELETION;
patches/edits stay allowed. ESSENTIAL_SKILLS are permanently pinned (the system prompt
references them). Best-effort: an unreadable sidecar lets the delete through.)ÚESSENTIAL_SKILLSzSkill 'z—' is essential to Hermes (the agent's own operating manual referenced by the system prompt) and cannot be deleted. Patches and edits are still allowed.z$essential-guard lookup failed for %sTr†   zpinned-guardz]' is pinned and cannot be deleted by skill_manage. Ask the user to run `hermes curator unpin zf` if they want to delete it. Patches and edits are allowed on pinned skills; only deletion is blocked.N)r]   rŽ   r   r‰   rŠ   r‹   )r€   rŽ   s   & r   Ú_pinned_guardr�   �   sŒ   € ðRÝ6ØÔ#à˜$˜ð  Jð KðLð $ô �$˜×'Ò'à�d�Vð )Ø)-¨ð /FðGð	Hñ øô ô RÜ�‰Ð;¸TÈDˆÖQðRús   ‚2 ²#AÁAc          	      ót   € V ^8„  d   QhR\         R\        R\        \        \         \        3,          ,          /# )r	   r€   rz   r   ©r   r   r   r   r   )r   s   "r   r   r   ¤   s1   € ÷ ,ñ ,¬#ð ,¼$ð ,Ä8ÌDÔQTÔVYÐQYÍNÕC[ñ ,r   c           
     ó>  € \        4       '       g   R# Rp\        V R4      '       d   \        V RV  RV  R24      #  ^ RIHp V! V4      '       d   \        V RV  R	24      #   ^ RIH	p VP                  R3VP                  R3VP                  R33 F)  w  rVV! V 4      '       g   K  \        V RV RV  R24      u # 	  VP                  4       P                  V 4      pVP                  V4      '       gE   \!        V\"        4      '       d   RVP                  R4      : 2MRp\        V RV  RV RV  R24      #  R#   \
         d    \        P                  R
T RR7        Lÿi ; i  \
         d-    \        P%                  RT RR7       \        T RT  R24      u # i ; i)a  Refuse autonomous deletes of anything but curator-owned sediment. Content writes are not
gated by ownership: the review fork exists to improve every skill it learns from, and every
write is ledgered and reversible, while an archive removes a skill the user relies on.Nz&Refusing background curator delete forzpinned skill guardz pinned skill 'ze': pinned skills are off-limits to autonomous maintenance. Ask the user to run `hermes curator unpin z` if they want it changed.)Úis_external_skill_pathz skill 'zl': the skill lives in skills.external_dirs, which are externally owned and read-only to autonomous curation.z)external skill guard lookup failed for %sTr†   rƒ   zprotected built-inzhub-installedÚbundledÚ z'.zcreated_by=Ú
created_byzno usage recordz%': the skill is not curator-managed (zW). Only curator-managed skills may be archived autonomously. Run `hermes curator adopt z` to opt it in.z&owned skill guard lookup failed for %szd': agent ownership could not be verified because the provenance record is unavailable or unreadable.)r   r‹   r   r]   r“   r   r‰   rŠ   rn   r„   Úis_protected_builtinÚis_hub_installedÚ
is_bundledÚ
load_usagerN   Ú_is_curator_managed_recordÚ
isinstancer   Úwarning)	r€   rz   Úrefuser“   r„   Ú	predicateÚlabelÚ	usage_recÚ_details	   &&       r   Ú_background_review_delete_guardr£   ¤   s  € ô !×"Ò"ÙØ5€FÜ�$Ð,×-Ò-ÜØˆh�o d Vð ,à�FÐ4ð6ó7ð 	7ðWÝ<Ù! )×,Ò,ÜØ�(˜( 4 &ð )Ið JóKð Kð -ðXÝ%à×-Ñ-Ð/CÐDØ×)Ñ)¨?Ð;Ø×#Ñ# YÐ/ó!1ÑˆIñ ˜�ŒÜ 6 (¨!¨E¨7°(¸4¸&ÀÐ CÓDÒDñ!1ð  ×*Ñ*Ó,×0Ñ0°Ó6ˆ	Ø×5Ñ5°i×@Ò@ÜHRÐS\Ô^b×HcÒHc˜ Y§]¡]°<Ó%@Ñ$CÑDØ-ð äØ�(˜( 4 &ð )$Ø$+ 9ð -;Ø;?¸&ÀðQóRð Rð Añ øô5 ô WÜ�‰Ð@À$ÐQUˆÖVðWûô* ô XÜ�‰Ð?ÀÐPTˆÔUÜØˆh�h˜t˜fð %Vð WóXò 	XðXús<   ¼D< ÁD< Á#?E% Â'E% Â=7E% Ã5AE% Ä<#E"Å!E"Å%4FÆFc                óŒ   € V ^8„  d   QhR\         R\        R\         R\         R\        \        \         \        3,          ,          /# )r	   r€   ÚtargetÚactionÚ
file_labelr   r‘   )r   s   "r   r   r   Ó   sB   € ÷ 
*ñ 
*Ü
ð
*Üð
*Ü%(ð
*Ü69ð
*Ü>FÄtÌCÔQTÈHÅ~Õ>Vñ
*r   c           	     óv   € \        4       '       d   \        V4      '       d   R# \        RV RV  RV R2RR7      # )zARequire review forks to load the exact target before mutating it.NzRefusing background curator z for skill 'z': the current zÅ content has not been loaded in this review turn. Call skill_view(name) for SKILL.md, or skill_view(name, file_path=...) for a supporting file, then retry the write using the content just returned.T)Ú_read_before_write_required)r   rU   r   )r€   r¥   r¦   r§   s   &&&&r   Ú*_background_review_read_before_write_guardrª   Ó   sL   € ô !×"Ò"Ô&AÀ&×&IÒ&IÙÜØ
& v h¨l¸4¸&ÀÐPZÈ|ð \!ð 	"ð %)ô*ð *r   c          	      ót   € V ^8„  d   QhR\         R\         R\        \        \         \        3,          ,          /# )r	   r¦   r€   r   ©r   r   r   r   )r   s   "r   r   r   à   s3   € ÷ Yñ Y¬ð Y´Cð Y¼HÄTÌ#ÌsÈ(Å^Õ<Tñ Yr   c                 óz   € V R 8w  d   R# ^ RI Hp VP                  V4      pV'       d   \        WR,          4      # R# )ÚdeleteNrf   r"   )rn   rg   Ú_find_skillr£   )r¦   r€   rq   Úexistings   &&  r   Ú_background_review_preflightr±   à   s9   € Ø�ÔÙÝ0Ø×Ñ Ó%€HßFNÔ*¨4¸&Õ1AÓBÐXÐTXÐXr   c          	      óŠ   € V ^8„  d   QhR\         R\        \         ,          R\        \        \         \        3,          ,          /# )r	   r€   Úabsorbed_intor   r¬   )r   s   "r   r   r   è   s5   € ÷ ñ Ü
ðÜ&¤s�mðÜ08¼¼cÄ3¸h½Õ0Hñr   c                ó¦   € \        4       '       d-   \        V\        4      '       d   VP                  4       '       d   R# \	        RV  RV  R2RR7      # )uÖ  Fail closed on unverified deletes during the curator consolidation pass. The fork's only
legitimate delete is a consolidation declared via ``absorbed_into=<umbrella>`` (existence
validated in ``_delete_skill``); the deterministic inactivity prune never calls skill_manage,
so a bare delete here can only be the LLM pass pruning without evidence.

A delete with no forwarding target â€” ``absorbed_into`` omitted (``None``) or empty (``""``) â€” is the
fail-open behavior reported in #29912: the consolidation pass archived whole clusters of active skills
with zero verified consolidations (``consolidated_this_run == 0``), leaving active automations pointing
at names that no longer resolve. Refuse it; keep the skill active.
Nz-Refusing background curator delete of skill 'uP  ': the consolidation pass may only archive a skill it has absorbed into an umbrella. Pass absorbed_into=<umbrella> (the umbrella must already exist) to record a verified consolidation. Pruning a skill with no forwarding target is not permitted here â€” the deterministic inactivity prune handles staleness archival separately. Keeping 'z	' active.T)Ú_fail_closed)r   rœ   r   Ústripr   )r€   r³   s   &&r   Ú#_curator_consolidation_delete_guardr·   è   sW   € ô !×"Ò"¤z°-Ä×'EÒ'EÈ-×J]ÑJ]×J_ÒJ_ÙÜØ
7¸°vð >3ð 48°&¸	ð		Cð
 ôð r   c                ó€   € V ^8„  d   Qh/ ^ \         9   d*   \        P                  \        \        ,          ,          ;R&   # )r	   rM   )Ú__conditional_annotations__Ú_ctxvarsÚ
ContextVarr   r)   )r   s   "r   r   r      s8   € ÷ !ñ !÷dGò Gœx×2Ñ2´8Ô<VÕ3WÕXñ Gòe!r   )$r¹   rF   Úcontextvarsrº   Úloggingr/   Ú
contextlibr   Úpathlibr   Útypingr   r   r   Ú	getLoggerr‰   r   r   r'   r)   r»   rM   rQ   rU   rX   rc   rr   rx   r~   r‹   r�   r£   rª   r±   r·   r   )r¹   s   @r   Ú<module>rÂ      s±   øðö!ó
 Û Û Ý Ý ß &Ñ &à	×	Ò	Ð5Ó	6€õ9õõ÷'ñ 'ð" ×ÒÐ6ÀÔEð ó Gõ#õEõ
Dõ
õ_õõdõ8õõ*,õ^
*õY÷r   