Skip to content

Offer mem0 as a place for shared memory to live - #97

Merged
ParallelEntrepreneur merged 1 commit into
mainfrom
feat/mem0-memory
Sep 17, 2026
Merged

ParallelEntrepreneur merged 1 commit into
mainfrom
feat/mem0-memory

Conversation

@ParallelEntrepreneur

@ParallelEntrepreneur ParallelEntrepreneur commented Sep 17, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Shared memory gets a second provider beside files. Under Settings → Modules → Memory → Provider, mem0 keeps approved notes in your mem0 project through its Platform API (v3), and each colony's MEMORY.md lists the notes most relevant to its task first.

Nothing a colony can see or do changes, and that's deliberate.

  • Review stays on the Mothership. Proposals queue locally as before, and mem0 only receives a note once you approve it.
  • Notes are stored exactly as approved. Every write sends infer: false and immutable: true. With inference on, mem0's extraction model rewrites what it stores and later merges it with other memories, so a colony could read text nobody reviewed. add also treats a response without stored results as an error, because that's what an inferred write looks like.
  • Colonies never talk to mem0 and never see the key. At boot the Mothership writes the colony's three scopes into its read-only session directory, in exactly the layout files mounts. memory_search, memory_propose and the prompt work with no change inside the guest.
  • Other tools' memories are left alone. A scope is a mem0 user_id (colonizer:repo:<owner>/<repo> and so on), and every memory carries app_id: colonizer. Listing filters on both. Delete fetches the memory first and checks both, because a mem0 project can be shared.
  • The key is stored like provider keys. It lives in config/memory-keys/mem0 (0600) with MEM0_API_KEY as a fallback, and is never written to modules.json or returned by the API. It gets its own row in Settings with Save, Remove and Check.

Failure never costs a colony or a proposal

When What happens
mem0 is down at boot The colony starts with an empty layout and logs a warning
An approval can't reach mem0 502 with the reason, and the proposal stays in the queue
Review is off and the store fails The note is queued for review instead of dropped

One design choice worth a look

The relevance query is the task (issue title, instructions, issue body), not the prompt. The prompt was 2,225 characters in testing, and most of it is harness boilerplate shared by every colony. Sending it would pull every ranking toward the same notes, and a long preamble could push the task past the query limit. The query in the same test was 85 characters.

Test plan

  • cargo test: 107 pass, 8 new. They cover the verbatim and immutable write, scope and app isolation on list and delete, refusing unsafe ids, a rejected key never appearing in an error, the colony layout ordered by relevance and rewritten on resume, and the task query.
  • Clippy reports the same 11 warnings as main. tsc --noEmit and vite build are clean.
  • End to end on a separate instance (its own port, data and config) against a local stand-in for the v3 API, since no mem0 key was available:
    • Picked mem0 in Settings, saved the key, and Check said "mem0 accepted the key". The key isn't in modules.json or in either API response, and the file is 0600 inside a 0700 directory.
    • Three notes reached the stand-in with the right scope user_id, app_id=colonizer, infer=false and immutable=true.
    • A real colony booted with shared memory: 3 notes from mem0, most relevant to this task first. Notes for another repo were left out.
    • Its agent read both indexes, found the mem0 note with memory_search, and proposed a note that came back through review.
    • Approving with mem0 stopped returned 502 and kept the proposal. Approving with mem0 back stored it with the colony and repo it came from.
  • Against the real mem0 Platform with a real key. The stand-in follows mem0's published OpenAPI spec, but it isn't mem0.

Out of scope

  • Self-hosted mem0 serves a different API. README and the Settings description say so.
  • Semantic search from inside a colony. That would need a new colony-to-Mothership endpoint with scope enforcement, which deserves its own review rather than riding along here.

Shared memory gains a second provider beside `files`: approved notes are stored
in a mem0 project through the Platform API (v3). Everything a colony can see or
do is unchanged, which is the point of how it is built rather than a limitation
of it.

Review stays on the Mothership. Proposals queue locally as before, and mem0
only receives a note once a human approves it. Every note is written with
`infer: false` and `immutable: true`: with inference on, mem0's extraction
model rewrites what it stores and later consolidates it with other memories, so
a colony could end up reading text no human reviewed — the one thing review
exists to prevent. `add` also refuses to call it a success when mem0 answers
without stored results, since that is what an inferred write looks like.

Colonies never reach mem0 and never see the key. At boot the Mothership lists
the colony's three scopes and writes them into its read-only session directory
in exactly the layout `files` mounts, so `memory_search`, `memory_propose` and
the prompt pointing at MEMORY.md work without a line changing in the guest.
The index is ordered by mem0's relevance to the task — the issue title,
instructions and body, not the full prompt, which is mostly boilerplate shared
by every colony and would pull every ranking toward the same notes.

A scope is a mem0 `user_id` (`colonizer:repo:<owner>/<repo>` and so on) and
every memory carries `app_id: colonizer`. Listing filters on both, and delete
first fetches the memory and checks both, because a mem0 project may hold other
tools' memories and an id from the web UI proves neither.

The key is stored like provider keys (config/memory-keys/mem0, 0600), falls
back to MEM0_API_KEY, and is never written to modules.json nor returned by the
API. Settings gets its own key row with Save, Remove and Check.

Failure never costs a colony or a proposal. mem0 down at boot: the colony
starts with an empty layout and a warning. An approval that cannot reach mem0
returns 502 and puts the proposal back. With review off, a note that cannot be
stored is queued for review instead of dropped.

Verified end to end on a separate instance against a local stand-in for the v3
API (no mem0 key was available): key saved through the UI and checked, three
notes stored with infer false and immutable true, a real colony booted with
"shared memory: 3 notes from mem0, most relevant to this task first", its agent
read both indexes and found the mem0 note with memory_search, its proposal came
back through review, an approval with mem0 stopped returned 502 and kept the
proposal, and the same approval with mem0 back stored it with its provenance.

107 tests pass, 8 of them new; clippy's warning set is identical to main's.
Self-hosted mem0 serves a different API and is out of scope, as is semantic
search from inside a colony.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@ParallelEntrepreneur
ParallelEntrepreneur merged commit d0b13cd into main Sep 17, 2026
ParallelEntrepreneur added a commit that referenced this pull request Sep 23, 2026
Shared memory gains a second provider beside `files`: approved notes are stored
in a mem0 project through the Platform API (v3). Everything a colony can see or
do is unchanged, which is the point of how it is built rather than a limitation
of it.

Review stays on the Mothership. Proposals queue locally as before, and mem0
only receives a note once a human approves it. Every note is written with
`infer: false` and `immutable: true`: with inference on, mem0's extraction
model rewrites what it stores and later consolidates it with other memories, so
a colony could end up reading text no human reviewed — the one thing review
exists to prevent. `add` also refuses to call it a success when mem0 answers
without stored results, since that is what an inferred write looks like.

Colonies never reach mem0 and never see the key. At boot the Mothership lists
the colony's three scopes and writes them into its read-only session directory
in exactly the layout `files` mounts, so `memory_search`, `memory_propose` and
the prompt pointing at MEMORY.md work without a line changing in the guest.
The index is ordered by mem0's relevance to the task — the issue title,
instructions and body, not the full prompt, which is mostly boilerplate shared
by every colony and would pull every ranking toward the same notes.

A scope is a mem0 `user_id` (`colonizer:repo:<owner>/<repo>` and so on) and
every memory carries `app_id: colonizer`. Listing filters on both, and delete
first fetches the memory and checks both, because a mem0 project may hold other
tools' memories and an id from the web UI proves neither.

The key is stored like provider keys (config/memory-keys/mem0, 0600), falls
back to MEM0_API_KEY, and is never written to modules.json nor returned by the
API. Settings gets its own key row with Save, Remove and Check.

Failure never costs a colony or a proposal. mem0 down at boot: the colony
starts with an empty layout and a warning. An approval that cannot reach mem0
returns 502 and puts the proposal back. With review off, a note that cannot be
stored is queued for review instead of dropped.

Verified end to end on a separate instance against a local stand-in for the v3
API (no mem0 key was available): key saved through the UI and checked, three
notes stored with infer false and immutable true, a real colony booted with
"shared memory: 3 notes from mem0, most relevant to this task first", its agent
read both indexes and found the mem0 note with memory_search, its proposal came
back through review, an approval with mem0 stopped returned 502 and kept the
proposal, and the same approval with mem0 back stored it with its provenance.

107 tests pass, 8 of them new; clippy's warning set is identical to main's.
Self-hosted mem0 serves a different API and is out of scope, as is semantic
search from inside a colony.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant