Agent Configuration

An agent's optional agent.ts calls defineAgent (from eve) to configure the model and other runtime behavior. The root agent's file lives at agent/agent.ts, or in the app root with a flat layout. Declared subagents have their own agent.ts and accept the same configuration.

Set the model

A typical config selects a model:

agent/agent.ts
import { defineAgent } from "eve";

export default defineAgent({
  model: "anthropic/claude-opus-5.5",
});

For a static AI Gateway model ID, make the same change with eve set model anthropic/claude-opus-5.5 from the project root or /model anthropic/claude-opus-5.5 in the local dev TUI.

Omit the root agent.ts to use openai/gpt-6-luna-fast with reasoning: "high". When agent.ts is present, model is required.

Every local agent.ts, root or subagent, must export the value defineAgent returns, and so must a dynamic subagent resolver that selects a local agent. The build rejects a plain object with Export defineAgent({...}) from "<path>".

A gateway model ID string routes through the Vercel AI Gateway. To call a provider directly and configure the model in code, pass a model instance. eve types it as EveLanguageModel, exported from eve and eve/models, and every AI SDK language model satisfies it.

With a static Gateway model, agent.ts needs no code at runtime. A dynamic model or a model instance keeps agent.ts loaded at runtime, because eve resolves it while the agent runs. See Authored module lifecycle.

Use eve's helpers for direct OpenAI or Anthropic access without installing another provider package:

agent/agent.ts
import { defineAgent } from "eve";
import { anthropic } from "eve/models/anthropic";

export default defineAgent({
  model: anthropic(), // claude-sonnet-5
});

openai() from eve/models/openai defaults to gpt-6-luna-fast. Both helpers accept an optional native provider model ID and read OPENAI_API_KEY or ANTHROPIC_API_KEY. In local development they can also use credentials saved through /login; deployments need the API key in the server environment.

For a local ChatGPT subscription, use chatgpt() from eve/models/openai and sign in with /login. It defaults to gpt-6-luna-fast and can't run in a deployment.

/login can switch a static Gateway string to an eve helper and manage its import, and /model changes the selected model and settings immediately. Edit custom provider SDK calls and dynamic expressions by hand. For provider-specific configuration, install an AI SDK provider package and pass its language model.

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 model instance.

eve fills some provider options by default. A value you set at the same path wins and is sent unchanged.

OptionCallsDefault value
providerOptions.openai.safetyIdentifier, providerOptions.anthropic.metadata.userIdOpenAI and Anthropic model and compaction callsSHA-256 fingerprint of the turn's auth.current authenticator, issuer, type, id, and subject. Unset when auth.current is null.
providerOptions.gateway.sessionIdAI Gateway callsThe Agent Runs gen_ai.conversation.id, shared by every turn, delegated subagent, and compaction call in the conversation.
providerOptions.openai.promptCacheKeyDirect OpenAI and chatgpt() callsSHA-256 hash of the session ID, unique per subagent session.

The safety identifier never contains the caller's raw identity fields or attributes. It follows the current caller when a later turn changes users. AI Gateway hashes session IDs longer than 256 characters.

The Gateway session ID groups the conversation's generations into one session in the AI Gateway logs Sessions view. It also drives session affinity. With affinity, AI Gateway first tries the provider that served the previous call, so that provider can read the prompt from its cache. Affinity chooses only among the providers your routing allows, but it can move one ahead of your order. To keep order exactly, set sessionAffinity: false:

agent/agent.ts
import { defineAgent } from "eve";

export default defineAgent({
  model: "anthropic/claude-opus-5.5",
  modelOptions: {
    providerOptions: {
      gateway: { order: ["bedrock", "anthropic"], sessionAffinity: false },
    },
  },
});

Use a local model

To test against a model on your own machine, use the AI SDK OpenAI-compatible provider. It works with any local server that implements the OpenAI Chat Completions API, such as Ollama, LM Studio, llama.cpp, or vLLM. With Ollama:

pnpm add @ai-sdk/openai-compatible
ollama pull gpt-oss:20b
OLLAMA_CONTEXT_LENGTH=64000 ollama serve
agent/agent.ts
import { createOpenAICompatible } from "@ai-sdk/openai-compatible";
import { defineAgent } from "eve";

const ollama = createOpenAICompatible({
  name: "ollama",
  baseURL: "http://localhost:11434/v1",
  includeUsage: true, // report token usage while streaming
});

export default defineAgent({
  model: ollama("gpt-oss:20b"),
  modelContextWindowTokens: 64_000,
});
  • Context window. AI Gateway has no metadata for local models, so set modelContextWindowTokens to the context length your server allocates, not the model's maximum. eve uses it to decide when to compact. Ollama allocates as little as 4K tokens by default; raise it to at least 64K for agents, as above.
  • Tool calling. eve agents call tools on most turns, so pick a model with native tool-calling support. gpt-oss:20b, qwen3, and gemma4 are good starting points on Ollama.
  • Usage. includeUsage: true lets token usage reach eve's session limits. Local models report no cost, so costUsd stays unset in a sessionUsage check.
  • Other servers. Change baseURL, for example to http://localhost:1234/v1 for LM Studio. Pass apiKey for a remote OpenAI-compatible server that requires one.

A deployment cannot reach a server on your machine, so use a Gateway or hosted provider model when you deploy.

Choose the model dynamically

To select a model from the incoming prompt with an AI SDK decision model, use auto from eve/models.

model also accepts defineDynamic(fn) from eve/models or eve. The function returns the model, and a dynamic model has no compiled default.

agent/agent.ts
import { defineAgent } from "eve";
import { defineDynamic } from "eve/models";

export default defineAgent({
  model: defineDynamic((ctx) =>
    ctx.session.auth.initiator?.attributes.plan === "enterprise"
      ? "anthropic/claude-opus-5.5"
      : "anthropic/claude-sonnet-5",
  ),
});

The function returns a Gateway model ID, a model instance such as an AI SDK language model, or a selection object. See Dynamic capabilities for the context and memo rules.

  • When it changes. eve runs the function before each ordinary model call. A memo keyed by ctx.turn?.id ?? null keeps a choice for the turn; ctx.session.id keeps it until a deployment change. Switching models can reduce prompt-cache reuse.
  • Failures stop the turn. A thrown error or invalid selection fails the turn before the provider call. null is not a model. Missing credentials fail at request time.
  • Serialization. Only memo results must be JSON. The function can return a live provider instance built from those results. ctx.model is unavailable during model selection.
  • Selection object. { model, reasoning?, modelContextWindowTokens?, modelOptions? }. Omit modelContextWindowTokens to read it from the AI Gateway model catalog that eve build or eve dev recorded, without a network request. A model that catalog doesn't list fails the turn. Set modelContextWindowTokens for an unlisted, custom, or newly published model, or rebuild. Dynamic agents cannot set sibling modelContextWindowTokens or modelOptions fields; return per-model values from the function.

The session.started runtime identity does not include a model ID for a dynamic agent. Each public model.started event reports the concrete model selected for that model call.

Reasoning effort

reasoning sets the model's reasoning effort through AI SDK's provider-agnostic option. In an authored agent.ts with a static model, omit reasoning to use the provider's default.

agent/agent.ts
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 for provider-specific reasoning controls. A dynamic model selection can return reasoning to override the agent setting; omit it to inherit the agent setting.

Run eve set model --reasoning high to update this field from the command line.

Prompt caching

eve caches prompts automatically for AI Gateway models and for Anthropic models it recognizes by provider or model id. On a directly called model, set modelOptions.promptCache to use a 1-hour cache. It also marks an unrecognized model as Anthropic, such as a Bedrock application inference profile:

agent/agent.ts
import { bedrock } from "@ai-sdk/amazon-bedrock";
import { defineAgent } from "eve";

export default defineAgent({
  model: bedrock(process.env.BEDROCK_INFERENCE_PROFILE_ARN!),
  modelContextWindowTokens: 200_000,
  modelOptions: {
    promptCache: { anthropic: { ttl: "1h" } }, // or { anthropic: {} } for the default 5m
  },
});

Not every Claude model supports a 1-hour cache. eve rejects promptCache on Gateway models; use providerOptions.gateway.caching instead.

Compaction

Compaction summarizes older turns as the conversation approaches the context window, and is on by default. The token count includes the fixed checkpoint prompt, so compaction starts before the conversation alone reaches the threshold. Lower compaction.threshold, a fraction from 0 to 1, to compact sooner:

agent/agent.ts
export default defineAgent({
  model: "anthropic/claude-opus-5.5",
  compaction: {
    threshold: 0.75, // default 0.9
  },
});

See Default harness for how the loop applies it.

Runtime limits

limits sets eve's runtime caps. sessionUsage receives the session's usage and returns true when the session must stop starting model calls:

agent/agent.ts
export default defineAgent({
  model: "anthropic/claude-opus-5.5",
  limits: {
    sessionUsage: ({ window }) =>
      window.inputTokens - window.cacheReadTokens >= 200_000 ||
      window.outputTokens >= 20_000 ||
      (window.costUsd ?? 0) >= 1.5,
    sessionTimeoutMs: 7 * 24 * 60 * 60 * 1_000,
  },
});

sessionTimeoutMs sets how long any session, including a delegated session, may sit idle before eve completes it. Omit it for 30 days, or set it to false to disable the timeout. The idle window starts when the session is created. It restarts each time a turn ends, including turns that clear or compact the context or are cancelled. A session never expires while a turn is open, such as while its tasks work or it waits for a person to answer. Process restarts don't change the window.

When the window elapses, eve emits session.ended with outcome: "completed" and releases the channel addresses, such as a Slack thread, that route messages to the session. The next message at that address starts a new session. To carry the earlier conversation over, pass it as prior history. A session with zero retention loses its stored payloads when it ends.

sessionUsage gets two TokenUsage records, a type exported from eve. total is everything the session has spent, and window is what it spent since the last approved continuation. Each record has these fields:

FieldMeaning
inputTokensAll input, including cache reads and writes.
cacheReadTokensHistory the provider replays from its prompt cache on every step. Subtract it from inputTokens to count only new input.
cacheWriteTokensInput written to the prompt cache.
outputTokensGenerated output.
costUsdModel token cost in US dollars that AI Gateway reports with each step. Excludes tool and infrastructure spend. Unset when no step reported a cost.

window is total minus the session's total when its own budget prompt was last accepted. Before the first approval, it equals total. Accepting a budget prompt that a subagent passed up starts the subagent's window, not the parent's. The window survives redeploys and replays.

Omit sessionUsage to stop at 40_000_000 new input tokens per window:

({ window }) => window.inputTokens - window.cacheReadTokens >= 40_000_000;

sessionUsage can raise its threshold after each approval. This limit asks at 20,000 new input tokens, then at 40,000, 80,000, and so on:

agent/agent.ts
export default defineAgent({
  model: "anthropic/claude-opus-5.5",
  limits: {
    sessionUsage: ({ total, window }) => {
      const used = total.inputTokens - total.cacheReadTokens;
      // Usage when the last continuation was approved; 0 before the first.
      const approvedAt = used - (window.inputTokens - window.cacheReadTokens);
      // Double past it, so each approval allows twice the usage before the next prompt.
      let next = 20_000;
      while (next <= approvedAt) next *= 2;
      return used >= next;
    },
  },
});

Set sessionUsage: false, or a callback that always returns false, to leave sessions uncapped. eve calls a sessionUsage callback before each model call once the window has usage. The model call that crosses a limit finishes, because exact usage arrives after the call completes. Before the next model call, eve pauses the turn (turn.paused) and sends a continuation prompt:

OptionResult
ContinueStarts a new window and resumes the same turn.
StopCancels the turn (turn.settled with outcome: "cancelled"), not an error. The session stays resumable, but because it is still over budget, the next message prompts again.

Declining a delegated child's prompt cancels the root turn and the whole delegation tree, so the parent never receives an error result it could retry.

A message that answers neither option waits behind the open prompt. It starts no second prompt or turn. After Continue, the paused turn receives it (delivery.consumed).

If an earlier deployment showed the continuation prompt, that turn has already ended. A message that answers neither option waits behind the prompt and emits no event until Continue. Continue starts a new turn that receives the waiting message once.

Some sessions can't request input from a human, such as markdown schedules and delegated runs without input proxying. They skip the prompt and fail the next model call with SESSION_USAGE_LIMIT_REACHED. Its details carry the total and window usage the check saw.

Each subagent session checks its own usage against its own sessionUsage, or the root agent's when it authors none. A dynamic subagent always uses the root agent's. Remote agents run under their own deployment's limits.

A child's usage, including its own subagents', also counts in the parent's usage. Approving a continuation starts a new window without erasing total. The parent counts delegated usage at these points:

Delegated workCounts against the parent
Agent taskWith each reply. A turn with no reply, such as a cancelled one, counts with the next reply, or when the turn ends if no call waits.
ctx.agent session from any other serve workflow toolThe same way as an agent task.
ctx.agent session from an execute or task toolWhen the tool finishes.
Remote agentThe same way as an agent task, cost included.

Some delegated usage doesn't count against the parent:

  • A ctx.agent turn still running when its workflow tool finishes or is cancelled.
  • The ctx.agent usage of an execute tool whose parent turn is cancelled while it waits.
  • What a cancelled agent task's own subagents spend after the task reports its cancelled turn.

A cancelled agent task ends and sends no further reply. Usage the cancelled run reported up to that point still counts against the parent when its usage report or final outcome arrives.

Workflow world

The Workflow world is the backend that stores and runs sessions, so they survive restarts and redeploys. Omit experimental.workflow.world to use Vercel Workflow on Vercel and the SDK's local world in local development or eve start. Self-hosted deployments can select a world package in the root agent.ts:

agent/agent.ts
import { defineAgent } from "eve";

export default defineAgent({
  model: "anthropic/claude-opus-5.5",
  experimental: {
    workflow: {
      world: "@workflow/world-postgres",
    },
  },
});

Install the package in your app; it must export a default factory or a createWorld() function. Install a version built against the same @workflow/* major version as your eve release, currently 5.x:

pnpm add @workflow/world-postgres

eve fails at startup, naming the version to install, when the world targets a different major version.

Put credentials, such as the Postgres connection string, and host-specific options in the environment variables the world package reads, not in agent.ts. If the package must stay external in hosted output, list it in build.externalDependencies. See Workflow worlds to choose a world and set up the Postgres world's database.

Workflow checkpoint batching

eve saves a Workflow step after every model call and its inline tool calls. experimental.workflow.modelCallsPerStep lets one step run several sequential model calls:

agent/agent.ts
import { defineAgent } from "eve";

export default defineAgent({
  model: "anthropic/claude-opus-5.5",
  experimental: {
    workflow: {
      modelCallsPerStep: 4,
    },
  },
});

The value is a positive-integer maximum. Omit it for 1. It applies separately to each root agent or declared subagent. A higher value can reduce save overhead in sequential tool loops, but more work runs again after a crash. If the step is interrupted, its earlier model calls and inline tool calls can run again, repeating provider costs, events, and side effects. Use stable idempotency keys for non-idempotent tools.

A batch ends before eve waits for input, authorization, or other blocking work. It can end below the maximum when the turn completes or steering arrives. Before assistant output begins, steering can interrupt pending model generation; running tools finish before the batch yields. This option is experimental and may change or disappear in any release. See Execution model and durability for the retry behavior.

Run data retention

Run-data retention is how long the Workflow World keeps a session's run data. Omit experimental.workflow.retention to derive it from the session's resolved OpenTelemetry trace policy when the session starts:

  • When the policy allows both inputs and outputs, eve uses the World's default retention period.
  • When the policy drops the trace or excludes either content direction, eve requests deletion when the run ends (Workflow's experimental_retention: 0). Workflow can't delete inputs and outputs separately.

Without a custom policy, public conversations and development sessions use the World's default. Private and unknown-audience sessions outside development request 0, even with no OpenTelemetry destination installed. Destination export filters and lifecycle-provider policies don't change the decision.

Set experimental.workflow.retention to 0 to always request deletion, or to "default" to always keep run data for the World's default period. This override is experimental and has no compatibility promise:

agent/agent.ts
import { defineAgent } from "eve";

export default defineAgent({
  model: "anthropic/claude-opus-5.5",
  experimental: {
    workflow: {
      retention: 0,
    },
  },
});

The resolved value applies to every run of the session, including runs after a redeploy. Session timeout and workflow tool runs keep the World's default.

Retention applies per agent. A subagent with its own session uses its own explicit value or derives retention from its resolved trace decision, including any inherited content ceiling. experimental.workflow.world is root-only.

At 0, supporting Worlds delete run and step inputs, outputs, and errors; event payloads; and stream contents when the run reaches a terminal state. The data remains available while the session is active. Run, step, and event records remain for the World's default period. These records include IDs, timestamps, statuses, step names, and attributes, which can contain content such as eve's session title. Local traces, exported telemetry, and data stored by tools, channels, or model providers are not deleted.

The Workflow World enforces deletion. A World older than spec version 4 rejects 0, and a spec version 4 World without retention support keeps the data. Confirm support with your World provider. See Workflow data retention.

⚠️ At 0, completed session transcripts become unreadable. Deletion races reads of finished runs. Depending on the World, reading deleted session history returns either an empty stream or 410 with code: "session_expired". Persist anything you need to keep from inside a tool, or set retention: "default" to preserve completed-session reads independently of tracing.

Other defineAgent fields

All other defineAgent fields are optional. For the exported types, see the TypeScript API Reference.

FieldTypeDefaultDescription
reasoningAgentReasoningDefinitionprovider defaultProvider-agnostic reasoning effort forwarded to the agent's turn model calls.
modelOptionsAgentModelOptionsDefinitionnoneProvider option overrides forwarded to the model call, and promptCache settings.
limitsAgentLimitsDefinitionfield-specificeve's runtime limits.
experimentalAgentExperimentalDefinitionunsetUnstable opt-ins: workflow.world, workflow.modelCallsPerStep, and workflow.retention.
build{ externalDependencies?: string[] }noneHosted-build packaging controls. externalDependencies keeps listed packages unbundled and includes them in the hosted output.
toolboolean | "deferred"trueExposes this agent to its parent model as a tool. "deferred" hides it from the tool list until the model finds it with eve__search and calls it with eve__tool. On the root agent, controls the built-in agent tool. With false, authored workflow tools can still call a subagent through ctx.agent().

externalDependencies only controls packaging; it doesn't authorize, configure, or review any third-party service those packages call.

During eve dev, packages listed in externalDependencies use normal Node.js resolution instead of being bundled, so restart the dev server after replacing one.

Where adjacent settings live

ConcernLives in
Instructions promptagent/instructions.md, Instructions
Per-tool approval (HITL)agent/tools/*.ts, Tools
Inbound auth & network policythe channel layer, Auth & route protection
Sandbox / workspaceagent/sandbox/, Sandbox
Telemetry & debuggingagent/instrumentation/, Instrumentation