Skip to content

feat(sandbox-blaxel): add Blaxel sandbox provider#1275

Merged
omeraplak merged 7 commits into
VoltAgent:mainfrom
zrosenbauer:feat/sandbox-blaxel
May 11, 2026
Merged

feat(sandbox-blaxel): add Blaxel sandbox provider#1275
omeraplak merged 7 commits into
VoltAgent:mainfrom
zrosenbauer:feat/sandbox-blaxel

Conversation

@zrosenbauer
Copy link
Copy Markdown
Contributor

@zrosenbauer zrosenbauer commented May 9, 2026

PR Checklist

Bugs / Features

What is the current behavior?

VoltAgent has no first-party workspace sandbox provider for Blaxel. Users wanting Blaxel-managed sandboxes have to wire it up themselves.

What is the new behavior?

Adds @voltagent/sandbox-blaxel, a WorkspaceSandbox implementation backed by @blaxel/core. Supports streaming stdout/stderr, per-call timeouts and AbortSignal, output truncation, lazy provisioning, and getSandbox() for direct SDK access.

Notes for reviewers

  • New monorepo dep: es-toolkit (used for attempt/attemptAsync result tuples and small nullish utilities).
  • Modern tsconfig (target: ES2023, module: Preserve, verbatimModuleSyntax) targeting Node 22+; tsup handles emission.
  • Constructor mutates process.env.BL_API_KEY / BL_WORKSPACE — the only auth path the Blaxel SDK supports (no per-call credential injection).
  • Validated with pnpm -C packages/sandbox-blaxel typecheck && pnpm -C packages/sandbox-blaxel test:coverage (67 tests, 100% coverage).
  • Sandbox docs page reorganized per-provider (Blaxel / Daytona / E2B); experimental notes across website/docs/workspaces/*.md converted to :::warning admonitions.

Summary by CodeRabbit

  • New Features

    • Added a Blaxel sandbox provider for running agent commands with streaming stdout/stderr, per-call timeouts/abort support, output truncation, and lazy provisioning.
  • Documentation

    • Expanded sandbox docs with Blaxel setup, examples, multi-tenant routing, and unified experimental warnings across workspace guides.
  • Tests

    • Added comprehensive tests for Blaxel behavior, env handling, truncation, timeouts/abort, lifecycle, and utilities.
  • Chores

    • Introduced sandbox-blaxel package with build/test config and updated .gitignore.

Review Change Stack

Ships @voltagent/sandbox-blaxel implementing WorkspaceSandbox over
@blaxel/core. Uses the SDK-native pattern (process.exec → process.wait →
process.logs/get) with native env/streaming/timeout support — no manual
polling, no shell-prefix env hacks.

Public API:
- BlaxelSandbox class (execute, getSandbox, destroy, getInfo)
- BlaxelSandboxOptions with apiKey/workspace + a config bag extending
  SandboxCreateConfiguration with cwd, env, defaultTimeoutMs,
  maxOutputBytes, pollIntervalMs
- BlaxelSandboxConfig, BlaxelSandboxInstance type re-exports
- SandboxCreateConfiguration re-export from @blaxel/core for full SDK
  type access without a separate import

Tests cover 53 cases at 100% coverage on lines/branches/funcs/stmts.
Docs updated in website/docs/workspaces/sandbox.md with import,
getSandbox, and tenant-router examples.
…nfig

Cleanup pass on top of the initial provider commit:

- Extract option parsing into shell.ts. parseOptions returns the
  attempt-tuple shape and absorbs the stdin/empty-command validation.
  Shell-escape and env helpers are now private to shell.ts.
- Pull bracket helpers into utils.ts (toError, truncateOutput,
  withEventListener). withAbort lives at the bottom of sandbox.ts as a
  thin wrapper over withEventListener.
- Fold per-call sandbox/process plumbing into private methods on the
  class (killProcess, runProcess, fetchProcessOutput). Each helper
  resolves the sandbox via this.resolveSandbox() rather than threading
  it through every signature.
- Class layout: fields -> constructor -> public methods -> private
  methods.
- Replace single-file index.spec.ts with sandbox.spec.ts / shell.spec.ts
  / utils.spec.ts. 67 tests, 100% line/branch/func/stmt coverage. New
  test:coverage script in package.json.
- Modernize tsconfig.json for Node 22+: target/lib ES2023, module
  Preserve, moduleResolution Bundler, verbatimModuleSyntax. tsup target
  bumped to es2023 to match.
- Drop output.ts (truncateOutput moved into utils.ts).
- Re-export NormalizedCommand from @voltagent/core so the wrapper has a
  type for the parser result.
@changeset-bot
Copy link
Copy Markdown

changeset-bot Bot commented May 9, 2026

🦋 Changeset detected

Latest commit: 320cf75

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

This PR includes changesets to release 1 package
Name Type
@voltagent/sandbox-blaxel 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

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 9, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b363c2a2-0b2e-4ed5-ad81-983fa06d2304

📥 Commits

Reviewing files that changed from the base of the PR and between 442e1b9 and 320cf75.

📒 Files selected for processing (2)
  • packages/sandbox-blaxel/src/index.ts
  • packages/sandbox-blaxel/src/sandbox.ts
✅ Files skipped from review due to trivial changes (1)
  • packages/sandbox-blaxel/src/index.ts

📝 Walkthrough

Walkthrough

Adds @voltagent/sandbox-blaxel: a new WorkspaceSandbox provider implemented with @blaxel/core, plus types, shell/options/utils, core runtime with lazy provisioning and lifecycle handling, comprehensive tests, package/build/test configs, documentation updates, and re-exports NormalizedCommand from core workspace.

Changes

Blaxel Sandbox Provider Implementation

Layer / File(s) Summary
Type Definitions & Configuration
packages/sandbox-blaxel/package.json, packages/sandbox-blaxel/src/types.ts, packages/sandbox-blaxel/src/constants.ts, packages/sandbox-blaxel/src/index.ts, packages/sandbox-blaxel/tsconfig.json, packages/sandbox-blaxel/tsup.config.ts, packages/sandbox-blaxel/vitest.config.ts
Adds package metadata and exports; introduces BlaxelSandboxInstance, BlaxelSandboxConfig, BlaxelSandboxOptions; defines timeout/output/poll constants and package build/test/type settings.
Shell Command Parsing & Environment
packages/sandbox-blaxel/src/shell.ts, packages/sandbox-blaxel/src/shell.spec.ts
parseOptions() validates inputs, normalizes/escapes command+args into a shell-safe string, parses/coerces env, and resolves timeouts/maxOutputBytes/pollIntervalMs; applyEnvBindings() mirrors non-null bindings to process.env; tests cover env behavior.
Utility Helpers
packages/sandbox-blaxel/src/utils.ts, packages/sandbox-blaxel/src/utils.spec.ts
toError() coerces unknown into Error; withEventListener() manages listener lifecycle around async work; truncateOutput() truncates UTF‑8 strings by byte length without splitting codepoints; unit tests validate edge cases.
Core Implementation
packages/sandbox-blaxel/src/sandbox.ts
BlaxelSandbox implements WorkspaceSandbox: memoized lazy provisioning via SDK, execute lifecycle (exec + wait), abort/timeout handling with kill/cleanup, output fetching/truncation, destroy(), getSandbox(), and getInfo().
Sandbox Tests
packages/sandbox-blaxel/src/sandbox.spec.ts
Comprehensive Vitest coverage: constructor env forwarding, command forwarding/escaping, streaming/close wiring, timeout/abort/truncation, validation and robustness on SDK failures, lazy provisioning memoization/retry/destroy, and getInfo() shape.

Core Workspace Type Exports

Layer / File(s) Summary
NormalizedCommand Re-exports
packages/core/src/workspace/sandbox/index.ts, packages/core/src/workspace/index.ts
Adds NormalizedCommand to the sandbox barrel and workspace public re-exports.

Documentation & Changeset

Layer / File(s) Summary
Sandbox Documentation
website/docs/workspaces/sandbox.md
Expands provider overview (Blaxel/Daytona/E2B), adds install/config examples, underlying client access, and multi-tenant router examples; updates custom provider guidance and operationContext examples.
Experimental Warning Admonitions
website/docs/workspaces/{filesystem,overview,search,security,skills}.md
Standardizes experimental API notices by replacing blockquote notes with :::warning Experimental admonitions.
Changeset & Gitignore
.changeset/sandbox-blaxel.md, .gitignore
Adds a minor changeset documenting the new package and ignores .joggr in git.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant WorkspaceSandbox as BlaxelSandbox
  participant BlaxelSDK as `@blaxel/core`
  Client->>WorkspaceSandbox: execute(options)
  WorkspaceSandbox->>WorkspaceSandbox: parseOptions()
  WorkspaceSandbox->>BlaxelSDK: createIfNotExists(config) (lazy)
  BlaxelSDK-->>WorkspaceSandbox: sandboxInstance
  WorkspaceSandbox->>BlaxelSDK: exec(processName, command)
  WorkspaceSandbox->>BlaxelSDK: wait(processName, maxWait)
  BlaxelSDK-->>WorkspaceSandbox: finished / timeout
  WorkspaceSandbox->>BlaxelSDK: get/logs(processName)
  WorkspaceSandbox-->>Client: WorkspaceSandboxResult
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • VoltAgent/voltagent#1068: Adds getSandbox() accessor and provider instance type exports for other sandbox providers; closely related.
  • VoltAgent/voltagent#1025: Main PR adds a new Blaxel sandbox provider and workspace sandbox types/exports that extend the same Workspace sandbox provider surface.
  • VoltAgent/voltagent#1051: Overlaps on NormalizedCommand and command-normalization changes.

Suggested reviewers

  • Diluka

Poem

🐰 I hopped through code to make a space,

Blaxel sandboxes now run with grace,
Timeouts, truncation, streams that sing,
Tests and docs now welcome spring.
Hooray — a sandbox for every place!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 59.09% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat(sandbox-blaxel): add Blaxel sandbox provider' clearly and concisely describes the main change: introducing a new Blaxel sandbox provider package. It is specific, follows conventional commit format, and accurately reflects the changeset.
Description check ✅ Passed The PR description comprehensively addresses the template requirements: includes completed checklist items confirming tests, docs, and changesets were added; clearly explains current behavior (no first-party Blaxel provider), new behavior (adds @voltagent/sandbox-blaxel with full feature support), and provides detailed reviewer notes on implementation details, dependencies, and validation results.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

…ions

- Add a plain-English "what is a sandbox" intro and explain how agents use
  the execute_command tool.
- Reorganize the remote providers section per-provider (Blaxel, Daytona,
  E2B) with a uniform layout: blurb, install, configure, getSandbox
  example, multi-tenant routing.
- Add an "Available providers" table at the top with links to upstream
  docs.
- Add a small italic note under each install snippet calling out that the
  upstream SDK ships as a regular dependency.
- Convert the "Experimental" blockquote at the top of every workspace doc
  to the site's :::warning admonition style.
- Rewrite the sandbox-blaxel changeset to be consumer-focused with a code
  example.
@zrosenbauer zrosenbauer marked this pull request as ready for review May 9, 2026 22:44
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🧹 Nitpick comments (2)
packages/sandbox-blaxel/src/sandbox.ts (1)

128-147: ⚖️ Poor tradeoff

destroy() clears the cache before awaiting deletion — concurrent execute() can re-provision mid-tear-down.

this.sandbox = undefined happens synchronously before current.delete() resolves. A concurrent execute() (or getSandbox()) arriving in that window triggers resolveSandbox()createSandbox()SandboxInstance.createIfNotExists(...), which (depending on the SDK's naming/idempotency) may either resurrect the same sandbox (racing with delete) or spin up a new one that's never reaped. Consider either (a) keeping the cache populated until delete() settles, or (b) tracking a destroying state so concurrent calls reject with a clear error.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/sandbox-blaxel/src/sandbox.ts` around lines 128 - 147, The destroy()
method clears this.sandbox before awaiting deletion, allowing concurrent
execute() / getSandbox() → resolveSandbox() → createSandbox() →
SandboxInstance.createIfNotExists to re-provision mid-teardown; fix by not
nulling this.sandbox synchronously — either (A) keep this.sandbox until
current.delete() finishes (set this.sandbox = pending only after delete
resolves) or (B) add a destroying flag (e.g., this.destroying) that destroy()
sets immediately and check it in
getSandbox()/resolveSandbox()/createSandbox()/execute() to reject or wait, and
ensure you still call current.delete() and clear state after deletion; update
references to destroy(), execute(), getSandbox(), resolveSandbox(),
createSandbox(), SandboxInstance.createIfNotExists, this.sandbox, and
current.delete() accordingly.
packages/sandbox-blaxel/src/sandbox.spec.ts (1)

65-65: ⚡ Quick win

Reduce as unknown as chains to preserve test-time type guarantees.

Several double-casts bypass static checks. For deliberate invalid-input tests, prefer // @ts-expect-error`` at the callsite; for mocks, use narrowed helper types (Partial<...> / explicit interfaces) instead of `unknown` bridges.

💡 Example pattern
-    await sandbox.execute({
-      command: "ls",
-      env: { KEEP: "yes", DROP: undefined, NULLISH: null } as unknown as Record<string, string>,
-    });
+    await sandbox.execute({
+      command: "ls",
+      // `@ts-expect-error` intentional invalid values to test runtime env normalization
+      env: { KEEP: "yes", DROP: undefined, NULLISH: null },
+    });

-    await expect(
-      sandbox.execute({} as unknown as Parameters<BlaxelSandbox["execute"]>[0]),
-    ).rejects.toThrow("Sandbox command is required");
+    await expect(
+      // `@ts-expect-error` intentional missing command to test runtime validation
+      sandbox.execute({}),
+    ).rejects.toThrow("Sandbox command is required");

As per coding guidelines, **/*.ts: Maintain type safety in TypeScript-first codebase.

Also applies to: 123-123, 131-131, 167-167, 223-223, 369-369, 472-472, 502-502, 579-579, 650-650, 675-675

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/sandbox-blaxel/src/sandbox.spec.ts` at line 65, The test creates
instance: null as unknown as BlaxelSandboxInstance which double-casts away type
safety; replace that pattern by declaring the variable with a narrowed
mock-friendly type (e.g., let instance: Partial<BlaxelSandboxInstance> | null =
null) and update usages to either cast at the callsite or use //
`@ts-expect-error` for deliberate invalid-input tests; locate the variable named
instance in sandbox.spec.ts and change its declaration to a Partial or explicit
test interface for BlaxelSandboxInstance, then adjust any code that assumes full
instance shape to access mocked fields or perform targeted casts.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/sandbox-blaxel/src/sandbox.spec.ts`:
- Around line 134-142: Save the original environment values before tests and
restore them after each test to avoid cross-test leakage: capture
process.env.BL_API_KEY, process.env.BL_WORKSPACE, and process.env.BL_REGION into
variables (e.g., originalBlApiKey, originalBlWorkspace, originalBlRegion) before
the test that runs the current beforeEach, keep the existing vi.restoreAllMocks
call, and then in an afterEach restore each env var by setting
process.env.BL_API_KEY = originalBlApiKey (or deleting process.env.BL_API_KEY if
the original was undefined), doing the same for BL_WORKSPACE and BL_REGION; also
apply the same backup/restore pattern for the similar block referenced at lines
144-146.

In `@packages/sandbox-blaxel/src/sandbox.ts`:
- Around line 59-69: Constructor currently calls applyEnvBindings which mutates
process.env causing cross-instance clobbering; update the BlaxelSandbox
constructor to stop writing BL_API_KEY/BL_WORKSPACE into process.env by default
(e.g., remove or gate the applyEnvBindings call and store apiKey/workspace on
the instance config instead), or add an explicit opt-in flag on
BlaxelSandboxOptions to enable global env binding and document that it is
process-global; also add a clear docstring to BlaxelSandboxOptions noting that
enabling env binding mutates process.env and will affect other instances.
- Around line 216-227: The code currently treats any rejection from
sandbox.process.wait as a timeout; change the error handling in the attemptAsync
block around sandbox.process.wait (used in this function) to distinguish a real
timeout from other failures by checking the waitError message/class (match the
SDK timeout text, e.g. "did not finish in time" or the SDK-specific error class)
and only set timedOut = true and call this.killProcess({ processName }) when it
is a timeout; for non-timeout errors re-throw the waitError (or return it in the
result) so callers can see the actual failure instead of an unconditional
timedOut: true.

In `@packages/sandbox-blaxel/src/utils.spec.ts`:
- Around line 57-63: The test for truncateOutput currently only asserts
byteLength ≤ 4 which allows invalid/truncated UTF-8 sequences; update the spec
to assert that the returned content is valid UTF-8 and preserves whole
codepoints (e.g. for input "éééé" expect content to equal "éé" and truncated ===
true), or alternatively validate by round-tripping: Buffer.from(content,
"utf8").toString("utf8") === content and content is a prefix of the original
string by codepoints; target the truncateOutput function and the test case to
ensure no partial codepoints are returned.

In `@website/docs/workspaces/sandbox.md`:
- Around line 178-214: The TenantBlaxelSandboxRouter example currently never
disposes per-tenant sandboxes; add a destroy method on TenantBlaxelSandboxRouter
that iterates this.sandboxes, calls destroy() on each BlaxelSandbox instance
(from getSandboxForTenant), awaits/handles promises as needed, and then clears
the map, ensuring the class implements the WorkspaceSandbox destroy signature;
apply the same pattern to the Daytona/E2B router examples so copy-pasting
doesn't leak sandboxes.

---

Nitpick comments:
In `@packages/sandbox-blaxel/src/sandbox.spec.ts`:
- Line 65: The test creates instance: null as unknown as BlaxelSandboxInstance
which double-casts away type safety; replace that pattern by declaring the
variable with a narrowed mock-friendly type (e.g., let instance:
Partial<BlaxelSandboxInstance> | null = null) and update usages to either cast
at the callsite or use // `@ts-expect-error` for deliberate invalid-input tests;
locate the variable named instance in sandbox.spec.ts and change its declaration
to a Partial or explicit test interface for BlaxelSandboxInstance, then adjust
any code that assumes full instance shape to access mocked fields or perform
targeted casts.

In `@packages/sandbox-blaxel/src/sandbox.ts`:
- Around line 128-147: The destroy() method clears this.sandbox before awaiting
deletion, allowing concurrent execute() / getSandbox() → resolveSandbox() →
createSandbox() → SandboxInstance.createIfNotExists to re-provision
mid-teardown; fix by not nulling this.sandbox synchronously — either (A) keep
this.sandbox until current.delete() finishes (set this.sandbox = pending only
after delete resolves) or (B) add a destroying flag (e.g., this.destroying) that
destroy() sets immediately and check it in
getSandbox()/resolveSandbox()/createSandbox()/execute() to reject or wait, and
ensure you still call current.delete() and clear state after deletion; update
references to destroy(), execute(), getSandbox(), resolveSandbox(),
createSandbox(), SandboxInstance.createIfNotExists, this.sandbox, and
current.delete() accordingly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: fcb4ee91-3c57-4c86-a662-f5e118036b84

📥 Commits

Reviewing files that changed from the base of the PR and between ad4c44c and 88ff694.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (23)
  • .changeset/sandbox-blaxel.md
  • .gitignore
  • packages/core/src/workspace/index.ts
  • packages/core/src/workspace/sandbox/index.ts
  • packages/sandbox-blaxel/package.json
  • packages/sandbox-blaxel/src/constants.ts
  • packages/sandbox-blaxel/src/index.ts
  • packages/sandbox-blaxel/src/sandbox.spec.ts
  • packages/sandbox-blaxel/src/sandbox.ts
  • packages/sandbox-blaxel/src/shell.spec.ts
  • packages/sandbox-blaxel/src/shell.ts
  • packages/sandbox-blaxel/src/types.ts
  • packages/sandbox-blaxel/src/utils.spec.ts
  • packages/sandbox-blaxel/src/utils.ts
  • packages/sandbox-blaxel/tsconfig.json
  • packages/sandbox-blaxel/tsup.config.ts
  • packages/sandbox-blaxel/vitest.config.ts
  • website/docs/workspaces/filesystem.md
  • website/docs/workspaces/overview.md
  • website/docs/workspaces/sandbox.md
  • website/docs/workspaces/search.md
  • website/docs/workspaces/security.md
  • website/docs/workspaces/skills.md

Comment thread packages/sandbox-blaxel/src/sandbox.spec.ts
Comment thread packages/sandbox-blaxel/src/sandbox.ts
Comment thread packages/sandbox-blaxel/src/sandbox.ts Outdated
Comment thread packages/sandbox-blaxel/src/utils.spec.ts
Comment thread website/docs/workspaces/sandbox.md
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

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

3 issues found across 24 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="packages/sandbox-blaxel/src/utils.ts">

<violation number="1" location="packages/sandbox-blaxel/src/utils.ts:58">
P2: `truncateOutput` can return UTF-8 text whose byte length exceeds `maxBytes` when the slice ends in the middle of a multibyte character.</violation>
</file>

<file name="packages/sandbox-blaxel/src/shell.ts">

<violation number="1" location="packages/sandbox-blaxel/src/shell.ts:74">
P1: Constructor-time mutation of global env vars can leak or overwrite Blaxel credentials across sandbox instances in the same process.</violation>
</file>

<file name="packages/sandbox-blaxel/src/sandbox.ts">

<violation number="1" location="packages/sandbox-blaxel/src/sandbox.ts:104">
P1: Abort can be missed during sandbox provisioning, so the command may still start after cancellation.</violation>
</file>

Tip: cubic can generate docs of your entire codebase and keep them up to date. Try it here.

Comment thread packages/sandbox-blaxel/src/shell.ts
Comment thread packages/sandbox-blaxel/src/sandbox.ts
Comment thread packages/sandbox-blaxel/src/utils.ts Outdated
- Test env hygiene: snapshot BL_API_KEY/BL_WORKSPACE/BL_REGION at file
  load and restore them in afterEach so tests don't leak inherited env
  state to other workers.
- Document the env-mutation auth model on BlaxelSandboxOptions, the
  constructor, and as an :::info admonition under the Blaxel docs
  section, with a link to the upstream Blaxel auth docs.
- runProcess: only treat wait() rejections that match "did not finish in
  time" as timeouts; re-throw other rejections so real failures
  (network, teardown) aren't masked. Adds an isWaitTimeoutError helper.
- truncateOutput: walk the cut point back to a UTF-8 codepoint boundary
  so the result is always valid UTF-8 with byte length <= maxBytes.
  Strengthen the spec to assert exact value, byte length, and round-trip
  validity; add a 4-byte-doesn't-fit edge case.
- execute(): re-check options.signal?.aborted after resolveSandbox()
  returns, so a signal that fires during provisioning bails before
  starting a process. Extracts an abortedResult helper.
- Tenant router doc examples (Blaxel/Daytona/E2B): add destroy() so
  copy-pasted code disposes per-tenant sandboxes.
- Test plumbing: replace fragile microtask-counting in the abort
  mid-flight test with a setImmediate yield.
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/sandbox-blaxel/src/sandbox.ts`:
- Around line 208-218: Pass the abort signal into runProcess and add immediate
checks before and after launching the remote process so a late abort cannot
leave it running: update the call site that currently does const sandbox = await
this.resolveSandbox(); ... await sandbox.process.exec({...}) to include the
controller.signal (or parsed.signal) when calling runProcess/runProcess
invocation, and inside runProcess (the function that calls sandbox.process.exec)
check signal.aborted and short-circuit (and call killProcess if needed)
immediately before invoking sandbox.process.exec and again immediately after
exec returns/throws; ensure you reference the sandbox.process.exec call,
processName, parsed (for env/cwd/timeoutMs/pollIntervalMs), and killProcess so
the guard uses the same identifiers and correctly prevents starting or cleans up
any remote process if the signal fired just before/during exec.
- Around line 132-150: The destroy() implementation clears this.sandbox before
the pending promise resolves, allowing concurrent execute() calls (via
resolveSandbox(), killProcess(), fetchProcessOutput()) to create a new sandbox
and operate on the wrong instance; fix by not removing the shared promise until
the original instance is resolved and deleted: capture the pending promise in a
local (already done as pending), await resolution to a concrete
BlaxelSandboxInstance (using attemptAsync to get [err, current]), perform
current.delete(), only after successful deletion set this.sandbox = undefined
(or otherwise atomically replace it), and ensure execute()/resolveSandbox()
patterns capture the resolved sandbox per execution rather than re-resolving
this.sandbox mid-flight so the same concrete instance (BlaxelSandboxInstance) is
used throughout the lifecycle.
- Around line 230-235: The timeout branch currently discards the result of
killProcess so callers get { timedOut: true } even if remote cleanup failed;
update the isWaitTimeoutError handling to capture the return value from
killProcess({ processName }) (call it e.g. killResult), check its
success/failure indicator (the shape returned by killProcess), and if the kill
failed either throw an error (or return a different result conveying failure)
instead of unconditionally returning { timedOut: true }; modify the logic in the
function containing isNotNil/isWaitTimeoutError to propagate the killProcess
failure via throw or an explicit failure return so callers aren’t misled.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a6557698-e7fd-461d-97e7-31c571376edb

📥 Commits

Reviewing files that changed from the base of the PR and between 88ff694 and 1552c3e.

📒 Files selected for processing (6)
  • packages/sandbox-blaxel/src/sandbox.spec.ts
  • packages/sandbox-blaxel/src/sandbox.ts
  • packages/sandbox-blaxel/src/types.ts
  • packages/sandbox-blaxel/src/utils.spec.ts
  • packages/sandbox-blaxel/src/utils.ts
  • website/docs/workspaces/sandbox.md
✅ Files skipped from review due to trivial changes (2)
  • packages/sandbox-blaxel/src/utils.spec.ts
  • website/docs/workspaces/sandbox.md
🚧 Files skipped from review as they are similar to previous changes (3)
  • packages/sandbox-blaxel/src/types.ts
  • packages/sandbox-blaxel/src/utils.ts
  • packages/sandbox-blaxel/src/sandbox.spec.ts

Comment thread packages/sandbox-blaxel/src/sandbox.ts
Comment thread packages/sandbox-blaxel/src/sandbox.ts Outdated
Comment thread packages/sandbox-blaxel/src/sandbox.ts
Thread the resolved sandbox through one execute() call so a concurrent
destroy() can't cause re-entrant resolveSandbox() calls in runProcess /
fetchProcessOutput / killProcess to provision a fresh sandbox and
operate on the wrong instance.

Add a post-exec abort check inside runProcess to handle the case where
the abort listener fires while exec() is in flight — its kill lands
before the remote process exists, then exec() leaves the just-launched
process running unchecked. Matches the post-run check in the e2b
provider.

Kill failures during timeout cleanup remain swallowed to stay
consistent with the e2b provider (`requestKill().catch(() =>
undefined)`); callers see `timedOut: true` and can `destroy()` if they
suspect the remote process is still running.
Tighten comments added in the previous commit so each is at most one
line, in keeping with the codebase's "comments only when the why is
non-obvious" posture.
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
packages/sandbox-blaxel/src/sandbox.ts (1)

362-364: 💤 Low value

Add an explicit return type to getSdkConfig().

omit(...) infers a Partial<...>-like shape via es-toolkit, but pinning the return type (e.g. Omit<BlaxelSandboxConfig, "cwd" | "defaultTimeoutMs" | "maxOutputBytes" | "pollIntervalMs">) makes the SDK contract explicit and prevents accidental drift if BlaxelSandboxConfig grows new voltagent-only keys that aren't added to the omit list.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/sandbox-blaxel/src/sandbox.ts` around lines 362 - 364, Add an
explicit return type to the getSdkConfig method: change its signature to return
Omit<BlaxelSandboxConfig, "cwd" | "defaultTimeoutMs" | "maxOutputBytes" |
"pollIntervalMs"> so the SDK contract is explicit; keep the body using
omit(this.config ?? {}, [...]) but annotate the method return type as
Omit<BlaxelSandboxConfig, "cwd" | "defaultTimeoutMs" | "maxOutputBytes" |
"pollIntervalMs"> to prevent accidental shape drift when BlaxelSandboxConfig
gains new keys.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@packages/sandbox-blaxel/src/sandbox.ts`:
- Around line 362-364: Add an explicit return type to the getSdkConfig method:
change its signature to return Omit<BlaxelSandboxConfig, "cwd" |
"defaultTimeoutMs" | "maxOutputBytes" | "pollIntervalMs"> so the SDK contract is
explicit; keep the body using omit(this.config ?? {}, [...]) but annotate the
method return type as Omit<BlaxelSandboxConfig, "cwd" | "defaultTimeoutMs" |
"maxOutputBytes" | "pollIntervalMs"> to prevent accidental shape drift when
BlaxelSandboxConfig gains new keys.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 3fdb95d5-e3f5-413a-8ed8-f862d80362f4

📥 Commits

Reviewing files that changed from the base of the PR and between 1552c3e and 4e1c6e3.

📒 Files selected for processing (1)
  • packages/sandbox-blaxel/src/sandbox.ts

…yping

Drop the `SandboxCreateConfiguration` re-export from index.ts. It was
unnecessary (BlaxelSandboxConfig already extends it, so consumers get
the full SDK shape transitively) and inconsistent with sandbox-daytona
and sandbox-e2b, which don't re-export their SDK config types.
Consumers who genuinely need the raw SDK type can import it directly
from `@blaxel/core`.

Annotate `getSdkConfig` with an explicit return type
(`Omit<BlaxelSandboxConfig, voltagent-extras>`) so the SDK contract is
declared at the function boundary and typos in the omit list become a
type error instead of a silent runtime no-op.
@zrosenbauer zrosenbauer force-pushed the feat/sandbox-blaxel branch from 442e1b9 to 320cf75 Compare May 10, 2026 00:34
Copy link
Copy Markdown
Member

@omeraplak omeraplak left a comment

Choose a reason for hiding this comment

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

wow, good to see you!! ❤️

@omeraplak omeraplak merged commit 74eb6f0 into VoltAgent:main May 11, 2026
22 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.

2 participants