feat(sdk): filesystem-only auto-pause via lifecycle.onTimeout object form#1471
Conversation
🦋 Changeset detectedLatest commit: 762d945 The changes in this PR will be included in the next version bump. This PR includes changesets to release 2 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
PR SummaryMedium Risk Overview Create paths normalize the object form, reject Reviewed by Cursor Bugbot for commit 762d945. Bugbot is set up for automated code reviews on this repo. Configure here. |
Package ArtifactsBuilt from eed1974. Download artifacts from this workflow run. JS SDK ( npm install ./e2b-2.30.7-feat-auto-pause-filesystem-only.0.tgzCLI ( npm install ./e2b-cli-2.13.1-feat-auto-pause-filesystem-only.0.tgzPython SDK ( pip install ./e2b-2.29.6+feat.auto.pause.filesystem.only-py3-none-any.whl |
814e9f7 to
09c9826
Compare
|
Addressed the two actionable points from the Cursor summary (pushed):
The third point (live cold-boot e2e depends on the backend JS typecheck/lint and Python ruff all pass. |
1bed5d8 to
f259b02
Compare
|
Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits. |
| for _ in range(150): | ||
| if not sbx.is_running(): | ||
| break | ||
| sleep(0.2) |
There was a problem hiding this comment.
this could potentially halt CI for 150*0.2*n
Adds a keepMemory option (keep_memory in Python) to the sandbox lifecycle config. When false and onTimeout is pause, a timeout auto-pause takes a filesystem-only snapshot (no memory) instead of a full memory one, so the sandbox cold-boots on resume. Defaults to true. The option maps to the autoPauseMemory create field. Combining it with auto-resume is rejected client-side, since a filesystem-only snapshot can only be resumed explicitly. Applies the change to JS and both sync and async Python, with payload, validation, and end-to-end tests, plus a changeset. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Signed-off-by: Babis Chalios <babis.chalios@e2b.dev>
The deprecated Sandbox() constructor doesn't accept lifecycle; the lifecycle option is on Sandbox.create(). Matches the JS example and the live e2e tests. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Signed-off-by: Babis Chalios <babis.chalios@e2b.dev>
E2B_DEBUG short-circuits Sandbox.create to a debug sandbox before the client-side lifecycle validation runs, so under E2B_DEBUG the four keep_memory/auto_resume rejection tests (Python sync+async) and their two JS equivalents would fail with DID NOT RAISE. Gate them with skip_debug / skipIf(isDebug), matching the e2e tests in the same files. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Signed-off-by: Babis Chalios <babis.chalios@e2b.dev>
lifecycle.get('keep_memory', True) only applies the default when the key is
absent, so an explicit None read as False — wrongly tripping validation and
sending autoPauseMemory: null. Default a missing/None keep_memory to True,
matching the JS SDK's `?? true`.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Babis Chalios <babis.chalios@e2b.dev>
A timeout auto-pause with keep_memory=None resumes the same sandbox in place (boot id unchanged), proving None defaults to a full memory snapshot rather than being wrongly treated as filesystem-only (which would cold-boot). sync + async. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Signed-off-by: Babis Chalios <babis.chalios@e2b.dev>
Move keepMemory/keep_memory from a top-level lifecycle field into the
onTimeout option, which now accepts either the bare action ('pause' /
'kill') or an object form. The object form is a discriminated union on
action: keepMemory is only valid with action 'pause', so pairing it with
'kill' is a compile-time type error (TS) / static error (ty). For untyped
callers that bypass the type, both SDKs additionally raise
InvalidArgumentError/InvalidArgumentException at runtime.
The bare string form is unchanged (back-compatible), and autoResume stays
a top-level lifecycle field.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Signed-off-by: Babis Chalios <babis.chalios@e2b.dev>
…ly auto-pause Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Signed-off-by: Babis Chalios <babis.chalios@e2b.dev>
- Fold Literal["pause","kill"] into the SandboxOnTimeout union so the on_timeout field is just `SandboxOnTimeout` (matches the JS type). - autoResume/auto_resume docs: revert to the clearer `onTimeout`/`on_timeout` wording and note it is not supported when keepMemory/keep_memory is false. - Drop the self-explanatory auto_pause_memory body comments (JS + Python). - Tests: use sandbox.files instead of cat/echo in the reboot tests and drop the redundant isRunning()/is_running() asserts (covered by getInfo().state). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Signed-off-by: Babis Chalios <babis.chalios@e2b.dev>
- Reword the client-side guard errors to match review (JS + Python): keepMemory "only allowed when action is 'pause'", drop "resolved" from the autoResume error, and the autoResume+keepMemory error now reads "autoResume: true is not a valid value when keepMemory: false ... must be resumed explicitly using Sandbox.connect()". - Drop the redundant auto_pause_memory payload-serialization test (sync + async); the live cold-boot e2e already covers that field end to end. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Signed-off-by: Babis Chalios <babis.chalios@e2b.dev>
Drop the vague 'resolved action' phrasing from the auto_resume note in the sync + async create() docstrings, per review. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Signed-off-by: Babis Chalios <babis.chalios@e2b.dev>
e252d90 to
a26c55c
Compare
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit a26c55c. Configure here.
Normalizing lifecycle.on_timeout branched into the object path for anything that wasn't a str, so an untyped caller passing on_timeout=None (or any other non-dict) hit `.get()` on it and raised AttributeError. Branch on dict instead and pass everything else through as the action, so a non-"pause" value falls back to kill semantics like a missing on_timeout — matching the prior behavior. Adds a regression test in both SDKs. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

Filesystem-only auto-pause (
onTimeoutobject form)Adds an object form to the sandbox lifecycle
onTimeout(on_timeoutin Python) that controls the snapshot kind taken when a sandbox auto-pauses on timeout, viakeepMemory(keep_memory).onTimeoutnow accepts either the existing bare action ('pause'/'kill') or the object form{ action, keepMemory }. WhenkeepMemoryisfalse(withaction: 'pause'), a timeout auto-pause takes a filesystem-only snapshot (no memory) instead of a full memory one, so the sandbox cold-boots (reboots) from disk on resume — losing running processes and open connections. Defaults totrue(full memory snapshot), so existing callers are unaffected. The bare string form is unchanged.It's the create-time / auto-pause counterpart to the explicit
pause(keepMemory=false)from #1465: samekeepMemorynaming, mapped onto theautoPauseMemorycreate field.Type safety
The object form is a discriminated union on
action:keepMemoryis only valid withaction: 'pause'. Pairing it withaction: 'kill'is a compile-time type error (TS) / static error (ty), and is additionally rejected at runtime (InvalidArgumentError/InvalidArgumentException) for untyped callers.Behavior & validation
keepMemoryonly applies to apauseaction.connect(). CombiningkeepMemory: falsewithautoResumeis rejected client-side.Usage
Changes
spec/openapi.yml:autoPauseMemoryon the create body (+ regenerated JS/Python clients).SandboxOnTimeoutdiscriminated union ('pause' | 'kill' | { action: 'pause'; keepMemory? } | { action: 'kill' }) and the PythonSandboxOnTimeoutPause/SandboxOnTimeoutKillTypedDicts, wired throughcreateSandbox/_create_sandbox(sync + async) toautoPauseMemory, with the client-side guards.action: 'kill'type/runtime guard) and live cold-boot e2e in both SDKs; changeset (e2b+@e2b/python-sdk, minor).Backend dependency
The live e2e tests exercise the real auto-pause→cold-boot path and require the infra-side
autoPauseMemorysupport (e2b-dev/infra#3055), now merged and deployed.🤖 Generated with Claude Code