@jmtrin/opencode-kevin 1.1.0 → 1.3.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.
Files changed (131) hide show
  1. package/dist/plugin/capabilities.d.ts +2 -0
  2. package/dist/plugin/capabilities.js +3 -0
  3. package/dist/plugin/index.d.ts +5 -4
  4. package/dist/plugin/index.js +401 -108
  5. package/dist/plugin/native.d.ts +2 -2
  6. package/dist/plugin/native.js +2 -2
  7. package/package.json +56 -63
  8. package/LICENSE +0 -21
  9. package/README.md +0 -576
  10. package/dist/migrations/001_initial.sql +0 -92
  11. package/dist/migrations/002_indexes.sql +0 -14
  12. package/dist/migrations/003_v02_signal.sql +0 -58
  13. package/dist/migrations/004_v03_knowledge.sql +0 -138
  14. package/dist/migrations/005_v04_signal.sql +0 -57
  15. package/dist/migrations/006_v05_glassbox.sql +0 -118
  16. package/dist/migrations/007_v06_pull.sql +0 -145
  17. package/dist/migrations/008_v07_truth.sql +0 -124
  18. package/dist/migrations/009_v08_team.sql +0 -100
  19. package/dist/migrations/010_v09_native.sql +0 -78
  20. package/dist/migrations/011_v10_proven.sql +0 -69
  21. package/dist/migrations/012_v11_drift.sql +0 -24
  22. package/dist/plugin/Archiver.d.ts +0 -42
  23. package/dist/plugin/Archiver.js +0 -83
  24. package/dist/plugin/ArtifactWriter.d.ts +0 -93
  25. package/dist/plugin/ArtifactWriter.js +0 -291
  26. package/dist/plugin/CausalChain.d.ts +0 -22
  27. package/dist/plugin/CausalChain.js +0 -197
  28. package/dist/plugin/ConflictDetector.d.ts +0 -35
  29. package/dist/plugin/ConflictDetector.js +0 -260
  30. package/dist/plugin/ContextInjector.d.ts +0 -222
  31. package/dist/plugin/ContextInjector.js +0 -533
  32. package/dist/plugin/ConventionMiner.d.ts +0 -35
  33. package/dist/plugin/ConventionMiner.js +0 -242
  34. package/dist/plugin/Curator.d.ts +0 -116
  35. package/dist/plugin/Curator.js +0 -324
  36. package/dist/plugin/Feedback.d.ts +0 -67
  37. package/dist/plugin/Feedback.js +0 -120
  38. package/dist/plugin/HookLiveness.d.ts +0 -88
  39. package/dist/plugin/HookLiveness.js +0 -322
  40. package/dist/plugin/InjectionLedger.d.ts +0 -98
  41. package/dist/plugin/InjectionLedger.js +0 -295
  42. package/dist/plugin/LessonFixer.d.ts +0 -44
  43. package/dist/plugin/LessonFixer.js +0 -45
  44. package/dist/plugin/Materializer.d.ts +0 -83
  45. package/dist/plugin/Materializer.js +0 -181
  46. package/dist/plugin/MemoryService.d.ts +0 -360
  47. package/dist/plugin/MemoryService.js +0 -1242
  48. package/dist/plugin/Migrate.d.ts +0 -17
  49. package/dist/plugin/Migrate.js +0 -271
  50. package/dist/plugin/PatternMiner.d.ts +0 -49
  51. package/dist/plugin/PatternMiner.js +0 -132
  52. package/dist/plugin/QualityGate.d.ts +0 -124
  53. package/dist/plugin/QualityGate.js +0 -119
  54. package/dist/plugin/Reflector.d.ts +0 -91
  55. package/dist/plugin/Reflector.js +0 -307
  56. package/dist/plugin/RepoIdentity.d.ts +0 -124
  57. package/dist/plugin/RepoIdentity.js +0 -300
  58. package/dist/plugin/RepoTruth.d.ts +0 -80
  59. package/dist/plugin/RepoTruth.js +0 -599
  60. package/dist/plugin/Retrospective.d.ts +0 -17
  61. package/dist/plugin/Retrospective.js +0 -213
  62. package/dist/plugin/SharedLayer.d.ts +0 -159
  63. package/dist/plugin/SharedLayer.js +0 -462
  64. package/dist/plugin/Store.d.ts +0 -29
  65. package/dist/plugin/Store.js +0 -50
  66. package/dist/plugin/ToolCallObserver.d.ts +0 -32
  67. package/dist/plugin/ToolCallObserver.js +0 -201
  68. package/dist/plugin/columns.d.ts +0 -11
  69. package/dist/plugin/columns.js +0 -54
  70. package/dist/plugin/confidence.d.ts +0 -8
  71. package/dist/plugin/confidence.js +0 -34
  72. package/dist/plugin/contract.d.ts +0 -54
  73. package/dist/plugin/contract.js +0 -383
  74. package/dist/plugin/diff.d.ts +0 -8
  75. package/dist/plugin/diff.js +0 -182
  76. package/dist/plugin/escape.d.ts +0 -39
  77. package/dist/plugin/escape.js +0 -60
  78. package/dist/plugin/fingerprint.d.ts +0 -27
  79. package/dist/plugin/fingerprint.js +0 -73
  80. package/dist/plugin/inferability.d.ts +0 -32
  81. package/dist/plugin/inferability.js +0 -88
  82. package/dist/plugin/kevin_approve.d.ts +0 -34
  83. package/dist/plugin/kevin_approve.js +0 -52
  84. package/dist/plugin/kevin_audit.d.ts +0 -213
  85. package/dist/plugin/kevin_audit.js +0 -563
  86. package/dist/plugin/kevin_bench.d.ts +0 -10
  87. package/dist/plugin/kevin_bench.js +0 -82
  88. package/dist/plugin/kevin_conflicts.d.ts +0 -9
  89. package/dist/plugin/kevin_conflicts.js +0 -50
  90. package/dist/plugin/kevin_contract.d.ts +0 -8
  91. package/dist/plugin/kevin_contract.js +0 -59
  92. package/dist/plugin/kevin_doctor.d.ts +0 -62
  93. package/dist/plugin/kevin_doctor.js +0 -196
  94. package/dist/plugin/kevin_facts.d.ts +0 -42
  95. package/dist/plugin/kevin_facts.js +0 -36
  96. package/dist/plugin/kevin_forget.d.ts +0 -33
  97. package/dist/plugin/kevin_forget.js +0 -260
  98. package/dist/plugin/kevin_native.d.ts +0 -29
  99. package/dist/plugin/kevin_native.js +0 -79
  100. package/dist/plugin/kevin_propose.d.ts +0 -23
  101. package/dist/plugin/kevin_propose.js +0 -14
  102. package/dist/plugin/kevin_publish.d.ts +0 -38
  103. package/dist/plugin/kevin_publish.js +0 -18
  104. package/dist/plugin/kevin_why.d.ts +0 -23
  105. package/dist/plugin/kevin_why.js +0 -111
  106. package/dist/plugin/memory-format.d.ts +0 -20
  107. package/dist/plugin/memory-format.js +0 -59
  108. package/dist/plugin/metrics.d.ts +0 -97
  109. package/dist/plugin/metrics.js +0 -284
  110. package/dist/plugin/okf-export.d.ts +0 -3
  111. package/dist/plugin/okf-export.js +0 -134
  112. package/dist/plugin/okf-import.d.ts +0 -76
  113. package/dist/plugin/okf-import.js +0 -271
  114. package/dist/plugin/okf.d.ts +0 -107
  115. package/dist/plugin/okf.js +0 -303
  116. package/dist/plugin/perf.d.ts +0 -35
  117. package/dist/plugin/perf.js +0 -174
  118. package/dist/plugin/query-tokenizer.d.ts +0 -13
  119. package/dist/plugin/query-tokenizer.js +0 -133
  120. package/dist/plugin/redact.d.ts +0 -2
  121. package/dist/plugin/redact.js +0 -18
  122. package/dist/plugin/replay-types.d.ts +0 -69
  123. package/dist/plugin/replay-types.js +0 -156
  124. package/dist/plugin/replay.d.ts +0 -36
  125. package/dist/plugin/replay.js +0 -202
  126. package/dist/plugin/sqlite-adapter.d.ts +0 -12
  127. package/dist/plugin/sqlite-adapter.js +0 -81
  128. package/dist/plugin/time-ms.d.ts +0 -1
  129. package/dist/plugin/time-ms.js +0 -16
  130. package/dist/plugin/uuid.d.ts +0 -1
  131. package/dist/plugin/uuid.js +0 -50
package/README.md DELETED
@@ -1,576 +0,0 @@
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.1.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.1.0](#-whats-new-in-110)
52
- - [What's new in 1.0.0](#-whats-new-in-100)
53
- - [How it works](#-how-it-works)
54
- - [The 26 tools](#-the-26-tools)
55
- - [The benchmark: proven, not promised](#-the-benchmark-proven-not-promised)
56
- - [Curation: from session noise to AGENTS.md](#-curation-from-session-noise-to-agentsmd)
57
- - [Team sharing: one file, zero servers](#-team-sharing-one-file-zero-servers)
58
- - [The contract](#-the-contract)
59
- - [Hooks & latency budgets](#-hooks--latency-budgets)
60
- - [Configuration](#%EF%B8%8F-configuration)
61
- - [Supported runtimes](#-supported-runtimes)
62
- - [Design & trust](#-design--trust)
63
- - [Development](#-development)
64
- - [License](#-license)
65
-
66
- ---
67
-
68
- ## 🤔 Why Kevin
69
-
70
- Every coding session produces experience — errors, fixes, decisions,
71
- conventions, lessons. Without memory, most of it evaporates when the context
72
- window closes: the next session starts near zero and walks straight into the
73
- same wall.
74
-
75
- Kevin closes that loop:
76
-
77
- | 🔍 Observe | 🧠 Learn | 🎯 Recall | ✍️ Curate | 👥 Share | 📏 Prove |
78
- |---|---|---|---|---|---|
79
- | 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 |
80
-
81
- **Local first, by design.** Your coding experience belongs to you:
82
-
83
- ```
84
- no cloud service · no telemetry · no network calls · no hidden write path
85
- ```
86
-
87
- Knowledge lives in a local SQLite database, is projected into files you
88
- control, and is shared — optionally — through a single file you can read,
89
- diff and review like code.
90
-
91
- ---
92
-
93
- ## 🔁 The Kevin loop
94
-
95
- ```
96
- CODING SESSION
97
-
98
-
99
- 🔍 OBSERVE ────── tool calls, chat signals, failures
100
-
101
-
102
- 🧠 LEARN ─────── lessons, causal chains, patterns
103
-
104
-
105
- 💾 REMEMBER ───── confidence · provenance · recurrence
106
-
107
-
108
- 🎯 RECALL ────── rank → gates → token budget
109
-
110
-
111
- MODEL CONTEXT ──── only what matters now
112
-
113
-
114
- ✍️ CURATE ─────── propose → HUMAN review → approve
115
-
116
-
117
- AGENTS.md
118
-
119
-
120
- NEXT SESSION ─────────────────▶ 🔁
121
- ```
122
-
123
- **Experience compounds instead of evaporating.**
124
-
125
- ---
126
-
127
- ## 🚀 Quick start
128
-
129
- ### 1. Declare the plugin
130
-
131
- ```jsonc
132
- // opencode.json
133
- {
134
- "plugin": ["@jmtrin/opencode-kevin"]
135
- }
136
- ```
137
-
138
- ```bash
139
- npm install @jmtrin/opencode-kevin
140
- ```
141
-
142
- ### 2. Restart OpenCode
143
-
144
- On first boot Kevin migrates its database to schema version `012` and starts
145
- observing. Nothing else is required.
146
-
147
- ### 3. Talk to it
148
-
149
- ```
150
- kevin_status → is everything healthy?
151
- kevin_why → why did this keep failing, and how was it fixed?
152
- kevin_query → what does Kevin remember about X?
153
- kevin_doctor → health report: hooks, deps, perf, verdict
154
- ```
155
-
156
- ### 4. Where data lives
157
-
158
- ```
159
- ~/.opencode-kevin/
160
- ├── kevin.db ← everything Kevin learns (SQLite, WAL)
161
- ├── skills/ ← generated pull channels
162
- └── refs/ ← topic reference bundles
163
-
164
- <repo>/.kevin/
165
- ├── AGENTS.md ← curated knowledge (marker block, human-approved)
166
- └── knowledge.okf ← optional team-sharing file (opt-in)
167
- ```
168
-
169
- ---
170
-
171
- ## 🆕 What's new in 1.1.0 — "Drift"
172
-
173
- > 1.1.0 protects what 1.0.0 proved: a published number without a regression gate is marketing.
174
-
175
- - 🛡️ **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.
176
- - 🗑️ **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`.
177
- - ⏱️ **Millisecond timestamps** — new `_ms` columns with conservative backfill; `settle()` and `CausalChain` now decide sub-second causality.
178
- - 🧹 **Debt paid** — one `STOP_WORDS` source, one `readOriginCallId`, `ConflictDetector` via `mapRow`, one column-probe registry; every setting has an on-path test.
179
- - 📜 **Public hygiene** — `LICENSE` (MIT), `homepage` filled, `docs/DISTRIBUTION.md` checklist, `scripts/release-notes.mjs` for `gh release create`, and `<!-- demo -->` slot.
180
-
181
- ---
182
-
183
- ## 🆕 What's new in 1.0.0
184
-
185
- > 1.0.0 is the **proven release**: the surface is frozen as data, the cost is
186
- > measured, the value is benchmarked — reproducibly.
187
-
188
- - ❄️ **A frozen public contract** — nine clauses (`C-01` … `C-09`) derived from
189
- live source, digest-stamped, enforced by an append-only golden file.
190
- Inspect it live with `kevin_contract`.
191
- - ⏱️ **Latency budgets** — eight instrumented scopes; `npm run bench:check`
192
- fails if any scope exceeds its p95 budget. `dispose` joins as the seventh
193
- hook with crash-safe deferred settlement.
194
- - 📊 **A reproducible benchmark** — committed synthetic corpus, four arms,
195
- committed result. See [below](#-the-benchmark-proven-not-promised).
196
- - 🛡️ **An untrusted-input boundary** — everything reaching an artifact or
197
- prompt is escaped at the single write path; stored text is never trusted.
198
- - 📦 **A corrected published package** — types-first exports, `dist/` only,
199
- verified against the packed tarball by `npm run verify:pack`.
200
-
201
- ---
202
-
203
- ## ⚙️ How it works
204
-
205
- Kevin is an intentionally deterministic pipeline — no LLM in the core loop:
206
-
207
- ```
208
- your coding session
209
-
210
-
211
- ┌────────────────────────────────────────────┐
212
- │ 🔍 OBSERVE │
213
- │ tool.execute.before/after · chat.message │
214
- │ Failures auto-detected from exit codes, │
215
- │ stderr and stdout markers. │
216
- └──────────────────┬─────────────────────────┘
217
-
218
- ┌────────────────────────────────────────────┐
219
- │ 🧠 LEARN │
220
- │ Reflector → lessons │
221
- │ CausalChain → failure/fix links │
222
- │ PatternMiner → repeated sequences │
223
- │ Evidence raises confidence; recurrence │
224
- │ lowers it and flags staleness. │
225
- └──────────────────┬─────────────────────────┘
226
-
227
- ┌────────────────────────────────────────────┐
228
- │ 🎯 INJECT │
229
- │ QualityGate → BM25 × origin × recency × │
230
- │ truth penalty → token-budget fit │
231
- │ Deduped per session, inside <kevin-context>│
232
- └──────────────────┬─────────────────────────┘
233
-
234
- ┌────────────────────────────────────────────┐
235
- │ ✍️ CURATE │
236
- │ propose → HUMAN REVIEW → approve │
237
- │ Nothing writes without approval. │
238
- └──────────────────┬─────────────────────────┘
239
-
240
- ┌────────────────────────────────────────────┐
241
- │ 📏 PROVE │
242
- │ perf budgets · retrieval benchmark · │
243
- │ contract digest │
244
- │ The system measures itself instead of │
245
- │ merely claiming to work. │
246
- └────────────────────────────────────────────┘
247
- ```
248
-
249
- ---
250
-
251
- ## 🧰 The 26 tools
252
-
253
- <details open>
254
- <summary><b>🧠 Core memory</b></summary>
255
-
256
- | Tool | What it does |
257
- |---|---|
258
- | `kevin_save` | Store a memory: decision, rule, pattern, context or solution |
259
- | `kevin_query` | Search memories — slim payload; `evidence: true` exposes confidence |
260
- | `kevin_get` | Fetch one memory in full |
261
- | `kevin_recall` | Ranked recall with origin-aware scoring |
262
- | `kevin_status` | Session scoreboard: counts, precision, metrics |
263
- | `kevin_config` | List/set any of the 31 settings — no SQL required |
264
- | `kevin_project` | Show, initialize or rekey the repository identity |
265
-
266
- </details>
267
-
268
- <details>
269
- <summary><b>🔎 Understanding & debugging</b></summary>
270
-
271
- | Tool | What it does |
272
- |---|---|
273
- | `kevin_why` | Failure→fix trace for a recurring error, with confidence and evidence |
274
- | `kevin_trace` | Dry-run: exactly what would be injected for a query, zero side effects |
275
- | `kevin_feedback` | Rate an injected memory `useful` / `wrong` / `outdated` / `ignore` |
276
- | `kevin_conflicts` | Surface contradictions between memories and repository truth |
277
- | `kevin_facts` | Scan the repository for ground-truth facts |
278
- | `kevin_retrospective` | Per-session markdown retrospective |
279
-
280
- </details>
281
-
282
- <details>
283
- <summary><b>✍️ Curation & publishing</b></summary>
284
-
285
- | Tool | What it does |
286
- |---|---|
287
- | `kevin_propose` | Dry-run curation proposals with unified diffs — writes nothing |
288
- | `kevin_approve` | The **only** path that writes `AGENTS.md` (or rejects) |
289
- | `kevin_publish` | Regenerate skill/ref pull bundles under `~/.opencode-kevin/` |
290
-
291
- </details>
292
-
293
- <details>
294
- <summary><b>👥 Team & operations</b></summary>
295
-
296
- | Tool | What it does |
297
- |---|---|
298
- | `kevin_share` | Promote curated memories into `.kevin/knowledge.okf` (approval-gated) |
299
- | `kevin_sync` | Import the shared file into the local layer |
300
- | `kevin_export` / `kevin_import` | Markdown/OKF bundles out and in |
301
- | `kevin_audit` | Whole-system report: memories, injections, channels, team, perf, contract |
302
- | `kevin_doctor` | Health verdict: `healthy` / `degraded` / `unknown`, with reasons |
303
- | `kevin_native` | Show/enable/disable native host registration (default off) |
304
- | `kevin_contract` | **v1.0.0** — inspect the frozen public surface at runtime |
305
- | `kevin_bench` | **v1.0.0** — report benchmark results; never runs them in-session |
306
-
307
- </details>
308
-
309
- ---
310
-
311
- ## 📊 The benchmark: proven, not promised
312
-
313
- Kevin ships a committed synthetic corpus and a four-arm harness measuring
314
- whether real retrieval beats trivial baselines at surfacing labelled-relevant
315
- memories:
316
-
317
- ```
318
- bench/corpus/
319
- seed: 1262835273 (xorshift32, byte-for-byte regenerable)
320
- digest: adecbdf4c7af82e2
321
- result: bench/results/2026-08-21-adecbdf4c7af82e2.json (k = 5)
322
- ```
323
-
324
- ```
325
- npm run bench # run the harness (also persists one row per arm)
326
- npm run bench:check # gate: every perf scope within its p95 budget
327
- npm run verify:pack # gate: seven properties against the packed tarball
328
- ```
329
-
330
- | Arm | Precision@5 | Recall@5 | MRR |
331
- |---|---:|---:|---:|
332
- | `none` (control) | 0.000 | 0.000 | 0.000 |
333
- | `recent-k` (baseline) | 0.050 | 0.026 | 0.109 |
334
- | `random-k` (floor) | 0.048 | 0.028 | 0.093 |
335
- | **`kevin`** | **0.950** | **0.546** | **1.000** |
336
-
337
- ```
338
- Precision@5 Recall@5 MRR
339
- 0.950 ██████████████████░░ 0.546 ██████████░░░░ 1.000 ████████████████████
340
- 0.050 █░░░░░░░░░░░░░░░░░░░ 0.026 █░░░░░░░░░░░░░ 0.109 ██░░░░░░░░░░░░░░░░░░
341
- ```
342
-
343
- The labelling rule is mechanical and the retrieval numbers are exactly
344
- reproducible — asserted by running the harness twice in-process.
345
-
346
- **Honest limits, stated up front.** This benchmark measures retrieval on a
347
- 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.
348
-
349
- ---
350
-
351
- ## ✍️ Curation: from session noise to AGENTS.md
352
-
353
- Knowledge worth keeping becomes a proposal; a human decides; only then is it
354
- written — once, atomically, inside a marker pair you can edit around:
355
-
356
- ```
357
- memories ──▶ kevin_propose ──▶ pending proposals (unified diffs, no writes)
358
-
359
- HUMAN REVIEW
360
-
361
- ┌── approve ────┴──── reject ──▶ decision recorded
362
- ▼ nothing written
363
- AGENTS.md updated atomically ◀── the single write path (D6-01)
364
- ```
365
-
366
- ```markdown
367
- <!-- kevin:begin — curated by opencode-kevin, safe to edit -->
368
- - Always run `npm run typecheck` before committing (fixed 3 CI failures)
369
- <!-- kevin:end -->
370
- ```
371
-
372
- Deliberately conservative: only non-inferable memories are eligible — an
373
- LLM-recoverable diagnostic is not something a human should have to review into
374
- a permanent rule. Re-applying an unchanged plan is a counted noop, never a
375
- write. **Kevin can propose. Humans decide.**
376
-
377
- ---
378
-
379
- ## 👥 Team sharing: one file, zero servers
380
-
381
- Opt-in via `shared_layer_enabled='1'`. Curated knowledge exports to one
382
- `.kevin/knowledge.okf` — header lines plus one JSON entry per line, sorted by a
383
- deterministic `entry_id`, LF-only, ≤4096 bytes per line — designed so git
384
- merges are meaningful and conflicts are parseable:
385
-
386
- ```
387
- DEV A DEV B
388
- │ share (approval-gated) │ git pull
389
- ▼ ▼
390
- knowledge.okf ◀═══════ git ══════▶ knowledge.okf
391
- │ kevin_sync
392
-
393
- projected into local memories
394
- (layer='shared', immutable)
395
- ```
396
-
397
- Repository identity resolves **once**, in order:
398
- `.kevin/project.json` (declared) → git remote hash (never a raw URL) → path.
399
- Two clones of the same repository are one team; different repositories never
400
- leak into each other. Sharing requires explicit approval
401
- (`share_requires_approval='1'`), author identity is hashed by default, and
402
- tombstones archive rather than delete.
403
-
404
- ---
405
-
406
- ## 📜 The contract
407
-
408
- Kevin 1.x makes promises about its published surface **in writing**.
409
- [`docs/CONTRACT.md`](docs/CONTRACT.md) freezes nine clauses — `C-01` … `C-09` —
410
- from the `AGENTS.md` marker bytes to the database schema, each tagged `frozen`
411
- or `forward-only` and stamped with the release that incurred the obligation. A
412
- test diffs the live contract against an append-only golden file on every run:
413
- removals and silent changes fail loudly; additions must carry `since`.
414
-
415
- ```jsonc
416
- // kevin_contract (excerpt)
417
- {
418
- "contract_version": 1,
419
- "digest": "1de9740bba2e9f95",
420
- "clauses": [
421
- { "id": "C-03", "title": "Tool names and argument shapes", "stability": "frozen", "since": "0.2.0" },
422
- { "id": "C-07", "title": "Database schema", "stability": "forward-only", "since": "0.1.0" }
423
- ]
424
- }
425
- ```
426
-
427
- > 1.0.0 is not just a version number — it is where Kevin starts making explicit, testable promises about its surface.
428
-
429
- ---
430
-
431
- ## ⏱️ Hooks & latency budgets
432
-
433
- Six host hooks plus Kevin's own `dispose` checkpoint — eight measured scopes,
434
- each with a declared p95/max budget enforced by `npm run bench:check`:
435
-
436
- | Scope | p95 budget | max |
437
- |---|---:|---:|
438
- | `tool.execute.before` | 2 ms | 10 ms |
439
- | `tool.execute.after` | 5 ms | 25 ms |
440
- | `chat.message` | 2 ms | 10 ms |
441
- | `chat.system.transform` | 15 ms | 50 ms |
442
- | `session.compacting` | 15 ms | 50 ms |
443
- | `event` | 5 ms | 25 ms |
444
- | `session.idle` | 150 ms | 600 ms |
445
- | `dispose` | 50 ms | 250 ms |
446
-
447
- Measured on the reference laptop: retrieval p50 ≈ 0.2 ms, p95 < 1 ms — orders
448
- of magnitude under budget. Samples persist to `perf_samples` at idle; a breach
449
- degrades `kevin_doctor`'s verdict, because a plugin that is technically alive
450
- but consistently slow is not healthy.
451
-
452
- ---
453
-
454
- ## ⚙️ Configuration
455
-
456
- Everything is a TEXT setting managed through `kevin_config` (or any SQLite
457
- client). All values are TEXT — flags compare with `=== "1"`, never truthiness.
458
-
459
- | Key | Default | Purpose |
460
- |---|---|---|
461
- | `quality_gate_enabled` | `'1'` | Weak lessons stored but never injected |
462
- | `lesson_snippet_injection` | `'1'` | Compact 2-line snippets instead of full bodies |
463
- | `patternminer_enabled` | `'0'` | Deterministic tool-sequence mining (≥5 sessions) |
464
- | `cross_project_enabled` | `'0'` | Include imported cross-project rows |
465
- | `llm_reflection_enabled` | `'0'` | Opt-in LLM enrichment at pattern promotion |
466
- | `tool_calls_dedup_enabled` | `'0'` | Suppress duplicate call recordings per minute bucket |
467
- | `deterministic_retrieval` | `'0'` | Freeze the clock for hermetic tests/replay |
468
- | `pre_prompt_budget_tokens` | `'400'` | Pre-prompt injection cap (clamped `[0, 4000]`) |
469
- | `archive_after_days` | `'30'` | Age threshold for idle archival |
470
- | `curation_enabled` | `'1'` | Idle dry-run proposal generation |
471
- | `agents_md_path` | `'AGENTS.md'` | Where curated knowledge lands |
472
- | `skill_emission_enabled` | `'0'` | Register project-knowledge skill on v2 hosts |
473
- | `reference_emission_enabled` | `'0'` | Register `@kevin/<topic>` mentions |
474
- | `injection_confidence_floor` | `'0.6'` | Memories below this never inject |
475
- | `repo_truth_enabled` | `'0'` | Repository fact scanning |
476
- | `convention_mining_enabled` | `'0'` | Deterministic convention mining |
477
- | `conflict_detection_enabled` | `'0'` | Contradiction surfacing |
478
- | `error_lesson_mode` | `'all'` | Error lesson injection mode |
479
- | `shared_layer_enabled` | `'0'` | Team sharing via `.kevin/knowledge.okf` |
480
- | `okf_path` | `'.kevin/knowledge.okf'` | Shared file location |
481
- | `share_requires_approval` | `'1'` | No export without human confirmation |
482
- | `author_identity_mode` | `'hashed'` | Author identity hashed, never raw email |
483
- | `shared_confidence_floor` | `'0.7'` | Confidence floor for shared projections |
484
- | `hook_liveness_enabled` | `'1'` | Per-hook liveness tracking |
485
- | `native_registration_enabled` | `'0'` | v2 native skill/reference registration |
486
- | `host_probe_history_enabled` | `'0'` | Append-only probe history |
487
- | `dead_hook_report_threshold` | `'3'` | Consecutive misses before a hook reads dead |
488
- | `perf_enabled` | `'1'` | Latency instrumentation |
489
- | `perf_ring_capacity` | `'512'` | Samples per scope (clamped `[64, 8192]`) |
490
- | `perf_flush_on_idle` | `'1'` | Persist samples at idle |
491
- | `contract_report_enabled` | `'1'` | Contract block in `kevin_audit` |
492
-
493
- ---
494
-
495
- ## 🖥️ Supported runtimes
496
-
497
- | Runtime | SQLite backend | Status |
498
- |---|---|---|
499
- | Node 24.x | `node:sqlite` (stable) | ✅ **Supported** — the reference row |
500
- | 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) |
501
- | Node 22.5+ | `node:sqlite` behind `--experimental-sqlite` | 🔶 Works, unsupported — exercised in CI, not promised |
502
- | Bun ≥ 1.1 | `bun:sqlite` | ✅ **Supported** — smoke-tested in `npm run verify` |
503
-
504
- Zero process spawns, zero network calls — asserted by source scan on every
505
- test run.
506
-
507
- ---
508
-
509
- ## 🎨 Design & trust
510
-
511
- **Principles.** Local first · deterministic by default (no LLM in the core
512
- loop) · evidence over vibes (every memory carries evidence, provenance,
513
- recurrence and confidence) · signal over noise (inject *less, better* context,
514
- not more) · humans hold the write boundary · git-friendly collaboration ·
515
- important claims become executable checks.
516
-
517
- **Security model.** Stored knowledge is treated as untrusted input: anything
518
- reaching an artifact or prompt passes through idempotent escaping at the
519
- single write path; permanent project-file changes require explicit human
520
- approval; author identity in the shared layer is hashed
521
- (`author_identity_mode='hashed'`). No network service is required to store,
522
- retrieve or share knowledge.
523
-
524
- **Without memory vs with Kevin:**
525
-
526
- ```
527
- without: session 1 solve ─▶ context closes ─▶ session 2 same problem ─▶ solve again
528
-
529
- with: session 1 solve ─▶ lesson ─▶ memory ─┐
530
- session 2 problem ◀── recall ◀───────┘ ─▶ fix ─▶ evidence ↑
531
- ```
532
-
533
- **Why not just `AGENTS.md`?** It is excellent for durable, human-authored
534
- guidance — Kevin treats it as a curated destination, not as a memory system.
535
- It cannot do failure/fix traces, confidence scoring, evidence tracking, ranked
536
- retrieval, recurrence signals, dry-run injection inspection, automatic
537
- proposal generation, team projections, or performance and contract
538
- instrumentation. That is what Kevin adds around it.
539
-
540
- ---
541
-
542
- ## 🛠️ Development
543
-
544
- ```bash
545
- npm install
546
- npm run typecheck # tsc --noEmit (strict)
547
- npm run lint # biome
548
- npm test # vitest — 1374 tests across 190 files
549
- npm run verify # install checks + Bun smoke + verify:pack
550
- npm run gen:corpus # regenerate the seeded corpus (byte-identical)
551
- npm run replay # replay recorded sessions deterministically
552
- ```
553
-
554
- Project layout:
555
-
556
- ```
557
- plugin/ 53 modules — Store, MemoryService, Reflector, Perf, Contract, columns, kevin_forget …
558
- migrations/ 001 → 012 — additive, idempotent, forward-only forever
559
- scripts/ bench · gen-corpus · verify-pack · verify-install · …
560
- tests/ unit · integration · e2e · replay fixtures
561
- bench/ committed corpus + committed results
562
- docs/ CONTRACT.md · per-release plans/tasks · roadmap
563
- ```
564
-
565
- ---
566
-
567
- ## 📄 License
568
-
569
- MIT — see the package manifest. Kevin is built by [jmtrin](https://github.com/jmtrin);
570
- bug reports and PRs welcome at the [issue tracker](https://github.com/jmtrin/opencode-kevin/issues).
571
-
572
- <div align="center">
573
-
574
- ### ⚡ Kevin — *Observe. Learn. Remember. Improve.*
575
-
576
- </div>
@@ -1,92 +0,0 @@
1
- -- ============================================================
2
- -- Kevin 0.1.0 — Schema inicial
3
- -- ============================================================
4
-
5
- -- Tabla de versiones para migraciones
6
- CREATE TABLE IF NOT EXISTS schema_version (
7
- version TEXT PRIMARY KEY,
8
- applied_at TEXT NOT NULL DEFAULT (datetime('now'))
9
- );
10
-
11
- -- ============================================================
12
- -- memories: lecciones aprendidas
13
- -- ============================================================
14
- CREATE TABLE IF NOT EXISTS memories (
15
- id TEXT PRIMARY KEY,
16
- type TEXT NOT NULL CHECK(type IN ('error', 'pattern', 'decision', 'context')),
17
- content TEXT NOT NULL,
18
- scope TEXT NOT NULL DEFAULT 'project' CHECK(scope IN ('project', 'session')),
19
- relevance_score REAL DEFAULT 0.5,
20
- source_tool TEXT,
21
- source_session TEXT,
22
- metadata TEXT,
23
- created_at TEXT NOT NULL DEFAULT (datetime('now')),
24
- updated_at TEXT NOT NULL DEFAULT (datetime('now')),
25
- expires_at TEXT
26
- );
27
-
28
- CREATE INDEX IF NOT EXISTS idx_memories_type ON memories(type);
29
- CREATE INDEX IF NOT EXISTS idx_memories_scope ON memories(scope);
30
- CREATE INDEX IF NOT EXISTS idx_memories_relevance ON memories(relevance_score DESC);
31
- CREATE INDEX IF NOT EXISTS idx_memories_created ON memories(created_at);
32
-
33
- -- FTS5: búsqueda full-text con remoción de diacríticos (mejor para español)
34
- CREATE VIRTUAL TABLE IF NOT EXISTS memories_fts USING fts5(
35
- content,
36
- content='memories',
37
- tokenize='unicode61 remove_diacritics 1'
38
- );
39
-
40
- -- Triggers para mantener FTS5 sincronizado (FTS5 external-content se indexa por rowid)
41
- CREATE TRIGGER IF NOT EXISTS memories_ai AFTER INSERT ON memories BEGIN
42
- INSERT INTO memories_fts(rowid, content) VALUES (new.rowid, new.content);
43
- END;
44
-
45
- CREATE TRIGGER IF NOT EXISTS memories_ad AFTER DELETE ON memories BEGIN
46
- INSERT INTO memories_fts(memories_fts, rowid, content) VALUES ('delete', old.rowid, old.content);
47
- END;
48
-
49
- CREATE TRIGGER IF NOT EXISTS memories_au AFTER UPDATE ON memories BEGIN
50
- INSERT INTO memories_fts(memories_fts, rowid, content) VALUES ('delete', old.rowid, old.content);
51
- INSERT INTO memories_fts(rowid, content) VALUES (new.rowid, new.content);
52
- END;
53
-
54
- -- ============================================================
55
- -- tool_calls: observación de tool calls del agente
56
- -- ============================================================
57
- CREATE TABLE IF NOT EXISTS tool_calls (
58
- id TEXT PRIMARY KEY,
59
- session_id TEXT NOT NULL,
60
- ts TEXT NOT NULL DEFAULT (datetime('now')),
61
- tool TEXT NOT NULL,
62
- args_summary TEXT,
63
- success INTEGER NOT NULL CHECK(success IN (0,1)),
64
- duration_ms INTEGER,
65
- agent TEXT,
66
- error_type TEXT,
67
- metadata TEXT
68
- );
69
-
70
- CREATE INDEX IF NOT EXISTS idx_tool_calls_session ON tool_calls(session_id);
71
- CREATE INDEX IF NOT EXISTS idx_tool_calls_tool ON tool_calls(tool);
72
- CREATE INDEX IF NOT EXISTS idx_tool_calls_ts ON tool_calls(ts);
73
- CREATE INDEX IF NOT EXISTS idx_tool_calls_success ON tool_calls(success);
74
-
75
- -- ============================================================
76
- -- retrospectives: resúmenes de sesión
77
- -- ============================================================
78
- CREATE TABLE IF NOT EXISTS retrospectives (
79
- id TEXT PRIMARY KEY,
80
- session_id TEXT NOT NULL,
81
- ts TEXT NOT NULL DEFAULT (datetime('now')),
82
- failure_count INTEGER DEFAULT 0,
83
- success_count INTEGER DEFAULT 0,
84
- lessons_count INTEGER DEFAULT 0,
85
- file_path TEXT,
86
- metadata TEXT
87
- );
88
-
89
- -- ============================================================
90
- -- Seed: versión inicial
91
- -- ============================================================
92
- INSERT OR IGNORE INTO schema_version (version) VALUES ('001');
@@ -1,14 +0,0 @@
1
- -- ============================================================
2
- -- Kevin 0.1.1 — Migration 002: indexes adicionales
3
- -- ============================================================
4
-
5
- -- F#29: uniqueness on retrospectives.session_id
6
- -- Prevents duplicate retrospective rows under concurrent session.idle events.
7
- CREATE UNIQUE INDEX IF NOT EXISTS idx_retrospectives_session
8
- ON retrospectives(session_id);
9
-
10
- -- F#31: index on memories.expires_at
11
- -- Every query/queryRelevant/loadAll filters WHERE (expires_at IS NULL OR expires_at > datetime('now')).
12
- -- Without this index the filter is a linear scan on large tables.
13
- CREATE INDEX IF NOT EXISTS idx_memories_expires
14
- ON memories(expires_at);