@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
@@ -1,563 +0,0 @@
1
- import { effectivePrePromptCap } from "./ContextInjector.js";
2
- import { hasRepoIdColumn } from "./MemoryService.js";
3
- import { contractDigest, describeContract } from "./contract.js";
4
- import { BUDGETS } from "./perf.js";
5
- const ALL_FALSE_CAPABILITIES = {
6
- skills: false,
7
- references: false,
8
- apiVersion: null,
9
- };
10
- function groupCounts(store, column) {
11
- const rows = store
12
- .prepare(`SELECT ${column} AS k, COUNT(*) AS n FROM memories GROUP BY ${column}`)
13
- .all();
14
- const out = {};
15
- for (const r of rows) {
16
- out[r.k ?? "unknown"] = r.n;
17
- }
18
- return out;
19
- }
20
- function scalar(store, sql, ...params) {
21
- const row = store.prepare(sql).get(...params);
22
- return row.n ?? 0;
23
- }
24
- export function buildAudit(store, metrics, capabilities = ALL_FALSE_CAPABILITIES, projectId, repoId) {
25
- let partial = false;
26
- // Memories block: `total` + the three GROUP BYs are pre-006 safe; the
27
- // lifecycle counters need the 006 columns and fall back separately.
28
- let memories = {
29
- total: 0,
30
- by_status: {},
31
- by_origin: {},
32
- by_type: {},
33
- ignored: 0,
34
- archived: 0,
35
- with_feedback: 0,
36
- superseded_with_target: 0,
37
- };
38
- try {
39
- memories = {
40
- ...memories,
41
- total: scalar(store, "SELECT COUNT(*) AS n FROM memories"),
42
- by_status: groupCounts(store, "status"),
43
- by_origin: groupCounts(store, "origin"),
44
- by_type: groupCounts(store, "type"),
45
- };
46
- }
47
- catch {
48
- partial = true;
49
- }
50
- try {
51
- memories = {
52
- ...memories,
53
- ignored: scalar(store, "SELECT COUNT(*) AS n FROM memories WHERE ignored = 1"),
54
- archived: scalar(store, "SELECT COUNT(*) AS n FROM memories WHERE status = 'archived'"),
55
- with_feedback: scalar(store, "SELECT COUNT(*) AS n FROM memories WHERE feedback_positive > 0 OR feedback_negative > 0"),
56
- superseded_with_target: scalar(store, "SELECT COUNT(*) AS n FROM memories WHERE status = 'superseded' AND superseded_by IS NOT NULL"),
57
- };
58
- }
59
- catch {
60
- partial = true;
61
- }
62
- // Injections block: grouped rollup over the ledger (same query the
63
- // InjectionLedger.outcomeCounts() runs), rates from the metric cache.
64
- let injections = {
65
- total: 0,
66
- effective: 0,
67
- ineffective: 0,
68
- inconclusive: 0,
69
- unmeasured: 0,
70
- precision_rate: 0,
71
- coverage_rate: 0,
72
- };
73
- try {
74
- const rows = store
75
- .prepare("SELECT outcome, COUNT(*) AS n FROM kevin_injections GROUP BY outcome")
76
- .all();
77
- const counts = {
78
- unmeasured: 0,
79
- effective: 0,
80
- ineffective: 0,
81
- inconclusive: 0,
82
- };
83
- for (const r of rows) {
84
- counts[r.outcome] = r.n;
85
- }
86
- injections = {
87
- total: rows.reduce((acc, r) => acc + r.n, 0),
88
- effective: counts.effective,
89
- ineffective: counts.ineffective,
90
- inconclusive: counts.inconclusive,
91
- unmeasured: counts.unmeasured,
92
- precision_rate: metrics.precisionRate(),
93
- coverage_rate: metrics.coverageRate(),
94
- };
95
- }
96
- catch {
97
- partial = true;
98
- }
99
- // Feedback block: verdict rollup from the table, totals from the cache.
100
- let feedback = {
101
- positive: 0,
102
- negative: 0,
103
- by_verdict: {},
104
- };
105
- try {
106
- const rows = store
107
- .prepare("SELECT verdict, COUNT(*) AS n FROM memory_feedback GROUP BY verdict")
108
- .all();
109
- const byVerdict = {};
110
- for (const r of rows) {
111
- byVerdict[r.verdict] = r.n;
112
- }
113
- feedback = {
114
- positive: metrics.get("feedback_positive_total"),
115
- negative: metrics.get("feedback_negative_total"),
116
- by_verdict: byVerdict,
117
- };
118
- }
119
- catch {
120
- partial = true;
121
- }
122
- const blocked = metrics.blockedSnapshot();
123
- const tokens = {
124
- pre_prompt: metrics.get("tokens_injected_pre_prompt"),
125
- compacting: metrics.get("tokens_injected_compacting"),
126
- };
127
- let settings = {};
128
- try {
129
- const rows = store
130
- .prepare("SELECT key, value FROM kevin_settings")
131
- .all();
132
- settings = {};
133
- for (const r of rows) {
134
- settings[r.key] = r.value;
135
- }
136
- }
137
- catch {
138
- partial = true;
139
- }
140
- // v0.6.0 (K6-023 / plan §5.8) — channels + curation. Both blocks are
141
- // gated on migration 007's schema: on a pre-007 database they are
142
- // OMITTED (not zero-valued) and `partial` is set, so the release's
143
- // scoreboard is never presented as computed when it cannot be.
144
- let channels;
145
- let curation;
146
- let conflicts;
147
- let mix;
148
- let hasSchema007 = false;
149
- try {
150
- store.prepare("SELECT 1 FROM curation_proposals LIMIT 1").get();
151
- hasSchema007 = true;
152
- }
153
- catch {
154
- hasSchema007 = false;
155
- }
156
- if (hasSchema007) {
157
- try {
158
- const emission = (capable, key) => {
159
- if (!capable)
160
- return "unavailable";
161
- return settings[key] === "1" ? "on" : "off";
162
- };
163
- const registered = (key) => {
164
- const row = store
165
- .prepare("SELECT value FROM kevin_metrics WHERE key = ?")
166
- .get(key);
167
- return row?.value ?? 0;
168
- };
169
- channels = {
170
- push: {
171
- tokens_pre_prompt: metrics.get("tokens_injected_pre_prompt"),
172
- tokens_compacting: metrics.get("tokens_injected_compacting"),
173
- injections_total: metrics.get("injections_total"),
174
- precision_rate: metrics.precisionRate(),
175
- coverage_rate: metrics.coverageRate(),
176
- // The EFFECTIVE cap (K6-021 clamp semantics), not the raw
177
- // setting: the channel comparison must use the budget the
178
- // push channel actually charges against.
179
- budget_tokens: effectivePrePromptCap(settings.pre_prompt_budget_tokens),
180
- },
181
- pull: {
182
- proposals_created: metrics.get("proposals_created"),
183
- // "approved" is the human decision: approved + applied.
184
- proposals_approved: metrics.get("proposals_approved"),
185
- proposals_rejected: metrics.get("proposals_rejected"),
186
- artifact_writes_total: metrics.get("artifact_writes_total"),
187
- artifact_writes_noop: metrics.get("artifact_writes_noop"),
188
- references_registered: registered("references_registered"),
189
- skills_registered: registered("skills_registered"),
190
- skill_emission: emission(capabilities.skills, "skill_emission_enabled"),
191
- reference_emission: emission(capabilities.references, "reference_emission_enabled"),
192
- },
193
- };
194
- }
195
- catch {
196
- partial = true;
197
- channels = undefined;
198
- }
199
- try {
200
- const proposalRows = store
201
- .prepare("SELECT status, COUNT(*) AS n FROM curation_proposals GROUP BY status")
202
- .all();
203
- const byStatus = {};
204
- for (const r of proposalRows) {
205
- byStatus[r.status] = r.n;
206
- }
207
- curation = {
208
- // The Curator predicate is `inferable != 1` (plan §5.5): an
209
- // unclassified (NULL) memory must not be silently withheld.
210
- eligible: scalar(store, "SELECT COUNT(*) AS n FROM memories WHERE inferable IS NOT 1"),
211
- curated: scalar(store, "SELECT COUNT(*) AS n FROM memories WHERE curated = 1"),
212
- inferable: scalar(store, "SELECT COUNT(*) AS n FROM memories WHERE inferable = 1"),
213
- non_inferable: scalar(store, "SELECT COUNT(*) AS n FROM memories WHERE inferable = 0"),
214
- unknown: scalar(store, "SELECT COUNT(*) AS n FROM memories WHERE inferable IS NULL"),
215
- proposals_by_status: byStatus,
216
- };
217
- }
218
- catch {
219
- partial = true;
220
- curation = undefined;
221
- }
222
- }
223
- else {
224
- partial = true;
225
- }
226
- // v0.8.0 (K8-018/023 / plan §5.2, §5.7) — the shared-layer block.
227
- // `write_refusals` lives OUTSIDE the frozen METRIC_KEYS ladder (K7-004)
228
- // and is read as a bare SQL scalar, following the `skills_registered`
229
- // precedent; a missing row means no refusals yet. The K8-023 team
230
- // rollup (shared_entries, okf_imports, layer precision) is gated on
231
- // migration 009's schema and is pure SQL — no JavaScript-side
232
- // aggregation (plan §5.7 acceptance).
233
- let team = { write_refusals: 0 };
234
- try {
235
- team = {
236
- write_refusals: scalar(store, `SELECT COALESCE(
237
- (SELECT value FROM kevin_metrics WHERE key = 'shared_write_refusals'),
238
- 0
239
- ) AS n`),
240
- };
241
- }
242
- catch {
243
- partial = true;
244
- team = undefined;
245
- }
246
- let hasSchema009 = false;
247
- try {
248
- store.prepare("SELECT 1 FROM shared_entries LIMIT 1").get();
249
- hasSchema009 = true;
250
- }
251
- catch {
252
- hasSchema009 = false;
253
- }
254
- if (hasSchema009 && team !== undefined) {
255
- try {
256
- const repoWhere = (column) => repoId ? `${column} = ?` : "1 = 1";
257
- const repoParams = repoId ? [repoId] : [];
258
- const lastImport = store
259
- .prepare(`SELECT imported_at, entries_rejected
260
- FROM okf_imports
261
- WHERE ${repoWhere("repo_id")}
262
- ORDER BY imported_at DESC, id DESC LIMIT 1`)
263
- .get(...repoParams);
264
- // v0.5.0 precision formula (effective / (effective +
265
- // ineffective)) per layer, same shape as `mix`.
266
- const layerPrecision = (layer) => {
267
- const row = store
268
- .prepare(`SELECT
269
- SUM(CASE WHEN i.outcome = 'effective' THEN 1 ELSE 0 END) AS effective,
270
- SUM(CASE WHEN i.outcome = 'ineffective' THEN 1 ELSE 0 END) AS ineffective
271
- FROM kevin_injections i JOIN memories m ON m.id = i.memory_id
272
- WHERE m.layer = ? ${repoId ? "AND (m.repo_id = ? OR m.repo_id IS NULL)" : ""}`)
273
- .get(layer, ...(repoId ? [repoId] : []));
274
- const effective = row.effective ?? 0;
275
- const measured = effective + (row.ineffective ?? 0);
276
- return measured > 0 ? effective / measured : 0;
277
- };
278
- // The v0.7.0 maturity floor, verbatim: below it the
279
- // precision numbers are omitted and `immature_db` reported
280
- // (plan §5.7 step 5).
281
- const memoryCount = scalar(store, "SELECT COUNT(*) AS n FROM memories");
282
- const settled = scalar(store, "SELECT COUNT(*) AS n FROM kevin_injections WHERE outcome IN ('effective','ineffective')");
283
- const mature = memoryCount >= 100 && settled >= 50;
284
- team = {
285
- ...team,
286
- shared_total: scalar(store, `SELECT COUNT(*) AS n FROM shared_entries
287
- WHERE ${repoWhere("repo_id")} AND op = 'assert'`, ...repoParams),
288
- tombstones: scalar(store, `SELECT COUNT(*) AS n FROM shared_entries
289
- WHERE ${repoWhere("repo_id")} AND op = 'tombstone'`, ...repoParams),
290
- // Counts distinct non-null author_hash values; with
291
- // author_identity_mode='none' imports write NULL, so
292
- // the count is naturally 0.
293
- distinct_authors: scalar(store, `SELECT COUNT(DISTINCT author_hash) AS n FROM shared_entries
294
- WHERE ${repoWhere("repo_id")} AND author_hash IS NOT NULL`, ...repoParams),
295
- last_import_at: lastImport?.imported_at ?? null,
296
- last_import_rejected: lastImport?.entries_rejected ?? 0,
297
- ...(mature
298
- ? {
299
- precision_shared: layerPrecision("shared"),
300
- precision_local: layerPrecision("local"),
301
- }
302
- : { reason: "immature_db" }),
303
- };
304
- }
305
- catch {
306
- partial = true;
307
- team = undefined;
308
- }
309
- }
310
- try {
311
- const rows = store
312
- .prepare("SELECT kind, status, COUNT(*) AS n FROM memory_conflicts GROUP BY kind, status")
313
- .all();
314
- const byKind = {};
315
- const byStatus = {};
316
- for (const row of rows) {
317
- byKind[row.kind] = (byKind[row.kind] ?? 0) + row.n;
318
- byStatus[row.status] = (byStatus[row.status] ?? 0) + row.n;
319
- }
320
- conflicts = { by_kind: byKind, by_status: byStatus };
321
- }
322
- catch {
323
- partial = true;
324
- }
325
- // v0.7.0 (K7-019 / plan §5.3, K7-006) — the `truth` block is
326
- // project-scoped. On a pre-008 database the repo_facts query throws, so
327
- // the block is omitted and `partial` set — a report that cannot answer
328
- // "how many facts does this project have?" must say so.
329
- // v0.8.0 (K8-007 / plan §5.7) — the penalized-memory rollup scopes on
330
- // repo_id once the 009 column exists and an identity is resolved
331
- // (NULL-repo_id rows are global); project_id stays as the fallback.
332
- let truth;
333
- if (projectId || repoId) {
334
- try {
335
- const truncatedRow = store
336
- .prepare("SELECT value FROM repo_facts WHERE project_id = ? AND key_path = '_truncated'")
337
- .get(projectId);
338
- const penalized = repoId && hasRepoIdColumn(store)
339
- ? scalar(store, "SELECT COUNT(*) AS n FROM memories WHERE (repo_id = ? OR repo_id IS NULL) AND truth_penalty > 0", repoId)
340
- : scalar(store, "SELECT COUNT(*) AS n FROM memories WHERE project_id = ? AND truth_penalty > 0", projectId);
341
- truth = {
342
- facts_scanned: scalar(store, "SELECT COUNT(*) AS n FROM repo_facts WHERE project_id = ?", projectId),
343
- penalized_memories: penalized,
344
- truncated: truncatedRow
345
- ? { is_truncated: true, count: Number(truncatedRow.value) || 0 }
346
- : { is_truncated: false },
347
- };
348
- }
349
- catch {
350
- partial = true;
351
- truth = undefined;
352
- }
353
- }
354
- try {
355
- const typeRows = store
356
- .prepare(`SELECT m.type, COUNT(*) AS n
357
- FROM kevin_injections i JOIN memories m ON m.id = i.memory_id
358
- GROUP BY m.type`)
359
- .all();
360
- const injectedByType = {
361
- error: 0,
362
- rule: 0,
363
- decision: 0,
364
- pattern: 0,
365
- solution: 0,
366
- context: 0,
367
- };
368
- for (const row of typeRows)
369
- injectedByType[row.type] = row.n;
370
- const total = typeRows.reduce((sum, row) => sum + row.n, 0);
371
- const nonError = total - (injectedByType.error ?? 0);
372
- const precisionFor = (isError) => {
373
- const row = store
374
- .prepare(`SELECT
375
- SUM(CASE WHEN i.outcome = 'effective' THEN 1 ELSE 0 END) AS effective,
376
- SUM(CASE WHEN i.outcome = 'ineffective' THEN 1 ELSE 0 END) AS ineffective
377
- FROM kevin_injections i JOIN memories m ON m.id = i.memory_id
378
- WHERE ${isError ? "m.type = 'error'" : "m.type <> 'error'"}`)
379
- .get();
380
- const effective = row.effective ?? 0;
381
- const measured = effective + (row.ineffective ?? 0);
382
- return measured > 0 ? effective / measured : 0;
383
- };
384
- const precisionError = precisionFor(true);
385
- const precisionNonError = precisionFor(false);
386
- const memoryCount = scalar(store, "SELECT COUNT(*) AS n FROM memories");
387
- const settled = scalar(store, "SELECT COUNT(*) AS n FROM kevin_injections WHERE outcome IN ('effective','ineffective')");
388
- const mature = memoryCount >= 100 && settled >= 50;
389
- const meets = mature &&
390
- nonError / Math.max(total, 1) >= 0.5 &&
391
- precisionNonError > precisionError;
392
- mix = {
393
- injected_by_type: injectedByType,
394
- injected_total: total,
395
- non_error_injected: nonError,
396
- non_error_share: total > 0 ? nonError / total : 0,
397
- precision_error: precisionError,
398
- precision_non_error: precisionNonError,
399
- meets_exit_criterion: meets,
400
- ...(mature ? {} : { reason: "immature_db" }),
401
- };
402
- }
403
- catch {
404
- partial = true;
405
- }
406
- // v0.9.0 (K9-020 / plan §5.5) — the host block, mirroring the
407
- // channels/curation precedent: gated on migration 010's schema, pure
408
- // SQL, omitted (not zero-valued) on a pre-010 database with
409
- // `partial` set. The verdict is derived entirely in SQL: degraded
410
- // when any hook is dead, unknown when no hook has been observed at
411
- // all (never rounded to healthy), healthy otherwise.
412
- let host;
413
- let hasSchema010 = false;
414
- try {
415
- const row = store
416
- .prepare("SELECT 1 AS n FROM sqlite_master WHERE type='table' AND name='hook_liveness'")
417
- .get();
418
- hasSchema010 = !!row;
419
- }
420
- catch {
421
- hasSchema010 = false;
422
- }
423
- if (hasSchema010) {
424
- try {
425
- const hookRow = store
426
- .prepare(`SELECT
427
- COUNT(*) AS total,
428
- SUM(CASE WHEN dead_since IS NOT NULL THEN 1 ELSE 0 END) AS dead,
429
- SUM(CASE WHEN fire_count > 0 THEN 1 ELSE 0 END) AS live,
430
- SUM(fire_count) AS fires_total,
431
- SUM(error_count) AS errors_total
432
- FROM hook_liveness`)
433
- .get();
434
- const dead = hookRow.dead ?? 0;
435
- const live = hookRow.live ?? 0;
436
- const unknown = hookRow.total - dead - live;
437
- const versionRow = store
438
- .prepare(`SELECT plugin_version FROM host_probes
439
- ORDER BY probed_at DESC, id DESC LIMIT 1`)
440
- .get();
441
- const regRows = store
442
- .prepare(`SELECT surface, registered, verified, COUNT(*) AS n
443
- FROM native_registrations
444
- GROUP BY surface, registered, verified`)
445
- .all();
446
- const bySurface = {};
447
- let total = 0;
448
- let verified = 0;
449
- let failures = 0;
450
- for (const row of regRows) {
451
- total += row.n;
452
- if (row.verified === 1)
453
- verified += row.n;
454
- if (row.registered === 1 && row.verified === 0)
455
- failures += row.n;
456
- const acc = bySurface[row.surface] ?? { registered: 0, verified: 0 };
457
- acc.registered += row.registered === 1 ? row.n : 0;
458
- acc.verified += row.verified === 1 ? row.n : 0;
459
- bySurface[row.surface] = acc;
460
- }
461
- host = {
462
- plugin_version: versionRow?.plugin_version ?? null,
463
- hooks: {
464
- live,
465
- dead,
466
- unknown: unknown < 0 ? 0 : unknown,
467
- fires_total: hookRow.fires_total ?? 0,
468
- errors_total: hookRow.errors_total ?? 0,
469
- },
470
- native: { total, verified, failures, by_surface: bySurface },
471
- verdict: dead > 0
472
- ? "degraded"
473
- : hookRow.total === 0
474
- ? "unknown"
475
- : unknown > 0
476
- ? "unknown"
477
- : "healthy",
478
- };
479
- }
480
- catch {
481
- partial = true;
482
- host = undefined;
483
- }
484
- }
485
- // v0.9.0 (K9-020): pre-010 databases simply omit `host` without
486
- // marking the report partial — the host block is additive, unlike
487
- // channels/curation which are the v0.6.0 scoreboard. Existing
488
- // v0.8.0-era tests run on 007/008 stores and expect partial false.
489
- // v1.0.0 (K10-020 / plan §5.6) — perf block: the most recent aggregate
490
- // row per scope, read directly from perf_samples. A scope with no rows
491
- // reports count 0, zero timings and within_budget true — never NULL,
492
- // never NaN. Omitted on a pre-011 database (host precedent).
493
- let perf;
494
- let hasSchema011 = false;
495
- try {
496
- const row = store
497
- .prepare("SELECT 1 AS n FROM sqlite_master WHERE type='table' AND name='perf_samples'")
498
- .get();
499
- hasSchema011 = !!row;
500
- }
501
- catch {
502
- hasSchema011 = false;
503
- }
504
- if (hasSchema011) {
505
- try {
506
- const scopes = {};
507
- const latest = store.prepare(`SELECT sample_count AS count, p50_ms AS p50, p95_ms AS p95,
508
- max_ms AS max, budget_p95_ms AS budget_p95, within_budget
509
- FROM perf_samples WHERE scope = ? ORDER BY id DESC LIMIT 1`);
510
- for (const budget of BUDGETS) {
511
- const row = latest.get(budget.scope);
512
- scopes[budget.scope] = row
513
- ? {
514
- count: row.count,
515
- p50: row.p50,
516
- p95: row.p95,
517
- max: row.max,
518
- budget_p95: row.budget_p95,
519
- within_budget: row.within_budget === 1,
520
- }
521
- : {
522
- count: 0,
523
- p50: 0,
524
- p95: 0,
525
- max: 0,
526
- budget_p95: budget.p95Ms,
527
- within_budget: true,
528
- };
529
- }
530
- perf = { scopes };
531
- }
532
- catch {
533
- perf = undefined;
534
- }
535
- }
536
- // v1.0.0 (K10-020 / plan §5.6) — contract block: pure function of the
537
- // source, no database involvement.
538
- const liveContract = describeContract();
539
- const contract = {
540
- contract_version: liveContract.contractVersion,
541
- digest: contractDigest(liveContract),
542
- clause_count: liveContract.clauses.length,
543
- deprecated_count: liveContract.clauses.filter((c) => c.deprecated).length,
544
- };
545
- return {
546
- memories,
547
- injections,
548
- blocked,
549
- feedback,
550
- tokens,
551
- settings,
552
- conflicts,
553
- truth,
554
- mix,
555
- channels,
556
- curation,
557
- team,
558
- host,
559
- perf,
560
- contract,
561
- partial,
562
- };
563
- }
@@ -1,10 +0,0 @@
1
- import type { Store } from "./Store.js";
2
- export interface KevinBenchDeps {
3
- store: Store;
4
- /** Working directory used to locate bench/corpus (defaults process.cwd()). */
5
- cwd?: string;
6
- }
7
- export interface KevinBenchArgs {
8
- action: "status" | "last";
9
- }
10
- export declare function buildKevinBench(deps: KevinBenchDeps, args: KevinBenchArgs): Record<string, unknown>;
@@ -1,82 +0,0 @@
1
- /**
2
- * K10-019 — kevin_bench: reports what `npm run bench` recorded.
3
- *
4
- * It NEVER runs the benchmark — running it from inside a live session
5
- * would measure the session and pollute the user's database. The module
6
- * imports nothing from scripts/bench.ts; the corpus digest of the on-disk
7
- * corpus is recomputed here with the same fnv1a64 discipline so a result
8
- * computed from a different corpus is visibly a different measurement.
9
- */
10
- import { readFileSync, statSync } from "node:fs";
11
- import { join } from "node:path";
12
- import { fnv1a64 } from "./fingerprint.js";
13
- function onDiskCorpusDigest(cwd) {
14
- try {
15
- const dir = join(cwd, "bench", "corpus");
16
- statSync(dir);
17
- const mem = readFileSync(join(dir, "memories.jsonl"), "utf8");
18
- const q = readFileSync(join(dir, "queries.jsonl"), "utf8");
19
- return {
20
- digest: fnv1a64(`${mem}\n${q}`),
21
- queries: q.split("\n").filter((l) => l.trim().length > 0).length,
22
- };
23
- }
24
- catch {
25
- return null;
26
- }
27
- }
28
- function noRuns() {
29
- return {
30
- has_runs: false,
31
- hint: "npm run bench",
32
- };
33
- }
34
- export function buildKevinBench(deps, args) {
35
- if (args.action === "status") {
36
- const count = deps.store
37
- .prepare("SELECT COUNT(*) AS c FROM bench_runs")
38
- .get();
39
- if (!count || count.c === 0)
40
- return noRuns();
41
- const latest = deps.store
42
- .prepare("SELECT corpus_digest, contract_digest, package_version, runtime, ran_at FROM bench_runs ORDER BY id DESC LIMIT 1")
43
- .get();
44
- if (!latest)
45
- return noRuns();
46
- const disk = onDiskCorpusDigest(deps.cwd ?? process.cwd());
47
- return {
48
- has_runs: true,
49
- total_runs: count.c,
50
- corpus_digest: latest.corpus_digest,
51
- contract_digest: latest.contract_digest,
52
- package_version: latest.package_version,
53
- runtime: latest.runtime,
54
- last_ran_at: latest.ran_at,
55
- corpus_on_disk: disk === null ? null : disk.digest,
56
- matches_disk_corpus: disk !== null && disk.digest === latest.corpus_digest,
57
- };
58
- }
59
- const latest = deps.store
60
- .prepare("SELECT corpus_digest, contract_digest, package_version, runtime, ran_at FROM bench_runs ORDER BY id DESC LIMIT 1")
61
- .get();
62
- if (!latest)
63
- return noRuns();
64
- const rows = deps.store
65
- .prepare("SELECT arm, k, precision_at_k, recall_at_k, mrr FROM bench_runs WHERE corpus_digest = ? AND ran_at = ?")
66
- .all(latest.corpus_digest, latest.ran_at);
67
- return {
68
- has_runs: true,
69
- corpus_digest: latest.corpus_digest,
70
- contract_digest: latest.contract_digest,
71
- package_version: latest.package_version,
72
- runtime: latest.runtime,
73
- ran_at: latest.ran_at,
74
- arms: rows.map((r) => ({
75
- arm: r.arm,
76
- k: r.k,
77
- precision_at_k: r.precision_at_k,
78
- recall_at_k: r.recall_at_k,
79
- mrr: r.mrr,
80
- })),
81
- };
82
- }
@@ -1,9 +0,0 @@
1
- import type { ConflictDetector } from "./ConflictDetector.js";
2
- import type { Store } from "./Store.js";
3
- export interface KevinConflictsDeps {
4
- store: Store;
5
- detector: ConflictDetector;
6
- projectId: string;
7
- }
8
- export type ConflictAction = "list" | "acknowledge" | "resolve";
9
- export declare function executeKevinConflicts(deps: KevinConflictsDeps, action: ConflictAction, id?: string, keep?: string, requestedStatus?: "open" | "acknowledged" | "resolved"): Record<string, unknown>;