+
    lHàj[h  ã                   ó–  € R t ^ RIt^ RIt^ RIHtHt ^ RIHtHtHtH	t	H
t
 ^ RIHt ^ RIt]P                  ! R4      t^<tR R ltR2R R	 l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tRR/R R lltR R ltR R ltR R ltR  R! ltR" R# ltR$ R% ltR& R' lt R( R) lt!R* R+ lt"R, R- lt#R4t$R. R/ lt%R5R0 R1 llt&R# )6zgCron job argument normalization, validation and result shaping (re-exported by
tools/cronjob_tools.py).N)ÚdatetimeÚ	timedelta)ÚAnyÚDictÚListÚOptionalÚUnion)Úeffective_job_stateztools.cronjob_toolsc                óv   € V ^8„  d   QhR\         \        \         \        3,          ,          R,          R\        /# ©é   ÚscheduleNÚreturn)ÚstrÚdictr   Úbool)Úformats   "Ú;/home/ubuntu/.hermes/hermes-agent/tools/cronjob_job_args.pyÚ__annotate__r      s2   € ÷ "Oñ "OÜ”Dœœc˜•NÕ" TÕ)ð"Oä	ñ"Oó    c                óÂ  € V '       g   R# \        V \        4      '       d   T pM;Rp\        P                  ! \        4      ;_uu_ 4        ^ RIHp V! V 4      pRRR4       \        V\        4      '       d   VP                  R4      R8w  d   R# VP                  R4      pV'       g   R#  \        P                  ! \        V4      4      p\        P                  ! 4       pTP                  f   TP                  TP                  R7      pYE,
          p\!        ^ 4      Tu;8*  ;'       d    \!        \"        R7      8*  # u #   + '       g   i     Lí; i  \         d     R# i ; i)	a  True when the job's first fire is close enough that the creating conversation is still
alive when it happens. Only near one-shots qualify; recurring jobs and one-shots beyond the
horizon outlive the conversation, which is what the synthetic-drop rule protects.FN)Úparse_scheduleÚkindÚonceÚrun_at)Útzinfo)Úminutes)Ú
isinstancer   Ú
contextlibÚsuppressÚ	ExceptionÚ	cron.jobsr   Úgetr   Úfromisoformatr   Ú
ValueErrorÚhermes_timeÚnowr   Úreplacer   Ú_THREAD_HORIZON_MINUTES)r   Úparsedr   r   Úfire_atr&   Údeltas   &      r   Ú!_first_fire_within_thread_horizonr,      s  € ÷ Ùä�(œD×!Ò!Ø‰àˆÜ× Ò ¤×+Õ+Ý0á# HÓ-ˆF÷ ,ô �fœd×#Ò# v§z¡z°&Ó'9¸VÔ'CÙØ�Z‰Z˜Ó!€FßÙðÜ×(Ò(¬¨V«Ó5ˆô �/Š/Ó
€CØ‡~�~ÒØ—/‘/¨¯©�/Ó4ˆð
 �M€EÜ�Q‹<˜5×NÔN¤IÔ6MÔ$NÑNÐNÑNÐN÷+ ,×+ûô ô Úðús   ÁD<Â)E Ä<E	ÅEÅEc                ó®   € V ^8„  d   QhR\         \        \         \        3,          ,          R,          R\        \        \         \         3,          ,          /# r   )r   r   r   r   )r   s   "r   r   r   ;   s;   € ÷ 0ñ 0Ü”Dœœc˜•NÕ" TÕ)ð0äŒd”3œ�8�nÕñ0r   c                 óV  € ^ RI HpHp V! R4      pV! R4      pV'       d	   V'       g   R# V! 4       '       g   R# V! R4      ;'       g    RpV'       d{   VR8X  dt   V! R4      ;'       g    RpV'       dZ   \        V4      \        V4      8X  dA   \	        V 4      '       d   \
        P                  RV4       M\
        P                  R	V4       RpV'       d   \
        P                  R
WSV4       RTRTRV! R4      ;'       g    RRTRV! R4      ;'       g    RRV! R4      ;'       g    R/# )é    ©Úasync_delivery_supportedÚget_session_envÚHERMES_SESSION_PLATFORMÚHERMES_SESSION_CHAT_IDNÚHERMES_SESSION_THREAD_IDÚslackÚHERMES_SESSION_MESSAGE_IDuc   Cron origin: keeping synthetic Slack thread_id=%s â€” first fire is within the conversation horizonzWCron origin: dropping synthetic per-message Slack thread_id=%s (== creation message id)z+Cron origin captured thread_id=%s for %s:%sÚplatformÚchat_idÚ	chat_nameÚHERMES_SESSION_CHAT_NAMEÚ	thread_idÚuser_idÚHERMES_SESSION_USER_IDÚscope_idÚHERMES_SESSION_SCOPE_ID)Úgateway.session_contextr1   r2   r   r,   ÚloggerÚdebug)r   r1   r2   Úorigin_platformÚorigin_chat_idr<   Ú
message_ids   &      r   Ú_origin_from_envrG   ;   s   € ÷ RÙ%Ð&?Ó@€OÙ$Ð%=Ó>€Nß§Ùñ $×%Ò%ÙÙÐ :Ó;×CÐC¸t€I÷ �_¨Ô/Ù$Ð%@ÓA×IÐIÀTˆ
ßœ#˜i›.¬C°
«OÔ;Ü0°×:Ò:Ü—‘ð6àõô —‘ð<àôð
 !�	ßÜ�‰Ø9Ø¨ô	8ð 	�O Y°Ø‘_Ð%?Ó@×HÐHÀDÈ+ÐW`à‘?Ð#;Ó<×DÐDÀð 	‘OÐ$=Ó>×FÐFÀ$ðð r   c                óŠ   € V ^8„  d   QhR\         \        \        3,          R\        \        ,          R\        \        ,          /# ©r   ÚjobÚuser_deliverr   )r   r   r   r   )r   s   "r   r   r   n   s9   € ÷ #Sñ #S¤¤S¬# X¥ð #S¼hÄs½mð #SÔPXÔY\ÕP]ñ #Sr   c                óî  € T;'       g    RP                  4       P                  4       R8X  d   R#  ^ RIHp V! V 4      pV'       d{   ^ RIHpHp V Uu. uF  qfP                  R4      R8X  g   K  VNK  	  ppV'       dA   V! R4      '       d3   V! 4       '       g&   R	V^ ,          R
,           RV^ ,          R,           R2# R#   R# u upi   \         d     T P                  R4      '       d    R#   R# i ; i)u_  Notice when a created job won't deliver anywhere: CLI/TUI sessions have no capturable
origin, so deliver='origin' (or omitted) saves output but never delivers it. None when the
user explicitly asked for ``local`` or the job resolves to a real target.

TUI/CLI sessions cannot be captured as a cron ``origin`` (no ``HERMES_SESSION_PLATFORM``/``CHAT_ID`` is
set for them), so a ``deliver="origin"`` request â€” or an omitted ``deliver`` that defaults to
origin-or-local â€” produces a job that runs and saves output to ``last_output`` but is never delivered
back into the session. This is by design (there is no live-delivery channel for local sessions), but
silently dropping the user's "tell me when it runs" intent is the trap reported in 51568. Surface it at
create time so the agent can relay it instead of promising a delivery that never happens. See #51568.
Ú ÚlocalN)Ú_resolve_delivery_targetsr0   Ú_resolved_fromÚorigin_fallbackr3   zpNote: this stateless HTTP API session cannot receive cron delivery, so this job will report to the home channel r8   Ú:r9   z instead of back here.Úoriginuh  This is a local-only cron job: its output is saved (view it with cronjob(action='list')) but will NOT be delivered back into this session â€” CLI/TUI and stateless HTTP API sessions have no live-delivery channel. To be notified when it runs, recreate or update the job with deliver set to a gateway-connected platform, e.g. deliver='telegram' or deliver='all'.)	ÚstripÚlowerÚcron.schedulerrO   rA   r1   r2   r"   r    )rJ   rK   rO   Útargetsr1   r2   ÚtÚfallbacks   &&      r   Ú_local_delivery_noticerZ   n   s  € ð 	×Ð˜×!Ñ!Ó#×)Ñ)Ó+¨wÔ6ÙðÝ<Ù+¨CÓ0ˆß÷ ZÙ#*Ó[¡7˜a¯e©eÐ4DÓ.EÐIZÑ.ZŸ˜¡7ˆHÐ[ß™OÐ,E×FÒFÑOg×OiÒOið?Ø?GÈ½{È:Õ?VÐ>WÐWXØ# A�; yÕ1Ð2Ð2HðJð Kñ ð ð	RñSùò \øô ô Ø�7‰7�8×ÒÚð ð	RñSðús;   ±C
 ÁC
 ÁCÁ/CÁ5C
 ÂC
 Â$C
 ÃC
 Ã
!C4Ã3C4c                óŠ   € V ^8„  d   QhR\         \        \        3,          R\        \        ,          R\        \        ,          /# rI   )r   r   r   r   Úlist)r   s   "r   r   r   ”   s2   € ÷ (ñ (œd¤3¬ 8�nð (¼HÄS½Mð (ÌdÔSVÍiñ (r   c                ó2  € . pV P                  R4      '       g   V P                  R4      '       d   VP                  R4       V P                  R4      '       d   VP                  R4       T;'       g    RP                  4       P                  4       pV'       dþ   RVP	                  R4      9   d   VP                  R	4       VP                  R
4      '       d   VP                  R4       VP	                  R4       F™  pVP                  4       P	                  R4      p\        V4      ^8X  g   K4  V^ ,          R9  g   KD  V^,          '       g   KU  V^,          P                  R4      '       d   Ku  VP                  RVP                  4        R24        V# 	  V# )zqMode guidance echoed once in the create/update response (not in the schema, which is
paid for on every API call).Úmonitor_scriptÚmonitor_urlus  Monitor mode: the source runs first each tick and its output is hashed as exact bytes â€” unchanged output suppresses the agent run (silent no_change tick), changed output injects a MONITOR CHANGE DETECTED diff into the prompt. The first tick always runs as baseline. The source must emit STABLE output (no timestamps, no random ordering) or every tick will look changed.Úno_agentué   no_agent mode: stdout is delivered verbatim; EMPTY stdout sends nothing at all (watchdog pattern â€” script should stay quiet when there is nothing to report). Non-zero exit or timeout sends an error alert. prompt/skills are ignored.rM   ÚallÚ,u�   deliver='all' resolves at fire time and never includes bot-chat targets â€” channels connected later are picked up automatically.z	bot-chat:zJTargeting another profile's Bot Chat costs that bot an agent turn per run.rR   Ú#zdeliver target 'uk   ' has no :thread_id segment â€” on thread/topic platforms the delivery lands in the main chat, not a topic.)zbot-chatÚsms)r"   ÚappendrT   rU   ÚsplitÚ
startswithÚlen)rJ   rK   ÚnotesÚ_deliverÚtargetÚpartss   &&    r   Ú_mode_guidance_notesrm   ”   sV  € ð €EØ
‡w�wÐ× Ò  C§G¡G¨M×$:Ò$:Ø�‰ð@ô	Að ‡w�wˆz×ÒØ�‰ð6ô	7ð
 ×"Ð" ×)Ñ)Ó+×1Ñ1Ó3€HßØ�H—N‘N 3Ó'Ô'Ø�L‰Lð!ô"ð ×Ñ˜{×+Ò+Ø�L‰LÐeÔfà—n‘n SÖ)ˆFØ—L‘L“N×(Ñ(¨Ó-ˆEä�E“
˜a–Ø˜!•HÐ$7Ö7Ø˜!—H–HØ˜a�×+Ñ+¨C×0Ô0Ø—‘Ø& v§|¡|£~Ð&6ð 72ð 2ô3ð Ø€Lñ *ð €Lr   c                óŠ   € V ^8„  d   QhR\         \        ,          R\         \        ,          R\         \        ,          R\        /# )r   Úmonitorr^   r_   r   )r   r   Útuple)r   s   "r   r   r   ¿   s<   € ÷ ñ Ü”c�]ðäœS•Mðô œ#•ðô $)ñr   c                óž   € V f   W3# V P                  4       pV'       g   R# VP                  4       P                  R4      '       d   RV3# VR3# )a|  Resolve the model-facing ``monitor`` field into the stored ``(monitor_script,
monitor_url)`` pair. http(s):// is a URL, anything else a script path (a legal script path
never starts with a URL scheme). None = unchanged, '' = clear; setting one source clears
the other so switching transports never trips mutual exclusion; an explicit ``monitor``
wins over the legacy alias fields.rM   )rM   rM   )zhttp://zhttps://)rT   rU   rg   )ro   r^   r_   Úvalues   &&& r   Ú_split_monitor_argrs   ¿   sQ   € ð ‚ØÐ*Ð*Ø�M‰M‹O€EßØˆØ‡{�{ƒ}×ÑÐ 7×8Ò8Ø�5ˆyÐØ�"ˆ9Ðr   c                óR   € V ^8„  d   QhR\         \        \        3,          R\        /# ©r   rJ   r   ©r   r   r   )r   s   "r   r   r   Ò   s&   € ÷ Eñ Eœœc¤3˜h�ð E¬Cñ Er   c                 óÔ   € V P                  R 4      ;'       g    / pVP                  R4      VP                  R^ 4      r2Vf   R# V^8X  d   V^ 8X  d   R# R# V'       d   V RV 2# V R2# )ÚrepeatÚtimesÚ	completedÚforeverr   z1/1Ú/z times©r"   )rJ   Úrepry   rz   s   &   r   Ú_repeat_displayr   Ò   sr   € Ø
�'‰'�(Ó
×
!Ð
!˜r€CØ—w‘w˜wÓ'¨¯©°¸aÓ)@ˆ9Ø‚}ÙØ�„zØ" aœˆvÐ2¨UÐ2ß%.ˆiˆ[˜˜%˜Ð!ÐD°u°g¸VÐ4DÐDr   c                óF   € V ^8„  d   QhR\         R\        \        ,          /# )r   Úitemsr   )r   r\   r   )r   s   "r   r   r   Ü   s   € ÷ =ñ =œ3ð =¤4¬¥9ñ =r   c                óŽ   € V f   . # \        V \        4      '       d   V .p R V  4        Uu. uF  q'       g   K  VNK  	  up# u upi )zMStripped, non-empty ``str(item)`` values from a str-or-iterable (order kept).c              3   óT   "  € T F  p\        V4      P                  4       x € K   	  R # 5i©N)r   rT   )Ú.0Úis   & r   Ú	<genexpr>Ú"_clean_str_list.<locals>.<genexpr>â   s   é € Ð6±¨1œ˜A›Ÿ™Ÿ˜³ùs   ‚&()r   r   )r�   Úss   & r   Ú_clean_str_listrŠ   Ü   sC   € à‚}Øˆ	Ü�%œ×ÒØ�ˆÙ6±Ô6Ó<Ñ6�!»!�AˆAÑ6Ñ<Ð<ùÒ<s
   «	A¹Ac                ó~   € V ^8„  d   QhR\         \        ,          R\         \        ,          R\        \        ,          /# )r   ÚskillÚskillsr   )r   r   r   r\   )r   s   "r   r   r   å   s5   € ÷ Oñ OœX¤c�]ð O¼8ÄC½=ð OÔTXÔY\ÕT]ñ Or   c                 ó´   € Vf   V '       d   V .M. pM\        V\        4      '       d   V.p\        \        P	                  \        R V 4       4      4      4      # )Nc              3   ó8   "  € T F  q;'       g    R x € K  	  R# 5i)rM   N© )r…   Úitems   & r   r‡   Ú$_canonical_skills.<locals>.<genexpr>ë   s   é € Ð-LÁV¸T¯j¨j°b¬jÃVùs   ‚�)r   r   r\   r   ÚfromkeysrŠ   )rŒ   r�   s   &&r   Ú_canonical_skillsr”   å   sE   € Ø‚~ß!�%‘ r‰Ü	�FœC×	 Ò	 Ø�ˆä”—‘œoÑ-LÁVÓ-LÓLÓMÓNÐNr   Ústrip_trailing_slashFc                óh   € V ^8„  d   QhR\         \        ,          R\        R\         \        ,          /# )r   rr   r•   r   )r   r   r   r   )r   s   "r   r   r   î   s-   € ÷ ñ ¬´#­ð ÔQUð ÔbjÔknÕboñ r   c                óŠ   € V f   R # \        V 4      P                  4       pV'       d   VP                  R4      pT;'       g    R # )Nr|   )r   rT   Úrstrip)rr   r•   Útexts   &$ r   Ú_normalize_optional_job_valuerš   î   s9   € Ø‚}ÙÜˆu‹:×ÑÓ€DßØ�{‰{˜3ÓˆØ�<ˆ<�4Ðr   c                óF   € V ^8„  d   QhR\         R\        \        ,          /# )r   rr   r   )r   r   r   )r   s   "r   r   r   ÷   s   € ÷ &ñ &¤Cð &¬H´S­Mñ &r   c                óÔ   € V f   R# \        V \        \        34      '       d%   RP                  \	        V 4      4      ;'       g    R# \        V 4      P                  4       ;'       g    R# )z©Canonical string form of ``deliver``; None for None/empty. MCP clients may pass a list
(``["telegram"]``) which the scheduler's ``str(deliver).split(",")`` would mangle.Nrb   )r   r\   rp   ÚjoinrŠ   r   rT   )rr   s   &r   Ú_normalize_deliver_paramrž   ÷   sT   € ð ‚}ÙÜ�%œ$¤˜×'Ò'Ø�x‰xœ¨Ó.Ó/×7Ð7°4Ð7Üˆu‹:×ÑÓ×%Ð% Ð%r   c                ó\   € V ^8„  d   QhR\         \        ,          R\         \        ,          /# ©r   Údeliverr   ©r   r   )r   s   "r   r   r     s"   € ÷ ñ ¬´­ð ¼(Ä3½-ñ r   c                óN  € V '       g   R#  ^ RI Hp ^ RIHpHp \        T 4      P                  R4       FB  pT! TP                  4       4      pT'       g   K#   T! T4      pT! T4      '       d   K<  RT R2u # 	  R#   \
         d     R# i ; i  \
         d    RT R2u u # i ; i)	zÿValidate ``bot-chat[:<profile>]`` deliver elements at create time: Bot Chat delivery is
machine-local, so the profile must exist where the scheduler fires (Desktop rosters may
show same-named profiles from other machines). Returns an error string or None.N)Úparse_bot_chat_deliver_token)Únormalize_profile_nameÚprofile_existsrb   zinvalid bot-chat profile name 'Ú'zbot-chat delivery profile 'uÄ   ' not found on this gateway's machine. Bot Chat delivery is machine-local â€” use a profile that exists here (hermes profile list), or omit the name (deliver='bot-chat') for the job's own profile.)	Úcron.scheduler_deliveryr¤   Úhermes_cli.profilesr¥   r¦   r    r   rf   rT   )r¡   r¤   r¥   r¦   ÚpartÚprofile_argÚcanons   &      r   Ú_validate_bot_chat_deliverr­     s¶   € ÷ ÙðÝHßNô �G“×"Ñ" 3Ö'ˆÙ2°4·:±:³<Ó@ˆßÙð	DÙ*¨;Ó7ˆEñ ˜e×$Ô$à-¨k¨]ð ;Gð GòHñ (ñ øô! ô Úðûô ô 	DØ4°[°MÀÐCÔCð	Dús#   ŒA< ÁBÁ<BÂ
BÂB$Â#B$c                ó\   € V ^8„  d   QhR\         \        ,          R\         \        ,          /# r    r¢   )r   s   "r   r   r     s"   € ÷ 5ñ 5¬8´C­=ð 5¼XÄc½]ñ 5r   c                ór  a€ ^ RI Ho ^ RIHp V! S! RR4      4      '       g   V # R V3R llpV f   V! 4       # \	        \        V 4      P                  R4      4       Uu. uF   q3P                  4       R	8X  d   V! 4       MTNK"  	  ppRP                  \        P                  V4      4      ;'       g    R# u upi )
a®  Resolve ``origin`` to a concrete target for creates made FROM a cron run (the creating
session is ephemeral, so by fire time there is no origin). Non-cron sessions: unchanged.
Cron sessions: ``origin`` (or omitted) becomes the creating run's ``platform:chat_id[:thread]``
from HERMES_CRON_AUTO_DELIVER_*, or ``local`` when it has no concrete target; other
elements pass through. Otherwise the scheduler would guess a home channel.)r2   )Úis_truthy_valueÚHERMES_CRON_SESSIONrM   c                ó$   € V ^8„  d   QhR\         /# )r   r   ©r   )r   s   "r   r   Ú3_resolve_cron_context_deliver.<locals>.__annotate__(  s   € ÷ ]ñ ]œSñ ]r   c                  óä   <€ S! R R4      P                  4       p S! RR4      P                  4       pV '       d	   V'       g   R# S! RR4      P                  4       pV'       d   V  RV RV 2# V  RV 2# )Ú!HERMES_CRON_AUTO_DELIVER_PLATFORMrM   Ú HERMES_CRON_AUTO_DELIVER_CHAT_IDrN   Ú"HERMES_CRON_AUTO_DELIVER_THREAD_IDrR   )rT   )r8   r9   r<   r2   s      €r   Ú_creator_targetÚ6_resolve_cron_context_deliver.<locals>._creator_target(  sy   ø€ Ù"Ð#FÈÓK×QÑQÓSˆÙ!Ð"DÀbÓI×OÑOÓQˆßŸwÙÙ#Ð$HÈ"ÓM×SÑSÓUˆ	ß6?�(�˜1˜W˜I Q y kÐ2Ð\ÈÀzÐQRÐSZÐR[ÐE\Ð\r   Nrb   rS   )rA   r2   Úutilsr°   rŠ   r   rf   rU   r�   r   r“   )r¡   r°   r¹   ÚpÚresolvedr2   s   &    @r   Ú_resolve_cron_context_deliverr¾     s¦   ø€ õ 8Ý%Ù™?Ð+@À"ÓE×FÒFØˆ÷]ð ]ð ‚ÙÓ Ð ÜKZÔ[^Ð_fÓ[g×[mÑ[mÐnqÓ[rÔKsÓtÑKsÀa§W¡W£Y°(Ô%:‘Ô!ÀÒAÑKs€HÐtà�8‰8”D—M‘M (Ó+Ó,×4Ð4°Ð4ùò us   Á&B4c                ó<   € V ^8„  d   QhR\         R\         R\        /# )r   Úbase_urlÚ
configuredr   )r   r   )r   s   "r   r   r   7  s!   € ÷ 	)ñ 	)œ3ð 	)¬Cð 	)´Dñ 	)r   c                óŽ   € ^ RI Hp  V! V4      V! V 4      rC\        T^,          4      ;'       d    YC8H  #   \         d     R# i ; i)zíOrigin (scheme, host, effective port) equality, as for any bearer secret moved to a
new URL: a host-only or subdomain match would let a job send the stored key over plain
HTTP, to another port, or to a host the operator never configured.©Úbase_url_originF)r»   rÄ   r$   r   )rÀ   rÁ   rÄ   ÚwantÚgots   &&   r   Ú_same_originrÇ   7  sK   € õ &ðÙ# JÓ/±ÀÓ1Jˆcô ��Q•‹=×(Ð(˜S™[Ð(øô ô Úðús   ˆ5 µAÁAc                óH   € V ^8„  d   QhR\         R\         R\         R\         /# )r   ÚbuÚprovÚwhyr   r³   )r   s   "r   r   r   C  s.   € ÷ Jñ Jœ#ð J¤Sð J¬sð J´sñ Jr   c                ó   € RV : RV: RV 2# )zQThe one refusal wording for a base_url override; ``why`` names the endpoint rule.z	base_url z is not allowed for provider z. r�   )rÉ   rÊ   rË   s   &&&r   Ú_base_url_refusedrÍ   C  s   € à�r‘fÐ9¸$¹ÀÀCÀ5ÐIÐIr   c                óF   € V ^8„  d   QhR\         R\        \         ,          /# )r   rÉ   r   )r   r   )r   s   "r   r   r   H  s   € ÷ !7ñ !7¤ð !7¬´#­ñ !7r   c                ó   a a	€ ^ RI Hp ^ RIHo	 ^ RIHp ^ RIHp ^ RIH	p Rp V! S 4       \        ;QJ d0    T	3R	 lS	P                  S R
R7       4       F  '       g   K   R
M%	  RM!! T	3R	 lS	P                  S R
R7       4       4      '       g   R# S	P                  4       pTP                  R4      p\!        T\"        4      '       d   TP                  R4      MRT! RR4      T.R TP%                  4        4       OR S	P'                  T4       4       Op\        ;QJ d    T 3R lT 4       F  '       g   K   R
M	  RM! T 3R lT 4       4      '       d   R# \        S RT4      #   \         d    \        S RR4      u # i ; i)a,  Bare 'custom' is BYOK only while the runtime attaches no stored key. The resolver picks
the host-gated env keys by HOSTNAME, so a base_url that would receive one must be an origin
the operator or the provider registry names; pool and ``model.key_env`` keys already match
their configured URL exactly.)Úget_secret_str)Úruntime_provider©ÚPROVIDER_REGISTRY)ÚOPENROUTER_BASE_URLrÃ   z½A stored API key matches its hostname, and a stored credential may only be sent to a configured endpoint (same scheme, host and port); configure the endpoint as a custom provider to use it.ÚcustomzIt is not a valid URL.c              3   óF   <"  € T F  pSP                  V4      x € K  	  R # 5ir„   )Úhas_usable_secret)r…   ÚkeyÚrps   & €r   r‡   Ú+_custom_stored_key_error.<locals>.<genexpr>[  s!   øé € ÐgÑ4f¨Sˆr×#Ñ# C×(Ð(Ó4fùs   ƒ!T)ÚollamaFNÚmodelrÀ   ÚOPENAI_BASE_URLrM   c              3   ó<   "  € T F  p\        VR R4      x € K  	  R# 5i)Úinference_base_urlrM   N)Úgetattr)r…   r¼   s   & r   r‡   rÚ   d  s   é € Ð	SÑ8R°1Œ'�!Ð)¨2×
.Ð
.Ó8Rùs   ‚c              3   óB   "  € T F  qP                  R 4      x € K  	  R# 5i)rÀ   Nr}   )r…   Úentrys   & r   r‡   rÚ   e  s   é € Ð	UÑ-T E�)‰)�J×
Ð
Ó-Tùs   ‚c              3   ó`   <"  € T F#  q'       g   K  \        S\        V4      4      x € K%  	  R # 5ir„   )rÇ   r   )r…   ÚurlrÉ   s   & €r   r‡   rÚ   g  s$   øé € Ð
C±¨#»sÔ%Œ<˜œC ›H×%Ð%³ùs   ƒ	.‘.)Úagent.secret_scoperÐ   Ú
hermes_clirÑ   Úhermes_cli.authrÓ   Úhermes_constantsrÔ   r»   rÄ   r$   rÍ   ÚanyÚ_host_gated_env_key_candidatesÚload_configr"   r   r   ÚvaluesÚget_compatible_custom_providers)
rÉ   rÐ   rÓ   rÔ   rÄ   Ú
custom_whyÚcfgÚ	model_cfgrÁ   rÙ   s
   f        @r   Ú_custom_stored_key_errorrñ   H  sM  ù€ õ
 2Ý1Ý1Ý4Ý%ð	ð ðIÙ˜Ô÷ ‹3Ôg°B×4UÑ4UÐVXÐaeÐ4UÔ4fÓg�3�3Š3Ôg°B×4UÑ4UÐVXÐaeÐ4UÔ4fÓg×gÒgÙà
�.‰.Ó
€CØ—‘˜Ó €Iä%/°	¼4×%@Ò%@ˆ	�‰�jÔ!ÀdÙÐ(¨"Ó-Ð/Bðñ 
TÐ8I×8PÑ8PÔ8RÓ	Sð	ñ
 
V¨R×-OÑ-OÐPSÔ-TÓ	Uð€J÷ ƒsÔ
C±Ó
C‡s‡s‚sÔ
C±Ó
C×CÒCÙÜ˜R ¨:Ó6Ð6øô! ô IÜ   XÐ/GÓHÒHðIús   ¤E" Å"E=Å<E=c                ó~   € V ^8„  d   QhR\         \        ,          R\         \        ,          R\         \        ,          /# )r   ÚproviderrÀ   r   )r   r   r   )r   s   "r   r   r   l  s0   € ÷ 1"ñ 1"Ü”s�mð1"Ü'/´¥}ð1"Ü9AÄ#½ñ1"r   c                ó6  € \        VRR7      pV'       g   R# \        V 4      pV'       g    R#  ^ RIHpHpHp ^ RIHp TP                  4       R	8X  d   \        T4      # T! T4      '       dm    T! T4      p\        T;'       g    / P                  R
4      ;'       g    R4      p	T	'       d   \        Y)4      '       d   R# \        Y#RT	;'       g    R R24      #  T! T4      p
\        T
\        4      '       d   TP                  T
4      MRpT'       d!   \        \        TRR4      ;'       g    R4      MRpT'       d   \        Y,4      '       d   R# \        Y#R4      #   \         d    RT: R2u # i ; i  \         d    Rp ELi ; i  \         d    Tp
 Lµi ; i)ad  Reject pairing a stored credential with an off-origin base_url (a prompt-injected job
could exfil the key). Allowed: no override; bare 'custom' while no stored key would go with
it, or at a configured origin; an override with the same origin as the named provider's
own endpoint. Everything else fails closed. Returns an error string if blocked, else None.T)r•   Nzwbase_url override requires an explicit provider. Set provider to a configured custom provider to use a custom endpoint.)Úhas_named_custom_providerÚresolve_requested_providerÚ_get_named_custom_providerrÒ   z2Unable to validate base_url override for provider z
; refused.rÕ   rÀ   rM   z]A named custom provider's stored credential may only be sent to its own configured endpoint (Úunknownz): same scheme, host and port.rß   z³A named provider's stored credential may only be sent to its own endpoint (same scheme, host and port); use a configured custom provider (provider="custom") for a custom base_url.)rš   Úhermes_cli.runtime_providerrõ   rö   r÷   rç   rÓ   r    rU   rñ   r   r"   rÇ   rÍ   r   rà   )ró   rÀ   rÉ   rÊ   rõ   rö   r÷   rÓ   ÚcpÚcfg_urlr½   ÚpconfigÚ	known_urls   &&           r   Ú_validate_cron_base_urlrþ   l  s�  € ô 
' xÀdÔ	K€BßÙÜ(¨Ó2€DßðCñ	DðW÷	(ñ 	(õ 	6ð ‡z�zƒ|�xÔÜ'¨Ó+Ð+Ù  ×&Ò&ð	Ù+¨DÓ1ˆBô �r—x�x˜R—n‘n ZÓ0×6Ð6°BÓ7ˆß”| B×0Ò0ÙÜ Øð $Ø$+×$8Ð$8¨yÐ#9Ð9WðYóZð 	ZðÙ-¨dÓ3ˆô 2<¸HÄc×1JÒ1JÐ×#Ñ# HÔ-ÐPT€GßIP””G˜GÐ%9¸2Ó>×DÐDÀ"ÔEÐVX€Iß”\ "×0Ò0ÙäØ
ð !ó"ð "øô5 ô WØCÀDÁ8È:ÐVÒVðWûô ô 	Ø‹Bð	ûô ô ØŠðús5   ¯E Á.E4 ÃF ÅE1Å0E1Å4FÆFÆFÆFc                ó\   € V ^8„  d   QhR\         \        ,          R\         \        ,          /# )r   Úscriptr   r¢   )r   s   "r   r   r      s"   € ÷ ñ ¤x´¥}ð ¼Ä#½ñ r   c                óº  € V '       d   V P                  4       '       g   R# ^ RIHp V P                  4       pV! 4       R,          pVP                  R4      '       g   \	        V4      ^8¼  d   V^,          R8X  d   RV RV: RV R2# ^ R	IHp VP                  R
R
R7       W2,          pV! WS4      '       d   RV: 2# VP                  4       '       g
   RV RV R2# R# )u°   Scripts must be relative paths within HERMES_HOME/scripts/ (absolute / ~ / drive-letter
rejected â€” prompt-injection guard). Error string if blocked, else None; empty = clear.N)Úget_hermes_homeÚscriptsrR   z Script path must be relative to z'/. Got absolute or home-relative path: z. Place scripts in z/ and use just the filename.)Úvalidate_within_dirT)ÚparentsÚexist_okz9Script path escapes the scripts directory via traversal: zScript file not found: z. Create it in z/ first.)r|   Ú~)	rT   rè   r  rg   rh   Útools.path_securityr  ÚmkdirÚis_file)r   r  ÚrawÚscripts_dirr  Úresolved_scripts   &     r   Ú_validate_cron_script_pathr     sæ   € ÷ ˜Ÿ™ŸšÙå0Ø
�,‰,‹.€CÙ!Ó# iÕ/€KØ
‡~�~�j×!Ò!¤c¨#£h°!¤m¸¸A½À#¼à.¨{¨mð <3Ø36±'ð : Ø +˜}Ð,HðJð	Kõ
 8Ø×Ñ˜d¨TÐÔ2Ø!Õ'€OÙ˜?×8Ò8ØJÈ3É'ÐRÐRØ×"Ñ"×$Ò$à% oÐ%6ð 7Ø'˜=¨ð2ð	3ñ r   c                óª   € V ^8„  d   QhR\         \        \        \        ,          ,          ,          R\        R\         \        \        ,          ,          /# )r   Úcontext_fromÚ
continuityr   )r   r   r\   r   )r   s   "r   r   r   »  s:   € ÷ 
ñ 
Üœ3¤¤c¥�?Õ+ð
äð
ä!¤$¤s¥)Õ,ñ
r   c                óT  € \        V 4      p\        ;QJ d    R V 4       F  '       g   K   RM	  RM! R V 4       4      pV'       d   V'       g   VP                  R4       M7V'       g0   V'       d(   V Uu. uF  qDP                  4       R8w  g   K  VNK  	  ppT;'       g    R# u upi )zVcontinuity=True ensures "self" is in context_from; False removes it; others untouched.c              3   óF   "  € T F  qP                  4       R 8H  x € K  	  R# 5i)ÚselfN)rU   )r…   Úrs   & r   r‡   Ú$_apply_continuity.<locals>.<genexpr>À  s   é € Ð5±¨1—7‘7“9 Ö&³ùs   ‚!TFr  N)rŠ   ré   re   rU   )r  r  ÚrefsÚhas_selfr  s   &&   r   Ú_apply_continuityr  »  sv   € ô ˜<Ó(€Dß‹sÑ5±Ó5�s�sŠsÑ5±Ó5Ó5€HßŸ(Ø�‰�FÕßŸHÙÓ7™4�a§7¡7£9°Ñ#6—�™4ˆÐ7Ø�<ˆ<�4Ðùò 8s   Á6B%ÂB%c                ó\   € V ^8„  d   QhR\         \        ,          R\        \        ,          /# )r   r  r   )r\   r   r   r   )r   s   "r   r   r   È  s"   € ÷ ñ ¤d¬3¥ið ´H¼SµMñ r   c                óÆ   € ^ RI Hp V  FT  p\        V\        4      '       d&   VP	                  4       P                  4       R8X  d   K>  V! V4      '       d   KN  RV R2u # 	  R# )u©   Error string if any non-"self" ref names a missing job ("self" resolves to the job's
own id at run time, so it can't be checked â€” the job doesn't exist yet at create).)Úget_jobr  zcontext_from job 'z>' not found. Use cronjob(action='list') to see available jobs.N)r!   r  r   r   rT   rU   )r  Ú_get_jobÚref_ids   &  r   Ú_validate_context_from_refsr  È  s`   € õ .ÛˆÜ�fœc×"Ò" v§|¡|£~×';Ñ';Ó'=ÀÔ'GÙÙ˜×Ôà$ V Hð -Dð DòEñ	 ñ r   c                ót   € V ^8„  d   QhR\         \        \        3,          R\         \        \        3,          /# ru   rv   )r   s   "r   r   r   Ü  s*   € ÷ 2ñ 2”Tœ#œs˜(•^ð 2¬¬S´#¨X­ñ 2r   c                 ó�  a a€ ^ RI Hp \        S P                  R4      ;'       g    R4      p\	        S P                  R4      S P                  R4      4      p\        S P                  R4      ;'       g    R4      p\        S P                  R4      ;'       g9    VR	,          ;'       g(    V'       d
   V^ ,          MR;'       g    T;'       g    R
4      p/ RVbRVbRV'       d
   V^ ,          MRbRVbR\        V4      ^d8”  d   VR,          R,           MTbRS P                  R4      bRS P                  R4      bR\        \        S P                  R4      ;'       g    R4      P                  4       4      bRS P                  R4      bRS P                  R4      ;'       g    RbR\        S 4      bRS P                  RR4      bRS P                  R4      bRS P                  R4      bRS P                  R4      bRS P                  R4      bRS P                  R4      bRS P                  R4      R S P                  R 4      '       d   V! S R ,          R!R!R"7      MS P                  R 4      R#S P                  R#R!4      R$\        S 4      R%S P                  R%4      R&S P                  R&4      /Cp\         F0  pS P                  V4      '       g   K  VR'8X  d   R!MS V,          Wg&   K2  	  S P                  R(4      ;'       g    . p\        V\        4      '       d   V.pV 3R) lo\        ;QJ d    V3R* lV 4       F  '       g   K   R!M	  R+M! V3R* lV 4       4      '       d   R!VR,&   V U	u. uF  p	S! V	4      '       d   K  V	NK  	  p
p	V
'       d   W¦R(&   \        S P                  R-4      \        4      '       d   S R-,          VR-&   V# u up	i ).r/   )Úredact_sensitive_textÚpromptrM   rŒ   r�   Úidrø   Úname:Né2   Nzcron jobÚjob_idNÚprompt_preview:Néd   Nz...rÜ   ró   ÚpinnedrÀ   r   Úschedule_displayÚ?rx   r¡   rN   Únext_run_atÚlast_run_atÚlast_statusÚlast_delivery_errorÚlast_delivery_unverifiedÚlast_fire_errorÚ
last_errorT)ÚforceÚredact_url_credentialsÚenabledÚstateÚ	paused_atÚpaused_reasonr`   r  c                 ó�   <€ \        V 4      P                  4       P                  4       R 8H  ;'       g    V SP                  R4      8H  # )r  r$  )r   rT   rU   r"   )r  rJ   s   &€r   Ú<lambda>Ú_format_job.<locals>.<lambda>  s5   ø€ œ˜A›Ÿ™›×,Ñ,Ó.°&Ñ8×NÐN¸AÀÇÁÈÃÑ<NÐNr   c              3   ó4   <"  € T F  pS! V4      x € K  	  R # 5ir„   r�   )r…   r  Úis_selfs   & €r   r‡   Ú_format_job.<locals>.<genexpr>  s   øé € Ð
+™{˜!‰7�1�:ˆ:›{ùs   ƒFr  Úattach_to_session)Úagent.redactr"  r   r"   r”   rh   r   rT   r   r	   Ú_FORMAT_JOB_OPTIONAL_KEYSr   ré   )rJ   r"  r#  r�   r'  r%  ÚresultrØ   Ústored_refsr  Úexternal_refsr>  s   f          @r   Ú_format_jobrF  Ü  sy  ù€ Ý2ä�—‘˜Ó"×(Ð( bÓ)€FÜ˜sŸw™w wÓ/°·±¸Ó1BÓC€FÜ�—‘˜“×+Ð+ )Ó,€FÜˆs�w‰w�v‹×fÐf &¨¥+×fÐf¿v°&¸¶)È2×fÐfÐRX×fÐfÐ\fÓg€DðØ�&ðà�ðð 	Ÿf�˜–¨$ðð 	�&ð	ð
 	´#°f³+ÀÔ2C˜& �,¨Ö.Èðð 	�—‘˜Ó!ðð 	�C—G‘G˜JÓ'ðð 	”$”s˜3Ÿ7™7 7Ó+×1Ð1¨rÓ2×8Ñ8Ó:Ó;ðð 	�C—G‘G˜JÓ'ðð 	�C—G‘GÐ.Ó/×6Ð6°3ðð 	”/ #Ó&ðð 	�3—7‘7˜9 gÓ.ðð 	�s—w‘w˜}Ó-ðð 	�s—w‘w˜}Ó-ðð  	�s—w‘w˜}Ó-ð!ð" 	˜sŸw™wÐ'<Ó=ð#ð$ 	# C§G¡GÐ,FÓ$Gð%ð& 	˜3Ÿ7™7Ð#4Ó5Øà�W‰W�\×"Ò"ñ ,Ø�Õ TÀ$õ
à(+¯©°Ó(=Ø�3—7‘7˜9 dÓ+àÔ$ SÓ)Ø�S—W‘W˜[Ó)Ø˜Ÿ™ Ó1ñ7€F÷: )ˆØ�7‰7�3�<Œ<Ø"%¨Ô"3™$¸¸S½ˆF‹Kñ )ð —'‘'˜.Ó)×/Ð/¨R€KÜ�+œs×#Ò#Ø"�mˆÜN€Gß
ƒsÔ
+™{Ó
+‡s‡s‚sÔ
+™{Ó
+×+Ò+Ø#ˆˆ|ÑÙ +Ó>¡˜1±7¸1·:—Q�Q¡€MÐ>ßØ!.ˆ~ÑÜ�#—'‘'Ð-Ó.´×5Ò5Ø&)Ð*=Õ&>ˆÐ"Ñ#Ø€Mùò ?s   Í'OÍ<Oc                ó0   € V ^8„  d   QhR\         R\        /# )r   Úpluralr   )r   r   )r   s   "r   r   r     s   € ÷ >ñ >¤Tð >´dñ >r   c                ó–   €  ^ RI Hp V! 4       pTRJ d   T '       d   RMRpRRRRT R	2/# RTf   R/# R
/#   \         d    RR/u # i ; i)a©  ``gateway_running``/``warning`` payload via the shared CLI helper so CLI and tool agree
on "scheduler active". False -> warning (no gateway process), None -> probe failed.

Thin adapter over the shared CLI helper ``hermes_cli.cron._builtin_gateway_liveness`` (#87033) so the
CLI and this tool can never disagree about what "scheduler active" means. ``plural`` rewords the warning
for multi-job results (the ``list`` action).
)Ú_builtin_gateway_livenessÚgateway_runningNFzthese jobs are savedzthis job is savedÚwarningu&   The Hermes gateway is not running â€” z˜ but will NOT fire until the gateway is started (hermes gateway install / hermes gateway start). Tell the user the task is scheduled but not active yet.T)Úhermes_cli.cronrJ  r    )rH  rJ  Ú_gwÚsubjects   &   r   Ú_gateway_liveness_noticerP    s�   € ð)Ý=Ù'Ó)ˆð ˆeƒ|ß,2Ñ(Ð8Kˆà˜uØØ8¸¸	ð BJð Jð
ð 	
ð  s¢{˜tÐ=Ð=¸Ð=Ð=øô ô )Ø! 4Ð(Ò(ð)ús   ‚6 ¶AÁAr„   )NN)	r   Úreasoning_effortr^   r_   Úmonitor_stater`   Úenabled_toolsetsÚworkdirÚinterpreter)F)'Ú__doc__r   Úloggingr   r   Útypingr   r   r   r   r   r!   r	   r%   Ú	getLoggerrB   r(   r,   rG   rZ   rm   rs   r   rŠ   r”   rš   rž   r­   r¾   rÇ   rÍ   rñ   rþ   r  r  r  rB  rF  rP  r�   r   r   Ú<module>rZ     sÇ   ðñó Û ß (ß 3Õ 3å )ã ð 
×	Ò	Ð0Ó	1€ð
 Ð õ"O÷J0õf#SõL(õVõ&Eõ=÷OðÐX]÷ õ&õõ85õ4	)õJõ
!7õH1"õhõ6
õðOÐ õ
2÷j>ñ >r   