§
    øžyj{v ã                  óL  — U d Z ddlm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 ddlmZmZmZmZmZmZ daded<   dad	ed
<   dad	ed<   dad	ed<   dad	ed<   dad	ed<   dad	ed<   ead	ed<   dad	ed<   da ded<   dZ!da"d	ed<    e	j#        ¦   «         Z$ G d„ d¦  «        Z%d^d„Z&d_d„Z'd`d„Z(ddl)m*Z*m+Z+  e*d ¦  «         dd!l,m-Z- dd"l.m/Z/m0Z0m1Z1m2Z2m3Z3m4Z4m5Z5m6Z6m7Z7  ej8        d#¦  «        Z9 ej:        d$¦  «        Z;d%d&gZ<d'Z=d(Z>d)Z?d*Z@d+ZAd,ZB eCh d-£¦  «        ZD eCh d.£¦  «        ZEdad1„ZFd2ZGd`d3„ZHd4ZIdbd6„ZJdcd8„ZKddd;„ZLded?„ZMdfdA„ZNdgdC„ZOdhdE„ZP G dF„ dG¦  «        ZQ G dH„ dIe/¦  «        ZRdidL„ZSd`dM„ZTdidN„ZUdjdP„ZVdkdR„ZW ej:        dS¦  «        ZXddddTœdld\„ZYdkd]„ZZdS )muq  
Google Chat platform adapter.

Uses authenticated HTTP callbacks or Google Cloud Pub/Sub for inbound
events and the Google Chat REST API for outbound messages. Pub/Sub remains
available for no-public-URL deployments.

Concurrency model
-----------------
The Pub/Sub SubscriberClient invokes its message callback in a background
thread (managed by the client's internal executor). The adapter's
``handle_message`` coroutine must run on the asyncio event loop, so the
callback uses ``asyncio.run_coroutine_threadsafe`` with
``add_done_callback`` (never ``.result()`` â€” that would block the callback
thread and saturate the Pub/Sub executor under load).

All outbound Chat REST calls go through ``asyncio.to_thread`` because the
googleapiclient is synchronous. This keeps the event loop responsive.

Pub/Sub delivery diagram::

    Pub/Sub stream   ->  callback thread        ->  asyncio loop
    (streaming_pull)     (_on_pubsub_message)       (handle_message)
         |                       |                        |
         |   at-least-once       |  parse + dedup         |  agent work
         |   delivery            |  _submit_on_loop       |  send() response
         |                       |  message.ack()         |
         v                       v                        v

Event type routing
------------------
Inbound envelope carries ``type`` in [MESSAGE, ADDED_TO_SPACE, REMOVED_FROM_SPACE,
CARD_CLICKED]. Only MESSAGE dispatches to the agent. ADDED_TO_SPACE caches the
bot's resource name (belt-and-suspenders on top of eager resolution in connect()).
CARD_CLICKED is ACK'd only in v1 (follow-up PR implements interactivity).
é    )ÚannotationsN)ÚPath)ÚAnyÚCallableÚDictÚListÚOptionalÚTupleFÚboolÚGOOGLE_CHAT_AVAILABLEr   Úhttplib2Ú	pubsub_v1Úgax_exceptionsÚservice_accountÚAuthorizedHttpÚbuild_serviceÚ	HttpErrorÚMediaFileUploadÚ_google_modules_loadedé,  Ú_google_id_token_requestc                  ó$   — e Zd Zefdd„Zddd„ZdS )Ú_CachedGoogleAuthRequestÚrequestr   Úttl_secondsÚintÚreturnÚNonec                ó`   — || _         || _        t          j        ¦   «         | _        i | _        d S ©N)Ú_requestÚ_ttl_secondsÚ	threadingÚLockÚ_lockÚ_cache)Úselfr   r   s      úK/home/ragecks/.hermes/hermes-agent/plugins/platforms/google_chat/adapter.pyÚ__init__z!_CachedGoogleAuthRequest.__init__R   s+   € ØˆŒØ'ˆÔÝ”^Ñ%Ô%ˆŒ
Ø@BˆŒˆˆó    ÚGETÚurlÚstrÚmethodÚkwargsc                óö  — |                      ¦   «         |f}|d         dk    r | j        d||dœ|¤ŽS t          j        ¦   «         }| j        5  | j                             |¦  «        }|r |d         |k    r|d         cd d d ¦  «         S d d d ¦  «         n# 1 swxY w Y    | j        d||dœ|¤Ž}t          |dd ¦  «        dk    r3| j        5  || j        z   |f| j        |<   d d d ¦  «         n# 1 swxY w Y   |S )Nr   r+   )r,   r.   é   ÚstatuséÈ   © )	Úupperr!   ÚtimeÚ	monotonicr%   r&   ÚgetÚgetattrr"   )r'   r,   r.   r/   Ú	cache_keyÚnowÚcachedÚresponses           r(   Ú__call__z!_CachedGoogleAuthRequest.__call__X   sÅ  € Ø—\’\‘^”^ SÐ)ˆ	Ø�QŒ<˜5Ò Ð Ø �4”=ÐB S°ÐBÐB¸6ÐBÐBÐBåŒnÑÔˆØŒZð 	!ð 	!Ø”[—_’_ YÑ/Ô/ˆFØð !˜& œ) cš/˜/Ø˜a”yð	!ð 	!ð 	!ð 	!ñ 	!ô 	!ð 	!ð 	!ð 	!ð 	!ð 	!ñ 	!ô 	!ð 	!ð 	!ð 	!ð 	!ð 	!ð 	!øøøð 	!ð 	!ð 	!ð 	!ð
 !�4”=ÐB S°ÐBÐB¸6ÐBÐBˆÝ�8˜X tÑ,Ô,°Ò3Ð3Ø”ð Mð MØ*-°Ô0AÑ*AÀ8Ð)L�”˜IÑ&ðMð Mð Mñ Mô Mð Mð Mð Mð Mð Mð Møøøð Mð Mð Mð Màˆs$   Á0BÂBÂBÃC.Ã.C2Ã5C2N)r   r   r   r   r   r   )r+   )r,   r-   r.   r-   r/   r   r   r   )Ú__name__Ú
__module__Ú__qualname__Ú"_GOOGLE_ID_TOKEN_CERTS_TTL_SECONDSr)   r>   r4   r*   r(   r   r   Q   sN   € € € € € Ø8Zð Cð Cð Cð Cð Cðð ð ð ð ð ð r*   r   r   c                 óö   — t           5  t          €K	 ddlm}  n"# t          $ r}t          d¦  «        |‚d }~ww xY wt          |                      ¦   «         ¦  «        at          cd d d ¦  «         S # 1 swxY w Y   d S )Nr   )Úrequestsú6google-auth is required for Google Chat HTTP callbacks)Ú_google_id_token_request_lockr   Úgoogle.auth.transportrD   ÚImportErrorÚRuntimeErrorr   ÚRequest)Úgoogle_requestsÚexcs     r(   Ú_get_google_id_token_requestrM   j   sß   € å	&ð (ð (Ý#Ð+ðfØMÐMÐMÐMÐMÐMÐMøÝð fð fð fÝ"Ð#[Ñ\Ô\ÐbeÐeøøøøðføøøå'?À×@WÒ@WÑ@YÔ@YÑ'ZÔ'ZÐ$Ý'ð(ð (ð (ð (ñ (ô (ð (ð (ð (ð (ð (ð (øøøð (ð (ð (ð (ð (ð (s+   ˆA.‘—A.˜
7¢2²7·*A.Á.A2Á5A2Útokenr-   ÚaudienceúDict[str, Any]c                óœ   — 	 ddl m} n"# t          $ r}t          d¦  «        |‚d }~ww xY w|                     | t          ¦   «         |¦  «        S )Nr   )Úid_tokenrE   )Úgoogle.oauth2rR   rH   rI   Úverify_oauth2_tokenrM   )rN   rO   rR   rL   s       r(   Ú_verify_google_id_tokenrU   v   s{   € ð^Ø*Ð*Ð*Ð*Ð*Ð*Ð*øÝð ^ð ^ð ^ÝÐSÑTÔTÐZ]Ð]øøøøð^øøøð ×'Ò'ØÝ$Ñ&Ô&Øñô ð s   ‚	 ‰
(“#£(c                 óÐ   — t           rt          S da 	 ddl} ddlm} ddlm} ddlm} ddl	m
} ddlm} dd	lm} dd
lm} n# t"          $ r daY dS w xY w| a|a|a|a|a
|a|a|adadS )uq  Lazily import the heavy google-cloud + googleapiclient stack.

    Idempotent. Returns True if the optional deps are installed and
    were successfully imported, False otherwise. On success, mutates
    the module globals so existing code using ``pubsub_v1``,
    ``service_account``, ``HttpError``, etc. transparently uses the
    real classes.

    Why deferred: the import chain pulls in google.cloud.pubsub_v1,
    googleapiclient, grpc, and friends â€” about 33MB RSS and 110ms wall
    on a fresh interpreter. Plugin discovery imports this module on
    every CLI invocation, even ones that never touch a gateway.
    Tr   N)r   )Ú
exceptions)r   )r   )Úbuild)r   )r   F)r   r   r   Úgoogle.cloudr   Úgoogle.api_corerW   rS   r   Úgoogle_auth_httplib2r   Úgoogleapiclient.discoveryrX   Úgoogleapiclient.errorsr   Úgoogleapiclient.httpr   rH   r   r   )Ú	_httplib2Ú
_pubsub_v1Ú_gax_exceptionsÚ_service_accountÚ_AuthorizedHttpÚ_build_serviceÚ
_HttpErrorÚ_MediaFileUploads           r(   Ú_load_google_modulesrg   ƒ   s  € õ" ð %Ý$Ð$Ø!ÐðØ$Ð$Ð$Ð$Ø8Ð8Ð8Ð8Ð8Ð8ØAÐAÐAÐAÐAÐAØEÐEÐEÐEÐEÐEØJÐJÐJÐJÐJÐJØEÐEÐEÐEÐEÐEØBÐBÐBÐBÐBÐBØLÐLÐLÐLÐLÐLÐLøÝð ð ð Ø %ÐØˆuˆuðøøøð €HØ€IØ$€NØ&€OØ$€NØ"€MØ€IØ&€OØ ÐØˆ4s   ’.A ÁAÁA)ÚPlatformÚPlatformConfigÚgoogle_chat)ÚMessageDeduplicator)	ÚBasePlatformAdapterÚMessageEventÚMessageTypeÚProcessingOutcomeÚ
SendResultÚcache_audio_from_bytesÚcache_document_from_bytesÚcache_image_from_bytesÚcache_video_from_byteszgateway.platforms.google_chatz:^projects/(?P<project>[^/]+)/subscriptions/(?P<sub>[^/]+)$z(https://www.googleapis.com/auth/chat.botz&https://www.googleapis.com/auth/pubsubé   é   é   g      ð?g       @g333333Ó?>   éô  éö  é÷  éø  é­  >	   ÚtextÚimageÚbuttonsÚdividerÚ	selectionÚbutton_listÚdecorated_textÚtext_paragraphÚselection_inputrL   ÚBaseExceptionc                ó  — t          | dd¦  «        }t          |dd¦  «        }t          |t          ¦  «        r	|t          v S t	          | ¦  «                             ¦   «         }d|v sd|v rdS d|v rd|v sd	|v sd
|v rdS d|v sd|v rdS dS )u¹  Classify outbound API errors as transient (retryable) vs permanent.

    Retries are applied to:
      - HTTP 429 (rate-limited)
      - HTTP 5xx (server errors)
      - Network/transport failures (timeout, connection reset, DNS)

    Authentication errors (401/403), client errors (4xx other than 429),
    and well-formed non-retryable failures are NOT retried â€” those
    indicate a misconfiguration or revoked token, not a hiccup.
    ÚrespNr2   Útimeoutz	timed outTÚ
connectionÚresetÚrefusedÚabortedzbroken pipezremote disconnectedF)r9   Ú
isinstancer   Ú_RETRYABLE_HTTP_STATUSESr-   Úlower)rL   rˆ   r2   r}   s       r(   Ú_is_retryable_errorr‘     sÂ   € õ �3˜ Ñ%Ô%€DÝ�T˜8 TÑ*Ô*€FÝ�&�#ÑÔð 2ØÕ1Ð1Ð1õ ˆs‰8Œ8�>Š>ÑÔ€DØ�DÐÐ˜K¨4Ð/Ð/ØˆtØ�tÐÐ ¨D  °IÀÐ4EÐ4EÈÐVZÐIZÐIZØˆtØ˜ÐÐÐ 5¸Ð =Ð =ØˆtØˆ5r*   z
<consumed>c                 ó   — t          ¦   «         S )a@  Check if Google Chat optional dependencies are installed.

    Triggers the lazy import of the google-cloud + googleapiclient stack
    on first call. Subsequent calls hit the cached result. This is the
    canonical "are the deps available" probe used by the plugin registry
    and the adapter's own startup gate.
    )rg   r4   r*   r(   Úcheck_google_chat_requirementsr“   )  s   € õ  Ñ!Ô!Ð!r*   )zgoogleapis.comzchat.google.comzdrive.google.comzdocs.google.comzlh3.googleusercontent.comzlh4.googleusercontent.comzlh5.googleusercontent.comzlh6.googleusercontent.comr,   c                óä   ‡— 	 ddl m}  || ¦  «        }n# t          $ r Y dS w xY w|j        dk    rdS |j        pd                     ¦   «         Š‰sdS t          ˆfd„t          D ¦   «         ¦  «        S )zAReturn True iff *url* is https and targets a Google-owned domain.r   )ÚurlparseFÚhttpsÚ c              3  óT   •K  — | ]"}‰|k    p‰                      d |z   ¦  «        V — Œ#dS )ú.N)Úendswith)Ú.0ÚhÚhosts     €r(   ú	<genexpr>z(_is_google_owned_host.<locals>.<genexpr>R  s<   øè è € ÐVÐV°qˆt�qŠyÐ2˜DŸMšM¨#°©'Ñ2Ô2ÐVÐVÐVÐVÐVÐVr*   )Úurllib.parser•   Ú	ExceptionÚschemeÚhostnamer�   ÚanyÚ_TRUSTED_ATTACHMENT_HOSTS)r,   r•   Úparsedr�   s      @r(   Ú_is_google_owned_hostr¦   E  s¦   ø€ ðØ)Ð)Ð)Ð)Ð)Ð)à�˜#‘”ˆˆøÝð ð ð Øˆuˆuðøøøà„}˜ÒÐØˆuØŒOÐ!˜r×(Ò(Ñ*Ô*€DØð ØˆuÝÐVÐVÐVÐVÕ<UÐVÑVÔVÑVÔVÐVs   ƒ •
#¢#r}   c                ó’   — | s| S t          j        dd| ¦  «        } t          j        dd| ¦  «        } t          j        dd| ¦  «        } | S )a  Sanitize subscription paths and email-like tokens from an error string.

    Covers project IDs leaking via Pub/Sub exception messages, plus SA-ish
    email addresses. agent/redact.py handles log-level redaction elsewhere;
    this helper is for user-facing error messages.
    z&projects/[^/\s]+/subscriptions/[^/\s]+z,projects/<redacted>/subscriptions/<redacted>zprojects/[^/\s]+/topics/[^/\s]+z%projects/<redacted>/topics/<redacted>z9[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.iam\.gserviceaccount\.comz&<sa>@<project>.iam.gserviceaccount.com)ÚreÚsub)r}   s    r(   Ú_redact_sensitiverª   U  sj   € ð ð ØˆÝŒ6Ø1Ø6Øñô €Dõ
 Œ6Ø*Ø/Øñô €Dõ
 Œ6ØDØ0Øñô €Dð
 €Kr*   Úmimern   c                óü   — | st           j        S |                      d¦  «        rt           j        S |                      d¦  «        rt           j        S |                      d¦  «        rt           j        S t           j        S )z—Map a MIME string to a hermes MessageType.

    Anything not image/audio/video falls through to DOCUMENT so the agent
    still receives the file.
    úimage/úaudio/úvideo/)rn   ÚDOCUMENTÚ
startswithÚPHOTOÚAUDIOÚVIDEO)r«   s    r(   Ú_mime_for_message_typerµ   p  su   € ð ð $ÝÔ#Ð#Ø‡‚�xÑ Ô ð !ÝÔ Ð Ø‡‚�xÑ Ô ð !ÝÔ Ð Ø‡‚�xÑ Ô ð !ÝÔ Ð ÝÔÐr*   ÚmappingÚkeyÚcontextc                óÎ   — |                       |¦  «        }|€t          |› d|› d�¦  «        ‚t          |¦  «                             ¦   «         }|st          |› d|› d�¦  «        ‚|S )Nr™   z is required)r8   Ú
ValueErrorr-   Ústrip)r¶   r·   r¸   Úvalues       r(   Ú_required_strr½   �  sz   € Ø�KŠK˜ÑÔ€EØ€}Ý˜GÐ7Ð7 cÐ7Ð7Ð7Ñ8Ô8Ð8Ý�‰JŒJ×ÒÑÔ€EØð 9Ý˜GÐ7Ð7 cÐ7Ð7Ð7Ñ8Ô8Ð8Ø€Lr*   Úbuttonc                ó&  — t          | dd¦  «        }t          | dd¦  «        }|                      d¦  «        pi }t          |t          ¦  «        st	          d¦  «        ‚d„ t          |                     ¦   «         ¦  «        D ¦   «         }|d||dœidœS )	Nr}   r¾   ÚactionÚ
parametersz#button.parameters must be an objectc                óR   — g | ]$\  }}t          |¦  «        t          |¦  «        d œ‘Œ%S ))r·   r¼   ©r-   )r›   r·   r¼   s      r(   ú
<listcomp>z#_button_to_chat.<locals>.<listcomp>‘  s@   € ð ð ð áˆC�õ �C‘”¥3 u¡:¤:Ð.Ð.ðð ð r*   )ÚfunctionrÁ   )r}   ÚonClick)r½   r8   rŽ   Údictrº   ÚsortedÚitems)r¾   r}   rÀ   Ú
raw_paramsrÁ   s        r(   Ú_button_to_chatrË   ‹  s²   € Ý˜ ¨Ñ2Ô2€DÝ˜6 8¨XÑ6Ô6€FØ—’˜LÑ)Ô)Ð/¨R€JÝ�j¥$Ñ'Ô'ð @ÝÐ>Ñ?Ô?Ð?ðð å  ×!1Ò!1Ñ!3Ô!3Ñ4Ô4ðñ ô €Jð
 Ø¨6ÀÐLÐLÐMðð ð r*   Úwidgetc                óh  — t          | t          ¦  «        st          d¦  «        ‚t          |                      d¦  «        pd¦  «                             ¦   «         }|t          vrt          d|pd› �¦  «        ‚|dv r-ddt                               t          | dd	¦  «        ¦  «        iiS |d
k    r«t                               t          | dd	¦  «        ¦  «        t          |                      dd¦  «        ¦  «        dœ}|                      d¦  «        rt          | d         ¦  «        |d<   |                      d¦  «        rt          | d         ¦  «        |d<   d|iS |dk    rdi iS |dk    rDdt          | dd	¦  «        i}|                      d¦  «        rt          | d         ¦  «        |d<   d|iS |dv rM|                      d¦  «        pg }t          |t          ¦  «        r|st          d¦  «        ‚ddd„ |D ¦   «         iiS |dv �rt          | dd	¦  «        }|                      d ¦  «        pg }t          |t          ¦  «        r|st          d!¦  «        ‚g }|D ]~}t          |t          ¦  «        st          d"¦  «        ‚|                     t          |dd#¦  «        t          |d$d#¦  «        t          |                     d%d&¦  «        ¦  «        d'œ¦  «         Œd(|t          |                      d)¦  «        p|¦  «        t          |                      d*¦  «        pd+¦  «        |d,œiS t          d|› �¦  «        ‚)-Nzcard widgets must be objectsÚtyper—   zunsupported widget type: z	<missing>>   r}   r„   ÚtextParagraphr}   rÌ   rƒ   Ú	wrap_textT)r}   ÚwrapTextÚ	top_labelÚtopLabelÚbottom_labelÚbottomLabelÚdecoratedTextr€   r~   ÚimageUrlÚ	image_urlÚalt_textÚaltText>   r   r‚   r   z*button widgets require at least one buttonÚ
buttonListc                ó,   — g | ]}t          |¦  «        ‘ŒS r4   )rË   )r›   Úbtns     r(   rÄ   z#_widget_to_chat.<locals>.<listcomp>Á  s    € Ð*WÐ*WÐ*WÀC­?¸3Ñ+?Ô+?Ð*WÐ*WÐ*Wr*   >   r�   r…   ÚnamerÉ   z+selection widgets require at least one itemzselection items must be objectszselection itemr¼   ÚselectedF)r}   r¼   rß   ÚselectionInputÚlabelÚselection_typeÚ	CHECK_BOX)rÞ   rá   rÎ   rÉ   )rŽ   rÇ   rº   r-   r8   r»   Ú_CARD_WIDGET_TYPESÚGoogleChatAdapterÚformat_messager½   r   ÚlistÚappend)	rÌ   Úwidget_typeÚ	decoratedr~   Úraw_buttonsrÞ   Ú	raw_itemsrÉ   Úitems	            r(   Ú_widget_to_chatrî   ›  s¬  € Ý�f�dÑ#Ô#ð 9ÝÐ7Ñ8Ô8Ð8Ý�f—j’j Ñ(Ô(Ð.¨BÑ/Ô/×5Ò5Ñ7Ô7€KØÕ,Ð,Ð,ÝÐQ°[Ð5OÀKÐQÐQÑRÔRÐRàÐ0Ð0Ð0àØÕ)×8Ò8Ý! &¨&°(Ñ;Ô;ñô ðð
ð 	
ð Ð&Ò&Ð&å%×4Ò4Ý˜f f¨hÑ7Ô7ñô õ ˜VŸZšZ¨°TÑ:Ô:Ñ;Ô;ð	%
ð %
ˆ	ð �:Š:�kÑ"Ô"ð 	=Ý$'¨¨{Ô(;Ñ$<Ô$<ˆI�jÑ!Ø�:Š:�nÑ%Ô%ð 	CÝ'*¨6°.Ô+AÑ'BÔ'BˆI�mÑ$Ø Ð+Ð+Ø�iÒÐØ˜2ˆÐØ�gÒÐØ�]¨6°;ÀÑIÔIÐJˆØ�:Š:�jÑ!Ô!ð 	7Ý" 6¨*Ô#5Ñ6Ô6ˆE�)ÑØ˜ÐÐØÐ0Ð0Ð0Ø—j’j Ñ+Ô+Ð1¨rˆÝ˜+¥tÑ,Ô,ð 	K°Kð 	KÝÐIÑJÔJÐJØ˜yÐ*WÐ*WÈ;Ð*WÑ*WÔ*WÐXÐYÐYØÐ6Ð6Ñ6Ý˜V V¨XÑ6Ô6ˆØ—J’J˜wÑ'Ô'Ð-¨2ˆ	Ý˜)¥TÑ*Ô*ð 	L°)ð 	LÝÐJÑKÔKÐKØ&(ˆØð 	ð 	ˆDÝ˜d¥DÑ)Ô)ð DÝ Ð!BÑCÔCÐCØ�LŠLÝ% d¨FÐ4DÑEÔEÝ& t¨WÐ6FÑGÔGÝ  §¢¨*°eÑ!<Ô!<Ñ=Ô=ðð ñ ô ð ð ð ØÝ˜VŸZšZ¨Ñ0Ô0Ð8°DÑ9Ô9Ý˜FŸJšJÐ'7Ñ8Ô8ÐG¸KÑHÔHØð	ð ð
ð 	
õ Ð>°Ð>Ð>Ñ
?Ô
?Ð?r*   Ú	card_specc                óŒ  — t          | t          ¦  «        st          d¦  «        ‚|                      d¦  «        pg }t          |t          ¦  «        r|st          d¦  «        ‚g }|D ]³}t          |t          ¦  «        st          d¦  «        ‚|                     d¦  «        pg }t          |t          ¦  «        r|st          d¦  «        ‚dd„ |D ¦   «         i}|                     d¦  «        rt          |d         ¦  «        |d<   |                     |¦  «         Œ´d|i}|                      d¦  «        }|rêt          |t          ¦  «        st          d	¦  «        ‚d
t          |d
d¦  «        i}|                     d¦  «        rt          |d         ¦  «        |d<   |                     d¦  «        r?t          |d         ¦  «        |d<   t          |                     d¦  «        pd¦  «        |d<   |                     d¦  «        rt          |d         ¦  «        |d<   ||d<   t          |                      d¦  «        pd¦  «        |dœS )Nzcard must be an objectÚsectionsz/card.sections must contain at least one sectionzcard sections must be objectsÚwidgetsz5card section widgets must contain at least one widgetc                ó,   — g | ]}t          |¦  «        ‘ŒS r4   )rî   )r›   Úws     r(   rÄ   z)card_spec_to_cards_v2.<locals>.<listcomp>ê  s    € Ð/TÐ/TÐ/TÀqµÀÑ0BÔ0BÐ/TÐ/TÐ/Tr*   Úheaderzcard.header must be an objectÚtitlezcard.headerÚsubtitlerØ   r×   Ú
image_typeÚSQUAREÚ	imageTypeÚimage_alt_textÚimageAltTextÚcard_idzhermes-card)ÚcardIdÚcard)rŽ   rÇ   rº   r8   rç   r-   rè   r½   )	rï   Úraw_sectionsrñ   Úsectionrò   Úrenderedrÿ   rõ   Úrendered_headers	            r(   Úcard_spec_to_cards_v2r  Û  sh  € Ý�i¥Ñ&Ô&ð 3ÝÐ1Ñ2Ô2Ð2à—=’= Ñ,Ô,Ð2°€LÝ�l¥DÑ)Ô)ð L°ð LÝÐJÑKÔKÐKà%'€HØð 	"ð 	"ˆÝ˜'¥4Ñ(Ô(ð 	>ÝÐ<Ñ=Ô=Ð=Ø—+’+˜iÑ(Ô(Ð.¨BˆÝ˜'¥4Ñ(Ô(ð 	V°ð 	VÝÐTÑUÔUÐUØ$-Ð/TÐ/TÈGÐ/TÑ/TÔ/TÐ#UˆØ�;Š;�xÑ Ô ð 	8Ý!$ W¨XÔ%6Ñ!7Ô!7ˆH�XÑØ�Š˜Ñ!Ô!Ð!Ð!à&¨Ð1€DØ�]Š]˜8Ñ$Ô$€FØð )Ý˜&¥$Ñ'Ô'ð 	>ÝÐ<Ñ=Ô=Ð=à•] 6¨7°MÑBÔBð+
ˆð �:Š:�jÑ!Ô!ð 	BÝ*-¨f°ZÔ.@Ñ*AÔ*AˆO˜JÑ'Ø�:Š:�kÑ"Ô"ð 	UÝ*-¨f°[Ô.AÑ*BÔ*BˆO˜JÑ'Ý+.¨v¯zªz¸,Ñ/GÔ/GÐ/SÈ8Ñ+TÔ+TˆO˜KÑ(Ø�:Š:Ð&Ñ'Ô'ð 	LÝ.1°&Ð9IÔ2JÑ.KÔ.KˆO˜NÑ+Ø(ˆˆX‰å˜)Ÿ-š-¨	Ñ2Ô2ÐC°mÑDÔDÈdÐSÐSÐSr*   c                  ó:   — e Zd ZdZdd„Zdd„Zdd„Zdd„Zdd„ZdS )Ú_ThreadCountStoreuR  Per-(chat_id, thread_name) inbound message counter, persisted to disk.

    Drives the DM main-flow vs side-thread heuristic:

    - prev_count == 0 (first time we see this thread) â†’ "main flow":
      Google Chat just auto-created a fresh thread for the user's
      top-level message. Treat it as part of the shared DM session;
      bot replies at top-level (no thread.name on outbound).
    - prev_count >= 1 (we've already seen this thread) â†’ "side thread":
      user explicitly engaged a thread that's been around. Isolate
      session by thread, route bot reply into the same thread.

    Persistence is essential: without it, every gateway restart wipes
    counts and active side-threads silently demote to "main flow",
    which leaks main-flow context into the user's isolated thread
    (the bug RamÃ³n reported across 4 iterations of the in-memory
    version).

    File format (JSON):
        {"<chat_id>": {"<thread_name>": <int_count>, ...}, ...}

    Failure modes are non-fatal: a missing or corrupt file resets to
    empty (logged as warning) so the adapter never crashes on disk
    issues. The next ``incr`` will write a fresh file.

    Save strategy: write-through after every ``incr``. The file is
    tiny (a few KB even for very active bots), so the simplicity of
    write-through outweighs the cost of debouncing for now.
    ÚpathÚ_Pathc                ó0   — || _         i | _        d| _        d S ©NF)Ú_pathÚ_countsÚ_loaded)r'   r  s     r(   r)   z_ThreadCountStore.__init__"  s   € ØˆŒ
Ø24ˆŒØˆŒˆˆr*   r   r   c                óP  — d| _         | j                             ¦   «         s	i | _        dS 	 | j                             d¬¦  «        }|                     ¦   «         rt          j        |¦  «        ni }n€# t          j        $ r3}t           
                    d| j        |¦  «         i | _        Y d}~dS d}~wt          $ r3}t           
                    d| j        |¦  «         i | _        Y d}~dS d}~ww xY wi }t          |t          ¦  «        r—|                     ¦   «         D ]‚\  }}t          |t          ¦  «        rt          |t          ¦  «        sŒ0i }|                     ¦   «         D ]4\  }}	t          |t          ¦  «        rt          |	t           ¦  «        r|	||<   Œ5|r|||<   Œƒ|| _        dS )uˆ   Load counts from disk. Safe to call multiple times.

        Missing file â†’ empty store. Corrupt JSON â†’ empty store + warn.
        TNúutf-8©ÚencodingzD[GoogleChat] thread-count store at %s is corrupt; starting fresh: %sz8[GoogleChat] could not read thread-count store at %s: %s)r  r  Úexistsr  Ú	read_textr»   ÚjsonÚloadsÚJSONDecodeErrorÚloggerÚwarningÚOSErrorrŽ   rÇ   rÉ   r-   r   )
r'   ÚrawÚdatarL   ÚcleanÚchat_idÚthreadsÚclean_threadsÚthread_nameÚcounts
             r(   Úloadz_ThreadCountStore.load'  s×  € ð
 ˆŒØŒz× Ò Ñ"Ô"ð 	ØˆDŒLØˆFð	Ø”*×&Ò&°Ð&Ñ8Ô8ˆCØ&)§i¢i¡k¤kÐ9•4”:˜c‘?”?�?°rˆDˆDøÝÔ#ð 	ð 	ð 	Ý�NŠNð%à”
˜Cñô ð ð
 ˆDŒLØˆFˆFˆFˆFˆFøøøøÝð 	ð 	ð 	Ý�NŠNØJØ”
˜Cñô ð ð ˆDŒLØˆFˆFˆFˆFˆFøøøøð	øøøð ,.ˆÝ�d�DÑ!Ô!ð 		3Ø$(§J¢J¡L¤Lð 3ð 3Ñ �˜Ý! '­3Ñ/Ô/ð µzÀ'Í4Ñ7PÔ7Pð ØØ02�Ø*1¯-ª-©/¬/ð ;ð ;Ñ&�K Ý! +­sÑ3Ô3ð ;½
À5Í#Ñ8NÔ8Nð ;Ø5:˜ kÑ2øØ ð 3Ø%2�E˜'‘NøØˆŒˆˆs$   «AA1 Á1C.Â (B.Â.C.Â;(C)Ã)C.r  r-   r   r   c                ó`   — | j                              |i ¦  «                             |d¦  «        S )z:Return the current count for (chat_id, thread_name), or 0.r   )r  r8   )r'   r  r   s      r(   r8   z_ThreadCountStore.getP  s*   € àŒ|×Ò ¨Ñ,Ô,×0Ò0°¸aÑ@Ô@Ð@r*   c                ó    — | j                              |i ¦  «        }|                     |d¦  «        }|dz   ||<   |                      ¦   «          |S )u¥   Increment count and write through to disk. Returns the
        PRE-increment value (the heuristic input â€” "have we seen this
        thread before this message?").r   r1   )r  Ú
setdefaultr8   Ú_save)r'   r  r   Úchat_countsÚprevs        r(   Úincrz_ThreadCountStore.incrT  sN   € ð ”l×-Ò-¨g°rÑ:Ô:ˆØ�Š˜{¨AÑ.Ô.ˆØ#'¨!¡8ˆ�KÑ Ø�
Š
‰ŒˆØˆr*   c                óž  — 	 | j         j                             dd¬¦  «         | j                              | j         j        dz   ¦  «        }|                     t          j        | j        d¬¦  «        d¬¦  «         t          j
        || j         ¦  «         d	S # t          $ r,}t                               d| j         |¦  «         Y d	}~d	S d	}~ww xY w)
uá   Atomic write of the counts dict to disk.

        Failure is non-fatal â€” log warning and continue. The in-memory
        counts stay consistent within the running process; only restart
        recovery is affected.
        T©ÚparentsÚexist_okz.tmp)Ú,ú:)Ú
separatorsr  r  z;[GoogleChat] could not persist thread-count store to %s: %sN)r  ÚparentÚmkdirÚwith_suffixÚsuffixÚ
write_textr  Údumpsr  ÚosÚreplacer  r  r  )r'   ÚtmprL   s      r(   r&  z_ThreadCountStore._save^  sã   € ð		ØŒJÔ×#Ò#¨D¸4Ð#Ñ@Ô@Ð@Ø”*×(Ò(¨¬Ô):¸VÑ)CÑDÔDˆCØ�NŠN�4œ: d¤l¸zÐJÑJÔJÐU\ˆNÑ]Ô]Ð]ÝŒJ�s˜DœJÑ'Ô'Ð'Ð'Ð'øÝð 	ð 	ð 	Ý�NŠNØMØ”
˜Cñô ð ð ð ð ð ð ð øøøøð	øøøs   ‚BB Â
CÂ !CÃCN)r  r  ©r   r   )r  r-   r   r-   r   r   )	r?   r@   rA   Ú__doc__r)   r"  r8   r)  r&  r4   r*   r(   r  r    s‡   € € € € € ðð ð<ð ð ð ð
'ð 'ð 'ð 'ðRAð Að Að Aðð ð ð ðð ð ð ð ð r*   r  c                  ó’  ‡ — e Zd ZdZeZdZdZdZd‹ˆ fd„Z	dŒd
„Z
d�d„ZedŽd„¦   «         Zed�d„¦   «         Zd�d„Zd‘d„Zd’d„Zd“d„Zd’d„Zddœd”d!„Zd•d"„Zd•d#„Ze	 d–d—d)„¦   «         Zd˜d+„Zd™d,„Zdšd/„Zd›d1„Z	 dœd�d7„Zdžd9„ZdŸd<„Z	 	 d d¡dB„Z	 dœd¢dD„Z 	 dœd£ˆ fdJ„Z!ddKœd¤dN„Z"d¥dO„Z#d¦dR„Z$d§dU„Z% e&j'        dV¦  «        Z(e)d¨dW„¦   «         Z*	 dœd©dX„Z+dŒdY„Z,dZd[œdªd_„Z-d«d`„Z.dœd¬da„Z/d­db„Z0d®dg„Z1d¯di„Z2	 	 	 d°d±dl„Z3	 	 d d²do„Z4	 	 	 d°d³dr„Z5	 	 d d´dt„Z6	 	 d dµdv„Z7	 	 	 d°d¶dx„Z8ed·d{„¦   «         Z9d|Z:d¸d„Z;d¹d�„Z<dºdƒ„Z=	 	 d d»d‡„Z>d¼d‰„Z?d½dŠ„Z@ˆ xZAS )¾rå   aZ  
    Google Chat bot adapter using Pub/Sub pull + Chat REST API.

    Required environment (see gateway/config.py Google Chat block):
      GOOGLE_CHAT_PROJECT_ID           (or GOOGLE_CLOUD_PROJECT fallback)
      GOOGLE_CHAT_SUBSCRIPTION_NAME    (or GOOGLE_CHAT_SUBSCRIPTION fallback)
      GOOGLE_CHAT_SERVICE_ACCOUNT_JSON (or GOOGLE_APPLICATION_CREDENTIALS)

    Optional:
      GOOGLE_CHAT_ALLOWED_USERS, GOOGLE_CHAT_ALLOW_ALL_USERS
      GOOGLE_CHAT_HOME_CHANNEL
      GOOGLE_CHAT_MAX_MESSAGES (FlowControl, default 1)
      GOOGLE_CHAT_MAX_BYTES    (FlowControl, default 16_777_216 = 16 MiB)
    é
   g       @g      ^@Úconfigri   c                ó¬  •— t          ¦   «                              |t          d¦  «        ¦  «         t          ¦   «          d | _        d | _        d | _        d | _        i | _        i | _	        i | _
        d | _        d | _        d | _        d | _        d | _        d | _        d | _        t%          ¦   «         | _        i | _        i | _        d| _        i | _        i | _        	 ddlm}  |¦   «         }n-# t6          t8          f$ r t;          j        ¦   «         dz  }Y nw xY wt?          |dz  ¦  «        | _         i | _!        i | _"        	 tG          tI          j%        dd¦  «        ¦  «        | _&        n# tN          tP          f$ r
 d	| _&        Y nw xY w	 tG          tI          j%        d
tS          d¦  «        ¦  «        ¦  «        | _*        n# tN          tP          f$ r
 d| _*        Y nw xY w| j+        j,         -                    d¦  «        ptI          j%        dd¦  «        pd .                    ¦   «         | _/        | j+        j,         -                    d¦  «        ptI          j%        dd¦  «        p| j/         .                    ¦   «         | _0        | j+        j,         -                    d¦  «        ptI          j%        dd¦  «        pd .                    ¦   «          1                    ¦   «         | _2        d S )Nrj   Fr   )Úget_hermes_homeú.hermeszgoogle_chat_thread_counts.jsonÚGOOGLE_CHAT_MAX_MESSAGESÚ1r1   ÚGOOGLE_CHAT_MAX_BYTESi   Úhttp_events_urlÚGOOGLE_CHAT_HTTP_EVENTS_URLr—   Úhttp_events_audienceÚ GOOGLE_CHAT_HTTP_EVENTS_AUDIENCEÚ!http_events_service_account_emailÚ-GOOGLE_CHAT_HTTP_EVENTS_SERVICE_ACCOUNT_EMAIL)3Úsuperr)   rh   rg   Ú_subscriberÚ	_chat_apiÚ_user_chat_apiÚ_user_credentialsÚ_user_creds_by_emailÚ_user_chat_api_by_emailÚ_last_sender_by_chatÚ_credentialsÚ_project_idÚ_subscription_pathÚ_streaming_pull_futureÚ_supervisor_taskÚ_loopÚ_bot_user_idrk   Ú_dedupÚ_typing_messagesÚ_clarify_stateÚ_shutting_downÚ_rate_limit_hitsÚ_last_inbound_threadÚhermes_constantsr@  ÚModuleNotFoundErrorrH   r  Úhomer  Ú_thread_count_storeÚ_typing_card_inflightÚ_orphan_typing_messagesr   r7  ÚgetenvÚ_max_messagesrº   Ú	TypeErrorr-   Ú
_max_bytesr>  Úextrar8   r»   Ú_http_events_urlÚ_http_events_audiencer�   Ú"_http_events_service_account_email)r'   r>  Ú_get_hermes_homeÚ_hermes_homeÚ	__class__s       €r(   r)   zGoogleChatAdapter.__init__‡  sû  ø€ õ
 	‰Œ×Ò˜¥¨-Ñ!8Ô!8Ñ9Ô9Ð9õ 	ÑÔÐØ*.ˆÔØ(,ˆŒð" .2ˆÔØ04ˆÔà46ˆÔ!Ø79ˆÔ$ð 57ˆÔ!Ø+/ˆÔØ*.ˆÔØ15ˆÔØ59ˆÔ#Ø8<ˆÔØ:>ˆŒ
Ø+/ˆÔÝ)Ñ+Ô+ˆŒØ02ˆÔØ.0ˆÔØ#ˆÔØ02ˆÔð 57ˆÔ!ð	4ØLÐLÐLÐLÐLÐLØ+Ð+Ñ-Ô-ˆLˆLøÝ#¥[Ð1ð 	4ð 	4ð 	4Ý œ:™<œ<¨)Ñ3ˆLˆLˆLð	4øøøå#4ØÐ;Ñ;ñ$
ô $
ˆÔ ð @BˆÔ"ð
 >@ˆÔ$ð	#Ý!$¥R¤YÐ/IÈ3Ñ%OÔ%OÑ!PÔ!PˆDÔÐøÝ�IÐ&ð 	#ð 	#ð 	#Ø!"ˆDÔÐÐð	#øøøð	/Ý!¥"¤)Ð,CÅSÐIYÑEZÔEZÑ"[Ô"[Ñ\Ô\ˆDŒOˆOøÝ�IÐ&ð 	/ð 	/ð 	/Ø.ˆDŒOˆOˆOð	/øøøð ŒKÔ×!Ò!Ð"3Ñ4Ô4ð ÝŒyÐ6¸Ñ;Ô;ðàß
Š%‰'Œ'ð	 	Ôð ŒKÔ×!Ò!Ð"8Ñ9Ô9ð %ÝŒyÐ;¸RÑ@Ô@ð%àÔ$ß
Š%‰'Œ'ð	 	Ô"ð ŒKÔ×!Ò!Ð"EÑFÔFð ÝŒyÐHÈ"ÑMÔMðàß
Š%‰'Œ'—%’%‘'”'ð	 	Ô/Ð/Ð/s6   ÃC) Ã)'DÄDÄ<'E$ Å$E?Å>E?Æ4F8 Æ8GÇGr   r   c                ó  — | j         j                             d¦  «        pt          j        d¦  «        }|�rK|                     ¦   «                              d¦  «        rf	 t          j        |¦  «        }n*# t          j	        $ r}t          d|› �¦  «        |‚d}~ww xY wt          j                             |t          ¬¦  «        S t          j                             |¦  «        st#          d¦  «        ‚	 t%          |dd	¬
¦  «        5 }t          j        |¦  «        }ddd¦  «         n# 1 swxY w Y   n*# t          j	        $ r}t          d|› �¦  «        |‚d}~ww xY wt          j                             |t          ¬¦  «        S 	 ddlm} n# t,          $ r d}Y nw xY w|€t          d¦  «        ‚	 |                     t          ¬¦  «        \  }}n%# t0          $ r}t          d|› �¦  «        |‚d}~ww xY wt2                               d¦  «         |S )u  Load Service Account credentials from env or config.extra,
        falling back to Application Default Credentials.

        Priority:
          1. Explicit ``extra['service_account_json']`` (path or inline JSON)
          2. ``GOOGLE_APPLICATION_CREDENTIALS`` env var (path)
          3. Application Default Credentials via ``google.auth.default()``
             â€” works on Cloud Run / GCE / GKE with a workload identity
             attached, or locally via ``gcloud auth application-default
             login``. Lets operators run the gateway in GCP without
             managing SA key files. Pattern lifted from PR #14965.
        Úservice_account_jsonÚGOOGLE_APPLICATION_CREDENTIALSÚ{z"Inline SA JSON is not valid JSON: N©Úscopesz7Service Account JSON file not found at configured path.Úrr  r  z-Service Account JSON file is not valid JSON: r   z±No Service Account credentials configured. Set GOOGLE_CHAT_SERVICE_ACCOUNT_JSON or GOOGLE_APPLICATION_CREDENTIALS, or install google-auth to use Application Default Credentials.zÁNo Service Account credentials configured and Application Default Credentials are unavailable. Set GOOGLE_CHAT_SERVICE_ACCOUNT_JSON or run ``gcloud auth application-default login``. ADC error: zI[GoogleChat] No SA JSON configured; using Application Default Credentials)r>  rj  r8   r7  rf  Úlstripr±   r  r  r  rº   r   ÚCredentialsÚfrom_service_account_infoÚ_CHAT_SCOPESr  r  ÚFileNotFoundErrorÚopenr"  Úgoogle.authÚauthrH   Údefaultr    r  Úinfo)r'   Úsa_pathr�  rL   ÚfhÚgoogle_authÚcredentialsÚ_projects           r(   Ú_load_sa_credentialsz&GoogleChatAdapter._load_sa_credentialsÿ  sÛ  € ð ŒKÔ×!Ò!Ð"8Ñ9Ô9ð ;ÝŒyÐ9Ñ:Ô:ð 	ð ñ 	à�~Š~ÑÔ×*Ò*¨3Ñ/Ô/ð 	ðÝœ: gÑ.Ô.�D�DøÝÔ+ð ð ð Ý$ØB¸SÐBÐBñô àðøøøøðøøøõ 'Ô2×LÒLØ¥ð Mñ ô ð õ ”7—>’> 'Ñ*Ô*ð Ý'ØMñô ð ðÝ˜' 3°Ð9Ñ9Ô9ð )¸RÝœ9 R™=œ=�Dð)ð )ð )ñ )ô )ð )ð )ð )ð )ð )ð )øøøð )ð )ð )ð )øøåÔ'ð ð ð Ý ØIÀCÐIÐIñô àðøøøøðøøøõ #Ô.×HÒHØ�\ð Iñ ô ð ð	Ø-Ð-Ð-Ð-Ð-Ð-Ð-øÝð 	ð 	ð 	ØˆKˆKˆKð	øøøàÐÝðQñô ð ð
		Ø$/×$7Ò$7½|Ð$7Ñ$LÔ$LÑ!ˆK˜˜øÝð 	ð 	ð 	Ýð$ð "ð	$ð $ñô ð ðøøøøð	øøøõ 	�Šð"ñ	
ô 	
ð 	
ð Ðs~   ÁA4 Á4BÂBÂBÃ3D2 ÄD&ÄD2 Ä&D*Ä*D2 Ä-D*Ä.D2 Ä2EÅEÅEÆF
 Æ
FÆFÆ.G Ç
G/ÇG*Ç*G/úTuple[str, Optional[str]]c                ó>  — | j         j                             d¦  «        pd                     ¦   «         }| j         j                             d¦  «        pd                     ¦   «         }| j         j                             d¦  «        pd                     ¦   «         }|r]t                               |¦  «        }|st          d¦  «        ‚|                     d¦  «        }|r||k    rt          d¦  «        ‚|p||fS |r|dfS |st          d	¦  «        ‚t          d
¦  «        ‚)zÔReturn (project_id, subscription_path) after validation.

        ``subscription_path`` is ``None`` for HTTP-inbound deployments. Raises
        ValueError with a sanitized message on any config problem.
        Ú
project_idr—   Úsubscription_namerE  zRGOOGLE_CHAT_SUBSCRIPTION_NAME must match 'projects/<project>/subscriptions/<sub>'.Úprojectzjproject_id in GOOGLE_CHAT_PROJECT_ID does not match the project embedded in GOOGLE_CHAT_SUBSCRIPTION_NAME.Nz<GOOGLE_CHAT_PROJECT_ID (or GOOGLE_CLOUD_PROJECT) is not set.zGOOGLE_CHAT_SUBSCRIPTION_NAME (or GOOGLE_CHAT_SUBSCRIPTION) is not set. Set GOOGLE_CHAT_HTTP_EVENTS_URL for HTTP callback mode.)r>  rj  r8   r»   Ú_SUBSCRIPTION_PATH_REÚmatchrº   Úgroup)r'   rŠ  ÚsubscriptionrE  rŽ  Úsubscription_projects         r(   Ú_validate_configz"GoogleChatAdapter._validate_configH  sY  € ð ”kÔ'×+Ò+¨LÑ9Ô9Ð?¸R×FÒFÑHÔHˆ
ØœÔ)×-Ò-Ð.AÑBÔBÐHÀb×OÒOÑQÔQˆØœ;Ô,×0Ò0Ð1BÑCÔCÐIÀr×PÒPÑRÔRˆàð 	DÝ)×/Ò/°Ñ=Ô=ˆEØð Ý ð@ñô ð ð $)§;¢;¨yÑ#9Ô#9Ð Øð Ð2°jÒ@Ð@Ý ðIñô ð ð Ð5Ð!5°|ÐCÐCàð 	$Ø˜tÐ#Ð#àð 	ÝØNñô ð õ ðFñ
ô 
ð 	
r*   Úfuturer   c                ó†   — 	 |                       ¦   «          d S # t          $ r t                               d¦  «         Y d S w xY w)Nz1[GoogleChat] Background inbound processing failed)Úresultr    r  Ú	exception)r“  s    r(   Ú_log_background_failurez)GoogleChatAdapter._log_background_failurep  sV   € ð	RØ�MŠM‰OŒOˆOˆOˆOøÝð 	Rð 	Rð 	RÝ×ÒÐPÑQÔQÐQÐQÐQÐQð	Røøøs   ‚ ˜$A ¿A Úloopú#Optional[asyncio.AbstractEventLoop]r   c                óZ   — | d uo't           t          | dd„ ¦  «        ¦   «         ¦  «         S )NÚ	is_closedc                 ó   — dS r
  r4   r4   r*   r(   ú<lambda>z;GoogleChatAdapter._loop_accepts_callbacks.<locals>.<lambda>y  s   € ÐPU€ r*   )r   r9   )r˜  s    r(   Ú_loop_accepts_callbacksz)GoogleChatAdapter._loop_accepts_callbacksw  s4   € à˜4ÐÐY­Ð-V­W°T¸;ÈÈÑ-VÔ-VÑ-XÔ-XÑ(YÔ(YÐ$YÐYr*   Úcoroc                óV  — | j         }|                      |¦  «        st                               d¦  «         dS 	 ddlm}  |||t          dt          j        ¬¦  «        }n+# t          $ r t                               d¦  «         Y dS w xY w|€dS | 	                    | j
        ¦  «         dS )zHSchedule a coroutine on the adapter loop from a Pub/Sub callback thread.z9[GoogleChat] Loop not accepting callbacks; dropping eventNr   )Úsafe_schedule_threadsafez3[GoogleChat] Failed to schedule background callback)r  Úlog_messageÚ	log_levelz1[GoogleChat] Loop closed between check and submit)rX  rž  r  r  Úagent.async_utilsr¡  ÚloggingÚWARNINGrI   Úadd_done_callbackr—  )r'   rŸ  r˜  r¡  r“  s        r(   Ú_submit_on_loopz!GoogleChatAdapter._submit_on_loop{  sÙ   € àŒzˆØ×+Ò+¨DÑ1Ô1ð 	õ �NŠNÐVÑWÔWÐWØˆFð
	ØBÐBÐBÐBÐBÐBØ-Ð-Ø�dÝØQÝ!œ/ð	ñ ô ˆFˆFøõ ð 	ð 	ð 	Ý�NŠNÐNÑOÔOÐOØˆFˆFð	øøøð ˆ>ØˆFØ× Ò  Ô!=Ñ>Ô>Ð>Ð>Ð>s   º%A  Á $BÂBr  c                ó’   — t          j        dt          t          j        ¦   «         dz  ¦  «        ¦  «        }t          |¦  «        dz  S )zBLocation where the resolved bot user_id is cached across restarts.ÚHERMES_HOMErA  zgoogle_chat_bot_id.json)r7  rf  r-   r  rb  )r'   Úbases     r(   Ú_bot_id_cache_pathz$GoogleChatAdapter._bot_id_cache_path•  s8   € åŒy˜­­E¬J©L¬L¸9Ñ,DÑ(EÔ(EÑFÔFˆÝ�T‰{Œ{Ð6Ñ6Ð6r*   úOptional[str]c                ó  — |                       ¦   «         }|                     ¦   «         sd S 	 t          j        |                     d¬¦  «        ¦  «        }|                     d¦  «        pd S # t          t          j        f$ r Y d S w xY w)Nr  r  Úbot_user_id)r¬  r  r  r  r  r8   r  r  )r'   r  r  s      r(   Ú_load_cached_bot_idz%GoogleChatAdapter._load_cached_bot_idš  s‹   € Ø×&Ò&Ñ(Ô(ˆØ�{Š{‰}Œ}ð 	Ø�4ð	Ý”:˜dŸnšn°g˜nÑ>Ô>Ñ?Ô?ˆDØ—8’8˜MÑ*Ô*Ð2¨dÐ2øÝ�Ô-Ð.ð 	ð 	ð 	Ø�4�4ð	øøøs   ¬>A+ Á+BÂBr¯  r-   c                ó  — 	 |                       ¦   «         }|j                             dd¬¦  «         |                     t	          j        d|i¦  «        d¬¦  «         d S # t          $ r  t                               dd¬¦  «         Y d S w xY w)NTr+  r¯  r  r  z0[GoogleChat] Could not persist bot_user_id cache©Úexc_info)	r¬  r1  r2  r5  r  r6  r  r  Údebug)r'   r¯  r  s      r(   Ú_save_cached_bot_idz%GoogleChatAdapter._save_cached_bot_id¤  s°   € ð	\Ø×*Ò*Ñ,Ô,ˆDØŒK×Ò d°TÐÑ:Ô:Ð:Ø�OŠOÝ”
˜M¨;Ð7Ñ8Ô8Ø ð ñ ô ð ð ð øõ ð 	\ð 	\ð 	\Ý�LŠLÐKÐVZˆLÑ[Ô[Ð[Ð[Ð[Ð[ð	\øøøs   ‚AA Á&B	ÂB	c              ƒ  ó  ‡ K  — g }‰ j         j        r5‰ j         j        j        r$|                     ‰ j         j        j        ¦  «         t	          j        dd¦  «                             ¦   «         }|r2|                     d„ |                     d¦  «        D ¦   «         ¦  «         |D ]ç}	 t          j
        |fˆ fd„	¦  «        ƒ d{V —†}nM# t          $ r@}t                               d|t          t          |¦  «        ¦  «        ¦  «         Y d}~Œhd}~ww xY w|                     dg ¦  «        D ]`}|                     d	i ¦  «                             d
¦  «        dk    r1|                     d	i ¦  «                             d¦  «        }|r|c c S ŒaŒèdS )ud  Resolve ``users/{id}`` via Chat API members.list on a known space.

        Tries the home channel first, then any space from the allowlist.
        If no space is known, returns None and self-filter falls back to
        filtering ``sender.type == 'BOT'`` (which is still safe but less
        precise â€” own messages and other bots look alike).
        ÚGOOGLE_CHAT_BOOTSTRAP_SPACESr—   c              3  óf   K  — | ],}|                      ¦   «         ¯|                      ¦   «         V — Œ-d S r    )r»   )r›   Úss     r(   rž   z9GoogleChatAdapter._resolve_bot_user_id.<locals>.<genexpr>½  sK   è è € ð $ð $Ø¸a¿gºg¹i¼ið$Ø—’‘	”	ð$ð $ð $ð $ð $ð $r*   r.  c                óÐ   •— ‰j                              ¦   «                              ¦   «                              | d¬¦  «                             ‰                     ¦   «         ¬¦  «        S )Né2   )r1  ÚpageSize©Úhttp)rM  ÚspacesÚmembersrç   ÚexecuteÚ_new_authed_http)r¹  r'   s    €r(   r�  z8GoogleChatAdapter._resolve_bot_user_id.<locals>.<lambda>Ã  sM   ø€  D¤N×$9Ò$9Ñ$;Ô$;ß’W‘Y”Yß’T ¨R�TÑ0Ô0ß’W $×"7Ò"7Ñ"9Ô"9�WÑ:Ô:ð r*   Nz*[GoogleChat] members.list failed on %s: %sÚmembershipsÚmemberrÎ   ÚBOTrÞ   )r>  Úhome_channelr  rè   r7  rf  r»   ÚextendÚsplitÚasyncioÚ	to_threadr   r  r´  rª   r-   r8   )r'   Úcandidate_spacesÚextra_spacesÚspacerÀ  rL   rÄ  rÞ   s   `       r(   Ú_resolve_bot_user_idz&GoogleChatAdapter._resolve_bot_user_id¯  sù  øè è € ð ')ÐØŒ;Ô#ð 	F¨¬Ô(@Ô(Hð 	FØ×#Ò# D¤KÔ$<Ô$DÑEÔEÐEå”yÐ!?ÀÑDÔD×JÒJÑLÔLˆØð 	Ø×#Ò#ð $ð $Ø#/×#5Ò#5°cÑ#:Ô#:ð$ñ $ô $ñ ô ð ð &ð 	$ð 	$ˆEðÝ 'Ô 1Ø"ð ;ð ;ð ;ð ;ð ;ñ!ô !ð ð ð ð ð ð ��øõ ð ð ð Ý—’Ø@ØÝ%¥c¨#¡h¤hÑ/Ô/ñô ð ð
 ����øøøøðøøøð "Ÿ+š+ m°RÑ8Ô8ð $ð $�Ø—:’:˜h¨Ñ+Ô+×/Ò/°Ñ7Ô7¸5Ò@Ð@Ø!Ÿ:š: h°Ñ3Ô3×7Ò7¸Ñ?Ô?�DØð $Ø#˜˜˜˜˜øð	$ð
 ˆts   Â'CÃ
DÃ6DÄDF)Úis_reconnectrÏ  c          	   ƒ  ó
  ‡ ‡	‡
‡‡K  — t          ¦   «         s‰                      ddd¬¦  «         dS t          j        ¦   «         ‰ _        	 ‰                      ¦   «         \  }Š‰                      ¦   «         Š
nn# t          t          f$ rZ}t          t          |¦  «        ¦  «        }t                               d|¦  «         ‰                      d|d¬¦  «         Y d}~dS d}~ww xY w|‰ _        ‰‰ _        ‰
‰ _        	 t          j        ˆ
fd„¦  «        ƒ d{V —†‰ _        ng# t$          $ rZ}t          t          |¦  «        ¦  «        }t                               d	|¦  «         ‰                      d
|d¬¦  «         Y d}~dS d}~ww xY w	 ddlm}mŠ	m} t          j        |¦  «        ƒ d{V —†Š‰�D‰‰ _        t          j        ˆ	ˆfd„¦  «        ƒ d{V —†‰ _        t                               d¦  «         t          j        |¦  «        ƒ d{V —†}|r=t                               dt5          |¦  «        d                     |¦  «        ¦  «         n‰€t                               d¦  «         nZ# t$          $ rM}t                               dt          t          |¦  «        ¦  «        ¦  «         d‰ _        d‰ _        Y d}~nd}~ww xY w	 t          j        ‰ j        j        ¦  «        ƒ d{V —† n,# t$          $ r t                               dd¬¦  «         Y nw xY w‰�ùt>                                ‰
¬¦  «        ‰ _!        	 t          j        ˆ ˆfd„¦  «        ƒ d{V —† n¹# tD          j#        $ r ‰                      ddd¬¦  «         Y dS tD          j$        $ r ‰                      ddd¬¦  «         Y dS t$          $ rZ}t          t          |¦  «        ¦  «        }t                               d|¦  «         ‰                      d|d¬¦  «         Y d}~dS d}~ww xY w‰  %                    ¦   «         ‰ _&        ‰ j&        s[‰  '                    ¦   «         ƒ d{V —†‰ _&        ‰ j&        r‰  (                    ‰ j&        ¦  «         nt                               d¦  «         ‰�.t          j)        ‰  *                    ¦   «         ¦  «        ‰ _+        d}n	d‰ _+        d }‰  ,                    ¦   «          t                               d!|pd"|‰rd#nd$‰ j&        pd%‰ j-        ‰ j.        ¦  «         dS )&zBValidate config, authenticate, start Pub/Sub pull, resolve bot id.Úmissing_depsz<google-cloud-pubsub / google-api-python-client not installedF©ÚcodeÚmessageÚ	retryablez)[GoogleChat] Config validation failed: %sÚconfig_invalidNc                 ó*   •— t          dd‰ d¬¦  «        S )NÚchatÚv1F)r…  Úcache_discovery)r   ©r…  s   €r(   r�  z+GoogleChatAdapter.connect.<locals>.<lambda>ú  s"   ø€ �ØØØ +Ø$)ð	ñ ô € r*   z0[GoogleChat] Failed to build Chat API client: %sÚchat_api_initr1   )Úload_user_credentialsÚbuild_user_chat_serviceÚlist_authorized_emailsc                 ó   •—  ‰ ‰¦  «        S r    r4   )Ú_build_user_chatÚ
user_credss   €€r(   r�  z+GoogleChatAdapter.connect.<locals>.<lambda>  s   ø€ Ð,Ð,¨ZÑ8Ô8€ r*   uN   [GoogleChat] Legacy user OAuth loaded â€” fallback attachment delivery enabledz1[GoogleChat] %d per-user OAuth tokens on disk: %sz, u²   [GoogleChat] No user OAuth tokens at setup â€” file attachments will degrade to text-only fallback. Each user runs /setup-files once in their own DM to enable native attachments.zX[GoogleChat] User OAuth load failed (attachments will degrade to text-only fallback): %szK[GoogleChat] thread-count store load failed (treating all threads as fresh)Tr²  rÛ  c                 ó>   •— ‰ j                              d‰i¬¦  «        S )Nr�  )r   )rL  Úget_subscription)r'   Úsubscription_paths   €€r(   r�  z+GoogleChatAdapter.connect.<locals>.<lambda>C  s(   ø€ ˜DÔ,×=Ò=Ø!/Ð1BÐ Cð >ñ ô € r*   Úsubscription_not_foundz1Pub/Sub subscription not found at configured pathÚsubscription_permissionzAService Account lacks roles/pubsub.subscriber on the subscriptionz([GoogleChat] subscription.get failed: %sÚsubscription_checkz^[GoogleChat] bot_user_id not yet resolved; will resolve on first addedToSpace or member lookupÚpubsubr¾  zp[GoogleChat] Connected; project=%s, inbound=%s, subscription=%s, bot_user_id=%s, flow_control(msgs=%s, bytes=%s)z<unset>z
<redacted>z<none>z<unresolved>)/rg   Ú_set_fatal_errorrÉ  Úget_running_looprX  r’  r‡  rº   r|  rª   r-   r  ÚerrorrT  rU  rS  rÊ  rM  r    ÚoauthrÝ  rÞ  rß  rO  rN  r�  ÚlenÚjoinr  rc  r"  r   ÚSubscriberClientrL  r   ÚNotFoundÚPermissionDeniedr°  rY  rÎ  rµ  Úcreate_taskÚ_run_supervisorrW  Ú_mark_connectedrg  ri  )r'   rÏ  rŠ  rL   ÚmsgÚ_load_user_credsÚ_list_emailsÚ
authorizedÚinboundrá  r…  rå  râ  s   `        @@@@r(   ÚconnectzGoogleChatAdapter.connectÙ  s§  øøøøøè è € õ $Ñ%Ô%ð 	Ø×!Ò!Ø#ØVØð "ñ ô ð ð
 �5åÔ-Ñ/Ô/ˆŒ
ð	Ø,0×,AÒ,AÑ,CÔ,CÑ)ˆJÐ)Ø×3Ò3Ñ5Ô5ˆKˆKøÝÕ-Ð.ð 	ð 	ð 	Ý#¥C¨¡H¤HÑ-Ô-ˆCÝ�LŠLÐDÀcÑJÔJÐJØ×!Ò!Ð'7ÀÐPUÐ!ÑVÔVÐVØ�5�5�5�5�5øøøøð		øøøð &ˆÔØ"3ˆÔØ'ˆÔð	Ý#*Ô#4ðð ð ð ñ$ô $ð ð ð ð ð ð ˆDŒNˆNøõ ð 	ð 	ð 	Ý#¥C¨¡H¤HÑ-Ô-ˆCÝ�LŠLÐKÈSÑQÔQÐQØ×!Ò! ÀÈuÐ!ÑUÔUÐUØ�5�5�5�5�5øøøøð		øøøð$	'ðð ð ð ð ð ð ð ð ð õ
  'Ô0Ð1AÑBÔBÐBÐBÐBÐBÐBÐBˆJØÐ%Ø)3�Ô&Ý,3Ô,=Ø8Ð8Ð8Ð8Ð8ñ-ô -ð 'ð 'ð 'ð 'ð 'ð '�Ô#õ —’ð2ñô ð õ  'Ô0°Ñ>Ô>Ð>Ð>Ð>Ð>Ð>Ð>ˆJØð Ý—’ØGÝ˜
‘O”O T§Y¢Y¨zÑ%:Ô%:ñô ð ð ð Ð#Ý—’ð4ñô ð øøõ ð 	'ð 	'ð 	'Ý�NŠNð5å!¥# c¡(¤(Ñ+Ô+ñô ð ð
 &*ˆDÔ"Ø"&ˆDÔÐÐÐÐÐøøøøð	'øøøð	ÝÔ# DÔ$<Ô$AÑBÔBÐBÐBÐBÐBÐBÐBÐBÐBøÝð 	ð 	ð 	Ý�NŠNð(Ø26ð ñ ô ð ð ð ð	øøøð Ð(å(×9Ò9ÀkÐ9ÑRÔRˆDÔðÝÔ'ðð ð ð ð ñô ð ð ð ð ð ð ð ð øõ
 "Ô*ð ð ð Ø×%Ò%Ø1ØOØ#ð &ñ ô ð ð
 �u�uÝ!Ô2ð 	ð 	ð 	Ø×%Ò%Ø2ð'ð $ð &ñ ô ð ð �u�uÝð ð ð Ý'­¨C©¬Ñ1Ô1�Ý—’ÐGÈÑMÔMÐMØ×%Ò%Ð+?ÈÐX\Ð%Ñ]Ô]Ð]Ø�u�u�u�u�uøøøøð	øøøð !×4Ò4Ñ6Ô6ˆÔØÔ ð 	Ø&*×&?Ò&?Ñ&AÔ&AÐ AÐ AÐ AÐ AÐ AÐ AˆDÔØÔ ð Ø×(Ò(¨Ô):Ñ;Ô;Ð;Ð;å—’ðJñô ð ð
 Ð(õ %,Ô$7¸×8LÒ8LÑ8NÔ8NÑ$OÔ$OˆDÔ!ØˆGˆGà$(ˆDÔ!ØˆGà×ÒÑÔÐÝ�Šð>àÐ#˜)ØØ-Ð;ˆLˆL°8ØÔÐ/ ØÔØŒOñ		
ô 		
ð 		
ð ˆtsƒ   Á	+A5 Á5C ÂACÃC Ã9"D Ä
F Ä&AE;Å;F ÆCI$ É$
J;É.AJ6Ê6J;Ê?$K$ Ë$&LÌLÌ3M Í'PÍ<&PÎ%	PÎ.APÐPc              ƒ  ó²  K  — d| _         | j        rv| j                             ¦   «         s]| j                             ¦   «          	 t	          j        | j        d¬¦  «        ƒ d{V —† n!# t          j        t          j        f$ r Y nw xY w| j        �W	 | j                             ¦   «          t	          j	        | j        j
        d¦  «        ƒ d{V —† n# t          $ r Y nw xY wd| _        | j        �=	 t	          j	        | j        j        ¦  «        ƒ d{V —† n# t          $ r Y nw xY wd| _        |                      ¦   «          t                               d¦  «         dS )zKClean shutdown: stop accepting new messages, wait in-flight, close clients.Tç      @©r‰   Nç      $@z[GoogleChat] Disconnected)r]  rW  ÚdoneÚcancelrÉ  Úwait_forÚCancelledErrorÚTimeoutErrorrV  rÊ  r•  r    rL  ÚcloseÚ_mark_disconnectedr  r�  ©r'   s    r(   Ú
disconnectzGoogleChatAdapter.disconnect€  s¨  è è € à"ˆÔØÔ ð 	¨Ô)>×)CÒ)CÑ)EÔ)Eð 	ØÔ!×(Ò(Ñ*Ô*Ð*ðÝÔ& tÔ'<ÀcÐJÑJÔJÐJÐJÐJÐJÐJÐJÐJÐJøÝÔ*­GÔ,@ÐAð ð ð Ø�ðøøøàÔ&Ð2ðØÔ+×2Ò2Ñ4Ô4Ð4ÝÔ'¨Ô(CÔ(JÈDÑQÔQÐQÐQÐQÐQÐQÐQÐQÐQøÝð ð ð Ø�ðøøøà*.ˆDÔ'ØÔÐ'ðÝÔ'¨Ô(8Ô(>Ñ?Ô?Ð?Ð?Ð?Ð?Ð?Ð?Ð?Ð?øÝð ð ð Ø�ðøøøà#ˆDÔØ×ÒÑ!Ô!Ð!Ý�ŠÐ/Ñ0Ô0Ð0Ð0Ð0s6   Á!A& Á&BÂBÂ>C Ã
CÃCÃ-$D Ä
DÄDc              ƒ  ó0  K  — d}| j         �s	t          j                             | j        | j        ¬¦  «        }	 | j                             | j        | j	        |¬¦  «        }|| _
        |dk    rt                               d|¦  «         d}t          j        |j        ¦  «        ƒ d{V —† | j         rdS �nX# t          j        $ r Y dS t"          j        $ r |                      ddd¬	¦  «         Y dS t"          j        $ r |                      d
dd¬	¦  «         Y dS t*          $ rè}|dz  }t-          t/          |¦  «        ¦  «        }t                               d|| j        |¦  «         || j        k    r"|                      dd|› d�d¬	¦  «         Y d}~dS t5          | j        | j        d|dz
  z  z  ¦  «        }t;          j        d|¦  «        }	 t          j        |¦  «        ƒ d{V —† n# t          j        $ r Y Y d}~dS w xY wY d}~nd}~ww xY w| j         �¯dS dS )a  Run the streaming_pull with exponential backoff; fatal after 10 attempts.

        ``subscribe()`` returns a concurrent.futures.Future that resolves when
        the stream dies. We await ``future.result()`` in a worker thread and
        react to exceptions.
        r   )Úmax_messagesÚ	max_bytes)ÚcallbackÚflow_controlz9[GoogleChat] Pub/Sub stream reconnected after %d attemptsNÚpubsub_authz6Pub/Sub authentication failed (SA key invalid/revoked)FrÒ  Úpubsub_permissionz.SA lacks pubsub.subscriber on the subscriptionr1   z4[GoogleChat] Pub/Sub stream died (attempt %d/%d): %sÚpubsub_reconnect_exhaustedzPub/Sub reconnect failed z times; giving upé   ) r]  r   ÚtypesÚFlowControlrg  ri  rL  Ú	subscriberU  Ú_on_pubsub_messagerV  r  r�  rÉ  rÊ  r•  r  r   ÚUnauthenticatedrê  rò  r    rª   r-   r  Ú_MAX_RECONNECT_ATTEMPTSÚminÚ_RECONNECT_MAX_DELAYÚ_RECONNECT_BASE_DELAYÚrandomÚuniformÚsleep)r'   ÚattemptÚflowr“  rL   rö  ÚdelayÚ	sleep_fors           r(   rô  z!GoogleChatAdapter._run_supervisorœ  sò  è è € ð ˆØÔ%ñ =	Ý”?×.Ò.Ø!Ô/Øœ/ð /ñ ô ˆDð8ØÔ)×3Ò3ØÔ+Ø!Ô4Ø!%ð 4ñ ô �ð
 /5�Ô+Ø˜Q’;�;Ý—K’KÐ [Ð]dÑeÔeÐeØ�åÔ'¨¬Ñ6Ô6Ð6Ð6Ð6Ð6Ð6Ð6Ð6àÔ&ð Ø�FñøåÔ)ð ð ð Ø��Ý!Ô1ð ð ð Ø×%Ò%Ø&ØTØ#ð &ñ ô ð ð
 ��Ý!Ô2ð ð ð Ø×%Ò%Ø,ØLØ#ð &ñ ô ð ð
 ��Ýð ð ð Ø˜1‘�Ý'­¨C©¬Ñ1Ô1�Ý—’ØJØØÔ0Øñ	ô ð ð ˜dÔ:Ò:Ð:Ø×)Ò)Ø9Ø V¸GÐ VÐ VÐ VØ"'ð *ñ ô ð ð
 �F�F�F�F�FÝØÔ-ØÔ.°!¸À!¹Ñ2DÑEñô �õ
 #œN¨1¨eÑ4Ô4�	ðÝ!œ-¨	Ñ2Ô2Ð2Ð2Ð2Ð2Ð2Ð2Ð2Ð2øÝÔ-ð ð ð Ø�F�F�F�F�F�Fðøøøð 3Ð2Ð2Ð2Ð2øøøøð/øøøðI Ô%ñ =	ð =	ð =	ð =	ð =	sU   ¹A7B4 Â4H	Ã&H	Ã/&H	Ä	H	Ä!A*HÆ8HÇ
G%Ç$HÇ%G<Ç4HÇ;G<Ç<HÈH	r—   ÚenveloperP   Úce_typeú4Optional[Tuple[Dict[str, Any], Dict[str, Any], str]]c                óà  — |                       d¦  «        pi }|r|                      d¦  «        nd}|rH|                      d¦  «        pi }|                      d¦  «        p|                      d¦  «        pi }||dfS t          |                       d¦  «        t          ¦  «        rU|                       dd¦  «        d	k    rdS | d         }|                       d¦  «        p|                      d¦  «        pi }||d
fS d| v sd| v �rm|                       dd	¦  «        d	k    rdS |                       d¦  «        pd                     ¦   «         }|                       d¦  «        p|pd}d|pd                     dd¦  «                             dd¦  «        z   }|                       dd¦  «        pd}	|                       d¦  «        pd}
t          |
¦  «                             ¦   «                              ¦   «         pd}|dvrd}|                       dd¦  «        pd||||dœ|	|	dœ}|                       d¦  «        pd}|rd|i|d<   |                       dd¦  «        pd|                       d d!¦  «        d"œ}||d#fS dS )$uŸ  Detect Pub/Sub envelope format and return ``(message, space, format_name)``.

        Three known formats are accepted. Returns ``None`` when the envelope
        is unrecognized, is a non-MESSAGE event, or otherwise should be
        silently dropped.

        Format 1 â€” Workspace Add-ons (canonical, ce-type-driven)::

            {"chat": {"messagePayload": {"message": {...}, "space": {...}}}}

        Format 2 â€” Native Chat API Pub/Sub (alternative configuration where
        the Chat app publishes events directly without the Workspace
        Add-ons wrapper)::

            {"type": "MESSAGE", "message": {...}, "space": {...}}

        Format 3 â€” Relay / flat (a custom Cloud Run relay that flattens the
        Chat event into top-level fields)::

            {"event_type": "MESSAGE", "sender_email": "...", "text": "...",
             "space_name": "spaces/X", "thread_name": "spaces/X/threads/Y",
             "message_name": "spaces/X/messages/M.M"}

        For format 3 the helper synthesizes a Chat-API-shaped ``message``
        dict so downstream code (``_dispatch_message`` â†’
        ``_build_message_event``) can consume it without branching.
        rØ  ÚmessagePayloadNrÔ  rÍ  Úworkspace_addonsrÎ   r—   ÚMESSAGEÚnative_chat_apiÚ
event_typeÚsender_emailÚsender_display_nameÚUnknownzusers/relay-Úunknownú@Ú_at_r™   Ú_r}   Úsender_typeÚHUMAN>   rÅ  r3  Úmessage_name)rÞ   ÚemailÚdisplayNamerÎ   )rÞ   Úsenderr}   ÚargumentTextr   rÞ   ÚthreadÚ
space_nameÚ
space_typeÚSPACE)rÞ   Ú	spaceTypeÚ
relay_flat)r8   rŽ   rÇ   r»   r8  r-   r5   )r"  r#  Ú
chat_blockÚmsg_payload_wrapperrö  rÍ  r+  Úsender_displayÚsender_name_surrogater}   Úsender_type_rawr2  r   s                r(   Ú_extract_message_payloadz*GoogleChatAdapter._extract_message_payloadæ  sä  € ðH —\’\ &Ñ)Ô)Ð/¨Rˆ
ØBLÐV˜jŸnšnÐ-=Ñ>Ô>Ð>ÐRVÐØð 	2Ø%×)Ò)¨)Ñ4Ô4Ð:¸ˆCØ'×+Ò+¨GÑ4Ô4ÐN¸¿ºÀÑ8HÔ8HÐNÈBˆEØ˜Ð1Ð1Ð1õ
 �h—l’l 9Ñ-Ô-­tÑ4Ô4ð 	1Ø�|Š|˜F BÑ'Ô'¨9Ò4Ð4Ø�tØ˜9Ô%ˆCØ—L’L Ñ)Ô)ÐC¨S¯WªW°WÑ-=Ô-=ÐCÀˆEØ˜Ð0Ð0Ð0ð
 ˜8Ð#Ð# ~¸Ð'AÑ'AØ�|Š|˜L¨)Ñ4Ô4¸	ÒAÐAØ�tØ$ŸLšL¨Ñ8Ô8Ð>¸B×EÒEÑGÔGˆLà—’Ð2Ñ3Ô3ð Øðàð ð ØÐ, 9×5Ò5°c¸6ÑBÔB×JÒJÈ3ÐPSÑTÔTñUð "ð —<’< ¨Ñ+Ô+Ð1¨rˆDð  (Ÿ|š|¨MÑ:Ô:ÐE¸gˆOÝ˜oÑ.Ô.×4Ò4Ñ6Ô6×<Ò<Ñ>Ô>ÐIÀ'ˆKØÐ"2Ð2Ð2Ø%�à Ÿš ^°RÑ8Ô8Ð>¸Bà1Ø)Ø#1Ø'ð	ð ð Ø $ð
#ð 
#ˆCð #Ÿ,š, }Ñ5Ô5Ð;¸ˆKØð 6Ø!'¨Ð 5��H‘à Ÿš \°2Ñ6Ô6Ð<¸"Ø%Ÿ\š\¨,¸Ñ@Ô@ðð ˆEð ˜˜|Ð+Ð+àˆtr*   rÔ  c                óÀ	  — | j         r|                     ¦   «          dS 	 t          j        |j                             d¦  «        ¦  «        }n?# t          $ r2 t                               d¦  «         | 	                    ¦   «          Y dS w xY wt          t          |di ¦  «        pi ¦  «        }|                     d¦  «        pd}t                               dt          |                     ¦   «         ¦  «        |¦  «         t!          j        d¦  «        rZ	 d	d
lm}  |t          j        |¦  «        ¦  «        }n# t          $ r d}Y nw xY wt                               d|dd…         ¦  «         	 |                     d¦  «        pi }d|v sd|v �r(|                     d¦  «        pi }|                     d¦  «        pi }	|                     d¦  «        pi }
d|v rš|
                     d¦  «        pi }|                     d¦  «        dk    r:| j        s3|                     d¦  «        }|r|| _        |                      |¦  «         t                               d|	                     dd¦  «        ¦  «         n/t                               d|	                     dd¦  «        ¦  «         | 	                    ¦   «          dS d|v sd|                     ¦   «         v r0t                               d¦  «         | 	                    ¦   «          dS |                      ||¦  «        }|€Qt                               d|t          |                     ¦   «         ¦  «        ¦  «         | 	                    ¦   «          dS |\  }}	}|                     d¦  «        pi }|                     d¦  «        pd}|dk    r| 	                    ¦   «          dS |                     d¦  «        pd}|rK| j                             |¦  «        r1t                               d |¦  «         | 	                    ¦   «          dS t          |¦  «        }d|vr|	r|	|d<   t          |¦  «        }d|vr|	r|	|d<   |                      |                      ||¦  «        ¦  «         | 	                    ¦   «          dS # t          $ rE t                               d!¦  «         	 | 	                    ¦   «          Y dS # t          $ r Y Y dS w xY ww xY w)"u¡  Pub/Sub callback â€” parse envelope and dispatch to asyncio loop.

        Runs in a Pub/Sub SubscriberClient worker thread, NOT the event loop.
        Never block this function; never raise out of it (that triggers
        Pub/Sub nack + infinite redelivery).

        Google Chat Events API uses CloudEvents-style Pub/Sub messages. The
        event type is carried in Pub/Sub message attributes (``ce-type``),
        not in the JSON body. The body is wrapped in a ``chat`` object whose
        keys depend on the event type:

          - google.workspace.chat.message.v1.created
              -> envelope["chat"]["messagePayload"] = {space, message}
          - google.workspace.chat.membership.v1.created
              -> envelope["chat"]["membershipPayload"] = {space, membership}
          - google.workspace.chat.membership.v1.deleted
              -> envelope["chat"]["membershipPayload"] = {space, membership}
        Nr  z-[GoogleChat] Could not parse Pub/Sub envelopeÚ
attributeszce-typer—   z)[GoogleChat] Envelope keys=%s, ce-type=%sÚGOOGLE_CHAT_DEBUG_RAWr   )Úredact_sensitive_textz<redact filter unavailable>z([GoogleChat] RAW envelope (redacted): %siÐ  rØ  Ú
membershipÚ
MEMBERSHIPÚmembershipPayloadrÍ  ÚcreatedrÄ  rÎ   rÅ  rÞ   z[GoogleChat] ADDED_TO_SPACE %sú?z"[GoogleChat] REMOVED_FROM_SPACE %srÌ   rÿ   z;[GoogleChat] Card/widget event ack'd (v2 feature, deferred)zO[GoogleChat] Envelope did not match a known message format; ce-type=%s, keys=%sr7  z[GoogleChat] Dedup drop for %sz([GoogleChat] Error in _on_pubsub_message)r]  Únackr  r  r  Údecoder    r  r–  ÚackrÇ   r9   r8   r´  rç   Úkeysr7  rf  Úagent.redactrH  r6  rY  rµ  r�  r�   rD  rZ  Úis_duplicater¨  Ú_dispatch_message)r'   rÔ  r"  Úattrsr#  rH  Údumpr?  ÚmplrÍ  rI  rÄ  rÞ   Ú	extractedrö  Ú_fmtr7  r2  Úmsg_nameÚmsg_with_spaceÚenriched_envs                        r(   r  z$GoogleChatAdapter._on_pubsub_messageV  s"  € ð& Ôð 	Ø�LŠL‰NŒNˆNØˆFð	Ý”z '¤,×"5Ò"5°gÑ">Ô">Ñ?Ô?ˆHˆHøÝð 	ð 	ð 	Ý×ÒÐLÑMÔMÐMØ�KŠK‰MŒMˆMØˆFˆFð	øøøõ
 •W˜W l°BÑ7Ô7Ð=¸2Ñ>Ô>ˆØ—)’)˜IÑ&Ô&Ð,¨"ˆÝ�ŠØ7Ý�—’‘”Ñ!Ô!Øñ	
ô 	
ð 	
õ
 Œ9Ð,Ñ-Ô-ð 	Rð
5Ø>Ð>Ð>Ð>Ð>Ð>à,Ð,­T¬Z¸Ñ-AÔ-AÑBÔB��øÝð 5ð 5ð 5Ø4���ð5øøøå�LŠLÐCÀTÈ%È4È%Ä[ÑQÔQÐQðN	Ø!Ÿš fÑ-Ô-Ð3°ˆJð ˜wÐ&Ð&¨,¸'Ð*AÑ*AØ —n’nÐ%8Ñ9Ô9Ð?¸R�ØŸš Ñ(Ô(Ð.¨B�Ø ŸWšW \Ñ2Ô2Ð8°b�
Ø Ð'Ð'à'Ÿ^š^¨HÑ5Ô5Ð;¸�FØ—z’z &Ñ)Ô)¨UÒ2Ð2¸4Ô;LÐ2Ø%Ÿzšz¨&Ñ1Ô1˜Øð ;Ø04˜DÔ-Ø ×4Ò4°TÑ:Ô:Ð:Ý—K’KØ8¸%¿)º)ÀFÈCÑ:PÔ:Pñô ð ð õ —K’KØ<¸e¿iºiÈÐPSÑ>TÔ>Tñô ð ð —’‘”�Ø�ð ˜7Ð"Ð" f°·²±´Ð&?Ð&?Ý—’ØQñô ð ð —’‘”�Ø�ð ×5Ò5°hÀÑHÔHˆIØÐ Ý—’ð*Ø+2µD¸¿º¹¼Ñ4IÔ4Iñô ð ð —’‘”�Ø�à(ÑˆC�˜Ø—W’W˜XÑ&Ô&Ð,¨"ˆFØ Ÿ*š* VÑ,Ô,Ð2°ˆKð ˜eÒ#Ð#Ø—’‘”�Ø�ð —w’w˜v‘”Ð,¨"ˆHØð ˜DœK×4Ò4°XÑ>Ô>ð Ý—’Ð=¸xÑHÔHÐHØ—’‘”�Ø�õ " #™YœYˆNØ˜nÐ,Ð,°Ð,Ø*/�˜wÑ'õ   ™>œ>ˆLØ˜lÐ*Ð*¨uÐ*Ø(-�˜WÑ%à× Ò  ×!7Ò!7¸ÈÑ!UÔ!UÑVÔVÐVØ�KŠK‰MŒMˆMˆMˆMøÝð 	ð 	ð 	Ý×ÒÐGÑHÔHÐHðØ—’‘”����øÝð ð ð Ø���ðøøøð		øøøsq   Ÿ,A Á8BÂBÄ#D6 Ä6EÅEÅ,ER Ê4AR Ë>A'R Í'AR Î7A"R ÐA1R Ò$SÒ3S
Ó

SÓSÓSÓSc              ƒ  ó¶  K  — |                       |¦  «        }|€i S |\  }}}|                     d¦  «        pi }|                     d¦  «        dk    ri S |                     d¦  «        pd}|r| j                             |¦  «        ri S t	          |¦  «        }d|vr|r||d<   t	          |¦  «        }	d|	vr|r||	d<   |                      ||	¦  «        ƒ d {V —† i S )Nr7  rÎ   rÅ  rÞ   r—   rÍ  )rD  r8   rZ  rS  rÇ   rT  )
r'   r"  rX  rö  rÍ  rY  r7  rZ  r[  r\  s
             r(   Údispatch_http_eventz%GoogleChatAdapter.dispatch_http_event×  s  è è € Ø×1Ò1°(Ñ;Ô;ˆ	ØÐØˆIà$ÑˆˆU�DØ—’˜Ñ"Ô"Ð( bˆØ�:Š:�fÑÔ Ò&Ð&ØˆIà—7’7˜6‘?”?Ð( bˆØð 	˜œ×0Ò0°Ñ:Ô:ð 	ØˆIå˜c™œˆØ˜.Ð(Ð(¨UÐ(Ø&+ˆN˜7Ñ#å˜H‘~”~ˆØ˜,Ð&Ð&¨5Ð&Ø$)ˆL˜Ñ!à×$Ò$ ^°\ÑBÔBÐBÐBÐBÐBÐBÐBÐBØˆ	r*   Úauth_headerúTuple[bool, str]c           	     óD  — | j         r| j        sdS |                     d¦  «        sdS |dd …                              ¦   «         }|sdS 	 t	          || j         ¦  «        }nM# t
          $ r@}t                               dt          t          |¦  «        ¦  «        ¦  «         Y d }~dS d }~ww xY wd„ | j         
                    d¦  «        D ¦   «         }t          |                     d	¦  «        pd
¦  «                             ¦   «                              ¦   «         }|r||vrdS dS )N)FÚ&google_chat_http_events_not_configuredúBearer )FÚmissing_google_beareré   z6[GoogleChat] HTTP event bearer verification failed: %s)FÚinvalid_google_bearerc                ó‚   — h | ]<}|                      ¦   «         ¯|                      ¦   «                              ¦   «         ’Œ=S r4   )r»   r�   )r›   rí   s     r(   ú	<setcomp>z>GoogleChatAdapter.verify_http_event_request.<locals>.<setcomp>  sJ   € ð 
ð 
ð 
àØ�zŠz‰|Œ|ð
Ø�JŠJ‰LŒL×ÒÑ Ô ð
ð 
ð 
r*   r.  r5  r—   )FÚ!unexpected_google_bearer_identity)Tr—   )rl  rm  r±   r»   rU   r    r  r  rª   r-   rÈ  r8   r�   )r'   r_  rN   ÚclaimsrL   ÚexpectedÚclaim_emails          r(   Úverify_http_event_requestz+GoogleChatAdapter.verify_http_event_requestð  s\  € ØÔ)ð 	C°Ô1Xð 	CØBÐBà×%Ò% iÑ0Ô0ð 	2Ø1Ð1à˜A˜B˜B”×%Ò%Ñ'Ô'ˆØð 	2Ø1Ð1ð	2Ý,¨U°DÔ4NÑOÔOˆFˆFøÝð 	2ð 	2ð 	2Ý�NŠNØHÝ!¥# c¡(¤(Ñ+Ô+ñô ð ð 2Ð1Ð1Ð1Ð1Ð1øøøøð	2øøøð
ð 
àÔ?×EÒEÀcÑJÔJð
ñ 
ô 
ˆõ
 ˜&Ÿ*š* WÑ-Ô-Ð3°Ñ4Ô4×:Ò:Ñ<Ô<×BÒBÑDÔDˆØð 	>˜k°Ð9Ð9Ø=Ð=àˆxs   Á	A Á
B)Á)5B$Â$B)rö  c              ƒ  óö  K  — 	 |                       ||¦  «        ƒ d{V —†}|€dS |j        pd                     ¦   «         }|                     d¦  «        r_|j        �X|j        r|j        j        r|j        j        nd}|                      |j        j        |j        j        ||¬¦  «        ƒ d{V —†}|rdS |  	                    |¦  «        ƒ d{V —† dS # t          $ r t                               d¦  «         Y dS w xY w)u  Translate a Chat message payload to a MessageEvent and hand off.

        Intercepts the ``/setup-files`` admin command BEFORE the agent
        sees it â€” that's a bot-local OAuth setup flow, not a prompt.
        Everything else flows to ``handle_message`` as normal.
        Nr—   z/setup-files)r  Ú	thread_idÚraw_textr+  z%[GoogleChat] _dispatch_message failed)Ú_build_message_eventr}   r»   r±   ÚsourceÚuser_id_altÚ_handle_setup_files_commandr  ro  Úhandle_messager    r  r–  )r'   rö  r"  Úeventr}   r+  Úhandleds          r(   rT  z#GoogleChatAdapter._dispatch_message  sf  è è € ð	FØ×3Ò3°C¸ÑBÔBÐBÐBÐBÐBÐBÐBˆEØˆ}Ø�ð ”JÐ$ "×+Ò+Ñ-Ô-ˆDØ�Š˜~Ñ.Ô.ð °5´<Ð3Kð ”|ðØ(-¬Ô(@ð�E”LÔ,Ð,àð ð
 !%× @Ò @Ø!œLÔ0Ø#œlÔ4Ø!Ø!-ð	 !Añ !ô !ð ð ð ð ð ð �ð ð Ø�Fà×%Ò% eÑ,Ô,Ð,Ð,Ð,Ð,Ð,Ð,Ð,Ð,Ð,øÝð 	Fð 	Fð 	FÝ×ÒÐDÑEÔEÐEÐEÐEÐEð	Føøøs   „C ¤BC Â3C Ã$C8Ã7C8Nr  ro  rp  r+  c              ƒ  ól  ‡ ‡‡‡‡K  — ddl mŠ |r&|                     ¦   «                              ¦   «         nd}|                     d¬¦  «        }t          |¦  «        dk    r|d                              ¦   «         nd}d)ˆˆ ˆfd
„}|s°‰                     ¦   «                              ¦   «         }	‰                     |¦  «        }
|
                     ¦   «         }|r‰ 	                    |¦  «        nd}|�|pd} |d|› d|
› d�¦  «        ƒ d{V —† dS |	s |d¦  «        ƒ d{V —† dS  |d¦  «        ƒ d{V —† dS |dk    �r]‰                     ¦   «                              ¦   «         s |d¦  «        ƒ d{V —† dS 	 ddl
}ddl}|                     ¦   «         }|                     |¦  «        5  t          j        ‰j        |¦  «        ƒ d{V —† ddd¦  «         n# 1 swxY w Y   |                     ¦   «                              ¦   «                              ¦   «         d         }nd# t&          $ r  |d¦  «        ƒ d{V —† Y dS t(          $ r:}t*                               d|¦  «          |d|› �¦  «        ƒ d{V —† Y d}~dS d}~ww xY w |d|› d�¦  «        ƒ d{V —† dS |dk    �rE	 ddl
}ddl}|                     ¦   «         }|                     |¦  «        5  t          j        ‰j        |¦  «        ƒ d{V —† ddd¦  «         n# 1 swxY w Y   |                     ¦   «                              ¦   «         pd}nT# t&          $ r d}Y nFt(          $ r:}t*                               d|¦  «          |d|› �¦  «        ƒ d{V —† Y d}~dS d}~ww xY w|r7‰ j                             |d¦  «         ‰ j                             |d¦  «         nd‰ _        d‰ _         |d |› d!�¦  «        ƒ d{V —† dS 	 ddl
}ddl}|                     ¦   «         }|                     |¦  «        5  t          j        ‰j        ||¦  «        ƒ d{V —† ddd¦  «         n# 1 swxY w Y   |                     ¦   «                              ¦   «         }nd# t&          $ r  |d"¦  «        ƒ d{V —† Y dS t(          $ r:}t*                               d#|¦  «          |d|› �¦  «        ƒ d{V —† Y d}~dS d}~ww xY w	 t          j        ‰j	        |¦  «        ƒ d{V —†Š‰�Vt          j        ˆˆfd$„¦  «        ƒ d{V —†}|r‰‰ j        |<   |‰ j        |<   n‰‰ _        |‰ _         |d%¦  «        ƒ d{V —† dS n2# t(          $ r%}t*                               d&|¦  «         Y d}~nd}~ww xY w |d'‰                     |¦  «        › d(|› d!�¦  «        ƒ d{V —† dS )*uP  Run the in-chat OAuth setup flow for native attachment delivery.

        Returns ``True`` if the message was consumed (no agent dispatch),
        ``False`` if it should fall through.

        Multi-user mode: ``sender_email`` is the asker's identity, which
        is also the per-user OAuth key. ``status`` / ``start`` / ``revoke``
        / code-exchange all operate on THIS user's token slot. When
        ``sender_email`` is ``None`` (e.g. tests, or older inbound events
        without a populated email field) the handler falls back to the
        legacy single-user path so pre-multi-user installs keep working.

        Subcommands:
          /setup-files                  â†’ show status + next step
          /setup-files start            â†’ print OAuth URL
          /setup-files revoke           â†’ revoke and delete stored token
          /setup-files <CODE_OR_URL>    â†’ exchange auth code for token

        Pre-requisite: client_secret.json must already be on the host
        (one-time terminal step). The status reply tells the user how to
        do that if it's missing.
        r1   )rí  N)Úmaxsplitr—   r}   r-   r   r   c              “  óº   •K  — d| i}‰rd‰i|d<   	 ‰                      ‰|¦  «        ƒ d {V —† d S # t          $ r  t                               dd¬¦  «         Y d S w xY w)Nr}   rÞ   r9  z+[GoogleChat] /setup-files reply send failedTr²  )Ú_create_messager    r  r´  )r}   Úbodyr  r'   ro  s     €€€r(   Ú_replyz=GoogleChatAdapter._handle_setup_files_command.<locals>._reply]  s¤   øè è € Ø$*¨D >ˆDØð 5Ø"(¨)Ð!4��X‘ðØ×*Ò*¨7°DÑ9Ô9Ð9Ð9Ð9Ð9Ð9Ð9Ð9Ð9Ð9øÝð ð ð Ý—’ØAØ!ð ñ ô ð ð ð ð ðøøøs   ’0 °&AÁAzshared (legacy)u2   âœ… Native attachment delivery is **active** for `z`.
Token: `z(`
Send `/setup-files revoke` to disable.Tu¶  ðŸ”§ Native attachment delivery is **not configured**.
**Step 1 (one-time, on the host):** create OAuth client credentials at https://console.cloud.google.com/apis/credentials â†’ *Create credentials* â†’ *OAuth client ID* â†’ *Desktop app*. Download the JSON. Then on the host run:
```
python -m plugins.platforms.google_chat.oauth --client-secret /path/to/client_secret.json
```
**Step 2:** come back here and send `/setup-files start`.uf   ðŸ”§ Client credentials are stored but you haven't authorized yet. Send `/setup-files start` to begin.Ústartuk   âš ï¸� No client credentials stored for this profile. Send `/setup-files` (no args) for setup instructions.r   éÿÿÿÿug   â�Œ Couldn't generate the OAuth URL. Check the gateway logs and verify the client_secret.json is valid.z*[GoogleChat] /setup-files start failed: %su   â�Œ Error: z01. Open this URL in your browser and authorize:
u>  

2. After clicking *Allow*, your browser will fail to load `http://localhost:1/?...&code=...`. That's expected.

3. Copy the entire failed URL from the browser's URL bar and paste it back here as: `/setup-files <PASTE_URL>` (or just the `code=...` value).

Tip: the URL contains your access grant â€” keep it private.ÚrevokezRevoked.z4Revoke completed (some steps may have been skipped).z+[GoogleChat] /setup-files revoke failed: %su   â�Œ Error revoking: u   âœ… Done.
```
z
```u�   â�Œ Token exchange failed. The code may have expired or the URL is malformed. Send `/setup-files start` to get a fresh OAuth URL.z-[GoogleChat] /setup-files exchange failed: %sc                 ó.   •— ‰                      ‰ ¦  «        S r    )rÞ  )Ú	new_credsÚoauth_helpers   €€r(   r�  z?GoogleChatAdapter._handle_setup_files_command.<locals>.<lambda>û  s   ø€ ˜L×@Ò@ÀÑKÔK€ r*   uZ   âœ… Authorized! Native attachment delivery is now active. Try asking me to send you a PDF.z0[GoogleChat] post-exchange creds load failed: %suz   âš ï¸� Token exchanged but the gateway couldn't load the new credentials in-memory. Restart the gateway and the token at `z(` will be picked up.
Helper output:
```
)r}   r-   r   r   )r—   rí  r»   r�   rÈ  rî  Ú_client_secret_pathr  Ú_token_pathrÝ  ÚioÚ
contextlibÚStringIOÚredirect_stdoutrÉ  rÊ  Úget_auth_urlÚgetvalueÚ
splitlinesÚ
SystemExitr    r  r  r€  rP  ÚpoprQ  rO  rN  Úexchange_auth_code)r'   r  ro  rp  r+  Ú
sender_keyÚpartsÚargr}  Úclient_secret_presentÚ
token_pathÚtoken_presentÚcredsÚwhor†  r‡  ÚbufÚauth_urlrL   ÚoutputÚnew_apir‚  rƒ  s   ```                  @@r(   rt  z-GoogleChatAdapter._handle_setup_files_command5  s
  øøøøøè è € ð: 	,Ð+Ð+Ð+Ð+Ð+ð
 6BÐK�\×'Ò'Ñ)Ô)×/Ò/Ñ1Ô1Ð1Àtˆ
à—’¨�Ñ*Ô*ˆå"% e¡*¤*¨q¢. .ˆe�AŒh�nŠnÑÔÐ°bˆð
	ð 
	ð 
	ð 
	ð 
	ð 
	ð 
	ð 
	ð ð &	à×0Ò0Ñ2Ô2×9Ò9Ñ;Ô;ð "ð &×1Ò1°*Ñ=Ô=ˆJØ&×-Ò-Ñ/Ô/ˆMð !ð+�×2Ò2°:Ñ>Ô>Ð>Ø&*ð ð Ð Ø Ð5Ð$5�Ø�fð=Øð=ð =à)ð=ð =ð =ñô ð ð ð ð ð ð ð ð �tØ(ð Ø�fð
Pñô ð ð ð ð ð ð ð ð �tØ�&ðFñô ð ð ð ð ð ð ð ð �4à�'Š>‰>Ø×3Ò3Ñ5Ô5×<Ò<Ñ>Ô>ð Ø�fðGñô ð ð ð ð ð ð ð ð �tðð �	�	�	Ø!Ð!Ð!Ð!Ø—k’k‘m”m�Ø×/Ò/°Ñ4Ô4ð ð Ý!Ô+Ø$Ô1°:ñô ð ð ð ð ð ð ð ðð ð ñ ô ð ð ð ð ð ð øøøð ð ð ð ð Ÿ<š<™>œ>×/Ò/Ñ1Ô1×<Ò<Ñ>Ô>¸rÔB��øÝð ð ð Ø�fðGñô ð ð ð ð ð ð ð ð �t�tÝð ð ð Ý—’Ø@À#ñô ð ð �fÐ0¨3Ð0Ð0Ñ1Ô1Ð1Ð1Ð1Ð1Ð1Ð1Ð1Ø�t�t�t�t�tøøøøðøøøð �&ðOØðOð Oð Oñ	ô 	ð 	ð 	ð 	ð 	ð 	ð 	ð 	ð �4à�(Š?‰?ðØ�	�	�	Ø!Ð!Ð!Ð!Ø—k’k‘m”m�Ø×/Ò/°Ñ4Ô4ð Mð MÝ!Ô+¨LÔ,?ÀÑLÔLÐLÐLÐLÐLÐLÐLÐLðMð Mð Mñ Mô Mð Mð Mð Mð Mð Mð Møøøð Mð Mð Mð MàŸš™œ×-Ò-Ñ/Ô/Ð=°:��øÝð Pð Pð PØO���Ýð ð ð Ý—’ØAÀ3ñô ð ð �fÐ9°CÐ9Ð9Ñ:Ô:Ð:Ð:Ð:Ð:Ð:Ð:Ð:Ø�t�t�t�t�tøøøøðøøøð ð +ØÔ)×-Ò-¨j¸$Ñ?Ô?Ð?ØÔ,×0Ò0°¸TÑBÔBÐBÐBà)-�Ô&Ø&*�Ô#Ø�&Ð9¨FÐ9Ð9Ð9Ñ:Ô:Ð:Ð:Ð:Ð:Ð:Ð:Ð:Ø�4ð	ØˆIˆIˆIØÐÐÐØ—+’+‘-”-ˆCØ×+Ò+¨CÑ0Ô0ð ð ÝÔ'Ø Ô3°S¸*ñô ð ð ð ð ð ð ð ðð ð ñ ô ð ð ð ð ð ð øøøð ð ð ð ð —\’\‘^”^×)Ò)Ñ+Ô+ˆFˆFøÝð 	ð 	ð 	Ø�&ð%ñô ð ð ð ð ð ð ð ð
 �4�4Ýð 	ð 	ð 	Ý�NŠNØ?Àñô ð ð �&Ð, sÐ,Ð,Ñ-Ô-Ð-Ð-Ð-Ð-Ð-Ð-Ð-Ø�4�4�4�4�4øøøøð	øøøð	Ý%Ô/ØÔ2°Jñô ð ð ð ð ð ð ˆIð Ð$Ý 'Ô 1ØKÐKÐKÐKÐKñ!ô !ð ð ð ð ð ð �ð ð 2Ø<E�DÔ-¨jÑ9Ø?F�DÔ0°Ñ<Ð<à-6�DÔ*Ø*1�DÔ'Ø�fð?ñô ð ð ð ð ð ð ð ð �tð %øõ ð 	ð 	ð 	Ý�NŠNØBÀCñô ð ð ð ð ð ð øøøøð	øøøð
 ˆfð7à×+Ò+¨JÑ7Ô7ð7ð 7ð *0ð7ð 7ð 7ñ
ô 
ð 	
ð 	
ð 	
ð 	
ð 	
ð 	
ð 	
ð ˆtsà   Å81H  Æ)!GÇ
H  ÇGÇH  ÇGÇAH  È JÈ>	JÉ/I<É<JÊ#1L5 Ë!LË5L5 ÌLÌL5 ÌLÌ	+L5 Ì5NÍ	NÍ/NÎNÏ(1Q9 Ð"QÐ;Q9 ÑQÑQ9 ÑQÑ)Q9 Ñ9SÒ	SÒ /SÓSÓA6U Õ
VÕ!VÖVúOptional[MessageEvent]c           	   ƒ  ó®  K  — |                      d¦  «        p|                      d¦  «        pi }|                      d¦  «        pd}|                      d¦  «        p|                      d¦  «        pd                     ¦   «         }|                      d¦  «        pi }|                      d¦  «        pd}|                      d¦  «        pi }|                      d¦  «        pd}	|                      d	¦  «        p|                      d
¦  «        p|	}
|                      d
¦  «        pd}|r0|r.|                     ¦   «                              ¦   «         | j        |<   |dv rdnd}|                      d¦  «        p|                      d¦  «        pd}|                     ¦   «         }|                      d¦  «        pi }t          |¦  «        }|rUt          |                      d¦  «        pd¦  «        }|r/|                     d¦  «        sd|› d|› �                     ¦   «         }g }g }t          j	        }|                      d¦  «        pg }|D ]ž}	 |  
                    |¦  «        ƒ d{V —†\  }}n*# t          $ r t                               d¦  «         Y ŒHw xY w|sŒO|                     |¦  «         |                     |pd¦  «         |t          j	        k    r|st          |pd¦  «        }ŒŸ|rt          j        }d}|r|r| j                             ||¦  «        }|dk    r;|dk    }|r|nd}|r|r|r|| j        |<   n.|r| j                             |d¦  «         n|}|r|r
|| j        |<   |                      ||                      d	¦  «        p|                      d¦  «        pd||p|	|
||	pd¬¦  «        }t-          |||||                      d¦  «        pd||¬¦  «        S )z4Parse a Chat API message into a hermes MessageEvent.rÍ  rÞ   r—   rÎ   r=  r9  Nr7  r6  r5  >   ÚDMÚDIRECT_MESSAGEÚdmr�  r8  r}   ÚslashCommandÚ	commandIdú/z/cmd_Ú Ú
attachmentz'[GoogleChat] attachment download failedúapplication/octet-streamr   )r  Ú	chat_nameÚ	chat_typeÚuser_idÚ	user_namero  rs  )r}   Úmessage_typerr  Úraw_messageÚ
message_idÚ
media_urlsÚmedia_types)r8   r5   r»   r�   rR  r   r-   r±   rn   ÚTEXTÚ_download_attachmentr    r  r–  rè   rµ   ÚCOMMANDrc  r)  r_  rŽ  Úbuild_sourcerm   )r'   rö  r"  rÍ  r:  r;  r9  r   r7  Úsender_namerA  r+  r¨  r}   ÚslashÚis_slashÚ
command_idr®  r¯  r«  ÚattachmentsÚattÚ
local_pathr«   Úprev_thread_countÚis_side_threadÚsession_thread_idrr  s                               r(   rq  z&GoogleChatAdapter._build_message_event  sˆ  è è € ð —’˜WÑ%Ô%Ð?¨¯ª°Ñ)9Ô)9Ð?¸RˆØ—Y’Y˜vÑ&Ô&Ð,¨"ˆ
Ø—i’i Ñ'Ô'ÐG¨5¯9ª9°[Ñ+AÔ+AÐGÀR×NÒNÑPÔPˆ
Ø—’˜Ñ"Ô"Ð( bˆØ—j’j Ñ(Ô(Ð0¨DˆØ—’˜Ñ"Ô"Ð( bˆØ—j’j Ñ(Ô(Ð.¨BˆØŸš MÑ2Ô2ÐX°f·j²jÀÑ6IÔ6IÐXÈ[ˆØ—z’z 'Ñ*Ô*Ð0¨bˆð ð 	Q˜Jð 	QØ4@×4FÒ4FÑ4HÔ4H×4NÒ4NÑ4PÔ4PˆDÔ% jÑ1à&Ð*BÐBÐB�D�DÈˆ	Ø�wŠw�~Ñ&Ô&Ð?¨#¯'ª'°&©/¬/Ð?¸RˆØ�zŠz‰|Œ|ˆð —’˜Ñ'Ô'Ð-¨2ˆÝ˜‘;”;ˆØð 	;Ý˜UŸYšY {Ñ3Ô3Ð9°rÑ:Ô:ˆJØð ; $§/¢/°#Ñ"6Ô"6ð ;Ø2˜zÐ2Ð2¨DÐ2Ð2×8Ò8Ñ:Ô:�ð !#ˆ
Ø!#ˆÝ"Ô'ˆØ—g’g˜lÑ+Ô+Ð1¨rˆØð 	Bð 	BˆCðØ)-×)BÒ)BÀ3Ñ)GÔ)GÐ#GÐ#GÐ#GÐ#GÐ#GÐ#GÑ �
˜D˜DøÝð ð ð Ý× Ò Ð!JÑKÔKÐKØ�ðøøøð ð ØØ×Ò˜jÑ)Ô)Ð)Ø×Ò˜tÐAÐ'AÑBÔBÐBà�{Ô/Ò/Ð/¸Ð/Ý5°d°j¸bÑAÔA�øàð 	/Ý&Ô.ˆLð ÐØð 	˜:ð 	Ø $Ô 8× =Ò =Ø˜Kñ!ô !Ðð* ˜ÒÐØ.°Ò2ˆNØ/=Ð G  À4Ðð ð @˜zð @¨nð @Ø8C�Ô)¨*Ñ5Ð5Øð @ØÔ)×-Ò-¨j¸$Ñ?Ô?Ð?øà +Ðàð D˜zð DØ8C�Ô)¨*Ñ5à×"Ò"ØØ—i’i Ñ.Ô.ÐI°%·)²)¸FÑ2CÔ2CÐIÀrØð "Ð0 [Ø$Ø'Ø$Ð,¨ð! #ñ 
ô 
ˆõ$ ØØ%ØØØ—w’w˜v‘”Ð.¨$Ø!Ø#ð
ñ 
ô 
ð 	
s   ÉI%É%$JÊJr¥  ú#Tuple[Optional[str], Optional[str]]c           	   ƒ  óv  ‡ ‡‡K  — |                      d¦  «        pd}|                      d¦  «        pd}|                      d¦  «        pd}|                      d¦  «        pi }|                      d¦  «        pdŠ|                      d¦  «        pdŠ|dk    r ‰st                               d	¦  «         d
|fS d
}‰rqdˆˆ fd„}	 t          j        |¦  «        ƒ d
{V —†}nN# t
          $ rA}t                               dt          t          |¦  «        ¦  «        ¦  «         d
}Y d
}~nd
}~ww xY w|€¢‰r t          ‰¦  «        st                               d¦  «         d
|fS dˆˆ fd„}		 t          j        |	¦  «        ƒ d
{V —†}nP# t          $ rC}t                               dt          t          |¦  «        ¦  «        ¦  «         d
|fcY d
}~S d
}~ww xY w|€d
|fS |r|                     d¦  «        d         nd}
d|
v r2d|
                     dd¦  «        d                              ¦   «         z   }nd}|                     d¦  «        rt          ||pd¬¦  «        }nb|                     d¦  «        rt!          ||pd¬¦  «        }n9|                     d¦  «        rt#          ||pd¬¦  «        }nt%          ||
¦  «        }||fS )u#  Download an inbound attachment to the local cache; return (path, mime).

        Priority for bot Service Accounts:

          1. ``attachmentDataRef.resourceName`` via ``chat.media.download`` â€”
             the supported bot path. The Service Account bearer token has
             ``chat.bot`` scope which the Chat API authorises against the
             space membership.
          2. Drive-hosted files (``source == 'DRIVE_FILE'``) require user
             OAuth and Drive scope; skip with a log.
          3. Direct HTTP fetch of ``downloadUri`` only as a last resort â€”
             that URL is meant for user OAuth tokens (chat.google.com
             returns 401 for SA bearer tokens) and is unlikely to work,
             but we keep the path for forward-compat with Google changes.
        ÚcontentTyper—   rr  rÞ   ÚattachmentDataRefÚresourceNameÚdownloadUriÚ
DRIVE_FILEzb[GoogleChat] Skipping Drive-picker attachment (no resourceName, would need user-OAuth Drive scope)Nr   Úbytesc                 ó  •— ‰j                              ¦   «                              ‰¬¦  «        } ddlm} dd l}|                     ¦   «         } ||| ¦  «        }d}|s|                     ¦   «         \  }}|¯|                     ¦   «         S )N)rÂ  r   )ÚMediaIoBaseDownloadF)	rM  ÚmediaÚdownload_mediar^   rÇ  r†  ÚBytesIOÚ
next_chunkr‹  )	ÚreqrÇ  r†  r˜  Ú
downloaderr   Ú_statusÚresource_namer'   s	          €€r(   Ú_fetch_mediaz<GoogleChatAdapter._download_attachment.<locals>._fetch_mediaÀ  s¥   ø€ Ø”n×*Ò*Ñ,Ô,×;Ò;Ø!.ð <ñ ô �ð EÐDÐDÐDÐDÐDØ�	�	�	à—j’j‘l”l�Ø0Ð0°°cÑ:Ô:�
Ø�Øð <Ø$.×$9Ò$9Ñ$;Ô$;‘M�G˜Tð ð <à—|’|‘~”~Ð%r*   z,[GoogleChat] media.download_media failed: %sz8[GoogleChat] Rejecting attachment fetch: non-Google hostc                 ó´   •— dd l mc mc m}  |                      ‰j        ¦  «        }|                     ‰d¬¦  «        }|                     ¦   «          |j        S )Nr   é   rþ  )	Úgoogle.auth.transport.requestsr  Ú	transportrD   ÚAuthorizedSessionrS  r8   Úraise_for_statusÚcontent)ÚgarÚauthed_sessionrˆ   Údownload_urir'   s      €€r(   Ú
_fetch_uriz:GoogleChatAdapter._download_attachment.<locals>._fetch_uriß  so   ø€ Ø<Ð<Ð<Ð<Ð<Ð<Ð<Ð<Ð<Ð<Ð<Ð<à!$×!6Ò!6°tÔ7HÑ!IÔ!I�Ø%×)Ò)¨,ÀÐ)ÑCÔC�Ø×%Ò%Ñ'Ô'Ð'Ø”|Ð#r*   zx[GoogleChat] downloadUri fetch failed (SA tokens often lack access here; this is expected for user-uploaded content): %sr£  r  r¥  r™   r1   r­   z.jpg)Úextr®   z.oggr¯   z.mp4)r   rÅ  )r8   r  r�  rÉ  rÊ  r   r  rª   r-   r¦   r    rÈ  Úrsplitr�   r±   rs   rq   rt   rr   )r'   r¥  r«   rr  rÞ   Úattachment_data_refr  rÐ  rL   rÛ  ÚfilenamerÜ  ÚlocalrÚ  rÏ  s   `            @@r(   r±  z&GoogleChatAdapter._download_attachment“  s}  øøøè è € ð$ �~Š~˜mÑ,Ô,Ð2°ˆØ—’ Ñ)Ô)Ð/¨RˆØ�~Š~˜fÑ%Ô%Ð+¨ˆØ(ŸnšnÐ-@ÑAÔAÐGÀRÐØ+×/Ò/°Ñ?Ô?ÐEÀ2ˆØ!—~’~ mÑ4Ô4Ð:¸ˆð �\Ò!Ð!¨-Ð!Ý�KŠKðCñô ð ð ˜�:Ðà $ˆð ð 	ð&ð &ð &ð &ð &ð &ð &ðÝ$Ô.¨|Ñ<Ô<Ð<Ð<Ð<Ð<Ð<Ð<��øÝð ð ð Ý—’ØBÝ%¥c¨#¡h¤hÑ/Ô/ñô ð ð ������øøøøðøøøð ˆ<˜Lˆ<Ý(¨Ñ6Ô6ð "Ý—’ØNñô ð ð ˜T�zÐ!ð$ð $ð $ð $ð $ð $ð $ð	"Ý$Ô.¨zÑ:Ô:Ð:Ð:Ð:Ð:Ð:Ð:��øÝð "ð "ð "Ý—’ð#õ &¥c¨#¡h¤hÑ/Ô/ñ	ô ð ð ˜T�zÐ!Ð!Ð!Ð!Ð!Ð!øøøøð"øøøð ˆ<Ø˜�:Ðð +/Ð@�4—:’:˜c‘?”? 2Ô&Ð&°LˆØ�(ˆ?ˆ?Ø˜Ÿš¨¨QÑ/Ô/°Ô3×9Ò9Ñ;Ô;Ñ;ˆCˆCàˆCØ�?Š?˜8Ñ$Ô$ð 	>Ý*¨4°S°]¸FÐCÑCÔCˆEˆEØ�_Š_˜XÑ&Ô&ð 	>Ý*¨4°S°]¸FÐCÑCÔCˆEˆEØ�_Š_˜XÑ&Ô&ð 	>Ý*¨4°S°]¸FÐCÑCÔCˆEˆEå-¨d°HÑ=Ô=ˆEØ�dˆ{Ðs6   ÃC Ã
D(Ã'7D#Ä#D(Å$E? Å?
GÆ	8GÇGÇGr×  Úreply_toÚmetadataúOptional[Dict[str, Any]]rp   c           	   ƒ  ó$  K  — |                       |||¬¦  «        }|                      |¦  «         	 |                      |                      |¦  «        ¦  «        }|s&t	          dd¬¦  «        |                      |¦  «         S d}| j                             |d¦  «        }|t          k    rd}d}	t          |¦  «        D �]ê\  }
}d|i}|r|
dk    s|sd|i|d	<   	 |
dk    r!|r|  
                    ||¦  «        ƒ d{V —†}d
}	n|                      ||¦  «        ƒ d{V —†}|}Œb# t          $ �r|}t          t          |dd¦  «        dd¦  «        }|dk    rR|                      ddd¬¦  «         t	          dt          |¦  «        ¬¦  «        cY d}~c |                      |¦  «         S |dk    r�|
dk    rB|r@t                                d¦  «         d}|                      ||¦  «        ƒ d{V —†}|}Y d}~�Œ3t                                d¦  «         t	          dd¬¦  «        cY d}~c |                      |¦  «         S |dk    rc| j                             |d¦  «        dz   | j        |<   | j        |         t(          k    r&t                                d| j        |         ¦  «         ‚ ‚ d}~ww xY w|€&t	          dd¬¦  «        |                      |¦  «         S |	rt          | j        |<   ||                      |¦  «         S # |                      |¦  «         w xY w)uŸ  Send a text message.

        Signature matches ``BasePlatformAdapter.send``: ``content`` is the
        message body, ``reply_to`` is an optional message_id (the inbound
        message to thread under), and ``metadata`` may carry ``thread_id``
        (the resolved Google Chat ``spaces/X/threads/Y`` resource name).

        If a typing card is tracked for this chat, transform it in-place via
        ``messages.patch`` â€” NO delete+create. Google Chat shows a tombstone
        ("Message deleted by its author") on delete, which is visual noise.
        Patch rewrites the text of the existing message seamlessly.

        Also pauses the base class's ``_keep_typing`` loop for this chat so
        it can't post a racing typing card between the patch and the reply.

        If ``content`` exceeds MAX_MESSAGE_LENGTH, the first chunk patches
        the typing card (if any), subsequent chunks are new messages.
        ©r  Fzempty message©Úsuccessrì  Nr}   r   rÞ   r9  Trˆ   r2   é“  Úchat_forbiddenz6Bot lacks access (removed from space or perms revoked)rÒ  é”  z:[GoogleChat] Typing card disappeared; creating new messagez&[GoogleChat] send target 404; skippingztarget not foundr|   r1   z8[GoogleChat] Rate limit hit %d times on chat; throttling)Ú_resolve_thread_idÚpause_typing_for_chatÚ_chunk_textræ   rp   Úresume_typing_for_chatr[  rŽ  Ú_TYPING_CONSUMED_SENTINELÚ	enumerateÚ_patch_messager{  r   r9   rê  r-   r  r�  r^  r8   Ú_RATE_LIMIT_WARN_THRESHOLDr  )r'   r  r×  rá  râ  ro  ÚchunksÚlast_resultÚtyping_msg_nameÚpatched_typingÚidxÚchunkr|  r•  rL   r2   s                   r(   ÚsendzGoogleChatAdapter.send	  sæ  è è € ð2 ×+Ò+¨H°hÈÐ+ÑPÔPˆ	Ø×"Ò" 7Ñ+Ô+Ð+ðI	1ð
 ×%Ò% d×&9Ò&9¸'Ñ&BÔ&BÑCÔCˆFØð HÝ!¨%°ÐGÑGÔGðD ×'Ò'¨Ñ0Ô0Ð0Ð0ðA 15ˆKØ"Ô3×7Ò7¸ÀÑFÔFˆOàÕ";Ò;Ð;Ø"&�Ø"ˆNå'¨Ñ/Ô/ð -ñ -‘
��UØ(.° �àð 9 #¨¢' '° 'Ø&,¨iÐ%8�D˜‘Nð(Ø˜a’x�x O�xØ'+×':Ò':¸?ÈDÑ'QÔ'QÐ!QÐ!QÐ!QÐ!QÐ!QÐ!Q˜Ø)-˜˜à'+×';Ò';¸GÀTÑ'JÔ'JÐ!JÐ!JÐ!JÐ!JÐ!JÐ!J˜Ø"(�K�KøÝ ð !ñ !ð !Ý$¥W¨S°&¸$Ñ%?Ô%?ÀÈ4ÑPÔP�FØ ’}�}Ø×-Ò-Ø!1Ø$\Ø&+ð .ñ ô ð õ
  *°%½sÀ3¹x¼xÐHÑHÔHÐHÐHÐHÐHÐHÐHÐHðJ ×'Ò'¨Ñ0Ô0Ð0Ð0ðI  ’}�}ð  !š8˜8¨˜8Ý"ŸKšKØ \ñô ð ð /3˜OØ+/×+?Ò+?ÀÈÑ+NÔ+NÐ%NÐ%NÐ%NÐ%NÐ%NÐ%N˜FØ*0˜KØ$˜H˜H˜H™HÝŸšÐ$LÑMÔMÐMÝ)°%Ð?QÐRÑRÔRÐRÐRÐRÐRÐRÐRÐRð. ×'Ò'¨Ñ0Ô0Ð0Ð0ð-  ’}�}à Ô1×5Ò5°g¸qÑAÔAÀAÑEð Ô-¨gÑ6ð  Ô0°Ô9Õ=WÒWÐWÝ"ŸNšNØ ZØ $Ô 5°gÔ >ñô ð ð ØøøøøðC!øøøðD Ð"Ý!¨%°ÐGÑGÔGð ×'Ò'¨Ñ0Ô0Ð0Ð0ð	 ð KÝ1J�Ô% gÑ.Øà×'Ò'¨Ñ0Ô0Ð0Ð0øˆD×'Ò'¨Ñ0Ô0Ð0Ð0øøøsp   ±:K8 ÂAK8 ÃADÄK8 ÄJ%Ä)AJ ÆJ%ÆK8 Æ!AJ Ç)K8 Ç/*J ÈJ%ÈK8 È6A*J Ê J%Ê%K8 ËK8 Ë8Lrÿ   c           	   ƒ  ó^  K  — d|gi}|                       d ||¬¦  «        }|rd|i|d<   	 |                      ||¦  «        ƒ d {V —†}|j        p	d|d         i|_        |S # t          $ r]}t	          t	          |dd ¦  «        dd ¦  «        }t          dt          t          |¦  «        ¦  «        |t          v ¬¦  «        cY d }~S d }~wt          $ r_}t                               d	d
¬¦  «         t          dt          t          |¦  «        ¦  «        t          |¦  «        ¬¦  «        cY d }~S d }~ww xY w)NÚcardsV2rå  rÞ   r9  rˆ   r2   F)rç  rì  rÕ  z[GoogleChat] send_card failedTr²  )rë  r{  Úraw_responser   r9   rp   rª   r-   r�   r    r  r´  r‘   )	r'   r  rÿ   râ  r|  ro  r•  rL   r2   s	            r(   Ú	send_cardzGoogleChatAdapter.send_cardo  s•  è è € ð !*¨D¨6Ð2ˆØ×+Ò+¨D°(ÀGÐ+ÑLÔLˆ	Øð 	1Ø$ iÐ0ˆD�‰Nð	Ø×/Ò/°¸Ñ>Ô>Ð>Ð>Ð>Ð>Ð>Ð>ˆFØ"(Ô"5Ð"U¸)ÀTÈ)Ä_Ð9UˆFÔØˆMøÝð 	ð 	ð 	Ý�W S¨&°$Ñ7Ô7¸À4ÑHÔHˆFÝØÝ'­¨C©¬Ñ1Ô1Ø Õ$<Ð<ðñ ô ð ð ð ð ð ð øøøøõ
 ð 	ð 	ð 	Ý�LŠLÐ8À4ˆLÑHÔHÐHÝØÝ'­¨C©¬Ñ1Ô1Ý-¨cÑ2Ô2ðñ ô ð ð ð ð ð ð øøøøð	øøøs1   ª3A Á
D,Á(AC Â:D,Ã D,ÃAD'Ä!D,Ä'D,ÚquestionÚchoicesúOptional[list]Ú
clarify_idÚsession_keyc           	   ƒ  óÐ  •K  — |s,t          ¦   «                              ||||||¦  «        ƒ d {V —†S g }|D ]d}t          |¦  «                             ¦   «         }	|	sŒ&t	          |	¦  «        dk    r|	n|	d d…         dz   }
|                     |
d||	dœdœ¦  «         Œe|                     dd|ddœdœ¦  «         |s,t          ¦   «                              ||||||¦  «        ƒ d {V —†S t          d	|› �d
diddd|› �dœd|dœgigdœ¦  «        }|                      |||¬¦  «        ƒ d {V —†}|j        r|| j	        |<   |S t          ¦   «                              ||||||¦  «        ƒ d {V —†S )NéP   éM   z...Úhermes_clarify)r  Úchoice)r}   rÀ   rÁ   zOther / type answerÚ	__other__zclarify-rö   ÚQuestionrò   r}   u   â�“ )rÎ   r}   r   )rÎ   r   )rý   rõ   rñ   )râ  )
rK  Úsend_clarifyr-   r»   rî  rè   r  rý  rç  r\  )r'   r  rþ  rÿ  r  r  râ  r   r  Úchoice_textrá   rÿ   r•  rp  s                €r(   r
  zGoogleChatAdapter.send_clarifyŒ  sm  øè è € ð ð 	Ý™œ×-Ò-Ø˜ 7¨J¸ÀXñô ð ð ð ð ð ð ð ð )+ˆØð 	ð 	ˆFÝ˜f™+œ+×+Ò+Ñ-Ô-ˆKØð ØÝ#& {Ñ#3Ô#3°rÒ#9Ð#9�K�K¸{È3ÈBÈ3Ô?OÐRWÑ?WˆEØ�NŠNà!Ø.à&0Ø"-ð#ð #ðð ñ	ô 	ð 	ð 	ð 	�Šà-Ø*à",Ø)ðð ðð ñ		
ô 		
ð 		
ð ð 	Ý™œ×-Ò-Ø˜ 7¨J¸ÀXñô ð ð ð ð ð ð ð õ %à2 jÐ2Ð2Ø" JÐ/ð "Ø%+Ð5F¸HÐ5FÐ5FÐGÐGØ%.¸7ÐCÐCð$ðððð ñ
ô 
ˆð —~’~ g¨t¸h�~ÑGÔGÐGÐGÐGÐGÐGÐGˆØŒ>ð 	Ø.9ˆDÔ 
Ñ+ØˆMÝ‘W”W×)Ò)Ø�X˜w¨
°KÀñ
ô 
ð 
ð 
ð 
ð 
ð 
ð 
ð 	
r*   )Úfinalizer­  r  c          	   ƒ  óx  K  — |st          dd¬¦  «        S t          |¦  «        t          k    r|dt          dz
  …         dz   }	 |                      |d|i¦  «        ƒ d{V —†S # t          $ r�}t          t          |dd¦  «        d	d¦  «        }|d
k    r&| j                             |d¦  «        dz   | j        |<   t          dt          t          |¦  «        ¦  «        ¬¦  «        cY d}~S d}~wt          $ rD}t                               dd¬¦  «         t          dt          |¦  «        ¬¦  «        cY d}~S d}~ww xY w)uJ  Edit a previously sent message via ``messages.patch``.

        Required for the gateway tool-progress + token-streaming pipeline:
        ``GatewayStreamConsumer`` and ``send_progress_messages`` both gate
        on this method being overridden (see gateway/run.py:10199 and
        gateway/stream_consumer.py). Without it, Google Chat shows no
        tool activity (no "ðŸ”� web_searchâ€¦", no progressive token edits).

        ``message_id`` is the Google Chat resource name
        ``spaces/X/messages/Y``. ``finalize`` is unused here â€” Google
        Chat's patch API has no streaming lifecycle state, so the same
        patch closes the stream and any prior edit.

        404 (message gone) and 403 (perms revoked) are reported as
        non-success; the gateway falls back to ``send()`` for the next
        edit cycle.
        Fzmissing message_idræ  Nr1   u   â€¦r}   rˆ   r2   r|   r   z [GoogleChat] edit_message failedTr²  )rp   rî  Ú_MAX_TEXT_LENGTHrñ  r   r9   r^  r8   rª   r-   r    r  r´  )r'   r  r­  r×  r  rL   r2   s          r(   Úedit_messagezGoogleChatAdapter.edit_messageÏ  sŽ  è è € ð2 ð 	IÝ eÐ3GÐHÑHÔHÐHåˆw‰<Œ<Õ*Ò*Ð*ØÐ4Õ 0°1Ñ 4Ð4Ô5¸Ñ=ˆGð	=Ø×,Ò,¨Z¸&À'Ð9JÑKÔKÐKÐKÐKÐKÐKÐKÐKøÝð 	ð 	ð 	Ý�W S¨&°$Ñ7Ô7¸À4ÑHÔHˆFØ˜Š}ˆ}àÔ)×-Ò-¨g°qÑ9Ô9¸AÑ=ð Ô% gÑ.õ ØÕ%6µs¸3±x´xÑ%@Ô%@ðñ ô ð ð ð ð ð ð øøøøõ ð 	=ð 	=ð 	=Ý�LŠLÐ;ÀdˆLÑKÔKÐKÝ eµ3°s±8´8Ð<Ñ<Ô<Ð<Ð<Ð<Ð<Ð<Ð<øøøøð	=øøøs1   ÁA" Á"
D9Á,A6C(Ã"D9Ã(D9Ã59D4Ä.D9Ä4D9c           	   ƒ  ó˜  ‡ ‡K  — ‰sdS dˆˆ fd„}	 t          j        |¦  «        ƒ d{V —† dS # t          $ rj}t          t          |dd¦  «        dd¦  «        }|d	v rY d}~dS t                               d
t          t          |¦  «        ¦  «        ¦  «         Y d}~dS d}~wt          $ r  t                               dd¬¦  «         Y dS w xY w)uÊ  Delete a message â€” used sparingly (deletion creates a tombstone).

        The base contract returns False on unsupported. We do support it,
        but most internal code should prefer ``edit_message`` to avoid the
        "Message deleted by its author" tombstone. Provided so the
        gateway's stream-consumer fallback paths (e.g. removing an aborted
        partial preview) work correctly when explicit deletion is the
        right call.
        Fr   r   c                 óÒ   •— ‰j                              ¦   «                              ¦   «                              ‰ ¬¦  «                             ‰                     ¦   «         ¬¦  «         d S ©N)rÞ   r½  )rM  r¿  ÚmessagesÚdeleterÁ  rÂ  )r­  r'   s   €€r(   Ú
_do_deletez4GoogleChatAdapter.delete_message.<locals>._do_delete		  sP   ø€ à”×%Ò%Ñ'Ô'ß’‘”ß’˜Z�Ñ(Ô(ß’˜d×3Ò3Ñ5Ô5�Ñ6Ô6Ð6Ð6Ð6r*   NTrˆ   r2   >   rè  rê  z&[GoogleChat] delete_message failed: %sz"[GoogleChat] delete_message failedr²  r:  )	rÉ  rÊ  r   r9   r  r´  rª   r-   r    )r'   r  r­  r  rL   r2   s   ` `   r(   Údelete_messagez GoogleChatAdapter.delete_messageü  s$  øøè è € ð ð 	Ø�5ð	ð 	ð 	ð 	ð 	ð 	ð 	ð	ÝÔ# JÑ/Ô/Ð/Ð/Ð/Ð/Ð/Ð/Ð/Ø�4øÝð 	ð 	ð 	Ý�W S¨&°$Ñ7Ô7¸À4ÑHÔHˆFØ˜Ð#Ð#Ø�u�u�u�u�uÝ�LŠLØ8Ý!¥# c¡(¤(Ñ+Ô+ñô ð ð �5�5�5�5�5øøøøÝð 	ð 	ð 	Ý�LŠLÐ=ÈˆLÑMÔMÐMØ�5�5ð	øøøs    ‘- ­
C	·$BÁ!5BÂ)C	ÃC	r4  r|  c              ƒ  óp  ‡ ‡‡‡K  — g }d|v r|                      d¦  «         d|v r|                      d¦  «         d                     |¦  «        pdŠd„ |                     ¦   «         D ¦   «         Šdˆˆˆ ˆfd„}t          j        |¦  «        ƒ d{V —†}t          d	|                     d
‰¦  «        ¬¦  «        S )z8Update a message's text (and optionally cards) in-place.r}   rû  r.  c                ó"   — i | ]\  }}|d v¯	||“ŒS )>   r9  r4   )r›   ÚkÚvs      r(   ú
<dictcomp>z4GoogleChatAdapter._patch_message.<locals>.<dictcomp>-	  s(   € ÐLÐLÐL™t˜q !°qÀÐ7KÐ7K�a˜Ð7KÐ7KÐ7Kr*   r   rP   c                 óÒ   •— ‰j                              ¦   «                              ¦   «                              ‰ ‰‰¬¦  «                             ‰                     ¦   «         ¬¦  «        S )N)rÞ   Ú
updateMaskr|  r½  )rM  r¿  r  ÚpatchrÁ  rÂ  )r4  Ú
patch_bodyr'   Úupdate_masks   €€€€r(   Ú	_do_patchz3GoogleChatAdapter._patch_message.<locals>._do_patch/	  sP   ø€ à”×%Ò%Ñ'Ô'ß’‘”ß’˜L°[Àz�ÑRÔRß’˜d×3Ò3Ñ5Ô5�Ñ6Ô6ð	r*   NTrÞ   ©rç  r­  ©r   rP   )rè   rï  rÉ   rÉ  rÊ  rp   r8   )r'   r4  r|  Úupdate_mask_fieldsr!  rˆ   r  r   s   ``    @@r(   rñ  z GoogleChatAdapter._patch_message!	  sõ   øøøøè è € ð  ÐØ�Tˆ>ˆ>Ø×%Ò% fÑ-Ô-Ð-Ø˜ÐÐØ×%Ò% iÑ0Ô0Ð0Ø—h’hÐ1Ñ2Ô2Ð<°fˆð MÐL t§z¢z¡|¤|ÐLÑLÔLˆ
ð	ð 	ð 	ð 	ð 	ð 	ð 	ð 	ð 	õ Ô& yÑ1Ô1Ð1Ð1Ð1Ð1Ð1Ð1ˆÝ $°4·8²8¸FÀLÑ3QÔ3QÐRÑRÔRÐRr*   r}   ú	List[str]c                ó„  — |sg S t          |¦  «        t          k    r|gS g }|}|ršt          |¦  «        t          k    r|                     |¦  «         nl|                     ddt          ¦  «        }|t          dz  k     rt          }|                     |d |…         ¦  «         ||d …                              ¦   «         }|°š|S )NÚ
r   r  )rî  r  rè   Úrfindrx  )r'   r}   ró  Ú	remainingÚcuts        r(   rí  zGoogleChatAdapter._chunk_text:	  sÑ   € Øð 	ØˆIÝˆt‰9Œ9Õ(Ò(Ð(Ø�6ˆMØˆØˆ	Øð 		1Ý�9‰~Œ~Õ!1Ò1Ð1Ø—’˜iÑ(Ô(Ð(Øà—/’/ $¨Õ+;Ñ<Ô<ˆCØÕ%¨Ñ*Ò*Ð*Ý&�Ø�MŠM˜) D S Dœ/Ñ*Ô*Ð*Ø! # $ $œ×.Ò.Ñ0Ô0ˆIð ð 		1ð ˆr*   u'   [â€‹â€Œâ€�â€Žâ€�â� ï»¿ï¸€-ï¸�ó „€-ó ‡¯]c                ó6  ‡‡‡— |s|S |}i ŠdgŠdˆˆfd„Št          j        dˆfd„|¦  «        }t          j        dˆfd	„|¦  «        }t          j        d
ˆfd„|t           j        ¬¦  «        }t          j        dˆfd„|¦  «        }t          j        dˆfd„|¦  «        }t          j        dˆfd„|¦  «        }| j                             d|¦  «        }t          j        dd|¦  «        }‰                     ¦   «         D ]\  }}|                     ||¦  «        }Œ|S )aÞ  Convert standard Markdown to Google Chat's formatting dialect.

        Google Chat renders a small subset: ``*bold*``, ``_italic_``,
        ``~strikethrough~``, fenced/inline code. Standard Markdown
        constructs (``**bold**``, ``# headers``, ``[text](url)``) do
        not render and need conversion before they reach Chat.

        Code blocks (fenced AND inline) are protected from transformation
        via placeholder substitution so backticks-wrapped content with
        literal asterisks or brackets stays intact. Invisible Unicode
        codepoints that render as tofu in Chat's restricted font stack
        are stripped at the end. Empty/None input passes through.

        Pattern lifted from PR #14965.
        r   r¼   r-   r   c                óJ   •— d‰d         › d�}‰dxx         dz  cc<   | ‰|<   |S )Nz GCr   ú r1   r4   )r¼   r·   ÚcounterÚplaceholderss     €€r(   Ú_phz-GoogleChatAdapter.format_message.<locals>._phz	  s;   ø€ Ø+˜7 1œ:Ð+Ð+Ð+ˆCØ�AˆJˆJŒJ˜!‰OˆJˆJ‰JØ %ˆL˜ÑØˆJr*   z(```(?:[^\n]*\n)?[\s\S]*?```)c                ó@   •—  ‰|                       d¦  «        ¦  «        S ©Nr   ©r�  ©Úmr0  s    €r(   r�  z2GoogleChatAdapter.format_message.<locals>.<lambda>„	  s   ø€ �c�c˜!Ÿ'š' !™*œ*‘o”o€ r*   z	(`[^`]+`)c                ó@   •—  ‰|                       d¦  «        ¦  «        S r2  r3  r4  s    €r(   r�  z2GoogleChatAdapter.format_message.<locals>.<lambda>‡	  s   ø€ ¨c¨c°!·'²'¸!±*´*©o¬o€ r*   z^#{1,6}\s+(.+)$c                ól   •—  ‰d|                       d¦  «                             ¦   «         › d�¦  «        S ©NÚ*r1   )r�  r»   r4  s    €r(   r�  z2GoogleChatAdapter.format_message.<locals>.<lambda>Œ	  s3   ø€ �c�cÐ3˜aŸgšg a™jœj×.Ò.Ñ0Ô0Ð3Ð3Ð3Ñ4Ô4€ r*   )Úflagsz\*\*\*(.+?)\*\*\*c                óH   •—  ‰d|                       d¦  «        › d�¦  «        S )Nz*_r1   z_*r3  r4  s    €r(   r�  z2GoogleChatAdapter.format_message.<locals>.<lambda>”	  s'   ø€ �c�cÐ-˜qŸwšw q™zœzÐ-Ð-Ð-Ñ.Ô.€ r*   z\*\*(.+?)\*\*c                óH   •—  ‰d|                       d¦  «        › d�¦  «        S r8  r3  r4  s    €r(   r�  z2GoogleChatAdapter.format_message.<locals>.<lambda>›	  s'   ø€ �c�cÐ+˜aŸgšg a™jœjÐ+Ð+Ð+Ñ,Ô,€ r*   z\[([^\]]+)\]\(([^)]+)\)c                ót   •—  ‰d|                       d¦  «        › d|                       d¦  «        › d�¦  «        S )NÚ<r  Ú|r1   Ú>r3  r4  s    €r(   r�  z2GoogleChatAdapter.format_message.<locals>.<lambda>¢	  s9   ø€ �c�cÐ8˜aŸgšg a™jœjÐ8Ð8¨1¯7ª7°1©:¬:Ð8Ð8Ð8Ñ9Ô9€ r*   r—   z  +r¤  )r¼   r-   r   r-   )r¨   r©   Ú	MULTILINEÚ_INVISIBLE_RErÉ   r8  )Úclsr×  r}   r·   r¼   r0  r.  r/  s        @@@r(   ræ   z GoogleChatAdapter.format_messageb	  s”  øøø€ ð" ð 	ØˆNàˆØ')ˆØ�#ˆð	ð 	ð 	ð 	ð 	ð 	ð 	õ ŒvØ,Ø%Ð%Ð%Ð%Øñ
ô 
ˆõ
 Œv�lÐ$=Ð$=Ð$=Ð$=¸tÑDÔDˆõ ŒvØØ4Ð4Ð4Ð4ØÝ”,ð	
ñ 
ô 
ˆõ ŒvØ Ø.Ð.Ð.Ð.Øñ
ô 
ˆõ ŒvØØ,Ð,Ð,Ð,Øñ
ô 
ˆõ ŒvØ&Ø9Ð9Ð9Ð9Øñ
ô 
ˆð Ô ×$Ò$ R¨Ñ.Ô.ˆõ Œv�f˜c 4Ñ(Ô(ˆð '×,Ò,Ñ.Ô.ð 	,ð 	,‰JˆC�Ø—<’<  UÑ+Ô+ˆDˆDàˆr*   c                ó¼   — |r-dD ]*}|                      |¦  «        }|rt          |¦  «        c S Œ+|r
d|v rd|vr|S |r| j                              |¦  «        }|r|S dS )uP  Return the Google Chat thread resource name to reply under, or None.

        Priority:
          1. ``metadata['thread_id']`` â€” populated by the gateway's session
             plumbing from ``SessionSource.thread_id`` (the inbound
             ``thread.name``). Canonical path for groups.
          2. ``metadata['thread_name']`` / ``metadata['thread_ts']`` â€” Slack
             precedent aliases that the broader codebase sometimes passes.
          3. ``reply_to`` if it already looks like a thread resource name
             (``spaces/X/threads/Y``). Message names ``spaces/X/messages/Y``
             cannot be converted to threads without an extra API call.
          4. ``self._last_inbound_thread[chat_id]`` â€” Google Chat DMs spawn
             a new thread per top-level user message, and the adapter
             intentionally drops thread_id from the source so the session
             key stays stable. Without this fallback, DM replies would
             land at top-level (a fresh thread separate from the user's),
             visually disconnected from the user's question.
        )ro  r   Ú	thread_tsz	/threads/z
/messages/N)r8   r-   r_  )r'   rá  râ  r  r·   r¼   r<   s          r(   rë  z$GoogleChatAdapter._resolve_thread_id²	  sœ   € ð0 ð 	&Ø@ð &ð &�Ø Ÿš SÑ)Ô)�Øð &Ý˜u™:œ:Ð%Ð%Ð%ð&àð 	˜ xÐ/Ð/°LÈÐ4PÐ4PØˆOØð 	ØÔ.×2Ò2°7Ñ;Ô;ˆFØð Ø�Øˆtr*   c                ó`   — t          | j        t                               d¬¦  «        ¬¦  «        S )a>  Return a fresh AuthorizedHttp.

        googleapiclient's discovery client is NOT thread-safe because httplib2
        shares SSL state between calls. Passing a fresh http= to each
        ``execute()`` avoids record-layer failures when calls run in
        ``asyncio.to_thread`` workers. Cheap (~no network).
        rÒ  rþ  r½  )r   rS  r   ÚHttpr  s    r(   rÂ  z"GoogleChatAdapter._new_authed_http×	  s(   € õ ˜dÔ/µh·m²mÈB°mÑ6OÔ6OÐPÑPÔPÐPr*   zchat-api-call©Úop_nameÚsync_fnúCallable[[], Any]rI  c             ƒ  óÈ  K  — t           }d}t          dt          dz   ¦  «        D �](}	 t          j        |¦  «        ƒ d{V —†c S # t          j        $ r ‚ t          $ rî}|}t          |¦  «        }|r|t          k    r‚ |t          z  t          j	        ¦   «         z  }t          ||z   t          t          z   ¦  «        }	t                               d||t          t          t          |¦  «        ¦  «        |	¦  «         	 t          j        |	¦  «        ƒ d{V —† n# t          j        $ r ‚ w xY wt          |dz  t          ¦  «        }Y d}~�Œ"d}~ww xY w|�|‚t#          |› d�¦  «        ‚)uã  Run ``sync_fn`` in a thread with bounded retry + jittered backoff.

        Wraps a sync Chat API call (typically a ``.execute()``) so transient
        429/5xx/timeout failures don't drop user-visible messages. Permanent
        failures (auth, client errors, validation) bubble up on the first
        attempt â€” see :func:`_is_retryable_error`. Cancellation propagates
        immediately, no extra retries after a CancelledError.

        Pattern lifted from PR #14965.
        Nr1   z<[GoogleChat] %s attempt %d/%d failed (%s); retrying in %.2fsr  z": retry loop exited without result)Ú_RETRY_BASE_DELAYÚrangeÚ_RETRY_MAX_ATTEMPTSrÉ  rÊ  r  r    r‘   Ú_RETRY_JITTERr  r  Ú_RETRY_MAX_DELAYr  r  rª   r-   r  rI   )
r'   rJ  rI  r   Úlast_excr  rL   rÕ  ÚjitterÚwaits
             r(   Ú_call_with_retryz"GoogleChatAdapter._call_with_retryá	  s¯  è è € õ  "ˆØ,0ˆÝ˜QÕ 3°aÑ 7Ñ8Ô8ð 	9ñ 	9ˆGð9Ý$Ô.¨wÑ7Ô7Ð7Ð7Ð7Ð7Ð7Ð7Ð7Ð7Ð7øÝÔ)ð ð ð ØÝð 9ð 9ð 9Ø�Ý/°Ñ4Ô4�	Ø ð  GÕ/BÒ$BÐ$BØØ¥Ñ.µ´±´Ñ@�Ý˜5 6™>Õ+;½mÑ+KÑLÔL�Ý—’ð(à˜WÕ&9Ý%¥c¨#¡h¤hÑ/Ô/°ñ	ô ð ðÝ!œ-¨Ñ-Ô-Ð-Ð-Ð-Ð-Ð-Ð-Ð-Ð-øÝÔ-ð ð ð Øðøøøå˜E A™IÕ'7Ñ8Ô8�����‘øøøøð#9øøøð& ÐØˆNÝ˜gÐIÐIÐIÑJÔJÐJs0   ¨AÁEÁBEÃ9DÄEÄD%Ä%EÅEc              ƒ  óø  ‡ ‡K  — ||dœŠ|                      d¦  «        pi }|                      d¦  «        rd‰d<   dˆˆ fd„}‰                      |d	¬
¦  «        ƒ d{V —†}|                      d¦  «        pi                       d¦  «        pd}|rK|rI	 ‰ j                             ||¦  «         n,# t          $ r t
                               dd¬¦  «         Y nw xY wt          d|                      d¦  «        ¬¦  «        S )u1  POST spaces/{space}/messages via REST, returning SendResult.

        When ``body`` carries ``thread.name``, we MUST pass
        ``messageReplyOption=REPLY_MESSAGE_FALLBACK_TO_NEW_THREAD`` â€”
        otherwise Google Chat silently ignores ``thread.name`` and
        creates a new thread anyway. From the official docs:

            "Default. Starts a new thread. Using this option ignores
             any thread ID or threadKey that's included."

        See https://developers.google.com/workspace/chat/api/reference/rest/v1/spaces.messages/create
        ©r1  r|  r9  rÞ   Ú$REPLY_MESSAGE_FALLBACK_TO_NEW_THREADÚmessageReplyOptionr   rP   c                 ó¼   •—  ‰j                              ¦   «                              ¦   «         j        di ‰ ¤Ž                     ‰                     ¦   «         ¬¦  «        S )Nr½  r4   )rM  r¿  r  ÚcreaterÁ  rÂ  )r/   r'   s   €€r(   Ú
_do_createz5GoogleChatAdapter._create_message.<locals>._do_create(
  s]   ø€ ð�”×%Ò%Ñ'Ô'ß’‘”Üð"ð "à ð"ð "÷ ’˜d×3Ò3Ñ5Ô5�Ñ6Ô6ð	r*   zmessages.createrH  Nr—   ú.[GoogleChat] outbound thread-count incr failedTr²  r"  r#  )r8   rU  rc  r)  r    r  r´  rp   )r'   r  r|  Úthread_metar\  rˆ   Úresp_threadr/   s   `      @r(   r{  z!GoogleChatAdapter._create_message
  sb  øøè è € ð -4¸TÐ!BÐ!BˆØ—h’h˜xÑ(Ô(Ð.¨BˆØ�?Š?˜6Ñ"Ô"ð 	Rð ,RˆFÐ'Ñ(ð	ð 	ð 	ð 	ð 	ð 	ð 	ð ×*Ò*¨:Ð?PÐ*ÑQÔQÐQÐQÐQÐQÐQÐQˆð —x’x Ñ)Ô)Ð/¨R×4Ò4°VÑ<Ô<ÐBÀˆØð 	�{ð 	ðØÔ(×-Ò-¨g°{ÑCÔCÐCÐCøÝð ð ð Ý—’ØDØ!ð ñ ô ð ð ð ðøøøõ
  $°4·8²8¸FÑ3CÔ3CÐDÑDÔDÐDs   ÂB, Â,&CÃCc              ƒ  óD  ‡ ‡‡‡K  — ‰‰ j         v rdS ‰‰ j        v rY	 t          j        ‰ j        ‰                              ¦   «         d¬¦  «        ƒ d{V —† n# t          j        t          f$ r Y nw xY wdS ‰                      d|‰¬¦  «        }dt          ‰ j	        dd¦  «        pdiŠ|rd|i‰d	<   t          j
        ¦   «         Š‰‰ j        ‰<   dˆˆˆˆ fd„}t          j         |¦   «         ¦  «        }	 t          j        |¦  «        ƒ d{V —† dS # t          j        $ r ‚ w xY w)u  Post a visible 'Hermes is thinkingâ€¦' marker message.

        NOT ephemeral (Google Chat has no ephemeral text messages outside
        slash command responses). ``send()`` PATCHes this marker in-place
        with the real response (no deletion tombstone). The typing card is
        either patched by ``send()`` (success) or by
        ``on_processing_complete`` (failure / cancellation).

        IMPORTANT â€” must place the typing card in the user's thread:
        ``messages.patch`` cannot change a message's ``thread`` (it's
        immutable on update). If we create the typing card at top-level
        and the user is replying inside thread T, send() will patch the
        top-level card in place â€” leaving the bot's whole response
        stranded outside the user's thread. We resolve the thread the
        same way send() does.

        IMPORTANT â€” cancellation safety:
        ``base.py``'s ``_keep_typing`` calls this through
        ``asyncio.wait_for(send_typing, timeout=1.5)``. When the
        create-API call takes longer than 1.5s, ``wait_for`` cancels
        ``send_typing`` mid-flight â€” but the underlying ``asyncio.to_thread``
        keeps running and creates a card in Chat that we have NO way to
        track (the storage line never runs). Next ``_keep_typing`` tick
        sees an empty slot and creates a SECOND card. Result: one orphan
        "Hermes is thinkingâ€¦" stuck in chat forever, plus one card that
        gets patched into the reply.

        Fix: reserve the slot with an in-flight ``Event``, run the
        create in a background task, and ``await asyncio.shield`` it.
        Cancellation of THIS coroutine no longer cancels the create â€”
        the task runs to completion and the msg_id lands in the slot
        regardless.
        Nrý  rþ  )rá  râ  r  r}   Útyping_status_textu   Hermes is thinkingâ€¦rÞ   r9  r   r   c               “  ó  •K  — 	 ‰                      ‰‰¦  «        ƒ d {V —†} | j        rS| j        rL‰‰j        vr| j        ‰j        ‰<   n3‰j                             ‰g ¦  «                             | j        ¦  «         n,# t          $ r t           	                    dd¬¦  «         Y nw xY w‰j
                             ‰d ¦  «         ‰                     ¦   «          d S # ‰j
                             ‰d ¦  «         ‰                     ¦   «          w xY w)Nz1[GoogleChat] send_typing background create failedTr²  )r{  rç  r­  r[  re  r%  rè   r    r  r´  rd  rŽ  Úset)r•  r|  r  Ú	completedr'   s    €€€€r(   Ú_create_and_recordz9GoogleChatAdapter.send_typing.<locals>._create_and_recordƒ
  s;  øè è € ð Ø#×3Ò3°G¸TÑBÔBÐBÐBÐBÐBÐBÐB�Ø”>ð 4 fÔ&7ð 4ð  dÔ&;Ð;Ð;Ø9?Ô9J˜Ô-¨gÑ6Ð6ð Ô4×?Ò?Ø# Rñô ç š& Ô!2Ñ3Ô3Ð3øøÝð ð ð Ý—’ØGØ!ð ñ ô ð ð ð ðøøøð Ô*×.Ò.¨w¸Ñ=Ô=Ð=Ø—’‘”���øð Ô*×.Ò.¨w¸Ñ=Ô=Ð=Ø—’‘”��øøøs*   …A6A< Á;C Á<&B%Â"C Â$B%Â%C Ã1D
r:  )r[  rd  rÉ  r  rT  r  ÚKeyErrorrë  r9   r>  ÚEventró  Úshieldr  )r'   r  râ  ro  re  Útaskr|  rd  s   ``    @@r(   Úsend_typingzGoogleChatAdapter.send_typingC
  sÈ  øøøøè è € ðF �dÔ+Ð+Ð+ØˆFØ�dÔ0Ð0Ð0ð
ÝÔ&ØÔ.¨wÔ7×<Ò<Ñ>Ô>Øðñ ô ð ð ð ð ð ð ð ð øõ Ô(­(Ð3ð ð ð Ø�ðøøøàˆFà×+Ò+Ø H°gð ,ñ 
ô 
ˆ	ð •G˜DœKÐ)=¸tÑDÔDð 'Ø&ð 
ˆð ð 	1Ø$ iÐ0ˆD�‰Nå”M‘O”Oˆ	Ø.7ˆÔ" 7Ñ+ð	 ð 	 ð 	 ð 	 ð 	 ð 	 ð 	 ð 	 ð 	 õ4 Ô"Ð#5Ð#5Ñ#7Ô#7Ñ8Ô8ˆð
	Ý”. Ñ&Ô&Ð&Ð&Ð&Ð&Ð&Ð&Ð&Ð&Ð&øÝÔ%ð 	ð 	ð 	ð ð	øøøs   œ9A ÁA/Á.A/Ã2D ÄDc              ƒ  ó–   K  — | j                              |¦  «        }|sdS |t          k    r| j                              |d¦  «         dS dS )u}  Stop the typing indicator â€” NO-OP when a live card is tracked.

        Google Chat has no separate typing API: the "Hermes is thinkingâ€¦"
        marker is a real message that ``send()`` patches in-place with the
        agent's reply. Deleting the marker creates a "Message deleted by
        its author" tombstone, which is visual noise.

        Upstream code (gateway/run.py and gateway/platforms/base.py) calls
        ``stop_typing`` at three moments per turn â€” typically BEFORE
        ``send()`` runs (so deleting the slot would leave ``send()``
        nothing to patch, forcing it to create a fresh message and leaving
        the original card as a tombstone). To fix this without modifying
        upstream contracts, ``stop_typing`` here is intentionally a NO-OP
        when the slot holds a real ``message_name``: the card is left in
        place so ``send()`` can patch it.

        Three cases:
          * Slot empty â†’ nothing to do.
          * Slot holds SENTINEL â†’ ``send()`` already patched the card;
            pop the sentinel so the next turn starts clean.
          * Slot holds a real ``message_name`` â†’ leave it for ``send()``
            to consume. NO-OP.

        Stranded cards on error / cancellation paths (where ``send()``
        never runs) are reaped by ``on_processing_complete`` â€” see that
        hook for the patch-to-final-state cleanup.
        N)r[  r8   rï  rŽ  )r'   r  Úcurrents      r(   Ústop_typingzGoogleChatAdapter.stop_typing©
  sZ   è è € ð8 Ô'×+Ò+¨GÑ4Ô4ˆØð 	ØˆFØÕ/Ò/Ð/ØÔ!×%Ò% g¨tÑ4Ô4Ð4ØˆFàˆr*   rv  rm   Úoutcomero   c              ƒ  óx  K  — |j         €dS |j         j        }	 | j                             |d¦  «        }|rk|t          k    r`|t
          j        k    rdnd}	 |                      |d|i¦  «        ƒ d{V —† n,# t          $ r t           
                    dd¬¦  «         Y nw xY w| j                             |g ¦  «        }|D ]N}	 |                      |ddi¦  «        ƒ d{V —† Œ"# t          $ r  t           
                    d	|d¬¦  «         Y ŒKw xY wdS # t          $ r  t           
                    d
d¬¦  «         Y dS w xY w)u“  Reap typing card(s) after the message-handling cycle ends.

        SUCCESS: ``send()`` set the SENTINEL after patching. Pop it.

        FAILURE / CANCELLED: ``send()`` may not have run, leaving a real
        ``message_name`` in the slot. Patching the card to a final state
        (``"(interrupted)"``) avoids the tombstone that ``messages.delete``
        would create. If ``send()`` did run (e.g. base.py error-send branch
        patched it), the slot holds the SENTINEL â€” pop and exit.

        Orphan cards: when a background ``send_typing`` task creates a
        card AFTER ``send()`` already populated the slot (race window
        when the API call takes longer than _keep_typing's wait_for
        timeout), the orphan id is stashed in ``self._orphan_typing_messages``.
        Patch each orphan with an empty-ish marker so the user doesn't
        see "Hermes is thinkingâ€¦" stuck forever.
        Nz(interrupted)z
(no reply)r}   z9[GoogleChat] on_processing_complete patch fallback failedTr²  õ   Â·z0[GoogleChat] orphan typing-card patch failed: %sz5[GoogleChat] cleanup in on_processing_complete failed)rr  r  r[  rŽ  rï  ro   Ú	CANCELLEDrñ  r    r  r´  re  )r'   rv  rn  r  rl  rá   ÚorphansÚ	orphan_ids           r(   Úon_processing_completez(GoogleChatAdapter.on_processing_completeÎ
  sâ  è è € ð( Œ<ÐØˆFØ”,Ô&ˆð 	ØÔ+×/Ò/°¸Ñ>Ô>ˆGØð ˜7Õ&?Ò?Ð?ð (/Õ2CÔ2MÒ'MÐ'M�O�OØ%ð ðØ×-Ò-¨g¸À°ÑGÔGÐGÐGÐGÐGÐGÐGÐGÐGøÝ ð ð ð Ý—L’LØSØ!%ð !ñ ô ð ð ð ðøøøð Ô2×6Ò6°wÀÑCÔCˆGØ$ð ð �	ðØ×-Ò-¨i¸&À$¸ÑHÔHÐHÐHÐHÐHÐHÐHÐHÐHøÝ ð ð ð Ý—L’LØJØ!¨Dð !ñ ô ð ð ð ðøøøðð øõ ð 	ð 	ð 	Ý�LŠLØGÐRVð ñ ô ð ð ð ð ð	øøøsY   ™<D ÁA5 Á4D Á5&BÂD ÂBÂ"D ÃC ÃD Ã 'D
ÄD Ä	D
Ä
D Ä&D9Ä8D9úOptional[SendResult]c              ƒ  ól  K  — | j                              |¦  «        }|r|t          k    rdS | j                              |d¦  «         	 |                      |d|i¦  «        ƒ d{V —†}t          | j         |<   |S # t
          $ r2}t          t          |dd¦  «        dd¦  «        }|dk    rY d}~dS ‚ d}~ww xY w)uÒ  Patch the tracked typing card with ``text`` (no tombstone).

        Returns ``None`` if there's no real typing card to patch (caller
        should create a new message). Returns the patch result if the
        card was successfully patched. Raises on transient HttpErrors so
        the caller can decide whether to fall back to ``_create_message``.

        Leaves the SENTINEL in place when present: a previous ``send()``
        already consumed the typing card, and the SENTINEL must stay in
        the slot to keep the base class's ``_keep_typing`` loop from
        creating a fresh "Hermes is thinkingâ€¦" card during any subsequent
        attachment send (which would later be reaped as "(no reply)").
        Nr}   rˆ   r2   rê  )r[  r8   rï  rŽ  rñ  r   r9   )r'   r  r}   rl  r•  rL   r2   s          r(   Ú_consume_typing_card_with_textz0GoogleChatAdapter._consume_typing_card_with_text
  sæ   è è € ð  Ô'×+Ò+¨GÑ4Ô4ˆØð 	˜'Õ%>Ò>Ð>Ø�4àÔ×!Ò! '¨4Ñ0Ô0Ð0ð		Ø×.Ò.¨w¸À¸ÑGÔGÐGÐGÐGÐGÐGÐGˆFÝ-FˆDÔ! 'Ñ*ØˆMøÝð 	ð 	ð 	Ý�W S¨&°$Ñ7Ô7¸À4ÑHÔHˆFØ˜Š}ˆ}à�t�t�t�t�tØøøøøð	øøøs   Á.A7 Á7
B3Â&B.Â-B.Â.B3rØ   Úcaptionc           	   ƒ  óÔ  K  — |                       |||¬¦  «        }g }|r|                     |¦  «         |                     |¦  «         d                     |¦  «        }	 |                      ||¦  «        ƒ d{V —†}	|	�|	S d|i}
|rd|i|
d<   |                      ||
¦  «        ƒ d{V —†S # t
          $ r5}t          dt          t          |¦  «        ¦  «        ¬¦  «        cY d}~S d}~ww xY w)	u  Send an inline image via attachment URL (no upload).

        If a typing card is tracked for this chat, patch it in-place with
        the image (caption + URL) â€” same anti-tombstone pattern used by
        ``send()``. Otherwise create a new message.
        rå  r'  Nr}   rÞ   r9  Fræ  )	rë  rè   rï  rw  r{  r   rp   rª   r-   )r'   r  rØ   rx  rá  râ  ro  Ú
text_partsr}   Úpatchedr|  rL   s               r(   Ú
send_imagezGoogleChatAdapter.send_image*  s;  è è € ð ×+Ò+¨H°hÈÐ+ÑPÔPˆ	Ø "ˆ
Øð 	'Ø×Ò˜gÑ&Ô&Ð&Ø×Ò˜)Ñ$Ô$Ð$Ø�yŠy˜Ñ$Ô$ˆð		PØ ×?Ò?ÀÈÑNÔNÐNÐNÐNÐNÐNÐNˆGØÐ"Ø�Ø$*¨D >ˆDØð 5Ø"(¨)Ð!4��X‘Ø×-Ò-¨g°tÑ<Ô<Ð<Ð<Ð<Ð<Ð<Ð<Ð<øÝð 	Pð 	Pð 	PÝ eÕ3DÅSÈÁXÄXÑ3NÔ3NÐOÑOÔOÐOÐOÐOÐOÐOÐOøøøøð	Pøøøs$   ÁB( Á?(B( Â(
C'Â2*C"ÃC'Ã"C'Ú
image_pathr/   c              ‹  ó˜   K  — |                       |||d|                      ||                     d¦  «        |¬¦  «        ¬¦  «        ƒ d {V —†S )Nzimage/*râ  rå  ©Ú	mime_hintro  ©Ú
_send_filerë  r8   )r'   r  r}  rx  rá  r/   s         r(   Úsend_image_filez!GoogleChatAdapter.send_image_fileJ  sp   è è € ð —_’_Ø�Z ØØ×-Ò-¨h¸¿
º
À:Ñ8NÔ8NÐX_Ð-Ñ`Ô`ð %ñ 
ô 
ð 
ð 
ð 
ð 
ð 
ð 
ð 	
r*   Ú	file_pathÚ	file_namec              ‹  óš   K  — |                       |||d |                      ||                     d¦  «        |¬¦  «        |¬¦  «        ƒ d {V —†S )Nrâ  rå  )r€  ro  Úoverride_filenamer�  )r'   r  r„  rx  r…  rá  r/   s          r(   Úsend_documentzGoogleChatAdapter.send_documentX  ss   è è € ð —_’_Ø�Y ØØ×-Ò-¨h¸¿
º
À:Ñ8NÔ8NÐX_Ð-Ñ`Ô`Ø'ð	 %ñ 
ô 
ð 
ð 
ð 
ð 
ð 
ð 
ð 	
r*   Ú
audio_pathc              ‹  ó˜   K  — |                       |||d|                      ||                     d¦  «        |¬¦  «        ¬¦  «        ƒ d {V —†S )Nz	audio/oggrâ  rå  r  r�  )r'   r  r‰  rx  rá  r/   s         r(   Ú
send_voicezGoogleChatAdapter.send_voiceh  óp   è è € ð —_’_Ø�Z Ø!Ø×-Ò-¨h¸¿
º
À:Ñ8NÔ8NÐX_Ð-Ñ`Ô`ð %ñ 
ô 
ð 
ð 
ð 
ð 
ð 
ð 
ð 	
r*   Ú
video_pathc              ‹  ó˜   K  — |                       |||d|                      ||                     d¦  «        |¬¦  «        ¬¦  «        ƒ d {V —†S )Nz	video/mp4râ  rå  r  r�  )r'   r  r�  rx  rá  r/   s         r(   Ú
send_videozGoogleChatAdapter.send_videov  rŒ  r*   Úanimation_urlc              ƒ  óF   K  — |                       |||||¬¦  «        ƒ d{V —†S )zBGoogle Chat has no native animation type; fall back to send_image.)rx  rá  râ  N)r|  )r'   r  r�  rx  rá  râ  s         r(   Úsend_animationz GoogleChatAdapter.send_animation„  sM   è è € ð —_’_Ø�]¨GØ¨ð %ñ 
ô 
ð 
ð 
ð 
ð 
ð 
ð 
ð 	
r*   rL   r   c                ó4   — t          | ¦  «        pd}d|v pd|v S )uþ  Detect Google Chat's media.upload bot-auth rejection.

        Returns True for the canonical ``"doesn't support app
        authentication"`` wording (and the legacy
        ``ACCESS_TOKEN_SCOPE_INSUFFICIENT`` variant some older clients
        still see). Used to flag a misuse â€” calling ``media.upload``
        through the SA-authed Chat API client instead of the user-authed
        one. With correct routing this error should never fire in the
        adapter; it remains as a defensive check.
        r—   z"doesn't support app authenticationÚACCESS_TOKEN_SCOPE_INSUFFICIENTrÃ   )rL   r}   s     r(   Ú_is_app_auth_attachment_errorz/GoogleChatAdapter._is_app_auth_attachment_error¢  s-   € õ �3‰xŒxˆ~˜2ˆà0°DÐ8ð 9Ø0°DÐ8ð	
r*   Ú
__legacy__r5  úOptional[Any]c              ƒ  ó¸  ‡‡	K  — ddl m}mŠm} | j                             |¦  «        }| j                             |¦  «        }|�”|�’	 t          j        |||¦  «        ƒ d{V —†}n.# t          $ r! t                               dd¬¦  «         d}Y nw xY w|€8| j                             |d¦  «         | j                             |d¦  «         dS || j        |<   |S 	 t          j        ||¦  «        ƒ d{V —†Š	‰	€dS t          j        ˆˆ	fd„¦  «        ƒ d{V —†}n.# t          $ r! t                               d|d¬¦  «         Y dS w xY w‰	| j        |<   || j        |<   |S )	až  Get (or build + cache) a user-authed Chat client for ``email``.

        Hits ``self._user_chat_api_by_email`` first; on miss, loads the
        per-user token from disk, refreshes if needed, builds an API
        client, and caches both. Refresh failures evict the slot so the
        next request goes back through the disk path (and ultimately the
        text-notice fallback if the user has revoked).
        r1   )rÝ  rÞ  Úrefresh_or_noneNz+[GoogleChat] cached per-user refresh raisedTr²  c                 ó   •—  ‰ ‰¦  «        S r    r4   )Ú_buildr–  s   €€r(   r�  z;GoogleChatAdapter._load_per_user_chat_api.<locals>.<lambda>Ú  s   ø€ °&°&¸±-´-€ r*   z4[GoogleChat] per-user creds load/build failed for %s)rí  rÝ  rÞ  r™  rQ  r8   rP  rÉ  rÊ  r    r  r´  rŽ  )
r'   r5  Ú_loadÚ_refreshÚ
cached_apiÚcached_credsÚ	refreshedÚapir›  r–  s
           @@r(   Ú_load_per_user_chat_apiz)GoogleChatAdapter._load_per_user_chat_api¶  s  øøè è € ð	
ð 	
ð 	
ð 	
ð 	
ð 	
ð 	
ð 	
ð 	
ð 	
ð Ô1×5Ò5°eÑ<Ô<ˆ
ØÔ0×4Ò4°UÑ;Ô;ˆØÐ! lÐ&>ð!Ý")Ô"3°H¸lÈEÑ"RÔ"RÐRÐRÐRÐRÐRÐR�	�	øÝð !ð !ð !Ý—’ØAÈDð ñ ô ð ð !�	�	�	ð	!øøøð
 Ð ØÔ,×0Ò0°¸Ñ=Ô=Ð=ØÔ)×-Ò-¨e°TÑ:Ô:Ð:Ø�tØ/8ˆDÔ% eÑ,ØÐð
	Ý!Ô+¨E°5Ñ9Ô9Ð9Ð9Ð9Ð9Ð9Ð9ˆEØˆ}Ø�tÝÔ)Ð*?Ð*?Ð*?Ð*?Ð*?Ñ@Ô@Ð@Ð@Ð@Ð@Ð@Ð@ˆCˆCøÝð 	ð 	ð 	Ý�LŠLØFØ ð ñ ô ð ð �4�4ð	øøøð ,1ˆÔ! %Ñ(Ø.1ˆÔ$ UÑ+Øˆ
s*   ÁA% Á%(BÂBÃD Ã9D Ä'EÅEú#Tuple[Optional[Any], Optional[str]]c              ƒ  óŽ  K  — |r!|                       |¦  «        ƒ d{V —†}|�||fS | j        �˜	 ddlm} t	          j        || j        d¦  «        ƒ d{V —†}n.# t          $ r! t           	                    dd¬¦  «         d}Y nw xY w|€*t           
                    d¦  «         d| _        d| _        dS || _        | j        | j        fS dS )	uQ  Resolve the user-authed Chat client for an outbound attachment.

        Lookup order:
          1. Per-user token for ``sender_email`` â€” the asker's identity.
          2. Legacy single-user fallback (``self._user_chat_api``) for
             pre-multi-user installs.
          3. None â€” caller posts the setup-instructions text notice.

        Returns ``(client, identity_label)`` where ``identity_label`` is
        the sanitized email or the literal ``"__legacy__"`` sentinel.
        ``_invalidate_user_creds`` uses the label to evict the right slot
        on auth failure.
        Nr1   )r™  z([GoogleChat] legacy creds refresh raisedTr²  uP   [GoogleChat] legacy user-OAuth refresh returned None â€” evicting fallback creds©NN)r¢  rN  rí  r™  rÉ  rÊ  rO  r    r  r´  r  Ú_LEGACY_USER_IDENTITY)r'   r+  r¡  r�  r   s        r(   Ú_acquire_user_chat_apiz(GoogleChatAdapter._acquire_user_chat_apiæ  sO  è è € ð  ð 	)Ø×4Ò4°\ÑBÔBÐBÐBÐBÐBÐBÐBˆCØˆØ˜LÐ(Ð(àÔÐ*ð!ðð ð ð ð ð õ #*Ô"3Ø˜dÔ4°dñ#ô #ð ð ð ð ð ð �	�	øõ ð !ð !ð !Ý—’Ø>Èð ñ ô ð ð !�	�	�	ð	!øøøð
 Ð Ý—’ð.ñô ð ð *.�Ô&Ø&*�Ô#Ø!�zØ%.ˆDÔ"ØÔ&¨Ô(BÐBÐBàˆzs   ®'A Á(BÂ BÚidentityc                ó°   — |sdS || j         k    rd| _        d| _        dS | j                             |d¦  «         | j                             |d¦  «         dS )uí   Drop creds for ``identity`` after an auth failure.

        ``identity`` comes from ``_acquire_user_chat_api`` â€” either the
        sender email (per-user slot) or ``__legacy__`` for the fallback
        slot. None is a no-op.
        N)r¦  rO  rN  rP  rŽ  rQ  )r'   r¨  s     r(   Ú_invalidate_user_credsz(GoogleChatAdapter._invalidate_user_creds  sl   € ð ð 	ØˆFØ�tÔ1Ò1Ð1Ø%)ˆDÔ"Ø"&ˆDÔØˆFØÔ!×%Ò% h°Ñ5Ô5Ð5ØÔ$×(Ò(¨°4Ñ8Ô8Ð8Ð8Ð8r*   r  r€  r‡  c           	   ƒ  ón  ‡‡‡‡‡‡K  — t           j                             ‰¦  «        st          dd‰› �¬¦  «        S |p t           j                             ‰¦  «        pdŠ|pdŠ| j                             ‰¦  «        }|                      |¦  «        ƒ d{V —†\  Š}‰€ |                      ‰‰‰||¬¦  «        ƒ d{V —†S 	 |  	                    ‰|pd¦  «        ƒ d{V —† n,# t          $ r t                               d	d
¬¦  «         Y nw xY wd ˆˆˆˆˆfd„}		 t          j        |	¦  «        ƒ d{V —†}
n¼# t          $ r¯}t!          t!          |dd¦  «        dd¦  «        }|dv rVt                               d||¦  «         |                      |¦  «         |                      ‰‰‰||¬¦  «        ƒ d{V —†cY d}~S t          dt'          t)          |¦  «        ¦  «        ¬¦  «        cY d}~S d}~ww xY w|
                     d¦  «        }|st          dd¬¦  «        S dd|igi}|r||d<   |rd|i|d<   ‰|dœŠ|rd‰d<   d ˆˆfd„}	 t          j        |¦  «        ƒ d{V —†}|                     d¦  «        pi                      d¦  «        pd}‰rK|rI	 | j                             ‰|¦  «         n,# t          $ r t                               dd
¬¦  «         Y nw xY wt          d
|                     d¦  «        ¬¦  «        S # t          $ r5}t          dt'          t)          |¦  «        ¦  «        ¬¦  «        cY d}~S d}~ww xY w)!uò  Native Chat attachment via user-OAuth media.upload.

        Two-step on the wire: ``media.upload`` then
        ``spaces.messages.create`` with the returned ``attachmentDataRef``.
        BOTH calls go through a user-authed Chat API client â€” the
        SA-authed client is rejected by ``media.upload`` regardless of
        scopes.

        Multi-user routing: the bot looks up the most recent inbound
        sender for this ``chat_id`` and uses THAT user's stored OAuth
        token. Falls back to a legacy single-user token when present
        (for pre-multi-user installs), and to a setup-instructions text
        notice when neither is available.

        Google Chat ``messages.patch`` cannot add an attachment to an
        existing message, so we cannot transform the typing card directly
        into the file message. Instead we patch the typing card with the
        caption (or a single space when none) so it retires without a
        tombstone, then create the attachment message.
        Fzfile not found: ræ  z
upload.binr¦  N)r  r  rß  rx  ro  r¤  z4[GoogleChat] _send_file pre-patch typing-card failedTr²  r   rP   c                 ó¤   •— t          ‰‰d¬¦  «        } ‰                     ¦   «                              ‰d‰i| ¬¦  «                             ¦   «         S )NF)ÚmimetypeÚ	resumablerß  )r1  r|  Ú
media_body)r   rÈ  ÚuploadrÁ  )rÈ  Úchat_apir  rß  r«   r  s    €€€€€r(   Ú_uploadz-GoogleChatAdapter._send_file.<locals>._uploada  sV   ø€ Ý# D°4À5ÐIÑIÔIˆEà—’Ñ Ô ß’Ø"Ø$ hÐ/Ø$ð ñ ô ÷
 ’‘”ðr*   rˆ   r2   >   é‘  rè  u‚   [GoogleChat] media.upload auth failure for identity=%s (token revoked or scope missing) â€” falling back to text notice. Status=%srÁ  z$upload returned no attachmentDataRefr¥  r}   rÞ   r9  rW  rX  rY  c                 óŠ   •—  ‰                       ¦   «                              ¦   «         j        di ‰¤Ž                     ¦   «         S )Nr4   )r¿  r  r[  rÁ  )r±  Úcreate_kwargss   €€r(   Ú_create_with_attachmentz=GoogleChatAdapter._send_file.<locals>._create_with_attachment�  sG   ø€ ð�—’Ñ!Ô!ß’‘”Üð)ð )à'ð)ð )÷ ’‘”ð	r*   r—   r]  r"  r#  )r7  r  r  rp   ÚbasenamerR  r8   r§  Ú_post_attachment_fallbackrw  r    r  r´  rÉ  rÊ  r   r9   r  rª  rª   r-   rc  r)  )r'   r  r  rx  r€  ro  r‡  r+  r¨  r²  Úupload_resprL   r2   Úattachment_refr|  r¶  rˆ   r_  r±  rµ  rß  r«   s    ``               @@@@r(   r‚  zGoogleChatAdapter._send_file%  s  øøøøøøè è € õ: Œw�~Š~˜dÑ#Ô#ð 	NÝ eÐ3LÀdÐ3LÐ3LÐMÑMÔMÐMà$ÐN­¬×(8Ò(8¸Ñ(>Ô(>ÐNÀ,ˆØÐ6Ð6ˆàÔ0×4Ò4°WÑ=Ô=ˆØ#'×#>Ò#>¸|Ñ#LÔ#LÐLÐLÐLÐLÐLÐLÑˆ�(ð ÐØ×7Ò7ØØØ!ØØ#ð 8ñ ô ð ð ð ð ð ð ð ð	Ø×5Ò5°g¸w¸~È#ÑNÔNÐNÐNÐNÐNÐNÐNÐNÐNøÝð 	ð 	ð 	Ý�LŠLØFØð ñ ô ð ð ð ð	øøøð
	ð 
	ð 
	ð 
	ð 
	ð 
	ð 
	ð 
	ð 
	ð 
	ð	Ý 'Ô 1°'Ñ :Ô :Ð:Ð:Ð:Ð:Ð:Ð:ˆKˆKøÝð 	ð 	ð 	Ý�W S¨&°$Ñ7Ô7¸À4ÑHÔHˆFØ˜Ð#Ð#Ý—’ð-à.6¸ñô ð ð
 ×+Ò+¨HÑ5Ô5Ð5Ø!×;Ò;Ø#ØØ%Ø#Ø'ð <ñ ô ð ð ð ð ð ð ð ð ð ð ð ð õ ØÕ%6µs¸3±x´xÑ%@Ô%@ðñ ô ð ð ð ð ð ð øøøøð!	øøøð( %ŸšÐ)<Ñ=Ô=ˆØð 	ÝØØ<ðñ ô ð ð Ð/°Ð@ÐAð 
ˆð ð 	#Ø"ˆD�‰LØð 	1Ø$ iÐ0ˆD�‰Nð 4;ÀDÐ(IÐ(IˆØð 	à6ð Ð.Ñ/ð	ð 	ð 	ð 	ð 	ð 	ð 	ð	Ý Ô*Ð+BÑCÔCÐCÐCÐCÐCÐCÐCˆDð  Ÿ8š8 HÑ-Ô-Ð3°×8Ò8¸Ñ@Ô@ÐFÀBˆKØð ˜;ð ðØÔ,×1Ò1°'¸;ÑGÔGÐGÐGøÝ ð ð ð Ý—L’LØHØ!%ð !ñ ô ð ð ð ðøøøõ
 Ø¨¯ª°&Ñ)9Ô)9ðñ ô ð øõ ð 	ð 	ð 	ÝØÕ%6µs¸3±x´xÑ%@Ô%@ðñ ô ð ð ð ð ð ð øøøøð	øøøs€   Â>C Ã&DÄDÄD/ Ä/
G(Ä9A4G#Æ-G(Æ3*G#ÇG(Ç#G(È>A
K5 Ê	J% Ê$K5 Ê%&KËK5 ËKË&K5 Ë5
L4Ë?*L/Ì)L4Ì/L4rß  c              ƒ  óp  K  — g }|r|                      |¦  «         |                     d|› d�ddd|› d�g¦  «         dd                     |¦  «        i}|rd	|i|d
<   	 |                      ||¦  «        ƒ d{V —† n,# t          $ r t
                               dd¬¦  «         Y nw xY wt          dd¬¦  «        S )aA  Post a text notice when native attachment delivery is unavailable.

        Tells the user that file delivery requires a one-time consent
        flow (``/setup-files``) and reports the local-host path so the
        file isn't lost. Returns ``success=False`` so callers know the
        attachment did not land.
        u   âš ï¸� No he podido adjuntar **z**.u¤   Google Chat sÃ³lo permite adjuntar archivos cuando el bot tiene permiso explÃ­cito tuyo (OAuth de usuario). Es un consentimiento Ãºnico que se hace desde este chat.uD   **Para activarlo:** envÃ­a `/setup-files` y sigue las instrucciones.u-   Mientras tanto el archivo estÃ¡ en el host: `ú`r}   r'  rÞ   r9  Nz3[GoogleChat] attachment fallback notice send failedTr²  FuO   google_chat: native attachment requires user OAuth â€” run /setup-files in chatræ  )rè   rÇ  rï  r{  r    r  r´  rp   )r'   r  r  rß  rx  ro  Úlinesr|  s           r(   r¸  z+GoogleChatAdapter._post_attachment_fallback»  s.  è è € ð ˆØð 	"Ø�LŠL˜Ñ!Ô!Ð!Ø�ŠØ;¨hÐ;Ð;Ð;ð2ð SØC¸DÐCÐCÐCð
ñ 	ô 	ð 	ð !'¨¯	ª	°%Ñ(8Ô(8Ð9ˆØð 	1Ø$ iÐ0ˆD�‰Nð	Ø×&Ò& w°Ñ5Ô5Ð5Ð5Ð5Ð5Ð5Ð5Ð5Ð5øÝð 	ð 	ð 	Ý�LŠLØEØð ñ ô ð ð ð ð	øøøõ
 Øð'ð
ñ 
ô 
ð 	
s   ÁA; Á;&B$Â#B$c           	   ƒ  ó°  ‡ ‡K  — 	 t          j        ˆˆ fd„¦  «        ƒ d{V —†}nR# t          $ rE}t                               dt          t          |¦  «        ¦  «        ¦  «         ‰d‰dœcY d}~S d}~ww xY w|                     d¦  «        p|                     d¦  «        pd                     ¦   «         }|                     d	¦  «        p‰}||d
v rdnd‰dœS )z)Return {name, type, chat_id} for a space.c                 óª   •— ‰j                              ¦   «                              ‰ ¬¦  «                             ‰                     ¦   «         ¬¦  «        S r  )rM  r¿  r8   rÁ  rÂ  )r  r'   s   €€r(   r�  z1GoogleChatAdapter.get_chat_info.<locals>.<lambda>ì  sB   ø€ ˜œ×-Ò-Ñ/Ô/ß’˜'�Ñ"Ô"ß’˜d×3Ò3Ñ5Ô5�Ñ6Ô6ð r*   Nz%[GoogleChat] get_chat_info failed: %sr�  )rÞ   rÎ   r  r=  rÎ   r—   r6  >   rž  rŸ  r   )	rÉ  rÊ  r   r  r´  rª   r-   r8   r5   )r'   r  r�  rL   r;  Údisplays   ``    r(   Úget_chat_infozGoogleChatAdapter.get_chat_infoè  s;  øøè è € ð
	JÝ Ô*ð7ð 7ð 7ð 7ð 7ñô ð ð ð ð ð ð ˆDˆDøõ
 ð 	Jð 	Jð 	JÝ�LŠLØ7Õ9JÍ3ÈsÉ8Ì8Ñ9TÔ9Tñô ð ð $¨WÀÐIÐIÐIÐIÐIÐIÐIÐIøøøøð		Jøøøð
 —h’h˜{Ñ+Ô+ÐE¨t¯xªx¸Ñ/?Ô/?ÐEÀ2×LÒLÑNÔNˆ
Ø—(’(˜=Ñ)Ô)Ð4¨WˆàØ&Ð*BÐBÐB�D�DÈØð
ð 
ð 	
s   †% ¥
A4¯:A/Á)A4Á/A4)r>  ri   ©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"  rP   r#  r-   r   r$  )rÔ  r   r   r   )r"  rP   r   rP   )r_  r-   r   r`  )rö  rP   r"  rP   r   r   r    )
r  r-   ro  r­  rp  r-   r+  r­  r   r   )rö  rP   r"  rP   r   rœ  )r¥  rP   r   r¾  r¥  )
r  r-   r×  r-   rá  r­  râ  rã  r   rp   )r  r-   rÿ   rP   râ  rã  r   rp   )r  r-   rþ  r-   rÿ  r   r  r-   r  r-   râ  rã  r   rp   )
r  r-   r­  r-   r×  r-   r  r   r   rp   )r  r-   r­  r-   r   r   )r4  r-   r|  rP   r   rp   )r}   r-   r   r%  )r×  r-   r   r-   )rá  r­  râ  rã  r  r­  r   r­  )rJ  rK  rI  r-   r   r   )r  r-   r|  rP   r   rp   )r  r-   râ  r   r   r   )r  r-   r   r   )rv  rm   rn  ro   r   r   )r  r-   r}   r-   r   ru  )NNN)r  r-   rØ   r-   rx  r­  rá  r­  râ  rã  r   rp   )r  r-   r}  r-   rx  r­  rá  r­  r/   r   r   rp   )r  r-   r„  r-   rx  r­  r…  r­  rá  r­  r/   r   r   rp   )r  r-   r‰  r-   rx  r­  rá  r­  r/   r   r   rp   )r  r-   r�  r-   rx  r­  rá  r­  r/   r   r   rp   )r  r-   r�  r-   rx  r­  rá  r­  râ  rã  r   rp   )rL   r   r   r   )r5  r-   r   r—  )r+  r­  r   r£  )r¨  r­  r   r   )r  r-   r  r-   rx  r­  r€  r­  ro  r­  r‡  r­  r   rp   )r  r-   r  r-   rß  r-   rx  r­  ro  r­  r   rp   )r  r-   r   rP   )Br?   r@   rA   r;  r  ÚMAX_MESSAGE_LENGTHr  r  r  r)   r‡  r’  Ústaticmethodr—  rž  r¨  r¬  r°  rµ  rÎ  rû  r  rô  rD  r  r^  rm  rT  rt  rq  r±  rù  rý  r
  r  r  rñ  rí  r¨   ÚcompilerB  Úclassmethodræ   rë  rÂ  rU  r{  rj  rm  rt  rw  r|  rƒ  rˆ  r‹  r�  r’  r•  r¦  r¢  r§  rª  r‚  r¸  rÁ  Ú__classcell__)rp  s   @r(   rå   rå   q  sù  ø€ € € € € ðð ð *Ðà ÐØÐØ Ððsð sð sð sð sð sðpGð Gð Gð GðR#
ð #
ð #
ð #
ðP ðRð Rð Rñ „\ðRð ðZð Zð Zñ „\ðZð?ð ?ð ?ð ?ð47ð 7ð 7ð 7ð
ð ð ð ð	\ð 	\ð 	\ð 	\ð%ð %ð %ð %ðT 5:ð eð eð eð eð eð eðN1ð 1ð 1ð 1ð8Eð Eð Eð EðT à13ðmð mð mð mñ „\ðmð^ð ð ð ðBð ð ð ð2ð ð ð ð>$Fð $Fð $Fð $FðV '+ð^ð ^ð ^ð ^ð ^ð@|
ð |
ð |
ð |
ð|qð qð qð qðt #'Ø-1ðd1ð d1ð d1ð d1ð d1ðT .2ð	ð ð ð ð ðH .2ðA
ð A
ð A
ð A
ð A
ð A
ð A
ðR ð+=ð +=ð +=ð +=ð +=ð +=ðZ#ð #ð #ð #ðJSð Sð Sð Sð2ð ð ð ð6 �B”Jð		ñô €Mð ðMð Mð Mñ „[ðMðf "&ð	#ð #ð #ð #ð #ðJQð Qð Qð Qð 'ð	,Kð ,Kð ,Kð ,Kð ,Kð ,Kð\2Eð 2Eð 2Eð 2Eðhdð dð dð dð dðL#ð #ð #ð #ðJ7ð 7ð 7ð 7ðxð ð ð ðH "&Ø"&Ø-1ðPð Pð Pð Pð PðH "&Ø"&ð
ð 
ð 
ð 
ð 
ð$ "&Ø#'Ø"&ð
ð 
ð 
ð 
ð 
ð( "&Ø"&ð
ð 
ð 
ð 
ð 
ð$ "&Ø"&ð
ð 
ð 
ð 
ð 
ð$ "&Ø"&Ø-1ð
ð 
ð 
ð 
ð 
ð< ð
ð 
ð 
ñ „\ð
ð" )Ðð.ð .ð .ð .ð`-ð -ð -ð -ð^9ð 9ð 9ð 9ð, $(Ø+/ðTð Tð Tð Tð Tðl(
ð (
ð (
ð (
ðZ
ð 
ð 
ð 
ð 
ð 
ð 
ð 
r*   rå   r>  ri   c                óÀ   — t          | di ¦  «        pi }t          |                     d¦  «        p)|                     d¦  «        o|                     d¦  «        ¦  «        S )zCPlugin-side config gate for HTTP callback or Pub/Sub inbound modes.rj  rE  rŠ  r‹  )r9   r   r8   )r>  rj  s     r(   r’  r’    sa   € å�F˜G RÑ(Ô(Ð.¨B€EÝØ�	Š	Ð#Ñ$Ô$ð 	HØ�IŠI�lÑ#Ô#ÐF¨¯	ª	Ð2EÑ(FÔ(Fñô ð r*   c                 ó  — t          ¦   «         sdS t          j        d¦  «        pt          j        d¦  «        } t          j        d¦  «        pt          j        d¦  «        }t          j        d¦  «        }t          |p| o|¦  «        S )u	  ``check_fn`` for the platform registry pass â€” stricter than the
    deps-only ``check_google_chat_requirements``.

    The registry pass at ``gateway/config.py:_apply_env_overrides`` adds
    the platform to ``cfg.platforms`` whenever ``check_fn`` returns True.
    For backward compat with the pre-plugin behavior, we ALSO require
    the minimum Pub/Sub env vars so an unconfigured user doesn't
    accidentally see ``google_chat`` enabled. This matches the legacy
    ``if gc_project and gc_subscription`` gate.
    FÚGOOGLE_CHAT_PROJECT_IDÚGOOGLE_CLOUD_PROJECTÚGOOGLE_CHAT_SUBSCRIPTION_NAMEÚGOOGLE_CHAT_SUBSCRIPTIONrF  )r“   r7  rf  r   )rŒ  r�  rE  s      r(   Ú_check_for_registryrÎ    s�   € õ *Ñ+Ô+ð Øˆuå
Œ	Ð*Ñ+Ô+ð 	-ÝŒ9Ð+Ñ,Ô,ð õ
 	Œ	Ð1Ñ2Ô2ð 	1ÝŒ9Ð/Ñ0Ô0ð õ ”iÐ =Ñ>Ô>€OÝ�Ð= GÐ$<°Ñ>Ô>Ð>r*   c                ó\   — t          t          | dd¦  «        ¦  «        ot          | ¦  «        S )z7``GatewayConfig.get_connected_platforms()`` polls this.ÚenabledF)r   r9   r’  )r>  s    r(   Ú_is_connectedrÑ  %  s*   € å•˜ 	¨5Ñ1Ô1Ñ2Ô2ÐOÕ7GÈÑ7OÔ7OÐOr*   rã  c                 ó8  — t          j        d¦  «        pt          j        d¦  «        } t          j        d¦  «        pt          j        d¦  «        }t          j        d¦  «        }|s| r|sdS i }| r| |d<   |r||d<   |r||d	<   t          j        d
¦  «        }|r||d<   t          j        d¦  «        }|r||d<   t          j        d¦  «        pt          j        d¦  «        }|r||d<   t          j        d¦  «        }|r|t          j        dd¦  «        dœ|d<   |S )u�  Seed ``PlatformConfig.extra`` from env vars during
    ``_apply_env_overrides``.

    The registry's env-enablement hook is called BEFORE the adapter is
    constructed, so ``gateway status`` and ``get_connected_platforms()``
    reflect env-only configuration without instantiating the Pub/Sub client.
    Returns ``None`` when the required Pub/Sub project/subscription aren't
    set; the caller then skips auto-enabling the platform.

    The special ``home_channel`` key in the returned dict is handled by the
    core hook â€” it becomes a proper ``HomeChannel`` dataclass on the
    ``PlatformConfig`` rather than being merged into ``extra``.
    rÊ  rË  rÌ  rÍ  rF  NrŠ  r‹  rE  rH  rG  rJ  rI  Ú GOOGLE_CHAT_SERVICE_ACCOUNT_JSONrs  rr  ÚGOOGLE_CHAT_HOME_CHANNELÚGOOGLE_CHAT_HOME_CHANNEL_NAMEÚHome)r  rÞ   rÆ  )r7  rf  )rŒ  r�  rE  ÚseedrG  Úhttp_events_sa_emailÚsa_jsonrb  s           r(   Ú_env_enablementrÚ  *  sƒ  € õ 	Œ	Ð*Ñ+Ô+ð 	-ÝŒ9Ð+Ñ,Ô,ð õ
 	Œ	Ð1Ñ2Ô2ð 	1ÝŒ9Ð/Ñ0Ô0ð õ ”iÐ =Ñ>Ô>€OØð  ð ¨Lð ØˆtØ€DØð %Ø$ˆˆ\ÑØð 1Ø$0ˆÐ Ñ!Øð 2Ø"1ˆÐÑÝœ9Ð%GÑHÔHÐØð <Ø';ˆÐ#Ñ$Ýœ9Ð%TÑUÔUÐØð IØ4HˆÐ0Ñ1å
Œ	Ð4Ñ5Ô5ð 	7ÝŒ9Ð5Ñ6Ô6ð ð ð /Ø'.ˆÐ#Ñ$ÝŒ9Ð/Ñ0Ô0€DØð 
àÝ”IÐ=¸vÑFÔFð 
ð  
ˆˆ^Ñð €Kr*   r   c                 óú  — ddl m} m}m}m}m} ddlm}m}  |d¦  «        }|r | d|› d�¦  «          |dd¦  «        sd	S  | d
¦  «          | d¦  «          | d¦  «          | d¦  «          | d¦  «          | d¦  «          | d¦  «          | d¦  «          | d¦  «          | d¦  «          | d¦  «          | d¦  «          | d¦  «          | d¦  «          | d¦  «          |d |d¦  «        pd¬¦  «        }|s |d¦  «         d	S  |d| 	                    ¦   «         ¦  «          |d |d¦  «        pd¬¦  «        }	|	s |d¦  «         d	S  |d|	 	                    ¦   «         ¦  «          |d |d¦  «        pdd ¬!¦  «        }
|
r |d|
 	                    ¦   «         ¦  «          |d"d ¦  «        rS |d# |d$¦  «        pd¬¦  «        }|r, |d$| 
                    d%d¦  «        ¦  «          |d&¦  «         n$ |d$d¦  «         n |d'd(¦  «          |d)¦  «          |d* |d+¦  «        pd¬¦  «        }|r |d+| 	                    ¦   «         ¦  «         t          ¦   «           |d,¦  «          | d-¦  «         d	S ).a’  Walk the user through Google Chat configuration via ``hermes setup``.

    The setup wizard at ``hermes_cli/gateway.py`` calls this for plugin
    platforms instead of using the in-tree ``_PLATFORMS`` data block. The
    flow mirrors the in-tree built-ins: print the GCP setup instructions,
    prompt for env vars, persist them to ``~/.hermes/.env`` so the next
    gateway restart picks them up.
    r   )Ú
print_infoÚprint_successÚprint_warningÚpromptÚprompt_yes_no)Úget_env_valueÚsave_env_valuerÌ  z/Google Chat: already configured (subscription: ú)zReconfigure Google Chat?FNz@Google Chat needs a GCP project, a Pub/Sub topic + subscription,zBand a Service Account with Pub/Sub Subscriber on the subscription.zWalkthrough:zP  1. Create or select a GCP project; enable Google Chat API + Cloud Pub/Sub API.zA  2. Create a Service Account (no project-level IAM role needed).zN  3. Create a Pub/Sub topic (e.g. hermes-chat-events) and a Pull subscription.zU  4. On the TOPIC: add chat-api-push@system.gserviceaccount.com as Pub/Sub Publisher.zH  5. On the SUBSCRIPTION: grant your Service Account Pub/Sub Subscriber.z+  6. Download the Service Account JSON key.uK     7. Google Chat API console â†’ Configuration: connection = Cloud Pub/Sub,zA     point at the topic, enable 1:1 + group, restrict visibility.zP  8. Install the bot in a space (fires ADDED_TO_SPACE and resolves its user_id).r—   z<Full guide: website/docs/user-guide/messaging/google_chat.mdz GCP project ID (e.g. my-project)rÊ  )r€  u5   Project ID is required â€” skipping Google Chat setupz:Pub/Sub subscription (projects/<proj>/subscriptions/<sub>)u7   Subscription is required â€” skipping Google Chat setupz-Path to Service Account JSON (or inline JSON)rÓ  T)r€  Úpasswordz0Restrict access to specific users? (recommended)z%Allowed user emails (comma-separated)ÚGOOGLE_CHAT_ALLOWED_USERSr¤  zAllowlist configuredÚGOOGLE_CHAT_ALLOW_ALL_USERSÚtrueuA   âš ï¸�  Open access â€” anyone who can DM the bot can command it.zFHome space for cron/notification delivery (e.g. spaces/AAAA, or empty)rÔ  z1Google Chat configuration saved to ~/.hermes/.envz+Restart the gateway: hermes gateway restart)Úhermes_cli.cli_outputrÜ  rÝ  rÞ  rß  rà  Úhermes_cli.configrá  râ  r»   r8  Úprint)rÜ  rÝ  rÞ  rß  rà  rá  râ  Úexisting_subrŒ  r�  r‚  Úallowedrb  s                r(   Úinteractive_setuprí  _  s  € ðð ð ð ð ð ð ð ð ð ð ð ð ð ð @Ð?Ð?Ð?Ð?Ð?Ð?Ð?à �=Ð!@ÑAÔA€LØð Øˆ
ÐTÀ\ÐTÐTÐTÑUÔUÐUØˆ}Ð7¸Ñ?Ô?ð 	ØˆFà€JÐQÑRÔRÐRØ€JÐSÑTÔTÐTØ€Jˆ~ÑÔÐØ€JÐaÑbÔbÐbØ€JÐRÑSÔSÐSØ€JÐ_Ñ`Ô`Ð`Ø€JÐfÑgÔgÐgØ€JÐYÑZÔZÐZØ€JÐ<Ñ=Ô=Ð=Ø€JÐ\Ñ]Ô]Ð]Ø€JÐRÑSÔSÐSØ€JÐaÑbÔbÐbØ€Jˆr�N„N€NØ€JÐMÑNÔNÐNØ€Jˆr�N„N€NàˆfØ*Ø�Ð6Ñ7Ô7Ð=¸2ðñ ô €Gð ð ØˆÐMÑNÔNÐNØˆØ€NÐ+¨W¯]ª]©_¬_Ñ=Ô=Ð=à�6ØDØ�Ð=Ñ>Ô>ÐDÀ"ðñ ô €Lð ð ØˆÐOÑPÔPÐPØˆØ€NÐ2°L×4FÒ4FÑ4HÔ4HÑIÔIÐIàˆfØ7Ø�Ð@ÑAÔAÐGÀRØðñ ô €Gð
 ð LØˆÐ9¸7¿=º=¹?¼?ÑKÔKÐKà€}ÐGÈÑNÔNð [Ø�&Ø3Ø!�MÐ"=Ñ>Ô>ÐDÀ"ð
ñ 
ô 
ˆð ð 	<ØˆNÐ6¸¿ºÈÈRÑ8PÔ8PÑQÔQÐQØˆMÐ0Ñ1Ô1Ð1Ð1àˆNÐ6¸Ñ;Ô;Ð;Ð;àˆÐ4°fÑ=Ô=Ð=ØˆÐYÑZÔZÐZàˆ6ØPØ�Ð8Ñ9Ô9Ð?¸Rðñ ô €Dð ð AØˆÐ1°4·:²:±<´<Ñ@Ô@Ð@å	�G„G€GØ€MÐEÑFÔFÐFØ€JÐ<Ñ=Ô=Ð=Ð=Ð=r*   z!^(?:spaces|users)/[A-Za-z0-9_-]+$)ro  Úmedia_filesÚforce_documentr  rÔ  ro  r­  rî  úOptional[List[str]]rï  c          	   ƒ  óœ	  K  — |sddiS t                                |¦  «        sdd|›d�iS |�t          j        d|¦  «        sdd|›d�iS t          | d	i ¦  «        pi }|                     d
¦  «        p't          j        d¦  «        pt          j        d¦  «        }t          €ddiS 	 ddlm	} n# t          $ r}	dd|	› �icY d}	~	S d}	~	ww xY w	 |�r:|                     ¦   «         }
|
                     d¦  «        ra	 t          j        |¦  «        }n## t          j        $ r}dd|› �icY d}~S d}~ww xY wt          j                             |t$          ¬¦  «        }�n
t
          j                             |¦  «        sdd|› �iS 	 t+          |dd¬¦  «        5 }t          j        |¦  «        }ddd¦  «         n# 1 swxY w Y   n## t          j        $ r}dd|› �icY d}~S d}~ww xY wt          j                             |t$          ¬¦  «        }nZ	 ddlm} n# t2          $ r ddicY S w xY w	 |                     t$          ¬¦  «        \  }}n# t          $ r}dd|› �icY d}~S d}~ww xY wn-# t6          j        $ r ‚ t          $ r}	dd|	› �icY d}	~	S d}	~	ww xY w	 t7          j        t7          j        |j         |¦   «         ¦  «        d¬¦  «        ƒ d{V —† nA# t6          j         $ r ddicY S t6          j        $ r ‚ t          $ r}	dd|	› �icY d}	~	S d}	~	ww xY wt          |d d¦  «        }|sdd!iS d"|i}|rd#|i|d$<   d%|› d&�}	 ddl!}n# t2          $ r dd'icY S w xY w	 | "                    | #                    d(¬)¦  «        d*¬+¦  «        4 ƒd{V —†}| $                    ||d,|› �d-d.œ¬/¦  «        4 ƒd{V —†	 }|j%        d0k    rU| &                    ¦   «         ƒ d{V —†}dd1|j%        › d2|dd3…         › �icddd¦  «        ƒd{V —† cddd¦  «        ƒd{V —† S |                     ¦   «         ƒ d{V —†}ddd¦  «        ƒd{V —† n# 1 ƒd{V —†swxY w Y   ddd¦  «        ƒd{V —† n# 1 ƒd{V —†swxY w Y   d*|                     d#¦  «        d4œS # t6          j        $ r ‚ t          $ r-}	tN           (                    d5d*¬6¦  «         dd7|	› �icY d}	~	S d}	~	ww xY w)8a+  POST a single Google Chat message via the REST API without the SDK.

    Used by ``tools/send_message_tool._send_via_adapter`` when the gateway
    runner is not in this process (e.g. ``hermes cron`` running as a
    separate process from ``hermes gateway``).  Without this hook,
    ``deliver=google_chat`` cron jobs fail with ``No live adapter for
    platform``.

    Configuration: requires service-account credentials via
    ``GOOGLE_CHAT_SERVICE_ACCOUNT_JSON``, ``GOOGLE_APPLICATION_CREDENTIALS``,
    or Application Default Credentials, and a space resource name as
    ``chat_id`` (e.g. ``spaces/AAAA-BBBB`` or ``users/<id>``).

    Security: ``chat_id`` is validated against the documented Google Chat
    resource-name character set before substitution into the REST URL so
    a tampered value cannot path-traverse or query-inject.

    ``media_files`` and ``force_document`` are accepted for signature
    parity but are not implemented for the standalone path; messages with
    attachments send as text-only.  The live adapter handles attachments.
    rì  zAGoogle Chat standalone send: chat_id (space resource) is requiredz%Google Chat standalone send: chat_id zK must match 'spaces/<id>' or 'users/<id>' with only [A-Za-z0-9_-] in the idNz.^spaces/[A-Za-z0-9_-]+/threads/[A-Za-z0-9_-]+$z'Google Chat standalone send: thread_id z& must match 'spaces/<id>/threads/<id>'rj  rr  rÓ  rs  z6Google Chat standalone send: google-auth not installedr   )rJ   z8Google Chat standalone send: google-auth import failed: rt  z8Google Chat standalone send: inline SA JSON is invalid: ru  z7Google Chat standalone send: SA JSON file not found at rw  r  r  z6Google Chat standalone send: SA JSON file is invalid: zkGoogle Chat standalone send: no SA credentials configured and google-auth is not installed for ADC fallbackzoGoogle Chat standalone send: no SA credentials configured and Application Default Credentials are unavailable: z5Google Chat standalone send: credential load failed: rÿ  rþ  z4Google Chat standalone send: token refresh timed outz3Google Chat standalone send: token refresh failed: rN   z@Google Chat standalone send: refreshed credentials have no tokenr}   rÞ   r9  zhttps://chat.googleapis.com/v1/z	/messagesz2Google Chat standalone send: aiohttp not installedg      >@)ÚtotalT)r‰   Ú	trust_envrc  zapplication/json)ÚAuthorizationzContent-Type)r  Úheadersi�  z*Google Chat standalone send: API returned z: r   r"  z"Google Chat standalone send raisedr²  z$Google Chat standalone send failed: ))Ú_GCHAT_CHAT_ID_RErŽ  r¨   r9   r8   r7  rf  r   rÓ  rJ   r    rx  r±   r  r  r  ry  rz  r{  r  r  r}  r"  r~  r  rH   r€  rÉ  r  r  rÊ  Úrefreshr  ÚaiohttpÚClientSessionÚClientTimeoutÚpostr2   r}   r  r´  )Úpconfigr  rÔ  ro  rî  rï  rj  Úsa_valueÚ_GoogleAuthRequestÚeÚstrippedr�  rL   r–  rƒ  Ú_google_authr†  rN   r|  r,   Ú_aiohttpÚsessionrˆ   r}   Úpayloads                            r(   Ú_standalone_sendr  Â  sD	  è è € ð< ð ^ØÐ\Ð]Ð]Ý×"Ò" 7Ñ+Ô+ð ØðO°Gð Oð Oð Oðð 	ð Ð¥R¤XÐ._ÐajÑ%kÔ%kÐØð*°ið *ð *ð *ðð 	õ
 �G˜W bÑ)Ô)Ð/¨R€Eà�	Š	Ð(Ñ)Ô)ð 	7ÝŒ9Ð7Ñ8Ô8ð	7åŒ9Ð5Ñ6Ô6ð õ ÐØÐQÐRÐRðYØPÐPÐPÐPÐPÐPÐPøÝð Yð Yð YØÐWÐTUÐWÐWÐXÐXÐXÐXÐXÐXÐXøøøøðYøøøð$VØñ 	Ø—’Ñ(Ô(ˆHØ×"Ò" 3Ñ'Ô'ð iðgÝœ: hÑ/Ô/�D�DøÝÔ+ð gð gð gØ#Ð%eÐ`cÐ%eÐ%eÐfÐfÐfÐfÐfÐfÐføøøøðgøøøå'Ô3×MÒMÈdÕ[gÐMÑhÔh�‘å”w—~’~ hÑ/Ô/ð kØ#Ð%iÐ_gÐ%iÐ%iÐjÐjðeÝ˜h¨°gÐ>Ñ>Ô>ð -À"Ý#œy¨™}œ}˜ð-ð -ð -ñ -ô -ð -ð -ð -ð -ð -ð -øøøð -ð -ð -ð -øøåÔ+ð eð eð eØ#Ð%cÐ^aÐ%cÐ%cÐdÐdÐdÐdÐdÐdÐdøøøøðeøøøå'Ô3×MÒMÈdÕ[gÐMÑhÔh��ðØ2Ð2Ð2Ð2Ð2Ð2Ð2øÝð ð ð ØðHðð ð ð ðøøøð
Ø".×"6Ò"6½lÐ"6Ñ"KÔ"K‘��x�xøÝð ð ð ØðRØLOðRð Rðð ð ð ð ð ð øøøøðøøøøøõ
 Ô!ð ð ð ØÝð Vð Vð VØÐTÐQRÐTÐTÐUÐUÐUÐUÐUÐUÐUøøøøðVøøøð

TÝÔÝÔ˜eœmÐ-?Ð-?Ñ-AÔ-AÑBÔBØð
ñ 
ô 
ð 	
ð 	
ð 	
ð 	
ð 	
ð 	
ð 	
ð 	
øõ Ôð Qð Qð QØÐOÐPÐPÐPÐPÝÔ!ð ð ð ØÝð Tð Tð TØÐRÈqÐRÐRÐSÐSÐSÐSÐSÐSÐSøøøøðTøøøõ �E˜7 DÑ)Ô)€EØð ]ØÐ[Ð\Ð\à" GÐ,€DØð -Ø  )Ð,ˆˆX‰à
>¨GÐ
>Ð
>Ð
>€CðOØ"Ð"Ð"Ð"Ð"øÝð Oð Oð OØÐMÐNÐNÐNÐNðOøøøðEØ×)Ò)°(×2HÒ2HÈtÐ2HÑ2TÔ2TÐ`dÐ)ÑeÔeð 	,ð 	,ð 	,ð 	,ð 	,ð 	,ð 	,ÐipØ—|’|ØØà%6¨uÐ%6Ð%6Ø$6ðð ð $ñ ô ð ,ð ,ð ,ð ,ð ,ð ,ð ,ð ,ð Ø”; #Ò%Ð%Ø!%§¢¡¤Ð,Ð,Ð,Ð,Ð,Ð,�DØ#ð7Øœ;ð7ð 7Ø*.¨t°¨t¬*ð7ð 7ðð,ð ,ð ,ð ,ñ ,ô ,ð ,ð ,ð ,ð ,ð ,ð ,ð ,ð	,ð 	,ð 	,ð 	,ñ 	,ô 	,ð 	,ð 	,ð 	,ð 	,ð 	,ð 	,ð 	,ð 	,ð !%§	¢	¡¤Ð+Ð+Ð+Ð+Ð+Ð+�ð,ð ,ð ,ñ ,ô ,ð ,ð ,ð ,ð ,ð ,ð ,ð ,ð ,ð ,ð ,ð ,ð ,ð ,ð ,ð ,ð ,ð ,ð ,øøøð ,ð ,ð ,ð ,ð	,ð 	,ð 	,ñ 	,ô 	,ð 	,ð 	,ð 	,ð 	,ð 	,ð 	,ð 	,ð 	,ð 	,ð 	,ð 	,ð 	,ð 	,ð 	,ð 	,ð 	,ð 	,ð 	,øøøð 	,ð 	,ð 	,ð 	,ð" Ø!Ÿ+š+ fÑ-Ô-ð
ð 
ð 	
øõ Ô!ð ð ð ØÝð Eð Eð EÝ�ŠÐ9ÀDˆÑIÔIÐIØÐCÀÐCÐCÐDÐDÐDÐDÐDÐDÐDøøøøðEøøøsÃ  Â&B- Â-
CÂ7CÂ=CÃCÃ,I$ Ã9D ÄI$ ÄD.ÄD)Ä#D.Ä$I$ Ä)D.Ä.AI$ Æ F? ÆF3Æ'F? Æ3F7Æ7F? Æ:F7Æ;F? Æ>I$ Æ?GÇGÇGÇI$ ÇGÇ*I$ È
H ÈI$ ÈH"ÈI$ È!H"È"I$ È&I ÉI$ É
I ÉIÉI ÉI$ ÉI É I$ É$JÉ=J	ÊJÊ	JÊ<K ËLË$LË<LÌLÌLÌ;M  Í MÍMÍ1R Î%QÎ,<P9Ï(QÏ:R ÐP9Ð'QÐ9
Q	ÑQÑQ	ÑQÑ
R Ñ
Q&Ñ&R Ñ)Q&Ñ*R ÒSÒ"SÓ SÓSc                ó”   — |                       ddd„ t          t          t          dgdt          t
          dt          ddd	d
dd¬¦  «         dS )uB  Plugin entry point â€” called by the Hermes plugin system at startup.

    Registers the Google Chat adapter under the ``google_chat`` name.
    The gateway's ``_create_adapter`` consults the platform registry
    BEFORE its built-in if/elif chain, so this registration is what
    drives adapter creation at runtime.
    rj   zGoogle Chatc                ó    — t          | ¦  «        S r    )rå   )Úcfgs    r(   r�  zregister.<locals>.<lambda>e  s   € Õ$5°cÑ$:Ô$:€ r*   rÓ  z2Run `hermes setup` to install Google Chat support.rÔ  rå  ræ  ru   u   ðŸ’¬Tu{  You are on Google Chat. Limited markdown subset is rendered: *bold*, _italic_, ~strike~, `code`. No headings or lists. Message size limit: 4000 characters; longer responses are split across multiple messages. You are in a space (DM or group). Images render inline; audio, video, and document attachments render as download cards (no native voice/video UI). To send files, include MEDIA:/absolute/path/to/file in your response. Native file attachments require the user to run /setup-files once in their own DM â€” until they do, file requests fall back to a text notice with the host path. Do NOT generate interactive Card v2 buttons â€” Google Chat interactivity is not yet supported by this gateway; ask for typed confirmations instead. While you are generating a response, a 'Hermes is thinkingâ€¦' marker message appears in the space and is deleted once your response is ready. You do NOT have access to Google Chat-specific APIs â€” you cannot search space history, list space members, or manage spaces. Do not promise to perform these actions; explain that you can only read messages sent directly to you and respond in the same space/thread.)rÞ   rá   Úadapter_factoryÚcheck_fnÚvalidate_configÚis_connectedÚrequired_envÚinstall_hintÚsetup_fnÚenv_enablement_fnÚcron_deliver_env_varÚstandalone_sender_fnÚallowed_users_envÚallow_all_envÚmax_message_lengthÚemojiÚallow_update_commandÚplatform_hintN)Úregister_platformrÎ  r’  rÑ  rí  rÚ  r  )Úctxs    r(   Úregisterr  Z  sw   € ð ×ÒØØØ:Ð:Ý$Ý(Ý"à.ð
ð JÝ"õ *ð 8õ .à5Ø3ð  ØØ!ððI ñ 8ô 8ð 8ð 8ð 8r*   rÂ  )rN   r-   rO   r-   r   rP   )r   r   )rL   r†   r   r   )r,   r-   r   r   )r}   r-   r   r-   )r«   r-   r   rn   )r¶   rP   r·   r-   r¸   r-   r   r-   )r¾   rP   r   rP   )rÌ   rP   r   rP   )rï   rP   r   rP   )r>  ri   r   r   )r   rã  r:  )r  r-   rÔ  r-   ro  r­  rî  rð  rï  r   r   rP   )[r;  Ú
__future__r   rÉ  r  r¥  r7  r  r¨   r#   r6   Úpathlibr   r  Útypingr   r   r   r   r	   r
   r   Ú__annotations__r   r   r   r   r   r   r    r   r   r   rB   r   r$   rF   r   rM   rU   rg   Úgateway.configrh   ri   Úgateway.platforms.helpersrk   Úgateway.platforms.baserl   rm   rn   ro   rp   rq   rr   rs   rt   Ú	getLoggerr  rÅ  r�  r{  r  rò  rO  rM  rQ  rP  Ú	frozensetr�   rä   r‘   rï  r“   r¤   r¦   rª   rµ   r½   rË   rî   r  r  rå   r’  rÎ  rÑ  rÚ  rí  rö  r  r  r4   r*   r(   ú<module>r%     sQ  ðð#ð #ð #ðJ #Ð "Ð "Ð "Ð "Ð "à €€€Ø €€€Ø €€€Ø 	€	€	€	Ø €€€Ø 	€	€	€	Ø Ð Ð Ð Ø €€€Ø !Ð !Ð !Ð !Ð !Ð !Ø =Ð =Ð =Ð =Ð =Ð =Ð =Ð =Ð =Ð =Ð =Ð =Ð =Ð =Ð =Ð =ð  $Ð Ð #Ð #Ð #Ñ #Ø€Ð Ð Ð Ñ Ø€	Ð Ð Ð Ñ Ø€Ð Ð Ð Ñ Ø€Ð Ð Ð Ñ Ø€Ð Ð Ð Ñ Ø€Ð Ð Ð Ñ Ø€	Ð Ð Ð Ñ Ø€Ð Ð Ð Ñ à$Ð Ð $Ð $Ð $Ñ $Ø%(Ð "Ø $Ð Ð $Ð $Ð $Ñ $Ø . 	¤Ñ 0Ô 0Ð ðð ð ð ð ñ ô ð ð2	(ð 	(ð 	(ð 	(ð
ð 
ð 
ð 
ð)ð )ð )ð )ðV 4Ð 3Ð 3Ð 3Ð 3Ð 3Ð 3Ð 3ð 	€ˆÑ Ô Ð Ø 9Ð 9Ð 9Ð 9Ð 9Ð 9ð
ð 
ð 
ð 
ð 
ð 
ð 
ð 
ð 
ð 
ð 
ð 
ð 
ð 
ð 
ð 
ð 
ð 
ð 
ð 
ð 
ð 
ð& 
ˆÔ	Ð:Ñ	;Ô	;€ð #˜œ
ØAñô Ð ð  /Ø,ð€ð Ð ð Ð ð Ð ØÐ ØÐ Ø€Ø$˜9Ð%>Ð%>Ð%>Ñ?Ô?Ð Ø�Yð 
 ð 
 ð 
 ñ 
ô 
Ð ðð ð ð ðJ )Ð ð"ð "ð "ð "ð 	Ð ðWð Wð Wð Wð ð ð ð ð6 ð  ð  ð  ð"ð ð ð ðð ð ð ð =@ð =@ð =@ð =@ð@%Tð %Tð %Tð %TðPkð kð kð kð kñ kô kð kð\J*
ð J*
ð J*
ð J*
ð J*
Ð+ñ J*
ô J*
ð J*
ðdTð ð ð ð?ð ?ð ?ð ?ð2Pð Pð Pð Pð
2ð 2ð 2ð 2ðjY>ð Y>ð Y>ð Y>ð@ �B”JÐCÑDÔDÐ ð  $Ø'+Ø ðUEð UEð UEð UEð UEð UEðp@ð @ð @ð @ð @ð @r*   