Signed-off-by: Casey Gowrie <ctgowrie@gmail.com>
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 lineagectx.getSandbox(): live sandbox handle for the current agentctx.getSkill(identifier): handle for a named skill visible to the current agentdefineState(name, initial): typed durable state withget()andupdate()(imported fromeve/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.currentauth.initiatoridturn.idturn.sequence- optional
parent
Behavior:
auth.currentis the caller for the active inbound turn.auth.initiatoris 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"). parentis present for child subagent sessions and includes the parentcallId,sessionId,rootSessionId, andturn.
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
RuntimeSandboxSessionextends the ordinary sandbox I/O surface withstop(). It is exported fromeve/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.
identifieris 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
nameandfile(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) => providerandheaders: (ctx) => valuesresolvers - 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:
- The runtime populates durable seed values (auth, session id, compiled bundle).
- Before each step, the framework derives step-local values (session metadata, sandbox access, skill access) from the durable state.
- Authored code runs inside the managed scope, so
ctxanddefineStateaccessors resolve automatically. - 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.