@jmtrin/opencode-kevin 0.4.0 → 0.6.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 +580 -360
- package/dist/migrations/006_v05_glassbox.sql +118 -0
- package/dist/migrations/007_v06_pull.sql +145 -0
- package/dist/plugin/Archiver.d.ts +42 -0
- package/dist/plugin/Archiver.js +99 -0
- package/dist/plugin/Archiver.js.map +1 -0
- package/dist/plugin/ArtifactWriter.d.ts +50 -0
- package/dist/plugin/ArtifactWriter.js +240 -0
- package/dist/plugin/ArtifactWriter.js.map +1 -0
- package/dist/plugin/ContextInjector.d.ts +87 -0
- package/dist/plugin/ContextInjector.js +212 -44
- package/dist/plugin/ContextInjector.js.map +1 -1
- package/dist/plugin/Curator.d.ts +96 -0
- package/dist/plugin/Curator.js +252 -0
- package/dist/plugin/Curator.js.map +1 -0
- package/dist/plugin/Feedback.d.ts +67 -0
- package/dist/plugin/Feedback.js +138 -0
- package/dist/plugin/Feedback.js.map +1 -0
- package/dist/plugin/InjectionLedger.d.ts +9 -2
- package/dist/plugin/InjectionLedger.js +63 -9
- package/dist/plugin/InjectionLedger.js.map +1 -1
- package/dist/plugin/Materializer.d.ts +59 -0
- package/dist/plugin/Materializer.js +237 -0
- package/dist/plugin/Materializer.js.map +1 -0
- package/dist/plugin/MemoryService.d.ts +38 -0
- package/dist/plugin/MemoryService.js +265 -30
- package/dist/plugin/MemoryService.js.map +1 -1
- package/dist/plugin/Migrate.js +41 -0
- package/dist/plugin/Migrate.js.map +1 -1
- package/dist/plugin/QualityGate.d.ts +53 -0
- package/dist/plugin/QualityGate.js +50 -8
- package/dist/plugin/QualityGate.js.map +1 -1
- package/dist/plugin/Retrospective.d.ts +1 -0
- package/dist/plugin/Retrospective.js +24 -1
- package/dist/plugin/Retrospective.js.map +1 -1
- package/dist/plugin/capabilities.d.ts +15 -0
- package/dist/plugin/capabilities.js +42 -0
- package/dist/plugin/capabilities.js.map +1 -0
- package/dist/plugin/confidence.d.ts +3 -1
- package/dist/plugin/confidence.js +14 -2
- package/dist/plugin/confidence.js.map +1 -1
- package/dist/plugin/diff.d.ts +8 -0
- package/dist/plugin/diff.js +183 -0
- package/dist/plugin/diff.js.map +1 -0
- package/dist/plugin/index.d.ts +3 -1
- package/dist/plugin/index.js +371 -0
- package/dist/plugin/index.js.map +1 -1
- package/dist/plugin/inferability.d.ts +32 -0
- package/dist/plugin/inferability.js +89 -0
- package/dist/plugin/inferability.js.map +1 -0
- package/dist/plugin/kevin_approve.d.ts +34 -0
- package/dist/plugin/kevin_approve.js +50 -0
- package/dist/plugin/kevin_approve.js.map +1 -0
- package/dist/plugin/kevin_audit.d.ts +95 -0
- package/dist/plugin/kevin_audit.js +233 -0
- package/dist/plugin/kevin_audit.js.map +1 -0
- package/dist/plugin/kevin_propose.d.ts +23 -0
- package/dist/plugin/kevin_propose.js +15 -0
- package/dist/plugin/kevin_propose.js.map +1 -0
- package/dist/plugin/kevin_publish.d.ts +38 -0
- package/dist/plugin/kevin_publish.js +19 -0
- package/dist/plugin/kevin_publish.js.map +1 -0
- package/dist/plugin/kevin_why.js +23 -2
- package/dist/plugin/kevin_why.js.map +1 -1
- package/dist/plugin/metrics.d.ts +32 -1
- package/dist/plugin/metrics.js +86 -2
- package/dist/plugin/metrics.js.map +1 -1
- package/dist/plugin/replay-types.d.ts +327 -0
- package/dist/plugin/replay-types.js +65 -0
- package/dist/plugin/replay-types.js.map +1 -0
- package/dist/plugin/replay.d.ts +36 -0
- package/dist/plugin/replay.js +203 -0
- package/dist/plugin/replay.js.map +1 -0
- package/migrations/006_v05_glassbox.sql +118 -0
- package/migrations/007_v06_pull.sql +145 -0
- package/package.json +3 -2
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
import { homedir } from "node:os";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { MARKER_BEGIN, MARKER_END, } from "./ArtifactWriter.js";
|
|
4
|
+
import { computeConfidence } from "./confidence.js";
|
|
5
|
+
import { uuidv7 } from "./uuid.js";
|
|
6
|
+
const MAX_CANDIDATE_LINES = 20;
|
|
7
|
+
const MAX_CANDIDATE_CHARS = 4000;
|
|
8
|
+
const CONFIDENCE_FLOOR = 0.6;
|
|
9
|
+
const MAX_SENTENCE_CHARS = 160;
|
|
10
|
+
export function firstSentence(content) {
|
|
11
|
+
const match = content.match(/^[\s\S]*?(?=[.!?](?:\s|$)|\r?\n|$)/);
|
|
12
|
+
const sentence = (match ? match[0] : content).trim();
|
|
13
|
+
return sentence.slice(0, MAX_SENTENCE_CHARS);
|
|
14
|
+
}
|
|
15
|
+
function evidenceString(row) {
|
|
16
|
+
const parts = [];
|
|
17
|
+
if (row.evidence_count >= 2) {
|
|
18
|
+
const last = row.last_verified_at
|
|
19
|
+
? `, last ${row.last_verified_at.slice(0, 10)}`
|
|
20
|
+
: "";
|
|
21
|
+
parts.push(`verified ${row.evidence_count}×${last}`);
|
|
22
|
+
}
|
|
23
|
+
if (row.feedback_positive >= 1) {
|
|
24
|
+
parts.push(`feedback ${row.feedback_positive}×`);
|
|
25
|
+
}
|
|
26
|
+
return parts.join(", ");
|
|
27
|
+
}
|
|
28
|
+
export class Curator {
|
|
29
|
+
store;
|
|
30
|
+
memoryService;
|
|
31
|
+
projectId;
|
|
32
|
+
metrics;
|
|
33
|
+
constructor(store, memoryService, projectId, metrics) {
|
|
34
|
+
this.store = store;
|
|
35
|
+
this.memoryService = memoryService;
|
|
36
|
+
this.projectId = projectId;
|
|
37
|
+
this.metrics = metrics ?? null;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Plan §5.4 selection predicate — all clauses must hold, evaluated
|
|
41
|
+
* verbatim. The confidence floor cannot be expressed in SQL (it is the
|
|
42
|
+
* two-sided formula of `computeConfidence`), so the cheap clauses run
|
|
43
|
+
* in SQL and confidence is computed and filtered per row in JS.
|
|
44
|
+
*
|
|
45
|
+
* The floor is `confidence >= 0.6` where the feedback arm of D6-09 also
|
|
46
|
+
* clears it: `computeConfidence` credits one positive human verdict at
|
|
47
|
+
* 0.05 (K5-010), so a row with `feedback_positive = 1` and no causal
|
|
48
|
+
* evidence scores 0.55 — yet D6-09's warrant is "either the world
|
|
49
|
+
* verified it twice, or a human verified it once". The disjunction
|
|
50
|
+
* (K6-012 acceptance) is the floor's complement for the feedback arm.
|
|
51
|
+
*
|
|
52
|
+
* Ordered by `confidence DESC, updated_at DESC`, capped at 20 lines and
|
|
53
|
+
* 4000 characters of content, whichever binds first. The char budget
|
|
54
|
+
* counts the raw content length: rendered lines are truncated to 160
|
|
55
|
+
* chars, so a rendered-line budget could never bind before the line cap.
|
|
56
|
+
*/
|
|
57
|
+
candidates(limit) {
|
|
58
|
+
const rows = this.store
|
|
59
|
+
.prepare(`SELECT id, content, evidence_count, recurrence_count,
|
|
60
|
+
feedback_positive, feedback_negative,
|
|
61
|
+
last_verified_at, updated_at
|
|
62
|
+
FROM memories
|
|
63
|
+
WHERE status = 'active'
|
|
64
|
+
AND ignored = 0
|
|
65
|
+
AND curated = 0
|
|
66
|
+
AND (inferable IS NULL OR inferable != 1)
|
|
67
|
+
AND (evidence_count >= 2 OR feedback_positive >= 1)`)
|
|
68
|
+
.all();
|
|
69
|
+
const scored = rows
|
|
70
|
+
.map((row) => ({
|
|
71
|
+
row,
|
|
72
|
+
confidence: computeConfidence(row.evidence_count ?? 0, row.recurrence_count ?? 0, row.feedback_positive ?? 0, row.feedback_negative ?? 0),
|
|
73
|
+
}))
|
|
74
|
+
.filter((s) => s.confidence >= CONFIDENCE_FLOOR || s.row.feedback_positive >= 1)
|
|
75
|
+
.sort((a, b) => b.confidence - a.confidence ||
|
|
76
|
+
b.row.updated_at.localeCompare(a.row.updated_at));
|
|
77
|
+
const maxLines = limit ?? MAX_CANDIDATE_LINES;
|
|
78
|
+
const result = [];
|
|
79
|
+
let totalChars = 0;
|
|
80
|
+
for (const s of scored) {
|
|
81
|
+
if (result.length >= maxLines)
|
|
82
|
+
break;
|
|
83
|
+
if (totalChars + s.row.content.length > MAX_CANDIDATE_CHARS)
|
|
84
|
+
break;
|
|
85
|
+
const evidence = evidenceString(s.row);
|
|
86
|
+
result.push({
|
|
87
|
+
memoryId: s.row.id,
|
|
88
|
+
line: `- ${firstSentence(s.row.content)} (${evidence})`,
|
|
89
|
+
confidence: s.confidence,
|
|
90
|
+
evidence,
|
|
91
|
+
});
|
|
92
|
+
totalChars += s.row.content.length;
|
|
93
|
+
}
|
|
94
|
+
return result;
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Renders the block sorted by memory id (D6-10): confidence orders
|
|
98
|
+
* selection, id orders output. Adding one candidate to a set of ten
|
|
99
|
+
* must change exactly one line of the rendered block.
|
|
100
|
+
*/
|
|
101
|
+
renderBlock(candidates) {
|
|
102
|
+
const sorted = [...candidates].sort((a, b) => a.memoryId.localeCompare(b.memoryId));
|
|
103
|
+
return `${sorted.map((c) => c.line).join("\n")}\n`;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Plan §5.4 — strict dry run (D5-08): plan only, never write. Returns a
|
|
107
|
+
* pending proposal per call, or [] when no candidate clears the floor.
|
|
108
|
+
*
|
|
109
|
+
* The proposal is a whole-block proposal: the persisted `proposed_text`
|
|
110
|
+
* is the merged block (current block in the file + new candidate lines),
|
|
111
|
+
* so approving it replaces the file block with exactly what was reviewed.
|
|
112
|
+
* The current block is learned from `plan().before` — the Curator has no
|
|
113
|
+
* fs capability, the writer reads (D6-01). Both plan calls are
|
|
114
|
+
* deterministic, so a second propose() with unchanged inputs reproduces
|
|
115
|
+
* the persisted diff byte-identically.
|
|
116
|
+
*
|
|
117
|
+
* A new proposal supersedes prior pending (or rejected) rows for the
|
|
118
|
+
* same (project_id, kind, target_path) triple — §5.5's regeneration
|
|
119
|
+
* arrow. Rows are never deleted (the audit trail is append-only).
|
|
120
|
+
*
|
|
121
|
+
* The schema's `memory_id` column is singular while a whole-block
|
|
122
|
+
* proposal carries several ids; the contributing ids are stored joined
|
|
123
|
+
* by "," (uuidv7 ids contain no comma) and split on read.
|
|
124
|
+
*/
|
|
125
|
+
propose(kind, writer) {
|
|
126
|
+
const targetPath = this.targetPathFor(kind);
|
|
127
|
+
const candidates = this.candidates();
|
|
128
|
+
if (candidates.length === 0)
|
|
129
|
+
return [];
|
|
130
|
+
const newBlock = this.renderBlock(candidates);
|
|
131
|
+
const currentBlock = extractBlock(writer.plan(targetPath, newBlock).before);
|
|
132
|
+
// Merge: keep the current block verbatim and append only lines that
|
|
133
|
+
// are not already in it. Without the dedup, a block the user (or an
|
|
134
|
+
// approval) already pasted would be proposed doubled.
|
|
135
|
+
const currentLines = currentBlock === "" ? [] : currentBlock.split(/\r?\n/);
|
|
136
|
+
const newLines = newBlock
|
|
137
|
+
.split("\n")
|
|
138
|
+
.filter((l) => l !== "" && !currentLines.includes(l));
|
|
139
|
+
const mergedBlock = currentBlock === ""
|
|
140
|
+
? newBlock
|
|
141
|
+
: newLines.length === 0
|
|
142
|
+
? currentBlock
|
|
143
|
+
: `${currentBlock}${currentBlock.endsWith("\n") ? "" : "\n"}${newLines.join("\n")}\n`;
|
|
144
|
+
const plan = writer.plan(targetPath, mergedBlock);
|
|
145
|
+
const prior = this.store
|
|
146
|
+
.prepare(`SELECT id FROM curation_proposals
|
|
147
|
+
WHERE project_id = ? AND kind = ? AND target_path = ?
|
|
148
|
+
AND status IN ('pending', 'rejected')`)
|
|
149
|
+
.all(this.projectId, kind, targetPath);
|
|
150
|
+
for (const row of prior) {
|
|
151
|
+
this.transition(row.id, "supersede");
|
|
152
|
+
}
|
|
153
|
+
const memoryIds = candidates.map((c) => c.memoryId);
|
|
154
|
+
const id = uuidv7();
|
|
155
|
+
this.store
|
|
156
|
+
.prepare(`INSERT INTO curation_proposals
|
|
157
|
+
(id, project_id, memory_id, kind, target_path,
|
|
158
|
+
proposed_text, diff, status)
|
|
159
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, 'pending')`)
|
|
160
|
+
.run(id, this.projectId, memoryIds.join(","), kind, targetPath, mergedBlock, plan.diff);
|
|
161
|
+
this.metrics?.incr("proposals_created", 1);
|
|
162
|
+
const row = this.store
|
|
163
|
+
.prepare("SELECT created_at FROM curation_proposals WHERE id = ?")
|
|
164
|
+
.get(id);
|
|
165
|
+
return [
|
|
166
|
+
{
|
|
167
|
+
id,
|
|
168
|
+
kind,
|
|
169
|
+
targetPath,
|
|
170
|
+
memoryIds,
|
|
171
|
+
proposedText: mergedBlock,
|
|
172
|
+
diff: plan.diff,
|
|
173
|
+
status: "pending",
|
|
174
|
+
createdAt: row.created_at,
|
|
175
|
+
},
|
|
176
|
+
];
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Plan §5.5 state machine, as explicit transitions with an exhaustive
|
|
180
|
+
* switch — an unknown transition or an illegal source state throws
|
|
181
|
+
* rather than silently no-oping. `decided_at` stamps approve/reject/
|
|
182
|
+
* supersede, `applied_at` stamps apply.
|
|
183
|
+
*/
|
|
184
|
+
transition(proposalId, transition) {
|
|
185
|
+
const row = this.store
|
|
186
|
+
.prepare("SELECT status FROM curation_proposals WHERE id = ?")
|
|
187
|
+
.get(proposalId);
|
|
188
|
+
if (!row) {
|
|
189
|
+
throw new Error(`proposal not found: ${proposalId}`);
|
|
190
|
+
}
|
|
191
|
+
const status = row.status;
|
|
192
|
+
let next = null;
|
|
193
|
+
switch (transition) {
|
|
194
|
+
case "approve":
|
|
195
|
+
if (status === "pending")
|
|
196
|
+
next = "approved";
|
|
197
|
+
break;
|
|
198
|
+
case "reject":
|
|
199
|
+
if (status === "pending")
|
|
200
|
+
next = "rejected";
|
|
201
|
+
break;
|
|
202
|
+
case "apply":
|
|
203
|
+
if (status === "approved")
|
|
204
|
+
next = "applied";
|
|
205
|
+
break;
|
|
206
|
+
case "supersede":
|
|
207
|
+
if (status === "pending" || status === "rejected") {
|
|
208
|
+
next = "superseded";
|
|
209
|
+
}
|
|
210
|
+
break;
|
|
211
|
+
default:
|
|
212
|
+
throw new Error(`unknown transition: ${transition}`);
|
|
213
|
+
}
|
|
214
|
+
if (next === null) {
|
|
215
|
+
throw new Error(`illegal transition: ${status} -> ${transition}`);
|
|
216
|
+
}
|
|
217
|
+
if (next === "applied") {
|
|
218
|
+
this.store
|
|
219
|
+
.prepare("UPDATE curation_proposals SET status = 'applied', applied_at = datetime('now') WHERE id = ?")
|
|
220
|
+
.run(proposalId);
|
|
221
|
+
}
|
|
222
|
+
else {
|
|
223
|
+
this.store
|
|
224
|
+
.prepare("UPDATE curation_proposals SET status = ?, decided_at = datetime('now') WHERE id = ?")
|
|
225
|
+
.run(next, proposalId);
|
|
226
|
+
}
|
|
227
|
+
return next;
|
|
228
|
+
}
|
|
229
|
+
/** Plan §5.6 target paths. agents_md is a setting; skill/reference live under ~/.opencode-kevin. */
|
|
230
|
+
targetPathFor(kind) {
|
|
231
|
+
switch (kind) {
|
|
232
|
+
case "agents_md":
|
|
233
|
+
return this.memoryService.getSetting("agents_md_path", "AGENTS.md");
|
|
234
|
+
case "skill":
|
|
235
|
+
return join(homedir(), ".opencode-kevin", "skills", "project-knowledge.md");
|
|
236
|
+
case "reference":
|
|
237
|
+
return join(homedir(), ".opencode-kevin", "refs", "project-knowledge.md");
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
/** The block content currently between the markers, "" when absent or malformed. */
|
|
242
|
+
function extractBlock(before) {
|
|
243
|
+
const begin = before.indexOf(MARKER_BEGIN);
|
|
244
|
+
const end = before.indexOf(MARKER_END);
|
|
245
|
+
if (begin === -1 || end === -1 || end < begin)
|
|
246
|
+
return "";
|
|
247
|
+
return before
|
|
248
|
+
.slice(begin + MARKER_BEGIN.length, end)
|
|
249
|
+
.replace(/^\r?\n/, "")
|
|
250
|
+
.replace(/\r?\n$/, "");
|
|
251
|
+
}
|
|
252
|
+
//# sourceMappingURL=Curator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Curator.js","sourceRoot":"","sources":["../../plugin/Curator.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import type { Store } from "./Store.js";
|
|
2
|
+
import type { Metrics } from "./metrics.js";
|
|
3
|
+
/**
|
|
4
|
+
* v0.5.0 Feedback (K5-009 / plan §5.3, D5-02).
|
|
5
|
+
*
|
|
6
|
+
* The human-judgement half of the glassbox: the agent can report whether a
|
|
7
|
+
* memory was useful, wrong, outdated, or should be ignored. Verdicts are
|
|
8
|
+
* stored in `memory_feedback` (migration 006_v05_glassbox.sql) and folded
|
|
9
|
+
* into the memory's `feedback_positive` / `feedback_negative` counters —
|
|
10
|
+
* kept SEPARATE from `evidence_count` / `recurrence_count` by design: human
|
|
11
|
+
* judgement is evidence about the memory, causal counters are evidence
|
|
12
|
+
* about the world (the confidence-poisoning defect closed in v0.4.0).
|
|
13
|
+
*
|
|
14
|
+
* D5-07 — the `ignore` verdict is a hard lifecycle action, not a soft
|
|
15
|
+
* signal: the memory is stamped `ignored = 1`, which excludes it from
|
|
16
|
+
* retrieval (K5-008) and from the quality gate (K5-007).
|
|
17
|
+
*
|
|
18
|
+
* Schema:
|
|
19
|
+
* memory_feedback(id PK, memory_id, verdict, session_id, note, created_at)
|
|
20
|
+
* memories(feedback_positive, feedback_negative, ignored)
|
|
21
|
+
*/
|
|
22
|
+
export type FeedbackVerdict = "useful" | "wrong" | "outdated" | "ignore";
|
|
23
|
+
export interface FeedbackRecordInput {
|
|
24
|
+
memoryId: string;
|
|
25
|
+
verdict: FeedbackVerdict;
|
|
26
|
+
sessionId?: string | null;
|
|
27
|
+
note?: string | null;
|
|
28
|
+
}
|
|
29
|
+
export interface FeedbackRow {
|
|
30
|
+
id: string;
|
|
31
|
+
memoryId: string;
|
|
32
|
+
verdict: FeedbackVerdict;
|
|
33
|
+
sessionId: string | null;
|
|
34
|
+
note: string | null;
|
|
35
|
+
createdAt: string;
|
|
36
|
+
}
|
|
37
|
+
export interface FeedbackCounts {
|
|
38
|
+
positive: number;
|
|
39
|
+
negative: number;
|
|
40
|
+
}
|
|
41
|
+
export declare class Feedback {
|
|
42
|
+
private readonly store;
|
|
43
|
+
private readonly metrics;
|
|
44
|
+
private readonly now;
|
|
45
|
+
constructor(store: Store, metrics?: Metrics | null, now?: () => Date);
|
|
46
|
+
/**
|
|
47
|
+
* Records one human verdict and folds it into the memory's counters.
|
|
48
|
+
* Counters are RECOMPUTED from the table (never incremented) so rows
|
|
49
|
+
* deleted in tests or by hand cannot drift them.
|
|
50
|
+
*
|
|
51
|
+
* The `ignore` verdict also stamps `memories.ignored = 1` (D5-07).
|
|
52
|
+
* Returns the feedback row id.
|
|
53
|
+
*/
|
|
54
|
+
record(input: FeedbackRecordInput): string;
|
|
55
|
+
/** Human-judgement counters for one memory (from the row, not the table). */
|
|
56
|
+
countsFor(memoryId: string): FeedbackCounts;
|
|
57
|
+
/**
|
|
58
|
+
* Raw verdict history, newest first. Used by `kevin_feedback` (K5-011)
|
|
59
|
+
* and by the audit tool (K5-016).
|
|
60
|
+
*/
|
|
61
|
+
list(memoryId?: string, limit?: number): FeedbackRow[];
|
|
62
|
+
/**
|
|
63
|
+
* Recompute a memory's positive/negative counters straight from the
|
|
64
|
+
* verdict table. Kept private — `record` is the only mutation path.
|
|
65
|
+
*/
|
|
66
|
+
private recomputeCounters;
|
|
67
|
+
}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { uuidv7 } from "./uuid.js";
|
|
2
|
+
const POSITIVE_VERDICTS = ["useful"];
|
|
3
|
+
const NEGATIVE_VERDICTS = [
|
|
4
|
+
"wrong",
|
|
5
|
+
"outdated",
|
|
6
|
+
"ignore",
|
|
7
|
+
];
|
|
8
|
+
// v0.5.0 — pre-006 DBs lack `memory_feedback`; record() fails loudly with a
|
|
9
|
+
// descriptive error instead of a bare "no such table" (the tool layer turns
|
|
10
|
+
// it into a graceful message).
|
|
11
|
+
// v0.6.0 (K6-001a) — positive-only caching: a successful probe is cached,
|
|
12
|
+
// a failed probe is NOT. A Store migrated in place heals on the next call.
|
|
13
|
+
const feedbackTableCache = new WeakMap();
|
|
14
|
+
function hasFeedbackTable(store) {
|
|
15
|
+
const cached = feedbackTableCache.get(store);
|
|
16
|
+
if (cached === true)
|
|
17
|
+
return true;
|
|
18
|
+
try {
|
|
19
|
+
store.prepare("SELECT COUNT(*) FROM memory_feedback").get();
|
|
20
|
+
feedbackTableCache.set(store, true);
|
|
21
|
+
return true;
|
|
22
|
+
}
|
|
23
|
+
catch {
|
|
24
|
+
return false;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
export class Feedback {
|
|
28
|
+
store;
|
|
29
|
+
metrics;
|
|
30
|
+
now;
|
|
31
|
+
constructor(store, metrics, now = () => new Date()) {
|
|
32
|
+
this.store = store;
|
|
33
|
+
this.metrics = metrics ?? null;
|
|
34
|
+
this.now = now;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Records one human verdict and folds it into the memory's counters.
|
|
38
|
+
* Counters are RECOMPUTED from the table (never incremented) so rows
|
|
39
|
+
* deleted in tests or by hand cannot drift them.
|
|
40
|
+
*
|
|
41
|
+
* The `ignore` verdict also stamps `memories.ignored = 1` (D5-07).
|
|
42
|
+
* Returns the feedback row id.
|
|
43
|
+
*/
|
|
44
|
+
record(input) {
|
|
45
|
+
if (!hasFeedbackTable(this.store)) {
|
|
46
|
+
throw new Error("kevin_feedback requires migration 006_v05_glassbox.sql (missing table memory_feedback)");
|
|
47
|
+
}
|
|
48
|
+
const id = uuidv7();
|
|
49
|
+
this.store
|
|
50
|
+
.prepare(`INSERT INTO memory_feedback
|
|
51
|
+
(id, memory_id, verdict, session_id, note)
|
|
52
|
+
VALUES (?, ?, ?, ?, ?)`)
|
|
53
|
+
.run(id, input.memoryId, input.verdict, input.sessionId ?? null, input.note ?? null);
|
|
54
|
+
this.recomputeCounters(input.memoryId);
|
|
55
|
+
if (input.verdict === "ignore") {
|
|
56
|
+
this.store
|
|
57
|
+
.prepare("UPDATE memories SET ignored = 1 WHERE id = ?")
|
|
58
|
+
.run(input.memoryId);
|
|
59
|
+
}
|
|
60
|
+
// v0.5.0 (K5-023 / plan §5.3, D5-06) — lifecycle action of a
|
|
61
|
+
// negative verdict. `wrong` is an opinion and deserves a second
|
|
62
|
+
// opinion: it demotes only at feedback_negative >= 2. `outdated`
|
|
63
|
+
// is a self-verifying claim about the world and acts at once.
|
|
64
|
+
// `useful` confirms the memory and refreshes its verification.
|
|
65
|
+
if (input.verdict === "outdated") {
|
|
66
|
+
this.store
|
|
67
|
+
.prepare("UPDATE memories SET status = 'stale' WHERE id = ?")
|
|
68
|
+
.run(input.memoryId);
|
|
69
|
+
}
|
|
70
|
+
else if (input.verdict === "wrong") {
|
|
71
|
+
const counts = this.countsFor(input.memoryId);
|
|
72
|
+
if (counts.negative >= 2) {
|
|
73
|
+
this.store
|
|
74
|
+
.prepare("UPDATE memories SET status = 'stale' WHERE id = ?")
|
|
75
|
+
.run(input.memoryId);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
else if (input.verdict === "useful") {
|
|
79
|
+
this.store
|
|
80
|
+
.prepare("UPDATE memories SET last_verified_at = datetime('now') WHERE id = ?")
|
|
81
|
+
.run(input.memoryId);
|
|
82
|
+
}
|
|
83
|
+
if (POSITIVE_VERDICTS.includes(input.verdict)) {
|
|
84
|
+
this.metrics?.incr("feedback_positive_total", 1);
|
|
85
|
+
}
|
|
86
|
+
else if (NEGATIVE_VERDICTS.includes(input.verdict)) {
|
|
87
|
+
this.metrics?.incr("feedback_negative_total", 1);
|
|
88
|
+
}
|
|
89
|
+
return id;
|
|
90
|
+
}
|
|
91
|
+
/** Human-judgement counters for one memory (from the row, not the table). */
|
|
92
|
+
countsFor(memoryId) {
|
|
93
|
+
const row = this.store
|
|
94
|
+
.prepare("SELECT feedback_positive, feedback_negative FROM memories WHERE id = ?")
|
|
95
|
+
.get(memoryId);
|
|
96
|
+
if (!row)
|
|
97
|
+
return { positive: 0, negative: 0 };
|
|
98
|
+
return { positive: row.feedback_positive, negative: row.feedback_negative };
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Raw verdict history, newest first. Used by `kevin_feedback` (K5-011)
|
|
102
|
+
* and by the audit tool (K5-016).
|
|
103
|
+
*/
|
|
104
|
+
list(memoryId, limit = 50) {
|
|
105
|
+
const rows = this.store
|
|
106
|
+
.prepare(`SELECT id, memory_id, verdict, session_id, note, created_at
|
|
107
|
+
FROM memory_feedback
|
|
108
|
+
${memoryId ? "WHERE memory_id = ?" : ""}
|
|
109
|
+
ORDER BY created_at DESC, rowid DESC
|
|
110
|
+
LIMIT ?`)
|
|
111
|
+
.all(...(memoryId ? [memoryId, limit] : [limit]));
|
|
112
|
+
return rows.map((r) => ({
|
|
113
|
+
id: r.id,
|
|
114
|
+
memoryId: r.memory_id,
|
|
115
|
+
verdict: r.verdict,
|
|
116
|
+
sessionId: r.session_id,
|
|
117
|
+
note: r.note,
|
|
118
|
+
createdAt: r.created_at,
|
|
119
|
+
}));
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Recompute a memory's positive/negative counters straight from the
|
|
123
|
+
* verdict table. Kept private — `record` is the only mutation path.
|
|
124
|
+
*/
|
|
125
|
+
recomputeCounters(memoryId) {
|
|
126
|
+
this.store
|
|
127
|
+
.prepare(`UPDATE memories SET
|
|
128
|
+
feedback_positive = (
|
|
129
|
+
SELECT COUNT(*) FROM memory_feedback
|
|
130
|
+
WHERE memory_id = ? AND verdict = 'useful'),
|
|
131
|
+
feedback_negative = (
|
|
132
|
+
SELECT COUNT(*) FROM memory_feedback
|
|
133
|
+
WHERE memory_id = ? AND verdict IN ('wrong','outdated','ignore'))
|
|
134
|
+
WHERE id = ?`)
|
|
135
|
+
.run(memoryId, memoryId, memoryId);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
//# sourceMappingURL=Feedback.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Feedback.js","sourceRoot":"","sources":["../../plugin/Feedback.ts"],"names":[],"mappings":"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"}
|
|
@@ -22,7 +22,7 @@ import type { Metrics } from "./metrics.js";
|
|
|
22
22
|
* (ContextInjector.recordInjections) skips them.
|
|
23
23
|
*/
|
|
24
24
|
export type InjectionHook = "pre_prompt" | "compacting";
|
|
25
|
-
export type InjectionOutcome = "unmeasured" | "effective" | "ineffective";
|
|
25
|
+
export type InjectionOutcome = "unmeasured" | "effective" | "ineffective" | "inconclusive";
|
|
26
26
|
export interface InjectionRecordInput {
|
|
27
27
|
memoryId: string;
|
|
28
28
|
fingerprint: string;
|
|
@@ -60,7 +60,9 @@ export declare class InjectionLedger {
|
|
|
60
60
|
*
|
|
61
61
|
* An ineffective injection also bumps the target memory's
|
|
62
62
|
* `recurrence_count` (negative evidence, plan §5.3) and stamps
|
|
63
|
-
* `last_injected_at
|
|
63
|
+
* `last_injected_at` (monotonically — an older row can never
|
|
64
|
+
* regress a newer injection's timestamp; the column means "most
|
|
65
|
+
* recent injection").
|
|
64
66
|
*/
|
|
65
67
|
settle(sessionId: string): void;
|
|
66
68
|
/**
|
|
@@ -81,5 +83,10 @@ export declare class InjectionLedger {
|
|
|
81
83
|
unsettledForSession(sessionId: string): number;
|
|
82
84
|
/** Latest ledger rows for a session, newest first (used by tests/tools). */
|
|
83
85
|
rowsForSession(sessionId: string): InjectionRow[];
|
|
86
|
+
/**
|
|
87
|
+
* v0.5.0 (K5-005 / plan §8.4) — one grouped rollup over the whole
|
|
88
|
+
* ledger, zero-filled for every outcome. Consumed by `kevin_audit`.
|
|
89
|
+
*/
|
|
90
|
+
outcomeCounts(): Record<InjectionOutcome, number>;
|
|
84
91
|
}
|
|
85
92
|
export {};
|
|
@@ -29,13 +29,16 @@ export class InjectionLedger {
|
|
|
29
29
|
*
|
|
30
30
|
* An ineffective injection also bumps the target memory's
|
|
31
31
|
* `recurrence_count` (negative evidence, plan §5.3) and stamps
|
|
32
|
-
* `last_injected_at
|
|
32
|
+
* `last_injected_at` (monotonically — an older row can never
|
|
33
|
+
* regress a newer injection's timestamp; the column means "most
|
|
34
|
+
* recent injection").
|
|
33
35
|
*/
|
|
34
36
|
settle(sessionId) {
|
|
35
37
|
const injections = this.store
|
|
36
38
|
.prepare(`SELECT id, memory_id, fingerprint, injected_at, outcome
|
|
37
39
|
FROM kevin_injections
|
|
38
|
-
WHERE session_id =
|
|
40
|
+
WHERE session_id = ?
|
|
41
|
+
ORDER BY injected_at ASC, id ASC`)
|
|
39
42
|
.all(sessionId);
|
|
40
43
|
for (const inj of injections) {
|
|
41
44
|
// Same identity dimension CausalChain uses: the failing call's
|
|
@@ -67,6 +70,11 @@ export class InjectionLedger {
|
|
|
67
70
|
LIMIT 1`)
|
|
68
71
|
.get(sessionId, inj.fingerprint, inj.injected_at, originCallId, originCallId);
|
|
69
72
|
const n = countRow.n;
|
|
73
|
+
// v0.5.0 (K5-005 / plan §5.1, D5-01) — three-way settlement:
|
|
74
|
+
// recurrences >= 1 → ineffective (existing side effects unchanged)
|
|
75
|
+
// else fixes >= 1 → effective (a linked fix was OBSERVED)
|
|
76
|
+
// else → inconclusive (new majority bucket; excluded
|
|
77
|
+
// from the precision denominator)
|
|
70
78
|
if (n >= 1) {
|
|
71
79
|
if (inj.outcome === "unmeasured") {
|
|
72
80
|
this.store
|
|
@@ -78,9 +86,12 @@ export class InjectionLedger {
|
|
|
78
86
|
this.store
|
|
79
87
|
.prepare(`UPDATE memories
|
|
80
88
|
SET recurrence_count = MAX(recurrence_count, ?),
|
|
81
|
-
last_injected_at =
|
|
89
|
+
last_injected_at = CASE
|
|
90
|
+
WHEN last_injected_at IS NULL
|
|
91
|
+
OR ? > last_injected_at
|
|
92
|
+
THEN ? ELSE last_injected_at END
|
|
82
93
|
WHERE fingerprint = ? AND id = ?`)
|
|
83
|
-
.run(countRow.n, inj.injected_at, inj.fingerprint, inj.memory_id);
|
|
94
|
+
.run(countRow.n, inj.injected_at, inj.injected_at, inj.fingerprint, inj.memory_id);
|
|
84
95
|
// v0.4.0 (K4-025 / plan §5.1 rule 4, D4-06) — recurrence
|
|
85
96
|
// expels: a fingerprint at `recurrence_count >= 3` is
|
|
86
97
|
// demoted to `status='stale'` and never injected again
|
|
@@ -92,11 +103,35 @@ export class InjectionLedger {
|
|
|
92
103
|
.run(inj.memory_id);
|
|
93
104
|
}
|
|
94
105
|
else if (inj.outcome === "unmeasured") {
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
106
|
+
// Mirror of the recurrence predicate with the success flag
|
|
107
|
+
// inverted and the fingerprint matched on
|
|
108
|
+
// `fix_for_fingerprint` (populated by CausalChain.onSuccess,
|
|
109
|
+
// indexed by idx_tool_calls_fix_fp since migration 004). The
|
|
110
|
+
// `ts >= injected_at` bound and `session_id = ?` filter are
|
|
111
|
+
// kept; there is no `origin_call_id` exemption for fixes —
|
|
112
|
+
// a fix is not the creating call.
|
|
113
|
+
const fixRow = this.store
|
|
114
|
+
.prepare(`SELECT COUNT(*) AS m FROM tool_calls
|
|
115
|
+
WHERE session_id = ?
|
|
116
|
+
AND success = 1
|
|
117
|
+
AND fix_for_fingerprint = ?
|
|
118
|
+
AND ts >= ?
|
|
119
|
+
LIMIT 1`)
|
|
120
|
+
.get(sessionId, inj.fingerprint, inj.injected_at);
|
|
121
|
+
if (fixRow.m >= 1) {
|
|
122
|
+
this.store
|
|
123
|
+
.prepare(`UPDATE kevin_injections SET outcome = 'effective'
|
|
124
|
+
WHERE id = ?`)
|
|
125
|
+
.run(inj.id);
|
|
126
|
+
this.metrics?.incr("injections_effective", 1);
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
this.store
|
|
130
|
+
.prepare(`UPDATE kevin_injections SET outcome = 'inconclusive'
|
|
131
|
+
WHERE id = ?`)
|
|
132
|
+
.run(inj.id);
|
|
133
|
+
this.metrics?.incr("injections_inconclusive", 1);
|
|
134
|
+
}
|
|
100
135
|
}
|
|
101
136
|
}
|
|
102
137
|
}
|
|
@@ -165,6 +200,25 @@ export class InjectionLedger {
|
|
|
165
200
|
ORDER BY injected_at ASC, id ASC`)
|
|
166
201
|
.all(sessionId);
|
|
167
202
|
}
|
|
203
|
+
/**
|
|
204
|
+
* v0.5.0 (K5-005 / plan §8.4) — one grouped rollup over the whole
|
|
205
|
+
* ledger, zero-filled for every outcome. Consumed by `kevin_audit`.
|
|
206
|
+
*/
|
|
207
|
+
outcomeCounts() {
|
|
208
|
+
const rows = this.store
|
|
209
|
+
.prepare("SELECT outcome, COUNT(*) AS n FROM kevin_injections GROUP BY outcome")
|
|
210
|
+
.all();
|
|
211
|
+
const out = {
|
|
212
|
+
unmeasured: 0,
|
|
213
|
+
effective: 0,
|
|
214
|
+
ineffective: 0,
|
|
215
|
+
inconclusive: 0,
|
|
216
|
+
};
|
|
217
|
+
for (const r of rows) {
|
|
218
|
+
out[r.outcome] = r.n;
|
|
219
|
+
}
|
|
220
|
+
return out;
|
|
221
|
+
}
|
|
168
222
|
}
|
|
169
223
|
/**
|
|
170
224
|
* BUG-003 — read `origin_call_id` (the failing tool_call that CREATED the
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"InjectionLedger.js","sourceRoot":"","sources":["../../plugin/InjectionLedger.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"InjectionLedger.js","sourceRoot":"","sources":["../../plugin/InjectionLedger.ts"],"names":[],"mappings":"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"}
|