+
    jHàjñ7  ã                  óÔ  a € 0 t $ R t^ RIHt ^ RIt^ RIt^ RIHtHt ^ RI	H
t
 ^ RIHtHtHtHt ^ RIHt ]P$                  ! 4       t]! 4       tR]R&   / tR	]R
&   RsR]R&   RsR]R&   R R ltR R ltR R ltR R ltR R ltR R ltR R lt RR/R R  llt!R!t"]R" R# l4       t#R$ R% lt$R& R' lt%R( R) lt&R7RR/R* R+ lllt'R, R- lt(R. R/ lt)R7R0 R1 llt*R2RRRR3R4/R5 R6 llt+R# )8z9Shared CLI/TUI-safe helpers for background MCP discovery.)ÚannotationsN)ÚcontextmanagerÚnullcontext)Úcopy_context)ÚDictÚIteratorÚOptionalÚSet)Úhermes_home_keyzset[str]Ú_mcp_discovery_startedzdict[str, threading.Thread]Ú_mcp_discovery_threadzOptional[threading.Timer]Ú_mcp_discovery_deferredúOptional[list[str]]Ú_mcp_server_filterc               ó    € V ^8„  d   QhRRRR/# )é   ÚtoolsetsÚobjectÚreturnr   © )Úformats   "Ú;/home/ubuntu/.hermes/hermes-agent/hermes_cli/mcp_startup.pyÚ__annotate__r      s   € ÷ ñ  Fð Ð/Bñ ó    c                óÈ  € . p\        V \        4      '       dG   V P                  R4       Uu. uF*  q"P                  4       '       g   K  VP                  4       NK,  	  ppMZ\        V \        \
        \        34      '       d:   V  F3  pVP                  R \        V4      P                  R4       4       4       K5  	  V'       d   RV9   g   RV9   d	   Rs\        # Vs\        # u upi )a
  Derive the MCP spawn allowlist from a ``-t/--toolsets`` value.

Built-in toolset names in the list are harmless (they never match a
configured ``mcp_servers`` key). ``all``/``*`` or an empty/absent value
clears the filter. Returns the stored list for logging/tests.
Ú,c              3  óp   "  € T F,  qP                  4       '       g   K  VP                  4       x € K.  	  R # 5i©N)Ústrip)Ú.0Úts   & r   Ú	<genexpr>Ú(set_mcp_server_filter.<locals>.<genexpr>*   s"   é € ÐNÑ,@ qÇGÁGÇIœ˜Ÿ™Ÿ˜Ó,@ùs   ‚6ž6ÚallÚ*N)	Ú
isinstanceÚstrÚsplitr   ÚlistÚtupleÚsetÚextendr   )r   Únamesr    Úitems   &   r   Úset_mcp_server_filterr.      s³   € ð €EÜ�(œC× Ò Ø$,§N¡N°3Ô$7ÓEÑ$7˜q¿7¹7¿9”�—‘–Ñ$7ˆÐEˆÜ	�Hœt¤U¬CÐ0×	1Ò	1ÛˆDØ�L‰LÑN¬C°«I¯O©O¸CÔ,@ÓNÖNñ ç�E˜U”N c¨U¤lØ!Ðô Ðð #ÐÜÐùò Fs   ¬CÁCc               ó   € V ^8„  d   QhRR/# )r   r   r   r   )r   s   "r   r   r   2   s   € ÷ ñ Ð2ñ r   c                 ó   € \         # r   )r   r   r   r   Úget_mcp_server_filterr1   2   s   € ÜÐr   c               ó   € V ^8„  d   QhRR/# ©r   r   Úboolr   )r   s   "r   r   r   6   s   € ÷ ñ  Tñ r   c                 óÞ   €  ^ RI Hp  V ! 4       ;'       g    / p\        VP                  R4      \        4      '       d   VR,          '       d   R# ^ RIHp V! V4      #   \         d     R# i ; i)zBCheap config probe so non-MCP users avoid importing the MCP stack.)Úread_raw_configÚmcp_serversT)Úhas_enabled_agent_plugin_mcp)Úhermes_cli.configr6   r%   ÚgetÚdictÚhermes_cli.agent_pluginsr8   Ú	Exception)r6   Ú
raw_configr8   s      r   Ú_has_configured_mcp_serversr?   6   s]   € ð
Ý5á$Ó&×,Ð,¨"ˆ
Ü�j—n‘n ]Ó3´T×:Ò:¸zÈ-×?XÔ?XÙÝIá+¨JÓ7Ð7øÜô Úðús   ‚A –6A ÁA ÁA,Á+A,c               ó   € V ^8„  d   QhRR/# r3   r   )r   s   "r   r   r   E   s   € ÷ dñ d¨Tñ dr   c                ó’   € \         ;QJ d&    R T ;'       g    .  4       F  '       g   K   R# 	  R# ! R T ;'       g    .  4       4      # )at  True when a discovery run left servers usable: a live session OR a lazy registration.

A ``lazy: true`` server never connects until first use, so an all-lazy config (the
memory-saving setup the feature exists for) looked like a run that achieved nothing:
the zero-connected warning fired on every startup and the retry path re-ran discovery
on every later call (#111717).
c              3  óz   "  € T F1  qP                  R 4      ;'       g    VP                  R4      R8H  x € K3  	  R# 5i)Ú	connectedÚstatusÚlazyN)r:   )r   Úentrys   & r   r!   Ú0_discovery_registered_servers.<locals>.<genexpr>M   s2   é € ÐcÑUaÈ5�y‰y˜Ó%×FÐF¨¯©°8Ó)<ÀÑ)FÔFÓUaùs   ‚;ž;TF)Úany)rD   s   &r   Ú_discovery_registered_serversrI   E   sG   € ÷ ‹3ÑcÐU[×UaÐUaÐ_aÐUaÓc�3Œ3ÐcŠ3Ðcˆ3ÑcÐU[×UaÐUaÐ_aÐUaÓcÓcÐcr   c               ó   € V ^8„  d   QhRR/# r3   r   )r   s   "r   r   r   P   s   € ÷ Añ A˜Dñ Ar   c                 óB   € ^ RI Hp  \        V ! 4       ;'       g    . 4      # )é    ©Úget_mcp_status)Útools.mcp_tool_discoveryrN   rI   rM   s    r   Ú_any_mcp_connectedrP   P   s   € Ý7ä(©Ó)9×)?Ð)?¸RÓ@Ð@r   c               ó   € V ^8„  d   QhRR/# )r   r   z	list[str]r   )r   s   "r   r   r   V   s   € ÷ fñ f 9ñ fr   c                 óv   € ^ RI Hp  V ! 4       p\        f   V# V Uu. uF  q"\        9   g   K  VNK  	  up# u upi )rL   )Úmcp_servers_awaiting_connect)rO   rS   r   )rS   ÚpendingÚns      r   Ú_servers_awaiting_connectrV   V   s7   € ÝEá*Ó,€GÜ(Ò0ˆ7ÐeÁ'Ó6eÁ'¸QÔRdÑMd·q°qÁ'Ñ6eÐeùÒ6es   œ6­6c               ó    € V ^8„  d   QhRRRR/# )r   Úthread_namer&   r   ÚNoner   )r   s   "r   r   r   ]   s   € ÷ :ñ :¸3ð :À4ñ :r   c           	     ór  a a€ \        4       o\        ;_uu_ 4        S\        9   dÛ   \        P	                  S4      pVe"   VP                  4       '       d    RRR4       R#  \        4       pV'       d   \        4       M. pT'       d   T'       g    RRR4       R# T'       d#   S P                  RRP                  T4      4       MS P                  R4       \        P                  S4       \        P                  SR4       \        P                  S4       \        4       '       g    RRR4       R# R VV 3R llp\         P"                  ! \%        4       P&                  V3VRR7      pV\        S&   VP)                  4        RRR4       R#   \         d     RRR4       R# i ; i  + '       g   i     R# ; i)	a  Spawn one background MCP discovery thread per profile home.

If the first run exits without connecting any server (e.g. startup cancellation / OOM restart),
later calls may retry instead of pinning the profile in "already started" with zero MCP tools.
Likewise a server added to ``mcp_servers`` after that run (``hermes mcp add`` against a running
Desktop backend) is connected by the next call, which every agent build makes, so a new session
gets its tools without a reload (#76954). Discovery is additive: live servers are untouched.
Nz5MCP server(s) %s not connected yet; running discoveryz, z_Background MCP discovery previously exited with no connected servers; retrying discovery threadc               ó   € V ^8„  d   QhRR/# ©r   r   rY   r   )r   s   "r   r   Ú4start_background_mcp_discovery.<locals>.__annotate__‡   s   € ÷ 	>ñ 	>˜4ñ 	>r   c            	     óþ  <€  \        4         \        4       '       g   SP                  R 4       \
        ;_uu_ 4        \        P                  S R4       RRR4       R#   \         d    SP	                  RRR7        LOi ; i  \         d    SP	                  RRR7        Lsi ; i  + '       g   i     R# ; i  \
        ;_uu_ 4        \        P                  S R4       RRR4       i   + '       g   i     i ; i; i)z>Background MCP discovery completed with zero connected serversz7Failed to inspect MCP status after background discoveryT©Úexc_infoz$Background MCP tool discovery failedN)Ú-_discover_mcp_tools_without_interactive_oauthrP   Úwarningr=   ÚdebugÚ_mcp_discovery_lockr   Úpop)Úhome_keyÚloggers   €€r   Ú	_discoverÚ1start_background_mcp_discovery.<locals>._discover‡   sÁ   ø€ ð>Ü=Ô?ðkÜ-×/Ò/ØŸ™Ð'gÔh÷ )Ö(Ü)×-Ñ-¨h¸Ô=÷ )Ñ(øô !ô kØ—L‘LÐ!ZÐei�LÖjðkûäô TØ—‘ÐCÈd�ÖSðTú÷ )×(Ð(û×(Ö(Ü)×-Ñ-¨h¸Ô=÷ )×(×(Ð(ýsi   ƒ
B Ž!A ½B&ÁA?Á<B Á=B: Á>A?Á?B ÂB#Â B: Â"B#Â#B: Â&B7	Â:C<Ã	C)	Ã 	C<Ã)C9Ã4C<T)ÚtargetÚargsÚnameÚdaemon)r
   rd   r   r   r:   Úis_aliverP   rV   r=   ÚinfoÚjoinrb   Údiscardre   Úaddr?   Ú	threadingÚThreadr   ÚrunÚstart)rg   rX   ÚthreadrC   rT   rh   rf   s   d$    @r   Ústart_background_mcp_discoveryrx   ]   s]  ù€ ô Ó €Hß	Ö	ØÔ-Ô-Ü*×.Ñ.¨xÓ8ˆFØÒ! f§o¡o×&7Ò&7Ø÷	 
Ñ	ð
Ü.Ó0�	ß9BÔ3Ô5È�÷ §Ø÷ 
Ñ	÷ Ø—‘ÐSÐUY×U^ÑU^Ð_fÓUgÕhà—‘ð9ôô #×*Ñ*¨8Ô4Ü!×%Ñ% h°Ô5ä×"Ñ" 8Ô,Ü*×,Ò,Ø÷1 
Ñ	÷@	>ñ 	>ô ×!Ò!¬«×);Ñ);À9À,ÐU`ÐimÔnˆØ*0Ô˜hÑ'Ø�‰Œ÷a 
Ñ	øô ô Ø÷ 
Ñ	ðú÷ 
×	Ð	úsU   ›:F%ÁF%Á"FÂ F%ÂF%ÂF%ÂF%Â#BF%Ä1AF%ÆF"ÆF%Æ!F"Æ"F%Æ%F6	Úsingle_queryFc               ó$   € V ^8„  d   QhRRRRRR/# )r   Úexplicitúfloat | Nonery   r4   r   Úfloatr   )r   s   "r   r   r   š   s"   € ÷ ñ ¨ð Èð ÐY^ñ r   c               ó:  € V e   V # V'       d   RMRpV'       d   RMRp ^ RI HpHp \        VP	                  W#4      4      p \        T! 4       ;'       g    / P	                  Y&4      4      pT^ 8”  d   T# T#   \
         d    Tu # i ; i  \
         d    Tu # i ; i)zÕResolve the MCP discovery wait bound: explicit arg > config.yaml > ``DEFAULT_CONFIG``.

Lazy and fail-safe: a missing/invalid value or broken config falls back to a short bound so
startup can never hang or crash.
Ú"mcp_single_query_discovery_timeoutÚmcp_discovery_timeoutg      .@g      ø?)Úload_configÚDEFAULT_CONFIG)r9   r�   r‚   r}   r:   r=   )r{   ry   ÚkeyÚfallbackr�   r‚   ÚdefaultÚvals   &$      r   Ú_resolve_discovery_timeoutr‡   š   s    € ð ÒØˆß2>Ñ
.ÐD[€Cß#‰t¨€HðßAä˜×*Ñ*¨3Ó9Ó:ˆðÜ‘[“]×(Ð( b×-Ñ-¨cÓ;Ó<ˆØ˜A”gˆsÐ* 7Ð*øô	 ô ØŠðûô
 ô ØŠðús/    "A7 ÁB
 ÁB
 Á5B
 Á7BÂBÂ
BÂBgü©ñÒMb@?c               ó   € V ^8„  d   QhRR/# )r   r   zIterator[None]r   )r   s   "r   r   r   »   s   € ÷ $ñ $˜~ñ $r   c               #  óØ   "  € \         P                  ! 4       p \         P                  ! \        4        Rx € \         P                  ! V 4       R#   \         P                  ! T 4       i ; i5i)z¡Temporarily lower the interpreter switch interval so discovery's CPU work can't
monopolize the GIL against concurrent threads (see _DISCOVERY_SWITCH_INTERVAL_S).N)ÚsysÚgetswitchintervalÚsetswitchintervalÚ_DISCOVERY_SWITCH_INTERVAL_S)Úprevs    r   Ú_discovery_gil_budgetr�   º   sH   é € ô × Ò Ó"€DÜ×ÒÔ6Ô7ð$Ûä×Ò˜dÖ#øŒ×Ò˜dÕ#üs   ‚0A*³A ·A*ÁA'Á'A*c               ó   € V ^8„  d   QhRR/# r\   r   )r   s   "r   r   r   Æ   s   € ÷ Eñ E°tñ Er   c            	     ó<  €  ^ RI Hp  \	        4       ;_uu_ 4        V ! 4       ;_uu_ 4        ^ RIHp \        f	   V! 4        MV! \        R7       RRR4       RRR4       R#   \         d
    \        p  Lki ; i  + '       g   i     L1; i  + '       g   i     R# ; i)zCRun MCP discovery without letting OAuth read from the user's stdin.)Úsuppress_interactive_oauth)Údiscover_mcp_toolsN)Úallowed_mcp_names)Útools.mcp_oauthr’   r=   r   r�   rO   r“   r   )r’   r“   s     r   ra   ra   Æ   sm   € ð1Ý>ô 
×	 Õ	 Ñ"<×">Õ">Ý?ô
 Ò%ÙÕ áÔ1CÕD÷ #?×	 Ñ	 øô ô 1Ü%0Ò"ð1ú÷ #?×">ú×	 ×	 Ð	 ús3   ‚A  šB
ª$A7	ÁB
Á A4Á3A4Á7BÂB
Â
B	c               ó$   € V ^8„  d   QhRRRRRR/# )r   rX   r&   Údelayr|   r   rY   r   )r   s   "r   r   r   Ù   s"   € ÷ ñ ¸3ð À|ð ÐX\ñ r   c                óH  a a€ \         ;_uu_ 4        \        4       \        9   g	   \        e    RRR4       R# R V V3R llp\        P
                  ! Vf   ^ MTV4      pRVn        S R2Vn        VsVe   VP                  4        RRR4       R#   + '       g   i     R# ; i)ah  Arm ``start_background_mcp_discovery`` to run ``delay`` seconds from now.

Used by the Desktop ``serve`` backend after its socket is announced: the thread's first act is
the ~350ms ``mcp`` SDK import, which would hold the GIL against the renderer's connect + first
hydration reads (or the web_server import) if started earlier.

``delay=None`` arms without a clock: the standalone dashboard fires it from the first ``/api/ws``
client (``start_deferred_mcp_discovery_now``) or the first agent build (``wait_for_mcp_discovery``),
so an idle, unvisited dashboard never spawns the configured stdio MCP servers (#58733).
Nc               ó   € V ^8„  d   QhRR/# r\   r   )r   s   "r   r   Ú4defer_background_mcp_discovery.<locals>.__annotate__é   s   € ÷ 	Sñ 	S�tñ 	Sr   c                 óz   <€ \         ;_uu_ 4        R sR R R 4       \        S SR7       R #   + '       g   i     L; i)N©rg   rX   )rd   r   rx   rœ   s   €€r   Ú_fireÚ-defer_background_mcp_discovery.<locals>._fireé   s'   ø€ ç$Ö$Ø*.Ð'÷ %ä*°&Àk×R÷ %×$ús   �*ª:	Tz	-deferred)	rd   r
   r   r   rs   ÚTimerrm   rl   rv   )rg   rX   r—   r�   Útimers   dd$  r   Údefer_background_mcp_discoveryr¡   Ù   s…   ù€ ÷ 
Ö	ÜÓÔ 6Ô6Ô:QÒ:]Ø÷ 
Ñ	÷	Sñ 	Sô —’ U¢]¡¸¸uÓEˆØˆŒØ#�} IÐ.ˆŒ
Ø"'ÐØÒØ�K‰KŒM÷# 
×	×	Ò	ús   ‘B¸ABÂB!	c               ó   € V ^8„  d   QhRR/# r\   r   )r   s   "r   r   r   ù   s   € ÷ ñ ¨$ñ r   c                 ó¸   € \         ;_uu_ 4        \        Rup sRRR4       X f   R# V P                  4        V P                  4        R#   + '       g   i     L8; i)zBRun an armed deferred start immediately (idempotent, thread-safe).N)rd   r   ÚcancelÚfunction)r    s    r   Ú start_deferred_mcp_discovery_nowr¦   ù   s@   € ÷ 
Ö	Ü)@À$Ð&ˆÐ&÷ 
à‚}ÙØ	‡L�L„NØ	‡N�NÖ÷ 
×	ús   �
A	Á	A	c               ó$   € V ^8„  d   QhRRRRRR/# )r   Útimeoutr|   ry   r4   r   rY   r   )r   s   "r   r   r     s'   € ÷ Xñ X Lð XÈ$ð XÐ[_ñ Xr   c               óž   € \        4        \        4       pVe   VP                  4       '       g   R# VP                  \	        WR7      R7       R# )a>  Wait for background MCP discovery before the first tool snapshot.

``join`` returns the instant discovery completes, so this only blocks for a still-pending
server's real connect time. ``single_query`` uses ``mcp_single_query_discovery_timeout``
(15s vs 1.5s) because one-shot sessions have no second turn to recover.
N)ry   ©r¨   )r¦   Ú_current_home_threadrn   rp   r‡   )r¨   ry   rw   s   &$ r   Úwait_for_mcp_discoveryr¬     s:   € ô %Ô&Ü!Ó#€FØ‚~˜VŸ_™_×.Ò.ÙØ
‡K�KÔ2°7ÔV€KÖWr   c               ó   € V ^8„  d   QhRR/# )r   r   zOptional[threading.Thread]r   )r   s   "r   r   r     s   € ÷ 8ñ 8Ð8ñ 8r   c                 ó<   € \         P                  \        4       4      # )zFDiscovery thread for the profile home the caller is scoped to, if any.)r   r:   r
   r   r   r   r«   r«     s   € ä ×$Ñ$¤_Ó%6Ó7Ð7r   c               ó   € V ^8„  d   QhRR/# r3   r   )r   s   "r   r   r     s   € ÷ 4ñ 4 ñ 4r   c                 óN   € \        4       p V RJ;'       d    V P                  4       # )aë  True if THIS module's discovery thread (for the current profile home) is still running.

Mirrors ``tui_gateway.entry.mcp_discovery_in_flight``; surfaces that start discovery here
(desktop, dashboard sidecar) populate this thread, so the late-refresh scheduler consults both.

Those processes populate THIS module's ``_mcp_discovery_thread``, not ``tui_gateway.entry``'s, so the
late-refresh scheduler must consult both to decide whether a slow server's tools are still pending (see
#51587).
N)r«   rn   )rw   s    r   Úmcp_discovery_in_flightr±     s&   € ô "Ó#€FØ˜Ð×3Ð3 &§/¡/Ó"3Ð3r   c               ó    € V ^8„  d   QhRRRR/# )r   r¨   r|   r   r4   r   )r   s   "r   r   r   %  s   € ÷ !ñ ! ð !¸ñ !r   c                óp   € \        4       pVf   R# VP                  V R7       VP                  4       '       * # )z¶Block up to ``timeout`` for THIS module's discovery; True once complete, False if still
running. For the off-critical-path late-refresh waiter (accepts a long wait, reports outcome).Trª   )r«   rp   rn   )r¨   rw   s   & r   Újoin_mcp_discoveryr´   %  s2   € ô "Ó#€FØ‚~ÙØ
‡K�K˜€KÔ Ø�‰Ó Ô Ð r   r¨   rX   zcli-mcp-discoveryc               ó(   € V ^8„  d   QhRRRRRRRR/# )	r   r¨   r|   ry   r4   rX   r&   r   rY   r   )r   s   "r   r   r   /  s:   € ÷ _ñ _ð ð_ð ð	_ð
 ð_ð
 04ñ_r   c                ó‚   €  \        WR7       \        WR7       R#   \         d    T P                  RRR7        R# i ; i)uE  Give configured MCP tools a bounded chance to register before AIAgent.

Non-interactive first turns (``chat -q``, ``hermes -z``) can construct ``AIAgent`` before any
path started discovery, and ``wait_for_mcp_discovery()`` only joins an existing thread â€” so
start discovery if needed, then wait up to the configured bound.
rœ   )r¨   ry   z7MCP discovery readiness check failed before agent buildTr_   N)rx   r¬   r=   rc   )rg   r¨   ry   rX   s   $$$$r   Ú'ensure_mcp_discovery_before_agent_buildr·   /  s;   € ð_Ü&¨fÕNÜ w×JøÜô _Ø�‰ÐNÐY]ˆ×^ð_ús   ‚ œ>½>r   ),Ú__conditional_annotations__Ú__doc__Ú
__future__r   rŠ   rs   Ú
contextlibr   r   Úcontextvarsr   Útypingr   r   r   r	   Úhermes_constantsr
   ÚLockrd   r*   r   Ú__annotations__r   r   r   r.   r1   r?   rI   rP   rV   rx   r‡   r�   r�   ra   r¡   r¦   r¬   r«   r±   r´   r·   )r¸   s   @r   Ú<module>rÁ      s  øðÜ ?å "ã 
Û ß 2Ý $ß 0Ó 0å ,à—n’nÓ&Ð ñ
 $'£5Ð ˜Ó (Ø57Ð Ð2Ó 7Ø59Ð Ð2Ó 9ð +/Ð Ð'Ó .õõ*õõdõAõfõ:ðzÈu÷ ð:  &Ð ð ô$ó ð$õEõ&õ@ñXÐQV÷ Xð Xõ8õ
4÷!ð_ð !ð_ð ð	_ð
 +÷_ñ _r   