Ë
    âß²jâ,  ã            /       óÞ  — U d Z ddlZddlmZ ddlmZ ddlmZmZ  e	«       Z
eed<   daeed<   d	efd
„Z ed„ dD «       «      xZ\  ZZZZZZZZZZZZZZZZ Z!Z"Z# ede
¬«      Z$ ede
¬«      Z% ede
¬«      Z& ede
¬«      Z' ede
¬«      Z(g e¢e&‘e'‘e(‘­D � ci c]  } | jR                  | “Œ c} Z*de+ded	dfd„Z,de+d	dfd„Z-ed>de+dz  d	ed   fd„«       Z.dede/dz  d	e/dz  fd„Z0ddddddddddddddddddde
ddfde+de+de+d e+d!e+d"e+d#e+d$e+d%e+d&e+d'e+de+d(e+d)e+d*e+d+e+d,e+d-ed.e+d/ed0e+d1e+dz  d	e1f.d2„Z2d3e1d	dfd4„Z3d?d5„Z4d@d6e+d7e+d	e+fd8„Z5 e6h d9£«      Z7d	efd:„Z8d?d;„Z9d	efd<„Z:d	efd=„Z;yc c} w )AaI  Session-scoped context variables for the Hermes gateway.

Replaces the old ``os.environ``-based ``HERMES_SESSION_*`` state with task-local ``ContextVar``s
(inherited by ``run_in_executor`` threads), so concurrently handled messages no longer clobber each
other's routing ids.  ``get_session_env`` is a drop-in for ``os.getenv``.
é    N)Úcontextmanager)Ú
ContextVar)ÚAnyÚIteratorÚ_UNSETFÚ_session_context_engagedÚreturnc                  ó   — t         S )zHTrue if any session has been bound via set_session_vars in this process.)r   © ó    ú</home/ubuntu/.hermes/hermes-agent/gateway/session_context.pyÚsession_context_engagedr      s   € ä#Ð#r   c              #   ó>   K  — | ]  }t        |t        ¬ «      –— Œ y­w)©ÚdefaultN)r   r   )Ú.0Únames     r   ú	<genexpr>r   (   s   è ø€ ò 
¨tŒ*�T¤6×
*Ð
*ñ 
ùs   ‚)ÚHERMES_SESSION_PLATFORMÚHERMES_SESSION_SOURCEÚHERMES_SESSION_CHAT_IDÚHERMES_SESSION_CHAT_TYPEÚHERMES_SESSION_CHAT_NAMEÚHERMES_SESSION_THREAD_IDÚHERMES_SESSION_USER_IDÚHERMES_SESSION_USER_ID_ALTÚHERMES_SESSION_USER_NAMEÚHERMES_SESSION_SCOPE_IDÚHERMES_SESSION_KEYÚHERMES_SESSION_IDÚHERMES_UI_SESSION_IDÚHERMES_SESSION_MESSAGE_IDÚHERMES_SESSION_PROFILEÚ HERMES_BROWSER_CONTROL_PRINCIPALÚ'HERMES_BROWSER_CONTROL_TRANSPORT_FAMILYÚHERMES_CRON_SESSIONÚHERMES_SESSION_PARENT_CHAT_IDÚHERMES_SESSION_ASYNC_DELIVERYr   ÚHERMES_SESSION_HISTORY_DELIVERYÚ!HERMES_CRON_AUTO_DELIVER_PLATFORMÚ HERMES_CRON_AUTO_DELIVER_CHAT_IDÚ"HERMES_CRON_AUTO_DELIVER_THREAD_IDÚfuncÚargsc                 óN   — 	 ddl m}  t        || «      |Ž  y# t        $ r Y yw xY w)zVBest-effort call of ``agent.runtime_cwd.<func>``; import/runtime failures are ignored.r   )Úruntime_cwdN)Úagentr0   ÚgetattrÚ	Exception)r-   r.   r0   s      r   Ú_runtime_cwdr4   H   s-   € ðÝ%Ø"Œ�˜TÓ" DÒ)øÜò Ùðús   ‚ ˜	$£$Ú
session_idc                 ó’   — t         j                  | «       	 ddlm}  |«       ry	 | t
        j                  d<   y# t        $ r Y Œw xY w)züSynchronize ``HERMES_SESSION_ID`` across ContextVar and ``os.environ`` (tools read it
    with an os.environ fallback).  Delegated subagent children (built in the parent process)
    get ONLY the task-local write, or they would clobber the parent's id.r   )Úis_delegated_child_contextNr    )Ú_SESSION_IDÚsetÚagent.delegation_contextr7   r3   ÚosÚenviron)r5   r7   s     r   Úset_current_session_idr=   Q   sK   € ô ‡O�O�JÔðÝGÙ%Ô'Øð (ð '1„B‡J�JÐ"Ò#øô ò Ùðús   —: º	AÁAc              #   óÊ   K  — t         j                  «       }| �t         j                  | «       	 d–— t         j                  |«       y# t         j                  |«       w xY w­w)z™Bind a task-local session id and restore the prior value on exit; never touches
    ``os.environ``.  ``session_id=None`` is a pure save/restore boundary.N)r8   Úgetr9   )r5   Úpreviouss     r   Úscoped_current_session_idrA   _   sF   è ø€ ô �‰Ó €HØÐÜ�‰˜
Ô#ð"Ûä�‰˜Õ!øŒ�‰˜Õ!üs   ‚,A#¯A	 ³A#Á	A Á A#ÚsourceÚmetadatac                 óx   — dD �ci c]  }t        | |d«      x}r|t        |«      “Œ }}|r
i |xs i ¥|¥S |S c c}w )zKKeep inbound route anchors for durable deliveries after the source is gone.)Úscope_idÚparent_chat_idN)r2   Ústr)rB   rC   ÚkeyÚvalueÚanchorss        r   Úsource_route_metadatarK   l   s[   € à*Hö 9 3Ü# F¨C°Ó6Ð6�5Ð6ð ”C˜“J‰ð 9€Gð 9á.5Ð*ˆxŠ~˜2Ð* 'Ð*ÐC¸8ÐCùò9s   …"7Ú TÚplatformÚchat_idÚ	chat_typeÚ	chat_nameÚ	thread_idÚuser_idÚuser_id_altÚ	user_namerE   Úsession_keyÚ
message_idÚprofileÚbrowser_control_principalÚ browser_control_transport_familyÚcwdÚasync_deliveryÚui_session_idÚcron_sessionrF   Úsession_history_deliveryc                 ól  — da | |||||||||	|
||||||||f}t        t        |«      D ��cg c]  \  }}|j                  |«      ‘Œ }}}|j	                  t
        j                  t        |«      «      «       |j	                  t        j                  |€t        n|«      «       t        d|«       |S c c}}w )u±  Set all session context variables and return reset tokens.  Call
    ``clear_session_vars(tokens)`` in a ``finally``; not nestable, clearing resets every var
    to ``""`` rather than restoring prior values (tokens are accepted only for API compat).

    ``session_history_delivery`` declares whether the bound chat id is one the client can address again:
    ``"1"`` (audited producers â€” explicit session-id header, native API sessions, /v1/runs) or
    ``""`` / omitted (default-deny, #98619).  ``None`` leaves the var at ``_UNSET`` ("never
    declared"), which ``session_history_delivery_supported()`` treats as NOT capable â€” an omitted declaration
    cannot grant wake authority.TÚset_session_cwd)
r   ÚzipÚ_SESSION_VARSr9   ÚappendÚ_SESSION_ASYNC_DELIVERYÚboolÚ_SESSION_HISTORY_DELIVERYr   r4   )rM   rB   rN   rO   rP   rQ   rR   rS   rT   rE   rU   r5   rV   rW   rX   rY   rZ   r[   r\   r]   rF   r^   ÚvaluesÚvarrI   Útokenss                             r   Úset_session_varsrj   s   s¹   € ð&  $Ðà�&˜' 9¨i¸ÀGÈ[Ø�8˜[¨*°mÀZÐQXØ!Ð#CÀ\ÐSað€Fô
 03´=À&Ó/I×J¡  eˆc�g‰g�e�nÐJ€FÑJØ
‡M�MÔ)×-Ñ-¬d°>Ó.BÓCÔDØ
‡M�MÔ+×/Ñ/Ð:RÐ:ZµÐ`xÓyÔzÜÐ" CÔ(Ø€Mùó	 Ks   «B0ri   c                 ó¶   — t         D ]  }|j                  d«       Œ t        j                  t        «       t        j                  t        «       t        d«       y)uµ  Mark session context variables as explicitly cleared (``""``, not ``_UNSET``), so
    ``get_session_env`` returns empty instead of stale ``os.environ`` values.  Async-delivery
    goes back to ``_UNSET``: a cleared context is default-supported, not opted-out.  Wake
    capability goes back to ``_UNSET`` too â€” but for the opposite reason: a cleared context has
    declared nothing, and an undeclared capability FAILS CLOSED (#98619).rL   Úclear_session_cwdN)rb   r9   rd   r   rf   r4   )ri   rh   s     r   Úclear_session_varsrm   “   sC   € ô ò ˆØ�‰��ðä×Ñ¤Ô'Ü×!Ñ!¤&Ô)ÜÐ$Õ%r   c                  óÚ   — t         j                  «       D ]  } | j                  t        «       Œ t        j                  t        «       t
        j                  t        «       t        d«       y)a’  Reset every session var to ``_UNSET`` ("never bound here") for THIS context.  Call at
    the top of a fresh task *before* it binds: ``create_task`` snapshots the context, so B's
    task inherits A's already-set vars and a subprocess spawned before B binds would read A's
    identity.  ``_SESSION_ASYNC_DELIVERY`` and ``_SESSION_HISTORY_DELIVERY`` (outside ``_VAR_MAP``)
    are reset explicitly too.rl   N)Ú_VAR_MAPrg   r9   r   rd   rf   r4   ©rh   s    r   Úreset_session_varsrq       sJ   € ô �‰Ó ò ˆØ�‰”�ðä×Ñ¤Ô'Ü×!Ñ!¤&Ô)ÜÐ$Õ%r   r   r   c                 ó�   — t         j                  | «      }|�|j                  «       x}t        ur|S t        j                  | |«      S )z¯Read a session var by legacy ``HERMES_SESSION_*`` name; drop-in for os.getenv.  The
    ContextVar wins if ever set here (even to ``""``); else ``os.environ``; else *default*.)ro   r?   r   r;   Úgetenv)r   r   rh   rI   s       r   Úget_session_envrt   ­   s?   € ô �,‰,�tÓ
€CØ
€ S§W¡W£YÐ.˜E´vÑ=ØˆÜ�9‰9�T˜7Ó#Ð#r   >   rL   ÚcliÚtuiÚtoolÚcodexÚlocalÚkanbanÚdesktopÚgatewayÚwebhookÚ
api_serverÚmsgraph_webhookc                  óš   — t        j                  d«      xs t        dd«      } | t        dd«      f}d„ |D «       }t        d„ |D «       «      S )z“Whether this turn is delivered over a human messaging channel (checks
    ``HERMES_PLATFORM``, then the session platform, then the session source).ÚHERMES_PLATFORMr   rL   r   c              3   ór   K  — | ]/  }t        |xs d «      j                  «       j                  «       –— Œ1 y­w)rL   N)rG   ÚstripÚlower)r   Úvs     r   r   z/session_is_messaging_surface.<locals>.<genexpr>Ä   s*   è ø€ Ò;¨qŒc�!’'�r‹l× Ñ Ó"×(Ñ(×*Ñ;ùs   ‚57c              3   ó4   K  — | ]  }|xr |t         v–— Œ y ­w©N)ÚNON_MESSAGING_SESSION_SURFACES)r   Úidents     r   r   z/session_is_messaging_surface.<locals>.<genexpr>Å   s   è ø€ ÒYÈˆuÒD˜Ô&DÐDÓDÑYùs   ‚)r;   rs   rt   Úany)rM   Úidentss     r   Úsession_is_messaging_surfacerŒ   ¿   sO   € ô �y‰yÐ*Ó+Ò]¬Ð?XÐZ\Ó/]€HØœÐ(?ÀÓDÐE€FÙ;°FÔ;€FÜÑYÐRXÔYÓYÐYr   c                  ó.   — t         j                  d«       y)aG  Declare that this session cannot receive an async background completion.  Unlike
    ``set_session_vars(async_delivery=False)`` this does NOT latch ``_session_context_engaged``
    (flipping the subprocess env bridge), which a one-shot CLI must not do as a side effect.

    See NousResearch/hermes-agent#53027 and #63142.
    FN)rd   r9   r   r   r   Údeclare_stateless_channelrŽ   È   s   € ô ×Ñ Õ&r   c                  ó”   — t         j                  j                  d«      ryt        j                  «       } | t        u rdS t        | «      S )zûWhether the current session can deliver a background completion later.  False for
    stateless channels (:func:`declare_stateless_channel`) and Kanban workers
    (``HERMES_KANBAN_TASK``: one-shot subprocesses whose parent disappears after the turn).ÚHERMES_KANBAN_TASKFT)r;   r<   r?   rd   r   re   )rI   s    r   Úasync_delivery_supportedr‘   Ò   s;   € ô 
‡z�z‡~�~Ð*Ô+ØÜ#×'Ñ'Ó)€EØœF‘?ˆ4Ð3¬¨U«Ð3r   c                  ó0   — t         j                  «       dk(  S )z Whether this request declares a server-history consumer for detached results.

    Fail closed on omitted bindings; never borrow authority from the environment.Ú1)rf   r?   r   r   r   Ú"session_history_delivery_supportedr”   Ü   s   € ô %×(Ñ(Ó*¨cÑ1Ð1r   r‡   )r	   N)rL   )<Ú__doc__r;   Ú
contextlibr   Úcontextvarsr   Útypingr   r   Úobjectr   Ú__annotations__r   re   r   Útuplerb   Ú_SESSION_PLATFORMÚ_SESSION_SOURCEÚ_SESSION_CHAT_IDÚ_SESSION_CHAT_TYPEÚ_SESSION_CHAT_NAMEÚ_SESSION_THREAD_IDÚ_SESSION_USER_IDÚ_SESSION_USER_ID_ALTÚ_SESSION_USER_NAMEÚ_SESSION_SCOPE_IDÚ_SESSION_KEYr8   Ú_SESSION_UI_SESSION_IDÚ_SESSION_MESSAGE_IDÚ_SESSION_PROFILEÚ_BROWSER_CONTROL_PRINCIPALÚ!_BROWSER_CONTROL_TRANSPORT_FAMILYÚ_CRON_SESSIONÚ_SESSION_PARENT_CHAT_IDrd   rf   Ú_CRON_AUTO_DELIVER_PLATFORMÚ_CRON_AUTO_DELIVER_CHAT_IDÚ_CRON_AUTO_DELIVER_THREAD_IDr   ro   rG   r4   r=   rA   ÚdictrK   Úlistrj   rm   rq   rt   Ú	frozensetrˆ   rŒ   rŽ   r‘   r”   rp   s   0r   ú<module>r´      s9  ðòó 
Ý %Ý "ß  ñ ‹h€ˆÓ ð "'Ð ˜$Ó &ð$ ó $ñ& 
ñ 
ð 8ô 
ó ð€ñ Ø�Ð(8Ð:LØÐ*Ð,<Ð>RØÐ)¨<¸ØÐ/Ð1AØÐ AÀ=ÐRiñ %Ð%DÈfÔUÐ ñ 'Ð'HÐRXÔYÐ ñ )Ð)LÐV\Ô]Ð Ù'Ð(JÐTZÔ[Ð Ù)Ð*NÐX^Ô_Ð ð&Øð&Ø/ð&Ø1Kð&à ñ&ö ˜cˆC�H‰H�c‰Mò €ð�sð  3ð ¨4ó ð1 sð 1¨tó 1ð ñ	"¨#°©*ð 	"ÀÈÁò 	"ó ð	"ðD #ð D°¸±ð DÀÈÁó Dð  b¸ÈbØ¨"¸RÐTVØ¨ÀÐVXØ¨ÈbØ,.¸2ÐVZØ°ÈrØ+/ñØðØ #ðØ36ðØHKðàðà$'ðà7:ðàNQðð ðð $'ðð ;>ðð QTðð ð	ð $'ð	ð ILð	ð
 '*ðð
 69ðð
 PTðð ðð ,/ðð JMðð " D™jðð 
óð@
&˜tð 
&¨ó 
&ó
&ñ$˜#ð $¨ð $°Só $ñ "+ò ,ó "Ð ðZ dó Zó'ð4 $ó 4ð2¨Dô 2ùòus   ÂE*