Agents
Configure an eve agent's model, reasoning effort, compaction, limits, and runtime behavior in agent.ts.
An eve app has one root agent assembled from the files under agent/. Its optional agent.ts calls defineAgent (from eve) when you need to configure the model or other runtime behavior. Declared subagents have their own agent.ts and capabilities; this page covers the configuration shared by root agents and subagents.
Set the model
A typical config selects a model:
import { defineAgent } from "eve";
export default defineAgent({
model: "anthropic/claude-opus-4.8",
});For a static AI Gateway model ID, you can make the same source change from the
project root with eve set --model anthropic/claude-opus-4.8 or from the local
dev TUI with /model anthropic/claude-opus-4.8.
The root agent.ts can be omitted when no runtime config is needed. In that case, eve defaults
to zai/glm-5.2. GLM 5.2 does not support image input; choose a
vision-capable model or route image inputs to Gemini
Flash.
When agent.ts is present, model is required.
model accepts a gateway model id string, which routes through the Vercel AI Gateway. To call a provider directly and configure the model in code, pass a provider-authored LanguageModel.
Provider-specific AI SDK packages are regular project dependencies. A fresh eve init app includes the core ai package, but it does not install every provider package. Install the provider package you import, then set that provider's API key:
npm install @ai-sdk/anthropicimport { anthropic } from "@ai-sdk/anthropic";
import { defineAgent } from "eve";
export default defineAgent({
model: anthropic("claude-opus-4-8"),
});Direct provider model ids use the provider's native format. For Anthropic, the
version uses hyphens (claude-opus-4-8), while the Gateway id above uses a dot
(anthropic/claude-opus-4.8).
Model use is subject to the terms, data-processing commitments, retention behavior, and available controls of the selected provider and routing path. Review the AI Gateway model catalog for gateway-routed models, and review the provider's terms when you configure a direct LanguageModel.
Choose the model dynamically
model also accepts defineDynamic({ events }). Each matching handler must
return the concrete model for its scope; a dynamic model has no compiled
default.
import { defineAgent, defineDynamic } from "eve";
export default defineAgent({
model: defineDynamic({
events: {
"session.started": (_event, ctx) => {
if (ctx.session.auth.initiator?.attributes.plan === "enterprise") {
return "anthropic/claude-opus-4.8";
}
return "anthropic/claude-sonnet-5";
},
},
}),
});Handlers receive the shared dynamic resolver
context (ctx.session, ctx.channel,
ctx.messages) and return a gateway model id, an AI SDK LanguageModel, a
selection object. Returning null or undefined fails the turn.
- Scopes.
session.started(once per session),turn.started(once per turn),step.started(every model step). Precedence: step > turn > session. Prefersession.started: prompt caches are per model, so every switch re-ingests the conversation at uncached prices. If no active selection exists before model-dependent work begins, the turn fails. - Failures stop the turn. A resolver that throws, returns no model, or returns an invalid selection fails before the provider call. A selected model without valid credentials fails at request time.
- Serialization. Session/turn selections must be model id strings; return
live
LanguageModelobjects only fromstep.started. - Selection object.
{ model, modelContextWindowTokens?, modelOptions? }. WhenmodelContextWindowTokensis omitted, eve resolves it from the AI Gateway catalog and caches successful metadata in durable session state for 24 hours. Set it explicitly for an unlisted or custom model. Dynamic agents cannot set siblingmodelContextWindowTokensormodelOptionsfields; return per-model values from the handler.
The session.started runtime identity does not include a model id for a
dynamic agent. Each public step.started event reports the concrete modelId
selected for that model call.
Reasoning effort
Set reasoning to control the model's reasoning effort through AI SDK's
provider-agnostic option:
export default defineAgent({
model: "openai/gpt-5.5",
reasoning: "high",
});Supported values are "provider-default", "none", "minimal", "low",
"medium", "high", and "xhigh". The selected model and provider determine
which levels are available and how they map to provider-native settings. Use
modelOptions.providerOptions when you need provider-specific reasoning controls.
Run eve set --reasoning high to update this field from the command line.
Compaction
Compaction summarizes older turns as you approach the context window. It's on by default, so you only tune when it kicks in. eve adds the estimated fixed checkpoint-prompt envelope to the trigger count, so compaction starts sooner than the conversation-only estimate. Lower thresholdPercent to compact sooner:
export default defineAgent({
model: "anthropic/claude-opus-4.8",
compaction: {
thresholdPercent: 0.75, // default 0.9
},
});See Default harness for how the loop applies it.
Runtime limits
Use limits for framework-owned runtime caps. Session token limits stop the
current durable session from starting another model call after accumulated
provider-reported input or output token usage reaches the configured limit:
export default defineAgent({
model: "anthropic/claude-opus-4.8",
limits: {
maxInputTokensPerSession: 200_000,
maxOutputTokensPerSession: 20_000,
sessionTimeoutMs: 7 * 24 * 60 * 60 * 1_000,
},
});sessionTimeoutMs sets an absolute lifetime for every session, including
delegated sessions. It defaults to 30 days, starts at creation, and survives
restarts and redeployments. At the deadline, eve lets an active turn settle,
then emits session.completed and releases the continuation; the next
qualifying channel message starts fresh. Set it to false to disable the
timeout. Expiration does not delete stored session data.
Input and output budgets are checked independently. The model call that crosses
either limit is allowed to finish because providers only report exact token
usage after a call completes. Before the next model call, eve pauses the
session and sends a deterministic continuation prompt with two options:
Approve grants a fresh budget window of the configured size (both input
and output windows reset together), and Stop cancels the in-flight turn
through the standard cancellation path (turn.cancelled → session.waiting)
— a user decision, not an error. The session stays resumable; because it is
still over budget, the next message re-raises the prompt. Declining a
delegated child's prompt cancels the root turn, which cascades to the whole
delegation tree — the delegating parent never receives an error result it
could retry against a fresh quota share. A reply that answers neither option
is queued while the existing prompt stays pending; eve does not raise another
copy. The reply is processed once the budget is granted.
Sessions that cannot reach a human — task-mode runs such as schedules and
subagents without input proxying — skip the prompt and fail the next model
call with SESSION_TOKEN_LIMIT_REACHED. A delegated task with no inherited
quota also fails instead of raising a continuation prompt that could only
grant another zero-token window.
When maxInputTokensPerSession is omitted, root sessions apply a default
input budget of 40_000_000 provider-reported input tokens.
maxOutputTokensPerSession is unset unless configured. Setting either limit
to false uncaps that axis — the session never stops on it.
Delegated subagent sessions have no fixed default. Each child receives a share of the delegating parent's remaining quota at dispatch time — the remainder in the current budget window split evenly across the batch's local subagent calls — and a completed child's usage counts against the parent's quota, so a delegation tree can never outspend the budget configured at its root. Approving a continuation opens a fresh parent window for later child grants without erasing lifetime usage. An authored child limit applies only when it is tighter than the parent's grant; an uncapped parent delegates uncapped children.
Workflow world
By default, eve selects the Workflow SDK world for the host: Vercel Workflow on
Vercel, and the SDK's local world in local development or eve start. Advanced
self-hosted deployments can select the Workflow world package to use from the
root agent.ts:
import { defineAgent } from "eve";
export default defineAgent({
model: "anthropic/claude-opus-4.8",
experimental: {
workflow: {
world: "@workflow/world-postgres",
},
},
});Install that package in your app. It should export a default factory or
createWorld() function. Pin a version built against the same @workflow/*
line as your eve release (currently the 5.0.0-beta line):
pnpm add @workflow/world-postgres@5.0.0-beta.xThe npm latest tag can lag behind that line, so an unpinned install may pull
an incompatible protocol version that the Workflow SDK rejects during initialization.
Put credentials and host-specific options in runtime environment variables read
by the world package, not in agent.ts. For the Postgres world, that means
putting the connection string or credentials in the env vars it reads. If the
installed package must stay external in hosted output, list it in
build.externalDependencies.
Other defineAgent fields
defineAgent takes a few more fields, all optional. For the exported types, see the TypeScript API Reference.
| Field | Type | Default | Description |
|---|---|---|---|
reasoning | AgentReasoningDefinition | provider default | Provider-agnostic reasoning effort forwarded to the agent's turn model calls. |
modelOptions | AgentModelOptionsDefinition | none | Provider option overrides forwarded to the model call. |
limits | AgentLimitsDefinition | field-specific | Framework-owned runtime limits. Sessions complete after 30 days by default; token-limit defaults and inheritance are described above. Set a limit to false to disable it. |
experimental | { workflow?: { world?: string } } | unset | Opt-in settings that can change or disappear in any release. Treat them as unstable. workflow.world selects the Workflow world package backing session state, queues, hooks, and streams on the root agent. |
outputSchema | Standard Schema or a JSON Schema object | none | Structured return type for task-mode runs (a subagent, schedule, or remote job). Interactive conversation turns ignore it unless the client supplies a per-message schema. |
build | { externalDependencies?: string[] } | none | Hosted-build packaging controls. externalDependencies keeps listed packages external while eve compiles authored modules such as tools and channels, and traces those packages into the hosted output. |
externalDependencies is a packaging control only. It keeps selected packages as runtime dependencies in the hosted output; it does not authorize, configure, or review any third-party service those packages may call.
During eve dev, ordinary dependencies are bundled into each retained runtime generation. Packages listed in externalDependencies keep normal Node.js resolution instead, so replacing one of those packages requires restarting the dev server.
Where adjacent settings live
| Concern | Lives in |
|---|---|
| Instructions prompt | agent/instructions.md, Instructions |
| Per-tool approval (HITL) | agent/tools/*.ts, Tools |
| Inbound auth & network policy | the channel layer, Auth & route protection |
| Sandbox / workspace | agent/sandbox/, Sandbox |
| Telemetry & debugging | agent/instrumentation.ts, Instrumentation |
What to read next
- Default harness for compaction and model context, and Built-in tools for the framework-provided tool set
- TypeScript API Reference for every
defineAgentfield and type - Subagents for the
descriptionrequirement and child-agent config