@jmtrin/opencode-kevin 1.5.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,667 +1,711 @@
1
- <div align="center">
2
-
3
- # ⚡ Kevin
4
-
5
- ```
6
- ╔══════════════════════════════════════════════╗
7
- ║ ║
8
- ║ ██╗ ██╗███████╗██╗ ██╗██╗███╗ ██╗ ║
9
- ║ ██║ ██╔╝██╔════╝██║ ██║██║████╗ ██║ ║
10
- ║ █████╔╝ █████╗ ██║ ██║██║██╔██╗ ██║ ║
11
- ║ ██╔═██╗ ██╔══╝ ╚██╗ ██╔╝██║██║╚██╗██║ ║
12
- ║ ██║ ██╗███████╗ ╚████╔╝ ██║██║ ╚████║ ║
13
- ║ ╚═╝ ╚═╝╚══════╝ ╚═══╝ ╚═╝╚═╝ ╚═══╝ ║
14
- ║ ║
15
- ║ Local-First Memory for OpenCode ║
16
- ║ ║
17
- ╚══════════════════════════════════════════════╝
18
- ```
19
-
20
- ### Local-first memory for OpenCode — it observes, learns, remembers and proves it.
21
-
22
- **Kevin watches. Kevin learns. Kevin remembers.**
23
-
24
- It turns every coding session into durable, confidence-scored knowledge,
25
- injects exactly what matters back into the model's context, curates the best
26
- of it into files you control, and shares it across a team through one
27
- git-friendly file — deterministically, locally, with zero network calls.
28
-
29
- ![version](https://img.shields.io/badge/version-1.4.0-blue)
30
- ![node](https://img.shields.io/badge/node-%E2%89%A522.5-green)
31
- ![tests](https://img.shields.io/badge/tests-1380%20passing-brightgreen)
32
- ![deps](https://img.shields.io/badge/runtime%20deps-1-orange)
33
- ![network](https://img.shields.io/badge/network-zero-black)
34
- ![license](https://img.shields.io/badge/license-MIT-lightgrey)
35
-
36
- </div>
37
-
38
- <!-- uncomment when docs/demo.gif lands
39
- ![demo](docs/demo.gif)
40
- -->
41
-
42
- > **AI agents are good at solving problems. Kevin makes sure they don't have to solve the same problem twice.**
43
-
44
- ---
45
-
46
- ## 📖 Contents
47
-
48
- - [Why Kevin](#-why-kevin)
49
- - [The Kevin loop](#-the-kevin-loop)
50
- - [Quick start](#-quick-start)
51
- - [What's new in 1.4.0 — Bridge](#-whats-new-in-140--bridge)
52
- - [What's new in 1.3.0 — Bedrock](#-whats-new-in-130--bedrock)
53
- - [What's new in 1.2.0 — Surface](#-whats-new-in-120--surface)
54
- - [What's new in 1.1.0 — Drift](#-whats-new-in-110--drift)
55
- - [What's new in 1.0.0](#-whats-new-in-100)
56
- - [How it works](#-how-it-works)
57
- - [The 26 tools (plugin) + 11 MCP tools](#-the-26-tools-plugin--11-mcp-tools)
58
- - [MCP Bridge — cross-harness memory](#-mcp-bridge--cross-harness-memory)
59
- - [The benchmark: proven, not promised](#-the-benchmark-proven-not-promised)
60
- - [Curation: from session noise to AGENTS.md](#-curation-from-session-noise-to-agentsmd)
61
- - [Team sharing: one file, zero servers](#-team-sharing-one-file-zero-servers)
62
- - [The contract](#-the-contract)
63
- - [Hooks & latency budgets](#-hooks--latency-budgets)
64
- - [Configuration](#%EF%B8%8F-configuration)
65
- - [Supported runtimes](#-supported-runtimes)
66
- - [Design & trust](#-design--trust)
67
- - [Development](#-development)
68
- - [License](#-license)
69
-
70
- ---
71
-
72
- ## 🤔 Why Kevin
73
-
74
- Every coding session produces experience — errors, fixes, decisions,
75
- conventions, lessons. Without memory, most of it evaporates when the context
76
- window closes: the next session starts near zero and walks straight into the
77
- same wall.
78
-
79
- Kevin closes that loop:
80
-
81
- | 🔍 Observe | 🧠 Learn | 🎯 Recall | ✍️ Curate | 👥 Share | 📏 Prove |
82
- |---|---|---|---|---|---|
83
- | Tool calls, chat messages, session signals | Failures become lessons, causal chains, patterns | Relevant knowledge ranked and injected inside a token budget | The best knowledge becomes human-approved `AGENTS.md` guidance | Curated knowledge travels through one git-friendly file | Latency and retrieval quality are measured, not assumed |
84
-
85
- **Local first, by design.** Your coding experience belongs to you:
86
-
87
- ```
88
- no cloud service · no telemetry · no network calls · no hidden write path
89
- ```
90
-
91
- Knowledge lives in a local SQLite database, is projected into files you
92
- control, and is shared — optionally — through a single file you can read,
93
- diff and review like code.
94
-
95
- ---
96
-
97
- ## 🔁 The Kevin loop
98
-
99
- ```
100
- CODING SESSION
101
-
102
-
103
- 🔍 OBSERVE ────── tool calls, chat signals, failures
104
-
105
-
106
- 🧠 LEARN ─────── lessons, causal chains, patterns
107
-
108
-
109
- 💾 REMEMBER ───── confidence · provenance · recurrence
110
-
111
-
112
- 🎯 RECALL ────── rank → gates → token budget
113
-
114
-
115
- MODEL CONTEXT ──── only what matters now
116
-
117
-
118
- ✍️ CURATE ─────── propose → HUMAN review → approve
119
-
120
-
121
- AGENTS.md
122
-
123
-
124
- NEXT SESSION ─────────────────▶ 🔁
125
- ```
126
-
127
- **Experience compounds instead of evaporating.**
128
-
129
- ---
130
-
131
- ## 🚀 Quick start
132
-
133
- ### 1. Declare the plugin
134
-
135
- ```jsonc
136
- // opencode.json
137
- {
138
- "plugin": ["@jmtrin/opencode-kevin"]
139
- }
140
- ```
141
-
142
- ```bash
143
- npm install @jmtrin/opencode-kevin
144
- ```
145
-
146
- ### 2. Restart OpenCode
147
-
148
- On first boot Kevin migrates its database to schema version `013` and starts
149
- observing. Nothing else is required.
150
-
151
- ### 3. Talk to it
152
-
153
- ```
154
- kevin_status → is everything healthy?
155
- kevin_why → why did this keep failing, and how was it fixed?
156
- kevin_query what does Kevin remember about X?
157
- kevin_doctor health report: hooks, deps, perf, verdict
158
- ```
159
-
160
- ### 4. Where data lives
161
-
162
- ```
163
- ~/.opencode-kevin/
164
- ├── kevin.db ← everything Kevin learns (SQLite, WAL)
165
- ├── skills/ ← generated pull channels
166
- ├── refs/ topic reference bundles
167
- └── tui/
168
- ├── proposals.json pending proposals projection (512 KiB cap)
169
- ├── conflicts.json ← open conflicts projection
170
- ├── health.json doctor+perf snapshot
171
- ├── meta.json {generatedAt, versions}
172
- ├── dashboard.html static review surface (file://, zero network)
173
- ├── actions.json mailbox queue (TUI panels → session.idle)
174
- └── results.json last action results (audit)
175
-
176
- <repo>/.kevin/
177
- ├── AGENTS.md ← curated knowledge (marker block, human-approved)
178
- └── knowledge.okf ← optional team-sharing file (opt-in)
179
- ```
180
-
181
- ---
182
-
183
- ## 🆕 What's new in 1.4.0 — "Bridge"
184
-
185
- > 1.4.0 opens Kevin to every MCP harness — same local DB, same ranked recall, no network.
186
-
187
- - 🌉 **MCP Bridge** — new package `@jmtrin/kevin-mcp` (stdio MCP server, SDK 1.30.0): 7 read tools (`query/get/recall/why/status/trace/feedback` with provenance `{repo_id, identity_source, channel:"mcp"}` + token ceiling `ceil(chars/4)` + `pull_mcp` ledger), 3 gated write tools (`save/approve/share` behind `mcp_write_enabled` / `mcp_approve_enabled` double-gate + `mcp_writes_refused/accepted`), `ping` liveness — single binary `npx @jmtrin/kevin-mcp [--repo <id>]`, stderr `ready` line, SIGINT/SIGTERM flush.
188
- - 🔐 **Identity & repo scope** `RepoIdentity` (declared remote → path hex-16), `mcp_repo_override` setting, `--repo` CLI, scope guard `repo_mismatch` on every tool (read + write), resolves once at init (heals OKF header on rekey).
189
- - 🗄️ **Migration 013** — `channel TEXT DEFAULT 'plugin' CHECK(plugin|mcp)`, `injected_at_ms` probe, 5 metrics `mcp_requests_total/reads_served/writes_accepted/writes_refused/errors_total`, `schema_version 013`; `busy_timeout=5000` on Store for plugin↔MCP WAL concurrency; pre-013 DBs omit `mcp` block in `kevin_audit` (partial:true).
190
- - 📊 **Contracts & audit** — `C-04/C-05` since `1.4.0` (3 MCP config keys, 5 MCP metrics), `C-07` schema `013`, golden `v1.json` 35/61, `METRIC_KEY_LABELS`, `kevin_audit` gains `mcp{requests,reads,writes_accepted/refused,errors,channel_split{plugin,mcp}}`.
191
- - ⏱️ **Perf** — `mcp.read` p95 25/max 100, `mcp.write` p95 50/max 250, flush every 100 requests or SIGINT to `perf_samples`; concurrency stress 500×500 interleaved ops, zero `SQLITE_BUSY`.
192
- - 📚 **Harnesses & demo** — recipes for Claude Code / Codex / Cursor / Windsurf / Gemini CLI / Opencode (`docs/harnesses/*.md`, valid JSON blocks), 10-min cross-harness demo `docs/demo-cross-harness.md`, release ordering `coretui→plugin→mcp` in `docs/DISTRIBUTION.md`.
193
-
194
- **Upgrade:** `npm i @jmtrin/kevin-core@1.4.0 @jmtrin/opencode-kevin@1.4.0 @jmtrin/opencode-kevin-tui@1.4.0 @jmtrin/kevin-mcp@1.4.0` DB auto-migrates to 013; writes remain gated off until `mcp_write_enabled='1'`.
195
-
196
- ---
197
-
198
- ## 🆕 What's new in 1.3.0 — "Bedrock"
199
-
200
- > 1.3.0 declares the border the code already had. No visible behavior change everything after this changes *because* of it.
201
-
202
- - 🏗️ **Monorepo with a hostless core** — root becomes a private `npm workspaces` manager; `packages/core` (`@jmtrin/kevin-core` **zero deps**), `packages/plugin` (`@jmtrin/opencode-kevin` — name frozen by C-06, now depends on core `1.3.0` exact), `packages/tui` (`@jmtrin/opencode-kevin-tui`, isolated). 60+ domain modules `plugin/*.ts` `packages/core/src/*.ts` via `git mv`, migrations `migrations/` `packages/core/migrations/` (12 SQL, built to `dist/migrations`).
203
- - 🔌 **One new type + one new export** (`KevinEnv` + `exportMigrationsDir`): every `process.cwd()`/`homedir()` touchpoint now takes an injected `KevinEnv {projectRoot,dataRoot}` via `resolveEnv()` (defaults `cwd` / `~/.opencode-kevin` only in `env.ts`). Core scans as `0` `process.cwd/homedir/node:os` outside the allowlist (K13-007), verified by `packages/core/tests/core_purity_scan.test.ts`.
204
- - 🔄 **Replay lives in core** — `replay.ts` + `idle-pipeline.ts` (`IDLE_STEP_ORDER` single source, `composeIdlePipeline`) moved to core; adapter and replay both mount the same pipeline (D13-07). Parity harness `packages/plugin/tests/parity.test.ts` mounts adapter vs core wirings over every `tests/replay/fixtures/*.json` and asserts byte-identical outputs (swapped-step probe proves sensitivity).
205
- - 📦 **Packaging proof** — both tarballs verified + offline consumer smoke (`npm install <core.tgz> <tui.tgz> <plugin.tgz>` → `Store` + `Migrate` + `exportMigrationsDir` `schema_version 012`). Core zero-deps and `types`-first exports; plugin pins core exact `1.3.0`, `exports["./tui"]` redirects to the tui package — external specifiers unchanged.
206
- - 📜 **Contract unchanged** — `describeContract({scanRoots})` now accepts explicit roots (monorepo vs packed), golden values byte-equal (D13-05, 26/32/56 unchanged, no new setting/tool/metric/migration).
207
-
208
- **Upgrade:** no action required drop-in reorganization, behavior diff empty (K13-016), DB untouched.
209
-
210
- ---
211
-
212
- ## 🆕 What's new in 1.1.0 — "Drift"
213
-
214
- > 1.1.0 protects what 1.0.0 proved: a published number without a regression gate is marketing.
215
-
216
- - 🛡️ **Continuous benchmark gate** `npm run bench:regress` compares the last two `bench/results` against per-metric thresholds (`precision@k` >0.02, `recall` >0.05, `mrr` >0.05 on the `kevin` arm); CI fails when truth drifts.
217
- - 🗑️ **Lifecycle closure — `kevin_forget`** — dry-run default, `confirm:true` archives locally and publishes a tombstone through the single write path; second identical run is a `noop`.
218
- - ⏱️ **Millisecond timestamps** — new `_ms` columns with conservative backfill; `settle()` and `CausalChain` now decide sub-second causality.
219
- - 🧹 **Debt paid** — one `STOP_WORDS` source, one `readOriginCallId`, `ConflictDetector` via `mapRow`, one column-probe registry; every setting has an on-path test.
220
- - 📜 **Public hygiene** — `LICENSE` (MIT), `homepage` filled, `docs/DISTRIBUTION.md` checklist, `scripts/release-notes.mjs` for `gh release create`, and `<!-- demo -->` slot.
221
-
222
- ---
223
-
224
- ## 🆕 What's new in 1.2.0 — "Surface"
225
-
226
- > 1.2.0 gives the human-in-the-loop a place to stand: every pending proposal is readable with its diff without opening an editor, and every approval rides the same gated handler.
227
-
228
- - 🖥️ **Three review surfaces, one projection** — TUI panels (`/kevin` route, `k` to open) where the host renders them, plus a static `dashboard.html` under `~/.opencode-kevin/tui/` that opens via `file://` with zero network, zero fetch, inline CSS/JS and embedded JSON.
229
- - 💬 **Chat-command bridge `/kevin-*` (universal, immediate)** `/kevin-approve <id> <token>`, `/kevin-reject <id> <token> [note]`, `/kevin-ack <id>` execute through the existing `kevinApprove` / acknowledge handlers; valid commands are swallowed, invalid/stale ones pass through untouched to the model.
230
- - 📦 **Mailbox for TUI panels (idle-latency)** — proposals approved from the TUI write `~/.opencode-kevin/tui/actions.json`; the session consumes it at the next `session.idle` before `curator.propose`, then refreshes snapshots (`tui_snapshots_enabled='1'`).
231
- - ⏱️ **Latency honesty** chat commands apply this turn; mailbox actions apply at next idle. Both disclosed in-surface (`queuedapplies at session idle` toast, copy hint on dashboard).
232
- - 📦 **Packaging** — new export `opencode-kevin/tui` (`dist/plugin/tui.*`), `engines.opencode ^1.18.0` validated by the host with skip-with-warning.
233
-
234
- | Surface | Review | Action | Latency |
235
- |---|---|---|---|
236
- | TUI panel (`/kevin`)CLI/TUI host | Proposals / Conflicts / Health tabs, diff dialog, truncation markers | `a` approve / `r` reject / `x` ack mailbox | next idle |
237
- | Static dashboard (`dashboard.html`) | Proposals (+escaped diff `<pre>`), conflicts two-column, health banner | Copy `/kevin-*` button paste into chat input | immediate (chat bridge) |
238
- | Any client (Desktop, CLI) | — | Type `/kevin-approve …` etc in chat input | immediate |
239
-
240
- Snapshots + dashboard are capped at 512 KiB (diff truncation with `truncated:true`), written atomically via `tmp`+`rename`, and read best-effort with empty-state explanations (`no snapshots yet — open an opencode session…`) when missing/corrupt/stale-token.
241
-
242
- ---
243
-
244
- ## 🆕 What's new in 1.0.0
245
-
246
- > 1.0.0 is the **proven release**: the surface is frozen as data, the cost is
247
- > measured, the value is benchmarked reproducibly.
248
-
249
- - ❄️ **A frozen public contract** — nine clauses (`C-01` `C-09`) derived from
250
- live source, digest-stamped, enforced by an append-only golden file.
251
- Inspect it live with `kevin_contract`.
252
- - ⏱️ **Latency budgets** — eight instrumented scopes; `npm run bench:check`
253
- fails if any scope exceeds its p95 budget. `dispose` joins as the seventh
254
- hook with crash-safe deferred settlement.
255
- - 📊 **A reproducible benchmark**committed synthetic corpus, four arms,
256
- committed result. See [below](#-the-benchmark-proven-not-promised).
257
- - 🛡️ **An untrusted-input boundary** everything reaching an artifact or
258
- prompt is escaped at the single write path; stored text is never trusted.
259
- - 📦 **A corrected published package** — types-first exports, `dist/` only,
260
- verified against the packed tarball by `npm run verify:pack`.
261
-
262
- ---
263
-
264
- ## ⚙️ How it works
265
-
266
- Kevin is an intentionally deterministic pipeline — no LLM in the core loop:
267
-
268
- ```
269
- your coding session
270
-
271
-
272
- ┌────────────────────────────────────────────┐
273
- │ 🔍 OBSERVE │
274
- │ tool.execute.before/after · chat.message │
275
- Failures auto-detected from exit codes, │
276
- │ stderr and stdout markers. │
277
- └──────────────────┬─────────────────────────┘
278
-
279
- ┌────────────────────────────────────────────┐
280
- 🧠 LEARN │
281
- Reflector → lessons │
282
- │ CausalChain failure/fix links │
283
- PatternMiner repeated sequences │
284
- Evidence raises confidence; recurrence │
285
- lowers it and flags staleness.
286
- └──────────────────┬─────────────────────────┘
287
-
288
- ┌────────────────────────────────────────────┐
289
- 🎯 INJECT │
290
- QualityGate BM25 × origin × recency × │
291
- truth penalty token-budget fit │
292
- │ Deduped per session, inside <kevin-context>│
293
- └──────────────────┬─────────────────────────┘
294
-
295
- ┌────────────────────────────────────────────┐
296
- │ ✍️ CURATE │
297
- propose HUMAN REVIEW approve │
298
- │ Nothing writes without approval. │
299
- └──────────────────┬─────────────────────────┘
300
-
301
- ┌────────────────────────────────────────────┐
302
- │ 📏 PROVE │
303
- │ perf budgets · retrieval benchmark · │
304
- contract digest
305
- The system measures itself instead of
306
- merely claiming to work.
307
- └────────────────────────────────────────────┘
308
- ```
309
-
310
- ---
311
-
312
- ## 🧰 The 26 tools (plugin) + 11 MCP tools
313
-
314
- <details open>
315
- <summary><b>🧠 Core memory</b></summary>
316
-
317
- | Tool | What it does |
318
- |---|---|
319
- | `kevin_save` | Store a memory: decision, rule, pattern, context or solution |
320
- | `kevin_query` | Search memories — slim payload; `evidence: true` exposes confidence |
321
- | `kevin_get` | Fetch one memory in full |
322
- | `kevin_recall` | Ranked recall with origin-aware scoring |
323
- | `kevin_status` | Session scoreboard: counts, precision, metrics |
324
- | `kevin_config` | List/set any of the 31 settings — no SQL required |
325
- | `kevin_project` | Show, initialize or rekey the repository identity |
326
-
327
- </details>
328
-
329
- <details>
330
- <summary><b>🔎 Understanding & debugging</b></summary>
331
-
332
- | Tool | What it does |
333
- |---|---|
334
- | `kevin_why` | Failure→fix trace for a recurring error, with confidence and evidence |
335
- | `kevin_trace` | Dry-run: exactly what would be injected for a query, zero side effects |
336
- | `kevin_feedback` | Rate an injected memory `useful` / `wrong` / `outdated` / `ignore` |
337
- | `kevin_conflicts` | Surface contradictions between memories and repository truth |
338
- | `kevin_facts` | Scan the repository for ground-truth facts |
339
- | `kevin_retrospective` | Per-session markdown retrospective |
340
-
341
- </details>
342
-
343
- <details>
344
- <summary><b>✍️ Curation & publishing</b></summary>
345
-
346
- | Tool | What it does |
347
- |---|---|
348
- | `kevin_propose` | Dry-run curation proposals with unified diffs — writes nothing |
349
- | `kevin_approve` | The **only** path that writes `AGENTS.md` (or rejects) |
350
- | `kevin_publish` | Regenerate skill/ref pull bundles under `~/.opencode-kevin/` |
351
-
352
- </details>
353
-
354
- <details>
355
- <summary><b>👥 Team & operations</b></summary>
356
-
357
- | Tool | What it does |
358
- |---|---|
359
- | `kevin_share` | Promote curated memories into `.kevin/knowledge.okf` (approval-gated) |
360
- | `kevin_sync` | Import the shared file into the local layer |
361
- | `kevin_export` / `kevin_import` | Markdown/OKF bundles out and in |
362
- | `kevin_audit` | Whole-system report: memories, injections, channels, team, perf, contract |
363
- | `kevin_doctor` | Health verdict: `healthy` / `degraded` / `unknown`, with reasons |
364
- | `kevin_native` | Show/enable/disable native host registration (default off) |
365
- | `kevin_contract` | **v1.0.0** inspect the frozen public surface at runtime |
366
- | `kevin_bench` | **v1.0.0** report benchmark results; never runs them in-session |
367
-
368
- </details>
369
-
370
- ---
371
-
372
- ## 🌉 MCP Bridge — cross-harness memory
373
-
374
- Kevin's SQLite file becomes accessible outside OpenCode via the MCP bridge — same ranking, same gates, same file you already trust.
375
-
376
- | Surface | Install | Transport | Reads | Writes |
377
- |---|---|---|---|---|
378
- | **MCP** `@jmtrin/kevin-mcp` | `npx -y @jmtrin/kevin-mcp` ( `--repo <id>` optional) | stdio (SDK 1.30.0), no HTTP/SSE | 7 tools always on (query/get/recall/why/status/trace/feedback + ping) | 3 tools gated (`save` needs `mcp_write_enabled='1'`, `approve/share` need + `mcp_approve_enabled='1'`) |
379
-
380
- - **One DB, two processes** plugin and MCP server share `.opencode-kevin/kevin.db` via WAL + `busy_timeout=5000`; every read/write is repo-scoped (`repo_mismatch` on drift).
381
- - **Provenance** every MCP response carries `{repo_id, identity_source, channel:"mcp"}` (plus confidence/evidence when applicable).
382
- - **Harnesses** — `docs/harnesses/{claude-code,codex,cursor,windsurf,gemini-cli,opencode}.md` with exact JSON/TOML snippets, verification, troubleshooting, uninstall; demo `docs/demo-cross-harness.md` (opencode → Claude Code recall).
383
-
384
- See `packages/mcp/README.md` for full MCP reference.
385
-
386
- ---
387
-
388
- ## 📊 The benchmark: proven, not promised
389
-
390
- Kevin ships a committed synthetic corpus and a four-arm harness measuring
391
- whether real retrieval beats trivial baselines at surfacing labelled-relevant
392
- memories:
393
-
394
- ```
395
- bench/corpus/
396
- seed: 1262835273 (xorshift32, byte-for-byte regenerable)
397
- digest: adecbdf4c7af82e2
398
- result: bench/results/2026-08-21-adecbdf4c7af82e2.json (k = 5)
399
- ```
400
-
401
- ```
402
- npm run bench # run the harness (also persists one row per arm)
403
- npm run bench:check # gate: every perf scope within its p95 budget
404
- npm run verify:pack # gate: seven properties against the packed tarball
405
- ```
406
-
407
- | Arm | Precision@5 | Recall@5 | MRR |
408
- |---|---:|---:|---:|
409
- | `none` (control) | 0.000 | 0.000 | 0.000 |
410
- | `recent-k` (baseline) | 0.050 | 0.026 | 0.109 |
411
- | `random-k` (floor) | 0.048 | 0.028 | 0.093 |
412
- | **`kevin`** | **0.950** | **0.546** | **1.000** |
413
-
414
- ```
415
- Precision@5 Recall@5 MRR
416
- 0.950 ██████████████████░░ 0.546 ██████████░░░░ 1.000 ████████████████████
417
- 0.050 █░░░░░░░░░░░░░░░░░░░ 0.026 █░░░░░░░░░░░░░ 0.109 ██░░░░░░░░░░░░░░░░░░
418
- ```
419
-
420
- The labelling rule is mechanical and the retrieval numbers are exactly
421
- reproducible — asserted by running the harness twice in-process.
422
-
423
- **Honest limits, stated up front.** This benchmark measures retrieval on a
424
- synthetic corpus built to have a ranked answer; it does **not** prove that real sessions look like this synthetic corpus. It does not prove that a surfaced memory changed what the model did. Retrieval quality is one layer of the agent loop, not the whole of it.
425
-
426
- ---
427
-
428
- ## ✍️ Curation: from session noise to AGENTS.md
429
-
430
- Knowledge worth keeping becomes a proposal; a human decides; only then is it
431
- written — once, atomically, inside a marker pair you can edit around:
432
-
433
- ```
434
- memories ──▶ kevin_propose ──▶ pending proposals (unified diffs, no writes)
435
-
436
- HUMAN REVIEW
437
-
438
- ┌── approve ────┴──── reject ──▶ decision recorded
439
- ▼ nothing written
440
- AGENTS.md updated atomically ◀── the single write path (D6-01)
441
- ```
442
-
443
- ```markdown
444
- <!-- kevin:begin curated by opencode-kevin, safe to edit -->
445
- - Always run `npm run typecheck` before committing (fixed 3 CI failures)
446
- <!-- kevin:end -->
447
- ```
448
-
449
- Deliberately conservative: only non-inferable memories are eligible — an
450
- LLM-recoverable diagnostic is not something a human should have to review into
451
- a permanent rule. Re-applying an unchanged plan is a counted noop, never a
452
- write. **Kevin can propose. Humans decide.**
453
-
454
- ---
455
-
456
- ## 👥 Team sharing: one file, zero servers
457
-
458
- Opt-in via `shared_layer_enabled='1'`. Curated knowledge exports to one
459
- `.kevin/knowledge.okf` — header lines plus one JSON entry per line, sorted by a
460
- deterministic `entry_id`, LF-only, ≤4096 bytes per line — designed so git
461
- merges are meaningful and conflicts are parseable:
462
-
463
- ```
464
- DEV A DEV B
465
- │ share (approval-gated) │ git pull
466
- ▼ ▼
467
- knowledge.okf ◀═══════ git ══════▶ knowledge.okf
468
- kevin_sync
469
-
470
- projected into local memories
471
- (layer='shared', immutable)
472
- ```
473
-
474
- Repository identity resolves **once**, in order:
475
- `.kevin/project.json` (declared) → git remote hash (never a raw URL) → path.
476
- Two clones of the same repository are one team; different repositories never
477
- leak into each other. Sharing requires explicit approval
478
- (`share_requires_approval='1'`), author identity is hashed by default, and
479
- tombstones archive rather than delete.
480
-
481
- ---
482
-
483
- ## 📜 The contract
484
-
485
- Kevin 1.x makes promises about its published surface **in writing**.
486
- [`docs/CONTRACT.md`](docs/CONTRACT.md) freezes nine clauses — `C-01` … `C-09` —
487
- from the `AGENTS.md` marker bytes to the database schema, each tagged `frozen`
488
- or `forward-only` and stamped with the release that incurred the obligation. A
489
- test diffs the live contract against an append-only golden file on every run:
490
- removals and silent changes fail loudly; additions must carry `since`.
491
-
492
- ```jsonc
493
- // kevin_contract (excerpt)
494
- {
495
- "contract_version": 1,
496
- "digest": "1de9740bba2e9f95",
497
- "clauses": [
498
- { "id": "C-03", "title": "Tool names and argument shapes", "stability": "frozen", "since": "0.2.0" },
499
- { "id": "C-07", "title": "Database schema", "stability": "forward-only", "since": "0.1.0" }
500
- ]
501
- }
502
- ```
503
-
504
- > 1.0.0 is not just a version number — it is where Kevin starts making explicit, testable promises about its surface.
505
-
506
- ---
507
-
508
- ## ⏱️ Hooks & latency budgets
509
-
510
- Six host hooks plus Kevin's own `dispose` checkpoint plus two MCP bridges ten measured scopes,
511
- each with a declared p95/max budget enforced by `npm run bench:check`:
512
-
513
- | Scope | p95 budget | max |
514
- |---|---:|---:|
515
- | `tool.execute.before` | 2 ms | 10 ms |
516
- | `tool.execute.after` | 5 ms | 25 ms |
517
- | `chat.message` | 2 ms | 10 ms |
518
- | `chat.system.transform` | 15 ms | 50 ms |
519
- | `session.compacting` | 15 ms | 50 ms |
520
- | `event` | 5 ms | 25 ms |
521
- | `session.idle` | 150 ms | 600 ms |
522
- | `dispose` | 50 ms | 250 ms |
523
- | `mcp.read` | 25 ms | 100 ms |
524
- | `mcp.write` | 50 ms | 250 ms |
525
-
526
- Measured on the reference laptop: retrieval p50 ≈ 0.2 ms, p95 < 1 ms — orders
527
- of magnitude under budget. Samples persist to `perf_samples` at idle; a breach
528
- degrades `kevin_doctor`'s verdict, because a plugin that is technically alive
529
- but consistently slow is not healthy.
530
-
531
- ---
532
-
533
- ## ⚙️ Configuration
534
-
535
- Everything is a TEXT setting managed through `kevin_config` (or any SQLite
536
- client). All values are TEXT — flags compare with `=== "1"`, never truthiness.
537
-
538
- | Key | Default | Purpose |
539
- |---|---|---|
540
- | `quality_gate_enabled` | `'1'` | Weak lessons stored but never injected |
541
- | `lesson_snippet_injection` | `'1'` | Compact 2-line snippets instead of full bodies |
542
- | `patternminer_enabled` | `'0'` | Deterministic tool-sequence mining (≥5 sessions) |
543
- | `cross_project_enabled` | `'0'` | Include imported cross-project rows |
544
- | `llm_reflection_enabled` | `'0'` | Opt-in LLM enrichment at pattern promotion |
545
- | `tool_calls_dedup_enabled` | `'0'` | Suppress duplicate call recordings per minute bucket |
546
- | `deterministic_retrieval` | `'0'` | Freeze the clock for hermetic tests/replay |
547
- | `pre_prompt_budget_tokens` | `'400'` | Pre-prompt injection cap (clamped `[0, 4000]`) |
548
- | `archive_after_days` | `'30'` | Age threshold for idle archival |
549
- | `curation_enabled` | `'1'` | Idle dry-run proposal generation |
550
- | `agents_md_path` | `'AGENTS.md'` | Where curated knowledge lands |
551
- | `skill_emission_enabled` | `'0'` | Register project-knowledge skill on v2 hosts |
552
- | `reference_emission_enabled` | `'0'` | Register `@kevin/<topic>` mentions |
553
- | `injection_confidence_floor` | `'0.6'` | Memories below this never inject |
554
- | `repo_truth_enabled` | `'0'` | Repository fact scanning |
555
- | `convention_mining_enabled` | `'0'` | Deterministic convention mining |
556
- | `conflict_detection_enabled` | `'0'` | Contradiction surfacing |
557
- | `error_lesson_mode` | `'all'` | Error lesson injection mode |
558
- | `shared_layer_enabled` | `'0'` | Team sharing via `.kevin/knowledge.okf` |
559
- | `okf_path` | `'.kevin/knowledge.okf'` | Shared file location |
560
- | `share_requires_approval` | `'1'` | No export without human confirmation |
561
- | `author_identity_mode` | `'hashed'` | Author identity hashed, never raw email |
562
- | `shared_confidence_floor` | `'0.7'` | Confidence floor for shared projections |
563
- | `hook_liveness_enabled` | `'1'` | Per-hook liveness tracking |
564
- | `native_registration_enabled` | `'0'` | v2 native skill/reference registration |
565
- | `host_probe_history_enabled` | `'0'` | Append-only probe history |
566
- | `dead_hook_report_threshold` | `'3'` | Consecutive misses before a hook reads dead |
567
- | `perf_enabled` | `'1'` | Latency instrumentation |
568
- | `perf_ring_capacity` | `'512'` | Samples per scope (clamped `[64, 8192]`) |
569
- | `perf_flush_on_idle` | `'1'` | Persist samples at idle |
570
- | `contract_report_enabled` | `'1'` | Contract block in `kevin_audit` |
571
- | `tui_snapshots_enabled` | `'1'` | Snapshot + dashboard flush at idle (opencode-kevin/tui) |
572
- | `mcp_write_enabled` | `'0'` | Gate for MCP `save` (refused counter `mcp_writes_refused`) |
573
- | `mcp_approve_enabled` | `'0'` | Double-gate for MCP `approve`/`share` (needs + `mcp_write_enabled`) |
574
- | `mcp_repo_override` | `''` | Override `RepoIdentity` for MCP (hex-16 or empty) |
575
-
576
- ---
577
-
578
- ## 🖥️ Supported runtimes
579
-
580
- | Runtime | SQLite backend | Status |
581
- |---|---|---|
582
- | Node 24.x | `node:sqlite` (stable) | **Supported** the reference row |
583
- | Node 22.5+ | `better-sqlite3` (optional dep) | ⚠️ **Supported with a caveat** — needs a build toolchain; without one there is no backend (and npm install still succeeds silently) |
584
- | Node 22.5+ | `node:sqlite` behind `--experimental-sqlite` | 🔶 Works, unsupported exercised in CI, not promised |
585
- | Bun ≥ 1.1 | `bun:sqlite` | **Supported** — smoke-tested in `npm run verify` |
586
-
587
- Zero network calls asserted by source scan (forbidden: http/https/net/dgram/fetch/SSE/HttpTransport/child_process/spawn) on every test run; MCP uses stdio only, no spawn, logs on stderr only.
588
-
589
- ---
590
-
591
- ## 🎨 Design & trust
592
-
593
- **Principles.** Local first · deterministic by default (no LLM in the core
594
- loop) · evidence over vibes (every memory carries evidence, provenance,
595
- recurrence and confidence) · signal over noise (inject *less, better* context,
596
- not more) · humans hold the write boundary · git-friendly collaboration ·
597
- important claims become executable checks.
598
-
599
- **Security model.** Stored knowledge is treated as untrusted input: anything
600
- reaching an artifact or prompt passes through idempotent escaping at the
601
- single write path; permanent project-file changes require explicit human
602
- approval; author identity in the shared layer is hashed
603
- (`author_identity_mode='hashed'`). No network service is required to store,
604
- retrieve or share knowledge.
605
-
606
- **Without memory vs with Kevin:**
607
-
608
- ```
609
- without: session 1 solve ─▶ context closes ─▶ session 2 same problem ─▶ solve again
610
-
611
- with: session 1 solve ─▶ lesson ─▶ memory ─┐
612
- session 2 problem ◀── recall ◀───────┘ ─▶ fix ─▶ evidence
613
- ```
614
-
615
- **Why not just `AGENTS.md`?** It is excellent for durable, human-authored
616
- guidance Kevin treats it as a curated destination, not as a memory system.
617
- It cannot do failure/fix traces, confidence scoring, evidence tracking, ranked
618
- retrieval, recurrence signals, dry-run injection inspection, automatic
619
- proposal generation, team projections, or performance and contract
620
- instrumentation. That is what Kevin adds around it.
621
-
622
- ---
623
-
624
- ## 🛠️ Development
625
-
626
- ```bash
627
- npm install # hoists workspaces (root private, 4 packages)
628
- npm run build # core tui plugin mcp (tsc + copy-migrations)
629
- npm run typecheck # -w core -w tui -w plugin -w mcp + root (strict)
630
- npm run lint # biome
631
- npm test # vitestroot suite (190 files)
632
- npm test -w @jmtrin/kevin-core # core only — also passes with @opencode-ai/plugin absent (K13-013)
633
- npm run verify # install checks + Bun smoke + verify:pack (×2 + consumer)
634
- npm run verify:pack # dual-tarball + offline consumer smoke (K13-014)
635
- npm run gen:corpus # regenerate the seeded corpus (byte-identical)
636
- npm run replay # replay recorded sessions deterministically (now via core)
637
- ```
638
-
639
- Project layout (Bedrock monorepo):
640
-
641
- ```
642
- packages/core/ @jmtrin/kevin-core — ~60 modules (zero deps), src/*.ts, migrations/ 001→013, dist/migrations
643
- packages/plugin/ @jmtrin/opencode-kevin 4 modules (index, host, native, capabilities), adapter thin, depends on core+tui 1.4.0 exact
644
- packages/tui/ @jmtrin/opencode-kevin-tui isolated TUI panel (target-exclusive, own package.json/exports)
645
- packages/mcp/ @jmtrin/kevin-mcp MCP bridge (stdio, 11 tools, pure helper, identity+provenance)
646
- scripts/ bench · gen-corpus · verify-pack (dual) · verify-install · …
647
- tests/ unit · integration · e2e · replay fixtures (at root, run via workspaces)
648
- bench/ committed corpus + committed results
649
- docs/ CONTRACT.md · per-release plans/tasks · roadmap v2
650
- ```
651
-
652
- > **C-10 preview** — the future public surface is the explicit re-export list at `packages/core/src/index.ts` (keep it minimal and deliberate).
653
-
654
- C-06 frozen: `plugin` package name `@jmtrin/opencode-kevin` and `exports["./tui"]` specifier unchanged; consumers see no break.
655
-
656
- ---
657
-
658
- ## 📄 License
659
-
660
- MITsee the package manifest. Kevin is built by [jmtrin](https://github.com/jmtrin);
661
- bug reports and PRs welcome at the [issue tracker](https://github.com/jmtrin/opencode-kevin/issues).
662
-
663
- <div align="center">
664
-
665
- ### ⚡ Kevin — *Observe. Learn. Remember. Improve.*
666
-
667
- </div>
1
+ <div align="center">
2
+
3
+ # ⚡ Kevin
4
+
5
+ ```
6
+ ╔══════════════════════════════════════════════╗
7
+ ║ ║
8
+ ║ ██╗ ██╗███████╗██╗ ██╗██╗███╗ ██╗ ║
9
+ ║ ██║ ██╔╝██╔════╝██║ ██║██║████╗ ██║ ║
10
+ ║ █████╔╝ █████╗ ██║ ██║██║██╔██╗ ██║ ║
11
+ ║ ██╔═██╗ ██╔══╝ ╚██╗ ██╔╝██║██║╚██╗██║ ║
12
+ ║ ██║ ██╗███████╗ ╚████╔╝ ██║██║ ╚████║ ║
13
+ ║ ╚═╝ ╚═╝╚══════╝ ╚═══╝ ╚═╝╚═╝ ╚═══╝ ║
14
+ ║ ║
15
+ ║ Local-First Memory for OpenCode ║
16
+ ║ ║
17
+ ╚══════════════════════════════════════════════╝
18
+ ```
19
+
20
+ ### Local-first memory for OpenCode — it observes, learns, remembers and proves it.
21
+
22
+ **Kevin watches. Kevin learns. Kevin remembers.**
23
+
24
+ It turns every coding session into durable, confidence-scored knowledge,
25
+ injects exactly what matters back into the model's context, curates the best
26
+ of it into files you control, and shares it across a team through one
27
+ git-friendly file — deterministically, locally, with zero network calls.
28
+
29
+ ![version](https://img.shields.io/badge/version-2.0.0-blue)
30
+ ![node](https://img.shields.io/badge/node-%E2%89%A522.5-green)
31
+ ![tests](https://img.shields.io/badge/tests-1509%20passing-brightgreen)
32
+ ![deps](https://img.shields.io/badge/runtime%20deps-1-orange)
33
+ ![network](https://img.shields.io/badge/network-zero-black)
34
+ ![license](https://img.shields.io/badge/license-MIT-lightgrey)
35
+
36
+ </div>
37
+
38
+ <!-- uncomment when docs/demo.gif lands
39
+ ![demo](docs/demo.gif)
40
+ -->
41
+
42
+ > **AI agents are good at solving problems. Kevin makes sure they don't have to solve the same problem twice.**
43
+
44
+ ---
45
+
46
+ ## 📖 Contents
47
+
48
+ - [Why Kevin](#-why-kevin)
49
+ - [The Kevin loop](#-the-kevin-loop)
50
+ - [Quick start](#-quick-start)
51
+ - [What's new in 2.0.0 — Commonwealth](#-whats-new-in-200--commonwealth)
52
+ - [What's new in 1.5.0 — Diaspora](#-whats-new-in-150--diaspora)
53
+ - [What's new in 1.4.0 — Bridge](#-whats-new-in-140--bridge)
54
+ - [What's new in 1.3.0 — Bedrock](#-whats-new-in-130--bedrock)
55
+ - [What's new in 1.2.0 — Surface](#-whats-new-in-120--surface)
56
+ - [What's new in 1.1.0 — Drift](#-whats-new-in-110--drift)
57
+ - [What's new in 1.0.0](#-whats-new-in-100)
58
+ - [How it works](#-how-it-works)
59
+ - [The 27 tools (plugin) + 11 MCP tools](#-the-27-tools-plugin--11-mcp-tools)
60
+ - [MCP Bridge cross-harness memory](#-mcp-bridge--cross-harness-memory)
61
+ - [The benchmark: proven, not promised](#-the-benchmark-proven-not-promised)
62
+ - [Curation: from session noise to AGENTS.md](#-curation-from-session-noise-to-agentsmd)
63
+ - [Team sharing: one file, zero servers](#-team-sharing-one-file-zero-servers)
64
+ - [The contract](#-the-contract)
65
+ - [Hooks & latency budgets](#-hooks--latency-budgets)
66
+ - [Configuration](#%EF%B8%8F-configuration)
67
+ - [Supported runtimes](#-supported-runtimes)
68
+ - [Design & trust](#-design--trust)
69
+ - [Development](#-development)
70
+ - [License](#-license)
71
+
72
+ ---
73
+
74
+ ## 🤔 Why Kevin
75
+
76
+ Every coding session produces experience errors, fixes, decisions,
77
+ conventions, lessons. Without memory, most of it evaporates when the context
78
+ window closes: the next session starts near zero and walks straight into the
79
+ same wall.
80
+
81
+ Kevin closes that loop:
82
+
83
+ | 🔍 Observe | 🧠 Learn | 🎯 Recall | ✍️ Curate | 👥 Share | 📏 Prove |
84
+ |---|---|---|---|---|---|
85
+ | Tool calls, chat messages, session signals | Failures become lessons, causal chains, patterns | Relevant knowledge ranked and injected inside a token budget | The best knowledge becomes human-approved `AGENTS.md` guidance | Curated knowledge travels through one git-friendly file | Latency and retrieval quality are measured, not assumed |
86
+
87
+ **Local first, by design.** Your coding experience belongs to you:
88
+
89
+ ```
90
+ no cloud service · no telemetry · no network calls · no hidden write path
91
+ ```
92
+
93
+ Knowledge lives in a local SQLite database, is projected into files you
94
+ control, and is shared — optionally — through a single file you can read,
95
+ diff and review like code.
96
+
97
+ ---
98
+
99
+ ## 🔁 The Kevin loop
100
+
101
+ ```
102
+ CODING SESSION
103
+
104
+
105
+ 🔍 OBSERVE ────── tool calls, chat signals, failures
106
+
107
+
108
+ 🧠 LEARN ─────── lessons, causal chains, patterns
109
+
110
+
111
+ 💾 REMEMBER ───── confidence · provenance · recurrence
112
+
113
+
114
+ 🎯 RECALL ────── rank → gates → token budget
115
+
116
+
117
+ MODEL CONTEXT ──── only what matters now
118
+
119
+
120
+ ✍️ CURATE ─────── propose → HUMAN review → approve
121
+
122
+
123
+ AGENTS.md
124
+
125
+
126
+ NEXT SESSION ─────────────────▶ 🔁
127
+ ```
128
+
129
+ **Experience compounds instead of evaporating.**
130
+
131
+ ---
132
+
133
+ ## 🚀 Quick start
134
+
135
+ ### 1. Declare the plugin
136
+
137
+ ```jsonc
138
+ // opencode.json
139
+ {
140
+ "plugin": ["@jmtrin/opencode-kevin"]
141
+ }
142
+ ```
143
+
144
+ ```bash
145
+ npm install @jmtrin/opencode-kevin
146
+ ```
147
+
148
+ ### 2. Restart OpenCode
149
+
150
+ On first boot Kevin migrates its database to schema version `014` and starts
151
+ observing. Nothing else is required.
152
+
153
+ ### 3. Talk to it
154
+
155
+ ```
156
+ kevin_status is everything healthy?
157
+ kevin_why why did this keep failing, and how was it fixed?
158
+ kevin_query → what does Kevin remember about X?
159
+ kevin_doctor → health report: hooks, deps, perf, verdict
160
+ ```
161
+
162
+ ### 4. Where data lives
163
+
164
+ ```
165
+ ~/.opencode-kevin/
166
+ ├── kevin.db everything Kevin learns (SQLite, WAL)
167
+ ├── skills/ ← generated pull channels
168
+ ├── refs/ topic reference bundles
169
+ └── tui/
170
+ ├── proposals.json pending proposals projection (512 KiB cap)
171
+ ├── conflicts.json open conflicts projection
172
+ ├── health.json doctor+perf snapshot
173
+ ├── meta.json {generatedAt, versions}
174
+ ├── dashboard.html static review surface (file://, zero network)
175
+ ├── actions.json ← mailbox queue (TUI panels → session.idle)
176
+ └── results.json ← last action results (audit)
177
+
178
+ <repo>/.kevin/
179
+ ├── AGENTS.md ← curated knowledge (marker block, human-approved)
180
+ ├── knowledge.okf ← single-file when okf_write_version='2' (opt-in, legacy)
181
+ └── knowledge/ ← sharded dir when okf_write_version='3' (default): knowledge.okf (primary ≤2000) + knowledge-002.okf …
182
+ ```
183
+
184
+ ---
185
+
186
+ ## 🆕 What's new in 2.0.0 — "Commonwealth"
187
+
188
+ > 2.0.0 unitesOKF v3 sharding + MemorySources.
189
+
190
+ - ❄️ **Contract v2 C-10..C-14** — `C-10` core exports (`1.3.0`), `C-11` MCP tool names + refusal vocabulary (`1.4.0`), `C-12` skills layout (`1.5.0`), `C-13` MIF envelope + redaction (`1.5.0`), `C-14` MemorySources names/precedence/dedup (`2.0.0`); `contract_version` `1` `2`, succession carried verbatim, `kevin_contract` v2, `tests/fixtures/contract/v2.json` golden, `diffContract` still fails loudly on any carried-clause drift.
191
+ - 📂 **OKF v3 sharded `.kevin/knowledge/`** primary `knowledge.okf` (≤2000) + overflow `knowledge-002.okf` (zero-padded, deterministic), cap `2000`, `okf_write_version` `'3'` default `'2'` rollback (byte-exact legacy), reader walks all shards lexicographically and enforces global `entry_id` uniqueness (duplicate structured error naming both files), writer packs primary / spills overflow / collapses gaps, `healHeader` rewrites every shard.
192
+ - 🧠 **MemorySources on idle** — `opencode-plugin (10)`, `claude-memory (20)`, `codex-memories (30)`, `opencode-native (40)` in precedence order; settings `sources_enabled` (`'1'` master) + `source_claude_memory`/`source_codex_memories`/`source_opencode_native` (`'0'` per-source) + `okf_write_version`; idle only (after skills refresh, before snapshots), per-source errors never block next source, cycle bounded `10 s`; incremental `memory_sources.meta_json` `{mtime,size}` caches; dedup by fingerprint (lower precedence wins, `source_dedup_skips_total++`, `also_seen_by[]`), topic clash `memory_conflicts kind='source_pair'` (open, never auto-resolved); tool #27 `kevin_sources` (show-only), `kevin_trace` `source` + `kevin_audit` `sources` block with health/last_sync/counts/skips, provenance `<!-- kevin:source=claude-memory -->` when non-default.
193
+ - 🗄️ **Migration `014`** — `memory_sources` table + `memories.source` + `idx_memories_source` + 3 metric seeds (`source_syncs_total`, `source_dedup_skips_total`, `okf_v3_files_written`), retire `import_host_memory` → `{error:"removed_in_2.0.0", replacement:"sources_*"}` with one-shot translation to `sources`; `Defaults Outcome` both `FALSE` (no default flip, no enum collapse), CC adapter gate `K16-021` not taken → `v2.1` (zero `cc-adapter` code in `2.0.0`).
194
+ - ⏱️ **Latency** `sources.sync` `p95 50/max 250` (idle, same `bench:check` red rule).
195
+
196
+ **Upgrade:** `npm i @jmtrin/kevin-core@2.0.0 @jmtrin/opencode-kevin@2.0.0 @jmtrin/opencode-kevin-tui@2.0.0 @jmtrin/kevin-mcp@2.0.0` — DB auto-migrates to `014`.
197
+
198
+ ---
199
+
200
+ ## 🆕 What's new in 1.5.0"Diaspora"
201
+
202
+ > 1.5.0 shares curated knowledge as versioned skills mirrored to every harness and as portable `.mif` memory same DB, same recall, gated emission.
203
+
204
+ - 📚 **Canonical skills** — `skills_canonical_dir` (`'.agents/skills'` canonical `.md` + `manifest.json`), atomic writes, ≤80/150 caps, deterministic sort, idle refresh after snapshots flush; missing `manifest.json` external-edit signal, `SKILL.md` frontmatter `name/metadata(hash,generated_at)`, manifest written last (`C-02`).
205
+ - 🪞 **Mirrors** — `skills_mirror_claude` / `skills_mirror_cursor` exact byte copy of canonical, deleted skills pruned following canonical, single-prefix enforcement, external mirror edits discarded silently, `EXTERNAL` (disk!=manifest) skip.
206
+ - 📦 **MIF portable memory** — `MifDocument` `{version,exported_at,entries{vendor}}`, `kevin_export {mif, redact_pii}` + `kevin_import {mif,claude,codex}` with `SECRET_PATTERNS` redaction, vendor preservation, metrics `mif_exports/imports_total`, fingerprint before redact, `MAX_BODY 500` warn.
207
+ - 📥 **Host import** — gate `import_host_memory='1'`, Claude `~/.claude.json` lineage + Codex `~/.codex/sessions/*.jsonl` lineage, dedup by fingerprint, `pending` memories with `origin:host`, capped walk `1 MiB/5000`, `lstatSync` symlink-safe, `frontmatter` unclosed → skipped.
208
+ - 📊 **Contracts**`C-04/C-05` since `1.5.0` (+4 settings / +3 metrics → 39/64), `C-07` `013`, golden `v1.json` `39/64`, `kevin_audit` channels `v2` + perf `skills.emit/mif.codec` (`p95 25/max 100`), scans green (`core_purity_scan`, `single_write_path`).
209
+
210
+ **Upgrade:** `npm i @jmtrin/kevin-core@1.5.0 @jmtrin/opencode-kevin@1.5.0 @jmtrin/opencode-kevin-tui@1.5.0 @jmtrin/kevin-mcp@1.5.0` — DB auto-migrates to `013`; emission/mirrors gated off until canonical/mirror paths set, host import gated off until `import_host_memory='1'`.
211
+
212
+ ---
213
+
214
+ ## 🆕 What's new in 1.4.0 "Bridge"
215
+
216
+ > 1.4.0 opens Kevin to every MCP harness same local DB, same ranked recall, no network.
217
+
218
+ - 🌉 **MCP Bridge** — new package `@jmtrin/kevin-mcp` (stdio MCP server, SDK 1.30.0): 7 read tools (`query/get/recall/why/status/trace/feedback` with provenance `{repo_id, identity_source, channel:"mcp"}` + token ceiling `ceil(chars/4)` + `pull_mcp` ledger), 3 gated write tools (`save/approve/share` behind `mcp_write_enabled` / `mcp_approve_enabled` double-gate + `mcp_writes_refused/accepted`), `ping` liveness — single binary `npx @jmtrin/kevin-mcp [--repo <id>]`, stderr `ready` line, SIGINT/SIGTERM flush.
219
+ - 🔐 **Identity & repo scope** — `RepoIdentity` (declared remote → path hex-16), `mcp_repo_override` setting, `--repo` CLI, scope guard `repo_mismatch` on every tool (read + write), resolves once at init (heals OKF header on rekey).
220
+ - 🗄️ **Migration 013** — `channel TEXT DEFAULT 'plugin' CHECK(plugin|mcp)`, `injected_at_ms` probe, 5 metrics `mcp_requests_total/reads_served/writes_accepted/writes_refused/errors_total`, `schema_version 013`; `busy_timeout=5000` on Store for plugin↔MCP WAL concurrency; pre-013 DBs omit `mcp` block in `kevin_audit` (partial:true).
221
+ - 📊 **Contracts & audit** — `C-04/C-05` since `1.4.0` (3 MCP config keys, 5 MCP metrics), `C-07` schema `013`, golden `v1.json` 35/61, `METRIC_KEY_LABELS`, `kevin_audit` gains `mcp{requests,reads,writes_accepted/refused,errors,channel_split{plugin,mcp}}`.
222
+ - ⏱️ **Perf** — `mcp.read` p95 25/max 100, `mcp.write` p95 50/max 250, flush every 100 requests or SIGINT to `perf_samples`; concurrency stress 500×500 interleaved ops, zero `SQLITE_BUSY`.
223
+ - 📚 **Harnesses & demo** — recipes for Claude Code / Codex / Cursor / Windsurf / Gemini CLI / Opencode (`docs/harnesses/*.md`, valid JSON blocks), 10-min cross-harness demo `docs/demo-cross-harness.md`, release ordering `core→tui→plugin→mcp` in `docs/DISTRIBUTION.md`.
224
+
225
+ **Upgrade:** `npm i @jmtrin/kevin-core@1.4.0 @jmtrin/opencode-kevin@1.4.0 @jmtrin/opencode-kevin-tui@1.4.0 @jmtrin/kevin-mcp@1.4.0` — DB auto-migrates to 013; writes remain gated off until `mcp_write_enabled='1'`.
226
+
227
+ ---
228
+
229
+ ## 🆕 What's new in 1.3.0"Bedrock"
230
+
231
+ > 1.3.0 declares the border the code already had. No visible behavior changeeverything after this changes *because* of it.
232
+
233
+ - 🏗️ **Monorepo with a hostless core** — root becomes a private `npm workspaces` manager; `packages/core` (`@jmtrin/kevin-core` **zero deps**), `packages/plugin` (`@jmtrin/opencode-kevin` — name frozen by C-06, now depends on core `1.3.0` exact), `packages/tui` (`@jmtrin/opencode-kevin-tui`, isolated). 60+ domain modules `plugin/*.ts` → `packages/core/src/*.ts` via `git mv`, migrations `migrations/` → `packages/core/migrations/` (12 SQL, built to `dist/migrations`).
234
+ - 🔌 **One new type + one new export** (`KevinEnv` + `exportMigrationsDir`): every `process.cwd()`/`homedir()` touchpoint now takes an injected `KevinEnv {projectRoot,dataRoot}` via `resolveEnv()` (defaults `cwd` / `~/.opencode-kevin` only in `env.ts`). Core scans as `0` `process.cwd/homedir/node:os` outside the allowlist (K13-007), verified by `packages/core/tests/core_purity_scan.test.ts`.
235
+ - 🔄 **Replay lives in core** — `replay.ts` + `idle-pipeline.ts` (`IDLE_STEP_ORDER` single source, `composeIdlePipeline`) moved to core; adapter and replay both mount the same pipeline (D13-07). Parity harness `packages/plugin/tests/parity.test.ts` mounts adapter vs core wirings over every `tests/replay/fixtures/*.json` and asserts byte-identical outputs (swapped-step probe proves sensitivity).
236
+ - 📦 **Packaging proof**both tarballs verified + offline consumer smoke (`npm install <core.tgz> <tui.tgz> <plugin.tgz>` `Store` + `Migrate` + `exportMigrationsDir` `schema_version 012`). Core zero-deps and `types`-first exports; plugin pins core exact `1.3.0`, `exports["./tui"]` redirects to the tui package — external specifiers unchanged.
237
+ - 📜 **Contract unchanged** `describeContract({scanRoots})` now accepts explicit roots (monorepo vs packed), golden values byte-equal (D13-05, 26/32/56 unchanged, no new setting/tool/metric/migration).
238
+
239
+ **Upgrade:** no action required — drop-in reorganization, behavior diff empty (K13-016), DB untouched.
240
+
241
+ ---
242
+
243
+ ## 🆕 What's new in 1.1.0 — "Drift"
244
+
245
+ > 1.1.0 protects what 1.0.0 proved: a published number without a regression gate is marketing.
246
+
247
+ - 🛡️ **Continuous benchmark gate** — `npm run bench:regress` compares the last two `bench/results` against per-metric thresholds (`precision@k` >0.02, `recall` >0.05, `mrr` >0.05 on the `kevin` arm); CI fails when truth drifts.
248
+ - 🗑️ **Lifecycle closure — `kevin_forget`** — dry-run default, `confirm:true` archives locally and publishes a tombstone through the single write path; second identical run is a `noop`.
249
+ - ⏱️ **Millisecond timestamps** — new `_ms` columns with conservative backfill; `settle()` and `CausalChain` now decide sub-second causality.
250
+ - 🧹 **Debt paid** — one `STOP_WORDS` source, one `readOriginCallId`, `ConflictDetector` via `mapRow`, one column-probe registry; every setting has an on-path test.
251
+ - 📜 **Public hygiene** `LICENSE` (MIT), `homepage` filled, `docs/DISTRIBUTION.md` checklist, `scripts/release-notes.mjs` for `gh release create`, and `<!-- demo -->` slot.
252
+
253
+ ---
254
+
255
+ ## 🆕 What's new in 1.2.0 "Surface"
256
+
257
+ > 1.2.0 gives the human-in-the-loop a place to stand: every pending proposal is readable with its diff without opening an editor, and every approval rides the same gated handler.
258
+
259
+ - 🖥️ **Three review surfaces, one projection** — TUI panels (`/kevin` route, `k` to open) where the host renders them, plus a static `dashboard.html` under `~/.opencode-kevin/tui/` that opens via `file://` with zero network, zero fetch, inline CSS/JS and embedded JSON.
260
+ - 💬 **Chat-command bridge `/kevin-*` (universal, immediate)** — `/kevin-approve <id> <token>`, `/kevin-reject <id> <token> [note]`, `/kevin-ack <id>` execute through the existing `kevinApprove` / acknowledge handlers; valid commands are swallowed, invalid/stale ones pass through untouched to the model.
261
+ - 📦 **Mailbox for TUI panels (idle-latency)** — proposals approved from the TUI write `~/.opencode-kevin/tui/actions.json`; the session consumes it at the next `session.idle` before `curator.propose`, then refreshes snapshots (`tui_snapshots_enabled='1'`).
262
+ - ⏱️ **Latency honesty** — chat commands apply this turn; mailbox actions apply at next idle. Both disclosed in-surface (`queued — applies at session idle` toast, copy hint on dashboard).
263
+ - 📦 **Packaging** — new export `opencode-kevin/tui` (`dist/plugin/tui.*`), `engines.opencode ^1.18.0` validated by the host with skip-with-warning.
264
+
265
+ | Surface | Review | Action | Latency |
266
+ |---|---|---|---|
267
+ | TUI panel (`/kevin`) — CLI/TUI host | Proposals / Conflicts / Health tabs, diff dialog, truncation markers | `a` approve / `r` reject / `x` ack → mailbox | next idle |
268
+ | Static dashboard (`dashboard.html`) | Proposals (+escaped diff `<pre>`), conflicts two-column, health banner | Copy `/kevin-*` button → paste into chat input | immediate (chat bridge) |
269
+ | Any client (Desktop, CLI) | — | Type `/kevin-approve …` etc in chat input | immediate |
270
+
271
+ Snapshots + dashboard are capped at 512 KiB (diff truncation with `truncated:true`), written atomically via `tmp`+`rename`, and read best-effort with empty-state explanations (`no snapshots yet — open an opencode session…`) when missing/corrupt/stale-token.
272
+
273
+ ---
274
+
275
+ ## 🆕 What's new in 1.0.0
276
+
277
+ > 1.0.0 is the **proven release**: the surface is frozen as data, the cost is
278
+ > measured, the value is benchmarked — reproducibly.
279
+
280
+ - ❄️ **A frozen public contract** — nine clauses (`C-01` … `C-09`) derived from
281
+ live source, digest-stamped, enforced by an append-only golden file.
282
+ Inspect it live with `kevin_contract`.
283
+ - ⏱️ **Latency budgets** — eight instrumented scopes; `npm run bench:check`
284
+ fails if any scope exceeds its p95 budget. `dispose` joins as the seventh
285
+ hook with crash-safe deferred settlement.
286
+ - 📊 **A reproducible benchmark** — committed synthetic corpus, four arms,
287
+ committed result. See [below](#-the-benchmark-proven-not-promised).
288
+ - 🛡️ **An untrusted-input boundary** — everything reaching an artifact or
289
+ prompt is escaped at the single write path; stored text is never trusted.
290
+ - 📦 **A corrected published package** types-first exports, `dist/` only,
291
+ verified against the packed tarball by `npm run verify:pack`.
292
+
293
+ ---
294
+
295
+ ## ⚙️ How it works
296
+
297
+ Kevin is an intentionally deterministic pipeline — no LLM in the core loop:
298
+
299
+ ```
300
+ your coding session
301
+
302
+
303
+ ┌────────────────────────────────────────────┐
304
+ 🔍 OBSERVE
305
+ tool.execute.before/after · chat.message
306
+ Failures auto-detected from exit codes,
307
+ │ stderr and stdout markers. │
308
+ └──────────────────┬─────────────────────────┘
309
+
310
+ ┌────────────────────────────────────────────┐
311
+ │ 🧠 LEARN │
312
+ Reflector → lessons │
313
+ │ CausalChain → failure/fix links │
314
+ PatternMiner → repeated sequences │
315
+ Evidence raises confidence; recurrence │
316
+ │ lowers it and flags staleness. │
317
+ └──────────────────┬─────────────────────────┘
318
+
319
+ ┌────────────────────────────────────────────┐
320
+ 🎯 INJECT │
321
+ QualityGate BM25 × origin × recency × │
322
+ truth penalty token-budget fit │
323
+ Deduped per session, inside <kevin-context>│
324
+ └──────────────────┬─────────────────────────┘
325
+
326
+ ┌────────────────────────────────────────────┐
327
+ │ ✍️ CURATE │
328
+ │ propose → HUMAN REVIEW → approve │
329
+ │ Nothing writes without approval. │
330
+ └──────────────────┬─────────────────────────┘
331
+
332
+ ┌────────────────────────────────────────────┐
333
+ │ 📏 PROVE │
334
+ perf budgets · retrieval benchmark · │
335
+ contract digest │
336
+ The system measures itself instead of │
337
+ merely claiming to work. │
338
+ └────────────────────────────────────────────┘
339
+ ```
340
+
341
+ ---
342
+
343
+ ## 🧰 The 27 tools (plugin) + 11 MCP tools
344
+
345
+ <details open>
346
+ <summary><b>🧠 Core memory</b></summary>
347
+
348
+ | Tool | What it does |
349
+ |---|---|
350
+ | `kevin_save` | Store a memory: decision, rule, pattern, context or solution |
351
+ | `kevin_query` | Search memories — slim payload; `evidence: true` exposes confidence |
352
+ | `kevin_get` | Fetch one memory in full |
353
+ | `kevin_recall` | Ranked recall with origin-aware scoring |
354
+ | `kevin_status` | Session scoreboard: counts, precision, metrics |
355
+ | `kevin_config` | List/set any of the 43 settings — no SQL required |
356
+ | `kevin_project` | Show, initialize or rekey the repository identity |
357
+
358
+ </details>
359
+
360
+ <details>
361
+ <summary><b>🔎 Understanding & debugging</b></summary>
362
+
363
+ | Tool | What it does |
364
+ |---|---|
365
+ | `kevin_why` | Failure→fix trace for a recurring error, with confidence and evidence |
366
+ | `kevin_trace` | Dry-run: exactly what would be injected for a query, zero side effects |
367
+ | `kevin_feedback` | Rate an injected memory `useful` / `wrong` / `outdated` / `ignore` |
368
+ | `kevin_conflicts` | Surface contradictions between memories and repository truth |
369
+ | `kevin_facts` | Scan the repository for ground-truth facts |
370
+ | `kevin_retrospective` | Per-session markdown retrospective |
371
+
372
+ </details>
373
+
374
+ <details>
375
+ <summary><b>✍️ Curation & publishing</b></summary>
376
+
377
+ | Tool | What it does |
378
+ |---|---|
379
+ | `kevin_propose` | Dry-run curation proposals with unified diffs — writes nothing |
380
+ | `kevin_approve` | The **only** path that writes `AGENTS.md` (or rejects) |
381
+ | `kevin_publish` | Regenerate skill/ref pull bundles under `~/.opencode-kevin/` |
382
+
383
+ </details>
384
+
385
+ <details>
386
+ <summary><b>👥 Team & operations</b></summary>
387
+
388
+ | Tool | What it does |
389
+ |---|---|
390
+ | `kevin_share` | Promote curated memories into `.kevin/knowledge.okf` (approval-gated) |
391
+ | `kevin_sync` | Import the shared file into the local layer |
392
+ | `kevin_export` / `kevin_import` | Markdown/OKF bundles out and in |
393
+ | `kevin_audit` | Whole-system report: memories, injections, channels, team, perf, contract |
394
+ | `kevin_doctor` | Health verdict: `healthy` / `degraded` / `unknown`, with reasons |
395
+ | `kevin_native` | Show/enable/disable native host registration (default off) |
396
+ | `kevin_contract` | **v2.0.0** — inspect the frozen public surface at runtime (`contract_version: 2`, `C-01..C-14`) |
397
+ | `kevin_bench` | **v1.0.0** — report benchmark results; never runs them in-session |
398
+ | `kevin_sources` | **v2.0.0** — show MemorySources provenance, sync status and per-source health (tool #27, show-only) |
399
+
400
+ </details>
401
+
402
+ ---
403
+
404
+ ## 🌉 MCP Bridge cross-harness memory
405
+
406
+ Kevin's SQLite file becomes accessible outside OpenCode via the MCP bridge — same ranking, same gates, same file you already trust.
407
+
408
+ | Surface | Install | Transport | Reads | Writes |
409
+ |---|---|---|---|---|
410
+ | **MCP** `@jmtrin/kevin-mcp` | `npx -y @jmtrin/kevin-mcp` ( `--repo <id>` optional) | stdio (SDK 1.30.0), no HTTP/SSE | 7 tools always on (query/get/recall/why/status/trace/feedback + ping) | 3 tools gated (`save` needs `mcp_write_enabled='1'`, `approve/share` need + `mcp_approve_enabled='1'`) |
411
+
412
+ - **One DB, two processes** plugin and MCP server share `.opencode-kevin/kevin.db` via WAL + `busy_timeout=5000`; every read/write is repo-scoped (`repo_mismatch` on drift).
413
+ - **Provenance** — every MCP response carries `{repo_id, identity_source, channel:"mcp"}` (plus confidence/evidence when applicable).
414
+ - **Harnesses** — `docs/harnesses/{claude-code,codex,cursor,windsurf,gemini-cli,opencode}.md` with exact JSON/TOML snippets, verification, troubleshooting, uninstall; demo `docs/demo-cross-harness.md` (opencode → Claude Code recall).
415
+
416
+ See `packages/mcp/README.md` for full MCP reference.
417
+
418
+ ---
419
+
420
+ ## 📊 The benchmark: proven, not promised
421
+
422
+ Kevin ships a committed synthetic corpus and a four-arm harness measuring
423
+ whether real retrieval beats trivial baselines at surfacing labelled-relevant
424
+ memories:
425
+
426
+ ```
427
+ bench/corpus/
428
+ seed: 1262835273 (xorshift32, byte-for-byte regenerable)
429
+ digest: adecbdf4c7af82e2
430
+ result: bench/results/2026-08-21-adecbdf4c7af82e2.json (k = 5)
431
+ ```
432
+
433
+ ```
434
+ npm run bench # run the harness (also persists one row per arm)
435
+ npm run bench:check # gate: every perf scope within its p95 budget
436
+ npm run verify:pack # gate: seven properties against the packed tarball
437
+ ```
438
+
439
+ | Arm | Precision@5 | Recall@5 | MRR |
440
+ |---|---:|---:|---:|
441
+ | `none` (control) | 0.000 | 0.000 | 0.000 |
442
+ | `recent-k` (baseline) | 0.050 | 0.026 | 0.109 |
443
+ | `random-k` (floor) | 0.048 | 0.028 | 0.093 |
444
+ | **`kevin`** | **0.950** | **0.546** | **1.000** |
445
+
446
+ ```
447
+ Precision@5 Recall@5 MRR
448
+ 0.950 ██████████████████░░ 0.546 ██████████░░░░ 1.000 ████████████████████
449
+ 0.050 █░░░░░░░░░░░░░░░░░░░ 0.026 █░░░░░░░░░░░░░ 0.109 ██░░░░░░░░░░░░░░░░░░
450
+ ```
451
+
452
+ The labelling rule is mechanical and the retrieval numbers are exactly
453
+ reproducible — asserted by running the harness twice in-process.
454
+
455
+ **Honest limits, stated up front.** This benchmark measures retrieval on a
456
+ synthetic corpus built to have a ranked answer; it does **not** prove that real sessions look like this synthetic corpus. It does not prove that a surfaced memory changed what the model did. Retrieval quality is one layer of the agent loop, not the whole of it.
457
+
458
+ ---
459
+
460
+ ## ✍️ Curation: from session noise to AGENTS.md
461
+
462
+ Knowledge worth keeping becomes a proposal; a human decides; only then is it
463
+ written — once, atomically, inside a marker pair you can edit around:
464
+
465
+ ```
466
+ memories ──▶ kevin_propose ──▶ pending proposals (unified diffs, no writes)
467
+
468
+ HUMAN REVIEW
469
+
470
+ ┌── approve ────┴──── reject ──▶ decision recorded
471
+ ▼ nothing written
472
+ AGENTS.md updated atomically ◀── the single write path (D6-01)
473
+ ```
474
+
475
+ ```markdown
476
+ <!-- kevin:begin curated by opencode-kevin, safe to edit -->
477
+ - Always run `npm run typecheck` before committing (fixed 3 CI failures)
478
+ <!-- kevin:end -->
479
+ ```
480
+
481
+ Deliberately conservative: only non-inferable memories are eligible — an
482
+ LLM-recoverable diagnostic is not something a human should have to review into
483
+ a permanent rule. Re-applying an unchanged plan is a counted noop, never a
484
+ write. **Kevin can propose. Humans decide.**
485
+
486
+ ---
487
+
488
+ ## 👥 Team sharing: one file, zero servers
489
+
490
+ Opt-in via `shared_layer_enabled='1'`. Curated knowledge exports to one
491
+ `.kevin/knowledge.okf` — header lines plus one JSON entry per line, sorted by a
492
+ deterministic `entry_id`, LF-only, ≤4096 bytes per line — designed so git
493
+ merges are meaningful and conflicts are parseable:
494
+
495
+ ```
496
+ DEV A DEV B
497
+ share (approval-gated) │ git pull
498
+ ▼ ▼
499
+ knowledge.okf ◀═══════ git ══════▶ knowledge.okf
500
+ │ kevin_sync
501
+
502
+ projected into local memories
503
+ (layer='shared', immutable)
504
+ ```
505
+
506
+ Repository identity resolves **once**, in order:
507
+ `.kevin/project.json` (declared) → git remote hash (never a raw URL) → path.
508
+ Two clones of the same repository are one team; different repositories never
509
+ leak into each other. Sharing requires explicit approval
510
+ (`share_requires_approval='1'`), author identity is hashed by default, and
511
+ tombstones archive rather than delete.
512
+
513
+ ---
514
+
515
+ ## 📜 The contract
516
+
517
+ Kevin 2.x makes promises about its published surface **in writing**.
518
+ [`docs/CONTRACT.md`](docs/CONTRACT.md) freezes fourteen clauses `C-01` `C-14` —
519
+ from the `AGENTS.md` marker bytes to MemorySources, each tagged `frozen`
520
+ or `forward-only` and stamped with the release that incurred the obligation. A
521
+ test diffs the live contract against an append-only golden file on every run:
522
+ removals and silent changes fail loudly; additions must carry `since`; `C-10` core exports (`1.3.0`), `C-11` MCP (`1.4.0`), `C-12` skills (`1.5.0`), `C-13` MIF (`1.5.0`), `C-14` MemorySources (`2.0.0`) — succession carried verbatim. `kevin_contract` v2 reports `contract_version: 2`.
523
+
524
+ ```jsonc
525
+ // kevin_contract (excerpt)
526
+ {
527
+ "contract_version": 2,
528
+ "digest": "a1b2c3d4e5f6a7b8",
529
+ "clauses": [
530
+ { "id": "C-03", "title": "Tool names and argument shapes", "stability": "frozen", "since": "0.2.0" },
531
+ { "id": "C-07", "title": "Database schema", "stability": "forward-only", "since": "0.1.0" },
532
+ { "id": "C-14", "title": "MemorySources", "stability": "frozen", "since": "2.0.0" }
533
+ ]
534
+ }
535
+ ```
536
+
537
+ > 1.0.0 is not just a version number — it is where Kevin starts making explicit, testable promises about its surface. 2.0.0 proves succession: every carried clause byte-equal.
538
+
539
+ ---
540
+
541
+ ## ⏱️ Hooks & latency budgets
542
+
543
+ Six host hooks plus Kevin's own `dispose` checkpoint plus two MCP bridges plus `sources.sync` eleven measured scopes,
544
+ each with a declared p95/max budget enforced by `npm run bench:check`:
545
+
546
+ | Scope | p95 budget | max |
547
+ |---|---:|---:|
548
+ | `tool.execute.before` | 2 ms | 10 ms |
549
+ | `tool.execute.after` | 5 ms | 25 ms |
550
+ | `chat.message` | 2 ms | 10 ms |
551
+ | `chat.system.transform` | 15 ms | 50 ms |
552
+ | `session.compacting` | 15 ms | 50 ms |
553
+ | `event` | 5 ms | 25 ms |
554
+ | `session.idle` | 150 ms | 600 ms |
555
+ | `dispose` | 50 ms | 250 ms |
556
+ | `mcp.read` | 25 ms | 100 ms |
557
+ | `mcp.write` | 50 ms | 250 ms |
558
+ | `sources.sync` | 50 ms | 250 ms |
559
+
560
+ Measured on the reference laptop: retrieval p50 0.2 ms, p95 < 1 ms — orders
561
+ of magnitude under budget. Samples persist to `perf_samples` at idle; a breach
562
+ degrades `kevin_doctor`'s verdict, because a plugin that is technically alive
563
+ but consistently slow is not healthy.
564
+
565
+ ---
566
+
567
+ ## ⚙️ Configuration
568
+
569
+ Everything is a TEXT setting managed through `kevin_config` (or any SQLite
570
+ client). All values are TEXT flags compare with `=== "1"`, never truthiness.
571
+
572
+ | Key | Default | Purpose |
573
+ |---|---|---|
574
+ | `quality_gate_enabled` | `'1'` | Weak lessons stored but never injected |
575
+ | `lesson_snippet_injection` | `'1'` | Compact 2-line snippets instead of full bodies |
576
+ | `patternminer_enabled` | `'0'` | Deterministic tool-sequence mining (≥5 sessions) |
577
+ | `cross_project_enabled` | `'0'` | Include imported cross-project rows |
578
+ | `llm_reflection_enabled` | `'0'` | Opt-in LLM enrichment at pattern promotion |
579
+ | `tool_calls_dedup_enabled` | `'0'` | Suppress duplicate call recordings per minute bucket |
580
+ | `deterministic_retrieval` | `'0'` | Freeze the clock for hermetic tests/replay |
581
+ | `pre_prompt_budget_tokens` | `'400'` | Pre-prompt injection cap (clamped `[0, 4000]`) |
582
+ | `archive_after_days` | `'30'` | Age threshold for idle archival |
583
+ | `curation_enabled` | `'1'` | Idle dry-run proposal generation |
584
+ | `agents_md_path` | `'AGENTS.md'` | Where curated knowledge lands |
585
+ | `skill_emission_enabled` | `'0'` | Register project-knowledge skill on v2 hosts |
586
+ | `reference_emission_enabled` | `'0'` | Register `@kevin/<topic>` mentions |
587
+ | `injection_confidence_floor` | `'0.6'` | Memories below this never inject |
588
+ | `repo_truth_enabled` | `'0'` | Repository fact scanning |
589
+ | `convention_mining_enabled` | `'0'` | Deterministic convention mining |
590
+ | `conflict_detection_enabled` | `'0'` | Contradiction surfacing |
591
+ | `error_lesson_mode` | `'all'` | Error lesson injection mode |
592
+ | `shared_layer_enabled` | `'0'` | Team sharing via `.kevin/knowledge.okf` |
593
+ | `okf_path` | `'.kevin/knowledge.okf'` | Shared file location |
594
+ | `share_requires_approval` | `'1'` | No export without human confirmation |
595
+ | `author_identity_mode` | `'hashed'` | Author identity hashed, never raw email |
596
+ | `shared_confidence_floor` | `'0.7'` | Confidence floor for shared projections |
597
+ | `hook_liveness_enabled` | `'1'` | Per-hook liveness tracking |
598
+ | `native_registration_enabled` | `'0'` | v2 native skill/reference registration |
599
+ | `host_probe_history_enabled` | `'0'` | Append-only probe history |
600
+ | `dead_hook_report_threshold` | `'3'` | Consecutive misses before a hook reads dead |
601
+ | `perf_enabled` | `'1'` | Latency instrumentation |
602
+ | `perf_ring_capacity` | `'512'` | Samples per scope (clamped `[64, 8192]`) |
603
+ | `perf_flush_on_idle` | `'1'` | Persist samples at idle |
604
+ | `contract_report_enabled` | `'1'` | Contract block in `kevin_audit` |
605
+ | `tui_snapshots_enabled` | `'1'` | Snapshot + dashboard flush at idle (opencode-kevin/tui) |
606
+ | `mcp_write_enabled` | `'0'` | Gate for MCP `save` (refused counter `mcp_writes_refused`) |
607
+ | `mcp_approve_enabled` | `'0'` | Double-gate for MCP `approve`/`share` (needs + `mcp_write_enabled`) |
608
+ | `mcp_repo_override` | `''` | Override `RepoIdentity` for MCP (hex-16 or empty) |
609
+ | `skills_canonical_dir` | `'.agents/skills'` | Canonical skills root (validated: no absolute, no "..") |
610
+ | `skills_mirror_claude` | `'0'` | Gate for `.claude/skills` exact byte copy |
611
+ | `skills_mirror_cursor` | `'0'` | Gate for `.cursor/skills` exact byte copy |
612
+ | `sources_enabled` | `'1'` | Master toggle for MemorySources (idle, bounded 10s) |
613
+ | `source_claude_memory` | `'0'` | Per-source gate: claude-memory (20) |
614
+ | `source_codex_memories` | `'0'` | Per-source gate: codex-memories (30) |
615
+ | `source_opencode_native` | `'0'` | Per-source gate: opencode-native (40) |
616
+ | `okf_write_version` | `'3'` | OKF writer version `2` legacy / `3` sharded (rollback byte-exact) |
617
+
618
+ > `import_host_memory` retired in `2.0.0` → `{error:"removed_in_2.0.0", replacement:"sources_*"}` — one-shot translation to `sources_enabled` + per-source flags on first `014` migration. `mcp_repo_override` still gates MCP identity; `okf_write_version='3'` shards, `'2'` rolls back byte-exact.
619
+
620
+ ---
621
+
622
+ ## 🖥️ Supported runtimes
623
+
624
+ | Runtime | SQLite backend | Status |
625
+ |---|---|---|
626
+ | Node 24.x | `node:sqlite` (stable) | ✅ **Supported** — the reference row |
627
+ | Node 22.5+ | `better-sqlite3` (optional dep) | ⚠️ **Supported with a caveat** — needs a build toolchain; without one there is no backend (and npm install still succeeds silently) |
628
+ | Node 22.5+ | `node:sqlite` behind `--experimental-sqlite` | 🔶 Works, unsupported exercised in CI, not promised |
629
+ | Bun 1.1 | `bun:sqlite` | **Supported** — smoke-tested in `npm run verify` |
630
+
631
+ Zero network callsasserted by source scan (forbidden: http/https/net/dgram/fetch/SSE/HttpTransport/child_process/spawn) on every test run; MCP uses stdio only, no spawn, logs on stderr only.
632
+
633
+ ---
634
+
635
+ ## 🎨 Design & trust
636
+
637
+ **Principles.** Local first · deterministic by default (no LLM in the core
638
+ loop) · evidence over vibes (every memory carries evidence, provenance,
639
+ recurrence and confidence) · signal over noise (inject *less, better* context,
640
+ not more) · humans hold the write boundary · git-friendly collaboration ·
641
+ important claims become executable checks.
642
+
643
+ **Security model.** Stored knowledge is treated as untrusted input: anything
644
+ reaching an artifact or prompt passes through idempotent escaping at the
645
+ single write path; permanent project-file changes require explicit human
646
+ approval; author identity in the shared layer is hashed
647
+ (`author_identity_mode='hashed'`). No network service is required to store,
648
+ retrieve or share knowledge.
649
+
650
+ **Without memory vs with Kevin:**
651
+
652
+ ```
653
+ without: session 1 solve ─▶ context closes ─▶ session 2 same problem ─▶ solve again
654
+
655
+ with: session 1 solve ─▶ lesson ─▶ memory ─┐
656
+ session 2 problem ◀── recall ◀───────┘ ─▶ fix ─▶ evidence ↑
657
+ ```
658
+
659
+ **Why not just `AGENTS.md`?** It is excellent for durable, human-authored
660
+ guidanceKevin treats it as a curated destination, not as a memory system.
661
+ It cannot do failure/fix traces, confidence scoring, evidence tracking, ranked
662
+ retrieval, recurrence signals, dry-run injection inspection, automatic
663
+ proposal generation, team projections, or performance and contract
664
+ instrumentation. That is what Kevin adds around it.
665
+
666
+ ---
667
+
668
+ ## 🛠️ Development
669
+
670
+ ```bash
671
+ npm install # hoists workspaces (root private, 4 packages)
672
+ npm run build # core → tui → plugin → mcp (tsc + copy-migrations)
673
+ npm run typecheck # -w core -w tui -w plugin -w mcp + root (strict)
674
+ npm run lint # biome
675
+ npm test # vitest — root suite (233 files)
676
+ npm test -w @jmtrin/kevin-core # core only — also passes with @opencode-ai/plugin absent (K13-013)
677
+ npm run verify # install checks + Bun smoke + verify:pack (×2 + consumer)
678
+ npm run verify:pack # dual-tarball + offline consumer smoke (K13-014)
679
+ npm run gen:corpus # regenerate the seeded corpus (byte-identical)
680
+ npm run replay # replay recorded sessions deterministically (now via core)
681
+ ```
682
+
683
+ Project layout (Bedrock monorepo):
684
+
685
+ ```
686
+ packages/core/ @jmtrin/kevin-core — ~60 modules (zero deps), src/*.ts, migrations/ 001→014, dist/migrations
687
+ packages/plugin/ @jmtrin/opencode-kevin — 4 modules (index, host, native, capabilities), adapter thin, depends on core+tui 2.0.0 exact
688
+ packages/tui/ @jmtrin/opencode-kevin-tui — isolated TUI panel (target-exclusive, own package.json/exports)
689
+ packages/mcp/ @jmtrin/kevin-mcp — MCP bridge (stdio, 11 tools, pure helper, identity+provenance)
690
+ scripts/ bench · gen-corpus · verify-pack (dual) · verify-install · …
691
+ tests/ unit · integration · e2e · replay fixtures (at root, run via workspaces)
692
+ bench/ committed corpus + committed results
693
+ docs/ CONTRACT.md · per-release plans/tasks · roadmap v2
694
+ ```
695
+
696
+ > **C-10 preview** — the future public surface is the explicit re-export list at `packages/core/src/index.ts` (keep it minimal and deliberate).
697
+
698
+ C-06 frozen: `plugin` package name `@jmtrin/opencode-kevin` and `exports["./tui"]` specifier unchanged; consumers see no break.
699
+
700
+ ---
701
+
702
+ ## 📄 License
703
+
704
+ MIT — see the package manifest. Kevin is built by [jmtrin](https://github.com/jmtrin);
705
+ bug reports and PRs welcome at the [issue tracker](https://github.com/jmtrin/opencode-kevin/issues).
706
+
707
+ <div align="center">
708
+
709
+ ### ⚡ Kevin — *Observe. Learn. Remember. Improve.*
710
+
711
+ </div>