Feature

Memory.

Curtis.ai remembers durable facts about you across conversations. This doc explains what gets stored, where, how it's used, how to manage it, and the editing UI new in…

Curtis.ai remembers durable facts about you across conversations. This doc explains what gets stored, where, how it's used, how to manage it, and the editing UI new in v1.0.

What memory is for

Facts the model should know in every future chat:

  • Preferences — "I prefer concise answers without preamble"
  • Identity — "My name is Jordan, I work primarily in Rust"
  • Projects — "My main project is Aurora, a Rust async runtime"
  • Instructions — "Always cite sources as wikilinks when you reference a note"

What it's not for:

  • The topic of this particular chat (that's conversation history)
  • Ephemeral requests ("summarize this for me right now")
  • Things the model is doing this turn

Where facts live

By default: AI/Curtis Memory.md in your vault. Configurable in Settings → Memory → Memory file path.

The file is plain markdown — one bullet per fact, with hidden HTML-comment metadata for id + timestamp:

# Curtis Memory

Long-term facts about the user, captured during chat and editable by hand.
Delete a line to forget; edit a line to correct.

- I prefer concise answers without preamble [preference] <!-- id:abc123 updated:1784594521 -->
- My main project is Aurora, a Rust async runtime [project] <!-- id:def456 updated:1784594600 -->
- Always cite sources as wikilinks [instruction] <!-- id:ghi789 updated:1784594700 -->

You can edit this file directly — add bullets, delete them, rewrite them. The plugin watches the file and reloads on save.

How facts get in

Ask before saving (default)

After each assistant turn, Curtis.ai fires a background call to the active model with a strict extraction prompt:

"Extract 0-3 durable facts about the user from this chat turn. A durable fact is something true across future conversations: a preference, identity trait, long-lived project detail, or standing instruction. Do NOT capture ephemeral requests. Respond with ONLY a JSON array."

Proposed facts appear under the last message as a Worth remembering? bar — each with Save and Skip. Nothing is written to the memory file until you tap Save. Facts you skip are not proposed again that session; the bar itself is transient — sending your next message clears it, so decide before you type. Up to three proposals per turn, deduped against what you already saved.

Extraction is best-effort: failures are silent and non-fatal. Note the extraction call goes through your active provider, like every other request.

Save silently (opt-in)

Same extraction, no confirmation — extracted facts are written directly to the memory file after each turn. Choose this in Settings → Memory → Fact capture → Save silently after each turn if you prefer convenience over review.

Manual only

  • /remember <fact> — type it in chat
  • Right-click any selection in a note → Save to memory — stores the highlighted text verbatim

Manual facts go through the same addFact API and dedupe against existing facts (case-insensitive exact match).

How facts get out

Every prompt includes a ## What you know about the user block appended to the system prompt:

## What you know about the user
- I prefer concise answers without preamble [preference]
- My main project is Aurora, a Rust async runtime [project]
- Always cite sources as wikilinks [instruction]

Full injection, not retrieval. Every fact goes in every prompt. This works because capture is signal-gated (the model only stores durable facts) so the set stays small — typically tens of facts, not thousands.

Managing memory

ActionHow
List recent facts/memory (shows last 8 in a Notice)
Open the file/memory open or Settings → Memory → Open
Forget one fact/forget <substring>
Clear everything/memory clear or Settings → Memory → Clear
Edit a factSettings → Memory → fact list → Edit (new in v1.0)
Delete a factSettings → Memory → fact list → Delete (new in v1.0)
Add a fact by handOpen the file and add a new bullet, or use /remember

Memory editing UI (new in v1.0)

Before v1.0, the only ways to fix an incorrect fact were:

  • Edit the markdown file by hand
  • /forget then /remember (two steps, loses the original timestamp)

v1.0 adds an edit/delete UI directly in Settings → Memory. Every fact in the memory file renders as a row with two buttons:

  • Edit — opens a modal with the fact's content and category. Change either, click Save. The fact's id is preserved; only content, category, and updated timestamp change.
  • Delete — removes the fact immediately (with a confirmation prompt for safety).

This makes memory correction a first-class operation — no more file editing or two-step forget/remember.

updateFact API (power users)

If you're scripting against the plugin or writing tools that need to mutate memory programmatically, the underlying API is:

class MemoryStore {
  // Update an existing fact by ID. Returns the updated fact or null if not found.
  async updateFact(id: string, content: string, category?: string): Promise<MemoryFact | null>;

  // Delete a fact by ID. Returns true if a fact was deleted.
  async deleteFact(id: string): Promise<boolean>;
}

The category parameter is optional — pass it to change category, omit to preserve the existing one. Validation matches the markdown-file rules: must be one of preference | identity | project | instruction | other (or empty string for "other").

The Edit Fact modal in settings calls updateFact under the hood.

Privacy

  • The memory file lives in your vault — readable, editable, syncable via Syncthing/git like any other note.
  • Facts are injected into the system prompt of every chat. If your active provider is a cloud API (OpenAI, Anthropic, etc.), facts are sent to that provider along with your message.
  • For fully private memory, switch to a local provider (Ollama, LM Studio).
  • Facts never leave your machine except via the active AI provider. No telemetry, no analytics.

Design notes

The memory system is modeled on obsidian-copilot's user-memory layer (see the design doc in their src/memory/). Key choices:

  • Markdown over JSON — user-editable, survives plugin uninstall, no corruption risk
  • LLM-gated capture over regex — the model decides what's durable, so recall can be trivial (full injection)
  • Ratified capture over silent capture — v1.5.1: the model proposes, the user decides. Silent persistence contradicts the plugin's consent posture, and human ratification filters the classic auto-memory failure modes (poisoned and stale facts) at the gate
  • Full injection over retrieval — at the scale of personal facts (tens, not thousands), retrieval adds complexity for no benefit
  • Categories as enum — preference | identity | project | instruction | other. Keeps the file parseable even with hand-edits.
  • Edit UI over file-only — added in v1.0 because the file-only workflow made correcting facts needlessly high-friction.

See the codebase (src/memory/memory.ts) for implementation details.