Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/vercel/eve/llms.txt

Use this file to discover all available pages before exploring further.

defineState is a typed, named slot of durable per-session memory for an agent. Use it when the agent needs to remember something between conversation turns — a running budget, a glossary, a checklist — without standing up an external store. The values survive workflow step boundaries, so they outlast crashes, redeploys, and days-long sessions.

Define a state slot

import { defineState } from "eve/context";

const budget = defineState("my-agent.budget", () => ({ count: 0, cap: 25 }));
Pass defineState(name, initial) a stable string name (namespace it to your agent) and an initial function that produces the starting value the first time the slot is read. You get back a StateHandle<T>:
MethodWhat it does
get()Read the current value. Returns initial() on first access within a context.
update(fn)Replace the value with fn(current).
Declare the handle once at module scope and import it wherever you read or write the slot.

Read and write state in a tool

agent/lib/budget.ts
import { defineState } from "eve/context";

export const budget = defineState("my-agent.budget", () => ({ count: 0, cap: 25 }));
agent/tools/spend.ts
import { defineTool } from "eve/tools";
import { z } from "zod";
import { budget } from "../lib/budget.js";
import { runQuery } from "../lib/warehouse.js";

export default defineTool({
  description: "Run a query, counting it against the session budget.",
  inputSchema: z.object({ sql: z.string() }),
  async execute({ sql }) {
    const { count, cap } = budget.get();
    if (count >= cap) throw new Error("Query budget exhausted for this session.");
    budget.update((s) => ({ ...s, count: s.count + 1 }));
    return runQuery(sql);
  },
});
get() and update() require an active eve context. Calling them outside tools, hooks, or framework-managed code throws.

Read state in a hook

Because the handle is declared at module scope, the same budget import works in a hook:
agent/hooks/log-budget.ts
import { defineHook } from "eve/hooks";
import { budget } from "../lib/budget.js";

export default defineHook({
  events: {
    async "turn.completed"(_event, ctx) {
      const { count, cap } = budget.get();
      console.info("budget after turn", { sessionId: ctx.session.id, count, cap });
    },
  },
});

Reset state between turns

State is durable by default and does not reset between turns. If you want a clean slate every turn, overwrite it from a lifecycle hook on turn.started:
agent/hooks/reset-budget.ts
import { defineHook } from "eve/hooks";
import { budget } from "../lib/budget.js";

export default defineHook({
  events: {
    async "turn.started"() {
      budget.update(() => ({ count: 0, cap: 25 }));
    },
  },
});
The hook imports the same module-scope budget handle as the tool, so both read and write the same slot.

State is never shared with subagents

Every subagent starts with its own fresh state, whether it’s a built-in agent copy or a declared specialist. defineState values never cross the parent/child boundary, even when the child is a copy of the same agent.

State vs external storage

defineState holds conversation-scoped working memory that lives and dies with the session:
Use caseWhere to store it
Running counters, current plan, what the user told you this conversationdefineState
Data that must outlive the sessionExternal store (connection or your own database)
Data shared across sessions or usersExternal store
Data that must be queried independently of a turnExternal store
Think of defineState as the agent’s short-term memory — durably persisted for the life of the session, but scoped entirely to it.

Hooks

Read and write state from lifecycle event handlers

Tools

Read state inside tool execute handlers

Execution Model & Durability

How step durability keeps state consistent across crashes

TypeScript API Reference

The ctx accessors available alongside state

Build docs developers (and LLMs) love