Back to Microsandbox

Agent client

docs/sdk/python/agent-client.mdx

0.6.711.1 KB
Original Source

AgentClient is the low-level raw transport for talking to agentd through a running sandbox's relay socket. For most applications, use Sandbox, exec, and fs instead. Reach for this API when you are building protocol-level tools or higher-level SDK helpers.

All request and response bodies are raw CBOR bytes. The SDK handles framing and correlation ids, but it does not encode or decode the CBOR message body for you. The raw body is the full CBOR-encoded protocol Message body (v, t, and p), not just the inner payload.

<p className="msb-label" id="typical-flow">Typical flow</p>
python
from microsandbox import AgentClient

client = await AgentClient.connect_sandbox("dev")   # 1. connect
ready = client.ready_bytes()                        # 2. inspect handshake
frame = await client.request(0, body)               # 3. raw request/response
await client.close()                                # 4. close

Constants

NameValueDescription
FLAG_TERMINAL0b0000_0001Last frame for a correlation id
FLAG_SESSION_START0b0000_0010First frame of a streaming session
FLAG_SHUTDOWN0b0000_0100Shutdown frame

Constructors

<span className="msb-recv">AgentClient.</span><span className="msb-hn">connect_sandbox()</span>

python
@classmethod
async def connect_sandbox(cls, name: str, *, timeout: float | None = None) -> AgentClient
<Accordion title="Example">
python
client = await AgentClient.connect_sandbox("dev", timeout=5.0)
</Accordion>

Connect to a running sandbox by name. Sandbox names are limited to 128 UTF-8 bytes.

<p className="msb-label">Parameters</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><code>name</code><span className="msb-type">str</span></div> <div className="msb-param-desc">Sandbox name, up to 128 UTF-8 bytes.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>timeout</code><span className="msb-type">float | None</span></div> <div className="msb-param-desc">Connection timeout in seconds. <code>None</code> uses the default.</div> </div> </div> <p className="msb-label">Returns</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><span className="msb-type">AgentClient</span></div> <div className="msb-param-desc">Connected client.</div> </div> </div>

<span className="msb-recv">AgentClient.</span><span className="msb-hn">connect()</span>

python
@classmethod
async def connect(cls, path: str, *, timeout: float | None = None) -> AgentClient
<Accordion title="Example">
python
path = AgentClient.socket_path("dev")
client = await AgentClient.connect(path)
</Accordion>

Connect to an agent relay socket by path. Use this when you already know the socket path, for example one returned by socket_path().

<p className="msb-label">Parameters</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><code>path</code><span className="msb-type">str</span></div> <div className="msb-param-desc">Path to the agentd relay socket.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>timeout</code><span className="msb-type">float | None</span></div> <div className="msb-param-desc">Connection timeout in seconds. <code>None</code> uses the default.</div> </div> </div> <p className="msb-label">Returns</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><span className="msb-type">AgentClient</span></div> <div className="msb-param-desc">Connected client.</div> </div> </div>

<span className="msb-recv">AgentClient.</span><span className="msb-hn">socket_path()</span>

python
@staticmethod
def socket_path(name: str) -> str
<Accordion title="Example">
python
path = AgentClient.socket_path("dev")
</Accordion>

Resolve a sandbox's agentd relay socket path without connecting. Returns the same path connect_sandbox() would dial, so you can talk to agentd over a raw byte transport (for example a transparent relay that splices bytes to and from the socket) instead of this frame client. The sandbox need not be running. Sandbox names are limited to 128 UTF-8 bytes.

<p className="msb-label">Parameters</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><code>name</code><span className="msb-type">str</span></div> <div className="msb-param-desc">Sandbox name, up to 128 UTF-8 bytes.</div> </div> </div> <p className="msb-label">Returns</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><span className="msb-type">str</span></div> <div className="msb-param-desc">Filesystem path to the relay socket.</div> </div> </div>

Instance methods

<span className="msb-recv">client.</span><span className="msb-hn">request()</span>

python
async def request(self, flags: int, body: bytes) -> RawFrame
<Accordion title="Example">
python
frame = await client.request(0, body)
print(frame["id"], frame["flags"])
</Accordion>

Send one raw frame and wait for one response frame.

<p className="msb-label">Parameters</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><code>flags</code><span className="msb-type">int</span></div> <div className="msb-param-desc">Frame flag byte, e.g. a combination of <code>FLAG_*</code> constants.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>body</code><span className="msb-type">bytes</span></div> <div className="msb-param-desc">CBOR-encoded protocol message body.</div> </div> </div> <p className="msb-label">Returns</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><a className="msb-type" href="#rawframe">RawFrame</a></div> <div className="msb-param-desc">The response frame.</div> </div> </div>

<span className="msb-recv">client.</span><span className="msb-hn">stream()</span>

python
async def stream(self, flags: int, body: bytes) -> AgentStream
<Accordion title="Example">
python
from microsandbox import FLAG_SESSION_START, FLAG_TERMINAL

stream = await client.stream(FLAG_SESSION_START, body)
async for frame in stream:
    if frame["flags"] & FLAG_TERMINAL:
        break
</Accordion>

Open a raw streaming session. The returned AgentStream carries the protocol correlation id and is also an async iterator of raw frames.

<p className="msb-label">Parameters</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><code>flags</code><span className="msb-type">int</span></div> <div className="msb-param-desc">Frame flag byte; pass <code>FLAG_SESSION_START</code> to open a session.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>body</code><span className="msb-type">bytes</span></div> <div className="msb-param-desc">CBOR-encoded protocol message body.</div> </div> </div> <p className="msb-label">Returns</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><a className="msb-type" href="#agentstream">AgentStream</a></div> <div className="msb-param-desc">Open streaming session.</div> </div> </div>

<span className="msb-recv">client.</span><span className="msb-hn">send()</span>

python
async def send(self, id: int, flags: int, body: bytes) -> None
<Accordion title="Example">
python
stream = await client.stream(FLAG_SESSION_START, body)
await client.send(stream.id, 0, follow_up_body)
</Accordion>

Send a follow-up frame on an existing correlation id. Use the id from the AgentStream returned by stream().

<p className="msb-label">Parameters</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><code>id</code><span className="msb-type">int</span></div> <div className="msb-param-desc">Correlation id of an open session, from <code>stream.id</code>.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>flags</code><span className="msb-type">int</span></div> <div className="msb-param-desc">Frame flag byte.</div> </div> <div className="msb-param"> <div className="msb-param-key"><code>body</code><span className="msb-type">bytes</span></div> <div className="msb-param-desc">CBOR-encoded protocol message body.</div> </div> </div>

<span className="msb-recv">client.</span><span className="msb-hn">ready_bytes()</span>

python
def ready_bytes(self) -> bytes
<Accordion title="Example">
python
ready = client.ready_bytes()
</Accordion>

Return the cached handshake core.ready frame body as CBOR bytes.

<p className="msb-label">Returns</p> <div className="msb-params"> <div className="msb-param"> <div className="msb-param-key"><span className="msb-type">bytes</span></div> <div className="msb-param-desc">CBOR-encoded <code>core.ready</code> frame body.</div> </div> </div>

<span className="msb-recv">client.</span><span className="msb-hn">close()</span>

python
async def close(self) -> None
<Accordion title="Example">
python
await client.close()
</Accordion>

Close the client. Calling it more than once is safe.

Types

RawFrame

<p className="msb-backref">Returned by <a href="#client-request">request()</a> · yielded by <a href="#agentstream">AgentStream</a></p>

A raw protocol frame with a CBOR-encoded body.

python
class RawFrame(TypedDict):
    id: int
    flags: int
    body: bytes
FieldTypeDescription
idintProtocol correlation id
flagsintFrame flag byte (combination of FLAG_* constants)
bodybytesCBOR-encoded protocol message body

AgentStream

<p className="msb-backref">Returned by <a href="#client-stream">stream()</a></p>

An open raw agent stream. Carries the protocol correlation id and is both an async context manager and an async iterator of RawFrame. Iteration stops once a frame with FLAG_TERMINAL set is delivered or the stream reaches EOF.

Property / MethodTypeDescription
idintProtocol correlation id; pass to send() for follow-up frames
next()Awaitable[RawFrame | None]Read the next frame; returns None at EOF
close()Awaitable[None]Release the stream handle early; safe to call more than once
async withAgentStreamAsync context manager; closes the stream on exit
async forRawFrameAsync iterator over frames until terminal or EOF
<Accordion title="Example">
python
from microsandbox import FLAG_SESSION_START, FLAG_TERMINAL

async with await client.stream(FLAG_SESSION_START, body) as stream:
    async for frame in stream:
        if frame["flags"] & FLAG_TERMINAL:
            break
</Accordion>