@danielsimonjr/memory-mcp 9.8.3 → 9.9.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 (41) hide show
  1. package/README.md +360 -1829
  2. package/dist/core/ManagerContext.d.ts +4 -0
  3. package/dist/core/ManagerContext.d.ts.map +1 -1
  4. package/dist/core/ManagerContext.js +6 -0
  5. package/dist/features/KeywordExtractor.d.ts +61 -0
  6. package/dist/features/KeywordExtractor.d.ts.map +1 -0
  7. package/dist/features/KeywordExtractor.js +126 -0
  8. package/dist/features/ObservationNormalizer.d.ts +90 -0
  9. package/dist/features/ObservationNormalizer.d.ts.map +1 -0
  10. package/dist/features/ObservationNormalizer.js +193 -0
  11. package/dist/features/index.d.ts +2 -0
  12. package/dist/features/index.d.ts.map +1 -1
  13. package/dist/features/index.js +3 -0
  14. package/dist/search/HybridSearchManager.d.ts +80 -0
  15. package/dist/search/HybridSearchManager.d.ts.map +1 -0
  16. package/dist/search/HybridSearchManager.js +187 -0
  17. package/dist/search/QueryAnalyzer.d.ts +76 -0
  18. package/dist/search/QueryAnalyzer.d.ts.map +1 -0
  19. package/dist/search/QueryAnalyzer.js +227 -0
  20. package/dist/search/QueryPlanner.d.ts +58 -0
  21. package/dist/search/QueryPlanner.d.ts.map +1 -0
  22. package/dist/search/QueryPlanner.js +137 -0
  23. package/dist/search/ReflectionManager.d.ts +71 -0
  24. package/dist/search/ReflectionManager.d.ts.map +1 -0
  25. package/dist/search/ReflectionManager.js +124 -0
  26. package/dist/search/SymbolicSearch.d.ts +61 -0
  27. package/dist/search/SymbolicSearch.d.ts.map +1 -0
  28. package/dist/search/SymbolicSearch.js +163 -0
  29. package/dist/search/index.d.ts +5 -0
  30. package/dist/search/index.d.ts.map +1 -1
  31. package/dist/search/index.js +8 -0
  32. package/dist/server/toolDefinitions.d.ts +1 -1
  33. package/dist/server/toolDefinitions.d.ts.map +1 -1
  34. package/dist/server/toolDefinitions.js +141 -1
  35. package/dist/server/toolHandlers.d.ts.map +1 -1
  36. package/dist/server/toolHandlers.js +167 -0
  37. package/dist/types/index.d.ts +1 -1
  38. package/dist/types/index.d.ts.map +1 -1
  39. package/dist/types/types.d.ts +118 -0
  40. package/dist/types/types.d.ts.map +1 -1
  41. package/package.json +2 -2
@@ -14,6 +14,7 @@ import { ObservationManager } from './ObservationManager.js';
14
14
  import { HierarchyManager } from './HierarchyManager.js';
15
15
  import { GraphTraversal } from './GraphTraversal.js';
16
16
  import { SearchManager } from '../search/SearchManager.js';
17
+ import { RankedSearch } from '../search/RankedSearch.js';
17
18
  import { SemanticSearch } from '../search/index.js';
18
19
  import { IOManager } from '../features/IOManager.js';
19
20
  import { TagManager } from '../features/TagManager.js';
@@ -35,6 +36,7 @@ export declare class ManagerContext {
35
36
  private _graphTraversal?;
36
37
  private _searchManager?;
37
38
  private _semanticSearch?;
39
+ private _rankedSearch?;
38
40
  private _ioManager?;
39
41
  private _tagManager?;
40
42
  private _analyticsManager?;
@@ -58,6 +60,8 @@ export declare class ManagerContext {
58
60
  * Returns null if no embedding provider is configured.
59
61
  */
60
62
  get semanticSearch(): SemanticSearch | null;
63
+ /** RankedSearch - Phase 11: TF-IDF/BM25 ranked search for hybrid search */
64
+ get rankedSearch(): RankedSearch;
61
65
  /** IOManager - Import, export, and backup operations */
62
66
  get ioManager(): IOManager;
63
67
  /** TagManager - Tag alias management */
@@ -1 +1 @@
1
- {"version":3,"file":"ManagerContext.d.ts","sourceRoot":"","sources":["../../src/core/ManagerContext.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEjD,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAC7D,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAC3D,OAAO,EAAE,cAAc,EAA6C,MAAM,oBAAoB,CAAC;AAC/F,OAAO,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AACvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AACnE,OAAO,EAAE,kBAAkB,EAAE,MAAM,mCAAmC,CAAC;AACvE,OAAO,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAG/D;;;GAGG;AACH,qBAAa,cAAc;IAGzB,QAAQ,CAAC,OAAO,EAAE,YAAY,CAAC;IAC/B,OAAO,CAAC,QAAQ,CAAC,qBAAqB,CAAS;IAC/C,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAS;IAG5C,OAAO,CAAC,cAAc,CAAC,CAAgB;IACvC,OAAO,CAAC,gBAAgB,CAAC,CAAkB;IAC3C,OAAO,CAAC,mBAAmB,CAAC,CAAqB;IACjD,OAAO,CAAC,iBAAiB,CAAC,CAAmB;IAC7C,OAAO,CAAC,eAAe,CAAC,CAAiB;IACzC,OAAO,CAAC,cAAc,CAAC,CAAgB;IACvC,OAAO,CAAC,eAAe,CAAC,CAAwB;IAChD,OAAO,CAAC,UAAU,CAAC,CAAY;IAC/B,OAAO,CAAC,WAAW,CAAC,CAAa;IACjC,OAAO,CAAC,iBAAiB,CAAC,CAAmB;IAC7C,OAAO,CAAC,mBAAmB,CAAC,CAAqB;IACjD,OAAO,CAAC,eAAe,CAAC,CAAiB;gBAE7B,cAAc,EAAE,MAAM;IAclC,qDAAqD;IACrD,IAAI,aAAa,IAAI,aAAa,CAEjC;IAED,sCAAsC;IACtC,IAAI,eAAe,IAAI,eAAe,CAErC;IAED,4CAA4C;IAC5C,IAAI,kBAAkB,IAAI,kBAAkB,CAE3C;IAED,qDAAqD;IACrD,IAAI,gBAAgB,IAAI,gBAAgB,CAEvC;IAED,sEAAsE;IACtE,IAAI,cAAc,IAAI,cAAc,CAEnC;IAED,4CAA4C;IAC5C,IAAI,aAAa,IAAI,aAAa,CAEjC;IAED;;;OAGG;IACH,IAAI,cAAc,IAAI,cAAc,GAAG,IAAI,CAa1C;IAED,wDAAwD;IACxD,IAAI,SAAS,IAAI,SAAS,CAEzB;IAED,wCAAwC;IACxC,IAAI,UAAU,IAAI,UAAU,CAE3B;IAED,yDAAyD;IACzD,IAAI,gBAAgB,IAAI,gBAAgB,CAEvC;IAED,kEAAkE;IAClE,IAAI,kBAAkB,IAAI,kBAAkB,CAE3C;IAED,kDAAkD;IAClD,IAAI,cAAc,IAAI,cAAc,CAEnC;CACF"}
1
+ {"version":3,"file":"ManagerContext.d.ts","sourceRoot":"","sources":["../../src/core/ManagerContext.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEjD,OAAO,EAAE,aAAa,EAAE,MAAM,oBAAoB,CAAC;AACnD,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AACvD,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAC7D,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AACrD,OAAO,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAC3D,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACzD,OAAO,EAAE,cAAc,EAA6C,MAAM,oBAAoB,CAAC;AAC/F,OAAO,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AACvD,OAAO,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AACnE,OAAO,EAAE,kBAAkB,EAAE,MAAM,mCAAmC,CAAC;AACvE,OAAO,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAG/D;;;GAGG;AACH,qBAAa,cAAc;IAGzB,QAAQ,CAAC,OAAO,EAAE,YAAY,CAAC;IAC/B,OAAO,CAAC,QAAQ,CAAC,qBAAqB,CAAS;IAC/C,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAS;IAG5C,OAAO,CAAC,cAAc,CAAC,CAAgB;IACvC,OAAO,CAAC,gBAAgB,CAAC,CAAkB;IAC3C,OAAO,CAAC,mBAAmB,CAAC,CAAqB;IACjD,OAAO,CAAC,iBAAiB,CAAC,CAAmB;IAC7C,OAAO,CAAC,eAAe,CAAC,CAAiB;IACzC,OAAO,CAAC,cAAc,CAAC,CAAgB;IACvC,OAAO,CAAC,eAAe,CAAC,CAAwB;IAChD,OAAO,CAAC,aAAa,CAAC,CAAe;IACrC,OAAO,CAAC,UAAU,CAAC,CAAY;IAC/B,OAAO,CAAC,WAAW,CAAC,CAAa;IACjC,OAAO,CAAC,iBAAiB,CAAC,CAAmB;IAC7C,OAAO,CAAC,mBAAmB,CAAC,CAAqB;IACjD,OAAO,CAAC,eAAe,CAAC,CAAiB;gBAE7B,cAAc,EAAE,MAAM;IAclC,qDAAqD;IACrD,IAAI,aAAa,IAAI,aAAa,CAEjC;IAED,sCAAsC;IACtC,IAAI,eAAe,IAAI,eAAe,CAErC;IAED,4CAA4C;IAC5C,IAAI,kBAAkB,IAAI,kBAAkB,CAE3C;IAED,qDAAqD;IACrD,IAAI,gBAAgB,IAAI,gBAAgB,CAEvC;IAED,sEAAsE;IACtE,IAAI,cAAc,IAAI,cAAc,CAEnC;IAED,4CAA4C;IAC5C,IAAI,aAAa,IAAI,aAAa,CAEjC;IAED;;;OAGG;IACH,IAAI,cAAc,IAAI,cAAc,GAAG,IAAI,CAa1C;IAED,2EAA2E;IAC3E,IAAI,YAAY,IAAI,YAAY,CAE/B;IAED,wDAAwD;IACxD,IAAI,SAAS,IAAI,SAAS,CAEzB;IAED,wCAAwC;IACxC,IAAI,UAAU,IAAI,UAAU,CAE3B;IAED,yDAAyD;IACzD,IAAI,gBAAgB,IAAI,gBAAgB,CAEvC;IAED,kEAAkE;IAClE,IAAI,kBAAkB,IAAI,kBAAkB,CAE3C;IAED,kDAAkD;IAClD,IAAI,cAAc,IAAI,cAAc,CAEnC;CACF"}
@@ -15,6 +15,7 @@ import { ObservationManager } from './ObservationManager.js';
15
15
  import { HierarchyManager } from './HierarchyManager.js';
16
16
  import { GraphTraversal } from './GraphTraversal.js';
17
17
  import { SearchManager } from '../search/SearchManager.js';
18
+ import { RankedSearch } from '../search/RankedSearch.js';
18
19
  import { SemanticSearch, createEmbeddingService, createVectorStore } from '../search/index.js';
19
20
  import { IOManager } from '../features/IOManager.js';
20
21
  import { TagManager } from '../features/TagManager.js';
@@ -40,6 +41,7 @@ export class ManagerContext {
40
41
  _graphTraversal;
41
42
  _searchManager;
42
43
  _semanticSearch;
44
+ _rankedSearch;
43
45
  _ioManager;
44
46
  _tagManager;
45
47
  _analyticsManager;
@@ -99,6 +101,10 @@ export class ManagerContext {
99
101
  }
100
102
  return this._semanticSearch;
101
103
  }
104
+ /** RankedSearch - Phase 11: TF-IDF/BM25 ranked search for hybrid search */
105
+ get rankedSearch() {
106
+ return (this._rankedSearch ??= new RankedSearch(this.storage));
107
+ }
102
108
  /** IOManager - Import, export, and backup operations */
103
109
  get ioManager() {
104
110
  return (this._ioManager ??= new IOManager(this.storage));
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Keyword Extractor
3
+ *
4
+ * Phase 11: Extracts and scores keywords from text
5
+ * for lexical search enhancement.
6
+ *
7
+ * @module features/KeywordExtractor
8
+ */
9
+ /**
10
+ * A keyword with importance score.
11
+ */
12
+ export interface ScoredKeyword {
13
+ keyword: string;
14
+ score: number;
15
+ positions: number[];
16
+ }
17
+ /**
18
+ * Keyword Extractor extracts and scores keywords from text.
19
+ *
20
+ * Features:
21
+ * - Position-based scoring (earlier = more important)
22
+ * - Domain-specific keyword boosting
23
+ * - Length-based scoring (longer words often more specific)
24
+ * - Stopword filtering
25
+ *
26
+ * @example
27
+ * ```typescript
28
+ * const extractor = new KeywordExtractor();
29
+ * const keywords = extractor.extract('The software project was completed on time');
30
+ * // Returns scored keywords sorted by importance
31
+ * ```
32
+ */
33
+ export declare class KeywordExtractor {
34
+ private stopwords;
35
+ private domainBoosts;
36
+ constructor();
37
+ /**
38
+ * Extract keywords with scores from text.
39
+ */
40
+ extract(text: string): ScoredKeyword[];
41
+ private tokenize;
42
+ private isKeyword;
43
+ private calculateScore;
44
+ /**
45
+ * Extract top N keywords.
46
+ */
47
+ extractTop(text: string, n: number): string[];
48
+ /**
49
+ * Add custom domain boost for a keyword.
50
+ */
51
+ addDomainBoost(keyword: string, boost: number): void;
52
+ /**
53
+ * Remove a domain boost.
54
+ */
55
+ removeDomainBoost(keyword: string): boolean;
56
+ /**
57
+ * Get all domain boosts.
58
+ */
59
+ getDomainBoosts(): Map<string, number>;
60
+ }
61
+ //# sourceMappingURL=KeywordExtractor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"KeywordExtractor.d.ts","sourceRoot":"","sources":["../../src/features/KeywordExtractor.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,EAAE,MAAM,EAAE,CAAC;CACrB;AAED;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,SAAS,CAAc;IAC/B,OAAO,CAAC,YAAY,CAAsB;;IA2B1C;;OAEG;IACH,OAAO,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,EAAE;IAyBtC,OAAO,CAAC,QAAQ;IAOhB,OAAO,CAAC,SAAS;IAIjB,OAAO,CAAC,cAAc;IAiBtB;;OAEG;IACH,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE;IAM7C;;OAEG;IACH,cAAc,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI;IAIpD;;OAEG;IACH,iBAAiB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO;IAI3C;;OAEG;IACH,eAAe,IAAI,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC;CAGvC"}
@@ -0,0 +1,126 @@
1
+ /**
2
+ * Keyword Extractor
3
+ *
4
+ * Phase 11: Extracts and scores keywords from text
5
+ * for lexical search enhancement.
6
+ *
7
+ * @module features/KeywordExtractor
8
+ */
9
+ /**
10
+ * Keyword Extractor extracts and scores keywords from text.
11
+ *
12
+ * Features:
13
+ * - Position-based scoring (earlier = more important)
14
+ * - Domain-specific keyword boosting
15
+ * - Length-based scoring (longer words often more specific)
16
+ * - Stopword filtering
17
+ *
18
+ * @example
19
+ * ```typescript
20
+ * const extractor = new KeywordExtractor();
21
+ * const keywords = extractor.extract('The software project was completed on time');
22
+ * // Returns scored keywords sorted by importance
23
+ * ```
24
+ */
25
+ export class KeywordExtractor {
26
+ stopwords;
27
+ domainBoosts;
28
+ constructor() {
29
+ this.stopwords = new Set([
30
+ 'a', 'an', 'the', 'is', 'are', 'was', 'were', 'be', 'been',
31
+ 'have', 'has', 'had', 'do', 'does', 'did', 'will', 'would',
32
+ 'could', 'should', 'can', 'to', 'of', 'in', 'for', 'on',
33
+ 'with', 'at', 'by', 'from', 'as', 'and', 'or', 'but',
34
+ ]);
35
+ this.domainBoosts = new Map([
36
+ ['project', 1.5],
37
+ ['task', 1.5],
38
+ ['meeting', 1.3],
39
+ ['deadline', 1.4],
40
+ ['completed', 1.2],
41
+ ['started', 1.2],
42
+ ['person', 1.3],
43
+ ['company', 1.3],
44
+ ['team', 1.3],
45
+ ['release', 1.4],
46
+ ['feature', 1.3],
47
+ ['bug', 1.2],
48
+ ['issue', 1.2],
49
+ ['milestone', 1.4],
50
+ ]);
51
+ }
52
+ /**
53
+ * Extract keywords with scores from text.
54
+ */
55
+ extract(text) {
56
+ const words = this.tokenize(text);
57
+ const keywordMap = new Map();
58
+ for (let i = 0; i < words.length; i++) {
59
+ const word = words[i].toLowerCase();
60
+ if (this.isKeyword(word)) {
61
+ const existing = keywordMap.get(word);
62
+ if (existing) {
63
+ existing.positions.push(i);
64
+ existing.score += this.calculateScore(word, i, words.length);
65
+ }
66
+ else {
67
+ keywordMap.set(word, {
68
+ keyword: word,
69
+ score: this.calculateScore(word, i, words.length),
70
+ positions: [i],
71
+ });
72
+ }
73
+ }
74
+ }
75
+ return Array.from(keywordMap.values())
76
+ .sort((a, b) => b.score - a.score);
77
+ }
78
+ tokenize(text) {
79
+ return text
80
+ .replace(/[^a-zA-Z0-9\s]/g, ' ')
81
+ .split(/\s+/)
82
+ .filter(w => w.length > 0);
83
+ }
84
+ isKeyword(word) {
85
+ return word.length > 2 && !this.stopwords.has(word);
86
+ }
87
+ calculateScore(word, position, totalWords) {
88
+ let score = 1.0;
89
+ // Position boost (earlier = more important)
90
+ const positionFactor = 1 - (position / totalWords) * 0.3;
91
+ score *= positionFactor;
92
+ // Domain boost
93
+ const boost = this.domainBoosts.get(word) ?? 1.0;
94
+ score *= boost;
95
+ // Length boost (longer words often more specific)
96
+ if (word.length > 6)
97
+ score *= 1.1;
98
+ return score;
99
+ }
100
+ /**
101
+ * Extract top N keywords.
102
+ */
103
+ extractTop(text, n) {
104
+ return this.extract(text)
105
+ .slice(0, n)
106
+ .map(k => k.keyword);
107
+ }
108
+ /**
109
+ * Add custom domain boost for a keyword.
110
+ */
111
+ addDomainBoost(keyword, boost) {
112
+ this.domainBoosts.set(keyword.toLowerCase(), boost);
113
+ }
114
+ /**
115
+ * Remove a domain boost.
116
+ */
117
+ removeDomainBoost(keyword) {
118
+ return this.domainBoosts.delete(keyword.toLowerCase());
119
+ }
120
+ /**
121
+ * Get all domain boosts.
122
+ */
123
+ getDomainBoosts() {
124
+ return new Map(this.domainBoosts);
125
+ }
126
+ }
@@ -0,0 +1,90 @@
1
+ /**
2
+ * Observation Normalizer
3
+ *
4
+ * Phase 11: Transforms observations to be self-contained facts
5
+ * through coreference resolution and temporal anchoring.
6
+ *
7
+ * @module features/ObservationNormalizer
8
+ */
9
+ import type { Entity } from '../types/index.js';
10
+ /**
11
+ * Options for observation normalization.
12
+ */
13
+ export interface NormalizationOptions {
14
+ /** Resolve pronouns to entity names */
15
+ resolveCoreferences?: boolean;
16
+ /** Convert relative dates to absolute dates */
17
+ anchorTimestamps?: boolean;
18
+ /** Extract and tag keywords */
19
+ extractKeywords?: boolean;
20
+ /** Reference date for relative date conversion (default: now) */
21
+ referenceDate?: Date;
22
+ }
23
+ /**
24
+ * Result of normalizing an observation.
25
+ */
26
+ export interface NormalizationResult {
27
+ original: string;
28
+ normalized: string;
29
+ changes: string[];
30
+ keywords?: string[];
31
+ }
32
+ /**
33
+ * Observation Normalizer transforms observations to self-contained facts.
34
+ *
35
+ * Applies transformations:
36
+ * 1. Coreference resolution: 'He works' -> 'Alice works'
37
+ * 2. Temporal anchoring: 'yesterday' -> '2026-01-07'
38
+ * 3. Keyword extraction: Identifies important terms
39
+ *
40
+ * @example
41
+ * ```typescript
42
+ * const normalizer = new ObservationNormalizer();
43
+ * const result = normalizer.normalize(
44
+ * 'He started the project yesterday',
45
+ * { name: 'Bob', entityType: 'person', observations: [] }
46
+ * );
47
+ * // result.normalized = 'Bob started the project on 2026-01-07'
48
+ * ```
49
+ */
50
+ export declare class ObservationNormalizer {
51
+ private pronounPatterns;
52
+ private relativeTimePatterns;
53
+ /**
54
+ * Normalize an observation for an entity.
55
+ */
56
+ normalize(observation: string, entity: Entity, options?: NormalizationOptions): NormalizationResult;
57
+ /**
58
+ * Resolve pronouns to entity name.
59
+ */
60
+ resolveCoreferences(text: string, entity: Entity): {
61
+ text: string;
62
+ changed: boolean;
63
+ };
64
+ private guessMasculine;
65
+ private guessFeminine;
66
+ /**
67
+ * Convert relative timestamps to absolute dates.
68
+ */
69
+ anchorTimestamps(text: string, referenceDate: Date): {
70
+ text: string;
71
+ changed: boolean;
72
+ replacements: string[];
73
+ };
74
+ private addDays;
75
+ private addMonths;
76
+ private formatDate;
77
+ private formatMonth;
78
+ /**
79
+ * Extract important keywords from text.
80
+ */
81
+ extractKeywords(text: string): string[];
82
+ /**
83
+ * Normalize all observations for an entity.
84
+ */
85
+ normalizeEntity(entity: Entity, options?: NormalizationOptions): {
86
+ entity: Entity;
87
+ results: NormalizationResult[];
88
+ };
89
+ }
90
+ //# sourceMappingURL=ObservationNormalizer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ObservationNormalizer.d.ts","sourceRoot":"","sources":["../../src/features/ObservationNormalizer.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAEhD;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,uCAAuC;IACvC,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,+CAA+C;IAC/C,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,+BAA+B;IAC/B,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,iEAAiE;IACjE,aAAa,CAAC,EAAE,IAAI,CAAC;CACtB;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;CACrB;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,qBAAa,qBAAqB;IAChC,OAAO,CAAC,eAAe,CAIrB;IAEF,OAAO,CAAC,oBAAoB,CAU1B;IAEF;;OAEG;IACH,SAAS,CACP,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,oBAAyB,GACjC,mBAAmB;IAuCtB;;OAEG;IACH,mBAAmB,CACjB,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,GACb;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,OAAO,CAAA;KAAE;IAsCrC,OAAO,CAAC,cAAc;IAKtB,OAAO,CAAC,aAAa;IAKrB;;OAEG;IACH,gBAAgB,CACd,IAAI,EAAE,MAAM,EACZ,aAAa,EAAE,IAAI,GAClB;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,OAAO,CAAC;QAAC,YAAY,EAAE,MAAM,EAAE,CAAA;KAAE;IAoB7D,OAAO,CAAC,OAAO;IAMf,OAAO,CAAC,SAAS;IAMjB,OAAO,CAAC,UAAU;IAIlB,OAAO,CAAC,WAAW;IAInB;;OAEG;IACH,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE;IAwBvC;;OAEG;IACH,eAAe,CACb,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,oBAAyB,GACjC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,mBAAmB,EAAE,CAAA;KAAE;CAatD"}
@@ -0,0 +1,193 @@
1
+ /**
2
+ * Observation Normalizer
3
+ *
4
+ * Phase 11: Transforms observations to be self-contained facts
5
+ * through coreference resolution and temporal anchoring.
6
+ *
7
+ * @module features/ObservationNormalizer
8
+ */
9
+ /**
10
+ * Observation Normalizer transforms observations to self-contained facts.
11
+ *
12
+ * Applies transformations:
13
+ * 1. Coreference resolution: 'He works' -> 'Alice works'
14
+ * 2. Temporal anchoring: 'yesterday' -> '2026-01-07'
15
+ * 3. Keyword extraction: Identifies important terms
16
+ *
17
+ * @example
18
+ * ```typescript
19
+ * const normalizer = new ObservationNormalizer();
20
+ * const result = normalizer.normalize(
21
+ * 'He started the project yesterday',
22
+ * { name: 'Bob', entityType: 'person', observations: [] }
23
+ * );
24
+ * // result.normalized = 'Bob started the project on 2026-01-07'
25
+ * ```
26
+ */
27
+ export class ObservationNormalizer {
28
+ pronounPatterns = {
29
+ masculine: /\b(he|him|his)\b/gi,
30
+ feminine: /\b(she|her|hers)\b/gi,
31
+ neutral: /\b(they|them|their|theirs)\b/gi,
32
+ };
33
+ relativeTimePatterns = [
34
+ [/\byesterday\b/i, (ref) => this.formatDate(this.addDays(ref, -1))],
35
+ [/\btoday\b/i, (ref) => this.formatDate(ref)],
36
+ [/\btomorrow\b/i, (ref) => this.formatDate(this.addDays(ref, 1))],
37
+ [/\blast week\b/i, (ref) => `week of ${this.formatDate(this.addDays(ref, -7))}`],
38
+ [/\blast month\b/i, (ref) => this.formatMonth(this.addMonths(ref, -1))],
39
+ [/\blast year\b/i, (ref) => `${ref.getFullYear() - 1}`],
40
+ [/\bthis week\b/i, (ref) => `week of ${this.formatDate(ref)}`],
41
+ [/\bthis month\b/i, (ref) => this.formatMonth(ref)],
42
+ [/\bthis year\b/i, (ref) => `${ref.getFullYear()}`],
43
+ ];
44
+ /**
45
+ * Normalize an observation for an entity.
46
+ */
47
+ normalize(observation, entity, options = {}) {
48
+ const { resolveCoreferences = true, anchorTimestamps = true, extractKeywords = false, referenceDate = new Date(), } = options;
49
+ let normalized = observation;
50
+ const changes = [];
51
+ if (resolveCoreferences) {
52
+ const corefResult = this.resolveCoreferences(normalized, entity);
53
+ if (corefResult.changed) {
54
+ normalized = corefResult.text;
55
+ changes.push(`Resolved pronouns to '${entity.name}'`);
56
+ }
57
+ }
58
+ if (anchorTimestamps) {
59
+ const timeResult = this.anchorTimestamps(normalized, referenceDate);
60
+ if (timeResult.changed) {
61
+ normalized = timeResult.text;
62
+ changes.push(...timeResult.replacements);
63
+ }
64
+ }
65
+ const keywords = extractKeywords
66
+ ? this.extractKeywords(normalized)
67
+ : undefined;
68
+ return {
69
+ original: observation,
70
+ normalized,
71
+ changes,
72
+ keywords,
73
+ };
74
+ }
75
+ /**
76
+ * Resolve pronouns to entity name.
77
+ */
78
+ resolveCoreferences(text, entity) {
79
+ let result = text;
80
+ let changed = false;
81
+ // Determine gender hint from entity type or name patterns
82
+ const isMasculine = this.guessMasculine(entity);
83
+ const isFeminine = this.guessFeminine(entity);
84
+ // Replace pronouns based on detected gender
85
+ if (isMasculine) {
86
+ const newText = result
87
+ .replace(this.pronounPatterns.masculine, entity.name);
88
+ if (newText !== result) {
89
+ result = newText;
90
+ changed = true;
91
+ }
92
+ }
93
+ else if (isFeminine) {
94
+ const newText = result
95
+ .replace(this.pronounPatterns.feminine, entity.name);
96
+ if (newText !== result) {
97
+ result = newText;
98
+ changed = true;
99
+ }
100
+ }
101
+ // Always try neutral pronouns for non-person entities
102
+ if (entity.entityType.toLowerCase() !== 'person') {
103
+ const newText = result
104
+ .replace(this.pronounPatterns.neutral, entity.name);
105
+ if (newText !== result) {
106
+ result = newText;
107
+ changed = true;
108
+ }
109
+ }
110
+ return { text: result, changed };
111
+ }
112
+ guessMasculine(entity) {
113
+ const masculineNames = ['john', 'james', 'bob', 'mike', 'david', 'alex'];
114
+ return masculineNames.some(n => entity.name.toLowerCase().includes(n));
115
+ }
116
+ guessFeminine(entity) {
117
+ const feminineNames = ['alice', 'jane', 'sarah', 'mary', 'emma', 'lisa'];
118
+ return feminineNames.some(n => entity.name.toLowerCase().includes(n));
119
+ }
120
+ /**
121
+ * Convert relative timestamps to absolute dates.
122
+ */
123
+ anchorTimestamps(text, referenceDate) {
124
+ let result = text;
125
+ const replacements = [];
126
+ for (const [pattern, resolver] of this.relativeTimePatterns) {
127
+ const match = result.match(pattern);
128
+ if (match) {
129
+ const replacement = resolver(referenceDate);
130
+ result = result.replace(pattern, replacement);
131
+ replacements.push(`'${match[0]}' -> '${replacement}'`);
132
+ }
133
+ }
134
+ return {
135
+ text: result,
136
+ changed: replacements.length > 0,
137
+ replacements,
138
+ };
139
+ }
140
+ addDays(date, days) {
141
+ const result = new Date(date);
142
+ result.setDate(result.getDate() + days);
143
+ return result;
144
+ }
145
+ addMonths(date, months) {
146
+ const result = new Date(date);
147
+ result.setMonth(result.getMonth() + months);
148
+ return result;
149
+ }
150
+ formatDate(date) {
151
+ return date.toISOString().split('T')[0];
152
+ }
153
+ formatMonth(date) {
154
+ return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}`;
155
+ }
156
+ /**
157
+ * Extract important keywords from text.
158
+ */
159
+ extractKeywords(text) {
160
+ const stopwords = new Set([
161
+ 'a', 'an', 'the', 'is', 'are', 'was', 'were', 'be', 'been',
162
+ 'being', 'have', 'has', 'had', 'do', 'does', 'did', 'will',
163
+ 'would', 'could', 'should', 'may', 'might', 'must', 'shall',
164
+ 'can', 'to', 'of', 'in', 'for', 'on', 'with', 'at', 'by',
165
+ 'from', 'as', 'into', 'through', 'during', 'before', 'after',
166
+ 'above', 'below', 'between', 'under', 'again', 'further',
167
+ 'then', 'once', 'here', 'there', 'when', 'where', 'why', 'how',
168
+ 'all', 'each', 'few', 'more', 'most', 'other', 'some', 'such',
169
+ 'no', 'nor', 'not', 'only', 'own', 'same', 'so', 'than', 'too',
170
+ 'very', 's', 't', 'just', 'don', 'now', 'and', 'but', 'or',
171
+ ]);
172
+ const words = text
173
+ .toLowerCase()
174
+ .replace(/[^a-z0-9\s]/g, '')
175
+ .split(/\s+/)
176
+ .filter(w => w.length > 2 && !stopwords.has(w));
177
+ // Return unique keywords
178
+ return [...new Set(words)];
179
+ }
180
+ /**
181
+ * Normalize all observations for an entity.
182
+ */
183
+ normalizeEntity(entity, options = {}) {
184
+ const results = entity.observations.map(obs => this.normalize(obs, entity, options));
185
+ return {
186
+ entity: {
187
+ ...entity,
188
+ observations: results.map(r => r.normalized),
189
+ },
190
+ results,
191
+ };
192
+ }
193
+ }
@@ -9,4 +9,6 @@ export { AnalyticsManager } from './AnalyticsManager.js';
9
9
  export { CompressionManager } from './CompressionManager.js';
10
10
  export { ArchiveManager, type ArchiveCriteria, type ArchiveOptions, type ArchiveResult, } from './ArchiveManager.js';
11
11
  export { StreamingExporter, type StreamResult } from './StreamingExporter.js';
12
+ export { ObservationNormalizer, type NormalizationOptions, type NormalizationResult, } from './ObservationNormalizer.js';
13
+ export { KeywordExtractor, type ScoredKeyword, } from './KeywordExtractor.js';
12
14
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/features/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EACL,SAAS,EACT,KAAK,YAAY,EACjB,KAAK,YAAY,EACjB,KAAK,aAAa,EAClB,KAAK,cAAc,EACnB,KAAK,UAAU,GAChB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAC7D,OAAO,EACL,cAAc,EACd,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,KAAK,aAAa,GACnB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,iBAAiB,EAAE,KAAK,YAAY,EAAE,MAAM,wBAAwB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/features/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EACL,SAAS,EACT,KAAK,YAAY,EACjB,KAAK,YAAY,EACjB,KAAK,aAAa,EAClB,KAAK,cAAc,EACnB,KAAK,UAAU,GAChB,MAAM,gBAAgB,CAAC;AACxB,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AACzD,OAAO,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAC7D,OAAO,EACL,cAAc,EACd,KAAK,eAAe,EACpB,KAAK,cAAc,EACnB,KAAK,aAAa,GACnB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,iBAAiB,EAAE,KAAK,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAG9E,OAAO,EACL,qBAAqB,EACrB,KAAK,oBAAoB,EACzB,KAAK,mBAAmB,GACzB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EACL,gBAAgB,EAChB,KAAK,aAAa,GACnB,MAAM,uBAAuB,CAAC"}
@@ -9,3 +9,6 @@ export { AnalyticsManager } from './AnalyticsManager.js';
9
9
  export { CompressionManager } from './CompressionManager.js';
10
10
  export { ArchiveManager, } from './ArchiveManager.js';
11
11
  export { StreamingExporter } from './StreamingExporter.js';
12
+ // Phase 11 Sprint 5: Semantic Compression
13
+ export { ObservationNormalizer, } from './ObservationNormalizer.js';
14
+ export { KeywordExtractor, } from './KeywordExtractor.js';
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Hybrid Search Manager
3
+ *
4
+ * Phase 11: Orchestrates three-layer hybrid search combining
5
+ * semantic, lexical, and symbolic signals.
6
+ *
7
+ * @module search/HybridSearchManager
8
+ */
9
+ import type { HybridSearchOptions, HybridSearchResult, ReadonlyKnowledgeGraph } from '../types/index.js';
10
+ import type { SemanticSearch } from './SemanticSearch.js';
11
+ import type { RankedSearch } from './RankedSearch.js';
12
+ import { SymbolicSearch } from './SymbolicSearch.js';
13
+ /**
14
+ * Default weights for hybrid search layers.
15
+ */
16
+ export declare const DEFAULT_HYBRID_WEIGHTS: {
17
+ semantic: number;
18
+ lexical: number;
19
+ symbolic: number;
20
+ };
21
+ /**
22
+ * Hybrid Search Manager
23
+ *
24
+ * Combines three search layers:
25
+ * 1. Semantic: Vector similarity via embeddings
26
+ * 2. Lexical: Keyword matching via TF-IDF/BM25
27
+ * 3. Symbolic: Structured metadata filtering
28
+ *
29
+ * @example
30
+ * ```typescript
31
+ * const hybrid = new HybridSearchManager(semanticSearch, rankedSearch);
32
+ * const results = await hybrid.search(graph, 'machine learning', {
33
+ * semanticWeight: 0.5,
34
+ * lexicalWeight: 0.3,
35
+ * symbolicWeight: 0.2,
36
+ * symbolic: { tags: ['ai'] }
37
+ * });
38
+ * ```
39
+ */
40
+ export declare class HybridSearchManager {
41
+ private semanticSearch;
42
+ private rankedSearch;
43
+ private symbolicSearch;
44
+ constructor(semanticSearch: SemanticSearch | null, rankedSearch: RankedSearch);
45
+ /**
46
+ * Perform hybrid search combining all three layers.
47
+ *
48
+ * @param graph - Knowledge graph to search
49
+ * @param query - Search query text
50
+ * @param options - Hybrid search options with weights
51
+ * @returns Combined and ranked results
52
+ */
53
+ search(graph: ReadonlyKnowledgeGraph, query: string, options?: Partial<HybridSearchOptions>): Promise<HybridSearchResult[]>;
54
+ /**
55
+ * Execute semantic search layer.
56
+ */
57
+ private executeSemanticSearch;
58
+ /**
59
+ * Execute lexical search layer (TF-IDF/BM25).
60
+ */
61
+ private executeLexicalSearch;
62
+ /**
63
+ * Execute symbolic search layer.
64
+ */
65
+ private executeSymbolicSearch;
66
+ /**
67
+ * Merge results from all three layers.
68
+ */
69
+ private mergeResults;
70
+ /**
71
+ * Search with full entity resolution.
72
+ * Alias for search() since we now always resolve entities.
73
+ */
74
+ searchWithEntities(graph: ReadonlyKnowledgeGraph, query: string, options?: Partial<HybridSearchOptions>): Promise<HybridSearchResult[]>;
75
+ /**
76
+ * Get the symbolic search instance for direct access.
77
+ */
78
+ getSymbolicSearch(): SymbolicSearch;
79
+ }
80
+ //# sourceMappingURL=HybridSearchManager.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"HybridSearchManager.d.ts","sourceRoot":"","sources":["../../src/search/HybridSearchManager.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAEV,mBAAmB,EACnB,kBAAkB,EAClB,sBAAsB,EAEvB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AACtD,OAAO,EAAE,cAAc,EAAE,MAAM,qBAAqB,CAAC;AAGrD;;GAEG;AACH,eAAO,MAAM,sBAAsB;;;;CAIlC,CAAC;AAEF;;;;;;;;;;;;;;;;;;GAkBG;AACH,qBAAa,mBAAmB;IAI5B,OAAO,CAAC,cAAc;IACtB,OAAO,CAAC,YAAY;IAJtB,OAAO,CAAC,cAAc,CAAiB;gBAG7B,cAAc,EAAE,cAAc,GAAG,IAAI,EACrC,YAAY,EAAE,YAAY;IAKpC;;;;;;;OAOG;IACG,MAAM,CACV,KAAK,EAAE,sBAAsB,EAC7B,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,OAAO,CAAC,mBAAmB,CAAM,GACzC,OAAO,CAAC,kBAAkB,EAAE,CAAC;IAuChC;;OAEG;YACW,qBAAqB;IA8BnC;;OAEG;YACW,oBAAoB;IA4BlC;;OAEG;IACH,OAAO,CAAC,qBAAqB;IAsB7B;;OAEG;IACH,OAAO,CAAC,YAAY;IAkDpB;;;OAGG;IACG,kBAAkB,CACtB,KAAK,EAAE,sBAAsB,EAC7B,KAAK,EAAE,MAAM,EACb,OAAO,GAAE,OAAO,CAAC,mBAAmB,CAAM,GACzC,OAAO,CAAC,kBAAkB,EAAE,CAAC;IAIhC;;OAEG;IACH,iBAAiB,IAAI,cAAc;CAGpC"}