Skip to main content
Add persistent memory to Codex with the Cognee memory plugin — no code and no pip install. It works in the Codex CLI and can also be activated through the Codex IDE plugin. The plugin hooks into Codex’s lifecycle, so it:
  • captures your prompts, tool traces, and assistant responses into session memory
  • injects relevant context on every prompt submit
  • syncs the session into your knowledge graph on session end
Sessions are disposable; your memory isn’t.

Install

The Cognee memory plugin depends on Codex lifecycle hooks. Enable hooks before installing it.
Enable hooks, then install from the Codex marketplace with the Codex CLI:
Make sure Cognee hooks are enabled for both the Codex CLI and the Codex IDE plugin. If Codex asks you to review hooks, open /hooks and allow or trust the Cognee hooks. Until hooks are enabled and trusted, Codex will not call the plugin on prompt submit, tool use, stop, compaction, or session end.
On startup the status line shows cognee: <dataset> · <mode> to confirm the plugin is active. While recall is auto (the default, see Sessions & datasets), it reads cognee: auto (<dataset>) · <mode>: the server picks which readable datasets each recall searches, and <dataset> is where saves go.

Configure your backend

Configure the plugin once in ~/.cognee/.env. The file is created with a commented template on the first session start, and its values act exactly like shell exports — a real export in your shell still wins, per terminal. It is shared with the Claude Code plugin, so both read the same configuration.
Point the plugin at Cognee Cloud or a remote server by setting both:
Cloud mode is a pure thin client: it talks to your remote server over HTTP only and does not install a local Cognee runtime.Pointing COGNEE_BASE_URL at a Cognee server you run yourself? Without COGNEE_API_KEY, the plugin logs in as COGNEE_USER_EMAIL / COGNEE_USER_PASSWORD (default default_user@example.com / default_password) to mint its key. From cognee 1.6.0, a server creates the default user only when it starts with DEFAULT_USER_PASSWORD set. So either start your server with DEFAULT_USER_PASSWORD set to the same value as COGNEE_USER_PASSWORD, or set COGNEE_API_KEY so no login is needed. Any other user must already exist on the server. If the login fails, the error message tells you which of these to fix.If that self-hosted server listens on a loopback address (127.0.0.1, localhost) — a docker compose stack or a systemd service, say — set COGNEE_MANAGED_ENDPOINT=true as well. By default, when a loopback URL is down at session start, the plugin boots its own embedded local server on that port. With the flag set, it never boots or installs anything over your deployment: SessionStart reports Cognee Memory OFFLINE instead, and recall and capture stay off for that session. Start the deployment, then start a new session.
Re-running any of these blocks is safe: when a key appears more than once the last value wins, so pasting again with a new value updates the setting. Editing the file directly (nano ~/.cognee/.env) works too. Either way, changes apply on the next launch. The file format:
  • Comments are whole lines starting with # — a trailing # note after a value becomes part of the value.
  • Quotes around values are optional, and a leading export is tolerated so existing shell profile lines paste verbatim.
  • Every variable in the Configuration Reference can live here, so there is nothing else to persist. The one exception is COGNEE_ENV_FILE itself: the plugin reads it before it opens the file, so it only works as a shell export.

Which mode wins, and how to switch

You can configure both modes at once — keep COGNEE_BASE_URL + COGNEE_API_KEY and LLM_API_KEY in the file together. The mode is then decided per terminal, by three rules in order:
  1. A COGNEE_BACKEND export wins. export COGNEE_BACKEND=local (or =cloud) pins that terminal to that mode.
  2. Otherwise, cloud wins when configured. If COGNEE_BASE_URL is set — in the file or the shell — the plugin connects to it.
  3. Otherwise, local. With no URL anywhere, the plugin boots the local server.
So with both modes in ~/.cognee/.env:
To go local, use the switch — not unset COGNEE_BASE_URL. Unsetting does not work: the env file re-injects the URL at the next launch. Export COGNEE_BACKEND=local instead. (Deleting the line from the file works too, but that changes the default for every terminal.)
Details worth knowing:
  • The switch is pinned. COGNEE_BACKEND=cloud with no COGNEE_BASE_URL configured still counts as cloud — the plugin does not silently fall back to local, and the status line shows ✕ (missing_cognee_base_url) so you know exactly what to fix.
  • A forced-local switch blanks COGNEE_BASE_URL and COGNEE_API_KEY in the process environment, so the per-prompt hooks and every spawned worker resolve the same local endpoint — not just SessionStart. They are emptied rather than deleted on purpose: a child process that reloads the env file must not re-inject the cloud values.
  • The shared COGNEE_BACKEND flips every Cognee plugin in that terminal: Claude Code, Codex and Cursor (Cursor’s hooks read it from ~/.cognee/.env, not from a terminal export, unless Cursor was started from that terminal). To flip only one, use the plugin-specific name — COGNEE_CLAUDE_BACKEND, COGNEE_CODEX_BACKEND or COGNEE_CURSOR_BACKEND — which beats the shared one.
  • Accepted values: local (aliases native, sdk) and cloud (aliases http, api, server). Anything else is ignored.
  • COGNEE_BACKEND can also live in ~/.cognee/.env to make a mode the durable default; a shell export still overrides it per terminal.
  • Not sure what a terminal resolved? The status line’s mode field shows it live.
Your Claude Code or Codex plan pays only for your conversation with the model. Cognee’s own LLM calls (cognify, summaries, embeddings, search completions) run in the Cognee backend and are billed by the provider configured there. In local mode that is your LLM_API_KEY; in Cloud/remote mode it is your tenant’s key. The one exception is Claude Code’s local mode with no LLM key, which uses your Claude subscription through the Claude observer.
In local mode, the single LLM_API_KEY above covers extraction, summarization, and embeddings: Cognee defaults to openai/gpt-5.6-luna for the LLM and openai/text-embedding-3-large for embeddings, and embeddings reuse LLM_API_KEY when EMBEDDING_API_KEY is unset. To use another provider, set LLM_PROVIDER, LLM_MODEL, and — for Azure, Ollama, or OpenAI-compatible endpoints — LLM_ENDPOINT. Changing only the LLM leaves embeddings on OpenAI, so also set the EMBEDDING_* variables or set EMBEDDING_API_KEY to an OpenAI key so the default embeddings keep working. See LLM providers and embedding providers.

Use it

Use Codex as usual — memory is captured and recalled automatically. To verify, end a session with /exit (which syncs it into Cognee), then start a fresh session and ask: “What do you know from cognee?” Answering from a clean session proves it’s recalling from your memory. The plugin also ships skills for explicit requests. Ask for what you want and Codex picks the matching skill:

Recaps: standup, digest, timeline

The three recap skills answer “what did I work on?” from what the server already records, with no extra hooks and no LLM call on the server. Ask for a standup (“what did I work on yesterday?”), a weekly digest, or the history of a topic (“how did dataset switching evolve?”). Each runs the plugin’s scripts/cognee-recap.py wrapper, which prints a Markdown outline that Codex then summarises for you. Codex treats that outline as recorded data, not as instructions. Every coding-agent session this identity can see is in scope, Codex, Claude Code, Cursor, and Antigravity alike. Under shared agent memory (the default) a standup asked from Codex also covers that day’s Claude Code sessions, and it can include other people’s prompts too. You can also run the wrapper yourself:
CODEX_PLUGIN_ROOT is set for the plugin’s skills; from your own terminal, substitute the plugin’s install path.
  • --since takes 24h, 7d, 2w, today, yesterday, week (since Monday), month (the last 30 days), all, or a date (a local calendar day). Sessions are picked by last activity.
  • --projects <substr>,… keeps sessions whose working directory matches. --all-sessions adds sessions that are not from a coding agent, such as MCP clients and scheduled jobs.
  • --max-sessions (default 25) caps the sessions fetched, newest first, before --projects filters them. --max-learnings (default 40, 0 = all) caps the lessons the digest fetches and lists.
  • --session-key <host session id> picks the right launch record, and so the right dataset, when several Codex sessions share a directory. Without it the wrapper finds the launch record itself, and falls back to COGNEE_PLUGIN_DATASET or agent_sessions only when no single record matches.
  • --json returns the data instead of the Markdown outline.
Lessons appear only once a session has been synced into the graph, so the current session’s lessons show up in the digest and timeline after its next sync: the every-N automatic improve, a manual python3 "${CODEX_PLUGIN_ROOT}/scripts/sync-session-to-graph.py", or the final sync on exit. If the server is unreachable, returns an HTTP error (for 401/403, check COGNEE_API_KEY or the plugin identity), or refuses the identity, the wrapper prints one line to stderr and exits 1. Run python3 "${CODEX_PLUGIN_ROOT}/scripts/doctor.py" --json to check the mode and URL. If only the timeline’s graph lookup fails, the wrapper still prints the asked events and exits 0.

Sessions & datasets

  • Sessions — by default the plugin derives the Cognee session id from the Codex thread, so a new conversation starts a new one and codex resume continues the same one automatically. (Should a launch report no thread id at all, the plugin falls back to a fresh per-launch id.) Set COGNEE_SESSION_ID before launching to pin a named session, or to deliberately share one live session across two terminals. Whatever you pin is normalized to the ASCII set A-Z a-z 0-9 - _ ., with every other character — accented and non-Latin letters included — replaced by _; leading and trailing . and _ are then trimmed, and the result is capped at 120 characters. So proyecto-café pins proyecto-caf, two ids that first differ past character 120 land on one session, and a value with no ASCII alphanumerics at all normalizes to nothing and is ignored, leaving the thread-derived id in place. Every Cognee agent integration applies that same rule, so one value names the same session in Codex as it does in Claude Code.
  • Datasets — all writes go to one dataset, the active one (agent_sessions by default). Set COGNEE_PLUGIN_DATASET to use a custom one. The Codex and Claude Code plugins default to the same one, so memory carries across both. COGNEE_PLUGIN_DATASET does not restrict recall, which is auto by default (see Auto recall). auto is reserved: COGNEE_PLUGIN_DATASET=auto means the default dataset. A dataset that does not exist yet — on a new account, the default one — is created at session start, before the session is registered on it.
Sharing a dataset takes more than agreeing on a name. Where the server supports it, each plugin authenticates as its own agent sub-user, and grants only flow child to parent: your user sees what an agent writes, but an agent sees nothing your user or another plugin’s agent owns. Shared agent memory — on by default — closes that gap. Every plugin agent of your user joins one cognee-agent role holding read and write on your datasets, and the launch’s dataset is addressed by its canonical UUID rather than by name, which is what makes “the same dataset” mean the same rows in Codex and in Claude Code. Grants are backfilled at every session start and roughly every 60 seconds by the background watcher, so a dataset another plugin creates becomes visible here without a restart. Set COGNEE_SHARED_AGENT_MEMORY=false for separated, per-plugin memory: the agent leaves the shared role and starts on a private dataset, and nothing already written moves. That rests on there being an agent identity to separate, though — under the default COGNEE_PLUGIN_IDENTITY=auto one is provisioned only in service of shared memory, so opting out on a fresh install simply runs the plugin as your own user, which sees everything anyway. Pair it with COGNEE_PLUGIN_IDENTITY=true to insist on a dedicated agent sub-user. Keys and multiple machines. Every machine the plugin runs on holds its own key for the plugin’s agent, so a new machine never logs another one out. The plugin provisions an agent key only against a server whose provision route adds keys and revokes none — one that advertises x-cognee-additive-plugin-keys in its OpenAPI spec. Against an older server, where provisioning would revoke every other machine’s key, the plugin runs as your own user under auto and stops with an error under COGNEE_PLUGIN_IDENTITY=true. A key the server rejects with 401 (revoked from the dashboard, or pruned server-side) is replaced in the same session start. A refused registration (403) keeps the key for the next session start, but that launch runs as your own user under auto and stops with an error under true. When the 403 names the dataset rather than the key, the error says the server refused the dataset, and the status line does not report it as an auth failure. When the agent already holds the maximum number of keys in use, the server refuses this machine a key (409) and the plugin runs on your personal key under auto; under true the session start stops with an error instead. Memory still works, but what this machine stores is attributed to you rather than to Codex. Every session start then warns about it, and the doctor’s Memory Sharing line reports the key limit, until the machine gets its own key. To free a slot, ask Codex to manage its Cognee keys (the cognee-keys skill). It lists the agent’s keys most recently used first, with this machine’s marked, and revokes the one you pick. Under the hood it runs python3 "${CODEX_PLUGIN_ROOT}/scripts/plugin-keys.py" list and revoke <id>, authenticating as your user rather than as the agent. --all lists every plugin agent of your user, and revoking this machine’s own key needs --force. Start a new session afterwards to get the key. To move a running session to another dataset, ask Codex to switch datasets (the cognee-switch-datasets skill), optionally naming the dataset. Without a name it lists the datasets you can write to as a numbered list; a name that is not listed is created for you. Because a Cognee session never spans two datasets, the switch first syncs the current session into its dataset — and aborts if that fails, changing nothing — then registers a fresh session on the chosen one. The choice lives in the launch record, so it survives a resume and beats COGNEE_PLUGIN_DATASET for the rest of the launch — as does the session it registers, which outranks a pinned COGNEE_SESSION_ID so a shell export cannot drag a switched launch back. Switching to a dataset also restricts recall to it (see below). Before 1.7.9, switching under shared memory to a dataset another account owns could create an empty dataset named after that dataset’s UUID; delete any such leftover.

Auto recall and restricting it to one dataset

Saves always go to the active dataset, but recall is auto by default: each prompt’s graph recall, and a cognee-search that names no dataset, sends no dataset, and the server picks the readable dataset(s) most likely to hold the answer (dataset routing). Routing needs a cognee 1.6.6 server; an older one searches every readable dataset. Routing is best effort: when the server cannot narrow the query (backend access control off, fewer than two readable datasets, nothing close enough to the query, or a vector database without routing support), it searches every readable dataset instead. A dataset that has not been through improve on 1.6.6 or later has no routing index: a 1.6.6 or 1.6.7 server always searches it, while a newer server searches it only when no dataset is picked or the picked dataset returns nothing. When the picked dataset returns nothing the server widens to the rest. Switching to a dataset restricts recall to it. Ask Codex to pin recall (“only recall from this dataset”) to restrict it to the active dataset, or to set recall back to auto (“let Cognee pick the dataset again”) to lift the restriction. Pinning and going back to auto leave saves where they are and start no new session. Under the hood, the skill runs:
COGNEE_PLUGIN_RECALL_MODE (exported or in ~/.cognee/.env) sets the mode every session starts in: auto (the default) or pinned (recall restricted to the session’s dataset). Any other value means auto. A choice made in the session (a switch, pin, or auto) wins over it for that session, and a session switched before this mode existed stays restricted. Because COGNEE_PLUGIN_DATASET no longer restricts recall on its own, set COGNEE_PLUGIN_RECALL_MODE=pinned to keep recall on a configured dataset. Session start says so once per machine and dataset when a non-default COGNEE_PLUGIN_DATASET is configured and COGNEE_PLUGIN_RECALL_MODE was never set. A COGNEE_PLUGIN_READ_DATASET_IDS read set also restricts recall, and auto cannot lift it. switch-dataset.py --list prints the mode in effect and why. The status line shows only whether recall is auto. Session history and guidance (bound to the active dataset) and the code lane (this repository’s code dataset) always keep their explicit dataset. So does each prompt’s graph recall in a session that names a project with COGNEE_PROJECT_NODE_SET (see Project-scoped recall): it stays on the active dataset even under auto recall. When the server is unreachable, cognee-search falls back to the local cognee-cli, which sends no dataset under auto recall and the active dataset when recall is restricted.

Searching one dataset without switching

A switch is not needed just to look in another dataset. A one-off, graph-only search of one named dataset leaves the active dataset, the Cognee session, the recall mode, and where writes go as they were. It runs without the session id, which belongs to the active dataset, and Codex says which dataset the answer came from. While recall is restricted, the memory skill offers this when the search comes back empty: it lists the other datasets your identity can read (read-only ones included, since a search needs no write access) as a numbered list, and searches the one you pick:
While recall is restricted, the wrapper searches a dataset other than the active one only with --user-picked, which marks it as your choice: a dataset you named or picked when Codex asked. Without the flag it refuses and sends no request, so the agent cannot leave a pinned dataset for an id it found in memory. Under auto recall the flag is not needed. If the server rejects the search with an HTTP error (other than an auth failure, or a 404, which reads as an empty result), the error Codex reports says which dataset id it searched and adds the server’s own reason from the response, instead of a bare HTTP status. Recall no longer appends a list of the other datasets you can read to the prompt context. The settings that tuned that list (COGNEE_RECALL_DATASET_HINT, COGNEE_DATASETS_CACHE_TTL, COGNEE_DATASETS_CACHE_RETRY) and its readable-datasets.json cache are gone.

Project-scoped recall

When a session names a project, each prompt’s graph recall is limited to that project’s node set plus the shared sets in COGNEE_RECALL_SHARED_NODE_SETS (default global,user_context), so other projects’ documents and sessions in the same dataset stop crowding the hits. user_context is where cognee-remember saves facts and preferences about you, so those stay recallable in every project. COGNEE_PROJECT_NODE_SET names the project and also tags captured Q&A and traces with it; that needs a Cognee server 1.6.5 or newer, whose Q&A and trace entries accept a node_set (the bundled local server qualifies) — on an older one, the session’s captures stay queued. When the plugin’s check for that support gets no answer (a timeout, a connection error, a reply that is not JSON, HTTP 408, 429, or 5xx), captures stay queued and the next hook that writes, drains, or recalls checks again after 30 seconds, 2 minutes, then every 10 minutes; nothing is sent untagged, and session start says once that the session’s memory is queued. COGNEE_RECALL_PROJECT_NODE_SET names it for recall only, which, unlike capture tagging, works on any server. COGNEE_PROJECT_NODE_SET wins when both are set. Session, trace, and code recall are not filtered. Set COGNEE_RECALL_PROJECT_SCOPE=false to keep tagging capture while leaving recall unfiltered.

How It Works

The plugin registers Codex lifecycle hooks: A background watcher runs the update and LLM-key checks and refreshes shared agent memory, and a final sync on session end bridges the session into the permanent graph.

What gets captured

Automatic capture is what fills session memory: your prompts, the tool calls Codex makes, and its answers. Explicit requests — the memory skill, or cognee-remember.sh — are independent of COGNEE_CAPTURE and the tool filters below and always store what you asked for, though their text is redacted the same way. Private spans. Text you wrap in <private>…</private> is replaced with [private] before the plugin logs it, stores it, writes it to a file, or sends it as a query, for automatic capture and explicit remember alike; the text around it is kept. No setting turns this off, COGNEE_CAPTURE_REDACT=false included. Spans don’t nest (each opener ends at the next close), a span may cross lines, and an opener with no close is left as plain text. Codex still sees the span in the live turn, and a file uploaded with --file is sent unchanged. Two filters run before a captured value is stored:
  • Credential paths are skipped entirely. A tool call whose path arguments — or, for a shell command, any of its words — point at .env, .env.*, *.pem, *.key, *.p12, *.pfx, id_rsa*, id_ed25519*, .netrc, .npmrc, */.ssh/*, */.aws/credentials, secrets.*, or credentials.* is not captured at all, so cat ~/.aws/credentials, less "$HOME/.ssh/id_rsa", source .env.local, and grep KEY .env are skipped too. The shell check splits the command into words rather than running it: quotes are removed, $HOME / ${HOME} read as ~, and words are cut at |, ;, &, <, >, parentheses, and backticks. A path reached through another variable or a glob (cat ~/.aws/cred*) is not recognised. Extend the list with COGNEE_CAPTURE_DENY_PATHS.
  • Common secrets are redacted. Private key blocks, database connection URLs, Authorization: Bearer … headers, secret / token / password / api_key assignments, vendor key prefixes (sk-, ghp_, xoxb-, whsec_, npm_, pypi-, and similar), AWS access key ids (AKIA…, ASIA…, …), and bcrypt hashes become [redacted:<kind>]. A value under a credential-looking key — authorization, x-api-key, _auth, or any key ending in secret, secret_key, secret_access_key, access_key_id, token, session_token, auth_token, password, or api_key, which covers aws_secret_access_key, SecretAccessKey / SessionToken in AWS JSON, and .npmrc’s _authToken — is replaced wholesale with [redacted:credential] rather than pattern-matched. This runs on prompts, traces, and answers alike, and since 1.7.8 on the queries recall and search send, and it runs before truncation — so a clipped secret cannot slip past the pattern that would have matched it.
Two kinds of shell call are left out of the tool traces as noise. Commands that actually run the plugin or the Cognee CLI (its scripts, cognee-cli, or anything under the plugin’s install path) are skipped so the plugin does not record itself. A command that only mentions “cognee”, such as work inside a Cognee repository, is captured. Successful read-only lookups (ls, cat, grep, rg, git log, git status, gh pr view, and similar) are skipped too. Failures, builds, tests, edits, and any command with a pipe, redirect, or substitution are kept. hook.log records each skip without the command text. COGNEE_CAPTURE=false stops automatic capture altogether: nothing is captured, buffered, or replayed, while recall and explicit remember keep working. It does not erase memory already stored. COGNEE_CAPTURE_TOOLS narrows which tool calls are captured. It takes a pipe-separated list of tool names, globs, or Tool(prefix:*) command matchers. A matcher selects a call whose command starts with that word, so Bash(git:*) keeps git status but drops rg foo and gitx. Only the first word of a compound command such as cd x && git status is checked. COGNEE_CAPTURE_DENY_TOOLS uses the same syntax and drops matching calls after the allowlist has run, so COGNEE_CAPTURE_DENY_TOOLS="Bash(rg:*)|Bash(grep:*)" captures everything except those searches. An entry the plugin cannot parse, such as Bash(rg) or Bash(), does not quietly match nothing. Tool calls stop being captured, buffered replay stops at the first buffered tool call and leaves the rest in the buffer, and every prompt shows Cognee: automatic capture is off until this is fixed: … until you correct the value. Despite that wording, prompts and answers are still captured.
Redaction is best effort, and the path filter only matches file paths a tool call names — a secret typed into a command line is still captured unless a redaction rule catches it. COGNEE_CAPTURE=false is the strict opt-out.

Session distillation (self-improvement)

The Cognee coding-agent plugins (Claude Code, Codex) run session distillation for you — you never call improve() by hand. A distillation pass fires on two automatic triggers: The every-N trigger has a per-session cooldown: after an improve, the next automatic one waits at least COGNEE_IMPROVE_COOLDOWN seconds (default 86400, 24 hours), and a failed attempt starts the same wait as a backoff. Session end, the sync skill, and a dataset switch improve regardless of it. Going idle does not trigger an improve.
Overlapping triggers are safe. A per-session improve lock on the server serializes concurrent runs, and unchanged session content dedups server-side by content hash — so a repeat improve over content that hasn’t changed is a cheap no-op, not duplicated work.

Configuration

All triggers are tuned through environment variables read by the plugin. The defaults are chosen so distillation stays out of your way; you rarely need to change them.
The plugin READMEs document additional advanced knobs — timing (poll deadlines, busy-retry intervals for a held session lock), session-sync retries, and the update-notification variables (COGNEE_UPDATE_CHECK, COGNEE_UPDATE_CHECK_INTERVAL). You almost never need them — reach for the table above first.

Turning it down or off

  • Reduce mid-session improves: raise COGNEE_IMPROVE_COOLDOWN, or set COGNEE_AUTO_IMPROVE_EVERY=0 to turn the every-N trigger off.
  • Session-end distillation always runs when the plugin is active — it’s how a finished session reaches permanent memory.

Confirming it happened

  • Cloud UI: the Self-improvement card at the top of a session on the Sessions page shows the status of the last graph enrichment and the dataset it wrote to.
  • Plugin hook log: each automatic run emits an improve_fired event you can grep for when debugging (in local SDK mode, where the plugin calls the library directly instead of the HTTP endpoint, look for auto_improve_fired instead).
  • improve-unsupported.json marker: if this file appears in the plugin’s shared state directory (24h TTL), the server rejected the improve endpoint and the plugin fell back to the legacy remember bridge for that window — a signal the server predates session-aware improve.
Codex differs from the shared reference above. The Codex plugin has no idle trigger: its background watcher never runs an improve, and COGNEE_IDLE_THRESHOLD is not read. Mid-session improves come only from the every-N trigger, and COGNEE_IMPROVE_COOLDOWN defaults to 86400 (24 hours). COGNEE_IDLE_DISABLED=1 still stops the watcher, which now only runs the update and LLM-key checks and the shared-memory refresh.

Coding memory (opt-in)

COGNEE_CODING_MEMORY=true (in the environment or ~/.cognee/.env) switches the plugin to a mode built for the server’s coding distillation (DISTILLATION_MODE=coding). With the setting unset, nothing in this section applies.
  • Nothing happens while the agent works. No trace per tool call, no recall per prompt, no pre-compact anchor, no code-graph indexing, and no improve every 150 entries.
  • Each request is sent once, at its end. The Stop hook (send-unit.py) reads the transcript and queues every tool call not sent yet as a trace entry (failed calls, reads, and ls included, redacted, and filtered only by COGNEE_CAPTURE_TOOLS, COGNEE_CAPTURE_DENY_TOOLS, the credential paths, and the plugin’s own commands), then one qa entry with the request, the final message, and the repository and git context. Commands are sent as Bash calls and file changes as Write (a new file) or Edit; Codex subagent threads are not read yet. Entries go through the existing replay buffer, so nothing is lost while the server is down. improve runs at session end and at most once a day in long sessions (COGNEE_IMPROVE_COOLDOWN).
  • One memory block per session. The first prompt of a session, and the first one after a compaction, gets a block of about 2,000 characters: the newest test baseline, the two newest pitfalls, the lessons recalled for the request (each tagged with its session’s date and commit, and the files it names that changed since), and the recent commits. Only this repository’s lessons are used. A session without lessons gets no block.
  • Server. Coding mode needs a cognee server with event-time support for QA and trace entries, which no released cognee version has yet (the bundled 1.6.6 included); on a server without it memory is off (nothing is captured or injected) and session start says so. Coding mode sets COGNEE_PROJECT_NODE_SET=auto when it is not already set. When the plugin starts the local server it also sets DISTILLATION_MODE=coding and AUTO_FEEDBACK=false, each only when not already set. A local server that is already running keeps the mode it started with; when that differs from the session’s, session start says so with the server’s pid and how to restart it, and the plugin never restarts a shared server itself. For a remote server, set DISTILLATION_MODE=coding there. The coding distillation deletes the lessons a newer one replaces, so the key needs delete on the dataset as well as write; under shared agent memory the plugin grants that delete to its own agent, only on the dataset it writes to.
  • Failures fail open: a slow, unreachable, or keyless server costs the block or delays the send, never the session. doctor shows the server support, whether the running local server is in coding mode, and the delete permission while coding mode is on.

Code graph

Repositories can be indexed into a deterministic code graph — symbols, calls, imports, endpoints, dependencies. Indexing makes no LLM or embedding calls by default, so it is fast and costs no tokens. It requires a Cognee server ≥ 1.5.4. Starting a session inside a git repository indexes it automatically — but only when the server is local, so a private checkout is never shipped to a hosted tenant on the plugin’s own initiative. Auto-indexing runs in the background, never blocks the first prompt, and re-indexes after any turn that changed the working tree. Index explicitly when automation won’t: against a Cloud tenant, for a different repo, or for a git URL. The simplest route is to ask the agent to index the repository — its code skill (/cognee-memory:cognee-code in Claude Code, codebase in Codex) runs this command:
The plugin-root variable is expanded only inside the plugin’s skills, so from your own terminal substitute the plugin’s install path. An explicit request is its own consent, so it bypasses every auto-index gate below. Add --index-vectors to also embed the extracted code facts so semantic search can see them — that is the one flag that makes embedding calls. Query the graph through the same code skill. Prompts that mention an identifier-shaped token from an indexed repo also get code facts injected automatically by the per-prompt recall hook. Each indexed repository gets its own dataset, named codebase-<repo-name>-<digest>, where the digest identifies the indexed path or git URL — two checkouts that share a basename would otherwise share one graph and delete each other’s nodes. Code-graph searches resolve the dataset from the current checkout, so the generated name rarely needs typing. Indexing writes its snapshot into the repository itself at <repo>/.enola/ (untracked) — add .enola/ to the repository’s .gitignore or your global excludes.
Freshness depends on where the server runs. A local server indexes the repository path on this machine, so the graph reflects your working tree, including uncommitted and untracked changes. A cloud or remote server clones a git URL — it cannot read your disk, so the graph reflects only the last pushed commit, and the plugin does not re-submit URL-indexed repositories after local edits. The output looks identical either way, so push before relying on code answers about work in progress, or use a local server for branches you are actively editing.
always also accepts 1, true, yes, and on; off also accepts 0, false, and no. Any other value means auto. Automatic indexing skips directories that are not git repositories, hold no source files, or exceed 3000 source files. Explicit indexing has no size cap.

Memory credit in answers

On by default; set COGNEE_PLUGIN_CITE_MEMORY=off to turn it off. An answer that used recalled memory ends with a two-line credit led by Cognee’s logotype in braille, each row as inline code so it stands apart from the quote, naming the dataset the memory came from:
In the ChatGPT app (a session whose transcript says originator: Codex Desktop), the credit is instead one line led by Cognee’s waveform icon, drawn in KaTeX in brand Lavender (#BC9BFF), followed by <what>, from the <dataset> dataset. The app shows the icon’s source until the closing $ streams in.
  • The recall hook marks every injected graph item with the dataset the server says it came from, [dataset <name>]. Under auto recall that mark is the only place the answer learns which dataset the server picked. A graph item from a server that names no dataset, and anything else, is marked with its origin, such as an agent trace or an earlier session turn.
  • On turns where memory was injected, an instruction above it tells Codex how many memories Cognee found and asks for the credit only when the answer used a marked item, naming its dataset, and never for what it knows from AGENTS.md, the code, or the conversation. The first credit in a session also says briefly how it connects to the earlier work.
  • The Stop hook reads only the credit at the end of the answer and logs a memory_credit event to hook.log with injected (this turn injected memory) and credited (the answer ends with the credit), plus logo_exact for the ChatGPT app’s icon. Only a credit on an injected turn is counted. Nothing is sent anywhere.
  • The credit state lives in ~/.cognee-plugin/codex/sessions/<session>/, which the state sweep removes after a week idle.
With the same setting on, resuming a chat shows once, in the terminal and to Codex, how its last sync went, naming the dataset it was saved to:
It appears only on resume; a new chat shows nothing. It says “sent” rather than “saved” because the submit runs in the background. On older servers, a sync skipped because another improve held the lock also looks the same; current servers queue that improve behind the running one instead of skipping it.

Debugging & Resuming Sessions

Hooks are callbacks from Codex, not a durable job queue: whatever happens while hooks are disabled or untrusted never reaches the plugin at all, and nothing replays it later. A backend the plugin cannot reach is the milder case — tool traces and answers are buffered on disk instead and replayed on a later prompt, so an outage costs you visibility rather than memory. The memory header tells the two apart. When memory does not appear, check these layers first: A resume keeps both the session and any dataset you switched to on its own. COGNEE_SESSION_ID is what you need for the other case: making a second terminal, or a different conversation, write into one shared live session. Set it in both terminals and keep COGNEE_PLUGIN_DATASET the same, otherwise each lands in its own session. After changing hook trust, credentials, dataset, or session id, restart Codex so SessionStart can run with the new state. Hook commands run with python3, falling back to python. On Windows, Codex tries py -3 and then python instead, because python3 there is usually the Microsoft Store stub. Any Python 3.9 or newer works: the hooks are stdlib-only and never import cognee, so the 3.9 in macOS’s Command Line Tools is enough.
If none of those interpreters resolves on PATH, every hook fails with a “hook failure” error and no session is created. Check with python3 --version (on Windows, py -3 --version) in the shell that launches Codex, or reinstall Python with “Add python.exe to PATH” checked.
Local mode additionally needs a Python 3.10–3.14 installed on the machine, because the plugin builds its own virtualenv for the Cognee server under ~/.cognee-plugin/venv and installs only the pinned cognee package into it. The plugin never downloads an installer or an interpreter. If uv is already on PATH, or left in ~/.cognee-plugin/uv by an earlier version, the plugin uses it with interpreter downloads disabled. Otherwise, or when uv finds no suitable interpreter, it uses the stdlib venv with the Python running the hook if that is in range, else the first of python3.14 … python3.10, python3, python on PATH that is. With no such interpreter, once a bootstrap attempt has failed, every later session start adds “Cognee Memory: cannot start the local Cognee server. Cognee requires Python 3.10 or newer (up to 3.14), and no such interpreter is installed on this machine…” to its message, the status line shows ✕ (cognee_needs_python_3_10), and hook.log records host_python_too_old_for_venv. Install one (python.org, Homebrew, pyenv, or uv python install 3.12) and start a new session; there is nothing else to configure. Cloud mode has no such requirement. Every hook runs through the plugin’s scripts/hook_runner.py, so a hook that crashes — or runs on a Python older than 3.9 — is reported rather than failing silently. The traceback is appended to ~/.cognee-plugin/codex/hook-crash.log, Codex shows a Cognee memory: hook <script> failed (…) message that points at that file (once per hour for the same crash, so a failing PostToolUse hook doesn’t repeat it on every tool call), and the hook exits cleanly instead of being run a second time. Memory for that hook is skipped until the cause is fixed, so check hook-crash.log first when that message appears. The plugin also ships a read-only diagnostic: "${CODEX_PLUGIN_ROOT}/scripts/cognee-cli.sh" doctor — ask Codex to run it, or substitute the plugin’s install path and run it from your own terminal (add --json for machine-readable output). It reports the resolved mode and which backend switch forced it, the env file with the key names it defines and any a shell export is shadowing, the server URL and whether it answers, where the API key came from, whether memory is shared or separated, the Python the local Cognee server runs on (Runtime Python — the plugin venv’s interpreter once it is built, before that the newest 3.10–3.14 found on PATH, or MISSING when there is none or none was found at the last session start; cloud mode reports it as not needed), the local and server cognee versions, the embedding model, and the circuit-breaker state. It never writes anything, and it needs no Cognee checkout — unlike the rest of cognee-cli.sh, the doctor subcommand runs from wherever you happen to be. Exit Codex normally (for example with /exit) when you want SessionEnd to trigger the final graph sync. If the process is killed instead, the detached exit watcher that SessionStart left running is the fallback: it follows the Codex session process itself, and starts the sync once that process is gone. The two share one marker per exit, keyed on the session and how much it has stored, so an exit syncs once. A resumed chat that is left again after new turns syncs again, an exit with nothing new is skipped, and a final sync whose every attempt failed releases its marker so the next exit retries.

Reading the memory header

Every turn shows a one-line header, which is the quickest read on whether memory is working. You see it, with the status line above it, under the answer and its memory credit: the recall hook keeps it and the Stop hook prints it once the answer is done. A recall that was skipped (server down) still says so above the answer, and a turn you interrupt prints nothing for it. The same header opens the recalled context Codex receives, except on a turn where recall found nothing: then it is shown to you only.
1 memory hit is how many memory blocks this turn’s lookup found and injected. On a prompt that arms the repository code lane — an identifier-shaped token in the prompt, and a working directory inside a repo you indexed — N code facts follows the hit count, and those facts are part of the total. 12/40 turns had hits this session is the running ratio, reading memory warming up (7 turns) until the first hit. saved last turn counts what the previous turn wrote, though not all of it is a server write: 1 prompt means the prompt was recorded locally, to be sent paired with that turn’s answer, while the trace and answer counts are server writes only. The same numbers are written to ~/.cognee-plugin/codex/last_recall.json. Each prompt makes two recall requests in parallel, plus a third when the code lane fires. The graph-scope recall is sent without the session id, so it brings back no conversation history — Codex already has that in its context. Against cognee 1.6.0 or later, the plugin keeps only the retrieved context from each dataset’s result and shrinks it before injecting it under === Cognee memory ===. Passages that are this session’s own synced turns are dropped. Each remaining passage is capped at COGNEE_RECALL_PASSAGE_CHARS, cut at a paragraph break and marked with how much was cut. Entity lines that only state a type or a node set are pruned. If the block is still over COGNEE_RECALL_CONTEXT_CHARS, passages are trimmed from the end; entities and facts never are. A result the plugin cannot parse, such as an older server’s bare context, is injected whole. The second request, with the session id, fetches the server-rendered session guidance, which is injected under === Active agent guidance ===. Code facts are injected one line per symbol (name [kind] file:line, followed by its relations), and only when the lane found any. When the server cannot be reached, the header grows the outage instead of going quiet:
A buffered trace or answer is never counted as saved. Buffered entries sit in ~/.cognee-plugin/codex/bridge/ and replay in order on a later prompt once the server answers again, so awaiting replay falling to zero is what confirms the backlog cleared. oldest is worth a look: a buffer belonging to an earlier session only drains when that session runs again, so entries can wait weeks with nothing pointing at them. Both segments disappear once the buffer has drained.

Configuration Reference

Precedence:
  1. Environment variables (shell exports)
  2. ~/.cognee/.env — the one-time setup file, shared with the Claude Code plugin; loaded into the environment at process start, so every variable below except COGNEE_ENV_FILE itself can live in it
  3. Defaults
The COGNEE_BACKEND / COGNEE_CODEX_BACKEND mode switch follows the same precedence; its effect is described in Which mode wins.
There is no config.json. Older plugin versions wrote ~/.cognee-plugin/config.json, and SessionStart read a base_url from it while the per-turn hooks did not — so a stale URL there could point the two halves of the plugin at different servers. SessionStart now deletes a leftover file. Put everything in ~/.cognee/.env instead.

Update or Remove

The cognee marketplace tracks the repository’s main branch, so updates arrive as new commits and are not automatic. Pull the latest with:
If a stale cached copy persists, remove and re-add the plugin:

GitHub Repository

View source code and the full configuration reference

Claude Code plugin

The same memory plugin for Claude Code