@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.
Files changed (141) hide show
  1. package/dist/plugin/index.d.ts +4 -3
  2. package/dist/plugin/index.js +122 -113
  3. package/dist/plugin/native.d.ts +2 -2
  4. package/dist/plugin/native.js +2 -2
  5. package/package.json +56 -68
  6. package/LICENSE +0 -21
  7. package/README.md +0 -605
  8. package/dist/migrations/001_initial.sql +0 -92
  9. package/dist/migrations/002_indexes.sql +0 -14
  10. package/dist/migrations/003_v02_signal.sql +0 -58
  11. package/dist/migrations/004_v03_knowledge.sql +0 -138
  12. package/dist/migrations/005_v04_signal.sql +0 -57
  13. package/dist/migrations/006_v05_glassbox.sql +0 -118
  14. package/dist/migrations/007_v06_pull.sql +0 -145
  15. package/dist/migrations/008_v07_truth.sql +0 -124
  16. package/dist/migrations/009_v08_team.sql +0 -100
  17. package/dist/migrations/010_v09_native.sql +0 -78
  18. package/dist/migrations/011_v10_proven.sql +0 -69
  19. package/dist/migrations/012_v11_drift.sql +0 -24
  20. package/dist/plugin/Archiver.d.ts +0 -42
  21. package/dist/plugin/Archiver.js +0 -83
  22. package/dist/plugin/ArtifactWriter.d.ts +0 -93
  23. package/dist/plugin/ArtifactWriter.js +0 -291
  24. package/dist/plugin/CausalChain.d.ts +0 -22
  25. package/dist/plugin/CausalChain.js +0 -197
  26. package/dist/plugin/ChatBridge.d.ts +0 -41
  27. package/dist/plugin/ChatBridge.js +0 -103
  28. package/dist/plugin/ConflictDetector.d.ts +0 -35
  29. package/dist/plugin/ConflictDetector.js +0 -260
  30. package/dist/plugin/ContextInjector.d.ts +0 -222
  31. package/dist/plugin/ContextInjector.js +0 -533
  32. package/dist/plugin/ConventionMiner.d.ts +0 -35
  33. package/dist/plugin/ConventionMiner.js +0 -242
  34. package/dist/plugin/Curator.d.ts +0 -116
  35. package/dist/plugin/Curator.js +0 -324
  36. package/dist/plugin/DashboardHtml.d.ts +0 -5
  37. package/dist/plugin/DashboardHtml.js +0 -180
  38. package/dist/plugin/Feedback.d.ts +0 -67
  39. package/dist/plugin/Feedback.js +0 -120
  40. package/dist/plugin/HookLiveness.d.ts +0 -88
  41. package/dist/plugin/HookLiveness.js +0 -322
  42. package/dist/plugin/InjectionLedger.d.ts +0 -98
  43. package/dist/plugin/InjectionLedger.js +0 -295
  44. package/dist/plugin/LessonFixer.d.ts +0 -44
  45. package/dist/plugin/LessonFixer.js +0 -45
  46. package/dist/plugin/Materializer.d.ts +0 -83
  47. package/dist/plugin/Materializer.js +0 -181
  48. package/dist/plugin/MemoryService.d.ts +0 -360
  49. package/dist/plugin/MemoryService.js +0 -1242
  50. package/dist/plugin/Migrate.d.ts +0 -17
  51. package/dist/plugin/Migrate.js +0 -271
  52. package/dist/plugin/PatternMiner.d.ts +0 -49
  53. package/dist/plugin/PatternMiner.js +0 -132
  54. package/dist/plugin/QualityGate.d.ts +0 -124
  55. package/dist/plugin/QualityGate.js +0 -119
  56. package/dist/plugin/Reflector.d.ts +0 -91
  57. package/dist/plugin/Reflector.js +0 -307
  58. package/dist/plugin/RepoIdentity.d.ts +0 -124
  59. package/dist/plugin/RepoIdentity.js +0 -300
  60. package/dist/plugin/RepoTruth.d.ts +0 -80
  61. package/dist/plugin/RepoTruth.js +0 -599
  62. package/dist/plugin/Retrospective.d.ts +0 -17
  63. package/dist/plugin/Retrospective.js +0 -216
  64. package/dist/plugin/SharedLayer.d.ts +0 -159
  65. package/dist/plugin/SharedLayer.js +0 -462
  66. package/dist/plugin/Store.d.ts +0 -29
  67. package/dist/plugin/Store.js +0 -50
  68. package/dist/plugin/ToolCallObserver.d.ts +0 -32
  69. package/dist/plugin/ToolCallObserver.js +0 -201
  70. package/dist/plugin/TuiActions.d.ts +0 -43
  71. package/dist/plugin/TuiActions.js +0 -181
  72. package/dist/plugin/TuiSnapshots.d.ts +0 -24
  73. package/dist/plugin/TuiSnapshots.js +0 -158
  74. package/dist/plugin/columns.d.ts +0 -11
  75. package/dist/plugin/columns.js +0 -54
  76. package/dist/plugin/confidence.d.ts +0 -8
  77. package/dist/plugin/confidence.js +0 -34
  78. package/dist/plugin/contract.d.ts +0 -54
  79. package/dist/plugin/contract.js +0 -386
  80. package/dist/plugin/diff.d.ts +0 -8
  81. package/dist/plugin/diff.js +0 -182
  82. package/dist/plugin/escape.d.ts +0 -39
  83. package/dist/plugin/escape.js +0 -60
  84. package/dist/plugin/fingerprint.d.ts +0 -27
  85. package/dist/plugin/fingerprint.js +0 -73
  86. package/dist/plugin/inferability.d.ts +0 -32
  87. package/dist/plugin/inferability.js +0 -88
  88. package/dist/plugin/kevin_approve.d.ts +0 -34
  89. package/dist/plugin/kevin_approve.js +0 -52
  90. package/dist/plugin/kevin_audit.d.ts +0 -229
  91. package/dist/plugin/kevin_audit.js +0 -631
  92. package/dist/plugin/kevin_bench.d.ts +0 -10
  93. package/dist/plugin/kevin_bench.js +0 -82
  94. package/dist/plugin/kevin_conflicts.d.ts +0 -9
  95. package/dist/plugin/kevin_conflicts.js +0 -50
  96. package/dist/plugin/kevin_contract.d.ts +0 -8
  97. package/dist/plugin/kevin_contract.js +0 -59
  98. package/dist/plugin/kevin_doctor.d.ts +0 -62
  99. package/dist/plugin/kevin_doctor.js +0 -196
  100. package/dist/plugin/kevin_facts.d.ts +0 -42
  101. package/dist/plugin/kevin_facts.js +0 -36
  102. package/dist/plugin/kevin_forget.d.ts +0 -33
  103. package/dist/plugin/kevin_forget.js +0 -260
  104. package/dist/plugin/kevin_native.d.ts +0 -29
  105. package/dist/plugin/kevin_native.js +0 -79
  106. package/dist/plugin/kevin_propose.d.ts +0 -23
  107. package/dist/plugin/kevin_propose.js +0 -14
  108. package/dist/plugin/kevin_publish.d.ts +0 -38
  109. package/dist/plugin/kevin_publish.js +0 -18
  110. package/dist/plugin/kevin_why.d.ts +0 -23
  111. package/dist/plugin/kevin_why.js +0 -111
  112. package/dist/plugin/memory-format.d.ts +0 -20
  113. package/dist/plugin/memory-format.js +0 -59
  114. package/dist/plugin/metrics.d.ts +0 -97
  115. package/dist/plugin/metrics.js +0 -288
  116. package/dist/plugin/okf-export.d.ts +0 -3
  117. package/dist/plugin/okf-export.js +0 -134
  118. package/dist/plugin/okf-import.d.ts +0 -76
  119. package/dist/plugin/okf-import.js +0 -271
  120. package/dist/plugin/okf.d.ts +0 -107
  121. package/dist/plugin/okf.js +0 -303
  122. package/dist/plugin/perf.d.ts +0 -35
  123. package/dist/plugin/perf.js +0 -174
  124. package/dist/plugin/query-tokenizer.d.ts +0 -13
  125. package/dist/plugin/query-tokenizer.js +0 -133
  126. package/dist/plugin/redact.d.ts +0 -2
  127. package/dist/plugin/redact.js +0 -18
  128. package/dist/plugin/replay-types.d.ts +0 -69
  129. package/dist/plugin/replay-types.js +0 -156
  130. package/dist/plugin/replay.d.ts +0 -36
  131. package/dist/plugin/replay.js +0 -202
  132. package/dist/plugin/sqlite-adapter.d.ts +0 -12
  133. package/dist/plugin/sqlite-adapter.js +0 -81
  134. package/dist/plugin/time-ms.d.ts +0 -1
  135. package/dist/plugin/time-ms.js +0 -16
  136. package/dist/plugin/tui-types.d.ts +0 -59
  137. package/dist/plugin/tui-types.js +0 -4
  138. package/dist/plugin/tui.d.ts +0 -18
  139. package/dist/plugin/tui.js +0 -198
  140. package/dist/plugin/uuid.d.ts +0 -1
  141. package/dist/plugin/uuid.js +0 -50
@@ -1,124 +0,0 @@
1
- -- ============================================================
2
- -- Kevin 0.7.0 — Migration 008: Project Truth (additive)
3
- -- ============================================================
4
- -- Backward-compatible, additive only. Two new tables, two new
5
- -- nullable/defaulted columns on `memories`, one index, five
6
- -- metric seeds, four setting seeds. NO table rebuild: nothing
7
- -- here widens a CHECK constraint on an existing table.
8
- --
9
- -- Section 1: repo_facts — the ground-truth store.
10
- -- Section 2: memory_conflicts — surfaced, never auto-resolved.
11
- -- Section 3: memories truth columns.
12
- -- Section 4: metric seeds.
13
- -- Section 5: setting seeds.
14
- -- Section 6: schema_version.
15
- -- ============================================================
16
-
17
- -- ------------------------------------------------------------
18
- -- 1. repo_facts — facts extracted from package.json and
19
- -- tsconfig.json. Both are JSON, parsed with JSON.parse; this
20
- -- release adds NO parser and NO runtime dependency.
21
- --
22
- -- The UNIQUE index INCLUDES project_id and that is load-
23
- -- bearing (D7-02). Kevin's DB is global
24
- -- (~/.opencode-kevin/kevin.db, projectId =
25
- -- fingerprint(process.cwd())). Without project_id, project A's
26
- -- packageManager=npm would overwrite project B's
27
- -- packageManager=pnpm and contradictions() would then flag A's
28
- -- memories against B's repository.
29
- --
30
- -- source_mtime lets scan() skip re-parsing an unchanged file.
31
- -- ------------------------------------------------------------
32
- CREATE TABLE IF NOT EXISTS repo_facts (
33
- id TEXT PRIMARY KEY,
34
- project_id TEXT NOT NULL,
35
- file TEXT NOT NULL,
36
- key_path TEXT NOT NULL,
37
- value TEXT NOT NULL,
38
- fingerprint TEXT NOT NULL,
39
- source_mtime TEXT,
40
- scanned_at TEXT NOT NULL DEFAULT (datetime('now'))
41
- );
42
- CREATE UNIQUE INDEX IF NOT EXISTS uq_repo_facts ON repo_facts(project_id, file, key_path);
43
- CREATE INDEX IF NOT EXISTS idx_repo_facts_fp ON repo_facts(fingerprint);
44
-
45
- -- ------------------------------------------------------------
46
- -- 2. memory_conflicts — detection only. status moves
47
- -- open → acknowledged → resolved, and ONLY the kevin_conflicts
48
- -- tool may move it to 'resolved' (D7-06). No session.idle path
49
- -- writes 'resolved'.
50
- --
51
- -- memory_a / memory_b / fact_id carry NO REFERENCES clause on
52
- -- purpose: Store sets PRAGMA foreign_keys = ON, and a hard FK
53
- -- would block deleting a memory that participates in a
54
- -- conflict.
55
- -- ------------------------------------------------------------
56
- CREATE TABLE IF NOT EXISTS memory_conflicts (
57
- id TEXT PRIMARY KEY,
58
- project_id TEXT NOT NULL,
59
- memory_a TEXT NOT NULL,
60
- memory_b TEXT,
61
- fact_id TEXT,
62
- kind TEXT NOT NULL CHECK (kind IN ('repo_truth','decision_pair','temporal')),
63
- detail TEXT,
64
- status TEXT NOT NULL DEFAULT 'open' CHECK (status IN ('open','acknowledged','resolved')),
65
- detected_at TEXT NOT NULL DEFAULT (datetime('now')),
66
- resolved_at TEXT
67
- );
68
-
69
- CREATE INDEX IF NOT EXISTS idx_conflicts_status ON memory_conflicts(status);
70
- CREATE INDEX IF NOT EXISTS idx_conflicts_memory ON memory_conflicts(memory_a);
71
-
72
- -- ------------------------------------------------------------
73
- -- 3. memories: the de-ranking column and its timestamp.
74
- --
75
- -- truth_penalty is clamped by application code to [0, 0.5] and
76
- -- multiplies rankScore as (1 - truth_penalty), AFTER the
77
- -- existing BM25 × origin_boost × recency_decay chain. At 0.0
78
- -- the v0.6.0 ranking is reproduced exactly (D7-04).
79
- --
80
- -- There is deliberately no status transition here. Contra-
81
- -- diction de-ranks; it never deletes (Principle 24, D7-03).
82
- -- ------------------------------------------------------------
83
- ALTER TABLE memories ADD COLUMN truth_penalty REAL NOT NULL DEFAULT 0.0;
84
- ALTER TABLE memories ADD COLUMN contradicted_at TEXT;
85
-
86
- CREATE INDEX IF NOT EXISTS idx_memories_truth_penalty ON memories(truth_penalty);
87
-
88
- -- ------------------------------------------------------------
89
- -- 4. Metric seeds. Order matches the additions to METRIC_KEYS
90
- -- in metrics.ts (28 → 33).
91
- -- ------------------------------------------------------------
92
- INSERT OR IGNORE INTO kevin_metrics (key, value) VALUES
93
- ('repo_facts_scanned', 0),
94
- ('memories_contradicted', 0),
95
- ('conventions_mined', 0),
96
- ('conflicts_detected', 0),
97
- ('error_lessons_suppressed', 0);
98
-
99
- -- ------------------------------------------------------------
100
- -- 5. Setting seeds. Values are TEXT, always. Read them with an
101
- -- explicit string comparison or an explicit Number() parse —
102
- -- never `=== 1`, and never for truthiness.
103
- --
104
- -- error_lesson_mode is a TEXT ENUM ('all' | 'triage_only').
105
- -- `if (mode)` is true for BOTH values and would put every
106
- -- installation into triage mode on upgrade. Compare with
107
- -- === "triage_only" (D7-12). Defaults to 'all', preserving
108
- -- v0.6.0 behaviour exactly.
109
- --
110
- -- The three feature flags default to '0' (off): a release that
111
- -- changes nothing until asked must not silently start scanning
112
- -- the repository or mining conventions on upgrade. They are
113
- -- opted into explicitly. (Task K7-001 / plan §5 & §12.)
114
- -- ------------------------------------------------------------
115
- INSERT OR IGNORE INTO kevin_settings (key, value) VALUES
116
- ('repo_truth_enabled', '0'),
117
- ('convention_mining_enabled', '0'),
118
- ('conflict_detection_enabled', '0'),
119
- ('error_lesson_mode', 'all');
120
-
121
- -- ------------------------------------------------------------
122
- -- 6. Version marker.
123
- -- ------------------------------------------------------------
124
- INSERT OR IGNORE INTO schema_version (version) VALUES ('008');
@@ -1,100 +0,0 @@
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');
@@ -1,78 +0,0 @@
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');
@@ -1,69 +0,0 @@
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');
@@ -1,24 +0,0 @@
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');
@@ -1,42 +0,0 @@
1
- import type { MemoryService } from "./MemoryService.js";
2
- import type { Store } from "./Store.js";
3
- import type { Metrics } from "./metrics.js";
4
- /**
5
- * v0.5.0 Archiver (K5-012 / plan §5.4, D5-05).
6
- *
7
- * Lifecycle tail of the memory loop: reflectors mark a lesson `stale` when
8
- * its fingerprint recurred enough times (penalizeRecurringReflectors,
9
- * migration 003), but nothing ever retires those rows — they keep
10
- * circulating through status filters and inflating counts. The Archiver
11
- * runs on `session.idle` and retires stale memories whose last activity
12
- * (`updated_at`, falling back to `last_verified_at`) is older than
13
- * `archive_after_days` (kevin_settings, seeded '30' in migration 006).
14
- *
15
- * Rules:
16
- * - Only `status = 'stale'` rows with `archived_at IS NULL`.
17
- * - `type != 'pattern'`: a pattern is the FIXED form of a fingerprint
18
- * (K4-025); archiving it would break kevin_why and the recurrence
19
- * re-admission path (QualityGate rule 4).
20
- * - Clock: the injection clock (K5-008) — tests freeze it; production
21
- * defaults to `new Date()`. Never `Date.now()` inside.
22
- * - `memories_archived` is bumped by the batch size, once per run.
23
- * - Pre-006 DBs (no `archived_at`) degrade to a no-op.
24
- *
25
- * Archived rows remain queryable with `includeSuperseded: true`-style
26
- * reads (status filter is exclusive, not destructive).
27
- */
28
- export declare class Archiver {
29
- private readonly store;
30
- private readonly memoryService;
31
- private readonly metrics;
32
- private readonly now;
33
- constructor(store: Store, memoryService: MemoryService, metrics?: Metrics | null, now?: () => Date);
34
- /**
35
- * Archives every eligible stale memory. Returns the number of rows
36
- * retired. Idempotent: archived rows are excluded by `archived_at IS
37
- * NULL`, so a second run archives nothing.
38
- */
39
- run(): number;
40
- private archiveAfterDays;
41
- private hasArchivedColumn;
42
- }
@@ -1,83 +0,0 @@
1
- import { hasArchivedColumn } from "./columns.js";
2
- /**
3
- * v0.5.0 Archiver (K5-012 / plan §5.4, D5-05).
4
- *
5
- * Lifecycle tail of the memory loop: reflectors mark a lesson `stale` when
6
- * its fingerprint recurred enough times (penalizeRecurringReflectors,
7
- * migration 003), but nothing ever retires those rows — they keep
8
- * circulating through status filters and inflating counts. The Archiver
9
- * runs on `session.idle` and retires stale memories whose last activity
10
- * (`updated_at`, falling back to `last_verified_at`) is older than
11
- * `archive_after_days` (kevin_settings, seeded '30' in migration 006).
12
- *
13
- * Rules:
14
- * - Only `status = 'stale'` rows with `archived_at IS NULL`.
15
- * - `type != 'pattern'`: a pattern is the FIXED form of a fingerprint
16
- * (K4-025); archiving it would break kevin_why and the recurrence
17
- * re-admission path (QualityGate rule 4).
18
- * - Clock: the injection clock (K5-008) — tests freeze it; production
19
- * defaults to `new Date()`. Never `Date.now()` inside.
20
- * - `memories_archived` is bumped by the batch size, once per run.
21
- * - Pre-006 DBs (no `archived_at`) degrade to a no-op.
22
- *
23
- * Archived rows remain queryable with `includeSuperseded: true`-style
24
- * reads (status filter is exclusive, not destructive).
25
- */
26
- export class Archiver {
27
- store;
28
- memoryService;
29
- metrics;
30
- now;
31
- constructor(store, memoryService, metrics, now = () => new Date()) {
32
- this.store = store;
33
- this.memoryService = memoryService;
34
- this.metrics = metrics ?? null;
35
- this.now = now;
36
- }
37
- /**
38
- * Archives every eligible stale memory. Returns the number of rows
39
- * retired. Idempotent: archived rows are excluded by `archived_at IS
40
- * NULL`, so a second run archives nothing.
41
- */
42
- run() {
43
- if (!this.hasArchivedColumn())
44
- return 0;
45
- const days = this.archiveAfterDays();
46
- if (days <= 0)
47
- return 0;
48
- const now = this.now();
49
- const cutoff = formatSqliteUtc(new Date(now.getTime() - days * 86_400_000));
50
- this.store
51
- .prepare(`UPDATE memories
52
- SET status = 'archived', archived_at = ?, updated_at = ?
53
- WHERE status = 'stale'
54
- AND type != 'pattern'
55
- AND archived_at IS NULL
56
- AND updated_at < ?`)
57
- .run(formatSqliteUtc(now), formatSqliteUtc(now), cutoff);
58
- const changed = this.store.prepare("SELECT changes() AS n").get();
59
- const n = changed.n;
60
- if (n > 0)
61
- this.metrics?.incr("memories_archived", n);
62
- return n;
63
- }
64
- archiveAfterDays() {
65
- try {
66
- const raw = this.memoryService.getSetting("archive_after_days", "30");
67
- const n = Number(raw);
68
- return Number.isFinite(n) ? n : 30;
69
- }
70
- catch {
71
- return 30;
72
- }
73
- }
74
- hasArchivedColumn() {
75
- return hasArchivedColumn(this.store);
76
- }
77
- }
78
- /** 'YYYY-MM-DD HH:MM:SS' UTC — the format SQLite `datetime('now')` uses,
79
- * so lexicographic comparisons against `updated_at` are valid. */
80
- function formatSqliteUtc(d) {
81
- const pad = (n) => String(n).padStart(2, "0");
82
- return `${d.getUTCFullYear()}-${pad(d.getUTCMonth() + 1)}-${pad(d.getUTCDate())} ${pad(d.getUTCHours())}:${pad(d.getUTCMinutes())}:${pad(d.getUTCSeconds())}`;
83
- }
@@ -1,93 +0,0 @@
1
- import type { Store } from "./Store.js";
2
- import type { Metrics } from "./metrics.js";
3
- export declare const MARKER_BEGIN = "<!-- kevin:begin \u2014 curated by opencode-kevin, safe to edit -->";
4
- export declare const MARKER_END = "<!-- kevin:end -->";
5
- export type WriteOutcome = "written" | "noop" | "refused";
6
- /**
7
- * v0.8.0 (K8-019 / D8-08) — the two write modes. `markers` is the
8
- * v0.6.0 behaviour, byte for byte: a splice between the two marker
9
- * comments, used for `AGENTS.md`, a file humans edit. `whole` replaces
10
- * the entire file and is used only for Kevin-owned paths such as
11
- * `.kevin/knowledge.okf` — a file humans do not hand-edit.
12
- */
13
- export type WriteMode = "markers" | "whole";
14
- export interface WriteRequest {
15
- readonly path: string;
16
- readonly mode: WriteMode;
17
- /** marker block body ("markers"), or whole-file content ("whole"). */
18
- readonly content: string;
19
- /**
20
- * Caller-side refusal reason (K8-020 / D6-03): when present, the plan
21
- * is refused — nothing is written, and the refusal is audited with
22
- * both hashes. The refusal conditions belong to the caller; the
23
- * writer only records them.
24
- */
25
- readonly refusal?: string;
26
- }
27
- export interface WritePlan {
28
- readonly path: string;
29
- readonly before: string;
30
- readonly after: string;
31
- readonly diff: string;
32
- readonly outcome: WriteOutcome;
33
- readonly reason?: string;
34
- readonly hashBefore: string;
35
- readonly hashAfter: string;
36
- }
37
- /**
38
- * Rule 9, layer (a) — superseded in v1.0.0 by `escapeForMarkerBlock`
39
- * in `plugin/escape.ts` (K10-027), which carries the same idempotent
40
- * entity discipline.
41
- */
42
- /**
43
- * v1.0.0 (K10-027 / plan §5.7) — the boundary functions now live in
44
- * `plugin/escape.ts`; this composer applies them in fence-then-marker
45
- * order. Layer (b) — strip any line containing `kevin:begin` or
46
- * `kevin:end`, in any casing — is a filter rather than an escape and
47
- * stays here. Without layer (b), a memory containing a literal
48
- * `<!-- kevin:end -->` line would close the marker comment early and
49
- * let subsequent content escape the curated region on the next
50
- * regeneration — a marker-injection variant of the v0.1.5
51
- * prompt-injection defect (plan §3.5). The trailing `-->` strip is kept
52
- * as defence in depth although `escapeForMarkerBlock` already escapes
53
- * every `>` that could form one.
54
- */
55
- export declare function sanitizeArtifactBody(body: string): string;
56
- /**
57
- * v1.0.0 (K10-027 / plan §5.7, rule 2) — whole-file writes escape by
58
- * container. Only `.okf` files are line-oriented JSON: each line gets
59
- * `escapeForOkfLine`, which is the identity on a well-formed OKF line
60
- * (canonical JSON never contains raw control characters) and therefore
61
- * preserves the re-render `noop`. Kevin-owned markdown paths keep their
62
- * bytes — fences there are legitimate content.
63
- */
64
- export declare function escapeForContainer(path: string, content: string): string;
65
- /**
66
- * v0.6.0 (K6-005 / plan §5.1) — the single write path to disk (D6-01).
67
- *
68
- * `plan()` is pure: it reads the target file, locates the marker pair, splices
69
- * the body between the markers and returns a {@link WritePlan}. It performs no
70
- * writes — rule 1. `apply()` is implemented by K6-007; until then it is a stub
71
- * that throws, so no caller can accidentally write before the audit trail
72
- * exists.
73
- *
74
- * `projectId` is a constructor argument rather than a per-call argument so that
75
- * every audit row is attributed without the call site having to remember.
76
- */
77
- export declare class ArtifactWriter {
78
- private readonly store;
79
- private readonly projectId;
80
- private readonly metrics;
81
- constructor(store: Store, projectId: string, metrics?: Metrics | null);
82
- plan(path: string, body: string): WritePlan;
83
- plan(request: WriteRequest): WritePlan;
84
- /**
85
- * K8-019 (D8-08) — the single write funnel: every file Kevin writes
86
- * goes through this method, which is the ONLY call site of `apply()`
87
- * in the plugin (asserted by tests/unit/single_write_path.test.ts).
88
- */
89
- write(request: WriteRequest, proposalId?: string): WriteOutcome;
90
- apply(plan: WritePlan, proposalId?: string): WriteOutcome;
91
- private renameTemp;
92
- private audit;
93
- }