Configuration and sharing
Memory works with no configuration at all. Everything here is opt-in.
Two models, not one
Settings → Memory configures both, and they do different jobs:
| Setting | What it does |
|---|---|
| Memory model | Housekeeping — metadata enrichment, query expansion, merge blending. Point it at something cheap; it falls back to your small model, then the default. |
| Embedding model | Turns fragment text into the vectors retrieval searches. Changing it is the bigger decision — see below. |
Keep the memory model off a rate-limited primary endpoint. It runs on background work you aren't waiting for, and it will happily eat a quota you wanted for the agent.
Embedders
Pick one in Settings → Memory → Embedding model, which writes the memory.embedding block for you. The same block by hand:
Built-in (default). A local ONNX model runs in a worker thread, cached once under <configDir>/models and shared across every workspace. No API key, no local server, no network. This is what you get when you configure nothing.
OpenAI. Higher-quality vectors, at the cost of sending fragment text to OpenAI.
{
"memory": {
"embedding": {
"provider": "openai",
"apiKey": "sk-...",
"model": "text-embedding-3-small"
}
}
}Ollama. Local, and swappable for any embedding model you've pulled.
{
"memory": {
"embedding": {
"provider": "ollama",
"baseURL": "http://localhost:11434",
"model": "nomic-embed-text",
"dimensions": 768
}
}
}Changing embedder
Vectors from two different embedders aren't comparable, even at the same dimension — so the corpus records which embedder built it. When that identity stops matching, the mismatched fragments are re-embedded in bounded background batches, and the vector index is rebuilt if the dimension changed.
Nothing is lost and nothing blocks, but recall is uneven while the corpus catches up, and a large store takes a while. Re-seeding the workspace forces the issue rather than waiting for the background pass.
Where it lives
| What | Path |
|---|---|
| Database | .podcode/data/memory.db, per workspace |
| Shareable fragments | .podcode/memory/, git-trackable JSON |
| Embedder model cache | <configDir>/models, shared across workspaces |
Sharing with your team
Two tiers exist today.
Solo
Fragments live in the workspace's SQLite database. Nothing leaves the machine.
Git-synced
Fragments are serialised to .podcode/memory/<YYYY-MM>/<id>.json — one file per fragment, bucketed by the month it was created. Commit the directory and your team inherits the workspace's knowledge on clone.
This runs automatically, in both directions, by default. Export fires about 30 seconds after extraction stores anything (debounced per workspace) and on the 30-minute maintenance tick. Import fires when a workspace opens and files exist beside it — a fresh clone, or a teammate's work that landed while the app was closed — when the files change on disk, and on the same tick.
What it does not do is touch git. No git add, no commit, no push: those stay your explicit actions. Sync's job ends at the files.
One file per fragment is what makes that safe. Two people adding fragments produce two files with different ids, so new fragments never conflict.
Drive it by hand from the Memory Browser's Sync control, Settings → Memory → Team sync, or the terminal:
podcode memory sync # import theirs, then export ours
podcode memory status # tracking state, settings in effect, last push/pullSettings
{
"memory": {
"sync": {
"autoExport": true,
"autoImport": true,
"excludeTypes": ["preference", "context"],
"includeQuarantined": false,
"secretGuard": true
}
}
}The same block in a workspace's .podcode/config.json overrides the global one, so some workspaces sync while others stay local. Settings → Memory → Team sync lists every workspace with Use default / Sync / Local only; podcode memory config --sync | --local | --default does the same thing. Turning a workspace local-only stops automatic export and import — files already written stay on disk until you remove them.
What crosses
Everything, including retired fragments, so retirement and supersession propagate rather than resurrecting on a teammate's machine.
Held back: embeddings (a peer may use a different embedder, and re-embeds on import), your local recency signal, quarantined fragments unless you opt in, anything in excludeTypes, and any fragment whose text matches a credential pattern. Withheld fragments are counted and named in the result, never dropped silently.
When the same fragment changes twice
New fragments can't collide, but one fragment can change on two machines between syncs. The directory ships a .gitattributes marking these files merge=union, so git keeps both versions instead of writing conflict markers, and the import resolves them:
- If one supersedes the other, the superseding fragment wins.
- Otherwise, the later
timeUpdatedwins.
An import also repairs a file that did end up with conflict markers, reports how many it fixed, and lists anything it couldn't parse rather than skipping it quietly.
Turning it down
Memory is non-blocking and degrades quietly: if embedding fails, the session continues without it and logs a warning. If you want less of it in your context, the levers are the embedder and the fragments themselves — prune aggressively in the browser, since a smaller, curated store retrieves better than a large noisy one.
Next
- Providers — where the models come from.