---
title: "Storage and History"
description: "The persistence and audit layer manages local relational storage across the Python core, TypeScript OSS package, and agent plugins. It initializes SQLite databases, enforces history and message tab..."
last_updated: "2026-10-11T17:37:10.294005+00:00"
canonical_url: "https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/python-core/storage-and-history"
---

<details>
<summary>Relevant source files</summary>

The following files were used as context for generating this wiki page:

- [integrations/mem0-agent-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/memory_core.py)
- [mem0/memory/storage.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/memory/storage.py)
- [integrations/antigravity-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/memory_core.py)
- [integrations/agent-plugin-core/python/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/memory_core.py)
- [integrations/kimi-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/core/memory_core.py)
- [integrations/codex-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/codex-plugin/core/memory_core.py)
- [integrations/cursor-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/core/memory_core.py)
- [integrations/claude-code-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/core/memory_core.py)
- [mem0/memory/main.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/memory/main.py)
- [mem0-ts/src/oss/src/storage/SQLiteManager.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/storage/SQLiteManager.ts)
- [mem0-ts/src/oss/src/storage/base.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/storage/base.ts)
- [mem0-ts/src/oss/src/storage/MemoryHistoryManager.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/storage/MemoryHistoryManager.ts)
- [mem0-ts/src/oss/src/storage/SupabaseHistoryManager.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/storage/SupabaseHistoryManager.ts)
- [mem0/memory/utils.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/memory/utils.py)
</details>

## Overview

The persistence and audit layer manages local relational storage across the Python core, TypeScript OSS package, and agent plugins. It initializes SQLite databases, enforces history and message table schemas, and provides structured transaction tracking for memory versioning and conversational audits.

Sources: [mem0/memory/storage.py:11-19](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/memory/storage.py#L11-L19), [mem0-ts/src/oss/src/storage/SQLiteManager.ts:6-15](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0-ts/src/oss/src/storage/SQLiteManager.ts#L6-L15)

By recording state mutation logs, before-and-after values, and session scopes, the storage engine supports transactional rollback, incremental schema migrations, and reliable agent checkpointing across distributed plugin environments.

Sources: [mem0/memory/storage.py:20-101](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/memory/storage.py#L20-L101), [integrations/agent-plugin-core/python/memory_core.py:699-714](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/memory_core.py#L699-L714)

## SQLite Storage Architecture and Initialization

### Connection Setup and Thread Isolation

The `SQLiteManager` class initializes a SQLite connection using the database path specified by `db_path`, which defaults to `":memory:"` if omitted. To support multi-threaded environments and asynchronous workers across client runtimes, `check_same_thread=False` is passed to `sqlite3.connect()`. A thread lock (`threading.Lock`) protects all transaction boundaries, schema operations, and query executions.

Sources: [mem0/memory/storage.py:11-19](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/memory/storage.py#L11-L19)

> [!IMPORTANT]
> When `check_same_thread=False` is enabled, concurrent access from multiple threads must be serialized explicitly. `SQLiteManager` wraps every state change, table creation, and migration step inside `self._lock` to prevent race conditions during transaction commits.

Sources: [mem0/memory/storage.py:14-19](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/memory/storage.py#L14-L19)

### Schema Generation and Table Definitions

Upon instantiation, `SQLiteManager` invokes schema checks and table creation methods to guarantee that the required relational structures exist. It runs `_migrate_history_table()`, `_create_history_table()`, and `_create_messages_table()`, each wrapped in explicit `BEGIN`, `COMMIT`, and `ROLLBACK` blocks to ensure atomicity.

Sources: [mem0/memory/storage.py:16-18](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/memory/storage.py#L16-L18)

| Table Name | Column Name | Data Type | Key Constraints / Purpose |
| :--- | :--- | :--- | :--- |
| `history` | `id` | TEXT | Primary Key, UUID string identifying the history event |
| `history` | `memory_id` | TEXT | Identifier linking the mutation to a specific memory record |
| `history` | `old_memory` | TEXT | Previous state value before an update or deletion |
| `history` | `new_memory` | TEXT | Updated state value after a mutation |
| `history` | `event` | TEXT | Category or type of mutation event |
| `history` | `created_at` | DATETIME | Timestamp when the record was initially created |
| `history` | `updated_at` | DATETIME | Timestamp when the record was last modified |
| `history` | `is_deleted` | INTEGER | Deletion status flag (0 for active, 1 for deleted) |
| `history` | `actor_id` | TEXT | Identifier of the entity or agent triggering the action |
| `history` | `role` | TEXT | Role associated with the event or actor |
| `messages` | `id` | TEXT | Primary Key, UUID string identifying the message |
| `messages` | `session_scope` | TEXT | Scoping key grouping conversation turns by session |
| `messages` | `role` | TEXT | Sender role (e.g., user, assistant, system) |
| `messages` | `content` | TEXT | Raw textual body of the conversation turn |
| `messages` | `name` | TEXT | Optional display name or handle for the message author |
| `messages` | `created_at` | DATETIME | ISO timestamp recorded when the message was stored |

Sources: [mem0/memory/storage.py:68-79](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/memory/storage.py#L68-L79), [mem0/memory/storage.py:134-141](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/memory/storage.py#L134-L141)

## History Mutation Records and Versioning

### Logging State Mutations and Before-and-After Values

The `SQLiteManager` class records state transitions via `add_history` and `batch_add_history` methods. Each mutation captures the state prior to modification in `old_memory` and the subsequent state in `new_memory`, tied together by a unique `memory_id` and tracked under an `event` category. Transaction execution is safeguarded by an explicit thread lock, rolling back changes on any encountered exception.

```python
def add_history(
    self,
    memory_id: str,
    old_memory: Optional[str],
    new_memory: Optional[str],
    event: str,
    *,
    created_at: Optional[str] = None,
    updated_at: Optional[str] = None,
    is_deleted: int = 0,
    actor_id: Optional[str] = None,
    role: Optional[str] = None,
) -> None:
    with self._lock:
        try:
            self.connection.execute("BEGIN")
            self.connection.execute(
                """
                INSERT INTO history (
                    id, memory_id, old_memory, new_memory, event,
                    created_at, updated_at, is_deleted, actor_id, role
                )
                VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
            """,
                (
                    str(uuid.uuid4()),
                    memory_id,
                    old_memory,
                    new_memory,
                    event,
                    created_at,
                    updated_at,
                    is_deleted,
                    actor_id,
                    role,
                ),
            )
            self.connection.execute("COMMIT")
        except Exception as e:
            self.connection.execute("ROLLBACK")
            logger.error(f"Failed to add history record: {e}")
            raise

def batch_add_history(self, records: List[Dict[str, Any]]) -> None:
    with self._lock:
        try:
            self.connection.execute("BEGIN")
            self.connection.executemany(
                """
                INSERT INTO history (
                    id, memory_id, old_memory, new_memory, event,
                    created_at, updated_at, is_deleted, actor_id, role
                )
                VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
            """,
                [
                    (
                        str(uuid.uuid4()),
                        record.get("memory_id"),
                        record.get("old_memory"),
                        record.get("new_memory"),
                        record.get("event"),
                        record.get("created_at"),
                        record.get("updated_at"),
                        record.get("is_deleted", 0),
                        record.get("actor_id"),
                        record.get("role"),
                    )
                    for record in records
                ],
            )
            self.connection.execute("COMMIT")
        except Exception as e:
            self.connection.execute("ROLLBACK")
            logger.error(f"Failed to batch add history records: {e}")
            raise

```
Sources: [mem0/memory/storage.py:150-226](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/memory/storage.py#L150-L226)

> [!NOTE]
> When batch inserting mutation records through `batch_add_history`, each dictionary entry defaults `is_deleted` to `0` if not explicitly supplied, while optional fields such as `actor_id`, `role`, `created_at`, and `updated_at` are safely retrieved using `.get()`.

Sources: [mem0/memory/storage.py:193-220](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/memory/storage.py#L193-L220)

### History Retrieval Methods

Version history for any tracked memory entry can be queried using the `get_history` method, which fetches all associated mutation records filtered by `memory_id`. The returned rows are ordered chronologically by their creation timestamp and updated datetime values, then mapped into dictionaries with boolean conversion applied to the `is_deleted` status flag.

```python
def get_history(self, memory_id: str) -> List[Dict[str, Any]]:
    with self._lock:
        cur = self.connection.execute(
            """
            SELECT id, memory_id, old_memory, new_memory, event,
                   created_at, updated_at, is_deleted, actor_id, role
            FROM history
            WHERE memory_id = ?
            ORDER BY created_at ASC, DATETIME(updated_at) ASC
        """,
            (memory_id,),
        )
        rows = cur.fetchall()

    return [
        {
            "id": r[0],
            "memory_id": r[1],
            "old_memory": r[2],
            "new_memory": r[3],
            "event": r[4],
            "created_at": r[5],
            "updated_at": r[6],
            "is_deleted": bool(r[7]),
            "actor_id": r[8],
            "role": r[9],
        }
        for r in rows
    ]
```
Sources: [mem0/memory/storage.py:227-255](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/memory/storage.py#L227-L255)

> [!IMPORTANT]
> The sort order in `get_history` relies on `ORDER BY created_at ASC, DATETIME(updated_at) ASC` to preserve the exact linear trajectory of edits performed on a given memory item over its lifecycle.

Sources: [mem0/memory/storage.py:229-236](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/memory/storage.py#L229-L236)

## Session Message Audit and Staging

### Storing Conversation Turns and Pruning Prerequisite Messages

The `SQLiteManager` class records raw conversation turns in the `messages` table via the `save_messages` method. When messages are ingested under a given `session_scope`, each item is assigned a generated UUID, its respective role, content, and optional author name, and an ISO-formatted UTC timestamp. 

```python
def save_messages(self, messages: List[Dict[str, Any]], session_scope: str) -> None:
    if not messages:
        return
    with self._lock:
        try:
            self.connection.execute("BEGIN")
            now = datetime.now(timezone.utc).isoformat()
            for message in messages:
                self.connection.execute(
                    """
                    INSERT INTO messages (id, session_scope, role, content, name, created_at)
                    VALUES (?, ?, ?, ?, ?, ?)
                """,
                    (
                        str(uuid.uuid4()),
                        session_scope,
                        message.get("role"),
                        message.get("content"),
                        message.get("name"),
                        now,
                    ),
                )
```
Sources: [mem0/memory/storage.py:257-278](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/memory/storage.py#L257-L278)

> [!WARNING]
> Immediately after saving new rows, `save_messages` executes an automated retention pruning query that deletes any message for that `session_scope` exceeding the ten most recent entries, determined by descending `created_at` ordering.

Sources: [mem0/memory/storage.py:279-291](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/memory/storage.py#L279-L291)

> [!TIP]
> The subquery within the pruning delete statement wraps the `LIMIT 10` selection inside a derived table. This forces SQLite to materialize the ordered temporary result set before evaluating the outer `NOT IN` clause against the table.

Sources: [mem0/memory/storage.py:282-289](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/memory/storage.py#L282-L289)

### Retrieving Bounded History and Session Contexts

Recent conversation turns are queried using the `get_last_messages` method, which accepts a `session_scope` identifier and an optional `limit` parameter defaulting to 10. The underlying SQL query fetches the latest records in descending order of creation, then wraps them in a subquery to re-sort the results chronologically in ascending order before returning them to the caller.

```python
def get_last_messages(self, session_scope: str, limit: int = 10) -> List[Dict[str, Any]]:
    with self._lock:
        # Subquery picks the latest N rows (DESC + LIMIT), outer query
        # re-sorts them chronologically (ASC) for the caller.
        cur = self.connection.execute(
            """
            SELECT role, content, name, created_at FROM (
                SELECT role, content, name, created_at
                FROM messages
                WHERE session_scope = ?
                ORDER BY created_at DESC
                LIMIT ?
            ) ORDER BY created_at ASC
        """,
            (session_scope, limit),
        )
        rows = cur.fetchall()

    return [
        {
            "role": r[0],
            "content": r[1],
            "name": r[2],
            "created_at": r[3],
        }
        for r in rows
    ]
```
Sources: [mem0/memory/storage.py:298-324](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/memory/storage.py#L298-L324)

## Schema Migrations and Table Evolution

### History Table Schema Validation and Transformation

The `SQLiteManager` class implements an automated migration routine inside `_migrate_history_table` to handle evolution of the `history` table schema across storage revisions. When initialized, the manager inspects SQLite metadata tables to determine whether a pre-existing `history` table exists. If present, it queries column information via `PRAGMA table_info(history)` to evaluate whether the existing column set matches the expected production schema.

```python
cur.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='history'")
if cur.fetchone() is None:
    self.connection.execute("COMMIT")
    return  # nothing to migrate

cur.execute("PRAGMA table_info(history)")
old_cols = {row[1] for row in cur.fetchall()}

expected_cols = {
    "id",
    "memory_id",
    "old_memory",
    "new_memory",
    "event",
    "created_at",
    "updated_at",
    "is_deleted",
    "actor_id",
    "role",
}

if old_cols == expected_cols:
    self.connection.execute("COMMIT")
    return
```
Sources: [mem0/memory/storage.py:32-55](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/memory/storage.py#L32-L55)

If column discrepancies are detected—such as legacy group-chat metadata columns—the migration procedure executes a safe table alteration wrapped inside an explicit transaction block. It drops any leftover staging tables from previous interrupted migrations, renames the active `history` table to `history_old`, provisions a new `history` table conforming to the current schema definition, copies intersecting columns, and drops the old table.

```python
logger.info("Migrating history table to new schema (no convo columns).")

# Clean up any existing history_old table from previous failed migration
cur.execute("DROP TABLE IF EXISTS history_old")

# Rename the current history table
cur.execute("ALTER TABLE history RENAME TO history_old")

# Create the new history table with updated schema
cur.execute(
    """
    CREATE TABLE history (
        id           TEXT PRIMARY KEY,
        memory_id    TEXT,
        old_memory   TEXT,
        new_memory   TEXT,
        event        TEXT,
        created_at   DATETIME,
        updated_at   DATETIME,
        is_deleted   INTEGER,
        actor_id     TEXT,
        role         TEXT
    )
"""
)

# Copy data from old table to new table
intersecting = list(expected_cols & old_cols)
if intersecting:
    cols_csv = ", ".join(intersecting)
    cur.execute(f"INSERT INTO history ({cols_csv}) SELECT {cols_csv} FROM history_old")

# Drop the old table
cur.execute("DROP TABLE history_old")

# Commit the transaction
self.connection.execute("COMMIT")
logger.info("History table migration completed successfully.")
```
Sources: [mem0/memory/storage.py:57-94](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/memory/storage.py#L57-L94)

> [!WARNING]
> If any exception occurs during the schema transformation, the transaction block immediately invokes `ROLLBACK` and propagates the error, preventing partial table migrations or data corruption.

Sources: [mem0/memory/storage.py:96-100](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/memory/storage.py#L96-L100)

### Expected Schema Definition

The history migration and table initialization logic enforces a strict ten-column schema structure. The table attributes, data types, and constraints used during `_migrate_history_table` and `_create_history_table` are outlined below.

| Column Name | Data Type | Constraint / Role |
| :--- | :--- | :--- |
| `id` | `TEXT` | Primary Key |
| `memory_id` | `TEXT` | References target memory entry |
| `old_memory` | `TEXT` | Previous state value |
| `new_memory` | `TEXT` | Updated state value |
| `event` | `TEXT` | Mutation category or event type |
| `created_at` | `DATETIME` | Creation timestamp |
| `updated_at` | `DATETIME` | Modification timestamp |
| `is_deleted` | `INTEGER` | Deletion flag status |
| `actor_id` | `TEXT` | Identifier of performing actor |
| `role` | `TEXT` | Role associated with the record |

Sources: [mem0/memory/storage.py:40-51](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/memory/storage.py#L40-L51), [mem0/memory/storage.py:68-79](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/memory/storage.py#L68-L79), [mem0/memory/storage.py:108-119](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/mem0/memory/storage.py#L108-L119)

## Agent Checkpoint and Flush Isolation

### Tracking Inflight Flushes and Uncommitted Event IDs

The plugin memory cores manage synchronization through explicit database schema cleanups and state tracking. During database initialization or cleanup sequences, legacy or deprecated staging tables—specifically `sidekick_calls`, `sidekick_snapshots`, `sidekick_state`, and `sidekick_packets`—are unconditionally dropped from the SQLite connection to prevent schema pollution across revisions.

```python
DROP TABLE IF EXISTS sidekick_calls;
DROP TABLE IF EXISTS sidekick_snapshots;
DROP TABLE IF EXISTS sidekick_state;
DROP TABLE IF EXISTS sidekick_packets;
```
Sources: [integrations/mem0-agent-plugin/core/memory_core.py:693-696](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/memory_core.py#L693-L696)

To handle ongoing persistence tasks, the `prepare_flush` method utilizes an immediate transaction (`BEGIN IMMEDIATE`) to inspect pending memory flush packets for a given repository and session. It queries the `flushes` table for rows where the status is outside terminal states, enforcing retry thresholds.

```python
def prepare_flush(
    self, repo: RepoContext, session_id: str, reason: str
) -> tuple[str, list[dict[str, Any]]] | None:
    with self.conn:
        self.conn.execute("BEGIN IMMEDIATE")
        existing = self.conn.execute(
            """SELECT * FROM flushes
               WHERE repo_id = ? AND session_id = ?
                 AND status NOT IN ('semantic-succeeded', 'explicitly-stored', 'gave-up')
               ORDER BY created_at LIMIT 1""",
            (repo.identity, session_id),
        ).fetchone()
```
Sources: [integrations/mem0-agent-plugin/core/memory_core.py:797-808](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/memory_core.py#L797-L808)

> [!CAUTION]
> If a flush packet exceeds `MAX_FLUSH_ATTEMPTS` (fixed at 5), the engine transitions its status to `gave-up` and records a telemetry event, preventing infinite retry loops on failing synchronization payloads.

Sources: [integrations/mem0-agent-plugin/core/memory_core.py:65-70](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/memory_core.py#L65-L70), [integrations/mem0-agent-plugin/core/memory_core.py:809-816](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/memory_core.py#L809-L816)

### Subagent State Markers and Repository Scopes

Subagent and sidekick instances are tracked via the `sidekick_runs` table, capturing runtime execution lifecycle events. The `stop_subagent` method manages concurrent subagent termination by wrapping operations in an immediate transaction block, resolving missing agent identifiers, and upserting execution records with transcript paths and final messages.

```python
def stop_subagent(
    self,
    repo: RepoContext,
    session_id: str,
    agent_id: str,
    agent_type: str,
    transcript_path: str,
    final_message: str,
) -> str:
    now = utc_now()
    self.conn.execute("BEGIN IMMEDIATE")
```
Sources: [integrations/mem0-agent-plugin/core/memory_core.py:1068-1078](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/memory_core.py#L1068-L1078)

```python
self.conn.execute(
    """INSERT INTO sidekick_runs
       (repo_id, session_id, agent_id, agent_type, started_at,
        stopped_at, transcript_path, final_message)
       VALUES (?, ?, ?, ?, ?, ?, ?, ?)
       ON CONFLICT(repo_id, session_id, agent_id) DO UPDATE SET
         stopped_at = excluded.stopped_at,
         transcript_path = excluded.transcript_path,
         final_message = excluded.final_message""",
    (
        repo.identity,
        session_id,
        agent_id,
        agent_type,
        now,
        now,
        bounded(transcript_path, 2000),
        redact(final_message).strip(),
    ),
)
```
Sources: [integrations/mem0-agent-plugin/core/memory_core.py:1089-1108](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/memory_core.py#L1089-L1108)

> [!NOTE]
> When an `agent_id` is omitted during subagent stoppage, the store queries for active records without a stop timestamp (`stopped_at IS NULL`). If exactly one overlapping run exists, it correlates automatically; otherwise, it assigns a unique fallback identifier using nanosecond timestamps.

Sources: [integrations/mem0-agent-plugin/core/memory_core.py:1080-1088](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/memory_core.py#L1080-L1088)

Project context isolation is maintained by the `repo_for_session` method and the `session_scopes` table. Working directories and repository identities are pinned per session ID to guarantee that subsequent hooks within the same coding agent session reference consistent git metadata and root paths.

| Table Name | Target Column | Purpose |
| :--- | :--- | :--- |
| `events` | `repo_id` | Stores incremental prompt, tool, and session event payloads |
| `session_scopes` | `repo_id` | Maintains persistent working directory and git root bindings per session |
| `flushes` | `repo_id` | Tracks batch synchronization attempts, payload identifiers, and error statuses |
| `retrievals` | `repo_id` | Logs context retrieval scores, ranks, and injected memory snippets |
| `operations` | `repo_id` | Records execution duration, success flags, and error traces for agent tools |
| `sidekick_runs` | `repo_id` | Markers for native subagent execution lifecycles, transcripts, and final messages |

Sources: [integrations/mem0-agent-plugin/core/memory_core.py:1209-1216](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/memory_core.py#L1209-L1216)

## Related

- [Memory Lifecycle](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/python-core/memory-lifecycle)
- [Database Vector Stores](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/vector-stores/database-vector-stores)


## Sitemap

See the full [sitemap](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/llms.txt) for all pages in this wiki.
