Skip to content

feat(sdk): filesystem-only auto-pause via lifecycle.onTimeout object form#1471

Merged
bchalios merged 11 commits into
mainfrom
feat/auto-pause-filesystem-only
Jun 25, 2026
Merged

feat(sdk): filesystem-only auto-pause via lifecycle.onTimeout object form#1471
bchalios merged 11 commits into
mainfrom
feat/auto-pause-filesystem-only

Conversation

@bchalios

@bchalios bchalios commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

Filesystem-only auto-pause (onTimeout object form)

Adds an object form to the sandbox lifecycle onTimeout (on_timeout in Python) that controls the snapshot kind taken when a sandbox auto-pauses on timeout, via keepMemory (keep_memory).

onTimeout now accepts either the existing bare action ('pause' / 'kill') or the object form { action, keepMemory }. When keepMemory is false (with action: '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 to true (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: same keepMemory naming, mapped onto the autoPauseMemory create field.

Type safety

The object form is a discriminated union on action: keepMemory is only valid with action: 'pause'. Pairing it with action: 'kill' is a compile-time type error (TS) / static error (ty), and is additionally rejected at runtime (InvalidArgumentError / InvalidArgumentException) for untyped callers.

Behavior & validation

  • keepMemory only applies to a pause action.
  • Incompatible with auto-resume — auto-resume wakes a paused sandbox on inbound traffic by restoring its memory snapshot in place; a filesystem-only snapshot has no memory to restore (resuming cold-boots it), so it must be resumed explicitly via connect(). Combining keepMemory: false with autoResume is rejected client-side.

Usage

// JS/TS — filesystem-only auto-pause on timeout
const sbx = await Sandbox.create({
  lifecycle: { onTimeout: { action: 'pause', keepMemory: false } },
})

// bare string form still works (full memory snapshot)
const sbx2 = await Sandbox.create({ lifecycle: { onTimeout: 'pause' } })
# Python
sbx = Sandbox.create(
    lifecycle={"on_timeout": {"action": "pause", "keep_memory": False}}
)

Changes

  • spec/openapi.yml: autoPauseMemory on the create body (+ regenerated JS/Python clients).
  • JS SandboxOnTimeout discriminated union ('pause' | 'kill' | { action: 'pause'; keepMemory? } | { action: 'kill' }) and the Python SandboxOnTimeoutPause / SandboxOnTimeoutKill TypedDicts, wired through createSandbox / _create_sandbox (sync + async) to autoPauseMemory, with the client-side guards.
  • Tests: payload serialization + validation (offline, incl. the 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 autoPauseMemory support (e2b-dev/infra#3055), now merged and deployed.

🤖 Generated with Claude Code

@cla-bot cla-bot Bot added the cla-signed label Jun 22, 2026
@changeset-bot

changeset-bot Bot commented Jun 22, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 762d945

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 2 packages
Name Type
e2b Minor
@e2b/python-sdk Minor

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

@cursor

cursor Bot commented Jun 22, 2026

Copy link
Copy Markdown

PR Summary

Medium Risk
Changes default sandbox timeout pause/resume semantics when keepMemory is false and depend on backend autoPauseMemory; mistakes in defaults or validation could cause unexpected cold boots or broken auto-resume.

Overview
Adds autoPauseMemory to the sandbox create API and extends lifecycle.onTimeout / on_timeout in JS and Python so callers can pass { action: 'pause', keepMemory: false } (or keep_memory) for filesystem-only timeout auto-pause, mapped to that field. Bare 'pause' / 'kill' is unchanged; default keepMemory / keep_memory stays full memory.

Create paths normalize the object form, reject keep_memory with action: 'kill', reject keep_memory: false with auto_resume: true, and treat missing / None keep_memory as full memory so it is not sent as filesystem-only. OpenAPI, generated clients, exports, docstrings, changeset, and sync/async tests cover validation and live cold-boot on connect().

Reviewed by Cursor Bugbot for commit 762d945. Bugbot is set up for automated code reviews on this repo. Configure here.

@github-actions

github-actions Bot commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

Package Artifacts

Built from eed1974. Download artifacts from this workflow run.

JS SDK (e2b@2.30.7-feat-auto-pause-filesystem-only.0):

npm install ./e2b-2.30.7-feat-auto-pause-filesystem-only.0.tgz

CLI (@e2b/cli@2.13.1-feat-auto-pause-filesystem-only.0):

npm install ./e2b-cli-2.13.1-feat-auto-pause-filesystem-only.0.tgz

Python SDK (e2b==2.29.6+feat-auto-pause-filesystem-only):

pip install ./e2b-2.29.6+feat.auto.pause.filesystem.only-py3-none-any.whl

@bchalios bchalios force-pushed the feat/auto-pause-filesystem-only branch from 814e9f7 to 09c9826 Compare June 22, 2026 16:15
@bchalios

Copy link
Copy Markdown
Contributor Author

Addressed the two actionable points from the Cursor summary (pushed):

  1. autoPauseMemory sent on every create — now only sent when onTimeout/on_timeout is pause; omitted otherwise (JS sends undefined, Python leaves it UNSET). keepMemory is forced true when not pausing anyway, so this is behavior-preserving and matches the field's contract.
  2. Missing test for the keepMemory=false + non-pause guard — added a dedicated offline test in all three SDKs: keepMemory=false requires onTimeout pause (JS) and test_filesystem_only_auto_pause_requires_pause (Python sync + async). Both guards (requires-pause and incompatible-with-auto-resume) now have offline coverage.

The third point (live cold-boot e2e depends on the backend autoPauseMemory support) is the known dependency on e2b-dev/infra#3055 — those tests stay red in CI until it deploys; the offline payload/validation tests pass independently.

JS typecheck/lint and Python ruff all pass.

@bchalios bchalios force-pushed the feat/pause-filesystem-only branch from 1bed5d8 to f259b02 Compare June 25, 2026 10:33
Base automatically changed from feat/pause-filesystem-only to main June 25, 2026 10:42
@bchalios bchalios marked this pull request as ready for review June 25, 2026 10:50
@chatgpt-codex-connector

Copy link
Copy Markdown

Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits.
Credits must be used to enable repository wide code reviews.

Comment thread packages/python-sdk/tests/async/sandbox_async/test_create.py
Comment thread .changeset/auto-pause-filesystem-only.md
Comment thread packages/python-sdk/e2b/sandbox_async/sandbox_api.py

@mishushakov mishushakov left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

let's do final round

Comment thread packages/python-sdk/tests/sync/sandbox_sync/test_create.py Outdated
Comment thread packages/python-sdk/tests/sync/sandbox_sync/test_create.py Outdated
Comment thread packages/python-sdk/tests/sync/sandbox_sync/test_create.py Outdated
Comment thread packages/python-sdk/tests/sync/sandbox_sync/test_create.py Outdated
Comment thread packages/python-sdk/tests/async/sandbox_async/test_create.py
Comment thread packages/python-sdk/e2b/sandbox/sandbox_api.py
Comment thread packages/python-sdk/e2b/sandbox/sandbox_api.py Outdated
Comment thread packages/js-sdk/src/sandbox/sandboxApi.ts
Comment thread packages/python-sdk/e2b/sandbox_async/sandbox_api.py Outdated
Comment thread packages/python-sdk/e2b/sandbox_sync/sandbox_api.py
@bchalios bchalios changed the title feat(sdk): filesystem-only auto-pause via lifecycle.keepMemory feat(sdk): filesystem-only auto-pause via lifecycle.onTimeout object form Jun 25, 2026
@bchalios bchalios enabled auto-merge (squash) June 25, 2026 14:57
Comment on lines +105 to +108
for _ in range(150):
if not sbx.is_running():
break
sleep(0.2)

@mishushakov mishushakov Jun 25, 2026

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

this could potentially halt CI for 150*0.2*n

@bchalios bchalios disabled auto-merge June 25, 2026 16:47
bchalios and others added 9 commits June 25, 2026 18:53
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>
@bchalios bchalios force-pushed the feat/auto-pause-filesystem-only branch from e252d90 to a26c55c Compare June 25, 2026 16:55

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ 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.

Comment thread packages/python-sdk/e2b/sandbox_async/sandbox_api.py
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>
@bchalios bchalios enabled auto-merge (squash) June 25, 2026 17:10
@bchalios bchalios merged commit 7e7e951 into main Jun 25, 2026
28 of 29 checks passed
@bchalios bchalios deleted the feat/auto-pause-filesystem-only branch June 25, 2026 17:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants