@jmtrin/opencode-kevin 0.2.0 → 0.4.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 +108 -35
- package/dist/migrations/004_v03_knowledge.sql +138 -0
- package/dist/migrations/005_v04_signal.sql +57 -0
- package/dist/plugin/CausalChain.d.ts +22 -0
- package/dist/plugin/CausalChain.js +179 -0
- package/dist/plugin/CausalChain.js.map +1 -0
- package/dist/plugin/ContextInjector.d.ts +89 -1
- package/dist/plugin/ContextInjector.js +276 -71
- package/dist/plugin/ContextInjector.js.map +1 -1
- package/dist/plugin/InjectionLedger.d.ts +85 -0
- package/dist/plugin/InjectionLedger.js +189 -0
- package/dist/plugin/InjectionLedger.js.map +1 -0
- package/dist/plugin/LessonFixer.d.ts +44 -0
- package/dist/plugin/LessonFixer.js +46 -0
- package/dist/plugin/LessonFixer.js.map +1 -0
- package/dist/plugin/MemoryService.d.ts +123 -2
- package/dist/plugin/MemoryService.js +439 -31
- package/dist/plugin/MemoryService.js.map +1 -1
- package/dist/plugin/Migrate.js +22 -0
- package/dist/plugin/Migrate.js.map +1 -1
- package/dist/plugin/QualityGate.d.ts +71 -0
- package/dist/plugin/QualityGate.js +78 -0
- package/dist/plugin/QualityGate.js.map +1 -0
- package/dist/plugin/Reflector.d.ts +33 -0
- package/dist/plugin/Reflector.js +126 -32
- package/dist/plugin/Reflector.js.map +1 -1
- package/dist/plugin/Retrospective.js +10 -1
- package/dist/plugin/Retrospective.js.map +1 -1
- package/dist/plugin/ToolCallObserver.d.ts +1 -0
- package/dist/plugin/ToolCallObserver.js +16 -9
- package/dist/plugin/ToolCallObserver.js.map +1 -1
- package/dist/plugin/confidence.d.ts +6 -0
- package/dist/plugin/confidence.js +23 -0
- package/dist/plugin/confidence.js.map +1 -0
- package/dist/plugin/index.d.ts +5 -0
- package/dist/plugin/index.js +336 -44
- package/dist/plugin/index.js.map +1 -1
- package/dist/plugin/kevin_why.d.ts +23 -0
- package/dist/plugin/kevin_why.js +108 -0
- package/dist/plugin/kevin_why.js.map +1 -0
- package/dist/plugin/memory-format.d.ts +12 -0
- package/dist/plugin/memory-format.js +45 -5
- package/dist/plugin/memory-format.js.map +1 -1
- package/dist/plugin/metrics.d.ts +7 -1
- package/dist/plugin/metrics.js +19 -0
- package/dist/plugin/metrics.js.map +1 -1
- package/dist/plugin/okf-export.d.ts +3 -0
- package/dist/plugin/okf-export.js +127 -0
- package/dist/plugin/okf-export.js.map +1 -0
- package/dist/plugin/okf-import.d.ts +76 -0
- package/dist/plugin/okf-import.js +272 -0
- package/dist/plugin/okf-import.js.map +1 -0
- package/dist/plugin/query-tokenizer.d.ts +13 -0
- package/dist/plugin/query-tokenizer.js +86 -0
- package/dist/plugin/query-tokenizer.js.map +1 -0
- package/migrations/004_v03_knowledge.sql +138 -0
- package/migrations/005_v04_signal.sql +57 -0
- package/package.json +1 -1
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import { uuidv7 } from "./uuid.js";
|
|
2
|
+
export class InjectionLedger {
|
|
3
|
+
store;
|
|
4
|
+
metrics;
|
|
5
|
+
constructor(store, metrics) {
|
|
6
|
+
this.store = store;
|
|
7
|
+
this.metrics = metrics ?? null;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Records one injected memory. Idempotent at the row level (UUID PK);
|
|
11
|
+
* duplicates are expected only via the caller's per-session seen-set.
|
|
12
|
+
*/
|
|
13
|
+
record(input) {
|
|
14
|
+
this.store
|
|
15
|
+
.prepare(`INSERT INTO kevin_injections
|
|
16
|
+
(id, memory_id, fingerprint, session_id, hook, tokens, outcome)
|
|
17
|
+
VALUES (?, ?, ?, ?, ?, ?, 'unmeasured')`)
|
|
18
|
+
.run(uuidv7(), input.memoryId, input.fingerprint, input.sessionId, input.hook, input.tokens);
|
|
19
|
+
this.metrics?.incr("injections_total", 1);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Settles every unmeasured injection of the session: a fingerprint that
|
|
23
|
+
* failed again (as a failing tool_call) after the injection is
|
|
24
|
+
* `ineffective`, otherwise `effective`. Idempotent — only
|
|
25
|
+
* `outcome = 'unmeasured'` rows are flipped, and the recurrence charge
|
|
26
|
+
* is `MAX(recurrence_count, n)` where n = all failing calls of the
|
|
27
|
+
* fingerprint after `injected_at` (a later idle re-computes n and
|
|
28
|
+
* catches up — plan §5.1 rule 4: 3 recurrences → stale).
|
|
29
|
+
*
|
|
30
|
+
* An ineffective injection also bumps the target memory's
|
|
31
|
+
* `recurrence_count` (negative evidence, plan §5.3) and stamps
|
|
32
|
+
* `last_injected_at`.
|
|
33
|
+
*/
|
|
34
|
+
settle(sessionId) {
|
|
35
|
+
const injections = this.store
|
|
36
|
+
.prepare(`SELECT id, memory_id, fingerprint, injected_at, outcome
|
|
37
|
+
FROM kevin_injections
|
|
38
|
+
WHERE session_id = ?`)
|
|
39
|
+
.all(sessionId);
|
|
40
|
+
for (const inj of injections) {
|
|
41
|
+
// Same identity dimension CausalChain uses: the failing call's
|
|
42
|
+
// `error_fingerprint` (stamped by Reflector) or the legacy
|
|
43
|
+
// `fingerprint` hash. `ts` and `injected_at` are both
|
|
44
|
+
// `datetime('now')` text → lexicographic comparison is valid.
|
|
45
|
+
// COUNT (not LIMIT 1): every failing call after the injection
|
|
46
|
+
// is a recurrence — the charge must reach 3 so D4-06 expels
|
|
47
|
+
// the lesson.
|
|
48
|
+
//
|
|
49
|
+
// BUG-003 — the exemption is now bounded to the lesson's OWN
|
|
50
|
+
// creating call (memories.metadata.origin_call_id, stamped by
|
|
51
|
+
// Reflector). The old code excluded the session's FIRST failing
|
|
52
|
+
// call of the fingerprint, which is only the creating call when
|
|
53
|
+
// the lesson was born in THIS session; a lesson created in an
|
|
54
|
+
// earlier session had its first in-session failure (a genuine
|
|
55
|
+
// post-injection recurrence) wrongly exempted, inflating
|
|
56
|
+
// precision. Memories without a tracked creating call (agent-
|
|
57
|
+
// saved, test fixtures) get no exemption: only the `ts >=
|
|
58
|
+
// injected_at` bound applies.
|
|
59
|
+
const originCallId = readOriginCallId(this.store, inj.memory_id);
|
|
60
|
+
const countRow = this.store
|
|
61
|
+
.prepare(`SELECT COUNT(*) AS n FROM tool_calls
|
|
62
|
+
WHERE session_id = ?
|
|
63
|
+
AND success = 0
|
|
64
|
+
AND COALESCE(error_fingerprint, fingerprint) = ?
|
|
65
|
+
AND ts >= ?
|
|
66
|
+
AND (? IS NULL OR id != ?)
|
|
67
|
+
LIMIT 1`)
|
|
68
|
+
.get(sessionId, inj.fingerprint, inj.injected_at, originCallId, originCallId);
|
|
69
|
+
const n = countRow.n;
|
|
70
|
+
if (n >= 1) {
|
|
71
|
+
if (inj.outcome === "unmeasured") {
|
|
72
|
+
this.store
|
|
73
|
+
.prepare(`UPDATE kevin_injections SET outcome = 'ineffective'
|
|
74
|
+
WHERE id = ?`)
|
|
75
|
+
.run(inj.id);
|
|
76
|
+
this.metrics?.incr("injections_ineffective", 1);
|
|
77
|
+
}
|
|
78
|
+
this.store
|
|
79
|
+
.prepare(`UPDATE memories
|
|
80
|
+
SET recurrence_count = MAX(recurrence_count, ?),
|
|
81
|
+
last_injected_at = ?
|
|
82
|
+
WHERE fingerprint = ? AND id = ?`)
|
|
83
|
+
.run(countRow.n, inj.injected_at, inj.fingerprint, inj.memory_id);
|
|
84
|
+
// v0.4.0 (K4-025 / plan §5.1 rule 4, D4-06) — recurrence
|
|
85
|
+
// expels: a fingerprint at `recurrence_count >= 3` is
|
|
86
|
+
// demoted to `status='stale'` and never injected again
|
|
87
|
+
// (only a new causal pattern — from a linked fix —
|
|
88
|
+
// re-admits the lesson, not the stale error row).
|
|
89
|
+
this.store
|
|
90
|
+
.prepare(`UPDATE memories SET status = 'stale'
|
|
91
|
+
WHERE id = ? AND recurrence_count >= 3`)
|
|
92
|
+
.run(inj.memory_id);
|
|
93
|
+
}
|
|
94
|
+
else if (inj.outcome === "unmeasured") {
|
|
95
|
+
this.store
|
|
96
|
+
.prepare(`UPDATE kevin_injections SET outcome = 'effective'
|
|
97
|
+
WHERE id = ?`)
|
|
98
|
+
.run(inj.id);
|
|
99
|
+
this.metrics?.incr("injections_effective", 1);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Per-fingerprint failing tool-call counts for the session. Feeds
|
|
105
|
+
* QualityGate.canInject and the HITL suggestion block.
|
|
106
|
+
*/
|
|
107
|
+
recurrencesFor(sessionId) {
|
|
108
|
+
const rows = this.store
|
|
109
|
+
.prepare(`SELECT COALESCE(error_fingerprint, fingerprint) AS fp
|
|
110
|
+
FROM tool_calls
|
|
111
|
+
WHERE session_id = ? AND success = 0
|
|
112
|
+
AND (error_fingerprint IS NOT NULL OR fingerprint IS NOT NULL)`)
|
|
113
|
+
.all(sessionId);
|
|
114
|
+
const out = new Map();
|
|
115
|
+
for (const r of rows) {
|
|
116
|
+
if (!r.fp)
|
|
117
|
+
continue;
|
|
118
|
+
out.set(r.fp, (out.get(r.fp) ?? 0) + 1);
|
|
119
|
+
}
|
|
120
|
+
return out;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* v0.4.0 (K4-017) — recurrence counts for the QualityGate at
|
|
124
|
+
* injection time: only failing calls that happened AFTER the
|
|
125
|
+
* fingerprint was already injected this session count. The failure
|
|
126
|
+
* that *created* a lesson is not a recurrence — it precedes any
|
|
127
|
+
* injection (plan §5.1 rule 4, same `ts >= injected_at` semantics
|
|
128
|
+
* `settle` uses).
|
|
129
|
+
*/
|
|
130
|
+
postInjectionRecurrencesFor(sessionId) {
|
|
131
|
+
const rows = this.store
|
|
132
|
+
.prepare(`SELECT COALESCE(t.error_fingerprint, t.fingerprint) AS fp
|
|
133
|
+
FROM tool_calls t
|
|
134
|
+
WHERE t.session_id = ? AND t.success = 0
|
|
135
|
+
AND (t.error_fingerprint IS NOT NULL OR t.fingerprint IS NOT NULL)
|
|
136
|
+
AND t.ts >= COALESCE(
|
|
137
|
+
(SELECT MAX(injected_at) FROM kevin_injections
|
|
138
|
+
WHERE session_id = t.session_id
|
|
139
|
+
AND fingerprint = COALESCE(t.error_fingerprint, t.fingerprint)),
|
|
140
|
+
'9999-12-31')`)
|
|
141
|
+
.all(sessionId);
|
|
142
|
+
const out = new Map();
|
|
143
|
+
for (const r of rows) {
|
|
144
|
+
if (!r.fp)
|
|
145
|
+
continue;
|
|
146
|
+
out.set(r.fp, (out.get(r.fp) ?? 0) + 1);
|
|
147
|
+
}
|
|
148
|
+
return out;
|
|
149
|
+
}
|
|
150
|
+
/** Number of rows for the session not yet settled (drives tests and settle). */
|
|
151
|
+
unsettledForSession(sessionId) {
|
|
152
|
+
const row = this.store
|
|
153
|
+
.prepare(`SELECT COUNT(*) AS n FROM kevin_injections
|
|
154
|
+
WHERE session_id = ? AND outcome = 'unmeasured'`)
|
|
155
|
+
.get(sessionId);
|
|
156
|
+
return row.n;
|
|
157
|
+
}
|
|
158
|
+
/** Latest ledger rows for a session, newest first (used by tests/tools). */
|
|
159
|
+
rowsForSession(sessionId) {
|
|
160
|
+
return this.store
|
|
161
|
+
.prepare(`SELECT id, memory_id, fingerprint, session_id, hook, tokens,
|
|
162
|
+
injected_at, outcome
|
|
163
|
+
FROM kevin_injections
|
|
164
|
+
WHERE session_id = ?
|
|
165
|
+
ORDER BY injected_at ASC, id ASC`)
|
|
166
|
+
.all(sessionId);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
/**
|
|
170
|
+
* BUG-003 — read `origin_call_id` (the failing tool_call that CREATED the
|
|
171
|
+
* memory) from memories.metadata, mirroring the feedback loop's
|
|
172
|
+
* `readOriginCallId` in MemoryService. Returns null when absent/malformed.
|
|
173
|
+
*/
|
|
174
|
+
function readOriginCallId(store, memoryId) {
|
|
175
|
+
const row = store
|
|
176
|
+
.prepare("SELECT metadata FROM memories WHERE id = ?")
|
|
177
|
+
.get(memoryId);
|
|
178
|
+
if (!row?.metadata)
|
|
179
|
+
return null;
|
|
180
|
+
try {
|
|
181
|
+
const parsed = JSON.parse(row.metadata);
|
|
182
|
+
const id = parsed?.origin_call_id;
|
|
183
|
+
return typeof id === "string" && id.length > 0 ? id : null;
|
|
184
|
+
}
|
|
185
|
+
catch {
|
|
186
|
+
return null;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
//# sourceMappingURL=InjectionLedger.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"InjectionLedger.js","sourceRoot":"","sources":["../../plugin/InjectionLedger.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* v0.4.0 (K4-014) — Deterministic fix capture (plan §5.4 / D4-07).
|
|
3
|
+
*
|
|
4
|
+
* Zero-cost "Fixed by:" raw material from local data: when CausalChain
|
|
5
|
+
* links a success to a failing fingerprint, the linked tool_call's
|
|
6
|
+
* `args_summary` becomes a deterministic fix string stored in
|
|
7
|
+
* `memories.fix_args`. The default promotion path is pure local data —
|
|
8
|
+
* no LLM calls (principle 14).
|
|
9
|
+
*/
|
|
10
|
+
/** Max length of the `args_summary` embedded in a fix_args string. */
|
|
11
|
+
export declare const FIX_ARGS_TRUNCATE = 120;
|
|
12
|
+
export interface LinkedToolCall {
|
|
13
|
+
tool: string;
|
|
14
|
+
args_summary: string | null;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* `"bash" with args "npm i -g rg"` style. Returns null when the linked
|
|
18
|
+
* call carries no args_summary (nothing to say).
|
|
19
|
+
*/
|
|
20
|
+
export declare function extractFixArgs(call: LinkedToolCall): string | null;
|
|
21
|
+
export interface EnrichInput {
|
|
22
|
+
lesson: string;
|
|
23
|
+
fixArgs: string | null;
|
|
24
|
+
originalError: string | null;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Opt-in LLM phrasing hook (K4-015): receives the lesson, the
|
|
28
|
+
* deterministic fix_args and the original error; returns a one-line
|
|
29
|
+
* `Fix:` phrasing, or null to fall back to the deterministic text.
|
|
30
|
+
* May be async; never runs on the failure hot path.
|
|
31
|
+
*/
|
|
32
|
+
export type EnrichFn = (input: EnrichInput) => Promise<string | null>;
|
|
33
|
+
export interface PatternLike {
|
|
34
|
+
content: string;
|
|
35
|
+
fixArgs: string | null;
|
|
36
|
+
}
|
|
37
|
+
/** Deterministic `Fixed by: {fix_args}` line (or "" when there is none). */
|
|
38
|
+
export declare function deterministicFixLine(pattern: PatternLike): string;
|
|
39
|
+
/**
|
|
40
|
+
* Promotion-time fix phrasing. Default path returns the deterministic
|
|
41
|
+
* `Fixed by: {fix_args}` line (or "" when there is none). When an
|
|
42
|
+
* `enrichFn` is supplied and returns a phrase, that phrase wins.
|
|
43
|
+
*/
|
|
44
|
+
export declare function enrichAtPromotion(pattern: PatternLike, enrichFn?: EnrichFn): Promise<string>;
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* v0.4.0 (K4-014) — Deterministic fix capture (plan §5.4 / D4-07).
|
|
3
|
+
*
|
|
4
|
+
* Zero-cost "Fixed by:" raw material from local data: when CausalChain
|
|
5
|
+
* links a success to a failing fingerprint, the linked tool_call's
|
|
6
|
+
* `args_summary` becomes a deterministic fix string stored in
|
|
7
|
+
* `memories.fix_args`. The default promotion path is pure local data —
|
|
8
|
+
* no LLM calls (principle 14).
|
|
9
|
+
*/
|
|
10
|
+
/** Max length of the `args_summary` embedded in a fix_args string. */
|
|
11
|
+
export const FIX_ARGS_TRUNCATE = 120;
|
|
12
|
+
/**
|
|
13
|
+
* `"bash" with args "npm i -g rg"` style. Returns null when the linked
|
|
14
|
+
* call carries no args_summary (nothing to say).
|
|
15
|
+
*/
|
|
16
|
+
export function extractFixArgs(call) {
|
|
17
|
+
const raw = call.args_summary?.trim();
|
|
18
|
+
if (!raw)
|
|
19
|
+
return null;
|
|
20
|
+
const truncated = raw.length > FIX_ARGS_TRUNCATE
|
|
21
|
+
? `${raw.slice(0, FIX_ARGS_TRUNCATE)}…`
|
|
22
|
+
: raw;
|
|
23
|
+
return `${call.tool} with args "${truncated}"`;
|
|
24
|
+
}
|
|
25
|
+
/** Deterministic `Fixed by: {fix_args}` line (or "" when there is none). */
|
|
26
|
+
export function deterministicFixLine(pattern) {
|
|
27
|
+
return pattern.fixArgs ? `Fixed by: ${pattern.fixArgs}` : "";
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Promotion-time fix phrasing. Default path returns the deterministic
|
|
31
|
+
* `Fixed by: {fix_args}` line (or "" when there is none). When an
|
|
32
|
+
* `enrichFn` is supplied and returns a phrase, that phrase wins.
|
|
33
|
+
*/
|
|
34
|
+
export async function enrichAtPromotion(pattern, enrichFn) {
|
|
35
|
+
if (enrichFn) {
|
|
36
|
+
const phrased = await enrichFn({
|
|
37
|
+
lesson: pattern.content,
|
|
38
|
+
fixArgs: pattern.fixArgs,
|
|
39
|
+
originalError: null,
|
|
40
|
+
});
|
|
41
|
+
if (phrased)
|
|
42
|
+
return phrased;
|
|
43
|
+
}
|
|
44
|
+
return deterministicFixLine(pattern);
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=LessonFixer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LessonFixer.js","sourceRoot":"","sources":["../../plugin/LessonFixer.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,sEAAsE;AACtE,MAAM,CAAC,MAAM,iBAAiB,GAAG,GAAG,CAAC;AAOrC;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,IAAoB;IAClD,MAAM,GAAG,GAAG,IAAI,CAAC,YAAY,EAAE,IAAI,EAAE,CAAC;IACtC,IAAI,CAAC,GAAG;QAAE,OAAO,IAAI,CAAC;IACtB,MAAM,SAAS,GACd,GAAG,CAAC,MAAM,GAAG,iBAAiB;QAC7B,CAAC,CAAC,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,iBAAiB,CAAC,GAAG;QACvC,CAAC,CAAC,GAAG,CAAC;IACR,OAAO,GAAG,IAAI,CAAC,IAAI,eAAe,SAAS,GAAG,CAAC;AAChD,CAAC;AAqBD,4EAA4E;AAC5E,MAAM,UAAU,oBAAoB,CAAC,OAAoB;IACxD,OAAO,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,aAAa,OAAO,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;AAC9D,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACtC,OAAoB,EACpB,QAAmB;IAEnB,IAAI,QAAQ,EAAE,CAAC;QACd,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC;YAC9B,MAAM,EAAE,OAAO,CAAC,OAAO;YACvB,OAAO,EAAE,OAAO,CAAC,OAAO;YACxB,aAAa,EAAE,IAAI;SACnB,CAAC,CAAC;QACH,IAAI,OAAO;YAAE,OAAO,OAAO,CAAC;IAC7B,CAAC;IACD,OAAO,oBAAoB,CAAC,OAAO,CAAC,CAAC;AACtC,CAAC"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import type { Store } from "./Store.js";
|
|
2
2
|
import type { Metrics } from "./metrics.js";
|
|
3
|
-
export type MemoryType = "error" | "pattern" | "decision" | "context";
|
|
3
|
+
export type MemoryType = "error" | "pattern" | "decision" | "context" | "rule" | "solution";
|
|
4
4
|
export type MemoryScope = "project" | "session";
|
|
5
|
-
export type MemoryOrigin = "reflector" | "agent" | "pattern" | "retrospective";
|
|
5
|
+
export type MemoryOrigin = "reflector" | "agent" | "pattern" | "retrospective" | "causal" | "imported";
|
|
6
6
|
export interface Memory {
|
|
7
7
|
id: string;
|
|
8
8
|
type: MemoryType;
|
|
@@ -21,10 +21,26 @@ export interface Memory {
|
|
|
21
21
|
fingerprint?: string | null;
|
|
22
22
|
/** v0.2.0 — origin of the memory. */
|
|
23
23
|
origin?: MemoryOrigin | null;
|
|
24
|
+
/** v0.3.0 — confidence derived from causal evidence (0..1). Only set by CausalChain. */
|
|
25
|
+
confidence?: number | null;
|
|
26
|
+
/** v0.3.0 — number of confirmed causal fix confirmations. */
|
|
27
|
+
evidenceCount?: number | null;
|
|
28
|
+
/** v0.3.0 — last time causal evidence was observed. */
|
|
29
|
+
lastVerifiedAt?: string | null;
|
|
30
|
+
/** v0.3.0 — lifecycle status. Default 'active'. */
|
|
31
|
+
status?: string | null;
|
|
32
|
+
/** v0.4.0 — deterministic capture of the linked fix call (K4-014). */
|
|
33
|
+
fixArgs?: string | null;
|
|
34
|
+
/** v0.4.0 (BUG-008/010) — negative evidence: how many times the
|
|
35
|
+
* fingerprint recurred after injection (demotes confidence). */
|
|
36
|
+
recurrenceCount?: number | null;
|
|
24
37
|
}
|
|
25
38
|
export interface SaveInput {
|
|
26
39
|
type: MemoryType;
|
|
27
40
|
content: string;
|
|
41
|
+
/** v0.4.0 (BUG-008) — preserve the original id on okf import;
|
|
42
|
+
* when absent a fresh uuidv7 is generated. */
|
|
43
|
+
id?: string;
|
|
28
44
|
scope?: MemoryScope;
|
|
29
45
|
relevanceScore?: number;
|
|
30
46
|
sourceTool?: string;
|
|
@@ -37,6 +53,15 @@ export interface SaveInput {
|
|
|
37
53
|
origin?: MemoryOrigin;
|
|
38
54
|
/** v0.2.0 — explicit fingerprint. Auto-derived for `type='error'` if absent. */
|
|
39
55
|
fingerprint?: string;
|
|
56
|
+
/** v0.3.0 — number of confirmed causal fix confirmations. */
|
|
57
|
+
evidenceCount?: number;
|
|
58
|
+
/** v0.3.0 — last time causal evidence was observed. */
|
|
59
|
+
lastVerifiedAt?: string;
|
|
60
|
+
/** v0.3.0 — lifecycle status. Default 'active'. */
|
|
61
|
+
status?: string;
|
|
62
|
+
/** v0.4.0 (BUG-008/010) — how many times the fingerprint recurred
|
|
63
|
+
* after injection (negative evidence, demotes confidence). */
|
|
64
|
+
recurrenceCount?: number;
|
|
40
65
|
}
|
|
41
66
|
export interface QueryInput {
|
|
42
67
|
text: string;
|
|
@@ -48,6 +73,13 @@ export interface QueryInput {
|
|
|
48
73
|
* behavior, per plan §B6.3 / K2-010).
|
|
49
74
|
*/
|
|
50
75
|
full?: boolean;
|
|
76
|
+
/** v0.3.0 — when true, includes rows where status = 'superseded'.
|
|
77
|
+
* Default false (only active rows). */
|
|
78
|
+
includeSuperseded?: boolean;
|
|
79
|
+
/** v0.3.0 (BUG-001) — when true, the slim payload also carries
|
|
80
|
+
* `confidence`, `evidence_count` and `last_verified_at` (v0.3.0 K3
|
|
81
|
+
* evidence fields). Default false (minimal slim shape). */
|
|
82
|
+
evidence?: boolean;
|
|
51
83
|
}
|
|
52
84
|
/** v0.2.0 — slim query payload (K2-010). Snippet is a short content prefix;
|
|
53
85
|
* `score` is the FTS5 BM25 score when available, falling back to
|
|
@@ -59,28 +91,88 @@ export interface SlimMemory {
|
|
|
59
91
|
score: number;
|
|
60
92
|
snippet: string;
|
|
61
93
|
}
|
|
94
|
+
/** v0.3.0 (BUG-001) — slim payload extended with the evidence fields when
|
|
95
|
+
* `query({ evidence: true })`. Fills the `kevin_query(evidence: true)`
|
|
96
|
+
* contract without falling back to the full `Memory` shape. */
|
|
97
|
+
export interface SlimMemoryWithEvidence extends SlimMemory {
|
|
98
|
+
confidence: number | null;
|
|
99
|
+
evidence_count: number | null;
|
|
100
|
+
last_verified_at: string | null;
|
|
101
|
+
}
|
|
62
102
|
export interface GetRelevantInput {
|
|
63
103
|
query?: string;
|
|
64
104
|
maxTokens?: number;
|
|
65
105
|
scope?: MemoryScope | "all";
|
|
106
|
+
/** v0.3.0 — when true, includes rows where status = 'superseded'.
|
|
107
|
+
* Default false (only active rows). */
|
|
108
|
+
includeSuperseded?: boolean;
|
|
109
|
+
/**
|
|
110
|
+
* v0.4.0 (BUG-016) — when false, the relevance bump (K2-023) is
|
|
111
|
+
* skipped. Used by ContextInjector's probe fetch so the decision and
|
|
112
|
+
* any retry both see the ORIGINAL ranking; the single bump is applied
|
|
113
|
+
* by the fetch that actually produces the injected block.
|
|
114
|
+
*/
|
|
115
|
+
bump?: boolean;
|
|
66
116
|
}
|
|
67
117
|
export declare class MemoryService {
|
|
68
118
|
private readonly metrics;
|
|
69
119
|
constructor(store: Store, metrics?: Metrics | null);
|
|
70
120
|
private store;
|
|
121
|
+
private hasRecurrenceColumn;
|
|
122
|
+
private _hasRecurrenceColumn;
|
|
71
123
|
save(input: SaveInput): string;
|
|
72
124
|
getById(id: string): Memory | null;
|
|
125
|
+
/**
|
|
126
|
+
* v0.4.0 (K4-016) — most recent ACTIVE memory for a fingerprint,
|
|
127
|
+
* optionally filtered by type. Feeds the HITL suggestion lookup
|
|
128
|
+
* (most-recurred fingerprint → its pattern memory).
|
|
129
|
+
*/
|
|
130
|
+
getByFingerprint(fingerprint: string, type?: MemoryType): Memory | null;
|
|
73
131
|
update(id: string, fields: Partial<Memory>): void;
|
|
74
132
|
delete(id: string): void;
|
|
75
133
|
/** v0.1.x behavior — returns full `Memory` rows. */
|
|
76
134
|
query(input: QueryInput & {
|
|
77
135
|
full: true;
|
|
78
136
|
}): Memory[];
|
|
137
|
+
/** v0.3.0 (BUG-001) — slim rows carrying the evidence fields. */
|
|
138
|
+
query(input: QueryInput & {
|
|
139
|
+
evidence: true;
|
|
140
|
+
}): SlimMemoryWithEvidence[];
|
|
79
141
|
/** v0.2.0 default — returns `SlimMemory` rows. */
|
|
80
142
|
query(input: QueryInput): SlimMemory[];
|
|
143
|
+
private isCrossProjectEnabled;
|
|
144
|
+
/**
|
|
145
|
+
* v0.4.0 (K4-012) — read a kevin_settings flag by key. Falls back to
|
|
146
|
+
* the caller-provided default when the key is missing or the table is
|
|
147
|
+
* unavailable (legacy DB without the settings table).
|
|
148
|
+
*/
|
|
149
|
+
getSetting(key: string, fallback?: string): string;
|
|
81
150
|
private loadAll;
|
|
82
151
|
private queryRelevant;
|
|
83
152
|
getRelevant(input: GetRelevantInput): Memory[];
|
|
153
|
+
/**
|
|
154
|
+
* v0.4.0 (BUG-016) — apply the K2-023 relevance bump to a fixed slice
|
|
155
|
+
* of ids, exactly once. Lets ContextInjector probe without mutating
|
|
156
|
+
* and still bump the slice it actually injects.
|
|
157
|
+
*/
|
|
158
|
+
bumpRelevance(ids: string[]): void;
|
|
159
|
+
/**
|
|
160
|
+
* v0.3.0 (K3-004) — Promote an error memory to a causal pattern.
|
|
161
|
+
*
|
|
162
|
+
* Creates a new `pattern` memory with `origin = 'causal'`, derived
|
|
163
|
+
* confidence, and evidence count. The original error memory is NOT
|
|
164
|
+
* deleted — the audit trail is preserved. Returns the new memory id,
|
|
165
|
+
* or null when the source error is not eligible (missing fingerprint,
|
|
166
|
+
* wrong type, or already promoted).
|
|
167
|
+
*/
|
|
168
|
+
/**
|
|
169
|
+
* v0.4.0 (K4-009) — returns `{ id, created }` so callers can tell a
|
|
170
|
+
* newly-created pattern from an idempotent refresh.
|
|
171
|
+
*/
|
|
172
|
+
promoteToPattern(errorId: string, evidenceCount: number, recurrenceCount?: number): {
|
|
173
|
+
id: string;
|
|
174
|
+
created: boolean;
|
|
175
|
+
} | null;
|
|
84
176
|
/**
|
|
85
177
|
* v0.2.0 (K2-026) — Feedback loop positive half (plan §B6.10 / D2-10).
|
|
86
178
|
*
|
|
@@ -93,4 +185,33 @@ export declare class MemoryService {
|
|
|
93
185
|
* Returns the number of memories that received a positive boost.
|
|
94
186
|
*/
|
|
95
187
|
boostPositiveReflectors(sessionId: string): number;
|
|
188
|
+
/**
|
|
189
|
+
* v0.3.0 (K3-013) — Feedback loop negative half.
|
|
190
|
+
*
|
|
191
|
+
* For each reflector-sourced error memory from this session whose
|
|
192
|
+
* fingerprint DID recur as a failing tool_call (the lesson didn't
|
|
193
|
+
* prevent the error), decrement `relevance_score` by `RELEVANCE_PENALTY`
|
|
194
|
+
* (down to zero) and increment `evidence_count` as a negative signal.
|
|
195
|
+
* Agent-saved memories are NEVER penalized.
|
|
196
|
+
*
|
|
197
|
+
* Returns the number of memories penalized.
|
|
198
|
+
*/
|
|
199
|
+
/**
|
|
200
|
+
* v0.3.0 fix — Mirror of the free function `countSupersedeCandidates`
|
|
201
|
+
* exposed as an instance method so `okf-import` (which holds a
|
|
202
|
+
* `MemoryService` reference but not the underlying `Store`) can count
|
|
203
|
+
* rows that `save()` will mark as superseded.
|
|
204
|
+
*/
|
|
205
|
+
countSupersedeCandidates(type: MemoryType, fingerprint: string | null | undefined, projectId: string | null): number;
|
|
206
|
+
penalizeRecurringReflectors(sessionId: string): number;
|
|
96
207
|
}
|
|
208
|
+
/**
|
|
209
|
+
* v0.3.0 fix — Count active memories that would be superseded by a new
|
|
210
|
+
* row with the given (type, fingerprint, projectId) tuple. Used by
|
|
211
|
+
* `okf-import` to populate `ImportResult.superseded` accurately.
|
|
212
|
+
*
|
|
213
|
+
* Matches the supersede logic in `save()`: only `decision` and `rule`
|
|
214
|
+
* types supersede prior rows with the same fingerprint. Returns 0 for
|
|
215
|
+
* any other type.
|
|
216
|
+
*/
|
|
217
|
+
export declare function countSupersedeCandidates(store: Store, type: MemoryType, fingerprint: string | null | undefined, projectId: string | null): number;
|