Ë
    âß²jI ã                   óþ  — U 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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ZddlZddlmZmZ ddlmZ ddlmZ ddlmZmZ  ej<                  e«      Z �d†d„Z!d	d
ddddddœZ" e#e"«      Z$ e#h d£«      Z% e#h d£«      Z& e#ddh«      Z' e#ddh«      Z(ddddœde)de)de*ddddf
d„Z+dZ,d Z-d!Z. ej^                  e.«      Z0de)fd"„Z1e2e3ffd#„Z4d$Z5d%Z6�d‡d&e)dz  de7dz  fd'„Z8de7dz  fd(„Z9d)e7dz  de7fd*„Z:de)dz  fd+„Z;�d‡d,e)d-dde)fd.„Z<�dˆd0e)d1e=de=fd2„Z>de)fd3„Z?d4e)de*fd5„Z@d4e)d6e*de*fd7„ZAd4e)d8e*de)fd9„ZBd:e)de=fd;„ZCde)dz  fd<„ZDd=e)eEe)   z  eFe)d>f   z  eGe)   z  dz  de=fd?„Z	 �d‡ddd@œdAe)dz  d=e)eEe)   z  eFe)d>f   z  eGe)   z  dz  dBe)dz  de)dz  fdC„ZHdDe)fdE„ZIdDe)dz  de7fdF„ZJdGe)de7fdH„ZKde=fdI„ZLdDe)dz  deFe7e7f   fdJ„ZM�d‡dKe)dLe)dz  de=fdM„ZNddlOZOddNlOmPZPmQZQ ddOlRmSZS ddPlTmUZUmVZVmWZWmXZXmYZYmZZZm[Z[m\Z\m]Z] ej¼                  j¿                  d e) eSe`«      jÃ                  «       jÄ                  d!   «      «       ddQlcmdZdmeZe ddRlfmgZg ddSlhmiZimjZjmkZkmlZl ddTlmmnZnmoZompZp ddUlqmrZr ddVlsmtZtmuZu ddWlvmwZw ddXlxmyZymzZzm{Z{ eUrddYl|m}Z} eP G dZ„ d[«      «       Z~eP G d\„ d]«      «       Z�d‡dd^œd_e)dz  d`eYdaeYde)dz  fdb„Z€	 �d‡dd^œdceGe)   d_e)dz  d`eYdaeYde=f
dd„Z�deZ‚dfZƒdgeYde)fdh„Z„�d‰die)dje*de)fdk„Z…dl„ Z† ezdmdn«      Z‡doZˆde*fdp„Z‰dqddrœdse*dte)dueXe*   ddfdv„ZŠdte)de‹fdw„ZŒd,e)dxe)dye)dze)fd{„Z� e�d|d}dmdn«      \  ZŽZ�d~e‹de=fd„Z�d€eSd�e)d0e)d~e‹de)f
d‚„Z‘�dŠd~e‹d0e)de)fdƒ„Z’�dŠd~e‹d0e)de)fd„„Z“die)d0e)d…e*dte)d†e)d‡e)de)fdˆ„Z”�d‹die)d0e)d…e*de)fd‰„Z•d€eSdŠe*de*fd‹„Z– ezdŒd�«      Z— e�dŽd�dŒd�«      \  Z˜Z™�dŒd~e‹d0e)de)fd�„Zš�dŒd~e‹d0e)de)fd‘„Z›�d�die)d0e)d…e*de)fd’„Zœ ezd“d”«      Z� e�d•d–d“d”«      \  ZžZŸd—d˜d™dšd›dœœZ �dŽd~e‹d0e)de)fd�„Z¡�dŽd~e‹d0e)de)fdž„Z¢ ezdŸd «      Z£ ezd¡d¢«      Z¤ e�d£d¤dŸd «      \  Z¥Z¦ e�d¥d¦d¡d¢«      \  Z§Z¨e‡e—e�e£e¤d§œZ© e{«       Zª ey«       Z«d¨Z¬d©Z­dªZ®d«Z¯d¬Z°e‡e—e�e£e¤gd­„ d®D «       ¢d¯„ e°D «       ¢­Z±d°Z²d±Z³d²Z´dGe)deFe)d>f   fd³„Zµd´dµd¶d·d¸d¹dºd»ej¼                  �jm                  d¼d½«      d¾ej¼                  �jm                  d¿dÀ«      ej¼                  �jm                  d¿dÁ«      dÂdÃdÄdÅg eµdÆ«      ¢ eµdÇ«      ¢­Z·deWeS   fdÈ„Z¸deWeS   fdÉ„Z¹deWeS   fdÊ„ZºdeSfdË„Z»deWeS   fdÌ„Z¼deWeS   fdÍ„Z½deWeS   fdÎ„Z¾de¿fdÏ„ZÀdeWeS   fdÐ„ZÁdeWeS   fdÑ„ZÂd/d/dÒœdeSdÓe=dÔe=deXeS   fdÕ„ZÃdÖeSde=fd×„ZÄdÖeSdØe¿de=fdÙ„ZÅdeSdÚeSde=fdÛ„ZÆ�d�dGe)dÝe)de)fdÞ„ZÇdeWe\eSeSf      fdß„ZÈ�d�d_e)deWe)   fdà„ZÉh dá£ZÊde=fdâ„ZËde=fdã„ZÌd_e)däe)deWeS   fdå„ZÍ�d�d_e)deWeS   fdæ„ZÎdeWe\eSeSf      fdç„ZÏdèeSd_e)dée)ddfdê„ZÐ�d�dèeSd_e)deXeS   fdë„ZÑ�d�de)d_e)deXe)   fdì„ZÒ e	�j¦                  dí«      ZÔde)de)fdî„ZÕ	 �d‡d_e)dïe)dðeXeWe7      deXe)   fdñ„ZÖdòe)de=fdó„Z×i dôdõ“död÷“dødù“dúdû“düdù“dýdþ“dÿ�d “�d�d“�d�d“�d�d“�ddù“�ddù“�d�d	“�d
�d“�d�d“�d�d“�d�d“�d�ddùdùdù�dœ¥ZØh �d£ZÙ�d�d�d�d�d�dœZÚ�dZÛe\e)d>f   eÜ�d<   �d�jm                   eÝ�d„ eÛD «       eÞ�d�¬ «      «      Zß�d!Zà e	�j¦                  �d"eßz   �d#z   eàz   �d$z   e	�jÂ                  «      Zâ e	�j¦                  �d%eàz   �d&z   e	�jÂ                  «      Zã�d'e)�d(�d)deXe\e)e*f      f�d*„Zädòe)de)f�d+„Zåde)de=f�d,„Zæ�d-e)de=f�d.„Zç�d/Zè�d-e)de*f�d0„Zé�d-e)de)f�d1„Zê�d-e)deEf�d2„Zë�d3e)f�d4„Zì�d3e)deEf�d5„Zí e	�j¦                  �d6e	�jÜ                  «      Zï e	�j¦                  �d7«      Zð�d8e)deEf�d9„Zñ�d-e)�d:eEde)f�d;„Zò�d-e)�d:eEde)f�d<„Zó�d-e)de)f�d=„Zôd~e‹�d>e)de)f�d?„Zõd~e‹�d>e)de)f�d@„ZöeP G �dA„ �dB«      «       Z÷eØe eÚfD � ��ci c]$  }  eø| �jó                  «       «      D ]  \  }}||“Œ
 Œ& c}}} ZúeVe)e)f   eÜ�dC<   �d>e)�dDe)de)f�dE„ZûdÜdÜd�dFœd~e‹�d>e)�dDe)�dGeXe)   deXe÷   f
�dH„ZüdÜdÜd�dFœd~e‹�d>e)�dDe)�dGeXe)   deXe÷   f
�dI„ZýeP G �dJ„ �dK«      «       Zþ�dLeXe)   �dMeXe)   de)f�dN„ZÿeP G �dO„ �dP«      «       �Z  e	�j¦                  �dQe	�jÂ                  «       e	�j¦                  �dRe	�jÂ                  «       e	�j¦                  �dSe	�jÂ                  «      f�ZeFe	�j                  e)   d>f   eÜ�dT<   �d��dU„�ZeP G �dV„ �dW«      «       �ZeP G �dX„ �dY«      «       �Z�dZ�Z e#h �d[£«      �Z�d\�Z�d]�Z	�d‘�d^eX�e
   �d_e)de)f�d`„�Z�dae)�dbe)de=f�dc„�Z�dd�de„ f�df�dg„ f�dh�di„ f�dj�dk„ f�dl�dm„ f�dn�do„ ff�Ze\e\e)eZe)ge=f   f   d>f   eÜ�dp<   �d��d^eX�e
   �d_e)de)f�dq„�Z�d‘�d^eX�e
   �d_e)de=f�dr„�Z G �ds„ �dte)«      �Zd/�duœ�dveVe)enf   d_e)daen�dwe=ddf
�dx„�Z�dy�ZeZenge[eXe]e)�dtf         f   �Z�d‡�dze7�d{e)�d|e)dz  de)dz  f�d}„�Z	 �d‡�dze7�d{e)�d|e)dz  deEe)   dz  f�d~„�Z�deYde\eXe*   eYf   f�d€„�Z�d�eYdGe)�d‚eZg eYf   deYf�dƒ„�Zeô�Z G �d„„ �d…e«      �Zyc c}}} w (’  zZBase platform adapter interface; every platform adapter inherits from BasePlatformAdapter.é    N)ÚABCÚabstractmethod)Úurlsplit)Únormalize_proxy_url)Úfirst_proxy_env_valueÚshould_bypass_proxyÚreturnc                 ó|   — | j                  «       ry| j                  «       }|�t        j                  d||¬«       yy)z©Done-callback for a detached fatal-error handler task (carrier cancelled in
    ``_notify_fatal_error``): retrieve its exception so asyncio never logs "never retrieved".Nz,Detached fatal-error handler task failed: %s©Úexc_info)Ú	cancelledÚ	exceptionÚloggerÚerror)ÚtaskÚexcs     ú;/home/ubuntu/.hermes/hermes-agent/gateway/platforms/base.pyÚ#_consume_detached_handler_exceptionr      s;   € ð ‡~�~ÔØØ
�.‰.Ó
€CØ
€Ü�‰ÐCÀSÐSVˆÕWð ó    z	audio/oggz
audio/opusz
audio/mpegz	audio/wavz	audio/m4az
audio/flac)ú.oggú.opusú.mp3ú.m2aú.wavú.m4aú.flac>   ú.3gpú.aviú.mkvú.movú.mp4ú.webm>   ú.gifú.jpgú.pngú.jpegú.webpr   r   r   r   Ú32ké<   )ÚbitrateÚtimeoutÚoutput_pathÚpathr*   r+   r,   z
str | Nonec                ó¼  — ddl }|j                  d«      }|sy|€0t        j                  dd¬«      \  }}t	        j
                  |«       n|}t        j                  j                  t        | «      «      t        j                  j                  |«      k(  }|r|dz   n|}		 t        j                  |dd	d
dt        | «      ddddd|dddddddd|	gd|t        j                  ¬«      }
|
j                  dk(  r<t        j                  j                  |	«      dkD  r|rt	        j                  |	|«       |S t        j!                  d| |
j                  |
j"                  xs dj%                  dd¬«      dd  «       t)        j*                  t,        «      5  t	        j.                  |	«       ddd«       y# t&        $ r t        j!                  d!| d¬"«       Y Œ[w xY w# 1 sw Y   yxY w)#u‡  Best-effort ffmpeg transcode to Ogg/Opus (voip-tuned) for native voice bubbles: the written
    ``.ogg`` path (a NEW temp file unless ``output_path`` is given; caller cleans up), or None when
    ffmpeg is missing/fails. ``output_path`` may equal ``path`` (in-place container repair) â€” the
    encode goes through a sidecar so a failed run never truncates the source. Blocking (to_thread).r   NÚffmpegÚvoice_transcode_r   )ÚprefixÚsuffixz.tmp.oggz-vr   z-yz-iz-acodecÚlibopusz-acÚ1z-b:az-vbrÚonz-applicationÚvoipz-compression_levelÚ10z-fÚoggT)Úcapture_outputr+   Ústdinz:ffmpeg Ogg/Opus transcode of %s failed (returncode=%s): %sr   úutf-8Úreplace©Úerrorsiô  z)voice transcode to Ogg/Opus failed for %sr   )ÚshutilÚwhichÚtempfileÚmkstempÚosÚcloser-   ÚabspathÚstrÚ
subprocessÚrunÚDEVNULLÚ
returncodeÚgetsizer<   r   ÚwarningÚstderrÚdecodeÚ	ExceptionÚ
contextlibÚsuppressÚOSErrorÚunlink)r-   r*   r+   r,   Ú_shutilr/   ÚfdÚogg_pathÚin_placeÚ	work_pathÚresults              r   Útranscode_to_ogg_opusrZ   5   s®  € ó Ø�]‰]˜8Ó$€FÙØØÐÜ×'Ñ'Ð/AÈ&ÔQ‰ˆˆHÜ
�‰��àˆÜ�w‰w�‰œs 4›yÓ)¬R¯W©W¯_©_¸XÓ-FÑF€HÙ)1�˜:Ò%°x€IðYÜ—‘Ø�T˜7 D¨$´°D³	Ø˜	 5¨#¨v°wÀÈØ˜VÐ%9¸4ÀÀuÈiðYð  ¨¼
×8JÑ8Jô	Lˆð
 ×Ñ Ò!¤b§g¡g§o¡o°iÓ&@À1Ò&DÙÜ—
‘
˜9 hÔ/ØˆOÜ�‰ÐSÐUYÐ[a×[lÑ[lØŸ™Ò,¨×4Ñ4°WÀYÐ4ÓOÐPTÐQTÐUô	Wô 
×	Ñ	œWÓ	%ñ Ü
�	‰	�)Ô÷àøô	 ò YÜ�‰ÐBÀDÐSWˆÖXðYú÷àús&   ÂBF+ Ä/AF+ ÆGÆ+!GÇGÇGç      >@ç      @é   c                 óV   — t        | d| «      }t        |xs d«      j                  «       S )z=Normalize a Platform enum / raw string into a lowercase name.ÚvalueÚ )ÚgetattrrF   Úlower)Úplatformr_   s     r   Ú_platform_namerd   `   s(   € ä�H˜g xÓ0€EÜˆuŠ{˜Ó×!Ñ!Ó#Ð#r   c                 ó,   — 	  | «       S # |$ r |cY S w xY w)zX``thunk()``, or ``default`` when it raises one of ``exc`` (numeric config/env coercion).© )ÚthunkÚdefaultr   s      r   Ú_or_defaultri   f   s"   € ðÙ‹wˆøØò ØŠðús   ‚	 ‰’gffffffÖ?ç      ð?Úreply_to_message_idc                 ó´  — t        | dd«      }t        t        | dd«      «      }|�d|ini }|dk(  rt        | dd«      nd}|rt        |«      |d<   |sy|dk(  rQt        | dd«      d	k(  rAd
|d<   t        |«      dvrt        |«      |d<   |xs t        | dd«      }|�t        |«      |d<   t        t        | dd«      xs d«      j                  «       }|r||d<   |S )zÜPlatform-aware thread metadata for adapter sends. Telegram DM topics route with
    ``message_thread_id`` + a reply anchor; anchorless synthetic/resumed sends fall back to
    ``direct_messages_topic_id`` when supported.Ú	thread_idNrc   ÚslackÚscope_idÚslack_team_idÚtelegramÚ	chat_typeÚdmTÚ telegram_dm_topic_reply_fallback>   r`   r4   Údirect_messages_topic_idÚ
message_idÚtelegram_reply_to_message_idÚprofiler`   Úhermes_profile)ra   rd   rF   Ústrip)Úsourcerk   rm   rc   Úmetadataro   Úanchorrx   s           r   Ú_thread_metadata_for_sourcer~   r   s  € ô ˜ ¨TÓ2€IÜœg f¨j¸$Ó?Ó@€HØ+4Ð+@�˜YÑ'Àb€Hð 5=ÀÒ4GŒw�v˜z¨4Ô0ÈT€HÙÜ$'¨£Mˆ�Ñ!ÙØØ�:Ò¤'¨&°+¸tÓ"DÈÒ"LØ7;ˆÐ3Ñ4Üˆy‹> Ñ*Ü36°y³>ˆHÐ/Ñ0Ø$ÒK¬°¸ÀdÓ(KˆØÐÜ7:¸6³{ˆHÐ3Ñ4ô ”'˜& )¨TÓ2Ò8°bÓ9×?Ñ?ÓA€GÙØ%,ˆÐ!Ñ"Ø€Or   c                 ó@   — t        | j                  t        | «      «      S )zG``_thread_metadata_for_source`` for an event, anchored on its reply id.)r~   r{   Ú_reply_anchor_for_event©Úevents    r   Ú_thread_metadata_for_eventrƒ   �   s   € ä& u§|¡|Ô5LÈUÓ5SÓTÐTr   r|   c                 ó.   — | rt        | «      ni }d|d<   |S )z>Clone metadata and mark a user-visible reply as notify-worthy.TÚnotify)Údict)r|   Únotify_metadatas     r   Ú_mark_notify_metadatarˆ   ”   s   € á(0”d˜8”n°b€OØ $€O�HÑØÐr   c                 ó°  — t        | dd«      }|�|S t        | dd«      }t        t        |dd«      «      }t        |dd«      }t        | dd«      }|dk(  r"t        |t        «      r|j	                  d«      ry|d	k(  r/|r-t        |d
d«      dk7  ryt        | dd«      xs t        | dd«      S |dk(  r|rt        | dd«      rt        | dd«      S t        | dd«      S )z;Return reply_to id for platforms that need reply semantics.Úreply_anchor_overrideNr{   rc   rm   Úraw_messagern   Ú_hermes_no_thread_responserq   rr   rs   rv   rk   Úfeishu)ra   rd   Ú
isinstancer†   Úget)r‚   Úoverrider{   rc   rm   r‹   s         r   r€   r€   ›   sñ   € ä�uÐ5°tÓ<€HØÐØˆÜ�U˜H dÓ+€FÜœg f¨j¸$Ó?Ó@€HÜ˜ ¨TÓ2€IÜ˜% °Ó5€KØ�GÒ¤
¨;¼Ô =Ø—‘Ð <Ô=ð Ø�:Ò¡)ô �6˜;¨Ó-°Ò5ØÜ�u˜l¨DÓ1Ò`´W¸UÐDYÐ[_Ó5`Ð`Ø�8Ò¡	¬g°eÐ=RÐTXÔ.YÜ�uÐ3°TÓ:Ð:Ü�5˜,¨Ó-Ð-r   ÚkindÚ	file_namec                 ó(   — |rd|› d�nd}d| › d|› d�S )zQUser-facing "couldn't deliver" notice; ``file_name`` is the only name ever shown.ú (ú)r`   u   âš ï¸� Couldn't deliver the z attachmentú.rf   )r‘   r’   r2   s      r   Ú_media_failure_textr—   ´   s)   € á"+ˆr�)�˜AÑ°€FØ)¨$¨¨{¸6¸(À!ÐDÐDr   FÚextÚis_voicec                 ór   — |xs dj                  «       }|t        vryt        | «      dk7  ry|xs |t        v S )ur  True when a media file should use the platform's audio sender. Telegram: explicit
    ``is_voice`` ([[audio_as_voice]]) routes ANY format to the voice sender (adapter transcodes
    non-Opus); otherwise only sendAudio's MP3/M4A qualify â€” a plain Opus/OGG attachment is never
    turned into a voice bubble, everything else â†’ document. Other platforms: any audio ext.r`   Frq   T)rb   Ú_AUDIO_EXTSrd   Ú_TELEGRAM_AUDIO_ATTACHMENT_EXTS)rc   r˜   r™   Únormalized_exts       r   Úshould_send_media_as_audiorž   º   sB   € ð
 ’i˜R×&Ñ&Ó(€NØœ[Ñ(ØÜ�hÓ :Ò-ØØÒH�~Ô)HÐHÐHr   c                 óL  — ddl m} t        | «      |v rdnd}t        j                  j                  t        j                  «       ddt        j                  «       j                  dd › d	|› �«      }t        j                  t        j                  j                  |«      d
¬«       |S )uî  Unique temp output path for gateway auto-TTS: ``.ogg`` for ``OPUS_VOICE_PLATFORMS``
    (the tool's ``_repair_ogg_container`` then guarantees real Opus bytes), else ``.mp3``.
    Platform-awareness lives HERE because ``_clear_session_env`` wipes the TTS tool's
    ``HERMES_SESSION_PLATFORM`` contextvar before the post-handler auto-TTS block runs.

    Platforms whose native voice bubbles require Ogg/Opus (``tools.tts_tool.OPUS_VOICE_PLATFORMS`` â€” the
    single source of truth) get an explicit ``.ogg`` path; the tool's central container repair
    (``_repair_ogg_container``) then guarantees real Ogg/Opus bytes for every provider, including MP3-only
    backends like Edge TTS. Everything else keeps the MP3 default. See #36685, #57049.
    r   )ÚOPUS_VOICE_PLATFORMSr8   Úmp3Úhermes_voiceÚ
tts_reply_Né   r–   T)Úexist_ok)Útools.tts_toolr    rd   rC   r-   ÚjoinrA   Ú
gettempdirÚuuidÚuuid4ÚhexÚmakedirsÚdirname)rc   r    r˜   Ú
audio_paths       r   Úbuild_auto_tts_output_pathr¯   Ç   sƒ   € õ 4Ü! (Ó+Ð/CÑC‰%È€CÜ—‘—‘Ü×ÑÓ˜~°¼D¿J¹J»L×<LÑ<LÈSÈbÐ<QÐ;RÐRSÐTWÐSXÐ/Yó[€Jä‡K�K”—‘—‘ 
Ó+°dÕ;ØÐr   Úsc                 ó<   — t        | j                  d«      «      dz  S )u  UTF-16 code units in *s* â€” Telegram's 4 096 limit counts those, so astral chars
    (emoji, CJK Ext B) cost **two** units although Python's ``len()`` counts one.

    Ported from nearai/ironclaw#2304 which discovered the same discrepancy in Rust's ``chars().count()``.
    z	utf-16-ler]   )ÚlenÚencode)r°   s    r   Ú	utf16_lenr´   Ú   s   € ô ˆq�x‰x˜Ó$Ó%¨Ñ*Ð*r   Úbudgetc                 ó¤   —  || «      |k  rt        | «      S dt        | «      }}||k  r'||z   dz   dz  } || d| «      |k  r|}n|dz
  }||k  rŒ'|S )zNLargest codepoint offset *n* with ``len_fn(s[:n]) <= budget`` (binary search).r   é   r]   N©r²   )r°   rµ   Úlen_fnÚloÚhiÚmids         r   Ú_custom_unit_to_cpr½   ã   sm   € áˆaƒy�FÒÜ�1‹vˆØ”�A“ˆ€BØ
ˆrŠ'Ø�B‰w˜‰{˜qÑ ˆÙ�!�D�S�'‹?˜fÒ$Ø‰Bà�q‘ˆBð ˆr‹'ð €Ir   Úlimitc                 ó*   — | dt        | |t        «       S )uT   Longest prefix of *s* with UTF-16 length â‰¤ *limit*; never splits a surrogate pair.N)r½   r´   )r°   r¾   s     r   Ú_prefix_within_utf16_limitrÀ   ñ   s   € àÐ5Ô   E¬9Ó5Ð6Ð6r   Úhostc                 ó®  — t        j                  t        «      5  t        j                  | «      }t        |dd«      }|j                  xs |xr |j                   cddd«       S # 1 sw Y   nxY w	 t        j                  | dt        j                  t        j                  «      }t        d„ |D «       «      S # t        j                  t        f$ r Y yw xY w)zœTrue if *host* would expose the server beyond loopback (incl. IPv4-mapped
    ::ffff:127.0.0.1); hostnames are resolved and DNS failure fails closed (True).Úipv4_mappedNc              3   óh   K  — | ]*  �^ }}t        j                  |d    «      j                   –— Œ, y­w)r   N)Ú	ipaddressÚ
ip_addressÚis_loopback)Ú.0Ú_Úsockaddrs      r   ú	<genexpr>z(is_network_accessible.<locals>.<genexpr>  s,   è ø€ ÒbÉÈ!ÈX”y×+Ñ+¨H°Q©KÓ8×DÑDÔDÑbùó   ‚02T)rP   rQ   Ú
ValueErrorrÅ   rÆ   ra   rÇ   Ú_socketÚgetaddrinfoÚ	AF_UNSPECÚSOCK_STREAMÚanyÚgaierrorrR   )rÁ   ÚaddrÚmappedÚresolveds       r   Úis_network_accessibler×   ö   s»   € ô 
×	Ñ	œZÓ	(ñ IÜ×#Ñ# DÓ)ˆä˜˜}¨dÓ3ˆØ×$Ñ$ÒG¨Ò)F°F×4FÑ4FÐH÷	I÷ Iò Iúð
Ü×&Ñ& t¨T´7×3DÑ3DÄg×FYÑFYÓZˆäÑbÐYaÔbÓbÐbøÜ×ÑœgÐ&ò Ùðús   šAA%Á%A.Á2AB8 Â8CÃCc                  óì  — t         j                  dk7  ry	 t        j                  ddgddddt        j                  ¬	«      } d
„ | j                  «       D «       D ���ci c]'  \  }}}|r|j                  «       |j                  «       “Œ) }}}}dD ]N  \  }}}|j                  |«      dk(  sŒ|j                  |«      sŒ.|j                  |«      sŒ@d||   › d||   › �c S  y# t
        $ r Y yw xY wc c}}}w )z¥Read the macOS system HTTP(S) proxy via ``scutil --proxy``: ``http://host:port``
    when an HTTP(S) proxy is enabled, else None (non-macOS or any subprocess error).ÚdarwinNÚscutilz--proxyé   Tr;   r<   )r+   ÚtextÚencodingr>   rM   c              3   óZ   K  — | ]#  }|j                  «       j                  d «      –— Œ% y­w)z : N)rz   Ú	partition)rÈ   Úlines     r   rË   z-_detect_macos_system_proxy.<locals>.<genexpr>  s!   è ø€ ÒYÀ˜dŸj™j›l×4Ñ4°U×;ÑYùs   ‚)+))ÚHTTPSEnableÚ
HTTPSProxyÚ	HTTPSPort)Ú
HTTPEnableÚ	HTTPProxyÚHTTPPortr4   zhttp://ú:)	Úsysrc   rG   Úcheck_outputrI   rO   Ú
splitlinesrz   r�   )ÚoutÚkeyÚsepÚvalÚpropsÚ
enable_keyÚhost_keyÚport_keys           r   Ú_detect_macos_system_proxyró     s  € ô ‡|�|�xÒØðÜ×%Ñ% x°Ð&;ÀQÈTÐ\cØ-6¼z×?QÑ?QôSˆñ ZÈÏÉÓHXÔY÷bð báˆC��cÑ]`ð 	�	‰	‹�S—Y‘Y“[Ñ ðb€Eò bð+]ò AÑ&ˆ
�H˜hà�9‰9�ZÓ  CÓ'¨E¯I©I°hÕ,?ÀEÇIÁIÈhÕDWØ˜U 8™_Ð-¨Q¨u°X©Ð.?Ð@Ò@ðAð øô ò Ùðüôbs   –+C  Á,C/Ã 	C,Ã+C,Útarget_hosts.c                 ó   — t        | «      S )z¨True when NO_PROXY/no_proxy matches at least one target host (exact hosts, domain /
    wildcard suffixes, IP literals, CIDR ranges, optional host:port entries, ``*``).©Ú_should_bypass_proxy)rô   s    r   r   r     s   € ô   Ó-Ð-r   )rô   Ú
configuredÚplatform_env_varrø   c                ó  — ddl m} | r || d«      xs dj                  «       nd}|st        |xs d«      j                  «       }|st	        «       syt        «       }t        |xs
 t        «       «      }|rt        |«      rdS |S )uñ  Proxy URL: *platform_env_var* (e.g. ``DISCORD_PROXY``) first, then the adapter's own YAML
    value *configured* (``telegram.proxy_url``), then HTTPS_PROXY / HTTP_PROXY / ALL_PROXY (any
    case), then the macOS system proxy â€” the latter two only when ``gateway.trust_env`` is true.
    None when nothing is found or NO_PROXY matches a target.

    *platform_env_var* is a per-adapter, per-profile-configurable setting (each proxy URL can
    embed credentials, e.g. ``http://user:pass@host``) so it is read scope-aware: under a
    secondary multiplex profile it comes from that profile's own ``.env``, not the shared
    process env another profile's ``TELEGRAM_PROXY``/``DISCORD_PROXY``/etc. may hold; the YAML
    value is the same profile's, so a secondary keeps its configured route without any env
    bridge (#108440). The generic ``HTTPS_PROXY``/``HTTP_PROXY``/``ALL_PROXY`` fallback stays a raw
    process-env read â€” those are OS/system-level network settings, not a per-profile Hermes concept.r   )Úget_scoped_secretr`   N)	Úgateway.platforms._sharedrû   rz   rF   Úgateway_trust_envr   r   ró   r   )rù   rô   rø   Ú_get_scoped_proxy_varr_   Úproxys         r   Úresolve_proxy_urlr   !  s€   € õ  UÙK[Ñ"Ð#3°RÓ8Ò>¸B×EÑEÔGÐac€EÙÜ�JÒ$ "Ó%×+Ñ+Ó-ˆÙÜ Ô"ØÜ%Ó'ˆÜ Ò EÔ)CÓ)EÓF€EÙÔ0°Ô>ˆ4ÐIÀEÐIr   Ú	proxy_urlc                 ó¾   — 	 ddl m} |j                  | d¬«      S # t        $ r8 | j	                  «       j                  d«      rt        j                  d| «       Y yw xY w)u  ``aiohttp_socks.ProxyConnector`` for ``proxy_url``, or None when aiohttp_socks is missing
    (SOCKS logs a warning; HTTP callers fall back to ``proxy=``). ``rdns=True`` forces remote DNS
    through the proxy â€” required by Shadowrocket/Clash-style SOCKS and against GFW DNS pollution.r   )ÚProxyConnectorT)ÚrdnsÚsocksuV   aiohttp_socks not installed â€” SOCKS proxy %s ignored. Run: pip install aiohttp-socksN)Úaiohttp_socksr  Úfrom_urlÚImportErrorrb   Ú
startswithr   rL   )r  r  s     r   Ú_aiohttp_socks_connectorr
  =  s\   € ðÝ0Ø×&Ñ& y°tÐ&Ó<Ð<øÜò Ø�?‰?Ó×'Ñ'¨Ô0Ü�N‰Nð <Ø=FôHáð	ús   ‚ ›>AÁAc                 óv   — | si S | j                  «       j                  d«      rt        | «      }|�d|iS i S d| iS )u„   Kwargs for ``commands.Bot()`` / ``discord.Client()``: SOCKS â†’ ``{"connector"}``,
    HTTP â†’ ``{"proxy": url}``, None â†’ ``{}``.r  Ú	connectorrÿ   )rb   r	  r
  ©r  r  s     r   Úproxy_kwargs_for_botr  K  sK   € ñ Øˆ	Ø‡�Ó×#Ñ# GÔ,Ü,¨YÓ7ˆ	Ø+4Ð+@�˜YÐ'ÐHÀbÐHØ�YÐÐr   Únamec                 ó®   — 	 ddl m}  |«       }t        |t        «      r|j                  | «      nd}t        |t        «      r|S i S # t        $ r i cY S w xY w)zVRead-only ``config.yaml`` section ``name``; ``{}`` when unreadable/missing/not a dict.r   )Úload_config_readonlyN)Úhermes_cli.configr  rO   rŽ   r†   r�   )r  Ú_load_configÚcfgÚsections       r   Ú_config_sectionr  V  sU   € ðÝJÙ‹nˆô  *¨#¬tÔ4ˆc�g‰g�dŒm¸$€GÜ  ¬$Ô/ˆ7Ð7°RÐ7øô ò ØŠ	ðús   ‚A ÁAÁAc                  ó¶   — t        d«      j                  dd«      } t        | t        «      r | j	                  «       j                  «       dvS | �t        | «      S dS )zð``gateway.trust_env`` from config.yaml (default True): whether gateway
    ``aiohttp.ClientSession``s honor HTTP(S)_PROXY / NO_PROXY / SSL_CERT_FILE. Set false
    when the gateway inherits a proxy env it must not use. Fail-open to default.ÚgatewayÚ	trust_envT>   Ú0ÚnoÚoffÚfalse)r  r�   rŽ   rF   rz   rb   Úbool©r_   s    r   rý   rý   a  sT   € ô ˜IÓ&×*Ñ*¨;¸Ó=€EÜ�%œÔØ�{‰{‹}×"Ñ"Ó$Ð,GÐGÐGØÐ+Œ4�‹;Ð5°Ð5r   c                 ó†   — | si i fS t        | «      }|�d|ii fS | j                  «       j                  d«      ri i fS i d| ifS )a  ``(session_kwargs, request_kwargs)`` for a standalone ``aiohttp.ClientSession``. With
    aiohttp-socks every scheme uses a connector (mautrix-style libs never forward per-request
    ``proxy=``); without it HTTP falls back to ``({}, {"proxy": url})`` and SOCKS is ignored.r  r  rÿ   )r
  rb   r	  r  s     r   Úproxy_kwargs_for_aiohttpr!  k  s^   € ñ Ø�2ˆvˆÜ(¨Ó3€IØÐØ˜YÐ'¨Ð+Ð+Ø —‘Ó(×3Ñ3°GÔ<ˆB�ˆ8Ð\À2ÈÐQZÐG[ÐB\Ð\r   ÚhostnameÚno_proxy_valuec                 ó   — t        | |¬«      S )z›Return True when ``hostname`` matches a ``NO_PROXY`` entry (``no_proxy_value`` overrides the
    environment); same matcher as :func:`should_bypass_proxy`.)r#  rö   )r"  r#  s     r   Úis_host_excluded_by_no_proxyr%  w  s   € ô   ¸ÔHÐHr   )Ú	dataclassÚfield©ÚPath)	ÚTYPE_CHECKINGÚDictÚListÚOptionalÚAnyÚCallableÚ	AwaitableÚTupleÚUnion)ÚPlatformÚPlatformConfig)Úfence_state_after)ÚEA_HEADER_TEXTÚEA_REASON_LABEL_TEXTÚapproval_timeout_secondsÚformat_approval_deadline_line)ÚMessageEventÚMessageTypeÚProcessingOutcome)Údiagnostic_wake_muted)ÚSessionSourceÚbuild_session_key)ÚTranscriptReadError)Úget_default_hermes_rootÚget_hermes_dirÚget_hermes_home)ÚToolPreviewc                   ó<   — e Zd ZU dZdZeed<   dZeed<   dZeed<   y)	ÚAudioFormatzžDeclared PCM format for a streaming-TTS session: every ``write_streaming_tts``
    chunk must be raw little-endian PCM at this rate / channels / sample width.iÀ]  Úsample_rater·   Úchannelsr]   Úsample_widthN)	Ú__name__Ú
__module__Ú__qualname__Ú__doc__rG  ÚintÚ__annotations__rH  rI  rf   r   r   rF  rF  ‘  s&   … ñSà€K�ÓØ€HˆcÓØ€L�#Ôr   rF  c                   óX   — e Zd ZU dZdZeed<    ee¬«      Z	eed<   dZ
eed<   dZeed<   y	)
ÚStreamingTTSHandlez›Opaque handle returned by ``begin_streaming_tts``; adapters may extend it with
    platform state. The base fields are consumer bookkeeping / cancellation.r`   Úchat_id)Údefault_factoryÚaudio_formatFÚaudibleÚabortedN)rJ  rK  rL  rM  rR  rF   rO  r'  rF  rT  rU  r  rV  rf   r   r   rQ  rQ  œ  s7   … ñPà€GˆSÓÙ %°kÔ B€L�+ÓBð €GˆTÓØ€GˆTÔr   rQ  r�   Úsession_keyÚturn_markerr‚   c                ó^   — | sy|€|�t        |dd«      xs t        |dd«      }|€dS | › d|› �S )uñ   Per-turn streaming-TTS suppression key â€” turn-scoped (not chat-scoped) so
    overlapping turns in one chat can't suppress each other's fallback paths.
    ``turn_marker`` is normally the run generation, else the event's message/update id.Nrv   Úplatform_update_idrç   )ra   )rW  rX  r‚   s      r   Ústreaming_tts_turn_keyr[  ¨  sN   € ñ ØØÐ˜uÐ0Ü˜e \°4Ó8Òf¼GÀEÐK_ÐaeÓ<fˆØÐ&ˆ4ÐJ¨{¨m¸1¸[¸MÐ,JÐJr   Úcompleted_turnsc                ó@   — t        |||¬«      }t        |xr || v «      S )zUPure, turn-scoped auto-TTS suppression decision (testable without the adapter stack).r�   )r[  r  )r\  rW  rX  r‚   Úturn_keys        r   Ú$streaming_tts_should_skip_whole_filer_  ³  s'   € ô & k°;ÀeÔL€HÜ�Ò8˜X¨Ð8Ó9Ð9r   z�Secure secret entry is not supported over messaging. Load this skill in the local CLI to be prompted, or add the key to ~/.hermes/.env manually.z­This bot is private and you're not on its allowed list. If you own it, run `hermes pairing approve {platform} <request-id>` on the host (`hermes pairing list` shows the id).rc   c                 ób   — t        | d| «      }t        j                  t        |xs d«      ¬«      S )zL``UNAUTHORIZED_ACTION_NOTICE`` for a ``Platform`` member or its string name.r_   z
<platform>)rc   )ra   ÚUNAUTHORIZED_ACTION_NOTICEÚformatrF   )rc   r  s     r   Úunauthorized_action_noticerc  Ç  s.   € ä�8˜W hÓ/€DÜ%×,Ñ,´c¸$Ò:NÀ,Ó6OÐ,ÓPÐPr   ÚurlÚmax_lenc                 ó¶  — |dkD  r| �t        | «      nd}|sy	 t        |«      }|}|j                  rp|j                  rd|j
                  xs d}|j                  dd«      d   }|dv rdn|rd|› �nd	}|j                  › d
|j                  j                  dd«      d   › |› �}t        |«      |k  r|S |dk  rd|z  S |d|dz
   › d�S # t        $ r |d| cY S w xY w)z?Return a URL string safe for logs (no query/fragment/userinfo).r   Nr`   ú/r·   éÿÿÿÿ)r`   rg  z/.../z/...z://ú@rÛ   r–   ú...)rF   r   rO   ÚschemeÚnetlocr-   Úrsplitr²   )rd  re  ÚrawÚparsedÚsafer-   ÚbasenameÚtails           r   Úsafe_url_for_logrs  Í  s  € à ’k c oŒ#ˆcŒ(¸2€CÙØðÜ˜#“ˆð €DØ‡}‚}˜Ÿšà�{‰{Ò ˜bˆØ—;‘;˜s AÓ& rÑ*ˆØ˜YÑ&‰rÁ(¨e°H°:Ñ,>ÐPVˆØ—-‘-�  F§M¡M×$8Ñ$8¸¸aÓ$@ÀÑ$DÐ#EÀdÀVÐLˆÜ
ˆ4ƒy�GÒØˆØ# qšLˆ3�‰=ÐH°°l°wÀ±{Ð1CÐ0DÀCÐ.HÐHøô ò Ø�8�Gˆ}Òðús   ™C ÃCÃCc              ƒ   óp   K  — ddl m}m}  || «      }|r  ||«      st        dt	        |«      › �«      ‚yy­w)z²Re-validate each redirect target (a public URL 302-ing to http://169.254.169.254/ would
    bypass the pre-flight is_safe_url()). Async because httpx awaits response event hooks.r   )Úis_safe_urlÚredirect_target_from_responsez.Blocked redirect to private/internal address: N)Útools.url_safetyru  rv  rÍ   rs  )Úresponseru  rv  Úredirect_urls       r   Ú_ssrf_redirect_guardrz  â  sB   è ø€ ÷ LÙ0°Ó:€LÙ™K¨Ô5ÜÐIÔJZÐ[gÓJhÐIiÐjÓkÐkð 6€|ùs   ‚46zcache/imagesÚimage_cachei   c                  óD   — t        d„ t        t        t        t        f«      S )u¡   Max inbound media bytes held in memory (``gateway.max_inbound_media_bytes``);
    ``0`` / negative / unparseable disables the cap; unreadable config â†’ default.c                  ó0   — t        t        d«      d   «      S )Nr  Úmax_inbound_media_bytes)rN  r  rf   r   r   ú<lambda>z-get_inbound_media_max_bytes.<locals>.<lambda>  s   € œs¤?°9Ó#=Ð>WÑ#XÓY€ r   )ri   ÚDEFAULT_INBOUND_MEDIA_MAX_BYTESÚKeyErrorÚ	TypeErrorrÍ   rf   r   r   Úget_inbound_media_max_bytesrƒ  þ  s"   € ô ÑYÜ6¼Ä9ÌjÐ8Yó[ð [r   Úmedia©Ú
media_typeÚ	max_bytesÚsizer†  r‡  c          	      óZ   — |€
t        «       n|}|r| |kD  rt        d|› d| › d|› d�«      ‚yy)z­Raise ``ValueError`` if an inbound payload exceeds the cap (``max_bytes`` of ``0``
    disables it; pass it explicitly to resolve the limit once across an incremental read).NzInbound z payload is too large (z	 bytes > z bytes))rƒ  rÍ   )rˆ  r†  r‡  r¾   s       r   Úvalidate_inbound_media_sizerŠ    sH   € ð .7Ð->Ô'Ô)ÀI€EÙ�˜’Ü˜8 J <Ð/FÀtÀfÈIÐV[ÐU\Ð\cÐdÓeÐeð €ur   c             ƒ   ó–  K  — t        «       }| j                  j                  d«      }|r	 t        |«      }t	        |||¬«       g }d}| j                  «       2 3 d{  –—† }|t        |«      z  }t	        |||¬«       |j                  |«       Œ7# t
        $ r t        j                  d||«       Y Œmw xY w7 ŒX6 dj                  |«      S ­w)z¹Read an httpx streaming body under the media cap: reject an oversized ``Content-Length``
    early, then re-check the running total per chunk (a lying/absent header can't smuggle more).zcontent-lengthr…  z2Ignoring invalid Content-Length for inbound %s: %rr   Nr   )rƒ  Úheadersr�   rN  rŠ  rÍ   r   ÚdebugÚaiter_bytesr²   Úappendr§   )rx  r†  r‡  Úcontent_lengthÚdeclared_sizeÚchunksÚtotalÚchunks           r   Ú_read_httpx_body_with_limitr•    sÐ   è ø€ ô ,Ó-€IØ×%Ñ%×)Ñ)Ð*:Ó;€NÙð	cÜ Ó/ˆMô (¨À*ÐXaÕbØ€FØ€EØ×+Ñ+Ó-÷ ð ˆeØ”�U“ÑˆÜ# E°jÈIÕVØ�‰�eÕøô ò 	kÜ�L‰LÐMÈzÐ[iÖjð	kúðøÐ-ð �8‰8�FÓÐùsK   ‚(C	«B ¶"C	ÁB7ÁB5ÁB7Á /C	Â B2Â/C	Â1B2Â2C	Â5B7Â7C	Úconstant_nameÚnew_subpathÚold_namec                 óœ   ‡‡‡‡— dt         fˆˆˆfd„Šd	dt        dt        fˆfd„}d| › d�x‰_        ‰_        d| › d�x|_        |_        ‰|fS )
a7  ``(get_<kind>_cache_dir, cleanup_<kind>_cache)`` pair. The getter resolves fresh via
    get_hermes_dir (active profile) unless a test monkeypatched the module constant away from
    its import-time default, and creates the directory; ``cleanup(max_age_hours=24)`` deletes
    older files and returns the count.r	   c                  óÊ   •— t        ‰‰«      } t        «       j                  ‰«      }t        j                  ‰«      }|�|�||k7  rt	        |«      } | j                  dd¬«       | S )NT)Úparentsr¥   )rB  Úglobalsr�   Ú_CACHE_DIR_IMPORT_DEFAULTSr)  Úmkdir)ÚdÚcurrentrh   r–  r—  r˜  s      €€€r   Úget_dirz%_cache_dir_accessors.<locals>.get_dir(  s_   ø€ Ü˜;¨Ó1ˆÜ“)—-‘- Ó.ˆÜ,×0Ñ0°Ó?ˆØÐ 7Ð#6¸7ÀgÒ;MÜ�W“ˆAØ	�‰˜ tˆÔ,Øˆr   Úmax_age_hoursc                 ó&   •— t         ‰«       | «      S ©N)Ú_cleanup_cache_dir)r¢  r¡  s    €r   Úcleanupz%_cache_dir_accessors.<locals>.cleanup1  s   ø€ Ü!¡'£)¨]Ó;Ð;r   Úget_Ú
_cache_dirÚcleanup_Ú_cache)é   )r)  rN  rJ  rL  )r‘   r–  r—  r˜  r¦  r¡  s    ``` @r   Ú_cache_dir_accessorsr¬  #  s_   û€ ð
”T÷ ñ<œsð <¬Cõ <à04°T°F¸*Ð.EÐE€GÔ�wÔ+Ø08¸¸¸fÐ.EÐE€GÔ�wÔ+Ø�GÐÐr   ÚimageÚIMAGE_CACHE_DIRÚdatac                 ó´   — t        | «      dk\  xrI | dd dk(  xs? | dd dk(  xs5 | dd dv xs, | dd	 d
k(  xs" | dd dk(  xr t        | «      dk\  xr | dd dk(  S )zDReturn True if *data* starts with a known image magic-byte sequence.é   Né   s   ‰PNG

rÛ   s   ÿØÿé   >   ó   GIF87aó   GIF89ar]   s   BMs   RIFFr¤   s   WEBPr¸   )r¯  s    r   Ú_looks_like_imager¶  <  s”   € äˆt‹9˜‰>ò W˜t B Q˜xÐ+?Ñ?ò  VÀ4ÈÈÀ8ÈÑC^ò  VØ�r˜�(Ð4Ð4ò VØ8<¸R¸a¸ÀEÑ8Iò Và˜˜�8˜wÑ&ÒT¬3¨t«9¸©?ÒT¸tÀAÀb¸zÈWÑ?TðWr   Ú	cache_dirr1   c                 ó�   — | |› dt        j                  «       j                  dd › |› �z  }|j                  |«       t	        |«      S )zQWrite ``data`` to ``<cache_dir>/<prefix>_<uuid12><ext>``; return the path string.rÉ   Nr¤   )r©   rª   r«   Úwrite_bytesrF   )r·  r1   r˜   r¯  Úfilepaths        r   Ú_write_cache_filer»  C  sG   € à˜f˜X Q¤t§z¡z£|×'7Ñ'7¸¸Ð'<Ð&=¸c¸UÐCÑC€HØ×Ñ˜ÔÜˆx‹=Ðr   c                 óÀ   — t        t        | «      d¬«       t        | «      s(| dd j                  dd¬«      }t	        d|› d	|›d
�«      ‚t        t        «       d|| «      S )z”Save raw image bytes to the cache and return the absolute path; raises
    ValueError when *data* isn't an image (e.g. an upstream HTML error page).r­  ©r†  NéP   r;   r<   r=   z$Refusing to cache non-image data as z (starts with: r•   Úimg)rŠ  r²   r¶  rN   rÍ   r»  Úget_image_cache_dir)r¯  r˜   Úsnippets      r   Úcache_image_from_bytesrÂ  J  sh   € ô  ¤ D£	°gÕ>Ü˜TÔ"Ø�s˜�)×"Ñ" 7°9Ð"Ó=ˆÜÐ?À¸uÀOÐT[ÐS^Ð^_Ð`ÓaÐaÜÔ0Ó2°E¸3ÀÓEÐEr   c              ƒ   óT   K  — t        j                  t        | |«      ƒ d{  –—† S 7 Œ­w)z;Cache image bytes without blocking the caller's event loop.N)ÚasyncioÚ	to_threadrÂ  ©r¯  r˜   s     r   Úcache_image_from_bytes_asyncrÇ  T  ó"   è ø€ ä×"Ñ"Ô#9¸4ÀÓE×EÐEÐEúó   ‚(¡&¢(ÚretriesÚacceptÚ	log_labelc             ƒ   ó¸  K  — ddl m}m} ddl}	 || «      st	        dt        | «      › �«      ‚d|dœ}
 |ddd	t        gi¬
«      4 ƒd{  –—† }t        |dz   «      D ]†  }	 |j                  d| |
¬«      4 ƒd{  –—† }|j                  «        t        ||¬«      ƒ d{  –—† }ddd«      ƒd{  –—†  t        j                  ||«      ƒ d{  –—† c cddd«      ƒd{  –—†  S  ddd«      ƒd{  –—†  y7 Œ¬7 Œ7 Œ\7 ŒN# 1 ƒd{  –—†7  sw Y   Œ^xY w7 ŒF7 Œ6# |	j                  |	j                  f$ rŽ}t        ||	j                  «      r|j                   j"                  dk  r‚ ||k  rSd|dz   z  }t$        j'                  d||dz   |t        | «      ||«       t        j(                  |«      ƒ d{  –—†7   Y d}~�Œ^‚ d}~ww xY w7 ŒÒ# 1 ƒd{  –—†7  sw Y   yxY w­w)z¬Shared downloader behind ``cache_*_from_url``: SSRF-checked (pre-flight + per-redirect;
    raises ValueError), size-capped, linear-backoff retries on timeouts / 429 / 5xx.r   )Úcreate_ssrf_safe_async_clientru  Nz&Blocked unsafe URL (SSRF protection): z)Mozilla/5.0 (compatible; HermesAgent/1.0))z
User-AgentÚAcceptr[   Trx  )r+   Úfollow_redirectsÚevent_hooksr·   ÚGET)rŒ  r½  i­  ç      ø?z'%s cache retry %d/%d for %s (%.1fs): %s)rw  rÎ  ru  ÚhttpxrÍ   rs  rz  ÚrangeÚstreamÚraise_for_statusr•  rÄ  rÅ  ÚTimeoutExceptionÚHTTPStatusErrorrŽ   rx  Ústatus_coder   r�  Úsleep)rd  r˜   rÊ  r†  rË  Úcache_fnrÌ  rÎ  ru  rÔ  rŒ  ÚclientÚattemptrx  Úcontentr   Úwaits                    r   Ú_cache_media_from_urlrá  Y  sæ  è ø€ ÷ LÛÙ�sÔÜÐAÔBRÐSVÓBWÐAXÐYÓZÐZØHÐTZÑ[€GÙ,Ø t¸*ÔG[ÐF\Ð9]ô÷ ð à	Ü˜W q™[Ó)ò 	ˆGðØ!Ÿ=™=¨°¸W˜=ÓE÷ að aÈØ×-Ñ-Ô/Ü$?ÀÐU_Ô$`×`�G÷a÷ aô %×.Ñ.¨x¸À#ÓF×FÑF÷÷ ñ ð	÷÷ ñ øð
aøà`øðaø÷ a÷ añ aúð Gøðùð ×*Ñ*¨E×,AÑ,AÐBò 	Ü˜c 5×#8Ñ#8Ô9¸c¿l¹l×>VÑ>VÐY\Ò>\ØØ˜WÒ$Ø '¨A¡+Ñ.�DÜ—L‘LÐ!JÈIÐW^ÐabÑWbØ!(Ô*:¸3Ó*?ÀÀsôLä!Ÿ-™-¨Ó-×-Ñ-ÝØûð	úðø÷ ÷ ñ üs	  ‚AGÁ	C6Á
GÁGÁ DÁ8C8
Á9DÁ<!C>ÂC:
ÂC>Â"DÂ-C<Â.DÃDÃDÃGÃGÃDÃGÃ$GÃ%GÃ0GÃ1GÃ8DÃ:C>Ã<DÃ>DÄDÄDÄDÄGÄG Ä2A<F;Æ.F1
Æ/F;Æ4GÆ:F;Æ;G Ç GÇGÇGÇGÇGÇGc           	   ƒ   óJ   K  — t        | ||ddt        d¬«      ƒ d{  –—† S 7 Œ­w)zEDownload an image URL into the image cache; return the absolute path.r­  zimage/*,*/*;q=0.8ÚMedia©r†  rË  rÜ  rÌ  N)rá  rÂ  ©rd  r˜   rÊ  s      r   Úcache_image_from_urlræ  w  ó.   è ø€ ä&ØˆS�' gÐ6IÜ'°7ô<÷ <ð <ð <úó   ‚#œ!�#r¢  c                 óL  — t        j                   «       |dz  z
  }d}| j                  «       D ]h  }|j                  «       sŒ|j                  «       j                  |k  sŒ2t        j                  t        «      5  |j                  «        |dz  }ddd«       Œj |S # 1 sw Y   ŒvxY w)zQDelete files in *cache_dir* older than *max_age_hours*; return the count removed.i  r   r·   N)	ÚtimeÚiterdirÚis_fileÚstatÚst_mtimerP   rQ   rR   rS   )r·  r¢  ÚcutoffÚremovedÚfs        r   r¥  r¥  ~  s‘   € ä�Y‰Y‹[˜M¨DÑ0Ñ1€FØ€GØ×ÑÓ ò ˆØ�9‰9�;˜1Ÿ6™6›8×,Ñ,¨vÓ5Ü×$Ñ$¤WÓ-ñ Ø—‘”
Ø˜1‘�÷ð ðð
 €N÷ð ús   Á8BÂB#	zcache/audioÚaudio_cacheÚaudioÚAUDIO_CACHE_DIRc                 ót   — ddl m} t        t        | «      d¬«       t	        t        «       d || |«      | «      S )zKSave raw audio bytes to the cache (container-sniffed ext); return the path.r   )Úsniff_audio_extró  r½  )Útools.audio_containerrö  rŠ  r²   r»  Úget_audio_cache_dir)r¯  r˜   rö  s      r   Úcache_audio_from_bytesrù  �  s3   € õ 6Ü¤ D£	°gÕ>ÜÔ0Ó2°G¹_ÈTÐSVÓ=WÐY]Ó^Ð^r   c              ƒ   óT   K  — t        j                  t        | |«      ƒ d{  –—† S 7 Œ­w)z;Cache audio bytes without blocking the caller's event loop.N)rÄ  rÅ  rù  rÆ  s     r   Úcache_audio_from_bytes_asyncrû  ˜  rÈ  rÉ  c           	   ƒ   óJ   K  — t        | ||ddt        d¬«      ƒ d{  –—† S 7 Œ­w)zEDownload an audio URL into the audio cache; return the absolute path.ró  zaudio/*,*/*;q=0.8ÚAudiorä  N)rá  rù  rå  s      r   Úcache_audio_from_urlrþ  �  rç  rè  zcache/videosÚvideo_cacheÚvideoÚVIDEO_CACHE_DIRz	video/mp4zvideo/quicktimez
video/webmzvideo/x-matroskazvideo/x-msvideo)r!   r    r"   r   r   c                 óZ   — t        t        | «      d¬«       t        t        «       d|| «      S )zDSave raw video bytes to the cache and return the absolute file path.r   r½  )rŠ  r²   r»  Úget_video_cache_dirrÆ  s     r   Úcache_video_from_bytesr  ®  s%   € ä¤ D£	°gÕ>ÜÔ0Ó2°G¸SÀ$ÓGÐGr   c              ƒ   óT   K  — t        j                  t        | |«      ƒ d{  –—† S 7 Œ­w)z;Cache video bytes without blocking the caller's event loop.N)rÄ  rÅ  r  rÆ  s     r   Úcache_video_from_bytes_asyncr  ´  rÈ  rÉ  zcache/documentsÚdocument_cachezcache/screenshotsÚbrowser_screenshotsÚdocumentÚDOCUMENT_CACHE_DIRÚ
screenshotÚSCREENSHOT_CACHE_DIR)r®  rô  r  r
  r  ÚHERMES_MEDIA_ALLOW_DIRSÚHERMES_MEDIA_TRUST_RECENT_FILESÚ!HERMES_MEDIA_TRUST_RECENT_SECONDSÚHERMES_MEDIA_DELIVERY_STRICT)Úimagesró  ÚvideosÚ	documentsÚscreenshotsc              #   ó.   K  — | ]  }t         |z  –— Œ y ­wr¤  ©Ú_HERMES_HOME©rÈ   rŸ  s     r   rË   rË   Õ  s   è ø€ ò _˜1„l�QÕñ _ùs   ‚)r{  rò  rÿ  r  r  c              #   ó4   K  — | ]  }t         d z  |z  –— Œ y­w)ÚcacheNr  r  s     r   rË   rË   Ø  s   è ø€ ÒH Q„l�WÑ˜qÕ ÑHùs   ‚iX  )	z/etcz/procz/sysz/devú/rootz/bootz/var/logz/var/libz/var/run)	z.sshz.awsz.gnupgz.kubez.dockerz.configz.azurez.gcloudzLibrary/Keychainsc                 ó    — | | › d�| › d�| › d�fS )z:A SQLite store plus its WAL/SHM/rollback-journal sidecars.z-walz-shmz-journalrf   )r  s    r   Ú_sqlite_filesr  è  s$   € à�T�F˜$�- D 6¨ °4°&¸Ð0AÐBÐBr   ú.envz	auth.jsonz	auth.lockÚcredentialszconfig.yamlz.anthropic_oauth.jsonzgoogle_token.jsonzgoogle_oauth_pending.jsonÚauthzgoogle_oauth.jsonzwebhook_subscriptions.jsonr  zbws_cache.jsonzbws_cache.enc.jsonÚpairingz
mcp-tokensÚsessionszbrowser-profilezstate.dbú	kanban.dbc                  ód   — t        «       D � �cg c]  } t        D ]
  }| dz  |z  ‘Œ Œ c}} S c c}} w )a½  Per-profile cache roots ``<root>/profiles/<name>/cache/{images,...}`` (the static safe
    roots cover only the active HERMES_HOME). Enumerated at check time so profiles created after
    startup count and are allowlisted BEFORE the ``/root`` denylist (HERMES_HOME symlinked).

    ``HERMES_HOME=/opt/data``) while the model emits a profile-scoped path silently fails delivery.
    Enumerated dynamically at check time so profiles created after startup are covered, and so the resolved
    profile path is allowlisted *before* the ``/root`` system denylist is consulted (which otherwise wins
    when HERMES_HOME is symlinked under a denied prefix and $HOME is not that prefix). See issue #31733.
    r  )Ú_profile_dirsÚ_MEDIA_DELIVERY_CACHE_SUBDIRS)ÚpÚsubdirs     r   Ú_profile_cache_rootsr)  ü  s1   € ô +8«/×f QÔHeÒf¸fˆA�‰K˜&Ó ÐfÐ ÓfÐfùÓfs   Ž,c                  óœ   — 	 t         dz  j                  «       D � cg c]  } | j                  «       sŒ| ‘Œ c} S c c} w # t        $ r g cY S w xY w)z?Every ``<root>/profiles/<name>`` directory, read at check time.Úprofiles)Ú_HERMES_ROOTrë  Úis_dirrR   ©r'  s    r   r%  r%  	  sE   € ðÜ(¨:Ñ5×>Ñ>Ó@ÖO�aÀAÇHÁHÅJ’ÒOÐOùÒOøÜò ØŠ	ðús   ‚= ›8±8µ= ¸= ½AÁ
Ac                  óp   — t        t        j                  t        «       t        gt        «       ¢­«      «      S )uÁ  Every Hermes home whose credential stores the denylist must cover: the ACTIVE home
    (the per-turn HERMES_HOME override under ``gateway.multiplex_profiles``), the shared root
    and every ``<root>/profiles/*``. Enumerated at check time like ``_profile_cache_roots`` on
    the allow side â€” a denylist frozen at import covers only the launch profile, so a
    ``MEDIA:<root>/profiles/<other>/.env`` emitted in any profile's turn would upload it.)Úlistr†   ÚfromkeysrC  r,  r%  rf   r   r   Ú_credential_home_rootsr2    s'   € ô ”—‘œÓ0´,ÐQÄÃÑQÓRÓSÐSr   c                  óœ   — t        t        j                  j                  dd«      j	                  «       xs t
        «      j                  «       S )zLKanban is root-shared across profiles by design (``kanban_db.kanban_home``).ÚHERMES_KANBAN_HOMEr`   )r)  rC   Úenvironr�   rz   r,  Ú
expanduserrf   r   r   Ú_kanban_rootr7    s4   € ä”—
‘
—‘Ð3°RÓ8×>Ñ>Ó@ÒPÄLÓQ×\Ñ\Ó^Ð^r   c                  óæ   — t        j                  t        «      5  t        «       dz  dz  j	                  «       D � cg c]  } | j                  «       sŒ| ‘Œ c} cddd«       S c c} w # 1 sw Y   g S xY w)z˜Every directory under ``<root>/kanban/boards`` (lax on purpose: the DENY side must catch a
    board whatever its name; the allow side filters further).ÚkanbanÚboardsN)rP   rQ   rR   r7  rë  r-  r.  s    r   Ú_kanban_board_dirsr;    s^   € ô 
×	Ñ	œWÓ	%ñ [Ü(›N¨XÑ5¸Ñ@×IÑIÓKÖZ�aÈqÏxÉxÍz’ÒZ÷[ñ [ùÚZ÷[à€Iús"   š!A&»A!ÁA!ÁA&Á!A&Á&A0c                  óü   — t         j                  j                  dd«      j                  «       } | rt	        | «      j                  «       gS t        «       dz  dz  g}|j                  d„ t        «       D «       «       |S )zCReturn durable Kanban attachment roots without importing kanban_db.ÚHERMES_KANBAN_ATTACHMENTS_ROOTr`   r9  Úattachmentsc              3   ó¬   K  — | ]L  }|j                  «       s:t        j                  d |j                  «      r|dz  j	                  «       r|dz  –— ŒN y­w)z[a-z0-9][a-z0-9_-]{0,63}r#  r>  N)Ú
is_symlinkÚreÚ	fullmatchr  rì  )rÈ   r-   s     r   rË   z+_kanban_attachment_roots.<locals>.<genexpr>-  sL   è ø€ ò 5¨$ØŸ™Ô)¬b¯l©lÐ;VÐX\×XaÑXaÔ.bØ˜[Ñ(×1Ñ1Ô3ð ˜Õ%ñ 5ùs   ‚AA)	rC   r5  r�   rz   r)  r6  r7  Úextendr;  )r�   Úrootss     r   Ú_kanban_attachment_rootsrE  '  sp   € ä�z‰z�~‰~Ð>ÀÓC×IÑIÓK€HÙÜ�X“×)Ñ)Ó+Ð,Ð,Ü‹^˜hÑ&¨Ñ6Ð7€EØ	‡L�Lñ 5Ô2DÓ2Fô 5ô 5ð €Lr   c                  óÂ   ‡— ddl m}  ˆfd„ | «       j                  t        j                  «      D «       }g t        t        t        «      ¢t        «       ¢t        «       ¢|¢S )zCReturn roots from which model-emitted local media may be delivered.r   )Úmedia_delivery_allow_dirsc              3   óÚ   •K  — | ]b  }|j                  d «      D ]L  }t        t        j                  j	                  |j                  «       «      «      xŠj                  «       r‰–— ŒN Œd y­w)ú,N)Úsplitr)  rC   r-   r6  rz   Úis_absolute)rÈ   r”  Úraw_rootÚroots      €r   rË   z0_media_delivery_allowed_roots.<locals>.<genexpr>6  se   øè ø€ ò OØØŸ™ CÓ(òOàÜœŸ™×+Ñ+¨H¯N©NÓ,<Ó=Ó>Ð>ˆD×KÑKÔMô 	ðOØñOùs   ƒA(A+)
Úgateway.media_policyrG  rJ  rC   ÚpathsepÚmapr)  ÚMEDIA_DELIVERY_SAFE_ROOTSr)  rE  )rG  Úoperator_rootsrM  s     @r   Ú_media_delivery_allowed_rootsrS  3  s`   ø€ å>óOÙ3Ó5×;Ñ;¼B¿J¹JÓGôO€Nð:ŒS”Ô0Ó1ð :Ô4HÓ4Jð :Ü%Ó'ð:Ø*8ð:ð :r   c                  óŽ   ‡‡— ddl m} m}  | «       sy |«       j                  «       Št	        t
        «      Št        ˆˆfd„‰«      S )zKRecency window (seconds) for trusting fresh files; 0 = pure-allowlist mode.r   )Úmedia_delivery_trust_recentÚ#media_delivery_trust_recent_secondsç        c                  ó6   •— ‰ rt        dt        ‰ «      «      S ‰S ©NrW  )ÚmaxÚfloat)Úcustomrh   s   €€r   r  z1_media_delivery_recency_seconds.<locals>.<lambda>E  s   ø€ ¹&œs 3¬¨f«Ó6€ Àg€ r   )rN  rU  rV  rz   r[  Ú,_MEDIA_DELIVERY_TRUST_RECENT_DEFAULT_SECONDSri   )rU  rV  r\  rh   s     @@r   Ú_media_delivery_recency_secondsr^  >  s9   ù€ çeÙ&Ô(ØÙ0Ó2×8Ñ8Ó:€FÜÔ@ÓA€GÜÔMÈwÓWÐWr   c                  óh   — t        «       D � �cg c]  } t        d«      D ]  }| |z  ‘Œ	 Œ c}} S c c}} w )z¯Named-board ``kanban.db`` stores (+ sidecars): they sit beside the ATTACHMENTS dir
    ``_kanban_attachment_roots`` allowlists and hold every task, comment and run transcript.r#  )r;  r  )Úboardr  s     r   Ú_kanban_board_db_pathsra  H  s1   € ô '9Ó&:×b˜UÄ}ÐU`ÓGaÒb¸tˆE�D‹LÐbˆLÓbÐbùÓbs   Ž.c                  óÔ   ‡ — t        t        j                  j                  d«      «      Š g t	        t         t
        «      ¢ˆ fd„t        D «       ¢d„ t        «       D «       ¢t        «       ¢S )zEReturn absolute denylist paths under which delivery is never allowed.Ú~c              3   ó(   •K  — | ]	  }‰|z  –— Œ y ­wr¤  rf   )rÈ   ÚsubÚhomes     €r   rË   z/_media_delivery_denied_paths.<locals>.<genexpr>R  s   øè ø€ ÒI˜Sˆd�S�jÑIùs   ƒc              3   ó<   K  — | ]  }t         D ]	  }||z  –— Œ Œ y ­wr¤  )Ú_ROOT_CREDENTIAL_PATHS)rÈ   ÚrÚrels      r   rË   z/_media_delivery_denied_paths.<locals>.<genexpr>S  s!   è ø€ ÒZ˜!ÔCYÒZ¸Cˆa�#�gÐZˆgÑZùó   ‚)	r)  rC   r-   r6  rP  Ú_MEDIA_DELIVERY_DENIED_PREFIXESÚ$_MEDIA_DELIVERY_DENIED_HOME_SUBPATHSr2  ra  )rf  s   @r   Ú_media_delivery_denied_pathsrn  N  sa   ø€ ä”—‘×"Ñ" 3Ó'Ó(€Dð'ŒS”Ô6Ó7ð 'ÛIÔ$HÔIð'áZÔ5Ó7ÔZð'ô $Ó%ð'ð 'r   )ÚstrictÚexpandro  rp  c                ó€   — 	 |r| j                  «       n| j                  |¬«      S # t        t        t        f$ r Y yw xY w)u»   ``path[.expanduser()].resolve(strict)`` or None when it fails (OSError / RuntimeError /
    ValueError â€” embedded NUL, symlink loop, undeterminable home, missing file under ``strict``).©ro  N)r6  ÚresolverR   ÚRuntimeErrorrÍ   )r-   ro  rp  s      r   Ú_resolve_pathru  W  s?   € ðÙ%+�—‘Ô!°×>Ñ>ÀfÐ>ÓMÐMøÜ”\¤:Ð.ò Ùðús   ‚#& ¦=¼=rÖ   c                 óÞ   — t        t        t        j                  j	                  d«      «      «      }t        «       D ]/  }t        |d¬«      }|€Œ| |k(  xs t        | |«      }|sŒ)||k7  sŒ/ y y)ut  True if ``resolved`` lives under a deny-listed system path â€” except a denied prefix that
    IS the running user's own home: ``/root`` is listed so a non-root gateway can't deliver
    another user's home, but a root-run gateway's own deliverables live under ``$HOME=/root``.
    Credential sub-dirs (``~/.ssh``, ``~/.hermes/.env``) stay blocked (more-specific entries).rc  T©rp  F)ru  r)  rC   r-   r6  rn  Ú_path_is_within)rÖ   rf  ÚdeniedÚresolved_deniedÚhits        r   Ú_path_under_denied_prefixr|  `  sq   € ô
 œœbŸg™g×0Ñ0°Ó5Ó6Ó7€DÜ.Ó0ò ˆÜ'¨°tÔ<ˆØÐ"ØØ˜/Ñ)ÒW¬_¸XÀÓ-WˆÚ�? dÓ*Ùðð r   Úwindow_secondsc                 ó’   — |dk  ry	 t        j                   «       | j                  «       j                  z
  |k  S # t        $ r Y yw xY w)u¯   True if mtime is within ``window_seconds`` â€” a session-scoped trust signal: agents
    produce artifacts seconds before sending; pre-existing host files are days/months old.r   F)rê  rí  rî  rR   )rÖ   r}  s     r   Ú_file_is_recently_producedr  p  sH   € ð ˜ÒØðÜ—	‘	“˜hŸm™m›o×6Ñ6Ñ6¸>ÑIÐIøÜò Ùðús   ˆ1: º	AÁArM  c                 ó„   — t        j                  t        «      5  | j                  |«       	 d d d «       y# 1 sw Y   yxY w)NTF)rP   rQ   rÍ   Úrelative_to)r-   rM  s     r   rx  rx  {  s;   € Ü	×	Ñ	œZÓ	(ñ Ø×Ñ˜ÔØ÷÷ ð ð ús   š6¶?r`   rh   c                 ól   — 	 ddl m}  || |«      S # t        $ r t        j                  | |«      cY S w xY w)u  Scope-aware TERMINAL_* read: the per-turn scope carries the ACTIVE profile's settings while
    os.getenv reads whatever a prior turn pinned into the process env. Only ImportError falls
    back â€” a refusal scope must raise rather than rebuild another profile's policy from the env.r   )Úterminal_env)Útools.terminal_scoperƒ  r  rC   Úgetenv)r  rh   rƒ  s      r   Ú_tenvr†  ‚  s;   € ð(Ý5ñ ˜˜gÓ&Ð&øô ò (Ü�y‰y˜˜wÓ'Ò'ð(ús   ‚ ‘3²3c                  óŽ  — t        dd«      j                  «       } 	 ddl}| r|j                  | «      ng }g }t        |t        «      r|ndD ]æ  }t        |t        «      r|j                  «       nd}|j                  d«      }|dk  rŒ<||dz   d j                  dd«      d   }t        j                  j                  |d| «      }|j                  d	«      st        |«      dkD  r|d   dk(  sŒ¡t        t!        |«      «      t!        |«      }
}	|	€ŒÃ|
j#                  «       sŒÔ|j%                  |	|
f«       Œè |S # t        $ r g cY S w xY w)
z«Parse ``TERMINAL_DOCKER_VOLUMES`` (JSON list of ``host:container[:mode]``) into
    ``(host_path, container_path)``; named volumes / non-absolute hosts can't resolve here.ÚTERMINAL_DOCKER_VOLUMESr`   r   Nrf   z:/r·   rç   rg  )r†  rz   ÚjsonÚloadsrO   rŽ   r0  rF   ÚfindrJ  rC   r-   r6  r	  r²   ru  r)  rK  r�  )rn  Ú_jsonro  ÚmountsÚentryÚspecrí   Úcontainer_rawÚhost_expandedÚ	host_pathÚcontainer_paths              r   Ú_parse_docker_volume_mountsr”  �  s?  € ô Ð)¨2Ó
.×
4Ñ
4Ó
6€CðÛÙ%(�—‘˜SÔ!¨bˆð ')€FÜ% f¬dÔ3‘¸ò 7ˆÜ *¨5´#Ô 6ˆu�{‰{Œ}¸Bˆà�i‰i˜‹oˆØ�!Š8ØØ˜S 1™W˜X˜×,Ñ,¨S°!Ó4°QÑ7ˆäŸ™×*Ñ*¨4°°¨:Ó6ˆØ×(Ñ(¨Ô-´#°mÓ2DÀqÒ2HÈ]Ð[\ÑM]ÐadÒMdØÜ$1´$°}Ó2EÓ$FÌÈ]ÓH[�>ˆ	ØÑ  ^×%?Ñ%?Õ%AØ�M‰M˜9 nÐ5Õ6ð7ð €Møô# ò ØŠ	ðús   œD6 Ä6EÅEc                 ó†  — 	 ddl m} 	 ddlm}  |«       xs d}g }t        dd«      j                  «       x}r|j                   |d|› �«      «       |dk7  r|j                   |d|› �«      «       |j                  d«       | r|j                   |d	| › �«      «       |S # t        $ r dgcY S w xY w# t        $ r d}Y Œ¥w xY w)
u|  Candidate host sandbox dir names for the delivering session, best first. Mirrors
    ``_resolve_container_task_id`` (tools/terminal_tool.py): containers are PROFILE-scoped
    (``default``, else ``profile:<name>``); legacy ``session:<key>`` sandboxes stay as a fallback.
    The key is passed explicitly because delivery runs after the turn's contextvars were cleared.

    Takes the key explicitly because the delivery pipeline runs after ``_handle_message_with_agent`` cleared
    the turn's session contextvars (#93950) â€” an ambient lookup here would silently collapse onto
    ``default`` and miss the session's real sandbox.
    r   )Úsanitize_task_id_for_pathrh   )Úget_active_profile_nameÚ$TERMINAL_DOCKER_SHARED_CONTAINER_KEYr`   zshared:zprofile:zsession:)Útools.environments.path_utilsr–  rO   Úhermes_cli.profilesr—  r†  rz   r�  )rW  r–  r—  rx   Ú
candidatesÚshareds         r   Ú_docker_sandbox_dir_candidatesr�  ¨  sà   € ðÝKðÝ?Ù)Ó+Ò8¨yˆð €JäÐ=¸rÓB×HÑHÓJÐJ€vÐJØ×ÑÑ3°g¸f¸XÐ4FÓGÔHØ�)ÒØ×ÑÑ3°h¸w¸iÐ4HÓIÔJØ×Ñ�iÔ ÙØ×ÑÑ3°h¸{¸mÐ4LÓMÔNØÐøô! ò Øˆ{Òðûô
 ò ØŠðús"   ‚B  ‰B2 Â B/Â.B/Â2C Â?C >   r4   r5   ÚyesÚtruec                  óX   — t        dd«      j                  «       j                  «       dk(  S )NÚTERMINAL_ENVr`   Údocker)r†  rz   rb   rf   r   r   Ú_docker_env_activer£  Ê  s&   € Ü� Ó$×*Ñ*Ó,×2Ñ2Ó4¸Ñ@Ð@r   c                  óv   — t        «       xr. t        dd«      j                  «       j                  «       t        v S )z@Docker backend with persistent containers (the default) enabled.ÚTERMINAL_CONTAINER_PERSISTENTrŸ  )r£  r†  rz   rb   Ú_TRUTHYrf   r   r   Ú_docker_persistent_activer§  Î  s0   € äÓÒm¤EÐ*IÈ6Ó$R×$XÑ$XÓ$Z×$`Ñ$`Ó$bÔfmÐ$mÐmr   Úleafc                 óê   — t        «       sg S 	 ddlm}  |«       dz  }t        | «      D �cg c],  }||z  |z  j	                  d¬«      x}j                  «       r|‘Œ. c}S c c}w # t        $ r g cY S w xY w)z§Existing ``<sandbox>/docker/<candidate>/<leaf>`` host dirs in candidate order;
    the translator tries each until the file resolves. Empty unless Docker + persistent.r   )Úget_sandbox_dirr¢  Frr  )r§  Útools.environments.baserª  r�  rs  r-  rO   )rW  r¨  rª  Úbaser  Úcands         r   Ú _docker_persistent_sandbox_rootsr®  Ó  sˆ   € ô %Ô&Øˆ	ðÝ;ÙÓ  8Ñ+ˆÜ!?ÀÓ!Lö R˜Ø! D™[¨4Ñ/×8Ñ8ÀÐ8ÓFÐF�D×NÑNÔPò ò Rð 	Rùò Røäò ØŠ	ðús"   ŽA$ «1AÁA$ ÁA$ Á$A2Á1A2c                 ó¤  — t        «       sg S t        dd«      j                  «       j                  «       t        v rot        d«      xs t        j                  «       }	 t        t
        j                  j                  |«      «      j                  d¬«      }|j                  «       r|gS g S t        | d«      S # t        t        t        f$ r g cY S w xY w)z¡Existing host candidates for ``/workspace``: the explicit cwd mount
    (``TERMINAL_DOCKER_MOUNT_CWD_TO_WORKSPACE``) if set, else the persistent sandbox layouts.Ú&TERMINAL_DOCKER_MOUNT_CWD_TO_WORKSPACEr  ÚTERMINAL_CWDFrr  Ú	workspace)r§  r†  rz   rb   r¦  rC   Úgetcwdr)  r-   r6  rs  rR   rt  rÍ   r-  r®  )rW  ÚcwdrÁ   s      r   Ú$_default_docker_workspace_host_rootsrµ  á  s´   € ô %Ô&Øˆ	ÜÐ5°wÓ?×EÑEÓG×MÑMÓOÔSZÑZÜ�NÓ#Ò2¤r§y¡y£{ˆð	ÜœŸ™×*Ñ*¨3Ó/Ó0×8Ñ8ÀÐ8ÓFˆDð Ÿ™œ�ˆvÐ.¨BÐ.Ü+¨K¸ÓEÐEøô œ¤zÐ2ò 	ØŠIð	ús   Á8B6 Â6CÃCc                  ó²   — t        «       sg S 	 ddlm}   | «       D �cg c]  }t        |d   «      t        |d   «      f‘Œ  c}S c c}w # t        $ r g cY S w xY w)z¶(host, container) pairs for the auto-mounted Hermes cache dirs (``/root/.hermes/...`` in
    MEDIA tags); longer prefixes than the ``/root`` home mount, so longest-prefix match wins.r   )Úget_cache_directory_mountsr’  r“  )r£  Útools.credential_filesr·  r)  rO   )r·  Úms     r   Ú_cache_dir_container_mountsrº  ð  s[   € ô ÔØˆ	ðÝEÙKeÓKgÖhÀa”�a˜‘nÓ%¤t¨AÐ.>Ñ,?Ó'@ÒAÒhÐhùÒhøÜò ØŠ	ðús"   ŽA �#AÁ A ÁA ÁAÁAÚ	candidateÚreasonc                 ó†   — t        «       syt        j                  dt        t	        | «      «      ||r
d|› �«       yd«       y)zÑName WHY a container-absolute MEDIA path failed translation (otherwise the only signal is
    the generic "Skipping unsafe MEDIA directive path" line). Docker-only; host rejections quiet.

    See #93950.
    Nz�Docker MEDIA path %s did not resolve to a host sandbox file (%s%s); the producing container's sandbox directory may not exist yet or was prunedz, session_key=r`   )r£  r   rL   Ú_log_safe_pathrF   )r»  rW  r¼  s      r   Ú_warn_unresolved_docker_mediar¿  ü  sF   € ô ÔØÜ
‡N�Nð  ä!/´°I³Ó!?ÀÙ5@�^ K =Ð1õJð GIõJr   c           
      óÎ  — | j                  «       syt        j                  t        «      5  ddlm}  |«        ddd«       g t        «       ¢t        «       ¢}|D ��ch c]  \  }}|j                  «       ’Œ }}}d|vr!|j                  d„ t        |«      D «       «       d|vrA| j                  «       j                  d«      s"|j                  d„ t        |d	«      D «       «       |st        | |d
«       y| j                  «       }|D ��	�
cg c]O  \  }}	|	j                  «       j                  d«      xs d}
||
k(  s|j                  |
dz   «      r||	t        |
«      f‘ŒQ }}	}}
|st        | |d«       yt!        |d„ ¬«      D ]=  \  }}	}t#        || j%                  |	«      z  d¬«      }|€Œ)||k(  st'        ||«      sŒ;|c S  t        | |d«       y# 1 sw Y   �Œ–xY wc c}}w c c}
}	}w )z¸Container-absolute path -> host path via longest-prefix match over ``docker_volumes``, the
    auto-mounted cache dirs (``/root/.hermes/...``), persistent ``/workspace`` and ``/root``.Nr   )Ú_ensure_terminal_env_bridgedú
/workspacec              3   ó6   K  — | ]  }|t        d «      f–— Œ y­w)rÂ  Nr(  ©rÈ   rM  s     r   rË   z9_translate_docker_container_media_path.<locals>.<genexpr>  s   è ø€ Òo°T�tœT ,Ó/Ô0Ñoùó   ‚r  z/root/.hermesc              3   ó6   K  — | ]  }|t        d «      f–— Œ y­w)r  Nr(  rÄ  s     r   rË   z9_translate_docker_container_media_path.<locals>.<genexpr>  s    è ø€ ò eØ&*ˆT”4˜“=Ô!ñeùrÅ  rf  zno sandbox mounts resolvedrg  zno mounted prefix matchesc                 ó   — | d    S )Nr]   rf   )r¹  s    r   r  z8_translate_docker_container_media_path.<locals>.<lambda>*  s   € ÈAÈaÉDÈ5€ r   )rì   Trr  zhost file missing from sandbox)rK  rP   rQ   rO   Útools.terminal_toolrÁ  r”  rº  Úas_posixrC  rµ  r	  r®  r¿  Úrstripr²   Úsortedru  r�  rx  )r»  rW  rÁ  r�  rÉ   ÚcÚmountedÚcandidate_posixÚ	host_rootÚcontainer_rootr1   ÚmatchedÚ_scoreÚ
translateds                 r   Ú&_translate_docker_container_media_pathrÔ  
  só  € ð × Ñ Ô"Øô 
×	Ñ	œYÓ	'ñ 'ÝDÙ$Ô&÷'ð NÔ*Ó,ÐMÔ/JÓ/LÐM€FØ(.×/¡  1ˆq�z‰z�|Ð/€GÑ/à˜7Ñ"Ø�‰ÑoÔ=aÐbmÓ=nÔoÔoð �gÑ i×&8Ñ&8Ó&:×&EÑ&EÀoÔ&VØ�‰ñ eÜ.NÈ{Ð\bÓ.côeô 	eáÜ% i°Ð>ZÔ[Øà×(Ñ(Ó*€OØY_÷ Yð YÑ<U¸IÀ~Ø,×5Ñ5Ó7×>Ñ>¸sÓCÒJÀs�6Ø! VÒ+¨×/IÑ/IÈ&ÐSVÉ,Ô/Wð ˜>¬3¨v«;Ò7ð Y€Gò Yñ Ü% i°Ð>YÔZØÜ-3°GÁÔ-Qò Ñ)ˆ	�> 6Ü" 9¨y×/DÑ/DÀ^Ó/TÑ#TÐ]aÔbˆ
ØÑ!Ø˜iÒ'¬?¸:ÀyÕ+QØÒð	ô
 " )¨[Ð:ZÔ[Ø÷?'ñ 'üó 0ùôYs   «GÁGÄ	AG ÇGc                 óB  — t        | «      }|sy	 t        t        j                  j	                  |«      «      }|j                  «       syt        ||¬«      }|€t        |d¬«      }|�|j                  «       syt        «       D ]+  }t        |d¬«      }|€Œt        ||«      sŒ t        |«      c S  ddlm}  |«       st#        |«      rdS t        |«      S t%        «       }|dkD  r"t#        |«      st'        ||«      rt        |«      S y# t
        t        t        f$ r Y yw xY w)aÊ  Safe absolute file path for native media delivery, else None. Default: any existing
    regular file outside the credential / system denylist (symmetric with inbound). Strict
    (``HERMES_MEDIA_DELIVERY_STRICT=1``, public bots where prompt injection must not exfiltrate
    host secrets): MUST be under a Hermes cache, an operator root (``HERMES_MEDIA_ALLOW_DIRS``),
    or freshly produced within the recency window. Symlinks are resolved before any check.N©rW  Trr  rw  r   )Úmedia_delivery_strict)Ú_normalize_media_tag_pathr)  rC   r-   r6  rR   rt  rÍ   rK  rÔ  ru  rì  rS  rx  rF   rN  r×  r|  r^  r  )	r-   rW  r»  ÚexpandedrÖ   rM  Úresolved_rootr×  Úwindows	            r   Úvalidate_media_delivery_pathrÜ  3  s  € ô *¨$Ó/€IÙØðÜœŸ™×*Ñ*¨9Ó5Ó6ˆð ×ÑÔ!Øä5°hÈKÔX€HØÐÜ  °$Ô7ˆØÐ˜x×/Ñ/Ô1Øä-Ó/ò !ˆÜ% d°4Ô8ˆØÑ$¬¸À=Õ)QÜ�x“=Ò ð!õ
 ;Ù Ô"Ü0°Ô:ˆtÐMÄÀHÃÐMä,Ó.€FØ�Š
Ô4°XÔ>Ü*¨8°VÔ<Ü�8‹}ÐØøô3 ”\¤:Ð.ò áðús   �(D ÄDÄDz[\x00-\x1f\x7f\x85\u2028\u2029]c                 óF   — t         j                  dt        | «      «      dd S )z9Return a single-line, length-bounded path for log output.ú?NéÈ   )Ú_LOG_UNSAFE_CHARSre  rF   ©r-   s    r   r¾  r¾  _  s    € ä× Ñ  ¤c¨$£iÓ0°°#Ð6Ð6r   ÚlabelÚdroppedc                 óæ   — t        | «      }t        ||¬«      }|sddlm}  ||«      }|sFt	        |«      sdnd}t
        j                  d||t        |«      «       |�|j                  ||dœ«       |S )ak  ``validate_media_delivery_path`` plus the shared "Skipping unsafe ..." warning. A path the
    host cannot see is retried against the active remote sandbox (ssh/modal/...; #466). When
    ``dropped`` is a list, a rejected path is appended as ``{"path", "reason"}`` so the caller can
    report the drop instead of booking a delivery that never happened (#115908).rÖ  r   )Úfetch_remote_mediaznot found on this hostzdenied by the delivery policyzSkipping %s (%s): %s)r-   r¼  )	rF   rÜ  Úgateway.media_fetchrå  Ú_existing_regular_filer   rL   r¾  r�  )Úraw_pathrW  râ  rã  rn  Ú	safe_pathrå  r¼  s           r   Ú_validated_delivery_pathrê  d  st   € ô ˆh‹-€CÜ,¨S¸kÔJ€IÙÝ:Ù& sÓ+ˆ	Ùô 2HÈÔ1LÑ)ÐRqˆÜ�‰Ð-¨u°f¼nÈSÓ>QÔRØÐØ�N‰N C°6Ñ:Ô;ØÐr   rn  c                 ó¤   — 	 t        t        j                  j                  | «      «      j	                  «       S # t
        t        t        f$ r Y yw xY w)NF)r)  rC   r-   r6  rì  rR   rt  rÍ   )rn  s    r   rç  rç  y  sC   € ðÜ”B—G‘G×&Ñ& sÓ+Ó,×4Ñ4Ó6Ð6øÜ”\¤:Ð.ò Ùðús   ‚58 ¸AÁAú.pdfzapplication/pdfú.mdztext/markdownú.txtz
text/plainú.csvztext/csvú.logú.jsonzapplication/jsonú.xmlzapplication/xmlú.yamlzapplication/yamlú.ymlú.tomlzapplication/tomlú.iniú.cfgú.zipzapplication/zipú.doczapplication/mswordú.xlszapplication/vnd.ms-excelú.pptzapplication/vnd.ms-powerpointú.docxzGapplication/vnd.openxmlformats-officedocument.wordprocessingml.documentzAapplication/vnd.openxmlformats-officedocument.spreadsheetml.sheetzIapplication/vnd.openxmlformats-officedocument.presentationml.presentation)ú.xlsxú.pptxú.tsú.pyú.sh>L   ú.cú.hú.mú.rú.ccú.csú.exú.goú.jlú.jsú.ktú.plú.rbú.rsú.tfú.batú.cjsú.cljú.cppú.cssú.erlú.exsú.hclú.hppú.jsxú.luaú.mjsú.phpú.ps1ú.pyiú.rstú.sqlú.srtú.texú.tsxú.vttú.zshú.bashú.confú.diffú.fishú.javaú.lessú.sassú.scssú.cmakeú.jsonlú.patchú.protoú.scalaú.swiftú.gradleú.ndjsonú.graphqlú	.makefileú	.markdownú.dockerfileú.propertiesrí  r   r  rÿ  r÷  rï  r  ú.htmrö  rð  ú.tsvrî  rò  rô  ú.htmlrñ  rõ  ró  z
image/jpegz	image/pngz
image/webpz	image/gif)r$   r&   r%   r'   r#   ):r%   r$   r&   r#   r'   z.bmpz.tiffz.svgr!   r    r   r   r"   r   r   r   r   r   r   r   r   rì  rü  rù  z.odtz.rtfrî  rí  z.epubrý  rú  z.odsrï  r=  rñ  rò  ró  rô  z.kmzz.kmlz.geojsonz.gpxrþ  rû  z.odpz.keyrø  z.tarz.gzz.tgzz.bz2z.xzz.7zz.rarz.apkz.ipar>  r<  ÚMEDIA_DELIVERY_EXTSÚ|c              #   ó>   K  — | ]  }|j                  d «      –— Œ y­w©r–   N©Úlstrip©rÈ   Úes     r   rË   rË   ¸  s   è ø€ Ò)U¸A¨!¯(©(°3¯-Ñ)Uùó   ‚T)rì   Úreverseu9   ï¼ˆï¼‰ã€ˆã€‰ã€Šã€‹ï¼šï¼Œã€‚ï¼›ï¼�ï¼Ÿã€�â€œâ€�â€˜â€™ã€�ã€‘zq[`"'*_]{0,3}MEDIA:\s*(?P<path>`[^`\n]+?`|"[^"\n]+?"|'[^'\n]+?'|(?:~/|/|[A-Za-z]:[/\\])\S+?(?:[^\S\n]+\S+?)*?\.(?:z))(?=[\s`"'*_,;:)\]}\[z%]|MEDIA:|\.(?:\s|$)|$)[`"'*_]{0,3}\.?zp[`"'*_]{0,3}MEDIA:\s*(?P<path>`[^`\n]+`|"[^"\n]+"|'[^'\n]+'|(?:~/|/|[A-Za-z]:[/\\])[^\s\n`"']+?)(?=[`"'\s,;:)\]}z]|MEDIA:|$)[`"'*_]{0,3}\s*Ú	scan_textÚmatchzre.Matchc                 óø  — t        |j                  d«      «      }|syt        |«      }|r||j                  d«      fS |j	                  d«      }| |d j                  dd«      d   }|j                  dd«      }|dk7  r|d| }|j                  d«      |z
  }t        d«      D ]V  }t        j                  d	||d «      }	|	s y||	j                  «       z   }
t        t        |d|
 «      «      }|r	|||
z   fc S |
}ŒX y)
aŠ  Extensionless MEDIA tag match -> validated on-disk ``(safe_path, end_offset)`` or None: the
    captured path first, then extended across single spaces (max 8 tokens, never past a newline
    or the next ``MEDIA:``).

    When that fails validation, the candidate is progressively extended forward across single spaces
    (validation-gated, bounded at 8 tokens, never past a newline or a subsequent ``MEDIA:`` keyword) so
    unknown-extension paths containing spaces deliver (#24032). Returns ``(safe_path, end_offset)`` where
    ``end_offset`` is the index in ``scan_text`` just past the matched path, or ``None`` when nothing
    validates.
    r-   NÚ
r·   r   úMEDIA:rh  r²  z[ \t]*[^ \t]+)
rØ  ÚgrouprÜ  ÚendÚstartrJ  r‹  rÕ  rA  rJ  )rI  rJ  r-   rp  rP  ÚsegmentÚnxtÚposrÉ   ÚtokenÚtok_ends              r   Ú_match_extensionless_pathrV  ø  s  € ô % U§[¡[°Ó%8Ó9€DÙØÜ'¨Ó-€DÙØ�U—Y‘Y˜vÓ&Ð&Ð&Ø�K‰K˜Ó€EØ˜˜Ð×%Ñ% d¨AÓ.¨qÑ1€GØ
�,‰,�x Ó
#€CØ
ˆb‚yØ˜$˜3�-ˆØ
�)‰)�FÓ
˜eÑ
#€CÜ�1‹Xò ˆÜ—‘Ð)¨7°3°4¨=Ó9ˆÙØð ð ˜Ÿ	™	›Ñ#ˆÜ+Ô,EÀgÈhÈwÐFWÓ,XÓYˆÙØ˜ ™Ð(Ò(Ø‰ðð r   c                 óâ   — t        | xs d«      j                  «       }t        |«      dk\  r%|d   |d   k(  r|d   dv r|dd j                  «       }|j                  d«      j	                  d«      S )Nr`   r]   r   rh  z`"'r·   z
`"',.;:)}])rF   rz   r²   rD  rÊ  )rn  r-   s     r   rØ  rØ    sk   € ÜˆsŠy�b‹>×ÑÓ!€DÜ
ˆ4ƒy�A‚~˜$˜q™' T¨"¡XÒ-°$°q±'¸VÑ2CØ�A�bˆz×ÑÓ!ˆØ�;‰;�vÓ×%Ñ% mÓ4Ð4r   c                 óT   — t        | «      j                  j                  «       t        vS )uC  True when ``path`` has no extension or one outside MEDIA_DELIVERY_EXTS â€” such paths
    take the validated delivery pass so nonexistent / denylisted ones stay visible.

    ``path`` â€” either the basename has no extension at all (Caddyfile, Makefile, â€¦) or the extension is not
    in MEDIA_DELIVERY_EXTS (.py, .log, .weirdext, â€¦). Such paths route through the validated delivery pass
    (``validate_media_delivery_path``) instead of the unconditional one, so every file type is deliverable
    (#36060) while nonexistent / denylisted paths stay visible in the text.
    )r)  r2   rb   r?  rá  s    r   Ú!_path_lacks_deliverable_extensionrY  "  s#   € ô �‹:×Ñ×"Ñ"Ó$Ô,?Ð?Ð?r   rÜ   c                 ó"   — d| v xs
 d| v xs d| v S )NrM  ú[[audio_as_voice]]ú[[as_document]]rf   ©rÜ   s    r   Ú_has_media_directivesr^  .  s$   € Ø�tÐÒXÐ3°tÐ;ÒXÐ?PÐTXÐ?XÐXr   z<|eos|>c                 ó(  — t        | j                  «       «      x}}|t        t        «      k\  r]| |t        t        «      z
  | t        k(  rA|t        t        «      z  }|t        t        «      k\  r| |t        t        «      z
  | t        k(  rŒA||k  r|S dS )zœOffset where the run of exact ``<|eos|>`` tokens closing ``text`` (trailing whitespace
    ignored) begins, else -1; the run ends at ``len(text.rstrip())``.rh  )r²   rÊ  Ú_TERMINAL_SENTINEL)rÜ   rO  rP  s      r   Ú_terminal_sentinel_startra  :  sŽ   € ô �d—k‘k“mÓ$Ð$€Cˆ%Ø
”3Ô)Ó*Ò
*¨t°E¼CÔ@RÓ<SÑ4SÐTYÐ/ZÔ^pÒ/pØ”Ô'Ó(Ñ(ˆð ”3Ô)Ó*Ò
*¨t°E¼CÔ@RÓ<SÑ4SÐTYÐ/ZÔ^pÓ/pà˜C’Kˆ5Ð' RÐ'r   c                 ó¾   — t         }|j                  |j                  | «      «      }t        | «      }|dk\  r&t	        ||t        | j                  «       «      fg«      }|S )a   Offset-preserving mask of protected spans (code, quotes, JSON string values) and of a
    terminal ``<|eos|>`` sentinel, so a tag glued to it ends on whitespace like any other.
    BasePlatformAdapter is defined later in this module; resolved at call time.r   )ÚBasePlatformAdapterÚ_mask_json_string_mediaÚ_mask_protected_spansra  Ú_blank_spansr²   rÊ  )rÜ   ÚAÚmaskedrP  s       r   Ú_mask_media_scan_textri  C  sY   € ô 	€AØ×&Ñ& q×'>Ñ'>¸tÓ'DÓE€FÜ$ TÓ*€EØ�‚zÜ˜f¨¬s°4·;±;³=Ó/AÐ'BÐ&CÓDˆØ€Mr   c                 ó¦   — t        t        | «      «      }t        | «      }|r/|dk\  r*|j                  |t	        | j                  «       «      f«       |S )z±Spans to delete from ``text``: its deliverable MEDIA tags (located on the masked copy)
    plus a terminal ``<|eos|>`` sentinel, which is a control token and never user content.r   )Ú_real_media_tag_spansri  ra  r�  r²   rÊ  )rÜ   ÚspansrP  s      r   Ú_deliverable_tag_spansrm  O  sH   € ô "Ô"7¸Ó"=Ó>€EÜ$ TÓ*€EÙ�˜!’Ø�‰�eœS §¡£Ó/Ð0Ô1Ø€Lr   rh  c              #   óÊ   K  — t         j                  | «      D ]G  }t        |j                  d«      «      }|sŒ t	        |«      sŒ,t        | |«      }|€Œ;||d   |d   f–— ŒI y­w)z Yield ``(match, safe_path, end_offset)`` for every extension-less / unknown-extension
    MEDIA tag in ``masked`` that ``validate_media_delivery_path`` accepts.r-   Nr   r·   )ÚMEDIA_EXTENSIONLESS_TAG_REÚfinditerrØ  rN  rY  rV  )rh  rJ  r-   rÖ   s       r   Ú_extensionless_media_matchesrq  Y  sg   è ø€ ô ,×4Ñ4°VÓ<ò 6ˆÜ(¨¯©°VÓ)<Ó=ˆÚÔ5°dÕ;Ü0°¸Ó?ˆHØÑ#Ø˜X a™[¨(°1©+Ð5Ó5ñ6ùs   ‚5A#¸A#ÁA#ÁA#c                 ó²   — t         j                  | «      D �cg c]  }|j                  «       ‘Œ }}|j                  d„ t	        | «      D «       «       |S c c}w )z´(start, end) spans of deliverable MEDIA tags on a masked copy: known-extension tags
    unconditionally, extension-less / unknown ones only if validate_media_delivery_path accepts.c              3   óH   K  — | ]  \  }}}|j                  «       |f–— Œ y ­wr¤  ©rP  )rÈ   rJ  rÉ   rO  s       r   rË   z(_real_media_tag_spans.<locals>.<genexpr>h  s!   è ø€ Ò`©-¨%°°C�%—+‘+“- Ô%Ñ`ùs   ‚ ")ÚMEDIA_TAG_CLEANUP_RErp  ÚspanrC  rq  )rh  r¹  rl  s      r   rk  rk  d  sL   € ô &:×%BÑ%BÀ6Ó%JÖK �1—6‘6•8ÐK€EÐKØ	‡L�LÑ`Ô;WÐX^Ó;_Ô`Ô`Ø€Lùò Ls   ˜Az```[^\n]*\n.*?```z	`[^`\n]+`rß  c                 óŽ   — t         t        fD ��cg c](  }|j                  | «      D ]  }|j                  «       ‘Œ Œ* c}}S c c}}w )zH(start, end) spans of fenced code blocks and inline code in ``content``.)Ú_FENCED_CODE_REÚ_INLINE_CODE_RErp  rv  )rß  Úrxr¹  s      r   Ú_code_spansr{  p  s7   € ä /´ÐA×`˜È2Ï;É;ÐW^ÓK_Ò`ÀaˆA�F‰F�HÐ`ˆHÓ`Ð`ùÓ`s   �-Arl  c                 ó’   — t        | «      }|D ]"  \  }}||| D �cg c]  }|dk(  r|nd‘Œ c}||| Œ$ dj                  |«      S c c}w )zQReplace every non-newline char inside ``spans`` with a space (offsets preserved).rL  Ú r`   )r0  r§   )rÜ   rl  ÚcharsrP  rO  rÌ  s         r   rf  rf  u  s]   € ä�‹J€EØò O‰
ˆˆsØ=BÀ5ÈÐ=MÖN¸  d¢™A°Ñ3ÒNˆˆe�CÑðOà�7‰7�5‹>Ðùò Os   ›Ac                 ó  — |s| S g }t        |«      D ]C  \  }}|r)||d   d   k  r|d   d   t        |d   d   |«      f|d<   Œ1|j                  ||f«       ŒE t        | «      }t	        |«      D ]
  \  }}|||…= Œ dj                  |«      S )z—Delete ``spans`` from ``text``, merging overlapping/nested ones first so multi-pattern
    matches over the same tag never double-delete adjacent text.rh  r·   r   r`   )rË  rZ  r�  r0  Úreversedr§   )rÜ   rl  Úmergedr°   rF  r~  rP  rO  s           r   Ú_delete_spansr‚  }  s¬   € ñ ØˆØ€FÜ�u“ò "‰ˆˆ1Ù�a˜6 "™: a™=Ò(Ø  ™* Q™-¬¨V°B©Z¸©]¸AÓ)>Ð?ˆF�2ŠJà�M‰M˜1˜a˜&Õ!ð	"ô
 �‹J€EÜ˜vÓ&ò ‰
ˆˆsØ�%˜�)Ñðà�7‰7�5‹>Ðr   c                 óŽ   — | rt        | «      s| S | j                  dd«      j                  dd«      }t        |t        |«      «      S )u`  Remove MEDIA: tags and [[audio_as_voice]] / [[as_document]] markers so they never render
    as text (backstop after ``extract_media``). Protected spans are mask-located only â€” tags
    inside them are neither stripped nor mangled, matching ``extract_media`` so display and
    delivery agree. Empty/None text is returned as-is.

    See #16434.
    r[  r`   r\  )r^  r<   r‚  rm  )rÜ   Úcleaneds     r   Ú_strip_media_tag_directivesr…  Ž  sG   € ñ Ô,¨TÔ2ØˆØ�l‰lÐ/°Ó4×<Ñ<Ð=NÐPRÓS€GÜ˜Ô"8¸Ó"AÓBÐBr   Úfilenamec                 ó–  — t        «       }|rt        |«      j                  ndj                  dd«      j	                  «       }|r|dv rd}|dt        j                  «       j                  dd › d|› �z  }|j                  «       j                  |j                  «       «      st        d	|›�«      ‚|j                  | «       t        |«      S )
z¯Save raw document bytes to the cache as ``doc_{uuid12}_{original_name}`` and return
    the absolute path; raises ValueError if the sanitized path escapes the cache directory.r	  ú r`   >   ú..r–   Údoc_Nr¤   rÉ   zPath traversal rejected: )Úget_document_cache_dirr)  r  r<   rz   r©   rª   r«   rs  Úis_relative_torÍ   r¹  rF   )r¯  r†  r·  Ú	safe_namerº  s        r   Úcache_document_from_bytesrŽ  œ  s»   € ô 'Ó(€Iá(0”�h“×$Ò$°j×IÑIÈ&ÐRTÓU×[Ñ[Ó]€IÙ˜	 [Ñ0Øˆ	Ø˜T¤$§*¡*£,×"2Ñ"2°3°BÐ"7Ð!8¸¸)¸ÐEÑE€Hà×ÑÓ×,Ñ,¨Y×->Ñ->Ó-@ÔAÜÐ4°X°LÐAÓBÐBØ×Ñ˜ÔÜˆx‹=Ðr   c              ƒ   óT   K  — t        j                  t        | |«      ƒ d{  –—† S 7 Œ­w)z>Cache document bytes without blocking the caller's event loop.N)rÄ  rÅ  rŽ  )r¯  r†  s     r   Úcache_document_from_bytes_asyncr�  ¬  s"   è ø€ ä×"Ñ"Ô#<¸dÀHÓM×MÐMÐMúrÉ  c                   óF   — e Zd ZU dZeed<   eed<   eed<   eed<   defd„Zy)	ÚCachedMediaz)Result of caching one attachment's bytes.r-   r†  r‘   Údisplay_namer	   c                 óV   — d| j                   › d| j                  › d| j                  › d�S )z>One-line transcript annotation pointing the agent at the file.ú[z 'z' saved at: ú])r‘   r“  r-   ©Úselfs    r   Úcontext_notezCachedMedia.context_noteº  s,   € à�4—9‘9�+˜R × 1Ñ 1Ð2°,¸t¿y¹y¸kÈÐKÐKr   N)rJ  rK  rL  rM  rF   rO  r™  rf   r   r   r’  r’  ²  s(   … á3Ø
ƒIØƒOØ
ƒIØÓðL˜cô Lr   r’  Ú_MIME_TO_EXTÚ	mime_typec                 óÂ   — | r0t         j                  j                  | «      d   j                  «       nd}|xs( t        j                  |xs dj                  «       d«      S )z=Best-effort file extension from filename, then MIME fallback.r·   r`   )rC   r-   Úsplitextrb   rš  r�   )r†  r›  r˜   s      r   Ú_resolve_media_extrž  Ç  sM   € á3;Œ"�'‰'×
Ñ
˜8Ó
$ QÑ
'×
-Ñ
-Ô
/À€CØÒA”,×"Ñ" I¢O°×#:Ñ#:Ó#<¸bÓAÐAr   ©r†  r›  Údefault_kindr   c                ó¦  — ddl m} t        ||«      }|xs dj                  «       }|rt	        j
                  dd|«      n|j                  d«      xs d}dt        d	t        d
fdt        dt        dfdt        dt        d
ffD ]k  \  }}	}
}}|j                  |› d�«      s
||	v s||k(  sŒ'||	v r|n|
}	  || |¬«      }|r|j                  |› d�«      r|n|	|   }t         ||«      |||«      c S  |xs	 |rd|› �nd}t!        | |«      }t"        j%                  |«      xs |xs d}t         ||«      |d|xs |«      S # t        $ r
 |dk7  r‚ Y  yw xY w)zäClassify and cache raw attachment bytes -> CachedMedia (None only for images failing
    validation). ``default_kind`` biases ambiguous ext/MIME (Telegram native photo, no name);
    anything not image/video/audio is a document.r   )Úto_agent_visible_cache_pathr`   z	[^\w.\- ]rÉ   r–   Úfiler­  r$   Tr   r!   Fró  r   rg  )r˜   Nr	  zdocument.binzapplication/octet-stream)r¸  r¢  rž  rb   rA  re  rD  ÚSUPPORTED_IMAGE_DOCUMENT_TYPESrÂ  ÚSUPPORTED_VIDEO_TYPESr  Ú_AUDIO_MIME_TYPESrù  r	  rÍ   r’  rŽ  ÚSUPPORTED_DOCUMENT_TYPESr�   )r¯  r†  r›  r   r¢  r˜   ÚmimeÚdisplayr‘   ÚtableÚdefault_extrÜ  ÚpassthroughÚkind_extr-   Úout_mimeÚfallback_names                    r   Úcache_media_bytesr°  Í  s�  € õ
 CÜ
˜X yÓ
1€CØŠO˜×"Ñ"Ó$€DÙ5=Œb�f‰f�\ 3¨Ô1ÀCÇJÁJÈsÃOÒD]ÐW]€Gð Ô4°fÔ>TÐVZÐ[ØÔ+¨VÔ5KÈUÐSØÔ'¨Ô1GÈÐNð<Pò WÑ7ˆˆe�[ (¨Kð —‘ 4 &¨ 
Ô+¨s°e©|¸|ÈtÒ?SØØ ™,‘3¨Kˆð	Ù˜D hÔ/ˆDñ
 '¨4¯?©?¸d¸VÀ1¸:Ô+F‘4ÈEÐRZÉOˆÜÑ6°tÓ<¸hÈÈgÓVÒVðWð" ÒM±S 8¨C¨5Ñ!1¸n€MÜ$ T¨=Ó9€DÜ'×+Ñ+¨CÓ0ÒV°DÒVÐ<V€HÜÑ2°4Ó8¸(ÀJÐPWÒPhÐ[hÓiÐiøô ò 	Ø�wŠØÚð	ús   Â1
D=Ä=EÅEc             ƒ   óZ   K  — t        j                  t        | |||¬«      ƒ d{  –—† S 7 Œ­w)zDClassify and cache attachment bytes without blocking the event loop.rŸ  N)rÄ  rÅ  r°  )r¯  r†  r›  r   s       r   Úcache_media_bytes_asyncr²  î  s4   è ø€ ô ×"Ñ"ÜØØØØ!ô÷ ð ð ús   ‚"+¤)¥+c                   ó^   — e Zd ZU eed<   ej                  dz  ed<   eed<   eed<   ddœdd„Zy)	ÚTextDebounceStater‚   Nr   Úfirst_tsÚlast_ts©Úunlessc                ó¦   — | j                   �E| j                   |ur6| j                   j                  «       s| j                   j                  «        yyyy)z<Cancel the pending flush timer (if live and not ``unless``).N)r   ÚdoneÚcancel)r˜  r¸  s     r   Úcancel_timerzTextDebounceState.cancel_timer  sA   € à�9‰9Ð  T§Y¡Y°fÑ%<ÀTÇYÁYÇ^Á^ÔEUØ�I‰I×ÑÕð FVÐ%<Ð r   )r¸  zasyncio.Task | Noner	   N)	rJ  rK  rL  r:  rO  rÄ  ÚTaskr[  r¼  rf   r   r   r´  r´  ÿ  s)   … àÓØ
�,‰,˜Ñ
ÓØƒOØƒNà>Bö r   r´  ÚexistingÚnewc                 ó   — | r| › d|› �S |S )zC``existing\nnew`` when both non-empty; the non-empty one otherwise.rL  rf   )r¾  r¿  s     r   Ú_append_textrÁ    s   € á#+ˆhˆZ�r˜#˜ÐÐ4°Ð4r   c                   óN   — e Zd ZU dZeed<   eed<   eed<   eed<   eed<   eed<   y)	Ú_ExtractedResponsezLDeliverable parts of a handler response (see ``_extract_response_content``).Útext_contentr  Úmedia_filesÚlocal_filesÚforce_document_attachmentsÚpre_extractN)rJ  rK  rL  rM  rF   rO  r0  r  rf   r   r   rÃ  rÃ    s(   … áVØÓØƒLØÓØÓØ $Ó$ØÔr   rÃ  z4^(?:please\s+)?restart\s+(?:the\s+)?gateway[.!?\s]*$z=^(?:please\s+)?restart\s+(?:the\s+)?hermes\s+gateway[.!?\s]*$z(^(?:please\s+)?restart\s+hermes[.!?\s]*$Ú#_PLAINTEXT_GATEWAY_RESTART_PATTERNSc                 ó²  ‡— t        j                  t        «      5  | �| j                  t        j
                  k7  r
	 ddd«       y| j                  xs dj                  «       Š‰r‰j                  d«      r
	 ddd«       yt        t        | dd«      dd«      dk7  r
	 ddd«       yt        ˆfd„t        D «       «      rd| _        ddd«       y# 1 sw Y   yxY w)	a  Rewrite a tiny set of DM plaintext admin phrases (exact matches only) into slash commands so
    ``restart gateway`` never reaches the LLM/tool path (a self-restart from inside the running
    agent leaves the gateway stuck in ``draining`` waiting on that agent).Nr`   rg  r{   rr   rs   c              3   ó@   •K  — | ]  }|j                  ‰«      –— Œ y ­wr¤  )rJ  )rÈ   ÚpatternrÜ   s     €r   rË   z3coerce_plaintext_gateway_command.<locals>.<genexpr>.  s   øè ø€ ÒV wˆw�}‰}˜T×"ÑVùs   ƒz/restart)rP   rQ   rO   Úmessage_typer;  ÚTEXTrÜ   rz   r	  ra   rÒ   rÉ  )r‚   rÜ   s    @r   Ú coerce_plaintext_gateway_commandrÏ  "  sÂ   ø€ ô 
×	Ñ	œYÓ	'ñ 	$Øˆ=˜E×.Ñ.´+×2BÑ2BÒBØ÷	$ð 	$ð —
‘
Ò ˜b×'Ñ'Ó)ˆÙ�t—‘ sÔ+Ø÷	$ð 	$ô ”7˜5 (¨DÓ1°;ÀÓEÈÒMØ÷	$ð 	$ô ÓVÔ2UÔVÔVØ#ˆEŒJ÷	$÷ 	$ñ 	$ús   ›!CÁ2CÂ CÂ%CÃCc                   ó¤   — e Zd ZU dZeed<   eed<   eed<   eeeeef      ed<   eed<   eed<   eed<   d	Z	e
eeef      ed
<   edee   fd„«       Zy	)ÚExecApprovalPromptut  One exec-approval prompt, ready for a platform to render natively (see
    ``BasePlatformAdapter.send_exec_approval``). ``actions`` rows are ``(label, choice, style)``
    with ``choice`` in ``once`` / ``session`` / ``always`` / ``deny`` â€” the vocabulary
    ``tools.approval.resolve_gateway_approval`` accepts â€” and ``style`` in ``primary`` /
    ``danger`` / ``""``.rR  rW  rÜ   ÚactionsÚcommandÚdescriptionÚsmart_deniedNr|   r	   c                 óN   — | j                   D ��cg c]  \  }}}|‘Œ
 c}}S c c}}w r¤  )rÒ  )r˜  rÉ   Úchoices      r   ÚchoiceszExecApprovalPrompt.choicesB  s    € à+/¯<©<×8™<˜1˜f a’Ó8Ð8ùÓ8s   �!)rJ  rK  rL  rM  rF   rO  r,  r1  r  r|   r-  r+  r.  ÚpropertyrØ  rf   r   r   rÑ  rÑ  2  st   … ñð
 ƒLØÓØ
ƒIØ�%˜˜S #˜Ñ&Ñ'Ó'ØƒLØÓØÓØ)-€Hˆh�t˜C ˜H‘~Ñ&Ó-àð9˜˜c™ò 9ó ñ9r   rÑ  c                   ó–   — e Zd ZU dZeed<   dZee   ed<   dZ	ee   ed<   dZ
eed<   dZeed<   dZee   ed	<   d
Zeed<   dZee   ed<   y)Ú
SendResultzResult of sending a message.ÚsuccessNrv   r   Úraw_responseFÚ	retryableÚretry_afterrf   Úcontinuation_message_idsÚ
error_kind)rJ  rK  rL  rM  r  rO  rv   r-  rF   r   rÝ  r.  rÞ  rß  r[  rà  Útuplerá  rf   r   r   rÛ  rÛ  G  sg   … á&ØƒMØ $€J�˜‘Ó$Ø€Eˆ8�C‰=Óð €L�#ÓØ€IˆtÓØ#'€K�˜%‘Ó'ð ')Ð˜eÓ(ð !%€J�˜‘Ô$r   rÛ  g      N@>   ÚunknownÚtoo_longÚ	forbiddenÚ	not_foundÚ	transientÚ
bad_formatÚrate_limited)zchat not found)zmessage to edit not foundzmessage to reply not foundzthread not foundÚtopic_deletedÚmessage_id_invalidr   Ú
error_textc                 óÀ   — |r|gng }| �7|j                  d„ t        | «      | j                  j                  fD «       «       dj	                  |«      j                  «       S )uš   Lowercased blob (error_text + str(exc) + exception class name) that both
    send-error classifiers match against â€” one builder so they can never drift.c              3   ó&   K  — | ]	  }|sŒ|–— Œ y ­wr¤  rf   ©rÈ   r'  s     r   rË   z_error_blob.<locals>.<genexpr>s  s   è ø€ ÒH˜1Âa”QÑHùs   ‚Šr}  )rC  rF   Ú	__class__rJ  r§   rb   )r   rì  Úpartss      r   Ú_error_blobrò  n  sP   € ñ 'ˆZ‰L¨B€EØ
€Ø�‰ÑH¤ S£¨3¯=©=×+AÑ+AÐ BÔHÔHØ�8‰8�E‹?× Ñ Ó"Ð"r   ÚblobÚneedlesc                 ó,   ‡ — t        ˆ fd„|D «       «      S )Nc              3   ó&   •K  — | ]  }|‰v –— Œ
 y ­wr¤  rf   )rÈ   Únró  s     €r   rË   z_any_in.<locals>.<genexpr>x  s   øè ø€ Ò*˜Qˆq�DŒyÑ*ùó   ƒ)rÒ   )ró  rô  s   ` r   Ú_any_inrù  w  s   ø€ ÜÓ* 'Ô*Ó*Ð*r   rä  c                 ó   — t        | ddd«      S )NÚmessage_too_longztoo longzmessage is too long©rù  ©Úbs    r   r  r  }  s   € œ7 1Ð&8¸*ÐF[Ó\€ r   rè  c                 óP   — t        | dddd«      xs d| v xr d| v xs
 d| v xr d| v S )	Nzcan't parse entitieszcant parse entitieszcan't find endzunsupported start tagÚentityÚparsezbad requestÚentitrü  rý  s    r   r  r  ~  sF   € Ü�Ð)Ð+@ÐBRÐTkÓlò 	1Ø˜ˆMÒ*˜g¨˜lò	1à˜QÐÒ/ 7¨a <ð r   rå  c           
      ó&   — t        | ddddddd«      S )Nrå  zbot was blockedzblocked by the userzuser is deactivatedznot enough rightszhave no rightsznot a memberrü  rý  s    r   r  r  ‚  s!   € œGØ	ˆ;Ð)Ð+@ÐBWØÐ-¨~ó?€ r   ræ  c                 ó.   — t        | gt        ¢t        ¢­Ž S r¤  )rù  Ú _CHAT_LEVEL_NOT_FOUND_SUBSTRINGSÚ_SUBCHAT_NOT_FOUND_SUBSTRINGSrý  s    r   r  r  …  s   € œG AÐiÔ(HÐiÔKhÒi€ r   ré  c                 ó    — t        | dddd«      S )NÚfloodztoo many requestszretry afterz
rate limitrü  rý  s    r   r  r  †  s   € œw q¨'Ð3FÈÐWcÓd€ r   rç  c                 ó&   — t        | gt        ¢d‘­Ž S )NÚconnecttimeout)rù  Ú_RETRYABLE_ERROR_PATTERNSrý  s    r   r  r  ‡  s   € œG AÐTÔ(AÐTÐCSÒT€ r   Ú_SEND_ERROR_CLASSIFIERSc                 óN   ‡— t        | |«      Št        ˆfd„t        D «       d«      S )z²Map a send exception / error string to a :data:`SEND_ERROR_KINDS` value; anything
    unrecognized is ``"unknown"`` so an unclassified failure is never mistaken for a benign one.c              3   ó:   •K  — | ]  \  }} |‰«      sŒ|–— Œ y ­wr¤  rf   )rÈ   r‘   Úmatchesró  s      €r   rË   z&classify_send_error.<locals>.<genexpr>Ž  s   øè ø€ ÒT™-˜$ ÁgÈdÅm”ÑTùs   ƒ”rã  )rò  Únextr  ©r   rì  ró  s     @r   Úclassify_send_errorr  Š  s&   ø€ ô �s˜JÓ'€DÜÓTÔ+BÔTÐV_Ó`Ð`r   c                 ó`   — t        | |«      }t        |gt        ¢­Ž  xr t        |gt        ¢­Ž S )zèWhether a ``not_found`` failure means the *whole chat* is gone (only that marks a target
    dead; a deleted topic / edited-away message leaves the parent chat reachable). When both
    markers are present the sub-chat reading wins.)rò  rù  r  r  r  s      r   Úis_chat_level_not_foundr  ‘  s;   € ô �s˜JÓ'€DÜ˜Ð=Ô<Ò=Ð=ò AÜ˜Ð@Ô?Ò@ðBr   c                   ó\   ‡ — e Zd ZU dZee   ed<   ddedee   fˆ fd„Ze	defd„«       Z
ˆ xZS )ÚEphemeralReplyaf  System-notice reply that auto-deletes after ``ttl_seconds`` on platforms implementing
    ``delete_message`` (others leave it). ``None`` ttl uses ``display.ephemeral_system_ttl``
    (``0`` disables globally). Subclassing ``str`` keeps it transparent to everything that
    treats handler results as text; ``isinstance`` lets the send path schedule deletion.Úttl_secondsrÜ   c                 ó6   •— t         ‰| �  | |«      }||_        |S r¤  )ÚsuperÚ__new__r  )ÚclsrÜ   r  Úinstancerð  s       €r   r  zEphemeralReply.__new__¡  s    ø€ Ü‘7‘? 3¨Ó-ˆØ*ˆÔØˆr   r	   c                 ó,   — t         j                  | «      S )z6The underlying text (explicit form of ``str(reply)``).)rF   Ú__str__r—  s    r   rÜ   zEphemeralReply.text¦  s   € ô �{‰{˜4Ó Ð r   r¤  )rJ  rK  rL  rM  r-  rN  rO  rF   r  rÙ  rÜ   Ú__classcell__)rð  s   @r   r  r  š  sG   ø… ñ\ð ˜#‘Óñ˜3ð ¨X°c©]õ ð
 ð!�cò !ó ô!r   r  ©Ú
merge_textÚpending_messagesr!  c                ó†  — | j                  |«      }|�r(t        |dd«      }|t        j                  k(  }|j                  t        j                  k(  }|xr |}t        |j                  «      }	dt        dt        t        t
              fd„}
g }|	r |
|«      |_
         |
|«      }|s|j                  s|	�r |s|	re|j                  j                  |j                  «       |j                  j                  |j                  «       |j                  j                  |«       |j                  r/t        j                  |j                  |j                  «      |_        |s|rt        j                  |_        nA|t        j                   k(  r.|j                  t        j                   k7  r|j                  |_        dD ]  }t#        ||«      sŒt%        ||«       Œ y|t        j                   k(  xr |j                  t        j                   k(  }|r4|r2|j                  r%t'        |j                  |j                  «      |_        y|| |<   y)zÃStore or merge a pending event: photo bursts/albums merge into the queued event so the next
    turn sees the whole burst; with ``merge_text`` rapid TEXT follow-ups append instead of
    replace.rÍ  NÚmsgr	   c                 ó†   — t        t        | dg «      xs g «      }|d gt        | j                  «      t        |«      z
  z  z   S )NÚmedia_text_inlined)r0  ra   r²   Ú
media_urls)r$  Úflagss     r   Ú_padded_inline_flagsz9merge_pending_message_event.<locals>._padded_inline_flags¹  s?   € Üœ Ð&:¸BÓ?ÒEÀ2ÓFˆEØ˜D˜6¤S¨¯©Ó%8¼3¸u»:Ñ%EÑFÑFÐFr   )Ú_gateway_pending_stt_textÚ _gateway_pending_stt_transcripts)r�   ra   r;  ÚPHOTOrÍ  r  r'  r:  r,  r-  r&  rC  Úmedia_typesrÜ   rc  Ú_merge_captionrÎ  ÚhasattrÚdelattrrÁ  )r"  rW  r‚   r!  r¾  Úexisting_typeÚexisting_is_photoÚincoming_is_photoÚ
both_photoÚincoming_has_mediar)  Úincoming_inline_flagsÚattrÚ	both_texts                 r   Úmerge_pending_message_eventr9  ¬  sõ  € ð
  ×#Ñ# KÓ0€HÚÜ ¨.¸$Ó?ˆØ)¬[×->Ñ->Ñ>ÐØ!×.Ñ.´+×2CÑ2CÑCÐØ&Ò<Ð+<ˆ
Ü! %×"2Ñ"2Ó3Ðð	G¤lð 	G´t¼HÄT¹NÑ7Kó 	Gð 79ÐÙÙ*>¸xÓ*HˆHÔ'Ù$8¸Ó$?Ð!ñ ˜×,Ò,Ò0BÙÑ/Ø×#Ñ#×*Ñ*¨5×+;Ñ+;Ô<Ø×$Ñ$×+Ñ+¨E×,=Ñ,=Ô>Ø×+Ñ+×2Ñ2Ð3HÔIØ�zŠzÜ 3× BÑ BÀ8Ç=Á=ÐRW×R\ÑR\Ó ]�”Ù Ñ$5Ü(3×(9Ñ(9�Õ%Ø¤+×"2Ñ"2Ò2°u×7IÑ7IÌ[×M]ÑM]Ò7]Ø(-×(:Ñ(:�Ô%ð Zò ,�Ü˜8 TÕ*Ü˜H dÕ+ð,ð Ø!¤[×%5Ñ%5Ñ5Ò`¸%×:LÑ:LÔP[×P`ÑP`Ñ:`ˆ	Ù™)Ø�zŠzÜ ,¨X¯]©]¸E¿J¹JÓ G�”ØØ$)Ð�[Ò!r   )	ÚconnecterrorÚconnectionerrorÚconnectionresetÚconnectionrefusedr
  Únetworkzbroken pipeÚremotedisconnectedÚeoferrorÚconfig_extraÚ
channel_idÚ	parent_idc                 óÐ   — | j                  d«      xs i }t        |t        «      sy||fD ]9  }|r|j                  |«      nd}|€Œt        |«      j	                  «       x}sŒ7|c S  y)z¹Per-channel ephemeral prompt from ``config.extra["channel_prompts"]``: exact *channel_id*
    first, then *parent_id* (threads inherit the parent prompt). Blank prompts count as absent.Úchannel_promptsN)r�   rŽ   r†   rF   rz   )rA  rB  rC  Úpromptsrì   Úprompts         r   Úresolve_channel_promptrH  å  st   € ð ×ÑÐ0Ó1Ò7°R€GÜ�gœtÔ$ØØ˜IÐ&ò ˆÙ%(�—‘˜SÔ!¨dˆØÑ¬S°«[×->Ñ->Ó-@Ð#@ 6Ñ#@ØŠMðð r   c                 óD  ‡	— | j                  d«      xs g }t        |t        «      r|sy||fD �ch c]  }|sŒt        |«      ’Œ }}|sy|D ]Í  }t        |t        «      rt        |j                  dd«      «      |vrŒ1|j                  d«      xs |j                  d«      }t        |t        «      r'|j                  «       r|j                  «       gc S dc S t        |t        «      sŒ�|sŒ t        j                  ˆ	fd„|D «       «      }t        |«      xs dc S  yc c}w )a  Auto-loaded skill(s) for a channel/thread from ``channel_skill_bindings`` (entries
    ``{id: "<channel/forum id>", skills: [...]}``; ``skill: "<name>"`` also accepted). Exact
    *channel_id* first, then *parent_id* (threads inherit). Deduplicated ordered list or None.Úchannel_skill_bindingsNÚidr`   ÚskillsÚskillc              3   ój   •K  — | ]*  }t        |t        «      sŒ|j                  «       xŠsŒ'‰–— Œ, y ­wr¤  )rŽ   rF   rz   )rÈ   r  Únms     €r   rË   z)resolve_channel_skills.<locals>.<genexpr>  s1   øè ø€ ò !YØ¬°D¼#Õ)>È$Ï*É*Ë,ÐDVÀBÑDV”ñ!Yùs   ƒ3™3¬3)r�   rŽ   r0  rF   r†   rz   r1  )
rA  rB  rC  Úbindingsrì   Úids_to_checkrŽ  rL  ÚseenrO  s
            @r   Úresolve_channel_skillsrS  ò  s  ø€ ð
 ×ÑÐ 8Ó9Ò?¸R€HÜ�h¤Ô%©XØØ)3°YÐ(?ÖG Â3”C˜•HÐG€LÐGÙØØò 	&ˆÜ˜%¤Ô&¬#¨e¯i©i¸¸bÓ.AÓ*BÈ,Ñ*VØØ—‘˜8Ó$Ò:¨¯	©	°'Ó(:ˆÜ�fœcÔ"Ø'-§|¡|¤~�F—L‘L“NÐ#Ò?¸4Ò?Ü�fœdÕ#ªÜ—=‘=ó !YØ%ô!Yó YˆDä˜“:Ò% Ò%ð	&ð ùò Hs
   °D¸DrŽ  c                 óJ   — t        | t        «      rt        | «      dk(  r| S d| fS )zc``(generation, callback)`` from a post-delivery slot; legacy bare callbacks have no
    generation.r]   N)rŽ   râ  r²   )rŽ  s    r   Ú_split_post_delivery_entryrU  
  s&   € ô ˜u¤eÔ,´°U³¸q²ˆ5ÐSÀtÈUÀmÐSr   ÚobjÚfactoryc                 óL   — t        | |d«      }|€ |«       }t        | ||«       |S )už   ``getattr(obj, name)`` or create it via ``factory`` â€” the getattr-guard for
    tests that build adapters via ``object.__new__`` and never run ``__init__``.N)ra   Úsetattr)rV  r  rW  r_   s       r   Ú
_lazy_attrrZ    s/   € ô �C˜˜tÓ$€EØ€}Ù“	ˆÜ��T˜5Ô!Ø€Lr   c            %       óÖ  — e Zd ZU dZdZeed<   dZeed<   dede	e   ddfd	„Z
d
Zeed<   dZeed<   dZeed<   dZeed<   d
Zeed<   dZeed<   dZdedefd„Zedeegef   fd„«       Zdedefd„Zdedeegef   fd„Zedefd„«       Zedefd„«       Z	 	 �d­de	e   de	eeef      de	e   defd„Z�d®dede	eeef      defd„Z de	e   fd„Z!	 �d®ded edede	eeef      de"f
d!„Z#d"ed#eddfd$„Z$d%d&d'œd"ed(ed)ede	e   fd*„Z%d+d,defd-„Z& ed.„ «      Z' ed/„ «      Z( ed0„ «      Z) ed1„ «      Z*dedefd2„Z+d3ed ge,d   dz  f   ddfd4„Z-d5Z.edefd6„«       Z/dd7œd8e	e   ddfd9„Z0�d¯d:„Z1�d¯d;„Z2d<ed=ed>eddfd?„Z3d@eddfdA„Z4�d¯dB„Z5dCedDedEedefdF„Z6�d¯dG„Z7dZ8eedH<   dZ9e	e:   edI<   �d®dJeddfdK„Z;�d¯dL„Z<edefdM„«       Z=edefdN„«       Z>d3e?ddfdO„Z@d3e	eeeef   ege,d   f      ddfdP„ZAdQe	eege	e   f      ddfdR„ZBd"eCddfdS„ZDd3e	eeCege,e   f      ddfdT„ZEd3e	eeeef   ge,d   f      ddfdU„ZFdVe	eee	e   e	e   gef      ddfdW„ZG	 	 �d°dddXœdYe	e   de	e   de	e   dZed[e	e   de	e   fd\„ZHd]eddfd^„ZId_e	e   ddfd`„ZJde	e   fda„ZKdbe	dc   fdd„ZLd"dedefdf„ZM�d®dbe	e   de	e   fdg„ZNdhZOeedi<   djZPeQedk<   djZReQedl<   dmZSeQedn<   doZTeQedp<   dqZUeQedr<   dsZVeQedt<   djdduœdvedweQdxeQdye	eQ   deQf
dz„ZW�d¯d{„ZXd"dedefd|„ZYdbdcdefd}„ZZd"dedefd~„Z[�d±d„Z\d€e	de   deQfd�„Z]dvede	de   fd‚„Z^�d±dƒ„Z_dveddfd„„Z`dveddfd…„Zad†ede	e:   fd‡„Zbd†ede	e:   fdˆ„Zceddd‰œdŠedefd‹„«       Zeed�d¯dŒ„«       Zfed	 	 �d°deded�e	e   de	eeef      de"f
dŽ„«       ZgdZheed�<   d�ed‘ede	e   fd’„Zidd“œded”eded•ede"f
d–„Zjded”edefd—„Zkdefd˜„Zlded”ed™eddfdš„Zmd›en› dœ�Zoeed�<   džZpeedŸ<   d Zqeed¡<   er› d¢�Zseed£<   dœZteed¤<   d¥Zueed¦<   d§Zveed¨<   d©Zweedª<   ex�d²ded«ed¬edefd­„«       Zydedefd®„Zz�d³ded«ed¬ed¯e	eegef      def
d°„Z{d±ed²edefd³„Z|defd´„Z}	 �d´dµed±ed²edefd¶„Z~d·d¸d¹dºd»œZeeef   ed¼<   d½d¾d¿œZ€eeef   edÀ<   dÁedÂed²ede�e‚eeef      fdÃ„Zƒe„defdÄ„«       Z…	 	 	 �dµdedµed†ed±ede	eeef      dÁedÂed²ede"fdÅ„Z†dÆdÇde"fdÈ„Z‡exdÉeˆdÊedËeddÌfdÍ„«       Z‰	 �d®dedÎed=ed†edÏede	eeef      de"fdÐ„ZŠ	 �d®dedÑedÒe	eˆ   dÓed†ede	eeef      de"fdÔ„Z‹	 	 �d°dedYe	e   ded�e	e   de	eeef      de"fdÕ„ZŒ�d®deddfdÖ„Z�deddfd×„ZŽexdQed‘edØedÙedef
dÚ„«       Z��d®deddfdÛ„Z�	 �d¶dedÜe�e‚eef      de	eeef      dÝeQde"f
dÞ„Z‘	 	 �d­dedßedàe	e   d�e	e   de	eeef      de"fdá„Z’	 	 �d­dedâedàe	e   d�e	e   de	eeef      de"fdã„Z“exdäedefdå„«       Z”exdede‚e�e‚eef      ef   fdæ„«       Z•	 	 �d­dedçedàe	e   d�e	e   de	eeef      de"fdè„Z–ddéœdêedëedìededàe	e   d�e	e   de	eeef      díe	e   de"fdî„Z—ddddïœdedede	e"   fdð„Z˜dddddñœdedòede"fdó„Z™�d·ddddôœdõedöe	e   de	e   fd÷„Zš�d®dddøœdefdù„Z›dedefdú„Zœdedçede"fdû„Z�dedüeždefdý„ZŸ	 �d®dedüežde	eeef      de	e    fdþ„Z¡dÿe �d e¢ddf�d„Z£d�dœdÿe �deddf�d„Z¤�d®dÿe �de	e   ddf�d„Z¥�d®d�dœd†edz  �ded"ededz  f�d	„Z¦�d®d�dœd†edz  �ded"eddf�d
„Z§�d®d�dœd†edz  �ded"edef�d„Z¨	 	 �d­de�dedàe	e   d�e	e   de	eeef      de"f�d„Z©	 	 	 �d¸de�dedàe	e   díe	e   d�e	e   de	eeef      de"f�d„Zªdd�dœde�de�dede	eeef      ddf
�d„Z«	 	 �d­de�dedàe	e   d�e	e   de	eeef      de"f�d„Z¬ex�d·dìed†ede	e   f�d„«       Z­ex	 	 �d¹d†e�de	e�e®      de�e‚eef      f�d„«       Z¯ex�d·d†ede�e   f�d„«       Z°exdedef�d„«       Z±exdedef�d„«       Z²exdede‚e�e‚eef      ef   f�d„«       Z³exdedef�d„«       Z´exdede‚e�e   ef   f�d„«       Zµ	 	 �dºde�deQ�d e¶�jn                  dz  ddf�d!„Z¸	 �d®d�d"�d#�d$œde�d%e¶�jr                  dz  �d&eQ�d'eddf
�d(„Zº�d®deddf�d)„Z»deddf�d*„Z¼deddf�d+„Z½�d®d†ededdf�d,„Z¾d�d-œd†edVe�d.edz  ddf�d/„Z¿ex�d0edeg e,d   f   f�d1„«       ZÀd�d-œd†e�d.edz  dedz  f�d2„ZÁdZÂe	e   e�d3<   dZÃe	e   e�d4<   dZÄe	e   e�d5<   d"eCddf�d6„ZÅd"eC�d7eÆddf�d8„ZÇ�d9e�d:e�d;eddf�d<„ZÈex�de	e   def�d=„«       ZÉex�de	e   def�d>„«       ZÊex�de	e   def�d?„«       ZË�d@ede‚e	e   ef   f�dA„ZÌd�dBœd"eC�dCe	e   ddf�dD„ZÍdbe	eÎ   f�dE„ZÏdbe	eÎ   dd f�dF„ZÐ	 	 �d»deded�e	e   de�dGe�dHeQd�dIf�dJ„ZÑ�dK�dIdef�dL„ZÒex�dK�dIdef�dM„«       ZÓde�dK�dId�e	e   ded�dNf
�dO„ZÔdeded�e	e   ded�dIf
�dP„ZÕex�dQe	e   �dRedef�dS„«       ZÖde®ee×f   f�dT„ZØd"eCdef�dU„ZÙ�dVeCd"eCdef�dW„ZÚd†edeQf�dX„ZÛd†ed"eCddf�dY„ZÜd†e�dZeQddf�d[„ZÝd†edef�d\„ZÞd†eddf�d]„Zßd�d^œd†e�d_e	e¶�jn                     ddf�d`„Zàd†edef�da„Zád†edef�db„Zâd�dcœd"eCd†e�dde	e¶�jn                     def�de„Zãd†e�dfedef�dg„Zäd
d
�dhœd†e�die�djeddf�dk„Zåd†e�dle¶�jn                  ddf�dm„Zæd"eCd†e�dneddf�do„Zçd"eCddf�dp„Zèd"eCd†eddf�dq„ZédeQf�dr„Zê�dsede‚e�e   e	e   f   f�dt„Zëd"eCd†e�dde¶�jn                  �dse�dueˆdef�dv„Zìd"eC�dse�dwe�dxedeeef   �dyedef�dz„Zíd"eCd†e�dse�d{d �d|ede	e   f�d}„Zî�d~e�dKed"eC�d{d ddf
�d„Zïd"eC�dueˆ�d€eˆ�d�edÝeQdeeef   �dyeddf�d‚„Zðd"eCdÜeˆdeeef   dÝeQ�dyeddf�dƒ„Zñd�d„œd"eCd†e�dsedeeef   d�e	e   �d|ed�d…f�d†„Zòd"eCd†e�dsedeeef   �d|e�d‡e�dyeddf�dˆ„Zód"eC�d‰eôde	e®   f�dŠ„Zõd"eC�d‹�dŒdeeef   �d�e�dyeddf�dŽ„Zöd"eC�dde¶�jn                  de	e®   de	e¶�jr                     f�d�„Z÷�d@ed"eCd†e�d|ed�dŒf
�d�„Zød†e�dde¶�jn                  ddf�d‘„Zùd†e�dde¶�jn                  ddf�d’„Zúd"eCd†eddf�d“„Zû�d”eCd†eddf�d•„Züd†eddf�d–„Zýd†e�dde	e¶�jn                     ddf�d—„Zþ�d¯�d˜„Zÿd†edef�d™„�Z d†ede	eC   f�dš„�Z	 	 	 	 	 	 	 	 �d¼de�d›e	e   dedYe	e   �dœe	e   d[e	e   �d�e	e   �dže	e   �dŸe	e   dZe�d e	e   �d¡e	e   d�e	e   d”e	e   �d¢e�d£e�d¤e	e   deÎf$�d¥„�Zeddedeeef   f�d¦„«       �Zdbdcde	e�e      f�d§„�Zdedef�d¨„�Zex	 	 �d½de�d©e�dªe	�d«   de�e   f�d¬„«       �Zy(¾  rc  zLBase class for platform adapters: connect/auth, receive, send, handle media.FÚsupports_code_blocksÚsupports_status_textrR  rÜ   r	   Nc                 ó~   — t        | dt        «      }|r||t        |«      <   y|j                  t        |«      d«       y)zÁSet or clear (``None``) the live working-state phrase for a chat. In-memory only: the
        next typing refresh renders it; a no-op store on adapters that never read
        ``_status_text``.Ú_status_textN)rZ  r†   rF   Úpop)r˜  rR  rÜ   Ústores       r   Úset_status_textz#BasePlatformAdapter.set_status_text&  s5   € ô ˜4 ´Ó6ˆÙØ"&ˆE”#�g“,Òà�I‰I”c˜'“l DÕ)r   TÚsupports_async_deliveryÚsplits_long_messagesrg  Útyped_command_prefixÚsupports_inchannel_continuableÚinteractive_resumeÚserves_profile_prefixÚconfigrc   c                 óœ  — || _         || _        d | _        d| _        d | _        d | _        d | _        d\  | _        | _        d | _	        d | _
        d | _        t        «       | _        dx| _        | _        i | _        i | _        i | _        i | _        i | _        d| _        t,        | _        t0        | _        d | _        i | _        t        «       | _        i | _        t        «       | _        d | _        d | _         d | _!        d | _"        d| _#        t        «       t        «       c| _$        | _%        t        «       | _&        t        «       | _'        i | _(        y )NF)FTÚ	interrupt))ri  rc   Ú_message_handlerÚ_no_message_handler_loggedÚ_reaction_handlerÚ_platform_event_handlerÚ_topic_recovery_fnÚ_runningÚ_fatal_error_retryableÚ_fatal_error_codeÚ_fatal_error_messageÚ_fatal_error_handlerÚsetÚ_detached_fatal_tasksÚ_platform_lock_takeover_allowedÚ!_platform_lock_takeover_attemptedÚ_active_sessionsÚ_pending_messagesÚ_pending_text_batchesÚ_pending_text_batch_tasksÚ_session_tasksÚ_busy_text_modeÚ"DEFAULT_BUSY_TEXT_DEBOUNCE_SECONDSÚ_busy_text_debounce_secondsÚ"DEFAULT_BUSY_TEXT_HARD_CAP_SECONDSÚ_busy_text_hard_cap_secondsÚ_human_delay_range_msÚ_text_debounceÚ_background_tasksÚ_post_delivery_callbacksÚ_expected_cancelled_tasksÚ_busy_session_handlerÚ_owner_profileÚ_shared_listener_profileÚ_authorization_checkÚ_auto_tts_defaultÚ_auto_tts_enabled_chatsÚ_auto_tts_disabled_chatsÚ_streaming_tts_completed_turnsÚ_typing_pausedr_  )r˜  ri  rc   s      r   Ú__init__zBasePlatformAdapter.__init__N  sO  € ØˆŒØ ˆŒØ:>ˆÔØ05ˆÔ'ØX\ˆÔàcgˆÔ$àLPˆÔØ5@Ñ2ˆŒ�tÔ2Ø04ˆÔØ37ˆÔ!ØimˆÔ!ô
 +.«%ˆÔ"àX]Ð]ˆÔ,¨tÔ/Uð ;=ˆÔØ:<ˆÔØ>@ˆÔ"ØBDˆÔ&Ø79ˆÔð
 %0ˆÔÜ2TˆÔ(Ü2TˆÔ(ð AEˆÔ"Ø<>ˆÔä47³EˆÔð 9;ˆÔ%Ü<?»EˆÔ&Ø_cˆÔ"à-1ˆÔð 8<ˆÔ%àcgˆÔ!à',ˆÔÜFIÃeÌSËUÐCˆÔ$ dÔ&Cô
 9<»ˆÔ+ä#&£5ˆÔà,.ˆÕr   c                 ó   — t         S )z„Length function for message size; override where the platform counts
        differently from ``len`` (Telegram: UTF-16 code units).r¸   r—  s    r   Úmessage_len_fnz"BasePlatformAdapter.message_len_fn�  s	   € ô ˆ
r   c                 ó"   ‡ — t        ˆ fd„d«      S )zÏPer-chat max length in ``message_len_fn_for_chat`` units: the adapter's
        ``MAX_MESSAGE_LENGTH`` (4096 when absent); the relay adapter overrides since it fronts N
        platforms with different caps.c                  ó8   •— t        t        ‰ dd«      xs d«      S )NÚMAX_MESSAGE_LENGTHé   )rN  ra   r—  s   €r   r  zABasePlatformAdapter.max_message_length_for_chat.<locals>.<lambda>š  s   ø€ ¤3¤w¨tÐ5IÈ4Ó'PÒ'XÐTXÓ#Y€ r   r˜  ©ri   ©r˜  rR  s   ` r   Úmax_message_length_for_chatz/BasePlatformAdapter.max_message_length_for_chat–  s   ø€ ô ÓYÐ[_Ó`Ð`r   c                 ó   — | j                   S )z�Per-chat length function (companion to max_message_length_for_chat); the relay
        adapter overrides it per the chat's fronting platform.)r”  rš  s     r   Úmessage_len_fn_for_chatz+BasePlatformAdapter.message_len_fn_for_chatœ  s   € ð ×"Ñ"Ð"r   c                  ó   — y)u  Whether this adapter enforces its own config-driven access policy at intake
        (``dm_policy``/``group_policy``/``allow_from``: WeCom, Weixin, QQBot, WhatsAppâ€¦). The
        gateway env allowlist runs *after* the adapter; without one it trusts this flag ONLY when
        the effective policy is a real ``"allowlist"`` â€” never ``"open"`` (the default), which would
        be a network-exposed fail-open (SECURITY.md Â§2.6). Open access still requires
        ``{PLATFORM}_ALLOW_ALL_USERS`` / ``GATEWAY_ALLOW_ALL_USERS``.Frf   r—  s    r   Úenforces_own_access_policyz.BasePlatformAdapter.enforces_own_access_policy¡  s   € ð r   c                  ó   — y)u`  Whether inbound was already authorized by a TRUSTED UPSTREAM (relay only): the Team
        Gateway connector authenticates the WebSocket and resolves owner-only author binding BEFORE
        delivery, so the no-allowlist default-deny would be wrong. Authorization DELEGATED, not
        ABSENT â€” every network-exposed direct adapter leaves it False.Frf   r—  s    r   Úauthorization_is_upstreamz-BasePlatformAdapter.authorization_is_upstream«  ó   € ð r   rr   r|   c                  ó   — y)zÿWhether native streaming-draft updates (``send_draft``) work for this chat type;
        ``chat_id`` lets the relay adapter answer per negotiated capabilities. Consumers
        fall back to ``send`` + ``edit_message`` when False or ``send_draft`` raises.Frf   )r˜  rr   r|   rR  s       r   Úsupports_draft_streamingz,BasePlatformAdapter.supports_draft_streaming³  r¢  r   rß  c                  ó   — y)z±Whether the stream consumer should finalize with a *fresh* final message (best-effort
        deleting the preview) instead of final-editing it (Telegram: keeps rich rendering).Frf   )r˜  rß  r|   s      r   Úprefers_fresh_final_streamingz1BasePlatformAdapter.prefers_fresh_final_streaming»  ó   € ð r   c                  ó   — y)zþMax single-message length (``message_len_fn`` units) the stream consumer may
        accumulate before splitting, for rich send/draft paths exceeding the legacy cap
        (Telegram Rich Messages: 32,768 vs 4,096). ``None`` = use ``MAX_MESSAGE_LENGTH``.Nrf   r—  s    r   Ústreaming_overflow_limitz,BasePlatformAdapter.streaming_overflow_limitÀ  s   € ð r   Údraft_idc              ƒ   óL   K  — t        t        | «      j                  › d�«      ‚­w)u{  Send or update an animated streaming-draft preview. Reuse one non-zero ``draft_id``
        across a response so the platform animates (different responses need different ids). Drafts
        have no message_id (no edit/reply/delete) â€” the final answer goes out as a regular ``send``.
        Must be overridden by adapters returning True from :meth:`supports_draft_streaming`.z does not implement send_draft)ÚNotImplementedErrorÚtyperJ  )r˜  rR  rª  rß  r|   s        r   Ú
send_draftzBasePlatformAdapter.send_draftÆ  s&   è ø€ ô "¤T¨$£Z×%8Ñ%8Ð$9Ð9WÐ"XÓYÐYùs   ‚"$r‚   Úsinkc                 ó<  — ddl m}m}m} t	        ||«      r(|j
                  r|j                  |j
                  «       yt	        ||«      r|j                  s|j                  «        yt	        ||«      r)|j
                  r|j                  |j
                  «       yyy)z‹Render a MessageChunk / MessageStop / Commentary onto the sink (a
        GatewayStreamConsumer), mapping 1:1 onto its existing primitives.r   )ÚMessageChunkÚMessageStopÚ
CommentaryN)
Úgateway.stream_eventsr±  r²  r³  rŽ   rÜ   Úon_deltaÚfinalÚon_segment_breakÚon_commentary)r˜  r‚   r¯  r±  r²  r³  s         r   Úrender_message_eventz(BasePlatformAdapter.render_message_eventÑ  sq   € ÷ 	PÑOÜ�e˜\Ô*¨u¯zªzØ�M‰M˜%Ÿ*™*Õ%Ü˜˜{Ô+°E·K²Kà×!Ñ!Õ#Ü˜˜zÔ*¨u¯zªzØ×Ñ˜uŸz™zÕ*ð 0:Ð*r   Úallé(   )ÚmodeÚpreview_max_lenr¼  r½  c                ób  — ddl m} t        ||«      syddlm}m}  ||j                  d¬«      › d|j                  › �}|dk(  r{|j                  roddl}|j                  |j                  d	t        ¬
«      }	|dkD  rt        |	«      |kD  r|	d|dz
   dz   }	|› dt        |j                  j                  «       «      › d|	› �S |j                  s|› d�S |dk(  r|› d|j                  › d�S |dkD  r|nd}
 ||j                  |j                  |j                  |
¬«      }|› d| j                  |«      › d�S )a   Rendered chrome for a ToolCallChunk, or None to eat it (adapters without editing/rich
        text override to None). ``mode``: tool-progress mode ("all"/"new"/"verbose");
        ``preview_max_len`` mirrors ``tool_preview_length`` (0 = no cap in verbose).r   )ÚToolCallChunkN)Úget_tool_emojiÚprepare_tool_previewu   âš™ï¸�)rh   r}  ÚverboseF)Úensure_asciirh   rÛ   rj  ú(z)
z: "ú"r»  )Úfallbackre  )r´  r¿  rŽ   Úagent.displayrÀ  rÁ  Ú	tool_nameÚargsr‰  ÚdumpsrF   r²   r0  ÚkeysÚpreviewÚformat_tool_preview)r˜  r‚   r¼  r½  r¿  rÀ  rÁ  Úheadr‰  Úargs_strÚcapÚprepareds               r   Úformat_tool_eventz%BasePlatformAdapter.format_tool_eventÝ  s2  € õ 	8Ü˜% Ô/ØßFÙ  §¡¸(ÔCÐDÀAÀeÇoÁoÐEVÐWˆØ�9Ò §¢ÛØ—z‘z %§*¡*¸5Ì#�zÓNˆHØ Ò"¤s¨8£}°Ò'FØ#Ð$8 _°qÑ%8Ð9¸EÑA�Ø�V˜1œT %§*¡*§/¡/Ó"3Ó4Ð5°S¸¸
ÐCÐCØ�}Š}Ø�V˜3�<ÐØ�9ÒØ�V˜3˜uŸ}™}˜o¨QÐ/Ð/à!0°1Ò!4‰o¸"ˆÙ'Ø�O‰O˜UŸZ™Z°%·-±-ÈôNˆà��s˜4×3Ñ3°HÓ=Ð>¸aÐ@Ð@r   rÌ  rD  c                 ó   — |j                   S )z“Platform-native formatting of a compact tool preview; rich-text adapters may use
        the preview's metadata (e.g. a URL shortened for display).r]  )r˜  rÌ  s     r   rÍ  z'BasePlatformAdapter.format_tool_previewö  s   € ð �|‰|Ðr   c                 ó   — | j                   d uS r¤  ©rt  r—  s    r   r  zBasePlatformAdapter.<lambda>û  s   € ¨D×,EÑ,EÈTÐ,Q€ r   c                 ó   — | j                   S r¤  rÕ  r—  s    r   r  zBasePlatformAdapter.<lambda>ü  s   € °×0IÑ0I€ r   c                 ó   — | j                   S r¤  )rs  r—  s    r   r  zBasePlatformAdapter.<lambda>ý  s   € ¨T×-CÑ-C€ r   c                 ó   — | j                   S r¤  )rr  r—  s    r   r  zBasePlatformAdapter.<lambda>þ  s   € °$×2MÑ2M€ r   c                 ól   — || j                   v xs% || j                  vxr t        | j                  «      S )u=  Whether auto-TTS fires for ``chat_id``: explicit ``/voice on|tts`` wins,
        then explicit ``/voice off``, then the global ``voice.auto_tts`` default.

        Decision layers (Issue #16007): 1. Explicit ``/voice on`` or ``/voice tts`` â†’ always fire (even if
        ``voice.auto_tts`` is False). 2. 3.
        )rŽ  r�  r  r�  rš  s     r   Ú_should_auto_tts_for_chatz-BasePlatformAdapter._should_auto_tts_for_chat   s<   € ð ˜$×6Ñ6Ð6ò [Ø˜4×8Ñ8Ð8ÒY¼TÀ$×BXÑBXÓ=Yð	[r   Úhandlerc                 ó   — || _         y r¤  )ru  ©r˜  rÛ  s     r   Úset_fatal_error_handlerz+BasePlatformAdapter.set_fatal_error_handler
  s
   € Ø$+ˆÕ!r   z@connected but not yet confirmed active; recovering in backgroundc                  ó   — y)zÅTrue while connect() succeeded but delivery is not confirmed.

        Adapters with a separately-proven receive path override this; the
        default adapter is either connected or not.
        Frf   r—  s    r   Úsend_path_degradedz&BasePlatformAdapter.send_path_degraded  s   € ð r   )Úlistener_baserá  c                ó´   — d| _         dx| _        | _        d| _        | j                  r| j                  «        y|rd|ini } | j                  	 dddddœ|¤Ž y)a  ``listener_base`` (``http://host:port``) is stamped by port-binders after a REAL bind: under the
        multiplexer it is the shared listener a served profile's ``/p/<profile>/`` mirror hangs off, and
        what the dashboard/Desktop report as that profile's api_server/webhook URL.TNrá  Ú	connected©Úplatform_stateÚ
error_codeÚerror_message)rã  )rq  rs  rt  rr  rà  Ú_mark_degradedÚ_write_runtime_status_safe)r˜  rá  Úextras      r   Ú_mark_connectedz#BasePlatformAdapter._mark_connected  sq   € ð ˆŒØ=AÐAˆÔ Ô!:Ø&*ˆÔ#Ø×"Ò"Ø×ÑÕ!á8E�_ mÑ4È2ˆEØ+ˆD×+Ñ+ØðgØ,7ÀDÐX\ñgØ`eógr   c                 óB   — | j                  ddd| j                  ¬«       y)zKPublish ``retrying`` for a running adapter whose delivery path is unproven.Úconnected_degradedÚretryingNrä  )ré  ÚDEGRADED_STATUS_MESSAGEr—  s    r   rè  z"BasePlatformAdapter._mark_degraded*  s'   € à×'Ñ'Ø Ø%ØØ×6Ñ6ð	 	(õ 	
r   c                 óV   — d| _         | j                  s| j                  ddd d ¬«       y y )NFÚdisconnectedrä  )rq  Úhas_fatal_errorré  r—  s    r   Ú_mark_disconnectedz&BasePlatformAdapter._mark_disconnected3  s5   € ØˆŒØ×#Ò#Ø×+Ñ+Ø¨~È$Ð^bð ,õ dð $r   ÚcodeÚmessagerÞ  c                óh   — d| _         ||c| _        | _        || _        | j	                  dd||¬«       y )NFÚfatalrä  )rq  rs  rt  rr  ré  )r˜  rô  rõ  rÞ  s       r   Ú_set_fatal_errorz$BasePlatformAdapter._set_fatal_error9  s<   € ØˆŒØ<@À'Ð9ˆÔ Ô 9Ø&/ˆÔ#Ø×'Ñ'¨ÀÐTXÐhoÐ'Õpr   Úcontextc                 óÌ  — 	 ddl m} t        | dd«      xs | j                  j                  } |d
d|i|¤Ž y# t
        $ r¢}t        | dt        «      }| j                  j                  |f|v}|j                  | j                  j                  |f«        |rt        j                  nt        j                  d|rdnd	z   || j                  j                  |«       Y d}~yd}~ww xY w)zPPublish runtime status; log preparation failures without disrupting the adapter.r   )Úpublish_runtime_statusÚ_runtime_status_platform_keyNrc   Ú_status_write_loggedz.Failed to write runtime status (%s) for %s: %sz" (further failures at debug level)r`   rf   )Úgateway.statusrû  ra   rc   r_   rO   rZ  rv  Úaddr   rL   r�  )r˜  rù  Úkwargsrû  Úplatform_keyr   ÚloggedÚfirsts           r   ré  z.BasePlatformAdapter._write_runtime_status_safe?  sË   € ð	3Ý=ô # 4Ð)GÈÓNÒeÐRV×R_ÑR_×ReÑReˆLÙ"ÑC¨LÐC¸FÓCøÜò 	3Ü Ð&<¼cÓBˆFØ—]‘]×(Ñ(¨'Ð2¸&Ð@ˆEØ�J‰J˜Ÿ™×+Ñ+¨WÐ5Ô6Ø6™uŒV�^Š^¬&¯,©,Ø@ÑlqÑDhÐwyÑzØ˜Ÿ™×,Ñ,¨c÷3ñ 3ûð		3ús   ‚58 ¸	C#ÁBCÃC#c              ƒ   óÄ  K  — | j                   }|sy  || «      }t        j                  |«      rqt        j                  |«      }t	        | dt
        «      }|j                  |«       |j                  |j                  «       	 t        j                  |«      ƒ d {  –—†  y y 7 Œ# t        j                  $ r' |j                  «       s|j                  t        «       ‚ w xY w­w)Nrw  )ru  rÄ  ÚiscoroutineÚensure_futurerZ  rv  rÿ  Úadd_done_callbackÚdiscardÚshieldÚCancelledErrorrº  r   )r˜  rÛ  rY   r   Ú_taskss        r   Ú_notify_fatal_errorz'BasePlatformAdapter._notify_fatal_errorO  s¿   è ø€ Ø×+Ñ+ˆÙØÙ˜“ˆÜ×Ñ˜vÔ&ô ×(Ñ(¨Ó0ˆDô   Ð&=¼sÓCˆFØ�J‰J�tÔØ×"Ñ" 6§>¡>Ô2ðÜ—n‘n TÓ*×*Ñ*ð 'ð +ùÜ×)Ñ)ò à—y‘y”{Ø×*Ñ*Ô+NÔOØð	üs0   ‚A?C ÂB# ÂB!ÂB# ÂC Â!B# Â#:CÃC ÚscopeÚidentityÚresource_descc                 óÌ  — ddl m}m}m} ||c| _        | _        d| j                  j                  i} ||||¬«      \  }}	|ry| j                  r‰| j                  s}t        |	t        «      rmd| _        d| _	         ||	«      }
|
�Ut        j                  d| j                  ||
«        ||||¬«      \  }}	|r#t        j                  d	| j                  ||
«       yt        |	t        «      r|	j!                  d
«      nd}
 ||	«      }|
rd|
› d�nd}|rd|› d|› �n|}|rd|› d�nd}|› d|› d|› �}t        j#                  d| j                  |«       | j%                  |› d�|d¬«       y)zúAcquire a scoped lock for this adapter; True on success. A live cross-HERMES_HOME
        holder is replaced only when the runner armed this adapter for its initial
        ``--replace`` connect (the status module validates ownership and terminates).r   )Úacquire_scoped_lockÚscoped_lock_owner_labelÚtake_over_scoped_lock_holderrc   ©r|   TFNuK   [%s] %s was held by gateway PID %d â€” explicit --replace handoff completedz)[%s] Acquired %s after taking over PID %dÚpidz (PID r•   r`   z	 by the 'z' profile gatewayz+ Stop that gateway first (hermes --profile z gateway stop).z Stop the other gateway first.z already in user–   z[%s] %sÚ_lock)rÞ  )rþ  r  r  r  Ú_platform_lock_scopeÚ_platform_lock_identityrc   r_   rx  ry  rŽ   r†   r   rL   r  Úinfor�   r   rø  )r˜  r  r  r  r  r  r  Ú	lock_metaÚacquiredr¾  Ú	owner_pidÚowner_profileÚpid_partÚholderÚremedyrõ  s                   r   Ú_acquire_platform_lockz*BasePlatformAdapter._acquire_platform_lockj  sˆ  € ÷	Xñ 	XàBGÈÐ?ˆÔ! 4Ô#?Ø §¡×!4Ñ!4Ð5ˆ	Ù0°¸È9ÔUÑˆ�(ÙØØ×0Ò0¸×9_Ò9_Ü˜x¬Ô.à38ˆDÔ0Ø59ˆDÔ2Ù4°XÓ>ˆIØÐ$Ü—‘ØaØ—I‘I˜}¨iô9ñ &9¸ÀÐS\Ô%]Ñ"�˜(ÙÜ—K‘KÐ KÈTÏYÉYÐXeÐgpÔqØÜ+5°hÄÔ+E�H—L‘L Ô'È4ˆ	ñ 0°Ó9ˆÙ,5�V˜I˜; aÑ(¸2ˆÙKX�9˜]˜OÐ+<¸X¸JÑGÐ^fˆá"ð @À¸ÈoÑ^Ø(Hð 	à"�O ?°6°(¸!¸F¸8ÐDˆÜ�‰�Y §	¡	¨7Ô3Ø×Ñ   u˜o¨wÀ$ÐÔGØr   c                 ód   — t        | dd«      }|syddlm}  || j                  |«       d| _        y)z;Release the scoped lock acquired by _acquire_platform_lock.r  Nr   )Úrelease_scoped_lock)ra   rþ  r#  r  r  )r˜  r  r#  s      r   Ú_release_platform_lockz*BasePlatformAdapter._release_platform_lock�  s2   € ä˜4Ð!:¸DÓAˆÙØÝ6Ù˜D×5Ñ5°xÔ@Ø'+ˆÕ$r   Ú_plugin_handler_nativeÚ_plugin_handlers_wiredÚnativec           	      óÆ  — 	 ddl m}  |«       j                  t        | j                  dt        | j                  «      «      «      }| j                  |us| j                  €|| _
        t        «       | _        |D ]v  \  }}|t        |dd«      xs t        |«      f}|| j                  v rŒ1	  ||| «       t        j                  d| j                  |«       | j                  j!                  |«       Œx y# t        $ r+}t        j                  d| j                  |«       Y d}~yd}~ww xY w# t        $ r.}t        j                  d| j                  ||d	¬
«       Y d}~Œ‡d}~ww xY w)aX  Invoke plugin-registered native handler factories (``ctx.register_platform_handler``)
        with ``(native, adapter)``; adapters call this from ``connect()`` once the native
        client exists and :meth:`rewire_plugin_handlers` re-runs it for plugins loaded later.
        Idempotent per native client: a factory is keyed by ``(plugin, qualname)`` and skipped once
        wired on this ``native`` (a force re-discovery hands back NEW function objects for the same
        plugin, so identity alone would double-register). Each factory is isolated so a bad plugin
        can't block connecting.r   )Úget_plugin_managerr_   z0[%s] Could not load plugin handler factories: %sNrL  z+[%s] Wired native handlers from plugin '%s'z+[%s] Plugin '%s' handler factory raised: %sTr   )Úhermes_cli.pluginsr)  Úget_platform_handler_factoriesra   rc   rF   rO   r   rL   r  r%  r&  rv  Úreprr  r   rÿ  )	r˜  r'  r)  Ú	factoriesrF  rW  Úplugin_namerì   r   s	            r   Ú_wire_plugin_handlersz)BasePlatformAdapter._wire_plugin_handlersŸ  sC  € ð	Ý=Ù*Ó,×KÑKÜ˜Ÿ™ w´°D·M±MÓ0BÓCóEˆIð
 ×&Ñ&¨fÑ4¸×8SÑ8SÐ8[à*0ˆDÔ'Ü*-«%ˆDÔ'Ø$-ò 	1Ñ ˆG�[Ø¤¨°ÀÓ FÒ WÌ$ÈwË-ÐXˆCØ�d×1Ñ1Ñ1Øð1Ù˜ Ô%Ü—‘ÐIÈ4Ï9É9ÐVaÔbð
 ×'Ñ'×+Ñ+¨CÕ0ñ	1øô ò 	Ü�N‰NÐMÈtÏyÉyÐZ[Ô\Üûð	ûô ò 1Ü—‘ÐJÈDÏIÉIÐWbØ ¨4ð ÷ 1ñ 1ûð1ús0   ‚AC2 Â**D)Ã2	D&Ã;!D!Ä!D&Ä)	E Ä2$EÅE c                 óT   — | j                   €y| j                  | j                  «       y)u¸  Register handlers of plugins loaded AFTER ``connect()`` wired the first batch (#87770);
        the gateway runner calls this on every plugin-loaded event. Safe to call repeatedly: only
        factories not yet wired on the live native client run. Before ``connect()`` has wired once
        there is nothing to re-wire â€” connect will pick everything up. Adapters with extra plugin
        registries (Slack action handlers) extend this.N)r&  r/  r%  r—  s    r   Úrewire_plugin_handlersz*BasePlatformAdapter.rewire_plugin_handlers¿  s'   € ð ×&Ñ&Ð.ØØ×"Ñ" 4×#>Ñ#>Õ?r   c                 óJ   — | j                   j                  j                  «       S )z%Human-readable name for this adapter.)rc   r_   Útitler—  s    r   r  zBasePlatformAdapter.nameÉ  s   € ð �}‰}×"Ñ"×(Ñ(Ó*Ð*r   c                 ó   — | j                   S )z(Check if adapter is currently connected.)rq  r—  s    r   Úis_connectedz BasePlatformAdapter.is_connectedÎ  s   € ð �}‰}Ðr   c                 ó   — || _         y)zISet the incoming-message handler (MessageEvent -> optional response str).N)rl  rÝ  s     r   Úset_message_handlerz'BasePlatformAdapter.set_message_handlerÓ  s
   € à 'ˆÕr   c                 ó   — || _         y)zÇInstall the gateway-owned normalized platform-event boundary (stable dicts + internal
        ``SessionSource``); the runner owns authorization and plugin dispatch: no callback = fail
        closed.N)ro  rÝ  s     r   Úset_platform_event_handlerz.BasePlatformAdapter.set_platform_event_handler×  s   € ð
 (/ˆÕ$r   Úfnc                 ó   — || _         y)zÄInstall a thread_id-recovery hook (Telegram DM topic mode): called with ``event.source``
        before session keying; a non-None return replaces ``source.thread_id``. ``None`` clears
        it.N)rp  )r˜  r:  s     r   Úset_topic_recovery_fnz)BasePlatformAdapter.set_topic_recovery_fnÞ  s   € ð #%ˆÕr   c                 ó|  — t        | dd«      }t        |dd«      }|�|€y	  ||«      }|�%t        |«      t        |j                  xs d«      k(  ry	 	 dd	lm}  ||t        |«      ¬
«      |_        y# t        $ r t        j                  dd¬«       Y yw xY w# t        $ r t        j                  dd¬«       Y yw xY w)zDRewrite ``event.source.thread_id`` in place if the hook returns one.rp  Nr{   r`   ztopic recovery hook failedTr   r   )Úreplace_source)rm   ztopic recovery rewrite failed)	ra   rF   rm   rO   r   r�  Úgateway.session_identityr>  r{   )r˜  r‚   Úrecoverr{   Ú	recoveredr>  s         r   Ú_apply_topic_recoveryz)BasePlatformAdapter._apply_topic_recoveryä  sÆ   € ä˜$Ð 4°dÓ;ˆÜ˜ ¨$Ó/ˆØˆ?˜f˜nØð	Ù ›ˆIØÐ ¤C¨	£N´c¸&×:JÑ:JÒ:PÈbÓ6QÒ$QØð %Rð
	IÝ?Ù)¨&¼CÀ	»NÔKˆE�Løô ò 	Ü�L‰LÐ5ÀˆLÔEÙð	ûô ò 	IÜ�L‰LÐ8À4ˆLÖHð	Iús#   ¡/A2 ÁB Á2 BÂBÂ B;Â:B;c                 ó   — || _         y)zESet an optional handler for messages arriving during active sessions.N)r‰  rÝ  s     r   Úset_busy_session_handlerz,BasePlatformAdapter.set_busy_session_handler÷  s
   € à%,ˆÕ"r   c                 ó   — || _         y)a7  Set the handler for platform-native emoji-reaction events: a normalised dict
        (``platform``, ``event_name`` "reaction:added"/"reaction:removed", ``reaction``,
        ``user_id``, ``item_user_id``, ``channel_id``, ``message_ts``, ``event_ts``, ``raw_event``)
        fanned out via ``HookRegistry.emit``.N)rn  rÝ  s     r   Úset_reaction_handlerz(BasePlatformAdapter.set_reaction_handlerû  s   € ð
 ")ˆÕr   Úcallbackc                 ó   — || _         y)zµRegister ``(user_id, chat_type, chat_id) -> bool``; adapters pulling external context
        (Slack thread replies) use it to flag non-allowlisted senders as unverified background.N)rŒ  )r˜  rG  s     r   Úset_authorization_checkz+BasePlatformAdapter.set_authorization_check	  s   € ð %-ˆÕ!r   )Úis_botrm   Úuser_idrJ  rm   c                óP  — |r| j                   €yi }|rd|d<   |�||d<   	  | j                   |||fi |¤Ž}|du s|du r|S t        j                  d| j                  t        |«      j                  |«       y# t        $ r& t        j                  d| j                  |d¬«       Y yw xY w)	ak  True/False from the registered check, or None when no check exists ("trust unknown",
        legacy). ``is_bot``/``thread_id`` are forwarded as keywords only when set so legacy
        three-positional callbacks keep working. Only literal booleans propagate: a truthy
        non-boolean is "unknown", never an authorization that gates a credentialed side effect.NTrJ  rm   z@[%s] Authorization check raised for user %s; treating as unknownr   FzE[%s] Authorization check returned %s for user %s; treating as unknown)rŒ  rO   r   rL   r  r­  rJ  )r˜  rK  rr   rR  rJ  rm   rê  rY   s           r   Ú_is_sender_authorizedz)BasePlatformAdapter._is_sender_authorized	  sÅ   € ñ ˜$×3Ñ3Ð;ØØ "ˆÙØ"ˆE�(‰OØÐ Ø!*ˆE�+Ñð	Ø.�T×.Ñ.¨w¸	À7ÑTÈeÑTˆFð
 �T‰>˜V u™_ØˆMÜ�‰Ð^Ø—y‘y¤$ v£,×"7Ñ"7¸ô	Bàøô ò 	Ü�N‰NÐ]ØŸ9™9 g¸ð ô >áð	ús   ¡A6 Á6,B%Â$B%Úsession_storec                 ó   — || _         y)ztSet the session store (e.g. Slack checks for an active thread session
        before handling un-mentioned replies).N)Ú_session_store)r˜  rN  s     r   Úset_session_storez%BasePlatformAdapter.set_session_store"	  s   € ð ,ˆÕr   Úprofile_namec                 ó\   — |xs dj                  «       xs dx}dk(  rd| _        y|| _        y)z™Declare the owning multiplex profile (secondary profiles only); read by
        :meth:`_session_key_profile` so adapter-level keys leave ``agent:main:``.r`   Nrh   )rz   rŠ  )r˜  rR  r  s      r   Úset_owner_profilez%BasePlatformAdapter.set_owner_profile'	  s4   € ð 1=Ò0BÀ×/IÑ/IÓ/KÒ/SÈtÐ'S tÐXaÒ&a˜dˆÕÐgkˆÕr   c                 ód   — t        | dd«      }t        |t        «      r|j                  «       r|S dS )z°Transport profile for :func:`resolve_identity`: the owner name, or ``None`` = derive it
        from the registry (the primary's identity then spells ``"default"`` out itself).rŠ  N)ra   rŽ   rF   rz   )r˜  Úowners     r   Ú_owner_transport_profilez,BasePlatformAdapter._owner_transport_profile,	  s/   € ô ˜Ð.°Ó5ˆÜ" 5¬#Ô.°5·;±;´=ˆuÐJÀdÐJr   r{   r>  c                 ó  — |€yddl m}m}  ||«      }|�|S t        | dd«      }|�t	        t        |dd«      «      sy	  |||| | j                  «       ¬«      S # t        $ r% t        j                  d| j                  d¬	«       Y yw xY w)
aà  Pin the source's :class:`RoutingIdentity` before anything derives a key from it. Every
        ingress path (fresh event, batch merge, busy path, control command, callback) calls this
        FIRST. Returns the identity, or ``None`` when it cannot be resolved (a rejected route under
        multiplexing marks ``source.profile_route_rejected``; ``_drop_unresolved`` reads it) or when
        no runner seam exists (hand-built adapters, restored sources: the legacy readers stay).Nr   )Úcanonical_identityÚidentity_ofÚgateway_runnerÚ_transport_owner)ÚrunnerÚadapterÚtransport_profilez9[%s] identity resolution failed; using legacy key readersTr   )
r?  rY  rZ  ra   ÚcallablerW  rO   r   r�  r  )r˜  r{   rY  rZ  r  r]  s         r   Ú_canonicalizez!BasePlatformAdapter._canonicalize2	  s¡   € ð ˆ>ØßLÙ˜vÓ&ˆØÐØˆOÜ˜Ð/°Ó6ˆØˆ>¤¬'°&Ð:LÈdÓ*SÔ!TØð	Ù%Ø˜v¨tÀt×GdÑGdÓGfôhð høäò 	ô �L‰LÐTÐVZ×V_ÑV_ÐjnˆLÔoÙð		ús   ¿A Á+BÂBr:  c           	      óº   — t        |dd«      }| j                  |«      �yt        |dd«      duryt        j                  d| j                  t        |dd«      «       y)	uë   True when *event* must be dropped: its identity could not be resolved because the route
        targets an unserved profile. Same disposition as the runner's ingress gate â€” one WARNING,
        never a fall-through to ``agent:main``.r{   NFÚprofile_route_rejectedTzV[%s] Dropping inbound event for %s: explicit profile route targets an unserved profilerR  rÞ  )ra   ra  r   rL   r  )r˜  r‚   r{   s      r   Ú_drop_unresolvedz$BasePlatformAdapter._drop_unresolvedJ	  sa   € ô ˜ ¨$Ó/ˆØ×Ñ˜fÓ%Ð1ØÜ�6Ð3°UÓ;À4ÑGØÜ�‰ØdØ�I‰I”w˜v y°#Ó6ô	8ð r   c                 ó–  — ddl m}  ||«      }|�|j                  S |�t        |dd«      ndt        | dd«      fD ]'  }t	        |t
        «      sŒ|j                  «       sŒ%|c S  t        | dd«      }|rt        |dd«      nd}t        |«      sy	  ||«      }t	        |t
        «      r|j                  «       r|S dS # t        $ r Y yw xY w)u¢  Profile namespace for an adapter-derived session key. Ingress runs BEFORE the runner
        stamps ``source.profile``, so without this every bot in a multiplexed gateway shares one
        ``agent:main:`` lane. Order: pinned ``RoutingIdentity`` â†’ ``source.profile`` â†’
        ``_owner_profile`` â†’ session-store resolver; getattr-guarded (object.__new__ in tests),
        type-checked (no MagicMock in the key).r   )rZ  Nrx   rŠ  rP  Ú_resolve_profile_for_key)	r?  rZ  Úsession_key_profilera   rŽ   rF   rz   r`  rO   )r˜  r{   rZ  r  r»  ra  ÚresolverrÖ   s           r   Ú_session_key_profilez(BasePlatformAdapter._session_key_profileX	  sá   € õ 	9Ù˜vÓ&ˆØÐØ×/Ñ/Ð/à06Ð0BŒG�F˜I tÔ,ÈÜ�DÐ*¨DÓ1ð3ò 	!ˆIô ˜)¤SÕ)¨i¯o©oÕ.?Ø Ò ð		!ô
 ˜Ð.°Ó5ˆÙGL”7˜5Ð"<¸dÔCÐRVˆÜ˜Ô!Øð	Ù Ó'ˆHô & h´Ô4¸¿¹Ô9IˆxÐSÈtÐSøô ò 	Ùð	ús   ÂB< Â<	CÃCi   Ú_SPLIT_THRESHOLDrW  Ú_text_batch_delay_secondsÚ_text_batch_split_delay_secondsg333333Ó?Ú_TEXT_BATCH_DEFAULT_DELAY_Sç       @Ú_TEXT_BATCH_MAX_DELAY_Srj   Ú!_TEXT_BATCH_DEFAULT_SPLIT_DELAY_Sg      @Ú_TEXT_BATCH_MAX_SPLIT_DELAY_S©Ú	min_valueÚ	max_valuerì   rh   rs  rt  c                óV  — t        | j                  dd«      xs i }	 t        |j                  |«      «      }t        j                  |«      r|dk  rt        |«      }t        ||«      }|�||kD  rt        j                  d|||«       |}|S # t        t
        f$ r t        |«      }Y Œpw xY w)uq   Float from ``config.extra``; NaN/Inf/negative/unparseable â†’ ``default``; clamped to ``[min_value, max_value]``.rê  Nr   z%%s=%s exceeds the %s ceiling; clamped)ra   ri  r[  r�   r‚  rÍ   ÚmathÚisfiniterZ  r   rL   )r˜  rì   rh   rs  rt  rê  ro  s          r   Ú_coerce_float_extraz'BasePlatformAdapter._coerce_float_extra‚	  s£   € ä˜Ÿ™ W¨dÓ3Ò9°rˆð	$Ü˜5Ÿ9™9 S›>Ó*ˆFô �}‰}˜VÔ$¨°ª
Ü˜7“^ˆFÜ�V˜YÓ'ˆØÐ  V¨iÒ%7Ü�N‰NÐBÀCÈÐQZÔ[ØˆFØˆøô œ:Ð&ò 	$Ü˜7“^ŠFð	$ús   �B ÂB(Â'B(c                 óÎ   — | j                  d| j                  | j                  ¬«      | _        | j                  d| j                  | j                  | j
                  ¬«      | _        y)zsRead ``text_batch_delay_seconds`` / ``text_batch_split_delay_seconds`` from ``config.extra`` at the shared cadence.Útext_batch_delay_seconds)rt  Útext_batch_split_delay_secondsrr  N)rx  rm  ro  rk  rp  rq  rl  r—  s    r   Ú_configure_text_batch_delaysz0BasePlatformAdapter._configure_text_batch_delays‘	  sf   € à)-×)AÑ)AØ&¨×(HÑ(HÐTX×TpÑTpð *Bó *rˆÔ&à/3×/GÑ/GØ,¨d×.TÑ.TØ×4Ñ4À×@bÑ@bð 0Hó 0dˆÕ,r   c                 ó8   — | j                  |j                  «      S )zOAdapter-level session key for ``event``, profile-namespaced like the agent run.)Ú_source_session_keyr{   ©r˜  r‚   s     r   Ú_event_session_keyz&BasePlatformAdapter._event_session_key™	  s   € à×'Ñ'¨¯©Ó5Ð5r   c                 óÌ   — | j                  |«       | j                  j                  }t        ||j	                  dd«      |j	                  dd«      | j                  |«      ¬«      S )NÚgroup_sessions_per_userTÚthread_sessions_per_userF)r‚  rƒ  rx   )ra  ri  rê  r?  r�   ri  )r˜  r{   rê  s      r   r~  z'BasePlatformAdapter._source_session_key�	  s[   € Ø×Ñ˜6Ô"Ø—‘×!Ñ!ˆÜ Ø¨E¯I©IÐ6OÐQUÓ,VØ%*§Y¡YÐ/IÈ5Ó%QØ×-Ñ-¨fÓ5ô7ð 	7r   c                 ó$   — | j                  |«      S )z?Session-scoped key for text batching (subclasses may override).)r€  r  s     r   Ú_text_batch_keyz#BasePlatformAdapter._text_batch_key¥	  s   € à×&Ñ& uÓ-Ð-r   c                 óÎ  — | j                  |«      ry| j                  |«      }| j                  j                  |«      }|€|x}| j                  |<   n‡|j                  r%t        |j                  |j                  «      |_        |j                  rJ|j                  j                  |j                  «       |j                  j                  |j                  «       t        |j                  xs d«      |_
        | j                  j                  |«      }|r |j                  «       s|j                  «        t        j                  | j!                  |«      «      | j                  |<   y)zMBuffer a text event (merging into a pending one) and restart the flush timer.Nr`   )rd  r…  r|  r�   rÜ   rÁ  r'  rC  r-  r²   Ú_last_chunk_lenr}  rº  r»  rÄ  Úcreate_taskÚ_flush_text_batch)r˜  r‚   rì   r¾  Ú
prior_tasks        r   Ú_enqueue_text_eventz'BasePlatformAdapter._enqueue_text_event©	  s  € à× Ñ  Ô'ØØ×"Ñ" 5Ó)ˆØ×-Ñ-×1Ñ1°#Ó6ˆØÐØ9>Ð>ˆH�t×1Ñ1°#Ò6à�zŠzÜ ,¨X¯]©]¸E¿J¹JÓ G�”Ø×ÒØ×#Ñ#×*Ñ*¨5×+;Ñ+;Ô<Ø×$Ñ$×+Ñ+¨E×,=Ñ,=Ô>Ü#& u§z¡zÒ'7°RÓ#8ˆÔ Ø×3Ñ3×7Ñ7¸Ó<ˆ
Ù˜jŸo™oÔ/Ø×ÑÔÜ.5×.AÑ.AÀ$×BXÑBXÐY\ÓB]Ó.^ˆ×&Ñ& sÒ+r   Úpendingc                 ór   — |�t        |dd«      nd}|| j                  k\  r| j                  S | j                  S )zMQuiet period before ``pending`` is dispatched; near-split chunks wait longer.r‡  r   )ra   rj  rl  rk  )r˜  rŒ  Úlast_lens      r   Ú_text_batch_delay_forz)BasePlatformAdapter._text_batch_delay_for½	  s>   € à=DÐ=P”7˜7Ð$5°qÔ9ÐVWˆØ7?À4×CXÑCXÒ7Xˆt×3Ñ3Ð|Ð^b×^|Ñ^|Ð|r   c                 ó:   — | j                   j                  |d«      S )zURemove and return the pending batch for ``key`` (adapters with side tables override).N)r|  r`  )r˜  rì   s     r   Ú_pop_text_batchz#BasePlatformAdapter._pop_text_batchÂ	  s   € à×)Ñ)×-Ñ-¨c°4Ó8Ð8r   c              ƒ   óB   K  — | j                  |«      ƒ d{  –—†  y7 Œ­w)zNHand a flushed batch to the pipeline (adapters with per-chat guards override).N)Úhandle_messager  s     r   Ú_dispatch_text_batchz(BasePlatformAdapter._dispatch_text_batchÆ	  s   è ø€ à×!Ñ! %Ó(×(Ò(ús   ‚—˜c              ƒ   ój   K  — | j                  |«      }|�| j                  |«      ƒ d{  –—†  yy7 Œ­w)zEDispatch the pending batch for ``key`` immediately (no quiet period).N)r‘  r”  )r˜  rì   r‚   s      r   Ú_flush_text_batch_nowz)BasePlatformAdapter._flush_text_batch_nowÊ	  s8   è ø€ à×$Ñ$ SÓ)ˆØÐØ×+Ñ+¨EÓ2×2Ñ2ð Ø2ús   ‚(3ª1«3c           	   ƒ   ó  K  — t        j                  «       }	 t        j                  | j                  | j                  j                  |«      «      «      ƒ d{  –—†  | j                  j                  |«      }|�@||ur<	 | j                  j                  |«      |u r| j                  j                  |d«       yy| j                  |«      }|€<	 | j                  j                  |«      |u r| j                  j                  |d«       yyt        j                  d| j                  |t        |j                  xs d«      «       t        j                  | j                  |«      «      ƒ d{  –—†  | j                  j                  |«      |u r| j                  j                  |d«       yy7 �ŒQ7 ŒB# t         j                   $ r Y ŒUw xY w# | j                  j                  |«      |u r| j                  j                  |d«       w w xY w­w)u²  Wait for the quiet period, then dispatch the batch for ``key``.

        Two races share this body. (1) ``_enqueue_text_event`` cancels the prior flush task
        on each new chunk; when ``Task.cancel()`` lands after ``sleep()`` already completed,
        CancelledError is delivered at the *next* await â€” after a superseded task would have
        popped the event, so the successor finds nothing and the message is lost. The identity
        check therefore runs synchronously between the sleep and the pop. (2) A cancel that
        lands while the dispatch is in flight would abort the agent turn (#12444), so the
        dispatch is shielded and the outer CancelledError swallowed.Nz&[%s] Flushing text batch %s (%d chars)r`   )rÄ  Úcurrent_taskrÛ  r�  r|  r�   r}  r`  r‘  r   r  r  r²   rÜ   r	  r”  r
  )r˜  rì   r˜  rV  r‚   s        r   r‰  z%BasePlatformAdapter._flush_text_batchÐ	  sÔ  è ø€ ô ×+Ñ+Ó-ˆð	>Ü—-‘- × :Ñ :¸4×;UÑ;U×;YÑ;YÐZ]Ó;^Ó _Ó`×`Ð`Ø×2Ñ2×6Ñ6°sÓ;ˆEØÐ  U°,Ñ%>Øð ×-Ñ-×1Ñ1°#Ó6¸,ÑFØ×.Ñ.×2Ñ2°3¸Õ=ð Gð ×(Ñ(¨Ó-ˆEØˆ}Øð ×-Ñ-×1Ñ1°#Ó6¸,ÑFØ×.Ñ.×2Ñ2°3¸Õ=ð Gô �K‰KÐ@À$Ç)Á)ÈSÔRUÐV[×V`ÑV`ÒVfÐdfÓRgÔhÜ—.‘. ×!:Ñ!:¸5Ó!AÓB×BÐBð ×-Ñ-×1Ñ1°#Ó6¸,ÑFØ×.Ñ.×2Ñ2°3¸Õ=ð Gð aùð CùÜ×%Ñ%ò 	Ùð	ûð ×-Ñ-×1Ñ1°#Ó6¸,ÑFØ×.Ñ.×2Ñ2°3¸Õ=ð Güsr   ‚H˜A F. ÁF)Á%F. Á?;HÂ:F. Ã;HÄ	A F. Å)F,Å*F. Å.;HÆ)F. Æ,F. Æ.GÇG ÇGÇG Ç<HÈHrW  c                 óö  ‡	— t        | dd«      }|sy	 t        |dd«      }t        |«      r ||«      nd}|j                  |xs |«      }|syt        |«      Š	t        ˆ	fd„t        t        ‰	«      dz
  dd«      D «       d«      }|�‰	d| Š	n/t        d„ t        ‰	«      D «       d«      }|�‰	j                  |«       ‰	syd	d
lm}  |‰	«      S # t        $ r t        j                  d|«       Y yt        $ r Y yw xY w)z˜Return media paths already delivered in prior turns of this session
        (MEDIA: tags / image_generate payloads), so an echoed old tag isn't re-sent.rP  NÚpeek_session_idz[Transcript read failed for session %s; media dedup runs with no history this turn (#100788)c              3   óR   •K  — | ]  }‰|   j                  d «      dk(  sŒ|–— Œ  y­w)ÚroleÚuserN©r�   )rÈ   ÚiÚhistorys     €r   rË   zGBasePlatformAdapter._history_media_paths_for_session.<locals>.<genexpr>
  s&   øè ø€ Ò\�1¸7À1¹:¿>¹>È&Ó;QÐU[Ó;[ŒQÑ\ùs   ƒ' 'r·   rh  c              3   óJ   K  — | ]  }|j                  d «      dk(  sŒ|–— Œ y­w)rœ  Ú	assistantNrž  )rÈ   r$  s     r   rË   zGBasePlatformAdapter._history_media_paths_for_session.<locals>.<genexpr>	
  s    è ø€ Òb sÀ3Ç7Á7È6Ã?ÐVaÓCaœsÑbùs   ‚#œ#r   )Ú_collect_history_media_paths)ra   r`  Úload_transcriptr@  r   rL   rO   r0  r  rÕ  r²   r€  ÚremoveÚgateway.runr£  )
r˜  rW  ra  ÚpeekÚ
session_idÚ
transcriptÚlast_user_idxÚ
last_replyr£  r   s
            @r   Ú _history_media_paths_for_sessionz4BasePlatformAdapter._history_media_paths_for_sessionë	  s  ø€ ô ˜Ð.°Ó5ˆÙØð	ä˜5Ð"3°TÓ:ˆDÜ.6°t¬n™˜kÔ*À$ˆJØ×.Ñ.¨zÒ/H¸[ÓIˆJñ Øô �zÓ"ˆÜÛ\œœc '›l¨QÑ.°°BÓ7Ô\Ð^bódˆàÐ$Ø˜n˜}Ð-‰Gô Ñb¬h°wÓ.?ÔbÐdhÓiˆJØÐ%Ø—‘˜zÔ*ÙØÝ<Ù+¨GÓ4Ð4øô3 #ò 	Ü�N‰Nð6Ø7Bôñ Üò 	Ùð	ús   “7C ÃC8Ã-C8Ã7C8c              ƒ   óD  ‡ ‡‡‡‡‡K  — ddœdt         dt        ddfˆ ˆfd„}t        Š‰j                  d¬«      s	 |d	«       yt	        j
                  «       Š‰j                  «       Šdˆfd
„	Šˆˆˆˆ ˆfd„}	 t        j                  |dd¬«      j                  «        	 t	        j                  ‰t        ¬«      ƒ d{  –—† S # t        $ r ‰j                  «         |dd¬«       Y yw xY w7 Œ-# t        j                  $ r  |d«       Y yt        $ r  |dd¬«       Y yw xY w­w)zCRun best-effort history lookup in a bounded isolated daemon thread.Fr   r¼  r   r	   Nc                óX   •— t         j                  d| z   dz   ‰j                  ‰|¬«       y )Nz[%s] z= %s; delivering bare local file path(s) without history dedupr   )r   rL   r  )r¼  r   r˜  rW  s     €€r   Ú
_fail_openzPBasePlatformAdapter._bounded_history_media_paths_for_session.<locals>._fail_open
  s-   ø€ Ü�N‰NØ˜&Ñ Ð#bÑbØ—	‘	˜;°ð õ ;r   )Úblockingz4Media-delivery history lookup capacity exhausted forc                 óp   •— ‰j                  «       s%|�‰j                  |«      n‰j                  | «       y y r¤  )rº  Úset_exceptionÚ
set_result)rY   r   Úresult_futures     €r   Ú_publish_resultzUBasePlatformAdapter._bounded_history_media_paths_for_session.<locals>._publish_result
  s6   ø€ Ø ×%Ñ%Ô'Ø7<Ð7H�×,Ñ,¨UÔ3Ø#×.Ñ.¨vÓ6ùð (r   c                  ó2  •— d\  } }	 ‰j                  ‰«      } 	 t        j                  t        «      5  ‰j                  ‰| |«       d d d «       ‰j                  «        y # t        $ r}|}Y d }~ŒWd }~ww xY w# 1 sw Y   Œ2xY w# ‰j                  «        w xY w)N©NN)r¬  ÚBaseExceptionrP   rQ   rt  Úcall_soon_threadsafeÚrelease)rY   r   r   rµ  Ú	admissionÚloopr˜  rW  s      €€€€€r   Ú_workerzMBasePlatformAdapter._bounded_history_media_paths_for_session.<locals>._worker#
  s“   ø€ Ø&‰MˆF�EðØ×>Ñ>¸{ÓK�ð$Ü×(Ñ(¬Ó6ñ NØ×-Ñ-¨o¸vÀuÔM÷Nð ×!Ñ!Õ#øô !ò Ø•ûðú÷Nð Nûð ×!Ñ!Õ#ús9   ˆA  šB ³A8ÁB Á 	A5Á)A0Á0A5Á8BÁ=B ÂBzmedia-history-lookupT)Útargetr  Údaemonz8Could not start media-delivery history lookup worker for©r+   z,Timed out loading media-delivery history forz(Media-delivery history lookup failed forr·  )rF   r  Ú_HISTORY_MEDIA_LOOKUP_ADMISSIONÚacquirerÄ  Úget_running_loopÚcreate_futureÚ	threadingÚThreadrP  rO   rº  Úwait_forÚ%_HISTORY_MEDIA_LOOKUP_TIMEOUT_SECONDSÚTimeoutError)r˜  rW  r¯  r½  rµ  r»  r¼  r´  s   ``  @@@@r   Ú(_bounded_history_media_paths_for_sessionz<BasePlatformAdapter._bounded_history_media_paths_for_session
  s  ýè ø€ à8=ò 	;œsð 	;´ð 	;À$ö 	;ô 4ˆ	Ø× Ñ ¨%Ð Ô0ÙÐMÔNØÜ×'Ñ'Ó)ˆØ×*Ñ*Ó,ˆõ	8÷

	$ð 
	$ð	Ü×Ñ GÐ2HÐQUÔV×\Ñ\Ô^ð	Ü ×)Ñ)¨-ÔAfÔg×gÐgøô ò 	à×ÑÔÙÐQÐ\`ÕaÙð		úð hùÜ×#Ñ#ò 	ÙÐEÔFÙÜò 	áÐAÈDÕQÙð	üsf   ˆA-D Á6&C  ÂC+ Â;C)Â<C+ Â?D Ã #C&Ã#D Ã%C&Ã&D Ã)C+ Ã+DÄD ÄDÄD ÄDÄD )Úis_reconnectrË  c             ƒ   ó   K  — y­w)uþ   Connect and start receiving; True on success. ``is_reconnect``: the reconnect watcher is
        re-establishing a dropped platform â€” adapters with a server-side update queue (Telegram)
        must preserve it so outage-time messages aren't discarded.Nrf   )r˜  rË  s     r   ÚconnectzBasePlatformAdapter.connect?
  ó   è ø� ùó   ‚c              ƒ   ó   K  — y­w)zDisconnect from the platform.Nrf   r—  s    r   Ú
disconnectzBasePlatformAdapter.disconnectE
  rÎ  rÏ  Úreply_toc              ƒ   ó   K  — y­w)zQSend ``content`` (may be markdown) to a chat; returns SendResult with message id.Nrf   ©r˜  rR  rß  rÒ  r|   s        r   ÚsendzBasePlatformAdapter.sendI
  rÎ  rÏ  ÚREQUIRES_EDIT_FINALIZEÚparent_chat_idr  c              ƒ   ó   K  — y­w)uÈ   Create a fresh thread under ``parent_chat_id`` for a CLIâ†’platform session handoff; its id
        as str, or None when unsupported/failed (the watcher then uses ``parent_chat_id``
        directly).Nrf   )r˜  r×  r  s      r   Úcreate_handoff_threadz)BasePlatformAdapter.create_handoff_threadQ
  s   è ø€ ð ùrÏ  )Úfinalizerv   rÚ  c             ƒ   ó$   K  — t        dd¬«      S ­w)aB  Edit a sent message (optional: success=False makes callers send anew). ``finalize`` marks
        the last edit of a streamed response; surfaces with a distinct "in progress" state (DingTalk
        AI Cards) close the message on it and set ``REQUIRES_EDIT_FINALIZE`` so it's routed even
        when content is unchanged.FúNot supported©rÜ  r   ©rÛ  )r˜  rR  rv   rß  rÚ  s        r   Úedit_messagez BasePlatformAdapter.edit_messageW
  s   è ø€ ô  %¨Ô?Ð?ùó   ‚c              ƒ   ó   K  — y­w)aØ  Delete a sent message; True on success (platforms without a deletion API return False and
        callers leave it). Used by the stream consumer's fresh-final cleanup to remove stale
        previews.

        Used by the stream consumer's fresh-final cleanup path (see openclaw/openclaw#72038) to remove
        long-lived preview messages after sending the completed reply as a fresh message so the platform's
        visible timestamp reflects completion time.
        Frf   )r˜  rR  rv   s      r   Údelete_messagez"BasePlatformAdapter.delete_message_
  s   è ø€ ð ùrÏ  c                 ó   — t        d„ d«      S )zŽDefault :class:`EphemeralReply` TTL from ``display.ephemeral_system_ttl``
        (``0`` = no auto-delete); non-fatal if config is unreadable.c                  óJ   — t        t        d«      j                  dd«      «      S )Nr©  Úephemeral_system_ttlr   )rN  r  r�   rf   r   r   r  zGBasePlatformAdapter._get_ephemeral_system_ttl_default.<locals>.<lambda>n
  s   € ”Cœ¨	Ó2×6Ñ6Ð7MÈqÓQÓR€ r   r   r™  r—  s    r   Ú!_get_ephemeral_system_ttl_defaultz5BasePlatformAdapter._get_ephemeral_system_ttl_defaultj
  s   € ô ÙRÐTUóWð 	Wr   r  c                 ó–   ‡ ‡‡‡— dˆˆˆ ˆfd„} |«       }	 t        j                  |«       y# t        $ r |j                  «        Y yw xY w)z°Spawn a detached task that deletes ``message_id`` after ``ttl_seconds``; best-effort
        (gateway restart, permission denied, Telegram 48h window) swallowed at debug level.Nc               “   ó   •K  — 	 t        j                  t        dt        ‰«      «      «      ƒ d {  –—†  ‰j	                  ‰‰¬«      ƒ d {  –—†  y 7 Œ 7 Œ# t
        $ r-} t        j                  d‰j                  ‰‰| «       Y d } ~ y d } ~ ww xY w­w)Nr·   )rR  rv   z*[%s] Ephemeral delete failed for %s/%s: %s)	rÄ  rÛ  rZ  rN  râ  rO   r   r�  r  )rF  rR  rv   r˜  r  s    €€€€r   Ú_run_deletezCBasePlatformAdapter._schedule_ephemeral_delete.<locals>._run_deletes
  s}   øè ø€ ðnÜ—m‘m¤C¨¬3¨{Ó+;Ó$<Ó=×=Ð=Ø×)Ñ)°'ÀjÐ)ÓQ×QÑQð >øØQùÜò nÜ—‘ÐIÈ4Ï9É9ÐV]Ð_iÐkl×mÑmûðnüsP   ƒB…+A °A±A ÁAÁA ÁBÁA ÁA Á	BÁ#BÂBÂBÂB©r	   N)rÄ  rˆ  rt  rD   )r˜  rR  rv   r  ré  Úcoros   ````  r   Ú_schedule_ephemeral_deletez.BasePlatformAdapter._schedule_ephemeral_deletep
  sC   û€ ÷	nð 	nñ ‹}ˆð	Ü×Ñ Õ%øÜò 	à�J‰JŽLð	ús   –, ¬AÁAu   âš ï¸� ú

Ú
_EA_HEADERz```
Ú_EA_CODE_OPENz
```
Ú_EA_CODE_CLOSEz: Ú_EA_REASON_LABELÚ_EA_DEADLINE_PREFIXz@

Smart DENY: owner override applies to this one operation only.Ú_EA_SMART_DENY_LINEi¸  Ú_EA_CMD_BUDGETr   Ú_EA_REASON_BUDGETrµ   r2   c                 óP   — t        | xs d«      } t        | «      |kD  r| d| |z   S | S )zETruncate ``text`` to ``budget`` chars, appending ``suffix`` when cut.r`   N)rF   r²   )rÜ   rµ   r2   s      r   Ú_truncate_previewz%BasePlatformAdapter._truncate_previewŒ
  s2   € ô �4’:˜2‹ˆÜ),¨T«°VÒ);ˆt�G�Vˆ}˜vÑ%ÐEÀÐEr   c                 ó   — |S )zPEscape hook for command preview/reason; HTML-mode platforms (Telegram) override.rf   )r˜  rÜ   s     r   Ú
_ea_escapezBasePlatformAdapter._ea_escape’
  s   € àˆr   Úescapec                 ó  — t        |xs d«      }|xs | j                  }| j                  } | ||«      «      |k  r|S dt        |«      }}||k  r-||z   dz   dz  } | ||d| «      «      |k  r|}n|dz
  }||k  rŒ-|d| |z   S )uf  ``_truncate_preview`` measured after ``escape`` (default ``_ea_escape``) in
        ``message_len_fn`` units: the platform cap applies to the wire payload, and escaping
        expands (``&`` â†’ ``&amp;``), so a raw-length cut can still overflow. Returns raw text (the
        caller escapes); ``suffix`` rides outside ``budget`` like ``_truncate_preview``.r`   r   r·   r]   N)rF   rù  r”  r²   )	r˜  rÜ   rµ   r2   rú  r¹   rº   r»   r¼   s	            r   Ú_ea_fitzBasePlatformAdapter._ea_fit–
  s¨   € ô
 �4’:˜2‹ˆØÒ*˜4Ÿ?™?ˆØ×$Ñ$ˆÙ‘&˜“,Ó 6Ò)ØˆKØ”C˜“IˆBˆØ�2ŠgØ˜‘7˜Q‘; 1Ñ$ˆCÙ‘f˜T $ 3˜ZÓ(Ó)¨VÒ3Ø‘à˜1‘W�ð �2‹gð �C�Rˆy˜6Ñ!Ð!r   rÔ  rÕ  c                 ó   — | j                   S )zPChars of command preview that fit; platforms with a hard message cap compute it.)rô  )r˜  rÔ  rÕ  s      r   Ú_exec_approval_cmd_budgetz-BasePlatformAdapter._exec_approval_cmd_budget©
  s   € à×"Ñ"Ð"r   c                 ó`   — | j                   | j                  t        t        «       «      «      z   S )zVThe "doing nothing means it will NOT run" line, with the configured approvals.timeout.)rò  rù  r9  r8  r—  s    r   Ú_ea_deadline_linez%BasePlatformAdapter._ea_deadline_line­
  s&   € à×'Ñ'¨$¯/©/Ô:WÔXpÓXrÓ:sÓ*tÑtÐtr   rÓ  c                 ó¸  — | j                   r)| j                  t        |xs d«      | j                   «      }| j                  t        |xs d«      | j                  ||«      «      }| j                  › | j
                  › | j                  |«      › | j                  › | j                  › | j                  |«      › | j                  «       › �}|r|| j                  z   S |S )zÌShared exec-approval prompt text: header + fenced (truncated) command + why it was
        flagged + the deadline line, plus the smart-deny line. Buttons/trailing instructions stay
        platform-local.r`   )rõ  rü  rF   rþ  rî  rï  rù  rð  rñ  r   ró  )r˜  rÓ  rÔ  rÕ  Úcmd_previewrÜ   s         r   Ú_format_exec_approvalz)BasePlatformAdapter._format_exec_approval±
  sÐ   € ð
 ×!Ò!ØŸ,™,¤s¨;Ò+<¸"Ó'=¸t×?UÑ?UÓVˆKØ—l‘lÜ�’˜2Ó × >Ñ >¸{ÈLÓ Yó[ˆà—?‘?Ð#Ø×%Ñ%Ð& t§¡°{Ó'CÐ&DÀT×EXÑEXÐDYØ×(Ñ(Ð)¨$¯/©/¸+Ó*FÐ)GØ×)Ñ)Ó+Ð,ð.ˆñ 3?ˆt�d×.Ñ.Ñ.ÐHÀDÐHr   z
Allow OncezAllow SessionzAlways AllowÚDeny)ÚonceÚsessionÚalwaysÚdenyÚ_EA_ACTION_LABELSÚprimaryÚdanger)r  r  Ú_EA_ACTION_STYLESÚallow_permanentÚallow_sessionc          	      óø   — dg}|s&|r$|j                  d«       |r|j                  d«       |j                  d«       |D �cg c].  }| j                  |   || j                  j                  |d«      f‘Œ0 c}S c c}w )zú``(label, choice, style)`` rows for the approval buttons. A smart deny is an owner
        override for one operation only, so it offers neither the session nor the permanent tier;
        the permanent tier is never offered without the session tier.r  r  r  r  r`   )r�  r	  r  r�   )r˜  r  r  rÕ  rØ  rÌ  s         r   Ú_exec_approval_actionsz*BasePlatformAdapter._exec_approval_actionsÆ
  sq   € ð
 �(ˆÙ¡Ø�N‰N˜9Ô%ÙØ—‘˜xÔ(Ø�‰�vÔØ[bÖcÐVW�×'Ñ'¨Ñ*¨A¨t×/EÑ/E×/IÑ/IÈ!ÈRÓ/PÒQÒcÐcùÒcs   Á3A7c                 ó:   — | j                   t        j                   uS )z™True when the adapter renders native approval buttons (overrides the prompt hook);
        the runner otherwise sends the plain-text ``/approve`` prompt.)Ú_send_exec_approval_promptrc  )r  s    r   Úsupports_exec_approval_buttonsz2BasePlatformAdapter.supports_exec_approval_buttonsÓ
  s   € ð ×-Ñ-Ô5H×5cÑ5cÐcÐcr   c	              ƒ   óÆ   K  — t        |||t        |xs d«      ||| j                  |||«      | j                  |||¬«      ¬«      }	| j	                  |	«      ƒ d{  –—† S 7 Œ­w)zÔInteractive exec-approval prompt; a press resolves via
        ``tools.approval.resolve_gateway_approval``. Text and choice set are shared; adapters
        render them natively in ``_send_exec_approval_prompt``.r`   )r  r  rÕ  )rR  rW  r|   rÓ  rÔ  rÕ  rÜ   rÒ  N)rÑ  rF   r  r  r  )
r˜  rR  rÓ  rW  rÔ  r|   r  r  rÕ  rG  s
             r   Úsend_exec_approvalz&BasePlatformAdapter.send_exec_approvalÙ
  su   è ø€ ô $Ø¨¸xÔQTÐU\ÒUbÐ`bÓQcØ#°,Ø×+Ñ+¨G°[À,ÓOØ×/Ñ/Ø /¸}Ð[gð 0ó iô	jˆð ×4Ñ4°VÓ<×<Ð<Ð<ús   ‚AA!ÁAÁA!rG  rÑ  c              ƒ   ó$   K  — t        dd¬«      S ­w)zKRender ``prompt`` with the platform's native buttons; the default has none.FrÜ  rÝ  rÞ  )r˜  rG  s     r   r  z.BasePlatformAdapter._send_exec_approval_prompté
  s   è ø€ ä %¨Ô?Ð?ùrà  ÚoptionsÚpageÚper_pageztuple[list, Dict[str, Any]]c                 óî   — t        | «      }t        d||z   dz
  |z  «      }t        dt        ||dz
  «      «      }||z  t        ||z  |z   |«      }}|dkD  rd|dz   › d|› d|› d�nd}||||||dœ}| || |fS )	uØ   Shared picker pagination: clamp ``page``, slice ``options`` -> ``(page_options, meta)``
        with ``page``/``total_pages``/``start``/``end``/``total``/``page_info`` (`` (Nâ€“M of T)``,
        empty for one page).r·   r   r”   u   â€“z of r•   r`   )r  Útotal_pagesrP  rO  r“  Ú	page_info)r²   rZ  Úmin)	r  r  r  r“  r  rP  rO  r  Úmetas	            r   Ú_format_choice_pagez'BasePlatformAdapter._format_choice_pageí
  s¬   € ô
 �G“ˆÜ˜!˜e hÑ.°Ñ2°xÑ?Ó@ˆÜ�1”c˜$ ¨a¡Ó0Ó1ˆØ˜H‘_¤c¨$°©/¸HÑ*DÀeÓ&LˆsˆØ<GÈ!ºO�b˜ ™˜ 3 s e¨4°¨w°aÑ8ÐQSˆ	Ø(,¸[ÐSXØ¨¸Iñ Gˆà�u˜SÐ! 4Ð'Ð'r   r3  Ú
confirm_idc              ƒ   ó$   K  — t        dd¬«      S ­w)am  Three-option slash-command confirmation (e.g. ``/reload-mcp``). Button adapters render
        Approve Once / Always Approve / Cancel and MUST resolve via
        ``GatewayRunner._resolve_slash_confirm(confirm_id, "once"|"always"|"cancel")``. Default (not
        supported) falls through to the gateway text fallback
        (``/approve``/``/always``/``/cancel``).FrÜ  rÝ  rÞ  )r˜  rR  r3  rõ  rW  r   r|   s          r   Úsend_slash_confirmz&BasePlatformAdapter.send_slash_confirmû
  s   è ø€ ô  %¨Ô?Ð?ùrà  ÚquestionrØ  Ú
clarify_idc              ƒ   óÜ  K  — |r¡	 ddl m} |j                  5  t        t	        |j
                  j                  |«      dd«      «      }ddd«       d}	rd}	t        |d¬	«      D �
�cg c]  \  }
}d
|
› d|› �‘Œ }}
}dj                  d|› �dg|¢d‘|	‘«      }ddl
m}  ||«       nd|› �}| j                  |||¬«      ƒ d{  –—† S # 1 sw Y   Œ€xY w# t        $ r d}Y Œ�w xY wc c}}
w 7 Œ'­w)a®  Clarify prompt; button-capable adapters SHOULD override. Multiple choice (``choices``):
        one button per choice plus "Other"; callbacks MUST resolve via
        ``tools.clarify_gateway.resolve_gateway_clarify(clarify_id, response)``, "Other" calls
        ``mark_awaiting_text(clarify_id)``. Open-ended: send the question as text (the gateway
        text-intercept resolves the next message). Default: numbered list +
        ``mark_awaiting_text``. Adapters whose prompt is a persistent card MAY define
        ``async retire_clarify_card(clarify_id, notice)``; the gateway calls it when the clarify
        ends without a click (timeout, session reset, superseding free prose).r   ©Úclarify_gatewayÚmulti_selectFNz;Reply with the number, the option text, or your own answer.uˆ   Multiple selections allowed â€” reply with the numbers separated by commas or spaces (e.g. "1, 3"), the option text, or your own answer.r·   rt  z  z. rL  u   â�“ r`   )Úmark_awaiting_text©rR  rß  r|   )Útoolsr'  r  r  ra   Ú_entriesr�   rO   Ú	enumerater§   Útools.clarify_gatewayr)  rÕ  )r˜  rR  r#  rØ  r$  rW  r|   Ú_cgÚ	_is_multiÚhintrŸ  r×  ÚnumberedrÜ   r)  s                  r   Úsend_clarifyz BasePlatformAdapter.send_clarify  s  è ø€ ñ ð"Ý8Ø—Y‘Yñ cÜ $¤W¨S¯\©\×-=Ñ-=¸jÓ-IÈ>Ð[`Ó%aÓ b�I÷cð QˆDÙðZ�ä<EÀgÐUVÔ<W×X©y¨q°&˜"˜Q˜C˜r & Ò*ÐXˆHÑXØ—9‘9  X JÐ/°ÐI°hÐIÀÐIÀDÐIÓJˆDå@Ù˜zÕ*à˜(˜Ð$ˆDØ—Y‘Y w¸Àx�YÓP×PÐP÷cð cûäò "Ø!’	ð"üó Yð QúsX   ‚C,†C ˜0CÁC ÁC,Á&C$Á8A
C,ÃC*ÃC,ÃCÃC ÃC!ÃC,Ã C!Ã!
C,c              ƒ   óH   K  — | j                  ||||¬«      ƒ d{  –—† S 7 Œ­w)zPSend a notice privately when the platform supports it; default is a normal send.©rR  rß  rÒ  r|   N©rÕ  )r˜  rR  rK  rß  rÒ  r|   s         r   Úsend_private_noticez'BasePlatformAdapter.send_private_notice%  s&   è ø€ ð —Y‘Y w¸È(Ð]e�YÓf×fÐfÐfúó   ‚"› œ"c              ƒ   ó   K  — y­w)zQSend a typing indicator; ``metadata`` carries platform context (Slack thread_id).Nrf   ©r˜  rR  r|   s      r   Úsend_typingzBasePlatformAdapter.send_typing+  rÎ  rÏ  c              ƒ   ó   K  — y­w)zIStop a persistent typing indicator; override where typing runs as a loop.Nrf   rš  s     r   Ústop_typingzBasePlatformAdapter.stop_typing.  rÎ  rÏ  Úvar_kwrã  c                óÄ   — 	 t        j                  | «      j                  }||v xs$ |xr  t        d„ |j                  «       D «       «      S # t        t        f$ r |cY S w xY w)z™Whether ``fn``'s signature takes keyword ``name`` (``var_kw``: a ``**kwargs`` also
        counts); ``unknown`` when the signature can't be introspected.c              3   óh   K  — | ]*  }|j                   t        j                  j                  u –— Œ, y ­wr¤  )r‘   ÚinspectÚ	ParameterÚVAR_KEYWORDrï  s     r   rË   z5BasePlatformAdapter._accepts_kwarg.<locals>.<genexpr>9  s,   è ø€ ò 1NØ89ˆA�F‰F”g×'Ñ'×3Ñ3Ô3ñ1NùrÌ   )rA  Ú	signatureÚ
parametersr‚  rÍ   rÒ   Úvalues)r:  r  r>  rã  Úparamss        r   Ú_accepts_kwargz"BasePlatformAdapter._accepts_kwarg1  sr   € ð	Ü×&Ñ& rÓ*×5Ñ5ˆFð �vˆ~ò O &ò #N¬Sñ 1NØ=C¿]¹]»_ô1Nó .Nð 	Oøô œ:Ð&ò 	ØŠNð	ús   ‚A ÁAÁAc              ƒ   óÀ   K  — |r;| j                  | j                  ddd¬«      r| j                  ||¬«      ƒ d{  –—†  y| j                  |«      ƒ d{  –—†  y7 Œ7 Œ­w)zñStop typing, forwarding ``metadata`` only if ``stop_typing`` accepts it (Slack AI
        status is per thread, so dropping metadata could clear a sibling thread; introspecting
        keeps legacy ``stop_typing(chat_id)`` adapters working).r|   TF©r>  rã  r  N)rH  r=  r:  s      r   Ú_stop_typing_with_metadataz.BasePlatformAdapter._stop_typing_with_metadata<  sf   è ø€ ñ ˜×+Ñ+Ø× Ñ  *°TÀ5ð ,ô Jà×"Ñ" 7°XÐ"Ó>×>Ð>ØØ×Ñ˜wÓ'×'Ñ'ð ?øà'ús!   ‚8AºA»AÁAÁAÁAr  Úhuman_delayc           	   ƒ   ó  K  — ddl m} d}|D �]  \  }}|dkD  rt        j                  |«      ƒ d{  –—†  	 t        j                  d| j                  t        |«      |r|dd nd«       |j                  d«      r| j                  d	 ||d
d «      i}
}	n2| j                  |«      r| j                  d|i}
}	n| j                  d|i}
}	 |	dd|i|
¤|xs d|dœ¤Žƒ d{  –—† }|j                  s,t        j                  d| j                  |j                  «       nd}�Œ |st!        dd¬«      S t!        ||rd¬«      S d¬«      S 7 �Œ7 Œi# t        $ r.}t        j                  d| j                  |d¬«       Y d}~�Œmd}~ww xY w­w)u®  Send ``(url, alt)`` images (``http(s)://`` or ``file://``) one by one (GIFs via
        ``send_animation``, local files via ``send_image_file``); override to bundle natively
        (Signal). Returns success when at least one image was delivered â€” the outcome
        the turn-level delivery tracker records; every override must return the same
        aggregate, or a media-only turn on that platform reports FAILURE (#106153).r   )ÚunquoteFNz[%s] Sending image: %s (alt=%s)é   r`   úfile://Ú
image_pathé   Úanimation_urlÚ	image_urlrR  )Úcaptionr|   z[%s] Failed to send image: %sTz[%s] Error sending image: %sr   zno images to sendrÝ  zall images failed to sendrf   )Úurllib.parserN  rÄ  rÛ  r   r  r  rs  r	  Úsend_image_fileÚ_is_animation_urlÚsend_animationÚ
send_imagerÜ  r   rO   rÛ  )r˜  rR  r  r|   rL  Ú_unquoteÚ	deliveredrT  Úalt_textÚsenderÚurl_kwÚ
img_resultÚimg_errs                r   Úsend_multiple_imagesz(BasePlatformAdapter.send_multiple_imagesF  s¨  è ø€ õ 	5Øˆ	Ø#)ó 	`ÑˆI�xØ˜QŠÜ—m‘m KÓ0×0Ð0ð`Ü—‘Ð=¸t¿y¹yÜ,¨YÓ7É(¸À#À2¹ÐXZô\à×'Ñ'¨	Ô2Ø%)×%9Ñ%9¸LÉ(ÐS\Ð]^Ð]_ÐS`ÓJaÐ;b˜F‘FØ×+Ñ+¨IÔ6Ø%)×%8Ñ%8¸?ÈIÐ:V˜F‘Fà%)§_¡_°{ÀIÐ6N˜F�FÙ#)ñ $\Ø#ð$\Ø'-ð$\Ø7?Ò7GÀ4ÐRZó$\÷ \�
à!×)Ò)Ü—L‘LÐ!@À$Ç)Á)ÈZ×M]ÑM]Õ^à $�Iùð#	`ñ( Ü eÐ3FÔGÐGÜØÙ#�$ôFð 	Fà)DôFð 	Fð) 1ùð\ùô ò `Ü—‘Ð;¸T¿Y¹YÈÐZ^�×_Ò_ûð`üsK   ‚/F±E²F·B%EÃEÃ>EÄ)FÅEÅ	E?Å#E:Å4FÅ:E?Å?FrT  rU  c              ƒ   ó^   K  — |r|› d|› �n|}| j                  ||||¬«      ƒ d{  –—† S 7 Œ­w)zFSend an image natively; default falls back to sending the URL as text.rL  r5  Nr6  )r˜  rR  rT  rU  rÒ  r|   rÜ   s          r   rZ  zBasePlatformAdapter.send_imagej  s:   è ø€ ñ -4�'�˜"˜Y˜KÑ(¸ˆØ—Y‘Y w¸ÀxÐZb�YÓc×cÐcÐcús   ‚$-¦+§-rS  c              ƒ   óJ   K  — | j                  |||||¬«      ƒ d{  –—† S 7 Œ­w)z_Send a GIF as a native animation (auto-plays inline); default falls back to
        send_image.)rR  rT  rU  rÒ  r|   N)rZ  )r˜  rR  rS  rU  rÒ  r|   s         r   rY  z"BasePlatformAdapter.send_animationq  s9   è ø€ ð
 —_‘_Ø }¸gÐPXÐckð %ó m÷ mð 	mð múrè  rd  c                 ód   — | j                  «       j                  d«      d   j                  d«      S )z=Check if a URL points to an animated GIF (vs a static image).rÞ  r   r#   )rb   rJ  Úendswith)rd  s    r   rX  z%BasePlatformAdapter._is_animation_urly  s+   € ð �y‰y‹{× Ñ  Ó% aÑ(×1Ñ1°&Ó9Ð9r   c                 ó  ‡‡— d}d}d}t        j                  || «      D �‡cg c]8  Št        ˆfd„|D «       «      r"‰j                  d«      ‰j                  d«      f‘Œ: }}|j	                  d„ t        j                  || «      D «       «       |s|| fS |D ��ch c]  \  }}|’Œ	 c}}Šˆfd„}| }	||fD ]  }
t        j
                  |
||	«      }	Œ |t        j
                  d	d
|	«      j                  «       fS c c}w c c}}w )z˜Extract ``![alt](url)`` and ``<img src=...>`` image URLs from a response;
        returns ``([(url, alt_text), ...], content with those tags removed)``.z$!\[([^\]]*)\]\((https?://[^\s\)]+)\)zA<img\s+src=["\']?(https?://[^\s"\'<>]+)["\']?\s*/?>\s*(?:</img>)?)r%   r$   r&   r#   r'   z	fal.mediazfal-cdnzreplicate.deliveryc              3   óÀ   •K  — | ]U  }‰j                  d «      j                  «       j                  |«      xs! |‰j                  d «      j                  «       v –— ŒW y­w)r]   N)rN  rb   rf  )rÈ   r˜   r¹  s     €r   rË   z5BasePlatformAdapter.extract_images.<locals>.<genexpr>‰  sS   øè ø€ ò -Ø ð Ÿ™ ›×)Ñ)Ó+×4Ñ4°SÓ9ÒV¸SÀAÇGÁGÈAÃJ×DTÑDTÓDVÐ=VÓVñ -ùs   ƒAAr]   r·   c              3   óB   K  — | ]  }|j                  d «      df–— Œ y­w)r·   r`   N)rN  )rÈ   rJ  s     r   rË   z5BasePlatformAdapter.extract_images.<locals>.<genexpr>‹  s   è ø€ Ò[¨u�u—{‘{ 1“~ rÔ*Ñ[ùs   ‚c                 ó”   •— | j                   dk\  r| j                  d«      n| j                  d«      }|‰v rdS | j                  d«      S )Nr]   r·   r`   r   )Ú	lastindexrN  )rJ  rd  Úextracted_urlss     €r   Ú_remove_if_extractedz@BasePlatformAdapter.extract_images.<locals>._remove_if_extracted‘  s?   ø€ Ø$)§O¡O°qÒ$8�%—+‘+˜a”.¸e¿k¹kÈ!»nˆCØ Ñ.�2ÐB°E·K±KÀ³NÐBr   ú\n{3,}rí  )rA  rp  rÒ   rN  rC  re  rz   )rß  Ú
md_patternÚhtml_patternÚmarkersr¹  r  rd  rÉ   rm  r„  rÌ  rl  s       `      @r   Úextract_imagesz"BasePlatformAdapter.extract_images~  s	  ù€ ð =ˆ
à[ˆð)ˆä46·K±KÀ
ÈGÓ4T÷ .¨qÜó -Ø$+ô-ô -ð —7‘7˜1“:˜qŸw™w q›zÒ*ð .ˆð .ð 	�‰Ñ[¼¿¹ÀLÐRYÓ8ZÔ[Ô[ÙØ˜7�?Ð"à,2×3¡& # qš#Ó3ˆô	Cð ˆØ" LÐ1ò 	EˆGÜ—f‘f˜WÐ&:¸GÓD‰Gð	Eà”r—v‘v˜i¨°Ó9×?Ñ?ÓAÐAÐAùò.ùó 4s   ¢=C6ÂC;r®   c           	   ‹   óL   K  — | j                  dd|||||«      ƒ d{  –—† S 7 Œ­w)zvSend audio as a native voice message (Telegram bubble / Discord attachment).
        Default: friendly failure notice.Ú
send_voiceró  N©Ú_send_media_fallback_notice)r˜  rR  r®   rU  rÒ  r|   r   s          r   rt  zBasePlatformAdapter.send_voice™  s8   è ø€ ð
 ×5Ñ5Ø˜' :¨w¸ÀÈ8óU÷ Uð 	Uð Uúó   ‚$�"ž$©r’   Úmethodr‘   r-   r’   c             ƒ   óª   K  — t         j                  d| j                  |||«       t        ||«      }	| j	                  ||	||||¬«      ƒ d{  –—† S 7 Œ­w)zÉShared default for send_voice/send_video/send_document/send_image_file. The local path is
        logged but NEVER echoed into chat (host layout leak); only the caller's ``file_name`` is
        shown.z3[%s] %s fallback: native %s send unavailable for %s©rU  rÒ  r|   Úshown_metadataN)r   rL   r  r—   Úemit_media_warning)
r˜  ry  r‘   r-   rR  rU  rÒ  r|   r’   rÜ   s
             r   rv  z/BasePlatformAdapter._send_media_fallback_notice¡  se   è ø€ ô 	�‰ÐLÈdÏiÉiÐY_ÐaeÐgkÔlÜ" 4¨Ó3ˆØ×,Ñ,¨W°dÀGÐV^ÐiqØ<Dð -ó F÷ Fð 	Fð Fús   ‚A
AÁAÁA)rÒ  r|   Úlogical_platformc             ƒ   ór   K  — | j                  |||¬«      sy| j                  ||||¬«      ƒ d{  –—† S 7 Œ­w)aJ  Present a classified channel diagnostic in the caller's owning scope.

        None means suppressed, NOT successfully sent. Transport receipts/exceptions
        pass through unchanged; logs and producer state belong outside this boundary.
        Existing routing/stream metadata is preserved, never inferred from text.
        ©rR  r|   N©rÒ  r|   )Úwarning_notifications_enabledrÕ  )r˜  rR  rß  rÒ  r|   r~  s         r   Úemit_warningz BasePlatformAdapter.emit_warning­  s?   è ø€ ð ×1Ñ1Ð2BÈGÐ^fÐ1ÔgØØ—Y‘Y˜w¨¸(ÈX�YÓV×VÐVÐVús   ‚.7°5±7r{  Únoticec             ƒ   ó¾   K  — | j                  ||r|› d|› �n|||¬«      ƒ d{  –—† }|�|S |r| j                  ||||¬«      ƒ d{  –—†  t        d|¬«      S 7 Œ47 Œ­w)aŠ  Present an optional media diagnostic without losing the requested caption.

        Preserve the legacy fallback-text receipt when shown (``shown_metadata`` is the exact
        metadata the legacy shown path passed; default None keeps callers that sent none
        byte-identical). When hidden, preserve the media failure even if the independent
        caption itself was delivered.
        rL  r�  NFrÝ  )rƒ  rÕ  rÛ  )r˜  rR  r„  rU  rÒ  r|   r|  rY   s           r   r}  z&BasePlatformAdapter.emit_media_warningº  s   è ø€ ð ×(Ñ(¨ÉG°W°I¸RÀ¸xÑ2HÐY_Ø2:À^ð )ó U÷ UˆàÐØˆMÙØ—)‘)˜G W°xÈ(�)ÓS×SÐSÜ %¨vÔ6Ð6ðUøð
 Tús!   ‚"A¤A¥"AÁAÁAÁA©r~  rR  r|   ÚvisibleÚhiddenc                ó2   — | j                  |||¬«      r|S |S )a  Project mixed content: the diagnostic variant when visible, else the requested remainder.

        For payloads that combine a requested result (caption, answer) with an automatic
        diagnostic. The requested part must be present in BOTH variants; never hide it.
        r€  ©r‚  )r˜  r‡  rˆ  r~  rR  r|   s         r   Úwarning_textz BasePlatformAdapter.warning_textÍ  s%   € ð ×-Ñ-Ð.>ÈÐZbÐ-ÔcØˆNØˆr   r€  c                ó:   — ddl m}  ||xs | j                  «      S )zNPresentation policy under the caller's owning profile; old plugins inherit it.r   rŠ  )Úgateway.warning_notificationsr‚  rc   )r˜  r~  rR  r|   r‚  s        r   r‚  z1BasePlatformAdapter.warning_notifications_enabled×  s   € åOÙ,Ð-=Ò-NÀÇÁÓOÐOr   c                 óÚ   — 	 ddl m}  ||d¬«      S # t        $ rO t        j                  dd|t        j
                  ¬«      }t        j                  dd	|«      j                  «       cY S w xY w)
z»Chat Markdown -> transcript-like spoken script (reasoning blocks removed,
        headings/bullets flattened, units expanded). Chunking and delivery limits are the TTS tool's
        job.r   )Úprepare_spoken_textN)Ú	max_charsz<think[\s>].*?</think>r}  )r(  z[*_`#\[\]()]r`   )Útools.tts_text_normalizer�  rO   rA  re  ÚDOTALLrz   )r˜  rÜ   r�  s      r   Úprepare_tts_textz$BasePlatformAdapter.prepare_tts_textÜ  s[   € ð	=ÝDÙ& t°tÔ<Ð<øÜò 	=ä—6‘6Ð3°S¸$ÄbÇiÁiÔPˆDÜ—6‘6˜/¨2¨tÓ4×:Ñ:Ó<Ò<ð	=ús   ‚ ’AA*Á)A*c              ‹   óH   K  —  | j                   d||dœ|¤Žƒ d{  –—† S 7 Œ­w)zSPlay auto-TTS audio; override for invisible playback (Web UI). Default: send_voice.)rR  r®   Nrf   )rt  )r˜  rR  r®   r   s       r   Úplay_ttszBasePlatformAdapter.play_ttsè  s'   è ø€ à$�T—_‘_ÐV¨WÀÑVÈvÑV×VÐVÐVúr8  rT  c                  ó   — y)zEReturn True when this adapter can accept streaming PCM for *chat_id*.Frf   )r˜  rR  rT  s      r   Úsupports_streaming_ttsz*BasePlatformAdapter.supports_streaming_ttsô  s   € àr   c              ƒ   ó   K  — y­w)z�Open a streaming-audio session; returns an opaque handle for the
        write/finish/abort calls, or ``None`` to decline (whole-file fallback).Nrf   )r˜  rR  rT  r|   s       r   Úbegin_streaming_ttsz'BasePlatformAdapter.begin_streaming_ttsø  ó   è ø€ ð
 ùrÏ  Úhandler”  c              ƒ   ó   K  — y­w)z:Write one PCM chunk to the adapter's outbound audio track.Nrf   )r˜  r›  r”  s      r   Úwrite_streaming_ttsz'BasePlatformAdapter.write_streaming_ttsÿ  rÎ  rÏ  )Úinterruptedrž  c             ƒ   ó   K  — y­w)z&Signal normal end of the audio stream.Nrf   )r˜  r›  rž  s      r   Úfinish_streaming_ttsz(BasePlatformAdapter.finish_streaming_tts  rÎ  rÏ  r   c              ƒ   ó   K  — y­w)z³Abort the stream due to an error or cancellation. Must be idempotent: late producer
        chunks after abort are silently dropped, not raised. Restores state to "not streaming".Nrf   )r˜  r›  r   s      r   Úabort_streaming_ttsz'BasePlatformAdapter.abort_streaming_tts  rÎ  rÏ  r�   rX  c                ó   — t        |||¬«      S )Nr�   )r[  ©r˜  rW  rX  r‚   s       r   Ú_streaming_tts_turn_keyz+BasePlatformAdapter._streaming_tts_turn_key	  s   € Ü% k°;ÀeÔLÐLr   c                ór   — | j                  |||¬«      }|�!t        | dt        «      j                  |«       y y )Nr�   r�  )r¥  rZ  rv  rÿ  )r˜  rW  rX  r‚   r^  s        r   Ú"_mark_streaming_tts_completed_turnz6BasePlatformAdapter._mark_streaming_tts_completed_turn  s=   € Ø×/Ñ/°¸[ÐPUÐ/ÓVˆØÐÜ�tÐ=¼sÓC×GÑGÈÕQð  r   c                óF   — t        t        | dt        «       «      |||¬«      S )Nr�  r�   )r_  ra   rv  r¤  s       r   Ú_streaming_tts_turn_completedz1BasePlatformAdapter._streaming_tts_turn_completed  s(   € Ü3Ü�DÐ:¼C»EÓBÀKÐQ\Ðdiôkð 	kr   Ú
video_pathc           	   ‹   óL   K  — | j                  dd|||||«      ƒ d{  –—† S 7 Œ­w)zJSend a video natively (inline playable). Default: friendly failure notice.Ú
send_videor   Nru  )r˜  rR  rª  rU  rÒ  r|   r   s          r   r¬  zBasePlatformAdapter.send_video  s8   è ø€ ð ×5Ñ5Ø˜' :¨w¸ÀÈ8óU÷ Uð 	Uð Uúrw  Ú	file_pathc           
   ‹   óP   K  — | j                  dd||||||¬«      ƒ d{  –—† S 7 Œ­w)z@Send a document/file natively. Default: friendly failure notice.Úsend_documentr£  rx  Nru  )r˜  rR  r­  rU  r’   rÒ  r|   r   s           r   r¯  z!BasePlatformAdapter.send_document  s@   è ø€ ð ×5Ñ5Ø˜V Y°¸À(ÈHÐ`ið 6ó k÷ kð 	kð kús   ‚&Ÿ$ &©r™   r|   Ú
media_pathr™   c             ƒ   ó  K  — t        |«      j                  j                  «       }|st        | j                  ||¬«      rt        d«      }n=|t        v rt        d«      }n)t        dt        j                  j                  |«      «      }	 | j                  |||¬«      ƒ d{  –—† }|�|j                  rdn|j                  }|�"t        j                  d| j                   |«       yy7 ŒE# t        $ r}	|	}Y d}	~	Œ7d}	~	ww xY w­w)aÂ  User-visible notice when a MEDIA attachment upload failed: the tag was
        already stripped from the text, so silence would be a silent drop.

        The non-streaming dispatch loop strips ``MEDIA:`` tags before sending attachments. When the
        subsequent upload returns ``success=False`` (for example Discord accepted the message but attached
        nothing), the user must see a failure notice instead of a silent drop (#66797).
        ©r™   ró  r   r£  r  Nz5[%s] Could not send media-delivery-failure notice: %s)r)  r2   rb   rž   rc   r—   Ú_VIDEO_EXTSrC   r-   rq  rƒ  rÜ  r   rO   r   r�  r  )
r˜  rR  r±  r™   r|   r˜   rÜ   r„  ÚproblemÚ
notify_errs
             r   Ú_notify_media_delivery_failurez2BasePlatformAdapter._notify_media_delivery_failure#  sß   è ø€ ô �:Ó×%Ñ%×+Ñ+Ó-ˆÙÔ1°$·-±-ÀÈxÕXÜ& wÓ/‰DØ”KÑÜ& wÓ/‰Dä& v¬r¯w©w×/?Ñ/?À
Ó/KÓLˆDð	!Ø×,Ñ,¨W°dÀXÐ,ÓN×NˆFØ$˜n°·²‘dÀFÇLÁLˆGð ÐÜ�L‰LÐPÐRV×R[ÑR[Ð]dÕeð ð	 Oùäò 	!Ø �Gûð	!üsB   ‚BDÂ
C) Â!C'Â" C) Ã%DÃ'C) Ã)	C>Ã2C9Ã4DÃ9C>Ã>DrQ  c           	   ‹   óL   K  — | j                  dd|||||«      ƒ d{  –—† S 7 Œ­w)zTSend a local image file natively (send_image takes a URL). Default: friendly notice.rW  r­  Nru  )r˜  rR  rQ  rU  rÒ  r|   r   s          r   rW  z#BasePlatformAdapter.send_image_file<  s9   è ø€ ð ×5Ñ5Ø˜w¨
°G¸WÀhÐPXóZ÷ Zð 	Zð Zúrw  c                 ó   — t        | |¬«      S )zBReturn a resolved path if it is safe for native attachment upload.rÖ  )rÜ  )r-   rW  s     r   rÜ  z0BasePlatformAdapter.validate_media_delivery_pathC  s   € ô ,¨D¸kÔJÐJr   rã  c           
      óv   — | xs g D ��cg c]"  \  }}t        ||d|«      x}r|t        |«      f‘Œ$ c}}S c c}}w )zWDrop unsafe MEDIA paths and normalize accepted paths; ``dropped`` collects the rejects.zMEDIA directive path)rê  r  )rÅ  rW  rã  r±  r™   ré  s         r   Úfilter_media_delivery_pathsz/BasePlatformAdapter.filter_media_delivery_pathsH  sU   € ð
 EPÒDUÐSU÷rÙ,@¨J¸Ü5°jÀ+ÐOeÐgnÓoÐo�	Ðoð œ˜X›Ò'órð 	rùó rs   Š'5c                 óR   ‡— ˆfd„| xs g D «       }|D �cg c]  }|sŒ|‘Œ	 c}S c c}w )z?Drop unsafe bare local file paths and normalize accepted paths.c              3   ó8   •K  — | ]  }t        |‰d «      –— Œ y­w)zlocal file pathN)rê  )rÈ   r'  rW  s     €r   rË   zBBasePlatformAdapter.filter_local_delivery_paths.<locals>.<genexpr>S  s   øè ø€ ÒlÐVWÔ.¨q°+Ð?P×QÑlùs   ƒrf   )Ú
file_pathsrW  Ú
safe_pathsr'  s    `  r   Úfilter_local_delivery_pathsz/BasePlatformAdapter.filter_local_delivery_pathsP  s-   ø€ ó mÐ[eÒ[kÐikÔlˆ
Ø%Ö+�aª’Ò+Ð+ùÒ+s   •$�$c           
      ó˜  — t         j                  | «      D �cg c]  }|j                  «       ‘Œ }}t        j                  | «      D ]·  }|j	                  «       }t        j                  d| t        d|dz
  «      | «      rŒ:|j                  d«      dd j                  «       }|j                  «       j                  d«      rt        |dd «      }|rt        |«      rŒ—|j                  ||j                  «       f«       Œ¹ |j!                  d	„ t        j                  d
| t
        j"                  «      D «       «       t%        | |«      S c c}w )zÇBlank fenced code, inline code and blockquotes (length-preserving so regex offsets stay
        valid) against MEDIA: false positives; backtick-quoted paths inside MEDIA: tags stay
        scannable.z
MEDIA:\s*$r   é   r·   rh  rM  r³  Nc              3   ó<   K  — | ]  }|j                  «       –— Œ y ­wr¤  )rv  )rÈ   r¹  s     r   rË   z<BasePlatformAdapter._mask_protected_spans.<locals>.<genexpr>i  s   è ø€ ÒT !�Q—V‘V—XÑTùrk  z^>.*$)rx  rp  rv  ry  rP  rA  ÚsearchrZ  rN  rz   Úupperr	  rØ  rÜ  r�  rO  rC  Ú	MULTILINErf  )rß  r¹  rl  rP  Úinnerr»  s         r   re  z)BasePlatformAdapter._mask_protected_spansV  s  € ô
 *9×)AÑ)AÀ'Ó)JÖK A�q—v‘v•xÐKˆÐKÜ ×)Ñ)¨'Ó2ò 	+ˆAØ—G‘G“IˆEÜ�y‰y˜¨´°A°u¸r±zÓ0BÀ5Ð(IÔJØð —G‘G˜A“J˜q Ð$×*Ñ*Ó,ˆEØ�{‰{‹}×'Ñ'¨Ô1Ü5°e¸A¸B°iÓ@�	ÙÔ!=¸iÔ!HØØ�L‰L˜% §¡£Ð)Õ*ð	+ð 	�‰ÑT¤r§{¡{°8¸WÄbÇlÁlÓ'SÔTÔTÜ˜G UÓ+Ð+ùò Ls   ˜Ec           	      óæ   — d| vsd| vr| S t        j                  d| «      D �cg c]8  }t        j                  d|j                  d«      «      r|j	                  d«      ‘Œ: }}t        | |«      S c c}w )aj  Blank ``MEDIA:<bare-path>`` tags inside JSON string *values* (stored tool-result text
        like ``{"result": "MEDIA:/x/stale.png"}``) so they are never re-delivered. Only
        value-context strings (``:,{[`` before the ``"``) and bare paths (``/``, ``~/``, ``X:\``)
        count; ``MEDIA:"..."`` quoted tags and line-start/prose tags are untouched. Offsets
        preserved.

        Here the ``MEDIA:`` is part of stored text, not an outbound directive, but the bare-path branch of
        ``MEDIA_TAG_CLEANUP_RE`` would still match it and re-deliver a stale file. (Regression report
        #34375.)
        rÅ  rM  z$(?<=[:,{\[])\s*"((?:[^"\\\n]|\\.)*)"z MEDIA:\s*(?:~/|/|[A-Za-z]:[/\\])r·   )rA  rp  rÄ  rN  rv  rf  )rß  r¹  rl  s      r   rd  z+BasePlatformAdapter._mask_json_string_medial  s{   € ð �gÑ °Ñ!8ØˆNô  "Ÿ{™{Ð+RÐT[Ó\öKØÜ�y‰yÐ<¸a¿g¹gÀa»jÔIð �F‰F�1�IðKˆð Kô ˜G UÓ+Ð+ùòKs   £=A.c                 óL  ‡	‡
‡— g Š
d| v Š	| j                  dd«      j                  dd«      }t        | «      }t        «       Šdt        ddfˆ	ˆ
ˆfd„}t        j                  |«      D ]E  }t        |j                  d«      «      }|sŒ 	  |t        j                  j                  |«      «       ŒG t        |«      D ]  \  }}} ||«       Œ ‰
r<t        |«      }|r/t!        j"                  dd	t%        ||«      «      j'                  «       }‰
|fS # t        t        t        f$ r Y Œ¼w xY w)
aI  Extract ``MEDIA:<path>`` tags and strip ``[[audio_as_voice]]`` / ``[[as_document]]`` ->
        ``([(path, is_voice), ...], cleaned)``. Both directives are message-global;
        ``[[as_document]]`` (unmodified sendDocument for large images) is detected by dispatch sites
        on the ORIGINAL response and only stripped here.r[  r`   r\  r-   r	   Nc                 óÈ   •— | ‰vr]‰j                  | «       ‰j                  | ‰xr6 t        j                  j	                  | «      d   j                  «       t        v f«       y y )Nr·   )rÿ  r�  rC   r-   r�  rb   r›   )r-   Úhas_voice_tagr„  Ú
seen_pathss    €€€r   Ú_addz/BasePlatformAdapter.extract_media.<locals>._add”  sU   ø€ à˜:Ñ%Ø—‘˜tÔ$Ø—‘˜d MÒ$f´b·g±g×6FÑ6FÀtÓ6LÈQÑ6O×6UÑ6UÓ6WÔ[fÐ6fÐgÕhð &r   rn  rí  )r<   ri  rv  rF   ru  rp  rØ  rN  rC   r-   r6  rR   rt  rÍ   rq  rm  rA  re  r‚  rz   )rß  r„  Úscan_contentrÍ  rJ  r-   rÉ   ré  rl  rË  r„  rÌ  s            @@@r   Úextract_mediaz!BasePlatformAdapter.extract_media�  s)  ú€ ð ˆØ,°Ð7ˆØ—/‘/Ð"6¸Ó;×CÑCÐDUÐWYÓZˆô -¨WÓ5ˆô ›%ˆ
ð	i”sð 	i˜t÷ 	iô
 *×2Ñ2°<Ó@ò 	ˆEÜ,¨U¯[©[¸Ó-@ÓAˆDÚðÙœŸ™×+Ñ+¨DÓ1Õ2ð		ô  <¸LÓIò 	‰OˆAˆy˜!Ù��Oð	ñ Ü*¨7Ó3ˆEÙÜŸ&™& ¨F´MÀ'È5Ó4QÓR×XÑXÓZ�Ø�gˆ~Ðøô  ¤¬zÐ:ò Ùðús   Â%DÄD#Ä"D#c                 óx   — t        | «      s| S t        j                  ddt        | «      «      j	                  «       S )a	  Strip MEDIA: directives from streamed/display text. Known-extension tags are
        removed unconditionally (as ``MEDIA_TAG_CLEANUP_RE``); extension-less tags only when
        ``validate_media_delivery_path`` accepts the path, so undeliverable paths stay visible.rn  rí  )r^  rA  re  r…  rÊ  r]  s    r   Ú"strip_media_directives_for_displayz6BasePlatformAdapter.strip_media_directives_for_displayª  s3   € ô
 % TÔ*ØˆKÜ�v‰v�i Ô)DÀTÓ)JÓK×RÑRÓTÐTr   c                 óÀ  ‡— dj                  d„ t        D «       «      }t        j                  d|z   dz   t        j                  «      }t        | «      }i }|j                  | «      D ]˜  Št        ˆfd„|D «       «      rŒ‰j                  d«      }t        j                  j                  |«      }t        j                  j                  |«      r|j                  ||«       Œzt        j                  dt!        |«      «       Œš |sg | fS | }|j#                  «       D ]  }|j%                  |d«      }Œ t'        |«      t        j(                  d	d
|«      j+                  «       fS )aX  Bare local file paths (absolute, ``~/`` or drive-letter) with deliverable extensions ->
        ``(expanded_paths, cleaned_text)``. Candidates must exist on disk (URLs / hallucinated paths
        ignored); paths inside fenced or inline code are skipped so code samples are never
        mutilated. Dispatch by type lives in ``gateway/run.py``.r@  c              3   ó>   K  — | ]  }|j                  d «      –— Œ y­wrB  rC  rE  s     r   rË   z:BasePlatformAdapter.extract_local_files.<locals>.<genexpr>¹  s   è ø€ ÒG¨a˜AŸH™H SŸMÑGùrG  zB(?<![/:\w.])(?:~/|/|[A-Za-z]:[/\\])(?:[\w.\-]+[/\\])*[\w.\-]+\.(?:z)\bc              3   ó`   •K  — | ]%  \  }}|‰j                  «       cxk  xr |k  nc –— Œ' y ­wr¤  rt  )rÈ   r°   rF  rJ  s      €r   rË   z:BasePlatformAdapter.extract_local_files.<locals>.<genexpr>Ä  s(   øè ø€ ÒA©d¨a°�1˜Ÿ™›Ö)¨×)Ð)ÑAùs   ƒ+.r   z6Skipping bare file path in reply (no file on disk): %sr`   rn  rí  )r§   r?  rA  ÚcompileÚ
IGNORECASEr{  rp  rÒ   rN  rC   r-   r6  ÚisfileÚ
setdefaultr   r  r¾  rF  r<   r0  re  rz   )	rß  Úext_partÚpath_reÚ
code_spansÚuniquern  rÙ  r„  rJ  s	           @r   Úextract_local_filesz'BasePlatformAdapter.extract_local_files³  s5  ø€ ð —8‘8ÑGÔ3FÔGÓGˆô
 —*‘*ØQÐT\Ñ\Ð_eÑeÜ�M‰Móˆô ! Ó)ˆ
ØˆØ×%Ñ% gÓ.ò 		kˆEÜÓA°jÔAÔAØØ—+‘+˜a“.ˆCÜ—w‘w×)Ñ)¨#Ó.ˆHÜ�w‰w�~‰~˜hÔ'Ø×!Ñ! (¨CÕ0ô —‘ÐTÔVdÐehÓViÕjð		kñ Ø�w�;ÐØˆØ—=‘=“?ò 	/ˆCØ—o‘o c¨2Ó.‰Gð	/ä�F‹|œRŸV™V I¨v°wÓ?×EÑEÓGÐGÐGr   ÚintervalÚ
stop_eventc              ƒ   óH  K  — t        dt        d|dz
  «      «      }	 	 |�m|j                  «       r]	 | j                  ||«      ƒ d{  –—†  | j                  j                  |«       t        | di «      j                  t        |«      d«       y|| j                  vr1	 t        j                  | j                  ||¬«      |¬«      ƒ d{  –—†  |€t        j"                  |«      ƒ d{  –—†  ŒÏt        j$                  «       }|j'                  «       |z   }|j                  «       sQ||j'                  «       z
  }	|	dk  rn8t        j"                  t        d|	«      «      ƒ d{  –—†  |j                  «       sŒQ�ŒX7 �Œ07 Œ±# t        j                  $ r Y ŒÄt        $ r+}t        j                  d| j                   |«       Y d}~Œòd}~ww xY w7 Œß7 Œi# t        j(                  $ r Y nw xY w	 | j                  ||«      ƒ d{  –—†7   | j                  j                  |«       t        | di «      j                  t        |«      d«       y# | j                  ||«      ƒ d{  –—†7   | j                  j                  |«       t        | di «      j                  t        |«      d«       w xY w­w)	u  Refresh the typing indicator every ``interval`` seconds until cancelled (platform typing
        state expires after ~5s). Chats in ``_typing_paused`` are skipped (approval waits â€” Slack's
        setStatus disables the compose box). Each ``send_typing`` is bounded by a sub-interval
        timeout so one slow round-trip is abandoned before the next tick, not the bubble lapsing.g      Ð?rÓ  Nr_  r  rÀ  z&[%s] send_typing error (non-fatal): %sr   )rZ  r  Úis_setÚ_stop_typing_quietlyr‘  r  ra   r`  rF   rÄ  rÇ  r;  rÉ  rO   r   r�  r  rÛ  rÃ  rê  r
  )
r˜  rR  rÞ  r|   rß  Ú_send_typing_timeoutÚ
typing_errr¼  ÚdeadlineÚ	remainings
             r   Ú_keep_typingz BasePlatformAdapter._keep_typingÔ  s]  è ø€ ô  # 4¬¨S°(¸T±/Ó)BÓCÐð	FØØÐ)¨j×.?Ñ.?Ô.AØð2 ×+Ñ+¨G°XÓ>×>Ð>Ø×Ñ×'Ñ'¨Ô0ä�D˜.¨"Ó-×1Ñ1´#°g³,ÀÕEð7  $×"5Ñ"5Ñ5ðfÜ%×.Ñ.¨t×/?Ñ/?ÀÐRZÐ/?Ó/[Ø7KôM÷ Mð Mð Ð%Ü!Ÿ-™-¨Ó1×1Ð1ØÜ×/Ñ/Ó1�ØŸ9™9›;¨Ñ1�Ø$×+Ñ+Ô-Ø (¨4¯9©9«;Ñ 6�IØ  A’~Øô "Ÿ-™-¬¨D°)Ó(<Ó=×=Ð=ð %×+Ñ+Õ-ñ! ð6 ?ùð-Mùä"×/Ñ/ò ÙÜ$ò fÜŸ™Ð%MÈtÏyÉyÐZd×eÑeûðfúð 2øð >ùÜ×%Ñ%ò 	Ùð	úØð ×+Ñ+¨G°XÓ>×>Ñ>Ø×Ñ×'Ñ'¨Ô0ä�D˜.¨"Ó-×1Ñ1´#°g³,ÀÕEøð ×+Ñ+¨G°XÓ>×>Ñ>Ø×Ñ×'Ñ'¨Ô0ä�D˜.¨"Ó-×1Ñ1´#°g³,ÀÕEüsÙ   ‚J"�G
 ±J"ÁE6ÁAJ"ÂG
 Â+E; ÃE9ÃE; ÃG
 Ã&GÃ'A7G
 ÅGÅG
 Å4G
 Å6J"Å9E; Å;GÆG
 ÆGÆ!F>Æ9G
 Æ>GÇG
 ÇG
 Ç
G ÇI ÇG Ç I Ç$J"Ç9G<Ç:AJ"ÉJÉIÉAJÊJ"g      à?r]   )r|   r+   Ústop_attemptsÚtyping_taskr+   rè  c             ƒ   ó¤  K  — | j                   j                  |«       	 |��|j                  «       s}|j                  «        t	        j
                  t        j                  t        j                  «      5  t        j                  t        j                  |«      |¬«      ƒ d{  –—†  ddd«       t        t        d|«      «      D ];  }|rt        j                  d«      ƒ d{  –—†  | j                  ||«      ƒ d{  –—†  Œ= 	 | j                   j                  |«       y7 Œ|# 1 sw Y   Œ{xY w7 ŒK7 Œ3# | j                   j                  |«       w xY w­w)zAStop the refresh task and platform typing state as one operation.NrÀ  r·   r   )r‘  rÿ  rº  r»  rP   rQ   rÄ  r
  rÉ  rÇ  r	  rÕ  rZ  rÛ  râ  r  )r˜  rR  ré  r|   r+   rè  rÞ  s          r   Ú_stop_typing_refreshz(BasePlatformAdapter._stop_typing_refreshü  s  è ø€ ð 	×Ñ×Ñ Ô(ð	1ØÐ&¨{×/?Ñ/?Ô/AØ×"Ñ"Ô$ä×(Ñ(¬×)?Ñ)?Ä×AUÑAUÓVñ YÜ!×*Ñ*¬7¯>©>¸+Ó+FÐPWÔX×XÐX÷Yä ¤ Q¨Ó!6Ó7ò C�ÙÜ!Ÿ-™-¨Ó*×*Ð*Ø×/Ñ/°¸ÓB×BÑBñCð
 ×Ñ×'Ñ'¨Õ0ð Yø÷Yð Yúð +øØBùà×Ñ×'Ñ'¨Õ0üse   ‚EŸAD0 Á3.D Â!DÂ"D Â&:D0 Ã D,Ã!D0 Ã:D.Ã;D0 ÄEÄD Ä D)Ä%D0 Ä.D0 Ä0EÅEc              ƒ   ó    K  — t        j                  t        «      5  | j                  ||«      ƒ d{  –—†  ddd«       y7 Œ# 1 sw Y   yxY w­w)z?Best-effort platform stop_typing; adapter errors are swallowed.N)rP   rQ   rO   rK  r:  s      r   râ  z(BasePlatformAdapter._stop_typing_quietly  sL   è ø€ ä× Ñ ¤Ó+ñ 	EØ×1Ñ1°'¸8ÓD×DÐD÷	Eð 	EØDø÷	Eð 	Eüs+   ‚AœA²A ³A·	AÁ AÁAÁAc                 ó:   — | j                   j                  |«       y)zNPause typing for a chat (approval waits); GIL-safe from the sync agent thread.N)r‘  rÿ  rš  s     r   Úpause_typing_for_chatz)BasePlatformAdapter.pause_typing_for_chat  s   € à×Ñ×Ñ Õ(r   c                 ó:   — | j                   j                  |«       y)z;Resume typing indicator for a chat after approval resolves.N)r‘  r  rš  s     r   Úresume_typing_for_chatz*BasePlatformAdapter.resume_typing_for_chat  s   € à×Ñ×#Ñ# GÕ,r   c              ƒ   óž   K  — |r+|| j                   v r| j                   |   j                  «        | j                  ||«      ƒ d{  –—†  y7 Œ­w)zDSignal the active session loop to stop and clear typing immediately.N)rz  rv  râ  )r˜  rW  rR  r|   s       r   Úinterrupt_session_activityz.BasePlatformAdapter.interrupt_session_activity  sC   è ø€ á˜;¨$×*?Ñ*?Ñ?Ø×!Ñ! +Ñ.×2Ñ2Ô4Ø×'Ñ'¨°Ó:×:Ò:ús   ‚AAÁAÁA©Ú
generationrô  c                óZ  — |rt        |«      sy| j                  j                  |«      }|�bt        |«      \  }}|�|�t	        |«      t	        |«      k  ryt        |«      r-|�|�t	        |«      t	        |«      k(  r| j                  ||«      }|€|nt	        |«      |f| j                  |<   y)u.  Register a deferred callback to fire after the main response. Same-key registrations are
        chained (both fire in order, per-callback exception isolation) so independent features
        coexist; ``generation`` ties it to a gateway run â€” stale generations never overwrite a
        fresher slot.N)r`  r‡  r�   rU  rN  Ú_chain_callbacks)r˜  rW  rG  rô  r¾  Úexisting_genÚexisting_cbs          r   Úregister_post_delivery_callbackz3BasePlatformAdapter.register_post_delivery_callback!  s·   € ñ ¤(¨8Ô"4ØØ×0Ñ0×4Ñ4°[ÓAˆØÐÜ(BÀ8Ó(LÑ%ˆL˜+ØÐ'¨JÐ,BÄsÈ:ÃÔY\Ð]iÓYjÒGjØä˜Ô$ØÐ$¨
Ð(:¼cÀ,Ó>OÔSVÐWaÓSbÒ>bØ×0Ñ0°¸hÓG�à"Ð*‰H´°Z³À(Ð0Kð 	×%Ñ% kÒ2r   Ú	callbacksc                  ó   ‡ — dˆ fd„}|S )z¡Async wrapper running ``callbacks`` in order with per-callback exception isolation;
        async so coroutines returned by async hooks are awaited, not dropped.c               “   óº   •K  — ‰D ])  } 	  | «       }t        j                  |«      r
|ƒ d {  –—†  Œ+ y 7 Œ# t        $ r t        j	                  dd¬«       Y ŒQw xY w­w)NzPost-delivery callback failedTr   )rA  ÚisawaitablerO   r   r�  )Ú_cbÚ_resultrú  s     €r   Ú_chainedz6BasePlatformAdapter._chain_callbacks.<locals>._chained9  sa   øè ø€ Ø ò Q�ðQÙ!›e�GÜ×*Ñ*¨7Ô3Ø%Ÿ˜øñ	Qð &ùÜ ò QÜ—L‘LÐ!@È4�LÖPðQüs1   ƒAŠ!5«3¬5°A³5µ AÁAÁAÁArê  rf   )rú  r   s   ` r   rö  z$BasePlatformAdapter._chain_callbacks5  s   ø€ õ	Qð ˆr   c                óð   — |r| j                   j                  |«      nd}|€yt        |«      \  }}|�|�t        |«      t        |«      k7  ry| j                   j	                  |d«       t        |«      r|S dS )zCPop a deferred callback, optionally requiring generation ownership.N)r‡  r�   rU  rN  r`  r`  )r˜  rW  rô  rŽ  Úentry_generationrG  s         r   Úpop_post_delivery_callbackz.BasePlatformAdapter.pop_post_delivery_callbackC  s„   € ñ CN�×-Ñ-×1Ñ1°+Ô>ÐSWˆØˆ=ØÜ%?ÀÓ%FÑ"Ð˜(ØÐ!Ð'7Ð'?Ä3ÐGWÓCXÔ\_Ð`jÓ\kÒCkØØ×%Ñ%×)Ñ)¨+°tÔ<Ü# HÔ-ˆxÐ7°4Ð7r   Ú
_ACK_EMOJIÚ	_OK_EMOJIÚ_FAIL_EMOJIc              ƒ   ó   K  — y­w)z.Hook called when background processing begins.Nrf   r  s     r   Úon_processing_startz'BasePlatformAdapter.on_processing_startV  rÎ  rÏ  Úoutcomec              ƒ   ó  K  — | j                   €| j                  €yt        | dd«      }t        | dd«      }t        | dd«      }t        |j                  dd«      }t        |dd«      }t	        |«      r!t	        |«      rt	        |«      r |«       r|r|sy |||«      ƒ d{  –—†  t
        j                  | j                   t
        j                  | j                  ij                  |«      }|r ||||«      ƒ d{  –—†  yy7 Œ_7 Œ­w)uv  Hook called when background processing completes. Default: opt-in reaction ack â€” with
        ``_OK_EMOJI``/``_FAIL_EMOJI`` set and ``_add_reaction``/``_remove_reaction`` present, swap
        the in-progress reaction for the outcome one. Remove-then-add is deterministic whether the
        platform replaces or stacks a sender's reactions. CANCELLED leaves it unreacted.NÚ_add_reactionÚ_remove_reactionÚ_reactions_enabledrR  rv   )	r  r  ra   r{   r`  r<  ÚSUCCESSÚFAILUREr�   )	r˜  r‚   r	  rÿ  r¥  ÚenabledrR  rv   Úemojis	            r   Úon_processing_completez*BasePlatformAdapter.on_processing_completeY  s÷   è ø€ ð
 �>‰>Ð! d×&6Ñ&6Ð&>ØÜ˜4 °$Ó7ˆÜ˜dÐ$6¸Ó=ˆÜ˜$Ð 4°dÓ;ˆÜ˜%Ÿ,™,¨	°4Ó8ˆÜ˜U L°$Ó7ˆ
Ü˜”¤X¨fÔ%5¼(À7Ô:KÑT[ÔT]Ù¡jØÙ�W˜jÓ)×)Ð)Ü"×*Ñ*¨D¯N©NÜ"×*Ñ*¨D×,<Ñ,<ð>ß>A¹cÀ'»lð 	áÙ�g˜z¨5Ó1×1Ñ1ð ð 	*øð 2ús%   ‚BDÂ D Â!ADÃ9DÃ:DÄDÚ	hook_namerÉ  r   c              �   óÔ   K  — t        | |d«      }t        |«      sy	  ||i |¤Žƒ d{  –—†  y7 Œ# t        $ r,}t        j	                  d| j
                  ||«       Y d}~yd}~ww xY w­w)zARun a lifecycle hook without letting failures break message flow.Nz[%s] %s hook failed: %s)ra   r`  rO   r   rL   r  )r˜  r  rÉ  r   ÚhookrF  s         r   Ú_run_processing_hookz(BasePlatformAdapter._run_processing_hookn  sb   è ø€ ä�t˜Y¨Ó-ˆÜ˜Œ~Øð	OÙ˜Ð' Ñ'×'Ò'ùÜò 	OÜ�N‰NÐ4°d·i±iÀÈA×NÑNûð	Oüs6   ‚A(�0 ¨.©0 ­A(®0 °	A%¹"A ÁA(Á A%Á%A(c                 ó\   ‡— | xs dj                  «       Št        ˆfd„t        D «       «      S )zGReturn True if the error string looks like a transient network failure.r`   c              3   ó&   •K  — | ]  }|‰v –— Œ
 y ­wr¤  rf   ©rÈ   ÚpatÚlowereds     €r   rË   z:BasePlatformAdapter._is_retryable_error.<locals>.<genexpr>|  s   øè ø€ ÒG c�3˜'”>ÑGùrø  )rb   rÒ   r  ©r   r  s    @r   Ú_is_retryable_errorz'BasePlatformAdapter._is_retryable_errorx  s)   ø€ ð ’;˜B×%Ñ%Ó'ˆÜÓGÔ-FÔGÓGÐGr   c                 ó(   — t        d| xs d«      dk(  S )a  Return True if the error string classifies as a rate limit / flood cap.

        Single wrapper around :func:`classify_send_error` so the call sites in
        :meth:`_send_with_retry` share one notion of "is this a rate limit" instead
        of inline copies that could drift.
        Nr`   ré  )r  )r   s    r   Ú_is_rate_limited_errorz*BasePlatformAdapter._is_rate_limited_error~  s   € ô # 4¨ª°"Ó5¸ÑGÐGr   c                 óT   ‡— | xs dj                  «       Št        ˆfd„dD «       «      S )u™   Return True for read/write timeouts â€” NOT retryable and NOT a plain-text
        fallback trigger, because the request may already have been delivered.r`   c              3   ó&   •K  — | ]  }|‰v –— Œ
 y ­wr¤  rf   r  s     €r   rË   z8BasePlatformAdapter._is_timeout_error.<locals>.<genexpr>�  s   øè ø€ ÒZ c�3˜'”>ÑZùrø  )z	timed outÚreadtimeoutÚwritetimeout)rb   rÒ   r  s    @r   Ú_is_timeout_errorz%BasePlatformAdapter._is_timeout_errorˆ  s)   ø€ ð ’;˜B×%Ñ%Ó'ˆÜÓZÐ-YÔZÓZÐZr   rx  c                 ó  ‡ — t        |t        «      s|dfS |j                  }|€t        ˆ fd„dt        «      }|r,|dkD  r't        ‰ «      j                  t        j                  u rd}|j                  t        |xs d«      fS )a)  Unwrap a str/None/:class:`EphemeralReply` response into ``(text, ttl)``. ``ttl > 0``
        means schedule ``_schedule_ephemeral_delete`` after a successful send; forced to 0 when the
        adapter doesn't override ``delete_message`` so non-supporting platforms degrade to normal
        sends.r   c                  ó6   •— t        ‰ j                  «       «      S r¤  )rN  ræ  r—  s   €r   r  z7BasePlatformAdapter._unwrap_ephemeral.<locals>.<lambda>˜  s   ø€ ¤c¨$×*PÑ*PÓ*RÓ&S€ r   )
rŽ   r  r  ri   rO   r­  râ  rc  rÜ   rN  )r˜  rx  Úttls   `  r   Ú_unwrap_ephemeralz%BasePlatformAdapter._unwrap_ephemeral�  sw   ø€ ô
 ˜(¤NÔ3Ø˜Q�;ÐØ×"Ñ"ˆØˆ;ÜÓSÐUVÔXaÓbˆCÙ�3˜’7œt D›z×8Ñ8Ô<O×<^Ñ<^Ñ^ØˆCØ�}‰}œc #¢(¨›mÐ+Ð+r   ©Úlog_cmdr*  c             ƒ   ó6  K  — t        |«      }| j                  |«      ƒ d{  –—† }| j                  |«      \  }}|sy|�@t        j	                  d| j
                  |t        |«      |j                  j                  «       | j                  |j                  j                  |t        |«      t        |«      ¬«      ƒ d{  –—† }|dkD  rL|j                  r?|j                  r2| j                  |j                  j                  |j                  |«       yyyy7 Œò7 ŒX­w)u˜   Call the handler and send its reply inline, with retry, threading and
        ephemeral deletion â€” no session lifecycle (active-session bypass paths).Nz4[%s] Sending command '/%s' response (%d chars) to %sr5  r   )rƒ   rl  r(  r   r  r  r²   r{   rR  Ú_send_with_retryr€   rˆ   rÜ  rv   rì  )r˜  r‚   r*  Úthread_metarx  rÜ   Úeph_ttlrY   s           r   Ú_dispatch_inline_replyz*BasePlatformAdapter._dispatch_inline_reply�  sü   è ø€ ô 1°Ó7ˆØ×.Ñ.¨uÓ5×5ˆØ×.Ñ.¨xÓ8‰ˆˆgÙØØÐÜ�K‰KÐNÐPT×PYÑPYÐ[bÜ˜D›	 5§<¡<×#7Ñ#7ô9à×,Ñ,Ø—L‘L×(Ñ(°$ÔAXÐY^ÓA_Ü*¨;Ó7ð -ó 9÷ 9ˆð �QŠ;˜6Ÿ>š>¨f×.?Ò.?Ø×+Ñ+¨E¯L©L×,@Ñ,@À&×BSÑBSÐU\Õ]ð /@˜>ˆ;ð 6øð9ús#   ‚ D¢D£BDÂ>DÂ?ADÄDc                 ó  — t        | j                  dd«      }t        |«      r|€t        j                  «       S 	  ||«      S # t
        $ r9 t        j                  d| j                  d¬«       t        j                  «       cY S w xY w)zˆRouted home + terminal policy for post-handler text, media and error delivery;
        a no-op without a runner or outside multiplexing.Ú _media_delivery_scope_for_sourceNz+[%s] Failed to resolve media delivery scopeTr   )	ra   r[  r`  rP   ÚnullcontextrO   r   r�  r  )r˜  r{   rs  s      r   Ú_media_delivery_scopez)BasePlatformAdapter._media_delivery_scope®  sz   € ô ˜$×-Ñ-Ð/QÐSWÓXˆÜ˜Ô  F NÜ×)Ñ)Ó+Ð+ð	,Ù˜6“?Ð"øÜò 	,Ü�L‰LÐFÈÏ	É	Ð\`ˆLÔaÜ×)Ñ)Ó+Ò+ð	,ús   ºA Á?BÂBc                 ó  — t        | j                  dd«      }t        |«      s| S 	  ||«      }t        |t        «      r|j                  | j                  k(  r|S | S # t        $ r% t        j                  d| j                  «       | cY S w xY w)a  The runner's CURRENT adapter for a new final-response send: a reconnect can swap the
        registry adapter mid-task; an unsent final response belongs on the replacement transport,
        while message IDs, edits and deletes stay owned by the old one (nothing is migrated).Ú_delivery_adapter_forNz6[%s] Failed to resolve live adapter for final delivery)
ra   r[  r`  rO   r   r�  r  rŽ   rc  rc   )r˜  r{   rs  Úlive_adapters       r   Ú_final_delivery_adapterz+BasePlatformAdapter._final_delivery_adapterº  sŒ   € ô ˜$×-Ñ-Ð/FÈÓMˆÜ˜Ô ØˆKð	Ù" 6›?ˆLô �lÔ$7Ô8¸\×=RÑ=RÐVZ×VcÑVcÒ=cØÐØˆøô ò 	Ü�L‰LÐQÐSW×S\ÑS\Ô]ØŠKð	ús   ¦A Á+B	ÂB	Úmax_retriesÚ
base_delayrÛ  c           	   ƒ   ó¬  ‡ ‡‡‡‡‡K  — dt         ddfˆˆˆˆ fd„Šdˆˆˆˆˆˆ fd„} ‰‰«      ƒ d{  –—† }|j                  s‰ j                  |«      r|S |j                  xs d}	‰ j	                  |	«      }
|j
                  xs% |
xs! |j                  duxs ‰ j                  |	«      }|s‰ j                  |	«      r|S |�r$|j                  }t        d|dz   «      D �]Ž  }|}|€|d	|dz
  z  z  }n4|t        kD  r+t        j                  d
‰ j                  |t        |	«       |c S |t        j                  dd«      z   }d}t        j                  d‰ j                  ||||	«       t!        j"                  |«      ƒ d{  –—†   ||«      ƒ d{  –—† }|€%t        j                  d‰ j                  |	«       |c S |}|j                  r%t        j%                  d‰ j                  |«       |c S |j                  xs d}	‰ j                  |«      r|c S |j                  �|j                  }‰ j	                  |	«      }
|j
                  r�Œj|
r�Œn|j                  ��Œ|‰ j                  |	«      r�Œ� nx ‰ j	                  |	«      s|j                  �#t        j                  d‰ j                  |	«       |S t        j                  d‰ j                  ||	«       d}	  ‰|«      ƒ d{  –—†  |S ‰ j+                  |«      r#t        j                  d‰ j                  |	«       |S t        j                  d‰ j                  |	«       ‰ j-                  ‰‰‰‰¬«      ƒ d{  –—† }|j                  s+t        j                  d‰ j                  |j                  «       |S 7 �Œ_7 �Œ7 �Œ7 Œº# t&        $ r,}t        j)                  d‰ j                  |«       Y d}~|S d}~ww xY w7 Œ€­w)zžSend with exponential-backoff retry on transient network errors; permanent
        failures fall back to a plain-text send, exhausted retries notify the user.rÜ   r	   rÛ  c              “   óJ   •K  — ‰j                  ‰| ‰‰¬«      ƒ d {  –—† S 7 Œ­w)Nr5  r6  )rÜ   rR  r|   rÒ  r˜  s    €€€€r   Ú_sendz3BasePlatformAdapter._send_with_retry.<locals>._sendÏ  s%   øè ø€ ØŸ™¨7¸DÈ8Ð^f˜Óg×gÐgÐgús   ƒ#œ!�#c              “   ó�   •K  — ‰j                  | «      s ‰‰«      ƒ d{  –—† S ‰j                  ‰| ‰‰¬«      ƒ d{  –—† S 7 Œ!7 Œ­w)u'  Retry: the whole payload normally; only the undelivered remainder after a partial split
            delivery (``raw_response["partial_overflow"]``). ``None`` when the adapter cannot resume â€” the
            caller then keeps the partial failure rather than re-sending the already-visible head.Nr�  )Ú_is_partial_deliveryÚ_resume_partial_send)Úpreviousr<  rR  rß  r|   rÒ  r˜  s    €€€€€€r   Ú_send_againz9BasePlatformAdapter._send_with_retry.<locals>._send_againÒ  sK   øè ø€ ð ×,Ñ,¨XÔ6Ù" 7›^×+Ð+Ø×2Ñ2°7¸HÈxÐbjÐ2Ók×kÐkð ,øØkús   ƒA A¡A½A¾AÁANr`   r·   r]   z{[%s] Server asked to retry after %.0fs (> %.0fs inline cap); returning typed failure for redelivery instead of sleeping: %sr   z7[%s] Send failed (attempt %d/%d, retrying in %.1fs): %sz”[%s] Split send partly delivered and the remainder cannot be resumed safely; not re-sending the whole payload (would duplicate the visible head): %sz[%s] Send succeeded on retry %dz�[%s] Rate-limited send exhausted retries; returning typed failure for redelivery (no notice sent inside active flood penalty): %sz4[%s] Failed to deliver response after %d retries: %su‹   âš ï¸� Message delivery failed after multiple attempts. Please try again â€” your request was processed but the response could not be sent.z/[%s] Could not send delivery-failure notice: %suL   [%s] Send failed after partial delivery: %s â€” not re-sending as plain textu3   [%s] Send failed: %s â€” trying plain-text fallbackr�  z"[%s] Fallback send also failed: %s)r@  rÛ  r	   úOptional[SendResult])rF   rÜ  Ú_send_retry_is_finalr   r  rÞ  rß  r  r$  rÕ  Ú _SEND_RETRY_INLINE_WAIT_CAP_SECSr   r  ÚrandomÚuniformrL   rÄ  rÛ  r  rO   r�  r>  Ú_send_plain_fallback)r˜  rR  rß  rÒ  r|   r8  r9  rA  rY   Ú	error_strÚis_rate_limitedÚ
is_networkÚserver_retry_afterrÞ  ÚbackoffÚdelayÚresumedr„  r¶  Úfallback_resultr<  s   `````               @r   r,  z$BasePlatformAdapter._send_with_retryÊ  sÆ  ýè ø€ ð
	hœcð 	h l÷ 	hð 	h÷	lò 	lñ ˜W“~×%ˆØ�>Š>˜T×6Ñ6°vÔ>ØˆMØ—L‘LÒ& Bˆ	ð ×5Ñ5°iÓ@ˆà×Ñò 3Øò3à×!Ñ!¨Ð-ò3ð ×'Ñ'¨	Ó2ð	 	ñ ˜d×4Ñ4°YÔ?ØˆMÚà!'×!3Ñ!3ÐÜ   K°!¡OÓ4ó G�Ø,�Ø�?Ø(¨A°'¸A±+Ñ,>Ñ?‘GØÔ?Ò?ô —L‘LðOàŸ	™	 7Ô,LÈiôð
 "’MØ¤&§.¡.°°AÓ"6Ñ6�Ø%)Ð"Ü—‘ÐXÐZ^×ZcÑZcØ&¨°U¸IôGä—m‘m EÓ*×*Ð*Ù +¨FÓ 3×3�Ø�?Ü—N‘NðbØcg×clÑclÐnwôyð "’MØ �Ø—>’>Ü—K‘KÐ AÀ4Ç9Á9ÈgÔVØ!’MØ"ŸL™LÒ.¨B�	Ø×,Ñ,¨VÔ4Ø!’MØ×%Ñ%Ð1Ø)/×);Ñ);Ð&ð #'×"=Ñ"=¸iÓ"H�à×$Ô$Û&Ø×)Ñ)Ò5Ø×/Ñ/°	Ö:áð]Gðn ×.Ñ.¨yÔ9¸V×=OÑ=OÐ=[Ü—L‘LðZàŸ	™	 9ôð
 "�MÜ—‘ÐSÐUY×U^ÑU^Ð`kÐmvÔwðmð ðkÙ ›-×'Ð'ð �ð ×$Ñ$ VÔ,ä�N‰NÐiÐko×ktÑktÐvô  AØˆMÜ�‰ÐLÈdÏiÉiÐYbÔcØ $× 9Ñ 9¸'À7ÐU]ÐhpÐ 9Ó q×qˆØ×&Ò&Ü�L‰LÐ=¸t¿y¹yÈ/×J_ÑJ_Ô`ØÐðU &ùðR +ùØ3ùðb (ùÜ ò kÜ—L‘LÐ!RÐTX×T]ÑT]Ð_i×jÐjØ�ûðkúð rús—   ˆ(O°N±E	OÅ:NÅ;OÆ
NÆB8OÉOÉ	OÉOÉ*A'OËN ËNËN Ë"A/OÍOÍ>OÎOÎOÎN Î	OÎ#!O
ÏOÏ
OÏOrY   c                  ó   — y)z¢True when a failed send must be returned as-is: neither a retry nor the plain-text
        fallback can fix it (a structured auth/target refusal). Default: never.Frf   )r˜  rY   s     r   rC  z(BasePlatformAdapter._send_retry_is_finalF  r§  r   c                 ót   — t        | dd«      }t        |t        «      xr t        |j	                  d«      «      S )zåTrue when a split payload was PARTLY delivered (``raw_response["partial_overflow"]``, the
        contract Telegram's send/edit-overflow paths set and the stream consumer reads): the visible
        head must never be sent again.rÝ  NÚpartial_overflow)ra   rŽ   r†   r  r�   )rY   rn  s     r   r>  z(BasePlatformAdapter._is_partial_deliveryK  s3   € ô
 �f˜n¨dÓ3ˆÜ˜#œtÓ$ÒJ¬¨c¯g©gÐ6HÓ.IÓ)JÐJr   rB  c             ƒ   ó   K  — y­w)a(  Deliver only the remainder of a partially delivered split payload. ``None`` (the default) means
        this adapter cannot resume; ``_send_with_retry`` then returns the partial failure instead of
        re-sending the whole payload. Override only where non-delivery of the remainder is CERTAIN.Nrf   )r˜  rR  rY   rÒ  r|   s        r   r?  z(BasePlatformAdapter._resume_partial_sendS  rš  rÏ  c          	   ƒ   ó€   K  — | j                  || j                  d|dd › �|dd ||¬«      ||¬«      ƒ d{  –—† S 7 Œ­w)z¬Last-resort send after a non-transient failure; platforms whose markup is not the
        likely culprit override it (Photon drops rich links instead of adding the banner).z+(Response formatting failed, plain text:)

Ni¬  r€  r5  )rÕ  r‹  rÔ  s        r   rG  z(BasePlatformAdapter._send_plain_fallbackZ  sf   è ø€ ð —Y‘YØ T×%6Ñ%6Ø?ÀÈÈÀÐ?OÐPÐRYÐZ_Ð[_ÐR`Ø¨(ð &7ó &4ð ¨ð	 ó 2÷ 2ð 	2ð 2ús   ‚5>·<¸>Úexisting_textÚnew_textc                 ó¼   — | s|S |j                  «       | j                  d«      D �cg c]  }|j                  «       ‘Œ c}v r| S | › d|› �j                  «       S c c}w )uÈ   Merge a new caption into existing text unless an identical (stripped) caption already
        exists â€” exact match per caption, not substring ("Meeting" isn't swallowed by "Meeting
        agenda").rí  )rz   rJ  )rU  rV  rÌ  s      r   r.  z"BasePlatformAdapter._merge_captiond  s]   € ñ
 ØˆOØ�>‰>Ó°=×3FÑ3FÀvÓ3NÖO¨a §¡¥	ÒOÑOØ Ð Ø�  X JÐ/×5Ñ5Ó7Ð7ùò  Ps   §Ac                 ó$   — t        | dt        «      S )Nr…  )rZ  r†   r—  s    r   Ú_text_debounce_storez(BasePlatformAdapter._text_debounce_storeo  s   € Ü˜$Ð 0´$Ó7Ð7r   c           	      óˆ  — t        | dd«      dk(  xri |j                  t        j                  k(  xrJ t        |dd«       xr: |j	                  «        xr' t        |j                  xs dj                  «       «      }|rDt        j                  d| j                  t        |dd	«      t        |j                  xs d«      «       |S )
z=Return True for normal text eligible for queue-mode debounce.r  rk  ÚqueueÚinternalFr`   zC[%s] Queue-text debounce candidate accepted: session=%s text_len=%drW  rÞ  )ra   rÍ  r;  rÎ  Ú
is_commandr  rÜ   rz   r   r�  r  r²   )r˜  r‚   rY   s      r   Ú!_is_queue_text_debounce_candidatez5BasePlatformAdapter._is_queue_text_debounce_candidater  s½   € ô �DÐ+¨[Ó9¸WÑDò LØ×"Ñ"¤k×&6Ñ&6Ñ6òLÜ?FÀuÈjÐZ_Ó?`Ð;`òLà×$Ñ$Ó&Ð&òLä+/°·±Ò1A¸r×0HÑ0HÓ0JÓ+Kð 	ñ Ü�L‰LÐ^ØŸ™¤G¨E°=À#Ó$FÌÈEÏJÉJÒL\ÐZ\ÓH]ô_àˆr   r¾  c                 ól   — dt         dt        t        df   dz  fd„} ||«      }|duxr | ||«      k(  S )zDReturn True when two text debounce events came from the same sender.r»  r	   .Nc                 ó  — t        | dd «      }|€y t        t        |dd «      «      }t        |dd «      xs t        |dd «      }|r|t        |«      fS t        |dd «      dv r%t        |dd «      r|dt        |j                  «      fS y )	Nr{   rc   Úuser_id_altrK  rr   >   rs   ÚprivaterR  rs   )ra   rd   rF   rR  )r»  r{   rc   r^  s       r   Ú	_identityzFBasePlatformAdapter._can_merge_text_debounce_events.<locals>._identity€  s“   € Ü˜Y¨°$Ó7ˆFØˆ~ØÜ%¤g¨f°jÀ$Ó&GÓHˆHÜ˜V ]°DÓ9Ò]¼WÀVÈYÐX\Ó=]ˆFÙØ ¤# f£+Ð.Ð.Ü�v˜{¨DÓ1Ð5FÑFÌ7ÐSYÐ[dÐfjÔKkØ  $¬¨F¯N©NÓ(;Ð<Ð<Ør   )r:  râ  rF   )r˜  r¾  r‚   rc  Úexisting_senders        r   Ú_can_merge_text_debounce_eventsz3BasePlatformAdapter._can_merge_text_debounce_events}  sH   € ð
	¤ð 
	´%¼¸S¸±/ÀDÑ2Hó 
	ñ $ HÓ-ˆØ dÐ*ÒR¨Á)ÈEÓBRÑ/RÐRr   c                 óü   — | j                  «       j                  |«      }|€yt        |j                  | j                  z   |j
                  | j                  z   «      }t        d|t        j                  «       z
  «      S )z<Return bounded busy-text debounce delay for ``session_key``.rW  )
rY  r�   r  r¶  r�  rµ  rƒ  rZ  rê  Ú	monotonic)r˜  rW  Ústaterå  s       r   Ú_text_debounce_delayz(BasePlatformAdapter._text_debounce_delayŽ  sj   € à×)Ñ)Ó+×/Ñ/°Ó<ˆØˆ=ØÜ�u—}‘} t×'GÑ'GÑGØ—~‘~¨×(HÑ(HÑHóJˆä�3˜¤4§>¡>Ó#3Ñ3Ó4Ð4r   c              ƒ   ó  K  — | j                  «       }|j                  |«      }|�­| j                  |j                  |«      s‘| j	                  |«      ƒ d{  –—†  |j                  |«      }|�e| j                  |j                  |«      sI| j
                  j                  |«      }|�+| j                  ||«      rt        | j
                  ||d¬«       yt        j                  «       }|€t        |d||¬«      }|||<   n¸|j                  r9t        |j                  j                  |j                  «      |j                  _
        t        |dd«      }|xs t        |dd«      }|�t        |«      |j                  _        |�0t        |j                  d«      rt        |«      |j                  _        ||_        |j%                  «        | j'                  |«      }	t)        j*                  | j-                  ||	«      «      |_        y7 �Œ¬­w)z@Buffer normal queue-mode busy text and schedule a bounded flush.NTr   )r‚   r   rµ  r¶  rv   rk   )rY  r�   re  r‚   Ú_flush_text_debounce_nowr{  r9  rê  rg  r´  rÜ   rÁ  ra   rF   rv   r/  rk   r¶  r¼  ri  rÄ  rˆ  Ú_flush_text_debouncer   )
r˜  rW  r‚   ra  rh  Úexisting_pendingÚnowÚlatest_message_idÚlatest_anchorrM  s
             r   Ú_queue_text_debouncez(BasePlatformAdapter._queue_text_debounce—  s¾  è ø€ à×)Ñ)Ó+ˆØ—	‘	˜+Ó&ˆØÐ T×%IÑ%IÈ%Ï+É+ÐW\Ô%]ð ×/Ñ/°Ó<×<Ð<Ø—I‘I˜kÓ*ˆEØÐ ¨×)MÑ)MÈeÏkÉkÐ[`Ô)aØ#'×#9Ñ#9×#=Ñ#=¸kÓ#JÐ Ø#Ð/°D×4XÑ4XÐYiÐkpÔ4qÜ/°×0FÑ0FÈÐUZÐgkÕlØÜ�n‰nÓˆØˆ=Ü%¨E¸ÀsÐTWÔXˆEØ!&ˆE�+Òà�zŠzÜ#/°·±×0@Ñ0@À%Ç*Á*Ó#M�—‘Ô Ü '¨¨|¸TÓ BÐØ-Ò\´¸Ð@UÐW[Ó1\ˆMØ Ð,Ü),Ð->Ó)?�—‘Ô&ØÐ(¬W°U·[±[ÐBWÔ-XÜ25°mÓ2D�—‘Ô/ØˆEŒMØ×ÑÔØ×)Ñ)¨+Ó6ˆÜ×(Ñ(¨×)BÑ)BÀ;ÐPUÓ)VÓWˆ�
ð/ =ûs   ‚AHÁHÁF,HrM  c              ƒ   óˆ  K  — 	 t        j                  |«      ƒ d{  –—†  | j                  |«      ƒ d{  –—†  	 t        j                  «       }| j                  «       j                  |«      }|�|j                  |u rd|_        yyy7 Œk7 ŒT# t         j                  $ rO Y t        j                  «       }| j                  «       j                  |«      }|�|j                  |u rd|_        yyyw xY w# t        j                  «       }| j                  «       j                  |«      }|�|j                  |u rd|_        w w w xY w­w)z2Timer task that flushes the debounced text buffer.N)rÄ  rÛ  rk  r
  r˜  rY  r�   r   )r˜  rW  rM  r   rh  s        r   rl  z(BasePlatformAdapter._flush_text_debounce·  s+  è ø€ ð		"Ü—-‘- Ó&×&Ð&Ø×/Ñ/°Ó<×<Ñ<ô ×*Ñ*Ó,ˆGØ×-Ñ-Ó/×3Ñ3°KÓ@ˆEØÐ  U§Z¡Z°7Ñ%:Ø!�•
ð &;Ð ð 'øØ<ùÜ×%Ñ%ò 	Øä×*Ñ*Ó,ˆGØ×-Ñ-Ó/×3Ñ3°KÓ@ˆEØÐ  U§Z¡Z°7Ñ%:Ø!�•
ð &;Ð ð	ûô ×*Ñ*Ó,ˆGØ×-Ñ-Ó/×3Ñ3°KÓ@ˆEØÐ  U§Z¡Z°7Ñ%:Ø!�•
ð &;Ð üs\   ‚E„B œB�B µB
¶B »AEÂB Â
B ÂC.ÂC1 Â AEÃ-C.Ã.C1 Ã1AD?Ä?Ec              ƒ   óˆ  K  — | j                  «       }|j                  |«      }|€y|j                  t        j                  «       ¬«       d|_        | j                  j                  |«      }|�| j                  ||j                  «      sy|j                  |d«       t        | j                  ||j                  d¬«       y­w)z@Force-flush one debounced busy-text burst into the pending slot.NFr·  Tr   )rY  r�   r¼  rÄ  r˜  r   r{  re  r‚   r`  r9  )r˜  rW  ra  rh  rŒ  s        r   rk  z,BasePlatformAdapter._flush_text_debounce_nowÄ  sª   è ø€ à×)Ñ)Ó+ˆØ—	‘	˜+Ó&ˆØˆ=ØØ×Ñ¤'×"6Ñ"6Ó"8ÐÔ9ØˆŒ
Ø×(Ñ(×,Ñ,¨[Ó9ˆØÐ t×'KÑ'KÈGÐUZ×U`ÑU`Ô'aØØ�	‰	�+˜tÔ$Ü# D×$:Ñ$:¸KÈÏÉÐaeÕfØùs   ‚C Cc                 ój   — | j                  «       j                  |d«      }|�|j                  «        yy)zACancel and drop pending text debounce state for control commands.N)rY  r`  r¼  )r˜  rW  rh  s      r   Ú_discard_text_debouncez*BasePlatformAdapter._discard_text_debounceÓ  s4   € à×)Ñ)Ó+×/Ñ/°¸TÓBˆØÐØ×ÑÕ ð r   ©Úguardrw  c                óf   — | j                   j                  |«      }|�|�||ury| j                   |= y)zµRelease the session guard; with ``guard`` given, only if the entry is still that exact
        Event (an old task's unwind must not clear the guard a reset-like command swapped in).N)rz  r�   )r˜  rW  rw  Úcurrent_guards       r   Ú_release_session_guardz*BasePlatformAdapter._release_session_guardÜ  s>   € ð ×-Ñ-×1Ñ1°+Ó>ˆØÐ  UÐ%6¸=ÐPUÑ;UØØ×!Ñ! +Ñ.r   c                 óv   — t        | j                  j                  |«      dd«      }t        |xr  |«       «      S )zµTrue if the recorded owner task for ``session_key`` has exited. No owner task at all is
        NOT stale (guards installed outside handle_message, as tests do, must not be healed).rº  N)ra   r~  r�   r  )r˜  rW  rº  s      r   Ú_session_task_is_stalez*BasePlatformAdapter._session_task_is_staleä  s4   € ô �t×*Ñ*×.Ñ.¨{Ó;¸VÀTÓJˆÜ�D’O™T›VÓ$Ð$r   c                 óP  — || j                   vs| j                  |«      syt        j                  d| j                  |«       | j                   j                  |d«       | j                  j                  |d«       | j                  j                  |d«       | j                  |«       y)zµClear a stale session lock; True if healed. On-entry safety net: without it a split-brain
        (guard held, nothing processing) traps the chat in "Interrupting..." until restart.FzB[%s] Healing stale session lock for %s (owner task is done/absent)NT)	rz  r|  r   rL   r  r`  r{  r~  ru  ©r˜  rW  s     r   Ú_heal_stale_session_lockz,BasePlatformAdapter._heal_stale_session_lockê  sŽ   € ð ˜d×3Ñ3Ñ3¸4×;VÑ;VÐWbÔ;cØÜ�‰Ð[Ø—y‘y +ô	/à×Ñ×!Ñ! +¨tÔ4Ø×Ñ×"Ñ" ;°Ô5Ø×Ñ×Ñ ¨TÔ2Ø×#Ñ# KÔ0Ør   )Úinterrupt_eventr€  c                ó   — |xs t        j                  «       }|| j                  |<   t        j                  | j	                  ||«      «      }| j                  ||«      s0| j                  j                  |d«       | j                  ||¬«       yy)z¾Spawn a background processing task under the session guard; True on success. If
        ``create_task`` is stubbed with a non-Task sentinel (tests), the guard is rolled back
        (False).Nrv  FT)	rÄ  ÚEventrz  rˆ  Ú_process_message_backgroundÚ_track_session_taskr~  r`  rz  )r˜  r‚   rW  r€  rw  r   s         r   Ú_start_session_processingz-BasePlatformAdapter._start_session_processing÷  s€   € ð
  Ò2¤7§=¡=£?ˆØ-2ˆ×Ñ˜kÑ*Ü×"Ñ" 4×#CÑ#CÀEÈ;Ó#WÓXˆØ×'Ñ'¨°TÔ:Ø×Ñ×#Ñ# K°Ô6Ø×'Ñ'¨¸5Ð'ÔAØØr   r   c                 ó$  — || j                   |<   	 | j                  j                  |«       t	        |d«      rJ|j                  | j                  j                  «       |j                  | j                  j                  «       y# t        $ r Y yw xY w)uÆ   Record ``task`` as the session owner and track it for shutdown; False when
        ``create_task`` was stubbed with an unhashable sentinel (tests) â€” the owner entry is left
        for the caller.Fr  T)r~  r†  rÿ  r‚  r/  r  r  rˆ  )r˜  rW  r   s      r   r„  z'BasePlatformAdapter._track_session_task  s…   € ð ,0ˆ×Ñ˜KÑ(ð	Ø×"Ñ"×&Ñ& tÔ,ô �4Ð,Ô-Ø×"Ñ" 4×#9Ñ#9×#AÑ#AÔBØ×"Ñ" 4×#AÑ#A×#IÑ#IÔJØøô ò 	Ùð	ús   ‘B Â	BÂB©Úrelease_guardÚdiscard_pendingrˆ  r‰  c             ƒ   óæ  K  — | j                   j                  |d«      }|��|j                  «       st        j	                  d| j
                  |«       | j                  j                  |«       |j                  «        	 t        j                  t        j                  |«      d¬«      ƒ d{  –—†  |r-| j                   j                  |d«       | j#                  |«       |r| j%                  |«       yy7 ŒH# t        j                  $ r Y Œ[t        j                  $ r$ t        j                  d| j
                  |«       Y Œ�t        $ r& t        j	                  d| j
                  |d¬«       Y Œ½w xY w­w)	zÉCancel in-flight processing for one session. ``release_guard=False`` keeps the guard so
        reset-like commands finish atomically; the await is bounded (5s) so a wedged finally can't
        stall.Nz0[%s] Cancelling active processing for session %sr\   rÀ  zt[%s] Cancelled task for %s did not exit within 5s; unblocking dispatch and letting the task unwind in the backgroundz3[%s] Session cancellation raised while unwinding %sTr   )r~  r`  rº  r   r�  r  rˆ  rÿ  r»  rÄ  rÇ  r	  r
  rÉ  rL   rO   r{  ru  rz  )r˜  rW  rˆ  r‰  r   s        r   Úcancel_session_processingz-BasePlatformAdapter.cancel_session_processing  s6  è ø€ ð
 ×"Ñ"×&Ñ& {°DÓ9ˆØÐ D§I¡I¤KÜ�L‰LÐKÈTÏYÉYÐXcÔdØ×*Ñ*×.Ñ.¨tÔ4Ø�K‰KŒMð
9Ü×&Ñ&¤w§~¡~°dÓ';ÀSÔI×IÐIñ Ø×"Ñ"×&Ñ& {°DÔ9Ø×'Ñ'¨Ô4ÙØ×'Ñ'¨Õ4ð ð JùÜ×)Ñ)ò ÙÜ×'Ñ'ò 7Ü—‘ð  cà#Ÿy™y¨+ö7ô ò 9Ü—‘ÐRÐTX×T]ÑT]Ø(°4ð ö 9ð9üsU   ‚A;E1Á>-C6 Â+C4Â,C6 Â0AE1Ã4C6 Ã6E.Ä	E1Ä3E.Ä>E1Å +E.Å+E1Å-E.Å.E1Úcommand_guardc              ƒ   óÊ   K  — | j                  |«      ƒ d{  –—†  | j                  j                  |d«      }| j                  ||¬«       |�| j	                  ||«       yy7 ŒI­w)z�Tail of /stop, /new, /reset: release the command-scoped guard, then
        spawn a fresh processing task for any follow-up queued meanwhile.Nrv  )rk  r{  r`  rz  r…  )r˜  rW  rŒ  Úpending_events       r   Ú$_drain_pending_after_session_commandz8BasePlatformAdapter._drain_pending_after_session_command.  sg   è ø€ ð ×+Ñ+¨KÓ8×8Ð8Ø×.Ñ.×2Ñ2°;ÀÓEˆØ×#Ñ# K°}Ð#ÔEØÐ$Ø×*Ñ*¨=¸+ÕFð %ð 	9ús   ‚A#—A!˜A
A#Úcmdc              ƒ   ó8  K  — t         j                  d| j                  ||«       | j                  j	                  |«      }t        j                  «       }|| j                  |<   	 | j                  ||¬«      ƒ d{  –—†  | j                  |dd¬«      ƒ d{  –—†  | j                  ||«      ƒ d{  –—†  y7 Œ;7 Œ!# t        $ rR | j                  j	                  |«      |u r3|| j                  v r|�|| j                  |<   ‚ | j                  ||¬«       ‚ w xY w7 Œg­w)zÌDispatch a reset-like bypass command (/stop, /new, /reset): keep a guard installed while
        the runner handles it (follow-ups stay queued), cancel the old task AFTER the response,
        drain once.ú8[%s] Command '/%s' bypassing active-session guard for %sr)  NFr‡  rv  )r   r�  r  rz  r�   rÄ  r‚  r/  r‹  rO   r~  rz  r�  )r˜  r‚   rW  r�  ry  rŒ  s         r   Ú _dispatch_active_session_commandz4BasePlatformAdapter._dispatch_active_session_command8  s!  è ø€ ô 	�‰ÐOÐQU×QZÑQZÐ\_ÐalÔmØ×-Ñ-×1Ñ1°+Ó>ˆÜŸ™›ˆØ-:ˆ×Ñ˜kÑ*ð	ð ×-Ñ-¨e¸SÐ-ÓA×AÐAØ×0Ñ0°ÈEÐchÐ0Ói×iÐið ×7Ñ7¸À]ÓS×SÑSð BøØiùÜò 	à×$Ñ$×(Ñ(¨Ó5¸ÑFØ $×"5Ñ"5Ñ5¸-Ð:SØ9F�D×)Ñ)¨+Ñ6ð ð ×/Ñ/°À=Ð/ÔQØð	úð 	TúsO   ‚A!DÁ$B: Á:B6Á;B: ÂB8ÂB: ÂDÂ0DÂ1DÂ6B: Â8B: Â:ADÄDc              ƒ   óv  K  — d|_         | j                  s5t        | dd«      s'd| _        t        j                  d| j                  «       y|j                  rt        |«       | j                  |«      ryt        |j                  xs i j                  d«      xs d«      j                  «       }|sut        | dd«      �h|j                  j                  t         j"                  k(  rA|j                  j$                  d	k(  r(t'        j(                  | j*                  |«      ƒ d{  –—†  | j-                  |«      }|r||k7  rt        j/                  d
||«       y|| j0                  v r| j3                  |«       || j0                  v r| j5                  ||«      ƒ d{  –—†  y| j7                  ||«      |_         y7 Œ”7 Œ­w)z“Process an incoming message; returns quickly by spawning a background
        task so new messages (and interrupts) can arrive while an agent runs.Frm  Tz¦[%s] Dropping inbound message: no gateway message handler is installed on this adapter. The adapter is connected and can send, but every inbound message is discarded.NÚgateway_session_keyr`   rp  rs   z@Dropping internally routed event: expected session=%s derived=%s)Ú_gateway_acceptedrl  ra   rm  r   r   r  Úallow_gateway_controlrÏ  rd  rF   r|   r�   rz   r{   rc   r3  ÚTELEGRAMrr   rÄ  rÅ  rB  r€  rL   rz  r  Ú_handle_message_while_activer…  )r˜  r‚   Úexpected_session_keyrW  s       r   r“  z"BasePlatformAdapter.handle_messageO  s’  è ø€ ð #(ˆÔØ×$Ò$ô ˜4Ð!=¸uÔEØ26�Ô/Ü—‘ðHð —I‘Iô	ð à×&Ò&Ü,¨UÔ3à× Ñ  Ô'ØÜ" E§N¡NÒ$8°b×#=Ñ#=Ð>SÓ#TÒ#ZÐXZÓ[×aÑaÓcÐñ %¬°Ð7KÈTÓ)RÐ)^Ø—L‘L×)Ñ)¬X×->Ñ->Ò>À5Ç<Á<×CYÑCYÐ]aÒCaÜ×#Ñ# D×$>Ñ$>ÀÓF×FÐFØ×-Ñ-¨eÓ4ˆÙ KÐ3GÒ$GÜ�N‰NÐ]Ø/°ô>àà˜$×/Ñ/Ñ/Ø×)Ñ)¨+Ô6Ø˜$×/Ñ/Ñ/Ø×3Ñ3°E¸;ÓG×GÐGØà"&×"@Ñ"@ÀÈÓ"TˆÕð Gøð Hús%   ‚DF9Ä F5Ä!A6F9ÆF7ÆF9Æ7F9c              ƒ   ó‚  K  — | j                  |j                  «       |j                  «       }ddlm}m}  ||«      ru	 |r5 ||«      r-| j                  |«       | j                  |||«      ƒ d{  –—†  yt        j                  d| j                  ||«       | j                  |«      ƒ d{  –—†  	 y|sf|j                  rZ	 ddlm} |j#                  |d¬	«      du}|r<t        j                  d| j                  |«       	 | j                  |«      ƒ d{  –—†  y| j$                  �	 | j%                  ||«      ƒ d{  –—† ry	 |j&                  r|| j(                  v ry|j*                  t,        j.                  k(  r@t        j                  d| j                  |«       t1        | j(                  ||«       d|_        y| j5                  |«      rGt        j                  d| j                  || j6                  «       | j9                  ||«      ƒ d{  –—†  yt        j                  d| j                  |«       t1        | j(                  |||j*                  t,        j:                  k(  ¬«       d|_        y7 �Œ7 �ŒÈ# t        $ r.}t        j                  d| j                  ||d¬«       Y d}~yd}~ww xY w# t        $ r d
}Y �Œâw xY w7 �Œ®# t        $ r-}t        j                  d| j                  |d¬«       Y d}~yd}~ww xY w7 �ŒÂ# t        $ r.}t        j                  d| j                  |d¬«       Y d}~�Œðd}~ww xY w7 �Œ.­w)z‘Route a message that arrived while ``session_key`` is busy: bypass
        commands / clarify replies dispatch inline, everything else is queued.r   )Úis_interrupt_then_dispatchÚshould_bypass_active_sessionNr’  z&[%s] Command '/%s' dispatch failed: %sTr   r&  )Úinclude_choice_promptsFz5[%s] Routing message to clarify text-intercept for %sz/[%s] Clarify text-intercept dispatch failed: %sz$[%s] Busy-session handler failed: %sz=[%s] Queuing photo follow-up for session %s without interruptun   [%s] New text message while session %s is active â€” debouncing follow-up (busy_text_mode=queue, window=%.2fs)uq   [%s] New message while session %s is active â€” queuing follow-up (no interrupt, will cascade after current turn)r   )ra  r{   Úget_commandÚhermes_cli.commandsrœ  r�  ru  r“  r   r�  r  r/  rO   r   r—  r+  r'  Úget_pending_for_sessionr‰  r\  r{  rÍ  r;  r,  r9  r–  r^  r�  rq  rÎ  )	r˜  r‚   rW  r�  rœ  r�  rF  Ú_clarify_modÚ_has_text_clarifys	            r   r™  z0BasePlatformAdapter._handle_message_while_activey  s  è ø€ ð 	×Ñ˜5Ÿ<™<Ô(Ø×ÑÓ!ˆßbÙ'¨Ô,ð
iáÑ5°cÔ:Ø×/Ñ/°Ô<Ø×?Ñ?ÀÀ{ÐTWÓX×XÐXð ô —L‘LÐ![Ø!%§¡¨C°ô>à×5Ñ5°eÓ<×<Ñ<ð ñ �u×2Ò2ð*ÝAØ$0×$HÑ$HØ¸ð %Ió %>ØEIð%JÐ!ñ !Ü—‘ÐTÐVZ×V_ÑV_ÐalÔmðqØ×5Ñ5°eÓ<×<Ð<ð Ø×%Ñ%Ð1ðbØ×3Ñ3°E¸;ÓG×GÐGØð Hð �>Š>˜k¨T×-CÑ-CÑCØà×Ñ¤×!2Ñ!2Ò2Ü�L‰LÐXÐZ^×ZcÑZcÐepÔqÜ'¨×(>Ñ(>ÀÈUÔSØ&*ˆEÔ#ØØ×1Ñ1°%Ô8Ü�L‰Lð UØVZ×V_ÑV_Ø$ d×&FÑ&FôHð ×+Ñ+¨K¸Ó?×?Ñ?ä�L‰Lð KØLPÏIÉIÐWbôdä'¨×(>Ñ(>ÀÈUØ38×3EÑ3EÌ×IYÑIYÑ3Yõ[à&*ˆEÕ#ðm Yùð =úÜò iÜ—‘ÐEÀtÇyÁyÐRUÐWXÐcg�×hÐhØûðiûô ò *Ø$)Ó!ð*úð
 =úÜ ò qÜ—L‘LÐ!RÐTX×T]ÑT]Ð_`Ðko�L×pÐpØûðqúð
 Húäò bÜ—‘ÐCÀTÇYÁYÐPQÐ\`�×aÒaûðbúð  @ûs  ‚<L?¿1I7 Á0I1Á1I7 Á5L?Á66I7 Â,I4Â-I7 Â2L?ÃJ1 Ã#L?ÄK ÄKÄK ÄL?Ä(L Ä=K?Ä>L ÅCL?ÈL<ÈA"L?É1I7 É4I7 É7	J.Ê $J)Ê$L?Ê)J.Ê.L?Ê1K Ê<L?Ê?K Ë L?ËK Ë	K<Ë#K7Ë2L?Ë7K<Ë<L?Ë?L Ì	L9Ì#L4Ì.L?Ì4L9Ì9L?c                 ób   — | j                   }|sy|\  }}t        j                  |dz  |dz  «      S )z³Random human-like pacing delay (s) from this adapter's ``human_delay`` config range
        (ms), installed per profile by the runner (``_wire_adapter_handlers``); ``None`` = off.rW  g     @�@)r„  rE  rF  )r˜  Úboundsrº   r»   s       r   Ú_get_human_delayz$BasePlatformAdapter._get_human_delayÃ  s7   € ð ×+Ñ+ˆÙØØ‰ˆˆBÜ�~‰~˜b 6™k¨2°©;Ó7Ð7r   rÄ  c              ƒ   ó`  K  — g }d}	 ddl m}m}  |«       rÒddl}| j	                  |«      }|st        d«      ‚t        | j                  «      }|j                  t        j                  |||¬«      ƒ d{  –—† «      }|j                  dd«      rZ|j                  d«      xs |j                  d	«      g}	|	D �
cg c]*  }
|
sŒt        |
«      j                  «       sŒ t        |
«      ‘Œ, }}
||fS 7 Œxc c}
w # t        $ r.}t         j#                  d
| j$                  |«       Y d}~||fS d}~ww xY w­w)zÑSynthesize auto-TTS audio -> ``(existing_paths, requested_path)``; empty/None on failure
        (logged, never raised). Path built platform-aware HERE: HERMES_SESSION_PLATFORM is cleared
        post-handler.Nr   )Útext_to_speech_toolÚcheck_tts_requirementsz!Empty text after markdown cleanup)rÜ   r,   rÜ  Tr¾  r­  z[%s] Auto-TTS failed: %s)r¦   r¨  r©  r‰  r“  rÍ   r¯   rc   rŠ  rÄ  rÅ  r�   r)  ÚexistsrF   rO   r   rL   r  )r˜  rÄ  ÚpathsÚrequested_pathr¨  r©  rŒ  Úspeech_textÚtts_dataÚraw_tts_pathsr-   Útts_errs               r   Ú_synthesize_auto_ttsz(BasePlatformAdapter._synthesize_auto_ttsÌ  s  è ø€ ð ˆØˆð	KßRÙ%Ô'Û$Ø"×3Ñ3°LÓA�Ù"Ü$Ð%HÓIÐIÜ!;¸D¿M¹MÓ!J�Ø Ÿ;™;¬W×->Ñ->Ø'¨kÀ~ô.W÷ (Wó X�à—<‘< 	¨4Ô0Ø$,§L¡L°Ó$>Ò$]À8Ç<Á<ÐP[ÓC\ÐB]�MØ3@Öa¨4ÂDÌTÐRVËZ×M^ÑM^ÕM`œS �YÐa�EÐað �nÐ$Ð$ð(Wúò bøÜò 	KÜ�N‰NÐ5°t·y±yÀ'×JÐJØ�nÐ$Ð$ûð	Küs`   ‚D.ˆA,C4 Á4C-Á5AC4 Â8C/Ã C/ÃC/Ã'C4 Ã)D.Ã-C4 Ã/C4 Ã4	D+Ã=!D&ÄD.Ä&D+Ä+D.rÅ  c                 óö   — t        |dd«      }t        | j                  |j                  j                  «      xr= |j
                  t        j                  k(  xr |xr | xr | j                  |||¬«       «      S )z�Auto-TTS on voice input (voice-first), gated by /voice or voice.auto_tts;
        skipped when streaming TTS already delivered audio this turn.Ú_hermes_run_generationNr�   )	ra   r  rÚ  r{   rR  rÍ  r;  ÚVOICEr©  )r˜  r‚   rW  r€  rÄ  rÅ  rô  s          r   Ú_wants_auto_ttsz#BasePlatformAdapter._wants_auto_ttsã  s‡   € ô ˜_Ð.FÈÓMˆ
ÜØ×*Ñ*¨5¯<©<×+?Ñ+?Ó@ò ]Ø×"Ñ"¤k×&7Ñ&7Ñ7ò]Ø<Hò]ØQ\È_ò]à×6Ñ6°{ÀJÐV[Ð6Ó\Ð\ó^ð 	^r   Útts_pathr  Úrecord_deliveryc              ƒ   ó  K  — d}|r)| j                   t        j                  k(  r|r
|dd |k(  r|}| j                  |j                  j
                  |||¬«      ƒ d{  –—† } ||«       t        |xr t        |dd«      «      S 7 Œ&­w)uª   Play one synthesized TTS file. Returns True when the ORIGINAL reply text rode
        along as a Telegram caption (first file, â‰¤1024 chars) so the text send is skipped.Ni   )rR  r®   rU  r|   rÜ  F)rc   r3  r˜  r•  r{   rR  r  ra   )	r˜  r‚   rÄ  r¶  r  r|   r·  rU  Ú
tts_results	            r   Ú_play_tts_filez"BasePlatformAdapter._play_tts_fileí  sŽ   è ø€ ð
 ˆÙ�T—]‘]¤h×&7Ñ&7Ò7¹LÈ\ÐZ_Ð[_ÐM`ÐdpÒMpØ"ˆGØŸ=™=Ø—L‘L×(Ñ(°XÀwÐYað )ó c÷ cˆ
á˜
Ô#Ü�GÒE¤¨
°I¸uÓ EÓFÐFðcús   ‚ABÁBÁ'BÚdelivery_adapterÚis_ephemeral_responsec              ƒ   óâ  K  — |sFt        |j                  xs d«      j                  «       j                  d| j                  xs df«      ry	 ddlm}m}m}m	}	 t        j                  |«      ƒ d{  –—† sy|j                  }
t        |dd«      }|€t        |dd«      } ||t        |xs d«      |«      }t        j                  |	||t        t        |
j                  d	|
j                  «      «      |
j                  t        |
d
d«      |t        |dd«      ¬«      ƒ d{  –—†  t        j                  ||«      ƒ d{  –—†  |S 7 ŒÑ7 Œ&7 Œ
# t         $ r t"        j%                  dd¬«       Y yw xY w­w)zÌLedger the final response BEFORE the send so a crash before platform ACK redelivers on
        next boot; best-effort, skips slash-command and ephemeral replies. Returns the obligation id
        or None.r`   rg  ú!Nr   )Úcompute_obligation_idÚledger_enabledÚmark_attemptingÚrecord_obligationÚledger_message_idrv   r_   rm   rŠ  )Úobligation_idrW  rc   rR  rm   rß  Úadapter_profilezdelivery ledger record failedTr   )rF   rÜ   rD  r	  re  Úgateway.delivery_ledgerr¿  rÀ  rÁ  rÂ  rÄ  rÅ  r{   ra   rc   rR  rO   r   r�  )r˜  r‚   rW  rÄ  r»  r¼  r¿  rÀ  rÁ  rÂ  r{   Ú
_ledger_idrÄ  s                r   Ú_record_delivery_obligationz/BasePlatformAdapter._record_delivery_obligationú  sh  è ø€ ñ !¤C¨¯
©
Ò(8°bÓ$9×$@Ñ$@Ó$B×$MÑ$MØ�$×+Ñ+Ò2¨sÐ3ô%5àð	÷[ó [ä ×*Ñ*¨>Ó:×:Ð:ØØ—\‘\ˆFô ! Ð(;¸TÓBˆJØÐ!Ü$ U¨L¸"Ó=�
Ù1ØœS Ò!1¨rÓ2°LóBˆMä×#Ñ#Ø!°ÈKÜœW V§_¡_°g¸v¿¹ÓOÓPØŸ™´'¸&À+ÈtÓ2TØ$Ü 'Ð(8Ð:JÈDÓ QôS÷ Sð Sô ×#Ñ# O°]ÓC×CÐCØ Ð ð# ;øðSøð Dùäò 	Ü�L‰LÐ8À4ˆLÔHÙð	üsm   ‚A
E/Á$E	 Á1EÁ2E	 Á6E/Á7B'E	 ÄEÄE	 Ä<EÄ=E	 ÅE/ÅE	 ÅE	 ÅE	 Å	 E,Å)E/Å+E,Å,E/rÄ  c              ƒ   óö  K  — 	 ddl m} ddlm}m}m} t        |dd«      rt        j                  ||«      ƒ d{  –—†  yt        t        |dd«      xs d«      }	t        j                  |||	«      ƒ d{  –—†   ||	«      rst        | j                  d	d«      }
| j                  |j                  «      }||ur”t        |
«      rˆ |
|j                  j                  t        |d
d«      ¬«      ƒ d{  –—†  y ||	«      €Mt        | j                  dd«      }t        |«      r* ||j                  j                  t        |d
d«      ¬«       yyyyy7 �Œ7 ŒÚ7 Œb# t        $ r t         j#                  dd¬«       Y yw xY w­w)aÆ  Mark the ledger row delivered/failed (best-effort). On ``send_path_degraded`` with a
        replacement adapter live, trigger another redelivery sweep (the watcher's may have run
        before this failure landed; atomic claiming keeps it idempotent). On any other rejection arm
        the runner's timed redelivery, so the reply goes out once the flood penalty or the retry
        backoff has passed instead of waiting for the next restart (#91653).r   )Úclassify_dead_error)Úis_reconnect_onlyÚmark_deliveredÚmark_failedrÜ  FNr   r`   Ú*_redeliver_failed_obligations_for_platformrŠ  )rx   Ú_schedule_flood_redeliveryzdelivery ledger update failedTr   )Úgateway.dead_targetsrÊ  rÆ  rË  rÌ  rÍ  ra   rÄ  rÅ  rF   r[  r7  r{   r`  rc   rO   r   r�  )r˜  rÄ  rY   r‚   r»  rÊ  rË  rÌ  rÍ  r   Ú	redeliverÚliveÚschedules                r   Ú_finalize_delivery_obligationz1BasePlatformAdapter._finalize_delivery_obligation  sw  è ø€ ð	IÝ@ß^Ñ^Ü�v˜y¨%Ô0Ü×'Ñ'¨¸ÓF×FÐFØÜœ ¨°Ó4Ò:¸Ó;ˆEÜ×#Ñ# K°ÀÓF×FÐFÙ  Ô'Ü#Ø×'Ñ'Ð)UÐW[ó]�	à×3Ñ3°E·L±LÓA�ØÐ/Ñ/´H¸YÔ4GÙ# E§L¡L×$9Ñ$9Ü,3Ð4DÐFVÐX\Ó,]ô_÷ _ñ _á$ UÓ+Ð3Ü" 4×#6Ñ#6Ð8TÐVZÓ[�Ü˜HÔ%Ù˜UŸ\™\×2Ñ2Ü%,Ð-=Ð?OÐQUÓ%VöXð &ð 4ð 5HÐ/ð Gùð Gøð_ùô ò 	IÜ�L‰LÐ8À4ˆLÖHð	Iüsu   ‚E9„6E ºE»E ¿E9Á 4E Á4EÁ5A9E Ã.EÃ/E Ã3E9Ã4AE ÅE9ÅE ÅE ÅE Å E6Å3E9Å5E6Å6E9rÆ  rÇ  c          
   ƒ   ó2  ‡ ‡‡‡K  — ddl m} dt        dt        fˆfd„}	|D �
�cg c]  \  }
}|rŒ	 |	|
«      sŒ|
‘Œ }}
}||D �
cg c]  }
 |	|
«      sŒ|
‘Œ c}
z  }|r6‰ j	                  ||D �
cg c]  }
d ||
«      › �df‘Œ c}
‰||«      ƒ d{  –—†  |j
                  j                  Šdt        d	t        d
t        dt        fˆˆˆ fd„}|D �
�cg c]  \  }
}|s	 |	|
«      rŒ|
|df‘Œ }}
}|r*t        j                  d‰ j                  t        |«      «       ||D �
cg c]  }
 |	|
«      rŒ|
ddf‘Œ c}
z  }|D ]B  \  }}}|dkD  rt        j                  |«      ƒ d{  –—†  	  | ||||¬«      ƒ d{  –—† «       ŒD yc c}}
w c c}
w c c}
w 7 Œÿc c}}
w c c}
w 7 Œ>7 Œ*# t        $ rl} |t        dt        |«      ¬«      «       |r"t        j                  d‰ j                  |«       n"t        j!                  d‰ j                  ||«       Y d}~Œ×d}~ww xY w­w)up  Deliver MEDIA-tag files and detected local files by type: images batched via
        ``send_multiple_images`` unless ``[[as_document]]``; otherwise audio â†’ send_voice (MEDIA
        tags only, never bare local files), video â†’ send_video, else send_document. Every failure is
        reported. Each send feeds ``record_delivery`` so media-only turns report SUCCESS.r   )Úquoter-   r	   c                 ó`   •— t        | «      j                  j                  «       t        v xr ‰ S r¤  )r)  r2   rb   Ú_IMAGE_EXTS)r-   rÇ  s    €r   Ú	_as_imagezABasePlatformAdapter._deliver_media_attachments.<locals>._as_imageE  s*   ø€ Ü˜“:×$Ñ$×*Ñ*Ó,´Ð;Ò^ÐD^Ð@^Ð^r   rP  r`   Nr™   Ú	media_tagc             “   óT  •K  — t        | «      j                  j                  «       }|r6t        ‰j                  ||¬«      r‰j                  ‰| ‰|¬«      ƒ d{  –—† }ne|t        v rA|r"t        j                  d‰j                  |‰«       ‰j                  ‰| ‰¬«      ƒ d{  –—† }n‰j                  ‰| ‰¬«      ƒ d{  –—† }|j                  sNt        j                  d‰j                  |rdnd	||j                  «       ‰j                  ‰| |‰¬
«      ƒ d{  –—†  |S 7 ŒÆ7 Œ7 Œd7 Œ­w)z_MEDIA-tag files (``media_tag``) may route to send_voice; bare local files never
            do.r³  )rR  r®   r|   r™   Nz([%s] Sending video attachment (%s) to %s)rR  rª  r|   )rR  r­  r|   z[%s] Failed to send %s (%s): %sr„  z
local filer°  )r)  r2   rb   rž   rc   rt  r´  r   r  r  r¬  r¯  rÜ  rL   r   r·  )r-   r™   rÚ  r˜   rY   rR  r|   r˜  s        €€€r   Ú	_send_onezABasePlatformAdapter._deliver_media_attachments.<locals>._send_oneO  s  øè ø€ ô �t“*×#Ñ#×)Ñ)Ó+ˆCÙÔ7¸¿¹ÀsÐU]Õ^Ø#Ÿ™°wÈ4ÐZbÐmu˜Óv×v‘ØœÑ#ÙÜ—K‘KÐ JÈDÏIÉIÐWZÐ\cÔdØ#Ÿ™°wÈ4ÐZb˜Óc×c‘à#×1Ñ1¸'ÈTÐ\dÐ1Óe×e�Ø—>’>Ü—‘Ð@À$Ç)Á)Ù*3™w¸ÀsÈFÏLÉLôZà×9Ñ9¸'À4ÐRZÐemÐ9Ón×nÐnØˆMð wøð døàeøð oúsJ   ƒAD(ÁD ÁAD(Â"D"Â#D(Â?D$Ã AD(ÄD&ÄD(Ä"D(Ä$D(Ä&D(Tz0[%s] Delivering %d non-image MEDIA attachment(s)F)r™   rÚ  rÝ  z[%s] Error sending media: %sz$[%s] Error sending local file %s: %s)rV  rÖ  rF   r  Ú_send_image_batchr{   rR  rÛ  r   r  r  r²   rÄ  rÛ  rO   rL   r   )r˜  r‚   rÅ  rÆ  rÇ  rL  r|   r·  Ú_quoterÙ  r'  r™   Ú_image_pathsrÜ  Úvr[  r-   rÚ  ÚerrrR  s   `   ` `            @r   Ú_deliver_media_attachmentsz.BasePlatformAdapter._deliver_media_attachments;  s  ûè ø€ õ 	1ð	_œCð 	_¤Dõ 	_à-8×Z™k˜a ÂÉYÐWXÍ\šÐZˆÑZØ KÖ@˜q±9¸Qµ<šÒ@Ñ@ˆÙØ×(Ñ(Ø¸\ÖJ¸˜7¡6¨!£9 +Ð.°Ò3ÒJÈHÐVaØó!÷ !ð !ð —,‘,×&Ñ&ˆð	¤#ð 	´Dð 	ÄTð 	Ìj÷ 	ð" +6×O¡$ ! Q¹Á)ÈAÅ,�!�Q˜’ÐOˆÑOÙÜ�K‰KÐJÈDÏIÉIÔWZÐ[`ÓWaÔbØ¨[ÖM¨Á	È!Å�1�e˜UÒ#ÒMÑMˆØ).ò 
	_Ñ%ˆD�(˜IØ˜QŠÜ—m‘m KÓ0×0Ð0ð_Ù¡i°¸xÐS\Ô&]× ]Õ^ñ	
	_ùó; [ùÚ@ùò Kð!úó, Pùò Nð 1øà ]ùÜò _Ù¤
°5ÄÀCÃÔ IÔJÙÜ—N‘NÐ#AÀ4Ç9Á9ÈcÕRä—L‘LÐ!GÈÏÉÐTXÐZ]Ô^ÿøð_üs¶   †H¤
E>¯E>¸E>¼HÁFÁFÁHÁ-F	
ÂHÂFÂ?HÃFÃ!FÃ(4HÄFÄ*FÄ1+HÅFÅHÅ"FÅ2F
Å3FÅ;HÆHÆFÆ	HÆ(A"HÈ
HÈHÈHc           	   ƒ   ó.  K  — 	 | j                  |j                  j                  |||¬«      ƒ d{  –—† } ||«       y7 Œ# t        $ rI}t        j                  d| j                  |d¬«        |t        dt        |«      ¬«      «       Y d}~yd}~ww xY w­w)zÑBatch-send images; a failure is logged (never raised) so other attachments still go.
        The batch result feeds ``record_delivery`` so media-only turns report their real
        outcome instead of FAILURE.)rR  r  r|   rL  Nz[%s] Error batching images: %sTr   FrÝ  )	rb  r{   rR  rO   r   rL   r  rÛ  rF   )r˜  r‚   r  r|   rL  r·  rY   Ú	batch_errs           r   rÝ  z%BasePlatformAdapter._send_image_batchp  sŠ   è ø€ ð	Ø×4Ñ4ØŸ™×,Ñ,°VÀhÐ\gð 5ó i÷ iˆFñ 	˜Õðiùäò 	Ü�N‰NÐ;¸T¿Y¹YÈ	Ð\`ˆNÔaÙœJ¨u¼CÀ	»NÔKÔLÜûð	üs;   ‚B„,A  °>±A  µ	B¾A  Á 	BÁ	?BÂBÂBÂB©r¼  z&tuple[SendResult, BasePlatformAdapter]c             ƒ   óª  K  — | j                  |j                  «      }t        j                  d|j                  t        |«      |j                  j                  «       | j                  |||||«      ƒ d{  –—† }|j                  |j                  j                  |||¬«      ƒ d{  –—† }	|�| j                  ||	||«      ƒ d{  –—†  |	|fS 7 ŒW7 Œ(7 Œ­w)uo  The delivery-ledger bracket every final text goes through, on the CURRENT transport
        (a reconnect may have replaced this adapter): record the obligation before the send,
        send with retry, finalize from the result â€” so a refused final (flood control, a dead
        transport) leaves a ledger row the boot sweep / runtime redelivery can act on. ``event``
        supplies the source and the ledger identity (``ledger_message_id`` or ``message_id``).
        Returns the result with the adapter that sent it: that adapter owns ``result.message_id``
        (an ephemeral delete must go to the same transport).z&[%s] Sending response (%d chars) to %sNr5  )
r7  r{   r   r  r  r²   rR  rÈ  r,  rÔ  )
r˜  r‚   rW  rÄ  r|   rÒ  r¼  r»  rÄ  rY   s
             r   Úsend_final_ledgeredz'BasePlatformAdapter.send_final_ledgered  sÞ   è ø€ ð  ×7Ñ7¸¿¹ÓEÐÜ�‰Ð<Ð>N×>SÑ>SÜ˜Ó% u§|¡|×';Ñ';ô	=à"×>Ñ>Ø�; Ð.>Ð@UóW÷ Wˆà'×8Ñ8Ø—L‘L×(Ñ(°,ÈÐ\dð 9ó f÷ fˆàÐ$Ø×4Ñ4°]ÀFÈEÐScÓd×dÐdØÐ'Ð'Ð'ðWøðføð eús6   ‚A3CÁ5CÁ60CÂ&CÂ'CÃCÃ	CÃCÃCÚephemeral_ttlc           	   ƒ   ó  K  — | j                  ||||t        |«      |¬«      ƒ d{  –—† \  }}	 ||«       |rR|dkD  rL|j                  r?|j                  r2|	j	                  |j
                  j                  |j                  |«       yyyyy7 Œd­w)zSNormal-lane final: the ledger bracket plus the message-id owner's ephemeral delete.)rÒ  r¼  Nr   )rç  r€   rÜ  rv   rì  r{   rR  )
r˜  r‚   rW  rÄ  r|   r¼  rè  r·  rY   r»  s
             r   Ú_send_final_textz$BasePlatformAdapter._send_final_text•  s˜   è ø€ ð *.×)AÑ)AØ�; ¨hÜ,¨UÓ3ÐK`ð *Bó *b÷ $bÑ ˆÐ ñ 	˜ÔÙ˜]¨QÒ.°6·>²>Àf×FWÒFWØ×7Ñ7¸¿¹×8LÑ8LÈf×N_ÑN_ÐanÕoð GX°>Ð.ˆ=ð	$bús   ‚$B¦B§A%BrF  c              ƒ   ój  K  — d}	 t        |«      }t        |«      rt        |«      dd nd}| j                  |j                  «      5  t	        |«      rdnW| j                  dt        |«      j                  › d|› d�d|j                  j                  |j                  j                  |¬«      }ddd«       €|S | j                  |j                  j                  ||¬	«      ƒ d{  –—†  |S # 1 sw Y   Œ?xY w7 Œ# t        $ r.}t        j                  d
| j                  |d¬«       Y d}~|S d}~ww xY w­w)z Tell the user a turn failed rather than leaving radio silence (last resort:
        a failing notice is logged, never raised). Returns the thread metadata used.Ni,  zno details availablezSorry, I encountered an error (z).
z2
Try again or use /reset to start a fresh session.zSorry, I encountered an error.r†  r*  z2[%s] Failed to send error notification to user: %sTr   )rƒ   rF   r3  r{   r=  r‹  r­  rJ  rc   rR  rÕ  rO   r   r   r  )r˜  r‚   rF  Ú_thread_metadataÚerror_detailrß  r¶  s          r   Ú_notify_turn_errorz&BasePlatformAdapter._notify_turn_error   sF  è ø€ ð  Ðð	lÜ9¸%Ó@ÐÜ+.¨q¬6œ3˜q›6 $ 3™<Ð7MˆLð ×+Ñ+¨E¯L©LÓ9ñ uÜ"7¸Ô">™$ÀD×DUÑDUØ5´d¸1³g×6FÑ6FÐ5GÀtÈLÈ>ð ZHð Hà4Ø%*§\¡\×%:Ñ%:ÀEÇLÁL×DXÑDXÐcsð	 EVó Eu�÷uð ˆØ'Ð'Ø—)‘) E§L¡L×$8Ñ$8À'ÐTd�)Óe×eÐeð  Ð÷uð uúð fùÜò 	lÜ�L‰LØDÀdÇiÁiÐQ[Ðfjð ÷ lð làÐûð	lüs`   ‚D3†AC9 ÁA&C+Â-C9 Â8D3Â9+C9 Ã$C7Ã%C9 Ã)D3Ã+C4Ã0C9 Ã9	D0Ä#D+Ä%D3Ä+D0Ä0D3Ú	extractedrÃ  Úanything_sentc          	   ƒ   óB  K  — | j                  «       }|j                  |j                  |j                  }	}}|rGt        j                  d| j                  t        |«      «       | j                  |||||«      ƒ d{  –—†  | j                  |||	|j                  |||¬«      ƒ d{  –—†  |sn|sk|	sh|se|j                  j                  «       rJt        j                  d| j                  t        |j                  «      |j                  j                  «       yyyyyy7 ŒŸ7 Œw­w)zðSend extracted image URLs, MEDIA files and bare local files (human-paced),
        then fail loudly if a non-empty response produced nothing deliverable. Attachment
        results feed ``record_delivery`` so the turn outcome reflects them.z1[%s] Extracted %d image(s) to send as attachmentsN)rÇ  rL  r|   r·  z¡[%s] response_delivery_dropped: non-empty response (%d chars) produced no delivered message or attachment for %s (empty after extract, recovery yielded nothing).)r¦  r  rÅ  rÆ  r   r  r  r²   rÝ  râ  rÇ  rÈ  rz   r   r{   rR  )
r˜  r‚   rï  r|   rð  r·  rL  r  rÅ  rÆ  s
             r   Ú_deliver_attachmentsz(BasePlatformAdapter._deliver_attachments·  s  è ø€ ð ×+Ñ+Ó-ˆØ+4×+;Ñ+;¸Y×=RÑ=RÐT]×TiÑTi˜[�ˆÙÜ�K‰KÐKÈTÏYÉYÔX[Ð\bÓXcÔdØ×(Ñ(¨°¸À+ÈÓ_×_Ð_Ø×-Ñ-Ø�; Ø'0×'KÑ'KØ#¨hÈð .ó Y÷ 	Yð 	Yñ ¡©;¹+È9×K`ÑK`×KfÑKfÔKhÜ�L‰Lð SàTX×T]ÑT]Ü˜Y×2Ñ2Ó3°U·\±\×5IÑ5IõKð Li¸+¨; �ð `øð	Yús%   ‚A9DÁ;DÁ<)DÂ%DÂ&A6DÄDc                 ó  — t        | j                  dd«      rt        |dd«      ryd|i}| j                  | j                  ddd¬«      r||d<   t	        j
                   | j                  |j                  j                  fi |¤Ž«      S )	z©Spawn the typing-refresh task, or None when ``typing_indicator=False``.
        ``stop_event`` is passed only when the (possibly overridden) ``_keep_typing`` accepts it.Útyping_indicatorTÚ_heartbeat_session_idNr|   rß  FrJ  )ra   ri  rH  rç  rÄ  rˆ  r{   rR  )r˜  r‚   r€  r|   r   s        r   Ú_start_typing_refreshz)BasePlatformAdapter._start_typing_refreshÌ  s…   € ô
 �t—{‘{Ð$6¸Ô=ÄÈÐPgÐimÔAnØØ",¨hÐ!7ˆØ×Ñ˜t×0Ñ0°,ÀuÐVZÐÔ[Ø#2ˆF�<Ñ Ü×"Ñ"Ð#4 4×#4Ñ#4°U·\±\×5IÑ5IÑ#TÈVÑ#TÓUÐUr   c          	   ƒ   ó&  K  — d|v }|}| j                  |j                  «      5  | j                  |«      \  }}| j                  ||¬«      }| j	                  |«      \  }}	t        |	«      j                  «       }	|r4t        j                  d| j                  t        |«      t        |«      «       g }
|s'| j                  |	«      \  }
}	| j                  |
|¬«      }
ddd«       
r| j                  |«      ƒ d{  –—† nd}|rU|
D �cg c]	  }||v sŒ|‘Œ }}|r?t        j                  d| j                  t        |«      |«       |
D �cg c]	  }||vsŒ|‘Œ }
}|
r*t        j                  d| j                  t        |
«      «       	sbs`|
s^s\t        |«      j                  «       }|rAt        j                  d| j                  t        |«      |j                  j                  «       |}	t!        |	|
||¬«      S # 1 sw Y   �ŒxY w7 �Œc c}w c c}w ­w)	u‰  Split a handler response into deliverable text + attachments. Order matters: MEDIA tags â†’
        image URLs â†’ residual directives â†’ bare local paths (skipped for ephemeral notices so config
        paths stay text; unknown-extension MEDIA tags survive for the bare-path detector). History
        dedup is bare-path only, off-loop, fail-open. An emptied non-empty response is recovered.r\  rÖ  z<[%s] extract_images found %d image(s) in response (%d chars)NzQ[%s] Suppressing %d bare local file path(s) already delivered in this session: %sz5[%s] extract_local_files found %d file(s) in responsez›[%s] response_delivery_recovered: extract pipeline reduced a non-empty response (%d chars) to empty with no attachment; delivering recovered original to %s)rÄ  r  rÅ  rÆ  rÇ  rÈ  )r3  r{   rÏ  r»  rr  Ú_strip_media_directivesrz   r   r  r  r²   rÝ  rÀ  rÊ  rL   rR  rÃ  )r˜  rx  r‚   rW  r¼  Úforce_documentrÈ  rÅ  r  rÄ  rÆ  r   r'  Ú
suppressedÚ
_recovereds                  r   Ú_extract_response_contentz-BasePlatformAdapter._extract_response_contentØ  s*  è ø€ ð +¨hÐ6ˆØˆð ×'Ñ'¨¯©Ó5ñ 	eØ$(×$6Ñ$6°xÓ$@Ñ!ˆK˜Ø×:Ñ:¸;ÐT_Ð:Ó`ˆKØ#'×#6Ñ#6°xÓ#@Ñ ˆF�Lô 3°<Ó@×FÑFÓHˆLÙÜ—‘ÐZÐ\`×\eÑ\eÔgjÐkqÓgrÔtwð  yAó  uBô  CØˆKÙ(Ø,0×,DÑ,DÀ\Ó,RÑ)�˜\Ø"×>Ñ>¸{ÐXcÐ>Ód�÷	eñ "ð ×FÑFÀ{ÓS×SÐSØ'+ð 	áØ%0ÖA °A¸²Lš!ÐAˆJÐAÙÜ—‘ð /Ø04·	±	¼3¸z»?ÈJôXà*5ÖJ Q¸À'Ò9IšqÐJ�ÐJÙÜ�K‰KÐOÐQU×QZÑQZÔ\_Ð`kÓ\lÔmñ ¡©+¹Ü0°Ó:×@Ñ@ÓBˆJÙÜ—‘ð  TàUY×U^ÑU^Ü" ;Ó/°·±×1EÑ1EôGð  *�Ü!Ø%¨fÀ+Ø#ÀÐ\gôið 	i÷G	eñ 	eúð Tûò Bùò KùsT   ‚"H¤B6G7ÃHÃ8HÃ9HÄ	HÄHÄ3HÅ	HÅHÅB#HÇ7HÇ<	HÈ
Hc              ƒ   ó\  K  — | j                  |t        |dd«      ¬«      }t        |«      rqt        j                  t
        j                  t        «      5   |«       }t        j                  |«      r#t        j                  |t        ¬«      ƒ d{  –—†  ddd«       yy7 Œ# 1 sw Y   yxY w­w)uö   Run the one-shot post-delivery callback (bounded, errors swallowed). The generation is
        read HERE â€” stamped on the interrupt event DURING the handler await; an earlier snapshot
        would let stale runs fire a fresher run's callbacks.r³  Nró  rÀ  )r  ra   r`  rP   rQ   rÄ  rÉ  rO   rA  rý  rÇ  Ú'_POST_DELIVERY_CALLBACK_TIMEOUT_SECONDS)r˜  rW  r€  Ú_post_cbÚ_post_results        r   Ú_fire_post_delivery_callbackz0BasePlatformAdapter._fire_post_delivery_callback
  s¢   è ø€ ð ×2Ñ2Ø¤G¨OÐ=UÐW[Ó$\ð 3ó ^ˆä�HÔÜ×$Ñ$¤W×%9Ñ%9¼9ÓEñ jÙ'›z�Ü×&Ñ& |Ô4Ü!×*Ñ*¨<ÔAhÔi×iÐi÷jð jð ð jø÷jð jüs0   ‚AB,Á;B ÂBÂB Â
B,ÂB Â B)Â%B,c                 ó–  — | j                   j                  |d«      }t        j                  «       }|�d| j                  j                  |«      }|�||ur|| j                   |<   yt        j                  d| j                  «       | j                  ||«       y|�1| j                  j                  |«      |u r| j                  ||«       yyy)a+  End-of-task guard/ownership reconciliation. A late ``_pending_messages`` arrival must not
        drop: re-queue it if another task already owns the session (drain handoff), else spawn the
        drain task and leave it the guard. Nothing pending: release the guard only if we still own
        it.NuH   [%s] Late-arrival pending message during cleanup â€” spawning drain task)r{  r`  rÄ  r˜  r~  r�   r   r�  r  Ú_spawn_drain_taskÚ_cleanup_finished_session_task)r˜  rW  r€  Úlate_pendingr˜  Úexisting_tasks         r   Ú_finish_session_taskz(BasePlatformAdapter._finish_session_task  sÆ   € ð
 ×-Ñ-×1Ñ1°+¸tÓDˆÜ×+Ñ+Ó-ˆØÐ#Ø ×/Ñ/×3Ñ3°KÓ@ˆMØÐ(¨]À,Ñ-Nð 7C�×&Ñ& {Ò3ä—‘Ø^Ø—I‘Iôð ×&Ñ& |°[ÕAØÐ%¨$×*=Ñ*=×*AÑ*AÀ+Ó*NÐR^Ñ*^Ø×/Ñ/°¸_ÕMð +_Ð%r   c           
   ƒ   ó&  ‡‡K  — dxŠŠˆˆfd„}| j                   j                  |«      xs t        j                  «       }|| j                   |<   t	        |«      }| j                  |||«      }	 | j                  d|«      ƒ d{  –—†  | j                  |«      ƒ d{  –—† }| j                  |j                  «      5  t        |«      rd}ddd«       t        |t        «      }| j                  |«      \  }}	|rA|j                  «       r1|| j                  v r#t         j#                  d| j$                  |«       d}|s7t         j'                  d| j$                  |j                  j(                  «       �nµ| j+                  ||||¬«      ƒ d{  –—† }
|
j,                  |
j.                  }}t1        |«      }g d}}| j3                  |||||«      r| j5                  |«      ƒ d{  –—† \  }}d}t7        |«      D ]a  \  }}	 || j9                  ||||dk(  ||«      ƒ d{  –—† z  }t;        j<                  t>        «      5  tA        jB                  |«       ddd«       Œc |s9|�7t;        j<                  t>        «      5  tA        jB                  |«       ddd«       |s(|
jD                  s|
j.                  s|
jF                  s|s|r%| jI                  |j                  j(                  «       |r!|s| jK                  ||||||	|«      ƒ d{  –—†  | jM                  ||
|‰xs ||¬	«      ƒ d{  –—†  ‰r‰ntO        |«       }| jP                  jS                  | jU                  |tW        |d
d«      |¬«      xs d«       | j                  d||rtX        jZ                  ntX        j\                  «      ƒ d{  –—†  | j_                  |«      ƒ d{  –—†  || j                  v �r6| j                  ja                  |«      }t         j'                  d| j$                  «       | jc                  |«       | je                  |j                  j(                  ||¬«      ƒ d{  –—†  | jg                  ||«       	 | je                  |j                  j(                  ||¬«      ƒ d{  –—†  | ji                  ||«      ƒ d{  –—†  | je                  |j                  j(                  d|d¬«      ƒ d{  –—†  | j_                  |«      ƒ d{  –—†  | jk                  ||«       y	 | je                  |j                  j(                  ||¬«      ƒ d{  –—†  | ji                  ||«      ƒ d{  –—†  | je                  |j                  j(                  d|d¬«      ƒ d{  –—†  | j_                  |«      ƒ d{  –—†  | jk                  ||«       y7 �Œ17 �Œ# 1 sw Y   �ŒòxY w7 �Œ=7 �Œë7 �Œµ# 1 sw Y   �ŒæxY w# t;        j<                  t>        «      5  tA        jB                  |«       ddd«       w # 1 sw Y   w xY wxY w# 1 sw Y   �ŒœxY w7 �Œ27 �Œ7 �Œ�7 �Œy7 �Œñ7 �Œ±7 �Œš7 �Œl7 �ŒV# t        jl                  $ r^ t        jn                  «       | jp                  v }| j                  d||rtX        jr                  ntX        j\                  «      ƒ d{  –—†7   ‚ tt        $ rŽ}| j                  d|tX        j\                  «      ƒ d{  –—†7   t         jw                  d| j$                  |d¬«       | jy                  ||«      ƒ d{  –—†7  xs |}t        |tz        t|        f«      r‚ Y d}~�ŒAd}~ww xY w7 �Œ7 �Œ7 �ŒÙ7 �ŒÃ# | je                  |j                  j(                  ||¬«      ƒ d{  –—†7   | ji                  ||«      ƒ d{  –—†7   | je                  |j                  j(                  d|d¬«      ƒ d{  –—†7   | j_                  |«      ƒ d{  –—†7   | jk                  ||«       w xY w­w)z4Background task that actually processes the message.Fc                 óD   •— | �dŠ‰xs t        t        | dd«      «      Šy y )NTrÜ  F)r  ra   )rY   Údelivery_attemptedÚdelivery_succeededs    €€r   Ú_record_deliveryzIBasePlatformAdapter._process_message_background.<locals>._record_delivery2  s/   ø€ àÐ!Ø%)Ð"Ø%7Ò%b¼4ÄÈÐPYÐ[`Ó@aÓ;bÑ"ð "r   r  Nz:[%s] Suppressing stale response for interrupted session %sz0[%s] Handler returned empty/None response for %srå  r   )rð  r·  r³  r�   r`   r  z([%s] Processing queued follow-up messager  r·   )r|   rè  z[%s] Error handling message: %sTr   )?rz  r�   rÄ  r‚  rƒ   rö  r  rl  r3  r{   r=  rŽ   r  r(  rá  r{  r   r  r  r�  rR  rü  rÄ  rÅ  rˆ   rµ  r±  r-  rº  rP   rQ   rR   rC   r¥  r  rÆ  rî  rê  rò  r  r�  r  r¥  ra   r<  r  r  rk  r`  Ú_clear_session_guardrë  r  r  r  r
  r˜  rˆ  Ú	CANCELLEDr¸  r   rî  Ú
SystemExitÚKeyboardInterrupt)r˜  r‚   rW  r  r€  rì  ré  rx  r¼  Ú_ephemeral_ttlrï  rÄ  rÅ  Ú_final_thread_metadataÚ
_tts_pathsÚ_tts_requested_pathÚ_tts_caption_deliveredÚ
_tts_indexÚ	_tts_pathÚprocessing_okrŽ  ÚexpectedrF  r
  r  s                          @@r   rƒ  z/BasePlatformAdapter._process_message_background.  s»  ùè ø€ à27Ð7ÐÐ/õ	cð ×/Ñ/×3Ñ3°KÓ@ÒSÄGÇMÁMÃOˆØ-<ˆ×Ñ˜kÑ*Ü5°eÓ<ÐØ×0Ñ0°¸ÐIYÓZˆðf	DØ×+Ñ+Ð,AÀ5ÓI×IÐIØ!×2Ñ2°5Ó9×9ˆHð ×+Ñ+¨E¯L©LÓ9ñ $Ü(¨Ô/Ø#�H÷$ô %/¨x¼Ó$HÐ!à'+×'=Ñ'=¸hÓ'GÑ$ˆH�ná˜O×2Ñ2Ô4¸È×H^ÑH^Ñ9^Ü—‘ÐXÐZ^×ZcÑZcØ'ô)à�ÙÜ—‘ÐOÐQU×QZÑQZÐ\a×\hÑ\h×\pÑ\pÖqà"&×"@Ñ"@Ø˜e [ÐH]ð #Aó #_÷ _�	à,5×,BÑ,BÀI×DYÑDY˜k�ä)>Ð?OÓ)PÐ&Ø24°dÐ/�
Ø×'Ñ'Ø˜{¨O¸\È;ôXà<@×<UÑ<UÐVbÓ<c×6cÑ3�JÐ 3à).Ð&Ü-6°zÓ-Bò 1Ñ)�J 	ð1Ø.¸×8KÑ8KØ! <°¸JÈ!¹OÐMcØ,ó9.÷ 3.ñ .Ð.ô (×0Ñ0´Ó9ñ 1ÜŸI™I iÔ0÷1ð 1ð1ñ "Ð&9Ð&EÜ#×,Ñ,¬WÓ5ñ 7ÜŸ	™	Ð"5Ô6÷7ñ   9×#3Ò#3°y×7LÒ7LÐPY×PeÒPeÙ%Ñ)?Ø×.Ñ.¨u¯|©|×/CÑ/CÔDÙÑ(>Ø×/Ñ/Ø˜{¨LÐ:PØ-¨~Ð?OóQ÷ Qð Qð ×/Ñ/Ø˜9Ð&<Ø"4Ò"NÐ8NØ$4ð 0ó 6÷ 6ð 6ñ 3EÑ.ÌdÐS[ËnÐJ\ˆMà×/Ñ/×7Ñ7¸×8TÑ8TØœW _Ð6NÐPTÓUØð 9Uó 9ò 9#à "ô$ð ×+Ñ+Ø(¨%Ù-:Ô!×)Ò)Ô@Q×@YÑ@Yó[÷ [ð [ð
 ×/Ñ/°Ó<×<Ð<Ø˜d×4Ñ4Ò4Ø $× 6Ñ 6× :Ñ :¸;Ó G�Ü—‘ÐGÈÏÉÔSØ×)Ñ)¨+Ô6Ø×/Ñ/°·±×0DÑ0DÀkÐ\lÐ/Óm×mÐmØ×&Ñ& }°kÔBØð" ×+Ñ+¨E¯L©L×,@Ñ,@À+ÐXhÐ+Ói×iÐiØ×3Ñ3°KÀÓQ×QÐQà×+Ñ+Ø—‘×$Ñ$ dÐ5EÐUVð ,ó X÷ Xð Xð ×/Ñ/°Ó<×<Ð<Ø×%Ñ% k°?ÕCð= 5ð. ×+Ñ+¨E¯L©L×,@Ñ,@À+ÐXhÐ+Ói×iÐiØ×3Ñ3°KÀÓQ×QÐQà×+Ñ+Ø—‘×$Ñ$ dÐ5EÐUVð ,ó X÷ Xð Xð ×/Ñ/°Ó<×<Ð<Ø×%Ñ% k°?ÕCðK JùØ9ù÷$ñ $úð_ùð 7dùð
3.ù÷1ñ 1ûœZ×0Ñ0´Ó9ñ 1ÜŸI™I iÔ0÷1÷ 1ñ 1ý÷7ñ 7úðQùð6ùð[ùð
 =ùð
 nùð& jùØQùðXùð =úô- ×%Ñ%ò 	Ü×+Ñ+Ó-°×1OÑ1OÐOˆHØ×+Ñ+Ø(¨%Ù/7Ô!×+Ò+Ô=N×=VÑ=VóX÷ Xñ Xð Üò 	Ø×+Ñ+Ð,DÀeÔM^×MfÑMfÓg×gÑgÜ�L‰LÐ:¸D¿I¹IÀqÐSWˆLÔXØ&*×&=Ñ&=¸eÀQÓ&G× GÐ GÒ\ÐL\Ðä˜!œjÔ*;Ð<Ô=Øõ >ûð	úð jùØQùðXùð =úð ×+Ñ+¨E¯L©L×,@Ñ,@À+ÐXhÐ+Ói×iÑiØ×3Ñ3°KÀÓQ×QÑQà×+Ñ+Ø—‘×$Ñ$ dÐ5EÐUVð ,ó X÷ Xñ Xð ×/Ñ/°Ó<×<Ñ<Ø×%Ñ% k°?ÕCüsR  „A)`Á.Y ÂV4ÂY ÂV7ÂY Â<V:Ã
C Y Æ
WÆAY ÇW
Ç Y Ç;WÈWÈWÈY È8WÉ'Y É5X%ÊA5Y Ì X2Ì!Y Ì"X5Ì#BY Î)X8Î*Y ÏX;ÏB
Y ÑX>ÑY Ñ%+`ÒYÒ`Ò*YÒ+0`ÓYÓ`Ó4Y
Ó5`Ô]" Ô+`Ô8]Ô9`Õ]Õ0`Ö]Ö`Ö]Ö`Ö4Y Ö7Y Ö:WÖ?	Y ×
Y ×W×W	×Y ×X"×7X	Ø	X"ØXØX"Ø"Y Ø%X/Ø*	Y Ø5Y Ø8Y Ø;Y Ø>Y Ù`Ù`Ù`Ù
`ÙA)]Ú6Z9Ú7]Û$]Û)[,Û*=]Ü'\*Ü( ]Ý]" Ý]Ý]" Ý`Ý`Ý`Ý`Ý",`Þ^Þ`Þ)^,Þ*1`ß_ß`ß5_8ß6`à`rŽ  c           	      ó�   — | j                  |«       | j                  |t        j                  | j	                  ||«      «      «       y)u	  Hand the session to a fresh task for a queued follow-up â€” never recurse (chained
        follow-ups grew the C stack to SIGSEGV). Clearing (not deleting) the Event keeps the guard
        live for concurrent inbound; ownership moves so stale-lock detection works.N)r  r„  rÄ  rˆ  rƒ  )r˜  rŽ  rW  s      r   r  z%BasePlatformAdapter._spawn_drain_task¤  s@   € ð 	×!Ñ! +Ô.Ø× Ñ ØÜ×Ñ × @Ñ @ÀÐP[Ó \Ó]õ	_r   c                 ó`   — | j                   j                  |«      }|�|j                  «        yy)zUClear (not delete) the session's interrupt Event so the guard stays live for inbound.N)rz  r�   Úclear)r˜  rW  Ú_actives      r   r  z(BasePlatformAdapter._clear_session_guard­  s,   € à×'Ñ'×+Ñ+¨KÓ8ˆØÐØ�M‰M�Oð r   c                 ó€   — | j                  ||¬«       || j                  vr| j                  j                  |d«       yy)uŒ  Release a finished owner task's guard, dropping its ``_session_tasks`` entry ONLY if the
        guard was released: after a concurrent guard swap the done-task entry lets
        ``_session_task_is_stale`` heal the orphan.

        Release-then-conditional-delete is the #48300 fix: when a concurrent path (reset/new command, drain
        handoff) swapped ``_active_sessions[key]`` to a different guard, ``_release_session_guard`` skips on
        the guard mismatch and the lock stays installed. If we deleted ``_session_tasks`` unconditionally
        (the old order), ``_session_task_is_stale`` would later see no owner task and report "not stale", so
        the orphaned guard would never be healed â€” a permanent session deadlock. Keeping the done-task entry
        when the guard survives lets the on-entry self-heal detect the stale lock and clear it on the next
        inbound message.
        rv  N)rz  rz  r~  r`  )r˜  rW  r€  s      r   r  z2BasePlatformAdapter._cleanup_finished_session_task³  s@   € ð 	×#Ñ# K°Ð#ÔGØ˜d×3Ñ3Ñ3Ø×Ñ×#Ñ# K°Õ6ð 4r   c           
   ƒ   ó¶  K  — t        d«      D ]œ  }| j                  D �cg c]  }|j                  «       rŒ|‘Œ }}|s nn|D ]-  }| j                  j	                  |«       |j                  «        Œ/ 	 t        j                  t        j                  d„ |D «       ddiŽd¬«      ƒ d{  –—†  Œž t        j                  t         «      5  d
dlm}  || j&                  d¬«       ddd«       | j)                  «       j+                  «       D ]  }|j-                  «        Œ | j                  | j                  | j.                  | j&                  | j0                  | j)                  «       fD ]  }|j3                  «        Œ yc c}w 7 ŒØ# t        j                  $ r6 t        j                  d| j                  t        d	„ |D «       «      «       Y  �Œw xY w# 1 sw Y   ŒíxY w­w)z}Cancel in-flight background tasks (shutdown/replacement); 5s bound each,
        stragglers are untracked and left to unwind.é   c              3   óF   K  — | ]  }t        j                  |«      –— Œ y ­wr¤  )rÄ  r	  ©rÈ   Úts     r   rË   z>BasePlatformAdapter.cancel_background_tasks.<locals>.<genexpr>Ó  s   è ø€ Ò$F¸1¤W§^¡^°A×%6Ñ$Fùs   ‚!Úreturn_exceptionsTr\   rÀ  Nzo[%s] %d background task(s) did not exit within 5s; releasing tracking and letting them unwind in the backgroundc              3   ó>   K  — | ]  }|j                  «        –— Œ y ­wr¤  )rº  r"  s     r   rË   z>BasePlatformAdapter.cancel_background_tasks.<locals>.<genexpr>Ø  s   è ø€ Ò-J¸q°!·&±&³(¬lÑ-JùrG  r   )Úflush_pending_to_fileÚadapter_shutdown)r¼  )rÕ  r†  rº  rˆ  rÿ  r»  rÄ  rÇ  ÚgatherrÉ  r   rL   r  ÚsumrP   rQ   rO   Úgateway.shutdown_flushr&  r{  rY  rF  r¼  r~  rz  r  )r˜  rÉ   r   Útasksr&  rh  Úbuckets          r   Úcancel_background_tasksz+BasePlatformAdapter.cancel_background_tasksÅ  s¯  è ø€ ô
 �q“ò 	ˆAØ&*×&<Ñ&<ÖP˜dÀDÇIÁIÅK’TÐPˆEÐPÙÙØò �Ø×.Ñ.×2Ñ2°4Ô8Ø—‘•ððÜ×&Ñ&Ü—N‘NÑ$FÀÔ$FÐ_ÐZ^Ñ_Øô!÷ !ñ !ð	ô  × Ñ ¤Ó+ñ 	UÝDÙ! $×"8Ñ"8ÐASÕT÷	Uð ×.Ñ.Ó0×7Ñ7Ó9ò 	!ˆEØ×ÑÕ ð	!à×-Ñ-¨t×/MÑ/MÈt×ObÑObØ×-Ñ-¨t×/DÑ/DÀd×F_ÑF_ÓFaðcò 	ˆFà�L‰L�Nñ	ùò) Qð!ùô ×'Ñ'ò Ü—‘ð  ^à#Ÿy™y¬#Ñ-JÀEÔ-JÓ*JôLó ð	ú÷
	Uð 	Uüsd   ‚GŸE:µE:¹8GÁ24FÂ&E?Â'FÂ+GÃGÃ!BGÅ?FÆAG
ÇGÇ	G
Ç
GÇGÇGc                 ó\   — || j                   v xr | j                   |   j                  «       S )z3Check if there's a pending interrupt for a session.)rz  rá  r~  s     r   Úhas_pending_interruptz)BasePlatformAdapter.has_pending_interruptã  s,   € à˜d×3Ñ3Ð3Òc¸×8MÑ8MÈkÑ8Z×8aÑ8aÓ8cÐcr   c                 ó:   — | j                   j                  |d«      S )z0Get and clear any pending message for a session.N)r{  r`  r~  s     r   Úget_pending_messagez'BasePlatformAdapter.get_pending_messageç  s   € à×%Ñ%×)Ñ)¨+°tÓ<Ð<r   Ú	chat_nameÚ	user_nameÚ
chat_topicra  Úchat_id_altro   Úguild_idÚrole_authorizedÚauto_thread_createdÚauto_thread_initial_namec                 ó„  — dt         t           fd„}t        | j                  t        |«      |||�|dk(  rdn
t        |«      | ||«      |xs dj	                  «       xs d||	|
 ||«       ||«       ||«       ||«      ¬«      }t        | dd«      }|d}}| j                  �1dd	lm} 	 | j                  j                  t        di |¤Ž|¬
«      xs |}t        di |¤||||dœ¤Ž}t        j                  | «      |_        ||_        |S # |$ r d}Y Œ>t        $ r& t        j                  d| j                  |d¬«       Y Œkw xY w)z¦Build a SessionSource; with ``gateway.profile_routes`` configured the matching
        profile is stamped on ``source.profile`` for per-profile HERMES_HOME isolation.r	   c                 ó    — | rt        | «      S d S r¤  )rF   r  s    r   Ú_optz.BasePlatformAdapter.build_source.<locals>._optö  s   € Ù!&”3�u“:Ð0¨DÐ0r   Nr`   )rc   rR  r2  rr   rK  r3  rm   r4  ra  r5  rJ  ro   r6  r×  rv   rŠ  Fr   )ÚProfileRouteRejected)rÅ  TzAProfile resolution failed for %s/%s, defaulting to active profiler   )rx   r7  r8  r9  rf   )r-  rF   r†   rc   rz   ra   r[  Úgateway.profile_routingr=  Ú_profile_name_for_sourcer>  rO   r   rL   ÚweakrefÚrefÚ_transport_adapter_refrc  )r˜  rR  r2  rr   rK  r3  rm   r4  ra  r5  rJ  ro   r6  r×  rv   r7  r8  r9  r<  Úfieldsr  rx   rc  r=  r{   s                            r   Úbuild_sourcez BasePlatformAdapter.build_sourceë  so  € ð	1œ8¤C™=ó 	1äØ—]‘]¬C°«LÀIÐYbØ#˜O¨w¸"ª}‘DÄ#ÀgÃ,Ø©4°	«?Ø"Ò( b×/Ñ/Ó1Ò9°TÀ{Ø#¨F¹TÀ(»^Ù˜(“^±D¸Ó4HÙ˜JÓ'ô)ˆô   Ð&6¸Ó=ˆØ*7¸Ð'ˆØ×ÑÐ*ÝDðFØ×-Ñ-×FÑFÜ!Ñ+ FÑ+¸]ð Gó Lò ]ØO\ð ô ñ R ð R°È/Ø3FØ8PóRˆô
 )0¯©°DÓ(9ˆÔ%Ø(>ˆÔ%Øˆøð (ò .Ø)-Ò&Üò FÜ—‘ÐbØ#Ÿ}™}¨gÀð ö FðFús   Â)*D ÄD?Ä+D?Ä>D?c              ƒ   ó   K  — y­w)zrGet information about a chat/channel; dict with at least ``name``
        and ``type`` ("dm", "group", "channel").Nrf   rš  s     r   Úget_chat_infoz!BasePlatformAdapter.get_chat_info  rÎ  rÏ  c                  ó   — y)zªPer-source toolset override REPLACING ``platform_toolsets.<platform>``, or None
        (default); validated via ``_get_platform_tools`` (webhook adapter pins per-route).Nrf   )r˜  r{   s     r   Útoolsets_for_sourcez'BasePlatformAdapter.toolsets_for_source  s   € ð r   c                 ó   — |S )zrFormat a message for this platform (override for e.g. Telegram
        MarkdownV2); default returns content as-is.rf   )r˜  rß  s     r   Úformat_messagez"BasePlatformAdapter.format_message!  s	   € ð ˆr   Ú
max_lengthr¹   zCallable[[str], int]c                 óŽ  — |xs t         } || «      |k  r| gS d}d}g }| }d}|�rÙ|�d|› d�nd}	||z
   ||	«      z
   ||«      z
  }
|
dk  rt        d|dz  «      }
 ||	«       ||«      z   ||z
  k  r/|	|z   }|�t        |d	|«      d
   r||z  }|j                  |«       �n^|t         urt	        ||
|«      n|
}|d| }|j                  d«      }||dz  k  r|j                  d«      }|dk  rt        d|«      }|d| }|j                  d«      |j                  d«      z
  }|dz  dk(  r‚|j                  d«      }|d
kD  r/||dz
     dk(  r$|j                  dd
|«      }|d
kD  r||dz
     dk(  rŒ$|d
kD  r8t        |j                  dd
|«      |j                  dd
|«      «      }||dz  kD  r|}|d| }||d j                  «       }|	|z   }t        ||du|xs d«      \  }}|r|nd}|j                  |r||z   n|«       |r�ŒÙt        |«      dkD  r2t        |«      D ��cg c]  \  }}|› d|dz   › dt        |«      › d�‘Œ }}}|S c c}}w )a  Split a long message into chunks preserving code blocks: a split inside a fence closes it
        at the chunk end and reopens it (same language tag) in the next; multi-chunk output gets
        ``(1/3)`` indicators. ``len_fn`` overrides ``len`` (``utf16_len`` for Telegram).é
   z
```Nz```rL  r`   r·   r]   Tr   r}  ú`z\`ú\r±  r”   rg  r•   )	r²   rZ  r5  r�  r½   ÚrfindÚcountrD  r-  )rß  rK  r¹   Ú_lenÚINDICATOR_RESERVEÚFENCE_CLOSEr’  ræ  Ú
carry_langr1   ÚheadroomÚfinal_chunkÚ	_cp_limitÚregionÚsplit_atr»  Úbacktick_countÚlast_btÚ
safe_splitÚ
chunk_bodyÚ
full_chunkÚin_codeÚlangrŸ  r”  s                            r   Útruncate_messagez$BasePlatformAdapter.truncate_message&  sÎ  € ð Š}œˆÙ�‹=˜JÒ&Ø�9ÐØÐØˆØˆØˆ	Ø$(ˆ
ÚØ-7Ð-C�s˜:˜, bÑ)ÈˆFà!Ð$5Ñ5¹¸V»ÑDÁtÈKÓGXÑXˆHØ˜!Š|Ü˜q *°¡/Ó2�á�F‹|™d 9›oÑ-°Ð>OÑ1OÒOØ$ yÑ0�ØÐ)Ô.?À	È4ÐQ[Ó.\Ð]^Ò._Ø ;Ñ.�KØ—‘˜kÔ*Ùð BFÌSÁÔ" 9¨h¸Ô=ÐV^ð à˜z 	Ð*ˆFØ—|‘| DÓ)ˆHØ˜) q™.Ò(Ø!Ÿ<™<¨Ó,�Ø˜!Š|ô ˜q )Ó,�à! ) 8Ð,ˆIØ&Ÿ_™_¨SÓ1°I·O±OÀEÓ4JÑJˆNØ Ñ! QÒ&Ø#Ÿ/™/¨#Ó.�Ø ’k i°¸!±Ñ&<ÀÒ&DØ'Ÿo™o¨c°1°gÓ>�Gð  ’k i°¸!±Ñ&<ÀÓ&Dà˜Q’;Ü!$Ø!Ÿ™¨¨Q°Ó8¸)¿/¹/È$ÐPQÐSZÓ:[ó"]�Jà! I°¡NÒ2Ø#-˜Ø" 9 HÐ-ˆJØ! ( )Ð,×3Ñ3Ó5ˆIØ *Ñ,ˆJä-¨j¸*ÈDÐ:PÐR\ÒRbÐ`bÓc‰MˆG�TÙ!(™¨dˆJà�M‰M±g˜* {Ò2À:ÔNóY ôZ ˆv‹;˜Š?ÜIRÐSYÓIZ×[¹X¸QÀ˜˜˜r ! a¡% ¨¬#¨f«+¨°aÒ8Ð[ˆFÑ[Øˆùó \s   È!I)NNNr¤  rê  r·  ©r‚   r:  r	   N)rj  )rj  N)údangerous commandF)rd  NTTFrY  ©r`   )NNNN)r`   N)rn  NN)NNr]   rn  )Nrs   NNNNNNFNNNNFFN)r˜  N(  rJ  rK  rL  rM  r\  r  rO  r]  rF   r-  rb  rc  rd  re  rf  rg  rh  r[  r4  r3  r’  rÙ  r/  rN  r”  r›  r�  rŸ  r¡  r+  r.  r¤  r¦  r©  rÛ  r®  r¹  rÒ  rÍ  rò  Úfatal_error_messageÚfatal_error_codeÚfatal_error_retryablerÚ  r0  rÞ  rï  rà  rë  rè  ró  rø  ré  r  r!  r$  r%  r&  rv  r/  r1  r  r5  ÚMessageHandlerr7  r9  r<  r:  rB  rD  rF  rI  rM  rQ  rT  rW  ra  rd  ri  rj  rk  r[  rl  rm  ro  rp  rq  rx  r|  r€  r~  r…  r‹  r�  r‘  r”  r–  r‰  r¬  rÊ  r   rÍ  rÑ  rÕ  rÖ  rÙ  rß  râ  ræ  rì  r6  rî  rï  rð  r7  rñ  rò  ró  rô  rõ  Ústaticmethodr÷  rù  rü  rþ  r   r  r	  r  r,  r1  r  Úclassmethodr  r  r  r0  r  r"  r3  r7  r;  r=  rH  rK  rb  rZ  rY  rX  rr  rt  rv  rƒ  r}  r‹  r‚  r“  r•  rF  r—  rQ  r™  Úbytesr�  r   r¢  r¥  r§  r©  r¬  r¯  r·  rW  rÜ  r†   r»  rÀ  re  rd  rÏ  rÑ  rÝ  rÄ  r‚  rç  r½  rë  râ  rî  rð  rò  rù  rö  r  r  r  r  r  r<  r  r  r  r  r$  r(  r/  r>  r3  r7  r,  rC  r>  r?  rG  r.  r´  rY  r^  re  ri  rq  rl  rk  ru  rz  r|  r  r…  r„  r‹  r�  r“  r“  r™  r¦  r±  rµ  rº  rÈ  rÔ  râ  rÝ  rç  rê  r¸  rî  rò  rö  rü  r  r  rƒ  r  r  r  r-  r/  r1  rD  rF  rH  rJ  rb  rf   r   r   rc  rc    s&   … ÙVð "'Ð˜$Ó&à!&Ð˜$Ó&ð* sð *°(¸3±-ð *ÀDó *ð %)Ð˜TÓ(à!&Ð˜$Ó&à #Ð˜#Ó#ð ,1Ð" DÓ0ð  $Ð˜Ó#ð
 #(Ð˜4Ó'ð €Nð@/˜~ð @/¸ó @/ðD ð ¨#¨°¨Ñ 4ò ó ðð
a°3ð a¸3ó að#¨sð #°xÀÀÀsÀ
Ñ7Kó #ð
 ð¨Dò ó ðð ð¨4ò ó ðð UYØ!%òØ! #™ðØ9AÀ$ÀsÈCÀxÁ.Ñ9Qðà˜#‘ðà*.óò°Sð ÀHÈTÐRUÐWZÐRZÉ^ÑD\ð Ðhló ð
¨(°3©-ó ð ?CòZ¨ð Z°sð ZÀSð ZØ#+¨D°°c°©NÑ#;ðZØGQóZð
+¨#ð 
+°Sð 
+¸Tó 
+ð <AÐY[ò A sð A°Sð AÐSVð AÐ`hÐilÑ`mó Að2¨=ð ¸Só ñ
 ÑQÓR€OÙ"Ñ#IÓJÐÙÑ CÓDÐÙ$Ñ%MÓNÐð[°ð [¸ó [ð,¨xÐ9NÐ8OÐQZÐ[_ÑQ`ÐcgÑQgÐ8gÑ/hð ,Ðmqó ,ð aÐàð Dò ó ðð AEò g°¸±ð gÈó gô
ôdðq Sð q°3ð qÀdð qÈtó qð3°#ð 3ÀDó 3ô ð6$¨Cð $¸3ð $Èsð $ÐW[ó $ôL,ð #'Ð˜CÓ&Ø,0Ð˜H S™MÓ0ò1¨Cð 1¸4ó 1ô@@ð ð+�cò +ó ð+ð ð˜dò ó ðð(¨>ð (¸dó (ð/Ø ¨$¨s°C¨x©.¸#Ð)>À	È$ÁÐ)OÑ PÑQð/ØVZó/ð%¨°¸3¸%ÀÈ#ÁÐ:NÑ1OÑ(Pð %ÐUYó %ðI¨<ð I¸Dó Ið&-°¸À<ÐQTÐBUÐW`ÐaeÑWfÐBfÑ9gÑ0hð -Ðmqó -ð)¨H°X¸tÀCÈÀH¹~Ð>NÐPYÐZ^ÑP_Ð>_Ñ5`Ñ,að )Ðfjó )ð-Ø  ¨3°¸±¸xÈ¹}Ð*MÈtÐ*SÑ!TÑUð-ØZ^ó-ð X\Ø7;ñØOTØ9=ò¨X°c©]ð ÀxÐPSÁ}ð Ø'/°¡}ðØHLðà)1°#©ðàBJÈ4Á.óð4,¨sð ,°tó ,ð
l¨h°s©mð lÀó lð
K¨(°3©-ó Kð H¨_Ñ$=ó ð0 nð ¸ó òT¨8°C©=ð TÀHÈSÁMó Tð> !Ð�cÓ Ø'*Ð˜uÓ*Ø-0Ð# UÓ0ð *-Ð Ó,Ø%(Ð˜UÓ(Ø/2Ð% uÓ2Ø+.Ð! 5Ó.àRUÐtxò  sð °Uð È%ð ÐbjÐkpÑbqð ð  ~Có ôdð6¨ð 6¸3ó 6ð7¨/ð 7¸có 7ð. ^ð .¸ó .ô_ð(}¨X°nÑ-Eð }È%ó }ð
9 3ð 9¨8°NÑ+Có 9ô)ð3¨sð 3°tó 3ð>¨3ð >°4ó >ð6$5¸Cð $5ÀHÈSÁMó $5ðL,È#ð ,ÐRZÐ[^ÑR_ó ,ð\ Ø49ò F¨Tð F¸dò Fó ðFð
 ó,ó ð,ð ØOSØ8<ò` #ð `°ð `¸xÈ¹}ð `Ø% d¨3°¨8¡nÑ5ð`ØAKò`ó ð`ð
 $)Ð˜DÓ(ð¸#ð ÀSð ÈXÐVYÉ]ó ð PUò@Øð@Ø(+ð@Ø69ð@ØHLð@ØYcó@ð	¨Cð 	¸Sð 	ÀTó 	ðW°3ó Wð°#ð À3ð ÐUXð Ð]aó ð&   Ð/¨tÐ4€J�Ó4Ø €M�3Ó Ø#€N�CÓ#Ø3Ð4°BÐ7Ð�cÓ7Ø%Ð˜Ó%ØcÐ˜ÓcØ€N�CÓØÐ�sÓàòF ð F¨Sð F¸#ð FÈ#ò Fó ðFð
˜sð  só ò"˜Cð "¨ð "°cð "È8ÐT\Ð^aÐ]bÐdgÐ]gÑThÑKið "Ðuxó "ð&#°Sð #Èð #ÐQTó #ðu 3ó uð
 Z_òIØðIØ),ðIØRVðIØcfóIð$ ¨ÀNÐ\bñ)dÐ�t˜C ˜H‘~ó dà1:ÀHÑ(MÐ�t˜C ˜H‘~ÓMðdØ&*ðdØ;?ðdØOSðdØX\Ð]bÐcfÐhkÐmpÐcpÑ]qÑXródð ðd¨tò dó ðdð PcØgkØ"ò=Øð=Ø%(ð=Ø7:ð=ØILð=à˜4  S ™>Ñ*ð=àDHð=à`dð=ð ð=ð 
ó	=ð @Ð7Kð @ÐPZó @ð ð( Tð (°ð (Àð (ÐHeò (ó ð(ð .2ò@Øð@Ø#&ð@Ø14ð@ØCFð@ØTWð@à˜4  S ™>Ñ*ð@à6@ó@ð @DòQØðQØ&)ðQØ4<¸T±NðQØPSðQàðQà$,¨T°#°s°(©^Ñ$<ðQàHRóQðB ]aØ-1ògØðgØ%-¨c¡]ðgØ=@ðgØLTÐUXÉMðgà˜4  S ™>Ñ*ðgà6@ógò`¨ð `Àó `ðX¨ð X°ó Xð ðO˜8ð O¨3ð O¸4ð OÈ$ð OÐSWò Oó ðOò(¸ð (Ètó (ð ILò"FØð"FØ$(¨¨s°C¨x©Ñ$9ð"Fà˜4  S ™>Ñ*ð"Fà@Eð"FàPZó"FðJ FJØMQòdØðdØ'*ðdØ5=¸c±]ðdà˜3‘-ðdà2:¸4ÀÀSÀ¹>Ñ2JðdàV`ódð JNØMQòmØðmØ+.ðmØ9AÀ#¹ðmà˜3‘-ðmà2:¸4ÀÀSÀ¹>Ñ2JðmàV`ómð ð:˜sð : tò :ó ð:ð ðB ð B¨¨d°5¸¸c¸±?Ñ.CÀSÐ.HÑ(Iò Bó ðBð6 GKØMQòUØðUØ(+ðUØ6>¸s±mðUà˜3‘-ðUà2:¸4ÀÀSÀ¹>Ñ2JðUà`jóUð dhò
FØð
FØ!$ð
FØ,/ð
FØ:=ð
FØHPÐQTÉð
Fà˜3‘-ð
Fà+3°D¸¸c¸±NÑ+Cð
FàS[Ð\_ÑS`ð
Fð 
ó
Fð 7;ÀTÐ\`òWØðWØ%(ðWà	�*Ñ	óWð 59À4ÐRVØò7Øð7Ø$'ð7ð 
ó7ñ&ÐY]ÐgkÐvzò  Cð °¸#±ð ð  @Hð  ILñ  @Mó ñPÈdÐ]aò PÐfjó Pð

= Sð 
=¨Só 
=ðW cð W°sð WÈó Wð¨cð Àð ÐQUó ð
 ]aòØðØ*5ðØAIÈ$ÈsÐTWÈxÉ.ÑAYðà	Ð$Ñ	%óðIÐ0Bñ IÈ5ð IÐUYô Ið ]bó 5Ð1Cñ 5ÐUYð 5Ðfjô 5òcÐ0Bñ cÈ8ÐTWÉ=ð cÐdhô cñMÐhló M°3¸±:ñ MÈCð MÐbeð MÐqtÐw{Ñq{ô MñRÐswó R¸cÀD¹jñ RÐWZð RÐmpð Rð  }Aô Rñ
kÐnró k¸¸t¹ñ kÐRUð kÐhkð kÐw{ô kð
 GKØMQòUØñUØ(+ðUØ6>¸s±mðUà˜3‘-ðUà2:¸4ÀÀSÀ¹>Ñ2JðUà`jôUð Z^ØW[ØAEòk¨3ñ k¸3ð kÈÐRUÉð kØ'/°¡}ðkØGOÐPSÁ}ðkà&.¨t°C¸°H©~Ñ&>ðkàT^ôkð BGØ-1ófØñfØ(+ñfØ:>ðfà˜4  S ™>Ñ*ðfà6:ôfð4 GKØMQòZØñZØ(+ðZØ6>¸s±mðZà˜3‘-ðZà2:¸4ÀÀSÀ¹>Ñ2JðZà`jôZð òK¨3ð K¸Sð KÈ(ÐSVÉ-ó Kó ðKð ØDFØDHòr¸cñ rØ-5°d¸4±jÑ-AðrØMQÐRWÐX[Ð]aÐXaÑRbÑMcóró ðrð ò,¸Sð ,È$ÈsÉ)ó ,ó ð,ð
 ð, sð ,¨só ,ó ð,ð* ð,¨ð ,°ó ,ó ð,ð( ð&˜sð & u¨T°%¸¸T¸	Ñ2BÑ-CÀSÐ-HÑ'Ió &ó ð&ðP ðU°ð U¸ó Uó ðUð ðH Sð H¨U°4¸±9¸c°>Ñ-Bó Hó ðHð@ PTØ>Bò&F¨#ñ &F¸ñ &FØ'.§}¢}°tÑ';ð&FØGKô&FðR @Dñ1ØQUÙ±1ó1Øñ1Ø)0¯ª¸Ñ)<ñ1àñ1à-0ð1à9=ô1ò$E°#ð EÈô Eð
)¨Sð )°Tô )ð-¨cð -°dô -ò;¸Cð ;È#ð ;Ðaeô ;ð QUóMØðMØ*2ñMØCFÈÁ:ðMØY]ôMð( ñ Xð °(¸2¸yÈ¹Ð;NÑ2Oó ó ðð =Aó
8Øñ
8Ø/2°T©zð
8ØEMÐPTÁ_ô
8ð !%€J�˜‘Ô$Ø#€Iˆx˜‰}Ô#Ø!%€K�˜#‘Ô%ð=¨|ð =Àô =ð2°,ñ 2ÐIZð 2Ð_cô 2ñ*O°Cñ OÀñ OÈsð OÐW[ô Oð ñH 8¨C¡=ð H°Tó Hó ðHð
 ñH h¨s¡mð H¸ó Hó ðHð ñ[ ¨#¡ð [°4ó [ó ð[ñ,¨#ð ,°%¸À¹ÀsÐ8JÑ2Kô ,ð ]aó ^°,ñ ^ÈHÐUXÉMð ^Ðeiô ^ð"
,¨H°]Ñ,Cô 
,ð¨h°}Ñ.Eð ÐJ_ô ð" [_Ø25òzØðzØ%(ðzØ4<¸S±MðzØTWñzàñzà*/ðzá:Fôzñx©<ð ¸Dô ð
 ñK¡\ð K°dó Kó ðKðØñÙ$0ðØ?GÈ¹}ðØX[ðÙ`vôð2Øð2Ø),ð2Ø;CÀC¹=ð2ØTWð2Ù\hô2ð ñ8 h¨s¡mñ 8¸sð 8Àsó 8ó ð8ð8 d¨3Ð0AÐ+AÑ&Bô 8ð	°|ð 	Èô 	ñS¸ð SÈ\ð SÐ^bô Sð"5°ð 5¸ô 5ðX°cð XÀ,ð XÐSWô Xð@"°cñ "À%ð "ÈDô "ð¸#ð À$ô ð!°#ð !¸$ô !ð \`ó /°#ñ /ÀÈ'Ï-Ê-ÑAXð /Ðdhô /ð%°#ð %¸$ô %ð°Cð ¸Dô ð NRó¨|ð È#ñ Ø3;¸G¿MºMÑ3JðØVZôð¨sñ ¸#ð À$ô ð Z^Ø@Dó5¸3ñ 5ÐRVñ 5Ø9=ð5ØIMô5ð6GØñGØ/6¯}ª}ðGØAEôGðT¸Lð TÐWZñ TÐadð TÐimô Tð.(U¨,ð (U¸4ô (UðTH+¸ð H+ÐSVð H+Ð[_ô H+ðT8 %ô 8ñ%°sð %¸uÀTÈ#ÁYÐPXÐY\ÑP]ÐE]Ñ?^ô %ð.^ \ð ^Àñ ^ÐV]×VcÒVcñ ^Ø&)ñ^Ø8<ð^ØAEô^ðGØ!ñGØ14ñGØ@CñGØLPðGà�s˜C�x‘.ñGà3;ðGà@DôGð Ø!ð Ø03ñ ØCFñ à/ñ àHLð àQYÐZ]ÑQ^ô ñDIØ ñIØ*-ðIØ6BñIà/ðIà48ôIð>3_Ø!ñ3_Ø04ñ3_ØCGñ3_à$(ð3_à7<ð3_àHLÈSÐRUÈXÉñ3_ð "ð3_ð '+ô3_ðj Ø!ð Ø+/ð Ø;?ÀÀSÀ¹>ð ØX]ñ à!ð à&*ô ð" @Eó(Ø!ð(Ø03ñ(ØCFð(ØRVÐWZÐ\_ÐW_ÑR`ð(à˜3‘-ñ(à8<ð(ñ 
2ô(ð,	pØ!ð	pØ03ñ	pØCFð	pØRVÐWZÐ\_ÐW_ÑR`ñ	pà#ñ	pà47ñ	pàJRð	pàW[ô	pð ¨lñ  ¸}ð  ÐQYÐZ^ÑQ_ô  ð.K°ñ KÑI]ð KØ-1°#°s°(©^ñKØOSñKà4<ðKàAEôKð*
V¨<ñ 
VÈ'Ï-Ê-ð 
VØ(0°©ð
VØ;CÀGÇLÂLÑ;Qô
Vñ0i¸ð 0iÀLð 0iÐ_bñ 0iØBFð0iÙK_ô0iðd
j¸cñ 
jÐT[×TaÒTað 
jÐfjô 
jðN°ñ NÀgÇmÂmð NÐX\ô Nð0tD°|ð tDÐRUð tDÐZ^ô tDñl_¨|ð _È#ð _ÐRVô _ð°ð ¸ô ð7Øñ7Ø19¸'¿-º-Ñ1Hð7ØMQô7õ$ð<d°ð d¸õ dð=¨sð =°xÀÑ7Mõ =ð
 OSØBFØEIØ]bØBFØJNØCHØ26ò*Øñ*Ø'/°¡}ð*ØHKð*à˜#‘ñ*à2:¸3±-ð*ð ˜C‘=ñ*ð 6>¸c±]ñ*ð ˜c‘]ñ	*ð 9AÀ¹ð	*ð W[ñ	*ð
 ˜3‘-ñ*ð
 3;¸3±-ð*ð ! ™ð*ð ;CÀ3¹-ñ*ð ñ*ð =Añ*ð #+¨3¡-ð*ð <Iõ*ðX ð4¨3ð 4°4¸¸S¸±>ó 4ó ñ4ð¨/ð ¸hÀtÈCÁyÑ>Qõ ð
 cð ¨cõ ð
 Ø9=ØDHò< #ñ <°3ñ <Ø!)Ñ*@Ñ!Að<ØMQÐRUÉYó<ó ò<r   rc  )r   zasyncio.Taskr	   Nr¤  )F)r¾  )r$   )r$   r]   )r   )r   r]   )r!   re  rc  )Nr`   (  rM  rÄ  rP   rA  rÅ   Úloggingrv  rC   rE  rA  ÚsocketrÎ   rG   rè   rA   rÅ  rê  r©   r@  Úabcr   r   rV  r   Úutilsr   Úagent.proxy_bypassr   r   r÷   Ú	getLoggerrJ  r   r   r¦  Ú	frozensetr›   r´  rØ  rœ   Ú_TELEGRAM_VOICE_EXTSrF   rN  rZ   rþ  rÈ  Ú!_HISTORY_MEDIA_LOOKUP_MAX_WORKERSÚBoundedSemaphorerÁ  rd   r‚  rÍ   ri   r€  r‚  r†   r~   rƒ   rˆ   r€   r—   r  rž   r¯   r´   r½   rÀ   r×   ró   r0  râ  rv  r   r
  r  r  rý   r!  r%  Údataclassesr&  r'  Úpathlibr)  Útypingr*  r+  r,  r-  r.  r/  r0  r1  r2  r-   ÚinsertÚ__file__rs  r›  Úgateway.configr3  r4  Úgateway.platforms.helpersr5  Ú$gateway.platforms.base_exec_approvalr6  r7  r8  r9  Úgateway.platforms.eventr:  r;  r<  r�  r=  Úgateway.sessionr>  r?  Úgateway.session_transcriptr@  Úhermes_constantsrA  rB  rC  rÇ  rD  rF  rQ  r[  r_  Ú*GATEWAY_SECRET_CAPTURE_UNSUPPORTED_MESSAGEra  rc  rs  rz  r®  r€  rƒ  rŠ  rl  r•  r¬  rÀ  Úcleanup_image_cacher¶  r»  rÂ  rÇ  rá  ræ  r¥  rô  rø  Úcleanup_audio_cacherù  rû  rþ  r  r  Úcleanup_video_cacher¥  r  r  r
  r  r‹  Úcleanup_document_cacheÚget_screenshot_cache_dirÚcleanup_screenshot_cacher�  r  r,  ÚMEDIA_DELIVERY_ALLOW_DIRS_ENVÚMEDIA_DELIVERY_TRUST_RECENT_ENVÚ'MEDIA_DELIVERY_TRUST_RECENT_SECONDS_ENVÚMEDIA_DELIVERY_STRICT_ENVr&  rQ  r]  rl  rm  r  r§   rh  r)  r%  r2  r7  r;  rE  rS  r[  r^  ra  rn  ru  r|  r  rx  r†  r”  r�  r¦  r£  r§  r®  rµ  rº  r¿  rÔ  rÜ  rÕ  rà  r¾  rê  rç  r§  Ú_TEXT_INJECT_EXTENSIONSr¤  r?  rO  rË  r²   Ú_MEDIA_EXT_ALTERNATIONÚ_MEDIA_CJK_TERMINATORSrÖ  ru  ro  rV  rØ  rY  r^  r`  ra  ri  rm  rq  rk  r’  rx  ry  r{  rf  r‚  r…  rŽ  r�  r’  r€  Úitemsrš  rž  r°  r²  r´  rÁ  rÃ  rÉ  ÚPatternrÏ  rÑ  rÛ  rD  ÚSEND_ERROR_KINDSr  r  r¸  rò  rù  r  r  r  r  r9  r  ri  rH  rS  rU  rZ  rø  rc  )rª  r˜   r¨  s   000r   ú<module>r”     sÈ  ðÚ `ã Û Û Û Û Û Û 	Û Û 	Û Û Û 
Û Û Û Û Û ß #Ý !å %ß aà	ˆ×	Ñ	˜8Ó	$€ôXð  ,¸ÈlØ °|ñEÐ ñ Ð)Ó*€áÒIÓJ€ÙÒBÓC€á"+¨V°VÐ,<Ó"=Ð Ù  &¨'Ð!2Ó3Ð ð 8=ÈRØ6:ò! ð !°ð !Àsð !Ø'3ð!Ø?Kó!ðD +/Ð 'à(+Ð %ð %&Ð !Ø"< )×"<Ñ"<Ð=^Ó"_Ð ð$ ó $ð &/°
Ð$;ó ð &*Ð "Ø%(Ð "ò¸SÀ4¹Zð ÐSWÐZ^ÑS^ó ð:U¨°©ó Uð
 D¨4¡Kð °Dó ð. c¨D¡jó .ò2E˜cð E¨lð EÀcó Eò
I¨cð 
I¸Tð 
IÈdó 
Ið¨Có ð&+�ð +˜ó +ð˜#ð  sð °só ð7 #ð 7¨cð 7°có 7ð
 ð ¨ó ð  C¨$¡Jó ð*. c¨D°©I¡o¸¸cÀ3¸h¹Ñ&GÈ#ÈcÉ(Ñ&RÐUYÑ&Yð .Ð^bó .ð $(ñJàHLØ!òJØ˜D‘jðJà˜˜S™	‘/ E¨#¨s¨(¡OÑ3°c¸#±hÑ>ÀÑEðJð �d‘
ðJð '*¨D¡jóJð8¨ó ð  C¨$¡Jð  °4ó  ð8˜#ð 8 $ó 8ð6˜4ó 6ð	]¨¨d©
ð 	]°u¸TÀ4¸ZÑ7Hó 	]òI¨3ð IÀÀdÁ
ð IÐVZó Ió ß (Ý ß ^× ^Õ ^à ‡�‡��‘3‘t˜H“~×-Ñ-Ó/×7Ñ7¸Ñ:Ó;Ô <ç 3Ý 7÷có cç PÑ PÝ ?ß <Ý :ß UÑ UáÝ)à
÷ð ó ðð ÷ð ó ðñKÐ]aò K¨¨d©
ð KÀð KÐWZð KÐfiÐlpÑfpó Kð =Añ:ØRVò:¸#¸c¹(ð :ÐQTÐW[ÑQ[ð :Ø69ð:ØLOð:Ø[_ó:ðbð +ðið ð
Q¨ð Q°ó QòI˜#ð I¨ð I°Só Iò*lñ ! °Ó?€ð #4Ð ð[ Só [ð %,ÈòfØ
ðfØ!ðfØ8@À¹ðfØQUófð¸sð Àuó ð*˜sð °3ð ÀSð ÐTWó ñ* ,@ØÐ °ó,?Ñ (Ð Ð(ðW˜Eð W dó Wð ð ¨sð ¸ð ÀEð Ècó òF ð F¨Sð F¸có FòF¨Uð F¸ð FÈ#ó Fð
 Sð ¨sð ¸Sð ÐQTð Ð^að Ø58ðØ=@óò<< Cð <¨cð <ÀSð <ÐQTó <ð	 $ð 	°sð 	¸só 	ñ ! °Ó>€Ù+?ØÐ ¨}ó,>Ñ (Ð Ð(ò_ ð _¨Sð _¸có _òF¨Uð F¸ð FÈ#ó Fò
< Cð <¨cð <ÀSð <ÐQTó <ñ ! °Ó?€Ù+?ØÐ °ó,?Ñ (Ð Ð(ð Ð!2¸\ØÐ(9ñ;Ð ò
H ð H¨Sð H¸có HòF¨Uð F¸ð FÈ#ó Fñ $Ð$5Ð7GÓHÐ Ù%Ð&9Ð;PÓQÐ Ù1EØÐ$Ð&7Ð9Ió2KÑ .Ð Ð.á5IØÐ(Ð*=Ð?Tó6VÑ 2Ð Ð2ð
 '¸?Ø&Ð>PØ0ñ2Ð ñ Ó €Ù&Ó(€Ø 9Ð Ø"CÐ Ø*MÐ 'ð ;Ð à YÐ à�_ oÐ7IÐK_ðJñ_ð !^ô _ðJñ
 IÐ*GÔHñJÐ ð 03Ð ,ð#[Ð ð(Ð $ðC˜ð C  c¨3 h¡ó Cð ˆK˜ m°]ÐD[ØÐ4°b·g±g·l²lÀ6ÐK^Ó6_Ø  "§'¡'§,¢,¨wÐ8HÓ"IØ‡G�G‡L‚L�Ð.Ó/°¸Lð Ð!ð\ñ %2°*Ó$=ð\ñ ANÈkÓ@Zñ\Ð ð
g˜d 4™jó 
gð�t˜D‘zó ðT  T¡
ó Tð_�dó _ð
˜D ™Jó ð	 $ t¡*ó 	ð: t¨D¡zó :ðX¨ó Xðc  T¡
ó cð' d¨4¡jó 'ð 16Àeò ˜ð ¨ð ¸tð ÐPXÐY]ÑP^ó ð¨ð °ó ð ¨ð ¸uð Èó ð˜$ð  dð ¨tó ò'�ð '˜cð '¨3ó 'ð T¨%°°d°
Ñ*;Ñ%<ó ò6°ð ¸TÀ#¹Yó ò> %€ðA˜Dó Aðn 4ó nð
°#ð ¸Sð ÀTÈ$ÁZó òF°cð FÀ4ÈÁ:ó Fð	 T¨%°°d°
Ñ*;Ñ%<ó 	ðJ¨Tð JÀð JÈSð JÐUYó Jò&°dð &Èð &ÐV^Ð_cÑVdó &òR$ sð $¸ð $ÀhÈsÁmó $ðR �B—J’JÐAÓBÐ ð7˜ð 7 ó 7ð >Bò°Cð Àð Ø&.¨t°D©zÑ&:ðØFNÈsÁmóð* ð ¨ó ð
CØ
Ðð
CØ$ oð
CØ7=¸|ð
CØMSÐU_ð
Cà
ˆLð
Cà!Ð#5ð
Cà7=Ñ?Pð
Cñ Ñð
Cñ "(Ñ);ð
Cñ >EÑFXð
Cñ ˆLð	
Cñ ! ,ð	
Cñ 17Ñ8Ið	
Cñ
 Ñ ð
Cñ
 #)Ñ*Dð
Cñ Ñ+ð
Cñ ÑVð
Cñ QÙXØ °\ó
CÐ óÐ ñ ¡<¹É|Ùò"Ð ñ&	(Ð �U˜3 ˜8‘_ô 	ñ Ÿš¡&Ò)UÐATÔ)UÐ[^ÑhlÕ"mÓnÐ ñ2 aÐ à!�r—z’zñ>à@VñWñZ ñ ð #9ñ9ñ <hñhð ‡M‚MóÐ ð8 (˜RŸZšZñð 5ñ5ñ8ñð
 ‡M‚MóÐ ñ ¨ñ  ±Zð  ÀHÈUÐSVÐX[ÐS[É_ÑD]ô  ðF5 3ð 5¨3ô 5ð	@¨Cð 	@°Dô 	@ñY ð Y¨ô Yñ Ð ñ( 3ð (¨3ô (ñ	 ð 	¨ô 	ñ ð ¨ô ñ6¨ô 6ñ #ð ¨$ô ð �"—*’*Ñ1°2·9²9Ó=€Ø�"—*’*™\Ó*€ña˜ð a ô añ
�sñ  4ð ¨Cô ñ˜ñ  Dð ¨Sô ñ"C cð C¨cô Cð Eñ °Sð ¸Sô ð N°ñ NÀð NÈô Nð ÷	Lò 	Ló ð	Lð  +Ð,AÐCaÐb÷ .ð  .àÙ˜eŸkšk›mÓ,ò .ñ 	ˆˆTð 	ˆ#�Ið .Øô .€ˆd�3˜�8‰nô .ñB ñ B°ð B¸ô Bð 79È2Ø48ój˜Eñ j°ñ jÀSñ jØ$,¨S¡MðjØ=EÀkÑ=RôjðH ØØ"&óØ
ñð ñð ñ	ð
 ˜3‘-ðð ˆkÑôð" ÷	ò 	ó ð	ñ5˜8 C™=ñ 5¨x¸©}ð 5Àô 5ð
 ÷ò ó ñð €B‡J‚JÑFÈÏÊÓVØ€B‡J‚JÑOÐQS×Q^ÒQ^Ó_Ø€B‡J‚JÑ:¸B¿MºMÓJðDLÑ # U¨2¯:ª:°c©?¸CÐ+?Ñ%@ô Lö$ð  ÷9ò 9ó ñ9ð( ÷%ò %ó ñ%ñ* $(Ñ  ñ Û`óbÑ ñ
 $7Ñ  ñ!Ñ ó
#�X™mÑ,ñ #Àð #Ècõ #ñ+�#ñ + ð +¨õ +ñ Ò\Ð]Ùò 2ð 3ñ ò ?ð @ñ ÒiÐjÙÒdÐeÙÒTÐUðJWÑ ˜˜u S¨(°C°5¸$°;Ñ*?Ð%?Ñ@À#ÐEÑFô Wóa˜X¡mÑ4ñ aÀ#ð aÈsõ aóB ©-Ñ!8ñ BÈSð BÐZ^õ Bö!�Sõ !ð& LQô,*°$°s¸LÐ7HÑ2Ið ,*ÐX[ð ,*Ø'3ñ,*ØDHð,*ØUYõ,*ñb@Ñ ð
 ˜<˜.¨)°H¸UÀ3ÑHXÐCXÑ=YÑ4ZÑ*[Ð[Ñ\�ó
¨ñ 
¸3ñ 
È3ÐQUÉ:ð 
ÐadÐgkÑakõ 
ð BFóØñØ$'ñØ47¸$±JðØJNÈsÉ)ÐVZÑJZõñ0T cð T¨e°H¸S±MÀ3Ð4FÑ.Gõ Tñ�Cð ˜sñ ¨X°b¸#°gÑ->ð À3õ ð 6Ñ öF-˜#ö F-ùôy
 .s   á)k8