Skip to main content

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:

pip install vswarm

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

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.

When the control-plane API and public gateway use different origins, configure both explicitly. Relative preview and process-channel ticket URLs are resolved against gateway_base_url; API requests continue to use base_url. Unified deployments may omit gateway_base_url and it defaults to the API origin.

client = VSwarmClient(
    base_url="http://vswarm-api.internal:8080",
    gateway_base_url="https://gateway.vswarm.example",
    token="secret",
)

Recommended browser sandbox flow:

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:

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:

from vswarm import SandboxCreateRequest, WorkspaceCreateRequest

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

Product backends that act for an end user must use a separate delegation credential. Configure the server with VSWARM_DELEGATED_ACTOR_TOKEN, keep that secret out of browsers, and pass it alongside project and user. This hardened delegation boundary requires VSwarm Python SDK 0.1.3 or newer:

client = VSwarmClient(
    base_url="http://vswarm.internal:8080",
    token="api-secret",
    delegated_actor_token="product-backend-secret",
    project="project-1",
    user="user-1",
)

VSwarm rejects actor headers that are not accompanied by the delegation credential; possession of the general API token alone never grants identity impersonation.

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:

sandbox = await client.sandboxes.create(
    SandboxCreateRequest(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:

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:

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:

  • client.system.info()
  • client.require_contract(...)
  • 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)
  • client.runtime_leases.create/list/get/reconcile(...)
  • runtime lease renew/activity/checkpoint/hibernate/close/resume methods and typed variants
  • client.snapshots.create/list/get/restore(...) and typed variants
  • 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)
  • client.artifacts.open_download(id) returns a checked async stream context; use async with and response.aiter_bytes() for tree artifacts so complete archives are never buffered in SDK memory.

Workspace capability contract

await client.system.info() returns the server's typed version and enabled feature set. Applications that depend on a specific runtime contract should call await client.require_contract(...) explicitly during startup.

Workspace resources expose one canonical method per operation. Those methods return detached, recursively immutable response models; the former raw mapping methods and _typed twins are not part of the 0.1.5 API.

Runtime leases and snapshots

A workspace is durable state and may exist with zero live compute. A runtime lease is the authoritative allocation of live compute for that workspace. Checkpoint commits an immutable snapshot without releasing compute; hibernate quiesces runtime resources, commits the snapshot, and only then releases compute. Resume restores into a new runtime generation:

from vswarm import RuntimeActionRequest, RuntimeCreateRequest, WorkspaceCreateRequest

workspace = await client.workspaces.create(
    WorkspaceCreateRequest(source={"type": "empty"})
)
lease = await workspace.create_runtime_lease(
    RuntimeCreateRequest(
        template="base-ubuntu",
        client_action_id="task-42:create-runtime",
        idle_ttl_seconds=1800,
        hard_ttl_seconds=28800,
    )
)

snapshot = await lease.checkpoint(
    RuntimeActionRequest(
        client_action_id="task-42:checkpoint",
        expected_runtime_generation=lease.model.runtime_generation,
    )
)
hibernated = await lease.hibernate(
    RuntimeActionRequest(
        client_action_id="task-42:hibernate",
        expected_runtime_generation=lease.model.runtime_generation,
    )
)
resumed = await lease.resume(
    RuntimeActionRequest(client_action_id="task-42:resume")
)

Use a stable client action ID only for retries of the same normalized request. Changing a request while reusing an action ID returns idempotency_conflict. Snapshot restore verifies both archive and manifest checksums before replacing workspace data. Old runtime-scoped resource IDs do not control the new generation.

Reconnecting to a PTY

Install the optional stream dependency with pip install "vswarm[pty]". The stream descriptor keeps the API bearer credential in WebSocket headers rather than adding it to the URL:

sandbox = await client.sandboxes.get("sbx_123")
stream = await sandbox.pty.stream("pty_123", offset=last_offset)

async with stream:
    async for frame in stream:
        if frame["gap"]:
            print("terminal replay was truncated")
        consume(frame["data"])
        last_offset = frame["next_offset"]

The stream also exposes versioned input, resize, signal, and close controls. Create a fresh stream with the last acknowledged offset after a transport disconnect; a WebSocket disconnect does not terminate the authoritative PTY session.

ProcessClient.restart_group performs an identity-preserving, generation-fenced, idempotent server-side restart. 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.

Runtime leases and snapshots are reported as supported in workspace.v1. Read-only leases remain unavailable until every runtime backend can enforce a read-only workspace mount. Worker administration, deployment administration, and billing remain outside this SDK contract.

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:

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

Capacity can be inspected before or after requests:

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:

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:

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

Compatibility and releases

Workspace file changes use the bounded durable SSE stream rather than polling:

from vswarm import WorkspaceFileEventSubscribeRequest

request = WorkspaceFileEventSubscribeRequest(
    sandbox_id="sbx_123",
    runtime_generation="generation-4",
    after_sequence=17,
)
async for item in client.events.subscribe_workspace_files(request):
    print(item)

The iterator closes the response on completion, cancellation, and contract failure. EventStreamResyncRequired tells consumers to refresh their bounded workspace projection before resuming.

The SDK requires Python 3.10 or newer. The public API remains provisional while the SDK is on the 0.x release line, so consumers should pin an explicitly tested range. Releases may remove provisional compatibility surfaces when the replacement server/SDK contract is made mandatory; those removals are called out in the changelog.

Use an explicit contract requirement when an application must fail closed against an incompatible VSwarm server:

from vswarm import ContractRequirement, VSwarmClient

async with VSwarmClient(base_url="https://api.example.com", token="secret") as client:
    await client.require_contract(ContractRequirement(
        minimum_api_version="0.2.0",
        runtime_state_contract_version="4",
        required_features=frozenset({"runtime_leases", "snapshots"}),
    ))

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.

Retained byte logs (source contract; publish a matching SDK before rollout)

Task and managed-process stdout/stderr can be read during execution and after runtime teardown. The source runtime may explicitly report unsupported capture. Each page includes committed offsets, a producer epoch, completeness and loss. Completeness means the sealed watermark was drained, not that capture was lossless.

import codecs

decoder = codecs.getincrementaldecoder("utf-8")("replace")
async for page in client.processes.follow_log_pages(process_id, stream="stdout"):
    print(decoder.decode(page.data, final=page.complete), end="")
    # Persist only after consumption; pass after=cursor to resume.
    cursor = page.next_cursor
    if page.loss_reason:
        print("Capture loss:", page.loss_reason)

Use task.follow_log_pages() for a TaskRunHandle. log_page() performs one bounded read (default 32 KiB, maximum 64 KiB). Keep stream/source/cursor together. Cancelling the iterator detaches it; it never cancels or retries the workload. Transport/auth errors propagate, and stale epoch cursors are rejected.

ContractRequirement(minimum_api_version="0.2.0") checks only the base API. Workspace consumers should continue passing their required runtime-state version and feature set explicitly.

Retained log readers distinguish expired from empty output: expiry preserves identity and the committed watermark but returns no bytes. Preserve each page's cursor with its stream and producer epoch. The API defaults sealed log retention to seven days; the operator may configure a different positive duration.

Trace context scopes use task-local context variables. Nested or concurrent sessions restore their parent scope, including failed entry, cancellation, and failed finalization. Scope finalization has a five-second deadline. Synchronous trace.event(...) calls are required durable receipts, so callers must handle acknowledgement failures explicitly; these are not a best-effort telemetry queue. When workload execution already failed, cleanup preserves that primary exception. Optional distributed tracing can instrument the public HTTPX transport; W3C headers may also be supplied with RequestOptions.headers. Diagnostic X-VSwarm-* identifiers are not fabricated W3C spans.

SDK CI and release checks run python -m mypy --strict sdks/python/vswarm as well as the test suite. JSON-object and binary convenience responses are validated at the public return boundary rather than being unchecked type casts.

Concurrent process-channel consumers

Use a stable application role key when independent readers and controllers share one managed process channel:

from vswarm import ProcessChannelAttachRequest

ticket = await client.process_channels.attach_ticket(
    process_id, channel_id,
    ProcessChannelAttachRequest(
        audience="robotics", client_action_id=request_id,
        consumer_key="observer", ttl_seconds=30,
    ),
)

Repeated calls renew that consumer's existing route; other consumer keys keep their connections. Omit consumer_key to retain legacy ticket rotation. A channel admits at most four active consumer routes, each with a separate connection limit of min(parent.max_connections, 16) on each serving gateway. These are not aggregate channel or fleet limits: two roles configured with eight can admit sixteen requests on one gateway. Inactive consumers expire, renewal cannot exceed the parent expiry, and closing the channel revokes all consumers. See process-channel diagnostics for the renewable credential and deployment-wide ingress boundary.

Metadata

Release files for vswarm 0.1.23

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for vswarm 0.1.23
File Size Uploaded Provenance
vswarm-0.1.23.tar.gz 121.6 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for vswarm 0.1.23
File Interpreter ABI Platform
vswarm-0.1.23-py3-none-any.whl Python 3 none any Details

Total release size: 223.0 kB

Release files / vswarm-0.1.23.tar.gz

Download URL vswarm-0.1.23.tar.gz
Size 121.6 kB
Tags Source
SHA-256 checksum
How to use checksums
3f15d14434aa0e13c5a78f7a624ae262f9ace62a46e8e4ea6c58afa3b73bf051
BLAKE2b-256 checksum
How to use checksums
e5c0bcd2ff95eda227ac3d5e40c891b7d963efab300697bf071495224bea4fc3
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 14, 2026.

Transparency log

Release files / vswarm-0.1.23-py3-none-any.whl

Download URL vswarm-0.1.23-py3-none-any.whl
Size 101.4 kB
Tags Python 3
SHA-256 checksum
How to use checksums
b9876e153f8e2d6bd97b4cdfd20c319be6ad4e7c1b613ed5a57f8cff753bbfe2
BLAKE2b-256 checksum
How to use checksums
530c765e5c6d0b6f156511b0d6f78d849fb7ba45e8fd31e255381b03a44c9d6d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 14, 2026.

Transparency log

Security for vswarm 0.1.23

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for vswarm-0.1.23.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 14, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page