§
    øžyj‹á ã                  ó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ZddlZddlZddlZddlmZmZ ddlmZ ddlmZmZmZmZmZ ddlmZ erddlZdZn	 ddlZdZn# e$ r d	ZdZY nw xY wdd
lmZm Z  ddl!m"Z"m#Z#m$Z$m%Z%m&Z& ddl'm(Z(m)Z) ddl*m+Z+ ddl,m-Z. ddl,m/Z0 d€d„Z1 ej2        e3¦  «        Z4dZ5dZ6dZ7dZ8d�d„Z9d‚d„Z:dƒd!„Z;d„d"„Z<d…d%„Z=d&Z>d'Z?d(Z@dd)lAmBZCmDZD daEd*eFd+<   dZGd*eFd,<   d-ZHd�d.„ZId�d/„ZJd0ZKd1ZLd2ZMd3ZNd4ZOd5d6gZP G d7„ d8eQ¦  «        ZR	 d€d†d=„ZSd>ZTd‡dA„ZUdˆdB„ZVd‰dD„ZWd‡dE„ZXdŠdG„ZYd‹dJ„ZZdˆdK„Z[dˆdL„Z\d„dM„Z]dŒdP„Z^dŒdQ„Z_d�dS„Z`dˆdT„ZadŽdV„ZbdŽdW„Zcd�dZ„Zdd�d[„Zed‘d]„Zfd�d^„Zgd’d_„Zh G d`„ dae"¦  «        Zid“dd„ZjdedfdgdhdededediœZkdjdjdkdldmdndndoœZld	dpœd”ds„Zmd•du„Znddd	dvœd–d~„Zod„d„ZpdS )—u•  
Photon Spectrum (iMessage) platform adapter for Hermes Agent.

Both directions of traffic flow through a small supervised Node sidecar
(see ``sidecar/index.mjs``) that runs the ``spectrum-ts`` SDK â€” the SDK is
TypeScript-only and there is no public HTTP message API, so a sidecar is
unavoidable.

Inbound:
    The SDK's ``app.messages`` is a long-lived **gRPC** stream. The sidecar
    serializes each message to a normalized JSON event and streams it to this
    adapter over a loopback ``GET /inbound`` (NDJSON). A background task here
    consumes that stream, dedupes on ``messageId``, and dispatches a
    ``MessageEvent`` to the gateway via ``BasePlatformAdapter.handle_message``.
    No webhook, no public URL, no signing secret.

Outbound:
    ``send`` / ``send_typing`` are loopback POSTs to the sidecar's control
    endpoints, authenticated with a shared bearer token. URL-only messages can
    route through spectrum-ts' ``richlink()`` builder via ``/send-richlink``.
    Outbound media (images, voice notes, video, documents) goes through
    spectrum-ts' ``attachment()`` / ``voice()`` content builders via the
    sidecar's ``/send-attachment`` endpoint.
é    )ÚannotationsN)ÚdatetimeÚtimezone)ÚPath)ÚTYPE_CHECKINGÚAnyÚDictÚListÚOptional)ÚurlparseTF)ÚPlatformÚPlatformConfig)ÚBasePlatformAdapterÚMessageEventÚMessageTypeÚProcessingOutcomeÚ
SendResult)Úcompile_mention_patternsÚstrip_markdowné   )Úload_project_credentials)ÚUnscopedSecretError)Ú
get_secretc                óz   — 	 t          | |¦  «        }n$# t          $ r t          j        | ¦  «        }Y nw xY w|�|n|S )a¹  Scope-aware credential read with the default-profile startup fallback.

    Secondary profiles construct their adapters under a profile secret
    scope -- the scope is authoritative and a scoped miss returns ``default``
    (no cross-profile borrow from ``os.environ``, which may hold another
    profile's value). The DEFAULT profile's adapter constructs and sends
    *unscoped* under multiplexing, where a bare ``get_secret`` would raise
    ``UnscopedSecretError`` and crash this path; there ``os.environ`` is that
    profile's own value, so fall back to it. Same pattern as the Slack
    ``SLACK_APP_TOKEN`` read (#59739) and
    ``gateway/platforms/whatsapp_common.py::_get_wsecret``.
    )Ú_scoped_get_secretÚ_UnscopedSecretErrorÚosÚgetenv)ÚnameÚdefaultÚvals      úF/home/ragecks/.hermes/hermes-agent/plugins/platforms/photon/adapter.pyÚ_get_scoped_secretr#   J   sR   € ðÝ   wÑ/Ô/ˆˆøÝð ð ð ÝŒi˜‰oŒoˆˆˆðøøøà�/ˆ3ˆ3 wÐ.s   ‚ “4³4iU"  z	127.0.0.1i@  zphoton-sidecar.jsonÚreturnr   c                 ó8   — ddl m}   | ¦   «         dz  t          z  S )Nr   ©Úget_hermes_homeÚruntime)Úhermes_constantsr'   Ú_RUNTIME_RECORD_NAMEr&   s    r"   Ú_runtime_record_pathr+   y   s-   € à0Ð0Ð0Ð0Ð0Ð0àˆ?ÑÔ˜yÑ(Õ+?Ñ?Ð?ó    ÚportÚintÚtokenÚstrÚpidÚNonec                ó˜  — ddl }	 t          ¦   «         }|j                             dd¬¦  «         |                     t          |j        ¦  «        dd¬¦  «        \  }}	 	 t          j        |d¦  «         n# t          $ r Y nw xY wt          j	        |d	d
¬¦  «        5 }t          j        | ||dœ|¦  «         ddd¦  «         n# 1 swxY w Y   t          j        ||¦  «         dS # t          $ r( 	 t          j        |¦  «         n# t          $ r Y nw xY w‚ w xY w# t          $ r&}t                                d|¦  «         Y d}~dS d}~ww xY w)z@Atomically persist ``{port, token, pid}`` with owner-only perms.r   NT)ÚparentsÚexist_okz.photon-sidecar.z.tmp)ÚdirÚprefixÚsuffixi€  Úwúutf-8©Úencoding)r-   r/   r1   z3[photon] failed to write sidecar runtime record: %s)Útempfiler+   ÚparentÚmkdirÚmkstempr0   r   ÚchmodÚOSErrorÚfdopenÚjsonÚdumpÚreplaceÚBaseExceptionÚunlinkÚ	ExceptionÚloggerÚwarning)	r-   r/   r1   r=   ÚpathÚfdÚtmpÚfhÚes	            r"   Ú_write_runtime_recordrQ   €   sñ  € à€O€O€OðQÝ#Ñ%Ô%ˆØŒ×Ò $°ÐÑ6Ô6Ð6Ø×"Ò"Ý�D”KÑ Ô Ð);ÀFð #ñ 
ô 
‰ˆˆCð	ðÝ”˜˜eÑ$Ô$Ð$Ð$øÝð ð ð Ø�ðøøøå”˜2˜s¨WÐ5Ñ5Ô5ð J¸Ý”	 4°%ÀÐDÐDÀbÑIÔIÐIðJð Jð Jñ Jô Jð Jð Jð Jð Jð Jð Jøøøð Jð Jð Jð JåŒJ�s˜DÑ!Ô!Ð!Ð!Ð!øÝð 	ð 	ð 	ðÝ”	˜#‘”��øÝð ð ð Ø�ðøøøàð	øøøøõ ð Qð Qð QÝ�ŠÐLÈaÑPÔPÐPÐPÐPÐPÐPÐPÐPøøøøðQøøøs�   †AD ÁA5 Á4C$ Á5
BÁ?C$ ÂBÂC$ ÂCÂ6C$ ÃCÃC$ Ã	CÃ
C$ Ã$
DÃ/DÄDÄ
DÄDÄDÄDÄD Ä
E	Ä#EÅE	úOptional[Dict[str, Any]]c                 óÐ   — 	 t          j        t          ¦   «                              d¬¦  «        ¦  «        } n# t          t
          f$ r Y d S w xY wt          | t          ¦  «        r| nd S )Nr:   r;   )rD   Úloadsr+   Ú	read_textrB   Ú
ValueErrorÚ
isinstanceÚdict©Úraws    r"   Ú_read_runtime_recordr[   Ÿ   sp   € ðÝŒjÕ-Ñ/Ô/×9Ò9À7Ð9ÑKÔKÑLÔLˆˆøÝ•ZÐ ð ð ð Øˆtˆtðøøøå˜S¥$Ñ'Ô'Ð1ˆ3ˆ3¨TÐ1s   ‚47 ·AÁAc                 ón   — 	 t          ¦   «                              d¬¦  «         d S # t          $ r Y d S w xY w)NT)Ú
missing_ok)r+   rH   rB   © r,   r"   Ú_delete_runtime_recordr_   §   sL   € ðÝÑÔ×%Ò%°Ð%Ñ6Ô6Ð6Ð6Ð6øÝð ð ð Øˆˆðøøøs   ‚"& ¦
4³4r   Úboolc                ój  — 	 t          | ¦  «        }n# t          t          f$ r Y dS w xY w|dk    rdS 	 ddlm} t           ||¦  «        ¦  «        S # t          $ r Y nw xY wt          j        dk    rB	 t          j	        |d¦  «         n)# t          $ r Y dS t          $ r Y dS t          $ r Y dS w xY wdS dS )z8Best-effort liveness check for the recorded sidecar pid.Fr   )Ú_pid_existsÚposixT)r.   Ú	TypeErrorrV   Úgateway.statusrb   r`   rI   r   r   ÚkillÚProcessLookupErrorÚPermissionErrorrB   )r1   Úpid_intrb   s      r"   Ú_sidecar_pid_aliverj   ®   s  € ðÝ�c‘(”(ˆˆøÝ•zÐ"ð ð ð Øˆuˆuðøøøà�!‚|€|Øˆuðà.Ð.Ð.Ð.Ð.Ð.å�K�K Ñ(Ô(Ñ)Ô)Ð)øÝð ð ð Øˆðøøøå	„w�'ÒÐð	ÝŒG�G˜QÑÔÐÐøÝ!ð 	ð 	ð 	Ø�5�5Ýð 	ð 	ð 	Ø�4�4Ýð 	ð 	ð 	Ø�5�5ð	øøøàˆtð ˆ4s;   ‚ ’'¦'³A Á
AÁAÁ2B Â
B.Â	B.Â!	B.Â-B.i   i £ ç      .@)Údir_writableÚresolve_sidecar_dirzOptional[Path]Ú_SIDECAR_DIRÚ_NPM_ERROR_LOGi,  c                 ó:   — t           €t          ¦   «         a t           S )zBSidecar runtime dir, resolved once on first use (never at import).)rn   rm   r^   r,   r"   Ú_sidecar_dirrq   å   s   € õ ÐÝ*Ñ,Ô,ˆÝÐr,   c                 ó@   — t           �t           S t          ¦   «         dz  S )zEPath of the persisted npm-failure log (derived from the sidecar dir).Nz.photon-npm-error.log)ro   rq   r^   r,   r"   Ú_npm_error_logrs   í   s   € åÐ!ÝÐÝ‰>Œ>Ð3Ñ3Ð3r,   iX  )zinternal sidecar errorzupstream connect errorzupstream unavailablezconnection droppedzreset reason: overflowÚupstream_overflowÚupstream_unavailableç      >@z.pluginpayloadattachmentç      @z#(?<![\w@])@?hermes\s+agent\b[,:\-]?z(?<![\w@])@?hermes\b[,:\-]?c                  ó,   ‡ — e Zd ZdZdddœdˆ fd„Zˆ xZS )ÚPhotonSidecarErrorz=Structured failure returned by the supervised Photon sidecar.Úsidecar_errorF©Úerror_classÚ	retryablerL   r0   Ústatus_coder.   Úerrorr|   r}   r`   r$   r2   c               ó®   •— || _         || _        || _        || _        || _        t          ¦   «                              d|› d|› d|› d|› d|› �
¦  «         d S )NzPhoton sidecar z
 returned ú (z, retryable=ú): )rL   r~   r   r|   r}   ÚsuperÚ__init__)ÚselfrL   r~   r   r|   r}   Ú	__class__s         €r"   r„   zPhotonSidecarError.__init__!  s”   ø€ ð ˆŒ	Ø&ˆÔØˆŒ
Ø&ˆÔØ"ˆŒÝ‰Œ×Òð?˜dð ?ð ?¨kð ?ð ?Øð?ð ?Ø)2ð?ð ?Ø7<ð?ð ?ñ	
ô 	
ð 	
ð 	
ð 	
r,   )rL   r0   r~   r.   r   r0   r|   r0   r}   r`   r$   r2   )Ú__name__Ú
__module__Ú__qualname__Ú__doc__r„   Ú__classcell__©r†   s   @r"   ry   ry     sU   ø€ € € € € ØGÐGð +Øð
ð 
ð 
ð 
ð 
ð 
ð 
ð 
ð 
ð 
ð 
ð 
r,   ry   rL   r~   ÚtextÚdatac                ó¶  — |€A	 t          j        |¦  «        }n# t          $ r i }Y nw xY wt          |t          ¦  «        r|ni }t          |                     d¦  «        p|d d…         pd¦  «        }t          |                     d¦  «        pd¦  «        }t          |                     d¦  «        ¦  «        }|dk    r	t          }d}t          | ||||¬	¦  «        S )
Nr   éÈ   zsidecar errorr|   rz   r}   Útarget_not_allowedF)rL   r~   r   r|   r}   )
rD   rT   rI   rW   rX   r0   Úgetr`   Ú_TARGET_NOT_ALLOWED_MESSAGEry   )rL   r~   r�   rŽ   Úparsedr   r|   r}   s           r"   Ú_sidecar_error_from_responser•   5  sû   € ð €|ð	Ý”Z Ñ%Ô%ˆFˆFøÝð 	ð 	ð 	ØˆFˆFˆFð	øøøå# F­DÑ1Ô1Ð9ˆvˆv°rˆå�—’˜Ñ!Ô!ÐB T¨$¨3¨$¤ZÐB°?ÑCÔC€EÝ�d—h’h˜}Ñ-Ô-Ð@°ÑAÔA€KÝ�T—X’X˜kÑ*Ô*Ñ+Ô+€IØÐ*Ò*Ð*õ ,ˆØˆ	ÝØØØØØðñ ô ð s   „ ™(§(u‹   shared/free-tier Photon lines cannot initiate outbound sends to new targets â€” upgrade to a dedicated line or use another delivery channelÚvaluer    c                óT   — 	 t          | ¦  «        S # t          t          f$ r |cY S w xY w©N©r.   rd   rV   ©r–   r    s     r"   Ú_coerce_portr›   Z  ó<   € ðÝ�5‰zŒzÐøÝ•zÐ"ð ð ð Øˆˆˆðøøøó   ‚ ‘'¦'c                 óN   — t          ¦   «         dz  dz                       ¦   «         S )a³  True when spectrum-ts is present under node_modules/.

    Checks the dependency's own directory, not just node_modules/'s
    existence: npm creates node_modules/ before aborting on ENOSPC, a
    network timeout, or EACCES, so an empty/partial node_modules/ would
    otherwise read as "installed". Shared by check_requirements(),
    _start_sidecar(), and `hermes photon status` so all three agree on
    what "installed" means.
    Únode_moduleszspectrum-ts)rq   Úexistsr^   r,   r"   Úsidecar_deps_installedr¡   a  s$   € õ ‰NŒN˜^Ñ+¨mÑ;×CÒCÑEÔEÐEr,   Úfloatc                óT   — 	 t          | ¦  «        S # t          t          f$ r |cY S w xY wr˜   )r¢   rd   rV   rš   s     r"   Ú_coerce_floatr¤   n  s<   € ðÝ�U‰|Œ|ÐøÝ•zÐ"ð ð ð Øˆˆˆðøøør�   c                óT   — 	 t          | ¦  «        S # t          t          f$ r |cY S w xY wr˜   r™   rš   s     r"   Ú_coerce_intr¦   u  rœ   r�   Úvaluesc                 ó   — | D ]}|�|c S Œ	dS )züReturn the first value that is not None.

    Unlike ``a or b``, this preserves an explicit falsy value (e.g. ``0`` or
    ``""``) so config can intentionally set a zero/empty without it silently
    falling through to a later source or a default.
    Nr^   )r§   Úvs     r"   Ú
_first_setrª   |  s+   € ð ð ð ˆØˆ=ØˆHˆHˆHð àˆ4r,   ÚexcrG   c                óú   — t          | t          j        t          f¦  «        rdS t          r*t	          t
          dd¦  «        }|�t          | |¦  «        rdS dt          | ¦  «        j                             ¦   «         v S )z<True when *exc* indicates the request timed out (call hung).TÚTimeoutExceptionNÚtimeout)	rW   ÚasyncioÚTimeoutErrorÚHTTPX_AVAILABLEÚgetattrÚhttpxÚtyper‡   Úlower)r«   Útimeout_excs     r"   Ú_is_timeout_errorr·   ‰  su   € å�#�Ô,­lÐ;Ñ<Ô<ð ØˆtÝð Ý�eÐ%7¸Ñ>Ô>ˆØÐ"¥z°#°{Ñ'CÔ'CÐ"Ø�4Ø�˜S™	œ	Ô*×0Ò0Ñ2Ô2Ð2Ð2r,   c                 ó*  — t           st                               d¦  «         dS t          j        t          j        d¦  «        pd¦  «        s1t                               dt          j        d¦  «        pd¦  «         dS t          ¦   «         �st          t          j        d¦  «        ¦  «        rt          t          ¦   «         ¦  «        rdS d} 	 t          ¦   «                              ¦   «         rAt          ¦   «                              d	¬
¦  «                             ¦   «         dt          …         } n# t           $ r Y nw xY w| r)t                               dt          ¦   «         | ¦  «         n't                               dt          ¦   «         ¦  «         dS dS )zEReturn True when both Python deps and the Node sidecar are available.u1   photon: httpx not installed â€” pip install httpxFÚPHOTON_NODE_BINÚnodez*photon: node binary '%s' not found on PATHÚnpmTÚ r:   r;   NuY   photon: spectrum-ts not installed at %s (last npm error: %s) â€” run: hermes photon setupuD   photon: spectrum-ts not installed at %s â€” run: hermes photon setup)r±   rJ   rK   ÚshutilÚwhichr   r   r¡   r`   Ú_dir_writablerq   rs   r    rU   ÚstripÚ_NPM_ERROR_LOG_MAX_CHARSrB   Údebug)Ú	npm_errors    r"   Úcheck_requirementsrÄ   ”  s–  € åð Ý�ŠÐJÑKÔKÐKØˆuÝŒ<�œ	Ð"3Ñ4Ô4Ð>¸Ñ?Ô?ð Ý�ŠØ8ÝŒIÐ'Ñ(Ô(Ð2¨Fñ	
ô 	
ð 	
ð ˆuÝ!Ñ#Ô#ñ 'õ •”˜UÑ#Ô#Ñ$Ô$ð 	­µ|±~´~Ñ)FÔ)Fð 	Ø�4ð
 ˆ	ð	ÝÑÔ×&Ò&Ñ(Ô(ð lÝ*Ñ,Ô,×6Ò6ÀÐ6ÑHÔH×NÒNÑPÔPÐQjÕRjÐQjÔk�	øøÝð 	ð 	ð 	ØˆDð	øøøàð 	Ý�LŠLðDå‘”Øñ	ô ð ð õ �LŠLØVÝ‘”ñô ð ð ˆuØˆ4s   ÃA!D/ Ä/
D<Ä;D<c                 óØ   — t          ¦   «         dz  } t          ¦   «         dz  dz  }	 |                      ¦   «         j        |                     ¦   «         j        k    S # t          $ r Y dS w xY w)a(  True when node_modules exists but is older than the committed lockfile.

    `hermes update` rewrites ``package-lock.json`` when the spectrum-ts pin is
    bumped, but does not reinstall ``node_modules``. npm records the state of
    the last install in ``node_modules/.package-lock.json``; when the top-level
    lockfile is newer than that marker, the install is out of date. This is the
    same signal ``npm ci`` uses. Returns False (do nothing) if either file is
    missing or unreadable, so a first-run or odd filesystem never blocks start.
    zpackage-lock.jsonrŸ   z.package-lock.jsonF)rq   ÚstatÚst_mtimerB   )ÚlockfileÚmarkers     r"   Ú_sidecar_deps_stalerÊ   Ê  so   € õ ‰~Œ~Ð 3Ñ3€HÝ‰^Œ^˜nÑ,Ð/CÑC€FðØ�}Š}‰ŒÔ'¨&¯+ª+©-¬-Ô*@Ò@Ð@øÝð ð ð Øˆuˆuðøøøs   §3A Á
A)Á(A)c                 ó  — t          j        d¦  «        } | st                               d¦  «         dS ddlm} 	 t          j        | dgt          t          ¦   «         ¦  «        dddd	d
t           |¦   «         ¬¦	  «	        }|j        dk    r_t                               d¦  «         t          j        | dgt          t          ¦   «         ¦  «        dddd	d
t           |¦   «         ¬¦	  «	        }n6# t          j        $ r$ t                               dt          ¦  «         Y dS w xY w|j        dk    r=t                               d|j        p|j        pd                     ¦   «         ¦  «         dS t                               d¦  «         dS )u¿  Reinstall the sidecar's node_modules from the lockfile (blocking).

    Mirrors ``hermes photon install-sidecar``: ``npm ci`` for an exact,
    reproducible install, falling back to ``npm install`` if the lockfile is
    missing or drifted. Runs the postinstall patch as part of the install.
    Best-effort â€” a failure here just leaves the (stale) deps in place and the
    normal ``_start_sidecar`` readiness check reports the real error.
    r»   z=[photon] cannot reinstall stale sidecar deps: npm not on PATHNr   ©Úwindows_hide_flagsÚciTr:   rF   F)ÚcwdÚcapture_outputr�   r<   ÚerrorsÚcheckr®   Úcreationflagsz?[photon] sidecar `npm ci` failed; falling back to `npm install`Úinstallz9[photon] sidecar dependency reinstall timed out after %ssz0[photon] sidecar dependency reinstall failed: %sr¼   z7[photon] sidecar dependencies reinstalled from lockfile)r½   r¾   rJ   rK   Úhermes_cli._subprocess_compatrÍ   Ú
subprocessÚrunr0   rq   Ú_NPM_REINSTALL_TIMEOUTÚ
returncodeÚTimeoutExpiredr   ÚstderrÚstdoutrÀ   Úinfo)r»   rÍ   Úresults      r"   Ú_reinstall_sidecar_depsrß   Ü  s¼  € õ Œ,�uÑ
Ô
€CØð Ý�ŠÐVÑWÔWÐWØˆð AÐ@Ð@Ð@Ð@Ð@ð Ý”Ø�$ˆKÝ•L‘N”NÑ#Ô#ØØ °	ØÝ*Ø,Ð,Ñ.Ô.ð
ñ 
ô 
ˆð Ô Ò!Ð!Ý�NŠNØQñô ð õ  ”^Ø�iÐ Ý�™œÑ'Ô'Ø#Ø G°IØÝ.Ø0Ð0Ñ2Ô2ðñ ô ˆFøøõ Ô$ð 	ð 	ð 	õ
 	�ŠØGÝ"ñ	
ô 	
ð 	
ð 	ˆˆð	øøøð Ô˜AÒÐÝ�ŠØ>ØŒ]Ð1˜fœmÐ1¨r×8Ò8Ñ:Ô:ñ	
ô 	
ð 	
ð 	
ð 	
õ
 	�ŠÐMÑNÔNÐNÐNÐNs   ºB/C* Ã*/DÄDÚcfgr   c                óþ   — | j         pi }|                     d¦  «        pt          j        d¦  «        }|                     d¦  «        pt	          d¦  «        }|r|s"t          ¦   «         \  }}t          |o|¦  «        S dS )NÚ
project_idÚPHOTON_PROJECT_IDÚproject_secretÚPHOTON_PROJECT_SECRETT)Úextrar’   r   r   r#   r   r`   )rà   ræ   râ   rä   Ú	stored_idÚ
stored_secs         r"   Úvalidate_configré     s†   € ØŒIˆO˜€EØ—’˜<Ñ(Ô(ÐJ­B¬IÐ6IÑ,JÔ,J€JØ—Y’YÐ/Ñ0Ô0Ð_Õ4FÐG^Ñ4_Ô4_€NØð .˜^ð .å 8Ñ :Ô :Ñˆ	�:Ý�IÐ, *Ñ-Ô-Ð-Øˆ4r,   c                ó    — t          | ¦  «        S r˜   )ré   ©rà   s    r"   Úis_connectedrì   "  s   € Ý˜3ÑÔÐr,   úOptional[dict]c                 óÆ   — t          ¦   «         \  } }| r|sdS | |dœ}t          j        dd¦  «                             ¦   «         }|r|t          j        dd¦  «        dœ|d<   |S )	zÕSeed PlatformConfig.extra from env so env-only setups appear in status.

    The special ``home_channel`` key is handled by the core plugin hook and
    becomes a proper ``HomeChannel`` on ``PlatformConfig``.
    N)râ   rä   ÚPHOTON_HOME_CHANNELr¼   ÚPHOTON_HOME_CHANNEL_NAMEÚHome)Úchat_idr   Úhome_channel)r   r   r   rÀ   )râ   rä   ÚseedÚhomes       r"   Ú_env_enablementrö   &  s†   € õ ":Ñ!;Ô!;Ñ€J�Øð ˜>ð ØˆtØ *¸nÐMÐM€DÝŒ9Ð*¨BÑ/Ô/×5Ò5Ñ7Ô7€DØð 
àÝ”IÐ8¸&ÑAÔAð 
ð  
ˆˆ^Ñð €Kr,   c                 óx   — t          j        dd¦  «                             ¦   «                              ¦   «         dvS )a:  Send agent replies as markdown (spectrum-ts ``markdown()`` builder).

    iMessage renders it natively; other Spectrum platforms degrade to
    readable plain text. On-device rendering can't be unit-tested, so
    ``PHOTON_MARKDOWN=false`` is the kill-switch back to stripped plain
    text without a release.
    ÚPHOTON_MARKDOWNÚtrue>   Ú0ÚnoÚfalse©r   r   rÀ   rµ   r^   r,   r"   Ú_markdown_enabledrþ   9  s<   € õ Œ9Ð&¨Ñ/Ô/×5Ò5Ñ7Ô7×=Ò=Ñ?Ô?ð Hð ð r,   úOptional[str]c                ó  — | pd                      ¦   «         }t          j        d|t          j        ¬¦  «        sd S 	 t	          |¦  «        }n# t
          $ r Y d S w xY w|j                             ¦   «         dvs|j        sd S |S )Nr¼   zhttps?://\S+)Úflags>   ÚhttpÚhttps)	rÀ   ÚreÚ	fullmatchÚ
IGNORECASEr   rV   Úschemerµ   Únetloc)r�   Ú	candidater”   s      r"   Ú_url_only_candidater
  F  s™   € Ø�˜×"Ò"Ñ$Ô$€IÝŒ<˜¨½"¼-ÐHÑHÔHð ØˆtðÝ˜)Ñ$Ô$ˆˆøÝð ð ð Øˆtˆtðøøøà„}×ÒÑÔÐ$5Ð5Ð5¸V¼]Ð5ØˆtØÐs   »A Á
AÁAc                ó@   — t          ¦   «         sdS t          | ¦  «        S )a&  Return a URL that should be sent via spectrum-ts ``richlink()``.

    Keep this intentionally narrow: only exact http(s) URL messages become
    rich links. Prose containing URLs and Markdown links stay on the normal
    markdown/text path so Hermes does not drop labels or rewrite intent.
    N)rþ   r
  )r�   s    r"   Ú_richlink_candidater  S  s%   € õ ÑÔð ØˆtÝ˜tÑ$Ô$Ð$r,   ÚcontentúDict[str, Any]c                ó  — t          |                      d¦  «        pd¦  «                             ¦   «         }t          |                      d¦  «        pd¦  «                             ¦   «         }t          |                      d¦  «        pd¦  «                             ¦   «         }g }|r|                     |¦  «         |r||k    r|                     |¦  «         |r|                     |¦  «         |rd                     |¦  «        ndS )NÚurlr¼   ÚtitleÚsummaryÚ
z'[Photon rich link received with no URL])r0   r’   rÀ   ÚappendÚjoin)r  r  r  r  Úpartss        r"   Ú_format_richlink_contentr  _  sú   € Ý
ˆg�kŠk˜%Ñ Ô Ð& BÑ
'Ô
'×
-Ò
-Ñ
/Ô
/€CÝ�—’˜GÑ$Ô$Ð*¨Ñ+Ô+×1Ò1Ñ3Ô3€EÝ�'—+’+˜iÑ(Ô(Ð.¨BÑ/Ô/×5Ò5Ñ7Ô7€GØ€EØð Ø�Š�UÑÔÐØð �7˜eÒ#Ð#Ø�Š�WÑÔÐØ
ð Ø�Š�SÑÔÐØ$ÐSˆ4�9Š9�UÑÔÐÐ*SÐSr,   c                óÆ  — |                       d¦  «        }|dk    r$t          |                       d¦  «        pd¦  «        S |dk    r$t          |                       d¦  «        pd¦  «        S |dk    rq|                       d¦  «        pg D ]Y}t          |t          ¦  «        sŒ|                      d¦  «        pi }t          |t          ¦  «        rt	          |¦  «        }|r|c S ŒZd S )	Nr´   r�   r¼   Úrichlinkr  ÚgroupÚitemsr  )r’   r
  rW   rX   Ú_richlink_url_from_content)r  ÚctypeÚitemÚitem_contentr  s        r"   r  r  m  sñ   € Ø�KŠK˜ÑÔ€EØ�‚€Ý" 7§;¢;¨vÑ#6Ô#6Ð#<¸"Ñ=Ô=Ð=Ø�
ÒÐÝ" 7§;¢;¨uÑ#5Ô#5Ð#;¸Ñ<Ô<Ð<Ø�ÒÐØ—K’K Ñ(Ô(Ð.¨Bð 	ð 	ˆDÝ˜d¥DÑ)Ô)ð ØØŸ8š8 IÑ.Ô.Ð4°"ˆLÝ˜,­Ñ-Ô-ð Ý0°Ñ>Ô>�Øð Ø�J�J�JøØˆ4r,   Úpayloadc                ó   — |                       d¦  «        dk    rdS t          |                       d¦  «        pd¦  «                             ¦   «         }t          |                       d¦  «        pd¦  «                             ¦   «         }|                     t          ¦  «        p+|                     t          ¦  «        pt          |v pt          |v }|S )Nr´   Ú
attachmentFr   r¼   Úid)r’   r0   rµ   ÚendswithÚ#_RICHLINK_PREVIEW_ATTACHMENT_SUFFIX)r   r   Úattachment_idÚis_preview_payloads       r"   Ú_is_richlink_preview_attachmentr(    sÅ   € Ø‡{‚{�6ÑÔ˜lÒ*Ð*ØˆuÝˆw�{Š{˜6Ñ"Ô"Ð( bÑ)Ô)×/Ò/Ñ1Ô1€DÝ˜Ÿš DÑ)Ô)Ð/¨RÑ0Ô0×6Ò6Ñ8Ô8€Mà�ŠÕ9Ñ:Ô:ð 	@Ø×!Ò!Õ"EÑFÔFð	@å.°$Ð6ð	@õ /°-Ð?ð	 ð Ðr,   c                ó\  — |                       d¦  «        dk    r9t          |                       d¦  «        p|                       d¦  «        pd¦  «        S |                       d¦  «        dk    rÀg }|                       d¦  «        pg D ]�}t          |t          ¦  «        r|                      d¦  «        nd }t          |t          ¦  «        rL|                     t          |                      d¦  «        p|                      d¦  «        pd¦  «        ¦  «         Œ�d	                     |¦  «        pd
S dS )Nr´   r"  r   r#  ú	(unnamed)r  r  r  z, z(group)z	(unknown))r’   r0   rW   rX   r  r  )r  Úlabelsr  r  s       r"   Ú_richlink_preview_labelr,  �  s"  € Ø‡{‚{�6ÑÔ˜lÒ*Ð*Ý�7—;’;˜vÑ&Ô&ÐJ¨'¯+ª+°dÑ*;Ô*;ÐJ¸{ÑKÔKÐKØ‡{‚{�6ÑÔ˜gÒ%Ð%ØˆØ—K’K Ñ(Ô(Ð.¨Bð 	ð 	ˆDÝ2<¸TÅ4Ñ2HÔ2HÐR˜4Ÿ8š8 IÑ.Ô.Ð.ÈdˆLÝ˜,­Ñ-Ô-ð Ø—’Ý˜×(Ò(¨Ñ0Ô0ÐY°L×4DÒ4DÀTÑ4JÔ4JÐYÈkÑZÔZñô ð øð �yŠy˜Ñ Ô Ð- IÐ-Øˆ;r,   c                óP  — t          | ¦  «        rdS |                      d¦  «        dk    rdS |                      d¦  «        pg }|sdS |D ][}t          |t          ¦  «        s dS |                     d¦  «        pi }t          |t          ¦  «        s dS t          |¦  «        s dS Œ\dS )NTr´   r  Fr  r  )r(  r’   rW   rX   )r  r  r  r  s       r"   Ú_is_richlink_preview_contentr.  Ÿ  sÎ   € Ý& wÑ/Ô/ð ØˆtØ‡{‚{�6ÑÔ˜gÒ%Ð%ØˆuØ�KŠK˜Ñ Ô Ð& B€EØð ØˆuØð ð ˆÝ˜$¥Ñ%Ô%ð 	Ø�5�5Ø—x’x 	Ñ*Ô*Ð0¨bˆÝ˜,­Ñ-Ô-ð 	Ø�5�5Ý.¨|Ñ<Ô<ð 	Ø�5�5ð	àˆ4r,   c                  ó  ‡ — e Zd ZdZeZdZdŒˆ fd„Zed�d
„¦   «         Z	dŽd„Z
d�d„Zddœd�d„Zd‘d„Zd‘d„Zd‘d„Zd‘d„Zd‘d„Zd’d„Zd“d„Zd”d„Zd•d"„Zed–d&„¦   «         Zed—d(„¦   «         Zed—d)„¦   «         Zd‘d*„Zd‘d+„Zd˜d.„Zd‘d/„Zd‘d0„Zd™d1„Zdšd3„Zd‘d4„Z d‘d5„Z!	 	 d›dœd>„Z"	 d�džˆ fdD„Z#	 	 	 dŸd ˆ fdG„Z$	 	 	 dŸd¡dI„Z%	 	 	 dŸd¢dK„Z&	 	 	 dŸd£dM„Z'	 	 	 	 d¤d¥dP„Z(	 	 	 dŸd¦dR„Z)	 d�d§dV„Z*	 d�d¨dX„Z+d�d©dY„Z,d©dZ„Z-d[Z.d\Z/dªd]„Z0 e1j2        d^¦  «        Z3e4d«d_„¦   «         Z5d¬d`„Z6d­da„Z7d®db„Z8d¯dc„Z9d°de„Z:d±df„Z;	 d�d²dg„Z<	 d�d³dh„Z=d´dj„Z>dkZ?dlZ@dµdm„ZAd¶dn„ZBed·dp„¦   «         ZCed¸dr„¦   «         ZD	 	 	 	 d¹dºdx„ZEd»d{„ZFd|d|d}œd¼d€„ZGd½d‚„ZHd6d6d6dƒd„œd¾d‰„ZId¿d‹„ZJˆ xZKS )ÀÚPhotonAdapterzËBidirectional bridge to Photon Spectrum via the Node spectrum-ts sidecar.

    Inbound: consume the sidecar's ``/inbound`` gRPC stream.
    Outbound: loopback POSTs to the sidecar's control channel.
    FÚconfigr   c                óB  •— t          ¦   «                              |t          d¦  «        ¦  «         |j        pi }t	          ¦   «         \  }}t          j        d¦  «        p|                     d¦  «        p|pd| _        t          d¦  «        p|                     d¦  «        p|pd| _
        t          |                     d¦  «        pt          j        d¦  «        t          ¦  «        | _        t          | _        t          d	¦  «        pt!          j        d
¦  «        | _        t'          t          j        dd¦  «        ¦  «                             ¦   «         dv| _        t          j        d¦  «        pt-          j        d¦  «        pd| _        t3          t5          |                     d¦  «        t          j        d¦  «        ¦  «        d¦  «        | _        t3          t5          |                     d¦  «        t          j        d¦  «        ¦  «        d¦  «        | _        t;          t5          |                     d¦  «        t          j        d¦  «        ¦  «        d¦  «        | _        | j        dk    | _        tA          ¦   «         | _!        d | _"        d | _#        d | _$        d | _%        d| _&        d | _'        d| _(        d | _)        d| _*        d| _+        d| _,        d | _-        i | _.        i | _/        i | _0        i | _1        i | _2        i | _3        |                     d¦  «        }|€t          j        d¦  «        }t'          |¦  «         4                    ¦   «                              ¦   «         dv | _5        |  6                    d |v r|d          nt          j        d!¦  «        ¦  «        | _7        d S )"NÚphotonrã   râ   r¼   rå   rä   Úsidecar_portÚPHOTON_SIDECAR_PORTÚPHOTON_SIDECAR_TOKENé   ÚPHOTON_SIDECAR_AUTOSTARTrù   )rú   rü   rû   r¹   rº   Úprobe_interval_secondsÚPHOTON_PROBE_INTERVAL_SECONDSg     À‚@Úprobe_timeout_secondsÚPHOTON_PROBE_TIMEOUT_SECONDSg      $@Úprobe_max_failuresÚPHOTON_PROBE_MAX_FAILURESé   r   Frk   ç        Úrequire_mentionÚPHOTON_REQUIRE_MENTION>   Ú1ÚonÚyesrù   Úmention_patternsÚPHOTON_MENTION_PATTERNS)8rƒ   r„   r   ræ   r   r   r   r’   Ú_project_idr#   Ú_project_secretr›   Ú_DEFAULT_SIDECAR_PORTÚ_sidecar_portÚ_DEFAULT_SIDECAR_BINDÚ_sidecar_bindÚsecretsÚ	token_hexÚ_sidecar_tokenr0   rµ   Ú_autostart_sidecarr½   r¾   Ú	_node_binr¤   rª   Ú_probe_intervalÚ_probe_timeoutr¦   Ú_probe_max_failuresÚ_probe_enabledrþ   Úsupports_code_blocksÚ_sidecar_procÚ_sidecar_supervisor_taskÚ_inbound_taskÚ_sidecar_health_taskÚ_inbound_runningÚ_http_clientÚ_sidecar_health_intervalÚ_watchdog_taskÚ_watchdog_runningÚ_probe_failuresÚ_last_upstream_activityÚ_respawn_lockÚ_seen_messagesÚ_sent_message_idsÚ_last_inbound_by_chatÚ_recent_richlinks_by_chatÚ_typing_last_sentÚ_pending_fffcrÀ   rA  Ú_compile_mention_patternsÚ_mention_patterns)r…   r1  ræ   rç   rè   Ú_require_mentionr†   s         €r"   r„   zPhotonAdapter.__init__Â  s¯  ø€ Ý‰Œ×Ò˜¥¨(Ñ!3Ô!3Ñ4Ô4Ð4Ø”Ð" ˆõ
 !9Ñ :Ô :Ñˆ	�:åŒIÐ)Ñ*Ô*ð Ø�yŠy˜Ñ&Ô&ðàðð ð	 	Ôõ Ð6Ñ7Ô7ð Ø�yŠyÐ)Ñ*Ô*ðàðð ð	 	Ôõ *Ø�IŠI�nÑ%Ô%ÐI­¬Ð3HÑ)IÔ)IÝ!ñ
ô 
ˆÔõ 3ˆÔåÐ5Ñ6Ô6ÐO½'Ô:KÈBÑ:OÔ:Oð 	Ôõ #&ÝŒIÐ0°&Ñ9Ô9ñ#
ô #
ç
Š%‰'Œ'Ð-ð#.ˆÔõ œÐ#4Ñ5Ô5ÐW½¼ÀfÑ9MÔ9MÐWÐQWˆŒõ(  -ÝØ—	’	Ð2Ñ3Ô3Ý”	Ð9Ñ:Ô:ñô ð ñ 
ô  
ˆÔõ ,ÝØ—	’	Ð1Ñ2Ô2Ý”	Ð8Ñ9Ô9ñô ð ñ
ô 
ˆÔõ $/ÝØ—	’	Ð.Ñ/Ô/Ý”	Ð5Ñ6Ô6ñô ð ñ$
ô $
ˆÔ ð #Ô2°QÒ6ˆÔõ %6Ñ$7Ô$7ˆÔ!ð :>ˆÔØ@DˆÔ%Ø59ˆÔØ<@ˆÔ!Ø %ˆÔØ;?ˆÔØ(,ˆÔ%à6:ˆÔØ!&ˆÔØ ˆÔð (+ˆÔ$Ø59ˆÔð 13ˆÔð 46ˆÔð 68ˆÔ"ð <>ˆÔ&à35ˆÔà;=ˆÔð
 !Ÿ9š9Ð%6Ñ7Ô7ÐØÐ#Ý!œyÐ)AÑBÔBÐÝ"Ð#3Ñ4Ô4×:Ò:Ñ<Ô<×BÒBÑDÔDð I
ð  
ˆÔð "&×!?Ò!?à! UÐ*Ð*ð Ð$Ô%Ð%å”Ð4Ñ5Ô5ñ"
ô "
ˆÔÐÐr,   rZ   r   r$   ú'list[re.Pattern]'c                ó<   — t          | dt          t          ¬¦  «        S )aD  Compile group-mention wake words from config/env.

        ``raw`` is a list (config or env JSON), a string (env var: JSON
        list, or comma/newline-separated), or None (use Hermes defaults).
        Mirrors the BlueBubbles implementation so both iMessage channels
        accept the same configuration shapes.
        r3  )Ú
log_prefixÚdefaultsÚlogger_)r   Ú_DEFAULT_MENTION_PATTERNSrJ   rY   s    r"   rj  z'PhotonAdapter._compile_mention_patternsI  s'   € õ (ØØÝ.Ýð	
ñ 
ô 
ð 	
r,   r�   r0   r`   c                óZ   ‡— ‰r| j         sdS t          ˆfd„| j         D ¦   «         ¦  «        S )NFc              3  óB   •K  — | ]}|                      ‰¦  «        V — Œd S r˜   )Úsearch)Ú.0Úpatternr�   s     €r"   ú	<genexpr>zBPhotonAdapter._message_matches_mention_patterns.<locals>.<genexpr>\  s/   øè è € ÐNÐN¨G�7—>’> $Ñ'Ô'ÐNÐNÐNÐNÐNÐNr,   )rk  Úany)r…   r�   s    `r"   Ú!_message_matches_mention_patternsz/PhotonAdapter._message_matches_mention_patternsY  s@   ø€ Øð 	˜4Ô1ð 	Ø�5ÝÐNÐNÐNÐN°tÔ7MÐNÑNÔNÑNÔNÐNr,   c                ó  — |s|S | j         D ]r}|                     |                     ¦   «         ¦  «        }|rG|                     ¦   «         |                     ¦   «         d…                              d¦  «        }|p|c S Œs|S )zÀStrip a leading wake word before dispatch.

        Custom mention patterns are regexes, so we only strip a leading
        match to avoid deleting ordinary words later in the prompt.
        Nz ,:-)rk  ÚmatchÚlstripÚend)r…   r�   rw  r|  Úcleaneds        r"   Ú_clean_mention_textz!PhotonAdapter._clean_mention_text^  sŒ   € ð ð 	ØˆKØÔ-ð 	'ð 	'ˆGØ—M’M $§+¢+¡-¤-Ñ0Ô0ˆEØð 'ØŸ+š+™-œ-¨¯	ª	©¬¨¨Ô5×<Ò<¸VÑDÔD�Ø� $Ð&Ð&Ð&ð'ð ˆr,   )Úis_reconnectr�  c             ƒ  óŽ  K  — t           s|                      ddd¬¦  «         dS | j        r| j        s|                      ddd¬¦  «         dS t	          j        dd¬¦  «        }|| _        | j        r~	 |                      ¦   «         ƒ d {V —† n|# t          $ rU}|                      d	d
|› �d¬¦  «         t          ¦   «          |                     ¦   «         ƒ d {V —† d | _        Y d }~dS d }~ww xY wt                               d¦  «         d| _        t          j        ¦   «                              |                      ¦   «         ¦  «        | _        t          j        ¦   «                              |                      ¦   «         ¦  «        | _        t-          j        ¦   «         | _        | j        rc| j        r\t          j        ¦   «         | _        d| _        t          j        ¦   «                              |                      ¦   «         ¦  «        | _        |                      ¦   «          t                                d| j!        | j"        ¦  «         dS )NÚMISSING_DEPúhttpx not installedF©r}   ÚMISSING_CREDENTIALSzRPHOTON_PROJECT_ID and PHOTON_PROJECT_SECRET are required. Run: hermes photon setuprv   ©r®   Ú	trust_envÚSIDECAR_FAILEDz failed to start Photon sidecar: TuD   [photon] sidecar autostart disabled â€” inbound + outbound will failuD   [photon] connected â€” sidecar on %s:%d, streaming inbound over gRPC)#r±   Ú_set_fatal_errorrH  rI  r³   ÚAsyncClientr]  rQ  Ú_start_sidecarrI   r_   ÚacloserJ   rK   r\  r¯   Úget_event_loopÚcreate_taskÚ_inbound_looprZ  Ú_monitor_sidecar_healthr[  ÚtimeÚ	monotonicrb  rV  ÚLockrc  r`  Ú_presence_watchdogr_  Ú_mark_connectedrÝ   rM  rK  )r…   r�  ÚclientrP   s       r"   ÚconnectzPhotonAdapter.connecto  s—  è è € Ýð 	Ø×!Ò!ØÐ4Àð "ñ ô ð ð �5ØÔð 	 tÔ';ð 	Ø×!Ò!Ø%ð+àð	 "ñ ô ð ð �5åÔ"¨4¸5ÐAÑAÔAˆØ"ˆÔð Ô"ð 	ðØ×)Ò)Ñ+Ô+Ð+Ð+Ð+Ð+Ð+Ð+Ð+Ð+øÝð ð ð Ø×%Ò%Ø$Ø:°qÐ:Ð:Ø"ð &ñ ô ð õ 'Ñ(Ô(Ð(Ø—m’m‘o”oÐ%Ð%Ð%Ð%Ð%Ð%Ð%Ø$(�Ô!Ø�u�u�u�u�uøøøøðøøøõ �NŠNØVñô ð ð
 !%ˆÔÝ$Ô3Ñ5Ô5×AÒAØ×ÒÑ Ô ñ
ô 
ˆÔõ %,Ô$:Ñ$<Ô$<×$HÒ$HØ×(Ò(Ñ*Ô*ñ%
ô %
ˆÔ!õ (,¤~Ñ'7Ô'7ˆÔ$ØÔð 	 4Ô#:ð 	Ý!(¤¡¤ˆDÔØ%)ˆDÔ"Ý")Ô"8Ñ":Ô":×"FÒ"FØ×'Ò'Ñ)Ô)ñ#ô #ˆDÔð 	×ÒÑÔÐÝ�ŠØRØÔ Ô 2ñ	
ô 	
ð 	
ð ˆts   Á1B Â
C+ÂA
C&Ã&C+r2   c              ƒ  óª  K  — d| _         |                      ¦   «         ƒ d {V —† | j        �a| j        }d | _        |                     ¦   «          |t	          j        ¦   «         ur*	 |ƒ d {V —† n # t          j        $ r Y nt          $ r Y nw xY w| j        �a| j        }d | _        |                     ¦   «          |t	          j        ¦   «         ur*	 |ƒ d {V —† n # t          j        $ r Y nt          $ r Y nw xY wt          | j
                             ¦   «         ¦  «        D ]2\  }\  }}|r(|                     ¦   «         s|                     ¦   «          Œ3| j
                             ¦   «          |                      ¦   «         ƒ d {V —† | j        �8	 | j                             ¦   «         ƒ d {V —† n# t          $ r Y nw xY wd | _        |                      ¦   «          d S ©NF)r\  Ú_stop_watchdogr[  Úcancelr¯   Úcurrent_taskÚCancelledErrorrI   rZ  Úlistri  r  ÚdoneÚclearÚ_stop_sidecarr]  r�  Ú_mark_disconnected)r…   ÚtaskÚchat_keyÚ_Ú	fffc_tasks        r"   Ú
disconnectzPhotonAdapter.disconnect±  sZ  è è € Ø %ˆÔð ×!Ò!Ñ#Ô#Ð#Ð#Ð#Ð#Ð#Ð#Ð#ØÔ$Ð0ØÔ,ˆDØ(,ˆDÔ%Ø�KŠK‰MŒMˆMØ�7Ô/Ñ1Ô1Ð1Ð1ðØ�J�J�J�J�J�J�J�JøÝÔ-ð ð ð Ø�DÝ ð ð ð Ø�DðøøøàÔÐ)ØÔ%ˆDØ!%ˆDÔØ�KŠK‰MŒMˆMØ�7Ô/Ñ1Ô1Ð1Ð1ðØ�J�J�J�J�J�J�J�JøÝÔ-ð ð ð Ø�DÝ ð ð ð Ø�Dðøøøõ )-¨TÔ-?×-EÒ-EÑ-GÔ-GÑ(HÔ(Hð 	#ð 	#Ñ$ˆH‘n�q˜)Øð # §¢Ñ!1Ô!1ð #Ø× Ò Ñ"Ô"Ð"øØÔ× Ò Ñ"Ô"Ð"Ø× Ò Ñ"Ô"Ð"Ð"Ð"Ð"Ð"Ð"Ð"ØÔÐ(ðØÔ'×.Ò.Ñ0Ô0Ð0Ð0Ð0Ð0Ð0Ð0Ð0Ð0øÝð ð ð Ø�ðøøøà $ˆDÔØ×ÒÑ!Ô!Ð!Ð!Ð!sB   Á#A, Á,B	Á=	B	ÂB	ÃC ÃC1Ã%	C1Ã0C1ÆF( Æ(
F5Æ4F5c                óR   — t          j        |                      ¦   «         ¦  «         dS )a  Notify the gateway of a fatal error from a detached task.

        ``_monitor_sidecar_health`` and ``_supervise_sidecar`` run as
        ``self._sidecar_health_task`` / ``self._sidecar_supervisor_task`` and
        used to call ``await self._notify_fatal_error()`` directly, on their
        own call stack. That routes straight into
        ``GatewayRunner._handle_adapter_fatal_error_impl``, which tears the
        adapter down via ``_safe_adapter_disconnect`` -> ``disconnect()``.
        ``disconnect()`` cancels ``self._sidecar_health_task`` and awaits it
        to make sure it's really gone -- but when the *health task itself* is
        what's driving this whole notification (the common case: the health
        poll is what detected the fatal condition), that task IS the one
        awaiting several frames up the stack, so ``disconnect()`` cancels and
        awaits its own caller. ``disconnect()``'s
        ``task is not asyncio.current_task()`` guard doesn't catch this,
        because the wrapper task ``_await_adapter_cleanup_with_timeout``
        creates around ``disconnect()`` (via ``asyncio.ensure_future``) is
        the current task there, not the health/supervisor task further up
        the plain-await chain. The self-cancellation throws an unhandled
        ``CancelledError`` through the health/supervisor task with nothing
        to catch it -- ``CancelledError`` stopped subclassing ``Exception``
        in Python 3.8, so the ``except Exception`` around the notify call
        never saw it -- and the task dies silently mid-handoff: no log line,
        no retry, the platform stranded with no automated way back.

        Dispatching the notification onto a brand-new task instead -- the
        same pattern ``DiscordAdapter._handle_bot_task_done`` already uses --
        means ``disconnect()`` can cancel the health/supervisor task freely
        without that cancellation ever reaching back into the code that's
        still running the handoff, so the handoff always reaches the
        reconnect queue.
        N)r¯   r�  Ú_notify_fatal_error_logged©r…   s    r"   Ú_dispatch_fatal_notificationz*PhotonAdapter._dispatch_fatal_notificationÚ  s(   € õB 	Ô˜D×;Ò;Ñ=Ô=Ñ>Ô>Ð>Ð>Ð>r,   c              ƒ  ó¦   K  — 	 |                       ¦   «         ƒ d {V —† d S # t          $ r&}t                               d|¦  «         Y d }~d S d }~ww xY w)Nz,[photon] fatal-error notification failed: %s)Ú_notify_fatal_errorrI   rJ   rK   )r…   r«   s     r"   rª  z(PhotonAdapter._notify_fatal_error_loggedý  s€   è è € ð	PØ×*Ò*Ñ,Ô,Ð,Ð,Ð,Ð,Ð,Ð,Ð,Ð,Ð,øÝð 	Pð 	Pð 	PÝ�NŠNÐIÈ3ÑOÔOÐOÐOÐOÐOÐOÐOÐOøøøøð	Pøøøs   „   
AªAÁAc              ƒ  óþ  K  — | j         }|€dS d| j        › d| j        › d�}d| j        i}d}| j        �rI	 |                     d||d¬¦  «        4 ƒd{V —†	 }|j        d	k    rt          d
|j        › �¦  «        ‚d}|                     ¦   «         2 3 d{V —†}| j        s n4| 	                    ¦   «         }|sŒ'|  
                    |¦  «        ƒ d{V —† ŒC6 	 ddd¦  «        ƒd{V —† n# 1 ƒd{V —†swxY w Y   n|# t          j        $ r ‚ t          $ r`}| j        sY d}~dS t                               d||¦  «         t          j        |¦  «        ƒ d{V —† t#          |dz  d¦  «        }Y d}~nd}~ww xY w| j        �°GdS dS )züConsume the sidecar's ``/inbound`` NDJSON stream, with reconnect.

        The sidecar owns the gRPC reconnect/heartbeat to Photon; this loop
        only has to re-open the loopback HTTP stream if it drops (e.g. the
        sidecar restarts).
        Núhttp://ú:z/inboundúX-Hermes-Sidecar-Tokeng      ð?ÚGET©Úheadersr®   r�   z/inbound returned z;[photon] inbound stream dropped (%s); reconnecting in %.1fsé   rv   )r]  rM  rK  rP  r\  Ústreamr~   ÚRuntimeErrorÚaiter_linesrÀ   Ú_on_inbound_liner¯   rž  rI   rJ   rK   ÚsleepÚmin)r…   r—  r  rµ  ÚbackoffÚrespÚlinerP   s           r"   r�  zPhotonAdapter._inbound_loop  sº  è è € ð Ô"ˆØˆ>ØˆFØI˜Ô*ÐIÐI¨TÔ-?ÐIÐIÐIˆØ+¨TÔ-@ÐAˆØˆØÔ#ñ 	1ð1Ø!Ÿ=š=Ø˜3¨¸ð )ñ ô ð :ð :ð :ð :ð :ð :ð :ð :àØÔ'¨3Ò.Ð.Ý*Ð+RÀÔ@PÐ+RÐ+RÑSÔSÐSØ!�GØ&*×&6Ò&6Ñ&8Ô&8ð :ð :ð :ð :ð :ð :ð :˜dØ#Ô4ð "Ø!˜EØ#Ÿzšz™|œ|˜Ø#ð %Ø$Ø"×3Ò3°DÑ9Ô9Ð9Ð9Ð9Ð9Ð9Ð9Ð9Ð9ð '9Ð&8ð:ð :ð :ñ :ô :ð :ð :ð :ð :ð :ð :ð :ð :ð :ð :ð :ð :ð :ð :ð :ð :ð :ð :øøøð :ð :ð :ð :øøõ Ô)ð ð ð ØÝð 1ð 1ð 1ØÔ,ð Ø�E�E�E�E�EÝ—’ØQØ�wñô ð õ ”m GÑ,Ô,Ð,Ð,Ð,Ð,Ð,Ð,Ð,Ý˜g¨™k¨4Ñ0Ô0������øøøøð1øøøð# Ô#ñ 	1ð 	1ð 	1ð 	1ð 	1sN   µC7 Á9C%ÂCÂ>C%ÃC7 Ã%
C/Ã/C7 Ã2C/Ã3C7 Ã7E0ÄE+ÄA	E+Å+E0c              ƒ  óF  K  — | j         �rt          j        | j        ¦  «        ƒ d{V —† | j         sdS 	 |                      di ¦  «        ƒ d{V —†}nA# t          j        $ r ‚ t          $ r%}t                               d|¦  «         Y d}~Œ‡d}~ww xY wt          |t          ¦  «        r|                     d¦  «        nd}t          |t          ¦  «        sŒÑ|                     d¦  «        }t          |t          ¦  «        rY|                     d¦  «        du rBt                               d|                     d	¦  «        |                     d
¦  «        ¦  «         |                     d¦  «        dur�Œmt          |                     d¦  «        pd¦  «        }|                     d¦  «        }t          |                     d¦  «        pd¦  «        }d|› d|› d|› �}t                               d|¦  «         |                      d|d¬¦  «         |                      ¦   «          dS dS )a  Promote degraded upstream Photon stream health into reconnect.

        The sidecar HTTP process can stay alive while spectrum-ts repeatedly
        fails to maintain the upstream inbound gRPC stream. Polling `/healthz`
        keeps that from becoming a silent inbound outage.
        Nú/healthzz([photon] sidecar health check failed: %sr·  Ú	stalenessÚzombieSuspectedTzV[photon] sidecar reports suspected zombie stream (silentForMs=%s, lastProbeOutcome=%s)ÚsilentForMsÚlastProbeOutcomeÚokFÚstateÚunknownÚdegradedForMsÚ	lastIssuezunknown stream issuez'Photon upstream stream degraded (state=z, degradedForMs=r‚   ú[photon] %sÚUPSTREAM_STREAM_DEGRADEDr…  )r\  r¯   r»  r^  Ú_sidecar_callrž  rI   rJ   rÂ   rW   rX   r’   rK   r0   r   rŠ  r¬  )	r…   rŽ   r«   r·  rÂ  rÇ  Údegraded_for_msÚ
last_issueÚmessages	            r"   r‘  z%PhotonAdapter._monitor_sidecar_health-  s{  è è € ð Ô#ñ 2	Ý”- Ô =Ñ>Ô>Ð>Ð>Ð>Ð>Ð>Ð>Ð>ØÔ(ð Ø�ðØ!×/Ò/°
¸BÑ?Ô?Ð?Ð?Ð?Ð?Ð?Ð?��øÝÔ)ð ð ð ØÝð ð ð Ý—’ÐGÈÑMÔMÐMØ����øøøøðøøøõ ,6°d½DÑ+AÔ+AÐK�T—X’X˜hÑ'Ô'Ð'ÀtˆFÝ˜f¥dÑ+Ô+ð Øð Ÿ
š
 ;Ñ/Ô/ˆIå˜9¥dÑ+Ô+ð	à—M’MÐ"3Ñ4Ô4¸Ð<Ð<å—’ð=à—M’M -Ñ0Ô0Ø—M’MÐ"4Ñ5Ô5ñ	ô ð ð �zŠz˜$ÑÔ uÐ,Ð,Ùå˜Ÿ
š
 7Ñ+Ô+Ð8¨yÑ9Ô9ˆEØ$Ÿjšj¨Ñ9Ô9ˆOÝ˜VŸZšZ¨Ñ4Ô4ÐNÐ8NÑOÔOˆJð Ø ð ð  Ø2Að ð  àð ð  ð õ
 �LŠL˜¨Ñ0Ô0Ð0Ø×!Ò!Ø*ØØð "ñ ô ð ð
 ×-Ò-Ñ/Ô/Ð/ØˆEðe2	ð 2	s   ´A ÁBÁ*B
Â
Br¿  c              ƒ  ó¨  K  — |                       ¦   «          	 t          j        |¦  «        }n0# t          j        $ r t                               d¦  «         Y d S w xY w|                     d¦  «        }|r|                      |¦  «        rd S 	 |                      |¦  «        ƒ d {V —† d S # t          $ r t           
                    d¦  «         Y d S w xY w)Nz'[photon] skipping non-JSON inbound lineÚ	messageIdz [photon] inbound dispatch failed)Ú_note_upstream_activityrD   rT   ÚJSONDecodeErrorrJ   rÂ   r’   Ú_is_duplicateÚ_dispatch_inboundrI   Ú	exception)r…   r¿  ÚeventÚmsg_ids       r"   rº  zPhotonAdapter._on_inbound_lineh  s  è è € ð 	×$Ò$Ñ&Ô&Ð&ð	Ý”J˜tÑ$Ô$ˆEˆEøÝÔ#ð 	ð 	ð 	Ý�LŠLÐBÑCÔCÐCØˆFˆFð	øøøð —’˜;Ñ'Ô'ˆØð 	�d×(Ò(¨Ñ0Ô0ð 	ØˆFð	AØ×(Ò(¨Ñ/Ô/Ð/Ð/Ð/Ð/Ð/Ð/Ð/Ð/Ð/øÝð 	Að 	Að 	AÝ×ÒÐ?Ñ@Ô@Ð@Ð@Ð@Ð@ð	Aøøøs!   ˜- ­)AÁAÂB) Â)$CÃCrÙ  c                óX  — t          j         ¦   «         }| j        }|                     |¦  «        }|�||z
  t          k     rdS ||v r||= |||<   t	          |¦  «        t
          k    rDt          |                     ¦   «         ¦  «        d t	          |¦  «        t
          z
  …         D ]}||= ŒdS )NTF)r’  rd  r’   Ú_DEDUP_WINDOW_SECONDSÚlenÚ_DEDUP_MAX_SIZErŸ  Úkeys)r…   rÙ  ÚnowÚseenÚtÚolds         r"   rÕ  zPhotonAdapter._is_duplicate|  s®   € ÝŒi‰kŒkˆØÔ"ˆØ�HŠH�VÑÔˆØˆ=˜S 1™WÕ'<Ò<Ð<Ø�4ð �Tˆ>ˆ>Ø�V�ØˆˆV‰Ýˆt‰9Œ9•Ò&Ð&Ý˜DŸIšI™KœKÑ(Ô(Ð)F­3¨t©9¬9µÑ+FÐ)FÔGð ð �Ø˜�I�IØˆur,   r¥  Ú
message_idc              ƒ  óº   K  — t          j        t          ¦  «        ƒ d {V —† | j                             |d ¦  «        rt
                               d||¦  «         d S d S )Nz_[photon] wait for attachment was too long, can't retrieve attachment data (message %s, chat %s))r¯   r»  Ú_FFFC_WAIT_SECONDSri  ÚpoprJ   rK   )r…   r¥  rã  s      r"   Ú_fffc_timeout_handlerz#PhotonAdapter._fffc_timeout_handler�  sx   è è € ÝŒmÕ.Ñ/Ô/Ð/Ð/Ð/Ð/Ð/Ð/Ð/ØÔ×!Ò! (¨DÑ1Ô1ð 	Ý�NŠNð(Ø)3°Xñô ð ð ð ð	ð 	r,   rØ  r  c              ƒ  óš  K  — |                      d¦  «        pi }|                      d¦  «        pi }|                      d¦  «        pi }|                      d¦  «        pd}|st                               d¦  «         dS |                      d¦  «        d	k    rd	nd
}|                      d¦  «        p|                      d¦  «        p|}|                      d¦  «        pd}	 |r(t          j        |                     dd¦  «        ¦  «        nt          j        t          j        ¬¦  «        }	n/# t          $ r" t          j        t          j        ¬¦  «        }	Y nw xY wg }
g }d9d„}|                      d¦  «        }|dk    rò|                      d¦  «        }|                      d¦  «        dk    p
|o|| j
        v }|st                               d¦  «         dS |                      d¦  «        pd}|                      |||||pd¬¦  «        }|                      t          d|› �t          j        ||                      d¦  «        ||                      d¦  «        pdd||	¬ ¦	  «	        ¦  «        ƒ d{V —† dS |d!k    rþ|                      d!¦  «        pd                     ¦   «         d"k    rÑ|}| j                             |d¦  «        }|r<|d#         r4|d#                              ¦   «         s|d#                              ¦   «          t-          j        |                      ||                      d¦  «        pd¦  «        ¦  «        }t3          j        ¦   «         |f| j        |<   t                               d$¦  «         dS |d%v rs| j                             |d¦  «        }|rV|d#         rN|d#                              ¦   «         s4|d#                              ¦   «          t                               d&¦  «         |                      ||¦  «        r*t                               d't;          |¦  «        ¦  «         dS |                      ||                      d¦  «        ¦  «         |d(k    rã|                      d)¦  «        d*u rt                               d+¦  «         dS |                      d,¦  «        pd                     ¦   «         }|st                               d-¦  «         dS |                      |||||pd¬¦  «        }|                      t          |t          j        ||                      d¦  «        ||	¬.¦  «        ¦  «        ƒ d{V —† dS |d!k    r%|                      d!¦  «        pd}t          j        }�n|d%v r ||¦  «        \  }}}
}�nø|d/k    rt?          |¦  «        }t          j        }�nÕ|d	k    �r¼g }t          j        }|                      d0¦  «        pg D �]=}tA          |tB          ¦  «        sŒ|                      d¦  «        pi }tA          |tB          ¦  «        sŒF|                      d¦  «        }|d!k    r/|                      d!¦  «        pd}|r| "                    |¦  «         Œ�|d/k    r#| "                    t?          |¦  «        ¦  «         Œ¹|d%v re ||¦  «        \  }}}} |t          j        k    r|}|
 #                    |¦  «         | #                    | ¦  «         |s| "                    |¦  «         �Œ"|r| "                    d1|› d2�¦  «         �Œ?|
r|t          j        k    rt          j$        }d3 %                    d4„ |D ¦   «         ¦  «                             ¦   «         }|s|
rd5nd6}nd1|› d2�}t          j        }|d	k    rM| j&        rF|  '                    |¦  «        st                               d7¦  «         dS |  (                    |¦  «        }|  )                    |tU          |¦  «        p|¦  «         |                      |||||pd¬¦  «        }t          ||||                      d¦  «        ||	|
|¬8¦  «        }!|                      |!¦  «        ƒ d{V —† dS ):uq  Normalize a sidecar inbound event and dispatch it to the gateway.

        Event shape (from ``sidecar/index.mjs``)::

            {
              "messageId": "...",
              "platform": "iMessage",
              "space": {"id": "...", "type": "dm"|"group", "phone": "+E164"},
              "sender": {"id": "+E164"},
              "content": {"type": "text", "text": "..."}
                       | {"type": "attachment"|"voice", "id", "name",
                          "mimeType", "size", "duration"?, "data"?,
                          "encoding"?}
                       | {"type": "reaction", "emoji": "â�¤ï¸�",
                          "targetMessageId": "..." | null,
                          "targetDirection": "inbound"|"outbound" | null,
                          "targetText": "..." | null},
              "timestamp": "2026-05-14T19:06:32.000Z"

        Attachment and voice content carry the bytes inline as base64 ``data``
        (with ``encoding == "base64"``) when the sidecar could read them
        within its size cap; otherwise only metadata is present and we surface
        a marker.
            }
        ÚspaceÚsenderr  r#  r¼   z![photon] inbound missing space.idNr´   r  ÚdmÚphoneÚ	timestampÚZz+00:00)Útzr   r  r$   ú-tuple[str, MessageType, List[str], List[str]]c                ó(  — |                       d¦  «        dk    }|                       d¦  «        p|rdnd}|                       d¦  «        pd}|s/|                     ¦   «                              d¦  «        s|dk    rd	}|rt          j        nt          |¦  «        }t          | |||¬
¦  «        }|r|rdnd||g|p|rdndgfS |rdnd}|                       d¦  «        }t          |t          t          f¦  «        rd|› d�nd}d|› d|› d|pd› |› d�|g g fS )Nr´   Úvoicer   r*  ÚmimeTyper¼   ú.cafúaudio/x-cafT©Úforce_audioz(voice)ú(attachment)ú	audio/mp4zapplication/octet-streamr"  Údurationz, duration: Úsz[Photon z received: r�   zunknown MIMEz)])
r’   rµ   r$  r   ÚVOICEÚ_attachment_message_typeÚ_cache_inbound_attachmentrW   r.   r¢   )	r   Úis_voicer   ÚmimeÚmtypeÚcachedÚlabelrú  Úduration_texts	            r"   Ú_normalize_binary_payloadzBPhotonAdapter._dispatch_inbound.<locals>._normalize_binary_payloadË  sœ  € ð —{’{ 6Ñ*Ô*¨gÒ5ˆHØ—;’;˜vÑ&Ô&ÐP°hÐ+O¨7¨7ÀKˆDØ—;’;˜zÑ*Ô*Ð0¨bˆDð ð   §¢¡¤×!6Ò!6°vÑ!>Ô!>ð  À$È-ÒBWÐBWØ�Ø)1ÐU•KÔ%Ð%Õ7OÐPTÑ7UÔ7UˆEÝ.Ø˜˜t°ðñ ô ˆFð ð à!)Ð=�I�I¨~ØØ�HØÐV¨XÐU˜k˜kÐ;UÐWð	ð ð  (Ð9�G�G¨\ˆEØ—{’{ :Ñ.Ô.ˆHõ ˜h­­e¨Ñ5Ô5ðÐ*˜xÐ*Ð*Ð*Ð*àð ð>˜5ð >ð >¨Tð >ð >ØÐ*˜Nð>Ø,9ð>ð >ð >àØØðð r,   ÚreactionÚtargetMessageIdÚtargetDirectionÚoutboundz6[photon] ignoring reaction on a message we didn't sendÚemoji)rò   Ú	chat_nameÚ	chat_typeÚuser_idÚ	user_namezreaction:added:rÒ  Ú
targetTextT)	r�   Úmessage_typeÚsourcerã  Úreply_to_message_idÚreply_to_textÚreply_to_is_own_messageÚraw_messagerí  r�   u   ï¿¼r   u?   [photon] U+FFFC placeholder received â€” waiting for attachment>   rò  r"  u9   [photon] attachment arrived â€” cancelling U+FFFC timeoutz5[photon] suppressing rich-link preview attachment: %sÚpoll_optionÚselectedFz"[photon] ignoring poll deselectionr  z,[photon] ignoring poll vote with empty title)r�   r  r  rã  r  rí  r  r  z"[Photon content type not handled: Ú]r  c              3  ó   K  — | ]}|¯|V — Œ	d S r˜   r^   )rv  Úparts     r"   rx  z2PhotonAdapter._dispatch_inbound.<locals>.<genexpr>ˆ  s'   è è € ÐAÐA d¸DÐA˜TÐAÐAÐAÐAÐAÐAr,   rø  z[Photon empty group received]zR[photon] ignoring group message (require_mention=true, no mention pattern matched))r�   r  r  rã  r  rí  Ú
media_urlsÚmedia_types)r   r  r$   rð  )+r’   rJ   rK   r   ÚfromisoformatrF   rß  r   ÚutcrV   re  rÂ   Úbuild_sourceÚhandle_messager   r   ÚTEXTrÀ   ri  ræ  r   rœ  r¯   r�  rç  r’  r“  Ú_is_recent_richlink_previewrÝ   r,  Ú_record_last_inboundr  rW   rX   r  ÚextendÚDOCUMENTr  rA  rz  r€  Ú_record_recent_richlinkr  )"r…   rØ  ré  rê  r  Úspace_idr  Ú	sender_idÚts_strrí  r  r  r  r  Ú	target_idÚis_oursr
  r  r¥  Úprevr¤  Úchoicer�   r  Ú
text_partsr  r  Ú	item_typeÚ	item_textrÉ   Ú
item_mtypeÚ	item_urlsÚ
item_typesÚmessage_events"                                     r"   rÖ  zPhotonAdapter._dispatch_inbound•  s	  è è € ð4 —	’	˜'Ñ"Ô"Ð( bˆØ—’˜8Ñ$Ô$Ð*¨ˆØ—)’)˜IÑ&Ô&Ð,¨"ˆà—9’9˜T‘?”?Ð( bˆØð 	Ý�NŠNÐ>Ñ?Ô?Ð?ØˆFð  %Ÿyšy¨Ñ0Ô0°GÒ;Ð;�G�GÀˆ	Ø—J’J˜tÑ$Ô$ÐF¨¯	ª	°'Ñ(:Ô(:ÐF¸hˆ	à—’˜;Ñ'Ô'Ð-¨2ˆð	6ð ð3•Ô& v§~¢~°c¸8Ñ'DÔ'DÑEÔEÐEå”\¥X¤\Ð2Ñ2Ô2ð ˆIøõ
 ð 	6ð 	6ð 	6Ý œ­¬Ð5Ñ5Ô5ˆIˆIˆIð	6øøøð
 !#ˆ
Ø!#ˆð#	ð #	ð #	ð #	ðJ —’˜FÑ#Ô#ˆØ�JÒÐð
  ŸšÐ$5Ñ6Ô6ˆIØ—k’kÐ"3Ñ4Ô4¸
ÒBð ØÐA˜i¨4Ô+AÐAð ð ð Ý—’ØLñô ð ð �Ø—K’K Ñ(Ô(Ð.¨BˆEØ×&Ò&Ø Ø"Ø#Ø!Ø#Ð+ tð 'ñ ô ˆFð ×%Ò%ÝØ2¨5Ð2Ð2Ý!,Ô!1Ø!Ø$Ÿyšy¨Ñ5Ô5Ø(1Ø")§+¢+¨lÑ";Ô";Ð"C¸tØ,0Ø %Ø'ð
ñ 
ô 
ñô ð ð ð ð ð ð ð ð ˆFð �FŠ?ˆ? §¢¨FÑ 3Ô 3Ð 9°r×@Ò@ÑBÔBÀhÒNÐNØˆHØÔ%×)Ò)¨(°DÑ9Ô9ˆDØð !˜˜Qœð !¨¨Q¬¯ª©¬ð !Ø�Q”—’Ñ Ô Ð ÝÔ&Ø×*Ò*¨8°U·Y²Y¸{Ñ5KÔ5KÐ5QÈrÑRÔRñô ˆDõ -1¬NÑ,<Ô,<¸dÐ+CˆDÔ˜xÑ(Ý�LŠLÐZÑ[Ô[Ð[ØˆFð Ð+Ð+Ð+ØÔ%×)Ò)¨(°DÑ9Ô9ˆDØð Z˜˜Qœð Z¨¨Q¬¯ª©¬ð ZØ�Q”—’Ñ Ô Ð Ý—’ÐXÑYÔYÐYð
 ×+Ò+¨H°gÑ>Ô>ð 	Ý�KŠKØGÝ'¨Ñ0Ô0ñô ð ð ˆFð
 	×!Ò! (¨E¯IªI°kÑ,BÔ,BÑCÔCÐCØ�MÒ!Ð!ð �{Š{˜:Ñ&Ô&¨%Ð/Ð/Ý—’ÐAÑBÔBÐBØ�Ø—k’k 'Ñ*Ô*Ð0¨b×7Ò7Ñ9Ô9ˆFØð Ý—’ÐKÑLÔLÐLØ�Ø×&Ò&Ø Ø"Ø#Ø!Ø#Ð+ tð 'ñ ô ˆFð ×%Ò%ÝØÝ!,Ô!1Ø!Ø$Ÿyšy¨Ñ5Ô5Ø %Ø'ðñ ô ñ	ô 	ð 	ð 	ð 	ð 	ð 	ð 	ð 	ð ˆFØ�FŠ?ˆ?Ø—;’;˜vÑ&Ô&Ð,¨"ˆDÝÔ$ˆE‰EØÐ-Ð-Ð-Ø3LÐ3LÈWÑ3UÔ3UÑ0ˆD�%˜ [¡[Ø�jÒ Ð Ý+¨GÑ4Ô4ˆDÝÔ$ˆE‰EØ�gÒÑØ$&ˆJÝÔ$ˆEØŸš GÑ,Ô,Ð2°ð Yñ Y�Ý! $­Ñ-Ô-ð ØØ#Ÿxšx¨	Ñ2Ô2Ð8°b�Ý! ,µÑ5Ô5ð ØØ(×,Ò,¨VÑ4Ô4�	Ø Ò&Ð&Ø ,× 0Ò 0°Ñ 8Ô 8Ð >¸B�IØ ð 5Ø"×)Ò)¨)Ñ4Ô4Ð4ØØ 
Ò*Ð*Ø×%Ò%Õ&>¸|Ñ&LÔ&LÑMÔMÐMØØÐ 7Ð7Ð7Ø@YÐ@YØ$ñAô AÑ=�F˜J¨	°:ð ¥Ô 0Ò0Ð0Ø *˜Ø×%Ò% iÑ0Ô0Ð0Ø×&Ò& zÑ2Ô2Ð2Ø$ð 2Ø"×)Ò)¨&Ñ1Ô1Ð1ÙØð YØ×%Ò%Ð&WÈ9Ð&WÐ&WÐ&WÑXÔXÐXùØð -˜e¥{Ô'7Ò7Ð7Ý#Ô,�Ø—9’9ÐAÐA¨jÐAÑAÔAÑAÔA×GÒGÑIÔIˆDØð YØ)3ÐX�~�~Ð9X�øà@¸Ð@Ð@Ð@ˆDÝÔ$ˆEð ˜ÒÐ DÔ$8ÐØ×9Ò9¸$Ñ?Ô?ð Ý—’ðIñô ð ð �Ø×+Ò+¨DÑ1Ô1ˆDà×$Ò$ XÕ/IÈ'Ñ/RÔ/RÐ/ZÐVZÑ[Ô[Ð[à×"Ò"ØØØØØÐ' 4ð #ñ 
ô 
ˆõ %ØØØØ—y’y Ñ-Ô-ØØØ!Ø#ð	
ñ 	
ô 	
ˆð ×!Ò! -Ñ0Ô0Ð0Ð0Ð0Ð0Ð0Ð0Ð0Ð0Ð0s   ÃA	D( Ä()EÅEr-   r.   ú	List[int]c           	     óÌ   — 	 t          j        ddd| › �dgdddddd	¬
¦  «        }n# t          t           j        f$ r g cY S w xY wd„ |j                             ¦   «         D ¦   «         S )zBPIDs listening on a local TCP port (empty if none/undeterminable).Úlsofz-tiztcp:z-sTCP:LISTENTr:   rF   rw   F©rÐ   r�   r<   rÑ   r®   rÒ   c                óx   — g | ]7}|                      ¦   «                              ¦   «         ¯(t          |¦  «        ‘Œ8S r^   )rÀ   Úisdigitr.   )rv  Útoks     r"   ú
<listcomp>z5PhotonAdapter._find_listener_pids.<locals>.<listcomp>½  s7   € ÐPÐPÐP˜S¸#¿)º)¹+¼+×:MÒ:MÑ:OÔ:OÐP•�C‘”ÐPÐPÐPr,   )rÖ   r×   rB   rÚ   rÜ   Úsplit)r-   Úouts     r"   Ú_find_listener_pidsz!PhotonAdapter._find_listener_pids³  s�   € ð	Ý”.Ø˜  t  ¨~Ð>Ø#¨$¸ÈÐ\_Ðglðñ ô ˆCˆCøõ �Ô2Ð3ð 	ð 	ð 	ØˆIˆIˆIð	øøøàPÐP C¤J×$4Ò$4Ñ$6Ô$6ÐPÑPÔPÐPs   ‚"% ¥A ¿A r1   c           	     ó¬   — 	 t          j        ddt          | ¦  «        ddgdddddd	¬
¦  «        }n# t          t           j        f$ r Y d	S w xY wd|j        v S )z;True if ``pid``'s command line is a Photon sidecar process.Úpsz-pz-ozcommand=Tr:   rF   rw   Fr8  zphoton/sidecar/index.mjs)rÖ   r×   r0   rB   rÚ   rÜ   )r1   r>  s     r"   Ú_pid_is_sidecarzPhotonAdapter._pid_is_sidecar¿  s}   € ð	Ý”.Ø�t�S ™XœX t¨ZÐ8Ø#¨$¸ÈÐ\_Ðglðñ ô ˆCˆCøõ �Ô2Ð3ð 	ð 	ð 	Ø�5�5ð	øøøð *¨S¬ZÐ7Ð7s   ‚-0 °A
Á	A
c                óT   — 	 t          j        | d¦  «         dS # t          $ r Y dS w xY w)Nr   TF)r   rf   rB   )r1   s    r"   Ú
_pid_alivezPhotonAdapter._pid_aliveÌ  s=   € ð	ÝŒG�C˜‰OŒOˆOØ�4øÝð 	ð 	ð 	Ø�5�5ð	øøøs   ‚ ™
'¦'c              ƒ  ó¤  ‡ K  — t           j        dk    rdS 	 t          j        dd¬¦  «        4 ƒd{V —†}|                     d‰ j        › d‰ j        › d�d	‰ j        i¬
¦  «        ƒ d{V —† ddd¦  «        ƒd{V —† n# 1 ƒd{V —†swxY w Y   n# t          j        $ r Y dS w xY wˆ fd„}t          j
        |¦  «        ƒ d{V —†\  }}|st          d‰ j        › d|pd› d�¦  «        ‚|D ]S}t                               d|‰ j        ¦  «         	 t          j        |t           j        ¦  «         ŒD# t$          $ r Y ŒPw xY wt'          j        ¦   «         dz   }t'          j        ¦   «         |k     rgt)          ˆ fd„|D ¦   «         ¦  «        rLt          j        d¦  «        ƒ d{V —† t'          j        ¦   «         |k     rt)          ˆ fd„|D ¦   «         ¦  «        °L|D ]H}‰                      |¦  «        r1	 t          j        |t           j        ¦  «         Œ8# t$          $ r Y ŒDw xY wŒIt          j        d¦  «        ƒ d{V —† |rt          d‰ j        › d|› d�¦  «        ‚dS )u:  Kill an orphaned sidecar squatting our port before spawning ours.

        A hard gateway exit (crash, SIGKILL, supervisor restart) used to leave
        the detached sidecar running with a token the new gateway doesn't
        know, so it can't be told to ``/shutdown`` â€” and every replacement
        spawn died on EADDRINUSE, failing each reconnect attempt. The
        stdin-EOF watch prevents new orphans; this reclaims the port from
        orphans that predate it (or survived it). Listeners are verified by
        command line before being signalled.
        Úwin32Nç       @Fr‡  r°  r±  rÁ  r²  ©rµ  c                 óv   •‡— ‰                      ‰j        ¦  «        } ˆfd„| D ¦   «         Š‰ˆfd„| D ¦   «         fS )Nc                ó>   •— g | ]}‰                      |¦  «        ¯|‘ŒS r^   )rB  )rv  r1   r…   s     €r"   r<  zGPhotonAdapter._reap_stale_sidecar.<locals>._inspect.<locals>.<listcomp>ó  s,   ø€ ÐFÐFÐF˜C¨D×,@Ò,@ÀÑ,EÔ,EÐF�CÐFÐFÐFr,   c                ó   •— g | ]}|‰v¯|‘Œ	S r^   r^   )rv  r1   Úmines     €r"   r<  zGPhotonAdapter._reap_stale_sidecar.<locals>._inspect.<locals>.<listcomp>ô  s   ø€ ÐBÐBÐB #°#¸T°/°/˜#°/°/°/r,   )r?  rK  )ÚfoundrL  r…   s    @€r"   Ú_inspectz3PhotonAdapter._reap_stale_sidecar.<locals>._inspectñ  sS   øø€ Ø×,Ò,¨TÔ-?Ñ@Ô@ˆEØFÐFÐFÐF 5ÐFÑFÔFˆDØÐBÐBÐBÐB¨ÐBÑBÔBÐBÐBr,   zport z% is in use by another process (pids: rÈ  uR   , not a Photon sidecar) â€” free it or set PHOTON_SIDECAR_PORT to a different portz5[photon] reaping orphaned sidecar (pid %d) on port %dç      @c              3  óB   •K  — | ]}‰                      |¦  «        V — Œd S r˜   )rD  )rv  Úpr…   s     €r"   rx  z4PhotonAdapter._reap_stale_sidecar.<locals>.<genexpr>  s/   øè è € Ð,OÐ,OÀA¨T¯_ª_¸QÑ-?Ô-?Ð,OÐ,OÐ,OÐ,OÐ,OÐ,Or,   gš™™™™™¹?çš™™™™™É?z. is also held by non-sidecar processes (pids: u<   ) â€” free it or set PHOTON_SIDECAR_PORT to a different port)ÚsysÚplatformr³   r‹  ÚpostrM  rK  rP  ÚRequestErrorr¯   Ú	to_threadr¸  rJ   rK   r   rf   ÚsignalÚSIGTERMrB   r’  ry  r»  rD  ÚSIGKILL)r…   r—  rN  ÚstaleÚforeignr1   Údeadlines   `      r"   Ú_reap_stale_sidecarz!PhotonAdapter._reap_stale_sidecarÔ  sÕ  øè è € õ Œ<˜7Ò"Ð"ØˆFð	ÝÔ(°ÀÐFÑFÔFð ð ð ð ð ð ð È&Ø—k’kØO˜dÔ0ÐOÐO°4Ô3EÐOÐOÐOØ5°tÔ7JÐKð "ñ ô ð ð ð ð ð ð ð ðð ð ñ ô ð ð ð ð ð ð ð ð ð ð ð ð ð ð ð ð ð ð øøøð ð ð ð øøõ
 Ô!ð 	ð 	ð 	ØˆFˆFð	øøøð	Cð 	Cð 	Cð 	Cð 	Cõ
  'Ô0°Ñ:Ô:Ð:Ð:Ð:Ð:Ð:Ð:‰ˆˆwØð 	ÝðJ˜Ô*ð Jð JØ!Ð. YðJð Jð Jñô ð ð
 ð 	ð 	ˆCÝ�NŠNØGØ�TÔ'ñô ð ðÝ”˜�Vœ^Ñ,Ô,Ð,Ð,øÝð ð ð Ø�ðøøøå”9‘;”; Ñ$ˆÝŒi‰kŒk˜HÒ$Ð$­Ð,OÐ,OÐ,OÐ,OÈÐ,OÑ,OÔ,OÑ)OÔ)OÐ$Ý”- Ñ$Ô$Ð$Ð$Ð$Ð$Ð$Ð$Ð$õ Œi‰kŒk˜HÒ$Ð$­Ð,OÐ,OÐ,OÐ,OÈÐ,OÑ,OÔ,OÑ)OÔ)OÐ$àð 	ð 	ˆCØ�Š˜sÑ#Ô#ð ðÝ”G˜C¥¤Ñ0Ô0Ð0Ð0øÝð ð ð Ø�Dðøøøðõ Œm˜CÑ Ô Ð Ð Ð Ð Ð Ð Ð Øð 	Ýð;˜Ô*ð ;ð ;Ø$+ð;ð ;ð ;ñô ð ð	ð 	sX   —B ³6A;Á)B Á;
BÂB ÂBÂ	B ÂB ÂB Ä
D*Ä*
D7Ä6D7Ç(HÈ
HÈHc              ƒ  óÆ	  K  — t          ¦   «         s‚t                               dt          ¦   «         ¦  «         t	          j        t          ¦  «        ƒ d {V —† t          ¦   «         s.t          dt          ¦   «         › dt          ¦   «         › d�¦  «        ‚t          ¦   «         r9t           	                    d¦  «         t	          j        t          ¦  «        ƒ d {V —† |  
                    ¦   «         ƒ d {V —† t          j                             ¦   «         }| j        |d<   | j        |d<   t!          | j        ¦  «        |d<   | j        |d	<   | j        |d
<   d|d<   ddlm} 	 t	          j        t,          j        | j        t!          t          ¦   «         dz  ¦  «        t!          t          ¦   «         ¦  «        gdddddd |¦   «         ¬¦	  «	        ƒ d {V —†}|j        dk    r/t          |j        p|j        pd                     ¦   «         ¦  «        ‚|j                             ¦   «         r2t                               d|j                             ¦   «         ¦  «         n2# t<          $ r%}t           	                    d|¦  «         Y d }~nd }~ww xY wt-          j        | j        t!          t          ¦   «         dz  ¦  «        gt,          j         t,          j         t,          j!        |tD          j#        dk     |¦   «         ¬¦  «        | _$        t	          j%        ¦   «         }| &                    |  '                    | j$        ¦  «        ¦  «        | _(        tS          j)        ¦   «         dz   }d }tU          j+        dd¬¦  «        4 ƒd {V —†}tS          j)        ¦   «         |k     �r| j$         ,                    ¦   «         �+t[          ¦   «          t          d| j$        j        › d �¦  «        ‚	 | .                    d!| j        › d"| j        › d#�d$| j        i¬%¦  «        ƒ d {V —†}	|	j/        d&k    r9ta          | j        | j        | j$        j1        ¦  «         	 d d d ¦  «        ƒd {V —† d S n# tT          j2        $ r}
|
}Y d }
~
nd }
~
ww xY wt	          j3        d'¦  «        ƒ d {V —† tS          j)        ¦   «         |k     �°	 d d d ¦  «        ƒd {V —† n# 1 ƒd {V —†swxY w Y   t[          ¦   «          t          d(|› �¦  «        ‚))Nz7[photon] sidecar deps not installed; installing into %sz0Photon sidecar deps could not be installed into z& (see log for the npm error). Run: cd z' && npm ci   (or `hermes photon setup`)zX[photon] sidecar deps are stale (lockfile newer than install); reinstalling before startrã   rå   r5  ÚPHOTON_SIDECAR_BINDr6  rC  ÚPHOTON_SIDECAR_WATCH_STDINr   rÌ   z$patch-spectrum-mixed-attachments.mjsTr:   rF   é
   F)rÐ   r�   r<   rÑ   r®   rÒ   rÓ   r¼   rË  z<[photon] failed to apply Spectrum mixed attachment patch: %sz	index.mjsrF  )ÚstdinrÜ   rÛ   ÚenvÚstart_new_sessionrÓ   rk   rG  r‡  z Photon sidecar exited with code z before becoming readyr°  r±  rÁ  r²  rH  r�   rR  z0Photon sidecar did not become ready within 15s: )4r¡   rJ   rÝ   rq   r¯   rW  rß   r¸  rÊ   rK   r^  r   ÚenvironÚcopyrH  rI  r0   rK  rM  rP  rÕ   rÍ   rÖ   r×   rR  rÙ   rÛ   rÜ   rÀ   rÂ   rI   ÚPopenÚPIPEÚSTDOUTrS  rT  rX  rŽ  r�  Ú_supervise_sidecarrY  r’  r³   r‹  Úpollr_   rU  r~   rQ   r1   rV  r»  )r…   rd  rÍ   Úpatchr«   Úloopr]  Úlast_errr—  r¾  rP   s              r"   rŒ  zPhotonAdapter._start_sidecar  sV  è è € Ý%Ñ'Ô'ð 	õ �KŠKØIÝ‘”ñô ð õ Ô#Õ$;Ñ<Ô<Ð<Ð<Ð<Ð<Ð<Ð<Ð<Ý)Ñ+Ô+ð Ý"ðWÝ#‘~”~ðWð Wå+™~œ~ðWð Wð Wñô ð õ Ñ Ô ð 	=Ý�NŠNð,ñô ð õ Ô#Õ$;Ñ<Ô<Ð<Ð<Ð<Ð<Ð<Ð<Ð<Ø×&Ò&Ñ(Ô(Ð(Ð(Ð(Ð(Ð(Ð(Ð(åŒj�oŠoÑÔˆØ#'Ô#3ˆÐÑ Ø'+Ô';ˆÐ#Ñ$Ý%(¨Ô);Ñ%<Ô%<ˆÐ!Ñ"Ø%)Ô%7ˆÐ!Ñ"Ø&*Ô&9ˆÐ"Ñ#ð -0ˆÐ(Ñ)ð 	EÐDÐDÐDÐDÐDð	õ "Ô+Ý”à”NÝ�™œÐ)OÑOÑPÔPÝ�™œÑ'Ô'ðð
  $Ø G°IØØð 1Ð0Ñ2Ô2ðñ ô ð ð ð ð ð ð ˆEð Ô 1Ò$Ð$Ý" E¤LÐ$F°E´LÐ$FÀB×#MÒ#MÑ#OÔ#OÑPÔPÐPØŒ|×!Ò!Ñ#Ô#ð BÝ—’˜]¨E¬L×,>Ò,>Ñ,@Ô,@ÑAÔAÐAøøÝð 	ð 	ð 	Ý�NŠNØNØñô ð ð ð ð ð ð øøøøð	øøøõ (Ô-ØŒ^�S¥¡¤°+Ñ!=Ñ>Ô>Ð?Ý”/Ý”?ÝÔ$ØÝ"œ|¨wÒ6ð -Ð,Ñ.Ô.ð
ñ 
ô 
ˆÔõ Ô%Ñ'Ô'ˆØ(,×(8Ò(8Ø×#Ò# DÔ$6Ñ7Ô7ñ)
ô )
ˆÔ%õ
 ”9‘;”; Ñ%ˆØ(,ˆÝÔ$¨S¸EÐBÑBÔBð 	)ð 	)ð 	)ð 	)ð 	)ð 	)ð 	)ÀfÝ”)‘+”+ Ò(Ñ(ØÔ%×*Ò*Ñ,Ô,Ð8Ý*Ñ,Ô,Ð,Ý&ðQØÔ-Ô8ðQð Qð Qñô ð ð!Ø!'§¢ØS $Ô"4ÐSÐS°tÔ7IÐSÐSÐSØ!9¸4Ô;NÐ Oð "-ñ "ô "ð ð ð ð ð ð �Dð Ô'¨3Ò.Ð.õ .Ø Ô.Ø Ô/Ø Ô.Ô2ñô ð ð
 ð-	)ð 	)ð 	)ñ 	)ô 	)ð 	)ð 	)ð 	)ð 	)ð 	)ð 	)ð 	)ð 	)ð 	)ð /øõ Ô)ð !ð !ð !Ø �H�H�H�H�H�Høøøøð!øøøå”m CÑ(Ô(Ð(Ð(Ð(Ð(Ð(Ð(Ð(õ1 ”)‘+”+ Ò(Ñ(Ð(ð	)ð 	)ð 	)ñ 	)ô 	)ð 	)ð 	)ð 	)ð 	)ð 	)ð 	)ð 	)ð 	)ð 	)ð 	)ð 	)ð 	)ð 	)ð 	)ð 	)ð 	)ð 	)ð 	)øøøð 	)ð 	)ð 	)ð 	)õ4 	Ñ Ô Ð ÝØI¸xÐIÐIñ
ô 
ð 	
sW   ÅC7I É
JÉI?É?JÍ7AR2ÏA%QÑR2ÑQ*ÑQ%Ñ R2Ñ%Q*Ñ*5R2Ò2
R<Ò?R<Úprocúsubprocess.Popenc              ƒ  óV  K  — |j         €dS |j         }t          j        ¦   «         }	 	 |                     d|j        ¦  «        ƒ d{V —†}|snBt
                               d|                     dd¦  «                             ¦   «         ¦  «         Œfn2# t          $ r%}t
           
                    d|¦  «         Y d}~nd}~ww xY w| j        ra|                     ¦   «         }t
                               d|¦  «         |                      dd	|› d
�d¬¦  «         |                      ¦   «          dS dS )z1Pump the sidecar's stdout/stderr into our logger.NTz[photon-sidecar] %sr:   rF   z&[photon-sidecar] supervisor exited: %suG   [photon] sidecar exited unexpectedly (code %s) â€” triggering reconnectÚSIDECAR_CRASHEDz)Photon sidecar exited unexpectedly (code ú)r…  )rÜ   r¯   rŽ  Úrun_in_executorÚreadlinerJ   rÝ   ÚdecodeÚrstriprI   rK   r\  rl  r   rŠ  r¬  )r…   rp  rÜ   rn  r¿  rP   Ú	exit_codes          r"   rk  z PhotonAdapter._supervise_sidecar¢  st  è è € àŒ;ÐØˆFØ”ˆÝÔ%Ñ'Ô'ˆð	Hð]Ø!×1Ò1°$¸¼ÑHÔHÐHÐHÐHÐHÐHÐH�Øð ØÝ—’Ð1°4·;²;¸wÈ	Ñ3RÔ3R×3YÒ3YÑ3[Ô3[Ñ\Ô\Ð\ð	]ð øåð 	Hð 	Hð 	HÝ�NŠNÐCÀQÑGÔGÐGÐGÐGÐGÐGÐGøøøøð	HøøøàÔ ð 	0ØŸ	š	™œˆIÝ�LŠLØYØñô ð ð ×!Ò!Ø!ØH¸IÐHÐHÐHØð "ñ ô ð ð
 ×-Ò-Ñ/Ô/Ð/Ð/Ð/ð	0ð 	0s   §A'B Â
B>ÂB9Â9B>c              ƒ  ó¢  K  — | j         }|€t          ¦   «          d S 	 |j        �+	 |j                             ¦   «          n# t          $ r Y nw xY w| j        �M	 | j                             d| j        › d| j        › d�d| j	        id¬¦  «        ƒ d {V —† n# t          $ r Y nw xY w	 | 
                    d¬¦  «         nÝ# t          j        $ rË t          j        d	k    rc	 t          j        t          j        |j        ¦  «        t&          j        ¦  «         n?# t*          t,          f$ r |                     ¦   «          Y nw xY w|                     ¦   «          	 | 
                    d¬¦  «         n)# t          j        $ r |                     ¦   «          Y nw xY wY nw xY wd | _         t          ¦   «          | j        �>t5          j        ¦   «         }| j        |ur| j                             ¦   «          d | _        d S d S # d | _         t          ¦   «          | j        �<t5          j        ¦   «         }| j        |ur| j                             ¦   «          d | _        w xY w)
Nr°  r±  z	/shutdownr²  rG  r´  rO  )r®   rF  )rX  r_   rc  ÚcloserI   r]  rU  rM  rK  rP  ÚwaitrÖ   rÚ   rS  rT  r   ÚkillpgÚgetpgidr1   rX  rY  rg   rh   Ú	terminaterf   rY  r¯   r�  rœ  )r…   rp  r�  s      r"   r¢  zPhotonAdapter._stop_sidecar½  sè  è è € ØÔ!ˆØˆ<õ #Ñ$Ô$Ð$ØˆFð@	5ð ŒzÐ%ðØ”J×$Ò$Ñ&Ô&Ð&Ð&øÝ ð ð ð Ø�Dðøøøð Ô Ð,ðØÔ+×0Ò0ØT $Ô"4ÐTÐT°tÔ7IÐTÐTÐTØ!9¸4Ô;NÐ OØ #ð 1ñ ô ð ð ð ð ð ð ð ð øõ
 !ð ð ð Ø�Dðøøøð Ø—	’	 #�	Ñ&Ô&Ð&Ð&øÝÔ,ð  ð  ð  Ý”< 7Ò*Ð*ð)Ýœ	¥"¤*¨T¬XÑ"6Ô"6½¼ÑGÔGÐGÐGøÝ.µÐ@ð )ð )ð )ØŸšÑ(Ô(Ð(Ð(Ð(ð)øøøð —N’NÑ$Ô$Ð$ð Ø—I’I c�IÑ*Ô*Ð*Ð*øÝ!Ô0ð  ð  ð  Ø—I’I‘K”K�K�K�Kð øøøøøð øøøð "&ˆDÔõ #Ñ$Ô$Ð$ØÔ,Ð8õ.  'Ô3Ñ5Ô5�ØÔ0¸ÐDÐDØÔ1×8Ò8Ñ:Ô:Ð:Ø04�Ô-Ð-Ð-ð5 9Ð8øð	 "&ˆDÔõ #Ñ$Ô$Ð$ØÔ,Ð8õ.  'Ô3Ñ5Ô5�ØÔ0¸ÐDÐDØÔ1×8Ò8Ñ:Ô:Ð:Ø04�Ô-Ð4Ð4Ð4Ð4s¼   �G4 ¥? ¾G4 ¿
AÁ	G4 ÁAÁ
G4 Á;B ÂG4 Â
B ÂG4 ÂB Â G4 Â$B; Â:G4 Â;FÃ6DÄFÄ%D:Ä7FÄ9D:Ä:FÅE)Å(FÅ)#FÆFÆFÆFÆG4 ÆFÆG4 Ç4AIc                óD   — t          j        ¦   «         | _        d| _        dS )z”Record proof the upstream gRPC channel is live, and clear failures.

        Called on every inbound line and after every successful probe.
        r   N)r’  r“  rb  ra  r«  s    r"   rÓ  z%PhotonAdapter._note_upstream_activity  s"   € õ
 (,¤~Ñ'7Ô'7ˆÔ$Ø ˆÔÐÐr,   c              ƒ  óœ  K  — | j         }|€dS d| j        › d| j        › d�}	 |                     |d| j        i| j        ¬¦  «        ƒ d{V —†}nr# t          j        $ r ‚ t          $ rV}t          |¦  «        r!t                               d|¦  «         Y d}~d	S t                               d
|¦  «         Y d}~dS d}~ww xY w|j        dk    rdndS )uê  Drive one liveness probe via the sidecar's ``/probe``.

        Returns a strict tri-state verdict:

        - ``"alive"``        â€” the sidecar completed a real upstream gRPC
          round-trip (HTTP 200). Only this counts as proof of liveness.
        - ``"hung"``         â€” the probe HTTP call itself timed out: the
          sidecar's event loop is unresponsive. Counts toward respawn.
        - ``"inconclusive"`` â€” anything else (503 from the sidecar's strict
          probe, connection refused, transport error). NEVER counts as alive
          and NEVER counts toward a respawn: a rejected upstream probe may
          just mean the network is down, and a dead sidecar process is the
          supervisor's job, not ours.
        NÚinconclusiver°  r±  z/prober²  r´  z![photon] probe HTTP call hung: %sÚhungz1[photon] probe transport error (inconclusive): %sr�   Úalive)r]  rM  rK  rU  rP  rT  r¯   rž  rI   r·   rJ   rÂ   r~   )r…   r—  r  r¾  rP   s        r"   Ú_probe_oncezPhotonAdapter._probe_once  s/  è è € ð Ô"ˆØˆ>Ø!�>ØG˜Ô*ÐGÐG¨TÔ-?ÐGÐGÐGˆð	"ØŸšØØ1°4Ô3FÐGØÔ+ð %ñ ô ð ð ð ð ð ð ˆDˆDøõ
 Ô%ð 	ð 	ð 	ØÝð 	"ð 	"ð 	"Ý  Ñ#Ô#ð Ý—’Ð@À!ÑDÔDÐDØ�v�v�v�v�vÝ�LŠLÐLÈaÑPÔPÐPØ!�>�>�>�>�>øøøøð	"øøøð Ô*¨cÒ1Ð1ˆwˆw°~ÐEs   ¢*A ÁB<Á&*B7ÂB7Â7B<Úreasonc              ƒ  óØ  K  — | j         }|€t          j        ¦   «         x}| _         |                     ¦   «         rt                               d¦  «         dS |4 ƒd{V —† t                               d|¦  «         	 |                      ¦   «         ƒ d{V —† n*# t          $ r t           	                    d¦  «         Y nw xY w	 |  
                    ¦   «         ƒ d{V —† n<# t          $ r/ t           	                    d¦  «         Y ddd¦  «        ƒd{V —† dS w xY w|                      ¦   «          t                               d¦  «         ddd¦  «        ƒd{V —† dS # 1 ƒd{V —†swxY w Y   dS )a¹  Tear down and restart the sidecar to recover a dead gRPC stream.

        A fresh ``Spectrum()`` re-subscribes the inbound stream and
        re-registers presence with Photon cloud. The inbound loop's existing
        reconnect logic re-opens the loopback NDJSON stream automatically once
        the new sidecar's ``/inbound`` is up. Guarded by a lock so overlapping
        triggers (watchdog + a manual call) can't double-spawn.
        Nz.[photon] respawn already in progress; skippingu5   [photon] presence watchdog: %s â€” respawning sidecarz.[photon] error stopping sidecar during respawnz7[photon] failed to respawn sidecar; watchdog will retryzB[photon] presence watchdog: sidecar respawned, gRPC stream renewed)rc  r¯   r”  ÚlockedrJ   rÝ   rK   r¢  rI   r×  rŒ  rÓ  )r…   r†  Úlocks      r"   Ú_respawn_sidecarzPhotonAdapter._respawn_sidecar3  sž  è è € ð Ô!ˆØˆ<Ý(/¬©¬Ð6ˆD�4Ô%Ø�;Š;‰=Œ=ð 	Ý�KŠKÐHÑIÔIÐIØˆFØð 	^ð 	^ð 	^ð 	^ð 	^ð 	^ð 	^ð 	^Ý�NŠNØGÈñô ð ðSØ×(Ò(Ñ*Ô*Ð*Ð*Ð*Ð*Ð*Ð*Ð*Ð*øÝð Sð Sð SÝ× Ò Ð!QÑRÔRÐRÐRÐRðSøøøðØ×)Ò)Ñ+Ô+Ð+Ð+Ð+Ð+Ð+Ð+Ð+Ð+øÝð ð ð Ý× Ò ØMñô ð ð ð	^ð 	^ð 	^ñ 	^ô 	^ð 	^ð 	^ð 	^ð 	^ð 	^ð 	^ð 	^ð 	^ð 	^ðøøøð ×(Ò(Ñ*Ô*Ð*Ý�KŠKÐ\Ñ]Ô]Ð]ð%	^ð 	^ð 	^ñ 	^ô 	^ð 	^ð 	^ð 	^ð 	^ð 	^ð 	^ð 	^ð 	^ð 	^ð 	^ð 	^ð 	^ð 	^ð 	^ð 	^ð 	^ð 	^ð 	^ð 	^øøøð 	^ð 	^ð 	^ð 	^ð 	^ð 	^sZ   ÁEÁ;BÂEÂ$B=Â:EÂ<B=Â=EÃCÃEÃ$DÄ EÄDÄ1EÅ
E#Å&E#c              ƒ  ó0  K  — t          j        | j        ¦  «        ƒ d{V —† | j        �rl	 t	          j        ¦   «         | j        z
  }|| j        k     r#t          j        | j        |z
  ¦  «        ƒ d{V —† ŒR|                      ¦   «         ƒ d{V —†}|dk    r|                      ¦   «          nŠ|dk    rj| xj	        dz  c_	        t                               d| j	        | j        ¦  «         | j	        | j        k    r#|                      | j	        › d�¦  «        ƒ d{V —† nt                               d¦  «         n9# t           j        $ r ‚ t           $ r t                               d¦  «         Y nw xY wt          j        | j        ¦  «        ƒ d{V —† | j        �°jdS dS )	u!  Periodically confirm the sidecar (and its stream) is responsive.

        The sidecar owns primary zombie-stream detection (silence tracking +
        upstream probe -> degraded /healthz -> exit 75). This loop is the
        adapter's conservative second layer: it probes on a long interval,
        skips the probe when natural inbound traffic already proved liveness,
        and only counts a *hung* probe (the sidecar HTTP call itself timing
        out) toward a respawn. Inconclusive probes â€” the sidecar answered but
        couldn't prove upstream liveness â€” reset nothing and trigger nothing:
        the network may just be down, and restarting can't fix that. After
        ``_probe_max_failures`` consecutive hung probes we respawn the sidecar
        to force a fresh stream.
        Nr„  rƒ  r   z$[photon] presence probe hung (%d/%d)z consecutive hung probesz6[photon] presence probe inconclusive; taking no actionz+[photon] presence watchdog iteration failed)r¯   r»  rS  r`  r’  r“  rb  r…  rÓ  ra  rJ   rK   rU  rŠ  rÂ   rž  rI   r×  )r…   ÚidleÚverdicts      r"   r•  z PhotonAdapter._presence_watchdogV  s(  è è € õ  Œm˜DÔ0Ñ1Ô1Ð1Ð1Ð1Ð1Ð1Ð1Ð1ØÔ$ñ "	6ð Põ ”~Ñ'Ô'¨$Ô*FÑF�Ø˜$Ô.Ò.Ð.Ý!œ-¨Ô(<¸tÑ(CÑDÔDÐDÐDÐDÐDÐDÐDÐDØà $× 0Ò 0Ñ 2Ô 2Ð2Ð2Ð2Ð2Ð2Ð2�Ø˜gÒ%Ð%Ø×0Ò0Ñ2Ô2Ð2Ð2Ø Ò&Ð&ð Ð(Ô(¨AÑ-Ð(Ô(Ý—N’NØ>ØÔ,¨dÔ.Fñô ð ð Ô+¨tÔ/GÒGÐGØ"×3Ò3Ø#Ô3ÐMÐMÐMñô ð ð ð ð ð ð ð øõ —L’LØPñô ð øøõ Ô)ð ð ð ØÝð Pð Pð PÝ× Ò Ð!NÑOÔOÐOÐOÐOðPøøøå”- Ô 4Ñ5Ô5Ð5Ð5Ð5Ð5Ð5Ð5Ð5ðE Ô$ñ "	6ð "	6ð "	6ð "	6ð "	6s   «AD4 Á4B?D4 Ä43E*Å)E*c              ƒ  óÈ   K  — d| _         | j        �Q| j                             ¦   «          	 | j        ƒ d {V —† n # t          j        $ r Y nt
          $ r Y nw xY wd | _        d S d S rš  )r`  r_  rœ  r¯   rž  rI   r«  s    r"   r›  zPhotonAdapter._stop_watchdog‹  sŸ   è è € Ø!&ˆÔØÔÐ*ØÔ×&Ò&Ñ(Ô(Ð(ðØÔ)Ð)Ð)Ð)Ð)Ð)Ð)Ð)Ð)øÝÔ)ð ð ð Ø�Ýð ð ð Ø�ðøøøà"&ˆDÔÐÐð +Ð*s   «9 ¹AÁ
	AÁANrò   r  Úreply_torÿ   ÚmetadatarR   r   c              ƒ  ód   K  — |                       ||                      |¦  «        ¦  «        ƒ d {V —†S r˜   )Ú_sidecar_sendÚformat_message)r…   rò   r  r�  r�  s        r"   ÚsendzPhotonAdapter.send™  s>   è è € ð ×'Ò'¨°×1DÒ1DÀWÑ1MÔ1MÑNÔNÐNÐNÐNÐNÐNÐNÐNr,   ÚquestionÚchoicesúOptional[list]Ú
clarify_idÚsession_keyc              ƒ  ó„  •K  — |s,t          ¦   «                              ||||||¦  «        ƒ d {V —†S ddlm}  ||¦  «         |                      ||t          |¦  «        ¦  «        ƒ d {V —†}|j        sLt                               d|j	        ¦  «         t          ¦   «                              ||||||¦  «        ƒ d {V —†S |S )Nr   )Úmark_awaiting_textz<[photon] poll clarify failed (%s); falling back to text list)
rƒ   Úsend_clarifyÚtools.clarify_gatewayr›  Ú_sidecar_send_pollrŸ  ÚsuccessrJ   rK   r   )
r…   rò   r•  r–  r˜  r™  r�  r›  rÞ   r†   s
            €r"   rœ  zPhotonAdapter.send_clarify­  s(  øè è € ð ð 	õ ™œ×-Ò-Ø˜ 7¨J¸ÀXñô ð ð ð ð ð ð ð ð
 	=Ð<Ð<Ð<Ð<Ð<àÐ˜:Ñ&Ô&Ð&Ø×.Ò.¨w¸Å$ÀwÁ-Ä-ÑPÔPÐPÐPÐPÐPÐPÐPˆØŒ~ð 	õ
 �NŠNØNØ”ñô ð õ ™œ×-Ò-Ø˜ 7¨J¸ÀXñô ð ð ð ð ð ð ð ð ˆr,   Ú	image_urlÚcaptionc              ƒ  óê   •K  — 	 ddl m}  ||¦  «        ƒ d {V —†}n:# t          $ r- t          ¦   «                              ||||¦  «        ƒ d {V —†cY S w xY w|                      |||¬¦  «        ƒ d {V —†S )Nr   )Úcache_image_from_url©r¡  )Úgateway.platforms.baser£  rI   rƒ   Ú
send_imageÚ_sidecar_send_attachment)	r…   rò   r   r¡  r�  r�  r£  Ú
local_pathr†   s	           €r"   r¦  zPhotonAdapter.send_imageØ  sÜ   øè è € ð	SØCÐCÐCÐCÐCÐCà3Ð3°IÑ>Ô>Ð>Ð>Ð>Ð>Ð>Ð>ˆJˆJøÝð 	Sð 	Sð 	Så™œ×+Ò+¨G°YÀÈÑRÔRÐRÐRÐRÐRÐRÐRÐRÐRÐRð	Søøøð ×2Ò2Ø�Z¨ð 3ñ 
ô 
ð 
ð 
ð 
ð 
ð 
ð 
ð 	
s   … �4AÁAÚ
image_pathc              ‹  óB   K  — |                       |||¬¦  «        ƒ d {V —†S ©Nr¤  ©r§  )r…   rò   r©  r¡  r�  r�  Úkwargss          r"   Úsend_image_filezPhotonAdapter.send_image_fileë  óJ   è è € ð ×2Ò2Ø�Z¨ð 3ñ 
ô 
ð 
ð 
ð 
ð 
ð 
ð 
ð 	
r,   Ú
audio_pathc              ‹  óD   K  — |                       |||d¬¦  «        ƒ d {V —†S )Nrò  )r¡  Úkindr¬  )r…   rò   r°  r¡  r�  r�  r­  s          r"   Ú
send_voicezPhotonAdapter.send_voiceø  sL   è è € ð ×2Ò2Ø�Z¨°wð 3ñ 
ô 
ð 
ð 
ð 
ð 
ð 
ð 
ð 	
r,   Ú
video_pathc              ‹  óB   K  — |                       |||¬¦  «        ƒ d {V —†S r«  r¬  )r…   rò   r´  r¡  r�  r�  r­  s          r"   Ú
send_videozPhotonAdapter.send_video  r¯  r,   Ú	file_pathÚ	file_namec              ‹  óD   K  — |                       ||||¬¦  «        ƒ d {V —†S )N)r   r¡  r¬  )r…   rò   r·  r¡  r¸  r�  r�  r­  s           r"   Úsend_documentzPhotonAdapter.send_document  sL   è è € ð ×2Ò2Ø�Y Y¸ð 3ñ 
ô 
ð 
ð 
ð 
ð 
ð 
ð 
ð 	
r,   Úanimation_urlc              ƒ  óD   K  — |                       |||||¦  «        ƒ d {V —†S r˜   )r¦  )r…   rò   r»  r¡  r�  r�  s         r"   Úsend_animationzPhotonAdapter.send_animation   sG   è è € ð —_’_Ø�] G¨X°xñ
ô 
ð 
ð 
ð 
ð 
ð 
ð 
ð 	
r,   r  Úoptionsú	list[str]c              ƒ  ó^   K  — |                       ||t          |pg ¦  «        ¦  «        ƒ d{V —†S )züSend a native iMessage poll through Photon Spectrum.

        Thin public wrapper over :meth:`_sidecar_send_poll`, the single
        implementation of the sidecar's ``/send-poll`` primitive (also used
        by the poll-backed clarify path).
        N)rž  rŸ  )r…   rò   r  r¾  r�  s        r"   Ú	send_pollzPhotonAdapter.send_poll-  s>   è è € ð ×,Ò,¨W°e½TÀ'À-ÈRÑ=PÔ=PÑQÔQÐQÐQÐQÐQÐQÐQÐQr,   Úeffectc              ƒ  ó¶  K  — |                      ¦   «         r|                      ¦   «         st          dd¬¦  «        S 	 |                      d||                      ¦   «         |                      ¦   «         dœ¦  «        ƒ d{V —†}n5# t          $ r(}t          dt	          |¦  «        ¬¦  «        cY d}~S d}~ww xY wt          d|                     d¦  «        ¬	¦  «        S )
z9Send text with a native iMessage bubble or screen effect.Fztext and effect are required©rŸ  r   z/send-effect)ÚspaceIdr�   rÂ  NTrÒ  ©rŸ  rã  )rÀ   r   rÍ  rI   r0   r’   )r…   rò   r�   rÂ  r�  rŽ   rP   s          r"   Úsend_effectzPhotonAdapter.send_effect<  sÿ   è è € ð �zŠz‰|Œ|ð 	S 6§<¢<¡>¤>ð 	SÝ eÐ3QÐRÑRÔRÐRð	;Ø×+Ò+ØØ#¨T¯ZªZ©\¬\ÀVÇ\Â\Á^Ä^ÐTÐTñô ð ð ð ð ð ð ˆDˆDøõ ð 	;ð 	;ð 	;Ý eµ3°q±6´6Ð:Ñ:Ô:Ð:Ð:Ð:Ð:Ð:Ð:øøøøð	;øøøå $°4·8²8¸KÑ3HÔ3HÐIÑIÔIÐIs   ½AB Â
B4ÂB/Â)B4Â/B4c              ƒ  ó<  K  — t          j         ¦   «         }|| j                             |d¦  «        z
  t          k     rd S || j        |<   	 |                      d|ddœ¦  «        ƒ d {V —† d S # t
          $ r&}t                               d|¦  «         Y d }~d S d }~ww xY w)Nr@  ú/typingÚstart©rÅ  rÇ  z[photon] send_typing failed: %s)r’  rh  r’   Ú_TYPING_COOLDOWN_SECONDSrÍ  rI   rJ   rÂ   )r…   rò   r�  rß  rP   s        r"   Úsend_typingzPhotonAdapter.send_typingO  sá   è è € ÝŒi‰kŒkˆØ�Ô'×+Ò+¨G°SÑ9Ô9Ñ9Õ<TÒTÐTØˆFØ*-ˆÔ˜wÑ'ð	?Ø×$Ò$Ø w¸ÐAÐAñô ð ð ð ð ð ð ð ð ð øõ ð 	?ð 	?ð 	?Ý�LŠLÐ:¸AÑ>Ô>Ð>Ð>Ð>Ð>Ð>Ð>Ð>øøøøð	?øøøs   Á
A+ Á+
BÁ5BÂBc              ƒ  óæ   K  — | j                              |d ¦  «         	 |                      d|ddœ¦  «        ƒ d {V —† d S # t          $ r&}t                               d|¦  «         Y d }~d S d }~ww xY w)NrÉ  ÚstoprË  z[photon] stop_typing failed: %s)rh  ræ  rÍ  rI   rJ   rÂ   )r…   rò   rP   s      r"   Ústop_typingzPhotonAdapter.stop_typing[  s¹   è è € ØÔ×"Ò" 7¨DÑ1Ô1Ð1ð	?Ø×$Ò$Ø w¸Ð@Ð@ñô ð ð ð ð ð ð ð ð ð øõ ð 	?ð 	?ð 	?Ý�LŠLÐ:¸AÑ>Ô>Ð>Ð>Ð>Ð>Ð>Ð>Ð>øøøøð	?øøøs   ŸA  Á 
A0Á
A+Á+A0iè  r�   c                ó  — |sd S | j         }||v r||= t          j        ¦   «         ||<   t          |¦  «        | j        k    rDt	          |                     ¦   «         ¦  «        d t          |¦  «        | j        z
  …         D ]}||= Œd S d S r˜   )re  r’  rÜ  Ú_SENT_IDS_MAXrŸ  rÞ  )r…   rã  Úsentrâ  s       r"   Ú_record_sent_messagez"PhotonAdapter._record_sent_messagem  s¡   € Øð 	ØˆFØÔ%ˆØ˜ÐÐØ�ZÐ Ýœ9™;œ;ˆˆZÑÝˆt‰9Œ9�tÔ)Ò)Ð)Ý˜DŸIšI™KœKÑ(Ô(Ð)I­3¨t©9¬9°tÔ7IÑ+IÐ)IÔJð ð �Ø˜�I�Ið *Ð)ðð r,   z^any;-;(\+\d{6,})$c                óh   — | j                              |¦  «        }|r|                     d¦  «        n|S )Nr   )Ú_DM_CHAT_GUID_REr|  r  )Úclsrò   r|  s      r"   Ú_normalize_chat_keyz!PhotonAdapter._normalize_chat_key  s1   € àÔ$×*Ò*¨7Ñ3Ô3ˆØ!&Ð3ˆu�{Š{˜1‰~Œ~ˆ~¨GÐ3r,   c                ó  — |r|sd S |                       |¦  «        }| j        }||v r||= |||<   t          |¦  «        | j        k    rDt	          |                     ¦   «         ¦  «        d t          |¦  «        | j        z
  …         D ]}||= Œd S d S r˜   )rØ  rf  rÜ  Ú_LAST_INBOUND_CHATS_MAXrŸ  rÞ  )r…   rò   rã  ÚkeyÚlastrâ  s         r"   r#  z"PhotonAdapter._record_last_inbound„  s·   € ð ð 	˜jð 	ØˆFØ×&Ò& wÑ/Ô/ˆØÔ)ˆØ�$ˆ;ˆ;Ø�S�	ØˆˆS‰	Ýˆt‰9Œ9�tÔ3Ò3Ð3Ý˜DŸIšI™KœKÑ(Ô(Ø:•#�d‘)”)˜dÔ:Ñ:Ð:ôð ð �ð ˜�I�Ið	 4Ð3ðð r,   c                óZ  — |rt          |¦  «        sd S |                      |¦  «        }| j        }||v r||= t          j        ¦   «         ||<   t	          |¦  «        | j        k    rDt          |                     ¦   «         ¦  «        d t	          |¦  «        | j        z
  …         D ]}||= Œd S d S r˜   )r
  rØ  rg  r’  rÜ  rÚ  rŸ  rÞ  )r…   rò   r�   rÛ  Úrecentrâ  s         r"   r&  z%PhotonAdapter._record_recent_richlink”  sÄ   € Øð 	Õ1°$Ñ7Ô7ð 	ØˆFØ×&Ò& wÑ/Ô/ˆØÔ/ˆØ�&ˆ=ˆ=Ø�s�Ý”i‘k”kˆˆs‰Ýˆv‰;Œ;˜Ô5Ò5Ð5Ý˜FŸKšK™MœMÑ*Ô*Ø<•#�f‘+”+ Ô <Ñ<Ð<ôð  ð  �ð ˜3�K�Kð	 6Ð5ð ð  r,   c                ó
  — |rt          |¦  «        sdS |                      |¦  «        }| j                             |¦  «        }|€dS t	          j        ¦   «         |z
  t
          k    r| j                             |d ¦  «         dS dS )NFT)r.  rØ  rg  r’   r’  Ú"_RICHLINK_PREVIEW_SUPPRESS_SECONDSræ  )r…   rò   r  rÛ  rÜ  s        r"   r"  z)PhotonAdapter._is_recent_richlink_preview¢  sŒ   € Øð 	Õ:¸7ÑCÔCð 	Ø�5Ø×&Ò& wÑ/Ô/ˆØÔ-×1Ò1°#Ñ6Ô6ˆØˆ<Ø�5ÝŒ9‰;Œ;˜ÑÕ BÒBÐBØÔ*×.Ò.¨s°DÑ9Ô9Ð9Ø�5Øˆtr,   c                óx   — t          j        dd¦  «                             ¦   «                              ¦   «         dv S )NÚPHOTON_REACTIONSrü   >   rC  rD  rE  rù   rý   r«  s    r"   Ú_reactions_enabledz PhotonAdapter._reactions_enabled®  s:   € ÝŒyÐ+¨WÑ5Ô5×;Ò;Ñ=Ô=×CÒCÑEÔEð J
ð 
ð 	
r,   r
  c              ƒ  ó²   K  — 	 |                       d|||dœ¦  «        ƒ d{V —† dS # t          $ r&}t                               d|¦  «         Y d}~dS d}~ww xY w)zCTapback ``emoji`` onto a message. Soft-fails (False), never raises.z/react)rÅ  rÒ  r
  NTz [photon] add_reaction failed: %sF©rÍ  rI   rJ   rÂ   )r…   rò   rã  r
  rP   s        r"   Ú_add_reactionzPhotonAdapter._add_reaction³  s™   è è € ð	Ø×$Ò$ØØ#°*ÀuÐMÐMñô ð ð ð ð ð ð ð ð �4øÝð 	ð 	ð 	Ý�LŠLÐ;¸QÑ?Ô?Ð?Ø�5�5�5�5�5øøøøð	øøøs   „ & ¦
A°AÁAc              ƒ  ó°   K  — 	 |                       d||dœ¦  «        ƒ d{V —† dS # t          $ r&}t                               d|¦  «         Y d}~dS d}~ww xY w)a+  Retract our tapback from a message. Soft-fails (False), never raises.

        The sidecar tracks one reaction handle per target message; after a
        sidecar restart the handle is gone and removal is best-effort (the
        stale tapback self-heals when the next reaction replaces it).
        z/unreact)rÅ  rÒ  NTz#[photon] remove_reaction failed: %sFrå  )r…   rò   rã  rP   s       r"   Ú_remove_reactionzPhotonAdapter._remove_reactionÁ  s–   è è € ð	Ø×$Ò$Ø¨¸jÐIÐIñô ð ð ð ð ð ð ð ð �4øÝð 	ð 	ð 	Ý�LŠLÐ>ÀÑBÔBÐBØ�5�5�5�5�5øøøøð	øøøs   „% ¥
A¯AÁAc              ƒ  óÄ   K  — |p,| j                              |                      |¦  «        ¦  «        }|sdddœS |                      |||¦  «        ƒ d{V —†}|sdddœS d|dœS )uA  Tapback ``emoji`` onto a message in ``chat_id``.

        Without ``message_id``, targets the chat's most recent inbound
        message (typically the one the agent is responding to). iMessage
        maps â�¤ï¸�ðŸ‘�ðŸ‘ŽðŸ˜‚â€¼ï¸�â�“ to native tapbacks; anything else uses Apple's
        custom-emoji reaction.
        Fuk   no message to react to â€” pass message_id (no inbound message seen in this chat since the gateway started)rÄ  Nz'reaction failed (see gateway debug log)TrÆ  )rf  r’   rØ  ræ  )r…   rò   r
  rã  ÚtargetrÆ  s         r"   Úadd_reactionzPhotonAdapter.add_reaction×  s²   è è € ð ð 
˜tÔ9×=Ò=Ø×$Ò$ WÑ-Ô-ñ 
ô  
ˆð ð 	à ðOðð ð ð
 ×%Ò% g¨v°uÑ=Ô=Ð=Ð=Ð=Ð=Ð=Ð=ˆØð 	à ØBðð ð ð  ¨vÐ6Ð6Ð6r,   c              ƒ  óÂ   K  — |p,| j                              |                      |¦  «        ¦  «        }|sdddœS |                      ||¦  «        ƒ d{V —†}|sdddœS d|dœS )z1Retract our tapback from a message (best-effort).Fu)   no message to unreact â€” pass message_idrÄ  Nz&unreact failed (see gateway debug log)TrÆ  )rf  r’   rØ  rè  )r…   rò   rã  rê  rÆ  s        r"   Úremove_reactionzPhotonAdapter.remove_reactionõ  s­   è è € ð ð 
˜tÔ9×=Ò=Ø×$Ò$ WÑ-Ô-ñ 
ô  
ˆð ð 	à ØDðð ð ð ×(Ò(¨°&Ñ9Ô9Ð9Ð9Ð9Ð9Ð9Ð9ˆØð 	à ØAðð ð ð  ¨vÐ6Ð6Ð6r,   r   c              ƒ  óÎ   K  — |                       ¦   «         sdS t          |j        dd¦  «        }t          |dd¦  «        }|r!|r!|                      ||d¦  «        ƒ d{V —† dS dS dS )u=   Tapback ðŸ‘€ on the triggering message while the agent works.Nrò   rã  u   ðŸ‘€)rã  r²   r  ræ  )r…   rØ  rò   rã  s       r"   Úon_processing_startz!PhotonAdapter.on_processing_start		  sœ   è è € à×&Ò&Ñ(Ô(ð 	ØˆFÝ˜%œ,¨	°4Ñ8Ô8ˆÝ˜U L°$Ñ7Ô7ˆ
Øð 	H�zð 	HØ×$Ò$ W¨j¸,ÑGÔGÐGÐGÐGÐGÐGÐGÐGÐGÐGð	Hð 	Hð 	Hð 	Hr,   u   ðŸ‘�u   ðŸ‘Žc              ƒ  ó   K  — |d|dœS )zÖReturn whatever we know about a Spectrum space id.

        Photon's ``space.id`` is opaque; the inbound event also carries the
        DM/group type, but here we only have the id, so infer conservatively.
        rë  )r   r´   r#  r^   )r…   rò   s     r"   Úget_chat_infozPhotonAdapter.get_chat_info	  s   è è € ð  ¨°WÐ=Ð=Ð=r,   c                ó@   — t          ¦   «         r|S t          |¦  «        S r˜   )rþ   r   )r…   r  s     r"   r“  zPhotonAdapter.format_message"	  s%   € õ ÑÔð 	ØˆNÝ˜gÑ&Ô&Ð&r,   r   c                ó´   ‡— t          j        | ¦  «        rdS | sdS |                      ¦   «         Šd‰v sd‰v rdS t          ˆfd„t          D ¦   «         ¦  «        S )NTFzretryable=falseÚauth_or_configc              3  ó    •K  — | ]}|‰v V — Œ	d S r˜   r^   )rv  ÚpatÚlowereds     €r"   rx  z4PhotonAdapter._is_retryable_error.<locals>.<genexpr>3	  s'   øè è € ÐHÐH c�3˜'�>ÐHÐHÐHÐHÐHÐHr,   )r   Ú_is_retryable_errorrµ   ry  Ú_PHOTON_RETRYABLE_PATTERNS)r   r÷  s    @r"   rø  z!PhotonAdapter._is_retryable_error*	  st   ø€ åÔ2°5Ñ9Ô9ð 	Ø�4Øð 	Ø�5Ø—+’+‘-”-ˆØ Ð'Ð'Ð+;¸wÐ+FÐ+FØ�5ÝÐHÐHÐHÐHÕ-GÐHÑHÔHÑHÔHÐHr,   rÞ   c                ó–   — | j         }t          |t          ¦  «        o-|                     d¦  «        du o|                     d¦  «        dv S )u  True when the sidecar classified the failure as permanent.

        ``auth_or_config`` and ``target_not_allowed`` cannot be fixed by
        retrying or by the plain-text fallback resend â€” attempting either
        just double-sends a doomed request (issue #50971).
        r}   Fr|   )rô  r‘   )Úraw_responserW   rX   r’   )rÞ   rZ   s     r"   Ú_is_permanent_sidecar_failurez+PhotonAdapter._is_permanent_sidecar_failure5	  sS   € ð Ô!ˆå�s�DÑ!Ô!ð SØ—’˜Ñ$Ô$¨Ð-ðSà—’˜Ñ&Ô&Ð*RÐRð	
r,   r   rG  Úmax_retriesÚ
base_delayr¢   c              ƒ  óÌ  K  — |                       |¦  «        }|                      ||||¬¦  «        ƒ d{V —†}|j        r|S |                      |¦  «        r|S |j        pd}	|j        p|                      |	¦  «        }
|
s|                      |	¦  «        r|S |
rßt          d|dz   ¦  «        D ]¯}|d|dz
  z  z  }t           
                    d||||	¦  «         t          j        |¦  «        ƒ d{V —† |                      ||||¬¦  «        ƒ d{V —†}|j        r|c S |j        pd}	|                      |¦  «        r|c S |j        s|                      |	¦  «        s nŒ°t                               d||	¦  «         t           
                    d|	¦  «         |                      ||d| j        …         d	d	¬
¦  «        ƒ d{V —†}|j        s t                               d|j        ¦  «         |S )u÷   Retry sends without the generic Markdown banner.

        Photon replies are markdown (rendered by iMessage) or stripped plain
        text under ``PHOTON_MARKDOWN=false`` â€” either way the gateway's
        generic banner never applies.
        )rò   r  r�  r�  Nr¼   r   r¶  z;[photon] Send failed (attempt %d/%d, retrying in %.1fs): %sz8[photon] Failed to deliver response after %d retries: %sz6[photon] Send failed: %s - retrying plain-text messageF©r  Úmarkdownz)[photon] Plain-text retry also failed: %s)r“  r”  rŸ  rü  r   r}   rø  r·   ÚrangerJ   rK   r¯   r»  r’  ÚMAX_MESSAGE_LENGTH)r…   rò   r  r�  r�  rý  rþ  r�   rÞ   Ú	error_strÚ
is_networkÚattemptÚdelayÚfallback_results                 r"   Ú_send_with_retryzPhotonAdapter._send_with_retryD	  s¸  è è € ð ×"Ò" 7Ñ+Ô+ˆØ—y’yØØØØð	 !ñ 
ô 
ð 
ð 
ð 
ð 
ð 
ð 
ˆð Œ>ð 	ØˆMà×-Ò-¨fÑ5Ô5ð 	ð ˆMà”LÐ& Bˆ	ØÔ%ÐL¨×)AÒ)AÀ)Ñ)LÔ)Lˆ
Øð 	˜d×4Ò4°YÑ?Ô?ð 	ØˆMàð 	Ý   K°!¡OÑ4Ô4ð ð �Ø" a¨G°a©KÑ&8Ñ9�Ý—’ØQØ˜[¨%°ñô ð õ ”m EÑ*Ô*Ð*Ð*Ð*Ð*Ð*Ð*Ð*Ø#ŸyšyØ#Ø Ø%Ø%ð	  )ñ  ô  ð ð ð ð ð ð �ð ”>ð "Ø!�M�M�MØ"œLÐ.¨B�	Ø×5Ò5°fÑ=Ô=ð "ð "�M�M�MØÔ(ð ¨D×,DÒ,DÀYÑ,OÔ,Oð Ø�Eøå—’ØNØ ñô ð õ 	�ŠØDØñ	
ô 	
ð 	
ð !%× 2Ò 2ØØÐ*�4Ô*Ð*Ô+ØØð	 !3ñ !
ô !
ð 
ð 
ð 
ð 
ð 
ð 
ˆð Ô&ð 	]Ý�LŠLÐDÀoÔF[Ñ\Ô\Ð\ØÐr,   r'  r  c              ƒ  óJ  K  — 	 |                       d||dœ¦  «        ƒ d {V —†}n5# t          $ r(}t          dt          |¦  «        ¬¦  «        cY d }~S d }~ww xY w|                      |                     d¦  «        ¦  «         t          d|                     d¦  «        ¬¦  «        S )Nú/send-richlink©rÅ  r  FrÄ  rÒ  TrÆ  )rÍ  rI   r   r0   rÔ  r’   )r…   r'  r  rŽ   rP   s        r"   Ú_sidecar_send_richlinkz$PhotonAdapter._sidecar_send_richlink˜	  sÕ   è è € ð	;Ø×+Ò+Ø ¨h¸sÐ"CÐ"Cñô ð ð ð ð ð ð ˆDˆDøõ ð 	;ð 	;ð 	;Ý eµ3°q±6´6Ð:Ñ:Ô:Ð:Ð:Ð:Ð:Ð:Ð:øøøøð	;øøøà×!Ò! $§(¢(¨;Ñ"7Ô"7Ñ8Ô8Ð8Ý $°4·8²8¸KÑ3HÔ3HÐIÑIÔIÐIs   „$ ¤
A®AÁAÁATr   r  r  c             ƒ  ób  K  — |rt          |¦  «        nd }|rG|                      ||¦  «        ƒ d {V —†}|j        r|S t                               d|j        ¦  «         d}t          |¦  «        | j        k    r=t                               dt          |¦  «        | j        ¦  «         |d | j        …         }||dœ}|rt          ¦   «         rd|d<   	 |  	                    d|¦  «        ƒ d {V —†}ny# t          $ r<}	t          dt          |	¦  «        |	j        |	j        dœ|	j        ¬	¦  «        cY d }	~	S d }	~	wt          $ r(}	t          dt          |	¦  «        ¬
¦  «        cY d }	~	S d }	~	ww xY w|                      |                     d¦  «        ¦  «         t          d|                     d¦  «        ¬¦  «        S )Nz>[photon] rich-link send failed, falling back to plain text: %sFz0[photon] truncating outbound from %d to %d chars©rÅ  r�   r  Úformatú/sendr{   ©rŸ  r   rû  r}   rÄ  rÒ  TrÆ  )r  r  rŸ  rJ   rK   r   rÜ  r  rþ   rÍ  ry   r   r0   r|   r}   rI   rÔ  r’   )
r…   r'  r�   r  r  Úrich_urlÚrich_resultÚbodyrŽ   rP   s
             r"   r’  zPhotonAdapter._sidecar_send¢	  s'  è è € ð 19ÐBÕ& tÑ,Ô,Ð,¸dˆØð 	Ø $× ;Ò ;¸HÀhÑ OÔ OÐOÐOÐOÐOÐOÐOˆKØÔ"ð #Ø"Ð"Ý�NŠNØPØÔ!ñô ð ð ˆHÝˆt‰9Œ9�tÔ.Ò.Ð.Ý�NŠNØBÝ�D‘	”	˜4Ô2ñô ð ð Ð1˜$Ô1Ð1Ô2ˆDØ+3¸TÐBÐBˆð ð 	(Õ)Ñ+Ô+ð 	(Ø'ˆD�‰Nð	;Ø×+Ò+¨G°TÑ:Ô:Ð:Ð:Ð:Ð:Ð:Ð:ˆDˆDøÝ!ð 		ð 		ð 		ÝØÝ˜!‘f”fà#$¤=Ø!"¤ðð ð œ+ðñ ô ð ð ð ð ð ð øøøøõ ð 	;ð 	;ð 	;Ý eµ3°q±6´6Ð:Ñ:Ô:Ð:Ð:Ð:Ð:Ð:Ð:øøøøð	;øøøà×!Ò! $§(¢(¨;Ñ"7Ô"7Ñ8Ô8Ð8Ý $°4·8²8¸KÑ3HÔ3HÐIÑIÔIÐIs0   ÃC, Ã,
E"Ã61D-Ä'E"Ä-E"Ä:EÅE"ÅE"rŸ  c              ƒ  ó@  K  — d„ |pg D ¦   «         }|r|                      ¦   «         st          dd¬¦  «        S t          |¦  «        dk     rt          dd¬¦  «        S ||                      ¦   «         d| j        …         |dœ}	 |                      d	|¦  «        ƒ d{V —†}n5# t
          $ r(}t          dt          |¦  «        ¬¦  «        cY d}~S d}~ww xY w|                      |                     d
¦  «        ¦  «         t          d|                     d
¦  «        ¬¦  «        S )züPOST a poll to the sidecar's ``/send-poll`` endpoint.

        Renders a native iMessage poll. ``options`` are choice strings; the
        sidecar's ``poll()`` builder degrades to a numbered text list on
        platforms without native polls.
        c                ó’   — g | ]D}t          |¦  «                             ¦   «         ¯#t          |¦  «                             ¦   «         ‘ŒES r^   )r0   rÀ   )rv  Úos     r"   r<  z4PhotonAdapter._sidecar_send_poll.<locals>.<listcomp>Ù	  s9   € ÐJÐJÐJ 1½3¸q¹6¼6¿<º<¹>¼>ÐJ•�A‘”—’‘”ÐJÐJÐJr,   Fzpoll title is requiredrÄ  r¶  zpoll needs at least two optionsN)rÅ  r  r¾  z
/send-pollrÒ  TrÆ  )	rÀ   r   rÜ  r  rÍ  rI   r0   rÔ  r’   )r…   r'  r  r¾  Úoptsr  rŽ   rP   s           r"   rž  z PhotonAdapter._sidecar_send_pollÐ	  sO  è è € ð KÐJ¨¨°BÐJÑJÔJˆØð 	M˜EŸKšK™MœMð 	MÝ eÐ3KÐLÑLÔLÐLÝˆt‰9Œ9�qŠ=ˆ=Ý eÐ3TÐUÑUÔUÐUàØ—[’[‘]”]Ð#< TÔ%<Ð#<Ô=Øð 
ð  
ˆð
	;Ø×+Ò+¨L¸$Ñ?Ô?Ð?Ð?Ð?Ð?Ð?Ð?ˆDˆDøÝð 	;ð 	;ð 	;Ý eµ3°q±6´6Ð:Ñ:Ô:Ð:Ð:Ð:Ð:Ð:Ð:øøøøð	;øøøà×!Ò! $§(¢(¨;Ñ"7Ô"7Ñ8Ô8Ð8Ý $°4·8²8¸KÑ3HÔ3HÐIÑIÔIÐIs   ÂB Â
CÂ)CÃCÃCr"  )r   Ú	mime_typer¡  r²  rL   r   r  r²  c             ƒ  óÆ  K  — |                       t          |¦  «        ¦  «        }|st          dd|› �¬¦  «        S |s ddl}|                     |¦  «        \  }	}
|	pd}|||dk    rdnddœ}|r||d	<   |r||d
<   |r||d<   	 |                      d|¦  «        ƒ d{V —†}ny# t          $ r<}t          dt          |¦  «        |j        |j        dœ|j        ¬¦  «        cY d}~S d}~wt          $ r(}t          dt          |¦  «        ¬¦  «        cY d}~S d}~ww xY w|  
                    |                     d¦  «        ¦  «         t          d|                     d¦  «        ¬¦  «        S )a‰  POST a local file to the sidecar's ``/send-attachment`` endpoint.

        ``kind`` is ``"voice"`` for audio sent as a voice note (downgrades
        to a plain audio attachment on platforms without voice notes),
        otherwise ``"attachment"``. spectrum-ts infers ``name`` and
        ``mimeType`` from the file extension; we only pass overrides when
        Hermes supplied them.
        Fz#unsafe or missing attachment path: rÄ  r   Nrò  r"  ©rÅ  rL   r²  r   ró  r¡  ú/send-attachmentr{   r  rÒ  TrÆ  )Úvalidate_media_delivery_pathr0   r   Ú	mimetypesÚ
guess_typerÍ  ry   r|   r}   rI   rÔ  r’   )r…   r'  rL   r   r  r¡  r²  Ú	safe_pathr  Úguessedr¦  r  rŽ   rP   s                 r"   r§  z&PhotonAdapter._sidecar_send_attachmentê	  sÿ  è è € ð* ×5Ò5µc¸$±i´iÑ@Ô@ˆ	Øð 	ÝØÐ%QÈ4Ð%QÐ%Qðñ ô ð ð ð 	(ØÐÐÐà"×-Ò-¨iÑ8Ô8‰JˆG�QØ˜ 4ˆIàØØ# wš˜�G�G°Lð 
ð  
ˆð
 ð 	 ØˆD�‰LØð 	)Ø(ˆD�ÑØð 	&Ø%ˆD�‰Oð	;Ø×+Ò+Ð,>ÀÑEÔEÐEÐEÐEÐEÐEÐEˆDˆDøÝ!ð 		ð 		ð 		ÝØÝ˜!‘f”fà#$¤=Ø!"¤ðð ð œ+ðñ ô ð ð ð ð ð ð øøøøõ ð 	;ð 	;ð 	;Ý eµ3°q±6´6Ð:Ñ:Ô:Ð:Ð:Ð:Ð:Ð:Ð:øøøøð	;øøøà×!Ò! $§(¢(¨;Ñ"7Ô"7Ñ8Ô8Ð8Ý $°4·8²8¸KÑ3HÔ3HÐIÑIÔIÐIs0   ÂB Â
DÂ(1CÃDÃDÃ,DÄ	DÄDr  c              ƒ  ó  K  — | j         €t          d¦  «        ‚d| j        › d| j        › |› �}d| j        i}t          j        dd¬¦  «        4 ƒd {V —†}|                     |||¬¦  «        ƒ d {V —†}d d d ¦  «        ƒd {V —† n# 1 ƒd {V —†swxY w Y   |j        d	k    rt          ||j        |j
        ¦  «        ‚|                     ¦   «         pi }|                     d
¦  «        st          ||j        |j
        |¦  «        ‚|S )NzPhoton adapter not connectedr°  r±  r²  rv   Fr‡  ©rD   rµ  r�   rÆ  )r]  r¸  rM  rK  rP  r³   r‹  rU  r~   r•   r�   rD   r’   )r…   rL   r  r  rµ  r—  r¾  rŽ   s           r"   rÍ  zPhotonAdapter._sidecar_call%
  sÄ  è è € àÔÐ$ÝÐ=Ñ>Ô>Ð>ð H˜Ô*ÐGÐG¨TÔ-?ÐGÀÐGÐGˆØ+¨TÔ-@ÐAˆÝÔ$¨T¸UÐCÑCÔCð 	Fð 	Fð 	Fð 	Fð 	Fð 	Fð 	FÀvØŸš S¨t¸W˜ÑEÔEÐEÐEÐEÐEÐEÐEˆDð	Fð 	Fð 	Fñ 	Fô 	Fð 	Fð 	Fð 	Fð 	Fð 	Fð 	Fð 	Fð 	Fð 	Fð 	Fð 	Fð 	Fð 	Fð 	Fð 	Fð 	Fð 	Fð 	Føøøð 	Fð 	Fð 	Fð 	FàÔ˜sÒ"Ð"Ý.¨t°TÔ5EÀtÄyÑQÔQÐQØ�yŠy‰{Œ{Ð ˜bˆØ�xŠx˜‰~Œ~ð 	XÝ.¨t°TÔ5EÀtÄyÐRVÑWÔWÐWØˆs   ÁBÂ
BÂB)r1  r   )rZ   r   r$   rm  )r�   r0   r$   r`   )r�   r0   r$   r0   )r�  r`   r$   r`   ©r$   r2   )r¿  r0   r$   r2   )rÙ  r0   r$   r`   )r¥  r0   rã  r0   r$   r2   )rØ  r  r$   r2   )r-   r.   r$   r5  )r1   r.   r$   r`   )rp  rq  r$   r2   )r$   r0   )r†  r0   r$   r2   )NN)
rò   r0   r  r0   r�  rÿ   r�  rR   r$   r   r˜   )rò   r0   r•  r0   r–  r—  r˜  r0   r™  r0   r�  rR   r$   r   )NNN)rò   r0   r   r0   r¡  rÿ   r�  rÿ   r�  rR   r$   r   )rò   r0   r©  r0   r¡  rÿ   r�  rÿ   r�  rR   r$   r   )rò   r0   r°  r0   r¡  rÿ   r�  rÿ   r�  rR   r$   r   )rò   r0   r´  r0   r¡  rÿ   r�  rÿ   r�  rR   r$   r   )NNNN)rò   r0   r·  r0   r¡  rÿ   r¸  rÿ   r�  rÿ   r�  rR   r$   r   )rò   r0   r»  r0   r¡  rÿ   r�  rÿ   r�  rR   r$   r   )
rò   r0   r  r0   r¾  r¿  r�  rR   r$   r   )
rò   r0   r�   r0   rÂ  r0   r�  rR   r$   r   )rò   r0   r$   r2   )rã  rÿ   r$   r2   )rò   r0   r$   r0   )rò   rÿ   rã  rÿ   r$   r2   )rò   r0   r�   r0   r$   r2   )rò   r0   r  r  r$   r`   ©r$   r`   )rò   r0   rã  r0   r
  r0   r$   r`   )rò   r0   rã  r0   r$   r`   )rò   r0   r
  r0   rã  rÿ   r$   r  )rò   r0   rã  rÿ   r$   r  )rØ  r   r$   r2   )rò   r0   r$   r  )r  r0   r$   r0   )r   rÿ   r$   r`   )rÞ   r   r$   r`   )NNr   rG  )rò   r0   r  r0   r�  rÿ   r�  r   rý  r.   rþ  r¢   r$   r   )r'  r0   r  r0   r$   r   )
r'  r0   r�   r0   r  r`   r  r`   r$   r   )r'  r0   r  r0   r¾  rŸ  r$   r   )r'  r0   rL   r0   r   rÿ   r  rÿ   r¡  rÿ   r²  r0   r$   r   )rL   r0   r  r  r$   r  )Lr‡   rˆ   r‰   rŠ   Ú_MAX_MESSAGE_LENGTHr  ÚSUPPORTS_MESSAGE_EDITINGr„   Ústaticmethodrj  rz  r€  r˜  r¨  r¬  rª  r�  r‘  rº  rÕ  rç  rÖ  r?  rB  rD  r^  rŒ  rk  r¢  rÓ  r…  rŠ  r•  r›  r”  rœ  r¦  r®  r³  r¶  rº  r½  rÁ  rÇ  rÍ  rÐ  rÒ  rÚ  rÔ  r  ÚcompilerÖ  ÚclassmethodrØ  r#  r&  r"  rã  ræ  rè  rë  rí  rï  Ú	_OK_EMOJIÚ_FAIL_EMOJIrñ  r“  rø  rü  r	  r  r’  rž  r§  rÍ  r‹   rŒ   s   @r"   r0  r0  µ  sÛ  ø€ € € € € ðð ð -Ðð  %ÐðC
ð C
ð C
ð C
ð C
ð C
ðN ð
ð 
ð 
ñ „\ð
ðOð Oð Oð Oð
ð ð ð ð" 5:ð @ð @ð @ð @ð @ð @ðD'"ð '"ð '"ð '"ðR!?ð !?ð !?ð !?ðFPð Pð Pð Pð&1ð &1ð &1ð &1ðP9ð 9ð 9ð 9ðvAð Að Að Að(ð ð ð ð"ð ð ð ðZ1ð Z1ð Z1ð Z1ð| ð	Qð 	Qð 	Qñ „\ð	Qð ð
8ð 
8ð 
8ñ „\ð
8ð ðð ð ñ „\ððBð Bð Bð BðHH
ð H
ð H
ð H
ðT0ð 0ð 0ð 0ð6G5ð G5ð G5ð G5ðV!ð !ð !ð !ð!Fð !Fð !Fð !FðF!^ð !^ð !^ð !^ðF36ð 36ð 36ð 36ðj
'ð 
'ð 
'ð 
'ð$ #'Ø-1ðOð Oð Oð Oð Oð6 .2ð!ð !ð !ð !ð !ð !ð !ð^ "&Ø"&Ø-1ð
ð 
ð 
ð 
ð 
ð 
ð 
ð. "&Ø"&Ø-1ð
ð 
ð 
ð 
ð 
ð" "&Ø"&Ø-1ð
ð 
ð 
ð 
ð 
ð" "&Ø"&Ø-1ð
ð 
ð 
ð 
ð 
ð" "&Ø#'Ø"&Ø-1ð
ð 
ð 
ð 
ð 
ð$ "&Ø"&Ø-1ð
ð 
ð 
ð 
ð 
ð$ .2ðRð Rð Rð Rð Rð( .2ðJð Jð Jð Jð Jð&
?ð 
?ð 
?ð 
?ð 
?ð?ð ?ð ?ð ?ð €MØ!Ðð	ð 	ð 	ð 	ð  "�r”zÐ"7Ñ8Ô8Ðàð4ð 4ð 4ñ „[ð4ðð ð ð ð  ð  ð  ð  ð
ð 
ð 
ð 
ð
ð 
ð 
ð 
ð
ð ð ð ðð ð ð ð4 %)ð	7ð 7ð 7ð 7ð 7ð> 9=ð7ð 7ð 7ð 7ð 7ð(Hð Hð Hð Hð €IØ€Kð>ð >ð >ð >ð'ð 'ð 'ð 'ð ðIð Ið Iñ „\ðIð ð
ð 
ð 
ñ „\ð
ð$ #'ØØØðRð Rð Rð Rð RðhJð Jð Jð Jð Øð,Jð ,Jð ,Jð ,Jð ,Jð ,Jð\Jð Jð Jð Jð> #Ø#'Ø!%Ø ð9Jð 9Jð 9Jð 9Jð 9Jð 9Jðvð ð ð ð ð ð ð r,   r0  r   r   c                óN  — | pd                      ¦   «         } |                      d¦  «        rt          j        S |                      d¦  «        rt          j        S |                      d¦  «        rt          j        S |                      d¦  «        rt          j        S t          j        S )Nr¼   úimage/zvideo/úaudio/zapplication/)rµ   Ú
startswithr   ÚPHOTOÚVIDEOÚAUDIOr%  )r   s    r"   rý  rý  =
  s”   € ØˆJ�B×ÒÑÔ€DØ‡‚�xÑ Ô ð !ÝÔ Ð Ø‡‚�xÑ Ô ð !ÝÔ Ð Ø‡‚�xÑ Ô ð !ÝÔ Ð Ø‡‚�~Ñ&Ô&ð $ÝÔ#Ð#ÝÔÐr,   ú.jpgz.pngz.gifz.webp)z
image/jpegz	image/pngz	image/gifz
image/webpz
image/heicz
image/heifz
image/tiffú.mp3z.oggz.wavrô  ú.m4a)z	audio/mp3z
audio/mpegz	audio/oggz	audio/wavrõ  rù  z	audio/aacrö  r   r÷  c               ó  — |                       d¦  «        }|sdS 	 t          j        |¦  «        }n:# t          t          f$ r&}t
                               d|¦  «         Y d}~dS d}~ww xY wddlm}m	}m
}	 |pd                     ¦   «         }|rt          |¦  «        j        nd}
	 |                     d¦  «        rF|
pt                                |d¦  «        }	  |	||¦  «        S # t          $ r  |||¦  «        cY S w xY w|s|                     d	¦  «        r-|
pt                                 ||rd
nd¦  «        } |||¦  «        S  |||¦  «        S # t"          $ r'}t
                               d||¦  «         Y d}~dS d}~ww xY w)a  Decode a base64-inlined inbound attachment and cache it locally.

    The sidecar inlines the attachment bytes as ``content["data"]`` (base64).
    We decode them and route to the shared media cache by MIME type, returning
    the cached absolute path so the caller can populate ``media_urls`` (which
    the gateway then hands to the model). Returns ``None`` when there are no
    bytes (over the sidecar's inline cap or a failed read) or when caching
    fails, so the caller can fall back to a text marker.
    rŽ   Nz6[photon] failed to decode inbound attachment bytes: %sr   )Úcache_audio_from_bytesÚcache_document_from_bytesÚcache_image_from_bytesr¼   r/  r5  r0  r7  r6  z2[photon] failed to cache inbound attachment %s: %s)r’   Úbase64Ú	b64decoderV   rd   rJ   rK   r¥  r9  r:  r;  rµ   r   r8   r1  Ú_IMAGE_EXT_BY_MIMEÚ_AUDIO_EXT_BY_MIMErI   )r  r   r   r÷  Údata_b64rZ   r«   r9  r:  r;  r8   Úexts               r"   rþ  rþ  `
  s  € ð  �{Š{˜6Ñ"Ô"€HØð ØˆtðÝÔ˜xÑ(Ô(ˆˆøÝ�	Ð"ð ð ð Ý�ŠÐOÐQTÑUÔUÐUØˆtˆtˆtˆtˆtøøøøðøøøðð ð ð ð ð ð ð ð ð ð ˆJ�B×ÒÑÔ€Dà"&Ð.�T�$‰ZŒZÔÐ¨B€FðØ�?Š?˜8Ñ$Ô$ð 	<ØÐ@Õ.×2Ò2°4¸Ñ@Ô@ˆCð<Ø-Ð-¨c°3Ñ7Ô7Ð7øÝð <ð <ð <ð 1Ð0°°dÑ;Ô;Ð;Ð;Ð;ð<øøøð ð 	4˜$Ÿ/š/¨(Ñ3Ô3ð 	4Øð Õ.×2Ò2Ø Ð7�f�f°ñô ˆCð *Ð)¨#¨sÑ3Ô3Ð3à(Ð(¨¨dÑ3Ô3Ð3øÝð ð ð Ý�ŠÐKÈTÐSVÑWÔWÐWØˆtˆtˆtˆtˆtøøøøðøøøsR   ›0 °A'ÁA"Á"A'Â#2E ÃC" Ã"C;Ã8E Ã:C;Ã;AE ÅE Å
E?ÅE:Å:E?r¾  c                óÐ  — 	 |                       ¦   «         pi }n# t          $ r i }Y nw xY wt          |t          ¦  «        si }t	          |                     d¦  «        pd¦  «        }t          |                     d¦  «        ¦  «        }|dk    r
t          }d}nJ| j        dk    rd| j        › d| j	        d	d…         › �}n$t	          |                     d
¦  «        pd¦  «        }|||dœS )a9  Build the structured error dict for a failed standalone sidecar call.

    Mirrors ``_sidecar_error_from_response``: parse the sidecar body
    independently, carry the error class + retryability, and swap in the
    canonical user-facing message for ``target_not_allowed`` (never the raw
    upstream text).
    r|   rz   r}   r‘   Fr�   zsidecar returned z: Nr   zsidecar reported failure)r   r|   r}   )
rD   rI   rW   rX   r0   r’   r`   r“   r~   r�   )r¾  rŽ   r|   r}   r   s        r"   Ú_standalone_errorrC  œ
  s  € ðØ�yŠy‰{Œ{Ð ˜bˆˆøÝð ð ð Øˆˆˆðøøøå�d�DÑ!Ô!ð ØˆÝ�d—h’h˜}Ñ-Ô-Ð@°ÑAÔA€KÝ�T—X’X˜kÑ*Ô*Ñ+Ô+€IØÐ*Ò*Ð*Ý+ˆØˆ	ˆ	Ø	Ô	˜SÒ	 Ð	 ØI DÔ$4ÐIÐI¸¼	À$À3À$¼ÐIÐIˆˆå�D—H’H˜WÑ%Ô%ÐCÐ)CÑDÔDˆØ¨;ÀYÐOÐOÐOs   ‚ ™(§()Ú	thread_idÚmedia_filesÚforce_documentÚpconfigrò   rÐ  rD  rE  r—  rF  c             ƒ  ó>  K  — t           sddiS t          | j        pi                      d¦  «        pt	          j        d¦  «        t          ¦  «        }t          d¦  «        }|s­t          ¦   «         }d}	|r‰|                     d¦  «        rtt          |                     d¦  «        ¦  «        r9t          |d         ¦  «        }t          |                     d	¦  «        |¦  «        }nd
|                     d¦  «        › d�}	|sddt          › d�|	z   iS dt          › d|› �}
d|i}d }	 t          j        dd¬¦  «        4 ƒd {V —†}|�rjt          |¦  «        }|ru|                     |
› d�||dœ|¬¦  «        ƒ d {V —†}|j        dk    rD|                     ¦   «         pi }|                     d¦  «        r|                     d¦  «        }nd }nd }|sâ||d t&          …         dœ}t)          ¦   «         rt          |¦  «        sd|d<   |                     |
› d�||¬¦  «        ƒ d {V —†}|j        dk    r!t+          |¦  «        cd d d ¦  «        ƒd {V —† S |                     ¦   «         pi }|                     d¦  «        s!t+          |¦  «        cd d d ¦  «        ƒd {V —† S |                     d¦  «        }dd l}|pg D �]!\  }}t/          j        t          |¦  «        ¦  «        }|st2                               d¦  «         ŒD|                     |¦  «        \  }}|||rd nd!d"œ}|r||d#<   |                     |
› d$�||¬¦  «        ƒ d {V —†}|j        dk    r#t+          |¦  «        c cd d d ¦  «        ƒd {V —† S |                     ¦   «         pi }|                     d¦  «        s#t+          |¦  «        c cd d d ¦  «        ƒd {V —† S |                     d¦  «        p|}�Œ#	 d d d ¦  «        ƒd {V —† n# 1 ƒd {V —†swxY w Y   d%|d&œS # t8          $ r}dd'|› �icY d }~S d }~ww xY w)(Nr   r„  r4  r5  r6  r¼   r/   r1   r-   z/ A stale sidecar runtime record was found (pid u4    is not running) â€” the gateway appears to be down.zšPhoton standalone send requires a running sidecar. Start the Hermes gateway (which spawns the sidecar and records its address under <hermes-home>/runtime/z=), or set PHOTON_SIDECAR_TOKEN in this process's environment.r°  r±  r²  rv   Fr‡  r  r  r$  r�   rÆ  rÒ  r  r  r  r  r   z-[photon] standalone send skipping unsafe pathrò  r"  r  ró  r  TrÆ  zPhoton standalone send failed: )r±   r›   ræ   r’   r   r   rJ  r#   r[   rj   r0   r*   rL  r³   r‹  r  rU  r~   rD   r'  rþ   rC  r  r   r  rJ   rK   r   rI   )rG  rò   rÐ  rD  rE  rF  r-   r/   ÚrecordÚ
stale_hintÚbaserµ  Úlast_message_idr—  r  r¾  rŽ   Ú	send_bodyr  Ú
media_pathrÿ  r!  r"  r¦  Úatt_bodyrP   s                             r"   Ú_standalone_sendrP  ¶
  s   è è € õ ð 0ØÐ.Ð/Ð/ÝØ	ŒÐ	˜"×!Ò! .Ñ1Ô1ÐUµR´YÐ?TÑ5UÔ5UÝñô €Dõ Ð5Ñ6Ô6€EØð õ
 &Ñ'Ô'ˆØˆ
Øð 		�f—j’j Ñ)Ô)ð 		Ý! &§*¢*¨UÑ"3Ô"3Ñ4Ô4ð Ý˜F 7œOÑ,Ô,�Ý# F§J¢J¨vÑ$6Ô$6¸Ñ=Ô=��ð*Ø—z’z %Ñ(Ô(ð*ð *ð *ð ð
 ð 		àð5õ ,ð5ð 5ð 5ð 8Bñ	Bðð ð 4Õ*Ð3Ð3¨TÐ3Ð3€DØ'¨Ð/€GØ%)€OðB@ÝÔ$¨T¸UÐCÑCÔCð =	Kð =	Kð =	Kð =	Kð =	Kð =	Kð =	KÀvàñ <Ý.¨wÑ7Ô7�Øð (Ø!'§¢ØÐ/Ð/Ð/Ø)0¸ÐBÐBØ 'ð "-ñ "ô "ð ð ð ð ð ð �Dð
 Ô'¨3Ò.Ð.Ø#Ÿyšy™{œ{Ð0¨b˜ØŸ8š8 D™>œ>ð ,Ø.2¯hªh°{Ñ.CÔ.C˜O˜Oà'+˜H˜Hà#'˜Øð <à#*Ø 'Ð(<Õ)<Ð(<Ô =ð1ð 1�Iõ )Ñ*Ô*ð 9Õ3FÀwÑ3OÔ3Oð 9Ø.8˜	 (Ñ+Ø!'§¢Ø˜˜˜¨YÀð "-ñ "ô "ð ð ð ð ð ð �Dð Ô'¨3Ò.Ð.Ý0°Ñ6Ô6ð;=	Kð =	Kð =	Kð =	Kñ =	Kô =	Kð =	Kð =	Kð =	Kð =	Kð =	Kð =	Kð =	Kð =	Kð<  Ÿ9š9™;œ;Ð,¨"�DØŸ8š8 D™>œ>ð 7Ý0°Ñ6Ô6ðA=	Kð =	Kð =	Kð =	Kñ =	Kô =	Kð =	Kð =	Kð =	Kð =	Kð =	Kð =	Kð =	Kð =	KðB '+§h¢h¨{Ñ&;Ô&;�Oð
 ÐÐÐà(3Ð(9°rð Kñ KÑ$�
˜HÝ/ÔLÍSÐQ[É_Ì_Ñ]Ô]�	Ø ð Ý—N’NÐ#RÑSÔSÐSØØ&×1Ò1°)Ñ<Ô<‘
�˜à&Ø%Ø'/ÐA˜G˜G°\ð,ð ,�ð
 ð 3Ø+2�H˜ZÑ(Ø#Ÿ[š[ØÐ-Ð-Ð-°HÀgð )ñ ô ð ð ð ð ð ð �ð Ô# sÒ*Ð*Ý,¨TÑ2Ô2Ð2Ð2ðs=	Kð =	Kð =	Kð =	Kñ =	Kô =	Kð =	Kð =	Kð =	Kð =	Kð =	Kð =	Kð =	Kð =	Kðt —y’y‘{”{Ð( b�Ø—x’x ‘~”~ð 3Ý,¨TÑ2Ô2Ð2Ð2ðy=	Kð =	Kð =	Kð =	Kñ =	Kô =	Kð =	Kð =	Kð =	Kð =	Kð =	Kð =	Kð =	Kð =	Kðz #'§(¢(¨;Ñ"7Ô"7Ð"J¸?�‘ð+KðQ=	Kð =	Kð =	Kñ =	Kô =	Kð =	Kð =	Kð =	Kð =	Kð =	Kð =	Kð =	Kð =	Kð =	Kð =	Kð =	Kð =	Kð =	Kð =	Kð =	Kð =	Kð =	Kð =	Køøøð =	Kð =	Kð =	Kð =	Kð~  ¨Ð?Ð?Ð?øÝð @ð @ð @ØÐ>¸1Ð>Ð>Ð?Ð?Ð?Ð?Ð?Ð?Ð?øøøøð@øøøst   Ä#P Ä?C:O+È9P É9O+ÊP ÊCO+ÍP Í1;O+Î,P Î?O+ÏP Ï+
O5Ï5P Ï8O5Ï9P Ð
PÐPÐPÐPc                óô   — ddl m} |                      ddd„ t          t          t
          ddgd|j        t          d	t          d
dt          dddd¬¦  «         |  
                    dd|j        |j        ¬¦  «         dS )z.Called by the Hermes plugin loader at startup.r   )Úclir3  ziMessage via Photonc                ó    — t          | ¦  «        S r˜   )r0  rë   s    r"   ú<lambda>zregister.<locals>.<lambda>5  s   € ¥M°#Ñ$6Ô$6€ r,   rã   rå   z‹Run: hermes photon setup  (logs in via device flow, creates a Spectrum project, links your phone number, installs the spectrum-ts sidecar).rï   ÚPHOTON_ALLOWED_USERSÚPHOTON_ALLOW_ALL_USERSu   ðŸ“±Tua  You are communicating via Photon Spectrum (iMessage). Treat replies like regular text messages â€” short and friendly. Markdown is rendered (bold, italics, lists, code), but keep formatting light and conversational. Recipient identifiers are E.164 phone numbers; never expose them in responses unless the user asked. Attachments arrive as metadata only.)r   r  Úadapter_factoryÚcheck_fnré   rì   Úrequired_envÚinstall_hintÚsetup_fnÚenv_enablement_fnÚcron_deliver_env_varÚstandalone_sender_fnÚallowed_users_envÚallow_all_envÚmax_message_lengthr
  Úpii_safeÚallow_update_commandÚplatform_hintz1Set up and manage the Photon iMessage integration)r   Úhelpr[  Ú
handler_fnN)r¼   rR  Úregister_platformrÄ   ré   rì   Úgateway_setuprö   rP  r'  Úregister_cli_commandÚregister_cliÚdispatch)ÚctxÚ_clis     r"   Úregisterrn  ,  s»   € ð ÐÐÐÐÐà×ÒØØ#Ø6Ð6Ý#Ý'Ý!Ø)Ð+BÐCð$ð Ô#Ý)Ø2Ý-Ø0Ø.Ý.Øð Ø!ð?ð; ñ $ô $ð $ðN ×ÒØØ@ØÔ"Ø”=ð	 ñ ô ð ð ð r,   r˜   )r$   r   )r-   r.   r/   r0   r1   r.   r$   r2   )r$   rR   r%  )r1   r   r$   r`   )
rL   r0   r~   r.   r�   r0   rŽ   rR   r$   ry   )r–   r   r    r.   r$   r.   r&  )r–   r   r    r¢   r$   r¢   )r§   r   r$   r   )r«   rG   r$   r`   )rà   r   r$   r`   )r$   rí   )r�   r0   r$   rÿ   )r  r  r$   r0   )r  r  r$   rÿ   )r   r  r$   r`   )r  r  r$   r`   )r   r0   r$   r   )
r  r  r   r0   r   r0   r÷  r`   r$   rÿ   )r¾  r   r$   r  )rG  r   rò   r0   rÐ  r0   rD  rÿ   rE  r—  rF  r`   r$   r  )qrŠ   Ú
__future__r   r¯   r<  rD   Úloggingr   r  rN  r½   rX  rÖ   rS  r’  r   r   Úpathlibr   Útypingr   r   r	   r
   r   Úurllib.parser   r³   r±   ÚImportErrorÚgateway.configr   r   r¥  r   r   r   r   r   Úgateway.platforms.helpersr   r   Úauthr   Úagent.secret_scoper   r   r   r   r#   Ú	getLoggerr‡   rJ   rJ  rL  r'  r*   r+   rQ   r[   r_   rj   rÝ  rÛ  rå  Úsidecar_pathsrl   r¿   rm   rn   Ú__annotations__ro   rÁ   rq   rs   rØ   rù  rà  r%  rÌ  rr  r¸  ry   r•   r“   r›   r¡   r¤   r¦   rª   r·   rÄ   rÊ   rß   ré   rì   rö   rþ   r
  r  r  r  r(  r,  r.  r0  rý  r>  r?  rþ  rC  rP  rn  r^   r,   r"   ú<module>r|     s�  ððð ð ð0 #Ð "Ð "Ð "Ð "Ð "à €€€Ø €€€Ø €€€Ø €€€Ø 	€	€	€	Ø 	€	€	€	Ø €€€Ø €€€Ø €€€Ø Ð Ð Ð Ø 
€
€
€
Ø €€€Ø 'Ð 'Ð 'Ð 'Ð 'Ð 'Ð 'Ð 'Ø Ð Ð Ð Ð Ð Ø ;Ð ;Ð ;Ð ;Ð ;Ð ;Ð ;Ð ;Ð ;Ð ;Ð ;Ð ;Ð ;Ð ;Ø !Ð !Ð !Ð !Ð !Ð !àð ð €L€L€LØ€O€OðØˆˆˆØˆˆøØð ð ð ØˆØˆˆˆðøøøð 4Ð 3Ð 3Ð 3Ð 3Ð 3Ð 3Ð 3ðð ð ð ð ð ð ð ð ð ð ð ð ð ð OÐ NÐ NÐ NÐ NÐ NÐ NÐ Nà *Ð *Ð *Ð *Ð *Ð *à JÐ JÐ JÐ JÐ JÐ JØ ?Ð ?Ð ?Ð ?Ð ?Ð ?ð/ð /ð /ð /ð( 
ˆÔ	˜8Ñ	$Ô	$€ð
 Ð Ø#Ð ð
 Ð ð -Ð ð@ð @ð @ð @ðQð Qð Qð Qð>2ð 2ð 2ð 2ðð ð ð ðð ð ð ð@ €Ø!Ð àÐ ð NÐ MÐ MÐ MÐ MÐ MÐ MÐ Mð  $€Ð #Ð #Ð #Ñ #Ø!%€Ð %Ð %Ð %Ñ %ØÐ ðð ð ð ð4ð 4ð 4ð 4ð Ð ð
Ð ð &*Ð "Ø&@Ð #ð
 Ð ð +Ø"ðÐ ð
ð 
ð 
ð 
ð 
˜ñ 
ô 
ð 
ð6 &*ð	ð ð ð ð ðBNð ð
ð ð ð ð
Fð 
Fð 
Fð 
Fðð ð ð ðð ð ð ð
ð 
ð 
ð 
ð3ð 3ð 3ð 3ð3ð 3ð 3ð 3ðlð ð ð ð$8Oð 8Oð 8Oð 8Oðvð ð ð ð ð  ð  ð  ðð ð ð ð&
ð 
ð 
ð 
ð
ð 
ð 
ð 
ð	%ð 	%ð 	%ð 	%ðTð Tð Tð Tðð ð ð ð$ð ð ð ð"ð ð ð ðð ð ð ð,Bð Bð Bð Bð BÐ'ñ Bô Bð BðP<
 ð 
 ð 
 ð 
 ð  ØØØØØØðð Ð ð ØØØØØØðð Ð ð  ð4ð 4ð 4ð 4ð 4ð 4ðxPð Pð Pð Pð>  $Ø"&Ø ðp@ð p@ð p@ð p@ð p@ð p@ðl2ð 2ð 2ð 2ð 2ð 2s   Á&A- Á-	A9Á8A9