---
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-06-30T10:41:22.893Z"
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. Tools didn't fix this, because a tool answers a question and forgets, it has no memory between calls. What the agent needs is somewhere to *keep* what it's been told.

That's what `defineState` is: a durable, per-session slot the agent can write to and read back, turns later. You'll declare that slot 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)` hands you a typed handle with two methods: `get()` reads the current value (running `initial()` the first time), and `update(fn)` replaces it. The value is durable, it survives across the step and turn boundaries of a session, so what you write on turn one is still there on turn five. We declared the handle at module scope so any tool that imports it shares 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 just 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 pulled the commuter straight from the garage. That's `defineState` doing its one job: holding onto something between turns.

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

State checkpoints at step boundaries, the same durability you saw as `session.waiting` in 1.3. It's not a variable living in memory that a restart would wipe; it's written into the durable session. Redeploy mid-conversation and the commuter's still on file.

If the second turn asks "which bike?" anyway, check that both tools import the *same* handle from `../lib/garage.js`, two separate `defineState` calls with the same name are still two slots in your code. And if `recall_bikes` never fires, nudge the persona: it should check `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 a memory. Next we give it judgment, a way to behave differently depending on *who's* standing at the counter.


---

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