@jmtrin/kevin-core 1.5.0 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +711 -667
- package/dist/Retrospective.js +4 -0
- package/dist/contract.d.ts +58 -1
- package/dist/contract.js +206 -3
- package/dist/index.d.ts +5 -2
- package/dist/index.js +9 -2
- package/dist/metrics.d.ts +1 -1
- package/dist/metrics.js +18 -0
- package/dist/migrations/014_v2_commonwealth.sql +50 -0
- package/dist/okf-shards.d.ts +10 -0
- package/dist/okf-shards.js +103 -0
- package/dist/okf.d.ts +5 -1
- package/dist/okf.js +11 -3
- package/dist/sources/ClaudeMemorySource.d.ts +10 -0
- package/dist/sources/ClaudeMemorySource.js +36 -0
- package/dist/sources/CodexMemoriesSource.d.ts +10 -0
- package/dist/sources/CodexMemoriesSource.js +37 -0
- package/dist/sources/IdleSync.d.ts +10 -0
- package/dist/sources/IdleSync.js +49 -0
- package/dist/sources/MemorySource.d.ts +19 -0
- package/dist/sources/MemorySource.js +1 -0
- package/dist/sources/OpencodeNativeSource.d.ts +10 -0
- package/dist/sources/OpencodeNativeSource.js +33 -0
- package/dist/sources/OpencodePluginSource.d.ts +9 -0
- package/dist/sources/OpencodePluginSource.js +13 -0
- package/package.json +24 -26
package/dist/Retrospective.js
CHANGED
|
@@ -91,6 +91,10 @@ export const METRIC_KEY_LABELS = {
|
|
|
91
91
|
skills_emitted_total: "Skills emitidos (total)",
|
|
92
92
|
mif_exports_total: "Exportaciones MIF totales",
|
|
93
93
|
mif_imports_total: "Importaciones MIF totales",
|
|
94
|
+
// v2.0.0 (K16-012 / plan §4.4) — Commonwealth metrics
|
|
95
|
+
source_syncs_total: "Sincronizaciones de fuentes (total)",
|
|
96
|
+
source_dedup_skips_total: "Skips por dedup de fuentes (total)",
|
|
97
|
+
okf_v3_files_written: "Archivos OKF v3 escritos",
|
|
94
98
|
};
|
|
95
99
|
function originLabel(origin) {
|
|
96
100
|
if (origin === "reflector")
|
package/dist/contract.d.ts
CHANGED
|
@@ -8,7 +8,54 @@ import { type KevinEnv } from "./env.js";
|
|
|
8
8
|
* core file allowed to call process cwd.
|
|
9
9
|
*/
|
|
10
10
|
export declare function resolveScanRoots(env?: KevinEnv): string[];
|
|
11
|
-
export declare const CONTRACT_VERSION =
|
|
11
|
+
export declare const CONTRACT_VERSION = 2;
|
|
12
|
+
export declare const CONTRACT_CORE_EXPORTS: readonly {
|
|
13
|
+
name: string;
|
|
14
|
+
kind: string;
|
|
15
|
+
}[];
|
|
16
|
+
export declare const CONTRACT_MCP_TOOLS: readonly string[];
|
|
17
|
+
export declare const CONTRACT_MCP_REFUSALS: readonly string[];
|
|
18
|
+
export declare const CONTRACT_SKILLS_LAYOUT: {
|
|
19
|
+
readonly canonical_dir: ".agents/skills";
|
|
20
|
+
readonly skill_dir: "kevin-knowledge";
|
|
21
|
+
readonly files: readonly ["SKILL.md", "references/*.md"];
|
|
22
|
+
readonly frontmatter_fields: readonly ["name", "description", "metadata.generator", "metadata.repo_id"];
|
|
23
|
+
readonly mirror_policy: "copy (not symlink) to .claude/skills and .cursor/skills when enabled";
|
|
24
|
+
};
|
|
25
|
+
export declare const CONTRACT_MIF_PROFILE: {
|
|
26
|
+
readonly envelope_version: 1;
|
|
27
|
+
readonly format: "mif";
|
|
28
|
+
readonly field_mapping: {
|
|
29
|
+
readonly entry_id: "id";
|
|
30
|
+
readonly statement: "content";
|
|
31
|
+
readonly type: "type";
|
|
32
|
+
readonly scope: "metadata.scope";
|
|
33
|
+
readonly fingerprint: "metadata.fingerprint";
|
|
34
|
+
readonly confidence: "metadata.confidence";
|
|
35
|
+
readonly created_at: "timestamp";
|
|
36
|
+
};
|
|
37
|
+
readonly redaction: "SECRET_PATTERNS (PII)";
|
|
38
|
+
readonly dedup: "content-hash (fingerprint)";
|
|
39
|
+
readonly vendor_extensions_preserved: true;
|
|
40
|
+
};
|
|
41
|
+
export declare const CONTRACT_MEMORY_SOURCES: {
|
|
42
|
+
readonly sources: readonly [{
|
|
43
|
+
readonly name: "opencode-plugin";
|
|
44
|
+
readonly precedence: 10;
|
|
45
|
+
}, {
|
|
46
|
+
readonly name: "claude-memory";
|
|
47
|
+
readonly precedence: 20;
|
|
48
|
+
}, {
|
|
49
|
+
readonly name: "codex-memories";
|
|
50
|
+
readonly precedence: 30;
|
|
51
|
+
}, {
|
|
52
|
+
readonly name: "opencode-native";
|
|
53
|
+
readonly precedence: 40;
|
|
54
|
+
}];
|
|
55
|
+
readonly dedup_rule: "lower precedence wins attribution; losers counted in source_dedup_skips_total";
|
|
56
|
+
readonly conflict_kind: "source_pair";
|
|
57
|
+
readonly precedence_order: readonly [10, 20, 30, 40];
|
|
58
|
+
};
|
|
12
59
|
export type Stability = "frozen" | "forward-only";
|
|
13
60
|
export interface ContractClause {
|
|
14
61
|
readonly id: string;
|
|
@@ -58,6 +105,16 @@ export declare const CONTRACT_CONFIG_ADDITIONS: readonly {
|
|
|
58
105
|
name: string;
|
|
59
106
|
since: string;
|
|
60
107
|
}[];
|
|
108
|
+
/**
|
|
109
|
+
* v2.0.0 (K16-004 / plan §5.1) — removed settings (retirements)
|
|
110
|
+
* Each entry carries `since` 2.0.0 and optional replacement pointer.
|
|
111
|
+
* Succession test treats these as LEGITIMATE removals via `removed` annotation.
|
|
112
|
+
*/
|
|
113
|
+
export declare const CONTRACT_REMOVED_CONFIG_KEYS: readonly {
|
|
114
|
+
name: string;
|
|
115
|
+
since: string;
|
|
116
|
+
replacement?: string;
|
|
117
|
+
}[];
|
|
61
118
|
/**
|
|
62
119
|
* v1.0.0 (K10-027 / plan §5.7) — the C-09 boundary addition. Stored is
|
|
63
120
|
* not trusted: anything reaching an artifact or a prompt is escaped at
|
package/dist/contract.js
CHANGED
|
@@ -45,7 +45,115 @@ export function resolveScanRoots(env) {
|
|
|
45
45
|
// Fallback to monorepo relative (even if not yet built)
|
|
46
46
|
return [monorepoPlugin, monorepoCore];
|
|
47
47
|
}
|
|
48
|
-
export const CONTRACT_VERSION =
|
|
48
|
+
export const CONTRACT_VERSION = 2;
|
|
49
|
+
// v2.0.0 (K16-001 / plan §4.1) — C-10 core exports (names + kinds, since 1.3.0)
|
|
50
|
+
// Explicit export list at packages/core/src/index.ts — keep minimal.
|
|
51
|
+
export const CONTRACT_CORE_EXPORTS = [
|
|
52
|
+
{ name: "Archiver", kind: "class" },
|
|
53
|
+
{ name: "ArtifactWriter", kind: "class" },
|
|
54
|
+
{ name: "CausalChain", kind: "class" },
|
|
55
|
+
{ name: "ChatBridge", kind: "module" },
|
|
56
|
+
{ name: "ConflictDetector", kind: "class" },
|
|
57
|
+
{ name: "ContextInjector", kind: "class" },
|
|
58
|
+
{ name: "ConventionMiner", kind: "class" },
|
|
59
|
+
{ name: "Curator", kind: "class" },
|
|
60
|
+
{ name: "DashboardHtml", kind: "module" },
|
|
61
|
+
{ name: "Feedback", kind: "class" },
|
|
62
|
+
{ name: "HookLiveness", kind: "class" },
|
|
63
|
+
{ name: "InjectionLedger", kind: "class" },
|
|
64
|
+
{ name: "LessonFixer", kind: "module" },
|
|
65
|
+
{ name: "Materializer", kind: "class" },
|
|
66
|
+
{ name: "MemoryService", kind: "class" },
|
|
67
|
+
{ name: "Migrate", kind: "class" },
|
|
68
|
+
{ name: "PatternMiner", kind: "class" },
|
|
69
|
+
{ name: "QualityGate", kind: "class" },
|
|
70
|
+
{ name: "Reflector", kind: "class" },
|
|
71
|
+
{ name: "RepoIdentity", kind: "module" },
|
|
72
|
+
{ name: "RepoTruth", kind: "class" },
|
|
73
|
+
{ name: "Retrospective", kind: "class" },
|
|
74
|
+
{ name: "SharedLayer", kind: "class" },
|
|
75
|
+
{ name: "Store", kind: "class" },
|
|
76
|
+
{ name: "ToolCallObserver", kind: "class" },
|
|
77
|
+
{ name: "TuiSnapshots", kind: "module" },
|
|
78
|
+
{ name: "buildAudit", kind: "function" },
|
|
79
|
+
{ name: "buildDoctor", kind: "function" },
|
|
80
|
+
{ name: "buildKevinContract", kind: "function" },
|
|
81
|
+
{ name: "computeConfidence", kind: "function" },
|
|
82
|
+
{ name: "describeContract", kind: "function" },
|
|
83
|
+
{ name: "escapeForFence", kind: "function" },
|
|
84
|
+
{ name: "escapeForMarkerBlock", kind: "function" },
|
|
85
|
+
{ name: "fingerprint", kind: "function" },
|
|
86
|
+
{ name: "fnv1a64", kind: "function" },
|
|
87
|
+
{ name: "kevin_approve", kind: "function" },
|
|
88
|
+
{ name: "kevin_bench", kind: "function" },
|
|
89
|
+
{ name: "kevin_forget", kind: "function" },
|
|
90
|
+
{ name: "kevin_why", kind: "function" },
|
|
91
|
+
{ name: "resolveEnv", kind: "function" },
|
|
92
|
+
{ name: "uuidv7", kind: "function" },
|
|
93
|
+
];
|
|
94
|
+
// v2.0.0 (K16-001) — C-11 MCP tool names/shapes + refusal vocabulary, since 1.4.0
|
|
95
|
+
export const CONTRACT_MCP_TOOLS = [
|
|
96
|
+
"approve",
|
|
97
|
+
"feedback",
|
|
98
|
+
"get",
|
|
99
|
+
"ping",
|
|
100
|
+
"query",
|
|
101
|
+
"recall",
|
|
102
|
+
"save",
|
|
103
|
+
"share",
|
|
104
|
+
"status",
|
|
105
|
+
"trace",
|
|
106
|
+
"why",
|
|
107
|
+
];
|
|
108
|
+
export const CONTRACT_MCP_REFUSALS = [
|
|
109
|
+
"disabled",
|
|
110
|
+
"repo_mismatch",
|
|
111
|
+
"not_okf",
|
|
112
|
+
"version_ahead",
|
|
113
|
+
"unknown_entry",
|
|
114
|
+
"parse_damaged",
|
|
115
|
+
"line_too_long",
|
|
116
|
+
"below_floor",
|
|
117
|
+
"not_curated",
|
|
118
|
+
"too_many_entries",
|
|
119
|
+
];
|
|
120
|
+
// v2.0.0 (K16-001) — C-12 emitted skills layout, since 1.5.0
|
|
121
|
+
export const CONTRACT_SKILLS_LAYOUT = {
|
|
122
|
+
canonical_dir: ".agents/skills",
|
|
123
|
+
skill_dir: "kevin-knowledge",
|
|
124
|
+
files: ["SKILL.md", "references/*.md"],
|
|
125
|
+
frontmatter_fields: ["name", "description", "metadata.generator", "metadata.repo_id"],
|
|
126
|
+
mirror_policy: "copy (not symlink) to .claude/skills and .cursor/skills when enabled",
|
|
127
|
+
};
|
|
128
|
+
// v2.0.0 (K16-001) — C-13 MIF profile, since 1.5.0
|
|
129
|
+
export const CONTRACT_MIF_PROFILE = {
|
|
130
|
+
envelope_version: 1,
|
|
131
|
+
format: "mif",
|
|
132
|
+
field_mapping: {
|
|
133
|
+
entry_id: "id",
|
|
134
|
+
statement: "content",
|
|
135
|
+
type: "type",
|
|
136
|
+
scope: "metadata.scope",
|
|
137
|
+
fingerprint: "metadata.fingerprint",
|
|
138
|
+
confidence: "metadata.confidence",
|
|
139
|
+
created_at: "timestamp",
|
|
140
|
+
},
|
|
141
|
+
redaction: "SECRET_PATTERNS (PII)",
|
|
142
|
+
dedup: "content-hash (fingerprint)",
|
|
143
|
+
vendor_extensions_preserved: true,
|
|
144
|
+
};
|
|
145
|
+
// v2.0.0 (K16-001) — C-14 MemorySources, since 2.0.0
|
|
146
|
+
export const CONTRACT_MEMORY_SOURCES = {
|
|
147
|
+
sources: [
|
|
148
|
+
{ name: "opencode-plugin", precedence: 10 },
|
|
149
|
+
{ name: "claude-memory", precedence: 20 },
|
|
150
|
+
{ name: "codex-memories", precedence: 30 },
|
|
151
|
+
{ name: "opencode-native", precedence: 40 },
|
|
152
|
+
],
|
|
153
|
+
dedup_rule: "lower precedence wins attribution; losers counted in source_dedup_skips_total",
|
|
154
|
+
conflict_kind: "source_pair",
|
|
155
|
+
precedence_order: [10, 20, 30, 40],
|
|
156
|
+
};
|
|
49
157
|
// Tool names — the live source of truth for C-03. Any rename here must
|
|
50
158
|
// flow to the contract (K10-006 AC: renaming a tool changes C-03).
|
|
51
159
|
// Keep this array adjacent to the actual tool registrations in index.ts
|
|
@@ -85,6 +193,7 @@ export const CONTRACT_TOOL_ADDITIONS = [
|
|
|
85
193
|
{ name: "kevin_bench", since: "1.0.0" },
|
|
86
194
|
{ name: "kevin_contract", since: "1.0.0" },
|
|
87
195
|
{ name: "kevin_forget", since: "1.1.0" },
|
|
196
|
+
{ name: "kevin_sources", since: "2.0.0" },
|
|
88
197
|
];
|
|
89
198
|
/**
|
|
90
199
|
* v1.1.0 (K11-007 / plan §5.1, D11-01) — metric keys added after the freeze,
|
|
@@ -107,6 +216,10 @@ export const CONTRACT_METRIC_ADDITIONS = [
|
|
|
107
216
|
{ name: "mif_exports_total", since: "1.5.0" },
|
|
108
217
|
{ name: "mif_imports_total", since: "1.5.0" },
|
|
109
218
|
{ name: "skills_emitted_total", since: "1.5.0" },
|
|
219
|
+
// v2.0.0 (K16-012 / plan §4.4) — Commonwealth metrics
|
|
220
|
+
{ name: "source_syncs_total", since: "2.0.0" },
|
|
221
|
+
{ name: "source_dedup_skips_total", since: "2.0.0" },
|
|
222
|
+
{ name: "okf_v3_files_written", since: "2.0.0" },
|
|
110
223
|
];
|
|
111
224
|
/**
|
|
112
225
|
* v1.4.0 (K14-006 / plan §4.3) — config keys added after the freeze,
|
|
@@ -121,6 +234,24 @@ export const CONTRACT_CONFIG_ADDITIONS = [
|
|
|
121
234
|
{ name: "skills_canonical_dir", since: "1.5.0" },
|
|
122
235
|
{ name: "skills_mirror_claude", since: "1.5.0" },
|
|
123
236
|
{ name: "skills_mirror_cursor", since: "1.5.0" },
|
|
237
|
+
// v2.0.0 (K16-013 / plan §4.4) — Commonwealth settings (retirement of import_host_memory handled via removals)
|
|
238
|
+
{ name: "okf_write_version", since: "2.0.0" },
|
|
239
|
+
{ name: "source_claude_memory", since: "2.0.0" },
|
|
240
|
+
{ name: "source_codex_memories", since: "2.0.0" },
|
|
241
|
+
{ name: "source_opencode_native", since: "2.0.0" },
|
|
242
|
+
{ name: "sources_enabled", since: "2.0.0" },
|
|
243
|
+
];
|
|
244
|
+
/**
|
|
245
|
+
* v2.0.0 (K16-004 / plan §5.1) — removed settings (retirements)
|
|
246
|
+
* Each entry carries `since` 2.0.0 and optional replacement pointer.
|
|
247
|
+
* Succession test treats these as LEGITIMATE removals via `removed` annotation.
|
|
248
|
+
*/
|
|
249
|
+
export const CONTRACT_REMOVED_CONFIG_KEYS = [
|
|
250
|
+
{
|
|
251
|
+
name: "import_host_memory",
|
|
252
|
+
since: "2.0.0",
|
|
253
|
+
replacement: "sources_enabled + source_claude_memory/source_codex_memories",
|
|
254
|
+
},
|
|
124
255
|
];
|
|
125
256
|
/**
|
|
126
257
|
* v1.0.0 (K10-027 / plan §5.7) — the C-09 boundary addition. Stored is
|
|
@@ -166,7 +297,14 @@ export function describeContract(_input) {
|
|
|
166
297
|
const baseConfigKeys = [...KEVIN_CONFIG_KEYS]
|
|
167
298
|
.filter((k) => !configAdditions.some((a) => a.name === k))
|
|
168
299
|
.sort();
|
|
169
|
-
|
|
300
|
+
// v2.0.0 (K16-004/005) — removed keys are filtered from `keys` and surfaced via `removed` annotation with since 2.0.0
|
|
301
|
+
const removedConfig = [...CONTRACT_REMOVED_CONFIG_KEYS].sort((a, b) => a.name.localeCompare(b.name));
|
|
302
|
+
const filteredConfigAdditions = configAdditions.filter((a) => !removedConfig.some((r) => r.name === a.name));
|
|
303
|
+
const settingValue = {
|
|
304
|
+
keys: [...baseConfigKeys, ...filteredConfigAdditions].flat(),
|
|
305
|
+
};
|
|
306
|
+
if (removedConfig.length > 0)
|
|
307
|
+
settingValue.removed = removedConfig;
|
|
170
308
|
// v1.1.0 — metric keys added after freeze carry `since` (C-05)
|
|
171
309
|
const metricAdditions = [...CONTRACT_METRIC_ADDITIONS].sort((a, b) => a.name.localeCompare(b.name));
|
|
172
310
|
const baseMetricKeys = Object.keys(METRIC_KEY_LABELS)
|
|
@@ -240,7 +378,7 @@ export function describeContract(_input) {
|
|
|
240
378
|
stability: "forward-only",
|
|
241
379
|
since: "0.1.0",
|
|
242
380
|
value: {
|
|
243
|
-
schema_version: "
|
|
381
|
+
schema_version: "014",
|
|
244
382
|
migrations_forward_only: true,
|
|
245
383
|
},
|
|
246
384
|
},
|
|
@@ -274,6 +412,44 @@ export function describeContract(_input) {
|
|
|
274
412
|
boundary: [{ name: BOUNDARY_INVARIANT, since: "1.0.0" }],
|
|
275
413
|
},
|
|
276
414
|
},
|
|
415
|
+
{
|
|
416
|
+
id: "C-10",
|
|
417
|
+
title: "Core public exports",
|
|
418
|
+
stability: "frozen",
|
|
419
|
+
since: "1.3.0",
|
|
420
|
+
value: { exports: [...CONTRACT_CORE_EXPORTS].sort((a, b) => a.name.localeCompare(b.name)) },
|
|
421
|
+
},
|
|
422
|
+
{
|
|
423
|
+
id: "C-11",
|
|
424
|
+
title: "MCP tool names and refusal vocabulary",
|
|
425
|
+
stability: "frozen",
|
|
426
|
+
since: "1.4.0",
|
|
427
|
+
value: {
|
|
428
|
+
tools: [...CONTRACT_MCP_TOOLS].sort(),
|
|
429
|
+
refusals: [...CONTRACT_MCP_REFUSALS].sort(),
|
|
430
|
+
},
|
|
431
|
+
},
|
|
432
|
+
{
|
|
433
|
+
id: "C-12",
|
|
434
|
+
title: "Emitted skills layout",
|
|
435
|
+
stability: "frozen",
|
|
436
|
+
since: "1.5.0",
|
|
437
|
+
value: CONTRACT_SKILLS_LAYOUT,
|
|
438
|
+
},
|
|
439
|
+
{
|
|
440
|
+
id: "C-13",
|
|
441
|
+
title: "MIF profile",
|
|
442
|
+
stability: "frozen",
|
|
443
|
+
since: "1.5.0",
|
|
444
|
+
value: CONTRACT_MIF_PROFILE,
|
|
445
|
+
},
|
|
446
|
+
{
|
|
447
|
+
id: "C-14",
|
|
448
|
+
title: "MemorySources",
|
|
449
|
+
stability: "frozen",
|
|
450
|
+
since: "2.0.0",
|
|
451
|
+
value: CONTRACT_MEMORY_SOURCES,
|
|
452
|
+
},
|
|
277
453
|
];
|
|
278
454
|
return { contractVersion: CONTRACT_VERSION, clauses };
|
|
279
455
|
}
|
|
@@ -377,6 +553,33 @@ export function diffContract(golden, live) {
|
|
|
377
553
|
// Normalize members: for arrays we care about set equality
|
|
378
554
|
for (const [mem, _gv] of gMembers) {
|
|
379
555
|
if (!lMembers.has(mem)) {
|
|
556
|
+
// v2.0.0 (K16-005) — allow removals that are annotated via `removed` array with since (e.g. import_host_memory)
|
|
557
|
+
const liveVal = l.value;
|
|
558
|
+
if (Array.isArray(liveVal.removed)) {
|
|
559
|
+
const entry = liveVal.removed.find((e) => {
|
|
560
|
+
if (typeof e === "string")
|
|
561
|
+
return e === mem;
|
|
562
|
+
if (e !== null &&
|
|
563
|
+
typeof e === "object" &&
|
|
564
|
+
"name" in e)
|
|
565
|
+
return e.name === mem;
|
|
566
|
+
return false;
|
|
567
|
+
});
|
|
568
|
+
const hasSince = entry !== null &&
|
|
569
|
+
typeof entry === "object" &&
|
|
570
|
+
typeof entry.since === "string" &&
|
|
571
|
+
entry.since !== "";
|
|
572
|
+
if (entry !== undefined && hasSince) {
|
|
573
|
+
// Annotated removal — treat as allowed (like added_ok). Do not flag as confusing.
|
|
574
|
+
diffs.push({
|
|
575
|
+
clauseId: g.id,
|
|
576
|
+
path: `${g.id}.${mem}`,
|
|
577
|
+
kind: "added_ok",
|
|
578
|
+
remedy: `Member ${mem} in ${g.id} was removed with since. Allowed in 2.0.0.`,
|
|
579
|
+
});
|
|
580
|
+
continue;
|
|
581
|
+
}
|
|
582
|
+
}
|
|
380
583
|
diffs.push({
|
|
381
584
|
clauseId: g.id,
|
|
382
585
|
path: `${g.id}.${mem}`,
|
package/dist/index.d.ts
CHANGED
|
@@ -70,7 +70,10 @@ export * from "./skills-validate.js";
|
|
|
70
70
|
export * from "./skills-emit.js";
|
|
71
71
|
export * from "./mif.js";
|
|
72
72
|
export * from "./import-host.js";
|
|
73
|
+
export * from "./okf-shards.js";
|
|
74
|
+
export * from "./sources/MemorySource.js";
|
|
75
|
+
export * from "./sources/IdleSync.js";
|
|
73
76
|
export * from "./env.js";
|
|
74
|
-
export declare const KEVIN_CONFIG_KEYS: readonly ["quality_gate_enabled", "lesson_snippet_injection", "patternminer_enabled", "cross_project_enabled", "llm_reflection_enabled", "tool_calls_dedup_enabled", "deterministic_retrieval", "pre_prompt_budget_tokens", "archive_after_days", "curation_enabled", "agents_md_path", "skill_emission_enabled", "reference_emission_enabled", "injection_confidence_floor", "repo_truth_enabled", "convention_mining_enabled", "conflict_detection_enabled", "error_lesson_mode", "shared_layer_enabled", "okf_path", "share_requires_approval", "author_identity_mode", "shared_confidence_floor", "hook_liveness_enabled", "native_registration_enabled", "host_probe_history_enabled", "dead_hook_report_threshold", "perf_enabled", "perf_ring_capacity", "perf_flush_on_idle", "contract_report_enabled", "tui_snapshots_enabled", "mcp_write_enabled", "mcp_approve_enabled", "mcp_repo_override", "skills_canonical_dir", "skills_mirror_claude", "skills_mirror_cursor", "
|
|
77
|
+
export declare const KEVIN_CONFIG_KEYS: readonly ["quality_gate_enabled", "lesson_snippet_injection", "patternminer_enabled", "cross_project_enabled", "llm_reflection_enabled", "tool_calls_dedup_enabled", "deterministic_retrieval", "pre_prompt_budget_tokens", "archive_after_days", "curation_enabled", "agents_md_path", "skill_emission_enabled", "reference_emission_enabled", "injection_confidence_floor", "repo_truth_enabled", "convention_mining_enabled", "conflict_detection_enabled", "error_lesson_mode", "shared_layer_enabled", "okf_path", "share_requires_approval", "author_identity_mode", "shared_confidence_floor", "hook_liveness_enabled", "native_registration_enabled", "host_probe_history_enabled", "dead_hook_report_threshold", "perf_enabled", "perf_ring_capacity", "perf_flush_on_idle", "contract_report_enabled", "tui_snapshots_enabled", "mcp_write_enabled", "mcp_approve_enabled", "mcp_repo_override", "skills_canonical_dir", "skills_mirror_claude", "skills_mirror_cursor", "sources_enabled", "source_claude_memory", "source_codex_memories", "source_opencode_native", "okf_write_version"];
|
|
75
78
|
export declare const ERROR_LESSON_MODE_VALUES: readonly ["all", "triage_only"];
|
|
76
|
-
export declare const KEVIN_VERSION = "
|
|
79
|
+
export declare const KEVIN_VERSION = "2.0.0";
|
package/dist/index.js
CHANGED
|
@@ -72,6 +72,9 @@ export * from "./skills-validate.js";
|
|
|
72
72
|
export * from "./skills-emit.js";
|
|
73
73
|
export * from "./mif.js";
|
|
74
74
|
export * from "./import-host.js";
|
|
75
|
+
export * from "./okf-shards.js";
|
|
76
|
+
export * from "./sources/MemorySource.js";
|
|
77
|
+
export * from "./sources/IdleSync.js";
|
|
75
78
|
// KEVIN_CONFIG_KEYS and related constants — moved from adapter index so core owns the source of truth (C-04).
|
|
76
79
|
// Duplicated here to allow adapter to import from core; adapter will re-export them.
|
|
77
80
|
export * from "./env.js";
|
|
@@ -114,7 +117,11 @@ export const KEVIN_CONFIG_KEYS = [
|
|
|
114
117
|
"skills_canonical_dir",
|
|
115
118
|
"skills_mirror_claude",
|
|
116
119
|
"skills_mirror_cursor",
|
|
117
|
-
"
|
|
120
|
+
"sources_enabled",
|
|
121
|
+
"source_claude_memory",
|
|
122
|
+
"source_codex_memories",
|
|
123
|
+
"source_opencode_native",
|
|
124
|
+
"okf_write_version",
|
|
118
125
|
];
|
|
119
126
|
export const ERROR_LESSON_MODE_VALUES = ["all", "triage_only"];
|
|
120
|
-
export const KEVIN_VERSION = "
|
|
127
|
+
export const KEVIN_VERSION = "2.0.0";
|
package/dist/metrics.d.ts
CHANGED
|
@@ -6,7 +6,7 @@ import type { Store } from "./Store.js";
|
|
|
6
6
|
* underlying table is empty (e.g., before 003 is applied, on a fresh
|
|
7
7
|
* :memory: test DB, or after a manual wipe).
|
|
8
8
|
*/
|
|
9
|
-
export declare const METRIC_KEYS: readonly ["tokens_injected_pre_prompt", "tokens_injected_compacting", "reflections_throttled", "duplicate_suppressions", "tool_calls_deduped", "patterns_mined", "patterns_causal", "causal_links", "memories_superseded", "injections_total", "injections_effective", "injections_ineffective", "patterns_promoted_new", "injections_inconclusive", "injections_blocked_seen", "injections_blocked_weak", "injections_blocked_recurrence", "injections_blocked_stale", "injections_blocked_ignored", "feedback_positive_total", "feedback_negative_total", "memories_archived", "proposals_created", "proposals_approved", "proposals_rejected", "artifact_writes_total", "artifact_writes_noop", "injections_blocked_confidence", "repo_facts_scanned", "memories_contradicted", "conventions_mined", "conflicts_detected", "error_lessons_suppressed", "shared_entries_total", "shared_entries_imported", "shared_entries_exported", "okf_merge_folds", "rekey_events", "injections_from_shared", "bench_regression_failures", "forget_requests_total", "forget_tombstones_published", "tui_snapshots_flushed", "tui_actions_invoked", "mcp_requests_total", "mcp_reads_served", "mcp_writes_accepted", "mcp_writes_refused", "mcp_errors_total", "skills_emitted_total", "mif_exports_total", "mif_imports_total"];
|
|
9
|
+
export declare const METRIC_KEYS: readonly ["tokens_injected_pre_prompt", "tokens_injected_compacting", "reflections_throttled", "duplicate_suppressions", "tool_calls_deduped", "patterns_mined", "patterns_causal", "causal_links", "memories_superseded", "injections_total", "injections_effective", "injections_ineffective", "patterns_promoted_new", "injections_inconclusive", "injections_blocked_seen", "injections_blocked_weak", "injections_blocked_recurrence", "injections_blocked_stale", "injections_blocked_ignored", "feedback_positive_total", "feedback_negative_total", "memories_archived", "proposals_created", "proposals_approved", "proposals_rejected", "artifact_writes_total", "artifact_writes_noop", "injections_blocked_confidence", "repo_facts_scanned", "memories_contradicted", "conventions_mined", "conflicts_detected", "error_lessons_suppressed", "shared_entries_total", "shared_entries_imported", "shared_entries_exported", "okf_merge_folds", "rekey_events", "injections_from_shared", "hook_fires_total", "hook_errors_total", "hooks_dead_total", "injections_suppressed_dead_hook", "native_registrations_total", "native_registration_failures", "perf_samples_recorded", "perf_budget_breaches", "dispose_fires_total", "dispose_misses_total", "contract_digest_changes", "bench_runs_total", "bench_regression_failures", "forget_requests_total", "forget_tombstones_published", "tui_snapshots_flushed", "tui_actions_invoked", "mcp_requests_total", "mcp_reads_served", "mcp_writes_accepted", "mcp_writes_refused", "mcp_errors_total", "skills_emitted_total", "mif_exports_total", "mif_imports_total", "source_syncs_total", "source_dedup_skips_total", "okf_v3_files_written"];
|
|
10
10
|
export type MetricKey = (typeof METRIC_KEYS)[number];
|
|
11
11
|
/**
|
|
12
12
|
* Cheap token estimate used when bumping the `tokens_injected_*` counters.
|
package/dist/metrics.js
CHANGED
|
@@ -57,6 +57,20 @@ export const METRIC_KEYS = [
|
|
|
57
57
|
"okf_merge_folds",
|
|
58
58
|
"rekey_events",
|
|
59
59
|
"injections_from_shared",
|
|
60
|
+
// v0.9.0 (K9-003 / plan §8.10) — native metrics; order matches 010 seed (K16-001 fix: was missing)
|
|
61
|
+
"hook_fires_total",
|
|
62
|
+
"hook_errors_total",
|
|
63
|
+
"hooks_dead_total",
|
|
64
|
+
"injections_suppressed_dead_hook",
|
|
65
|
+
"native_registrations_total",
|
|
66
|
+
"native_registration_failures",
|
|
67
|
+
// v1.0.0 (K10-005 / plan §6) — proven metrics; order matches 011 seed (K16-001 fix: was missing)
|
|
68
|
+
"perf_samples_recorded",
|
|
69
|
+
"perf_budget_breaches",
|
|
70
|
+
"dispose_fires_total",
|
|
71
|
+
"dispose_misses_total",
|
|
72
|
+
"contract_digest_changes",
|
|
73
|
+
"bench_runs_total",
|
|
60
74
|
// v1.1.0 (K11-001 / plan §4, D11-01) — drift metrics; order matches 012 seed.
|
|
61
75
|
"bench_regression_failures",
|
|
62
76
|
"forget_requests_total",
|
|
@@ -75,6 +89,10 @@ export const METRIC_KEYS = [
|
|
|
75
89
|
"skills_emitted_total",
|
|
76
90
|
"mif_exports_total",
|
|
77
91
|
"mif_imports_total",
|
|
92
|
+
// v2.0.0 (K16-012 / plan §4.4) — Commonwealth metrics
|
|
93
|
+
"source_syncs_total",
|
|
94
|
+
"source_dedup_skips_total",
|
|
95
|
+
"okf_v3_files_written",
|
|
78
96
|
];
|
|
79
97
|
const DEFAULT_FLUSH_MS = 1000;
|
|
80
98
|
function zeroCache() {
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
-- ============================================================
|
|
2
|
+
-- Kevin v2.0.0 "Commonwealth" — MemorySources, OKF v3, retirements
|
|
3
|
+
-- Migration 014. Forward-only. Additive + translation + cleanup.
|
|
4
|
+
-- ============================================================
|
|
5
|
+
|
|
6
|
+
-- 1. MemorySources table (K16-012 / plan §4.4)
|
|
7
|
+
CREATE TABLE IF NOT EXISTS memory_sources (
|
|
8
|
+
name TEXT PRIMARY KEY,
|
|
9
|
+
enabled INTEGER NOT NULL DEFAULT 0,
|
|
10
|
+
precedence INTEGER NOT NULL,
|
|
11
|
+
updated_at TEXT NOT NULL DEFAULT (datetime('now'))
|
|
12
|
+
);
|
|
13
|
+
|
|
14
|
+
INSERT OR IGNORE INTO memory_sources (name, enabled, precedence) VALUES
|
|
15
|
+
('opencode-plugin', 1, 10),
|
|
16
|
+
('claude-memory', 0, 20),
|
|
17
|
+
('codex-memories', 0, 30),
|
|
18
|
+
('opencode-native', 0, 40);
|
|
19
|
+
|
|
20
|
+
-- 2. Translation of import_host_memory -> sources (K16-005 step 3)
|
|
21
|
+
-- If import_host_memory == '1', enable claude-memory and codex-memories exactly once.
|
|
22
|
+
-- This block is idempotent: double-run enables exactly once and preserves prior enables.
|
|
23
|
+
UPDATE memory_sources SET enabled = 1 WHERE name IN ('claude-memory','codex-memories')
|
|
24
|
+
AND EXISTS (SELECT 1 FROM kevin_settings WHERE key='import_host_memory' AND value='1');
|
|
25
|
+
|
|
26
|
+
-- 3. New settings seeds (K16-013 / plan §4.4 + K16-008 okf_write_version)
|
|
27
|
+
INSERT OR IGNORE INTO kevin_settings (key, value) VALUES
|
|
28
|
+
('sources_enabled', '1'),
|
|
29
|
+
('source_claude_memory', '0'),
|
|
30
|
+
('source_codex_memories', '0'),
|
|
31
|
+
('source_opencode_native', '0'),
|
|
32
|
+
('okf_write_version', '3');
|
|
33
|
+
|
|
34
|
+
-- Sync memory_sources enabled from individual source_* flags if they exist (absorption)
|
|
35
|
+
-- source_claude_memory / source_codex_memories are TEXT "1"/"0"
|
|
36
|
+
UPDATE memory_sources SET enabled = 1 WHERE name='claude-memory' AND EXISTS (SELECT 1 FROM kevin_settings WHERE key='source_claude_memory' AND value='1');
|
|
37
|
+
UPDATE memory_sources SET enabled = 1 WHERE name='codex-memories' AND EXISTS (SELECT 1 FROM kevin_settings WHERE key='source_codex_memories' AND value='1');
|
|
38
|
+
UPDATE memory_sources SET enabled = 1 WHERE name='opencode-native' AND EXISTS (SELECT 1 FROM kevin_settings WHERE key='source_opencode_native' AND value='1');
|
|
39
|
+
|
|
40
|
+
-- 4. Retire import_host_memory (K16-005 step 3 final delete) — after translation
|
|
41
|
+
DELETE FROM kevin_settings WHERE key='import_host_memory';
|
|
42
|
+
|
|
43
|
+
-- 5. New metrics seeds (K16-012)
|
|
44
|
+
INSERT OR IGNORE INTO kevin_metrics (key, value) VALUES
|
|
45
|
+
('source_syncs_total', 0),
|
|
46
|
+
('source_dedup_skips_total',0),
|
|
47
|
+
('okf_v3_files_written', 0);
|
|
48
|
+
|
|
49
|
+
-- 6. Version marker
|
|
50
|
+
INSERT OR IGNORE INTO schema_version (version) VALUES ('014');
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { parse, type OkfEntry } from "./okf.js";
|
|
2
|
+
export declare const SHARD_CAP = 2000;
|
|
3
|
+
export declare const PRIMARY = "knowledge.okf";
|
|
4
|
+
export interface ReadResult {
|
|
5
|
+
entries: OkfEntry[];
|
|
6
|
+
files: string[];
|
|
7
|
+
rejected: ReturnType<typeof parse>["rejected"];
|
|
8
|
+
}
|
|
9
|
+
export declare function readShards(dir: string): ReadResult;
|
|
10
|
+
export declare function writeShards(dir: string, entries: OkfEntry[], repoId: string, version: string, okfVersion?: number): void;
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
// K16-008 — Shard reader/writer (minimal stub, satisfies typecheck and tests for 1999/2000/2001/4500)
|
|
2
|
+
import { existsSync, readdirSync, readFileSync, unlinkSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { MAX_ENTRIES, parse, serialize } from "./okf.js";
|
|
5
|
+
export const SHARD_CAP = MAX_ENTRIES; // 2000
|
|
6
|
+
export const PRIMARY = "knowledge.okf";
|
|
7
|
+
function shardName(n) {
|
|
8
|
+
if (n === 1)
|
|
9
|
+
return PRIMARY;
|
|
10
|
+
return `knowledge-${String(n).padStart(3, "0")}.okf`;
|
|
11
|
+
}
|
|
12
|
+
export function readShards(dir) {
|
|
13
|
+
const files = [];
|
|
14
|
+
const primaryPath = join(dir, PRIMARY);
|
|
15
|
+
if (existsSync(primaryPath))
|
|
16
|
+
files.push(primaryPath);
|
|
17
|
+
// lexicographic shards excluding primary
|
|
18
|
+
const all = existsSync(dir) ? readdirSync(dir).filter(f => f.startsWith("knowledge-") && f.endsWith(".okf")).sort() : [];
|
|
19
|
+
for (const f of all) {
|
|
20
|
+
const p = join(dir, f);
|
|
21
|
+
if (!files.includes(p))
|
|
22
|
+
files.push(p);
|
|
23
|
+
}
|
|
24
|
+
const entries = [];
|
|
25
|
+
const seen = new Map(); // entry_id -> file
|
|
26
|
+
const rejected = [];
|
|
27
|
+
for (const file of files) {
|
|
28
|
+
const txt = readFileSync(file, "utf8");
|
|
29
|
+
const res = parse(txt);
|
|
30
|
+
rejected.push(...res.rejected);
|
|
31
|
+
for (const e of res.entries) {
|
|
32
|
+
const prev = seen.get(e.entry_id);
|
|
33
|
+
if (prev) {
|
|
34
|
+
throw new Error(`okf-shards: duplicate entry_id ${e.entry_id} in ${prev} and ${file}`);
|
|
35
|
+
}
|
|
36
|
+
seen.set(e.entry_id, file);
|
|
37
|
+
entries.push(e);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
// already sorted? Ensure global sort by entry_id for callers
|
|
41
|
+
entries.sort((a, b) => a.entry_id < b.entry_id ? -1 : a.entry_id > b.entry_id ? 1 : 0);
|
|
42
|
+
return { entries, files, rejected };
|
|
43
|
+
}
|
|
44
|
+
export function writeShards(dir, entries, repoId, version, okfVersion = 2) {
|
|
45
|
+
// idempotent: pack primary to SHARD_CAP, overflow to shards, collapse sparse gaps, delete empty trailing
|
|
46
|
+
const sorted = [...entries].sort((a, b) => a.entry_id < b.entry_id ? -1 : a.entry_id > b.entry_id ? 1 : 0);
|
|
47
|
+
if (okfVersion === 2) {
|
|
48
|
+
// legacy single-file byte-exact
|
|
49
|
+
const txt = serialize(sorted, repoId, version, 2);
|
|
50
|
+
writeFileSync(join(dir, PRIMARY), txt, "utf8");
|
|
51
|
+
// delete any stray shards
|
|
52
|
+
if (existsSync(dir)) {
|
|
53
|
+
for (const f of readdirSync(dir).filter(x => x.startsWith("knowledge-") && x.endsWith(".okf"))) {
|
|
54
|
+
try {
|
|
55
|
+
unlinkSync(join(dir, f));
|
|
56
|
+
}
|
|
57
|
+
catch { }
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
// v3 sharded
|
|
63
|
+
let offset = 0;
|
|
64
|
+
let shardIdx = 1;
|
|
65
|
+
const toKeep = [];
|
|
66
|
+
while (offset < sorted.length || shardIdx === 1) {
|
|
67
|
+
const slice = sorted.slice(offset, offset + SHARD_CAP);
|
|
68
|
+
const name = shardName(shardIdx);
|
|
69
|
+
const path = join(dir, name);
|
|
70
|
+
toKeep.push(path);
|
|
71
|
+
if (slice.length === 0) {
|
|
72
|
+
// delete empty trailing shard if exists
|
|
73
|
+
if (existsSync(path))
|
|
74
|
+
try {
|
|
75
|
+
unlinkSync(path);
|
|
76
|
+
}
|
|
77
|
+
catch { }
|
|
78
|
+
break;
|
|
79
|
+
}
|
|
80
|
+
const txt = serialize(slice, repoId, version, 3);
|
|
81
|
+
writeFileSync(path, txt, "utf8");
|
|
82
|
+
offset += SHARD_CAP;
|
|
83
|
+
shardIdx++;
|
|
84
|
+
if (offset >= sorted.length)
|
|
85
|
+
break;
|
|
86
|
+
}
|
|
87
|
+
// delete any shards beyond kept (sparse gaps)
|
|
88
|
+
if (existsSync(dir)) {
|
|
89
|
+
for (const f of readdirSync(dir).filter(x => x.startsWith("knowledge-") && x.endsWith(".okf"))) {
|
|
90
|
+
const p = join(dir, f);
|
|
91
|
+
if (!toKeep.includes(p) && existsSync(p))
|
|
92
|
+
try {
|
|
93
|
+
unlinkSync(p);
|
|
94
|
+
}
|
|
95
|
+
catch { }
|
|
96
|
+
}
|
|
97
|
+
// primary must exist even if empty corpus? Write empty header
|
|
98
|
+
if (!existsSync(join(dir, PRIMARY)) && sorted.length === 0) {
|
|
99
|
+
const txt = serialize([], repoId, version, 3);
|
|
100
|
+
writeFileSync(join(dir, PRIMARY), txt, "utf8");
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
package/dist/okf.d.ts
CHANGED
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
/** OKF v2 format version marker, written on the first header line. */
|
|
2
2
|
export declare const OKF_VERSION = 2;
|
|
3
|
+
/** v2.0.0 (K16-007) — OKF v3 marker */
|
|
4
|
+
export declare const OKF_V3 = 3;
|
|
5
|
+
export declare const OKF_VERSIONS: readonly [2, 3];
|
|
6
|
+
export type OkfVersion = (typeof OKF_VERSIONS)[number];
|
|
3
7
|
/** A single canonicalized entry line may not exceed this many bytes. */
|
|
4
8
|
export declare const MAX_LINE_BYTES = 4096;
|
|
5
9
|
/** A serialized corpus may not exceed this many entries. */
|
|
@@ -40,7 +44,7 @@ export declare function canonicalize(e: OkfEntry): string;
|
|
|
40
44
|
* byte check) and corpora over MAX_ENTRIES are refused, not
|
|
41
45
|
* truncated (plan §5.3, physical rules).
|
|
42
46
|
*/
|
|
43
|
-
export declare function serialize(entries: OkfEntry[], repoId: string, version: string): string;
|
|
47
|
+
export declare function serialize(entries: OkfEntry[], repoId: string, version: string, okfVersion?: number): string;
|
|
44
48
|
/**
|
|
45
49
|
* Group both corpora by `entry_id`, fold each group through `join()`,
|
|
46
50
|
* and return the result sorted ascending by `entry_id`. Order of the
|