Brain & Memory

Persistent agent memory and per-project knowledge base

Overview

The SDK provides three related resources for AI agent memory and project intelligence:

  • r.memory — Persistent facts and decisions that survive across sessions. Network-scoped.
  • r.brain — Send messages to the Brain AI (response streams via WebSocket).
  • r.projectBrain — Per-project dashboard: tasks, milestones, team activity, usage stats.

Memory (r.memory)

Facts

Store and retrieve facts — things worth remembering across sessions such as patterns, conventions, preferences, and gotchas.

Store a fact

1const { data: fact } = await r.memory.facts.add({
2 fact: 'This project uses Hono, not Express',
3 project_id: 'proj_123',
4 source: 'manual',
5 tags: ['architecture', 'routing'],
6});
7
8console.log(fact.id);
9console.log(fact.fact);
10console.log(fact.tags);

Input fields:

FieldTypeRequiredDescription
factstringYesThe fact to remember.
project_idstring?NoScope to a project.
organization_idstring?NoScope to an organization.
agent_idstring?NoAssociate with a specific agent.
user_idstring?NoAssociate with a specific user.
source'auto' | 'manual' | 'extraction'NoHow the fact was discovered. Default: 'manual'.
tagsstring[]?NoTags for categorization.

Memory rejects facts that appear to contain secrets (API keys, passwords, tokens). Store secrets in environment variables instead.

List facts

1const { data: facts } = await r.memory.facts.list({
2 project_id: 'proj_123',
3});
4
5for (const fact of facts) {
6 console.log(`[${fact.source}] ${fact.fact}`);
7 console.log(` Tags: ${fact.tags.join(', ')}`);
8}

Filter parameters:

ParameterTypeDescription
project_idstring?Filter by project.
organization_idstring?Filter by organization.
agent_idstring?Filter by agent.
user_idstring?Filter by user.
querystring?Full-text search within facts.

Remove a fact

1await r.memory.facts.remove('fact_123');

Decisions

Log architectural, strategic, or technical decisions with context. Decisions are searchable and shared across the organization.

Log a decision

1const { data: decision } = await r.memory.decisions.log({
2 decision: 'Use pgvector for semantic search',
3 context: 'Evaluated Pinecone, Weaviate, and pgvector. Chose pgvector for simplicity -- it runs in our existing Postgres instance with no additional infrastructure.',
4 tags: ['architecture', 'database', 'search'],
5 project_id: 'proj_123',
6});
7
8console.log(decision.id);
9console.log(decision.decision);

Input fields:

FieldTypeRequiredDescription
decisionstringYesThe decision that was made.
contextstring?NoWhy the decision was made, alternatives considered.
tagsstring[]?NoTags for categorization.
project_idstring?NoScope to a project.
organization_idstring?NoScope to an organization.
agent_idstring?NoAssociate with an agent.

List decisions

1const { data: decisions } = await r.memory.decisions.list({
2 project_id: 'proj_123',
3 topic: 'database',
4 limit: 10,
5});
6
7for (const d of decisions) {
8 console.log(`Decision: ${d.decision}`);
9 console.log(`Context: ${d.context}`);
10 console.log(`Tags: ${d.tags.join(', ')}`);
11}

Search memory

Search across all memory types (facts, decisions, and conversations) with relevance ranking.

1const { data: results } = await r.memory.search({
2 query: 'authentication approach',
3 project_id: 'proj_123',
4 limit: 10,
5});
6
7for (const result of results) {
8 console.log(`[${result.type}] Score: ${result.score}`);
9 console.log(` ${result.content}`);
10}

Input fields:

FieldTypeRequiredDescription
querystringYesSearch query.
project_idstring?NoScope to a project.
organization_idstring?NoScope to an organization.
agent_idstring?NoFilter by agent.
limitnumber?NoMax results (default 10).

Returns: SingleResponse<MemorySearchResult[]>

1interface MemorySearchResult {
2 type: 'fact' | 'decision' | 'conversation';
3 id: string;
4 content: string;
5 score: number;
6 metadata: Record<string, unknown>;
7}

Build context injection

Build a formatted context block from relevant memories for a given message. Use this to enrich system prompts with past knowledge.

1const { data: { context } } = await r.memory.context({
2 message: 'How should I implement the payment flow?',
3 project_id: 'proj_123',
4 max_tokens: 2000,
5});
6
7// Use the context in a system prompt
8const systemPrompt = `You are a helpful assistant.
9
10## Relevant context from memory:
11${context}
12
13Now answer the user's question.`;

Input fields:

FieldTypeRequiredDescription
messagestringYesThe current message/task to find context for.
project_idstring?NoScope to a project.
organization_idstring?NoScope to an organization.
agent_idstring?NoFilter by agent.
max_tokensnumber?NoToken budget for the context string (default 2000).

Purge memory

Delete all memory (facts and decisions) for a project. This is irreversible.

1const { data: result } = await r.memory.purge('proj_123');
2
3console.log(`Deleted ${result.factsDeleted} facts and ${result.decisionsDeleted} decisions`);

Purging memory is irreversible. All facts and decisions for the project will be permanently deleted.

Brain (r.brain)

Send messages to the Brain AI. The response streams back via WebSocket.

1const { data: { message_id } } = await r.brain.sendMessage({
2 message: 'What should I work on next?',
3 history: [
4 { role: 'user', content: 'I just finished the auth module.' },
5 { role: 'assistant', content: 'Great! The next priority is the billing integration.' },
6 ],
7 context: {
8 current_route: '/dashboard',
9 org_slug: 'my-org',
10 },
11});
12
13console.log('Message ID:', message_id);
14// Listen on WebSocket for the streamed response

Project Brain (r.projectBrain)

Per-project intelligence dashboard with tasks, milestones, team activity, and usage stats.

List tasks

1const { data: tasks } = await r.projectBrain.tasks('proj_123', {
2 status: 'pending',
3 milestone: 'v1-launch',
4});
5
6for (const task of tasks) {
7 console.log(`${task.title} [${task.status}] — effort: ${task.effort}`);
8}

Get project stats

1const { data: stats } = await r.projectBrain.stats('proj_123');
2console.log(stats);

List milestones

1const { data: milestones } = await r.projectBrain.milestones('proj_123');
2
3for (const m of milestones) {
4 console.log(`${m.name}: ${m.task_progress.completed}/${m.task_progress.total} tasks`);
5 console.log(` ${m.task_progress.percentage}% complete`);
6}

List project decisions

1const { data: decisions } = await r.projectBrain.decisions('proj_123', {
2 limit: 5,
3});

List project agents

1const { data: agents } = await r.projectBrain.agents('proj_123');
2
3for (const agent of agents) {
4 console.log(`${agent.name} (@${agent.username}) — ${agent.model}`);
5}

Team activity

1const { data: activity } = await r.projectBrain.teamActivity('proj_123');
2
3for (const member of activity.team) {
4 console.log(`${member.name}: ${member.completed_count} completed, ${member.active_claim_count} active`);
5 if (member.current_work) {
6 for (const work of member.current_work) {
7 console.log(` Working on: ${work.task_title}`);
8 }
9 }
10}
11
12console.log('\nActive claims:');
13for (const claim of activity.active_claims) {
14 console.log(` ${claim.task_title} — claimed by ${claim.agent_id}`);
15}

Complete a task

1await r.projectBrain.completeTask('proj_123', 'task_456', 'Implemented and tested');

Get usage stats

1const { data: usage } = await r.projectBrain.usage('proj_123');
2
3console.log(`Total requests: ${usage.total_requests}`);
4console.log(`Input tokens: ${usage.input_tokens}`);
5console.log(`Output tokens: ${usage.output_tokens}`);
6console.log(`Cost: $${usage.cost_dollars.toFixed(4)}`);

Golden path: agent with persistent memory

Here is a complete example of an agent that uses memory to maintain context across sessions:

1import { Recursiv } from '@recursiv/sdk';
2
3const r = new Recursiv();
4
5const PROJECT_ID = 'proj_123';
6const AGENT_ID = 'agent_456';
7
8// 1. Load relevant context from memory
9const { data: { context } } = await r.memory.context({
10 message: 'Review the latest code changes',
11 project_id: PROJECT_ID,
12 agent_id: AGENT_ID,
13 max_tokens: 1500,
14});
15
16// 2. Chat with the agent, injecting memory context
17const { data: reply } = await r.agents.chat(AGENT_ID, {
18 message: `Context from memory:\n${context}\n\nPlease review the latest code changes and suggest improvements.`,
19});
20
21console.log('Agent response:', reply.content);
22
23// 3. Store what the agent learned
24await r.memory.facts.add({
25 fact: 'Code review completed on 2025-01-15. Found 3 issues in auth module.',
26 project_id: PROJECT_ID,
27 agent_id: AGENT_ID,
28 source: 'auto',
29 tags: ['code-review', 'auth'],
30});
31
32// 4. Log the decision
33await r.memory.decisions.log({
34 decision: 'Refactor auth middleware to use async validation',
35 context: 'Current sync validation blocks the event loop under load. Agent review identified this as the primary performance bottleneck.',
36 project_id: PROJECT_ID,
37 agent_id: AGENT_ID,
38 tags: ['auth', 'performance'],
39});