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