§
    ÚŽ’jÔ‡  ã                   óŽ   — d Z ddlmZmZmZ ddlmZ ddlmZ ddl	m
Z
mZ ddlmZ dd	lmZ erdd
lmZ ndZ G d„ d¦  «        ZdS )zBThe permission engine for checking and enforcing permission rules.é    )ÚAnyÚListÚTYPE_CHECKINGé   )ÚPermissionContext)ÚPermissionRule)ÚPermissionDecisionÚPermissionBehavior)ÚPermissionModeé   )Ú_execute_async_or_sync_func)ÚToolBaser   c                   óN  — e Zd ZdZdeddfd„Zdeddfd„Zded	e	e
ef         defd
„Zded	e	e
ef         defd„Zded	e	e
ef         defd„Zded	e	e
ef         defd„Zded	e	e
ef         defd„Zded	e	e
ef         defd„Zedededefd„¦   «         Zededefd„¦   «         Zded	e	e
ef         dedz  fd„Zdede	e
ef         dedz  fd„Zdede	e
ef         dedz  fd„Zdede	e
ef         dedz  fd„Zdedede	e
ef         defd„Zded	e	e
ef         dee         fd„ZdS )ÚPermissionEnginea¤  Engine for checking and enforcing permission rules.

    Evaluates tool execution requests against configured permission rules.
    Matching strategy is delegated to each tool's :meth:`ToolBase.match_rule`:

    - Bash tools: substring / prefix wildcard matching against the command
    - Write/Read/Edit tools: glob matching against file paths
    - Other tools: generic pattern matching (or tool-name-level only)

    Each :class:`PermissionMode` has its own ``_check_<mode>`` method so
    that mode policies are self-contained and readable in isolation. See
    :meth:`check_permission` for the dispatcher and the individual methods
    for per-mode evaluation order.
    ÚcontextÚreturnNc                 ó   — || _         dS )a8  Initialize the permission engine.

        Args:
            context (`PermissionContext`):
                The permission context containing rules and mode

        Example:
            >>> context = PermissionContext(mode=PermissionMode.ACCEPT_EDITS)
            >>> engine = PermissionEngine(context)
        N)r   )Úselfr   s     úKC:\Projects\DSCons\.venv\Lib\site-packages\agentscope\permission\_engine.pyÚ__init__zPermissionEngine.__init__!   s   € ð ˆŒˆˆó    Úrulec                 óv  — |j         t          j        k    rS|j        | j        j        vrg | j        j        |j        <   | j        j        |j                                      |¦  «         dS |j         t          j        k    rS|j        | j        j        vrg | j        j        |j        <   | j        j        |j                                      |¦  «         dS |j         t          j	        k    rS|j        | j        j
        vrg | j        j
        |j        <   | j        j
        |j                                      |¦  «         dS dS )ai  Add a permission rule to the context.

        Args:
            rule (`PermissionRule`):
                The permission rule to add

        Example:
            >>> engine.add_rule(PermissionRule(
            ...     tool_name="Bash",
            ...     rule_content="git:*",
            ...     behavior=PermissionBehavior.ALLOW,
            ... ))
        N)Úbehaviorr
   ÚALLOWÚ	tool_namer   Úallow_rulesÚappendÚDENYÚ
deny_rulesÚASKÚ	ask_rules)r   r   s     r   Úadd_rulezPermissionEngine.add_rule1   s  € ð Œ=Õ.Ô4Ò4Ð4ØŒ~ T¤\Ô%=Ð=Ð=Ø;=�”Ô(¨¬Ñ8ØŒLÔ$ T¤^Ô4×;Ò;¸DÑAÔAÐAÐAÐAØŒ]Õ0Ô5Ò5Ð5ØŒ~ T¤\Ô%<Ð<Ð<Ø:<�”Ô'¨¬Ñ7ØŒLÔ# D¤NÔ3×:Ò:¸4Ñ@Ô@Ð@Ð@Ð@ØŒ]Õ0Ô4Ò4Ð4ØŒ~ T¤\Ô%;Ð;Ð;Ø9;�”Ô& t¤~Ñ6ØŒLÔ" 4¤>Ô2×9Ò9¸$Ñ?Ô?Ð?Ð?Ð?ð 5Ð4r   ÚtoolÚ
tool_inputc              ƒ   óú  K  — | j         j        }|t          j        k    r|                      ||¦  «        ƒ d{V —†S |t          j        k    r|                      ||¦  «        ƒ d{V —†S |t          j        k    r|                      ||¦  «        ƒ d{V —†S |t          j	        k    r|  
                    ||¦  «        ƒ d{V —†S |t          j        k    r|                      ||¦  «        ƒ d{V —†S t          d|› �¦  «        ‚)u  Check permission for a tool execution request.

        Dispatches to a per-mode private method so each mode's policy
        is self-contained and readable in isolation:

        - DEFAULT      â†’ :meth:`_check_default`
        - EXPLORE      â†’ :meth:`_check_explore`
        - ACCEPT_EDITS â†’ :meth:`_check_accept_edits`
        - BYPASS       â†’ :meth:`_check_bypass`
        - DONT_ASK     â†’ :meth:`_check_dont_ask`

        Args:
            tool (`ToolBase`):
                The tool instance being called.
            tool_input (`dict[str, Any]`):
                The tool input data, used for rule matching and
                tool-specific checks.

        Returns:
            `PermissionDecision`:
                Decision indicating whether to allow, deny, or ask.
        NzUnknown permission mode: )r   Úmoder   ÚDEFAULTÚ_check_defaultÚEXPLOREÚ_check_exploreÚACCEPT_EDITSÚ_check_accept_editsÚBYPASSÚ_check_bypassÚDONT_ASKÚ_check_dont_askÚ
ValueError)r   r$   r%   r'   s       r   Úcheck_permissionz!PermissionEngine.check_permissionM   s7  è è € ð6 Œ|Ô ˆØ•>Ô)Ò)Ð)Ø×,Ò,¨T°:Ñ>Ô>Ð>Ð>Ð>Ð>Ð>Ð>Ð>Ø•>Ô)Ò)Ð)Ø×,Ò,¨T°:Ñ>Ô>Ð>Ð>Ð>Ð>Ð>Ð>Ð>Ø•>Ô.Ò.Ð.Ø×1Ò1°$¸
ÑCÔCÐCÐCÐCÐCÐCÐCÐCØ•>Ô(Ò(Ð(Ø×+Ò+¨D°*Ñ=Ô=Ð=Ð=Ð=Ð=Ð=Ð=Ð=Ø•>Ô*Ò*Ð*Ø×-Ò-¨d°JÑ?Ô?Ð?Ð?Ð?Ð?Ð?Ð?Ð?ÝÐ;°TÐ;Ð;Ñ<Ô<Ð<r   c              ƒ   óî  K  — |                       ||¦  «        ƒ d{V —†}|r|S |                      ||¦  «        ƒ d{V —†}|r#|                      ||¦  «        ƒ d{V —†|_        |S |                      ||¦  «        ƒ d{V —†}|r|S |                     || j        ¦  «        ƒ d{V —†}|j        t          j	        t          j
        fv r|S |                      |¦  «        r#|                      ||¦  «        ƒ d{V —†|_        |S |                      ||¦  «        ƒ d{V —†}|r|S t          t          j        d|j        › �d| j        j        j        › �¬¦  «        }|                      ||¦  «        ƒ d{V —†|_        |S )uÝ  Permission check for :attr:`PermissionMode.DEFAULT`.

        Every operation requires explicit permission unless it is a
        read-only invocation, an allow rule matches, or the tool's own
        ``check_permissions`` returns ALLOW. Evaluation order:

        1. Deny rules â†’ DENY
        2. Ask rules â†’ ASK (with suggestions)
        3. Read-only fast path â†’ ALLOW
        4. ``tool.check_permissions``:
            - ALLOW / DENY â†’ returned as-is
            - Safety ASK (bypass-immune) â†’ returned with suggestions; cannot
              be overridden by allow rules
            - Non-safety ASK / PASSTHROUGH â†’ continue
        5. Allow rules â†’ ALLOW
        6. Default â†’ ASK (with suggestions)

        Args:
            tool (`ToolBase`):
                The tool instance being called.
            tool_input (`dict[str, Any]`):
                The tool input data.

        Returns:
            `PermissionDecision`:
                The final decision.
        NúPermission required for úMode: ©r   ÚmessageÚdecision_reason©Ú_check_deny_rulesÚ_check_ask_rulesÚ_generate_suggestionsÚsuggested_rulesÚ_check_read_only_fast_pathÚcheck_permissionsr   r   r
   r   r   Ú_is_safety_askÚ_check_allow_rulesr	   r!   Únamer'   Úvalue©	r   r$   r%   ÚdenyÚaskÚ	read_onlyÚtool_decisionÚallowÚdefaults	            r   r)   zPermissionEngine._check_defaultu   sM  è è € ðF ×+Ò+¨D°*Ñ=Ô=Ð=Ð=Ð=Ð=Ð=Ð=ˆØð 	ØˆKð
 ×)Ò)¨$°
Ñ;Ô;Ð;Ð;Ð;Ð;Ð;Ð;ˆØð 	Ø(,×(BÒ(BØØñ)ô )ð #ð #ð #ð #ð #ð #ˆCÔð ˆJð
 ×9Ò9¸$À
ÑKÔKÐKÐKÐKÐKÐKÐKˆ	Øð 	ØÐð #×4Ò4°ZÀÄÑNÔNÐNÐNÐNÐNÐNÐNˆØÔ!ÝÔ$ÝÔ#ð&
ð 
ð 
ð !Ð Ø×Ò˜}Ñ-Ô-ð 	!Ø26×2LÒ2LØØñ3ô 3ð -ð -ð -ð -ð -ð -ˆMÔ)ð !Ð ð
 ×-Ò-¨d°JÑ?Ô?Ð?Ð?Ð?Ð?Ð?Ð?ˆØð 	ØˆLõ
 %Ý'Ô+Ø:¨t¬yÐ:Ð:Ø> T¤\Ô%6Ô%<Ð>Ð>ð
ñ 
ô 
ˆð
 )-×(BÒ(BØØñ)
ô )
ð #
ð #
ð #
ð #
ð #
ð #
ˆÔð ˆr   c              ƒ   óR  K  — |                       ||¦  «        ƒ d{V —†}|r|S |                      ||¦  «        ƒ d{V —†}|r#|                      ||¦  «        ƒ d{V —†|_        |S |                      ||¦  «        ƒ d{V —†}|r|S t          t          j        d|j        › d�d¬¦  «        S )u(  Permission check for :attr:`PermissionMode.EXPLORE`.

        Read-only mode â€” modifications are categorically denied. Evaluation
        order:

        1. Deny rules â†’ DENY
        2. Ask rules â†’ ASK (with suggestions)
        3. Read-only fast path â†’ ALLOW
        4. ``tool.check_permissions`` â†’ not applicable (see below)
        5. Allow rules â†’ not applicable (see below)
        6. Mode fallback â†’ DENY

        ``tool.check_permissions`` is not invoked: EXPLORE is fully
        resolved by the read-only verdict, so safety ASK paths (e.g.
        ``rm -rf /``) are subsumed into the broader DENY. Allow rules are
        intentionally not consulted â€” EXPLORE's read-only guarantee
        cannot be granted away by a user-configured rule.

        Args:
            tool (`ToolBase`):
                The tool instance being called.
            tool_input (`dict[str, Any]`):
                The tool input data.

        Returns:
            `PermissionDecision`:
                ALLOW for read-only invocations, DENY otherwise.
        NúPermission denied for z (explore mode is read-only)z)Explore mode does not allow modificationsr7   )	r;   r<   r=   r>   r?   r	   r
   r   rC   )r   r$   r%   rF   rG   rH   s         r   r+   zPermissionEngine._check_exploreÖ   s%  è è € ðH ×+Ò+¨D°*Ñ=Ô=Ð=Ð=Ð=Ð=Ð=Ð=ˆØð 	ØˆKð
 ×)Ò)¨$°
Ñ;Ô;Ð;Ð;Ð;Ð;Ð;Ð;ˆØð 	Ø(,×(BÒ(BØØñ)ô )ð #ð #ð #ð #ð #ð #ˆCÔð ˆJð
 ×9Ò9¸$À
ÑKÔKÐKÐKÐKÐKÐKÐKˆ	Øð 	ØÐõ$ "Ý'Ô,ð/¨¬ð /ð /ð /ð Hð
ñ 
ô 
ð 	
r   c              ƒ   óî  K  — |                       ||¦  «        ƒ d{V —†}|r|S |                      ||¦  «        ƒ d{V —†}|r#|                      ||¦  «        ƒ d{V —†|_        |S |                      ||¦  «        ƒ d{V —†}|r|S |                     || j        ¦  «        ƒ d{V —†}|j        t          j	        t          j
        fv r|S |                      |¦  «        r#|                      ||¦  «        ƒ d{V —†|_        |S |                      ||¦  «        ƒ d{V —†}|r|S t          t          j        d|j        › �d| j        j        j        › �¬¦  «        }|                      ||¦  «        ƒ d{V —†|_        |S )uø  Permission check for :attr:`PermissionMode.ACCEPT_EDITS`.

        Edits within working directories are auto-allowed by each tool's
        own ``check_permissions``; other operations follow the normal
        flow. Evaluation order:

        1. Deny rules â†’ DENY
        2. Ask rules â†’ ASK (with suggestions)
        3. :meth:`ToolBase.check_read_only` â†’ True â†’ ALLOW (fast path)
        4. ``tool.check_permissions``:
            - ALLOW (e.g. ``Write`` to a file in the working directory) /
              DENY â†’ returned as-is
            - Safety ASK (bypass-immune) â†’ returned with suggestions
            - Non-safety ASK / PASSTHROUGH â†’ continue
        5. Allow rules â†’ ALLOW
        6. Default â†’ ASK (with suggestions)

        Args:
            tool (`ToolBase`):
                The tool instance being called.
            tool_input (`dict[str, Any]`):
                The tool input data.

        Returns:
            `PermissionDecision`:
                The final decision.
        Nr5   r6   r7   r:   rE   s	            r   r-   z$PermissionEngine._check_accept_edits)  sM  è è € ðF ×+Ò+¨D°*Ñ=Ô=Ð=Ð=Ð=Ð=Ð=Ð=ˆØð 	ØˆKð
 ×)Ò)¨$°
Ñ;Ô;Ð;Ð;Ð;Ð;Ð;Ð;ˆØð 	Ø(,×(BÒ(BØØñ)ô )ð #ð #ð #ð #ð #ð #ˆCÔð ˆJð
 ×9Ò9¸$À
ÑKÔKÐKÐKÐKÐKÐKÐKˆ	Øð 	ØÐð #×4Ò4°ZÀÄÑNÔNÐNÐNÐNÐNÐNÐNˆØÔ!ÝÔ$ÝÔ#ð&
ð 
ð 
ð !Ð Ø×Ò˜}Ñ-Ô-ð 	!Ø26×2LÒ2LØØñ3ô 3ð -ð -ð -ð -ð -ð -ˆMÔ)ð !Ð ð
 ×-Ò-¨d°JÑ?Ô?Ð?Ð?Ð?Ð?Ð?Ð?ˆØð 	ØˆLõ
 %Ý'Ô+Ø:¨t¬yÐ:Ð:Ø> T¤\Ô%6Ô%<Ð>Ð>ð
ñ 
ô 
ˆð
 )-×(BÒ(BØØñ)
ô )
ð #
ð #
ð #
ð #
ð #
ð #
ˆÔð ˆr   c              ƒ   ó  K  — |                       ||¦  «        ƒ d{V —†}|r|S |                      ||¦  «        ƒ d{V —†}|r#|                      ||¦  «        ƒ d{V —†|_        |S |                      ||¦  «        ƒ d{V —†}|r|S |                     || j        ¦  «        ƒ d{V —†}|j        t          j	        t          j
        fv r|S |                      ||¦  «        ƒ d{V —†}|r|S t          t          j	        d|j        › d�d¬¦  «        S )uû  Permission check for :attr:`PermissionMode.BYPASS`.

        BYPASS is the "fully trusted" mode: the user has explicitly
        opted out of safety prompts. All tool-emitted safety ASKs
        (``rm -rf /``, write to ``~/.bashrc``, command-injection
        patterns, dangerous sed, etc.) are **skipped** â€” only
        user-configured deny / ask rules and tool-emitted DENY remain
        as guardrails. The :attr:`PermissionDecision.bypass_immune`
        field has no effect in BYPASS by design.

        Use BYPASS only for sandboxed / containerized environments or
        when you fully trust the agent's behavior. For unattended
        execution where safety still matters, use
        :attr:`PermissionMode.DONT_ASK` instead â€” it converts safety
        ASKs to DENY rather than skipping them.

        Evaluation order:

        1. Deny rules â†’ DENY
        2. Ask rules â†’ ASK (with suggestions; honors explicit user intent)
        3. Read-only fast path â†’ ALLOW
        4. ``tool.check_permissions``:
            - ALLOW / DENY â†’ returned as-is
            - ASK (including bypass-immune safety ASKs) â†’ falls through
            - PASSTHROUGH â†’ falls through
        5. Allow rules â†’ ALLOW
        6. Fallback â†’ ALLOW (BYPASS)

        Args:
            tool (`ToolBase`):
                The tool instance being called.
            tool_input (`dict[str, Any]`):
                The tool input data.

        Returns:
            `PermissionDecision`:
                The final decision.
        NúPermission granted for z (bypass mode)z!Bypass mode allows all operationsr7   )r;   r<   r=   r>   r?   r@   r   r   r
   r   r   rB   r	   rC   ©r   r$   r%   rF   rG   rH   rI   rJ   s           r   r/   zPermissionEngine._check_bypass‹  s¤  è è € ð\ ×+Ò+¨D°*Ñ=Ô=Ð=Ð=Ð=Ð=Ð=Ð=ˆØð 	ØˆKð
 ×)Ò)¨$°
Ñ;Ô;Ð;Ð;Ð;Ð;Ð;Ð;ˆØð 	Ø(,×(BÒ(BØØñ)ô )ð #ð #ð #ð #ð #ð #ˆCÔð ˆJð
 ×9Ò9¸$À
ÑKÔKÐKÐKÐKÐKÐKÐKˆ	Øð 	ØÐð #×4Ò4°ZÀÄÑNÔNÐNÐNÐNÐNÐNÐNˆØÔ!ÝÔ$ÝÔ#ð&
ð 
ð 
ð !Ð ð
 ×-Ò-¨d°JÑ?Ô?Ð?Ð?Ð?Ð?Ð?Ð?ˆØð 	ØˆLõ
 "Ý'Ô-ØG¨d¬iÐGÐGÐGØ?ð
ñ 
ô 
ð 	
r   c              ƒ   óÖ  K  — |                       ||¦  «        ƒ d{V —†}|r|S |                      ||¦  «        ƒ d{V —†}|r7|                      ||¦  «        ƒ d{V —†|_        |                      ||¦  «        S |                      ||¦  «        ƒ d{V —†}|r|S |                     || j        ¦  «        ƒ d{V —†}|j        t          j
        t          j        fv r|S |                      |¦  «        r7|                      ||¦  «        ƒ d{V —†|_        |                      ||¦  «        S |                      ||¦  «        ƒ d{V —†}|r|S t          t          j        d|j        › d�d¬¦  «        S )uŒ  Permission check for :attr:`PermissionMode.DONT_ASK`.

        Used when no user is available to answer prompts (scheduled
        tasks, background runs). Behaves as the *unattended* counterpart
        of :attr:`PermissionMode.ACCEPT_EDITS`: read-only invocations and
        edits within a working directory are auto-allowed, while anything
        that would otherwise prompt the (absent) user is refused.
        Invariant: this method must never return
        :attr:`PermissionBehavior.ASK` â€” every code path that would
        otherwise ASK is converted to DENY via
        :meth:`_convert_ask_to_deny`. Evaluation order:

        1. Deny rules â†’ DENY
        2. Ask rules â†’ DENY (converted, with suggestions preserved)
        3. Read-only fast path â†’ ALLOW
        4. ``tool.check_permissions``:
            - ALLOW (e.g. an edit within a working directory) / DENY â†’
              returned as-is
            - Safety ASK â†’ DENY (converted, with suggestions preserved)
            - Non-safety ASK / PASSTHROUGH â†’ continue
        5. Allow rules â†’ ALLOW
        6. Default â†’ DENY (user not available to answer)

        Args:
            tool (`ToolBase`):
                The tool instance being called.
            tool_input (`dict[str, Any]`):
                The tool input data.

        Returns:
            `PermissionDecision`:
                The final decision (never ASK).
        NrM   z% (dont_ask mode - user not available)z2User is not available to answer permission promptsr7   )r;   r<   r=   r>   Ú_convert_ask_to_denyr?   r@   r   r   r
   r   r   rA   rB   r	   rC   rQ   s           r   r1   z PermissionEngine._check_dont_askë  s*  è è € ðR ×+Ò+¨D°*Ñ=Ô=Ð=Ð=Ð=Ð=Ð=Ð=ˆØð 	ØˆKð
 ×)Ò)¨$°
Ñ;Ô;Ð;Ð;Ð;Ð;Ð;Ð;ˆØð 	8Ø(,×(BÒ(BØØñ)ô )ð #ð #ð #ð #ð #ð #ˆCÔð ×,Ò,¨T°3Ñ7Ô7Ð7ð
 ×9Ò9¸$À
ÑKÔKÐKÐKÐKÐKÐKÐKˆ	Øð 	ØÐð #×4Ò4°ZÀÄÑNÔNÐNÐNÐNÐNÐNÐNˆØÔ!ÝÔ$ÝÔ#ð&
ð 
ð 
ð !Ð Ø×Ò˜}Ñ-Ô-ð 	BØ26×2LÒ2LØØñ3ô 3ð -ð -ð -ð -ð -ð -ˆMÔ)ð ×,Ò,¨T°=ÑAÔAÐAð
 ×-Ò-¨d°JÑ?Ô?Ð?Ð?Ð?Ð?Ð?Ð?ˆØð 	ØˆLõ
 "Ý'Ô,ð8¨¬ð 8ð 8ð 8ðð
ñ 
ô 
ð 	
r   Úask_decisionc                 óh   — t          t          j        d| j        › d�d|j        › �|j        ¬¦  «        S )uˆ  Convert an ASK decision into a DENY for DONT_ASK mode.

        DONT_ASK's invariant is "never return ASK" â€” the user is not
        available to answer prompts. This helper turns whatever produced
        the ASK (an ASK rule, a safety check) into a DENY while
        preserving traceability by carrying the original reason and
        ``suggested_rules`` forward; callers (e.g. a UI surfacing the
        scheduled-task failure) can still show the user what rule they
        could add to unblock the operation in the future.

        Args:
            tool (`ToolBase`):
                The tool whose invocation is being denied.
            ask_decision (`PermissionDecision`):
                The original ASK decision to convert.

        Returns:
            `PermissionDecision`:
                A DENY decision with the original ASK's reason and
                suggestions attached.
        rM   z< (dont_ask mode - ASK converted to DENY, user not available)z6DONT_ASK mode converted ASK to DENY. Original reason: )r   r8   r9   r>   )r	   r
   r   rC   r9   r>   )r$   rT   s     r   rS   z%PermissionEngine._convert_ask_to_denyQ  sY   € õ4 "Ý'Ô,ð'¨¬ð 'ð 'ð 'ð
CØ$0Ô$@ðCð Cð )Ô8ð
ñ 
ô 
ð 	
r   Údecisionc                 ó:   — | j         t          j        k    o| j        S )aD  Whether a decision is a bypass-immune safety ASK.

        A safety ASK is an ASK that a tool has explicitly marked with
        :attr:`PermissionDecision.bypass_immune` ``= True``. Tools emit
        these for dangerous operations (e.g. write to ``~/.bashrc``,
        ``rm -rf /``, command-injection patterns) that must be surfaced
        to the user regardless of allow rules in
        ``DEFAULT``/``ACCEPT_EDITS``. ``BYPASS`` mode intentionally
        skips this check (see :meth:`_check_bypass`); ``DONT_ASK``
        converts the ASK to DENY (see :meth:`_check_dont_ask`).

        Args:
            decision (`PermissionDecision`):
                The decision returned by a tool's ``check_permissions``.

        Returns:
            `bool`:
                True iff ``behavior == ASK`` and ``bypass_immune`` is set.
        )r   r
   r!   Úbypass_immune)rV   s    r   rA   zPermissionEngine._is_safety_asky  s#   € ð, ÔÕ!3Ô!7Ò7ð 'ØÔ&ð	
r   c              ƒ   óŠ   K  — |                      |¦  «        ƒ d{V —†r%t          t          j        d|j        › d�d¬¦  «        S dS )u  Read-only fast path shared by every mode.

        A read-only invocation has no side effects, so it is auto-allowed
        in every :class:`PermissionMode`. Keeping this in a single helper
        (rather than re-inlining it per mode) guarantees the modes cannot
        drift apart on read-only handling â€” the exact divergence that
        previously left ``DEFAULT`` and ``DONT_ASK`` without a read-only
        fast path while ``ACCEPT_EDITS`` / ``EXPLORE`` had one.

        Args:
            tool (`ToolBase`):
                The tool instance being called.
            tool_input (`dict[str, Any]`):
                The tool input data.

        Returns:
            `PermissionDecision | None`:
                An ALLOW decision if this invocation is read-only, else None.
        NrP   z (read-only invocation)z%Read-only operations are auto-allowedr7   )Úcheck_read_onlyr	   r
   r   rC   ©r   r$   r%   s      r   r?   z+PermissionEngine._check_read_only_fast_path“  sr   è è € ð0 ×%Ò% jÑ1Ô1Ð1Ð1Ð1Ð1Ð1Ð1ð 	Ý%Ý+Ô1ð.¨d¬ið .ð .ð .ð !Hðñ ô ð ð ˆtr   Ú
input_datac              ƒ   óö   K  — | j         j                             |j        g ¦  «        }|D ]N}|                      |||¦  «        ƒ d{V —†r/t          t          j        d|j        › d�d|j        › �¬¦  «        c S ŒOdS )aZ  Check if any deny rules match the request.

        Args:
            tool (`ToolBase`):
                The tool instance being called
            input_data (`dict[str, Any]`):
                The tool input data

        Returns:
            `PermissionDecision | None`:
                DENY decision if a rule matches, None otherwise
        NzPermission to use z has been deniedúRule: r7   )	r   r    ÚgetrC   Ú_rule_matchesr	   r
   r   Úrule_content©r   r$   r\   Úrulesr   s        r   r;   z"PermissionEngine._check_deny_rules¶  s°   è è € ð" ”Ô'×+Ò+¨D¬I°rÑ:Ô:ˆØð 	ð 	ˆDØ×'Ò'¨¨d°JÑ?Ô?Ð?Ð?Ð?Ð?Ð?Ð?ð Ý)Ý/Ô4ØL°´ÐLÐLÐLØ$@¨TÔ->Ð$@Ð$@ðñ ô ð ð ð ðð ˆtr   c              ƒ   óô   K  — | j         j                             |j        g ¦  «        }|D ]M}|                      |||¦  «        ƒ d{V —†r.t          t          j        d|j        › �d|j        › �¬¦  «        c S ŒNdS )ax  Check if any ask rules match the request.

        Args:
            tool (`ToolBase`):
                The tool instance being called (used for tool-specific checks)
            input_data (`dict[str, Any]`):
                The tool input data

        Returns:
            `PermissionDecision | None`:
                ASK decision if a rule matches, None otherwise
        Nr5   r^   r7   )	r   r"   r_   rC   r`   r	   r
   r!   ra   rb   s        r   r<   z!PermissionEngine._check_ask_rulesÑ  s­   è è € ð" ”Ô&×*Ò*¨4¬9°bÑ9Ô9ˆØð 	ð 	ˆDØ×'Ò'¨¨d°JÑ?Ô?Ð?Ð?Ð?Ð?Ð?Ð?ð Ý)Ý/Ô3ØB°t´yÐBÐBØ$@¨TÔ->Ð$@Ð$@ðñ ô ð ð ð ðð ˆtr   c              ƒ   óä   K  — | j         j                             |j        g ¦  «        }|D ]E}|                      |||¦  «        ƒ d{V —†r&t          t          j        d|j        › �|¬¦  «        c S ŒFdS )a|  Check if any allow rules match the request.

        Args:
            tool (`ToolBase`):
                The tool instance being called (used for tool-specific checks)
            input_data (`dict[str, Any]`):
                The tool input data

        Returns:
            `PermissionDecision | None`:
                ALLOW decision if a rule matches, None otherwise
        NrP   )r   r8   Úupdated_input)r   r   r_   rC   r`   r	   r
   r   rb   s        r   rB   z#PermissionEngine._check_allow_rulesì  s¢   è è € ð" ”Ô(×,Ò,¨T¬Y¸Ñ;Ô;ˆØð 	ð 	ˆDØ×'Ò'¨¨d°JÑ?Ô?Ð?Ð?Ð?Ð?Ð?Ð?ð Ý)Ý/Ô5ØA°d´iÐAÐAØ",ðñ ô ð ð ð ðð ˆtr   c              ƒ   óZ   K  — |j         sdS t          |j        |j         |¦  «        ƒ d{V —†S )a  Check if a rule matches the input data.

        The matching strategy depends on the tool type:
        - Bash: Substring matching against the command
        - Write/Read: Glob pattern matching against file paths
        - Other: Generic pattern matching

        Args:
            rule (`PermissionRule`):
                The permission rule to check
            input_data (`dict[str, Any]`):
                The tool input data

        Returns:
            `bool`:
                True if the rule matches, False otherwise
        TN)ra   r   Ú
match_rule)r   r$   r   r\   s       r   r`   zPermissionEngine._rule_matches  sX   è è € ð0 Ô ð 	Ø�4õ 1ØŒOØÔØñ
ô 
ð 
ð 
ð 
ð 
ð 
ð 
ð 	
r   c              ƒ   ó<   K  — t          |j        |¦  «        ƒ d{V —†S )ak  Generate suggested permission rules from a tool call.

        This method analyzes the tool call and generates broader permission
        suggestions that the user can apply to avoid future confirmations.

        Strategy:
        - For Bash: Extract command prefix (e.g., "npm run" -> "npm run:*")
        - For File operations: Extract directory (e.g.,
         "src/file.py" -> "src/**")
        - For other tools: Generate exact match rule

        Args:
            tool (`ToolBase`):
                The tool instance being called (used for tool-specific
                suggestions)
            tool_input (`dict[str, Any]`):
                The tool input data (used for generating suggestions)

        Returns:
            `List[PermissionRule]`:
                List of suggested permission rules (usually 1, max 5 for
                compound commands)
        N)r   Úgenerate_suggestionsr[   s      r   r=   z&PermissionEngine._generate_suggestions,  sB   è è € õB 1ØÔ%Øñ
ô 
ð 
ð 
ð 
ð 
ð 
ð 
ð 	
r   )Ú__name__Ú
__module__Ú__qualname__Ú__doc__r   r   r   r#   r   ÚdictÚstrr   r	   r3   r)   r+   r-   r/   r1   ÚstaticmethodrS   ÚboolrA   r?   r;   r<   rB   r`   r   r=   © r   r   r   r      sÄ  € € € € € ðð ðà"ðð 
ðð ð ð ð @˜^ð @°ð @ð @ð @ð @ð8&=àð&=ð ˜˜c˜”Nð&=ð 
ð	&=ð &=ð &=ð &=ðP_àð_ð ˜˜c˜”Nð_ð 
ð	_ð _ð _ð _ðBQ
àðQ
ð ˜˜c˜”NðQ
ð 
ð	Q
ð Q
ð Q
ð Q
ðf`àð`ð ˜˜c˜”Nð`ð 
ð	`ð `ð `ð `ðD^
àð^
ð ˜˜c˜”Nð^
ð 
ð	^
ð ^
ð ^
ð ^
ð@d
àðd
ð ˜˜c˜”Nðd
ð 
ð	d
ð d
ð d
ð d
ðL ð%
Øð%
à(ð%
ð 
ð%
ð %
ð %
ñ „\ð%
ðN ð
Ð!3ð 
¸ð 
ð 
ð 
ñ „\ð
ð2!àð!ð ˜˜c˜”Nð!ð 
˜dÑ	"ð	!ð !ð !ð !ðFàðð ˜˜c˜”Nðð 
˜dÑ	"ð	ð ð ð ð6àðð ˜˜c˜”Nðð 
˜dÑ	"ð	ð ð ð ð6àðð ˜˜c˜”Nðð 
˜dÑ	"ð	ð ð ð ð6#
àð#
ð ð#
ð ˜˜c˜”Nð	#
ð
 
ð#
ð #
ð #
ð #
ðJ$
àð$
ð ˜˜c˜”Nð$
ð 
ˆnÔ	ð	$
ð $
ð $
ð $
ð $
ð $
r   r   N)rn   Útypingr   r   r   Ú_contextr   Ú_ruler   Ú	_decisionr	   r
   Ú_typesr   Ú_utils._commonr   r$   r   r   rs   r   r   ú<module>rz      sç   ðà HÐ HØ +Ð +Ð +Ð +Ð +Ð +Ð +Ð +Ð +Ð +à 'Ð 'Ð 'Ð 'Ð 'Ð 'Ø !Ð !Ð !Ð !Ð !Ð !Ø =Ð =Ð =Ð =Ð =Ð =Ð =Ð =Ø "Ð "Ð "Ð "Ð "Ð "Ø 8Ð 8Ð 8Ð 8Ð 8Ð 8àð ØÐÐÐÐÐÐà€Hð
ð 
ð 
ð 
ð 
ñ 
ô 
ð 
ð 
ð 
r   