
    Áj                    &   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	Z	ddl
Z
ddlmZ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%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/m0Z0m1Z1 ddl2m3Z3 ddl4m5Z5m6Z6m7Z7m8Z8m9Z9m:Z: ddl;m<Z<m=Z=m>Z>m?Z?m@Z@ erddlAmBZB g dZCdZDdZEdZFeFZGdZHdZIeHZJeIZKddddddd ZLdZMd!ZNd"ZOd#ZP ej                  d$      ZReFfd@d%ZS e	j                  d&      ZUdAd'ZVdBd(ZWdCd)ZXdDd*ZYe  G d+ d,             ZZd-Z[ej                  ej                  fZ^dEd.Z_dFd/Z`dGd0Za G d1 d2e      Zb G d3 d4      ZcdHd5ZddId6ZedHd7ZfdHd8ZgdHd9ZhdJd:ZidKd;ZjdHd<ZkdHd=ZldLd>ZmdMd?Zny)Nu>  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_ISLNKS_ISREG)TYPE_CHECKINGAnyListOptional
NamedTuplecast)PathPurePosixPath)partial)islice)asynccontextmanager)field	dataclass)MappingCallableIterableIterator	AwaitableAsyncIterator)run_sync   )_withindownload_session_skills   )NotGiven	not_givenis_given).BetaManagedAgentsAgentToolset20260401BashInput.BetaManagedAgentsAgentToolset20260401EditInput.BetaManagedAgentsAgentToolset20260401GlobInput.BetaManagedAgentsAgentToolset20260401GrepInput.BetaManagedAgentsAgentToolset20260401ReadInput/BetaManagedAgentsAgentToolset20260401WriteInput)	ToolErrorBetaContentBetaAsyncFunctionToolBetaFunctionToolResultType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  P i  @z	image/pngz
image/jpegz	image/gifz
image/webpapplication/pdf)z.pngz.jpgz.jpegz.gifz.webpz.pdfi     iP  z\x1b\[[0-9;?]*[ -/]*[@-~]c                     t        |       r| S |S )u   Resolve a configured cap to an effective size limit.

    ``not_given`` selects ``default``; ``None`` disables the size check
    (uncapped); a positive int is the cap. Governs only the size guard — callers
    still reject non-regular files.
    r    )
configureddefaults     H/tmp/pip-target-k_scjv0h/lib/python/anthropic/lib/tools/agent_toolset.py_resolve_max_bytesr?      s     "*-::7:    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     r>   _default_bash_envrI      s8      ZZ--/RTQq||L7QAqDRRRs
   AAc                $   t        | t              ryt        | t              ryt        | t              ryt        | t              ryt        | t
              ry| j                  t        j                  k(  ry| j                  xs dj                         S )a2  Map a filesystem ``OSError`` to a consistent, runtime-independent phrase.

    The raw ``OSError`` string is platform-specific (``[Errno 2] ENOENT: ...``)
    and can embed a host path; normalise the common cases so the model sees the
    same wording everywhere and never the runner's absolute paths.
    zno such file or directoryznot a directoryzis a directoryzpermission deniedzfile already existsz!too many levels of symbolic linksz	i/o error)

isinstanceFileNotFoundErrorNotADirectoryErrorIsADirectoryErrorPermissionErrorFileExistsErrorerrnoELOOPstrerrorlower)es    r>   
_fs_reasonrV      ss     !&'*!'( !&'!_%"!_%$ww%++2JJ%+,,..r@   c           	     :    t        |  d| dt        |             S )N: )r(   rV   )op	file_pathrU   s      r>   	_fs_errorr[      s"    t2i[:a=/:;;r@   c                     g S N r^   r@   r>   _empty_skill_dirsr_      s    Ir@   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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.   uO  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. Image/PDF files,
            which ``read`` returns as base64 content blocks, are not subject to
            the 256 KiB default (``max_image_base64_bytes`` /
            ``max_pdf_bytes`` govern instead), but an explicit positive cap
            binds them too.
        max_image_base64_bytes: Cap on the *base64-encoded* size of an image
            ``read`` returns as a content block. ``not_given`` (default)
            uses the built-in 5 MiB cap — a memory bound plus the API's
            per-image limit; a positive int overrides it; ``None`` disables it
            (only ``max_file_bytes`` / the API's own limit then apply).
        max_pdf_bytes: Cap on the raw size of a PDF ``read`` returns as a
            document block. ``not_given`` (default) uses the built-in 20 MiB
            cap; a positive int overrides it; ``None`` disables it.
    )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max_image_base64_bytesmax_pdf_bytes)r=   initreprzBashSession | None_bash)ra   rn   ro   
list[Path]_skill_dirsc                   K   | j                   8t        j                  | j                  | j                         d {   | _         | j                   S 7 wNri   )rp   r/   startrc   ri   selfs    r>   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r]   )rp   closerw   s    r>   r{   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)rg   rc   )rf   rg   r   rc   rr   rw   s    r>   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)rr   r   r   shutilrmtree	Exceptionlogwarning)rx   	skill_dirrU   s      r>   _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r]   )r}   rw   s    r>   
__aenter__zAgentToolContext.__aenter__,  s"     !!! 	"s   c                   K   	 | j                          d {    | j                          d {    y 7 7 # | j                          d {  7   w xY wwr]   )r{   r   )rx   excs     r>   	__aexit__zAgentToolContext.__aexit__0  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   rC   getcwdrc   __annotations__re   rf   rg   ri   r   rk   rl   rm   rp   r_   rr   ry   r{   r}   r   r   r   r^   r@   r>   r.   r.      s    4r ',BII&FG#F$$ %)F!(!J
!'+C	$+,5N)54=1=+4M(4 %dU KEK $4EEX]^K^

	x)r@   r.   (   c                 x    t        t        j                  t        j                  t        j                              S r]   )OSErrorrQ   rR   rC   rS   r^   r@   r>   _symlink_loop_errorr   ;  s     5;;EKK 899r@   c           
     P   t        t        j                  j                  t	        |                   }g }d}	 	  |j                  d      j                  t        |       S # t        $ r t               dt        $ r}|j                  t        vr |}Y d}~nd}~ww xY w	 t        t        j                  |      j                        }n_# t        $ rS}|j                  t        vs|j                   |k(  r |j#                  |j$                         |j                   }Y d}~d}~ww xY w|s||dz  }|t&        kD  r
t               t        t        j                  j                  t        j                  j)                  |j                   t        j*                  |                        }q)u>  Return ``path`` with ``.``/``..`` collapsed lexically and every symlink
    followed, or raise ``OSError`` — the same outcome on every supported Python,
    and never a partly resolved path.

    Trailing components that do not exist yet are kept as spelled, so a new
    file under new directories still canonicalises; a dangling symlink met on
    the way is read and followed by hand, and more than ``_MAX_SYMLINK_HOPS``
    such hops count as a loop. Non-strict ``Path.resolve()`` is avoided because
    its symlink-loop handling differs between CPython versions.
    r   T)strictNr   )r   rC   pathnormpathstrresolvejoinpathreversedRuntimeErrorr   r   rQ   _MISSING_ERRNOSr   lstatst_modeparentappendname_MAX_SYMLINK_HOPSjoinreadlink)r   prefixmissing_tailhopsrU   
unresolved
is_symlinks          r>   _canonicalizer   ?  sS    "''""3t9-.F LD
	76>>>.77,9OPP 	2%'T1 	wwo-J		 &!1!9!9:J 	wwo-&1H,]]F	 	##%''bgg&&rww||FMM2;;vCV'WXY/ s0   'A   B<BB(C 	D$A	DD$c           	     J   t        |      }t        | j                        j                         }	 t        |j	                         r|n||z        }| j                  st        ||      st        d|d      |S # t
        $ r }t        d|dt        |             |d}~ww xY w)u"  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. ``.`` and ``..`` components are
    collapsed lexically first; then every symlink (including the leaf, even a
    dangling one) is followed 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.
    A symlink loop or an unreadable component rejects the path outright rather
    than falling back to an unresolved path. See the trust model on
    :class:`AgentToolContext`.
    zpath rX   Nz escapes workdir)
r   rc   r   r   is_absoluter   
ValueErrorrV   re   r   )ctxp	candidaterootfullrU   s         r>   r1   r1   g  s     QI$$&D@)*?*?*AYtiGWX !!'$*=5%5677K	  @5R
178a?@s    A9 9	B"BB"c                  (    e Zd ZU dZded<   	 ded<   y)r0   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.
    r   outputint	exit_codeN)r   r   r   r   r   r^   r@   r>   r0   r0     s     K? N8r@   r0   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)rx   procs     r>   __init__zBashSession.__init__  s     
r@   Nru   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)cwdri   stdinstdoutstderr)dictrI   anyioopen_process
subprocessPIPESTDOUT)clsrc   ri   baser   s        r>   rv   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   
returncoderw   s    r>   closedzBashSession.closed  s     zz$$D00r@   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    r>   read_until_sentinelz-BashSession.exec.<locals>.read_until_sentinel  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   r   r   r   r   uuiduuid4hexsendencode	bytearrayr   
fail_afterTimeoutErrorCancelScoper{   get_cancelled_exc_class_ANSIsubdecoderfindr0   striprstripr   r   
splitlinesr   )rx   cmdtimeoutr   sentinelsentinel_splitwrappedr   rU   textidxouttailcoder   r   r   r   s                 @@@@r>   execzBashSession.exec  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   acloser   r   killmove_on_afterwaitrw   s    r>   r{   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   )rc   rb   ri   rh   r   r/   )r   rd   )r  r   r  floatr   r0   r   )r   r   r   r   r   classmethodrv   propertyr   BASH_DEFAULT_TIMEOUTr  r{   r^   r@   r>   r/   r/     sI    
 `d 
 
 1 1 5I J%Xr@   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rt   )r/   rv   rc   ri   )r   sessions   r>   _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   )r{   r(   r  r  r   r   )
commandrestart
timeout_msr  r   r  r
  rU   r  r  s
           r>   ry   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   )r{   )ry   r  r  r   s    @@r>   	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$ry   r   input_schema)r   z,AsyncIterator[Callable[..., Awaitable[str]]])r   r,   r"   r   r   )r   r!  s   ` r>   r3   r3     s>    ,& ,&f?C 3	 r@   c           	        |dk(  rt        |j                  t              }n&t        |j                  t              }||dz  dz  nd}t        |j                        r0|j                  $||j                  nt        ||j                        }|||kD  rt        d| d| d| d      t        j                  | j                               j                  d	      }|dk(  r	d
dd|ddS ddt        t        |      |ddS )u  Read an image/PDF as a base64 ``image``/``document`` content block.

    The text cap does not apply here — its 256 KiB default would reject most
    real images. Instead the media caps (``max_image_base64_bytes`` /
    ``max_pdf_bytes``, defaulting to the API's own limits) govern, checked
    against the stat size before opening (same OOM rationale as the text path)
    and tightened by an *explicitly* configured ``max_file_bytes`` — an
    explicit cap is a memory bound and binds every read.
    r9   N   r   read:  is  bytes, exceeds z -byte limit for image/PDF files.asciidocumentbase64)type
media_typedata)r,  sourceimage)r?   rm   DEFAULT_MAX_PDF_BYTESrl   DEFAULT_MAX_IMAGE_BASE64_BYTESr!   rk   minr(   r+  standard_b64encode
read_bytesr   r   r   )targetrZ   sizer-  r   limitb64_capr.  s           r>   _read_binary_blockr:  T  s    &&"3#4#46KL$S%?%?A_`&-&9A"t""#(:(:(F&+m""UCDVDV9WTE\&4v5EeWLlmnn$$V%6%6%89@@ID&&"xO`jn.opp#4Z3HRVW r@   c                >     t        dt              dd fd       }|S )Nreadr"  c           	       K   	 t        |       }	 |j                         }t	        |j
                        st        d|  d      t        j                  |j                  j                               }|'|rt        d      t        || |j                  |      gS t        j                        }|.|j                  |kD  rt        d|  d|j                   d| d      |j                  d	      }|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
      |d}~wt         $ r}t#        d| |      |d}~ww xY ww)z,Read a file rooted at the working directory.r&  N: not a regular filez5read: view_range is not supported for image/PDF filesr'  r(  z6-byte limit. Use bash (head/tail/sed) to read a slice.utf-8encodingzQ: not valid UTF-8 text (binary files are only supported for image/PDF extensions)r<  r  z/read: view_range must be [start_line, end_line]r   r   r   )r1   r   r(   statr   r   _BINARY_MEDIA_TYPESgetsuffixrT   r:  st_sizer?   rk   	read_textUnicodeDecodeErrorr   r[   r   splitmaxr   )rZ   
view_ranger6  rU   str-  r8  r  
start_lineend_linelinesrv   endr   s                r>   r<  zbeta_read_tool.<locals>.readr  s    	1!#y1F	9 B2::&&3G HII,001D1D1FGJ% #$[\\*69bjj*VYZ[[&s'9'9:E RZZ%%7YKtBJJ<7Gw O@ @  ##W#5D Kz?aMNN)
H

4 AzA~&"Q,hCJyyuS)**S  	1fQCL)q0	12  	! 	#tu  	9FIq1q8	9s`   F?E B	E; F?AE; 3A(F?	E8$E33E88F?;F<FF<)F77F<<F?r]   )rZ   r   rK  zOptional[List[int]]r   r+   )r,   r&   )r   r<  s   ` r>   r4   r4   q  s&    &/]^-+ _-+^ Kr@   c                <     t        dt              d fd       }|S )Nwriter"  c                (  K   	 t        |       }	 |j                  j	                  dd       |j                  |d       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?  r@  rR  zwrote z
 bytes to )	r1   r   r(   r   mkdir
write_textr   r[   r   )rZ   contentr6  rU   r   s       r>   rR  zbeta_write_tool.<locals>.write  s     	2!#y1F	:MMt<g8 G~Z	{;;  	2gaSM*1	2
  	:GY29	:sE   BA 0A3 B	A0A++A00B3	B<B

BB)rZ   r   rX  r   r   r   )r,   r'   )r   rR  s   ` r>   r5   r5     s%    '0_`< a< Lr@   c                >     t        dt              dd fd       }|S )Neditr"  c           	     T  K   	 t        |       }	 |j                         }t	        |j
                        st        d|  d      t        j                        }|.|j                  |kD  rt        d|  d|j                   d| d      |j                  d      }|j                  |      }	|	dk(  rt        d|        |s|	dkD  rt        d|	 d|  d      |r|j                  ||      n|j                  ||d      }
	 |j                  |
d       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	      |d}~w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@  z1: not valid UTF-8 text (cannot edit binary files)rZ  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)))r1   r   r(   rB  r   r   r?   rk   rF  rG  rH  r   r[   countr   rW  )rZ   
old_string
new_stringreplace_allr6  rU   rL  r8  r  r\  updatedr   s              r>   rZ  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? ?  ##W#5D 

:&A:<YKHIIuqy7wjSdeff:E$,,z:64<<XbdnpqKr	9g8 2{e%BBRSS%BBRSSC  	1fQCL)q0	1"  	! 	jfYK/`abhii 	9FIq1q8	9  	9FIq1q8	9sw   F(D% BE A$F(F	 F(%	E.D==EF(FE''F3FFF(		F%F  F%%F(r]   )
rZ   r   r]  r   r^  r   r_  r   r   r   )r,   r#   )r   rZ  s   ` r>   r6   r6     s(    &/]^%T _%TN Kr@   c                V    	 | j                         j                  S # t        $ r Y yw xY w)Ng        )rB  st_mtimer   )r   s    r>   _mtime_or_zerorc    s+    vvx    s    	((c              #  r   K   | D ]  }	 t        |      }t        ||      s| ! y# t        $ r Y .w xY ww)zYield the matches whose canonical path is inside ``root``; a match that
    cannot be canonicalised (symlink loop, unreadable) is dropped, not raised.N)r   r   r   )matchesr   match	canonicals       r>   	_confinedrh    sJ       	%e,I 9d#K  		s    7(77	4747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                    }|t        t!        ||            }|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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r]   )r   ).0ms     r>   	<genexpr>z/beta_glob_tool.<locals>.glob.<locals>.<genexpr>  s     EAQEs   )r   r   re   r(   lstripr   partsr1   r   rc   r   listr   rj  WALK_MAX_ENTRIESr   rh  sortrc  r   GLOB_RESULT_LIMIT)patternr   confiner   patrU   re  r   s          r>   rj  zbeta_glob_tool.<locals>.glob  sp     #'=$$&)) FGG9D..%C
 ))dmG6L6R6R.R LMM9'T2D CKK(002C))	1 6$))C.2BCDG  9Wg67G6yyE1C2C)DEEE+ " 9#fQCL1q89 G$ 	1fQCL)q0	1sO   B FD= 4F(E -AF=	EEEFF ,E;;F  Fr]   rz  r   r   r  r   r   )r,   r$   )r   rj  s   ` r>   r7   r7     s(    &/]^&F _&FP Kr@   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   rm  r   zgrep: rg failed: r   r   z
[output truncated at  bytes]zgrep: invalid regex: )r1   r   rc   r   r   r(   r   whichr   run_processr   r   r   r   r   r   GREP_OUTPUT_LIMITrecompileerror
_walk_grep)	rz  r   searchrU   r  resultr  rxr   s	           r>   r  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r]   r}  )r,   r%   )r   r  s   ` r>   r8   r8   #  s%    &/]^& _&: Kr@   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 r  FT)r   r   r  )linebudgethitss    r>   pushz_walk_grep.<locals>.pushI  sF    #d)a-A:KK/0A/B'JKDr@   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   r  r   )r   firawr  r  r  s        r>   scanz_walk_grep.<locals>.scanR  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   rm  )r  r   r   rd   )r   r   r   rd   )r  is_filerC   walkr   r   rw  r   )r  r  r  seendirpathdirnames	filenamesdr   r   r  r  r  s   `         @@@r>   r  r  E  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"]
    )r3   r4   r5   r6   r7   r8   )r   s    r>   r2   r2   {  s>     	sssss r@   )r<   rj   r=   r   r   z
int | None)r   zdict[str, str])rU   r   r   r   )rY   r   rZ   r   rU   r   r   r(   )r   rq   )r   r   )r   r   r   r   )r   r.   r   r   r   r   )r   r.   r   zBetaAsyncFunctionTool[Any])r6  r   rZ   r   r7  r   r-  r   r   r.   r   r)   )r   r   r   r  )re  zIterable[Path]r   r   r   zIterator[Path])r  zre.Pattern[str]r  r   r   r   )r   r.   r   z list[BetaAsyncFunctionTool[Any]])or   
__future__r   rC   r  r   rQ   r+  r   loggingr   rB  r   r   typingr   r   r   r	   r
   r   pathlibr   r   	functoolsr   	itertoolsr   
contextlibr   dataclassesr   r   collections.abcr   r   r   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*   r+   r,   _clientr-   __all__r   r  DEFAULT_MAX_FILE_BYTESREAD_MAX_BYTESr2  r1  READ_IMAGE_MAX_BASE64_BYTESREAD_PDF_MAX_BYTESrC  r  r  ry  rw  r  r   r?   	getLoggerr   rI   rV   r[   r_   r.   r   ENOENTENOTDIRr   r   r   r1   r0   r/   r3   r:  r4   r5   r6   rc  rh  r7   r8   r  r2   r^   r@   r>   <module>r     s  B # 	 	       ! G G '   * ( [ [   $ 5 )    )   # ' "1 ( < *        

/0 J` ; g;<S"/,< u) u) u)p  <</:%ZP28 8 F FR8v:1h$)X	*ZD35lr@   