
    )jj                        U d Z ddlmZ ddlmZ  e            Zeed<    ede          Zeed<    ede          Z	eed	<    ed
e          Z
eed<    ede          Zeed<    ede          Zeed<    ede          Zeed<    ede          Zeed<    ede          Zeed<    ede          Zeed<    ede          Zeed<    ede          Zeed<    ede          Zeed<   ee	e
eeeeeeeeedZded d!fd"Z	 	 	 	 	 	 	 	 	 	 d3d$ed%ed&ed'ed(ed)ed*eded+ed,ed efd-Zd.ed d!fd/Zd4d0ed1ed efd2Zd!S )5a   
Session-scoped context variables for the Hermes gateway.

Replaces the previous ``os.environ``-based session state
(``HERMES_SESSION_PLATFORM``, ``HERMES_SESSION_CHAT_ID``, etc.) with
Python's ``contextvars.ContextVar``.

**Why this matters**

The gateway processes messages concurrently via ``asyncio``.  When two
messages arrive at the same time the old code did:

    os.environ["HERMES_SESSION_THREAD_ID"] = str(context.source.thread_id)

Because ``os.environ`` is *process-global*, Message A's value was
silently overwritten by Message B before Message A's agent finished
running.  Background-task notifications and tool calls therefore routed
to the wrong thread.

``contextvars.ContextVar`` values are *task-local*: each ``asyncio``
task (and any ``run_in_executor`` thread it spawns) gets its own copy,
so concurrent messages never interfere.

**Backward compatibility**

The public helper ``get_session_env(name, default="")`` mirrors the old
``os.getenv("HERMES_SESSION_*", ...)`` calls.  Existing tool code only
needs to replace the import + call site:

    # before
    import os
    platform = os.getenv("HERMES_SESSION_PLATFORM", "")

    # after
    from gateway.session_context import get_session_env
    platform = get_session_env("HERMES_SESSION_PLATFORM", "")
    )
ContextVar)Any_UNSETHERMES_SESSION_PLATFORM)default_SESSION_PLATFORMHERMES_SESSION_CHAT_ID_SESSION_CHAT_IDHERMES_SESSION_CHAT_NAME_SESSION_CHAT_NAMEHERMES_SESSION_THREAD_ID_SESSION_THREAD_IDHERMES_SESSION_USER_ID_SESSION_USER_IDHERMES_SESSION_USER_NAME_SESSION_USER_NAMEHERMES_SESSION_KEY_SESSION_KEYHERMES_SESSION_ID_SESSION_IDHERMES_SESSION_MESSAGE_ID_SESSION_MESSAGE_ID!HERMES_CRON_AUTO_DELIVER_PLATFORM_CRON_AUTO_DELIVER_PLATFORM HERMES_CRON_AUTO_DELIVER_CHAT_ID_CRON_AUTO_DELIVER_CHAT_ID"HERMES_CRON_AUTO_DELIVER_THREAD_ID_CRON_AUTO_DELIVER_THREAD_ID)r   r	   r   r   r   r   r   r   r   r   r   r   
session_idreturnNc                 V    ddl }| |j        d<   t                              |            dS )a  Synchronize ``HERMES_SESSION_ID`` across ContextVar and ``os.environ``.

    Long-lived single-process entrypoints like the CLI can rotate sessions via
    ``/new``, ``/resume``, ``/branch``, or compression splits without
    reconstructing the entire agent. Tools still consult
    ``get_session_env("HERMES_SESSION_ID")`` with an ``os.environ`` fallback,
    so both storage paths must move together when the active session changes.
    r   Nr   )osenvironr   set)r   r"   s     </home/ubuntu/.hermes/hermes-agent/gateway/session_context.pyset_current_session_idr&   V   s2     III&0BJ"#OOJ     platformchat_id	chat_name	thread_iduser_id	user_namesession_key
message_idcwdc
                    t                               |           t                              |          t                              |          t                              |          t
                              |          t                              |          t                              |          t                              |          t                              |          g	}
	 ddl
m}  ||	           n# t          $ r Y nw xY w|
S )u  Set all session context variables and return reset tokens.

    Call ``clear_session_vars(tokens)`` in a ``finally`` block when the handler
    exits. Note ``clear_session_vars`` resets every var to ``""`` (to suppress
    the ``os.environ`` fallback) rather than restoring prior values — these
    helpers are not nestable/stack-safe, and the returned tokens are accepted
    only for API compatibility.

    ``cwd`` pins the logical working directory for this context.
    r   )set_session_cwd)r   r$   r
   r   r   r   r   r   r   r   agent.runtime_cwdr3   	Exception)r)   r*   r+   r,   r-   r.   r/   r   r0   r1   tokensr3   s               r%   set_session_varsr7   e   s    . 	h''W%%y))y))W%%y))%%
##
++
F555555   Ms   %C7 7
DDr6   c           	          t           t          t          t          t          t
          t          t          t          f	D ]}|	                    d           	 ddl
m}  |             dS # t          $ r Y dS w xY w)a+  Mark session context variables as explicitly cleared.

    Sets all variables to ``""`` so that ``get_session_env`` returns an empty
    string instead of falling back to (potentially stale) ``os.environ``
    values.  The *tokens* argument is accepted for API compatibility with
    callers that saved the return value of ``set_session_vars``, but the
    actual clearing uses ``var.set("")`` rather than ``var.reset(token)``
    to ensure the "explicitly cleared" state is distinguishable from
    "never set" (which holds the ``_UNSET`` sentinel).
    r(   r   )clear_session_cwdN)r   r
   r   r   r   r   r   r   r   r$   r4   r9   r5   )r6   varr9   s      r%   clear_session_varsr;      s     	
   	777777   s   A$ $
A21A2namer   c                     ddl }t                              |           }||                                }|t          ur|S |                    | |          S )u  Read a session context variable by its legacy ``HERMES_SESSION_*`` name.

    Drop-in replacement for ``os.getenv("HERMES_SESSION_*", default)``.

    Resolution order:
    1. Context variable (set by the gateway for concurrency-safe access).
       If the variable was explicitly set (even to ``""``) via
       ``set_session_vars`` or ``clear_session_vars``, that value is
       returned — **no fallback to os.environ**.
    2. ``os.environ`` (only when the context variable was never set in
       this context — i.e. CLI, cron scheduler, and test processes that
       don't use ``set_session_vars`` at all).
    3. *default*
    r   N)r"   _VAR_MAPgetr   getenv)r<   r   r"   r:   values        r%   get_session_envrB      sT     III
,,t

C
		L99T7###r'   )
r(   r(   r(   r(   r(   r(   r(   r(   r(   r(   )r(   )__doc__contextvarsr   typingr   objectr   __annotations__r   r
   r   r   r   r   r   r   r   r   r   r   r>   strr&   listr7   r;   rB    r'   r%   <module>rK      s<  $ $ $L # " " " " "      
 fhh    !+
+Df U U U : U U U)z*BFSSS * S S S!+,FPV!W!W!W J W W W!+,FPV!W!W!W J W W W)z*BFSSS * S S S!+,FPV!W!W!W J W W W%:&:FKKKj K K K$*%8&IIIZ I I I #-*-HRX"Y"Y"Y Z Y Y Y +5*5Xbh*i*i*i Z i i i)34V`f)g)g)g J g g g+5:6Zdj+k+k+k j k k k  1. 2 2. 2&$!4)D(B*F   s  t          ' ''' ' 	'
 ' ' ' ' ' 
' 
' ' ' 'Tt     >$ $# $ $S $ $ $ $ $ $r'   