memorysync-mastra
v1.0.1
Published
MemorySync memory for Mastra agents: input/output processors for automatic recall and persistence, a zero-config agent wrapper, five agent memory tools, and helpers. Works on current @mastra/core.
Maintainers
Readme
memorysync-mastra
Long-term memory for Mastra agents, backed by MemorySync — on Mastra's native processor pipeline.
- Processors — recalled context injected as a system message before each
generate/streamcall, the exchange persisted after it and distilled server-side into durable facts. withMemorySync— zero-config wrapper that merges the processors into any agent config.- Five agent tools — add, search, list, update, delete; they never throw.
- Helpers —
getMemoryContext,searchMemories,saveTurnfor hand-wired setups.
npm install memorysync-mastra @mastra/coreSet MEMORYSYNC_API_KEY in the environment (create a key at app.memorysync.io), or pass apiKey explicitly.
Processors — memory on the native pipeline
import { Agent } from "@mastra/core/agent";
import { openai } from "@ai-sdk/openai";
import { createMemorySyncProcessors } from "memorysync-mastra";
const { inputProcessor, outputProcessor } = createMemorySyncProcessors({
userId: "customer-7", // per-end-user scoping
sessionId: "thread-42", // groups the stored facts by thread
});
const agent = new Agent({
id: "assistant",
name: "Assistant",
instructions: "You are a helpful assistant.",
model: openai("gpt-4o-mini"),
inputProcessors: [inputProcessor],
outputProcessors: [outputProcessor],
});
// First conversation
await agent.generate("I'm vegetarian and I fly aisle.");
// Any later call — same user, any thread, any model
const { text } = await agent.generate("Book my trip: flight plus a dinner spot.");
// The model already saw: vegetarian, aisle seat — injected from memory.Or wrap the config:
import { withMemorySync } from "memorysync-mastra";
const agent = new Agent(withMemorySync(
{ id: "assistant", name: "Assistant", instructions, model },
{ userId: "customer-7" },
));Injection happens once per call — the processors use processInput, not the per-step hook, so multi-step tool loops never pay for the context block twice. Recall failing means the call proceeds without context; persistence failing is reported through onError and never thrown. A stream that dies mid-flight persists nothing (the failed run is detected via finishReason: "error" and skipped), so no half-turns are ever stored.
Who is the agent acting for?
Identity resolves per call from four sources, in priority order:
// 1. Your own resolver — wins over everything (multi-user servers).
createMemorySyncProcessors({
resolveIdentity: (requestContext) => ({ userId: session.userId }),
});
// 2. Static ids — one agent per user (scripts, workers).
createMemorySyncProcessors({ userId: "customer-7" });
// 3. Mastra's RequestContext — set by server middleware.
import { RequestContext } from "@mastra/core/request-context";
import { MASTRA_RESOURCE_ID_KEY } from "memorysync-mastra";
const ctx = new RequestContext();
ctx.set(MASTRA_RESOURCE_ID_KEY, "customer-7");
await agent.generate(messages, { requestContext: ctx });
// 4. Mastra's own memory plumbing.
await agent.generate("...", { memory: { resource: "customer-7", thread: "t-1" } });No resolved user means no write. If none of the sources yields a user id, recall is skipped and the persist is refused — reported through onError, never written to a default scope.
Recall modes and switches
createMemorySyncProcessors({ userId, mode: "query" }); // relevant to the latest message (default)
createMemorySyncProcessors({ userId, mode: "profile" }); // overview of the user: newest facts, listed (1.0.1; 1.0.0 searched with a generic prompt and injected nothing)
createMemorySyncProcessors({ userId, mode: "full" }); // both
createMemorySyncProcessors({ userId, persist: false }); // read-only
createMemorySyncProcessors({ userId, recall: false }); // write-only
createMemorySyncProcessors({ userId, k: 12, template: "What you know:\n{context}" });Agent tools
import { createMemorySyncTools } from "memorysync-mastra";
const agent = new Agent({
// ...
tools: { ...createMemorySyncTools({ userId: "customer-7" }) },
});
// Untrusted agents: search + list only.
createMemorySyncTools({ userId: "customer-7", readOnly: true });add_memory, search_memory, list_memories, update_memory, delete_memory — the same five operations, same response strings as the MemorySync LangChain, AI SDK and CrewAI tool sets. A memory failure can never abort the agent run: tools return short readable error strings instead of throwing, and add_memory derives a client ref from the content so a repeating agent gets "already stored", never a duplicate.
Helpers
import { getMemoryContext, saveTurn, searchMemories } from "memorysync-mastra";
// Prompt-ready context block ("" for a new user)
const context = await getMemoryContext("what should I cook?", { userId: "customer-7" });
// Scored raw results
const hits = await searchMemories("dietary preferences", { userId: "customer-7" });
// Explicit persistence — THROWS on failure (an explicit call is owed the
// truth), unlike the processors' reported-never-thrown discipline.
await saveTurn(
{ user: "I'm vegetarian", assistant: "Noted!", sessionId: "thread-42" },
{ userId: "customer-7" },
);All surfaces share the same idempotency seeds, so mixing styles cannot double-store a turn.
Version support
| Package | Requires | Runtime |
| --- | --- | --- |
| memorysync-mastra 1.0.1 | @mastra/core >=1.42 <2 (peer) | Node 20+ (@mastra/core itself requires Node 22+) |
The CI suite drives a real @mastra/core Agent through the processors — hook timing, RequestContext keys, message shapes — on the pinned core and again on the latest 1.x release.
