---
title: "Python Agent Core"
description: "The Python Agent Core provides the shared runtime foundation for Mem0 coding agent plugins, managing local evidence persistence, hook orchestration, and background synchronization. It solves the ch..."
last_updated: "2026-10-11T17:37:10.212608+00:00"
canonical_url: "https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/agent-plugins/python-agent-core"
---

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

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

- [integrations/agent-plugin-core/python/hook_runner.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/hook_runner.py)
- [integrations/cursor-plugin/core/hook_runner.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/core/hook_runner.py)
- [integrations/codex-plugin/core/hook_runner.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/codex-plugin/core/hook_runner.py)
- [integrations/antigravity-plugin/core/hook_runner.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/hook_runner.py)
- [integrations/kimi-plugin/core/hook_runner.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/core/hook_runner.py)
- [integrations/claude-code-plugin/core/hook_runner.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/core/hook_runner.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/mem0-agent-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/memory_core.py)
- [integrations/antigravity-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-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/codex-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/codex-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)
- [integrations/kimi-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/core/memory_core.py)
- [integrations/agent-plugin-core/python/flush_worker.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/flush_worker.py)
- [integrations/antigravity-plugin/core/flush_worker.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/flush_worker.py)
- [integrations/cursor-plugin/core/flush_worker.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/core/flush_worker.py)
- [integrations/agent-plugin-core/python/mcp_server.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/mcp_server.py)
- [integrations/kimi-plugin/core/flush_worker.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/core/flush_worker.py)
- [integrations/mem0-agent-plugin/core/mcp_server.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/mcp_server.py)
- [integrations/claude-code-plugin/core/mcp_server.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/core/mcp_server.py)
- [integrations/codex-plugin/core/mcp_server.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/codex-plugin/core/mcp_server.py)
- [integrations/agent-plugin-core/python/telemetry.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/telemetry.py)
- [integrations/cursor-plugin/core/mcp_server.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/core/mcp_server.py)
- [integrations/antigravity-plugin/core/mcp_server.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/mcp_server.py)
- [integrations/kimi-plugin/core/mcp_server.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/core/mcp_server.py)
- [integrations/mem0-agent-plugin/mcp.json](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/mcp.json)
- [integrations/codex-plugin/hooks/adapter.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/codex-plugin/hooks/adapter.py)
- [integrations/claude-code-plugin/.mcp.json](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/.mcp.json)
- [integrations/codex-plugin/.mcp.json](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/codex-plugin/.mcp.json)
- [integrations/agent-plugin-core/python/memory_cli.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/memory_cli.py)
</details>

## Overview

The Python Agent Core provides the shared runtime foundation for Mem0 coding agent plugins, managing local evidence persistence, hook orchestration, and background synchronization. It solves the challenge of capturing developer interactions across distinct agent harnesses by maintaining an SQLite-backed evidence store for session events and subagent turns. Design decisions prioritize local-first reliability through detached worker handoffs, robust error recovery, and strict data scoping.

Sources: [integrations/agent-plugin-core/python/hook_runner.py:1-34](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/hook_runner.py#L1-L34), [integrations/agent-plugin-core/python/memory_core.py:1-8](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/memory_core.py#L1-L8), [integrations/agent-plugin-core/python/flush_worker.py:1-6](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/flush_worker.py#L1-L6)

## Evidence Store Architecture and Session State

### Storage Model and Session Synchronization

The underlying SQLite-backed storage model captures session events, subagent turns, and prompt evidence locally before synchronization. When agents compact or conclude sessions, the system evaluates the state via `flush_session()`, verifying API keys, determining repository contexts through `store.repo_for_session()`, and preparing pending packets via `store.prepare_flush()` to synchronize records with the platform.

Sources: [integrations/agent-plugin-core/python/memory_core.py:1962-1978](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/memory_core.py#L1962-L1978), [integrations/mem0-agent-plugin/core/memory_core.py:1962-1978](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/memory_core.py#L1962-L1978), [integrations/antigravity-plugin/core/memory_core.py:1962-1978](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/memory_core.py#L1962-L1978), [integrations/cursor-plugin/core/memory_core.py:1962-1978](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/core/memory_core.py#L1962-L1978), [integrations/codex-plugin/core/memory_core.py:1962-1978](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/codex-plugin/core/memory_core.py#L1962-L1978)

> [!NOTE]
> Extraction jobs are durable server-side. Transient polling failures during `_wait_for_event()` do not discard jobs that may still complete normally.

Sources: [integrations/agent-plugin-core/python/memory_core.py:1925-1929](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/memory_core.py#L1925-L1929), [integrations/mem0-agent-plugin/core/memory_core.py:1925-1929](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/memory_core.py#L1925-L1929), [integrations/antigravity-plugin/core/memory_core.py:1925-1929](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/memory_core.py#L1925-L1929), [integrations/cursor-plugin/core/memory_core.py:1925-1929](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/core/memory_core.py#L1925-L1929), [integrations/codex-plugin/core/memory_core.py:1925-1929](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/codex-plugin/core/memory_core.py#L1925-L1929)

### Public API Surface and Event Polling

The public functions manage background heartbeats and remote event tracking. The function `touch_handoff_heartbeat()` updates the worker's handoff file timestamp using `MEM0_CODE_HANDOFF_PATH` to prevent recovery mechanisms from prematurely relaunching active workers. Meanwhile, `_wait_for_event()` polls server-side extraction tasks using configurable parameters.

| Parameter / Variable | Environment Variable | Default Value | Purpose |
| :--- | :--- | :--- | :--- |
| Extraction Wait Timeout | `MEM0_CODE_EXTRACTION_WAIT_SECONDS` | `"120"` | Maximum seconds to wait for remote event extraction to complete. |
| Event Poll Interval | `MEM0_CODE_EVENT_POLL_SECONDS` | `"1"` (minimum `0.1`) | Delay interval between successive event status polling checks. |
| Worker Handoff Path | `MEM0_CODE_HANDOFF_PATH` | `""` (disabled) | Filesystem path for the worker handoff heartbeat file. |
| Platform API URL | `MEM0_API_URL` | `DEFAULT_API_URL` | Base URL endpoint for Mem0 platform synchronization requests. |

Sources: [integrations/agent-plugin-core/python/memory_core.py:1893-1910](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/memory_core.py#L1893-L1910), [integrations/mem0-agent-plugin/core/memory_core.py:1893-1910](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/mem0-agent-plugin/core/memory_core.py#L1893-L1910), [integrations/antigravity-plugin/core/memory_core.py:1893-1910](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/memory_core.py#L1893-L1910), [integrations/cursor-plugin/core/memory_core.py:1893-1910](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/core/memory_core.py#L1893-L1910), [integrations/codex-plugin/core/memory_core.py:1893-1910](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/codex-plugin/core/memory_core.py#L1893-L1910)

## Hook Runner Orchestration and Injection

### Input Handling and First-Prompt Context Injection

The hook runner reads JSON payloads from standard input via `read_hook_input()`, safely parsing dictionary structures while falling back to empty dictionaries on decoding or operating system errors. 

```python
def read_hook_input() -> dict:
    try:
        value = json.load(sys.stdin)
        return value if isinstance(value, dict) else {}
    except (json.JSONDecodeError, OSError):
        return {}

```
Sources: [integrations/agent-plugin-core/python/hook_runner.py:44-50](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/hook_runner.py#L44-L50)

When a user submits a prompt, `first_prompt_memory_output()` determines whether the interaction represents the session's initial prompt via `record_user_prompt()`. If it is the first prompt, the function checks environment constraints before executing a memory search.

```python
def first_prompt_memory_output(store: EvidenceStore, hook_input: dict) -> dict:
    """Search once before the agent handles the first prompt in a session."""
    repo, session_id, prompt, is_first_prompt = record_user_prompt(store, hook_input)
    if not is_first_prompt:
        return {}
    try:
        minimum_query_chars = int(os.environ.get("MEM0_CODE_MIN_QUERY_CHARS", "20"))
    except ValueError:
        minimum_query_chars = 20
    if len(prompt.strip()) < max(minimum_query_chars, 1):
        return {}
    result = search_memories(
        store, repo, session_id, bounded(prompt, 6000),
        top_k=5, operation="first-prompt-search", timeout=2,
    )
```
Sources: [integrations/agent-plugin-core/python/hook_runner.py:62-76](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/hook_runner.py#L62-L76)

> [!NOTE]
> The search query character length must meet or exceed the minimum threshold defined by `MEM0_CODE_MIN_QUERY_CHARS` (defaulting to 20 characters) and is bounded to a maximum of 6000 characters before querying.

Sources: [integrations/agent-plugin-core/python/hook_runner.py:67-74](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/hook_runner.py#L67-L74)

If matching memories are retrieved, they are formatted into additional context and logged via telemetry under the `context_injected` event trigger. The hook runner then returns a dictionary containing `hookSpecificOutput` with the populated `additionalContext`.

```python
if not result.memories:
    return {}
context = format_context(
    result.memories,
    "Mem0 found these relevant memories from earlier work in this repository:",
)
telemetry.record(
    "context_injected",
    repo=repo, session_id=session_id, trigger="first-prompt",
    memory_count=len(result.memories), context_chars=len(context),
    prompt_chars=len(prompt),
)
return {
    "hookSpecificOutput": {
        "hookEventName": "UserPromptSubmit",
        "additionalContext": context,
    },
}
```
Sources: [integrations/agent-plugin-core/python/hook_runner.py:77-94](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/hook_runner.py#L77-L94)

### Event Recording and Tool Error Adaptation

Agent turns and tool executions are captured through specialized record handlers. The function `default_record_stop()` logs the assistant response without transcript parsing by retrieving the session repository via `store.repo_for_session()`, redacting sensitive substrings from `last_assistant_message`, and recording the clean text.

```python
def default_record_stop(store: EvidenceStore, hook_input: dict):
    """Record the assistant's response without transcript parsing."""
    session_id = _session_id(hook_input)
    repo = store.repo_for_session(session_id, hook_input.get("cwd"))
    message = redact(hook_input.get("last_assistant_message", "")).strip()
    if message:
        store.record_assistant_response(repo, session_id, message)
    return repo, session_id

```
Sources: [integrations/agent-plugin-core/python/hook_runner.py:52-60](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/hook_runner.py#L52-L60)

Specific integrations like the Codex plugin adapter augment default behavior by handling subagent lifecycle events and normalizing tool execution responses. Specifically, `_post_tool()` inspects tool response objects for numeric exit codes or boolean flags (`isError`, `success`) to reliably flag tool failures before passing payloads to `record_tool()`.

```python
response = payload.get("tool_response")
failed = None
if isinstance(response, dict):
    exit_code = response.get("exit_code")
    if isinstance(exit_code, int) and not isinstance(exit_code, bool):
        failed = exit_code != 0
    elif isinstance(response.get("isError"), bool):
        failed = response["isError"]
    elif isinstance(response.get("success"), bool):
        failed = not response["success"]
if failed:
    payload = {**payload, "error": response}
record_tool(store, payload, failed=failed)
```
Sources: [integrations/codex-plugin/hooks/adapter.py:43-55](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/codex-plugin/hooks/adapter.py#L43-L55)

> [!WARNING]
> Boolean evaluation order for tool responses checks non-boolean integers first (`exit_code != 0`), followed by `isError`, and finally `success`. Ensure tool response schemas populate these keys accurately to prevent misclassified execution states.

Sources: [integrations/codex-plugin/hooks/adapter.py:45-52](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/codex-plugin/hooks/adapter.py#L45-L52)

## Detached Background Flush Worker Lifecycle

### Subprocess Handoff and Payload Initialization

When print-mode host processes or exit hooks face termination constraints (such as Claude Code canceling `SessionEnd` hooks as parent streams close), hooks persist their input payloads and launch `flush_worker.py` as a detached background process in a new session.

```python
def main() -> int:
    if len(sys.argv) != 2:
        return 2
    handoff_path = Path(sys.argv[1])
    os.environ["MEM0_CODE_HANDOFF_PATH"] = str(handoff_path)
    harness = os.environ.get("MEM0_PLUGIN_HARNESS")
    if harness:
        source_tag = os.environ.get("MEM0_PLUGIN_SOURCE_TAG", "")
        configure_harness(
            harness,
            env_prefix=os.environ.get("MEM0_PLUGIN_ENV_PREFIX", ""),
            data_dir_name=os.environ.get("MEM0_PLUGIN_DATA_DIR_NAME", ""),
            source_tag=source_tag,
        )
        telemetry.init(harness=harness, source_tag=source_tag.upper())
    completed = False
```
Sources: [integrations/agent-plugin-core/python/flush_worker.py:25-40](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/flush_worker.py#L25-L40), [integrations/antigravity-plugin/core/flush_worker.py:25-40](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/flush_worker.py#L25-L40), [integrations/cursor-plugin/core/flush_worker.py:25-40](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/core/flush_worker.py#L25-L40), [integrations/kimi-plugin/core/flush_worker.py:25-40](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/core/flush_worker.py#L25-L40)

The worker expects exactly one command-line argument: the file path to the handoff JSON payload (`sys.argv[1]`). It assigns this path to the environment variable `MEM0_CODE_HANDOFF_PATH`. If `MEM0_PLUGIN_HARNESS` is configured in the environment, the worker initializes the plugin harness configuration and sets up telemetry using `telemetry.init()`.

```python
try:
    payload = json.loads(handoff_path.read_text(encoding="utf-8"))
    delay = float(payload.get("delay_seconds") or 0)
    if delay > 0:
        payload.pop("delay_seconds", None)
        temporary = handoff_path.with_suffix(f".{os.getpid()}.tmp")
        try:
            temporary.write_text(json.dumps(payload), encoding="utf-8")
            temporary.replace(handoff_path)
        finally:
            temporary.unlink(missing_ok=True)
        time.sleep(delay)
        if not handoff_path.exists():
            return 0
```
Sources: [integrations/agent-plugin-core/python/flush_worker.py:41-54](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/flush_worker.py#L41-L54), [integrations/antigravity-plugin/core/flush_worker.py:41-54](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/flush_worker.py#L41-L54), [integrations/cursor-plugin/core/flush_worker.py:41-54](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/core/flush_worker.py#L41-L54), [integrations/kimi-plugin/core/flush_worker.py:41-54](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/core/flush_worker.py#L41-L54)

After loading the payload, the worker inspects `delay_seconds`. If a positive delay is specified, the worker pops the delay key, writes the modified payload to a temporary file (`.<pid>.tmp`), atomically replaces the original handoff file, and sleeps for the requested duration.

```python
hook_input = payload.get("hook_input") or {}
reason = str(payload.get("reason") or "checkpoint")
wait_for_inflight = bool(payload.get("wait_for_inflight"))
store = EvidenceStore()
try:
    if wait_for_inflight:
        session_id = str(
            hook_input.get("session_id") or "unknown-session"
        )
        repo = store.repo_for_session(session_id, hook_input.get("cwd"))
        deadline = time.monotonic() + float(
            os.environ.get("MEM0_CODE_EXTRACTION_WAIT_SECONDS", "120")
        )
        while (
            store.has_inflight_flush(repo.identity, session_id)
            and time.monotonic() < deadline
        ):
            touch_handoff_heartbeat()
            time.sleep(0.25)
```
Sources: [integrations/agent-plugin-core/python/flush_worker.py:55-73](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/flush_worker.py#L55-L73), [integrations/antigravity-plugin/core/flush_worker.py:55-73](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/flush_worker.py#L55-L73), [integrations/cursor-plugin/core/flush_worker.py:55-73](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/core/flush_worker.py#L55-L73), [integrations/kimi-plugin/core/flush_worker.py:55-73](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/core/flush_worker.py#L55-L73)

### Heartbeat Tracking and Inflight Checkpointing

If the payload configures `wait_for_inflight` as true, the worker monitors existing database activity before executing the final checkpoint. It retrieves the active repository context for the session and polls `store.has_inflight_flush()` until active flushes clear or a timeout is reached.

```python
deadline = time.monotonic() + float(
    os.environ.get("MEM0_CODE_EXTRACTION_WAIT_SECONDS", "120")
)
while (
    store.has_inflight_flush(repo.identity, session_id)
    and time.monotonic() < deadline
):
    touch_handoff_heartbeat()
    time.sleep(0.25)
```
Sources: [integrations/agent-plugin-core/python/flush_worker.py:65-73](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/flush_worker.py#L65-L73), [integrations/antigravity-plugin/core/flush_worker.py:65-73](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/flush_worker.py#L65-L73), [integrations/cursor-plugin/core/flush_worker.py:65-73](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/core/flush_worker.py#L65-L73), [integrations/kimi-plugin/core/flush_worker.py:65-73](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/core/flush_worker.py#L65-L73)

> [!NOTE]
> The extraction wait timeout defaults to 120 seconds, configurable via the `MEM0_CODE_EXTRACTION_WAIT_SECONDS` environment variable. During each 0.25-second poll iteration, the worker invokes `touch_handoff_heartbeat()`.

Sources: [integrations/agent-plugin-core/python/flush_worker.py:66-73](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/flush_worker.py#L66-L73), [integrations/agent-plugin-core/python/memory_core.py:1893-1901](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/memory_core.py#L1893-L1901)

The helper `touch_handoff_heartbeat()` updates the modification timestamp of the handoff file via `os.utime(path)`, signaling to external supervisor processes that the background worker remains active.

```python
def touch_handoff_heartbeat() -> None:
    """Mark the worker's handoff file alive so recovery does not relaunch it."""
    path = os.environ.get("MEM0_CODE_HANDOFF_PATH", "")
    if not path:
        return
    try:
        os.utime(path)
    except OSError:
        pass
```
Sources: [integrations/agent-plugin-core/python/memory_core.py:1893-1901](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/memory_core.py#L1893-L1901)

### Status Evaluation and Recovery Cleanup

Once inflight checks conclude, the worker invokes `checkpoint_session(store, hook_input, reason)` to process and transmit the session evidence packet to the platform backend.

```python
    # Hooks capture the conversation before handoff; the worker only flushes it.
    result = checkpoint_session(store, hook_input, reason)
    print(json.dumps(result, sort_keys=True), flush=True)
    completed = result.get("status") in {
        "semantic-succeeded",
        "explicitly-stored",
        "nothing-to-flush",
    }
finally:
    store.close()
return 0
```
Sources: [integrations/agent-plugin-core/python/flush_worker.py:74-84](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/flush_worker.py#L74-L84), [integrations/antigravity-plugin/core/flush_worker.py:74-84](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/flush_worker.py#L74-L84), [integrations/cursor-plugin/core/flush_worker.py:74-84](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/core/flush_worker.py#L74-L84), [integrations/kimi-plugin/core/flush_worker.py:74-84](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/core/flush_worker.py#L74-L84)

The completion status determines how the handoff file is cleaned up in the `finally` block. Successful status codes validate completion, unlinking the handoff file. If incomplete and the file uses a `.running` suffix, it is renamed back to `.json` for recovery.

```python
finally:
    telemetry.flush()
    if completed:
        try:
            handoff_path.unlink()
        except OSError:
            pass
    elif handoff_path.suffix == ".running":
        try:
            handoff_path.replace(handoff_path.with_suffix(".json"))
        except OSError:
            pass

```
Sources: [integrations/agent-plugin-core/python/flush_worker.py:85-97](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/flush_worker.py#L85-L97), [integrations/antigravity-plugin/core/flush_worker.py:85-97](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/flush_worker.py#L85-L97), [integrations/cursor-plugin/core/flush_worker.py:85-97](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/core/flush_worker.py#L85-L97), [integrations/kimi-plugin/core/flush_worker.py:85-97](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/core/flush_worker.py#L85-L97)

| Status Code | Action | Outcome |
| :--- | :--- | :--- |
| `semantic-succeeded` | Complete execution | Handoff file is unlinked |
| `explicitly-stored` | Complete execution | Handoff file is unlinked |
| `nothing-to-flush` | Complete execution | Handoff file is unlinked |
| Other / Error | Incomplete execution | `.running` suffix renamed to `.json` |

Sources: [integrations/agent-plugin-core/python/flush_worker.py:77-81](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/flush_worker.py#L77-L81), [integrations/agent-plugin-core/python/flush_worker.py:87-96](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/flush_worker.py#L87-L96)

## MCP Server Memory Search Tool

### JSON-RPC Protocol Handling and Request Routing

The Model Context Protocol (MCP) server runs over standard input and output streams, processing newline-delimited JSON-RPC 2.0 messages via `main()`. Incoming payloads are parsed and routed through `handle_request()`, which supports methods including `initialize`, `ping`, `tools/list`, and `tools/call`. Initialization responds with protocol version `2024-11-05`, tool capabilities, and server metadata containing name `mem0` and `PLUGIN_VERSION`.

```python
def handle_request(message: Any) -> dict[str, Any] | None:
    if not isinstance(message, dict):
        return None
    request_id = message.get("id")
    method = message.get("method")

    if method == "notifications/initialized":
        return None
    if method == "initialize":
        requested = (message.get("params") or {}).get("protocolVersion")
        return {
            "jsonrpc": "2.0",
            "id": request_id,
            "result": {
                "protocolVersion": requested or PROTOCOL_VERSION,
                "capabilities": {"tools": {"listChanged": False}},
                "serverInfo": {"name": "mem0", "version": PLUGIN_VERSION},
            },
        }
    if method == "ping":
        return {"jsonrpc": "2.0", "id": request_id, "result": {}}
    if method == "tools/list":
        return {
            "jsonrpc": "2.0",
            "id": request_id,
            "result": {
                "tools": [
                    {
                        "name": TOOL_NAME,
                        "description": TOOL_DESCRIPTION,
                        "inputSchema": TOOL_SCHEMA,
                        "annotations": {
                            "readOnlyHint": True,
                            "idempotentHint": True,
                            "openWorldHint": True,
                        },
                    }
                ]
            },
        }
```
Sources: [integrations/agent-plugin-core/python/mcp_server.py:152-191](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/mcp_server.py#L152-L191)

> [!NOTE]
> Unhandled methods return a JSON-RPC error with code `-32601` ("Method not found"), while parse errors yield code `-32700` and internal exceptions return code `-32603`. Notifications such as `notifications/initialized` return `None` and produce no output.

Sources: [integrations/agent-plugin-core/python/mcp_server.py:158-159](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/mcp_server.py#L158-L159), [integrations/agent-plugin-core/python/mcp_server.py:206-231](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/mcp_server.py#L206-L231)

### Tool Schema and Argument Validation

When a client invokes `tools/call` for `search_memories`, arguments are validated by `_validate_arguments()` against a strict input schema. The tool accepts a required `query` string alongside optional parameters `top_k`, `category`, `scope`, and `run_id`.

| Parameter | Type | Validation Rules | Description |
| :--- | :--- | :--- | :--- |
| `query` | string | Non-empty, max 2000 chars | Direct question about earlier work in the repository |
| `top_k` | integer | Integer between 1 and 20 (inclusive) | Maximum memories to return |
| `category` | string | Must be in `CODING_MEMORY_CATEGORY_NAMES` | Optional memory category filter |
| `scope` | string | Must be in `SEARCH_SCOPES` | Search boundary (`repo`, `dir`, or `mine`) |
| `run_id` | string | Non-empty string when provided | Coding-agent session ID restriction |

Sources: [integrations/agent-plugin-core/python/mcp_server.py:28-68](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/mcp_server.py#L28-L68), [integrations/agent-plugin-core/python/mcp_server.py:75-112](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/mcp_server.py#L75-L112)

> [!WARNING]
> Passing boolean values for integer parameters like `top_k` or supplying unknown keys outside the permitted set triggers a `ToolInputError`, which is caught and returned to the client as an MCP tool error response with `isError: true`.

Sources: [integrations/agent-plugin-core/python/mcp_server.py:78-83](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/mcp_server.py#L78-L83), [integrations/agent-plugin-core/python/mcp_server.py:93-96](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/mcp_server.py#L93-L96), [integrations/agent-plugin-core/python/mcp_server.py:201-202](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/mcp_server.py#L201-L202)

### Workspace Resolution and Execution

The helper `_workspace_cwd(params)` inspects request metadata for `x-codex-turn-metadata` workspaces to determine the active working directory. If absent, `call_search_memories()` falls back to `CLAUDE_PROJECT_DIR` or the current working directory via `resolve_repo()`, then executes `search_memories()` with operation `mcp-search`.

```python
def call_search_memories(arguments: Any, cwd: str | None = None) -> str:
    query, top_k, category, scope, run_id = _validate_arguments(arguments)
    repo = resolve_repo(cwd or os.environ.get("CLAUDE_PROJECT_DIR") or os.getcwd())
    result = search_memories(
        None,
        repo,
        None,
        query,
        top_k=top_k,
        category=category,
        scope=scope,
        run_id=run_id,
        operation="mcp-search",
    )
    return format_search_result(result)


def _workspace_cwd(params: dict[str, Any]) -> str | None:
    meta = params.get("_meta")
    if not isinstance(meta, dict):
        return None
    metadata = meta.get("x-codex-turn-metadata")
    if not isinstance(metadata, dict):
        return None
    workspaces = metadata.get("workspaces") or {}
    if isinstance(workspaces, dict):
        return next((path for path in workspaces if isinstance(path, str) and path), None)
    return None
```
Sources: [integrations/agent-plugin-core/python/mcp_server.py:115-142](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/mcp_server.py#L115-L142)

## Telemetry Spooling and Identity Management

### Spooling Architecture and Non-Blocking Recording

Telemetry events record session details locally without blocking tool execution or touching the network during short-lived agent turns. The `record` function appends a single JSON line to a local spool file within a strict 3 to 6-second hook execution budget. 

> [!NOTE]
> Telemetry never transmits sensitive content such as prompts, memory text, queries, file paths, repository names, or raw API keys. Recorded payloads are restricted to event names, durations, coarse outcomes, counts, and identifiers hashed with a random per-installation salt.

Sources: [integrations/agent-plugin-core/python/telemetry.py:8-17](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/telemetry.py#L8-L17)

### Installation State and Salt Management

The installation salt is generated once and memoized using an atomic file creation mechanism (`O_CREAT |O_EXCL`) located at `telemetry-salt` within the data directory. This design isolates salt generation from identity dictionaries, preventing concurrent processes from overwriting repository hashes or splitting user identification during rapid tool invocations. Telemetry can be completely disabled across any process by setting the environment variable `MEM0_TELEMETRY` to `false`, `0`, `no`, or `off`.

Sources: [integrations/agent-plugin-core/python/telemetry.py:115-149](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/telemetry.py#L115-L149)

### Configuration Constants and Claim Parameters

The telemetry module defines several operational constants governing spool limits, batch sizes, timeout boundaries, and claim retry mechanics for background delivery workers.

| Constant Name | Value | Purpose |
| :--- | :--- | :--- |
| `SPOOL_LIMIT_BYTES` | `262144` (256 KB) | Maximum size threshold for the local telemetry spool file |
| `BATCH_SIZE` | `100` | Maximum number of events sent in a single PostHog batch request |
| `SEND_TIMEOUT` | `5` | Maximum network timeout duration in seconds for HTTP requests |
| `CLAIM_STALE_SECONDS` | `120` | Time window before an unacknowledged claim is considered stale |
| `CLAIM_EXPIRY_SECONDS` | `604800` (7 days) | Total expiration period for parked claims |
| `MAX_CLAIM_ATTEMPTS` | `3` | Maximum retry attempts before a batch claim is permanently discarded |
| `MAX_PARKED_PER_RUN` | `3` | Bound on parked claims drained per flush run after the live spool |
| `RETRY_COOLDOWN_SECONDS` | `60` | Cooldown period added per prior attempt before a released claim is reclaimable |

Sources: [integrations/agent-plugin-core/python/telemetry.py:98-112](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/telemetry.py#L98-L112)

## Command-Line Diagnostics and User Controls

### Status Verification and Health Diagnostics

The CLI tooling exposed by `memory_cli.py` provides administrative commands for inspecting plugin state, verifying environment configuration, and running diagnostic checks. Invoking `status` retrieves summary metrics including pause state, repository identity, local data path, API key configuration status, total recorded session details, memory updates, returned memories, and subagent runs. When executed with `--json`, these properties are serialized directly as a JSON object, while default execution formats human-readable metrics through `_print_status`. The `doctor` command executes diagnostic validations against the current working directory, printing individual check outcomes as `PASS` or `FAIL` with detailed error descriptions, returning an exit code of `0` on success or `1` if any check fails.

```python
def _print_status(value: dict) -> None:
    last = value.get("last_operation") or {}
    print(f"Mem0: {'paused' if value['paused'] else 'active'}")
    print(f"Repository: {value['repo_id']}")
    print(f"Local data: {value['data_dir']}")
    print(f"API key: {'configured' if value['api_key_configured'] else 'missing'}")
    print(
        "Saved on this computer: "
        f"{value['events']} session details, {value['flushes']} memory updates"
    )
    print(
        f"Used in this repository: {value['retrievals']} memories returned, "
        f"{value['subagent_runs']} subagent runs"
    )
    if last:
        item_label = ""
        if last["operation"] in {"flush", "flush-retry"}:
            item_label = f", {last['item_count']} memories"
        operation = (
            "memory update"
            if last["operation"] in {"flush", "flush-retry"}
            else last["operation"].replace("-", " ")
        )
        print(
            f"Last {operation}: "
            f"{'succeeded' if last['success'] else 'failed'} "
            f"({last['duration_ms']:.1f} ms{item_label})"
        )
    subagent = value.get("last_subagent") or {}
    if subagent:
        state = "finished" if subagent.get("stopped_at") else "started"
        print(
            "Last subagent: "
            f"{state}, received {subagent['context_chars']} characters of memory, "
            f"agent {subagent['agent_id']}"
        )

```
Sources: [integrations/agent-plugin-core/python/memory_cli.py:23-59](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/memory_cli.py#L23-L59), [integrations/agent-plugin-core/python/memory_cli.py:67-72](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/memory_cli.py#L67-L72), [integrations/agent-plugin-core/python/memory_cli.py:92-116](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/memory_cli.py#L92-L116)

### Configuration Management and Repository Forgetting

Users can toggle memory persistence dynamically using the `pause` and `resume` commands, which update the `paused` setting in the evidence store to `true` or `false` respectively. Repository data removal is handled by the `forget` command, which requires explicit confirmation via the `--yes` flag to prevent accidental data destruction. If `--yes` is omitted, the CLI aborts with exit code `2` and displays a warning message. When confirmed, `forget` invokes `forget_remote_repo()` if `--include-project-memory` and `--remote` are specified, alongside `store.forget_local_repo()` to clear local SQLite evidence for the repository identity.

> [!CAUTION]
> The `forget` command permanently destroys local session evidence and, when `--remote` is supplied, deletes scoped repository and user memories from the Mem0 platform. This operation requires the `--yes` safety flag and cannot be undone.

Sources: [integrations/agent-plugin-core/python/memory_cli.py:76-80](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/memory_cli.py#L76-L80), [integrations/agent-plugin-core/python/memory_cli.py:117-146](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/memory_cli.py#L117-L146)

## Related

- [TypeScript Agent Core](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/agent-plugins/typescript-agent-core)
- [Claude Code Plugin](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/agent-plugins/claude-code-plugin)
- [IDE Plugins Family](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/agent-plugins/ide-plugins-family)


## Sitemap

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