§
    ÚŽ’j_D  ã                   óî   — d Z ddlZddlZddlmZmZ ddlmZ ddlm	Z	m
Z
mZmZ ddlmZ ddlmZmZ d	d
lmZmZmZmZ ddlmZ ddlmZ  G d„ de¦  «        Z G d„ de¦  «        Z G d„ de¦  «        ZdS )z The tool protocol in agentscope.é    N)ÚabstractmethodÚABC)ÚPath)ÚAsyncGeneratorÚAnyÚCallableÚList)Ú	BaseModelé   )ÚDEFAULT_DANGEROUS_FILESÚDEFAULT_DANGEROUS_DIRECTORIESé   )ÚPermissionContextÚPermissionDecisionÚPermissionRuleÚPermissionBehavior)Ú	ToolChunk)Ú_remove_title_fieldc                   ó@   ‡ — e Zd ZdZedededefˆ fd„¦   «         Zˆ xZS )Ú
ParamsBasezdA base class for tool parameters that remove the title field from the
    exported JSON schema.
    ÚargsÚkwargsÚreturnc                 óP   •— t           t          ¦   «         j        |i |¤Ž¦  «        S )z_An override implementation to remove the title field from the
        exported schema.
        )r   ÚsuperÚmodel_json_schema)Úclsr   r   Ú	__class__s      €úCC:\Projects\DSCons\.venv\Lib\site-packages\agentscope\tool\_base.pyr   zParamsBase.model_json_schema   s*   ø€ õ
 #Ð#<¥5¡7¤7Ô#<¸dÐ#MÀfÐ#MÐ#MÑNÔNÐNó    )	Ú__name__Ú
__module__Ú__qualname__Ú__doc__Úclassmethodr   Údictr   Ú__classcell__)r   s   @r   r   r      su   ø€ € € € € ðð ð ðO cð O°Sð O¸Tð Oð Oð Oð Oð Oñ „[ðOð Oð Oð Oð Or    r   c                   óz   — e Zd ZdZedddeeef         dede	e
df         f         de	e
df         fd	„¦   «         ZdS )
ÚToolMiddlewareBaseu…  Base class for tool middlewares.

    A tool middleware wraps the execution of a tool in an onion fashion: the
    first registered middleware is the outermost layer and runs its pre-logic
    before any inner layer, then its post-logic after all inner layers have
    completed. Subclass this and implement :meth:`on_tool_call` â€” the signature
    is already spelled out, so second-party developers only need to fill in the
    body without reasoning about the wrapping protocol.

    Streaming and non-streaming tools are unified: ``next_handler`` always
    returns an async generator, so a middleware never needs to know whether the
    underlying tool yields a stream of chunks or returns a single chunk.

    Example:
        ```python
        class LoggingMiddleware(ToolMiddlewareBase):
            async def on_tool_call(self, tool, input_kwargs, next_handler):
                print(f"Calling {tool.name} with {input_kwargs}")
                async for chunk in next_handler(**input_kwargs):
                    yield chunk
                print(f"Finished {tool.name}")

        tool = MyTool(middlewares=[LoggingMiddleware()])
        ```
    ÚtoolÚToolBaseÚinput_kwargsÚnext_handler.Nr   c              ƒ   ó
   K  — dS )aå  Intercept a single tool invocation.

        Add pre-/post-logic around ``next_handler``, rewrite the tool inputs by
        passing modified keyword arguments to ``next_handler``, or transform
        the yielded chunks.

        Args:
            tool (`ToolBase`):
                The tool instance being invoked.
            input_kwargs (`dict[str, Any]`):
                The tool's input arguments for this invocation. Pass them on
                via ``next_handler(**input_kwargs)``; mutate or replace them to
                change what the inner layers and the tool itself receive.
            next_handler (`Callable[..., AsyncGenerator[ToolChunk, None]]`):
                Call it as ``next_handler(**input_kwargs)`` to run the next
                layer. It always returns an async generator, regardless of
                whether the underlying tool is streaming or not.

        Yields:
            `ToolChunk`:
                The chunks produced by this tool invocation.
        N© )Úselfr*   r,   r-   s       r   Úon_tool_callzToolMiddlewareBase.on_tool_call?   ó
   è è € € € r    )r!   r"   r#   r$   r   r&   Ústrr   r   r   r   r1   r/   r    r   r)   r)   $   sˆ   € € € € € ðð ð4 ðàðð ˜3 ˜8”nðð ˜s N°9¸d°?Ô$CÐCÔDð	ð
 
˜	 4˜Ô	(ðð ð ñ „^ðð ð r    r)   c            	       ó&  — e Zd ZU dZeed<   	 eed<   	 eeef         ed<   	 eed<   	 eed<   	 dZ	eed<   	 dZ
eed	<   	 dZeed
<   	 dZedz  ed<   	 eZee         ed<   	 eZee         ed<   	 	 d!ded         dz  ddfd„Zdededeeedf         z  fd„Zdededeeedf         z  fd„Zedeeef         dedefd„¦   «         Zdeeef         defd„Zdedz  deeef         defd„Zdeeef         dee         fd„Zdededefd„Z dedefd „Z!dS )"r+   zThe tool protocol.ÚnameÚdescriptionÚinput_schemaÚis_concurrency_safeÚis_read_onlyFÚis_external_toolÚis_state_injectedÚis_mcpNÚmcp_nameÚdangerous_filesÚdangerous_directoriesÚmiddlewaresr)   r   c                 ó   — |�|ng | _         dS )a)  Initialize the tool with optional middlewares.

        Args:
            middlewares (`List[ToolMiddlewareBase] | None`, optional):
                A list of :class:`ToolMiddlewareBase` instances wrapping the
                tool execution in an onion fashion. Defaults to an empty list.
        N)Ú_middlewares)r0   r@   s     r   Ú__init__zToolBase.__init__�   s   € ð 'Ð2ˆKˆK¸ð 	ÔÐÐr    r   r   c              �   ó„   K  — | j         st          | j        j        › d�¦  «        ‚t	          | j        j        › d�¦  «        ‚)aA  Execute the tool logic.

        This is the new override point for tool implementations.
        Subclasses should override this method instead of
        :meth:`__call__`.  The base implementation raises
        :exc:`NotImplementedError` for non-external tools and
        :exc:`RuntimeError` for external tools.

        Args:
            **kwargs: Tool input arguments.

        Returns:
            `ToolChunk | AsyncGenerator[ToolChunk, None]`:
                A single :class:`~agentscope.tool.ToolChunk` or an
                async generator that yields them.
        z does not implement callz6 is an external tool and should not be called directly)r:   ÚNotImplementedErrorr   r!   ÚRuntimeError)r0   r   r   s      r   ÚcallzToolBase.call�   sa   è è € ð* Ô$ð 	Ý%Ø”>Ô*ÐDÐDÐDñô ð õ ØŒ~Ô&ð "ð "ð "ñ
ô 
ð 	
r    c           	   �   óx  ‡ ‡‡K  — |r4t          t          ‰ ¦  «        j        › dt          |¦  «        › d�¦  «        ‚t	          ‰ dg ¦  «        Š‰s9t          j        ‰ j        ¦  «        r ‰ j        d
i |¤ŽS  ‰ j        d
i |¤Žƒ d{V —†S 	 ddt          dt          dt          t          df         fˆˆˆ fd	„Š ‰d
i |¤ŽS )aÔ  Invoke the tool, layering any registered middlewares around
        :meth:`call`.

        Tools are always invoked with keyword arguments only. ``*args`` is
        accepted in the signature solely to stay Liskov-compatible with
        subclasses that override ``__call__`` with their own positional
        parameters; any positional argument actually passed here is rejected
        (raising :exc:`TypeError`) so it fails loudly instead of being silently
        dropped.

        Middlewares are applied in an onion fashion: the first registered
        middleware is the outermost layer and runs its pre-logic before
        any inner layers, then its post-logic after all inner layers
        have completed.
        z5 must be called with keyword arguments only, but got z positional argument(s).rB   Nr   ÚindexÚchain_kwargsr   c                óà  •‡ K  — ‰ t          ‰¦  «        k    rwt          j        ‰	j        ¦  «        r ‰	j        di |¤Ž2 3 d{V —†}|W V — Œ6 dS  ‰	j        di |¤Žƒ d{V —†}t	          |t
          ¦  «        r|2 3 d{V —†}|W V — Œ6 dS |W V — dS ‰‰          }t          |¦  «        }dt          dt
          t          df         fˆˆ fd„}| 	                    ‰	||¬¦  «        2 3 d{V —†}|W V — Œ6 dS )z"Execute the tool middleware chain.NÚkwr   c                 ó@   •K  —  ‰‰dz   fi | ¤Ž2 3 d {V —†}|W V — Œ6 d S )Nr   r/   )rL   ÚchunkÚexecute_chainrI   s     €€r   r-   z>ToolBase.__call__.<locals>.execute_chain.<locals>.next_handlerë   s`   øè è € ð (5 }°U¸Q±YÐ'EÐ'EÀ"Ð'EÐ'Eð $ð $ð $ð $ð $ð $ð $˜eØ#˜˜˜˜˜ð (FÐ'EÐ'Es   �)r*   r,   r-   r/   )
ÚlenÚinspectÚisasyncgenfunctionrG   Ú
isinstancer   r&   r   r   r1   )
rI   rJ   rN   ÚresultÚmwr,   r-   rO   r@   r0   s
   `      €€€r   rO   z(ToolBase.__call__.<locals>.execute_chainÑ   sÊ  øøè è € ð
 �˜KÑ(Ô(Ò(Ð(õ Ô-¨d¬iÑ8Ô8ð 	%Ø'0 t¤yÐ'@Ð'@°<Ð'@Ð'@ð $ð $ð $ð $ð $ð $ð $˜eØ#˜˜˜˜˜ð (AÐ'@Ð'@ð $- 4¤9Ð#<Ð#<¨|Ð#<Ð#<Ð<Ð<Ð<Ð<Ð<Ð<�FÝ! &­.Ñ9Ô9ð %Ø+1ð (ð (ð (ð (ð (ð (ð ( %Ø"'˜K˜K˜K˜K˜Kð ,2¨6¨6ð %˜˜˜˜˜˜à  Ô'�Ý# LÑ1Ô1�ð$Ýð$å#¥I¨t OÔ4ð$ð $ð $ð $ð $ð $ð $ð $&§?¢?ØØ!-Ø!-ð $3ñ $ô $ð  ð  ð  ð  ð  ð  ð  ˜%ð
  �K�K�K�K�Kð$ð $ð $s   ¾AÁ8BÃ C-r/   )r   )Ú	TypeErrorÚtyper!   rP   ÚgetattrrQ   rR   rG   Úintr   r   r   )r0   r   r   rO   r@   s   `  @@r   Ú__call__zToolBase.__call__¯   s8  øøøè è € ð( ð 	ÝÝ˜‘:”:Ô&ð Eð EÝ!$ T¡¤ðEð Eð Eñô ð õ ˜d N°BÑ7Ô7ˆØð 	-ÝÔ)¨$¬)Ñ4Ô4ð +Ø �t”yÐ*Ð* 6Ð*Ð*Ð*Ø"˜œÐ,Ð, VÐ,Ð,Ð,Ð,Ð,Ð,Ð,Ð,Ð,ð ð%	 ð %	 Ýð%	 åð%	 õ �I t˜OÔ,ð%	 ð %	 ð %	 ð %	 ð %	 ð %	 ð %	 ð %	 ðN ˆ}Ð&Ð&˜vÐ&Ð&Ð&r    Ú
tool_inputÚcontextc              ƒ   ó
   K  — dS )z%Check permissions for the tool usage.Nr/   )r0   r[   r\   s      r   Úcheck_permissionszToolBase.check_permissionsú   r2   r    c              ƒ   ó   K  — | j         S )uË  Decide whether this specific invocation is read-only.

        Returns the static :attr:`is_read_only` attribute by default.
        Subclasses with input-dependent semantics (e.g. ``Bash``) should
        override this to inspect ``tool_input`` â€” for example, ``Bash`` is
        statically marked as not read-only but ``ls -a`` is in fact read-only.

        Should be cheap â€” the permission engine may call this before the
        full :meth:`check_permissions` flow.

        Args:
            tool_input (`dict[str, Any]`):
                The tool input data for this invocation.

        Returns:
            `bool`:
                ``True`` if this invocation is read-only, ``False`` otherwise.
        )r9   ©r0   r[   s     r   Úcheck_read_onlyzToolBase.check_read_only  s   è è € ð, Ô Ð r    Úrule_contentc              ƒ   ó   K  — |du S )a2  Check if a permission rule matches the tool input.

        .. note:: This is an optional method. A rule with no content (``None``)
        is a tool-name-level rule that matches every invocation; a rule
        with content requires the tool to override this method with its
        own matching logic, otherwise it returns ``False``.

        This means:
        - ``_FunctionTool`` and ``MCPTool`` (which do not override this)
          can still be controlled at the tool-name level via rules like
          ``{"tool_name": "my_tool", "rule_content": None}``.
        - Specific tools (Bash, Read, Write, Edit, Glob, Grep) override
          this method to support fine-grained pattern matching.

        Args:
            rule_content (`str | None`):
                The rule pattern to match. ``None`` means "match all
                invocations of this tool" (tool-name-level rule).
            tool_input (`dict[str, Any]`):
                The tool input data

        Returns:
            `bool`:
                True if the rule matches, False otherwise
        Nr/   )r0   rb   r[   s      r   Ú
match_rulezToolBase.match_rule  s   è è € ð> ˜tÐ#Ð#r    c              ƒ   óL   K  — t          | j        dt          j        d¬¦  «        gS )aP  Generate suggested permission rules for the tool input.

        .. note:: Suggest a single tool-name-level rule (``rule_content=None``)
        that allows all invocations of this tool. Tools can override this to
        provide finer-grained suggestions.

        For example:
        - File tools (Read/Write/Edit): suggest a glob pattern covering the
          parent directory (e.g., "src/main.py" -> "src/**")
        - Bash: suggest command prefix patterns (e.g., "git commit -m 'xxx'"
          -> "git commit:*")
        - Grep/Glob: suggest patterns based on search paths

        Args:
            tool_input (`dict[str, Any]`):
                The tool input data

        Returns:
            `List[PermissionRule]`:
                List of suggested permission rules (usually 1, max 5 for
                compound operations)
        NÚ	suggested)Ú	tool_namerb   ÚbehaviorÚsource)r   r5   r   ÚALLOWr`   s     r   Úgenerate_suggestionszToolBase.generate_suggestions;  s5   è è € õ6 Øœ)Ø!Ý+Ô1Ø"ð	ñ ô ð
ð 	
r    Ú	file_pathc                 ó6  — t          j        ¦   «         }t          |j                             ¦   «         ¦  «        }|g|z   }t           j                             t           j                             |¦  «        ¦  «        }|D ]š}t           j                             t           j                             |¦  «        ¦  «        }	 t           j                             ||¦  «         | 	                    |t           j
        z   ¦  «        s||k    r dS Œ‹# t          $ r Y Œ—w xY wdS )u¡  Check if a file path is within any allowed working directory.

        A "working directory" is the process's current directory plus any
        entries in :attr:`PermissionContext.working_directories`. Paths
        are compared via :func:`os.path.realpath` so that aliases like
        macOS's ``/tmp`` â†’ ``/private/tmp`` and symlinked working
        directories compare equal on both sides.

        Used by tools that conditionally auto-allow file operations in
        :attr:`PermissionMode.ACCEPT_EDITS` (e.g. Write, Edit, and the
        filesystem-command branch of Bash).

        Args:
            file_path (`str`):
                The file path to check.
            context (`PermissionContext`):
                The permission context containing the working directories.

        Returns:
            `bool`:
                True if ``file_path`` is within any allowed working
                directory.
        TF)ÚosÚgetcwdÚlistÚworking_directoriesÚkeysÚpathÚrealpathÚ
expanduserÚrelpathÚ
startswithÚsepÚ
ValueError)	r0   rl   r\   Úcurrent_dirÚadditional_dirsÚall_working_dirsÚabs_file_pathÚworking_dirÚabs_working_dirs	            r   Ú_path_in_allowed_working_pathz&ToolBase._path_in_allowed_working_path^  s  € õ8 ”i‘k”kˆÝ˜wÔ:×?Ò?ÑAÔAÑBÔBˆØ'˜=¨?Ñ:Ðåœ×(Ò(­¬×);Ò);¸IÑ)FÔ)FÑGÔGˆà+ð 	ð 	ˆKÝ œg×.Ò.Ý”×"Ò" ;Ñ/Ô/ñô ˆOð
Ý”—’ ¨Ñ?Ô?Ð?à!×,Ò,¨_½r¼vÑ-EÑFÔFð à$¨Ò7Ð7à˜4˜4ð 8øõ ð ð ð ð �ðøøøð
 ˆus   Â=AD	Ä	
DÄDc                 ó¸  — t           j                             t           j                             |¦  «        ¦  «        }t	          |¦  «        j        }d„ |D ¦   «         }t           j                             |¦  «        }|                     ¦   «         }| j        D ]}||                     ¦   «         k    r dS Œ| j	        D ]}|                     ¦   «         }	|	|v r dS ŒdS )at  Check if a file path is dangerous (sensitive file or directory).

        A path is considered dangerous if:
        1. The filename matches a dangerous file (e.g., .bashrc, .gitconfig)
        2. Any path segment matches a dangerous directory (e.g., .git, .ssh)

        Case-insensitive matching is used to prevent bypasses on
        case-insensitive filesystems (macOS, Windows).

        Args:
            file_path (`str`):
                The file path to check

        Returns:
            `bool`:
                True if the path is dangerous and should require explicit
                permission

        Example:
            >>> self._is_dangerous_path("/home/user/.bashrc")
            True
            >>> self._is_dangerous_path("/home/user/.git/config")
            True
            >>> self._is_dangerous_path("/home/user/project/main.py")
            False
        c                 ó6   — g | ]}|                      ¦   «         ‘ŒS r/   )Úlower)Ú.0Úps     r   ú
<listcomp>z/ToolBase._is_dangerous_path.<locals>.<listcomp>³  s    € Ð:Ð:Ð:¨!˜AŸGšG™IœIÐ:Ð:Ð:r    TF)
rn   rs   Úabspathru   r   ÚpartsÚbasenamerƒ   r>   r?   )
r0   rl   Úabs_pathÚ
path_partsÚpath_parts_lowerÚfilenameÚfilename_lowerÚdangerous_fileÚdangerous_dirÚdangerous_dir_lowers
             r   Ú_is_dangerous_pathzToolBase._is_dangerous_path’  së   € õ: ”7—?’?¥2¤7×#5Ò#5°iÑ#@Ô#@ÑAÔAˆõ ˜(‘^”^Ô)ˆ
Ø:Ð:¨zÐ:Ñ:Ô:Ðõ ”7×#Ò# HÑ-Ô-ˆØ!ŸšÑ)Ô)ˆØ"Ô2ð 	ð 	ˆNØ ×!5Ò!5Ñ!7Ô!7Ò7Ð7Ø�t�tð 8ð
 "Ô7ð 	ð 	ˆMØ"/×"5Ò"5Ñ"7Ô"7ÐØ"Ð&6Ð6Ð6Ø�t�tð 7ð ˆur    )N)"r!   r"   r#   r$   r3   Ú__annotations__r&   r   Úboolr:   r;   r<   r=   r   r>   rp   r   r?   r	   rC   r   r   rG   rZ   r   r   r   r^   ra   rd   r   rk   r€   r’   r/   r    r   r+   r+   ^   sñ  € € € € € € ØÐà
€I€I�IØ*ØÐÐÑØ.Ø�s˜C�x”.Ð Ð Ñ ØEØÐÐÑØ+ØÐÐÑðà"Ð�dÐ"Ð"Ñ"ðNà#Ð�tÐ#Ð#Ñ#ðð €FˆDÐÐÑØKØ€Hˆc�D‰jÐÐÑð!ð "9€O�T˜#”YÐ8Ð8Ñ8ØMØ'DÐ˜4 œ9ÐDÐDÑDðð
 :>ð
ð 
àÐ.Ô/°$Ñ6ð
ð 
ð
ð 
ð 
ð 
ð
àð
ð ð
ð 
�^ I¨t OÔ4Ñ	4ð	
ð 
ð 
ð 
ð>I'àðI'ð ðI'ð 
�^ I¨t OÔ4Ñ	4ð	I'ð I'ð I'ð I'ðV ð4à˜˜c˜”Nð4ð #ð4ð 
ð	4ð 4ð 4ñ „^ð4ð!à˜˜c˜”Nð!ð 
ð!ð !ð !ð !ð0$à˜D‘jð$ð ˜˜c˜”Nð$ð 
ð	$ð $ð $ð $ðB!
à˜˜c˜”Nð!
ð 
ˆnÔ	ð!
ð !
ð !
ð !
ðF2àð2ð #ð2ð 
ð	2ð 2ð 2ð 2ðh1¨Cð 1°Dð 1ð 1ð 1ð 1ð 1ð 1r    r+   )r$   rQ   rn   Úabcr   r   Úpathlibr   Útypingr   r   r   r	   Úpydanticr
   Ú
_constantsr   r   Ú
permissionr   r   r   r   Ú	_responser   Ú_utilsr   r   r)   r+   r/   r    r   ú<module>r�      s”  ðð 'Ð &Ø €€€Ø 	€	€	€	Ø #Ð #Ð #Ð #Ð #Ð #Ð #Ð #Ø Ð Ð Ð Ð Ð Ø 6Ð 6Ð 6Ð 6Ð 6Ð 6Ð 6Ð 6Ð 6Ð 6Ð 6Ð 6à Ð Ð Ð Ð Ð à NÐ NÐ NÐ NÐ NÐ NÐ NÐ Nðð ð ð ð ð ð ð ð ð ð ð ð !Ð  Ð  Ð  Ð  Ð  Ø 'Ð 'Ð 'Ð 'Ð 'Ð 'ð
Oð 
Oð 
Oð 
Oð 
O�ñ 
Oô 
Oð 
Oð7ð 7ð 7ð 7ð 7˜ñ 7ô 7ð 7ðteð eð eð eð eˆsñ eô eð eð eð er    