Skip to content

feat(sandbox): expand sandbox provider catalog with E2B, Modal, Runloop, Vercel, and LangSmith - #8

Merged
AliMuhammadOfficial merged 9 commits into
mainfrom
feat/sandbox-providers
Jul 24, 2026
Merged

feat(sandbox): expand sandbox provider catalog with E2B, Modal, Runloop, Vercel, and LangSmith#8
AliMuhammadOfficial merged 9 commits into
mainfrom
feat/sandbox-providers

Conversation

@AliMuhammadOfficial

Copy link
Copy Markdown
Member

Pull Request

Summary

AnyCode shipped exactly one remote sandbox backend (Daytona). This PR brings the catalog to parity with the LangChain deepagents sandbox lineup: E2B, Modal, Runloop, Vercel Sandbox, and LangSmith now implement the SandboxProvider protocol alongside Daytona, each behind its own install extra with lazy SDK imports and honest capability reports. Observable changes:

  • New public providers E2BSandboxProvider, ModalSandboxProvider, RunloopSandboxProvider, VercelSandboxProvider, LangSmithSandboxProvider, plus a create_sandbox_provider(name) factory (all exported from the package root and anycode.sandbox).
  • New extras: sandbox-e2b, sandbox-modal, sandbox-runloop, sandbox-vercel, sandbox-langsmith — each with a matching CI optional-extras matrix entry (enforced by tests/test_ci.py) and refreshed uv.lock. Deliberately not added to the all extra (Modal is heavyweight; the Vercel Python SDK is beta).
  • SandboxSpec.secret_references values are now generic provider-prefixed references (<provider>:<name>) instead of daytona:-only; each backend validates its own prefix at create time and returns sandbox_secret_reference_invalid for foreign prefixes.
  • Fail-closed contract: a backend that cannot enforce a requested network mode, snapshot restore, or secret scheme fails create() with a typed error (sandbox_network_policy_unsupported, sandbox_snapshot_unsupported, sandbox_secrets_unsupported) — never silently granting more than requested.
  • Capability honesty: the new backends declare command_streaming=False (buffered v1 — output delivered after command completion, streaming contract still satisfied); Modal (snapshot_filesystem) and Runloop (snapshot_disk) declare and implement real snapshots; every report carries an explicit limitations tuple.

Change type

  • Bug fix
  • Backward-compatible feature
  • Documentation
  • Breaking change
  • Test or internal maintenance
  • Security-sensitive change handled through a private advisory

Compatibility

  • Version impact: minor
  • Python API: additive exports only; the SandboxProvider protocol is unchanged. The SandboxSpec secret validator is loosened, not tightened — every previously valid spec (daytona:* references) still validates, and plaintext values are still rejected. Daytona now additionally rejects foreign-prefixed references at create() with a typed error; that input was previously unreachable (model validation rejected it), so no working caller changes behavior. CLI, YAML/TOML, checkpoints, durable run data: none.
  • Deprecation or migration path: none required.
  • Rollback or persisted-data impact: none — no persisted formats touched; new extras are opt-in.

Verification

New tests/test_sandbox_providers.py: a shared lifecycle-conformance helper (create → execute → stream → file write/read roundtrip → cancel → snapshot → destroy → health, evidence digests sha256:*, monotone stream sequences, isinstance(provider, SandboxProvider)) run against all five backends with injected fake clients — fully offline, no SDK installs needed. Per-provider tests cover network fail-closed, secret rejection/prefix mapping (Modal modal:<name>Secret.from_name), Runloop blueprint/snapshot argument mapping, Vercel runtime mapping, LangSmith shell-based file transfer including the missing-file error path, and missing-SDK pip install "anycode-py[sandbox-<name>]" guidance via sys.modules monkeypatching. tests/test_sandbox.py extended for the generalized secret prefixes.

  • Regression or behavior tests added or updated
  • uv run python -m pytest
  • uv run python -m ruff check .
  • uv run python -m ruff format --check src/
  • uv run python -m pyright
  • uv run python -m mkdocs build --strict (plus scripts/check_docs.py: 409 exports validated)

Documentation and release notes

  • Public docstrings and reference pages match the implementation (reference/public-api.md Sandboxes section lists all backends + factory; the generated API inventory picks up the new exports at build time)
  • Guides, examples, README, and site_docs/llms.txt updated where needed (guides/sandbox-providers.md gained a provider-selection matrix and factory section; getting-started/installation.md extras table now lists all sandbox extras — the base sandbox extra was previously missing there)
  • [Unreleased] changelog entry added, or no user-visible change
  • Security, configuration, or release documentation updated where needed (credentials stay in each SDK's own configuration: E2B_API_KEY, Modal tokens, RUNLOOP_API_KEY, VERCEL_TOKEN/VERCEL_OIDC_TOKEN + team/project ids, LANGSMITH_API_KEY)

Reviewer notes

  • Design tradeoff — buffered streaming: all five new backends implement stream() as buffered execute (shared buffered_stream helper in _base.py) and declare command_streaming=False. Live streaming exists in some SDKs (E2B background commands, Modal stdout iteration) but claiming it requires verification against each real SDK surface; upgrading a backend later is additive. This is the capability-honesty rule: never claim what is not implemented.
  • SDK-surface tolerance: adapters call SDKs through call_maybe_async (handles sync/async/.aio variants) and getattr-based response parsing, mirroring the defensive style of the existing Daytona adapter. All constructors accept an injected client, so tests never import an SDK.
  • LangSmith file transfer runs through in-sandbox base64 shell commands (the SDK surface is run()-centric); the capability report's limitations note the image must provide a POSIX shell and base64.
  • Areas needing careful review: the fail-closed network guards per backend (E2B/Runloop/Vercel/LangSmith = unrestricted-only; Modal = none/CIDR-allowlist, domain allowlists fail), and the Modal Secret.from_name mapping.
  • Follow-up work (recorded in the internal spec): wiring tools/bash.py through a SandboxProvider backend; live streaming upgrades after real-SDK verification; real-credential integration tests.

@AliMuhammadOfficial
AliMuhammadOfficial merged commit a0eb55e into main Jul 24, 2026
34 of 35 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant