+
    ƒWÝjs  ã                  óú  € R t ^ RIHt ^ RIt^ RIt^ RIt^ RIt^ RIt^ RIt^ RI	t	^ RI
t
^ RIt^ RIt^ RIt^ RIHt ^ RIHt ^ RIHt ^ RIHtHtHtHt ^ RIHt ]'       d   ^ RIHt ]P:                  ! R	4      tR
 R ltR R lt R R lt!Rt"R R lt#R R lt$R R lt%Rt&R R lt'R R lt(R R lt)R R lt*R  R! lt+R" R# lt,R$ R% lt-R& R' lt.R( R) lt/R3R* R+ llt0R4R, R- llt1R. R/ lt2R5R0 R1 llt3^ R2I4H5t6 R# )6aÉ  Cron pre-run script execution: timeouts, Windows venv bootstrap, process-tree termination,
and the claim-heartbeat thread that keeps a long script's run claim alive.

Split out of ``cron.scheduler``. Import names from this module directly (``cron.scheduler`` only
imports the few it calls itself). Origin-resident helpers and sibling split modules are reached
late-bound (``_sched`` / module refs at the bottom) so monkeypatching the defining module works.
)ÚannotationsN)Úcron_env_setting)Ú_ensure_cron_dir)ÚPath)ÚAnyÚCallableÚOptionalÚTYPE_CHECKING)Úwindows_hide_flags)Ú_CancelEventLikezcron.schedulerc               ó   € V ^8„  d   QhRR/# )é   ÚreturnzOptional[int]© )Úformats   "ÚIC:\Users\johnn\AppData\Local\hermes\hermes-agent\cron\scheduler_script.pyÚ__annotate__r   $   s   € ÷ ,ñ ,˜-ñ ,ó    c                ó@   € \        \        V 4      4      pV^ 8”  d   V# R# )zD``int(float(raw))`` when > 0, else None; raises on unparsable input.N)ÚintÚfloat)ÚrawÚtimeouts   & r   Ú_positive_intr   $   s    € ä”%˜“*‹o€GØ ”kˆ7Ð+ tÐ+r   c               ó(   € V ^8„  d   QhRRRRRRRR/# )r   Úenv_varÚstrÚ
config_keyÚparsezCallable[[Any], Any]Úlabelr   )r   s   "r   r   r   *   s,   € ÷ ñ ØðØ!ðØ*>ðØGJñr   c                óî  € \        V 4      P                  4       pV'       d    V! V4      pVe   V#   \
        P                  ! 4       ;'       g    / p\        V\        4      '       d   VP                  R/ 4      M/ pVP                  V4      pVe   V! V4      pVe   V# R#   \         d    \        P	                  RY4        L’i ; i  \         d"   p	\        P                  RY94        Rp	?	R# Rp	?	ii ; i)uµ   Shared env â†’ ``cron.<config_key>`` resolution. ``parse`` returns the value or None to keep
looking; a parse error on the env var WARNs, on config DEBUGs. None when neither yields.Nz#Invalid %s=%r; using config/defaultÚcronz!Failed to load %s from config: %s)r   ÚstripÚ	ExceptionÚloggerÚwarningÚ_schedÚload_configÚ
isinstanceÚdictÚgetÚdebug)
r   r   r   r   Ú	env_valueÚvalueÚcfgÚcron_cfgÚ
configuredÚexcs
   &&&&      r   Ú_timeout_from_env_or_configr2   *   sì   € ô ! Ó)×/Ñ/Ó1€Ißð	VÙ˜)Ó$ˆEØÒ Ø�ð !ð	FÜ× Ò Ó"×(Ð( bˆÜ*4°S¼$×*?Ò*?�3—7‘7˜6 2Ô&ÀRˆØ—\‘\ *Ó-ˆ
ØÒ!Ù˜*Ó%ˆEØÒ Ø�ñ øô ô 	VÜ�N‰NÐ@À'ÖUð	Vûô ô FÜ�‰Ð8¸%×EÐEÙûðFús/   £B! ³C ÁAC Â!!CÃCÃC4ÃC/Ã/C4c               ó   € V ^8„  d   QhRR/# ©r   r   r   r   )r   s   "r   r   r   C   s   € ÷ Lñ L˜Sñ Lr   c                 óD  € \         P                  \         P                  8w  d"    \        \         P                  4      p V e   V #  \        RR\        R4      pVf   \         P                  # T#   \         d(    \
        P                  R\         P                  4        LYi ; i)zOResolve cron pre-run script timeout from module/env/config with a safe default.z<Invalid patched _SCRIPT_TIMEOUT=%r; using env/config/defaultÚHERMES_CRON_SCRIPT_TIMEOUTÚscript_timeout_secondszcron script timeout)r&   Ú_SCRIPT_TIMEOUTÚ_DEFAULT_SCRIPT_TIMEOUTr   r#   r$   r%   r2   )r   Úresolveds     r   Ú_get_script_timeoutr;   C   s“   € ä×Ñ¤×!?Ñ!?Ô?ð	(Ü#¤F×$:Ñ$:Ó;ˆGØÒ"Ø�ð #ô +Ø$Ð&>ÄØó€Hð .6Ò-=Œ6×)Ñ)ÐKÀ8ÐKøô ô 	(Ü�N‰NØNÜ×&Ñ&ö(ð	(ús   ¥A- Á-/BÂBi,  c               ó   € V ^8„  d   QhRR/# r4   r   )r   s   "r   r   r   X   s   € ÷ Iñ I ñ Ir   c                 ó>   € \        RR\        R4      p V f   \        # T # )z¤Per-attachment media-send timeout: HERMES_CRON_MEDIA_SEND_TIMEOUT env, then
``cron.media_send_timeout_seconds``, then 300s (long TTS audio can exceed a 30s window).ÚHERMES_CRON_MEDIA_SEND_TIMEOUTÚmedia_send_timeout_secondszcron media-send timeout)r2   r   Ú_DEFAULT_MEDIA_SEND_TIMEOUT©r:   s    r   Ú_get_media_send_timeoutrB   X   s,   € ô +Ø(Ð*FÌØ!ó#€Hð +3Ò*:Ô&ÐHÀÐHr   c               ó   € V ^8„  d   QhRR/# )r   r   r   r   )r   s   "r   r   r   a   s   € ÷ 2ñ 2 ñ 2r   c                 ó6   € \        RR\        R4      p V f   R# T # )zøBound on run_job's SessionDB init: HERMES_CRON_SESSION_DB_TIMEOUT env, then
``cron.session_db_timeout_seconds`` (in DEFAULT_CONFIG), then 10s. Unlike sibling timeouts,
0 is meaningful (unlimited, debugging opt-in), so values pass through untouched.ÚHERMES_CRON_SESSION_DB_TIMEOUTÚsession_db_timeout_secondszcron.session_db_timeout_secondsg      $@)r2   r   rA   s    r   Ú_get_session_db_timeoutrG   a   s+   € ô +Ø(Ð*FÌØ)ó+€Hð Ò#ˆ4Ð1¨Ð1r   c               ó    € V ^8„  d   QhRRRR/# )r   Úvenv_dirr   r   údict[str, str]r   )r   s   "r   r   r   k   s   € ÷ ñ  tð °ñ r   c                ó  €  V R ,          P                  RR7      P                  4       pR T 4        UUu/ uF2  w  r#TP                  4       P	                  4       TP                  4       bK4  	  upp#   \         d    / u # i ; iu uppi )z
pyvenv.cfgz	utf-8-sig)Úencodingc              3  óX   "  € T F   pR V9   g   K  VP                  R ^4      x € K"  	  R# 5i)Ú=N)Úsplit)Ú.0r   s   & r   Ú	<genexpr>Ú+_read_windows_pyvenv_cfg.<locals>.<genexpr>r   s'   é € ÐL¹°ÀÈÁÔ,˜3Ÿ9™9 S¨!×,Ð,»ùs   ‚	*�*)Ú	read_textÚ
splitlinesÚOSErrorr"   Úlower)rI   ÚlinesÚkeyr-   s   &   r   Ú_read_windows_pyvenv_cfgrY   k   s†   € ðØ˜LÕ(×3Ñ3¸[Ð3ÓI×TÑTÓVˆñ
 M¹ÔLôáL‰JˆCð 	�	‰	‹×ÑÓ˜UŸ[™[›]Ò*ÙLòð øô ô ØŠ	ðüós   ‚'A1 µ8BÁ1BÂ Ba'  import importlib.machinery, os, sys, types
repo, script = sys.argv[1], sys.argv[2]
sys.argv = [script] + sys.argv[3:]
if sys.flags.safe_path:
    sys.path.insert(0, repo)
else:
    sys.path[0:1] = [os.path.dirname(script), repo]
main = types.ModuleType("__main__")
main.__file__ = script
main.__loader__ = importlib.machinery.SourceFileLoader("__main__", script)
main.__cached__ = None
main.__builtins__ = __builtins__
sys.modules["__main__"] = main
with open(script, "rb") as f:
    code = compile(f.read(), script, "exec")
exec(code, main.__dict__)
c               ó    € V ^8„  d   QhRRRR/# )r   Úscriptr   r   z tuple[list[str], dict[str, str]]r   )r   s   "r   r   r   �   s   € ÷ 3ñ 3 Dð 3Ð-Mñ 3r   c                óp  € ^ RI Hp ^ RIHp \	        \
        4      P                  4       P                  ^,          pV! V4      f   \        P                  \        V 4      ./ 3# V! V4      pVP                  4       '       g   \        RV 24      h\        V4      R\        \        V4      \        V 4      .RR/3# )aÊ  POSIX managed-store installs run cron ``.py`` scripts on the selected dependency venv's
interpreter: the store Python has the repo and managed site-packages only on its in-process
``sys.path``, so its children import neither (#123044). No ``PYTHONPATH``: everything the
script spawns would inherit it and a foreign interpreter would load the store's compiled
extensions (#123440). The venv resolves Hermes from its generation's workspace snapshot,
rebuilt only on a dependency change, so the bootstrap puts the live checkout first.
Lazy installs are off for the script's process tree: a script importing ``hermes_bootstrap``
could otherwise complete a source update and ``execv`` itself onto the bare store Python.©Úresolve_store_python)Úproject_pythonz/dependency environment interpreter is missing: ú-cÚHERMES_DISABLE_LAZY_INSTALLSÚ1)Úhermes_cli._launchersr^   Úpm.environmentsr_   r   Ú__file__ÚresolveÚparentsÚsysÚ
executabler   Úis_fileÚRuntimeErrorÚ_POSIX_SCRIPT_BOOTSTRAP)r[   r^   r_   ÚrepoÚpythons   &    r   Ú_posix_cron_script_argvro   �   s£   € õ ;Ý.ä”‹>×!Ñ!Ó#×+Ñ+¨AÕ.€DÙ˜DÓ!Ò)Ü—‘¤ F£Ð,¨bÐ0Ð0ñ ˜DÓ!€FØ�>‰>×ÒäÐLÈVÈHÐUÓVÐVÜ�‹[˜$Ô 7¼¸T»ÄCÈÃKÐPØ+¨SÐ1ð3ð 3r   c               ó    € V ^8„  d   QhRRRR/# )r   Ú
python_exer   r   ztuple[str, dict[str, str]]r   )r   s   "r   r   r   ¦   s   € ÷ .)ñ .)°ð .)Ð8Rñ .)r   c           
     óì  € \         P                  R8w  d   V / 3# \        P                  ! V 4      pVP                  P                  p/ pVP
                  P                  4       R8X  d*   VP                  R4      pVP                  4       '       d   Tp^ RI	H
p ^ RIHpHp \        \        4      P                  4       P                   ^,          pV! V4      p	V	eS   V! V4      p
V
eG   \#        V	4      R\$        P&                  P)                  \#        V4      \#        V! V
4      4      .4      /3# \+        V4      pVP-                  RR4      pVR	,          R
,          pRV9   Ed   V'       Ed   \        P                  ! V4      R,          pVP                  4       '       dÏ   VP                  4       '       d¹   Tp\#        V4      VR&   \#        \        P                  ! \        4      P                  4       P                   ^,          4      \#        V4      .p\$        P.                  P-                  RR4      pV'       d   VP1                  V4       \$        P&                  P)                  V4      VR&   \#        V4      V3# )a  Hidden, output-capable Python invocation for Windows cron scripts. ``pythonw.exe`` loses
captured output; uv venv launchers can re-exec the base console python and flash a window
even with CREATE_NO_WINDOW, so run the base python directly with venv paths overlaid in env.Úwin32zpythonw.exez
python.exer]   )Úcommitted_venvÚsite_packagesÚ
PYTHONPATHÚhomeÚ ÚLibúsite-packagesÚuvÚVIRTUAL_ENV)rh   Úplatformr&   r   ÚparentÚnamerV   Ú	with_nameÚexistsrc   r^   rd   rt   ru   re   rf   rg   r   ÚosÚpathsepÚjoinrY   r*   ÚenvironÚappend)rq   ÚinterpreterrI   Úenv_overlayÚsiblingr^   rt   Údependency_siterm   Úmanaged_pythonÚenvironmentr.   rw   ru   Úbase_pythonÚpythonpath_entriesÚexisting_pythonpaths   &                r   Ú_windows_cron_python_invocationr�   ¦   sû  € ô ‡|�|�wÔØ˜2ˆ~Ðä—+’+˜jÓ)€KØ×!Ñ!×(Ñ(€HØ"$€Kà×Ñ×ÑÓ =Ô0Ø×'Ñ'¨Ó5ˆØ�>‰>×ÒØ!ˆKå:ßPä”‹>×!Ñ!Ó#×+Ñ+¨AÕ.€DÙ)¨$Ó/€NØÒ!ñ
 % TÓ*ˆØÒ"Ü�~Ó&¨´r·z±z·±Ü�T“œC¡°Ó <Ó=Ð>ó8@ð )Að Að Aô # 8Ó
,€CØ�7‰7�6˜2Ó€DØ˜uÕ$ Õ6€MØˆs…{—t�tÜ—k’k $Ó'¨,Õ6ˆØ×Ñ×Ò M×$8Ñ$8×$:Ò$:Ø%ˆKÜ),¨X«ˆK˜Ñ&ä”F—K’K¤Ó)×1Ñ1Ó3×;Ñ;¸AÕ>Ó?ÄÀ]ÓASð"UÐä"$§*¡*§.¡.°¸rÓ"BÐß"Ø"×)Ñ)Ð*=Ô>Ü(*¯
©
¯©Ð8JÓ(KˆK˜Ñ%äˆ{Ó˜[Ð(Ð(r   c               ó    € V ^8„  d   QhRRRR/# ©r   Úproczsubprocess.Popenr   ÚNoner   )r   s   "r   r   r   ×   s   € ÷ ñ Ð)9ð ¸dñ r   c                óì  € V P                  4       e   R# \        P                  R8w  d   \        V 4       M< \        P
                  ! RR\        V P                  4      RR.R^
\        4       RR	7        V P                  R
R7       R#   \        \        P                  3 d    T P                  4         LBi ; i  \        P                   d'    T P                  4        T P                  R
R7        R# i ; i)zBBest-effort hard stop of a cron script and every child it spawned.Nrs   Útaskkillz/PIDz/Tz/FTF)Úcapture_outputr   ÚcreationflagsÚcheckç      ð?©r   )Úpollrh   r}   Ú_terminate_process_groupÚ
subprocessÚrunr   Úpidr
   rU   ÚTimeoutExpiredÚkillÚwait)r“   s   &r   Ú_terminate_cron_script_processr¤   ×   sÀ   € à‡y�yƒ{ÒÙÜ
‡|�|�wÔÜ  Õ&ð	Ü�NŠNØ˜V¤S¨¯©£]°D¸$Ð?ÐPTÐ^`Ü0Ó2¸%õAð
Ø�	‰	˜#ˆ	Öøô œ×2Ñ2Ð3ô 	Ø�I‰IŽKð	ûô ×$Ñ$ô Ø�	‰	ŒØ�	‰	˜#ˆ	×ðús#   ·;B Á3B8 Â+B5Â4B5Â87C3Ã2C3c               ó    € V ^8„  d   QhRRRR/# r’   r   )r   s   "r   r   r   ë   s   € ÷ Mñ MÐ#3ð M¸ñ Mr   c           
     óÜ  €  \         P                  ! V P                  4      p\         P                  ! V\        P
                  4       \        P                  ! \        P                  4      ;_uu_ 4        T P                  RR7       RRR4        \         P                  ! T^ 4       \        P                  ! \        \        \        34      ;_uu_ 4        \         P                  ! T\        \        R\        P
                  4      4       RRR4       R#   \        \        \        3 d     R# i ; i  + '       g   i     L±; i  \        \        3 d     R# i ; i  + '       g   i     R# ; i)z¯POSIX: TERM the script's process group, then KILL if ANY member survived (a survivor holds
the pipe write ends open and the caller's communicate() would block on EOF forever).Nrš   r›   ÚSIGKILL)r‚   Úgetpgidr    ÚkillpgÚsignalÚSIGTERMÚProcessLookupErrorÚPermissionErrorrU   Ú
contextlibÚsuppressrž   r¡   r£   Úgetattr)r“   Úprocess_groups   & r   r�   r�   ë   sí   € ðÜŸ
š
 4§8¡8Ó,ˆÜ
�	Š	�-¤§¡Ô0ô 
×	Ò	œZ×6Ñ6×	7Õ	7Ø�	‰	˜#ˆ	Ô÷ 
8ðÜ
�	Š	�- Ô#ô 
×	Ò	Ô0´/Ä7ÐK×	LÕ	LÜ
�	Š	�-¤¬°¼F¿N¹NÓ!KÔL÷ 
MÑ	Løô ¤´Ð9ô Úðúç	7×	7ûô ¤Ð(ô Úðúç	L×	LÐ	Lús<   ‚AD Á3D/ÂE Ã5EÄD,Ä+D,Ä/D?	ÅEÅEÅE+	c               ó    € V ^8„  d   QhRRRR/# r’   r   )r   s   "r   r   r   ý   s   € ÷ Iñ IÐ&6ð I¸4ñ Ir   c                ób  a € S P                  4       e   R# RR/R V 3R lllp\        S RR4      p\        V\        4      '       d   V^ 8:  d
   V! RV4      #  ^ RIHp  T! T4      '       d   R#  T! RT4       R#   \         d    T! R	R
R7      u # i ; i  \         d    T! RTR
R7      u # i ; i)zHTerminate a script tree, then fall back to the local process-group path.NÚexc_infoFc               ó$   € V ^8„  d   QhRRRRRR/# )r   Úreasonr   r´   Úboolr   r”   r   )r   s   "r   r   Ú1_terminate_cron_script_tree.<locals>.__annotate__  s!   € ÷ -ñ -˜ð -¨tð -Àñ -r   c               ó`   <€ \         P                  ! V R ,           .VO5RV/  \        S4       R# )z+; falling back to process-group terminationr´   N)r$   r%   r¤   )r¶   r´   Úargsr“   s   &$*€r   ÚfallbackÚ-_terminate_cron_script_tree.<locals>.fallback  s3   ø€ Ü�ŠØÐBÕBð	^ØEIò	^ØT\ò	^ä& tÖ,r   r    z-Cron script tree-kill received invalid pid %r)Úkill_process_treez,agent.deadline.kill_process_tree unavailableT©r´   z'Cron script tree-kill failed for pid %sz3Cron script tree-kill reported no signal for pid %s)rœ   r°   r(   r   Úagent.deadliner½   r#   )r“   r»   r    r½   s   f   r   Ú_terminate_cron_script_treerÀ   ý   sÊ   ø€ à‡y�yƒ{Òáð-°e÷ -ò -ô
 �$˜˜tÓ
$€CÜ�cœ3×Ò 3¨!¤8ÙÐGÈÓMÐMðWõ 	5ðWÙ˜S×!Ò!Ùð "ñ ÐBÀCÖHøô ô WÙÐFÐQUÔVÒVðWûô
 ô WÙÐAÀ3ÐQUÔVÒVðWús$   ÁA: ÁB Á:BÂBÂB.Â-B.c               ó    € V ^8„  d   QhRRRR/# r’   r   )r   s   "r   r   r     s   € ÷ ñ Ð.ð °4ñ r   c                óø  € \         P                  ! \        P                  4      ;_uu_ 4        V P	                  RR7        RRR4       R#   + '       g   i     M; i\         P                  ! \
        4      ;_uu_ 4        T P                  4        RRR4       M  + '       g   i     M; iT P                  T P                  3 FV  p\         P                  ! \
        4      ;_uu_ 4        Te   TP                  4        RRR4       KD    + '       g   i     KU  ; i	  \         P                  ! \        P                  4      ;_uu_ 4        T P                  RR7       RRR4       R#   + '       g   i     R# ; i)z­Reap a terminated script without blocking forever: a surviving descendant can hold the pipe
write ends open, so bound the drain and abandon the pipes (output is not needed).g      @r›   N)r®   r¯   rž   r¡   ÚcommunicaterU   r¢   ÚstdoutÚstderrÚcloser£   )r“   Ústreams   & r   Ú_drain_script_pipesrÈ     så   € ô 
×	Ò	œZ×6Ñ6×	7Õ	7Ø×Ñ ÐÔ%Ø÷ 
8×	7×	7Ñ	7úô 
×	Ò	œW×	%Õ	%Ø�	‰	Œ÷ 
&×	%×	%Ð	%úà—;‘; §¡Ó,ˆÜ× Ò ¤×)Õ)ØÒ!Ø—‘”÷ *×)×)Ò)úñ -ô 
×	Ò	œZ×6Ñ6×	7Õ	7Ø�	‰	˜#ˆ	Ô÷ 
8×	7×	7Ò	7ús/   ­AÁA	Â BÂB*	Ã*D	Ä	DÅE(Å(E9	c               ó(   € V ^8„  d   QhRRRRRRRR/# )r   rq   r   rˆ   rJ   Úscript_pathr   z	list[str]r   )r   s   "r   r   r   (  s,   € ÷ 6ñ 6Øð6Ø"0ð6Ø?Bð6ØGPñ6r   c           	     ón  € \        R VP                  RR4      P                  \        P                  4       4       \
        P                  ! VP                  RR4      4      R,          R,          4      pVP                  4       '       g   \        P                  RV4       W.# R\        V4      : R	2pV R
WB.# )al  Bootstrap a cron script under the base interpreter with ``.pth`` support. Overlay mode puts
the venv on ``PYTHONPATH``, but ``.pth`` files are only processed by ``site.addsitedir()``, so
editable installs would be invisible; bootstrap via addsitedir + ``runpy.run_path`` (keeps
``__file__``/``sys.path[0]`` semantics). Plain invocation if the venv is unresolvable.c              3  óp   "  € T F,  p\        V4      P                  R 8X  g   K  \        V4      x € K.  	  R# 5i)rz   N)r   r   )rP   Úitems   & r   rQ   Ú/_windows_cron_bootstrap_argv.<locals>.<genexpr>.  s0   é € ð AÑ1d¨Ü! $›ZŸ_™_°Ñ?ô %œ$˜tŸ*˜*Ó1dùs   ‚6£6rv   rx   r|   ry   rz   z}Windows cron script: venv site-packages %s not found; running without .pth processing (editable installs may be unimportable)z,import os, runpy, site, sys;site.addsitedir(z¢);script = sys.argv[1];sys.argv = [script] + sys.argv[2:];sys.path.insert(0, os.path.dirname(os.path.abspath(script)));runpy.run_path(script, run_name='__main__')r`   )Únextr*   rO   r‚   rƒ   r&   r   Úis_dirr$   r%   r   )rq   rˆ   rÊ   ru   Ú	bootstraps   &&&  r   Ú_windows_cron_bootstrap_argvrÒ   (  s»   € ô ñ A°·±ÀÈrÓ1R×1XÑ1XÔY[×YcÑYcÔ1dó AäŸš [§_¡_°]ÀBÓ%GÓHÈ5ÕPÐSbÕbód€Mð ×Ñ×!Ò!ä�‰ðNàô	ð Ð(Ð(ð	Ü˜}Ó-Ñ0ð 16ð	6ð ð ˜˜iÐ5Ð5r   c               ó    € V ^8„  d   QhRRRR/# )r   rÊ   r   r   z$tuple[Optional[Path], Optional[str]]r   )r   s   "r   r   r   C  s   € ÷ +ñ + cð +Ð.Rñ +r   c                óŠ  € \         P                  ! 4       R,          p\        V4       VP                  4       pR\	        V 4      9   d	   RRV : 23#  \         P
                  ! V 4      P                  4       pTP                  4       '       d   TP                  4       MY,          P                  4       p TP                  T4       TP                  4       '       g   RRT R	T R
23# TP                  4       '       g   RRT 23# TR3#   \        \        \        3 d    RRT : 23u # i ; i  \         d    RRT RT : 23u # i ; i)u  Validate a job script path; ``(path, None)`` or ``(None, error)``. Scripts MUST resolve
inside HERMES_HOME/scripts/ (relative, absolute and ``~`` paths are all validated â€” path
traversal / absolute-path injection); contract of lifecycle_guard._expand_candidate_path.ÚscriptsÚ Nz*Blocked: script path contains a NUL byte: z5Blocked: script path is not a valid filesystem path: z=Blocked: script path resolves outside the scripts directory (z): zScript not found: z<. Cron scripts are looked up only in this profile's folder (zr); if the job was copied from another profile, copy the script there too, or edit the job with `hermes cron edit`.zScript path is not a file: )r&   Ú_get_hermes_homer   rf   r   r   Ú
expanduserÚ
ValueErrorrk   rU   Úis_absoluteÚrelative_tor�   rj   )rÊ   Úscripts_dirÚscripts_dir_resolvedr   Úpaths   &    r   Ú_resolve_script_pathrß   C  sl  € ô ×)Ò)Ó+¨iÕ7€KÜ�[Ô!Ø&×.Ñ.Ó0Ðð ”�[Ó!Ô!ØÐAÀ+ÁÐQÐQÐQð]Ü�kŠk˜+Ó&×1Ñ1Ó3ˆð  ŸO™O×-Ò-ˆ3�;‰;Œ=°KÕ4E×3NÑ3NÓ3P€Dð
Ø×ÑÐ-Ô.ð �;‰;�=Š=ð Ø   ð 'Ø$Ð%ð &BðCð
ð 	
ð
 �<‰<�>Š>ØÐ2°4°&Ð9Ð9Ð9Ø�ˆ:Ðøô/ œ¤gÐ.ô ]àÐLÈ[ÉOÐ\Ð\Ò\ð]ûô ô 
ØðØ$Ð% S¨©ð9ð
ò 	
ð
ús$   Á$D Â2D) ÄD&Ä%D&Ä)EÅEc               ó    € V ^8„  d   QhRRRR/# )r   r‡   r   r   z#tuple[Optional[str], Optional[str]]r   )r   s   "r   r   r   q  s   € ÷ ñ ¨3ð Ð3Vñ r   c                ó  a€ ^ RI Ho V P                  4       p \        V4      P	                  4       pVP                  4       '       g
   RRV: R23# VP                  4       P                  pVP                  P                  4       VP                  4       P                  P                  4       0p\        P                  ! T4      '       g   RRT 23# \        P                   R	8w  d   T^I,          '       g   RR
T 23# \"        ;QJ d    T3R lT 4       F  '       d   K   RM	  RM! T3R lT 4       4      '       g   RRT 23# \%        T4      R3#   \         d    RRT 23u # \        \        3 d   pRRT: RT 23u Rp?# Rp?ii ; i)u)  ``(python_exe, error)`` for a job's ``interpreter`` field. Checked at run time, not create
time: a user venv can be rebuilt or moved while the job lives. Bare names are refused â€” they
silently change meaning with PATH. The name (and the symlink target's name) must look like a
Python: the lifecycle guard classifies ``.py`` scripts as Python and skips its shell reference
walk, so ``interpreter=/bin/bash`` would run an unscanned ``.py`` body as shell. ``pythonw``
is refused because it discards captured output (an agent job would go silently quiet).)Ú_INTERPRETER_IMAGE_RENz8Interpreter must be an absolute or ~-prefixed path (got z@). Bare names like 'python3' are not stable across PATH changes.zInterpreter not found: z#Unable to resolve interpreter path z: z Interpreter path is not a file: rs   zInterpreter is not executable: c              3  ó‚   <"  € T F4  pSP                  V4      ;'       d    VP                  R 4      '       * x € K6  	  R# 5i)ÚpythonwN)ÚmatchÚ
startswith)rP   r   râ   s   & €r   rQ   Ú,_resolve_cron_interpreter.<locals>.<genexpr>Š  s:   øé € ð "Ù �4ð %×*Ñ*¨4Ó0×SÐS¸¿¹ÈÓ9SÔ5SÔSÛ ùs   ƒ? ?FTzLInterpreter must be a Python executable (python, python3, python3.12, ...): )Úcron.lifecycle_guardrâ   r"   r   rØ   rÚ   ÚstatÚst_moder   rV   rf   ÚFileNotFoundErrorrk   rU   ÚS_ISREGrh   r}   Úallr   )r‡   r   r:   ÚmodeÚnamesr1   râ   s   &     @r   Ú_resolve_cron_interpreterrð   q  s�  ø€ õ ;à
×
Ñ
Ó
€Cð
JÜ˜“9×'Ñ'Ó)ˆØ×#Ñ#×%Ò%ØÐTÐUXÑT[ð \Zð Zð [ð [à�}‰}‹×&Ñ&ˆØ—‘×$Ñ$Ó&¨×(8Ñ(8Ó(:×(?Ñ(?×(EÑ(EÓ(GÐHˆô
 �<Š<˜×ÒØÐ7¸°zÐBÐBÐBÜ
‡|�|�wÔ t¨e§|¤|ØÐ6°x°jÐAÐAÐAß‹3ô "Ù ó"�3�3Š3ô "Ù ó"÷ "ò "àÐcÐdlÐcmÐnÐnÐnÜˆx‹=˜$ÐÐøô ô 5ØÐ.¨s¨eÐ4Ð4Ò4Üœ'Ð"ô JØÐ:¸3¹'ÀÀCÀ5ÐIÐIÕIûðJús6   ™.E ÁE ÁAE ÅFÅ#FÅ2FÅ3
FÅ=FÆFc               ó$   € V ^8„  d   QhRRRRRR/# )r   rÞ   r   r‡   úOptional[str]r   z9tuple[Optional[list[str]], dict[str, str], Optional[str]]r   )r   s   "r   r   r   �  s$   € ÷ 6ñ 6Ø
ð6Ø*ð6à>ñ6r   c                ó¬  € V P                   P                  4       R9   dp   \        P                  ! R4      ;'       g*    \        P
                  P                  R4      '       d   RMRpVf   R/ RV P                  : R23# V\        V 4      ./ R3# \        V\        4      '       dA   VP                  4       '       d+   \        V4      w  r4V'       d   V\        V 4      ./ V3# R/ V3# \        P                  R8w  d   \        V 4      w  rVWVR3# \        \        P                   4      w  r6V'       d   \#        W6\        V 4      4      VR3# V\        V 4      .VR3# )uŸ  ``(argv, env_overlay, error)`` for a validated script. Interpreter by extension â€” the
shebang is deliberately NOT honoured (small, auditable surface): ``.sh``/``.bash`` â†’ bash,
else the job's ``interpreter`` when set, else a Python chosen by ``_posix_cron_script_argv``
/ ``_windows_cron_python_invocation``. Interpreter selection reads PM's install records and
may raise; callers run this inside their ``try``.Úbashz	/bin/bashNzCannot run .sh/.bash script z{: bash not found on PATH. On Windows, install Git for Windows (which ships Git Bash) or rewrite the script as Python (.py).rs   >   ú.shú.bash)ÚsuffixrV   ÚshutilÚwhichr‚   rÞ   Úisfiler   r   r(   r"   rð   rh   r}   ro   r�   ri   rÒ   )rÞ   r‡   Ú_bashrq   ÚerrÚargvrˆ   s   &&     r   Ú_script_argvrþ   �  s;  € ð ‡{�{×ÑÓÐ.Ô.ä—’˜VÓ$×^Ð^¼¿¹¿¹À{×8SÒ8S©ÐY]ˆØŠ=Ø˜Ø.¨t¯y©y©mð <9ð 9ðð ð
 ”s˜4“yÐ! 2 tÐ+Ð+Ü�+œs×#Ò#¨×(9Ñ(9×(;Ò(;ô 4°KÓ@‰ˆ
ß+5�œS ›YÐ'À"ÀcÐIÐI¸4À"ÀcÐIÐIÜ
‡|�|�wÔÜ3°DÓ9ÑˆØ $Ð&Ð&Ü=¼c¿n¹nÓMÑ€JßÜ+¨JÄSÈÃYÓOÐQ\Ð^bÐbÐbØœ˜D›	Ð" K°Ð5Ð5r   c          
     ó,   € V ^8„  d   QhRRRRRRRRRR	/# )
r   rÊ   r   Úworkdirrò   Úcancel_eventúOptional[_CancelEventLike]r‡   r   útuple[bool, str]r   )r   s   "r   r   r   °  s=   € ÷ h8ñ h8Øðh8Ø,ðh8à,ðh8àBOðh8ð ñh8r   c                óœ  € \        V 4      w  rEVf   RV3# \        4       p \        WC4      w  rxpVf   RV3# ^ RIHp	 RRRR/p
\
        P                  R8X  d'   R	\        4       \        \        R
^ 4      ,          RRRR/p
V	! RR7      pVP                  V4       \        P                  ! V3R\        P                  R\        P                  RRRT;'       g    \        VP                  4      RV/V
B p\        P                   ! 4       V,           p Ve/   VP#                  4       '       d   \%        V4       \'        V4       R # V\        P                   ! 4       ,
          pV^ 8:  d!   \%        V4       \'        V4       RRV RV 23#  VP)                  \+        RV4      R7      w  pp T;'       g    RP/                  4       pT;'       g    RP/                  4       p ^ RIHp T! T4      pT! T4      pTP:                  ^ 8w  d\   RTP:                   2.pT'       d   TP=                  RT 24       T'       d   TP=                  RT 24       RRP?                  T4      3# RT3#   \        P,                   d     EKk  i ; i  \4         d%   p\6        P9                  RT4       R;pp Rp?L·Rp?ii ; i  \4         d   pRRT 23u Rp?# Rp?ii ; i)!uî  Execute a cron job's script and return ``(success, output)``; on failure *output* is the
error message for the LLM to report. Env goes through ``build_subprocess_env`` (SECURITY.md
Â§2.3). ``workdir`` sets the subprocess cwd only; the Python process cwd is NEVER mutated (an
``os.chdir()`` would leak into concurrent gateway sessions).

Args: script_path: Path to the script. Relative paths are resolved against HERMES_HOME/scripts/.
Absolute and ~-prefixed paths are also validated to ensure they stay within the scripts dir. workdir:
Optional absolute path to use as the script's cwd. When set, the subprocess runs in this directory
instead of the scripts-dir parent. See #69396. interpreter: the job's optional Python for
``.py`` scripts (#8714).
NF)Úbuild_subprocess_envÚstart_new_sessionTÚerrorsÚreplacers   r˜   ÚCREATE_NEW_PROCESS_GROUPrL   zutf-8)Ústrip_launch_profilerÄ   rÅ   ÚtextÚcwdÚenvzScript timed out after zs: gš™™™™™¹?r›   rx   )Úredact_sensitive_textz/Failed to redact sensitive text from output: %sz[REDACTED - redaction failed]zScript exited with code zstderr:
zstdout:
Ú
zScript execution failed: )Fz5Script cancelled because cron fire ownership was lost) rß   r;   rþ   Útools.environments.localr  rh   r}   r
   r°   rž   ÚupdateÚPopenÚPIPEr   r~   ÚtimeÚ	monotonicÚis_setrÀ   rÈ   rÃ   Úminr¡   r"   Úagent.redactr  r#   r$   r%   Ú
returncoder†   r„   )rÊ   r   r  r‡   rÞ   rü   Úscript_timeoutrý   rˆ   r  Úpopen_kwargsr  r“   ÚdeadlineÚ	remainingÚ
stdout_rawÚ
stderr_rawrÄ   rÅ   r  ÚeÚpartsr1   s   &&&&                   r   Ú_run_job_scriptr"  °  sæ  € ô % [Ó1�I€DØ‚|Ø�cˆzÐÜ(Ó*€NðU8Ü!-¨dÓ!@Ñˆ˜3ØŠ<Ø˜#�:ÐÝAð   Ø�ið(
ˆô �<‰<˜7Ô"àÔ!3Ó!5Üœ*Ð&@À!ÓDõ"Eð ˜GØ˜)ð%ˆLñ #¸Ô=ˆØ�
‰
�;Ôô
 ×ÒØñFÜ#Ÿ™ðFÜ1;·±ðFØGKðFà×+Ð+œ3˜tŸ{™{Ó+ðFà14ðFà8DñFˆô —>’>Ó# nÕ4ˆØð Ò'¨L×,?Ñ,?×,AÒ,AÜ+¨DÔ1Ü# DÔ)ØUÐUØ ¤4§>¢>Ó#3Õ3ˆIØ˜AŒ~Ü+¨DÔ1Ü# DÔ)ð Ð 7¸Ð7GÀsÈ4È&ÐQÐQÐQðØ)-×)9Ñ)9Ä#ÀcÈ9ÓBUÐ)9Ó)VÑ&�
˜JØð ×"Ð" ×)Ñ)Ó+ˆØ×"Ð" ×)Ñ)Ó+ˆð	>Ý:Ù*¨6Ó2ˆFÙ*¨6Ó2ˆFð
 �?‰?˜aÔØ/°·±Ð/@ÐAÐBˆEßØ—‘˜y¨¨Ð1Ô2ßØ—‘˜y¨¨Ð1Ô2Ø˜$Ÿ)™) EÓ*Ð*Ð*Ø�Vˆ|Ðøô- ×,Ñ,ô Ûðûô ô 	>Ü�N‰NÐLÈaÔPØ=Ð=ˆF•Vûð	>ûô ô 8ØÐ1°#°Ð7Ð7Õ7ûð8ús£   ¡J/ ·BJ/ ÃAJ/ Ä)J/ ÅAJ/ ÆI  Æ$	J/ Æ.J/ ÇJ/ ÇI= Ç0(J/ ÈJ/ È5&J/ ÉJ/ É I:É5J/ É9I:É:J/ É=J,ÊJ'Ê"J/ Ê'J,Ê,J/ Ê/KÊ:KË KËKc               ó    € V ^8„  d   QhRRRR/# )r   r   r   r   zOptional[threading.Thread]r   )r   s   "r   r   r     s   € ÷ ñ ¨3ð Ð=Wñ r   c                óÊ   € \         P                  ! \        P                  ! 4       P                  V 3VRR7      p VP                  4        V#   \         d    T! 4         R# i ; i)zÃStart ``loop_fn`` on a daemon thread inside a copy of the current context (multiplexed
profile ContextVars). On failure calls ``fail_log()`` inside the except (traceback intact) and
returns None.T)Útargetrº   r   ÚdaemonN)Ú	threadingÚThreadÚcontextvarsÚcopy_contextrŸ   Ústartr#   )Úloop_fnr   Úfail_logÚthreads   &&& r   Ú_start_heartbeat_threadr/    s^   € ô ×ÒÜ×'Ò'Ó)×-Ñ-°W°JÀTÐRVôX€FðØ�‰Œð €Møô ô ÙŒ
Úðús   ºA ÁA"Á!A"c          
     ó,   € V ^8„  d   QhRRRRRRRRR	R
/# )r   Újobr)   rÊ   r   r   rò   r  r  r   r  r   )r   s   "r   r   r   )  s6   € ÷ )+ñ )+Ø	ð)+Øð)+Ø*7ð)+à,ð)+ð ñ)+r   c                ó¼  a aaaa	a
a€ R VV VV3R llpS P                  R4      pS P                  R4      p\        V\        4      '       d%   \        VP                  R4      ;'       g    R4      MRo
\        V\        4      '       d   VP                  R4      R8X  d	   S
'       g   V! 4       # \        S P                  R	4      ;'       g    R4      o	\        P
                  ! 4       oR
 V	V
V3R llp\        VRV	3R l4      pVf   V! 4       #  V! 4       SP                  4        VP                  RR7       #   SP                  4        TP                  RR7       i ; i)ul  Run a cron script while heartbeating its owned one-shot claim. A long script can outlive
the stale-claim TTL; without a heartbeat another scheduler would re-dispatch the one-shot.
Recurring/unclaimed runs have no durable claim â†’ no thread. The owner is captured from the
dispatched job, never re-read, so a stale runner cannot extend a replacement owner's claim.c               ó   € V ^8„  d   QhRR/# )r   r   r  r   )r   s   "r   r   Ú:_run_job_script_with_claim_heartbeat.<locals>.__annotate__1  s   € ÷ Cñ CÐ!ñ Cr   c                 ó@   <€ \        SSS SP                  R 4      R7      # )r‡   )r   r  r‡   )r"  r*   )r  r1  rÊ   r   s   €€€€r   rŸ   Ú1_run_job_script_with_claim_heartbeat.<locals>.run1  s$   ø€ Ü˜{°GÈ,Ø+.¯7©7°=Ó+AôCð 	Cr   ÚscheduleÚ	run_claimÚbyrx   ÚkindÚonceÚidc               ó   € V ^8„  d   QhRR/# )r   r   r”   r   )r   s   "r   r   r4  >  s   € ÷ cñ c˜Tñ cr   c                 óÜ   <€ SP                  \        P                  4      '       g    \        P                  ! S SR 7       K@  R#   \         d    \
        P                  RS RR7        Kh  i ; i))Úexpected_ownerz+Job '%s': script run_claim heartbeat failedTr¾   N)r£   r&   Ú_RUN_CLAIM_HEARTBEAT_SECONDSÚheartbeat_run_claimr#   r$   r+   )Újob_idÚownerÚstops   €€€r   Ú_heartbeat_loopÚ=_run_job_script_with_claim_heartbeat.<locals>._heartbeat_loop>  s[   ø€ Ø—)‘)œF×?Ñ?×@Ò@ðcÜ×*Ò*¨6À%×Hñ Aøô ô cÜ—‘ÐJÈFÐ]a�×bðcús   ¨A Á#A+Á*A+zcron-script-claim-heartbeatc                 ó4   <€ \         P                  R S RR7      # )z4Job '%s': could not start script run_claim heartbeatTr¾   )r$   r+   )rB  s   €r   Ú<lambda>Ú6_run_job_script_with_claim_heartbeat.<locals>.<lambda>G  s   ø€ ”—‘ØBÀFÐUYð ô [r   rš   r›   )	r*   r(   r)   r   r'  ÚEventr/  Úsetr„   )r1  rÊ   r   r  rŸ   r7  ÚclaimrE  Úheartbeat_threadrB  rC  rD  s   ffff     @@@r   Ú$_run_job_script_with_claim_heartbeatrN  )  s  þ€ ÷Có Cð �w‰w�zÓ"€HØ�G‰G�KÓ €EÜ*4°U¼D×*AÒ*AŒC�—	‘	˜$“×%Ð% 2Ô&Àr€EÜ�x¤×&Ò&¨8¯<©<¸Ó+?À6Ô+IÏeÙ‹uˆä�—‘˜“×$Ð$ "Ó%€FÜ�?Š?Ó€D÷cò cô /ØÐ6ô	[óÐð
 ÒÙ‹uˆð+Ù‹uà�‰Œ
à×Ñ cÐÕ*øð 	�‰Œ
à×Ñ cÐÕ*ús   ÄD7 Ä7$E)Ú	scheduler)N)NNN)NN)7Ú__doc__Ú
__future__r   r®   r)  Úloggingr‚   rø   rª   ré   rž   rh   r'  r  Úcron.env_settingsr   Ú	cron.jobsr   Úpathlibr   Útypingr   r   r   r	   Úhermes_cli._subprocess_compatr
   Úcron.schedulerr   Ú	getLoggerr$   r   r2   r;   r@   rB   rG   rY   rl   ro   r�   r¤   r�   rÀ   rÈ   rÒ   rß   rð   rþ   r"  r/  rN  r!   rO  r&   r   r   r   Ú<module>rZ     sÙ   ðñõ #ã Û Û Û 	Û Û Û Û Û 
Û Û Ý .Ý &Ý ß 9Ó 9å <çÝ/ð 
×	Ò	Ð+Ó	,€õ,õõ2Lð$ "Ð õIõ2õðÐ õ(3õ2.)õbõ(Mõ$Iõ6õ 6õ6+õ\÷>6÷@h8õV÷)+÷\ %r   