Memories
The core unit of knowledge in Cortex.
A memory is a scoped, cited, and auditable piece of knowledge stored in Cortex. Unlike traditional search indexes, memories carry semantic context with full provenance.
Memory Structure
{
"id": "uuid",
"title": "API Rate Limiting Policy",
"content": "All external API endpoints must implement rate limiting...",
"scope": "engineering",
"category": "policy",
"source": "GitHub",
"source_url": "https://github.com/org/repo/pull/123",
"author": {
"id": "uuid",
"name": "John Doe"
},
"tags": ["api", "security"],
"clearance_min": "Contractor",
"status": "active",
"valid_from": "2025-01-15T10:30:00Z",
"valid_until": null
}
Lifecycle
stateDiagram-v2
[*] --> Active: remember()
Active --> Superseded: Newer memory supersedes
Active --> Redacted: redact()
Superseded --> [*]
Redacted --> [*]
| Status | Description |
|---|---|
active | Currently searchable and recalled |
superseded | Replaced by a newer, more authoritative memory |
redacted | Removed from search; row retained for provenance |
Writing Memories
Memories can be written via:
| Method | Use Case |
|---|---|
MCP remember tool | AI agents storing knowledge |
| REST API | CI/CD pipelines, integrations |
| CLI | Manual file uploads |
| Connectors | GitHub PRs, Slack messages |
How content is stored
A memory is always one row with one whole-content copy and one citation, but
its content is chunked for embedding: long content is split into overlapping,
paragraph-aware chunks (~1.2k chars, ~150-char overlap) and each chunk is
embedded as its own vector, all tagged with the same memory_id. Short content
(≲1.4k chars) stays a single chunk, so nothing changes for the common case.
A best-effort graph extraction then runs on the memory.
Chunking means a specific fact buried deep in a long document can still be matched on recall, while the memory list, citations, and audit trail stay one-per-memory. When several chunks of the same memory match a query, recall returns that memory once (highest-scoring chunk wins). Redaction, supersession, and deletion apply to every chunk of a memory.
How connectors sync
| Connector | Mode | First-run backfill | Per-run cap |
|---|---|---|---|
| GitHub | Webhook (real-time) | none — merged PRs going forward only | — |
| Slack | Webhook (real-time) | up to ~50 recent messages per synced channel | 50 |
| Gmail | Scheduled pull (~15 min) | messages from the last 3 days | 8 |
| Google Drive | Scheduled pull (~15 min) | Docs modified in the last 3 days | 4 |
| Notion | Scheduled pull (~15 min) | pages edited in the last 30 days | 8 |
GitHub and Slack push events as they happen. Gmail, Drive and Notion have no
webhook for this content, so a scheduled job (Upstash QStash → /api/cron/ connectors) pulls what changed since each connector's stored cursor. Every
connector card also has a Sync now button for an on-demand run. Per-run caps
are deliberately small (each item costs an embed + a graph-extraction LLM call);
subsequent runs pick up more incrementally. Raise the caps for a one-time
backfill.
Scope Resolution
When writing, the scope is determined by:
- Explicit
scopeparameter (highest priority) - Source-based heuristic (e.g., GitHub →
engineering) - Default scope (
public-docs)
An agent can only write into scopes its acting user can read. This prevents "write-up" permission leaks.
Meeting notes (online or offline)
Memories → Add Memory → Meeting Brief is built for any meeting, not only
recorded calls. Fill in a written summary and/or upload a real transcript file
(.txt, .md, .docx, .pdf) — there is no audio-upload option; text only.
Extract Decisions (AI) runs your summary + transcript through the LLM
(POST /api/memories/extract-decisions) and returns decisions actually
grounded in that text — nothing is templated or guessed. Extracted decisions
are appended to the saved memory's content alongside your summary and
transcript.
Reading Memories
Memories are retrieved via:
| Method | Description |
|---|---|
MCP recall tool | Semantic search with citations |
| REST API | Programmatic access |
MCP list_memories | Recent memories (no search) |
Permission Filtering
Every read operation:
- Resolves the caller's allowed scopes
- Filters results to those scopes only
- Re-checks clearance level (defence-in-depth)
- Drops expired memories
- Returns only
activestatus memories
Ranking
Results are ranked by a blend of semantic relevance (70%) and recency (30%), not recency alone — a strong exact match can still outrank a pile of recently-synced but loosely-related items.
Conflict Detection
When a new memory is written, Cortex checks for near-neighbours with similarity > 0.75. Conflicts are classified as:
| Kind | Description |
|---|---|
contradiction | Conflicting information |
duplicate | Same information, different source |
stale | Outdated information |
Conflicts are resolved by authority (role level) or recency, with dissent retained.