Skip to content

feat(sandbox): add the sandbox socket and the E2B provider - #2154

Merged
chaojhou merged 20 commits into
mainfrom
feature/yihan/sandbox-layer
Nov 3, 2026
Merged

chaojhou merged 20 commits into
mainfrom
feature/yihan/sandbox-layer

Conversation

@zoroyihan7

@zoroyihan7 zoroyihan7 commented Oct 23, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Adds hyperloom.sandbox: the sandbox socket, which is the interface the measurement layer creates and drives sandboxes through, and the E2B provider that implements it over the official e2b SDK. The measurement layer and the toolbox both create sandboxes through this socket. Nothing on main imports hyperloom.sandbox yet, so the change is inert until the measurement layer does.

The package is taken from multibranch-dev at 43dffa419 and matches it except for docstrings:

  • socket.py: the Sandbox and SandboxProvider protocol methods each have a one-line docstring here instead of a bare ..., because CodeQL flagged every ... as a statement with no effect (the docstring-only body is the shape hyperloom.plugs.events uses).
  • __init__.py, socket.py, e2b.py and the test file: where a docstring names the local provider (hyperloom.sandbox.local), the E2B stand-in service or the contract suite, it says here that they arrive in a later PR. Those docstrings are unchanged on multibranch-dev, where those modules exist.

Files (6 files, +1493 / -1):

  • src/hyperloom/sandbox/socket.py (203 lines): the roles (measurement, subrun, dev), CreateOptions, RunSettings, OutputSinks, SandboxInfo, CommandResult, the Sandbox and SandboxProvider protocols, the metadata keys, and the five refusals a provider reports: SandboxDenied, SandboxGone, SandboxCreateFailed, SandboxQuotaExceeded, SandboxUnavailable (all under SandboxError).
  • src/hyperloom/sandbox/__init__.py (40 lines): re-exports the socket's names.
  • src/hyperloom/sandbox/e2b.py (584 lines): the provider over the e2b SDK, against any E2B-compatible service (E2B_API_URL, E2B_SANDBOX_URL).
    • Both service URLs are required. If api_url or sandbox_url is blank, or from_environ finds E2B_API_URL or E2B_SANDBOX_URL unset or empty, construction raises ValueError naming the variable before any request is made. Without this check the SDK would fill the URL in from the process environment or default to the public E2B service (https://api.e2b.app) and send it the role's key.
    • One API key per role (HYPERLOOM_SANDBOX_KEY_<ROLE>), read once, and never logged or put in an error or repr.
    • An optional workspace (workspace=; from_environ reads HYPERLOOM_SANDBOX_WORKSPACE). When one is set, every create sends it as the metadata agent-sandbox.io/workspace, unless the caller's metadata names a workspace, which wins. A create refused with workspace_required raises SandboxDenied naming that variable. Without a workspace, creates are unchanged, since a Claw grant's key names its own.
    • Control-plane calls are retried with exponential backoff while the failure is transient (network error, 5xx, rate limit, timeout). A create is retried only when it carries an idempotency key, because only then does a replay return the same sandbox.
    • Every SDK failure the socket has a word for is classified into its exceptions; one it has none for (a bad argument, say) propagates unchanged.
    • A 410 on a create means the key's sandbox had ended before this create first sent it (SandboxGone). A create failure naming sandbox_create_failed, or a workload in phase Failed, is final and not replayed (SandboxCreateFailed), and so is a 410 idempotency_key_spent answering the create's own replay. A 429 naming grant_quota_exceeded means the session's quota is full (SandboxQuotaExceeded).
  • src/hyperloom/sandbox/tests/test_sandbox_e2b.py (658 lines, 56 tests): the provider against a canned HTTP service. It covers a particular status code, a server error that clears on the third try, a create whose sandbox failed before it ran, a replay answered with a spent key, a data plane that answers 403 or cannot be reached, key redaction, the retry budget, the workspace on every create, and a missing or blank service URL refused before any request is sent.
  • pyproject.toml (+7): the new optional dependency group hosted (e2b>=2.53.1,<3, imported only by hyperloom.sandbox.e2b), added to the test extra so the tests run in CI.
  • src/hyperloom/plugs/tests/test_architecture.py (1 line): hyperloom.sandbox leaves _DEV_ONLY, as hyperloom.hosted did in feat(hosted): add the MCP service core with token scopes and tool registration #2135. Without this, the package's own imports of hyperloom.sandbox.socket fail the "main imports nothing dev-only" guard. Which modules may import sandbox code is still governed by test_rule_2_no_module_imports_a_hosted_implementation_outside_its_owners, which also keeps import e2b inside sandbox/e2b.py. This PR's code does not import hyperloom.contracts, and .importlinter has no sandbox lines on multibranch-dev, so none are added.

What is not in this PR

The rest of hyperloom/sandbox on multibranch-dev is left for a follow-up:

  • The local provider and what only it uses: local.py, local_sandbox.py, templates.py, gpu_pool.py, gated_pool.py, identity.py, launcher.py, node.py, reaper.py, card_occupancy.py, drm_fdinfo.py, with their tests (test_sandbox_local.py, test_card_census*.py, test_card_occupancy.py, test_gated_pool.py, kernel_tree.py). They import only each other and code that is on main (hyperloom.common.outside_facts, hyperloom.common.env_safety).
  • The tests that import hyperloom.measure (the measurement runner and sandbox pool, which are not on main): conftest.py, contract_harness.py, contract/, test_contract_harness.py, test_e2b_bootstrap.py, test_e2b_standin.py, test_sandbox_own_tmp.py, and the E2B stand-in service they use (tests/e2b_standin/).

Because this PR has only one provider, the code-metrics gate prints two advisory A12 "single implementation" notes, for Sandbox and SandboxProvider. The local provider will be the second implementation.

Review template

  • Linked issue(s): none.
  • Tests: added, taken from multibranch-dev with the code. At the merge of main 0eda8afda, pytest src/hyperloom/sandbox src/hyperloom/plugs/tests/test_architecture.py passes on Python 3.10 (e2b 2.55.1) and 3.11 (e2b 2.62.0) with only src on the path: 56 provider tests and 16 architecture tests, 72 passed on each. socket.py is 100% covered and e2b.py 88% (30 of 260 statements missed), so the changed lines are above the 80% diff-coverage bar. Also run: ruff check and ruff format --check on the changed Python files, scripts/check_broad_suppress.py src scripts, and lint-imports --no-cache (12 kept, 0 broken).
  • Size/complexity triggers crossed: 827 production Python lines (e2b.py 584, socket.py 203, __init__.py 40), above the 400-line review trigger and under the 1000-line budget. The change has two main modules: an interface module and a provider module that implements it. The provider is under the ~800-line module trigger, and the tests are the provider's own.
  • Observable effect: none. Nothing on main imports hyperloom.sandbox, no CLI, entry point or flag uses it, and the legacy flow behaves exactly as before. The new hosted extra installs the e2b SDK for anyone who asks for it, and the test extra now includes it.
  • Breaking changes: no.
  • PR addresses single concern: yes (the sandbox socket and its E2B provider).
  • Root cause is upstream: no.

🤖 Generated with Claude Code

zoroyihan7 and others added 3 commits October 23, 2026 19:35
…d how they apply and differ

The recipe contract is the notebook's: a frozen, validated document that names
the image, the source commits, the patches, the kernel artifacts and the server
arguments of one configuration, and the delta that turns one recipe into
another. The measurement contract in the next commit builds on RecipeDelta, so
the two land together; this commit is the recipe half, taken as written on the
multi-branch development branch.

The contracts package starts here with an empty export list; the next commit
adds the measurement names.

Co-authored-by: siliangchen-amd <SiLiang.Chen@amd.com>
Co-Authored-By: Claude <noreply@anthropic.com>
… the two JSON Schemas

The test request a multi-branch module hands to the toolbox and the test result
it gets back, as frozen dataclasses that validate on construction and round-trip
through to_document() and from_document(). The two JSON Schemas describe the
documents of the hosted measurement: the spec the application writes into a
sandbox and the result manifest the runner signs.

The schemas ship as package data. hyperloom.contracts joins the top-level layer
contract between agents and inference_optimizer: it builds on the optimizer's
server-argument helpers (through the recipe contract) and nothing below imports
it.

Nothing on main imports the package yet; it is the vocabulary the sandbox, the
measurement runner and the toolbox share.

Co-Authored-By: Claude <noreply@anthropic.com>
The sandbox socket is the interface the measurement layer creates and drives
sandboxes through: roles, create options, run settings, output sinks, and the
refusals a provider reports (denied, gone, quota exceeded, unavailable). The
E2B provider implements it over the official e2b SDK against any E2B-compatible
service: one API key per role, control-plane calls retried with backoff while
the failure is transient, a create retried only when it carries an idempotency
key, and every SDK failure classified into the socket's exceptions.

The e2b SDK is the new optional dependency group ``hosted``; the test extra
includes it so the provider's failure-handling tests run in CI. hyperloom.sandbox
leaves the list of packages that exist only on the development branch in the
architecture guard, as hyperloom.hosted did; who may import it stays governed by
the owners rule beside it, which also keeps ``import e2b`` inside
sandbox/e2b.py.

Nothing on main imports the package yet.

Co-Authored-By: Claude <noreply@anthropic.com>
@zoroyihan7 zoroyihan7 added the type:feature New capability or improvement label Oct 23, 2026
@github-actions

github-actions Bot commented Oct 23, 2026 •

Copy link
Copy Markdown

Code metrics gate: PASSED

Judged: the 6 files this change touches. Files it does not touch never fail it; anything found there is listed under Outside this change for information.

Dimension Limit New Worse Improved Over the limit vs base
Cyclomatic complexity fails > 20 0 0 0 0 +0
Cognitive complexity fails > 30 0 0 0 0 +0
Function length (lines) fails > 60 0 0 0 0 +0
Nested block depth fails > 4 0 0 0 0 +0
Parameter count fails > 5 0 0 0 0 +0
Module length (lines) warns > 800, fails > 1200 0 0 0 40 +0
Duplicated lines per file fails > 0 0 0 0 0 +0
Dead code (vulture) fails > 0 0 0 0 0 +0
Directory size (entries) fails > 50 0 0 0 1 +0
Check Findings Effect
Comments 0
Broad catch without a reason 0
Broad catch that records nothing 0
English only 0
Production code imports test code 0
Repeated literals 0
A1 Private name of another package 0
A8 Flag argument 0
A9 Tuple return 0
A10 Raw vocabulary comparison 0
A11 Hidden global write 0
A12 Pure forwarder 0
A12 Single-implementation abstraction 2 advisory, never fails

A12 Single-implementation abstraction (advisory, never fails): 2

Where Problem
src/hyperloom/sandbox/socket.py:146 A12 single implementation: Sandbox is abstract and only _E2BSandbox (src/hyperloom/sandbox/e2b.py) implements it; use the implementation until a second one exists
src/hyperloom/sandbox/socket.py:183 A12 single implementation: SandboxProvider is abstract and only E2BSandboxProvider (src/hyperloom/sandbox/e2b.py) implements it; use the implementation until a second one exists

Verdict computed by the gate scripts in /home/runner/work/_temp/code-metrics-gate, not by this tree's copy.

Thresholds and sources: [tool.hyperloom.code_metrics] in pyproject.toml. Tools: complexipy 8.0.1, jscpd 5.4.1, radon 6.0.1, ruff 0.16.2, vulture 2.16.

Comment thread src/hyperloom/sandbox/socket.py Fixed
Comment thread src/hyperloom/sandbox/socket.py Fixed
Comment thread src/hyperloom/sandbox/socket.py Fixed
Comment thread src/hyperloom/sandbox/socket.py Fixed
Comment thread src/hyperloom/sandbox/socket.py Fixed
Comment thread src/hyperloom/sandbox/socket.py Fixed
Comment thread src/hyperloom/sandbox/socket.py Fixed
Comment thread src/hyperloom/sandbox/socket.py Fixed
Comment thread src/hyperloom/sandbox/socket.py Fixed
Comment thread src/hyperloom/sandbox/socket.py Fixed
…scanner does not flag

The generic-api-key rule of the secret scan read KEY_MEASUREMENT as a credential
(a `*_KEY` constant holding a high-entropy string). The key is a fixture the
provider tests pass to the SDK and then assert never appears in a repr, error or
log line, so any distinct string serves; the new value is all zeros after the
role name.

Co-Authored-By: Claude <noreply@anthropic.com>
zoroyihan7 and others added 6 commits October 24, 2026 06:09
Co-Authored-By: Claude <noreply@anthropic.com>
…ayer

Co-Authored-By: Claude <noreply@anthropic.com>
Conflict in src/hyperloom/contracts/__init__.py (main added the package
with a docstring only): resolved to the multibranch-dev version, which
keeps main's package docstring and adds the measurement exports.

Co-Authored-By: Claude <noreply@anthropic.com>
Updated to multibranch-dev e81281d: contracts/measurement.py, the
measurement spec and result JSON Schemas, test_measurement_contract.py
(recipe.py and test_recipe_contract.py were already identical).

Added, because measurement.py now imports them (same layer, nothing
else imported): contracts/_validate.py, consistency.py, long_context.py
and their tests (test_consistency_contract.py,
test_long_context_contract.py, test_long_context_bar.py,
long_context_checks.py).

Removed: the "contracts/*.schema.json" package-data line in
pyproject.toml; main's "contracts/*.json" already ships the schemas.

Co-Authored-By: Claude <noreply@anthropic.com>
…ayer

Co-Authored-By: Claude <noreply@anthropic.com>

# Conflicts:
#	src/hyperloom/plugs/tests/test_architecture.py
…branch-dev e81281d

Takes multibranch-dev e81281d's version of every file this PR carries:
- sandbox/socket.py, sandbox/__init__.py: new SandboxCreateFailed refusal
  (the platform made the sandbox and it failed before it ran).
- sandbox/e2b.py: a create failure naming sandbox_create_failed or a
  workload in phase Failed is final, not retried; a 410
  idempotency_key_spent answering a create's own replay is
  SandboxCreateFailed, a 410 on the first send stays SandboxGone.
- sandbox/tests/test_sandbox_e2b.py: tests for both (42 tests). The one
  fixture value the secret scan flagged stays e2b_measurement_key_000000000000.

No file was added or removed; pyproject.toml and test_architecture.py keep
this PR's earlier change (hosted extra; hyperloom.sandbox leaves _DEV_ONLY).

Co-Authored-By: Claude <noreply@anthropic.com>
…ner does not flag

Brings test_sandbox_e2b.py up to multibranch-dev d8755f4.

Co-Authored-By: Claude <noreply@anthropic.com>
Base automatically changed from feature/yihan/measurement-contract to main November 2, 2026 01:51
zoroyihan7 and others added 4 commits November 2, 2026 03:10
The measurement contract (#2153) is on main now; the contracts take main's
version.

Co-Authored-By: Claude <noreply@anthropic.com>
…s instead of ellipses

CodeQL flags each bare `...` as a statement with no effect; the docstring-only
body is the shape the other protocols in the tree (hyperloom.plugs.events) use.

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
Main squash-merged #2335, #2336, #2339, #2340, #2343, #2344, #2345, #2346, #2347 and #2348 since the last merge. No conflicts; the diff against main is still this PR's six files.

Co-Authored-By: Claude <noreply@anthropic.com>
@zoroyihan7
zoroyihan7 marked this pull request as ready for review November 3, 2026 05:12
@zoroyihan7
zoroyihan7 requested a review from a team as a code owner November 3, 2026 05:12
zoroyihan7 and others added 2 commits November 3, 2026 07:02
…ured

Board hyperloom-multibranch #156 c5098 / c4103. A SaFE key used directly against
the sandbox service, from a user who is in more than one workspace, has every
create refused with 400 workspace_required: the create sends no
agent-sandbox.io/workspace. A system-admin key always needs one; the platform
gives no default. A Claw grant's key names its own workspace and is not affected.

- E2BSandboxProvider takes workspace= (from_environ reads
  HYPERLOOM_SANDBOX_WORKSPACE; unset or empty names none) and adds it to every
  create's metadata as agent-sandbox.io/workspace. A workspace the caller's
  metadata names itself wins; the caller's mapping is not changed. Without one
  the metadata is exactly the caller's, as before.
- A create refused with workspace_required raises SandboxDenied naming
  HYPERLOOM_SANDBOX_WORKSPACE, the role and what the create named, keeping the
  service's own message. It is not replayed. Other refusals are unchanged.
- WORKSPACE_KEY joins the caller-written agent-sandbox.io keys in the socket.

Tests against the canned control plane: the workspace goes with creates of
both roles and a caller's own wins; from_environ sends one only when the
variable is set (unset, as on Claw, sends the caller's metadata only); the
refusal names the variable and is sent once; another 400 keeps its message.

Co-Authored-By: Claude <noreply@anthropic.com>
…m asked

The code-metrics gate refuses a comment that points at a PR or issue: the
two comments added in 333ed66 named the board item. The history is in that
commit's message.

Co-Authored-By: Claude <noreply@anthropic.com>

@chaojhou chaojhou left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR #2154 -- hyperloom.sandbox: the sandbox socket and its E2B provider

What it does: adds hyperloom.sandbox, taken from multibranch-dev 018cccb:

  • socket.py is the interface the measurement layer will create and drive sandboxes through: roles, options, results, and the Sandbox/SandboxProvider protocols with their refusals.
  • e2b.py is a provider over the official e2b SDK, with one API key per role, bounded retries, and failure classification.
  • pyproject.toml gains an optional hosted extra (e2b>=2.53.1,<3), which the test extra now pulls in.

Nothing on main imports the package yet.

Risk: high -- pyproject.toml adds a third-party SDK dependency (e2b, plus native pyqwest, connectrpc, protobuf-py) to the test extra and a new hosted extra
Decisions for a human:

  • pyproject.toml -- every test install now pulls e2b and its native pyqwest wheel; licenses are MIT/Apache-2.0/BSD; e2b 2.62 needs Python >=3.11, so 3.10 resolves to 2.53.x -- is a hosted-only SDK in the test extra acceptable?
  • src/hyperloom/sandbox/e2b.py -- with E2B_API_URL unset, from_environ (:343, _params :325-331) inherits the SDK default and sends the role key to https://api.e2b.app. A probe at head confirmed X-API-KEY reaching that host. This is the SDK's documented default and nothing on main reaches it today -- should the provider refuse a missing endpoint before the measurement layer wires it in?
  • src/hyperloom/sandbox/socket.py, init.py -- Sandbox and SandboxProvider have one implementation (advisory A12 from the gate). The docstrings name hyperloom.sandbox.local and the E2B stand-in, which are not in this PR, and SandboxError says "three failure classes" where five are defined (socket.py:6-8, :60; init.py:4) -- keep them byte-identical with multibranch-dev, as the author proposes, or reword them here?

Blocking issues: 1

  1. [X2] The description's sizes, totals, test count and source commit predate the last two commits [verified]
    Problem: the body still describes the first push:

    • body "Files (6 files, +1308 / -1)", socket.py "(199 lines)", e2b.py "(529 lines)", the test file "(532 lines)";
    • Tests: "42 provider tests ... 58 passed";
    • Size triggers: "768 production Python lines", with the interface module at 199 lines and the provider at 529;
    • Summary: byte-identical to multibranch-dev 40fefcc.

    At 4b37717, git diff --shortstat against main shows 6 files, +1438/-1, and wc -l gives socket.py 203, e2b.py 571 and test_sandbox_e2b.py 616, for 814 production lines. The paragraph added with f86d8e7/4b377175b already states 571/616/203, 66 tests and 018cccb, so the body contradicts itself.
    Impact: a squash merge makes this body the commit message and the release-note source, so it records sizes, a test count and a source commit that the merged tree does not have. That includes the 768-line figure the size-trigger justification rests on.
    Action: update the Summary, the file bullets, the test count and the size-trigger figures to the head numbers, and name 018cccb once. No code change.

Checked: the 6 files at 4b37717. Every SDK name e2b.py uses (exception classes, Sandbox.connect/get_info/set_timeout/kill/list, SandboxQuery, ApiParams keys) against e2b 2.53.1 and 2.62.0. The ordering of _classify and _is_transient (the SandboxNotRunning and SandboxUnreachable subclasses of TimeoutException). The retry deadline, and that a keyless create is never replayed (retries=0 turns off the SDK's RetryableTransport). Key handling: provider repr, exception messages, tracebacks and SDK logging carry no key; an in-memory httpx request remains on __cause__. Import of hyperloom.sandbox and .socket without e2b installed. test_architecture.py: hyperloom.sandbox leaves _DEV_ONLY, stays in _HOSTED, and rule 2 keeps import e2b in sandbox/e2b.py, as #2135 did for hyperloom.hosted. The licenses of the new transitive dependencies. Independent readers: three lenses (shape, credentials, SDK behaviour and packaging), each with two adversarial verifiers per finding | Ran: test_sandbox_e2b.py against e2b 2.53.1 and 2.62.0 in fresh venvs (50/50 each); a probe with E2B_* unset and httpx.Client.send intercepted; git diff --shortstat and wc -l at head; git merge-tree against main c18af70 (clean); CI at 4b37717: 12 test shards, coverage on 3.10 and 3.11, code-metrics, pr-hygiene and import-linter succeed | Skill: 3eec7f38c6e3 | Model: claude-opus-5-5 | Base: f60df7f | Head: 4b37717

zoroyihan7 and others added 2 commits November 3, 2026 07:50
…later

SandboxError and the socket's module docstring said "three" failure
classes where five are defined. The docstrings that name the local
implementation, the E2B stand-in and the contract suite now say those
arrive in a later PR, since this branch does not carry them.

Co-Authored-By: Claude <noreply@anthropic.com>
…efault

Without api_url or sandbox_url the e2b SDK falls back to E2B_API_URL /
E2B_SANDBOX_URL in the process environment and then to the public E2B
service, so a provider built without them sent the role's key to
https://api.e2b.app. Both URLs are now required keyword arguments; a blank
one, or one from_environ cannot find, raises ValueError naming its
variable before any request is made.

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Claude <noreply@anthropic.com>
@chaojhou

chaojhou commented Nov 3, 2026

Copy link
Copy Markdown
Collaborator

PR #2154 -- hyperloom.sandbox: the sandbox socket and its E2B provider

What it does: adds hyperloom.sandbox, taken from multibranch-dev 43dffa4:

  • socket.py is the interface the measurement layer will create and drive sandboxes through: roles, options, results, and the Sandbox/SandboxProvider protocols with their five refusals.
  • e2b.py is a provider over the official e2b SDK, with one API key per role, bounded retries and failure classification. Since f8f0aa7 it requires both service URLs, and a blank one raises ValueError before any request, so a role key never reaches the SDK's public-cloud default.
  • pyproject.toml gains an optional hosted extra (e2b>=2.53.1,<3), which the test extra now pulls in.

Nothing on main imports the package yet.

Risk: high -- pyproject.toml adds a third-party SDK dependency (e2b, plus native pyqwest, connectrpc, protobuf-py) to the test extra and a new hosted extra
Decisions for a human:

  • pyproject.toml -- every test install now pulls e2b and its native pyqwest wheel (licenses MIT/Apache-2.0/BSD; Python 3.10 resolves e2b 2.55.x, 3.11 resolves 2.62.0) -- is a hosted-only SDK in the test extra acceptable?

Blocking issues: none

Checked: the fc65295 delta: _require_url and the required api_url/sandbox_url in E2BSandboxProvider, with _params folded into the constructor; from_environ now refusing an unset or empty E2B_API_URL/E2B_SANDBOX_URL; the docstrings now count five failure classes and mark the local provider and the stand-in as arriving later; every E2BSandboxProvider( caller in the head tree (only test_sandbox_e2b.py, all passing both URLs); the body's numbers against git diff --shortstat and wc -l; the merge of main as fc65295 adds nothing to the six files. The first round's three lenses (shape, credentials, SDK behaviour and packaging), each with two adversarial verifiers per finding, cover the unchanged code | Ran: pytest src/hyperloom/sandbox/tests at fc65295 against e2b 2.62.0 (56 passed); git merge-tree against main 0eda8af (clean); CI at fc65295: 32 success, 5 skipped | Skill: 3eec7f38c6e3 | Model: claude-opus-5-5 | Prior: X2 fixed | Base: 0eda8af | Head: fc65295

LGTM

@zoroyihan7

Copy link
Copy Markdown
Collaborator Author

@chaojhou thanks for the review. All four items are addressed at fc65295. CI is green, and the files are those of multibranch-dev 43dffa4 except for the docstring sentences below.

[X2] Stale description: rewritten at the head.

  • 6 files, +1493/-1.
  • e2b.py is 584 lines, socket.py 203, __init__.py 40 and the test file 658.
  • 56 provider tests plus 16 architecture tests: 72 pass on 3.10 and 3.11.
  • 827 production lines for the size triggers.
  • The source commit is named once.

The owner (Yihan) decided the three questions:

  1. e2b in the test extra: kept, so pyproject.toml is unchanged. The SDK and its transitive dependencies are MIT, Apache-2.0 or BSD, and Python 3.10 resolves to a 2.5x SDK above the 2.53.1 floor.
  2. Missing endpoint: the provider now refuses it.
    • api_url and sandbox_url are required keyword arguments.
    • A blank value, or from_environ without E2B_API_URL or E2B_SANDBOX_URL, raises a ValueError naming the variable before any request.
    • E2B_SANDBOX_URL is required as well. Without it the SDK would send commands, files and the sandbox access token to e2b.app.
    • A test intercepts httpx.Client.send and asserts that nothing is sent. With the guard removed, the request goes to https://api.e2b.app.
    • Every caller on multibranch-dev already passes both URLs.
  3. Docstrings:
    • SandboxError and the socket module now say five failure classes, here and on multibranch-dev.
    • Here, the docstrings that name hyperloom.sandbox.local, the E2B stand-in or the contract suite say those arrive in a later PR. On multibranch-dev, where the modules exist, they still name them.

Could you take another look?

@chaojhou
chaojhou dismissed their stale review November 3, 2026 10:15

X2 fixed at fc65295; merging with the maintainer's consent.

@chaojhou chaojhou left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. Clean review at fc65295; merges clean on latest main. Merging with the maintainer's consent to the decisions on the card.

@chaojhou
chaojhou merged commit 34a4e6d into main Nov 3, 2026
44 checks passed
@chaojhou
chaojhou deleted the feature/yihan/sandbox-layer branch November 3, 2026 10:15
zoroyihan7 added a commit that referenced this pull request Nov 3, 2026
main's sandbox package (#2154) and this branch's measure package are both
on main now, so the architecture test's dev-only list keeps only
hyperloom_multibranch.

Co-Authored-By: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

type:feature New capability or improvement

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants