Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions .changeset/lifecycle-optional-on-timeout.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
---
'e2b': patch
'@e2b/python-sdk': patch
---

Make `lifecycle.onTimeout` / `lifecycle["on_timeout"]` optional, so the "no timeout action configured" state the SDKs already put on the wire can actually be expressed by a typed caller, and treat a nullish `keepMemory` / `keep_memory` as unconfigured rather than as an explicit choice.

```ts
import { Sandbox } from 'e2b'

// Opt out of auto-resume without expressing a preference about the timeout
// action — previously a type error, since onTimeout was required.
await Sandbox.create({ lifecycle: { autoResume: false } })

// A keepMemory that spreads in as undefined is no longer sent as `true`, and no
// longer trips the pause-only guard on a kill action.
const keepMemory: boolean | undefined = undefined
await Sandbox.create({ lifecycle: { onTimeout: { action: 'pause', keepMemory } } })
```

```python
from e2b import Sandbox

# Opt out of auto-resume without expressing a preference about the timeout
# action — previously a type error, since on_timeout was required.
Sandbox.create(lifecycle={"auto_resume": False})

# A keep_memory that is None is no longer sent as True, and no longer trips the
# pause-only guard on a kill action.
Sandbox.create(lifecycle={"on_timeout": {"action": "pause", "keep_memory": None}})

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Make the Python nullish example type-check

When users copy this new example into a type-checked Python project, Pyright rejects it because SandboxOnTimeoutPause.keep_memory remains NotRequired[bool], so None is not accepted even though the runtime now treats it as unset. Either widen the public type to accept None, matching the advertised JS/Python behavior, or avoid documenting None as a supported typed call.

AGENTS.md reference: AGENTS.md:L3-L3

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This sample does not typecheck, and the changeset is the one artifact here that ships verbatim to users (into both packages/js-sdk/CHANGELOG.md and packages/python-sdk/CHANGELOG.md). Written to a file and run through the repo's own checker from packages/python-sdk:

$ uv run ty check --output-format concise /tmp/x.py
/tmp/x.py:3:16: error[invalid-argument-type] Argument to bound method `create` is incorrect: Expected `SandboxLifecycle | None`, found `dict[...]`
/tmp/x.py:3:41: error[invalid-argument-type] Invalid argument to key "on_timeout" with declared type `Literal["pause", "kill"] | SandboxOnTimeoutPause | SandboxOnTimeoutKill` on TypedDict `SandboxLifecycle`

The JS twin above it compiles clean, so this is the asymmetry described in the other comment rather than a typo. The read_from_config() variant in the PR description has the same problem when the function returns Optional[bool].

Since the recommendation is to leave the type strict, the sample is what should change. This version typechecks, and I confirmed with a request-capture probe that it produces exactly the wire behavior the changeset describes — keep_memory=None gives autoPause: true with autoPauseMemory omitted, True and False send autoPauseMemory accordingly:

# A keep_memory the caller does not have an opinion about is left out of the
# request entirely, so the API's default applies.
Sandbox.create(
    lifecycle={
        "on_timeout": {
            "action": "pause",
            **({"keep_memory": keep_memory} if keep_memory is not None else {}),
        }
    }
)

One more prose nit further down: line 33 writes autoResume: True, mixing the JS option name with Python's capitalization.

```

`autoResume: True` still requires an explicit `onTimeout` of `'pause'`; the error now names that knob instead of implying the SDK knows which action the API would have picked.
2 changes: 1 addition & 1 deletion .changeset/omit-auto-pause-when-lifecycle-unset.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
'@e2b/python-sdk': patch
---

Omit `autoPause` from the create-sandbox request when no timeout lifecycle is configured, and omit `autoPauseMemory` unless `keepMemory` / `keep_memory` was chosen. Sending the SDK's local defaults for those fields was indistinguishable from an explicit choice, so the API could not tell "no preference" from a client choice and own its defaults. Explicit values are still always sent:
Omit `autoPause` from the create-sandbox request when no timeout lifecycle is configured, and omit `autoPauseMemory` unless `keepMemory` / `keep_memory` was chosen. Sending the SDK's local defaults for those fields was indistinguishable from an explicit choice, so the API could not tell "no preference" from a client choice and own its defaults. Explicit values are sent exactly as before:

```ts
import { Sandbox } from 'e2b'
Expand Down
31 changes: 20 additions & 11 deletions packages/js-sdk/src/sandbox/sandboxApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -456,14 +456,15 @@ export type SandboxLifecycle = {
* Omitted from the create request when unset, leaving the API's default
* (currently `kill`) in effect.
*/
onTimeout: SandboxOnTimeout
onTimeout?: SandboxOnTimeout

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This line — the PR's headline change — has no automated guard whatsoever in the JS SDK. I reverted it to onTimeout: SandboxOnTimeout and got: lifecycleRequest.test.ts 12/12 still passing, pnpm run typecheck clean, pnpm run lint unaffected. Nothing in CI notices.

It is not that the contract is unexpressed — your tests express it perfectly, that is precisely what dropping the as never casts did. They are just never typechecked:

  • packages/js-sdk/tsconfig.json is the only tsconfig and sets "include": ["src"], and typecheck is a bare tsc --noEmit, so tests/** is excluded.
  • vitest.config.mts declares no typecheck block, so vitest strips types via esbuild without checking them.
  • oxlint isn't type-aware.

Adding tests to the include list makes the guard appear immediately — 4 errors, all in this PR's own test file, and nothing else:

tests/sandbox/lifecycleRequest.test.ts(100,5): error TS2741: Property 'onTimeout' is missing in type '{ autoResume: false; }' but required in type 'SandboxLifecycle'.
tests/sandbox/lifecycleRequest.test.ts(108,56): error TS2741: Property 'onTimeout' is missing in type '{}' but required in type 'SandboxLifecycle'.
tests/sandbox/lifecycleRequest.test.ts(132,7): error TS2741: ...
tests/sandbox/lifecycleRequest.test.ts(143,7): error TS2741: ...

The honest cost: flipping include is not free, because tests/ has 37 pre-existing errors across 18 files today (worst offender 5, in tests/sandbox/commands/commandHandle.test.ts) — bounded, but not a one-liner, and out of scope here. A tsconfig.tests.json plus a typecheck:tests script scoped to a growing allowlist would let this PR's file be guarded now without blocking on that cleanup.

Not blocking for this PR — but as long as it holds, "the tests cover the optional onTimeout" is true only of the source text, not of anything CI runs.


/**
* Auto-resume enabled flag.
*
* Leave unset to let the API pick the behavior. Set `false` to opt out
* explicitly and keep auto-resume off even if the API's default changes.
* Can be `true` only when `onTimeout` is `pause`. Not supported when
* Can be `true` only alongside an explicit `onTimeout` of `pause`, because
* auto-resume only has meaning for a sandbox that pauses. Not supported when
* `keepMemory` is `false` (a filesystem-only snapshot must be resumed
* explicitly via `connect()`).
*/
Expand Down Expand Up @@ -1575,18 +1576,21 @@ export class SandboxApi {
// `{ action, keepMemory }`. The discriminated union type forbids `keepMemory`
// on `action: 'kill'`; re-check at runtime for untyped callers.
const requestedOnTimeout = opts?.lifecycle?.onTimeout
// A missing (or explicitly nullish, for untyped callers) onTimeout is not a
// choice of `kill` — it leaves the timeout action to the API. Locally it
// still resolves to kill semantics for the validation below.
// A missing (or explicitly nullish) onTimeout is not a choice of `kill` — it
// leaves the timeout action to the API. The guards below therefore never
// assume what the API would pick; they only constrain what the caller said.
const onTimeoutConfigured = requestedOnTimeout != null
const onTimeout = requestedOnTimeout ?? 'kill'
const action = typeof onTimeout === 'string' ? onTimeout : onTimeout.action
const hasKeepMemory =
// A nullish keepMemory is not a choice of snapshot kind, the same way a
// nullish onTimeout is not a choice of action: the field is left out of the
// request and the API's default applies.
const requestedKeepMemory =
typeof onTimeout !== 'string' && 'keepMemory' in onTimeout
const keepMemory =
typeof onTimeout !== 'string' && 'keepMemory' in onTimeout
? (onTimeout.keepMemory ?? true)
: true
? onTimeout.keepMemory
: undefined
const hasKeepMemory = requestedKeepMemory != null
const keepMemory = requestedKeepMemory ?? true
// A missing autoResume (or an explicit null from an untyped caller) is left
// out of the request entirely, so the API keeps ownership of the default
// instead of receiving the SDK's local one as an explicit opt-out.
Expand All @@ -1599,8 +1603,13 @@ export class SandboxApi {
}

if (autoResume && action !== 'pause') {
// Without a configured action there is no `kill` to name — the SDK no
// longer decides what an unset onTimeout means — so point at the knob.
const hint = onTimeoutConfigured

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Same half-covered conditional as the python side (see the comment on build_lifecycle_config): istanbul records this ternary as [0, 2] — the '' arm, taken when the caller did configure a non-pause action, never runs.

It is invisible in the summary because this file's branch total is byte-identical on base and head (1338/1827 overall, 44 → 42 arms in this block, 4 zero-count arms at both ends): the keepMemory refactor happened to remove as many arms as the hint ternary added. Only a per-arm comparison surfaces it.

Making the hint unconditional keeps all 12 tests in lifecycleRequest.test.ts green. Verified fix — passes on head, fails against that mutant:

test('an explicit kill action is not told to leave onTimeout unset', async () => {
  await expect(
    Sandbox.create('base', {
      apiKey: TEST_API_KEY,
      lifecycle: { onTimeout: 'kill', autoResume: true },
    })
  ).rejects.toThrowError(
    /^autoResume can only be true when onTimeout action is 'pause'\.$/
  )
})

The anchored regex is what does the work — it fails if the hint is appended when an action was given.

? ''
: " Set lifecycle.onTimeout to 'pause': leaving it unset defers the action to the API."

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Gating the hint is right — an unconditional hint that tells the user to do the thing they already did was a real defect on #1687 — but this is gated the less useful way round. The branch that gets the hint is the one where onTimeout is unset, and the branch that gets nothing is { onTimeout: 'kill', autoResume: true }, where the user has a concrete line in front of them to change. TASTE asks error messages to say what to do, so if either case deserves the pointer it is arguably the configured one.

A single message covers both without a branch, and states the rule in the option names the caller typed:

autoResume can only be true when lifecycle.onTimeout is 'pause'. Leaving onTimeout unset defers the action to the API, which does not enable auto-resume.

Non-blocking, and if you keep the branch, note the Python side spells the same knob lifecycle['on_timeout'] while this one says lifecycle.onTimeout — correct per-language idiom, just worth a glance to confirm it was deliberate.

throw new InvalidArgumentError(
"autoResume can only be true when onTimeout action is 'pause'."
`autoResume can only be true when onTimeout action is 'pause'.${hint}`
)
}

Expand Down
69 changes: 63 additions & 6 deletions packages/js-sdk/tests/sandbox/lifecycleRequest.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,17 +93,26 @@ test('Sandbox.create sends the pause snapshot kind alongside autoPause', async (
})

test('Sandbox.create omits autoPause for a lifecycle without onTimeout', async () => {
// Untyped callers can build the lifecycle conditionally and leave
// onTimeout out, or pass it as null; neither selects an action.
// onTimeout is optional, so opting out of auto-resume without expressing a
// preference about the timeout action is a typed call, not a cast.
await Sandbox.create('base', {
apiKey: TEST_API_KEY,
lifecycle: { autoResume: false } as never,
lifecycle: { autoResume: false },
})

expect(lastCreateBody).toBeDefined()
expect(lastCreateBody).not.toHaveProperty('autoPause')
expect(lastCreateBody?.autoResume).toEqual({ enabled: false })

// An empty lifecycle expresses nothing at all.
await Sandbox.create('base', { apiKey: TEST_API_KEY, lifecycle: {} })

expect(lastCreateBody).toBeDefined()
expect(lastCreateBody).not.toHaveProperty('autoPause')
expect(lastCreateBody).not.toHaveProperty('autoResume')

// Untyped callers can also pass onTimeout as null; that selects no action
// either.
await Sandbox.create('base', {
apiKey: TEST_API_KEY,
lifecycle: { onTimeout: null } as never,
Expand All @@ -115,16 +124,25 @@ test('Sandbox.create omits autoPause for a lifecycle without onTimeout', async (
})

test('Sandbox.create rejects autoResume without a timeout action', async () => {
// An unconfigured onTimeout still resolves to kill semantics locally, so
// autoResume has no pause to attach to.
// Auto-resume only has meaning for a sandbox that pauses, so it needs an
// explicit 'pause' rather than whichever action the API would have picked.
await expect(
Sandbox.create('base', {
apiKey: TEST_API_KEY,
lifecycle: { autoResume: true } as never,
lifecycle: { autoResume: true },
})
).rejects.toThrowError(InvalidArgumentError)

expect(lastCreateBody).toBeUndefined()

// The message points at the knob to turn instead of naming a default the SDK
// no longer decides.
await expect(
Sandbox.create('base', {
apiKey: TEST_API_KEY,
lifecycle: { autoResume: true },
})
).rejects.toThrowError(/Set lifecycle.onTimeout to 'pause'/)
})

test('an explicit autoResume: false is sent', async () => {
Expand Down Expand Up @@ -154,3 +172,42 @@ test('an explicit null autoResume from an untyped caller is omitted', async () =

expect(lastCreateBody).not.toHaveProperty('autoResume')
})

test('a nullish keepMemory is not a choice of snapshot kind', async () => {
// Spreading an optional value in yields `keepMemory: undefined`, which is no
// more a choice than leaving the key out.
const unset: boolean | undefined = undefined

await Sandbox.create('base', {
apiKey: TEST_API_KEY,
lifecycle: { onTimeout: { action: 'pause', keepMemory: unset } },
})

expect(lastCreateBody?.autoPause).toBe(true)
expect(lastCreateBody).not.toHaveProperty('autoPauseMemory')

// It therefore doesn't trip the pause-only guard on a kill action either.
await Sandbox.create('base', {
apiKey: TEST_API_KEY,
lifecycle: { onTimeout: { action: 'kill', keepMemory: unset } as never },
})

expect(lastCreateBody?.autoPause).toBe(false)
expect(lastCreateBody).not.toHaveProperty('autoPauseMemory')
})

test('a nullish keepMemory still allows autoResume', async () => {
const unset: boolean | undefined = undefined

await Sandbox.create('base', {
apiKey: TEST_API_KEY,
lifecycle: {
onTimeout: { action: 'pause', keepMemory: unset },
autoResume: true,
},
})

expect(lastCreateBody?.autoPause).toBe(true)
expect(lastCreateBody).not.toHaveProperty('autoPauseMemory')
expect(lastCreateBody?.autoResume).toEqual({ enabled: true })
})
30 changes: 20 additions & 10 deletions packages/python-sdk/e2b/sandbox/sandbox_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -534,7 +534,7 @@
`"kill"`); an omitted `auto_resume` leaves the choice to the API.
"""

on_timeout: SandboxOnTimeout
on_timeout: NotRequired[SandboxOnTimeout]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

The JS half of this change fully delivers what the PR sets out to do; the Python half delivers only one of the two states, and I think the right fix is in the docs rather than here.

NotRequired[T] makes the key optional but still rejects None as a value, whereas the JS mirror onTimeout?: SandboxOnTimeout accepts undefined (exactOptionalPropertyTypes is off). All three lifecycle keys — on_timeout, keep_memory and auto_resume — are now runtime-lenient toward None but type-strict against it. That is exactly why the JS test file could drop its as never casts in this PR while the Python test file still routes every None row through cast(Any, ...): those calls remain unexpressible for a typed caller. Python has no undefined, so a caller holding an Optional[bool] from config has no direct spelling.

I checked that widening works mechanically — changing this line and keep_memory to NotRequired[Optional[...]] makes the changeset's sample and all of ty pass with 31 tests still green, and turns 5 of the 7 cast sites in the test file into plain typed calls. I am not recommending it, because TASTE's "absence is undefined, never null" rules out the T | null shape in an optional property, and the same conclusion was reached for NotRequired[str] on the egressProxy credentials in #1702 — worth keeping the two consistent.

So the code here looks right as it stands. What needs to change is the advertising: the changeset and the PR description both present keep_memory=None as the Python mirror of the JS keepMemory: undefined idiom, and it isn't one. Concrete alternative in the changeset comment.

One small thing that follows from this: nothing typed can reach the None branches, so the cast(Any, ...) in the new tests is correct and should stay — it is documenting untyped-caller tolerance, not a shape users are meant to write. A sentence to that effect above NO_ACTION_AUTO_RESUME_CASES would keep a future reader from "fixing" the casts away.

"""
What should happen to the sandbox when timeout is reached. `"kill"` terminates
the sandbox; `"pause"` pauses it for later resume. Accepts either the bare
Expand All @@ -548,8 +548,9 @@
"""
Whether activity should cause the sandbox to resume when paused. Leave unset
to let the API pick the behavior. Set `False` to opt out explicitly and keep
auto-resume off even if the API's default changes. Can be `True` only when
`on_timeout` is `pause`. Not supported when `keep_memory` is `False`
auto-resume off even if the API's default changes. Can be `True` only
alongside an explicit `on_timeout` of `pause`, because auto-resume only has
meaning for a sandbox that pauses. Not supported when `keep_memory` is `False`
(a filesystem-only snapshot must be resumed explicitly via `connect()`).
"""

Expand Down Expand Up @@ -811,20 +812,23 @@
"""
# on_timeout accepts a bare action or {"action", "keep_memory"}; normalize.
# Only the object form carries keep_memory; anything else (a bare action
# string, or an unexpected value from an untyped caller) passes through as
# the action, so a non-"pause" value resolves to kill instead of crashing.
on_timeout_raw = lifecycle.get("on_timeout") if lifecycle else None
# A missing on_timeout — or an explicit None from an untyped caller — is not
# a choice of kill. It only resolves to kill semantics locally, for the
# validation below and for keep_memory.
# A missing on_timeout — or an explicit None — is not a choice of kill. The
# guards below therefore never assume what the API would pick; they only
# constrain what the caller said.
on_timeout_configured = on_timeout_raw is not None
if isinstance(on_timeout_raw, dict):
on_timeout = on_timeout_raw.get("action", "kill")
keep_memory_provided = "keep_memory" in on_timeout_raw
# A None keep_memory is not a choice of snapshot kind, the same way a
# None on_timeout is not a choice of action: the field is left out of
# the request and the API's default applies.
keep_memory = on_timeout_raw.get("keep_memory")
keep_memory_provided = keep_memory is not None
else:
# Only fall back when unconfigured, not on other falsy-but-present
# values an untyped caller might pass.

Check warning on line 831 in packages/python-sdk/e2b/sandbox/sandbox_api.py

View check run for this annotation

Claude / Claude Code Review

Python keep_memory typed as NotRequired[bool] rejects the Optional[bool] spread the PR advertises

`SandboxOnTimeoutPause.keep_memory` (line 492) is still typed `NotRequired[bool]`, but `build_lifecycle_config` now treats `keep_memory: None` as "unconfigured" and both the PR description and `.changeset/lifecycle-optional-on-timeout.md` advertise passing an `Optional[bool]` (e.g. `keep_memory: read_from_config()`) straight into `Sandbox.create(lifecycle=...)`. Under pyright that call fails type checking (`None is not assignable to bool`), forcing every None-`keep_memory` case — even for `act
on_timeout = on_timeout_raw if on_timeout_configured else "kill"
keep_memory = None
keep_memory_provided = False
Expand All @@ -845,9 +849,15 @@
auto_resume = lifecycle.get("auto_resume") if lifecycle else None

if auto_resume and on_timeout != "pause":
raise InvalidArgumentException(
"auto_resume can only be True when on_timeout action is 'pause'."
)
message = "auto_resume can only be True when on_timeout action is 'pause'."
if not on_timeout_configured:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

This conditional is half-tested, and it is the only uncovered thing the PR adds.

On base, build_lifecycle_config had zero uncovered lines and zero uncovered branch arcs. On head it has exactly one: arc 853 → 860, the false side of this if — i.e. auto_resume: True alongside an action the caller did configure but that isn't pause. The JS twin is identical: the hint ternary at sandboxApi.ts:1608 reads [0, 2].

The reason is that no test in either repo pairs autoResume: true with an explicit 'kill'. Every one of the 8 python and 7 JS occurrences uses 'pause', or an unset/None action. So making the hint unconditional passes the whole suite — I ran it: if True: here → 31/31 python still green, and the JS equivalent → 12/12 still green.

That matters because the failure mode is the exact confusion this PR set out to remove. The behaviour on head is correct:

{'on_timeout': 'kill',              'auto_resume': True} -> "...action is 'pause'."
{'on_timeout': {'action': 'kill'},  'auto_resume': True} -> "...action is 'pause'."
{'auto_resume': True}                                    -> "...action is 'pause'. Set lifecycle['on_timeout'] to 'pause': leaving it unset defers the action to the API."

But nothing holds it there, so a later edit could tell a caller who did set on_timeout to "leave it unset", with CI green.

Verified fix — passes on head, fails against the surviving mutant:

@pytest.mark.parametrize(
    "on_timeout",
    [
        pytest.param("kill", id="bare-kill"),
        pytest.param({"action": "kill"}, id="object-kill"),
    ],
)
def test_explicit_kill_auto_resume_error_does_not_suggest_leaving_on_timeout_unset(
    test_api_key, on_timeout
):
    with pytest.raises(InvalidArgumentException) as excinfo:
        Sandbox.create(
            api_key=test_api_key,
            lifecycle={"on_timeout": on_timeout, "auto_resume": True},
        )

    assert "leaving it unset" not in str(excinfo.value)

This also closes the arc, taking the function back to fully covered.

# Without a configured action there is no "kill" to name — the SDK no
# longer decides what an unset on_timeout means — so point at the knob.
message += (
" Set lifecycle['on_timeout'] to 'pause': leaving it unset defers"
" the action to the API."
)
raise InvalidArgumentException(message)

if not keep_memory and auto_resume:
raise InvalidArgumentException(
Expand Down
2 changes: 1 addition & 1 deletion packages/python-sdk/e2b/sandbox_async/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ async def create(
:param mcp: MCP server to enable in the sandbox
:param network: Sandbox network configuration. ``allow_out``/``deny_out`` may also be a callable receiving a :class:`SandboxNetworkSelectorContext` (``ctx.all_traffic``, ``ctx.rules``) and returning a list of strings. Per-host transform rules are nested under ``network.rules``; a rule's ``transform`` may be a callable receiving a :class:`SandboxNetworkTransformContext` of placeholder strings (``ctx.iam.tokens[name]``).
:param iam: Sandbox workload identity configuration. A non-empty ``tokens`` map enables workload identity for the sandbox; token definitions can be created with :meth:`Secret.iam_token`. Example: ``{"tokens": {"aws": Secret.iam_token(audience="sts.amazonaws.com", token_type="JWT-SVID")}}``. Registered tokens are exposed to ``network.rules`` ``transform`` callables as ``ctx.iam.tokens[name]`` placeholders, which the egress proxy resolves per request
:param lifecycle: Sandbox lifecycle configuration — ``on_timeout``: ``"kill"`` or ``"pause"`` (omitted from the request when unset, leaving the API's default, currently ``"kill"``, in effect), or an object ``{"action": "pause"|"kill", "keep_memory": bool}`` where ``keep_memory`` set to ``False`` makes a timeout auto-pause filesystem-only (cold-boots on resume; cannot be combined with ``auto_resume``); an omitted ``keep_memory`` leaves the snapshot kind to the API; ``auto_resume``: leave unset to let the API pick the behavior, set ``False`` to opt out explicitly, or ``True`` (only when ``on_timeout`` action is ``"pause"``). Example: ``{"on_timeout": {"action": "pause", "keep_memory": False}}``
:param lifecycle: Sandbox lifecycle configuration — ``on_timeout``: ``"kill"`` or ``"pause"`` (omitted from the request when unset, leaving the API's default, currently ``"kill"``, in effect), or an object ``{"action": "pause"|"kill", "keep_memory": bool}`` where ``keep_memory`` set to ``False`` makes a timeout auto-pause filesystem-only (cold-boots on resume; cannot be combined with ``auto_resume``); an omitted ``keep_memory`` leaves the snapshot kind to the API; ``auto_resume``: leave unset to let the API pick the behavior, set ``False`` to opt out explicitly, or ``True`` (only alongside an explicit ``on_timeout`` action of ``"pause"``). Example: ``{"on_timeout": {"action": "pause", "keep_memory": False}}``
:param volume_mounts: Dictionary mapping mount paths to AsyncVolume instances or volume names
:param logger: Logger used for request and response logging for this sandbox. Accepts any standard library `logging.Logger`. When omitted, no request/response logging is emitted.

Expand Down
2 changes: 1 addition & 1 deletion packages/python-sdk/e2b/sandbox_sync/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ def create(
:param mcp: MCP server to enable in the sandbox
:param network: Sandbox network configuration. ``allow_out``/``deny_out`` may also be a callable receiving a :class:`SandboxNetworkSelectorContext` (``ctx.all_traffic``, ``ctx.rules``) and returning a list of strings. Per-host transform rules are nested under ``network.rules``; a rule's ``transform`` may be a callable receiving a :class:`SandboxNetworkTransformContext` of placeholder strings (``ctx.iam.tokens[name]``).
:param iam: Sandbox workload identity configuration. A non-empty ``tokens`` map enables workload identity for the sandbox; token definitions can be created with :meth:`Secret.iam_token`. Example: ``{"tokens": {"aws": Secret.iam_token(audience="sts.amazonaws.com", token_type="JWT-SVID")}}``. Registered tokens are exposed to ``network.rules`` ``transform`` callables as ``ctx.iam.tokens[name]`` placeholders, which the egress proxy resolves per request
:param lifecycle: Sandbox lifecycle configuration — ``on_timeout``: ``"kill"`` or ``"pause"`` (omitted from the request when unset, leaving the API's default, currently ``"kill"``, in effect), or an object ``{"action": "pause"|"kill", "keep_memory": bool}`` where ``keep_memory`` set to ``False`` makes a timeout auto-pause filesystem-only (cold-boots on resume; cannot be combined with ``auto_resume``); an omitted ``keep_memory`` leaves the snapshot kind to the API; ``auto_resume``: leave unset to let the API pick the behavior, set ``False`` to opt out explicitly, or ``True`` (only when ``on_timeout`` action is ``"pause"``). Example: ``{"on_timeout": {"action": "pause", "keep_memory": False}}``
:param lifecycle: Sandbox lifecycle configuration — ``on_timeout``: ``"kill"`` or ``"pause"`` (omitted from the request when unset, leaving the API's default, currently ``"kill"``, in effect), or an object ``{"action": "pause"|"kill", "keep_memory": bool}`` where ``keep_memory`` set to ``False`` makes a timeout auto-pause filesystem-only (cold-boots on resume; cannot be combined with ``auto_resume``); an omitted ``keep_memory`` leaves the snapshot kind to the API; ``auto_resume``: leave unset to let the API pick the behavior, set ``False`` to opt out explicitly, or ``True`` (only alongside an explicit ``on_timeout`` action of ``"pause"``). Example: ``{"on_timeout": {"action": "pause", "keep_memory": False}}``
:param volume_mounts: Dictionary mapping mount paths to Volume instances or volume names
:param logger: Logger used for request and response logging for this sandbox. Accepts any standard library `logging.Logger`. When omitted, no request/response logging is emitted.

Expand Down
Loading
Loading