How to Implement Custom beforeStop and afterStart Hooks for Open Agents Sandboxes

You can implement custom beforeStop and afterStart hooks for Open Agents sandboxes by passing an object containing async functions to the hooks property of VercelSandbox.create() or connectVercelSandbox(), where afterStart runs immediately after the VM is ready and beforeStop executes right before the sandbox shutdown sequence begins.

The vercel-labs/open-agents repository provides a robust lifecycle hook system that allows you to execute arbitrary code during critical sandbox transitions. These hooks receive the full sandbox instance, enabling you to run commands, manipulate files, or persist data using the standard sandbox API.

Understanding the Sandbox Hook Interface

The SandboxHooks Interface

The hook contract is defined in packages/sandbox/interface.ts at lines 27-38. The SandboxHooks interface accepts four optional lifecycle callbacks:

export interface SandboxHooks {
  /** Called after the sandbox starts and is ready. */
  afterStart?: SandboxHook;

  /** Called before the sandbox stops. */
  beforeStop?: SandboxHook;

  /** Called when the sandbox is about to timeout (before beforeStop). */
  onTimeout?: SandboxHook;

  /** Called after timeout is successfully extended. */
  onTimeoutExtended?: (sandbox: Sandbox, additionalMs: number) => Promise<void>;
}

The SandboxHook type is a simple function signature: (sandbox: Sandbox) => Promise<void>.

Hook Timing and Use Cases

Hook When it fires Typical applications
afterStart Immediately after sandbox creation or reconnection, once the VM is ready to accept commands. Installing extra CLI tools, injecting environment variables, starting background services, initializing git configs, or logging telemetry.
beforeStop Right before the stop() method delegates to the Vercel SDK shutdown sequence. Committing pending changes, uploading build artifacts, cleaning temporary files, persisting logs, or reporting final status.

Wiring Custom Hooks into the Sandbox Lifecycle

Implementing afterStart Hooks

The afterStart hook is invoked in two code paths within packages/sandbox/vercel/sandbox.ts.

During initial creation (lines 94-98):

const sandbox = new VercelSandbox(...);

if (hooks?.afterStart) {
  await hooks.afterStart(sandbox);
}

During reconnection (lines 54-57):

if (options.hooks?.afterStart) {
  await options.hooks.afterStart(sandbox);
}

This guarantees your initialization logic runs whether you are spawning a fresh micro-VM or attaching to an existing one.

Implementing beforeStop Hooks

The beforeStop hook is invoked inside the stop() method (lines 58-68) before the SDK shutdown:

if (this.hooks?.beforeStop) {
  try {
    await this.hooks.beforeStop(this);
  } catch (error) {
    console.error("[VercelSandbox] beforeStop hook failed:", error);
  }
}
await this.sdk.stop();

The implementation wraps the hook in a try/catch block, ensuring that a failure in your custom logic does not prevent the sandbox from terminating. This prevents resource leaks even when your artifact upload or git commit fails.

Practical Implementation Examples

Example 1: Install a CLI Tool After Startup

Use afterStart to install dependencies that are not baked into the base image:

import { connectVercelSandbox } from "@open-agents/sandbox/vercel";

async function startSandbox() {
  const sandbox = await connectVercelSandbox({
    name: "my-session",
    source: { url: "https://github.com/owner/repo", branch: "main" },
    hooks: {
      afterStart: async (sb) => {
        // Install httpie globally inside the sandbox
        await sb.exec("npm i -g httpie", sb.workingDirectory, 60_000);
        console.log("✅ httpie installed inside sandbox");
      },
    },
  });

  return sandbox;
}

Example 2: Commit Changes Before Shutdown

Use beforeStop to persist work before the micro-VM disappears:

import { connectVercelSandbox } from "@open-agents/sandbox/vercel";

async function startAndMonitor() {
  const sandbox = await connectVercelSandbox({
    name: "demo-session",
    hooks: {
      beforeStop: async (sb) => {
        // Commit any pending edits to a git remote
        await sb.exec("git add -A", sb.workingDirectory, 30_000);
        await sb.exec(
          'git commit -m "autosave before stop"',
          sb.workingDirectory,
          30_000,
        );
        console.log("🗂️ Changes committed before sandbox shutdown");
      },
    },
  });

  // Later, when you decide to finish
  await sandbox.stop(); // beforeStop invoked automatically
}

Example 3: Combined Setup and Teardown

Using both hooks together for a full lifecycle management cycle:

const sandbox = await connectVercelSandbox({
  name: "full-cycle",
  hooks: {
    afterStart: async (sb) => {
      // Prepare a temporary directory for processing
      await sb.mkdir("/tmp/data", { recursive: true });
      await sb.writeFile("/tmp/data/.gitkeep", "");
    },
    beforeStop: async (sb) => {
      // Archive the temporary data before the VM disappears
      await sb.exec(
        "tar -czf /tmp/archive.tgz -C /tmp/data .",
        sb.workingDirectory,
        45_000,
      );
      // Upload the archive to external storage
      await sb.exec(
        `curl -X POST -F "file=@/tmp/archive.tgz" https://api.example.com/upload`,
        sb.workingDirectory,
        60_000,
      );
    },
  },
});

Critical Implementation Details

Aspect Implementation Detail
Timeout Buffer The sandbox adds a 30-second buffer (TIMEOUT_BUFFER_MS) to the user-specified timeout, ensuring the beforeStop hook has time to finish before the Vercel SDK force-stops the VM.
Idempotent Stops The stop() method checks this.isStopped and returns early on repeated calls. The beforeStop hook runs only once per instance.
Error Isolation Hook invocations are wrapped in try/catch blocks. A failing beforeStop hook logs to console.error but does not prevent the sandbox from terminating.
Proactive Timeout The onTimeout hook fires when the sandbox is about to hit its time limit, but beforeStop is not invoked automatically by the timeout timer—you must explicitly call sandbox.stop().
Hook Ordering afterStart runs after the sandbox object is fully constructed and any repository cloning completes. beforeStop runs immediately before the SDK stop request.

Key Source Files

File Purpose
packages/sandbox/interface.ts Defines the SandboxHooks interface, SandboxHook type, and hook signatures (lines 27-38).
packages/sandbox/vercel/sandbox.ts Implements hook invocation in create(), connect(), and stop() methods (lines 54-68, 94-98).
packages/sandbox/vercel/config.ts Exposes the VercelSandboxConfig and VercelSandboxConnectConfig types that include the optional hooks property.
apps/web/app/workflows/sandbox-lifecycle.ts Demonstrates real-world usage of the hook system in the Open Agents web application.

Summary

  • Define hooks as async functions matching the (sandbox: Sandbox) => Promise<void> signature.
  • Pass hooks via the hooks property when calling VercelSandbox.create() or connectVercelSandbox().
  • Use afterStart to bootstrap the environment—install tools, clone additional repos, or start services once the VM is ready.
  • Use beforeStop to persist state—commit code, upload artifacts, or clean resources before the micro-VM terminates.
  • Handle errors gracefully—hook failures are logged but never block the sandbox lifecycle, ensuring resources are always released.

Frequently Asked Questions

How do I ensure my beforeStop hook has enough time to upload large files?

The Open Agents sandbox automatically adds a 30-second buffer (TIMEOUT_BUFFER_MS) to your specified timeout. This buffer gives the beforeStop hook time to complete before the Vercel SDK force-terminates the VM. For very large uploads, consider compressing files or using the onTimeout hook to trigger uploads before the timeout deadline approaches.

Can I modify the sandbox timeout from within a hook?

While you cannot directly modify the timeout from within afterStart or beforeStop, the SandboxHooks interface includes an onTimeout hook that fires when the sandbox is about to reach its time limit. If you implement onTimeoutExtended, you can handle timeout extension events. To extend a timeout, you typically call the appropriate sandbox management API outside the hook context before the deadline.

What happens if my afterStart hook throws an error?

If the afterStart hook throws an error, the error is caught and logged to the console, but the sandbox creation or connection process continues. This design ensures that a failed bootstrap script (such as a failed npm install) does not indefinitely block the sandbox lifecycle. You should implement robust error handling inside your hook and consider retry logic for critical initialization steps.

Is it possible to register multiple hooks or change hooks after sandbox creation?

The current implementation in packages/sandbox/vercel/sandbox.ts stores the hooks object passed during construction and does not provide a public API to modify or add hooks after the sandbox instance is created. If you need dynamic behavior, you should implement conditional logic inside your hook functions or maintain state outside the sandbox that your hooks can reference. For completely different lifecycle behaviors, you would need to create a new sandbox instance with a different hooks configuration.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →