Summary
When the same agent participates in multiple groups (projects), its memory,
climate, and persona are keyed only to agent_id — they are global across all
conversations. Only the raw message thread is scoped per conversation (via
conversation_reads). The result: an agent working in group B "remembers" and
acts on the context from group A, mixing its group A work into group B.
This makes running several independent projects with the same agent set
impractical. Today you either accept the bleed, manually wipe memory files, or
spin up a separate agent per project (N projects → N agents) even when the
underlying LLMs are identical.
Concrete symptom (BYOA)
Agent nova sits in multiple project groups. Its memory index
(~/.cumora/agents/<id>/memory/MEMORY.md) ends up indexing five unrelated
projects (paper review, article review, PPT repo, OpenRouter plan, …). In a new
group about one project, nova reads all five and treats the others as
background — exactly the "agent1 in group2 treats its group1 work as
background" symptom.
Root cause
agent_memory / agent_workspace (memory) are keyed by agent_id only
(server/src/db/migrate.ts).
- Retrieval in
server/src/agents/runtime/inproc-client.ts (loadMemory)
filters only agent_id + path LIKE 'memory/%' — no project/conversation
dimension.
- Climate (
loadClimate) is likewise per-agent and global.
projects + conversations.project_id exist, and the wake prompt already
surfaces "Project: " to the agent — but that is a label, not an
isolation boundary.
- The plumbing to fix this already exists:
agent_memory.source stores
conversationId, and conversations.project_id links a conversation to a
project — it is just not used to filter memory.
Why the current workarounds are painful
- Manual wipe (documented in
COORDINATION.md under "memory pollution"):
read each memory file, delete the overfit ones, re-wipe every time pollution
re-accumulates.
- One agent per project: for N projects you maintain N agents (and their
homes) even though you may only have 2 underlying LLMs — defeating the point
of a reusable agent identity.
Proposed fix
Add a scope dimension to memory and filter retrieval on the current
conversation's project (fall back to global when a conversation has no project):
- Add nullable
project_id to agent_memory / agent_workspace, or derive it
from source.conversationId → conversations.project_id.
- In
loadMemory, join the current conversation(s) to their project and
include global memories + the current project's memories, excluding other
projects'.
- Optionally scope climate the same way (or keep it global and document the
tradeoff).
- For BYOA, scope the injected
MEMORY.md digest / memory-directory read to
the current conversation's project.
Related
I did not find an existing issue or discussion covering cross-group context
isolation or per-project memory scoping. This is distinct from the documented
agent↔agent isolation (separate home dirs), which works fine — the gap is
within a single agent across groups.
Summary
When the same agent participates in multiple groups (projects), its memory,
climate, and persona are keyed only to
agent_id— they are global across allconversations. Only the raw message thread is scoped per conversation (via
conversation_reads). The result: an agent working in group B "remembers" andacts on the context from group A, mixing its group A work into group B.
This makes running several independent projects with the same agent set
impractical. Today you either accept the bleed, manually wipe memory files, or
spin up a separate agent per project (N projects → N agents) even when the
underlying LLMs are identical.
Concrete symptom (BYOA)
Agent
novasits in multiple project groups. Its memory index(
~/.cumora/agents/<id>/memory/MEMORY.md) ends up indexing five unrelatedprojects (paper review, article review, PPT repo, OpenRouter plan, …). In a new
group about one project, nova reads all five and treats the others as
background — exactly the "agent1 in group2 treats its group1 work as
background" symptom.
Root cause
agent_memory/agent_workspace(memory) are keyed byagent_idonly(
server/src/db/migrate.ts).server/src/agents/runtime/inproc-client.ts(loadMemory)filters only
agent_id+path LIKE 'memory/%'— no project/conversationdimension.
loadClimate) is likewise per-agent and global.projects+conversations.project_idexist, and the wake prompt alreadysurfaces "Project: " to the agent — but that is a label, not an
isolation boundary.
agent_memory.sourcestoresconversationId, andconversations.project_idlinks a conversation to aproject — it is just not used to filter memory.
Why the current workarounds are painful
COORDINATION.mdunder "memory pollution"):read each memory file, delete the overfit ones, re-wipe every time pollution
re-accumulates.
homes) even though you may only have 2 underlying LLMs — defeating the point
of a reusable agent identity.
Proposed fix
Add a scope dimension to memory and filter retrieval on the current
conversation's project (fall back to global when a conversation has no project):
project_idtoagent_memory/agent_workspace, or derive itfrom
source.conversationId → conversations.project_id.loadMemory, join the current conversation(s) to their project andinclude global memories + the current project's memories, excluding other
projects'.
tradeoff).
MEMORY.mddigest / memory-directory read tothe current conversation's project.
Related
I did not find an existing issue or discussion covering cross-group context
isolation or per-project memory scoping. This is distinct from the documented
agent↔agent isolation (separate home dirs), which works fine — the gap is
within a single agent across groups.