§
    ‰eîiyx  ã            #       óF  — 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	m	Z	m
Z
 ddlmZ ddlmZ ddlmZmZmZmZmZ  ej        e¦  «        ZddlmZ 	 ddlmZ d	Zn# e$ r d
ZY nw xY w e¦   «                              ¦   «         Zedz  Z e dz  Z! ej"        ¦   «         Z#e dz  Z$dZ%dHdee&         dee         dee&         fd„Z'dee&ef         dee&ef         fd„Z(defd„Z)defd„Z*d„ Z+de&de,fd„Z-de&dee&ef         fd„Z.de	de	fd„Z/ddœdee&ef         d e	d!ee&         dee&         fd"„Z0de1de,fd#„Z2dIdee&ef         d!ee&         dee&         fd$„Z3deee&ef                  fd%„Z4d&eee&ef                  fd'„Z5d(ee&         dee&         fd)„Z6	 	 	 	 	 	 	 	 	 	 	 	 	 dJd*e&de&d+ee&         d,ee,         d-ee&         d.eee&ef                  dee&         deee&                  d/ee&         d0ee&         d1ee&         d2ee&         d3eee&ee&         f                  d4eee&                  d(ee&         dee&ef         f d5„Z7d6e&deee&ef                  fd7„Z8dKd8e9deee&ef                  fd9„Z:d6e&d:ee&ef         deee&ef                  fd;„Z;dId6e&d<ee&         deee&ef                  fd=„Z<d6e&deee&ef                  fd>„Z=d6e&deee&ef                  fd?„Z>d6e&de9fd@„Z?	 	 dHd6e&dAe9dBee&         dCee&         fdD„Z@d6e&de9fdE„ZAdeee&ef                  fdF„ZBd6e&de&fdG„ZCdS )Lz�
Cron job storage and management.

Jobs are stored in ~/.hermes/cron/jobs.json
Output is saved to ~/.hermes/cron/output/{job_id}/{timestamp}.md
é    N)ÚdatetimeÚ	timedelta)ÚPath)Úget_hermes_home)ÚOptionalÚDictÚListÚAnyÚUnion)Únow)ÚcroniterTFÚcronz	jobs.jsonÚoutputéx   ÚskillÚskillsÚreturnc                 óô   — |€| r| gng }n(t          |t          ¦  «        r|g}nt          |¦  «        }g }|D ]@}t          |pd¦  «                             ¦   «         }|r||vr|                     |¦  «         ŒA|S )zPNormalize legacy/single-skill and multi-skill inputs into a unique ordered list.NÚ )Ú
isinstanceÚstrÚlistÚstripÚappend)r   r   Ú	raw_itemsÚ
normalizedÚitemÚtexts         ú+/home/div/.hermes/hermes-agent/cron/jobs.pyÚ_normalize_skill_listr    /   s›   € à€~Ø$Ð,�U�G�G¨"ˆ	ˆ	Ý	�F�CÑ	 Ô	 ð !Ø�Hˆ	ˆ	å˜‘L”Lˆ	à€JØð $ð $ˆÝ�4�:˜2‰Œ×$Ò$Ñ&Ô&ˆØð 	$�D 
Ð*Ð*Ø×Ò˜dÑ#Ô#Ð#øØÐó    Újobc                 ó¸   — t          | ¦  «        }t          |                     d¦  «        |                     d¦  «        ¦  «        }||d<   |r|d         nd|d<   |S )zLReturn a job dict with canonical `skills` and legacy `skill` fields aligned.r   r   r   N)Údictr    Úget)r"   r   r   s      r   Ú_apply_skill_fieldsr&   @   s[   € å�c‘”€JÝ" :§>¢>°'Ñ#:Ô#:¸J¿NºNÈ8Ñ<TÔ<TÑUÔU€FØ!€JˆxÑØ'-Ð7˜& œ)˜)°4€JˆwÑØÐr!   Úpathc                 ób   — 	 t          j        | d¦  «         dS # t          t          f$ r Y dS w xY w)z<Set directory to owner-only access (0700). No-op on Windows.iÀ  N)ÚosÚchmodÚOSErrorÚNotImplementedError©r'   s    r   Ú_secure_dirr.   I   sG   € ðÝ
Œ��uÑÔÐÐÐøÝÕ(Ð)ð ð ð Øˆˆðøøøs   ‚ ™.­.c                 óŽ   — 	 |                       ¦   «         rt          j        | d¦  «         dS dS # t          t          f$ r Y dS w xY w)z;Set file to owner-only read/write (0600). No-op on Windows.i€  N)Úexistsr)   r*   r+   r,   r-   s    r   Ú_secure_filer1   Q   sa   € ðØ�;Š;‰=Œ=ð 	"ÝŒH�T˜5Ñ!Ô!Ð!Ð!Ð!ð	"ð 	"øåÕ(Ð)ð ð ð Øˆˆðøøøs   ‚)/ ¯AÁAc                  óÆ   — t                                dd¬¦  «         t                               dd¬¦  «         t          t           ¦  «         t          t          ¦  «         dS )z6Ensure cron directories exist with secure permissions.T©ÚparentsÚexist_okN)ÚCRON_DIRÚmkdirÚ
OUTPUT_DIRr.   © r!   r   Úensure_dirsr:   Z   sS   € å‡N‚N˜4¨$€NÑ/Ô/Ð/Ý×Ò˜T¨DÐÑ1Ô1Ð1Ý•ÑÔÐÝ•
ÑÔÐÐÐr!   Úsc                 ó>  — |                       ¦   «                              ¦   «         } t          j        d| ¦  «        }|st	          d| › d�¦  «        ‚t          |                     d¦  «        ¦  «        }|                     d¦  «        d         }dddd	œ}|||         z  S )
u€   
    Parse duration string into minutes.
    
    Examples:
        "30m" â†’ 30
        "2h" â†’ 120
        "1d" â†’ 1440
    zD^(\d+)\s*(m|min|mins|minute|minutes|h|hr|hrs|hour|hours|d|day|days)$zInvalid duration: 'z''. Use format like '30m', '2h', or '1d'é   é   r   é<   i   )ÚmÚhÚd)r   ÚlowerÚreÚmatchÚ
ValueErrorÚintÚgroup)r;   rE   ÚvalueÚunitÚmultiplierss        r   Úparse_durationrL   f   s›   € ð 	
�Š‰	Œ	�ŠÑÔ€AÝŒHÐ\Ð^_Ñ`Ô`€EØð [ÝÐY¨qÐYÐYÐYÑZÔZÐZå�—’˜A‘”ÑÔ€EØ�;Š;�q‰>Œ>˜!Ô€Dà ¨Ð.Ð.€KØ�;˜tÔ$Ñ$Ð$r!   Úschedulec                 ó`  — |                       ¦   «         } | }|                      ¦   «         }|                     d¦  «        r5| dd…                               ¦   «         }t          |¦  «        }d|d|› d�dœS |                      ¦   «         }t          |¦  «        dk    rut          d„ |dd…         D ¦   «         ¦  «        rTt          st          d	¦  «        ‚	 t          | ¦  «         n'# t          $ r}t          d
| › d|› �¦  «        ‚d}~ww xY wd| | dœS d| v st          j        d| ¦  «        r™	 t          j        |                      dd¦  «        ¦  «        }|j        €|                     ¦   «         }d|                     ¦   «         d|                     d¦  «        › �dœS # t          $ r}t          d| › d|› �¦  «        ‚d}~ww xY w	 t          | ¦  «        }t)          ¦   «         t+          |¬¦  «        z   }d|                     ¦   «         d|› �dœS # t          $ r Y nw xY wt          d|› d�¦  «        ‚)uM  
    Parse schedule string into structured format.
    
    Returns dict with:
        - kind: "once" | "interval" | "cron"
        - For "once": "run_at" (ISO timestamp)
        - For "interval": "minutes" (int)
        - For "cron": "expr" (cron expression)
    
    Examples:
        "30m"              â†’ once in 30 minutes
        "2h"               â†’ once in 2 hours
        "every 30m"        â†’ recurring every 30 minutes
        "every 2h"         â†’ recurring every 2 hours
        "0 9 * * *"        â†’ cron expression
        "2026-02-03T14:00" â†’ once at timestamp
    zevery é   NÚintervalr@   )ÚkindÚminutesÚdisplayé   c              3   ó@   K  — | ]}t          j        d |¦  «        V — ŒdS )z^[\d\*\-,/]+$N)rD   rE   )Ú.0Úps     r   ú	<genexpr>z!parse_schedule.<locals>.<genexpr>ž   s@   è è € ð ð Ø*+�ŒÐ! 1Ñ%Ô%ðð ð ð ð ð r!   zOCron expressions require 'croniter' package. Install with: pip install croniterzInvalid cron expression 'z': r   )rQ   ÚexprrS   ÚTz^\d{4}-\d{2}-\d{2}ÚZz+00:00Úoncezonce at z%Y-%m-%d %H:%M)rQ   Úrun_atrS   zInvalid timestamp '©rR   zonce in zInvalid schedule 'zÄ'. Use:
  - Duration: '30m', '2h', '1d' (one-shot)
  - Interval: 'every 30m', 'every 2h' (recurring)
  - Cron: '0 9 * * *' (cron expression)
  - Timestamp: '2026-02-03T14:00:00' (one-shot at time))r   rC   Ú
startswithrL   ÚsplitÚlenÚallÚHAS_CRONITERrF   r   Ú	ExceptionrD   rE   r   ÚfromisoformatÚreplaceÚtzinfoÚ
astimezoneÚ	isoformatÚstrftimeÚ_hermes_nowr   )	rM   ÚoriginalÚschedule_lowerÚduration_strrR   ÚpartsÚeÚdtr]   s	            r   Úparse_schedulerr   {   sà  € ð$ �~Š~ÑÔ€HØ€HØ—^’^Ñ%Ô%€Nð × Ò  Ñ*Ô*ð 
Ø   ”|×)Ò)Ñ+Ô+ˆÝ  Ñ.Ô.ˆàØØ* Ð*Ð*Ð*ð
ð 
ð 	
ð �NŠNÑÔ€EÝ
ˆ5�z„z�Q‚€�3ð ð Ø/4°R°a°R¬yðñ ô ñ ô €õ ð 	pÝÐnÑoÔoÐoð	KÝ�XÑÔÐÐøÝð 	Kð 	Kð 	KÝÐI¸ÐIÐIÀaÐIÐIÑJÔJÐJøøøøð	Køøøð ØØð
ð 
ð 	
ð ˆh€€�"œ(Ð#8¸(ÑCÔC€ð	EåÔ'¨×(8Ò(8¸¸hÑ(GÔ(GÑHÔHˆBð ŒyÐ Ø—]’]‘_”_�àØŸ,š,™.œ.ØE b§k¢kÐ2BÑ&CÔ&CÐEÐEðð ð øõ
 ð 	Eð 	Eð 	EÝÐC°8ÐCÐCÀÐCÐCÑDÔDÐDøøøøð	Eøøøð	Ý  Ñ*Ô*ˆÝ‘”¥°7Ð!;Ñ!;Ô!;Ñ;ˆàØ×&Ò&Ñ(Ô(Ø, (Ð,Ð,ð
ð 
ð 	
øõ
 ð ð ð Øˆðøøøõ ð	C˜Xð 	Cð 	Cð 	Cñô ð sD   ÃC$ Ã$
DÃ.DÄDÄ+A0F Æ
G Æ&F;Æ;G ÇAH È
HÈHrq   c                 ó  — t          ¦   «         j        }| j        €St          j        ¦   «                              ¦   «         j        }|                      |¬¦  «                             |¦  «        S |                      |¦  «        S )aÇ  Return a timezone-aware datetime in Hermes configured timezone.

    Backward compatibility:
    - Older stored timestamps may be naive.
    - Naive values are interpreted as *system-local wall time* (the timezone
      `datetime.now()` used when they were created), then converted to the
      configured Hermes timezone.

    This preserves relative ordering for legacy naive timestamps across
    timezone changes and avoids false not-due results.
    N)rg   )rk   rg   r   r   rh   rf   )rq   Ú	target_tzÚlocal_tzs      r   Ú_ensure_awarerv   Ô   sf   € õ ‘”Ô$€IØ	„yÐÝ”<‘>”>×,Ò,Ñ.Ô.Ô5ˆØ�zŠz ˆzÑ*Ô*×5Ò5°iÑ@Ô@Ð@Ø�=Š=˜Ñ#Ô#Ð#r!   ©Úlast_run_atr   rx   c                óô   — |                       d¦  «        dk    rdS |rdS |                       d¦  «        }|sdS t          t          j        |¦  «        ¦  «        }||t	          t
          ¬¦  «        z
  k    r|S dS )a  Return a one-shot run time if it is still eligible to fire.

    One-shot jobs get a small grace window so jobs created a few seconds after
    their requested minute still run on the next tick. Once a one-shot has
    already run, it is never eligible again.
    rQ   r\   Nr]   )Úseconds)r%   rv   r   re   r   ÚONESHOT_GRACE_SECONDS)rM   r   rx   r]   Ú	run_at_dts        r   Ú_recoverable_oneshot_run_atr}   ç   s‰   € ð ‡|‚|�FÑÔ˜vÒ%Ð%ØˆtØð Øˆtà�\Š\˜(Ñ#Ô#€FØð Øˆtå�hÔ4°VÑ<Ô<Ñ=Ô=€IØ�C�)Õ,AÐBÑBÔBÑBÒBÐBØˆØˆ4r!   c                 ó6  — d}d}|                       d¦  «        }|dk    r<|                       dd¦  «        dz  }|dz  }t          |t          ||¦  «        ¦  «        S |d	k    r·t          r°	 t	          ¦   «         }t          | d
         |¦  «        }|                     t          ¦  «        }|                     t          ¦  «        }	t          |	|z
   	                    ¦   «         ¦  «        }|dz  }t          |t          ||¦  «        ¦  «        S # t          $ r Y nw xY w|S )a(  Compute how late a job can be and still catch up instead of fast-forwarding.

    Uses half the schedule period, clamped between 120 seconds and 2 hours.
    This ensures daily jobs can catch up if missed by up to 2 hours,
    while frequent jobs (every 5-10 min) still fast-forward quickly.
    r   i   rQ   rP   rR   r=   r?   r>   r   rY   )r%   ÚmaxÚminrc   rk   r   Úget_nextr   rG   Útotal_secondsrd   )
rM   Ú	MIN_GRACEÚ	MAX_GRACErQ   Úperiod_secondsÚgracer   r   ÚfirstÚseconds
             r   Ú_compute_grace_secondsr‰     s   € ð €IØ€Ià�<Š<˜ÑÔ€DàˆzÒÐØ!Ÿš i°Ñ3Ô3°bÑ8ˆØ !Ñ#ˆÝ�9�c %¨Ñ3Ô3Ñ4Ô4Ð4àˆv‚~€~�,€~ð		Ý‘-”-ˆCÝ˜H VÔ,¨cÑ2Ô2ˆDØ—M’M¥(Ñ+Ô+ˆEØ—]’]¥8Ñ,Ô,ˆFÝ  &¨5¡.×!?Ò!?Ñ!AÔ!AÑBÔBˆNØ" aÑ'ˆEÝ�y¥# e¨YÑ"7Ô"7Ñ8Ô8Ð8øÝð 	ð 	ð 	ØˆDð	øøøð Ðs   Á*BD	 Ä	
DÄDc                 óô  — t          ¦   «         }| d         dk    rt          | ||¬¦  «        S | d         dk    rf| d         }|r5t          t          j        |¦  «        ¦  «        }|t          |¬¦  «        z   }n|t          |¬¦  «        z   }|                     ¦   «         S | d         dk    rMt          sdS t          | d	         |¦  «        }| 	                    t          ¦  «        }|                     ¦   «         S dS )
zo
    Compute the next run time for a schedule.

    Returns ISO timestamp string, or None if no more runs.
    rQ   r\   rw   rP   rR   r^   r   NrY   )
rk   r}   rv   r   re   r   ri   rc   r   r�   )rM   rx   r   rR   ÚlastÚnext_runr   s          r   Úcompute_next_runr�   "  s  € õ ‰-Œ-€Cà�Ô˜6Ò!Ð!Ý*¨8°SÀkÐRÑRÔRÐRà	�&Ô	˜ZÒ	'Ð	'Ø˜9Ô%ˆØð 	8å ¥Ô!7¸Ñ!DÔ!DÑEÔEˆDØ�i°Ð8Ñ8Ô8Ñ8ˆHˆHð �Y¨wÐ7Ñ7Ô7Ñ7ˆHØ×!Ò!Ñ#Ô#Ð#à	�&Ô	˜VÒ	#Ð	#Ýð 	Ø�4Ý˜ Ô(¨#Ñ.Ô.ˆØ—=’=¥Ñ*Ô*ˆØ×!Ò!Ñ#Ô#Ð#àˆ4r!   c                  óh  — t          ¦   «          t                               ¦   «         sg S 	 t          t          dd¬¦  «        5 } t	          j        | ¦  «        }|                     dg ¦  «        cddd¦  «         S # 1 swxY w Y   dS # t          j        $ rá 	 t          t          dd¬¦  «        5 } t	          j        |  	                    ¦   «         d¬¦  «        }|                     dg ¦  «        }|r)t          |¦  «         t                               d¦  «         |cddd¦  «         cY S # 1 swxY w Y   Y dS # t          $ r3}t                               d	|¦  «         t          d
|› �¦  «        |‚d}~ww xY wt           $ r3}t                               d|¦  «         t          d|› �¦  «        |‚d}~ww xY w)zLoad all jobs from storage.Úrúutf-8©ÚencodingÚjobsNF)Ústrictz8Auto-repaired jobs.json (had invalid control characters)z#Failed to auto-repair jobs.json: %sz*Cron database corrupted and unrepairable: zIOError reading jobs.json: %szFailed to read cron database: )r:   Ú	JOBS_FILEr0   ÚopenÚjsonÚloadr%   ÚJSONDecodeErrorÚloadsÚreadÚ	save_jobsÚloggerÚwarningrd   ÚerrorÚRuntimeErrorÚIOError)ÚfÚdatar“   rp   s       r   Ú	load_jobsr¤   F  sc  € å�M„M€MÝ×ÒÑÔð Øˆ	ðHÝ•)˜S¨7Ð3Ñ3Ô3ð 	(°qÝ”9˜Q‘<”<ˆDØ—8’8˜F BÑ'Ô'ð	(ð 	(ð 	(ð 	(ñ 	(ô 	(ð 	(ð 	(ð 	(ð 	(ð 	(ð 	(øøøð 	(ð 	(ð 	(ð 	(ð 	(ð 	(øõ Ôð Xð Xð Xð	XÝ•i ¨wÐ7Ñ7Ô7ð ¸1Ý”z !§&¢&¡(¤(°5Ð9Ñ9Ô9�Ø—x’x ¨Ñ+Ô+�Øð _å˜d‘O”O�OÝ—N’NÐ#]Ñ^Ô^Ð^Øðð ð ð ñ ô ð ð ð ð ð ð ð ð øøøð ð ð ð ð ð ð øõ ð 	Xð 	Xð 	XÝ�LŠLÐ>ÀÑBÔBÐBÝÐOÈAÐOÐOÑPÔPÐVWÐWøøøøð	Xøøøõ ð Hð Hð HÝ�ŠÐ4°aÑ8Ô8Ð8ÝÐ?¸AÐ?Ð?Ñ@Ô@ÀaÐGøøøøðHøøøs„   «B Á*A9Á,B Á9A=Á=B Â A=ÂB ÂF1ÂD5Â-A+D'ÄD5Ä$F1Ä'D+	Ä+D5Ä.D+	Ä/D5Ä5
E2Ä?.E-Å-E2Å2F1Å>.F,Æ,F1r“   c                 óŽ  — t          ¦   «          t          j        t          t          j        ¦  «        dd¬¦  «        \  }}	 t          j        |dd¬¦  «        5 }t          j	        | t          ¦   «                              ¦   «         dœ|d¬	¦  «         |                     ¦   «          t          j        |                     ¦   «         ¦  «         d
d
d
¦  «         n# 1 swxY w Y   t          j        |t          ¦  «         t!          t          ¦  «         d
S # t"          $ r( 	 t          j        |¦  «         n# t&          $ r Y nw xY w‚ w xY w)zSave all jobs to storage.ú.tmpz.jobs_©ÚdirÚsuffixÚprefixÚwr�   r‘   )r“   Ú
updated_atr>   )ÚindentN)r:   ÚtempfileÚmkstempr   r•   Úparentr)   Úfdopenr—   Údumprk   ri   ÚflushÚfsyncÚfilenorf   r1   ÚBaseExceptionÚunlinkr+   )r“   ÚfdÚtmp_pathr¢   s       r   rœ   rœ   c  ss  € å�M„M€MÝÔ#­­IÔ,<Ñ(=Ô(=ÀfÐU]Ð^Ñ^Ô^�L€BˆðÝŒY�r˜3¨Ð1Ñ1Ô1ð 	!°QÝŒI˜tµ;±=´=×3JÒ3JÑ3LÔ3LÐMÐMÈqÐYZÐ[Ñ[Ô[Ð[Ø�GŠG‰IŒIˆIÝŒH�Q—X’X‘Z”ZÑ Ô Ð ð	!ð 	!ð 	!ñ 	!ô 	!ð 	!ð 	!ð 	!ð 	!ð 	!ð 	!øøøð 	!ð 	!ð 	!ð 	!õ 	Œ
�8�YÑ'Ô'Ð'Ý•YÑÔÐÐÐøÝð ð ð ð	ÝŒI�hÑÔÐÐøÝð 	ð 	ð 	ØˆDð	øøøàðøøøsU   ÁD ÁA3CÃD ÃCÃD ÃCÃ1D Ä
EÄD2Ä1EÄ2
D?Ä<EÄ>D?Ä?EÚworkdirc                 óÂ  — | €dS t          | ¦  «                             ¦   «         }|sdS t          |¦  «                             ¦   «         }|                     ¦   «         st          d|›d�¦  «        ‚|                     ¦   «         }|                     ¦   «         st          d|› �¦  «        ‚|                     ¦   «         st          d|› �¦  «        ‚t          |¦  «        S )uh  Normalize and validate a cron job workdir.

    Rules:
      - Empty / None â†’ None (feature off, preserves old behaviour).
      - ``~`` is expanded.  Relative paths are rejected â€” cron jobs run detached
        from any shell cwd, so relative paths have no stable meaning.
      - The path must exist and be a directory at create/update time.  We do
        NOT re-check at run time (a user might briefly unmount the dir; the
        scheduler will just fall back to old behaviour with a logged warning).

    Returns the absolute path string, or None when disabled.
    Raises ValueError on invalid input.
    Nz+Cron workdir must be an absolute path (got zN). Cron jobs run detached from any shell cwd, so relative paths are ambiguous.zCron workdir does not exist: z!Cron workdir is not a directory: )	r   r   r   Ú
expanduserÚis_absoluterF   Úresolver0   Úis_dir)rº   ÚrawÚexpandedÚresolveds       r   Ú_normalize_workdirrÃ   v  sü   € ð €ØˆtÝ
ˆg‰,Œ,×
Ò
Ñ
Ô
€CØð ØˆtÝ�C‰yŒy×#Ò#Ñ%Ô%€HØ×ÒÑ!Ô!ð 
Ýð[¸#ð [ð [ð [ñ
ô 
ð 	
ð ×ÒÑ!Ô!€HØ�?Š?ÑÔð EÝÐC¸ÐCÐCÑDÔDÐDØ�?Š?ÑÔð IÝÐG¸XÐGÐGÑHÔHÐHÝˆx‰=Œ=Ðr!   ÚpromptÚnameÚrepeatÚdeliverÚoriginÚmodelÚproviderÚbase_urlÚscriptÚcontext_fromÚenabled_toolsetsc                 ó¾  — t          |¦  «        }|�|dk    rd}|d         dk    r|€d}|€|rdnd}t          j        ¦   «         j        dd…         }t	          ¦   «                              ¦   «         }t          ||¦  «        }t          |t          ¦  «        r!t          |¦  «         	                    ¦   «         nd}t          |	t          ¦  «        r!t          |	¦  «         	                    ¦   «         nd}t          |
t          ¦  «        r4t          |
¦  «         	                    ¦   «          
                    d	¦  «        nd}|pd}|pd}|pd}t          |t          ¦  «        r!t          |¦  «         	                    ¦   «         nd}|pd}|rd
„ |D ¦   «         nd}|pd}t          |¦  «        }t          |t          ¦  «        r,| 	                    ¦   «         r| 	                    ¦   «         gnd}n&t          |t          ¦  «        rd„ |D ¦   «         pd}nd}| p|r|d         ndpd}i d|“d|p|dd…          	                    ¦   «         “d| “d|“d|r|d         nd“d|“d|“d|“d|“d|“d|“d|                     d|¦  «        “d|ddœ“dd“dd “d!d“d"d“|t          |¦  «        dddd||||d#œ
¥}t          ¦   «         }|                     |¦  «         t#          |¦  «         |S )$a  
    Create a new cron job.

    Args:
        prompt: The prompt to run (must be self-contained, or a task instruction when skill is set)
        schedule: Schedule string (see parse_schedule)
        name: Optional friendly name
        repeat: How many times to run (None = forever, 1 = once)
        deliver: Where to deliver output ("origin", "local", "telegram", etc.)
        origin: Source info where job was created (for "origin" delivery)
        skill: Optional legacy single skill name to load before running the prompt
        skills: Optional ordered list of skills to load before running the prompt
        model: Optional per-job model override
        provider: Optional per-job provider override
        base_url: Optional per-job base URL override
        script: Optional path to a Python script whose stdout is injected into the
                prompt each run.  The script runs before the agent turn, and its output
                is prepended as context.  Useful for data collection / change detection.
        context_from: Optional job ID (or list of job IDs) whose most recent output
                      is injected into the prompt as context before each run.
                      Useful for chaining cron jobs: job A finds data, job B processes it.
        enabled_toolsets: Optional list of toolset names to restrict the agent to.
                          When set, only tools from these toolsets are loaded, reducing
                          token overhead. When omitted, all default tools are loaded.
        workdir: Optional absolute path.  When set, the job runs as if launched
                from that directory: AGENTS.md / CLAUDE.md / .cursorrules from
                that directory are injected into the system prompt, and the
                terminal/file/code_exec tools use it as their working directory
                (via TERMINAL_CWD).  When unset, the old behaviour is preserved
                (no context files injected, tools use the scheduler's cwd).

    Returns:
        The created job dict
    Nr   rQ   r\   r=   rÈ   Úlocalé   ú/c                 ó’   — g | ]D}t          |¦  «                             ¦   «         ¯#t          |¦  «                             ¦   «         ‘ŒES r9   ©r   r   )rV   Úts     r   ú
<listcomp>zcreate_job.<locals>.<listcomp>ä  s9   € ÐVÐVÐV¨aÅsÈ1ÁvÄvÇ|Â|Á~Ä~ÐV�3˜q™6œ6Ÿ<š<™>œ>ÐVÐVÐVr!   c                 ó’   — g | ]D}t          |¦  «                             ¦   «         ¯#t          |¦  «                             ¦   «         ‘ŒES r9   rÔ   ©rV   Újs     r   rÖ   zcreate_job.<locals>.<listcomp>ì  s9   € ÐOÐOÐO¨1ÅÀAÁÄÇÂÁÄÐO�˜A™œŸš™œÐOÐOÐOr!   zcron jobÚidrÅ   é2   rÄ   r   r   rÉ   rÊ   rË   rÌ   rÍ   rM   Úschedule_displayrS   rÆ   )ÚtimesÚ	completedÚenabledTÚstateÚ	scheduledÚ	paused_atÚpaused_reason)
Ú
created_atÚnext_run_atrx   Úlast_statusÚ
last_errorÚlast_delivery_errorrÇ   rÈ   rÎ   rº   )rr   ÚuuidÚuuid4Úhexrk   ri   r    r   r   r   ÚrstriprÃ   r   r%   r�   r¤   r   rœ   )rÄ   rM   rÅ   rÆ   rÇ   rÈ   r   r   rÉ   rÊ   rË   rÌ   rÍ   rÎ   rº   Úparsed_scheduleÚjob_idr   Únormalized_skillsÚnormalized_modelÚnormalized_providerÚnormalized_base_urlÚnormalized_scriptÚnormalized_toolsetsÚnormalized_workdirÚlabel_sourcer"   r“   s                               r   Ú
create_jobr÷   —  sÏ  € õf % XÑ.Ô.€Oð Ð˜f¨šk˜kØˆð �vÔ &Ò(Ð(¨V¨^Øˆð €Ø$Ð1�(�(¨'ˆåŒZ‰\Œ\Ô˜c˜r˜cÔ"€FÝ
‰-Œ-×
!Ò
!Ñ
#Ô
#€Cå-¨e°VÑ<Ô<ÐÝ-7¸½sÑ-CÔ-CÐM•s˜5‘z”z×'Ò'Ñ)Ô)Ð)ÈÐÝ3=¸hÍÑ3LÔ3LÐV�#˜h™-œ-×-Ò-Ñ/Ô/Ð/ÐRVÐÝ?IÈ(ÕTWÑ?XÔ?XÐb�#˜h™-œ-×-Ò-Ñ/Ô/×6Ò6°sÑ;Ô;Ð;Ð^bÐØ'Ð/¨4ÐØ-Ð5°ÐØ-Ð5°ÐÝ/9¸&Å#Ñ/FÔ/FÐP�˜F™œ×)Ò)Ñ+Ô+Ð+ÈDÐØ)Ð1¨TÐØZjÐtÐVÐVÐ3CÐVÑVÔVÐVÐptÐØ-Ð5°ÐÝ+¨GÑ4Ô4Ðõ �,¥Ñ$Ô$ð Ø1=×1CÒ1CÑ1EÔ1EÐO˜×*Ò*Ñ,Ô,Ð-Ð-È4ˆˆÝ	�L¥$Ñ	'Ô	'ð ØOÐO°ÐOÑOÔOÐWÐSWˆˆàˆàÐSÐ7HÐRÐ0°Ô3Ð3ÈdÐbÐXb€Lð Øˆfð à�Ð1˜ S b SÔ)×/Ò/Ñ1Ô1ð ð 	�&ð ð 	Ð#ð	 ð
 	Ð):ÐDÐ" 1Ô%Ð%Àð ð 	Ð!ð ð 	Ð'ð ð 	Ð'ð ð 	Ð#ð ð 	˜ð ð 	�Oð ð 	˜O×/Ò/°	¸8ÑDÔDð ð 	ØØð
ð 
ð ð" 	�4ð# ð$ 	�ð% ð& 	�Tð' ð( 	˜ð) ð* Ý'¨Ñ8Ô8ØØØØ#àØØ/Ø%ð? ð  ð  €CõD ‰;Œ;€DØ‡K‚K�ÑÔÐÝˆd�O„O€Oà€Jr!   rî   c                 óf   — t          ¦   «         }|D ]}|d         | k    rt          |¦  «        c S Œ dS )zGet a job by ID.rÚ   N)r¤   r&   )rî   r“   r"   s      r   Úget_jobrù     sG   € å‰;Œ;€DØð ,ð ,ˆØˆtŒ9˜ÒÐÝ& sÑ+Ô+Ð+Ð+Ð+ð àˆ4r!   Úinclude_disabledc                 óR   — d„ t          ¦   «         D ¦   «         }| sd„ |D ¦   «         }|S )z2List all jobs, optionally including disabled ones.c                 ó,   — g | ]}t          |¦  «        ‘ŒS r9   ©r&   rØ   s     r   rÖ   zlist_jobs.<locals>.<listcomp>%  s!   € Ð8Ð8Ð8 qÕ Ñ"Ô"Ð8Ð8Ð8r!   c                 ó>   — g | ]}|                      d d¦  «        ¯|‘ŒS )rß   T)r%   rØ   s     r   rÖ   zlist_jobs.<locals>.<listcomp>'  s+   € Ð:Ð:Ð:�a 1§5¢5¨°DÑ#9Ô#9Ð:�Ð:Ð:Ð:r!   )r¤   )rú   r“   s     r   Ú	list_jobsrÿ   #  s9   € à8Ð8­I©K¬KÐ8Ñ8Ô8€DØð ;Ø:Ð:˜4Ð:Ñ:Ô:ˆØ€Kr!   Úupdatesc           
      óÒ  — t          ¦   «         }t          |¦  «        D �]Ç\  }}|d         | k    rŒd|v r$|d         }|dv rd|d<   nt          |¦  «        |d<   t          i |¥|¥¦  «        }d|v }d|v sd|v rJt	          |                     d¦  «        |                     d¦  «        ¦  «        }||d<   |r|d         nd|d<   |rœ|d         }	t          |	t          ¦  «        rt          |	¦  «        }	|	|d<   |                     d	|	                     d
|                     d	¦  «        ¦  «        ¦  «        |d	<   |                     d¦  «        dk    rt          |	¦  «        |d<   |                     dd¦  «        rF|                     d¦  «        dk    r-|                     d¦  «        st          |d         ¦  «        |d<   |||<   t          |¦  «         t          ||         ¦  «        c S dS )zCUpdate a job by ID, refreshing derived schedule fields when needed.rÚ   rº   )Nr   FNrM   r   r   r   rÜ   rS   rà   Úpausedrå   rß   T)r¤   Ú	enumeraterÃ   r&   r    r%   r   r   rr   r�   rœ   )
rî   r   r“   Úir"   Ú_wdÚupdatedÚschedule_changedrï   Úupdated_schedules
             r   Ú
update_jobr	  +  s1  € å‰;Œ;€DÝ˜D‘/”/ð ),ñ ),‰ˆˆ3ØˆtŒ9˜ÒÐØð ˜ÐÐØ˜)Ô$ˆCØÐ'Ð'Ð'Ø%)�˜	Ñ"Ð"å%7¸Ñ%<Ô%<�˜	Ñ"å%Ð&8¨Ð&8°Ð&8Ñ9Ô9ˆØ%¨Ð0Ðà�wÐÐ '¨WÐ"4Ð"4Ý 5°g·k²kÀ'Ñ6JÔ6JÈGÏKÊKÐX`ÑLaÔLaÑ bÔ bÐØ 1ˆG�HÑØ7HÐRÐ0°Ô3Ð3ÈdˆG�GÑàð 	LØ& zÔ2Ðõ Ð*­CÑ0Ô0ð 7Ý#1Ð2BÑ#CÔ#CÐ Ø&6�˜
Ñ#Ø*1¯+ª+Ø"Ø ×$Ò$ Y°·²Ð<NÑ0OÔ0OÑPÔPñ+ô +ˆGÐ&Ñ'ð �{Š{˜7Ñ#Ô# xÒ/Ð/Ý)9Ð:JÑ)KÔ)K�˜Ñ&à�;Š;�y $Ñ'Ô'ð 	K¨G¯KªK¸Ñ,@Ô,@ÀHÒ,LÐ,LÐU\×U`ÒU`ÐanÑUoÔUoÐ,LÝ%5°g¸jÔ6IÑ%JÔ%JˆG�MÑ"àˆˆQ‰Ý�$‰ŒˆÝ" 4¨¤7Ñ+Ô+Ð+Ð+Ð+Øˆ4r!   Úreasonc                 óh   — t          | ddt          ¦   «                              ¦   «         |dœ¦  «        S )z Pause a job without deleting it.Fr  )rß   rà   râ   rã   )r	  rk   ri   )rî   r
  s     r   Ú	pause_jobr  [  s=   € åØàØÝ$™œ×0Ò0Ñ2Ô2Ø#ð		
ð 	
ñô ð r!   c           	      ó~   — t          | ¦  «        }|sdS t          |d         ¦  «        }t          | dddd|dœ¦  «        S )z=Resume a paused job and compute the next future run from now.NrM   Trá   ©rß   rà   râ   rã   rå   )rù   r�   r	  )rî   r"   rå   s      r   Ú
resume_jobr  h  sY   € å
�&‰/Œ/€CØð Øˆtå" 3 z¤?Ñ3Ô3€KÝØàØ ØØ!Ø&ð	
ð 	
ñ	ô 	ð 	r!   c           	      ó�   — t          | ¦  «        }|sdS t          | ddddt          ¦   «                              ¦   «         dœ¦  «        S )z1Schedule a job to run on the next scheduler tick.NTrá   r  )rù   r	  rk   ri   )rî   r"   s     r   Útrigger_jobr  {  sX   € å
�&‰/Œ/€CØð ØˆtÝØàØ ØØ!Ý&™=œ=×2Ò2Ñ4Ô4ð	
ð 	
ñ	ô 	ð 	r!   c                 ó¦   ‡ — t          ¦   «         }t          |¦  «        }ˆ fd„|D ¦   «         }t          |¦  «        |k     rt          |¦  «         dS dS )zRemove a job by ID.c                 ó,   •— g | ]}|d          ‰k    ¯|‘ŒS )rÚ   r9   )rV   rÙ   rî   s     €r   rÖ   zremove_job.<locals>.<listcomp>�  s'   ø€ Ð1Ð1Ð1�!˜q œw¨&Ò0Ð0ˆAÐ0Ð0Ð0r!   TF)r¤   ra   rœ   )rî   r“   Úoriginal_lens   `  r   Ú
remove_jobr  Œ  sV   ø€ å‰;Œ;€DÝ�t‘9”9€LØ1Ð1Ð1Ð1�tÐ1Ñ1Ô1€DÝ
ˆ4�y„y�<ÒÐÝ�$‰ŒˆØˆtØˆ5r!   ÚsuccessrŸ   Údelivery_errorc                 ór  — t           5  t          ¦   «         }t          |¦  «        D �]\\  }}|d         | k    �rIt          ¦   «                              ¦   «         }||d<   |rdnd|d<   |s|nd|d<   ||d<   |                     d	¦  «        r‘|d	                              d
d¦  «        dz   |d	         d
<   |d	                              d¦  «        }|d	         d
         }	|�>|dk    r8|	|k    r2|                     |¦  «         t          |¦  «          ddd¦  «         dS t          |d         |¦  «        |d<   |d         €d|d<   d
|d<   n|                     d¦  «        dk    rd|d<   t          |¦  «          ddd¦  «         dS �Œ^t           
                    d| ¦  «         ddd¦  «         dS # 1 swxY w Y   dS )uK  
    Mark a job as having been run.
    
    Updates last_run_at, last_status, increments completed count,
    computes next_run_at, and auto-deletes if repeat limit reached.

    ``delivery_error`` is tracked separately from the agent error â€” a job
    can succeed (agent produced output) but fail delivery (platform down).
    rÚ   rx   ÚokrŸ   ræ   Nrç   rè   rÆ   rÞ   r   r=   rÝ   rM   rå   Frß   rà   r  rá   z0mark_job_run: job_id %s not found, skipping save)Ú_jobs_file_lockr¤   r  rk   ri   r%   Úpoprœ   r�   r�   rž   )
rî   r  rŸ   r  r“   r  r"   r   rÝ   rÞ   s
             r   Úmark_job_runr  —  s†  € õ 
ð %Sð %SÝ‰{Œ{ˆÝ ‘o”oð !	ñ !	‰FˆAˆsØ�4Œy˜FÒ"Ñ"Ý!‘m”m×-Ò-Ñ/Ô/�Ø%(��MÑ"Ø-4Ð%A T T¸'��MÑ"Ø18Ð$B E E¸d��LÑ!à-;�Ð)Ñ*ð —7’7˜8Ñ$Ô$ð 
Ø14°X´×1BÒ1BÀ;ÐPQÑ1RÔ1RÐUVÑ1V�C˜”M +Ñ.ð   œM×-Ò-¨gÑ6Ô6�EØ # H¤¨kÔ :�IØÐ(¨U°QªY¨Y¸9ÈÒ;MÐ;MàŸš ™œ˜Ý! $™œ˜Øð-%Sð %Sð %Sñ %Sô %Sð %Sð %Sð %Sõ2 &6°c¸*´oÀsÑ%KÔ%K��MÑ"ð �}Ô%Ð-Ø%*�C˜	‘NØ#.�C˜‘L�LØ—W’W˜WÑ%Ô%¨Ò1Ð1Ø#.�C˜‘Lå˜$‘”�ØðG%Sð %Sð %Sñ %Sô %Sð %Sð %Sð %Sñ #õD 	�ŠÐIÈ6ÑRÔRÐRðK%Sð %Sð %Sñ %Sô %Sð %Sð %Sð %Sð %Sð %Sð %Sð %Søøøð %Sð %Sð %Sð %Sð %Sð %Ss   ˆDF,ÄAF,ÆF,Æ,F0Æ3F0c                 óþ  — t           5  t          ¦   «         }|D ]Ê}|d         | k    r¼|                     di ¦  «                             d¦  «        }|dvr ddd¦  «         dS t          ¦   «                              ¦   «         }t          |d         |¦  «        }|r;||                     d¦  «        k    r"||d<   t          |¦  «          ddd¦  «         dS  ddd¦  «         dS ŒË	 ddd¦  «         dS # 1 swxY w Y   dS )	u  Preemptively advance next_run_at for a recurring job before execution.

    Call this BEFORE run_job() so that if the process crashes mid-execution,
    the job won't re-fire on the next gateway restart.  This converts the
    scheduler from at-least-once to at-most-once for recurring jobs â€” missing
    one run is far better than firing dozens of times in a crash loop.

    One-shot jobs are left unchanged so they can still retry on restart.

    Returns True if next_run_at was advanced, False otherwise.
    rÚ   rM   rQ   ©r   rP   NFrå   T)r  r¤   r%   rk   ri   r�   rœ   )rî   r“   r"   rQ   r   Únew_nexts         r   Úadvance_next_runr   Ê  s¬  € õ 
ð ð Ý‰{Œ{ˆØð 	ð 	ˆCØ�4Œy˜FÒ"Ð"Ø—w’w˜z¨2Ñ.Ô.×2Ò2°6Ñ:Ô:�ØÐ3Ð3Ð3Ø ðð ð ñ ô ð ð ð õ "‘m”m×-Ò-Ñ/Ô/�Ý+¨C°
¬O¸SÑAÔA�Øð   ¨C¯GªG°MÑ,BÔ,BÒ BÐ BØ)1�C˜Ñ&Ý˜d‘O”O�OØðð ð ñ ô ð ð ð ð ðð ð ñ ô ð ð ð ð #ð ðð ð ñ ô ð ð ð ð ð ð ð øøøð ð ð ð ð ð s%   ˆAC2Á"A&C2ÃC2Ã#C2Ã2C6Ã9C6c            	      óf  — t          ¦   «         } t          ¦   «         }d„ t          j        |¦  «        D ¦   «         }g }d}|D �]Ý}|                     dd¦  «        sŒ|                     d¦  «        }|s™t          |                     di ¦  «        | |                     d¦  «        ¬¦  «        }|sŒm||d<   |}t                               d	|                     d
|d         ¦  «        |¦  «         |D ]}|d         |d         k    r	||d<   d} nŒt          t          j
        |¦  «        ¦  «        }	|	| k    rì|                     di ¦  «        }
|
                     d¦  «        }t          |
¦  «        }|dv r™| |	z
                       ¦   «         |k    r~t          |
|                      ¦   «         ¦  «        }|rZt                               d|                     d
|d         ¦  «        |||¦  «         |D ]}|d         |d         k    r	||d<   d} nŒ�ŒÈ|                     |¦  «         �Œß|rt!          |¦  «         |S )aO  Get all jobs that are due to run now.

    For recurring jobs (cron/interval), if the scheduled time is stale
    (more than one period in the past, e.g. because the gateway was down),
    the job is fast-forwarded to the next future run instead of firing
    immediately.  This prevents a burst of missed jobs on gateway restart.
    c                 ó,   — g | ]}t          |¦  «        ‘ŒS r9   rý   rØ   s     r   rÖ   z get_due_jobs.<locals>.<listcomp>ñ  s!   € ÐDÐDÐD qÕ Ñ"Ô"ÐDÐDÐDr!   Frß   Trå   rM   rx   rw   z:Job '%s' had no next_run_at; recovering one-shot run at %srÅ   rÚ   rQ   r  zSJob '%s' missed its scheduled time (%s, grace=%ds). Fast-forwarding to next run: %s)rk   r¤   ÚcopyÚdeepcopyr%   r}   r�   Úinforv   r   re   r‰   r‚   r�   ri   r   rœ   )r   Úraw_jobsr“   ÚdueÚ
needs_saver"   rŒ   Úrecovered_nextÚrjÚnext_run_dtrM   rQ   r†   r  s                 r   Úget_due_jobsr,  ç  s†  € õ ‰-Œ-€CÝ‰{Œ{€HØDÐD­D¬M¸(Ñ,CÔ,CÐDÑDÔD€DØ
€CØ€Jàð 9ñ 9ˆØ�wŠw�y $Ñ'Ô'ð 	Øà—7’7˜=Ñ)Ô)ˆØð 	Ý8Ø—’˜
 BÑ'Ô'ØØŸGšG MÑ2Ô2ðñ ô ˆNð
 "ð Øà!/ˆC�ÑØ%ˆHÝ�KŠKØLØ—’˜  D¤	Ñ*Ô*Øñô ð ð
 ð ð �Ø�d”8˜s 4œyÒ(Ð(Ø(6�B�}Ñ%Ø!%�JØ�Eð )õ
 $¥HÔ$:¸8Ñ$DÔ$DÑEÔEˆØ˜#ÒÐØ—w’w˜z¨2Ñ.Ô.ˆHØ—<’< Ñ'Ô'ˆDõ
 +¨8Ñ4Ô4ˆEØÐ+Ð+Ð+°°{Ñ1B×0QÒ0QÑ0SÔ0SÐV[Ò0[Ð0[õ ,¨H°c·m²m±o´oÑFÔF�Øð Ý—K’Kð:àŸš ¨¨D¬	Ñ2Ô2Ø ØØ ñô ð ð 'ð "ð "˜Ø˜dœ8 s¨4¤yÒ0Ð0Ø08˜B˜}Ñ-Ø)-˜JØ!˜Eð 1ñ à�JŠJ�s‰OŒOˆOùàð Ý�(ÑÔÐà€Jr!   c                 óÔ  — t          ¦   «          t          | z  }|                     dd¬¦  «         t          |¦  «         t	          ¦   «                              d¦  «        }||› d�z  }t          j        t          |¦  «        dd¬¦  «        \  }}	 t          j
        |dd	¬
¦  «        5 }|                     |¦  «         |                     ¦   «          t          j        |                     ¦   «         ¦  «         ddd¦  «         n# 1 swxY w Y   t          j        ||¦  «         t!          |¦  «         n5# t"          $ r( 	 t          j        |¦  «         n# t&          $ r Y nw xY w‚ w xY w|S )zSave job output to file.Tr3   z%Y-%m-%d_%H-%M-%Sz.mdr¦   z.output_r§   r«   r�   r‘   N)r:   r8   r7   r.   rk   rj   r®   r¯   r   r)   r±   Úwriter³   r´   rµ   rf   r1   r¶   r·   r+   )rî   r   Újob_output_dirÚ	timestampÚoutput_filer¸   r¹   r¢   s           r   Úsave_job_outputr2  6  s°  € å�M„M€MÝ &Ñ(€NØ×Ò °ÐÑ5Ô5Ð5Ý�ÑÔÐå‘”×&Ò&Ð':Ñ;Ô;€IØ  iÐ#4Ð#4Ð#4Ñ4€KåÔ#­¨NÑ(;Ô(;ÀFÐS]Ð^Ñ^Ô^�L€BˆðÝŒY�r˜3¨Ð1Ñ1Ô1ð 	!°QØ�GŠG�F‰OŒOˆOØ�GŠG‰IŒIˆIÝŒH�Q—X’X‘Z”ZÑ Ô Ð ð	!ð 	!ð 	!ñ 	!ô 	!ð 	!ð 	!ð 	!ð 	!ð 	!ð 	!øøøð 	!ð 	!ð 	!ð 	!õ 	Œ
�8˜[Ñ)Ô)Ð)Ý�[Ñ!Ô!Ð!Ð!øÝð ð ð ð	ÝŒI�hÑÔÐÐøÝð 	ð 	ð 	ØˆDð	øøøàðøøøð ÐsU   ÂD3 Â'ADÃ7D3 ÄDÄD3 Ä
DÄ'D3 Ä3
E%Ä>EÅE%Å
E ÅE%ÅE Å E%)NN)N)NNNNNNNNNNNNN)F)DÚ__doc__r#  r—   Úloggingr®   Ú	threadingr)   rD   ré   r   r   Úpathlibr   Úhermes_constantsr   Útypingr   r   r	   r
   r   Ú	getLoggerÚ__name__r�   Úhermes_timer   rk   r   rc   ÚImportErrorr¾   Ú
HERMES_DIRr6   r•   ÚLockr  r8   r{   r   r    r&   r.   r1   r:   rG   rL   rr   rv   r}   r$   r‰   r�   r¤   rœ   rÃ   r÷   rù   Úboolrÿ   r	  r  r  r  r  r  r   r,  r2  r9   r!   r   ú<module>r@     s]  ððð ð €€€Ø €€€Ø €€€Ø €€€Ø Ð Ð Ð Ø 	€	€	€	Ø 	€	€	€	Ø €€€Ø (Ð (Ð (Ð (Ð (Ð (Ð (Ð (Ø Ð Ð Ð Ð Ð Ø ,Ð ,Ð ,Ð ,Ð ,Ð ,Ø 3Ð 3Ð 3Ð 3Ð 3Ð 3Ð 3Ð 3Ð 3Ð 3Ð 3Ð 3Ð 3Ð 3à	ˆÔ	˜8Ñ	$Ô	$€à *Ð *Ð *Ð *Ð *Ð *ðØ!Ð!Ð!Ð!Ð!Ð!Ø€L€LøØð ð ð Ø€L€L€Lðøøøð ˆ_ÑÔ×&Ò&Ñ(Ô(€
Ø˜Ñ€Ø�{Ñ"€	ð
 !�)”.Ñ"Ô"€Ø˜Ñ €
ØÐ ðð  ¨#¤ð ¸xÈ¼}ð ÐX\Ð]`ÔXað ð ð ð ð"˜T # s (œ^ð °°S¸#°X´ð ð ð ð ð�dð ð ð ð ð�tð ð ð ð ðð ð ð%�cð %˜cð %ð %ð %ð %ð*V˜Sð V T¨#¨s¨(¤^ð Vð Vð Vð Vðr$�hð $ 8ð $ð $ð $ð $ð. "&ð	ð ð Ø�3˜�8Œnðà	ðð ˜#”ð	ð
 ˆc„]ðð ð ð ð6 Tð ¨cð ð ð ð ð@ð ˜t C¨ Hœ~ð ¸HÀS¼Mð ÐU]Ð^aÔUbð ð ð ð ðHH�4˜˜S #˜XœÔ'ð Hð Hð Hð Hð:�D˜˜c 3˜hœÔ(ð ð ð ð ð& ¨¤ð °(¸3´-ð ð ð ð ðH Ø Ø!Ø'+ØØ"&ØØ"Ø"Ø Ø48Ø,0Ø!ð@ð @Øð@àð@ð �3Œ-ð@ð �SŒMð	@ð
 �cŒ]ð@ð �T˜#˜s˜(”^Ô$ð@ð �CŒ=ð@ð �T˜#”YÔð@ð �CŒ=ð@ð �sŒmð@ð �sŒmð@ð �SŒMð@ð ˜5  d¨3¤i Ô0Ô1ð@ð ˜t CœyÔ)ð@ð �cŒ]ð@ð  
ˆ#ˆsˆ(„^ð!@ð @ð @ð @ðF�Cð ˜H T¨#¨s¨(¤^Ô4ð ð ð ð ðð  ð °°d¸3À¸8´nÔ1Eð ð ð ð ð-�sð - T¨#¨s¨(¤^ð -¸ÀÀcÈ3ÀhÄÔ8Pð -ð -ð -ð -ð`
ð 
�cð 
 8¨C¤=ð 
¸HÀTÈ#ÈsÈ(Ä^Ô<Tð 
ð 
ð 
ð 
ð�sð ˜x¨¨S°#¨X¬Ô7ð ð ð ð ð&˜ð  ¨¨c°3¨h¬Ô 8ð ð ð ð ð"�sð ˜tð ð ð ð ð EIØ15ð0Sð 0S˜ð 0S tð 0S°H¸S´Mð 0SØ!)¨#¤ð0Sð 0Sð 0Sð 0Sðf˜Sð  Tð ð ð ð ð:L�d˜4  S œ>Ô*ð Lð Lð Lð Lð^˜Cð ¨ð ð ð ð ð ð s   ÁA% Á%A/Á.A/