RecipesPer-conversation memory

Per-conversation memory

Sometimes you don’t want long-term memory — you want a conversation sandbox that knows everything within the chat and nothing across chats.

The scope trick

Mnemosyne writes can carry a scope tag. The recall query filters on it:

// On every write
await mnemo.facts.create({
  content: extracted,
  scope: "conversation",
  scopeRef: conversationId,
});
 
// On every recall
const result = await mnemo.recall({
  query,
  filter: { scope: "conversation", scopeRef: conversationId },
});

Now the agent only retrieves facts from this specific conversation.

The “don’t learn from this conversation” toggle

In the Orchester UI (the project that built Mnemosyne) there’s a toggle called “Don’t learn from this conversation”. Under the hood it sets scope = "conversation" and scope_ref = conversationId for every write, and at session end the conversation-scoped facts are closed en masse:

await mnemo.facts.closeByScope({
  scope: "conversation",
  scopeRef: conversationId,
});

The bitemporal interval closes, the facts move to mnemo_fact_archive, and they don’t show up in future recalls.

Why bitemporal scoping is better than deleting

If you delete, you lose the audit trail. If you close, the row stays queryable for compliance review but doesn’t pollute future recall.

A regulator asking “did your bot ever know X?” can be answered with a historical query; a customer asking “stop using X in future answers” is honoured by closing the validity window.

Scope hierarchy

ScopeExamples
globalDefaults that apply to every conversation
workspacePer-tenant, applies across all conversations
teamPer-team within a workspace
employeePer-user / per-agent
conversationPer-session, doesn’t leak
agentPer-agent identity, regardless of which user is talking

Recall composes them — by default a recall against conversation:X sees its own facts plus employee:Y plus workspace:Z plus global. The hierarchy is documented in the architecture spec.