
    $jv              
           d Z ddlmZ ddlmZmZ  G d de          ZdZdZeefZ	de
d	z  d
e
de
de
de
f
dZde
defdZde
de
fdZde
d	z  de
fdZd	S )u  Docket and Redis key encoding for background tasks.

The compound Docket task key embeds the auth boundary so that the parser can
reject cross-scope access without consulting Redis.  Authenticated and
anonymous tasks live in disjoint keyspaces:

    auth:{enc_scope}:{client_task_id}:{task_type}:{enc_identifier}
    anon:{client_task_id}:{task_type}:{enc_identifier}

The same `auth/anon` partition is used for the per-task Redis prefix
(``fastmcp:task:auth:{enc_scope}`` vs ``fastmcp:task:anon``) — see
``task_redis_prefix``.

``task_scope`` is the raw scope identifier (typically derived from
``client_id`` or ``client_id|sub``); encoding happens once, at the boundary,
in this module.
    )	TypedDict)quoteunquotec                   B    e Zd ZU dZedz  ed<   eed<   eed<   eed<   dS )TaskKeyPartszDecoded segments of a Docket task key.

    ``task_scope`` is ``None`` for anonymous tasks, the raw scope string
    otherwise.
    N
task_scopeclient_task_id	task_typecomponent_identifier)__name__
__module____qualname____doc__str__annotations__     a/home/rurouni/.hermes/hermes-agent/venv/lib/python3.11/site-packages/fastmcp/server/tasks/keys.pyr   r      sO           d
NNNr   r   authanonr   Nr	   r
   r   returnc           	          t          |d          }| t           d| d| d| S t          | d          }t           d| d| d| d| 	S )az  Build Docket task key with embedded metadata.

    When ``task_scope`` is ``None`` the task is anonymous and lives in the
    ``anon`` keyspace.  Otherwise it lives under ``auth:{enc_scope}``.

    Args:
        task_scope: Raw authorization scope, or ``None`` for anonymous tasks
        client_task_id: Client-provided task ID
        task_type: Type of task ("tool", "prompt", "resource")
        component_identifier: Tool name, prompt name, or resource URI

    Returns:
        Encoded task key for Docket

    Examples:
        >>> build_task_key("client-a", "task456", "tool", "my_tool")
        'auth:client-a:task456:tool:my_tool'

        >>> build_task_key(None, "task456", "tool", "my_tool")
        'anon:task456:tool:my_tool'

        >>> build_task_key("client-a", "task456", "resource", "file://data.txt")
        'auth:client-a:task456:resource:file%3A%2F%2Fdata.txt'
     safeN:)r   	_ANON_TAG	_AUTH_TAG)r   r	   r
   r   encoded_identifierencoded_scopes         r   build_task_keyr!   )   s    < 3"===OOnOOyOO;MOOO*2...MXX}XX~XX	XXDVXXr   task_keyc                 
   |                      d          \  }}}|t          vs|st          d|  dt           d          |t          k    rV|                    dd          }t          |          dk    rt          d|  d          |\  }}}d	||t          |          d
S |                    dd          }t          |          dk    rt          d|  d          |\  }}}}t          |          ||t          |          d
S )a  Parse Docket task key to extract metadata.

    Args:
        task_key: Encoded task key from Docket

    Returns:
        Dict with keys: ``task_scope`` (``str | None``), ``client_task_id``,
        ``task_type``, ``component_identifier``.

    Raises:
        ValueError: If the key has an unrecognized tag or wrong segment count.

    Examples:
        >>> parse_task_key("auth:client-a:task456:tool:my_tool")
        `{'task_scope': 'client-a', 'client_task_id': 'task456', 'task_type': 'tool', 'component_identifier': 'my_tool'}`

        >>> parse_task_key("anon:task456:tool:my_tool")
        `{'task_scope': None, 'client_task_id': 'task456', 'task_type': 'tool', 'component_identifier': 'my_tool'}`
    r   zInvalid task key format: z. Expected leading tag in .      zInvalid anonymous task key: zD. Expected: anon:{client_task_id}:{task_type}:{component_identifier}N)r   r	   r
   r      z Invalid authenticated task key: zP. Expected: auth:{enc_scope}:{client_task_id}:{task_type}:{component_identifier})	partition_VALID_TAGS
ValueErrorr   splitlenr   )	r"   tag_restpartsr	   r
   r   r    s	            r   parse_task_keyr1   P   s   ( %%c**LCD
+T6 6 6'26 6 6
 
 	

 i

3""u::??\x \ \ \   9>5	#5,"$+,>$?$?	
 
 	
 JJsAE
5zzQfx f f f
 
 	
 DI@M>9.@m,,( '(: ; ;	  r   c                 ,    t          |           d         S )a]  Extract just the client task ID from a task key.

    Args:
        task_key: Full encoded task key

    Returns:
        Client-provided task ID

    Examples:
        >>> get_client_task_id_from_key("auth:client-a:task456:tool:my_tool")
        'task456'

        >>> get_client_task_id_from_key("anon:task456:tool:my_tool")
        'task456'
    r	   )r1   )r"   s    r   get_client_task_id_from_keyr3      s      (##$455r   c                 R    | 
dt            S dt           dt          | d           S )zReturn the Redis key prefix that owns a given scope.

    Authenticated tasks live under ``fastmcp:task:auth:{enc_scope}``;
    anonymous tasks live under ``fastmcp:task:anon``.  Callers append
    ``f":{task_id}:..."`` to compose the final key.
    Nzfastmcp:task:r   r   r   )r   r   r   )r   s    r   task_redis_prefixr5      s;     *y***C9CCuZb'A'A'ACCCr   )r   typingr   urllib.parser   r   r   r   r   r)   r   r!   r1   r3   r5   r   r   r   <module>r8      s?   $       ' ' ' ' ' ' ' '
 
 
 
 
9 
 
 
 		)$$d
$$ $ 	$
 	$ $ $ $N6S 6\ 6 6 6 6r6# 6# 6 6 6 6&	D#* 	D 	D 	D 	D 	D 	D 	Dr   