+
    jHàj;v  ã                  ó„  € R t ^ RIHt ^ RIt^ RIt^ RIt^ RIt^ RIt^ RIH	t	H
t
HtHt ^ RIHtHtHtHtHt ]P&                  ! ]4      t^t^dt^<tRQtRt]P6                  ! R]P8                  ! ]4      ,           R,           4      t]P6                  ! R]P<                  4      tR	] R
2t R] R
2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4      4       t+R t,R! R" lt-R# R$ lt.RRR% R& llt/RSR( R) llt0R* R+ lt1R, R- lt2R. R/ lt3R0 R1 lt4R2 R3 lt5R4R'/R5 R6 llt6R7 R8 lt7R9 R: lt8R; R< lt9R= R> lt: ! R? R@4      t;RA RB lt<RC] RD2t=RE RF lt>RG RH lt?/ ]@Pƒ                  RTRI 4      CRJ]>RK]?/C]@Pƒ                  RURL 4      C]@Pƒ                  RVRM 4      CtBRNR/RO RP lltC. RWOtDR# )XuÄ  Recurring in-session wakeups â€” the /loop command (Claude Code parity).

A loop stops when the agent ends a wakeup reply with ``LOOP_COMPLETE`` on its own line, when
``--times N`` ticks have fired, when the ``--until`` judge rules the condition met, or when the
``loops.max_ticks`` backstop pauses it. State lives in SessionDB ``state_meta`` (same contract as
``hermes_cli/goals.py``); CLI, gateway, and TUI all drive it through :class:`LoopManager`.
)ÚannotationsN)Ú	dataclassÚfieldÚfieldsÚasdict)ÚAnyÚDictÚListÚOptionalÚTupleÚLOOP_COMPLETEz	(?im)^\s*z\s*[.!]?\s*$z)^(?=\d)(?:(\d+)h)?(?:(\d+)m)?(?:(\d+)s)?$uÇ  [/loop wakeup #{tick}{cadence}]
Recurring task: {prompt}

This is an automatic wakeup from the /loop the user set. Perform the task now against the CURRENT state (re-check files, processes, or services fresh â€” do not assume anything from earlier iterations still holds). Report concisely what you found or did this iteration.
If the task is now complete, no longer applicable, or the thing you were watching has finished, say so and end your reply with u)    on its own line â€” that stops the loop.uþ  [/loop wakeup #{tick}{cadence}]
Recurring task: {prompt}

Stop condition: {until}

This is an automatic wakeup from the /loop the user set. Perform the task now against the CURRENT state (re-check files, processes, or services fresh â€” do not assume anything from earlier iterations still holds). Report concisely what you found or did this iteration, and show concrete evidence of the stop condition's status.
If the stop condition is met, or the task is no longer applicable, say so and end your reply with c               ó    € V ^8„  d   QhRRRR/# )é   ÚtokenÚstrÚreturnúOptional[int]© )Úformats   "Ú5/home/ubuntu/.hermes/hermes-agent/hermes_cli/loops.pyÚ__annotate__r   G   s   € ÷ 
(ñ 
( ð 
(¨ñ 
(ó    c                óö   € V '       d$   \         P                  V P                  4       4      MRpV'       g   R# R VP                  4        4       w  r#pVR,          V^<,          ,           V,           pV^ 8”  d   V# R# )z˜Total seconds for ``30s``/``5m``/``2h``/``1h30m``, else None.

A bare number is NOT an interval (it collides with prompt text like ``/loop 3 things``).
Nc              3  óJ   "  € T F  q'       d   \        V4      M^ x € K  	  R# 5i)é    N)Úint)Ú.0Úgs   & r   Ú	<genexpr>Ú'parse_interval_token.<locals>.<genexpr>O   s   é € Ð8©Z¨ž1”�Q” !Ô#«Zùs   ‚!#é  )Ú_INTERVAL_TOKEN_REÚmatchÚstripÚgroups)r   ÚmÚhÚmntÚsÚtotals   &     r   Úparse_interval_tokenr*   G   sa   € ÷
 49Ô× Ñ  §¡£Ô/¸d€AßÙÙ8¨Q¯X©X¬ZÓ8�I€AˆAØ��H�s˜R•xÕ !Õ#€EØ˜A”Iˆ5Ð' 4Ð'r   c               ó    € V ^8„  d   QhRRRR/# )r   Útextr   r   údict[str, Any]r   )r   s   "r   r   r   T   s   € ÷ )cñ )c˜#ð )c .ñ )cr   c           
     ó8  € T ;'       g    RP                  4       pRRRRR^ RRRR/pV'       g	   / VCRR/C# ^ RrC\        P                  ! R	V4      pV'       db    \        VP	                  ^4      4      pV^8  d   \
        h TRTP                  4        YP                  4       R ,           P                  4       p\        P                  ! RV\        P                  4      pV'       dA   VP	                  ^4      P                  4       pVRVP                  4        P                  4       pVP                  R^4      pV'       dH   V^ ,          P                  4       R8X  d,   \        V4      ^8”  d   V^,          pVP                  R^4      pV'       d   \        V^ ,          4      MRpVe*   \        V4      ^8”  d   V^,          P                  4       MRpV'       g	   / VCRR/C# / VCRVRVRVRV/C#   \
         d    / TCRR
TP	                  ^4      : 2/Cu # i ; i)zâParse ``/loop [interval] <prompt> [--times N] [--until ...]``.

Returns ``{"interval_seconds": int|None, "prompt", "times", "until", "error"}``;
``interval_seconds`` None means self-paced, ``error`` is set for unusable input.
Ú Úinterval_secondsNÚpromptÚtimesÚuntilÚerrorÚemptyz\s--times\s+(\S+)z(--times expects a positive integer, got z\s--until\s+(.+)$Úeveryz1missing prompt (usage: /loop [interval] <prompt>))r#   ÚreÚsearchr   ÚgroupÚ
ValueErrorÚstartÚendÚDOTALLÚsplitÚlowerÚlenr*   )	r,   ÚrawÚresultr2   r3   Úm_timesÚm_untilÚtokensÚintervals	   &        r   Úparse_loop_argsrG   T   sÿ  € ð �:ˆ:�2×
Ñ
Ó
€CØ0°$¸À"ÀgÈqÐRYÐ[]Ð_fÐhlÐm€FßØ+�&Ð+˜' 7Ñ+Ð+ð �bˆ5Ü�iŠiÐ,¨cÓ2€Gßð	hÜ˜Ÿ™ aÓ(Ó)ˆEØ�qŒyÜ Ð ð ð Ð$�W—]‘]“_Ð%¨¯K©K«M¨NÐ(;Õ;×BÑBÓDˆä�iŠiÐ,¨c´2·9±9Ó=€GßØ—‘˜aÓ ×&Ñ&Ó(ˆØÐ#�G—M‘M“OÐ$×*Ñ*Ó,ˆð �Y‰Y�t˜QÓ€Fß�&˜•)—/‘/Ó# wÔ.´3°v³;À´?Ø�Q�iˆØ—‘˜4 Ó#ˆç28Ô# F¨1¥IÔ.¸d€HØÒÜ#& v£;°¤?ˆf�Q�i�o‰oÔ¸ˆçØW�&ÐW˜'Ð#VÑWÐWØbˆfÐbÐ(¨(°H¸cÀ7ÈEÐSZÐ\aÑbÐbøô+ ô 	hØg�fÐg˜gÐ)QÐRY×R_ÑR_Ð`aÓRbÑQeÐ'fÑgÒgð	hús   Á'G0 Ç0&HÈHc               ó    € V ^8„  d   QhRRRR/# )r   ÚsecondsÚfloatr   r   r   )r   s   "r   r   r   €   s   € ÷ 	ñ 	˜Uð 	 sñ 	r   c           
     ó4  € \        \        \        ^ \        V 4      4      4      R4      w  r\        V^<4      w  r4V'       d   V R2.M. pV'       d   VP	                  V R24       V'       g	   V'       g   VP	                  V R24       RP                  V4      # )uB   Render seconds as a compact human interval (``90`` â†’ ``1m30s``).r    r&   r%   r(   r/   )Údivmodr   ÚmaxÚroundÚappendÚjoin)rI   r&   Úremr%   r(   Úpartss   &     r   Úformat_intervalrS   €   sx   € ä”Cœ˜Aœu W›~Ó.Ó/°Ó6�F€AÜ�#�r‹?�D€Aß�ˆs�!ˆW‰I €EßØ�‰˜�s˜!�WÔß—Ø�‰˜�s˜!�WÔØ�7‰7�5‹>Ðr   c               ó   € V ^8„  d   QhRR/# )r   r   r-   r   )r   s   "r   r   r   Œ   s   € ÷ ñ �~ñ r   c                 ó¾   €  ^ RI Hp  V ! 4       ;'       g    / P                  R4      ;'       g    / p\        V\        4      '       d   V# / #   \
         d    / u # i ; i)r   )Úload_configÚloops)Úhermes_cli.configrV   ÚgetÚ
isinstanceÚdictÚ	Exception)rV   Úsections     r   Ú_loops_configr^   Œ   sW   € ðÝ1á“=×&Ð& B×+Ñ+¨GÓ4×:Ð:¸ˆÜ$ W¬d×3Ò3ˆwÐ;¸Ð;øÜô ØŠ	ðús!   ‚A –A ¯A Á
A ÁAÁAc               ó(   € V ^8„  d   QhRRRRRRRR/# )r   Úkeyr   Údefaultr   Úfloorr   r   )r   s   "r   r   r   –   s(   € ÷ ñ �Sð  3ð ¨sð °sñ r   c           	     ó‚   €  \        V\        \        4       P                  W4      4      4      #   \         d    Tu # i ; i)zM``loops.<key>`` as an int clamped to ``floor``; ``default`` on any bad value.)rM   r   r^   rY   r\   )r`   ra   rb   s   &&&r   Ú_config_intrd   –   s:   € ðÜ�5œ#œm›o×1Ñ1°#Ó?Ó@ÓAÐAøÜô ØŠðús   ‚+. ®>½>c               ó   € V ^8„  d   QhRR/# ©r   r   r   r   )r   s   "r   r   r   ž   s   € ÷ Pñ P˜cñ Pr   c                 ó$   € \        R \        ^4      # )Úmin_interval_seconds)rd   ÚDEFAULT_MIN_INTERVAL_SECONDSr   r   r   rh   rh   ž   s   € ÜÐ-Ô/KÈQÓOÐOr   c               ó   € V ^8„  d   QhRR/# rf   r   )r   s   "r   r   r   ¢   s   € ÷ :ñ :˜3ñ :r   c                 ó$   € \        R \        ^ 4      # )Ú	max_ticks)rd   ÚDEFAULT_MAX_TICKSr   r   r   Úmax_ticks_defaultrn   ¢   s   € Ü�{Ô$5°qÓ9Ð9r   c               ó   € V ^8„  d   QhRR/# rf   r   )r   s   "r   r   r   ¦   s   € ÷ Yñ Y #ñ Yr   c                 ó$   € \        R \        ^
4      # )Úself_paced_floor_seconds)rd   Ú DEFAULT_SELF_PACED_FLOOR_SECONDSr   r   r   rq   rq   ¦   s   € ÜÐ1Ô3SÐUWÓXÐXr   c               ó   € V ^8„  d   QhRR/# rf   r   )r   s   "r   r   r   ª   s   € ÷ lñ l Cñ lr   c                 óL   € \        4       p \        R \        V \        4      V 4      # )Úself_paced_ceiling_seconds)rq   rd   rM   Ú"DEFAULT_SELF_PACED_CEILING_SECONDS)rb   s    r   ru   ru   ª   s#   € Ü$Ó&€EÜÐ3´S¸Ô@bÓ5cÐejÓkÐkr   c                  óN  € ] tR t^¯t$ RtR]R&   RtR]R&   RtR]R&   RtR	]R
&   Rt	R	]R&   ^ t
R]R&   RtR]R&   ]tR]R&   ^ tR]R&   RtR	]R&   RtR	]R&   RtR	]R&   RtR]R&   RtR]R&   RtR]R&   RtR]R&   ]! ]R7      tR]R&   R  R! lt]R" R# l4       tR$ R% ltR& R' ltR(tR# ))Ú	LoopStatez,Serializable /loop state stored per session.r   r1   ÚactiveÚstatusrF   Úmodeç        rJ   r0   Úcurrent_delayr   r2   r/   r3   rl   Úticks_firedÚ
created_atÚlast_fired_atÚnext_due_atFÚboolÚawaiting_responseÚlast_response_digestNúOptional[str]Úpaused_reasonÚlast_stop_reason)Údefault_factoryzdict[str, str]Úroutec               ó   € V ^8„  d   QhRR/# ©r   r   r   r   )r   s   "r   r   ÚLoopState.__annotate__É   s   € ÷ <ñ <˜ñ <r   c                	óD   € \         P                  ! \        V 4      R R7      # )F)Úensure_ascii)ÚjsonÚdumpsr   ©Úselfs   &r   Úto_jsonÚLoopState.to_jsonÉ   s   € Ü�zŠzœ& ›,°UÔ;Ð;r   c               ó    € V ^8„  d   QhRRRR/# )r   rA   r   r   rx   r   )r   s   "r   r   rŒ   Í   s   € ÷ ñ ˜Cð  Iñ r   c                	ó”  € \         P                  ! V4      pVP                  R 4      pRVP                  RR4      RVP                  RR4      RVP                  RR4      RVP                  R4      RVP                  R4      R \        V\        4      '       d   TM/ /pR	\
        R
\        R\        R\        /p\        V 4       F}  pVP                  V9  g   K  WVP                  ,          ! VP                  VP                  VP                  4      ;'       g    WVP                  ,          ! 4       4      WFP                  &   K  	  V ! R/ VB # )r‰   r1   r/   rz   ry   r{   rF   r†   r‡   r   r   rJ   r‚   r   )r�   ÚloadsrY   rZ   r[   r   r   rJ   r‚   r   ÚnameÚtypera   )ÚclsrA   Údatar‰   ÚkwargsÚcastsÚfs   &&     r   Ú	from_jsonÚLoopState.from_jsonÌ   sÿ   € ä�zŠz˜#‹ˆØ—‘˜Ó!ˆà�d—h‘h˜x¨Ó,Ø�d—h‘h˜x¨Ó2Ø�D—H‘H˜V ZÓ0Ø˜TŸX™X oÓ6Ø §¡Ð);Ó <Øœj¨´×5Ò5‘U¸2ð"
ˆð œ˜U¤C¨´%¸ÄÐFˆÜ˜–ˆAØ�v‰v˜VÖ#Ø!&§v¡v¦¨t¯x©x¸¿¹ÀÇ	Á	Ó/J×/]Ð/]Èe×TZÑTZÎmËoÓ!^�—v‘v“ñ ñ ‰}�V‰}Ðr   c               ó   € V ^8„  d   QhRR/# r‹   r   )r   s   "r   r   rŒ   ß   s   € ÷ Añ A˜sñ Ar   c                	ó¶   € V P                   R 8X  d2   V P                  '       d   R\        V P                  4       2MRpRV 2# R\        V P                  4       2# )Ú
self_pacedz, currently r/   z
self-pacedzevery )r{   r}   rS   r0   )r’   Úlives   & r   Úcadence_labelÚLoopState.cadence_labelß   sZ   € Ø�9‰9˜Ô$ØKO×K]×K]ÐK]�\¤/°$×2DÑ2DÓ"EÐ!FÑGÐceˆDØ ˜vÐ&Ð&Øœ¨×(=Ñ(=Ó>Ð?Ð@Ð@r   c               ó   € V ^8„  d   QhRR/# r‹   r   )r   s   "r   r   rŒ   å   s   € ÷ Xñ X ñ Xr   c                	ó¢   € V P                   R 8w  d   R# V P                  \        P                  ! 4       ,
          pV^ 8:  d   R# R\        V4       2# )ry   r/   zdue nowznext in )rz   r�   ÚtimerS   )r’   Ú	remainings   & r   Úremaining_labelÚLoopState.remaining_labelå   sE   € Ø�;‰;˜(Ô"ÙØ×$Ñ$¤t§y¢y£{Õ2ˆ	Ø%¨œNˆyÐW°(¼?È9Ó;UÐ:VÐ0WÐWr   r   )Ú__name__Ú
__module__Ú__qualname__Ú__firstlineno__Ú__doc__Ú__annotations__rz   r{   r0   r}   r2   r3   rm   rl   r~   r   r€   r�   rƒ   r„   r†   r‡   r   r[   r‰   r“   ÚclassmethodrŸ   r¥   r«   Ú__static_attributes__r   r   r   rx   rx   ¯   sà   ‡ á6àƒKØ€FˆCÓØ€Dˆ#ÓØ!Ð�eÓ!Ø€M�5ÓØ€Eˆ3ƒNØ€Eˆ3ƒOØ&€IˆsÓ&Ø€K�ÓØ€J�ÓØ€M�5ÓØ€K�Óð $Ð�tÓ#Ø "Ð˜#Ó"Ø#'€M�=Ó'Ø&*Ð�mÓ*ñ "°$Ô7€Eˆ>Ó7õ<ð ôó ðõ$A÷Xñ Xr   rx   zloop:c               ó    € V ^8„  d   QhRRRR/# )r   Ú
session_idr   r   r   )r   s   "r   r   r   ï   s   € ÷ )ñ )˜#ð ) #ñ )r   c                ó   € \          V  2# ©N)Ú_META_PREFIX©r¶   s   &r   Ú	_meta_keyr»   ï   s   € Üˆ^˜J˜<Ð(Ð(r   c               ó   € V ^8„  d   QhRR/# )r   r   zOptional[Any]r   )r   s   "r   r   r   ó   s   € ÷ ñ ˜ñ r   c                 ó|   €  ^ RI Hp  V ! 4       #   \         d"   p\        P	                  RT4        Rp?R# Rp?ii ; i)aB  The goals module's cached SessionDB, so goals/loops/heartbeats share one connection and
its off-loop bootstrap (a cold cache on the loop thread never runs ``SessionDB()`` inline).

The previous copy here did, which froze the loop for the init duration and dropped the first ``loop:*``
write (the /goal bug class, #88965).
)Ú_get_session_dbz,LoopManager: SessionDB bootstrap failed (%s)N)Úhermes_cli.goalsr¾   r\   ÚloggerÚdebug)Ú	_goals_dbÚexcs     r   r¾   r¾   ó   s8   € ðÝAñ ‹;Ðøô ô Ü�‰ÐCÀSÔIÝûðús   ‚ �;š6¶;c               ó   € V ^8„  d   QhRR/# )r   Úlabelr   r   )r   s   "r   r   r     s   € ÷ ñ �#ñ r   c                ór   €  V! 4       #   \          d#   p\        P                  RY4       Tu Rp?# Rp?ii ; i)zLRun one SessionDB call; any error is logged at debug and yields ``default``.zLoopManager: %s failed: %sN)r\   rÀ   rÁ   )rÅ   Úfnra   rÃ   s   &&& r   Ú_db_oprÈ     s2   € ðÙ‹tˆøÜô Ü�‰Ð1°5Ô>Ø�ûðús   ‚	 ‰6”1«6±6r/   c               ó$   € V ^8„  d   QhRRRRRR/# )r   rA   r   r¶   r   úOptional[LoopState]r   )r   s   "r   r   r     s"   € ÷ ñ �cð  sð Ð4Gñ r   c                óœ   €  \         P                  V 4      #   \         d*   pT'       d   \        P	                  RY4        Rp?R# Rp?ii ; i)zW``LoopState`` from stored JSON; None (warning when *session_id* given) on corrupt data.z3LoopManager: could not parse stored loop for %s: %sN)rx   rŸ   r\   rÀ   Úwarning)rA   r¶   rÃ   s   && r   Ú_parse_staterÍ     s>   € ðÜ×"Ñ" 3Ó'Ð'øÜô ßÜ�N‰NÐPÐR\ÔbÝûðús   ‚ —A¢AÁAc               ó    € V ^8„  d   QhRRRR/# )r   r¶   r   r   rÊ   r   )r   s   "r   r   r     s   € ÷ :ñ :˜#ð :Ð"5ñ :r   c                óˆ   a a€ S '       d   \        4       MRoSf   R# \        RVV 3R l4      pV'       d   \        VS 4      # R# )z4Load the loop for a session, or None if none exists.NÚget_metac                 ó8   <€ S P                  \        S4      4      # r¸   )rÐ   r»   )Údbr¶   s   €€r   Ú<lambda>Úload_loop.<locals>.<lambda>  s   ø€  R§[¡[´¸:Ó1FÔ%Gr   )r¾   rÈ   rÍ   )r¶   rA   rÒ   s   f @r   Ú	load_looprÕ     s8   ù€ ç(ŒÔ	¨d€BØ	‚zÙÜ
�ÕGÓ
H€Cß,/Œ<˜˜ZÓ(Ð9°TÐ9r   c               ó$   € V ^8„  d   QhRRRRRR/# )r   r¶   r   Ústaterx   r   ÚNoner   )r   s   "r   r   r     s&   € ÷ 
Tñ 
T˜#ð 
T ið 
T°Dñ 
Tr   c                ó„   a aa€ S '       g   R# \        4       oSf   ^ RIHp V! RRS 4       R# \        RVV V3R l4       R# )z5Persist a loop to SessionDB. No-op if DB unavailable.N)Ú_warn_dropped_writeÚLoopManagerÚloopÚset_metac                 óV   <€ S P                  \        S4      SP                  4       4      # r¸   )rÝ   r»   r“   )rÒ   r¶   r×   s   €€€r   rÓ   Úsave_loop.<locals>.<lambda>(  s   ø€ ˜rŸ{™{¬9°ZÓ+@À%Ç-Á-Ã/ÔRr   )r¾   r¿   rÚ   rÈ   )r¶   r×   rÚ   rÒ   s   ff @r   Ú	save_looprà     s3   ú€ çÙÜ	Ó	€BØ	‚zÝ8á˜M¨6°:Ô>ÙÜ
ˆ:ÖRÖSr   c               ó    € V ^8„  d   QhRRRR/# )r   r¶   r   r   rØ   r   )r   s   "r   r   r   +  s   € ÷ %ñ %˜3ð % 4ñ %r   c                óL   € \        V 4      pVe   RVn        \        W4       R# R# )zDMark a loop cleared in the DB (preserved for audit, status=cleared).NÚcleared)rÕ   rz   rà   )r¶   r×   s   & r   Ú
clear_looprä   +  s&   € ä�jÓ!€EØÒØ ˆŒÜ�*Ö$ñ r   c               ó   € V ^8„  d   QhRR/# )r   r   zlist[tuple[str, LoopState]]r   )r   s   "r   r   r   3  s   € ÷ ñ Ð6ñ r   c                 ó  a€ \        4       oSf   . # . p \        RV3R l. 4       F_  w  rV\        \        4      R pV'       d   V'       d   \	        V4      MRpVf   K:  VP
                  R8X  g   KM  V P                  W434       Ka  	  V # )zª``[(session_id, LoopState), ...]`` for every ACTIVE loop; ``[]`` on any DB error.

Used by the gateway's idle wakeup watcher, which scans for due loops on a coarse tick.
NÚlist_meta_prefixc                 ó.   <€ S P                  \        4      # r¸   )rç   r¹   )rÒ   s   €r   rÓ   Ú#list_active_loops.<locals>.<lambda><  s   ø€ °r×7JÑ7JÌ<Ô7Xr   ry   )r¾   rÈ   r@   r¹   rÍ   rz   rO   )Úoutr`   rA   r¶   r×   rÒ   s        @r   Úlist_active_loopsrë   3  s~   ø€ ô
 
Ó	€BØ	‚zØˆ	Ø')€CÜÐ-Ô/XÐZ\Ö]‰ˆØœœ\Ó*Ð+Ð,ˆ
ß%/·C”˜SÔ!¸TˆØÔ §¡°Ö!9Ø�J‰J˜
Ð*Ö+ñ	 ^ð
 €Jr   c               ó    € V ^8„  d   QhRRRR/# )r   rÒ   r   r   r‚   r   )r   s   "r   r   r   D  s   € ÷ ñ ˜cð  dñ r   c                ó¼   € V P                  \        4       FB  w  rV'       d   \        W!\        \        4      R 4      MRpVe   VP                  R8X  g   KA   R# 	  R# )u  True when *db* holds an ACTIVE ``loop:*`` row â€” or a row that cannot be parsed (unknown, so the
caller keeps its full scan). Unlike :func:`list_active_loops` this takes the store explicitly and
propagates read errors, so an idle gate can tell "empty" from "unavailable".Nry   TF)rç   r¹   rÍ   r@   rz   )rÒ   r`   rA   r×   s   &   r   Ústore_has_active_looprî   D  sN   € ð ×'Ñ'¬Ö5‰ˆß>A”˜S¤c¬,Ó&7Ð&8Ð"9Ô:ÀtˆØŠ=˜EŸL™L¨HÖ4Úñ 6ñ r   Úreasonc               ó(   € V ^8„  d   QhRRRRRRRR/# )r   Úold_session_idr   Únew_session_idrï   r   r‚   r   )r   s   "r   r   r   O  s*   € ÷ ñ ¨Cð Àð ÐQTð Ð^bñ r   c               ób  € V '       d   V'       d   W8X  d   R#  \        V 4      pVe   VP                  R8X  g   \        V4      e   R# \        W4       \        V 4       \        P                  RYT;'       g    R4       R#   \         d"   p\        P                  RT4        Rp?R# Rp?ii ; i)al  Carry a /loop from a parent session to its continuation. Best-effort, never raises.

Context compression rotates ``session_id`` to a fresh child; without this the loop silently
dies at the compaction boundary.

Copies the loop onto the new session and archives the old row as ``cleared`` so exactly one active loop
row exists per logical conversation. See #33618.
FNrã   z(LoopManager: migrated loop %s -> %s (%s)ÚrotationTz&LoopManager: loop migration failed: %s)rÕ   rz   rà   rä   rÀ   rÁ   r\   )rñ   rò   rï   r×   rÃ   s   &&$  r   Úmigrate_loop_to_sessionrõ   O  s�   € ÷ §°>Ô3SÙðÜ˜.Ó)ˆØŠ=˜EŸL™L¨IÔ5¼À>Ó9RÒ9^ÙÜ�.Ô(Ü�>Ô"Ü�‰Ø6Ø¨F×,@Ð,@°jô	
ñ øÜô Ü�‰Ð=¸sÔCÝûðús   š-B Á	7B ÂB.ÂB)Â)B.c               ó    € V ^8„  d   QhRRRR/# )r   Únr   r   r   r   )r   s   "r   r   r   j  s   € ÷ .ñ .�Cð .˜Cñ .r   c                ó&   € T  R V ^8w  d   R 2# R 2# )z tickr(   r/   r   )r÷   s   &r   Ú_ticks_labelrù   j  s%   € ØˆS�˜Q !œV�cÐ,Ð-Ð-¨Ð,Ð-Ð-r   c               ó    € V ^8„  d   QhRRRR/# )r   rï   r…   r   r   r   )r   s   "r   r   r   n  s   € ÷ .ñ .�-ð . Cñ .r   c                ó    € V '       d   R V  2# R# )õ    â€” r/   r   ©rï   s   &r   Ú_dashrþ   n  s   € ß%ˆU�6�(ÐÐ-¨2Ð-r   c               ó    € V ^8„  d   QhRRRR/# )r   Úresponser   r   r‚   r   )r   s   "r   r   r   r  s   € ÷ Mñ M¨ð M°ñ Mr   c                óV   € \        V 4      ;'       d    \        P                  V 4      RJ# )zBTrue when the agent ended its reply with the LOOP_COMPLETE marker.N)r‚   Ú_LOOP_COMPLETE_REr8   )r   s   &r   Úresponse_signals_completer  r  s%   € ä�‹>×LÐLÔ/×6Ñ6°xÓ@ÈÐLÐLr   c               ó    € V ^8„  d   QhRRRR/# )r   r   r   r   r   )r   s   "r   r   r   w  s   € ÷ Gñ G˜sð G sñ Gr   c                óz  € T ;'       g    RP                  4       P                  4       p\        P                  ! RRV4      p\        P                  ! RRV4      p\        P                  ! RRV4      p\        P                  ! RRV4      p\        P
                  ! VP                  RR4      4      P                  4       # )	z Digest for self-paced change detection; whitespace-normalized with clock/timestamp/duration
tokens stripped so 'checked at 14:02:33' doesn't defeat the backoff.r/   z\d{1,2}:\d{2}(:\d{2})?z\d{4}-\d{2}-\d{2}zH\b\d+(\.\d+)?\s*(s|sec|secs|seconds|m|min|mins|minutes|h|hr|hrs|hours)\bz\s+Ú zutf-8Úreplace)r#   r?   r7   ÚsubÚhashlibÚsha256ÚencodeÚ	hexdigest)r   r,   s   & r   Ú_digest_responser  w  s‘   € ð �NˆN˜×!Ñ!Ó#×)Ñ)Ó+€DÜ�6Š6Ð+¨R°Ó6€DÜ�6Š6Ð&¨¨DÓ1€DÜ�6Š6Ð]Ð_aÐcgÓh€DÜ�6Š6�&˜#˜tÓ$€DÜ�>Š>˜$Ÿ+™+ g¨yÓ9Ó:×DÑDÓFÐFr   c                  ó   € ] tR tRtRtR R lt]R R l4       tR R ltR	 R
 lt	R R lt
R R ltR R ltRRR^ RRRR/R R lltR*R R lltR R ltR R ltR+R R  lltR! R" ltR# R$ ltR% R& ltR' R( ltR)tR# ),rÛ   i‚  a  Per-session /loop state + tick decisions.

Drivers call ``set``/``pause``/``resume``/``clear`` for user controls, ``is_due()`` (cheap,
in-memory), ``fire_tick()`` to claim a tick and get the wakeup message, ``complete_tick()`` to
evaluate the finished turn, and ``status_line()``.
c               ó   € V ^8„  d   QhRR/# )r   r¶   r   r   )r   s   "r   r   ÚLoopManager.__annotate__Š  s   € ÷ Añ A 3ñ Ar   c                	ó2   € Wn         \        V4      V n        R # r¸   )r¶   rÕ   Ú_state)r’   r¶   s   &&r   Ú__init__ÚLoopManager.__init__Š  s   € Ø$ŒÜ+4°ZÓ+@ˆŽr   c               ó   € V ^8„  d   QhRR/# ©r   r   rÊ   r   )r   s   "r   r   r  �  s   € ÷ ñ Ð*ñ r   c                	ó   € V P                   # r¸   )r  r‘   s   &r   r×   ÚLoopManager.stateŽ  s   € à�{‰{Ðr   c               ó   € V ^8„  d   QhRR/# ©r   r   rØ   r   )r   s   "r   r   r  ’  s   € ÷ 1ñ 1˜ñ 1r   c                ó:   € \        V P                  4      V n        R# )zARe-read state from the DB (cross-process safety for the gateway).N)rÕ   r¶   r  r‘   s   &r   ÚrefreshÚLoopManager.refresh’  s   € ä §¡Ó0ˆŽr   c               ó   € V ^8„  d   QhRR/# ©r   r   r‚   r   )r   s   "r   r   r  –  s   € ÷ Jñ J˜4ñ Jr   c                	ó`   € V P                   R J;'       d    V P                   P                  R8H  # )Nry   ©r  rz   r‘   s   &r   Ú	is_activeÚLoopManager.is_active–  s(   € Ø�{‰{ $Ð&×IÐI¨4¯;©;×+=Ñ+=ÀÑ+IÐIr   c               ó   € V ^8„  d   QhRR/# r  r   )r   s   "r   r   r  ™  s   € ÷ Vñ V˜$ñ Vr   c                	ó`   € V P                   R J;'       d    V P                   P                  R9   # )N>   ry   Úpausedr!  r‘   s   &r   Úhas_loopÚLoopManager.has_loop™  s)   € Ø�{‰{ $Ð&×UÐU¨4¯;©;×+=Ñ+=ÐAUÑ+UÐUr   c               ó   € V ^8„  d   QhRR/# )r   r   rx   r   )r   s   "r   r   r  œ  s   € ÷ ñ �yñ r   c                	óZ   € \        V P                  V P                  4       V P                  # r¸   )rà   r¶   r  r‘   s   &r   Ú_saveÚLoopManager._saveœ  s   € Ü�$—/‘/ 4§;¡;Ô/Ø�{‰{Ðr   c               ó   € V ^8„  d   QhRR/# r‹   r   )r   s   "r   r   r     s   € ÷ 8ñ 8˜Sñ 8r   c                	ó®  € V P                   pVe   VP                  R8X  d   R# \        VP                  4      pVP                  '       d   VP                   RVP                   R2.pM3VP
                  '       d   VP                   RVP
                   R2.pMV.pVP                  '       d   VP                  RVP                   24       VP                  4        RRP                  V4       2pVP                  R8X  dH   VP                  4       pVP                  '       d   R	MV'       d   RV 2MR
pRV V RVP                   2# VP                  R8X  d(   RV \        VP                  4       RVP                   2# VP                  R8X  d(   RV \        VP                  4       RVP                   2# RVP                   RV RVP                   2# )Nrã   z6No loop set. Start one with /loop [interval] <prompt>.Ú/ú runsz budgetzuntil: ú, ry   z, wakeup runningr/   u   â†» Loop (active, ú): r&  u   â�¸ Loop (paused, Údoneu   âœ“ Loop finished (zLoop ()r  rz   rù   r~   r2   rl   r3   rO   r¥   rP   r«   rƒ   r1   rþ   r†   r‡   )r’   r(   ÚfiredÚcapsÚmetarª   Útails   &      r   Ústatus_lineÚLoopManager.status_line   sš  € Ø�K‰KˆØŠ9˜Ÿ™ IÔ-ÙKÜ˜QŸ]™]Ó+ˆØ�7�7ˆ7Ø—}‘}�o Q q§w¡w i¨uÐ5Ð6‰DØ�[�[ˆ[Ø—}‘}�o Q q§{¡{ m°7Ð;Ð<‰Dà�7ˆDØ�7�7ˆ7Ø�K‰K˜' !§'¡' Ð+Ô,Ø—/‘/Ó#Ð$ B t§y¡y°£Ð&7Ð8ˆØ�8‰8�xÔØ×)Ñ)Ó+ˆIØ)*×)<×)<Ð)<Ñ%×W`ÀRÈ	À{ÑCSÐfhˆDØ'¨ v¨d¨V°3°q·x±x°jÐAÐAØ�8‰8�xÔØ'¨ v¬e°A·O±OÓ.DÐ-EÀSÈÏÉÈ
ÐSÐSØ�8‰8�vÔØ(¨¨´°a×6HÑ6HÓ0IÐ/JÈ#ÈaÏhÉhÈZÐXÐXØ˜Ÿ™�z  D 6¨¨Q¯X©X¨JÐ7Ð7r   r0   Nr2   r3   r/   r‰   c               ó0   € V ^8„  d   QhRRRRRRRRRR	R
R/# )r   r1   r   r0   r   r2   r   r3   r‰   úOptional[dict[str, str]]r   rx   r   )r   s   "r   r   r  ¸  sF   € ÷ ñ àðð (ð	ð
 ðð ðð (ðð 
ñr   c               ó.  € T;'       g    RP                  4       pV'       g   \        R4      h\        P                  ! 4       pVRJ pV'       d   RM%\        \	        \        V4      \        4       4      4      p\        TV'       d   RMRTV'       d   \        \        4       4      MT\	        ^ \        T;'       g    ^ 4      4      T;'       g    RP                  4       \        4       TT\        T;'       g    / 4      R7
      p	W�n        V P                  4       # )z=Start a new loop (replaces any existing one for the session).r/   zloop prompt is emptyNr|   r£   rF   )
r1   r{   r0   r}   r2   r3   rl   r   r�   r‰   )r#   r:   r©   rJ   rM   r   rh   rx   rq   rn   r[   r  r+  )
r’   r1   r0   r2   r3   r‰   Únowr£   rF   r×   s
   &&$$$$    r   ÚsetÚLoopManager.set¸  sÕ   € ð —,�,˜B×%Ñ%Ó'ˆßÜÐ3Ó4Ð4ä�iŠi‹kˆØ%¨Ð-ˆ
ß$‘3¬%´´CÐ8HÓ4IÔK_ÓKaÓ0bÓ*cˆÜØß!+‘°Ø%ß?Iœ%Ô 8Ó :Ô;ÈxÜ�aœ˜UŸZ˜Z a›Ó)Ø—;�;˜B×%Ñ%Ó'Ü'Ó)ØØÜ�u—{�{ Ó#ô
ˆð ŒØ�z‰z‹|Ðr   c               ó    € V ^8„  d   QhRRRR/# )r   rï   r   r   rÊ   r   )r   s   "r   r   r  Ø  s   € ÷ ñ ˜Cð Ð4Gñ r   c                	óœ   € V P                   pV'       d   VP                  R9  d   R# RVRuVn        Vn        Vn        V P	                  4       # )ry   r&  NF>   ry   r&  )r  rz   r†   rƒ   r+  )r’   rï   r(   s   && r   ÚpauseÚLoopManager.pauseØ  s@   € Ø�K‰Kˆß�A—H‘HÐ$8Ô8ÙØ9AÀ6È5Ð6ˆŒ�!”/ 1Ô#6Ø�z‰z‹|Ðr   c               ó   € V ^8„  d   QhRR/# r  r   )r   s   "r   r   r  ß  s   € ÷ ñ Ð+ñ r   c                	óV  € V P                   pV'       d   VP                  R 8X  d   R# RRRuVn        Vn        Vn        VP                  ;'       g    VP
                  ;'       g    \        4       p\        P                  ! 4       \        VR4      ,           Vn	        V P                  4       # )rã   Nry   Fg      @)r  rz   r†   rƒ   r}   r0   rq   r©   Úminr�   r+  )r’   r(   Údelays   &  r   ÚresumeÚLoopManager.resumeß  sz   € Ø�K‰Kˆß�A—H‘H 	Ô)ÙØ9AÀ4ÈÐ6ˆŒ�!”/ 1Ô#6à—‘×SÐS 1×#5Ñ#5×SÐSÔ9QÓ9SˆÜŸ	š	›¤c¨%°£oÕ5ˆŒØ�z‰z‹|Ðr   c               ó   € V ^8„  d   QhRR/# r  r   )r   s   "r   r   r  é  s   € ÷ ñ �tñ r   c                	ó¬   € V P                   e   V P                   P                  R8X  d   R# RV P                   n        V P                  4        R V n         R# )Nrã   FT)r  rz   r+  r‘   s   &r   ÚclearÚLoopManager.clearé  s?   € Ø�;‰;Ò $§+¡+×"4Ñ"4¸	Ô"AÙØ&ˆ�‰ÔØ�
‰
ŒØˆŒÙr   c               ó    € V ^8„  d   QhRRRR/# )r   r=  zOptional[float]r   r‚   r   )r   s   "r   r   r  ñ  s   € ÷ 
ñ 
˜/ð 
°Tñ 
r   c                óâ   € V P                   pVRJ;'       dY    VP                  R8H  ;'       dB    VP                  '       * ;'       d)    Ve   TM\        P                  ! 4       VP                  8¬  # )z>Cheap check: active, not mid-wakeup, and the clock has passed.Nry   )r  rz   rƒ   r©   r�   )r’   r=  r(   s   && r   Úis_dueÚLoopManager.is_dueñ  si   € à�K‰Kˆà�TˆM÷ Kð K˜aŸh™h¨(Ñ2÷ Kð K¸1×;NÑ;NÔ7N÷ Kð KØš‘¬T¯YªY«[¸Q¿]¹]ÑJð	
r   c               ó   € V ^8„  d   QhRR/# )r   r   r…   r   )r   s   "r   r   r  ù  s   € ÷ dñ d˜=ñ dr   c                óô  € V P                   pVe   V P                  4       '       g   R# V;P                  ^,          un        \        P                  ! 4       Vn        RVn        VP                  VP                  ;'       g    VP                  ;'       g    \        4       ,           Vn	        V P                  4        VP                  P                  4       P                  R4      '       d   VP                  P                  4       # VP                  R8X  d   RVP!                  4        2MRpVP"                  '       d   \$        M\&        pVP)                  VP                  W!P                  VP"                  R7      # )a@  Claim a due tick; returns the message to inject, or None.

The message is the wakeup-framed prompt, or the raw command when the loop's prompt is
itself a slash command (``/loop 10m /recap``). Marks ``awaiting_response`` so the tick
can't double-fire; drivers MUST follow up with ``complete_tick`` (or ``abandon_tick``).
NTr/  rF   r1  z, self-paced)ÚtickÚcadencer1   r3   )r  rP  r~   r©   r€   rƒ   r}   r0   rq   r�   r+  r1   ÚlstripÚ
startswithr#   r{   r¥   r3   Ú!WAKEUP_PROMPT_WITH_UNTIL_TEMPLATEÚWAKEUP_PROMPT_TEMPLATEr   )r’   r(   rU  Útemplates   &   r   Ú	fire_tickÚLoopManager.fire_tickù  sþ   € ð �K‰KˆØŠ9˜DŸK™KŸMšMÙØ	�Š˜Õ�ÜŸ)š)›+ˆŒØ"ˆÔð Ÿ™¨1¯?©?×+nÐ+n¸a×>PÑ>P×+nÐ+nÔTlÓTnÕoˆŒØ�
‰
Œà�8‰8�?‰?Ó×'Ñ'¨×,Ò,Ø—8‘8—>‘>Ó#Ð#Ø./¯f©f¸
Ô.B�B�q—‘Ó(Ð)Ñ*ÈˆØ89¿¿¸Õ4ÔE[ˆØ�‰ A§M¡M¸7Ï8É8Ð[\×[bÑ[bˆÓcÐcr   c               ó   € V ^8„  d   QhRR/# r  r   )r   s   "r   r   r    s   € ÷ ñ ˜dñ r   c                óÀ   € V P                   pVe   VP                  '       g   R# RVn        \        ^ VP                  ^,
          4      Vn        V P	                  4        R# )z<Roll back a fired tick whose injection failed (nothing ran).NF)r  rƒ   rM   r~   r+  )r’   r(   s   & r   Úabandon_tickÚLoopManager.abandon_tick  sF   € à�K‰KˆØŠ9˜A×/×/Ð/ÙØ#ˆÔÜ˜A˜qŸ}™}¨qÕ0Ó1ˆŒØ�
‰
Žr   c               ó(   € V ^8„  d   QhRRRRRRRR/# )r   rz   r   rï   Úmessager   r-   r   )r   s   "r   r   r    s.   € ÷ 	Yñ 	Y˜Cð 	Y¨ð 	Y°sð 	Y¸~ñ 	Yr   c                ó‚   € V P                   pWn        VR8X  d   W$n        MW$n        V P	                  4        RVRRRVRV/# )zTPersist a terminal (``done``) or recoverable (``paused``) stop and build the result.r3  rz   ÚstoppedTrï   rb  )r  rz   r‡   r†   r+  )r’   rz   rï   rb  r(   s   &&&& r   Ú_stopÚLoopManager._stop  sD   € à�K‰KˆØŒØ�VÔØ!'Õà$ŒOØ�
‰
ŒØ˜& )¨T°8¸VÀYÐPWÐXÐXr   c               ó    € V ^8„  d   QhRRRR/# )r   Úlast_responser   r   r-   r   )r   s   "r   r   r  %  s   € ÷ =añ =a¨3ð =a°>ñ =ar   c           	     ó  € V P                   pVe   VP                  '       g   RV'       d   VP                  MRRRRRRR/# RVn        \        P                  ! 4       p\	        VP
                  4      p\        V4      '       d   V P                  R	R
RV R24      # VP                  '       d…   T;'       g    RP                  4       '       de    ^ RI
Hp V! VP                  V4      w  rgr‰p
VR	8X  d   V P                  R	RV 2RV RV 24      # VR8X  d   RV 2pV P                  RVRV R24      # VP                  '       dX   VP
                  VP                  8¼  d=   V P                  R	RVP                   R2RVP                   RVP                   R24      # VP                   '       de   VP
                  VP                   8¼  dJ   V P                  RRVP
                   RVP                    R2RVP
                   RVP                    R24      # VP"                  R8X  dz   \%        V4      p\'        4       pV'       dF   WÒP(                  8X  d6   \+        \-        VP.                  V4      ^,          \1        4       4      Vn        M\3        V4      Vn        WÒn        MVP4                  Vn        W2P.                  ,           Vn        V P9                  4        RR RRRR!RR/#   \         d%   pRR\        T4      P                   2rv Rp?ELRp?ii ; i)"zéEvaluate the finished wakeup turn and schedule what's next.

Returns ``{"status": "active|done|paused", "stopped": bool, "reason": str, "message": str}``;
``message`` is a user-visible one-liner, "" in the common still-looping case.
Nrz   rd  Frï   zno tick in flightrb  r/   r3  z#agent signaled the task is completeu   âœ“ Loop finished after u    â€” task complete.)Ú
judge_goalÚcontinuezjudge unavailable: zstop condition met: rü   Úblockedz$stop condition judged unachievable: r&  u   â�¸ Loop paused â€” z3. /loop resume to keep going, /loop stop to end it.zcompleted the requested r0  u   âœ“ Loop finished â€” ran r/  z times.ztick budget exhausted (Ú)zP ticks used (loops.max_ticks). /loop resume to keep going, /loop stop to end it.r£   ry   zloop continues)r  rƒ   rz   r©   rù   r~   r  re  r3   r#   r¿   rj  r\   r™   r­   r2   rl   r{   r  rq   r„   rF  rM   r}   ru   rJ   r0   r�   r+  )r’   rh  r(   r=  Úticksrj  Úverdictrï   Ú_pfÚ_waitÚ_tfrÃ   ÚwhyÚdigestrb   s   &&             r   Úcomplete_tickÚLoopManager.complete_tick%  sÖ  € ð �K‰KˆØŠ9˜A×/×/Ð/Ø¯!˜aŸhšh°°yÀ%ÈÐSfÐhqÐsuÐvÐvØ#ˆÔÜ�iŠi‹kˆÜ˜QŸ]™]Ó+ˆô % ]×3Ò3Ø—:‘:˜fÐ&KØ 8¸¸Ð?RÐSóUð Uð �7�7ˆ7˜×+Ð+¨×2Ñ2×4Ò4ðYÝ7á3=¸a¿g¹gÀ}Ó3UÑ0� ¨Sð ˜&Ô Ø—z‘z &Ð,@ÀÀÐ*IØ$<¸U¸GÀ5ÈÈÐ"QóSð Sà˜)Ô#à<¸V¸HÐE�Ø—z‘z (¨CØ$8¸¸Ð=pÐ"qósð sð �7�7ˆ7�q—}‘}¨¯©Ô/Ø—:‘:˜fÐ(@ÀÇÁÀ	ÈÐ&OØ :¸1¿7¹7¸)À1ÀQÇWÁWÀIÈWÐUóWð Wð �;�;ˆ;˜1Ÿ=™=¨A¯K©KÔ7Ø—:‘:ØÐ3°A·M±M°?À!ÀAÇKÁKÀ=ÐPQÐRØ& q§}¡} o°Q°q·{±{°mð DWð Wóð ð �6‰6�\Ô!Ü% mÓ4ˆFÜ,Ó.ˆEß˜&×$:Ñ$:Ô:Ü"%¤c¨!¯/©/¸5Ó&AÀAÕ&EÔGaÓGcÓ"d�•ä"'¨£,�”Ø%+Õ"à×0Ñ0ˆAŒOØŸo™oÕ-ˆŒØ�
‰
ŒØ˜( I¨u°hÐ@PÐR[Ð]_Ð`Ð`øôK ô YØ",Ð0CÄDÈÃI×DVÑDVÐCWÐ.XžûðYús   ÃK ËL
Ë&LÌL
)r  r¶   )úuser-pausedr¸   )r­   r®   r¯   r°   r±   r  Úpropertyr×   r  r"  r'  r+  r8  r>  rB  rH  rL  rP  r[  r_  re  ru  r´   r   r   r   rÛ   rÛ   ‚  s›   † ñõAð ôó ðõ1õJõVõõ8ð0ð +/ð	ð
 ðð ðð +/÷÷@õõ÷
õdõ0õ	Y÷=añ =ar   rÛ   c               ó    € V ^8„  d   QhRRRR/# )r   r¶   r   r   r‚   r   )r   s   "r   r   r   e  s   € ÷ ñ  cð ¨dñ r   c                ó    €  ^ RI Hp V! V R7      pVP                  4       ;'       d    VP                  4       '       * #   \         d     R# i ; i)züTrue when an ACTIVE, non-parked /goal should defer this session's /loop tick.

Both features inject synthetic turns at idle boundaries; interleaving them would burn the
goal's turn budget. Parked (waiting), paused, or done goals do NOT block the loop.
)ÚGoalManagerrº   F)r¿   r{  r"  Ú
is_waitingr\   )r¶   r{  Úmgrs   &  r   Úgoal_blocks_loop_tickr~  e  sD   € ðÝ0á ZÔ0ˆØ�}‰}‹×7Ð7 s§~¡~Ó'7Ô#7Ð7øÜô Úðús   ‚%> ¨> ¾AÁAu  Usage: /loop [interval] <prompt> [--times N] [--until <condition>]
  /loop 5m check the deploy status      â€” first run now, then every 5m
  /loop every 10m /recap                â€” loop a slash command
  /loop keep fixing tests until green   â€” self-paced (backs off while output is unchanged)
  /loop 2m poll CI --times 30           â€” stop after 30 runs
  /loop 5m watch the queue --until queue is empty
Controls: /loop status Â· /loop pause Â· /loop resume Â· /loop stop
The loop also stops itself when the agent replies with Ú.c               ó    € V ^8„  d   QhRRRR/# ©r   r}  rÛ   r   r   r   )r   s   "r   r   r   �  s   € ÷ rñ r�{ð r sñ rr   c                óR   € V P                  R R7      pVf   R# RVP                   R2# )rw  rý   zNo loop set.u   â�¸ Loop paused: z
Use /loop resume to continue.)rB  r1   ©r}  r×   s   & r   Ú_pause_outputr„  �  s0   € Ø�I‰I˜]ˆIÓ+€EØ"š]ˆ>ÐqÐ2CÀEÇLÁLÀ>ÐQpÐ0qÐqr   c               ó    € V ^8„  d   QhRRRR/# r�  r   )r   s   "r   r   r   †  s   € ÷ tñ t˜ð t¨ñ tr   c                ón   € V P                  4       pVf   R# RVP                  4        RVP                   2# )NzNo loop to resume.u   â–¶ Loop resumed (r2  )rH  r¥   r1   rƒ  s   & r   Ú_resume_outputr‡  †  s>   € Ø�J‰J‹L€EØ#(¢=ÐÐsÐ8JÈ5×K^ÑK^ÓK`ÐJaÐadÐej×eqÑeqÐdrÐ6sÐsr   c                ó"   € V P                  4       # r¸   )r8  ©r}  s   &r   rÓ   rÓ   �  s   € °·±Ô0Ar   rB  rH  c                ó6   € V P                  4       '       d   R # R# )u   âœ“ Loop stopped.zNo active loop.)rL  r‰  s   &r   rÓ   rÓ   �  s   € ÐTW×T]ÑT]×T_ÒT_Ð=PÐ=vÐevÐ=vr   c                ó   € \         # r¸   )Ú	LOOP_HELPr‰  s   &r   rÓ   rÓ   ‘  s   € ½)r   r‰   c               ó(   € V ^8„  d   QhRRRRRRRR/# )	r   r}  rÛ   Úargsr   r‰   r;  r   r-   r   )r   s   "r   r   r   •  s0   € ÷ 79ñ 79Ø	ð79à
ð79ð $ð	79ð
 ñ79r   c          	     ó’  € T;'       g    RP                  4       p\        P                  VP                  4       4      pVe   RV! V 4      RR/# \	        V4      pVR,          '       d%   VR,          R8X  d   RRRR/# RR	VR,           2RR/# V P                  4       p V P                  VR
,          VR,          VR,          VR,          VR7      pRTP                  4        RTP                   2.p	T'       d   T	P                  R4       TR,          eA   TR,          TP                  8  d)   T	P                  R\        TP                  4       R24       TP                  R8X  d=   T	P                  R\        TP                  4       R\        \        4       4       R24       TP                   '       d6   T	P                  RTP                    RTP                   ^8w  d   RMR R24       TP"                  '       d   T	P                  RTP"                   24       TP                   '       g2   TP$                  '       d    T	P                  RTP$                   R24       TP&                  R8X  d   R MTP)                  4       p
T	P                  R!T
 R"24       RR#P+                  T	4      RR$/#   \         d   pRR	T 2RR/u Rp?# Rp?ii ; i)%u  Surface-agnostic handler for ``/loop <args>`` â†’ ``{"output": str, "created": bool}``.

``output`` is printed/sent verbatim by each surface. ``route`` is stored on new loops so the
gateway's idle watcher can inject wakeups into the right chat; CLI/TUI pass None.
r/   NÚoutputÚcreatedFr4   r5   u4   Usage: /loop [interval] <prompt> â€” see /loop help.z/loop: r1   r0   r2   r3   )r0   r2   r3   r‰   u   â†» Loop set (r2  z-(replaced the previous loop for this session)z(interval raised to the u(    minimum â€” loops.min_interval_seconds)r£   zSelf-paced: first check in z; backs off up to z while nothing changes.zRuns z timer(   z, then stops.zStops when: zBackstop budget: z( ticks (loops.max_ticks; 0 = unlimited).ry   z$fires now, then on the cadence abovezFirst wakeup u4   . Controls: /loop status Â· pause Â· resume Â· stop.Ú
T)r#   Ú_CONTROL_COMMANDSrY   r?   rG   r'  r>  r:   r¥   r1   rO   r0   rS   r{   r}   ru   r2   r3   rl   rz   r«   rP   )r}  rŽ  r‰   ÚargÚcontrolÚparsedÚ	replacingr×   rÃ   ÚlinesÚfirsts   &&$        r   Údispatch_loop_commandrš  •  s˜  € ð �:ˆ:�2×
Ñ
Ó
€CÜ×#Ñ# C§I¡I£KÓ0€GØÒØ™' #›,¨	°5Ð9Ð9ä˜SÓ!€FØˆg‡„Ø�'�?˜gÔ%ØÐTÐV_ÐafÐgÐgØ˜G F¨7¥OÐ#4Ð5°yÀ%ÐHÐHà—‘“€Ið	=Ø—‘Ø�8ÕØ#Ð$6Õ7Ø˜•/Ø˜•/Øð ó 
ˆð ˜e×1Ñ1Ó3Ð4°C¸¿¹°~ÐFÐG€EßØ�‰ÐDÔEØÐ Õ!Ò-°&Ð9KÕ2LÈu×OeÑOeÔ2eØ�‰Ø&¤°u×7MÑ7MÓ'NÐ&Oð P*ð *ô	
ð ‡z�z�\Ô!Ø�‰Ø)¬/¸%×:MÑ:MÓ*NÐ)Oð PÜ.Ô/IÓ/KÓLÐMÐMdðfô	
ð ‡{‡{€{Ø�‰�u˜UŸ[™[˜M¨°e·k±kÀQÔ6F©sÈBÐ.OÈ}Ð]Ô^Ø‡{‡{€{Ø�‰�| E§K¡K =Ð1Ô2Ø�;�;ˆ;˜5Ÿ?Ÿ?˜?Ø�‰Ð(¨¯©Ð(9Ð9aÐbÔcØ6;·l±lÀhÔ6NÑ2ÐTY×TiÑTiÓTk€EØ	‡L�L�=  Ð'[Ð\Ô]Ø�d—i‘i Ó&¨	°4Ð8Ð8øô1 ô =Ø˜G C 5˜/¨9°eÐ<Õ<ûð=ús   Â2J( Ê(KÊ3KÊ;KËKi„  r¸   )r/   )r/   rz   )ÚstoprL  Úcancel)Úhelpz--helpz-h)rm   ri   ÚLOOP_COMPLETE_MARKERrY  rX  rÛ   rx   rä   rš  rS   r~  rë   rÕ   rõ   r*   rG   r  rà   )Er±   Ú
__future__r   r	  r�   Úloggingr7   r©   Údataclassesr   r   r   r   Útypingr   r   r	   r
   r   Ú	getLoggerr­   rÀ   ri   rm   rr   rv   rž  ÚcompileÚescaper  Ú
IGNORECASEr!   rY  rX  r*   rG   rS   r^   rd   rh   rn   rq   ru   rx   r¹   r»   r¾   rÈ   rÍ   rÕ   rà   rä   rë   rî   rõ   rù   rþ   r  r  rÛ   r~  rŒ  r„  r‡  r[   Úfromkeysr“  rš  Ú__all__r   r   r   Ú<module>r©     s&  ðñõ #ã Û Û Û 	Û ß 8Ó 8ß 3Õ 3à	×	Ò	˜8Ó	$€ð
  "Ð àÐ ð $&Ð  Ø%,Ð "ð 'Ð à—J’JØ�2—9’9Ð1Ó2Õ2°_ÕDóÐ ð —Z’ZØ0°"·-±-óÐ ðBð ÐÐEðGð ð	"ð #7Ð!7ð 8ð
ð "õ
(õ)cõX	õõõPõ:õYõlð
 ÷9Xð 9Xó ð9Xðx €õ)õ÷÷õ:õ
Tõ%õõ"ðÐWY÷ õ6.õ.õMõ
G÷`añ `aõFð >ð Ð˜Aðð 
õrõ
tðØ
‡m�m�NÑ$AÓBðàˆ]Øˆnñð ‡m�mÐ/Ñ1vÓwð	ð
 ‡m�mÐ,Ñ.CÓDðÐ ð79ð '+÷	79òt‚r   