Skip to content

toddgilbert93/Horatio

Repository files navigation

Horatio

A flight recorder for MCP-driven Blender work. Horatio sits transparently between your MCP client (Claude Desktop / Claude Code / Cursor) and blender-mcp, records every message, distills the traffic with NVIDIA Nemotron into persistent per-file memory, and serves that memory back to agents over MCP — so every future session warm-starts instead of cold-starts.

Claude ──stdio──> tap ──stdio──> uvx blender-mcp ──tcp──> Blender
                   │
                   └──append──> raw.jsonl ──tail── distiller (Nemotron)
                                                      ├── digest.jsonl   (activity, live)
                                                      └── note.md + blends/<id>/memory.md
Claude ──stdio──> memory-server ──reads──> file memory + notes
                                           (recall / search_sessions / log_decision)

Core invariant: raw.jsonl is the source of truth. Everything downstream is re-derivable (horatio rebuild). The only other source of truth is decisions.jsonl (agent-authored via log_decision).

Deeper docs: architecture & data flow · desktop app internals · why Nemotron · contributor guide (CLAUDE.md).

Why Nemotron

  • Continuous ambient distillation (summarizing every session, all session long) is only economical on a fast, cheap, open model — trivial on nvidia/nemotron-3-super-120b-a12b, absurd on frontier API pricing.
  • Its 1M context enables one-pass full-transcript synthesis experiments.
  • Open weights mean the same system can run fully local (Nemotron Nano via Ollama) — always-on, private, zero token cost.

Setup

Desktop app (recommended)

The Electron app is the viewer + control plane: browse sessions (notes, digests, raw, artifacts), manage the NVIDIA API key, wrap/unwrap MCP servers, and fix store paths if a client is writing to the wrong home.

Build a downloadable macOS app (unsigned .dmg — right-click → Open the first time):

npm install && npm run desktop:install
npm run desktop:dist          # → desktop/release/*.dmg

Or run from the repo (use a normal terminal, not an environment that sets ELECTRON_RUN_AS_NODE):

npm run desktop:install       # once
npm run desktop               # build + Electron hot reload
# or, after desktop:install:
cd desktop && npm start       # production build of the UI, no Vite

Projects are Blender files, not arbitrary names. Sessions live in a flat sessions/ tree and are linked (never moved) to a blends/<id>/ bucket when a .blend path shows up in MCP traffic or you click Link .blend…. Memory hangs off that file; Reveal / Open jump to it on disk.

CLI

npm install && npm run build
cp .env.example .env   # or create .env with: NVIDIA_API_KEY=nvapi-...
node dist/distill/cli.js install
node dist/distill/cli.js install --wrap blender
node dist/distill/cli.js uninstall

install --wrap sets HORATIO_HOME to the platform app-data folder (~/Library/Application Support/Horatio on macOS). Optional --project-dir <path> overrides to <path>/.horatio. Restart the MCP client after install so wraps pick up the new home.

Legacy FLIGHTREC_* env vars still resolve (with a deprecation warning). Older flightrec stores need a one-time horatio migrate.

install finds Claude Code, Claude Desktop, and Cursor configs automatically, wraps the named server with the tap, and registers the memory server alongside. Claude Code uses the official claude mcp CLI; Desktop and Cursor get surgical JSON edits with a timestamped backup and atomic writes. Wrapped entries are self-describing, so uninstall restores the originals without a state file. Use --client <claude-code|claude-desktop|cursor> to target one client, or --config <path> for any config with the standard mcpServers shape.

Manual config (what install writes)
{
  "mcpServers": {
    "blender": {
      "command": "node",
      "args": ["/ABSOLUTE/PATH/Horatio/dist/tap.js", "--", "uvx", "blender-mcp"],
      "env": {
        "HORATIO_HOME": "/Users/you/Library/Application Support/Horatio"
      }
    },
    "horatio-memory": {
      "command": "node",
      "args": ["/ABSOLUTE/PATH/Horatio/dist/memory-server.js"],
      "env": {
        "HORATIO_HOME": "/Users/you/Library/Application Support/Horatio"
      }
    }
  }
}

The tap auto-spawns the activity follower per session (opt out with HORATIO_NO_ACTIVITY=1 / legacy FLIGHTREC_NO_AUTODISTILL=1 and run node dist/distill/cli.js watch latest in a terminal instead — nicer for demos).

Commands

node dist/distill/cli.js watch [session]              # live activity → digests; auto-update memory at session end
node dist/distill/cli.js update [session]             # session note.md + fold into blends/<id>/memory.md
node dist/distill/cli.js rebuild [session]            # wipe digests, re-derive activity, then update memory
node dist/distill/cli.js export-memory [session] [--out path] [--assemble]
node dist/distill/cli.js migrate [--dry-run]          # one-time flightrec (v1) → Horatio (v2) store
npm run desktop                                       # viewer + control plane

Legacy aliases: distill --followwatch, --saveupdate, --replayrebuild.

update / “Update memory” in the app folds digests into durable file memory. Live capture mainly writes activity (Tier 1); synthesis runs at session end, on idle, or when you update manually. recall() returns existing notes/memory — it does not synthesize on demand.

export-memory writes agent-memory.md into the session folder (and optionally --out to your coding-agent project). It uses Nemotron to produce an agent-oriented brief from the session note + file memory; pass --assemble to skip the LLM and concatenate mechanically.

session = latest (default), a session id, or a path.

Store layout

Canonical app-owned store (default, v2):

~/Library/Application Support/Horatio/   # macOS
~/.config/horatio/                       # Linux
%APPDATA%/Horatio/                       # Windows
  config.json                            # storeVersion: 2
  .env                                   # NVIDIA_API_KEY (optional; also loads repo .env)
  live/<session-id>.json                 # per-tap live pointers
  sessions/<ISO-id>/                     # flat — sessions never move
    raw.jsonl  digest.jsonl  note.md  distill.log  artifacts/
    session.json  link.json  memory.json
    agent-memory.md                      # optional export
  blends/<basename>-<hash8>/             # one folder per Blender file
    meta.json                            # { blendPath, name, … }
    memory.md                            # durable cross-session file memory
    decisions.jsonl
    memory-history/                      # recent memory.md archives

Project-local override (--project-dir or HORATIO_HOME=<workspace>/.horatio):

<workspace>/.horatio/
  (same shape as above)

If an older wrap still points at a repo .flightrec / flightrec app-data path, use Preferences → Fix store paths (or install --repair-store / horatio migrate), then restart the client.

Memory tools (MCP)

  • recall — judgment-first warm start for the active .blend (decisions / constraints / failures / threads first; scene inventory last and marked historical). Warns if the .blend file is newer than last memory update. Agents must re-check the live scene with Blender MCP. Resolves from optional file arg → live session link → most recently linked file.
  • search_sessions(query) — substring search across notes, digest events, and file memory, with session/batch citations. No embeddings; at 1M context, brute force is the architecture.
  • log_decision(text) — record a durable decision from the driving agent; memory is bidirectional.

Guarantees

  • The tap is transparent: byte-for-byte forwarding, verified byte-identical against un-tapped runs; logging failures can never affect forwarding; base64 screenshots never reach the log.
  • No speculation in distilled memory: every digest event cites the raw-record seq numbers supporting it; error strings are verbatim; hallucinated causality is prompt-forbidden and schema-policed — bad memory poisons future sessions, so incomplete beats invented.
  • Batch boundaries are deterministic (record-timestamp gaps, not wall clock): rebuild reproduces the same batches a live run made.

About

Horatio — a flight recorder for MCP-driven Blender work: capture traffic, distill with NVIDIA Nemotron into persistent memory, serve it back via MCP.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

No releases published

Packages

 
 
 

Contributors