
    4RjrZ                       U d Z ddlmZ ddlZddlZddlZddlmZmZm	Z	 ddl
mZ ddlZddlmZ dd	lmZmZmZ d
dl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 erd
dl m!Z! ddl"m#Z# d
dl$m%Z% ddgZ& ejN                  e(      Z)dZ*dZ+dZ,eee   e	dgee   f   f   Z-g ee.Z/de0d<   dd	 	 	 	 	 	 	 	 	 	 	 ddZ1ddZ2 G d d      Z3y) u  The self-hosted environment worker — the full composition of the
control-plane poller and the per-session tool runner.

:class:`EnvironmentWorker` claims work items from a self-hosted environment, and
for each claimed ``session`` work item: builds the per-session
:class:`~anthropic.lib.tools.agent_toolset.AgentToolContext` and downloads the
session agent's skills, then runs a
:class:`~anthropic.lib.tools._beta_session_runner.SessionToolRunner` for the
session *while* heartbeating the work-item lease in parallel; on exit it
force-stops the work item and loops to the next one. The lease heartbeat
reporting ``state == "stopping"`` (or a lost lease) ends the session run.

Build one from the generated work resource::

    client.beta.environments.work.worker(environment_id=..., environment_key=...)

or construct it directly::

    from anthropic.lib.environments import EnvironmentWorker

    EnvironmentWorker(client, environment_id=..., environment_key=...)

:meth:`EnvironmentWorker.handle_item` runs that same per-work-item flow for a
single work item you've already claimed (e.g. a ``worker poll --on-work`` script
handed one to a fresh process); with no arguments it reads the ``ANTHROPIC_*``
env vars that command sets.
    )annotationsN)TYPE_CHECKINGUnionCallable)Sequence   )TRANSIENT_ERRORS   )
_is_status
aiter_work_is_fatal_4xx   )HeadersNotGiven	not_given)_copy_client_with_bearer_auth)BetaSelfHostedWorkBetaSessionWorkData)DEFAULT_MAX_IDLEBetaAnyRunnableTool_run_session_tools)AsyncAnthropicAgentToolContext)	AsyncWorkEnvironmentWorkerEnvironmentWorkerToolsg      >@g     V@NO_HEARTBEATr   ztuple[type[Exception], ...]_HEARTBEAT_TRANSIENT_ERRORS)extra_headersc                 K   t         }t        }t        }t        j                         }|j                         s=	 t        j                  |      5  | j                  ||||       d{   }	ddd       	j                  }t        j                         }|	j                  dkD  r3|	j                  }t        dt        |	j                  dz  t                     }|	j                  dv s|	j                  s<t        j!                  d|	j                  |	j                         |j#                          yt        j,                  |      5  |j/                          d{    ddd       |j                         s<yy7 # 1 sw Y   xY w# t$        $ r}
t'        |
      r+t        j)                  d|
       |j#                          Y d}
~
yt        j                         |z
  |kD  r,t        j)                  d	||
       |j#                          Y d}
~
yt        j+                  d
|
       Y d}
~
d}
~
ww xY w7 # 1 sw Y   xY ww)a  Keep the work-item lease alive while a session is being served.

    ``work`` must be bound to a sub-client authenticated for the environment;
    this loop adds no auth of its own. Sets ``stop`` when the control plane
    reports the work is ``stopping`` / ``stopped``, when the lease is no
    longer extended, on a permanent heartbeat failure, or when transient
    failures have run long enough that the lease must be assumed lost (so two
    runners don't end up serving the same work).
    )environment_idexpected_last_heartbeatr    Nr   g      ?r   )stoppingstoppedz5heartbeat signals shutdown state=%s lease_extended=%sz$permanent heartbeat failure error=%sz=lease assumed lost: no successful heartbeat in %.0fs error=%sz$transient heartbeat failure error=%s)_HEARTBEAT_DEFAULT_HEARTBEAT_TTL_DEFAULT_NO_HEARTBEAT_SENTINELtime	monotonicis_setanyio
fail_after	heartbeatlast_heartbeatttl_secondsmaxminstatelease_extendedloginfosetr   r   errorwarningmove_on_afterwait)workwork_idr"   stopr    intervalttllastlast_successrespes              j/var/www/html/meridian/meridian-ai/venv/lib/python3.12/site-packages/anthropic/lib/environments/_worker.py_heartbeat_looprF   Z   s    " "H
 C!D>>#Lkkm$	 !!(+ !^^#1,0"/	 ,  0 &&D>>+L!#&&sC(8(81(<>P$QRzz44D<O<OPRVR\R\^b^q^qr
  * 	))+	O kkmm  + 	CQ		@!D
 ~~,.4		Y[^`ab
KK>BB	C2 	 	s   8H>F E;)E8*E;.F 6CH>H2H0H2H>6H>8E;;F F 	H-1H(H>AH(H>H(#H>(H--H>0H22H;7H>c               r    | xs t         j                  j                  |      }|st        d| d|       |S )zFall back to ``env_var`` for ``value``; raise a clear error if still empty.

    The ``ANTHROPIC_*`` env vars are the ones the ``ant worker poll --on-work``
    command sets on the process it spawns for a claimed work item.
    zhandle_item: u     is required — pass it or set )osenvironget
ValueError)valuenameenv_varresolveds       rE   _requirerP      s<     /

w/H=.NwiXYYO    c            
          e Zd ZdZdddddeeddd		 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 	 ddZddZddZddddd	 	 	 	 	 	 	 	 	 dd	Z	dd
Z
y)r   u#  Run a self-hosted environment worker.

    Composed from the control-plane poller (``client.beta.environments.work.poller``)
    and the per-session :class:`SessionToolRunner`. For each claimed ``session``
    work item it builds the per-session :class:`AgentToolContext` and downloads
    the session agent's skills, then runs a session tool runner for the session
    *while* heartbeating the work-item lease in parallel; on exit it force-stops
    the work item and loops to the next one.

    A single ``environment_key`` is the worker's only credential: a Bearer-only
    scoped sub-client is built once per call (one for polling, one for
    heartbeat / force-stop, and the session tool runner builds its own
    internally), so every request the worker issues is authenticated by the
    environment key with the parent client's ``X-Api-Key`` cleared.

    Async only — :meth:`run` loops forever, so bound it (cancel the task or wrap
    it in :func:`asyncio.wait_for`) when you want it to stop.

    Use :meth:`handle_item` if you already hold a claimed work item (e.g. a
    ``worker poll --on-work`` script handed one to a fresh process) and just
    want the per-item flow without the poll loop — with no arguments it reads the
    ``ANTHROPIC_*`` env vars that command sets, so ``environment_id`` (only used
    by :meth:`run`) isn't needed.

    Prefer ``client.beta.environments.work.worker(...)`` to build one; the direct
    constructor below is equivalent.

    Example::

        from anthropic import AsyncAnthropic

        client = AsyncAnthropic()

        # Long-running daemon: poll for work, serve each session, loop.
        await client.beta.environments.work.worker(
            environment_id=environment_id,
            environment_key=environment_key,
            workdir="/workspace",
        ).run()

        # Already-claimed item (e.g. inside `ant worker poll --on-work ...`):
        await client.beta.environments.work.worker(workdir="/workspace").handle_item()

        # Equivalent, constructing the worker directly:
        from anthropic.lib.environments import EnvironmentWorker

        await EnvironmentWorker(client, workdir="/workspace").handle_item()

    Args:
      client: The async Anthropic client.
      environment_id: The self-hosted environment to poll for work. Required by
        :meth:`run`; not used by :meth:`handle_item`.
      environment_key: The environment key — the worker's single credential.
        Used as the Bearer credential on the scoped sub-clients the worker
        constructs for the control-plane (poll / ack / stop) and session-level
        (events stream / list / send + heartbeat / force-stop) calls.
        Required by :meth:`run`; :meth:`handle_item` falls back to it (then to
        ``ANTHROPIC_ENVIRONMENT_KEY``) when not passed one.
      tools: Tools to expose to each claimed session. Either a fixed list, or a
        factory invoked once per session with that session's
        :class:`AgentToolContext`. Defaults to
        ``beta_agent_toolset_20260401(env)`` (the standard
        ``agent_toolset_20260401`` set bound to the per-session context).
      workdir: Base directory for the per-session :class:`AgentToolContext`.
        Defaults to :func:`os.getcwd` captured when the worker is constructed
        (matches the TS worker's ``process.cwd()``-at-construction), so a
        ``chdir`` between constructing the worker and serving a session does not
        change where tools resolve paths.
      unrestricted_paths: Forwarded to the per-session :class:`AgentToolContext`.
      max_idle: Forwarded to the session tool runner — seconds to keep running
        after the session goes idle with ``stop_reason`` ``end_turn``. Defaults
        to :data:`~anthropic.lib.environments.DEFAULT_MAX_IDLE` (60s). ``None``
        disables it.
      worker_id: Optional identifier sent on each poll. Defaults to a unique,
        hostname-prefixed id.
      extra_headers: Optional headers passed through per request on every
        call the worker makes (poll / ack / stop / heartbeat and the session
        tool runner's event stream / list / send). They are threaded into
        each call's ``extra_headers=`` and never assigned onto the client, so
        client state is not mutated. Auth and ``x-stainless-helper`` are
        supplied by the worker's scoped sub-clients (and the parent client's
        ``default_headers`` propagate via their ``client.copy()``); a header
        given here overrides a scoped client's same-named default for that
        request, so use it for caller passthrough (e.g. trace ids), not auth.
    NF)	r"   environment_keytoolsworkdirunrestricted_pathsmax_file_bytesmax_idle	worker_idr    c       	            || _         || _        || _        || _        |t	        j
                         n|| _        || _        || _        || _	        |	| _
        |
| _        y )N)_client_environment_id_environment_key_toolsrH   getcwd_workdir_unrestricted_paths_max_file_bytes	_max_idle
_worker_id_extra_headers)selfclientr"   rS   rT   rU   rV   rW   rX   rY   r    s              rE   __init__zEnvironmentWorker.__init__   s`     - / @G		T[#5 -!#+rQ   c                    t        | j                        r| j                  |      S | j                  | j                  S ddlm}  ||      S )Nr   )beta_agent_toolset_20260401)callabler^   tools.agent_toolsetrj   )rf   envrj   s      rE   
_tools_forzEnvironmentWorker._tools_for  sB    DKK ;;s##;;";; 	F*3//rQ   c                `  K   | j                   }| j                  }||t        d      t        | j                  |d      }t        |j                  j                  j                  || j                  d| j                        2 3 d{   }| j                  ||       d{    $7 7 6 yw)a  Poll the environment and service each claimed session until cancelled.

        Loops forever; cancel the task (or wrap it in :func:`asyncio.wait_for`)
        to stop it. Equivalent to claiming work items via
        ``client.beta.environments.work.poller`` and running the per-item flow
        for each.

        Raises:
          ValueError: if ``environment_id`` / ``environment_key`` were not passed
            to the constructor.
        NzWEnvironmentWorker.run: environment_id and environment_key are required to poll for workzenvironments-work-poller
auth_tokenhelperF)r"   rY   	auto_stopr    )r\   r]   rK   r   r[   r   betaenvironmentsr<   rd   re   _handle_item)rf   r"   rS   poll_client	work_items        rE   runzEnvironmentWorker.run$  s      --//!_%<vww 4LL_=W
  *))..)oo-- 
 	@ 	@) ##I???	@ @ 
s<   BB.B,B(	B,B."B*#B.(B,*B.,B.)r=   r"   
session_idrS   c               *  K   t        |dd      }t        |dd      }t        |dd      }t        |xs | j                  dd	      }t        j                  ||t	        j                  d
|            }| j                  ||       d{    y7 w)u^  Service a single, already-claimed work item without the poll loop.

        Builds the per-session :class:`AgentToolContext` (workdir from this
        worker's options) and downloads the session agent's skills, then runs a
        :class:`SessionToolRunner` for the session *while* heartbeating the
        work-item lease in parallel, and force-stops the work item on exit
        (whether the runner finishes normally, raises, or the heartbeat loop
        signals shutdown).

        Use this when something else does the claiming — e.g. a
        ``worker poll --on-work`` script that hands an already-claimed item to a
        fresh process. ``work_id`` / ``environment_id`` / ``session_id`` fall
        back to ``ANTHROPIC_WORK_ID`` / ``ANTHROPIC_ENVIRONMENT_ID`` /
        ``ANTHROPIC_SESSION_ID`` (the env vars that command sets) when not
        passed; ``environment_key`` resolves in order: the explicit argument,
        then this worker's own ``environment_key``, then
        ``ANTHROPIC_ENVIRONMENT_KEY`` — so with no arguments inside that command
        it just works. Non-session work items are ignored (but still
        force-stopped so the lease doesn't sit until TTL).

        Raises:
          ValueError: if any of ``work_id`` / ``environment_id`` / ``session_id``
            / ``environment_key`` is still empty after the fallbacks.
        r=   ANTHROPIC_WORK_ID)rM   rN   r"   ANTHROPIC_ENVIRONMENT_IDrz   ANTHROPIC_SESSION_IDrS   ANTHROPIC_ENVIRONMENT_KEYsession)typeid)r   r"   dataN)rP   r]   r   model_constructr   rv   )rf   r=   r"   rz   rS   rx   s         rE   handle_itemzEnvironmentWorker.handle_itemC  s     @ 7<OP!.7GQklj|E[\
 #4t44"/
 '66)$44)
S
	
 	?;;;s   B	BBBc           	        K   ddl m} t         j                  |d      }|j                  j
                  j                  	 |j                  j                  }t        j                         4 d{   t        j                         }|j                  |j                  |f	 	 	 	 	 	 	 d fd}j                  |        | j                   j                   j                   ||      }	 |j#                          d{     j%                  |      }		 t'         j                  ||	 j(                  | j*                        4 d{   }
|
2 3 d{   }
7 7 \7 7 6 ddd      d{  7   n# 1 d{  7  sw Y   nxY w|j-                          j.                  j1                          n/# |j-                          j.                  j1                          w xY w	 t        j2                  d	
      5  |j5                  ddd       d{  7   ddd       nX# 1 sw Y   nOxY w# t        j2                  d	
      5  |j5                  ddd       d{  7   ddd       w # 1 sw Y   w xY wxY wddd      d{  7   n# 1 d{  7  sw Y   nxY wt        j2                  d	
      5  	 j7                  |j                  |j                  d	 j*                         d{  7   nC# t8        $ r7}t;        |d      s!t<        j?                  d|j                  |       Y d}~nd}~ww xY wddd       y# 1 sw Y   yxY w# t        j2                  d	
      5  	 j7                  |j                  |j                  d	 j*                         d{  7   nC# t8        $ r7}t;        |d      s!t<        j?                  d|j                  |       Y d}~nd}~ww xY wddd       w # 1 sw Y   w xY wxY ww)u  The per-item body shared by :meth:`run`'s poll loop and :meth:`handle_item`.

        Runs a :class:`SessionToolRunner` for the work item's session while
        heartbeating its lease, force-stopping the work item on exit. All
        control-plane traffic for this work item — heartbeat + force-stop —
        flows through a
        Bearer-only sub-client built here; the session tool runner builds its
        own ``session-tool-runner``-tagged sub-client internally.
        r   r   zenvironments-workerrp   Nc                   K   	 t        | ||j                         d {    j                  j                          y 7 # j                  j                          w xY ww)N)r=   r"   r>   r    )rF   re   cancel_scopecancel)r=   r"   stop_evrf   tgwork_ress      rE   
_heartbeatz2EnvironmentWorker._handle_item.<locals>._heartbeat  s]     
	1-$$++9!(*.*=*=   ..0 ..0s+   A#A AA A#A A  A#)rU   rV   rW   rg   rz   )rT   rX   rS   r    T)shield)r"   forcer    i  z-force-stop on exit failed work_id=%s error=%s)r=   strr"   r   r   anyio.EventreturnNone) rl   r   r   r[   rt   ru   r<   r   r   r,   create_task_groupEventr"   
start_soonr`   ra   rb   
__aenter__rn   r   rc   re   r7   r   r   CancelScope	__aexit__r>   	Exceptionr   r5   r8   )rf   rx   rS   r   worker_clientrz   r>   r   rm   rT   calls_rD   r   r   s   `            @@rE   rv   zEnvironmentWorker._handle_itemx  s     	;
 6LL_=R
 !%%2277T	d"**J..0 B> B>B{{} $-<<*3*B*B+/1 1$'1 )1 	14 j) ' MM'+'?'?#'#7#7()>..*** OOC0E1#5 LL&"'%)^^,;*.*=*=$ 	% 	% #+0 % %a $yB>` +	%%5	% 	% 	% 	% 	% 
..0 
..00**$7 >!mmD$===> > >**$7 >!mmD$===> > >CB> B> B> B> B>P ""$/ 
d	d"--!'0'?'?"&*&9&9	 (    ! d%a-		"QS\S_S_abcd
d 
d 
d""$/ 
d	d"--!'0'?'?"&*&9&9	 (    ! d%a-		"QS\S_S_abcd
d 
d 
ds  ?P.M 3E4M 7A8J0H1E H13F9E"F9E9E&E$
E&E9M  H1"F9$E&&E9'F92E53F99F	?F F	F9+H19,G%%H1)J?H%HH%	J%H.	*J1I:I.	I" I.	%	I:.I73I::J=M J	M J!JJ!M $P:M<6K92K53K98M9	L9-L4/M4L99M<	PM
PO?(O3*6N' N#
!N'&O3'	O'	0-O"	O3"O'	'O3*	O?3O<8O??P)rg   r   r"   
str | NonerS   r   rT   zEnvironmentWorkerTools | NonerU   zstr | os.PathLike[str] | NonerV   boolrW   zint | None | NotGivenrX   zfloat | NonerY   r   r    Headers | Noner   r   )rm   r   r   zSequence[BetaAnyRunnableTool])r   r   )
r=   r   r"   r   rz   r   rS   r   r   r   )rx   r   rS   r   r   r   )__name__
__module____qualname____doc__r   r   rh   rn   ry   r   rv    rQ   rE   r   r      s    Tt &*&*/315#(09!1 $(,,, #	,
 $, -, /, !, ., , , &, 
,8	0@D #%)!%&*3< 3< #	3<
 3< $3< 
3<jidrQ   )r<   r   r=   r   r"   r   r>   r   r    r   r   r   )rL   r   rM   r   rN   r   r   r   )4r   
__future__r   rH   r)   loggingtypingr   r   r   collections.abcr   r,   _retryr	   _pollerr   r   r   _typesr   r   r   _scoped_clientr   types.beta.environmentsr   r   tools._beta_session_runnerr   r   r   r[   r   rl   r    resources.beta.environments.workr   __all__	getLoggerr   r5   r&   r'   r(   r   TimeoutErrorr   __annotations__rF   rP   r   r   rQ   rE   <module>r      s%  8 # 	   1 1 $  % : : 2 2 : N  )6=  8
9g!   '   !8-?,@(K^B_,_#``  <]=M;\|;\ 8 \ %)=
= = 	=
 = "= 
=@	{d {drQ   