@jmtrin/opencode-kevin 0.6.0 → 0.7.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 (52) hide show
  1. package/README.md +582 -580
  2. package/dist/migrations/001_initial.sql +91 -91
  3. package/dist/migrations/003_v02_signal.sql +57 -57
  4. package/dist/migrations/004_v03_knowledge.sql +138 -138
  5. package/dist/migrations/005_v04_signal.sql +57 -57
  6. package/dist/migrations/006_v05_glassbox.sql +118 -118
  7. package/dist/migrations/007_v06_pull.sql +144 -144
  8. package/dist/migrations/008_v07_truth.sql +124 -0
  9. package/dist/plugin/ConflictDetector.d.ts +35 -0
  10. package/dist/plugin/ConflictDetector.js +283 -0
  11. package/dist/plugin/ConflictDetector.js.map +1 -0
  12. package/dist/plugin/ConventionMiner.d.ts +35 -0
  13. package/dist/plugin/ConventionMiner.js +243 -0
  14. package/dist/plugin/ConventionMiner.js.map +1 -0
  15. package/dist/plugin/Curator.js +45 -14
  16. package/dist/plugin/Curator.js.map +1 -1
  17. package/dist/plugin/MemoryService.d.ts +18 -0
  18. package/dist/plugin/MemoryService.js +92 -2
  19. package/dist/plugin/MemoryService.js.map +1 -1
  20. package/dist/plugin/Migrate.js +31 -2
  21. package/dist/plugin/Migrate.js.map +1 -1
  22. package/dist/plugin/Reflector.js +13 -0
  23. package/dist/plugin/Reflector.js.map +1 -1
  24. package/dist/plugin/RepoTruth.d.ts +80 -0
  25. package/dist/plugin/RepoTruth.js +600 -0
  26. package/dist/plugin/RepoTruth.js.map +1 -0
  27. package/dist/plugin/Retrospective.js +8 -0
  28. package/dist/plugin/Retrospective.js.map +1 -1
  29. package/dist/plugin/index.d.ts +7 -2
  30. package/dist/plugin/index.js +135 -2
  31. package/dist/plugin/index.js.map +1 -1
  32. package/dist/plugin/kevin_audit.d.ts +41 -1
  33. package/dist/plugin/kevin_audit.js +98 -3
  34. package/dist/plugin/kevin_audit.js.map +1 -1
  35. package/dist/plugin/kevin_conflicts.d.ts +9 -0
  36. package/dist/plugin/kevin_conflicts.js +51 -0
  37. package/dist/plugin/kevin_conflicts.js.map +1 -0
  38. package/dist/plugin/kevin_facts.d.ts +42 -0
  39. package/dist/plugin/kevin_facts.js +37 -0
  40. package/dist/plugin/kevin_facts.js.map +1 -0
  41. package/dist/plugin/metrics.d.ts +3 -3
  42. package/dist/plugin/metrics.js +12 -2
  43. package/dist/plugin/metrics.js.map +1 -1
  44. package/dist/plugin/replay-types.d.ts +2 -2
  45. package/migrations/001_initial.sql +91 -91
  46. package/migrations/003_v02_signal.sql +57 -57
  47. package/migrations/004_v03_knowledge.sql +138 -138
  48. package/migrations/005_v04_signal.sql +57 -57
  49. package/migrations/006_v05_glassbox.sql +118 -118
  50. package/migrations/007_v06_pull.sql +144 -144
  51. package/migrations/008_v07_truth.sql +124 -0
  52. package/package.json +3 -2
@@ -0,0 +1,124 @@
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');
@@ -0,0 +1,35 @@
1
+ import type { MemoryService } from "./MemoryService.js";
2
+ import type { RepoTruth } from "./RepoTruth.js";
3
+ import type { Store } from "./Store.js";
4
+ import type { Metrics } from "./metrics.js";
5
+ export type ConflictKind = "repo_truth" | "decision_pair" | "temporal";
6
+ export interface Conflict {
7
+ readonly id: string;
8
+ readonly kind: ConflictKind;
9
+ readonly memoryA: string;
10
+ readonly memoryB?: string;
11
+ readonly factId?: string;
12
+ readonly detail: string;
13
+ }
14
+ export interface RepoTruthConflictInput {
15
+ readonly memoryId: string;
16
+ readonly factId?: string | null;
17
+ readonly reasons: readonly string[];
18
+ }
19
+ export declare class ConflictDetector {
20
+ private readonly store;
21
+ private readonly projectId;
22
+ private readonly repoTruth?;
23
+ private readonly memoryService?;
24
+ private readonly metrics;
25
+ constructor(store: Store, projectId: string, metrics?: Metrics | null, repoTruth?: RepoTruth | undefined, memoryService?: MemoryService | undefined);
26
+ detect(repoTruthInput?: readonly RepoTruthConflictInput[]): Conflict[];
27
+ acknowledge(id: string): void;
28
+ /** Human-only operation. It resolves the conflict row, never a memory. */
29
+ resolve(id: string, keepMemoryId: string): void;
30
+ private detectRepoTruth;
31
+ private repoTruthInputs;
32
+ private detectDecisionPairs;
33
+ private detectTemporal;
34
+ private insertIfNew;
35
+ }
@@ -0,0 +1,283 @@
1
+ import { normalize } from "./fingerprint.js";
2
+ import { uuidv7 } from "./uuid.js";
3
+ const POLARITY_RULES = [
4
+ {
5
+ positive: ["use"],
6
+ negative: [
7
+ ["do", "not", "use"],
8
+ ["don't", "use"],
9
+ ["never", "use"],
10
+ ],
11
+ },
12
+ { positive: ["always"], negative: [["never"]] },
13
+ { positive: ["required"], negative: [["forbidden"], ["not", "required"]] },
14
+ { positive: ["enable"], negative: [["disable"]] },
15
+ { positive: ["prefer"], negative: [["avoid"]] },
16
+ ];
17
+ const STOP_WORDS = new Set([
18
+ "a",
19
+ "an",
20
+ "and",
21
+ "are",
22
+ "as",
23
+ "at",
24
+ "be",
25
+ "by",
26
+ "for",
27
+ "from",
28
+ "in",
29
+ "is",
30
+ "it",
31
+ "of",
32
+ "on",
33
+ "or",
34
+ "that",
35
+ "the",
36
+ "this",
37
+ "to",
38
+ "with",
39
+ "we",
40
+ "when",
41
+ "where",
42
+ "you",
43
+ ]);
44
+ const POLARITY_WORDS = new Set([
45
+ "use",
46
+ "do",
47
+ "not",
48
+ // "don't" tokenizes to ["don", "t"] (see tokens()); the lexicon phrase
49
+ // ["don't","use"] is matched by tokenizing the phrase the same way, so
50
+ // both fragments must be excluded from subjects() too (K7-014).
51
+ "don",
52
+ "t",
53
+ "never",
54
+ "always",
55
+ "required",
56
+ "forbidden",
57
+ "enable",
58
+ "disable",
59
+ "prefer",
60
+ "avoid",
61
+ ]);
62
+ export class ConflictDetector {
63
+ store;
64
+ projectId;
65
+ repoTruth;
66
+ memoryService;
67
+ metrics;
68
+ constructor(store, projectId, metrics, repoTruth, memoryService) {
69
+ this.store = store;
70
+ this.projectId = projectId;
71
+ this.repoTruth = repoTruth;
72
+ this.memoryService = memoryService;
73
+ this.metrics = metrics ?? null;
74
+ }
75
+ detect(repoTruthInput) {
76
+ const detected = [];
77
+ detected.push(...this.detectRepoTruth(repoTruthInput));
78
+ detected.push(...this.detectDecisionPairs());
79
+ detected.push(...this.detectTemporal());
80
+ return detected;
81
+ }
82
+ acknowledge(id) {
83
+ this.store
84
+ .prepare("UPDATE memory_conflicts SET status = 'acknowledged' WHERE id = ? AND project_id = ? AND status = 'open'")
85
+ .run(id, this.projectId);
86
+ }
87
+ /** Human-only operation. It resolves the conflict row, never a memory. */
88
+ resolve(id, keepMemoryId) {
89
+ const row = this.store
90
+ .prepare(`SELECT memory_a, memory_b FROM memory_conflicts
91
+ WHERE id = ? AND project_id = ? AND status <> 'resolved'`)
92
+ .get(id, this.projectId);
93
+ if (!row)
94
+ throw new Error(`conflict not found: ${id}`);
95
+ if (keepMemoryId !== row.memory_a && keepMemoryId !== row.memory_b) {
96
+ throw new Error("keep must identify one of the conflict memories");
97
+ }
98
+ this.store
99
+ .prepare("UPDATE memory_conflicts SET status = 'resolved', resolved_at = datetime('now') WHERE id = ? AND project_id = ?")
100
+ .run(id, this.projectId);
101
+ }
102
+ detectRepoTruth(input) {
103
+ const items = input ?? this.repoTruthInputs();
104
+ const out = [];
105
+ for (const item of items) {
106
+ for (const reason of item.reasons) {
107
+ this.memoryService?.applyTruthPenalty(item.memoryId, 0.5, reason);
108
+ const id = this.insertIfNew({
109
+ kind: "repo_truth",
110
+ memoryA: item.memoryId,
111
+ memoryB: null,
112
+ factId: item.factId ?? null,
113
+ detail: reason,
114
+ });
115
+ if (id)
116
+ out.push({
117
+ id,
118
+ kind: "repo_truth",
119
+ memoryA: item.memoryId,
120
+ factId: item.factId ?? undefined,
121
+ detail: reason,
122
+ });
123
+ }
124
+ }
125
+ // v0.7.0 (D7-03) — "recoverable by re-scanning": a memory whose
126
+ // contradiction has lapsed (the repo no longer contradicts it) is
127
+ // de-ranked no longer. The conflict row stays open — resolution is
128
+ // human-initiated only (D7-06) — but the score penalty lifts, so
129
+ // fixing the repo genuinely recovers the memory. Runs only on the
130
+ // production path (no explicit input): explicit-input callers control
131
+ // every write themselves.
132
+ if (!input && this.repoTruth && this.memoryService) {
133
+ const penalized = this.store
134
+ .prepare("SELECT id FROM memories WHERE project_id = ? AND truth_penalty > 0 AND status = 'active'")
135
+ .all(this.projectId);
136
+ for (const row of penalized) {
137
+ const memory = this.memoryService.getById(row.id);
138
+ if (!memory)
139
+ continue;
140
+ if ((this.repoTruth.contradictions(memory) ?? []).length === 0) {
141
+ this.memoryService.applyTruthPenalty(row.id, 0, "");
142
+ }
143
+ }
144
+ }
145
+ return out;
146
+ }
147
+ repoTruthInputs() {
148
+ if (!this.repoTruth)
149
+ return [];
150
+ const rows = this.store
151
+ .prepare(`SELECT id, type, content, scope, relevance_score, source_tool,
152
+ source_session, metadata, created_at, updated_at, expires_at,
153
+ project_id, fingerprint, origin, evidence_count, last_verified_at,
154
+ status, fix_args, ignored, superseded_by, feedback_positive,
155
+ feedback_negative, curated, curated_at, inferable,
156
+ truth_penalty, contradicted_at
157
+ FROM memories WHERE project_id = ? AND status = 'active'`)
158
+ .all(this.projectId);
159
+ return rows.flatMap((row) => {
160
+ const memory = row;
161
+ const reasons = this.repoTruth?.contradictions(memory) ?? [];
162
+ return reasons.length > 0 ? [{ memoryId: row.id, reasons }] : [];
163
+ });
164
+ }
165
+ detectDecisionPairs() {
166
+ const rows = this.store
167
+ .prepare(`SELECT id, type, content, fingerprint FROM memories
168
+ WHERE project_id = ? AND status = 'active'
169
+ AND type IN ('decision', 'rule') ORDER BY id`)
170
+ .all(this.projectId);
171
+ const out = [];
172
+ for (let i = 0; i < rows.length; i++) {
173
+ for (let j = i + 1; j < rows.length; j++) {
174
+ const a = rows[i];
175
+ const b = rows[j];
176
+ if (!a || !b || a.fingerprint === b.fingerprint)
177
+ continue;
178
+ const pair = oppositePair(a.content, b.content);
179
+ if (!pair)
180
+ continue;
181
+ const id = this.insertIfNew({
182
+ kind: "decision_pair",
183
+ memoryA: a.id,
184
+ memoryB: b.id,
185
+ factId: null,
186
+ detail: pair,
187
+ });
188
+ if (id)
189
+ out.push({
190
+ id,
191
+ kind: "decision_pair",
192
+ memoryA: a.id,
193
+ memoryB: b.id,
194
+ detail: pair,
195
+ });
196
+ }
197
+ }
198
+ return out;
199
+ }
200
+ detectTemporal() {
201
+ const rows = this.store
202
+ .prepare(`SELECT m.id,
203
+ MAX(CASE WHEN i.outcome = 'effective' THEN i.injected_at END) AS effective_at,
204
+ MAX(CASE WHEN i.outcome = 'ineffective' THEN i.injected_at END) AS ineffective_at
205
+ FROM memories m JOIN kevin_injections i ON i.memory_id = m.id
206
+ WHERE m.project_id = ? AND m.status = 'active'
207
+ GROUP BY m.id
208
+ HAVING effective_at IS NOT NULL AND ineffective_at IS NOT NULL
209
+ AND ineffective_at > effective_at`)
210
+ .all(this.projectId);
211
+ const out = [];
212
+ for (const row of rows) {
213
+ const detail = `memory was effective at ${row.effective_at} and became ineffective at ${row.ineffective_at}`;
214
+ const id = this.insertIfNew({
215
+ kind: "temporal",
216
+ memoryA: row.id,
217
+ memoryB: null,
218
+ factId: null,
219
+ detail,
220
+ });
221
+ if (id)
222
+ out.push({ id, kind: "temporal", memoryA: row.id, detail });
223
+ }
224
+ return out;
225
+ }
226
+ insertIfNew(input) {
227
+ const existing = this.store
228
+ .prepare(`SELECT id FROM memory_conflicts
229
+ WHERE project_id = ? AND kind = ? AND memory_a = ?
230
+ AND (memory_b IS ? OR (memory_b IS NULL AND ? IS NULL))
231
+ AND (fact_id IS ? OR (fact_id IS NULL AND ? IS NULL))
232
+ AND status = 'open' LIMIT 1`)
233
+ .get(this.projectId, input.kind, input.memoryA, input.memoryB, input.memoryB, input.factId, input.factId);
234
+ if (existing)
235
+ return null;
236
+ const id = uuidv7();
237
+ this.store
238
+ .prepare(`INSERT INTO memory_conflicts
239
+ (id, project_id, memory_a, memory_b, fact_id, kind, detail)
240
+ VALUES (?, ?, ?, ?, ?, ?, ?)`)
241
+ .run(id, this.projectId, input.memoryA, input.memoryB, input.factId, input.kind, input.detail);
242
+ this.metrics?.incr("conflicts_detected", 1);
243
+ return id;
244
+ }
245
+ }
246
+ function tokens(text) {
247
+ return normalize(text).match(/[a-z0-9@._/-]+/g) ?? [];
248
+ }
249
+ function hasPhrase(words, phrase) {
250
+ if (phrase.length === 1)
251
+ return words.includes(phrase[0] ?? "");
252
+ for (let i = 0; i <= words.length - phrase.length; i++) {
253
+ if (phrase.every((word, offset) => words[i + offset] === word))
254
+ return true;
255
+ }
256
+ return false;
257
+ }
258
+ function polarity(text, rule) {
259
+ const words = tokens(text);
260
+ // Lexicon phrases are run through the same tokenizer as the text: "don't
261
+ // use" → ["don","t","use"], which is exactly how the text's own apostrophe
262
+ // is split, so a phrase never becomes unreachable (K7-014).
263
+ const match = (phrase) => hasPhrase(words, tokens(phrase.join(" ")));
264
+ const negative = rule.negative.some(match);
265
+ return { positive: !negative && match(rule.positive), negative };
266
+ }
267
+ function subjects(text) {
268
+ return new Set(tokens(text).filter((token) => !STOP_WORDS.has(token) && !POLARITY_WORDS.has(token)));
269
+ }
270
+ function oppositePair(a, b) {
271
+ const shared = [...subjects(a)].some((token) => subjects(b).has(token));
272
+ if (!shared)
273
+ return null;
274
+ for (const rule of POLARITY_RULES) {
275
+ const pa = polarity(a, rule);
276
+ const pb = polarity(b, rule);
277
+ if ((pa.positive && pb.negative) || (pa.negative && pb.positive)) {
278
+ return `statements carry opposite ${rule.positive.join(" ")} polarity for shared subject ${shared}`;
279
+ }
280
+ }
281
+ return null;
282
+ }
283
+ //# sourceMappingURL=ConflictDetector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ConflictDetector.js","sourceRoot":"","sources":["../../plugin/ConflictDetector.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,35 @@
1
+ import type { MemoryService } from "./MemoryService.js";
2
+ import type { Store } from "./Store.js";
3
+ import type { Metrics } from "./metrics.js";
4
+ export interface MinedConvention {
5
+ /** Caller-supplied, derived from the normalized statement (D7-11). */
6
+ readonly fingerprint: string;
7
+ /** Human-readable rule statement. */
8
+ readonly statement: string;
9
+ /** Distinct sessions in which the pattern held. */
10
+ readonly support: number;
11
+ readonly kind: "sequence" | "co_edit";
12
+ }
13
+ export declare class ConventionMiner {
14
+ private readonly store;
15
+ private readonly memoryService;
16
+ private readonly projectId;
17
+ private readonly metrics;
18
+ constructor(store: Store, memoryService: MemoryService, projectId: string, metrics?: Metrics | null);
19
+ /** v0.7.0 (K7-010) — the `sequence` miner over successful tool_calls. */
20
+ mineSequence(minSupport?: number): MinedConvention[];
21
+ /** v0.7.0 (K7-011) — the `co_edit` miner over same-session file writes. */
22
+ mineCoEdit(minSupport?: number): MinedConvention[];
23
+ /** v0.7.0 (K7-012) — mine both kinds in one call. */
24
+ mine(minSupport?: number): MinedConvention[];
25
+ /**
26
+ * v0.7.0 (K7-012 / plan §5.4, D7-11) — emit mined(conventions) as `rule`
27
+ * memories. Returns the number of memories created or refreshed and
28
+ * increments `conventions_mined`. Runs on session.idle only, behind
29
+ * convention_mining_enabled. The fingerprint derives from the statement,
30
+ * so a re-mine of an unchanged convention collides and supersedes.
31
+ */
32
+ emit(conventions: MinedConvention[]): number;
33
+ private materialize;
34
+ private fetchToolCalls;
35
+ }