
    4Rj                       d Z ddlmZ ddlZddlZddlZddlZddlZddlZddl	m
Z
 ddlmZmZmZmZmZmZ ddlmZm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 m!Z!m"Z" ddl#Z#ddl$Z#ddl%m&Z& ddl'm(Z(m)Z) ddl*m+Z+m,Z, ddl-m.Z. ddl/m0Z0m1Z1m2Z2m3Z3m4Z4m5Z5 ddl6m7Z7m8Z8m9Z9 erddl:m;Z; g dZ<dZ=dZ>dZ?e?Z@dZAdZBdZCdZD ej                  d      ZFd2dZG ej                  d      ZId3dZJd4d ZKd5d!ZLe G d" d#             ZMd6d$ZN G d% d&e      ZO G d' d(      ZPd7d)ZQd7d*ZRd7d+ZSd7d,ZTd8d-ZUd7d.ZVd7d/ZWd9d0ZXd:d1ZYy);u>  Reference implementations of the ``agent_toolset_20260401`` tools — ``bash``,
``read``, ``write``, ``edit``, ``glob``, ``grep`` — plus the workdir/skills
:class:`AgentToolContext`.

This sits next to the other ``lib/tools`` helpers (the Messages tool runner, the
memory tool, …). Importing it pulls in ``subprocess`` etc., so it is kept out of
``anthropic.lib.tools.__init__`` — depend on it explicitly
(``from anthropic.lib.tools.agent_toolset import beta_agent_toolset_20260401``).

The result of :func:`beta_agent_toolset_20260401` is a plain
``list[BetaAsyncFunctionTool]`` — *async* function tools, so it is for the
**async** runners only: ``client.beta.sessions.events.tool_runner(...)`` (the
``SessionToolRunner``, always async) for a managed-agents session, or — via the
:class:`~anthropic.lib.environments.EnvironmentWorker` — the self-hosted
environment worker. The sync ``Anthropic`` ``messages.tool_runner`` accepts
``BetaRunnableTool``, which excludes the async function tools this returns, so
it cannot consume this toolset.

.. warning::
   ``bash`` is **stateful**: it owns a persistent ``/bin/bash`` subprocess that
   is only torn down by its ``close`` cleanup hook. Only ``SessionToolRunner``
   (and the ``EnvironmentWorker`` built on it) invoke that hook. The Messages
   ``client.beta.messages.tool_runner(...)`` does **not** call ``close``, so
   handing this toolset to the Messages tool runner leaks the bash subprocess
   (one orphaned shell per run). Run stateful tools under
   ``client.beta.sessions.events.tool_runner(...)`` / the environment worker,
   or drop ``bash`` from the toolset before using the Messages tool runner.

Trust model: the file tools confine to ``workdir`` (symlink-aware) and are safe
without a sandbox; ``bash`` is unrestricted and should run inside one. See
:class:`AgentToolContext`.
    )annotationsN)S_ISREG)TYPE_CHECKINGAnyListOptional
NamedTuplecast)PathPurePosixPath)partial)islice)asynccontextmanager)field	dataclass)MappingCallable	AwaitableAsyncIterator)run_sync   )_withindownload_session_skills   )NotGiven	not_given)is_given).BetaManagedAgentsAgentToolset20260401BashInput.BetaManagedAgentsAgentToolset20260401EditInput.BetaManagedAgentsAgentToolset20260401GlobInput.BetaManagedAgentsAgentToolset20260401GrepInput.BetaManagedAgentsAgentToolset20260401ReadInput/BetaManagedAgentsAgentToolset20260401WriteInput)	ToolErrorBetaAsyncFunctionToolbeta_async_tool)AsyncAnthropic)AgentToolContextBashSession
BashResultresolve_pathbeta_agent_toolset_20260401beta_bash_toolbeta_read_toolbeta_write_toolbeta_edit_toolbeta_glob_toolbeta_grep_tooli  g      ^@i   i     iP  z\x1b\[[0-9;?]*[ -/]*[@-~]c                (    t        |       r| S t        S )u  Resolve a configured cap to an effective size limit.

    ``not_given`` selects ``DEFAULT_MAX_FILE_BYTES``; ``None`` disables the size
    check (uncapped); a positive int is the cap. Governs only the size guard —
    callers still reject non-regular files.
    )r   DEFAULT_MAX_FILE_BYTES)
configureds    i/var/www/html/meridian/meridian-ai/venv/lib/python3.12/site-packages/anthropic/lib/tools/agent_toolset.py_resolve_max_bytesr8   `   s     "*-:I3II    z!anthropic.lib.tools.agent_toolsetc                     t         j                  j                         D  ci c]  \  } }| j                  d      r| | c}} S c c}} w )u  The environment for the bash subprocess, with the runner's own
    credentials scrubbed.

    The bash tool runs model-issued commands, so it must never inherit the
    runner's ``ANTHROPIC_*`` variables (API key, environment key, per-work
    session tokens): a prompt-injected ``echo $ANTHROPIC_API_KEY`` would
    otherwise land the credential straight in the session transcript. Passing
    an explicit ``env`` to :class:`AgentToolContext` does NOT add to this
    default — it FULLY REPLACES it. The provided mapping becomes the entire
    bash environment verbatim; nothing here is merged in, so callers who want
    the scrubbed process environment plus extras must build that mapping
    themselves.
    
ANTHROPIC_)osenvironitems
startswith)kvs     r7   _default_bash_envrB   m   s8      ZZ--/RTQq||L7QAqDRRRs
   AAc                "   t        |t              rd}njt        |t              rd}nWt        |t              rd}nDt        |t              rd}n1t        |t
              rd}n|j                  xs dj                         }t        |  d| d|       S )zMap a filesystem ``OSError`` to a consistent, runtime-independent message.

    The raw ``OSError`` string is platform-specific (``[Errno 2] ENOENT: ...``);
    normalise the common cases so the model sees the same wording everywhere.
    zno such file or directoryznot a directoryzis a directoryzpermission deniedzfile already existsz	i/o errorz: )	
isinstanceFileNotFoundErrorNotADirectoryErrorIsADirectoryErrorPermissionErrorFileExistsErrorstrerrorlowerr$   )op	file_pathereasons       r7   	_fs_errorrP   ~   s     !&',	A)	*"	A(	)!	A	'$	A	'&**+224t2i[6(344r9   c                     g S N rS   r9   r7   _empty_skill_dirsrT      s    Ir9   c                      e Zd ZU dZ eej                        Zded<   dZ	ded<   dZ
d	ed
<   dZded<   dZded<   eZded<    eddd      Zded<    eedd      Zded<   ddZddZddZddZddZd dZy)!r(   u	  Workdir + path-policy for the agent toolset.

    Trust model — two tiers:

    - The file tools (:func:`beta_read_tool`, :func:`beta_write_tool`,
      :func:`beta_edit_tool`, :func:`beta_glob_tool`, :func:`beta_grep_tool`)
      resolve paths against ``workdir`` and reject escapes unless
      ``unrestricted_paths`` is set. :func:`resolve_path` follows every symlink
      (including the leaf, even a dangling one) before the check and returns
      that canonical path for the operation, so a symlink inside the workdir
      that points outside it can neither pass the check nor be followed
      afterwards — a real boundary, consistent with the memory tool, so the
      file tools are safe to use without a sandbox.
    - :func:`beta_bash_tool` runs an unrestricted ``/bin/bash`` regardless of
      ``unrestricted_paths``. Confinement for it must come from the OS layer
      (e.g. a self-hosted environment runner).

    Attributes:
        workdir: Base directory for resolving relative tool paths. Defaults to
            :func:`os.getcwd` captured when the context is constructed (TS
            parity: ``process.cwd()`` at construction), so a ``chdir`` between
            constructing this context and the first tool call does not move
            where paths resolve. Pass an explicit path to override.
        unrestricted_paths: When ``False`` (default), the file tools reject
            paths that resolve outside ``workdir``. Does **not**
            constrain :func:`beta_bash_tool`.
        env: Optional environment for the bash subprocess. When unset, the bash
            tool inherits the process environment with the runner's
            ``ANTHROPIC_*`` credentials scrubbed. When provided, it FULLY
            REPLACES that default environment — the mapping is used verbatim
            and is NOT merged with or added to the scrubbed process
            environment. To keep the defaults plus extra vars, build the
            combined mapping yourself before passing it.
        max_file_bytes: Size cap for the ``read`` and ``edit`` tools, which both
            load the whole file into memory. ``not_given`` (default) uses the
            built-in 256 KiB cap; a positive int sets a custom cap; ``None``
            disables the cap entirely. Disabling it reintroduces the OOM risk on
            a model-controlled path, so pass ``None`` only when the sandbox can
            absorb arbitrarily large files. The non-regular-file (FIFO/device)
            guard always applies regardless of this value.
    )default_factorystr | os.PathLike[str]workdirFboolunrestricted_pathsNzAsyncAnthropic | Noneclientz
str | None
session_idOptional[Mapping[str, str]]envint | None | NotGivenmax_file_bytes)defaultinitreprzBashSession | None_bash)rV   rb   rc   
list[Path]_skill_dirsc                   K   | j                   8t        j                  | j                  | j                         d {   | _         | j                   S 7 wNr^   )rd   r)   startrX   r^   selfs    r7   bashzAgentToolContext.bash   sB     ::*004880LLDJzz Ms   ;AAAc                |   K   | j                   *| j                   j                          d {    d | _         y y 7 wrR   )rd   closerk   s    r7   ro   zAgentToolContext.close   s5     ::!**""$$$DJ "$s   *<:<c                   K   | j                   | j                  yt        | j                   | j                  | j                         d{   | _        y7 
w)zDownload the session agent's skills into ``{workdir}/skills/<name>/``.

        No-op unless both :attr:`client` and :attr:`session_id` are set. The
        download + safe archive extraction lives in
        :mod:`anthropic.lib.tools._skills`.
        N)r\   rX   )r[   r\   r   rX   rf   rk   s    r7   setup_skillszAgentToolContext.setup_skills   sG      ;;$//"9!8QUQ`Q`jnjvjv!wwws   A	AAAc                   K   | j                   D ]0  }	 t        t        t        j                  |d             d{    2 g | _         y7 # t
        $ r!}t        j                  d||       Y d}~bd}~ww xY ww)u   Remove the skill directories :meth:`setup_skills` downloaded.

        Only the directories this context created are removed — a pre-existing
        ``{workdir}/skills`` tree is left untouched.
        T)ignore_errorsNz,failed to remove downloaded skill dir %s: %s)rf   r   r   shutilrmtree	Exceptionlogwarning)rl   	skill_dirrN   s      r7   _cleanup_skillsz AgentToolContext._cleanup_skills   sv      )) 	ZIZwv}}itTUUU	Z  V ZJIWXYYZs>   A9(AA
A 
A9
A	A6A1,A91A66A9c                B   K   | j                          d {    | S 7 wrR   )rq   rk   s    r7   
__aenter__zAgentToolContext.__aenter__   s"     !!! 	"s   c                   K   	 | j                          d {    | j                          d {    y 7 7 # | j                          d {  7   w xY wwrR   )ro   rz   )rl   excs     r7   	__aexit__zAgentToolContext.__aexit__   sG     	)**,&&((( ($&&(((s?   A9 59 A7A9 AAAAAreturnr)   r   None)r   r(   )r~   objectr   r   )__name__
__module____qualname____doc__r   r<   getcwdrX   __annotations__rZ   r[   r\   r^   r   r`   rd   rT   rf   rm   ro   rq   rz   r|   r   rS   r9   r7   r(   r(      s    (Z ',BII&FG#F$$ %)F!(!J
!'+C	$+,5N)5 %dU KEK $4EEX]^K^

	x)r9   r(   c                R   t        |      }| j                  r |j                         r|j                         S t        | j                        j                         }|j                         r|n||z  j                         }| j                  st        ||      st        d|d      |S )uq  Resolve ``p`` against the workdir; reject results that escape it.

    Absolute and relative inputs go through the same canonicalise-then-contain
    check — an absolute path that lands inside the workdir is permitted, only
    paths that resolve *outside* are rejected. ``Path.resolve()`` follows every
    symlink (including the leaf, even a dangling one) before the containment
    check, so a symlink under the workdir that targets ``/etc`` is rejected —
    and the resolved path is what the tool then operates on, so it can't be
    followed afterwards either. See the trust model on :class:`AgentToolContext`.
    zpath z escapes workdir)r   rZ   is_absoluteresolverX   r   
ValueError)ctxp	candidaterootfulls        r7   r+   r+     s     QI
)"7"7"9  ""$$&D"..0IdY6FOOQD!!'$*=5%5677Kr9   c                  (    e Zd ZU dZded<   	 ded<   y)r*   u   Result of :meth:`BashSession.exec` — the captured output and exit code.

    A ``NamedTuple`` so it unpacks positionally (``out, code = await s.exec(...)``)
    and reads by name (``result.output`` / ``result.exit_code``) interchangeably.
    stroutputint	exit_codeN)r   r   r   r   r   rS   r9   r7   r*   r*     s     K? N8r9   r*   c                  V    e Zd ZdZd	dZeddd
d       Zedd       Ze	fddZ
ddZy)r)   u  A persistent ``/bin/bash`` process; cwd, env and jobs survive across calls.

    .. warning::
        :class:`BashSession` is **stateful and not safe to share concurrently**.
        Interleaved :meth:`exec` calls would race for the same stdin/stdout
        pipes (mixed input, output read by the wrong caller, and corrupted
        sentinel detection). Each :class:`AgentToolContext` creates its own
        session, so the safe pattern is *one context per session* — never a
        single ``AgentToolContext`` (or hand-constructed ``BashSession``) shared
        across multiple sessions running on different self-hosted environments.
        Holding the shared instance behind a per-call lock would serialize all
        bash work and is almost certainly not what you want.
    c                    || _         y)u   Use :meth:`BashSession.start` to construct — ``__init__`` takes an
        already-spawned process and is intended for internal use.N)_proc)rl   procs     r7   __init__zBashSession.__init__6  s     
r9   Nri   c          	        K   |t        |      n	t               }t        j                  g d|i |ddddt        j
                  t        j
                  t        j                         d {   } | |      S 7 w)N)z	/bin/bashz--noprofilez--norc dumb)PS1PS2TERM)cwdr^   stdinstdoutstderr)dictrB   anyioopen_process
subprocessPIPESTDOUT)clsrX   r^   baser   s        r7   rj   zBashSession.start;  sm     OtCy1B1D''2>4>2v>//??$$
 
 4y
s   A*A;,A9-A;c                2    | j                   j                  duS )u  Whether the underlying bash process has exited / been torn down.

        Inverse of "alive". Named ``closed`` (not ``alive``) to match the TS
        ``BashSession.closed`` boolean — porting code between the two SDKs
        should not have to flip the sense of this check.
        N)r   
returncoderk   s    r7   closedzBashSession.closedH  s     zz$$D00r9   c                  K   | j                   rt        d      | j                  j                  | j                  j                  J | j                  j                  }| j                  j                  dt        j                         j                   d}|d d  d|dd   }d| d| d}|j                  |j                                d {    t               d	|j                         dfd
}	 t        j                  |      5   |        d {    d d d        t"        j%                  dj'                  d            }	|	j)                  |      }
|
dk  rt+        |	j-                         d      S |	d |
 j/                  d      }|	|
t1        |      z   d  j-                         }	 |rt3        |j5                         d         nd}rd|z   }t+        ||      S 7 7 # 1 sw Y   xY w# t        $ rZ}t        j                  d      5  | j                          d {  7   d d d        n# 1 sw Y   nxY wt        d| d      |d }~wt        j                          $ rF t        j                  d      5  | j                          d {  7   d d d         # 1 sw Y    xY ww xY w# t6        $ r d}Y w xY ww)Nz)bash session terminated; restart required
__ANT_CMD__DONE__   z''z{ z
} </dev/null 2>&1; printf '\nz	%d\n' $?
Fc                    K   	 	 j                  d       d {   } | sy j                  |        t	              t
        kD  rd t	              t
        z
  = dv ry ]7 G# t        j                  $ r Y y w xY ww)NTi   )receiver   EndOfStreamextendlenBASH_OUTPUT_LIMIT)chunkbufmarkerr   	truncateds    r7   read_until_sentinelz-BashSession.exec.<locals>.read_until_sentinelf  s     "(.."66E 

5!s8// :c#h):::; $IS= 6(( s8   A=A$ A"A$ AA="A$ $A:7A=9A::A=Tshieldzbash command timed out after sr   replaceerrorsr   
z[output truncated]
r   )r   RuntimeErrorr   r   r   uuiduuid4hexsendencode	bytearrayr   
fail_afterTimeoutErrorCancelScopero   get_cancelled_exc_class_ANSIsubdecoderfindr*   striprstripr   r   
splitlinesr   )rl   cmdtimeoutr   sentinelsentinel_splitwrappedr   rN   textidxouttailcoder   r   r   r   s                 @@@@r7   execzBashSession.execR  s    ;;JKKzz+

0A0A0MMM

  ""  

 0 019$RaL>HQRL>: u=n=M[Yjj)***k	"	 	$	!!'* ,)+++,. yySZZyZ9:jj"7djjlB//4Cj%C#h-')*002	043t(+,"D (3.C#t$$y 	+4 ,, , 	R ""$/ #jjl""# # #!>wiqIJPQQ,,. 	 ""$/ #jjl""##	,  	D	s   CK
G')K
=G8 G,G*G,"G8 *B	K
4 J9 K
*G,,G51G8 8	J6IH:+H.,H:1	I:I	?I0J6J)JJ) 	J6)J2	.J66K
9KK
KK
c                  K   | j                   j                  Lt        j                  d      5  	 | j                   j                  j	                          d {    d d d        | j                   j                  Z| j                   j                          t        j                  d      5  | j                   j                          d {    d d d        t        j                  d      5  	 | j                   j	                          d {    d d d        y 7 # t
        $ r Y w xY w# 1 sw Y   xY w7 l# 1 sw Y   kxY w7 8# t
        $ r Y Aw xY w# 1 sw Y   y xY ww)NTr      )
r   r   r   r   acloserv   r   killmove_on_afterwaitrk   s    r7   ro   zBashSession.close  s'    ::'""$/ ****11333
 ::  (JJOO$$Q' (jjoo'''(d+ 	jj'')))	 	 4    (( ( * 	 	s   -E D)'DDDAE *D7D5	D7E +E-E
EE	E D	D&#D)%D&&D))D2.E 5D77E <E E	EEEEEE )r   zanyio.abc.Processr   r   )rX   rW   r^   r]   r   r)   )r   rY   )r   r   r   floatr   r*   r   )r   r   r   r   r   classmethodrj   propertyr   BASH_DEFAULT_TIMEOUTr   ro   rS   r9   r7   r)   r)   '  sI    
 `d 
 
 1 1 5I J%Xr9   r)   c                l     t         d fd       } t        dt              t        t        |            S )Nc                   K   ddfd	 d	 	 	 	 	 	 	 dfd} 	 |  j                          d{    yy7 # j                          d{  7   w w xY ww)z)Run a command in a persistent bash shell.Nc                 ~   K   3t         j                   j                   j                         d {   S 7 wrh   )r)   rj   rX   r^   )r   sessions   r7   _sessionz3beta_bash_tool.<locals>.bash_tool.<locals>._session  s7      + 1 1#++377 1 KKN Ls   1=;=c                x  K   |r,		j                          d {    d 	         d {    y| st        d      |r|dz  nt        }	          d {   }|j                  | |       d {   \  }}|dk7  rt        |      |S 7 p7 a7 87 # t        t
        f$ r}t        d|       |d }~ww xY ww)Nzbash session restartedzbash: command is requiredg     @@)r   zbash: r   )ro   r$   r   r   r   r   )
commandrestart
timeout_msr   r   r   r   rN   r   r   s
           r7   rm   z/beta_bash_tool.<locals>.bash_tool.<locals>.bash  s      &!--/))"Gj  / ;<<-7j6)=QG5"*$"#&&'&"BB	T qyn$J *  %B ,/ 5&-145sg   B:BB:BB:
B BB 2B3B :B:B:B B B7#B22B77B:r   )NNN)r   Optional[str]r   Optional[bool]r   zOptional[int]r   r   )ro   )rm   r   r   r   s    @@r7   	bash_toolz!beta_bash_tool.<locals>.bash_tool  s      '+	 hl	"	4B	Wd		,	&J"mmo%% #% "mmo%% #s1   A$A A$AA$A!AA!!A$rm   nameinput_schema)r   z,AsyncIterator[Callable[..., Awaitable[str]]])r   r&   r   r
   r   )r   r   s   ` r7   r-   r-     s>    ,& ,&f?C 3	 r9   c                >     t        dt              dd fd       }|S )Nreadr   c           	       K   	 t        |       }	 |j                         }t	        |j
                        st        d|  d      t        j                        }|.|j                  |kD  rt        d|  d|j                   d| d      |j                         }|s|S t        |      dk7  rt        d	      |\  }}|j                  d
      }	t        d|dz
        }
|dkD  r|n
t        |	      }d
j                  |	|
|       S # t        $ r}t        d|       |d}~ww xY w# t        $ r  t        $ r}t        d| |      |d}~ww xY ww)z,Read a file rooted at the working directory.zread: N: not a regular file is  bytes, exceeds z6-byte limit. Use bash (head/tail/sed) to read a slice.r  r   z/read: view_range must be [start_line, end_line]r   r   r   )r+   r   r$   statr   st_moder8   r`   st_size	read_textOSErrorrP   r   splitmaxjoin)rM   
view_rangetargetrN   stlimitr   
start_lineend_linelinesrj   endr   s               r7   r  zbeta_read_tool.<locals>.read  sp    	1!#y1F	9 B2::&&3G HII&s'9'9:E RZZ%%7YKtBJJ<7Gw O@ @  ##%D
 Kz?aMNN)
H

4 AzA~&"Q,hCJyyuS)**9  	1fQCL)q0	1   	 	9FIq1q8	9sG   ED B	D# A(E	D DD  E#E5EEErR   )rM   r   r  zOptional[List[int]]r   r   )r&   r"   )r   r  s   ` r7   r.   r.     s&    &/]^ + _ +D Kr9   c                <     t        dt              d fd       }|S )Nwriter   c                $  K   	 t        |       }	 |j                  j	                  dd       |j                  |       dt        |       d|  S # t        $ r}t        d|       |d}~ww xY w# t        $ r}t        d| |      |d}~ww xY ww)z4Write a file, creating parent directories as needed.zwrite: NT)parentsexist_okr  zwrote z
 bytes to )	r+   r   r$   parentmkdir
write_textr
  rP   r   )rM   contentr  rN   r   s       r7   r  zbeta_write_tool.<locals>.write  s     	2!#y1F	:MMt<g& G~Z	{;;  	2gaSM*1	2
  	:GY29	:sE   BA .A1  B	A.A))A..B1	B:BBB)rM   r   r  r   r   r   )r&   r#   )r   r  s   ` r7   r/   r/     s%    '0_`< a< Lr9   c                >     t        dt              dd fd       }|S )Neditr   c           	       K   	 t        |       }	 |j                         }t	        |j
                        st        d|  d      t        j                        }|.|j                  |kD  rt        d|  d|j                   d| d      |j                         }|j                  |      }	|	dk(  rt        d	|        |s|	d
kD  rt        d|	 d|  d      |r|j                  ||      n|j                  ||d
      }
	 |j                  |
       d|  d|r|	 dS d
 dS # t        $ r}t        d|       |d}~ww xY w# t        $ r  t        $ r}t        d| |      |d}~ww xY w# t        $ r}t        d| |      |d}~ww xY ww)z-Replace text in a file by exact string match.zedit: Nr  r  r  z5-byte limit. Use bash (sed/awk) to edit a large file.r   r   zedit: old_string not found in r   zedit: old_string appears z
 times in z (must be unique)zedited z (z replacement(s)))r+   r   r$   r  r   r  r8   r`   r  r	  r
  rP   countr   r  )rM   
old_string
new_stringreplace_allr  rN   r  r  r   r"  updatedr   s              r7   r   zbeta_edit_tool.<locals>.edit%  s    	1!#y1F	9
 B2::&&3G HII&s'9'9:E RZZ%%7YKtBJJ<7Gw O? ?  ##%D
 

:&A:<YKHIIuqy7wjSdeff:E$,,z:64<<XbdnpqKr	9g& 2{e%BBRSS%BBRSS?  	1fQCL)q0	1"  	 	9FIq1q8	9  	9FIq1q8	9sk   FD! B	E A$F E) F!	D>*D99D>>FE&E!!E&&F)	F2F  FFrR   )
rM   r   r#  r   r$  r   r%  r   r   r   )r&   r   )r   r   s   ` r7   r0   r0   $  s(    &/]^#T _#TJ Kr9   c                V    	 | j                         j                  S # t        $ r Y yw xY w)Ng        )r  st_mtimer
  )r   s    r7   _mtime_or_zeror)  N  s+    vvx    s    	((c                >     t        dt              dd fd       }|S )Nglobr   c                >  K   d}t        |       j                         r4j                  st        d      t        d      }| j	                  d      }nqj                  s"dt        |       j                  v rt        d      |r	 t        |      }n#t        j                        j                         }| }j                  s|}	 t        t        |j                  |      t                    }|*|D cg c]  }t!        |j                         |      s|! }}|sy|j#                  t$        d	       d
j'                  d |dt(         D              S # t        $ r}t        d|       |d}~ww xY w# t        t        f$ r}t        d|       |d}~ww xY wc c}w w)z1List files matching a glob pattern, newest first.Nz$glob: absolute pattern not permitted/z..z*glob: '..' is not permitted in the patternzglob: 
no matchesT)keyreverser   c              3  2   K   | ]  }t        |        y wrR   )r   ).0ms     r7   	<genexpr>z/beta_glob_tool.<locals>.glob.<locals>.<genexpr>}  s     EAQEs   )r   r   rZ   r$   lstripr   partsr+   r   rX   r   listr   r+  WALK_MAX_ENTRIESr
  r   sortr)  r  GLOB_RESULT_LIMIT)	patternpathconfiner   patrN   matchesr3  r   s	           r7   r+  zbeta_glob_tool.<locals>.globV  s     #'=$$&)) FGG9D..%C
 ))dmG6L6R6R.R LMM9'T2D CKK(002C))	1 6$))C.2BCDG  #*KQWQYY['-JqKGK6yyE1C2C)DEEE+ " 9#fQCL1q89 G$ 	1fQCL)q0	1 Ls`   B FE 4F(E2 -F3FF;F	E/E**E//F2FFFFrR   r;  r   r<  r   r   r   )r&   r    )r   r+  s   ` r7   r1   r1   U  s(    &/]^&F _&FP Kr9   c                >     t        dt              dd fd       }|S )Ngrepr   c                  K   	 |rt        |      n"t        j                        j                         }t        j                  d      x}rt        j                  |ddd| dt        |      gd	       d{   }|j                  d
k(  ry|j                  dk7  r(t        d|j                  j                  d             |j                  j                  d      }t        |      t         kD  r|dt          dt          dz   }|S 	 t#        j$                  |       }t)        ||      S # t        $ r}t        d|       |d}~ww xY w7 # t"        j&                  $ r}t        d|       |d}~ww xY ww)z.Search file contents for a regular expression.zgrep: Nrgz-nz--no-headingz-ez--F)checkr   r.  r   zgrep: rg failed: r   r   z
[output truncated at  bytes]zgrep: invalid regex: )r+   r   rX   r   r   r$   rt   whichr   run_processr   r   r   r   r   r   GREP_OUTPUT_LIMITrecompileerror
_walk_grep)	r;  r<  searchrN   rD  resultr   rxr   s	           r7   rB  zbeta_grep_tool.<locals>.grep  s~    	104\#t,$s{{:K:S:S:UF d##2# !,,T>4$FL F   A%#  A%"3FMM4H4HPY4H4Z3[ \]]--&&i&8C3x++,,-2IJ[I\\c0ddJ	@G$B "f%%/  	1fQCL)q0	1 xx 	@3A378a?	@sY   E51D) AE57E	8BE5E E5)	E2EEE5E2E--E22E5rR   r@  )r&   r!   )r   rB  s   ` r7   r2   r2     s%    &/]^& _&: Kr9   c                   
 g t         
d
fdd	 fd}|j                         r	 ||       nd}t        j                  |      D ]  \  }}}|D cg c]&  }|dvst	        |      |z  j                         r%|( c}|d d  |D ]P  }t	        |      |z  }	|	j                         r"|dz  }|t        kD  s	 ||	      r9rdj                        ndc c S   rdj                        S dS c c}w )
Nc                    t        |       dz   z  dk  rj                  dt         d       yj                  |        y)Nr   r   z[output truncated at rF  FT)r   appendrI  )linebudgethitss    r7   pushz_walk_grep.<locals>.push  sF    #d)a-A:KK/0A/B'JKDr9   c           	        	 | j                  d      5 }d|j                  d      v r
	 d d d        y|j                  d       t        |d      D ]e  \  }}t	        |      t
        kD  r|j                  d      j                  d	      }j                  |      sL |  d
| d
|       r] d d d        y 	 d d d        y# 1 sw Y   yxY w# t        $ r Y yw xY w)Nrb    i   Tr   r   r   r   z
:F)
openr  seek	enumerater   GREP_MAX_LINE_LENGTHr   r   rN  r
  )r   firawrT  rW  rP  s        r7   scanz_walk_grep.<locals>.scan  s    	4 %AaffSk)% % q	'1o %FAs 3x"66 ::Y:7>>vFDyyttfAaS$4H/I$% %%	% %   		sQ   C CC A(CC,C-C 6C8C C
C 
C 	CCr   )z.gitnode_modulesr   r   r.  )rT  r   r   rY   )r   r   r   rY   )rI  is_filer<   walkr   
is_symlinkr8  r  )rP  rN  rc  seendirpathdirnames	filenamesdr   r   rU  rV  rW  s   `         @@@r7   rM  rM    s    DF& ~~V,.GGFO 	E(GXy $q0H'HRVW^R_bcRcQoQoQqHQK " EG}t+ ??$	**$t*.2499T?DE	E #499T?44s   	C3 C3=C3c                ~    t        |       t        |       t        |       t        |       t	        |       t        |       gS )u  Return the ``agent_toolset_20260401`` implementations bound to ``ctx``.

    The result is a plain list of :class:`~anthropic.lib.tools.BetaAsyncFunctionTool`
    instances — *async* function tools, so it is for the **async** runners only:
    the ``AsyncAnthropic`` ``client.beta.messages.tool_runner`` and
    ``client.beta.sessions.events.tool_runner`` (always async). The sync
    ``Anthropic`` ``messages.tool_runner`` takes ``BetaRunnableTool``, which
    excludes the async function tools this returns. Filter or extend it before
    passing it on::

        tools = [*beta_agent_toolset_20260401(ctx), my_custom_tool]
        tools = [t for t in beta_agent_toolset_20260401(ctx) if t.name != "grep"]
    )r-   r.   r/   r0   r1   r2   )r   s    r7   r,   r,     s>     	sssss r9   )r6   r_   r   z
int | None)r   zdict[str, str])rL   r   rM   r   rN   r
  r   r$   )r   re   )r   r(   r   r   r   r   )r   r(   r   zBetaAsyncFunctionTool[Any])r   r   r   r   )rP  zre.Pattern[str]rN  r   r   r   )r   r(   r   z list[BetaAsyncFunctionTool[Any]])Zr   
__future__r   r<   rJ  r   rt   loggingr   r  r   typingr   r   r   r   r	   r
   pathlibr   r   	functoolsr   	itertoolsr   
contextlibr   dataclassesr   r   collections.abcr   r   r   r   r   	anyio.abcanyio.to_threadr   _skillsr   r   _typesr   r   _utilsr   
types.betar   r   r    r!   r"   r#   _beta_functionsr$   r%   r&   _clientr'   __all__r   r   r5   READ_MAX_BYTESrI  r_  r:  r8  rK  r   r8   	getLoggerrw   rB   rP   rT   r(   r+   r*   r)   r-   r.   r/   r0   r)  r1   r2   rM  r,   rS   r9   r7   <module>r     sM  B # 	 	      G G '   * ( G G   $ 5 )   O N)   # '    

/0J g;<S"5* g) g) g)T*8 8 F FR8v$N$'T*ZD35lr9   