Files
vercel__eve/docs/guides/session-context.md
T
Casey Gowrie cbe71057bf feat(eve): allow authored sandbox stops (#1801)
Signed-off-by: Casey Gowrie <ctgowrie@gmail.com>
2026-08-10 18:43:49 +00:00

6.2 KiB

title, description
title description
Session Context Runtime helpers: ctx.session, ctx.getSandbox, ctx.getSkill, and defineState.

eve exposes runtime state through the ctx parameter passed to tool execute, hook handlers, channel event handlers, and connection auth/header resolvers:

  • ctx.session: session metadata, turn, auth, and parent lineage
  • ctx.getSandbox(): live sandbox handle for the current agent
  • ctx.getSkill(identifier): handle for a named skill visible to the current agent
  • defineState(name, initial): typed durable state with get() and update() (imported from eve/context)

These APIs work only inside active authored runtime execution, including tools, channel event handlers, and authored hooks. They throw when called outside a managed context.

ctx.session

ctx.session exposes durable runtime metadata about the current execution.

import { defineTool } from "eve/tools";
import { z } from "zod";

export default defineTool({
  description: "Return the active session metadata.",
  inputSchema: z.object({}),
  async execute(_input, ctx) {
    return {
      sessionId: ctx.session.id,
      turnId: ctx.session.turn.id,
      turnSequence: ctx.session.turn.sequence,
      currentCaller: ctx.session.auth.current?.principalId,
      initiator: ctx.session.auth.initiator?.principalId,
      parentSessionId: ctx.session.parent?.sessionId,
      parentCallId: ctx.session.parent?.callId,
    };
  },
});

Public session fields:

  • auth.current
  • auth.initiator
  • id
  • turn.id
  • turn.sequence
  • optional parent

Behavior:

  • auth.current is the caller for the active inbound turn.
  • auth.initiator is the caller that started the durable session.
  • Unprotected agents expose both as null.
  • Top-level schedule sessions expose the framework app principal (principalId: "eve:app", principalType: "runtime").
  • parent is present for child subagent sessions and includes the parent callId, sessionId, rootSessionId, and turn.

ctx.getSandbox()

ctx.getSandbox() returns a live handle for the current agent's sandbox.

const sandbox = await ctx.getSandbox();
const result = await sandbox.run({ command: "npm test" });

Behavior:

  • It takes no arguments. Each agent has exactly one sandbox.
  • It is async because eve binds or restores sandbox state lazily.
  • It only works when sandbox access is attached to the active runtime path.
  • Visibility is node-local. A subagent sees its own sandbox, not the parent's.
  • The returned RuntimeSandboxSession extends the ordinary sandbox I/O surface with stop(). It is exported from eve/sandbox.

Call stop() to release sandbox compute while preserving the durable session and its filesystem:

const sandbox = await ctx.getSandbox();
await sandbox.stop();

Each backend implements this with its native lifecycle operation. Treat the stop as the end of sandbox work in the current callback; a later callback calls ctx.getSandbox() normally and eve reopens the same durable session. Vercel also supports using the same handle again: its next command or file operation automatically resumes the sandbox, just as it would after an inactivity timeout. No separate eve reconnect state is created, and provider failures reject the returned promise.

SandboxSession also exposes resolvePath(path), which returns the live backend-native path for a logical /workspace/... location. Use it when authored code needs that path before passing it to shell code or a child process.

See Sandbox for lifecycle details.

ctx.getSkill(identifier)

ctx.getSkill(identifier) returns a handle for a named skill visible to the current agent.

const skill = ctx.getSkill("research");
const notes = await skill.file("references/checklist.md").text();

Behavior:

  • It is synchronous. File content is read lazily from the active sandbox.
  • It only works when sandbox access is attached to the active runtime path.
  • identifier is the path-derived skill id.
  • Visibility follows the current agent's sandbox.
  • A missing skill surfaces when a file accessor reads a missing sandbox path.
  • The returned handle exposes name and file(relativePath).

See Skills for the full authoring model.

Custom state with defineState

Use defineState when your agent needs durable typed state that tools, hooks, and channel handlers can share. State survives workflow step boundaries. Declare the handle at module scope so every importer shares it:

import { defineState } from "eve/context";

interface BudgetState {
  readonly count: number;
  readonly cap: number;
}

export const budget = defineState<BudgetState>("myapp.budget", () => ({
  count: 0,
  cap: 25,
}));

get() reads the current value (returning initial() on first access), and update(fn) applies a function to it. Both throw outside a managed scope. See State for the full read/write model and examples from tools and hooks.

Where these APIs work

Safe places:

  • inside defineTool(...).execute(input, ctx)
  • inside connection auth: (ctx) => provider and headers: (ctx) => values resolvers
  • inside authored callbacks eve runs inside the runtime
  • after asynchronous boundaries inside the same authored execution chain

Unsafe places:

  • top-level module evaluation
  • build scripts
  • discovery-time code paths

If you call them outside an active eve runtime context, they throw immediately with a message explaining the required scope.

How it works

The framework sets up a context container before invoking authored code:

  1. The runtime populates durable seed values (auth, session id, compiled bundle).
  2. Before each step, the framework derives step-local values (session metadata, sandbox access, skill access) from the durable state.
  3. Authored code runs inside the managed scope, so ctx and defineState accessors resolve automatically.
  4. After the step, the framework commits mutable state (for example sandbox changes) back to the durable session.

The framework manages this lifecycle. Authored code only uses ctx and the public accessors.