diff --git a/content/docs/changelog.mdx b/content/docs/changelog.mdx index 18e2d38..e6b6e1a 100644 --- a/content/docs/changelog.mdx +++ b/content/docs/changelog.mdx @@ -14,6 +14,18 @@ breaking changes require a major bump. ## Unreleased +### Memory evidence and hardening + +- Opt-in scoped fact evidence, partial answers and persistent conflict warnings; + a rebuildable schema-15 projection binds facts to sources and claim revisions. +- Durable corrections, bounded delivery, conservative lifecycle decisions and + atomic replay/sync. Remote context honors the server's disabled reranker. +- The [paired answerability study](/docs/memory-benchmark/answerability) retained + 24/27 hits and reduced unwanted injections 15/18 → 3/18 on its new synthetic + fixture. Exact metadata matched 36/45 in both repeats; p95 was 376.6 → 386.6 ms. + Compound retrieval and unsupported-subject fallback remain incomplete, and + the guard remains disabled by default. + ### Security - Updated transitive `h2` from 0.4.14 to 0.4.16, fixing diff --git a/content/docs/how-kimetsu-works/configuration.mdx b/content/docs/how-kimetsu-works/configuration.mdx index e24c791..8084ff0 100644 --- a/content/docs/how-kimetsu-works/configuration.mdx +++ b/content/docs/how-kimetsu-works/configuration.mdx @@ -5,6 +5,15 @@ description: "Every knob in project.toml, the off-switches, and the environment Every knob in `project.toml`, the off-switches, and the environment overrides. +## Unreleased fact guard + +On builds containing structured fact evidence, `broker.explicit_fact_guard` +defaults to `false`. Enable with `kimetsu config set broker.explicit_fact_guard true`. +It adds scoped supported/missing/conflicting evidence without extra model calls; +injected context still consumes tokens. See [results, storage behavior and +limitations](/docs/memory-benchmark/answerability). This setting is unreleased +and is deliberately separate from the published configuration example below. + Project config lives in `/project.toml`: ```toml @@ -39,7 +48,7 @@ budget_run_cap_tokens = 8000 # per-run ceiling on injected tokens compress_capsules = true # strip tags, cap at 3 sentences; ranking unaffected session_dedupe = true # skip capsules already injected this session warm_start = true # SessionStart injects the digest + episodic resume -answer_grade_min_score = 0.92 # top capsule >= this gets a "Verified answer" prefix +answer_grade_min_score = 0.92 # relevance marker; not independent verification proactive_prefetch = false # opt-in trajectory-based pre-fetch at PreToolUse [storage] diff --git a/content/docs/memory-benchmark/answerability.mdx b/content/docs/memory-benchmark/answerability.mdx new file mode 100644 index 0000000..4feca60 --- /dev/null +++ b/content/docs/memory-benchmark/answerability.mdx @@ -0,0 +1,83 @@ +--- +title: "Structured answerability (unreleased)" +description: "Paired evidence-delivery measurements, configuration and remaining gaps for the opt-in fact guard." +--- + +This is **unreleased, opt-in work**, not a new overall BrainBench score or a +claim about generated-answer accuracy. It adds source-bound configuration facts +and reports which requested attributes are supported, missing or conflicting. + +## Paired results — September 7, 2026 + +The campaign contains 688 observations over 299 distinct scenario/query cases. +Both sides enable the fact guard; the baseline is the preceding answerability +build and the candidate adds structured evidence. No positive-hit losses or +execution errors were observed. + +| Fixture | Positive hits, old → new | Unwanted injections, old → new | Subsequent p95, old → new | +|---|---|---|---| +| Development: 210 queries | 169/197 → 169/197 | 7/13 → 7/13 | 1,111.7 → 1,094.0 ms | +| Prior answerability: 44 queries | 24/24 → 24/24 | 0/20 → 0/20 | 409.1 → 380.7 ms | +| New structured facts: 45 queries, two repeats | 24/27 → 24/27 | 15/18 → 3/18 | 376.6 → 386.6 ms | + +On the new fixture, **80% fewer unwanted injections** means 15 failures became +3, or 83.3% → 16.7% of the 18 negative cases. Exact status, values, missing +attributes and conflicts matched **36/45 cases in each repeat**: 33/42 direct +fact questions and all three broad controls. Rankings and metadata meaning +were stable across repeats. + +The new-fixture p95 increased about **2.7%**, and mean MCP result size increased +from **613.5 to 651.2 bytes** (6.2%). Peak MCP working set remained about +653.5 MiB. These short runs do not establish zero overhead or a statistically +significant speed change. + +All runs used cached BGE-small embeddings, default thread settings and a 6,000 +output budget. Development used TinyBERT with cutoff 0.30; the other fixtures +used the optional quantized mMARCO reranker with cutoff 0.55. No model default +was promoted. The byte budget includes serialization and is not billed tokens. + +## What changes for an agent + +With `Orchid staging gateway port is 7319.` recorded, a recognized port-and-timeout +question can return port evidence and identify timeout as missing. Production +evidence cannot fill a staging request. Conflicting eligible values stay marked +as conflicting even when a capsule cap or budget removes one source. Exact +unit comparison treats `30 seconds` and `30000 ms` as equivalent. + +On a build containing this change, enable it with: + +```sh +kimetsu config set broker.explicit_fact_guard true +``` + +The default is `false`; set it back to `false` to disable it. Published packages +may not contain this unreleased feature. An older warm daemon must be restarted +with the updated build to use its new behavior. + +Schema 15 maintains a rebuildable SQLite projection bound to claim revisions, +source events, validity and exact evidence excerpts. Corrections and replay +refresh the projection; the tagged agent record path is covered. Ordinary +retrieval skips fact hydration when the guard is disabled, but writes still +maintain the projection. No extra model calls are needed; evidence sent to an +agent still consumes context tokens. + +## Remaining gaps and measurement limits + +Six compound-question cases per repeat omitted port evidence: three retrieved +nothing and three retrieved retries alone. Hit@4 counts the latter as hits, +which shows why retrieval hit rate is not complete answerability. Three cases +using the subject word `Unknown` fell outside the conservative grammar and +returned unrelated evidence through legacy fallback. These account for the +remaining unwanted injections. + +The fixture is assistant-authored and repeats templates across three project +names. It is not 45 independent task families, does not measure an LLM reader, +and cannot certify corpus-wide conflict detection. No grammar, parameters or +expectations changed after these results. Retrieval per requested attribute and +safer unsupported-subject handling remain follow-up work. + +The tested implementation passed 1,470 workspace tests (six ignored), 132 +benchmark Rust tests, 18 Python tests and six release CLI/MCP probes. + +Source: [full report, frozen fixtures, raw observations and hashes](https://github.com/RodCor/kimetsu/blob/281c6df/docs/audits/2026-09-07-structured-facts.md). + diff --git a/content/docs/memory-benchmark/brainbench.mdx b/content/docs/memory-benchmark/brainbench.mdx index e64052a..7e7443a 100644 --- a/content/docs/memory-benchmark/brainbench.mdx +++ b/content/docs/memory-benchmark/brainbench.mdx @@ -24,6 +24,10 @@ Brain Quality Index of 80.0%**. By capability: ## v2.6.0 re-measurement (264 scenarios) +For the newer, unreleased paired evidence-delivery work, see +[Structured answerability](/docs/memory-benchmark/answerability). Its synthetic +45-query result is separate from the historical overall indices on this page. + Re-run on the semantic build, all 264 scenarios of the comprehensive set, none skipped: diff --git a/content/docs/memory-benchmark/index.mdx b/content/docs/memory-benchmark/index.mdx index bdeec8f..be1c984 100644 --- a/content/docs/memory-benchmark/index.mdx +++ b/content/docs/memory-benchmark/index.mdx @@ -9,6 +9,10 @@ them rather than take our word for it. ## The headline numbers +The [unreleased structured-answerability study](/docs/memory-benchmark/answerability) +reports a separate paired experiment: unwanted injections 15/18 → 3/18, with +24/27 positive hits retained. It does not replace the published scores below. + | benchmark | result | |-----------|--------| | LoCoMo | **89.4%** (1,540 questions, LLM-judged accuracy) | diff --git a/content/docs/memory-benchmark/meta.json b/content/docs/memory-benchmark/meta.json index 91b6481..2d7c648 100644 --- a/content/docs/memory-benchmark/meta.json +++ b/content/docs/memory-benchmark/meta.json @@ -4,6 +4,7 @@ "index", "retrieval-and-correctness", "brainbench", + "answerability", "longmemeval", "beam", "comparison" diff --git a/src/app/(home)/_components/explore-section.tsx b/src/app/(home)/_components/explore-section.tsx index c464b7c..e611b58 100644 --- a/src/app/(home)/_components/explore-section.tsx +++ b/src/app/(home)/_components/explore-section.tsx @@ -1,566 +1,74 @@ -"use client"; - -import { - ArrowRight, - BrainCircuit, - CheckCircle2, - Clock, - Copy, - Database, - RefreshCw, - Search, - Star, - Terminal, - TrendingUp, -} from "lucide-react"; +import { Database, MessageSquare, Search } from "lucide-react"; import Link from "next/link"; -import { useState } from "react"; - -/* ── data ─────────────────────────────────────────────────────────────── */ -const loopSteps = [ - { - icon: Terminal, - title: "Work", - body: "The agent solves a task and earns a lesson worth keeping.", - }, +const steps = [ { icon: Database, - title: "Capture", - body: "It lands in the brain as a memory. No model, no cloud, no cost.", + title: "Keep the useful lesson", + body: "Save a project convention, a decision, or the command that finally worked. Memories live in your project’s SQLite database.", }, { icon: Search, - title: "Recall", - body: "Next run it comes back by meaning, before the work even starts.", - }, - { - icon: CheckCircle2, - title: "Cite", - body: "The agent marks the memories that actually moved the task.", - }, - { - icon: TrendingUp, - title: "Learn", - body: "Cited memories rise in rank; stale, unused ones decay and prune.", - }, -]; - -const STAGES = [ - "Lexical FTS5", - "Semantic embeddings", - "Cross-encoder rerank", - "HyDE expansion", -]; -const levels = [ - { - name: "basic", - active: 1, - note: "Keyword search, zero model load. Fastest, lightest.", - }, - { - name: "flexible", - active: 2, - note: "Adds local embeddings, so meaning matches beat exact wording.", - }, - { - name: "deep", - active: 3, - note: "Adds a local cross-encoder that re-ranks the top hits.", - default: true, - }, - { - name: "advanced", - active: 4, - note: "Adds HyDE query expansion for the hardest recall.", - }, -]; - -// Architecture edge — the rows Kimetsu wins outright. -const archCols = ["Kimetsu", "mem0", "Cognee", "Zep", "Letta"]; -const archRows = [ - { - label: "Model in the memory pipeline", - cells: ["None", "LLM", "LLM", "LLM", "LLM"], - }, - { - label: "Cost to store and recall", - cells: ["$0", "Metered", "Metered", "Metered", "Metered"], + title: "Recall it in context", + body: "Search by words or meaning. Kimetsu selects relevant memories within a delivery budget and brings them into the next task.", }, { - label: "Runs fully on your machine", - cells: [ - "Yes", - "Self-host / cloud", - "Self-host / cloud", - "Cloud", - "Self-host / cloud", - ], + icon: MessageSquare, + title: "Learn from the outcome", + body: "Citations and feedback help track which memories contributed. Corrections and lifecycle rules keep old advice from outliving its usefulness.", }, ]; -// BEAM by token scale. Blank = not publicly reported at that scale. -const beamCols = ["", "Kimetsu", "Cognee", "mem0", "Honcho"]; -const beamRows = [ - { label: "BEAM 100K", cells: ["73.3%", "79%", "—", "63%"] }, - { label: "BEAM 1M", cells: ["66.0%", "—", "62%", "63%"] }, - { label: "BEAM 10M", cells: ["—", "67%", "48.6%", "41%"] }, -]; - -// BrainBench — our own reader-free benchmark. Scores from a live 142-scenario run. -const brainbench = { - overall: "80.0%", - dims: [ - { - icon: Clock, - name: "Forgetting", - score: "88%", - n: 49, - body: "Stale and superseded memories decay and get pruned instead of lingering.", - }, - { - icon: Copy, - name: "Dedup", - score: "77%", - n: 50, - body: "Duplicate and reworded memories are detected before they pile up.", - }, - { - icon: Star, - name: "Importance", - score: "76%", - n: 38, - body: "Useful memories rank above noise, so the right lesson surfaces first.", - }, - { - icon: Search, - name: "Retrieval", - score: "63%", - n: 5, - body: "The memory a query needs comes back by meaning, not just keywords.", - }, - ], -}; - -/* ── panels ───────────────────────────────────────────────────────────── */ - -function LoopPanel() { - return ( -
-

- Most memory tools are a bucket you read and write. Kimetsu is a cycle: - every session feeds the next one, and the knowledge that proves useful - is the knowledge that survives. -

-
-
-
    - {loopSteps.map((s, i) => ( -
  1. -
    - - - {i + 1} - -
    -
    -

    - {s.title} -

    -

    {s.body}

    -
    -
  2. - ))} -
-
-
-
- - - feeds the next session - -
-
-
- ); -} - -function LevelsPanel() { +export function ExploreSection() { return ( -
-

- Each level stacks one more local stage onto the last. Every stage runs - on your machine, so more accuracy costs compute, never a token. -

-
- ← faster, lighter - more accurate, heavier → +
+
+
+

Across sessions, across agents

+

+ The useful part of yesterday, ready today. +

+
+ + Inside the memory loop → +
-
- {levels.map((lvl) => ( -
-
- - {lvl.name} - - {lvl.default && ( - - default for new projects - - )} - - {lvl.note} +
    + {steps.map((step, index) => ( +
  1. +
    + + + 0{index + 1}
    -
    - {STAGES.map((stage, i) => { - const on = i < lvl.active; - return ( - - {stage} - - ); - })} -
    -
+

{step.title}

+

+ {step.body} +

+ ))} -
-

- Prefer full control? Set{" "} - - level = "custom" - {" "} - and tune the embedder, reranker, and HyDE by hand. -

-
- ); -} - -function BenchmarksPanel() { - return ( -
-

- mem0, Cognee, Zep, Honcho, and Letta all call a model to build and query - memory, so every stored fact and every lookup carries token cost. - Kimetsu runs the whole memory pipeline on local compute, at $0. -

- - {/* Architecture edge */} -
- - - - - {archCols.map((c, i) => ( - - ))} - - - - {archRows.map((row) => ( - - - {row.cells.map((cell, i) => ( - - ))} - - ))} - -
- The architecture edge - - {c} -
{row.label} - {cell} -
-
- - {/* LoCoMo */} -

- LoCoMo, the long-conversation benchmark -

-
- - - - - - - - - - - - - - - - - - - - - -
System - Kimetsu - mem0HonchoZepLetta
- LoCoMo (1,540 questions) - - 89.4% - - 92.5% - - 89.9% - - 75.1% - - 74.0% -
-

- LLM-judged accuracy on the standard 1,540-question set, as published - by each vendor. Kimetsu is the only system here with no LLM in the - memory pipeline. Harness and full methodology are public. -

-
- - {/* BEAM by scale */} -

- BEAM, by conversation size -

-
- - - - {beamCols.map((c, i) => ( - - ))} - - - - {beamRows.map((row) => ( - - - {row.cells.map((cell, i) => ( - - ))} - - ))} - -
- {c} -
{row.label} - {cell} -
-

- Kimetsu's 73.3% (100K, 400 probes) matches the prior public state - of the art on that bucket and it leads mem0 at 1M, with no model in - the retrieval path. Cognee (a knowledge-graph system with an LLM in - the loop) leads at 100K/10M. Vendor numbers are self-reported; a blank - cell means no comparable public figure at that scale. + +

+

+ + Choose your compute budget. + {" "} + Start with lexical search. Add local embeddings and reranking when + your workload benefits from them. Delivered context still uses your + agent’s tokens.

-
- -
- Read the full methodology + Compare retrieval models →
-
- ); -} - -function BrainBenchPanel() { - return ( -
-

- Every benchmark above grades an LLM reading from memory, so the reader - model colors the score. BrainBench is the one we built to grade the - memory - itself — reader-free, no - model in the loop. It scores four things a brain has to get right, over - 142 scenarios. -

- - {/* Overall score + dimensions */} -
-
- - Brain Quality Index - - - {brainbench.overall} - - - 142 scenarios, reader-free - -
- -
- {brainbench.dims.map((d) => ( -
-
- - - - - {d.name} - - - n={d.n} - - - {d.score} - -
- {/* score bar */} -
-
-
-

{d.body}

-
- ))} -
-
- -

- A live run of the open{" "} - kbench brainbench{" "} - harness. Because no LLM reads the answers, this score is a direct - measure of the brain and does not drift with the reader model. We - publish it as-is, including dedup, where we still have headroom. -

-
- ); -} - -/* ── tabbed container ─────────────────────────────────────────────────── */ - -const TABS = [ - { id: "how", label: "How it works", Panel: LoopPanel }, - { id: "levels", label: "Retrieval levels", Panel: LevelsPanel }, - { id: "bench", label: "Benchmarks", Panel: BenchmarksPanel }, - { id: "brainbench", label: "BrainBench", Panel: BrainBenchPanel }, -] as const; - -export function ExploreSection() { - const [active, setActive] = useState<(typeof TABS)[number]["id"]>("how"); - const ActivePanel = (TABS.find((tab) => tab.id === active) ?? TABS[0]).Panel; - - return ( -
-
-
-
- Under the hood -
-

- The parts worth a closer look -

-
- - {/* tab bar */} -
-
- {TABS.map((t) => ( - - ))} -
-
- - {/* active panel */} -
- -
-
); } diff --git a/src/app/(home)/_components/home-visuals.tsx b/src/app/(home)/_components/home-visuals.tsx index ba33730..cdbe5c6 100644 --- a/src/app/(home)/_components/home-visuals.tsx +++ b/src/app/(home)/_components/home-visuals.tsx @@ -1,190 +1,54 @@ -import { - ArrowLeftRight, - BellRing, - BookOpen, - GitMerge, - PackageOpen, - Radio, -} from "lucide-react"; -import { Reveal } from "./reveal"; - -/* Proactive — the second differentiator beside "free". Standalone section. */ - -const proactiveBehaviors = [ - { - icon: BellRing, - title: "Session-start digest", - body: "Open a new session and the brain has already handed the agent the conventions, gotchas, and decisions that matter here.", - }, - { - icon: BookOpen, - title: "Episodic resume", - body: "It picks up where you left off: what you were doing last time and the state you left it in, without you re-explaining.", - }, - { - icon: Radio, - title: "Context before the ask", - body: "Before a non-trivial task, the relevant lesson is surfaced automatically, so the agent avoids the mistake instead of repeating it.", - }, -]; - -export function Proactive() { - return ( -
-
-
-
- Free is half the - story -
-

- Most memory waits to be asked. Kimetsu speaks first. -

-

- A vector store sits idle until you query it. Kimetsu is proactive: - it reads the moment, decides what the agent needs, and puts it on - the table before the first turn, so knowledge shows up when it - changes the outcome, not after. -

-
- - {/* passive vs proactive contrast */} -
-
- - Passive memory - -

- you ask it fetches -

-

- Nothing happens until a query arrives. If the agent does not know - to ask, the memory never surfaces. -

-
-
- - Proactive memory - -

- it anticipates then - delivers -

-

- The brain surfaces the right context on its own, at session start - and ahead of each task, before the agent can repeat a solved - mistake. -

-
-
- -
- {proactiveBehaviors.map((b, i) => ( - -
-
- -
-

{b.title}

-

{b.body}

-
-
- ))} -
-
-
- ); -} - -/* Brain sharing — export, merge, swap. A brain is a portable file. */ +import { ArrowLeftRight, GitMerge, PackageOpen } from "lucide-react"; +import Link from "next/link"; const sharingModes = [ { icon: PackageOpen, title: "Export", - body: "One command turns your brain into a shareable pack: gzip-compressed and security-scrubbed, so credentials and PII never leave your machine.", + body: "Package selected memories for another machine or teammate. Review the pack before sharing it.", }, { icon: GitMerge, title: "Merge", - body: "Import a teammate’s pack additively. It dedups against what you already know, and re-importing is always safe.", + body: "Import a pack into an existing brain. Duplicate detection helps keep repeated knowledge from piling up.", }, { icon: ArrowLeftRight, title: "Swap", - body: "Replace your current memories with a pack’s, reversibly. Old memories are superseded, never deleted, so you can always swap back.", + body: "Use a different set of project memories while preserving superseded history.", }, ]; export function BrainSharing() { return ( -
-
-
-
- Brains are - portable -
-

- Export, merge, and swap brains freely +
+
+
+

Bring the knowledge with you

+

+ A useful lesson travels well.

-

- A brain is a file, not a hostage. Pack up what your agent has - learned, hand it to a teammate, install one from a URL, or swap - whole brains in and out. Onboard a new machine or a new hire with - one import. -

-
- -
- {sharingModes.map((m, i) => ( - -
-
- -
-

{m.title}

-

{m.body}

-
-
- ))}
- - -
-
- $ - - kimetsu brain export team.json.gz --name rust-conventions - - # scrubbed pack -
-
- $ - - kimetsu brain import team.json.gz - - # merge + dedup -
-
- $ - - kimetsu brain import https://example.com/pack.json.gz - - # from a URL -
-
- $ - - kimetsu brain import other.json.gz --mode replace --yes - - - {" "} - # swap, reversible - -
+ + Manage your brain → + +
+
+ {sharingModes.map((item) => ( +
+ +

{item.title}

+

+ {item.body} +

- + ))}
); diff --git a/src/app/(home)/_components/projects-section.tsx b/src/app/(home)/_components/projects-section.tsx index 75f2d23..26fda51 100644 --- a/src/app/(home)/_components/projects-section.tsx +++ b/src/app/(home)/_components/projects-section.tsx @@ -1,7 +1,6 @@ import { ArrowRight, Bot, GitFork, Radio, Sparkles } from "lucide-react"; import Link from "next/link"; import { projects } from "@/lib/projects"; -import { Reveal } from "./reveal"; export function ProjectsSection() { return ( @@ -10,7 +9,7 @@ export function ProjectsSection() { className="w-full border-t border-fd-border bg-fd-card/20" >
- +
@@ -30,15 +29,18 @@ export function ProjectsSection() { className="group inline-flex shrink-0 items-center gap-1.5 font-medium text-fd-primary" > Explore all projects - +
- +
- {projects.map((project, index) => ( - -
+ {projects.map((project) => ( +
+
{project.eyebrow} @@ -68,7 +70,7 @@ export function ProjectsSection() { href={project.website} className="inline-flex items-center gap-1.5 rounded-lg bg-fd-primary px-4 py-2 text-sm font-medium text-fd-primary-foreground transition-opacity hover:opacity-90" > - Open project + Open project
- +
))}
diff --git a/src/app/(home)/_components/quick-start.tsx b/src/app/(home)/_components/quick-start.tsx new file mode 100644 index 0000000..a1f883d --- /dev/null +++ b/src/app/(home)/_components/quick-start.tsx @@ -0,0 +1,115 @@ +"use client"; + +import { Check, Copy } from "lucide-react"; +import Link from "next/link"; +import { useState } from "react"; + +const hosts = [ + { id: "claude-code", label: "Claude Code" }, + { id: "codex", label: "Codex" }, + { id: "cursor", label: "Cursor" }, + { id: "pi", label: "Pi" }, + { id: "openclaw", label: "OpenClaw" }, +]; + +export function QuickStart() { + const [host, setHost] = useState(hosts[0].id); + const [feedback, setFeedback] = useState({ command: "", ok: false }); + const command = `npm install -g kimetsu-ai\nkimetsu setup --host ${host}`; + const currentFeedback = feedback.command === command; + + async function copyCommands() { + try { + await navigator.clipboard.writeText(command); + setFeedback({ command, ok: true }); + } catch { + setFeedback({ command, ok: false }); + } + } + + return ( +
+
+
+

Start with your agent

+

+ Two commands. A memory that stays. +

+

+ Install the binary, then run setup from your project directory. + Choose the agent you already use. +

+ + All installation options → + +
+
+
+
+ ); +} diff --git a/src/app/(home)/_components/reveal.tsx b/src/app/(home)/_components/reveal.tsx deleted file mode 100644 index eab840d..0000000 --- a/src/app/(home)/_components/reveal.tsx +++ /dev/null @@ -1,61 +0,0 @@ -"use client"; - -import { type ReactNode, useEffect, useRef, useState } from "react"; - -/** - * Reveals its children with a fade-up the first time they scroll into view. - * CSS transition only, no dependencies. Respects prefers-reduced-motion and - * degrades to "always visible" if the observer never fires. - */ -export function Reveal({ - children, - delay = 0, - className = "", -}: { - children: ReactNode; - delay?: number; - className?: string; -}) { - const ref = useRef(null); - const [shown, setShown] = useState(false); - - useEffect(() => { - const el = ref.current; - if (!el) return; - - if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) { - setShown(true); - return; - } - - const io = new IntersectionObserver( - ([entry]) => { - if (entry.isIntersecting) { - setShown(true); - io.disconnect(); - } - }, - { threshold: 0.15, rootMargin: "0px 0px -8% 0px" }, - ); - io.observe(el); - - // Safety: never leave content hidden if the observer misbehaves. - const t = window.setTimeout(() => setShown(true), 1500); - return () => { - io.disconnect(); - window.clearTimeout(t); - }; - }, []); - - return ( -
- {children} -
- ); -} diff --git a/src/app/(home)/layout.tsx b/src/app/(home)/layout.tsx index c16b056..ae4b3a6 100644 --- a/src/app/(home)/layout.tsx +++ b/src/app/(home)/layout.tsx @@ -2,5 +2,12 @@ import { HomeLayout } from "fumadocs-ui/layouts/home"; import { baseOptions } from "@/lib/layout.shared"; export default function Layout({ children }: LayoutProps<"/">) { - return {children}; + return ( + <> + + Skip to content + + {children} + + ); } diff --git a/src/app/(home)/page.tsx b/src/app/(home)/page.tsx index 5cc8777..3777520 100644 --- a/src/app/(home)/page.tsx +++ b/src/app/(home)/page.tsx @@ -1,251 +1,300 @@ -import { - ArrowRight, - Compass, - Cpu, - Database, - Lock, - MessageSquare, - TrendingUp, -} from "lucide-react"; -import Image from "next/image"; +import { ArrowRight, Database, GitBranch, Lock, Terminal } from "lucide-react"; import Link from "next/link"; -import { appName, links, tagline } from "@/lib/shared"; +import { links } from "@/lib/shared"; import { ExploreSection } from "./_components/explore-section"; -import { BrainSharing, Proactive } from "./_components/home-visuals"; +import { BrainSharing } from "./_components/home-visuals"; import { ProjectsSection } from "./_components/projects-section"; -import { Reveal } from "./_components/reveal"; +import { QuickStart } from "./_components/quick-start"; -const BASE = ""; - -const stats = [ - { - value: "89.4%", - label: "LoCoMo", - note: "the long-conversation memory benchmark, full 1,540-question set", - }, - { - value: "83.0%", - label: "LongMemEval", - note: "the public long-term-memory benchmark", - }, +const principles = [ { - value: "73.3%", - label: "BEAM 100K memory bench", - note: "matches the prior public SOTA, with no model in the pipeline", - }, - { - value: "13×", - label: "cheaper per solved task", - note: "$0.19 vs $2.47 on a 16-task Terminal-Bench slice", + icon: Database, + title: "Local by default", + body: "Project memory lives in SQLite. Start with lexical search or add local semantic models.", }, { - value: "~1M", - label: "memories in ~3 GB RAM", - note: "sub-2s retrieval, one SQLite file", + icon: GitBranch, + title: "Portable knowledge", + body: "Keep project decisions with the project. Export and import memories when you change machines or collaborate.", }, { - value: "$0", - label: "API cost to remember", - note: "the memory pipeline calls no model", + icon: Lock, + title: "No hosted memory account", + body: "The local memory path needs no cloud service or memory API key. Your agent keeps using its own provider.", }, ]; -const features = [ +const questions = [ { - icon: Database, - title: "Remembers what matters", - body: "Project conventions, failure patterns, the exact command that regenerates your schema. Captured once, retrieved by meaning.", - }, - { - icon: TrendingUp, - title: "Learns what helps", - body: "Memories the model cites before it solves a problem get promoted. Stale advice and silent passengers decay and get pruned.", + question: "Does remembering really cost zero tokens?", + answer: + "Local storage and lexical retrieval need no LLM calls. Local embeddings and reranking use your machine’s compute. Memory delivered to an agent still consumes context tokens; optional answer generation and model-assisted harvesting have separate costs.", }, { - icon: Compass, - title: "Never explores twice", - body: "A session-start digest and an episodic resume mean the first turn already knows the repo and what you were doing last time.", + question: "Can I use it offline?", + answer: + "Local memory works offline. Installation and the first download of optional models need a connection. Whether the rest of your session works offline depends on the agent and model you use.", }, { - icon: MessageSquare, - title: "Answers, not just injects", - body: "kimetsu ask composes a grounded, cited answer from memory using a local model. Zero frontier tokens, works offline.", + question: "Which agents can use it?", + answer: + "Setup supports Claude Code, Codex, Cursor, Pi, and OpenClaw. Integration behavior varies: Cursor uses MCP and guidance rather than automatic prompt hooks. The installation guide covers each host.", }, { - icon: Cpu, - title: "Model-free retrieval", - body: "FTS5, local embeddings, and a local cross-encoder reranker. Nothing in storage or retrieval calls an LLM.", - }, - { - icon: Lock, - title: "Yours on your machine", - body: "One SQLite file per project. No external vector database, no cloud, no telemetry. Back it up with cp.", + question: "What do the new benchmark numbers prove?", + answer: + "They measure delivery of stored evidence on a frozen synthetic fixture. They do not measure whether an agent gives a correct final answer. The structured-fact guard is unreleased and opt-in, and compound questions can still miss an attribute.", }, ]; export default function HomePage() { return ( -
- {/* Hero */} -
- Kimetsu logo -
- - 100% local - - - No cloud, no telemetry - - - MIT / Apache-2.0 - -
-

- Memory for your coding agent that gets sharper every run -

-

- {appName} is a single Rust binary that runs next to your agent over - MCP. It remembers what matters, learns which memories actually helped, - and lets that knowledge compound across sessions. {tagline}. -

-
- - Get started{" "} - - - +
+ - - $ - npm install -g kimetsu-ai - -
- - {/* Metrics */} -
- -
- {stats.map((s) => ( +
+ + project / memory + + Illustrative workflow +
+
+
+ 01 A lesson from this session +
+

+ “Regenerate the API client after changing the schema.” +

- - {s.value} - - - {s.label} - - - {s.note} - + className="my-6 ml-4 h-8 border-l border-dashed border-fd-primary/60" + aria-hidden + /> +
+ 02 Context for the next task
- ))} -
- +

+ Your agent is about to edit the API. +

+
+

+ Relevant project memory +

+

+ After changing the schema, run{" "} + + npm run generate:api + {" "} + and commit the generated client. +

+
+
+
+ Saved locally. Available across sessions. +
+ +
- - {/* Explore: how it works / retrieval levels / benchmarks (tabbed) */} +
+
+ Works alongside + {["Claude Code", "Codex", "Cursor", "Pi", "OpenClaw"].map((host) => ( + + {host} + + ))} + + Host setup → + +
+
+ - - {/* Proactive differentiator */} - - - {/* Features */} -
-
-
- {features.map((f, i) => ( - -
-
- -
-

{f.title}

-

{f.body}

-
-
- ))} +
+
+
+

Measured, with the limits included

+

+ Better memory knows when evidence is missing. +

+

+ Our latest structured-answerability experiment reduced unwanted + injections while preserving positive retrieval hits. +

+

+ 45 synthetic cases, two repeats. Evidence delivery, not + generated-answer accuracy. Exact metadata matched 36/45 cases in + each repeat. +

+ + Inspect the results and remaining gaps → +
-
-
- - {/* Brain sharing: export / merge / swap */} - - - {/* Public project directory */} - - - {/* Final CTA */} -
- -

- Set it up in two commands -

-
-
- $ npm install - -g kimetsu-ai -
-
- $ kimetsu - setup --host claude-code +
+
+

Structured fact guard

+ + Unreleased · Opt-in +
+ + + + + + + + + + + {[ + ["Unwanted injections ↓", "15/18", "3/18"], + ["Positive retrieval hits", "24/27", "24/27"], + ["P95 latency", "376.6 ms", "386.6 ms"], + ["Mean response bytes", "613.5", "651.2"], + ].map(([label, before, after]) => ( + + + + + + ))} + +
+ Paired comparison on the frozen structured-answerability fixture +
+ Measure + + Before + + After +
+ {label} + + {before} + {after}
+

+ 80% fewer unwanted injections; p95 latency increased by about + 2.7%. This feature remains disabled by default. +

-
- +
+

+ Looking for LoCoMo, LongMemEval, or earlier BrainBench results? +

+ + Browse all benchmarks → + +
+
+
+

Small footprint, clear ownership

+

+ Your project. Your memory. +

+
+ {principles.map((item) => ( +
- Read the docs{" "} - + +

{item.title}

+

+ {item.body} +

+
+ ))} +
+
+ +
+
+
+

Before you install

+

+ A few useful answers. +

+ + Read the documentation → - - crates.io - - - npm -
- +
+ {questions.map((item) => ( +
+ + {item.question} + +

+ {item.answer} +

+
+ ))} +
+
+ +
); } diff --git a/src/app/(home)/projects/page.tsx b/src/app/(home)/projects/page.tsx index c214157..feb7454 100644 --- a/src/app/(home)/projects/page.tsx +++ b/src/app/(home)/projects/page.tsx @@ -38,7 +38,7 @@ const sidequestLoop = [ export default function ProjectsPage() { return ( -
+
diff --git a/src/app/global.css b/src/app/global.css index ead2553..9ea187f 100644 --- a/src/app/global.css +++ b/src/app/global.css @@ -10,53 +10,199 @@ /* Kimetsu brand accent: "code dark, run green". Contrast-safe in both modes. */ :root { - --color-fd-primary: #15803d; /* green-700, AA on light surfaces */ + --color-fd-primary: #126c33; --color-fd-primary-foreground: #ffffff; + --color-fd-muted-foreground: #626262; } .dark { --color-fd-primary: #22c55e; /* green-500 on dark */ --color-fd-primary-foreground: #052e16; /* dark text on bright green */ --color-fd-ring: #22c55e; + --color-fd-muted-foreground: #a3a3a3; } html { scrollbar-gutter: stable; } -html > body[data-scroll-locked] { - margin-right: 0px !important; - --removed-body-scroll-bar-size: 0px !important; +/* Home: readable static content, compact controls, and a visible keyboard path. */ +.km-home { + min-width: 0; } -/* ── Motion ────────────────────────────────────────────────────────────── - Scroll/entrance reveals + tab-panel transition. CSS-only, dependency-free. - Everything collapses to "just show it" under prefers-reduced-motion. */ +.km-home :is(h1, h2, h3) { + text-wrap: balance; +} -@keyframes kimetsu-fade-up { - from { - opacity: 0; - transform: translateY(10px); - } - to { - opacity: 1; - transform: none; +.km-home :is(a, button, select, summary, pre):focus-visible, +.km-skip-link:focus-visible { + outline: 2px solid var(--color-fd-primary); + outline-offset: 4px; +} + +.km-home :is(a, button, select, summary) { + touch-action: manipulation; +} + +.km-skip-link { + position: fixed; + top: -6rem; + left: 1rem; + z-index: 100; + border-radius: 0.5rem; + padding: 0.75rem 1rem; + background: var(--color-fd-primary); + color: var(--color-fd-primary-foreground); +} + +.km-skip-link:focus { + top: 1rem; +} + +.km-hero { + padding: 5.5rem 1.5rem 5rem; + background-image: radial-gradient( + var(--color-fd-border) 0.7px, + transparent 0.7px + ); + background-size: 24px 24px; +} + +.km-hero-grid { + display: grid; + gap: 3.5rem; + max-width: 72rem; + margin-inline: auto; + align-items: center; +} + +.km-memory-example { + overflow: hidden; + border: 1px solid var(--color-fd-border); + border-radius: 1rem; + background: var(--color-fd-background); + box-shadow: 0 14px 45px -28px rgb(0 0 0 / 35%); +} + +.km-step { + display: inline-flex; + align-items: center; + justify-content: center; + width: 2rem; + height: 2rem; + flex-shrink: 0; + border: 1px solid var(--color-fd-border); + border-radius: 50%; + color: var(--color-fd-primary); +} + +.km-section { + max-width: 72rem; + margin-inline: auto; + padding: 4.5rem 1.5rem; + border-bottom: 1px solid var(--color-fd-border); +} + +.km-eyebrow { + margin-bottom: 1rem; + font-family: var(--font-mono); + font-size: 0.7rem; + font-weight: 600; + letter-spacing: 0.1em; + text-transform: uppercase; + color: var(--color-fd-primary); +} + +.km-heading { + max-width: 40rem; + font-family: var(--font-mono); + font-size: clamp(1.65rem, 3vw, 2.15rem); + font-weight: 600; + line-height: 1.25; + letter-spacing: -0.04em; +} + +.km-button-primary, +.km-button-secondary, +.km-copy-button { + display: inline-flex; + min-height: 2.75rem; + align-items: center; + justify-content: center; + gap: 0.5rem; + border: 1px solid var(--color-fd-border); + border-radius: 0.6rem; + padding: 0.65rem 1.1rem; + font-size: 0.875rem; + font-weight: 500; + cursor: pointer; + transition: + background-color 150ms, + border-color 150ms; +} + +.km-button-primary { + border-color: var(--color-fd-primary); + background: var(--color-fd-primary); + color: var(--color-fd-primary-foreground); +} + +.km-button-primary:hover { + background: color-mix(in srgb, var(--color-fd-primary), black 12%); +} + +.km-button-secondary { + background: var(--color-fd-background); +} + +.km-button-secondary:hover, +.km-copy-button:hover { + border-color: var(--color-fd-primary); + background: var(--color-fd-accent); +} + +.km-text-link { + color: var(--color-fd-primary); + font-size: 0.875rem; + font-weight: 500; + text-underline-offset: 4px; +} + +.km-text-link:hover { + text-decoration: underline; +} + +@media (min-width: 1024px) { + .km-hero-grid { + grid-template-columns: 1.15fr 0.85fr; } } -/* Hero + above-the-fold: play once on load. Stagger via inline animation-delay. */ -.km-enter { - animation: kimetsu-fade-up 0.6s cubic-bezier(0.22, 1, 0.36, 1) both; +@media (max-width: 639px) { + .km-hero { + padding: 3rem 1.25rem; + } + + .km-section { + padding: 3rem 1.25rem; + } + + .km-home table :is(th, td) { + padding: 0.85rem 0.6rem; + } } -/* Tab panels: replay on switch (keyed remount). */ -.km-panel-in { - animation: kimetsu-fade-up 0.4s cubic-bezier(0.22, 1, 0.36, 1) both; +html > body[data-scroll-locked] { + margin-right: 0px !important; + --removed-body-scroll-bar-size: 0px !important; } @media (prefers-reduced-motion: reduce) { - .km-enter, - .km-panel-in { - animation: none; + .km-home *, + .km-home *::before, + .km-home *::after { + animation: none !important; + transition: none !important; } } diff --git a/src/app/layout.tsx b/src/app/layout.tsx index ca23d25..8101282 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -22,7 +22,7 @@ export const metadata: Metadata = { template: "%s | kimetsu.dev", }, description: - "Kimetsu documentation, open agent projects, and a credential-free discovery gateway.", + "Local memory for coding agents. Carry project decisions, conventions, and fixes across sessions with one Rust binary. Explore setup, documentation, and measured results.", alternates: { canonical: "/" }, openGraph: { type: "website", @@ -30,7 +30,7 @@ export const metadata: Metadata = { siteName: "kimetsu.dev", title: "Kimetsu — proactive memory for coding agents", description: - "Kimetsu documentation, open agent projects, and a credential-free discovery gateway.", + "Carry project decisions, conventions, and fixes across coding sessions. Local memory, one Rust binary, and benchmarks you can inspect.", }, }; diff --git a/src/lib/layout.shared.tsx b/src/lib/layout.shared.tsx index 4c7878b..bbc3180 100644 --- a/src/lib/layout.shared.tsx +++ b/src/lib/layout.shared.tsx @@ -1,4 +1,5 @@ import type { BaseLayoutProps } from "fumadocs-ui/layouts/shared"; +import { GitFork } from "lucide-react"; import Image from "next/image"; import { appName, links } from "./shared"; @@ -21,11 +22,18 @@ export function baseOptions(): BaseLayoutProps { ), }, - githubUrl: links.github, links: [ { - text: "Projects", - url: "/projects", + type: "icon", + label: "Kimetsu on GitHub", + text: "GitHub", + icon: , + url: links.github, + external: true, + }, + { + text: "Install", + url: "/docs/install", active: "nested-url", }, { @@ -34,14 +42,14 @@ export function baseOptions(): BaseLayoutProps { active: "nested-url", }, { - text: "crates.io", - url: links.crates, - external: true, + text: "Benchmarks", + url: "/docs/memory-benchmark", + active: "nested-url", }, { - text: "npm", - url: links.npm, - external: true, + text: "Projects", + url: "/projects", + active: "nested-url", }, ], }; diff --git a/src/lib/projects.ts b/src/lib/projects.ts index 1dc1817..38b4442 100644 --- a/src/lib/projects.ts +++ b/src/lib/projects.ts @@ -28,7 +28,7 @@ export const projects: readonly Project[] = [ name: "Kimetsu", eyebrow: "Memory infrastructure", description: - "A local, model-free memory system that lets coding agents retain useful project knowledge and improve across sessions.", + "A local memory system that lets coding agents retain useful project knowledge and carry it across sessions.", status: "Active", website: "https://kimetsu.dev/docs/", repository: "https://github.com/RodCor/kimetsu",