
    $j1                        U d Z ddlmZ ddlZddlZddlZddlmZ ddlm	Z	 ddl
mZ ddlmZmZ ddlmZmZ erdd	lmZ dd
lmZ ddlmZ  ej        e          Zd;dZ edd           G d d                      Zd<dZ edd           G d d                      Z e	dd          Zde d<   d=dZ!d>d!Z"d?d#Z#d@d$Z$d;d%Z%dAd&Z&d@d'Z'dBd*Z(i Z)d+e d,<   dCd0Z*dDd2Z+ e            Z,d3e d4<   d5Z-dEd8Z.dFd:Z/dS )Ga%  Task context and scoping for background task execution.

Determines authorization scope (``get_task_scope``), manages the context
snapshot that is captured at task submission and restored in workers
(``TaskContextSnapshot``), and maintains in-process registries for live
sessions and servers.
    )annotationsN)OrderedDict)
ContextVar)	dataclass)TYPE_CHECKINGAny)parse_task_keytask_redis_prefix)Docket)ServerSession)FastMCPreturn
str | Nonec                     ddl m}   |             }|dS |j        r|j                            d          nd}|r|j         d| S |j        S )uT  Get the authorization scope for task isolation.

    Returns the raw scope identifier for the current access token, or
    ``None`` when no auth context is present (anonymous tasks).

    The scope is composed as ``client_id|sub`` when the token carries a
    ``sub`` claim — necessary for fixed-OAuth servers where ``client_id`` is
    shared across all users — and falls back to ``client_id`` alone for
    DCR/CIMD flows where the client identity is already per-user.

    Encoding for Redis/Docket keys happens at the boundary in ``keys.py``;
    this function returns the raw value.
    r   )get_access_tokenNsub|)fastmcp.server.dependenciesr   claimsget	client_id)r   tokenr   s      d/home/rurouni/.hermes/hermes-agent/venv/lib/python3.11/site-packages/fastmcp/server/tasks/context.pyget_task_scoper      su     =<<<<<E}t%*\
;%,

5
!
!
!tC
 */))C)))?    T)frozenslotsc                  *    e Zd ZU dZded<   	 ded<   dS )TaskContextInfozInformation about the current background task context.

    Returned by ``get_task_context()`` when running inside a Docket worker.
    Contains identifiers needed to communicate with the MCP session.
    strtask_idr   
task_scopeN)__name__
__module____qualname____doc____annotations__ r   r   r   r   7   s6           LLL2PPr   r   TaskContextInfo | Nonec                    ddl m}   |             sdS ddlm} 	 |                                }t          |j                  }t          |d         |d                   S # t          $ r Y dS t          t          f$ r Y dS w xY w)aJ  Get the current task context if running inside a background task worker.

    This function extracts task information from the Docket execution context.
    Returns None if not running in a task context (e.g., foreground execution).

    Returns:
        TaskContextInfo with task_id and task_scope, or None if not in a task.
    r   )is_docket_availableN)current_executionclient_task_idr"   )r!   r"   )r   r+   docket.dependenciesr,   r   r	   keyr   LookupError
ValueErrorKeyError)r+   r,   	execution	key_partss       r   get_task_contextr5   F   s     @?????   t555555
%))++	"9=11	./ .
 
 
 	
    tt!   tts   AA 
B ,B ?B c                      e Zd ZU dZdZded<   dZded<   dZded<   dZded<   e	dd
            Z
e	dd            ZddZddZdS )TaskContextSnapshotzAll context data snapshotted at task-submission time.

    Stored as a single Redis key per task, restored once in the worker.
    Nr   access_token_jsonzdict[str, str] | Nonehttp_headersorigin_request_id
session_idr   c                   ddl m}m}m}  |            } |            }|j        }	 |j        }n# t          $ r d}Y nw xY w | |r|                                nd |d          pd|t          |j	                  nd|          S )z6Capture current context for background task execution.r   )r   get_contextget_http_headersNT)include_allr8   r9   r:   r;   )
r   r   r=   r>   request_contextr;   RuntimeErrormodel_dump_jsonr    
request_id)clsr   r=   r>   access_tokenctxrA   r;   s           r   capturezTaskContextSnapshot.captureo   s    	
 	
 	
 	
 	
 	
 	
 	
 	
 	
 ('))kmm-	JJ 	 	 	JJJ	s2>H,,...D))d;;;Ct3B3NO.///TX!	
 	
 	
 		
s   / >>rawstr | bytesc                   t          |t                    r|                                }t          j        |          }|                    d          }t          |t                    rd |                                D             } | |                    d          ||                    d          |                    d                    S )z&Deserialize from JSON stored in Redis.r9   c                r    i | ]4\  }}t          |                                          t          |          5S r(   )r    lower).0kvs      r   
<dictcomp>z1TaskContextSnapshot.from_json.<locals>.<dictcomp>   s2    JJJ$!Qs1vv||~~s1vvJJJr   r8   r:   r;   r@   )
isinstancebytesdecodejsonloadsr   dictitems)rE   rI   parsedheaderss       r   	from_jsonzTaskContextSnapshot.from_json   s     c5!! 	**,,CC**^,,gt$$ 	KJJ'--//JJJGs$jj)<== $jj)<==zz,//	
 
 
 	
r   r    c                \    t          j        | j        | j        | j        | j        d          S )z$Serialize to JSON for Redis storage.r@   )rU   dumpsr8   r9   r:   r;   )selfs    r   to_jsonzTaskContextSnapshot.to_json   s7    z%)%; $ 1%)%;"o	 
 
 	
r   docketr   r"   r!   ttl_secondsintNonec                .  K   |                     t          ||                    }|                                4 d{V }|                    ||                                 |           d{V  ddd          d{V  dS # 1 d{V swxY w Y   dS )z*Store this snapshot as a single Redis key.N)ex)r/   _snapshot_redis_keyredissetr_   )r^   r`   r"   r!   ra   r/   rg   s          r   savezTaskContextSnapshot.save   sD      jj,ZAABB<<>> 	A 	A 	A 	A 	A 	A 	AU))CK)@@@@@@@@@	A 	A 	A 	A 	A 	A 	A 	A 	A 	A 	A 	A 	A 	A 	A 	A 	A 	A 	A 	A 	A 	A 	A 	A 	A 	A 	A 	A 	A 	As    1B
BB)r   r7   )rI   rJ   r   r7   )r   r    )
r`   r   r"   r   r!   r    ra   rb   r   rc   )r#   r$   r%   r&   r8   r'   r9   r:   r;   classmethodrH   r[   r_   ri   r(   r   r   r7   r7   c   s          
 %)((((*.L....$(((((!J!!!!
 
 
 [
4 
 
 
 [
	
 	
 	
 	

A 
A 
A 
A 
A 
Ar   r7   task_snapshot)defaultz2ContextVar[tuple[str, TaskContextSnapshot] | None]_task_snapshotr!   r    snapshotrc   c                >    t                               | |f           dS )z"Cache a snapshot keyed by task_id.N)rm   rh   )r!   rn   s     r   _set_cached_snapshotrp      s!    *+++++r   TaskContextSnapshot | Nonec                V    t                                           }||\  }}|| k    r|S dS )z/Get cached snapshot if it belongs to this task.N)rm   r   )r!   cachedcached_task_idrn   s       r   _get_cached_snapshotru      s8    !!F#) W$$O4r   r"   c                ,    t          |            d| dS )z/Build the Redis key suffix for a task snapshot.:z	:snapshot)r
   )r"   r!   s     r   rf   rf      s!    
++@@g@@@@r   c           	       K   t          |          }||S ddlm}m} 	  |            j        }n# t
          $ r d}Y nw xY w||                                }|dS 	 |                                4 d{V }|                    |                    t          | |                               d{V }ddd          d{V  n# 1 d{V swxY w Y   |dS t                              |          }t          ||           |S # t          t          j        t           t"          f$ r" t$                              d| |d           Y dS w xY w)u   Load task context snapshot from Redis (async) and cache it.

    Idempotent — returns the cached value if already loaded for this task.
    Nr   )_current_docket
get_serverz&Failed to load task snapshot for %s:%sTexc_info)ru   r   ry   rz   _docketrB   r   rg   r/   rf   r7   r[   rp   OSErrorrU   JSONDecodeErrorr2   r1   _loggerwarning)	r"   r!   rs   ry   rz   r`   rg   rI   rn   s	            r   _load_task_snapshot_asyncr      s5      "'**FGGGGGGGG%   ~ $$&&~t<<>> 	X 	X 	X 	X 	X 	X 	XU		&**-@W-U-U"V"VWWWWWWWWC	X 	X 	X 	X 	X 	X 	X 	X 	X 	X 	X 	X 	X 	X 	X 	X 	X 	X 	X 	X 	X 	X 	X 	X 	X 	X 	X;4&0055Wh///T)8Z@   4	 	 	
 	
 	
 ttsI   / >>D 6=C3D 
CD CD +D A E
	E
c                 4    t                      } | r| j        ndS )a  Get the session_id for the current background task, if available.

    Loads the task snapshot (from cache or Redis) and returns the session_id
    that was captured at task submission time.  Returns None if not in a task
    context or if the snapshot isn't available.
    N)_get_task_snapshot_syncr;   )rn   s    r   get_task_session_idr      s"     '((H"*484r   c                     t                      } | dS t          | j                  }||S t          | j        | j                  S )zGet the task snapshot using only sync operations.

    Fallback chain:
    1. ContextVar cache (keyed by task_id, set by async or sync loaders)
    2. Sync Redis GET (works for both memory:// and real Redis)
    N)r5   ru   r!   _load_task_snapshot_syncr"   )	task_infors   s     r   r   r      sK     !""It!)"344F#I$8):KLLLr   c                   	 ddl m} |                                }n# t          t          f$ r Y dS w xY w|dS 	 t          |j                  }|                    |                    t          | |                              }|dS t          
                    |          }t          ||           |S # t          t          j        t          t           t          f$ r" t"                              d| |d           Y dS w xY w)zLoad snapshot via sync Redis.

    For memory:// backends (fakeredis), shares the same FakeServer instance
    that Docket uses so data is accessible. For real Redis, creates a standard
    sync connection.
    r   )current_docketNz5Failed to load task snapshot via sync Redis for %s:%sTr{   )r.   r   r   r0   ImportError_get_sync_redisurlr/   rf   r7   r[   rp   r~   rU   r   r2   r1   r   r   )r"   r!   
_docket_cvr`   
sync_redisrI   rn   s          r   r   r     s"   DDDDDD!!%   tt~t$VZ00
nnVZZ(;J(P(PQQRR;4&0055Wh///T)8ZM   C	 	 	
 	
 	
 tts%    22AB4 +B4 4AC>=C>r   r   c                    ddl m}  ||           }|ddlm}  ||          S ddlm} |                    |           S )a  Get a sync Redis client that shares the same backend as Docket.

    For memory:// URLs, connects to the same fakeredis FakeServer instance
    so data written by the async Docket client is visible. For real Redis
    URLs, creates a standard sync connection.
    r   )get_memory_serverN)	FakeRedis)server)Redis)docket._redisr   	fakeredisr   rg   r   from_url)r   r   r   r   r   s        r   r   r   6  su     0/////s##F''''''y''''>>#r   z%dict[str, weakref.ref[ServerSession]]_task_sessionsr;   sessionr   c                >    t          j        |          t          | <   dS )zRegister a session for in-process background task access.

    Called automatically when a task is submitted to Docket. The session is
    stored as a weakref so it doesn't prevent garbage collection when the
    client disconnects.
    N)weakrefrefr   )r;   r   s     r   register_task_sessionr   U  s     ")W!5!5N:r   ServerSession | Nonec                    t                               |           }|dS  |            }|t                               | d           |S )u   Get a registered session by ID if still alive.

    Returns None in distributed workers where the session lives in another
    process — callers must handle this gracefully.
    N)r   r   pop)r;   r   r   s      r   get_task_sessionr   _  sL     

Z
(
(C
{tceeG:t,,,Nr   z&OrderedDict[str, weakref.ref[FastMCP]]_task_server_mapi'  r   r   c                    t          j        |          t          | <   t          t                    t          k    r:t                              d           t          t                    t          k    8dS dS )zRegister the server for a background task.

    Called at task-submission time so that background workers can resolve
    the correct (child) server for mounted tasks.
    F)lastN)r   r   r   len_TASK_SERVER_MAP_MAX_SIZEpopitem)r!   r   s     r   register_task_serverr   r  sh     !(F 3 3W


";
;
;  e ,,, 

";
;
;
;
;
;
;r   FastMCP | Nonec                    t                               |           }|dS  |            }|t                               | d           |S )z@Get the registered server for a background task, if still alive.N)r   r   r   )r!   r   r   s      r   get_task_serverr   }  sJ    


w
'
'C
{tSUUF~Wd+++Mr   )r   r   )r   r)   )r!   r    rn   r7   r   rc   )r!   r    r   rq   )r"   r   r!   r    r   r    )r"   r   r!   r    r   rq   )r   rq   )r   r    r   r   )r;   r    r   r   r   rc   )r;   r    r   r   )r!   r    r   r   r   rc   )r!   r    r   r   )0r&   
__future__r   rU   loggingr   collectionsr   contextvarsr   dataclassesr   typingr   r   fastmcp.server.tasks.keysr	   r
   r`   r   mcp.server.sessionr   fastmcp.server.serverr   	getLoggerr#   r   r   r   r5   r7   rm   r'   rp   ru   rf   r   r   r   r   r   r   r   r   r   r   r   r   r(   r   r   <module>r      s0     # " " " " "    # # # # # # " " " " " " ! ! ! ! ! ! % % % % % % % % G G G G G G G G .000000------
'
H
%
%   2 $d###Q Q Q Q Q Q Q $#Q   : $d###KA KA KA KA KA KA KA $#KA` FPZTF F F    
, , , ,
   A A A A
% % % %P5 5 5 5M M M M$! ! ! !H   8 9; : : : :6 6 6 6    <G;==  H H H H" - - - -     r   