@jmtrin/opencode-kevin 0.9.0 → 1.1.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/LICENSE +21 -0
- package/README.md +575 -823
- package/dist/migrations/001_initial.sql +91 -91
- package/dist/migrations/002_indexes.sql +13 -13
- 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/011_v10_proven.sql +69 -0
- package/dist/migrations/012_v11_drift.sql +24 -0
- package/dist/plugin/Archiver.js +2 -18
- package/dist/plugin/ArtifactWriter.d.ts +25 -10
- package/dist/plugin/ArtifactWriter.js +43 -25
- package/dist/plugin/CausalChain.js +32 -14
- package/dist/plugin/ConflictDetector.js +7 -30
- package/dist/plugin/ContextInjector.js +0 -1
- package/dist/plugin/ConventionMiner.js +0 -1
- package/dist/plugin/Curator.js +0 -1
- package/dist/plugin/Feedback.js +2 -20
- package/dist/plugin/HookLiveness.d.ts +7 -0
- package/dist/plugin/HookLiveness.js +26 -28
- package/dist/plugin/InjectionLedger.js +104 -58
- package/dist/plugin/LessonFixer.js +0 -1
- package/dist/plugin/Materializer.js +1 -89
- package/dist/plugin/MemoryService.d.ts +59 -1
- package/dist/plugin/MemoryService.js +13 -107
- package/dist/plugin/Migrate.d.ts +1 -1
- package/dist/plugin/Migrate.js +30 -3
- package/dist/plugin/PatternMiner.js +0 -1
- package/dist/plugin/QualityGate.js +0 -1
- package/dist/plugin/Reflector.js +0 -1
- package/dist/plugin/RepoIdentity.js +0 -1
- package/dist/plugin/RepoTruth.js +0 -1
- package/dist/plugin/Retrospective.js +11 -1
- package/dist/plugin/SharedLayer.js +0 -1
- package/dist/plugin/Store.js +0 -1
- package/dist/plugin/ToolCallObserver.js +18 -6
- package/dist/plugin/capabilities.js +0 -1
- package/dist/plugin/columns.d.ts +11 -0
- package/dist/plugin/columns.js +54 -0
- package/dist/plugin/confidence.js +0 -1
- package/dist/plugin/contract.d.ts +54 -0
- package/dist/plugin/contract.js +383 -0
- package/dist/plugin/diff.js +0 -1
- package/dist/plugin/escape.d.ts +39 -0
- package/dist/plugin/escape.js +60 -0
- package/dist/plugin/fingerprint.js +0 -1
- package/dist/plugin/host.js +0 -1
- package/dist/plugin/index.d.ts +2 -2
- package/dist/plugin/index.js +448 -229
- package/dist/plugin/inferability.js +0 -1
- package/dist/plugin/kevin_approve.js +0 -1
- package/dist/plugin/kevin_audit.d.ts +31 -0
- package/dist/plugin/kevin_audit.js +60 -1
- package/dist/plugin/kevin_bench.d.ts +10 -0
- package/dist/plugin/kevin_bench.js +82 -0
- package/dist/plugin/kevin_conflicts.js +0 -1
- package/dist/plugin/kevin_contract.d.ts +8 -0
- package/dist/plugin/kevin_contract.js +59 -0
- package/dist/plugin/kevin_doctor.d.ts +5 -0
- package/dist/plugin/kevin_doctor.js +30 -2
- package/dist/plugin/kevin_facts.js +0 -1
- package/dist/plugin/kevin_forget.d.ts +33 -0
- package/dist/plugin/kevin_forget.js +260 -0
- package/dist/plugin/kevin_native.js +0 -1
- package/dist/plugin/kevin_propose.js +0 -1
- package/dist/plugin/kevin_publish.js +0 -1
- package/dist/plugin/kevin_why.js +1 -19
- package/dist/plugin/memory-format.js +0 -1
- package/dist/plugin/metrics.d.ts +1 -1
- package/dist/plugin/metrics.js +4 -1
- package/dist/plugin/native.js +0 -1
- package/dist/plugin/okf-export.js +0 -1
- package/dist/plugin/okf-import.js +0 -1
- package/dist/plugin/okf.js +0 -1
- package/dist/plugin/perf.d.ts +35 -0
- package/dist/plugin/perf.js +174 -0
- package/dist/plugin/query-tokenizer.js +56 -9
- package/dist/plugin/redact.js +0 -1
- package/dist/plugin/replay-types.js +0 -1
- package/dist/plugin/replay.js +0 -1
- package/dist/plugin/sqlite-adapter.js +0 -1
- package/dist/plugin/time-ms.d.ts +1 -0
- package/dist/plugin/time-ms.js +16 -0
- package/dist/plugin/uuid.js +0 -1
- package/package.json +22 -6
- package/dist/plugin/Archiver.js.map +0 -1
- package/dist/plugin/ArtifactWriter.js.map +0 -1
- package/dist/plugin/CausalChain.js.map +0 -1
- package/dist/plugin/ConflictDetector.js.map +0 -1
- package/dist/plugin/ContextInjector.js.map +0 -1
- package/dist/plugin/ConventionMiner.js.map +0 -1
- package/dist/plugin/Curator.js.map +0 -1
- package/dist/plugin/Feedback.js.map +0 -1
- package/dist/plugin/HookLiveness.js.map +0 -1
- package/dist/plugin/InjectionLedger.js.map +0 -1
- package/dist/plugin/LessonFixer.js.map +0 -1
- package/dist/plugin/Materializer.js.map +0 -1
- package/dist/plugin/MemoryService.js.map +0 -1
- package/dist/plugin/Migrate.js.map +0 -1
- package/dist/plugin/PatternMiner.js.map +0 -1
- package/dist/plugin/QualityGate.js.map +0 -1
- package/dist/plugin/Reflector.js.map +0 -1
- package/dist/plugin/RepoIdentity.js.map +0 -1
- package/dist/plugin/RepoTruth.js.map +0 -1
- package/dist/plugin/Retrospective.js.map +0 -1
- package/dist/plugin/SharedLayer.js.map +0 -1
- package/dist/plugin/Store.js.map +0 -1
- package/dist/plugin/ToolCallObserver.js.map +0 -1
- package/dist/plugin/capabilities.js.map +0 -1
- package/dist/plugin/confidence.js.map +0 -1
- package/dist/plugin/diff.js.map +0 -1
- package/dist/plugin/fingerprint.js.map +0 -1
- package/dist/plugin/host.js.map +0 -1
- package/dist/plugin/index.js.map +0 -1
- package/dist/plugin/inferability.js.map +0 -1
- package/dist/plugin/kevin_approve.js.map +0 -1
- package/dist/plugin/kevin_audit.js.map +0 -1
- package/dist/plugin/kevin_conflicts.js.map +0 -1
- package/dist/plugin/kevin_doctor.js.map +0 -1
- package/dist/plugin/kevin_facts.js.map +0 -1
- package/dist/plugin/kevin_native.js.map +0 -1
- package/dist/plugin/kevin_propose.js.map +0 -1
- package/dist/plugin/kevin_publish.js.map +0 -1
- package/dist/plugin/kevin_why.js.map +0 -1
- package/dist/plugin/memory-format.js.map +0 -1
- package/dist/plugin/metrics.js.map +0 -1
- package/dist/plugin/native.js.map +0 -1
- package/dist/plugin/okf-export.js.map +0 -1
- package/dist/plugin/okf-import.js.map +0 -1
- package/dist/plugin/okf.js.map +0 -1
- package/dist/plugin/query-tokenizer.js.map +0 -1
- package/dist/plugin/redact.js.map +0 -1
- package/dist/plugin/replay-types.js.map +0 -1
- package/dist/plugin/replay.js.map +0 -1
- package/dist/plugin/sqlite-adapter.js.map +0 -1
- package/dist/plugin/uuid.js.map +0 -1
- package/migrations/001_initial.sql +0 -92
- package/migrations/002_indexes.sql +0 -14
- package/migrations/003_v02_signal.sql +0 -58
- package/migrations/004_v03_knowledge.sql +0 -138
- package/migrations/005_v04_signal.sql +0 -57
- package/migrations/006_v05_glassbox.sql +0 -118
- package/migrations/007_v06_pull.sql +0 -145
- package/migrations/008_v07_truth.sql +0 -124
- package/migrations/009_v08_team.sql +0 -100
- package/migrations/010_v09_native.sql +0 -78
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { readOriginCallId } from "./MemoryService.js";
|
|
2
|
+
import { hasColumn } from "./columns.js";
|
|
3
|
+
import { toMs } from "./time-ms.js";
|
|
1
4
|
import { uuidv7 } from "./uuid.js";
|
|
2
5
|
export class InjectionLedger {
|
|
3
6
|
store;
|
|
@@ -11,11 +14,21 @@ export class InjectionLedger {
|
|
|
11
14
|
* duplicates are expected only via the caller's per-session seen-set.
|
|
12
15
|
*/
|
|
13
16
|
record(input) {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
// v1.1.0 (K11-003 / plan §5.2, D11-01) — dual-write injected_at_ms when column exists
|
|
18
|
+
if (hasColumn(this.store, "kevin_injections", "injected_at_ms")) {
|
|
19
|
+
this.store
|
|
20
|
+
.prepare(`INSERT INTO kevin_injections
|
|
21
|
+
(id, memory_id, fingerprint, session_id, hook, tokens, outcome, injected_at_ms)
|
|
22
|
+
VALUES (?, ?, ?, ?, ?, ?, 'unmeasured', ?)`)
|
|
23
|
+
.run(uuidv7(), input.memoryId, input.fingerprint, input.sessionId, input.hook, input.tokens, Date.now());
|
|
24
|
+
}
|
|
25
|
+
else {
|
|
26
|
+
this.store
|
|
27
|
+
.prepare(`INSERT INTO kevin_injections
|
|
28
|
+
(id, memory_id, fingerprint, session_id, hook, tokens, outcome)
|
|
29
|
+
VALUES (?, ?, ?, ?, ?, ?, 'unmeasured')`)
|
|
30
|
+
.run(uuidv7(), input.memoryId, input.fingerprint, input.sessionId, input.hook, input.tokens);
|
|
31
|
+
}
|
|
19
32
|
this.metrics?.incr("injections_total", 1);
|
|
20
33
|
// v0.8.0 (K8-024 / plan §5.7) — shared-layer consumption is counted
|
|
21
34
|
// separately so the audit can tell how much of the push channel
|
|
@@ -40,20 +53,24 @@ export class InjectionLedger {
|
|
|
40
53
|
* recent injection").
|
|
41
54
|
*/
|
|
42
55
|
settle(sessionId) {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
.
|
|
56
|
+
// v1.1.0 (K11-003 / plan §5.2, D11-01/D11-07) — ms-aware settle: readers
|
|
57
|
+
// prefer _ms and fall back to legacy string. Column probes are cached.
|
|
58
|
+
const hasInjMs = hasColumn(this.store, "kevin_injections", "injected_at_ms");
|
|
59
|
+
const hasToolMs = hasColumn(this.store, "tool_calls", "ts_ms");
|
|
60
|
+
const injections = (hasInjMs
|
|
61
|
+
? this.store.prepare(`SELECT id, memory_id, fingerprint, injected_at, injected_at_ms, outcome
|
|
62
|
+
FROM kevin_injections
|
|
63
|
+
WHERE session_id = ?
|
|
64
|
+
ORDER BY injected_at ASC, id ASC`)
|
|
65
|
+
: this.store.prepare(`SELECT id, memory_id, fingerprint, injected_at, outcome
|
|
66
|
+
FROM kevin_injections
|
|
67
|
+
WHERE session_id = ?
|
|
68
|
+
ORDER BY injected_at ASC, id ASC`)).all(sessionId);
|
|
49
69
|
for (const inj of injections) {
|
|
50
70
|
// Same identity dimension CausalChain uses: the failing call's
|
|
51
71
|
// `error_fingerprint` (stamped by Reflector) or the legacy
|
|
52
|
-
// `fingerprint` hash.
|
|
53
|
-
//
|
|
54
|
-
// COUNT (not LIMIT 1): every failing call after the injection
|
|
55
|
-
// is a recurrence — the charge must reach 3 so D4-06 expels
|
|
56
|
-
// the lesson.
|
|
72
|
+
// `fingerprint` hash.
|
|
73
|
+
// v1.1.0 — time comparison uses toMs helper (prefers _ms).
|
|
57
74
|
//
|
|
58
75
|
// BUG-003 — the exemption is now bounded to the lesson's OWN
|
|
59
76
|
// creating call (memories.metadata.origin_call_id, stamped by
|
|
@@ -65,17 +82,50 @@ export class InjectionLedger {
|
|
|
65
82
|
// precision. Memories without a tracked creating call (agent-
|
|
66
83
|
// saved, test fixtures) get no exemption: only the `ts >=
|
|
67
84
|
// injected_at` bound applies.
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
.
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
85
|
+
const metaRow = this.store
|
|
86
|
+
.prepare("SELECT metadata FROM memories WHERE id = ?")
|
|
87
|
+
.get(inj.memory_id);
|
|
88
|
+
const originCallId = readOriginCallId(metaRow?.metadata ?? null);
|
|
89
|
+
// v1.1.0 — heuristic: when legacy string and _ms diverge by >2s
|
|
90
|
+
// (manual UPDATE of injected_at in tests), trust the string
|
|
91
|
+
// because the ms reflects wall time at record, not the pinned
|
|
92
|
+
// fixture time. Real rows differ by <1s (second truncation).
|
|
93
|
+
const rawInjMs = inj.injected_at_ms ?? null;
|
|
94
|
+
const stringMs = inj.injected_at
|
|
95
|
+
? Date.parse(`${inj.injected_at.replace(" ", "T")}Z`)
|
|
96
|
+
: null;
|
|
97
|
+
const injectedMs = rawInjMs !== null &&
|
|
98
|
+
stringMs !== null &&
|
|
99
|
+
!Number.isNaN(stringMs) &&
|
|
100
|
+
Math.abs(rawInjMs - stringMs) > 2000
|
|
101
|
+
? stringMs
|
|
102
|
+
: toMs(inj.injected_at, rawInjMs);
|
|
103
|
+
// Fetch candidate failing calls for this fingerprint and filter by ms
|
|
104
|
+
const failRows = (hasToolMs
|
|
105
|
+
? this.store.prepare(`SELECT id, ts, ts_ms FROM tool_calls
|
|
106
|
+
WHERE session_id = ?
|
|
107
|
+
AND success = 0
|
|
108
|
+
AND COALESCE(error_fingerprint, fingerprint) = ?`)
|
|
109
|
+
: this.store.prepare(`SELECT id, ts FROM tool_calls
|
|
110
|
+
WHERE session_id = ?
|
|
111
|
+
AND success = 0
|
|
112
|
+
AND COALESCE(error_fingerprint, fingerprint) = ?`)).all(sessionId, inj.fingerprint);
|
|
113
|
+
let n = 0;
|
|
114
|
+
for (const r of failRows) {
|
|
115
|
+
if (originCallId !== null && r.id === originCallId)
|
|
116
|
+
continue;
|
|
117
|
+
const tsMs = toMs(r.ts, r.ts_ms ?? null);
|
|
118
|
+
if (injectedMs !== null && tsMs !== null) {
|
|
119
|
+
if (tsMs < injectedMs)
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
// fallback to string comparison when either side missing (legacy)
|
|
124
|
+
if (r.ts < inj.injected_at)
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
n++;
|
|
128
|
+
}
|
|
79
129
|
// v0.5.0 (K5-005 / plan §5.1, D5-01) — three-way settlement:
|
|
80
130
|
// recurrences >= 1 → ineffective (existing side effects unchanged)
|
|
81
131
|
// else fixes >= 1 → effective (a linked fix was OBSERVED)
|
|
@@ -97,7 +147,7 @@ export class InjectionLedger {
|
|
|
97
147
|
OR ? > last_injected_at
|
|
98
148
|
THEN ? ELSE last_injected_at END
|
|
99
149
|
WHERE fingerprint = ? AND id = ?`)
|
|
100
|
-
.run(
|
|
150
|
+
.run(n, inj.injected_at, inj.injected_at, inj.fingerprint, inj.memory_id);
|
|
101
151
|
// v0.4.0 (K4-025 / plan §5.1 rule 4, D4-06) — recurrence
|
|
102
152
|
// expels: a fingerprint at `recurrence_count >= 3` is
|
|
103
153
|
// demoted to `status='stale'` and never injected again
|
|
@@ -116,15 +166,31 @@ export class InjectionLedger {
|
|
|
116
166
|
// `ts >= injected_at` bound and `session_id = ?` filter are
|
|
117
167
|
// kept; there is no `origin_call_id` exemption for fixes —
|
|
118
168
|
// a fix is not the creating call.
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
169
|
+
// v1.1.0 — ms-aware: fetch and filter via toMs.
|
|
170
|
+
const fixCandidates = (hasToolMs
|
|
171
|
+
? this.store.prepare(`SELECT ts, ts_ms FROM tool_calls
|
|
172
|
+
WHERE session_id = ?
|
|
173
|
+
AND success = 1
|
|
174
|
+
AND fix_for_fingerprint = ?`)
|
|
175
|
+
: this.store.prepare(`SELECT ts FROM tool_calls
|
|
176
|
+
WHERE session_id = ?
|
|
177
|
+
AND success = 1
|
|
178
|
+
AND fix_for_fingerprint = ?`)).all(sessionId, inj.fingerprint);
|
|
179
|
+
let hasFix = false;
|
|
180
|
+
for (const fr of fixCandidates) {
|
|
181
|
+
const tsMs = toMs(fr.ts, fr.ts_ms ?? null);
|
|
182
|
+
if (injectedMs !== null && tsMs !== null) {
|
|
183
|
+
if (tsMs >= injectedMs) {
|
|
184
|
+
hasFix = true;
|
|
185
|
+
break;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
else if (fr.ts >= inj.injected_at) {
|
|
189
|
+
hasFix = true;
|
|
190
|
+
break;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
if (hasFix) {
|
|
128
194
|
this.store
|
|
129
195
|
.prepare(`UPDATE kevin_injections SET outcome = 'effective'
|
|
130
196
|
WHERE id = ?`)
|
|
@@ -226,24 +292,4 @@ export class InjectionLedger {
|
|
|
226
292
|
return out;
|
|
227
293
|
}
|
|
228
294
|
}
|
|
229
|
-
|
|
230
|
-
* BUG-003 — read `origin_call_id` (the failing tool_call that CREATED the
|
|
231
|
-
* memory) from memories.metadata, mirroring the feedback loop's
|
|
232
|
-
* `readOriginCallId` in MemoryService. Returns null when absent/malformed.
|
|
233
|
-
*/
|
|
234
|
-
function readOriginCallId(store, memoryId) {
|
|
235
|
-
const row = store
|
|
236
|
-
.prepare("SELECT metadata FROM memories WHERE id = ?")
|
|
237
|
-
.get(memoryId);
|
|
238
|
-
if (!row?.metadata)
|
|
239
|
-
return null;
|
|
240
|
-
try {
|
|
241
|
-
const parsed = JSON.parse(row.metadata);
|
|
242
|
-
const id = parsed?.origin_call_id;
|
|
243
|
-
return typeof id === "string" && id.length > 0 ? id : null;
|
|
244
|
-
}
|
|
245
|
-
catch {
|
|
246
|
-
return null;
|
|
247
|
-
}
|
|
248
|
-
}
|
|
249
|
-
//# sourceMappingURL=InjectionLedger.js.map
|
|
295
|
+
// v1.1.0 — readOriginCallId deduplicated: imported from MemoryService (K11-003/K11-013)
|
|
@@ -2,100 +2,13 @@ import { homedir } from "node:os";
|
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { firstSentence } from "./Curator.js";
|
|
4
4
|
import { normalize } from "./fingerprint.js";
|
|
5
|
+
import { STOP_WORDS } from "./query-tokenizer.js";
|
|
5
6
|
export const SKILL_TOPIC = "project-knowledge";
|
|
6
7
|
/** Hash-like noise: FNV-1a output is 16 lowercase hex chars; an 8+ hex
|
|
7
8
|
* token is indistinguishable from a fingerprint prefix and must never
|
|
8
9
|
* become a topic (D6-14). */
|
|
9
10
|
const HEX_LIKE_RE = /^[0-9a-f]{8,}$/;
|
|
10
11
|
const TOKEN_RE = /[^a-z0-9]+/;
|
|
11
|
-
/** Conservative English function words; code-adjacent meaning-bearing
|
|
12
|
-
* words (npm, ts2304, cargo...) are deliberately NOT stop-words. */
|
|
13
|
-
const STOP_WORDS = new Set([
|
|
14
|
-
"a",
|
|
15
|
-
"about",
|
|
16
|
-
"after",
|
|
17
|
-
"again",
|
|
18
|
-
"all",
|
|
19
|
-
"also",
|
|
20
|
-
"an",
|
|
21
|
-
"and",
|
|
22
|
-
"any",
|
|
23
|
-
"are",
|
|
24
|
-
"as",
|
|
25
|
-
"at",
|
|
26
|
-
"be",
|
|
27
|
-
"been",
|
|
28
|
-
"before",
|
|
29
|
-
"being",
|
|
30
|
-
"but",
|
|
31
|
-
"by",
|
|
32
|
-
"can",
|
|
33
|
-
"could",
|
|
34
|
-
"did",
|
|
35
|
-
"do",
|
|
36
|
-
"does",
|
|
37
|
-
"for",
|
|
38
|
-
"from",
|
|
39
|
-
"had",
|
|
40
|
-
"has",
|
|
41
|
-
"have",
|
|
42
|
-
"he",
|
|
43
|
-
"her",
|
|
44
|
-
"his",
|
|
45
|
-
"if",
|
|
46
|
-
"in",
|
|
47
|
-
"into",
|
|
48
|
-
"is",
|
|
49
|
-
"it",
|
|
50
|
-
"its",
|
|
51
|
-
"may",
|
|
52
|
-
"might",
|
|
53
|
-
"more",
|
|
54
|
-
"most",
|
|
55
|
-
"must",
|
|
56
|
-
"not",
|
|
57
|
-
"of",
|
|
58
|
-
"on",
|
|
59
|
-
"one",
|
|
60
|
-
"or",
|
|
61
|
-
"our",
|
|
62
|
-
"per",
|
|
63
|
-
"shall",
|
|
64
|
-
"she",
|
|
65
|
-
"so",
|
|
66
|
-
"than",
|
|
67
|
-
"that",
|
|
68
|
-
"the",
|
|
69
|
-
"their",
|
|
70
|
-
"them",
|
|
71
|
-
"then",
|
|
72
|
-
"there",
|
|
73
|
-
"these",
|
|
74
|
-
"they",
|
|
75
|
-
"this",
|
|
76
|
-
"those",
|
|
77
|
-
"through",
|
|
78
|
-
"to",
|
|
79
|
-
"too",
|
|
80
|
-
"under",
|
|
81
|
-
"up",
|
|
82
|
-
"us",
|
|
83
|
-
"via",
|
|
84
|
-
"was",
|
|
85
|
-
"we",
|
|
86
|
-
"were",
|
|
87
|
-
"what",
|
|
88
|
-
"when",
|
|
89
|
-
"where",
|
|
90
|
-
"which",
|
|
91
|
-
"while",
|
|
92
|
-
"who",
|
|
93
|
-
"will",
|
|
94
|
-
"with",
|
|
95
|
-
"would",
|
|
96
|
-
"you",
|
|
97
|
-
"your",
|
|
98
|
-
]);
|
|
99
12
|
/** Sanitize a memory type into a filesystem-safe topic prefix. */
|
|
100
13
|
function sanitizeType(type) {
|
|
101
14
|
const cleaned = type
|
|
@@ -266,4 +179,3 @@ export class Materializer {
|
|
|
266
179
|
}));
|
|
267
180
|
}
|
|
268
181
|
}
|
|
269
|
-
//# sourceMappingURL=Materializer.js.map
|
|
@@ -160,8 +160,55 @@ export interface GetRelevantInput {
|
|
|
160
160
|
*/
|
|
161
161
|
now?: Date;
|
|
162
162
|
}
|
|
163
|
+
interface MemoryRow {
|
|
164
|
+
id: string;
|
|
165
|
+
type: MemoryType;
|
|
166
|
+
content: string;
|
|
167
|
+
scope: MemoryScope;
|
|
168
|
+
relevance_score: number;
|
|
169
|
+
source_tool: string | null;
|
|
170
|
+
source_session: string | null;
|
|
171
|
+
metadata: string | null;
|
|
172
|
+
created_at: string;
|
|
173
|
+
updated_at: string;
|
|
174
|
+
expires_at: string | null;
|
|
175
|
+
/** v0.2.0 columns — nullable for rows from pre-003 DBs. */
|
|
176
|
+
project_id?: string | null;
|
|
177
|
+
fingerprint?: string | null;
|
|
178
|
+
origin?: MemoryOrigin | null;
|
|
179
|
+
/** v0.3.0 */
|
|
180
|
+
evidence_count?: number;
|
|
181
|
+
last_verified_at?: string | null;
|
|
182
|
+
status?: string;
|
|
183
|
+
/** v0.4.0 */
|
|
184
|
+
recurrence_count?: number;
|
|
185
|
+
/** v0.4.0 */
|
|
186
|
+
fix_args?: string | null;
|
|
187
|
+
/** v0.5.0 (K5-009) */
|
|
188
|
+
ignored?: number;
|
|
189
|
+
/** v0.5.0 (K5-009) */
|
|
190
|
+
superseded_by?: string | null;
|
|
191
|
+
/** v0.5.0 (K5-010) */
|
|
192
|
+
feedback_positive?: number;
|
|
193
|
+
/** v0.5.0 (K5-010) */
|
|
194
|
+
feedback_negative?: number;
|
|
195
|
+
/** v0.6.0 (K6-011 / migration 007) */
|
|
196
|
+
curated?: number;
|
|
197
|
+
/** v0.6.0 (K6-011 / migration 007) */
|
|
198
|
+
curated_at?: string | null;
|
|
199
|
+
/** v0.6.0 (K6-011 / migration 007) — 1 = inferable, 0 = non_inferable,
|
|
200
|
+
* NULL = unknown. */
|
|
201
|
+
inferable?: number | null;
|
|
202
|
+
/** v0.7.0 (K7-008 / migration 008) — de-ranking penalty in [0, 0.5]. */
|
|
203
|
+
truth_penalty?: number | null;
|
|
204
|
+
/** v0.7.0 (K7-008 / migration 008) — first contradiction timestamp. */
|
|
205
|
+
contradicted_at?: string | null;
|
|
206
|
+
/** v0.8.0 (K8-018 / migration 009) — layer marker on the row. */
|
|
207
|
+
layer?: string | null;
|
|
208
|
+
}
|
|
163
209
|
export declare const DATE_NOW = "2099-01-01T00:00:00.000Z";
|
|
164
210
|
export declare function hasRepoIdColumn(store: Store): boolean;
|
|
211
|
+
export declare function mapRow(row: MemoryRow, score?: number): Memory;
|
|
165
212
|
export declare class MemoryService {
|
|
166
213
|
private readonly metrics;
|
|
167
214
|
constructor(store: Store, metrics?: Metrics | null, repoId?: string | null);
|
|
@@ -169,7 +216,6 @@ export declare class MemoryService {
|
|
|
169
216
|
private repoId;
|
|
170
217
|
setRepoId(repoId: string | null): void;
|
|
171
218
|
private hasRecurrenceColumn;
|
|
172
|
-
private _hasRecurrenceColumn;
|
|
173
219
|
private hasIgnoredColumn;
|
|
174
220
|
private hasCuratedColumn;
|
|
175
221
|
private hasTruthColumns;
|
|
@@ -290,6 +336,17 @@ export declare class MemoryService {
|
|
|
290
336
|
countSupersedeCandidates(type: MemoryType, fingerprint: string | null | undefined, projectId: string | null): number;
|
|
291
337
|
penalizeRecurringReflectors(sessionId: string): number;
|
|
292
338
|
}
|
|
339
|
+
/**
|
|
340
|
+
* v0.3.0 fix — Extract `origin_call_id` from the memory metadata blob.
|
|
341
|
+
*
|
|
342
|
+
* Reflector stores the failing tool_call id in metadata.origin_call_id
|
|
343
|
+
* (when available) so the feedback loop can exclude the original call
|
|
344
|
+
* from the recurrence count. Returns null when metadata is absent,
|
|
345
|
+
* malformed, or lacks the field.
|
|
346
|
+
* // v1.1.0 (K11-003 / plan §5.5, D11-05) — single source for origin lookup;
|
|
347
|
+
* // InjectionLedger reuses this implementation (K11-013).
|
|
348
|
+
*/
|
|
349
|
+
export declare function readOriginCallId(metadata: string | null): string | null;
|
|
293
350
|
/**
|
|
294
351
|
* v0.3.0 fix — Count active memories that would be superseded by a new
|
|
295
352
|
* row with the given (type, fingerprint, projectId) tuple. Used by
|
|
@@ -300,3 +357,4 @@ export declare class MemoryService {
|
|
|
300
357
|
* any other type.
|
|
301
358
|
*/
|
|
302
359
|
export declare function countSupersedeCandidates(store: Store, type: MemoryType, fingerprint: string | null | undefined, projectId: string | null): number;
|
|
360
|
+
export {};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { deterministicFixLine } from "./LessonFixer.js";
|
|
2
|
+
import { hasCuratedColumn as columnsHasCurated, hasIgnoredColumn as columnsHasIgnored, hasLayerColumn as columnsHasLayer, hasRecurrenceColumn as columnsHasRecurrence, hasRepoIdColumn as columnsHasRepoId, hasTruthColumns as columnsHasTruth, } from "./columns.js";
|
|
2
3
|
import { computeConfidence } from "./confidence.js";
|
|
3
4
|
import { fingerprint as computeFingerprint } from "./fingerprint.js";
|
|
4
5
|
import { classify } from "./inferability.js";
|
|
@@ -82,106 +83,21 @@ const MEMORY_ROW_SELECT = `id, type, content, scope, relevance_score, source_too
|
|
|
82
83
|
metadata, created_at, updated_at, expires_at,
|
|
83
84
|
project_id, fingerprint, origin,
|
|
84
85
|
evidence_count, recurrence_count, last_verified_at, status, fix_args`;
|
|
85
|
-
|
|
86
|
-
* v0.5.0 (K5-008/009 / plan §5.6) — cached per-store probe for the 006-only
|
|
87
|
-
* `ignored` column (pre-006 DBs must not reference it). Shared by the
|
|
88
|
-
* retrieval filter, the row SELECT, and the save() ignored stamp.
|
|
89
|
-
* v0.6.0 (K6-001a) — positive-only caching: a successful probe is cached,
|
|
90
|
-
* a failed probe is NOT. A Store migrated in place heals on the next call.
|
|
91
|
-
*/
|
|
92
|
-
const ignoredColumnCache = new WeakMap();
|
|
86
|
+
// v1.1.0 (K11-011 / plan §5.5, D11-06) — probes delegate to columns registry
|
|
93
87
|
function hasIgnoredColumn(store) {
|
|
94
|
-
|
|
95
|
-
if (cached === true)
|
|
96
|
-
return true;
|
|
97
|
-
try {
|
|
98
|
-
store.prepare("SELECT ignored FROM memories LIMIT 1").get();
|
|
99
|
-
ignoredColumnCache.set(store, true);
|
|
100
|
-
return true;
|
|
101
|
-
}
|
|
102
|
-
catch {
|
|
103
|
-
return false;
|
|
104
|
-
}
|
|
88
|
+
return columnsHasIgnored(store);
|
|
105
89
|
}
|
|
106
|
-
/**
|
|
107
|
-
* v0.6.0 (K6-011 / plan §5.4) — cached per-store probe for the 007-only
|
|
108
|
-
* `curated` column (pre-007 DBs must not reference it). Same positive-only
|
|
109
|
-
* caching discipline as `ignored` (K6-001a): a successful probe is cached,
|
|
110
|
-
* a failed probe is NOT.
|
|
111
|
-
*/
|
|
112
|
-
const curatedColumnCache = new WeakMap();
|
|
113
90
|
function hasCuratedColumn(store) {
|
|
114
|
-
|
|
115
|
-
if (cached === true)
|
|
116
|
-
return true;
|
|
117
|
-
try {
|
|
118
|
-
store.prepare("SELECT curated FROM memories LIMIT 1").get();
|
|
119
|
-
curatedColumnCache.set(store, true);
|
|
120
|
-
return true;
|
|
121
|
-
}
|
|
122
|
-
catch {
|
|
123
|
-
return false;
|
|
124
|
-
}
|
|
91
|
+
return columnsHasCurated(store);
|
|
125
92
|
}
|
|
126
|
-
/**
|
|
127
|
-
* v0.7.0 (K7-008 / migration 008) — cached per-store probe for the 008-only
|
|
128
|
-
* `truth_penalty` column (pre-008 DBs must not reference it). Same
|
|
129
|
-
* positive-only caching discipline as `curated` (K6-001a).
|
|
130
|
-
*/
|
|
131
|
-
const truthColumnsCache = new WeakMap();
|
|
132
93
|
function hasTruthColumns(store) {
|
|
133
|
-
|
|
134
|
-
if (cached === true)
|
|
135
|
-
return true;
|
|
136
|
-
try {
|
|
137
|
-
store.prepare("SELECT truth_penalty FROM memories LIMIT 1").get();
|
|
138
|
-
truthColumnsCache.set(store, true);
|
|
139
|
-
return true;
|
|
140
|
-
}
|
|
141
|
-
catch {
|
|
142
|
-
return false;
|
|
143
|
-
}
|
|
94
|
+
return columnsHasTruth(store);
|
|
144
95
|
}
|
|
145
|
-
/**
|
|
146
|
-
* v0.8.0 (K8-007 / plan §5.7) — cached per-store probe for the 009-only
|
|
147
|
-
* `repo_id` column (pre-009 DBs must not reference it). Same positive-only
|
|
148
|
-
* caching discipline as `ignored` (K6-001a): a successful probe is cached,
|
|
149
|
-
* a failed probe is NOT. Exported so `kevin_audit`'s rollups scope on the
|
|
150
|
-
* same column.
|
|
151
|
-
*/
|
|
152
|
-
const repoIdColumnCache = new WeakMap();
|
|
153
96
|
export function hasRepoIdColumn(store) {
|
|
154
|
-
|
|
155
|
-
if (cached === true)
|
|
156
|
-
return true;
|
|
157
|
-
try {
|
|
158
|
-
store.prepare("SELECT repo_id FROM memories LIMIT 1").get();
|
|
159
|
-
repoIdColumnCache.set(store, true);
|
|
160
|
-
return true;
|
|
161
|
-
}
|
|
162
|
-
catch {
|
|
163
|
-
return false;
|
|
164
|
-
}
|
|
97
|
+
return columnsHasRepoId(store);
|
|
165
98
|
}
|
|
166
|
-
/**
|
|
167
|
-
* v0.8.0 (K8-018 / plan §5.2) — cached per-store probe for the 009-only
|
|
168
|
-
* `layer` column (pre-009 DBs must not reference it). Same positive-only
|
|
169
|
-
* caching discipline: pre-009 databases have no shared layer, so the
|
|
170
|
-
* immutability refusal in `update()` is skipped there entirely.
|
|
171
|
-
*/
|
|
172
|
-
const layerColumnCache = new WeakMap();
|
|
173
99
|
function hasLayerColumn(store) {
|
|
174
|
-
|
|
175
|
-
if (cached === true)
|
|
176
|
-
return true;
|
|
177
|
-
try {
|
|
178
|
-
store.prepare("SELECT layer FROM memories LIMIT 1").get();
|
|
179
|
-
layerColumnCache.set(store, true);
|
|
180
|
-
return true;
|
|
181
|
-
}
|
|
182
|
-
catch {
|
|
183
|
-
return false;
|
|
184
|
-
}
|
|
100
|
+
return columnsHasLayer(store);
|
|
185
101
|
}
|
|
186
102
|
/**
|
|
187
103
|
* v0.5.0 (K5-009 / plan §5.3) — the 006-only columns are appended when the
|
|
@@ -204,7 +120,7 @@ function rowSelect(store) {
|
|
|
204
120
|
: withCurated;
|
|
205
121
|
return hasLayerColumn(store) ? `${withTruth}, layer` : withTruth;
|
|
206
122
|
}
|
|
207
|
-
function mapRow(row, score) {
|
|
123
|
+
export function mapRow(row, score) {
|
|
208
124
|
const mem = {
|
|
209
125
|
id: row.id,
|
|
210
126
|
type: row.type,
|
|
@@ -320,21 +236,10 @@ export class MemoryService {
|
|
|
320
236
|
}
|
|
321
237
|
// v0.4.0 (BUG-008) — cached column probe for pre-005 DBs (which lack
|
|
322
238
|
// `recurrence_count`); save() must not reference the column there.
|
|
239
|
+
// v1.1.0 (K11-011) — delegates to columns registry
|
|
323
240
|
hasRecurrenceColumn() {
|
|
324
|
-
|
|
325
|
-
try {
|
|
326
|
-
this.store
|
|
327
|
-
.prepare("SELECT recurrence_count FROM memories LIMIT 1")
|
|
328
|
-
.get();
|
|
329
|
-
this._hasRecurrenceColumn = true;
|
|
330
|
-
}
|
|
331
|
-
catch {
|
|
332
|
-
this._hasRecurrenceColumn = false;
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
return this._hasRecurrenceColumn;
|
|
241
|
+
return columnsHasRecurrence(this.store);
|
|
336
242
|
}
|
|
337
|
-
_hasRecurrenceColumn;
|
|
338
243
|
// v0.5.0 (K5-008 / plan §5.6) — cached column probe for pre-006 DBs
|
|
339
244
|
// (which lack `ignored`); the retrieval filter must not reference the
|
|
340
245
|
// column there.
|
|
@@ -1246,8 +1151,10 @@ function originBoost(mem) {
|
|
|
1246
1151
|
* (when available) so the feedback loop can exclude the original call
|
|
1247
1152
|
* from the recurrence count. Returns null when metadata is absent,
|
|
1248
1153
|
* malformed, or lacks the field.
|
|
1154
|
+
* // v1.1.0 (K11-003 / plan §5.5, D11-05) — single source for origin lookup;
|
|
1155
|
+
* // InjectionLedger reuses this implementation (K11-013).
|
|
1249
1156
|
*/
|
|
1250
|
-
function readOriginCallId(metadata) {
|
|
1157
|
+
export function readOriginCallId(metadata) {
|
|
1251
1158
|
if (!metadata)
|
|
1252
1159
|
return null;
|
|
1253
1160
|
try {
|
|
@@ -1333,4 +1240,3 @@ function rankCompare(a, b, nowMs, deterministic) {
|
|
|
1333
1240
|
}
|
|
1334
1241
|
return sqliteUtcToMs(b.createdAt) - sqliteUtcToMs(a.createdAt); // newer first
|
|
1335
1242
|
}
|
|
1336
|
-
//# sourceMappingURL=MemoryService.js.map
|
package/dist/plugin/Migrate.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ export interface MigrateResult {
|
|
|
5
5
|
applied: string[];
|
|
6
6
|
}
|
|
7
7
|
export type PostApplyHook = (store: Store) => void;
|
|
8
|
-
export declare const HOOK_NAMES: readonly ["tool.execute.before", "tool.execute.after", "chat.message", "experimental.chat.system.transform", "experimental.session.compacting", "event"];
|
|
8
|
+
export declare const HOOK_NAMES: readonly ["tool.execute.before", "tool.execute.after", "chat.message", "experimental.chat.system.transform", "experimental.session.compacting", "event", "dispose"];
|
|
9
9
|
export declare class Migrate {
|
|
10
10
|
private store;
|
|
11
11
|
private migrationsDir;
|
package/dist/plugin/Migrate.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { readFileSync, readdirSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
// v0.9.0 (K9-001 / plan §5.3, D9-08) — the six hooks Kevin registers, in
|
|
4
|
-
// hook-object order
|
|
4
|
+
// hook-object order, plus `dispose` (v1.0.0 K10-013): the seventh
|
|
5
|
+
// instrumented hook. Canonical list: the "010" post-apply hook seeds
|
|
5
6
|
// hook_liveness from it, and HookLiveness (K9-009) wraps exactly these
|
|
6
7
|
// names, so a future hook added here is visible in the table on the next
|
|
7
8
|
// migration run.
|
|
@@ -12,6 +13,7 @@ export const HOOK_NAMES = [
|
|
|
12
13
|
"experimental.chat.system.transform",
|
|
13
14
|
"experimental.session.compacting",
|
|
14
15
|
"event",
|
|
16
|
+
"dispose",
|
|
15
17
|
];
|
|
16
18
|
// Built-in post-apply hooks, keyed by migration version. Each hook runs inside
|
|
17
19
|
// the same transaction as the migration's DDL, so a hook failure rolls back the
|
|
@@ -163,6 +165,24 @@ const DEFAULT_POST_APPLY_HOOKS = {
|
|
|
163
165
|
.prepare("UPDATE hook_liveness SET experimental = CASE WHEN hook LIKE 'experimental.%' THEN 1 ELSE 0 END")
|
|
164
166
|
.run();
|
|
165
167
|
},
|
|
168
|
+
// v1.0.0 (K10-005 / plan §6.1) — Proven: four operations, all idempotent.
|
|
169
|
+
// 1. Seed dispose row defensively.
|
|
170
|
+
// 2-3. Re-derive perf_budget_breaches and bench_runs_total.
|
|
171
|
+
// 4. Normalise NULL within_budget to 1.
|
|
172
|
+
"011": (store) => {
|
|
173
|
+
store
|
|
174
|
+
.prepare("INSERT OR IGNORE INTO hook_liveness (hook) VALUES ('dispose')")
|
|
175
|
+
.run();
|
|
176
|
+
store
|
|
177
|
+
.prepare("UPDATE kevin_metrics SET value = (SELECT COUNT(*) FROM perf_samples WHERE within_budget = 0) WHERE key = 'perf_budget_breaches'")
|
|
178
|
+
.run();
|
|
179
|
+
store
|
|
180
|
+
.prepare("UPDATE kevin_metrics SET value = (SELECT COUNT(*) FROM bench_runs) WHERE key = 'bench_runs_total'")
|
|
181
|
+
.run();
|
|
182
|
+
store
|
|
183
|
+
.prepare("UPDATE perf_samples SET within_budget = 1 WHERE within_budget IS NULL")
|
|
184
|
+
.run();
|
|
185
|
+
},
|
|
166
186
|
};
|
|
167
187
|
export class Migrate {
|
|
168
188
|
store;
|
|
@@ -200,7 +220,10 @@ export class Migrate {
|
|
|
200
220
|
// updates and re-derivation, so a no-op startup can heal a
|
|
201
221
|
// crash that landed between the DDL and the hook without
|
|
202
222
|
// re-applying DDL.
|
|
203
|
-
if (from === "008" ||
|
|
223
|
+
if (from === "008" ||
|
|
224
|
+
from === "009" ||
|
|
225
|
+
from === "010" ||
|
|
226
|
+
from === "011") {
|
|
204
227
|
const repairHook = this.postApplyHooks.get(from);
|
|
205
228
|
if (repairHook)
|
|
206
229
|
this.store.transaction(() => repairHook(this.store));
|
|
@@ -224,6 +247,11 @@ export class Migrate {
|
|
|
224
247
|
applied: pending.map((m) => m.version),
|
|
225
248
|
};
|
|
226
249
|
}
|
|
250
|
+
// v1.1.0 (K11-015) — lexicographic ordering is valid through "999" because
|
|
251
|
+
// versions are zero-padded 3-digit strings ("001" … "999"). Any future
|
|
252
|
+
// migration beyond 999 must use a 4-digit prefix and this comparison must
|
|
253
|
+
// become numeric (parseInt). Until then, string > works and keeps the
|
|
254
|
+
// migration idempotency simple (plan §5.5, D11-??).
|
|
227
255
|
listPending(current) {
|
|
228
256
|
let files = [];
|
|
229
257
|
try {
|
|
@@ -241,4 +269,3 @@ export class Migrate {
|
|
|
241
269
|
.filter((m) => m !== null && m.version > current);
|
|
242
270
|
}
|
|
243
271
|
}
|
|
244
|
-
//# sourceMappingURL=Migrate.js.map
|
package/dist/plugin/Reflector.js
CHANGED