# Memori Comparison & Greenfield Analysis

## Overview

This document records our analysis of [Memori](https://github.com/MemoriLabs/Memori) (Memori Labs, open-source with commercial cloud offering) against Coda's `@coda/memory` package and the server-side `MemoryService` integration. Memori is agent-native memory infrastructure — a SQL-native layer that automatically captures structured memory from agent execution traces (tool calls, decisions, outcomes) and conversation, then retrieves relevant memories via hybrid search for injection into the agent's context.

We evaluated Memori to identify transferable techniques and assess our memory architecture against its more production-focused design, as we did with [MemOS](memos-comparison.md) (memory OS), [Stash](stash-comparison.md) (agent memory), and the other [search comparisons](../search/).

---

## How Memori works

Memori's core thesis is that agent memory should be derived from **execution traces** (tool calls, decisions, outcomes), not just conversation text. It captures structured facts automatically from every LLM interaction and makes them available through hybrid retrieval.

### Multi-agent processing pipeline

Three specialized agents work in sequence:

| Agent               | Role                                                                                 |
| ------------------- | ------------------------------------------------------------------------------------ |
| **Capture Agent**   | Intercepts every LLM API call; records conversation turns, tool execution, decisions |
| **Analysis Agent**  | Extracts structured insights as semantic triples (subject–predicate–object)          |
| **Retrieval Agent** | Selects the most relevant memories for injection into the next LLM call              |

The capture agent runs synchronously (no latency added to request path), while fact extraction runs asynchronously in the background.

### Memory representation — semantic triples

Memori's core data model uses RDF-style semantic triples:

```
(subject) —[predicate]→ (object)

Examples:
(user) —[prefers]→ (Snowflake over GraphQL)
(query_snowflake) —[failed_with]→ (timeout on 5+ JOINs)
(user) —[frequently_accesses]→ (STATEMENT_PERIOD table)
```

Each triple is linked back to the exact conversation turn that produced it, preserving provenance. Triples are classified into four categories:

| Category        | Description                                |
| --------------- | ------------------------------------------ |
| **Facts**       | Specific information and data points       |
| **Preferences** | User/agent settings and inclinations       |
| **Rules**       | Constraints and operating procedures       |
| **Summaries**   | High-level conversation-level abstractions |

This representation compresses unstructured dialogue into compact semantic units, improving vector search accuracy and reducing token overhead.

### Dual memory modes

Memori offers two retrieval strategies, usable independently or combined:

**Conscious Mode** — one-shot working memory injection at conversation start. Promotes 5-10 essential memories from long-term storage into short-term context. Runs once per session. Analogous to our `formatMemorySection()` prompt injection.

**AutoIngest Mode** — dynamic per-query memory search. Retrieves 3-5 most relevant memories per LLM call. Runs on every request. Enables the agent to access different memories for different questions within a single session.

**Combined Mode** — merges fixed essential context (conscious) with query-specific context (auto). Essential memories provide baseline user knowledge; dynamic memories provide query-relevant recall.

### Retrieval strategy

Memori uses hybrid search combining three signals:

1. **Vector search** — semantic similarity via embeddings
2. **BM25 keyword search** — full-text matching
3. **Entity matching** — structured queries by agent, user, domain, or custom dimension

This is closer to our search service's hybrid approach than to MemOS's simpler retrieval, though it operates on memory facts rather than schema metadata.

### SQL-native storage

Unlike most memory systems that use vector databases or key-value stores, Memori uses a relational foundation:

- PostgreSQL, MySQL, SQLite, MongoDB, CockroachDB, Oracle
- Full-text search with versioning built into the schema
- Complex analytics and reporting via standard SQL queries
- Migration path from SQLite (dev) to PostgreSQL (production)

This enables compliance queries ("show all memories containing PII for user X"), analytics ("which tools fail most across all users"), and structured auditing that vector-only or key-value stores cannot express.

### Integration pattern — zero-code SDK wrapper

Memori's SDK patches existing LLM clients transparently:

```python
# Three lines to add memory to any agent
from memori import Memori
m = Memori(api_key="...")
m.register(openai_client)  # patches all API calls
```

This intercepts every LLM API call without changes to agent code, prompts, or tool definitions. Supported providers: OpenAI, Anthropic, AWS Bedrock, Gemini, DeepSeek, Grok, custom endpoints. Also available as an MCP server for Claude, Cursor, Codex.

### Session-based organization

Memori groups LLM interactions into sessions — multi-step agent executions that span multiple turns. Sessions preserve temporal context and enable queries like "what happened in the user's last debugging session?" or "what tools were used during onboarding?"

---

## How our system works

Coda's `@coda/memory` package (`packages/memory/`) provides cross-session learning through a two-stage pipeline: raw observations are consolidated into durable facts with confidence decay, then facts are injected into the system prompt at session start.

### Architecture

```
Tool Execution
  │
  ▼
emitMemoryForToolExecution()    ← fire-and-forget, best-effort
  ├─ emitToolObservations()      [tool_success / tool_failure]
  ├─ emitEntityObservations()    [account, product, schema table access]
  └─ emitQueryPatternObservations()  [domain keyword matching]
  │
  ▼
ObservationLog.append()          ← sorted set, keyed by timestamp
  │
  ▼
[Next session start]
  │
  ▼
MemoryService.loadMemory(identityId)
  │
  ▼
consolidate()
  1. Read checkpoint (last-consolidated timestamp)
  2. Fetch observations since checkpoint
  3. Deduplicate to fact keys (one reinforcement per fact per session)
  4. Reinforce existing facts or create new (confidence 0.5)
  5. Decay unreinforced facts; delete if below 0.1 threshold
  6. Persist updated facts + new checkpoint
  7. Prune old observations (7-day buffer)
  │
  ▼
formatMemorySection()            ← ~300 token budget
  │
  ▼
System prompt injection          ← memory first, then domain instructions
  │
  ▼
Tool ranking via toolAffinities  ← confidence-weighted boost in searchCatalog()
```

### Data model

Two entity types:

| Entity          | Storage                         | Purpose                                       | TTL                                    |
| --------------- | ------------------------------- | --------------------------------------------- | -------------------------------------- |
| **Observation** | Redis sorted set (by timestamp) | Append-only raw events from tool execution    | 7-day buffer after consolidation prune |
| **Fact**        | Redis hash map (by identity)    | Consolidated beliefs with confidence 0.0--1.0 | Exponential decay; expires below 0.1   |

Four observation types: `tool_success`, `tool_failure`, `entity_access`, `query_pattern`.

Five fact categories: `tool_affinity`, `tool_avoidance`, `entity_frequency`, `domain_preference`, `failure_pattern`.

### Decay strategies

Two pluggable `DecayStrategy` implementations behind a common interface:

| Strategy             | Formula                                   | Half-life         | When to use                          |
| -------------------- | ----------------------------------------- | ----------------- | ------------------------------------ |
| **ExponentialDecay** | `confidence * 0.95 ^ days`                | ~13.5 days        | Default; simple time-based decay     |
| **AdaptiveDecay**    | `confidence * 0.95 ^ (days / easeFactor)` | ~17.5--33.75 days | SM-2 inspired; per-fact ease factors |

### System prompt injection

`formatMemorySection()` ranks facts by `confidence * reinforcementCount`, groups by display category (Tool preferences, Frequent entities, Domain focus, Known issues), and assembles markdown under a "What you know about this user" header. Budget: ~300 tokens. Returns empty string if no facts pass the filter.

### Tool affinity integration

`extractToolAffinities()` produces a `Map<string, number>` from `tool_affinity` (positive) and `tool_avoidance` (negative) facts. Consumed by `searchCatalog()` as a tiebreaker boost (max +/-2 points).

---

## Side-by-side comparison

| Aspect                      | Memori                                                                         | Coda `@coda/memory`                                                                 |
| --------------------------- | ------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------- |
| **Problem**                 | Persistent structured memory for any LLM agent                                 | Cross-session learning for a domain-specific AI agent                               |
| **Architecture**            | Multi-agent pipeline (capture → analysis → retrieval)                          | Two-stage pipeline (observations → facts → prompt injection)                        |
| **Memory representation**   | Semantic triples (subject–predicate–object) with provenance                    | Typed observations + aggregated facts with confidence                               |
| **Memory categories**       | Facts, Preferences, Rules, Summaries                                           | tool_affinity, tool_avoidance, entity_frequency, domain_preference, failure_pattern |
| **Consolidation**           | LLM-dependent: semantic triple extraction via Analysis Agent                   | Deterministic: counting, averaging, decaying. Zero LLM calls                        |
| **Retrieval**               | Hybrid: vector + BM25 + entity matching. Per-query dynamic search              | None — facts loaded in bulk at session start, not searched                          |
| **Memory modes**            | Conscious (session-start), AutoIngest (per-query), Combined                    | Session-start injection only (equivalent to Conscious mode)                         |
| **Execution trace capture** | Automatic via SDK middleware — intercepts all LLM calls, tool calls, decisions | Explicit callback (`onToolExecution`) emitting typed observations                   |
| **Storage**                 | SQL-native: PostgreSQL, MySQL, SQLite, MongoDB, CockroachDB                    | Redis (sorted sets + hash maps), in-memory fallback                                 |
| **Confidence/decay**        | Not described — relies on relevance scoring at retrieval time                  | Exponential or SM-2 adaptive decay with pluggable `DecayStrategy`                   |
| **Multi-tenant isolation**  | Entity-based scoping (agent, user, domain, custom dimensions)                  | `identityId`-keyed storage; SHA256-hashed identity in Redis keys                    |
| **User visibility**         | SQL-queryable memory store; audit trails                                       | None — users cannot view, edit, or delete their memory                              |
| **LLM dependency**          | Required for fact extraction (Analysis Agent) and retrieval ranking            | Zero — all processing is deterministic aggregation                                  |
| **Integration effort**      | 3 lines (register SDK wrapper on existing LLM client)                          | ~300 lines server integration (explicit emitters, prompt formatter, affinity)       |
| **Provider support**        | OpenAI, Anthropic, Bedrock, Gemini, DeepSeek, Grok, custom                     | AWS Bedrock (Claude) only                                                           |
| **Session grouping**        | Native session model grouping multi-turn interactions                          | Per-conversation Redis keys (7-day TTL)                                             |
| **Prompt budget**           | Variable (5-10 conscious + 3-5 auto per query)                                 | Fixed ~300 token budget with ranked truncation                                      |
| **Latency**                 | Async extraction; retrieval adds query latency per mode                        | <100ms consolidation (500ms timeout with empty fallback)                            |
| **Indexing cost**           | LLM calls per interaction (extraction + embedding)                             | Zero (pure aggregation)                                                             |
| **Deployment**              | Cloud hosted, BYODB, VPC, or on-premises                                       | In-process TypeScript (no additional services)                                      |
| **Code footprint**          | Full framework (~thousands of LoC Python/TypeScript)                           | ~600 LoC TypeScript (package) + ~300 LoC server integration                         |
| **Maturity**                | Production service with cloud offering; TypeScript SDK launched 2026           | Production-deployed; ~900 LoC with comprehensive test suite                         |
| **Benchmark (LoCoMo)**      | 81.95% accuracy, 1,294 avg tokens/query                                        | No external benchmark                                                               |

---

## Where Memori excels

### 1. Execution trace capture beyond conversation

Memori captures structured memory from the full execution trace — not just what the user said and what the agent replied, but what tools were called, what decisions were made, what outcomes resulted. Our system only emits observations from the `onToolExecution` callback, capturing tool name and success/failure status. Memori captures the full decision chain: _why_ the agent chose a particular tool, what input it constructed, and how the result influenced the next step.

This matters because agent effectiveness depends on execution patterns, not just tool preference statistics. A user whose queries consistently fail because the agent constructs 5-way JOINs would benefit from the agent remembering the failed query structures, not just that `query_snowflake` sometimes fails.

### 2. Per-query dynamic retrieval (AutoIngest mode)

Our system loads all facts once at session start and injects them into the system prompt. Memori's AutoIngest mode retrieves 3-5 relevant memories per LLM call, adapting context to each question within a session. A user who asks about royalties and then pivots to product metadata gets different memories surfaced for each question.

This is a meaningful architectural difference. Our session-start injection is simpler and cheaper (one load, no per-query overhead) but cannot adapt within a session. For users with diverse query patterns, session-start injection front-loads context that may not be relevant to the current question, wasting token budget.

### 3. Semantic triple representation

Memori's subject–predicate–object triples provide richer structure than our flat fact model. A triple like `(user, prefers, Snowflake over GraphQL)` captures the relationship explicitly, enabling structured queries ("what does the user prefer?") and inference ("if they prefer Snowflake, prioritize Snowflake tools"). Our facts store display text (`"Prefers query_snowflake"`) without machine-readable relationship structure.

The semantic triple also compresses better — a triple conveys the same information as a sentence but in a queryable, composable format. Multiple triples about the same subject can be aggregated into a rich user profile without LLM interpretation.

### 4. SQL-native storage and auditability

Memori's relational foundation enables capabilities that Redis key-value storage cannot:

- **Compliance queries**: "Show all memories containing PII for user X" — a single SQL query.
- **Cross-tenant analytics**: "Which tools fail most across all users?" — a GROUP BY query.
- **Audit trails**: Full provenance from memory back to the specific conversation turn.
- **Data lifecycle management**: Standard SQL-based retention policies, archival, and deletion.

Our Redis storage provides fast key-based lookup but cannot express these queries without scanning all keys. For compliance (GDPR) and operational analytics, SQL is strictly more capable.

### 5. Zero-code integration via SDK middleware

Memori patches existing LLM clients transparently — three lines of code, no changes to agent logic, prompts, or tool definitions. Our integration requires ~300 lines of explicit code: observation emitters wired into the `onToolExecution` callback, prompt formatter, affinity extractor, and server initialization. Memori's approach is more portable and lower-friction for adoption.

### 6. Hybrid retrieval with multiple signals

Memori's retrieval combines vector search, BM25, and entity matching — similar to our search service's multi-signal RRF fusion. Our memory system does not search at all; it loads all facts and relies on prompt ranking. For small fact sets (~50 per user), bulk loading is fine. But if fact volume grows (which it will with richer observation types), retrieval becomes necessary, and Memori's hybrid approach is better than vector-only.

---

## Where our system excels

### 1. Zero LLM dependency

Our consolidation is pure aggregation: counting, averaging, decaying. No LLM calls, no embedding, no external API dependencies at any point in the memory pipeline. Memori requires LLM calls for every interaction — the Analysis Agent extracts semantic triples via LLM, and the Retrieval Agent uses LLM-based ranking. Every memory operation costs money and introduces non-determinism. For a best-effort enhancement layer, deterministic zero-cost consolidation is the right trade-off.

### 2. Confidence decay as a first-class concept

Our `DecayStrategy` interface with `ExponentialDecay` and `AdaptiveDecay` implementations provides principled temporal relevance management. Facts that aren't reinforced naturally age out. Memori's retrieval relies on similarity scoring at query time but does not describe a decay mechanism — old memories that are no longer relevant remain in the store unless explicitly deleted.

This matters for a domain where user behavior genuinely changes over time. A user who worked with royalty data six months ago but now focuses on contract analysis should see their tool affinities shift automatically. Our decay model handles this without manual cleanup; Memori would require explicit pruning or LLM-based relevance filtering.

### 3. Graceful degradation

`MemoryService.loadMemory()` races consolidation against a 500ms timeout and returns empty results on any failure. Memory is strictly additive — if it's slow or broken, the agent falls back to stateless behavior with no degradation in core functionality. Memori's deeper integration (SDK middleware intercepting every LLM call) means failures in the memory layer have broader blast radius. A Memori retrieval failure during AutoIngest mode could add latency to every LLM call in the session.

### 4. Simplicity and low operational overhead

The entire memory system is ~900 lines of TypeScript across the `@coda/memory` package and server integration. No additional services, no database provisioning, no embedding infrastructure. Memori requires a separate database (PostgreSQL/MySQL), LLM calls for extraction, embedding infrastructure for vector search, and the multi-agent pipeline. Each dependency adds operational surface area, failure modes, and cost.

### 5. Domain-specific fact categories and tool affinity

Our fact categories (`tool_affinity`, `tool_avoidance`, `entity_frequency`, `domain_preference`, `failure_pattern`) are purpose-built for an AI agent that uses tools to query structured data. The `extractToolAffinities()` function directly feeds `searchCatalog()` with a per-user boost signal. Memori's generic memory categories (Facts, Preferences, Rules, Summaries) require the consumer to interpret and map memories to domain-specific actions.

### 6. Budget-constrained prompt injection

`formatMemorySection()` ranks facts by signal strength (`confidence * reinforcementCount`), groups by category, and truncates to a 300-token budget. This prevents memory from consuming unbounded context window space. Memori's retrieval returns variable counts (5-10 conscious + 3-5 per-query) without a token budget mechanism — the consumer bears the cost of managing context window utilization.

---

## Transferable techniques evaluated

### Future applicability: Per-query dynamic memory retrieval

**Memori pattern**: AutoIngest mode retrieves 3-5 relevant memories per LLM call based on the current query, adapting context within a session.

**Assessment**: Our session-start injection is a single bulk load — the agent receives the same memory context regardless of what the user asks within a session. For users who ask about diverse topics (royalties in one turn, products in the next), dynamic retrieval would surface more relevant facts per question.

**Implementation direction**: We would not adopt Memori's full AutoIngest architecture (which requires vector search infrastructure and per-query LLM overhead). Instead, we could implement lightweight fact filtering based on the current query's domain keywords — if the user asks about "royalties," boost `domain_preference:royalty` and `entity_frequency:*royalty*` facts in the prompt. This is a simple keyword-match filter on the already-loaded fact set, requiring no new infrastructure.

**Verdict**: Deferred. Our current ~50 facts per user fit comfortably in 300 tokens. Per-query filtering adds complexity without clear benefit at this scale. Revisit when fact volume grows or when users demonstrate diverse within-session query patterns that the static prompt fails to serve.

### Future applicability: Semantic triple representation

**Memori pattern**: Subject–predicate–object triples with provenance links back to source conversations.

**Assessment**: Our facts store `content` as display text (`"Prefers query_snowflake"`) and `subject` as a composite key (`"tool_affinity:query_snowflake"`). The information is implicitly structured but not machine-queryable as a triple. Converting to semantic triples would enable richer queries ("what does the user prefer?", "what entities relate to contracts?") and programmatic fact composition.

**Implementation direction**: Our `Fact` type already has `category` (predicate type) and `subject` (composite key encoding entity). Adding explicit `predicateVerb` and `object` fields would formalize the structure without changing the consolidation pipeline. The `observationToFactKey()` mapping already decomposes observations into these components — the triple structure is latent in the code.

**Verdict**: Deferred. The current flat model works for our four fact categories. Semantic triples become valuable when we add procedural memory or relationship facts that require compositional queries. Not worth the migration cost for tool affinity counting.

### Future applicability: Execution trace capture

**Memori pattern**: Automatic capture of tool call inputs, decision logic, and result details — not just success/failure status.

**Assessment**: Our observations record tool name and success/failure status but not the query input, result shape, or decision context. Memori's richer trace capture enables facts like "queries with >5 JOINs tend to timeout" or "the user's Snowflake queries typically filter by ARTIST*ID." Our system can only produce "query_snowflake tends to fail" — the \_why* is lost.

**Implementation direction**: Extend `emitToolObservations()` to capture structured metadata from tool inputs and results — query complexity metrics (JOIN count, WHERE clause count), result row counts, error categories. This requires no architectural changes; the `metadata` field on `Observation` already accepts `Record<string, unknown>`. The consolidation pipeline would need new fact key mappings for `failure_pattern` categories derived from metadata patterns.

**Verdict**: Worth pursuing. The observation emitter infrastructure already supports this — the gap is in what metadata we extract, not in the architecture. Adding query complexity metrics to `tool_failure` observations would enable `failure_pattern` facts like "queries with >3 JOINs fail 60% of the time." Implementation: ~50 lines in `observation-emitters.ts` + corresponding fact key mappings in `consolidate.ts`.

### Future applicability: SQL-native storage for compliance

**Memori pattern**: Relational database with full-text search, enabling SQL queries for compliance, analytics, and audit.

**Assessment**: Our Redis storage provides O(1) key-based lookup but cannot express compliance queries ("show all memories for user X for GDPR deletion") or cross-tenant analytics ("which tools fail most globally"). The `FactRepository` interface is storage-agnostic — an Aurora MySQL implementation would provide SQL queryability without changing the consolidation or prompt injection layers.

**Implementation direction**: Add an `AuroraFactRepository` implementing the existing `FactRepository` interface. The Prisma schema already has Aurora MySQL configured (`packages/db/`). Facts could be stored in a `memory_fact` table with indexed columns for `identity_id`, `category`, `subject`, and `confidence`. The interface contract (`get`, `upsert`, `upsertBatch`, `delete`, `deleteBatch`) maps directly to SQL operations.

**Verdict**: Deferred until the privacy/compliance review. The implementation is straightforward (~150 lines for the Prisma model + repository) but requires product decisions about data retention policies and user visibility. If GDPR compliance is required, SQL storage becomes mandatory — Redis key scanning is not a viable compliance mechanism.

### Rejected: SDK middleware interception

**Memori pattern**: Transparent patching of LLM client libraries to capture all interactions without code changes.

**Assessment**: Our agent uses AWS Bedrock via a custom `converseWithTools()` orchestrator. There is no standard LLM client to patch. The Bedrock SDK is called through our own abstraction layer, and the tool-use loop is custom-built. Memori's middleware pattern assumes a standard OpenAI/Anthropic client call pattern — our architecture is too custom for transparent interception.

**Verdict**: Rejected. Our explicit `onToolExecution` callback pattern is the right integration point for a custom orchestrator. The cost is ~300 lines of explicit code, but we get precise control over what is observed and when.

### Rejected: Multi-agent extraction pipeline

**Memori pattern**: Three specialized agents (Capture, Analysis, Retrieval) processing memory in sequence.

**Assessment**: This adds three LLM agent invocations to the memory pipeline. Each agent requires its own system prompt, tool definitions, and LLM calls. For our system where memory is a best-effort enhancement, tripling the LLM call count (and cost) in the memory layer is not justified. Our deterministic aggregation achieves 80% of the signal (tool affinity, entity frequency) at 0% of the LLM cost.

**Verdict**: Rejected. Same rationale as LLM-dependent consolidation in the [MemOS comparison](memos-comparison.md) and [Stash comparison](stash-comparison.md).

### Rejected: Session grouping model

**Memori pattern**: Explicit session objects grouping multi-turn interactions for temporal queries.

**Assessment**: Our conversations are already session-scoped — each conversation ID in the `ConversationStore` represents a session. Observations carry `createdAt` timestamps. We can reconstruct session boundaries from conversation metadata without a separate session model. Adding a session abstraction would duplicate existing conversation scoping.

**Verdict**: Rejected. The information is already available through the conversation store.

---

## Greenfield assessment: if we were building memory today

### What we would keep

1. **Observation → Fact two-stage model.** The separation of raw events (append-only, fire-and-forget) from consolidated beliefs (with confidence and decay) is the right abstraction. Memori's semantic triple extraction is richer but requires LLM calls. Our deterministic aggregation provides sufficient signal for our use case.

2. **Zero LLM consolidation.** Deterministic aggregation is the right choice for a best-effort enhancement layer. Memori's LLM-dependent triple extraction produces richer facts but at cost, latency, and non-determinism we don't need to accept.

3. **Pluggable `DecayStrategy` interface.** Clean separation between the consolidation algorithm and the decay model. Memori does not describe a decay mechanism — our confidence decay is a genuine advantage for temporal relevance management.

4. **Budget-constrained prompt injection.** The 300-token budget with ranked truncation is the right approach. Memori's variable retrieval counts without budget constraints risk context window bloat.

5. **Fire-and-forget observation emission.** Memory writes must not block the critical path. Memori's async extraction pattern validates this choice — both systems decouple observation capture from the request path.

6. **`identityId`-scoped isolation.** Per-user memory with no cross-tenant leakage is a hard requirement.

### What we would change

1. **Capture richer execution trace metadata.** Memori's biggest insight is that tool name + success/failure is insufficient. We should capture query complexity metrics, result characteristics, and error categories in observation metadata. This requires no architectural changes — just richer metadata extraction in `emitToolObservations()`.

2. **Add within-session fact relevance filtering.** Memori's AutoIngest mode is over-engineered for our scale, but the insight — different questions need different context — is valid. A lightweight keyword-match filter on the loaded fact set would improve relevance without adding retrieval infrastructure.

3. **Plan for SQL storage.** Memori's SQL-native foundation enables compliance, analytics, and audit capabilities that Redis cannot provide. Our `FactRepository` interface already supports backend swapping. The Aurora MySQL implementation should be built before the first compliance review, not after.

4. **Add a memory visibility API.** Both Memori (SQL-queryable store) and MemOS (MemFeedback) provide user visibility into memory. Our system has no user-facing memory interface. A REST endpoint returning active facts and supporting deletion is needed for trust and compliance.

5. **Formalize fact structure.** Memori's semantic triples are more expressive than our display-text facts. Adding explicit `predicateVerb` and `object` fields to `Fact` would enable machine-readable queries without changing the consolidation pipeline. The information is already latent in `observationToFactKey()`.

### What we would not adopt from Memori

1. **LLM-dependent fact extraction.** The Analysis Agent's semantic triple extraction is Memori's most powerful feature — and its most expensive. For our use case (tool affinity statistics + entity frequency), deterministic aggregation produces sufficient signal at zero cost. LLM extraction becomes valuable only when facts require natural-language understanding (e.g., "the user seems frustrated with Snowflake performance"), which is beyond our current scope.

2. **SDK middleware interception.** Our custom Bedrock orchestrator does not conform to standard LLM client patterns. Explicit observation callbacks give us precise control. Memori's zero-code integration is elegant for standard OpenAI/Anthropic usage but inapplicable to our architecture.

3. **Multi-agent memory pipeline.** Three specialized agents add LLM cost, latency, and complexity. A single deterministic consolidation function is simpler, cheaper, and sufficient for our fact categories.

4. **Separate embedding infrastructure.** Memori's vector search requires embedding models and vector indexes. Our fact set is small enough for bulk loading. If retrieval becomes necessary, we should use the search service's existing HNSW infrastructure rather than adding a parallel embedding stack to the memory layer.

---

## Benchmark comparison

### LoCoMo benchmark (Memori)

Memori reports results on the LoCoMo (Long-Context Conversation Memory) benchmark:

- Overall accuracy: 81.95%
- Average token consumption: 1,294 tokens per query
- ~4.97% of full-context token footprint
- 67% fewer tokens than Zep on same benchmark
- Outperforms Zep, LangMem, and Mem0

The LoCoMo benchmark evaluates question answering, summarization, and multi-modal dialogue over conversations averaging 300 turns across up to 35 sessions.

### Coda memory (no formal benchmark)

Coda's memory system has no external benchmark. Effectiveness is measured through operational metrics:

- Tool affinity accuracy: does the boosted tool match user preference?
- Prompt section token efficiency: facts within ~300 token budget?
- Consolidation latency: under 500ms timeout threshold?
- Graceful degradation: does the agent function normally when memory fails?

A direct comparison is not meaningful — Memori solves a retrieval problem (find the right memory for this query) while Coda loads all facts and lets the LLM decide relevance. The LoCoMo benchmark tests retrieval quality over long conversation histories, which is orthogonal to our bulk-load-and-inject approach.

---

## Relationship to prior comparisons

| System                                          | Layer     | Relationship to Coda's memory                                                                 |
| ----------------------------------------------- | --------- | --------------------------------------------------------------------------------------------- |
| [Stash](stash-comparison.md)                    | Memory    | Agent memory layer — inspired `@coda/memory` design (observation → fact consolidation, decay) |
| [MemOS](memos-comparison.md)                    | Memory OS | Full memory operating system — validates design choices, identifies future gaps               |
| **Memori**                                      | Memory    | Production memory infrastructure — highlights execution trace capture and SQL storage gaps    |
| [LightRAG](../search/lightrag-comparison.md)    | Retrieval | Graph-enhanced RAG — search layer, orthogonal to memory                                       |
| [LLM Wiki](../knowledge/llm-wiki-comparison.md) | Knowledge | Pre-compiled knowledge — addresses knowledge accumulation, not agent learning                 |

Stash inspired `@coda/memory`'s observation → fact consolidation pattern. MemOS validated the architecture while surfacing gaps in procedural memory and user visibility. Memori highlights two additional gaps: (1) execution trace richness — we capture tool name + status but not query structure, result shape, or decision context; and (2) SQL-native storage — Redis cannot support compliance queries or cross-tenant analytics.

The three memory comparisons (Stash, MemOS, Memori) converge on a consistent finding: our lean, deterministic, zero-LLM memory architecture is the right starting point for a domain-specific agent, but the next evolution requires richer observation metadata, user-facing visibility, and a queryable storage backend.

---

## Project maturity assessment

| Factor             | Memori                                                      | Coda `@coda/memory`                                       |
| ------------------ | ----------------------------------------------------------- | --------------------------------------------------------- |
| **Age**            | Active since 2025; cloud offering launched March 2026       | Implemented 2026; ~3 months in production                 |
| **Maintainership** | Memori Labs team (multi-person company); active development | Solo team; embedded in monorepo                           |
| **Adoption**       | Production cloud customers; TypeScript SDK launched 2026    | Internal project                                          |
| **Documentation**  | Official docs site, architecture guides, benchmark reports  | Architecture doc + design exploration + inline JSDoc      |
| **Code quality**   | Framework-scale Python + TypeScript SDK                     | ~900 LoC TypeScript with comprehensive tests              |
| **Test coverage**  | Not assessed                                                | Unit + integration tests for consolidation, decay, stores |
| **Dependencies**   | PostgreSQL/MySQL + LLM API + embedding service              | Redis (in-memory fallback); zero external dependencies    |
| **Deployment**     | Cloud hosted, BYODB, VPC, on-prem options                   | In-process TypeScript (no additional services)            |
| **Commercial**     | Yes — Memori Cloud with paid tiers                          | Internal                                                  |

Memori is a commercially-backed product with production customers and a cloud offering. Coda's memory is a purpose-built internal feature with a deliberately minimal footprint. Memori's maturity reflects its broader ambition (any agent, any LLM, any deployment); ours reflects our narrower scope (one agent, one LLM, one deployment).

---

## Summary

Memori is agent-native memory infrastructure that automatically captures structured memory from execution traces using semantic triples, stores them in a SQL-native backend, and retrieves them via hybrid search with dual memory modes (session-start injection and per-query dynamic retrieval). Its key innovations are execution trace capture beyond conversation text, semantic triple representation for machine-readable facts, and SQL-native storage for compliance and analytics.

Coda's `@coda/memory` is a lightweight, deterministic memory layer that consolidates tool-execution observations into facts with confidence decay, injecting personalized context into the system prompt at session start. It prioritizes zero LLM cost, graceful degradation, and simplicity.

The two systems reflect different design philosophies: Memori invests in LLM-powered richness (semantic extraction, multi-agent pipeline, hybrid retrieval) while Coda invests in operational simplicity (deterministic consolidation, zero dependencies, graceful fallback). Memori validates our core architecture while highlighting three specific gaps worth addressing:

**Bottom line**: Our architecture is sound for its scope. Memori's most transferable insight is **richer execution trace capture** — we should capture query complexity, result characteristics, and error categories in observation metadata, not just tool name and success/failure. The second insight is **SQL storage for compliance** — our `FactRepository` interface already supports backend swapping, and Aurora MySQL should be wired before the first compliance review. The third is **user-facing memory visibility** — a simple REST endpoint for fact inspection and deletion. These three improvements are incremental additions to our existing architecture, not replacements.

---

## References

- Memori Labs: [Memori](https://github.com/MemoriLabs/Memori) (open-source)
- Memori docs: [memorilabs.ai/docs](https://memorilabs.ai/docs/)
- Memori architecture: [memorilabs.ai/docs/open-source/architecture](https://memorilabs.ai/docs/open-source/architecture/)
- Memori paper: [arXiv 2603.19935](https://arxiv.org/abs/2603.19935)
- LoCoMo benchmark: [snap-research.github.io/locomo](https://snap-research.github.io/locomo/)
- MemOS comparison: [memos-comparison.md](memos-comparison.md)
- Stash comparison: [stash-comparison.md](stash-comparison.md)
- Agent memory design exploration: [agent-memory-exploration.md](../../archive/agent-memory-exploration.md)
- Agent memory architecture: [agent-memory.md](../../../architecture/agent-memory.md)
- Memory package: `packages/memory/src/`
- Memory service integration: `apps/server/src/ai/memory/`
