Installation
npx shadcn@latest add @pixeldosa/agent-memoryRequires `cn` at `@/lib/utils`. `onForget` should really delete. The component keeps its own copy for Undo. When a user edits an inferred memory, store it as `stated`, because they have now said it.
Usage
"use client";
import { AgentMemory } from "@/components/ui/agent-memory";
<AgentMemory
memories={memories}
onEdit={(id, text) => memory.update(id, { text, origin: "stated" })}
onForget={(id) => memory.delete(id)}
onRestore={(item) => memory.restore(item)}
onForgetAll={() => memory.clear()}
paused={settings.memoryPaused}
onPausedChange={(paused) => save({ memoryPaused: paused })}
/>
Why it exists
An agent that remembers you is only trustworthy if you can see what it remembers. And the risky memories aren't the ones you told it. They're the ones it inferred. A wrong guess like "manages a team of eight" quietly shapes every later answer, and you never see why.
Features
- Stated and inferred, kept apart. What you said sits in one list. What it picked up sits in another, framed as "Guesses from how you work. Worth checking."
- Where it came from. Each memory can show its source: "Noticed across 6 conversations".
- Edit in place. Enter saves and Escape cancels. An edited guess becomes something you stated.
- Forget means now.
onForgetfires immediately. The row stays put, struck through, with Undo, and focus moves to it. - Pause means pause. Nothing new is remembered, and the note says so. Existing memories are still used until you forget them.
- Forget everything needs a second step that says it can't be undone.
Examples
Only ask about inferences, for a lighter check-in:
<AgentMemory memories={memories.filter((m) => m.origin === "inferred")} {...handlers} />
Props
| Prop | Type | Description |
|---|---|---|
memories | { id, text, origin: "stated" | "inferred", source? }[] | Required. |
onEdit | (id, text) => void | Required. |
onForget | (id) => void | Required. Really delete. The component keeps a copy for Undo. |
onRestore | (memory) => void | Enables Undo. |
onForgetAll | () => void | Shows "Forget everything…". |
paused | boolean | |
onPausedChange | (paused) => void | Shows the Remember new things switch. |
headingLevel | 2 | 3 | 4 | 5 | 6 | Heading level, so the component fits your page's outline. Defaults to 3. |
Accessibility
- A labelled region, with Edit and Forget buttons named after the memory they act on.
- The pause control is a real
role="switch". - Focus moves to Undo after forgetting, and back to Forget after restoring.
- Forgets and restores are announced by content in a polite live region.
- Forget all's confirmation focuses Cancel, the safe choice.
When not to use
It isn't chat history, and it isn't a data export. It shows the facts the agent carries
into future conversations. Use Autonomy Control for what the agent may do on its own.
why it's built this way
Engineering Notes
Built around the difference between stated and inferred memory. From the model side, an inferred preference that is wrong is worse than no memory, because it shapes answers without the user ever seeing why. So origin is a required field, the two kinds are listed in separate sections (the inferred list has a dashed border and reads 'Guesses from how you work'), and each memory can carry its source. Forgetting is honest about timing. `onForget` fires at once, so the caller can really delete it, and the component keeps a local snapshot with the original position so the row stays in place, struck through, with Undo. Focus moves to Undo after a forget and back to Forget after a restore, so keyboard users never lose their place. Pausing uses a real `role="switch"`, and its note says exactly what pausing does and doesn't do, because 'paused' is widely misread as 'deleted'. Forget all is the only irreversible action. It opens an inline confirmation with focus on Cancel and uses the destructive intent. A polite live region announces forgets and restores by content.
how it moves, and when it doesn't
Motion Notes
Still, apart from direct feedback. It is the user's turn, so nothing moves unless the user acts. The pause switch thumb slides and its track colour changes at `--pd-duration-fast` on the standard curve. Hover colour changes run at `--pd-duration-instant`. Forgetting doesn't collapse or fade the row. It swaps to its struck-through state instantly and keeps its height, so the list doesn't shift under the pointer and Undo appears exactly where the user clicked. Everything is instant under prefers-reduced-motion.