Ë
    âß²jÖ ã                   ó¶  — U d Z ddlZddlZddlZddlZddlZddlZddlZddl	Z	ddl
Z
ddlZddlZddlZddlZddlZddlmZ ddlmZmZmZ 	 ddlZddlmZ ddlmZmZmZmZmZm Z m!Z! ejD                  jG                  d e$ ee%«      jL                  jL                  «      «       ddl'm(Z(m)Z) ddl*m+Z+ dd	l,m-Z- dd
l.m/Z/m0Z0 ddl1m2Z2 ddl3m4Z5 ddl6m7Z7 ddl8m9Z9m:Z: ddl;m<Z< ddl=m>Z>  ej~                  e@«      ZA�d-d„ZBd„ ZCde$fd„ZDdeEde$fd„ZFdeGe$e$f   dz  fd„ZHde$dz  fd„ZI G d„ deJ«      ZK e
j˜                  d«      ZMde$dz  deGe$e$f   dz  fd„ZNdZOi aPeEe$eQf   eRd<   deGe$e$f   dz  fd „ZSd!e$ddfd"„ZTdeEd#e$dz  de$fd$„ZUd%ZVdeWfd&„ZXd'eEdeYfd(„ZZdd)œdeEd#e$d*ee   deGeYee$   f   fd+„Z[deEddfd,„Z\d-ee$   ddfd.„Z] G d/„ d0e^«      Z_d1eEde`e$   fd2„Zad3e`e$   d1eEde`e$   fd4„ZbdeEd1eEde`e$   fd5„ZcdeEd1eEd6e$deEdz  fd7„Zddd8lemfZfmgZgmhZhmiZimjZjmkZkmlZlmmZmmnZnmoZompZpmqZqmrZrmsZs dd9ltmuZumvZvmwZwmxZxmyZymzZzm{Z{m|Z| d:Z}d;Z~d<e$dee$   fd=„Zd<e$deYfd>„Z€i Z�ee$e�j                  �j                  f   eRd?<   i Z„ee$ee…   f   eRd@<   �d.dAe$dBee!ee$f      deGfdC„Z†i Z‡ee$ef   eRdD<   dBedefdE„ZˆdFe$defdG„Z‰ eŠ«       Z‹eŠeRdH<   i ZŒeEeGeEeQeGee$   ef   f   f   eRdI<    eŠ«       Z�eŠeRdJ<   i ZŽeEeGe…f   eRdK<    e�j                  «       Z�i Z‘eEeRdL<   i Z’eEeRdM<   i Z“eEeRdN<    eQ«       Z”da•e…eRdO<   g Z–e`eRdP<   dQZ—dRZ˜ eŠ«       Z™eŠeRdS<    G dT„ dUe «      Zš G dV„ dW«      Z›�d/dX„Zœde`eE   fdY„Z��d/dZ„Zž�d.dAe$dBee!ee$f      deYfd[„ZŸdAe$deYfd\„Z �d.dAe$dBee!ee$f      ddfd]„Z¡deWfd^„Z¢i Z£eEeRd_<   d`e$deeW   fda„Z¤deEdeeW   fdb„Z¥deEfdc„Z¦dAe$dde$deeWdfeWddf
dg„Z§deEfdh„Z¨dieEdjeWdeYfdk„Z©deEdAe$dleWdmeWd!e$ddfdn„Zª�d.doee`   de`fdp„Z«ddqœd!e$dreeŠ   de`fds„Z¬�d.dAe$dteeQ   deYfdu„Z­�d.dAe$dteeQ   deYfdv„Z®dweg eWf   dxeWdyeWdze�j^                  d{eg eYf   deYfd|„Z°deWfd}„Z±d~ee…   de�j                  �j                  fd„Z²�d0d€„Z³�d0d�„Z´ e�jj                  e´«       defd‚„Z¶de$fdƒ„Z·d„eEddfd…„Z¸�d.d†ee¹   deYfd‡„ZºdZ»edz  eRdˆ<   defd‰„Z¼deGeef   fdŠ„Z½d‹e¾deYfdŒ„Z¿d<e$deYfd�„ZÀd†e¹deYfdŽ„ZÁ�d0d�„ZÂde…fd�„ZÃd‘ZÄeÄZÅd’ZÆeÆd“z  ZÇd”ZÈdeWfd•„ZÉdd–œdAe$d—e$d˜eeW   deYfd™„ZÊ G dš„ d›«      ZËdœe$dAe$d�e$dže$de$f
dŸ„ZÌdeEdAe$dee$   fd „ZÍdeEdAe$dœe$deGeYe$e$ee$   f   fd¡„ZÎdeEdAe$dœe$d¢ee$   deGeeG   ee$   ee$   f   f
d£„ZÏe G d¤„ d¥«      «       ZÐdeEdAe$dœe$deÐfd¦„ZÑd1eEdAe$dee`   fd§„ZÒdeEdAe$dœe$d1eEdeeG   f
d¨„ZÓdAe$dœe$d©e$deGfdª„ZÔdeEdAe$d«eÐdeGeEe$f   fd¬„ZÕd­eEdAe$fd®„ZÖdAe$ddfd¯„Z×deEfd°„ZØdœe$dxeWddfd±„ZÙ	 �d.d²e$deEdAe$dœe$d³e$d´eeE   deEfdµ„ZÚd¶eEdAe$dœe$de$fd·„ZÛdAe$dœe$d¸e$ddfd¹„ZÜdeEdºe$dAe$d²e$de$f
d»„ZÝeGeYe$e$ee$   f   ZÞdeEdAe$dœe$d¢ee$   deGeeÞ   ee$   f   f
d¼„Zßd½Zà G d¾„ d¿«      ZádeEddfdÀ„Zâe G dÁ„ dÂ«      «       ZãdeEdAe$dœe$deãfdÃ„ZädeEdÄeEdÅeãfdÆ„Zå G dÇ„ dÈ«      ZædddddÉœdeEdÊee`   d¢ee$   dËeeš   dÌee$   deGeYe$e$ee$   f   fdÍ„Zçdd–œdAe$d˜eeW   ddfdÎ„ZèdeEdeYfdÏ„ZédddÐdddÑœdeEdÒeYd¢ee$   dËeeš   deYf
dÓ„ZêdÔZëdAe$dÕee$   dÌe$ddfdÖ„ZìddÐd×œdØeYdÙeYdÚe$dÛeYdÜeYdÝeYde$fdÞ„ZídÐdßœdeEdÜeYdàe$dáeYdeGe$eYeYeYee$   f   f
dâ„Zî G dã„ däe^«      Zï G då„ dæ«      Zðe G dç„ dè«      «       Zñdéeñdêeðdàe$dëe$dÒeYddfdì„ZòdeEdÌe$díee$   ddfdî„ZódéeñdÕee$   dÌe$deYfdï„ZôdeEdðe$deGee$   e$f   fdñ„ZõdddÐdddddòœdeEdÒeYd¢ee$   dóeeš   dôeeš   dõeeQ   deYfdö„Zöd÷e�jî                  dÌe$deYfdø„Zøddùdúœd÷e�jî                  dÌe$dAee$   dûeGedüf   deYf
dý„ZùdeEdeYfdþ„Zúdÿe�d edeYf�d„Zû�d0�d„Zü G �d„ �deJ«      ZýdeEf�d„Zþ�dZÿi �Z ee$eWf   eR�d<   �d�Zd�aeeW   eR�d	<    e�j                  «       �Zdee$   f�d
„�Z�d0�d„�Z�d„ �Z�d0�d„�Z�d0�d„�Zdoe`ddf�d„�Z	dee…   f�d„�Z
�d0�d„�ZdeEdÒeYdeYf�d„�ZdeE�de�j                  �j                  f�d„�Z�de`ddf�d„�Zd�d�l�m�Z d�d�l�m�Z�m�Z�m�Z�m�Z�m�Z d�d�l�m�Z�m�Z�m�Z d�d�l�m�Z�m�Z�m�Z�m�Z d�d�l �m!�Z!�m"�Z"�m#�Z#�m$�Z$�m%�Z%�m&�Z& e@�dk(  rÌ�de�jN                  v r°dd�l(�Z( �e(�jR                  dÐ�¬«      �Z*�e*�jW                  �de�d�¬ «       �e*�jW                  �d!e�d�¬ «       �e*�jY                  «       �Z-	 d�d"�l.�m/�Z/  �e/ e¼«       �d#�¬$«        �e0 eû�e-�jb                  �e-�jd                  «      rd«      ‚�d%«      ‚ �e�d�¬&«       dd�l3�Z3dd�l4�Z4dd�l5�Z5�d'�d(�d)�d*�d+œ�Z6�d,„ �Z7y# e$ r dZ	 ddlZn# e$ r dZY nw xY wY �	Œûw xY w# e^$ r Y ŒŠw xY w(1  z»Cron job scheduler: tick() runs due jobs (gateway calls it every 60s from a background thread).
A file lock (~/.hermes/cron/.tick.lock) keeps overlapping processes to one tick at a time.
é    N)Ú	dataclass)ÚdatetimeÚ	timedeltaÚtimezone)ÚPath)ÚAnyÚCallableÚDictÚListÚOptionalÚProtocolÚUnion)Úget_hermes_homeÚhermes_home_key)Úcron_env_setting)Úwindows_hide_flags)Úload_configÚload_config_readonly)Úget_fallback_chain©Únow)Úrequest_hard_interrupt)Ú"enter_non_dispatcher_owned_contextÚ!exit_non_dispatcher_owned_context)Ú	ctx_bound)Úis_max_iteration_handoffÚreturnc                 ó    — t        j                  t        «      5  | j                  «       }|�ddlm}  ||«       ddd«       y# 1 sw Y   yxY w)uí  Done-callback: close a SessionDB whose constructor finished after run_job's init timeout
    (worker abandoned via ``shutdown(wait=False)``), else its .db/WAL/SHM handles leak to EMFILE.

    If the constructor later completes inside that abandoned worker, the Future's result â€” an open SessionDB
    holding .db / WAL / SHM file handles â€” would be orphaned and never closed, leaking descriptors until
    EMFILE (#72782). This callback retrieves and closes that eventual late result.
    Nr   ©Úrelease_or_close)Ú
contextlibÚsuppressÚ	ExceptionÚresultÚhermes_state_registryr    )ÚfutureÚdbr    s      ú3/home/ubuntu/.hermes/hermes-agent/cron/scheduler.pyÚ_close_late_session_db_resultr)   6   sB   € ô 
×	Ñ	œYÓ	'ñ !Ø�]‰]‹_ˆØˆ>Ý>Ù˜RÔ ÷	!÷ !ñ !ús   š!AÁAc                 óê   — | r|sy|xs dj                  «       }|sy	 | j                  ||«       |S # t        $ r7 t        | dd«      }|€‚  ||«      }|r||k(  r‚ | j                  ||«       |cY S w xY w)aŸ  Persist a non-blank, unique title for a finished cron session; returns it (None if unset).
    Runs BEFORE end_session()/close() so no write races the close. Duplicate title (unique-index
    ValueError) -> get_next_title_in_lineage(); if unavailable, raise rather than end up untitled.

    Centralizes the title write so the cron finally block can guarantee a non-blank, unique title is
    persisted before end_session()/close() tear the connection down (issues #50535, #50536, #50537):
    - #50535: never leaves the session blank. base_title already carries a cron-id fallback for nameless
    jobs; this also guards a failed write. Recover by appending a #N suffix via get_next_title_in_lineage()
    when supported, instead of swallowing the error and ending up untitled. - #50536: this runs
    synchronously in the cron finally block ahead of the session close, so no in-flight title write can race
    the close.
    NÚ Úget_next_title_in_lineage)ÚstripÚset_session_titleÚ
ValueErrorÚgetattr)Ú
session_dbÚ
session_idÚ
base_titleÚtitleÚnext_title_fnÚdedupeds         r(   Ú_set_cron_session_titler7   E   s’   € ñ ™ZØØÒ˜2×$Ñ$Ó&€EÙØðØ×$Ñ$ Z°Ô7ØˆøÜò 	ä 
Ð,GÈÓNˆØÐ ØÙ Ó&ˆÙ˜' UÒ*ØØ×$Ñ$ Z°Ô9ØŠð	ús   ž2 ²=A2Á1A2c                  ó^   — 	 t        «       xs i } t        | «      }|ry	 y# t        $ r Y yw xY w)uÌ   Backup-provider clause for a provider-failure notice: "the backups failed too" vs "none
    configured" (most installs). Fails open to the former if config can't be read â€” never crash
    delivery.
    z$No backup provider succeeded either.uœ   No backup provider is configured â€” add one with `hermes fallback add`, or set a cron-wide default via `cron.model` + `cron.model_provider` in config.yaml.)r   r   r#   )ÚcfgÚchains     r(   Ú_fallback_chain_phraser;   f   sD   € ð
6Ü‹mÒ!˜rˆÜ" 3Ó'ˆñ Ø5ð	^ðøô	 ò 6Ù5ð6ús   ‚   	,«,Újobc                 óÐ  — | j                  d«      xs i j                  d«      }|dvry	 t        «       xs i }t        t        |t        «      r|j                  d«      xs i ni j                  dd«      «      }|dk  ryt        | j                  d	«      xs d«      d
z   }||k  ry| j                  d«      xs | j                  d«      xs d}d|› d|› d�S # t
        $ r d}Y Œkw xY w)zòReview nudge when a recurring job keeps failing, else "". The failure message is delivered
    BEFORE mark_job_run records this run, hence stored ``failure_streak`` + 1. Threshold:
    ``cron.failure_nudge_threshold`` (default 3, 0 disables).ÚscheduleÚkind>   ÚcronÚintervalr+   r@   Úfailure_nudge_thresholdé   r   Úfailure_streaké   ÚnameÚidzthis jobz
This job has failed u^    runs in a row â€” worth a review. Fix its prompt/config, or pause it with `hermes cron pause z3` (resume/remove also available) to stop the noise.)Úgetr   ÚintÚ
isinstanceÚdictr#   )r<   Úschedule_kindr9   Ú	thresholdÚstreakÚjob_refs         r(   Ú_failure_streak_nudgerP   x   s   € ð —W‘W˜ZÓ(Ò.¨B×3Ñ3°FÓ;€MØÐ0Ñ0ØðÜ‹mÒ!˜rˆÜÜ(2°3¼Ô(=ˆc�g‰g�f‹oÒ#¡À2×JÑJØ)¨1óó
ˆ	ð �A‚~ØÜ�—‘Ð)Ó*Ò/¨aÓ0°1Ñ4€FØ�	ÒØØ�g‰g�f‹oÒ< §¡¨£Ò<°*€Gà
   ð )FØFMÀYð O<ð	<ðøô ò ØŠ	ðús   «AC ÃC%Ã$C%c                  ó<   — 	 ddl m}   | «       S # t        $ r Y yw xY w)zŸBoot-vs-disk revision skew for THIS process, or None. Test seam over
    ``gateway.code_skew.detect_code_skew``; a broken import must never take delivery down.r   ©Údetect_code_skewN)Úgateway.code_skewrS   r#   rR   s    r(   Ú_detect_gateway_code_skewrU   •   s%   € ðÝ6áÓ!Ð!øÜò Ùðúó   ‚ �	šc                  ó<   — 	 ddl m}   | «       S # t        $ r Y yw xY w)zRFull revision currently on disk; kept separate from display-shortened skew labels.r   ©Úcurrent_code_shaN)rT   rY   r#   rX   s    r(   Ú_current_gateway_code_sharZ       s%   € ðÝ6áÓ!Ð!øÜò ÙðúrV   c                   ó0   ‡ — e Zd ZdZdededdfˆ fd„Zˆ xZS )ÚCronTickYieldeduä  A stale-code ticker yielded this tick to a fresh gateway.

    Raised by ``tick()`` BEFORE the tick lock when boot fingerprint â‰  disk, this process does NOT
    own the runtime lock and its live holder reports the disk revision â€” the stale process must
    stay out of the dispatch race (contention would starve the fresh ticker). Skew ``None`` never
    yields (fail open). Raised, not returned, so ``record_ticker_error`` sees it and ``hermes cron
    status`` isn't green.
    Úboot_revÚdisk_revr   Nc                 óN   •— || _         || _        t        ‰| �  d|› d|› d�«       y )NzDCron tick yielded to a fresh gateway process (stale code: booted on ú, disk is at ú))r]   r^   ÚsuperÚ__init__)Úselfr]   r^   Ú	__class__s      €r(   rc   zCronTickYielded.__init__´   s5   ø€ Ø ˆŒØ ˆŒÜ‰ÑðØ!˜
 -°¨z¸ð<õ	
ó    )Ú__name__Ú
__module__Ú__qualname__Ú__doc__Ústrrc   Ú__classcell__©re   s   @r(   r\   r\   ª   s'   ø„ ñð
 ð 
°ð 
¸÷ 
ñ 
rf   r\   z/stale code: booted on (\S+), disk is at (\S+)\)Úrecorded_errorc                 ó¼   — | r| j                  t        j                  «      syt        j	                  | «      }|r"|j                  d«      |j                  d«      fS dS )aO  ``(boot_rev, disk_rev)`` when a persisted ``ticker_last_error`` is a ``CronTickYielded``.

    ``hermes cron status`` runs in another process and only sees the marker text; a yielding
    ticker still refreshes its heartbeat, so this is the one signal that separates "stale code,
    firing nothing" from a healthy loop (#117275).
    NrE   é   )Ú
startswithr\   rg   Ú_STALE_YIELD_REÚsearchÚgroup)rn   Úmatchs     r(   Ústale_code_yield_labelsrv   À   sO   € ñ  ×!:Ñ!:¼?×;SÑ;SÔ!TØÜ×"Ñ" >Ó2€EÙ/4ˆE�K‰K˜‹N˜EŸK™K¨›NÐ+Ð>¸$Ð>rf   g      ¬@Ú_last_yield_logc                  óÐ   — t        «       } | €yt        «       }|€y	 ddlm}m} t        «       } ||«      ry ||«      }|�|j                  d«      |k7  ry	 | S # t        $ r Y yw xY w)ut  ``(boot_rev, disk_rev)`` when THIS profile's tick must yield to a fresher gateway, else None.

    Yields only when ALL hold: code skew, this process is not the cron owner for the profile being
    ticked, and another live host gateway whose served set covers that profile reports a fresh
    heartbeat on the disk revision. Every probe failure returns None â€” yielding is a certainty
    claim, never a guess.

    The question is asked PER PROFILE. One process multiplexes every profile, so "do I own the
    runtime lock" (process-global, stamped at boot for the launch home) cannot answer it: a
    stale multiplexer that holds the lock must still keep ticking the profiles nobody else ticks,
    and must still yield the ones a fresher gateway serves.

    ``gateway_state.json`` is per-HOME and last-writer-wins, not per-process: during a
    ``--replace`` takeover both the stale and the fresh gateway stamp it, so which one this
    predicate reads is write-order dependent. The pid equality in ``live_gateway_ticking`` is what
    binds the record to the current lock holder; the takeover window itself fails open (no yield).
    Nr   )Úlive_gateway_tickingÚowns_cron_tick_forÚcode_sha)rU   rZ   Úcron.scheduler_ownershipry   rz   Ú_get_hermes_homerH   r#   )ÚskewÚdisk_shary   rz   ÚhomeÚholder_statuss         r(   Ú#_should_yield_tick_to_fresh_gatewayr‚   Ò   s†   € ô$ %Ó&€DØ€|ØÜ(Ó*€HØÐØð
ßUäÓ!ˆÙ˜dÔ#ØÙ,¨TÓ2ˆØÐ  M×$5Ñ$5°jÓ$AÀXÒ$MØð %Nð €Køô ò Ùðús   œA ·A Á	A%Á$A%Úreasonc                 óì   — t        j                  «       }t        j                  d«      }t        j                  dd«      }|| k7  s|t	        |«      z
  t
        k\  rt        j                  d| «       | |dœay)z?Log the yield at error level once per episode (skew signature).rƒ   Úatç        u³   Cron tick yielded: this process is running stale code (%s) and a fresher gateway owns the runtime lock â€” jobs will fire from that process. Restart this one to reclaim its ticks.)rƒ   r…   N)ÚtimeÚ	monotonicrw   rH   ÚfloatÚ_YIELD_LOG_INTERVAL_SECONDSÚloggerÚerror)rƒ   r   Úlast_reasonÚlast_ats       r(   Ú_log_tick_yield_oncer�   ø   sm   € ô �.‰.Ó
€CÜ!×%Ñ% hÓ/€KÜ×!Ñ! $¨Ó,€GØ�fÒ ¤u¨W£~Ñ!5Ô:UÒ UÜ�‰ð>ð ô		ð
 "(¨sÑ3�Orf   rŒ   c           	      óx  — ddl m}m}m}m}m} | j                  d«      xs | j                  d«      xs d}| j                  d«      xs |}|xs dj                  «       }	|	j                  «       }
|
j                  d«      r	 |||«      S t        j                  d|
«      r	 |||«      S | j                  d	«      s. ||| ||	«      t        «       | j                  d
«      ¬«      }|�|S t        j                  dd|	dd «      }t        j                  dd|«      j                  «       j                  d«      }t        |«      dkD  r|dd j                  «       dz   } ||||«      }| j                  d	«      s4t        j                  d|
«      r	 t!        «       }|�|\  }}|d|› d|› d�z  }|S # t"        $ r d}Y Œ"w xY w)ag  One-line failure notice for chat delivery (full details stay in the run output).

    Deterministic scheduler/script shapes are matched first (their text can contain "timed out"
    and would otherwise be blamed on the model service); everything else goes through the shared
    ``classify_api_error`` verdict and the copy table in ``scheduler_failure_copy``.r   )Úclassify_cron_failure_reasonÚgeneric_failure_noticeÚinactivity_noticeÚprovider_failure_noticeÚscript_timeout_noticerF   rG   úcron jobzunknown errorzscript timed outzidle for \d+s\s*\(limit \d+s\)Úno_agentÚprovider)Úbackup_provider_phraser˜   Nz8^(RuntimeError|Exception|ValueError|HTTPStatusError):\s*r+   iÐ  z\s+Ú ú.é´   é±   z...z2cannot import name|modulenotfounderror|importerrorz< Likely cause: the gateway is running stale code (booted on r`   u-   ) â€” run `hermes gateway restart` to fix it.)Úcron.scheduler_failure_copyr‘   r’   r“   r”   r•   rH   r-   Úlowerrq   Úrers   r;   ÚsubÚrstripÚlenrU   r#   )r<   rŒ   r‘   r’   r“   r”   r•   Újob_nameÚjob_idÚtextrŸ   ÚnoticeÚcleanedÚmessager~   r]   r^   s                    r(   Ú$_summarize_cron_failure_for_deliveryrª     sÔ  € ÷8õ 8ð �w‰w�v‹Ò= #§'¡'¨$£-Ò=°:€HØ�W‰W�T‹]Ò&˜h€FØÒ$�_×+Ñ+Ó-€DØ�J‰J‹L€Eð
 ×ÑÐ*Ô+Ù$ X¨vÓ6Ð6ô 
‡y�yÐ2°EÔ:Ù  ¨6Ó2Ð2ð
 �7‰7�:ÔÙ(Ø�fÑ:¸4Ó@Ü#9Ó#;ÀcÇgÁgÈjÓFYô[ˆð ÐØˆMô �f‰fÐPÐRTÐVZÐ[`Ð\`ÐVaÓb€GÜ�f‰f�V˜S 'Ó*×0Ñ0Ó2×9Ñ9¸#Ó>€GÜ
ˆ7ƒ|�cÒØ˜$˜3�-×&Ñ&Ó(¨5Ñ0ˆÙ$ X¨v°wÓ?€Gð �7‰7�:Ô¤2§9¡9Ø=¸uô$ð	Ü,Ó.ˆDð ÐØ!%ÑˆH�hØðØ�Z˜}¨X¨Jð 76ð6ñˆGð €Nøô ò 	ØŠDð	ús   Æ
F+ Æ+F9Æ8F9g      @c                  ó2   — ddl m}   | dt        t        «      S )z½``cron.failure_repeat_alert_hours``: how long an ``alerted`` incident stays silent before one
    reminder ping. ``0`` (or negative) re-alerts on every failing run (the pre-gate behaviour).r   ©Ú_cron_config_numberÚfailure_repeat_alert_hours)Ú	cron.jobsr­   Ú"DEFAULT_FAILURE_REPEAT_ALERT_HOURSr‰   r¬   s    r(   Ú_failure_repeat_alert_hoursr±   O  s   € õ .áÐ;Ô=_ÔafÓgÐgrf   Úincidentc                 óþ   — t        «       }|dk  ry| j                  d«      }|sy	 ddlm}  |t	        j
                  t        |«      «      «      }t        «       |z
  t        |¬«      k  S # t        t        f$ r Y yw xY w)zñAn ``alerted`` incident withholds the per-run ping until the reminder cooldown has elapsed
    since its last ping. A missing/unparseable ``alerted_at`` (ledger written before the column
    existed) delivers rather than swallowing an alert.r   FÚ
alerted_at©Ú_ensure_aware)Úhours)r±   rH   r¯   r¶   r   Úfromisoformatrk   Ú	TypeErrorr/   Ú_hermes_nowr   )r²   r·   r´   r¶   Úlasts        r(   Ú_repeat_alert_withheldr¼   W  s}   € ô (Ó)€EØ�‚zØØ—‘˜lÓ+€JÙØðÝ+áœX×3Ñ3´C¸
³OÓDÓEˆô ‹=˜4Ñ¤)°%Ô"8Ñ8Ð8øô ”zÐ"ò Ùðús   ¦*A* Á*A<Á;A<©Úoutput_filer¾   c                óD  — 	 ddl m}m}  || d   t        |xs d«      | j	                  d«      |¬«      \  }} ||«      }|r|j	                  d«      nd}|d	k(  xs |d
k(  xr t        |«      }	|	|fS # t        $ r$}
t        j                  d| d   |
«       Y d}
~
yd}
~
ww xY w)a�  Record a durable failure incident (grouped by job + error signature). Returns
    ``(withheld, incident_id)``; withheld=True when the signature's incident is already ``closed``
    (operator ack) or ``alerted`` inside the ``cron.failure_repeat_alert_hours`` cooldown (a ping
    already went out) -> suppress the per-run ping. Store errors log at debug; the caller delivers
    as if none existed.r   )Úget_incidentÚupsert_incidentrG   r+   rF   )r¤   r¾   ÚstateNÚclosedÚalertedú?Incident store unavailable for job %s (delivery unaffected): %s)FN)	Úcron.incidentsrÀ   rÁ   rk   rH   r¼   r#   r‹   Údebug)r<   rŒ   r¾   rÀ   rÁ   Úincident_idÚ_is_newr²   rÂ   ÚwithheldÚexcs              r(   Ú_upsert_incident_for_failurerÌ   j  s¬   € ðß@á.Ø�‰I”s˜5š; BÓ'°#·'±'¸&³/È{ô \Ñˆ�Wá Ó,ˆÙ)1�—‘˜WÔ%°tˆØ˜HÑ$Òa¨°)Ñ);Ò)`Ô@VÐW_Ó@`ˆØ˜Ð$Ð$øÜò Ü�‰ØMØ�‰I�sô	ô ûð	ús   ‚A/A2 Á2	BÁ;BÂBc                 óˆ   — 	 ddl m}  || d   «       y# t        $ r$}t        j	                  d| d   |«       Y d}~yd}~ww xY w)z¬Best-effort: a successful run marks the job's open incidents ``resolved`` (never touches an
    operator ``closed`` ack). Store errors log at debug; delivery is unaffected.r   )Ú!close_incidents_for_recovered_jobrG   rÅ   N)rÆ   rÎ   r#   r‹   rÇ   )r<   rÎ   rË   s      r(   Ú$_resolve_incidents_for_recovered_jobrÏ   ‚  sE   € ðhÝDá)¨#¨d©)Õ4øÜò hÜ�‰ÐVÐX[Ð\`ÑXaÐcf×gÑgûðhús   ‚ ”	A�<¼ArÈ   c                 ó„   — | sy	 ddl m}  || d«       y# t        $ r!}t        j	                  d| |«       Y d}~yd}~ww xY w)zYBest-effort: mark incident ``alerted`` (no-op for closed; never resurrects an acked one).Nr   )Úset_incident_staterÄ   z&Failed marking incident %s alerted: %s)rÆ   rÑ   r#   r‹   rÇ   )rÈ   rÑ   rË   s      r(   Ú_mark_incident_alertedrÒ   �  sA   € áØðQÝ5á˜;¨	Õ2øÜò QÜ�‰Ð=¸{ÈC×PÑPûðQús   … •	?ž:º?c                   ó   — e Zd ZdZy)ÚCronPromptInjectionBlockeda&  Raised by _build_job_prompt when the assembled prompt (incl. runtime-loaded skill content,
    unseen by create-time scanning) trips the injection scanner; run_job turns it into a clean
    "job blocked" delivery.

    Assembled-prompt scanning (including loaded skill content) plugs the gap from #3968: create-time
    scanning only covers the user-supplied prompt field; skill content loaded at runtime was never scanned,
    so a malicious skill could carry an injection payload that reached the non-interactive (auto-approve)
    cron agent.
    N©rg   rh   ri   rj   © rf   r(   rÔ   rÔ   ™  s   „ òrf   rÔ   r9   c                 óJ  — | xs i j                  d«      xs i }|j                  d«      rddg}ng d¢}| xs i j                  d«      xs i }ddlm}  ||j                  d	«      «      }|D ]4  }t        |«      j	                  «       }|sŒ||vsŒ$|j                  |«       Œ6 |S )
uu  Toolsets a cron-spawned agent must never receive: ``messaging``/``clarify`` always
    (interactive); ``cronjob`` by default (loop prevention, not a security boundary â€”
    ``cron.allow_agent_scheduling: true`` lifts only that); ``agent.disabled_toolsets`` layered on
    top so per-job ``enabled_toolsets`` cannot widen past config.yaml's denylist.

    See #25752.
    r@   Úallow_agent_schedulingÚ	messagingÚclarify)ÚcronjobrÙ   rÚ   Úagentr   )Úparse_config_string_listÚdisabled_toolsets)rH   Úagent.skill_utilsrÝ   rk   r-   Úappend)r9   Úcron_cfgÚdisabledÚ	agent_cfgrÝ   Úuser_disabledrF   s          r(   Ú_resolve_cron_disabled_toolsetsrå   ¥  s¡   € ð ’	�r�‰˜vÓ&Ò,¨"€HØ‡|�|Ð,Ô-Ø Ð+‰â6ˆØ’˜—‘ Ó(Ò.¨B€IÝ:á,¨Y¯]©]Ð;NÓ-OÓP€MØò "ˆÜ�4‹y�‰Ó ˆÚ�D Ò(Ø�O‰O˜DÕ!ð"ð €Orf   Úper_jobc                 óÎ   — | D �cg c]
  }|dk7  sŒ	|‘Œ }}d| v r|S ddl m}  ||«      }t        |«      |z  r|S t        |«      D ]  }||vsŒ|j	                  |«       Œ |S c c}w )a   Layer enabled MCP servers onto a per-job ``enabled_toolsets`` allowlist (else a per-job list
    silently drops every MCP server). Mirrors ``_get_platform_tools``: ``no_mcp`` sentinel -> none
    (stripped); any MCP server already listed -> allowlist, add nothing; else union all enabled.Úno_mcpr   )Úenabled_mcp_server_names)Úhermes_cli.tools_configré   ÚsetÚsortedrà   )ræ   r9   Útr$   ré   Úenabled_mcprF   s          r(   Ú _merge_mcp_into_per_job_toolsetsrï   ½  s|   € ð !Ö2�A A¨£MŠaÐ2€FÐ2Ø�7ÑØˆå@Ù*¨3Ó/€KÜ
ˆ6ƒ{�[Ò ØˆÜ�{Ó#ò  ˆØ�vÒØ�M‰M˜$Õð ð €Mùò 3s
   …
A"�A"c                 óÔ   — | j                  d«      }|rt        t        |«      |xs i «      S 	 ddlm} t         ||xs i d«      «      S # t        $ r}t        d|› �«      |‚d}~ww xY w)u+  Toolset list for a cron job. Precedence: per-job ``enabled_toolsets`` (+ MCP merge) >
    ``cron`` platform config (``_get_platform_tools``, which strips _DEFAULT_OFF_TOOLSETS so fresh
    installs run without ``moa``). A lookup failure fails CLOSED: the run errors out.

    1. Per-job ``enabled_toolsets`` (set via ``cronjob`` tool on create/update). Keeps the agent's
    job-scoped toolset override intact â€” #6130. Enabled MCP servers are layered on per
    ``_merge_mcp_into_per_job_toolsets`` so a native-toolset allowlist does not silently strip MCP tools. 2.
    Mirrors gateway behavior (``_get_platform_tools(cfg, platform_key)``) so users can gate cron toolsets
    globally without recreating every job. 3. Never ``None``: AIAgent reads ``None`` as "every
    toolset", so an unreadable ``platform_toolsets.cron`` restriction would hand an unattended job
    the full default set (#111380). The raise reaches ``run_job``'s failure path, which records the
    error on the job and opens an incident, so the operator sees it instead of a widened run.
    Úenabled_toolsetsr   )Ú_get_platform_toolsr@   zœCron toolset resolution failed, so this run was refused rather than given every tool. Check `platform_toolsets.cron` in config.yaml (`hermes cron doctor`): N)rH   rï   Úlistrê   rò   rì   r#   ÚRuntimeError)r<   r9   ræ   rò   rË   s        r(   Ú_resolve_cron_enabled_toolsetsrõ   Ï  s~   € ð �g‰gÐ(Ó)€GÙÜ/´°W³¸sºyÀbÓIÐIðÝ?ÜÑ)¨#ª)°°VÓ<Ó=Ð=øÜò Üð[Ø[^ÐZ_ðaó
ð ð	ûðús   ®A
 Á
	A'ÁA"Á"A'Úmodelc           	      óV  — ddl m}m} | j                  d«      }|�k ||«      }|�)t        j                  d| j                  dd«      |«       |S t        j                  d| j                  dd«      || j                  dd«      «        |t        |t        «      r|ni t        |«      «      S )a  Effective reasoning config for a cron run. A per-job ``reasoning_effort`` pin beats global
    and per-model config and is model-independent by design (also governs an auth-fallback swap);
    clamping stays with provider transports. An unparseable pin warns and falls back to config.r   )Úparse_reasoning_effortÚresolve_reasoning_configÚreasoning_effortz-Job '%s': using per-job reasoning_effort '%s'rG   ú?uê   Job '%s': invalid stored reasoning_effort %r â€” ignoring the pin and falling back to config resolution. Fix with `cronjob action=update job_id=%s reasoning_effort=<level>` (valid: none, minimal, low, medium, high, xhigh, max, ultra).)
Úhermes_constantsrø   rù   rH   r‹   ÚinfoÚwarningrJ   rK   rk   )r<   r9   rö   rø   rù   ÚpinnedÚparseds          r(   Ú_resolve_job_reasoning_configr  ê  s¡   € ÷ Rà�W‰WÐ'Ó(€FØÐÙ'¨Ó/ˆØÐÜ�K‰KÐGÈÏÉÐQUÐWZÓI[Ð]cÔdØˆMÜ�‰ð>ð �G‰G�D˜#ÓØØ�G‰G�D˜#Óô	 ñ $¬:°c¼4Ô+@¡CÀbÌ#ÈeË*ÓUÐUrf   )Ú_ensure_cron_dirÚadvance_next_runsÚclaim_dispatchÚclaim_job_for_fireÚfire_claim_fenceÚclear_run_claimÚget_due_jobsÚheartbeat_fire_claimÚheartbeat_run_claimÚmark_job_runÚsave_job_outputÚself_removal_delivery_allowedÚself_removal_delivery_scopeÚuse_cron_store)Ú_TERMINAL_STATESÚHANDOFF_ADOPTION_GRACE_SECONDSÚcreate_executionÚfinish_executionÚget_executionÚmark_execution_handoff_pendingÚmark_execution_runningÚrecover_interrupted_executionsz[SILENT]z[CRON_FAILURE]r¦   c                 ó´   — | xs dj                  «       }|r|d   j                  «       t        k7  rydj                  |dd «      j	                  «       }|xs dS )a  Return failure evidence when an agent response declares a cron failure.

    Only the exact, standalone first line is control text. The caller keeps the complete response
    in the saved run output while routing this evidence through normal failure bookkeeping.
    r+   r   NÚ
rE   zCron agent reported failure.)Ú
splitlinesr¢   ÚCRON_FAILURE_MARKERÚjoinr-   )r¦   ÚlinesÚevidences      r(   Ú_cron_failure_marker_errorr    sX   € ð ŠZ�R×#Ñ#Ó%€EÙ�E˜!‘H—O‘OÓ%Ô)<Ò<ØØ�y‰y˜˜q˜r˜Ó#×)Ñ)Ó+€HØÒ5Ð5Ð5rf   c                 ó   — ddl m}  || «      S )uv  True when a cron final response should suppress delivery: ``[SILENT]`` (or SILENT /
    NO_REPLY / NO REPLY) as the whole response OR its own first/last line â€” NOT mid-sentence.
    Shares the webhook-lane matcher in :mod:`gateway.response_filters` so the two cannot drift.

    Recognizes the bracketed ``[SILENT]`` sentinel (whole-response, first line, or last line) plus the
    bracketless ``SILENT`` / ``NO_REPLY`` / ``NO REPLY`` variants the model emits when it drops the brackets
    (#51438, #46917). Whitespace-trimmed and case-insensitive. A token buried mid-sentence is treated as
    real content and delivered.
    r   )Úis_autonomous_silence_response)Úgateway.response_filtersr!  )r¦   r!  s     r(   Ú_is_cron_silence_responser#    s   € õ Há)¨$Ó/Ð/rf   Ú_parallel_poolsÚ_parallel_pool_max_workersr¥   r€   c                 óJ   — |�t        |«      | fS t        t        «       «      | fS )u´  ``(home key, job id)`` â€” the identity of one in-flight cron run.

    ONE gateway process ticks every profile, so a job id alone is not unique: two profiles
    routinely carry identically named jobs (``daily-brief``) and sharing a key made the second
    profile's run read as a duplicate of the first and get skipped. ``home`` defaults to the
    active cron scope's home, which the ticker binds per profile for the whole tick.
    )r   r}   )r¥   r€   s     r(   Ú_inflight_keyr'  5  s.   € ð &*Ð%5ŒO˜DÓ!Ð`fÐgÐg¼?ÔK[ÓK]Ó;^Ð`fÐgÐgrf   Ú_inflight_home_pathsc                 ó*   — | t         t        | «      <   | S )zMRecord ``home`` under its key so a claim can be mapped back to a usable path.)r(  r   ©r€   s    r(   Ú_remember_inflight_homer+  F  s   € à26Ôœ¨Ó.Ñ/Ø€Krf   Úhome_keyc                 óF   — t         j                  | «      xs t        | «      S )zOReal home Path for an in-flight key's home half (falls back to the key itself).)r(  rH   r   )r,  s    r(   Ú_inflight_home_pathr.  L  s   € ä×#Ñ# HÓ-Ò?´°h³Ð?rf   Ú_running_job_idsÚ_running_fire_ownersÚ_restart_safe_waiter_job_idsÚ_running_worker_pidsÚ_running_sinceÚ_running_allowance_sÚ_running_futuresÚ_forced_release_countÚ_forced_releasesé   g      >@Ú_interrupted_job_idsc                   ó$   — e Zd ZdZdefd„Zdd„Zy)Ú_CancelEventLikezYStructural type for cancellation sources (``threading.Event``, ``_CombinedCancelEvent``).r   c                  ó   — y ©NrÖ   ©rd   s    r(   Úis_setz_CancelEventLike.is_set‚  ó   � rf   Nc                  ó   — y r=  rÖ   r>  s    r(   rë   z_CancelEventLike.setƒ  r@  rf   ©r   N)rg   rh   ri   rj   Úboolr?  rë   rÖ   rf   r(   r;  r;    s   „ Ùcà!˜Ó!Ürf   r;  c                   ó:   — e Zd ZdZded   ddfd„Zdefd„Zd	d„Zy)
Ú_CombinedCancelEventzÁDuck-typed ``threading.Event`` ORing several cancellation sources (fire-claim heartbeat
    ``lost_ownership`` + per-transport events). Workers only call is_set()/set(), so no pump thread.
    Úeventsr;  r   Nc                 ó>   — |D �cg c]  }|€Œ|‘Œ	 c}| _         y c c}w r=  )Ú_events)rd   rF  Úevents      r(   rc   z_CombinedCancelEvent.__init__‹  s   € Ø+1ÖG %°UÑ5FšÒGˆ�ùÒGs   …�c                 ó:   — t        d„ | j                  D «       «      S )Nc              3   ó<   K  — | ]  }|j                  «       –— Œ y ­wr=  )r?  )Ú.0rI  s     r(   ú	<genexpr>z._CombinedCancelEvent.is_set.<locals>.<genexpr>�  s   è ø€ Ò< e�5—<‘<—>Ñ<ùs   ‚)ÚanyrH  r>  s    r(   r?  z_CombinedCancelEvent.is_setŽ  s   € ÜÑ<¨t¯|©|Ô<Ó<Ð<rf   c                 óF   — | j                   D ]  }|j                  «        Œ y r=  )rH  rë   )rd   rI  s     r(   rë   z_CombinedCancelEvent.set‘  s   € Ø—\‘\ò 	ˆEØ�I‰I�Kñ	rf   rB  )	rg   rh   ri   rj   r   rc   rC  r?  rë   rÖ   rf   r(   rE  rE  †  s4   „ ñðH Ð);Ñ <ð HÀó Hð=˜ó =ôrf   rE  c                  ó�   — t         5  t        d„ t        t        j	                  «       z  D «       «      cddd«       S # 1 sw Y   yxY w)uü  Thread-safe snapshot of executing job IDs (dispatch until ``_process_job`` returns). Read by
    the gateway shutdown drain, otherwise blind to cron work (runs outside ``_running_agents``).

    _drain_active_agents``) reads this to treat in-flight cron work as active the same way it already treats
    in-flight chat sessions via ``_running_agents`` â€” cron jobs run through their own thread pool here,
    entirely outside that dict, so without this the drain is structurally blind to them (#60432).
    c              3   ó&   K  — | ]	  }|d    –— Œ y­w©rE   NrÖ   ©rL  Úkeys     r(   rM  z&get_running_job_ids.<locals>.<genexpr>Ÿ  s   è ø€ ÒZ C˜˜Q�ÑZùó   ‚N)Ú_running_lockÚ	frozensetr/  r0  ÚkeysrÖ   rf   r(   Úget_running_job_idsrY  –  s>   € ô 
ñ [ÜÑZÔ+;Ô>R×>WÑ>WÓ>YÑ+YÔZÓZ÷[÷ [ò [ús	   ‡+<¼Ac            
      ó@  — t        j                   «       } t        5  t        t        t        j                  «       z  «      D �cg c]<  }|d   |t        v rt        | t        |   z
  d«      ndt        j                  |«      dœ‘Œ> c}cddd«       S c c}w # 1 sw Y   yxY w)zÂPer in-flight job: ``{"job_id", "elapsed_s", "worker_pid"}`` (``worker_pid`` None for in-process
    runs). The drain wait publishes this so ``hermes update`` can say WHICH job it is waiting on.rE   N)r¥   Ú	elapsed_sÚ
worker_pid)
r‡   rV  rì   r/  r0  rX  r3  Úroundr2  rH   )r   rT  s     r(   Úget_running_job_detailsr^  ¢  s•   € ô �)‰)‹+€CÜ	ñ 
ô
 Ô.Ô1E×1JÑ1JÓ1LÑLÓMö	
ð ð ˜1‘vØADÌÑAVœ% ¤n°SÑ&9Ñ 9¸1Ô=Ð\`Ü/×3Ñ3°CÓ8ó:ò
÷
ñ 
ùò
÷
ð 
ús   ›'BÁABÂBÂBÂBc                  ó°  ‡‡— t        j                   «       } t        5  t        j                  «       D ��ci c]  \  }}|t        v sŒ|| |z
  “Œ }}}|D �ci c]  }|t
        v sŒ|t
        |   “Œ c}Šddd«       s
t        «       S t        «       dz  Š|D �cg c]	  }|‰vsŒ|‘Œ }}|rÒi }t        j                  t        «      5  ddlm}  |«       D �ci c]  }|j                  d«      |“Œ }}ddd«       t        t        «       «      }t        5  |D ]\  }‰}	|d   |k(  r|j                  |d   «      nd}
t!        |
xs i «      }|rt#        |	d|z  dz  «      }	|	‰|<   |t        v sŒT|	t
        |<   Œ^ 	 ddd«       t        ˆˆfd„|j                  «       D «       «      S c c}}w c c}w # 1 sw Y   �Œ9xY wc c}w c c}w # 1 sw Y   ŒÒxY w# 1 sw Y   ŒZxY w)	uÒ  In-flight job IDs older than their stale-inflight allowance (``max(2 * interval,
    cron.inflight_max_minutes)``) â€” the scheduler's own definition of a claim that can no longer be
    making progress. ``sweep_stale_inflight`` cannot release these while the worker thread is still
    alive (a delivery blocked on a dead transport, #115469), so the gateway restart drain reads this to
    skip them the way it skips wedged chat turns; restart is their remedy.
    Nç      N@r   ©Ú	load_jobsrG   rE   ç       @c              3   óT   •K  — | ]  \  }}|t        ‰|   ‰«      k\  sŒ|d    –— Œ! y­wrR  )Úmax)rL  rT  ÚageÚ
allowancesÚfloor_secondss      €€r(   rM  z%get_wedged_job_ids.<locals>.<genexpr>Ò  s2   øè ø€ ò [Ù�3˜¨s´c¸*ÀS¹/È=Ó6YÓ/YˆˆA�ñ[ùs   ƒ(ž
()r‡   rV  r3  Úitemsr/  r4  rW  Ú_inflight_min_allowance_minutesr!   r"   r#   r¯   rb  rH   r   r}   Ú_job_interval_minutesre  )r   rT  ÚstartedÚagesÚ
unresolvedÚby_idrb  ÚjÚ
local_homeÚ	allowanceÚ	local_jobÚinterval_minutesrg  rh  s               @@r(   Úget_wedged_job_idsru  ¯  sà  ù€ ô �)‰)‹+€CÜ	ñ eÜ7E×7KÑ7KÓ7M×i¡| s¨GÐQTÔXhÒQh��S˜7‘]Ñ"ÐiˆÑiØ@DÖd¸ÈÔOcÒHc�cÔ/°Ñ4Ñ4Òdˆ
÷eñ Ü‹{ÐÜ3Ó5¸Ñ<€MØ!%Ö?˜#¨°JÒ)>’#Ð?€JÐ?Ùð
 ˆÜ× Ñ ¤Ó+ñ 	:Ý+Ù-6«[Ö9¨�Q—U‘U˜4“[ !‘^Ð9ˆEÐ9÷	:ô %Ô%5Ó%7Ó8ˆ
Üñ 		:Ø!ò :�Ø)�	Ø14°Q±¸:Ò1E˜EŸI™I c¨!¡fÔ-È4�	Ü#8¸ºÀbÓ#IÐ Ù#Ü # I¨sÐ5EÑ/EÈÑ/LÓ M�IØ"+�
˜3‘ØÔ*Ò*Ø09Ô(¨Ò-ñ:÷		:ô ô [Ø#Ÿz™z›|ô[ó [ð [ùó5 jùÚd÷eñ eüò @ùò :÷	:ð 	:ú÷		:ð 		:úsq   �F)µFÁFÁF)ÁF$Á#F$Á/F)Â	F6Â!F6ÃG ÃF;Ã-G ÄAGÅ'GÆF)Æ)F3Æ;G Ç G	ÇGc                 óv   — t        | |«      }t        5  |t        v xs |t        v cddd«       S # 1 sw Y   yxY w)aã  True when THIS process has an in-flight run of ``job_id`` FOR ``home`` (default: the active
    cron scope's home).

    The bare-id accessors above report the host-wide union so the shutdown drain and metrics see
    every profile's work. Liveness consumers must not: one process ticks every profile, so a
    ``daily-brief`` running in profile A would otherwise report profile B's idle ``daily-brief``
    as running and keep B's stale one-shot alive as a "possibly live run".
    N)r'  rV  r/  r0  ©r¥   r€   rT  s      r(   Úis_job_runningrx  Ö  s@   € ô ˜ Ó
%€CÜ	ñ FØÔ&Ð&ÒE¨#Ô1EÐ*E÷F÷ Fò Fús   “/¯8c                 ó�  — ddl m} t        | t        t	        «       «      «      }|j                  «       5 }t        5  |r|t        v r	 ddd«       ddd«       yt        j                  |«       t        j                  «       t        |<   t        t        |<   	 ddd«       ddd«       y# 1 sw Y   nxY wddd«       y# 1 sw Y   yxY w)aÆ  Atomically add ``job_id`` to the in-flight set; False (caller must skip) if already mid-run.
    Single dedupe owner for ticker + manual runs (the fire claim's 300s TTL is outlived by real
    jobs). Callers MUST pair success with ``release_running_job`` in a ``finally``.

    This is the single dedupe owner shared by the ticker's ``_submit_with_guard`` and manual runs
    (``tools/cronjob_tools``): the fire claim alone cannot prevent a double-fire because its TTL (300s) is
    routinely outlived by real jobs, after which a manual ``cronjob(action='run')`` would claim successfully
    and run the same job concurrently (idea from #53395 by @izumi0uu).
    Registration also makes the run visible to ``get_running_job_ids`` (the gateway shutdown drain, #60432)
    and ``mark_running_jobs_interrupted``. Dedupe is per PROFILE: the key carries the active cron
    scope's home, so one multiplexing process never treats two profiles' same-named jobs as one.
    r   )Ú
retirementNFT)Úhermes_cli.backend_retirementrz  r'  r+  r}   ÚworkrV  r/  Úaddr‡   r3  Ú_FUTURE_PENDINGr5  )r¥   rz  rT  Úadmitteds       r(   Útry_register_running_jobr€  ä  s§   € õ 9ä
˜Ô 7Ô8HÓ8JÓ KÓ
L€CØ	�‰Ó	ð ˜h¬ñ Ù˜3Ô"2Ñ2Ø÷÷ ð ô 	×Ñ˜SÔ!ô #Ÿi™i›kŒ�sÑÜ /Ô˜ÑØ÷÷ ÷ ñ ú÷ ÷ ñ ús.   ´B<»B'ÁB<Á>B'ÂB<Â'B0	Â,B<Â<Cc                 ó,  — t        | |«      }t        5  t        j                  |«       t        j                  |d«       t        j                  |d«       t        j                  |d«       t        j                  |d«       ddd«       y# 1 sw Y   yxY w)a¶  Remove ``job_id`` from the in-flight running set (idempotent).

    ``home`` MUST be passed by any caller that does not run inside the same cron scope the claim
    was registered under. The scope is a ContextVar the ticker binds per profile: a pool worker
    releasing in a ``finally`` outside ``ctx.run`` resolves the default (launch) home instead, so
    the discard misses the real key and every secondary profile's claim leaks.
    N)	r'  rV  r/  Údiscardr3  Úpopr4  r5  r2  rw  s      r(   Úrelease_running_jobr„  ÿ  sv   € ô ˜ Ó
%€CÜ	ñ ,Ü× Ñ  Ô%Ü×Ñ˜3 Ô%Ü× Ñ   dÔ+Ü×Ñ˜S $Ô'Ü× Ñ   dÔ+÷,÷ ,ñ ,ús   “A.B
Â
Bc                  óú  — t        j                  t        «      5  t        «       xs i } t	        | t
        «      r| j                  di «      ni j                  d«      }|�t        |«      }|dkD  r|cddd«       S ddd«       t        d«      j                  «       }|r	 t        |«      }|dkD  r|S 	 t        S t        S # 1 sw Y   ŒDxY w# t        t        f$ r# t        j                  d|t        «       Y t        S w xY w)zZStale allowance floor (min): ``cron.inflight_max_minutes``, else env escape hatch/default.r@   Úinflight_max_minutesNr   Ú HERMES_CRON_INFLIGHT_MAX_MINUTESz=Invalid HERMES_CRON_INFLIGHT_MAX_MINUTES=%r; using default %s)r!   r"   r#   r   rJ   rK   rH   r‰   r   r-   r/   r¹   r‹   rþ   Ú_INFLIGHT_MIN_ALLOWANCE_MINUTES)Ú_ucfgÚ_cfg_valÚvalÚraws       r(   rj  rj    s÷   € ä	×	Ñ	œYÓ	'ñ Ü“Ò# ˆä%/°´tÔ%<ˆE�I‰I�f˜bÔ!À"ß
‰#Ð$Ó
%ð 	ð ÐÜ˜“/ˆCØ�QŠwØ÷÷ ñ ô Ð=Ó
>×
DÑ
DÓ
F€CÙ
ð	1Ü˜“*ˆCØ�QŠwØ�
ð ô +Ð*Ô*Ð*÷)ð ûô œIÐ&ò 	1Ü�N‰NØOØÜ/õ1ô +Ð*ð	1ús   šAB<ÂC Â<CÃ*C:Ã9C:Ú_cron_interval_cacheÚexprc                 ó‚  — | t         v r	t         |    S d}t        j                  t        «      5  ddlm}  |«       rgddlm} ddlm}  |j                  «       } || |«      }|j                  |«      }|j                  |«      }||z
  j                  «       dz  }	|	dkD  r|	nd}ddd«       |t         | <   |S # 1 sw Y   ŒxY w)zYCron expression cadence (gap between next two fires) in minutes; None -> floor allowance.Nr   )Ú_ensure_croniter)Úcroniter)r   r`  )r�  r!   r"   r#   r¯   r�  r‘  r   r   Úget_nextÚtotal_seconds)
rŽ  r$   r�  Ú	_croniterr   ÚbaseÚitÚfirstÚsecondÚgaps
             r(   Ú_cron_interval_minutesrš  -  s¸   € àÔ#Ñ#Ü# DÑ)Ð)Ø€FÜ	×	Ñ	œYÓ	'ñ .Ý.áÔÝ7Ý)à�8—<‘<“>ˆDÙ˜4 Ó&ˆBØ—K‘K Ó)ˆEØ—[‘[ Ó*ˆFØ˜E‘>×0Ñ0Ó2°TÑ9ˆCØ !šG‘S¨ˆF÷.ð "(Ô˜ÑØ€M÷.ð .ús   ­A5B5Â5B>c                 óî  — t        j                  t        «      5  | j                  d«      }t	        |t
        «      r"|j                  «       rddlm}  ||«      xs i }t	        |t        «      rt|j                  d«      }|dk(  r)|j                  d«      }|rt        |«      ndcddd«       S |dk(  r0t        t        |j                  d	«      xs d
«      «      cddd«       S ddd«       y# 1 sw Y   yxY w)z·Best-effort job interval in minutes (None if unknown / one-shot -> floor). ``schedule`` is
    persisted as a parsed dict; the string path is only a fallback for programmatic callers.r>   r   )Úparse_scheduler?   rA   ÚminutesNr@   rŽ  r+   )r!   r"   r#   rH   rJ   rk   r-   r¯   rœ  rK   r‰   rš  )r<   r>   rœ  r?   r�  s        r(   rk  rk  C  s×   € ô 
×	Ñ	œYÓ	'ñ OØ—7‘7˜:Ó&ˆÜ�h¤Ô$¨¯©Ô)9Ý0á% hÓ/Ò5°2ˆHÜ�h¤Ô%Ø—<‘< Ó'ˆDØ�zÒ!Ø"Ÿ,™, yÓ1�Ù)0”u˜W”~°d÷Oñ Oð �vŠ~Ü-¬c°(·,±,¸vÓ2FÒ2LÈ"Ó.MÓN÷O÷ Oñ Oð ÷Oð ús   šB	C+Â-+C+Ã+C4c                  ó2  — t        j                   «       } t        5  t        d„ t        D «       «      t        j                  «       D ��ci c]  \  }}|d   t        | |z
  d«      “Œ c}}t        t        t        «      dœcddd«       S c c}}w # 1 sw Y   yxY w)zZProbe-visible snapshot; non-zero ``forced_releases`` means a job wedged and was recovered.c              3   ó&   K  — | ]	  }|d    –— Œ y­wrR  rÖ   rS  s     r(   rM  z+get_inflight_guard_stats.<locals>.<genexpr>[  s   è ø€ ÒA¨˜c !�fÑAùrU  rE   )ÚrunningÚrunning_ages_secondsÚforced_releasesÚrecent_forced_releasesN)
r‡   rV  rì   r/  r3  ri  r]  r6  ró   r7  )r   rT  rl  s      r(   Úget_inflight_guard_statsr¤  V  sŠ   € ä
�)‰)‹+€CÜ	ñ >äÑAÔ0@ÔAÓAô %3×$8Ñ$8Ó$:÷%á �C˜ð �A‘œ˜c G™m¨QÓ/Ñ/ó%ô  5Ü&*Ô+;Ó&<ñ>÷>ñ >ùó%÷>ð >ús   ›-BÁB
Á%BÂBÂBrF   Úage_secondsÚallowance_secondsc                 ó  — | |t        |d«      t        |d«      t        «       j                  «       dœ}t        5  t        j                  |«       t        dt         …= ddd«       	 t        «       dz  dz  }t        |j                  «       t        |dd¬«      5 }|j                  t        j                  |«      d	z   «       ddd«       y# 1 sw Y   ŒnxY w# 1 sw Y   yxY w# t        $ r }t        j!                  d
|«       Y d}~yd}~ww xY w)z@Persist a countable signal for one forced release (best-effort).rE   )r¥   rF   r¥  r¦  r…   Nr@   zinflight_forced_releases.jsonlÚaúutf-8©Úencodingr  z*Could not append forced-release record: %s)r]  rº   Ú	isoformatrV  r7  rà   Ú_FORCED_RELEASE_HISTORYr}   r  ÚparentÚopenÚwriteÚjsonÚdumpsr#   r‹   rÇ   )r¥   rF   r¥  r¦  ÚentryÚpathÚfhÚes           r(   Ú_record_forced_releaser·  d  sø   € ð ØÜ˜[¨!Ó,Ü"Ð#4°aÓ8Ü‹m×%Ñ%Ó'ñ)€Eô 
ñ 8Ü×Ñ Ô&ÜÐ6Ô6Ð6Ð6Ð7÷8ðFÜÓ! FÑ*Ð-MÑMˆÜ˜Ÿ™Ô%Ü�$˜ gÔ.ð 	/°"Ø�H‰H”T—Z‘Z Ó&¨Ñ-Ô.÷	/ð 	/÷8ð 8ú÷	/ð 	/ûäò FÜ�‰ÐAÀ1×EÑEûðFúsA   ¹$C
Á&3C" Â(CÃC" Ã
CÃCÃC" ÃC" Ã"	DÃ+DÄDc                  ó˜  — t        t        «       «      } t        5  t        D �ci c]#  }|d   | k(  r|d   t        j                  |«      “Œ% }}ddd«       j                  «       D ��cg c]!  \  }}|�|t        u s|j                  «       r|‘Œ# }}}|si S 	 ddl	m
}  ||«      S c c}w # 1 sw Y   Œ]xY wc c}}w # t        $ r i cY S w xY w)ub  Latest durable execution per releasable-looking claim (missing/pending/done future), one
    indexed query so the healthy path pays no DB work. Snapshot under _running_lock â€” iterating
    the set while try_register/release mutate it raises RuntimeError.

    Scoped to the ACTIVE profile's claims: the executions ledger it queries is that home's.
    r   rE   N)Úlatest_executions)r   r}   rV  r/  r5  rH   ri  r~  ÚdoneÚcron.executionsr¹  r#   )rq  rT  Úclaim_futuresr¥   ÚfutÚ
candidatesÚ_latest_execss          r(   Ú(_latest_executions_for_releasable_claimsrÀ  x  så   € ô !Ô!1Ó!3Ó4€JÜ	ñ 
ô (ö
à¨3¨q©6°ZÒ+?ð �‰FÔ$×(Ñ(¨Ó-Ñ-ð
ˆð 
÷
ð #0×"5Ñ"5Ó"7÷Ù�6˜3Øˆ;˜#¤Ñ0°C·H±H´Jò 	ð€Jñ ñ Øˆ	ðÝFÙ˜ZÓ(Ð(ùò
÷
ð 
üó
øô ò ØŠ	ðús4   š	B)£(B$ÁB)Á(&B5ÂB; Â$B)Â)B2Â;C	ÃC	ÚrowÚclaim_startedc                 óÈ   — | j                  d«      }|sy	 ddlm}  |t        j                  |«      «      }|j                  «       |k\  S # t        t        t        f$ r Y yw xY w)a  True when the ledger row was claimed at/after this in-memory claim. An older terminal row is
    the PREVIOUS run's (try_register->create_execution window); releasing on it would
    double-dispatch. Unparseable timestamps fail closed (the age path still bounds the claim).Ú
claimed_atFr   rµ   )	rH   r¯   r¶   r   r¸   Ú	timestampr/   r¹   ÚOSError)rÁ  rÂ  rÄ  Ú_ensure_aware_tsÚrow_tss        r(   Ú_row_belongs_to_claimrÉ  ’  sb   € ð —‘˜Ó&€JÙØðÝ?Ù!¤(×"8Ñ"8¸Ó"DÓEˆØ×ÑÓ! ]Ñ2Ð2øÜœ	¤7Ð+ò Ùðús   –3A
 Á
A!Á A!rf  rr  c           	      óÜ  — | j                  d«      xs |}|t        u rdn|€dnd}t        j                  d||||||«       t	        ||||«       |dk(  ry| j                  d«      xs i }t        |t        «      r)|j                  d	«      �t        j                  d
||«       y	 t        |dd|dz  d›d|dz  d›d�«       y# t        $ r!}	t        j                  d||	«       Y d}	~	yd}	~	ww xY w)z¨WARNING log + probe record for one forced release, then ``last_error`` unless the ledger
    already holds the run's real outcome or the job has a finite repeat budget.rF   ÚpendingNÚmissingÚfinisheduÉ   cron.inflight.forced_release event=forced_release reason=%s job='%s' id=%s age=%.0fs allowance=%.0fs future=%s â€” stale in-flight claim released; the job was skipping every fire with 'already running'úledger-terminalÚrepeatÚtimesu²   cron.inflight.forced_release.job_untouched job='%s' id=%s â€” finite-repeat job released without mark_job_run (repeat budget preserved); row left in place so it re-fires normallyFz+Stale in-flight claim force-released after é<   z.1fzm (allowance z=m); previous run never released the scheduler in-flight guardz.Could not record forced release for job %s: %s)	rH   r~  r‹   rþ   r·  rJ   rK   r  r#   )
r<   r¥   rf  rr  r½  rƒ   rF   Úfuture_staterÏ  r¶  s
             r(   Ú_record_stale_releaserÓ  ¡  s  € ð �7‰7�6‹?Ò$˜f€DØ #¤Ñ 6‘9ÈÈ¹IÐZd€LÜ
‡N�Nð	Kð 	��f˜c 9¨lô	<ô
 ˜6 4¨¨iÔ8ð Ð"Ò"Øð �W‰W�XÓÒ$ "€FÜ�&œ$Ô F§J¡J¨wÓ$7Ð$CÜ�‰ðDð �&ô		ð
 	ðTÜØ�EØ9¸#À¹(À3¸ð HØ# b™.¨Ð-ð .,ð-õ	.øô
 ò TÜ�‰ÐGÈÐQR×SÑSûðTús   Â$C Ã	C+Ã
C&Ã&C+Údue_jobsc           	      óì  — | xs g D �ci c]%  }t        |t        «      sŒ|j                  d«      |“Œ' }}t        «       dz  }t	        j                  «       }g }ddlm} t        «       }t        t        «       «      }|j                  «       D �	�ci c]  \  }	}|	t        |«      “Œ }
}	}t        5  t        D �cg c]  }|d   |k(  sŒ|‘Œ c}D �]:  }|d   }t        j                  |«      }|€
|t        |<   Œ*||z
  }|
j                  |«      }|}|rt        |d|z  dz  «      }t         j                  |«      }|t"        u rn|�|j%                  «       sŒ‡|j                  |«      }|�"|j                  d«      |v rt'        ||«      rd	}n	||k\  rd
}nŒÅt        j)                  |«       t        j+                  |d«       t,        j+                  |d«       t         j+                  |d«       t.        dz  a|j1                  |||||f«       �Œ= 	 ddd«       |D ]+  \  }}}}}t3        |j                  |«      xs i |||||«       Œ- |D �cg c]  }|d   ‘Œ	 c}S c c}w c c}}	w c c}w # 1 sw Y   Œ\xY wc c}w )uÍ  Force-release in-flight claims that can no longer be making progress; returns released ids.

    Stale = older than ``max(2 * interval, floor)`` AND (no live future â€” submit path hung before
    ``pool.submit`` returned â€” or finished without discarding the id) â€” or the claim's OWN ledger
    row is terminal regardless of age. Each release logs WARNING ``event=forced_release``, bumps
    the probe counter, mirrors JSONL, and writes ``last_error``.
    rG   r`  r   )r  rE   Nrc  ÚstatusrÎ  rf  )rJ   rK   rH   rj  r‡   r»  r  rÀ  r   r}   ri  rk  rV  r/  r3  re  r5  r~  rº  rÉ  r‚  rƒ  r4  r6  rà   rÓ  )rÔ  rp  ro  rh  r   ÚstaleÚ_terminal_statesÚ_latestÚ_local_homeÚjidÚ
_intervalsÚkrT  r¥   rl  rf  rt  rr  r½  Úlatestrƒ   Ú_reasonÚss                          r(   Úsweep_stale_inflightrá  Ä  ss  € ð '/¢n°"ÖM ¼ÀAÄtÕ9LˆQ�U‰U�4‹[˜!‰^ÐM€EÐMÜ3Ó5¸Ñ<€MÜ
�)‰)‹+€CØ€EÝDä6Ó8€GÜ!Ô"2Ó"4Ó5€Kà>C¿k¹k»m×L±F°C¸�#Ô,¨QÓ/Ñ/ÐL€JÑLä	ñ &@Ü/ÖG˜!°1°Q±4¸;Ó3F’AÒGó %	@ˆCØ˜‘VˆFÜ$×(Ñ(¨Ó-ˆGØˆà&)”˜sÑ#ØØ˜‘-ˆCØ)Ÿ~™~¨fÓ5ÐØ%ˆIÙÜ 	¨3Ð1AÑ+AÀDÑ+HÓI�	Ü"×&Ñ& sÓ+ˆCØ”oÑ%àØ�¨¯©¬Øð —[‘[ Ó(ˆFàÐ"Ø—J‘J˜xÓ(Ð,<Ñ<Ü)¨&°'Ô:à*‘Ø˜	Ò!Ø‘àÜ×$Ñ$ SÔ)Ü×Ñ˜s DÔ)Ü ×$Ñ$ S¨$Ô/Ü× Ñ   dÔ+Ü! QÑ&Ð!Ø�L‰L˜& # y°#°vÐ>Ö?ñK%	@÷&@ðP 16ò ]Ñ,ˆ��Y  WÜ˜eŸi™i¨Ó/Ò5°2°v¸sÀIÈsÐT[Õ\ð]àÖ �QˆAˆa‹DÒ Ð ùòk Nùó Mùò H÷&@ð &@üòT !s;   ‰IŸIÂIÂ-	I%Â6I ÃI ÃEI%ÉI1É I%É%I.)Úonly_ownersrâ  c                óp  — t         5  t        t        «      }t        j	                  «       D �����cg c]*  \  }}||vr!|j	                  «       D ]  \  }\  }}||||f‘Œ Œ, }}}}}}|�|D �	cg c]  }	|	d   d   |	d   f|v sŒ|	‘Œ }}	|D �
���ch c]	  \  }
}}}|’Œ }}}}
}|€"|j                  d„ t        |z
  |z
  D «       «       t        j                  d„ |D «       «       ddd«       g }D ]k  \  }}}}|d   }|s(t        j                  d|«       |j                  |«       Œ7	 t        |«      5  t        |d| |¬«      r|j                  |«       ddd«       Œm |S c c}}}}}w c c}	w c c}}}}
w # 1 sw Y   Œ“xY w# 1 sw Y   Œ/xY w# t        $ r!}t        j                  d	||«       Y d}~ŒÃd}~ww xY w)
a”  Best-effort: mark every in-flight cron job interrupted; returns the job IDs marked.

    Called by gateway shutdown right after ``process_registry.kill_all()``: a job whose tool was
    killed must never report success. ``only_owners`` (``(job_id, fire_owner)`` pairs) restricts
    marking. Tokens go into ``_interrupted_job_ids`` BEFORE ``last_status`` is written so
    ``run_one_job`` sees them.
    NrE   rp   c              3   ó@   K  — | ]  }d |d t        |d   «      f–— Œ y ­w)Nr   )r.  rS  s     r(   rM  z0mark_running_jobs_interrupted.<locals>.<genexpr>  s+   è ø€ ò  àð �s˜DÔ"5°c¸!±fÓ"=Ô>ñ ùs   ‚c              3   ó2   K  — | ]  \  }}}}|�|n|–— Œ y ­wr=  rÖ   )rL  ÚtokenrT  Ú_ownerÚ_profile_homes        r(   rM  z0mark_running_jobs_interrupted.<locals>.<genexpr>%  s*   è ø€ ò $
á1��s˜F Mð Ð&‰E¨CÓ/ñ$
ùs   ‚zdJob '%s' interrupted before its durable fire owner was registered; leaving persisted state untouchedF©Úexpected_fire_ownerz%Failed to mark job %s interrupted: %s)rV  rë   r1  r0  ri  Úextendr/  r9  Úupdater‹   rþ   rà   r  r  r#   )rƒ   râ  Úrestart_safe_waitersrT  Ú
executionsræ  ÚownerÚprofile_homeÚactive_firesÚfireÚ_tÚ_oÚ_pÚregistered_keysÚmarkedÚ_tokenÚ
fire_ownerr¥   r¶  s                      r(   Úmark_running_jobs_interruptedrú    s  € ô 
ñ 
Ü"Ô#?Ó@Ðô $8×#=Ñ#=Ó#?÷
ò 
á��ZØÐ.Ñ.Ø0:×0@Ñ0@Ó0Bò	
ñ -�Ñ,˜˜|ð �C˜ Ò-ð
Ø-ð
ˆô 
ð Ð"Ø-9Öb T¸dÀ1¹gÀa¹jÈ$ÈqÉ'Ð=RÐVaÒ=ašDÐbˆLÐbØ6B×CÑC¡? 2 s¨B°š3ÐCˆÓCØÐð ×Ññ  ô % Ñ6Ð9MÑMô ô ô 	×#Ñ#ñ $
à5Aô$
ô 	
÷+
ð2 €FØ1=ò OÑ-ˆ��Z Ø�Q‘ˆÙÜ�N‰Nð4àôð �M‰M˜&Ô!Øð	OÜ Ó-ñ *ÜØ˜E 6¸zõKà—M‘M &Ô)÷*øðOð& €MùöW
ùò cùÝC÷
ð 
ú÷N*ð *ûô ò 	OÜ�N‰NÐBÀFÈA×NÑNûð	Oúsk   ‡*E3±/EÁ E3Á,E&ÂE&Â	E3ÂE+ÂAE3Ä%FÄ0!E?ÅFÅE3Å3E<Å?F	ÆFÆ	F5ÆF0Æ0F5ræ  c                 óˆ   — t        | «      }t        5  |�|t        v r
	 ddd«       y|t        v cddd«       S # 1 sw Y   yxY w)a  Non-destructive peek: has shutdown marked THIS execution interrupted? Used before deciding
    what to deliver; does not clear the flag (the authoritative pre-``last_status`` check needs it).
    ``token`` scopes to one execution so a fresh run reusing the job ID isn't poisoned.NT)r'  rV  r9  )r¥   ræ  rT  s      r(   Ú_is_interruptedrü  @  sL   € ô ˜Ó
€CÜ	ñ +ØÐ Ô*>Ñ!>Ø÷+ð +ð Ô*Ð*÷+÷ +ò +ús   ’8§8¸Ac                 óØ   — t        | «      }t        5  d}|�|t        v rt        j                  |«       d}|t        v rt        j                  |«       d}|cddd«       S # 1 sw Y   yxY w)z´Return True and clear the flag if shutdown marked THIS execution interrupted. Called right
    before ``last_status`` is written; consuming stops the flag leaking into a later run.FNT)r'  rV  r9  r‚  )r¥   ræ  rT  Úhits       r(   Ú_consume_interrupted_flagrÿ  K  sk   € ô ˜Ó
€CÜ	ñ ØˆØÐ Ô*>Ñ!>Ü ×(Ñ(¨Ô/ØˆCØÔ&Ñ&Ü ×(Ñ(¨Ô-ØˆCØ÷÷ ò ús   ’AA Á A)Úget_idle_secondsÚlimit_sÚpoll_sÚstopÚfuture_donec                 ó²   — |j                  |«      s5 |«       ry	 t         | «       xs d«      }||k\  ry|j                  |«      sŒ5y# t        $ r d}Y Œ&w xY w)u  Poll idle time until limit (-> True), stop, or the future completes (-> False). Uses
    ``threading.Event.wait``, not asyncio, so a blocked event loop cannot disable the watchdog.

    Driven by ``threading.Event.wait`` (a kernel timeout), not asyncio, so a blocked event-loop /
    ``run_job`` thread cannot disable this watchdog the way ``asyncio.sleep`` / ``wait_for`` would (family A
    of #94285 â€” the 4118s-idle-on-a-600s-limit cron hang). Returns True when *limit_s* of inactivity was
    observed.
    Fr†   T)Úwaitr‰   r#   )r   r  r  r  r  Úidles         r(   Ú_inactivity_watchdog_loopr  Z  se   € ð �i‰i˜ÔÙŒ=Øð	ÜÑ)Ó+Ò2¨sÓ3ˆDð �7Š?Øð �i‰i˜Õð øô	 ò 	ØŠDð	ús   ›A ÁAÁAc                  ó¨   — t        d«      j                  «       } | sy	 t        | «      S # t        t        f$ r t
        j                  d| «       Y yw xY w)z÷Parse HERMES_CRON_TIMEOUT (seconds). 0 = unlimited; bad input = 600. Shared by the
    inactivity monitor and the cwd-lock bound so they can't drift: the lock bound must stay >= the
    inactivity limit or waiters fail while a healthy holder runs.ÚHERMES_CRON_TIMEOUTg     À‚@z2Invalid HERMES_CRON_TIMEOUT=%r; using default 600s)r   r-   r‰   r/   r¹   r‹   rþ   )rŒ  s    r(   Ú_cron_inactivity_secondsr  r  sS   € ô Ð0Ó
1×
7Ñ
7Ó
9€CÙØðÜ�S‹zÐøÜœ	Ð"ò Ü�‰ÐKÈSÔQÙðús   ž
) ©%AÁAÚmax_workersc                 ó,  — t        t        t        «       «      «      }t        j	                  |«      }|�t
        j	                  |«      | k7  rH|�|j                  dd¬«       t        j                  j                  | d¬«      }|t        |<   | t
        |<   |S )a]  Return (or create) the persistent parallel pool for the ACTIVE profile.

    Pools are per profile home because ``cron.max_parallel_jobs`` is a per-profile config key:
    a single process-global pool is created by whichever profile the multiplexing gateway ticks
    first and then silently imposes that profile's limit on every other profile.
    F©r  Úcancel_futureszcron-parallel)r  Úthread_name_prefix)
r   r+  r}   r$  rH   r%  ÚshutdownÚ
concurrentÚfuturesÚThreadPoolExecutor)r  r€   Úpools      r(   Ú_get_parallel_poolr  €  s‘   € ô Ô2Ô3CÓ3EÓFÓG€DÜ×Ñ˜tÓ$€DØ€|Ô1×5Ñ5°dÓ;¸{ÒJØÐØ�M‰M˜u°UˆMÔ;Ü×!Ñ!×4Ñ4Ø#¸ð 5ó Iˆà $Œ˜ÑØ+6Ô" 4Ñ(Ø€Krf   c                 ó¨   — t        | «      D ]D  }t        j                  |d«      }t        j                  |d«       |€Œ2|j	                  dd¬«       ŒF y)u¶  Drop the parallel pools of homes this process no longer ticks.

    Pools are per home and used to live until ``atexit``: a host that serves many profiles â€” or
    churns them â€” accumulated one ThreadPoolExecutor and its live ``cron-parallel`` threads per
    home ever ticked, and a deleted profile's pool was never reclaimed. ``wait=False`` so the
    ticker is never blocked; queued work still drains before the executor dies.
    NFr  )ró   r$  rƒ  r%  r  )Ú	home_keysrT  r  s      r(   Údiscard_parallel_poolsr  “  sO   € ô �I‹ò <ˆÜ×"Ñ" 3¨Ó-ˆÜ"×&Ñ& s¨DÔ1ØÑØ�M‰M˜u°UˆMÕ;ñ	<rf   c                  ó¾   — t        t        j                  «       «      D ]  } | j                  dd¬«       Œ t        j	                  «        t
        j	                  «        y)z:Shut down every profile's persistent pool on process exit.TFr  N)ró   r$  Úvaluesr  Úclearr%  )r  s    r(   Ú_shutdown_parallel_poolr  ¢  sG   € ä”_×+Ñ+Ó-Ó.ò 7ˆØ�‰˜4°ˆÕ6ð7ä×ÑÔÜ×$Ñ$Õ&rf   c                  ó"   — t        «       dz  dz  S )Nr@   zusage_audit.jsonl©r}   rÖ   rf   r(   Ú_usage_audit_pathr   ¬  s   € ÜÓ Ñ&Ð)<Ñ<Ð<rf   c                  ó’   — t        j                  t        j                  «      } | j	                  d«      | j
                  dz  d›d�z   S )z@RFC3339 UTC timestamp with millisecond precision and 'Z' suffix.z%Y-%m-%dT%H:%M:%S.éè  Ú03dÚZ)r   r   r   ÚutcÚstrftimeÚmicrosecondr   s    r(   Ú_utcnow_iso_msr(  °  s<   € ä
�,‰,”x—|‘|Ó
$€CØ�<‰<Ð,Ó-°3·?±?ÀdÑ3JÈ3Ð2OÈqÐ0QÑQÐQrf   Úrecordc                 ó8  — 	 t        «       }t        |j                  «       t        j                  | d¬«      }t        |dd¬«      5 }|j                  |dz   «       ddd«       y# 1 sw Y   yxY w# t        $ r }t        j                  d|«       Y d}~yd}~ww xY w)	u�   Append one JSONL line to cron/usage_audit.jsonl. NEVER raises â€” a logger bug must not
    break cron jobs (the whole write is inside one try).F)Úensure_asciir¨  r©  rª  r  Nzusage_audit write failed: %s)
r   r  r®  r±  r²  r¯  r°  r#   r‹   rþ   )r)  r´  ÚlineÚfr¶  s        r(   Ú_write_usage_auditr.  ¶  s   € ð:Ü Ó"ˆÜ˜Ÿ™Ô%Ü�z‰z˜&¨uÔ5ˆÜ�$˜ gÔ.ð 	!°!Ø�G‰G�D˜4‘KÔ ÷	!÷ 	!ñ 	!ûäò :Ü�‰Ð5°q×9Ñ9ûð:ús6   ‚AA0 ÁA$ÁA0 Á$A-Á)A0 Á-A0 Á0	BÁ9BÂBrË   c                 ó   — ddl m}  || «      S )u1  True when the interpreter is finalizing (tick fired during gateway teardown): concurrent.
    futures/asyncio refuse new work, so delivery attempts only pollute errors.log â€” callers skip
    with a warning. ``exc`` lets an already-raised scheduling error count as a shutdown signal.

    A cron tick can fire while the gateway is tearing down â€” SIGTERM from ``hermes update`` / ``hermes
    gateway stop`` / systemd restart, or an OOM-kill. Once finalization starts, ``concurrent.futures``
    refuses new work with ``RuntimeError: cannot schedule new futures after interpreter shutdown`` and
    asyncio's default executor is gone, so *any* attempt to schedule delivery (live-adapter,
    ``asyncio.run``, or a fresh pool) is doomed and only pollutes ``errors.log`` with a traceback. See
    #55924, #58720.
    r   )Úinterpreter_shutting_down)Útools.interpreter_shutdownr0  )rË   r0  s     r(   Ú_interpreter_shutting_downr2  Ã  s   € õ Eá$ SÓ)Ð)rf   Ú_hermes_homec                  ó&   — t         xs
 t        «       S )uÒ  Hermes home at call time (honouring the test override). Cron is per-profile: never freeze
    this at import or anchor it at the shared default root â€” either breaks profile isolation.

    Cron is per-profile by design (#4707): the in-process ticker runs inside a profile-scoped gateway, so
    resolving the active HERMES_HOME at call time means a profile's jobs are stored AND executed under that
    profile's home (its .env, config.yaml, scripts, skills).
    )r3  r   rÖ   rf   r(   r}   r}   Ø  s   € ô Ò,œ?Ó,Ð,rf   c                  ó.   — t        «       } | dz  }||dz  fS )zHResolve cron lock paths at call time so profile/env changes are honored.r@   z
.tick.lockr  )Úhermes_homeÚlock_dirs     r(   Ú_get_lock_pathsr8  ã  s$   € ä"Ó$€KØ˜VÑ#€HØ�X Ñ,Ð,Ð,rf   Úerrc                 ó  — | j                   €yt        �;| j                   t         j                  t         j                  t         j                  fv S t
        �,| j                   t         j                  t         j                  fv S y)u  True when *err* from the lock syscall means another ticker holds the lock (POSIX flock:
    EWOULDBLOCK/EAGAIN, EACCES on some NFS; msvcrt.locking: EACCES/EDEADLK). Everything else â€”
    notably EMFILE/ENFILE and EACCES on open() â€” must be surfaced, never swallowed as contention.F)ÚerrnoÚfcntlÚEWOULDBLOCKÚEAGAINÚEACCESÚmsvcrtÚEDEADLK)r9  s    r(   Ú_is_lock_contention_errnorB  í  s`   € ð ‡y�yÐØÜÐØ�y‰yœU×.Ñ.´·±¼e¿l¹lÐKÐKÐKÜÐØ�y‰yœUŸ\™\¬5¯=©=Ð9Ð9Ð9Ørf   c                 ó6   — | j                  «       }d|v xs d|v S )z:Text half of _is_fd_exhaustion (shared with the CLI hint).ztoo many open filesÚemfile)rŸ   )r¦   Úlowereds     r(   Ú_is_fd_exhaustion_textrF  ú  s"   € à�j‰j‹l€GØ  GÐ+ÒB¨x¸7Ð/BÐBrf   c                 ó¤   — t        | t        «      r-| j                  t        j                  t        j                  fv ryt        t        | «      «      S )zÄTrue when *exc* indicates fd exhaustion: EMFILE/ENFILE errno, or the "Too many open files"
    wording for wrapped exceptions (load_jobs wraps the OSError in a RuntimeError).

    See #87644.
    T)rJ   rÆ  r;  ÚEMFILEÚENFILErF  rk   )rË   s    r(   Ú_is_fd_exhaustionrJ     s8   € ô �#”wÔ C§I¡I´%·,±,ÄÇÁÐ1MÑ$MØÜ!¤# c£(Ó+Ð+rf   c                  ó   — t        j                  t        «      5  ddl} | j	                  «        ddd«       t        j                  t        «      5  ddlm}  |d«       ddd«       y# 1 sw Y   Œ:xY w# 1 sw Y   yxY w)aD  Best-effort fd reclamation: gc.collect() closes file objects stuck in reference cycles;
    apply_nofile_soft_limit() raises the RLIMIT_NOFILE soft limit for headroom. Never raises.

    The cron FD-leak family (#60859, #79742, #80792) leaks descriptors from abandoned workers/sessions. Two
    safe, idempotent levers:
    r   N)Úapply_nofile_soft_limit)r!   r"   r#   ÚgcÚcollectÚhermes_cli.resource_limitsrL  )rM  rL  s     r(   Ú_reclaim_fds_best_effortrP    sh   € ô 
×	Ñ	œYÓ	'ñ Ûà
�
‰
Œ÷ô 
×	Ñ	œYÓ	'ñ &ÝFá Ô%÷&ð &÷	ð ú÷&ð &ús   šA(ÁA4Á(A1Á4A=c                 óZ   ‡ ‡— ddl m}m}  |«       j                  «       sy |ˆ ˆfd„«      S )z@Send queued worker results through this gateway's live adapters.r   )Ú_pathÚdrainc                 ó$   •— t        | |‰‰|¬«      S )N©ÚadaptersÚloopÚfor_failure)Ú_deliver_result)Ú
queued_jobÚqueued_contentÚqueued_for_failurerV  rW  s      €€r(   ú<lambda>z&drain_delivery_queue.<locals>.<lambda>&  s   ø€ ¼ØØØØØ*ô@
€ rf   )Úcron.delivery_queuerR  rS  Úexists)rV  rW  rR  rS  s   ``  r(   Údrain_delivery_queuer`    s)   ù€ ç0ñ
 ‹7�>‰>ÔØÙô	
óð rf   i  r`  rC   ç      ð?c                  ó  — d} 	 ddl m}  |«       xs i }t        |t        «      r|j	                  di «      ni }|j	                  d«      }|�t        |«      }|dk\  r|S | S # t        $ r!}t        j                  d|«       Y d}~| S d}~ww xY w)z6Return the wall-clock bound for cron post-run cleanup.g      $@r   )r   r@   Úcleanup_timeout_secondsNz3Failed to load cron cleanup timeout from config: %s)	Úhermes_cli.configr   rJ   rK   rH   r‰   r#   r‹   rÇ   )Údefaultr   r9   rá   Ú
configuredÚtimeoutrË   s          r(   Ú_cron_cleanup_timeout_secondsrh  9  s”   € à€GðQÝ1á‹mÒ!˜rˆÜ*4°S¼$Ô*?�3—7‘7˜6 2Ô&ÀRˆØ—\‘\Ð";Ó<ˆ
ØÐ!Ü˜JÓ'ˆGØ˜!Š|Ø�ð €Nøô ò QÜ�‰ÐJÈC×PÐPØ€NûðQús   „AA  Á 	B
Á)BÂB
)Útimeout_secondsÚlabelri  c                óð  ‡ ‡‡	— |€
t        «       n
t        |«      }|dk  r		  ‰ «        yt        j                  «       Šg Š	d
ˆ ˆˆ	fd„}t        j                  t        |«      d|› �d¬«      }|j                  «        ‰j                  |«      st        j                  d	|||«       y‰	rt        j                  d||‰	d   «       yy# t        t        f$ r"}t        j                  d|||«       Y d}~yd}~ww xY w)zDRun fallible post-run cleanup without permanently wedging a cron ID.Nr   TzJob '%s': %s failed: %sFc                  ó®   •— 	  ‰«        ‰j                  «        y # t         $ r} ‰j                  | «       Y d } ~ Œ0d } ~ ww xY w# ‰j                  «        w xY wr=  )ÚBaseExceptionrà   rë   )rË   Úcleanuprº  rŒ   s    €€€r(   Ú_runnerz/_run_cron_cleanup_with_timeout.<locals>._runner[  sE   ø€ ð	ÙŒIð �H‰H�Jøô ò 	Ø�L‰L˜×Ñûð	ûð �H‰H�Jús    ƒ ›	?¤:µA º?¿A ÁAzcron-cleanup-©ÚtargetrF   ÚdaemonzRJob '%s': %s exceeded %.1fs; abandoning cleanup so future runs remain dispatchablerB  )rh  r‰   r#   ÚKeyboardInterruptr‹   rÇ   Ú	threadingÚEventÚThreadr   Ústartr  rŒ   )
rn  r¥   rj  ri  rg  rË   ro  Úworkerrº  rŒ   s
   `       @@r(   Ú_run_cron_cleanup_with_timeoutry  K  sî   ú€ ð 3BÐ2IÔ,Ô.ÌuÐUdÓOe€GØ�!‚|ð	ÙŒIØô
 �?‰?Ó€DØ!#€E÷ô ×ÑÜ˜Ó!¨-¸°xÐ(@ÈôO€Fà
‡L�L„NØ�9‰9�WÔÜ�‰Ø`ØØØô		ð
 ÙÜ�‰Ð.°¸¸uÀQ¹xÔHØØøô= Ô,Ð-ò 	Ü�L‰LÐ2°F¸EÀ3ÔGÜûð	ús   ¡C ÃC5ÃC0Ã0C5c                   ó"   — e Zd ZdZdefd„Zd„ Zy)Ú_BoundedCronSessionDBz¸Proxy SessionDB cleanup calls through the cron cleanup timeout; after the first failure or
    timeout all later calls fail immediately (a damaged connection leaks at most one worker).r¥   c                 ó.   — || _         || _        d| _        y )NF)Ú_session_dbÚ_job_idÚ	_disabled)rd   r1   r¥   s      r(   rc   z_BoundedCronSessionDB.__init__y  s   € Ø%ˆÔØˆŒØˆ�rf   c                 ó`   ‡ ‡‡— t        ‰ j                  ‰«      Št        ‰«      s‰S ˆˆ ˆfd„}|S )Nc                  ó   •‡ ‡‡— ‰j                   rt        d«      ‚i Šˆ ˆˆˆfd„}t        |‰j                  d‰› d�¬«      }|s+‰j	                  d«      }|�|‚d‰_         t        d‰› d	�«      ‚‰j	                  d
«      S )Nz9session finalization disabled after prior cleanup failurec                  óL   •— 	  ‰‰i ‰¤Ž‰d<   y # t         $ r} | ‰d<   ‚ d } ~ ww xY w)NÚvaluerŒ   )rm  )rË   ÚargsÚkwargsr$   rq  s    €€€€r(   Ú_callzB_BoundedCronSessionDB.__getattr__.<locals>._bounded.<locals>._call‰  s7   ø€ ðÙ&,¨dÐ&=°fÑ&=�F˜7’OøÜ$ò Ø&)�F˜7‘OØûðús   ƒ �	#˜ž#zsession finalization (ra   )r¥   rj  rŒ   Tzsession finalization method z
 timed outrƒ  )r  rô   ry  r~  rH   ÚTimeoutError)	r„  r…  r†  ÚokrŒ   r$   rF   rd   rq  s	   ``   @€€€r(   Ú_boundedz3_BoundedCronSessionDB.__getattr__.<locals>._boundedƒ  sŠ   û€ Ø�~Š~Ü"Ð#^Ó_Ð_àˆF÷ô 0Ø˜dŸl™lÐ4JÈ4È&ÐPQÐ2RôTˆBáØŸ
™
 7Ó+�ØÐ$Ø�Kà!%�”Ü"Ð%AÀ$ÀÀzÐ#RÓSÐSØ—:‘:˜gÓ&Ð&rf   )r0   r}  Úcallable)rd   rF   r‰  rq  s   `` @r(   Ú__getattr__z!_BoundedCronSessionDB.__getattr__~  s.   ú€ Ü˜×)Ñ)¨4Ó0ˆÜ˜ÔØˆMö	'ð0 ˆrf   N)rg   rh   ri   rj   rk   rc   r‹  rÖ   rf   r(   r{  r{  u  s   „ ñað¨3ó ó
rf   r{  r¤   Únow_isoÚmodec           	      ó    — d| › d|› d|› d|› d�	S )zKCommon markdown header for the short-circuit run docs (no_agent / monitor).ú# Cron Job: ú

**Job ID:** ú
**Run Time:** z
**Mode:** r  rÖ   )r¤   r¥   rŒ  r�  s       r(   Ú_job_doc_headerr’  ž  s5   € ð �x�jð !Ø�hð Ø ˜	ð "Ø�F˜"ð	ðrf   c                 óº   — | j                  d«      xs dj                  «       xs d}|r1t        |«      j                  «       st        j                  d||«       y|S )zLConfigured job workdir, or None when unset / no longer a directory (logged).Úworkdirr+   NuG   Job '%s': configured workdir %r no longer exists â€” running without it)rH   r-   r   Úis_dirr‹   rþ   )r<   r¥   r”  s      r(   Ú_resolve_job_workdirr–  ¨  sS   € à�w‰w�yÓ!Ò' R×.Ñ.Ó0Ò8°D€GÙ”t˜G“}×+Ñ+Ô-Ü�‰ØUØ�Gô	ð Ø€Nrf   c                 ó  — 	 ddl m}  |t        «       ¬«       | j                  d«      }t        |xs d«      j                  «       sdd	l	m
} t        |||«      S t        | |«      }	 t        | |||¬
«      \  }}	t        «       j!                  d«      }t#        |||d«      }|sd|› d|	› d|› �}d|› d|	› d�||	fS t%        |	«      s#t        j'                  d|«       d|› d�t(        dfS |	j                  «       s#t        j'                  d|«       d|› d�t(        dfS d|› d|	› d�|	dfS # t        $ r t        j                  d|d¬«       Y �Œ1w xY w# t        $ r(}
t        j                  d|«       dd|
› �}	}Y d}
~
�Œd}
~
ww xY w)uÌ   no_agent short-circuit â€” the script IS the job (no AIAgent, no tokens). stdout â†’ delivered
    verbatim; empty stdout or wakeAgent=false â†’ silent success; non-zero exit/timeout â†’ error alert.
    r   )Úload_hermes_dotenv©r6  z%Job '%s': no_agent .env reload failedT©Úexc_infoÚscriptr+   )ÚNO_AGENT_WITHOUT_SCRIPT_ERROR©r”  Úcancel_eventz.Job '%s': script execution raised unexpectedlyFzScript execution failed: Nú%Y-%m-%d %H:%M:%Szno_agent (script)u   âš  Cron watchdog 'z' script failed

ú

Time: z**Status:** script failed

r  u8   Job '%s' (no_agent): wakeAgent=false gate â€” silent runz%**Status:** silent (wakeAgent=false)
u0   Job '%s' (no_agent): empty stdout â€” silent runz"**Status:** silent (empty output)
z
---

)Úhermes_cli.env_loaderr˜  r}   r#   r‹   rÇ   rH   rk   r-   r¯   r�  Ú_block_and_pause_jobr–  Ú$_run_job_script_with_claim_heartbeatÚ	exceptionrº   r&  r’  Ú_parse_wake_gaterý   ÚSILENT_MARKER)r<   r¥   r¤   rŸ  r˜  Úscript_pathr�  Ú_job_workdirrˆ  ÚoutputrË   rŒ  ÚheaderÚalerts                 r(   Ú_run_no_agent_jobr­  ³  sÁ  € ðUÝ<áÔ'7Ó'9Õ:ð —'‘'˜(Ó#€Käˆ{Ò ˜bÓ!×'Ñ'Ô)Ý;ä# F¨HÐ6SÓTÐTô (¨¨VÓ4€Lð>Ü9Ø� lÀôO‰
ˆˆFô ‹m×$Ñ$Ð%8Ó9€GÜ˜X v¨wÐ8KÓL€Fáð " ( Ð+>Øˆhð Ø�Iðð 	ð
 ˜˜Ð =¸f¸XÀRÐHÈ%ÐQWÐWÐWô ˜FÔ#Ü�‰ÐNÐPVÔWØ˜�xÐEÐFÌÐW[Ð[Ð[à�<‰<Œ>Ü�‰ÐFÈÔOØ˜�xÐBÐCÄ]ÐTXÐXÐXà�F�8˜9 V H¨BÐ/°¸Ð=Ð=øôQ ò UÜ�‰Ð<¸fÈtˆ×TðUûô ò >Ü×ÑÐIÈ6ÔRØÐ7¸°uÐ=ˆFŽûð>ús)   ‚D& Á'E Ä&!EÅ
EÅ	E?ÅE:Å:E?Úextra_promptc                 óÖ  — ddl m}m}  || «      sd|dfS  || «      }t        «       j	                  d«      }t        |||d«      }|j                  sYt        j                  d||j                  «       d|› d|j                  › d	|› �}	d
|› d|j                  › d�|	|j                  f|dfS |j                  s&t        j                  d|«       d|› d�t        df|dfS d||j                  fS )an  Monitor gate (hash-suppressed change detection). Must run BEFORE any agent machinery so an
    unchanged tick costs no LLM/delivery. Returns ``(early_result | None, extra_prompt,
    monitor_context)``. Monitor context is runtime data and must remain distinct from a
    user-authored ``extra_prompt`` so the prompt scanner keeps its strict user-input boundary.
    r   )Úcheck_monitorÚjob_has_monitorNr   Úmonitorz#Job '%s': monitor source failed: %su   âš  Cron monitor 'z' source failed

r¡  Fz#**Status:** monitor source failed

r  u<   Job '%s': monitor output unchanged â€” suppressing agent runTz-**Status:** no_change (agent run suppressed)
)Úcron.monitorr°  r±  rº   r&  r’  rˆ  r‹   rŒ   Úchangedrý   r§  Úcontext_block)
r<   r¥   r¤   r®  r°  r±  Ú_monÚ_mon_nowr«  Ú
_mon_alerts
             r(   Ú_apply_monitor_gater¹  ê  s  € ÷ <á˜3ÔØ�\ 4Ð'Ð'Ù˜Ó€DÜ‹}×%Ñ%Ð&9Ó:€HÜ˜X v¨x¸ÓC€FØ�7Š7ô 	�‰Ð:¸FÀDÇJÁJÔOà   
Ð*=Ø�z‰zˆlð Ø�Jð ð 	ð �f�XÐBÀ4Ç:Á:À,ÈbÐQÐS]Ð_c×_iÑ_ið
à˜ðð 	ð �<Š<ä�‰ÐRÐTZÔ[à�V�HÐJÐKÌ]Ð\`ð
à˜ðð 	ð
 �˜t×1Ñ1Ð1Ð1rf   c                   ó:   — e Zd ZU dZeed<   eed<   eed<   eed<   y)Ú_CronJobConfigz4Config-derived inputs for one agent-backed cron run.r9   rö   Ú	model_cfgÚcron_default_providerN)rg   rh   ri   rj   rK   Ú__annotations__rk   r   rÖ   rf   r(   r»  r»    s   … á>à	ƒIØƒJØƒNØÔrf   r»  c                 ó0  — | j                  d«      xs t        d«      xs d}d}i }i }	 ddlm} t	        t        «       dz  «      }t        j                  j                  |«      �r7 |t        |«      «      }|j                  d«      xs i }|j                  d«      xs i }	d}
t        |	t        «      rXt	        |	j                  d«      xs d«      j                  «       }
t	        |	j                  d«      xs d«      j                  «       }| j                  d«      s�|
r|
}n|t        |t        «      r|nHt        |t        «      r7|j                  d	«      xs& |j                  d«      xs |j                  d
«      nd}t	        |xs d«      j                  «       xs |}t        |t        «      r|j                  «       s0t!        d|› d| j                  d«      ›dt        d«      ›d|› d�	«      ‚t#        j$                  t        «      5  ddlm} |j                  di «      }t        |t        «      r|j                  d«      r	 |d¬«       ddd«       t+        ||||«      S # t        $ r!}t        j                  d||«       Y d}~Œçd}~ww xY w# 1 sw Y   ŒDxY w)a  Load config.yaml and resolve the run's model: per-job pin > cron.model (fleet default) >
    the main agent model (config ``model:``, then HERMES_MODEL). Re-read every tick (no cache) so
    ``hermes cron edit --model`` and ``hermes model`` both apply next tick.rö   ÚHERMES_MODELr+   r   )Úload_user_config_effectivezconfig.yamlr@   Úmodel_providerre  rF   z8Job '%s': failed to load config.yaml, using defaults: %sNú
Cron job 'z%' has no model configured (job.model=z, HERMES_MODEL=zY, config.yaml model.default missing or empty). Set a per-job model via `hermes cron edit z= --model <name>` or set a default with `hermes model <name>`.)Úapply_ipv4_preferenceÚnetworkÚ
force_ipv4T)Úforce)rH   r   Úhermes_cli.config_effectiverÁ  rk   r}   Úosr´  r_  r   rJ   rK   r-   r#   r‹   rþ   rô   r!   r"   rü   rÄ  r»  )r<   r¥   r¤   rö   Ú_cron_default_providerÚ_cfgÚ
_model_cfgrÁ  Ú	_cfg_pathÚ_cron_cfg_for_modelÚ_cron_default_modelÚ_mainr¶  rÄ  Ú_net_cfgs                  r(   Ú_load_cron_job_configrÒ    sf  € ð �G‰G�GÓÒFÔ 0°Ó @ÒFÀB€EØÐØ€DØ€Jð^ÝJÜÔ(Ó*¨]Ñ:Ó;ˆ	Ü�7‰7�>‰>˜)Õ$Ù-¬d°9«oÓ>ˆDàŸ™ 'Ó*Ò0¨bˆJØ"&§(¡(¨6Ó"2Ò"8°bÐØ"$ÐÜÐ-¬tÔ4Ü&)Ð*=×*AÑ*AÀ'Ó*JÒ*PÈbÓ&Q×&WÑ&WÓ&YÐ#Ü),Ð-@×-DÑ-DÐEUÓ-VÒ-\ÐZ\Ó)]×)cÑ)cÓ)eÐ&Ø—7‘7˜7Ô#Ù&Ø/‘Eô +5°ZÄÔ*E™Jä% j´$Ô7ð #Ÿ™ yÓ1Òf°Z·^±^ÀGÓ5LÒfÐPZ×P^ÑP^Ð_eÔPfØ=?ð ô   ¢¨Ó,×2Ñ2Ó4Ò=¸�Eô �uœcÔ" u§{¡{¤}ÜØ˜˜
ð #ØŸ'™' 'Ó*Ð-ð .Ü,¨^Ó<Ð?ð @!ð "( ð )2ð2ó
ð 	
ô 
×	Ñ	œYÓ	'ñ .Ý:Ø—8‘8˜I rÓ*ˆÜ�h¤Ô%¨(¯,©,°|Ô*DÙ!¨Õ-÷	.ô
 ˜$  zÐ3IÓJÐJøô+ ò ^Ü�‰ÐQÐSYÐ[\×]Ñ]ûð^ú÷ .ð .ús%   ªE3I ÈAJÉ	J	É(JÊJ	ÊJc                 óZ  — t        | j                  di «      t        «      r| j                  di «      ni }t        d«      xs& | j                  dd«      xs |j                  dd«      }|syt	        |«      j                  «       }|j                  «       st        «       |z  }|j                  «       sy	 t        |dd¬«      5 }t        j                  |«      }ddd«       t        t        «      r|S dS # 1 sw Y   ŒxY w# t        $ r"}t        j                  d	|||«       Y d}~yd}~ww xY w)
zVPrefill messages from env or config.yaml (top-level key canonical; agent.* is legacy).rÜ   ÚHERMES_PREFILL_MESSAGES_FILEÚprefill_messages_filer+   NÚrr©  rª  z8Job '%s': failed to parse prefill messages file '%s': %s)rJ   rH   rK   r   r   Ú
expanduserÚis_absoluter}   r_  r¯  r±  Úloadró   r#   r‹   rþ   )r9   r¥   rã   Úprefill_fileÚpfpathÚ_pfÚprefill_messagesr¶  s           r(   Ú_load_prefill_messagesrÞ  O  s  € ä(2°3·7±7¸7ÀBÓ3GÌÔ(N�—‘˜ Ô$ÐTV€IäÐ7Ó8ò 	6Ø�7‰7Ð*¨BÓ/ò	6à�=‰=Ð0°"Ó5ð ñ
 ØÜ�,Ó×*Ñ*Ó,€FØ×ÑÔÜ!Ó# fÑ,ˆØ�=‰=Œ?ØðÜ�&˜#¨Ô0ð 	.°CÜ#Ÿy™y¨›~Ð÷	.ä#-Ð.>ÄÔ#EÐÐOÈ4ÐO÷	.ð 	.ûô ò Ü�‰ÐQÐSYÐ[aÐcdÔeÜûðús6   Â3C? ÃC3ÃC? Ã1C? Ã3C<Ã8C? Ã?	D*ÄD%Ä%D*c                 óF  — d}	 t        |«      rOt        | |«      }|sA| j                  d«      r0t        j                  t
        «      5  ddlm}  ||«       ddd«       |syt        |||«      S # 1 sw Y   ŒxY w# t
        $ r t        j                  d|d¬«       d}Y ŒAw xY w)a}  Pre-dispatch config validation: refuse unrunnable jobs (missing key, unready skill,
    unconfigured delivery) BEFORE AIAgent is built. run_one_job keys off BLOCKED_CONFIG_MARKER to
    record blocked_config and alert once (`preflight_alerted` bit). Must run after the wake gate so
    silent ticks stay silent. Opt-out: `cron.preflight: false`. Returns failure tuple or None.
    NÚpreflight_alertedr   )Úclear_preflight_alertedu7   Job '%s': preflight validation errored â€” failing openTrš  )Ú_cron_preflight_enabledÚ_preflight_job_configrH   r!   r"   r#   r¯   rá  r‹   rÇ   Ú_blocked_config_result)r<   r¥   r¤   r9   Ú
_pf_reasonrá  s         r(   Ú_preflight_or_blockræ  g  s    € ð €JðÜ" 3Ô'Ü.¨s°CÓ8ˆJÙ #§'¡'Ð*=Ô">ä×(Ñ(¬Ó3ñ 4ÝAÙ+¨FÔ3÷4ñ ØÜ! &¨(°JÓ?Ð?÷4ð 4ûô ò ä�‰ÐNÐPVÐaeˆÔfØŠ
ðús*   „AA: ÁA.ÁA: Á.A7Á3A: Á:#B ÂB rå  c           	      ó   — t         j                  d|| |«       d}	 ddlm}  || «      }|rt        nt        }d|› d	| › d
t        «       j                  d«      › d|› d�	}d|d|› d|› �fS # t        $ r t         j                  d| d¬«       Y Œbw xY w)zMThe ``blocked_config`` failure tuple for *_pf_reason*, alerting once per job.uZ   Job '%s' (ID: %s): BLOCKED by pre-dispatch config validation â€” %s (no LLM call was made)Fr   )Úmark_preflight_alertedz2Job '%s': could not persist preflight alert markerTrš  r�  r�  r‘  r   z”
**Status:** BLOCKED (configuration)

The pre-run configuration check found a problem, so the agent did not run (nothing was charged).

**Reason:** zÞ

Hermes tries again at the next scheduled time and clears this state on the first healthy run; this alert is not repeated. Check with `hermes cron doctor`. Set `cron.preflight: false` in config.yaml to disable this check.r+   rš   )
r‹   rþ   r¯   rè  r#   rÇ   ÚBLOCKED_CONFIG_SILENT_MARKERÚBLOCKED_CONFIG_MARKERrº   r&  )r¥   r¤   rå  Úalready_alertedrè  ÚmarkerÚblocked_docs          r(   rä  rä  †  sÈ   € ä
‡N�NØdØ�&˜*ô&ð €OðbÝ4Ù0°Ó8ˆñ .=Õ)ÔBW€Fà
�x�jð !Ø�hð Ü$›×/Ñ/Ð0CÓDÐEð Fð "�lð #7ð		7ð ð �+˜r f X¨Q¨z¨lÐ#;Ð;Ð;øô ò bÜ�‰ÐIÈ6Ð\`ˆÖaðbús   œA) Á)!BÂBÚjcc           	      óÞ  — ddl m}m} ddlm} |j
                  }| j                  d«      xs |j                  xs d}	 ||dœ}| j                  d«      r| j                  d«      |d<    |di |¤Ž|fS # t        $ �rï}	t        |	|«      }
t        |	«      }|
s|st         ||	«      «      |	‚t        j                  d	||
rd
nd|	«       t        |j                  «      D �]v  }t        |t         «      sŒt#        |j                  d«      xs d«      j%                  «       }t#        |j                  d«      xs d«      j%                  «       }|r|sŒr	 ddlm}m} ||dœ}|j                  d«      r|d   |d<    ||«      }|r||d<    |di |¤Ž} |||«      |d<   t        j-                  d||j                  d«      |«       ddlm}  ||xs7 t        |j0                  t         «      r|j0                  j                  d«      nd||j                  d«      |«      |d<   ||fc cY d}	~	S # t        $ r#}t        j3                  d|||«       Y d}~�Œqd}~ww xY w t         ||	«      «      |	‚d}	~	ww xY w)a\  Resolve the runtime, walking the fallback chain on auth/transient-network errors. Returns
    ``(runtime, model)``; provider+model swap atomically (never swap only the provider while keeping
    a paid primary model). Provider precedence: per-job pin > cron.model_provider > persisted
    global config (None lets resolve_runtime_provider read it).r   )Úresolve_runtime_providerÚformat_runtime_provider_error)Ú	AuthErrorr˜   N)Ú	requestedÚtarget_modelÚbase_urlÚexplicit_base_urlzCJob '%s': primary provider resolve failed (%s: %s), trying fallbackÚauthztransient networkr+   rö   )Úeffective_runtime_providerÚresolve_entry_api_keyÚexplicit_api_keyz*Job '%s': fallback resolved to %s model %s)Úpre_agent_fallback_noticeÚ_fallback_noticez Job '%s': fallback %s failed: %srÖ   )Úhermes_cli.runtime_providerrð  rñ  Úhermes_cli.authrò  rö   rH   r½  r#   rJ   Ú$_is_transient_provider_resolve_errorrô   r‹   rþ   r   r9   rK   rk   r-   Úhermes_cli.fallback_configrø  rù  rý   rû  r¼  rÇ   )r<   r¥   rî  rð  rñ  rò  rö   ró  Úruntime_kwargsÚresolve_excÚis_authÚis_transient_netr³  Úfb_providerÚfb_modelrø  rù  Ú	fb_kwargsÚ
fb_api_keyÚruntimerû  Úfb_excs                         r(   Ú_resolve_job_runtimer  ¡  sr  € ÷
Aå)à�H‰H€EØ—‘˜
Ó#ÒG r×'?Ñ'?ÒGÀ4€Ið6Xð #à!ñ
ˆð
 �7‰7�:ÔØ25·'±'¸*Ó2EˆNÐ.Ñ/Ù'Ñ9¨.Ñ9¸5Ð@Ð@øÜó +Xô ˜[¨)Ó4ˆÜ?ÀÓLÐÙÑ+ÜÑ<¸[ÓIÓJÐP[Ð[ä�‰ØQØ™g‘FÐ+>Àô	Mô (¨¯©Ó/ó 	^ˆEÜ˜e¤TÔ*ØÜ˜eŸi™i¨
Ó3Ò9°rÓ:×@Ñ@ÓBˆKÜ˜5Ÿ9™9 WÓ-Ò3°Ó4×:Ñ:Ó<ˆHÙ¡hØð^ßhà*5ÀxÑP�	Ø—9‘9˜ZÔ(Ø5:¸:Ñ5F�IÐ1Ñ2Ù2°5Ó9�
ÙØ4>�IÐ0Ñ1Ù2Ñ?°YÑ?�ñ 'AÀÈÓ&P�˜
Ñ#Ü—‘Ø@Ø˜GŸK™K¨
Ó3°Xô?õ
 QÙ.GØÒiÄ*ÈRÏ\É\Ô[_ÔB` "§,¡,×"2Ñ"2°:Ô">ÐfhØ˜7Ÿ;™; zÓ2°Hó/>�Ð*Ñ+ð  Ð(×(øÜò ^Ü—‘Ð?ÀÈÐV\×]Ò]ûð^úð=	^ô@ Ñ8¸ÓEÓFÈKÐWûðW+XúsI   ¿3A3 Á3
I,Á=CI'ÅCH%ÈI'ÈI,È%	IÈ.IÉI'ÉIÉI'É'I,r	  c           	      ó|  — t        | j                  d«      xs d«      j                  «       j                  «       }|sy 	 ddlm}  ||«      }|j                  «       r1t        j                  d||t        |j                  «       «      «       |S 	 y # t        $ r"}t        j                  d|||«       Y d }~y d }~ww xY w)Nr˜   r+   r   )Ú	load_poolz@Job '%s': loaded credential pool for provider %s with %d entriesz3Job '%s': failed to load credential pool for %s: %s)rk   rH   r-   rŸ   Úagent.credential_poolr  Úhas_credentialsr‹   rý   r£   Úentriesr#   rÇ   )r	  r¥   Úruntime_providerr  r  r¶  s         r(   Ú_load_credential_poolr  å  sµ   € Ü˜7Ÿ;™; zÓ2Ò8°bÓ9×?Ñ?ÓA×GÑGÓIÐÙØð	iÝ3ÙÐ)Ó*ˆØ×ÑÔ!Ü�K‰KØRØÐ(¬#¨d¯l©l«nÓ*=ô?ð ˆKð	 "ð øô ò iÜ�‰ÐJÈFÐTdÐfg×hÐhØûðiús   ¿AB Â	B;ÂB6Â6B;c                 óÀ   — 	 ddl m}  |«       }|r!t        j                  d| t	        |«      «       yy# t
        $ r!}t        j                  d| |«       Y d}~yd}~ww xY w)zŒRegister MCP servers for the agent's tool registry. Idempotent across ticks; non-fatal so a
    broken MCP server never kills a working job.r   )Údiscover_mcp_toolsz"Job '%s': %d MCP tool(s) availablez3Job '%s': MCP initialization failed (non-fatal): %sN)Útools.mcp_tool_discoveryr  r‹   rý   r£   r#   rþ   )r¥   r  Ú
_mcp_toolsÚ_mcp_excs       r(   Ú_init_cron_mcp_toolsr  ö  sZ   € ð`õ 	@Ù'Ó)ˆ
ÙÜ�K‰KÐ<¸fÄcÈ*ÃoÕVð øäò `Ü�‰ÐLÈfÐV^×_Ñ_ûð`ús   ‚/3 ³	A¼AÁAc                 óÆ  — t        «       }	 ddlm} |dk  r |«       S t        j                  j                  d¬«      }t        j                  «       }|j                  |j                  |«      }	 |j                  |¬«      |j                  d¬«       S # t        j                  j                  $ r |j                  t        «       ‚ w xY w# |j                  d¬«       w xY w# t        j                  j                  $ r* t        j!                  d| j#                  d	d
«      |«       Y yt$        $ r1}t        j'                  d| j#                  d	d
«      |«       Y d}~yd}~ww xY w)zÏOpen the SQLite session store under its own timeout (HERMES_CRON_TIMEOUT only watches
    run_conversation). A wedged sqlite3.connect returns None (no session store) instead of
    wedging the worker thread.r   )ÚacquirerE   ©r  ©rg  F)r  u‡   Job '%s': SessionDB init did not return within %.0fs â€” proceeding without a session store for this run instead of blocking it foreverrG   rû   z0Job '%s': SQLite session store not available: %sN)Ú_get_session_db_timeoutr%   r  r  r  r  ÚcontextvarsÚcopy_contextÚsubmitÚrunr$   r  r‡  Úadd_done_callbackr)   r‹   rŒ   rH   r#   rÇ   )r<   Ú_session_db_timeoutr  Ú_session_db_poolÚ_session_db_contextÚ_session_db_futurer¶  s          r(   Ú_open_cron_session_dbr'    sN  € ô 2Ó3Ðð`Ý1à !Ò#Ù“9ÐÜ%×-Ñ-×@Ñ@ÈQÐ@ÓOÐô *×6Ñ6Ó8ÐØ-×4Ñ4Ð5H×5LÑ5LÈgÓVÐð	2Ø%×,Ñ,Ð5HÐ,ÓIð ×%Ñ%¨5Ð%Õ1øô ×!Ñ!×.Ñ.ò 	ð ×0Ñ0Ô1NÔOØð	ûð ×%Ñ%¨5Ð%Õ1ûÜ×Ñ×*Ñ*ò 5Ü�‰ðRà�G‰G�D˜#ÓÐ 3õ	5ð ô ò `Ü�‰ÐGÈÏÉÐQUÐWZÓI[Ð]^×_Ð_Øûð`úsH   ŒC! žAC! Á/B Â C! Â4CÃC
 Ã
CÃC! Ã!AE Ä'E Ä/'EÅE c                 óî  — i }t        | d«      r2t        j                  t        «      5  | j	                  «       }ddd«       |j                  dd«      }|j                  dd«      }t        j                  d|||||j                  dd«      |j                  d	d«      |j                  d
«      xs d«       t        | dd¬«       t        d|› dt        |«      › dt        |«      › d|› �«      ‚# 1 sw Y   Œ³xY w)zHLog the agent's last activity, hard-interrupt it and raise TimeoutError.Úget_activity_summaryNÚlast_activity_descÚunknownÚseconds_since_activityr   z_Job '%s' idle for %.0fs (inactivity limit %.0fs) | last_activity=%s | iteration=%s/%s | tool=%sÚapi_call_countÚmax_iterationsÚcurrent_toolÚnonezCron job timed out (inactivity)zcron inactivity watchdog)Útool_reasonrÃ  z' idle for z	s (limit u   s) â€” last activity: )Úhasattrr!   r"   r#   r)  rH   r‹   rŒ   r   r‡  rI   )rÜ   r¤   r  Ú	_activityÚ
_last_descÚ	_secs_agos         r(   Ú_raise_inactivity_timeoutr6  5  s÷   € à€IÜˆuÐ,Ô-Ü× Ñ ¤Ó+ñ 	5Ø×2Ñ2Ó4ˆI÷	5à—‘Ð3°YÓ?€JØ—‘Ð6¸Ó:€IÜ
‡L�Lð	9à�)˜WØ�I—M‘MÐ"2°AÓ6¸	¿¹ÐFVÐXYÓ8ZØ�‰�nÓ%Ò/¨ô1ô ˜5Ð"CÐQkÕlÜ
Ø
�X�J˜kÜˆy‹>Ð
˜)¤C¨£L >ð 2Ø(˜\ð	+ó,ð ,÷	5ð 	5ús   ¨C+Ã+C4ÚpromptÚtask_idÚworker_statec                 óN  ‡ ‡‡‡‡‡‡‡‡‡‡‡‡— t        «       }|dkD  r|ndŠdŠ|j                  d«      }	t        |	t        «      xr |	j                  d«      dk(  Š|j                  d«      }
t        |
t        «      rt	        |
j                  d«      xs d	«      nd	Št        j                  «       Šdˆ ˆˆfd„}ˆˆˆˆˆfd„}t        j                  j                  d¬«      }t        j                  «       }|j                  |j                  ‰ j                  ||¬«      Š|�‰|d<   dŠt        j                   «       Šd
t"        fˆ fd„Šdˆˆˆˆˆˆfd„}t        j$                  |dt	        ‰«      dd › �d¬«      }	 ‰�|j'                  «        ‰€‰s‰€‰j)                  «       }nTd}	 t        j                  j+                  ‰h‰¬«      \  }}|r |«        ‰j)                  «       }n‰rn |«         |«        ŒQ‰j1                  «        |j/                  dd¬«       ‰rt3        ‰ ‰‰«       t        |t        «      s$t5        dt7        |«      j8                  › d|›�«      ‚|S # t,        $ r |j/                  dd¬«       ‚ w xY w# ‰j1                  «        |j/                  dd¬«       w xY w)z Run ``agent.run_conversation`` on a worker thread under the inactivity (not wall-clock)
    watchdog: default 600s, override HERMES_CRON_TIMEOUT, 0 = unlimited.r   Ng      @r>   r?   ÚonceÚ	run_claimÚbyr+   r   c                  ó†   •— ‰�‰j                  «       sy ‰ �t        ‰ d«      r‰ j                  d«       t        d‰› d�«      ‚)NÚ	interruptz"Cron fire claim ownership was lostrÃ  z'' lost its durable fire claim ownership)r?  r2  r?  rô   )rÜ   rŸ  r¤   s   €€€r(   Ú_abort_if_fire_claim_lostz;_run_agent_with_watchdog.<locals>._abort_if_fire_claim_lost`  sH   ø€ ØÐ |×':Ñ':Ô'<ØØÐ¤¨°Ô!<Ø�O‰OÐ@ÔAÜ˜Z¨ zÐ1XÐYÓZÐZrf   c                  óÀ   •— ‰r‰sy t        j                  «       } | ‰z
  t        k  ry | Š	 t        ‰‰¬«       y # t        $ r t
        j                  d‰d¬«       Y y w xY w)N©Úexpected_ownerz$Job '%s': run_claim heartbeat failedTrš  )r‡   rˆ   Ú_RUN_CLAIM_HEARTBEAT_SECONDSr
  r#   r‹   rÇ   )Ú_monoÚ_is_oneshotÚ_last_claim_heartbeatÚ_run_claim_ownerr¥   r¤   s    €€€€€r(   Ú_heartbeat_run_claim_if_duez=_run_agent_with_watchdog.<locals>._heartbeat_run_claim_if_dueg  sf   ø€ áÑ"2ØÜ—‘Ó ˆØÐ(Ñ(Ô+GÒGØØ %Ðð	ZÜ Ð7GÖHøÜò 	ZÜ�L‰LÐ?ÀÐTXˆLÖYð	Zús   «9 ¹!AÁArE   r  )r8  r&   Fc                  óœ   •— t        ‰d«      sy	 ‰j                  «       } t        | j                  dd«      xs d«      S # t        $ r Y yw xY w)Nr)  r†   r,  )r2  r)  r‰   rH   r#   )Ú_actrÜ   s    €r(   Ú_idle_secondsz/_run_agent_with_watchdog.<locals>._idle_seconds~  sR   ø€ Ü�uÐ4Ô5Øð	Ø×-Ñ-Ó/ˆDÜ˜Ÿ™Ð":¸CÓ@ÒGÀCÓHÐHøÜò 	Ùð	ús   �.? ¿	AÁ
Ac                  óF   •— ‰€y t        ‰‰‰ ‰‰j                  ¬«      rdŠy y )N)r   r  r  r  r  T)r  rº  )Ú_POLL_INTERVALÚ_cron_futureÚ_cron_inactivity_limitrL  Ú_inactivity_timeoutÚ_watch_stops   €€€€€€r(   Ú_watch_inactivityz3_run_agent_with_watchdog.<locals>._watch_inactivity‡  s5   ø€ à!Ð)ØÜ$Ø*Ð4JÐSaØ¨,×*;Ñ*;õ=ð #'Ñð=rf   zcron-inactivity-é   Trp  r  r  z agent.run_conversation returned z instead of dict: rB  )r  rH   rJ   rK   rk   r‡   rˆ   r  r  r  r  r  r   r!  Úrun_conversationrt  ru  r‰   rv  rw  r$   r  r#   r  rë   r6  rô   Útyperg   )rÜ   r7  r<   r¥   r¤   r8  rŸ  r9  Ú_cron_timeoutÚ_job_scheduleÚ
_run_claimr@  rI  Ú
_cron_poolÚ_cron_contextrS  Ú_watch_threadr$   rº  Ú_rN  rO  rP  rL  rQ  rF  rG  rH  rR  s   `  `` `             @@@@@@@@@r(   Ú_run_agent_with_watchdogr^  J  s¡  ÿü€ ô -Ó.€MØ.;¸aÒ.?™]ÀTÐØ€Nð —G‘G˜JÓ'€MÜ˜]¬DÓ1ÒY°m×6GÑ6GÈÓ6OÐSYÑ6Y€KØ—‘˜Ó%€JÜ:DÀZÔQUÔ:V”s˜:Ÿ>™>¨$Ó/Ò5°2Ô6Ð\^ÐÜ ŸN™NÓ,Ð÷[÷Zð Zô ×#Ñ#×6Ñ6À1Ð6ÓE€Jä×,Ñ,Ó.€MØ×$Ñ$Ø×Ñ˜5×1Ñ1°6À7ð %ó L€LàÐØ!-ˆ�XÑØÐÜ—/‘/Ó#€Kðœ5õ ÷'ò 'ô ×$Ñ$Ø Ð)9¼#¸f»+ÀbÀq¸/Ð9JÐ'KÐTXôZ€Mð=Ø!Ð-ð
 ×ÑÔ!Ø!Ð)±+À,ÐBVØ!×(Ñ(Ó*‰FàˆFØÜ$×,Ñ,×1Ñ1°<°.È.Ð1ÓY‘��aÙÙ-Ô/Ø)×0Ñ0Ó2�FØÙ&ØÙ)Ô+Ù+Ô-ð ð 	�‰ÔØ×Ñ °tÐÔ<áÜ! %¨Ð3IÔJä�fœdÔ#ÜØ.¬t°F«|×/DÑ/DÐ.EÐEWÐX^ÐWaÐbó
ð 	
ð €Møô ò Ø×Ñ °tÐÔ<Øðûð 	�‰ÔØ×Ñ °tÐÕ<ús   Å9A=I ÉI<É<I? É?%J$r$   c                 óü  — t        | j                  d«      xs d«      }| j                  d«      xs dj                  «       }t        | «      }| j                  d«      du s| j                  d«      du r$|s"t	        | j                  d«      xs |xs d	«      ‚|rt
        j                  d
|«       | j                  dd«      xs d}|rddlm}  ||| j                  dg «      «      }|j                  «       dk(  rd}|j                  «       r¡|rŸg }		 ddl	m
}
 t        | j                  d«      xs d«      }dg|
¢­D ]D  }d|ii fD ]9  }	  |j                  ||fi |¤Ž}|sŒ|	j                  |j                  «       «       Œ; ŒF |j                  «       |	v rt
        j                  d||«       d}|S # t        $ r d}
Y Œ¥w xY w# t        $ r' 	 |j                  |«      }n# t        $ r d}Y nw xY wY Œ“t        $ r d}Y ŒŸw xY w)zïDeliverable final response from a ``run_conversation`` result. Raises RuntimeError on
    `failed=True`/`completed=False`: the error text may sit in `final_response` and would otherwise
    be delivered as the reply with the job marked ok.Úturn_exit_reasonr+   Úfinal_responseÚfailedTÚ	completedFrŒ   zagent reported failurez„Job '%s' reached the iteration limit but produced a final fallback response; delivering the response instead of failing the cron runr   )Ú(repair_explicit_computer_use_media_pathsÚmessagesú(No response generated))ÚPERSISTENCE_ERROR_CAUSES)ÚlockedÚdiskr+  rö   NuN   Job '%s': abnormal empty turn (%s) â€” suppressing explainer for cron delivery)rk   rH   r-   r   rô   r‹   rþ   Úgateway.media_repairrd  Úhermes_state_errorsrg  r#   Ú#_format_turn_completion_explanationr¹   rà   rý   )r$   r¥   r¤   ÚAIAgentr`  Úfinal_response_textÚmax_iteration_summaryra  rd  Ú_explainer_variantsÚ_causesÚ_modelÚ_causeÚ_kwargsÚ_variants                  r(   Ú_final_response_from_resultrv  ¸  s-  € ô ˜6Ÿ:™:Ð&8Ó9Ò?¸RÓ@ÐØ!Ÿ:™:Ð&6Ó7Ò=¸2×DÑDÓFÐÜ4°VÓ<ÐØ‡z�z�(Ó˜tÑ#¨¯
©
°;Ó(?À5Ñ(HÑQfÜ˜6Ÿ:™: gÓ.ÒaÐ2EÒaÐIaÓbÐbÙÜ�‰ðFàô	ð
 —Z‘ZÐ 0°"Ó5Ò;¸€NáÝQáAØ˜FŸJ™J z°2Ó6ó8ˆà×ÑÓÐ!:Ò:Øˆð ×ÑÔÑ"2à Ðð	4ÝOô
 �V—Z‘Z Ó(Ò.¨BÓ/ˆØÐ&˜gÑ&ò 	AˆFØ$ fÐ-¨rÐ2ò A�ð"ØJ˜w×JÑJÐK[Ð]cÑoÐgnÑo�Hò Ø'×.Ñ.¨x¯~©~Ó/?Õ@ñAð	Að ×ÑÓ!Ð%8Ñ8Ü�K‰KØ`ØÐ(ô*ð  ˆNØÐøô/ ò 	4Ø3ŠGð	4ûô !ò &ð&Ø#*×#NÑ#NÐO_Ó#`™øÜ$ò &Ø#%šð&üä ò "Ø!’Hð"úsN   ÄF. ÅF?Æ.F<Æ;F<Æ?	G;Ç	GÇG;ÇG)	Ç&G;Ç(G)	Ç)G;Ç.
G;Ç:G;Úcron_session_idc                 ó:  — t        | |«      }|}	 |j                  |«      }|r|}	 dj                  |j                  «       «      dd j                  «       xs d|› �}
|
› dt        «       j                  d«      › �}t        |||«      st        ||d|› �«       d}	 |j                  |g«      }|j!                  |«      }|dv rd}t        j#                  d|||«       	 |j%                  ||«       |�d|_        	 ddlm}  ||«       y# t        t        f$ rh}t	        j
                  t        t        f«      5  t        |dd«      }	|	r|	}ddd«       n# 1 sw Y   nxY wt        j                  d||«       Y d}~�ŒUd}~ww xY w# t        t        f$ rn}t        j                  d	||«        t        |d
d„ «      d|› �«      d|› d|dd › �fD ]'  }	 t        |||«      r nŒ# t        t        f$ r Y Œ%w xY w Y d}~�Œbd}~ww xY w# t        t        f$ r"}t        j                  d||«       Y d}~�ŒRd}~ww xY w# t        t        f$ r"}t        j                  d||«       Y d}~�Œjd}~ww xY w# t        t        f$ r!}t        j                  d||«       Y d}~yd}~ww xY w)zTTitle, classify, end and release the cron session after the agent turn has returned.r2   Nz4Job '%s': failed to resolve cron compression tip: %srš   rÑ  zcron u    Â· z%b %d %H:%Mz.Job '%s': failed to set cron session title: %sr,   c                 ó   — | S r=  rÖ   )Úbs    r(   r]  z(_finalize_cron_session.<locals>.<lambda>  s   € È€ rf   iúÿÿÿÚcron_complete)ÚinterruptedrŒ   ÚemptyÚcron_incomplete_no_outputu^   Job '%s': session ended without a final assistant message (lifecycle=%s) â€” booking run as %sz5Job '%s': session lifecycle classification failed: %sFz#Job '%s': failed to end session: %sr   r   z2Job '%s': failed to close SQLite session store: %s)r{  Úget_compression_tipr#   rs  r!   r"   r0   r‹   rÇ   r  Úsplitr-   rº   r&  r7   Úsession_lifecycle_statusesrH   rþ   Úend_sessionÚ_end_session_on_closer%   r    )r1   rÜ   r¥   r¤   rw  r}  Ú_final_cron_session_idÚ_compression_tipr¶  Ú_agent_session_idÚ_title_baseÚ_cron_titleÚ	_fallbackÚ_end_reasonÚ	_statusesÚ
_lifecycler    s                    r(   Ú_finalize_cron_sessionr�  ö  så  € ô (¨
°FÓ;€Kð -ÐðXØ&×:Ñ:¸?ÓKÐÙØ%5Ð"ð ð —h‘h˜xŸ~™~Ó/Ó0°°"Ð5×;Ñ;Ó=ÒQÀ5ÈÈÐAQˆØ$˜ T¬+«-×*@Ñ*@ÀÓ*OÐ)PÐQˆÜ& {Ð4JÈKÔXÜ# KÐ1GÈ5ÐQWÐPXÐIYÔZð4 "€Kð
YØ×:Ñ:Ð<RÐ;SÓTˆ	Ø—]‘]Ð#9Ó:ˆ
ØÐ:Ñ:Ø5ˆKÜ�N‰Nð?à˜
 Kô1ðGØ×ÑÐ 6¸ÔDð ÐØ*/ˆEÔ'ðVÝ:Ù˜Õ%øôS Ô(Ð)ò XÜ× Ñ ¤)Ô->Ð!?Ó@ñ 
	;Ü '¨¨|¸TÓ BÐñ !Ø):Ð&÷
	;÷ 
	;ñ 
	;úô 	�‰ÐKÈVÐUV×WÒWûðXûô, Ô(Ð)ò Ü�‰ÐEÀvÈqÔQð KŒG�KÐ!<¹kÓJÈUÐSYÐRZÐK[Ó\Ø�F�8˜1Ð3°B°CÐ8Ð9Ð:ð<ò 	ˆIðÜ*¨;Ð8NÐPYÔZÙð [øäÔ0Ð1ò Ùðúð	ÿùð	ûôF Ô(Ð)ò YÜ�‰ÐLÈfÐVW×XÒXûðYûô Ô(Ð)ò GÜ�‰Ð:¸FÀA×FÒFûðGûô
 Ô(Ð)ò VÜ�‰ÐIÈ6ÐST×UÑUûðVús´   �D ¦A2F ÂAH ÃH6 Ã9I* ÄE?ÄE:Ä6EÅ	E:ÅE	ÅE:Å:E?ÆG?Æ<G:ÇGÇG:ÇG0Ç-G:Ç/G0Ç0G:Ç:G?ÈH3ÈH.È.H3È6I'ÉI"É"I'É*JÉ9JÊJr4   c                 ót   — d|› d|› dt        «       j                  d«      › d| j                  dd«      › d|› d	�S )
zDHeader of the persisted run document (title, ids, schedule, prompt).r�  r�  r‘  r   z
**Schedule:** Úschedule_displayzN/Az

## Prompt

ú

)rº   r&  rH   )r<   r4   r¥   r7  s       r(   Ú_run_doc_headerr‘  O  s\   € ð �u�gð Ø�hð Ü$›×/Ñ/Ð0CÓDÐEð FØŸ™Ð!3°UÓ;Ð<ð =Ø�x˜tð		%ðrf   c           
      ó¼  — | j                  d«      sddlm}m} 	  |«        | j                  d«      rt        | |||«      d
fS ddlm	}m
}	  |	| «      rt        |||«      d
fS t        | |||«      \  }
}}|
�|
d
fS d
}| j                  d«      }|rqt        | |t        | |«      |¬«      }|\  }}|rQt        |«      sFt        j!                  d||«       d|› d|› dt#        «       j%                  d«      › d�}d|t&        d
fd
fS 	 t)        | |||¬«      }|€"t        j!                  d|«       dd	t&        d
fd
fS d
|fS # |$ r9}t        j                  d||«       dd|› d|› d�d	t        |«      fd
fcY d
}~S d
}~ww xY w# t*        $ rY}t        j-                  d|||«       d|› d|› dt#        «       j%                  d«      › d|› d�	}d|d	t        |«      fd
fcY d
}~S d
}~ww xY w)zùRun every pre-agent gate and build the prompt. Returns ``(early_result, prompt)``: an early
    result short-circuits ``run_job`` (no_agent job, empty payload, monitor gate, wake gate,
    injection block, empty prompt); otherwise ``prompt`` is set.r—   r   )ÚInvalidUserConfigErrorÚrequire_parseable_user_configu    Job '%s': refusing to run â€” %sFr�  z	

Error: r  r+   N)ÚEMPTY_PAYLOAD_ERRORÚjob_payload_is_emptyrœ  rž  z6Job '%s' (ID: %s): wakeAgent=false, skipping agent runr�  r‘  r   u<   

Script gate returned `wakeAgent=false` â€” agent skipped.
T)Úprerun_scriptr®  Úruntime_data_promptu=   Job '%s' (ID: %s): blocked by prompt-injection scanner â€” %sz£
**Status:** BLOCKED

The assembled prompt (user prompt + loaded skill content) tripped the cron injection scanner and the agent was NOT run.

**Scanner result:** a  

Audit the skill(s) attached to this job for prompt-injection payloads or invisible-unicode markers. If the skill is legitimate and the match is a false positive, rephrase the content to avoid the threat pattern (`tools/cronjob_tools.py::_CRON_THREAT_PATTERNS`).z6Job '%s': script produced no output, skipping AI call.)rH   rd  r“  r”  r‹   rŒ   rk   r­  r¯   r•  r–  r£  r¹  r¤  r–  r¦  rý   rº   r&  r§  Ú_build_job_promptrÔ   rþ   )r<   r¥   r¤   r®  rŸ  r“  r”  rË   r•  r–  Ú_earlyÚmonitor_contextr—  r¨  Ú_ran_okÚ_script_outputÚ
silent_docr7  Ú	block_excrí  s                       r(   Ú_prepare_job_promptr   ]  sh  € ð �7‰7�:Ôß[ð	\Ù)Ô+ð ‡w�wˆzÔÜ   f¨h¸ÓEÀtÐKÐK÷ Dá˜CÔ Ü# F¨HÐ6IÓJÈDÐPÐPä,?ÀÀVÈXÐWcÓ,dÑ)€FˆL˜/ØÐØ�tˆ|Ðð €MØ—'‘'˜(Ó#€KÙÜ<ØØÜ(¨¨fÓ5Ø%ô	
ˆð #0Ñˆ�ÙÔ+¨NÔ;Ü�K‰KÐPÐRZÐ\bÔcà˜x˜jð )Ø%˜hð '!Ü!,£×!7Ñ!7Ð8KÓ!LÐ Mð NNðNð ð ˜*¤m°TÐ:¸DÐ@Ð@ð>Ü"Ø˜}¸<Ø /ô
ˆð. €~Ü�‰ÐLÈhÔWØ�bœ-¨Ð.°Ð4Ð4Ø�ˆ<ÐøðQ &ò 	\Ü�L‰LÐ;¸VÀSÔIØ˜\¨(¨°;¸s¸eÀ2ÐFÈÌCÐPSËHÐUÐW[Ð[Õ[ûð	\ûôd &ò >ä�‰ØKÈXÐW]Ð_hô	
ð ˜8˜*ð %Ø!˜(ð #Ü(›]×3Ñ3Ð4GÓHÐIð J#ð $- +ð .Tð
Tð 	ð �{ B¬¨I«Ð7¸Ð=Õ=ûð%>ús<   ›D8 ÄE9 Ä8E6Ä=.E1Å+E6Å1E6Å9	GÆAGÇGÇG)Ú!HERMES_CRON_AUTO_DELIVER_PLATFORMÚ HERMES_CRON_AUTO_DELIVER_CHAT_IDÚ"HERMES_CRON_AUTO_DELIVER_THREAD_IDc                   ó:   — e Zd ZdZdededee   fd„Zd	d„Zd	d„Z	y)
Ú_CronRunScopeu0  Per-run ContextVar / tool-cwd scope for ``run_job`` (ContextVars, not os.environ, so
    parallel jobs don't clobber each other). Construct before the try, ``enter()`` as its first
    statement, ``exit()`` in the finally â€” every setter here has a matching reset there.

    HERMES_SESSION_* are deliberately NOT seeded from job["origin"]: it is delivery metadata, not
    a sender, and terminal/tts/skills/send_message tools would act as if the origin user were
    driving the agent. Delivery reads job["origin"] / HERMES_CRON_AUTO_DELIVER_* directly.
    r<   r¥   Úexecution_idc                 óÐ  — ddl m}m} ddlm} || _        t        ||«      | _         |dddd| j                  xs d¬«      | _        t        D ]  }||   j                  d«       Œ d|› d|xs1 |j                  d	«      xs t        j                  «       j                  › �| _        | j                  r || j                  | j                  «       |d
   | _        d | _        d | _        y )Nr   )Úset_session_varsÚ_VAR_MAP)Úrecord_session_cwdr+   F)ÚplatformÚchat_idÚ	chat_nameÚasync_deliveryÚcwdzcron:ú:r¦  ÚHERMES_CRON_SESSION)Úgateway.session_contextr¨  r©  Útools.terminal_toolrª  Ú_var_mapr–  r”  Ú_ctx_tokensÚ_CRON_DELIVERY_VARSrë   rH   ÚuuidÚuuid4Úhexr8  Ú_cron_session_varÚ_cron_session_tokenÚ_non_dispatcher_token)rd   r<   r¥   r¦  r¨  r©  rª  rF   s           r(   rc   z_CronRunScope.__init__Å  sØ   € ßFÝ:à ˆŒä+¨C°Ó8ˆŒÙ+ØØØð !Ø—‘Ò" ô!
ˆÔô$ (ò 	#ˆDØ�T‰N×Ñ˜rÕ"ð	#ð ˜v˜h a¨Ò(c¸¿¹ÀÓ8OÒ(cÔSW×S]ÑS]ÓS_×ScÑScÐ'dÐeˆŒØ�<Š<Ù˜tŸ|™|¨T¯\©\Ô:Ø!)Ð*?Ñ!@ˆÔØ#'ˆÔ Ø%)ˆÕ"rf   Nc                 ób   — | j                   j                  d«      | _        t        «       | _        y )NÚ1)rº  rë   r»  r   r¼  r>  s    r(   Úenterz_CronRunScope.enteré  s+   € ð $(×#9Ñ#9×#=Ñ#=¸cÓ#BˆÔ ô
 &HÓ%IˆÕ"rf   c                 óZ  — ddl m} ddlm}  || j                  «        || j
                  «       | j                  �%| j                  j                  | j                  «       | j                  �t        | j                  «       t        D ]   }| j                  |   j                  d«       Œ" y )Nr   )Úclear_session_vars)Úclear_session_cwdr+   )r²  rÁ  r³  rÂ  r8  rµ  r»  rº  Úresetr¼  r   r¶  r´  rë   )rd   rÁ  rÂ  rF   s       r(   Úexitz_CronRunScope.exitó  sˆ   € Ý>Ý9á˜$Ÿ,™,Ô'Ù˜4×+Ñ+Ô,Ø×#Ñ#Ð/Ø×"Ñ"×(Ñ(¨×)AÑ)AÔBØ×%Ñ%Ð1Ü-¨d×.HÑ.HÔIÜ'ò 	(ˆDØ�M‰M˜$Ñ×#Ñ# BÕ'ñ	(rf   rB  )
rg   rh   ri   rj   rK   rk   r   rc   r¿  rÄ  rÖ   rf   r(   r¥  r¥  »  s1   „ ñð"*˜Dð "*¨#ð "*¸XÀc¹]ó "*óHJô(rf   r¥  c                 óR  — ddl m}m} ddlm}  |t        «       «        |t        «       ¬«       t        | «      }|rk|d   j                  |d   «       |d   j                  t        |d   «      «       |d	   j                  |j                  d
«      €dnt        |d
   «      «       yy)z[Re-read .env for this run and publish the auto-deliver target into the session ContextVars.r   )r˜  Úreset_secret_source_cache)r©  r™  r¡  r«  r¢  r¬  r£  Ú	thread_idNr+   )
r¢  r˜  rÆ  r²  r©  r}   Ú_resolve_delivery_targetrë   rk   rH   )r<   r˜  rÆ  r©  Údelivery_targets        r(   Ú*_reload_dotenv_and_publish_delivery_targetrÊ  	  s    € ÷ TÝ0áÔ.Ó0Ô1ÙÔ#3Ó#5Õ6ä.¨sÓ3€OÙØÐ4Ñ5×9Ñ9¸/È*Ñ:UÔVØÐ3Ñ4×8Ñ8¼¸_ÈYÑ=WÓ9XÔYØÐ5Ñ6×:Ñ:Ø!×%Ñ% kÓ2Ð:‰BÄÀOÐT_ÑD`Ó@aõ	
ð rf   c                   óœ   — e Zd ZU dZdZee   ed<   dZe	ed<   dZ
eed<   dZeed<   dZeed<   dZeed	<   dZeed
<   dZeed<   dZee	   ed<   y)Ú_CronAgentSetupz\Everything ``AIAgent(...)`` needs that is resolved from job + config (or a preflight block).NÚblockedr+   rö   r	  rÝ  r.  Úreasoning_configÚfallback_modelÚcredential_poolÚfallback_notice)rg   rh   ri   rj   rÍ  r   Ú
_RunResultr¾  rö   rk   r	  rK   rÝ  r   r.  rÎ  rÏ  rÐ  rÑ  rÖ   rf   r(   rÌ  rÌ  	  si   … áfØ$(€GˆX�jÑ!Ó(Ø€Eˆ3ƒOØ€GˆTÓØ Ð�cÓ Ø€N�CÓØ Ð�cÓ Ø€N�CÓØ€O�SÓØ%)€O�X˜c‘]Ô)rf   rÌ  c                 ó   — |j                   }t        |j                  ¬«      }t        ||«      |_        ddlm} |j                  di «      j                  d«      }|€|j                  d«      } ||«      |_        t        | «       t        | |||«      |_        |j                  �|S t        | ||«      \  |_        |_        |j                  j                  dd«      |_        t!        | t#        |t$        «      r|ni t'        |j                  «      «      |_        t+        |«      xs d|_        t/        |j                  |«      |_        t3        |«       t5        |«      r t7        | |«      }|rt9        |||«      |_        |S )z¯Resolve model/runtime/reasoning/pool for the run, in the original gate order: exfil guard ->
    preflight (may block) -> runtime (+ fallback chain) -> credential pool -> MCP.)rö   r   )Úresolve_turn_limitrÜ   Ú	max_turnsNrü  )r9   rÌ  rö   rÞ  rÝ  rd  rÔ  rH   r.  Ú_guard_job_credential_exfilræ  rÍ  r  r	  rƒ  rÑ  r  rJ   rK   rk   rÎ  r   rÏ  r  rÐ  r  râ  Ú_empty_requested_mcp_toolsetsrä  )	r<   r¥   r¤   rî  rË  ÚsetupÚ_resolve_turn_limitÚ_mtÚ_mcp_reasons	            r(   Ú_resolve_cron_agent_setuprÜ  "	  sL  € ð �6‰6€DÜ "§(¡(Ô+€EÜ3°D¸&ÓA€EÔõ LØ
�(‰(�7˜BÓ
×
#Ñ
# KÓ
0€CØ
€{Ø�h‰h�{Ó#ˆÙ.¨sÓ3€EÔô   Ô$ä'¨¨V°X¸tÓD€E„MØ‡}�}Ð Øˆä!5°c¸6À2Ó!FÑ€E„M�5”;Ø!ŸM™M×-Ñ-Ð.@À$ÓG€EÔÜ:Ø”Z ¤dÔ+‰T°´S¸¿¹Ó5Eó€EÔô .¨dÓ3Ò;°t€EÔÜ1°%·-±-ÀÓH€EÔä˜Ô ô ˜tÔ$Ü3°C¸Ó>ˆÙÜ2°6¸8À[ÓQˆEŒMØ€Lrf   rË  rØ  c          	      ó\  — |j                   }|j                  d«      xs i } | d*i d|j                  “d|j                  d«      “d|j                  d«      “d|j                  d«      “d|j                  d«      “d|j                  d«      “d|j                  d«      “d	|j                  d
«      “d|j                  d«      “d|j                  “d|j                  “d|j
                  “d|j                  “d|j                  “d|j                  d«      “d|j                  d«      “d|j                  d«      “d|j                  d«      “d|j                  d«      xs i j                  d«      “dt        ||«      “dt        |«      “dd “d!t        |«       “d"d “d#d$“d%d “d&d'“d(|“d)|“ŽS )+NÚprovider_routingrö   Úapi_keyrõ  r˜   Úrequested_providerÚapi_modeÚrequest_overridesÚacp_commandÚcommandÚacp_argsr„  r.  rÎ  rÝ  rÏ  rÐ  Úproviders_allowedÚonlyÚproviders_ignoredÚignoreÚproviders_orderÚorderÚprovider_sortÚsortÚopenrouter_min_coding_scoreÚ
openrouterÚmin_coding_scorerñ   rÞ   Ú
quiet_modeTÚskip_context_filesÚload_soul_identityÚskip_memoryFÚskip_background_reviewr«  r@   r2   r1   rÖ   )r	  rH   rö   r.  rÎ  rÝ  rÏ  rÐ  rõ   rå   rC  )	rm  r<   rË  rØ  r”  r2   r1   r	  Úprs	            r(   Ú_construct_cron_agentr÷  J	  s  € Ø�m‰m€GØ	�‰Ð$Ó	%Ò	+¨€BÙò Ø�kŠkðà—‘˜IÔ&ðð —‘˜ZÔ(ðð —‘˜ZÔ(ð	ð
 #Ÿ;™;Ð';Ô<ðð —‘˜ZÔ(ðð "Ÿ+™+Ð&9Ô:ðð —K‘K 	Ô*ðð —‘˜VÔ$ðð ×+Ò+ðð ×/Ò/ðð ×/Ò/ðð ×+Ò+ðð ×-Ò-ðð Ÿ&™& œ.ðð  Ÿ&™& Ô*ð!ð" Ÿ™˜wœð#ð$ —f‘f˜V”nð%ð& &*§X¡X¨lÓ%;Ò%A¸r×$FÑ$FÐGYÔ$Zð'ô( 8¸¸TÔBð)ô* :¸$Ô?ð+ñ, ð-ô0  $ G›}Ñ,ð1ñ2  ð3ñ4 ð5ñ6  $ð7ñ8 ð9ñ: ð;ñ< ð=ð rf   c                   ó>   — e Zd ZdZdededefd„Zdedee   dd	fd
„Zy	)Ú
_FireAuditz[One usage_audit.jsonl line per fire (created once the agent exists; fire id + start clock).r<   r¥   rö   c                 ó¨   — |||c| _         | _        | _        t        j                  «       j
                  | _        t        j                  «       | _	        y r=  )
r<   r¥   rö   r·  r¸  r¹  Úfire_idr‡   rˆ   Út_start)rd   r<   r¥   rö   s       r(   rc   z_FireAudit.__init__r	  s9   € Ø,/°¸Ð)ˆŒ�$”+˜tœzÜ—z‘z“|×'Ñ'ˆŒÜ—~‘~Ó'ˆ�rf   r$   rŒ   r   Nc                 ó˜  — t        t        «       | j                  | j                  |j	                  d«      |j	                  d«      |j	                  d«      t        |j	                  d«      «      | j                  j	                  d«      | j                  xs d t        t        j                  «       | j                  z
  dz  «      |dœ«       y )NÚprompt_tokensÚcompletion_tokensÚtotal_tokensÚresponse_silentÚdeliverr"  )Útsr¥   rû  rþ  rÿ  r   r  Údeliver_targetrö   Úduration_msrŒ   )r.  r(  r¥   rû  rH   rC  r<   rö   rI   r‡   rˆ   rü  )rd   r$   rŒ   s      r(   r°  z_FireAudit.writew	  s–   € ÜÜ Ó"Ø—k‘kØ—|‘|Ø#ŸZ™Z¨Ó8Ø!'§¡Ð,?Ó!@Ø"ŸJ™J ~Ó6Ü# F§J¡JÐ/@Ó$AÓBØ"Ÿh™hŸl™l¨9Ó5Ø—Z‘ZÒ' 4Ü¤§¡Ó 0°4·<±<Ñ ?À4ÑGÓHØñõ 	rf   )	rg   rh   ri   rj   rK   rk   rc   r   r°  rÖ   rf   r(   rù  rù  o	  s=   „ Ùeð(˜Dð (¨#ð (°có (ð
˜Dð ¨°#©ð ¸4ô rf   rù  )Údefer_agent_teardownr®  rŸ  r¦  r  rŸ  r¦  c          
      óØ	  — | d   }t        | j                  d«      xs | j                  d«      xs |xs d«      }t        | ||||«      \  }}|�|S ddlm}	 d|› d	t        «       j                  d
«      › �}
t        j                  d||«       t        j                  d|dd «       d}d}d}d}i }t        | ||«      }	 |j                  «        |j                  r!t        j                  d||j                  «       t        | «       t        | ||«      }|j                  }|j                  }t!        | |||«      }|j"                  �w|j"                  ddlm}  ||j                  d«      |||||
«      }|j)                  «        |r|st+        |||||
«       |s$|�|�|j-                  |«       S S t/        ||«       S S |j                  }t1        | «      }t3        |	| |||j                  |
|¬«      }t5        | ||«      }t7        ||| |||j8                  ||¬«      }t;        ||||	«      }|j<                  r7|j?                  «       r'tA        |«      stC        |«      €|j<                  › d|› �}|r|nd}tE        | |||«      d|› d�z   }t        j                  d|«       |jG                  tI        |tA        |xs d«      ¬«      d«       d||dfddlm}  ||j                  d«      |||||
«      }|j)                  «        |r|st+        |||||
«       |s$|�|�|j-                  |«       S S t/        ||«       S S # tJ        $ �r9}tM        |«      jN                  › dt        |«      › �}t        jQ                  d||«       	 ddl)m*}  |||«      rd| d<   ddl+m,}  ||«      }|r|| d <   n%# tJ        $ r t        j[                  d!|«       Y nw xY w|�|jG                  i |«       dd"l.m/} tE        | |› d#�||«       ||«      z   }d$|d|fcY d}~ddlm}  ||j                  d«      |||||
«      }|j)                  «        |r|st+        |||||
«       |s$|�|�|j-                  |«       S S t/        ||«       S S d}~ww xY w# ddlm}  ||j                  d«      |||||
«      }|j)                  «        |r|st+        |||||
«       |s$|�|�|j-                  |«       w w t/        ||«       w w xY w)%už  Execute a single cron job. Returns (success, full_output_doc, final_response, error).
    ``defer_agent_teardown``: if a list, the live agent is appended instead of torn down; the caller
    MUST call ``_teardown_cron_agent(agent)`` AFTER delivery (a torn-down async client can't
    deliver). ``extra_prompt``: per-fire context, never persisted.

    ``defer_agent_teardown``: when a caller passes a list, ``run_job`` skips the agent's async-resource
    teardown (``agent.close()`` + ``cleanup_stale_async_clients()``) in its ``finally`` block and instead
    appends the live agent to that list. The caller is then responsible for calling
    ``_teardown_cron_agent(agent)`` AFTER it has delivered the result. This closes the ordering window in
    #58720 where delivery ran against a torn-down async client (defense-in-depth alongside the
    interpreter-shutdown guard). When ``None`` (the default) teardown happens inline as before, so every
    existing caller is unchanged.
    ``extra_prompt``: optional per-run context from ``cronjob(action='run', prompt=...)`` (#57331). Appended
    to the stored prompt for this fire only â€” never persisted to the job definition.
    rG   rF   r7  r–   Nr   )rm  Úcron_r]  z%Y%m%d_%H%M%SzRunning job '%s' (ID: %s)z
Prompt: %séd   r+   z&Job '%s': using task-scoped workdir %s)Ú defer_teardown_to_running_workerr&   )r”  r2   r1   )r9  r�  rf  z## Response

r  zJob '%s' completed successfully)r  Tz: zJob '%s' failed: %s)Úis_model_unreachable_failureÚ_model_unreachable)Úhold_seconds_from_failureÚ_quota_hold_secondsz3Job '%s': unreachable-failure classification failed)Úformat_run_errorz	 (FAILED)F)0rk   rH   r   Ú	run_agentrm  rº   r&  r‹   rý   r¥  r¿  r”  rÊ  rÒ  r9   rö   rÜ  rÍ  Úcron.scheduler_detached_workerr
  rÄ  r�  rà   Ú_teardown_cron_agentr'  r÷  rù  r^  r8  rv  rÑ  r-   r#  r  r‘  r°  rK   r#   rV  rg   r¥  Úcron.unreachable_retryr  Úcron.quota_holdr  rÇ   Úcron.scheduler_diagnosticsr  ) r<   r  r®  rŸ  r¦  r¥   r¤   Úearlyr7  rm  Ú_cron_session_idrÜ   rö   r}  Ú_auditÚ_worker_stateÚscoperî  rË  rØ  r
  Ú_worker_teardown_deferredr$   ra  Úlogged_responserª  r¶  Ú	error_msgr  r  Ú_hold_sr  s                                    r(   Úrun_jobr  ‡	  sU  € ð& �‰Y€FÜ�3—7‘7˜6“?ÒO c§g¡g¨hÓ&7ÒO¸6ÒOÀZÓP€Hä'¨¨V°X¸|È\ÓZ�M€Eˆ6ØÐØˆÝ!à˜v˜h a¬«×(>Ñ(>¸Ó(OÐ'PÐQÐÜ
‡K�KÐ+¨X°vÔ>Ü
‡K�K�˜f T c˜lÔ+à€EØ€EØ€KØ#'€FØ€MÜ˜#˜v |Ó4€EðT4Ø�‰ŒØ�=Š=Ü�K‰KÐ@À&È%Ï-É-ÔXÜ2°3Ô7ä" 3¨°Ó9ˆØ�v‰vˆØ—‘ˆÜ)¨#¨v°xÀÓDˆØ�=‰=Ð$Ø—=‘=õn 	TÙ$DØ×Ñ˜hÓ'¨°e¸VÀXÐO_ó%aÐ!à�
‰
ŒÙÑ8Ü" ;°°v¸xÐIYÔZñ )Ø#Ð/ØÐ$Ø(×/Ñ/°Õ6ð %ô % U¨FÕ3ð )ðG —‘ˆô ,¨CÓ0ˆÜ%Ø�S˜$ ¨u¯}©}ÐIYØ"ô$ˆô ˜C ¨Ó/ˆä)Ø�6˜3 ¨°%·-±-ÀØ&ô(ˆô 5°V¸VÀXÈwÓWˆØ×!Ò! n×&:Ñ&:Ô&<ÔE^Ð_mÔEnÜ.¨~Ó>ÐFð !&× 5Ñ 5Ð6°d¸>Ð:JÐKˆNá,:™.Ð@YˆÜ   h°¸Ó?ÀOÐTcÐSdÐdfÐBgÑgˆÜ�‰Ð5°xÔ@Ø�‰”T˜&Ô2KÈNÒL`Ð^`Ó2aÔbÐdhÔiØ�V˜^¨TÐ1õ> 	TÙ$DØ×Ñ˜hÓ'¨°e¸VÀXÐO_ó%aÐ!à�
‰
ŒÙÑ8Ü" ;°°v¸xÐIYÔZñ )Ø#Ð/ØÐ$Ø(×/Ñ/°Õ6ð %ô % U¨FÕ3ð )øôU ó ,Ü˜A“w×'Ñ'Ð(¨¬3¨q«6¨(Ð3ˆ	Ü×ÑÐ.°¸)ÔDð	XÝKÙ+¨A¨uÔ5Ø,0�Ð(Ñ)õ BÙ/°Ó2ˆGÙØ-4�Ð)Ñ*ùÜò 	XÜ�L‰LÐNÐPVÖWð	Xúð ÐØ�L‰L˜˜YÔ'Ý?ä˜C H :¨YÐ!7¸ÀÓHÙ˜qÓ!ñ"ð 	ð �f˜b )Ð+Ô+õ 	TÙ$DØ×Ñ˜hÓ'¨°e¸VÀXÐO_ó%aÐ!à�
‰
ŒÙÑ8Ü" ;°°v¸xÐIYÔZñ )Ø#Ð/ØÐ$Ø(×/Ñ/°Õ6ð %ô % U¨FÕ3ð )ûðU,ûõ: 	TÙ$DØ×Ñ˜hÓ'¨°e¸VÀXÐO_ó%aÐ!à�
‰
ŒÙÑ8Ü" ;°°v¸xÐIYÔZñ )Ø#Ð/ØÐ$Ø(×/Ñ/°Õ6ð %ô % U¨FÕ3ð )ús]   Â>BL6 Æ<DL6 Ì6
Q9Í :Q4Í;)N%Î$Q4Î%OÏQ4ÏOÏ<Q4ÐQ9ÐQ< Ñ4Q9Ñ9Q< Ñ<A-S)c                ó4   ‡ ‡— dˆ ˆfd„}t        |‰d|¬«       y)uÞ  Release an ephemeral cron agent's async resources within a hard bound (this runs outside the
    inactivity watchdog). Shared by ``run_job``'s finally and deferred post-delivery teardown.

    Split out of ``run_job``'s ``finally`` so a caller that defers teardown (to deliver first â€” #58720) can
    invoke the identical cleanup AFTER delivery. The timeout matters because this executes after
    ``run_conversation`` has returned, outside the agent inactivity watchdog.
    Nc                  ó  •— 	 ‰�‰j                  «        	 ddlm}  |«        y # t        t        f$ r!} t        j	                  d‰| «       Y d } ~ Œ:d } ~ ww xY w# t        $ r!} t        j	                  d‰| «       Y d } ~ y d } ~ ww xY w)Nz-Job '%s': failed to close agent resources: %sr   )Úcleanup_stale_async_clientsz4Job '%s': failed to reap stale auxiliary clients: %s)Úcloser#   rs  r‹   rÇ   Úagent.auxiliary_clientr"  )r¶  r"  rÜ   r¥   s     €€r(   Ú_cleanup_agentz,_teardown_cron_agent.<locals>._cleanup_agent
  s   ø€ ð	UØÐ Ø—‘”ð	\ÝJÙ'Õ)øô Ô,Ð-ò 	UÜ�L‰LÐHÈ&ÐRS×TÑTûð	Uûô ò 	\Ü�L‰LÐOÐQWÐYZ×[Ñ[ûð	\ús+   ƒ$ –A ¤A³AÁAÁ	BÁ A<Á<Bzagent resource teardown)r¥   rj  ri  rB  )ry  )rÜ   r¥   ri  r%  s   ``  r(   r  r  
  s   ù€ ö\ô #Ø˜vÐ-FØ'ö)rf   c                 óø  ‡ ‡‡‡	‡
— ‰ j                  d«      }t        |t        «      rt        |j                  d«      xs d«      ndŠ	‰	s |d«      S t        ‰ j                  d«      xs d«      Št	        j
                  «       Š
t	        j
                  «       Šdt        ddfˆ ˆfd„}	 t        ‰‰	¬	«      }|du rt        j                  d‰«        |d«       ydˆˆˆ	ˆ
fd„}t        |dˆfd„«      }|€	 |d«       y	  |‰«      ‰
j                  «        |j                  d¬«       S # t        $ r# t        j                  d
‰d¬«        |d«       Y yw xY w# ‰
j                  «        |j                  d¬«       w xY w)zDRun ``run`` while keeping this job's owned durable fire claim fresh.Ú
fire_claimr=  r+   NrG   rŒ   r   c                 óš   •— ‰j                  d«      }|sy 	 t        |d| ¬«       y # t        $ r t        j	                  d‰d¬«       Y y w xY w)Nr¦  F©ÚsuccessrŒ   z8Job '%s': failed to close unstarted execution ledger rowTrš  )rH   r  r#   r‹   rþ   )rŒ   r¦  r<   r¥   s     €€r(   Ú_finish_unstartedz9_run_with_fire_claim_heartbeat.<locals>._finish_unstarted*
  sQ   ø€ Ø—w‘w˜~Ó.ˆÙØð	Ü˜\°5ÀÖFøÜò 	Ü�N‰NØJØØð ö ð	ús   —& ¦!A
Á	A
rB  z.Job '%s': initial fire_claim validation failedTrš  zEFire claim ownership could not be validated before execution started.Fz@Job '%s': fire claim ownership was already lost before executionz3Fire claim ownership lost before execution started.c                  ó‚  •— t        j                  «       } ‰j                  t        «      s£	 t	        ‰‰¬«      sjt        ‰«      rŒ/‰j                  t        «      st	        ‰‰¬«      rt        j                  «       } Œf‰j                  «        t        j                  d‰«       y t        j                  «       } ‰j                  t        «      sŒ¢y y # t        $ rf t        j                  d‰d¬«       t        j                  «       | z
  t        k\  r-‰j                  «        t        j                  d‰t        «       Y y Y Œ†w xY w)NrB  z;Job '%s': fire claim ownership lost; interrupting stale runz%Job '%s': fire_claim heartbeat failedTrš  zRJob '%s': fire_claim could not be renewed within %.1fs; interrupting uncertain run)r‡   rˆ   r  rD  r	  r  Ú _FIRE_CLAIM_MISS_CONFIRM_SECONDSrë   r‹   rþ   r#   rÇ   Ú#_FIRE_CLAIM_HEARTBEAT_GRACE_SECONDS)Úlast_confirmedr¥   Úlost_ownershiprï  r  s    €€€€r(   Ú_heartbeat_loopz7_run_with_fire_claim_heartbeat.<locals>._heartbeat_loopB
  s  ø€ ÜŸ™Ó)ˆØ—)‘)Ô8Ô9ðÜ+¨FÀ5ÕIÜ4°VÔ<à ð —y‘yÔ!AÔBÔFZØ"°5õG:ä)-¯©Ó)9˜Ø Ø"×&Ñ&Ô(Ü—N‘NØUØô ð Ü!%§¡Ó!1�ð# —)‘)Ô8×9øô$ ò Ü—‘ÐDÀfÐW[�Ô\ä—N‘NÓ$ ~Ñ5Ü:ò;ð #×&Ñ&Ô(Ü—N‘Nð5àÜ;ô	=ñ
 ñ;ðús$   ¬C Á6C Á<&C Â#C ÃA*D>Ä=D>zcron-fire-claim-heartbeatc                  ó4   •— t         j                  d‰ d¬«      S )Nz.Job '%s': could not start fire_claim heartbeatTrš  ©r‹   rþ   )r¥   s   €r(   r]  z0_run_with_fire_claim_heartbeat.<locals>.<lambda>f
  s   ø€ ”—‘Ø<¸fÈtð ó U€ rf   zAFire claim heartbeat could not be started; execution was not run.ra  r  rB  )rH   rJ   rK   rk   rt  ru  r	  r#   r‹   rþ   Ú_start_heartbeat_threadrë   r  )r<   r!  Úclaimr+  Úowns_fire_claimr1  Úheartbeat_threadr¥   r0  rï  r  s   `      @@@@r(   Ú_run_with_fire_claim_heartbeatr8  
  si  ü€ à�G‰G�LÓ!€EÜ*4°U¼DÔ*AŒC�—	‘	˜$“Ò% 2Ô&Àr€EÙÙ�4‹yÐä�—‘˜“Ò$ "Ó%€FÜ�?‰?Ó€DÜ—_‘_Ó&€Nð
¤ð 
¨ö 
ðÜ.¨vÀeÔLˆð ˜%ÑÜ�‰ÐYÐ[aÔbÙÐOÔPØ÷ ð  ôD /ØÐ4ó	UóVÐð ÐÙÐ]Ô^Øð+Ù�>Ó"à�‰Œ
Ø×Ñ cÐÕ*øôq ò Ü�‰ÐGÈÐZ^ˆÔ_ÙÐaÔbÙðûðn 	�‰Œ
Ø×Ñ cÐÕ*ús   Â'D& Ã<E Ä&)EÅEÅ$E9F)rV  rW  Úverboser®  rŸ  r9  c                óÆ  ‡ ‡‡‡‡‡‡— ‰ j                  d«      s(t        ‰ d   d‰ j                  d«      ¬«      }|d   ‰ d<   t        ‰ d   «      }t        j                  j                  d«      |k(  }|s	 t        ‰ «      ry	 ‰€/‰ j                  d«      }|r‰ j                  d«      rt        |«      Š‰ j                  d
«      }t        |t        «      rt        |j                  d«      xs d«      nd}t        «       Št        «       j                  «       }t!        ‰ d   «      }t"        5  |xs d|ft$        j'                  |i «      ‰<   ddd«       	 t)        ‰ d   «      5  t+        ‰ ˆˆˆˆˆ ˆˆfd„«      cddd«       t"        5  t$        j                  |«      }|�*|j-                  ‰d«       |st$        j-                  |d«       ddd«       S # t        $ r¤}	d|	› �}
t        j                  d	‰ d   |
«       ‰ j                  d
«      }t        |t        «      rt        |j                  d«      xs d«      nd}	 t        ‰ d   d|
fi |rd|ini ¤Ž t        |d|
¬«       n# t        |d|
¬«       w xY wY d}	~	yd}	~	ww xY w# 1 sw Y   �Œ8xY w# 1 sw Y   S xY w# 1 sw Y   nxY w	 t"        5  t$        j                  |«      }|�*|j-                  ‰d«       |st$        j-                  |d«       ddd«       y# 1 sw Y   yxY w# t"        5  t$        j                  |«      }|�*|j-                  ‰d«       |st$        j-                  |d«       ddd«       w # 1 sw Y   w xY wxY w)u­  Run ONE due job end-to-end: execute â†’ save output â†’ deliver â†’ mark. Shared by the built-in
    ticker and external providers' ``fire_due``; does NOT decide due-ness or acquire the initial
    claim (callers use the store CAS) but keeps it alive. True if processed (a job failure is
    recorded via ``mark_job_run``), False only if processing raised. ``cancel_event``: optional
    transport-level cancel (dashboard drain).r¦  rG   ÚdirectÚ_scheduled_instant©ÚsourceÚscheduled_instantÚ_HERMES_CRON_EXTERNAL_WORKERTz*Restart-safe cron worker dispatch failed: zJob '%s': %sr'  r=  r+   Frê  r)  NÚmanual_run_promptÚmanual_run_atc           
      ó*   •— t        ‰‰‰‰‰| ‰‰¬«      S )N©rV  rW  r9  r®  Ú
claim_lostÚtransport_cancelÚexecution_token)Ú_run_one_job_body)r0  rV  rŸ  rG  r®  r<   rW  r9  s    €€€€€€€r(   r]  zrun_one_job.<locals>.<lambda>«
  s%   ø€ Ô'8ØØ%ØØ#Ø!-Ø-Ø%1Ø$3ô(5€ rf   )rH   r  rk   rÉ  ÚenvironÚ_launch_external_cron_workerr#   r‹   rŒ   rJ   rK   r  r  Úobjectr}   Úresolver'  rV  r0  Ú
setdefaultr  r8  rƒ  )r<   rV  rW  r9  r®  rŸ  Ú	executionr¦  Úexternal_ownerÚhandoff_errorrŒ   r5  rï  Ú_stampedrù  rð  Ú	_fire_keyrî  rG  s   ``````            @r(   Úrun_one_jobrS  s
  s,  þ€ ð �7‰7�>Ô"Ü$Ø�‰I˜h¸#¿'¹'ÐBVÓ:WôYˆ	à'¨™oˆˆNÑä�s˜>Ñ*Ó+€LÜ—Z‘Z—^‘^Ð$BÓCÀ|ÑS€NÙð	Ü+¨CÔ0Øð 1ð" Ðð —7‘7Ð.Ó/ˆÙ˜Ÿ™ Ô0Ü˜x›=ˆLØ�G‰G�LÓ!€EÜ/9¸%ÄÔ/F”�U—Y‘Y˜t“_Ò*¨Ô+ÈB€JÜ“h€OÜ#Ó%×-Ñ-Ó/€LÜ˜c $™iÓ(€IÜ	ñ .àÒ˜$ ðK.Ô×'Ñ'¨	°2Ó6°ÑG÷.ð>Ü(¨¨T©Ó3ñ 	6Ü1Ø÷5ñ 5ó
6÷	6ð 	6ô ñ 	>Ü-×1Ñ1°)Ó<ˆJØÐ%Ø—‘˜°Ô5Ù!Ü(×,Ñ,¨Y¸Ô=÷	>ð 	>øôW ò 	Ø@ÀÀÐPˆEÜ�L‰L˜¨¨T©°EÔ:Ø—G‘G˜LÓ)ˆEÜ2<¸UÄDÔ2I”C˜Ÿ	™	 $›Ò-¨2Ô.ÈrˆEðKÜØ˜‘IØØññ :?Ð-¨uÑ5ÀBò	ô ! °uÀEÖJøÔ  °uÀEÖJúÜûð	ú÷4.ñ .ú÷"	>ð 	>ú÷	6ð 	6úð 	6ô ñ 	>Ü-×1Ñ1°)Ó<ˆJØÐ%Ø—‘˜°Ô5Ù!Ü(×,Ñ,¨Y¸Ô=÷	>÷ 	>ñ 	>ûŒ]ñ 	>Ü-×1Ñ1°)Ó<ˆJØÐ%Ø—‘˜°Ô5Ù!Ü(×,Ñ,¨Y¸Ô=÷	>÷ 	>ñ 	>ýs”   Á4G Ä) I?ÅL Å JÅ5	L ÆAJÇ	I<ÇA I7È9IÉI7ÉI/É/I7É7I<É?J	ÊJ	ÊJ!ÊL Ê+AK6Ë6K?ÌM Ì	AMÍ	M ÍMÍM z3Interrupted by shutdown before terminal completion.rù  c                 óŠ   — |�3t        | |¬«      r&t        | dt        |¬«       t        |dt        ¬«       yt        |dd¬«       y)u  Bookkeeping after fire-claim ownership loss. A transport-level cancel (dashboard drain) is
    not a real loss â€” we still own the claim, so record the interruption via the owner-fenced
    terminal write instead of leaving fire_claim/last_status stale; otherwise discard.NrB  Fré  r)  z6Fire claim ownership lost; stale result was discarded.)r	  r  Ú_OWNERSHIP_LOST_INTERRUPTEDr  )r¥   rù  r¦  s      r(   Ú_record_fire_ownership_lostrV  À
  sB   € ð ÐÔ"6°vÈjÕ"YÜ�V˜UÔ$?ÐU_Õ`Ü˜¨uÔ<WÖXäØ %ØJö	Lrf   )Údelivery_queuedÚnotification_suppressedÚshould_deliverÚunresolved_originÚnormalized_deliverÚincident_ackedr*  rX  c                 ó>   — | ry|r|ry|ry|r|ry|r|dk7  ry|r|syy)Nrb  ÚqueuedÚ
suppressedÚnot_configuredÚlocalÚ	deliveredÚsuppressed_ackedrÖ   ©Údelivery_errorrY  rZ  r[  r\  r*  rW  rX  s           r(   Ú_classify_delivery_outcomerf  Í
  s<   € ñ
 ØÙ™/ØÙØÙÑ+ØÙÐ,°Ò7ØÙ™gð "Ørf   )Úagent_declaredra  rg  c                óh  — |rt        |«      nd}t        |v }|xs t        |v }d}	d}
|rM|sKt        j                  dd|«      j                  «       }ddlm}  || j                  d«      xs | d   |«      }n¶|r|}t        | «       n¦t        | |xs d|¬	«      \  }	}
|	rd}nŒ|rSdd
lm}  || j                  d«      xs | d   | d   |j                  «       j                  d«      «      t        | «      z   }n7ddlm} t!        | |«      t        | «      z    || | j                  d«      «      z   }||||	|
fS )aM  Text to deliver for a finished run. Returns ``(deliver_content, blocked_config,
    silent_alert, incident_acked, failure_incident_id)``; ``silent_alert``: an alert-once marker
    says the operator was already told, deliver nothing. ``agent_declared``: *error* is the
    agent's own ``[CRON_FAILURE]`` evidence, delivered verbatim.r+   FNz\[blocked_config[^\]]*\]\s*r   )Úblocked_config_noticerF   rG   r½   )r’   r›   )Úhold_noticer  )rk   ré  rê  r    r¡   r-   rž   ri  rH   rÏ   rÌ   r’   r¢   rP   r  rj  rª   )r<   r*  rŒ   ra  r¾   rg  r9  Úblocked_config_silentÚblocked_configr\  Úfailure_incident_idÚ_pf_textri  Údeliver_contentr’   rj  s                   r(   Ú_compose_run_deliveryrp  ã
  sL  € ñ Œ#ˆeŒ* 2€Cä8¸CÐ?ÐØ*ÒJÔ.CÀsÐ.J€NØ€NØÐÙ™gä—6‘6Ð8¸"¸cÓB×HÑHÓJˆÝEÙ/°·±¸³Ò0LÀ3ÀtÁ9ÈhÓW‰Ù	Ø(ˆÜ,¨SÕ1ô
 /KØ�’˜"¨+ô/
Ñ+ˆÐ+ñ Ø ‰OÙõ KÙ4Ø—‘˜“Ò, 3 t¡9¨c°$©i¸¿¹»×9KÑ9KÈCÓ9Póä% cÓ*ñ+‰Oõ 4ä4°S¸%Ó@ÔCXÐY\ÓC]Ñ]á˜c 3§7¡7Ð+@Ó#AÓBñCð ð
 ˜NÐ,AÀ>ÐSfÐfÐfrf   c                   ó   — e Zd ZdZy)Ú_FireClaimLostDuringSideEffectzPRaised inside a side-effect fence when the durable fire claim is no longer ours.NrÕ   rÖ   rf   r(   rr  rr    s   „ ÚZrf   rr  c                   óT   — e Zd ZdZ	 	 ddedee   dee   fd„Zdefd„Z	d	„ Z
defd
„Zy)Ú_FireOwnershipzZFire-claim ownership checks for one run (``owner`` is None when the job carries no claim).Nr<   rE  rF  c                 óô   — || _         || _        || _        |€|�t        ||«      nd | _        |j                  d«      }t        |t        «      r$t        |j                  d«      xs d«      | _	        y d | _	        y )Nr'  r=  r+   )
r<   rE  rF  rE  rŸ  rH   rJ   rK   rk   rï  )rd   r<   rE  rF  r5  s        r(   rc   z_FireOwnership.__init__  s{   € ð ˆŒð %ˆŒØ 0ˆÔð Ð%Ð)9Ð)Eô ! Ð-=Ô>àð 	Ôð —‘˜Ó%ˆÜ3=¸eÄTÔ3J”S˜Ÿ™ 4›Ò.¨BÓ/ˆ�
ÐPTˆ�
rf   r   c                 óV   — | j                   d uxr | j                   j                  «       S r=  )rF  r?  r>  s    r(   Útransport_cancelledz"_FireOwnership.transport_cancelled-  s'   € Ø×$Ñ$¨DÐ0ÒS°T×5JÑ5J×5QÑ5QÓ5SÐSrf   c                 óŒ   — | j                   €t        j                  d«      S t        | j                  d   | j                   ¬«      S )NTrG   rB  )rï  r!   Únullcontextr  r<   r>  s    r(   Úside_effect_fencez _FireOwnership.side_effect_fence0  s6   € Ø�:‰:ÐÜ×)Ñ)¨$Ó/Ð/Ü §¡¨¡¸t¿z¹zÔJÐJrf   c                 óÌ  — | j                  «       ry| j                  €yt        | j                  d   «      ry| j                  d uxr | j                  j                  «       }	 t        | j                  d   | j                  ¬«      ry	 | j                  �| j                  j                  «        y# t        $ r* t        j                  d| j                  d   d¬«       |cY S w xY w)NTFrG   rB  z0Job '%s': fire_claim ownership validation failedrš  )rw  rï  r  r<   rE  r?  r	  r#   r‹   rÇ   rë   )rd   Úlatcheds     r(   Úlostz_FireOwnership.lost5  sÐ   € Ø×#Ñ#Ô%ØØ�:‰:ÐØÜ(¨¯©°$©Ô8àð —/‘/¨Ð-ÒJ°$·/±/×2HÑ2HÓ2Jˆð	Ü# D§H¡H¨T¡NÀ4Ç:Á:ÕNØð Oð �?‰?Ð&Ø�O‰O×ÑÔ!Øøô ò 	Ü�L‰LØBÀDÇHÁHÈTÁNÐ]að ô cð ŠNð		ús   Á#$B0 Â00C#Ã"C#)NN)rg   rh   ri   rj   rK   r   r;  rc   rC  rw  rz  r}  rÖ   rf   r(   rt  rt    s\   „ Ùdð CGØ7;ñUØðUØ%-Ð.>Ñ%?ðUà"Ð#3Ñ4óUð$T Tó TòKð
�dô rf   rt  c                   óÀ   — e Zd ZU dZeed<   eed<   ee   ed<   dZ	eed<   dZ
ee   ed<   dZeed	<   dZeed
<   dZeed<   dZeed<   dZeed<   dZee   ed<   dZeed<   y)Ú_RunDeliveryzUMutable outcome of the save/compose/deliver phase, read back by the bookkeeping tail.r<   r*  rŒ   FÚdelivery_attemptedNre  rY  rZ  rl  rg  r\  rm  Úside_effect_ownership_lost)rg   rh   ri   rj   rK   r¾  rC  r   rk   r€  re  rY  rZ  rl  rg  r\  rm  r�  rÖ   rf   r(   r  r  N  s�   … á_Ø	ƒIØƒMØ�C‰=ÓØ$Ð˜Ó$Ø$(€N�H˜S‘MÓ(Ø €N�DÓ Ø#Ð�tÓ#Ø €N�DÓ ð !€N�DÓ Ø €N�DÓ Ø)-Ð˜ #™Ó-Ø',Ð Ô,rf   r  ÚdÚfencerª  c                ó®  — | j                   }|j                  «       5 }	|	st        ‚t        |d   «      rdnt	        |d   |«      }
ddd«       |r
�t
        j                  d|
«       | j                  rt        |d   |«      rd| _        d| _	        t        || j                  | j                  |
| j                  ¬«      \  }| _        }| _        | _        t        |j!                  «       «      xr | | _        | j"                  rK| j                  s?|j%                  d«      r.dd	lm}  ||«      r d| _        t
        j                  d
|d   «       | j"                  r<| j                  r0t+        |«      r%t
        j                  d|d   t,        «       d| _        | j"                  r0|j/                  «       r d| _        t
        j1                  d|d   «       | j"                  syt3        t5        || j                   ¬«      «      dk(  xr t7        || j                   ¬«       | _        	 |j                  «       5 }|st        ‚d| _        t=        ||||| j                   ¬«      | _        ddd«       y# 1 sw Y   �Œ*xY w# 1 sw Y   yxY w# t@        $ rE}tC        |t        «      r‚ tE        |«      | _        t
        j                  d|d   |«       Y d}~yd}~ww xY w)z­Save output, compose the notice and deliver it (both side effects run under the fire-claim
    fence; a lost claim raises ``_FireClaimLostDuringSideEffect`` for the caller).rG   NzOutput saved to: %sFz`Interrupted by gateway shutdown before the run finished (tool subprocess was killed mid-flight).)r*  rŒ   ra  r¾   rg  r  r   )Ú
will_retryuA   Job '%s': suppressing failure notice â€” automatic re-run pendingu1   Job '%s': agent returned %s â€” skipping deliveryz;Job '%s': skipping delivery after fire claim ownership loss©rX  ÚoriginTrU  úDelivery failed for job %s: %s)#r<   rz  rr  r  r  r‹   rý   r*  rü  rŒ   rp  rg  rl  r\  rm  rC  r-   rY  rH   r  r…  r#  r§  r}  rþ   Ú_normalize_deliver_valueÚ_delivery_lane_valueÚ_resolve_delivery_targetsrZ  r€  rY  re  r#   rJ   rk   )r‚  rƒ  ra  rª  rV  rW  r9  rG  r<   Úowns_outputr¾   ro  Ú_silent_alertr…  Úowns_deliveryÚdes                   r(   Ú_save_compose_deliverr�  a  s¡  € ð �%‰%€CØ	×	 Ñ	 Ó	"ð 5 kÙÜ0Ð0ô 2°#°d±)Ô<‰DÜ   T¡¨FÓ3ð 	÷	5ñ �;Ð*Ü�‰Ð)¨;Ô7ð 	‡y‚y”_ S¨¡Y°Ô@ØˆŒ	ð7ð 	
Œô 	Ø�Q—Y‘Y a§g¡g¸nØ°×0@Ñ0@ô	BñØ˜Ô)¨=¸!Ô:JÈAÔLaô
 ˜O×1Ñ1Ó3Ó4ÒJ¸]Ð9J€AÔØ×Ò §	¢	¨c¯g©gÐ6JÔ.Kõ
 	6Ù�cŒ?Ø$ˆAÔÜ�K‰KØSÐUXÐY]ÑU^ô`ð 	×Ò˜AŸIšIÔ*CÀOÔ*Tô 	�‰ÐGÈÈTÉÔTaÔbØ ˆÔà×Ò˜EŸJ™JœLØ ˆÔÜ�‰ÐTÐVYÐZ^ÑV_Ô`à×ÒØä Ô!5°cÈ1Ï9É9À}Ô!UÓVÐZbÑbò 	JÜ)¨#¸q¿y¹y¸=ÔIÐIð ÔðFØ×$Ñ$Ó&ð 	¨-Ù Ü4Ð4Ø#'ˆAÔ Ü.ØØØ!Øð !"§	¡	˜Mô ˆAÔ÷		ð 	÷q5ñ 5ú÷p	ð 	ûô ò FÜ�bÔ8Ô9ØÜ˜r›7ˆÔÜ�‰Ð5°s¸4±yÀ"×EÑEûð	FúsA   �(I-È$J È40I:É$J É-I7É:JÉ?J ÊJ Ê	KÊ;KËKre  c                 ó®   — |r	 ddl m}  || d   d|i«       t        |dd¬	«       y# t        $ r$}t        j	                  d| d   |«       Y d}~Œ7d}~ww xY w)
z±Shutdown already wrote last_status, so mark_job_run is skipped (a second call would skip a
    fire or auto-delete the job); an unsent notice is recorded via update_job instead.r   ©Ú
update_jobrG   Úlast_delivery_errorz:Failed recording delivery_error for interrupted job %s: %sNFz;Interrupted by gateway shutdown before terminal completion.r)  )r¯   r“  r#   r‹   rÇ   r  )r<   r¦  re  r“  Ú_rec_errs        r(   Ú_finish_interrupted_runr–  ´  sm   € ñ ð
	cõ -Ù�s˜4‘yÐ#8¸.Ð"IÔJô Ø˜eØKöMøô ò 	cÜ�L‰LØLÈcÐRVÉiÐYa÷cñ cûð	cús   „' §	A°AÁAc                 ó  — | j                   }| j                  s*|j                  d«      rddlm}  ||d   ddi«       d|d<   d| j
                  i}| j                  s|j                  dd«      rd	|d
<   |j                  dd«      }| j                  s|r||d<   | j                  r.| j
                  s"| j                  r|j                  d«      rd|d<   |�||d<   | j                  rd|d<   t        |d   «      xs% t        |d   | j                  | j                  fi |¤Ž}|�|st        |dd¬«       y	t        | j
                  |j                  d«      t        |j                  d«      «      | j                  | j                  t!        t#        || j                   ¬«      «      | j$                  | j                  ¬«      }|dv r!| j                  st'        | j(                  «       t        || j                  | j                  |¬«       y	)zSmark_job_run (owner-fenced) + execution ledger row for a run that reached delivery.Úlast_delivery_queuedr   r’  rG   Nre  r  FTÚmodel_unreachabler  Úquota_hold_secondsrW  rÖ  rê  rl  z5Fire claim ownership lost before terminal completion.r)  Ú$_notification_all_targets_suppressedr†  )re  rW  rX  rY  rZ  r[  r\  r*  ©rb  r`  ©r*  rŒ   Údelivery_outcome)r<   rY  rH   r¯   r“  re  r*  rƒ  rl  r  r  rŒ   r  rf  rC  rZ  r‰  rŠ  r\  rÒ   rm  )	r‚  rù  r¦  r<   r“  Úmark_kwargsr  r÷  rž  s	            r(   Ú_finish_completed_runr   È  sé  € à
�%‰%€CØ×Ò §¡Ð(>Ô ?Ý(Ù�3�t‘9Ð5°tÐ<Ô=Ø&*ˆÐ"Ñ#Ø)¨1×+;Ñ+;Ð<€KØ�9Š9˜Ÿ™Ð!5°uÔ=ð ,0ˆÐ'Ñ(Ø�g‰gÐ+¨TÓ2€GØ�9Š9™à,3ˆÐ(Ñ)Ø‡y‚y˜×)Ò)¨a×.>Ò.>À3Ç7Á7ÐKaÔCbØ 1ˆ�HÑØÐØ-7ˆÐ)Ñ*Ø×ÒØ 0ˆ�HÑä*¨3¨t©9Ó5ò 6¼ØˆD‰	�1—9‘9˜aŸg™gñ:6Ø)4ñ:6€FàÐ¡fÜØ %ØIõ	Kð Ü1Ø×'Ñ'ØŸ™Ð 6Ó7Ü $ S§W¡WÐ-SÓ%TÓ UØ×'Ñ'Ø×-Ñ-ä3Ô4HÈÐ^_×^gÑ^gÐZgÔ4hÓiØ×'Ñ'Ø—	‘	ô
Ðð Ð:Ñ:À1Ç9Â9ä˜q×4Ñ4Ô5ÜØ˜aŸi™i¨q¯w©wÐIYõ[àrf   Úerr_textc                óú  — t        t        | d¬«      «      }t        | |«      \  }}|ryd}	 t        | t	        | |«      t        | «      z   ||d¬«      }t        | xr |dk(  xr t        | d¬«       «      }	t        |d|	|d	d	| j                  d
«      t        | j                  d«      «      ¬«      }
|
dv rt        |«       ||
fS # t        $ r/}t        |«      }t        j                  d| d   |«       Y d}~Œ£d}~ww xY w)zWFailure notice for a run that raised out of run_job. Returns (delivery_error, outcome).Tr†  )Nrc  NrU  rˆ  rG   r‡  Fr˜  r›  rd  rœ  )r‰  rŠ  rÌ   rY  rª   rP   r#   rk   r‹   rŒ   rC  r‹  rf  rH   rÒ   )r<   r¡  rV  rW  r[  r\  rm  re  Údelivery_excrZ  rž  s              r(   Ú_deliver_crash_failurer¤  ù  s/  € ô 2Ô2FÀsÐX\Ô2]Ó^Ðä*FÀsÈHÓ*UÑ'€NÐ'ÙØ'Ø€NðPÜ(Øô 1°°hÓ?ÔBWÐX[ÓB\Ñ\ØØØô
ˆô ØÐò 	AØ (Ñ*ò	Aä)¨#¸4Ô@Ð@óÐô
 2Ø%°dÐN_Ø-¸eÈUØŸ™Ð 6Ó7Ü $ S§W¡WÐ-SÓ%TÓ Uô	WÐð
 Ð:Ñ:ÜÐ2Ô3ØÐ+Ð+Ð+øô ò PÜ˜\Ó*ˆÜ�‰Ð5°s¸4±yÀ,×OÑOûðPús   ¬&C Ã	C:Ã%C5Ã5C:rD  rE  rF  rG  c          
      óV  ‡ ‡&— t        ‰ ||«      }|j                  }	|j                  }
|j                  }‰ j	                  d«      }|s#t        ‰ d   d‰ j	                  d«      ¬«      d   }d}d }ddlm}m}m	} d }d }	 t        ‰ d   «      sTt        j                  d	‰ j	                  d
‰ d   «      «       t        |dd¬«       	 |� ||«       |�ddlm}  ||«       yyt         j"                  j	                  d«      |k(  }|sAt%        |«      €6t        j'                  d‰ d   «       	 |� ||«       |�ddlm}  ||«       yy | |t)        «       «      «      }ddlm}  |t)        «       «      }g Š&d$ˆ&ˆ fd„}‰&||dœ}|j,                  �|j,                  |d<   	 t/        ‰ fi |¤Ž\  }}}} |«       r4 |«        t3        ‰ d   |	|«       	 |� ||«       |�ddlm}  ||«       yyd}|r$‰ j	                  d«      st5        |«      }|�d|d}}}t7        ‰ |||¬«      }	 t9        ||||||||¬«       |j>                  |j@                  }} |«        |j<                  r-t3        ‰ d   |	|«       	 |� ||«       |�ddlm}  ||«       yy|jB                  r|jE                  «       sd|_!        d|_#         |«       rŒ|jI                  «       } |j>                  r(|j@                  s| st        j'                  d‰ d   «       nH| rt        j'                  d‰ d   «       t3        ‰ d   |	|«       	 |� ||«       |�ddlm}  ||«       yytK        ‰ d   |«      r*tM        ‰ ||«       	 |� ||«       |�ddlm}  ||«       yytO        ||	|«      |� ||«       |�ddlm}  ||«       S S # t0        $ r	  |«        ‚ w xY w# t:        $ r d|_        Y �Œžw xY w# |j>                  |j@                  }} |«        w xY w# t0        $ �r†}!tQ        |!«      xs tS        |!«      jT                  }"t        jG                  d‰ d   |"tS        |!«      |!|!jV                  f¬«       d}#tY        |!tZ        «      r+|s)tY        |!t:        «      s |«       st]        ‰ |"||¬«      \  }}#	 tK        ‰ d   |«      s=t_        ‰ d   «      s/i }$|	�|	|$d<   tY        |!tZ        «      r||$d <   ta        ‰ d   d|"fi |$¤Ž n0# tZ        $ r$}%t        jG                  d!‰ d   |%«       Y d }%~%nd }%~%ww xY w	 t        |d|"|#¬"«       n0# tZ        $ r$}%t        jG                  d#‰ d   |%«       Y d }%~%nd }%~%ww xY wtY        |!tZ        «      s‚ Y d }!~!|� ||«       |�ddlm}  ||«       yyd }!~!ww xY w# |� ||«       |�ddlm}  ||«       w w xY w)%Nr¦  rG   r;  r<  r=  Fr   )Úbuild_profile_secret_scopeÚreset_secret_scopeÚset_secret_scopeu6   Job '%s': one-shot dispatch limit reached â€” skippingrF   z3Dispatch claim rejected; execution was not started.r)  )Úreset_terminal_scopeTr@  z;Cron job %s lost execution ownership before start; skipping)Úinstall_profile_terminal_scopec                  ó2   •— ‰D ]  } t        | ‰d   «       Œ y )NrG   )r  )Ú_deferred_agentÚ_deferred_agentsr<   s    €€r(   Ú_teardown_deferredz-_run_one_job_body.<locals>._teardown_deferredl  s$   ø€ ð $4ò A�Ü$ _°c¸$±iÕ@ñArf   )r  r®  r¦  rŸ  r—   )r<   r*  rŒ   rg  )rV  rW  r9  rG  zWAgent completed but produced empty response (model error, timeout, or misconfiguration)zkJob '%s': fire claim ownership lost after completed delivery; recording the delivered run's terminal statuszbJob '%s': transport cancellation arrived before terminal completion; recording the interrupted runzError processing job %s: %srš  r_  )rV  rW  rê  re  z/Failed to record interrupted run for job %s: %sr�  z0Failed to finish execution record for job %s: %srB  )1rt  rï  rz  r}  rH   r  Úagent.secret_scoper¦  r§  r¨  r  r‹   rý   r  Útools.terminal_scoper©  rÉ  rI  r  rþ   r}   rª  rŸ  r  rm  rV  r  r  r�  rr  r�  r€  re  r*  r-   rŒ   rw  rÿ  r–  r   rk   rV  rg   Ú__traceback__rJ   r#   r¤  r  r  )'r<   rV  rW  r9  r®  rE  rF  rG  rƒ  rù  Ú_side_effect_fenceÚ_fire_claim_ownership_lostr¦  r€  re  r¦  r§  r¨  Ú_scope_tokenÚ_terminal_scope_tokenr©  rO  rª  r®  Ú_run_kwargsr*  rª  ra  rŒ   rg  Úmarker_errorr‚  rw  r¶  Ú	_err_textrž  rŸ  Ú
record_errr­  s'   `                                     @r(   rH  rH     sk  ù€ ô ˜3 
Ð,<Ó=€EØ—‘€JØ×0Ñ0ÐØ!&§¡Ðà—7‘7˜>Ó*€LÙÜ'Ø�‰I˜h¸#¿'¹'ÐBVÓ:WôYØY]ñ_ˆàÐØ€N÷Jñ Jð €LØ ÐðI8ô
 ˜c $™iÔ(Ü�K‰KØHØ—‘˜  D¡	Ó*ô,ô Ø eØKõMð ðp Ð#Ù˜|Ô,Ø Ð,ÝAá Ð!6Õ7ð -ôk Ÿ™Ÿ™Ð(FÓGÈ<ÑWˆÙÔ"8¸Ó"FÐ"NÜ�N‰NÐXÐZ]Ð^bÑZcÔdØð` Ð#Ù˜|Ô,Ø Ð,ÝAá Ð!6Õ7ð -ñ] (Ñ(BÔCSÓCUÓ(VÓWˆõ"	,ñ !?Ô?OÓ?QÓ RÐð "$Ðö	Að %5Ø(Ø(ñ*ˆð ×ÑÐ)Ø*/×*<Ñ*<ˆK˜Ñ'ð	Ü5<¸SÑ5PÀKÑ5PÑ2ˆG�V˜^¨Uñ &Ô'ÙÔ Ü'¨¨D©	°:¸|ÔLØðf Ð#Ù˜|Ô,Ø Ð,ÝAá Ð!6Õ7ð -ða ˆÙ˜3Ÿ7™7 :Ô.Ü5°nÓEˆLØÐ'Ø16¸Àd ˜�ô ˜S¨'¸È~Ô^ˆð		!Ü!Ø�5˜.¨&¸8È$ÐX_Ø /õ1ð 23×1EÑ1EÀq×GWÑGW ÐáÔ à×'Ò'ä'¨¨D©	°:¸|ÔLØðn Ð#Ù˜|Ô,Ø Ð,ÝAá Ð!6Õ7ð -ðm �9Š9˜^×1Ñ1Ô3ØˆAŒIØoˆAŒGá%Ô'ð
 #(×";Ñ";Ó"=ÐØ×#Ò#¨A×,<Ò,<ÑEXÜ—‘ðDà˜‘Iõñ
 'Ü—N‘Nð8à˜D™	ô#ô ,¨C°©I°zÀ<ÔPØð| Ð#Ù˜|Ô,Ø Ð,ÝAá Ð!6Õ7ð -ô} % S¨¡Y°Ô@Ü# C¨°~ÔFØðt Ð#Ù˜|Ô,Ø Ð,ÝAá Ð!6Õ7ð -ôu % Q¨
°LÓAðp Ð#Ù˜|Ô,Ø Ð,ÝAá Ð!6Õ7ð -øô} ò 	ñ Ô Øð		ûô6 .ò 	0Ø+/ˆA×(ð	0ûð 23×1EÑ1EÀq×GWÑGW ÐáÕ ûôL ó 2ô ˜“FÒ.œd 1›g×.Ñ.ˆ	Ü�‰Ø)Ø�‰IØÜ˜1“g˜q !§/¡/Ð2ð	 	ô 	4ð
 (Ðô �qœ)Ô$Ù&Ü˜qÔ"@ÔAÙ.Ô0ä/EØ�Y¨¸ô0>Ñ,ˆNÐ,ð	cä-¨c°$©i¸ÔIÜ5°c¸$±iÔ@à �ØÐ)Ø9C�KÐ 5Ñ6Ü˜a¤Ô+Ø4B�KÐ 0Ñ1Ü˜S ™Y¨¨yÑH¸KÒHùÜò 	cä�L‰LÐJÈCÐPTÉIÐWa×bÑbûð	cúð	dÜØ e°9ÐO_öaøäò 	dÜ�L‰LÐKÈSÐQUÉYÐXb×cÑcûð	dúä˜!œYÔ'ØÛð Ð#Ù˜|Ô,Ø Ð,ÝAá Ð!6Õ7ð -ûðq2ûðl Ð#Ù˜|Ô,Ø Ð,ÝAá Ð!6Õ7ð -úsð   Á=AO8 ÃAO8 ÅAO8 ÆN( Æ,O8 Ç'7O8 ÈN= È2;O8 Ê
B O8 ÍO8 Î O8 Î(N:Î:O8 Î=OÏO ÏOÏO Ï!O5Ï5O8 Ï8
VÐBVÒAS!Ó VÓ!	TÓ*T	ÔVÔ	TÔVÔT"Ô!VÔ"	UÔ+U
ÕVÕ
UÕVÕ#V ÖVÖV ÖV(Úprocessc                óö   ‡— dt         fˆfd„}	 	 | j                  d¬«      } |«       ryt        «         |«       ryt        d|› d	�«      ‚# t        j                  $ r  |«       rddlm}  || «       Y yY Œiw xY w)
a£  Preserve ``run_one_job``'s synchronous contract after handoff.

    The worker owns the durable execution and survives this gateway process.
    The caller nevertheless waits while it remains alive so manual/background
    callers do not release their in-process guard or report stale job state.
    A gateway replacement may kill this waiter; it does not kill the scoped
    worker or change its ledger ownership.
    r   c                  ób   •— t        ‰«      } t        | xr | j                  d«      t        v «      S )NrÖ  )r  rC  rH   r  )Úcurrentr¦  s    €r(   Ú_is_terminalz9_wait_for_external_cron_worker_body.<locals>._is_terminal  s,   ø€ Ü Ó-ˆÜ�GÒI §¡¨HÓ 5Ô9IÐ IÓJÐJrf   Tra  r  r   )Ú"reap_terminal_worker_in_backgroundz`cron external worker exited before durable recovery could terminalize its execution state (exit ra   )rC  r  Ú
subprocessÚTimeoutExpiredr  r¿  r  rô   )rº  r¦  r¾  Ú
returncoder¿  s    `   r(   Ú#_wait_for_external_cron_worker_bodyrÃ    s‘   ø€ ðKœ$õ Kð ð	Ø Ÿ™¨c˜Ó2ˆJñ Œ>Øô
 	'Ô(ÙŒ>ØÜð5Ø5?°LÀðCó
ð 	
øô' ×(Ñ(ò 	ÙŒ~Ý]á2°7Ô;ÙÙð	ús   �A Á(A8Á7A8rÖ   )r¥   Úhandoff_filesrÄ  .c                óº  — 	 t        | |¬«      |�-t        5  t        j                  t	        |«      «       d d d «       |D ]  }	 |j                  d¬«       Œ S # 1 sw Y   Œ$xY w# t        $ r Y Œ0w xY w# |�:t        5  t        j                  t	        |«      «       d d d «       n# 1 sw Y   nxY w|D ]$  }	 |j                  d¬«       Œ# t        $ r Y Œ"w xY w w xY w)N©r¦  T©Ú
missing_ok)rÃ  rV  r1  r‚  r'  ÚunlinkrÆ  )rº  r¦  r¥   rÄ  r×  s        r(   Ú_wait_for_external_cron_workerrÊ  3  sð   € ðÜ2Ø ,ô
ð ÐÜñ LÜ,×4Ñ4´]À6Ó5JÔK÷Lð #ò 	ˆEðØ—‘¨�Õ-ñ	÷	Lð Lûô ò Ùðûð ÐÜñ LÜ,×4Ñ4´]À6Ó5JÔK÷L÷ Lñ Lúð #ò 	ˆEðØ—‘¨�Õ-øÜò Ùðúñ	úsd   ‚A3 –AÁA$ÁA!	Á$	A0Á/A0Á3	CÁ<B$Â	CÂ$B-Â)CÂ6C	ÃCÃ		C	ÃCÃC	ÃCc                 ó–  — t        | d   «      }t        | d   «      }t        «       dz  dz  }||› d�z  }||› d�z  }||› d�z  }t        j                  dd	d
t        |«      dt        |«      g}ddlm}m}	m}
m} ddl	m
} ddlm}m} ddlm}m}m} 	 t%        t'        «       j)                  d«      xs i j)                  dd«      «      } |	«       } ||d|› d|› �|¬«      }|j,                  dk(  ryt/        |«      €t1        d«      ‚t3        |«       	 |j5                  d«       t9        j:                  |t8        j<                  t8        j>                  z  t8        j@                  z  d«      }	 t9        jB                  |dd¬«      5 }tE        jF                  | t        t        «       jI                  «       «      |dœ|«       |jK                  «        t9        jL                  |jO                  «       «       ddd«       t        «       jI                  «       } ||«        | ||«      «      }	  | ||dd!t        |«      i¬"«      «      } |
|«        ||«      }d#D ]  }|jU                  |d«       Œ dd$l+m,} t[        t\        «      jI                  «       j^                  j^                  } |||«      }	 t9        j:                  |t8        j<                  t8        j>                  z  t8        j`                  z  d«      }	 tc        jd                  |jf                  t        |«      |tb        jh                  tb        jh                  |dtk        «       ¬%«      }t9        jl                  |«       	 tn        5  tp        js                  tu        |«      «       ddd«       tw        jx                  «       tz        z   }tw        jx                  «       |k  �r#|j}                  «       �r*	 tE        j~                  |j�                  d¬«      «      } 	 |jS                  d¬ «       t‰        | tŠ        «      r| j)                  d«      |k7  r't‚        j�                  d(|«       t‡        |||||f¬'«      S t‚        j�                  d)|| j)                  d*«      |«       tn        5  t‘        j’                  t”        t–        «      5  t™        | j)                  d*«      xs |jš                  «      tœ        tu        |«      <   ddd«       ddd«       t‡        |||||f¬'«      S |jŸ                  «       }!|!�¨tn        5  tp        j¡                  tu        |«      «       ddd«       |jS                  d¬ «       dd+lQmR}"  |"|«      }#|jS                  d¬ «       |j,                  d,k(  r) ||«      r!t1        d-t9        j¦                  «       › d.�«      ‚t1        d/|!› d0|#› �«      ‚tw        j¨                  d1«       tw        jx                  «       |k  r�Œ#t‚        j«                  d2|tz        «       t‡        ||||||f¬'«      S # t*        $ r d}Y �ŒUw xY w# t6        $ r Y �Œ
w xY w# 1 sw Y   �ŒCxY w# tP        $ r |jS                  d¬ «       ‚ w xY w#  |
|«       w xY w# t9        jl                  |«       w xY w# tP        $ r& |jS                  d¬ «       |jS                  d¬ «       ‚ w xY w# 1 sw Y   �Œ5xY w# t*        $ r< t‚        j…                  d&|«       t‡        |||||f¬'«      cY |jS                  d¬ «       S w xY w# |jS                  d¬ «       w xY w# 1 sw Y   �ŒLxY w# 1 sw Y   �ŒQxY w# 1 sw Y   �ŒxY w)3uÅ  Launch *job* outside the managed gateway process when required.

    Returns ``False`` outside a managed systemd gateway (in-process path).  In
    managed topology the job always goes to an external worker with the #101940
    ownership handoff: in a transient user scope, or â€” when no user D-Bus
    session exists and ``cron.require_restart_safe_scope`` is false â€” as a
    direct subprocess (process separation kept, cgroup isolation lost).
    r¦  rG   r@   zexternal-workersz.jsonz.readyú.stderrz-mzcron.schedulerú--external-worker-fileú
--ack-filer   )r¦  Úis_multiplex_activer§  r¨  ©Úhydrate_profile_secret_sources)Úbuild_subprocess_envÚstrip_launch_profile_env)Úrestart_safe_gateway_child_argvÚscoped_spawn_lost_user_busÚsystemd_user_bus_envÚrequire_restart_safe_scopeFzcron-z-exec-)Úunit_suffixr×  Ú
in_processNz;cron execution claim changed before external worker handoffiÀ  é€  Úwr©  rª  )r<   rð  Úmultiplex_activeTrÇ  ÚHERMES_HOME)Úscrub_secretsÚinherit_profile_homeÚextra)ÚHERMES_INTERACTIVEÚHERMES_GATEWAY_SESSIONÚHERMES_EXEC_ASK)Úpin_hermes_tree_on_pythonpath)r¯  ÚenvÚstdinÚstdoutÚstderrÚstart_new_sessionÚcreationflagszhCron external worker %s published an unreadable acknowledgement; treating handoff as ownership-uncertain)r¦  r¥   rÄ  z]Cron external worker acknowledgement mismatch for %s; treating handoff as ownership-uncertainz?Cron job '%s' handed to restart-safe worker pid=%s execution=%sÚpid)Úexternal_worker_stderr_tailÚscopedzUrestart-safe systemd scope could not be created: the user D-Bus session at /run/user/z²/bus disappeared after the gateway started. On a system-level service install, run `sudo loginctl enable-linger <gateway-user>`; the next fire dispatches without scope isolation.zCcron external worker exited before ownership acknowledgement (exit ra   gš™™™™™©?zqCron external worker for job '%s' did not acknowledge within %.0fs; leaving the durable execution claim untouched)Vrk   r}   ÚsysÚ
executabler¯  r¦  rÏ  r§  r¨  r¢  rÑ  Útools.environments.localrÒ  rÓ  Útools.process_registryrÔ  rÕ  rÖ  rC  r   rH   r#   r�  r  rô   r  ÚchmodrÆ  rÉ  r¯  ÚO_WRONLYÚO_CREATÚO_EXCLÚfdopenr±  ÚdumprL  ÚflushÚfsyncÚfilenorm  rÉ  rƒ  Úcron.scheduler_worker_envrä  r   Ú__file__r®  ÚO_TRUNCrÀ  ÚPopenÚargvÚDEVNULLr   r#  rV  r1  r}  r'  r‡   rˆ   r  r_  ÚloadsÚ	read_textr‹   r¥  rÊ  rJ   rK   rŒ   rý   r!   r"   r¹   r/   rI   rë  r2  Úpollr‚  r  rì  ÚgetuidÚsleeprþ   )$r<   r¦  r¥   Úhandoff_dirÚpayload_pathÚack_pathÚstderr_pathrä  r¦  rÏ  r§  r¨  rÑ  rÒ  rÓ  rÔ  rÕ  rÖ  r×  rÜ  ÚdispatchÚfdÚpayload_filerð  Úsecret_tokenÚ
worker_envÚ_presence_varrä  Ú	repo_rootÚ	stderr_fdrº  ÚdeadlineÚacknowledgementrÂ  rì  Ústderr_tails$                                       r(   rJ  rJ  K  sû  € ô �s˜>Ñ*Ó+€LÜ��T‘‹^€FÜ"Ó$ vÑ-Ð0BÑB€KØ L >°Ð!7Ñ7€LØ ˜~¨VÐ4Ñ4€Hà < .°Ð 8Ñ8€Kä�‰ØØØ ÜˆLÓØÜˆH‹ð€G÷ó õ EßW÷ñ ð+Ü%)Ü!Ó#×'Ñ'¨Ó/Ò5°2×:Ñ:Ð;WÐY^Ó_ó&
Ð"ñ
 +Ó,ÐÙ.ØØ˜F˜8 6¨,¨Ð8Ø#=ô€Hð
 ‡}�}˜Ò$Øä% lÓ3Ð;ÜØIó
ð 	
ô �[Ô!ðØ×Ñ˜%Ô ô 
�‰�œrŸ{™{¬R¯Z©ZÑ7¼"¿)¹)ÑCÀUÓ	K€BðÜ�Y‰Y�r˜3¨Ô1ð 
	,°\Ü�I‰IàÜ$'Ô(8Ó(:×(BÑ(BÓ(DÓ$EØ(8ñð
 ôð ×ÑÔ Ü�H‰H�\×(Ñ(Ó*Ô+÷
	,ô $Ó%×-Ñ-Ó/€LÙ" <Ô0Ù#Ñ$>¸|Ó$LÓM€Lð)Ù-Ñ.BØ*Ø!%Ø ¤# lÓ"3Ð4ô/
ó ˆ
ñ 	˜<Ô(Ù% jÓ1€Jð
ò ,ˆð
 	�‰�} dÕ+ð,õ HÜ”X“×&Ñ&Ó(×/Ñ/×6Ñ6€IÙ.¨z¸9ÓE€JðÜ—G‘G˜K¬¯©´r·z±zÑ)AÄBÇJÁJÑ)NÐPUÓVˆ	ð	 Ü ×&Ñ&Ø—‘Ü˜	“NØÜ ×(Ñ(Ü!×)Ñ)Ø Ø"&Ü0Ó2ô	ˆGô �H‰H�YÕô 
ñ @Ü$×(Ñ(¬°vÓ)>Ô?÷@ô �~‰~ÓÔ"@Ñ@€HÜ
�.‰.Ó
˜XÓ
%Ø�?‰?Õð1Ü"&§*¡*¨X×-?Ñ-?ÈÐ-?Ó-QÓ"R‘ð —‘¨4�Ô0ä˜´Ô5Ø"×&Ñ& ~Ó6¸,ÒFä—‘ð>à ôô
 6ØØ!-Ø!Ø#/°Ð"=ô	ð ô �K‰KØQØØ×#Ñ# EÓ*Øô	ô ñ ?¤
× 3Ñ 3´I¼zÓ Jñ ?Ü>AØ#×'Ñ'¨Ó.Ò=°'·+±+ó??Ô$¤]°6Ó%:Ñ;÷?÷ ?ô 2ØØ)ØØ+¨[Ð9ô	ð ð —\‘\“^ˆ
ØÐ!Üñ LÜ,×4Ñ4´]À6Ó5JÔK÷Là×Ñ¨4ÐÔ0ÝNÙ5°kÓBˆKØ×Ñ¨$ÐÔ/Ø�}‰} Ò(Ñ-GÈ
Ô-Sô #ð!Ü!#§¡£ ð .HðHóð ô ðØ#˜ A k ]ð4óð ô 	�
‰
�4ÔôK �.‰.Ó
˜XÔ
%ôV ‡N�Nð	8àÜ&ô	ô *ØØ!ØØ# X¨{Ð;ô	ð øô] ò +Ø%*Ó"ð+ûô& ò Úðú÷
	,ñ 
	,ûô ò Ø×Ñ tÐÔ,Øðûñ 	˜<Õ(ûô> �H‰H�YÕûÜò Ø×Ñ tÐÔ,Ø×Ñ dÐÔ+Øðú÷
@ñ @ûô ò Ü× Ñ ð>à ôô
 6ØØ!-Ø!Ø#/°Ð"=ô	ñ ð —‘¨4�Õ0ðûð —‘¨4�Õ0ú÷,?ñ ?ú÷ ?ñ ?ú÷Lñ LúsÛ   Â6X ÄX" Å,X? ÆA-X2Ç1X? È(Y Ê.AZ Ë6AY, Í
Z Í&Z8Ï%[ Ò\1Ò!9\$Ó\1Ô\>ØXØXØ"	X/Ø.X/Ø2X<Ø7X? Ø?YÙ
Y)Ù,ZÚZ Ú/Z5Ú8[Û0\
Û5\ Ü	\
Ü
\ Ü\!Ü$\.	Ü)\1Ü1\;Ü>]r  r  c           	      óê	  — 	 t        j                  | j                  d¬«      «      }|d   }t        |d   «      j	                  «       }t        |d   «      }	 	 | j                  d¬«       d
dlm}m}m}m}	m}
 d
dlm} d
dlm} d
dlm}m}  ||«      } |«       }t1        |j3                  dd	«      «      } |	|«        ||«        |
 ||«      «      }	 t5        |«      5   ||«      €8t        j7                  d|«       	 ddd«        ||«        |	|«        ||«       y		 |j8                  j;                  dd¬«       |j=                  |j>                  › dtA        jB                  «       › �«      }tA        jD                  |t@        jF                  t@        jH                  z  t@        jJ                  z  d«      }	 tA        jL                  |dd¬«      5 }t        jN                  tA        jB                  «       |dœ|«       |jQ                  «        tA        jR                  |jU                  «       «       ddd«       tA        jV                  ||«       	 t@        jZ                  j3                  d«      }|t@        jZ                  d<   	 t]        |ddd	¬«      |€!t@        jZ                  j_                  dd«       n|t@        jZ                  d<   ta        jb                  t        «      5  |je                  d«      j                  d¬«       ddd«       cddd«        ||«        |	|«        ||«       S # t        $ r; t        j                  d| «       Y 	 | j                  d¬«       y	# t        $ r Y y	w xY ww xY w# t        $ r Y �Œw xY w# 	 | j                  d¬«       w # t        $ r Y w w xY wxY w# 1 sw Y   �Œ{xY w# tX        $ r |j                  d¬«       ‚ w xY w# t        $ r9 t        j                  d|«       Y ddd«        ||«        |	|«        ||«       y	w xY w# 1 sw Y   �ŒxY w# |€!t@        jZ                  j_                  dd«       n|t@        jZ                  d<   ta        jb                  t        «      5  |je                  d«      j                  d¬«       ddd«       w # 1 sw Y   w xY wxY w# 1 sw Y   nxY w	  ||«        |	|«        ||«       y#  ||«        |	|«        ||«       w xY w)a  Adopt and execute one gateway-dispatched cron payload.

    The execution row is created by the gateway before spawn, then transferred
    here before the ready acknowledgement is published.  No side effect runs
    unless that durable ownership transfer succeeds.
    r©  rª  r<   rð  r¦  z.Cron external worker could not load payload %sTrÇ  Fr   )r¦  rÏ  r§  Úset_multiplex_activer¨  )Úadopt_claimed_executionrÐ  )Úreset_hermes_home_overrideÚset_hermes_home_overriderÜ  NzUCron external worker refused execution %s: durable ownership could not be established)ÚparentsÚexist_okz.tmprÚ  rÛ  )rë  r¦  zCCron external worker could not publish ready acknowledgement for %sr@  ©rV  rW  r9  rÌ  )3r±  r  r  r   rL  rk   r#   r‹   r¥  rÉ  rÆ  r¯  r¦  rÏ  r§  r  r¨  r»  r  r¢  rÑ  rü   r  r  rC  rH   r  rŒ   r®  ÚmkdirÚ	with_namerF   rÉ  Úgetpidr¯  ró  rô  rõ  rö  r÷  rø  rù  rú  Úreplacerm  rI  rS  rƒ  r!   r"   Úwith_suffix)r  r  Úpayloadr<   rð  r¦  r¦  rÏ  r§  r  r¨  r  rÑ  r  r  Ú
home_tokenÚprevious_multiplexrÜ  r  Úack_tmp_pathr  Úack_fileÚold_external_executions                          r(   Ú_run_external_worker_payloadr(  -  s‹  € ðÜ—*‘*˜\×3Ñ3¸WÐ3ÓEÓFˆØ�e‰nˆÜ˜G NÑ3Ó4×<Ñ<Ó>ˆÜ˜3˜~Ñ.Ó/‰ð
	Ø×Ñ¨4ÐÔ0÷õ õ 8ÝD÷ñ
 *¨,Ó7€JÙ,Ó.ÐÜ˜GŸK™KÐ(:¸EÓBÓCÐÙÐ)Ô*Ù" <Ô0Ù#Ñ$>¸|Ó$LÓM€Lð3/Ü˜LÓ)ñ .	LÙ& |Ó4Ð<Ü—‘ð/à ôð
 ÷.	Lñ` 	˜<Ô(ÙÐ/Ô0Ù" :Õ.ðUØ—‘×%Ñ%¨d¸TÐ%ÔBð  (×1Ñ1°X·]±]°OÀ4ÌÏ	É	ËÀ}Ð2UÓV�Ü—W‘W˜\¬2¯;©;¼¿¹Ñ+CÄbÇiÁiÑ+OÐQVÓW�ðÜŸ™ 2 s°WÔ=ð 4ÀÜŸ	™	¬"¯)©)«+À|Ñ"TÐV^Ô_Ø Ÿ™Ô(ÜŸ™ §¡Ó!2Ô3÷4ô —J‘J˜|¨XÕ6ô &(§Z¡Z§^¡^Ð4RÓ%SÐ"Ø9EŒB�J‰JÐ5Ñ6ðLÜ" 3°¸DÈ%ÔPà)Ð1Ü—J‘J—N‘NÐ#AÀ4ÕHàAW”B—J‘JÐ=Ñ>ô  ×(Ñ(¬Ó1ñ LØ×(Ñ(¨Ó3×:Ñ:ÀdÐ:ÔK÷L÷[.	Lð .	Lñ` 	˜<Ô(ÙÐ/Ô0Ù" :Õ.øôa ò Ü×ÑÐIÈ<ÔXØð	Ø×Ñ¨4ÐÕ0øÜò 	Ùð	úðûô ò 	Úð	ûð	Ø×Ñ¨4ÐÕ0øÜò 	Ùð	ý÷V4ñ 4ûô
 %ò Ø ×'Ñ'°4Ð'Ô8Øðûô ò Ü× Ñ ØYØ ôð ÷A.	Lñ` 	˜<Ô(ÙÐ/Ô0Ù" :Õ.ð/ú÷$Lñ Lûð *Ð1Ü—J‘J—N‘NÐ#AÀ4ÕHàAW”B—J‘JÐ=Ñ>ô  ×(Ñ(¬Ó1ñ LØ×(Ñ(¨Ó3×:Ñ:ÀdÐ:ÔK÷L÷ Lñ Lý÷[.	Lð .	Lúð .	Lñ` 	˜<Ô(ÙÐ/Ô0Ù" :Õ.øñ 	˜<Ô(ÙÐ/Ô0Ù" :Õ.ús6  ‚AL+ ÁM2 ÃS Ã  R2Ä S Ä"BOÆ9N6ÇAN)È0N6É3R2ÊP(ÊAR2Ë"PÌR2Ì		S Ì+M/Í
N ÍM Í	M+Í*M+Í.M/Í/N Í2	M?Í>M?ÎN&ÎNÎN&Î	N#Î N&Î"N#Î#N&Î)N3	Î.N6Î6OÏOÏPÏ5R2Ï6S ÐPÐR2ÐP%Ð R2Ð(AR/Ñ8"R#	Ò	R/Ò#R,Ò(R/Ò/R2Ò2R;Ò7S ÓS2c                  ó”   — 	 ddl m}   | «       j                  «        y# t        $ r }t        j                  d|«       Y d}~yd}~ww xY w)a   Best-effort: tell the active scheduler provider the job set changed. Call AFTER a successful
    store mutation so an external provider can re-provision/cancel the one-shot; no-op for the
    built-in. Kept out of cron/jobs.py (import cycle). Never raises.r   ©Úresolve_cron_schedulerz!on_jobs_changed notify failed: %sN)Úcron.scheduler_providerr+  Úon_jobs_changedr#   r‹   rÇ   )r+  r¶  s     r(   Ú_notify_provider_jobs_changedr.  Œ  s;   € ð=ÝBÙÓ ×0Ñ0Õ2øÜò =Ü�‰Ð8¸!×<Ñ<ûð=ús   ‚ ž	A§AÁAc                   óH   ‡ — e Zd ZdZdededdfˆ fd„Zdefd„Zdefd„Z	ˆ xZ
S )	ÚCronSchedulerRegistrationErrorzFA job was persisted but its first external trigger was not registered.r<   Úcauser   Nc                 óz   •— || _         || _        t        ‰| �  d|d   › dt	        |«      j
                  › d�«       y )NrÃ  rG   z:' was saved, but its first scheduler registration failed (zS). Do not create a duplicate. Pause/resume or update the job to retry registration.)r<   r1  rb   rc   rV  rg   )rd   r<   r1  re   s      €r(   rc   z'CronSchedulerRegistrationError.__init__š  sJ   ø€ ØˆŒØˆŒ
Ü‰ÑØ˜˜T™˜ð $$Ü$(¨£K×$8Ñ$8Ð#9ð :OðOõ	
rf   c                 óf   — | j                   j                  d«      xs | j                   d   }d|› d�S )zEHuman-facing variant for chat/CLI surfaces (no exception class name).rF   rG   zSaved cron job 'u¤   ', but couldn't register it with the external scheduler yet. The job is kept â€” don't re-create it; pause/resume or edit it (e.g. via /cron) to retry registration.)r<   rH   )rd   rj  s     r(   Úuser_messagez+CronSchedulerRegistrationError.user_message£  s9   € à—‘—‘˜VÓ$Ò6¨¯©°©ˆà˜u˜gð &Nð Nð	
rf   c                 ó>   — t        | «      | j                  d   ddddœS )zDReturn the public partial-failure contract without provider details.rG   TF)rŒ   r¥   Ú	job_savedÚscheduler_registeredÚretry_create)rk   r<   r>  s    r(   Úto_dictz&CronSchedulerRegistrationError.to_dict¬  s)   € ô ˜“YØ—h‘h˜t‘nØØ$)Ø!ñ#ð 	#rf   )rg   rh   ri   rj   rK   r#   rc   rk   r4  r9  rl   rm   s   @r(   r0  r0  —  s8   ø„ ÙPð
˜Dð 
¨ð 
°tõ 
ð
˜có 
ð#˜÷ #rf   r0  c                  óÀ   — ddl m} ddlm}  |di | ¤Ž}|j	                  dd«      s|S 	  |«       j                  |«       |S # t        $ r}t        ||«      |‚d}~ww xY w)zHPersist one job and register its first trigger with the active provider.r   )Ú
create_jobr*  ÚenabledTNrÖ   )r¯   r;  r,  r+  rH   Úregister_jobr#   r0  )r…  r;  r+  r<   rË   s        r(   Ú&create_job_with_scheduler_registrationr>  ¶  si   € å$Ý>á
Ñ
�vÑ
€CØ�7‰7�9˜dÔ#Øˆ
ð@ÙÓ ×-Ñ-¨cÔ2ð €Jøô ò @Ü,¨S°#Ó6¸CÐ?ûð@ús   ªA Á	AÁAÁAg     Àr@Ú_last_dead_owner_reap_atg     Õ@Ú_last_worktree_maintenance_atc                  óº  — t        «       } t        j                  t        «      5  t	        t
        «      j                  «       j                  j                  }|dz  j                  «       r| j                  t        |«      «       ddd«       t        j                  t        «      5  ddlm}  |«       D ]»  }t        |j                  d«      xs d«      j                  «       }|rt	        |«      j                  «       sŒK	 t!        j"                  g d¢ddd	d
d|¬«      }|j$                  dk(  rC|j&                  j                  «       r)| j                  |j&                  j                  «       «       Œ½ 	 ddd«       t)        | «      D �cg c]!  }t	        |«      dz  j                  «       sŒ |‘Œ# c}S # 1 sw Y   �Œ.xY w# t        $ r Y �Œw xY w# 1 sw Y   Œ\xY wc c}w )zœRepos whose ``.worktrees/`` to keep pruned: the hermes checkout plus job workdir repo roots,
    filtered to those that actually have a ``.worktrees/`` dir.z.gitNr   ra  r”  r+   )Úgitz	rev-parsez--show-toplevelTr©  r   é   )Úcapture_outputr¦   r«  Úerrorsrg  r¯  z
.worktrees)rë   r!   r"   r#   r   rü  rL  r®  r_  r}  rk   r¯   rb  rH   r-   r•  rÀ  r!  rÂ  rç  rì   )ÚreposÚinstall_rootrb  r<   r”  ÚproberÖ  s          r(   Ú_worktree_maintenance_reposrI  Õ  s  € ô “€Eô 
×	Ñ	œYÓ	'ñ )ÜœH“~×-Ñ-Ó/×6Ñ6×=Ñ=ˆØ˜6Ñ!×)Ñ)Ô+Ø�I‰I”c˜,Ó'Ô(÷)ô
 
×	Ñ	œYÓ	'ñ Ý'á“;ò 	ˆCÜ˜#Ÿ'™' )Ó,Ò2°Ó3×9Ñ9Ó;ˆGÙ¤$ w£-×"6Ñ"6Ô"8ØðÜ"Ÿ™Ú;Ø#'¨d¸WØ$¨a°Wô>�ð ×#Ñ# qÒ(¨U¯\©\×-?Ñ-?Ô-AØ—I‘I˜eŸl™l×0Ñ0Ó2Ô3øñ	÷ô" ˜e“}ÖJ�!¬¨a«°<Ñ)?×(GÑ(GÕ(IŠAÒJÐJ÷-)ñ )ûô& ò Úðú÷ð üò" KsJ   ¤AF/Â$AGÃ>A0F<Å.GÆ!GÆ(GÆ/F9Æ<	G	ÇGÇG	Ç	GÇGc                  óú   — t        j                  «       } t        5  t        �| t        z
  t        k  r
	 ddd«       y| addd«       dd„}t        j                  |dd¬«      j                  «        y# 1 sw Y   Œ4xY w)zùThrottled worktree prune from the cron tick, on a daemon thread so the tick never waits on
    git. Same conservative pruner as ``hermes -w`` startup (dirty/unpushed/locked trees untouched).
    Errors never propagate: GC is hygiene, not scheduling.Nc                  óæ   — 	 t        «       } | sy ddlm} | D ]  }	  ||«       Œ y # t        $ r t        j                  d|d¬«       Y Œ2w xY w# t        $ r t        j                  dd¬«       Y y w xY w)Nr   )Ú_prune_stale_worktreesz'Cron worktree maintenance failed for %sTrš  z!Cron worktree maintenance skipped)rI  ÚclirL  r#   r‹   rÇ   )rF  rL  Úrepos      r(   Ú_runz-_maybe_run_worktree_maintenance.<locals>._run  s€   € ð	MÜ/Ó1ˆEÙØÝ2àò a�ðaÙ*¨4Õ0ñaøô !ò aÜ—L‘LÐ!JÈDÐ[_�LÖ`ðaûäò 	MÜ�L‰LÐ<ÀtˆLÖLð	Mús6   ‚A �A ›&£A ¦!A
ÁA Á	A
Á
A Á A0Á/A0zcron-worktree-pruneTrp  rB  )r‡   rˆ   Ú_worktree_maintenance_lockr@  Ú&_WORKTREE_MAINTENANCE_INTERVAL_SECONDSrt  rv  rw  )r   rO  s     r(   Ú_maybe_run_worktree_maintenancerR  ô  sz   € ô
 �.‰.Ó
€CÜ	#ñ ,ä)Ð5ØÔ3Ñ3Ü4ò5ð ÷,ð ,ð ),Ð%÷,óMô ×Ñ˜DÐ'<ÀTÔJ×PÑPÕR÷1,ð ,ús   ›A1¼A1Á1A:c                 ót  — d}	 t        | dd¬«      }t        r7t        j                  |t        j                  t        j                  z  «       |S t
        r3t        j                  |j                  «       t
        j                  d«       |S # t        $ r¤}|�dt        j                  t        «      5  |j                  «        ddd«       n# 1 sw Y   nxY wt        |«      rt        j                  d«       Y d}~yt!        |«      rt        j#                  d|«       ‚ t        j#                  d|«       ‚ d}~ww xY w)	u)  Open + non-blocking lock the tick file (fcntl / msvcrt). Returns the fd, or None on genuine
    contention. A real OSError (esp. EMFILE/ENFILE) must NOT pass as contention â€” the scheduler
    would look healthy while no job runs â€” so it is re-raised for the ticker to record a FAILED
    tick.NrÛ  r©  rª  rE   u0   Tick skipped â€” another instance holds the lockuj   Cron tick could not acquire tick lock: %s â€” scheduler will attempt fd reclamation and retry with backoffz)Cron tick could not acquire tick lock: %s)r¯  r<  ÚflockÚLOCK_EXÚLOCK_NBr@  Úlockingrú  ÚLK_NBLCKrÆ  r!   r"   r#  rB  r‹   rÇ   rJ  rŒ   )Ú	lock_fileÚlock_fdrË   s      r(   Ú_acquire_tick_lockr[    sø   € ð
 €Gðô �y #°Ô8ˆÝÜ�K‰K˜¤§¡´·±Ñ!>Ô?ð ˆõ Ü�N‰N˜7Ÿ>™>Ó+¬V¯_©_¸aÔ@ØˆøÜò ØÐÜ×$Ñ$¤WÓ-ñ  Ø—‘”÷ ÷  ñ  úä(¨Ô-Ü—‘ÐOÔPÜÜ˜SÔ!ä�L‰Lð@àôð 	ô �L‰LÐDÀcÔJØûðús<   „A
B
 Á:B
 Â
	D7ÂD2Â.CÂ?	D2ÃC	Ã'D2Ã99D2Ä2D7c                 ó¼  — t         rMt        j                  t        t        f«      5  t        j
                  | t         j                  «       d d d «       nat        r[t        j                  t        t        f«      5  t        j                  | j                  «       t        j                  d«       d d d «       | j                  «        y # 1 sw Y   ŒxY w# 1 sw Y   Œ&xY w)NrE   )r<  r!   r"   rÆ  ÚIOErrorrT  ÚLOCK_UNr@  rW  rú  ÚLK_UNLCKr#  )rZ  s    r(   Ú_release_tick_lockr`  9  s—   € ÝÜ× Ñ ¤'¬7Ð!3Ó4ñ 	0Ü�K‰K˜¤§¡Ô/÷	0ð 	0å	Ü× Ñ ¤'¬7Ð!3Ó4ñ 	AÜ�N‰N˜7Ÿ>™>Ó+¬V¯_©_¸aÔ@÷	Aà‡M�M…O÷	0ð 	0ú÷	Að 	Aús   ¦%CÁ94CÃCÃCc                  óR  — t        t        «       «      } t        j                  «       }t        j                  | «      }|�||z
  t        k  ry|t        | <   	 ddlm}  |«       }|rt        j                  d|«       yy# t        $ r }t        j                  d|«       Y d}~yd}~ww xY w)a  Dead-owner reclaim: a run that died mid-flight would leave its row 'claimed' forever. Rows
    whose owner process is proved gone are released (_owner_is_live), as are rows whose live owner
    holds a claim older than the derived stale bound (the process is not killed). Throttled.Nr   )r  ziReclaimed %d cron execution(s) whose owner process died before reaching a terminal state (marked unknown)z'Dead-owner execution reclaim failed: %s)r   r}   r‡   rˆ   r?  rH   Ú!_DEAD_OWNER_REAP_INTERVAL_SECONDSr»  r  r‹   rþ   r#   rÇ   )Ú	_reap_keyÚ	_reap_nowÚ
_last_reapr  Ú
_reclaimedÚ	_reap_excs         r(   Ú_maybe_reap_dead_ownersrh  C  s¤   € ô  Ô 0Ó 2Ó3€IÜ—‘Ó €IÜ)×-Ñ-¨iÓ8€JàÐØ˜
Ñ"Ô%FÒFàØ*3Ô˜YÑ'ð
KÝBá3Ó5ˆ
ÙÜ�N‰NðDàõð øô
 ò KÜ�‰Ð>À	×JÑJûðKús   Á%A= Á=	B&ÂB!Â!B&c                 ó  ‡— t        t        «       «      Št        ˆfd„t        D «       «      sy| }t	        j
                  t        «      5  t        D �ch c]  }|d   ‰k(  sŒ|d   ’Œ }}| D �ch c]$  }t        |t        «      sŒ|j                  d«      ’Œ& }}||k  sddl
m}  |«       }ddd«       	 t        |«       yc c}w c c}w # 1 sw Y   Œ xY w# t        $ r }t        j                  d|«       Y d}~yd}~ww xY w)z°Bound the in-flight set BEFORE the dedup guard so a leaked claim is force-released now
    rather than eating every later fire until restart. Skipped when nothing is in flight.c              3   ó.   •K  — | ]  }|d    ‰k(  –— Œ y­w)r   NrÖ   )rL  rT  rÚ  s     €r(   rM  z1_sweep_stale_inflight_for_tick.<locals>.<genexpr>g  s   øè ø€ ÒA¨ˆs�1‰v˜Õ$ÑAùs   ƒNr   rE   rG   ra  z Stale in-flight sweep failed: %s)r   r}   rN  r/  r!   r"   r#   rJ   rK   rH   r¯   rb  rá  r‹   rþ   )	rÔ  Ú_sweep_jobsrT  Ú_inflight_idsrp  Ú_due_idsÚ_load_all_jobsr¶  rÚ  s	           @r(   Ú_sweep_stale_inflight_for_tickro  c  sß   ø€ ô "Ô"2Ó"4Ó5€KÜÓAÔ0@ÔAÔAØØ€KÜ	×	Ñ	œYÓ	'ñ +Ü+;ÖU C¸sÀ1¹vÈÓ?T˜˜Q›ÐUˆÐUØ)1ÖI A´ZÀÄ4Õ5H�A—E‘E˜$•KÐIˆÐIØ Ò(Ý=á(Ó*ˆK÷+ð>Ü˜[Õ)ùò VùÚI÷+ð +ûô ò >Ü�‰Ð9¸1×=Ñ=ûð>úsN   Á		C	ÁB?Á B?Á'C	Á-CÂCÂC	Â3C Â?
C	Ã	CÃ	C>ÃC9Ã9C>c                  óÄ  — 	 t        d«      j                  «       } | rt        | «      xs dS 	 t        j                  t        «      5  t        «       xs i }t        |t        «      r|j                  di «      ni j                  d«      }|�t        |«      xs dcddd«       S 	 ddd«       y# t        t        f$ r t
        j                  d«       Y Œ¥w xY w# 1 sw Y   yxY w)zXMax workers: env > config.yaml > unbounded (HERMES_CRON_MAX_PARALLEL=1 restores serial).ÚHERMES_CRON_MAX_PARALLELNz?Invalid HERMES_CRON_MAX_PARALLEL value; defaulting to unboundedr@   Úmax_parallel_jobs)r   r-   rI   r/   r¹   r‹   rþ   r!   r"   r#   r   rJ   rK   rH   )Ú_env_parr‰  Ú_cfg_pars      r(   Ú_resolve_max_parallel_workersru  w  sÖ   € ðZÜ#Ð$>Ó?×EÑEÓGˆÙÜ�x“=Ò( DÐ(ð ô 
×	Ñ	œYÓ	'ñ )Ü“Ò# ˆÜ-7¸¼tÔ-D�E—I‘I˜f bÔ)È"×QÑQÐReÓfˆØÐÜ�x“=Ò( D÷	)ñ )ð  ÷)ð
 øô œ	Ð"ò ZÜ�‰ÐXÖYðZú÷)ð
 ús   ‚)B, ÁACÂ,$CÃCÃCc                  óx   — 	 ddl m}   | «        y# t        $ r }t        j	                  d|«       Y d}~yd}~ww xY w)z–Reap MCP stdio orphans (only PIDs flagged by tools.mcp_tool._run_stdio's finally block);
    run AFTER jobs finish so live sessions are never touched.r   )Ú_kill_orphaned_mcp_childrenz'Post-tick MCP orphan cleanup failed: %sN)Útools.mcp_tool_lifecyclerw  r#   r‹   rÇ   )rw  Ú_es     r(   Ú_sweep_mcp_orphansrz  ‡  s5   € ðDÝHÙ#Õ%øÜò DÜ�‰Ð>À×CÑCûðDús   ‚ �	9™4´9c                 óì   — t        | d   d¬«      }|st        | d   dd¬«       yt        |t        «      rt        |«      n
t        | «      }| d   |d<   | j	                  d«      |d<   t        ||||¬	«      S )
z4Run one due job via the shared ``run_one_job`` body.rG   T)Ú
return_jobr¦  Fz+Fire claim lost; execution was not started.r)  r<  r  )r  r  rJ   rK   rH   rS  )r<   rV  rW  r9  ÚclaimedÚclaimed_jobs         r(   Ú_process_due_jobr  ‘  s}   € ô !  T¡°tÔ<€GÙÜØ�Ñ¨Ð6cõ	eàä#-¨g´tÔ#<”$�w”-Ä$ÀsÃ)€KØ"% nÑ"5€K�ÑØ(+¯©Ð0DÓ(E€KÐ$Ñ%Ü�{¨X¸DÈ'ÔRÐRrf   r  c                 ó€  ‡ ‡‡‡— ‰ d   Š‰ j                  d‰«      Šdˆ ˆˆfd„}dˆfd„}t        «       r |«         |«        yt        ‰«      st        j	                  d‰«       yt        «       }	 t        ‰d‰ j                  d«      ¬	«      }t        ‰ |d   ¬
«      }t        j                  «       }|||fˆfd„	}
	 |j                  |
«      }t$        5  t'        ‰|«      }|t(        v r	|t*        |<   ddd«       |S # t        $ r5}	t        ‰|¬«        |«        t        j                  d‰|	«       Y d}	~	yd}	~	ww xY w# t        $ rl}t        ‰|¬«        |«        t        |d   dd|› �¬«       t        |t         «      rt        |«      r |«        nt        j#                  d‰|«       Y d}~yd}~ww xY w# 1 sw Y   |S xY w)z•Submit with the in-flight dedup guard; None if a prior tick's run is still in flight.
    Running-set membership is released in the worker's finally.rG   rF   Nc                  óä   •— ‰j                  d«      } t        | t        «      r| j                  d«      dk(  sy	 t        ‰«       y# t        $ r!}t
        j                  d‰|«       Y d}~yd}~ww xY w)u   Best-effort claim cleanup on dispatch-failure paths. Only one-shots carry a run_claim;
        clear_run_claim takes _jobs_lock + full load/save and can raise on degraded paths
        (shutdown, EMFILE) â€” a claim expiring at TTL beats crashing the tick.

        Only one-shot jobs carry a ``run_claim`` (stamped by get_due_jobs, #59229), so recurring jobs skip
        the call entirely â€” clear_run_claim acquires _jobs_lock (blocking cross-process flock) and does a
        full load_jobs read, and the dispatch-failure paths fire exactly when the process can least afford N
        pointless lock/read round-trips (interpreter shutdown, EMFILE).  clear_run_claim itself does
        load_jobs/save_jobs file I/O; on those degraded paths it can raise, and these early-exits exist
        precisely to skip cleanly â€” a stale claim expiring at the TTL is a better outcome than crashing the
        tick (#86522).
        r>   r?   r;  Nz\Could not clear run_claim for job '%s' after dispatch failure: %s (claim will expire at TTL))rH   rJ   rK   r  r#   r‹   rþ   )Ú	_scheduleÚ	claim_errr<   r¥   Ú	job_labels     €€€r(   Ú_clear_run_claim_best_effortz8_submit_with_guard.<locals>._clear_run_claim_best_effort¦  sh   ø€ ð —G‘G˜JÓ'ˆ	Ü˜9¤dÔ+°	·±¸fÓ0EÈÒ0OØð	&Ü˜FÕ#øÜò 	&Ü�N‰Nð9à˜9÷&ñ &ûð	&ús   ¹A Á	A/ÁA*Á*A/c                  ó2   •— t         j                  d‰ «       y )Nu8   Job '%s' not dispatched â€” interpreter is shutting downr3  )r„  s   €r(   Ú_not_dispatched_shutdownz4_submit_with_guard.<locals>._not_dispatched_shutdown¾  s   ø€ Ü�‰ÐQÐS\Õ]rf   u%   Job '%s' already running â€” skippingÚbuiltinr<  r=  rÆ  r*  z6Job '%s' not dispatched: execution creation failed: %sc                 ót   •— 	 |j                  ‰| «      t        | d   |¬«       S # t        | d   |¬«       w xY w)NrG   r*  )r!  r„  )rp  Úctxr€   Úprocess_jobs      €r(   Ú_run_and_releasez,_submit_with_guard.<locals>._run_and_releaseâ  s4   ø€ ð	4Ø—7‘7˜;¨Ó*ä  $¡¨dÖ3øÔ  $¡¨dÖ3ús   ƒ% ¥7FzExecutor dispatch failed: r)  zJob '%s' not dispatched: %srB  )rH   r2  r€  r‹   rý   r}   r  rK   r  r  r#   r„  r¥  r   r  rJ   rô   rŒ   rV  r'  r/  r5  )r<   r  r‹  r…  r‡  Ú_claim_homerN  Údispatched_jobÚ_ctxÚexecution_errrŒ  r½  Ú
submit_errÚ_submit_keyr¥   r„  s   ` `           @@r(   Ú_submit_with_guardr“     s­  û€ ð �‰Y€FØ—‘˜ Ó'€I÷&õ0^ô "Ô#Ù Ô"Ù$Ô&ØÜ# FÔ+Ü�‰Ð;¸YÔGØô #Ó$€KðÜ$Ø˜9¸¿¹Ð@TÓ8UôWˆ	ä˜c°	¸$±Ô@ˆÜ×'Ñ'Ó)ˆð *¨t¸+õ 4ðØ�k‰kÐ*Ó+ˆô 
ñ 0Ü# F¨KÓ8ˆØÔ*Ñ*Ø,/Ô˜[Ñ)÷0ð €Jøô? ò ä˜F¨Õ5Ù$Ô&Ü×ÑØDÀiÐQ^ô	`äûðûô  ò 	Ü˜F¨Õ5Ù$Ô&ÜØ�d‰O UÐ4NÈzÈlÐ2[õ	]ä�j¤,Ô/Ô4NÈzÔ4ZÙ$Õ&ä�L‰LÐ6¸	À:ÔNÜûð	ú÷0ð €Jús>   Á0AC: Â;D; ÃF3Ã:	D8Ä+D3Ä3D8Ä;	F0ÅA"F+Æ+F0Æ3F=r  c                 ó¬   ‡— | st        «        yt        | «      gŠdt        j                  j                  ddfˆfd„}| D ]  }|j                  |«       Œ y)zžAsync (gateway ticker) mode: sweep via a done-callback after the LAST job completes; sweep
    inline when nothing was dispatched (all skipped / no due jobs).NÚ_fr   c                 ó"  •— ‰dxx   dz  cc<   t        j                  t        «      5  | j                  «       }|�.t        j                  d|t        |«      ||j                  f¬«       d d d «       ‰d   dk  rt        «        y y # 1 sw Y   ŒxY w)Nr   rE   z(Cron job future failed in async mode: %srš  )	r!   r"   r#   r¥  r‹   rŒ   rV  r±  rz  )r•  Ú_excÚ
_remainings     €r(   Ú_on_donez2_sweep_mcp_orphans_when_all_done.<locals>._on_done  s�   ø€ Ø�1‹˜Ñ‹Ü× Ñ ¤Ó+ñ 	EØ—<‘<“>ˆDØÐÜ—‘Ø>ÀÜ" 4›j¨$°×0BÑ0BÐCð ô E÷	Eð �a‰=˜AÒÜÕ ð ÷	Eð 	Eús   ¨ABÂB)rz  r£   r  r  ÚFuturer"  )r  r™  r•  r˜  s      @r(   Ú _sweep_mcp_orphans_when_all_doner›  ü  sZ   ø€ ñ ÜÔØÜ�g“,�€Jð	!”Z×'Ñ'×.Ñ.ð 	!°4õ 	!ð ò 'ˆØ
×Ñ˜XÕ&ñ'rf   )Útick)rY  rŠ  r‰  rÈ  r‹  )r  r¤  r4  )r£  r™  rÖ  r¦  )rê  ré  râ  r×  rÿ  rã  Ú__main__rÍ  )Úadd_helpT)rV  ÚrequiredrÎ  )Úsetup_loggingr@   )r6  r�  rE   )r9  )úcron.scheduler_deliveryÚBOT_CHAT_PLATFORM)zcron.scheduler_preflightÚSharedRouteAdapters)r¡  Úcron_delivery_targets)r¡  Úparse_bot_chat_deliver_token)r¢  r£  r¤  r¥  c                 óÌ   — t         j                  | «      }|€t        dt        ›d| ›�«      ‚dd l}ddlm}  |t        | g|¢­Ž  t        |j                  |d   «      |d   «      S )Nzmodule z has no attribute r   )Ú	warn_oncerE   )	Ú_PLUGIN_COMPAT_LAZYrH   ÚAttributeErrorrg   Ú	importlibÚhermes_cli.plugin_compatr§  r0   Úimport_module)rF   rq  rª  r§  s       r(   r‹  r‹  U  se   € Ü ×$Ñ$ TÓ*€FØ€~Ü˜w¤x lÐ2DÀTÀHÐMÓNÐNÛÝ2ÙŒh˜Ð&˜vÓ&Ü�9×*Ñ*¨6°!©9Ó5°v¸a±yÓAÐArf   )r&   zconcurrent.futures.Futurer   Nr=  )r   zfrozenset[str]rB  (8  rj   ÚatexitÚconcurrent.futuresr  r!   r  r;  r±  ÚloggingrÉ  r    rÀ  rî  rt  r‡   r·  Údataclassesr   r   r   r   r<  ÚImportErrorr@  Úpathlibr   Útypingr   r	   r
   r   r   r   r   r´  Úinsertrk   rü  r®  rü   r   r   Úcron.env_settingsr   Úhermes_cli._subprocess_compatr   rd  r   r   r   r   Úhermes_timer   rº   Úagent.interrupt_compatr   Úagent.delegation_contextr   r   Úagent.memory_providerr   Úagent.turn_failure_copyr   Ú	getLoggerrg   r‹   r)   r7   r;   rK   rP   ÚtuplerU   rZ   rô   r\   Úcompilerr   rv   rŠ   rw   rK  r¾  r‚   r�   rª   r°   r‰   r±   rC  r¼   rÌ   rÏ   rÒ   r#   rÔ   ró   rå   rï   rõ   r  r¯   r  r  r  r  r  r  r  r	  r
  r  r  r  r  r  r»  r  r  r  r  r  r  r  r  r§  r  r  r#  r$  r  r  r%  rI   r'  r(  r+  r.  rë   r/  r0  r1  r2  ÚLockrV  r3  r4  r5  r~  r6  r7  r­  rˆ  r9  r;  rE  rY  r^  ru  rx  r€  r„  rj  r�  rš  rk  r¤  r·  rÀ  rÉ  rÓ  rá  rú  rü  rÿ  ru  r  r  r  r  r  Úregisterr   r(  r.  rm  r2  r3  r}   r8  rÆ  rB  rF  rJ  rP  r`  Ú_DEFAULT_SCRIPT_TIMEOUTÚ_SCRIPT_TIMEOUTrD  r.  r-  rh  ry  r{  r’  r–  r­  r¹  r»  rÒ  rÞ  ræ  rä  r  r  r  r'  r6  r^  rv  r�  r‘  rÒ  r   r¶  r¥  rÊ  rÌ  rÜ  r÷  rù  r  r  r8  rS  rU  rV  rf  rp  rr  rt  r  r�  r–  r   r¤  rH  rþ  rÃ  rÊ  rJ  r(  r.  r0  r>  rb  r?  rQ  r@  rP  rI  rR  r[  r`  rh  ro  ru  rz  r  r“  r›  Úcron.scheduler_tickrœ  Úcron.scheduler_deliveryrY  rŠ  r‰  rÈ  r‹  Úcron.scheduler_scriptr  r¤  r4  Úcron.scheduler_promptr£  r™  rÖ  r¦  Úcron.scheduler_preflightrê  ré  râ  r×  rÿ  rã  rÿ  ÚargparseÚArgumentParserÚparserÚadd_argumentÚ
parse_argsr„  Úhermes_loggingr   Ú
SystemExitÚexternal_worker_filer&  ÚasyncioÚshutilÚsignalr¨  r‹  rÖ   rf   r(   ú<module>rÓ     s‹  ðòó Û Û Û Û Û Û Û 	Û 	Û Û 
Û Û Û Ý !ß 2Ñ 2ðÛõ ß G× GÑ Gð ‡�‡��‘3‘t˜H“~×,Ñ,×3Ñ3Ó4Ô 5ç =Ý .Ý <÷'å 9Ý *Ý 9÷Kå +Ý <à	ˆ×	Ñ	˜8Ó	$€ô!òðB ó ð$˜tð ¨ó ð: 5¨¨c¨¡?°TÑ#9ó ð 3¨¡:ó ô
�lô 
ð& �"—*‘*ÐOÓP€ð
?¨C°$©Jð 
?¸5ÀÀcÀ¹?ÈTÑ;Qó 
?ð %Ð Ø%'€��c˜6�kÑ"Ó 'ð#¨U°3¸°8©_¸tÑ-Có #ðL4 ð 4¨ó 4ðB¨dð B¸3À¹:ð BÈ#ó BðJ &)Ð "ðh Uó hð9 Tð 9¨dó 9ð( <@òØ	ðØðØ+3°C©=ðà
ˆ4�˜#‘ÐÑóð0h¨dð h°tó hð	Q¨°©ð 	Q¸$ó 	Qô	 ô 	ð¨ð °$°s±)ó ð0¨d°3©ið ¸dð ÀtÈCÁyó ð$¨ð °4ð ¸DÀ¹Ió ð6V tð V°$ð V¸sð VÀtÈdÁ{ó V÷.a÷ a÷ a÷ a÷$÷ $ó $ð €ð 'Ð ð
6 Sð 
6¨X°c©]ó 
6ð0 Cð 0¨Dó 0ð$ EG€��c˜:×-Ò-×@Ò@Ð@ÑAÓ FØ79Ð ˜D  h¨s¡mÐ!3Ñ4Ó 9òh˜#ð h X¨e°D¸#°IÑ.>Ñ%?ð hÈ5ó hð )+Ð �d˜3 ˜9‘oÓ *ð $ð ¨4ó ð@ #ð @¨$ó @ñ ›Ð �#Ó ØNPÐ �d˜5 $ v¨u°X¸c±]ÀDÐ5HÑ/IÐ'IÑ"JÐJÑKÓ Pñ %(£EÐ ˜cÓ )ð *,Ð �d˜5 #˜:Ñ&Ó +Ø�	—’Ó €ð €�Ó àÐ �dÓ ØÐ �$Ó ñ “(€ð Ð �sÓ ØÐ �$Ó ØÐ ð #'Ð ñ  ›EÐ �cÓ !ô�xô ÷ñ ô 	[ð

  d¡ó 

ô$[òNF˜3ð F h¨u°T¸3°YÑ/?Ñ&@ð FÈDó Fð Sð ¨Tó ò6, ð ,¨8°E¸$À¸)Ñ4DÑ+Eð ,ÐQUó ,ð"+¨ó +ð4  Ð �dÓ ð ð ¨°%©ó ð,˜tð ¨°©ó ð&> $ó >ðF 3ð F¨cð FÀð FÐZ_ð FÐdhó Fð(°$ó ð4˜tð °Eð ¸dó ð T˜tð  T¨Sð  T°uð  TÈð  TÐ]`ð  TÐeió  TòF?! 8¨D¡>ð ?!¸Tó ?!ðF 26ò7Øð7Ø!)¨#¡ð7à	ó7òt+˜Cð +¨°Ñ(8ð +ÀDó +ò cð °(¸6Ñ2Bð Èdó ðØ! " e )Ñ,ðØ7<ðØFKðØS\×SbÒSbðà˜"˜d˜(Ñ#ðð 
óð0 %ó ð H¨S¡Mð °j×6HÒ6H×6[Ò6[ó ô&<ô'ð €‡‚Ð'Ô (ð=˜4ó =ðR˜ó Rð
:˜tð 
:¨ó 
:ò* H¨]Ñ$;ð *Àtó *ð$ !€ˆd�T‰kÓ  ð-˜$ó -ð-˜˜t T˜zÑ*ó -ð
 7ð 
¨tó 
ðC ð C¨ó Cð,˜=ð ,¨Tó ,ô&ð"¨Có ð( Ð à)€Ø#Ð Ø&BÀQÑ&FÐ #à#&Ð  ð uó ð& MQò'Øð'Ø$'ð'Ø:BÀ5¹/ð'à	ó'÷T&ñ &ðR˜cð ¨3ð ¸ð ÀCð ÈCó ð˜dð ¨Cð °H¸S±Mó ð4>Ø	ð4>Øð4>Ø&)ð4>à
ˆ4��c˜8 C™=Ð(Ñ)ó4>ðn#2Ø	ð#2Øð#2Ø&)ð#2Ø9AÀ#¹ð#2à
ˆ8�E‰?˜H S™M¨8°C©=Ð8Ñ9ó#2ðL ÷ð ó ðð2K˜tð 2K¨Sð 2K¸Cð 2KÀNó 2Kðj ð ¨cð °h¸t±nó ð0@˜Tð @¨3ð @¸#ð @ÀDð @ÈXÐV[É_ó @ð>< 3ð <°#ð <À3ð <È5ó <ð6AX˜dð AX¨Cð AX°^ð AXÈÈdÐTWÈiÑHXó AXðH 4ð °ó ð"` ð `¨ó `ð"+˜tó +ð\,¨sð ,¸Uð ,Àtó ,ð. $(òkØðkØ!ðkØ+.ðkØ:=ðkØHKðkà˜4‘.ðkð 
ókð\;¨ð ;°cð ;ÀSð ;ÐVYó ;ð|VV°cð VVÀSð VVÐ[^ð VVÐcgó VVðr˜ð  cð °3ð Àð Èó ð �4˜˜c 8¨C¡=Ð0Ñ1€
ðUØ	ðUØðUØ&)ðUØ9AÀ#¹ðUà
ˆ8�JÑ ¨#¡Ð.Ñ/óUðp*Ð ÷C(ñ C(ðL
°Dð 
¸Tó 
ð& ÷
*ð 
*ó ð
*ð% 4ð %°ð %Àð %ÈOó %ðP"¨ð "°Dð "Àó "÷Jñ ð2 :>Ð]aØ/3ÐSWòy4Ø	ðy4Ø(0°©ðy4ØMUÐVYÉ]ðy4àÐ+Ñ,ðy4àCKÈCÁ=ðy4ð ˆ4��c˜8 C™=Ð(Ñ)óy4ðz ?Cò)Øð)Ø,4°U©Oð)à	ó)ð8Q+¨ð Q+°dó Q+ðj   d¸EØ"&ÐSWòG>Ø	ðG>Ø59ðG>à˜3‘-ðG>à6>Ð?OÑ6PðG>ð 
óG>ðT TÐ ð
L¨ð 
L¸À#¹ð 
LÐVYð 
LÐ^bó 
Lð  ¸%òØ'+ðØ@Dðàðà-1ðà<@ðð 48ðð 	ó	ð0 !ò.gØ	ð.gØð.gØ8;ð.gàð.gð ˆ3��d˜D (¨3¡-Ð/Ñ0ó.gôb[ Yô [÷3ñ 3ðl ÷-ð -ó ð-ð$PFØðPFØ*ðPFØ<?ðPFØILðPFà!ðPFð 
óPFðfM ð M°Sð MÈ(ÐSVÉ-ð MÐ\`ó Mð(.˜\ð .°xÀ±}ð .ÐTWð .Ð\`ó .ðb#,Ø	ð#,Øð#,à
ˆ8�C‰=˜#ÐÑó#,ðP   d¸EØ"&ÐQUØ37Ø(,ò	_8Ø	ð_8Ø59ð_8à˜3‘-ð_8à4<Ð=MÑ4Nð_8ð Ð/Ñ0ð_8ð ˜fÑ%ð	_8ð
 
ó_8ðD.
Ø×Òð.
ð ð.
ð 
ó	.
ðj !Ø&(òØ×Òðð ðð �S‰Mð	ð
 ˜˜s˜Ñ#ðð 
óð0_ dð _¨tó _ðD\/¨tñ \/¸tð \/Èô \/õ~=ö# \ô #ð>¸ô ñ, %*Ð !Ø-/Ñ ˜$˜s E˜zÑ*Ô /ñ *4Ñ &Ø15Ñ ˜x¨™Ô 5Ø+˜YŸ^š^Ó-Ñ ðK T¨#¡Yõ Kö>SôB!öHöKð@>¨Tð >°dõ >ð( x°¡}õ ö DðS˜$ð S¸ð SÀ$õ SðY˜Dñ Y¨
×(:Ò(:×(MÒ(Mõ Yñx'¨dð '°tõ '÷. %Ñ $÷÷ ÷ ñ ÷÷ ñ ÷÷ õ ÷÷ ÷ õ ð ‰zÒÙ 3§8¢8Ñ+Ýà(‘×(Ò(°%Õ8‰Ù×ÒÑ4¸4É$ÑÔOÙ×Ò™L¨t¹dÑÔCÙ× Ò Ó"‰ð
	ß4Ñ4âÑ&6Ó&8¹vÖFò Ù-©d×.GÒ.GÉÏÊÔWˆAó
ð 	
Ù]^ó
ð 	
ò 	‘Öõ Ý Ý ñ JÙNÙQÙ$_ò	Ñ õBøðyA ò Ø€EðÜøØò ØŠðýð	ûðFA ò 	Ùð	úsH   Áh/ æ1i è/iè7h<è;iè<iéiéiéiéiéiéi