@shamisoul/pi-cognee

Cognee persistent memory for the pi coding agent — native extension with auto-capture, auto-recall, and knowledge-graph tools. Fail-soft by design: pi keeps working when the cognee server is down.

Packages

Package details

extension

Install @shamisoul/pi-cognee from npm and Pi will load the resources declared by the package manifest.

$ pi install npm:@shamisoul/pi-cognee
Package
@shamisoul/pi-cognee
Version
0.7.0
Published
Sep 26, 2026
Downloads
193/mo · 11/wk
Author
shamisoul
License
MIT
Types
extension
Size
460.8 KB
Dependencies
0 dependencies · 3 peers
Pi manifest JSON
{
  "extensions": [
    "./src/index.ts"
  ]
}

Security note

Pi packages can execute code and influence agent behavior. Review the source before installing third-party packages.

README

pi-cognee

A native TypeScript extension for pi that adds Cognee persistent memory. No MCP, no extra npm dependencies — a self-contained HTTP client (Node 18+ global fetch) speaking the same server API as the official Claude Code and Codex cognee plugins, so all three agents can share one memory.

Resilience is a core requirement. The extension must never break or slow down pi when the cognee server is missing, slow, or erroring:

  • every network call is bounded by a short timeout (recall 4s, health 2.5s, requests 10s),
  • every hook, tool, and command body is fail-soft — server down means a one-time quiet "offline" notice and tools returning a helpful error string, never a crash or a hang,
  • a circuit breaker pauses auto-recall after repeated failures (file-shared across pi processes via ~/.cognee-plugin/pi/breaker.json) and a background probe reconnects automatically when the server comes back,
  • auto-captured turns and tool traces are buffered while the server is down and replayed on reconnect — buffered on DISK (~/.cognee-plugin/pi/bridge/<session>.json) once a send fails retryably, so they survive a pi exit and replay at the next launch (verify-before-replay dedupes sends that may have committed; shutdown makes one last bounded flush attempt first).

How it works — two memory tiers

  1. Session cache (server-side). Each finalized user prompt is paired with the assistant's final answer into one qa entry, and (v0.4) every allowed tool call lands as a trace entry (origin_function, status, redacted params/return) — both written to POST /api/v1/remember/entry under a per-session cognee session id (pi_<pi-session-id>). Secrets are redacted and huge payloads skipped before anything leaves the process (see Capture policy).

  2. Permanent knowledge graph (per dataset). The session cache is promoted into the graph via POST /api/v1/improve — automatically every COGNEE_AUTO_IMPROVE_EVERY captured entries (QA turns + traces), on shutdown (one bounded flush: a 1s health re-probe, a ≤4s buffered-write drain, a ≤4s improve submit), or manually with /cognee-sync or the cognee_sync tool. Explicit knowledge goes straight to the graph via /cognee-remember or cognee_remember.

  3. Code graph (per repository). POST /api/v1/remember with content_type="code" submits a repository to cognee's enola pipeline (requires cognee ≥ 1.5.4) — a deterministic graph of symbols, calls, imports, endpoints and dependencies, no LLM or embedding calls (fast, token-free). Each repo gets its own narrow dataset codebase-<repo-name>-<digest> (the digest is the indexed path, so two checkouts sharing a basename never land in one graph). See Code graph.

On every user prompt, before the model runs, the extension recalls relevant graph memory (scope=["graph"], search_type=HYBRID_COMPLETION, only_context=true, top_k=5 — the same pinned request the official plugins send), only while the server is healthy. When the prompt contains an identifier-shaped token (process_payment, UserService, billing/api.py) and the cwd sits inside an indexed repo, a code recall lane runs concurrently (same recall budget, never sequential) and its hits are appended as a bounded === Code graph facts === section. Hits are injected as a bounded context block that opens with === Cognee memory === and a stats line (Cognee memory: 3 memory hits · 2/5 turns had hits this session), capped at COGNEE_CONTEXT_MAX_CHARS (default 2000) — individual memories are injected whole (no per-item cap, matching the official plugins since 1.6.0). When recall is skipped or fails, the model instead sees a one-line outage header so consulted-and-failed memory is never silent: === Cognee memory: recall skipped (server unreachable|auth failed|server error|server not responding|circuit breaker open) · N awaiting replay ===.

When the context window is about to be compacted (session_before_compact), the extension stores a memory anchor into the session-cache tier first: one session-scoped graph recall (top_k=3, seeded by a keyword query built from the transcript being compacted) verbatim (capped at 8000 chars — it is stored as a session-cache QA answer), falling back to the recent turns themselves when the graph has nothing — so post-compact auto-recall keeps continuity. Detached and fail-soft: compaction itself is never blocked.

Install

Requirements. A cognee API server the extension can reach (in local mode pi-cognee boots one automatically since v0.4 — or you run your own), and Node 18+ (the extension itself stays a pure HTTP client; only the bootstrap spawns processes, strictly from the session-start/health-reprobe background paths).

Try it once, no persistence:

pi --extension ./src/index.ts        # from a checkout of this repo

Dev / personal use — drop this repo (or a pi-cognee/ directory containing index.ts) into ~/.pi/agent/extensions/, or use the explicit flag in your launch alias.

As a package:

pi install ./local-pi-cognee         # local directory
pi install npm:pi-cognee             # from npm (once published)
pi install git:github.com/topoteretes/cognee-integrations@main

Manage with pi list, pi remove, pi update --extensions.

Quickstart

Configure once in ~/.cognee/.env — the file is shared with the official Claude Code and Codex cognee plugins, so one configuration covers every agent. Shell exports always override the file. Re-pasting a block is safe: when a key appears more than once, the last value wins.

Local mode (default when COGNEE_BASE_URL is not set) — the extension talks to a local cognee API at http://localhost:8011. LLM_API_KEY is required by the server (pass-through), not by this extension. Cognee ≥1.2.2 enforces auth even on localhost, so a data-plane key is needed too — when COGNEE_API_KEY is not set, pi-cognee logs into the local server as the default user and auto-mints an owner API key (reusing an existing one when present), cached in ~/.cognee-plugin/api_key.json — the same shared cache the official plugins use. For the mint to succeed, start the server with DEFAULT_USER_PASSWORD set to the same value as COGNEE_USER_PASSWORD (defaults: default_user@example.com / default_password), or set COGNEE_API_KEY explicitly:

mkdir -p ~/.cognee
cat >> ~/.cognee/.env <<'EOF'
LLM_API_KEY="sk-..."
# Optional — only if you disable/replace the auto-mint:
# COGNEE_USER_PASSWORD="default_password"
# COGNEE_API_KEY="ck_..."
EOF
chmod 600 ~/.cognee/.env

Start the server (pi-cognee boots it automatically since v0.4 — see Local server bootstrap; to run one yourself instead: DEFAULT_USER_PASSWORD=default_password uvicorn cognee.api.client:app --port 8011).

On startup you should see a "Cognee Memory Connected" notification. If the server is not running, you get a one-time quiet notice instead — pi keeps working normally and the extension reconnects automatically once the server is up.

Cognee Cloud or a remote server — set both:

mkdir -p ~/.cognee
cat >> ~/.cognee/.env <<'EOF'
COGNEE_BASE_URL="https://your-instance.cognee.ai"
COGNEE_API_KEY="ck_..."
EOF
chmod 600 ~/.cognee/.env

Which mode wins, and how to switch

  1. A COGNEE_BACKEND export wins (local / native / sdk, or cloud / http / api / server; COGNEE_PI_BACKEND beats the shared name). Pinned cloud with no URL never falls back to local — the status line shows the missing URL.
  2. Otherwise, cloud wins when COGNEE_BASE_URL is set (file or shell).
  3. Otherwise, local at http://localhost:8011 (COGNEE_LOCAL_API_URL overrides).

Unlike the official plugins, forced-local does not scrub COGNEE_API_KEY (an explicit key for your local server is honored — pi-cognee's bootstrap only installs/spawns when the server is positively absent, so a key never conflicts with it); COGNEE_BASE_URL is effectively ignored on the forced-local path — only COGNEE_LOCAL_API_URL is read.

Local vs cloud

Local Cloud / remote
Configure LLM_API_KEY in ~/.cognee/.env (server-side) COGNEE_BASE_URL + COGNEE_API_KEY
Server URL http://localhost:8011 (COGNEE_LOCAL_API_URL) your instance URL
Who runs the server pi-cognee boots it automatically (bootstrap) or you do (uvicorn cognee.api.client:app) Cognee
COGNEE_API_KEY optional — auto-minted on loopback servers when unset required
Data location your machine Cognee Cloud

Local server bootstrap

Since v0.4, pi-cognee boots the local cognee server for you — the same way the official Claude Code / Codex plugins do, against the same shared state (~/.cognee-plugin), so all three agents single-flight one machine-wide server (shared venv, shared locks, shared pidfile; pi's own logs live under ~/.cognee-plugin/pi/). With LLM_API_KEY in ~/.cognee/.env, a fresh machine needs zero setup: start pi, and memory activates a few minutes later.

  • Trigger — at most once per pi process, from the session-start background path or an unreachable health probe: a ~2.5s presence probe (/health, loopback TCP handshake, pidfile) runs first; ready/busy → nothing (a running server is adopted as-is, never restarted, never version-checked), only a positively-absent server is ever installed over. Busy/unknown servers are refused with a log line — upgrading under a busy server corrupts its databases.
  • Install — self-managed uv (or any uv on PATH; auto-installed otherwise), uv venv --python 3.12 (COGNEE_PLUGIN_PYTHON), uv pip install cognee==1.6.0 — the pin is a hardcoded constant, env-overridable never (parity). Provider extras are detected from the effective env (postgres/pgvector → postgres-binary, neo4j, fastembed, ollama). A venv already at the pin is never reinstalled (skip-at-pin).
  • Boot — detached uvicorn cognee.api.client:app --port <port> (localhost-only, port from COGNEE_LOCAL_API_URL), console output piped into a detached capture-pump process that keeps the first 1 MiB of each boot in ~/.cognee-plugin/pi/server-console-<port>.log (previous boot in .1; the server never holds the file fd, so the cap holds for its whole lifetime), pidfile written as presence evidence, /health polled until COGNEE_SERVER_BOOT_DEADLINE (600s). Single-flighted by the shared server-bootstrap.lock (stale 60s, wait 35s; losers poll health and adopt).
  • Divergences from the reference (deliberate, see Differences): COGNEE_AGENT_MODE is not set (pi has no agent registration — an agent-mode server's zero-connections teardown would kill the shared server when the last Claude Code session unregisters; a pi-booted server persists until you stop it), the bootstrap is an in-process task (no detached worker surviving pi; a pi-kill mid-install self-heals via stale-lock reap
    • version probe on next launch), and there is no python3 -m venv fallback when uv is unavailable (a clear "install uv" error instead).
  • Opt out — COGNEE_LOCAL_BOOTSTRAP=off restores v0.3 behavior exactly: never install, never spawn ("run the server yourself"). /cognee-doctor shows the local runtime row (uv path, venv version vs pin, bootstrap state) and the server pidfile verdict.

Commands

Command What it does
/cognee Status: health + latency, mode, dataset, federation state, session, API key source, capture/auto settings, recall hits, queue, breaker, code-graph state
/cognee-doctor Diagnose: mode + why, env file and shell overrides, API key source (incl. plugin identity), reachability + latency + version, dataset list, federation state, memory sharing + provisioning verdicts, capture policy, breaker, timeouts, code graph, local runtime (uv/venv/pin + bootstrap state), server pidfile
/cognee-remember <text> Store text in the graph. --file <path> ingests one file (≤200 KB, text-only) verbatim under its real filename — code extensions route down the zero-LLM code-graph path (single file, no cross-file edges; use /cognee-index for those). Re-ingesting a changed file updates the stored item (chunk-level diff, same data_id) instead of duplicating it; identical content sends nothing. --node-set user_context|project_docs|agent_actions picks the memory category. Waits briefly for cognify
/cognee-search <query> Explicit graph search. --top-k N, --dataset <name>
/cognee-datasets [name] [--force] List memory datasets (reference picker format, active one starred; v0.4 hides read-only rows behind (N read-only dataset(s) not shown) when the permissions route answers, or notes (write access could not be verified — showing every readable dataset) when it does not) or switch the active dataset: syncs the current session into the dataset being left (abort on failure unless --force), validates/creates the target (read-only and unresolvable-UUID targets refused when writability was verified), mints a new session id (pi_<session>__2, __3…), re-points capture/recall/sync/statusline, and persists the switch to ~/.cognee-plugin/pi/active-dataset.json so it survives restarts and beats the COGNEE_PLUGIN_DATASET seed — under shared memory the target resolves as the principal (canonical UUID + grant backfill, created as the parent when absent) and the record carries dataset_id/dataset_ids. Unlisted names are created on switch; recall is scoped to the active dataset (context from the previous one stops being injected — switch back to see it; isolation is enforced by the server's /recall filters — see the v0.3.0 changelog note). A one-off look in another dataset needs no switch: /cognee-search <query> --dataset <name>
/cognee-index [path|git-url] Index a repo into the code graph (cognee ≥ 1.5.4). --dataset <name>, --index-vectors (embed code facts for semantic search), --wait <seconds> (poll until queryable). Default dataset codebase-<repo>-<digest>, printed on success. Local paths need a server sharing this filesystem (cloud servers reject them — pass a git URL; the command warns)
/cognee-code <seed> | '<operation-json>' Query the current repo's code graph: a plain word is a seed (exact/suffix/substring); or an exact operation — {"operation":"impact_analysis","targets":["process_payment"]}, query_facts (e.g. kind:"route" lists endpoints), explore, traverse, find_path, delta (what the last index changed). --dataset <name>, --top-k N
/cognee-sync Promote this session's cache into the permanent graph (manual, ignores nothing)
/cognee-forget [dataset] [dataId] Irreversible delete. No args lists datasets. With a dataset and no dataId it lists that dataset's stored data items and lets you pick one to delete (UI picker; non-UI prints data_ids to re-run with). Asks for confirmation with a content preview (add --yes in non-UI modes)

Code graph

Structural questions about a codebase — what calls X, what breaks if X changes, how does A reach B, list all endpoints — are answered by a code graph built per repository, not by semantic memory. Indexing runs cognee's enola pipeline server-side (deterministic, no LLM/embedding calls); querying is exact and instant.

/cognee-index .                          # index the cwd's repo (local server)
/cognee-index https://github.com/o/r --wait 60   # cloud server: git URL, poll until queryable
cognee_code(operation="impact_analysis", targets=["process_payment"])   # from the model
/cognee-code '{"operation":"delta"}'   # what the last (re-)index changed

Which search when: a structural question naming a symbol/file → cognee_code / /cognee-code. A conceptual question naming nothing ("how does auth work here?") → cognee_search / /cognee-search — but note code-graph facts are invisible to it unless the repo was indexed with --index-vectors. Chain them: hybrid search discovers the name, code search gives the exact structure. Treat code-graph results as a map, not ground truth — verify against files.

Automatic indexing. Opening pi inside a git repository indexes it automatically in the background (after the first successful health probe — never blocking startup) when the repo has 1–3000 source files. COGNEE_CODE_AUTOINDEX controls it:

Value New repos Notes
auto (default) indexed only when the server URL is loopback (localhost / 127.0.0.1 / ::1 / 0.0.0.0) — the code never leaves the machine any other server (cloud, or a COGNEE_LOCAL_API_URL pointed at a LAN host) is skipped: shipping a private checkout off-machine needs explicit consent
always indexed against any server opt-in for remote servers
off never auto-indexed explicit indexing still works

Already-indexed repos are always refreshed when the tree changed — regardless of the setting (the index command was the consent). Not a git repo, no code files, or >3000 source files → skipped; index explicitly if you want it (no size cap on explicit indexing).

Freshness. After any agent turn that changed the working tree, a git fingerprint (HEAD + dirty paths + diff + untracked stats; .enola/ excluded) triggers a debounced background re-index. What the graph can reflect depends on where the server runs — both are normal, but the results look identical:

Server Graph reflects Stays current via
Local the working tree, uncommitted and untracked changes included automatic re-index after turns that change the tree
Cloud / remote the last pushed commit (the server clones the repo; local paths are rejected) pushing, then re-indexing

On a cloud server, local edits are invisible until pushed — say so when answering from a cloud-indexed graph during in-progress work; {"operation":"delta"} shows what the graph actually knows. ~/.pi-side state lives in ~/.cognee-plugin/pi/code-graph/; re-indexing an unchanged repo is skipped server-side (content hashes), so re-running /cognee-index is safe.

.enola/ snapshot. Indexing writes enola's snapshot to <repo>/.enola/ (untracked). Add it to .gitignore or global excludes — the extension's change detection already ignores it:

printf '\n.enola/\n' >> .gitignore

Code recall lane. Once a repo is indexed from its checkout, the per-prompt memory recall adds a code lane automatically when the prompt mentions an identifier-shaped token (process_payment, UserService, billing/api.py) — the facts appear as a bounded === Code graph facts === section (1000-char hard cap) rendered before the memory block. The lane sends exactly what the official plugins' auto lane sends (verified against the reference source and a live server): scope=["code"], top_k=5, the full prompt as the query, code_query = {operation: "query_facts", name: <identifier>, limit: 5}, the session id, and the repo's own dataset from index state. It runs concurrently with the graph recall inside the same recall budget, so it can never add latency beyond it. Two behaviors worth knowing:

  • Empty results are never injected. For a symbol the graph cannot resolve, the server returns a single envelope entry ({"operation": "query_facts", "facts": [], …}) — it is filtered out before counting hits, so the lane stays silent instead of injecting raw JSON (v0.1 injected it; fixed in v0.2). The explicit cognee_code tool applies the same filter.
  • Closure-nested symbols are invisible to the graph. The enola indexer records module-level declarations and class members — a function nested inside another function (like the helpers inside this extension's factory) has no fact, so prompts naming one legitimately find nothing. Flat files and classes are the coverage unit.

Tools (model-facing)

Tool Use when…
cognee_remember(content | file, node_set?, dataset?) The user states a lasting preference, decision, convention, or fact — or asks you to remember something. file uploads one file from disk under its real filename (code extensions → zero-LLM code-graph route); a changed re-upload updates the stored item instead of duplicating it
cognee_recall(query, search_type?, session_only?, top_k?) Targeted memory lookup mid-task; session_only=true reads this session's raw Q&A cache
cognee_search(query, top_k?, dataset?, search_type?) Broad/exploratory graph search, cross-dataset (incl. memories written by the Claude Code / Codex plugins; federated reads apply when COGNEE_PLUGIN_READ_DATASET_IDS is set)
cognee_code(seed?, operation?, name?/start?/source?+target?/targets?/kind?/limit?/max_depth?/direction?, repo?, dataset?, top_k?) Structural code questions naming a symbol/file: callers of a seed, impact_analysis (what breaks if X changes), find_path (how A reaches B), query_facts kind:"route" (all endpoints), explore/traverse, delta (what the last index changed) — exact, instant, no tokens. Conceptual questions → cognee_search
cognee_forget(dataset, data_id?, entire_dataset?) The user explicitly asks to remove wrong/outdated/sensitive memories (irreversible)
cognee_sync(dataset?) The user asks to save this session's memory to the graph

All tool output is truncated at 4000 chars. Tool descriptions teach the model when to reach for memory, so you should not need to prompt for it.

Capture policy

Auto-capture follows the official plugins' rules. QA pairs (prompts + answers into the session cache) and tool-call traces (v0.4 — every allowed tool result as a TraceEntry in the same session cache, the PostToolUse equivalent on pi's tool_result event) share one policy:

  • COGNEE_CAPTURE=false disables all automation (QA capture, tool traces, and auto-recall); explicit tools and commands keep working,
  • secrets are redacted before truncation (so a clipped key cannot escape): private-key blocks, database URIs, bearer/basic tokens, key: value credential pairs, sk-… / sk-ant-… / gh[pousr]_ / github_pat_ / xox… / whsec_… vendor keys, bcrypt hashes, and — traces only — dict values under secret-ish keys (authorization, x-api-key, *secret, *token, *password, *api_key, …) replaced wholesale → [redacted:<kind>],
  • the tool allowlist (COGNEE_CAPTURE_TOOLS) defaults to the reference matcher translated to pi tool names (bash|powershell|read|write|edit|grep|find|ls); a custom value replaces it — widen to custom/MCP tools (mcp__*) or narrow at will; case-sensitive fnmatch globs,
  • the sensitive-path deny list refuses the WHOLE trace when a path-ish input key (path/paths/file_path/filepath/notebook_path/filename) carries a credential-looking file (.env*, *.pem, *.key, id_rsa*, .npmrc, .netrc, */.aws/credentials, */.ssh/*, *.p12, *.pfx, secrets.*, credentials.* + COGNEE_CAPTURE_DENY_PATHS extras) — never partially redacted; non-path keys (e.g. a grep pattern) are not denied,
  • self-reference is skipped even when the allowlist admits it: this extension's own cognee_* tools and shell lines mentioning cognee never feed the graph,
  • trace caps match the reference: 4000 bytes per param value, 8000 for the return value, 500 for error text (byte-exact, multibyte-safe, lone surrogates round-tripped to U+FFFD), and generate_feedback_with_llm is always false (per-step LLM feedback deferred),
  • huge QA payloads are skipped (prompts capped at 4000 bytes, answers at 8000; oversized prompts dropped rather than clipped), prompts under 5 chars and slash commands are ignored.

Explicit remember of inline text (cognee_remember, /cognee-remember) goes through the same redaction — a deliberate hardening beyond the official plugins, which leave explicit remember unfiltered: once stored, a secret is durable, cross-session, and shared with other agents. --file / file= uploads are stored verbatim under the real filename (like the official plugins' --file path): the filename extension is the server's loader-routing signal, and redacting code would corrupt what the zero-LLM code path ingests — point the tool at code, not at secrets.

Update-when-changed on --file (v0.3). A file upload is keyed by its basename in the target dataset: re-ingesting a file whose content changed PATCHes the stored data item (PATCH /api/v1/update, cognee ≥ 1.6.0) under the same data_id — the server re-ingests only the affected chunks and transparently falls back to a delete+re-ingest (same id) when chunk-level preconditions fail, so no duplicate items accumulate. Identical bytes are an authoritative no-op ("unchanged — nothing sent": no POST/PATCH is issued; the stateless content-hash compare against the stored text still performs its read-only discovery GETs — datasets, items, raw). Discovery is by exact filename match (latest createdAt wins on same-name twins — same-named files from different directories are a documented limitation); any discovery failure degrades to a plain add. A failed/errored update does not fall back to an add (the document still exists — a duplicate would corrupt its identity); a 404 (someone forgot the item mid-flight) does re-add. Prose memories (content) stay append-only — their synthetic names are timestamped by design. Repo code-graph indexing (/cognee-index) never uses this path: it submits whole-repo specs with no per-file data_id to update.

Environment variables

Shell exports > ~/.cognee/.env (path override: COGNEE_ENV_FILE) > defaults. The file also passes through LLM_API_KEY / LLM_MODEL for a local server (reported by /cognee-doctor).

Variable Default Meaning
COGNEE_BASE_URL — Cloud/remote server URL; setting it selects cloud mode
COGNEE_API_KEY — API key (sent as X-Api-Key); auto-minted on loopback servers when unset
COGNEE_USER_EMAIL / COGNEE_USER_PASSWORD default_user@example.com / default_password Login credentials for the local owner-key auto-mint (must match the server's DEFAULT_USER_*)
COGNEE_BACKEND / COGNEE_PI_BACKEND auto Pin local or cloud
COGNEE_LOCAL_API_URL http://localhost:8011 Local server URL
COGNEE_PLUGIN_DATASET agent_sessions Seed for the graph dataset — set per project for scoped memory; the default is shared with the Claude Code/Codex plugins. Once /cognee-datasets <name> switched datasets, the persisted record wins over this seed (switch back or delete ~/.cognee-plugin/pi/active-dataset.json to re-seed)
COGNEE_PLUGIN_READ_DATASET_IDS — JSON array of dataset UUIDs for federated graph recall (read-only federation; see Federated reads)
COGNEE_PLUGIN_IDENTITY auto Plugin-identity policy for shared agent memory: auto (provision only to enable shared memory; fall back to the principal on any obstacle), true/1/yes/on (explicit identity — obstacles become errors/warnings), false/0/no/off (principal only). Invalid values surface the exact reference error and behave as auto
COGNEE_SHARED_AGENT_MEMORY true Shared agent memory wiring (tenant + cognee-agent role + grant backfill + canonical datasets); off exactly on 0/false/no/off — the agent then leaves the shared role and writes its own private dataset (re-enabling rejoins the same role)
COGNEE_SESSION_PREFIX / COGNEE_SESSION_ID pi / auto Cognee session id (pi_<pi session id>)
COGNEE_CAPTURE true Master switch for auto-capture + auto-recall (gates QA pairs, tool traces, and the code lanes)
COGNEE_CAPTURE_TOOLS bash|powershell|read|write|edit|grep|find|ls Tool-trace allowlist — pipe-separated globs or a JSON array; a set value REPLACES the default (the reference PostToolUse matcher translated to pi tool names; widen to custom/MCP tools or narrow). Case-sensitive fnmatch-style match
COGNEE_CAPTURE_DENY_PATHS — Comma-separated globs or a JSON array EXTENDING the compiled-in sensitive-path deny list (.env*, *.pem, *.key, id_rsa*, .npmrc, .netrc, */.aws/credentials, */.ssh/*, *.p12, *.pfx, secrets.*, credentials.*); a hit refuses the whole trace entry. Only values under path-ish keys (path, paths, file_path, filepath, notebook_path, filename) are tested — basename or /-prefixed normalized full path, backslashes and case normalized
COGNEE_CAPTURE_REDACT true Trace redaction master switch (false passes input/output through — not advised). The QA pair path stays unconditionally redacted (one-line follow-up)
COGNEE_CAPTURE_REDACT_PATTERNS — Newline-separated regexes (or a JSON array) replacing matches with [redacted:custom] — newlines keep commas valid regex syntax. Invalid regexes are skipped with a /cognee-doctor warning (fail-soft)
COGNEE_CONTEXT_MAX_CHARS 2000 Cap for the injected recall block
COGNEE_AUTO_IMPROVE_EVERY 150 Auto-sync session→graph every N writes (0 = off) — gated (with the idle trigger) by the persisted improve-cooldown
COGNEE_IMPROVE_COOLDOWN_MS 1800000 Minimum spacing between AUTOMATIC syncs — persisted in ~/.cognee-plugin/pi/improve-state/, honored across restarts (final/manual/switch syncs are ungated)
COGNEE_IDLE_IMPROVE true Arms/disarms the idle improve trigger alone (agent_settled + one bounded timer; the every-N path keeps running)
COGNEE_IDLE_THRESHOLD_MS 60000 Quiet time after a settled turn before the idle improve fires (one attempt per arm; a throttled fire re-arms once at the cooldown's expiry)
COGNEE_DRAIN_BUDGET_MS 20000 Time box for a background write-queue drain (per-request timeouts clamp to the remaining budget)
COGNEE_STATUSLINE_COUNTS — Statusline recall segment: full = diagnostic strip (recall <graph>g/<code>c); false/0/off hides the segment; unset = · N memory hits · X/Y turns had hits this session
COGNEE_STATUSLINE_CREDITS — Set false/0/off to hide the render-only credits segment (cloud mode; read from the shared ~/.cognee-plugin/claude-code/credits.json)
COGNEE_BILLING_URL https://platform.cognee.ai/billing Top-up URL appended when the shared credits marker shows ≤ $1
COGNEE_CREDITS_FILE <pi-state-parent>/claude-code/credits.json Explicit override for the shared credits marker path (render-only)
COGNEE_FINAL_SYNC true Bounded shutdown flush (1s probe + ≤4s write drain + ≤4s improve); anything unflushed stays in the disk bridge and replays at the next launch
COGNEE_RECALL_TIMEOUT_MS 4000 Per-prompt recall budget
COGNEE_REQUEST_TIMEOUT_MS 10000 General request timeout
COGNEE_HEALTH_TIMEOUT_MS 2500 Health probe timeout
COGNEE_IMPROVE_SUBMIT_TIMEOUT_MS 60000 Improve submit timeout
COGNEE_REMEMBER_WAIT_SECONDS 8 Bounded wait for graph queryability after /cognee-remember
COGNEE_REMEMBER_BACKGROUND true run_in_background default for explicit remember writes — set false for a synchronous, immediately-queryable write
COGNEE_BUFFER_LIMIT 100 Write buffer bound — now enforced on the DISK bridge file too (drop-oldest) as well as the in-memory queue
COGNEE_BREAKER_THRESHOLD/WINDOW_MS/COOLDOWN_MS 5 / 300000 / 120000 Circuit breaker (only unreachable/5xx count)
COGNEE_BREAKER_FILE ~/.cognee-plugin/pi/breaker.json Cross-process breaker state file (open-until + consecutive-failure count, keyed by server URL)
COGNEE_API_KEY_CACHE ~/.cognee-plugin/api_key.json Owner-key cache path (parity-shared with the official plugins; override for tests/isolation)
COGNEE_CODE_STATE_DIR ~/.cognee-plugin/pi/code-graph/ Per-repo index-state directory (dataset + fingerprint + last status; override for tests)
COGNEE_PI_STATE_DIR ~/.cognee-plugin/pi Directory holding the persisted dataset-switch record (active-dataset.json, keyed by server URL + API-key fingerprint), the disk write bridge (bridge/<sha1(sid)>.json), the persisted improve-cooldown (improve-state/ + improve-counter.json), and the cross-process breaker; override for tests. Its parent is the shared bootstrap root (venv/locks/pidfile — and the shared credits marker at <parent>/claude-code/credits.json), so overriding it also relocates the whole bootstrap cluster
COGNEE_CODE_AUTOINDEX auto Code-graph auto-indexing of new repos: auto (loopback server only), always (any server), off. COGNEE_CAPTURE=false disables it as part of all automation
COGNEE_CODE_INDEX_TIMEOUT_MS 120000 Repo-index submit timeout (background pipelines confirm slowly; a timeout is not retried blindly — the submission may have landed)
COGNEE_LOCAL_BOOTSTRAP on Master switch for the local server bootstrap: off = v0.3 behavior exactly (never install, never spawn)
COGNEE_SERVER_BOOT_DEADLINE 600 (s) Overall boot deadline — health wait after the uvicorn spawn
COGNEE_INSTALL_TIMEOUT 600 (s) Per uv subprocess timeout; the install lock's stale/wait window is this + 60 s
COGNEE_PLUGIN_PYTHON 3.12 Python pin for uv venv (uv downloads a standalone CPython when absent)
COGNEE_PRESENCE_REPROBE_DELAY 3 (s) Delay before the absence-confirming presence re-probe (pre_install stage only)
COGNEE_PLUGIN_LOG_MAX_BYTES 20971520 (20 MiB) ~/.cognee-plugin/pi/bootstrap.log rotation cap

Federated reads (COGNEE_PLUGIN_READ_DATASET_IDS)

Set COGNEE_PLUGIN_READ_DATASET_IDS to a JSON array of dataset UUIDs to widen graph recall across datasets you granted yourself — the same read-only federation the official plugins ship (shell export or ~/.cognee/.env, shell wins):

COGNEE_PLUGIN_READ_DATASET_IDS='["3f2b8ac6-1d5e-4f7a-9c3b-2e8d7a6b5c4f", "01234567-89ab-cdef-0123-456789abcdef"]'

Find dataset UUIDs with /cognee-forget (it lists datasets with their ids) or the server UI.

  • Graph reads only. Auto-recall per prompt, /cognee-search, cognee_recall / cognee_search, and the pre-compact anchor recall address the read set by UUID (dataset_ids) — the federated payload drops both the dataset name and the session binding ("session history remains bound to ONE dataset; federated graph recall is a separate read"). Writes, /cognee-sync, and session-cache memory never federate: they always target the single session dataset.
  • Precedence. The env read set beats an explicit dataset / --dataset argument on the graph lane and beats UUID addressing passed per call; the code lane (cognee_code, scope=["code"]) is never federated.
  • UUID-only. Entries must be UUIDs (hyphens optional, case-insensitive); they are canonicalized to lowercase-hyphenated form and deduped preserving first-seen order. A malformed value (non-JSON, non-list, empty list, non-UUID entry) disables federation and surfaces the exact validation error — as a load-time config warning in /cognee and /cognee-doctor plus a one-time session-start notice, never a crash (the reference raises the same strings at recall time).
  • Cross-agent caution. The variable only selects among datasets your API key can already read — server-side RBAC is still enforced, and a UUID you have no read grant on simply fails recall (DatasetNotFoundError). Grants flow child→parent only: your user sees what the agent writes, but the agent sees nothing your user (or another plugin's agent) owns. Use dataset UUIDs for shared read targets; never as a way to widen writes.

Shared agent memory (COGNEE_PLUGIN_IDENTITY × COGNEE_SHARED_AGENT_MEMORY)

A provisioned plugin agent is its own user — and cognee's grants flow child→parent only: the parent sees what the agent creates, the agent sees nothing the parent or a sibling owns. Left alone, per-plugin identities would silo memory (pi could not recall what Claude Code stored). Shared agent memory (on by default) closes that loop with the server's existing permission model, no core changes: the parent owns a tenant and a role named cognee-agent; every plugin agent joins that role; the role holds read+write on the parent's datasets (backfilled at every wiring/refresh, so datasets created later converge); and the launch dataset is addressed by UUID — one canonical, parent-owned copy per name that every agent writes to, plus other readable same-named copies for recall. Every control-plane call authenticates as the principal (owner-only server-side — an agent key can never widen its own access); only tenants/select runs as the agent, on itself.

Capability gating (graceful degradation). Before any provisioning POST, the client verifies the server's advertised create-only contract (create_only query param on the provision route in /openapi.json — one probe per session start, 60 s TTL): older servers ignore unknown query params and would rotate the key out from under other machines, so without the contract the verdict is unsupported and zero provisioning/tenant/role/grant calls are issued — pi keeps the single-principal key chain of today byte-for-byte. Wiring additionally requires the x-cognee-session-dataset-ids extension on remember/entry (else typed_dataset_unsupported). cognee 1.6.0 advertises neither — on it, pi runs as the principal and /cognee-doctor reports Provisioning: server does not support provisioning / Memory: principal (no agent identity); the full flow is implemented once and lights up the moment a server catches up. Structural verdicts (unsupported, typed_dataset_unsupported, not_tenant_owner, tenantless_with_data) are recorded in the marker with the plugin version and suppress re-probing until the plugin updates.

Policy matrix (COGNEE_PLUGIN_IDENTITY tri-state auto|true|false; sharing default on, off on 0|false/no/off):

IDENTITY \ SHARED true (default) false
auto (default) Shared, graceful — provision when possible, wire the role, fall back to the principal whenever that cannot be done (older server, not tenant owner, tenant-less user with data) Principal unless already provisioned — never provisions; a cached identity is kept, leaves the shared role, writes its own private dataset
true Shared, strict — same wiring; every obstacle is an error (pi: one-line warning, never a silent fall back) Separated, strict — per-plugin isolation mode
false Principal only (sharing irrelevant) Principal only (identical)

A malformed COGNEE_PLUGIN_IDENTITY surfaces the exact reference error (COGNEE_PLUGIN_IDENTITY must be auto, true, or false) as a load-time config warning and behaves as auto — never a crash.

State files (under ~/.cognee-plugin/pi/, honoring COGNEE_PI_STATE_DIR):

  • agent-key.json — the provisioned identity: {base_url, api_key, agent_id, plugin_key: "pi", principal_fingerprint (sha256 of the principal key), updated_at, blocked?}; 0600, atomic tmp+rename. A cached key that passes its checks always wins (provisioning again would rotate it); a blocked key (server rejected it with 401/403 — stamped automatically) is never re-provisioned: create-only refuses an existing agent and rotating would revoke another machine's key.
  • shared-memory.json — the wiring marker: {base_url, mode: shared|separated, reason, tenant_id, role_id, parent_user_id, agent_id, granted: {<dataset UUID>: "ok" | {denied_at}}, canonical: {<name>: <write UUID>}, updated_at, plugin_version}. base_url mismatch → treated as absent (per-server marker). Grants memoized "ok" are never re-POSTed; denials (401/403 — typically a dataset someone shared to you read-only) are retried hourly.

Behavior when wired. Writes address the resolved canonical UUID (remember/entry dataset_id, improve dataset_id); graph recall widens to the read set (dataset_ids — precedence: env federation > shared-memory ids > explicit dataset, session binding dropped on that lane exactly like federation). The switcher narrows its listing via the permissions route (GET /permissions/principals/{user}/datasets?permission_name=write + via_role from the live marker), hides read-only rows with the reference's (N read-only dataset(s) not shown) note, refuses read-only/unresolvable-UUID targets when the route answers (unverifiable targets stay permissive — pi's fail-soft divergence), and resolves switches as the principal (canonical UUID + grant backfill, create-as-parent). Opting out (COGNEE_SHARED_AGENT_MEMORY=false) makes the agent leave the shared role and demotes the marker keeping the ids — re-enabling rejoins the same role. Under auto, a wiring failure right after provisioning reverts the fresh identity (server-side revoke; the agent user stays). The persisted dataset-switch record gains dataset_id/ dataset_ids fields and stays keyed on the principal fingerprint — flipping to a plugin identity never orphans the persisted switch. /cognee-doctor gains the Memory line (shared (role: cognee-agent) | separated (opt-out) | principal (shared memory unavailable: <reason>) | principal (no agent identity) | separated (<reason>)) and the Provisioning line (supported (create_only advertised) | server does not support provisioning | not probed (server unreachable) | disabled (COGNEE_PLUGIN_IDENTITY=false)); the API-key row labels the plugin-identity source. No surface shows raw keys.

Parity with the Claude Code / Codex cognee plugins

Feature Claude Code / Codex pi-cognee
HTTP contract (/health, /recall, /remember, /remember/entry, /improve, /datasets, /forget, /sessions/{id}) ✅ ✅ same wire format
Two-tier memory (session cache → graph via improve) ✅ ✅
Auto-recall per prompt (HYBRID_COMPLETION, graph scope, top_k=5, only_context) ✅ ✅ bounded 4s
Prompt+answer pairing into one QA entry ✅ ✅
Secret redaction + size limits before capture ✅ ✅ text-level; tool-call traces too (v0.4 — reference _capture_policy.py verbatim: allowlist, sensitive-path deny, recursive redaction with secret-key rule + custom patterns, 4000/8000/500B caps)
Tool-call trace capture (PostToolUse → trace entries in the session cache) ✅ ✅ v0.4 — tool_result handler → TraceEntry via POST /api/v1/remember/entry; default matcher translated (bash|powershell|read|write|edit|grep|find|ls + documented ls divergence), COGNEE_CAPTURE_TOOLS / COGNEE_CAPTURE_DENY_PATHS / COGNEE_CAPTURE_REDACT / COGNEE_CAPTURE_REDACT_PATTERNS; no per-trace toast (divergence), no Agent-tool analog (user allowlist covers custom tools)
Config: ~/.cognee/.env shared file, shell > file, COGNEE_BACKEND pinning ✅ ✅
Shared default dataset agent_sessions (cross-agent memory) ✅ ✅
Federated graph recall (COGNEE_PLUGIN_READ_DATASET_IDS — JSON UUID array; dataset_ids-only payload that drops the session binding; env read set beats explicit datasets; code lane and writes never federate) ✅ ✅ same semantics, exact reference error strings (surfaced as a load-time config warning instead of a recall-time raise)
Explicit remember with node_sets + background cognify wait ✅ ✅
Forget (doc / whole dataset, irreversible, confirmed, data-item discovery) ✅ ✅
Manual sync skill/command ✅ ✅ /cognee-sync + cognee_sync
Doctor (mode, env, reachability, latency, datasets) ✅ ✅ /cognee-doctor
Circuit breaker on recall ✅ file-shared ✅ file-shared (~/.cognee-plugin/pi/breaker.json, in-memory fast path, stale-read tolerant)
Code graph: repo indexing (content_type="code", codebase-<repo>-<digest> datasets, --index-vectors, --wait poll) ✅ ✅ same wire format
Code graph: deterministic queries (query_facts, explore, traverse, find_path, impact_analysis, delta) ✅ ✅ via cognee_code / /cognee-code (POST /api/v1/recall scope code + code_query)
Code graph: session-start auto-index + 3000-file cap + freshness fingerprint/re-index ✅ detached hooks ✅ in-process, debounced, fail-soft
Code graph: identifier-shaped code recall lane (=== Code graph facts ===) ✅ ✅ reference wire shape (code_query attached, prompt as query, session id, top_k 5), empty-result envelopes filtered, 1000-char cap, same recall budget
Per-file code ingestion (--file, real filename → zero-LLM code route, no cross-file edges) ✅ cognee-remember --file ✅ cognee_remember file= param + /cognee-remember --file (verbatim, guarded: exists/size/text-only/credential-path refusal)
Delta re-ingestion of changed files (PATCH /api/v1/update, chunk-level diff under a stable data_id) ➖ not used by the reference ✅ v0.3 — same-hash no-op, changed-hash update, exact-filename discovery; failed updates never duplicate-add
Pre-compact memory anchor (session-scoped graph recall + recent turns preserved) ✅ PreCompact hook ✅ session_before_compact → anchor stored into the session-cache tier
Server bootstrap (uv venv, pinned cognee, detached uvicorn, single-flight locks, presence gating, boot deadline) ✅ ✅ v0.4 — same shared root (~/.cognee-plugin), locks, pidfile and venv, so pi single-flights with Claude Code/Codex; divergences: no COGNEE_AGENT_MODE teardown, in-process task (no detached worker), no python3 -m venv fallback, COGNEE_LOCAL_BOOTSTRAP=off opt-out
Status text (●/✕ cognee: mode · dataset in pi's footer, health glyph + backend + dataset in every state) ✅ ✅ mode-guarded ui.setStatus at the health-probe points
Rich statusline renderer (hit counts, credits, update glyph) ✅ ✅ v0.4 — glyph precedence + recall/awaiting-replay/credits segments over shared markers; divergences: plain text (no ANSI), 120-char soft cap, no update marker (no marketplace), render-only credits (no billing refresh — gap 8)
cognee-recall subagent ✅ ❌ deferred
Dataset switcher ✅ ✅ /cognee-datasets — persisted record at ~/.cognee-plugin/pi/active-dataset.json wins over the COGNEE_PLUGIN_DATASET seed across restarts; v0.4 adds the permissions-route writability narrowing (read-only rows hidden + noted, unverifiable targets permissive), shared-memory switch resolution as the principal (canonical UUID + grant backfill, create-as-parent), and the dataset_id/dataset_ids record fields
Shared-agent-memory provisioning (/provision, tenants, roles, grants) ✅ ✅ v0.4 — full client-side parity gated by an openapi capability probe; on servers without the create_only contract (incl. cognee 1.6.0) it degrades to today's single-principal behavior with zero provisioning/wiring calls; see Shared agent memory
Idle watcher / exit watcher / detached final-sync worker ✅ ➖ in-process equivalents with persisted semantics: agent_settled + one bounded timer (one improve per arm, cooldown re-arm at expiry, COGNEE_IDLE_IMPROVE), persisted improve-cooldown (improve-state/ + improve-counter.json — replaces the in-memory timestamp across restarts), and crash-tolerant final sync via the disk bridge
Idle-watcher 60 s shared-memory refresh (COGNEE_SHARED_MEMORY_REFRESH) ✅ ➖ re-resolution at session start and on /cognee-datasets switch (deliberate skip — pi has no watcher)
Disk-backed write bridge (bridge/ spill + verify-before-replay + drain backoff) ✅ ✅ v0.4 — per-session flat file under ~/.cognee-plugin/pi/bridge/, ambiguity classification, fingerprint dedupe, doubling backoff, drop-oldest COGNEE_BUFFER_LIMIT, 7 d sweep; buffered turns survive a pi exit while the server is down

Consciously deferred (v0.2.0) — see Differences from the Claude Code/Codex plugins below for the full list of accepted gaps and deliberate behavioral divergences.

Differences from the Claude Code / Codex plugins

pi-cognee speaks the same wire protocol and shares ~/.cognee/.env (and the local owner-key cache) with the official plugins, so all three agents can share one memory. The gaps below are accepted — not implemented by design:

  1. Local server bootstrap — implemented in v0.4 (uv venv, pinned cognee 1.6.0, detached uvicorn, single-flight locks, boot deadline; see Local server bootstrap) with four documented divergences: no agent-mode server lifetime (COGNEE_AGENT_MODE is deliberately not set — pi has no agent registration until shared-agent-memory lands, and an agent-mode server's zero-connections teardown would kill the shared server under pi when the last Claude Code session unregisters; a pi-booted server persists until the user/machine stops it — revisit with gap 4), no detached bootstrap worker surviving pi exit (the package ships TS loaded by pi's runtime; a pi-kill mid-install self-heals via stale-lock reap + version probe on the next launch, which also makes COGNEE_LAZY_BOOTSTRAP meaningless — not read), no python3 -m venv fallback when uv is unavailable (a clear "install uv" error instead), and no Windows support (posix first). A pi-specific master switch COGNEE_LOCAL_BOOTSTRAP=off restores the pre-v0.4 "run the server yourself" contract.
  2. Tool-call trace capture — implemented in v0.4 (README gap 2; research/v04-traces-spec.md): the PostToolUse → trace-entry pipeline as a tool_result handler — every ALLOWED tool call lands in the session cache as an OpenAPI TraceEntry (origin_function, status, redacted method_params/method_return_value, 4000/8000/500-byte caps), with the reference capture policy verbatim: COGNEE_CAPTURE_TOOLS allowlist (default = the reference matcher translated to pi tool names: bash|powershell|read|write|edit|grep|find|ls — ls is a documented divergence, the same navigation family the reference's Glob covers; a user value REPLACES the default and can widen to custom/MCP tools), the compiled-in sensitive-path deny list plus COGNEE_CAPTURE_DENY_PATHS extras (entry-level refusal, never partial redaction), redaction before truncation (recursive redactForCapture: secret-ish dict keys wholesale, PEM/connection/ bearer/credential/vendor/bcrypt string rules, COGNEE_CAPTURE_REDACT_PATTERNS custom regexes → [redacted:custom], COGNEE_CAPTURE_REDACT=false opt-out), and the hard self-reference skip (cognee_* tools; shell lines mentioning cognee). Divergences: no per-trace toast (per-tool- call volume would be noise — silent success + buffered-failure counts), no Claude Agent-tool analog (no pi builtin; the user allowlist covers custom tool names), and the shared redactSecrets rule set was aligned to the reference superset (sk-ant-, github_pat_, bcrypt; tags renamed to the reference's [redacted:private-key] style).
  3. Dataset switcher — implemented in v0.2.0 with two documented divergences: single principal (every readable dataset is owned, hence writable — no permissions route until shared-agent-memory lands, gap 4), and no conn-handle register-then-unregister dance (that exists only for the reference's agent-mode server-count problem). Per-call dataset overrides still cover one-off reads without switching.
  4. Shared-agent-memory provisioning — implemented in v0.4 (README gap 4; research/v04-provisioning-spec.md): the write/grant side that decides which identity writes and which UUID it writes to — plugin identity /provision (create-only contract, capability-gated), tenant/role grants (cognee-agent role, read+write backfill, canonical parent-owned datasets), COGNEE_PLUGIN_IDENTITY × COGNEE_SHARED_AGENT_MEMORY policy, agent-key.json/shared-memory.json state, doctor Memory/Provisioning rows, and switcher writability narrowing. On servers without the create_only contract (cognee 1.6.0 today) everything degrades to the single-principal behavior with zero provisioning/tenant/role/grant calls — the client side is at parity so it lights up the moment a server catches up. Divergences: pi never hard-fails on strict-mode obstacles (one-line warning, principal fallback — fail-soft posture), no idle-watcher 60 s refresh (re-resolution at session start and on switch), no interactive identity reconnect (blocked identities surface a doctor warning; automatic re-provision stays disabled), and no cross-process file lock on the identity cache (benign race — create-only refuses rotation). See Shared agent memory.
  5. Rich statusline renderer — implemented in v0.4 (README gap 5; research/v04-resilience-spec.md §2.1): renderStatusline() composes one plain-text line for pi's footer — <glyph>cognee: <backend> · <dataset>[ · N awaiting replay][ · N memory hits[ · X/Y turns had hits this session]][ · credits: …] — from in-memory state plus the one shared credits marker, refreshed at the spec's points (health completion, each recall, each drain tick, improve settle, dataset switch; no timers). Glyph slot keeps the reference precedence (connection failure > llm-key verdict > ready signal): ● / ✕ offline / ✕ auth failed / ✕ server error / ✕ llm key not set (static local-mode verdict — the reference's real litellm ping is skipped, §5). Recall segment counts every scope that returned something and was injected (graph items + code facts — the reference's turns_with_hits rule), with the memory warming up (N turns) zero-hit form, the COGNEE_STATUSLINE_COUNTS=full diagnostic strip (recall <graph>g/<code>c), and false|0|off hiding. · N awaiting replay surfaces the durable bridge count. The credits segment is render-only over the shared ~/.cognee-plugin/claude-code/credits.json (pi never writes it): cloud mode, matching base_url, numeric balance, ≤ 7 d freshness (epoch-seconds markers tolerated), COGNEE_STATUSLINE_CREDITS opt-out, ≤ $1 → top up: <COGNEE_BILLING_URL|default>, > 15 m → (Nm/Nh/Nd ago), · last <label> ~$<cost>. Divergences: no ANSI colors (pi's footer is plain text — glyphs carry the semantics), a 120-visible-char soft cap that sheds pieces in the reference's reverse priority order (age hint → last-op → credits → cumulative turns → per-turn hits) where the reference never truncates, and no update segment / self-eviction (no plugin marketplace to check). Per-session marker files (conn-state/, llm-state/, recall/<sid>.json) are skipped — one process per terminal holds the state in memory (recall totals reset on restart; accepted).
  6. Idle watcher / exit watcher / detached final-sync worker — replaced in v0.4 (README gap 6) by in-process equivalents with the reference's exact semantics where they matter: the idle watcher maps to agent_settled + one bounded timer (COGNEE_IDLE_THRESHOLD_MS, default 60 s; every user message re-arms = the activity touch) — one improve attempt per arm, COGNEE_IDLE_IMPROVE=false disarms it, a throttled fire re-arms exactly once at the persisted cooldown's expiry (a quiet stretch that outlasts the cooldown still gets exactly one bridge), and session_shutdown clears the timers without improving on the way out (the final sync owns promotion). The exit watcher's detached worker maps to crash tolerance via the disk bridge (gap 7): anything the bounded 4 s final sync cannot flush stays spilled and replays at the next launch. The persisted improve-cooldown (~/.cognee-plugin/pi/improve-state/<sha1(sid)>.json + the improve-counter.json map — field-for-field with the reference: last_improved_at, turn_count_at_improve, trigger, failure merge fields) replaces the in-memory timestamp: both automatic paths (idle and every-N COGNEE_AUTO_IMPROVE_EVERY) consult improveThrottleReason() — cooldown (success younger than COGNEE_IMPROVE_COOLDOWN_MS), backoff (one auto attempt per window after a failure), no_new_entries (persisted counter did not advance) — while the final/manual/switch syncs stay ungated (parity). Only confirmed outcomes of the submitImprove wrapper stamp the state; the counter bumps once per successfully sent entry, which is what makes no_new_entries meaningful across restarts.
  7. Disk-backed write bridge — implemented in v0.4 (README gap 7; spec §2.5): ~/.cognee-plugin/pi/bridge/<sha1(sid)>.json — one flat file per cognee session id ({entries, fail_count, fail_at}; documented divergence from the reference's per-host-scope (dataset, session_id) map — pi's switch mints a new session id, so a session never spans datasets and per-session files kill the cross-terminal write race). Spill policy: a capture while the server is unusable spills immediately (never sent → never ambiguous); the first failed retryable drain attempt spills the entry plus whatever is queued behind it (order preserved), stamped _replay_ambiguous per the §1.5 classification (503 and positively-refused are unambiguous; timeouts/aborts/500/502/504 may have committed — /remember/entry has no idempotency, so a blind replay would duplicate); permanent 4xx stays drop-loudly for memory entries and enters the file's doubling 60 s → 3600 s drain backoff (any progress resets; the forced final-sync pass never stamps backoff, so a relaunch always replays). Replay is strictly head-first from the merged view (file first, then the memory tail, then other sessions' oldest spill), verify-before-replay for ambiguous QA and trace heads (one GET /api/v1/sessions/{id} fingerprint read — a match consumes the entry without re-sending; a detail failure replays, fail-open), time-boxed by COGNEE_DRAIN_BUDGET_MS (default 20 s), and COGNEE_BUFFER_LIMIT now bounds the file with drop-oldest. Entries replay at the next launch before any new capture lands (the session-start health drain is head-first); files older than 7 d are swept; buffered turns now SURVIVE a pi exit while the server is down — the crash-window divergence that remains (an entry whose HTTP write is in flight at the moment the host dies) exists in the reference too.
  8. Misc: credits/billing refresh, cognee-cli offline fallback, cognee-recall subagent, session-companion datasets / COGNEE_PROJECT_NODE_SET, ~/.cognee/.env template creation, usage metrics rollup (cognee-plugin metrics), other-readable-datasets hint on empty recall (COGNEE_RECALL_DATASET_HINT), /clear transcript handling, update check, log rotation.

Deliberate behavioral differences (implemented differently on purpose):

  • Forced-local keeps COGNEE_API_KEY — the official plugins scrub it (they auto-mint a local key); pi-cognee's bootstrap only installs/spawns when the server is positively absent, so an explicit key never conflicts with it and is honored.
  • Explicit remember is redacted like auto-capture — the official plugins leave explicit remember unfiltered (prevention beats cleanup via forget).
  • Owner-key mint is lazy (first authenticated call / first successful health probe, at most once per process) rather than a synchronous session-start bootstrap.
  • Buffering is disk-backed and draining is bounded in-process — the write bridge (~/.cognee-plugin/pi/bridge/, one flat file per session) makes buffered turns survive a pi exit while the server is down; the official plugins use detached workers and cross-process lock files (pi's per-session files + atomic rename make buffer.lock/drain.lock unnecessary — the residual same-session two-terminal race is accepted, bounded); the recall breaker, by contrast, is file-shared (~/.cognee-plugin/pi/breaker.json) with an in-memory fast path, so concurrent pi processes share outage state.
  • Code-graph state is pi-scoped (~/.cognee-plugin/pi/code-graph/, mirroring the official plugins' per-integration dirs) while dataset names are the same pure function of the repo path — so a repo indexed from pi, Claude Code, or Codex resolves to the same graph, and each integration refreshes it independently.
  • Explicit code queries resolve the dataset deterministically from the git root when no index state exists (the official tools require their own state file), so cognee_code / /cognee-code also work on repos indexed by the other plugins.

Ask in a GitHub issue if you need one of the gaps.

Development

node test/smoke.mjs    # registration + pure-helper checks; no network, no server

# Live checks (disposable selftest datasets, deleted on exit; secrets never printed):
COGNEE_LIVE_BASE_URL=https://cognee.example node test/live.mjs
COGNEE_LIVE_BASE_URL=https://cognee.example node test/probe-switcher.mjs   # switcher end-to-end

# Read-only capability discovery against a real server (GETs only — asserts the
# provisioning verdicts and that no write ever leaves the process):
COGNEE_LIVE_BASE_URL=https://cognee.apps.jazm.dev node test/probe-provisioning.mjs

# ONE bounded real bootstrap (uv venv + cognee==1.6.0 + uvicorn on an ephemeral
# port, inside a throwaway state root; kills exactly what it spawned; ~10 min cap):
PI_COGNEE_BOOTSTRAP_LIVE=1 node test/bootstrap-live.mjs

tsconfig.json is strict, noEmit, types-only — editors check src/ against the real pi extension types.

License

MIT — same as cognee.