@jmtrin/opencode-kevin 0.3.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 +38 -6
- package/dist/migrations/005_v04_signal.sql +57 -0
- package/dist/plugin/CausalChain.d.ts +13 -2
- package/dist/plugin/CausalChain.js +83 -13
- package/dist/plugin/CausalChain.js.map +1 -1
- package/dist/plugin/ContextInjector.d.ts +80 -4
- package/dist/plugin/ContextInjector.js +262 -88
- 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 +62 -1
- package/dist/plugin/MemoryService.js +206 -43
- package/dist/plugin/MemoryService.js.map +1 -1
- package/dist/plugin/Migrate.js +10 -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 +17 -0
- package/dist/plugin/Reflector.js +81 -26
- 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 +15 -8
- 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 +216 -44
- package/dist/plugin/index.js.map +1 -1
- package/dist/plugin/kevin_why.d.ts +4 -0
- package/dist/plugin/kevin_why.js +51 -35
- package/dist/plugin/kevin_why.js.map +1 -1
- 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 +16 -0
- package/dist/plugin/metrics.js.map +1 -1
- package/dist/plugin/okf-export.js +63 -22
- package/dist/plugin/okf-export.js.map +1 -1
- package/dist/plugin/okf-import.d.ts +4 -1
- package/dist/plugin/okf-import.js +45 -12
- package/dist/plugin/okf-import.js.map +1 -1
- 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/005_v04_signal.sql +57 -0
- package/package.json +1 -1
|
@@ -1,110 +1,133 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { QualityGate } from "./QualityGate.js";
|
|
2
|
+
import { formatMemories, formatMemorySnippets } from "./memory-format.js";
|
|
2
3
|
import { estimateTokens } from "./metrics.js";
|
|
4
|
+
import { STOP_WORDS } from "./query-tokenizer.js";
|
|
5
|
+
export const QUALITY_GATE_SETTING = "quality_gate_enabled";
|
|
6
|
+
export const SNIPPET_INJECTION_SETTING = "lesson_snippet_injection";
|
|
3
7
|
const SYSTEM_TRANSFORM_TOKENS = 1500;
|
|
4
8
|
const COMPACTING_TOKENS = 2000;
|
|
5
|
-
const STOP_WORDS = new Set([
|
|
6
|
-
"a",
|
|
7
|
-
"an",
|
|
8
|
-
"and",
|
|
9
|
-
"are",
|
|
10
|
-
"at",
|
|
11
|
-
"be",
|
|
12
|
-
"been",
|
|
13
|
-
"but",
|
|
14
|
-
"by",
|
|
15
|
-
"did",
|
|
16
|
-
"do",
|
|
17
|
-
"does",
|
|
18
|
-
"el",
|
|
19
|
-
"eso",
|
|
20
|
-
"for",
|
|
21
|
-
"how",
|
|
22
|
-
"i",
|
|
23
|
-
"if",
|
|
24
|
-
"in",
|
|
25
|
-
"is",
|
|
26
|
-
"it",
|
|
27
|
-
"la",
|
|
28
|
-
"las",
|
|
29
|
-
"los",
|
|
30
|
-
"mi",
|
|
31
|
-
"my",
|
|
32
|
-
"o",
|
|
33
|
-
"of",
|
|
34
|
-
"on",
|
|
35
|
-
"or",
|
|
36
|
-
"para",
|
|
37
|
-
"por",
|
|
38
|
-
"que",
|
|
39
|
-
"she",
|
|
40
|
-
"su",
|
|
41
|
-
"that",
|
|
42
|
-
"the",
|
|
43
|
-
"this",
|
|
44
|
-
"to",
|
|
45
|
-
"tu",
|
|
46
|
-
"un",
|
|
47
|
-
"una",
|
|
48
|
-
"we",
|
|
49
|
-
"were",
|
|
50
|
-
"what",
|
|
51
|
-
"when",
|
|
52
|
-
"where",
|
|
53
|
-
"which",
|
|
54
|
-
"who",
|
|
55
|
-
"why",
|
|
56
|
-
"with",
|
|
57
|
-
"y",
|
|
58
|
-
"you",
|
|
59
|
-
"como",
|
|
60
|
-
"con",
|
|
61
|
-
"de",
|
|
62
|
-
"en",
|
|
63
|
-
"he",
|
|
64
|
-
"they",
|
|
65
|
-
"was",
|
|
66
|
-
"sin",
|
|
67
|
-
]);
|
|
68
9
|
function isWordChar(ch) {
|
|
69
10
|
return /[a-z0-9áéíóúüñ]/i.test(ch);
|
|
70
11
|
}
|
|
12
|
+
/**
|
|
13
|
+
* BUG-005 — extract the lesson's `Suggestion:` text (first line after the
|
|
14
|
+
* marker) when present. Returns null for agent-saved notes that carry no
|
|
15
|
+
* suggestion payload. Also matches the truncated snippet-style lesson
|
|
16
|
+
* bodies used by older tests.
|
|
17
|
+
*/
|
|
18
|
+
function extractSuggestionText(content) {
|
|
19
|
+
const m = content.match(/\nSuggestion:\s*([^\n]+)/);
|
|
20
|
+
return m ? m[1].trim() : null;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* BUG-005 — extract the `fails with X` slot of a lesson. For lessons
|
|
24
|
+
* without a dispatched code this equals the coarse errorType that
|
|
25
|
+
* `QualityGate.evaluate` needs for the strength classification.
|
|
26
|
+
*/
|
|
27
|
+
function extractFailsWithErrorType(content) {
|
|
28
|
+
const m = content.match(/\bfails with ([^:]+):/i);
|
|
29
|
+
return m ? m[1].trim() : null;
|
|
30
|
+
}
|
|
71
31
|
export class ContextInjector {
|
|
72
32
|
memoryService;
|
|
73
33
|
metrics;
|
|
34
|
+
ledger;
|
|
74
35
|
lastRecurrenceCount = 0;
|
|
75
|
-
|
|
36
|
+
/** v0.4.0 (K4-016) — session that produced the last recurrence set. */
|
|
37
|
+
lastRecurredSession = null;
|
|
38
|
+
/** v0.4.0 (K4-017) — per-session seen-set (plan §5.1 rule 3). */
|
|
39
|
+
seenBySession = new Map();
|
|
40
|
+
constructor(memoryService, metrics = null, ledger = null) {
|
|
76
41
|
this.memoryService = memoryService;
|
|
77
42
|
this.metrics = metrics;
|
|
43
|
+
this.ledger = ledger;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* v0.4.0 (K4-017) — reset the per-session seen-set when a session is
|
|
47
|
+
* created (plan §5.1 rule 3). Wired from the `session.created` event.
|
|
48
|
+
*/
|
|
49
|
+
onSessionCreated(sessionId) {
|
|
50
|
+
this.seenBySession.delete(sessionId);
|
|
78
51
|
}
|
|
79
52
|
/**
|
|
80
53
|
* v0.3.0 (K3-020) — notify the injector that the negative feedback half
|
|
81
54
|
* fired N times in the last session.idle. The next system.transform or
|
|
82
55
|
* compacting hook will prepend a HITL suggestion block.
|
|
56
|
+
* v0.4.0 (K4-016) — the session id enables the concrete suggestion:
|
|
57
|
+
* the most-recurred fingerprint's pattern + its fix_args.
|
|
83
58
|
*/
|
|
84
|
-
setRecurrences(count) {
|
|
59
|
+
setRecurrences(count, sessionId) {
|
|
85
60
|
this.lastRecurrenceCount = count;
|
|
61
|
+
this.lastRecurredSession = sessionId ?? null;
|
|
86
62
|
}
|
|
87
63
|
/**
|
|
88
|
-
* v0.
|
|
89
|
-
* occurred
|
|
64
|
+
* v0.4.0 (K4-016) — generate a CONCRETE HITL suggestion block when
|
|
65
|
+
* recurrences occurred (plan §5.5): names the most-recurred pattern,
|
|
66
|
+
* its exact recurrence count, observed fix_args and confidence. The
|
|
67
|
+
* AGENTS.md draft line derives from the lesson's `Suggestion:` text —
|
|
68
|
+
* never a canned string, and never the anonymous "the same error
|
|
69
|
+
* pattern" without naming the pattern and count.
|
|
70
|
+
*
|
|
71
|
+
* BUG-012 — emits AT MOST ONCE per session: calling it resets the
|
|
72
|
+
* pending recurrence signal, so whichever hook (system.transform or
|
|
73
|
+
* compacting) runs first in a session consumes the block. Documented
|
|
74
|
+
* behavior; wire index.ts to match.
|
|
90
75
|
*/
|
|
91
76
|
generateSuggestion() {
|
|
92
|
-
|
|
77
|
+
const count = this.lastRecurrenceCount;
|
|
78
|
+
const sessionId = this.lastRecurredSession;
|
|
79
|
+
this.lastRecurrenceCount = 0;
|
|
80
|
+
this.lastRecurredSession = null;
|
|
81
|
+
if (count === 0)
|
|
93
82
|
return "";
|
|
94
|
-
const
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
83
|
+
const recurrences = this.ledger?.recurrencesFor(sessionId ?? "") ?? new Map();
|
|
84
|
+
let topFp = null;
|
|
85
|
+
let topCount = 0;
|
|
86
|
+
for (const [fp, n] of recurrences) {
|
|
87
|
+
if (n > topCount) {
|
|
88
|
+
topFp = fp;
|
|
89
|
+
topCount = n;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
const pattern = topFp
|
|
93
|
+
? this.memoryService.getByFingerprint(topFp, "pattern")
|
|
94
|
+
: null;
|
|
95
|
+
// No pattern memory for the recurred fingerprint (or no ledger): a
|
|
96
|
+
// short fallback that still names the count.
|
|
97
|
+
if (!pattern) {
|
|
98
|
+
return `<kevin-suggestion>
|
|
99
|
+
An error pattern recurred ${count} time(s) this session.
|
|
100
|
+
Consider adding a convention to AGENTS.md.
|
|
101
|
+
</kevin-suggestion>`;
|
|
102
|
+
}
|
|
103
|
+
const summary = pattern.content
|
|
104
|
+
.split("\n")[0]
|
|
105
|
+
.replace(/^Causal pattern:\s*/, "")
|
|
106
|
+
.trim();
|
|
107
|
+
const pct = pattern.confidence != null ? Math.round(pattern.confidence * 100) : 0;
|
|
108
|
+
const evidence = pattern.evidenceCount ?? 0;
|
|
109
|
+
const fixLine = pattern.fixArgs
|
|
110
|
+
? `Observed fix: ${pattern.fixArgs} (${evidence} confirmed fix${evidence === 1 ? "" : "es"}, confidence ${pct}%).`
|
|
111
|
+
: "";
|
|
112
|
+
return `<kevin-suggestion>
|
|
113
|
+
The error pattern "${summary}" recurred ${topCount} time(s) this session.
|
|
114
|
+
${fixLine}
|
|
115
|
+
Consider adding this convention to AGENTS.md:
|
|
116
|
+
- ${this.agentsDraftLine(pattern)}
|
|
105
117
|
</kevin-suggestion>`;
|
|
106
|
-
|
|
107
|
-
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* v0.4.0 (K4-016) — the AGENTS.md draft line derives from the lesson's
|
|
121
|
+
* `Suggestion:` text (kept verbatim in the pattern's `Original:` block).
|
|
122
|
+
*/
|
|
123
|
+
agentsDraftLine(pattern) {
|
|
124
|
+
const m = pattern.content.match(/\nSuggestion: ([^\n]+)/);
|
|
125
|
+
if (m)
|
|
126
|
+
return m[1].trim();
|
|
127
|
+
return `${pattern.content
|
|
128
|
+
.split("\n")[0]
|
|
129
|
+
.replace(/^Causal pattern:\s*/, "")
|
|
130
|
+
.trim()} recurred — document the fix in AGENTS.md`;
|
|
108
131
|
}
|
|
109
132
|
/**
|
|
110
133
|
* v0.2.0 (K2-024): origin-aware ranking at injection time is delegated
|
|
@@ -114,17 +137,33 @@ Draft:
|
|
|
114
137
|
* callers. Plan §B6.5: "apply the same multiplier as
|
|
115
138
|
* MemoryService.recall so reflector lessons outrank agent-saved notes
|
|
116
139
|
* at injection time" — satisfied transitively via the getRelevant call.
|
|
140
|
+
*
|
|
141
|
+
* v0.4.0 (K4-017): after retrieval, every memory is filtered through
|
|
142
|
+
* `QualityGate.canInject` (session seen-set + recurrence + strength),
|
|
143
|
+
* and each admitted memory is recorded in the `InjectionLedger`
|
|
144
|
+
* (plan §5.2 — one row per injected memory).
|
|
117
145
|
*/
|
|
118
|
-
inject(query, tag, cap, metricKey) {
|
|
119
|
-
|
|
146
|
+
inject(query, tag, cap, metricKey, sessionId) {
|
|
147
|
+
// BUG-016 — probe WITHOUT bumping so the overflow decision and any
|
|
148
|
+
// retry both see the ORIGINAL ranking. The single relevance bump
|
|
149
|
+
// (K2-023) is applied exactly once, to the slice that actually
|
|
150
|
+
// produces the injected block — see below.
|
|
151
|
+
let memories = this.memoryService.getRelevant({
|
|
152
|
+
query,
|
|
153
|
+
maxTokens: cap,
|
|
154
|
+
bump: false,
|
|
155
|
+
});
|
|
120
156
|
if (memories.length === 0)
|
|
121
157
|
return "";
|
|
122
|
-
const firstBlock =
|
|
158
|
+
const firstBlock = this.format(memories, tag);
|
|
123
159
|
const aggregateTokens = estimateTokens(firstBlock);
|
|
124
160
|
const firstRowProtect = memories[0]
|
|
125
161
|
?.protect;
|
|
126
162
|
const noProtectAboveTheFold = firstRowProtect === false;
|
|
127
163
|
if (aggregateTokens > 0.8 * cap && noProtectAboveTheFold) {
|
|
164
|
+
// Retry = a single fetch with the adjusted budget, ranked by
|
|
165
|
+
// the original scores (the probe never mutated them) — the
|
|
166
|
+
// equivalent of one getRelevant call with maxTokens=lowerCap.
|
|
128
167
|
const lowerCap = Math.max(1, Math.round(0.8 * cap));
|
|
129
168
|
memories = this.memoryService.getRelevant({
|
|
130
169
|
query,
|
|
@@ -133,10 +172,145 @@ Draft:
|
|
|
133
172
|
if (memories.length === 0)
|
|
134
173
|
return "";
|
|
135
174
|
}
|
|
136
|
-
|
|
175
|
+
else {
|
|
176
|
+
// No retry: the probe slice IS the injected slice — bump it
|
|
177
|
+
// exactly once here.
|
|
178
|
+
this.memoryService.bumpRelevance(memories.map((m) => m.id));
|
|
179
|
+
}
|
|
180
|
+
const admitted = this.admit(memories, sessionId);
|
|
181
|
+
if (admitted.length === 0)
|
|
182
|
+
return "";
|
|
183
|
+
const block = this.format(admitted, tag);
|
|
184
|
+
this.recordInjections(admitted, sessionId, tag, block);
|
|
137
185
|
this.metrics?.incr(metricKey, estimateTokens(block));
|
|
138
186
|
return block;
|
|
139
187
|
}
|
|
188
|
+
/**
|
|
189
|
+
* v0.4.0 (K4-017) — QualityGate admission: filters the ranked slice to
|
|
190
|
+
* memories that may be injected this session, updating the session
|
|
191
|
+
* seen-set.
|
|
192
|
+
*
|
|
193
|
+
* BUG-005 — strength/actionability now go through the REAL
|
|
194
|
+
* `QualityGate.evaluate` semantics (plan §5.1 rules 1-2), which this
|
|
195
|
+
* class had only re-derived from `metadata.dispatch`:
|
|
196
|
+
* - dispatched code → strong + actionable (rescued errorType);
|
|
197
|
+
* - generic fallback suggestion + no code → weak, NOT actionable
|
|
198
|
+
* (the generic-suggestion ban is now enforced even for legacy
|
|
199
|
+
* lessons without dispatch metadata, as long as the `Suggestion:`
|
|
200
|
+
* text is available);
|
|
201
|
+
* - specific suggestion without code → strong + actionable;
|
|
202
|
+
* - no `Suggestion:` text at all (agent-saved note) → strong +
|
|
203
|
+
* actionable — the agent explicitly asked to remember it;
|
|
204
|
+
* - causal patterns (type='pattern') → always strong + actionable:
|
|
205
|
+
* they are the FIXED form of a fingerprint (K4-025).
|
|
206
|
+
*/
|
|
207
|
+
admit(memories, sessionId) {
|
|
208
|
+
const qualityGateEnabled = this.memoryService.getSetting(QUALITY_GATE_SETTING, "1") === "1";
|
|
209
|
+
const seen = this.seenBySession.get(sessionId) ?? new Set();
|
|
210
|
+
const recurrences = this.ledger?.postInjectionRecurrencesFor(sessionId) ??
|
|
211
|
+
new Map();
|
|
212
|
+
const admitted = [];
|
|
213
|
+
for (const m of memories) {
|
|
214
|
+
const q = this.lessonQuality(m);
|
|
215
|
+
if (QualityGate.canInject({
|
|
216
|
+
id: m.id,
|
|
217
|
+
status: m.status ?? undefined,
|
|
218
|
+
strength: q.strength,
|
|
219
|
+
isActionable: q.isActionable,
|
|
220
|
+
}, {
|
|
221
|
+
seenThisSession: seen,
|
|
222
|
+
// v0.4.0 (K4-025) — plan §5.1 rule 4: a causal pattern
|
|
223
|
+
// re-admits a lesson that the stale error row cannot.
|
|
224
|
+
// The recurrence ban (QualityGate rule 3) is scoped to
|
|
225
|
+
// error lessons — a pattern is the FIXED form of the
|
|
226
|
+
// fingerprint and is exactly what D4-06 wants back in
|
|
227
|
+
// the prompt.
|
|
228
|
+
recurrenceCount: m.type === "pattern"
|
|
229
|
+
? 0
|
|
230
|
+
: m.fingerprint
|
|
231
|
+
? (recurrences.get(m.fingerprint) ?? 0)
|
|
232
|
+
: 0,
|
|
233
|
+
}, qualityGateEnabled)) {
|
|
234
|
+
admitted.push(m);
|
|
235
|
+
seen.add(m.id);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
this.seenBySession.set(sessionId, seen);
|
|
239
|
+
return admitted;
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* BUG-005 — the single source of truth for a memory's lesson quality,
|
|
243
|
+
* shared by `admit` (the gate) and `format` (the K4-023 `(low
|
|
244
|
+
* confidence)` marker). Routes lessons through `QualityGate.evaluate`
|
|
245
|
+
* — the production call site that was previously missing.
|
|
246
|
+
*/
|
|
247
|
+
lessonQuality(m) {
|
|
248
|
+
// K4-025: a causal pattern is the fixed form of the fingerprint —
|
|
249
|
+
// never gated by suggestion text.
|
|
250
|
+
if (m.type === "pattern") {
|
|
251
|
+
return { strength: "strong", isActionable: true, weak: false };
|
|
252
|
+
}
|
|
253
|
+
const meta = (m.metadata ?? null);
|
|
254
|
+
const dispatch = meta?.dispatch ?? null;
|
|
255
|
+
const suggestion = extractSuggestionText(m.content);
|
|
256
|
+
if (suggestion === null) {
|
|
257
|
+
// Agent-saved note without a `Suggestion:` line: the agent
|
|
258
|
+
// explicitly asked to remember it → strong + actionable.
|
|
259
|
+
return { strength: "strong", isActionable: true, weak: false };
|
|
260
|
+
}
|
|
261
|
+
// The lesson's `fails with X` slot holds the displayed errorType;
|
|
262
|
+
// when no code was dispatched it equals the coarse errorType that
|
|
263
|
+
// `evaluate` needs for the strength classification.
|
|
264
|
+
const errorType = extractFailsWithErrorType(m.content);
|
|
265
|
+
const q = QualityGate.evaluate({ errorType: errorType ?? "unknown", suggestion }, dispatch, errorType ?? "unknown");
|
|
266
|
+
return {
|
|
267
|
+
strength: q.strength,
|
|
268
|
+
isActionable: q.isActionable,
|
|
269
|
+
weak: q.strength === "weak",
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
/**
|
|
273
|
+
* v0.4.0 (K4-017) — one ledger row per admitted memory (plan §5.2).
|
|
274
|
+
* Token attribution uses the memory's share of the final block.
|
|
275
|
+
*/
|
|
276
|
+
recordInjections(admitted, sessionId, tag, block) {
|
|
277
|
+
if (this.ledger === null)
|
|
278
|
+
return;
|
|
279
|
+
const blockTokens = estimateTokens(block);
|
|
280
|
+
const measurable = admitted.filter((m) => m.fingerprint);
|
|
281
|
+
const perMemory = Math.max(1, Math.round(blockTokens / admitted.length));
|
|
282
|
+
const hook = tag === "context" ? "pre_prompt" : "compacting";
|
|
283
|
+
for (const m of measurable) {
|
|
284
|
+
this.ledger.record({
|
|
285
|
+
memoryId: m.id,
|
|
286
|
+
fingerprint: m.fingerprint,
|
|
287
|
+
sessionId,
|
|
288
|
+
hook,
|
|
289
|
+
tokens: perMemory,
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* v0.4.0 (K4-012) — snippet injection payload (plan §5.1 rule 5,
|
|
295
|
+
* D4-05): rows show `id:` + first 2 non-empty lines + `<protect>`
|
|
296
|
+
* instead of the full body. Gated by the `lesson_snippet_injection`
|
|
297
|
+
* setting (default `'1'`); when `'0'`, full content is restored.
|
|
298
|
+
* `escapeInjectedText` is applied to snippet content by the formatter.
|
|
299
|
+
* v0.4.0 (K4-023) — weak lessons admitted in debug mode are flagged so
|
|
300
|
+
* the formatter renders the `(low confidence)` marker.
|
|
301
|
+
*/
|
|
302
|
+
format(memories, tag) {
|
|
303
|
+
const snippetsOn = this.memoryService.getSetting(SNIPPET_INJECTION_SETTING, "1") === "1";
|
|
304
|
+
const items = memories.map((m) => ({
|
|
305
|
+
...m,
|
|
306
|
+
// BUG-005 — the weak marker must mirror the admission decision
|
|
307
|
+
// (K4-023 debug mode), not re-derive from dispatch alone.
|
|
308
|
+
weak: this.lessonQuality(m).weak,
|
|
309
|
+
}));
|
|
310
|
+
return snippetsOn
|
|
311
|
+
? formatMemorySnippets(items, tag)
|
|
312
|
+
: formatMemories(items, tag);
|
|
313
|
+
}
|
|
140
314
|
deriveQuery(messages) {
|
|
141
315
|
let lastUserContent = "";
|
|
142
316
|
for (let i = messages.length - 1; i >= 0; i--) {
|
|
@@ -165,7 +339,7 @@ Draft:
|
|
|
165
339
|
const query = this.deriveQuery(input.messages);
|
|
166
340
|
if (!query)
|
|
167
341
|
return;
|
|
168
|
-
const block = this.inject(query, "context", SYSTEM_TRANSFORM_TOKENS, "tokens_injected_pre_prompt");
|
|
342
|
+
const block = this.inject(query, "context", SYSTEM_TRANSFORM_TOKENS, "tokens_injected_pre_prompt", input.sessionID ?? "");
|
|
169
343
|
if (block)
|
|
170
344
|
output.system.push(block);
|
|
171
345
|
}
|
|
@@ -173,7 +347,7 @@ Draft:
|
|
|
173
347
|
const query = this.deriveQuery(input.messages);
|
|
174
348
|
if (!query)
|
|
175
349
|
return;
|
|
176
|
-
const block = this.inject(query, "memory", COMPACTING_TOKENS, "tokens_injected_compacting");
|
|
350
|
+
const block = this.inject(query, "memory", COMPACTING_TOKENS, "tokens_injected_compacting", input.sessionID);
|
|
177
351
|
if (block)
|
|
178
352
|
output.context.push(block);
|
|
179
353
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ContextInjector.js","sourceRoot":"","sources":["../../plugin/ContextInjector.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"ContextInjector.js","sourceRoot":"","sources":["../../plugin/ContextInjector.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import type { Store } from "./Store.js";
|
|
2
|
+
import type { Metrics } from "./metrics.js";
|
|
3
|
+
/**
|
|
4
|
+
* v0.4.0 InjectionLedger (K4-006/K4-007, plan §5.2, D4-04).
|
|
5
|
+
*
|
|
6
|
+
* The measurement half of the closed feedback loop: every lesson injected
|
|
7
|
+
* into a prompt is recorded in `kevin_injections`, and at `session.idle` the
|
|
8
|
+
* ledger settles each unmeasured row — if the same fingerprint failed again
|
|
9
|
+
* after the injection, the lesson is `ineffective`; otherwise `effective`.
|
|
10
|
+
* `precision_rate = effective / total` is the honest, measured value of the
|
|
11
|
+
* injection system (replacing Kevin_Token_Impact-style estimates).
|
|
12
|
+
*
|
|
13
|
+
* Schema (migration 005_v04_signal.sql):
|
|
14
|
+
* kevin_injections(id PK, memory_id, fingerprint, session_id, hook,
|
|
15
|
+
* tokens, injected_at, outcome)
|
|
16
|
+
*
|
|
17
|
+
* BUG-015 — a ledger row is only meaningful when its fingerprint can be
|
|
18
|
+
* matched against failing tool_calls (`COALESCE(error_fingerprint,
|
|
19
|
+
* fingerprint)`). Memories WITHOUT a fingerprint (agent-saved notes) can
|
|
20
|
+
* never match, so they are NOT recorded: settle() would otherwise mark
|
|
21
|
+
* them `effective` forever and inflate `precision_rate`. The caller
|
|
22
|
+
* (ContextInjector.recordInjections) skips them.
|
|
23
|
+
*/
|
|
24
|
+
export type InjectionHook = "pre_prompt" | "compacting";
|
|
25
|
+
export type InjectionOutcome = "unmeasured" | "effective" | "ineffective";
|
|
26
|
+
export interface InjectionRecordInput {
|
|
27
|
+
memoryId: string;
|
|
28
|
+
fingerprint: string;
|
|
29
|
+
sessionId: string;
|
|
30
|
+
hook: InjectionHook;
|
|
31
|
+
tokens: number;
|
|
32
|
+
}
|
|
33
|
+
interface InjectionRow {
|
|
34
|
+
id: string;
|
|
35
|
+
memory_id: string;
|
|
36
|
+
fingerprint: string;
|
|
37
|
+
session_id: string;
|
|
38
|
+
hook: InjectionHook;
|
|
39
|
+
tokens: number;
|
|
40
|
+
injected_at: string;
|
|
41
|
+
outcome: InjectionOutcome;
|
|
42
|
+
}
|
|
43
|
+
export declare class InjectionLedger {
|
|
44
|
+
private readonly store;
|
|
45
|
+
private readonly metrics;
|
|
46
|
+
constructor(store: Store, metrics?: Metrics | null);
|
|
47
|
+
/**
|
|
48
|
+
* Records one injected memory. Idempotent at the row level (UUID PK);
|
|
49
|
+
* duplicates are expected only via the caller's per-session seen-set.
|
|
50
|
+
*/
|
|
51
|
+
record(input: InjectionRecordInput): void;
|
|
52
|
+
/**
|
|
53
|
+
* Settles every unmeasured injection of the session: a fingerprint that
|
|
54
|
+
* failed again (as a failing tool_call) after the injection is
|
|
55
|
+
* `ineffective`, otherwise `effective`. Idempotent — only
|
|
56
|
+
* `outcome = 'unmeasured'` rows are flipped, and the recurrence charge
|
|
57
|
+
* is `MAX(recurrence_count, n)` where n = all failing calls of the
|
|
58
|
+
* fingerprint after `injected_at` (a later idle re-computes n and
|
|
59
|
+
* catches up — plan §5.1 rule 4: 3 recurrences → stale).
|
|
60
|
+
*
|
|
61
|
+
* An ineffective injection also bumps the target memory's
|
|
62
|
+
* `recurrence_count` (negative evidence, plan §5.3) and stamps
|
|
63
|
+
* `last_injected_at`.
|
|
64
|
+
*/
|
|
65
|
+
settle(sessionId: string): void;
|
|
66
|
+
/**
|
|
67
|
+
* Per-fingerprint failing tool-call counts for the session. Feeds
|
|
68
|
+
* QualityGate.canInject and the HITL suggestion block.
|
|
69
|
+
*/
|
|
70
|
+
recurrencesFor(sessionId: string): Map<string, number>;
|
|
71
|
+
/**
|
|
72
|
+
* v0.4.0 (K4-017) — recurrence counts for the QualityGate at
|
|
73
|
+
* injection time: only failing calls that happened AFTER the
|
|
74
|
+
* fingerprint was already injected this session count. The failure
|
|
75
|
+
* that *created* a lesson is not a recurrence — it precedes any
|
|
76
|
+
* injection (plan §5.1 rule 4, same `ts >= injected_at` semantics
|
|
77
|
+
* `settle` uses).
|
|
78
|
+
*/
|
|
79
|
+
postInjectionRecurrencesFor(sessionId: string): Map<string, number>;
|
|
80
|
+
/** Number of rows for the session not yet settled (drives tests and settle). */
|
|
81
|
+
unsettledForSession(sessionId: string): number;
|
|
82
|
+
/** Latest ledger rows for a session, newest first (used by tests/tools). */
|
|
83
|
+
rowsForSession(sessionId: string): InjectionRow[];
|
|
84
|
+
}
|
|
85
|
+
export {};
|