@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.
Files changed (55) hide show
  1. package/README.md +38 -6
  2. package/dist/migrations/005_v04_signal.sql +57 -0
  3. package/dist/plugin/CausalChain.d.ts +13 -2
  4. package/dist/plugin/CausalChain.js +83 -13
  5. package/dist/plugin/CausalChain.js.map +1 -1
  6. package/dist/plugin/ContextInjector.d.ts +80 -4
  7. package/dist/plugin/ContextInjector.js +262 -88
  8. package/dist/plugin/ContextInjector.js.map +1 -1
  9. package/dist/plugin/InjectionLedger.d.ts +85 -0
  10. package/dist/plugin/InjectionLedger.js +189 -0
  11. package/dist/plugin/InjectionLedger.js.map +1 -0
  12. package/dist/plugin/LessonFixer.d.ts +44 -0
  13. package/dist/plugin/LessonFixer.js +46 -0
  14. package/dist/plugin/LessonFixer.js.map +1 -0
  15. package/dist/plugin/MemoryService.d.ts +62 -1
  16. package/dist/plugin/MemoryService.js +206 -43
  17. package/dist/plugin/MemoryService.js.map +1 -1
  18. package/dist/plugin/Migrate.js +10 -0
  19. package/dist/plugin/Migrate.js.map +1 -1
  20. package/dist/plugin/QualityGate.d.ts +71 -0
  21. package/dist/plugin/QualityGate.js +78 -0
  22. package/dist/plugin/QualityGate.js.map +1 -0
  23. package/dist/plugin/Reflector.d.ts +17 -0
  24. package/dist/plugin/Reflector.js +81 -26
  25. package/dist/plugin/Reflector.js.map +1 -1
  26. package/dist/plugin/Retrospective.js +10 -1
  27. package/dist/plugin/Retrospective.js.map +1 -1
  28. package/dist/plugin/ToolCallObserver.d.ts +1 -0
  29. package/dist/plugin/ToolCallObserver.js +15 -8
  30. package/dist/plugin/ToolCallObserver.js.map +1 -1
  31. package/dist/plugin/confidence.d.ts +6 -0
  32. package/dist/plugin/confidence.js +23 -0
  33. package/dist/plugin/confidence.js.map +1 -0
  34. package/dist/plugin/index.d.ts +5 -0
  35. package/dist/plugin/index.js +216 -44
  36. package/dist/plugin/index.js.map +1 -1
  37. package/dist/plugin/kevin_why.d.ts +4 -0
  38. package/dist/plugin/kevin_why.js +51 -35
  39. package/dist/plugin/kevin_why.js.map +1 -1
  40. package/dist/plugin/memory-format.d.ts +12 -0
  41. package/dist/plugin/memory-format.js +45 -5
  42. package/dist/plugin/memory-format.js.map +1 -1
  43. package/dist/plugin/metrics.d.ts +7 -1
  44. package/dist/plugin/metrics.js +16 -0
  45. package/dist/plugin/metrics.js.map +1 -1
  46. package/dist/plugin/okf-export.js +63 -22
  47. package/dist/plugin/okf-export.js.map +1 -1
  48. package/dist/plugin/okf-import.d.ts +4 -1
  49. package/dist/plugin/okf-import.js +45 -12
  50. package/dist/plugin/okf-import.js.map +1 -1
  51. package/dist/plugin/query-tokenizer.d.ts +13 -0
  52. package/dist/plugin/query-tokenizer.js +86 -0
  53. package/dist/plugin/query-tokenizer.js.map +1 -0
  54. package/migrations/005_v04_signal.sql +57 -0
  55. 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"}
@@ -29,10 +29,18 @@ export interface Memory {
29
29
  lastVerifiedAt?: string | null;
30
30
  /** v0.3.0 — lifecycle status. Default 'active'. */
31
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;
32
37
  }
33
38
  export interface SaveInput {
34
39
  type: MemoryType;
35
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;
36
44
  scope?: MemoryScope;
37
45
  relevanceScore?: number;
38
46
  sourceTool?: string;
@@ -51,6 +59,9 @@ export interface SaveInput {
51
59
  lastVerifiedAt?: string;
52
60
  /** v0.3.0 — lifecycle status. Default 'active'. */
53
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;
54
65
  }
55
66
  export interface QueryInput {
56
67
  text: string;
@@ -65,6 +76,10 @@ export interface QueryInput {
65
76
  /** v0.3.0 — when true, includes rows where status = 'superseded'.
66
77
  * Default false (only active rows). */
67
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;
68
83
  }
69
84
  /** v0.2.0 — slim query payload (K2-010). Snippet is a short content prefix;
70
85
  * `score` is the FTS5 BM25 score when available, falling back to
@@ -76,6 +91,14 @@ export interface SlimMemory {
76
91
  score: number;
77
92
  snippet: string;
78
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
+ }
79
102
  export interface GetRelevantInput {
80
103
  query?: string;
81
104
  maxTokens?: number;
@@ -83,25 +106,56 @@ export interface GetRelevantInput {
83
106
  /** v0.3.0 — when true, includes rows where status = 'superseded'.
84
107
  * Default false (only active rows). */
85
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;
86
116
  }
87
117
  export declare class MemoryService {
88
118
  private readonly metrics;
89
119
  constructor(store: Store, metrics?: Metrics | null);
90
120
  private store;
121
+ private hasRecurrenceColumn;
122
+ private _hasRecurrenceColumn;
91
123
  save(input: SaveInput): string;
92
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;
93
131
  update(id: string, fields: Partial<Memory>): void;
94
132
  delete(id: string): void;
95
133
  /** v0.1.x behavior — returns full `Memory` rows. */
96
134
  query(input: QueryInput & {
97
135
  full: true;
98
136
  }): Memory[];
137
+ /** v0.3.0 (BUG-001) — slim rows carrying the evidence fields. */
138
+ query(input: QueryInput & {
139
+ evidence: true;
140
+ }): SlimMemoryWithEvidence[];
99
141
  /** v0.2.0 default — returns `SlimMemory` rows. */
100
142
  query(input: QueryInput): SlimMemory[];
101
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;
102
150
  private loadAll;
103
151
  private queryRelevant;
104
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;
105
159
  /**
106
160
  * v0.3.0 (K3-004) — Promote an error memory to a causal pattern.
107
161
  *
@@ -111,7 +165,14 @@ export declare class MemoryService {
111
165
  * or null when the source error is not eligible (missing fingerprint,
112
166
  * wrong type, or already promoted).
113
167
  */
114
- promoteToPattern(errorId: string, evidenceCount: number): string | null;
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;
115
176
  /**
116
177
  * v0.2.0 (K2-026) — Feedback loop positive half (plan §B6.10 / D2-10).
117
178
  *