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