+
    iHàjS	 ã            
      ó|  a € 0 t $ 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Ht ^ RIHt ^ RIHtHtHtHt ^ RIHt ^ RIHtHtHtHtHtHtHt ^ RIHt ^ R	IH t  ^ R
I!H"t" ^ RI#H$t$ ]PJ                  ! ]&4      t'^¨^ut(t)^^ut*t+Rt,R R lt-R R lt.R R lt/R R lt0R R lt1R R lt2R]'/R R llt3R R lt4R R lt5R  R! lt6R" R# lt7R$ R% lt8]9! 4       t:R&];R'&   R( R) lt<R* R+ lt=R, R- lt>R. R/ lt?R0 R1 lt@RšR2 R3 lltAR4 R5 ltBR6 R7 ltCRšR8 R9 lltDR:tER;tFR< R= ltGR> R? ltHR@ RA ltIRB RC ltJRD RE ltKRF RG ltLRH RI ltMRšRJ RK lltN ! RL RM]4      tORN RO ltPRP RQ ltQRR RS ltRRT RU ltSRV RW ltTRX RY ltURZ R[ ltVR\ R] ltWR^ R_ ltXR` Ra ltYRbRcRd]W^2Re3RfRgRh]X^2Re3RiRjRkRl RRm3RnRoRRp RRm3RqRrRs]Y^Rt33tZRu Rv lt[Rw Rx lt\RšRy Rz llt]R{ R| lt^R} R~ lt_R R€ lt`R›R� R‚ lltaRƒ R„ ltb ! R… R†]4      tcR‡ Rˆ ltdR‰ RŠ lteR‹ RŒ ltfR� RŽ ltgR�thR� R‘ ltiR’ R“ ltjR” R• ltkR–RR—R/R˜ R™ lltlR# )œul  Curator â€” background skill maintenance orchestrator.

Inactivity-triggered (no cron daemon): when the agent is idle and the last run is older than ``interval_hours``,
``maybe_run_curator()`` auto-transitions lifecycle states from activity timestamps, optionally forks an AIAgent that
may pin/archive/consolidate/patch skills via skill_manage, and persists scheduler state in ``.curator_state``.
Invariants: only curator-managed skills are touched; never delete, only archive (recoverable); pinned skills bypass
all auto-transitions; the fork uses the auxiliary client and never touches the main session's prompt cache.)ÚannotationsN)ÚCounter)Úcopy_context)ÚdatetimeÚ	timedeltaÚtimezoneÚUTC)ÚPath)ÚAnyÚCallableÚDictÚListÚ
NamedTupleÚOptionalÚSet©Úget_hermes_home)Úget_disabled_skill_names©Úskill_usage)Úatomic_json_writeFc               ó   € V ^8„  d   QhRR/# ©é   Úreturnr	   © )Úformats   "Ú2/home/ubuntu/.hermes/hermes-agent/agent/curator.pyÚ__annotate__r   (   s   € ÷ ;ñ ;�Tñ ;ó    c                 ó2   € \        4       R ,          R,          # )Úskillsz.curator_stater   r   r   r   Ú_state_filer"   (   s   € ÜÓ˜xÕ'Ð*:Õ:Ð:r   c               ó   € V ^8„  d   QhRR/# ©r   r   údict[str, Any]r   )r   s   "r   r   r   ,   s   € ÷ ñ �Nñ r   c                 ó  € R RRRRRRRRRRRR^ /p \        4       p VP                  4       '       d'   \        P                  ! VP	                  R	R
7      4      M/ p\        T\        4      '       dS   T P                  TP                  4        UUu/ uF&  w  rEY@9   g   TP                  R4      '       g   K$  YEbK(  	  upp4       T #   \
        \        P                  3 d#   p\        P                  RT4       T u Rp?# Rp?ii ; iu uppi )Úlast_run_atNÚlast_run_duration_secondsÚlast_run_summaryÚlast_run_summary_shown_atÚlast_report_pathÚpausedFÚ	run_countz	utf-8-sig©Úencodingz Failed to read curator state: %sÚ_)r"   ÚexistsÚjsonÚloadsÚ	read_textÚOSErrorÚJSONDecodeErrorÚloggerÚdebugÚ
isinstanceÚdictÚupdateÚitemsÚ
startswith)ÚbaseÚpathÚdataÚeÚkÚvs         r   Ú
load_staterD   ,   sâ   € à�tÐ8¸$Ð@RÐTXØ# TÐ+=¸tÀXÈuÐVaÐcdð€Dô ‹=€DðØCGÇ;Á;Ç=Â=Œt�zŠz˜$Ÿ.™.°+˜.Ó>Ô?ÐVXˆô �$œ×ÒØ�‰ d§j¡j¤lÔU¡l™d˜a°a´iÀ1Ç<Á<ÐPS×CT”T�Q’T¡lÒUÔVØ€Køô ”T×)Ñ)Ð*ô Ü�‰Ð7¸Ô;Ø�ûðüó Vs)   œ>C Â!D
Â4D
ÃDÃC<Ã6DÃ<Dc               ó    € V ^8„  d   QhRRRR/# )r   r@   r%   r   ÚNoner   )r   s   "r   r   r   <   s   € ÷ Kñ K�^ð K¨ñ Kr   c                ó˜   €  \        \        4       V ^RR7       R#   \         d$   p\        P	                  RTRR7        Rp?R# Rp?ii ; i)r   T)ÚindentÚ	sort_keysz Failed to save curator state: %s©Úexc_infoN)r   r"   Ú	Exceptionr7   r8   )r@   rA   s   & r   Ú
save_staterM   <   s@   € ðKÜœ+›-¨°aÀ4×HøÜô KÜ�‰Ð7¸ÀTˆ×JÒJûðKús   ‚ ›A	¦AÁA	c               ó    € V ^8„  d   QhRRRR/# )r   r,   Úboolr   rF   r   )r   s   "r   r   r   C   s   € ÷ 9ñ 9�tð 9 ñ 9r   c                óJ   € \        / \        4       CR \        V 4      /C4       R# )r,   N)rM   rD   rO   ©r,   s   &r   Ú
set_pausedrR   C   s   € ÜÐ7”*“,Ð7 ¬$¨v«,Ñ7Ö8r   c               ó   € V ^8„  d   QhRR/# ©r   r   rO   r   )r   s   "r   r   r   G   s   € ÷ ,ñ ,�4ñ ,r   c                 óF   € \        \        4       P                  R 4      4      # rQ   )rO   rD   Úgetr   r   r   Ú	is_pausedrW   G   s   € Ü”
“× Ñ  Ó*Ó+Ð+r   c               ó$   € V ^8„  d   QhRRRRRR/# )r   Únoder
   ÚkeysÚstrr   r%   r   )r   s   "r   r   r   M   s!   € ÷ 2ñ 2�3ð 2˜sð 2 ~ñ 2r   c                óš   € V F,  p\        V \        4      '       d   V P                  V4      MRp K.  	  \        V \        4      '       d   V # / # )z@Walk nested dict keys; {} if any level is missing or not a dict.N©r9   r:   rV   )rY   rZ   Úkeys   &* r   Ú_subdictr_   M   s>   € ãˆÜ *¨4´× 6Ò 6ˆt�x‰x˜Œ}¸DŠñ ä˜d¤D×)Ò)ˆ4Ð1¨rÐ1r   Úlogc               ó(   € V ^8„  d   QhRRRRRRRR/# )r   r?   r[   Úlabelr`   zlogging.Loggerr   r%   r   )r   s   "r   r   r   T   s)   € ÷  ñ   ð  ¨Cð  °nð  ÐR`ñ  r   c                ó�   €  ^ RI Hp V! 4       p\	        T.TO5!  #   \         d   pTP                  RY4       / u Rp?# Rp?ii ; i)zGRead a nested section of ~/.hermes/config.yaml. Tolerates missing file.©Úload_config_readonlyz Failed to load config for %s: %sN)Úhermes_cli.configre   rL   r8   r_   )rb   r`   r?   re   ÚcfgrA   s   $$*   r   Ú_read_config_sectionrh   T   sJ   € ðÝ:Ù"Ó$ˆô �CÐ˜$ÓÐøô ô Ø�	‰	Ð4°eÔ?Ø�	ûðús   ‚ œA§A ºAÁ Ac               ó   € V ^8„  d   QhRR/# r$   r   )r   s   "r   r   r   _   s   € ÷ <ñ <�nñ <r   c                 ó   € \        R R R7      # )Úcurator)rb   )rh   r   r   r   Ú_load_configrl   _   s   € Ü 	°Ô;Ð;r   c               ó   € V ^8„  d   QhRR/# )r   r^   r[   r   )r   s   "r   r   r   c   s   € ÷ ñ ˜ñ r   c                ót   €  V! \        4       P                  W4      4      #   \        \        3 d    Tu # i ; i©N)rl   rV   Ú	TypeErrorÚ
ValueError)r^   ÚdefaultÚcasts   &&&r   Ú_config_numberrt   c   s8   € ðÙ”L“N×&Ñ& sÓ4Ó5Ð5øÜ”zÐ"ô ØŠðús   ‚! ¡7¶7c               ó   € V ^8„  d   QhRR/# rT   r   )r   s   "r   r   r   j   s   € ÷ 5ñ 5�Dñ 5r   c                 óH   € \        \        4       P                  R R4      4      # )ÚenabledT)rO   rl   rV   r   r   r   Ú
is_enabledrx   j   s   € Ü”“×"Ñ" 9¨dÓ3Ó4Ð4r   c               ó   € V ^8„  d   QhRR/# ©r   r   Úintr   )r   s   "r   r   r   n   s   € ÷ Dñ D˜Cñ Dr   c                 ó"   € \        R \        4      # )Úinterval_hours)Ú_bounded_countÚDEFAULT_INTERVAL_HOURSr   r   r   Úget_interval_hoursr€   n   s   € äÐ*Ô,BÓCÐCr   c               ó   € V ^8„  d   QhRR/# )r   r   Úfloatr   )r   s   "r   r   r   s   s   € ÷ Kñ K˜Eñ Kr   c                 ó,   € \        R \        \        4      # )Úmin_idle_hours)rt   ÚDEFAULT_MIN_IDLE_HOURSr‚   r   r   r   Úget_min_idle_hoursr†   s   s   € ÜÐ*Ô,BÄEÓJÐJr   ÚsetÚ_warned_bad_valuesc               ó$   € V ^8„  d   QhRRRRRR/# )r   r^   r[   rr   r{   r   r   )r   s   "r   r   r   z   s!   € ÷ ñ ˜ð  cð ¨cñ r   c                óª   € \        W\        4      pV^8  d<   W3\        9  d.   \        P                  W34       \        P                  RWV4       V# V# )u¿  *key* (a ``curator.<key>`` day/hour count), floored at 1 like ``curator prune --days`` already
refuses (hermes_cli/curator.py::_cmd_prune). A value < 1 collapses stale_cutoff/archive_cutoff
onto or past "now" in apply_automatic_transitions(), mass-transitioning every skill with any
past activity on the next automatic pass â€” unlike the manual prune path this runs unconfirmed,
so it falls back to the default instead of acting on the bad value.z9curator.%s must be >= 1 (got %d); using the default of %d)rt   r{   rˆ   Úaddr7   Úwarning)r^   rr   Úvalues   && r   r~   r~   z   sO   € ô ˜3¬Ó-€EØˆq„yàˆ<Ô1Ô1Ü×"Ñ" C <Ô0Ü�N‰NÐVÐX[ÐdkÔlØˆØ€Lr   c               ó   € V ^8„  d   QhRR/# rz   r   )r   s   "r   r   r   Š   s   € ÷ Hñ H˜cñ Hr   c                 ó"   € \        R \        4      # )Ústale_after_days)r~   ÚDEFAULT_STALE_AFTER_DAYSr   r   r   Úget_stale_after_daysr’   Š   s   € ÜÐ,Ô.FÓGÐGr   c               ó   € V ^8„  d   QhRR/# rz   r   )r   s   "r   r   r   Ž   s   € ÷ Lñ L ñ Lr   c                 ó"   € \        R \        4      # )Úarchive_after_days)r~   ÚDEFAULT_ARCHIVE_AFTER_DAYSr   r   r   Úget_archive_after_daysr—   Ž   s   € ÜÐ.Ô0JÓKÐKr   c               ó   € V ^8„  d   QhRR/# rT   r   )r   s   "r   r   r   ’   s   € ÷ Hñ H˜ñ Hr   c                 óP   € \        \        4       P                  R\        4      4      # )u‰   LLM consolidation pass â€” OFF by default (prune only, no aux-model fork); ``hermes curator run --consolidate`` overrides per invocation.Úconsolidate)rO   rl   rV   ÚDEFAULT_CONSOLIDATEr   r   r   Úget_consolidaterœ   ’   s   € ä”“×"Ñ" =Ô2EÓFÓGÐGr   c               ó    € V ^8„  d   QhRRRR/# )r   ÚtsúOptional[str]r   úOptional[datetime]r   )r   s   "r   r   r   ™   s   € ÷ ñ �=ð Ð%7ñ r   c                ót   €  V '       d   \         P                  ! V 4      # R #   \        \        3 d     R # i ; iro   )r   Úfromisoformatrp   rq   )rž   s   &r   Ú
_parse_isor£   ™   s6   € ðß-/Œx×%Ò% bÓ)Ð9°TÐ9øÜ”zÐ"ô Úðús   ‚" Š"  " ¢7¶7c               ó    € V ^8„  d   QhRRRR/# )r   Únowr    r   rO   r   )r   s   "r   r   r       s   € ÷ Añ AÐ*ð A°dñ Ar   c                ó
  € \        4       '       d   \        4       '       d   R# \        4       p\        VP	                  R4      4      pT ;'       g    \
        P                  ! \        4      p Vf'    V P                  4       VR&   RVR&   \        V4       R# VP                  f   VP                  \        R7      pW,
          \        \!        4       R7      8¬  #   \         d"   p\        P                  RT4        Rp?R# Rp?ii ; i)	a/  Gates: curator.enabled, not paused, ``last_run_at`` present AND older than interval_hours. First observation seeds
``last_run_at`` to now and defers one interval, so a fresh install/update never mutates the library on its first tick.
``hermes curator run`` bypasses this; the idle check is the caller's.Fr'   Nuu   deferred first run â€” curator seeded, will run after one interval; use `hermes curator run --dry-run` to preview nowr)   z&Failed to seed curator last_run_at: %s©Útzinfo)Úhours)rx   rW   rD   r£   rV   r   r¥   r   Ú	isoformatrM   rL   r7   r8   r¨   Úreplacer   r€   )r¥   ÚstateÚlastrA   s   &   r   Úshould_run_nowr®       sÓ   € ô �<Š<œ9Ÿ;š;ÙÜ‹L€EÜ�e—i‘i Ó.Ó/€DØ
×
"Ð
"”—’œcÓ"€CØ‚|ð	FØ#&§=¡=£?ˆE�-Ñ ð )`ˆEÐ$Ñ%Ü�uÔñ Ø‡{�{ÒØ�|‰|¤3ˆ|Ó'ˆØ�Jœ9Ô+=Ó+?Ô@Ñ@Ð@øô ô 	FÜ�L‰LÐAÀ1×EÐEÙûð	Fús   Á0#C ÃDÃ!C=Ã=Dc               ó   € V ^8„  d   QhRR/# )r   r   úset[str]r   )r   s   "r   r   r   ¸   s   € ÷ ñ  ñ r   c                 ó’   €  ^ RI Hp  V ! 4       #   \         d-   p\        P	                  RTRR7       \        4       u Rp?# Rp?ii ; i)zšSkill names referenced by any cron job (incl. paused/disabled). Best-effort: a cron import error or corrupt jobs store yields an empty set, never a crash.)Úreferenced_skill_namesz0Curator could not read cron skill references: %sTrJ   N)Ú	cron.jobsr²   rL   r7   r8   r‡   )Ú_refsrA   s     r   Ú_cron_referenced_skillsrµ   ¸   s<   € ðÝ=Ù‹wˆøÜô Ü�‰ÐGÈÐUYˆÔZÜ‹u�ûðús   ‚ �Aš!A»AÁAc               ó    € V ^8„  d   QhRRRR/# )r   Únamer[   r   rO   r   )r   s   "r   r   r   Â   s   € ÷ (ñ ( #ð (¨$ñ (r   c           	     ó´  €  ^ RI HpHp V! R4      p V P	                  V4      ^ ,          Ve5   \
        P                  ! \        4      ;_uu_ 4        V! V4       RRR4       # #   \         d    R;rB Lai ; i  + '       g   i     # ; i  TeH   \
        P                  ! \        4      ;_uu_ 4        T! T4       RRR4       i   + '       g   i     i ; ii ; i)z�Archive via skill_usage with the ledger actor tagged 'curator', so the ledger entry reads as an autonomous transition, not a foreground call.)Úreset_ledger_actorÚset_ledger_actorrk   N)Útools.skill_ledgerr¹   rº   rL   Úarchive_skillÚ
contextlibÚsuppress)Ú_ur·   r¹   rº   Útoks   &&   r   Ú_archive_as_curatorrÁ   Â   s±   € ð(ßKÙ˜yÓ)ˆð(Ø×Ñ Ó% aÕ(àŠ?Ü×$Ò$¤Y×/Õ/Ù" 3Ô'÷ 0Ð/ð øô ô (Ø#'Ð'ˆÑ ð(ú÷ 0×/ûð Š?Ü×$Ò$¤Y×/Õ/Ù" 3Ô'÷ 0×/×/Ð/úð ús@   ‚A# “B
 Á	A7Á#A4Á3A4Á7BÂ
'CÂ1	C	Â:	CÃCÃ	Cc               ó    € V ^8„  d   QhRRRR/# )r   r¥   r    r   údict[str, int]r   )r   s   "r   r   r   Ñ   s   € ÷ 4ñ 4Ð%7ð 4À>ñ 4r   c           
     óx  a	a
€ ^ RI Ho	 T ;'       g    \        P                  ! \        4      p V \        \        4       R7      ,
          pV \        \        4       R7      ,
          p\        4       pR^ R^ R^ R^ R^ /o
R V	V
3R	 llpS	P                  4        EF¢  pS
R;;,          ^,          uu&   VR
,          pVP                  R4      '       g   Wc9   d   KA  VP                  RR4      '       g)   S	P                  V4       S
R;;,          ^,          uu&   K�  VP                  R4      R8X  d‹   \        VP                  R^ 4      ;'       g    ^ 4      ^ 8X  da   \        VP                  R4      4      '       gA   VP                  R4      '       g*   S	P                  V4       S
R;;,          ^,          uu&   EK!  \        VP                  R4      4      ;'       g%    \        VP                  R4      4      ;'       g    T pVP                  f   VP!                  \        R7      pVP                  RS	P"                  4      p\        VP                  R^ 4      ;'       g    ^ 4      ^ 8X  d/   Wq8”  d)   VS	P$                  8X  d   V! VS	P"                  R4       EKÿ  Wr8:  d?   VS	P&                  8w  d.   \)        S	V4      '       d   S
R;;,          ^,          uu&   EK@  EKC  Wq8:  d)   VS	P"                  8X  d   V! VS	P$                  R4       EKq  Wq8”  g   EKz  VS	P$                  8X  g   EKŽ  V! VS	P"                  R4       EK¥  	  S
# )a	  Move every curator-managed skill between active/stale/archived based on its latest real activity; pinned skills are
never touched. Built-ins are seeded with a baseline record on first sight so their inactivity clock starts NOW, not at epoch.
Returns a counter dict.r   )ÚdaysÚmarked_staleÚarchivedÚreactivatedÚcheckedÚseededc               ó(   € V ^8„  d   QhRRRRRRRR/# )r   r·   r[   r¬   r^   r   rF   r   )r   s   "r   r   Ú1apply_automatic_transitions.<locals>.__annotate__ß   s(   € ÷ ñ �3ð ˜sð ¨ð °ñ r   c                óT   <€ SP                  W4       SV;;,          ^,          uu&   R# )é   N)Ú	set_state)r·   r¬   r^   r¿   Úcountss   &&&€€r   Ú_setÚ)apply_automatic_transitions.<locals>._setß   s   ø€ Ø
�‰�TÔ!Øˆs��qÕ�r   r·   ÚpinnedÚ
_persistedTÚ
provenanceÚbundledÚ	use_countÚlast_activity_atÚfirst_seen_atÚ
created_atr§   r¬   )Útoolsr   r   r¥   r   r   r’   r—   rµ   Úcurated_reportrV   Úseed_record_if_missingr{   r£   Úreanchor_clockr¨   r«   ÚSTATE_ACTIVEÚSTATE_STALEÚSTATE_ARCHIVEDrÁ   )r¥   Ústale_cutoffÚarchive_cutoffÚ	protectedrÑ   Úrowr·   ÚanchorÚcurrentr¿   rÐ   s   &        @@r   Úapply_automatic_transitionsrè   Ñ   s‰  ù€ õ (à
×
"Ð
"”—’œcÓ"€CØœÔ(<Ó(>Ô?Õ?€LØœ9Ô*@Ó*BÔCÕC€Nô (Ó)€IØ˜a ¨Q°¸qÀ)ÈQÐPXÐZ[Ð\€F÷ñ ð × Ñ ×"ˆØˆy×˜QÕÓØ�6�{ˆØ�7‰7�8×Ò Ô 1Ùà�w‰w�| T×*Ò*Ø×%Ñ% dÔ+Ø�8× Õ!ÓÙà�G‰G�LÓ! YÔ.´3°s·w±w¸{ÈAÓ7N×7SÐ7SÐRSÓ3TÐXYÔ3YÜ" 3§7¡7Ð+=Ó#>×?Ò?ÈÏÉÐP_×H`ÒH`Ø×Ñ˜dÔ#Ø�8× Õ!ÓÚä˜CŸG™GÐ$6Ó7Ó8×dÐd¼JÀsÇwÁwÈ|ÓG\Ó<]×dÐdÐadˆØ�=‰=Ò Ø—^‘^¬3�^Ó/ˆFØ—'‘'˜' 2§?¡?Ó3ˆô ˆs�w‰w�{ AÓ&×+Ð+¨!Ó,°Ô1°fÔ6KØ˜"Ÿ.™.Ô(Ù�T˜2Ÿ?™?¨MÔ:ÚØÔ#¨°2×3DÑ3DÔ(DÜ" 2 t×,Ò,Ø�z×" aÕ'Ö"ò -àÔ#¨°2·?±?Ô(BÙ��r—~‘~ ~×6Ø×" w°"·.±.×'@Ù��r—‘¨×6ñC #ðD €Mr   u  â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�
DRY-RUN â€” REPORT ONLY. DO NOT MUTATE THE SKILL LIBRARY.
â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�

This is a PREVIEW pass. Follow every instruction below EXCEPT:

  â€¢ DO NOT call skill_manage with action=patch, create, delete, write_file, or remove_file.
  â€¢ skills_list and skill_view are FINE â€” read as much as you need.

Your output IS the deliverable. Produce the exact same human-readable summary and structured YAML block you would produce on a live run â€” but describe the actions you WOULD take, not actions you took. A downstream reviewer will read the report and decide whether to approve a live run with `hermes curator run` (no flag).

If you accidentally take a mutating action, say so explicitly in the summary so the reviewer can revert it.
â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�â•�u›(  You are running as Hermes' background skill CURATOR. This is an UMBRELLA-BUILDING consolidation pass, not a passive audit and not a duplicate-finder.

The goal of the skill collection is a LIBRARY OF CLASS-LEVEL INSTRUCTIONS AND EXPERIENTIAL KNOWLEDGE. A collection of hundreds of narrow skills where each one captures one session's specific bug is a FAILURE of the library â€” not a feature. An agent searching skills matches on descriptions, not on exact names (note: long descriptions are truncated to 57 chars in the system prompt skill index â€” keep the trigger class in that window). One broad umbrella skill with labeled subsections beats five narrow siblings for discoverability, not the other way around.

The right target shape is CLASS-LEVEL skills whose SKILL.md carries the always-on rules and whose `references/`, `templates/`, and `scripts/` hold a SMALL set of topical depth â€” not one-session-one-skill micro-entries, and not an umbrella that hoards one references/ file per absorbed sibling. Consolidation means DISTILLING: the absorbed content becomes rules (imperative + one clause of why), the same lesson stated twice becomes one rule, and incident narration, PR/issue numbers, dates and quoted chatter are dropped â€” the rule must stand without the story. Moving a file unchanged under references/ is filing, not consolidating. A SKILL.md body over ~24k chars is a consolidation target on its own: skill_view loads all of it into context for the rest of the session, so distill it to the always-on rules and push topic depth into references/.

Hard rules â€” do not violate:
1. DO NOT touch bundled, hub-installed, or external-dir skills (`skills.external_dirs`). The candidate list below is already filtered to local curator-managed skills only; external skills are externally owned and read-only to this background curator.
2. DO NOT delete any skill. Archiving (moving the skill's directory into ~/.hermes/skills/.archive/) is the maximum destructive action. Archives are recoverable; deletion is not.
3. DO NOT touch skills shown as pinned=yes. Skip them entirely.
3b. DO NOT archive, delete, consolidate, move, or otherwise modify any skill named in the protected built-ins list (currently: plan). These back load-bearing UX (slash-command entry points referenced in docs and tips) and are filtered out of the candidate list below â€” never resurrect one as an archive or absorb target.
3c. DO NOT archive or prune any skill marked `cron=yes` in the candidate list. A cron job depends on it and will fail to load it on its next run. You MAY still consolidate it into an umbrella â€” but only because the curator rewrites cron job skill references to follow consolidations; never simply prune it.
4. DO NOT use usage counters as a reason to skip consolidation. The counters are new and often mostly zero. Judge overlap on CONTENT, not on use_count. 'use=0' is not evidence a skill is valuable; it's absence of evidence either way. Corollary: 'use=0' is ALSO not a reason to PRUNE a skill. Never archive a never-used skill (use=0) unless it is at least 30 days old (check last_activity / created date) AND its content is genuinely obsolete or fully absorbed elsewhere â€” a recently-created skill simply may not have had its trigger come up yet.
5. DO NOT reject consolidation on the grounds that 'each skill has a distinct trigger'. Pairwise distinctness is the wrong bar. The right bar is: 'would a human maintainer write this as N separate skills, or as one skill with N labeled subsections?' When the answer is the latter, merge.

How to work â€” not optional:
1. Scan the full candidate list. Identify PREFIX CLUSTERS (skills sharing a first word or domain keyword). Examples you are likely to find: hermes-config-*, hermes-dashboard-*, gateway-*, codex-*, ollama-*, anthropic-*, gemini-*, mcp-*, salvage-*, pr-*, competitor-*, python-*, security-*, etc. Expect 10-25 clusters.
2. For each cluster with 2+ members, do NOT ask 'are these pairs overlapping?' â€” ask 'what is the UMBRELLA CLASS these skills all serve? Would a maintainer name that class and write one skill for it?' If yes, pick (or create) the umbrella and absorb the siblings into it.
3. Three ways to consolidate â€” use the right one per cluster:
   a. MERGE INTO EXISTING UMBRELLA â€” one skill in the cluster is already broad enough to be the umbrella (example: `pr-triage-salvage` for the PR review cluster). Patch it to add a labeled section for each sibling's unique insight, then archive the siblings.
   b. CREATE A NEW UMBRELLA SKILL.md â€” no existing member is broad enough. Use skill_manage action=create to write a new class-level skill whose SKILL.md covers the shared workflow and has short labeled subsections. Archive the now-absorbed narrow siblings.
   c. DEMOTE TO REFERENCES/TEMPLATES/SCRIPTS â€” a sibling has narrow-but-valuable depth that is only needed sometimes. Distill it into the umbrella's appropriate support directory:
      â€¢ `references/<topic>.md` â€” named by TOPIC, merged into an existing topical file when one covers it (decision tables, recipes, provider quirks, condensed domain notes). Never `<sibling-name>.md` copied verbatim; never a per-incident file.
      â€¢ `templates/<name>.<ext>` for starter files meant to be copied and modified
      â€¢ `scripts/<name>.<ext>` for statically re-runnable actions (verification scripts, fixture generators, probes)
      Then archive the old sibling. Re-home the content through the LEDGERED tool surface: `skill_manage action=write_file` on the umbrella to place the file (subdirectories are created for you), then `skill_manage action=remove_file` on the source to drop the original, then `skill_manage action=delete` on the source. Never a terminal move â€” a shell mv/cp writes the same bytes with no ledger entry, so the archive that follows snapshots an already-stripped package and `hermes curator rollback` restores a hollow skill (issue #96962).

Package integrity â€” not optional:
Before demoting or archiving a skill, inspect it as a COMPLETE directory package, not just SKILL.md. A skill root may include `references/`, `templates/`, `scripts/`, and `assets/`; `skill_view` discovers those relative to the skill root. A reference markdown file inside another skill is NOT a new skill root and does not get its own linked-file discovery.
If the source skill has support files OR SKILL.md contains relative links such as `references/...`, `templates/...`, `scripts/...`, or `assets/...`, DO NOT flatten only SKILL.md into `<umbrella>/references/<old>.md`. Choose one safe path instead:
   â€¢ keep it as a standalone skill, OR
   â€¢ fully merge it by re-homing every needed support file into the umbrella's canonical `references/`, `templates/`, `scripts/`, or `assets/` directories AND rewrite the destination instructions to the new paths, OR
   â€¢ archive the entire original skill package unchanged.
Never leave archived/demoted instructions pointing at files that were left behind under the old skill directory.
4. Also flag skills whose NAME is too narrow (contains a PR number, a feature codename, a specific error string, an 'audit' / 'diagnosis' / 'salvage' session artifact). These almost always belong as a subsection or support file under a class-level umbrella.
5. Iterate. After one consolidation round, scan the remaining set and look for the NEXT umbrella opportunity. Don't stop after 3 merges.

Your toolset:
  - skills_list, skill_view        â€” read the current landscape
    READ BEFORE WRITE â€” enforced, not advisory. Before skill_manage action=patch, action=write_file on a file that already exists, or action=remove_file, call skill_view on that SAME target in this review turn â€” skill_view(name) for SKILL.md, skill_view(name, file_path=...) for a supporting file â€” and build the write from the content it just returned. A write without that read is REFUSED and nothing is saved.
  - skill_manage action=patch      â€” add sections to the umbrella
  - skill_manage action=create     â€” create a new umbrella SKILL.md
  - skill_manage action=write_file â€” add a references/, templates/, or scripts/ file under an existing skill (the skill must already exist)
  - skill_manage action=delete     â€” archive a skill. MUST pass `absorbed_into=<umbrella>` naming the skill you merged its content into (the umbrella must already exist). Deletes without a verified forwarding target are refused â€” pruning with no absorption target is the deterministic staleness pass's job, never this one's. `absorbed_into` drives cron-job skill-reference migration â€” guessing from your YAML summary after the fact is fragile.
  You have NO terminal access in this pass â€” every filesystem mutation goes through skill_manage above so it is ledgered and rollback-able (issue #96962). Reading files works through skill_view (including skill_view(name, file_path=...) for support files).

'keep' is a legitimate decision ONLY when the skill is already a class-level umbrella and none of the proposed merges would improve discoverability. 'This is narrow but distinct from its siblings' is NOT a reason to keep â€” it's a reason to move it under an umbrella as a subsection or support file.

Expected output: real umbrella-ification. Process every obvious cluster. If you end the pass with fewer than 10 archives, you stopped too early â€” go back and look at the clusters you left alone.

When done, write a human summary AND a structured machine-readable block so downstream tooling can distinguish consolidation from pruning. Format EXACTLY:

## Structured summary (required)
```yaml
consolidations:
  - from: <old-skill-name>
    into: <umbrella-skill-name>
    reason: <one short sentence â€” why merged, not just 'similar'>
prunings:
  - name: <skill-name>
    reason: <one short sentence â€” why archived with no merge target>
```

Every skill you moved to .archive/ MUST appear in exactly one of the two lists. If you consolidated X into umbrella Y (patched Y, wrote a references file to Y, or created Y with X's content absorbed), X goes under `consolidations` with `into: Y`. If you archived X with no absorption â€” truly stale, irrelevant, or obsolete â€” X goes under `prunings`. Leave a list empty (`consolidations: []`) if none. Do not omit the block. The block comes AFTER your human-readable summary of clusters processed, patches made, and decisions left alone.c               ó   € V ^8„  d   QhRR/# r   r   )r   s   "r   r   r   Ù  s   € ÷ ñ �tñ r   c                 ó¼   € \        4       R,          R,          p  V P                  RRR7       V #   \         d"   p\        P	                  RT4        Rp?T # Rp?ii ; i)z�``~/.hermes/logs/curator/`` (telemetry next to agent.log, not under skills/). mkdir'd here too so gateway-only / bare-library entry paths work.Úlogsrk   T©ÚparentsÚexist_okz%Curator reports dir create failed: %sN)r   Úmkdirr5   r7   r8   )ÚrootrA   s     r   Ú_reports_rootrñ   Ù  s\   € äÓ˜vÕ%¨	Õ1€DðAØ�
‰
˜4¨$ˆ
Ô/ð €Køô ô AÜ�‰Ð<¸a×@Ð@Ø€KûðAús   š/ ¯AºAÁAc               ó$   € V ^8„  d   QhRRRRRR/# )r   Úneedler[   r?   r   rO   r   )r   s   "r   r   r   ã  s&   € ÷ @ñ @ cð @°ð @¸ñ @r   c                ó  a€ V P                  RR4      o\        ;QJ d>    V3R lVP                  RR4      P                  R4       4       F  '       g   K   R# 	  R# ! V3R lVP                  RR4      P                  R4       4       4      # )uß   True if *needle* equals a complete filename stem or directory name in *path* â€” so "api" does not match
"references/api-design.md". Hyphens and underscores are normalised ("open-webui-setup" matches "open_webui_setup.md").Ú-r0   c              3  óŽ   <"  € T F:  q;'       d-    VP                  R ^4      ^ ,          P                  RR4      S8H  x € K<  	  R# 5i)Ú.rõ   r0   N)Úrsplitr«   )Ú.0ÚpartÚnorm_needles   & €r   Ú	<genexpr>Ú,_needle_in_path_component.<locals>.<genexpr>ç  s?   øé € ÐÑ\~ÐTX×OÐO˜Ÿ™ C¨Ó+¨AÕ.×6Ñ6°s¸CÓ@ÀKÑOÔOÓ\~ùs
   ƒA�5AÚ\Ú/TF)r«   ÚanyÚsplit)ró   r?   rû   s   &&@r   Ú_needle_in_path_componentr  ã  sv   ø€ ð —.‘.  cÓ*€Kß‹3ÔÐ\`×\hÑ\hÐimÐorÓ\s×\yÑ\yÐz}Ô\~Ó�3Œ3ÐŠ3Ðˆ3ÔÐ\`×\hÑ\hÐimÐorÓ\s×\yÑ\yÐz}Ô\~ÓÓÐr   c               ó$   € V ^8„  d   QhRRRRRR/# )r   Útcr
   Úraw_fallbackrO   r   úOptional[dict[str, Any]]r   )r   s   "r   r   r   ê  s"   € ÷ 4ñ 4˜3ð 4°ð 4Ð:Rñ 4r   c               ó˜  € \        V \        4      '       d   V P                  R4      R8w  d   R# V P                  R4      ;'       g    Rp\        V\        4      '       g   \        V\        4      '       d   V# R#  \        P
                  ! V4      p\        T\        4      '       d   T# R#   \         d    T'       d   RT/u # Ru # i ; i)zÎParsed arguments of a ``skill_manage`` tool call (JSON string or dict), or None to skip. With *raw_fallback*,
a malformed string yields ``{"_raw": raw}`` so substring matching still catches the common case.r·   Úskill_manageNÚ	argumentsÚ Ú_raw)r9   r:   rV   r[   r2   r3   rL   )r  r  ÚrawÚargss   &$  r   Ú_skill_manage_argsr  ê  sª   € ô �bœ$×Ò 2§6¡6¨&£>°^Ô#CÙØ
�&‰&�Ó
×
#Ð
# €CÜ�cœ3×ÒÜ  ¤d×+Ò+ˆsÐ5°Ð5ð7Ü�zŠz˜#‹ˆô ˜d¤D×)Ò)ˆ4Ð3¨tÐ3øô ô 7ß ,�˜ˆ}Ò6°$Ò6ð7ús   Á;B+ Â+C	Â>C	ÃC	ÃC	c               ó$   € V ^8„  d   QhRRRRRR/# )r   r  r%   Úneedlesr°   r   rŸ   r   )r   s   "r   r   r   ù  s!   € ÷ 	ñ 	˜.ð 	°8ð 	Àñ 	r   c                ó"  aa€ R F†  oV P                  S4      o\        S\        4      '       g   K,  \        ;QJ d*    VV3R l\	        RV4       4       F  '       g   K   RM	  RM! VV3R l\	        RV4       4       4      '       g   K„  Su # 	  R# )uù   First argument value (file_path, file_content, content, new_string, _raw â€” in that order) that references one of
*needles*. ``file_path`` must match a whole path component; content fields match on word boundaries so "test" does not match "latest".c              3  ó¨   <"  € T FG  pSR 8X  d   \        VS4      M.\        P                  ! R\        P                  ! V4       R2S4      x € KI  	  R# 5i)Ú	file_pathz\bN)r  ÚreÚsearchÚescape)rù   ÚnÚhayr^   s   & €€r   rü   Ú"_find_reference.<locals>.<genexpr>þ  sW   øé € ð (
ñ |QÐvw°#¸Ô2DÔ& q¨#Ô.Ì"Ï)Ê)ÐWYÔZ\×ZcÒZcÐdeÓZfÐYgÐgiÐTjÐloÓJpÔpó  |Qùs   ƒAANTF)r  Úfile_contentÚcontentÚ
new_stringr  )rV   r9   r[   r   Úfilter)r  r  r  r^   s   &&@@r   Ú_find_referencer  ù  s—   ù€ ó NˆØ�h‰h�s‹mˆÜ�cœ3×Ô§C£Cõ (
ô |Bð  CGð  IPô  |Qó(
§C§C¢Cõ (
ô |Bð  CGð  IPô  |Qó(
÷ %
ô %
ð ŠJñ Nñ r   c          
     ó,   € V ^8„  d   QhRRRRRRRRRR	/# )
r   Úremovedú	list[str]ÚaddedÚafter_namesr°   Ú
tool_callsúlist[dict[str, Any]]r   údict[str, list[dict[str, Any]]]r   )r   s   "r   r   r     s4   € ÷ <ñ <Øð<Ø(ð<Ø7?ð<ØMað<à$ñ<r   c                óp  € . p. pR T;'       g    .  4        Uu. uF
  qff   K  VNK  	  pp\        V4      \        T;'       g    . 4      ,          p\        RV 4       FÌ  p	W™P                  RR4      V	P                  RR4      0p
V FŒ  pVP                  R4      p\	        V\
        4      '       d   V'       d   WÉ8X  g   WÈ9  d   K@  \        Wº4      pVf   KQ  VP                  RV	RVRRVP                  R	R
4       RV RV	 RVR,           2/4        K·  	  VP                  RV	/4       KÎ  	  RVRV/# u upi )a0  Split ``removed`` into consolidated vs pruned. Heuristic: a ``skill_manage`` call on a DIFFERENT, surviving-or-new
skill whose file_path/content arguments reference the removed name is the "absorbed" signal; earliest match wins.
Returns ``{"consolidated": [{name, into, evidence}], "pruned": [{name}]}``.c              3  ó<   "  € T F  p\        VR R7      x € K  	  R# 5i)T©r  N©r  ©rù   r  s   & r   rü   Ú+_classify_removed_skills.<locals>.<genexpr>  s   é € ÐeÑTdÈbÔ 2°2ÀD× IÑ IÓTdùó   ‚Nrõ   r0   r·   ÚintoÚevidencezskill_manage action=ÚactionÚ?z on 'z' referenced 'z' in :NéP   NÚconsolidatedÚpruned)r‡   r  r«   rV   r9   r[   r  Úappend)r   r"  r#  r$  r3  r4  ÚaÚparsed_callsÚdestinationsr·   r  r  Útargetr  s   &&&&          r   Ú_classify_removed_skillsr:    sV  € ð *,€LØ#%€FÙeÐT^×TdÐTdÐbdÐTdÔeÓwÑe˜!—A�AÑe€LÐwÜ�{Ó#¤c¨%¯+¨+°2Ó&6Õ6€LÜ�t˜WÖ%ˆØŸ™ c¨3Ó/°·±¸cÀ3Ó1GÐHˆÛ ˆDØ—X‘X˜fÓ%ˆFä˜f¤c×*Ò*·&¸F¼NÈfÔNhÙÜ! $Ó0ˆCØŒØ×#Ñ# V¨T°6¸6À:Ø';¸D¿H¹HÀXÈsÓ<SÐ;TÐTYÐZ`ÐYaÐaoÐptÐouÐuzÐ{~ð  @Cõ  |Dð  {Eð  &Fð%Gô Hâñ !ð �M‰M˜6 4˜.Ö)ñ &ð ˜L¨(°FÐ;Ð;ùò! xs
   šD3¤D3c               ó    € V ^8„  d   QhRRRR/# )r   Ú	llm_finalr[   r   údict[str, list[dict[str, str]]]r   )r   s   "r   r   r      s   € ÷ tñ t¨ð tÐ1Pñ tr   c                óž  a€ \        V \        4      '       d<   \        P                  ! RV \        P                  \        P
                  ,          4      MRpRoV'       d&    ^ RIpVP                  VP                  ^4      4      o\        S\        4      '       g   R. R. /# R V3R llpRV! RRR4      RV! RR	4      /#   \         d     LGi ; i)
u]  Extract the required fenced ```yaml block (``consolidations:`` / ``prunings:`` lists) from the curator's final
response. Tolerant: missing block or malformed YAML â†’ empty lists (caller falls back to the tool-call heuristic); a partial
block returns what parsed. Returns ``{"consolidations": [{from, into, reason}], "prunings": [{name, reason}]}``.z```ya?ml\s*\n(.*?)\n```NÚconsolidationsÚpruningsc               ó$   € V ^8„  d   QhRRRRRR/# )r   r^   r[   Úfieldsr   zlist[dict[str, str]]r   )r   s   "r   r   Ú/_parse_structured_summary.<locals>.__annotate__0  s'   € ÷ Añ A�cð A Cð AÐ,@ñ Ar   c                óP  <aaa€ SP                  V 4      ;'       g    . p\        V\        4      '       d
   R  V 4       MRpVV3R lV 4       pV Uau. uFG  o\        ;QJ d    V3R lS 4       F  '       d   K   RM	  RM! V3R lS 4       4      '       g   KE  SNKI  	  up# u upi )c              3  óV   "  € T F  p\        V\        4      '       g   K  Vx € K!  	  R # 5iro   )r9   r:   )rù   rA   s   & r   rü   Ú>_parse_structured_summary.<locals>._entries.<locals>.<genexpr>2  s   é € Ð9™c˜¤Z°´4×%8—1’1›cùs   ‚)Ÿ
)c           
   3  óÈ   <"  € T FR  p. SOR N Uu/ uF<  q"\        VP                  V4      ;o\        4      '       d   SP                  4       MRbK>  	  upx € KT  	  R# u upi 5i)Úreasonr
  N)r9   rV   r[   Ústrip)rù   rA   ÚfrB  rC   s   &  €€r   rü   rF  3  sY   øé € Ð~Ñv}ÐqrÐXkÐZ`ÐXkÐbjÑXkÓlÑXkÐST¤Z°a·e±e¸A³h°°Ä×%EÒ%E˜Ÿ™œ	È2ÒMÑXkÖlÓv}ùÒlùs   ƒA"�AAÁA"c              3  ó6   <"  € T F  pSV,          x € K  	  R # 5iro   r   )rù   rJ  rA   s   & €r   rü   rF  4  s   øé € Ð)?¹°1¨!¨A¯$ª$»ùs   ƒFTr   )rV   r9   ÚlistÚall)r^   rB  r  ÚentriesÚcleanedrA   rC   r@   s   &j   `@€r   Ú_entriesÚ+_parse_structured_summary.<locals>._entries0  st   û€ Ø�h‰h�s‹m×!Ð!˜rˆÜ=GÈÌT×=RÒ=RÑ9™cÔ9ÐXZˆÝ~Ñv}Ó~ˆÙ"Ô@™7�a§c£cÔ)?¹Ó)?§c§c¢cÔ)?¹Ó)?×&?—�™7Ñ@Ð@ùÒ@s   ÁB#Á#B#Á:B#ÂB#Úfromr.  r·   )r9   r[   r  r  ÚDOTALLÚ
IGNORECASEÚhermes_yamlÚ	safe_loadÚgrouprL   r:   )r<  ÚmatchÚyamlrP  r@   s   &   @r   Ú_parse_structured_summaryrZ     sÁ   ø€ ô
 \fÐfoÔqt×[uÒ[uŒB�IŠIÐ0°)¼R¿Y¹YÌÏÉÕ=VÔWÐ{€EØ€Dßð	Û&Ø—>‘> %§+¡+¨a£.Ó1ˆDô �dœD×!Ò!Ø  " j°"Ð5Ð5÷Að Að ™hÐ'7¸ÀÓHÈ*ÑV^Ð_iÐkqÓVrÐsÐsøô ô 	Ùð	ús   Á $B> Â>CÃCc               ó    € V ^8„  d   QhRRRR/# )r   r$  r%  r   zdict[str, dict[str, Any]]r   )r   s   "r   r   r   9  s   € ÷ 
ñ 
Ð4Hð 
ÐMfñ 
r   c                ó”  € / pR T ;'       g    .  4        F®  pVf   K	  VP                  R4      R8X  g   K!  VP                  R4      VP                  R4      rC\        V\        4      '       g   KZ  VP                  4       '       g   Kr  \        V\        4      '       g   KŠ  RVP                  4       RR/WP                  4       &   K°  	  V# )	ut  Model-declared absorption targets from ``skill_manage(action='delete')`` calls â€” the authoritative classification
signal (beats YAML parsing and substring heuristics). Returns ``{name: {"into": umbrella | "", "declared": True}}``;
``into == ""`` is an explicit prune. Deletes omitting ``absorbed_into`` are absent so the caller falls back to heuristic/YAML (older runs).c              3  ó<   "  € T F  p\        VR R7      x € K  	  R# 5i)Fr)  Nr*  r+  s   & r   rü   Ú6_extract_absorbed_into_declarations.<locals>.<genexpr>>  s   é € ÐWÑFVÀÔ# B°U×;Ñ;ÓFVùr-  r0  Údeleter·   Úabsorbed_intor.  ÚdeclaredT)rV   r9   r[   rI  )r$  Úoutr  r·   r9  s   &    r   Ú#_extract_absorbed_into_declarationsrc  9  s›   € ð &(€CÙWÀj×FVÐFVÐTVÐFVÖWˆØÔ §¡¨Ó 2°hÖ >ØŸ8™8 FÓ+¨T¯X©X°oÓ-F�&Ü˜$¤×$Ô$¨¯©¯¬¼*ÀVÌS×:QÔ:QØ%+¨V¯\©\«^¸ZÈÐ$N�—J‘J“LÓ!ñ	 Xð
 €Jr   c               ó0   € V ^8„  d   QhRRRRRRRRR	R
RR/# )r   r   r!  Ú	heuristicr&  Úmodel_blockr=  r8  r°   Úabsorbed_declarationsz#Optional[dict[str, dict[str, Any]]]r   r   )r   s   "r   r   r   F  s>   € ÷ +<ñ +<Øð+<Ø#Bð+<ØQpð+<àð+<à3Vð+<ð %ñ+<r   c           	     ód  aaa€ VP                  R. 4       Uu/ uF  qUR,          VbK  	  ppVP                  R. 4       Uu/ uF  qUR,          VbK  	  ppVP                  R. 4       Uu/ uF  qUR,          VbK  	  ppT;'       g    / p	. o. oV  EF
  oVP                  S4      VP                  S4      VP                  S4      V	P                  S4      3w  r«rÍV
'       d   V
P                  R4      ;'       g    RMRpV'       d#   VP                  R4      '       d   RVR,          /M/ pRR	 VV3R
 lllpRR VV3R lllpVe   VP                  RR4      MRpV'       d   VV9   d   V! VRV3/ VB  Kæ  VR8X  d0   T! RV'       d   VP                  R4      ;'       g    RMR4       EK  V
'       d=   V
P                  R4      V9   d'   T! V
R,          RV'       d   RMR,           V3/ VB  EK`  V
'       d6   V'       d.   V! VR,          RVP                  RR4      V
R,          R7       EK�  V
'       d   V! R4       EK°  V'       d&   V! VR,          RVP                  RR4      R7       EKÝ  T! V'       d   RMRV'       d   VP                  RR4      MR4       EK  	  RSRS/# u upi u upi u upi )u6  Merge heuristic (tool-call evidence) with the model's structured block.
First match wins; every removed skill lands in exactly one bucket:
- ``absorbed_into`` declared at delete is authoritative: existing target â†’ consolidated; ``""`` â†’ pruned; missing target â†’ fall through.
- Model-declared consolidation wins when its ``into`` is in ``destinations``.
- Model named a missing umbrella â†’ heuristic's finding, else pruned.
- Heuristic-only consolidation kept, marked ``source="tool-call audit"``.
- Otherwise pruned (model-declared, or no-evidence fallback).r3  r·   r?  rR  r@  rH  r
  r/  c          
     ó,   € V ^8„  d   QhRRRRRRRRRR/# )	r   r.  r[   ÚsourcerH  Úextrar
   r   rF   r   )r   s   "r   r   Ú/_reconcile_classification.<locals>.__annotate__\  s6   € ÷ 	kñ 	k˜ð 	k Sð 	k°#ð 	kÀSð 	kÈTñ 	kr   c           
     ó>   <€ SP                  R SRV RVRV/VC4       R# )r·   r.  rj  rH  N©r5  )r.  rj  rH  rk  r3  r·   s   &&&,€€r   Ú_consÚ(_reconcile_classification.<locals>._cons\  s)   ø€ Ø×Ñ ¨¨v°t¸XÀvÈxÐY_Ð iÐchÐ iÖjr   c               ó$   € V ^8„  d   QhRRRRRR/# )r   rj  r[   rH  r   rF   r   )r   s   "r   r   rl  _  s&   € ÷ 	Nñ 	N˜3ð 	N¨ð 	N°Tñ 	Nr   c                ó6   <€ SP                  R SRV RV/4       R# )r·   rj  rH  Nrn  )rj  rH  r·   r4  s   &&€€r   Ú_pruneÚ)_reconcile_classification.<locals>._prune_  s   ø€ Ø�M‰M˜6 4¨°6¸8ÀVÐLÖMr   Nr.  z(absorbed_into (model-declared at delete)z'absorbed_into="" (model-declared prune)Úmodelz+auditz.tool-call audit (model named missing umbrella))r/  Úmodel_claimed_intoz>fallback (model named missing umbrella, no tool-call evidence)z5tool-call audit (model omitted from structured block))r/  zno-evidence fallbackr4  )r
  ©rV   )r   re  rf  r8  rg  rA   Ú	heur_consÚ
model_consÚmodel_prunedra  ÚmcÚmpÚhcÚdecÚ	mc_reasonÚhc_evidencero  rs  Ú
into_claimr3  r·   r4  s   &&&&&              @@@r   Ú_reconcile_classificationr‚  F  ss  ú€ ð (1§}¡}°^ÀRÔ'HÓIÑ'H !�6•˜A’Ñ'H€IÐIØ(3¯©Ð8HÈ"Ô(MÓNÑ(M 1�F•)˜Q’,Ñ(M€JÐNØ*5¯/©/¸*ÀbÔ*IÓJÑ*I Q�f•I˜q’LÑ*I€LÐJØ$×*Ð*¨€HØ)+€LØ#%€FÜˆØ$Ÿ.™.¨Ó.°×0@Ñ0@ÀÓ0FÈ	ÏÉÐVZÓH[Ð]e×]iÑ]iÐjnÓ]oÐo‰ˆ�ß02�R—V‘V˜HÓ%×+Ð+¨ø¸ˆ	ß68¸R¿V¹VÀJ×=OÒ=O�z 2 j¥>Ñ2ÐUWˆ÷	kó 	k÷	Nó 	Nð -0ªO�S—W‘W˜V RÔ(Àˆ
ß˜*¨Ô4Ù�*ÐHÈ)ÑcÐWbÔcØ˜2ÔÙÐ>×\^ÀÇÁÈÓAQ×AWÐAWÐUWøÐdf×gß�B—F‘F˜6“N lÔ2Ù�"�V•*˜g·R©¸RÕ@À)Ñ[È{Õ[ß—BÙ�"�V•*ÐNÐY[×Y_ÑY_Ð`jÐlnÓYoð  EGð  HNõ  EO÷  Pð  PßÙÐS×TßÙ�"�V•*ÐUÐ`b×`fÑ`fÐgqÐsuÓ`v×wÐwáŸb‘7Ð&<×VX¸b¿f¹fÀXÈrÔ>RÐ^`×añ3 ð4 ˜L¨(°FÐ;Ð;ùòA JùÚNùÚJs   ˜J#Á J(Á(J-c                  óJ   € ] tR tRt$ R]R&   R]R&   R]R&   R]R&   R]R	&   R
tR# )Ú_RunDiffit  r°   r#  r!  r   r"  r%  r3  r4  r   N)Ú__name__Ú
__module__Ú__qualname__Ú__firstlineno__Ú__annotations__Ú__static_attributes__r   r   r   r„  r„  t  s    ‡ ØÓØÓØÓØ&Ó&Ø × r   r„  c          
     ó,   € V ^8„  d   QhRRRRRRRRRR	/# )
r   Úbefore_namesr°   r#  r$  r%  Úmodel_finalr[   r   r„  r   )r   s   "r   r   r   |  s=   € ÷ 
kñ 
k Xð 
k¸Hð 
kÐRfð 
kÐuxð 
kð  ~Fñ 
kr   c           
     ó  € \        W,
          4      \        W,
          4      rT\        V\        WEWR7      \        V4      \	        V4      \	        V4      ,          \        V4      R7      p\        WWVR,          VR,          4      # )zÝDiff the before/after skill sets and classify every removal: the model's YAML block carries intent + rationale,
the tool-call heuristic audits for hallucinated umbrellas/omissions, per-delete ``absorbed_into`` beats both.)r   r"  r#  r$  )r   re  rf  r8  rg  r3  r4  )Úsortedr‚  r:  rZ  r‡   rc  r„  )rŒ  r#  r$  r�  r   r"  Úclassifications   &&&&   r   Ú_diff_and_classifyr‘  |  ss   € ô ˜LÕ6Ó7¼ÀÕ@ZÓ9[ˆUÜ.ØÜ*°7ÐU`ÔxÜ-¨kÓ:ÌÈ[ÓIYÔ\_Ð`eÓ\fÕIfÜAÀ*ÓMô	€Nô �K¨%ÀÕ1OÐQ_Ð`hÕQiÓjÐjr   c               ó    € V ^8„  d   QhRRRR/# )r   Úreportr%  r   zdict[Any, dict[str, Any]]r   )r   s   "r   r   r   ‰  s    € ÷ Eñ EÐ)ð EÐ.Gñ Er   c                ó€   € V  Uu/ uF-  p\        V\        4      '       g   K  VP                  R 4      VbK/  	  up# u upi )r·   r]   )r“  Úrs   & r   Ú_by_namer–  ‰  s3   € Ù&,ÓD¡f ´
¸1¼d×0CÔˆA�E‰E�&‹M˜1Ò¡fÑDÐDùÒDs   …;¢;c          
     ó,   € V ^8„  d   QhRRRRRRRRRR/# )	r   rŒ  r°   Úafter_reportr%  r$  r�  r[   r   r   )r   s   "r   r   r   �  s;   € ÷ ñ ¨8ð ÐCWð Ðeyð ð  ILð ð  QTñ r   c           
     ó†  € \        \        V4      4      pW,
          '       g   R# \        WW#4      p^
p\        VP                  4      \        VP
                  4      ,           pVP                   Uu. uF+  pRVP                  RR4       RVP                  RR4       2NK-  	  p	pY•P
                   Uu. uF1  pR\        V\        4      '       d   VP                  RR4      MT R2NK3  	  up,          p	RV R	2.V	R
V ,           p
Wv8”  d   V
P                  RWv,
           R24       V
P                  R4       \        VP                   Uu0 uF,  qˆP                  R4      '       g   K  VP                  R4      kK.  	  up4      pV'       d   V
P                  RV^ ,           24       RP                  V
4      # u upi u upi u upi )a  The "where did my skills go?" lines appended to the user-visible ``final_summary``; "" when nothing was archived.
Capped at 10 entries so a big consolidation doesn't flood agent.log (full list is in REPORT.md); the pin hint
appears only when a consolidation produced an umbrella.r
  u     â€¢ r·   r1  õ    â†’ r.  u    â€” pruned (stale)z	archived z
 skill(s):Nu
     â€¦ and z morez"full report: hermes curator statusz,keep an umbrella stable: hermes curator pin Ú
)r‡   r–  r‘  Úlenr3  r4  rV   r9   r:   r5  r�  Újoin)rŒ  r˜  r$  r�  r#  ÚdiffÚSHOWÚtotalrA   rN  ÚlinesÚ	umbrellass   $$$$        r   Ú_build_rename_summaryr£  �  s„  € ô ”h˜|Ó,Ó-€KØ×%Ô%ÙÜ˜l¸ÓQ€DØ€DÜ�×!Ñ!Ó"¤S¨¯©Ó%5Õ5€EØOS×O`ÒO`ÓaÑO`È!�˜Ÿ™˜f cÓ*Ð+¨5°·±°v¸sÓ1CÐ0DÓEÑO`€GÐaØ×hsÒhsÓtÑhsÐcd�&¬z¸!¼T×/BÒ/B˜Ÿ™˜v sÔ+ÈÐJÐJ]Ó^ÑhsÑtÕt€GØ˜˜˜zÐ*Ð+¨g°e°t¨nÕ<€EØ„|Ø�‰�z %¥, ¨uÐ5Ô6Ø	‡L�LÐ5Ô6Ü¨t×/@Ò/@ÓRÑ/@¨!ÇEÁEÈ&ÇMœ˜Ÿ™˜fžÑ/@ÑRÓS€IßØ�‰ÐCÀIÈaÅLÀ>ÐRÔSØ�9‰9�UÓÐùò bùÚtùò
 Ss   Á01F4Â17F9ÅF>Å$F>c               ó$   € V ^8„  d   QhRRRRRR/# )r   r3  r%  r4  r   r%   r   )r   s   "r   r   r   £  s)   € ÷ Wñ WÐ%9ð WÐCWð WÐ\jñ Wr   c                óR  €  V  Uu/ uF^  p\        V\        4      '       g   K  VP                  R4      '       g   K4  VP                  R4      '       g   KM  VR,          VR,          bK`  	  ppV Uu. uF=  p\        V\        4      '       g   K  VP                  R4      '       g   K4  VR,          NK?  	  ppV'       g	   V'       d   ^ RIHp V! W4R7      # R. R^ R^ /# u upi u upi   \
         d6   p\        P                  RTR	R
7       R. R^ R^ R\        T4      /u Rp?# Rp?ii ; i)uâ   Point cron jobs at the umbrella when the curator consolidated a skill they list â€” otherwise the scheduler fails to
load it and the job runs without its instructions. Best-effort: a cron-module issue never breaks the curator.r·   r.  )Úrewrite_skill_refs)r3  r4  ÚrewritesÚjobs_updatedÚjobs_scannedz%Curator cron skill rewrite failed: %sTrJ   ÚerrorN)	r9   r:   rV   r³   r¦  rL   r7   r8   r[   )r3  r4  rA   Úconsolidated_mapÚpruned_namesr¦  s   &&    r   Ú_rewrite_cron_refsr­  £  s  € ð	WÙ:Fó  C¹,°QÌ*ÐUVÔX\×J]Ô0Ðbc×bgÑbgÐhn×boÔ0Ðtu×tyÑtyð  {A÷  uBÔ0˜A˜f�I q¨¥yÒ0¹,Ðð  CÙ+1Ó[©6 a´ZÀÄ4×5Hœ	ÈQÏUÉUÐSYÏ]œ	˜˜&Ÿ	˜	©6ˆÐ[ßŸ|Ý4Ù%Ð3CÔYÐYØ˜B °°>À1ÐEÐEùò CùÚ[øô
 ô WÜ�‰Ð<¸aÈ$ˆÔOØ˜B °°>À1ÀgÌsÐSTËvÐVÕVûðWúsb   ‚C& †C£C¼CÁCÁ*C& Á0C!ÂC!Â&C!Â3C& ÃC& ÃC& Ã
C& Ã&D&Ã1*D!ÄD&Ä!D&c               ó(   € V ^8„  d   QhRRRRRRRR/# )	r   r?   r	   rb   r[   Úrenderr
   r   rF   r   )r   s   "r   r   r   ²  s(   € ÷ >ñ >�dð > 3ð >°ð >¸ñ >r   c                óø   €  T P                  \        V4      '       d   V! 4       M\        P                  ! V^RR7      R,           RR7       R#   \         d"   p\
        P                  RY4        Rp?R# Rp?ii ; i)z�Best-effort write of *render()* (or the JSON dump of a non-callable payload);
rendering runs inside the guard so a serialisation error is logged, not raised.F)rH   Úensure_asciir›  úutf-8r.   zCurator %s write failed: %sN)Ú
write_textÚcallabler2   ÚdumpsrL   r7   r8   )r?   rb   r¯  rA   s   &&& r   Ú_write_filer¶  ²  sd   € ð>Ø�‰¤H¨V×$4Ò$4™œ¼$¿*º*ÀVÐTUÐdiÔ:jÐmqÕ:qð  }Dˆö  	EøÜô >Ü�‰Ð2°E×=Ò=ûð>ús   ‚A	A ÁA9ÁA4Á4A9c               ó<   € V ^8„  d   QhRRRRRRRRR	R
RRRR
RRRR/	# )r   Ú
started_atr   Úelapsed_secondsr‚   Úauto_countsrÃ   Úauto_summaryr[   Úbefore_reportr%  rŒ  r°   r˜  Úllm_metar%   r   zOptional[Path]r   )r   s   "r   r   r   »  sV   € ÷ *ñ *Øð*Ø.3ð*ØBPð*Ø`cð*à'ð*à7?ð*àOcð*ào}ð*ð ñ*r   c                ó‚  aaa€ \        4       V P                  R4      r˜W‰,          ^rºV
P                  4       '       d   V^,          pW‰ RV 2,          p
K.   V
P                  RRR7       TP                  R. 4      ;'       g    . p\        T4      \        T4      uoo\        T\        S4      Y×P                  R	R
4      ;'       g    R
4      pTT3R l\        TP                  T,          4       4       pT UUUu. uF,  w  pppT'       g   K  T'       g   K  TT8w  g   K$  RTRTRT/NK.  	  pppp\        \        R T 4       4      4      p\        TP                   TP"                  4      p\%        TP                  R^ 4      4      p/ RT P'                  4       bR\)        T^4      bRTP                  RR
4      bRTP                  RR
4      bRTbRR\+        T4      R\+        TP                  4      R\+        TP                  4      \+        T4      ,
          R\+        TP,                  4      R\+        TP.                  4      R\+        TP                   4      R\+        TP"                  4      R\+        T4      RTR \1        TP3                  4       4      /
bR!TbR"TP,                  bR#TP                   bR$TP"                  bR%TP"                   Uu. uF  pTR,          NK  	  upbR&TP.                  bRTbR'TbR(TP                  R	R
4      bR)TP                  R*R
4      bR+TP                  R,4      bRTP                  R. 4      /Co\5        T
R-,          R-S4       \5        T
R.,          R.T3R/ l4       T^ 8”  d   \5        T
R0,          R0T4       T
#   \         d"   p\
        P                  RT4        Rp?R# Rp?ii ; iu upppi u upi )1zÎWrite run.json + REPORT.md under logs/curator/{YYYYMMDD-HHMMSS}[-N]/ (N disambiguates a crash-rerun in the same
second). Returns the report dir, or None if it couldn't be created (reporting is best-effort).z%Y%m%d-%H%M%Srõ   TFrì   z!Curator run dir create failed: %sNr$  Úfinalr
  c              3  óÌ   <"  € T FY  qSP                  V4      ;'       g    / P                  R 4      SP                  V4      ;'       g    / P                  R 4      3x € K[  	  R# 5i)r¬   Nrw  )rù   r  Úafter_by_nameÚbefore_by_names   & €€r   rü   Ú$_write_run_report.<locals>.<genexpr>Î  sc   øé € ð  [ñ  sZÐmn�>×%Ñ% aÓ(×.Ð.¨B×3Ñ3°GÓ<¸}×?PÑ?PÐQRÓ?S×?YÐ?YÐWY×>^Ñ>^Ð_fÓ>gÕhó  sZùs   ƒA$ (A$Á	A$r·   rR  Útoc              3  óD   "  € T F  qP                  R R4      x € K  	  R# 5i)r·   ÚunknownNrw  r+  s   & r   rü   rÃ  Ð  s   é € Ð,\ÑQ[È2¯V©V°F¸I×-FÐ-FÓQ[ùs   ‚ r¨  r¸  Úduration_secondsru  ÚproviderÚauto_transitionsrÐ   ÚbeforeÚafterÚdeltaÚarchived_this_runÚadded_this_runÚconsolidated_this_runÚpruned_this_runÚstate_transitionsÚcron_jobs_rewrittenÚtool_calls_totalÚtool_call_countsrÇ   r3  r4  r¬  r"  Úcron_rewritesr<  Úllm_summaryÚsummaryÚ	llm_errorrª  zrun.jsonz	REPORT.mdc                 ó   <€ \        S 4      # ro   )Ú_render_report_markdown)Úpayloads   €r   Ú<lambda>Ú#_write_run_report.<locals>.<lambda>â  s   ø€ Ô<SÐT[Ô<\r   zcron_rewrites.json)rñ   Ústrftimer1   rï   rL   r7   r8   rV   r–  r‘  r‡   r�  r#  r:   r   r­  r3  r4  r{   rª   Úroundrœ  r   r"  ÚsumÚvaluesr¶  )r¸  r¹  rº  r»  r¼  rŒ  r˜  r½  rð   ÚstampÚrun_dirÚsuffixrA   r$  rž  Ústatesr  Úbr6  ÚtransitionsÚ	tc_countsrÕ  r¨  ÚprÁ  rÂ  rÛ  s   $$$$$$$$                @@@r   Ú_write_run_reportrê  »  sö  ú€ ô  “/ :×#6Ñ#6°Ó#Gˆ%Ø•l AˆVØ
�.‰.×
Ò
Ø�!�ˆØ˜7 ! F 8Ð,Õ,ŠðØ�‰˜d¨UˆÔ3ð —‘˜l¨BÓ/×5Ð5°2€JÜ$,¨\Ó$:¼HÀ]Ó<SÐ!€M�>Ü˜l¬C°Ó,>À
ÏLÉLÐY`ÐbdÓLe×LkÐLkÐikÓl€Dõ [ÔrxÐy}÷  zJñ  zJð  MYõ  zYô  sZó  [€FÙBHÕ_Á&±w°q¸!¸QÌAÔ2ÔRSÔ2ÐXYÐ]^ÑX^Ô2�F˜A˜v q¨$°Ó2Á&€KÒ_Ü $¤WÑ,\ÑQ[Ó,\Ó%\Ó ]€IÜ& t×'8Ñ'8¸$¿+¹+ÓF€MÜ�}×(Ñ(¨¸Ó;Ó<€LðØ�j×*Ñ*Ó,ðØ.@Ä%ÈÐYZÓB[ðà�—‘˜g rÓ*ðà,6¸¿¹ÀZÐQSÓ8TðàVhÐjuðð 	Ø”c˜,Ó'¨´#°d×6FÑ6FÓ2GÈÔRUÐVZ×VfÑVfÓRgÔjmÐnzÓj{ÕR{Ø¤ T§\¡\Ó!2Ð4DÄcÈ$Ï*É*ÃoØ#¤S¨×):Ñ):Ó%;Ð=NÔPSÐTX×T_ÑT_ÓP`Ø¤ [Ó!1Ð3HÈ,ÐXjÔloÐpy÷  qAñ  qAó  qCó  mDð	
ðð 	˜Iðð (2°4·<±<ðð BPÐQU×QbÑQbðð emÐnr×nyÑnyðð 	¨D¯KªKÓ8©K q˜˜6Ÿ˜©KÑ8ðð ;BÀ4Ç:Á:ðð PcÐdoðð rAð  CPðð 	�X—\‘\ '¨2Ó.ðð 1>¸x¿|¹|ÈIÐWYÓ?Zðð 	�X—\‘\ 'Ó*ðð -9¸(¿,¹,À|ÐUWÓ:Xñ€Gô �˜*Õ$ j°'Ô:Ü�˜+Õ% {Ô4\Ô]Ø�aÔÜ�GÐ2Õ2Ð4HÈ-ÔXØ€Nøô; ô Ü�‰Ð8¸!Ô<Ýûðüô `ùò 9s6   ÁN Ã9N5ÄN5ÄN5ÄN5ËN<ÎN2ÎN-Î-N2c               ó    € V ^8„  d   QhRRRR/# )r   Úentryr%   r   r[   r   )r   s   "r   r   r   è  s   € ÷ Wñ W˜.ð W¨Sñ Wr   c                ór   € V P                  R 4      ;'       g    RP                  4       ;p'       d   RV 2# R# )rH  r
  u    â€” )rV   rI  )rì  rH  s   & r   Ú_reason_suffixrî  è  s9   € Ø+0¯9©9°XÓ+>×+DÐ+DÀ"×*KÑ*KÓ*MÐ M Ö MˆU�6�(ÐÐVÐTVÐVr   c               ó    € V ^8„  d   QhRRRR/# ©r   rì  r%   r   r!  r   )r   s   "r   r   r   ì  s   € ÷ Añ A˜~ð A°)ñ Ar   c                óZ  € R V P                  RR4       RV P                  RR4       R2\        V 4      ,           pV P                  RR4      pV'       d%   VP                  R4      '       d   VR	V R
2,          pV.V P                  R4      '       d   RV R,           R2.,           # . ,           # )ú- `r·   r1  u   ` â†’ merged into `r.  Ú`rj  r
  ztool-call auditz  _(detected via z)_rv  u#     âš  The curator's summary named `zg` as the umbrella but that skill doesn't exist post-run; showing the tool-call audit's finding instead.)rV   rî  r=   )rì  Úlinerj  s   &  r   Ú_consolidated_linesrõ  ì  sÇ   € Ø�—‘˜6 3Ó'Ð(Ð(;¸E¿I¹IÀfÈcÓ<RÐ;SÐSTÐUÔXfÐglÓXmÕm€DØ�Y‰Y�x Ó$€Fß�&×#Ñ#Ð$5×6Ò6ØÐ# F 8¨2Ð.Õ.ˆØˆ6àŸ	™	Ð"6×7Ò7ð <¸EÐBVÕ<WÐ;Xð Y~ð ~ð õ Að Aà=?õAð Ar   c               ó    € V ^8„  d   QhRRRR/# )r   rì  r
   r   r!  r   )r   s   "r   r   r   ö  s   € ÷ tñ t˜ð t ñ tr   c                óŠ   € \        V \        4      '       d(   R V P                  RR4       R2\        V 4      ,           .# R V  R2.# )rò  r·   r1  ró  )r9   r:   rV   rî  )rì  s   &r   Ú_pruned_linesrø  ö  sM   € äGQÐRWÔY]×G^ÒG^ˆc�%—)‘)˜F CÓ(Ð)¨Ð+¬n¸UÓ.CÕCÐsÐsÐfiÐjoÐipÐpqÐdrÐsÐsr   c               ó    € V ^8„  d   QhRRRR/# rð  r   )r   s   "r   r   r   û  s   € ÷ ]ñ ]˜~ð ]°)ñ ]r   c           
     óL  € V P                  R 4      ;'       g    V P                  R4      ;'       g    RpRT RRP                  V P                  R4      ;'       g    . 4       RRP                  V P                  R4      ;'       g    . 4      ;'       g    R	 R
2pV.V P                  R4      ;'       g    / P                  4        UUu. uF  w  r4RV RV R2NK  	  upp,           V P                  R4      ;'       g    .  Uu. uF	  pRV R2NK  	  up,           # u uppi u upi )Újob_nameÚjob_idr1  rò  z`: `ú, rÊ  u   ` â†’ `rË  z(none)ró  Úmappedz    - `z` (consolidated)Údroppedz` dropped (pruned))rV   r�  r<   )rì  rû  ÚheadÚoldÚnewr·   s   &     r   Ú_cron_rewrite_linesr  û  s  € Ø�y‰y˜Ó$×BÐB¨¯	©	°(Ó(;×BÐB¸s€HØ��
˜$˜tŸy™y¨¯©°8Ó)<×)BÐ)BÀÓCÐDÀGÈDÏIÉIÐV[×V_ÑV_Ð`gÓVh×VnÐVnÐlnÓLo×L{ÐL{Ðs{ÐK|Ð|}Ð~€DØˆFÐSX×S\ÑS\Ð]eÓSf×SlÐSlÐjl×RsÑRsÔRuÔvÑRuÁhÀc˜ ˜u G¨C¨5Ð0@ÓAÑRuÒvÕvØ?D¿y¹yÈÓ?S×?YÐ?YÐWYÐ?YÓ[Ñ?Y°d�˜˜Ð0Ó1Ñ?YÑ[õ\ð ]ùÓvùÚ[s   Ã	DÄD!r3  z!Consolidated into umbrella skillsu$  _These skills were **absorbed into another skill** during this run â€” their content still lives, just under a different name. The original directory was moved to `~/.hermes/skills/.archive/` for safety and can be restored via `hermes curator restore <name>` if the consolidation was wrong._
zsee `run.json`r4  u!   Pruned â€” archived for stalenesszÕ_These skills were archived without being merged into an umbrella (e.g. stale, unused, or judged irrelevant). Directories live under `~/.hermes/skills/.archive/`. Restore any via `hermes curator restore <name>`._
r"  zNew skills this runzX_Usually these are new class-level umbrellas created via `skill_manage action=create`._
c                ó   € R V  R2.# )rò  ró  r   )r  s   &r   rÜ  rÜ    s   € �#�a�S˜�
‰|r   r
  rÑ  zState transitionsc                ót   € R V P                  R4       RV P                  R4       RV P                  R4       2.# )rò  r·   z`: rR  rš  rÄ  rw  )Úts   &r   rÜ  rÜ    s@   € À#ÀaÇeÁeÈFÃmÀ_ÐTWÐXY×X]ÑX]Ð^dÓXeÐWfÐfkÐlm×lqÑlqÐrvÓlwÐkxÐ@yÑ?zr   rÕ  z#Cron job skill references rewrittenz¾_Cron jobs that referenced a consolidated or pruned skill were updated in-place so they keep loading the right instructions on their next run. See `cron_rewrites.json` for the full record._
zsee `cron_rewrites.json`c               ó    € V ^8„  d   QhRRRR/# )r   ré  r%   r   r[   r   )r   s   "r   r   r     s   € ÷ %ñ %˜~ð %°#ñ %r   c                óx  € \        \        V P                  R^ 4      ;'       g    ^ 4      ^<4      w  rV'       d	   V RV R2MV R2pV P                  R4      ;'       g    / V P                  R4      ;'       g    / V P                  R4      ;'       g    / repV P                  R4      pRV P                  R	R
4       R2RV P                  R4      ;'       g    R RV P                  R4      ;'       g    R RV RVP                  R^ 4       RVP                  R^ 4       RVP                  R^ 4      R R2.V'       d   RV R2.M. ORNRVP                  R^ 4       2NRVP                  R ^ 4       2NR!VP                  R"^ 4       2NR#VP                  R$^ 4       2NR
NR%NR&VP                  R'^ 4       R(R)P                  R* \	        VP                  4       4       4       4      ;'       g    R+ R,2NR-VP                  R.^ 4       R/2NR0VP                  R1^ 4       R/2NR2VP                  R3^ 4       R/2NR4VP                  R5^ 4       R/2NR
Np\         FÒ  w  ršr¼rÞV P                  V	4      ;'       g    . pV	R68X  d   VP                  R74      ;'       g    . pV'       g   KN  TR8V
 R\        V4       R2.V'       d   V.M. ,           ,          pVR9V  F  pWŒ! V4      ,          pK  	  Y�e+   \        V4      V8”  d   R:\        V4      V,
           R;V R,2.M. R
.,           ,          pKÔ  	  V P                  R<4      ;'       g    R
P                  4       pV'       d   VR=VR
.,          pM;V'       g4   V P                  R>4      '       g   MVR?V P                  R>4      R
.,          pV. R@O,          pRP                  V4      # )Az$Render the human-readable REPORT.md.rÇ  zm ÚsrÐ   rÉ  rÔ  rØ  u   # Curator run â€” r¸  r
  r›  zModel: `ru  z(not resolved)z` via `rÈ  u   `  Â·  Duration: u     Â·  Agent-created skills: rÊ  rš  rË  z (rÌ  z+dz)
u   > âš  LLM pass error: `z`
z### Auto-transitions (pure, no LLM)
z- checked: rÉ   z- marked stale: rÆ   z0- archived (no LLM, pure time-based staleness): rÇ   z- reactivated: rÈ   z## LLM consolidation pass
z- tool calls: **rÓ  z** (by name: rý  c              3  ó4   "  € T F  w  rV R V 2x € K  	  R# 5i)Ú=Nr   )rù   rB   rC   s   &  r   rü   Ú*_render_report_markdown.<locals>.<genexpr>$  s2   é € ð  UGñ  mFÑdhÐdeÐXYÐWZÐZ[Ð\]Ð[^ÕU_ó  mFùs   ‚ÚnoneÚ)z!- consolidated into umbrellas: **rÏ  z**z%- pruned (archived for staleness): **rÐ  z- new skills this run: **rÎ  u7   - state transitions (active â†” stale â†” archived): **rÑ  rÕ  r§  z### Nu
   - â€¦ and z more (r<  z## LLM final summary
rÖ  z## LLM summary
)z## Recovery
z<- Restore an archived skill: `hermes curator restore <name>`zR- All archives live under `~/.hermes/skills/.archive/` and are recoverable by `mv`zH- See `run.json` in this directory for the full machine-readable record.r
  )	Údivmodr{   rV   r�  r�  r<   Ú_REPORT_SECTIONSrœ  rI  )ré  ÚminsÚsecsÚ	dur_labelrÐ   Úautorè  rª  r¡  r^   ÚtitleÚintror¯  ÚshowÚhintr<   rì  r¿  s   &                 r   rÚ  rÚ    s»  € äœ˜AŸE™EÐ"4°aÓ8×=Ð=¸AÓ>ÀÓC�J€Dß&*�4�&˜˜4˜& Ñ"°4°&¸°
€IØŸe™e H›o×3Ð3°°Q·U±UÐ;MÓ5N×5TÐ5TÐRTÐVW×V[ÑV[Ð\nÓVo×VuÐVuÐsu�)€FØ�E‰E�+Ó€Eà
˜QŸU™U <°Ó4Ð5°RÐ8Ø
�1—5‘5˜“>×5Ð5Ð%5Ð6°g¸a¿e¹eÀJÓ>O×>cÐ>cÐScÐ=dÐduÐvð  vAð A!Ø!'§¡¨H°aÓ!8Ð 9¸¸v¿z¹zÈ'ÐSTÓ?UÐ>VÐVXÐY_×YcÑYcÐdkÐmnÓYoÐprÐXsÐsvð	xð÷ 5:Ð$ U G¨3Ð/Ñ
0¸rð	ð
 	/ðð
 3>¸d¿h¹hÀyÐRSÓ>TÐ=UÐ0Vðð
 [kÐko×ksÑksð  uCð  EFó  lGð  kHð  YIðð ;¸4¿8¹8ÀJÐPQÓ;RÐ:SÐTðð YhÐhl×hpÑhpÐq~ð  ABó  iCð  hDð  WEðð GIðð 	&ðð ˜6Ÿ:™:Ð&8¸!Ó<Ð=¸]È4Ï9É9ñ  UGÔlrÐs|÷  tCñ  tCó  tEô  mFó  UGó  LG÷  LQð  LQð  KQð  KRð  RSð  	Tðð ,¨F¯J©JÐ7NÐPQÓ,RÐ+SÐSUÐVðð 0°·
±
Ð;LÈaÓ0PÐ/QÐQSÐTðð YrÐrx×r|Ñr|ð  ~Nð  PQó  sRð  rSð  SUð  WVðð BÀ&Ç*Á*ÐM`ÐbcÓBdÐAeÐegÐhðð kmð€E÷ 2BÑ-ˆ�E 4Ø—‘�c“
× Ð ˜bˆØ�/Ô!Ø—I‘I˜jÓ)×/Ð/¨RˆEßÙØ�D˜˜˜r¤# e£* ¨SÐ1Ð2Ç°u±gÈBÕOÕOˆØ˜5˜D“\ˆEØ�V˜E“]Õ"ŠEñ "àÒFVÔ[^Ð_dÓ[eÐhlÔ[l�Z¤ E£
¨TÕ 1Ð2°'¸$¸¸qÐAÑBÐrtÐy{Ðx|Õ|Õ|Šñ 2Bð �U‰U�;Ó×%Ð% 2×,Ñ,Ó.€EßØÐ*¨E°2Ð6Õ6‰ß˜Ÿ™˜m×,Ò,°ØÐ$ a§e¡e¨MÓ&:¸BÐ?Õ?ˆØ	ò ^õ ^€Eð �9‰9�UÓÐr   c               ó   € V ^8„  d   QhRR/# )r   r   r[   r   )r   s   "r   r   r   @  s   € ÷ ñ  ñ r   c                 óì  € \        4       p \        P                  ! 4        Uu. uF:  p\        P                  ! VR,          4      '       d   K(  VR,          V 9  g   K8  VNK<  	  ppV'       g   R# \	        4       pRP                  R\        V4       R2.V Uu. uFÃ  pRVR,           RVP                  RR	4       R
VR,           RVP                  R4      '       d   RMR RVR,          V9   d   RMR RVP                  R^ 4       RVP                  R^ 4       RVP                  R^ 4       RVP                  R^ 4       RVP                  R4      ;'       g    R 2NKÅ  	  up,           4      # u upi u upi )uÚ  Human/agent-readable list of LLM consolidation candidates.

Bundled built-ins are excluded even when ``curator.prune_builtins`` is
on. That flag makes them eligible for *deterministic archival*
(``apply_automatic_transitions`` â†’ ``archive_skill``), not for the
rewrite/umbrella pass. Listing them here invites umbrella deletes
that ``_background_review_delete_guard`` unconditionally refuses,
burning tool calls until the loop guard aborts the run.

Skills in ``skills.disabled`` (global or platform list) are excluded for
the same reason on the read side: ``skill_view`` â€” the pass's only read
path â€” refuses them, so the fork retries the same refused read until
the same-tool-failure halt ends the run with zero findings.
r·   z"No agent-created skills to review.r›  zAgent-created skills (z):
z- z  provenance=rÕ   Úagentz  state=r¬   z	  pinned=rÓ   ÚyesÚnoz  cron=z  activity=Úactivity_countz  use=r×   z  view=Ú
view_countz
  patches=Úpatch_countz  last_activity=rØ   Únever)r   r   rÜ   Ú
is_bundledrµ   r�  rœ  rV   )Údisabledr•  ÚrowsÚcron_referenceds       r   Ú_render_candidate_listr&  @  sŽ  € ô (Ó)€Hä×-Ò-Ô/óÙ/ˆaÜ×%Ò% a¨¥i×0ô 	
Ø56°vµYÀhÑ5N÷ 	
ˆÑ/ð 	ð ÷ Ù3Ü-Ó/€OØ�9‰9Ð.¬s°4«y¨k¸Ð>Ð?ñ
 óCñ
 ˆAð	 ˆQˆv�YˆK�} Q§U¡U¨<¸Ó%AÐ$BÀ(È1ÈWÍ:È,ð WØŸ5™5 Ÿ?š?‘%°Ð5°WÀaÈÅiÐSbÔFb¹UÐhlÐ<mð nØ—E‘EÐ*¨AÓ.Ð/¨v°a·e±e¸KÈÓ6KÐ5LÈGÐTU×TYÑTYÐZfÐhiÓTjÐSkð lØ—5‘5˜¨Ó*Ð+Ð+;¸A¿E¹EÐBTÓ<U×<`Ð<`ÐY`Ð;aó	cñ ñCõ ó ð ùòùòCs%   ¢%E,ÁE,ÁE,ÂAE1
ÃA;E1
Å	E1
c               ó$   € V ^8„  d   QhRRRRRR/# )r   r×  r[   rª  rŸ   r   r%   r   )r   s   "r   r   r   `  s&   € ÷ lñ l�sð l =ð l¸Nñ lr   c                ó   € RRRV RRRRR. RV/# )zFStructured result of an LLM pass that did not run (skipped or failed).r¿  r
  r×  ru  rÈ  r$  rª  r   )r×  rª  s   &&r   Ú	_llm_metar)  `  s$   € à�R˜ G¨W°b¸*ÀbÈ,ÐXZÐ\cÐejÐkÐkr   c               ó$   € V ^8„  d   QhRRRRRR/# )r   Ú
on_summaryúOptional[Callable[[str], None]]Úmessager[   r   rF   r   )r   s   "r   r   r   e  s"   € ÷  ñ  Ð7ð  À#ð  È$ñ  r   c                ó¨   € V '       d6   \         P                  ! \        4      ;_uu_ 4        V ! V4       R R R 4       R # R #   + '       g   i     R # ; iro   )r½   r¾   rL   )r+  r-  s   &&r   Ú_notifyr/  e  s5   € ßÜ× Ò ¤×+Õ+Ù�wÔ÷ ,Ñ+ñ ß+×+Ð+ús   «	A Á A	c               ó   € V ^8„  d   QhRR/# )r   r   r%  r   )r   s   "r   r   r   k  s   € ÷ ñ Ð2ñ r   c                 ó®   € \         P                  ! \        4      ;_uu_ 4        \        P                  ! 4       uuR R R 4       #   + '       g   i     . # ; iro   )r½   r¾   rL   r   rÜ   r   r   r   Ú_safe_curated_reportr2  k  s3   € Ü	×	Ò	œY×	'Õ	'Ü×)Ò)Ó+÷ 
(×	'×	'Ñ	'à€Iús   £AÁA	c          
     ó,   € V ^8„  d   QhRRRRRRRRRR	/# )
r   Úprefixr[   r»  Údry_runrO   rŒ  r°   r   Útupler   )r   s   "r   r   r   q  s1   € ÷ #ñ # ð #°3ð #Àð #ÐU]ð #Ðbgñ #r   c           	     ó®  €  \        4       pRV9   d   V  V R2p\        R4      pMC\         RV 2pV'       d   \         RV 2p\	        V4      pV  V RVP                  RR4       2p  \        T\        P                  ! 4       VP                  R. 4      ;'       g    . VP                  RR4      ;'       g    RR7      p	V	'       d   V RV	 2pWV3#   \         dF   p\        P                  RTR	R
7       T  T RT R2p\        RT R2\        T4      4      p Rp?L´Rp?ii ; i  \         d%   p\        P                  RTR	R
7        Rp?YV3# Rp?ii ; i)uÂ   The LLM half of a run: fork (unless no candidates), then append the rename map (`old-name â†’ umbrella`) so users
needn't dig into REPORT.md. Returns ``(final_summary, llm_meta)``; never raises.zNo agent-created skillsz; llm: skipped (no candidates)zskipped (no candidates)z

z; llm: r×  ú	no changezCurator LLM pass failed: %sTrJ   z; llm: error (r  zerror (Nr$  r¿  r
  )rŒ  r˜  r$  r�  r›  z'Curator rename summary build failed: %s)r&  r)  ÚCURATOR_REVIEW_PROMPTÚCURATOR_DRY_RUN_BANNERÚ_run_llm_reviewrV   rL   r7   r8   r[   r£  r   rÜ   )
r4  r»  r5  rŒ  Úcandidate_listÚfinal_summaryr½  ÚpromptrA   Úrename_liness
   &&&&      r   Ú_consolidation_passr@  q  sw  € ð5Ü/Ó1ˆØ$¨Ô6Ø%˜h | nÐ4RÐSˆMÜ Ð!:Ó;‰Hô .Ð.¨d°>Ð2BÐCˆFßÜ2Ð3°4¸°xÐ@�Ü& vÓ.ˆHØ%˜h | n°G¸H¿L¹LÈÐT_Ó<`Ð;aÐb‰Mð
RÜ,Ø%´K×4NÒ4NÓ4PØ—|‘| L°"Ó5×;Ð;¸ÈÏÉÐV]Ð_aÓIb×IhÐIhÐfhô
ˆ÷ Ø,˜o¨R°¨~Ð>ˆMð Ð"Ð"øô ô 5Ü�‰Ð2°AÀˆÔEØ!˜( < .°¸q¸cÀÐCˆÜ˜w q c¨˜^¬S°«VÓ4�ûð5ûô ô RÜ�‰Ð>ÀÈDˆ×QÐQØÐ"Ð"ûðRúsK   ‚#C ¦C ¹/C Á*2D% ÂD% Â8D% ÃD% ÃD"Ã;DÄD"Ä%EÄ0EÅEc               ó0   € V ^8„  d   QhRRRRRRRRRR	R
R/# )r   r+  r,  ÚsynchronousrO   r5  rš   zOptional[bool]Útriggerr[   r   r%   r   )r   s   "r   r   r   “  sN   € ÷ Uiñ UiØ/ðUiØEIðUiàðUià(6ðUiàHKðUið ñUir   c           	     óp  a aaaaaaaa€ Sf   \        4       MSo\        P                  ! \        4      o\	        4       oS'       d   R\        \        4       4      R^ R^ R^ /oM‡^ RIHp S'       d3    VP                  RR7      pVe   \        S R	VP                   R
24       M;\        P                   ! \        4      ;_uu_ 4        VP#                  4        RRR4       \%        SR7      oRP'                  V3R lR 4       4      ;'       g    RoS'       d   RMRo/ \)        4       CRS S 2/CpS'       gB   VP+                  SP-                  4       \/        VP1                  R^ 4      4      ^,           R7       \3        V4       VVVVVV VVV3	R lp	V'       d	   V	! 4        M=\5        4       p
\6        P8                  ! V
P:                  V	3RRR7      P=                  4        RSP-                  4       RSRS/#   \         d$   p\        P                  RTRR7        Rp?EL?Rp?ii ; i  + '       g   i     ELX; i)u{  Execute a single curator review pass: (1) automatic state transitions (no LLM); (2) if *consolidate* and there are
candidates, fork an AIAgent on the review prompt; (3) update .curator_state; (4) call *on_summary*.
*synchronous* runs the LLM review in the calling thread (default: daemon thread). *consolidate* ``None`` reads
``curator.consolidate`` (OFF by default); when off only the deterministic prune runs â€” no fork, no aux cost.
*dry_run* SKIPS the stale/archive transitions and instructs the fork to report only; REPORT.md is still written and
recorded in ``state.last_report_path`` so users can read what WOULD have happened.NrÉ   rÆ   rÇ   rÈ   )Úcurator_backupzpre-curator-run)rH  zcurator: snapshot created (r  z#Curator pre-run snapshot failed: %sTrJ   )r¥   rý  c              3  óf   <"  € T F&  w  rSV,          '       g   K  SV,           R V 2x € K(  	  R# 5i)Ú Nr   )rù   r^   rb   rÐ   s   &  €r   rü   Ú%run_curator_review.<locals>.<genexpr>³  sK   øé € ð ñ 4P¡Z Sð  TZð  [^÷  T_ñ  T_Ô ˆ6�#�;ˆ-�q˜˜Õ ó  4Pùs   ƒ1›1z
no changeszdry-run auto: zauto: r)   r-   )r'   r-   c                 ó,  <	€ \        4       p \        \        V 4      4      pS
'       d   \        SS	SV4      w  r#MS S	 R 2p\	        R4      p\
        P                  ! \        4      S,
          P                  4       p/ \        4       CRVRV/Cp\        4       p \        SVSS	WWcR7      pVe   \        V4      VR&   \        T4       \!        SSSYTS4       \#        SR
T 24       R#   \         d#   p\        P                  RTRR	7        Rp?LTRp?ii ; i)z"; llm: skipped (consolidation off)zskipped (consolidation off)r(   r)   )r¸  r¹  rº  r»  r¼  rŒ  r˜  r½  Nr+   zCurator report write failed: %sTrJ   z	curator: )r2  r‡   r–  r@  r)  r   r¥   r   Útotal_secondsrD   rê  r[   rL   r7   r8   rM   Ú_record_run_metricr/  )r¼  rŒ  r=  r½  ÚelapsedÚstate2r˜  Úreport_pathrA   r»  rš   rÐ   r5  Úhermes_homer+  r4  ÚstartrC  s            €€€€€€€€€r   Ú	_llm_passÚ%run_curator_review.<locals>._llm_passÀ  s!  ø€ ä,Ó.ˆÜœ8 MÓ2Ó3ˆßÜ&9¸&À,ÐPWÐYeÓ&fÑ#ˆM˜8ð  &˜h | nÐ4VÐWˆMÜ Ð!>Ó?ˆHÜ—<’<¤Ó$ uÕ,×;Ñ;Ó=ˆØj”J“LÐjÐ"=¸wÐHZÐ\iÑjˆä+Ó-ˆð	NÜ+Ø °'ÀvÐ\hØ+ÐUaôˆKð Ò&Ü-0°Ó-=�Ð)Ñ*ô 	�6ÔÜ˜7 G¨V°]ÐRZÐ\gÔhÜ�
˜i¨ Ð7Ö8øô	 ô 	NÜ�L‰LÐ:¸AÈˆL×MÑMûð	Nús   Â#C& Ã&DÃ1DÄDzcurator-review)r9  r  Údaemonr·   r¸  rÉ  Úsummary_so_far))rÆ   zmarked stale)rÇ   rÇ   )rÈ   rÈ   )rœ   r   r¥   r   r   rœ  r2  r  rE  Úsnapshot_skillsr/  r·   rL   r7   r8   r½   r¾   Úprune_old_snapshotsrè   r�  rD   r;   rª   r{   rV   rM   r   Ú	threadingÚThreadÚrunrP  )r+  rB  r5  rš   rC  rE  ÚsnaprA   r¬   rQ  Úctxr»  rÐ   rO  r4  rP  s   f&fff      @@@@@r   Úrun_curator_reviewr\  “  sæ  ÿø€ ð (3Ò':”/Ô#À€KÜ�LŠLœÓ€EÜ!Ó#€KßØœSÔ!5Ó!7Ó8¸.È!ÈZÐYZÐ\iÐklÐm‰å(÷ ðVØ%×5Ñ5Ð=NÐ5ÓO�ØÒ#Ü˜JÐ*EÀdÇiÁiÀ[ÐPQÐ(RÔSøô ×$Ò$¤Y×/Õ/Ø×2Ñ2Ô4÷ 0ä,°Ô7ˆØ—9‘9ô ñ 4Póó ÷ ð à	ð ÷ ")Ñ¨h€FØKŒz‹|ÐKÐ/°F°8¸L¸>Ð1JÑK€EßØ�‰ §¡Ó!2¼cÀ%Ç)Á)ÈKÐYZÓB[Ó>\Ð_`Õ>`ˆÔaÜˆuÔ÷9õ 9÷6 Ù�ô ‹nˆÜ×Ò §¡¨y¨lÀ4ÐN^Ô_×eÑeÔgØ˜%Ÿ/™/Ó+Ð-?ÀÐIYÐ[gÐhÐhøôw ô VÜ—‘ÐBÀAÐPT�×UÒUûðVú÷ 0×/Ð/ús$   Á10G3 ÃH$Ç3H!Ç>HÈH!È$H5	c               ó   € V ^8„  d   QhRR/# ©r   r   rF   r   )r   s   "r   r   r   í  s   € ÷ gñ gÐhlñ gr   c                ó�   aaaa€ ^ RI Hp R VVVV3R llpV'       d   RMSP                  R4      '       d   RMRp	V! W	W†R7       R	# )
é    )Úrecord_curator_runc               ó   € V ^8„  d   QhRR/# )r   r   rÃ   r   )r   s   "r   r   Ú(_record_run_metric.<locals>.__annotate__ð  s   € ÷ Fñ F˜.ñ Fr   c                 ó   <aa€ \        S4      \        S4      uoo\        \        S4      \        S4      SP                  R 4      ;'       g    . SP                  R4      ;'       g    R4      p \	        VV3R lV P
                  \        S4      ,           4       4      pR\        SP                  R^ 4      4      \        V P                  4      ,           R\        V P                  4      R\        V P                  4      RV/# )r$  r¿  r
  c              3  óÒ   <"  € T F\  p\        SV,          P                  R 4      ;'       g    ^ 4      \        SV,          P                  R 4      ;'       g    ^ 4      8„  x € K^  	  R# 5i)r   N)r{   rV   )rù   r  rË  rÊ  s   & €€r   rü   Ú;_record_run_metric.<locals>.what_changed.<locals>.<genexpr>ó  sS   øé € ð ?Ù=˜!ô ˜% �(Ÿ,™, }Ó5×:Ð:¸Ó;¼cÀ&ÈÅ)Ç-Á-ÐP]ÓB^×BcÐBcÐbcÓ>dÖdÛ=ùs   ƒ(A'¬)A'ÁA'rÇ   ÚcreatedÚmergedÚpatched)r–  r‘  r‡   rV   rà  r#  r{   rœ  r   r"  r3  )rž  ri  rË  rÊ  r˜  r¼  rÐ   r½  s     @@€€€€r   Úwhat_changedÚ(_record_run_metric.<locals>.what_changedð  sÓ   ú€ Ü  Ó/´¸,Ó1Gˆˆ�Ü!¤# f£+¬s°5«z¸8¿<¹<ÈÓ;U×;[Ð;[ÐY[Ð]e×]iÑ]iÐjqÓ]r×]xÐ]xÐvxÓyˆÜõ ?Ø#×/Ñ/´#°f³+Ö=ó?ó ?ˆàœC §
¡
¨:°qÓ 9Ó:¼SÀÇÁÓ=NÕNÐPYÔ[^Ð_c×_iÑ_iÓ[jØœ#˜d×/Ñ/Ó0°)¸WðFð 	Fr   Úskippedrª  ÚfailedÚsuccess)rC  ÚoutcomerÐ   rO  N)Ú,hermes_cli.observability.shared_metrics_loopra  rV   )
rC  r5  rÐ   r¼  r˜  r½  rO  ra  rj  ro  s
   &&ffff&   r   rK  rK  í  s7   û€ ÝO÷Fó F÷ #‰i°H·L±LÀ×4IÒ4I©Èy€GÙ˜wÀ×fr   c                  óN   € ] tR tRt$ RtR]R&   R]R&   R]R&   R]R&   R	]R
&   RtR# )Ú_ReviewRuntimeBindingiü  zCProvider/model for the curator review fork plus per-slot overrides.r[   rÈ  ru  rŸ   Úexplicit_api_keyÚexplicit_base_urlr%   Úrequest_overridesr   N)r…  r†  r‡  rˆ  Ú__doc__r‰  rŠ  r   r   r   rr  rr  ü  s!   ‡ ÙMØƒMØƒJØ#Ó#Ø$Ó$Ø%×%r   rr  c               ó$   € V ^8„  d   QhRRRRRR/# )r   Úruntime_overridesr
   Úslot_extra_bodyr   r%   r   )r   s   "r   r   r     s!   € ÷ ñ °ð Àcð Ènñ r   c                ó°   € \        T ;'       g    / 4      p\        V\         4      '       d+   V'       d#   / VP                  R4      ;'       g    / CVCVR&   V# )z<Merge resolver metadata with task-local request body fields.Ú
extra_body)r:   r9   rV   )rx  ry  rh  s   && r   Ú_merge_request_overridesr|    sN   € äÐ#×)Ð) rÓ*€FÜ�/¤4×(Ò(¯_ØV 6§:¡:¨lÓ#;×#AÐ#A¸rÐVÀoÐVˆˆ|ÑØ€Mr   c               ó    € V ^8„  d   QhRRRR/# )r   rg   r%   r   rr  r   )r   s   "r   r   r     s   € ÷ Añ A ð AÐ4Iñ Ar   c                óô  € R R lp\        V RR4      pVP                  R4      ;'       g    RP                  4       ;'       g    RpVP                  R4      ;'       g    RP                  4       ;'       g    RpV'       d   VR	8w  d   V'       d
   V! W4V4      # \        V RR4      pVP                  R4      '       dW   VP                  R4      '       d@   \        P	                  R
4       V! \        VR,          4      \        VR,          4      V4      # \        V R4      p\        VP                  R4      ;'       g    R	VP                  R4      ;'       g    VP                  R4      ;'       g    RRR/ 4      # )aý  Curator is a regular auxiliary task slot (``auxiliary.curator.*``), so it rides the canonical aux-model plumbing. Precedence:
  1. ``auxiliary.curator.{provider,model}`` when both are set non-auto
  2. Legacy ``curator.auxiliary.{provider,model}`` (deprecated) when both set
  3. Main ``model.{provider,default/model}`` pair ("auto" + "" = main chat model)
Non-empty slot ``api_key``/``base_url`` are returned as explicit overrides so ``resolve_runtime_provider`` doesn't reuse the main chat credential chain.c               ó(   € V ^8„  d   QhRRRRRRRR/# )r   rÈ  r[   ru  Úslotr%   r   rr  r   )r   s   "r   r   Ú-_resolve_review_runtime.<locals>.__annotate__  s/   € ÷ ñ ˜ð  Cð ¨~ð ÐBWñ r   c                ó¤   € R  VP                  R4      VP                  R4      3 4       w  r4\        WW4\        / VP                  R4      4      4      # )c              3  ór   "  € T F-  qe$   \        V4      P                  4       ;'       g    R MR x € K/  	  R # 5iro   )r[   rI  )rù   rC   s   & r   rü   Ú9_resolve_review_runtime.<locals>._slot.<locals>.<genexpr>  s8   é € ð  Gñ  [FÐUVºœc !›fŸl™l›n×4Ð4°øÈDÔPó  [Fùs   ‚'7ª7Úapi_keyÚbase_urlr{  )rV   rr  r|  )rÈ  ru  r€  r…  r†  s   &&&  r   Ú_slotÚ&_resolve_review_runtime.<locals>._slot  se   € ñ GÐ[_×[cÑ[cÐdmÓ[nÐpt×pxÑpxð  zDó  qEñ  [Fó  GÑˆÜ$ X°gÔIaÐbdÐfj×fnÑfnÐo{Óf|ÓI}Ó~Ð~r   Ú	auxiliaryrk   rÈ  r
  Nru  r  u|   curator: using deprecated curator.auxiliary.{provider,model} config â€” please migrate to auxiliary.curator.{provider,model}rr   )r_   rV   rI  r7   Úinfor[   rr  )rg   r‡  ÚtaskÚtask_providerÚ
task_modelÚlegacyÚmains   &      r   Ú_resolve_review_runtimer�    s?  € õô �C˜ iÓ0€DØ—X‘X˜jÓ)×/Ð/¨R×6Ñ6Ó8×@Ð@¸D€MØ—(‘(˜7Ó#×)Ð) r×0Ñ0Ó2×:Ð:°d€Jß˜¨&Ô0·ZÙ�]°Ó5Ð5Ü�c˜9 kÓ2€FØ‡z�z�*×Ò &§*¡*¨W×"5Ò"5Ü�‰ð  Sô  	TÙ”S˜ 
Õ+Ó,¬c°&¸µ/Ó.BÀFÓKÐKÜ�C˜Ó!€DÜ  §¡¨*Ó!5×!?Ð!?¸ÀÇÁÈ)ÓAT×AoÐAoÐX\×X`ÑX`ÐahÓXi×AoÐAoÐmoÐquÐw{Ð}ó  Að  Ar   c               ó   € V ^8„  d   QhRR/# )r   r   r6  r   )r   s   "r   r   r   $  s   € ÷ /ñ / %ñ /r   c            	     ó²  € / p / RRR3w  rr4 ^ RI Hp ^ RIHp \	        V! 4       4      pVP
                  pV! VP                  VP
                  VP                  VP                  R7      p V P                  R4      ;'       g    VP                  p\        V P                  R4      VP                  P                  R4      4      p\        V P                  R	4      \        4      '       d5   V R	,          P                  4       '       d   V R	,          P                  4       pYY!3#   \         d-   p\         P#                  R
\%        TRR4      Y74        Rp?L6Rp?ii ; i)a  ``(runtime_provider, model_name, provider_name, request_overrides)`` resolved the way the CLI does: AIAgent() without
explicit provider/model hits an auto-resolution path that fails for OAuth-only providers and pooled credentials
(HTTP 400 "No models provided"). Never raises.Nr
  rd   )Úresolve_runtime_provider)Ú	requestedÚtarget_modelrs  rt  rÈ  ru  r{  ru  u}   curator: auxiliary.curator.provider '%s' (model '%s') could not be resolved: %s â€” the review runs on the main model instead)rf   re   Úhermes_cli.runtime_providerr“  r�  ru  rÈ  rs  rt  rV   r|  ru  r9   r[   rI  rL   r7   rŒ   Úgetattr)ÚrpÚ	overridesrÈ  Ú
model_nameÚbindingre   r“  rA   s           r   Ú_resolve_review_providerrœ  $  s1  € ð €BØ/1°4¸¸TÐ/AÑ,€I˜ðlÝ:ÝHÜ)Ñ*>Ó*@ÓAˆØ—]‘]ˆ
Ù%Ø×&Ñ&°W·]±]Ø$×5Ñ5È×IbÑIbô
ˆð —6‘6˜*Ó%×9Ð9¨×)9Ñ)9ˆÜ,¨R¯V©VÐ4GÓ-HÈ'×JcÑJc×JgÑJgÐhtÓJuÓvˆ	Ü�b—f‘f˜W“o¤s×+Ò+°°7µ×0AÑ0A×0CÒ0CØ˜G�×*Ñ*Ó,ˆJð ˜8Ð.Ð.øô ô lÜ�‰ð 8Ü9@ÀÈ*ÐVZÓ9[Ð]g÷	lñ 	lûðlús   �C6D ÄD ÄEÄ*"EÅEc               ó    € V ^8„  d   QhRRRR/# )r   r>  r[   r   r%   r   )r   s   "r   r   r   =  s   € ÷ Hñ H˜Cð H Nñ Hr   c                óÊ  € \        R4      p ^ RIHp \	        4       w  rErgYV;'       g    RuTR&   TR&   Rp / p	TP                  R	4      p
\        T
\        4      '       d>   T
'       d6   T	P                  T
\        TP                  R
4      ;'       g    . 4      R7       ^ RI
Hp ^ RIHp T! R1RTRTRTP                  R4      RTP                  R4      RTP                  R4      RTP                  R4      RT/T	BRT! T! 4       T4      RR.RRRRRRRRRR/B p^ Tn        ^ Tn        RTn        \"        P$                  ! \        4      ;_uu_ 4        ^ RIHp T! 4        RRR4       \+        \,        P.                  R R!R"7      ;_uu_ 4       p\"        P0                  ! T4      ;_uu_ 4        \"        P2                  ! T4      ;_uu_ 4        TP5                  T R#7      pRRR4       RRR4       RRR4       \        X\6        4      '       d3   \        TP                  R$4      ;'       g    R4      P9                  4       MRpTTR%&   \;        T4      ^ð8”  d   TR&,          R',           MT;'       g    R(TR&   R) \=        TR*. 4      ;'       g    .  4       pT UUu. uFs  pTP                  R+4      ;'       g    RpR,TP                  R,4      ;'       g    RR+\        T\        4      '       d!   \;        T4      R-8”  d   TR.,          R',           MT/NKu  	  uppTR/&   Te>   \"        P$                  ! \        4      ;_uu_ 4        TP?                  4        RRR4       T#  T#   \         d   pRT 2;TR&   TR&   Tu Rp?# Rp?ii ; i  + '       g   i     EL*; i  + '       g   i     ELÁ; i  + '       g   i     ELÍ; i  + '       g   i     ELÙ; iu uppi   \         d   pR0T 2;TR&   TR&    Rp?LÞRp?ii ; i  + '       g   i     T# ; i  TeP   \"        P$                  ! \        4      ;_uu_ 4        TP?                  4        RRR4       i   + '       g   i     i ; ii ; i)2záSpawn an AIAgent fork on the review prompt. Returns ``final`` (untruncated response), ``summary`` (240-char cap),
``model``/``provider`` (what ran), ``tool_calls`` ([{name, arguments}], truncated) and ``error``. Never raises.r
  )ÚAIAgentzAIAgent import failed: rª  r×  Nru  rÈ  Úcommandr  )Úacp_commandÚacp_argsrd   )Úresolve_reasoning_configr…  r†  Úapi_modeÚcredential_poolru  Úreasoning_configÚenabled_toolsetsr!   Úmax_iterationsi'  Ú
quiet_modeTÚplatformrk   Úskip_context_filesÚskip_memoryÚbackground_review)Ú#_reset_background_review_read_marksÚwr²  r.   )Úuser_messageÚfinal_responser¿  :Néð   Nu   â€¦r8  c              3  óü   "  € T Fr  p\        V\        4      '       g   K  VP                  R 4      ;'       g    .  F8  p\        V\        4      '       g   K  VP                  R4      ;'       g    / x € K:  	  Kt  	  R# 5i)r$  ÚfunctionNr]   )rù   Úmsgr  s   &  r   rü   Ú"_run_llm_review.<locals>.<genexpr>y  sk   é € ð PÑ3g¨CÔkuÐvyÔ{÷  lAÔ'ØŸ'™' ,Ó/×5Ð5°2Ò5�2¼*ÀRÌ×:Nô (ˆr�v‰v�jÓ!×'Ð' RÔ'Ù5ñ (Ó3gùs   ‚A<ŸA<·A<ÁA<Á-A<Ú_session_messagesr	  r·   i�  :Ni�  Nr$  zerror: r   ) r)  Ú	run_agentrŸ  rL   rœ  rV   r9   r[   r;   rL  rf   re   Úhermes_constantsr£  Ú_memory_nudge_intervalÚ_skill_nudge_intervalÚ_memory_write_originr½   r¾   Útools.skill_manager_guardsr®  ÚopenÚosÚdevnullÚredirect_stdoutÚredirect_stderrÚrun_conversationr:   rI  rœ  r—  Úclose)r>  Úresult_metarŸ  rA   r˜  rš  rÈ  ru  Úreview_agentÚagent_kwargsr¡  re   r£  r®  rÀ  Úconv_resultr¿  ÚfnsÚfnr6  s   &                   r   r;  r;  =  s,  € ô #,¨B£-€KðÝ%ô 3KÓ2LÑ/€B�HØ4>ÇÀÈBÐ1€K�Ñ˜+ jÑ1Ø€Lð;%Ø')ˆØ—f‘f˜YÓ'ˆÜ�k¤3×'Ò'¯KØ×Ñ¨KÄ$ÀrÇvÁvÈfÃ~×G[ÐG[ÐY[ÓB\ÐÔ]Ý:Ý=áñ 
Øð
Ø'/ð
Ø9;¿¹À	Ó9Jð
ØUW×U[ÑU[Ð\fÓUgð
à—V‘V˜JÓ'ð
à9;¿¹Ð@QÓ9Rð
ð 0ð
ð 4@ñ
ñ 6Ñ6JÓ6LÈjÓYð
ð '˜Zð
ð  ð
ð ð
ð '0ð
ð EIð
ð W[ò
ˆð" /0ˆÔ+Ø-.ˆÔ*ð -@ˆÔ)ô × Ò ¤×+Õ+ÝVá/Ô1÷ ,ô
 ”"—*‘*˜c¨G×4Õ4¸Ü×'Ò'¨×0Õ0´*×2LÒ2LÈW×2UÕ2UØ&×7Ñ7ÀVÐ7ÓLˆK÷ 3V×0÷ 5ô ISÐS^Ô`d×HeÒHe”�K—O‘OÐ$4Ó5×;Ð;¸Ó<×BÑBÔDÐkmˆØ$ˆ�GÑÜ:=¸e»*ÀsÔ:J %¨¥+°Ö"5ÐQV×QeÐQeÐZeˆ�IÑñP´7¸<ÐI\Ð^`Ó3a×3gÐ3gÐegÐ3gó Pˆñ ô%
á� B§F¡F¨;Ó$7×$=Ð$=¸2˜að �R—V‘V˜F“^×)Ð) r¨;Ì:ÐVWÔY\×K]ÒK]ÔbeÐfgÓbhÐknÔbn¸¸$½À%¾ÐtuÓvÙò%
ˆ�LÑ!ð Ò#Ü×$Ò$¤Y×/Õ/Ø×"Ñ"Ô$÷ 0àÐð $ð ÐøôE ô Ø:QÐRSÐQTÐ8UÐUˆ�GÑ˜{¨9Ñ5ØÕûðú÷T ,×+Ð+ú÷ 3V×2UÐ2Uú×0×0Ð0ú÷ 5×4Ð4üó%
øô ô FØ:AÀ!À¸ÐEˆ�GÑ˜{¨9Ö5ûðFú÷ 0Ö/àÐûð Ò#Ü×$Ò$¤Y×/Õ/Ø×"Ñ"Ô$÷ 0×/×/Ð/úð $ús  �M ·0O Á((O ÂB>O ÅM>Å,O Æ	N:Æ(N&	ÇNÇN&	Ç"N:Ç*:O È%AO É*O ÊO ÊOÊ0OËA OÌO Ì9O9ÍM;Í"M6Í0M;Í6M;Í>N	Î		O ÎN#Î	N&	Î&N7Î1	N:Î:O	ÏO ÏO6ÏO1Ï,P Ï1O6Ï6P Ï9P
	Ð'Q"Ð4Q	Ñ	Q"ÑQÑ	Q"ç      ¬@c               ó   € V ^8„  d   QhRR/# r   r   )r   s   "r   r   r   �  s   € ÷ Cñ C˜ñ Cr   c                 ó@   € \        4       R ,          R,          R,          # )r!   z.lockszcurator-runr   r   r   r   Ú_run_claim_pathrÎ  �  s   € ÜÓ˜xÕ'¨(Õ2°]ÕBÐBr   c               ó   € V ^8„  d   QhRR/# rT   r   )r   s   "r   r   r   ‘  s   € ÷ ñ �Dñ r   c                 óì  € \        4       p  V P                  P                  RRR7        \        P
                  ! T \        P                  \        P                  ,          \        P                  ,          4      p\        P                   ! TRRR7      ;_uu_ 4       pTP#                  \%        \        P&                  ! 4       4      4       RRR4       R#   \         d     R# i ; i  \         dq     \        P                  ! 4       T P                  4       P                  ,
          \        8”  d   T P                  4        \        4       u # M  \         d     Mi ; i R# i ; i  + '       g   i     R# ; i)a  One automatic pass per home across processes: two CLIs launched seconds apart both saw the
weekly interval elapsed and both pruned the tree. O_EXCL create wins the claim; a claim older than
an hour is a crashed holder and is taken over. When the lock dir cannot be created, run anyway.Trì   Fr¯  r²  r.   N)rÎ  Úparentrï   r5   r¿  r¾  ÚO_CREATÚO_EXCLÚO_WRONLYÚFileExistsErrorÚtimeÚstatÚst_mtimeÚ_CLAIM_STALE_SECONDSÚunlinkÚ
_claim_runÚfdopenÚwriter[   Úgetpid)ÚlockÚfdÚfhs      r   rÛ  rÛ  ‘  s  € ô Ó€DðØ�‰×Ñ $°ÐÔ6ð	Ü�WŠW�Tœ2Ÿ:™:¬¯	©	Õ1´B·K±KÕ?Ó@ˆô 
�Š�2�s W×	-Õ	-°Ø
�‰””R—Y’Y“[Ó!Ô"÷ 
.áøô ô Úðûô ô ð	Ü�yŠy‹{˜TŸY™Y›[×1Ñ1Õ1Ô4HÔHØ—‘”Ü!“|Ò#ð Iøô ô 	Ùð	úâðú÷ 
.Ö	-áúsZ   ŒC ªAC$ Â.E"ÃC!Ã C!Ã$EÃ0AE
ÅEÅ	EÅ
EÅEÅEÅEÅEÅ"E3	c               ó   € V ^8„  d   QhRR/# r^  r   )r   s   "r   r   r   ©  s   € ÷ #ñ #˜Dñ #r   c                 ó´   € \         P                  ! \        4      ;_uu_ 4        \        4       P	                  4        R R R 4       R #   + '       g   i     R # ; iro   )r½   r¾   r5   rÎ  rÚ  r   r   r   Ú_release_run_claimrä  ©  s1   € Ü	×	Ò	œW×	%Õ	%ÜÓ× Ñ Ô"÷ 
&×	%×	%Ò	%ús   £AÁA	Úidle_for_secondsr+  c               ó$   € V ^8„  d   QhRRRRRR/# )r   rå  zOptional[float]r+  r,  r   r  r   )r   s   "r   r   r   ®  s'   € ÷ ñ ¨?ð ÐOnð ð  {Sñ r   c                ó0  €  \        4       '       d   V e   V \        4       R,          8  d   R# \        4       '       g   R#  \        VRR7      \	        4        #   \	        4        i ; i  \
         d$   p\        P                  RTRR7        Rp?R# Rp?ii ; i)zzBest-effort: run a curator pass if all gates pass. Returns the result dict if a pass was started, else None. Never raises.NrË  Ú	scheduled)r+  rC  zmaybe_run_curator failed: %sTrJ   )r®   r†   rÛ  r\  rä  rL   r7   r8   )rå  r+  rA   s   $$ r   Úmaybe_run_curatorré  ®  sw   € ðä×ÒÐ$4Ò$@ÐEUÔXjÓXlÐouÕXuÔEuÙÜ�|Š|Ùð	!Ü%°À[ÔQäÕ øÔÕ ûÜô Ü�‰Ð3°QÀˆÔFÝûðús4   ‚*A' ®A' ÁA Á
A' ÁA$Á$A' Á'BÁ2BÂBro   )NFFNÚmanual)mÚ__conditional_annotations__rv  Ú
__future__r   r½   r2   Úloggingr¿  r  rW  rÖ  Úcollectionsr   Úcontextvarsr   r   r   r   r   Úpathlibr	   Útypingr
   r   r   r   r   r   r   r¹  r   Úagent.skill_utilsr   rÛ   r   Úutilsr   Ú	getLoggerr…  r7   r   r…   r‘   r–   r›   r"   rD   rM   rR   rW   r_   rh   rl   rt   rx   r€   r†   r‡   rˆ   r‰  r~   r’   r—   rœ   r£   r®   rµ   rÁ   rè   r:  r9  rñ   r  r  r  r:  rZ  rc  r‚  r„  r‘  r–  r£  r­  r¶  rê  rî  rõ  rø  r  r  rÚ  r&  r)  r/  r2  r@  r\  rK  rr  r|  r�  rœ  r;  rÙ  rÎ  rÛ  rä  ré  )rë  s   @r   Ú<module>rõ     s¦  øðôoõ #ã Û Û Û 	Û 	Û Û Ý Ý $ß 7Ó 7Ý ß G× GÑ Gå ,Ý 6Ý Ý #à	×	Ò	˜8Ó	$€à17¸Ð .Ð Ð.Ø79¸2Ð 4Ð Ð4ð Ð õ
;õõ Kõ9õ,õ2ð Àv÷  õ<õõ5õDõ
Kñ ›%Ð �CÓ õõ HõLõHõ÷Aõ0õ(÷4ðtDð ð2pMð õnõ@õ4õ	õ<õ6tõ2
÷+<ô\!ˆzô !õ
kõEõõ,Wõ>õ*õZWõAõtõ
]ð Ð8ðJàK^Ð`bÐdtðvð Ð2ðpà�BÐ(ð*ð Ð#ð  &AÙ˜T 2ð'àÐ-¨tÑ5zð  }Að  CEð  FØÐ;ðKØL_ÐacÐeðAðÐ õ$%õT÷@lõ
 õõ#÷DUiõtgô&˜Jô &õõAõ./õ2HðZ Ð õCõõ0#ð
¸Tð Ðqu÷ ñ r   