Memory
Give an agent memory that persists across sessions. Useful for preferences, durable facts, learned heuristics — anything you'd want the agent to remember next time.
Memory is opt-in per agent and scoped to that agent. Each deployed agent has its own memory silo.
Enabling memory
Add a memory block to your swarmlord.jsonc:
jsonc
{
"name": "my-agent",
"memory": { "enabled": true },
}That's the whole minimum. All defaults are reasonable.
Full shape:
jsonc
{
"memory": {
"enabled": true,
"maxEntries": 200, // cap on live entries (oldest low-confidence pruned)
"maxCharsInjected": 2000, // budget for memory injected into the system prompt
"confidenceThreshold": 0.6, // auto-extracted writes below this are skipped
"minMessagesToExtract": 4, // skip extraction on trivial sessions
"dryRun": false, // emit events without writing — safe-rollout mode
},
}How the agent uses it
When memory is enabled, the agent gets three new tools:
| Tool | What it does |
|---|---|
remember | Store a durable memory (category + content + optional confidence) |
recall | Search memories for the current task |
forget | Delete a memory that's wrong or outdated |
The agent also gets a relevant slice of memory automatically injected at the start of each session — you don't have to prompt for it.
After every successful session, a lightweight model reviews the transcript and proposes memory writes — new facts, updates to existing memories, or deletions. Low-confidence proposals are filtered automatically.
Categories
Memories are tagged with a category so retrieval and inspection stay organized:
- fact —
User's stack is Python 3.12 - preference —
User prefers concise replies - instruction —
Always PR against develop - relationship —
Alice is on the platform team, reports to Bob - skill_observation —
This approach to X has worked well across three runs
You can use any string; the built-in five are suggestions.
SDK
Memory attaches to client.agent(...):
ts
import { createClient } from "swarmlord";
const client = createClient();
const agent = client.agent({ name: "my-agent" });
await agent.memory.add({ category: "preference", content: "User prefers Python 3.12" });
const hits = await agent.memory.search("python", { limit: 5 });
const all = await agent.memory.list({ category: "preference", limit: 50 });
await agent.memory.forget(hits[0].id);See the TypeScript SDK reference for the full API.
CLI
sh
swarmlord memory list --agent my-agent
swarmlord memory add --agent my-agent --category preference --content "User prefers concise replies"
swarmlord memory search --agent my-agent --query "python"
swarmlord memory forget <id>Learned memory never overrides instructions
A recurring risk with agent memory is that a learned fact contradicts the agent's original instructions or SOUL.md — and starts winning. We prevent this two ways:
- Every recalled memory is wrapped in a
<memory-context>block with an explicit note that it's background information, not a new instruction. - Retrieved memory is placed before SOUL.md and your
instructionsin the system prompt, so your instructions always appear later and win on any conflict.
Writes are also scanned for prompt-injection patterns (role markers, override phrases, jailbreak triggers) and blocked if matched — these trigger a memory.blocked event you can monitor.
Observability
Four events stream over SSE alongside normal session events. You can subscribe to them from session.send() via dedicated callbacks — no need to drop down to session.stream():
ts
await session.send(prompt, {
onText: d => process.stdout.write(d),
onMemoryRecalled: e => console.log(`recalled ${e.entryCount} entries (${e.charsInjected} chars)`),
onMemoryExtracted: e => console.log(`extraction: ${e.opsApplied}/${e.opsProposed} applied`),
onMemoryWrote: e => console.log(`wrote [${e.category}] id=${e.id}`),
onMemoryBlocked: e => console.warn(`blocked by sanitizer: ${e.reasons.join(", ")}`),
});| Callback | When it fires |
|---|---|
onMemoryRecalled | Auto-retrieval at the start of a session loop |
onMemoryExtracted | Once per session, after end-of-session extraction completes |
onMemoryWrote | Per successful write — tool, SDK, or extractor |
onMemoryBlocked | When the sanitizer rejects content |
If you need every raw event, onEvent: e => ... on the same send() call gives you the firehose.
Rollout tip
Set dryRun: true when you first enable memory. The extractor runs, events fire, but no writes land. Watch the memory-extracted events to see what the model would have stored, tune confidenceThreshold to your comfort level, then flip dryRun off.
When memory is unavailable
Memory requires a deployed agent. Raw sessions without a deployed-agent identity skip memory injection entirely, and the remember / recall / forget tools short-circuit with a clear message.