@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.
- package/README.md +582 -580
- package/dist/migrations/001_initial.sql +91 -91
- package/dist/migrations/003_v02_signal.sql +57 -57
- package/dist/migrations/004_v03_knowledge.sql +138 -138
- package/dist/migrations/005_v04_signal.sql +57 -57
- package/dist/migrations/006_v05_glassbox.sql +118 -118
- package/dist/migrations/007_v06_pull.sql +144 -144
- package/dist/migrations/008_v07_truth.sql +124 -0
- package/dist/plugin/ConflictDetector.d.ts +35 -0
- package/dist/plugin/ConflictDetector.js +283 -0
- package/dist/plugin/ConflictDetector.js.map +1 -0
- package/dist/plugin/ConventionMiner.d.ts +35 -0
- package/dist/plugin/ConventionMiner.js +243 -0
- package/dist/plugin/ConventionMiner.js.map +1 -0
- package/dist/plugin/Curator.js +45 -14
- package/dist/plugin/Curator.js.map +1 -1
- package/dist/plugin/MemoryService.d.ts +18 -0
- package/dist/plugin/MemoryService.js +92 -2
- package/dist/plugin/MemoryService.js.map +1 -1
- package/dist/plugin/Migrate.js +31 -2
- package/dist/plugin/Migrate.js.map +1 -1
- package/dist/plugin/Reflector.js +13 -0
- package/dist/plugin/Reflector.js.map +1 -1
- package/dist/plugin/RepoTruth.d.ts +80 -0
- package/dist/plugin/RepoTruth.js +600 -0
- package/dist/plugin/RepoTruth.js.map +1 -0
- package/dist/plugin/Retrospective.js +8 -0
- package/dist/plugin/Retrospective.js.map +1 -1
- package/dist/plugin/index.d.ts +7 -2
- package/dist/plugin/index.js +135 -2
- package/dist/plugin/index.js.map +1 -1
- package/dist/plugin/kevin_audit.d.ts +41 -1
- package/dist/plugin/kevin_audit.js +98 -3
- package/dist/plugin/kevin_audit.js.map +1 -1
- package/dist/plugin/kevin_conflicts.d.ts +9 -0
- package/dist/plugin/kevin_conflicts.js +51 -0
- package/dist/plugin/kevin_conflicts.js.map +1 -0
- package/dist/plugin/kevin_facts.d.ts +42 -0
- package/dist/plugin/kevin_facts.js +37 -0
- package/dist/plugin/kevin_facts.js.map +1 -0
- package/dist/plugin/metrics.d.ts +3 -3
- package/dist/plugin/metrics.js +12 -2
- package/dist/plugin/metrics.js.map +1 -1
- package/dist/plugin/replay-types.d.ts +2 -2
- package/migrations/001_initial.sql +91 -91
- package/migrations/003_v02_signal.sql +57 -57
- package/migrations/004_v03_knowledge.sql +138 -138
- package/migrations/005_v04_signal.sql +57 -57
- package/migrations/006_v05_glassbox.sql +118 -118
- package/migrations/007_v06_pull.sql +144 -144
- package/migrations/008_v07_truth.sql +124 -0
- 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
|
+
}
|