> ## Documentation Index
> Fetch the complete documentation index at: https://anvil.thatdevguy.in/llms.txt
> Use this file to discover all available pages before exploring further.

# Session Memory: Persist Agent Conversational State

> Use MemoryStore and the withMemory middleware to give each conversation its own typed, namespaced key-value memory backed by a pluggable StateStore.

Every agent that spans more than one request needs a place to remember things — a user's name, the last tool result, a running total. Anvil's `MemoryStore` gives you a typed, namespaced key-value store attached directly to the request context so you never have to thread raw store handles through your handler code.

## How it works

`MemoryStore` wraps any `StateStore` backend and scopes every key to a namespace, so two sessions never collide. The `withMemory` middleware creates a fresh `MemoryStore` for each incoming request and writes it to `ctx.state.memory`. Your handler reads and writes memory without knowing anything about the underlying store.

```
Request → withMemory middleware → ctx.state.memory (MemoryStore) → your handler
```

<Note>
  The default backend is SQLite (via `SqliteStateStore`). You can swap in any implementation of the `StateStore` interface — see [Custom backends](#custom-backends) below.
</Note>

## Quick start

<Steps>
  <Step title="Open (or create) a SQLite state store">
    ```ts theme={null}
    import { SqliteStateStore } from 'anvil/store';

    const store = await SqliteStateStore.open(); // writes to .anvil/state.db
    ```
  </Step>

  <Step title="Register the withMemory middleware">
    Apply `withMemory` to any router or route that needs per-session memory.

    ```ts theme={null}
    import { withMemory } from 'anvil/memory';
    import { createApp } from 'anvil';

    const app = createApp();
    app.use(withMemory(store));
    ```
  </Step>

  <Step title="Read and write from your handler">
    Access the `MemoryStore` through `ctx.state.memory` (or use the `getMemory` helper for typed access).

    ```ts theme={null}
    import { getMemory } from 'anvil/memory';

    app.post('/chat', async (ctx) => {
      const memory = getMemory(ctx)!;

      // Remember the user's name the first time they introduce themselves
      const name = await memory.get<string>('userName');
      if (!name) {
        await memory.set('userName', 'Alice');
      }

      // Append to a conversation log
      await memory.append('history', { role: 'user', text: ctx.body.message });
    });
    ```
  </Step>
</Steps>

## Namespacing

Each `MemoryStore` instance is scoped to a namespace so that different sessions or users never read each other's data. By default `withMemory` derives the namespace from the `x-session-id` request header, falling back to `'default'` when the header is absent.

Pass a `namespace` function to compute a namespace from any request property:

```ts theme={null}
// Use the authenticated user ID as the namespace
app.use(withMemory(store, (ctx) => ctx.state.userId ?? 'anonymous'));

// Use a query parameter
app.use(withMemory(store, (ctx) => ctx.query.get('sessionId') ?? 'default'));
```

<Tip>
  Use a stable, user-scoped identifier as the namespace when you want memory to persist across browser sessions or devices.
</Tip>

## MemoryStore API

### `memory.get<T>(key)`

Returns the stored value cast to `T`, or `undefined` if the key does not exist.

```ts theme={null}
const name = await memory.get<string>('userName');
// → 'Alice' | undefined
```

### `memory.set(key, value)`

Stores any JSON-serializable value under `key`. Overwrites any existing value.

```ts theme={null}
await memory.set('preferences', { theme: 'dark', language: 'en' });
```

### `memory.append<T>(key, item)`

Pushes `item` onto the end of the array stored at `key`. If the key does not exist, creates a new single-element array. Returns the new array length.

```ts theme={null}
const length = await memory.append<ChatMessage>('history', {
  role: 'user',
  text: 'Hello!',
});
// → 1 (first message in the array)
```

### `memory.delete(key)`

Removes the key from the namespace. No-op if the key does not exist.

```ts theme={null}
await memory.delete('temporaryContext');
```

### `memory.keys()`

Returns all keys registered under the current namespace. Useful for inspecting or clearing state.

```ts theme={null}
const keys = await memory.keys();
// → ['userName', 'history', 'preferences']
```

## Full example: remembering a user's name

The following route greets a returning user by name and saves new users for future requests.

```ts theme={null}
import { createApp } from 'anvil';
import { withMemory, getMemory } from 'anvil/memory';
import { SqliteStateStore } from 'anvil/store';

const store = await SqliteStateStore.open();
const app = createApp();

// Derive namespace from x-session-id header (the default behaviour)
app.use(withMemory(store));

app.post('/greet', async (ctx) => {
  const memory = getMemory(ctx)!;
  const { name } = await ctx.json<{ name?: string }>();

  if (name) {
    await memory.set('userName', name);
  }

  const stored = await memory.get<string>('userName');
  const greeting = stored
    ? `Welcome back, ${stored}!`
    : 'Hello, stranger! Tell me your name.';

  return ctx.json({ greeting });
});

export default app;
```

<Tip>
  Pass the same `x-session-id` header with every request from a client to keep memory consistent across the conversation.
</Tip>

## Custom backends

`MemoryStore` works with any object that satisfies the `StateStore` interface. You can provide Redis, Postgres, or any other persistence layer:

```ts theme={null}
import type { StateStore } from 'anvil/store';

class RedisStateStore implements StateStore {
  async get<T>(key: string): Promise<T | undefined> {
    const raw = await redis.get(key);
    return raw ? (JSON.parse(raw) as T) : undefined;
  }

  async set(key: string, value: unknown): Promise<void> {
    await redis.set(key, JSON.stringify(value));
  }

  async delete(key: string): Promise<void> {
    await redis.del(key);
  }

  async list(prefix?: string): Promise<string[]> {
    return redis.keys(prefix ? `${prefix}*` : '*');
  }
}

app.use(withMemory(new RedisStateStore()));
```

The `StateStore` interface requires four methods:

| Method   | Signature                              | Description                              |
| -------- | -------------------------------------- | ---------------------------------------- |
| `get`    | `get<T>(key): Promise<T \| undefined>` | Retrieve a value by key                  |
| `set`    | `set(key, value): Promise<void>`       | Persist any JSON-serializable value      |
| `delete` | `delete(key): Promise<void>`           | Remove a key                             |
| `list`   | `list(prefix?): Promise<string[]>`     | List keys, optionally filtered by prefix |

<Note>
  For development and tests, use the zero-dependency `MemoryStateStore` from `anvil/store`. It stores values in-process and requires no native dependencies.
</Note>
