
    j                       d Z ddlmZ ddlmZmZmZmZ ddlm	Z	 ddl
m
Z
mZ ddlmZmZmZ ddl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mZ e	 G d d                      Ze	 G d d                      Ze	 G d d                      ZdZdZ  G d d          Z! G d d          Z"e	 G d d                      Z# G d d          Z$ G d d          Z%dS )z#Data models for the sandbox client.    )annotations)AsyncIterator	AwaitableCallableIterator)	dataclass)datetimetimezone)TYPE_CHECKINGAnyOptionalN)SandboxConnectionErrorSandboxOperationErrorSandboxServerReloadError)AsyncSandbox)Sandbox)_AsyncWSStreamControl_WSStreamControlc                  J    e Zd ZU dZded<   ded<   ded<   edd	            Zd
S )ExecutionResultz+Result of executing a command in a sandbox.strstdoutstderrint	exit_codereturnboolc                    | j         dk    S )z.Return True if the command exited with code 0.r   )r   selfs    c/lsinfo/ai/hellotax_ai/base_platform/venv/lib/python3.11/site-packages/langsmith/sandbox/_models.pysuccesszExecutionResult.success#   s     ~""    Nr   r   )__name__
__module____qualname____doc____annotations__propertyr"    r#   r!   r   r      sU         55KKKKKKNNN# # # X# # #r#   r   c                  D    e Zd ZU dZded<   dZded<   edd
            ZdS )ResourceStatusa  Lightweight provisioning status for any async-created resource.

    Attributes:
        status: Resource lifecycle status. One of "provisioning", "ready", "failed".
        status_message: Human-readable details when status is "failed", None otherwise.
    r   statusNOptional[str]status_messagedatadict[str, Any]r   c                j     | |                     dd          |                     d                    S )z/Create a ResourceStatus from API response dict.r.   provisioningr0   )r.   r0   getclsr1   s     r!   	from_dictzResourceStatus.from_dict5   s>     s88Hn5588$455
 
 
 	
r#   )r1   r2   r   r-   )r%   r&   r'   r(   r)   r0   classmethodr9   r+   r#   r!   r-   r-   )   sZ           KKK$(N((((
 
 
 [
 
 
r#   r-   c                      e Zd ZU dZded<   ded<   ded<   ded<   dZd	ed
<   dZd	ed<   dZd	ed<   dZd	ed<   d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dd            ZdS )Snapshota  Represents a sandbox snapshot.

    Snapshots are built from Docker images or captured from running sandboxes.
    They are used to create new sandboxes.

    Attributes:
        id: Unique identifier (UUID).
        name: Display name.
        status: Build status. One of "building", "ready", "failed".
        fs_capacity_bytes: Filesystem capacity in bytes.
        docker_image: Source Docker image (for build snapshots).
        image_digest: Docker image digest after pull.
        source_sandbox_id: Source sandbox (for capture snapshots).
        status_message: Human-readable details when status is "failed".
        fs_used_bytes: Actual bytes used on the filesystem.
        created_by: User or service that created the snapshot.
        registry_id: Private registry ID, if applicable.
        created_at: Timestamp when the snapshot was created.
        updated_at: Timestamp when the snapshot was last updated.
    r   idnamer.   r   fs_capacity_bytesNr/   docker_imageimage_digestsource_sandbox_idr0   Optional[int]fs_used_bytes
created_byregistry_id
created_at
updated_atr1   r2   r   c                (    | |                     dd          |                     dd          |                     dd          |                     dd          |                     d          |                     d	          |                     d
          |                     d          |                     d          |                     d          |                     d          |                     d          |                     d                    S )z)Create a Snapshot from API response dict.r=    r>   r.   buildingr?   r   r@   rA   rB   r0   rD   rE   rF   rG   rH   )r=   r>   r.   r?   r@   rA   rB   r0   rD   rE   rF   rG   rH   r5   r7   s     r!   r9   zSnapshot.from_dictc   s     sxxb!!&"%%88Hj11"hh':A>>.11.11"hh':;;88$455((?33xx--//xx--xx--
 
 
 	
r#   )r1   r2   r   r<   )r%   r&   r'   r(   r)   r@   rA   rB   r0   rD   rE   rF   rG   rH   r:   r9   r+   r#   r!   r<   r<   >   s         * GGGIIIKKK"&L&&&&"&L&&&&'+++++$(N((((#'M'''' $J$$$$!%K%%%% $J$$$$ $J$$$$
 
 
 [
 
 
r#   r<   z!X-Langsmith-Sandbox-Service-Token   c                      e Zd ZdZddd%dZd&dZd'dZed(d            Zed(d            Z	ed(d            Z
ed(d            Zd)d*dZd)d+dZd)d+dZd)d+dZd)d+dZd)d+d Zeddd,d#            Zd(d$ZdS )-
ServiceURLar  Authenticated URL for accessing an HTTP service running in a sandbox.

    Properties auto-refresh the token transparently when it nears expiry.
    HTTP helper methods (``.get``, ``.post``, etc.) inject the auth header
    automatically.

    When constructed by :meth:`SandboxClient.service` or
    :meth:`Sandbox.service`, the object holds an internal refresher that
    re-calls the API to obtain a fresh token before the current one expires.

    Example::

        svc = sb.service(port=3000)

        resp = svc.get("/api/data")  # token injected + auto-refreshed
        print(svc.browser_url)  # always-fresh URL
    N
_refresherbrowser_urlr   service_urltoken
expires_atrP   "Optional[Callable[[], ServiceURL]]r   Nonec               L    || _         || _        || _        || _        || _        d S N_browser_url_service_url_token_expires_atrP   r    rQ   rR   rS   rT   rP   s         r!   __init__zServiceURL.__init__   .     ('%$r#   r   c                <   | j         dS | j                            dd          }t          j        |          }|j         |                    t          j                  }|t          j        t          j                  z
  	                                }|t          k    S NFZz+00:00)tzinforP   r]   replacer	   fromisoformatrd   r
   utcnowtotal_seconds_REFRESH_MARGIN_SECONDSr    rawexpires	remainings       r!   _should_refreshzServiceURL._should_refresh       ?"5&&sH55(-->!ooX\o::Gx|HL999HHJJ	333r#   c                    |                                  rF|                                 }|j        | _        |j        | _        |j        | _        |j        | _        d S d S rX   rp   rP   rZ   r[   r\   r]   r    freshs     r!   _maybe_refreshzServiceURL._maybe_refresh   s_    !! 	1OO%%E % 2D % 2D,DK$0D	1 	1r#   c                8    |                                   | j        S ).Return the raw JWT, refreshing if near expiry.rv   r\   r   s    r!   rS   zServiceURL.token   s     	{r#   c                8    |                                   | j        S )/Return the base URL, refreshing if near expiry.rv   r[   r   s    r!   rR   zServiceURL.service_url        	  r#   c                8    |                                   | j        S )7Return the browser auth URL, refreshing if near expiry.rv   rZ   r   s    r!   rQ   zServiceURL.browser_url   r}   r#   c                8    |                                   | j        S ):Return the ISO 8601 expiration, refreshing if near expiry.rv   r]   r   s    r!   rT   zServiceURL.expires_at   s     	r#   /methodpathkwargsr   httpx.Responsec                    | j                             d          dz   |                    d          z   }t          |                    dd          pi           }| j        |t          <   t          j        ||fd|i|S )a"  Make an HTTP request to the service, injecting the auth header.

        Args:
            method: HTTP method (GET, POST, etc.).
            path: Path relative to the service URL.
            **kwargs: Forwarded to ``httpx.request``.

        Returns:
            httpx.Response.
        r   headersN)	rR   rstriplstripdictpoprS   _AUTH_HEADERhttpxrequest)r    r   r   r   urlr   s         r!   r   zServiceURL.request   s{     %%c**S04;;s3C3CCvzz)T228b99 $
}VSDD'DVDDDr#   c                      | j         d|fi |S )zHTTP GET to the service.GETr   r    r   r   s      r!   r6   zServiceURL.get       t|E4226222r#   c                      | j         d|fi |S )zHTTP POST to the service.POSTr   r   s      r!   postzServiceURL.post   s    t|FD33F333r#   c                      | j         d|fi |S )zHTTP PUT to the service.PUTr   r   s      r!   putzServiceURL.put   r   r#   c                      | j         d|fi |S )zHTTP PATCH to the service.PATCHr   r   s      r!   patchzServiceURL.patch   s    t|GT44V444r#   c                      | j         d|fi |S )zHTTP DELETE to the service.DELETEr   r   s      r!   deletezServiceURL.delete   s    t|Hd55f555r#   r1   r2   c               R     | |d         |d         |d         |d         |          S )z+Create a ServiceURL from API response dict.rQ   rR   rS   rT   rQ   rR   rS   rT   rP   r+   r8   r1   rP   s      r!   r9   zServiceURL.from_dict   ?     s]+]+w-L)!
 
 
 	
r#   c                (    d| j         d| j        dS )NzServiceURL(service_url=, expires_at=)r[   r]   r   s    r!   __repr__zServiceURL.__repr__  s0    0d&7 0 0*0 0 0	
r#   )rQ   r   rR   r   rS   r   rT   r   rP   rU   r   rV   r$   r   rV   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   )r1   r2   rP   rU   r   rN   )r%   r&   r'   r(   r_   rp   rv   r*   rS   rR   rQ   rT   r   r6   r   r   r   r   r:   r9   r   r+   r#   r!   rN   rN      s        2 :>% % % % % %"4 4 4 41 1 1 1    X
 ! ! ! X!
 ! ! ! X!
       X E E E E E 3 3 3 3 34 4 4 4 43 3 3 3 35 5 5 5 56 6 6 6 6 
 :>	
 
 
 
 
 [

 
 
 
 
 
r#   rN   c                     e Zd ZdZddd)dZd*dZd+dZd,dZd,dZd,dZ	d,dZ
ed,d            Zed,d            Zed,d            Zed,d            Z	 d-d.dZd-d/d Zd-d/d!Zd-d/d"Zd-d/d#Zd-d/d$Zeddd0d'            Zd,d(ZdS )1AsyncServiceURLaR  Async variant of :class:`ServiceURL` with auto-refreshing token.

    Properties and HTTP helpers are async. Use with
    :meth:`AsyncSandboxClient.service` or :meth:`AsyncSandbox.service`.

    Example::

        svc = await sb.service(port=3000)

        resp = await svc.get("/api/data")
        print(await svc.get_browser_url())
    NrO   rQ   r   rR   rS   rT   rP   2Optional[Callable[[], Awaitable[AsyncServiceURL]]]r   rV   c               L    || _         || _        || _        || _        || _        d S rX   rY   r^   s         r!   r_   zAsyncServiceURL.__init__  r`   r#   r   c                <   | j         dS | j                            dd          }t          j        |          }|j         |                    t          j                  }|t          j        t          j                  z
  	                                }|t          k    S rb   re   rl   s       r!   rp   zAsyncServiceURL._should_refresh-  rq   r#   c                   K   |                                  rL|                                  d {V }|j        | _        |j        | _        |j        | _        |j        | _        d S d S rX   rs   rt   s     r!   rv   zAsyncServiceURL._maybe_refresh7  su      !! 	1//++++++++E % 2D % 2D,DK$0D	1 	1r#   c                H   K   |                                   d{V  | j        S )rx   Nry   r   s    r!   	get_tokenzAsyncServiceURL.get_tokenA  s2      !!#########{r#   c                H   K   |                                   d{V  | j        S )r{   Nr|   r   s    r!   get_service_urlzAsyncServiceURL.get_service_urlF  3      !!#########  r#   c                H   K   |                                   d{V  | j        S )r   Nr   r   s    r!   get_browser_urlzAsyncServiceURL.get_browser_urlK  r   r#   c                H   K   |                                   d{V  | j        S )r   Nr   r   s    r!   get_expires_atzAsyncServiceURL.get_expires_atP  s3      !!#########r#   c                    | j         S )z&Return the raw JWT without refreshing.)r\   r   s    r!   rS   zAsyncServiceURL.tokenW  s     {r#   c                    | j         S )z'Return the base URL without refreshing.)r[   r   s    r!   rR   zAsyncServiceURL.service_url\         r#   c                    | j         S )z/Return the browser auth URL without refreshing.)rZ   r   s    r!   rQ   zAsyncServiceURL.browser_urla  r   r#   c                    | j         S )z3Return the expiration timestamp without refreshing.)r]   r   s    r!   rT   zAsyncServiceURL.expires_atf       r#   r   r   r   r   r   r   c                  K   |                                   d{V                     d          dz   |                    d          z   }t          |                    dd          pi           }|                                  d{V |t          <   t          j                    4 d{V } |j	        ||fd|i| d{V cddd          d{V  S # 1 d{V swxY w Y   dS )a4  Make an async HTTP request to the service, injecting the auth header.

        Args:
            method: HTTP method (GET, POST, etc.).
            path: Path relative to the service URL.
            **kwargs: Forwarded to ``httpx.AsyncClient.request``.

        Returns:
            httpx.Response.
        Nr   r   )
r   r   r   r   r   r   r   r   AsyncClientr   )r    r   r   r   r   r   clients          r!   r   zAsyncServiceURL.requestm  s      ))++++++++33C883>SAQAQQvzz)T228b99&*nn&6&6 6 6 6 6 6 6$&& 	P 	P 	P 	P 	P 	P 	P&'OOWOOOOOOOOO	P 	P 	P 	P 	P 	P 	P 	P 	P 	P 	P 	P 	P 	P 	P 	P 	P 	P 	P 	P 	P 	P 	P 	P 	P 	P 	P 	P 	P 	Ps   )C
C Cc                0   K    | j         d|fi | d{V S )zAsync HTTP GET to the service.r   Nr   r   s      r!   r6   zAsyncServiceURL.get  4      !T\%88888888888r#   c                0   K    | j         d|fi | d{V S )zAsync HTTP POST to the service.r   Nr   r   s      r!   r   zAsyncServiceURL.post  s4      !T\&$99&999999999r#   c                0   K    | j         d|fi | d{V S )zAsync HTTP PUT to the service.r   Nr   r   s      r!   r   zAsyncServiceURL.put  r   r#   c                0   K    | j         d|fi | d{V S )z Async HTTP PATCH to the service.r   Nr   r   s      r!   r   zAsyncServiceURL.patch  s4      !T\'4::6:::::::::r#   c                0   K    | j         d|fi | d{V S )z!Async HTTP DELETE to the service.r   Nr   r   s      r!   r   zAsyncServiceURL.delete  s4      !T\(D;;F;;;;;;;;;r#   r1   r2   c               R     | |d         |d         |d         |d         |          S )z1Create an AsyncServiceURL from API response dict.rQ   rR   rS   rT   r   r+   r   s      r!   r9   zAsyncServiceURL.from_dict  r   r#   c                (    d| j         d| j        dS )NzAsyncServiceURL(service_url=r   r   r   r   s    r!   r   zAsyncServiceURL.__repr__  s0    04+< 0 0*0 0 0	
r#   )rQ   r   rR   r   rS   r   rT   r   rP   r   r   rV   r$   r   r   r   r   r   )r1   r2   rP   r   r   r   )r%   r&   r'   r(   r_   rp   rv   r   r   r   r   r*   rS   rR   rQ   rT   r   r6   r   r   r   r   r:   r9   r   r+   r#   r!   r   r     s         ( JN% % % % % %"4 4 4 41 1 1 1   
! ! ! !
! ! ! !
           X ! ! ! X! ! ! ! X!       X  (+P P P P P&9 9 9 9 9: : : : :9 9 9 9 9; ; ; ; ;< < < < < 
 JN	
 
 
 
 
 [

 
 
 
 
 
r#   r   c                  2    e Zd ZU dZded<   ded<   ded<   dS )OutputChunkaf  A single chunk of streaming output from command execution.

    Attributes:
        stream: Either "stdout" or "stderr".
        data: The text content of this chunk (valid UTF-8, server handles
            boundary splitting).
        offset: Byte offset within the stream. Used internally for
            reconnection; users typically don't need this.
    r   streamr1   r   offsetN)r%   r&   r'   r(   r)   r+   r#   r!   r   r     s7           KKKIIIKKKKKr#   r   c                      e Zd ZdZdZdZdZddddd'dZd(dZe	d)d            Z
e	d*d            Ze	d+d            Zd,dZd,dZd(d Zd-d"Ze	d.d#            Ze	d.d$            Zd/d%Zd&S )0CommandHandleaE  Handle to a running command with streaming output and auto-reconnect.

    Iterable, yielding OutputChunk objects (stdout and stderr interleaved
    in arrival order). Access .result after iteration to get the full
    ExecutionResult.

    Auto-reconnect behavior:
    - Server hot-reload (1001 Going Away): reconnect immediately
    - Network error / unexpected close:    reconnect with exponential backoff
    - User called kill():                  do NOT reconnect (propagate error)

    The auto-reconnect is transparent -- the iterator reconnects and
    continues yielding chunks without any user intervention. If all
    reconnect attempts are exhausted, SandboxConnectionError is raised.

    Construction modes (controlled by ``command_id``):
    - **New execution** (``command_id=""``, the default): the constructor
      eagerly reads the server's ``"started"`` message to populate
      ``command_id`` and ``pid`` before returning.
    - **Reconnection** (``command_id`` set): skips the started-message
      read, since reconnect streams don't emit one.

    Example:
        handle = sandbox.run("make build", timeout=600, wait=False)

        for chunk in handle:          # auto-reconnects on transient errors
            print(chunk.data, end="")

        result = handle.result
        print(f"Exit code: {result.exit_code}")
             ?       @rJ   r   
command_idstdout_offsetstderr_offsetmessage_streamIterator[dict]controlOptional[_WSStreamControl]sandboxr   r   r   r   r   r   r   rV   c                   || _         || _        || _        d | _        d | _        d | _        g | _        g | _        d| _        || _	        || _
        |r	|| _        d S |                                  d S )NF)_stream_control_sandbox_command_id_pid_result_stdout_parts_stderr_parts
_exhausted_last_stdout_offset_last_stderr_offset_consume_startedr    r   r   r   r   r   r   s          r!   r_   zCommandHandle.__init__  s     &*.#'	26(*(*#0 #0 
  	$)D!!#####r#   c                Z   	 t          | j                  }n# t          $ r t          dd          w xY w|                    d          dk    r(t          d|                    d           dd          |                    d          | _        |                    d	          | _        d
S )aH  Eagerly read the 'started' message to populate command_id and pid.

        Blocks briefly until the server sends the started message (arrives
        near-instantly after connection). After this call, command_id and
        pid are available, and the WebSocket is bound to the control object
        (so kill() works).
        -Command stream ended before 'started' messagecommand	operationtypestarted!Expected 'started' message, got ''r   pidN)nextr   StopIterationr   r6   r   r   r    	first_msgs     r!   r   zCommandHandle._consume_started  s    	T\**II 	 	 	'?#   	
 ==  I--'LIMM&4I4ILLL#    %==66MM%((			s    3r/   c                    | j         S )z=The server-assigned command ID. Available after construction.r   r   s    r!   r   zCommandHandle.command_id  r   r#   rC   c                    | j         S )z<The process ID on the sandbox. Available after construction.r   r   s    r!   r   zCommandHandle.pid"       yr#   r   c                X    | j         | D ]}| j         t          dd          | j         S )zThe final execution result. Blocks until the command completes.

        Drains the remaining stream if not already exhausted, then returns
        the ExecutionResult with aggregated stdout, stderr, and exit_code.
        N)Command stream ended without exit messager   r   r   r   r    _s     r!   resultzCommandHandle.result'  sM     <  <';#    |r#   Iterator[OutputChunk]c           	   #    K   | j         rdS | j        D ]}|                    d          }|dv rxt          ||d         |                    dd                    }|dk    r!| j                            |d                    n | j                            |d                    |V  |d	k    rWt          d
                    | j                  d
                    | j                  |d                   | _	        d| _          dS d| _         dS zBIterate over output chunks from the current stream (no reconnect).Nr   )r   r   r1   r   r   )r   r1   r   r   exitrJ   r   )r   r   r   T)
r   r   r6   r   r   appendr   r   joinr   r    msgmsg_typechunks       r!   _iter_streamzCommandHandle._iter_stream8  s0     ? 	F< 	 	CwwvH///##V778Q//  
 x''&--c&k::::&--c&k:::V##.774#566774#566!+.     
 #' $ r#   c              #  $  K   ddl }d}	 	 |                                 D ]|}d}|j        dk    r5|j        t	          |j                            d                    z   | _        n4|j        t	          |j                            d                    z   | _        |V  }dS # t          $ r}| j
        r| j
        j        r |dz  }|| j        k    rt          d| d          |t          |t                    }|s8t          | j        d	|dz
  z  z  | j                  }|                    |           | j        J | j                            | j        | j        | j        
          }|j        | _        |j
        | _
        d| _        Y d}~nd}~ww xY w)aE  Iterate over output chunks, auto-reconnecting on transient errors.

        Reconnect strategy:
        - 1001 Going Away (hot-reload): immediate reconnect, no delay
        - Other SandboxConnectionError:  exponential backoff (0.5s, 1s, 2s...)
        - After kill():                  no reconnect, error propagates
        r   NTr   utf-8   Lost connection  times in succession, giving up   r   r   F)timer  r   r   lenr1   encoder   r   r   r   killedMAX_AUTO_RECONNECTS
isinstancer   min_BACKOFF_BASE_BACKOFF_MAXsleepr   r   	reconnectr   r   )r    r  reconnect_attemptsr  eis_hot_reloaddelay
new_handles           r!   __iter__zCommandHandle.__iter__S  s      	*	()(!..00 
  
 E)*&|x//38<#!J--g66C C 400 49<#!J--g66C C 40  KKKK) ( ( (= T]%9 "a'"%(@@@01+= 1 1 1  
 !+1.F G G$ &*a4F4J.KL) E JJu%%%'333!]44$"&":"&": 5  

  *1 * 3"'7(*	(s   BB 
F(CFFc                J    | j         r| j                                          dS dS )a/  Send a kill signal to the running command (SIGKILL).

        The server kills the entire process group. The stream will
        subsequently yield an exit message with a non-zero exit code.

        Has no effect if the command has already exited or the
        WebSocket connection is closed.
        Nr   	send_killr   s    r!   killzCommandHandle.kill  s2     = 	&M##%%%%%	& 	&r#   r1   c                L    | j         r| j                             |           dS dS )zWrite data to the command's stdin.

        Args:
            data: String data to write to stdin.

        Has no effect if the command has already exited or the
        WebSocket connection is closed.
        Nr   
send_inputr    r1   s     r!   r4  zCommandHandle.send_input  s4     = 	+M$$T*****	+ 	+r#   c                    | j         S z8Last known stdout byte offset (for manual reconnection).r   r   s    r!   last_stdout_offsetz CommandHandle.last_stdout_offset       ''r#   c                    | j         S z8Last known stderr byte offset (for manual reconnection).r   r   s    r!   last_stderr_offsetz CommandHandle.last_stderr_offset  r:  r#   c                l    | j         J | j                            | j         | j        | j                  S )a  Reconnect to this command from the last known offsets.

        Returns a new handle that resumes output from where this one
        left off. Any output produced while disconnected is replayed
        from the server's ring buffer.

        Returns:
            A new CommandHandle.

        Raises:
            SandboxOperationError: If command_id is not found or
                session expired.
            SandboxConnectionError: If connection to sandbox fails.
        Nr  r   r   r'  r   r   r   s    r!   r'  zCommandHandle.reconnect  sD     +++}&&22 ' 
 
 	
r#   N)r   r   r   r   r   r   r   r   r   r   r   r   r   rV   r   r   r/   r   rC   r   r   )r   r  r1   r   r   rV   r   r   )r   r   )r%   r&   r'   r(   r!  r$  r%  r_   r   r*   r   r   r
  r  r-  r1  r4  r9  r>  r'  r+   r#   r!   r   r     ss        @ ML $ $ $ $ $ $<) ) ) ).       X     X    X    65( 5( 5( 5(n
& 
& 
& 
&
+ 
+ 
+ 
+ ( ( ( X( ( ( ( X(
 
 
 
 
 
r#   r   c                      e Zd ZdZdZdZdZddddd'dZd(dZe	d)d            Z
e	d*d            Ze	d+d            Zd,dZd,dZd(d Zd-d"Ze	d.d#            Ze	d.d$            Zd/d%Zd&S )0AsyncCommandHandlea  Async handle to a running command with streaming output and auto-reconnect.

    Async iterable, yielding OutputChunk objects (stdout and stderr interleaved
    in arrival order). Access .result after iteration to get the full
    ExecutionResult.

    Auto-reconnect behavior:
    - Server hot-reload (1001 Going Away): reconnect immediately
    - Network error / unexpected close:    reconnect with exponential backoff
    - User called kill():                  do NOT reconnect (propagate error)

    Construction modes (controlled by ``command_id``):
    - **New execution** (``command_id=""``, the default): call
      ``await handle._ensure_started()`` after construction to read the
      server's ``"started"`` message and populate ``command_id`` / ``pid``.
    - **Reconnection** (``command_id`` set): skips the started-message
      read, since reconnect streams don't emit one.

    Example:
        handle = await sandbox.run("make build", timeout=600, wait=False)

        async for chunk in handle:    # auto-reconnects on transient errors
            print(chunk.data, end="")

        result = await handle.result
        print(f"Exit code: {result.exit_code}")
    r   r   r   rJ   r   r   r   AsyncIterator[dict]r   Optional[_AsyncWSStreamControl]r   r   r   r   r   r   r   r   rV   c                   || _         || _        || _        d | _        d | _        d | _        g | _        g | _        d| _        || _	        || _
        |r|| _        d| _        d S d| _        d S )NFT)r   r   r   r   r   r   r   r   r   r   r   _startedr   s          r!   r_   zAsyncCommandHandle.__init__  s~     &*.#'	26(*(*#0 #0 
  	")D DMMM!DMMMr#   c                  K   | j         rdS 	 | j                                         d{V }n# t          $ r t	          dd          w xY w|                    d          dk    r(t	          d|                    d           dd          |                    d	          | _        |                    d
          | _        d| _         dS )z:Read the 'started' message to populate command_id and pid.Nr   r   r   r   r   r   r   r   r   T)rK  r   	__anext__StopAsyncIterationr   r6   r   r   r   s     r!   _ensure_startedz"AsyncCommandHandle._ensure_started  s      = 	F	"l4466666666II! 	 	 	'?#   	
 ==  I--'LIMM&4I4ILLL#    %==66MM%((	s	   - A	r/   c                    | j         S )z@The server-assigned command ID. Available after _ensure_started.r  r   s    r!   r   zAsyncCommandHandle.command_id  r   r#   rC   c                    | j         S )z?The process ID on the sandbox. Available after _ensure_started.r  r   s    r!   r   zAsyncCommandHandle.pid  r  r#   r   c                h   K   | j         | 2 3 d{V }6 | j         t          dd          | j         S )z&The final execution result. Awaitable.Nr  r   r   r  r  s     r!   r
  zAsyncCommandHandle.result!  so       <       a  <';#    |s   AsyncIterator[OutputChunk]c           	    \  K   |                                   d{V  | j        rdS | j        2 3 d{V }|                    d          }|dv ryt	          ||d         |                    dd                    }|dk    r!| j                            |d                    n | j                            |d                    |W V  |d	k    rWt          d
	                    | j                  d
	                    | j                  |d                   | _
        d| _         dS 6 d| _        dS r  )rO  r   r   r6   r   r   r  r   r   r  r   r  s       r!   _aiter_streamz AsyncCommandHandle._aiter_stream.  st     ""$$$$$$$$$? 	F 	 	 	 	 	 	 	#wwvH///##V778Q//  
 x''&--c&k::::&--c&k:::V##.774#566774#566!+.     
 #' $ &* s   D$c               J  K   ddl }d}	 	 |                                 2 3 d{V }d}|j        dk    r5|j        t	          |j                            d                    z   | _        n4|j        t	          |j                            d                    z   | _        |W V  6 dS # t          $ r}| j
        r| j
        j        r |dz  }|| j        k    rt          d| d          |t          |t                    }|s>t          | j        d	|dz
  z  z  | j                  }|                    |           d{V  | j        J | j                            | j        | j        | j        
           d{V }|j        | _        |j
        | _
        d| _        Y d}~nd}~ww xY w)z6Async iterate with auto-reconnect on transient errors.r   NTr   r  r  r  r  r  r  F)asynciorU  r   r   r  r1   r  r   r   r   r   r   r!  r"  r   r#  r$  r%  r&  r   r   r'  r   r   )r    rW  r(  r  r)  r*  r+  r,  s           r!   	__aiter__zAsyncCommandHandle.__aiter__J  sW     *	()(#'#5#5#7#7 
  
  
  
  
  
  
 %)*&|x//38<#!J--g66C C 400 49<#!J--g66C C 40  KKKKK $8 ) ( ( (= T]%9 "a'"%(@@@04+= 4 4 4  
 !+1.F G G$ /*a4F4J.KL) E "--.........'333#'=#:#:$"&":"&": $; $ $      

  *1 * 3"'7(*	(s#   B% B"A>B% %
F /C'FF c                Z   K   | j         r!| j                                          d{V  dS dS )z*Send a kill signal to the running command.Nr/  r   s    r!   r1  zAsyncCommandHandle.kill{  sF      = 	,-))+++++++++++	, 	,r#   r1   c                \   K   | j         r"| j                             |           d{V  dS dS )z"Write data to the command's stdin.Nr3  r5  s     r!   r4  zAsyncCommandHandle.send_input  sH      = 	1-**400000000000	1 	1r#   c                    | j         S r7  r8  r   s    r!   r9  z%AsyncCommandHandle.last_stdout_offset  r:  r#   c                    | j         S r<  r=  r   s    r!   r>  z%AsyncCommandHandle.last_stderr_offset  r:  r#   c                |   K   | j         J | j                            | j         | j        | j                   d{V S )z6Reconnect to this command from the last known offsets.Nr  r@  r   s    r!   r'  zAsyncCommandHandle.reconnect  sd      +++],,22 - 
 
 
 
 
 
 
 
 	
r#   N)r   rH  r   rI  r   r   r   r   r   r   r   r   r   rV   r   rA  rB  rC  )r   rS  rD  rE  )r   rG  )r%   r&   r'   r(   r!  r$  r%  r_   rO  r*   r   r   r
  rU  rX  r1  r4  r9  r>  r'  r+   r#   r!   rG  rG    sr        8 ML " " " " " ">   (       X     X 
 
 
 X
   8/( /( /( /(b, , , ,
1 1 1 1
 ( ( ( X( ( ( ( X(
 
 
 
 
 
r#   rG  )&r(   
__future__r   collections.abcr   r   r   r   dataclassesr   r	   r
   typingr   r   r   r   langsmith.sandbox._exceptionsr   r   r    langsmith.sandbox._async_sandboxr   langsmith.sandbox._sandboxr   langsmith.sandbox._ws_executer   r   r   r-   r<   r   rk   rN   r   r   r   rG  r+   r#   r!   <module>rf     s   ) ) " " " " " " H H H H H H H H H H H H ! ! ! ! ! ! ' ' ' ' ' ' ' ' / / / / / / / / / /            ======222222        
# 
# 
# 
# 
# 
# 
# 
# 
 
 
 
 
 
 
 
( 5
 5
 5
 5
 5
 5
 5
 5
x 3 L
 L
 L
 L
 L
 L
 L
 L
^\
 \
 \
 \
 \
 \
 \
 \
H         }
 }
 }
 }
 }
 }
 }
 }
@S
 S
 S
 S
 S
 S
 S
 S
 S
 S
r#   