---
title: "Remember the Bikes"
description: "Use defineState to give the dispatcher a durable garage that survives across turns, then build remember_bike and recall_bikes tools to write and read it."
canonical_url: "https://vercel.com/academy/building-agents-with-eve/remember-the-bikes"
md_url: "https://vercel.com/academy/building-agents-with-eve/remember-the-bikes.md"
docset_id: "vercel-academy"
doc_version: "1.0"
last_updated: "2026-08-08T22:50:08.934Z"
content_type: "lesson"
course: "building-agents-with-eve"
course_title: "Building Agents with eve"
prerequisites:  []
---

<agent-instructions>
Vercel Academy — structured learning, not reference docs.
Lessons are sequenced.
Adapt commands to the human's actual environment (OS, package manager, shell, editor) — detect from project context or ask, don't assume.
The lesson shows one path; if the human's project diverges, adapt concepts to their setup.
Preserve the learning goal over literal steps.
Quizzes are pedagogical — engage, don't spoil.
Quiz answers are included for your reference.
</agent-instructions>

# Remember the Bikes

# Remember the Bikes

A regular rolls up: "It's the commuter again, the rear shifting's gone sloppy." A good front-desk advisor doesn't say "and which bike is the commuter?" They already know it's the Surly Disc Trucker with the 700c wheels, because the shop keeps a card on file.

Our dispatcher has no card. Tell it about your Surly in one turn, ask it to book work on "the commuter" in the next, and it draws a blank. A tool can answer a question, but it has no memory between calls. The agent needs somewhere to keep what it has been told.

`defineState` provides a durable, per-session slot that the agent can write and read across turns. You'll declare it in `agent/lib/garage.ts`, then give the dispatcher two tools that use it.

## Outcome

The dispatcher saves a customer's bike and recalls it in a later turn, without asking them to repeat the details.

## Hands-on exercise

First, the memory itself. Create `agent/lib/garage.ts`:

```ts title="agent/lib/garage.ts"
import { defineState } from "eve/context";

export interface Bike {
  make: string;
  model: string;
  wheelSize?: string;
  notes?: string;
}

export interface Garage {
  readonly bikes: Readonly<Record<string, Bike>>;
}

export const garage = defineState<Garage>("bikeshop.garage", () => ({
  bikes: {},
}));
```

`defineState(name, initial)` returns a typed handle with two methods. `get()` reads the current value and runs `initial()` the first time. `update(fn)` replaces it. The value survives across the step and turn boundaries of a session, so data written on turn one is still there on turn five. Declaring the handle at module scope lets every importing tool share the same slot.

Now the hands. Two tools, both importing `garage` from `../lib/garage.js`:

**`remember_bike`** takes a `label` (a short name like "the commuter") plus the bike's `make`, `model`, and optional `wheelSize` and `notes`. It writes the bike into the garage with `garage.update(...)`, then returns the updated garage so the model can confirm.

**`recall_bikes`** takes no input and returns `garage.get()`, so the dispatcher can check what's on file before asking the customer to repeat themselves.

\*\*Warning: State is the agent's short-term memory, not a database\*\*

`defineState` lives and dies with the session: it's perfect for "what has this customer told me this conversation." Anything that must outlive the session, or be shared across customers, belongs in a real database. We're remembering bikes for the length of a chat, which is exactly its job.

## Try It

Save a bike in one turn, then reference it in the next, two separate turns, same session:

```text
you › it's a Surly Disc Trucker, 700c wheels. call it "the commuter".

  ⚙ remember_bike  { label: "the commuter", make: "Surly", model: "Disc Trucker", wheelSize: "700c" }

dispatcher › Got it, I've saved the commuter (Surly Disc Trucker, 700c) to your file.

you › the commuter's rear shifting feels sloppy. what do you recommend?

  ⚙ recall_bikes  {}
  ↳ { bikes: { "the commuter": { make: "Surly", model: "Disc Trucker", wheelSize: "700c" } } }

dispatcher › Sloppy rear shifting on the commuter usually means the derailleur
needs adjusting, sometimes a cable. A Basic Tune-Up ($65) covers that. Want me
to check openings?
```

The second turn never asked "which bike?" The dispatcher retrieved the commuter from the garage because `defineState` held the record between turns.

\*\*Note: Why it persisted\*\*

State checkpoints at step boundaries, using the same durability you saw as `session.waiting` in 1.3. Eve writes the value into the durable session, so a restart or mid-conversation redeploy doesn't wipe the commuter from the file.

If the second turn asks "which bike?" anyway, check that both tools import the *same* handle from `../lib/garage.js`. Two `defineState` calls with the same name are still separate slots in your code. If `recall_bikes` never fires, update the persona so it checks `recall_bikes` before asking a customer to repeat details.

## Done-When

- [ ] `remember_bike` writes a bike via `garage.update(...)` and returns the garage.
- [ ] `recall_bikes` returns `garage.get()` with an empty input schema.
- [ ] Saving a bike in one turn and referencing it in a later turn works without re-asking.
- [ ] Both tools import the same `garage` handle from `../lib/garage.js`.

## Solution

```ts title="agent/tools/remember_bike.ts"
import { defineTool } from "eve/tools";
import { z } from "zod";
import { garage } from "../lib/garage.js";

export default defineTool({
  description:
    "Save a customer's bike so the shop remembers it across visits " +
    "(make, model, wheel size, and any standing notes).",
  inputSchema: z.object({
    label: z.string().describe("A short name for the bike, e.g. 'the commuter'."),
    make: z.string(),
    model: z.string(),
    wheelSize: z.string().optional(),
    notes: z.string().optional(),
  }),
  async execute({ label, make, model, wheelSize, notes }) {
    garage.update((g) => ({
      bikes: { ...g.bikes, [label]: { make, model, wheelSize, notes } },
    }));
    return garage.get();
  },
});
```

```ts title="agent/tools/recall_bikes.ts"
import { defineTool } from "eve/tools";
import { z } from "zod";
import { garage } from "../lib/garage.js";

export default defineTool({
  description: "Read the bikes the shop has on file for this customer.",
  inputSchema: z.object({}),
  async execute() {
    return garage.get();
  },
});
```

The dispatcher now has tools and memory. Next, it will choose a playbook based on *who's* standing at the counter.


---

[Full course index](/academy/llms.txt) · [Sitemap](/academy/sitemap.md)
