Skip to content

ctx.getSandbox(environment) drops custom provider session methods at runtime, but its type still exposes them #5020

Open

Description

@utkarsh-var

Summary

With a custom provider from defineSandboxProvider(), ctx.getSandbox(environment) is typed to return the provider's session type, RuntimeSandboxSessionFor<Session>. At runtime it returns eve's fixed handle from buildSandboxSession(), which only forwards the standard SandboxSession surface (plus setNetworkPolicy when one is given). Any other method the provider's session implements is undefined on the returned object, so calling it throws TypeError: … is not a function.

TypeScript accepts the call, so nothing flags it until runtime.

Versions

  • Reproduced on eve 0.77.0.
  • Checked the published builds of 0.78.0, 0.79.2 and 0.80.1 (latest): buildSandboxSession in dist/src/execution/sandbox/session.js is unchanged, and so are dist/src/shared/sandbox-provider.d.ts and sandbox-session.d.ts.

Minimal repro

// agent/sandbox.ts
import { defineSandbox, type SandboxSession } from "eve/sandbox";
import { defineSandboxProvider } from "eve/sandbox/provider";

interface AcmeSandboxSession extends SandboxSession {
  exists(path: string): Promise<boolean>; // provider-specific method
}

export const AcmeSandbox = defineSandboxProvider<
  undefined,                 // environment options
  undefined,                 // open options
  { templateId: string },    // prepared artifact
  { remoteId: string },      // session state
  AcmeSandboxSession         // provider-specific session type
>({
  name: "acme",
  environment() {
    return {
      async prepare() { return { templateId: "t" }; },
      async start() {
        const sandbox = createAcmeSession(); // implements SandboxSession + exists()
        return { handle: handleFor(sandbox), state: { remoteId: "r" } };
      },
      async resume() { return handleFor(createAcmeSession()); },
    };
  },
});

export const environment = AcmeSandbox.environment();
export default defineSandbox(() => environment.open());
// agent/tools/check.ts
import { defineTool } from "eve/tools";
import { z } from "zod";
import { environment } from "../sandbox";

export default defineTool({
  description: "Check whether a file exists.",
  inputSchema: z.object({ path: z.string() }),
  async execute({ path }, ctx) {
    const sandbox = await ctx.getSandbox(environment); // typed as AcmeSandboxSession & { stop, delete }
    return sandbox.exists(path);                        // compiles; at runtime: TypeError: sandbox.exists is not a function
  },
});

Expected

One of these:

  1. ctx.getSandbox(environment) returns an object that exposes the provider session's own methods, as its type says; or
  2. the return type matches what is actually forwarded, so the call fails to compile. The docs should then say custom-provider extras aren't reachable from tools, and how to reach them instead.

Actual

buildSandboxSession(e, setNetworkPolicy) builds a new object with only resolvePath, run, spawn, readFile, readBinaryFile, readTextFile, writeFile, writeBinaryFile, writeTextFile, removePath (and setNetworkPolicy). Everything else on e, the provider's session, is lost. Unit tests that call the provider session directly pass, so the gap only shows up once tools run inside eve.

Notes

  • The sandbox docs ("Use the sandbox") say to "Pass the configured environment to preserve its specific session capabilities in authored runtime code", which suggests option 1 is intended. In practice that only holds for setNetworkPolicy on the built-in providers.
  • Related, but not the same: Type the sandbox passed to environment prepare() with the environment session capabilities #3717 is about the types of prepare() and of DefaultSandbox. This issue is about a runtime gap for custom providers whose types already promise the methods.
  • Possible fixes:
    • forward unknown members to the provider session, e.g. spread or proxy the provider session's own methods after the standard ones, so eve's wrappers still take precedence; or
    • add an explicit hook to defineSandboxProvider() for declaring extra session members to forward; or
    • narrow the type of getSandbox(environment) for custom providers to the forwarded surface.

Workaround we're using

The provider keeps a per-process Map<eveSessionId, ProviderSession>, filled in start/resume and cleared in the handle's stop/delete/shutdown callbacks and in onSessionEnd. Tools first await ctx.getSandbox(environment), which makes eve start or resume the sandbox in this process, then look up the provider session by ctx.session.id to call the extra methods. It works, but it depends on eve's lifecycle ordering and duplicates state eve already holds.

Activity

  1. roboeve commented on Oct 19, 2026

    @roboeve
    Contributor

    Confirmed on origin/main: a tool's ctx.getSandbox(environment) loses methods a custom provider session defines on its class, so sandbox.exists(...) compiles but throws TypeError: ... is not a function. Own-property methods survive, and hooks and channel events are not affected.

    Repro: #5021
    Regressed in: not a recent regression (fails on every checked release back to eve@0.65.0)

  2. added a commit that references this issue on Oct 19, 2026
    333c584
  3. roboeve commented on Oct 19, 2026

    @roboeve
    Contributor

    Fix proposed in #5022, assigned to @ruiconti. This issue closes when it merges.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions