Metadata-Version: 2.4
Name: vswarm
Version: 0.1.1
Summary: Async Python SDK for the VSwarm runtime platform
Author: Akshay Painjane
License-Expression: Apache-2.0
Project-URL: Homepage, https://pypi.org/project/vswarm/
Project-URL: Documentation, https://pypi.org/project/vswarm/#description
Project-URL: Changelog, https://pypi.org/project/vswarm/#history
Keywords: agents,browser-automation,runtime,sandbox,vswarm
Classifier: Development Status :: 3 - Alpha
Classifier: Framework :: AsyncIO
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Typing :: Typed
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
License-File: NOTICE
Requires-Dist: httpx<1,>=0.26
Requires-Dist: packaging<27,>=24
Provides-Extra: test
Requires-Dist: pytest>=8.0; extra == "test"
Requires-Dist: pytest-asyncio>=0.23; extra == "test"
Dynamic: license-file

# VSwarm Python SDK

The VSwarm Python SDK provides typed, asynchronous access to VSwarm workspaces,
sandboxes, processes, browser sessions, traces, artifacts, and runtime APIs.

Install the published SDK from PyPI:

```bash
pip install vswarm
```

For local VSwarm monorepo development, install this SDK in editable mode:

```bash
pip install -e "sdks/python[test]"
```

The Python SDK is distributed under the Apache License 2.0. That license applies
to this SDK distribution; it does not change the licensing or visibility of the
VSwarm server, gateway, deployment, or other private monorepo components.

Recommended browser sandbox flow:

```python
from vswarm import Admission, BrowserPolicy, RequestOptions, Viewport, VSwarmClient

async with VSwarmClient(base_url="http://127.0.0.1:8080", token="secret") as client:
    async with client.browser.session(
        template="browser-chromium",
        policy=BrowserPolicy(allowed_domains=["example.com"]),
        viewport=Viewport(width=1440, height=900),
        admission=Admission.fail_fast(),
    ) as browser:
        obs = await browser.observe_for_agent(screenshot=True, compact_elements=True)

        await browser.click(
            obs.element(text="Go"),
            observe_after=True,
            observation={"include": {"screenshot": True, "dom": True}},
        )
```

This is the primary SDK UX: the SDK asks VSwarm to create an owned browser session, the server creates or claims the sandbox, and cleanup is automatic on success, error, or cancellation. If the server has a compatible warm `browser-chromium` sandbox available, this path can avoid cold container startup. `client.browser_session(...)` remains as a compatibility alias for the same owned workflow.

Production services that create a session in one request and destroy it later can use detached owned
session creation while still relying on SDK cleanup semantics:

```python
browser = await client.browser.create_session(
    template="browser-chromium",
    policy=BrowserPolicy(allowed_domains=["example.com"]),
    viewport=Viewport(width=1440, height=900),
    admission=Admission.queue(timeout_seconds=30),
    options=RequestOptions(request_id="agent-task-create-123"),
)

try:
    obs = await browser.observe_for_agent(
        options=RequestOptions(request_id="agent-task-observe-123"),
    )
finally:
    await browser.cleanup(missing_ok=True)
```

`RequestOptions.request_id` sets `X-VSwarm-Request-ID` for correlation. It does not imply
idempotency.

Workspace and sandbox creation support explicit, persisted idempotency keys. Reusing a key
with equivalent normalized input returns the original resource with `idempotent_replay=true`;
reusing it with different input raises `VSwarmConflictError` with code
`idempotency_conflict`. This makes a caller-controlled retry after an ambiguous transport
failure safe:

```python
async with VSwarmClient(
    base_url="http://vswarm.internal:8080",
    token="secret",
    connect_timeout=10,
    timeout=60,
) as client:
    workspace = await client.workspaces.create(
        client_action_id="my-service:operation-123:workspace",
        source={"type": "empty"},
    )
    sandbox = await client.sandboxes.create(
        client_action_id="my-service:operation-123:sandbox",
        template="base-ubuntu",
        workspace={"id": workspace.id, "mount_path": "/workspace", "mode": "read_write"},
    )
```

The SDK does not retry creates automatically. Callers should retry only operations carrying a
stable idempotency key, and should keep retry counts bounded.

Advanced lifecycle control is still available when a caller needs to attach browser sessions to a long-lived sandbox:

```python
sandbox = await client.sandboxes.create(template="browser-chromium")
browser = await sandbox.browser.sessions.create(policy=BrowserPolicy(block_localhost=True))
try:
    obs = await browser.observe()
    await browser.action("click", target={"selector": "button"})
finally:
    await browser.close()
    await sandbox.destroy()
```

One-shot run execution uses the canonical runs namespace:

```python
result = await client.runs.execute(
    template="code-runner-multilang",
    profile="python",
    files={"/workspace/main.py": "print(input())"},
    stdin="hello",
    timeout_seconds=20,
)
```

Run-scoped trace context is available through the canonical traces namespace and the `client.trace(...)` async context manager:

```python
async with VSwarmClient(base_url="http://127.0.0.1:8080", token="secret") as client:
    async with client.trace(name="browser-login-task", capture_profile="debug", client_run_id="eval-42") as trace:
        await trace.event("client.agent.step.started", {"step": "open-login"}, step_id="step-open-login")

        async with client.browser.session(template="browser-chromium", trace=trace) as browser:
            obs = await browser.observe_for_agent()
            await browser.click(obs.element(text="Sign in"), observe_after=True)

        await trace.download_bundle("vswarm-trace-browser-login-task.zip")
```

While a trace context is active, the SDK propagates `X-VSwarm-Trace-ID`, `X-VSwarm-Client-Run-ID`, and optional step/parent headers on VSwarm calls. Client-side events stay under `client.*` ownership and are stored alongside VSwarm runtime/browser/artifact events in the same ordered trace.

Available SDK surface:

- `VSwarmClient.health()`
- `VSwarmClient.browser_capacity()`
- `VSwarmClient.reconcile_browser()`
- `client.browser.session(...)`
- `client.browser.create_session(...)`
- `client.browser.capacity()`
- `client.browser.reconcile()`
- `client.browser.attach(...)`
- `client.browser_session(...)`
- `client.runs.execute(...)`
- `client.trace(...)`
- `client.traces.create/list/get/events/artifacts/export/download_bundle(...)`
- `client.templates.list()` and `client.templates.get(name)`
- `client.sandboxes.create(...)`, `get(id)`, and `destroy(id)`
- `sandbox.exec(...)`
- `sandbox.files.list/read/write(...)`
- `sandbox.browser.sessions.create(...)` and `sandbox.browser.create_session(...)`
- typed `browser.observe(...)` returning `BrowserObservation`
- `browser.observe_for_agent(...)`
- typed helpers: `browser.navigate`, `click`, `double_click`, `hover`, `fill`, `type`, `press`, `scroll`, `drag`, `select_option`, `wait`, `get_text`, `find_text`, `extract_page`, `extract_section`, `capture_page`, `query_elements`, `screenshot`, `upload_file`, `evaluate`
- typed `browser.act(...)` for browser action objects
- raw `browser.action(...)` escape hatch
- `browser.batch(...)`
- `browser.actions()` and `browser.observations()`
- `browser.cleanup(missing_ok=True)`, `browser.close(missing_ok=True)`, and public `browser.sandbox_id` / `browser.session_id`
- `observation.compact_elements()` and `observation.element(...)`
- `browser.latest_screenshot_base64(observation)`
- `client.artifacts.get(id)`, `download_bytes(id)`, and `download_base64(id)`

### Workspace capability contract

`client.contract_version` is currently `workspace.v1`, and
`client.sdk_surface_descriptor` describes the methods and models implemented by
this SDK release. It is a static SDK-surface descriptor, not a claim about the
connected server. The descriptor is immutable and can also be imported as
`SDK_SURFACE_CAPABILITIES`. `client.capability_descriptor` and
`WORKSPACE_CAPABILITIES` remain backward-compatible aliases. Use
`await client.features()` or server-specific discovery to inspect capabilities
reported by the connected server.

Methods present in 0.1.0 continue to return ordinary mutable dictionaries.
Additive methods ending in `_typed` return detached, recursively immutable
response models that also support mapping-style reads such as
`process["state"]`:

- `client.workspaces.list_typed` and `workspace.usage_typed`
- `client.sessions.list_typed/get_typed` plus `close`
- `client.sandboxes.list_typed` and `sandbox.exec_typed`
- `sandbox.files.list_typed/stat_typed/search_typed`
- process, runtime-state, listener, process-group, and log `*_typed` methods
- route, preview attachment, PTY, artifact, and event `*_typed` methods

`native_process_restart` is `False` in the SDK-surface descriptor. The existing
`ProcessClient.restart` helper is a compatibility composition that stops a
process group and starts a new process; it is not an identity-preserving server
restart operation. The SDK parser supports runtime-state contract version `4`,
including aggregate `version` and `runtime_generation_version` fields.

Request helpers build detached payloads and do not mutate caller-owned command,
intent, endpoint, metadata, or options mappings.

Snapshots are intentionally reported as unsupported in `workspace.v1`: VSwarm
does not currently expose a public workspace snapshot API. Worker administration,
deployment administration, billing, and future snapshot/restore operations are
also outside this SDK contract until corresponding public APIs are available.
The SDK does not issue raw fallback requests for these future capabilities.

The default transport uses `httpx.AsyncClient` lazily. Tests and downstream apps can inject a fake async transport with a compatible `request(...)` method.

`browser.observe_for_agent(...)` is the bounded prompt/multimodal path. It sends server-side limits for `max_elements`, `max_dom_bytes`, and `max_screenshot_bytes`, requests compact elements, and only asks for screenshot base64 when `screenshot_base64=True`.

Browser actions return `BrowserActionResult` objects for page-state failures as well as successes. Recoverable failures such as `stale_element_ref`, `selector_not_found`, `action_timeout`, and `policy_blocked` are exposed through `result.code`, `result.message`, and `result.recoverable`.

## Admission and capacity

Browser sessions support typed admission options:

```python
async with client.browser.session(
    template="browser-chromium",
    admission=Admission.fail_fast(),
) as browser:
    ...
```

```python
async with client.browser.session(
    template="browser-chromium",
    admission=Admission.queue(timeout_seconds=30),
) as browser:
    ...
```

Capacity can be inspected before or after requests:

```python
capacity = await client.browser.capacity(template="browser-chromium")
print(capacity.available_slots)
print(capacity.raw.get("warm_pool_ready"))
```

Capacity failures expose retry metadata when the server provides it:

```python
from vswarm import VSwarmCapacityError

try:
    async with client.browser.session(admission=Admission.fail_fast()) as browser:
        ...
except VSwarmCapacityError as exc:
    print(exc.retry_after_seconds)
    print(exc.capacity)
```

Run the live local browser smoke test against a running VSwarm API:

```bash
PYTHONPATH=sdks/python python sdks/python/scripts/live_browser_smoke.py --api http://127.0.0.1:8080
```

## Compatibility and releases

The SDK requires Python 3.10 or newer. Public SDK versions follow Semantic
Versioning. While the SDK remains on the `0.x` release line, minor releases may
intentionally evolve provisional APIs; patch releases remain backward-compatible.

Use `min_api_version` with `compatibility="strict"` when an application must
fail closed against an older VSwarm API:

```python
async with VSwarmClient(
    base_url="https://api.example.com",
    token="secret",
    min_api_version="0.1.0",
    compatibility="strict",
) as client:
    await client.health()
```

## License

The VSwarm Python SDK is licensed under the Apache License 2.0. This SDK license
does not apply to the private VSwarm server or other monorepo components.
