+
    kHàj^T ã                  óœ  a € 0 t $ R t^ RIHt ^ RIt^ RIt^ RIt^ RIt^ RIt^ RI	t	^ RI
t
^ RIHt ^ RIHtHt ^ RIHt ^ RIHt ^ RIHt ^ RIHtHtHtHt ^ R	IHtHtHtHtH t H!t!H"t"H#t#H$t% ^ R
I&H't' ^ RI(H)t)H*t* ^ RI+H,t, ^ RI+H-t- ^ RI.H/t/ ^ RI+H0t1 ^ RI+H2t3 ^ RI+H4t5 ^ RI+H6t7 ^ RI+H8t9 ^ RI:H;t;H<t<H=t= ]P|                  ! ]?4      t@]! 4       tA] ! RRR7      tBR R ltCR R ltDR R ltER R ltFERSR R  lltG]R! R" l4       tHR# R$ ltIR% R& ltJR' R( ltKR) R* ltLR+ R, ltMR- R. ltNR/ R0 ltO]R1 R2 l4       tP]! ]PR3]Q4      tR]R4 R5 l4       tS]T! ]-Pª                  P­                  4       4      tWR6]XR7&   ^ÈtYR8RR9R/R: R; lltZR< R= lt[R> R? lt\ERSR@ RA llt]RB RC lt^RD RE lt_RF RG lt`RH RI lta] ! RRJR7      ] ! RK4      ]B] ! RRLR7      ] ! RRMR7      3RN RO lltb]/! ]b4      tc]APÉ                  RP4      ] ! RRJR7      ] ! RK4      ]B] ! RRLR7      ] ! RRMR7      3RQ RR ll4       te]APÉ                  RS4      ] ! R4      ] ! RRTR7      ] ! RRUR7      3RV RW ll4       tf ! RX RY])4      tg]APÑ                  RZ4      ] ! R4      3R[ R\ ll4       ti]APÉ                  R]4      ] ! R4      3R^ R_ ll4       tj]APÑ                  R]4      ]! R`4      ] ! R4      ]! R4      3Ra Rb ll4       tk]APÉ                  Rc4      ] ! R4      3Rd Re ll4       tl]APÛ                  Rc4      ] ! R4      3Rf Rg ll4       tn ! Rh Ri])4      to ! Rj Rk])4      tp ! Rl Rm]q4      trRntsRo Rp lttRqRr RsRt RuRv RwRx RyRz R{R| R}R~ /tuR]XR€&   R� R‚ ltvRƒ R„ ltwR… R† ltxR‡ Rˆ ltyR‰ ]xRŠ3R‹ ]yRŒ33tzR� RŽ lt{R� R� lt|R‘ R’ lt}]APý                  RS4      ] ! R4      3R“ R” ll4       t]APÛ                  RS4      ] ! R4      3R• R– ll4       t€R— R˜ lt�R™ Rš lt‚ ! R› Rœ])4      tƒ]APÑ                  R�4      ] ! R4      3Rž RŸ ll4       t„ ! R  R¡])4      t…]APÑ                  R¢4      ] ! R4      3R£ R¤ ll4       t†]APÛ                  R¢4      ] ! R`4      ] ! R`4      ] ! R4      3R¥ R¦ ll4       t‡R§ R¨ ltˆ]APÑ                  R©4      ] ! R4      3Rª R« ll4       t‰]APÉ                  R¬4      ]B] ! RR­R7      3R® R¯ ll4       tŠ ^ RI‹tŒ]APÉ                  R°4      ]B3R± R² ll4       tŽ]APÉ                  R³4      ]B3R´ Rµ ll4       t�]APÉ                  R¶4      ]B3R· R¸ ll4       t� ! R¹ Rº])4      t‘]APÑ                  R»4      ]B3R¼ R½ ll4       t’ ! R¾ R¿])4      t“]APÑ                  RÀ4      ] ! R4      3RÁ RÂ ll4       t” ! RÃ RÄ])4      t•]APÑ                  RÅ4      ] ! R4      3RÆ RÇ ll4       t– ! RÈ RÉ])4      t—]APÑ                  RÊ4      ] ! R4      3RË RÌ ll4       t˜RÍt™ ! RÎ RÏ])4      tš]APÑ                  RÐ4      RÑ RÒ l4       t›]APÑ                  RÓ4      ] ! R4      3RÔ RÕ ll4       tœRÖ R× lt�RØ RÙ ltžRÚ RÛ ltŸ]APÉ                  RÜ4      RÝ 4       t RÞ Rß lt¡Rà Rá lt¢Râ Rã lt£]APÉ                  Rä4      ] ! R4      ] ! R4      3Rå Ræ ll4       t¤]APÑ                  Rç4      ] ! R4      3Rè Ré ll4       t¥]APÛ                  Rç4      ] ! R4      3Rê Rë ll4       t¦]APÉ                  Rì4      ] ! R4      3Rí Rî ll4       t§]APÉ                  Rï4      ] ! R4      3Rð Rñ ll4       t¨]APÉ                  Rò4      ] ! R^RóRô7      ] ! R4      3Rõ Rö ll4       t©]APÑ                  R÷4      ] ! RK4      ] ! ^RøRù7      ] ! R4      3Rú Rû ll4       tª]APÉ                  Rü4      Rý 4       t« ! Rþ Rÿ])4      t¬ ! ER  ER])4      t­ ! ER ER])4      t® ! ER ER])4      t¯ER ER lt°ER ER	 lt±ER
 ER lt²ER ER lt³ER ER lt´ER ER ltµ]APÉ                  ER4      ER 4       t¶]APÉ                  ER4      ] ! RK4      3ER ER ll4       t·ER ER lt¸]APÑ                  ER4      ER ER l4       t¹]APý                  ER4      ER ER l4       tº]APÛ                  ER4      ] ! RKERR7      3ER ER  ll4       t»ER! ER" lt¼]APÑ                  ER#4      ER$ ER% l4       t½]APÑ                  ER&4      ER' ER( l4       t¾]APÑ                  ER)4      ER* ER+ l4       t¿ ! ER, ER-])4      tÀ ! ER. ER/])4      tÁ]APÉ                  ER04      ER1 4       tÂ]APý                  ER24      ER3 ER4 l4       tÃ]APÑ                  ER54      ER6 ER7 l4       tÄ ! ER8 ER9])4      tÅ]APÑ                  ER:4      ] ! R4      3ER; ER< ll4       tÆ ! ER= ER>])4      tÇERTtÈ]APÉ                  ER?4      ER@ 4       tÉ]APÉ                  ERA4      ERB 4       tÊERC ERD ltË]AEP™                  ERA4      ERE ERF l4       tÍERGtÎERH ERI ltÏERJ ERK ltÐERL ERM ltÑ ! ERN ERO4      tÒ]AEP§                  ERP4      ERQ ERR l4       tÔR#   ]� d    RtŒ EL�i ; i(U  u  Kanban dashboard plugin â€” backend API routes, mounted at /api/plugins/kanban/.

Every handler is a thin wrapper around ``hermes_cli.kanban_db`` (the same code paths the CLI
and gateway ``/kanban`` command use, so the surfaces cannot drift). The ``/events`` WebSocket
tails the append-only ``task_events`` table on a short poll (WAL reads run alongside the
dispatcher's write txns); it carries its credential in the query string (browsers can't set
``Authorization`` on an upgrade) and is gated by the dashboard's canonical WS auth check.
)ÚannotationsN)ÚThreadPoolExecutor)ÚclosingÚcontextmanager)Úasdict)Úpartial)ÚPath)ÚAnyÚCallableÚIteratorÚOptional)	Ú	APIRouterÚFileÚFormÚHTTPExceptionÚQueryÚ
UploadFileÚ	WebSocketÚWebSocketDisconnectÚstatus)ÚFileResponse)Ú	BaseModelÚField)Ú	kanban_db)Úkanban_workflow)Úcoalesced_read)Úkanban_db_connect)Úkanban_db_notify)Úkanban_db_dispatch)Úkanban_db_workspace)Úkanban_diagnostics)ÚKANBAN_ATTACHMENT_MAX_BYTESÚ_collision_free_pathÚ_safe_attachment_namez$Kanban board slug (omit for current))Údescriptionc               ó    € V ^8„  d   QhRRRR/# )é   Úwsr   ÚreturnÚbool© )Úformats   "ÚH/home/ubuntu/.hermes/hermes-agent/plugins/kanban/dashboard/plugin_api.pyÚ__annotate__r-   2   s   € ÷ %ñ %˜yð %¨Tñ %ó    c                óh   €  ^ RI Hp \        TP	                  T 4      4      #   \         d     R# i ; i)a  Authorize a WS upgrade via the dashboard's canonical gate (``web_server_chat._ws_auth_ok``:
``?token=`` / ``?ticket=`` / ``?internal=``) so this endpoint can never drift from core
auth; accepts when the dashboard isn't importable (bare-FastAPI test harness).)Úweb_server_chatT)Ú
hermes_clir0   Ú	Exceptionr)   Ú_ws_auth_ok)r'   Ú_wss   & r,   Ú_ws_upgrade_authorizedr5   2   s4   € ðÝ5ô �—‘ Ó#Ó$Ð$øô ô Úðús   ‚" ¢1°1c               ó    € V ^8„  d   QhRRRR/# )r&   ÚslugÚstrr(   úOptional[str]r*   )r+   s   "r,   r-   r-   =   s   € ÷ 5ñ 5 ð 5¨ñ 5r.   c                ó�   € \        4       ;_uu_ 4        \        P                  ! V 4      uuR R R 4       #   + '       g   i     R # ; i©N)Ú_value_error_400r   Ú_normalize_board_slug)r7   s   &r,   Ú_normalize_slug_or_400r>   =   s(   € Ü	×	Õ	Ü×.Ò.¨tÓ4÷ 
×	×	Ó	ús	   “4´A	c               ó    € V ^8„  d   QhRRRR/# )r&   Úboardr9   r(   r*   )r+   s   "r,   r-   r-   B   s   € ÷ ñ ˜-ð ¨Mñ r.   c                óF  € V e   V R8X  d   R# \        V 4      pV'       d�   V\        P                  8w  dl   \        P                  ! V4      '       g   \	        RRV: R2R7      h\        P
                  ! V4      P                  R4      '       d   \	        RRV: R2R7      hV# )	z–Validate/normalise a board slug query param (400 malformed, 404 unknown);
``None`` when omitted so ``kb.connect()`` falls through to the active board.NÚ é”  úboard ú does not exist©Ústatus_codeÚdetailÚarchivedz is archived)r>   r   ÚDEFAULT_BOARDÚboard_existsr   Úread_board_metadataÚget)r@   Únormeds   & r,   Ú_resolve_boardrO   B   sŒ   € ð ‚}˜ œÙÜ# EÓ*€Fß�&œI×3Ñ3Ô3Ü×%Ò% f×-Ò-Ü¨C¸&ÀÁ
È/Ð8ZÔ[Ð[ô ×(Ò(¨Ó0×4Ñ4°Z×@Ò@Ü¨C¸&ÀÁ
È,Ð8WÔXÐXØ€Mr.   c               ó    € V ^8„  d   QhRRRR/# )r&   r7   r8   r(   r*   )r+   s   "r,   r-   r-   S   s   € ÷ ñ ˜sð  sñ r.   c                óˆ   € \        V 4      pV'       d   \        P                  ! V4      '       g   \        RRV : R2R7      hV# )zANormalise a path slug and require the board to exist (400 / 404).rC   rD   rE   rF   )r>   r   rK   r   ©r7   rN   s   & r,   Ú_existing_board_slugrS   S   s:   € ä# DÓ)€Fßœ×/Ò/°×7Ò7Ü¨°f¸T¹HÀOÐ4TÔUÐUØ€Mr.   c               ó   € V ^8„  d   QhRR/# ©r&   r@   r9   r*   )r+   s   "r,   r-   r-   [   s   € ÷ $ñ $�ñ $r.   c                ó¼   €  \         P                  ! V R7       \
        P                  ! V R7      #   \         d!   p\        P	                  RT4        Rp?L=Rp?ii ; i)z¨Connect to the already-normalised ``board`` (``None`` = active). ``init_db`` is
idempotent; running it here lets a fresh install self-heal if POST /tasks arrives first.©r@   zkanban init_db failed: %sN)r   Úinit_dbr2   ÚlogÚwarningÚkbcÚconnect)r@   Úexcs   & r,   Ú_connr^   [   sJ   € ð6Ü×Ò Õ&ô �;Š;˜UÔ#Ð#øô ô 6Ü�‰Ð/°×5Ñ5ûð6ús   ‚0 °A»AÁAc               ó    € V ^8„  d   QhRRRR/# )r&   r@   r9   r(   z2Iterator[tuple[Optional[str], sqlite3.Connection]]r*   )r+   s   "r,   r-   r-   f   s   € ÷ ñ �}ð Ð)[ñ r.   c              #  ó¢   "  € \        V 4      p \        \        V R7      4      ;_uu_ 4       pW3x € RRR4       R#   + '       g   i     R# ; i5i)zGResolve the ``board`` query param, open a connection, close it on exit.rW   N)rO   r   r^   ©r@   Úconns   & r,   Ú_board_connrc   e   s9   é € ô ˜5Ó!€EÜ	”˜UÔ#×	$Ô	$¨ØˆkÒ÷ 
%×	$×	$Ò	$üs   ‚)A«;±
A»A	Á	Ac               ó$   € V ^8„  d   QhRRRRRR/# )r&   r@   r9   ÚfnzCallable[[], Any]r(   r	   r*   )r+   s   "r,   r-   r-   m   s"   € ÷ ñ ˜mð Ð1Bð Àsñ r.   c                óÌ   € \         P                  ! \        V 4      ;'       g    \         P                  4      ;_uu_ 4        V! 4       uuRRR4       #   + '       g   i     R# ; i)zªRun ``fn`` with the board pinned context-locally, not via the process-global
``HERMES_KANBAN_BOARD`` env var (concurrent requests for different boards would cross-write).N)r   Úscoped_current_boardrO   rJ   )r@   re   s   &&r,   Ú_with_board_pinnedrh   m   s=   € ô 
×	'Ò	'¬°uÓ(=×(XÐ(XÄ×AXÑAX×	YÕ	YÙ‹t÷ 
Z×	Y×	YÓ	Yús   Á AÁA#	c               ó$   € V ^8„  d   QhRRRRRR/# )r&   Úgetterr
   rb   úsqlite3.ConnectionÚlabelr8   r*   )r+   s   "r,   r-   r-   t   s"   € ÷ ñ �Xð Ð%7ð Àsñ r.   c                óD   € V ! W4      pVf   \        RV RV R2R7      hV# )NrC   Ú ú
 not foundrF   ©r   )rj   rb   Úidentrl   Úobjs   &&&& r,   Ú_requirers   t   s0   € Ù
�Ó
€CØ
‚{Ü¨°u°g¸Q¸u¸gÀZÐ4PÔQÐQØ€Jr.   c               ó0   € V ^8„  d   QhRRRRRRRRRRRR	/# )
r&   r@   r9   Úmoduler8   re   Útask_idÚauthorr(   r	   r*   )r+   s   "r,   r-   r-   {   s7   € ÷ +ñ +�Mð +¨3ð +°Cð +À#ð +È}ð +Ðadñ +r.   c                ó2   aaaa€ VVVV3R lp\        W4      # )z©Run a slow auxiliary-LLM task helper (``hermes_cli.<module>.<fn>``) with the board pinned;
the module is imported lazily so a missing aux client can't break plugin load.c                 ón   <€ \        \        P                  ! R S 24      S4      ! SS ;'       g    RR7      # )zhermes_cli.N©rw   )ÚgetattrÚ	importlibÚimport_module)rw   re   ru   rv   s   €€€€r,   Ú_runÚ_run_aux.<locals>._run~   s3   ø€ Ü”y×.Ò.°¸V¸HÐ/EÓFÈÔKÈGÐ]c×]kÐ]kÐgkÔmÐmr.   )rh   )r@   ru   re   rv   rw   r~   s   &ffff r,   Ú_run_auxr€   {   s   û€ ÷nð nä˜eÓ*Ð*r.   c               ó$   € V ^8„  d   QhRRRRRR/# )r&   rb   rk   rv   r8   r(   úkanban_db.Taskr*   )r+   s   "r,   r-   r-   ƒ   s"   € ÷ ?ñ ?Ð*ð ?°Sð ?¸^ñ ?r.   c                ó8   € \        \        P                  WR 4      # )Útask)rs   r   Úget_task)rb   rv   s   &&r,   Ú_require_taskr†   ƒ   s   € Ü”I×&Ñ&¨°vÓ>Ð>r.   c               ó$   € V ^8„  d   QhRRRRRR/# )r&   rb   rk   Úrun_idÚintr(   zkanban_db.Runr*   )r+   s   "r,   r-   r-   ‡   s"   € ÷ <ñ <Ð)ð <°3ð <¸=ñ <r.   c                ó8   € \        \        P                  WR 4      # )Úrun)rs   r   Úget_run)rb   rˆ   s   &&r,   Ú_require_runr�   ‡   s   € Ü”I×%Ñ% t°UÓ;Ð;r.   c               ó    € V ^8„  d   QhRRRR/# )r&   Úokr)   r(   ÚNoner*   )r+   s   "r,   r-   r-   ‹   s   € ÷ Fñ F�Dð F˜Tñ Fr.   c                ó0   € V '       g   \        RRR7      hR# )zC404 when a kanban_db mutator reports the task vanished mid-request.rC   ztask not foundrF   Nrp   )r�   s   &r,   Ú_require_okr’   ‹   s   € çÜ¨Ð4DÔEÐEñ r.   c               ó    € V ^8„  d   QhRRRR/# )r&   rH   r8   r(   r   r*   )r+   s   "r,   r-   r-   ‘   s   € ÷ 9ñ 9�cð 9˜mñ 9r.   c                ó   € \        R V R7      # )é™  rF   rp   )rH   s   &r,   Ú	_conflictr–   ‘   s   € Ü S°Ô8Ð8r.   c               ó$   € V ^8„  d   QhRRRRRR/# )r&   r   r‰   Útypesztype[BaseException]r(   úIterator[None]r*   )r+   s   "r,   r-   r-   –   s"   € ÷ ?ñ ?˜ð ?Ð%8ð ?¸^ñ ?r.   c              '  ób   "  €  Rx € R#   T d   p\        T \        T4      R7      hRp?ii ; i5i)zEMap the given exception types to ``HTTPException(status, str(exc))``.NrF   )r   r8   )r   r˜   Úes   &* r,   Ú_map_errorsrœ   •   s-   é € ð?ÝøØô ?Ü¨´s¸1³vÔ>Ð>ûð?üs   ‚/„
 ˆ/Š,‘'§,¬/é�  c               ó    € V ^8„  d   QhRRRR/# )r&   Úprefixr8   r(   r™   r*   )r+   s   "r,   r-   r-   ¢   s   € ÷ Hñ H˜3ð H >ñ Hr.   c              #  óx   "  €  Rx € R#   \          d    h \         d   p\        RT  RT 2R7      hRp?ii ; i5i)zWMap any unexpected exception to ``500 "<prefix>: <exc>"``; HTTPExceptions pass through.Néô  z: rF   )r   r2   )rŸ   r]   s   & r,   Ú_errors_to_500r¢   ¡   sB   é € ðHÝøÜô ØÜô HÜ¨°v°h¸bÀÀÐ4FÔGÐGûðHüs    ‚:„
 ˆ:Š7Ÿ7 2²7·:ú	list[str]ÚBOARD_COLUMNSÚlatest_summaryÚcurrent_run_started_atc               ó(   € V ^8„  d   QhRRRRRRRR/# )	r&   r„   r‚   r¥   r9   r¦   úOptional[int]r(   údict[str, Any]r*   )r+   s   "r,   r-   r-   µ   s*   € ÷ ñ �^ð ¸ð Ø'4ðØ@Nñr.   c               ó˜   € \        V 4      p \        P                  ! V 4      VR &   WR&   W#R&   V#   \         d    RRRRRR/TR &    L"i ; i)ÚageÚcreated_age_secondsNÚstarted_age_secondsÚtime_to_complete_secondsr¥   r¦   )r   r   Útask_ager2   )r„   r¥   r¦   Úds   &$$ r,   Ú
_task_dictr±   µ   sl   € äˆt‹€AðpÜ×%Ò% dÓ+ˆˆ%‰ð )ÐÑð #9ÐÑØ€Høô ô pØ)¨4Ð1FÈÐNhÐjnÐoˆˆ%‹ðpús   �0 °A	ÁA	c               ó    € V ^8„  d   QhRRRR/# )r&   Úazkanban_db.Attachmentr(   r©   r*   )r+   s   "r,   r-   r-   Å   s   € ÷ pñ pÐ,ð p°ñ pr.   c                óÆ   € RV P                   RV P                  RV P                  RV P                  RV P                  RV P
                  RV P                  RV P                  /# )	zR``stored_path`` is the absolute on-disk path workers read; UI downloads by ``id``.Úidrv   ÚfilenameÚcontent_typeÚsizeÚuploaded_byÚstored_pathÚ
created_at)rµ   rv   r¶   r·   r¸   r¹   rº   r»   )r³   s   &r,   Ú_attachment_dictr¼   Å   sY   € ð 	ˆa�d‰d�I˜qŸy™y¨*°a·j±jÀ.ÐRS×R`ÑR`Ø�—‘˜ q§}¡}°mÀQÇ]Á]ÐT`Ðbc×bnÑbnðpð pr.   c               ó    € V ^8„  d   QhRRRR/# )r&   ÚidsÚlistr(   r8   r*   )r+   s   "r,   r-   r-   Ì   s   € ÷ &ñ &�tð & ñ &r.   c                óF   € R P                  R.\        V 4      ,          4      # )Ú,Ú?)ÚjoinÚlen)r¾   s   &r,   Ú_placeholdersrÅ   Ì   s   € Ø�8‰8�S�EœC ›HÕ$Ó%Ð%r.   c               ó$   € V ^8„  d   QhRRRRRR/# )r&   rb   rk   Útask_idsúOptional[list[str]]r(   zdict[str, list[dict]]r*   )r+   s   "r,   r-   r-   Ð   s$   € ÷ !ñ !Ð$6ð !ÐBUð !Ðavñ !r.   c           
     ó¾  a a€ ^ RI Hp Ve   V'       g   / # \        P                  ! V! 4       4      pVe8   S P	                  R\        V4       R2\        V4      4      P                  4       pMS P	                  R4      P                  4       pV'       g   / # V Uu. uF  qUR,          NK  	  upoR V V3R llpV! R4      pV! R	4      p\        P                  ! S S4      p	/ p
V Fl  pVR,          p\        P                  ! WWV,          W‹,          W9P                  V4      R
7      pV'       g   KK  V Uu. uF  qÝP                  4       NK  	  upW«&   Kn  	  V
# u upi u upi )u®   ``{task_id: [diagnostic_dict, ...]}`` (tasks with none omitted) via three aggregate
queries (tasks, events, runs) â€” slurps the board; paginate if profiling shows a hotspot.©Úload_configz!SELECT * FROM tasks WHERE id IN (Ú)z.SELECT * FROM tasks WHERE status != 'archived'rµ   c               ó    € V ^8„  d   QhRRRR/# )r&   Útabler8   r(   zdict[str, list]r*   )r+   s   "r,   r-   Ú/_compute_task_diagnostics.<locals>.__annotate__à   s   € ÷ ñ ˜Sð  _ñ r.   c                óè   <€ S Uu/ uF  q. bK  	  ppSP                  R V  R\        S4       R2\        S4      4       F+  pVP                  VR,          . 4      P	                  V4       K-  	  V# u upi )zSELECT * FROM z WHERE task_id IN (z) ORDER BY idrv   )ÚexecuterÅ   ÚtupleÚ
setdefaultÚappend)rÎ   ÚtidÚby_taskÚrowrb   Úrow_idss   &   €€r,   Ú_rows_by_taskÚ0_compute_task_diagnostics.<locals>._rows_by_taskà   s|   ø€ Ù7>Ó#?±w°¨¢G±wˆÐ#?Ø—<‘<Ø˜U˜GÐ#6´}ÀWÓ7MÐ6NÈmÐ\Ô^cÐdkÓ^lönˆCà×Ñ˜s 9�~¨rÓ2×9Ñ9¸#Ö>ñnð ˆùò	 $@s   †A/Útask_eventsÚ	task_runs)ÚconfigÚgraph)Úhermes_cli.configrË   ÚkdÚconfig_from_runtime_configrÑ   rÅ   rÒ   Úfetchallr   Útask_graph_contextsÚcompute_task_diagnosticsrM   Úto_dict)rb   rÇ   rË   Údiag_configÚrowsÚrrÙ   Úevents_by_taskÚruns_by_taskÚgraph_by_taskÚoutrÕ   Údiagsr°   rØ   s   f&            @r,   Ú_compute_task_diagnosticsrî   Ð   s<  ù€ õ .àÒ§HØˆ	Ü×/Ò/±³Ó>€KØÒØ�|‰|Ð?ÄÈhÓ@WÐ?XÐXYÐZÔ\aÐbjÓ\kÓl×uÑuÓw‰à�|‰|ÐLÓM×VÑVÓXˆßØˆ	Ù $Ó%¡˜1��wˆw¡Ñ%€G÷ñ ñ # =Ó1€NÙ  Ó-€LÜ×1Ò1°$¸Ó@€MØ!#€CÛˆØ��gˆÜ×+Ò+Ø˜cÕ" LÕ$5¸k×QbÑQbÐcfÓQgôiˆç‰5Ù-2Ó3©U¨Ÿ	™	ž©UÑ3ˆC‹Hñ ð €Jùò' &ùò$ 4s   ÂEÄ2Ec               ó    € V ^8„  d   QhRRRR/# )r&   Údiagnosticsú
list[dict]r(   úOptional[dict]r*   )r+   s   "r,   r-   r-   ô   s   € ÷ bñ b°Jð bÀ>ñ br.   c                óö  € V '       g   R# / p^ ;r#R	RrTV  FØ  pVP                  R^4      pVP                  VR,          ^ 4      V,           WR,          &   W',          p\        Y6P                  R4      ;'       g    ^ 4      pVP                  R4      pV\        P                  9   g   K’  \        P                  P	                  V4      V8”  g   K¸  \        P                  P	                  V4      TrTKÚ  	  RVRVRVRV/# )
z\Compact card badge summary ``{count, kinds, latest_at, highest_severity}``; None when empty.NÚcountÚkindÚlast_seen_atÚseverityÚkindsÚ	latest_atÚhighest_severityéÿÿÿÿ)rM   Úmaxrà   ÚSEVERITY_ORDERÚindex)	rð   rø   rô   ÚlatestÚhighest_idxÚhighest_sevr°   ÚnÚsevs	   &        r,   Ú"_warnings_summary_from_diagnosticsr  ô   sß   € çÙØ€EØÐ€EØ! 4�ÛˆØ�E‰E�'˜1ÓˆØ Ÿ9™9 Q v¥Y°Ó2°QÕ6ˆ��iÑØ�
ˆÜ�VŸU™U >Ó2×7Ð7°aÓ8ˆØ�e‰e�JÓˆØ”"×#Ñ#Ö#¬×(9Ñ(9×(?Ñ(?ÀÓ(DÀ{Ö(RÜ')×'8Ñ'8×'>Ñ'>¸sÓ'CÀSšñ ð �U˜G U¨K¸ÐASÐU`ÐaÐar.   c               ó$   € V ^8„  d   QhRRRRRR/# )r&   Útask_dÚdictrí   zOptional[list[dict]]r(   r�   r*   )r+   s   "r,   r-   r-     s'   € ÷ Gñ G ð GÐ-Að GÀdñ Gr.   c                ó>   € V'       d   WR&   \        V4      V R&   R# R# )zcFull list in the payload (drawer renders without a second round-trip); card badge gets the summary.rð   ÚwarningsN)r  )r  rí   s   &&r,   Ú_attach_diagnosticsr
    s!   € çØ %ˆ}ÑÜ?ÀÓFˆˆzÓñ r.   c               ó$   € V ^8„  d   QhRRRRRR/# )r&   rb   rk   rv   r8   r(   údict[str, list[str]]r*   )r+   s   "r,   r-   r-     s(   € ÷ añ aÐ'ð a°#ð aÐ:Nñ ar.   c                óB   a a€ R V V3R llpRV! RR4      RV! RR4      /# )z8Return {'parents': [...], 'children': [...]} for a task.c               ó$   € V ^8„  d   QhRRRRRR/# )r&   Úcolr8   Úotherr(   r£   r*   )r+   s   "r,   r-   Ú _links_for.<locals>.__annotate__  s&   € ÷ |ñ |�#ð |˜cð | iñ |r.   c                ór   <€ SP                  R V  RV RV  2S34       Uu. uF  q"V ,          NK  	  up# u upi )zSELECT z FROM task_links WHERE z = ? ORDER BY )rÑ   )r  r  rè   rb   rv   s   && €€r,   Ú_idsÚ_links_for.<locals>._ids  sI   ø€ Ø $§¡¨w°s°eÐ;RÐSXÐRYÐYgÐhkÐglÐ-mÐpwÐoyÔ zÓ{Ñ z˜1�#—�Ñ zÑ{Ð{ùÒ{s    4ÚparentsÚ	parent_idÚchild_idÚchildrenr*   )rb   rv   r  s   ff r,   Ú
_links_forr    s-   ù€ ÷|ñ |à‘t˜K¨Ó4°jÁ$ÀzÐS^ÓB_Ð`Ð`r.   c               ó$   € V ^8„  d   QhRRRRRR/# )r&   rb   rk   Úlinksr  r(   rñ   r*   )r+   s   "r,   r-   r-     s#   € ÷ 	ñ 	Ð(ð 	Ð1Eð 	È*ñ 	r.   c           	     ó  € . p\         P                  . VR,          OVR,          O4       FX  p\        P                  ! W4      pV'       g   K#  VP	                  RVP
                  RVP                  RVP                  /4       KZ  	  V# )u¡   One {id, title, status} row per linked task, so UIs can render titles
instead of raw ids. Dropped/foreign rows are simply absent â€” callers fall
back to the id.r  r  rµ   Útitler   )r  Úfromkeysr   r…   rÔ   rµ   r  r   )rb   r  rç   rv   r„   s   &&   r,   Ú_link_tasksr    ss   € ð €DÜ—=‘=Ð!H 5¨Õ#3Ð!H°e¸JÕ6GÐ!HÖIˆÜ×!Ò! $Ó0ˆß‰4Ø�K‰K˜˜tŸw™w¨°·±¸XÀtÇ{Á{ÐSÖTñ Jð €Kr.   zFilter to a single tenantFz1Restrict to tasks using this workflow template idz+Restrict to tasks at this workflow step keyc          
     ó,   € V ^8„  d   QhRRRRRRRRRR/# ©r&   Útenantr9   Úinclude_archivedr)   r@   Úworkflow_template_idÚcurrent_step_keyr*   )r+   s   "r,   r-   r-   "  sA   € ÷ ?fñ ?fØð?fàð?fð ð?fð (ð	?fð
 $ñ?fr.   c                ó€  € \        V4      ;_uu_ 4       w  r%\        P                  ! WPVW4R7      p/ pVP                  R4      P	                  4        Fc  pVP                  VR,          R^ R^ /4      R;;,          ^,          uu&   VP                  VR,          R^ R^ /4      R;;,          ^,          uu&   Ke  	  VP                  R4       U	u/ uF  q™R,          V	R	,          bK  	  p
p	/ pVP                  R
4      P	                  4        FT  pVP                  VR,          R^ R^ /4      pVR;;,          ^,          uu&   VR;;,          VR,          R8H  ,          uu&   KV  	  \        VRR7      pVP                  R4      P                  4       R,          p\         Uu/ uF  qÿ. bK  	  ppV'       d   . VR&   \        P                  ! YV Uu. uF  pVP                  NK  	  up4      p\        P                  ! YV Uu. uF  pVP                  NK  	  up4      pV EF  pVP                  VP                  4      p\        TV'       d
   VR\         MRVP                  VP                  4      R7      pVP                  VP                  R^ R^ /4      VR&   V
P                  VP                  ^ 4      VR&   VP                  VP                  4      VR&   \        VVP                  VP                  4      4       TVP                   V9   d   VP                   MR,          P#                  V4       EK  	  VR,          P%                  R RR7       VR,          P%                  R R7       VP                  R4       U	u. uF  q™R,          NK  	  pp	\        P&                  ! V4       Uu. uF  pVR ,          NK  	  ppR!V Uu. uF  pR VR"VV,          /NK  	  upR#VR$VR%\)        V4      R&\)        \*        P*                  ! 4       4      /uuRRR4       # u up	i u upi u upi u upi u up	i u upi u upi   + '       g   i     R# ; i)'uœ   Full board grouped by status column; omitting ``board`` uses the active board
(``HERMES_KANBAN_BOARD`` env â†’ on-disk ``current`` pointer â†’ ``default``).)r"  r#  r$  r%  z*SELECT parent_id, child_id FROM task_linksr  r  r  r  zASELECT task_id, COUNT(*) AS n FROM task_comments GROUP BY task_idrv   r  zbSELECT l.parent_id AS pid, t.status AS cstatus FROM task_links l JOIN tasks t ON t.id = l.child_idÚpidÚdoneÚtotalÚcstatusN©rÇ   ú1SELECT COALESCE(MAX(id), 0) AS m FROM task_eventsÚmrI   ©r¥   r¦   Úlink_countsÚcomment_countÚprogressÚtodoc                ó   € V R ,          # )rµ   r*   ©r°   s   &r,   Ú<lambda>Úget_board.<locals>.<lambda>S  s   € ¨1¨T®7r.   T)ÚkeyÚreversec                ó@   € V R ,          RJ V R ,          ;'       g    ^ ) 3# )Úcompleted_atNr*   r4  s   &r,   r5  r6  T  s$   € ¨A¨nÕ,=ÀÐ,EÈÈ>ÕIZ×I_ÐI_Ð^_ÐG`Ñ+ar.   ©r7  zJSELECT DISTINCT tenant FROM tasks WHERE tenant IS NOT NULL ORDER BY tenantr"  ÚnameÚcolumnsÚtasksÚtenantsÚ	assigneesÚlatest_event_idÚnow)rc   r   Ú
list_tasksrÑ   râ   rÓ   rî   Úfetchoner¤   Úlatest_summariesrµ   Úcurrent_run_started_atsrM   r±   Ú_CARD_SUMMARY_PREVIEW_CHARSr
  r   rÔ   ÚsortÚknown_assigneesr‰   Útime)r"  r#  r@   r$  r%  rb   r>  r/  r×   rè   Úcomment_countsr1  ÚpÚdiagnostics_per_taskrA  Úcr=  ÚtÚsummary_mapÚrun_start_mapÚfullr°   r?  Úentryr@  r<  s   &&&&&                     r,   Ú	get_boardrT  "  sì  € ô 
�U×	Ô	™} Ü×$Ò$ØÐ2BØ!5ôZˆð 24ˆØ—<‘<Ð LÓM×VÑVÖXˆCØ×"Ñ" 3 {Õ#3°iÀÀJÐPQÐ5RÓSÐT^×_ÐcdÕdÓ_Ø×"Ñ" 3 z¥?°YÀÀ:ÈqÐ4QÓRÐS\×]ÐabÕbÕ]ñ Yð +/¯,©,Ð7zÔ*{ó*}Ù*{ Qˆi�L˜!˜C�&Ò Ñ*{ð 	ð *}à.0ˆØ—<‘<ØpórßrzÑrzÓr|ò}ˆCà×#Ñ# C¨¥J°¸¸GÀQÐ0GÓHˆAØˆg�J˜!�O‹JØˆf�I˜˜Y�¨6Ñ1Õ1�Iñ	}ô
  9¸ÈÔMÐØŸ,™,Ð'ZÓ[×dÑdÓfÐgjÕkˆÝ9FÓ)G¹°A¨Rª%¹ˆÐ)GßØ"$ˆG�JÑô  ×0Ò0°ÀeÓ7LÁeÀ¸¿¼ÁeÑ7LÓMˆô "×9Ò9¸$ÈuÓ@UÉuÈ!ÀÇÄÉuÑ@UÓVˆÜˆAØ—?‘? 1§4¡4Ó(ˆDÜ˜1×TX¨dÐ3OÔ4OÑ.PÐ^bØ2?×2CÑ2CÀAÇDÁDÓ2IôKˆAà*Ÿ™¨q¯t©t°iÀÀJÐPQÐ5RÓSˆAˆmÑØ!/×!3Ñ!3°A·D±D¸!Ó!<ˆAˆoÑØ$ŸL™L¨¯©Ó.ˆAˆj‰MÜ Ð#7×#;Ñ#;¸A¿D¹DÓ#AÔBØ §¡¨GÔ 3�A—H’H¸Õ@×GÑGÈ×Jñ ð 	��×ÑÑ!2¸DÐÔAØ��×ÑÑ!aÐÔbð )-¯©ð  6Bô  )Có  Dñ  )C 1�X—;�;ñ  )Cˆð  Dô 1:×0IÒ0IÈ$Ô0OÓPÑ0O u�U˜6—]�]Ñ0Oˆ	ÐPàÉ7ÓSÉ7À4˜  w°¸µÓ>É7ÑSÐU^Ð`gØ˜Ð$5´s¸?Ó7KÈUÔTWÔX\×XaÒXaÓXcÓTdðf÷k 
Ò	ùò*}ùò *Hùò
 8Mùò AVùò$ Dùò QùâS÷m 
×	Ð	úsh   ”B7P,ÃP	Ã$B6P,ÆPÆ%#P,ÇPÇP,Ç8PÈE'P,Í4PÎP,ÎP"Î1P,Î8P'
Ï0P,Ð	#P,Ð,P=	z/boardc          
     ó,   € V ^8„  d   QhRRRRRRRRRR/# r!  r*   )r+   s   "r,   r-   r-   h  s:   € ÷ ñ Øðàðð ðð (ð	ð
 $ñr.   c              ƒ  óx   "  € \        T TT;'       g    \        P                  ! 4       VVR 7      G Rj  x€L
 #  L5i)©r"  r#  r@   r$  r%  N)Ú_read_boardr   Úget_current_boardrW  s   &&&&&r,   Úget_board_endpointrZ  g  s>   é € ô ØØ)Ø×4Ð4”y×2Ò2Ó4Ø1Ø)ô÷ ð ñ ùs   ‚1:³8´:z/tasks/{task_id}z@With run_state_name: filter runs by column 'status' or 'outcome'z4With run_state_type: exact value for that run columnc               ó(   € V ^8„  d   QhRRRRRRRR/# )r&   rv   r8   r@   r9   Úrun_state_typeÚrun_state_namer*   )r+   s   "r,   r-   r-   |  s6   € ÷ Cñ CØðCàðCð "ðCð "ñ	Cr.   c                ó  a€ \        V4      ;_uu_ 4       w  poVR J VR J ,          '       d   \        RRR7      hVR9  d   \        RRR7      h\        SV 4      p\        V\        P
                  ! SV 4      \        P                  ! SV .4      P                  V 4      R7      p\        SV 4      p\        P                  ! SVR,          4      p\        R V3R lVR,           4       4      p\        T\        SV .R	7      P                  V 4      ;'       g    . 4       R
TR\        P                  ! SV 4       U	u. uF  p	\        V	4      NK  	  up	R\        P                  ! SV 4       U
u. uF  p
\        V
4      NK  	  up
R\        P                   ! SV 4       Uu. uF  p\#        V4      NK  	  upRTR\%        SV4      RV U	u. uFP  p	RV	P&                  RV	P(                  RV	P*                  RVP                  V	P&                  4      RV	P,                  /NKR  	  up	R\        P.                  ! SWVR7       Uu. uF  p\        V4      NK  	  up/uuR R R 4       # u up	i u up
i u upi u up	i u upi   + '       g   i     R # ; i)Nr�   zDrun_state_type and run_state_name must be passed together or omittedrF   r   z,run_state_type must be 'status' or 'outcome'r.  r  c              3  óR   <"  € T F  p\         P                  ! SV4      x € K  	  R # 5ir;   )r   r…   )Ú.0Úcidrb   s   & €r,   Ú	<genexpr>Úget_task.<locals>.<genexpr>�  s#   øé € Ð \ÑJ[À3¤×!3Ò!3°D¸#×!>Ð!>ÓJ[ùs   ƒ$'r+  r„   ÚcommentsÚeventsÚattachmentsr  Ú
link_tasksÚchild_resultsrµ   r  r¥   ÚresultÚruns)Ú
state_typeÚ
state_name)Nr   Úoutcome)rc   r   r†   r±   r   r¥   rF  rM   r  rE  Úfilterr
  rî   Úlist_commentsr   Úlist_eventsÚlist_attachmentsr¼   r  rµ   r  r   ri  Ú	list_runs)rv   r@   r\  r]  r„   r  r  Úchild_summariesr  rN  r›   r³   rè   rb   s   &&&&         @r,   r…   r…   {  s^  ø€ ô 
�U×	Ô	™}  tØ˜dÐ" ~¸Ð'=×>Ô>Ü¨CÐ8~ÔÐØÐ!<Ô<Ü¨CÐ8fÔgÐgÜ˜T 7Ó+ˆäØ¤×!9Ò!9¸$ÀÓ!HÜ#,×#DÒ#DÀTÈGÈ9Ó#U×#YÑ#YÐZaÓ#bôdˆô ˜4 Ó)ˆÜ#×4Ò4°T¸5ÀÕ;LÓMˆÜ˜$Ô \È%ÐPZÖJ[Ó \Ó]ˆÜ˜FÔ$=¸dÈgÈYÔ$W×$[Ñ$[Ð\cÓ$d×$jÐ$jÐhjÔkà�FØ¬I×,CÒ,CÀDÈ'Ô,RÓSÑ,R qœ žÑ,RÑSØ¬)×*?Ò*?ÀÀgÔ*NÓOÑ*N Q”v˜a–yÑ*NÑOØ¼×9SÒ9SÐTXÐZaÔ9bÓcÑ9b°AÔ,¨QÖ/Ñ9bÑcØ�UØœ+ d¨EÓ2Øá!ó#á!�Að �q—t‘t˜W a§g¡g¨x¸¿¹ÐCSÐUd×UhÑUhÐij×imÑimÓUnÐpxÐz{÷  {Cñ  {Có  DÙ!ñ#ð ¬	×(;Ò(;¸DÀ'Ðqõ  )Aó  Bñ  )A 1”V˜A–Yñ  )Añ  Bð
C÷ 
Ò	ùò  TùÚOùÚcùò#ùò B÷1 
×	Ð	úsO   •D I-Ä5IÅ	I-Å%IÅ9I-ÆIÆ)I-Æ>AI#"ÈI-È2I(&ÉI-ÉI-É-I>	c                  ó  € ] tR tRt$ R]R&   RtR]R&   RtR]R&   RtR]R&   ^ tR	]R
&   Rt	R]R&   Rt
R]R&   ]! ]R7      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tR]R&   RtR]R&   RtR]R&   RtR# )ÚCreateTaskBodyiž  r8   r  Nr9   ÚbodyÚassigneer"  r‰   ÚpriorityÚworkspace_kindÚworkspace_path)Údefault_factoryr£   r  Fr)   ÚtriageÚidempotency_keyr¨   Úmax_runtime_secondsrÈ   ÚskillsÚ	goal_modeÚgoal_max_turnsÚmodel_overrideÚprovider_overrideÚreasoning_effortÚ
project_idr*   )Ú__name__Ú
__module__Ú__qualname__Ú__firstlineno__Ú__annotations__rv  rw  r"  rx  ry  rz  r   r¿   r  r|  r}  r~  r  r€  r�  r‚  rƒ  r„  r…  Ú__static_attributes__r*   r.   r,   ru  ru  ž  sº   ‡ ØƒJØ€Dˆ-ÓØ"€HˆmÓ"Ø €FˆMÓ Ø€HˆcÓØ$(€N�MÓ(Ø$(€N�MÓ(Ù¨tÔ4€GˆYÓ4Ø€FˆDÓØ%)€O�]Ó)Ø)-Ð˜Ó-Ø"&€FÐÓ&Ø€IˆtÓØ$(€N�MÓ(Ø$(€N�MÓ(Ø'+Ð�}Ó+Ø&*Ð�mÓ*Ø $€J�×$r.   ru  z/tasksc               ó    € V ^8„  d   QhRRRR/# )r&   Úpayloadru  r@   r9   r*   )r+   s   "r,   r-   r-   ´  s   € ÷ ñ ˜ð °ñ r.   c                ó´  € \        V4      ;_uu_ 4       w  r\        4       ;_uu_ 4        \        P                  ! V3R RRV/V P	                  4       B p\        P
                  ! W#4      pRV'       d2   \        V\        P                  ! W#.4      P                  V4      R7      MR/pV'       dU   VP                  R8X  dD   VP                  '       d2    ^ RIHp ^ RIHp V! V! 4       R	7      w  r‰V'       g   V	'       d   W•R
&   VuuRRR4       uuRRR4       #   \         d     L#i ; i  + '       g   i     M; iRRR4       R#   + '       g   i     R# ; i)Ú
created_byÚ	dashboardr@   r„   ©r¦   NÚready)Ú_check_dispatcher_presence©Úget_hermes_home)Úhermes_homerZ   )rc   r<   r   Úcreate_taskÚ
model_dumpr…   r±   rF  rM   r   rw  Úhermes_cli.kanbanr“  Úhermes_constantsr•  r2   )
r�  r@   rb   rv   r„   rv  r“  r•  ÚrunningÚmessages
   &&        r,   r—  r—  ³  s
  € ä	�U×	Ô	™} Ô.>×.@Õ.@ä×'Ò'¨Ñj¸ÐjÈEÐjÐU\×UgÑUgÓUiÑjˆÜ×!Ò! $Ó0ˆØ &çô )3Ø¬×)JÒ)JÈ4ÐQZÓ)[×)_Ñ)_Ð`gÓ)hõ)
àð ˆ÷ �D—K‘K 7Ô*¨t¯}¯}¨}ðÝHÝ<Ù#=É/ÓJ[Ô#\Ñ �ß§7Ø&-˜‘Oð ÷) /AÑ.@×	Ò	øô$ ô Ùðú÷% /A×.@ú×	×	×	Ò	úsS   ”E©BD)	Â."D)	Ã+DÃ>DÄD)	Ä
EÄD&Ä#D)	Ä%D&Ä&D)	Ä)D9Ä4EÅE	z/tasks/{task_id}/attachmentsc               ó    € V ^8„  d   QhRRRR/# ©r&   rv   r8   r@   r9   r*   )r+   s   "r,   r-   r-   Ñ  s   € ÷ iñ i 3ð i¨}ñ ir.   c           	     óì   € \        V4      ;_uu_ 4       w  r\        W 4       R \        P                  ! W 4       Uu. uF  p\	        V4      NK  	  up/uuRRR4       # u upi   + '       g   i     R# ; i)rf  N)rc   r†   r   rq  r¼   )rv   r@   rb   r³   s   &&  r,   Úlist_task_attachmentsr   Ð  s[   € ä	�U×	Ô	™} Ü�dÔ$Ø¼Y×=WÒ=WÐX\Ô=fÓgÑ=f¸Ô 0°Ö 3Ñ=fÑgÐh÷ 
Ò	ùâg÷ 
×	Ð	ús   ”'A"»A
ÁA"ÁA"Á"A3	.c               ó(   € V ^8„  d   QhRRRRRRRR/# )r&   rv   r8   Úfiler   r@   r9   r¹   r*   )r+   s   "r,   r-   r-   Ø  s6   € ÷ Fñ FØðFà
ðFð ðFð ñ	Fr.   c              ƒ  óp  "  € \        V4      ;_uu_ 4       w  r$\        4       ;_uu_ 4        \        W@4       \        VP                  ;'       g    R4      p\
        P                  ! WR7      pVP                  RRR7       \        We4      p^ p \        VR4      ;_uu_ 4       p	VP                  R4      G Rj  x€L
 ;p
'       do   V\        V
4      ,          pV\        8”  d?   V	P                  4        VP                  RR7       \        RR	\        R,           R
2R7      hV	P!                  V
4       K�   RRR4       \
        P$                  ! Y@TP&                  \)        TP+                  4       4      TP,                  Yƒ;'       g    RR7      p\
        P.                  ! YL4      pRT'       d   \1        T4      MR/uuRRR4       uuRRR4       #  EL  + '       g   i     L¤; i  \"         d   p\        RRT 2R7      hRp?ii ; i  + '       g   i     M; iRRR4       R#   + '       g   i     R# ; i5i)už   Store an upload under ``attachments_root(board)/<task_id>/`` (sanitised,
collision-resolved name; ``_safe_attachment_name`` ValueError â†’ 400) and record it.rB   rW   T©r  Úexist_okÚwbN)Ú
missing_oki�  zattachment exceeds z	 MB limitrF   r¡   zfailed to store attachment: r�  )r¶   rº   r·   r¸   r¹   Ú
attachmenti   )rc   r<   r†   r#   r¶   r   Útask_attachments_dirÚmkdirr"   ÚopenÚreadrÄ   r!   ÚcloseÚunlinkr   ÚwriteÚOSErrorÚadd_attachmentr<  r8   Úresolver·   Úget_attachmentr¼   )rv   r¢  r@   r¹   rb   Ú	safe_nameÚdest_dirÚ	dest_pathr)  rì   Úchunkr]   Úatt_idÚatts   &&&&          r,   Úupload_task_attachmentrº  ×  sÎ  é € ô 
�U×	Ô	™} Ô.>×.@Õ.@Ü�dÔ$Ü)¨$¯-©-×*=Ð*=¸2Ó>ˆ	Ü×1Ò1°'ÔGˆØ�‰˜t¨dˆÔ3Ü(¨Ó=ˆ	Øˆð	^Ü�i ×&Ô&¨#Ø%)§Y¡Y¨{Ó%;×;Ð;�eÖ;ØœS ›ZÕ'�EØÔ:Ô:ØŸ	™	œØ!×(Ñ(°DÐ(Ô9Ü+Ø(+Ð6IÔJeÐjuÕJvÐIwð  xAð  5BôCð Cà—I‘I˜eÖ$ð <÷ 'ô ×)Ò)Ø I§N¡NÄÀI×DUÑDUÓDWÓ@XØ×*Ñ*°×E_ÐE_ÐT_ôbˆô ×&Ò& tÓ4ˆØ·sÔ.¨sÔ3ÀÐE÷/ /AÑ.@×	Ò	ò  <÷ '×&ûô ô 	^Ü¨CÐ:VÐWZÐV[Ð8\Ô]Ð]ûð	^ú÷# /A×.@ú×	×	×	Ò	üsœ   ‚H6–H"«A"H	ÂG"Â"GÂ7GÂ8GÃA.GÄ3G"Ä;A<H	Æ7
H"ÇH6ÇGÇGÇG"ÇH	ÇG"Ç"HÇ-G=Ç=HÈH	ÈHÈH"È
H6È"H3	È-	H6z/attachments/{attachment_id}c               ó    € V ^8„  d   QhRRRR/# ©r&   Úattachment_idr‰   r@   r9   r*   )r+   s   "r,   r-   r-   ú  s   € ÷ @ñ @ sð @°=ñ @r.   c                ó`  € \        V4      ;_uu_ 4       w  r\        P                  ! W 4      pVf   \        RRR7      h\        P                  ! VR7      P                  4       p \        VP                  4      P                  4       pVP                  V4       TP                  4       '       g   \        RRR7      h\        \        T4      TP                  TP                  ;'       g    RR7      uuR R R 4       #   \        \        3 d    \        RRR7      hi ; i  + '       g   i     R # ; i)	NrC   úattachment not foundrF   rW   zattachment file unavailablezattachment file missing on diskzapplication/octet-stream)Úpathr¶   Ú
media_type)rc   r   r³  r   Úattachments_rootr²  r   rº   Úrelative_toÚ
ValueErrorr°  Úis_filer   r8   r¶   r·   )r½  r@   rb   r¹  ÚrootÚstoreds   &&    r,   Údownload_attachmentrÈ  ù  só   € ä	�U×	Ô	™} Ü×&Ò& tÓ;ˆØŠ;Ü¨CÐ8NÔOÐOä×)Ò)°Ô6×>Ñ>Ó@ˆð	WÜ˜#Ÿ/™/Ó*×2Ñ2Ó4ˆFØ×Ñ˜tÔ$ð �~‰~×ÒÜ¨CÐ8YÔZÐZÜ¤ V£°s·|±|ÐPS×P`ÑP`×P~ÐP~Ðd~Ô÷ 
Ò	øô œGÐ$ô 	WÜ¨CÐ8UÔVÐVð	Wú÷ 
×	Ð	ús0   ”ADÁ$4C:ÂDÂ.9DÃ(DÃ:DÄDÄD-	c               ó    € V ^8„  d   QhRRRR/# r¼  r*   )r+   s   "r,   r-   r-     s   € ÷ 1ñ 1 Sð 1°ñ 1r.   c                óÀ   € \        V4      ;_uu_ 4       w  r\        P                  ! W 4      f   \        RRR7      hRRRV /uuR R R 4       #   + '       g   i     R # ; i)NrC   r¿  rF   r�   Trµ   )rc   r   Údelete_attachmentr   )r½  r@   rb   s   && r,   Úremove_attachmentrÌ    sK   € ä	�U×	Ô	™} Ü×&Ò& tÓ;ÒCÜ¨CÐ8NÔOÐOØ�d˜D -Ð0÷ 
×	×	Ó	ús   ”-AÁA	c                  óÜ   € ] tR tRt$ 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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# )ÚUpdateTaskBodyi  Nr9   r   rw  r¨   rx  r  rv  ri  Úblock_reasonÚsummaryrò   Úmetadatar‚  rƒ  Fr)   Úclear_model_overrider„  Úclear_reasoning_effortr*   )r†  r‡  rˆ  r‰  r   rŠ  rw  rx  r  rv  ri  rÏ  rÐ  rÑ  r‚  rƒ  rÒ  r„  rÓ  r‹  r*   r.   r,   rÎ  rÎ    s”   ‡ Ø €FˆMÓ Ø"€HˆmÓ"Ø"€HˆmÓ"Ø€Eˆ=ÓØ€Dˆ-ÓØ €FˆMÓ Ø"&€L�-Ó&à!€Gˆ]Ó!Ø#€HˆnÓ#ð %)€N�MÓ(Ø'+Ð�}Ó+Ø!&Ð˜$Ó&Ø&*Ð�mÓ*Ø#(Ð˜D×(r.   rÎ  c                  óØ   € ] tR t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
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# )ÚBulkTaskBodyi*  r£   r¾   Nr9   r   rw  r¨   rx  Fr)   Úarchiveri  rÐ  rò   rÑ  Úreclaim_firstr‚  rƒ  rÒ  r„  rÓ  r*   )r†  r‡  rˆ  r‰  rŠ  r   rw  rx  rÖ  ri  rÐ  rÑ  r×  r‚  rƒ  rÒ  r„  rÓ  r‹  r*   r.   r,   rÕ  rÕ  *  s�   ‡ Ø	ƒNØ €FˆMÓ Ø"€HˆmÓ"Ø"€HˆmÓ"Ø€GˆTÓØ €FˆMÓ Ø!€Gˆ]Ó!Ø#€HˆnÓ#Ø€M�4Óà$(€N�MÓ(Ø'+Ð�}Ó+Ø!&Ð˜$Ó&Ø&*Ð�mÓ*Ø#(Ð˜D×(r.   rÕ  c                  ó   € ] tR tRtRtRtR# )Ú_StatusRejectedi<  zMA status the dashboard may not set via this path; the message is user-facing.r*   N)r†  r‡  rˆ  r‰  Ú__doc__r‹  r*   r.   r,   rÙ  rÙ  <  s   † ÝWr.   rÙ  zFCannot set status to 'running' directly; use the dispatcher/claim pathc               ó$   € V ^8„  d   QhRRRRRR/# )r&   rv   r8   Úsr(   r)   r*   )r+   s   "r,   r-   r-   C  s!   € ÷ 	0ñ 	0˜Cð 	0 Cð 	0¨Dñ 	0r.   c                ó  € VR8w  d   \         P                  ! W4      MRpVR8X  d0   V'       d(   VP                  R9   d   \         P                  ! W4      # Ve(   VP                  R8X  d   \         P                  ! W4      # \        WV4      # )a  Drag-drop into ready/todo/triage: blocked/scheduled -> ready re-opens via ``unblock_task``;
leaving ``review`` goes through ``reopen_review_task`` (stale-run recovery, parent re-gate,
``review_reopened`` event) instead of a raw write; ``triage`` needs no current-state query.r|  Nr’  Úreview)ÚblockedÚ	scheduled)r   r…   r   Úunblock_taskÚreopen_review_taskÚ_set_status_direct)rb   rv   rÜ  Úcurrents   &&& r,   Ú_drag_torå  C  sq   € ð 45¸´=Œi× Ò  Ô/Àd€GØˆG„|Ÿ G§N¡NÐ6NÔ$NÜ×%Ò% dÓ4Ð4ØÒ˜wŸ~™~°Ô9Ü×+Ò+¨DÓ:Ð:Ü˜d¨QÓ/Ð/r.   r(  c           	     ót   € \         P                  ! WVP                  VP                  VP                  R R7      # )T)ri  rÐ  rÑ  Úforce)r   Úcomplete_taskri  rÐ  rÑ  ©rb   rÕ   rL  s   &&&r,   r5  r5  S  s(   € ¤×!8Ò!8Ø˜!Ÿ(™(¨A¯I©IÀÇ
Á
ÐRVõ"Xr.   rß  c           	     óH   € \         P                  ! W\        VR R4      R7      # ©rÏ  N©Úreason)r   Ú
block_taskr{   ré  s   &&&r,   r5  r5  U  s   € ¤I×$8Ò$8¸Ì7ÐSTÐVdÐfjÓKkÕ$lr.   rà  c           	     óH   € \         P                  ! W\        VR R4      R7      # rë  )r   Úschedule_taskr{   ré  s   &&&r,   r5  r5  V  s   € ¤i×&=Ò&=¸dÔPWÐXYÐ[iÐkoÓPpÕ&qr.   rÞ  c           	     óˆ   € \         P                  ! YVP                  VP                  VP                  ;'       g    R RR7      # )NT)rÐ  rÑ  Úreviewerrç  )r   Úrequest_reviewrÐ  rÑ  rw  ré  s   &&&r,   r5  r5  W  s1   € ¤9×#;Ò#;Ø˜1Ÿ9™9¨q¯z©zÀQÇZÁZ×EWÐEWÐSWÐ`dõ$fr.   r’  c                ó   € \        WR 4      # )r’  ©rå  ré  s   &&&r,   r5  r5  Y  s   € ¤(¨4°gÔ">r.   r2  c                ó   € \        WR 4      # )r2  rõ  ré  s   &&&r,   r5  r5  Z  s   € ¤¨$°VÔ!<r.   r|  c                ó   € \        WR 4      # )r|  rõ  ré  s   &&&r,   r5  r5  [  s   € ¤8¨D°xÔ#@r.   r©   Ú_STATUS_HANDLERSc               ó(   € V ^8„  d   QhRRRRRRRR/# )r&   rv   r8   rÜ  Úunknown_detailr(   r)   r*   )r+   s   "r,   r-   r-   ^  s(   € ÷ %ñ % ð %¨ð %Àð %Èñ %r.   c                óˆ   € VR8X  d   \        \        4      h\        P                  V4      pVf   \        V4      hV! WV4      # )zƒDispatch a status verb; raises ``_StatusRejected`` (user-facing message)
for ``running`` or an unknown status (``unknown_detail``).r›  )rÙ  Ú_RUNNING_DIRECT_MSGrø  rM   )rb   rv   rÜ  rL  rú  Úhandlers   &&&&& r,   Ú_apply_statusrþ  ^  sD   € ð 	ˆI„~ÜÔ1Ó2Ð2Ü×"Ñ" 1Ó%€GØ‚Ü˜nÓ-Ð-Ù�4 !Ó$Ð$r.   c               ó(   € V ^8„  d   QhRRRRRRRR/# )	r&   rv   r8   rx  r‰   r@   r9   r(   r�   r*   )r+   s   "r,   r-   r-   i  s.   € ÷ Lñ L ð L°ð L¸Mð LÈdñ Lr.   c                óJ   € \         P                  ! W\        V4      VR 7       R# ))rx  r@   N)r   Ú	edit_taskr‰   )rb   rv   rx  r@   s   &&&&r,   Ú_set_priorityr  i  s   € Ü×Ò˜´°H³ÀU×Kr.   c               ó    € V ^8„  d   QhRRRR/# ©r&   rv   r8   r(   r)   r*   )r+   s   "r,   r-   r-   m  s   € ÷ `ñ `¨ð `°Dñ `r.   c                óÊ   € VP                   '       d   RM-VP                  ;'       g    RP                  4       ;'       g    Rp\        P                  ! WW2P
                  R7      # )zDRaises ValueError/RuntimeError from kanban_db for the caller to map.NrB   )Úprovider)rÒ  r‚  Ústripr   Úset_model_overriderƒ  )rb   rv   rL  Ú	new_models   &&& r,   Ú_apply_model_overrider
  m  sM   € à×.×.Ð.‘°Q×5EÑ5E×5KÐ5KÈ×4RÑ4RÓ4T×4\Ð4\ÐX\€IÜ×'Ò'¨°y×K^ÑK^Ô_Ð_r.   c               ó    € V ^8„  d   QhRRRR/# r  r*   )r+   s   "r,   r-   r-   s  s   € ÷ sñ s¨3ð s°dñ sr.   c                ót   € \         P                  ! YVP                  '       d   R 4      # VP                  4      # r;   )r   Úset_reasoning_effortrÓ  r„  )rb   rv   rL  s   &&&r,   Ú_apply_reasoning_effortr  s  s/   € Ü×)Ò)¨$À×AY×AYÐAY¸ÓrÐrÐ_`×_qÑ_qÓrÐrr.   c                óF   € V P                   ;'       g    V P                  R J# r;   )rÒ  r‚  ©rL  s   &r,   r5  r5  y  s    € ˆq×%Ñ%×EÐE¨×)9Ñ)9ÀÐ)EÐEr.   zmodel override refusedc                óF   € V P                   ;'       g    V P                  R J# r;   )rÓ  r„  r  s   &r,   r5  r5  z  s    € ˆq×'Ñ'×IÐI¨1×+=Ñ+=ÀTÐ+IÐIr.   zreasoning override refusedc               ó(   € V ^8„  d   QhRRRRRRRR/# )	r&   rv   r8   r�  rÎ  Úreview_assignee_deferredr)   r(   r�   r*   )r+   s   "r,   r-   r-   ~  s0   € ÷ zñ z ð z¨~ð zÐY]ð zÐbfñ zr.   c           
     óH  € VP                   pVR8X  d   \        P                  ! W4      pMv\        R\        \
        4      ;_uu_ 4        \        WWBRV 24      pRRR4       VR8X  d:   X'       d2   V'       d*   VP                  '       g   \        P                  ! WR4      pX'       d   R# VR8X  d   \        W4      M. pV'       d'   RP                  R V 4       4      p\        R	V 24      h\        \        WV4      ;'       g    R
V: R24      h  + '       g   i     LÀ; i)z·PATCH status phase: 400 on a rejected verb, 409 when the transition is refused
(naming the blocking parent(s) for ``ready``/``done``/``review`` so the UI renders an actionable toast).rI   r�   zunknown status: NrÞ  r’  ú, c              3  ób   "  € T F%  qR ,          : RVR,           RVR,           R2x € K'  	  R# 5i)r  ú (rµ   z	, status=r   rÌ   Nr*   )r`  rL  s   & r,   rb  Ú _patch_status.<locals>.<genexpr>�  s0   é € Ð`ÑW_ÐRS˜w�Z™N¨"¨Q¨t­W¨I°Y¸qÀ½{¸mÈ1ÕMÓW_ùs   ‚-/u:   Cannot move to 'ready': blocked by parent(s) not done â€” zstatus transition to z not valid from current state)r   r   Úarchive_taskrœ   rÙ  rÄ  rþ  rw  Úassign_taskÚ_parents_blocking_readyrÃ   r–   Ú_open_parent_refusal)rb   rv   r�  r  rÜ  r�   ÚblockersÚnamess   &&&&    r,   Ú_patch_statusr  ~  sî   € ð 	�‰€AØˆJ„Ü×#Ò# DÓ2‰ä˜œo¬z×:Õ:Ü˜t¨aÐ<LÈQÈCÐ:PÓQˆB÷ ;à�Œ=ŸR×$<ÀW×EU×EUÐEUÜ×&Ò& t°dÓ;ˆBß	ÙØ9:¸g¼Ô& tÔ5È2€HßØ—	‘	Ñ`ÑW_Ó`Ó`ˆÜÐTÐUZÐT[Ð\Ó]Ð]Ü
Ô(¨¸Ó:×xÐxÐ@UÐVWÑUZÐZwÐ>xÓ
yÐy÷ ;×:ús   ÁDÄD!	c               ó$   € V ^8„  d   QhRRRRRR/# )r&   rv   r8   rÜ  r(   r9   r*   )r+   s   "r,   r-   r-   ’  s&   € ÷ 	Gñ 	G¨ð 	G°ð 	G¸ñ 	Gr.   c                óž   € VR9  d   R# \         P                  ! W4      pV'       g   R# RP                  R V 4       4      pRV RV: RV R2# )	z¡complete_task/request_review return bare False for a dependency refusal too;
for a refused ``done``/``review`` name the open parents instead of the generic text.Nr  c              3  ó6   "  € T F  w  rV R V R2x € K  	  R# 5i)r  rÌ   Nr*   )r`  r'  r   s   &  r,   rb  Ú'_open_parent_refusal.<locals>.<genexpr>š  s   é € ÐG¹h©{¨s˜#˜˜b  ¨Õ*»hùs   ‚zcannot move z to z#: unsatisfied parent dependencies: z/; complete the parents first (done or archived))r(  rÞ  )r   Úunsatisfied_parentsrÃ   )rb   rv   rÜ  r  rH   s   &&&  r,   r  r  ’  sa   € ð 	Ð"Ô"ÙÜ×,Ò,¨TÓ;€HßÙØ�Y‰YÑG¹hÓGÓG€FØ˜'˜ $ q¡eÐ+NÈvÈhð  WFð  Gð  Gr.   c               ó(   € V ^8„  d   QhRRRRRRRR/# )	r&   rv   r8   r�  rÎ  r@   r9   r(   r�   r*   )r+   s   "r,   r-   r-   ž  s/   € ÷ gñ g Sð g°>ð gÈ-ð gÐ\`ñ gr.   c                ó$  € \         P                  ! V 4      ;_uu_ 4        . . rTVP                  eh   VP                  P                  4       '       g   \	        RRR7      hVP                  R4       VP                  VP                  P                  4       4       VP                  e-   VP                  R4       VP                  VP                  4       VP                  V4       V P                  RRP                  V4       R	2V4       V P                  R
V\        \        P                  ! 4       4      34       RRR4       \         P                  ! YR Uu. uF  p\        W&4      f   K  VNK  	  upVR7       R#   + '       g   i     LJ; iu upi )u—   PATCH title/body phase: one UPDATE + ``edited`` event, then the post-commit observer
(field names only â€” values never leave the DB via this payload).Nr�   ztitle cannot be emptyrF   z	title = ?zbody = ?zUPDATE tasks SET r  z WHERE id = ?zZINSERT INTO task_events (task_id, kind, payload, created_at) VALUES (?, 'edited', NULL, ?)rW   )r  rv  )r   Ú	write_txnr  r  r   rÔ   rv  rÑ   rÃ   r‰   rJ  Únotify_task_updatedr{   )rb   rv   r�  r@   ÚsetsÚvalsÚfs   &&&&   r,   Ú_patch_title_bodyr,  ž  s(  € ô 
×	Ò	˜T×	"Õ	"Ø˜ˆdØ�=‰=Ò$Ø—=‘=×&Ñ&×(Ò(Ü#°Ð<SÔTÐTØ�K‰K˜Ô$Ø�K‰K˜Ÿ™×+Ñ+Ó-Ô.Ø�<‰<Ò#Ø�K‰K˜
Ô#Ø�K‰K˜Ÿ™Ô%Ø�‰�GÔØ�‰Ð(¨¯©°4«Ð(9¸ÐGÈÔNØ�‰ØhØ”cœ$Ÿ)š)›+Ó&Ð'ô	)÷ 
#ô ×!Ò!ØÑ#4ÓXÑ#4˜a¼ÀÓ8KŸ˜Ñ#4ÑXÐ`e÷g÷ 
#×	"üò  Ys   Ÿ1E:ÁC'E:ÅFÅ)FÅ:F
	c               ó$   € V ^8„  d   QhRRRRRR/# )r&   rv   r8   r�  rÎ  r@   r9   r*   )r+   s   "r,   r-   r-   µ  s!   € ÷  ñ  ˜ð   ~ð  ¸mñ  r.   c                ó  € \        V4      ;_uu_ 4       w  r#\        W04       VP                  R 8H  ;'       d    VP                  RJpVP                  e^   V'       gV   \	        R\
        4      ;_uu_ 4        \        \        P                  ! Y0VP                  ;'       g    R4      4       RRR4       VP                  e   \        W0W4       \         FP  w  rVpV! V4      '       g   K  \	        R\        \
        4      ;_uu_ 4        V! W0V4      pRRR4       \        X4       KR  	  VP                  e   \        W0VP                  V4       VP                  f   VP                  e   \!        W0W4       \        P"                  ! W04      p	RV	'       d2   \%        V	\        P&                  ! W0.4      P)                  V 4      R7      MR/uuRRR4       #   + '       g   i     EL/; i  + '       g   i     LÝ; i  + '       g   i     R# ; i)rÞ  Nr•   r�   r„   r‘  )rc   r†   r   rw  rœ   ÚRuntimeErrorr’   r   r  r  Ú_OVERRIDE_OPSrÄ  rx  r  r  rv  r,  r…   r±   rF  rM   )
rv   r�  r@   rb   r  ÚwantedÚapplyÚ_refusedr�   Úupdateds
   &&&       r,   Úupdate_taskr5  ´  s…  € ä	�U×	Ô	™} Ü�dÔ$ð $+§>¡>°XÑ#=×#^Ð#^À'×BRÑBRÐZ^ÐB^Ð Ø×ÑÒ'×0HÜ˜S¤,×/Õ/ÜœI×1Ò1°$À×AQÑAQ×AYÐAYÐUYÓZÔ[÷ 0à�>‰>Ò%Ü˜$¨ÔKß'4Ñ#ˆF˜8Ù�g�ŒÜ  ¤j´,×?Õ?Ù˜t¨gÓ6�B÷ @ä˜B–ñ	 (5ð
 ×ÑÒ'Ü˜$¨×)9Ñ)9¸5ÔAØ�=‰=Ò$¨¯©Ò(@Ü˜d¨WÔ<Ü×$Ò$ TÓ3ˆØçô #Ø¬I×,MÒ,MÈdÐT]Ó,^×,bÑ,bÐcjÓ,kõ
àð ÷) 
Ò	÷ 0×/Ð/ú÷ @×?ú÷ 
×	Ð	úsI   ”AG7ÁG7Á55G	Â*9G7Ã(G7Ä
G$ÄB6G7ÇG!Ç	G7Ç$G4Ç/G7Ç7H	c               ó    € V ^8„  d   QhRRRR/# rž  r*   )r+   s   "r,   r-   r-   Ð  s   € ÷ 5ñ 5˜ð 5 ]ñ 5r.   c                óÐ   € \        V4      ;_uu_ 4       w  r\        P                  ! W 4      '       g   \        R RV  R2R7      hRRRV /uuRRR4       #   + '       g   i     R# ; i)rC   ztask ro   rF   ÚdeletedTrv   N)rc   r   Údelete_taskr   )rv   r@   rb   s   && r,   r9  r9  Ï  sS   € ä	�U×	Ô	™} Ü×$Ò$ T×3Ò3Ü¨C¸%À¸yÈ
Ð8SÔTÐTØ˜4 ¨GÐ4÷ 
×	×	Ó	ús   ”5AÁA%	c               ó$   € V ^8„  d   QhRRRRRR/# )r&   rb   rk   rv   r8   r(   r¿   r*   )r+   s   "r,   r-   r-   ×  s'   € ÷ Wñ WÐ"4ð W¸sð WÀtñ Wr.   c           
     ó¨   € V P                  RV34      P                  4       pV Uu. uF   pRVR,          RVR,          RVR,          /NK"  	  up# u upi )a>  Parent rows (id, title, status) not ``done`` that block promotion to ``ready``.

Used to enrich the 409 response from :func:`update_task` so the dashboard can show an actionable toast
(#26744) instead of a silent no-op. Returns ``[]`` when nothing blocks the transition (e.g. no parents,
or all parents already done).
zSELECT t.id, t.title, t.status FROM tasks t JOIN task_links l ON l.parent_id = t.id WHERE l.child_id = ? AND t.status != 'done'rµ   r  r   )rÑ   râ   )rb   rv   rç   rè   s   &&  r,   r  r  ×  sa   € ð �<‰<ð	6ð 
ˆ
ó	÷ ‘H“Jð	 	ñ
 RVÓVÑQUÈAˆT�1�T•7˜G Q w¥Z°¸1¸X½;ÓGÑQUÑVÐVùÒVs   ¦&Ac               ó(   € V ^8„  d   QhRRRRRRRR/# )r&   rb   rk   rv   r8   Ú
new_statusr(   r)   r*   )r+   s   "r,   r-   r-   æ  s*   € ÷ 2ñ 2Ð/ð 2¸#ð 2È3ð 2ÐSWñ 2r.   c                ó
  € . pTp\         P                  ! V 4      ;_uu_ 4        V P                  RV34      P                  4       pVf    RRR4       R# VR,          R8X  dM   VR8X  dF   \         P                  ! WVR,          4      pVR8X  d!   \         P
                  ! W4      '       d   RMR	pVR8X  d(   \         P
                  ! W4      '       g    RRR4       R# VR,          R8H  pVR,          R9   ;'       d    VR9  pV P                  R
V3^,          V3,           4      p	V	P                  ^8w  d    RRR4       R# Rp
V'       d^   VR8w  dW   VR,          '       dH   \         P                  ! WRRRV R2R7      p
VP                  VR,          VR,          VR,          34       V P                  RW\        P                  ! RVRV/4      \        \        P                  ! 4       4      34       V'       d1   \         P                  ! WRR7      pVP                  VR,          4       RRR4       V F  w  rÍp\         P                  ! WÍVR7       K   	  VR9   d   \         P                   ! V 4       R#   + '       g   i     LS; i)zýDirect status write for drag-drop moves without a structured verb (todo<->ready,
running<->ready) + a ``status`` event. Leaving ``running`` closes the run as 'reclaimed'
so attempt history isn't orphaned; the worker is killed only AFTER the txn commits.z`SELECT status, current_run_id, worker_pid, claim_lock, worker_started_at FROM tasks WHERE id = ?NFr   r›  r’  Úcurrent_run_idrÞ  r2  a   UPDATE tasks SET status = ?,   claim_lock = CASE WHEN ? = 'running' THEN claim_lock ELSE NULL END,   claim_expires = CASE WHEN ? = 'running' THEN claim_expires ELSE NULL END,   worker_pid = CASE WHEN ? = 'running' THEN worker_pid ELSE NULL END WHERE id = ?Ú	reclaimedzstatus changed to z (dashboard/direct))rm  r   rÐ  Ú
worker_pidÚ
claim_lockÚworker_started_atzbINSERT INTO task_events (task_id, run_id, kind, payload, created_at) VALUES (?, ?, 'status', ?, ?)Úrequested_statusr�  rz   Úterminations)Ú
started_atT>   r(  rI   >   r(  r’  rÞ  )r   r'  rÑ   rD  Ú_retry_status_for_runÚ_parents_satisfiedÚrowcountÚ_end_runrÔ   ÚjsonÚdumpsr‰   rJ  Ú(invalidate_descendants_for_parent_reopenÚextendÚ_terminate_reclaimed_workerÚrecompute_ready)rb   rv   r=  rE  Úeffective_statusÚprevÚresume_statusÚwas_runningÚreopening_satisfied_parentÚcurrˆ   ri  r'  rB  rF  s   &&&            r,   rã  rã  æ  s[  € ð NP€LØ!ÐÜ	×	Ò	˜T×	"Õ	"Ø�|‰|ØnØˆJóç ™›
ð 	ð Š<Ø÷ 
#Ñ	"ð ��>˜YÔ&¨:¸Ô+@Ü%×;Ò;¸DÈ4ÐP`ÕKaÓbˆMØ Ô(Ü/8×/KÒ/KÈD×/ZÒ/Z¡8Ð`fÐ ð ˜wÔ&¬y×/KÒ/KÈD×/ZÒ/ZØ÷ 
#Ñ	"ð ˜8•n¨	Ñ1ˆØ%)¨(¥^Ð7KÑ%K×%|Ð%|ÐP`Ðh|ÑP|Ð"Ø�l‰lðð
 Ð !Õ# w jÕ0ó2ˆð �<‰<˜1ÔØ÷1 
#Ñ	"ð2 ˆßÐ+¨yÔ8¸TÐBR×=SÔ=SÜ×'Ò'Ø {¸;Ø,Ð-=Ð,>Ð>QÐRôTˆFð ×Ñ  lÕ!3°T¸,Õ5GÈÐNaÕIbÐ cÔdØ�‰ØpØœdŸjšj¨(Ð4DÐFXÐZdÐ)eÓfÔhkÔlp×luÒluÓlwÓhxÐyô	{÷ &ô ×GÒGÈÐ^iÔjˆFØ×Ñ  ~Õ 6Ô7÷M 
#óN (4Ñ#ˆ˜Ü×-Ò-¨cÈ*×Uñ (4ð Ð6Ô6Ü×!Ò! $Ô'Ù÷Y 
#×	"ús1   £'I2ÁA>I2ÃI2Ã;:I2Ä?	I2Å	I2ÅCI2É2J	c                  ó0   € ] tR tRt$ R]R&   RtR]R&   RtR# )	ÚCommentBodyi  r8   rv  r�  r9   rw   r*   N)r†  r‡  rˆ  r‰  rŠ  rw   r‹  r*   r.   r,   rX  rX    s   ‡ Ø
ƒIØ'€FˆM×'r.   rX  z/tasks/{task_id}/commentsc               ó$   € V ^8„  d   QhRRRRRR/# )r&   rv   r8   r�  rX  r@   r9   r*   )r+   s   "r,   r-   r-   #  s!   € ÷ ñ ˜ð  {ð ¸=ñ r.   c           	     óP  € VP                   P                  4       '       g   \        R RR7      h\        V4      ;_uu_ 4       w  r#\	        W04       \
        P                  ! Y0VP                  ;'       g    RVP                   R7       RR/uuRRR4       #   + '       g   i     R# ; i)r�   zbody is requiredrF   r�  )rw   rv  r�   TN)rv  r  r   rc   r†   r   Úadd_commentrw   ©rv   r�  r@   rb   s   &&& r,   r[  r[  "  su   € à�<‰<×Ñ×ÒÜ¨Ð4FÔGÐGÜ	�U×	Ô	™} Ü�dÔ$Ü×Ò˜d°G·N±N×4QÐ4QÀkÐX_×XdÑXdÕeØ�dˆ|÷ 
×	×	Ó	ús   ÁABÂB%	c                  ó,   € ] tR tRt$ R]R&   R]R&   RtR# )ÚLinkBodyi,  r8   r  r  r*   N)r†  r‡  rˆ  r‰  rŠ  r‹  r*   r.   r,   r^  r^  ,  s   ‡ ØƒNØ‡Mr.   r^  z/linksc               ó    € V ^8„  d   QhRRRR/# )r&   r�  r^  r@   r9   r*   )r+   s   "r,   r-   r-   2  s   € ÷ ,ñ ,�hð , }ñ ,r.   c           	     ó@  € \        V4      ;_uu_ 4       w  r\        4       ;_uu_ 4        \        P                  ! W P                  V P
                  4      pR RRV/uuRRR4       uuRRR4       #   + '       g   i     M; iRRR4       R#   + '       g   i     R# ; i)r�   TÚgatedN)rc   r<   r   rg  r  r  )r�  r@   rb   ra  s   &&  r,   Úadd_linkrb  1  sg   € ä	�U×	Ô	™} Ô.>×.@Õ.@Ü×$Ò$ T×+<Ñ+<¸g×>NÑ>NÓOˆØ�d˜G UÐ+÷ /AÑ.@×	Ò	×.@×.@ú×	×	×	Ò	ús"   ”B©1A/	Á
BÁ/A?Á:BÂB	c               ó$   € V ^8„  d   QhRRRRRR/# )r&   r  r8   r  r@   r9   r*   )r+   s   "r,   r-   r-   9  s'   € ÷ Oñ O˜3ð O°sð OÐP]ñ Or.   c           
     ó®   € \        V4      ;_uu_ 4       w  r#R \        \        P                  ! W0V4      4      /uuRRR4       #   + '       g   i     R# ; i)r�   N)rc   r)   r   Úunlink_tasks)r  r  r@   rb   s   &&& r,   Údelete_linkrf  8  s:   € ä	�U×	Ô	™} Ø”dœ9×1Ò1°$À8ÓLÓMÐN÷ 
×	×	Ó	ús   ”$AÁA	c          
     ó,   € V ^8„  d   QhRRRRRRRRR	R
/# )r&   rÕ   r8   r�  rÕ  r@   r9   rS  r  r(   r�   r*   )r+   s   "r,   r-   r-   >  s1   € ÷ 5ñ 5˜sð 5¨\ð 5À-ð 5ÐX\ð 5Ðaeñ 5r.   c                ó@  € VP                   '       d0   \        P                  ! W4      '       g   VP                  RRR7       VP                  eb   VP                   '       gP   VP                  p\        WWRRV: 24      '       g-   TP                  R\        WV4      ;'       g    RV: R2R7       VP                  e‡    VP                  '       d.   \        P                  ! YVP                  ;'       g    RRR	7      M*\        P                  ! YVP                  ;'       g    R4      pV'       g   VP                  RR
R7       VP                  e   \        WVP                  V4       \         F;  w  r‰p
V! V4      '       g   K   V	! WV4      '       g   VP                  RV
R7       K;  K=  	  R#   \         d'   pTP                  R\        T4      R7        Rp?L˜Rp?ii ; i  \         \        3 d(   pTP                  R\        T4      R7        Rp?K¨  Rp?ii ; i)u¨   Apply the bulk patch to one task, recording refusals in ``entry`` without aborting the
remaining ops â€” except a rejected status verb (``_StatusRejected`` propagates).Fzarchive refused©r�   ÚerrorNzunknown status ztransition to z refusedT)r×  zassign refused)rÖ  r   r  Úupdater   rþ  r  rw  r×  Úreassign_taskr  r/  r8   rx  r  r0  rÄ  )rb   rÕ   r�  r@   rS  rÜ  r�   r›   r1  r2  Úrefuseds   &&&&&      r,   Ú_bulk_apply_onern  >  sµ  € ð ‡‡€œy×5Ò5°d×@Ò@Ø�‰˜Ð%6ˆÔ7Ø‡~�~Ò!¨'¯/¯/¨/Ø�N‰NˆÜ˜T¨°oÀaÁUÐ4K×LÒLØ�L‰L˜EÔ)=¸dÈÓ)K×)mÐ)mÐQ_Ð`aÑ_dÐdlÐOmˆLÔnØ×ÑÒ#ð	1Øfm×f{×f{Ðf{”)×)Ò)¨$°W×5EÑ5E×5MÐ5MÈÐ]aÕbÜ ×,Ò,¨T¸×8HÑ8H×8PÐ8PÈDÓQð çØ—‘ Ð-=�Ô>ð ×ÑÒ#Ü�d ×!1Ñ!1°5Ô9ß"/Ñˆ�wÙ�'�?Œ?ð5Ù˜T¨×0Ò0Ø—L‘L E°�LÖ9ñ 1ó #0øô	 ô 	1Ø�L‰L˜E¬¨Q«ˆL×0Ñ0ûð	1ûô ¤Ð-ô 5Ø—‘ ¬S°«V�×4Ò4ûð5úsH   Ã F1 Ã#F1 Ã6,F1 Ä#F1 Ä2F1 Æ"G%Æ1G"Æ<GÇG"Ç%HÇ6HÈHz/tasks/bulkc               ó    € V ^8„  d   QhRRRR/# )r&   r�  rÕ  r@   r9   r*   )r+   s   "r,   r-   r-   [  s   € ÷ $ñ $˜ð $¨mñ $r.   c           
     ó$  € V P                   ;'       g    .  Uu. uF  q"'       g   K  VNK  	  ppV'       g   \        RRR7      h. p\        V4      ;_uu_ 4       w  rV FU  pRVRR/p \        P                  ! WV4      f   VP                  RR	R
7       M\        WVWV4        VP                  V4       KW  	  RV/uuRRR4       # u upi   \         d'   pTP                  R\        T4      R
7        Rp?LTRp?ii ; i  + '       g   i     R# ; i)uˆ   Apply the same patch to every id. Independent iteration â€” per-task
failures don't abort siblings; returns per-id outcome for partials.r�   zids is requiredrF   rµ   r�   TNFz	not foundri  Úresults)
r¾   r   rc   r   r…   rk  rn  r2   r8   rÔ   )	r�  r@   Úir¾   rq  rb   rÕ   rS  r›   s	   &&       r,   Úbulk_updaters  Z  sö   € ð —{‘{×(Ð( bÐ(Ó
/Ñ(�«Q�1ˆ1Ñ(€CÐ
/ßÜ¨Ð4EÔFÐFØ€GÜ	�U×	Ô	™} ÛˆCØ%)¨3°°dÐ$;ˆEð5Ü×%Ò% dÓ0Ò8Ø—L‘L E°�LÕ=ä# D¨w¸uÕEð �N‰N˜5Ö!ñ ð ˜7Ð#÷ 
Ò	ùò	 0øô ô 5Ø—‘ ¬S°«V�×4Ñ4ûð5ú÷ 
×	Ð	úsL   ™	C§CÁC>Á(+C
ÂC>ÂC
Â!C>Ã
C;	ÃC6	Ã1C>Ã6C;	Ã;C>Ã>D	z/diagnosticsz*Filter by severity: warning|error|criticalc               ó    € V ^8„  d   QhRRRR/# )r&   r@   r9   r÷   r*   )r+   s   "r,   r-   r-   s  s    € ÷ Vñ VØðVàñVr.   c                ó6  a€ \        V 4      ;_uu_ 4       w  r\        VRR7      pV'       dx   V'       dp   VP                  4        UUUu/ uFQ  w  rEV Uu. uF3  p\        P                  ! VP                  R4      V4      '       g   K1  VNK5  	  up;p'       g   KO  WGbKS  	  ppppV'       g   R. R^ /uuRRR4       # \        VP                  4       4      pVP                  R\        V4       R2\        V4      4      P                  4        U	u/ uF  q™R,          V	bK  	  p
p	. pVP                  4        FV  w  rEV
P                  V4      ;'       g	    R	RR
RRR/p	VP                  RVRV	R	,          RV	R
,          RV	R,          RV/4       KX  	  \        \        P                  4       UUu/ uF  w  rÍWÜbK	  	  uppoVP                  V3R lR7       RVR\!        R V 4       4      /uuRRR4       # u upi u upppi u up	i u uppi   + '       g   i     R# ; i)z‘Tasks with an active diagnostic, highest severity first then most recent; also
consumed by ``hermes kanban diagnostics`` when the dashboard runs.Nr+  r÷   rð   rô   z;SELECT id, title, status, assignee FROM tasks WHERE id IN (rÌ   rµ   r  r   rw  rv   Ú
task_titleÚtask_statusÚtask_assigneec                ó¸   <€ SP                  V R ,          ^ ,          P                  R4      R4      ) V R ,          ^ ,          P                  R4      ;'       g    ^ ) 3# )rð   r÷   rö   rû   )rM   )r×   Úsev_idxs   &€r,   r5  Ú"list_diagnostics.<locals>.<lambda>‹  sT   ø€ Ø�[‰[˜˜]Õ+¨AÕ.×2Ñ2°:Ó>ÀÓCÐCÀsÈ=ÕGYÐZ[ÕG\×G`ÑG`ÐaoÓGp×GuÐGuÐtuÐEvñ"xr.   r;  c              3  óF   "  € T F  p\        VR ,          4      x € K  	  R# 5i)rð   N)rÄ   )r`  r°   s   & r,   rb  Ú#list_diagnostics.<locals>.<genexpr>�  s   é € Ð0TÑPSÈ1´°Q°}Õ5E×1FÐ1FÓPSùs   ‚!)rc   rî   Úitemsrà   Úseverity_at_or_aboverM   r¿   ÚkeysrÑ   rÅ   rÒ   râ   rÔ   Ú	enumeraterý   rH  Úsum)r@   r÷   rb   Údiags_by_taskrÕ   Údlr°   Úkeepr¾   rè   rç   rì   rr  rÜ  rz  s   &&            @r,   Úlist_diagnosticsr†  r  s  ø€ ô 
�U×	Ô	™} Ü1°$ÀÔFˆßŸð  -×2Ñ2Ô4õfá4‘G�CÙ(*Óc© 1¬b×.EÒ.EÀaÇeÁeÈJÓFWÐYa×.bŸQ˜Q©ÑcÐc�DÓcô �’	Ù4ð ò f÷ Ø! 2 w°Ð2÷ 
Ò	ô �=×%Ñ%Ó'Ó(ˆØ$(§L¡LØIÌ-ÐX[ÓJ\ÐI]Ð]^Ð_ÔafÐgjÓakó%mßmuÑmuÓmwð%xó yñ %x˜q�$•˜’
ñ %xˆð yàˆØ$×*Ñ*Ö,‰GˆCØ—‘˜“×RÐR '¨4°¸4ÀÈTÐ!RˆAØ�J‰JØ˜3 ¨a°­j¸-ÈÈ8ÍÐVeÐghÐisÕgtØ˜rð#ö $ñ -ô
 %.¬b×.?Ñ.?Ô$@ÔAÑ$@™D˜A�1’4Ñ$@ÒAˆØ�‰ô xˆô 	yà˜s G¬SÑ0TÑPSÓ0TÓ-TÐU÷+ 
Ò	ùò dùôfùòyùó B÷% 
×	Ð	úsl   •HµHÁ	G5Á.G0ÂG0ÂG5ÂG5Â HÂ,HÂ<AHÄG<Ä0HÅAHÆ)H
Æ6/HÇ0G5Ç5HÈH	z/workers/activec               ó   € V ^8„  d   QhRR/# rU   r*   )r+   s   "r,   r-   r-   ™  s   € ÷ [ñ [˜}ñ [r.   c                ó@  € \        V 4      ;_uu_ 4       w  rVP                  R4      P                  4       pV Uu. uF  p\        V4      NK  	  ppRVR\	        V4      R\        \        P                  ! 4       4      /uuRRR4       # u upi   + '       g   i     R# ; i)zŠEvery running worker: an open ``task_runs`` row with a ``worker_pid`` whose
task is ``running``. Returns ``{workers, count, checked_at}``.az  SELECT r.id AS run_id, r.task_id, t.title AS task_title, t.status AS task_status, t.assignee AS task_assignee, r.profile, r.worker_pid, r.started_at, r.claim_lock, r.claim_expires, r.last_heartbeat_at, r.max_runtime_seconds FROM task_runs r JOIN tasks t ON t.id = r.task_id WHERE r.ended_at IS NULL AND r.worker_pid IS NOT NULL AND t.status = 'running' ORDER BY r.started_at ASCÚworkersrô   Ú
checked_atN)rc   rÑ   râ   r  rÄ   r‰   rJ  )r@   rb   rç   r×   r‰  s   &    r,   Úlist_active_workersr‹  ˜  s„   € ô 
�U×	Ô	™} Ø�|‰|ð(ó)÷ *2©«ð 	ñ )-Ó-© ”4˜–9©ˆÐ-Ø˜7 G¬S°«\¸<ÌÌTÏYÊYË[ÓIYÐZ÷ 
Ò	ùò .÷ 
×	Ð	ús   ”&BºBÁ.BÂBÂB	z/runs/{run_id}c               ó    € V ^8„  d   QhRRRR/# ©r&   rˆ   r‰   r@   r9   r*   )r+   s   "r,   r-   r-   ©  s   € ÷ ;ñ ;˜Sð ;¨ñ ;r.   c           	     ó–   € \        V4      ;_uu_ 4       w  rR\        \        W 4      4      /uuRRR4       #   + '       g   i     R# ; i)zT``{run: {...}}`` with the same serialisation as ``GET /tasks/{id}``; 404 if unknown.r‹   N)rc   r   r�   )rˆ   r@   rb   s   && r,   Úget_run_endpointr�  ¨  s4   € ô 
�U×	Ô	™} Ø”vœl¨4Ó8Ó9Ð:÷ 
×	×	Ó	úó	   ”7·A	z/runs/{run_id}/inspectc               ó    € V ^8„  d   QhRRRR/# r�  r*   )r+   s   "r,   r-   r-   °  s   € ÷ "Wñ "W ð "W¨]ñ "Wr.   c                ór  a € \        V4      ;_uu_ 4       w  r\        VS 4      pRRR4       R V 3R llpXP                  e	   V! R4      # VP                  pVf	   V! R4      # \        f   V! RVR7      #  \        P
                  ! V4      pVP                  . ROR7      p VP                  4       pVP                  R	4      p	RS RRRTRVP                  R4      RV	'       d   V	P                  MRRV	'       d   V	P                  MRR
VP                  R
4      RVRVP                  R4      RVP                  R4      RVP                  R4      /#   + '       g   i     EL2; i  \         d    Rp L¿i ; i  \        P                   d    T! RTR7      u # \        P                   d    RS RRRTRR/u # i ; i)z‰Live psutil stats for a run's worker; ``{alive: false, reason}`` when unavailable and
access-denied reported inline rather than as a 500.Nc               ó    € V ^8„  d   QhRRRR/# )r&   rí  r8   r(   r  r*   )r+   s   "r,   r-   Ú*inspect_run_endpoint.<locals>.__annotate__¶  s   € ÷ Mñ M�cð M tñ Mr.   c                ó   <€ R SRR/VCRV /C# )rˆ   ÚaliveFrí  r*   )rí  Úextrarˆ   s   &,€r,   Ú_deadÚ#inspect_run_endpoint.<locals>._dead¶  s   ø€ Ø˜& '¨5ÐL°EÐL¸8ÀVÑLÐLr.   zrun already endedzno worker_pid recordedzpsutil not available)r'  Úcpu_percentÚmemory_infoÚnum_threadsr   Úcreate_timeÚcmdline)Úattrsrˆ   r–  Tr'  Úmemory_rss_bytesÚmemory_vms_bytesÚnum_fdszprocess not foundrj  zaccess denied)rš  r›  rœ  r   r�  rž  )rc   r�   Úended_atrA  Ú_psutilÚProcessÚas_dictr¢  ÚAttributeErrorrM   ÚrssÚvmsÚNoSuchProcessÚAccessDenied)
rˆ   r@   rb   rè   r˜  r'  ÚprocÚinfor¢  Úmems
   f&        r,   Úinspect_run_endpointr¯  ¯  s§  ø€ ô 
�U×	Ô	™} Ü˜˜vÓ&ˆ÷ 
÷Mð Mð 	‡z�zÒÙÐ(Ó)Ð)Ø
�,‰,€CØ
‚{ÙÐ-Ó.Ð.Ü‚ÙÐ+°Ô5Ð5ðWÜ�Š˜sÓ#ˆØ�|‰|Ò"sˆ|Ótˆð	Ø—l‘l“nˆGð �h‰h�}Ó%ˆà�f˜g t¨U°CØ˜4Ÿ8™8 MÓ2Ø¯3 §¢°DØ¯3 §¢°DØ˜4Ÿ8™8 MÓ2°I¸wØ�d—h‘h˜xÓ(¨-¸¿¹À-Ó9PÐR[Ð]a×]eÑ]eÐfoÓ]pðrð 	r÷+ 
×	Ð	ûô$ ô 	ØŠGð	ûô × Ñ ô 3ÙÐ(¨cÔ2Ò2Ü×Ñô WØ˜& '¨4°¸¸WÀoÐVÒVðWúsZ   •EÁ6*E4 Â!E! Â10E4 Ã"E4 Ã8AE4 ÅE	Å!E1Å.E4 Å0E1Å1E4 Å4F6ÆF6Æ(F6Æ5F6c                  ó&   € ] tR tRt$ RtR]R&   RtR# )ÚTerminateRunBodyiÕ  Nr9   rí  r*   ©r†  r‡  rˆ  r‰  rí  rŠ  r‹  r*   r.   r,   r±  r±  Õ  ó   ‡ Ø €FˆM× r.   r±  z/runs/{run_id}/terminatec               ó$   € V ^8„  d   QhRRRRRR/# )r&   rˆ   r‰   r�  r±  r@   r9   r*   )r+   s   "r,   r-   r-   Ú  s'   € ÷ Dñ D 3ð DÐ1Að DÈ-ñ Dr.   c           	     óz  € \        V4      ;_uu_ 4       w  r#\        W04      pVP                  e   \        RV  R24      h\        P
                  ! W4P                  VP                  R7      '       g   \        RV  RVP                   R24      hRR	R
V RVP                  /uuRRR4       #   + '       g   i     R# ; i)ai  Terminate an in-flight run via ``reclaim_task`` (same SIGTERM->SIGKILL flow, bookkeeping
and events as ``POST /tasks/{id}/reclaim``); 409 if already ended / not reclaimable.

Closes the gap left by PR #28432, which shipped the read-only sibling endpoints (``/workers/active``,
``/runs/{run_id}``, ``/runs/{run_id}/inspect``) but no termination control surface.
Nzrun z already endedrì  zcannot terminate run z: task z$ is no longer in a reclaimable stater�   Trˆ   rv   )rc   r�   r£  r–   r   Úreclaim_taskrv   rí  )rˆ   r�  r@   rb   rè   s   &&&  r,   Úterminate_run_endpointr·  Ù  s™   € ô 
�U×	Ô	™} Ü˜Ó&ˆØ�:‰:Ò!Ü˜d 6 (¨.Ð9Ó:Ð:Ü×%Ò% d¯I©I¸g¿n¹n×MÓMÜÐ3°F°8¸7À1Ç9Á9À+ÐMqÐrÓsÐsØ�d˜H f¨i¸¿¹ÐC÷ 
×	×	Ó	ús   ”B
B)Â)B:	c                  ó&   € ] tR tRt$ RtR]R&   RtR# )ÚReclaimBodyiì  Nr9   rí  r*   r²  r*   r.   r,   r¹  r¹  ì  r³  r.   r¹  z/tasks/{task_id}/reclaimc               ó$   € V ^8„  d   QhRRRRRR/# )r&   rv   r8   r�  r¹  r@   r9   r*   )r+   s   "r,   r-   r-   ñ  s!   € ÷ 0ñ 0 3ð 0°ð 0À]ñ 0r.   c                óä   € \        V4      ;_uu_ 4       w  r#\        P                  ! W0VP                  R7      '       g   \	        RV  R24      hRRRV /uuRRR4       #   + '       g   i     R# ; i)ztRelease an active worker claim without waiting for the claim TTL
(``hermes kanban reclaim <task_id> --reason ...``).rì  zcannot reclaim z7: not in a claimable state (not running, or unknown id)r�   Trv   N)rc   r   r¶  rí  r–   r\  s   &&& r,   Úreclaim_task_endpointr¼  ð  sZ   € ô 
�U×	Ô	™} Ü×%Ò% d¸G¿N¹N×KÓKÜ˜o¨g¨YÐ6mÐnÓoÐoØ�d˜I wÐ/÷ 
×	×	Ó	ús   ”?AÁA/	c                  ó*   € ] tR tRt$ RtRtR]R&   RtR# )ÚSpecifyBodyiú  zyOnly the author is configurable; model + prompt come from
``auxiliary.triage_specifier`` in config.yaml, same as the CLI.Nr9   rw   r*   )r†  r‡  rˆ  r‰  rÚ  rw   rŠ  r‹  r*   r.   r,   r¾  r¾  ú  s   ‡ ñGð !€FˆM× r.   r¾  z/tasks/{task_id}/specifyc               ó$   € V ^8„  d   QhRRRRRR/# )r&   rv   r8   r�  r¾  r@   r9   r*   )r+   s   "r,   r-   r-     s&   € ÷ zñ z 3ð z°ð zÀ]ñ zr.   c                ó¨   € \        VRRWP                  4      pR\        VP                  4      RVP                  RVP
                  RVP                  /# )uµ   Flesh out a triage task via the auxiliary LLM (``hermes kanban specify``). Non-OK is NOT
an HTTP error â€” the UI renders the reason inline. Sync ``def`` â†’ runs in the threadpool.Úkanban_specifyÚspecify_taskr�   rv   rí  Ú	new_title)r€   rw   r)   r�   rv   rí  rÃ  ©rv   r�  r@   rm  s   &&& r,   Úspecify_task_endpointrÅ    sI   € ô �uÐ.°ÀÏÉÓX€GØ”$�w—z‘zÓ" I¨w¯©ÀÈ'Ï.É.ÐZeÐgn×gxÑgxÐyÐyr.   c                  óB   € ] tR tRt$ RtR]R&   RtR]R&   RtR]R&   R	tR# )
ÚReassignBodyi	  Nr9   ÚprofileFr)   r×  rí  r*   )	r†  r‡  rˆ  r‰  rÈ  rŠ  r×  rí  r‹  r*   r.   r,   rÇ  rÇ  	  s    ‡ Ø!€Gˆ]Ó!Ø€M�4ÓØ €FˆM× r.   rÇ  z/tasks/{task_id}/reassignc               ó$   € V ^8„  d   QhRRRRRR/# )r&   rv   r8   r�  rÇ  r@   r9   r*   )r+   s   "r,   r-   r-     s&   € ÷ 
Uñ 
U Cð 
U°,ð 
UÀ}ñ 
Ur.   c           
     óf  € \        V4      ;_uu_ 4       w  r#\        P                  ! Y0VP                  ;'       g    R\	        VP
                  4      VP                  R7      pV'       g   \        RV  R24      hRRRT RVP                  ;'       g    R/uuRRR4       #   + '       g   i     R# ; i)	zvReassign to another profile, optionally reclaiming first
(``hermes kanban reassign <task_id> <profile> [--reclaim]``).N)r×  rí  zcannot reassign zS: unknown id, or still running (pass reclaim_first=true to release the claim first)r�   Trv   rw  )rc   r   rl  rÈ  r)   r×  rí  r–   )rv   r�  r@   rb   r�   s   &&&  r,   Úreassign_task_endpointrË    s�   € ô 
�U×	Ô	™} Ü×$Ò$Ø˜7Ÿ?™?×2Ð2¨dÄ$Àw×G\ÑG\ÓB]Ðfm×ftÑftôvˆçÜØ" 7 )ð ,Oð OóPð Pð �d˜I w°
¸G¿O¹O×<SÐ<SÈtÐT÷ 
×	×	Ó	ús   ”A<BÂBÂB0	u  You estimate how much work an autonomous coding agent will spend on a kanban task. Given the task title and description, respond with STRICT JSON only (no prose, no code fence):
{"est_tokens": <integer total tokens across the whole run>, "complexity": "S"|"M"|"L", "rationale": "<one short sentence>"}
Base the token figure on a realistic multi-turn agent run (reading files, tool calls, edits, retries) â€” not a single reply. Sâ‰ˆsmall/localized, Mâ‰ˆmulti-file, Lâ‰ˆbroad or ambiguous. Be honest that this is a rough guess.c                  ó4   € ] tR tRt$ RtR]R&   RtR]R&   RtR# )	ÚEstimateBodyi*  rB   r8   r  Nr9   rv  r*   )r†  r‡  rˆ  r‰  r  rŠ  rv  r‹  r*   r.   r,   rÍ  rÍ  *  s   ‡ Ø€Eˆ3ƒOØ€Dˆ-×r.   rÍ  z	/estimatec               ó   € V ^8„  d   QhRR/# )r&   r�  rÍ  r*   )r+   s   "r,   r-   r-   0  s   € ÷ Dñ D Lñ Dr.   c                óF   € \        V P                  V P                  RR7      # )zCEstimate from raw title/body (create dialog, before a task exists).N©rv   )Ú_run_estimater  rv  )r�  s   &r,   Úestimate_text_endpointrÒ  /  s   € ô ˜Ÿ™¨¯©¸dÔCÐCr.   z/tasks/{task_id}/estimatec               ó    € V ^8„  d   QhRRRR/# rž  r*   )r+   s   "r,   r-   r-   6  s   € ÷ Añ A Cð A°ñ Ar.   c                ó¾   € \        V4      ;_uu_ 4       w  r\        W 4      pRRR4       \        XP                  VP                  V R7      #   + '       g   i     L2; i)zREstimate for an existing task; ``{ok, est_tokens, complexity, rationale, model}``.NrÐ  )rc   r†   rÑ  r  rv  )rv   r@   rb   r„   s   &&  r,   Úestimate_task_endpointrÕ  5  sB   € ô 
�U×	Ô	™} Ü˜TÓ+ˆ÷ 
ä˜Ÿ™ T§Y¡Y¸Ô@Ð@÷ 
×	ús   ”AÁA	c               ó$   € V ^8„  d   QhRRRRRR/# )r&   rÜ  r9   r  r‰   r(   r8   r*   )r+   s   "r,   r-   r-   =  s!   € ÷ /ñ /ˆMð /˜cð / cñ /r.   c                ór   € T ;'       g    R P                  4       p \        V 4      V8:  d   V # V RV R,           # )rB   Nu   â€¦)r  rÄ   )rÜ  r  s   &&r,   Ú_caprØ  =  s3   € Ø	
�ˆˆb�‰Ó€AÜ�A“˜!”ˆ1Ð.  2 A ¨¥Ð.r.   c               ó(   € V ^8„  d   QhRRRRRRRR/# )r&   r  r8   rv  r9   rv   r(   r  r*   )r+   s   "r,   r-   r-   B  s/   € ÷ .Yñ .Y˜ð .Y Mð .Y¸}ð .YÐQUñ .Yr.   c               óü  € T ;'       g    RP                  4       '       g   RRRR/#  ^ RIHp R\	        T R	4       R
\	        TR4      ;'       g    R 2p^ RIHpHpHp T! 4       '       d   RMT! RT;'       g    R 24      p T! RRRR\        /RRRT/.RR^<R7      p	 Te	   T! T4        T	P                  ^ ,          P                  P                  ;'       g    RP                  4       p\        T	RR4      p TP!                  4       P#                  R4      '       d   RM%\$        P&                  ! RT\$        P(                  4      p\*        P,                  ! T'       d   TP/                  ^ 4      MT4      p\1        T\2        4      '       d   TMRpT'       g   RRRR/#  \5        TP7                  R4      ;'       g    ^ 4      p\=        TP7                  R4      ;'       g    R4      P                  4       P?                  4       pRR RTRTR"9   d   TMRR!\=        TP7                  R!4      ;'       g    R4      P                  4       ;'       g    RRT/#   \         d
    RRRR/u # i ; i  \         d4   p
RRRR\        T
4      P                   2/u Rp
?
Te
   T! T4       # # Rp
?
ii ; i  Te
   T! T4       i i ; i  \         d    RRrË ELÔi ; i  \         d    Rp ELQi ; i  \8        \:        3 d    ^ p EL8i ; i)#uj   Never raises â€” config/parse/API errors become ``{"ok": False, "reason"}`` so the UI renders them inline.rB   r�   Frí  za title is required to estimate)Úcall_llmzauxiliary client unavailablezTitle: r�   z

Description:
i   z(none))Úget_affinity_scopeÚreset_affinity_scopeÚset_affinity_scopeNzkanban:ÚestimateÚkanban_estimatorÚroleÚsystemÚcontentÚuserg        i,  )r„   ÚmessagesÚtemperatureÚ
max_tokensÚtimeoutzLLM error: ÚmodelÚ{z\{.*\}z*could not parse an estimate from the modelÚ
est_tokensÚ
complexityTÚ	rationale>   ÚLÚMÚS) r  Úagent.auxiliary_clientrÛ  r2   rØ  Úagent.portal_tagsrÜ  rÝ  rÞ  Ú_ESTIMATE_SYSTEM_PROMPTÚtyper†  Úchoicesrœ  rã  r{   ÚlstripÚ
startswithÚreÚsearchÚDOTALLrK  ÚloadsÚgroupÚ
isinstancer  r‰   rM   Ú	TypeErrorrÄ  r8   Úupper)r  rv  rv   rÛ  Úuser_msgrÜ  rÝ  rÞ  Úaffinity_tokenÚrespr]   Úrawré  r-  rr   Úparsedrë  rì  s   &&$               r,   rÑ  rÑ  B  sõ  € à�KˆK�R×Ñ× Ò Ø�e˜XÐ'HÐIÐIðGÝ3ð œ˜e SÓ)Ð*Ð*<¼TÀ$ÈÓ=M×=YÐ=YÐQYÐ<ZÐ[€H÷ _Ñ^Ù/×1Ò1‘TÑ7IÈGÐT[×TiÐTiÐ_iÐSjÐJkÓ7l€Nð	1ÙØ#Ø˜x¨Ô4KÐLÈvÐW]Ð_hÐjrÐNsÐtØ¨°Rô9‰ð Ò%Ù  Ô0ðØ�|‰|˜A�×&Ñ&×.Ñ.×4Ð4°"×;Ñ;Ó=ˆÜ˜˜g tÓ,ˆð
Ø—J‘J“L×+Ñ+¨C×0Ò0‰D´b·i²iÀ	È3ÔPR×PYÑPYÓ6ZˆÜ�jŠj§q˜Ÿ™ œ¨cÓ2ˆÜ" 3¬×-Ò-‘°4ˆ÷ Ø�e˜XÐ'SÐTÐTðÜ˜Ÿ™ LÓ1×6Ð6°QÓ7ˆ
ô �V—Z‘Z Ó-×3Ð3°Ó4×:Ñ:Ó<×BÑBÓD€Jàˆd�L *¨lÈ*ÐXgÔJg¹JÐmqØ”S˜Ÿ™ KÓ0×6Ð6°BÓ7×=Ñ=Ó?×GÐGÀ4ÈÐRWðYð YøôM ô GØ�e˜XÐ'EÐFÒFðGûô ô KØ�e˜X¨´T¸#³Y×5GÑ5GÐ4HÐ'IÐJÔJàÒ%Ù  Õ0ð &ûðKûð Ò%Ù  Õ0ð &ûô
 ô Ø˜‹Uðûô ô Ø‹ðûô ”zÐ"ô Ø‹
ðús�   ¨I ÂI( Â,-J; ÃJ; Ã9$K Ä?K Å1K ÆK$ Æ;K$ ÉI%É$I%É(J&É3J!ÊJ&ÊJ) Ê!J&Ê&J) Ê)J8Ê;KËKËK!Ë K!Ë$K;Ë:K;c               ó   € V ^8„  d   QhRR/# )r&   r(   r  r*   )r+   s   "r,   r-   r-   u  s   € ÷ ñ ˜tñ r.   c                 óX   €  ^ RI Hp  V ! 4       ;'       g    / #   \         d    / u # i ; i)é    rÊ   )rß   rË   r2   rÊ   s    r,   Ú_load_config_or_emptyr  u  s.   € ðÝ1Ù‹}×"Ð" Ð"øÜô ØŠ	ðús   ‚ – ™)¨)z/configc                 óV  € \        4       P                  R4      ;'       g    / P                  R4      ;'       g    / p RV P                  R4      ;'       g    RR\        V P                  RR4      4      R\        V P                  RR4      4      R	\        V P                  R	R4      4      /# )
zJKanban dashboard preferences from the ``dashboard.kanban`` config section.r�  ÚkanbanÚdefault_tenantrB   Úlane_by_profileTÚinclude_archived_by_defaultFÚrender_markdown)r  rM   r)   )Úk_cfgs    r,   Ú
get_configr  }  s˜   € ô #Ó$×(Ñ(¨Ó5×;Ð;¸×@Ñ@ÀÓJ×PÐPÈb€Eà˜%Ÿ)™)Ð$4Ó5×;Ð;¸Øœ4 §	¡	Ð*;¸TÓ BÓCØ%¤t¨E¯I©IÐ6SÐUZÓ,[Ó'\Øœ4 §	¡	Ð*;¸TÓ BÓCð	Eð E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       pTP                  P	                  4        UUu. uF‡  w  r#T'       g   K  TP
                  '       g   K#  RTP                  RTP
                  P                  RTP
                  P                  ;'       g    RRTP
                  P                  ;'       g    R/NK‰  	  pppTP                  R R	7       T#   \         d    . u # i ; iu uppi )
z‘Every platform with a home_channel, from the live GatewayConfig (so env overlays
like ``TELEGRAM_HOME_CHANNEL`` are honored), sorted by platform.)Úload_gateway_configÚplatformÚchat_idÚ	thread_idrB   r<  ÚHomec                ó   € V R ,          # )r  r*   )rè   s   &r,   r5  Ú+_configured_home_channels.<locals>.<lambda>˜  s   € ˜a 
žmr.   r;  )Úgateway.configr  r2   Ú	platformsr~  Úhome_channelÚvaluer  r  r<  rH  )r  Úgw_cfgr  Úpcfgri  s        r,   Ú_configured_home_channelsr   Œ  sÜ   € ðÝ6Ù$Ó&ˆð %×.Ñ.×4Ñ4Ô6ôVñ 7‰NˆH¼$ô	càCG×CTÕCTô	cˆ�X—^‘^ Y°×0AÑ0A×0IÑ0IØ	�d×'Ñ'×1Ñ1×7Ð7°R¸À×ARÑAR×AWÑAW×AaÐAaÐ[aó	cá6ð ñ Vð ‡K�KÑ+€KÔ,Ø€Møô ô ØŠ	ðüóVs-   ‚C ¬C$½C$Á?C$ÂC$Â1C$ÃC!Ã C!c               ó   € V ^8„  d   QhRR/# )r&   r(   r8   r*   )r+   s   "r,   r-   r-   œ  s   € ÷ ñ ˜cñ r.   c                 óV   €  ^ RI Hp  V ! 4       ;'       g    R#   \         d     R# i ; i)z5Current Hermes profile name for notify-sub ownership.©Úget_active_profile_nameÚdefault)Úhermes_cli.profilesr$  r2   r#  s    r,   Ú_active_profile_namer'  œ  s-   € ðÝ?Ù&Ó(×5Ð5¨IÐ5øÜô Úðús   ‚ – ™(§(c               ó$   € V ^8„  d   QhRRRRRR/# )r&   r  r8   rH   r(   r  r*   )r+   s   "r,   r-   r-   ¥  s!   € ÷ ñ  ð ¨cð °dñ r.   c                ón   a € \        V 3R  l\        4        4       R4      pV'       g   \        RVR7      hV# )c              3  óF   <"  € T F  qR ,          S8X  g   K  Vx € K  	  R# 5i)r  Nr*   )r`  Úhr  s   & €r,   rb  Ú%_home_for_platform.<locals>.<genexpr>¦  s   øé € ÐUÑ7�q¸Z½=ÈHÑ;T—’Ó7ùs   ƒ!—
!NrC   rF   )Únextr   r   )r  rH   Úhomes   f& r,   Ú_home_for_platformr/  ¥  s/   ø€ ÜÔUÔ5Ô7ÓUÐW[Ó\€DßÜ¨°FÔ;Ð;Ø€Kr.   z/home-channelsc               ó    € V ^8„  d   QhRRRR/# )r&   rv   r9   r@   r*   )r+   s   "r,   r-   r-   ­  s   € ÷ ñ ˜}ð À=ñ r.   c           
     ó:  € \        4       p\        4       pV '       d¯   \        V4      ;_uu_ 4       w  r\        P                  ! W@4      pRRR4       X Uu0 uFn  p\        VP                  R4      ;'       g    R4      \        VP                  R4      ;'       g    R4      \        VP                  R4      ;'       g    R4      3kKp  	  ppRV Uu. uF&  p/ VCRVR,          VR,          VR,          3V9   /CNK(  	  up/#   + '       g   i     L¿; iu upi u upi )zŒEvery platform with a home channel plus whether *task_id* (if given) is
subscribed to it; without ``task_id`` every ``subscribed`` is false.Nr  rB   r  r  Úhome_channelsÚ
subscribed)r   Úsetrc   ÚkbnÚlist_notify_subsr8   rM   )rv   r@   ÚhomesÚsubscribed_homesrb   ÚsubsÚsubr.  s   &&      r,   Úget_home_channelsr;  ¬  s  € ô &Ó'€EÜ25³%ÐßÜ˜×Ô¡= EÜ×'Ò'¨Ó6ˆD÷  ñ y}ó~Ùx|ÐqtŒS�—‘˜Ó$×*Ð*¨Ó+¬S°·±¸Ó1C×1IÐ1IÀrÓ-JÌCÐPS×PWÑPWÐXcÓPd×PjÐPjÐhjÓLkÓlÑx|ð 	ð ~àÙw|ó~Ùw|ÐosÐjˆ4Ðj�  ZÕ 0°$°yµ/À4ÈÕCTÐUÐYiÑiÔjÑw|ñ~ð ð ÷	  ×üò~ùò~s)   °D Á!DÁ7"DÂ"DÂ=DÃ,DÄ D	z*/tasks/{task_id}/home-subscribe/{platform}c               ó$   € V ^8„  d   QhRRRRRR/# ©r&   rv   r8   r  r@   r9   r*   )r+   s   "r,   r-   r-   ¼  s&   € ÷ Fñ F˜Cð F¨3ð F°}ñ Fr.   c                ó,  € \        VRV: RV R24      p\        V4      ;_uu_ 4       w  r$\        W@4       \        P                  ! Y@YR,          VR,          ;'       g    R\        4       R7       RR	R
V RV/uuRRR4       #   + '       g   i     R# ; i)zŠSubscribe *task_id* to *platform*'s home channel. Idempotent at the DB
layer; 404 when the platform has no home or the task doesn't exist.ú(No home channel configured for platform zJ. Set one from the messenger via /sethome, or configure gateway.platforms.z.home_channel in config.yaml.r  r  N)rv   r  r  r  Únotifier_profiler�   Trv   r  )r/  rc   r†   r5  Úadd_notify_subr'  ©rv   r  r@   r.  rb   s   &&&  r,   Úsubscribe_homerC  »  s”   € ô ØØ
2°8±,ð ?à%˜JÐ&Cð	EóF€Dô
 
�U×	Ô	™} Ü�dÔ$Ü×ÒØ¨HÀ9½oØ˜;Õ'×/Ð/¨4ÔBVÓBXõ	Zð �d˜I w°ÀÐE÷ 
×	×	Ó	ús   ¨ABÂB	c               ó$   € V ^8„  d   QhRRRRRR/# r=  r*   )r+   s   "r,   r-   r-   Í  s&   € ÷ Fñ F˜cð F¨Sð F¸ñ Fr.   c           
     óþ   € \        VRV: R24      p\        V4      ;_uu_ 4       w  r$\        P                  ! Y@YR,          VR,          ;'       g    RR7       RRR	V R
V/uuRRR4       #   + '       g   i     R# ; i)zGRemove any notify subscription on *task_id* matching *platform*'s home.r?  Ú.r  r  N)rv   r  r  r  r�   Trv   r  )r/  rc   r5  Úremove_notify_subrB  s   &&&  r,   Úunsubscribe_homerH  Ì  su   € ô ˜hÐ*RÐS[ÑR^Ð^_Ð(`Óa€DÜ	�U×	Ô	™} Ü×ÒØ¨HÀ9½oÐY]Ð^iÕYj×YrÐYrÐnrõ	tà�d˜I w°ÀÐE÷ 
×	×	Ó	ús   ¥;A+Á+A<	z/statsc               ó   € V ^8„  d   QhRR/# rU   r*   )r+   s   "r,   r-   r-   Ù  s   € ÷ +ñ +�]ñ +r.   c                ó–   € \        V 4      ;_uu_ 4       w  r\        P                  ! V4      uuRRR4       #   + '       g   i     R# ; i)zNPer-status + per-assignee counts + oldest-ready age (HUD and router profiles).N)rc   r   Úboard_statsra   s   & r,   Ú	get_statsrL  Ø  s0   € ô 
�U×	Ô	™} Ü×$Ò$ TÓ*÷ 
×	×	Ó	úr�  z
/assigneesc               ó   € V ^8„  d   QhRR/# rU   r*   )r+   s   "r,   r-   r-   à  s   € ÷ >ñ >˜ñ >r.   c                óš   € \        V 4      ;_uu_ 4       w  rR\        P                  ! V4      /uuRRR4       #   + '       g   i     R# ; i)z{Union of on-disk profiles and assignees used on the board, so a fresh
profile appears in the picker before it has any task.r@  N)rc   r   rI  ra   s   & r,   Úget_assigneesrO  ß  s5   € ô 
�U×	Ô	™} ØœY×6Ò6°tÓ<Ð=÷ 
×	×	Ó	ús	   ”9¹A
	z/tasks/{task_id}/logi€„ )ÚgeÚlec               ó$   € V ^8„  d   QhRRRRRR/# )r&   rv   r8   Útailr¨   r@   r9   r*   )r+   s   "r,   r-   r-   è  s'   € ÷ 
_ñ 
_˜#ð 
_ ]ð 
_Ð]jñ 
_r.   c                ó¬  € \        V4      ;_uu_ 4       w  r#\        W04       RRR4       \        P                  ! WVR7      p\        P                  ! WR7      pVP                  4       '       d   VP                  4       P                  M^ pRT R\        V4      RVRJRTRT;'       g    R	R
\        T;'       d    Wa8„  4      /#   + '       g   i     L©; i)z‘Worker stdout/stderr log. ``tail`` caps the response bytes; 404 if the
task never spawned. On-disk log rotates at 2 MiB with one ``.log.1`` kept.N)Ú
tail_bytesr@   rW   rv   rÀ  ÚexistsÚ
size_bytesrã  rB   Ú	truncated)
rc   r†   r   Úread_worker_logÚworker_log_pathrV  ÚstatÚst_sizer8   r)   )rv   rS  r@   rb   rã  Úlog_pathr¸   s   &&&    r,   Úget_task_logr^  ç  s°   € ô 
�U×	Ô	™} Ü�dÔ$÷ 
ä×'Ò'¨ÈÔN€GÜ×(Ò(¨Ô>€HØ&.§o¡o×&7Ò&7ˆ8�=‰=‹?×"Ò"¸Q€Dà�7˜F¤C¨£M°8¸WÈDÐ=PØ�d˜I w§} }°"°kÄ4È×H\ÐH\ÐQUÑQ\ÓC]ð_ð _÷ 
×	ús   ”CÃC	z	/dispatchrü   )Úaliasc               ó$   € V ^8„  d   QhRRRRRR/# )r&   Údry_runr)   Úmax_nr‰   r@   r9   r*   )r+   s   "r,   r-   r-   ö  s"   € ÷ +ñ +�dð +°#ð +ÐVcñ +r.   c           	     ó  € \        V4      ;_uu_ 4       w  r#\        P                  ! W0WR7      p \        V4      uuRRR4       #   \         d    R\        T4      /u uuRRR4       # i ; i  + '       g   i     R# ; i)zCDispatch nudge so the UI doesn't wait out the 60 s dispatcher tick.)ra  Ú	max_spawnr@   Nri  )rc   ÚkbdÚdispatch_oncer   rþ  r8   )ra  rb  r@   rb   ri  s   &&&  r,   Údispatchrg  õ  sm   € ô 
�U×	Ô	™} Ü×"Ò" 4ÀEÔWˆð	+Ü˜&“>÷ 
Ò	øô ô 	+Øœc &›kÐ*Ñ*÷ 
Ò	ð	+ú÷	 
×	Ð	ús(   ”A-°
AÁA*ÁA-Á)A*Á*A-Á-A>	z/model-optionsc                 óÐ  €  ^ RI Hp Hp V ! V! 4       RRRR7      pRVP                  R. 4       Uu. uF  pVP                  R4      '       g   K  RVP                  RR4      R	VP                  R	4      ;'       g    VP                  RR4      R\	        VP                  R4      ;'       g    . 4      /NK�  	  up/# u upi   \
         d    \        P                  R
4       R. /u # i ; i)a
  Providers + curated models for the override dropdown via ``inventory.build_models_payload``
(same substrate as the Models page) so it can't offer a pair Hermes rejects. Skips pricing
and custom-provider probes: a slow/offline local endpoint must not hang the drawer.)Úbuild_models_payloadÚload_picker_contextTF)Úexplicit_onlyÚcanonical_orderÚprobe_custom_providersÚ	providersÚmodelsr7   rB   rl   zkanban model-options failed)Úhermes_cli.inventoryri  rj  rM   r¿   r2   rY   Ú	exception)ri  rj  r�  r×   s       r,   Úmodel_optionsrr     sæ   € ð
!ßRá&ÙÓ!°ÀtÐdiôkˆð ð #Ÿ;™; {°BÔ7ó&ñ 8�CØ—7‘7˜8×$ô:�˜Ÿ™ ¨Ó,¨g°s·w±w¸wÓ7G×7^Ð7^È3Ï7É7ÐSYÐ[]ÓK^Øœ4 §¡¨Ó 1× 7Ð 7°RÓ8ó:á7ñ&ð'ð 	'ùò&øô
 ô !Ü�‰Ð3Ô4Ø˜RÐ Ò ð!ús4   ‚.B> °B9Á*B9Á9/B9Â)B9Â5B> Â9B> Â>$C%Ã$C%c                  ó„   € ] tR t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tR# )ÚCreateBoardBodyi  r8   r7   Nr9   r<  r$   ÚiconÚcolorÚdefault_workdirr…  Fr)   Úswitchr*   )r†  r‡  rˆ  r‰  rŠ  r<  r$   ru  rv  rw  r…  rx  r‹  r*   r.   r,   rt  rt    sM   ‡ Ø
ƒIØ€Dˆ-ÓØ!%€K�Ó%Ø€Dˆ-ÓØ€Eˆ=ÓØ%)€O�]Ó)à $€J�Ó$Ø€FˆD×r.   rt  c                  ól   € ] tR tRt$ 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# )ÚRenameBoardBodyi#  Nr9   r<  r$   ru  rv  rw  r…  r*   )r†  r‡  rˆ  r‰  r<  rŠ  r$   ru  rv  rw  r…  r‹  r*   r.   r,   rz  rz  #  s>   ‡ Ø€Dˆ-ÓØ!%€K�Ó%Ø€Dˆ-ÓØ€Eˆ=Óà%)€O�]Ó)Ø $€J�×$r.   rz  c                  óB   € ] tR tRt$ RtR]R&   RtR]R&   RtR]R	&   R
tR# )ÚExportBoardBodyi0  rB   r8   ÚoutputTr)   rf  FÚlogsr*   N)	r†  r‡  rˆ  r‰  r}  rŠ  rf  r~  r‹  r*   r.   r,   r|  r|  0  s    ‡ Ø€FˆCÓØ€K�ÓØ€Dˆ$×r.   r|  c                  ó>   € ] tR tRt$ R]R&   RtR]R&   RtR]R	&   R
tR# )ÚImportBoardBodyi6  r8   rÖ  Nr9   r7   Fr)   rx  r*   )r†  r‡  rˆ  r‰  rŠ  r7   rx  r‹  r*   r.   r,   r€  r€  6  s   ‡ ØƒLØ€Dˆ-ÓØ€FˆD×r.   r€  c               ó    € V ^8„  d   QhRRRR/# )r&   rL  r   r(   r©   r*   )r+   s   "r,   r-   r-   <  s   € ÷ \ñ \˜Yð \¨>ñ \r.   c                óf   € RV P                   RV P                  RV P                  RV P                  /# )zFDisplay-metadata fields shared by create_board / write_board_metadata.r<  r$   ru  rv  )r<  r$   ru  rv  r  s   &r,   Ú_board_display_kwargsrƒ  <  s,   € à�A—F‘F˜M¨1¯=©=¸&À!Ç&Á&È'ÐST×SZÑSZÐ[Ð[r.   c               ó    € V ^8„  d   QhRRRR/# )r&   Úrefr9   r(   z2tuple[Optional[str], Optional[str], Optional[str]]r*   )r+   s   "r,   r-   r-   A  s   € ÷ ;ñ ;˜-ð ;Ð,^ñ ;r.   c           	     óØ  € V '       d   V P                  4       '       g   R# \        R4      ;_uu_ 4        ^ RIHp VP	                  4       ;_uu_ 4       pVP                  W P                  4       4      pRRR4       RRR4       Xf   \        RRV : R2R7      hVP                  VP                  VP                  ;'       g    R3#   + '       g   i     L[; i  + '       g   i     Lf; i)	zƒResolve a project id/slug to ``(id, name, primary_path)``; ``(None,)*3``
for a falsy ref, 400 when a non-empty ref doesn't resolve.Nzprojects unavailable©Úprojects_dbr�   zproject rE   rF   )NNN)
r  r¢   r1   rˆ  Úconnect_closingÚget_projectr   rµ   r<  Úprimary_path)r…  ÚpdbÚpconnÚprojs   &   r,   Ú_resolve_projectr�  A  s§   € ÷ �c—i‘i—k’kØÐÜ	Ð.×	/Õ	/Ý1Ø× Ñ ×"Ô" eØ—?‘? 5¯)©)«+Ó6ˆD÷ #÷ 
0ð ‚|Ü¨°h¸s¹gÀ_Ð4UÔVÐVØ�7‰7�D—I‘I × 1Ñ 1× 9Ð 9°TÐ:Ð:÷	 #×"ú÷ 
0×	/ús#   ´CÁ C	Á3CÃCÃCÃC)	c               ó   € V ^8„  d   QhRR/# )r&   r(   r©   r*   )r+   s   "r,   r-   r-   O  s   € ÷ ñ ˜ñ r.   c                 ó  €  ^ RI Hp  V P                  4       ;_uu_ 4       pV P                  VRR7       Uu/ uF  q"P                  VbK  	  upuuRRR4       # u upi   + '       g   i     R# ; i  \
         d    / u # i ; i)zCMap every project id -> Project (archived included) for annotation.r‡  T©r#  N)r1   rˆ  r‰  Úlist_projectsrµ   r2   )rŒ  r�  rL  s      r,   Ú_projects_by_idr”  O  so   € ðÝ1Ø× Ñ ×"Ô" eØ%(×%6Ñ%6°uÈtÐ%6Ô%TÓUÑ%T —D‘D˜!’GÑ%TÑU÷ #Ò"ùÚU÷ #×"Ð"ûäô ØŠ	ðús?   ‚A1  A¶AÁAÁ
A1 ÁAÁA.	Á(A1 Á.A1 Á1BÂ Bc               ó    € V ^8„  d   QhRRRR/# )r&   r7   r8   r(   zdict[str, int]r*   )r+   s   "r,   r-   r-   Y  s   € ÷ 	ñ 	˜ð 	 ñ 	r.   c           
     ó¬  €  \         P                  ! V R7      P                  4       '       g   / # \        \        P
                  ! V R7      4      ;_uu_ 4       pVP                  R4      P                  4       pV Uu/ uF  q3R,          \        VR,          4      bK  	  upuuRRR4       # u upi   + '       g   i     R# ; i  \         d    / u # i ; i)z>``{status: count}`` for a board; ``{}`` on a missing/empty DB.rW   z7SELECT status, COUNT(*) AS n FROM tasks GROUP BY statusr   r  N)
r   Úkanban_db_pathrV  r   r[   r\   rÑ   râ   r‰   r2   )r7   rb   rç   rè   s   &   r,   Ú_board_countsr˜  Y  s¡   € ðÜ×'Ò'¨dÔ3×:Ñ:×<Ò<ØˆIÜ”S—[’[ tÔ,×-Ô-°Ø—<‘<Ð YÓZ×cÑcÓeˆDÙ6:Ó;±d°�h•K¤ Q s¥V£Ò,±dÑ;÷ .Ò-ùâ;÷ .×-Ð-ûô ô ØŠ	ðúsK   ‚*C ­C ¯(C Á$B/Á;"B*ÂB/Â
C Â*B/Â/C 	Â:C Ã C ÃCÃCc               ó    € V ^8„  d   QhRRRR/# )r&   r@   r©   r(   r8   r*   )r+   s   "r,   r-   r-   e  s   € ÷ ñ  >ð °cñ r.   c                óþ   € \        V P                  R4      ;'       g    R4      P                  4       pV'       g   R#  \        P                  ! \        V4      4      '       d   R# R#   \        \        3 d     R# i ; i)z?Recommend a non-destructive task workspace from board metadata.rw  rB   ÚscratchÚworktreeÚdir)r8   rM   r  ÚkbwÚ_git_toplevelr   r°  rÄ  )r@   Úworkdirs   & r,   Ú_default_workspace_kindr¡  e  sh   € ä�%—)‘)Ð-Ó.×4Ð4°"Ó5×;Ñ;Ó=€GßÙðÜ ×.Ò.¬t°G«}×=Ò=ˆzÐHÀ5ÐHøÜ”ZÐ ô Úðús   ¾&A' Á%A' Á'A<Á;A<c               ó    € V ^8„  d   QhRRRR/# )r&   Úmetar  r(   r*   )r+   s   "r,   r-   r-   p  s   € ÷ ñ ˜tð ¨ñ r.   c                ób   € \        V 4      V R &   \        V P                  R4      4      w  qR&   pV # )Údefault_workspace_kindr…  Úproject_name)r¡  r�  rM   )r£  Ú_s   & r,   Ú_annotate_board_metar¨  p  s4   € Ü%<¸TÓ%B€DÐ	!Ñ"Ü!1°$·(±(¸<Ó2HÓ!IÑ€AˆNÑ˜QØ€Kr.   z	/projectsc                 óø  € \        R4      ;_uu_ 4        ^ RIHp  V P                  4       ;_uu_ 4       pV P	                  VRR7      pRRR4       RRR4       RX Uu. uFk  pRVP
                  RVP                  R	VP                  R
VP                  ;'       g    RRVP                  ;'       g    RRVP                  ;'       g    R/NKm  	  up/#   + '       g   i     L’; i  + '       g   i     L�; iu upi )z9Live (non-archived) projects available for board scoping.zfailed to list projectsr‡  Fr’  NÚprojectsrµ   r7   r<  r‹  rB   ru  rv  )r¢   r1   rˆ  r‰  r“  rµ   r7   r<  r‹  ru  rv  )rŒ  r�  rª  rL  s       r,   Úlist_kanban_projectsr«  v  sÑ   € ô 
Ð1×	2Õ	2Ý1Ø× Ñ ×"Ô" eØ×(Ñ(¨ÀÐ(ÓGˆH÷ #÷ 
3ð ñ óñ ˆAð 
ˆq�t‰t�V˜QŸV™V V¨Q¯V©VØ	˜Ÿ™×-Ð-¨2¨v°q·v±v·|°|ÀÀWÈaÏgÉgÏmÈmÐY[ó	]áñð ð ÷ #×"ú÷ 
3×	2üòs:   ”C$³C	ÁC$Á<C7ÂC7Â/C7ÃC7ÃC!ÃC$Ã$C4	z/boardsc               ó   € V ^8„  d   QhRR/# )r&   r#  r)   r*   )r+   s   "r,   r-   r-   „  s   € ÷ 2ñ 2 $ñ 2r.   c                óê  € \         P                  ! V R7      p\         P                  ! 4       p\        4       pV F²  pVR,          V8H  VR&   \	        VR,          4      VR&   \        R VR,          P                  4        4       4      VR&   \        V4      VR&   VP                  R4      ;'       g    R	;qTR&   V'       d   VP                  V4      MR	pV'       d   VP                  MR	VR
&   K´  	  RVRV/# )z9Every board on disk with task counts and the active slug.r’  r7   Ú
is_currentÚcountsc              3  ó<   "  € T F  w  rVR 8w  g   K  Vx € K  	  R# 5i)rI   Nr*   )r`  r   r  s   &  r,   rb  Úlist_boards.<locals>.<genexpr>Ž  s   é € ÐXÑ,?™y˜vÀ6ÈZÑCWŸšÓ,?ùs   ‚’
r)  r¥  r…  Nr¦  Úboardsrä  )
r   Úlist_boardsrY  r”  r˜  r‚  r~  r¡  rM   r<  )r#  r²  rä  Úproj_mapÚbr'  rŽ  s   &      r,   r³  r³  ƒ  sÞ   € ô ×"Ò"Ð4DÔE€FÜ×)Ò)Ó+€GÜÓ €HÛˆØ˜V�9¨Ñ/ˆˆ,‰Ü# A f¥IÓ.ˆˆ(‰ô ÑX¨A¨h­K×,=Ñ,=Ô,?ÓXÓXˆˆ'‰
Ü&=¸aÓ&@ˆÐ
"Ñ#Ø !§¡ lÓ 3× ;Ð ;°tÐ;ˆ�‰oß$'ˆx�|‰|˜CÔ ¨Tˆß)-˜DŸIšI°4ˆˆ.Óñ ð �f˜i¨Ð1Ð1r.   c               ó    € V ^8„  d   QhRRRR/# )r&   r  r8   r(   r*   )r+   s   "r,   r-   r-   –  s   € ÷ $ñ $˜3ð $ 3ñ $r.   c                óò   € \        V 4      P                  4       pVP                  4       '       g   \        RRR7      hVP	                  4       '       g   \        RRR7      h\        VP                  4       4      # )zNBoard default_workdir must be an absolute, existing directory (400 otherwise).r�   z+Project directory must be an absolute path.rF   z0Project directory must be an existing directory.)r   Ú
expanduserÚis_absoluter   Úis_dirr8   r²  )r  Ú	requesteds   & r,   Ú_validate_workdirr¼  –  sb   € ä�S“	×$Ñ$Ó&€IØ× Ñ ×"Ò"Ü¨Ð4aÔbÐbØ×Ñ×ÒÜ¨Ð4fÔgÐgÜˆy× Ñ Ó"Ó#Ð#r.   c               ó   € V ^8„  d   QhRR/# )r&   r�  rt  r*   )r+   s   "r,   r-   r-   ¡  s   € ÷ [ñ [ ?ñ [r.   c           	     ó^  € V P                   '       d   \        V P                   4      MRp\        V P                  4      w  r#pV'       d   V'       g   Tp\	        4       ;_uu_ 4        \
        P                  ! V P                  3RVRV/\        V 4      B pRRR4       V P                  '       d9   \	        4       ;_uu_ 4        \
        P                  ! XR,          4       RRR4       R\        X4      R\
        P                  ! 4       /#   + '       g   i     L|; i  + '       g   i     LE; i)uK   Create a board. Idempotent â€” ``slug`` collision returns the existing one.Nrw  r…  r7   r@   rä  )rw  r¼  r�  r…  r<   r   Úcreate_boardr7   rƒ  rx  Úset_current_boardr¨  rY  )r�  rw  r…  Ú_pnamer‹  r£  s   &     r,   Úcreate_board_endpointrÂ     sæ   € ð EL×D[×D[ÐD[Ô'¨×(?Ñ(?Ô@Ðae€Oä'7¸×8JÑ8JÓ'KÑ$€J˜ßŸOØ&ˆÜ	×	Õ	Ü×%Ò%Ø�L‰LñtØ*9ðtØFPðtÜTiÐjqÓTrñtˆ÷ 
ð ‡~‡~€~Ü×ÕÜ×'Ò'¨¨V­Ô5÷  àÔ)¨$Ó/°¼I×<WÒ<WÓ<YÐZÐZ÷ 
×	ú÷  ×ús   Á&/D	ÃDÄ	D	ÄD,	z/boards/{slug}c               ó    € V ^8„  d   QhRRRR/# )r&   r7   r8   r�  rz  r*   )r+   s   "r,   r-   r-   ²  s   € ÷ 1ñ 1�sð 1 _ñ 1r.   c                óª  € \        V 4      pRpVP                  e0   VP                  P                  4       pV'       d   \        V4      MRpRpVP                  eJ   VP                  P                  4       '       d(   \        VP                  4      w  rVpV'       d   Vf   TpMRp\        P                  ! V3RVRV/\        V4      B pR\        V4      /# )zNUpdate display metadata / default workdir / project scope (slug is immutable).NrB   rw  r…  r@   )
rS   rw  r  r¼  r…  r�  r   Úwrite_board_metadatarƒ  r¨  )	r7   r�  rN   rw  r  r…  rÁ  r‹  r£  s	   &&       r,   Úrename_boardrÆ  ±  sÖ   € ô " $Ó'€Fà%)€OØ×ÑÒ*Ø×%Ñ%×+Ñ+Ó-ˆß47Ô+¨CÔ0¸Rˆà $€JØ×ÑÒ%Ø×Ñ×#Ñ#×%Ò%Ü/?À×@RÑ@RÓ/SÑ,ˆJ ß Ò 7Ø".�øàˆJÜ×)Ò)ØñjØ /ðjØ<FðjÜJ_Ð`gÓJhñj€DàÔ)¨$Ó/Ð0Ð0r.   zHard-delete instead of archivec               ó    € V ^8„  d   QhRRRR/# )r&   r7   r8   Údeleter)   r*   )r+   s   "r,   r-   r-   É  s   € ÷ Eñ E�sð E Dñ Er.   c                óÈ   € \        4       ;_uu_ 4        \        P                  ! W'       * R7      pRRR4       RXR\        P                  ! 4       /#   + '       g   i     L); i)z)Archive (default) or hard-delete a board.)rÖ  Nri  rä  )r<   r   Úremove_boardrY  )r7   rÈ  Úress   && r,   Údelete_boardrÌ  È  sE   € ô 
×	Õ	Ü×$Ò$ T´:Ô>ˆ÷ 
à�c˜9¤i×&AÒ&AÓ&CÐDÐD÷ 
×	ús   “AÁA!	c               ó   € V ^8„  d   QhRR/# )r&   Ú	log_labelr8   r*   )r+   s   "r,   r-   r-   Ð  s   € ÷ >ñ > sñ >r.   c              ƒ  óx  "  €  \         P                  ! 4       P                  RV 4      G Rj  x€L
 #  L  \         d   p\	        R\        T4      R7      hRp?i\         d   p\	        R\        T4      R7      hRp?i\         d2   p\        P                  RT4       \	        R\        T4      R7      hRp?ii ; i5i)z€Run a blocking kanban_transfer call off the event loop, mapping its errors
to 404 (missing path) / 400 (invalid) / 500 (logged).NrC   rF   r�   z	%s failedr¡   )
ÚasyncioÚget_running_loopÚrun_in_executorÚFileNotFoundErrorr   r8   rÄ  r2   rY   rq  )re   rÎ  r]   s   && r,   Ú_run_transferrÔ  Ð  sŽ   é € ð>Ü×-Ò-Ó/×?Ñ?ÀÀbÓI×IÐIÑIøÜô >Ü¨´C¸³HÔ=Ð=ûÜô >Ü¨´C¸³HÔ=Ð=ûÜô >Ü�‰�k 9Ô-Ü¨´C¸³HÔ=Ð=ûð>üsT   ‚B:„(3 ¬1­3 °B:±3 ³B7¾AÁB7Á!B7Á"A8Á8B7ÂB7Â,B2Â2B7Â7B:z/boards/{slug}/exportc               ó    € V ^8„  d   QhRRRR/# )r&   r7   r8   rv  r|  r*   )r+   s   "r,   r-   r-   ß  s   € ÷ 'ñ ' cð '°ñ 'r.   c              ƒ  óÄ  a aaa"  € ^ RI Ho SP                  ;'       g    RP                  4       oS'       gd   \        P
                  ! 4       R,          R,          p VP                  RRR7       \        TS  R\        P                  ! R4       R2,          4      o\        VVVV 3R lRS  R24      G R
j  x€L
 #   \         d   p\        RRT 2R	7      hR
p?ii ; i L'5i)z>Write ``slug`` to a portable archive; return the path written.©Úkanban_transferrB   r
  zboard-exportsTr¤  r¡   z#Could not create export directory: rF   NÚ-z%Y%m%d-%H%M%Sz.tar.gzc                 óV   <€ SP                  SSS P                  S P                  R 7      # ))Úinclude_attachmentsÚinclude_logs)Úexport_boardrf  r~  )rv  rØ  r}  r7   s   €€€€r,   r5  Ú'export_board_endpoint.<locals>.<lambda>ì  s&   ø€ �×,Ñ,¨T°6Èt×O_ÑO_Ðnr×nwÑnwÐ,Ôxr.   zPOST /boards/z/export)r1   rØ  r}  r  r   Úkanban_homerª  r°  r   r8   rJ  ÚstrftimerÔ  )r7   rv  Ústagingr]   rØ  r}  s   ff  @@r,   Úexport_board_endpointrâ  Þ  sÏ   ûé € õ +à�k‰k×Ð˜R×&Ñ&Ó(€FßÜ×'Ò'Ó)¨HÕ4°ÕFˆð	eØ�M‰M $°ˆMÔ6ô �W $  q¬¯ª°Ó)GÐ(HÈÐPÕPÓQˆÜßxØ
˜�v˜WÐ%ó'÷ 'ð 'øô ô 	eÜ¨CÐ:]Ð^aÐ]bÐ8cÔdÐdûð	eúñ'ùs;   †2C ¹#C ÁB; Á0AC Â6CÂ7C Â;CÃCÃCÃC z/boards/importc               ó   € V ^8„  d   QhRR/# )r&   rv  r€  r*   )r+   s   "r,   r-   r-   ñ  s   € ÷ 
@ñ 
@ oñ 
@r.   c              ƒ  óü   a aa"  € ^ RI Ho S P                  ;'       g    RP                  4       oS'       g   \	        RRR7      h\        VV V3R lR4      G Rj  x€L
 p/ VCR	\        P                  ! 4       /C#  L5i)
z?Import a board archive as a NEW board; return the landed board.r×  rB   r�   zarchive path is requiredrF   c                 ó˜   <€ SP                  S SP                  ;'       g    R P                  4       ;'       g    RSP                  R7      # )rB   N)Úactivate)Úimport_boardr7   r  rx  )rÖ  rv  rØ  s   €€€r,   r5  Ú'import_board_endpoint.<locals>.<lambda>ù  s:   ø€ �×,Ñ,¨W°t·y±y·°ÀB×6MÑ6MÓ6O×6WÐ6WÐSWÐbf×bmÑbmÐ,Ônr.   zPOST /boards/importNrä  )r1   rØ  rÖ  r  r   rÔ  r   rY  )rv  ri  rÖ  rØ  s   f @@r,   Úimport_board_endpointré  ð  sn   úé € õ +à�|‰|×!Ð!˜r×(Ñ(Ó*€GßÜ¨Ð4NÔOÐOÜ ÞnØó÷ €Fð @ˆfÐ?�i¤×!<Ò!<Ó!>Ñ?Ð?ñùs   …2A<¸"A<ÁA:Á A<z/boards/{slug}/switchc               ó   € V ^8„  d   QhRR/# )r&   r7   r8   r*   )r+   s   "r,   r-   r-   ÿ  s   € ÷ ñ �sñ r.   c                óL   € \        V 4      p\        P                  ! V4       RV/# )zPersist ``slug`` as the active board for CLI / slash-command parity
(dashboard users pick boards client-side via localStorage).rä  )rS   r   rÀ  rR   s   & r,   Úswitch_boardrì  þ  s'   € ô " $Ó'€FÜ×Ò Ô'Ø�vÐÐr.   c                  ó&   € ] tR tRt$ RtR]R&   RtR# )ÚDescribeBodyi	  Nr9   r$   r*   )r†  r‡  rˆ  r‰  r$   rŠ  r‹  r*   r.   r,   rî  rî  	  s   ‡ Ø!%€K�×%r.   rî  c                  ó&   € ] tR tRt$ RtR]R&   RtR# )ÚDescribeAutoBodyi  Fr)   Ú	overwriter*   N)r†  r‡  rˆ  r‰  rñ  rŠ  r‹  r*   r.   r,   rð  rð    s   ‡ Ø€Iˆt×r.   rð  z	/profilesc                 óì  € \        R4      ;_uu_ 4        ^ RIHp  V P                  4       pRRR4       RX Uu. uFœ  pRVP                  R\        VP                  4      RVP                  ;'       g    RR	VP                  ;'       g    RR
VP                  ;'       g    RR\        VP                  4      R\        VP                  ;'       g    ^ 4      /NKž  	  up/#   + '       g   i     L»; iu upi )zzEvery installed profile with its description (profiles without one are
still routable on name alone, just less precisely).zfailed to list profiles©ÚprofilesNrô  r<  Ú
is_defaultré  rB   r  r$   Údescription_autoÚskill_count)r¢   r1   rô  Úlist_profilesr<  r)   rõ  ré  r  r$   rö  r‰   r÷  )Úprofiles_modrô  rL  s      r,   Úlist_profile_rosterrú    sÕ   € ô 
Ð1×	2Õ	2Ý7Ø×-Ñ-Ó/ˆ÷ 
3ð ñ ó	ñ ˆAð 
�—‘˜¤t¨A¯L©LÓ'9¸7ÀAÇGÁGÇMÀMÈrÐS]Ð_`×_iÑ_i×_oÐ_oÐmoØ	˜Ÿ™×+Ð+¨Ð-?ÄÀa×FXÑFXÓAYØ	œ˜AŸM™M×.Ð.¨QÓ/ó	1ñ ñ	ð ð ÷ 
3×	2üòs(   ”C¸9C1Á2C1ÂC1Â/C1ÃC1ÃC.	z/profiles/{profile_name}c               ó    € V ^8„  d   QhRRRR/# )r&   Úprofile_namer8   r�  rî  r*   )r+   s   "r,   r-   r-      s   € ÷ ?ñ ?¨Sð ?¸<ñ ?r.   c                ó¸  € \        R4      ;_uu_ 4        ^ RIHp VP                  V 4      pVR8X  d   ^ RIHp \        V! 4       4      pMVP                  V4      pVP                  4       '       g   \        RRV  R2R7      hVP                  ;'       g    R	P                  4       pVP                  WVR
R7       RRR4       RRRXRX/#   + '       g   i     L; i)z“Set (``description_auto: false`` so the auto-describer won't overwrite it
without ``--overwrite``) or clear (empty string) a profile's description.zfailed to update profileró  r%  r”  rC   ú	profile 'z' not foundrF   rB   F)r$   rö  Nr�   TrÈ  r$   )r¢   r1   rô  Únormalize_profile_namerš  r•  r   Úget_profile_dirrº  r   r$   r  Úwrite_profile_meta)rü  r�  rù  Úcanonr•  Úprofile_dirÚtexts   &&     r,   Úupdate_profile_descriptionr    sÄ   € ô 
Ð2×	3Õ	3Ý7Ø×3Ñ3°LÓAˆØ�IÔÝ8Ü™Ó0Ó1‰Kà&×6Ñ6°uÓ=ˆKØ×!Ñ!×#Ò#Ü¨C¸)ÀLÀ>ÐQ\Ð8]Ô^Ð^Ø×#Ñ#×)Ð) r×0Ñ0Ó2ˆØ×'Ñ'¨ÐX]Ð'Ô^÷ 
4ð �$˜	 5¨-¸Ð>Ð>÷ 
4×	3ús   ”AC	Á1#C	Â$C	Ã	C	z&/profiles/{profile_name}/describe-autoc               ó    € V ^8„  d   QhRRRR/# )r&   rü  r8   r�  rð  r*   )r+   s   "r,   r-   r-   3  s   € ÷ Cñ C¨ð CÐ6Fñ Cr.   c                ó.  € \        R4      ;_uu_ 4        ^ RIHp VP                  V \	        VP
                  4      R7      pRRR4       R\	        XP                  4      RVP                  RVP                  RVP                  /#   + '       g   i     LK; i)	u�   ``hermes profile describe <name> --auto``: persist with ``description_auto: true``.
Non-OK outcomes are NOT HTTP errors â€” the UI renders the reason inline.zdescriber crashed)Úprofile_describer)rñ  Nr�   rÈ  rí  r$   )
r¢   r1   r  Údescribe_profiler)   rñ  r�   rü  rí  r$   )rü  r�  r  rm  s   &&  r,   Úauto_describe_profiler
  2  s„   € ô 
Ð+×	,Õ	,Ý0Ø#×4Ñ4°\ÌTÐRY×RcÑRcÓMdÐ4Óeˆ÷ 
-ð ”$�w—z‘zÓ" I¨w×/CÑ/CÀXÈwÏ~É~Ð_lÐnu÷  oBñ  oBð  Cð  C÷ 
-×	,ús   ”-BÂB	c                  ó&   € ] tR tRt$ RtR]R&   RtR# )ÚDecomposeBodyi>  Nr9   rw   r*   )r†  r‡  rˆ  r‰  rw   rŠ  r‹  r*   r.   r,   r  r  >  r³  r.   r  z/tasks/{task_id}/decomposec               ó$   € V ^8„  d   QhRRRRRR/# )r&   rv   r8   r�  r  r@   r9   r*   )r+   s   "r,   r-   r-   C  s&   € ÷ nñ n Sð n°=ð nÈñ nr.   c                óþ   € \        VRRWP                  4      pR\        VP                  4      RVP                  RVP
                  R\        VP                  4      RVP                  ;'       g    . RVP                  /# )	u¡   Fan a triage task out into child tasks via the auxiliary LLM (``hermes kanban decompose``).
Non-OK is NOT an HTTP error. Sync ``def`` â†’ runs in the threadpool.Úkanban_decomposeÚdecompose_taskr�   rv   rí  ÚfanoutÚ	child_idsrÃ  )	r€   rw   r)   r�   rv   rí  r  r  rÃ  rÄ  s   &&& r,   Údecompose_task_endpointr  B  so   € ô �uÐ0Ð2BÀGÏ^É^Ó\€GàŒd�7—:‘:Ó 	¨7¯?©?¸HÀgÇnÁnØ”$�w—~‘~Ó&¨°W×5FÑ5F×5LÐ5LÈ"ÈkÐ[b×[lÑ[lðnð nr.   c                  óP   € ] tR tRt$ RtR]R&   RtR]R&   RtR]R&   RtR]R&   R	t	R# )
ÚOrchestrationSettingsBodyiO  Nr9   Úorchestrator_profileÚdefault_assigneezOptional[bool]Úauto_decomposeÚauto_promote_childrenr*   )
r†  r‡  rˆ  r‰  r  rŠ  r  r  r  r‹  r*   r.   r,   r  r  O  s-   ‡ Ø*.Ð˜-Ó.Ø&*Ð�mÓ*Ø%)€N�NÓ)Ø,0Ð˜>×0r.   r  z	/workflowc                 ó>   € \         P                  P                  4       # )zÑBoard columns (order, label, icon, drag target) and the manual move
allow-list. Every board uses the default workflow today; per-board
workflows (``board.json``) arrive in a later phase behind this same shape.)r   ÚDEFAULT_WORKFLOWrå   r*   r.   r,   Úget_workflowr  Y  s   € ô
 ×+Ñ+×3Ñ3Ó5Ð5r.   z/orchestrationc                 ó  € \        4       p \        V \        4      '       d   V P                  R4      ;'       g    / M/ p\         Uu/ uF,  q"VP                  V4      ;'       g    RP                  4       bK.  	  pp\        V4      p ^ RIHp VP                  4       ;'       g    RpVP                  4        F*  w  r'V'       d   VP                  V4      '       d   K&  WdV&   K,  	  RVR,          RVR,          R\        VP                  RR4      4      R	\        VP                  R	R4      4      R
VR,          RVR,          RV/# u upi   \         d:    RpTP                  4        UUu/ uF  w  r'Y';'       g    TbK  	  Mu uppi ppp L¦i ; i)a  Current orchestration knobs from config.yaml plus the resolved effective
values. An unset/unknown profile resolves to the active profile here; the
decomposer prefers the root card's assignee in that case and uses the active
profile only for cards with no assignee.r
  rB   ró  r%  r  r  r  Tr  Úresolved_orchestrator_profileÚresolved_default_assigneeÚactive_profile)r  rý  r  rM   Ú_PROFILE_SETTINGSr  r1   rô  r$  r~  Úprofile_existsr2   r)   )ÚcfgÚ
kanban_cfgÚkÚexplicitÚresolvedrù  Úactive_defaultÚvs           r,   Úget_orchestration_settingsr*  a  so  € ô  Ó
!€CÜ.8¸¼d×.CÒ.C�#—'‘'˜(Ó#×)Ð) røÈ€JÝ>OÓPÑ>O¸�J—N‘N 1Ó%×+Ð+¨×2Ñ2Ó4Ò4Ñ>O€HÐPÜ�H‹~€HðIÝ7Ø%×=Ñ=Ó?×LÐLÀ9ˆØ—N‘NÖ$‰DˆAß˜L×7Ñ7¸×:Ô:Ø,˜“ñ %ð 	 Ð)?Õ @Ø˜HÐ%7Õ8Øœ$˜zŸ~™~Ð.>ÀÓEÓFØ¤ j§n¡nÐ5LÈdÓ&SÓ!TØ'¨Ð2HÕ)IØ# XÐ.@Õ%AØ˜.ð*ð *ùò Qøô ô IØ"ˆØ7?·~±~Ô7GÔHÑ7G©t¨q�A×*Ð*˜NÒ*Ò7GùÓHˆÑH‰ðIús<   ÁEÁ#EÂE Â#4E ÃE Å FÅ'E?Å7E?Å>
FÆ
Fc               ó    € V ^8„  d   QhRRRR/# )r&   r  r9   r(   r8   r*   )r+   s   "r,   r-   r-   ~  s   € ÷ 
ñ 
 ð 
Àñ 
r.   c                óÎ   € T ;'       g    RP                  4       pV'       d0   Ve,    VP                  V4      pV'       g   \        RRV R2R7      hV#   \         d    Rp L*i ; i)z]Strip a profile name; 400 if non-empty and unknown. Fails open when the lookup itself errors.rB   Tr�   rþ  z' does not existrF   )r  r"  r2   r   )r  rù  r<  rV  s   &&  r,   Ú_validated_profile_namer-  ~  sj   € à�IˆI�2×ÑÓ€Dß�Ò(ð	Ø!×0Ñ0°Ó6ˆF÷ Ü¨C¸)ÀDÀ6ÐIYÐ8ZÔ[Ð[Ø€Køô	 ô 	ØŠFð	ús   ¨A ÁA$Á#A$c               ó   € V ^8„  d   QhRR/# )r&   r�  r  r*   )r+   s   "r,   r-   r-   Œ  s   € ÷ (ñ (Ð(Añ (r.   c                óN  € \        R4      ;_uu_ 4        ^ RIHpHp V! 4       ;'       g    / pRRR4       XP	                  R/ 4      p\        V\        4      '       g   / ;qCR&    ^ RIHp V P                  RR7      P                  4        F(  w  rgV\        9   d   \        Wu4      M
\        V4      WF&   K*  	  \        R4      ;_uu_ 4        X! V4       RRR4       \        4       #   + '       g   i     L¿; i  \         d    Rp Lœi ; i  + '       g   i     \        4       # ; i)	zUpdate orchestration knobs in config.yaml. Only fields explicitly passed
are written; empty profile strings clear the override.zfailed to load config)rË   Úsave_configNr
  ró  T)Úexclude_nonezfailed to save config)r¢   rß   rË   r0  rÓ   rý  r  r1   rô  r2   r˜  r~  r!  r-  r)   r*  )r�  rË   r0  r#  Úkanban_sectionrù  r7  r  s   &       r,   Úset_orchestration_settingsr3  ‹  sñ   € ô 
Ð/×	0Õ	0ß>Ù‹m×!Ð!˜rˆ÷ 
1ð —^‘^ H¨bÓ1€NÜ�n¤d×+Ò+Ø)+Ð+ˆ˜X™ðÝ7ð ×(Ñ(°dÐ(Ó;×AÑAÖC‰
ˆØNQÔUfÔNfÔ5°eÔJÔlpÐqvÓlwˆÓñ Dä	Ð/×	0Õ	0Ù�CÔ÷ 
1ä%Ó'Ð'÷ 
1×	0ûô ô ØŠðú÷
 
1Ö	0ä%Ó'Ð'ús)   ”C%Á%C8 Ã
	DÃ%C5	Ã8DÄDÄD$	g333333Ó?c               ó    € V ^8„  d   QhRRRR/# )r&   r'   r   r(   r¨   r*   )r+   s   "r,   r-   r-   §  s   € ÷ ñ �Yð  =ñ r.   c                óÞ   € V P                   P                  R4      pVe    \        V4      P                  4       '       g   R#  \	        ^ \        V4      4      #   \        \        3 d     R# i ; i)uZ  The client's event cursor, or None when it sent none (or garbage).

None starts the stream at the board's current tail. Only an explicit
``since`` replays history â€” including ``since=0``. A client that just
opened the board already holds the snapshot; replaying every
``task_events`` row (200 per 300 ms) turned each open into a refetch storm.
ÚsinceN)Úquery_paramsrM   r8   r  rü   r‰   rþ  rÄ  )r'   r  s   & r,   Ú_since_paramr8  §  s]   € ð �/‰/×
Ñ
˜gÓ
&€CØ
‚{œ#˜c›(Ÿ.™.×*Ò*ÙðÜ�1”c˜#“hÓÐøÜ”zÐ"ô Úðús   ÁA ÁA,Á+A,c               ó    € V ^8„  d   QhRRRR/# )r&   r  r9   r(   r*   )r+   s   "r,   r-   r-   ¸  s   € ÷ ñ �=ð  ]ñ r.   c                óh   €  V '       d   \         P                  ! V 4      # R #   \         d     R # i ; ir;   )r   r=   rÄ  )r  s   &r,   Ú	_ws_boardr;  ¸  s1   € ðß7:Œy×.Ò.¨sÓ3ÐDÀÐDøÜô Úðús   ‚" Š"  " ¢1°1c               ó    € V ^8„  d   QhRRRR/# )r&   rN   r9   r(   r*   )r+   s   "r,   r-   r-   ¿  s   € ÷ 
>ñ 
>˜=ð 
>¨]ñ 
>r.   c                ó€   € V '       d   V \         P                  8X  d   V # \         P                  ! V 4      '       d   V # R# )aT  Require an already-normalised slug to name a *live* board.

A stale dashboard tab can keep its old board slug around after the board
was archived or deleted; the event stream must reject it instead of handing
it to ``connect(board=slug)``, which would resurrect an empty board (#43243).
Returns ``None`` when the board is unknown/archived.
N)r   rJ   rK   )rN   s   &r,   Ú_ws_board_liver>  ¿  s4   € ÷ �Vœy×6Ñ6Ô6ØˆÜ×+Ò+¨F×3Ò3ˆ6Ð=¸Ð=r.   c                  ót   € ] tR tRtRtR R ltR R ltR R ltR	 R
 ltR t	R R lt
R R ltR R ltRtR# )Ú
_EventTailiÌ  zÜPer-socket ``task_events`` tailer. One SQLite connection, used/closed only on a
dedicated single-thread executor (connections are thread-affine); reusing it avoids
churning WAL/SHM sidecars while an idle dashboard polls.c               ó    € V ^8„  d   QhRRRR/# )r&   r@   r9   r(   r�   r*   )r+   s   "r,   r-   Ú_EventTail.__annotate__Ñ  s   € ÷ <ñ <˜mð <°ñ <r.   c                	ó.   € Wn         R V n        R V n        R # r;   ©Ú_boardr^   Ú	_executor)Úselfr@   s   &&r,   Ú__init__Ú_EventTail.__init__Ñ  s   € ØŒØ37ˆŒ
Ø7;ˆŽr.   c               ó   € V ^8„  d   QhRR/# ©r&   r(   r‰   r*   )r+   s   "r,   r-   rB  Ö  s   € ÷ 0ñ 0˜ñ 0r.   c                ó  € V P                   f'   \        P                  ! V P                  R7      V n         V P                   P	                  RR4      P                  4       pV'       d   \        V^ ,          R,          4      # ^ # )zFThe board's current tail: the cursor a client without one starts from.rW   r,  r-  r*   )r^   r[   r\   rE  rÑ   râ   r‰   )rG  rç   s   & r,   Ú_latestÚ_EventTail._latestÖ  s_   € à�:‰:ÒÜŸš¨4¯;©;Ô7ˆDŒJØ�z‰z×!Ñ!Ø?Àó
ç
‰(‹*ð 	÷ %)Œs�4˜•7˜3•<Ó Ð/¨aÐ/r.   c               ó    € V ^8„  d   QhRRRR/# ©r&   Úcursorr‰   r(   ztuple[int, list[dict]]r*   )r+   s   "r,   r-   rB  ß  s   € ÷ 9ñ 9˜Sð 9Ð%;ñ 9r.   c                	óÖ  € V P                   f'   \        P                  ! V P                  R7      V n         V P                   P	                  RV34      P                  4       p. pV FR  p VR,          '       d   \        P                  ! VR,          4      MR pVP                  / \        V4      CRV/C4       KT  	  V'       d   VR,          R,          V3# TV3#   \         d    R p LQi ; i)NrW   zmSELECT id, task_id, run_id, kind, payload, created_at FROM task_events WHERE id > ? ORDER BY id ASC LIMIT 200r�  rµ   rû   )r^   r[   r\   rE  rÑ   râ   rK  rû  r2   rÔ   r  )rG  rQ  rç   rì   rè   r�  s   &&    r,   Ú_fetchÚ_EventTail._fetchß  sÓ   € Ø�:‰:ÒÜŸš¨4¯;©;Ô7ˆDŒJØ�z‰z×!Ñ!ðFàˆIó÷  ‘x“zð 	ð ˆÛˆAðØ67¸	·l´lœ$Ÿ*š* Q y¥\Ô2È�ð �J‰JÐ6œ$˜q›'Ð6 9¨gÑ6Ö7ñ ÷ #'��R•˜•°SÐ8Ð8¨F°SÐ8Ð8øô ô Ø’ðús   Á(.CÃC(Ã'C(c               ó   € V ^8„  d   QhRR/# ©r&   r(   r�   r*   )r+   s   "r,   r-   rB  ï  s   € ÷ ñ ˜ñ r.   c                	óh   € V P                   e$   V P                   P                  4        R V n         R # R # r;   )r^   r­  ©rG  s   &r,   Ú_closeÚ_EventTail._closeï  s(   € Ø�:‰:Ò!Ø�J‰J×ÑÔØˆDŽJñ "r.   c                	ó¦   € V P                   f   \        ^RR7      V n         \        P                  ! 4       P                  ! V P                   V.VO5!  # )Nzkanban-events)Úmax_workersÚthread_name_prefix)rF  r   rÐ  rÑ  rÒ  )rG  re   Úargss   &&*r,   r~   Ú_EventTail._runô  s@   € Ø�>‰>Ò!Ü/¸AÐRaÔbˆDŒNÜ×'Ò'Ó)×9Ò9¸$¿.¹.È"ÐTÈtÓTÐTr.   c               ó   € V ^8„  d   QhRR/# rK  r*   )r+   s   "r,   r-   rB  ù  s   € ÷ -ñ -˜cñ -r.   c              ƒ  	óT   "  € V P                  V P                  4      G R j  x€L
 #  L5ir;   )r~   rM  rX  s   &r,   rÿ   Ú_EventTail.latestù  s   é € Ø—Y‘Y˜tŸ|™|Ó,×,Ð,Ñ,ùs   ‚(¡&¢(c               ó    € V ^8„  d   QhRRRR/# rP  r*   )r+   s   "r,   r-   rB  ü  s   € ÷ 4ñ 4 ð 4Ð)?ñ 4r.   c              ƒ  	óV   "  € V P                  V P                  V4      G R j  x€L
 #  L5ir;   )r~   rS  )rG  rQ  s   &&r,   ÚpollÚ_EventTail.pollü  s    é € Ø—Y‘Y˜tŸ{™{¨FÓ3×3Ð3Ñ3ùs   ‚ )¢'£)c               ó   € V ^8„  d   QhRR/# rV  r*   )r+   s   "r,   r-   rB  ÿ  s   € ÷ Dñ D ñ Dr.   c              ƒ  	ó�  "  € V P                   f   R #  \        P                  ! 4       P                  V P                   V P                  4      G R j  x€L
  V P                   P                  RRR7       R #  L#  \
         d!   p\        P                  RT4        R p?LGR p?ii ; i  T P                   P                  RRR7       i ; i5i)Nz1Kanban event stream connection cleanup failed: %sT)ÚwaitÚcancel_futures)	rF  rÐ  rÑ  rÒ  rY  r2   rY   rZ   Úshutdown)rG  r]   s   & r,   rk  Ú_EventTail.shutdownÿ  s›   é € Ø�>‰>Ò!Ùð	DÜ×*Ò*Ó,×<Ñ<¸T¿^¹^ÈTÏ[É[ÓY×YÐYð �N‰N×#Ñ#¨¸dÐ#ÖCñ	 ZøÜô 	RÜ�K‰KÐKÈS×QÑQûð	Rûð �N‰N×#Ñ#¨¸dÐ#ÕCüsL   ‚C”<A6 ÁA4ÁA6 ÁCÁ4A6 Á6B!ÂBÂB$ ÂB!Â!B$ Â$CÃCrD  N)r†  r‡  rˆ  r‰  rÚ  rH  rM  rS  rY  r~   rÿ   re  rk  r‹  r*   r.   r,   r@  r@  Ì  s8   † ñ@õ<õ
0õ9õ ò
Uõ
-õ4÷Dñ Dr.   r@  z/eventsc               ó   € V ^8„  d   QhRR/# )r&   r'   r   r*   )r+   s   "r,   r-   r-     s   € ÷ ,ñ ,˜Iñ ,r.   c              ƒ  ó>  "  € \        V 4      '       g+   V P                  \        P                  R 7      G Rj  x€L
  R# V P	                  4       G Rj  x€L
  \        V P                  P                  R4      4      p\        V4      pVe/   Vf+   V P                  \        P                  R 7      G Rj  x€L
  R# \        V4      p\        V 4      p Ve   TMVP                  4       G Rj  x€L
 p  \        P                  ! V P                  4       \        R7      G Rj  x€L
 pVR,          R8X  d    VP!                  4       G Rj  x€L
  R#  VP%                  V4      G Rj  x€L
 w  rWV'       g   Kƒ  V P'                  RVRV/4      G Rj  x€L
  K¢   ELW EL@ Lã L® L| LW  \"         d     L_i ; i LN L'  \(         d     TP!                  4       G Rj  x€L 
  R# \        P*                   d     TP!                  4       G Rj  x€L 
  R# \,         dP   p\.        P1                  RT4        T P                  4       G Rj  x€L 
   Rp?M  \,         d      Rp?Mi ; iRp?ii ; iTP!                  4       G Rj  x€L 
  R#   TP!                  4       G Rj  x€L 
  i ; i5i)	)ÚcodeNr@   )rè  rô  zwebsocket.disconnectre  rQ  zKanban event stream error: %s)r5   r­  Úhttp_statusÚWS_1008_POLICY_VIOLATIONÚacceptr;  r7  rM   r>  r@  r8  rÿ   rÐ  Úwait_forÚreceiveÚ_EVENT_POLL_SECONDSrk  ÚTimeoutErrorre  Ú	send_jsonr   ÚCancelledErrorr2   rY   rZ   )	r'   Ú	raw_boardr@   rS  r6  rQ  Úmsgre  r]   s	   &        r,   Ústream_eventsr{  
  s  é € ä! "×%Ò%Ø�h‰hœK×@Ñ@ˆhÓA×AÐAÙØ
�)‰)‹+×Ðô ˜"Ÿ/™/×-Ñ-¨gÓ6Ó7€IÜ˜9Ó%€EØÒ ¢ð �h‰hœK×@Ñ@ˆhÓA×AÐAÙÜ�eÓ€DÜ˜Ó€Eðð  Ò+‘°t·{±{³}×1DˆØðÜ#×,Ò,¨R¯Z©Z«\ÔCVÔW×W�Ø�v•;Ð"8Ô8Øð" �m‰m‹o×Òð% 9ð $(§9¡9¨VÓ#4×4‰NˆFß‰vØ—l‘l H¨f°hÀÐ#GÓH×HÒHò= 	Bâñ 	Bñ 2Eñ
 Xñ& 	øô!  ô Ùðúá4áHøÜô Øð �m‰m‹o×Óô ×!Ñ!ô Øð �m‰m‹o×Óô ô Ü�‰Ð3°SÔ9ð	Ø—(‘(“*×ÖøÜô 	Ýð	þð	úð �m‰m‹o×Óøˆd�m‰m‹o×Òüs„  ‚5J·F¸JÁFÁAJÂ0FÂ1JÃF1 Ã'FÃ(F1 Ã.-F ÄFÄF Ä/JÅFÅJÅ	F1 ÅF-ÅF1 Å/F1 ÆF/ÆF1 ÆJÆJÆJÆF1 ÆF ÆJÆF*Æ'F1 Æ)F*Æ*F1 Æ/F1 Æ1I!Æ<I? Æ=JÇGÇJÇI!Ç+I!Ç,I? Ç-JÈ HÈJÈI!ÈI!ÈIÈ)IÈ<H?È=IÉI? ÉIÉIÉI? ÉIÉIÉI!É!I? É$JÉ7I:É8JÉ?JÊJÊJÊJr;   )r  r  )ÕÚ__conditional_annotations__rÚ  Ú
__future__r   rÐ  r|   rK  Úloggingrø  Úsqlite3rJ  Úconcurrent.futuresr   Ú
contextlibr   r   Údataclassesr   Ú	functoolsr   Úpathlibr   Útypingr	   r
   r   r   Úfastapir   r   r   r   r   r   r   r   r   rp  Úfastapi.responsesr   Úpydanticr   r   r1   r   r   Úhermes_cli.web_read_coalescingr   r   r[   r   r5  r   re  r   rž  r    rà   Úhermes_cli.kanban_dbr!   r"   r#   Ú	getLoggerr†  rY   ÚrouterÚ_BOARD_Qr5   r>   rO   rS   r^   rc   rh   rs   r€   r†   r�   r’   r–   rœ   rÄ  r<   r¢   r¿   r  r€  r¤   rŠ  rG  r±   r¼   rÅ   rî   r  r
  r  r  rT  rX  rM   rZ  r…   ru  Úpostr—  r   rº  rÈ  rÈ  rÌ  rÎ  rÕ  r2   rÙ  rü  rå  rø  rþ  r  r
  r  r0  r  r  r,  Úpatchr5  r9  r  rã  rX  r[  r^  rb  rf  rn  rs  r†  Úpsutilr¤  ÚImportErrorr‹  r�  r¯  r±  r·  r¹  r¼  r¾  rÅ  rÇ  rË  ró  rÍ  rÒ  rÕ  rØ  rÑ  r  r  r   r'  r/  r;  rC  rH  rL  rO  r^  rg  rr  rt  rz  r|  r€  rƒ  r�  r”  r˜  r¡  r¨  r«  r³  r¼  rÂ  rÆ  rÌ  rÔ  râ  ré  rì  rî  rð  rú  r  r
  r  r  r  r!  r  r*  r-  Úputr3  ru  r8  r;  r>  r@  Ú	websocketr{  )r|  s   @r,   Ú<module>r”     sG  øðôõ #ã Û Û Û Û 	Û Û Ý 1ß .Ý Ý Ý ß 4Ó 4÷t÷ tõ tå *ß %å  Ý &Ý 9Ý /Ý .Ý 0Ý 1Ý /ß iÑ ià×Ò˜Ó!€á	‹€á�Ð#IÔJ€õ
%õ5õ
õ"÷$ð $ð ôó ðõõõ+õ?õ<õFõ9ð ô?ó ð?ñ ˜;¨¨ZÓ8Ð ð ôHó ðHñ   × @Ñ @× EÑ EÓ GÓH€ˆyÓ Hà!Ð ðÈð Ø7;÷õ põ&÷!ð !õHbõ$Gõaõ	ñ " $Ð4OÔPÙ" 5›\Ø#Ù*/°ÐBuÔ*vÙ&+¨DÐ>kÔ&l÷?fñD ˜YÓ'€ð ‡�ˆHÓá! $Ð4OÔPÙ" 5›\Ø#Ù*/°ÐBuÔ*vÙ&+¨DÐ>kÔ&löó ðð& ‡�ÐÓñ ! ›;Ù$)¨$Ð<~Ô$Ù$)¨$Ð<rÔ$sö	Có  ðCôD%�Yô %ð* ‡�ˆXÓÙ@EÀdÃö ó ðð8 ‡�Ð*Ó+Ù?DÀT»{ö ió ,ðið ‡�Ð+Ó,ñ ˜C“yÙ  ›;Ù!% d£ö	Fó -ðFðB ‡�Ð*Ó+ÙCHÈÃ;ö @ó ,ð@ð" ‡�Ð-Ó.ÙAFÀtÃö 1ó /ð1ô)�Yô )ô*)�9ô )ô$X�iô Xð _Ð õ	0ð  ñ XàÑlØÑqØñ fàÑ>Ø
Ñ<ØÑ@ð	$BÐ �.ó 	Bõ%õLõ`õsñ FÐG\Ð^vÐwÙIÐKbð  eAð  Bð€õzõ(	Gõgð, ‡�Ð Ó!ÙNSÐTXËkö  ó "ð ð4 ‡�Ð!Ó"Ù5:¸4³[ö 5ó #ð5õWõ2ôn(�)ô (ð
 ‡�Ð(Ó)ÙKPÐQUË;ö ó *ðôˆyô ð
 ‡�ˆXÓÙ7<¸T³{ö ,ó ð,ð ‡�ˆxÓÙ!& s£¹UÀ3»ZÑ`eÐfjÓ`kö Oó ðOõ
5ð8 ‡�ˆ]ÓÙ>CÀD»kö $ó ð$ð. ‡�ˆNÓà#Ù# DÐ6bÔcöVó ðVð>Ûð
 ‡�ÐÓØ/7ö [ó ð[ð ‡�ÐÓØ9Aö ;ó ð;ð ‡�Ð$Ó%Ø=Eö "Wó &ð"WôJ!�yô !ð ‡�Ð'Ó(ØZbö Dó )ðDô$!�)ô !ð ‡�Ð'Ó(ÙUZÐ[_ÓU`ö 0ó )ð0ô!�)ô !ð ‡�Ð'Ó(ÙUZÐ[_ÓU`ö zó )ðzô!�9ô !ð ‡�Ð(Ó)ÙW\Ð]aÓWbö 
Uó *ð
UðTð ô�9ô ð
 ‡�ˆ[ÓôDó ðDð
 ‡�Ð(Ó)Ù@EÀdÃö Aó *ðAõ/õ
.Yõfð ‡�ˆIÓñEó ðEõõ õð ‡�ÐÓÙ/4°T«{ÑSXÐY]ÓS^ö ó ðð ‡�Ð9Ó:ÙGLÈTÃ{ö Fó ;ðFð  ‡�Ð;Ó<ÙINÈtËö Fó =ðFð ‡�ˆHÓÙ%*¨4£[ö +ó ð+ð ‡�ˆLÓÙ).¨t«ö >ó ð>ð ‡�Ð"Ó#Ù5:¸4ÀAÈ)Ô5TÑmrÐswÓmxö 
_ó $ð
_ð ‡�ˆ[ÓÙ" 5›\¹¸aÀuÔ8MÑfkÐlpÓfqö +ó ð+ð ‡�ÐÓñ!ó ð!ô,	�iô 	ö%�iô %ö�iô ö�iô ÷\÷
;÷÷	÷÷ð ‡�‰KÓò	ó ð	ð ‡�‰IÓÙ).¨u«÷ 2ð 2ó ð2÷$$ð ‡�‰YÓö[ó ð[ð  ‡�ÑÓö1ó  ð1ð, ‡�ÑÓ Ù+0°ÑDdÔ+e÷ Eð Eó !ðE÷>ð ‡�Ñ$Ó%ö'ó &ð'ð" ‡�ÑÓö
@ó ð
@ð ‡�Ñ$Ó%öó &ðö&�9ô &ö�yô ð ‡�‰KÓò
ó ð
ð ‡�Ñ(Ó)ö?ó *ð?ð$ ‡�Ñ5Ó6öCó 7ðCö!�Iô !ð ‡�Ñ)Ó*ÙY^Ð_cÓYd÷ nð nó +ðnö1 	ô 1ñ AÐ ð ‡�‰KÓò6ó ð6ð ‡�ÑÓò*ó ð*÷8
ð ‡‚ÑÓö(ó ð(ñ0 Ð ÷÷"÷
>÷;Dó ;Dð| ×Ò‘)Óö,ó ò,øðo ô ØƒGðús   Óh> è>	ié
i