Ë
    ãß²jÆ  ã            
       ó<  — d Z ddlZddlZddlZddlZddlZddlZddlZddlmZm	Z	 ddl
mZ ddlmZmZmZmZ ddlmZmZmZ ddlmZ ddlmZ dd	lmZ dd
lmZ ddlmZ  ddlm!Z" dee#ef   dee$   fd„Z%ddœdee#ef   dee$   de&fd„Z'de#ddfd„Z(dDd„Z)d„ Z*ejV                  de#fd„«       Z,dddœde#de&de&ddfd„Z-d„ Z.d e#d!e#ddfd"„Z/d#e#d$e#de&fd%„Z0d&e1d e#d!e#de&fd'„Z2d e#dee$   fd(„Z3d$e#dee1   fd)„Z4d e#d!e#deee1   ee&   f   fd*„Z5d&e1d!e#de&fd+„Z6d e#d!e#d,e7de&fd-„Z8d.„ Z9d/„ Z:d0„ Z;d1„ Z<de#fd2„Z=dEd3„Z>dEd4„Z?d5e1de&fd6„Z@d7ed8e#d9e$de#ddf
d:„ZAdFd;„ZBdGd<„ZCde#ddfd=„ZDdHdee#   ddfd>„ZEd e#d!e#de&fd?„ZFde#dee#ef   ddfd@„ZGde#ddfdA„ZHde#ddfdB„ZIdDdC„ZJy)Iuù   Browser session lifecycle: inactivity janitor, orphan reaper, per-session teardown, atexit cleanup.

Split out of ``tools/browser_tool.py``. Facade-owned state is read through ``_bt`` (``tools.browser_tool``, resolved per call) â€” no import cycle.
é    N)ÚdatetimeÚtimezone)ÚPath)ÚAnyÚDictÚOptionalÚTuple)Úget_hermes_homeÚreset_hermes_home_overrideÚset_hermes_home_override)Úorigin)Úbrowser_tool_cdp)Úbrowser_tool_cloud)Úbrowser_tool_session)Úbrowser_tool_install)Úbrowser_tool_real_profileÚsession_infoÚreturnc                 ó  — | j                  d«      }t        |t        t        f«      rt        |t        «      st        |«      S t        |t
        «      r|j                  «       sy|j                  «       }|j                  d«      r|dd › d�}	 t        j                  |«      }|j                  € |j                  t         j"                  ¬«      }|j%                  «       S # t        $ r" t        j                  j                  d«       Y yw xY w)z£Provider-authoritative session expiry as epoch seconds; None when absent or
    malformed (cloud providers may omit ``expires_at``; local browsers never have one).Ú
expires_atN)ÚZÚzéÿÿÿÿz+00:00z7Ignoring invalid cloud browser session expiry timestamp)Útzinfo)ÚgetÚ
isinstanceÚintÚfloatÚboolÚstrÚstripÚendswithr   ÚfromisoformatÚ
ValueErrorÚ_btÚloggerÚwarningr   Úreplacer   ÚutcÚ	timestamp)r   ÚvalueÚ
normalizedÚparseds       úA/home/ubuntu/.hermes/hermes-agent/tools/browser_tool_lifecycle.pyÚ_session_expiry_timestampr/      sá   € ð ×Ñ˜\Ó*€EÜ�%œ#œu˜Ô&¬z¸%ÄÔ/FÜ�U‹|ÐÜ�eœSÔ!¨¯©¬Øà—‘“€JØ×Ñ˜:Ô&Ø" 3 B˜Ð(¨Ð/ˆ
ðÜ×'Ñ'¨
Ó3ˆð ‡}�}ÐØ—‘¤x§|¡|�Ó4ˆØ×ÑÓÐøô ò Ü�
‰
×ÑÐTÔUÙðús   ÂC Ã(D
Ä	D
)Únowr0   c                óZ   — t        | «      }|€y|€t        j                  «       |k\  S ||k\  S )z?Whether a cached browser session crossed its provider deadline.F)r/   Útime)r   r0   r   s      r.   Ú_session_has_expiredr3   0   s6   € ô +¨<Ó8€JØÐØØ˜;ŒD�I‰I‹K°JÑ>Ð>¨C°JÑ>Ð>ó    Úlabelc                 ó‚   — 	  |«        y# t         $ r+}t        j                  j                  d| |«       Y d}~yd}~ww xY w)uR   Run ``fn()``; log (debug) and swallow any exception â€” teardown must never abort.z%s failed: %sN)Ú	Exceptionr%   r&   Údebug)r5   ÚfnÚes      r.   Ú_best_effortr;   :   s5   € ð4Ù
�øÜò 4Ü�
‰
×Ñ˜¨%°×3Ñ3ûð4ús   ‚
 Š	>“!9¹>c                  ó   — ddl m}   | «        y )Nr   ©Ústop_all_lightpanda)Útools.browser_lightpandar>   r=   s    r.   Ú_stop_all_lightpandar@   B   s   € Ý<ÙÕr4   c                  óØ  — 	 t         j                  rydt         _        t        dt        j
                  «       t         j                  rñt         j                  j                  dt        t         j                  «      «       	 t        «        t         j                  5  t         j                  j                  «        t         j                  j                  «        t         j                  j                  «        t         j                   j                  «        t         j"                  j                  «        ddd«       t        dt$        «       t        dt&        «       y# t        $ r Y yw xY w# t        $ r+} t         j                  j                  d| «       Y d} ~ �Œd} ~ ww xY w# 1 sw Y   ŒpxY w# t         j                  5  t         j                  j                  «        t         j                  j                  «        t         j                  j                  «        t         j                   j                  «        t         j"                  j                  «        ddd«       w # 1 sw Y   w xY wxY w)uÁ   atexit: close this process's sessions, then sweep orphans left by crashed
    hermes processes â€” every clean exit reaps accumulated orphans, not only
    processes that used the browser tool.NTz#Real-profile chrome cleanup on exitz2Emergency cleanup: closing %s active session(s)...zEmergency cleanup error: %szLightpanda cleanup on exitzOrphan reap on exit)r%   Ú_cleanup_doner7   r;   Ú_real_profileÚ_terminate_real_profile_chromeÚ_active_sessionsr&   ÚinfoÚlenÚcleanup_all_browsersÚerrorÚ_cleanup_lockÚclearÚ_session_last_activityÚ_session_owner_homesÚ_cleanup_failuresÚ_recording_sessionsr@   Ú_reap_orphaned_browser_sessions)r:   s    r.   Ú_emergency_cleanup_all_sessionsrQ   G   s­  € ðÜ×ÒØØ ŒÔô Ð6¼×8dÑ8dÔeÜ
×ÒÜ�
‰
�‰ÐLÌcÔRU×RfÑRfÓNgÔhð
	0Ü Ô"ô ×"Ñ"ñ 0Ü×$Ñ$×*Ñ*Ô,Ü×*Ñ*×0Ñ0Ô2Ü×(Ñ(×.Ñ.Ô0Ü×%Ñ%×+Ñ+Ô-Ü×'Ñ'×-Ñ-Ô/÷0ô Ð-Ô/CÔDô Ð&Ô(GÕHøô5 ò ñ 	ðûô ò 	?Ü�J‰J×ÑÐ:¸A×>Ò>ûð	?ú÷0ð 0û”×"Ñ"ñ 0Ü×$Ñ$×*Ñ*Ô,Ü×*Ñ*×0Ñ0Ô2Ü×(Ñ(×.Ñ.Ô0Ü×%Ñ%×+Ñ+Ô-Ü×'Ñ'×-Ñ-Ô/÷0÷ 0ñ 0ýsf   ‚E “E Â 
E) ÂBF Å	E&Å%E&Å)	FÅ2 FÆF, ÆFÆF, Æ F)Æ,I)Æ=BIÉ	I)ÉI&É"I)Útask_idc              #   óL  K  — t         j                  j                  | «      }|€d–— yddlm}m}m} ddlm} t        |«      }	  |t        |«      «        | |t        |«      «      «      }	 d–—  ||«       	 t        |«       y#  ||«       w xY w# t        |«       w xY w­w)a  Run under the Hermes home + secret scope owning ``task_id``'s session (no-op if unrecorded).

    The janitor thread is process-global, so each teardown must re-enter its OWN
    profile's scope rather than inherit the spawning profile's; never falls
    through to ``os.environ``.
    Nr   )Úbuild_profile_secret_scopeÚreset_secret_scopeÚset_secret_scope)Úhydrate_profile_secret_sources)r%   rM   r   Úagent.secret_scoperT   rU   rV   Úhermes_cli.env_loaderrW   r   r   r   )rR   Ú
owner_homerT   rU   rV   rW   Ú
home_tokenÚsecret_tokens           r.   Ú_session_owner_scoper]   l   s�   è ø€ ô ×)Ñ)×-Ñ-¨gÓ6€JØÐÛØçcÑcÝDä)¨*Ó5€Jð/Ù&¤t¨JÓ'7Ô8Ù'Ñ(BÄ4È
ÓCSÓ(TÓUˆð	-Ûá˜|Õ,ä" :Õ.øñ ˜|Õ,ûä" :Õ.üs6   ‚AB$Á(B Á.B Á2B Á;B$Â
BÂB ÂB!Â!B$TF)ÚactivityÚsessionr^   r_   c                óV  — t         j                  5  |r t         j                  j                  | d«       |r t         j                  j                  | d«       t         j
                  j                  | d«       t         j                  j                  | d«       ddd«       y# 1 sw Y   yxY w)zRDrop the janitor's bookkeeping (and optionally the session entry) for ``task_id``.N)r%   rJ   rE   ÚpoprL   rM   rN   )rR   r^   r_   s      r.   Ú_forget_session_trackingrb   ˆ   s~   € ä	×	Ñ	ñ 1ÙÜ× Ñ ×$Ñ$ W¨dÔ3ÙÜ×&Ñ&×*Ñ*¨7°DÔ9Ü× Ñ ×$Ñ$ W¨dÔ3Ü×Ñ×!Ñ! '¨4Ô0÷1÷ 1ñ 1ús   ‘BBÂB(c            	      ó   — t        j                   «       } t        j                  5  t        t        j                  j                  «       «      D ��cg c]  \  }}| |z
  t        j                  kD  r|‘Œ }}}ddd«       D ]z  }t        | t        j                  j                  || «      z
  «      }t        j                  j                  d||«       	 t        |«      5  t        |«       ddd«       t        |«       Œ| yc c}}w # 1 sw Y   Œ�xY w# 1 sw Y   Œ)xY w# t        $ �rX}t        j                  5  t        j                  j                  |d«      dz   x}t        j                  |<   ddd«       n# 1 sw Y   nxY wt        j                   k  r7t        j                  j#                  d||t        j                   |«       Y d}~�ŒLt        j                  j%                  d|||«       	 t        |«      5  t'        |«       ddd«       n# 1 sw Y   nxY wn7# t        $ r+}t        j                  j%                  d||«       Y d}~nd}~ww xY wt        |d¬	«       n# t        |d¬	«       w xY wY d}~�Œùd}~ww xY w)
a;  Close sessions inactive longer than the timeout (cleanup thread).

    Each teardown runs under its owner profile's scope. A session whose cleanup
    keeps failing is force-reaped after MAX_INACTIVITY_CLEANUP_FAILURES attempts;
    only a successful cleanup clears its failure count.

    See #100738, #86402.
    Nz<Cleaning up inactive session for task: %s (inactive for %ss)r   é   z9Error cleaning up inactive session %s (attempt %d/%d): %szJBrowser cleanup failed %d times for inactive session %s; force-reaping: %sz+Force-reap of browser session %s failed: %sF)r^   )r2   r%   rJ   ÚlistrL   ÚitemsÚ"BROWSER_SESSION_INACTIVITY_TIMEOUTr   r   r&   rF   r]   Úcleanup_browserrb   r7   rN   ÚMAX_INACTIVITY_CLEANUP_FAILURESr'   rI   Ú_force_reap_browser_session)Úcurrent_timerR   Ú	last_timeÚsessions_to_cleanupÚelapsedr:   ÚfailuresÚreap_excs           r.   Ú"_cleanup_inactive_browser_sessionsrq   “   s0  € ô —9‘9“;€Lä	×	Ñ	ñ eÜAEÄc×F`ÑF`×FfÑFfÓFhÓAi÷ eÑ+=¨7°IØ".°Ñ":¼S×=cÑ=cÒ"cò  'ð eÐñ e÷eð 'ò BˆÜ�l¤S×%?Ñ%?×%CÑ%CÀGÈ\Ó%ZÑZÓ[ˆÜ�
‰
�‰ÐVÐX_ÐahÔið	BÜ% gÓ.ñ )Ü Ô(÷)ä$ WÕ-ñBùóe÷eð eú÷)ð )ûô ó 	BÜ×"Ñ"ñ fÜ<?×<QÑ<Q×<UÑ<UÐV]Ð_`Ó<aÐdeÑ<eÐe�œ3×0Ñ0°Ñ9÷f÷ fñ fúàœ#×=Ñ=Ò=Ü—
‘
×"Ñ"Ð#^Ø&¨´#×2UÑ2UÐWXôZåÜ�J‰J×Ñð -Ø.6¸ÀôDðBÜ)¨'Ó2ñ 9Ü/°Ô8÷9÷ 9ñ 9üäò cÜ—
‘
× Ñ Ð!NÐPWÐYa×bÑbûðcúô )¨¸5ÖAøÔ(¨¸5ÖAúÕAûð	BúsÀ   ¥+DÁ"C=Á2DÃDÃDÃ'DÃ=DÄDÄD	ÄDÄ
I=Ä%I8Ä57E5Å,	I8Å5E>Å:AI8Ç"I8Ç.HÇ9HÈ	HÈHÈHÈI È	IÈ$!I
ÉI É
IÉI ÉI8É I/É/I8É8I=Ú
socket_dirÚsession_namec                 óX  — 	 t         j                  j                  | |› d�«      }t        |dd¬«      5 }|j	                  t        t        j                  «       «      «       ddd«       y# 1 sw Y   yxY w# t        $ r+}t        j                  j                  d||«       Y d}~yd}~ww xY w)zÞRecord this hermes PID in ``<socket_dir>/<session>.owner_pid`` so the orphan
    reaper can tell live-owner daemons from crashed-owner ones. Best-effort: an
    OSError falls back to the legacy ``tracked_names`` heuristic.ú
.owner_pidÚwúutf-8©ÚencodingNz)Could not write owner_pid file for %s: %s)ÚosÚpathÚjoinÚopenÚwriter    ÚgetpidÚOSErrorr%   r&   r8   )rr   rs   r{   ÚfÚexcs        r.   Ú_write_owner_pidrƒ   »   s‰   € ðYÜ�w‰w�|‰|˜J¨<¨.¸
Ð(CÓDˆÜ�$˜ gÔ.ð 	&°!Ø�G‰G”CœŸ	™	›Ó$Ô%÷	&÷ 	&ñ 	&ûäò YÜ�
‰
×ÑÐDÀlÐTW×XÑXûðYús4   ‚1A5 ³-A)Á A5 Á)A2Á.A5 Á2A5 Á5	B)Á>!B$Â$B)Útokenr{   c                 ó.  — t         j                  j                  |«      j                  «       }| j	                  d«      rd| v r| j                  dd«      d   n| }t        |«      xr0 t         j                  j                  |«      j                  «       |k(  S )zKTrue when ``token`` (or its ``--flag=VALUE`` value) names exactly ``path``.Ú-Ú=rd   )rz   r{   ÚnormpathÚlowerÚ
startswithÚsplitr   )r„   r{   ÚwantÚ	candidates       r.   Ú_argv_token_is_pathrŽ   Ç   sv   € ä�7‰7×Ñ˜DÓ!×'Ñ'Ó)€DØ*/×*:Ñ*:¸3Ô*?ÀCÈ5ÁL�—‘˜C Ó# AÒ&ÐV[€IÜ�	‹?ÒJœrŸw™w×/Ñ/°	Ó:×@Ñ@ÓBÀdÑJÐJr4   Ú
daemon_pidc                 óT  ‡ ‡‡— dt         dt        fˆ ˆfd„}	 ddl}	 |j	                  ‰ «      }|j                  «       xs dj                  «       }t        |j                  «       xs g «      }dj                  |«      j                  «       }d|vrd|vr	 |d|«      S t        ˆfd„|D «       «      }
|
sq	 |j                  «       xs i j                  dd«      }t        |«      xr? t         j"                  j%                  |«      t         j"                  j%                  ‰«      k(  }
|
s	 |d‰«      S y# t        $ r  |d«      cY S w xY w# |j                  $ r Y y	|j                  t        f$ r}	 |d
|	«      cY d}	~	S d}	~	ww xY w# |j                  |j                  t        f$ r d	}
Y Œ‚w xY w)u¬  Confirm a live PID is genuinely *this* session's agent-browser daemon (fail-closed).

    The ``.pid`` file sits in a world-writable temp dir: a planted or recycled PID
    would turn the tree-kill into an arbitrary-process DoS. Both must pass:
    (1) identity â€” ``agent-browser`` in name/cmdline; (2) binding â€” the socket dir in
    the cmdline or ``AGENT_BROWSER_SOCKET_DIR`` in its environ (the real spoof defense).
    Úreasonr   c                 óP   •— t        j                  j                  d| z   ‰‰g|¢­Ž  y)Nz5Refusing to reap browser daemon PID %d (session %s): F)r%   r&   r'   )r‘   Úargsr�   rs   s     €€r.   Úrefusez/_verify_reapable_browser_daemon.<locals>.refuse×   s.   ø€ Ü�
‰
×ÑÐRÐU[Ñ[Ø% |ð	<Ø6:ó	<àr4   r   Nz,psutil unavailable for identity verificationÚ Ú Fz$could not read process identity (%s)zagent-browserz&not an agent-browser process (name=%r)c              3   ó6   •K  — | ]  }t        |‰«      –— Œ y ­w©N)rŽ   )Ú.0Útokrr   s     €r.   ú	<genexpr>z2_verify_reapable_browser_daemon.<locals>.<genexpr>ñ   s   øè ø€ ÒE¸Ô# C¨×4ÑEùs   ƒÚAGENT_BROWSER_SOCKET_DIRzNnot bound to session socket dir %s (possible recycled PID or planted pid file)T)r    r   ÚpsutilÚImportErrorÚProcessÚnamer‰   re   Úcmdliner|   ÚNoSuchProcessÚAccessDeniedr€   ÚanyÚenvironr   rz   r{   rˆ   )r�   rr   rs   r”   r�   Úprocr    Úargvr¡   r‚   ÚboundÚenv_dirs   ```         r.   Ú_verify_reapable_browser_daemonrª   Î   s›  ú€ ð”sð ¤dö ð
FÛðCØ�~‰~˜jÓ)ˆØ—	‘	“Ò!˜r×(Ñ(Ó*ˆÜ�D—L‘L“NÒ( bÓ)ˆØ—(‘(˜4“.×&Ñ&Ó(ˆð ˜dÑ" ¸gÑ'EÙÐ>ÀÓEÐEô
 ÓEÀÔEÓE€EÙð	Ø—|‘|“~Ò+¨×0Ñ0Ð1KÈRÓPˆGÜ˜“MÒ_¤b§g¡g×&6Ñ&6°wÓ&?Ä2Ç7Á7×CSÑCSÐT^ÓC_Ñ&_ˆEñ ÙÐfÐhrÓsÐsØøô9 ò FÙÐDÓEÒEðFûð ×Ñò ÙØ×Ñ¤Ð)ò CÙÐ<¸cÓBÕBûðCûð ×#Ñ# V×%9Ñ%9¼7ÐCò 	ØŠEð	úsH   ˜D0 �A/E Â4A0F Ä0EÅEÅE?ÅE?Å,E:Å4E?Å:E?Æ"F'Æ&F'c                 óŽ  — 	 t         j                  j                  | «      }	 t        j                  | «      5 }|D ]'  }	 t        ||j                  «       j                  «      }Œ) 	 ddd«       t        dt        j                  «       |z
  «      S # t        $ r Y yw xY w# t        $ r Y Œnw xY w# 1 sw Y   ŒHxY w# t        $ r Y ŒVw xY w)uý   Seconds since anything in ``socket_dir`` was last written; None if unknown (fail safe).
    Every command rewrites ``_stdout_<cmd>`` there â€” a restart-proof activity marker â€” and
    rewriting doesn't touch the dir mtime, so entries are scanned too.Nç        )	rz   r{   Úgetmtimer€   ÚscandirÚmaxÚstatÚst_mtimer2   )rr   ÚlatestÚentriesÚentrys       r.   Ú_socket_dir_idle_secondsrµ   ý   sÃ   € ðÜ—‘×!Ñ! *Ó-ˆðÜ�Z‰Z˜
Ó#ð 	 wØ ò �ðÜ  ¨¯©«×)>Ñ)>Ó?‘Fñ÷	ô ˆs”D—I‘I“K &Ñ(Ó)Ð)øô ò Ùðûô ò Ùðú÷		ð 	ûô ò Ùðús\   ‚B ¢B8 ·B,¾$BÁ"B,Á%B8 Â	BÂBÂ	B)Â&B,Â(B)Â)B,Â,B5Â1B8 Â8	CÃCc                 ó’   — 	 t        t        | «      j                  d¬«      j                  «       «      S # t        t
        f$ r Y yw xY w)z8Integer PID from ``path``; None when missing or corrupt.rw   rx   N)r   r   Ú	read_textr!   r$   r€   )r{   s    r.   Ú_read_pid_filer¸     sD   € ðÜ”4˜“:×'Ñ'°Ð'Ó9×?Ñ?ÓAÓBÐBøÜœÐ ò Ùðús   ‚14 ´AÁAc                 ó€   — t        t        j                  j                  | |› d�«      «      }|€yddlm} | ||«      fS )z`Read ``<session>.owner_pid`` and report ``(pid, alive)``; ``(None, None)`` when missing/corrupt.ru   )NNr   ©Ú_pid_exists)r¸   rz   r{   r|   Úgateway.statusr»   )rr   rs   Ú	owner_pidr»   s       r.   Ú_owner_pid_aliver¾     s>   € äœrŸw™wŸ|™|¨J¸<¸.È
Ð8SÓTÓU€IØÐØå*Ø‘k )Ó,Ð,Ð,r4   c                 ój   — ddl m} ddlm}  || «      }|€ |d| |«       y|j	                  | |«       y)z¾Tree-kill ``daemon_pid`` if it has a start-time fingerprint (so a PID swapped
    between check and kill is refused); False (logged via ``log``) when no fingerprint.
    Raises on OS errors.r   )Úget_process_start_time)ÚProcessRegistryzXRefusing to reap browser daemon PID %d (session %s): no start-time fingerprint availableFT)r¼   rÀ   Útools.process_registryrÁ   Ú_terminate_host_pid)r�   rs   ÚlogrÀ   rÁ   Údaemon_starts         r.   Ú_terminate_verified_daemonrÆ   %  s@   € õ 6Ý6Ù)¨*Ó5€LØÐÙÐfØ˜ô	&àØ×'Ñ'¨
°LÔAØr4   Útracked_namesc                 ón  — t        | |«      \  }}|du ra||v ryt        | «      }|�|t        j                  k  ryt        j                  j                  d||t        |«      t        j                  «       n|€||v ryt        j                  j                  | |› d�«      }t        j                  j                  |«      s9t        | «      }|�|t        j                  k  ryt        j                  | d¬«       yt        |«      }ddlm} |� ||«      st        j                  | d¬«       yt!        || |«      syd}		 t#        ||t        j                  j
                  «      syt        j                  j%                  d||«       d}	t        j                  | d¬«       |	S # t&        t(        t*        f$ r Y Œ/w xY w)	u  Reap one ``agent-browser-<session>`` dir if orphaned; True when a daemon was killed.

    A live ``owner_pid`` means another hermes process owns it â€” leave it UNLESS untracked
    here and idle past ``BROWSER_ORPHAN_GRACE_SECONDS`` (owner-alive alone made leaked
    daemons immortal); no owner_pid (legacy) falls back to this process's tracking. A
    pidless dir is only stale after the grace period (deleting it immediately races the
    creator's first stdout open). The PID is identity-verified before any tree-kill.
    TFu}   Browser session %s has a live owner (PID %s) but is untracked and idle for %ds (grace %ds) â€” treating as leaked and reapingú.pid©Úignore_errorsr   rº   z2Reaped orphaned browser daemon PID %d (session %s))r¾   rµ   r%   ÚBROWSER_ORPHAN_GRACE_SECONDSr&   r'   r   rz   r{   r|   ÚisfileÚshutilÚrmtreer¸   r¼   r»   rª   rÆ   rF   ÚProcessLookupErrorÚPermissionErrorr€   )
rr   rs   rÇ   r½   Úowner_aliveÚidle_sÚpid_filer�   r»   Úreapeds
             r.   Ú_reap_socket_dirrÖ   4  sŠ  € ô .¨j¸,ÓGÑ€Iˆ{Ø�dÑØ˜=Ñ(ØÜ)¨*Ó5ˆØˆ>˜V¤c×&FÑ&FÒFØÜ�
‰
×ÑðNà˜)¤S¨£[Ü×,Ñ,õ		.ð
 
Ð	 °Ñ!>Øä�w‰w�|‰|˜J¨<¨.¸Ð(=Ó>€HÜ�7‰7�>‰>˜(Ô#Ü)¨*Ó5ˆØˆ>˜V¤c×&FÑ&FÒFØÜ�‰�j°Õ5Øä Ó)€JÝ*ØÐ¡¨ZÔ!8Ü�‰�j°Õ5Øä*¨:°zÀ<ÔPØð €FðÜ)¨*°lÄCÇJÁJ×DVÑDVÔWØÜ�
‰
�‰ÐLÈjÐZfÔgØˆô ‡M�M�*¨DÕ1Ø€Møô ¤´Ð9ò Ùðús   Ä;%F Å!#F ÆF4Æ3F4c                  ó  — ddl } d„ }t        d|«       t        j                  «       }g }ddddt        j                  › �fD ]4  }|| j                  t
        j                  j                  ||«      «      z  }Œ6 |syt        j                  5  t        j                  j                  «       D �ch c]%  }|j                  d	«      sŒ|j                  d	«      ’Œ' }}ddd«       j                  t        j                  «       d}|D ]F  }t
        j                  j                  |«      j                  d«      }	|	sŒ4t        ||	|«      sŒB|d
z  }ŒH |r!t        j                   j#                  d|«       yyc c}w # 1 sw Y   ŒžxY w)zãKill agent-browser daemons whose owning hermes process is gone (an unclean exit loses
    ``_active_sessions`` but node + Chromium keep running). Scans the tmp dir for
    ``agent-browser-*`` socket dirs; safe from any context.r   Nc                  ó   — ddl m}   | «        y )Nr   ©Úreap_orphaned_lightpanda)r?   rÚ   rÙ   s    r.   Ú_reap_lpz1_reap_orphaned_browser_sessions.<locals>._reap_lpr  s   € ÝEÙ Õ"r4   zLightpanda orphan reapzagent-browser-h_*zagent-browser-cdp_*zagent-browser-hermes_*úagent-browser-rs   rd   z:Reaped %d orphaned browser session(s) from previous run(s))Úglobr;   r%   Ú_socket_safe_tmpdirÚ_REAL_PROFILE_SESSIONrz   r{   r|   rJ   rE   Úvaluesr   ÚaddÚbasenameÚremoveprefixrÖ   r&   rF   )
rÝ   rÛ   ÚtmpdirÚsocket_dirsÚprefixrF   rÇ   rÕ   rr   rs   s
             r.   rP   rP   j  sd  € ó ò#ô Ð)¨8Ô4ä×$Ñ$Ó&€FØ€Kà&Ð(=Ð?WØ#¤C×$=Ñ$=Ð#>Ð?ðAò ?ˆà�t—y‘y¤§¡§¡¨f°fÓ!=Ó>Ñ>‰ð?ñ Øä	×	Ñ	ñ yÜ>A×>RÑ>R×>YÑ>YÓ>[Öx°dÐ_c×_gÑ_gÐhvÕ_w˜Ÿ™ .Õ1ÐxˆÐx÷yð
 ×Ñ”c×/Ñ/Ô0à€FØ!ò ˆ
Ü—w‘w×'Ñ'¨
Ó3×@Ñ@ÐAQÓRˆÚÔ,¨Z¸À}ÕUØ�a‰K‰Fðñ
 Ü�
‰
�‰ÐTÐV\Õ]ð ùò y÷yð yús$   Â!E5Â,E0ÃE0ÃE5Å0E5Å5E>c                  ó$  — t        dt        t        j                  dz  «      «      } d}t        j                  rl|| z  dk(  r	 t        «        |dz  }	 t        «        t        d«      D ])  }t        j                  s nt        j                  d«       Œ+ t        j                  rŒkyy# t        $ r*}t        j                  j                  d|«       Y d}~Œˆd}~ww xY w# t        $ r*}t        j                  j                  d|«       Y d}~Œ®d}~ww xY w)z“Every 30s: close sessions idle past BROWSER_SESSION_INACTIVITY_TIMEOUT; reap
    orphans on startup AND every BROWSER_ORPHAN_REAP_INTERVAL seconds.rd   é   r   zOrphan reap error: %sNzCleanup thread error: %s)r¯   Úroundr%   ÚBROWSER_ORPHAN_REAP_INTERVALÚ_cleanup_runningrP   r7   r&   r'   rq   Úranger2   Úsleep)Úreap_every_cyclesÚcycler:   Ú_s       r.   Ú_browser_cleanup_thread_workerrñ   ‘  sè   € ô ˜Aœu¤S×%EÑ%EÈÑ%JÓKÓLÐØ€Eä
×
Ò
ØÐ$Ñ$¨Ò)ð?Ü/Ô1ð 	�‰
ˆð	>Ü.Ô0ô �r“ò 	ˆAÜ×'Ò'ÙÜ�J‰J�q�Mð	ô ×
Õ
øô ò ?Ü—
‘
×"Ñ"Ð#:¸A×>Ñ>ûð?ûô ò 	>Ü�J‰J×ÑÐ9¸1×=Ñ=ûð	>ús0   Á
B& Á
C Â&	CÂ/ CÃCÃ	DÃ% D
Ä
Dc                  ó¢  — t         j                  5  t         j                  �t         j                  j                  «       s|dt         _        t        j                  t        dd¬«      t         _        t         j                  j                  «        t         j                  j                  dt         j                  «       ddd«       y# 1 sw Y   yxY w)z;Start the background cleanup thread if not already running.NTzbrowser-cleanup)ÚtargetÚdaemonr    z0Started inactivity cleanup thread (timeout: %ss))r%   rJ   Ú_cleanup_threadÚis_aliverë   Ú	threadingÚThreadrñ   Ústartr&   rF   rg   © r4   r.   Ú_start_browser_cleanup_threadrû   ª  s’   € ä	×	Ñ	ñ xÜ×ÑÐ&¬c×.AÑ.A×.JÑ.JÔ.LØ#'ŒCÔ Ü"+×"2Ñ"2Ô:XÐaeØ8Iô#KŒCÔä×Ñ×%Ñ%Ô'Ü�J‰J�O‰OÐNÔPS×PvÑPvÔw÷x÷ xñ xús   ‘B+CÃCc                  ó°   — 	 dt         _        t         j                  } | �	 | j	                  d¬«       yy# t        $ r Y yw xY w# t
        t        f$ r Y yw xY w)z#Stop the background cleanup thread.FNé   ©Útimeout)r%   rë   rõ   r7   r|   Ú
SystemExitÚKeyboardInterrupt)Úthreads    r.   Ú_stop_browser_cleanup_threadr  µ  si   € ðØ$ŒÔÜ×$Ñ$ˆð
 Ðð	Ø�K‰K ˆKÕ"ð	 øô	 ò ñ 	ðûô Ô-Ð.ò 	Ùð	ús   ‚4  A ´	A ¿A ÁAÁAc                 óú   — t         j                  5  t        j                  «       t         j                  | <   t         j                  j                  | t        t        «       «      «       ddd«       y# 1 sw Y   yxY w)z¿Touch the activity timestamp and record the owning Hermes home on first sight (the
    janitor tears down under the owner's scope). Does NOT reset ``_cleanup_failures``.

    See #86402.
    N)r%   rJ   r2   rL   rM   Ú
setdefaultr    r
   )rR   s    r.   Ú_update_session_activityr  Ç  sZ   € ô 
×	Ñ	ñ MÜ.2¯i©i«kŒ×"Ñ" 7Ñ+Ü× Ñ ×+Ñ+¨G´S¼Ó9JÓ5KÔL÷M÷ Mñ Mús   ‘AA1Á1A:c                 ój   — 	 ddl m}  || j                  «       y# t        $ r t	        | «       Y yw xY w)u­  Best-effort kill of *proc* and every descendant; never raises.

    ``Popen.kill()`` only signals the direct child; npm/npx helpers and the detached
    daemon grandchild keep a capture pipe open so ``communicate()`` never sees EOF, so
    the whole tree must go (no grace: the caller already burned its timeout). Delegates
    to :func:`agent.deadline.kill_process_tree`, falling back to the legacy kill.

    ``Popen.kill()`` only signals the direct child PID. npm/npx routinely fork further processes
    (registry-fetch helpers, npm's own lifecycle runner, agent-browser's own detached daemon grandchild)
    that can survive a plain ``kill()`` of the top-level PID and keep a ``capture_output``-style pipe open,
    hanging the caller's ``communicate()`` past the nominal timeout â€” the same orphaned-pipe hazard already
    hit in production on POSIX (see ``tools/process_registry.py``'s ``_reader_loop``, issue 68915: a
    backgrounded grandchild inheriting a pipe's write end kept it from ever reaching EOF). That hazard is
    cross-platform, not Windows-specific; what *is* Windows-specific is the lack of a remedy other than
    killing the tree â€” anonymous pipes there don't support overlapped I/O, so there's no ``select()``-style
    non-blocking read to poll around a stuck grandchild the way POSIX can. Killing the whole process
    group/tree the child was launched into reaches those descendants on both platforms. See #68915.
    r   )Úkill_process_treeN)Úagent.deadliner  Úpidr7   Ú_legacy_kill_process_tree)r¦   Ú_deadline_kill_trees     r.   Ú_kill_process_treer  Ò  s-   € ð&(ÝKá˜DŸH™HÕ%øÜò (Ü! $Ö'ð(ús   ‚ ›2±2c                 óN  — t         j                  dk(  rA	 t        j                  ddt	        | j
                  «      ddgddt        j                  ¬«       y	t        t         d
d	«      }|€	 | j                  «        y		 t        j                  | j
                  «      }t        j                  t        t        dt        j                  «      fD ]  }	  |||«       Œ y	# t        $ r Y y	w xY w# t        $ r Y y	w xY w# t        t        f$ r Y y	w xY w# t        t        t        f$ r Y  y	w xY w)u�   Local tree-kill (SIGTERM then SIGKILL to the process group) â€” fallback when
    agent.deadline is unavailable; tests pin this signal sequence.ÚntÚtaskkillz/PIDz/Tz/FFT)ÚcheckÚcapture_outputÚstdinNÚkillpgÚSIGKILL)rz   r    Ú
subprocessÚrunr    r
  ÚDEVNULLr7   ÚgetattrÚkillÚgetpgidrÐ   r€   ÚsignalÚSIGTERMrÑ   )r¦   r  ÚpgidÚsigs       r.   r  r  í  s  € ô 
‡w�w�$‚ð	Ü�N‰N˜J¨´°D·H±H³¸tÀTÐJØ!&°tÄ:×CUÑCUõWð 	ô ”R˜ 4Ó(€FØ€~ð	Ø�I‰IŒKð 	ðÜ�z‰z˜$Ÿ(™(Ó#ˆô —‘¤¬°	¼6¿>¹>Ó JÐKò ˆð	Ù�4˜Õñøô! ò 	ØØð	ûô ò 	ØØð	ûô
 ¤Ð(ò Ùðûô
 #¤O´WÐ=ò 	Úð	úsG   •?C Á)C( Á;C7 Ã	DÃ	C%Ã$C%Ã(	C4Ã3C4Ã7D	ÄD	ÄD$Ä#D$r
  c                 ó*   — | dk  ryddl m}  || «      S )z[Best-effort 'is this PID alive' (cross-platform via gateway.status; zombies count as dead).r   Frº   )r¼   r»   )r
  Ú_gateway_pid_existss     r.   r»   r»     s   € à
ˆa‚xØÝAÙ˜sÓ#Ð#r4   Ú	directoryÚpatternÚmax_age_hoursc                 ó¼  — 	 t        j                   «       |dz  z
  }| j                  |«      D ]0  }	 |j                  «       j                  |k  r|j	                  «        Œ2 y# t
        $ r,}t        j                  j                  d|||«       Y d}~Œdd}~ww xY w# t
        $ r9}t        j                  j                  d|j                  «       |«       Y d}~yd}~ww xY w)zNDelete ``directory/pattern`` files older than ``max_age_hours``; never raises.é  zFailed to clean old %s %s: %sNz#%s cleanup error (non-critical): %s)
r2   rÝ   r°   r±   Úunlinkr7   r%   r&   r8   Ú
capitalize)r"  r#  r$  r5   Úcutoffr�   r:   s          r.   Ú_unlink_older_thanr*    sÀ   € ð	WÜ—‘“ °Ñ 4Ñ5ˆØ—‘ Ó(ò 	OˆAðOØ—6‘6“8×$Ñ$ vÒ-Ø—H‘H”Jøñ	Oøô ò OÜ—
‘
× Ñ Ð!@À%ÈÈA×NÑNûðOûäò WÜ�
‰
×ÑÐ>À×@PÑ@PÓ@RÐTU×VÑVûðWús@   ‚.B ±-A!ÁB Á!	BÁ*"BÂB ÂBÂB Â	CÂ"/CÃCc                 óÒ   — t        | «      }t        j                  «       }|t        j                  j	                  |d«      z
  dk  ry|t        j                  |<   t        | d|d«       y)zHPrune old browser screenshots; throttled to once per hour per directory.r¬   r&  Nzbrowser_screenshot_*.pngÚ
screenshot)r    r2   r%   Ú_last_screenshot_cleanup_by_dirr   r*  )Úscreenshots_dirr$  Úkeyr0   s       r.   Ú_cleanup_old_screenshotsr0  !  sZ   € ä
ˆoÓ
€CÜ
�)‰)‹+€CØ
ŒS×0Ñ0×4Ñ4°S¸#Ó>Ñ>ÀÒEØØ/2„C×'Ñ'¨Ñ,Ü�Ð(BÀMÐS_Õ`r4   c                 óÊ   — 	 t        «       dz  }|j                  «       rt        |d| d«       yy# t        $ r*}t        j                  j	                  d|«       Y d}~yd}~ww xY w)zPrune old browser recordings.Úbrowser_recordingsz*Recording cleanup error (non-critical): %sNzsession_*.webmÚ	recording)r
   r7   r%   r&   r8   Úexistsr*  )r$  Úrecordings_dirr:   s      r.   Ú_cleanup_old_recordingsr6  +  sa   € ðÜ(Ó*Ð-AÑAˆð ×ÑÔÜ˜>Ð+;¸]ÈKÕXð øô ò Ü�
‰
×ÑÐEÀqÔIÜûðús   ‚/ ¯	A"¸ AÁA"c                 ó¾   — t        j                  | «      }|| k(  s"t         j                  j                  |«      | k(  r!t         j                  j	                  |d«       yy)zíDrop stale last-active ownership after cleaning ``task_id``: a bare task always, a
    sidecar only if it was still the recorded owner (a later click must not resurrect a
    cleaned sidecar while a primary-session binding is preserved).N)r%   Ú_bare_task_id_for_session_keyÚ_last_active_session_keyr   ra   )rR   Úbare_task_ids     r.   Ú_drop_last_active_bindingr;  6  sQ   € ô ×4Ñ4°WÓ=€LØ�wÒ¤#×">Ñ">×"BÑ"BÀ<Ó"PÐT[Ò"[Ü×$Ñ$×(Ñ(¨°tÕ<ð #\r4   c                 ó.  — | €d} | g}| › t         j                  › �}t         j                  5  t        j                  | «      s#|t         j                  v r|j                  |«       ddd«       |D ]  }t        |«       Œ t        | «       y# 1 sw Y   Œ'xY w)zžClean up browser session(s) for a task: a bare task id reaps BOTH the primary
    session and any hybrid local sidecar; a ``::local`` key reaps only that one.NÚdefault)r%   Ú_LOCAL_SUFFIXrJ   Ú_is_local_sidecar_keyrE   ÚappendÚ_cleanup_single_browser_sessionr;  )rR   Úsession_keysÚsidecar_keyÚsession_keys       r.   rh   rh   ?  s’   € ð €Øˆà�9€LØ�Iœc×/Ñ/Ð0Ð1€KÜ	×	Ñ	ñ -Ü×(Ñ(¨Ô1°kÄS×EYÑEYÑ6YØ×Ñ Ô,÷-ð $ò 5ˆÜ'¨Õ4ð5ä˜gÕ&÷-ð -ús   ¬9BÂBc                 ó6  ‡‡— t         j                  j                  | ‰› d�«      }t         j                  j                  |«      sy	 t	        t        |«      j                  d¬«      j                  «       «      Št        ‰| ‰«      s"t        j                  j                  d‰‰«       yt        ‰‰ˆˆfd„«      syt        j                  j                  d‰‰«       y# t        t        t        t         f$ r# t        j                  j                  d	‰«       Y yw xY w)
zyTree-kill the daemon in ``<socket_dir>/<session>.pid`` if verifiably ours; True when
    a kill was issued. Never raises.rÉ   Frw   rx   z?Skipped daemon kill for %s: pid %s failed identity verificationc                  óF   •— t         j                  j                  d‰‰«      S )Nz@Skipped daemon kill for %s: no start-time fingerprint for pid %s)r%   r&   r8   )Ú_ar�   rs   s    €€r.   ú<lambda>z'_kill_verified_daemon.<locals>.<lambda>Z  s$   ø€ ÔPS×PZÑPZ×P`ÑP`ØRÐT`ÐblóQn€ r4   zKilled daemon pid %s for %sTz?Could not kill daemon pid for %s (already dead or inaccessible))rz   r{   r|   rÍ   r   r   r·   r!   rª   r%   r&   r8   rÆ   rÐ   r$   rÑ   r€   )rr   rs   rÔ   r�   s    ` @r.   Ú_kill_verified_daemonrI  O  sç   ù€ ô �w‰w�|‰|˜J¨<¨.¸Ð(=Ó>€HÜ�7‰7�>‰>˜(Ô#ØðÜœ˜h›×1Ñ1¸7Ð1ÓC×IÑIÓKÓLˆ
Ü.¨z¸:À|ÔTÜ�J‰J×ÑÐ^Ð`lÐnxÔyØÜ)¨*°lô Enô oàÜ�
‰
×ÑÐ6¸
ÀLÔQØøÜ¤
¬O¼WÐEò Ü�
‰
×ÑÐZÐ\hÔiÙðús   ÁA C Â(C Â:!C Ã9DÄDc                 ó  — |j                  dd«      }t        | d¬«       |r(t        j                  «       }|�	 |j	                  |«       |j                  dd«      }|ryt        j                  j                  t        j                  «       d	|› �«      }t        j                  j                  |«      r$t        ||«       t        j                   |d¬
«       yyy# t
        $ r*}t        j                  j                  d|«       Y d}~Œ¼d}~ww xY w)uº  Untrack ``task_id``, close its cloud provider session, kill its daemon â€” the
    unconditional tail of a teardown, and the whole of the janitor's force-reap path.

    The unconditional tail of ``_cleanup_single_browser_session``; also the whole of the janitor's
    force-reap path (#100738), which skips the polite agent-browser/Camofox ``close`` that kept failing but
    must still release the cloud session and the local Chromium.
    Úbb_session_idÚunknownT)r_   Nz)Could not close cloud browser session: %srs   r•   rÜ   rÊ   )r   rb   Ú_cloudÚ_get_cloud_providerÚclose_sessionr7   r%   r&   r'   rz   r{   r|   rÞ   r4  rI  rÎ   rÏ   )rR   r   rK  Úproviderr:   rs   rr   s          r.   Ú_release_session_resourcesrQ  d  så   € ð !×$Ñ$ _°iÓ@€MÜ˜W¨dÕ3áÜ×-Ñ-Ó/ˆØÐðSØ×&Ñ& }Ô5ð  ×#Ñ# N°BÓ7€LÙÜ—W‘W—\‘\¤#×"9Ñ"9Ó";¸~ÈlÈ^Ð=\Ó]ˆ
Ü�7‰7�>‰>˜*Ô%Ü! *¨lÔ;Ü�M‰M˜*°DÖ9ð &ð øô	 ò SÜ—
‘
×"Ñ"Ð#NÐPQ×RÑRûðSús   ¹C Ã	DÃ! DÄDc                 óf  — t        j                  | «       t        j                  5  t        j                  j                  | «      }t        j                  j                  | d«       t        j                  j                  | «       ddd«       rt        | |«       t        | «       y# 1 sw Y   Œ#xY w)z¡Janitor last resort: skip the failing ``close`` round-trips, release resources directly.

    Janitor last resort after repeated cleanup failures (#100738).
    N)Ú_cdpÚ_stop_cdp_supervisorr%   rJ   rE   r   rL   ra   rO   ÚdiscardrQ  r;  )rR   r   s     r.   rj   rj     s‡   € ô
 	×Ñ˜gÔ&Ü	×	Ñ	ñ 1Ü×+Ñ+×/Ñ/°Ó8ˆÜ×"Ñ"×&Ñ& w°Ô5Ü×Ñ×'Ñ'¨Ô0÷1ñ Ü" 7¨LÔ9Ü˜gÕ&÷1ð 1ús   ¦AB'Â'B0c                 ó.  ‡ — t        j                  ‰ «       t        j                  «       rˆ fd„}t	        d‰ › �|«       t        j
                  j                  d‰ «       t        j
                  j                  dt        t        j                  j                  «       «      «       t        j                  5  t        j                  j                  ‰ «      }ddd«       s!t        j
                  j                  d‰ «       yt        j
                  j                  d‰ |j                  dd	«      «       t        j                  ‰ «       |j                  d
«      xs i j                  d«      r 	 ddlm}  ||j                  dd«      «       nft#        |«      r!t        j
                  j                  d‰ «       n:	 t%        j&                  ‰ dg d¬«       t        j
                  j                  d‰ «       t)        ‰ |«       t        j
                  j                  d‰ «       y# 1 sw Y   �ŒJxY w# t        $ r+}t        j
                  j!                  d‰ |«       Y d}~Œid}~ww xY w# t        $ r+}t        j
                  j!                  d‰ |«       Y d}~Œ d}~ww xY w)z7Reap a single browser session by its exact session key.c                  ó8   •— ddl m} m}  |‰«      s	 | ‰«       y y )Nr   )Úcamofox_closeÚcamofox_soft_cleanup)Útools.browser_camofoxrX  rY  )rX  rY  rR   s     €r.   Ú_camofox_cleanupz9_cleanup_single_browser_session.<locals>._camofox_cleanup•  s   ø€ ßQÙ'¨Ô0Ù˜gÕ&ð 1r4   zCamofox cleanup for task z&cleanup_browser called for task_id: %szActive sessions: %sNz'No active session found for task_id: %sz+Found session for task %s: bb_session_id=%srK  rL  ÚfeaturesÚ
lightpandar   )Ústop_lightpandars   r•   z&lightpanda stop failed for task %s: %sz3Skipping agent-browser close for expired session %sÚcloseé
   rþ   z1agent-browser close command completed for task %sz*agent-browser close failed for task %s: %sz$Removed task %s from active sessions)rS  rT  r%   Ú_is_camofox_moder;   r&   r8   re   rE   ÚkeysrJ   r   Ú_maybe_stop_recordingr?   r^  r7   r'   r3   Ú_sessionÚ_run_browser_commandrQ  )rR   r[  r   r^  r:   s   `    r.   rA  rA  Ž  s	  ø€ ä×Ñ˜gÔ&ô ×ÑÔô	'ô 	Ð0°°	Ð:Ð<LÔMä‡J�J×ÑÐ=¸wÔGÜ‡J�J×ÑÐ*¬D´×1EÑ1E×1JÑ1JÓ1LÓ,MÔNô 
×	Ñ	ñ 9Ü×+Ñ+×/Ñ/°Ó8ˆ÷9ñ Ü�
‰
×ÑÐBÀGÔLØä‡J�J×ÑÐBÀGÈ\×M]ÑM]Ð^mÐoxÓMyÔzÜ×Ñ˜gÔ&ð 	×Ñ˜Ó$Ò*¨×/Ñ/°Ô=ð	UÝ@Ù˜L×,Ñ,¨^¸RÓ@ÕAô 
˜lÔ	+Ü�
‰
×ÑÐNÐPWÕXð	YÜ×)Ñ)¨'°7¸BÈÕKÜ�J‰J×ÑÐPÐRYÔZô ˜w¨Ô5Ü‡J�J×ÑÐ;¸WÕE÷99ñ 9ûô  ò 	UÜ�J‰J×ÑÐGÈÐRS×TÑTûð	Uûô ò 	YÜ�J‰J×ÑÐKÈWÐVW×XÑXûð	Yús<   Â4 HÅ*H) Æ69I  ÈH&È)	IÈ2!IÉIÉ 	JÉ)!JÊJc                  ó   — t         j                  5  t        t         j                  j	                  «       «      } ddd«        D ]  }t        |«       Œ 	 ddlm} |j                  «        t        j                  j                  «        dD ]'  \  }}t        t         |d«       t        t         |d«       Œ) y# 1 sw Y   Œ}xY w# t        $ r Y Œbw xY w)zIClean up all active browser sessions (shutdown) and reset cached lookups.Nr   )ÚSUPERVISOR_REGISTRY))Ú_agent_browser_resolvedÚ_cached_agent_browser)Ú_command_timeout_resolvedÚ_cached_command_timeout)Ú_snapshot_threshold_resolvedÚ_cached_snapshot_threshold)Ú_chromium_autoinstall_attemptedÚ_cached_chromium_installed)Ú_browser_engine_resolvedÚ_cached_browser_engineF)r%   rJ   re   rE   rb  rh   Útools.browser_supervisorrg  Ústop_allr7   Ú_installÚ_discover_homebrew_node_dirsÚcache_clearÚsetattr)Útask_idsrR   rg  ÚflagÚcaches        r.   rH   rH   ¾  sº   € ä	×	Ñ	ñ 5Üœ×,Ñ,×1Ñ1Ó3Ó4ˆ÷5àò !ˆÜ˜Õ ð!ðÝ@Ø×$Ñ$Ô&ô ×)Ñ)×5Ñ5Ô7ðò "‰ˆˆeô 	”�T˜5Ô!Ü”�U˜DÕ!ñ"÷5ð 5ûô ò Ùðús   ‘(B5ÁC Â5B>Ã	CÃC)r   N)r¦   zsubprocess.Popenr   N)é   )éH   r˜   )KÚ__doc__Ú
contextlibrz   rÎ   r  r  r÷   r2   r   r   Úpathlibr   Útypingr   r   r   r	   Úhermes_constantsr
   r   r   Útools.browser_tool_originr   r%   Útoolsr   rS  r   rM  r   rd  r   rt  r   rC   r    r   r/   r   r3   r;   r@   rQ   Úcontextmanagerr]   rb   rq   rƒ   rŽ   r   rª   rµ   r¸   r¾   rÆ   ÚsetrÖ   rP   rñ   rû   r  r  r  r  r»   r*  r0  r6  r;  rh   rI  rQ  rj   rA  rH   rú   r4   r.   ú<module>r†     s  ðñó
 Û 	Û Û Û Û Û ß 'Ý ß -Ó -ç bÑ bÝ 3Ý *Ý .Ý 2Ý 2Ý <ð¨D°°c°©Nð ¸xÈ¹ó ð. =Aò?Ø�s˜C�x‘.ð?Ø*2°5©/ð?à	ó?ð4˜ð 4 Dó 4óò
"IðJ ×Ñð/ #ò /ó ð/ð6 @DÐUZò 1 cð 1¸ð 1Èdð 1Ð_có 1ò%BðP	Y ð 	Y°Cð 	Y¸Dó 	YðK˜sð K¨#ð K°$ó Kð,°ð ,Àð ,Ø25ð,Ø:>ó,ð^*¨ð *°¸%±ó *ð,˜ð  ¨#¡ó ð- ð -°Cð -¸EÀ(È3Á-ÐQYÐZ^ÑQ_ÐB_Ñ<`ó -ð¨3ð ¸cð È4ó ð3 ð 3°Cð 3Èð 3ÐPTó 3òl$^òNò2xòð$M có Mó(ó6ð<$�Sð $˜Tó $ðW $ð W°ð WÀUð WÐSVð WÐ[_ó WóaóYð= sð =¨tó =ñ'˜X c™]ð '°dó 'ð  cð ¸ð Àó ð*:¨ð :¸4ÀÀSÀ¹>ð :Èdó :ð6'¨ð '°ó 'ð-F¨Sð -F°Tó -Fô`"r4   