@psiclawops/hypermem 0.6.2 → 0.7.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.
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Contradiction Detector — heuristic-based contradiction detection for the fact store.
3
+ *
4
+ * Detects when a newly ingested fact contradicts existing active facts using
5
+ * vector similarity (when available) and FTS candidate retrieval, scored by
6
+ * pattern-based heuristics (negation, numeric conflict, state conflict, temporal).
7
+ *
8
+ * No LLM calls — v1 is purely heuristic. LLM-enhanced scoring is a future item.
9
+ */
10
+ import type { FactStore } from './fact-store.js';
11
+ import type { VectorStore } from './vector-store.js';
12
+ export interface ContradictionCandidate {
13
+ existingFactId: number;
14
+ existingContent: string;
15
+ similarityScore: number;
16
+ contradictionScore: number;
17
+ reason: string;
18
+ }
19
+ export interface ContradictionResult {
20
+ contradictions: ContradictionCandidate[];
21
+ autoResolved: boolean;
22
+ resolvedCount: number;
23
+ }
24
+ export interface ContradictionDetectorConfig {
25
+ /** Minimum similarity to consider as candidate. Default: 0.6 */
26
+ minSimilarity?: number;
27
+ /** Minimum contradiction score for auto-resolution. Default: 0.85 */
28
+ autoResolveThreshold?: number;
29
+ /** Max candidates to evaluate per ingest. Default: 10 */
30
+ maxCandidates?: number;
31
+ /** Enable auto-resolution. Default: true */
32
+ autoResolve?: boolean;
33
+ }
34
+ export declare class ContradictionDetector {
35
+ private readonly factStore;
36
+ private readonly vectorStore?;
37
+ private readonly config;
38
+ constructor(factStore: FactStore, vectorStore?: VectorStore | undefined, config?: ContradictionDetectorConfig);
39
+ /**
40
+ * On fact ingest, check if the new fact contradicts existing active facts.
41
+ * Uses vector similarity (when available) + FTS to find candidates, then
42
+ * scores each candidate with heuristic contradiction checks.
43
+ */
44
+ detectOnIngest(agentId: string, newFact: {
45
+ content: string;
46
+ domain?: string;
47
+ }): Promise<ContradictionResult>;
48
+ /**
49
+ * Resolve a detected contradiction between an existing fact and a new fact.
50
+ */
51
+ resolveContradiction(oldFactId: number, newFactId: number, resolution: 'supersede' | 'keep-both' | 'reject-new'): void;
52
+ /**
53
+ * Auto-resolve high-confidence contradictions: newer supersedes older.
54
+ * Only resolves candidates above the autoResolveThreshold.
55
+ *
56
+ * @param agentId - The agent whose facts are being resolved (for audit trail)
57
+ * @param candidates - Scored contradiction candidates from detectOnIngest
58
+ * @returns Count of auto-resolved contradictions
59
+ */
60
+ autoResolve(_agentId: string, candidates: ContradictionCandidate[]): Promise<number>;
61
+ /**
62
+ * Find candidate facts that might contradict the new fact.
63
+ * Uses vector search (if available) and FTS, deduplicates, and returns
64
+ * up to maxCandidates results above minSimilarity.
65
+ */
66
+ private findCandidates;
67
+ /**
68
+ * Score a candidate fact against the new fact content for contradiction.
69
+ * Returns a ContradictionCandidate if any heuristic fires, null otherwise.
70
+ */
71
+ private scoreContradiction;
72
+ /**
73
+ * Compute Jaccard-like token overlap between two texts.
74
+ * Returns 0-1 where 1 means identical token sets.
75
+ */
76
+ private tokenOverlapSimilarity;
77
+ }
78
+ //# sourceMappingURL=contradiction-detector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contradiction-detector.d.ts","sourceRoot":"","sources":["../src/contradiction-detector.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AACjD,OAAO,KAAK,EAAE,WAAW,EAAsB,MAAM,mBAAmB,CAAC;AAIzE,MAAM,WAAW,sBAAsB;IACrC,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC;IACxB,eAAe,EAAE,MAAM,CAAC;IACxB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,mBAAmB;IAClC,cAAc,EAAE,sBAAsB,EAAE,CAAC;IACzC,YAAY,EAAE,OAAO,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,2BAA2B;IAC1C,gEAAgE;IAChE,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,qEAAqE;IACrE,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,yDAAyD;IACzD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,4CAA4C;IAC5C,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAkKD,qBAAa,qBAAqB;IAI9B,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAC;IAJ/B,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAwC;gBAG5C,SAAS,EAAE,SAAS,EACpB,WAAW,CAAC,EAAE,WAAW,YAAA,EAC1C,MAAM,CAAC,EAAE,2BAA2B;IAKtC;;;;OAIG;IACG,cAAc,CAClB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,GAC5C,OAAO,CAAC,mBAAmB,CAAC;IAuB/B;;OAEG;IACH,oBAAoB,CAClB,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,WAAW,GAAG,WAAW,GAAG,YAAY,GACnD,IAAI;IAcP;;;;;;;OAOG;IACG,WAAW,CACf,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,sBAAsB,EAAE,GACnC,OAAO,CAAC,MAAM,CAAC;IAoBlB;;;;OAIG;YACW,cAAc;IA0E5B;;;OAGG;IACH,OAAO,CAAC,kBAAkB;IA0D1B;;;OAGG;IACH,OAAO,CAAC,sBAAsB;CAa/B"}
@@ -0,0 +1,362 @@
1
+ /**
2
+ * Contradiction Detector — heuristic-based contradiction detection for the fact store.
3
+ *
4
+ * Detects when a newly ingested fact contradicts existing active facts using
5
+ * vector similarity (when available) and FTS candidate retrieval, scored by
6
+ * pattern-based heuristics (negation, numeric conflict, state conflict, temporal).
7
+ *
8
+ * No LLM calls — v1 is purely heuristic. LLM-enhanced scoring is a future item.
9
+ */
10
+ // ─── Internal Constants ──────────────────────────────────────────
11
+ const DEFAULT_CONFIG = {
12
+ minSimilarity: 0.6,
13
+ autoResolveThreshold: 0.85,
14
+ maxCandidates: 10,
15
+ autoResolve: true,
16
+ };
17
+ /** Words whose presence/absence flips meaning. */
18
+ const NEGATION_WORDS = new Set([
19
+ 'not', 'no', 'never', 'none', 'neither', 'nor', 'cannot', "can't",
20
+ "don't", "doesn't", "didn't", "won't", "wouldn't", "shouldn't",
21
+ "isn't", "aren't", "wasn't", "weren't", "hasn't", "haven't", "hadn't",
22
+ ]);
23
+ /** Antonym pairs where one in text A and the other in text B signals conflict. */
24
+ const STATE_ANTONYMS = [
25
+ ['enabled', 'disabled'],
26
+ ['active', 'inactive'],
27
+ ['active', 'deprecated'],
28
+ ['running', 'stopped'],
29
+ ['running', 'crashed'],
30
+ ['up', 'down'],
31
+ ['true', 'false'],
32
+ ['yes', 'no'],
33
+ ['on', 'off'],
34
+ ['open', 'closed'],
35
+ ['allowed', 'denied'],
36
+ ['allowed', 'blocked'],
37
+ ['available', 'unavailable'],
38
+ ['connected', 'disconnected'],
39
+ ['online', 'offline'],
40
+ ['present', 'absent'],
41
+ ['healthy', 'unhealthy'],
42
+ ['valid', 'invalid'],
43
+ ['complete', 'incomplete'],
44
+ ['success', 'failure'],
45
+ ];
46
+ // ─── Helpers ─────────────────────────────────────────────────────
47
+ /** Normalize text for comparison: lowercase, collapse whitespace. */
48
+ function normalize(text) {
49
+ return text.toLowerCase().replace(/\s+/g, ' ').trim();
50
+ }
51
+ /** Tokenize into word-boundary tokens. */
52
+ function tokenize(text) {
53
+ return normalize(text).split(/[\s,;:()[\]{}]+/).filter(Boolean);
54
+ }
55
+ /** Convert VectorStore distance (lower = closer) to a 0-1 similarity score. */
56
+ function distanceToSimilarity(distance) {
57
+ // sqlite-vec uses L2 distance by default. Convert to 0-1 similarity.
58
+ // distance=0 => similarity=1, distance grows => similarity decays toward 0.
59
+ return 1 / (1 + distance);
60
+ }
61
+ /**
62
+ * Build a safe FTS query from content. Takes the first several meaningful words
63
+ * to avoid FTS syntax errors from special characters.
64
+ */
65
+ function buildFtsQuery(content) {
66
+ const words = tokenize(content)
67
+ .filter(w => w.length > 2 && !/^\d+$/.test(w))
68
+ .slice(0, 6);
69
+ if (words.length === 0)
70
+ return '';
71
+ // OR-join for broad recall
72
+ return words.join(' OR ');
73
+ }
74
+ /**
75
+ * Extract numbers and their rough context (preceding word) from text.
76
+ * Returns pairs of [contextWord, number].
77
+ */
78
+ function extractNumbers(text) {
79
+ const results = [];
80
+ const tokens = tokenize(text);
81
+ for (let i = 0; i < tokens.length; i++) {
82
+ const num = parseFloat(tokens[i]);
83
+ if (!isNaN(num) && isFinite(num)) {
84
+ const context = i > 0 ? tokens[i - 1] : '';
85
+ results.push({ context, value: num });
86
+ }
87
+ }
88
+ return results;
89
+ }
90
+ /**
91
+ * Check if one text contains a negation of the other.
92
+ * Returns true if one has a negation word in a position where the other doesn't
93
+ * (or vice versa), given high token overlap.
94
+ */
95
+ function detectNegation(tokensA, tokensB) {
96
+ const negA = tokensA.filter(t => NEGATION_WORDS.has(t));
97
+ const negB = tokensB.filter(t => NEGATION_WORDS.has(t));
98
+ // One has negation words, the other doesn't (or different count)
99
+ if (negA.length !== negB.length) {
100
+ // Check that the non-negation content overlaps substantially
101
+ const contentA = new Set(tokensA.filter(t => !NEGATION_WORDS.has(t)));
102
+ const contentB = new Set(tokensB.filter(t => !NEGATION_WORDS.has(t)));
103
+ const intersection = [...contentA].filter(t => contentB.has(t));
104
+ const smaller = Math.min(contentA.size, contentB.size);
105
+ // Need at least 40% content overlap to consider it a negation of the same claim
106
+ return smaller > 0 && intersection.length / smaller >= 0.4;
107
+ }
108
+ return false;
109
+ }
110
+ /**
111
+ * Check for antonym state pairs between two token sets.
112
+ * Returns the matching antonym pair if found, or null.
113
+ */
114
+ function detectStateConflict(tokensA, tokensB) {
115
+ const setA = new Set(tokensA);
116
+ const setB = new Set(tokensB);
117
+ for (const [word1, word2] of STATE_ANTONYMS) {
118
+ if ((setA.has(word1) && setB.has(word2)) || (setA.has(word2) && setB.has(word1))) {
119
+ // Verify there's enough shared context (not just random words)
120
+ const contentA = new Set(tokensA.filter(t => t !== word1 && t !== word2));
121
+ const contentB = new Set(tokensB.filter(t => t !== word1 && t !== word2));
122
+ const intersection = [...contentA].filter(t => contentB.has(t));
123
+ const smaller = Math.min(contentA.size, contentB.size);
124
+ if (smaller > 0 && intersection.length / smaller >= 0.3) {
125
+ return setA.has(word1) ? [word1, word2] : [word2, word1];
126
+ }
127
+ }
128
+ }
129
+ return null;
130
+ }
131
+ /**
132
+ * Check for numeric conflicts: same contextual subject, different numbers.
133
+ */
134
+ function detectNumericConflict(textA, textB) {
135
+ const numsA = extractNumbers(textA);
136
+ const numsB = extractNumbers(textB);
137
+ for (const a of numsA) {
138
+ for (const b of numsB) {
139
+ // Same context word, different value
140
+ if (a.context && a.context === b.context && a.value !== b.value) {
141
+ return { contextWord: a.context, valueA: a.value, valueB: b.value };
142
+ }
143
+ }
144
+ }
145
+ return null;
146
+ }
147
+ // ─── ContradictionDetector ───────────────────────────────────────
148
+ export class ContradictionDetector {
149
+ factStore;
150
+ vectorStore;
151
+ config;
152
+ constructor(factStore, vectorStore, config) {
153
+ this.factStore = factStore;
154
+ this.vectorStore = vectorStore;
155
+ this.config = { ...DEFAULT_CONFIG, ...config };
156
+ }
157
+ /**
158
+ * On fact ingest, check if the new fact contradicts existing active facts.
159
+ * Uses vector similarity (when available) + FTS to find candidates, then
160
+ * scores each candidate with heuristic contradiction checks.
161
+ */
162
+ async detectOnIngest(agentId, newFact) {
163
+ const candidates = await this.findCandidates(agentId, newFact);
164
+ const contradictions = [];
165
+ for (const candidate of candidates) {
166
+ const scored = this.scoreContradiction(newFact.content, candidate);
167
+ if (scored) {
168
+ contradictions.push(scored);
169
+ }
170
+ }
171
+ // Sort by contradiction score descending
172
+ contradictions.sort((a, b) => b.contradictionScore - a.contradictionScore);
173
+ const result = {
174
+ contradictions,
175
+ autoResolved: false,
176
+ resolvedCount: 0,
177
+ };
178
+ return result;
179
+ }
180
+ /**
181
+ * Resolve a detected contradiction between an existing fact and a new fact.
182
+ */
183
+ resolveContradiction(oldFactId, newFactId, resolution) {
184
+ switch (resolution) {
185
+ case 'supersede':
186
+ this.factStore.markSuperseded(oldFactId, newFactId);
187
+ break;
188
+ case 'keep-both':
189
+ // No-op: both facts remain active
190
+ break;
191
+ case 'reject-new':
192
+ this.factStore.invalidateFact(newFactId);
193
+ break;
194
+ }
195
+ }
196
+ /**
197
+ * Auto-resolve high-confidence contradictions: newer supersedes older.
198
+ * Only resolves candidates above the autoResolveThreshold.
199
+ *
200
+ * @param agentId - The agent whose facts are being resolved (for audit trail)
201
+ * @param candidates - Scored contradiction candidates from detectOnIngest
202
+ * @returns Count of auto-resolved contradictions
203
+ */
204
+ async autoResolve(_agentId, candidates) {
205
+ if (!this.config.autoResolve)
206
+ return 0;
207
+ let resolved = 0;
208
+ for (const candidate of candidates) {
209
+ if (candidate.contradictionScore >= this.config.autoResolveThreshold) {
210
+ // The existing fact is older; the new fact (which triggered detection)
211
+ // is assumed to be the more recent truth. We mark the existing as superseded.
212
+ // Note: the caller must supply the newFactId when wiring this into ingest.
213
+ // For now, we invalidate the old fact since we don't have the new fact's id here.
214
+ this.factStore.invalidateFact(candidate.existingFactId);
215
+ resolved++;
216
+ }
217
+ }
218
+ return resolved;
219
+ }
220
+ // ─── Private Methods ────────────────────────────────────────────
221
+ /**
222
+ * Find candidate facts that might contradict the new fact.
223
+ * Uses vector search (if available) and FTS, deduplicates, and returns
224
+ * up to maxCandidates results above minSimilarity.
225
+ */
226
+ async findCandidates(agentId, newFact) {
227
+ const seen = new Set();
228
+ const candidates = [];
229
+ const { maxCandidates, minSimilarity } = this.config;
230
+ // Path 1: Vector similarity search (if VectorStore is available)
231
+ if (this.vectorStore) {
232
+ try {
233
+ const vectorResults = await this.vectorStore.search(newFact.content, {
234
+ tables: ['facts'],
235
+ limit: maxCandidates * 2, // over-fetch to allow for filtering
236
+ });
237
+ for (const vr of vectorResults) {
238
+ const similarity = distanceToSimilarity(vr.distance);
239
+ if (similarity < minSimilarity)
240
+ continue;
241
+ if (seen.has(vr.sourceId))
242
+ continue;
243
+ seen.add(vr.sourceId);
244
+ // Retrieve the full fact row for metadata checks
245
+ const facts = this.factStore.searchFacts(vr.content.slice(0, 30), {
246
+ agentId,
247
+ limit: 1,
248
+ });
249
+ const fact = facts.find(f => f.id === vr.sourceId);
250
+ if (fact && !fact.supersededBy && !fact.invalidAt) {
251
+ candidates.push({ fact, similarity });
252
+ }
253
+ }
254
+ }
255
+ catch {
256
+ // Vector search failed (embedding model unavailable, etc.)
257
+ // Fall through to FTS-only path
258
+ }
259
+ }
260
+ // Path 2: FTS search (always available, fills gaps)
261
+ if (candidates.length < maxCandidates) {
262
+ const ftsQuery = buildFtsQuery(newFact.content);
263
+ if (ftsQuery) {
264
+ try {
265
+ const ftsResults = this.factStore.searchFacts(ftsQuery, {
266
+ agentId,
267
+ domain: newFact.domain,
268
+ limit: maxCandidates * 2,
269
+ });
270
+ for (const fact of ftsResults) {
271
+ if (seen.has(fact.id))
272
+ continue;
273
+ if (fact.supersededBy || fact.invalidAt)
274
+ continue;
275
+ seen.add(fact.id);
276
+ // Compute a rough token-overlap similarity for FTS results
277
+ const similarity = this.tokenOverlapSimilarity(newFact.content, fact.content);
278
+ if (similarity >= minSimilarity) {
279
+ candidates.push({ fact, similarity });
280
+ }
281
+ }
282
+ }
283
+ catch {
284
+ // FTS query failed (malformed query, etc.)
285
+ }
286
+ }
287
+ }
288
+ // Sort by similarity descending, trim to maxCandidates
289
+ candidates.sort((a, b) => b.similarity - a.similarity);
290
+ return candidates.slice(0, maxCandidates);
291
+ }
292
+ /**
293
+ * Score a candidate fact against the new fact content for contradiction.
294
+ * Returns a ContradictionCandidate if any heuristic fires, null otherwise.
295
+ */
296
+ scoreContradiction(newContent, candidate) {
297
+ const existingContent = candidate.fact.content;
298
+ const tokensNew = tokenize(newContent);
299
+ const tokensExisting = tokenize(existingContent);
300
+ let bestScore = 0;
301
+ let bestReason = '';
302
+ // Heuristic 1: Negation detection (score: 0.9)
303
+ if (detectNegation(tokensNew, tokensExisting)) {
304
+ bestScore = 0.9;
305
+ bestReason = 'Negation detected: one fact negates the other';
306
+ }
307
+ // Heuristic 2: State conflict via antonym pairs (score: 0.85)
308
+ const stateConflict = detectStateConflict(tokensNew, tokensExisting);
309
+ if (stateConflict && stateConflict.length === 2) {
310
+ const score = 0.85;
311
+ if (score > bestScore) {
312
+ bestScore = score;
313
+ bestReason = `State conflict: "${stateConflict[0]}" vs "${stateConflict[1]}"`;
314
+ }
315
+ }
316
+ // Heuristic 3: Numeric conflict (score: 0.8)
317
+ const numConflict = detectNumericConflict(newContent, existingContent);
318
+ if (numConflict) {
319
+ const score = 0.8;
320
+ if (score > bestScore) {
321
+ bestScore = score;
322
+ bestReason = `Numeric conflict on "${numConflict.contextWord}": ${numConflict.valueA} vs ${numConflict.valueB}`;
323
+ }
324
+ }
325
+ // Heuristic 4: Temporal supersede — high similarity, different conclusion (score: 0.7)
326
+ // If similarity is very high (>0.8) but content isn't identical, it's likely a revised version
327
+ if (bestScore === 0 && candidate.similarity > 0.8) {
328
+ const contentDiffers = normalize(newContent) !== normalize(existingContent);
329
+ if (contentDiffers) {
330
+ bestScore = 0.7;
331
+ bestReason = 'Temporal supersede: high similarity with different content (likely revised)';
332
+ }
333
+ }
334
+ if (bestScore === 0)
335
+ return null;
336
+ return {
337
+ existingFactId: candidate.fact.id,
338
+ existingContent,
339
+ similarityScore: candidate.similarity,
340
+ contradictionScore: bestScore,
341
+ reason: bestReason,
342
+ };
343
+ }
344
+ /**
345
+ * Compute Jaccard-like token overlap between two texts.
346
+ * Returns 0-1 where 1 means identical token sets.
347
+ */
348
+ tokenOverlapSimilarity(textA, textB) {
349
+ const setA = new Set(tokenize(textA));
350
+ const setB = new Set(tokenize(textB));
351
+ if (setA.size === 0 || setB.size === 0)
352
+ return 0;
353
+ let intersection = 0;
354
+ for (const token of setA) {
355
+ if (setB.has(token))
356
+ intersection++;
357
+ }
358
+ const union = new Set([...setA, ...setB]).size;
359
+ return union > 0 ? intersection / union : 0;
360
+ }
361
+ }
362
+ //# sourceMappingURL=contradiction-detector.js.map
@@ -0,0 +1,129 @@
1
+ /**
2
+ * hypermem Expertise Store
3
+ *
4
+ * Stores domain expertise patterns — learned behaviors that make the Nth run
5
+ * better than the 1st. Two-phase lifecycle:
6
+ * 1. Observations: raw learnings logged from conversations, pipelines, reviews
7
+ * 2. Patterns: graduated observations with N≥3 confirming instances
8
+ *
9
+ * Patterns are agent-scoped but domain-tagged, enabling cross-agent queries.
10
+ * Patterns have confidence, frequency tracking, and decay on counter-evidence.
11
+ */
12
+ import type { DatabaseSync } from 'node:sqlite';
13
+ export interface ExpertiseObservation {
14
+ id: number;
15
+ agentId: string;
16
+ domain: string;
17
+ context: string | null;
18
+ observationText: string;
19
+ sourceType: 'conversation' | 'pipeline' | 'review' | 'manual';
20
+ sourceRef: string | null;
21
+ createdAt: string;
22
+ }
23
+ export interface ExpertisePattern {
24
+ id: number;
25
+ agentId: string;
26
+ domain: string;
27
+ patternText: string;
28
+ confidence: number;
29
+ frequency: number;
30
+ firstSeen: string;
31
+ lastConfirmed: string;
32
+ invalidatedAt: string | null;
33
+ invalidationReason: string | null;
34
+ decayScore: number;
35
+ }
36
+ export interface ExpertiseEvidence {
37
+ observationId: number;
38
+ patternId: number;
39
+ relationship: 'confirms' | 'contradicts';
40
+ createdAt: string;
41
+ }
42
+ export declare class ExpertiseStore {
43
+ private readonly db;
44
+ private readonly graduationThreshold;
45
+ constructor(db: DatabaseSync, graduationThreshold?: number);
46
+ /**
47
+ * Record a raw observation from any source.
48
+ */
49
+ record(agentId: string, observationText: string, domain: string, opts?: {
50
+ context?: string;
51
+ sourceType?: ExpertiseObservation['sourceType'];
52
+ sourceRef?: string;
53
+ }): ExpertiseObservation;
54
+ /**
55
+ * Get observations for an agent, optionally filtered by domain.
56
+ */
57
+ getObservations(agentId: string, opts?: {
58
+ domain?: string;
59
+ limit?: number;
60
+ }): ExpertiseObservation[];
61
+ /**
62
+ * Retrieve active expertise patterns for current work context.
63
+ * Returns patterns sorted by confidence DESC, frequency DESC.
64
+ * Excludes invalidated patterns by default.
65
+ */
66
+ query(agentId: string, domain: string, opts?: {
67
+ context?: string;
68
+ includeInvalidated?: boolean;
69
+ limit?: number;
70
+ minConfidence?: number;
71
+ }): ExpertisePattern[];
72
+ /**
73
+ * Cross-agent query: get patterns from any agent in a given domain.
74
+ * Useful for fleet-wide expertise ("what has any agent learned about X?").
75
+ */
76
+ queryFleet(domain: string, opts?: {
77
+ limit?: number;
78
+ minConfidence?: number;
79
+ }): ExpertisePattern[];
80
+ /**
81
+ * Graduate an observation to a pattern.
82
+ *
83
+ * If a similar pattern already exists (same agent, domain, and pattern text prefix match),
84
+ * increments its frequency and updates lastConfirmed instead of creating a duplicate.
85
+ *
86
+ * Auto-graduation happens when an observation has N≥graduationThreshold confirming
87
+ * evidence links. Can also be called manually.
88
+ */
89
+ graduate(agentId: string, observationId: number, opts?: {
90
+ patternText?: string;
91
+ confidence?: number;
92
+ }): ExpertisePattern | null;
93
+ /**
94
+ * Record evidence linking an observation to a pattern.
95
+ * If this pushes a pattern's contradicting evidence past threshold,
96
+ * auto-invalidates the pattern.
97
+ */
98
+ addEvidence(observationId: number, patternId: number, relationship: 'confirms' | 'contradicts'): void;
99
+ /**
100
+ * Check if any observations are ready for auto-graduation.
101
+ * An observation graduates when it has N≥threshold confirming evidence links.
102
+ * Returns the number of newly graduated patterns.
103
+ */
104
+ autoGraduate(agentId: string): number;
105
+ /**
106
+ * Mark a pattern as invalidated.
107
+ */
108
+ invalidate(patternId: number, reason: string): boolean;
109
+ /**
110
+ * List all active patterns for an agent, optionally filtered by domain.
111
+ */
112
+ list(agentId: string, opts?: {
113
+ domain?: string;
114
+ includeInvalidated?: boolean;
115
+ }): ExpertisePattern[];
116
+ /**
117
+ * Decay all patterns by a fixed rate. Similar to fact decay.
118
+ */
119
+ decayPatterns(agentId: string, decayRate?: number): number;
120
+ /**
121
+ * Get pattern and observation counts for an agent.
122
+ */
123
+ getStats(agentId: string): {
124
+ observations: number;
125
+ activePatterns: number;
126
+ invalidatedPatterns: number;
127
+ };
128
+ }
129
+ //# sourceMappingURL=expertise-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"expertise-store.d.ts","sourceRoot":"","sources":["../src/expertise-store.ts"],"names":[],"mappings":"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"}