---
title: "IDE Plugins Family"
description: "The IDE plugins family integrates Mem0 memory persistence directly into developer coding environments integrations/kimi-plugin/kimi.plugin.json:1-12, including OpenCode Integration and the Claude C..."
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/ide-plugins-family"
---

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

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

- [integrations/codex-plugin/hooks/hooks.json](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/codex-plugin/hooks/hooks.json)
- [integrations/antigravity-plugin/hooks.json](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/hooks.json)
- [integrations/cursor-plugin/mcp.json](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/mcp.json)
- [integrations/opencode-plugin/opencode-mem0.ts](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/opencode-plugin/opencode-mem0.ts)
- [integrations/kimi-plugin/hooks/adapter.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/hooks/adapter.py)
- [integrations/cursor-plugin/hooks/hooks.json](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/hooks/hooks.json)
- [integrations/cursor-plugin/hooks/adapter.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/hooks/adapter.py)
- [integrations/kimi-plugin/kimi.plugin.json](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/kimi.plugin.json)
- [integrations/antigravity-plugin/hooks/adapter.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/hooks/adapter.py)
- [integrations/codex-plugin/.mcp.json](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/codex-plugin/.mcp.json)
- [integrations/codex-plugin/hooks/adapter.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/codex-plugin/hooks/adapter.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/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/codex-plugin/core/memory_core.py)
- [integrations/kimi-plugin/core/hook_runner.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-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/claude-code-plugin/.mcp.json](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/.mcp.json)
- [integrations/antigravity-plugin/core/hook_runner.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-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/cursor-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-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/claude-code-plugin/hooks/hooks.json](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/hooks/hooks.json)
- [integrations/antigravity-plugin/core/_harness_id.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/_harness_id.py)
- [integrations/kimi-plugin/core/memory_core.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/core/memory_core.py)
- [integrations/antigravity-plugin/mcp_config.json](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/mcp_config.json)
- [integrations/cursor-plugin/plugin-build.json](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/plugin-build.json)
- [integrations/agent-plugin-core/python/hook_runner.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/agent-plugin-core/python/hook_runner.py)
- [integrations/kimi-plugin/core/_harness_id.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/core/_harness_id.py)
- [integrations/cursor-plugin/core/_harness_id.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/core/_harness_id.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/claude-code-plugin/adapters/claude/hook.py](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/claude-code-plugin/adapters/claude/hook.py)
</details>

## Overview

The IDE plugins family integrates Mem0 memory persistence directly into developer coding environments [integrations/kimi-plugin/kimi.plugin.json:1-12](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/kimi.plugin.json#L1-L12), including [OpenCode Integration](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/agent-plugins/opencode-integration) and the [Claude Code Plugin](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/agent-plugins/claude-code-plugin). These integrations solve the fragmentation of context across sessions by capturing prompts, tool executions, and transcript streams, bridging editor hooks [integrations/cursor-plugin/hooks/adapter.py:2-3](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/hooks/adapter.py#L2-L3) with the [Python Agent Core](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/agent-plugins/python-agent-core). Key design decisions include unified harness identifiers, normalized event adapters [integrations/kimi-plugin/hooks/adapter.py:85-100](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/hooks/adapter.py#L85-L100), and Model Context Protocol (MCP) server declarations [integrations/cursor-plugin/mcp.json:2-17](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/mcp.json#L2-L17) to synchronize long-term memory across hosts.

Sources: [integrations/kimi-plugin/kimi.plugin.json:1-12](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/kimi.plugin.json#L1-L12), [integrations/cursor-plugin/hooks/adapter.py:2-3](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/hooks/adapter.py#L2-L3), [integrations/kimi-plugin/hooks/adapter.py:85-100](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/hooks/adapter.py#L85-L100), [integrations/cursor-plugin/mcp.json:2-17](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/mcp.json#L2-L17)

## IDE MCP Server Manifest Specifications

### Cursor Environment Manifest

The Cursor plugin configures the `mem0` Model Context Protocol server via `mcp.json`, declaring a `stdio` transport type executing `python3` with path resolution tied to `${CURSOR_PLUGIN_ROOT}`. Environment variables pass template parameters for API authentication and memory scoping directly to the underlying server process.

```json
{
  "mcpServers": {
    "mem0": {
      "type": "stdio",
      "command": "python3",
      "args": [
        "${CURSOR_PLUGIN_ROOT}/core/mcp_server.py"
      ],
      "env": {
        "PLUGIN_OPTION_API_KEY": "${api_key}",
        "PLUGIN_OPTION_USER_ID": "${user_id}",
        "PLUGIN_OPTION_TOP_K": "${top_k}",
        "PLUGIN_OPTION_MAX_CONTEXT_CHARS": "${max_context_chars}",
        "PLUGIN_OPTION_SEARCH_SCOPE": "${search_scope}"
      }
    }
  }
}
```

Sources: [integrations/cursor-plugin/mcp.json:1-18](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/mcp.json#L1-L18)

| Option Key | Value Template | Purpose |
| :--- | :--- | :--- |
| `PLUGIN_OPTION_API_KEY` | `${api_key}` | Authentication token for memory service requests |
| `PLUGIN_OPTION_USER_ID` | `${user_id}` | User scope identifier for stored memories |
| `PLUGIN_OPTION_TOP_K` | `${top_k}` | Result count limit for memory retrieval queries |
| `PLUGIN_OPTION_MAX_CONTEXT_CHARS` | `${max_context_chars}` | Character threshold governing context injection size |
| `PLUGIN_OPTION_SEARCH_SCOPE` | `${search_scope}` | Boundary filter applied during memory searches |

Sources: [integrations/cursor-plugin/mcp.json:9-15](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/mcp.json#L9-L15)

> [!NOTE]
> Cursor relies on explicit `${CURSOR_PLUGIN_ROOT}` path interpolation within `args` to locate `mcp_server.py` relative to the installed plugin bundle directory.

Sources: [integrations/cursor-plugin/mcp.json:5-8](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/mcp.json#L5-L8)

### Codex Environment Manifest

The Codex plugin declares its MCP server configuration within `.mcp.json`, setting a working directory (`cwd`) of `.` alongside an explicit array of allowed environment variable names rather than interpolated option templates.

```json
{
  "mcpServers": {
    "mem0": {
      "type": "stdio",
      "command": "python3",
      "args": ["./core/mcp_server.py"],
      "cwd": ".",
      "env_vars": [
        "MEM0_API_KEY",
        "MEM0_API_URL",
        "MEM0_CODE_USER_ID",
        "MEM0_USER_ID",
        "MEM0_RESOLVED_USER_ID",
        "MEM0_PROJECT_ID",
        "MEM0_TELEMETRY"
      ]
    }
  }
}
```

Sources: [integrations/codex-plugin/.mcp.json:1-18](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/codex-plugin/.mcp.json#L1-L18)

> [!IMPORTANT]
> Codex uses the `env_vars` array specification to whitelist environment variables (`MEM0_API_KEY`, `MEM0_API_URL`, `MEM0_CODE_USER_ID`, `MEM0_USER_ID`, `MEM0_RESOLVED_USER_ID`, `MEM0_PROJECT_ID`, `MEM0_TELEMETRY`) to be passed through to the Python MCP server process.

Sources: [integrations/codex-plugin/.mcp.json:8-16](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/codex-plugin/.mcp.json#L8-L16)

### Antigravity Environment Manifest

The Antigravity plugin uses a streamlined `mcp_config.json` manifest structure that omits explicit transport type declarations, environment maps, and working directories, launching `python3` with a relative script path.

```json
{
  "mcpServers": {
    "mem0": {
      "command": "python3",
      "args": ["./core/mcp_server.py"]
    }
  }
}
```

Sources: [integrations/antigravity-plugin/mcp_config.json:1-8](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/mcp_config.json#L1-L8)

> [!WARNING]
> Omitting `type: "stdio"` and environment block definitions means Antigravity relies entirely on process inheritance for runtime configuration and default transport bindings.

Sources: [integrations/antigravity-plugin/mcp_config.json:1-8](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/mcp_config.json#L1-L8)

## Plugin Metadata and Host Declarations

### Kimi Plugin Definition

The Kimi extension is defined by `kimi.plugin.json`, specifying metadata, package keywords, an Apache-2.0 license, and integration interfaces for Kimi Code. It registers a Model Context Protocol (MCP) server running `python3` with `./core/mcp_server.py`, alongside an array of event hooks with specific execution timeouts.

```json
{
  "name": "mem0",
  "version": "0.3.3",
  "description": "Cross-session memory and token savings for coding agents.",
  "keywords": ["memory", "coding-agents", "continual-learning", "token-efficiency"],
  "author": { "name": "Mem0", "email": "support@mem0.ai" },
  "homepage": "https://docs.mem0.ai/integrations/kimi",
  "license": "Apache-2.0",
  "interface": {
    "displayName": "Mem0",
    "shortDescription": "Cross-session memory for Kimi Code"
  },
  "skills": "./skills/",
  "mcpServers": {
    "mem0": {
      "command": "python3",
      "args": ["./core/mcp_server.py"]
    }
  },
  "hooks": [
    { "event": "SessionStart", "command": "python3 ./hooks/adapter.py SessionStart", "timeout": 5 },
    { "event": "UserPromptSubmit", "command": "python3 ./hooks/adapter.py UserPromptSubmit", "timeout": 6 },
    { "event": "PostToolUse", "matcher": ".*", "command": "python3 ./hooks/adapter.py PostToolUse", "timeout": 3 },
    { "event": "PostToolUseFailure", "matcher": ".*", "command": "python3 ./hooks/adapter.py PostToolUseFailure", "timeout": 3 },
    { "event": "Stop", "command": "python3 ./hooks/adapter.py Stop", "timeout": 3 },
    { "event": "PreCompact", "command": "python3 ./hooks/adapter.py PreCompact", "timeout": 5 },
    { "event": "SessionEnd", "command": "python3 ./hooks/adapter.py SessionEnd", "timeout": 5 }
  ]
}
```

Sources: [integrations/kimi-plugin/kimi.plugin.json:1-28](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/kimi.plugin.json#L1-L28)

| Hook Event | Matcher | Command Template | Timeout (s) |
| :--- | :--- | :--- | :--- |
| `SessionStart` | — | `python3 ./hooks/adapter.py SessionStart` | 5 |
| `UserPromptSubmit` | — | `python3 ./hooks/adapter.py UserPromptSubmit` | 6 |
| `PostToolUse` | `.*` | `python3 ./hooks/adapter.py PostToolUse` | 3 |
| `PostToolUseFailure` | `.*` | `python3 ./hooks/adapter.py PostToolUseFailure` | 3 |
| `Stop` | — | `python3 ./hooks/adapter.py Stop` | 3 |
| `PreCompact` | — | `python3 ./hooks/adapter.py PreCompact` | 5 |
| `SessionEnd` | — | `python3 ./hooks/adapter.py SessionEnd` | 5 |

Sources: [integrations/kimi-plugin/kimi.plugin.json:20-28](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/kimi.plugin.json#L20-L28)

> [!NOTE]
> Kimi hook registrations rely on direct script invocations via `python3 ./hooks/adapter.py` paired with capitalized event selectors.

Sources: [integrations/kimi-plugin/kimi.plugin.json:20-28](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/kimi.plugin.json#L20-L28)

### Cursor Packaging and Native Build Configuration

Cursor plugin builds are managed via `plugin-build.json`, which sets the package identifier to `mem0`, version `0.3.3`, and links to the Cursor documentation homepage. The `native` manifest maps the plugin root directory `${CURSOR_PLUGIN_ROOT}` to specific bundle assets required for distribution.

```json
{
  "id": "mem0",
  "version": "0.3.3",
  "homepage": "https://docs.mem0.ai/integrations/cursor",
  "native": {
    "pluginRoot": "${CURSOR_PLUGIN_ROOT}",
    "files": {
      ".cursor-plugin/plugin.json": ".cursor-plugin/plugin.json",
      "hooks/hooks.json": "hooks/hooks.json",
      "hooks/adapter.py": "hooks/adapter.py",
      "mcp.json": "mcp.json"
    }
  }
}
```

Sources: [integrations/cursor-plugin/plugin-build.json:1-13](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/plugin-build.json#L1-L13)

| Bundled Asset Path | Target Destination | Purpose |
| :--- | :--- | :--- |
| `.cursor-plugin/plugin.json` | `.cursor-plugin/plugin.json` | Host extension registration metadata |
| `hooks/hooks.json` | `hooks/hooks.json` | Lifecycle event hook mappings |
| `hooks/adapter.py` | `hooks/adapter.py` | Python adapter translating host payloads |
| `mcp.json` | `mcp.json` | Model Context Protocol server configuration |

Sources: [integrations/cursor-plugin/plugin-build.json:7-12](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/plugin-build.json#L7-L12)

> [!CAUTION]
> The packaging manifest strictly requires all four bundled files under `native.files`; omitting any file path during compilation causes runtime initialization failures within the Cursor environment.

Sources: [integrations/cursor-plugin/plugin-build.json:5-13](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/plugin-build.json#L5-L13)

## Host Hook Adapters and Normalization

### Payload Translation and Transcript Parsing

Each IDE environment delivers lifecycle hooks and transcript files with distinct payloads. The host adapters translate these platform-specific structures into normalized arguments and key-value fields consumed by the shared Mem0 runtime.

The Kimi plugin adapter maps native events such as `SessionStart`, `UserPromptSubmit`, `PostToolUse`, `PostToolUseFailure`, `Stop`, `PreCompact`, and `SessionEnd` to shared actions, normalizing field variations where `tool_output` or `error` populates `tool_response`, and `response` sets `last_assistant_message`. When processing `Stop` events, the adapter resolves the session directory from `KIMI_CODE_HOME` or `~/.kimi-code` using `session_index.jsonl` or directory scanning, locates the agent wire transcript at `agents/main/wire.jsonl`, parses assistant message loops, and injects the latest assistant response.

```python
EVENTS = {
    "SessionStart": ["session-start"],
    "UserPromptSubmit": ["user-prompt"],
    "PostToolUse": ["post-tool"],
    "PostToolUseFailure": ["post-tool-failure"],
    "Stop": ["stop"],
    "PreCompact": ["flush", "--reason", "pre-compact"],
    "SessionEnd": ["flush", "--reason", "session-end"],
}


def _session_dir(session_id: str) -> Path | None:
    home = Path(os.environ.get("KIMI_CODE_HOME", Path.home() / ".kimi-code")).expanduser()
    found = None
    try:
        with (home / "session_index.jsonl").open(encoding="utf-8") as index:
            for line in index:
                try:
                    entry = json.loads(line)
                except json.JSONDecodeError:
                    continue
                if entry.get("sessionId") == session_id and isinstance(entry.get("sessionDir"), str):
                    found = Path(entry["sessionDir"])
    except OSError:
        pass
    if found:
        return found
    if not session_id or Path(session_id).name != session_id:
        return None
    return next((path for path in (home / "sessions").glob(f"*/{session_id}") if path.is_dir()), None)


def _last_assistant_message(transcript: Path) -> str:
    message = ""
    step_id = None
    parts = []
    try:
        lines = transcript.read_text(encoding="utf-8").splitlines()
    except OSError:
        return ""
    for line in lines:
        try:
            record = json.loads(line)
        except json.JSONDecodeError:
            continue
        if record.get("type") != "context.append_loop_event" or not isinstance(record.get("event"), dict):
            continue
        event = record["event"]
        if event.get("type") == "step.begin":
            step_id = event.get("uuid")
            parts = []
        elif event.get("type") == "content.part" and event.get("stepUuid") == step_id:
            part = event.get("part")
            if isinstance(part, dict) and part.get("type") == "text" and isinstance(part.get("text"), str):
                parts.append(part["text"])
        elif event.get("type") == "step.end" and event.get("uuid") == step_id:
            if event.get("finishReason") not in {"error", "interrupted"} and parts:
                message = "".join(parts)
            step_id = None
            parts = []
    return message


def normalize(payload: dict) -> dict:
    value = dict(payload)
    if "tool_output" in value:
        value.setdefault("tool_response", value["tool_output"])
    if "error" in value:
        value.setdefault("tool_response", value["error"])
    if "response" in value:
        value.setdefault("last_assistant_message", value["response"])
    if value.get("hook_event_name") == "Stop":
        session = _session_dir(str(value.get("session_id") or ""))
        transcript = session / "agents" / "main" / "wire.jsonl" if session else None
        if transcript and transcript.is_file():
            value.setdefault("transcript_path", str(transcript))
            if message := _last_assistant_message(transcript):
                value.setdefault("last_assistant_message", message)
    return value
```

Sources: [integrations/kimi-plugin/hooks/adapter.py:22-100](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/hooks/adapter.py#L22-L100)

The Cursor adapter translates camelCase native events into kebab-case actions, standardizing session identification across `conversation_id` and `parent_conversation_id`, mapping `workspace_roots` to working directories (`cwd`), and normalizing `tool_output` or `error_message` into `tool_response`.

```python
EVENTS = {
    "sessionStart": "session-start",
    "beforeSubmitPrompt": "user-prompt",
    "postToolUse": "post-tool",
    "postToolUseFailure": "post-tool-failure",
    "afterAgentResponse": "assistant-stop",
    "stop": "stop",
    "sessionEnd": "session-end",
    "preCompact": "pre-compact",
}


def normalize(payload: dict, event: str) -> dict:
    value = dict(payload)
    value.setdefault("session_id", value.get("conversation_id") or value.get("parent_conversation_id", ""))
    roots = value.get("workspace_roots") or []
    if roots:
        value.setdefault("cwd", roots[0])
    if "tool_output" in value:
        value.setdefault("tool_response", value["tool_output"])
    if "error_message" in value:
        value.setdefault("tool_response", value["error_message"])
    if "text" in value:
        value.setdefault("last_assistant_message", value["text"])
    if "summary" in value:
        value.setdefault("last_assistant_message", value["summary"])
    return {"action": EVENTS[event], "payload": value}
```

Sources: [integrations/cursor-plugin/hooks/adapter.py:25-51](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/hooks/adapter.py#L25-L51)

The Antigravity adapter reads structured step transcripts, filtering entries where `status` equals `DONE` and `type` is `USER_INPUT` or `PLANNER_RESPONSE`, extracting redacted content inside `<USER_REQUEST>` tags for prompts and plain text for assistant planner responses.

```python
def _subagent_start(store, hook_input):
    context = record_subagent_start(store, hook_input)
    if context:
        return {
            "hookSpecificOutput": {
                "hookEventName": "SubagentStart",
                "additionalContext": context,
            }
        }


def _subagent_stop(store, hook_input):
    record_subagent_stop(store, hook_input)


def _post_tool(store, payload):
    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/antigravity-plugin/hooks/adapter.py:28-48](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/hooks/adapter.py#L28-L48), [integrations/codex-plugin/hooks/adapter.py:27-56](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/codex-plugin/hooks/adapter.py#L27-L56)

> [!NOTE]
> The Codex adapter intercepts `post-tool` arguments, normalizing tool responses that return dictionaries with `exit_code`, `isError`, or `success` keys into explicit failure flags before passing payloads to `record_tool`.

Sources: [integrations/codex-plugin/hooks/adapter.py:42-56](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/codex-plugin/hooks/adapter.py#L42-L56)

### Event Routing and Field Mapping Matrix

| IDE Host | Native Event / Action | Normalized Target Action | Key Field Normalization |
| :--- | :--- | :--- | :--- |
| Kimi | `SessionStart` | `session-start` | Maps script argument directly |
| Kimi | `UserPromptSubmit` | `user-prompt` | Extracts additional context for injection |
| Kimi | `Stop` | `stop` | Resolves wire transcript for `last_assistant_message` |
| Cursor | `sessionStart` | `session-start` | Sets `session_id` from conversation identifiers |
| Cursor | `beforeSubmitPrompt` | `user-prompt` | Normalizes `workspace_roots[0]` to `cwd` |
| Cursor | `afterAgentResponse` | `assistant-stop` | Maps `text` or `summary` to `last_assistant_message` |
| Antigravity | `PreInvocation` | `session-start` / `user-prompt` | Parses `transcriptPath` for user prompt and assistant text |
| Antigravity | `PostToolUse` | `post-tool` / `post-tool-failure` | Unpacks `toolCall` name and args |
| Codex | `post-tool` | `codex-post-tool` | Evaluates response exit codes and error flags |

Sources: [integrations/kimi-plugin/hooks/adapter.py:22-100](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/hooks/adapter.py#L22-L100), [integrations/cursor-plugin/hooks/adapter.py:25-51](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/hooks/adapter.py#L25-L51), [integrations/antigravity-plugin/hooks/adapter.py:78-97](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/hooks/adapter.py#L78-L97), [integrations/codex-plugin/hooks/adapter.py:42-60](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/codex-plugin/hooks/adapter.py#L42-L60)

> [!WARNING]
> If an Antigravity payload lacks a valid working directory (`cwd`), the adapter bypasses runtime execution entirely, returning an empty JSON object or a default allow decision to prevent hook timeouts.

Sources: [integrations/antigravity-plugin/hooks/adapter.py:126-131](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/hooks/adapter.py#L126-L131)

## Shared Hook Orchestration Lifecycle

### Input Reading and Default Stops

The hook runner reads incoming JSON hook payloads directly from standard input via `read_hook_input()`, safely guarding against decoding errors or missing streams.

```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/cursor-plugin/core/hook_runner.py:44-50](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/core/hook_runner.py#L44-L50)

When recording agent stops without external transcript parsing, `default_record_stop()` extracts the session identifier, resolves the active repository, redacts the assistant response message, and records it to the evidence store.

```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/cursor-plugin/core/hook_runner.py:52-60](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/core/hook_runner.py#L52-L60)

### First-Prompt Memory Search

Before an agent processes the initial user prompt in a session, `first_prompt_memory_output()` executes a conditional search over historical repository memories.

```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,
    )
    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/cursor-plugin/core/hook_runner.py:62-94](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/core/hook_runner.py#L62-L94)

> [!NOTE]
> The first-prompt search strictly requires `is_first_prompt` to be true and enforces a minimum query length derived from `MEM0_CODE_MIN_QUERY_CHARS` (defaulting to 20 characters) before querying stored memories with a timeout of 2 seconds.

Sources: [integrations/cursor-plugin/core/hook_runner.py:64-76](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/core/hook_runner.py#L64-L76)

### Periodic Checkpoint Scheduling

Periodic flushing and background process handoffs coordinate asynchronous state persistence during active sessions.

```python
def schedule_periodic_checkpoint(
    store: EvidenceStore, hook_input: dict, repo, session_id: str,
) -> bool:
    if (
        not automatic_flush_enabled()
        or not api_key()
        or not store.checkpoint_due(repo.identity, session_id)
    ):
        return False
    if store.prepare_flush(repo, session_id, "periodic") is None:
        return False
    hand_off_flush(hook_input, "periodic")
    return True
```

Sources: [integrations/cursor-plugin/core/hook_runner.py:199-211](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/core/hook_runner.py#L199-L211)

> [!WARNING]
> Periodic checkpoint scheduling is conditional: it requires automatic flushing to be enabled via environment configuration, an active API key to be present, and a checkpoint to be explicitly due for the session identity.

Sources: [integrations/cursor-plugin/core/hook_runner.py:202-206](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/core/hook_runner.py#L202-L206)

### Lifecycle Configuration Constants

| Constant Name | Default Value | Purpose |
| :--- | :--- | :--- |
| `STALE_RUNNING_SECONDS` | `300` | Threshold in seconds before a running handoff lock is considered stale |
| `PENDING_EXPIRY_SECONDS` | `604800` (7 days) | Maximum retention age for pending handoff payload files |
| `PENDING_LAUNCH_LIMIT` | `5` | Maximum number of recovered handoffs launched in a single batch |
| `DEFAULT_IDLE_FLUSH_SECONDS` | `300` | Default idle duration required before triggering an automatic background flush |

Sources: [integrations/cursor-plugin/core/hook_runner.py:36-39](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/core/hook_runner.py#L36-L39)

## Host Identity and Bundle Generation

### Harness Identity Constants

Generated harness identity modules establish host-specific constants distinguishing IDE environments within shared agent core distributions. Each target plugin maintains an auto-generated configuration mapping its harness identifier, source tag, and platform application vocabulary.

| Plugin Integration | `HARNESS_ID` | `SOURCE_TAG` | `PLATFORM_SOURCE` | `PLATFORM_APPLICATION` |
| :--- | :--- | :--- | :--- | :--- |
| **Antigravity** | `"antigravity"` | `"ANTIGRAVITY_PLUGIN"` | `"MEM0_PLUGIN"` | `"antigravity"` |
| **Kimi** | `"kimi"` | `"KIMI_PLUGIN"` | `"MEM0_PLUGIN"` | `"kimi"` |
| **Cursor** | `"cursor"` | `"CURSOR_PLUGIN"` | `"MEM0_PLUGIN"` | `"cursor"` |

Sources: [integrations/antigravity-plugin/core/_harness_id.py:3-11](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/_harness_id.py#L3-L11), [integrations/kimi-plugin/core/_harness_id.py:3-11](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/core/_harness_id.py#L3-L11), [integrations/cursor-plugin/core/_harness_id.py:3-11](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/core/_harness_id.py#L3-L11)

> [!NOTE]
> The platform-side vocabulary treats the entire plugin family as a single source (`MEM0_PLUGIN`), while `PLATFORM_APPLICATION` identifies the specific editor host context. If the host application is unknown or empty, `memory_core` omits the application header entirely rather than transmitting a placeholder value.

Sources: [integrations/antigravity-plugin/core/_harness_id.py:6-11](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/_harness_id.py#L6-L11), [integrations/kimi-plugin/core/_harness_id.py:6-11](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/core/_harness_id.py#L6-L11), [integrations/cursor-plugin/core/_harness_id.py:6-11](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/core/_harness_id.py#L6-L11)

## Host Memory State and Ephemeral Storage

### API Key and Data Directory Resolution

Host plugins manage configuration state and caching via environment variables and local files. The API key caching subsystem inspects multiple fallback locations to locate credentials, and clears cached token files when all configuration sources are exhausted.

```python
"""Drop the cached key file once every configured key source is gone."""
configured = (
    os.environ.get("MEM0_API_KEY")
    or os.environ.get("PLUGIN_OPTION_API_KEY")
    or os.environ.get("CLAUDE_PLUGIN_OPTION_API_KEY")
    or os.environ.get("CLAUDE_PLUGIN_OPTION_MEM0_API_KEY")
    or ""
).strip()
if configured:
    return False
path = data_dir() / "api-key"
if not path.exists():
    return False
try:
    path.unlink()
except OSError:
    return False
return True
```

Sources: [integrations/codex-plugin/core/memory_core.py:431-448](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/codex-plugin/core/memory_core.py#L431-L448), [integrations/cursor-plugin/core/memory_core.py:431-448](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/core/memory_core.py#L431-L448), [integrations/antigravity-plugin/core/memory_core.py:431-448](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/memory_core.py#L431-L448), [integrations/kimi-plugin/core/memory_core.py:431-448](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/core/memory_core.py#L431-L448)

> [!WARNING]
> The credential removal utility `clear_stale_api_key_cache` verifies that every environment configuration vector (`MEM0_API_KEY`, `PLUGIN_OPTION_API_KEY`, `CLAUDE_PLUGIN_OPTION_API_KEY`, and `CLAUDE_PLUGIN_OPTION_MEM0_API_KEY`) is completely unset before unlinking the local cached `api-key` file.

Sources: [integrations/codex-plugin/core/memory_core.py:432-448](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/codex-plugin/core/memory_core.py#L432-L448), [integrations/cursor-plugin/core/memory_core.py:432-448](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/core/memory_core.py#L432-L448), [integrations/antigravity-plugin/core/memory_core.py:432-448](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/memory_core.py#L432-L448), [integrations/kimi-plugin/core/memory_core.py:432-448](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/core/memory_core.py#L432-L448)

Storage paths and scoping parameters rely on environment overrides with explicit fallback precedence.

| Option Function | Primary Variable | Fallback Variables | Default Value / Pathing |
| :--- | :--- | :--- | :--- |
| `data_dir()` | `MEM0_CODE_DATA_DIR` | `MEM0_PLUGIN_DATA_DIR`, `PLUGIN_DATA`, `CLAUDE_PLUGIN_DATA` | `~/.mem0/{_harness_data_dir_name}` |
| `user_id()` | `PLUGIN_OPTION_USER_ID`, `MEM0_CODE_USER_ID` | `MEM0_USER_ID`, `MEM0_RESOLVED_USER_ID`, `USER`, `USERNAME` | `"default"` |

Sources: [integrations/codex-plugin/core/memory_core.py:470-490](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/codex-plugin/core/memory_core.py#L470-L490), [integrations/cursor-plugin/core/memory_core.py:470-490](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/core/memory_core.py#L470-L490), [integrations/antigravity-plugin/core/memory_core.py:470-490](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/memory_core.py#L470-L490), [integrations/kimi-plugin/core/memory_core.py:470-490](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/core/memory_core.py#L470-L490)

### SQLite Evidence Store and Checkpointing

Local persistence relies on an SQLite-backed `EvidenceStore` configured with write-ahead logging and busy timeout protections.

```python
class EvidenceStore:
    def __init__(self, path: Path | None = None):
        directory = data_dir() if path is None else path.parent
        directory.mkdir(parents=True, exist_ok=True)
        self.path = path or directory / "evidence.sqlite3"
        try:
            self._open()
        except sqlite3.DatabaseError:
            self._quarantine()
            self._open()

    def _open(self) -> None:
        self.conn = sqlite3.connect(self.path, timeout=10)
        self.conn.row_factory = sqlite3.Row
        try:
            self.conn.execute("PRAGMA journal_mode=WAL")
            self.conn.execute("PRAGMA busy_timeout=10000")
            self._migrate()
        except sqlite3.DatabaseError:
            self.conn.close()
            raise
```

Sources: [integrations/codex-plugin/core/memory_core.py:555-575](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/codex-plugin/core/memory_core.py#L555-L575), [integrations/cursor-plugin/core/memory_core.py:555-575](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/core/memory_core.py#L555-L575), [integrations/antigravity-plugin/core/memory_core.py:555-575](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/memory_core.py#L555-L575), [integrations/kimi-plugin/core/memory_core.py:555-575](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/core/memory_core.py#L555-L575)

> [!NOTE]
> If a database error occurs during initialization, the `EvidenceStore` automatically quarantines the corrupted database file before attempting to recreate and reopen a clean SQLite store.

Sources: [integrations/codex-plugin/core/memory_core.py:560-564](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/codex-plugin/core/memory_core.py#L560-L564), [integrations/cursor-plugin/core/memory_core.py:560-564](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/core/memory_core.py#L560-L564), [integrations/antigravity-plugin/core/memory_core.py:560-564](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/memory_core.py#L560-L564), [integrations/kimi-plugin/core/memory_core.py:560-564](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/core/memory_core.py#L560-L564)

Transcript events are processed to extract checkpoint text payloads through `_checkpoint_message`, which handles user prompts, assistant stops, and subagent or sidekick termination messages while applying redaction.

```python
def _checkpoint_message(event: dict[str, Any]) -> str:
    kind = event.get("kind")
    payload = event.get("payload") or {}
    if kind == "user_prompt":
        return redact(payload.get("text", "")).strip()
    if kind == "assistant_stop":
        transcript_messages = payload.get("transcript_messages") or []
        if isinstance(transcript_messages, list):
            text = "\n".join(
                str(message.get("content") or "")
                for message in transcript_messages
                if isinstance(message, dict) and message.get("content")
            )
            if text:
                return text
        return redact(payload.get("text", "")).strip()
    if kind in {"subagent_stop", "sidekick_stop"}:
        return redact(payload.get("final_message", "")).strip()
    return ""
```

Sources: [integrations/codex-plugin/core/memory_core.py:509-527](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/codex-plugin/core/memory_core.py#L509-L527), [integrations/cursor-plugin/core/memory_core.py:509-527](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/cursor-plugin/core/memory_core.py#L509-L527), [integrations/antigravity-plugin/core/memory_core.py:509-527](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/antigravity-plugin/core/memory_core.py#L509-L527), [integrations/kimi-plugin/core/memory_core.py:509-527](https://github.com/blade47/mem0/blob/b7ad69afda6b6ed030347c66d48a13e4de9dec08/integrations/kimi-plugin/core/memory_core.py#L509-L527)

## Related

- [Python Agent Core](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/agent-plugins/python-agent-core)
- [Claude Code Plugin](https://www.doc0.dev/docs/faa36707-7c28-4f69-a18f-700ff61c704e/technical/agent-plugins/claude-code-plugin)


## Sitemap

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