{
  "markdown": "<div align=\"center\">\n\n# ForkMind 🧠\n\n### Local-first LLM branching, debugging & context offloading\n\n**Treat your AI conversation history like a Git repository — capture every call, branch from any turn, and diff outcomes side by side. All on your machine.**\n\n[![npm](https://img.shields.io/npm/v/forkmind.svg)](https://www.npmjs.com/package/forkmind)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](./LICENSE)\n[![CI](https://github.com/medhovarsh/forkmind/actions/workflows/ci.yml/badge.svg)](https://github.com/medhovarsh/forkmind/actions/workflows/ci.yml)\n[![Node](https://img.shields.io/badge/node-%E2%89%A520-43853d.svg)](https://nodejs.org)\n[![MCP](https://img.shields.io/badge/MCP-registry-8a63d2.svg)](https://registry.modelcontextprotocol.io)\n[![Live site](https://img.shields.io/badge/live-medhovarsh.github.io%2Fforkmind-58a6ff.svg)](https://medhovarsh.github.io/forkmind/)\n\n[**Try in 10s**](#try-it-in-10-seconds) · [Features](#features) · [Install](#install) · [Quick start](#quick-start-free-no-api-key) · [MCP](#mcp--let-agents-query-their-own-history) · [Capsules](#context-capsules--offload-context-as-an-encrypted-dag)\n\n![ForkMind demo — navigating the conversation DAG, inspecting a node, and diffing two branches side by side](./docs/forkmind-features-demo.gif)\n\n</div>\n\n> **What you're seeing:** the conversation DAG with a fork off the debug turn,\n> the node inspector (request/response, tokens, provenance), and the **⇄ Compare**\n> view diffing two branches with per-token deltas. The green **● streaming**\n> badge shows live capture — nodes appear as they're recorded.\n\nForkMind captures every LLM call into a local `.forkmind/` directory, visualizes\nthe conversation as a Directed Acyclic Graph (DAG), and lets you **branch**,\n**diff**, and **replay** from any point in the history. Works with **any\nOpenAI-compatible API**, defaulting to **free, open-source models** via\n[Ollama](https://ollama.com) — also Anthropic, Groq, OpenRouter, Together,\nvLLM, and LM Studio.\n\n<details>\n<summary>Static screenshot</summary>\n\n![ForkMind dashboard — conversation DAG with a branch, stream badges, and the node inspector](./docs/forkmind-dashboard.png)\n\n</details>\n\n---\n\n## Why\n\nDebugging agentic / tool-calling flows means re-running the same prompt with\ntiny tweaks over and over, then scrolling through terminal logs to see what\nchanged. ForkMind records each run as a node in a conversation tree, so instead\nof re-reading logs you **see** the whole history, **branch** from any turn, and\n**compare** outcomes visually.\n\nEverything is plain JSON on disk. No database, no account, no telemetry —\nnothing leaves your machine except the LLM call you were already making.\n\n---\n\n## Features\n\n- **Capture** — every LLM call is recorded to a plain JSON node under\n  `.forkmind/`. Works from any language via an OpenAI-compatible proxy;\n  streaming responses are reconstructed (text **and** fragmented tool-call args).\n- **DAG dashboard** — a React Flow canvas draws the whole conversation as a\n  tree: every turn, tool call, model, and token count, with the node inspector\n  one click away.\n- **Live capture stream** — nodes pulse into the DAG the instant they're\n  recorded (Server-Sent Events), so you can watch an agent think in real time.\n- **Branch** — fork any historical turn: edit the prompt or swap the model and\n  re-run, linked to the original as a visible branch.\n- **⇄ Compare** — pick any two nodes for a side-by-side, word-level diff of\n  prompts and responses, plus a token-usage table with per-field deltas. \"Git\n  diff for LLM outputs.\"\n- **⏪ Time-travel replay** — re-run a whole chain from an edited node; the\n  regenerated turns land as a sibling branch while your original user turns and\n  tool results re-apply in order.\n- **MCP server** — agents query their own `.forkmind/` history mid-task (recall\n  what they tried, trace how they got somewhere, self-correct).\n- **Regression testing** — pin a known-good output as a baseline, re-run it\n  after prompt/model changes, and catch drift with free offline checks\n  (contains / regex / similarity), tool-call assertions, or an opt-in LLM judge\n  graded against your own rubric. CI-ready.\n- **Trajectory regression** — pin a whole multi-turn agent *path* from the\n  captured graph and replay it, so a prompt change that reroutes an agent\n  mid-run gets caught even when the final answer still reads fine.\n- **Context capsules** — offload context into encrypted, immutable DAG capsules;\n  restore in full or per segment; replicate (RAID), export/import, crypto-shred.\n- **`forkmind demo`** — one command opens the dashboard on a pre-seeded sample\n  DAG, zero setup and zero API key.\n\n---\n\n## Try it in 10 seconds\n\n```bash\nnpx forkmind demo\n```\n\nNo API key, no setup: the dashboard opens with a pre-seeded conversation DAG —\na coding-agent debug session that forks into a failed fix and a winning fix,\nplus an archived context capsule. Everything lives in a throwaway temp\ndirectory; your project is never touched. If a local\n[Ollama](https://ollama.com) is running, **Fork from here** works live against\nyour local model.\n\nOnce you're in, try:\n\n- **⇄ Compare** any two nodes for a side-by-side, word-level diff of prompts,\n  responses, and token usage — \"git diff for LLM outputs\".\n- **⏪ Replay from here** to re-run a whole chain from an edited node; the\n  regenerated turns land as a sibling branch.\n- **Live capture** — nodes pulse into the DAG the instant they're recorded, so\n  you can watch an agent think in real time.\n\n## Install\n\n```bash\n# Run without installing (published on npm)\nnpx forkmind init\nnpx forkmind start\n\n# …or install the CLI globally\nnpm install -g forkmind\nforkmind start\n```\n\nNo npm registry needed either — ForkMind runs straight from the git link, and\nthe dashboard builds automatically on install:\n\n```bash\n# Run without installing, from GitHub\nnpx github:medhovarsh/forkmind init\nnpx github:medhovarsh/forkmind start\n\n# …or clone to hack on it\ngit clone https://github.com/medhovarsh/forkmind\ncd forkmind && npm install\n```\n\n### Install as a Claude Code plugin\n\nForkMind ships a Claude Code plugin (skill + `/forkmind` command) so Claude knows\nwhen and how to drive it — same install flow as any marketplace plugin:\n\n```text\n/plugin marketplace add Medhovarsh/forkmind\n/plugin install forkmind\n```\n\nThe plugin bundles:\n\n- **`forkmind` skill** — Claude reaches for ForkMind whenever you ask it to debug\n  a prompt, compare models, branch from a past turn, or regression-test a call.\n- **`/forkmind` command** — start / branch / test / mcp on demand.\n- **`forkmind-debugger` agent** — runs model/prompt comparisons in an isolated\n  context and returns a compact verdict instead of dumping transcripts.\n- **MCP server, auto-wired** — agents query their own `.forkmind/` history\n  (recall attempts, trace lineage, self-correct) with zero manual config.\n\nThe CLI is still what runs the proxy + dashboard; the plugin is the glue that\nteaches Claude to use it.\n\n## Quick start (free, no API key)\n\n```bash\n# 1. Install a free local model\n#    (install Ollama from https://ollama.com first)\nollama pull llama3\n\n# 2. Init + start ForkMind\nnpx github:medhovarsh/forkmind init    # create .forkmind/ in your project\nnpx github:medhovarsh/forkmind start   # proxy on http://localhost:4500 + dashboard\n\n# 3. Point your code at the proxy (see SDK below), make some calls\n\n# 4. Open the dashboard\nopen http://localhost:4500\n```\n\n### Drop-in SDK (auto-builds the tree)\n\n```bash\nnpm i openai            # the wrapper extends the official SDK\n```\n\n```js\nconst { ForkMindOpenAI } = require('forkmind');\n\nconst client = new ForkMindOpenAI({\n  apiKey: 'ollama',                       // ignored by Ollama; required by SDK\n  upstream: 'http://localhost:11434',     // free local open-source models\n});\n\n// Each call is recorded; sequential calls auto-chain into a conversation tree.\nconst res = await client.chat.completions.create({\n  model: 'llama3',\n  messages: [{ role: 'user', content: 'Explain backpropagation simply.' }],\n});\n```\n\nRun the full example:\n\n```bash\nnode examples/chain.js\n```\n\n### Any language — point your client at the proxy\n\nThe SDK wrapper is convenience, not a requirement. ForkMind's proxy speaks the\n**OpenAI-compatible wire protocol**, so capture works from *any* language: set\nyour client's base URL to `http://localhost:4500/v1` and you're recorded. Chain\nturns into a tree by passing back the `x-forkmind-node-id` from the previous\nresponse as the next request's `x-forkmind-parent` header (the JS wrapper just\nautomates this).\n\n```python\n# Python — official openai client, zero ForkMind code\nfrom openai import OpenAI\n\nclient = OpenAI(base_url=\"http://localhost:4500/v1\", api_key=\"ollama\")\nres = client.chat.completions.create(\n    model=\"llama3\",\n    messages=[{\"role\": \"user\", \"content\": \"Explain backpropagation simply.\"}],\n    extra_headers={\"x-forkmind-upstream\": \"http://localhost:11434\"},\n)\n# read res via .with_raw_response to grab x-forkmind-node-id and chain the next call\n```\n\n```bash\n# curl — anything that can POST JSON\ncurl http://localhost:4500/v1/chat/completions \\\n  -H 'content-type: application/json' \\\n  -H 'x-forkmind-upstream: http://localhost:11434' \\\n  -d '{\"model\":\"llama3\",\"messages\":[{\"role\":\"user\",\"content\":\"hi\"}]}' -i\n# response header `x-forkmind-node-id: <id>` → pass as `x-forkmind-parent` next call\n```\n\nGo, Ruby, Rust, Java — same deal: base URL + the two headers. The dashboard,\nbranching, MCP, and regression testing all work regardless of source language.\n\n---\n\n## Framework integrations\n\nForkMind ships thin adapters for the two biggest JS LLM ecosystems. Both route\nthrough the same proxy, so capture, branching, the dashboard, MCP, and\nregression all work unchanged — no model-class swap, no callbacks.\n\n### LangChain.js\n\n```bash\nnpm i @langchain/openai @langchain/core\n```\n\n```js\nconst { ChatOpenAI } = require('@langchain/openai');\nconst { forkmind } = require('forkmind/langchain');\n\nconst fm = forkmind({ upstream: 'http://localhost:11434' }); // free local Ollama\nconst model = new ChatOpenAI({\n  apiKey: 'ollama',\n  model: 'llama3',\n  configuration: fm.configuration, // baseURL → proxy + chaining fetch\n});\n\nawait model.invoke('Explain backpropagation simply.');\n// sequential calls on `fm` auto-chain; fm.setParent(id) to branch from a node.\n```\n\n### Vercel AI SDK\n\n```bash\nnpm i ai @ai-sdk/openai\n```\n\n```js\nconst { generateText } = require('ai');\nconst { forkmindOpenAI } = require('forkmind/vercel');\n\nconst openai = forkmindOpenAI({ upstream: 'http://localhost:11434' });\nconst { text } = await generateText({\n  model: openai('llama3'),\n  prompt: 'Explain backpropagation simply.',\n});\n// openai.setParent(id) / openai.resetParent() control the branch point.\n```\n\nBoth honor `FORKMIND_PROXY` (proxy base URL) and take an explicit `baseURL` /\n`upstream` per instance.\n\n---\n\n## Using other free / open providers\n\nForkMind is provider-agnostic — it forwards your auth headers verbatim and lets\nyou set the upstream per client. Anything OpenAI-compatible just works:\n\n| Provider              | `upstream`                          | `apiKey`             |\n| --------------------- | ----------------------------------- | -------------------- |\n| **Ollama** (local)    | `http://localhost:11434`            | any string           |\n| **LM Studio** (local) | `http://localhost:1234`             | any string           |\n| **Groq** (free tier)  | `https://api.groq.com/openai`       | `gsk_...`            |\n| **OpenRouter**        | `https://openrouter.ai/api`         | `sk-or-...`          |\n| **Together**          | `https://api.together.xyz`          | your key             |\n| **OpenAI**            | `https://api.openai.com` (default)  | `sk-...`             |\n\n```js\nnew ForkMindOpenAI({ apiKey: process.env.GROQ_API_KEY,\n                     upstream: 'https://api.groq.com/openai' });\n```\n\nYou can also override per request with the `x-forkmind-upstream` header if you\ncall the proxy directly instead of via the SDK.\n\n### Anthropic (Claude)\n\n```bash\nnpm i @anthropic-ai/sdk\n```\n\n```js\nconst { ForkMindAnthropic } = require('forkmind');\nconst client = new ForkMindAnthropic({ apiKey: process.env.ANTHROPIC_API_KEY });\nawait client.messages.create({ model: 'claude-3-5-sonnet-latest', max_tokens: 512,\n                               messages: [{ role: 'user', content: 'hi' }] });\n```\n\n---\n\n## How it works\n\n```\nyour app ──▶ ForkMindOpenAI (baseURL = localhost:4500/v1)\n                │  injects x-forkmind-parent\n                ▼\n         ForkMind proxy (Express, :4500)\n                │  forwards verbatim (your key, your upstream)\n                ▼\n         provider (Ollama / Groq / OpenAI / ...)\n                │  response\n                ▼\n         proxy reconstructs + saveNode()  ──▶  .forkmind/nodes/<id>.json\n                │  returns x-forkmind-node-id\n                ▼\n         wrapper chains it as the next call's parent\n```\n\n- **Deterministic node IDs.** `sha256(request + parentId)` → first 12 hex chars.\n  Same prompt under the same parent collapses to one node. The ID doesn't depend\n  on the response, so it can be returned as a header even before a streamed body\n  finishes.\n- **Streaming.** Bytes pass through to your app untouched (real SSE); the proxy\n  tees them, reconstructs the full message (text **and** fragmented tool-call\n  arguments), and saves the node on stream end.\n- **Branching.** Each node records its provider + upstream, so \"Fork from here\"\n  in the dashboard replays the edited request to the same host, linked to the\n  historical parent.\n- **Compare.** Any two nodes diff side by side — word-level prompt/response\n  changes and a token table with signed deltas — computed client-side from the\n  captured JSON (`dashboard/src/lib/diff.js`).\n- **Replay.** `POST /api/replay` walks a captured lineage from an edited node to\n  a chosen leaf, regenerating each assistant turn against the modified history\n  while original user turns and tool results re-apply verbatim. The new chain is\n  saved as a sibling branch.\n- **Live stream.** `saveNode` emits on an in-process bus; `GET /api/stream`\n  relays each new node to the dashboard over SSE, so the canvas updates without\n  polling.\n\n---\n\n## MCP — let agents query their own history\n\nForkMind ships an [MCP](https://modelcontextprotocol.io) server so an AI agent\ncan read its own `.forkmind/` history mid-task and self-correct — recall what it\nalready tried, see how it reached a state, or search past attempts.\n\n```bash\nforkmind mcp          # stdio MCP server (or: forkmind-mcp)\n```\n\nOne-line install via [Smithery](https://smithery.ai) (configured in\n[`smithery.yaml`](./smithery.yaml)) — run it from your project root so it sees\nyour `.forkmind/`:\n\n```bash\nnpx -y @smithery/cli install forkmind --client claude\n```\n\n…or register it manually with any MCP client (Claude Desktop / Claude Code /\nCursor / Cline):\n\n```jsonc\n{\n  \"mcpServers\": {\n    \"forkmind\": {\n      \"command\": \"npx\",\n      \"args\": [\"-y\", \"github:medhovarsh/forkmind\", \"mcp\"]\n    }\n  }\n}\n```\n\nTools exposed:\n\n| Tool                | Purpose                                                   |\n| ------------------- | -------------------------------------------------------- |\n| `forkmind_recent`   | Newest captured turns (compact)                          |\n| `forkmind_get_node` | Full request + response for one node                     |\n| `forkmind_lineage`  | Root→node path — the exact context that produced a state |\n| `forkmind_children` | Sibling branches forking from a node                     |\n| `forkmind_search`   | Substring search across all requests/responses           |\n| `forkmind_stats`    | Tree totals: nodes, roots, leaves, providers             |\n| `forkmind_context_save`    | Offload context into an encrypted DAG capsule      |\n| `forkmind_context_list`    | List saved capsules (title, digest, size, age)     |\n| `forkmind_context_digest`  | Digest + segment map — cheap pre-restore probe     |\n| `forkmind_context_restore` | Full or per-segment restore, integrity-verified    |\n| `forkmind_context_forget`  | Irreversible crypto-shred (requires id echo)       |\n| `forkmind_context_replicas`| Replica (RAID) health, optional sync               |\n| `forkmind_context_stats`   | Aggregate stats: count, bytes, estimated tokens    |\n| `forkmind_context_export`  | Portable passphrase-encrypted bundle               |\n| `forkmind_context_import`  | Import + re-verify a bundle, re-wrap locally       |\n\nThe server reads the `.forkmind/` in its working directory — point the client's\n`cwd` at your project.\n\n## Context capsules — offload context as an encrypted DAG\n\nMost context managers treat a full window as a cache-eviction problem: truncate\nand lose it. ForkMind **capsules** invert that — *persist first, verify, then\ncompact*. A capsule is an immutable, content-addressed DAG of context segments,\nAES-256-GCM encrypted on disk, restorable in full or one segment at a time.\n\n```bash\n# Save (items JSON from a file or stdin), get back a 12-char handle\necho '{\"title\":\"auth debug\",\"items\":[{\"role\":\"user\",\"content\":\"...\"}]}' \\\n  | forkmind context save --digest \"oauth loop root-caused; fix in token.js\"\n\nforkmind context list                 # all capsules, newest first\nforkmind context show 9f3ac21b7e04    # decrypt + verify + print\nforkmind context verify 9f3ac21b7e04  # DAG integrity: parents, acyclicity, hashes\nforkmind context forget 9f3ac21b7e04 --confirm 9f3ac21b7e04   # crypto-shred\n```\n\nSame engine over HTTP (`POST/GET/DELETE :4500/api/context…`) and via five MCP\ntools, so agents can archive their own context mid-task and pull it back later.\nThe Claude Code plugin ships a **`forkmind-archivist`** skill + subagent that\nteaches Claude the offload contract: **save → verify on disk → only then drop\nit from the window**.\n\nGuarantees:\n\n- **Immutable & acyclic by construction** — segment ids are hashes over\n  content + parents (Git-style); a cycle would require a hash to contain itself.\n- **No plaintext at rest** — per-capsule keys, wrapped by a master key stored\n  *outside* `.forkmind/` (`~/.forkmind-keys/`); an accidentally committed\n  `.forkmind/` leaks only ciphertext and structure.\n- **Digests are opt-in** — the agent writes a ≤5-line retrieval summary, or\n  omits it entirely for private capsules.\n- **Forgetting is real** — delete destroys the key first (crypto-shredding),\n  then tombstones the id so identical content can never resurrect it.\n- **The model is never touched** — capsules operate on what the client sends;\n  provider, weights, and KV cache are out of scope by design.\n\n### RAID — Redundant Array of Independent DAGs\n\nMirror capsules to any number of extra filesystem targets (second disk, synced\nfolder, network mount). Replicas hold **ciphertext + manifests only — keys are\nnever replicated**. If the primary copy is lost or bit-rots, restore self-heals\nfrom the first replica that passes verification; healed copies get no trust\nshortcut (full integrity check still runs).\n\n```bash\nforkmind context replicas add D:\\backup\\forkmind   # add target + sync\nforkmind context replicas list                     # coverage per target\nforkmind context replicas sync                     # catch up offline targets,\n                                                   # propagate tombstones\n```\n\nForgetting reaches every copy: reachable replicas are shredded immediately;\na replica that was offline gets its stale ciphertext removed on the next\n`sync` (tombstone propagation) — and it was unreadable anyway, since the\ncapsule key died at forget time. Tombstones also make heal refuse to\nresurrect anything forgotten.\n\n### Portable export/import\n\nMove a capsule to another machine or project — a laptop that doesn't share\nthis project's `~/.forkmind-keys/` master key, a teammate, cold storage:\n\n```bash\nforkmind context export 9f3ac21b7e04 --passphrase \"correct horse battery staple\" --out capsule.json\n# ... move capsule.json anywhere ...\nforkmind context import capsule.json --passphrase \"correct horse battery staple\"\n```\n\nThe bundle carries its own scrypt-derived key material (N=32768, deliberately\nslow to resist offline brute force of a weak passphrase) — it never depends\non the source machine's master key, and the passphrase is never written into\nthe bundle itself. On import, every segment is independently re-verified\n(recomputed id, recomputed hash, resolved parents, acyclic DFS) before\nanything touches disk — the bundle is never trusted blindly, only proven.\nImport is idempotent and honors tombstones, same as a fresh save.\n\n### Archive straight from the capture DAG\n\nThe two halves connect: any conversation the proxy captured can be archived\ninto a capsule in one move — no manual JSON assembly — and restored later as\na provider-ready `messages[]` array, ready to splice into the next request:\n\n```bash\n# archive the whole lineage ending at a captured turn\nforkmind context save --from-node a1b2c3d4e5f6 --digest \"auth debug, resolved\"\n\n# restore as chat messages (or GET /api/context/:id/messages)\nforkmind context show 9f3ac21b7e04 --messages\n```\n\nSame via MCP: `forkmind_context_save { fromNodeId }` and\n`forkmind_context_restore { asMessages: true }` — an agent can archive its own\ncaptured history mid-task and splice it back whenever needed. Capsules keep\n`sourceNodeIds` links back into the turn DAG.\n\n### Token savings\n\n`forkmind context save` and `forkmind context stats` report an estimated\ntoken count freed from your context window (`~4 bytes/token`, the standard\nrough heuristic) — a concrete number for how much a capsule actually saved.\n\n## Regression testing — pin good outputs, catch degradation\n\nTweaking a system prompt or swapping a model can silently degrade results.\nForkMind lets you pin a known-good captured node as a **baseline**, then re-run\nits exact request later and check the new output for drift.\n\n```bash\n# 1. Pin a good node (grab its id from the dashboard or forkmind_recent)\nforkmind regression pin a1b2c3d4e5f6 \\\n  --name octopus-fact \\\n  --contains \"hearts\" \\\n  --regex \"blue|copper\" \\\n  --min-similarity 0.5\n\n# 2. List / remove cases\nforkmind regression list\nforkmind regression remove octopus-fact\n\n# 3. Re-run after changing prompts/models (exit code 1 if any case fails — CI-ready)\nforkmind regression run                 # keyless local (Ollama)\nforkmind regression run --key $GROQ_API_KEY --upstream https://api.groq.com/openai\n```\n\n### Mechanical checks — free, offline, deterministic\n\n- **`contains`** — substrings that must appear\n- **`not-contains`** — substrings that must NOT appear\n- **`regex`** — patterns that must match\n- **`min-similarity`** — Jaccard word-overlap vs the baseline (drift guard;\n  defaults to `0.3` so a wildly different answer fails even without explicit\n  assertions). LLM output is non-deterministic, so prefer assertions over exact\n  match.\n\nNone of these read meaning. They answer *\"does this text still look like that\ntext\"*, not *\"is this answer still correct\"*. `contains` and `regex` are precise\nproxies — if a required fact disappears, they catch it every time. Similarity is\na deliberately cheap alarm: it flags that something changed, and it will cry wolf\non a rewrite that's perfectly correct.\n\n### Tool-call checks — what the agent DID, not what it said\n\nText checks ask whether the answer still reads right. For an agent, that's the\nwrong question. A wrong sentence is annoying; a wrong tool call writes to\nsomebody's system. These assert on the calls themselves — structured data, so\nthey're free, offline, and exact:\n\n```bash\nforkmind regression pin a1b2c3d4e5f6 \\\n  --name refund-flow \\\n  --tool 'create_ticket:{\"priority\":\"high\"}' \\  # must call it, with these args\n  --not-tool issue_refund \\                     # must never call this\n  --tools-exact                                 # and must call nothing else\n```\n\n- **`--tool name`** / **`--tool 'name:{json}'`** — the call must appear.\n  Arguments match as a **subset**, so you pin the fields that matter and ignore\n  the rest.\n- **`--not-tool name`** — the destructive-action guard. The check that matters\n  most when a prompt tweak makes an agent bolder than it should be.\n- **`--tools-exact`** — fail on any *extra* call, not just a missing one. With\n  no `--tool` at all, this asserts the agent acted on nothing.\n\nWorks identically on OpenAI `tool_calls` and Anthropic `tool_use` blocks; both\nnormalize to `{ name, args }`. A model that emits malformed argument JSON is\nreported with the raw text under `args._raw` rather than silently dropped, so\nthe assertion fails loudly instead of the call disappearing.\n\nThe failure mode this exists for: **the text can be word-for-word identical\nwhile the action is wrong.** Every text check passes, similarity scores 1.0, and\nthe agent charged the wrong account. Only the tool check catches it.\n\n### LLM judge — opt-in, costs an API call, reads meaning\n\nFor the cases where wording is free to change but the *content* must not, pin a\nrubric and let a model grade the replay against it:\n\n```bash\nforkmind regression pin a1b2c3d4e5f6 \\\n  --name refund-policy \\\n  --judge \"The answer must state the 30-day window and must not promise an exception.\" \\\n  --judge-threshold 0.8 \\\n  --judge-model gpt-4o          # optional: grade with a stronger model than the case uses\n\nforkmind regression run --judge-key $OPENAI_API_KEY\nforkmind regression run --no-judge   # mechanical checks only — free, offline, no API calls\n```\n\nThe judge sees the rubric, the approved baseline, and the candidate, and is told\nexplicitly **not** to penalize rewording — only content that is wrong, missing,\nor contradictory. It returns a `0-1` score; the case fails below\n`--judge-threshold` (default `0.7`).\n\nThree properties worth knowing before you trust it:\n\n- **It fails closed.** A judge that errors, times out, or returns unparseable\n  output marks the check **failed**, never skipped. A gate that silently passes\n  when its grader is broken is worse than no gate.\n- **A skip is visible.** `--no-judge` still records the check, flagged as\n  skipped in the report, so a suite can't quietly stop enforcing its rubric.\n- **It is not proof.** The judge is non-deterministic and only as good as the\n  rubric you wrote. It is a stronger signal than word overlap — not a\n  correctness guarantee.\n\nCases are JSON in `.forkmind/regressions/` — commit them to share baselines and\ngate prompt changes in CI.\n\n## Trajectory regression — pin the path, not the turn\n\nEverything above tests **one turn**. For an agent that's the wrong unit. Agents\nfail in the *middle* of a run — they skip the lookup, they write before they\nconfirm, a prompt tweak reroutes them entirely — and then produce a final\nsentence that reads completely fine. A last-message assertion sees nothing.\n\nBecause ForkMind captures real traffic as a parent-linked DAG, a whole path is\nalready sitting there. Freeze it:\n\n```bash\n# Pin the path ending at this node (walks up to the root)\nforkmind trajectory pin f4e5d6c7b8a9 \\\n  --name refund-run \\\n  --sequence exact \\                 # same actions, same order\n  --not-tool issue_refund \\          # never, anywhere in the run\n  --tool-order verify_identity,charge_card\n\nforkmind trajectory list\nforkmind trajectory run              # exit 1 on any failure — CI-ready\n```\n\nWhat gets checked across the whole run:\n\n- **`--sequence exact`** — the flattened action sequence must match the baseline\n  step for step. **`subsequence`** allows extra actions as long as the baseline\n  ones still appear in order. **`none`** leaves the route free.\n- **`--not-tool`** — a forbidden action anywhere in the trajectory.\n- **`--tool-order before,after`** — ordering constraints. *Searched before it\n  wrote. Verified before it charged.*\n- **`--judge`** — optional rubric on the **final** answer, same judge as above.\n- **`--from <nodeId>`** — start the path at an ancestor instead of the root, so\n  you can pin just the interesting tail of a long run.\n\n**Text is deliberately unconstrained.** Reworded output passes. Only the route\nis pinned — because that's the part that touches other people's systems.\n\n### The limitation, stated plainly\n\nReplaying a path **re-applies the original recorded tool results**. Tools are not\nexecuted live. That's deliberate — it holds the environment fixed so the only\nvariable is the model's decisions — but it has a hard consequence: the moment the\nagent takes a *different* action, the recorded result waiting for it answers a\nquestion it never asked. Everything after that point would be fiction.\n\nSo the run **stops at the first divergence** and reports exactly where:\n\n```\n  ✗ FAIL  refund-run  (2/3 steps)\n         path: verify_identity → issue_refund\n         ↳ failed divergence: step 2/3: expected [charge_card], got [issue_refund]\n           — replay stopped (recorded tool results no longer apply)\n```\n\nThat failure is the whole point of the feature: the final message would have\nread fine.\n\nTrajectories are JSON in `.forkmind/trajectories/` — commit them alongside your\nsingle-turn cases.\n\n## Zero cost & local\n\n- **No paid API required** — defaults to free local models via Ollama.\n- **No database** — every turn is a plain JSON file under `.forkmind/`.\n- **No account, no telemetry** — nothing leaves your machine except the LLM call\n  you were already making (relayed verbatim to the provider you choose).\n\n## `.forkmind/` layout\n\n```\n.forkmind/\n├── nodes/\n│   ├── a1b2c3d4e5f6.json     # one node per turn\n│   └── ...\n├── contexts/                 # encrypted context capsules\n│   └── 9f3ac21b7e04/\n│       ├── manifest.json     # public: DAG shape, hashes, opt-in digest\n│       └── seg-<id>.enc      # AES-256-GCM ciphertext per segment\n├── tombstones.json           # forgotten capsule ids (never resurrected)\n└── manifest.json            # version + root node ids\n```\n\nNode schema:\n\n```jsonc\n{\n  \"id\": \"a1b2c3d4e5f6\",\n  \"parentId\": null,           // null = root\n  \"timestamp\": \"2026-01-01T00:00:00.000Z\",\n  \"request\":  { /* the exact request body */ },\n  \"response\": { /* full or stream-reconstructed response */ },\n  \"meta\": { \"provider\": \"openai\", \"upstream\": \"http://localhost:11434\", \"stream\": true },\n  \"children\": [\"...\"]         // child node ids\n}\n```\n\n---\n\n## CLI\n\n| Command            | Does                                                     |\n| ------------------ | -------------------------------------------------------- |\n| `forkmind demo`    | Zero-setup showcase: sample DAG + dashboard in a temp dir |\n| `forkmind init`    | Create `.forkmind/` in the current directory             |\n| `forkmind start`   | Start the proxy (`:4500`) + serve the dashboard if built |\n| `forkmind mcp`     | Start the stdio MCP server for agents                    |\n| `forkmind regression pin/list/remove/run` | Pin baselines and re-run to catch drift |\n| `forkmind trajectory pin/list/remove/run` | Pin multi-turn agent paths and catch rerouting |\n| `forkmind context save/list/show/verify/forget` | Encrypted context capsules (see above) |\n\nEnv vars: `FORKMIND_PORT`, `FORKMIND_HOST` (default `127.0.0.1` — loopback\nonly; set `0.0.0.0` to expose on the LAN at your own risk),\n`FORKMIND_OPENAI_UPSTREAM`, `FORKMIND_ANTHROPIC_UPSTREAM`, `FORKMIND_PROXY`\n(SDK target base URL), `FORKMIND_KEY_DIR` (capsule master-key location,\ndefault `~/.forkmind-keys`).\n\n---\n\n## Development\n\n```bash\nnpm install            # installs proxy + dashboard (npm workspaces)\nnpm test               # jest: hashing, storage, stream reconstruction, API\nnpm run dashboard:dev  # vite dev server on :5173, proxies API to :4500\nnpm run dashboard:build\nnpm run lint\n```\n\n### Releasing to npm\n\nPublishing is tag-driven via `.github/workflows/release.yml` (needs an\n`NPM_TOKEN` repo secret with publish rights):\n\n```bash\nnpm version patch        # bumps package.json + tags\ngit push --follow-tags   # tag push → CI lints, tests, builds dashboard, publishes\n```\n\n`prepack` rebuilds `dashboard/dist` so the tarball always ships the UI.\n\nSee [CONTRIBUTING.md](./CONTRIBUTING.md).\n\n---\n\n## Roadmap\n\n- [x] CLI + deterministic storage engine\n- [x] Provider-agnostic proxy (OpenAI-compatible + Anthropic) with streaming\n- [x] Drop-in SDK wrappers with auto-chaining\n- [x] React Flow dashboard + branch execution\n- [x] MCP integration — let agents query their own `.forkmind/` history\n- [x] Automated regression: pin \"good\" branches, re-run on prompt edits\n- [x] Context capsules — offload context as an encrypted, immutable DAG;\n      restore in full or per segment; crypto-shred to forget\n- [x] RAID — Redundant Array of Independent DAGs: replicate capsules across\n      filesystem targets with self-healing restore and tombstone propagation\n- [x] Capsule export/import — portable, passphrase-encrypted bundles to move\n      context between machines and projects, independently re-verified on import\n- [x] Dashboard capsule panel — browse capsules, inspect the DAG segment map,\n      and run integrity verification from the UI (read-only by design)\n- [x] `forkmind demo` — zero-setup sample DAG + dashboard, no API key\n- [x] Branch diff view — side-by-side node compare with word-level highlighting\n      and per-token deltas\n- [x] Time-travel replay — re-run a whole chain from an edited node as a\n      sibling branch\n- [x] Live capture stream — SSE push so nodes appear in the DAG in real time\n\n## License\n\n[MIT](./LICENSE)\n",
  "bytes": 33349,
  "sha": "b0e33984ac02ba255b3aed280d36e93e14f3722e33b293ed6b9ba5bf909e5003",
  "repo_slug": "medhovarsh/forkmind",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/mcp_io_github_medhovarsh_forkmind_259c3b4e/readme"
}