@jmtrin/opencode-kevin 1.2.0 → 1.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.
Files changed (140) hide show
  1. package/README.md +84 -22
  2. package/dist/plugin/index.d.ts +5 -4
  3. package/dist/plugin/index.js +127 -113
  4. package/dist/plugin/native.d.ts +2 -2
  5. package/dist/plugin/native.js +2 -2
  6. package/package.json +56 -68
  7. package/dist/migrations/001_initial.sql +0 -92
  8. package/dist/migrations/002_indexes.sql +0 -14
  9. package/dist/migrations/003_v02_signal.sql +0 -58
  10. package/dist/migrations/004_v03_knowledge.sql +0 -138
  11. package/dist/migrations/005_v04_signal.sql +0 -57
  12. package/dist/migrations/006_v05_glassbox.sql +0 -118
  13. package/dist/migrations/007_v06_pull.sql +0 -145
  14. package/dist/migrations/008_v07_truth.sql +0 -124
  15. package/dist/migrations/009_v08_team.sql +0 -100
  16. package/dist/migrations/010_v09_native.sql +0 -78
  17. package/dist/migrations/011_v10_proven.sql +0 -69
  18. package/dist/migrations/012_v11_drift.sql +0 -24
  19. package/dist/plugin/Archiver.d.ts +0 -42
  20. package/dist/plugin/Archiver.js +0 -83
  21. package/dist/plugin/ArtifactWriter.d.ts +0 -93
  22. package/dist/plugin/ArtifactWriter.js +0 -291
  23. package/dist/plugin/CausalChain.d.ts +0 -22
  24. package/dist/plugin/CausalChain.js +0 -197
  25. package/dist/plugin/ChatBridge.d.ts +0 -41
  26. package/dist/plugin/ChatBridge.js +0 -103
  27. package/dist/plugin/ConflictDetector.d.ts +0 -35
  28. package/dist/plugin/ConflictDetector.js +0 -260
  29. package/dist/plugin/ContextInjector.d.ts +0 -222
  30. package/dist/plugin/ContextInjector.js +0 -533
  31. package/dist/plugin/ConventionMiner.d.ts +0 -35
  32. package/dist/plugin/ConventionMiner.js +0 -242
  33. package/dist/plugin/Curator.d.ts +0 -116
  34. package/dist/plugin/Curator.js +0 -324
  35. package/dist/plugin/DashboardHtml.d.ts +0 -5
  36. package/dist/plugin/DashboardHtml.js +0 -180
  37. package/dist/plugin/Feedback.d.ts +0 -67
  38. package/dist/plugin/Feedback.js +0 -120
  39. package/dist/plugin/HookLiveness.d.ts +0 -88
  40. package/dist/plugin/HookLiveness.js +0 -322
  41. package/dist/plugin/InjectionLedger.d.ts +0 -98
  42. package/dist/plugin/InjectionLedger.js +0 -295
  43. package/dist/plugin/LessonFixer.d.ts +0 -44
  44. package/dist/plugin/LessonFixer.js +0 -45
  45. package/dist/plugin/Materializer.d.ts +0 -83
  46. package/dist/plugin/Materializer.js +0 -181
  47. package/dist/plugin/MemoryService.d.ts +0 -360
  48. package/dist/plugin/MemoryService.js +0 -1242
  49. package/dist/plugin/Migrate.d.ts +0 -17
  50. package/dist/plugin/Migrate.js +0 -271
  51. package/dist/plugin/PatternMiner.d.ts +0 -49
  52. package/dist/plugin/PatternMiner.js +0 -132
  53. package/dist/plugin/QualityGate.d.ts +0 -124
  54. package/dist/plugin/QualityGate.js +0 -119
  55. package/dist/plugin/Reflector.d.ts +0 -91
  56. package/dist/plugin/Reflector.js +0 -307
  57. package/dist/plugin/RepoIdentity.d.ts +0 -124
  58. package/dist/plugin/RepoIdentity.js +0 -300
  59. package/dist/plugin/RepoTruth.d.ts +0 -80
  60. package/dist/plugin/RepoTruth.js +0 -599
  61. package/dist/plugin/Retrospective.d.ts +0 -17
  62. package/dist/plugin/Retrospective.js +0 -216
  63. package/dist/plugin/SharedLayer.d.ts +0 -159
  64. package/dist/plugin/SharedLayer.js +0 -462
  65. package/dist/plugin/Store.d.ts +0 -29
  66. package/dist/plugin/Store.js +0 -50
  67. package/dist/plugin/ToolCallObserver.d.ts +0 -32
  68. package/dist/plugin/ToolCallObserver.js +0 -201
  69. package/dist/plugin/TuiActions.d.ts +0 -43
  70. package/dist/plugin/TuiActions.js +0 -181
  71. package/dist/plugin/TuiSnapshots.d.ts +0 -24
  72. package/dist/plugin/TuiSnapshots.js +0 -158
  73. package/dist/plugin/columns.d.ts +0 -11
  74. package/dist/plugin/columns.js +0 -54
  75. package/dist/plugin/confidence.d.ts +0 -8
  76. package/dist/plugin/confidence.js +0 -34
  77. package/dist/plugin/contract.d.ts +0 -54
  78. package/dist/plugin/contract.js +0 -386
  79. package/dist/plugin/diff.d.ts +0 -8
  80. package/dist/plugin/diff.js +0 -182
  81. package/dist/plugin/escape.d.ts +0 -39
  82. package/dist/plugin/escape.js +0 -60
  83. package/dist/plugin/fingerprint.d.ts +0 -27
  84. package/dist/plugin/fingerprint.js +0 -73
  85. package/dist/plugin/inferability.d.ts +0 -32
  86. package/dist/plugin/inferability.js +0 -88
  87. package/dist/plugin/kevin_approve.d.ts +0 -34
  88. package/dist/plugin/kevin_approve.js +0 -52
  89. package/dist/plugin/kevin_audit.d.ts +0 -229
  90. package/dist/plugin/kevin_audit.js +0 -631
  91. package/dist/plugin/kevin_bench.d.ts +0 -10
  92. package/dist/plugin/kevin_bench.js +0 -82
  93. package/dist/plugin/kevin_conflicts.d.ts +0 -9
  94. package/dist/plugin/kevin_conflicts.js +0 -50
  95. package/dist/plugin/kevin_contract.d.ts +0 -8
  96. package/dist/plugin/kevin_contract.js +0 -59
  97. package/dist/plugin/kevin_doctor.d.ts +0 -62
  98. package/dist/plugin/kevin_doctor.js +0 -196
  99. package/dist/plugin/kevin_facts.d.ts +0 -42
  100. package/dist/plugin/kevin_facts.js +0 -36
  101. package/dist/plugin/kevin_forget.d.ts +0 -33
  102. package/dist/plugin/kevin_forget.js +0 -260
  103. package/dist/plugin/kevin_native.d.ts +0 -29
  104. package/dist/plugin/kevin_native.js +0 -79
  105. package/dist/plugin/kevin_propose.d.ts +0 -23
  106. package/dist/plugin/kevin_propose.js +0 -14
  107. package/dist/plugin/kevin_publish.d.ts +0 -38
  108. package/dist/plugin/kevin_publish.js +0 -18
  109. package/dist/plugin/kevin_why.d.ts +0 -23
  110. package/dist/plugin/kevin_why.js +0 -111
  111. package/dist/plugin/memory-format.d.ts +0 -20
  112. package/dist/plugin/memory-format.js +0 -59
  113. package/dist/plugin/metrics.d.ts +0 -97
  114. package/dist/plugin/metrics.js +0 -288
  115. package/dist/plugin/okf-export.d.ts +0 -3
  116. package/dist/plugin/okf-export.js +0 -134
  117. package/dist/plugin/okf-import.d.ts +0 -76
  118. package/dist/plugin/okf-import.js +0 -271
  119. package/dist/plugin/okf.d.ts +0 -107
  120. package/dist/plugin/okf.js +0 -303
  121. package/dist/plugin/perf.d.ts +0 -35
  122. package/dist/plugin/perf.js +0 -174
  123. package/dist/plugin/query-tokenizer.d.ts +0 -13
  124. package/dist/plugin/query-tokenizer.js +0 -133
  125. package/dist/plugin/redact.d.ts +0 -2
  126. package/dist/plugin/redact.js +0 -18
  127. package/dist/plugin/replay-types.d.ts +0 -69
  128. package/dist/plugin/replay-types.js +0 -156
  129. package/dist/plugin/replay.d.ts +0 -36
  130. package/dist/plugin/replay.js +0 -202
  131. package/dist/plugin/sqlite-adapter.d.ts +0 -12
  132. package/dist/plugin/sqlite-adapter.js +0 -81
  133. package/dist/plugin/time-ms.d.ts +0 -1
  134. package/dist/plugin/time-ms.js +0 -16
  135. package/dist/plugin/tui-types.d.ts +0 -59
  136. package/dist/plugin/tui-types.js +0 -4
  137. package/dist/plugin/tui.d.ts +0 -18
  138. package/dist/plugin/tui.js +0 -198
  139. package/dist/plugin/uuid.d.ts +0 -1
  140. package/dist/plugin/uuid.js +0 -50
@@ -1,322 +0,0 @@
1
- import { HOOK_NAMES } from "./Migrate.js";
2
- const DEFAULT_THRESHOLD = 3;
3
- /**
4
- * v0.9.0 (K9-010 / plan §5.3, D9-09) — parse the TEXT setting
5
- * `dead_hook_report_threshold` into the [1, 1000] clamp. NaN and empty
6
- * fall back to 3 (never 0: a zero threshold would report every hook dead
7
- * before a single session completes a checkpoint).
8
- */
9
- export function parseThreshold(text) {
10
- if (text === null || text === undefined || text === "")
11
- return DEFAULT_THRESHOLD;
12
- const n = Number.parseInt(text, 10);
13
- // NaN → default; 0 is not a valid threshold (K9-010 AC: 'abc'/''/'0'
14
- // all clamp to the default of three — never zero).
15
- if (Number.isNaN(n) || n === 0)
16
- return DEFAULT_THRESHOLD;
17
- return Math.min(1000, Math.max(1, n));
18
- }
19
- /**
20
- * v0.9.0 (K9-009/K9-010 / plan §5.3) — wraps the plugin's hooks with a
21
- * success-path recorder, dedups per-session checkpoints, and reports each
22
- * hook's liveness state. Persistence is machine-scoped: the hook_liveness
23
- * table carries no project_id or repo_id (D9-08).
24
- */
25
- export class HookLiveness {
26
- store;
27
- options;
28
- // v1.1.0 (K11-015) — debug counter for excess arity; never logged on hot path
29
- excessArityCount = 0;
30
- counters;
31
- seenSessions;
32
- suppressedSessions = new Set();
33
- threshold;
34
- pluginVersion;
35
- constructor(store, options) {
36
- this.store = store;
37
- this.options = options;
38
- this.threshold = parseThreshold(options.thresholdText);
39
- this.pluginVersion = options.pluginVersion;
40
- this.counters = new Map();
41
- this.seenSessions = new Map();
42
- for (const hook of HOOK_NAMES) {
43
- this.counters.set(hook, {
44
- fireCount: 0,
45
- errorCount: 0,
46
- expectedCount: 0,
47
- firstSeenAt: null,
48
- lastSeenAt: null,
49
- deadSince: null,
50
- });
51
- this.seenSessions.set(hook, new Set());
52
- }
53
- this.loadFromDb();
54
- }
55
- /**
56
- * Returns a new object with the same keys; function values are replaced
57
- * by delegating wrappers, non-function values (the tool map) pass by
58
- * reference. When liveness is disabled, returns the argument unchanged —
59
- * the same object, not a copy.
60
- */
61
- wrap(hooks) {
62
- if (!this.options.enabled)
63
- return hooks;
64
- const out = {};
65
- for (const [key, value] of Object.entries(hooks)) {
66
- if (typeof value === "function") {
67
- out[key] = this.makeWrapper(key, value);
68
- }
69
- else {
70
- out[key] = value;
71
- }
72
- }
73
- return out;
74
- }
75
- /**
76
- * v0.9.0 (K9-010 / plan §5.3) — a checkpoint: the session reached a
77
- * model turn (tool.execute.after fired), so the system prompt was
78
- * assembled and `experimental.chat.system.transform` MUST have been
79
- * offered. Deduped per session: 20 tool calls in one session count
80
- * exactly one expectation.
81
- */
82
- expect(hook, sessionID) {
83
- const seen = this.seenSessions.get(hook);
84
- if (!seen || seen.has(sessionID))
85
- return;
86
- seen.add(sessionID);
87
- const counters = this.counters.get(hook);
88
- if (!counters)
89
- return;
90
- counters.expectedCount += 1;
91
- // The dead flag is materialized here (not only in report()) so that
92
- // flush() persists dead_since even when no report() was ever called:
93
- // kevin_doctor's PURE-SQL blocks read dead_since from the table.
94
- if (counters.fireCount === 0 &&
95
- counters.expectedCount >= this.threshold &&
96
- counters.deadSince === null) {
97
- counters.deadSince = counters.lastSeenAt ?? new Date().toISOString();
98
- }
99
- // v0.9.0 (K9-011 / plan §5.3) — when the injection hook is dead, the
100
- // checkpointed session was suppressed: the host stopped offering the
101
- // transform, so no injection could happen. Counted once per session
102
- // (same dedup as expected_count) and persisted by flush(). This is
103
- // the counter that turns "zero injections" from an ambiguous number
104
- // into a diagnosis.
105
- if (hook === "experimental.chat.system.transform" &&
106
- counters.deadSince !== null &&
107
- !this.suppressedSessions.has(sessionID)) {
108
- this.suppressedSessions.add(sessionID);
109
- }
110
- }
111
- /**
112
- * Per-hook liveness verdicts in canonical HOOK_NAMES order.
113
- * A hook that fired once is live forever; dead requires both zero fires
114
- * and `expectedCount >= threshold`; everything else is unknown — and
115
- * unknown is never rounded to healthy (D9-09).
116
- */
117
- report() {
118
- const out = [];
119
- for (const hook of HOOK_NAMES) {
120
- const c = this.counters.get(hook);
121
- if (!c)
122
- continue;
123
- let state;
124
- if (c.fireCount > 0) {
125
- state = "live";
126
- }
127
- else if (c.expectedCount >= this.threshold) {
128
- // dead_since is set ONCE and never cleared: it documents the
129
- // historical death even if the hook later recovers.
130
- if (c.deadSince === null) {
131
- c.deadSince = c.lastSeenAt ?? new Date().toISOString();
132
- }
133
- state = "dead";
134
- }
135
- else {
136
- state = "unknown";
137
- }
138
- out.push({
139
- hook,
140
- experimental: hook.startsWith("experimental."),
141
- state,
142
- firstSeenAt: c.firstSeenAt,
143
- lastSeenAt: c.lastSeenAt,
144
- fireCount: c.fireCount,
145
- expectedCount: c.expectedCount,
146
- deadSince: c.deadSince,
147
- });
148
- }
149
- return out;
150
- }
151
- /** Persists every counter row. Called on the metrics.flush() cadence. */
152
- flush() {
153
- const store = this.store;
154
- store.transaction(() => {
155
- // Created lazily on first flush, mirroring Metrics.flush(): the
156
- // migration guarantees the table in production, but unit tests
157
- // against pre-010 DBs must not explode at dispose.
158
- store.exec(`CREATE TABLE IF NOT EXISTS hook_liveness (
159
- hook TEXT PRIMARY KEY,
160
- experimental INTEGER NOT NULL DEFAULT 0,
161
- fire_count INTEGER NOT NULL DEFAULT 0,
162
- error_count INTEGER NOT NULL DEFAULT 0,
163
- expected_count INTEGER NOT NULL DEFAULT 0,
164
- first_seen_at TEXT,
165
- last_seen_at TEXT,
166
- dead_since TEXT,
167
- plugin_version TEXT
168
- )`);
169
- const upsert = store.prepare(`INSERT INTO hook_liveness (hook, experimental, fire_count, error_count,
170
- expected_count, first_seen_at, last_seen_at, dead_since, plugin_version)
171
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
172
- ON CONFLICT(hook) DO UPDATE SET
173
- experimental = excluded.experimental,
174
- fire_count = excluded.fire_count,
175
- error_count = excluded.error_count,
176
- expected_count = excluded.expected_count,
177
- first_seen_at = excluded.first_seen_at,
178
- last_seen_at = excluded.last_seen_at,
179
- dead_since = excluded.dead_since,
180
- plugin_version = excluded.plugin_version`);
181
- for (const [hook, c] of this.counters) {
182
- upsert.run(hook, hook.startsWith("experimental.") ? 1 : 0, c.fireCount, c.errorCount, c.expectedCount, c.firstSeenAt, c.lastSeenAt, c.deadSince, this.pluginVersion);
183
- }
184
- // v0.9.0 (K9-011 / plan §5.3) — the four v0.9.0 counters are
185
- // re-derived here, on the metrics cadence, exactly like the 009
186
- // post-apply hook re-derives hooks_dead_total: SUM(fire_count),
187
- // SUM(error_count), COUNT(dead_since) and the per-session
188
- // suppression count. No Metrics dependency, no writes in the hot
189
- // path — flush() is the single persistence point.
190
- let fireSum = 0;
191
- let errorSum = 0;
192
- let deadCount = 0;
193
- for (const c of this.counters.values()) {
194
- fireSum += c.fireCount;
195
- errorSum += c.errorCount;
196
- if (c.deadSince !== null)
197
- deadCount += 1;
198
- }
199
- store.exec(`CREATE TABLE IF NOT EXISTS kevin_metrics (
200
- key TEXT PRIMARY KEY,
201
- value INTEGER NOT NULL DEFAULT 0,
202
- updated_at TEXT NOT NULL DEFAULT (datetime('now'))
203
- )`);
204
- const derive = store.prepare(`INSERT INTO kevin_metrics (key, value, updated_at)
205
- VALUES (?, ?, datetime('now'))
206
- ON CONFLICT(key) DO UPDATE SET
207
- value = excluded.value,
208
- updated_at = datetime('now')`);
209
- derive.run("hook_fires_total", fireSum);
210
- derive.run("hook_errors_total", errorSum);
211
- derive.run("hooks_dead_total", deadCount);
212
- derive.run("injections_suppressed_dead_hook", this.suppressedSessions.size);
213
- });
214
- }
215
- makeWrapper(key, delegate) {
216
- const record = () => {
217
- // v1.0.0 (K10-013) — dispose is recorded inside its own delegate
218
- // (via recordDispose()) rather than here: the wrapper's post-return
219
- // record would land after the store is closed and could never be
220
- // persisted — the event being detected is the process ending.
221
- if (key === "dispose")
222
- return;
223
- if (HOOK_NAMES.includes(key)) {
224
- this.recordSuccess(key);
225
- }
226
- };
227
- const recordError = (e) => {
228
- if (HOOK_NAMES.includes(key)) {
229
- this.recordError(key);
230
- }
231
- throw e;
232
- };
233
- // v1.1.0 (K11-015) — arity guard: maximum supported arity is 2 (plan §5.5).
234
- // Excess args are sliced and counted via excessArityCount; never logged.
235
- return async (...args) => {
236
- let callArgs = args;
237
- if (args.length > 2) {
238
- this.excessArityCount++;
239
- callArgs = args.slice(0, 2);
240
- }
241
- try {
242
- const result = await delegate(...callArgs);
243
- record();
244
- return result;
245
- }
246
- catch (e) {
247
- return recordError(e);
248
- }
249
- };
250
- }
251
- /**
252
- * v1.0.0 (K10-013 / plan §5.3) — records the `dispose` fire and
253
- * flushes immediately, from inside the dispose delegate: this is the
254
- * last write of the process, and nothing after it can persist.
255
- */
256
- recordDispose() {
257
- this.recordSuccess("dispose");
258
- this.flush();
259
- }
260
- recordSuccess(hook) {
261
- const c = this.counters.get(hook);
262
- if (!c)
263
- return;
264
- c.fireCount += 1;
265
- const now = new Date().toISOString();
266
- if (c.firstSeenAt === null)
267
- c.firstSeenAt = now;
268
- c.lastSeenAt = now;
269
- }
270
- recordError(hook) {
271
- const c = this.counters.get(hook);
272
- if (!c)
273
- return;
274
- c.errorCount += 1;
275
- }
276
- loadFromDb() {
277
- let rows = [];
278
- try {
279
- rows = this.store
280
- .prepare("SELECT hook, fire_count, error_count, expected_count, first_seen_at, last_seen_at, dead_since FROM hook_liveness")
281
- .all();
282
- }
283
- catch {
284
- // Graceful: pre-010 DBs have no hook_liveness table; the seeded
285
- // zeros remain until the first flush creates rows.
286
- rows = [];
287
- }
288
- for (const row of rows) {
289
- if (!HOOK_NAMES.includes(row.hook))
290
- continue;
291
- const c = this.counters.get(row.hook);
292
- if (!c)
293
- continue;
294
- c.fireCount = row.fire_count;
295
- c.errorCount = row.error_count;
296
- c.expectedCount = row.expected_count;
297
- c.firstSeenAt = row.first_seen_at;
298
- c.lastSeenAt = row.last_seen_at;
299
- c.deadSince = row.dead_since;
300
- }
301
- }
302
- }
303
- export function reduceVerdict(reports) {
304
- if (reports.length === 0) {
305
- return { verdict: "unknown", reason: "no hook reports yet" };
306
- }
307
- const dead = reports.filter((r) => r.state === "dead");
308
- if (dead.length > 0) {
309
- return {
310
- verdict: "degraded",
311
- reason: `${dead.map((r) => r.hook).join(", ")} dead since ${dead[0].deadSince ?? "unknown"}; ${dead.length} affected hook(s)`,
312
- };
313
- }
314
- if (reports.every((r) => r.state === "live")) {
315
- return { verdict: "healthy", reason: "all hooks live" };
316
- }
317
- const pending = reports.filter((r) => r.state === "unknown").length;
318
- return {
319
- verdict: "unknown",
320
- reason: `${pending} hook(s) without checkpoint`,
321
- };
322
- }
@@ -1,98 +0,0 @@
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" | "inconclusive";
26
- export interface InjectionRecordInput {
27
- memoryId: string;
28
- fingerprint: string;
29
- sessionId: string;
30
- hook: InjectionHook;
31
- tokens: number;
32
- /**
33
- * v0.8.0 (K8-024 / plan §5.7) — the memory's layer, passed by the
34
- * caller (the injector already knows it — no lookup on the hot path).
35
- * Drives the `injections_from_shared` counter.
36
- */
37
- layer?: string | null;
38
- }
39
- interface InjectionRow {
40
- id: string;
41
- memory_id: string;
42
- fingerprint: string;
43
- session_id: string;
44
- hook: InjectionHook;
45
- tokens: number;
46
- injected_at: string;
47
- outcome: InjectionOutcome;
48
- }
49
- export declare class InjectionLedger {
50
- private readonly store;
51
- private readonly metrics;
52
- constructor(store: Store, metrics?: Metrics | null);
53
- /**
54
- * Records one injected memory. Idempotent at the row level (UUID PK);
55
- * duplicates are expected only via the caller's per-session seen-set.
56
- */
57
- record(input: InjectionRecordInput): void;
58
- /**
59
- * Settles every unmeasured injection of the session: a fingerprint that
60
- * failed again (as a failing tool_call) after the injection is
61
- * `ineffective`, otherwise `effective`. Idempotent — only
62
- * `outcome = 'unmeasured'` rows are flipped, and the recurrence charge
63
- * is `MAX(recurrence_count, n)` where n = all failing calls of the
64
- * fingerprint after `injected_at` (a later idle re-computes n and
65
- * catches up — plan §5.1 rule 4: 3 recurrences → stale).
66
- *
67
- * An ineffective injection also bumps the target memory's
68
- * `recurrence_count` (negative evidence, plan §5.3) and stamps
69
- * `last_injected_at` (monotonically — an older row can never
70
- * regress a newer injection's timestamp; the column means "most
71
- * recent injection").
72
- */
73
- settle(sessionId: string): void;
74
- /**
75
- * Per-fingerprint failing tool-call counts for the session. Feeds
76
- * QualityGate.canInject and the HITL suggestion block.
77
- */
78
- recurrencesFor(sessionId: string): Map<string, number>;
79
- /**
80
- * v0.4.0 (K4-017) — recurrence counts for the QualityGate at
81
- * injection time: only failing calls that happened AFTER the
82
- * fingerprint was already injected this session count. The failure
83
- * that *created* a lesson is not a recurrence — it precedes any
84
- * injection (plan §5.1 rule 4, same `ts >= injected_at` semantics
85
- * `settle` uses).
86
- */
87
- postInjectionRecurrencesFor(sessionId: string): Map<string, number>;
88
- /** Number of rows for the session not yet settled (drives tests and settle). */
89
- unsettledForSession(sessionId: string): number;
90
- /** Latest ledger rows for a session, newest first (used by tests/tools). */
91
- rowsForSession(sessionId: string): InjectionRow[];
92
- /**
93
- * v0.5.0 (K5-005 / plan §8.4) — one grouped rollup over the whole
94
- * ledger, zero-filled for every outcome. Consumed by `kevin_audit`.
95
- */
96
- outcomeCounts(): Record<InjectionOutcome, number>;
97
- }
98
- export {};