+
    lHàjÄX  ã                   ó   a € R& t%0 t R t^ RIt^ RIt^ RIt^ RIt^ RIHtHt ^ RI	H
t
 ^ RIHt ]P                  ! R4      t/ t] ^ k ]! 4       t] ^k / t] ^k / t] ^k / t] ^k R R ltR R	 ltR't] ^k R
 R ltR(R R lltR R ltRRRRRR/R R lltR R ltR(R R lltR R ltR R lt R)R R llt!R  R! lt"RR/R" R# llt#R$ R% lt$R# )*z…MCP process lifecycle: stdio child PID tracking and orphan cleanup, graceful
server shutdown and draining of the background MCP loop.N)ÚDictÚOptional)Ú_core)Úmcp_tool_loopztools.mcp_toolc                óF   € V ^8„  d   QhR\         R\        \         ,          /# )é   ÚpidÚreturn)Úintr   )Úformats   "Ú=/home/ubuntu/.hermes/hermes-agent/tools/mcp_tool_lifecycle.pyÚ__annotate__r   &   s   € ÷ ñ œCð ¤H¬S¥Mñ ó    c                óD   € ^ RI Hp  V! V 4      #   \         d     R# i ; i)uä   Start-time fingerprint of the pgroup leader (PGID == leader PID on setsid spawn);
``None`` only when the reading is genuinely unavailable (already-reaped PID, no
/proc AND no psutil) â€” the psutil fallback covers macOS/Windows.)Úget_process_start_timeN)Úgateway.statusr   Ú	Exception)r   r   s   & r   Ú_leader_start_timer   &   s)   € õ 6ðÙ% cÓ*Ð*øÜô Úðús   ˆ �žc                ó$   € V ^8„  d   QhR\         /# ©r   r	   ©Úset)r   s   "r   r   r   1   s   € ÷ ñ œcñ r   c                 ó’  € \         P                  ! 4       p  RV  R2p\        4       p\         P                  ! V4       F\  p \	        V RV R2RR7      ;_uu_ 4       pVP                  R VP                  4       P                  4        4       4       RRR4       K^  	  V#   + '       g   i     Ks  ; i  \        \        \        3 d     K�  i ; i  \        \        \        3 d     Mi ; i ^ RIpTP                  T 4      P                  4        Uu0 uF  qfP                  kK  	  up# u upi   \         d    \        4       u # i ; i)	zGCurrent direct-child PIDs: /proc on Linux, else psutil, else empty set.z/proc/z/taskÚ/z	/childrenz	utf-8-sig)Úencodingc              3   óf   "  € T F'  qP                  4       '       g   K  \        V4      x € K)  	  R # 5i©N)Ústripr
   )Ú.0Úps   & r   Ú	<genexpr>Ú'_snapshot_child_pids.<locals>.<genexpr>B   s    é € Ð OÑ1A¨AÇWÁWÇY¤¤ Q§ Ó1Aùs   ‚1ž1N)ÚosÚgetpidr   ÚlistdirÚopenÚupdateÚreadÚsplitÚFileNotFoundErrorÚOSErrorÚ
ValueErrorÚpsutilÚProcessÚchildrenr   r   )Úmy_pidÚtask_dirÚfoundÚtidÚfr,   Úcs          r   Ú_snapshot_child_pidsr5   1   s  € ä�YŠY‹[€Fðð ˜F˜8 5Ð)ˆÜ“UˆÜ—:’:˜hÖ'ˆCðÜ˜X˜J a¨ u¨IÐ6À×MÕMÐQRØ—L‘LÑ O°·±³·±Ô1AÓ OÔO÷ NÑMñ (ð ˆ÷	 N×MÐMûä%¤w´
Ð;ô Úðûô œw¬
Ð3ô ÙðúðÛØ%Ÿ~™~¨fÓ5×>Ñ>Ô@ÓAÑ@˜!—”Ñ@ÑAÐAùÒAøÜô Ü‹uŠðús}   —)C ÁB3Á5BÂB3ÂC ÂB0Â*B3Â.C Â0B3Â3CÃ	C ÃCÃC ÃC)Ã(C)Ã-%D. ÄD)Ä&D. Ä)D. Ä.EÅEc                ó0   € V ^8„  d   QhR\         R\         /# )r   Úpidsr	   r   )r   s   "r   r   r   X   s   € ÷ ñ œsð ¤sñ r   c                ó¸  € V '       g   V #  ^ RI p\        4       pT  F~  p TP                  T4      P	                  4       p\        ;QJ d#    R TR,           4       F  '       g   K   RM	  RM! R TR,           4       4      '       d   Km  TP                  T4       K€  	  T#   \         d    T u # i ; i  TP
                  TP                  \        3 d     K¼  i ; i)zÜDrop non-MCP children from a PID snapshot delta. Tracking a stray child in _stdio_pgids
is catastrophic if it lacks start_new_session: its pgid can be the TUI parent's, so the
shutdown killpg() would kill the TUI itself.Nc              3   óD   "  € T F  p\          F	  q"V9   x € K  	  K  	  R # 5ir   )Ú_NON_MCP_CHILD_CMDLINE_MARKERS)r   ÚargÚmarkers   &  r   r    Ú'_filter_mcp_children.<locals>.<genexpr>h   s   é € Ðb©H S×Ca¸˜S–=ÑCa‘=«Hùs   ‚ :é   NNTF)
r,   ÚImportErrorr   r-   ÚcmdlineÚNoSuchProcessÚAccessDeniedr*   ÚanyÚadd)r7   r,   Úkeptr   Úargvs   &    r   Ú_filter_mcp_childrenrG   X   s¼   € ÷ ØˆðÛô ‹5€DÛˆð	Ø—>‘> #Ó&×.Ñ.Ó0ˆD÷ ‹sÑb¨D°®HÓb�s�sŠsÑb¨D°®HÓb×bÔbØ�H‰H�SŽMñ ð €Køô ô ØŠðûð ×$Ñ$ f×&9Ñ&9¼7ÐCô 	Úð	ús"   ŒB   B3Â B0Â/B0Â3"CÃCc                ó   € V ^8„  d   QhRR/# ©r   r	   N© )r   s   "r   r   r   m   s   € ÷ 	:ñ 	:¨4ñ 	:r   c                ó  € V f?   \         P                  P                  4        \         P                  P                  4        R# V  FC  p\         P                  P	                  VR4       \         P                  P	                  VR4       KE  	  R# )z–Drop connect-retry cooldowns: a restart must re-attempt every server immediately, not
honour a stale per-server backoff. Caller holds ``_core._lock``.N)r   Ú_server_connect_retry_afterÚclearÚ_server_connect_failuresÚpop)ÚkeysÚkeys   & r   Ú_clear_connect_cooldownsrR   m   sc   € ð ‚|Ü×)Ñ)×/Ñ/Ô1Ü×&Ñ&×,Ñ,Ö.ãˆCÜ×-Ñ-×1Ñ1°#°tÔ<Ü×*Ñ*×.Ñ.¨s°DÖ9ó r   c                ó   € V ^8„  d   QhRR/# rI   rJ   )r   s   "r   r   r   y   s   € ÷ 7ñ 7 tñ 7r   c                 ó"  € \         P                  ;_uu_ 4        \        \         P                  4      p \         P                  P	                  4        RRR4       X '       g   R# ^ RIHp ^ RIHpH	pH
p ^ RIHpHp ^ RIHp ^ RIHp V P%                  4        F”  w  ršR;r¼ V! V	4      pV! V! V! V	4      4      V	R7      pV! 4       ;'       g    / P%                  4        UUu/ uF  w  rÞWÚ9   g   K  WÞbK  	  pppV'       d   VP'                  V4       Te	   T! T4       Te   T! T4       K”  K–  	  R#   + '       g   i     Lè; iu uppi   \(         d    \*        P-                  RT	R	R
7        L^i ; i  Te	   T! T4       Te
   T! T4       i i ; i)aî  Re-run MCP registration for profiles whose ADOPTED shared connection an owner's
``/reload-mcp`` just tore down. Their tools vanished with the owner's teardown and nothing
re-runs their discovery until THEY reload, so they sat tool-less behind a healthy-looking
status (#106005). Runs after the owner's rediscovery, under each adopter's own home + secret
scope (its ``${VAR}`` refs must resolve to ITS credentials): the adopter re-adopts the owner's
new identical connection or connects its own.N)ÚPath)Úbuild_profile_secret_scopeÚreset_secret_scopeÚset_secret_scope)Úreset_hermes_home_overrideÚset_hermes_home_override)Úmcp_tool_discovery)Ú_load_mcp_config)Úprofile_homez0MCP: re-registration for profile scope %s failedT)Úexc_info)r   Ú_lockÚdictÚ_orphaned_adoptersrM   ÚpathlibrU   Úagent.secret_scoperV   rW   rX   Úhermes_constantsrY   rZ   Útoolsr[   Útools.mcp_tool_configr\   ÚitemsÚregister_mcp_serversr   ÚloggerÚdebug)ÚpendingrU   rV   rW   rX   rY   rZ   Ú
_discoveryr\   ÚadopterÚnamesÚ
home_tokenÚsecret_tokenÚnr4   Úserverss                   r   Ú_reregister_orphaned_adoptersrs   y   sN  € ô 
��‹Ü”u×/Ñ/Ó0ˆÜ× Ñ ×&Ñ&Ô(÷ 
÷ ÙÝßcÑcßUÝ6Ý6Ø!Ÿ-™-ž/‰ˆØ$(Ð(ˆ
ð	7Ù1°'Ó:ˆJÙ+Ñ,FÁtÈGÃ}Ó,UÐdkÔlˆLÙ)9Ó);×)AÐ)A¸r×(HÑ(HÔ(JÔYÑ(J¡ ÈaÉj”t�q’tÑ(JˆGÑYßØ×/Ñ/°Ô8ð Ò'Ù" <Ô0ØÒ%Ù*¨:Ö6ñ &ó *÷ 
�üó Zøô ô 	eÜ�L‰LÐKÈWÐ_cˆLÖdð	eûð Ò'Ù" <Ô0ØÒ%Ù*¨:Õ6ð &úsH   ™8D1Â A E
Ã 
EÃ/EÃ5E
Ä1E	ÅE
Å
#E0Å-E3Å/E0Å0E3Å3FÚscopern   Útimeoutg      .@c                óh   € V ^8„  d   QhR\         \        ,          R\         \        ,          R\        /# )r   rt   rn   ru   )r   Ústrr   Úfloat)r   s   "r   r   r   ›   s/   € ÷ V,ñ V,¤8¬C¥=ð V,ÄÌÅð V,Ü"'ñV,r   c           	     ó‚  aaaaa€ ^ RI Hp V RJ ;'       d    VRJ o\        P                  ;_uu_ 4        \        P                   Uu. uF3  pS'       g'   \        P
                  P                  V4      V 8X  g   K1  VNK5  	  upoVe!   S Uu. uF  qC! V4      V9   g   K  VNK  	  upoS Uu. uF  p\        P                  V,          NK  	  upoVe   \        S4      oMÔS'       d“   \        \        P                  4      \        \        P
                  4      ,          \        \        P                  4      ,          \        \        P                  4      ,          \        \        P                  4      ,          oM:\        P
                  P                  4        UUu0 uF  w  rEWP8X  g   K  VkK  	  uppoS'       gv   S Fo  p\        P                  P                  VR4       FH  pW`8w  g   K  \        P                  P                  V\        4       4      P                  V! V4      4       KJ  	  Kq  	  RRR4       V3R loS'       dˆ   VVVVV3R lp\        P                  ;_uu_ 4        \        P                  pRRR4       XeI   VP!                  4       '       d3   ^ RIHp	 V	! V! 4       V\&        RR7      p
V
e    V
P)                  VR7       \        P                  ;_uu_ 4        S'       g   S! 4        \/        S'       d   RMS4       RRR4       \0        P2                  ! S'       * R
7       Vf   ^ RIHp V! V R7       R# R# u upi u upi u upi u uppi   + '       g   i     EL3; i  + '       g   i     Lý; i  \*         d!   p\&        P-                  R	T4        Rp?LÚRp?ii ; i  + '       g   i     L­; i)uó  Close MCP server connections (in parallel) and stop the background loop. Each server
Task is signalled to exit its own ``async with`` so the anyio cancel-scope cleanup runs in
the Task that opened it. ``scope`` restricts teardown to one multiplexed profile's servers
(its ``/reload-mcp`` must not kill other profiles') and leaves the shared loop running if
anything else is still connected. ``names`` restricts it further to those server names
(dropped-from-config pruning); other servers' bookkeeping is untouched. Only the bare call
(no ``scope``, no ``names``) is the process-wide wildcard: the launch profile's registry
scope IS ``None``, so ``scope=None, names={...}`` prunes that unscoped owner's servers and
must leave a served profile's same-named ``(B, name)`` connection alone. ``timeout`` bounds
the wait for the close to land on the MCP loop â€” a caller running one pass per served
profile under a total budget divides it, or N profiles Ã— 15s starve the wildcard pass that
actually stops the loop.)Ú	_key_nameNc                  ó  <€ \         P                  P                  S4       S Fc  p \         P                  P	                  V R 4       \         P
                  P	                  V R 4       \         P                  P	                  V R 4       Ke  	  R # r   )r   Ú_server_connectingÚdifference_updateÚ_server_connect_errorsrO   Ú_server_scope_keysÚ_server_tool_scopes)rQ   Úselected_statuss    €r   Úclear_selected_statusÚ3shutdown_mcp_servers.<locals>.clear_selected_statusÂ   sc   ø€ Ü× Ñ ×2Ñ2°?ÔCÛ"ˆCÜ×(Ñ(×,Ñ,¨S°$Ô7Ü×$Ñ$×(Ñ(¨¨dÔ3Ü×%Ñ%×)Ñ)¨#¨tÖ4ó #r   c               “   ó.  <"  € \         P                  ! R  S 4       RR/ G Rj  x€L
 p \        SV 4       F>  w  r\        V\        4      '       g   K  \
        P                  RVP                  V4       K@  	  \        P                  ;_uu_ 4        S FC  p\        P                  P                  VR4       \        P                  P                  VR4       KE  	  S! 4        \        S'       d   RMS4       RRR4       R#  LÚ  + '       g   i     R# ; i5i)c              3   ó@   "  € T F  qP                  4       x € K  	  R # 5ir   )Úshutdown)r   Úservers   & r   r    Ú:shutdown_mcp_servers.<locals>._shutdown.<locals>.<genexpr>Ï   s   é € Ð,^ÑM]À6¯_©_×->Ð->ÓM]ùs   ‚Úreturn_exceptionsTNz!Error closing MCP server '%s': %s)ÚasyncioÚgatherÚzipÚ
isinstancer   ri   rj   Únamer   r_   Ú_serversrO   r   rR   )	Úresultsr‡   ÚresultrQ   r‚   Úselectedr�   Úservers_snapshotÚwildcards	       €€€€€r   Ú	_shutdownÚ'shutdown_mcp_servers.<locals>._shutdownÎ   sÅ   øé € Ü#ŸNšNÑ,^ÑM]Ó,^ÐwÐrvÑw×wˆGÜ"%Ð&6¸Ö"@‘�Ü˜f¤i×0Ô0Ü—L‘LÐ!DÀfÇkÁkÐSYÖZñ #Aô ——“Û#�CÜ—N‘N×&Ñ& s¨DÔ1Ü×,Ñ,×0Ñ0°°dÖ;ñ $ñ &Ô'Ü(·©¸ÔO÷ ‘ñ	 x÷ —�üs.   ƒ!D¤C?¥(DÁ=DÂA&DÃ5DÄD	Ä	D)Úsafe_schedule_threadsafez MCP shutdown: failed to schedule)ri   Úlog_message©ru   zError during MCP shutdown: %s©Úonly_if_idle)Ú_close_mcp_stderr_logs)rt   rJ   )Útools.mcp_tool_scoperz   r   r_   r�   r   Úgetr   r€   r|   r~   rg   ra   Ú
setdefaultrD   Ú	_mcp_loopÚ
is_runningÚagent.async_utilsr—   ri   r‘   ÚBaseExceptionrj   rR   Ú_loopÚ_stop_mcp_looprf   rœ   )rt   rn   ru   rz   rQ   Úownerrm   r•   Úloopr—   ÚfutureÚexcrœ   r‚   r’   r�   r“   r”   s   $$$          @@@@@r   Úshutdown_mcp_serversrª   ›   s¶  ü€ õ /Ø˜ˆ}×.Ð. ¨$ €HÜ	��‹Ü#(§>¢>Ól¡>˜C·XÄ×AYÑAY×A]ÑA]Ð^aÓAbÐfkÑAk—C�C¡>ÑlˆØÒÙ'/ÓK¡x °9¸S³>ÀUÑ3JŸ˜¡xÑKˆHÙ;CÓD¹8°CœEŸN™N¨3×/Ð/¹8ÑDÐØÒÜ! (›m‰Oßä”E—N‘NÓ#¤c¬%×*BÑ*BÓ&CÕCÜ”e×/Ñ/Ó0õ1ä”e×.Ñ.Ó/õ0ä25´e×6RÑ6RÓ2SõTñ ô
 6;×5MÑ5M×5SÑ5SÔ5UÔhÑ5U¡z sÐY^ÑYgŸs˜sÑ5UÒhˆO÷ Û�Ü$×8Ñ8×<Ñ<¸SÀ"ÖE�GØÖ'Ü×0Ñ0×;Ñ;¸GÄSÃUÓK×OÑOÑPYÐZ]ÓP^Ö_ó  Fñ  ÷% 
õ.5÷ ÷
	Pñ 
	Pô �[�[‹[Ü—?‘?ˆD÷ àÒ §¡× 1Ò 1ÝBÙ-©i«k¸4ÌÐ\~ÔˆFØÒ!ðGØ—M‘M¨'�MÔ2ô 
��‹ßÙ!Ô#Ü ¯¡°ÔG÷ 
ô 
×Ò¨(¤lÕ3ð ‚}Ý@Ù U×+ñ ùòG mùâKùÚDùó i÷ 
�ˆú÷^ �[ûô %ô GÜ—L‘LÐ!@À#×FÑFûðGú÷ 
�ús�   ´MÁMÁ!MÁ:MÂ MÂM	ÂM	Â$MÂ+ MÃMÃ&B3MÆ
M
Æ(M
Æ.MÆ:+MÇ*AMÉ'M-Ê:N  Ë$%N.ÍMÍM*	Í-M=	Î N+ÎN&Î&N+Î.N>	c                óä   € V ^8„  d   QhR\         R\        \        ,          R\        \        \
        \        3,          \        \
        \
        3,          \        \
        \
        3,          3,          /# )r   Úinclude_activeÚserver_namer	   )Úboolr   rw   Útupler`   r
   )r   s   "r   r   r   ô   sl   € ÷ ñ ¬ð ¼8ÄC½=ð ÌUÔSWÔX[Ô]`ÐX`ÕSaÔcgÔhkÔmpÐhpÕcqÔswÔx{ô  ~Að  yAõ  tBð  TBõ  NCñ r   c                óº  a€ R V3R llp\         P                  ;_uu_ 4        T! \         Uu/ uF  q3\        P	                  VR4      bK  	  up4      p\        P                  V4       V F  p\        P                  VR4       K  	  V '       d=   V! \        4      pVP                  V4       V F  p\        P                  VR4       K  	  V Uu/ uF%  qf\        9   g   K  V\        P                  V4      bK'  	  ppV Uu/ uF%  qf\        9   g   K  V\        P                  V4      bK'  	  ppRRR4       XXX3# u upi u upi u upi   + '       g   i     L$; i)z¯Pop the PIDs to reap (and their spawn-time pgids) out of the ledgers under the lock, so
a future spawn can't collide with stale state. Returns ``(pid -> owner, pid -> pgid)``.c                ót   € V ^8„  d   QhR\         \        \        3,          R\         \        \        3,          /# )r   Úentriesr	   )r`   r
   rw   )r   s   "r   r   Ú)_take_reapable_pids.<locals>.__annotate__÷   s.   € ÷ mñ mœœS¤#˜X�ð m¬4´´S°­>ñ mr   c                 ór   <€ V P                  4        UUu/ uF  w  rSe
   VS8X  g   K  WbK  	  upp# u uppi r   )rg   )r²   r   r¦   r­   s   &  €r   Ú_ownedÚ#_take_reapable_pids.<locals>._owned÷   s5   ø€ Ø-4¯]©]¬_Ôl©_™z˜sÀÒ@SÐW\Ð`kÑWk”
�’
©_ÒlÐlùÓls   •3©3ÚorphanN)r   r_   Ú_orphan_stdio_pidsÚ_orphan_stdio_pid_serversrž   r}   rO   Ú_stdio_pidsr&   Ú_stdio_pgidsÚ_stdio_starttimes)	r¬   r­   rµ   Úopidr7   Úactiver   ÚpgidsÚstartss	   &f       r   Ú_take_reapable_pidsrÁ   ô   s%  ø€ ÷mð mô 
��‹ÙÕWiÓjÑWiÈtÔ6×:Ñ:¸4ÀÓJÒJÑWiÑjÓkˆÜ×,Ñ,¨TÔ2ÛˆDÜ%×)Ñ)¨$°Ö5ñ çÙœKÓ(ˆFØ�K‰K˜ÔÛ�Ü—‘  TÖ*ñ á7;ÓS±t°ÄlÑ?RÔ+�”l×&Ñ& sÓ+Ò+±tˆÐSÙ=AÓ^¹T°cÔL]ÑE]Ô1�#Ô(×,Ñ,¨SÓ1Ò1¹TˆÐ^÷ 
ð �˜ÐÐùò kùò TùÚ^÷ 
�úsH   £E
®D;ÁAE
ÂA E
ÃE Ã!E Ã;E
ÄEÄEÄ,E
Ä;E
Å
E	c                ó¦   € V ^8„  d   QhR\         R\         R\        R\        \         ,          R\        \         ,          R\        \         ,          RR/# )	r   r   Úsigr­   ÚpgidÚmy_pgidÚexpected_startr	   N)r
   rw   r   )r   s   "r   r   r   	  sN   € ÷ 4-ñ 4-œSð 4-¤sð 4-¼ð 4-ÄHÌSÅMð 4-Ô\dÔehÕ\ið 4-Ü(0´­ð4-ØAEñ4-r   c                ó^  € Ve>   \        V 4      pVe/    ^ RIHp V! WV4      '       g   \        P	                  RW4       R#  \        \        RR4      pVe1   Ve-   Ve   W48X  d   \        P                  RW#4       M V! W14       R#  \        P                  ! W4       \        P                  R8X  d   \        W4       R# R#   \
        \        3 d     L“i ; i  \        \        \        3 d"   p	\        P	                  RY1Y)4        Rp	?	L„Rp	?	ii ; i  \        \        \        3 d     L�i ; i)u¢  SIGTERM/SIGKILL via the spawn-time pgroup on POSIX (reaches reparented grandchildren),
falling back to a per-pid signal.

PID-reuse guard (#43044): only signal if ``pid`` still names the process we spawned. Once
an MCP child exits and is reaped the kernel may recycle its PID/PGID onto an unrelated
process group; signalling the stale number would kill a stranger (observed: a recycled
PGID landing on a desktop browser's session leader). When ``expected_start`` was captured
at spawn and no longer matches â€” compared drift-tolerantly, because same-host readings
drift ~1 s on macOS (#117505) and exact equality skipped live, legitimately-owned servers
â€” skip entirely. Without a baseline (the capture raced the child's exit), or when the
current reading is unreadable (leader reaped: POSIX never reuses a PGID while a member
lives, so its reparented grandchildren are still ours), fall through to the legacy
best-effort path.N)Ústart_time_fingerprints_matchuw   Skip signalling MCP pid %d (%s): start-time mismatch â€” PID was recycled; refusing to kill an unrelated process group.Úkillpgu�   MCP server '%s' pgid %d matches gateway pgid; skipping killpg to avoid self-kill and using per-pid kill â€” any grandchildren in this group may not be reapedzHkillpg(%d, %d) failed for MCP server '%s': %s; falling back to kill(pid)Únt)r   r   rÈ   ri   rj   Ú	TypeErrorr+   Úgetattrr"   ÚwarningÚProcessLookupErrorÚPermissionErrorr*   ÚkillrŽ   Ú_kill_windows_process_tree)
r   rÃ   r­   rÄ   rÅ   rÆ   ÚcurrentrÈ   rÉ   r©   s
   &&&&&&    r   Ú_signal_mcp_processrÓ   	  s.  € ð Ò!Ü$ SÓ)ˆØÒðÝHÙ4°^×MÒMÜ—L‘LðGØHKôZñ ð	 Nô ”R˜ 4Ó(€FØÒ˜FÒ.ØÒ 4¤?ô �N‰Nð Kð MXõ_ð:Ù�tÔ!Ùð
Ü
�Š�Ôô 
‡w�w�$„Ü" 3Ö,ñ øô5 œzÐ*ô Ùðûô$ '¬¼ÐAô :ä—‘ÐgØ!¨÷:ñ :ûð:ûô ¤´Ð9ô Ùðús;   •*C Á=C ÂD ÃCÃCÃDÃ/DÄDÄD,Ä+D,c                ó4   € V ^8„  d   QhR\         R\         RR/# )r   r   rÃ   r	   N)r
   )r   s   "r   r   r   @  s!   € ÷ ñ ¤Cð ¬cð °dñ r   c                óì  € ^ RI p ^ RIp TP                  T 4      pTP	                  RR7      pT F  p TP                  4        K  	  T\        TRTP                  4      8X  d2   TP                  T^ R7      w  rxT F  p TP                  4        K  	  R# R#   \         d     R# i ; i  TP
                  TP                  \        3 d     R# i ; i  \         d     K°  i ; i  \         d     Kw  i ; i)u  Windows counterpart of the POSIX killpg path (#61059): after the direct child is signalled,
terminate every still-alive descendant (npx.cmd â†’ node.exe) so graceful teardown cannot leave
orphans reparented with ParentId=null. Best-effort, per-descendant; never raises.NT)Ú	recursiveÚSIGKILLr™   )Úsignalr,   r?   r-   r.   rA   rB   r*   Ú	terminater   rÌ   ÚSIGTERMÚ
wait_procsrÐ   )	r   rÃ   Ú_signalr,   ÚparentÚdescendantsÚchildÚ_Úalives	   &&       r   rÑ   rÑ   @  sø   € ó ðÛðØ—‘ Ó$ˆØ—o‘o°�oÓ5ˆó ˆð	Ø�O‰OÖñ ð
 Œg�g˜y¨'¯/©/Ó:Ô:Ø×$Ñ$ [¸!Ð$Ó<‰ˆÛˆEðØ—
‘
–ó ñ ;øô ô Úðûð
 × Ñ  &×"5Ñ"5´wÐ?ô Úðûô
 ô 	Úð	ûô ô ÚðúsE   †B ‹#B) ´CÁ?C$ÂB&Â%B&Â)"CÃCÃC!Ã C!Ã$C3Ã2C3c                óh   € V ^8„  d   QhR\         \        ,          R\         \        ,          R\        /# )r   rÄ   rÅ   r	   )r   r
   r®   )r   s   "r   r   r   \  s)   € ÷ 	ñ 	”x¤•}ð 	¬x¼­}ð 	Äñ 	r   c                óž   € V e   W8X  g   \        \        R4      '       g   R#  \        P                  ! V ^ 4       R#   \         d     R# i ; i)z‹A reaped leader's descendants that ignored SIGTERM keep its group alive, so the
SIGKILL pass must probe the group, not only the leader PID.rÉ   FT)Úhasattrr"   rÉ   r*   )rÄ   rÅ   s   &&r   Ú_group_aliverå   \  sD   € ð ‚|�t”¬g´b¸(×.CÒ.CÙðÜ
�	Š	�$˜ÔÙøÜô Úðús   ¤= ½AÁAc                óJ   € V ^8„  d   QhR\         R\        \        ,          RR/# )r   r¬   r­   r	   N)r®   r   rw   )r   s   "r   r   r   h  s,   € ÷ Añ A´ð AÌ8ÔTWÍ=ð AÐdhñ Ar   c                ó,  € ^ RI p\        W4      w  r4pV'       g   R#  \        P                  ! 4       pVP                  4        FP  w  rx\        WrP                  W„P                  V4      WeP                  V4      4       \        P                  RWx4       KR  	  \        P                  ! ^4       \        VRVP                  4      p	^ RIHp
 VP                  4        Fw  w  rxV
! V4      '       g$   \#        VP                  V4      V4      '       g   K6  \        WyW„P                  V4      WeP                  V4      4       \        P%                  RWx4       Ky  	  \&        P(                  ! RVP+                  4       4       R#   \        \
        3 d    Rp EL_i ; i)aš  Best-effort reap of stdio MCP subprocesses: SIGTERM, wait 2s, SIGKILL survivors. By
default only ``_orphan_stdio_pids`` are reaped so concurrent cron jobs / live sessions are
untouched; ``include_active=True`` also kills every ``_stdio_pids`` entry and is only for
final shutdown after the MCP loop has stopped. ``server_name`` limits the sweep to one
server (stdio reconnects cleaning up their old transport).Nz,Sent SIGTERM to orphaned MCP process %d (%s)r×   )Ú_pid_existsz6Force-killed MCP process %d (%s) after SIGTERM timeoutÚ
unregister)rØ   rÁ   r"   ÚgetpgrpÚAttributeErrorr*   rg   rÓ   rÚ   rž   ri   rj   ÚtimeÚsleeprÌ   r   rè   rå   rÍ   r   Ú_update_death_supervisorÚvalues)r¬   r­   rÜ   r7   r¿   rÀ   rÅ   r   r¦   Úsigkillrè   s   &&         r   Ú_kill_orphaned_mcp_childrenrñ   h  s&  € ó Ü-¨nÓJÑ€D�ßÙðÜ—*’*“,ˆð —j‘j–l‰
ˆÜ˜C§¡°%¿¹À3»È×R\ÑR\Ð]`ÓRaÔbÜ�‰ÐCÀSÖPñ #ô 	‡J‚Jˆq„MÜ�g˜y¨'¯/©/Ó:€GÝ*Ø—j‘j–l‰
ˆÙ�s×Òœ|¨E¯I©I°c«N¸G×DÔDÜ ¨e·Y±Y¸s³^ÀWÏjÉjÐY\ËoÔ^Ü�N‰NÐSÐUXÖ`ñ #ô 
×"Ò" <°·±³Ö@øô œGÐ$ô Ø‹ðús   žE< Å<FÆFc                ó$   € V ^8„  d   QhR\         /# r   )r®   )r   s   "r   r   r   ‡  s   € ÷ 3ñ 3¤ñ 3r   c                 ó0   € \         P                  ! RR7      # )zíStop the MCP loop only when no registered server still owns it. Probe paths create
temporary MCPServerTasks not placed in ``_servers``; they may clean up an idle loop but
must not tear down the process-global loop under live agent tools.Trš   )r¤   r¥   rJ   r   r   Ú_stop_mcp_loop_if_idlerô   ‡  s   € ô ×Ò¨TÔ2Ð2r   c                ó>   € V ^8„  d   QhR\         \        ,          RR/# )r   ru   r	   N)r   rx   )r   s   "r   r   r   Ž  s"   € ÷ kñ k¬H´U­Oð kÀtñ kr   c              ƒ  óp  "  € V f   \         P                  p \        P                  ! 4       p\        P                  ! 4        Uu. uF$  q"VJg   K
  VP                  4       '       d   K"  VNK&  	  ppV'       g   R# \        P                  R\        V4      4       V F  pVP                  4        K  	  \        P                  ! W0R7      G Rj  x€L
 w  rVV F+  pVP                  4       '       d   K  VP                  4        K-  	  V'       d#   \        P                  R\        V4      V 4       R# R# u upi  Lh5i)a  Cancel every task still pending on the MCP loop and reap it. ``Task.cancel()`` only
schedules the throw, so tasks need a cancellation cycle before the loop goes away; wait
for them here, on their owning loop, bounded so a task that suppresses cancellation
cannot hang process exit.Nz-Draining %d pending task(s) from the MCP loopr™   z3%d MCP loop task(s) still pending after %.1fs drain)r   Ú_MCP_LOOP_DRAIN_TIMEOUTrŠ   Úcurrent_taskÚ	all_tasksÚdoneri   rj   ÚlenÚcancelÚwaitÚ	cancelledÚ	exceptionrÍ   )ru   rÒ   Útrk   Útaskrú   Ústill_pendings   $      r   Ú_drain_mcp_loop_tasksr  Ž  sä   é € ð
 ‚Ü×/Ñ/ˆÜ×"Ò"Ó$€GÜ!×+Ò+Ô-ÓSÑ-�Q¸'Ð1AŒqÈ!Ï&É&Ï(�qˆqÑ-€GÐSßÙÜ
‡L�LÐ@Ä#ÀgÃ,ÔOÛˆØ�‰Žñ ä '§¢¨WÔ F×FÑ€DÛˆØ�~‰~×ÔØ�N‰NÖñ ÷ Ü�‰ÐLÌcÐR_ÓN`ÐbiÖjñ ùò Tñ Gùs7   ‚AD6ÁD/ÁD/Á'D/Á-AD6ÃD4ÃD6Ã/D6Ä*D6c                ó   € V ^8„  d   QhRR/# rI   rJ   )r   s   "r   r   r   ¤  s   € ÷ "ñ "¨ñ "r   c               ƒ  óø   "  € \         P                  ! 4       p  \        \        P                  R7      G Rj  x€L
  V P                  V P                  4       R#  L!  T P                  T P                  4       i ; i5i)a  Drain pending tasks, then stop the loop from its owning thread. Both must run as one
loop-owned sequence: a ``loop.stop`` queued separately by a timed-out caller can overtake
the scheduled drain, leaving the drain coroutine itself pending when the loop is closed.r™   N)rŠ   Úget_running_loopr  r   r÷   Ú	call_soonÚstop)r§   s    r   Ú_drain_and_stop_mcp_loopr	  ¤  sU   é € ô ×#Ò#Ó%€Dð"Ü#¬E×,IÑ,IÔJ×JÐJà�‰�t—y‘yÖ!ñ 	Køà�‰�t—y‘yÕ!üs+   ‚A:™A ¶A·A »A:ÁA ÁA7Á7A:c                óª  € V ^8„  d   Qh/ ^ \         9   d   \        \        \        3,          ;R&   ^\         9   d
   \        ;R&   ^\         9   d   \        \        \        3,          ;R&   ^\         9   d   \        \        \        3,          ;R&   ^\         9   d   \        \        \        3,          ;R&   ^\         9   d   \
        \        R3,          ;R&   # )r   rº   r¸   r¹   r»   r¼   .r:   )Ú__conditional_annotations__r`   r
   rw   r   r¯   )r   s   "r   r   r      s¨   € ÷ <ñ <÷ !Ò  ŒT”#”s�(�^Ñ  ñ<÷$  Ò ”CÑ ñ%<÷& /Ò .œ4¤¤S �>Ñ .ñ'<÷. "Ò !Œd”3œ�8�nÑ !ñ/<÷D 'Ò &”4œœS˜•>Ñ &ñE<÷bò ¤¤c¨3 h¥ñ òc<r   )ztui_gateway.slash_workerztui_gateway.entryz-dorg.eclipse.equinox.launcherzeclipse.jdt.lszorg.eclipse.equinox.launcher_r   )FN)&r  Ú__doc__ÚloggingrŠ   r"   rì   Útypingr   r   Útools.mcp_tool_commonr   re   r   r¤   Ú	getLoggerri   rº   r   r¸   r¹   r»   r¼   r   r5   r:   rG   rR   rs   rª   rÁ   rÓ   rÑ   rå   rñ   rô   r  r	  r   )r  s   @r   Ú<module>r     sä   øðö<ó Û Û 	Û ß !Ý 'Ý (à	×	Ò	Ð+Ó	,€ð !€Ó  ñ ›%Ð Ó Ø,.Ð Ó .ð  "€Ó !ð %'Ð Ó &õõðB3Ð ó õ÷*	:õ7ðDV,°4ð V,ÐPTð V,Ø*.÷V,õr÷*4-õnõ8	÷Aõ>3ðk¸d÷ k÷,"r   