@jmtrin/kevin-core 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 (145) hide show
  1. package/dist/Archiver.d.ts +42 -0
  2. package/dist/Archiver.js +83 -0
  3. package/dist/ArtifactWriter.d.ts +93 -0
  4. package/dist/ArtifactWriter.js +291 -0
  5. package/dist/CausalChain.d.ts +22 -0
  6. package/dist/CausalChain.js +197 -0
  7. package/dist/ChatBridge.d.ts +41 -0
  8. package/dist/ChatBridge.js +103 -0
  9. package/dist/ConflictDetector.d.ts +35 -0
  10. package/dist/ConflictDetector.js +260 -0
  11. package/dist/ContextInjector.d.ts +222 -0
  12. package/dist/ContextInjector.js +533 -0
  13. package/dist/ConventionMiner.d.ts +35 -0
  14. package/dist/ConventionMiner.js +242 -0
  15. package/dist/Curator.d.ts +118 -0
  16. package/dist/Curator.js +326 -0
  17. package/dist/DashboardHtml.d.ts +5 -0
  18. package/dist/DashboardHtml.js +180 -0
  19. package/dist/Feedback.d.ts +67 -0
  20. package/dist/Feedback.js +120 -0
  21. package/dist/HookLiveness.d.ts +88 -0
  22. package/dist/HookLiveness.js +322 -0
  23. package/dist/InjectionLedger.d.ts +98 -0
  24. package/dist/InjectionLedger.js +295 -0
  25. package/dist/LessonFixer.d.ts +44 -0
  26. package/dist/LessonFixer.js +45 -0
  27. package/dist/Materializer.d.ts +84 -0
  28. package/dist/Materializer.js +181 -0
  29. package/dist/MemoryService.d.ts +360 -0
  30. package/dist/MemoryService.js +1242 -0
  31. package/dist/Migrate.d.ts +24 -0
  32. package/dist/Migrate.js +285 -0
  33. package/dist/PatternMiner.d.ts +49 -0
  34. package/dist/PatternMiner.js +132 -0
  35. package/dist/QualityGate.d.ts +124 -0
  36. package/dist/QualityGate.js +119 -0
  37. package/dist/Reflector.d.ts +91 -0
  38. package/dist/Reflector.js +307 -0
  39. package/dist/RepoIdentity.d.ts +124 -0
  40. package/dist/RepoIdentity.js +300 -0
  41. package/dist/RepoTruth.d.ts +80 -0
  42. package/dist/RepoTruth.js +599 -0
  43. package/dist/Retrospective.d.ts +18 -0
  44. package/dist/Retrospective.js +216 -0
  45. package/dist/SharedLayer.d.ts +159 -0
  46. package/dist/SharedLayer.js +462 -0
  47. package/dist/Store.d.ts +29 -0
  48. package/dist/Store.js +50 -0
  49. package/dist/ToolCallObserver.d.ts +32 -0
  50. package/dist/ToolCallObserver.js +201 -0
  51. package/dist/TuiActions.d.ts +43 -0
  52. package/dist/TuiActions.js +181 -0
  53. package/dist/TuiSnapshots.d.ts +24 -0
  54. package/dist/TuiSnapshots.js +159 -0
  55. package/dist/capabilities.d.ts +6 -0
  56. package/dist/capabilities.js +1 -0
  57. package/dist/columns.d.ts +11 -0
  58. package/dist/columns.js +54 -0
  59. package/dist/confidence.d.ts +8 -0
  60. package/dist/confidence.js +34 -0
  61. package/dist/contract.d.ts +68 -0
  62. package/dist/contract.js +430 -0
  63. package/dist/diff.d.ts +8 -0
  64. package/dist/diff.js +182 -0
  65. package/dist/env.d.ts +5 -0
  66. package/dist/env.js +12 -0
  67. package/dist/escape.d.ts +39 -0
  68. package/dist/escape.js +60 -0
  69. package/dist/fingerprint.d.ts +27 -0
  70. package/dist/fingerprint.js +73 -0
  71. package/dist/host.d.ts +18 -0
  72. package/dist/host.js +7 -0
  73. package/dist/idle-pipeline.d.ts +9 -0
  74. package/dist/idle-pipeline.js +44 -0
  75. package/dist/index.d.ts +72 -0
  76. package/dist/index.js +109 -0
  77. package/dist/inferability.d.ts +32 -0
  78. package/dist/inferability.js +88 -0
  79. package/dist/kevin_approve.d.ts +34 -0
  80. package/dist/kevin_approve.js +52 -0
  81. package/dist/kevin_audit.d.ts +230 -0
  82. package/dist/kevin_audit.js +631 -0
  83. package/dist/kevin_bench.d.ts +11 -0
  84. package/dist/kevin_bench.js +83 -0
  85. package/dist/kevin_conflicts.d.ts +9 -0
  86. package/dist/kevin_conflicts.js +50 -0
  87. package/dist/kevin_contract.d.ts +8 -0
  88. package/dist/kevin_contract.js +59 -0
  89. package/dist/kevin_doctor.d.ts +63 -0
  90. package/dist/kevin_doctor.js +197 -0
  91. package/dist/kevin_facts.d.ts +42 -0
  92. package/dist/kevin_facts.js +36 -0
  93. package/dist/kevin_forget.d.ts +33 -0
  94. package/dist/kevin_forget.js +260 -0
  95. package/dist/kevin_native.d.ts +29 -0
  96. package/dist/kevin_native.js +79 -0
  97. package/dist/kevin_propose.d.ts +23 -0
  98. package/dist/kevin_propose.js +14 -0
  99. package/dist/kevin_publish.d.ts +38 -0
  100. package/dist/kevin_publish.js +18 -0
  101. package/dist/kevin_why.d.ts +23 -0
  102. package/dist/kevin_why.js +111 -0
  103. package/dist/memory-format.d.ts +20 -0
  104. package/dist/memory-format.js +59 -0
  105. package/dist/metrics.d.ts +97 -0
  106. package/dist/metrics.js +288 -0
  107. package/dist/migrations/001_initial.sql +92 -0
  108. package/dist/migrations/002_indexes.sql +14 -0
  109. package/dist/migrations/003_v02_signal.sql +58 -0
  110. package/dist/migrations/004_v03_knowledge.sql +138 -0
  111. package/dist/migrations/005_v04_signal.sql +57 -0
  112. package/dist/migrations/006_v05_glassbox.sql +118 -0
  113. package/dist/migrations/007_v06_pull.sql +145 -0
  114. package/dist/migrations/008_v07_truth.sql +124 -0
  115. package/dist/migrations/009_v08_team.sql +100 -0
  116. package/dist/migrations/010_v09_native.sql +78 -0
  117. package/dist/migrations/011_v10_proven.sql +69 -0
  118. package/dist/migrations/012_v11_drift.sql +24 -0
  119. package/dist/native.d.ts +23 -0
  120. package/dist/native.js +1 -0
  121. package/dist/okf-export.d.ts +3 -0
  122. package/dist/okf-export.js +134 -0
  123. package/dist/okf-import.d.ts +76 -0
  124. package/dist/okf-import.js +271 -0
  125. package/dist/okf.d.ts +107 -0
  126. package/dist/okf.js +303 -0
  127. package/dist/perf.d.ts +35 -0
  128. package/dist/perf.js +174 -0
  129. package/dist/query-tokenizer.d.ts +13 -0
  130. package/dist/query-tokenizer.js +133 -0
  131. package/dist/redact.d.ts +2 -0
  132. package/dist/redact.js +18 -0
  133. package/dist/replay-types.d.ts +69 -0
  134. package/dist/replay-types.js +156 -0
  135. package/dist/replay.d.ts +44 -0
  136. package/dist/replay.js +215 -0
  137. package/dist/sqlite-adapter.d.ts +12 -0
  138. package/dist/sqlite-adapter.js +78 -0
  139. package/dist/time-ms.d.ts +1 -0
  140. package/dist/time-ms.js +16 -0
  141. package/dist/tui-types.d.ts +59 -0
  142. package/dist/tui-types.js +4 -0
  143. package/dist/uuid.d.ts +1 -0
  144. package/dist/uuid.js +50 -0
  145. package/package.json +28 -0
@@ -0,0 +1,100 @@
1
+ -- ============================================================
2
+ -- Kevin 0.8.0 - Migration 009: Team (additive)
3
+ -- ============================================================
4
+ -- Backward-compatible, additive only. All new columns are
5
+ -- nullable or carry a NOT NULL DEFAULT so legacy rows keep
6
+ -- working without a destructive rebuild.
7
+ --
8
+ -- Scope note: this migration introduces `repo_id`, a SECOND
9
+ -- scoping dimension. `project_id` is retained on every table,
10
+ -- unchanged, as local-path provenance (D8-02). Nothing that
11
+ -- reads `project_id` today stops working.
12
+ -- ============================================================
13
+
14
+ -- 1. shared_entries: the local projection of the committed OKF file.
15
+ -- One row per (repo_id, entry_id). Rewritten by SharedLayer.import(),
16
+ -- never edited by hand, never the source of truth - the file is.
17
+ -- No REFERENCES to memories: an entry may arrive from a teammate
18
+ -- before any local memory corresponds to it (D8-12).
19
+ CREATE TABLE IF NOT EXISTS shared_entries (
20
+ id TEXT PRIMARY KEY,
21
+ repo_id TEXT NOT NULL,
22
+ entry_id TEXT NOT NULL,
23
+ type TEXT NOT NULL CHECK (type IN ('decision', 'rule', 'pattern', 'solution')),
24
+ statement TEXT NOT NULL,
25
+ scope TEXT,
26
+ confidence REAL NOT NULL DEFAULT 0.0,
27
+ evidence INTEGER NOT NULL DEFAULT 0,
28
+ origin TEXT NOT NULL DEFAULT 'shared',
29
+ author_hash TEXT,
30
+ op TEXT NOT NULL CHECK (op IN ('assert', 'tombstone')) DEFAULT 'assert',
31
+ supersedes TEXT,
32
+ created_at TEXT NOT NULL,
33
+ imported_at TEXT NOT NULL DEFAULT (datetime('now'))
34
+ );
35
+
36
+ -- 1b. Identity is (repo_id, entry_id). The UNIQUE index is what makes
37
+ -- import() an idempotent upsert instead of an append.
38
+ CREATE UNIQUE INDEX IF NOT EXISTS uq_shared_entries
39
+ ON shared_entries(repo_id, entry_id);
40
+ CREATE INDEX IF NOT EXISTS idx_shared_entries_op
41
+ ON shared_entries(op);
42
+ CREATE INDEX IF NOT EXISTS idx_shared_entries_type
43
+ ON shared_entries(type);
44
+
45
+ -- 2. okf_imports: append-only audit of every read of the shared file,
46
+ -- including no-op reads and refusals. `file_hash` drives the skip
47
+ -- path in SharedLayer.import() (D8-14).
48
+ CREATE TABLE IF NOT EXISTS okf_imports (
49
+ id TEXT PRIMARY KEY,
50
+ repo_id TEXT NOT NULL,
51
+ path TEXT NOT NULL,
52
+ file_hash TEXT,
53
+ entries_parsed INTEGER NOT NULL DEFAULT 0,
54
+ entries_folded INTEGER NOT NULL DEFAULT 0,
55
+ entries_rejected INTEGER NOT NULL DEFAULT 0,
56
+ skipped INTEGER NOT NULL DEFAULT 0,
57
+ imported_at TEXT NOT NULL DEFAULT (datetime('now'))
58
+ );
59
+
60
+ CREATE INDEX IF NOT EXISTS idx_okf_imports_repo
61
+ ON okf_imports(repo_id, imported_at);
62
+
63
+ -- 3. memories: the second scoping dimension and the layer marker.
64
+ -- repo_id is NULLABLE and back-filled by the post-apply hook, not by
65
+ -- a DEFAULT: the value depends on the row's existing project_id and
66
+ -- SQLite cannot express that in a column default.
67
+ -- layer carries NO CHECK constraint - widening it later would force
68
+ -- the migration-004 rebuild path (D8-07). Enforced in TypeScript.
69
+ ALTER TABLE memories ADD COLUMN repo_id TEXT;
70
+ ALTER TABLE memories ADD COLUMN layer TEXT NOT NULL DEFAULT 'local';
71
+ ALTER TABLE memories ADD COLUMN shared_entry_id TEXT;
72
+
73
+ CREATE INDEX IF NOT EXISTS idx_memories_repo_id
74
+ ON memories(repo_id, status);
75
+ CREATE INDEX IF NOT EXISTS idx_memories_layer
76
+ ON memories(layer);
77
+
78
+ -- 4. kevin_metrics: seed the six v0.8 counters (33 -> 39).
79
+ INSERT OR IGNORE INTO kevin_metrics (key, value) VALUES
80
+ ('shared_entries_total', 0),
81
+ ('shared_entries_imported', 0),
82
+ ('shared_entries_exported', 0),
83
+ ('okf_merge_folds', 0),
84
+ ('rekey_events', 0),
85
+ ('injections_from_shared', 0);
86
+
87
+ -- 5. kevin_settings: seed the five v0.8 flags (18 -> 23).
88
+ -- shared_layer_enabled defaults OFF: this release must be opted into,
89
+ -- because its first side effect is a new file in the user's repository.
90
+ -- shared_confidence_floor (0.7) is deliberately STRICTER than
91
+ -- injection_confidence_floor (0.6) - see 5.7.
92
+ INSERT OR IGNORE INTO kevin_settings (key, value) VALUES
93
+ ('shared_layer_enabled', '0'),
94
+ ('okf_path', '.kevin/knowledge.okf'),
95
+ ('share_requires_approval', '1'),
96
+ ('author_identity_mode', 'hashed'),
97
+ ('shared_confidence_floor', '0.7');
98
+
99
+ -- 6. Seed version 009.
100
+ INSERT OR IGNORE INTO schema_version (version) VALUES ('009');
@@ -0,0 +1,78 @@
1
+ -- ============================================================
2
+ -- Kevin v0.9.0 "Native" — host surface and hook liveness
3
+ -- Migration 010. Additive only. No table rebuild.
4
+ -- ============================================================
5
+
6
+ -- 1. Hook liveness. Deliberately NOT project-scoped: a hook is a
7
+ -- property of the host binary, not of a checkout. One row per
8
+ -- hook name, updated in place. See D9-08.
9
+ CREATE TABLE IF NOT EXISTS hook_liveness (
10
+ hook TEXT PRIMARY KEY,
11
+ experimental INTEGER NOT NULL DEFAULT 0,
12
+ fire_count INTEGER NOT NULL DEFAULT 0,
13
+ error_count INTEGER NOT NULL DEFAULT 0,
14
+ expected_count INTEGER NOT NULL DEFAULT 0,
15
+ first_seen_at TEXT,
16
+ last_seen_at TEXT,
17
+ dead_since TEXT,
18
+ plugin_version TEXT
19
+ );
20
+
21
+ CREATE INDEX IF NOT EXISTS idx_hook_liveness_dead
22
+ ON hook_liveness(dead_since);
23
+
24
+ -- 2. Host probe history. Append-only. Off by default; exists so a
25
+ -- user chasing an intermittent fault can turn it on and get a
26
+ -- timeline instead of a single current value.
27
+ CREATE TABLE IF NOT EXISTS host_probes (
28
+ id TEXT PRIMARY KEY,
29
+ probed_at TEXT NOT NULL DEFAULT (datetime('now')),
30
+ plugin_version TEXT,
31
+ flavour TEXT NOT NULL,
32
+ has_shell INTEGER NOT NULL DEFAULT 0,
33
+ v2_skill INTEGER NOT NULL DEFAULT 0,
34
+ v2_reference INTEGER NOT NULL DEFAULT 0,
35
+ notes TEXT
36
+ );
37
+
38
+ CREATE INDEX IF NOT EXISTS idx_host_probes_at
39
+ ON host_probes(probed_at);
40
+
41
+ -- 3. Native registration outcomes. One row per attach attempt, so
42
+ -- "registered but unverified" is a queryable state rather than a
43
+ -- log line.
44
+ CREATE TABLE IF NOT EXISTS native_registrations (
45
+ id TEXT PRIMARY KEY,
46
+ attached_at TEXT NOT NULL DEFAULT (datetime('now')),
47
+ surface TEXT NOT NULL CHECK (surface IN ('skill', 'reference')),
48
+ registered INTEGER NOT NULL DEFAULT 0,
49
+ verified INTEGER NOT NULL DEFAULT 0,
50
+ note TEXT
51
+ );
52
+
53
+ CREATE INDEX IF NOT EXISTS idx_native_registrations_surface
54
+ ON native_registrations(surface, attached_at);
55
+
56
+ -- 4. Metric seeds (39 -> 45).
57
+ INSERT OR IGNORE INTO kevin_metrics (key, value) VALUES
58
+ ('hook_fires_total', 0),
59
+ ('hook_errors_total', 0),
60
+ ('hooks_dead_total', 0),
61
+ ('injections_suppressed_dead_hook', 0),
62
+ ('native_registrations_total', 0),
63
+ ('native_registration_failures', 0);
64
+
65
+ -- 5. Setting seeds (23 -> 27).
66
+ -- hook_liveness_enabled defaults ON: it is a read-only instrument
67
+ -- on the success path, and an instrument nobody switches on is an
68
+ -- instrument nobody has.
69
+ -- native_registration_enabled defaults OFF: it changes where a
70
+ -- curated skill comes from, and that is a change a user opts into.
71
+ INSERT OR IGNORE INTO kevin_settings (key, value) VALUES
72
+ ('hook_liveness_enabled', '1'),
73
+ ('native_registration_enabled', '0'),
74
+ ('host_probe_history_enabled', '0'),
75
+ ('dead_hook_report_threshold', '3');
76
+
77
+ -- 6. Version marker.
78
+ INSERT OR IGNORE INTO schema_version (version) VALUES ('010');
@@ -0,0 +1,69 @@
1
+ -- =============================================================
2
+ -- Kevin v1.0.0 "Proven" — migration 011
3
+ -- Adds: perf sampling, benchmark results.
4
+ -- No ALTER TABLE (second release running); no column is retyped.
5
+ -- =============================================================
6
+
7
+ -- 1. Per-scope latency aggregates, flushed at session.idle.
8
+ -- Machine-scoped: no project_id, for the same reason hook_liveness
9
+ -- has none (v0.9.0 D9-08) — latency is a property of the install.
10
+ CREATE TABLE IF NOT EXISTS perf_samples (
11
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
12
+ scope TEXT NOT NULL,
13
+ sample_count INTEGER NOT NULL,
14
+ p50_ms REAL NOT NULL,
15
+ p95_ms REAL NOT NULL,
16
+ max_ms REAL NOT NULL,
17
+ budget_p95_ms REAL NOT NULL,
18
+ within_budget INTEGER NOT NULL DEFAULT 1,
19
+ recorded_at TEXT NOT NULL DEFAULT (datetime('now'))
20
+ );
21
+
22
+ CREATE INDEX IF NOT EXISTS idx_perf_samples_scope
23
+ ON perf_samples(scope, recorded_at DESC);
24
+ CREATE INDEX IF NOT EXISTS idx_perf_samples_breach
25
+ ON perf_samples(within_budget) WHERE within_budget = 0;
26
+
27
+ -- 2. Benchmark results. Append-only. One row per arm per run.
28
+ -- arm CHECK is closed by the plan; widening requires a table rebuild (intentional friction).
29
+ -- scope CHECK is intentionally absent: the scope union may gain members in a 1.x minor.
30
+ CREATE TABLE IF NOT EXISTS bench_runs (
31
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
32
+ corpus_digest TEXT NOT NULL,
33
+ contract_digest TEXT NOT NULL,
34
+ package_version TEXT NOT NULL,
35
+ runtime TEXT NOT NULL,
36
+ arm TEXT NOT NULL,
37
+ k INTEGER NOT NULL,
38
+ precision_at_k REAL NOT NULL,
39
+ recall_at_k REAL NOT NULL,
40
+ mrr REAL NOT NULL,
41
+ ran_at TEXT NOT NULL DEFAULT (datetime('now')),
42
+ CHECK (arm IN ('none', 'recent-k', 'random-k', 'kevin'))
43
+ );
44
+
45
+ CREATE INDEX IF NOT EXISTS idx_bench_runs_corpus
46
+ ON bench_runs(corpus_digest, ran_at DESC);
47
+ CREATE INDEX IF NOT EXISTS idx_bench_runs_arm
48
+ ON bench_runs(arm, ran_at DESC);
49
+
50
+ -- 3. Metric seeds (45 -> 51).
51
+ INSERT OR IGNORE INTO kevin_metrics (key, value) VALUES ('perf_samples_recorded', 0);
52
+ INSERT OR IGNORE INTO kevin_metrics (key, value) VALUES ('perf_budget_breaches', 0);
53
+ INSERT OR IGNORE INTO kevin_metrics (key, value) VALUES ('dispose_fires_total', 0);
54
+ INSERT OR IGNORE INTO kevin_metrics (key, value) VALUES ('dispose_misses_total', 0);
55
+ INSERT OR IGNORE INTO kevin_metrics (key, value) VALUES ('contract_digest_changes', 0);
56
+ INSERT OR IGNORE INTO kevin_metrics (key, value) VALUES ('bench_runs_total', 0);
57
+
58
+ -- 4. Setting seeds (27 -> 31). All TEXT; '0'/'1' are strings.
59
+ INSERT OR IGNORE INTO kevin_settings (key, value) VALUES ('perf_enabled', '1');
60
+ INSERT OR IGNORE INTO kevin_settings (key, value) VALUES ('perf_ring_capacity', '512');
61
+ INSERT OR IGNORE INTO kevin_settings (key, value) VALUES ('perf_flush_on_idle', '1');
62
+ INSERT OR IGNORE INTO kevin_settings (key, value) VALUES ('contract_report_enabled', '1');
63
+
64
+ -- 5. `dispose` becomes the seventh instrumented hook (v0.9.0 migration 010
65
+ -- seeded six rows; this is a data seed, not a schema change).
66
+ INSERT OR IGNORE INTO hook_liveness (hook) VALUES ('dispose');
67
+
68
+ -- 6. Schema version.
69
+ INSERT OR IGNORE INTO schema_version (version) VALUES ('011');
@@ -0,0 +1,24 @@
1
+ -- millisecond companions for the two tables whose ORDERING decides outcomes.
2
+ ALTER TABLE tool_calls ADD COLUMN ts_ms INTEGER;
3
+ ALTER TABLE kevin_injections ADD COLUMN injected_at_ms INTEGER;
4
+
5
+ -- conservative backfill: seconds -> ms (x1000). Rows keep their original
6
+ -- second-granularity value in the old column; nothing is rewritten there.
7
+ UPDATE tool_calls SET ts_ms =
8
+ CAST(strftime('%s', ts) AS INTEGER) * 1000
9
+ WHERE ts_ms IS NULL AND ts IS NOT NULL;
10
+
11
+ UPDATE kevin_injections SET injected_at_ms =
12
+ CAST(strftime('%s', injected_at) AS INTEGER) * 1000
13
+ WHERE injected_at_ms IS NULL AND injected_at IS NOT NULL;
14
+
15
+ CREATE INDEX idx_tool_calls_ts_ms ON tool_calls(ts_ms);
16
+ CREATE INDEX idx_injections_injected_ms ON kevin_injections(injected_at_ms);
17
+
18
+ -- metric seeds (C-05 additions carry since=1.1.0 in contract)
19
+ INSERT INTO kevin_metrics (key, value, updated_at) VALUES
20
+ ('bench_regression_failures', 0, datetime('now')),
21
+ ('forget_requests_total', 0, datetime('now')),
22
+ ('forget_tombstones_published', 0, datetime('now'));
23
+
24
+ INSERT INTO schema_version (version) VALUES ('012');
@@ -0,0 +1,23 @@
1
+ import type { Store } from "./Store.js";
2
+ import type { Materializer } from "./Materializer.js";
3
+ export interface NativeRegistration {
4
+ registered: {
5
+ skill: boolean;
6
+ reference: boolean;
7
+ };
8
+ verified: {
9
+ skill: boolean;
10
+ reference: boolean;
11
+ };
12
+ notes: string[];
13
+ }
14
+ export interface SettingsReader {
15
+ getSetting(key: string, fallback?: string): string | null;
16
+ }
17
+ export interface NativeDeps {
18
+ materializer: Materializer;
19
+ settings: SettingsReader;
20
+ onVerified?: (surface: "skill" | "reference", registered: boolean, verified: boolean) => void;
21
+ store?: Store;
22
+ }
23
+ export declare const V2_SPECIFIER = "@opencode-ai/plugin/v2/promise";
package/dist/native.js ADDED
@@ -0,0 +1 @@
1
+ export const V2_SPECIFIER = "@opencode-ai/plugin/v2/promise";
@@ -0,0 +1,3 @@
1
+ import type { Store } from "./Store.js";
2
+ export declare function exportOkf(store: Store, projectId?: string | null): string;
3
+ export declare function exportMarkdown(store: Store, projectId?: string | null): string;
@@ -0,0 +1,134 @@
1
+ import { computeConfidence } from "./confidence.js";
2
+ const EXPORT_TYPES = new Set(["decision", "rule", "pattern"]);
3
+ function formatTimestamp(ts) {
4
+ try {
5
+ // SQLite `datetime('now')` is UTC without a zone suffix; parse as
6
+ // UTC so a round-trip never shifts the value by the local offset.
7
+ const iso = ts.includes("T") ? ts : `${ts.replace(" ", "T")}Z`;
8
+ return new Date(iso).toISOString().replace("T", " ").slice(0, 19);
9
+ }
10
+ catch {
11
+ return ts;
12
+ }
13
+ }
14
+ /**
15
+ * BUG-008 — select export rows including `recurrence_count` (the v0.4.0
16
+ * demotion signal, column from migration 005). DBs that predate 005 have
17
+ * no such column; the SELECT is retried without it and recurrences
18
+ * degrade to 0 (legacy confidence formula applies — see below).
19
+ *
20
+ * v0.8.0 (K8-027) — the v1 export is scoped to one project: the global
21
+ * database may hold several projects' memories, and once an export lands
22
+ * in a committed file a cross-project leak becomes permanent history.
23
+ * A `project_id` of null keeps the legacy unscoped behaviour for direct
24
+ * callers that predate the fix; the kevin_export tool always threads it.
25
+ */
26
+ function selectExportRows(store, projectId) {
27
+ try {
28
+ return {
29
+ hasRecurrence: true,
30
+ rows: store
31
+ .prepare(`SELECT id, type, content, scope, relevance_score, fingerprint,
32
+ evidence_count, recurrence_count, last_verified_at, status,
33
+ source_tool, source_session, created_at, updated_at
34
+ FROM memories
35
+ WHERE status = 'active'
36
+ AND project_id = ?
37
+ ORDER BY type, created_at DESC`)
38
+ .all(projectId),
39
+ };
40
+ }
41
+ catch {
42
+ const legacy = store
43
+ .prepare(`SELECT id, type, content, scope, relevance_score, fingerprint,
44
+ evidence_count, last_verified_at, status,
45
+ source_tool, source_session, created_at, updated_at
46
+ FROM memories
47
+ WHERE status = 'active'
48
+ AND project_id = ?
49
+ ORDER BY type, created_at DESC`)
50
+ .all(projectId);
51
+ return {
52
+ hasRecurrence: false,
53
+ rows: legacy.map((r) => ({ ...r, recurrence_count: 0 })),
54
+ };
55
+ }
56
+ }
57
+ /**
58
+ * BUG-008 — the exported confidence must match the two-sided v0.4.0
59
+ * formula (K4-010), demoting lessons with recurrences. Pre-005 DBs (no
60
+ * `recurrence_count` column) keep the legacy one-sided formula.
61
+ */
62
+ function exportConfidence(row, hasRecurrence) {
63
+ const confidence = hasRecurrence
64
+ ? computeConfidence(row.evidence_count, row.recurrence_count)
65
+ : Math.min(1, 0.5 + 0.1 * row.evidence_count);
66
+ return confidence.toFixed(2);
67
+ }
68
+ export function exportOkf(store, projectId = null) {
69
+ const { rows: allRows, hasRecurrence } = selectExportRows(store, projectId);
70
+ const filtered = allRows.filter((r) => EXPORT_TYPES.has(r.type));
71
+ if (filtered.length === 0)
72
+ return "<!-- No exportable memories found. -->\n";
73
+ const blocks = [];
74
+ for (const m of filtered) {
75
+ const fm = [];
76
+ fm.push("---");
77
+ fm.push(`id: ${m.id}`);
78
+ fm.push(`type: ${m.type}`);
79
+ fm.push(`confidence: ${exportConfidence(m, hasRecurrence)}`);
80
+ fm.push(`evidence_count: ${m.evidence_count}`);
81
+ if (m.recurrence_count > 0) {
82
+ fm.push(`recurrence_count: ${m.recurrence_count}`);
83
+ }
84
+ if (m.last_verified_at) {
85
+ fm.push(`last_verified_at: ${formatTimestamp(m.last_verified_at)}`);
86
+ }
87
+ if (m.fingerprint) {
88
+ fm.push(`fingerprint: ${m.fingerprint}`);
89
+ }
90
+ fm.push(`created: ${formatTimestamp(m.created_at)}`);
91
+ fm.push(`scope: ${m.scope}`);
92
+ fm.push("---");
93
+ fm.push("");
94
+ fm.push(m.content);
95
+ blocks.push(fm.join("\n"));
96
+ }
97
+ return `${blocks.join("\n\n")}\n`;
98
+ }
99
+ export function exportMarkdown(store, projectId = null) {
100
+ const { rows: allRows, hasRecurrence } = selectExportRows(store, projectId);
101
+ const filtered = allRows.filter((r) => EXPORT_TYPES.has(r.type));
102
+ if (filtered.length === 0)
103
+ return "# Kevin Knowledge Export\n\n_No exportable memories found._\n";
104
+ const lines = [];
105
+ lines.push("# Kevin Knowledge Export");
106
+ lines.push("");
107
+ lines.push(`Exported: ${new Date().toISOString().replace("T", " ").slice(0, 19)}`);
108
+ lines.push(`Total entries: ${filtered.length}`);
109
+ lines.push("");
110
+ for (const m of filtered) {
111
+ lines.push(`## ${m.type}: \`${m.fingerprint ?? m.id.slice(0, 8)}\``);
112
+ lines.push("");
113
+ lines.push(`- **ID:** \`${m.id}\``);
114
+ lines.push(`- **Confidence:** ${exportConfidence(m, hasRecurrence)}`);
115
+ lines.push(`- **Evidence count:** ${m.evidence_count}`);
116
+ if (m.recurrence_count > 0) {
117
+ lines.push(`- **Recurrence count:** ${m.recurrence_count}`);
118
+ }
119
+ if (m.last_verified_at) {
120
+ lines.push(`- **Last verified:** ${formatTimestamp(m.last_verified_at)}`);
121
+ }
122
+ if (m.fingerprint) {
123
+ lines.push(`- **Fingerprint:** \`${m.fingerprint}\``);
124
+ }
125
+ lines.push(`- **Scope:** ${m.scope}`);
126
+ lines.push(`- **Created:** ${formatTimestamp(m.created_at)}`);
127
+ lines.push("");
128
+ lines.push(m.content);
129
+ lines.push("");
130
+ lines.push("---");
131
+ lines.push("");
132
+ }
133
+ return lines.join("\n");
134
+ }
@@ -0,0 +1,76 @@
1
+ import type { MemoryService } from "./MemoryService.js";
2
+ /**
3
+ * v0.3.0 fix — A single parsed bundle entry. `evidence_count` and
4
+ * `last_verified_at` are preserved across round-trips (export → import)
5
+ * so causal confidence is not lost when knowledge is shared between
6
+ * projects. v0.4.0 (BUG-008) — `recurrence_count` too, so the two-sided
7
+ * v0.4.0 confidence demotion survives the round-trip.
8
+ */
9
+ export interface ParsedEntry {
10
+ id: string;
11
+ type: string;
12
+ content: string;
13
+ fingerprint: string | null;
14
+ evidence_count: number;
15
+ /** v0.4.0 (BUG-008) — recurrence demotion, 0 when absent. */
16
+ recurrence_count: number;
17
+ last_verified_at: string | null;
18
+ }
19
+ /**
20
+ * v0.3.0 fix — Clean state-machine parser for the frontmatter bundle
21
+ * format produced by `okf-export.ts::exportOkf`. Each entry is:
22
+ *
23
+ * ---
24
+ * id: <uuid>
25
+ * type: <decision|rule|pattern>
26
+ * confidence: 0.70
27
+ * evidence_count: 2
28
+ * last_verified_at: 2026-07-25 12:34:56
29
+ * fingerprint: <hex>
30
+ * created: 2026-07-25 12:00:00
31
+ * scope: project
32
+ * ---
33
+ *
34
+ * <content body, may span multiple lines, may include `---` lines
35
+ * within — the body terminator is the NEXT top-level `---` followed
36
+ * by an `id:` line, or EOF>
37
+ *
38
+ * The previous implementation only ever flagged `inFm = true` once and
39
+ * never reset `contentStarted` between entries, so 2..N entries were
40
+ * silently dropped (bug #1). This rewrite handles arbitrary numbers of
41
+ * consecutive frontmatter sections.
42
+ */
43
+ export declare function parseMarkdownBundle(text: string): ParsedEntry[];
44
+ /**
45
+ * v0.3.0 fix — Fallback parser for the markdown-style `##` heading
46
+ * format produced by `okf-export.ts::exportMarkdown`. The previous
47
+ * version extracted `fingerprint: null` for every entry (regex was
48
+ * pinned to 16 hex chars while actual fingerprints are variable
49
+ * length) and contaminated `content` with the heading line and
50
+ * metadata bullets. This version cleanly separates metadata bullets
51
+ * (looking for `**ID:**`, `**Fingerprint:**`, `**Evidence count:**`,
52
+ * `**Last verified:**`, `**Scope:**` prefixes) from the content
53
+ * body, which starts after the first blank line following the bullet
54
+ * block and runs until the trailing `---` separator (or EOF).
55
+ */
56
+ export declare function parseMarkdownHeadings(text: string): ParsedEntry[];
57
+ export interface ImportResult {
58
+ imported: number;
59
+ superseded: number;
60
+ }
61
+ /**
62
+ * v0.3.0 fix — Ingest a bundle (frontmatter OR markdown) into the
63
+ * local SQLite store as `context` memories with `origin='imported'`.
64
+ *
65
+ * Fixes over the v0.3.0 baseline:
66
+ * * Multi-entry bundles now produce N imports instead of N=1 — the
67
+ * parser bug is closed.
68
+ * * `ParsedEntry.evidence_count` and `last_verified_at` are
69
+ * threaded through so causal confidence survives a round-trip.
70
+ * * `ImportResult.superseded` is populated using
71
+ * `countSupersedeCandidates`, which mirrors the supersede logic
72
+ * in `MemoryService.save()`. Previously it was hard-coded to 0.
73
+ * * Generated ids use `uuidv7()` (the project's id generator)
74
+ * instead of `crypto.randomUUID()` for consistency.
75
+ */
76
+ export declare function importOkf(bundle: string, memoryService: MemoryService): ImportResult;