Skip to Content

Memory

The CMDOP agent has two kinds of memory: working memory (the messages in the current session) and persistent memory (a shared SQLite store the agent can write to across sessions). They serve different purposes and are wired in different places.

Two layers

LayerLifetimeWhere it lives
Working memoryOne sessionRunContext.Messages — message list rebuilt every turn
Persistent memoryCross-sessionmemory.db SQLite at utils.DataDir()/memory.db

Working memory is the conversation history the LLM sees on the current turn. Persistent memory is something the agent explicitly chose to remember by calling a memory tool.

Working memory — the turn cycle

Every turn the runner injects fresh system context (identity, env info, target machine hints) and replays the message history. There are no hidden long-term facts at this layer — what the LLM sees is exactly what is in the message list plus the system prompt.

Working memory carries:

  • The user’s prompts.
  • The assistant’s previous replies.
  • Tool call requests and tool results.
  • The current todo list (re-injected each turn so the agent stays oriented).

When the history grows large, the chat layer can call the Jarvis summary agent (internal/agent/jarvis/) to produce a brief summary that replaces older turns and reduces token debt.

Persistent memory — the SQLite store

memory.db is a single SQLite file lazy-opened by whichever consumer needs it first. Two consumers, one file:

ConsumerHelperWiring
Local chat (CLI / TUI / Desktop)desktop/services/chat/service.go::getOrCreateMemoryStorechat.Optional.MemoryStore
Remote ask_agent (daemon)network/connection/agent_service.go::getOrCreateMemoryStorechat.ForDaemonDeps.MemoryStore

Both helpers are mutex-guarded singletons. On open failure they log a warning and return nil — the chat continues, just without memory tools.

See internal/agent/memory/CLAUDE.md for the open-once contract.

Memory tools

When the store is wired, four tools register through session_builder.go::tools.PersistentMemory:

ToolPurpose
remember_topicStore a fact under a topic key.
recall_memoryLook up facts by topic or free-text query.
list_memoriesBrowse stored topics.
forget_topicRemove a topic and its associated facts.

If a session never sees these tools despite a healthy DB, the upstream façade (chat.ForCLI, ForDaemon, ForDesktop) is missing the MemoryStore field. That is the canonical debug path.

How the agent decides what to remember

The agent is responsible for calling the memory tools — the system does not auto-summarise into the store. Typical patterns the system prompt encourages:

  • Long-lived preferences (“user prefers TypeScript over JavaScript for new components”).
  • Project anchors (“the auth service is at services/auth”).
  • Cross-session facts the next turn would otherwise have to rediscover.

Things that should not go into persistent memory:

  • Secrets or credentials.
  • High-churn state (current branch, current todo).
  • Arbitrary chat content “just in case”.

Storage and isolation

Persistent memory is per-installation, not per-workspace. The store at utils.DataDir()/memory.db is shared across all sessions on that machine for the user that owns the daemon. If you need workspace isolation, use distinct OS users.

Switching workspaces does not switch memory stores. If you store project-specific context, prefix topics with the project name so recall stays clean.

Don’t store secrets in memory. The store is not encrypted and is readable by any process running as the same OS user.

What is not memory

Several adjacent features get confused with memory and are documented elsewhere:

  • Sessions — persistent transcripts, see Sessions.
  • Todo list — the current plan, replayed each turn but stored in the session, not in memory.db. See todo_write in Tools.
  • Skill state — skills run as sub-sessions; their internal state is theirs to manage.

TAGS: memory, persistent-memory, working-memory, memory-tools DEPENDS_ON: [agents, agent-loop, tools]

Last updated on