
    `gjn                    j   U d Z ddlmZ ddlZddlmZmZ ddlmZm	Z	m
Z
mZ g dZ G d de      Z eh d	      Zd
dddZded<    ed       G d d             Z ed       G d d             ZddZ edddddddd      Z ed d!d"d#d$d% ed      egd&'       ed(d)d*d+d,d- ed.dd/d0d1d23       ed4d5d6d78      egd9'       ed:d;d<d=d>d? ed@       edAddBdCdDE      egdF'       edGdHdId#dJdK edL      egdM'       edNdOdPdQd>dR edSd5dTdU8       edV       edWdXdYdZ eej1                               E      egd['       ed\d]d^d#d$d_ ed`      egda'       edbdcdddQd>de edfd5dgdhdij       ed@       edWdXdYdX eej1                               E       edkddldmdnE      egdo'       edpdqdrdQd>ds edSd5dtdu8       edv       edWdXdYdZ eej1                               E      egdw'       edxdydzdQd>d{ ed|d5d}d~8       ed       edWdXdYdZ eej1                               E      egd'       eddddQdd eddd/ddd3       edddddd3       edddddd3      egd'       edddd=d>d edddddE       edddddE       edddddE       ed       edAddBdCdDE      egd'       edddd#d>d edfd5dd8       ed       edWdXdYdX eej1                               E      egd'       eddddQd>d ed       edWdXdYdZ eej1                               E      egd'       edddd#d$d edddddE       ed      egd'      gZded<   eD  ci c]  } | j4                  |  c} ZddZddZdddÄZdddĄZddńZ ddƄZ! ejD                  dǫ      Z#ddddddmdd͜Z$dd΄Z%ddϜ	 	 	 	 	 	 	 ddЄZ&yc c} w )u  Automation Blueprints — parameterized automation blueprints with typed slots.

A *blueprint* is a one-place definition of an automation that every surface
renders natively:

  * Dashboard / GUI app  -> a form (one field per slot)
  * CLI / TUI / messenger -> a pre-filled ``/blueprint`` slash command
  * Agent                 -> a seed prompt; it asks for any blank/ambiguous slot
  * Docs catalog          -> a copy-paste command + a ``hermes://`` deep-link

The single source of truth is the slot schema below. ``blueprint_form_schema``
emits what a form renderer needs; ``blueprint_slash_command`` emits the flattened
one-line command; ``fill_blueprint`` validates user-supplied values and turns a
blueprint into a ``cron.jobs.create_job`` kwargs dict (so there is no second job
engine). The form-where-there's-a-screen / agent-fills-where-there's-a-chat
split both consume this same module.

Design choice: users never type raw cron. A blueprint carries a fixed recurrence
in ``schedule_template`` and parameterizes only the human-friendly parts
(time-of-day, weekday set). Blueprints needing full flexibility expose a ``text``
slot named ``schedule`` that passes through verbatim.
    )annotationsN)	dataclassfield)AnyDictListOptional)BlueprintSlotAutomationBlueprintCATALOGget_blueprintblueprint_form_schemablueprint_slash_commandblueprint_deeplinkblueprint_catalog_entryfill_blueprintBlueprintFillErrorWEEKDAY_PRESETSc                      e Zd ZdZy)r   z1Raised when supplied slot values fail validation.N)__name__
__module____qualname____doc__     I/root/.hermes/venv/lib/python3.12/site-packages/cron/blueprint_catalog.pyr   r   -   s    ;r   r   >   enumtexttimeweekdays*z1-5z0,6)everydayr    weekendszDict[str, str]r   T)frozenc                  ~    e Zd ZU dZded<   ded<   ded<   dZded<   d	Zd
ed<   dZded<   dZded<   dZ	ded<   ddZ
y)r
   z'A single fillable field on a blueprint.strnametypelabelNr   defaultr   tupleoptionsFbooloptional helpTstrictc                t    | j                   t        vr&t        d| j                   d| j                   d      y )Nzunknown slot type z (slot ))r(   _SLOT_TYPES
ValueErrorr'   )selfs    r   __post_init__zBlueprintSlot.__post_init__M   s7    99K'1$))gdii[PQRSS (r   )returnNone)r   r   r   r   __annotations__r*   r,   r.   r0   r1   r7   r   r   r   r
   r
   <   sM    1
I
IJGSGUHdD#N
 FDTr   r
   c                      e Zd ZU dZded<   ded<   ded<   ded<   ded<   ded<    ee	      Zd
ed<   dZded<   dZ	ded<   dZ
ded<   y)r   z%A parameterized automation blueprint.r&   keytitledescriptioncategoryschedule_templateprompt_template)default_factoryzList[BlueprintSlot]slotsorigindeliver_defaultr   r+   skillstagsN)r   r   r   r   r:   r   listrC   rE   rF   rG   r   r   r   r   r   R   sV    /	HJM !&t!<E<#OS#FED%r   r   08:00c                "    t        ddd| d      S )Nr   z
What time?z24h local time, e.g. 08:00r'   r(   r)   r*   r0   )r
   )r*   s    r   <lambda>rL   j   s    	fL'	%! r   deliverr   zWhere to deliver?rD   )rD   localtelegramdiscordemailFzorigin = the chat you set this up from (or your configured home channel when created from the dashboard); local = save only, no message; or any connected platform namer'   r(   r)   r*   r,   r.   r1   r0   zmorning-briefzMorning briefingzVA short daily briefing: today's calendar, weather, and anything urgent waiting on you.dailyz{minute} {hour} * * *zProduce a concise morning briefing for the user: today's calendar events, the local weather, and any urgent items. Keep it short and scannable. If no data sources are connected, give a brief good-morning with the date and offer to connect calendar/email.)rS   briefing)r<   r=   r>   r?   r@   rA   rC   rG   zimportant-mailzImportant-mail monitorzYCheck your inbox periodically and ping you ONLY about mail that actually needs attention.rQ   z*/{interval_min} * * * *a<  Check the user's inbox for new messages since the last run. Surface ONLY mail matching: {criteria}. Score candidates with the urgency classifier and deliver only what clears the bar; if nothing does, respond with [SILENT]. Requires a connected mail source; if none is configured, explain how to connect one and stop.interval_minz
How often?30)15rV   60zminutes between checks)r'   r(   r)   r*   r,   r0   criteriar   u   Only notify me if the mail…zIneeds a reply today, is from my manager or family, or mentions a deadline)r'   r(   r)   r*   )rQ   monitorzweekly-reviewzWeekly reviewzGA weekly recap: what got done, what's still open, and what's coming up.weeklyz{minute} {hour} * * {dow}zProduce a weekly review for the user: what was accomplished this week, still-open items, and next week's calendar. Pull from connected sources. Keep it tight.z18:00dayz
Which day?sunday)r]   mondayfridaysaturday)r'   r(   r)   r*   r,   )r[   reviewzworkday-startzWorkday start reminderz4A weekday nudge with your agenda and top priorities.z{minute} {hour} * * 1-5zGive the user a brief weekday start-of-day nudge: today's calendar and the 1-3 highest-priority things to focus on, inferred from recent context and any task tools. Encouraging, short, one message.z09:00)rS   focuszcustom-reminderzCustom reminderz9A recurring reminder in your own words, on your schedule.generalzRemind the user: {what}whatu   Remind me to…ztake a break and stretchz14:00
recurrencer    z	Repeat onr"   )reminderzevening-winddownzEvening wind-downz]An end-of-day check-in: tomorrow's calendar at a glance and anything you should prep tonight.u  Give the user a short evening wind-down: tomorrow's calendar, any early commitments to prep for, and one gentle nudge to wrap up loose ends from today. Keep it calm and brief — one message. If no calendar is connected, just offer a friendly sign-off and the weather for tomorrow.z21:00)rS   eveningznews-digestzTopic news digestuy   A recurring digest on a topic you care about — deduped against what was already sent, so only genuinely new items land.u  Search the web for new and noteworthy items about: {topic}. Dedupe against what you sent in previous runs — only include genuinely new developments. Deliver a tight digest of at most {count} bullets, each one line with a link. If nothing new since last run, respond with [SILENT].topiczWhat topic?zAI and technologyz,a subject, product, person, or search phraserK   countzHow many bullets?5)3rj   8)digestresearchzbill-renewal-watchzBills & renewals reminderuq   A heads-up before a recurring payment, subscription renewal, or due date — so nothing auto-charges by surprise.zRemind the user about an upcoming payment or renewal: {what}. Phrase it as an actionable heads-up (e.g. 'review or cancel before it renews'), not just a notification. One short message.zWhat's due?z%my streaming subscription renews soonz10:00)rf   financezhabit-checkinzHabit check-inzMA recurring nudge to keep a habit on track and reflect on whether you did it.zNudge the user about their habit: {habit}. Ask whether they did it today, keep it warm and non-judgmental, and offer a one-line word of encouragement. One short message.habitzWhich habit?z20 minutes of readingz20:00)rp   	wellbeingzhydration-movezHydration & movement nudgezFA periodic nudge during the day to drink water, stand up, and stretch.z20 {start_hour}-{end_hour}/{interval_hours} * * 1-5zSend the user a brief, friendly nudge to drink some water, stand up, and stretch for a moment. Vary the wording each time so it doesn't feel robotic. One short line.interval_hours1)rs   2rk   zhours between nudges
start_hourz
Start hour9)7rl   rv   10z%first hour of the active window (24h)end_hourzEnd hour17)16rz   1819z$last hour of the active window (24h))rq   rb   z	meal-planzWeekly meal planzkA weekly meal plan plus a consolidated grocery list, tuned to your diet and how much time you have to cook.zBuild the user a meal plan for the coming week: {meals} per day, suited to a {diet} diet and roughly {effort} cooking effort. Include a consolidated grocery list grouped by aisle. Keep blueprints simple and skimmable.dietzDiet?no restrictions)r   
vegetarianveganzhigh-proteinzlow-carbmealszMeals per day?dinner only)r   zlunch and dinnerz	all threeeffortzCooking effort?quick)r   medium	ambitiousz17:00)r[   foodzlearn-dailyzDaily learning dripz[One bite-sized lesson a day on a topic you want to learn, building progressively over time.zTeach the user one bite-sized lesson about: {topic}. Build on earlier lessons so it progresses rather than repeating. Keep it to a couple of short paragraphs with one concrete example, and end with a single question to check understanding.u   Learn about…zSpanish vocabularyz08:30)learningrS   zgratitude-journalzGratitude & reflection promptzFA gentle evening prompt to reflect on the day and note what went well.u   Send the user a short, warm reflection prompt for the end of the day — invite them to note one thing that went well, one thing they are grateful for, and one small win. If they reply, acknowledge it kindly. One message.z21:30)rq   
reflectionzon-this-dayzOn-this-day discoveryzQA daily dose of curiosity: a notable historical event, fact, or word for the day.u   Give the user one interesting '{flavor}' item for today — keep it short, surprising, and genuinely interesting. One or two sentences, no filler.flavorz
What kind?on this day in history)r   zword of the dayzscience factzquote of the dayz07:30)rS   	curiosityzList[AutomationBlueprint]r   c                ,    t         j                  |       S N)_CATALOG_BY_KEYget)r<   s    r   r   r     s    s##r   c                   | j                   | j                  | j                  | j                  t	        | j
                        | j                  D cg c]f  }|j                  |j                  |j                  |j                  t	        |j                        |j                  |j                  |j                  dh c}dS c c}w )zIEmit the JSON a form renderer (dashboard / GUI) needs for this blueprint.rR   )r<   r=   r>   r?   rG   fields)r<   r=   r>   r?   rH   rG   rC   r'   r(   r)   r*   r,   r.   r1   r0   )	blueprintss     r   r   r     s     }} ,,&&Y^^$ __
  99		?JJ((	
 
s   A+B?c                   |xs i }d| j                    g}| j                  D ]  }|j                  |j                  |j                        }||dk(  r|j
                  r=d}t        |      }|j                  dk(  sd|v rd|j                  dd      z   dz   }|j                  |j                   d|         dj                  |      S )u   Build the flattened ``/blueprint <key> slot=val …`` command string.

    Uses each slot's default when ``values`` is omitted, so the docs/dashboard
    can show a ready-to-paste command. Free-text slots are quoted.
    z/blueprint r/   r    "z\"=)r<   rC   r   r'   r*   r.   r&   r(   replaceappendjoin)r   valuespartsr   valsvals         r   r   r     s     \rF9==/*+E__ 	)jj+;#)zzC3x66Vsd{c511C7Dxq'(	) 88E?r   c                   ddl m}m} |xs i }i }| j                  D ]E  }|j	                  |j
                  |j                        }|dvs.t        |      ||j
                  <   G |rd ||      z   nd}d || j                         | S )z>Build the ``hermes://blueprint/<key>?slot=val`` deep-link URL.r   )quote	urlencodeNr/   ?r/   zhermes://blueprint/)	urllib.parser   r   rC   r   r'   r*   r&   r<   )r   r   r   r   queryr   r   qss           r   r   r     s    -\rFE__ %jj+j HE!&&M% &+#	% 
 B y}}!5 6rd;;r   c                   | j                   }|j                  d      rZt        d | j                  D        d      }|r|j                  ndxs% |j                  d      d   j                         d   }d| dS d	|v rTt        d
 | j                  D        d      }t        |r|j                  ndxs d      }d|v rdnd}|dk(  r| dS | d| dS t        d | j                  D        d      }|r|j                  nd}d|v r	|rd| S dS d|v rEt        d | j                  D        d      }|r|j                  ndxs d}|r	|r| d| S |rd| S dS |rd| S y)zGA short human-readable description of when a blueprint runs (defaults).z*/c              3  @   K   | ]  }|j                   d k(  s|  yw)rU   Nr'   .0r   s     r   	<genexpr>z%_humanize_schedule.<locals>.<genexpr>+  s     J>1I1J   N/   r   zevery z minutesz{interval_hours}c              3  @   K   | ]  }|j                   d k(  s|  yw)rr   Nr   r   s     r   r   z%_humanize_schedule.<locals>.<genexpr>/  s     L;K1K1Lr   rs   z* * 1-5z
weekdays, r/   z
every hourz hoursc              3  @   K   | ]  }|j                   d k(  s|  yw)r   N)r(   r   s     r   r   z%_humanize_schedule.<locals>.<genexpr>3  s     EAAFFf4DaEr   zweekdays at zevery weekday{dow}c              3  >   K   | ]  }|j                   d v s|  yw))r\   re   Nr   r   s     r   r   z%_humanize_schedule.<locals>.<genexpr>8  s     WqqvvAV7VWs   z at zat zon a schedulez	daily at )r@   
startswithnextrC   r*   splitr&   )r   schediveveryscope	time_slotwhenday_slots           r   _humanize_scheduler   '  s   ''EJiooJDQ!tNC0@0C0I0I0KA0Nwh''U"LiooLdS2RZZ47C8 )U 2',|%
#VE7&v9VVEEtLI )9tDE(,dV$A/A%WIOOWY]^%-!!2<"TWD''#TF|884&!!r   c                t    i t        |       | j                  t        |       t        |       t	        |       dS )u   Unified serializable shape for a blueprint — used by the docs generator
    and the dashboard API. Combines the form schema, the ready-to-paste slash
    command, the deep-link URL, and a human-readable schedule.
    )schedulescheduleHumancommandappUrl)r   r@   r   r   r   )r   s    r   r   r   B  s?    

	
*//+I6*95$Y/ r   z^([01]?\d|2[0-3]):([0-5]\d)$0rt   rk   46)r]   r^   tuesday	wednesdaythursdayr_   r`   c                   | j                   }d|v r|d   rt        |d         S i }|j                  d      }d|v sd|v r|st        d      t        j                  t        |      j                               }|st        d|d      t        t        |j                  d                  |d	<   t        t        |j                  d
                  |d<   d|v rd|v rbt        |j                  dd            j                         }|t        vr$t        d|ddj                  t                     t        |   |d<   nUd|v rLt        |j                  dd            j                         }|t        vrt        d|      t        |   |d<   nd|d<   d|v r[t        |j                  dd            j                         }|j                         rt        |      dk  rt        d|d      ||d<   t        j                  d|      D ]  }	|	|vs|	|v st        ||	         ||	<    	  |j                   di |S # t"        $ r}
t        d|
       |
d}
~
ww xY w) zBFill the schedule_template placeholders from resolved slot values.r   r   z{minute}z{hour}za time is requiredzinvalid time u    — use HH:MM (24h)r   hour   minuter   re   r"   zunknown recurrence u    — one of , dowr\   r/   zunknown day r!   z{interval_min}rU   r   zinvalid interval u"    — minutes as a positive integerz	\{(\w+)\}z$schedule template missing value for Nr   )r@   r&   r   r   _TIME_REmatchstripintgrouplowerr   r   _DAY_TO_DOWisdigitrefindallformatKeyError)r   r   r   repltime_valmpresetr\   r   r'   es              r   _resolve_scheduler   [  sf   ''E Vz 26*%&&D zz&!HUh%/$%9::NN3x=..01$}XL@T%UVV3qwwqz?+VS_-X %6!L*=>DDFF_,()&<		/@Z?[\  *&1DKf_fjj+,224C+%(<w)?@@%c*DKDK 5 NB/0668zz|s2w!|$'8>`%abb!^
 

</ +tVD\*DJ+Tu||#d## T #Gs!KLRSSTs   *H< <	III)rD   c                  | j                   D ch c]  }|j                   }}t        t        |      |z
        }|rSt	        dt        |      dkD  rdnd ddj                  |       ddj                  d | j                   D                     i }| j                   D ]  }|j                  |j                  |j                        }|d	v r3|j                  r;t	        d
|j                   d|j                   d      |j                  dk(  r|j                  r~|j                  rrt        |      |j                  D ch c]  }t        |       c}vrDt	        |j                   d|ddj                  t        t        |j                                     |||j                  <    t!        | |      }		  | j"                  j$                  di |}
|
|	| j(                  |j                  d| j*                        d}| j,                  rt/        | j,                        |d<   |||d<   |S c c}w c c}w # t&        $ r}t	        d|       |d}~ww xY w)u  Validate ``values`` and return ``cron.jobs.create_job`` kwargs.

    Missing required (non-optional) slots raise BlueprintFillError naming the
    slot, so a form can show field errors and the agent knows what to ask.
    Unknown slot names are rejected (a typo'd ``tiem=07:15`` must not silently
    create a job with the default time). Enum values are checked against their
    options. The result is passed straight to ``create_job`` — no second schema.
    zunknown slotr   r   r/   z: r   u    — valid: c              3  4   K   | ]  }|j                     y wr   r   r   s     r   r   z!fill_blueprint.<locals>.<genexpr>  s     8YA8Ys   r   zmissing required value: z (r3   r   r   u    not allowed — one of z#blueprint prompt missing value for NrM   )promptr   r'   rM   rF   rD   r   )rC   r'   sortedsetr   lenr   r   r*   r.   r)   r(   r1   r,   r&   mapr   rA   r   r   r=   rE   rF   rH   )r   r   rD   r   knownunknownresolvedrawor   r   r   specs                r   r   r     s9    '__-QVV-E-S[5()G #g,"23;2yy!",tyy8Y8Y/Y.Z\
 	
  "H__ 
jj+*zz$'?xr!''RS%TUU66VQYY3s8\]\e\eKfWXCPQFKf;f$66(!C7":499SaiiEX;Y:Z[  
 !H5HS1**11=H=
 <<	9+D+DE	D i../XXKM . Lg  S #Fqc!JKQRRSs#   H+>H07H5 5	I>II)rI   )r<   r&   r8   zOptional[AutomationBlueprint])r   r   r8   Dict[str, Any]r   )r   r   r   Optional[Dict[str, Any]]r8   r&   )r   r   r8   r&   )r   r   r   r   r8   r&   )r   r   r   r   rD   r   r8   r   )'r   
__future__r   r   dataclassesr   r   typingr   r   r   r	   __all__r5   r   	frozensetr4   r   r:   r
   r   _TIME_DELIVERr+   keysr   r<   r   r   r   r   r   r   r   compiler   r   r   r   )rs   0r   <module>r      s  . # 	 ( , ,< <
 <= #  $T T T* $  .	 	':Q5
%	  *1N
 W~x("  &)4? #&&8-
 f5) 
 "9< 50
 'N| B
 
 "+. &J3R W~x( O51vF:K9;'N!
+"o2245
 

 %( !01$ W~x(!" !K5/ 6+C
 'N!
+"o2245
 61D_ !
$ $A!D  )H5G
 &? 'N!
+"o2245
 
 %36 !53
 6/ 'N!
+"o2245
 
 $36 * O4
 %F,_+
 !l%:<
 fJ&>;
 !
$ $C"F  A5$ &)5 61A%H
 F2C)I 'N| B
 -
0  K&N #,5= 61A, 'N!
+"o2245
 
 #58 -5# 'N!
+"o2245
 
 )-0 %%1
 F,0= 'N	
 $-_
g&	" gR &--155!8-$0*<6$ 2::56SSss7T| (,	4"44 %	4
 4i .s   N0